diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 8a237b6950..f365f39531 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -93,7 +93,7 @@ jobs: if: env.ENABLE_COVERAGE == 'true' uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 with: - name: coverage-${{ matrix.runner }} + name: coverage-jest-${{ matrix.runner }} path: | apps/web/coverage !apps/web/coverage/lcov-report @@ -124,9 +124,10 @@ jobs: name: Vitest strategy: matrix: - package: - - shared-components - - module-api + path: + - apps/desktop + - packages/shared-components + - packages/module-api runs-on: ubuntu-24.04 steps: - name: Checkout code @@ -149,30 +150,39 @@ jobs: uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5 with: path: | - packages/${{ matrix.package }}/node_modules/.cache - packages/${{ matrix.package }}/node_modules/.vite/vitest - key: ${{ hashFiles('pnpm-lock.yaml') }} + ${{ matrix.path }}/node_modules/.cache + ${{ matrix.path }}/node_modules/.vite/vitest + key: ${{ matrix.path }}-${{ hashFiles('pnpm-lock.yaml') }} - name: Setup playwright uses: ./.github/actions/setup-playwright - if: matrix.package == 'shared-components' + if: matrix.path == 'packages/shared-components' with: write-cache: ${{ github.event_name != 'merge_group' }} - name: Run tests - working-directory: "packages/${{ matrix.package }}" + working-directory: ${{ matrix.path }} run: pnpm test:unit --coverage=$ENABLE_COVERAGE # Dump the disk usage on failure, because this job seems to fail with disk fills sometimes - name: df - run: df + run: df -h && df -i if: ${{ failure() }} + - name: Calculate artifact name + if: env.ENABLE_COVERAGE == 'true' + id: artifact + run: | + NAME=$(basename "$MATRIX_PATH") + echo "name=$NAME" >> $GITHUB_OUTPUT + env: + MATRIX_PATH: ${{ matrix.path }} + - name: Upload Artifact if: env.ENABLE_COVERAGE == 'true' uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 with: - name: coverage-${{ matrix.package }} + name: coverage-${{ steps.artifact.outputs.name }} path: | - packages/${{ matrix.package }}/coverage - !packages/${{ matrix.package }}/coverage/lcov-report + ${{ matrix.path }}/coverage + !${{ matrix.path }}/coverage/lcov-report diff --git a/.prettierignore b/.prettierignore index e0a9e4fc57..0a8f1a2baf 100644 --- a/.prettierignore +++ b/.prettierignore @@ -62,4 +62,10 @@ CHANGELOG.md /packages/shared-components/typedoc/ /packages/shared-components/storybook-static/ +# These files are generated by running `pnpm -r lint:types` and do not adhere to prettier's requirements. +# All of them are .gitignored within their parent directory. +/packages/playwright-common/lib/ +/packages/module-api/lib/ +/packages/module-api/temp/ + /.nx/ diff --git a/CHANGELOG.md b/CHANGELOG.md index de6bb19956..8c53af59eb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +Changes in [1.12.17](https://github.com/element-hq/element-web/releases/tag/v1.12.17) (2026-04-30) +================================================================================================== +## 🐛 Bug Fixes + +* [Backport] Fix OIDC login callback handling on Element Desktop ([#33337](https://github.com/element-hq/element-web/pull/33337)). Contributed by @t3chguy. + + Changes in [1.12.16](https://github.com/element-hq/element-web/releases/tag/v1.12.16) (2026-04-28) ================================================================================================== ## 🦖 Deprecations diff --git a/apps/desktop/.eslintrc.cjs b/apps/desktop/.eslintrc.cjs index d8f162c2f0..7a1d06729c 100644 --- a/apps/desktop/.eslintrc.cjs +++ b/apps/desktop/.eslintrc.cjs @@ -86,5 +86,12 @@ module.exports = { "@typescript-eslint/no-non-null-assertion": "off", }, }, + { + files: ["src/**/*.test.ts", "electron-builder.ts", "vitest.config.ts"], + extends: ["plugin:matrix-org/typescript"], + parserOptions: { + project: ["tsconfig.node.json"], + }, + }, ], }; diff --git a/apps/desktop/electron-builder.ts b/apps/desktop/electron-builder.ts index 967a129c1c..9bcd771a73 100644 --- a/apps/desktop/electron-builder.ts +++ b/apps/desktop/electron-builder.ts @@ -52,6 +52,7 @@ interface Variant extends Metadata { } type Writable = NonNullable< + // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type T extends Function ? T : T extends object ? { -readonly [K in keyof T]: Writable } : T >; @@ -74,7 +75,7 @@ if (process.env.VARIANT_PATH) { } for (const key in variant) { - console.log(`${key}: ${variant[key]}`); + console.log(`${key}: ${variant[key as keyof Variant]}`); } interface Configuration extends BaseConfiguration { diff --git a/apps/desktop/package.json b/apps/desktop/package.json index 3b1f145d0c..84db56e160 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -3,7 +3,7 @@ "productName": "Element", "main": "lib/electron-main.js", "exports": "./lib/electron-main.js", - "version": "1.12.16", + "version": "1.12.17", "description": "Element: the future of secure communication", "author": { "name": "Element", @@ -32,8 +32,9 @@ "lint": "pnpm lint:types && pnpm lint:js", "lint:js": "eslint --max-warnings 0 src hak playwright scripts", "lint:js-fix": "eslint --fix --max-warnings 0 src hak playwright scripts && prettier --log-level=warn --write .", - "lint:types": "pnpm lint:types:src && pnpm lint:types:test && pnpm lint:types:scripts && pnpm lint:types:hak", + "lint:types": "pnpm lint:types:src && pnpm lint:types:node && pnpm lint:types:test && pnpm lint:types:scripts && pnpm lint:types:hak", "lint:types:src": "tsc --noEmit", + "lint:types:node": "tsc --noEmit -p tsconfig.node.json", "lint:types:test": "tsc --noEmit -p playwright/tsconfig.json", "lint:types:scripts": "tsc --noEmit -p scripts/tsconfig.json", "lint:types:hak": "tsc --noEmit -p hak/tsconfig.json", @@ -49,6 +50,7 @@ "docker:install": "scripts/in-docker.sh pnpm install", "clean": "rimraf webapp.asar dist packages deploys lib", "hak": "node scripts/hak/index.ts", + "test:unit": "vitest", "test:playwright": "nx test:playwright --", "test:playwright:open": "nx test:playwright -- --ui", "test:playwright:screenshots": "nx test:playwright:screenshots --", @@ -70,6 +72,7 @@ "@babel/preset-typescript": "^7.18.6", "@electron/asar": "4.2.0", "@electron/fuses": "^2.1.1", + "@element-hq/vite-common": "workspace:*", "@playwright/test": "catalog:", "@stylistic/eslint-plugin": "^5.0.0", "@types/auto-launch": "^5.0.1", @@ -79,6 +82,7 @@ "@types/pacote": "^11.1.1", "@typescript-eslint/eslint-plugin": "^8.0.0", "@typescript-eslint/parser": "^8.0.0", + "@vitest/coverage-v8": "catalog:", "app-builder-lib": "26.9.0", "chokidar": "^5.0.0", "detect-libc": "^2.0.0", @@ -95,17 +99,20 @@ "eslint-plugin-unicorn": "^56.0.0", "glob": "^13.0.0", "matrix-web-i18n": "catalog:", + "memfs": "^4.57.2", "mkdirp": "^3.0.0", "pacote": "^21.0.0", "prettier": "^3.0.0", "rimraf": "^6.0.0", "tar": "^7.5.8", - "typescript": "6.0.3" + "typescript": "6.0.3", + "vitest": "catalog:", + "vitest-sonar-reporter": "catalog:" }, "hakDependencies": { "matrix-seshat": "4.2.0" }, - "packageManager": "pnpm@10.33.0+sha512.10568bb4a6afb58c9eb3630da90cc9516417abebd3fabbe6739f0ae795728da1491e9db5a544c76ad8eb7570f5c4bb3d6c637b2cb41bfdcdb47fa823c8649319", + "packageManager": "pnpm@10.33.2+sha512.a90faf6feeab71ad6c6e57f94e0fe1a12f5dcc22cd754db40ae9593eb6a3e0b6b12e3540218bb37ae083404b1f2ce6db2a4121e979829b4aff94b99f49da1cf8", "nx": { "includedScripts": [] } diff --git a/apps/desktop/src/protocol.test.ts b/apps/desktop/src/protocol.test.ts new file mode 100644 index 0000000000..396a59f065 --- /dev/null +++ b/apps/desktop/src/protocol.test.ts @@ -0,0 +1,87 @@ +/* +Copyright 2026 Element Creations Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +import { expect, describe, it, beforeEach, vi } from "vitest"; +import { fs as memfs, vol } from "memfs"; + +import ProtocolHandler from "./protocol.js"; + +const TEST_PROTOCOL = "test.proto"; +const TEST_SESSION_ID = "test_session_id"; +const USER_DATA_DIR = "/Users/name/Library/Application Support/Element"; + +vi.mock("node:fs", () => ({ default: memfs })); +vi.mock("electron", () => ({ + app: { + getPath: vi.fn().mockReturnValue("/Users/name/Library/Application Support/Element"), + on: vi.fn(), + }, + ipcMain: { + handle: vi.fn(), + }, +})); + +beforeEach(() => { + // Reset the state of the in-memory fs + vol.reset(); +}); + +describe("ProtocolHandler", () => { + describe("getProfileFromDeeplink", () => { + const handler = new ProtocolHandler(TEST_PROTOCOL); + + beforeEach(() => { + vol.fromJSON( + { + "./sso-sessions.json": JSON.stringify({ [TEST_SESSION_ID]: USER_DATA_DIR }), + }, + USER_DATA_DIR, + ); + }); + + it("should handle legacy SSO URIs", () => { + expect( + handler.getProfileFromDeeplink([ + "Element.app", + `element://vector/webapp/?element-desktop-ssoid=${TEST_SESSION_ID}`, + ]), + ).toBe(USER_DATA_DIR); + }); + + it("should handle OIDC URIs with response_mode=query", () => { + expect( + handler.getProfileFromDeeplink([ + "Element.app", + `${TEST_PROTOCOL}:/vector/webapp/?no_universal_links=true&code=DEADBEEF&state=foobar:element-desktop-ssoid:${TEST_SESSION_ID}`, + ]), + ).toBe(USER_DATA_DIR); + }); + + it("should handle OIDC URIs with response_mode=fragment", () => { + expect( + handler.getProfileFromDeeplink([ + "Element.app", + `${TEST_PROTOCOL}:/vector/webapp/?no_universal_links=true#code=DEADBEEF&state=foobar:element-desktop-ssoid:${TEST_SESSION_ID}`, + ]), + ).toBe(USER_DATA_DIR); + }); + + it("should handle malformed OIDC URIs gracefully", () => { + expect( + handler.getProfileFromDeeplink([ + "Element.app", + `${TEST_PROTOCOL}:/vector/webapp/?no_universal_links=true#code=DEADBEEF:element-desktop-ssoid:${TEST_SESSION_ID}`, + ]), + ).toBeUndefined(); + }); + + it("should handle unrelated URIs gracefully", () => { + expect(handler.getProfileFromDeeplink(["Element.app", `${TEST_PROTOCOL}:/vector/webapp/`])).toBeUndefined(); + expect(handler.getProfileFromDeeplink(["Element.app", `test.unrelated:/vector/webapp/`])).toBeUndefined(); + }); + }); +}); diff --git a/apps/desktop/src/protocol.ts b/apps/desktop/src/protocol.ts index f6812b49e6..ad2558fc2a 100644 --- a/apps/desktop/src/protocol.ts +++ b/apps/desktop/src/protocol.ts @@ -97,7 +97,8 @@ export default class ProtocolHandler { const s = fs.readFileSync(storePath, { encoding: "utf8" }); const o = JSON.parse(s); return typeof o === "object" ? o : {}; - } catch { + } catch (e) { + console.warn("Unable to read protocol store, starting with empty store: ", e); return {}; } } @@ -130,10 +131,26 @@ export default class ProtocolHandler { let sessionId = parsedUrl.searchParams.get(SEARCH_PARAM); if (!sessionId) { // In OIDC, we must shuttle the value in the `state` param rather than `element-desktop-ssoid` - // We encode it as a suffix like `:element-desktop-ssoid:XXYYZZ` - sessionId = parsedUrl.searchParams.get("state")!.split(`:${SEARCH_PARAM}:`)[1]; + // We encode it as a suffix like `:element-desktop-ssoid:XXYYZZ`. + // The OIDC flow may have used response_mode=fragment or query, so we need to handle both cases. + let searchParams = parsedUrl.searchParams; + if (parsedUrl.hash.includes("=")) { + const [params] = parsedUrl.hash.substring(1).split("?", 2); + searchParams = new URLSearchParams(params); + } + + const state = searchParams.get("state"); + if (state) { + sessionId = state.split(`:${SEARCH_PARAM}:`)[1]; + } } - console.log("Forwarding to profile: ", store[sessionId]); + + if (!sessionId) { + console.warn("Unable to read session ID in deeplink url:", deeplinkUrl); + return undefined; + } + + console.log("Forwarding to profile:", store[sessionId]); return store[sessionId]; } } diff --git a/apps/desktop/tsconfig.json b/apps/desktop/tsconfig.json index e0490357fc..5993a344e6 100644 --- a/apps/desktop/tsconfig.json +++ b/apps/desktop/tsconfig.json @@ -14,5 +14,6 @@ "lib": ["es2022", "es2024.promise"], "strict": true }, - "include": ["./src/**/*.ts", "./src/**/*.cts"] + "include": ["./src/**/*.ts", "./src/**/*.cts"], + "exclude": ["./src/**/*.test.ts"] } diff --git a/apps/desktop/tsconfig.node.json b/apps/desktop/tsconfig.node.json new file mode 100644 index 0000000000..c9de5ead8d --- /dev/null +++ b/apps/desktop/tsconfig.node.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "resolveJsonModule": true, + "module": "nodenext", + "moduleResolution": "NodeNext", + "target": "es2022", + "sourceMap": false, + "typeRoots": [], + "types": [], + "skipLibCheck": true, + "noEmit": true, + "strict": true + }, + "include": ["./electron-builder.ts", "./vitest.config.ts", "./src/**/*.d.ts", "./src/**/*.test.ts"] +} diff --git a/apps/desktop/vitest.config.ts b/apps/desktop/vitest.config.ts new file mode 100644 index 0000000000..e9572047a0 --- /dev/null +++ b/apps/desktop/vitest.config.ts @@ -0,0 +1,23 @@ +/* +Copyright 2026 Element Creations Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +import { defineConfig, mergeConfig } from "vitest/config"; +import baseConfig from "@element-hq/vite-common/vite.config.js"; + +export default mergeConfig( + baseConfig, + defineConfig({ + test: { + coverage: { + // The coverage report currently chokes on this file as it doesn't process it as TypeScript + exclude: ["src/preload.cts"], + }, + include: ["src/**/*.test.ts"], + }, + }), + true, +); diff --git a/apps/web/Dockerfile b/apps/web/Dockerfile index 2cc7e22fdb..a29297130e 100644 --- a/apps/web/Dockerfile +++ b/apps/web/Dockerfile @@ -15,7 +15,7 @@ WORKDIR /src COPY --parents package.json pnpm-lock.yaml pnpm-workspace.yaml patches scripts **/package.json /src/ RUN corepack enable RUN --mount=type=bind,source=.git,target=/src/.git /src/scripts/docker-link-repos.sh -RUN pnpm install +RUN pnpm install --frozen-lockfile # Build COPY --link --exclude=.git --exclude=apps/web/docker . /src diff --git a/apps/web/package.json b/apps/web/package.json index 605b44c9bb..c04ddac5e8 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -1,6 +1,6 @@ { "name": "element-web", - "version": "1.12.16", + "version": "1.12.17", "description": "Element: the future of secure communication", "author": "New Vector Ltd.", "repository": { @@ -244,6 +244,6 @@ "engines": { "node": ">=22.18" }, - "packageManager": "pnpm@10.33.0+sha512.10568bb4a6afb58c9eb3630da90cc9516417abebd3fabbe6739f0ae795728da1491e9db5a544c76ad8eb7570f5c4bb3d6c637b2cb41bfdcdb47fa823c8649319", + "packageManager": "pnpm@10.33.2+sha512.a90faf6feeab71ad6c6e57f94e0fe1a12f5dcc22cd754db40ae9593eb6a3e0b6b12e3540218bb37ae083404b1f2ce6db2a4121e979829b4aff94b99f49da1cf8", "private": true } diff --git a/apps/web/playwright/e2e/left-panel/room-list-panel/room-list-custom-sections.spec.ts b/apps/web/playwright/e2e/left-panel/room-list-panel/room-list-custom-sections.spec.ts index 742b611faf..e40b1a6c0d 100644 --- a/apps/web/playwright/e2e/left-panel/room-list-panel/room-list-custom-sections.spec.ts +++ b/apps/web/playwright/e2e/left-panel/room-list-panel/room-list-custom-sections.spec.ts @@ -258,6 +258,47 @@ test.describe("Room list custom sections", () => { }); }); + test.describe("Collapse and expand all sections", () => { + test("should collapse all sections when 'Collapse all sections' button is clicked", async ({ page, app }) => { + await app.client.createRoom({ name: "my room" }); + await createCustomSection(page, "Work"); + + const roomList = getRoomList(page); + const header = getRoomListHeader(page); + + await expect(getSectionHeader(page, "Chats")).toBeVisible(); + await expect(getSectionHeader(page, "Work")).toBeVisible(); + + const collapseButton = header.getByRole("button", { name: "Collapse all sections" }); + await expect(collapseButton).toBeVisible(); + + await expect(roomList.getByRole("row", { name: "Open room my room" })).toBeVisible(); + + await collapseButton.click(); + + await expect(getSectionHeader(page, "Chats")).toHaveAttribute("aria-expanded", "false"); + await expect(getSectionHeader(page, "Work")).toHaveAttribute("aria-expanded", "false"); + }); + + test("should expand all sections when 'Expand all sections' button is clicked", async ({ page, app }) => { + await app.client.createRoom({ name: "my room" }); + await createCustomSection(page, "Work"); + + const roomList = getRoomList(page); + const header = getRoomListHeader(page); + + await expect(getSectionHeader(page, "Chats")).toBeVisible(); + + await header.getByRole("button", { name: "Collapse all sections" }).click(); + await expect(roomList.getByRole("row", { name: "Open room my room" })).not.toBeVisible(); + + await header.getByRole("button", { name: "Expand all sections" }).click(); + + await expect(getSectionHeader(page, "Chats")).toHaveAttribute("aria-expanded", "true"); + await expect(getSectionHeader(page, "Work")).toHaveAttribute("aria-expanded", "true"); + }); + }); + test.describe("Adding a room to a custom section", () => { test("should add a room to a custom section via the More Options menu", async ({ page, app }) => { await app.client.createRoom({ name: "my room" }); diff --git a/apps/web/playwright/e2e/timeline/timeline.spec.ts b/apps/web/playwright/e2e/timeline/timeline.spec.ts index 994cd4a101..4985e4c651 100644 --- a/apps/web/playwright/e2e/timeline/timeline.spec.ts +++ b/apps/web/playwright/e2e/timeline/timeline.spec.ts @@ -751,7 +751,7 @@ test.describe("Timeline", () => { await expect(page.locator(".mx_EventTile[data-layout=irc] .mx_ViewSourceEvent_expanded")).toBeVisible(); }); - test("should render file size in kibibytes on a file tile", async ({ page, room }) => { + test("should render file size in kibibytes on a file tile", async ({ page, app, room }) => { await page.goto(`/#/room/${room.roomId}`); await expect( page @@ -760,6 +760,7 @@ test.describe("Timeline", () => { ).toBeVisible(); // Upload a file from the message composer + app. await page .locator(".mx_MessageComposer_actions input[type='file']") .setInputFiles(getSampleFilePath("matrix-org-client-versions.json")); diff --git a/apps/web/playwright/snapshots/settings/appearance-user-settings-tab/appearance-user-settings-tab.spec.ts/appearance-tab-linux.png b/apps/web/playwright/snapshots/settings/appearance-user-settings-tab/appearance-user-settings-tab.spec.ts/appearance-tab-linux.png index 15da4c7c02..afce0e5b04 100644 Binary files a/apps/web/playwright/snapshots/settings/appearance-user-settings-tab/appearance-user-settings-tab.spec.ts/appearance-tab-linux.png and b/apps/web/playwright/snapshots/settings/appearance-user-settings-tab/appearance-user-settings-tab.spec.ts/appearance-tab-linux.png differ diff --git a/apps/web/playwright/snapshots/settings/appearance-user-settings-tab/theme-choice-panel.spec.ts/window-custom-theme-linux.png b/apps/web/playwright/snapshots/settings/appearance-user-settings-tab/theme-choice-panel.spec.ts/window-custom-theme-linux.png index ad8bd39b6d..dc175017bb 100644 Binary files a/apps/web/playwright/snapshots/settings/appearance-user-settings-tab/theme-choice-panel.spec.ts/window-custom-theme-linux.png and b/apps/web/playwright/snapshots/settings/appearance-user-settings-tab/theme-choice-panel.spec.ts/window-custom-theme-linux.png differ diff --git a/apps/web/playwright/testcontainers/mas.ts b/apps/web/playwright/testcontainers/mas.ts index 85b727189f..311922acf2 100644 --- a/apps/web/playwright/testcontainers/mas.ts +++ b/apps/web/playwright/testcontainers/mas.ts @@ -11,7 +11,7 @@ import { } from "@element-hq/element-web-playwright-common/lib/testcontainers/index.js"; const DOCKER_IMAGE = - "ghcr.io/element-hq/matrix-authentication-service:main@sha256:034500c4797287bfcdc4d13304e89ac65ce44a0fa33664836aea6e42c33535fb"; + "ghcr.io/element-hq/matrix-authentication-service:main@sha256:c765fb602f78e77eccaa8e020e56c39eef99eccbabc9cb0df2c5705f60ca899e"; /** * MatrixAuthenticationServiceContainer which freezes the docker digest to diff --git a/apps/web/playwright/testcontainers/synapse.ts b/apps/web/playwright/testcontainers/synapse.ts index 5fb1c27bfd..fbd78fc22c 100644 --- a/apps/web/playwright/testcontainers/synapse.ts +++ b/apps/web/playwright/testcontainers/synapse.ts @@ -8,7 +8,7 @@ Please see LICENSE files in the repository root for full details. import { SynapseContainer as BaseSynapseContainer } from "@element-hq/element-web-playwright-common/lib/testcontainers/index.js"; const DOCKER_IMAGE = - "ghcr.io/element-hq/synapse:develop@sha256:b2fec2c9460f5b297a3a4ce78037902590240a1978301ed1d4bc97918c451041"; + "ghcr.io/element-hq/synapse:develop@sha256:53b1c81dc161be1d999344ca727a520972b912fc24681dfafe25fca4b766b2a5"; /** * SynapseContainer which freezes the docker digest to stabilise tests, diff --git a/apps/web/res/css/_common.pcss b/apps/web/res/css/_common.pcss index d55cb07606..58bebb609b 100644 --- a/apps/web/res/css/_common.pcss +++ b/apps/web/res/css/_common.pcss @@ -163,9 +163,10 @@ b { font-weight: bold; } -a:hover, -a:link, -a:visited { +/* Keep the legacy link colour without overriding Compound anchors. */ +a:where(:not([data-kind])):hover, +a:where(:not([data-kind])):link, +a:where(:not([data-kind])):visited { color: $accent-alt; } diff --git a/apps/web/res/css/_compound.pcss b/apps/web/res/css/_compound.pcss index 2bc482551d..823cf1ef7b 100644 --- a/apps/web/res/css/_compound.pcss +++ b/apps/web/res/css/_compound.pcss @@ -1,3 +1,6 @@ +/* Shared cascade order: Compound tokens, Compound Web, shared components, then app overrides. */ +@layer compound-tokens, compound-web, shared-components, app-web; + /* Modules bundled with compound apply compound lastly. In order to catch issue due to css class ordering, we put compound at the end */ -@import url("@vector-im/compound-design-tokens/assets/web/css/compound-design-tokens.css") layer(compound); -@import url("@vector-im/compound-web/dist/style.css"); +@import url("@vector-im/compound-design-tokens/assets/web/css/compound-design-tokens.css") layer(compound-tokens); +@import url("@vector-im/compound-web/dist/style.css") layer(compound-web); diff --git a/apps/web/res/css/views/audio_messages/_PlaybackContainer.pcss b/apps/web/res/css/views/audio_messages/_PlaybackContainer.pcss index 21980b67e6..087726a1f7 100644 --- a/apps/web/res/css/views/audio_messages/_PlaybackContainer.pcss +++ b/apps/web/res/css/views/audio_messages/_PlaybackContainer.pcss @@ -11,9 +11,8 @@ Please see LICENSE files in the repository root for full details. /* Container for live recording and playback controls */ .mx_MediaBody.mx_VoiceMessagePrimaryContainer { - /* The waveform (right) has a 1px padding on it that we want to account for, otherwise */ - /* inherit from mx_MediaBody */ - padding-right: 11px; + /* Match mx_MediaBody spacing, offsetting the waveform's 1px internal right padding. */ + padding: 6px 11px 6px 12px; /* Cheat at alignment a bit */ display: flex; diff --git a/apps/web/res/css/views/auth/_CompleteSecurityBody.pcss b/apps/web/res/css/views/auth/_CompleteSecurityBody.pcss index 3aa9cbf518..d17914ead1 100644 --- a/apps/web/res/css/views/auth/_CompleteSecurityBody.pcss +++ b/apps/web/res/css/views/auth/_CompleteSecurityBody.pcss @@ -10,7 +10,7 @@ Please see LICENSE files in the repository root for full details. .mx_CompleteSecurityBody { color: $authpage-primary-color; background-color: $background; - border-radius: 4px; + border-radius: 24px; padding: 20px 20px 60px 20px; box-sizing: border-box; diff --git a/apps/web/res/css/views/messages/_MFileBody.pcss b/apps/web/res/css/views/messages/_MFileBody.pcss index 1f8ab80576..783a58c63d 100644 --- a/apps/web/res/css/views/messages/_MFileBody.pcss +++ b/apps/web/res/css/views/messages/_MFileBody.pcss @@ -7,7 +7,6 @@ Please see LICENSE files in the repository root for full details. */ .mx_MFileBody [data-type="download"] { - color: $accent; height: var(--cpd-space-9x); & object { diff --git a/apps/web/res/css/views/messages/_MediaBody.pcss b/apps/web/res/css/views/messages/_MediaBody.pcss index d3eb02ca65..aadf220885 100644 --- a/apps/web/res/css/views/messages/_MediaBody.pcss +++ b/apps/web/res/css/views/messages/_MediaBody.pcss @@ -17,6 +17,8 @@ Please see LICENSE files in the repository root for full details. color: $secondary-content; font: var(--cpd-font-body-md-regular); line-height: $font-24px; - - padding: 6px 12px; +} + +.mx_MAudioBody > .mx_MediaBody { + border-radius: var(--MBody-border-radius); } diff --git a/apps/web/res/css/views/messages/_TextualEvent.pcss b/apps/web/res/css/views/messages/_TextualEvent.pcss index bf7061f2ba..3dbde24791 100644 --- a/apps/web/res/css/views/messages/_TextualEvent.pcss +++ b/apps/web/res/css/views/messages/_TextualEvent.pcss @@ -19,3 +19,12 @@ Please see LICENSE files in the repository root for full details. opacity: unset; /* Unset the opacity value specified above on the search results panel */ } } + +.mx_TextualBody_urlPreviews { + /* Let shared-components own preview link colours instead of the app-wide anchor colour. */ + a:where(:not([data-kind])):hover, + a:where(:not([data-kind])):link, + a:where(:not([data-kind])):visited { + color: revert-layer; + } +} diff --git a/apps/web/res/css/views/settings/_ThemeChoicePanel.pcss b/apps/web/res/css/views/settings/_ThemeChoicePanel.pcss index e61671f4f5..d5e9f5c619 100644 --- a/apps/web/res/css/views/settings/_ThemeChoicePanel.pcss +++ b/apps/web/res/css/views/settings/_ThemeChoicePanel.pcss @@ -26,12 +26,22 @@ Please see LICENSE files in the repository root for full details. &.mx_ThemeChoicePanel_themeSelector_disabled { border-color: var(--cpd-color-border-disabled); + + .mx_ThemeChoicePanel_themeSelector_Label { + color: var(--cpd-color-text-disabled); + cursor: not-allowed; + } } .mx_ThemeChoicePanel_themeSelector_Label { - color: var(--cpd-color-text-primary); font: var(--cpd-font-body-md-semibold); } + + &:not(.mx_ThemeChoicePanel_themeSelector_disabled) { + .mx_ThemeChoicePanel_themeSelector_Label { + color: var(--cpd-color-text-primary); + } + } } } diff --git a/apps/web/res/css/views/settings/tabs/_SettingsSection.pcss b/apps/web/res/css/views/settings/tabs/_SettingsSection.pcss index c98732e884..0a70169248 100644 --- a/apps/web/res/css/views/settings/tabs/_SettingsSection.pcss +++ b/apps/web/res/css/views/settings/tabs/_SettingsSection.pcss @@ -12,7 +12,8 @@ Please see LICENSE files in the repository root for full details. color: $primary-content; - a { + /* Compound links carry data-kind and provide their own colour. */ + a:not([data-kind]) { color: $links; } diff --git a/apps/web/res/css/views/settings/tabs/_SettingsTab.pcss b/apps/web/res/css/views/settings/tabs/_SettingsTab.pcss index c92e454e05..9e43079364 100644 --- a/apps/web/res/css/views/settings/tabs/_SettingsTab.pcss +++ b/apps/web/res/css/views/settings/tabs/_SettingsTab.pcss @@ -10,7 +10,8 @@ Please see LICENSE files in the repository root for full details. color: $primary-content; - a { + /* Compound links carry data-kind and provide their own colour. */ + a:not([data-kind]) { color: $links; } diff --git a/apps/web/res/themes/dark-custom/css/dark-custom.pcss b/apps/web/res/themes/dark-custom/css/dark-custom.pcss index 7eaffcee0c..1b1b30f33d 100644 --- a/apps/web/res/themes/dark-custom/css/dark-custom.pcss +++ b/apps/web/res/themes/dark-custom/css/dark-custom.pcss @@ -1,9 +1,11 @@ -@import "../../../../res/css/_font-sizes.pcss"; -@import "../../legacy-light/css/_fonts.pcss"; -@import "../../legacy-light/css/_legacy-light.pcss"; -@import "../../legacy-dark/css/_legacy-dark.pcss"; -@import "../../light-custom/css/_custom.pcss"; -@import "../../../../res/css/_components.pcss"; +@layer compound-tokens, compound-web, shared-components, app-web; + +@import "../../../../res/css/_font-sizes.pcss" layer(app-web); +@import "../../legacy-light/css/_fonts.pcss" layer(app-web); +@import "../../legacy-light/css/_legacy-light.pcss" layer(app-web); +@import "../../legacy-dark/css/_legacy-dark.pcss" layer(app-web); +@import "../../light-custom/css/_custom.pcss" layer(app-web); +@import "../../../../res/css/_components.pcss" layer(app-web); @import "../../../../res/css/_compound.pcss"; -@import url("highlight.js/styles/atom-one-light.min.css"); -@import url("github-markdown-css/github-markdown-dark.css"); +@import url("highlight.js/styles/atom-one-light.min.css") layer(app-web); +@import url("github-markdown-css/github-markdown-dark.css") layer(app-web); diff --git a/apps/web/res/themes/dark/css/dark.pcss b/apps/web/res/themes/dark/css/dark.pcss index 05cc9dfb6a..abb4fc04ee 100644 --- a/apps/web/res/themes/dark/css/dark.pcss +++ b/apps/web/res/themes/dark/css/dark.pcss @@ -1,9 +1,11 @@ -@import "../../../../res/css/_font-sizes.pcss"; -@import "../../light/css/_fonts.pcss"; -@import "../../light/css/_light.pcss"; -@import "_dark.pcss"; -@import "../../light/css/_mods.pcss"; -@import "../../../../res/css/_components.pcss"; +@layer compound-tokens, compound-web, shared-components, app-web; + +@import "../../../../res/css/_font-sizes.pcss" layer(app-web); +@import "../../light/css/_fonts.pcss" layer(app-web); +@import "../../light/css/_light.pcss" layer(app-web); +@import "_dark.pcss" layer(app-web); +@import "../../light/css/_mods.pcss" layer(app-web); +@import "../../../../res/css/_components.pcss" layer(app-web); @import "../../../../res/css/_compound.pcss"; -@import url("highlight.js/styles/atom-one-dark.min.css"); -@import url("github-markdown-css/github-markdown-dark.css"); +@import url("highlight.js/styles/atom-one-dark.min.css") layer(app-web); +@import url("github-markdown-css/github-markdown-dark.css") layer(app-web); diff --git a/apps/web/res/themes/legacy-dark/css/legacy-dark.pcss b/apps/web/res/themes/legacy-dark/css/legacy-dark.pcss index d816c8c24d..99dadd6eab 100644 --- a/apps/web/res/themes/legacy-dark/css/legacy-dark.pcss +++ b/apps/web/res/themes/legacy-dark/css/legacy-dark.pcss @@ -1,8 +1,10 @@ -@import "../../../../res/css/_font-sizes.pcss"; -@import "../../legacy-light/css/_fonts.pcss"; -@import "../../legacy-light/css/_legacy-light.pcss"; -@import "_legacy-dark.pcss"; -@import "../../../../res/css/_components.pcss"; +@layer compound-tokens, compound-web, shared-components, app-web; + +@import "../../../../res/css/_font-sizes.pcss" layer(app-web); +@import "../../legacy-light/css/_fonts.pcss" layer(app-web); +@import "../../legacy-light/css/_legacy-light.pcss" layer(app-web); +@import "_legacy-dark.pcss" layer(app-web); +@import "../../../../res/css/_components.pcss" layer(app-web); @import "../../../../res/css/_compound.pcss"; -@import url("highlight.js/styles/atom-one-dark.min.css"); -@import url("github-markdown-css/github-markdown-dark.css"); +@import url("highlight.js/styles/atom-one-dark.min.css") layer(app-web); +@import url("github-markdown-css/github-markdown-dark.css") layer(app-web); diff --git a/apps/web/res/themes/legacy-light/css/legacy-light.pcss b/apps/web/res/themes/legacy-light/css/legacy-light.pcss index d196b2850c..dcc7b1a82a 100644 --- a/apps/web/res/themes/legacy-light/css/legacy-light.pcss +++ b/apps/web/res/themes/legacy-light/css/legacy-light.pcss @@ -1,7 +1,9 @@ -@import "../../../../res/css/_font-sizes.pcss"; -@import "_fonts.pcss"; -@import "_legacy-light.pcss"; -@import "../../../../res/css/_components.pcss"; +@layer compound-tokens, compound-web, shared-components, app-web; + +@import "../../../../res/css/_font-sizes.pcss" layer(app-web); +@import "_fonts.pcss" layer(app-web); +@import "_legacy-light.pcss" layer(app-web); +@import "../../../../res/css/_components.pcss" layer(app-web); @import "../../../../res/css/_compound.pcss"; -@import url("highlight.js/styles/atom-one-light.min.css"); -@import url("github-markdown-css/github-markdown-light.css"); +@import url("highlight.js/styles/atom-one-light.min.css") layer(app-web); +@import url("github-markdown-css/github-markdown-light.css") layer(app-web); diff --git a/apps/web/res/themes/light-custom/css/_custom.pcss b/apps/web/res/themes/light-custom/css/_custom.pcss index 5f0210ed10..1bf761a7e7 100644 --- a/apps/web/res/themes/light-custom/css/_custom.pcss +++ b/apps/web/res/themes/light-custom/css/_custom.pcss @@ -25,16 +25,17 @@ $panels: var(--panels, var(--cpd-color-gray-600)); $panel-actions: var(--panels-actions, var(--cpd-color-gray-300)); /* --timeline-background-color */ -$button-secondary-bg-color: var(--timeline-background-color); -$lightbox-border-color: var(--timeline-background-color); -$menu-bg-color: var(--timeline-background-color); -$message-action-bar-bg-color: var(--timeline-background-color); -$background: var(--timeline-background-color); +$custom-theme-background: var(--timeline-background-color, var(--cpd-color-bg-canvas-default)); +$button-secondary-bg-color: $custom-theme-background; +$lightbox-border-color: $custom-theme-background; +$menu-bg-color: $custom-theme-background; +$message-action-bar-bg-color: $custom-theme-background; +$background: $custom-theme-background; $togglesw-ball-color: var(--cpd-color-bg-action-primary-rest); $togglesw-off-color: var(--togglesw-off-color); $droptarget-bg-color: var(--timeline-background-color-50pct); /* still needs alpha at .5 */ $authpage-modal-bg-color: var(--timeline-background-color-50pct); /* still needs alpha at .59 */ -$roomheader-bg-color: var(--timeline-background-color); +$roomheader-bg-color: $custom-theme-background; /* --roomlist-highlights-color */ $panel-actions: var(--roomlist-highlights-color); diff --git a/apps/web/res/themes/light-custom/css/light-custom.pcss b/apps/web/res/themes/light-custom/css/light-custom.pcss index d02c0eeef4..ce575e0518 100644 --- a/apps/web/res/themes/light-custom/css/light-custom.pcss +++ b/apps/web/res/themes/light-custom/css/light-custom.pcss @@ -1,8 +1,10 @@ -@import "../../../../res/css/_font-sizes.pcss"; -@import "../../legacy-light/css/_fonts.pcss"; -@import "../../legacy-light/css/_legacy-light.pcss"; -@import "_custom.pcss"; -@import "../../../../res/css/_components.pcss"; +@layer compound-tokens, compound-web, shared-components, app-web; + +@import "../../../../res/css/_font-sizes.pcss" layer(app-web); +@import "../../legacy-light/css/_fonts.pcss" layer(app-web); +@import "../../legacy-light/css/_legacy-light.pcss" layer(app-web); +@import "_custom.pcss" layer(app-web); +@import "../../../../res/css/_components.pcss" layer(app-web); @import "../../../../res/css/_compound.pcss"; -@import url("highlight.js/styles/atom-one-light.min.css"); -@import url("github-markdown-css/github-markdown-light.css"); +@import url("highlight.js/styles/atom-one-light.min.css") layer(app-web); +@import url("github-markdown-css/github-markdown-light.css") layer(app-web); diff --git a/apps/web/res/themes/light-high-contrast/css/light-high-contrast.pcss b/apps/web/res/themes/light-high-contrast/css/light-high-contrast.pcss index c794d07499..b1c35e414e 100644 --- a/apps/web/res/themes/light-high-contrast/css/light-high-contrast.pcss +++ b/apps/web/res/themes/light-high-contrast/css/light-high-contrast.pcss @@ -1,9 +1,11 @@ -@import "../../../../res/css/_font-sizes.pcss"; -@import "../../light/css/_fonts.pcss"; -@import "../../light/css/_light.pcss"; -@import "_light-high-contrast.pcss"; -@import "../../light/css/_mods.pcss"; -@import "../../../../res/css/_components.pcss"; +@layer compound-tokens, compound-web, shared-components, app-web; + +@import "../../../../res/css/_font-sizes.pcss" layer(app-web); +@import "../../light/css/_fonts.pcss" layer(app-web); +@import "../../light/css/_light.pcss" layer(app-web); +@import "_light-high-contrast.pcss" layer(app-web); +@import "../../light/css/_mods.pcss" layer(app-web); +@import "../../../../res/css/_components.pcss" layer(app-web); @import "../../../../res/css/_compound.pcss"; -@import url("highlight.js/styles/atom-one-light.min.css"); -@import url("github-markdown-css/github-markdown-light.css"); +@import url("highlight.js/styles/atom-one-light.min.css") layer(app-web); +@import url("github-markdown-css/github-markdown-light.css") layer(app-web); diff --git a/apps/web/res/themes/light/css/light.pcss b/apps/web/res/themes/light/css/light.pcss index d3abfe669c..10e5e05d78 100644 --- a/apps/web/res/themes/light/css/light.pcss +++ b/apps/web/res/themes/light/css/light.pcss @@ -1,8 +1,10 @@ -@import "../../../../res/css/_font-sizes.pcss"; -@import "_fonts.pcss"; -@import "_light.pcss"; -@import "_mods.pcss"; -@import "../../../../res/css/_components.pcss"; +@layer compound-tokens, compound-web, shared-components, app-web; + +@import "../../../../res/css/_font-sizes.pcss" layer(app-web); +@import "_fonts.pcss" layer(app-web); +@import "_light.pcss" layer(app-web); +@import "_mods.pcss" layer(app-web); +@import "../../../../res/css/_components.pcss" layer(app-web); @import "../../../../res/css/_compound.pcss"; -@import url("highlight.js/styles/atom-one-light.min.css"); -@import url("github-markdown-css/github-markdown-light.css"); +@import url("highlight.js/styles/atom-one-light.min.css") layer(app-web); +@import url("github-markdown-css/github-markdown-light.css") layer(app-web); diff --git a/apps/web/src/@types/media_preview.ts b/apps/web/src/@types/media_preview.ts index d340e64caf..ffce5944ee 100644 --- a/apps/web/src/@types/media_preview.ts +++ b/apps/web/src/@types/media_preview.ts @@ -25,9 +25,9 @@ export interface MediaPreviewConfig extends Record { /** * Media preview setting for thumbnails of media in rooms. */ - media_previews: MediaPreviewValue; + media_previews?: MediaPreviewValue; /** * Media preview settings for avatars of rooms we have been invited to. */ - invite_avatars: MediaPreviewValue.On | MediaPreviewValue.Off; + invite_avatars?: MediaPreviewValue.On | MediaPreviewValue.Off; } diff --git a/apps/web/src/LegacyCallHandler.tsx b/apps/web/src/LegacyCallHandler.tsx index 81fb0093d0..3ccf500b26 100644 --- a/apps/web/src/LegacyCallHandler.tsx +++ b/apps/web/src/LegacyCallHandler.tsx @@ -725,7 +725,7 @@ export default class LegacyCallHandler extends TypedEventEmitter { - SettingsStore.setValue("fallbackICEServerAllowed", null, SettingLevel.DEVICE, allow); + SettingsStore.setValue("fallbackICEServerAllowed", null, SettingLevel.DEVICE, allow ?? null); }); } diff --git a/apps/web/src/MatrixClientPeg.ts b/apps/web/src/MatrixClientPeg.ts index 8148509ef1..5f8125be67 100644 --- a/apps/web/src/MatrixClientPeg.ts +++ b/apps/web/src/MatrixClientPeg.ts @@ -297,6 +297,9 @@ class MatrixClientPegClass implements IMatrixClientPeg { opts.lazyLoadMembers = true; opts.clientWellKnownPollPeriod = 2 * 60 * 60; // 2 hours opts.threadSupport = true; + if (SettingsStore.getValue("feature_user_status")) { + opts.unstableMSC4429SyncUserProfileFields = ["org.matrix.msc4426.status"]; + } if (SettingsStore.getValue("feature_sliding_sync")) { throw new UserFriendlyError("sliding_sync_legacy_no_longer_supported"); diff --git a/apps/web/src/Notifier.ts b/apps/web/src/Notifier.ts index b0a2ce5b42..b7e2144132 100644 --- a/apps/web/src/Notifier.ts +++ b/apps/web/src/Notifier.ts @@ -142,6 +142,16 @@ interface EmittedEvents { [NotifierEvent.NotificationHiddenChange]: (hidden: boolean) => void; } +/** + * Type representing a notification sound setting + */ +export type NotificationSound = { + url: string; + name?: string; + type?: string; + size?: number; +}; + class NotifierClass extends TypedEventEmitter { private notifsByRoom: Record = {}; @@ -223,12 +233,7 @@ class NotifierClass extends TypedEventEmitter): void => { this.setState({ crawlerSleepTime: parseInt(e.target.value, 10) }); - SettingsStore.setValue("crawlerSleepTime", null, SettingLevel.DEVICE, e.target.value); + SettingsStore.setValue("crawlerSleepTime", null, SettingLevel.DEVICE, e.target.valueAsNumber); }; public render(): React.ReactNode { diff --git a/apps/web/src/components/structures/MatrixChat.tsx b/apps/web/src/components/structures/MatrixChat.tsx index af4fe18a9f..0dbc2af2e4 100644 --- a/apps/web/src/components/structures/MatrixChat.tsx +++ b/apps/web/src/components/structures/MatrixChat.tsx @@ -1794,11 +1794,11 @@ export default class MatrixChat extends React.PureComponent { SettingsStore.watchSetting( "blacklistUnverifiedDevices", null, - (_settingName, _roomId, atLevel, blacklistEnabled: boolean) => { + (_settingName, _roomId, atLevel, blacklistEnabled) => { if (atLevel != SettingLevel.DEVICE) { return; } - crypto.globalBlacklistUnverifiedDevices = blacklistEnabled; + crypto.globalBlacklistUnverifiedDevices = !!blacklistEnabled; }, ); } diff --git a/apps/web/src/components/views/auth/LoginWithQR.tsx b/apps/web/src/components/views/auth/LoginWithQR.tsx index 4e1906b25e..808e42e66b 100644 --- a/apps/web/src/components/views/auth/LoginWithQR.tsx +++ b/apps/web/src/components/views/auth/LoginWithQR.tsx @@ -9,9 +9,8 @@ Please see LICENSE files in the repository root for full details. import React from "react"; import { ClientRendezvousFailureReason, + linkNewDeviceByGeneratingQR, MSC4108FailureReason, - MSC4108RendezvousSession, - MSC4108SecureChannel, MSC4108SignInWithQR, RendezvousError, type RendezvousFailureReason, @@ -55,6 +54,7 @@ export type FailureReason = RendezvousFailureReason | LoginWithQRFailureReason; */ export default class LoginWithQR extends React.Component { private finished = false; + private abortController?: AbortController; public constructor(props: IProps) { super(props); @@ -69,35 +69,31 @@ export default class LoginWithQR extends React.Component { } public componentDidMount(): void { - this.updateMode(this.props.mode).then(() => {}); + void this.updateMode(this.props.mode); } public componentDidUpdate(prevProps: Readonly): void { if (prevProps.mode !== this.props.mode) { - this.updateMode(this.props.mode).then(() => {}); + void this.updateMode(this.props.mode); } } private async updateMode(mode: Mode, showLoading = true): Promise { - if (this.state.rendezvous) { - const rendezvous = this.state.rendezvous; - rendezvous.onFailure = undefined; - this.setState({ rendezvous: undefined }); - } + this.abortController?.abort(); + this.abortController = new AbortController(); + this.setState({ rendezvous: undefined }); if (showLoading) { this.setState({ phase: Phase.Loading }); } + if (mode === Mode.Show) { - await this.generateAndShowCode(); + await this.generateAndShowCode(this.abortController); } } public componentWillUnmount(): void { - if (this.state.rendezvous && !this.finished) { - // eslint-disable-next-line react/no-direct-mutation-state - this.state.rendezvous.onFailure = undefined; - // calling cancel will call close() as well to clean up the resources - this.state.rendezvous.cancel(MSC4108FailureReason.UserCancelled); + if (!this.finished) { + this.abortController?.abort(); } } @@ -106,24 +102,18 @@ export default class LoginWithQR extends React.Component { this.props.onFinished(success); } - private generateAndShowCode = async (): Promise => { + private generateAndShowCode = async (abortController: AbortController): Promise => { let rendezvous: MSC4108SignInWithQR; try { - const transport = new MSC4108RendezvousSession({ - onFailure: this.onFailure, - client: this.props.client, - }); - await transport.send(""); - const channel = new MSC4108SecureChannel(transport, undefined, this.onFailure); - rendezvous = new MSC4108SignInWithQR(channel, false, this.props.client, this.onFailure); - - await rendezvous.generateCode(); + rendezvous = await linkNewDeviceByGeneratingQR(this.props.client, this.onFailure, abortController.signal); + if (abortController.signal.aborted) return; this.setState({ phase: Phase.ShowingQR, rendezvous, failureReason: undefined, }); } catch (e) { + if (abortController.signal.aborted) return; logger.error("Error whilst generating QR code", e); this.setState({ phase: Phase.Error, failureReason: ClientRendezvousFailureReason.HomeserverLacksSupport }); return; @@ -142,8 +132,9 @@ export default class LoginWithQR extends React.Component { // we ask the user to confirm that the channel is secure } catch (e: RendezvousError | unknown) { + if (abortController.signal.aborted) return; logger.error("Error whilst approving login", e); - await rendezvous?.cancel( + await rendezvous.cancel( e instanceof RendezvousError ? (e.code as MSC4108FailureReason) : ClientRendezvousFailureReason.Unknown, ); } @@ -210,6 +201,7 @@ export default class LoginWithQR extends React.Component { }; public reset(): void { + this.abortController?.abort(); this.setState({ rendezvous: undefined, verificationUri: undefined, diff --git a/apps/web/src/components/views/messages/SenderProfile.tsx b/apps/web/src/components/views/messages/SenderProfile.tsx index f3465d24f8..70ab205cc1 100644 --- a/apps/web/src/components/views/messages/SenderProfile.tsx +++ b/apps/web/src/components/views/messages/SenderProfile.tsx @@ -13,6 +13,7 @@ import { useCreateAutoDisposedViewModel, DisambiguatedProfileView } from "@eleme import { DisambiguatedProfileViewModel } from "../../../viewmodels/room/timeline/event-tile/DisambiguatedProfileViewModel"; import { useRoomMemberProfile } from "../../../hooks/room/useRoomMemberProfile"; +import { useUserStatus } from "../../../hooks/useUserStatus"; interface IProps { mxEvent: MatrixEvent; @@ -27,6 +28,7 @@ export default function SenderProfile({ mxEvent, onClick, withTooltip }: IProps) userId: sender, member: mxEvent.sender, }); + const userStatus = useUserStatus(sender); const disambiguatedProfileVM = useCreateAutoDisposedViewModel( () => @@ -37,9 +39,13 @@ export default function SenderProfile({ mxEvent, onClick, withTooltip }: IProps) colored: true, emphasizeDisplayName: true, withTooltip, + userStatus, }), ); + useEffect(() => { + disambiguatedProfileVM.setUserStatus(userStatus); + }, [disambiguatedProfileVM, userStatus]); useEffect(() => { disambiguatedProfileVM.setMember(sender ?? "", member); }, [disambiguatedProfileVM, member, sender]); diff --git a/apps/web/src/components/views/messages/TextualBodyFactory.tsx b/apps/web/src/components/views/messages/TextualBodyFactory.tsx index d947bb982e..b51eebe78b 100644 --- a/apps/web/src/components/views/messages/TextualBodyFactory.tsx +++ b/apps/web/src/components/views/messages/TextualBodyFactory.tsx @@ -210,7 +210,7 @@ export function TextualBodyFactory(props: Readonly): JSX.Element { vm={textualBodyVm} body={} bodyRef={contentRef} - urlPreviews={} + urlPreviews={} className={getTextualBodyClassName(content.msgtype as MsgType | undefined)} /> ); diff --git a/apps/web/src/components/views/settings/LayoutSwitcher.tsx b/apps/web/src/components/views/settings/LayoutSwitcher.tsx index 9df3968859..5997c95707 100644 --- a/apps/web/src/components/views/settings/LayoutSwitcher.tsx +++ b/apps/web/src/components/views/settings/LayoutSwitcher.tsx @@ -38,8 +38,8 @@ function LayoutSelector(): JSX.Element { { - // We don't have any file in the form, we can cast it as string safely - const newLayout = new FormData(evt.currentTarget).get("layout") as string | null; + // We don't have any file in the form, we can cast it as Layout safely + const newLayout = new FormData(evt.currentTarget).get("layout") as Layout; await SettingsStore.setValue("layout", null, SettingLevel.DEVICE, newLayout); }} > diff --git a/apps/web/src/components/views/settings/devices/LoginWithQRSection.tsx b/apps/web/src/components/views/settings/devices/LoginWithQRSection.tsx index 523633c884..f7f73b90ca 100644 --- a/apps/web/src/components/views/settings/devices/LoginWithQRSection.tsx +++ b/apps/web/src/components/views/settings/devices/LoginWithQRSection.tsx @@ -7,48 +7,35 @@ Please see LICENSE files in the repository root for full details. */ import React from "react"; -import { - type IServerVersions, - type OidcClientConfig, - type MatrixClient, - DEVICE_CODE_SCOPE, -} from "matrix-js-sdk/src/matrix"; +import { type MatrixClient } from "matrix-js-sdk/src/matrix"; import QrCodeIcon from "@vector-im/compound-design-tokens/assets/web/icons/qr-code"; import { Text } from "@vector-im/compound-web"; +import { isSignInWithQRAvailable } from "matrix-js-sdk/src/rendezvous"; import { _t } from "../../../../languageHandler"; import AccessibleButton from "../../elements/AccessibleButton"; import { SettingsSubsection } from "../shared/SettingsSubsection"; import { useMatrixClientContext } from "../../../../contexts/MatrixClientContext"; +import { useAsyncMemo } from "../../../../hooks/useAsyncMemo"; interface IProps { onShowQr: () => void; - versions?: IServerVersions; - oidcClientConfig?: OidcClientConfig; isCrossSigningReady?: boolean; } -export function shouldShowQr( - cli: MatrixClient, - isCrossSigningReady: boolean, - oidcClientConfig?: OidcClientConfig, - versions?: IServerVersions, -): boolean { - const msc4108Supported = !!versions?.unstable_features?.["org.matrix.msc4108"]; +export async function shouldShowQrForLinkNewDevice(cli: MatrixClient, isCrossSigningReady: boolean): Promise { + const doesServerHaveSupport = await isSignInWithQRAvailable(cli); - const deviceAuthorizationGrantSupported = oidcClientConfig?.grant_types_supported.includes(DEVICE_CODE_SCOPE); - - return ( - !!deviceAuthorizationGrantSupported && - msc4108Supported && - !!cli.getCrypto()?.exportSecretsBundle && - isCrossSigningReady - ); + return doesServerHaveSupport && !!cli.getCrypto()?.exportSecretsBundle && isCrossSigningReady; } -const LoginWithQRSection: React.FC = ({ onShowQr, versions, oidcClientConfig, isCrossSigningReady }) => { +const LoginWithQRSection: React.FC = ({ onShowQr, isCrossSigningReady }) => { const cli = useMatrixClientContext(); - const offerShowQr = shouldShowQr(cli, !!isCrossSigningReady, oidcClientConfig, versions); + const offerShowQr = useAsyncMemo( + () => shouldShowQrForLinkNewDevice(cli, !!isCrossSigningReady), + [cli, isCrossSigningReady], + false, + ); return ( diff --git a/apps/web/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.tsx b/apps/web/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.tsx index 4a8cecc204..fe5fa52be0 100644 --- a/apps/web/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.tsx +++ b/apps/web/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.tsx @@ -68,8 +68,6 @@ export default class AppearanceUserSettingsTab extends React.Component - - ): void => { this.setState({ autocompleteDelay: e.target.value }); - SettingsStore.setValue("autocompleteDelay", null, SettingLevel.DEVICE, e.target.value); + SettingsStore.setValue("autocompleteDelay", null, SettingLevel.DEVICE, e.target.valueAsNumber); }; private onReadMarkerInViewThresholdMs = (e: React.ChangeEvent): void => { this.setState({ readMarkerInViewThresholdMs: e.target.value }); - SettingsStore.setValue("readMarkerInViewThresholdMs", null, SettingLevel.DEVICE, e.target.value); + SettingsStore.setValue("readMarkerInViewThresholdMs", null, SettingLevel.DEVICE, e.target.valueAsNumber); }; private onReadMarkerOutOfViewThresholdMs = (e: React.ChangeEvent): void => { this.setState({ readMarkerOutOfViewThresholdMs: e.target.value }); - SettingsStore.setValue("readMarkerOutOfViewThresholdMs", null, SettingLevel.DEVICE, e.target.value); + SettingsStore.setValue("readMarkerOutOfViewThresholdMs", null, SettingLevel.DEVICE, e.target.valueAsNumber); }; private renderGroup(settingIds: BooleanSettingKey[], level = SettingLevel.ACCOUNT): JSX.Element { diff --git a/apps/web/src/components/views/settings/tabs/user/SessionManagerTab.tsx b/apps/web/src/components/views/settings/tabs/user/SessionManagerTab.tsx index 8cf234ca12..01aba5ccdb 100644 --- a/apps/web/src/components/views/settings/tabs/user/SessionManagerTab.tsx +++ b/apps/web/src/components/views/settings/tabs/user/SessionManagerTab.tsx @@ -162,14 +162,6 @@ const SessionManagerTab: React.FC<{ const disableMultipleSignout = !!accountManagement?.endpoint; const userId = matrixClient?.getUserId(); const currentUserMember = (userId && matrixClient?.getUser(userId)) || undefined; - const clientVersions = useAsyncMemo(() => matrixClient.getVersions(), [matrixClient]); - const oidcClientConfig = useAsyncMemo(async () => { - try { - return await matrixClient?.getAuthMetadata(); - } catch (e) { - logger.error("Failed to discover OIDC metadata", e); - } - }, [matrixClient]); const isCrossSigningReady = useAsyncMemo( async () => matrixClient.getCrypto()?.isCrossSigningReady() ?? false, [matrixClient], @@ -279,12 +271,7 @@ const SessionManagerTab: React.FC<{ return ( - + = ( _originalSettingName, _roomId, _level, diff --git a/apps/web/src/dispatcher/actions.ts b/apps/web/src/dispatcher/actions.ts index 49a3ce8868..ad19d1c7d8 100644 --- a/apps/web/src/dispatcher/actions.ts +++ b/apps/web/src/dispatcher/actions.ts @@ -403,4 +403,20 @@ export enum Action { * or keyboard event). */ UserActivity = "user_activity", + + /** + * Fired to request collapsing all room list sections. + */ + RoomListCollapseAllSections = "room_list_collapse_all_sections", + + /** + * Fired to request expanding all room list sections. + */ + RoomListExpandAllSections = "room_list_expand_all_sections", + + /** + * Fired to report the collapse state of a given room list section. + * Payload: {@link RoomListSectionsCollapseStateChangedPayload} + */ + RoomListSectionsCollapseStateChanged = "room_list_sections_collapse_state_changed", } diff --git a/apps/web/src/dispatcher/payloads/RoomListSectionsCollapseStateChangedPayload.ts b/apps/web/src/dispatcher/payloads/RoomListSectionsCollapseStateChangedPayload.ts new file mode 100644 index 0000000000..d3dbe21cf4 --- /dev/null +++ b/apps/web/src/dispatcher/payloads/RoomListSectionsCollapseStateChangedPayload.ts @@ -0,0 +1,20 @@ +/* + * Copyright 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial + * Please see LICENSE files in the repository root for full details. + */ + +import { type CollapseSectionsOption } from "@element-hq/web-shared-components"; + +import { type ActionPayload } from "../payloads"; +import { type Action } from "../actions"; + +export interface RoomListSectionsCollapseStateChangedPayload extends ActionPayload { + action: Action.RoomListSectionsCollapseStateChanged; + /** + * The new collapse state for the room list sections. + * If undefined, the feature is disabled. + */ + collapseSections?: CollapseSectionsOption; +} diff --git a/apps/web/src/dispatcher/payloads/SettingUpdatedPayload.ts b/apps/web/src/dispatcher/payloads/SettingUpdatedPayload.ts index ed84c2b18e..51844faa16 100644 --- a/apps/web/src/dispatcher/payloads/SettingUpdatedPayload.ts +++ b/apps/web/src/dispatcher/payloads/SettingUpdatedPayload.ts @@ -9,14 +9,26 @@ Please see LICENSE files in the repository root for full details. import { type ActionPayload } from "../payloads"; import { type Action } from "../actions"; import { type SettingLevel } from "../../settings/SettingLevel"; -import { type SettingValueType } from "../../settings/Settings"; +import { type SettingKey, type Settings } from "../../settings/Settings"; -export interface SettingUpdatedPayload extends ActionPayload { +export interface SettingUpdatedPayload extends ActionPayload { action: Action.SettingUpdated; - settingName: string; + settingName: S; roomId: string | null; level: SettingLevel; - newValueAtLevel: SettingLevel; - newValue: SettingValueType; + newValueAtLevel: Settings[S]["default"]; + newValue: Settings[S]["default"]; +} + +/** + * Type guard to check if a payload is a SettingUpdatedPayload for a specific setting. + * @param payload the payload to assert + * @param settingName the setting name to check for + */ +export function isSettingUpdatedPayload( + payload: SettingUpdatedPayload, + settingName: S, +): payload is SettingUpdatedPayload { + return payload.settingName === settingName; } diff --git a/apps/web/src/emojipicker/recent.ts b/apps/web/src/emojipicker/recent.ts index 05c1321716..42b3c4b276 100644 --- a/apps/web/src/emojipicker/recent.ts +++ b/apps/web/src/emojipicker/recent.ts @@ -29,7 +29,7 @@ const STORAGE_LIMIT = 100; function migrate(): void { const data: ILegacyFormat = JSON.parse(window.localStorage.mx_reaction_count || "{}"); const sorted = Object.entries(data).sort(([, [count1, date1]], [, [count2, date2]]) => date2 - date1); - const newFormat = sorted.map(([emoji, [count, date]]) => [emoji, count]); + const newFormat = sorted.map(([emoji, [count, date]]) => [emoji, count]); SettingsStore.setValue(SETTING_NAME, null, SettingLevel.ACCOUNT, newFormat.slice(0, STORAGE_LIMIT)); } @@ -41,7 +41,7 @@ export function add(emoji: string): void { const recents = getRecentEmoji(); const i = recents.findIndex(([e]) => e === emoji); - let newEntry; + let newEntry: RecentEmojiData[number]; if (i >= 0) { // first remove the existing tuple so that we can increment it and push it to the front [newEntry] = recents.splice(i, 1); diff --git a/apps/web/src/hooks/useUserStatus.ts b/apps/web/src/hooks/useUserStatus.ts new file mode 100644 index 0000000000..e73679c9b4 --- /dev/null +++ b/apps/web/src/hooks/useUserStatus.ts @@ -0,0 +1,98 @@ +/** +Copyright 2026 Element Creations Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +import { useEffect, useState } from "react"; +import { ClientEvent, MatrixError } from "matrix-js-sdk/src/matrix"; +import { logger as rootLogger } from "matrix-js-sdk/src/logger"; + +import { useMatrixClientContext } from "../contexts/MatrixClientContext"; +import { useTypedEventEmitter } from "./useEventEmitter"; +import { useFeatureEnabled } from "./useSettings"; + +const logger = rootLogger.getChild("useUserStatus"); + +export interface UserStatus { + emoji: string; + text: string; +} + +const MAX_STATUS_TEXT_BYTES = 256; + +export function userStatusTextWithinMaxLength(text: string): boolean { + const textEncoder = new TextEncoder(); + return textEncoder.encode(text).length <= MAX_STATUS_TEXT_BYTES; +} + +/** + * Hook to get the MSC4426 user status for a given user ID. Returns undefined if the feature is disabled, + * the user does not have a status, or if there was an error fetching the status. + * + * @param userId The ID of the user whose status is being fetched. + * @returns The user's status, or undefined if not available. + */ +export function useUserStatus(userId: string | undefined): UserStatus | undefined { + const isEnabled = useFeatureEnabled("feature_user_status"); + const matrixClient = useMatrixClientContext(); + const [rawUserStatus, setRawUserStatus] = useState(); + + useTypedEventEmitter(matrixClient, ClientEvent.UserProfileUpdate, (syncedUserId, syncProfile) => { + if (syncedUserId !== userId) { + return; + } + if (syncProfile["org.matrix.msc4426.status"]) { + setRawUserStatus(syncProfile["org.matrix.msc4426.status"]); + } + }); + useEffect(() => { + (async () => { + if (!isEnabled) { + return; + } + if (!userId) { + setRawUserStatus(undefined); + return; + } + if ((await matrixClient.doesServerSupportExtendedProfiles()) === false) { + setRawUserStatus(undefined); + return; + } + try { + const result = await matrixClient.getExtendedProfileProperty(userId, "org.matrix.msc4426.status"); + setRawUserStatus(result); + } catch (ex) { + if (ex instanceof MatrixError && ex.errcode === "M_NOT_FOUND") { + setRawUserStatus(undefined); + } else { + logger.warn(`Failed to get userStatus for ${userId}`, ex); + } + } + })(); + }, [isEnabled, userId, matrixClient]); + if (!isEnabled) { + return; + } + + if (typeof rawUserStatus !== "object" || rawUserStatus === null) { + logger.warn(`value of "org.matrix.msc4426.status" was not an object for ${userId}`); + return; + } + if ("emoji" in rawUserStatus === false || typeof rawUserStatus.emoji !== "string" || !rawUserStatus.emoji) { + logger.warn(`"emoji" property was not a valid string for ${userId}`); + return; + } + if ("text" in rawUserStatus === false || typeof rawUserStatus.text !== "string" || !rawUserStatus.text) { + logger.warn(`"text" property was not a valid string for ${userId}`); + return; + } + + return { + emoji: rawUserStatus.emoji, + text: userStatusTextWithinMaxLength(rawUserStatus.text) + ? rawUserStatus.text + : `${rawUserStatus.text.slice(0, MAX_STATUS_TEXT_BYTES)}…`, + }; +} diff --git a/apps/web/src/i18n/strings/cs.json b/apps/web/src/i18n/strings/cs.json index b2c02f1358..b2ab1feb4a 100644 --- a/apps/web/src/i18n/strings/cs.json +++ b/apps/web/src/i18n/strings/cs.json @@ -588,7 +588,6 @@ "video": "Video", "video_room": "Video místnost", "view_message": "Zobrazit zprávu", - "voice": "Hlas", "warning": "Upozornění" }, "composer": { @@ -3909,7 +3908,6 @@ "connection_lost": "Došlo ke ztrátě připojení k serveru", "connection_lost_description": "Bez připojení k serveru nelze uskutečňovat hovory.", "consulting": "Konzultace s %(transferTarget)s. Převod na %(transferee)s", - "decline_call": "Odmítnout", "default_device": "Výchozí zařízení", "dial": "Vytočit", "dialpad": "Číselník", @@ -3960,7 +3958,6 @@ "show_sidebar_button": "Zobrazit postranní panel", "silence": "Ztlumit zvonění", "silenced": "Oznámení ztlumena", - "skip_lobby_toggle_option": "Připojte se ihned", "start_screenshare": "Začít sdílet obrazovku", "stop_screenshare": "Ukončit sdílení obrazovky", "too_many_calls": "Přiliš mnoho hovorů", @@ -3982,7 +3979,6 @@ "user_is_presenting": "%(sharerName)s prezentuje", "video_call": "Videohovor", "video_call_incoming": "Příchozí videohovor", - "video_call_started": "Videohovor byl zahájen", "video_call_using": "Videohovor pomocí:", "voice_call": "Hlasový hovor", "voice_call_incoming": "Příchozí hlasový hovor", diff --git a/apps/web/src/i18n/strings/cy.json b/apps/web/src/i18n/strings/cy.json index 9bac8f0faf..7b45070fec 100644 --- a/apps/web/src/i18n/strings/cy.json +++ b/apps/web/src/i18n/strings/cy.json @@ -591,7 +591,6 @@ "video": "Fideo", "video_room": "Ystafell fideo", "view_message": "Gweld neges", - "voice": "Llais", "warning": "Rhybudd" }, "composer": { @@ -3872,7 +3871,6 @@ "connection_lost": "Mae cysylltedd â'r gweinydd wedi'i golli", "connection_lost_description": "Allwch chi ddim osod galwadau heb gysylltiad â'r gweinydd.", "consulting": "Ymgynghori â %(transferTarget)s. Trosglwyddo i %(transferee)s", - "decline_call": "Gwrthod", "default_device": "Dyfais Rhagosodedig", "dial": "Deial", "dialpad": "Pad Deialu", @@ -3923,7 +3921,6 @@ "show_sidebar_button": "Dangos y bar ochr", "silence": "Distewi galwad", "silenced": "Hysbysiadau wedi'u distewi", - "skip_lobby_toggle_option": "Ymunwch ar unwaith", "start_screenshare": "Dechreuwch rannu'ch sgrin", "stop_screenshare": "Stopiwch rannu'ch sgrin", "too_many_calls": "Gormod o Alwadau", @@ -3945,7 +3942,6 @@ "user_is_presenting": "Mae %(sharerName)s yn cyflwyno", "video_call": "Galwad fideo", "video_call_incoming": "Galwad fideo i mewn", - "video_call_started": "Galwad fideo wedi dechrau", "video_call_using": "Galwad fideo gan ddefnyddio:", "voice_call": "Galwad llais", "voice_call_incoming": "Galwad llais i mewn", diff --git a/apps/web/src/i18n/strings/da.json b/apps/web/src/i18n/strings/da.json index 5d439bf984..aad00cb3f7 100644 --- a/apps/web/src/i18n/strings/da.json +++ b/apps/web/src/i18n/strings/da.json @@ -3412,7 +3412,6 @@ "connection_lost": "Forbindelsen til serveren er tabt", "connection_lost_description": "Du kan ikke lave et opkald uden en forbindelse til serveren.", "consulting": "Konfererer med %(transferTarget)s. Overfør til %(transferee)s", - "decline_call": "Afvis", "default_device": "Standardenhed", "dial": "Ring", "dialpad": "Tastatur", @@ -3480,7 +3479,6 @@ "user_busy_description": "Brugeren du ringede til er optaget.", "user_is_presenting": "%(sharerName)s præsenterer", "video_call": "Videoopkald", - "video_call_started": "Videoopkald startet", "video_call_using": "Videoopkald med:", "voice_call": "Stemmeopkald", "you_are_presenting": "Du præsenterer" diff --git a/apps/web/src/i18n/strings/de_DE.json b/apps/web/src/i18n/strings/de_DE.json index 05eb83b9ba..cef095738d 100644 --- a/apps/web/src/i18n/strings/de_DE.json +++ b/apps/web/src/i18n/strings/de_DE.json @@ -588,7 +588,6 @@ "video": "Video", "video_room": "Videochat", "view_message": "Nachricht anzeigen", - "voice": "Sprachanruf", "warning": "Warnung" }, "composer": { @@ -3865,7 +3864,6 @@ "connection_lost": "Verbindung zum Server unterbrochen", "connection_lost_description": "Du kannst ohne Verbindung zum Server keine Anrufe tätigen.", "consulting": "%(transferTarget)s wird angefragt. Übertragung zu %(transferee)s", - "decline_call": "Ablehnen", "default_device": "Standardgerät", "dial": "Wählen", "dialpad": "Telefontastatur", @@ -3916,7 +3914,6 @@ "show_sidebar_button": "Seitenleiste anzeigen", "silence": "Anruf stummschalten", "silenced": "Benachrichtigungen stummgeschaltet", - "skip_lobby_toggle_option": "Sofort beitreten", "start_screenshare": "Bildschirmfreigabe starten", "stop_screenshare": "Bildschirmfreigabe beenden", "too_many_calls": "Zu viele Anrufe", @@ -3938,7 +3935,6 @@ "user_is_presenting": "%(sharerName)s präsentiert", "video_call": "Videoanruf", "video_call_incoming": "Eingehender Videoanruf", - "video_call_started": "Videoanruf hat begonnen", "video_call_using": "Videoanruf mit:", "voice_call": "Sprachanruf", "voice_call_incoming": "Eingehender Anruf", diff --git a/apps/web/src/i18n/strings/el.json b/apps/web/src/i18n/strings/el.json index ac5f9aa888..4844e88a55 100644 --- a/apps/web/src/i18n/strings/el.json +++ b/apps/web/src/i18n/strings/el.json @@ -3164,7 +3164,6 @@ "user_busy_description": "Ο χρήστης που καλέσατε είναι απασχολημένος.", "user_is_presenting": "%(sharerName)s παρουσιάζει", "video_call": "Βιντεοκλήση", - "video_call_started": "Ξεκίνησε η βιντεοκλήση", "voice_call": "Φωνητική κλήση", "you_are_presenting": "Παρουσιάζετε" }, diff --git a/apps/web/src/i18n/strings/en_EN.json b/apps/web/src/i18n/strings/en_EN.json index 62104e0c61..c838f38bf8 100644 --- a/apps/web/src/i18n/strings/en_EN.json +++ b/apps/web/src/i18n/strings/en_EN.json @@ -476,6 +476,7 @@ "description": "Description", "deselect_all": "Deselect all", "device": "Device", + "disabled_by_homeserver": "Disabled by homeserver", "edited": "edited", "email_address": "Email address", "emoji": "Emoji", @@ -1540,6 +1541,11 @@ "experimental_section": "Early previews", "extended_profiles_msc_support": "Requires your server to support MSC4133", "feature_disable_call_per_sender_encryption": "Disable per-sender encryption for Element Call", + "feature_user_status": { + "description": "Enables being able to see and set a current status.", + "display_name": "User status", + "required_msc_support": "Requires MSC4429 (Profile Updates for Legacy Sync)" + }, "feature_wysiwyg_composer_description": "Use rich text instead of Markdown in the message composer.", "group_calls": "New group call experience", "group_developer": "Developer", @@ -3147,6 +3153,14 @@ "server_error_detail": "Server unavailable, overloaded, or something else went wrong.", "shrug": "Prepends ¯\\_(ツ)_/¯ to a plain-text message", "spoiler": "Sends the given message as a spoiler", + "status": { + "description": "Set your current status", + "no_args": "No arguments provided. You should supply an emoij and an optional text component.", + "no_emoji": "You did not provide an emoji", + "no_text": "You did not provide any status text", + "too_long_emoji": "The first argument must be an emoji", + "too_long_text": "The text you provided was too long." + }, "tableflip": "Prepends (╯°□°)╯︵ ┻━┻ to a plain-text message", "topic": "Gets or sets the room topic", "topic_none": "This room has no topic.", diff --git a/apps/web/src/i18n/strings/eo.json b/apps/web/src/i18n/strings/eo.json index bddde9a546..30c79b8a0b 100644 --- a/apps/web/src/i18n/strings/eo.json +++ b/apps/web/src/i18n/strings/eo.json @@ -2485,7 +2485,6 @@ "user_busy_description": "La uzanto, kiun vi vokis, estas okupata.", "user_is_presenting": "%(sharerName)s prezentas", "video_call": "Vidvoko", - "video_call_started": "Videovoko komenciĝis", "voice_call": "Voĉvoko", "you_are_presenting": "Vi prezentas" }, diff --git a/apps/web/src/i18n/strings/es.json b/apps/web/src/i18n/strings/es.json index 55a0d5c10d..98d5720c42 100644 --- a/apps/web/src/i18n/strings/es.json +++ b/apps/web/src/i18n/strings/es.json @@ -3250,7 +3250,6 @@ "user_busy_description": "La persona a la que has llamado está ocupada.", "user_is_presenting": "%(sharerName)s está presentando", "video_call": "Llamada de vídeo", - "video_call_started": "Videollamada iniciada", "voice_call": "Llamada de voz", "you_are_presenting": "Estás presentando" }, diff --git a/apps/web/src/i18n/strings/et.json b/apps/web/src/i18n/strings/et.json index 3d8f43520c..dd0138bc07 100644 --- a/apps/web/src/i18n/strings/et.json +++ b/apps/web/src/i18n/strings/et.json @@ -588,7 +588,6 @@ "video": "Video", "video_room": "Videotuba", "view_message": "Vaata sõnumit", - "voice": "Hääl", "warning": "Hoiatus" }, "composer": { @@ -3865,7 +3864,6 @@ "connection_lost": "Ühendus sinu serveriga on katkenud", "connection_lost_description": "Kui ühendus sinu serveriga on katkenud, siis sa ei saa helistada.", "consulting": "Suhtlen teise osapoolega %(transferTarget)s. Saadan andmeid kasutajale %(transferee)s", - "decline_call": "Keeldu", "default_device": "Vaikimisi seade", "dial": "Helista", "dialpad": "Numbriklahvistik", @@ -3916,7 +3914,6 @@ "show_sidebar_button": "Näita külgpaani", "silence": "Vaigista kõne", "silenced": "Teavitused on summutatud", - "skip_lobby_toggle_option": "Liitu kohe", "start_screenshare": "Alusta oma seadme ekraani jagamist", "stop_screenshare": "Lõpeta oma seadme ekraani jagamine", "too_many_calls": "Liiga palju kõnesid", @@ -3938,7 +3935,6 @@ "user_is_presenting": "%(sharerName)s esitab", "video_call": "Videokõne", "video_call_incoming": "Saabuv videokõne", - "video_call_started": "Videokõne algas", "video_call_using": "Videokõne, kus on kasutusel:", "voice_call": "Häälkõne", "voice_call_incoming": "Saaduv häälkõne", diff --git a/apps/web/src/i18n/strings/fa.json b/apps/web/src/i18n/strings/fa.json index 355487d80d..a511425eb5 100644 --- a/apps/web/src/i18n/strings/fa.json +++ b/apps/web/src/i18n/strings/fa.json @@ -2187,7 +2187,6 @@ "user_busy": "کاربر مشغول", "user_busy_description": "کاربر موردنظر مشغول است.", "video_call": "تماس تصویری", - "video_call_started": "تماس تصویری شروع شد", "voice_call": "تماس صوتی" }, "web_default_device_name": "%(appName)s: %(browserName)s: روی %(osName)s", diff --git a/apps/web/src/i18n/strings/fi.json b/apps/web/src/i18n/strings/fi.json index a5988d49b9..7abd73cce3 100644 --- a/apps/web/src/i18n/strings/fi.json +++ b/apps/web/src/i18n/strings/fi.json @@ -3364,7 +3364,6 @@ "user_busy_description": "Käyttäjä, jolle soitit, on varattu.", "user_is_presenting": "%(sharerName)s esittää", "video_call": "Videopuhelu", - "video_call_started": "Videopuhelu aloitettu", "video_call_using": "Videopuhelu käyttäen:", "voice_call": "Äänipuhelu", "you_are_presenting": "Esität parhaillaan" diff --git a/apps/web/src/i18n/strings/fr.json b/apps/web/src/i18n/strings/fr.json index ea8aaa193d..cf422de78a 100644 --- a/apps/web/src/i18n/strings/fr.json +++ b/apps/web/src/i18n/strings/fr.json @@ -588,7 +588,6 @@ "video": "Vidéo", "video_room": "Salon vidéo", "view_message": "Afficher le message", - "voice": "Voix", "warning": "Attention" }, "composer": { @@ -1368,6 +1367,14 @@ "impossible_dialog_title": "Les intégrations ne sont pas autorisées" }, "invite": { + "confirm_unknown_users": { + "invite_subtitle": "Vous n'avez actuellement aucune discussion avec ces contacts. Veuillez confirmer leur invitation à rejoindre ce salon avant de continuer.", + "invite_title": "Inviter de nouveaux contacts dans ce salon ?", + "start_chat_subtitle_multiple_users": "Vous n'avez actuellement aucune discussion avec ces personnes. Veuillez confirmer leur invitation avant de continuer.", + "start_chat_subtitle_one_user": "Vous n'avez actuellement aucune discussion avec cette personne. Confirmez son invitation avant de continuer.", + "start_chat_title_multiple_users": "Démarrer une discussion avec ces nouveaux contacts ?", + "start_chat_title_one_user": "Démarrer une discussion avec ce nouveau contact ?" + }, "email_caption": "Inviter par e-mail", "email_limit_one": "Les invitations par e-mail ne peuvent être envoyées qu’une par une", "email_use_default_is": "Utilisez un serveur d’identité pour inviter avec un e-mail. Utilisez le serveur par défaut (%(defaultIdentityServerName)s) ou gérez-le dans les Paramètres.", @@ -3886,6 +3893,16 @@ "call_held": "%(peerName)s a mis l’appel en attente", "call_held_resume": "Vous avez mis l’appel en attente Reprendre", "call_held_switch": "Vous avez mis l’appel en attente Basculer", + "call_members": { + "exhaustive": { + "one": " participant à l'appel", + "other": " participants à l'appel" + }, + "overflow": { + "one": "+ %(overflowCount)s participant à l'appel", + "other": "+ %(overflowCount)s participants à l'appel" + } + }, "call_toast_unknown_room": "Salon inconnu", "camera_disabled": "Votre caméra est éteinte", "camera_enabled": "Votre caméra est toujours allumée", @@ -3895,7 +3912,6 @@ "connection_lost": "La connexion au serveur a été perdue", "connection_lost_description": "Vous ne pouvez pas passer d’appels sans connexion au serveur.", "consulting": "Consultation avec %(transferTarget)s. Transfert à %(transferee)s", - "decline_call": "Refuser", "default_device": "Appareil par défaut", "dial": "Composer", "dialpad": "Pavé numérique", @@ -3909,10 +3925,12 @@ "enable_microphone": "Activer le microphone", "expand": "Revenir à l’appel", "get_call_link": "Partager le lien de l'appel", + "group_call_started": "L'appel de groupe a commencé", "hangup": "Raccrocher", "hide_sidebar_button": "Masquer la barre latérale", "input_devices": "Périphériques d’entrée", "jitsi_call": "Conférence Jitsi", + "join_with_video": "Rejoigner en vidéo", "legacy_call": "Appel vidéo", "maximise": "Remplir l’écran", "maximise_call": "Plein écran", @@ -3946,7 +3964,6 @@ "show_sidebar_button": "Afficher la barre latérale", "silence": "Mettre l’appel en sourdine", "silenced": "Notifications silencieuses", - "skip_lobby_toggle_option": "Rejoignez immédiatement", "start_screenshare": "Commencer à partager mon écran", "stop_screenshare": "Arrêter de partager mon écran", "too_many_calls": "Trop d’appels", @@ -3968,7 +3985,6 @@ "user_is_presenting": "%(sharerName)s est à l’écran", "video_call": "Appel vidéo", "video_call_incoming": "Appel vidéo entrant", - "video_call_started": "Appel vidéo commencé", "video_call_using": "Appel vidéo utilisant :", "voice_call": "Appel audio", "voice_call_incoming": "Appel vocal entrant", @@ -3976,6 +3992,11 @@ "you_are_presenting": "Vous êtes à l’écran" }, "web_default_device_name": "%(appName)s : %(browserName)s pour %(osName)s", + "welcome": { + "tagline_element": "Conçu pour la vitesse et la simplicité.", + "title_element": "Soyez dans votre Element", + "title_generic": "Bienvenue sur %(brand)s" + }, "widget": { "added_by": "Widget ajouté par", "capabilities_dialog": { diff --git a/apps/web/src/i18n/strings/hr.json b/apps/web/src/i18n/strings/hr.json index 0c7c1a3cfd..e568dc3f39 100644 --- a/apps/web/src/i18n/strings/hr.json +++ b/apps/web/src/i18n/strings/hr.json @@ -593,7 +593,6 @@ "video": "Videozapis", "video_room": "Videosoba", "view_message": "Prikaži poruku", - "voice": "Glas", "warning": "Upozorenje" }, "composer": { @@ -3951,7 +3950,6 @@ "connection_lost": "Veza s poslužiteljem je prekinuta", "connection_lost_description": "Ne možete upućivati ​​pozive ako niste povezani s poslužiteljem.", "consulting": "Savjetovanje s %(transferTarget)s. Prijenos na %(transferee)s", - "decline_call": "Odbij", "default_device": "Zadani uređaj", "dial": "Biranje", "dialpad": "Brojčanik", @@ -4003,7 +4001,6 @@ "show_sidebar_button": "Prikaži bočnu traku", "silence": "Utišaj poziv", "silenced": "Obavijesti su utišane", - "skip_lobby_toggle_option": "Pridruži se odmah", "start_screenshare": "Započni dijeljenje zaslona", "stop_screenshare": "Zaustavi dijeljenje zaslona", "too_many_calls": "Previše poziva", @@ -4025,7 +4022,6 @@ "user_is_presenting": "%(sharerName)s dijeli zaslon", "video_call": "Videopoziv", "video_call_incoming": "Dolazni videopoziv", - "video_call_started": "Videopoziv je započeo", "video_call_using": "Videopoziv putem:", "voice_call": "Glasovni poziv", "voice_call_incoming": "Dolazni glasovni poziv", diff --git a/apps/web/src/i18n/strings/hu.json b/apps/web/src/i18n/strings/hu.json index c57bb688e5..e8ed517044 100644 --- a/apps/web/src/i18n/strings/hu.json +++ b/apps/web/src/i18n/strings/hu.json @@ -586,7 +586,6 @@ "video": "Videó", "video_room": "Videószoba", "view_message": "Üzenet megjelenítése", - "voice": "Hang", "warning": "Figyelmeztetés" }, "composer": { @@ -3860,7 +3859,6 @@ "connection_lost": "Megszakadt a kapcsolat a kiszolgálóval", "connection_lost_description": "Nem kezdeményezhet hívást a kiszolgálóval való kapcsolat nélkül.", "consulting": "Egyeztetés vele: %(transferTarget)s. Átadás ide: %(transferee)s", - "decline_call": "Elutasítás", "default_device": "Alapértelmezett eszköz", "dial": "Tárcsázás", "dialpad": "Tárcsázó", @@ -3910,7 +3908,6 @@ "show_sidebar_button": "Oldalsáv megjelenítése", "silence": "Hívás némítása", "silenced": "Értesítések némítva", - "skip_lobby_toggle_option": "Csatlakozás azonnal", "start_screenshare": "Képernyőmegosztás bekapcsolása", "stop_screenshare": "Képernyőmegosztás kikapcsolása", "too_many_calls": "Túl sok hívás", @@ -3932,7 +3929,6 @@ "user_is_presenting": "%(sharerName)s tartja a bemutatót", "video_call": "Videóhívás", "video_call_incoming": "Bejövő videóhívás", - "video_call_started": "A videóhívás elindult", "video_call_using": "Videóhívás:", "voice_call": "Hanghívás", "voice_call_incoming": "Bejövő hanghívás", diff --git a/apps/web/src/i18n/strings/hy.json b/apps/web/src/i18n/strings/hy.json index 69599a6d2e..39a0596f7f 100644 --- a/apps/web/src/i18n/strings/hy.json +++ b/apps/web/src/i18n/strings/hy.json @@ -3839,7 +3839,6 @@ "user_busy_description": "Ձեր զանգահարած օգտատերը զբաղված է։", "user_is_presenting": "%(sharerName)s-ը ներկայացնում է", "video_call": "Տեսազանգ", - "video_call_started": "Տեսազանգը սկսվեց", "video_call_using": "Տեսազանգ՝ օգտագործելով՝", "voice_call": "Ձայնային զանգ", "you_are_presenting": "Դուք ներկայացնում եք" diff --git a/apps/web/src/i18n/strings/id.json b/apps/web/src/i18n/strings/id.json index 6868a9c8cd..72e200fb87 100644 --- a/apps/web/src/i18n/strings/id.json +++ b/apps/web/src/i18n/strings/id.json @@ -588,7 +588,6 @@ "video": "Video", "video_room": "Ruangan video", "view_message": "Tampilkan pesan", - "voice": "Suara", "warning": "Peringatan" }, "composer": { @@ -3852,7 +3851,6 @@ "connection_lost": "Koneksi ke server telah hilang", "connection_lost_description": "Anda tidak dapat membuat panggilan tanpa terhubung ke server.", "consulting": "Mengkonsultasi dengan %(transferTarget)s. Transfer ke %(transferee)s", - "decline_call": "Tolak", "default_device": "Perangkat Bawaan", "dial": "Panggil", "dialpad": "Tombol Penyetel", @@ -3903,7 +3901,6 @@ "show_sidebar_button": "Tampilkan sisi bilah", "silence": "Diamkan panggilan", "silenced": "Notifikasi dibisukan", - "skip_lobby_toggle_option": "Segera bergabung", "start_screenshare": "Mulai membagikan layar Anda", "stop_screenshare": "Berhenti membagikan layar Anda", "too_many_calls": "Terlalu Banyak Panggilan", @@ -3925,7 +3922,6 @@ "user_is_presenting": "%(sharerName)s sedang mempresentasi", "video_call": "Panggilan video", "video_call_incoming": "Panggilan video masuk", - "video_call_started": "Panggilan video dimulai", "video_call_using": "Panggilan video menggunakan:", "voice_call": "Panggilan suara", "voice_call_incoming": "Panggilan suara masuk", diff --git a/apps/web/src/i18n/strings/is.json b/apps/web/src/i18n/strings/is.json index b7fad1bd7a..ecf64657ee 100644 --- a/apps/web/src/i18n/strings/is.json +++ b/apps/web/src/i18n/strings/is.json @@ -2824,7 +2824,6 @@ "user_busy_description": "Notandinn sem þú hringdir í er upptekinn.", "user_is_presenting": "%(sharerName)s er að kynna", "video_call": "Myndsímtal", - "video_call_started": "Myndsímtal er byrjað", "voice_call": "Raddsímtal", "you_are_presenting": "Þú ert að kynna" }, diff --git a/apps/web/src/i18n/strings/it.json b/apps/web/src/i18n/strings/it.json index 07296b29f9..8bfd4c3dfa 100644 --- a/apps/web/src/i18n/strings/it.json +++ b/apps/web/src/i18n/strings/it.json @@ -3412,7 +3412,6 @@ "user_busy_description": "L'utente che hai chiamato è occupato.", "user_is_presenting": "%(sharerName)s sta presentando", "video_call": "Videochiamata", - "video_call_started": "Videochiamata iniziata", "video_call_using": "Videochiamata usando:", "voice_call": "Telefonata", "you_are_presenting": "Stai presentando" diff --git a/apps/web/src/i18n/strings/ja.json b/apps/web/src/i18n/strings/ja.json index 74abafc872..f7f4cb95b5 100644 --- a/apps/web/src/i18n/strings/ja.json +++ b/apps/web/src/i18n/strings/ja.json @@ -3136,7 +3136,6 @@ "user_busy_description": "呼び出したユーザーは通話中です。", "user_is_presenting": "%(sharerName)sが画面を共有しています", "video_call": "ビデオ通話", - "video_call_started": "ビデオ通話を開始しました", "voice_call": "音声通話", "you_are_presenting": "あなたが画面を共有しています" }, diff --git a/apps/web/src/i18n/strings/ka.json b/apps/web/src/i18n/strings/ka.json index 9c14feaf3a..4e949f5cb2 100644 --- a/apps/web/src/i18n/strings/ka.json +++ b/apps/web/src/i18n/strings/ka.json @@ -2669,7 +2669,6 @@ "user_busy_description": "მომხმარებელი, რომელსაც დაურეკე, დაკავებულია.", "user_is_presenting": "%(sharerName)sწარმოადგენს", "video_call": "ვიდეო ზარი", - "video_call_started": "ვიდეო ზარი დაიწყო", "voice_call": "ხმოვანი ზარი", "you_are_presenting": "წარმოგიდგენთ" }, diff --git a/apps/web/src/i18n/strings/ko.json b/apps/web/src/i18n/strings/ko.json index fcbf1776ef..c453a1f0de 100644 --- a/apps/web/src/i18n/strings/ko.json +++ b/apps/web/src/i18n/strings/ko.json @@ -583,7 +583,6 @@ "video": "비디오", "video_room": "비디오 room", "view_message": "메시지 보기", - "voice": "음성", "warning": "경고" }, "composer": { @@ -3785,7 +3784,6 @@ "connection_lost": "서버와의 연결이 끊어졌습니다.", "connection_lost_description": "서버에 연결되지 않으면 전화를 걸 수 없습니다.", "consulting": "%(transferTarget)s와 상담 중입니다. %(transferee)s에게 통화 이전", - "decline_call": "거부", "default_device": "기본 기기", "dial": "전화걸기", "dialpad": "다이얼패드", @@ -3835,7 +3833,6 @@ "show_sidebar_button": "사이드바 표시", "silence": "통화 음소거", "silenced": "알림 음소거됨", - "skip_lobby_toggle_option": "즉시 가입하세요", "start_screenshare": "화면 공유를 시작하세요", "stop_screenshare": "화면 공유 중지", "too_many_calls": "전화가 너무 많아요", @@ -3857,7 +3854,6 @@ "user_is_presenting": "%(sharerName)s님이 화면을 공유중입니다.", "video_call": "영상 통화", "video_call_incoming": "수신 영상 통화", - "video_call_started": "영상통화가 시작되었습니다", "video_call_using": "사용 중인 영상 통화:", "voice_call": "음성 통화", "voice_call_incoming": "수신 음성 통화", diff --git a/apps/web/src/i18n/strings/lt.json b/apps/web/src/i18n/strings/lt.json index 2ff4a8ad86..04a9fad051 100644 --- a/apps/web/src/i18n/strings/lt.json +++ b/apps/web/src/i18n/strings/lt.json @@ -94,7 +94,7 @@ "show_advanced": "Rodyti išplėstinius", "show_all": "Rodyti viską", "sign_in": "Prisijungti", - "sign_out": "Atsijungti", + "sign_out": "Šalinti šį įrenginį", "skip": "Praleisti", "start": "Pradėti", "start_chat": "Pradėti pokalbį", @@ -208,7 +208,7 @@ "set_email": { "description": "Tai jums leis iš naujo nustatyti slaptažodį ir gauti pranešimus.", "verification_pending_description": "Patikrinkite savo el. laišką ir spustelėkite jame esančią nuorodą. Kai tai padarysite, spauskite tęsti.", - "verification_pending_title": "Laukiama Patikrinimo" + "verification_pending_title": "Laukiama patvirtinimo" }, "set_email_prompt": "Ar norite nustatyti el. pašto adresą?", "sign_in_instead_prompt": "Jau turite paskyrą? Prisijunkite čia", @@ -402,8 +402,13 @@ "format_code_block": "Kodo blokas", "format_inline_code": "Kodas", "format_insert_link": "Įterpti nuorodą", + "format_italic": "Kursyvas", "format_italics": "Kursyvas", + "format_link": "Nuoroda", + "format_ordered_list": "Sunumeruotas sąrašas", "format_strikethrough": "Perbrauktas", + "format_underline": "Pabraukimas", + "format_unordered_list": "Suženklintasis sąrašas", "no_perms_notice": "Jūs neturite leidimų rašyti šiame kambaryje", "placeholder": "Siųsti žinutę…", "placeholder_encrypted": "Siųsti šifruotą žinutę…", @@ -424,6 +429,8 @@ "voice_message_button": "Balso žinutė" }, "create_room": { + "action_create_room": "Kurti kambarį", + "action_create_video_room": "Kurti vaizdo kambarį", "encryption_forced": "Jūsų serveris reikalauja, kad šifravimas būtų įjungtas privačiuose kambariuose.", "encryption_label": "Įjungti visapusį šifravimą", "error_title": "Nepavyko sukurti kambario", @@ -431,6 +438,7 @@ "name_validation_required": "Įveskite kambario pavadinimą", "title_private_room": "Sukurti privatų kambarį", "title_public_room": "Sukurti viešą kambarį", + "title_video_room": "Kurti vaizdo pokalbių kambarį", "topic_label": "Tema (nebūtina)", "unfederated": "Blokuoti bet ką, kas nėra iš %(serverName)s, niekada nebeleidžiant prisijungti prie šio kambario.", "unfederated_label_default_off": "Jūs galite tai įjungti, jei kambarys bus naudojamas tik bendradarbiavimui su vidinėmis komandomis jūsų serveryje. Tai negali būti vėliau pakeista.", @@ -461,16 +469,33 @@ "event_content": "Įvykio turinys", "event_sent": "Įvykis išsiųstas!", "event_type": "Įvykio tipas", + "explore_account_data": "Naršyti paskyros duomenis", + "explore_room_account_data": "Naršyti kambario paskyros duomenis", + "explore_room_state": "Naršyti kambario būseną", "failed_to_find_widget": "Įvyko klaida ieškant šio valdiklio.", + "failed_to_send": "Nepavyko išsiųsti įvykio.", + "invalid_json": "Neatrodo kaip tinkamas JSON.", "level": "Lygis", + "no_receipt_found": "Nerasta kvito.", + "notifications_debug": "Pranešimų derinimas", "original_event_source": "Originalus įvykio šaltinis", + "room_id": "Kambario ID: %(roomId)s", + "send_custom_account_data_event": "Siųsti pasirinktinius paskyros duomenis įvykį", + "send_custom_room_account_data_event": "Siųsti pasirinktinius kambario paskyros duomenis įvykį", + "send_custom_timeline_event": "Siųsti pasirinktinės laiko skalės įvykį", + "server_info": "Serverio informacija", "setting_colon": "Nustatymas:", "setting_id": "Nustatymo ID", + "settings_explorer": "Nustatymų naršyklė", "show_hidden_events": "Rodyti paslėptus įvykius laiko juostoje", "state_key": "Būklės raktas", + "thread_root_id": "Gijos šaknies ID: %(threadRootId)s", "title": "Kūrėjo įrankiai", "toolbox": "Įrankinė", + "user_read_up_to": "Naudotojas perskaitė iki: ", + "user_read_up_to_ignore_synthetic": "Naudotojas perskaitė iki (ignoreSynthetic): ", "value": "Reikšmė", + "view_servers_in_room": "Peržiūrėti serverius kambaryje", "widget_screenshots": "Įjungti valdiklių ekrano kopijas palaikomuose valdikliuose" }, "dialog_close_label": "Uždaryti dialogą", @@ -581,7 +606,7 @@ "unverified_session_toast_title": "Naujas prisijungimas. Ar tai jūs?", "unverified_sessions_toast_description": "Peržiūrėkite, ar jūsų paskyra yra saugi", "unverified_sessions_toast_reject": "Vėliau", - "verification_dialog_title_user": "Patikrinimo Užklausa", + "verification_dialog_title_user": "Patvirtinimo prašymas", "verify_emoji": "Patvirtinti naudojant jaustukus", "verify_emoji_prompt": "Patvirtinti palyginant unikalius jaustukus.", "verify_emoji_prompt_qr": "Jei nuskaityti aukščiau esančio kodo negalite, patvirtinkite palygindami unikalius jaustukus.", @@ -591,7 +616,8 @@ "waiting_other_user": "Laukiama kol %(displayName)s patvirtins…" }, "verify_toast_description": "Kiti vartotojai gali nepasitikėti", - "verify_toast_title": "Patvirtinti šį seansą" + "verify_toast_title": "Patvirtinti šį seansą", + "withdraw_verification_action": "Atšaukti patvirtinimą" }, "error": { "admin_contact_short": "Susisiekite su savo serverio administratoriumi.", @@ -798,7 +824,9 @@ }, "keyboard": { "activate_button": "Aktyvuoti pasirinktą mygtuką", + "alt": "Alt", "autocomplete_cancel": "Atšaukti automatinį užbaigimą", + "backspace": "Naikinimo klavišas", "cancel_reply": "Atšaukti atsakymą į žinutę", "category_autocomplete": "Autorašymas", "category_calls": "Skambučiai", @@ -809,29 +837,42 @@ "composer_toggle_bold": "Perjungti paryškinimą", "composer_toggle_italics": "Perjungti kursyvą", "composer_toggle_quote": "Perjungti citatą", + "control": "Vald", "dismiss_read_marker_and_jump_bottom": "Atsisakyti skaitymo žymeklio ir nušokti į apačią", + "end": "Pab", + "enter": "Įvesti", + "escape": "Gr", "home": "Pradžia", "jump_room_search": "Nušokti į kambarių paiešką", "jump_to_read_marker": "Nušokti iki seniausios neperskaitytos žinutės", "number": "[skaičius]", + "page_down": "Puslapis žemyn", + "page_up": "Puslapis aukštyn", "room_list_collapse_section": "Sutraukti kambarių sąrašo skyrių", "room_list_expand_section": "Išplėsti kambarių sąrašo skyrių", "room_list_select_room": "Pasirinkti kambarį iš kambarių sąrašo", "search": "Paieška (turi būti įjungta)", + "shift": "Lyg2", + "space": "Tarpas", "toggle_microphone_mute": "Perjungti mikrofono nutildymą", "toggle_right_panel": "Perjungti dešinį skydelį", "toggle_top_left_menu": "Perjungti viršutinį kairės pusės meniu", "upload_file": "Įkelti failą" }, "labs": { + "ask_to_join": "Įjungti prašymą jungtis", "beta_feedback_leave_button": "Norėdami išeiti iš beta versijos, apsilankykite savo nustatymuose.", "bridge_state": "Rodyti informaciją apie tiltus kambario nustatymuose", "bridge_state_channel": "Kanalas: ", "bridge_state_creator": "Šis tiltas buvo parūpintas .", "bridge_state_manager": "Šis tiltas yra tvarkomas .", "bridge_state_workspace": "Darbo aplinka: ", + "currently_experimental": "Šiuo metu eksperimentinė.", "custom_themes": "Palaikykite pridėdami pasirinktines temas", + "dynamic_room_predecessors": "Dinaminiai kambario pirmtakai", "element_call_video_rooms": "Element skambučio vaizdo kambariai", + "feature_wysiwyg_composer_description": "Naudoti raiškųjį tekstą vietoj ženklinimo žinučių rengyklėje.", + "group_calls": "Nauja grupinio skambučio patirtis", "group_developer": "Kūrėjas", "group_encryption": "Šifravimas", "group_experimental": "Eksperimentinis", @@ -843,13 +884,21 @@ "group_themes": "Temos", "group_voip": "Garsas ir Vaizdas", "group_widgets": "Valdikliai", + "hidebold": "Slėpti pranešimų tašką (rodyti tik skaičių žymes)", "html_topic": "Rodyti kambarių temų HTML atvaizdavimą", "jump_to_date": "Pereiti prie datos (prideda /jumptodate ir perėjimo prie datos antraštes)", + "jump_to_date_msc_support": "Privaloma, kad jūsų serveris palaikytų MSC3030.", "latex_maths": "Atvaizduoti LaTeX matematikas žinutėse", "leave_beta": "Palikti beta versiją", + "location_share_live": "Tiesioginis vietos bendrinimas", + "location_share_live_description": "Laikinas įgyvendinimas. Vietos išlieka kambario istorijoje.", + "mjolnir": "Nauji būdai ignoruoti žmones", "msc3531_hide_messages_pending_moderation": "Leisti moderatoriams slėpti žinutes, laukiančias moderavimo.", "notification_settings": "Nauji pranešimų nustatymai", "report_to_moderators": "Pranešti prižiūrėtojams", + "sliding_sync": "Slankiojo sinchronizavimo režimas", + "sliding_sync_description": "Aktyviai kuriama, negalima išjungti. Šiuo metu nesuderinama su „Element“ skambučiais.", + "under_active_development": "Šiuo metu aktyviai kuriama.", "video_rooms": "Vaizdo kambariai", "video_rooms_a_new_way_to_chat": "Naujas būdas kalbėtis balsu ir vaizdu per %(brand)s.", "video_rooms_always_on_voip_channels": "Vaizdo kambariai - tai visada veikiantys VoIP kanalai, įterpti į %(brand)s kambarį.", @@ -951,6 +1000,7 @@ }, "powered_by_matrix": "Veikia su Matrix", "presence": { + "away": "Pasitraukę", "busy": "Užsiėmęs", "idle": "Neveiklus", "idle_for": "Neveiklus %(duration)s", @@ -1414,8 +1464,10 @@ "big_emoji": "Įjungti didelius jaustukus pokalbiuose", "code_block_expand_default": "Išplėsti kodo blokus pagal nutylėjimą", "code_block_line_numbers": "Rodyti eilučių numerius kodo blokuose", + "disable_historical_profile": "Rodyti dabartinę profilio nuotrauką ir vardą naudotojams žinučių istorijoje", "emoji_autocomplete": "Įjungti Jaustukų pasiūlymus rašant", "enable_markdown": "Įjungti Markdown", + "enable_markdown_description": "Pradėkite žinutes su /plain norint siųsti be ženklinimo.", "general": { "account_management_section": "Paskyros tvarkymas", "account_section": "Paskyra", @@ -1466,7 +1518,7 @@ "remove_email_prompt": "Pašalinti %(email)s?", "remove_msisdn_prompt": "Pašalinti %(phone)s?" }, - "inline_url_previews_default": "Įjungti URL nuorodų peržiūras kaip numatytasias", + "inline_url_previews_default": "Įjungti peržiūras", "insert_trailing_colon_mentions": "Įterpti dvitaškį po naudotojo paminėjimų žinutės pradžioje", "jump_to_bottom_on_send": "Peršokti į laiko juostos apačią, kai siunčiate žinutę", "key_backup": { @@ -1624,10 +1676,12 @@ "verified_sessions_list_description": "Geriausiam saugumui, atsijunkite iš bet kurios sesijos, kurios neatpažįstate arba nebenaudojate.", "verify_session": "Patvirtinti seansą" }, + "show_avatar_changes": "Rodyti profilio nuotraukos pakeitimus", "show_breadcrumbs": "Rodyti neseniai peržiūrėtų kambarių nuorodas virš kambarių sąrašo", "show_chat_effects": "Rodyti pokalbių efektus (animaciją, kai gaunate, pvz., konfeti)", "show_displayname_changes": "Rodyti rodomo vardo pakeitimus", "show_join_leave": "Rodyti prisijungimo/išėjimo žinutes (kvietimai/pašalinimai/blokavimai neturi įtakos)", + "show_nsfw_content": "Rodyti NSD turinį", "show_read_receipts": "Rodyti kitų vartotojų siųstus perskaitymo kvitus", "show_redaction_placeholder": "Rodyti pašalintų žinučių žymeklį", "show_stickers_button": "Rodyti lipdukų mygtuką", @@ -1905,8 +1959,8 @@ "m.room.member": { "accepted_3pid_invite": "%(targetName)s priėmė kvietimą, skirtą %(displayName)s", "accepted_invite": "%(targetName)s priėmė kvietimą", - "ban": "%(senderName)s uždraudė %(targetName)s", - "ban_reason": "%(senderName)s uždraudė %(targetName)s%(targetName)s: %(reason)s", + "ban": "%(senderName)s uždraudė naudotoją", + "ban_reason": "%(senderName)s uždraudė naudotoją: %(reason)s", "change_avatar": "%(senderName)s pakeitė savo profilio nuotrauką", "change_name": "%(oldDisplayName)s pasikeitė savo rodomą vardą į %(displayName)s", "change_name_avatar": "%(oldDisplayName)s pakeitė savo rodomą vardą ir profilio nuotrauką", @@ -1994,7 +2048,8 @@ "pending_moderation": "Žinutė laukia moderavimo", "pending_moderation_reason": "Žinutė laukia moderavimo: %(reason)s", "reactions": { - "add_reaction_prompt": "Pridėti reakciją" + "add_reaction_prompt": "Pridėti reakciją", + "label": "%(reactors)s sureagavo su %(content)s" }, "read_receipt_title": { "one": "Matė %(count)s žmogus", diff --git a/apps/web/src/i18n/strings/lv.json b/apps/web/src/i18n/strings/lv.json index d0c60566f7..f32957557b 100644 --- a/apps/web/src/i18n/strings/lv.json +++ b/apps/web/src/i18n/strings/lv.json @@ -3354,7 +3354,6 @@ "user_busy_description": "Lietotājs, kuram zvanāt, ir aizņemts.", "user_is_presenting": "%(sharerName)s prezentē", "video_call": "Video zvans", - "video_call_started": "Videozvans uzsākts", "voice_call": "Balss zvans", "you_are_presenting": "Jūs prezentējat" }, diff --git a/apps/web/src/i18n/strings/mg_MG.json b/apps/web/src/i18n/strings/mg_MG.json index c290f91b3e..0fd9d0e609 100644 --- a/apps/web/src/i18n/strings/mg_MG.json +++ b/apps/web/src/i18n/strings/mg_MG.json @@ -3394,7 +3394,6 @@ "user_busy_description": "Mbola Sahirana ny mpampiasa niantsoanao.", "user_is_presenting": "%(sharerName)sIzao", "video_call": "Antso an-tsary", - "video_call_started": "Manomboka ny antso an-tsary", "video_call_using": "Antso an-tsary mampiasa:", "voice_call": "Antso an-tariby", "you_are_presenting": "Mamkahalala anareo" diff --git a/apps/web/src/i18n/strings/nb_NO.json b/apps/web/src/i18n/strings/nb_NO.json index 78bee47967..6ca18886c7 100644 --- a/apps/web/src/i18n/strings/nb_NO.json +++ b/apps/web/src/i18n/strings/nb_NO.json @@ -588,7 +588,6 @@ "video": "Video", "video_room": "Videorom", "view_message": "Se melding", - "voice": "Tale", "warning": "Advarsel" }, "composer": { @@ -3859,7 +3858,6 @@ "connection_lost": "Mistet forbindelsen til serveren", "connection_lost_description": "Du kan ikke ringe uten tilkobling til serveren.", "consulting": "Rådføring med %(transferTarget)s. Overfør til %(transferee)s", - "decline_call": "Avslå", "default_device": "Standardenhet", "dial": "Ring", "dialpad": "Nummerpanel", @@ -3910,7 +3908,6 @@ "show_sidebar_button": "Vis sidepanel", "silence": "Demp samtale", "silenced": "Varslinger er dempet", - "skip_lobby_toggle_option": "Bli med umiddelbart", "start_screenshare": "Begynn å dele skjermen din", "stop_screenshare": "Slutt å dele skjermen din", "too_many_calls": "For mange samtaler", @@ -3932,7 +3929,6 @@ "user_is_presenting": "%(sharerName)s presenterer", "video_call": "Videosamtale", "video_call_incoming": "Innkommende videosamtale", - "video_call_started": "Videosamtale startet", "video_call_using": "Videosamtale ved hjelp av:", "voice_call": "Stemmesamtale", "voice_call_incoming": "Innkommende taleanrop", diff --git a/apps/web/src/i18n/strings/nl.json b/apps/web/src/i18n/strings/nl.json index 7d3426c61f..bb655923b4 100644 --- a/apps/web/src/i18n/strings/nl.json +++ b/apps/web/src/i18n/strings/nl.json @@ -2960,7 +2960,6 @@ "user_busy_description": "De persoon die je belde is bezet.", "user_is_presenting": "%(sharerName)s is aan het presenteren", "video_call": "Video-oproep", - "video_call_started": "Videogesprek gestart", "voice_call": "Spraakoproep", "you_are_presenting": "Je bent aan het presenteren" }, diff --git a/apps/web/src/i18n/strings/pl.json b/apps/web/src/i18n/strings/pl.json index 4a5428e5eb..67a6fbb35b 100644 --- a/apps/web/src/i18n/strings/pl.json +++ b/apps/web/src/i18n/strings/pl.json @@ -3829,7 +3829,6 @@ "connection_lost": "Połączenie z serwerem zostało przerwane", "connection_lost_description": "Nie możesz wykonywać rozmów bez połączenia z serwerem.", "consulting": "Konsultowanie z %(transferTarget)s. Transfer do %(transferee)s", - "decline_call": "Odrzuć", "default_device": "Urządzenie domyślne", "dial": "Wybierz numer", "dialpad": "Klawiatura telefoniczna", @@ -3881,7 +3880,6 @@ "show_sidebar_button": "Pokaż pasek boczny", "silence": "Wycisz rozmowę", "silenced": "Wyciszono powiadomienia", - "skip_lobby_toggle_option": "Dołącz teraz", "start_screenshare": "Udostępnij ekran", "stop_screenshare": "Przestań udostępniać ekran", "too_many_calls": "Zbyt wiele połączeń", @@ -3902,7 +3900,6 @@ "user_busy_description": "Użytkownik, do którego zadzwoniłeś jest zajęty.", "user_is_presenting": "%(sharerName)s prezentuje", "video_call": "Rozmowa wideo", - "video_call_started": "Rozpoczęto rozmowę wideo", "video_call_using": "Połączenie wideo przy użyciu:", "voice_call": "Rozmowa głosowa", "you_are_presenting": "Prezentujesz" diff --git a/apps/web/src/i18n/strings/pt.json b/apps/web/src/i18n/strings/pt.json index cbe8504bd2..d9f9f56f7a 100644 --- a/apps/web/src/i18n/strings/pt.json +++ b/apps/web/src/i18n/strings/pt.json @@ -3750,7 +3750,6 @@ "user_busy_description": "O utilizador para o qual tentou ligar está ocupado.", "user_is_presenting": "%(sharerName)s está apresentando", "video_call": "Chamada de vídeo", - "video_call_started": "Chamada de vídeo iniciada", "video_call_using": "Video-chamada usando:", "voice_call": "Chamada de voz", "you_are_presenting": "Estás a apresentar" diff --git a/apps/web/src/i18n/strings/pt_BR.json b/apps/web/src/i18n/strings/pt_BR.json index c89dadaa45..8223facad5 100644 --- a/apps/web/src/i18n/strings/pt_BR.json +++ b/apps/web/src/i18n/strings/pt_BR.json @@ -587,7 +587,6 @@ "video": "Vídeo", "video_room": "Sala de vídeo", "view_message": "Ver mensagem", - "voice": "Voz", "warning": "Atenção" }, "composer": { @@ -3834,7 +3833,6 @@ "connection_lost": "A conectividade com o servidor foi perdida", "connection_lost_description": "Você não pode fazer chamadas sem uma conexão com o servidor.", "consulting": "Consultar com %(transferTarget)s. Tranferir para %(transferee)s", - "decline_call": "Recusar", "default_device": "Aparelho padrão", "dial": "Discar", "dialpad": "Teclado de discagem", @@ -3885,7 +3883,6 @@ "show_sidebar_button": "Exibir a barra lateral", "silence": "Silenciar chamado", "silenced": "Notificações silenciadas", - "skip_lobby_toggle_option": "Junte-se imediatamente", "start_screenshare": "Começar a compartilhar sua tela", "stop_screenshare": "Parar de compartilhar sua tela", "too_many_calls": "Muitas chamadas", @@ -3907,7 +3904,6 @@ "user_is_presenting": "%(sharerName)s está apresentando", "video_call": "Chamada de vídeo", "video_call_incoming": "Chamada de vídeo recebida", - "video_call_started": "Videochamada iniciada", "video_call_using": "Chamada de vídeo usando:", "voice_call": "Chamada de voz", "voice_call_incoming": "Chamada de voz recebida", diff --git a/apps/web/src/i18n/strings/ru.json b/apps/web/src/i18n/strings/ru.json index 1bbf081882..5c1d981525 100644 --- a/apps/web/src/i18n/strings/ru.json +++ b/apps/web/src/i18n/strings/ru.json @@ -589,7 +589,6 @@ "video": "Видео", "video_room": "Видеочат", "view_message": "Посмотреть сообщение", - "voice": "Голос", "warning": "Внимание" }, "composer": { @@ -681,6 +680,11 @@ "unfederated_label_default_on": "Вы можете отключить это, если комната будет использоваться для совместной работы с внешними командами, у которых есть собственный домашний сервер. Это не может быть изменено позже.", "unsupported_version": "Сервер не поддерживает указанную версию чата." }, + "create_section_dialog": { + "create_section": "Новый раздел", + "label": "Название раздела", + "title": "Новый раздел" + }, "create_space": { "add_details_prompt": "Добавьте некоторые подробности, чтобы помочь людям узнать его.", "add_details_prompt_2": "Вы можете изменить их в любое время.", @@ -3900,7 +3904,6 @@ "connection_lost": "Соединение с сервером потеряно", "connection_lost_description": "Вы не можете совершать вызовы без подключения к серверу.", "consulting": "Общение с %(transferTarget)s. Перевод на %(transferee)s", - "decline_call": "Отклонить", "default_device": "Устройство по умолчанию", "dial": "Набор", "dialpad": "Панель набора номера", @@ -3951,7 +3954,6 @@ "show_sidebar_button": "Показать боковую панель", "silence": "Тихий вызов", "silenced": "Оповещения приглушены", - "skip_lobby_toggle_option": "Присоединиться прямо сейчас", "start_screenshare": "Начать делиться экраном", "stop_screenshare": "Перестать делиться экраном", "too_many_calls": "Слишком много звонков", @@ -3973,7 +3975,6 @@ "user_is_presenting": "%(sharerName)s показывает", "video_call": "Видеовызов", "video_call_incoming": "Входящий видеозвонок", - "video_call_started": "Начался видеозвонок", "video_call_using": "Видеозвонок с использованием:", "voice_call": "Голосовой вызов", "voice_call_incoming": "Входящий голосовой вызов", diff --git a/apps/web/src/i18n/strings/sk.json b/apps/web/src/i18n/strings/sk.json index 667307f088..d840d86287 100644 --- a/apps/web/src/i18n/strings/sk.json +++ b/apps/web/src/i18n/strings/sk.json @@ -593,7 +593,6 @@ "video": "Video", "video_room": "Video miestnosť", "view_message": "Zobraziť správu", - "voice": "Hlas", "warning": "Upozornenie" }, "composer": { @@ -3938,7 +3937,6 @@ "connection_lost": "Spojenie so serverom bolo prerušené", "connection_lost_description": "Bez pripojenia k serveru nie je možné uskutočňovať hovory.", "consulting": "Konzultovanie s %(transferTarget)s. Presmerovanie na %(transferee)s", - "decline_call": "Zamietnuť", "default_device": "Predvolené zariadenie", "dial": "Vytočiť číslo", "dialpad": "Číselník", @@ -3990,7 +3988,6 @@ "show_sidebar_button": "Zobraziť bočný panel", "silence": "Stlmiť hovor", "silenced": "Oznámenia stlmené", - "skip_lobby_toggle_option": "Pripojiť sa okamžite", "start_screenshare": "Spustiť zdieľanie vašej obrazovky", "stop_screenshare": "Zastaviť zdieľanie vašej obrazovky", "too_many_calls": "Príliš veľa hovorov", @@ -4012,7 +4009,6 @@ "user_is_presenting": "%(sharerName)s prezentuje", "video_call": "Video hovor", "video_call_incoming": "Prichádzajúci videohovor", - "video_call_started": "Videohovor bol spustený", "video_call_using": "Videohovor pomocou:", "voice_call": "Hlasový hovor", "voice_call_incoming": "Prichádzajúci hlasový hovor", diff --git a/apps/web/src/i18n/strings/sq.json b/apps/web/src/i18n/strings/sq.json index db8ea4f362..66c61e14f0 100644 --- a/apps/web/src/i18n/strings/sq.json +++ b/apps/web/src/i18n/strings/sq.json @@ -3196,7 +3196,6 @@ "user_busy_description": "Përdoruesi që thirrët është i zënë.", "user_is_presenting": "%(sharerName)s përfaqëson", "video_call": "Thirrje video", - "video_call_started": "Nisi thirrje me video", "voice_call": "Thirrje audio", "you_are_presenting": "Përfaqësoni" }, diff --git a/apps/web/src/i18n/strings/sv.json b/apps/web/src/i18n/strings/sv.json index 52aa32e1e6..1dbaf29712 100644 --- a/apps/web/src/i18n/strings/sv.json +++ b/apps/web/src/i18n/strings/sv.json @@ -3836,7 +3836,6 @@ "user_busy_description": "Användaren du ringde är upptagen.", "user_is_presenting": "%(sharerName)s presenterar", "video_call": "Videosamtal", - "video_call_started": "Videosamtal startat", "video_call_using": "Videosamtal med hjälp av:", "voice_call": "Röstsamtal", "you_are_presenting": "Du presenterar" diff --git a/apps/web/src/i18n/strings/tr.json b/apps/web/src/i18n/strings/tr.json index f98c5870f4..8c7f71555c 100644 --- a/apps/web/src/i18n/strings/tr.json +++ b/apps/web/src/i18n/strings/tr.json @@ -3736,7 +3736,6 @@ "user_busy_description": "Aradığınız kullanıcı meşgul.", "user_is_presenting": "%(sharerName)s sunum yapıyor", "video_call": "Görüntülü arama", - "video_call_started": "Görüntülü arama başlatıldı", "video_call_using": "Görüntülü arama kullanılıyor:", "voice_call": "Sesli arama", "you_are_presenting": "Sunum yapıyorsunuz" diff --git a/apps/web/src/i18n/strings/uk.json b/apps/web/src/i18n/strings/uk.json index 5b64446e6d..a1aab9a0fe 100644 --- a/apps/web/src/i18n/strings/uk.json +++ b/apps/web/src/i18n/strings/uk.json @@ -589,7 +589,6 @@ "video": "Відео", "video_room": "Відеокімната", "view_message": "Переглянути повідомлення", - "voice": "Голос", "warning": "Попередження" }, "composer": { @@ -3870,7 +3869,6 @@ "connection_lost": "Втрачено зʼєднання з сервером", "connection_lost_description": "Неможливо здійснювати виклики без з'єднання з сервером.", "consulting": "Консультація з %(transferTarget)s. Переадресація на %(transferee)s", - "decline_call": "Відхилити", "default_device": "Уставний пристрій", "dial": "Виклик", "dialpad": "Номеронабирач", @@ -3922,7 +3920,6 @@ "show_sidebar_button": "Показати бічну панель", "silence": "Тихий виклик", "silenced": "Сповіщення стишено", - "skip_lobby_toggle_option": "Приєднатися негайно", "start_screenshare": "Почати показ екрана", "stop_screenshare": "Вимкнути показ екрана", "too_many_calls": "Забагато викликів", @@ -3944,7 +3941,6 @@ "user_is_presenting": "%(sharerName)s показує", "video_call": "Відеовиклик", "video_call_incoming": "Вхідний відеовиклик", - "video_call_started": "Відеовиклик розпочато", "video_call_using": "Відеодзвінок за допомогою:", "voice_call": "Голосовий виклик", "voice_call_incoming": "Вхідний голосовий виклик", diff --git a/apps/web/src/i18n/strings/vi.json b/apps/web/src/i18n/strings/vi.json index 141845f695..d5f26ace6a 100644 --- a/apps/web/src/i18n/strings/vi.json +++ b/apps/web/src/i18n/strings/vi.json @@ -3137,7 +3137,6 @@ "user_busy_description": "Người dùng bạn vừa gọi hiện đang bận.", "user_is_presenting": "%(sharerName)s đang trình bày", "video_call": "Gọi video", - "video_call_started": "Cuộc gọi truyền hình đã bắt đầu", "voice_call": "Gọi thoại", "you_are_presenting": "Bạn đang trình bày" }, diff --git a/apps/web/src/i18n/strings/zh_Hans.json b/apps/web/src/i18n/strings/zh_Hans.json index 9c471ec469..ec46018f0c 100644 --- a/apps/web/src/i18n/strings/zh_Hans.json +++ b/apps/web/src/i18n/strings/zh_Hans.json @@ -219,7 +219,7 @@ "incorrect_password": "密码不正确", "log_in_new_account": "登录到你的新账户。", "logout_dialog": { - "description": "你确定要注销吗?", + "description": "你确定要移除此设备?", "megolm_export": "手动导出密钥", "setup_key_backup_title": "你将失去加密消息的访问权", "setup_secure_backup_description_1": "加密消息采用端到端加密技术确保安全。只有你与收件人拥有读取这些消息的密钥。", @@ -588,7 +588,6 @@ "video": "视频", "video_room": "视频房间", "view_message": "查看消息", - "voice": "语音", "warning": "警告" }, "composer": { @@ -684,8 +683,10 @@ "create_section_dialog": { "create_section": "创建区域", "description": "区域仅对你可见", + "edit_section": "编辑区域", "label": "区域名称", - "title": "创建区域" + "title": "创建区域", + "title_edition": "编辑区域" }, "create_space": { "add_details_prompt": "添加一些信息以便人们识别。", @@ -1116,7 +1117,7 @@ "waiting_other_device_details": "等待你在另一台设备上验证,%(deviceName)s(%(deviceId)s)…", "waiting_other_user": "正在等待 %(displayName)s 验证…" }, - "verification_requested_toast_title": "已请求验证", + "verification_requested_toast_title": "请求验证", "verified_identity_changed": "%(displayName)s (%(userId)s) 的数字身份已重置。了解更多", "verified_identity_changed_no_displayname": "%(userId)s的数字身份已重置。了解更多", "verify_toast_description": "可能不受其他用户信任", @@ -1433,7 +1434,7 @@ }, "keyboard": { "activate_button": "激活选择的按钮", - "alt": "Alt", + "alt": "", "autocomplete_cancel": "取消自动补全", "autocomplete_force": "强制完成", "autocomplete_navigate_next": "下一个自动补全建议", @@ -1457,11 +1458,11 @@ "composer_toggle_link": "切换链接", "composer_toggle_quote": "切换引用", "composer_undo": "撤消编辑", - "control": "Ctrl", + "control": "", "dismiss_read_marker_and_jump_bottom": "忽略已读标记并跳转到底部", - "end": "结束", + "end": "", "enter": "", - "escape": "Esc", + "escape": "", "go_home_view": "转到主页视图", "home": "", "jump_first_message": "跳转到首个消息", @@ -1493,7 +1494,7 @@ "scroll_up_timeline": "向上滚动时间线", "search": "搜索(要使其生效必须启用相关功能)", "send_sticker": "发送贴纸", - "shift": "Shift", + "shift": "", "space": "空格", "switch_to_space": "使用数字切换空间", "toggle_hidden_events": "切换隐藏事件的可见性", @@ -1843,6 +1844,12 @@ "ongoing": "正在移除…", "reason_label": "理由(可选)" }, + "remove_section_dialog": { + "confirmation": "你确定要移除此区域?", + "description": "此区域中的聊天仍将显示在聊天列表。", + "remove_section": "移除区域", + "title": "移除此区域?" + }, "report_content": { "description": "举报此消息会将其唯一的“事件 ID”发送给服务器管理员。如果此房间中的消息已加密,则服务器管理员将无法查看消息文本、任何文件或图像。", "disagree": "不同意", @@ -2452,7 +2459,7 @@ "description_3": "浏览器扩展阻止了该请求。", "description_4": "此服务器已离线。", "description_5": "服务器已拒绝你的请求。", - "description_6": "你所在的区域在连接 Internet 时遇到困难。", + "description_6": "你所在的地区在连接 Internet 时遇到困难。", "description_7": "尝试联系服务器时发生连接错误。", "description_8": "服务器的配置未能说明问题原因(CORS)。", "empty_timeline": "你已阅读所有消息", @@ -2573,7 +2580,7 @@ "dialog_title": "设置:加密", "key_storage": { "allow_key_storage": "允许密钥存储", - "description": "这将允许你在任意新设备上查看聊天历史,这对备份聊天和数字身份是必需的。", + "description": "这将允许你在任意新设备上查看聊天历史,这对备份聊天和数字身份是必需的。了解更多", "title": "密钥存储" }, "recovery": { @@ -3741,7 +3748,7 @@ "other": "%(names)s 与其他 %(count)s 个人正在输入…" }, "one_user": "%(displayName)s 正在输入…", - "two_users": "%(names)s 与其他 %(lastPerson)s 个人正在输入…" + "two_users": "%(names)s 与 %(lastPerson)s 正在输入…" }, "undecryptable_tooltip": "此消息无法解密" }, @@ -3894,7 +3901,6 @@ "connection_lost": "与服务器的连接已丢失", "connection_lost_description": "在未连接到服务器的情况下,你无法拨打电话。", "consulting": "正在与 %(transferTarget)s 协商。转接到 %(transferee)s", - "decline_call": "拒绝", "default_device": "默认设备", "dial": "拨号", "dialpad": "拨号盘", @@ -3945,7 +3951,6 @@ "show_sidebar_button": "显示边栏", "silence": "静音通话", "silenced": "通知已静音", - "skip_lobby_toggle_option": "立即加入", "start_screenshare": "开始分享屏幕", "stop_screenshare": "停止分享屏幕", "too_many_calls": "呼叫频繁", @@ -3967,7 +3972,6 @@ "user_is_presenting": "%(sharerName)s 正在分享", "video_call": "视频通话", "video_call_incoming": "视频通话来电", - "video_call_started": "已开始视频通话", "video_call_using": "视频通话时使用:", "voice_call": "语音通话", "voice_call_incoming": "语音通话来电", diff --git a/apps/web/src/i18n/strings/zh_Hant.json b/apps/web/src/i18n/strings/zh_Hant.json index a606287572..70a4fa882a 100644 --- a/apps/web/src/i18n/strings/zh_Hant.json +++ b/apps/web/src/i18n/strings/zh_Hant.json @@ -3399,7 +3399,6 @@ "user_busy_description": "您想要通話的使用者目前忙碌中。", "user_is_presenting": "%(sharerName)s 正在投影", "video_call": "視訊通話", - "video_call_started": "視訊通話已開始", "voice_call": "語音通話", "you_are_presenting": "您正在投影" }, diff --git a/apps/web/src/mjolnir/Mjolnir.ts b/apps/web/src/mjolnir/Mjolnir.ts index be4acf5965..63059b3c04 100644 --- a/apps/web/src/mjolnir/Mjolnir.ts +++ b/apps/web/src/mjolnir/Mjolnir.ts @@ -11,7 +11,7 @@ import { logger } from "matrix-js-sdk/src/logger"; import { MatrixClientPeg } from "../MatrixClientPeg"; import { ALL_RULE_TYPES, BanList } from "./BanList"; -import SettingsStore from "../settings/SettingsStore"; +import SettingsStore, { type CallbackFn } from "../settings/SettingsStore"; import { _t } from "../languageHandler"; import dis from "../dispatcher/dispatcher"; import { SettingLevel } from "../settings/SettingLevel"; @@ -38,7 +38,7 @@ export class Mjolnir { } public start(): void { - this.mjolnirWatchRef = SettingsStore.watchSetting("mjolnirRooms", null, this.onListsChanged.bind(this)); + this.mjolnirWatchRef = SettingsStore.watchSetting("mjolnirRooms", null, this.onListsChanged); this.dispatcherRef = dis.register(this.onAction); dis.dispatch>({ @@ -130,15 +130,10 @@ export class Mjolnir { this.updateLists(this._roomIds); }; - private onListsChanged( - settingName: string, - roomId: string | null, - atLevel: SettingLevel, - newValue: string[], - ): void { + private onListsChanged: CallbackFn<"mjolnirRooms"> = (settingName, roomId, atLevel, newValue): void => { // We know that ban lists are only recorded at one level so we don't need to re-eval them - this.updateLists(newValue); - } + this.updateLists(newValue ?? []); + }; private updateLists(listRoomIds: string[]): void { if (!MatrixClientPeg.get()) return; diff --git a/apps/web/src/settings/Settings.tsx b/apps/web/src/settings/Settings.tsx index 67d0a31e14..ba6b84e9c1 100644 --- a/apps/web/src/settings/Settings.tsx +++ b/apps/web/src/settings/Settings.tsx @@ -1,4 +1,5 @@ /* +Copyright 2026 Element Creations Ltd. Copyright 2024, 2025 New Vector Ltd. Copyright 2018-2024 The Matrix.org Foundation C.I.C. Copyright 2017 Travis Ralston @@ -53,6 +54,7 @@ import { type ComputedInviteConfig } from "../@types/invite-rules.ts"; import BlockInvitesConfigController from "./controllers/BlockInvitesConfigController.ts"; import RequiresSettingsController from "./controllers/RequiresSettingsController.ts"; import { type OrderedCustomSections, type CustomSectionsData } from "../stores/room-list-v3/section.ts"; +import { type NotificationSound } from "../Notifier.ts"; export const defaultWatchManager = new WatchManager(); @@ -228,6 +230,7 @@ export interface Settings { "feature_ask_to_join": IFeature; "feature_notifications": IFeature; "feature_msc4362_encrypted_state_events": IFeature; + "feature_user_status": IFeature; // These are in the feature namespace but aren't actually features "feature_hidebold": IBaseSetting; @@ -309,15 +312,7 @@ export interface Settings { "urlPreviewsEnabled_e2ee": IBaseSetting; "notificationsEnabled": IBaseSetting; "deviceNotificationsEnabled": IBaseSetting; - "notificationSound": IBaseSetting< - | { - name: string; - type: string; - size: number; - url: string; - } - | false - >; + "notificationSound": IBaseSetting; "notificationBodyEnabled": IBaseSetting; "audioNotificationsEnabled": IBaseSetting; "enableWidgetScreenshots": IBaseSetting; @@ -789,6 +784,30 @@ export const SETTINGS: Settings = { shouldWarn: true, default: false, }, + "feature_user_status": { + isFeature: true, + labsGroup: LabGroup.Profile, + displayName: _td("labs|feature_user_status|display_name"), + description: _td("labs|feature_user_status|description"), + supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG_PRIORITISED, + supportedLevelsAreOrdered: true, + controller: new ServerSupportUnstableFeatureController( + "feature_user_status", + defaultWatchManager, + [["org.matrix.msc4429"], ["org.matrix.msc4429.stable"]], + undefined, + _td("labs|feature_user_status|required_msc_support"), + false, + // We have to assume it's available during early startup because of a race: + // The feature is used to enable extra sync filters during MatrixClient setup + // and we can't check for serverside support until the client has finished setting up. + // Once the client has setup, (so by the time the user actually opens the labs menu) we can + // enforce proper checks. + true, + true, + ), + default: false, + }, "useCompactLayout": { supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS, displayName: _td("settings|preferences|compact_modern"), @@ -1126,7 +1145,13 @@ export const SETTINGS: Settings = { supportedLevelsAreOrdered: true, displayName: _td("settings|inline_url_previews_default"), default: true, - controller: new UIFeatureController(UIFeature.URLPreviews), + controller: new RequiresSettingsController([UIFeature.URLPreviews], false, (c) => { + if (c["io.element.msc4452.preview_url"]?.enabled !== false) { + // If the capability is not listed, or explicitly true then do not disable. + return false; + } + return _t("common|disabled_by_homeserver"); + }), }, "urlPreviewsEnabled_e2ee": { // Can only be enabled per-device to ensure neither the homeserver nor client config diff --git a/apps/web/src/settings/SettingsStore.ts b/apps/web/src/settings/SettingsStore.ts index c2f0eb98c8..10100a1af6 100644 --- a/apps/web/src/settings/SettingsStore.ts +++ b/apps/web/src/settings/SettingsStore.ts @@ -79,7 +79,7 @@ export const LEVEL_ORDER = [ SettingLevel.DEFAULT, ]; -function getLevelOrder(setting: ISetting): SettingLevel[] { +function getLevelOrder(setting: Settings[keyof Settings]): SettingLevel[] { // Settings which support only a single setting level are inherently ordered if (setting.supportedLevelsAreOrdered || setting.supportedLevels.length === 1) { // return a copy to prevent callers from modifying the array @@ -88,12 +88,12 @@ function getLevelOrder(setting: ISetting): SettingLevel[] { return LEVEL_ORDER; } -export type CallbackFn = ( - settingName: SettingKey, +export type CallbackFn = ( + settingName: S, roomId: string | null, atLevel: SettingLevel, - newValAtLevel: any, - newVal: any, + newValAtLevel: Settings[S]["default"] | null, + newVal: Settings[S]["default"] | null, ) => void; type HandlerMap = Partial<{ @@ -167,7 +167,11 @@ export default class SettingsStore { * if the change in value is worthwhile enough to react upon. * @returns {string} A reference to the watcher that was employed. */ - public static watchSetting(settingName: SettingKey, roomId: string | null, callbackFn: CallbackFn): string { + public static watchSetting( + settingName: S, + roomId: string | null, + callbackFn: CallbackFn, + ): string { const setting = SETTINGS[settingName]; if (!setting) throw new Error(`${settingName} is not a setting`); @@ -175,7 +179,11 @@ export default class SettingsStore { const watcherId = `${new Date().getTime()}_${SettingsStore.watcherCount++}_${finalSettingName}_${roomId}`; - const localizedCallback = (changedInRoomId: string | null, atLevel: SettingLevel, newValAtLevel: any): void => { + const localizedCallback = ( + changedInRoomId: string | null, + atLevel: SettingLevel, + newValAtLevel: Settings[S]["default"], + ): void => { if (!SettingsStore.doesSettingSupportLevel(settingName, atLevel)) { logger.warn( `Setting handler notified for an update of an invalid setting level: ` + @@ -220,7 +228,7 @@ export default class SettingsStore { * @param {string} settingName The setting name to monitor. * @param {String} roomId The room ID to monitor for changes in. Use null for all rooms. */ - public static monitorSetting(settingName: SettingKey, roomId: string | null): void { + public static monitorSetting(settingName: S, roomId: string | null): void { roomId = roomId || null; // the thing wants null specifically to work, so appease it. if (!this.monitors.has(settingName)) this.monitors.set(settingName, new Map()); @@ -228,7 +236,7 @@ export default class SettingsStore { const registerWatcher = (): void => { this.monitors.get(settingName)!.set( roomId, - SettingsStore.watchSetting( + SettingsStore.watchSetting( settingName, roomId, (settingName, inRoomId, level, newValueAtLevel, newValue) => { @@ -449,11 +457,10 @@ export default class SettingsStore { /** * Gets the default value of a setting. - * @param {string} settingName The name of the setting to read the value of. - * @param {String} roomId The room ID to read the setting value in, may be null. - * @return {*} The default value + * @param settingName The name of the setting to read the value of. + * @return The default value */ - public static getDefaultValue(settingName: SettingKey): any { + public static getDefaultValue(settingName: S): Settings[S]["default"] { // Verify that the setting is actually a setting if (!SETTINGS[settingName]) { throw new Error("Setting '" + settingName + "' does not appear to be a setting."); @@ -462,13 +469,13 @@ export default class SettingsStore { return SETTINGS[settingName].default; } - private static getFinalValue( - setting: ISetting, + private static getFinalValue( + setting: Settings[S], level: SettingLevel, roomId: string | null, - calculatedValue: any, + calculatedValue: Settings[S]["default"], calculatedAtLevel: SettingLevel | null, - ): any { + ): Settings[S]["default"] { let resultingValue = calculatedValue; if (setting.controller) { @@ -480,25 +487,22 @@ export default class SettingsStore { return resultingValue; } - /* eslint-disable valid-jsdoc */ //https://github.com/eslint/eslint/issues/7307 /** * Sets the value for a setting. The room ID is optional if the setting is not being * set for a particular room, otherwise it should be supplied. The value may be null * to indicate that the level should no longer have an override. - * @param {string} settingName The name of the setting to change. - * @param {String} roomId The room ID to change the value in, may be null. - * @param {SettingLevel} level The level + * @param settingName The name of the setting to change. + * @param roomId The room ID to change the value in, may be null. + * @param level The level * to change the value at. - * @param {*} value The new value of the setting, may be null. - * @return {Promise} Resolves when the setting has been changed. + * @param value The new value of the setting, may be null. + * @return Resolves when the setting has been changed. */ - - /* eslint-enable valid-jsdoc */ - public static async setValue( - settingName: SettingKey, + public static async setValue( + settingName: S, roomId: string | null, level: SettingLevel, - value: any, + value: Settings[S]["default"] | null, ): Promise { // Verify that the setting is actually a setting const setting = SETTINGS[settingName]; diff --git a/apps/web/src/settings/controllers/MediaPreviewConfigController.ts b/apps/web/src/settings/controllers/MediaPreviewConfigController.ts index d349ef33fa..beda3d1cf5 100644 --- a/apps/web/src/settings/controllers/MediaPreviewConfigController.ts +++ b/apps/web/src/settings/controllers/MediaPreviewConfigController.ts @@ -38,8 +38,8 @@ export default class MediaPreviewConfigController extends MatrixClientBackedCont const validMediaPreviews = Object.values(MediaPreviewValue); const validInviteAvatars = [MediaPreviewValue.Off, MediaPreviewValue.On]; return { - invite_avatars: validInviteAvatars.includes(inviteAvatars) ? inviteAvatars : undefined, - media_previews: validMediaPreviews.includes(mediaPreviews) ? mediaPreviews : undefined, + invite_avatars: validInviteAvatars.includes(inviteAvatars!) ? inviteAvatars : undefined, + media_previews: validMediaPreviews.includes(mediaPreviews!) ? mediaPreviews : undefined, }; } diff --git a/apps/web/src/settings/controllers/RequiresSettingsController.ts b/apps/web/src/settings/controllers/RequiresSettingsController.ts index a5bbc4b26b..3729ce7fbb 100644 --- a/apps/web/src/settings/controllers/RequiresSettingsController.ts +++ b/apps/web/src/settings/controllers/RequiresSettingsController.ts @@ -5,30 +5,75 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com Please see LICENSE files in the repository root for full details. */ -import SettingController from "./SettingController"; +import { logger as rootLogger } from "matrix-js-sdk/src/logger"; + +import type { Capabilities } from "matrix-js-sdk/src/matrix"; import SettingsStore from "../SettingsStore"; import type { BooleanSettingKey } from "../Settings.tsx"; +import MatrixClientBackedController from "./MatrixClientBackedController.ts"; +const logger = rootLogger.getChild("RequiresSettingsController"); /** * Disables a setting & forces it's value if one or more settings are not enabled + * and/or a capability on the client check does not pass. */ -export default class RequiresSettingsController extends SettingController { +export default class RequiresSettingsController extends MatrixClientBackedController { public constructor( public readonly settingNames: BooleanSettingKey[], - private forcedValue = false, + private readonly forcedValue = false, + /** + * Function to check the capabilites of the client. + * If defined this will be called when the MatrixClient is instantiated to check + * the returned capabilites. + * @returns `true` or a string if the feature is disabled by the feature, otherwise false. + */ + private readonly isCapabilityDisabled?: (caps: Capabilities) => boolean | string, ) { super(); } + protected initMatrixClient(): void { + if (this.client && this.isCapabilityDisabled) { + // Ensure we fetch capabilies at least once. + this.client.getCapabilities().catch((ex) => { + logger.warn("Failed to fetch capabilities", ex); + }); + } + } + + /** + * Checks if the `isCapabilityDisabled` function blocks the setting. + * @returns `true` or a string if the feature is disabled by the feature, otherwise false. + */ + private get isBlockedByCapabilites(): boolean | string { + if (!this.isCapabilityDisabled) { + return false; + } + // This works because the cached caps are stored forever, and we have made + // at least one call to get capaibilies. + const cachedCaps = this.client?.getCachedCapabilities(); + if (!cachedCaps) { + // If we do not have any capabilites yet, then assume the setting IS blocked. + return true; + } + return this.isCapabilityDisabled(cachedCaps); + } + + public get settingDisabled(): boolean | string { + if (this.settingNames.some((s) => !SettingsStore.getValue(s))) { + return true; + } + return this.isBlockedByCapabilites; + } + public getValueOverride(): any { if (this.settingDisabled) { // per the docs: we force a disabled state when the feature isn't active return this.forcedValue; } + if (this.isBlockedByCapabilites) { + return this.forcedValue; + } return null; // no override } - - public get settingDisabled(): boolean { - return this.settingNames.some((s) => !SettingsStore.getValue(s)); - } } diff --git a/apps/web/src/settings/controllers/ServerSupportUnstableFeatureController.ts b/apps/web/src/settings/controllers/ServerSupportUnstableFeatureController.ts index 1b212da638..c5c3fc3911 100644 --- a/apps/web/src/settings/controllers/ServerSupportUnstableFeatureController.ts +++ b/apps/web/src/settings/controllers/ServerSupportUnstableFeatureController.ts @@ -1,4 +1,5 @@ /* +Copyright 2026 Element Creations Ltd. Copyright 2024 New Vector Ltd. Copyright 2023 The Matrix.org Foundation C.I.C. @@ -12,6 +13,7 @@ import { type WatchManager } from "../WatchManager"; import SettingsStore from "../SettingsStore"; import { type SettingKey } from "../Settings.tsx"; import { _t } from "../../languageHandler.tsx"; +import PlatformPeg from "../../PlatformPeg.ts"; /** * Disables a given setting if the server unstable feature it requires is not supported @@ -28,6 +30,9 @@ export default class ServerSupportUnstableFeatureController extends MatrixClient * @param unstableFeatureGroups - If any one of the feature groups is satisfied, * then the setting is considered enabled. A feature group is satisfied if all of * the features in the group are supported (all features in a group are required). + * @param defaultEnabled - If we haven't been able to check for support yet, should + * this feature be enabled or disabled (default). + * @param forceReload - Should the client force reload. */ public constructor( private readonly settingName: SettingKey, @@ -36,12 +41,23 @@ export default class ServerSupportUnstableFeatureController extends MatrixClient private readonly stableVersion?: string, private readonly disabledMessage?: TranslationKey, private readonly forcedValue: any = false, + private readonly defaultEnabled = false, + private readonly forceReload = false, ) { super(); } + public onChange(): void { + if (this.forceReload) { + PlatformPeg.get()?.reload(); + } + } + public get disabled(): boolean { - return !this.enabled; + if (this.enabled !== undefined) { + return !this.enabled; + } + return !this.defaultEnabled; } public set disabled(newDisabledValue: boolean) { diff --git a/apps/web/src/slash-commands/SlashCommands.tsx b/apps/web/src/slash-commands/SlashCommands.tsx index f3e1d1ed85..d34949a4bc 100644 --- a/apps/web/src/slash-commands/SlashCommands.tsx +++ b/apps/web/src/slash-commands/SlashCommands.tsx @@ -1,4 +1,5 @@ /* +Copyright 2026 Element Creations Ltd. Copyright 2024 New Vector Ltd. Copyright 2020 The Matrix.org Foundation C.I.C. Copyright 2019 Michael Telatynski <7t3chguy@gmail.com> @@ -62,6 +63,7 @@ import { goto, join } from "./join"; import { manuallyVerifyDevice } from "../components/views/dialogs/ManualDeviceKeyVerificationDialog"; import upgraderoom from "./upgraderoom/upgraderoom"; import { emoticon } from "./emoticon"; +import { statusCommand } from "./status"; export { CommandCategories, Command }; @@ -819,6 +821,7 @@ export const Commands = [ }, renderingTypes: [TimelineRenderingType.Room], }), + statusCommand, // Command definitions for autocompletion ONLY: // /me is special because its not handled by SlashCommands.js and is instead done inside the Composer classes diff --git a/apps/web/src/slash-commands/status.ts b/apps/web/src/slash-commands/status.ts new file mode 100644 index 0000000000..7a1bf36d94 --- /dev/null +++ b/apps/web/src/slash-commands/status.ts @@ -0,0 +1,51 @@ +/* +Copyright 2026 Element Creations Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +import { _td } from "@element-hq/web-shared-components"; + +import { Command, CommandCategories, splitAtFirstSpace } from "./SlashCommands"; +import SettingsStore from "../settings/SettingsStore"; +import { reject, success } from "./utils"; +import { UserFriendlyError } from "../languageHandler"; +import { userStatusTextWithinMaxLength } from "../hooks/useUserStatus"; +import { TimelineRenderingType } from "../contexts/RoomContext"; + +export const statusCommand = new Command({ + command: "status", + args: " ", + description: _td("slash_command|status|description"), + isEnabled: () => SettingsStore.getValue("feature_user_status"), + runFn: function (cli, _roomId, _threadId, args) { + if (!args) { + return reject(new UserFriendlyError("slash_command|status|no_args")); + } + const [emojiText, text] = splitAtFirstSpace(args); + if (!emojiText) { + return reject(new UserFriendlyError("slash_command|status|no_emoji")); + } + if (!text) { + return reject(new UserFriendlyError("slash_command|status|no_text")); + } + const [emoji, additionalSegment] = [...new Intl.Segmenter().segment(emojiText)]; + if (additionalSegment) { + // This is "too long" in that it's more than one grapheme, so the error we give is + // that it's "not an emoji". + return reject(new UserFriendlyError("slash_command|status|too_long_emoji")); + } + if (!userStatusTextWithinMaxLength(text)) { + return reject(new UserFriendlyError("slash_command|status|too_long_text")); + } + return success( + cli.setExtendedProfileProperty("org.matrix.msc4426.status", { + emoji: emoji.segment, + text, + }), + ); + }, + category: CommandCategories.actions, + renderingTypes: [TimelineRenderingType.Room], +}); diff --git a/apps/web/src/stores/right-panel/RightPanelStoreIPanelState.ts b/apps/web/src/stores/right-panel/RightPanelStoreIPanelState.ts index 8b145c25ec..cd690ca147 100644 --- a/apps/web/src/stores/right-panel/RightPanelStoreIPanelState.ts +++ b/apps/web/src/stores/right-panel/RightPanelStoreIPanelState.ts @@ -60,8 +60,8 @@ export type IRightPanelForRoomStored = { history: Array; }; -export function convertToStorePanel(cacheRoom?: IRightPanelForRoom): IRightPanelForRoomStored | undefined { - if (!cacheRoom) return undefined; +export function convertToStorePanel(cacheRoom?: IRightPanelForRoom): IRightPanelForRoomStored | null { + if (!cacheRoom) return null; const storeHistory = [...cacheRoom.history].map((panelState) => convertCardToStore(panelState)); return { isOpen: cacheRoom.isOpen, history: storeHistory }; } diff --git a/apps/web/src/stores/room-list/RoomListStore.ts b/apps/web/src/stores/room-list/RoomListStore.ts index d35328bdad..fc2343d441 100644 --- a/apps/web/src/stores/room-list/RoomListStore.ts +++ b/apps/web/src/stores/room-list/RoomListStore.ts @@ -69,7 +69,7 @@ export class RoomListStoreClass extends AsyncStoreWithClient implem "feature_dynamic_room_predecessors", null, (_settingName, _roomId, _level, _newValAtLevel, newVal) => { - this.msc3946ProcessDynamicPredecessor = newVal; + this.msc3946ProcessDynamicPredecessor = !!newVal; this.regenerateAllLists({ trigger: true }); }, ); diff --git a/apps/web/src/theme.ts b/apps/web/src/theme.ts index a0a527c28a..4bd3ea322d 100644 --- a/apps/web/src/theme.ts +++ b/apps/web/src/theme.ts @@ -207,9 +207,10 @@ function generateCustomCompoundCSS(theme: CompoundTheme): string { for (const [token, value] of Object.entries(theme)) if (COMPOUND_TOKEN.test(token)) properties.push(`${token}: ${value};`); else logger.warn(`'${token}' is not a valid Compound token`); - // Insert the design token overrides into the 'custom' cascade layer as - // documented at https://compound.element.io/?path=/docs/develop-theming--docs - return `@layer compound.custom { :root, [class*="cpd-theme-"] { ${properties.join(" ")} } }`; + // Insert the design token overrides into the existing Compound tokens + // layer so custom themes win over the imported default tokens by source + // order without creating a lower-priority nested layer. + return `@layer compound-tokens { :root, [class*="cpd-theme-"] { ${properties.join(" ")} } }`; } /** diff --git a/apps/web/src/utils/exportUtils/exportCSS.ts b/apps/web/src/utils/exportUtils/exportCSS.ts index 8c470aaa0d..7e55b0cebe 100644 --- a/apps/web/src/utils/exportUtils/exportCSS.ts +++ b/apps/web/src/utils/exportUtils/exportCSS.ts @@ -6,11 +6,12 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com Please see LICENSE files in the repository root for full details. */ -import type { Rule, StyleSheet } from "css-tree"; +import type { CssNode, Rule, StyleSheet } from "css-tree"; import customCSS from "!!raw-loader!./exportCustomCSS.css"; const cssSelectorTextClassesRegex = /\.[\w-]+/g; +const appLayerName = "app-web"; function mutateCssText(css: string): string { // replace used fonts so that we don't have to bundle Inter & Fira Code @@ -41,6 +42,21 @@ function includeRule(rule: Rule, usedClasses: Set): boolean { return true; } +function includeNode(node: CssNode, usedClasses: Set): boolean { + if (node.type === "Atrule") { + if (node.name === "font-face") { + return false; + } + + if (node.block) { + node.block.children = node.block.children.filter((child) => includeNode(child, usedClasses)); + return !node.block.children.isEmpty; + } + } + + return node.type !== "Rule" || includeRule(node, usedClasses); +} + // naively culls unused css rules based on which classes are present in the html, // doesn't cull rules which won't apply due to the full selector not matching but gets rid of a LOT of cruft anyway. // We cannot use document.styleSheets as it does not handle variables in shorthand properties sanely, @@ -69,13 +85,7 @@ const getExportCSS = async (usedClasses: Set): Promise => { }) as StyleSheet; for (const rule of ast.children) { - if (rule.type === "Atrule") { - if (rule.name === "font-face") { - continue; - } - } - - if (rule.type === "Rule" && !includeRule(rule, usedClasses)) { + if (!includeNode(rule, usedClasses)) { continue; } @@ -83,7 +93,7 @@ const getExportCSS = async (usedClasses: Set): Promise => { } } - return css + customCSS; + return `${css}@layer ${appLayerName} {${customCSS}}`; }; export default getExportCSS; diff --git a/apps/web/src/utils/exportUtils/exportCustomCSS.css b/apps/web/src/utils/exportUtils/exportCustomCSS.css index 117eff6b23..fe8f7c7269 100644 --- a/apps/web/src/utils/exportUtils/exportCustomCSS.css +++ b/apps/web/src/utils/exportUtils/exportCustomCSS.css @@ -60,11 +60,6 @@ body { a.mx_reply_anchor { cursor: pointer; - color: #238cf5; -} - -a.mx_reply_anchor:hover { - text-decoration: underline; } @-webkit-keyframes mx_snackbar_fadein { diff --git a/apps/web/src/viewmodels/message-body/EventContentBodyViewModel.ts b/apps/web/src/viewmodels/message-body/EventContentBodyViewModel.ts index e1f765403f..cfa49544b9 100644 --- a/apps/web/src/viewmodels/message-body/EventContentBodyViewModel.ts +++ b/apps/web/src/viewmodels/message-body/EventContentBodyViewModel.ts @@ -231,7 +231,7 @@ export class EventContentBodyViewModel "TextualBody.enableBigEmoji", null, (_settingName, _roomId, _level, _newValAtLevel, newVal) => { - this.setEnableBigEmoji(newVal); + this.setEnableBigEmoji(!!newVal); }, ); this.disposables.track(() => SettingsStore.unwatchSetting(enableBigEmojiWatcherRef)); @@ -240,7 +240,7 @@ export class EventContentBodyViewModel "Pill.shouldShowPillAvatar", null, (_settingName, _roomId, _level, _newValAtLevel, newVal) => { - this.setShouldShowPillAvatar(newVal); + this.setShouldShowPillAvatar(!!newVal); }, ); this.disposables.track(() => SettingsStore.unwatchSetting(shouldShowPillAvatarWatcherRef)); diff --git a/apps/web/src/viewmodels/message-body/RedactedBodyViewModel.ts b/apps/web/src/viewmodels/message-body/RedactedBodyViewModel.ts index 4086c1db14..490f7be43f 100644 --- a/apps/web/src/viewmodels/message-body/RedactedBodyViewModel.ts +++ b/apps/web/src/viewmodels/message-body/RedactedBodyViewModel.ts @@ -75,7 +75,7 @@ export class RedactedBodyViewModel (_settingName, _roomId, _level, _newValAtLevel, newVal) => { if (this.showTwelveHour === newVal) return; - this.showTwelveHour = newVal; + this.showTwelveHour = !!newVal; this.updateTooltip(); }, ); diff --git a/apps/web/src/viewmodels/message-body/UrlPreviewGroupViewModel.ts b/apps/web/src/viewmodels/message-body/UrlPreviewGroupViewModel.ts index 55109ac876..b722def238 100644 --- a/apps/web/src/viewmodels/message-body/UrlPreviewGroupViewModel.ts +++ b/apps/web/src/viewmodels/message-body/UrlPreviewGroupViewModel.ts @@ -296,7 +296,7 @@ export class UrlPreviewGroupViewModel null, (_setting, _roomid, _level, compactLayout) => { this.snapshot.merge({ - compactLayout, + compactLayout: !!compactLayout, }); }, ); diff --git a/apps/web/src/viewmodels/room-list/RoomListHeaderViewModel.ts b/apps/web/src/viewmodels/room-list/RoomListHeaderViewModel.ts index 55626cb252..2e48532078 100644 --- a/apps/web/src/viewmodels/room-list/RoomListHeaderViewModel.ts +++ b/apps/web/src/viewmodels/room-list/RoomListHeaderViewModel.ts @@ -26,6 +26,7 @@ import { showSpaceSettings, } from "../../utils/space"; import type { ViewRoomPayload } from "../../dispatcher/payloads/ViewRoomPayload"; +import type { RoomListSectionsCollapseStateChangedPayload } from "../../dispatcher/payloads/RoomListSectionsCollapseStateChangedPayload"; import SettingsStore from "../../settings/SettingsStore"; import RoomListStoreV3 from "../../stores/room-list-v3/RoomListStoreV3"; import { SortingAlgorithm } from "../../stores/room-list-v3/skip-list/sorters"; @@ -77,6 +78,10 @@ export class RoomListHeaderViewModel if (this.activeSpace) { this.disposables.trackListener(this.activeSpace, RoomEvent.Name, this.onSpaceNameChange); } + + // Listen for section collapse state changes from RoomListViewModel + const dispatcherRef = defaultDispatcher.register(this.onDispatch); + this.disposables.track(() => defaultDispatcher.unregister(dispatcherRef)); } /** @@ -203,6 +208,23 @@ export class RoomListHeaderViewModel public createSection = (): void => { RoomListStoreV3.instance.createSection(); }; + + public collapseOrExpandSections = (): void => { + const action = + this.snapshot.current.collapseSections === "expand" + ? Action.RoomListExpandAllSections + : Action.RoomListCollapseAllSections; + defaultDispatcher.fire(action); + }; + + private readonly onDispatch = (payload: { action: string }): void => { + if (payload.action === Action.RoomListSectionsCollapseStateChanged) { + const { collapseSections } = payload as RoomListSectionsCollapseStateChangedPayload; + this.snapshot.merge({ + collapseSections: collapseSections && (collapseSections === "collapse" ? "expand" : "collapse"), + }); + } + }; } /** * Get the initial snapshot for the RoomListHeaderViewModel. diff --git a/apps/web/src/viewmodels/room-list/RoomListViewModel.ts b/apps/web/src/viewmodels/room-list/RoomListViewModel.ts index e711e340b0..6e28e61ed7 100644 --- a/apps/web/src/viewmodels/room-list/RoomListViewModel.ts +++ b/apps/web/src/viewmodels/room-list/RoomListViewModel.ts @@ -21,6 +21,7 @@ import { Action } from "../../dispatcher/actions"; import dispatcher from "../../dispatcher/dispatcher"; import { type ViewRoomDeltaPayload } from "../../dispatcher/payloads/ViewRoomDeltaPayload"; import { type ViewRoomPayload } from "../../dispatcher/payloads/ViewRoomPayload"; +import { type RoomListSectionsCollapseStateChangedPayload } from "../../dispatcher/payloads/RoomListSectionsCollapseStateChangedPayload"; import SpaceStore from "../../stores/spaces/SpaceStore"; import RoomListStoreV3, { CHATS_TAG, @@ -325,9 +326,24 @@ export class RoomListViewModel // Handle keyboard navigation shortcuts (Alt+ArrowUp/Down) // This was previously handled by useRoomListNavigation hook this.handleViewRoomDelta(payload as ViewRoomDeltaPayload); + } else if (payload.action === Action.RoomListCollapseAllSections) { + this.onCollapseAllSections(false); + } else if (payload.action === Action.RoomListExpandAllSections) { + this.onCollapseAllSections(true); } }; + /** + * Handles the collapse or expansion of all sections in the room list. + * @param expand - Whether to expand or collapse all sections + */ + private onCollapseAllSections(expand: boolean): void { + for (const sectionHeaderVM of this.roomSectionHeaderViewModels.values()) { + sectionHeaderVM.isExpanded = expand; + } + this.updateRoomListData(); + } + /** * Handle keyboard navigation shortcuts (Alt+ArrowUp/Down) to move between rooms. * Supports both regular navigation and unread-only navigation. @@ -581,6 +597,32 @@ export class RoomListViewModel sections: keepIfSame(previousSections, viewSections), isFlatList, }); + + this.notifyCollapseState(isFlatList); + } + + /** + * Notify the dispatcher about the current collapse state of the room list sections. + * @param isFlatList - Whether the room list is currently displayed as a flat list + */ + private notifyCollapseState(isFlatList: boolean): void { + // Hide collapse/expand all button if sections are disabled or if it's a flat list + if (!SettingsStore.getValue("feature_room_list_sections") || isFlatList) { + dispatcher.dispatch({ + action: Action.RoomListSectionsCollapseStateChanged, + collapseSections: undefined, + }); + return; + } + + // Determine if all sections are currently collapsed + const allCollapsed = this.snapshot.current.sections.every( + ({ id }) => !(this.roomSectionHeaderViewModels.get(id)?.isExpanded ?? true), + ); + dispatcher.dispatch({ + action: Action.RoomListSectionsCollapseStateChanged, + collapseSections: allCollapsed ? "collapse" : "expand", + }); } public createChatRoom = (): void => { diff --git a/apps/web/src/viewmodels/room/timeline/DateSeparatorViewModel.tsx b/apps/web/src/viewmodels/room/timeline/DateSeparatorViewModel.tsx index 2f3f4bf1ba..131c365530 100644 --- a/apps/web/src/viewmodels/room/timeline/DateSeparatorViewModel.tsx +++ b/apps/web/src/viewmodels/room/timeline/DateSeparatorViewModel.tsx @@ -79,7 +79,7 @@ export class DateSeparatorViewModel "feature_jump_to_date", null, (_settingName, _roomId, _level, _newValAtLevel, newVal) => { - this.jumpToDateEnabled = newVal; + this.jumpToDateEnabled = !!newVal; this.updateSnapshot(); }, ); @@ -89,7 +89,7 @@ export class DateSeparatorViewModel UIFeature.TimelineEnableRelativeDates, null, (_settingName, _roomId, _level, _newValAtLevel, newVal) => { - this.relativeDatesEnabled = newVal; + this.relativeDatesEnabled = !!newVal; this.updateSnapshot(); }, ); diff --git a/apps/web/src/viewmodels/room/timeline/event-tile/DisambiguatedProfileViewModel.ts b/apps/web/src/viewmodels/room/timeline/event-tile/DisambiguatedProfileViewModel.ts index c3b97087b3..858c8110c7 100644 --- a/apps/web/src/viewmodels/room/timeline/event-tile/DisambiguatedProfileViewModel.ts +++ b/apps/web/src/viewmodels/room/timeline/event-tile/DisambiguatedProfileViewModel.ts @@ -15,6 +15,7 @@ import { type MouseEvent } from "react"; import { _t } from "../../../../languageHandler"; import { getUserNameColorClass } from "../../../../utils/FormattingUtils"; import UserIdentifier from "../../../../customisations/UserIdentifier"; +import type { UserStatus } from "../../../../hooks/useUserStatus"; /** * Information about a member for disambiguation purposes. @@ -46,6 +47,10 @@ export interface DisambiguatedProfileViewModelProps { * The member information for disambiguation. */ member?: MemberInfo | null; + /** + * The user's present status. + */ + userStatus?: UserStatus; /** * The fallback name to use if the member's display name is not available. */ @@ -62,6 +67,7 @@ export interface DisambiguatedProfileViewModelProps { * Whether to show a tooltip with additional information. */ withTooltip?: boolean; + /** * Optional click handler for the profile. */ @@ -79,7 +85,7 @@ export class DisambiguatedProfileViewModel private static readonly computeSnapshot = ( props: DisambiguatedProfileViewModelProps, ): DisambiguatedProfileViewSnapshot => { - const { member, fallbackName, colored, emphasizeDisplayName, withTooltip } = props; + const { member, fallbackName, colored, emphasizeDisplayName, withTooltip, userStatus } = props; // Compute display name const displayName = member?.rawDisplayName || fallbackName; @@ -122,11 +128,15 @@ export class DisambiguatedProfileViewModel displayIdentifier, title, emphasizeDisplayName, + userStatus, }; }; public constructor(props: DisambiguatedProfileViewModelProps) { super(props, DisambiguatedProfileViewModel.computeSnapshot(props)); + this.snapshot.merge({ + userStatus: props.userStatus, + }); } public setMember(fallbackName: string, member?: MemberInfo | null): void { @@ -136,6 +146,13 @@ export class DisambiguatedProfileViewModel this.snapshot.set(DisambiguatedProfileViewModel.computeSnapshot(this.props)); } + public setUserStatus(userStatus?: UserStatus): void { + this.props.userStatus = userStatus; + this.snapshot.merge({ + userStatus, + }); + } + public onClick = (evt: MouseEvent): void => { this.props.onClick?.(evt); }; diff --git a/apps/web/test/slash-commands/status-test.ts b/apps/web/test/slash-commands/status-test.ts new file mode 100644 index 0000000000..2f81af17c6 --- /dev/null +++ b/apps/web/test/slash-commands/status-test.ts @@ -0,0 +1,62 @@ +/* +Copyright 2026 Element Creations Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +import { stubClient } from "../test-utils"; +import { statusCommand } from "../../src/slash-commands/status"; +import { UserFriendlyError } from "../../src/languageHandler"; + +describe("/status", () => { + const roomId = "!room:example.com"; + + let client: ReturnType; + + beforeEach(() => { + client = stubClient(); + client.setExtendedProfileProperty = jest.fn().mockResolvedValue(undefined); + }); + + function run(args?: string) { + return statusCommand.run(client, roomId, null, args); + } + + it("should reject if no args provided", () => { + const result = run(undefined); + expect(result.error).toBeInstanceOf(UserFriendlyError); + expect((result.error as UserFriendlyError).message).toBe( + "No arguments provided. You should supply an emoij and an optional text component.", + ); + }); + + it("should reject if no text is provided after the emoji", () => { + const result = run("🎉"); + expect(result.error).toBeInstanceOf(UserFriendlyError); + expect((result.error as UserFriendlyError).message).toBe("You did not provide any status text"); + }); + + it("should reject if the emoji field has more than one grapheme segment", () => { + const result = run("ab hello"); + expect(result.error).toBeInstanceOf(UserFriendlyError); + expect((result.error as UserFriendlyError).message).toBe("The first argument must be an emoji"); + }); + + it("should reject if the status text exceeds the maximum byte length", () => { + const longText = "a".repeat(257); + const result = run(`🎉 ${longText}`); + expect(result.error).toBeInstanceOf(UserFriendlyError); + expect((result.error as UserFriendlyError).message).toBe("The text you provided was too long."); + }); + + it("should set the extended profile property on success", async () => { + const result = run("🎉 Having a great day"); + expect(result.error).toBeUndefined(); + await result.promise; + expect(client.setExtendedProfileProperty).toHaveBeenCalledWith("org.matrix.msc4426.status", { + emoji: "🎉", + text: "Having a great day", + }); + }); +}); diff --git a/apps/web/test/test-utils/client.ts b/apps/web/test/test-utils/client.ts index f4d35220c2..7ab88da0b7 100644 --- a/apps/web/test/test-utils/client.ts +++ b/apps/web/test/test-utils/client.ts @@ -132,6 +132,7 @@ export const mockClientMethodsServer = (): Partial Promise.resolve(), isUserIgnored: jest.fn().mockReturnValue(false), getCapabilities: jest.fn().mockResolvedValue({}), + getCachedCapabilities: jest.fn().mockReturnValue({}), supportsThreads: jest.fn().mockReturnValue(false), supportsIntentionalMentions: jest.fn().mockReturnValue(false), getRoomUpgradeHistory: jest.fn().mockReturnValue([]), diff --git a/apps/web/test/unit-tests/__snapshots__/theme-test.ts.snap b/apps/web/test/unit-tests/__snapshots__/theme-test.ts.snap index af61bc7151..83b0c90a7c 100644 --- a/apps/web/test/unit-tests/__snapshots__/theme-test.ts.snap +++ b/apps/web/test/unit-tests/__snapshots__/theme-test.ts.snap @@ -1,3 +1,3 @@ // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing -exports[`theme setTheme applies a custom Compound theme 1`] = `"@layer compound.custom { :root, [class*="cpd-theme-"] { --cpd-color-icon-accent-tertiary: var(--cpd-color-blue-800); --cpd-color-text-action-accent: var(--cpd-color-blue-900); } }"`; +exports[`theme setTheme applies a custom Compound theme 1`] = `"@layer compound-tokens { :root, [class*="cpd-theme-"] { --cpd-color-icon-accent-tertiary: var(--cpd-color-blue-800); --cpd-color-text-action-accent: var(--cpd-color-blue-900); } }"`; diff --git a/apps/web/test/unit-tests/components/structures/MatrixChat-test.tsx b/apps/web/test/unit-tests/components/structures/MatrixChat-test.tsx index 04ce9de90f..bf53fe8910 100644 --- a/apps/web/test/unit-tests/components/structures/MatrixChat-test.tsx +++ b/apps/web/test/unit-tests/components/structures/MatrixChat-test.tsx @@ -557,7 +557,7 @@ describe("", () => { }); it("should not persist device language when not available", async () => { - await SettingsStore.setValue("language", null, SettingLevel.DEVICE, undefined); + await SettingsStore.setValue("language", null, SettingLevel.DEVICE, null); const languageBefore = SettingsStore.getValueAt(SettingLevel.DEVICE, "language", null, true, true); jest.spyOn(Lifecycle, "attemptDelegatedAuthLogin"); diff --git a/apps/web/test/unit-tests/components/structures/RoomView-test.tsx b/apps/web/test/unit-tests/components/structures/RoomView-test.tsx index c9092d7aa1..c00a3573e2 100644 --- a/apps/web/test/unit-tests/components/structures/RoomView-test.tsx +++ b/apps/web/test/unit-tests/components/structures/RoomView-test.tsx @@ -71,6 +71,7 @@ import ErrorDialog from "../../../../src/components/views/dialogs/ErrorDialog.ts import * as pinnedEventHooks from "../../../../src/hooks/usePinnedEvents"; import { TimelineRenderingType } from "../../../../src/contexts/RoomContext"; import { ModuleApi } from "../../../../src/modules/Api"; +import MatrixClientBackedController from "../../../../src/settings/controllers/MatrixClientBackedController.ts"; import { type ComposerInsertPayload, ComposerType } from "../../../../src/dispatcher/payloads/ComposerInsertPayload.ts"; // Used by group calls @@ -93,6 +94,7 @@ describe("RoomView", () => { beforeEach(() => { mockPlatformPeg({ reload: () => {} }); cli = mocked(stubClient()); + MatrixClientBackedController.matrixClient = cli; const roomName = (expect.getState().currentTestName ?? "").replace(/[^a-zA-Z0-9]/g, "").toLowerCase(); diff --git a/apps/web/test/unit-tests/components/structures/__snapshots__/RoomView-test.tsx.snap b/apps/web/test/unit-tests/components/structures/__snapshots__/RoomView-test.tsx.snap index 0dc189002c..6071bc7c50 100644 --- a/apps/web/test/unit-tests/components/structures/__snapshots__/RoomView-test.tsx.snap +++ b/apps/web/test/unit-tests/components/structures/__snapshots__/RoomView-test.tsx.snap @@ -237,7 +237,7 @@ exports[`RoomView for a local room in state ERROR should match the snapshot 1`] class="_content_n7ud0_38" >

Could not start a chat with this user @@ -247,7 +247,7 @@ exports[`RoomView for a local room in state ERROR should match the snapshot 1`] class="_actions_n7ud0_61" >

00:00
Message #49
  • @user48:example.com
    00:00
    Message #48
  • @user47:example.com
    00:00
    Message #47
  • @user46:example.com
    00:00
    Message #46
  • @user45:example.com
    00:00
    Message #45
  • @user44:example.com
    00:00
    Message #44
  • @user43:example.com
    00:00
    Message #43
  • @user42:example.com
    00:00
    Message #42
  • @user41:example.com
    00:00
    Message #41
  • @user40:example.com
    00:00
    Message #40
  • @user39:example.com
    00:00
    Message #39
  • @user38:example.com
    00:00
    Message #38
  • @user37:example.com
    00:00
    Message #37
  • @user36:example.com
    00:00
    Message #36
  • @user35:example.com
    00:00
    Message #35
  • @user34:example.com
    00:00
    Message #34
  • @user33:example.com
    00:00
    Message #33
  • @user32:example.com
    00:00
    Message #32
  • @user31:example.com
    00:00
    Message #31
  • @user30:example.com
    00:00
    Message #30
  • @user29:example.com
    00:00
    Message #29
  • @user28:example.com
    00:00
    Message #28
  • @user27:example.com
    00:00
    Message #27
  • @user26:example.com
    00:00
    Message #26
  • @user25:example.com
    00:00
    Message #25
  • @user24:example.com
    00:00
    Message #24
  • @user23:example.com
    00:00
    Message #23
  • @user22:example.com
    00:00
    Message #22
  • @user21:example.com
    00:00
    Message #21
  • @user20:example.com
    00:00
    Message #20
  • @user19:example.com
    00:00
    Message #19
  • @user18:example.com
    00:00
    Message #18
  • @user17:example.com
    00:00
    Message #17
  • @user16:example.com
    00:00
    Message #16
  • @user15:example.com
    00:00
    Message #15
  • @user14:example.com
    00:00
    Message #14
  • @user13:example.com
    00:00
    Message #13
  • @user12:example.com
    00:00
    Message #12
  • @user11:example.com
    00:00
    Message #11
  • @user10:example.com
    00:00
    Message #10
  • @user9:example.com
    00:00
    Message #9
  • @user8:example.com
    00:00
    Message #8
  • @user7:example.com
    00:00
    Message #7
  • @user6:example.com
    00:00
    Message #6
  • @user5:example.com
    00:00
    Message #5
  • @user4:example.com
    00:00
    Message #4
  • @user3:example.com
    00:00
    Message #3
  • @user2:example.com
    00:00
    Message #2
  • @user1:example.com
    00:00
    Message #1
  • @user0:example.com
    00:00
    Message #0
  • +
  • @user49:example.com
    00:00
    Message #49
  • @user48:example.com
    00:00
    Message #48
  • @user47:example.com
    00:00
    Message #47
  • @user46:example.com
    00:00
    Message #46
  • @user45:example.com
    00:00
    Message #45
  • @user44:example.com
    00:00
    Message #44
  • @user43:example.com
    00:00
    Message #43
  • @user42:example.com
    00:00
    Message #42
  • @user41:example.com
    00:00
    Message #41
  • @user40:example.com
    00:00
    Message #40
  • @user39:example.com
    00:00
    Message #39
  • @user38:example.com
    00:00
    Message #38
  • @user37:example.com
    00:00
    Message #37
  • @user36:example.com
    00:00
    Message #36
  • @user35:example.com
    00:00
    Message #35
  • @user34:example.com
    00:00
    Message #34
  • @user33:example.com
    00:00
    Message #33
  • @user32:example.com
    00:00
    Message #32
  • @user31:example.com
    00:00
    Message #31
  • @user30:example.com
    00:00
    Message #30
  • @user29:example.com
    00:00
    Message #29
  • @user28:example.com
    00:00
    Message #28
  • @user27:example.com
    00:00
    Message #27
  • @user26:example.com
    00:00
    Message #26
  • @user25:example.com
    00:00
    Message #25
  • @user24:example.com
    00:00
    Message #24
  • @user23:example.com
    00:00
    Message #23
  • @user22:example.com
    00:00
    Message #22
  • @user21:example.com
    00:00
    Message #21
  • @user20:example.com
    00:00
    Message #20
  • @user19:example.com
    00:00
    Message #19
  • @user18:example.com
    00:00
    Message #18
  • @user17:example.com
    00:00
    Message #17
  • @user16:example.com
    00:00
    Message #16
  • @user15:example.com
    00:00
    Message #15
  • @user14:example.com
    00:00
    Message #14
  • @user13:example.com
    00:00
    Message #13
  • @user12:example.com
    00:00
    Message #12
  • @user11:example.com
    00:00
    Message #11
  • @user10:example.com
    00:00
    Message #10
  • @user9:example.com
    00:00
    Message #9
  • @user8:example.com
    00:00
    Message #8
  • @user7:example.com
    00:00
    Message #7
  • @user6:example.com
    00:00
    Message #6
  • @user5:example.com
    00:00
    Message #5
  • @user4:example.com
    00:00
    Message #4
  • @user3:example.com
    00:00
    Message #3
  • @user2:example.com
    00:00
    Message #2
  • @user1:example.com
    00:00
    Message #1
  • @user0:example.com
    00:00
    Message #0
  • diff --git a/apps/web/test/unit-tests/utils/exportUtils/exportCSS-test.ts b/apps/web/test/unit-tests/utils/exportUtils/exportCSS-test.ts index 0cf154e9be..dc37a022e8 100644 --- a/apps/web/test/unit-tests/utils/exportUtils/exportCSS-test.ts +++ b/apps/web/test/unit-tests/utils/exportUtils/exportCSS-test.ts @@ -6,13 +6,103 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com Please see LICENSE files in the repository root for full details. */ +import fetchMock from "@fetch-mock/jest"; + import getExportCSS from "../../../../src/utils/exportUtils/exportCSS"; describe("exportCSS", () => { describe("getExportCSS", () => { + beforeEach(() => { + document.head.replaceChildren(); + }); + it("supports documents missing stylesheets", async () => { const css = await getExportCSS(new Set()); expect(css).not.toContain("color-scheme: light"); }); + + it("fetches export stylesheets and filters unused css", async () => { + document.head.innerHTML = ` + + + + `; + + fetchMock.get( + "end:/bundle.css", + ` + @font-face { font-family: Inter; src: url(inter.woff2); } + body { margin: 0; } + .mx_Used { font-family: Inter; color: #111111; } + .mx_Code { font-family: Fira Code; } + .mx_Unused { color: #123456; } + .mx_Empty {} + .mx_Used, .mx_UnusedComma { color: #abcdef; } + @media screen { + .mx_Used { --cpd-font-family-sans: "Inter"; } + .mx_UnusedNested { color: #654321; } + } + @supports (display: grid) { + .mx_UnusedSupported { color: #fedcba; } + } + `, + ); + fetchMock.get("end:/theme-light.css", ".mx_Theme { color: #222222; }"); + + const css = await getExportCSS(new Set(["mx_Used", "mx_Code", "mx_Theme"])); + + expect(fetchMock).toHaveFetchedTimes(1, "end:/bundle.css"); + expect(fetchMock).toHaveFetchedTimes(1, "end:/theme-light.css"); + expect(fetchMock).not.toHaveFetched("end:/theme-dark.css"); + + expect(css).toContain("margin:0"); + expect(css).toContain("#111"); + expect(css).toContain("#222"); + expect(css).toContain("#abcdef"); + + expect(css).not.toContain("@font-face"); + expect(css).not.toContain("#123456"); + expect(css).not.toContain("#654321"); + expect(css).not.toContain("#fedcba"); + + expect(css).not.toContain("font-family:Inter"); + expect(css).not.toContain("font-family:Fira Code"); + expect(css).toContain("BlinkMacSystemFont"); + expect(css).toContain("Menlo, Consolas"); + }); + + it("keeps export-only css in the app cascade layer after layered font rules", async () => { + document.head.innerHTML = ` + + `; + + fetchMock.get( + "end:/bundle.css", + ` + @layer compound-tokens, compound-web, shared-components, app-web; + @layer compound-web { + .mx_Typography { + font: var(--cpd-font-heading-lg-regular); + } + } + @layer app-web { + body { + font: var(--cpd-font-body-md-regular) !important; + } + } + `, + ); + + const css = await getExportCSS(new Set(["mx_Typography"])); + + expect(css).toContain("@layer compound-web{.mx_Typography{font:var(--cpd-font-heading-lg-regular)}}"); + expect(css).toContain("@layer app-web{body{font:var(--cpd-font-body-md-regular)!important}}"); + + const exportCssLayerIndex = css.indexOf("@layer app-web {"); + expect(exportCssLayerIndex).toBeGreaterThan( + css.indexOf("@layer app-web{body{font:var(--cpd-font-body-md-regular)!important}}"), + ); + expect(css.slice(exportCssLayerIndex)).toBe("@layer app-web {css-file-stub}"); + }); }); }); diff --git a/apps/web/test/viewmodels/room-list/RoomListHeaderViewModel-test.ts b/apps/web/test/viewmodels/room-list/RoomListHeaderViewModel-test.ts index 53f76d884f..e4afd6e7b8 100644 --- a/apps/web/test/viewmodels/room-list/RoomListHeaderViewModel-test.ts +++ b/apps/web/test/viewmodels/room-list/RoomListHeaderViewModel-test.ts @@ -323,6 +323,89 @@ describe("RoomListHeaderViewModel", () => { expect(createSectionSpy).toHaveBeenCalled(); }); + describe("collapseOrExpandSections", () => { + it("should dispatch RoomListCollapseAllSections when collapseSections is not 'expand'", () => { + const fireSpy = jest.spyOn(defaultDispatcher, "fire"); + vm = new RoomListHeaderViewModel({ matrixClient, spaceStore: SpaceStore.instance }); + + vm.collapseOrExpandSections(); + + expect(fireSpy).toHaveBeenCalledWith(Action.RoomListCollapseAllSections); + }); + + it("should dispatch RoomListExpandAllSections when collapseSections is 'expand'", () => { + const fireSpy = jest.spyOn(defaultDispatcher, "fire"); + vm = new RoomListHeaderViewModel({ matrixClient, spaceStore: SpaceStore.instance }); + + // Drive the VM into the "expand" state by simulating all sections collapsed + defaultDispatcher.dispatch( + { + action: Action.RoomListSectionsCollapseStateChanged, + collapseSections: "collapse", + }, + true, + ); + expect(vm.getSnapshot().collapseSections).toBe("expand"); + vm.collapseOrExpandSections(); + + expect(fireSpy).toHaveBeenCalledWith(Action.RoomListExpandAllSections); + }); + }); + + describe("RoomListSectionsCollapseStateChanged handling", () => { + it("should set collapseSections to 'expand' when collapseSections is collapse", () => { + vm = new RoomListHeaderViewModel({ matrixClient, spaceStore: SpaceStore.instance }); + + defaultDispatcher.dispatch( + { + action: Action.RoomListSectionsCollapseStateChanged, + collapseSections: "collapse", + }, + true, + ); + + expect(vm.getSnapshot().collapseSections).toBe("expand"); + }); + + it("should set collapseSections to 'collapse' when collapseSections is expand", () => { + vm = new RoomListHeaderViewModel({ matrixClient, spaceStore: SpaceStore.instance }); + + defaultDispatcher.dispatch( + { + action: Action.RoomListSectionsCollapseStateChanged, + collapseSections: "expand", + }, + true, + ); + + expect(vm.getSnapshot().collapseSections).toBe("collapse"); + }); + + it("should set collapseSections to undefined when collapseSections is undefined", () => { + vm = new RoomListHeaderViewModel({ matrixClient, spaceStore: SpaceStore.instance }); + + // First drive it into a non-undefined state + defaultDispatcher.dispatch( + { + action: Action.RoomListSectionsCollapseStateChanged, + collapseSections: "collapse", + }, + true, + ); + expect(vm.getSnapshot().collapseSections).toBe("expand"); + + defaultDispatcher.dispatch( + { + action: Action.RoomListSectionsCollapseStateChanged, + collapseSections: undefined, + }, + true, + ); + + expect(vm.getSnapshot().collapseSections).toBeUndefined(); + }); + }); + it("should toggle message preview from enabled to disabled", () => { jest.spyOn(SettingsStore, "getValue").mockImplementation((settingName: string) => { if (settingName === "RoomList.showMessagePreview") return true; diff --git a/apps/web/test/viewmodels/room-list/RoomListItemViewModel-test.tsx b/apps/web/test/viewmodels/room-list/RoomListItemViewModel-test.tsx index 10c7d66855..b545f316f9 100644 --- a/apps/web/test/viewmodels/room-list/RoomListItemViewModel-test.tsx +++ b/apps/web/test/viewmodels/room-list/RoomListItemViewModel-test.tsx @@ -664,7 +664,7 @@ describe("RoomListItemViewModel", () => { }); it("should update sections when OrderedCustomSections setting changes", () => { - let watchCallback: CallbackFn = () => {}; + let watchCallback: CallbackFn<"RoomList.OrderedCustomSections"> = () => {}; jest.spyOn(SettingsStore, "watchSetting").mockImplementation((setting, _room, callback) => { if (setting === "RoomList.OrderedCustomSections") watchCallback = callback; return "watcher-id"; diff --git a/apps/web/test/viewmodels/room-list/RoomListViewModel-test.tsx b/apps/web/test/viewmodels/room-list/RoomListViewModel-test.tsx index c0eacbdf35..c3fc431646 100644 --- a/apps/web/test/viewmodels/room-list/RoomListViewModel-test.tsx +++ b/apps/web/test/viewmodels/room-list/RoomListViewModel-test.tsx @@ -465,6 +465,20 @@ describe("RoomListViewModel", () => { }); }); + describe("notifyCollapseState", () => { + it("should dispatch collapseSections=undefined when feature_room_list_sections is disabled", () => { + viewModel = new RoomListViewModel({ client: matrixClient }); + + const dispatchSpy = jest.spyOn(dispatcher, "dispatch"); + RoomListStoreV3.instance.emit(RoomListStoreV3Event.ListsUpdate); + + expect(dispatchSpy).toHaveBeenCalledWith({ + action: Action.RoomListSectionsCollapseStateChanged, + collapseSections: undefined, + }); + }); + }); + describe("Keyboard navigation (ViewRoomDelta)", () => { beforeEach(() => { // stubClient sets up MatrixClientPeg which is needed when ViewRoom action is dispatched @@ -971,6 +985,96 @@ describe("RoomListViewModel", () => { expect(favSection!.roomIds).toEqual(["!fav1:server"]); }); + describe("Collapse/expand all sections", () => { + it("should collapse all sections when Action.RoomListCollapseAllSections is dispatched", async () => { + viewModel = new RoomListViewModel({ client: matrixClient }); + + const favHeader = viewModel.getSectionHeaderViewModel(DefaultTagID.Favourite); + const chatsHeader = viewModel.getSectionHeaderViewModel(CHATS_TAG); + expect(favHeader.isExpanded).toBe(true); + + dispatcher.dispatch({ action: Action.RoomListCollapseAllSections }); + await flushPromisesWithFakeTimers(); + + expect(favHeader.isExpanded).toBe(false); + expect(chatsHeader.isExpanded).toBe(false); + + const snapshot = viewModel.getSnapshot(); + expect(snapshot.sections.find((s) => s.id === DefaultTagID.Favourite)!.roomIds).toEqual([]); + expect(snapshot.sections.find((s) => s.id === CHATS_TAG)!.roomIds).toEqual([]); + }); + + it("should expand all sections when Action.RoomListExpandAllSections is dispatched", async () => { + viewModel = new RoomListViewModel({ client: matrixClient }); + + // Collapse first + const favHeader = viewModel.getSectionHeaderViewModel(DefaultTagID.Favourite); + favHeader.onClick(); + expect(favHeader.isExpanded).toBe(false); + + dispatcher.dispatch({ action: Action.RoomListExpandAllSections }); + await flushPromisesWithFakeTimers(); + + expect(favHeader.isExpanded).toBe(true); + const snapshot = viewModel.getSnapshot(); + expect(snapshot.sections.find((s) => s.id === DefaultTagID.Favourite)!.roomIds).toEqual([ + "!fav1:server", + "!fav2:server", + ]); + }); + }); + + describe("notifyCollapseState", () => { + it("should dispatch collapseSections=expand when all sections are expanded (default)", () => { + viewModel = new RoomListViewModel({ client: matrixClient }); + + const dispatchSpy = jest.spyOn(dispatcher, "dispatch"); + RoomListStoreV3.instance.emit(RoomListStoreV3Event.ListsUpdate); + + expect(dispatchSpy).toHaveBeenCalledWith({ + action: Action.RoomListSectionsCollapseStateChanged, + collapseSections: "expand", + }); + }); + + it("should dispatch collapseSection=collapse when all sections are collapsed", () => { + viewModel = new RoomListViewModel({ client: matrixClient }); + + // Collapse all sections + viewModel.getSectionHeaderViewModel(DefaultTagID.Favourite).isExpanded = false; + viewModel.getSectionHeaderViewModel(CHATS_TAG).isExpanded = false; + viewModel.getSectionHeaderViewModel(DefaultTagID.LowPriority).isExpanded = false; + + const dispatchSpy = jest.spyOn(dispatcher, "dispatch"); + RoomListStoreV3.instance.emit(RoomListStoreV3Event.ListsUpdate); + + expect(dispatchSpy).toHaveBeenCalledWith({ + action: Action.RoomListSectionsCollapseStateChanged, + collapseSections: "collapse", + }); + }); + + it("should dispatch collapseSection=undefined when it is a flat list", () => { + jest.spyOn(RoomListStoreV3.instance, "getSortedRoomsInActiveSpace").mockReturnValue({ + spaceId: "home", + sections: [ + { tag: DefaultTagID.Favourite, rooms: [] }, + { tag: CHATS_TAG, rooms: [regularRoom1] }, + { tag: DefaultTagID.LowPriority, rooms: [] }, + ], + }); + viewModel = new RoomListViewModel({ client: matrixClient }); + + const dispatchSpy = jest.spyOn(dispatcher, "dispatch"); + RoomListStoreV3.instance.emit(RoomListStoreV3Event.ListsUpdate); + + expect(dispatchSpy).toHaveBeenCalledWith({ + action: Action.RoomListSectionsCollapseStateChanged, + collapseSections: undefined, + }); + }); + }); + it("should apply sticky room within the correct section", async () => { stubClient(); viewModel = new RoomListViewModel({ client: matrixClient }); diff --git a/docs/labs.md b/docs/labs.md index 7919c4737f..e9786cddfe 100644 --- a/docs/labs.md +++ b/docs/labs.md @@ -131,5 +131,10 @@ joining a room. Replaces the legacy notification settings with a new one to manage push rules. +## User status (`feature_user_status`) + +Enables setting a status message in your profile and to be able to view other's statuses. +Requires [MSC4429](https://github.com/matrix-org/matrix-spec-proposals/pull/4429) and [MSC4426](https://github.com/matrix-org/matrix-spec-proposals/pull/4426). + **Warning** This feature has options which are not backwards compatible, disabling it may have unintended consequences. diff --git a/knip.ts b/knip.ts index 53718f2e15..9d4fab1d49 100644 --- a/knip.ts +++ b/knip.ts @@ -65,13 +65,8 @@ export default { ignoreExportsUsedInFile: true, compilers: { pcss: (text: string) => - [...text.matchAll(/(?<=@)import[^;]+/g)] - .map(([line]) => { - if (line.startsWith("import url(")) { - return line.replace("url(", "").slice(0, -1); - } - return line; - }) + [...text.matchAll(/@import\s+(?:url\()?["']([^"']+)["']\)?[^;]*;/g)] + .map(([, specifier]) => `import "${specifier}";`) .join("\n"), }, nx: { diff --git a/package.json b/package.json index 644e3bf96d..27cc32d0b7 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ "lodash": "^4.17.21", "mermaid": "^11.13.0", "minimist": "^1.2.6", - "nx": "22.6.5", + "nx": "22.7.0", "prettier": "3.8.3", "typescript": "catalog:", "vitepress": "^1.6.4", @@ -115,6 +115,6 @@ "engines": { "node": ">=22.18" }, - "packageManager": "pnpm@10.33.0+sha512.10568bb4a6afb58c9eb3630da90cc9516417abebd3fabbe6739f0ae795728da1491e9db5a544c76ad8eb7570f5c4bb3d6c637b2cb41bfdcdb47fa823c8649319", + "packageManager": "pnpm@10.33.2+sha512.a90faf6feeab71ad6c6e57f94e0fe1a12f5dcc22cd754db40ae9593eb6a3e0b6b12e3540218bb37ae083404b1f2ce6db2a4121e979829b4aff94b99f49da1cf8", "private": true } diff --git a/packages/module-api/package.json b/packages/module-api/package.json index c366f61942..c02b754185 100644 --- a/packages/module-api/package.json +++ b/packages/module-api/package.json @@ -30,21 +30,22 @@ "test:unit": "vitest" }, "devDependencies": { + "@element-hq/vite-common": "workspace:*", "@matrix-org/react-sdk-module-api": "^2.5.0", "@microsoft/api-extractor": "^7.49.1", "@types/node": "^22.10.7", "@types/react": "^19", "@types/react-dom": "^19.0.4", "@types/semver": "^7.5.8", - "@vitest/coverage-v8": "^4.0.0", + "@vitest/coverage-v8": "catalog:", "matrix-widget-api": "^1.17.0", "rollup-plugin-external-globals": "^0.13.0", "semver": "^7.6.3", "typescript": "^6.0.0", "unplugin-dts": "1.0.0-beta.6", - "vite": "^8.0.0", - "vitest": "^4.0.0", - "vitest-sonar-reporter": "^3.0.0" + "vite": "catalog:", + "vitest": "catalog:", + "vitest-sonar-reporter": "catalog:" }, "peerDependencies": { "@matrix-org/react-sdk-module-api": "*", diff --git a/packages/module-api/vite.config.ts b/packages/module-api/vite.config.ts index 43dd25d974..5aa3d1bd19 100644 --- a/packages/module-api/vite.config.ts +++ b/packages/module-api/vite.config.ts @@ -7,57 +7,43 @@ Please see LICENSE files in the repository root for full details. import { dirname, resolve } from "node:path"; import { fileURLToPath } from "node:url"; -import { defineConfig } from "vite"; +import { defineConfig, mergeConfig } from "vitest/config"; import dts from "unplugin-dts/vite"; import externalGlobals from "rollup-plugin-external-globals"; +import baseConfig from "@element-hq/vite-common/vite.config"; import packageJson from "./package.json" with { type: "json" }; 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", +export default mergeConfig( + baseConfig, + 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, }, - outDir: "lib", - target: "esnext", - sourcemap: true, - }, - plugins: [ - dts(), - externalGlobals({ - // Reuse React from the host app - react: "window.React", - }), - ], - define: { - // We cannot use `process.env.npm_package_version` as when building element-web with module-api set to `workspace` - // this would contain the version of element-web rather than that of the module-api. - __VERSION__: JSON.stringify(packageJson.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: { - provider: "v8", - include: ["src/**/*"], - reporter: [["lcov", { projectRoot: "../../" }]], - }, - reporters: [ - ["default", { summary: false }], - [ - "vitest-sonar-reporter", - { - outputFile: "coverage/sonar-report.xml", - onWritePath(path: string): string { - return `packages/element-web-module-api/${path}`; - }, - }, - ], + plugins: [ + dts(), + externalGlobals({ + // Reuse React from the host app + react: "window.React", + }), ], - }, -}); + define: { + // We cannot use `process.env.npm_package_version` as when building element-web with module-api set to `workspace` + // this would contain the version of element-web rather than that of the module-api. + __VERSION__: JSON.stringify(packageJson.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" } }, + }, + }), + true, +); diff --git a/packages/playwright-common/package.json b/packages/playwright-common/package.json index 978f924392..6effc87d1d 100644 --- a/packages/playwright-common/package.json +++ b/packages/playwright-common/package.json @@ -31,7 +31,7 @@ "@testcontainers/postgresql": "^11.0.0", "glob": "^13.0.5", "lodash-es": "^4.17.23", - "mailpit-api": "^1.2.0", + "mailpit-api": "^2.0.0", "strip-ansi": "^7.1.0", "testcontainers": "^11.0.0", "wait-on": "^9.0.4", diff --git a/packages/shared-components/.storybook/compound.css b/packages/shared-components/.storybook/compound.css index 42f8393666..dda8ccf13e 100644 --- a/packages/shared-components/.storybook/compound.css +++ b/packages/shared-components/.storybook/compound.css @@ -5,5 +5,8 @@ * Please see LICENSE files in the repository root for full details. */ -@import url("@vector-im/compound-design-tokens/assets/web/css/compound-design-tokens.css") layer(compound); -@import url("@vector-im/compound-web/dist/style.css"); +/* Shared cascade order: Compound tokens, Compound Web, shared components, then app overrides. */ +@layer compound-tokens, compound-web, shared-components, app-web; + +@import url("@vector-im/compound-design-tokens/assets/web/css/compound-design-tokens.css") layer(compound-tokens); +@import url("@vector-im/compound-web/dist/style.css") layer(compound-web); diff --git a/packages/shared-components/.storybook/main.ts b/packages/shared-components/.storybook/main.ts index 72cb55aea5..60fb388507 100644 --- a/packages/shared-components/.storybook/main.ts +++ b/packages/shared-components/.storybook/main.ts @@ -8,11 +8,13 @@ Please see LICENSE files in the repository root for full details. import type { StorybookConfig } from "@storybook/react-vite"; import fs from "node:fs"; import { nodePolyfills } from "vite-plugin-node-polyfills"; -import { mergeConfig } from "vite"; +import { mergeConfig, normalizePath, type Plugin } from "vite"; import { dirname, join } from "node:path"; import { fileURLToPath } from "node:url"; const __dirname = dirname(fileURLToPath(import.meta.url)); +const srcRoot = normalizePath(join(__dirname, "..", "src")); +const sharedComponentsLayer = "shared-components"; // Get a list of available languages so the language selector can display them at runtime const languageFiles = fs.readdirSync(join(__dirname, "..", "src", "i18n", "strings")).map((f) => f.slice(0, -5)); @@ -36,6 +38,24 @@ function getAbsolutePath(value: string): any { return dirname(fileURLToPath(import.meta.resolve(`${value}/package.json`))); } +function layerSharedComponentCssModules(): Plugin { + return { + name: "element-web-shared-components-storybook-css-layer", + enforce: "pre", + transform(code, id) { + const cssPath = normalizePath(id.split("?")[0]); + if (!cssPath.startsWith(srcRoot) || !cssPath.endsWith(".module.css")) { + return; + } + + return { + code: `@layer ${sharedComponentsLayer} {\n${code}\n}\n`, + map: null, + }; + }, + }; +} + const config: StorybookConfig = { stories: ["../src/**/*.stories.@(js|jsx|mjs|ts|tsx)"], addons: [ @@ -61,6 +81,7 @@ const config: StorybookConfig = { async viteFinal(config) { return mergeConfig(config, { plugins: [ + layerSharedComponentCssModules(), // Needed for counterpart to work nodePolyfills({ include: ["util"], globals: { global: false } }), { diff --git a/packages/shared-components/__vis__/linux/__baselines__/room-list/RoomListHeaderView/RoomListHeaderView.stories.tsx/collapse-sections-auto.png b/packages/shared-components/__vis__/linux/__baselines__/room-list/RoomListHeaderView/RoomListHeaderView.stories.tsx/collapse-sections-auto.png new file mode 100644 index 0000000000..333171f50e Binary files /dev/null and b/packages/shared-components/__vis__/linux/__baselines__/room-list/RoomListHeaderView/RoomListHeaderView.stories.tsx/collapse-sections-auto.png differ diff --git a/packages/shared-components/__vis__/linux/__baselines__/room/timeline/event-tile/EventTileView/DisambiguatedProfile/DisambiguatedProfile.stories.tsx/full-example-auto.png b/packages/shared-components/__vis__/linux/__baselines__/room/timeline/event-tile/EventTileView/DisambiguatedProfile/DisambiguatedProfile.stories.tsx/full-example-auto.png index fcc6af53b3..f016ac3067 100644 Binary files a/packages/shared-components/__vis__/linux/__baselines__/room/timeline/event-tile/EventTileView/DisambiguatedProfile/DisambiguatedProfile.stories.tsx/full-example-auto.png and b/packages/shared-components/__vis__/linux/__baselines__/room/timeline/event-tile/EventTileView/DisambiguatedProfile/DisambiguatedProfile.stories.tsx/full-example-auto.png differ diff --git a/packages/shared-components/__vis__/linux/__baselines__/room/timeline/event-tile/EventTileView/DisambiguatedProfile/DisambiguatedProfile.stories.tsx/with-user-status-auto.png b/packages/shared-components/__vis__/linux/__baselines__/room/timeline/event-tile/EventTileView/DisambiguatedProfile/DisambiguatedProfile.stories.tsx/with-user-status-auto.png new file mode 100644 index 0000000000..ad7c25d317 Binary files /dev/null and b/packages/shared-components/__vis__/linux/__baselines__/room/timeline/event-tile/EventTileView/DisambiguatedProfile/DisambiguatedProfile.stories.tsx/with-user-status-auto.png differ diff --git a/packages/shared-components/package.json b/packages/shared-components/package.json index 5f834d5432..9d0f114103 100644 --- a/packages/shared-components/package.json +++ b/packages/shared-components/package.json @@ -48,7 +48,7 @@ "build:doc": "nx typedoc", "lint": "pnpm lint:types && pnpm lint:js", "lint:js": "eslint --max-warnings 0 src", - "lint:types": "tsc --noEmit && tsc --noEmit -p tsconfig.node.json" + "lint:types": "nx lint:types" }, "dependencies": { "@element-hq/element-web-module-api": "workspace:*", @@ -71,6 +71,7 @@ }, "devDependencies": { "@element-hq/element-web-playwright-common": "workspace:*", + "@element-hq/vite-common": "workspace:*", "@fetch-mock/vitest": "^0.2.18", "@fontsource/inter": "catalog:", "@matrix-org/react-sdk-module-api": "^2.5.0", @@ -94,7 +95,7 @@ "@typescript-eslint/parser": "^8.53.1", "@vector-im/compound-web": "catalog:", "@vitest/browser-playwright": "^4.0.17", - "@vitest/coverage-v8": "^4.0.17", + "@vitest/coverage-v8": "catalog:", "eslint": "8", "eslint-config-google": "^0.14.0", "eslint-config-prettier": "^10.1.8", @@ -115,15 +116,15 @@ "typedoc-plugin-missing-exports": "^4.1.2", "typescript": "catalog:", "unplugin-dts": "1.0.0-beta.6", - "vite": "^8.0.0", + "vite": "catalog:", "vite-plugin-node-polyfills": "^0.26.0", - "vitest": "^4.0.18", - "vitest-sonar-reporter": "^3.0.0" + "vitest": "catalog:", + "vitest-sonar-reporter": "catalog:" }, "engines": { "node": ">=20.0.0" }, - "packageManager": "pnpm@10.33.0+sha512.10568bb4a6afb58c9eb3630da90cc9516417abebd3fabbe6739f0ae795728da1491e9db5a544c76ad8eb7570f5c4bb3d6c637b2cb41bfdcdb47fa823c8649319", + "packageManager": "pnpm@10.33.2+sha512.a90faf6feeab71ad6c6e57f94e0fe1a12f5dcc22cd754db40ae9593eb6a3e0b6b12e3540218bb37ae083404b1f2ce6db2a4121e979829b4aff94b99f49da1cf8", "peerDependencies": { "@vector-im/compound-web": "^8.3.5 || ^9.0.0" } diff --git a/packages/shared-components/project.json b/packages/shared-components/project.json index 1751c4c6c4..590ee73520 100644 --- a/packages/shared-components/project.json +++ b/packages/shared-components/project.json @@ -54,6 +54,14 @@ "cwd": "packages/shared-components" }, "dependsOn": ["typedoc", "^build:playwright"] + }, + "lint:types": { + "executor": "nx:run-commands", + "options": { + "commands": ["tsc --noEmit", "tsc --noEmit -p tsconfig.node.json"], + "cwd": "packages/shared-components" + }, + "dependsOn": ["^build"] } } } diff --git a/packages/shared-components/src/audio/PlayPauseButton/PlayPauseButton.module.css b/packages/shared-components/src/audio/PlayPauseButton/PlayPauseButton.module.css index 859f84bba9..74315673f1 100644 --- a/packages/shared-components/src/audio/PlayPauseButton/PlayPauseButton.module.css +++ b/packages/shared-components/src/audio/PlayPauseButton/PlayPauseButton.module.css @@ -6,6 +6,6 @@ */ .button { - border-radius: 32px !important; - background-color: var(--cpd-color-bg-subtle-primary) !important; + border-radius: 32px; + background-color: var(--cpd-color-bg-subtle-primary); } diff --git a/packages/shared-components/src/i18n/strings/en_EN.json b/packages/shared-components/src/i18n/strings/en_EN.json index d747318b37..fe6d4e0865 100644 --- a/packages/shared-components/src/i18n/strings/en_EN.json +++ b/packages/shared-components/src/i18n/strings/en_EN.json @@ -100,6 +100,7 @@ }, "appearance": "Appearance", "chat_moved": "Chat moved", + "collapse_all_sections": "Collapse all sections", "collapse_filters": "Collapse filter list", "empty": { "no_chats": "No chats yet", @@ -118,6 +119,7 @@ "show_activity": "See all activity", "show_chats": "Show all chats" }, + "expand_all_sections": "Expand all sections", "expand_filters": "Expand filter list", "filters": { "favourite": "Favourites", diff --git a/packages/shared-components/src/i18n/strings/fr.json b/packages/shared-components/src/i18n/strings/fr.json index 2fe4d11c69..45ef4ac2ba 100644 --- a/packages/shared-components/src/i18n/strings/fr.json +++ b/packages/shared-components/src/i18n/strings/fr.json @@ -99,6 +99,7 @@ "voice_call": "Ouvrir le salon %(roomName)s contenant un appel vocal." }, "appearance": "Apparence", + "chat_moved": "La conversation a été déplacée", "collapse_filters": "Réduire la liste des filtres", "empty": { "no_chats": "Pas encore de discussions", @@ -224,6 +225,7 @@ "message_timestamp_sent_at": "Envoyé à : %(dateTime)s", "url_preview": { "close": "Fermer l’aperçu", + "open_link": "Ouvrir le lien", "show_n_more": { "one": "Montrer %(count)s autre preview", "other": "Montrer %(count)s autres previews" diff --git a/packages/shared-components/src/i18n/strings/ru.json b/packages/shared-components/src/i18n/strings/ru.json index cc269cfaa3..fa4fb4a7e8 100644 --- a/packages/shared-components/src/i18n/strings/ru.json +++ b/packages/shared-components/src/i18n/strings/ru.json @@ -5,6 +5,7 @@ "action": { "back": "Назад", "click": "Нажмите", + "close": "Закрыть", "collapse": "Свернуть", "delete": "Удалить", "dismiss": "Закрыть", @@ -16,6 +17,7 @@ "invite": "Пригласить", "new_conversation": "Новый диалог", "new_room": "Новая комната", + "new_section": "Новый раздел", "new_video_room": "Новая видеокомната", "open_menu": "Открыть меню", "pause": "Пауза", @@ -34,6 +36,7 @@ "common": { "attachment": "Вложение", "encryption_enabled": "Шифрование включено", + "loading": "Загрузка…", "preferences": "Настройки", "state_encryption_enabled": "Экспериментальное шифрование включено" }, diff --git a/packages/shared-components/src/i18n/strings/zh_Hans.json b/packages/shared-components/src/i18n/strings/zh_Hans.json index 0a4469c2e7..bea7c6a4dd 100644 --- a/packages/shared-components/src/i18n/strings/zh_Hans.json +++ b/packages/shared-components/src/i18n/strings/zh_Hans.json @@ -129,6 +129,9 @@ "room_options": "房间选项", "section_created": "区域已创建", "section_header": { + "edit_section": "编辑区域", + "more_options": "更多选项", + "remove_section": "移除区域", "toggle": "切换区域 %(section)s", "toggle_unread": "切换到区域 %(section)s 中的未读房间" }, diff --git a/packages/shared-components/src/room-list/RoomListHeaderView/RoomListHeaderView.stories.tsx b/packages/shared-components/src/room-list/RoomListHeaderView/RoomListHeaderView.stories.tsx index 83551bc21b..d622503c1d 100644 --- a/packages/shared-components/src/room-list/RoomListHeaderView/RoomListHeaderView.stories.tsx +++ b/packages/shared-components/src/room-list/RoomListHeaderView/RoomListHeaderView.stories.tsx @@ -31,6 +31,7 @@ const RoomListHeaderViewWrapperImpl = ({ sort, toggleMessagePreview, createSection, + collapseOrExpandSections, ...rest }: RoomListHeaderProps): JSX.Element => { const vm = useMockedViewModel(rest, { @@ -44,6 +45,7 @@ const RoomListHeaderViewWrapperImpl = ({ openSpacePreferences, toggleMessagePreview, createSection, + collapseOrExpandSections, }); return ; }; @@ -65,6 +67,7 @@ const meta = { openSpacePreferences: fn(), toggleMessagePreview: fn(), createSection: fn(), + collapseOrExpandSections: fn(), }, parameters: { design: { @@ -109,3 +112,9 @@ export const PlusIcon: Story = { useComposeIcon: false, }, }; + +export const CollapseSections: Story = { + args: { + collapseSections: "collapse", + }, +}; diff --git a/packages/shared-components/src/room-list/RoomListHeaderView/RoomListHeaderView.test.tsx b/packages/shared-components/src/room-list/RoomListHeaderView/RoomListHeaderView.test.tsx index 48904171cd..d6ce1e9e3f 100644 --- a/packages/shared-components/src/room-list/RoomListHeaderView/RoomListHeaderView.test.tsx +++ b/packages/shared-components/src/room-list/RoomListHeaderView/RoomListHeaderView.test.tsx @@ -12,7 +12,7 @@ import React from "react"; import * as stories from "./RoomListHeaderView.stories"; -const { Default, NoComposeMenu, NoSpaceMenu } = composeStories(stories); +const { Default, NoComposeMenu, NoSpaceMenu, CollapseSections } = composeStories(stories); describe("RoomListHeaderView", () => { it("renders the default state", () => { @@ -29,4 +29,11 @@ describe("RoomListHeaderView", () => { const { container } = render(); expect(container).toMatchSnapshot(); }); + + it("should bind the collapse all sections action", () => { + const { getByRole } = render(); + const collapseButton = getByRole("button", { name: "Collapse all sections" }); + collapseButton.click(); + expect(CollapseSections.args?.collapseOrExpandSections).toHaveBeenCalled(); + }); }); diff --git a/packages/shared-components/src/room-list/RoomListHeaderView/RoomListHeaderView.tsx b/packages/shared-components/src/room-list/RoomListHeaderView/RoomListHeaderView.tsx index 05254899ea..d68fd25b0e 100644 --- a/packages/shared-components/src/room-list/RoomListHeaderView/RoomListHeaderView.tsx +++ b/packages/shared-components/src/room-list/RoomListHeaderView/RoomListHeaderView.tsx @@ -9,6 +9,7 @@ import React, { type JSX } from "react"; import { IconButton, H1 } from "@vector-im/compound-web"; import ComposeIcon from "@vector-im/compound-design-tokens/assets/web/icons/compose"; import PlusIcon from "@vector-im/compound-design-tokens/assets/web/icons/plus"; +import CollapseAllIcon from "@vector-im/compound-design-tokens/assets/web/icons/collapse-all"; import { type ViewModel, useViewModel } from "../../core/viewmodel"; import { Flex } from "../../core/utils/Flex"; @@ -21,6 +22,11 @@ import styles from "./RoomListHeaderView.module.css"; */ export type SortOption = "recent" | "alphabetical" | "unread-first"; +/** + * The available options for collapsing sections in the room list. + */ +export type CollapseSectionsOption = "collapse" | "expand"; + export interface RoomListHeaderViewSnapshot { /** * The title of the room list @@ -68,6 +74,12 @@ export interface RoomListHeaderViewSnapshot { * Whether to use the compose icon instead of the create icon. */ useComposeIcon: boolean; + /** + * If "collapse", an icon to collapse all sections is shown. + * If "expand", an icon to expand all sections is shown. + * If undefined, no icon are shown. + */ + collapseSections?: CollapseSectionsOption; } export interface RoomListHeaderViewActions { @@ -111,6 +123,10 @@ export interface RoomListHeaderViewActions { * Create a new section in the room list. */ createSection: () => void; + /** + * Collapse or expand all sections in the room list depending on the current state. + */ + collapseOrExpandSections: () => void; } /** @@ -136,7 +152,7 @@ interface RoomListHeaderViewProps { */ export function RoomListHeaderView({ vm }: Readonly): JSX.Element { const { translate: _t } = useI18n(); - const { title, displaySpaceMenu, displayComposeMenu, useComposeIcon } = useViewModel(vm); + const { title, displaySpaceMenu, displayComposeMenu, useComposeIcon, collapseSections } = useViewModel(vm); return ( ): J + {collapseSections && ( + vm.collapseOrExpandSections()} + tooltip={ + collapseSections === "collapse" + ? _t("room_list|collapse_all_sections") + : _t("room_list|expand_all_sections") + } + > + + + )} {/* If we don't display the compose menu, it means that the user can only send DM */} {displayComposeMenu ? ( diff --git a/packages/shared-components/src/room-list/RoomListHeaderView/index.ts b/packages/shared-components/src/room-list/RoomListHeaderView/index.ts index a0b6edee11..9e7bf46c2b 100644 --- a/packages/shared-components/src/room-list/RoomListHeaderView/index.ts +++ b/packages/shared-components/src/room-list/RoomListHeaderView/index.ts @@ -10,5 +10,6 @@ export type { RoomListHeaderViewSnapshot, RoomListHeaderViewActions, SortOption, + CollapseSectionsOption, } from "./RoomListHeaderView"; export { RoomListHeaderView } from "./RoomListHeaderView"; diff --git a/packages/shared-components/src/room-list/RoomListHeaderView/menu/OptionMenuView.module.css b/packages/shared-components/src/room-list/RoomListHeaderView/menu/OptionMenuView.module.css index 11a81da947..255babced3 100644 --- a/packages/shared-components/src/room-list/RoomListHeaderView/menu/OptionMenuView.module.css +++ b/packages/shared-components/src/room-list/RoomListHeaderView/menu/OptionMenuView.module.css @@ -7,5 +7,5 @@ .title { /* For first title, there is already enough space at the top */ - margin-top: 0 !important; + margin-top: 0; } diff --git a/packages/shared-components/src/room-list/RoomListHeaderView/test-utils.ts b/packages/shared-components/src/room-list/RoomListHeaderView/test-utils.ts index 37b53af6f3..70c51c45c8 100644 --- a/packages/shared-components/src/room-list/RoomListHeaderView/test-utils.ts +++ b/packages/shared-components/src/room-list/RoomListHeaderView/test-utils.ts @@ -24,6 +24,7 @@ export class MockedViewModel extends MockViewModel i public openSpacePreferences = vi.fn<() => void>(); public toggleMessagePreview = vi.fn<() => void>(); public createSection = vi.fn<() => void>(); + public collapseOrExpandSections = vi.fn<() => void>(); } export { defaultSnapshot } from "./default-snapshot"; diff --git a/packages/shared-components/src/room-list/RoomListSearchView/RoomListSearchView.module.css b/packages/shared-components/src/room-list/RoomListSearchView/RoomListSearchView.module.css index 9b7097373c..20b71a90bf 100644 --- a/packages/shared-components/src/room-list/RoomListSearchView/RoomListSearchView.module.css +++ b/packages/shared-components/src/room-list/RoomListSearchView/RoomListSearchView.module.css @@ -16,9 +16,8 @@ .search { /* The search button should take all the remaining space */ flex: 1; - /* !important is needed to override compound button in EW */ - font: var(--cpd-font-body-md-regular) !important; - color: var(--cpd-color-text-secondary) !important; + font: var(--cpd-font-body-md-regular); + color: var(--cpd-color-text-secondary); min-width: 0; svg { diff --git a/packages/shared-components/src/room/RoomStatusBar/RoomStatusBarView.module.css b/packages/shared-components/src/room/RoomStatusBar/RoomStatusBarView.module.css index c3832e2e9f..78300df3f9 100644 --- a/packages/shared-components/src/room/RoomStatusBar/RoomStatusBarView.module.css +++ b/packages/shared-components/src/room/RoomStatusBar/RoomStatusBarView.module.css @@ -7,18 +7,18 @@ .container { color: var(--cpd-color-text-primary); - svg { - /* Ensure button icons are primary too */ - color: var(--cpd-color-text-primary) !important; - } } -.secondaryAction svg { - color: var(--cpd-color-text-secondary) !important; +.container svg { + color: var(--cpd-color-text-primary); } -.primaryAction svg { - color: var(--cpd-color-text-on-solid-primary) !important; +.secondaryAction.secondaryAction[data-kind="secondary"] > svg { + color: var(--cpd-color-text-secondary); +} + +.primaryAction.primaryAction[data-kind="primary"] > svg { + color: var(--cpd-color-text-on-solid-primary); } .title { diff --git a/packages/shared-components/src/room/timeline/DateSeparatorView/DateSeparatorContextMenuView.module.css b/packages/shared-components/src/room/timeline/DateSeparatorView/DateSeparatorContextMenuView.module.css index fff772c4bd..75aa130936 100644 --- a/packages/shared-components/src/room/timeline/DateSeparatorView/DateSeparatorContextMenuView.module.css +++ b/packages/shared-components/src/room/timeline/DateSeparatorView/DateSeparatorContextMenuView.module.css @@ -6,15 +6,15 @@ */ .picker_menu { - max-inline-size: none !important; - padding: 0 !important; - gap: 0 !important; + max-inline-size: none; + padding: 0; + gap: 0; } .picker_menu_item { - padding: var(--cpd-space-3x) var(--cpd-space-5x) !important; + padding: var(--cpd-space-3x) var(--cpd-space-5x); } .picker_separator { - margin-inline: 0 !important; + margin-inline: 0; } diff --git a/packages/shared-components/src/room/timeline/DateSeparatorView/DateSeparatorDatePickerView.module.css b/packages/shared-components/src/room/timeline/DateSeparatorView/DateSeparatorDatePickerView.module.css index e664b1f5eb..f8e9894c38 100644 --- a/packages/shared-components/src/room/timeline/DateSeparatorView/DateSeparatorDatePickerView.module.css +++ b/packages/shared-components/src/room/timeline/DateSeparatorView/DateSeparatorDatePickerView.module.css @@ -6,15 +6,15 @@ */ .picker_menu_item { - padding: var(--cpd-space-3x) var(--cpd-space-5x) !important; + padding: var(--cpd-space-3x) var(--cpd-space-5x); } .picker_form { display: flex; - flex-direction: row !important; - flex-wrap: wrap !important; - padding: 0 !important; - gap: var(--cpd-space-2x) !important; + flex-direction: row; + flex-wrap: wrap; + padding: 0; + gap: var(--cpd-space-2x); color: var(--cpd-color-text-primary); font: var(--cpd-font-body-md-medium); } @@ -28,7 +28,7 @@ .picker_input_date { font: inherit; color-scheme: light; - padding: var(--cpd-space-2x) !important; + padding: var(--cpd-space-2x); :global(.cpd-theme-dark) &, :global(.cpd-theme-dark-hc) & { diff --git a/packages/shared-components/src/room/timeline/event-tile/EventTileView/DisambiguatedProfile/DisambiguatedProfile.module.css b/packages/shared-components/src/room/timeline/event-tile/EventTileView/DisambiguatedProfile/DisambiguatedProfile.module.css index 5733e1c71c..39b7c5227e 100644 --- a/packages/shared-components/src/room/timeline/event-tile/EventTileView/DisambiguatedProfile/DisambiguatedProfile.module.css +++ b/packages/shared-components/src/room/timeline/event-tile/EventTileView/DisambiguatedProfile/DisambiguatedProfile.module.css @@ -24,4 +24,8 @@ font-size: var(--cpd-font-size-body-sm); margin-inline-start: 5px; } + + .userStatus { + margin-inline-start: 5px; + } } diff --git a/packages/shared-components/src/room/timeline/event-tile/EventTileView/DisambiguatedProfile/DisambiguatedProfile.stories.tsx b/packages/shared-components/src/room/timeline/event-tile/EventTileView/DisambiguatedProfile/DisambiguatedProfile.stories.tsx index 3766c7b19d..02cd5913bc 100644 --- a/packages/shared-components/src/room/timeline/event-tile/EventTileView/DisambiguatedProfile/DisambiguatedProfile.stories.tsx +++ b/packages/shared-components/src/room/timeline/event-tile/EventTileView/DisambiguatedProfile/DisambiguatedProfile.stories.tsx @@ -40,6 +40,10 @@ const meta = { displayIdentifier: { control: "text" }, title: { control: "text" }, emphasizeDisplayName: { control: "boolean" }, + userStatus: { + status: { control: "string" }, + emoji: { control: "string" }, + }, }, args: { displayName: "Alice", @@ -82,6 +86,17 @@ export const WithTooltip: Story = { }, }; +export const WithUserStatus: Story = { + args: { + displayName: "Eve", + title: "Eve (@eve:matrix.org)", + userStatus: { + emoji: "🏝️", + text: "On holiday", + }, + }, +}; + export const FullExample: Story = { args: { displayName: "Eve", @@ -89,5 +104,9 @@ export const FullExample: Story = { colorClass: "mx_Username_color5", title: "Eve (@eve:matrix.org)", emphasizeDisplayName: true, + userStatus: { + emoji: "🏝️", + text: "On holiday", + }, }, }; diff --git a/packages/shared-components/src/room/timeline/event-tile/EventTileView/DisambiguatedProfile/DisambiguatedProfileView.tsx b/packages/shared-components/src/room/timeline/event-tile/EventTileView/DisambiguatedProfile/DisambiguatedProfileView.tsx index fa1102b81d..f7a3a2b62a 100644 --- a/packages/shared-components/src/room/timeline/event-tile/EventTileView/DisambiguatedProfile/DisambiguatedProfileView.tsx +++ b/packages/shared-components/src/room/timeline/event-tile/EventTileView/DisambiguatedProfile/DisambiguatedProfileView.tsx @@ -7,6 +7,7 @@ import React, { type JSX, type KeyboardEventHandler, type MouseEventHandler } from "react"; import classNames from "classnames"; +import { Text, Tooltip } from "@vector-im/compound-web"; import { type ViewModel, useViewModel } from "../../../../../core/viewmodel"; import styles from "./DisambiguatedProfile.module.css"; @@ -38,6 +39,14 @@ export interface DisambiguatedProfileViewSnapshot { * Whether to emphasize the display name with additional styling. */ emphasizeDisplayName?: boolean; + + /** + * User status message + */ + userStatus?: { + emoji: string; + text: string; + }; } /** @@ -80,7 +89,9 @@ interface DisambiguatedProfileViewProps { * ``` */ export function DisambiguatedProfileView({ vm, className }: Readonly): JSX.Element { - const { displayName, colorClass, displayIdentifier, title, emphasizeDisplayName } = useViewModel(vm); + const { displayName, colorClass, displayIdentifier, title, emphasizeDisplayName, userStatus } = useViewModel(vm); + + const userStatusEmoji = userStatus && [...new Intl.Segmenter().segment(userStatus.emoji)][0]?.segment; const displayNameClasses = classNames(colorClass, { [styles.disambiguatedProfile_displayName]: emphasizeDisplayName, @@ -115,6 +126,13 @@ export function DisambiguatedProfileView({ vm, className }: Readonly )} + {userStatus && ( + + + {userStatusEmoji} + + + )} ); } diff --git a/packages/shared-components/src/room/timeline/event-tile/EventTileView/DisambiguatedProfile/__snapshots__/DisambiguatedProfile.test.tsx.snap b/packages/shared-components/src/room/timeline/event-tile/EventTileView/DisambiguatedProfile/__snapshots__/DisambiguatedProfile.test.tsx.snap index 3ef52cea4d..9d4c1fc3d6 100644 --- a/packages/shared-components/src/room/timeline/event-tile/EventTileView/DisambiguatedProfile/__snapshots__/DisambiguatedProfile.test.tsx.snap +++ b/packages/shared-components/src/room/timeline/event-tile/EventTileView/DisambiguatedProfile/__snapshots__/DisambiguatedProfile.test.tsx.snap @@ -36,6 +36,11 @@ exports[`DisambiguatedProfileView > renders the full example 1`] = ` > @eve:matrix.org + + 🏝️ + `; diff --git a/packages/shared-components/src/room/timeline/event-tile/UrlPreviewGroupView/UrlPreviewGroupView.tsx b/packages/shared-components/src/room/timeline/event-tile/UrlPreviewGroupView/UrlPreviewGroupView.tsx index 79f0a96f93..01c7a07441 100644 --- a/packages/shared-components/src/room/timeline/event-tile/UrlPreviewGroupView/UrlPreviewGroupView.tsx +++ b/packages/shared-components/src/room/timeline/event-tile/UrlPreviewGroupView/UrlPreviewGroupView.tsx @@ -25,7 +25,14 @@ export interface UrlPreviewGroupViewSnapshot { } export interface UrlPreviewGroupViewProps { + /** + * The view model for the component. + */ vm: ViewModel & UrlPreviewGroupViewActions; + /** + * Extra CSS classes to apply to the component. + */ + className?: string; } export interface UrlPreviewGroupViewActions { @@ -42,7 +49,7 @@ export type UrlPreviewGroupViewModel = ViewModel +
    {previews.map((preview) => ( vm.onImageClick(preview)} {...preview} /> diff --git a/packages/shared-components/src/room/timeline/event-tile/actions/ActionBarView/ActionBarView.module.css b/packages/shared-components/src/room/timeline/event-tile/actions/ActionBarView/ActionBarView.module.css index 50cbb3f0d6..6b08fa635e 100644 --- a/packages/shared-components/src/room/timeline/event-tile/actions/ActionBarView/ActionBarView.module.css +++ b/packages/shared-components/src/room/timeline/event-tile/actions/ActionBarView/ActionBarView.module.css @@ -14,31 +14,31 @@ .toolbar_item { align-items: center; justify-content: center; - padding: 0 !important; - min-block-size: 0 !important; + padding: 0; + min-block-size: 0; - margin: 3px !important; - border-radius: 6px !important; + margin: 3px; + border-radius: 6px; &:hover { - background-color: var(--cpd-color-bg-subtle-secondary) !important; + background-color: var(--cpd-color-bg-subtle-secondary); z-index: 1; } } .toolbar_item[data-presentation="icon"] { - block-size: 28px !important; - inline-size: 28px !important; + block-size: 28px; + inline-size: 28px; - color: var(--cpd-color-icon-secondary) !important; + color: var(--cpd-color-icon-secondary); } .toolbar_item[data-presentation="label"] { - padding-inline-start: var(--cpd-space-2x) !important; - padding-inline-end: var(--cpd-space-2x) !important; + padding-inline-start: var(--cpd-space-2x); + padding-inline-end: var(--cpd-space-2x); font: var(--cpd-font-body-md-regular); - text-decoration: none !important; + text-decoration: none; white-space: nowrap; } } diff --git a/packages/shared-components/src/room/timeline/event-tile/body/AudioPlayerView/AudioPlayerView.module.css b/packages/shared-components/src/room/timeline/event-tile/body/AudioPlayerView/AudioPlayerView.module.css index f6bfec3969..1842cc90a1 100644 --- a/packages/shared-components/src/room/timeline/event-tile/body/AudioPlayerView/AudioPlayerView.module.css +++ b/packages/shared-components/src/room/timeline/event-tile/body/AudioPlayerView/AudioPlayerView.module.css @@ -6,8 +6,8 @@ */ .audioPlayer { - padding: var(--cpd-space-4x) var(--cpd-space-3x) var(--cpd-space-3x) var(--cpd-space-3x) !important; - border-radius: 8px !important; + padding: var(--cpd-space-4x) var(--cpd-space-3x) var(--cpd-space-3x) var(--cpd-space-3x); + border-radius: 8px; } .mediaInfo { diff --git a/packages/shared-components/src/room/timeline/event-tile/body/MFileBodyView/FileBodyView.module.css b/packages/shared-components/src/room/timeline/event-tile/body/MFileBodyView/FileBodyView.module.css index bf71413b93..52434c133d 100644 --- a/packages/shared-components/src/room/timeline/event-tile/body/MFileBodyView/FileBodyView.module.css +++ b/packages/shared-components/src/room/timeline/event-tile/body/MFileBodyView/FileBodyView.module.css @@ -26,8 +26,8 @@ & button { display: flex; - margin: 0 !important; - padding: 0 !important; + margin: 0; + padding: 0; border: none; width: 100%; min-block-size: unset; @@ -57,7 +57,6 @@ .content [data-type="download"] { align-items: center; - color: var(--cpd-color-text-action-accent); & iframe { margin: 0; diff --git a/packages/shared-components/src/room/timeline/event-tile/timestamp/MessageTimestampView/MessageTimestampView.module.css b/packages/shared-components/src/room/timeline/event-tile/timestamp/MessageTimestampView/MessageTimestampView.module.css index 6ee9d62aa1..e00b9f0678 100644 --- a/packages/shared-components/src/room/timeline/event-tile/timestamp/MessageTimestampView/MessageTimestampView.module.css +++ b/packages/shared-components/src/room/timeline/event-tile/timestamp/MessageTimestampView/MessageTimestampView.module.css @@ -6,7 +6,7 @@ */ .content { - color: var(--cpd-color-text-secondary) !important; /* override anchor color */ + color: var(--cpd-color-text-secondary); /* override anchor color */ font-size: var(--cpd-font-size-body-xs); font-variant-numeric: tabular-nums; display: inline-block; diff --git a/packages/shared-components/vite.config.ts b/packages/shared-components/vite.config.ts index 0a27742816..116689e645 100644 --- a/packages/shared-components/vite.config.ts +++ b/packages/shared-components/vite.config.ts @@ -6,12 +6,36 @@ * */ +import { readFileSync, writeFileSync } from "node:fs"; import { dirname, resolve } from "node:path"; import { fileURLToPath } from "node:url"; -import { defineConfig, esmExternalRequirePlugin } from "vite"; +import { defineConfig, esmExternalRequirePlugin, type Plugin } from "vite"; import dts from "unplugin-dts/vite"; const __dirname = dirname(fileURLToPath(import.meta.url)); +const cssLayerOrder = "@layer compound-tokens, compound-web, shared-components, app-web;"; +const sharedComponentsLayer = "shared-components"; + +function layerCssAssets(): Plugin { + return { + name: "element-web-shared-components-css-layer", + writeBundle(_options, bundle): void { + for (const asset of Object.values(bundle)) { + if (asset.type !== "asset" || asset.fileName !== "element-web-shared-components.css") { + continue; + } + + const cssPath = resolve(__dirname, "dist", asset.fileName); + const source = readFileSync(cssPath, "utf-8"); + if (source.startsWith(cssLayerOrder)) { + continue; + } + + writeFileSync(cssPath, `${cssLayerOrder}\n@layer ${sharedComponentsLayer} {\n${source}\n}\n`); + } + }, + }; +} export default defineConfig({ build: { @@ -50,6 +74,7 @@ export default defineConfig({ }, }, plugins: [ + layerCssAssets(), dts({ bundleTypes: true, include: ["src/**/*.{ts,tsx}"], diff --git a/packages/shared-components/vitest.config.ts b/packages/shared-components/vitest.config.ts index 1fa6c1b3a7..07cbe8c8e9 100644 --- a/packages/shared-components/vitest.config.ts +++ b/packages/shared-components/vitest.config.ts @@ -5,58 +5,18 @@ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial Please see LICENSE files in the repository root for full details. */ -import { defineConfig, ViteUserConfig } from "vitest/config"; +import { defineConfig, mergeConfig } from "vitest/config"; import path from "node:path"; import { fileURLToPath } from "node:url"; import { storybookTest } from "@storybook/addon-vitest/vitest-plugin"; import { storybookVis } from "storybook-addon-vis/vitest-plugin"; import { playwright, PlaywrightProviderOptions } from "@vitest/browser-playwright"; import { nodePolyfills } from "vite-plugin-node-polyfills"; -import { Reporter } from "vitest/reporters"; -import { env } from "process"; + +import baseConfig from "@element-hq/vite-common/vite.config"; const dirname = typeof __dirname !== "undefined" ? __dirname : path.dirname(fileURLToPath(import.meta.url)); -const reporters: NonNullable["reporters"] = [["default"]]; -const slowTestReporter: Reporter = { - onTestRunEnd(testModules, unhandledErrors, reason) { - const tests = testModules - .flatMap((m) => Array.from(m.children.allTests())) - .filter((test) => test.diagnostic()?.slow); - tests.sort((x, y) => x.diagnostic()?.duration! - y.diagnostic()?.duration!); - tests.reverse(); - if (tests.length > 0) { - console.warn("Slowest 10 tests:"); - } - for (const t of tests.slice(0, 10)) { - console.warn(`${t.module.moduleId} > ${t.fullName}: ${t.diagnostic()?.duration.toFixed(0)}ms`); - } - }, -}; - -// if we're running under GHA, enable the GHA & Sonar reporters -if (env["GITHUB_ACTIONS"] !== undefined) { - reporters.push([ - "github-actions", - { - silent: false, - }, - ]); - - reporters.push([ - "vitest-sonar-reporter", - { - outputFile: "coverage/sonar-report.xml", - onWritePath: (path) => `packages/shared-components/${path}`, - }, - ]); - - // if we're running against the develop branch, also enable the slow test reporter - if (env["GITHUB_REF"] == "refs/heads/develop") { - reporters.push(slowTestReporter); - } -} - const commonContextOptions: PlaywrightProviderOptions["contextOptions"] = { reducedMotion: "reduce", // Force consistent font rendering @@ -70,95 +30,93 @@ const commonLaunchOptions = { args: ["--font-render-hinting=none", "--disable-font-subpixel-positioning", "--disable-lcd-text"], }; -export default defineConfig({ - test: { - coverage: { - provider: "v8", - include: ["src/**/*.{ts,tsx}"], - exclude: ["src/**/*.stories.tsx"], - reporter: [["lcov", { projectRoot: "../../" }]], - }, - reporters, - globals: false, - pool: "threads", - projects: [ - { - extends: true, - plugins: [ - // The plugin will run tests for the stories defined in your Storybook config - // See options at: https://storybook.js.org/docs/next/writing-tests/integrations/vitest-addon#storybooktest - storybookTest({ - configDir: path.join(dirname, ".storybook"), - storybookScript: "storybook --ci", - tags: { - exclude: ["skip-test"], +export default mergeConfig( + baseConfig, + defineConfig({ + test: { + coverage: { + exclude: ["src/**/*.stories.tsx"], + }, + projects: [ + { + extends: true, + plugins: [ + // The plugin will run tests for the stories defined in your Storybook config + // See options at: https://storybook.js.org/docs/next/writing-tests/integrations/vitest-addon#storybooktest + storybookTest({ + configDir: path.join(dirname, ".storybook"), + storybookScript: "storybook --ci", + tags: { + exclude: ["skip-test"], + }, + }), + storybookVis({ + // 3px of difference allowed before marking as failed + failureThreshold: 3, + // When running in CI=1 mode, set the platform to `linux` as that is the platform where the browser-in-docker is running + snapshotRootDir: ({ ci, platform }) => `__vis__/${ci ? "linux" : platform}`, + }), + ], + test: { + name: "storybook", + browser: { + enabled: true, + headless: true, + provider: playwright({ + contextOptions: commonContextOptions, + launchOptions: commonLaunchOptions, + connectOptions: process.env.PW_TEST_CONNECT_WS_ENDPOINT + ? { + wsEndpoint: process.env.PW_TEST_CONNECT_WS_ENDPOINT, + exposeNetwork: "", + } + : undefined, + }), + instances: [{ browser: "chromium" }], }, - }), - storybookVis({ - // 3px of difference allowed before marking as failed - failureThreshold: 3, - // When running in CI=1 mode, set the platform to `linux` as that is the platform where the browser-in-docker is running - snapshotRootDir: ({ ci, platform }) => `__vis__/${ci ? "linux" : platform}`, - }), - ], - test: { - name: "storybook", - browser: { - enabled: true, - headless: true, - provider: playwright({ - contextOptions: commonContextOptions, - launchOptions: commonLaunchOptions, - connectOptions: process.env.PW_TEST_CONNECT_WS_ENDPOINT - ? { - wsEndpoint: process.env.PW_TEST_CONNECT_WS_ENDPOINT, - exposeNetwork: "", - } - : undefined, - }), - instances: [{ browser: "chromium" }], - }, - setupFiles: [".storybook/vitest.setup.ts"], - }, - }, - { - extends: true, - // as any is workaround for https://github.com/davidmyersdev/vite-plugin-node-polyfills/issues/150 - plugins: [nodePolyfills({ include: ["util"], globals: { global: false } }) as any], - test: { - name: "unit", - browser: { - enabled: true, - headless: true, - provider: playwright({ - // These tests don't actually take screenshots (at least at time of writing) - // but let's pass these options everywhere for consistency - contextOptions: commonContextOptions, - launchOptions: commonLaunchOptions, - }), - instances: [{ browser: "chromium" }], - }, - setupFiles: ["src/test/setupTests.ts"], - }, - css: { - modules: { - // Stabilise snapshots while keeping names distinct across CSS modules. - generateScopedName: "[name]_[local]", + setupFiles: [".storybook/vitest.setup.ts"], }, }, - }, - ], - }, - optimizeDeps: { - include: [ - "vite-plugin-node-polyfills/shims/buffer", - "vite-plugin-node-polyfills/shims/process", - "@vector-im/compound-design-tokens/assets/web/icons", - ], - }, - resolve: { - alias: { - "@test-utils": path.resolve(__dirname, "./src/test/utils/index.tsx"), + { + extends: true, + // as any is workaround for https://github.com/davidmyersdev/vite-plugin-node-polyfills/issues/150 + plugins: [nodePolyfills({ include: ["util"], globals: { global: false } }) as any], + test: { + name: "unit", + browser: { + enabled: true, + headless: true, + provider: playwright({ + // These tests don't actually take screenshots (at least at time of writing) + // but let's pass these options everywhere for consistency + contextOptions: commonContextOptions, + launchOptions: commonLaunchOptions, + }), + instances: [{ browser: "chromium" }], + }, + setupFiles: ["src/test/setupTests.ts"], + }, + css: { + modules: { + // Stabilise snapshots while keeping names distinct across CSS modules. + generateScopedName: "[name]_[local]", + }, + }, + }, + ], }, - }, -}); + optimizeDeps: { + include: [ + "vite-plugin-node-polyfills/shims/buffer", + "vite-plugin-node-polyfills/shims/process", + "@vector-im/compound-design-tokens/assets/web/icons", + ], + }, + resolve: { + alias: { + "@test-utils": path.resolve(__dirname, "./src/test/utils/index.tsx"), + }, + }, + }), + true, +); diff --git a/packages/vite-common/package.json b/packages/vite-common/package.json new file mode 100644 index 0000000000..4a335ac0e7 --- /dev/null +++ b/packages/vite-common/package.json @@ -0,0 +1,19 @@ +{ + "name": "@element-hq/vite-common", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "lint:types": "tsc --noEmit" + }, + "dependencies": { + "vitest": "catalog:" + }, + "devDependencies": { + "typescript": "catalog:" + }, + "peerDependencies": { + "@vitest/coverage-v8": "catalog:", + "vitest-sonar-reporter": "catalog:" + } +} diff --git a/packages/vite-common/tsconfig.json b/packages/vite-common/tsconfig.json new file mode 100644 index 0000000000..e540b512a3 --- /dev/null +++ b/packages/vite-common/tsconfig.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "target": "ESNext", + "module": "esnext", + "moduleResolution": "bundler", + "strict": true + }, + "include": ["vite.config.ts"] +} diff --git a/packages/vite-common/vite.config.ts b/packages/vite-common/vite.config.ts new file mode 100644 index 0000000000..f455b2d191 --- /dev/null +++ b/packages/vite-common/vite.config.ts @@ -0,0 +1,61 @@ +/* +Copyright 2026 Element Creations Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +import { defineConfig, type ViteUserConfig } from "vitest/config"; +import { type Reporter } from "vitest/reporters"; +import { env } from "node:process"; + +const reporters: NonNullable["reporters"] = [["default"]]; + +const slowTestReporter: Reporter = { + onTestRunEnd(testModules, unhandledErrors, reason) { + const tests = testModules + .flatMap((m) => Array.from(m.children.allTests())) + .filter((test) => test.diagnostic()?.slow); + tests.sort((x, y) => x.diagnostic()!.duration! - y.diagnostic()!.duration!); + tests.reverse(); + + if (tests.length > 0) { + console.warn("Slowest 10 tests:"); + } + for (const t of tests.slice(0, 10)) { + console.warn(`${t.module.moduleId} > ${t.fullName}: ${t.diagnostic()?.duration.toFixed(0)}ms`); + } + }, +}; + +// if we're running under GHA, enable the GHA & Sonar reporters +if (env["GITHUB_ACTIONS"] !== undefined) { + reporters.push(["github-actions", { silent: false }]); + reporters.push([ + "vitest-sonar-reporter", + { + outputFile: "coverage/sonar-report.xml", + onWritePath: (path): string => `${process.cwd()}/${path}`, + }, + ]); + + // if we're running against the develop branch, also enable the slow test reporter + if (env["GITHUB_REF"] == "refs/heads/develop") { + reporters.push(slowTestReporter); + } +} + +export default defineConfig({ + test: { + coverage: { + provider: "v8", + include: ["src/**/*.{ts,tsx}"], + reporter: [["lcov", { projectRoot: "../../" }]], + }, + environment: "node", + reporters, + pool: "threads", + globals: false, + include: ["src/**/*.test.ts"], + }, +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f038a47476..841534b316 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -13,11 +13,14 @@ catalogs: specifier: 1.59.1 version: 1.59.1 '@vector-im/compound-design-tokens': - specifier: 10.1.0 - version: 10.1.0 + specifier: 10.1.1 + version: 10.1.1 '@vector-im/compound-web': specifier: 9.2.1 version: 9.2.1 + '@vitest/coverage-v8': + specifier: 4.1.5 + version: 4.1.5 matrix-web-i18n: specifier: 3.6.0 version: 3.6.0 @@ -33,6 +36,15 @@ catalogs: typescript: specifier: 6.0.3 version: 6.0.3 + vite: + specifier: 8.0.10 + version: 8.0.10 + vitest: + specifier: 4.1.5 + version: 4.1.5 + vitest-sonar-reporter: + specifier: 3.0.0 + version: 3.0.0 overrides: pretty-format@30>react-is: 19.2.5 @@ -124,7 +136,7 @@ importers: version: 0.6.0 '@nx-tools/nx-container': specifier: ^7.2.1 - version: 7.2.1(@nx/devkit@22.6.5(nx@22.6.5))(@nx/js@22.6.5(@babel/traverse@7.29.0)(nx@22.6.5))(dotenv@17.4.2)(nx@22.6.5)(tslib@2.8.1) + version: 7.2.1(@nx/devkit@22.6.5(nx@22.7.0))(@nx/js@22.6.5(@babel/traverse@7.29.0)(nx@22.7.0))(dotenv@17.4.2)(nx@22.7.0)(tslib@2.8.1) '@playwright/test': specifier: 'catalog:' version: 1.59.1 @@ -136,7 +148,7 @@ importers: version: 3.14.0 eslint-plugin-matrix-org: specifier: ^3.0.0 - version: 3.0.0(756bde0db757d9adac5fccdfadf6b9bb) + version: 3.0.0(e6b158d923c3b8a20a719d9049b4bf8c) husky: specifier: ^9.0.0 version: 9.1.7 @@ -156,8 +168,8 @@ importers: specifier: ^1.2.6 version: 1.2.8 nx: - specifier: 22.6.5 - version: 22.6.5 + specifier: 22.7.0 + version: 22.7.0 prettier: specifier: 3.8.3 version: 3.8.3 @@ -216,6 +228,9 @@ importers: '@electron/fuses': specifier: ^2.1.1 version: 2.1.1 + '@element-hq/vite-common': + specifier: workspace:* + version: link:../../packages/vite-common '@playwright/test': specifier: 'catalog:' version: 1.59.1 @@ -239,10 +254,13 @@ importers: version: 11.1.8 '@typescript-eslint/eslint-plugin': specifier: ^8.0.0 - version: 8.58.2(@typescript-eslint/parser@8.58.2(eslint@8.57.1)(typescript@6.0.3))(eslint@8.57.1)(typescript@6.0.3) + version: 8.59.0(@typescript-eslint/parser@8.58.2(eslint@8.57.1)(typescript@6.0.3))(eslint@8.57.1)(typescript@6.0.3) '@typescript-eslint/parser': specifier: ^8.0.0 version: 8.58.2(eslint@8.57.1)(typescript@6.0.3) + '@vitest/coverage-v8': + specifier: 'catalog:' + version: 4.1.5(@vitest/browser@4.1.5)(vitest@4.1.5) app-builder-lib: specifier: 26.9.0 version: 26.9.0(patch_hash=2dfb3fcdfe573cca6c248cecf63ddea5c8fa0276859695fba6c9664d0ff285d6)(dmg-builder@26.9.0)(electron-builder-squirrel-windows@26.9.0) @@ -278,7 +296,7 @@ importers: version: 2.32.0(@typescript-eslint/parser@8.58.2(eslint@8.57.1)(typescript@6.0.3))(eslint@8.57.1) eslint-plugin-matrix-org: specifier: ^3.0.0 - version: 3.0.0(756bde0db757d9adac5fccdfadf6b9bb) + version: 3.0.0(e6b158d923c3b8a20a719d9049b4bf8c) eslint-plugin-n: specifier: ^17.12.0 version: 17.24.0(eslint@8.57.1)(typescript@6.0.3) @@ -291,6 +309,9 @@ importers: matrix-web-i18n: specifier: 'catalog:' version: 3.6.0 + memfs: + specifier: ^4.57.2 + version: 4.57.2(tslib@2.8.1) mkdirp: specifier: ^3.0.0 version: 3.0.1 @@ -309,6 +330,12 @@ importers: typescript: specifier: 6.0.3 version: 6.0.3 + vitest: + specifier: 'catalog:' + version: 4.1.5(@opentelemetry/api@1.9.1)(@types/node@18.19.130)(@vitest/browser-playwright@4.1.5)(@vitest/coverage-v8@4.1.5)(jsdom@26.1.0(patch_hash=040623e87b1c8b676c2a705513c0276c0704dd1b23fc3a1bb77cde8128b64b5f))(vite@8.0.10(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) + vitest-sonar-reporter: + specifier: 'catalog:' + version: 3.0.0(vitest@4.1.5) apps/web: dependencies: @@ -347,10 +374,10 @@ importers: version: 1.0.2 '@vector-im/compound-design-tokens': specifier: 'catalog:' - version: 10.1.0(@types/react@19.2.14)(react@19.2.5) + version: 10.1.1(@types/react@19.2.14)(react@19.2.5) '@vector-im/compound-web': specifier: 'catalog:' - version: 9.2.1(@fontsource/inconsolata@5.2.8)(@fontsource/inter@5.2.8)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(@vector-im/compound-design-tokens@10.1.0(@types/react@19.2.14)(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + version: 9.2.1(@fontsource/inconsolata@5.2.8)(@fontsource/inter@5.2.8)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(@vector-im/compound-design-tokens@10.1.1(@types/react@19.2.14)(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5) '@vector-im/matrix-wysiwyg': specifier: 2.40.0 version: 2.40.0(patch_hash=7bdf6150f2905bc2f055a6bcaa7b9d78fa7ffde82e800bcc454ac7b0096bd65e)(react@19.2.5) @@ -446,7 +473,7 @@ importers: version: 1.0.3 matrix-js-sdk: specifier: github:matrix-org/matrix-js-sdk#develop - version: https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/c09d3ca9e60b32e09111a83c6c7462d5442c6774 + version: https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/69985ee350a33ba75f1ad11f96468344f0c92a8d matrix-widget-api: specifier: ^1.17.0 version: 1.17.0 @@ -696,7 +723,7 @@ importers: version: 0.7.39 '@typescript-eslint/eslint-plugin': specifier: ^8.19.0 - version: 8.58.2(@typescript-eslint/parser@8.58.2(eslint@8.57.1)(typescript@6.0.3))(eslint@8.57.1)(typescript@6.0.3) + version: 8.59.0(@typescript-eslint/parser@8.58.2(eslint@8.57.1)(typescript@6.0.3))(eslint@8.57.1)(typescript@6.0.3) '@typescript-eslint/parser': specifier: ^8.19.0 version: 8.58.2(eslint@8.57.1)(typescript@6.0.3) @@ -741,13 +768,13 @@ importers: version: 2.32.0(@typescript-eslint/parser@8.58.2(eslint@8.57.1)(typescript@6.0.3))(eslint@8.57.1) eslint-plugin-jest: specifier: ^29.0.0 - version: 29.15.2(@typescript-eslint/eslint-plugin@8.58.2(@typescript-eslint/parser@8.58.2(eslint@8.57.1)(typescript@6.0.3))(eslint@8.57.1)(typescript@6.0.3))(eslint@8.57.1)(jest@30.3.0(@types/node@18.19.130)(babel-plugin-macros@3.1.0))(typescript@6.0.3) + version: 29.15.2(@typescript-eslint/eslint-plugin@8.59.0(@typescript-eslint/parser@8.58.2(eslint@8.57.1)(typescript@6.0.3))(eslint@8.57.1)(typescript@6.0.3))(eslint@8.57.1)(jest@30.3.0(@types/node@18.19.130)(babel-plugin-macros@3.1.0))(typescript@6.0.3) eslint-plugin-jsx-a11y: specifier: ^6.5.1 version: 6.10.2(eslint@8.57.1) eslint-plugin-matrix-org: specifier: ^3.0.0 - version: 3.0.0(756bde0db757d9adac5fccdfadf6b9bb) + version: 3.0.0(e6b158d923c3b8a20a719d9049b4bf8c) eslint-plugin-react: specifier: ^7.28.0 version: 7.37.5(eslint@8.57.1) @@ -911,6 +938,9 @@ importers: specifier: ^19 version: 19.2.5 devDependencies: + '@element-hq/vite-common': + specifier: workspace:* + version: link:../vite-common '@matrix-org/react-sdk-module-api': specifier: ^2.5.0 version: 2.5.0(patch_hash=016146c9cc96e6363609d2b2ac0896ccef567882eb1d73b75a77b8a30929de96)(react@19.2.5) @@ -930,7 +960,7 @@ importers: specifier: ^7.5.8 version: 7.7.1 '@vitest/coverage-v8': - specifier: ^4.0.0 + specifier: 'catalog:' version: 4.1.5(@vitest/browser@4.1.5)(vitest@4.1.5) matrix-widget-api: specifier: ^1.17.0 @@ -946,15 +976,15 @@ importers: version: 6.0.3 unplugin-dts: specifier: 1.0.0-beta.6 - version: 1.0.0-beta.6(patch_hash=c71b9e0f229ad1d76152030e8eceff26ee0b23c18619c205aa55fee274f89073)(@microsoft/api-extractor@7.58.5(@types/node@18.19.130))(esbuild@0.27.4)(rollup@4.60.1(patch_hash=603340e49399c6044e41a3998891667387d5ec1acbd38d4e5862f2ba3ef58de8))(typescript@6.0.3)(vite@8.0.9(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(webpack@5.106.2(esbuild@0.27.4)) + version: 1.0.0-beta.6(patch_hash=c71b9e0f229ad1d76152030e8eceff26ee0b23c18619c205aa55fee274f89073)(@microsoft/api-extractor@7.58.5(@types/node@18.19.130))(esbuild@0.27.4)(rollup@4.60.1(patch_hash=603340e49399c6044e41a3998891667387d5ec1acbd38d4e5862f2ba3ef58de8))(typescript@6.0.3)(vite@8.0.10(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(webpack@5.106.2(esbuild@0.27.4)) vite: - specifier: ^8.0.0 - version: 8.0.9(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) + specifier: 'catalog:' + version: 8.0.10(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) vitest: - specifier: ^4.0.0 - version: 4.1.5(@opentelemetry/api@1.9.1)(@types/node@18.19.130)(@vitest/browser-playwright@4.1.5)(@vitest/coverage-v8@4.1.5)(jsdom@26.1.0(patch_hash=040623e87b1c8b676c2a705513c0276c0704dd1b23fc3a1bb77cde8128b64b5f))(vite@8.0.9(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) + specifier: 'catalog:' + version: 4.1.5(@opentelemetry/api@1.9.1)(@types/node@18.19.130)(@vitest/browser-playwright@4.1.5)(@vitest/coverage-v8@4.1.5)(jsdom@26.1.0(patch_hash=040623e87b1c8b676c2a705513c0276c0704dd1b23fc3a1bb77cde8128b64b5f))(vite@8.0.10(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) vitest-sonar-reporter: - specifier: ^3.0.0 + specifier: 'catalog:' version: 3.0.0(vitest@4.1.5) packages/playwright-common: @@ -975,8 +1005,8 @@ importers: specifier: ^4.17.23 version: 4.18.1 mailpit-api: - specifier: ^1.2.0 - version: 1.9.0(react@19.2.5) + specifier: ^2.0.0 + version: 2.0.0 playwright-core: specifier: 'catalog:' version: 1.59.1 @@ -1013,7 +1043,7 @@ importers: version: 1.16.0 '@vector-im/compound-design-tokens': specifier: 'catalog:' - version: 10.1.0(@types/react@19.2.14)(react@19.2.5) + version: 10.1.1(@types/react@19.2.14)(react@19.2.5) classnames: specifier: ^2.5.1 version: 2.5.1 @@ -1060,6 +1090,9 @@ importers: '@element-hq/element-web-playwright-common': specifier: workspace:* version: link:../playwright-common + '@element-hq/vite-common': + specifier: workspace:* + version: link:../vite-common '@fetch-mock/vitest': specifier: ^0.2.18 version: 0.2.18(vitest@4.1.5) @@ -1080,10 +1113,10 @@ importers: version: 10.3.5(storybook@10.3.5(@testing-library/dom@10.4.1)(prettier@3.8.3)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)) '@storybook/addon-designs': specifier: ^11.0.1 - version: 11.1.3(@storybook/addon-docs@10.3.5(@types/react@19.2.14)(esbuild@0.27.4)(rollup@4.60.1(patch_hash=603340e49399c6044e41a3998891667387d5ec1acbd38d4e5862f2ba3ef58de8))(storybook@10.3.5(@testing-library/dom@10.4.1)(prettier@3.8.3)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(vite@8.0.9(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(webpack@5.106.2(esbuild@0.27.4)))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(storybook@10.3.5(@testing-library/dom@10.4.1)(prettier@3.8.3)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)) + version: 11.1.3(@storybook/addon-docs@10.3.5(@types/react@19.2.14)(esbuild@0.27.4)(rollup@4.60.1(patch_hash=603340e49399c6044e41a3998891667387d5ec1acbd38d4e5862f2ba3ef58de8))(storybook@10.3.5(@testing-library/dom@10.4.1)(prettier@3.8.3)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(vite@8.0.10(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(webpack@5.106.2(esbuild@0.27.4)))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(storybook@10.3.5(@testing-library/dom@10.4.1)(prettier@3.8.3)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)) '@storybook/addon-docs': specifier: ^10.0.7 - version: 10.3.5(@types/react@19.2.14)(esbuild@0.27.4)(rollup@4.60.1(patch_hash=603340e49399c6044e41a3998891667387d5ec1acbd38d4e5862f2ba3ef58de8))(storybook@10.3.5(@testing-library/dom@10.4.1)(prettier@3.8.3)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(vite@8.0.9(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(webpack@5.106.2(esbuild@0.27.4)) + version: 10.3.5(@types/react@19.2.14)(esbuild@0.27.4)(rollup@4.60.1(patch_hash=603340e49399c6044e41a3998891667387d5ec1acbd38d4e5862f2ba3ef58de8))(storybook@10.3.5(@testing-library/dom@10.4.1)(prettier@3.8.3)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(vite@8.0.10(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(webpack@5.106.2(esbuild@0.27.4)) '@storybook/addon-vitest': specifier: ^10.1.11 version: 10.3.5(@vitest/browser-playwright@4.1.5)(@vitest/browser@4.1.5)(@vitest/runner@4.1.5)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(storybook@10.3.5(@testing-library/dom@10.4.1)(prettier@3.8.3)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(vitest@4.1.5) @@ -1092,7 +1125,7 @@ importers: version: 2.0.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5) '@storybook/react-vite': specifier: ^10.0.7 - version: 10.3.5(esbuild@0.27.4)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(rollup@4.60.1(patch_hash=603340e49399c6044e41a3998891667387d5ec1acbd38d4e5862f2ba3ef58de8))(storybook@10.3.5(@testing-library/dom@10.4.1)(prettier@3.8.3)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(typescript@6.0.3)(vite@8.0.9(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(webpack@5.106.2(esbuild@0.27.4)) + version: 10.3.5(esbuild@0.27.4)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(rollup@4.60.1(patch_hash=603340e49399c6044e41a3998891667387d5ec1acbd38d4e5862f2ba3ef58de8))(storybook@10.3.5(@testing-library/dom@10.4.1)(prettier@3.8.3)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(typescript@6.0.3)(vite@8.0.10(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(webpack@5.106.2(esbuild@0.27.4)) '@stylistic/eslint-plugin': specifier: ^5.7.0 version: 5.10.0(eslint@8.57.1) @@ -1119,18 +1152,18 @@ importers: version: 19.2.3(@types/react@19.2.14) '@typescript-eslint/eslint-plugin': specifier: ^8.53.1 - version: 8.58.2(@typescript-eslint/parser@8.58.2(eslint@8.57.1)(typescript@6.0.3))(eslint@8.57.1)(typescript@6.0.3) + version: 8.59.0(@typescript-eslint/parser@8.58.2(eslint@8.57.1)(typescript@6.0.3))(eslint@8.57.1)(typescript@6.0.3) '@typescript-eslint/parser': specifier: ^8.53.1 version: 8.58.2(eslint@8.57.1)(typescript@6.0.3) '@vector-im/compound-web': specifier: 'catalog:' - version: 9.2.1(@fontsource/inconsolata@5.2.8)(@fontsource/inter@5.2.8)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(@vector-im/compound-design-tokens@10.1.0(@types/react@19.2.14)(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + version: 9.2.1(@fontsource/inconsolata@5.2.8)(@fontsource/inter@5.2.8)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(@vector-im/compound-design-tokens@10.1.1(@types/react@19.2.14)(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5) '@vitest/browser-playwright': specifier: ^4.0.17 - version: 4.1.5(playwright@1.59.1)(vite@8.0.9(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vitest@4.1.5) + version: 4.1.5(playwright@1.59.1)(vite@8.0.10(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vitest@4.1.5) '@vitest/coverage-v8': - specifier: ^4.0.17 + specifier: 'catalog:' version: 4.1.5(@vitest/browser@4.1.5)(vitest@4.1.5) eslint: specifier: '8' @@ -1152,7 +1185,7 @@ importers: version: 6.10.2(eslint@8.57.1) eslint-plugin-matrix-org: specifier: ^3.0.0 - version: 3.0.0(756bde0db757d9adac5fccdfadf6b9bb) + version: 3.0.0(e6b158d923c3b8a20a719d9049b4bf8c) eslint-plugin-react: specifier: ^7.37.5 version: 7.37.5(eslint@8.57.1) @@ -1176,7 +1209,7 @@ importers: version: 10.3.5(@testing-library/dom@10.4.1)(prettier@3.8.3)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) storybook-addon-vis: specifier: ^4.0.0 - version: 4.2.1(@storybook/addon-vitest@10.3.5(@vitest/browser-playwright@4.1.5)(@vitest/browser@4.1.5)(@vitest/runner@4.1.5)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(storybook@10.3.5(@testing-library/dom@10.4.1)(prettier@3.8.3)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(vitest@4.1.5))(@vitest/browser-playwright@4.1.5)(@vitest/browser@4.1.5)(babel-plugin-macros@3.1.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(storybook@10.3.5(@testing-library/dom@10.4.1)(prettier@3.8.3)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(typescript@6.0.3)(vitest@4.1.5) + version: 4.2.2(@storybook/addon-vitest@10.3.5(@vitest/browser-playwright@4.1.5)(@vitest/browser@4.1.5)(@vitest/runner@4.1.5)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(storybook@10.3.5(@testing-library/dom@10.4.1)(prettier@3.8.3)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(vitest@4.1.5))(@vitest/browser-playwright@4.1.5)(@vitest/browser@4.1.5)(babel-plugin-macros@3.1.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(storybook@10.3.5(@testing-library/dom@10.4.1)(prettier@3.8.3)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(typescript@6.0.3)(vitest@4.1.5) typedoc: specifier: ^0.28.16 version: 0.28.19(typescript@6.0.3) @@ -1191,20 +1224,36 @@ importers: version: 6.0.3 unplugin-dts: specifier: 1.0.0-beta.6 - version: 1.0.0-beta.6(patch_hash=c71b9e0f229ad1d76152030e8eceff26ee0b23c18619c205aa55fee274f89073)(@microsoft/api-extractor@7.58.5(@types/node@18.19.130))(esbuild@0.27.4)(rollup@4.60.1(patch_hash=603340e49399c6044e41a3998891667387d5ec1acbd38d4e5862f2ba3ef58de8))(typescript@6.0.3)(vite@8.0.9(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(webpack@5.106.2(esbuild@0.27.4)) + version: 1.0.0-beta.6(patch_hash=c71b9e0f229ad1d76152030e8eceff26ee0b23c18619c205aa55fee274f89073)(@microsoft/api-extractor@7.58.5(@types/node@18.19.130))(esbuild@0.27.4)(rollup@4.60.1(patch_hash=603340e49399c6044e41a3998891667387d5ec1acbd38d4e5862f2ba3ef58de8))(typescript@6.0.3)(vite@8.0.10(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(webpack@5.106.2(esbuild@0.27.4)) vite: - specifier: ^8.0.0 - version: 8.0.9(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) + specifier: 'catalog:' + version: 8.0.10(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) vite-plugin-node-polyfills: specifier: ^0.26.0 - version: 0.26.0(rollup@4.60.1(patch_hash=603340e49399c6044e41a3998891667387d5ec1acbd38d4e5862f2ba3ef58de8))(vite@8.0.9(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) + version: 0.26.0(rollup@4.60.1(patch_hash=603340e49399c6044e41a3998891667387d5ec1acbd38d4e5862f2ba3ef58de8))(vite@8.0.10(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) vitest: - specifier: ^4.0.18 - version: 4.1.5(@opentelemetry/api@1.9.1)(@types/node@18.19.130)(@vitest/browser-playwright@4.1.5)(@vitest/coverage-v8@4.1.5)(jsdom@26.1.0(patch_hash=040623e87b1c8b676c2a705513c0276c0704dd1b23fc3a1bb77cde8128b64b5f))(vite@8.0.9(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) + specifier: 'catalog:' + version: 4.1.5(@opentelemetry/api@1.9.1)(@types/node@18.19.130)(@vitest/browser-playwright@4.1.5)(@vitest/coverage-v8@4.1.5)(jsdom@26.1.0(patch_hash=040623e87b1c8b676c2a705513c0276c0704dd1b23fc3a1bb77cde8128b64b5f))(vite@8.0.10(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) vitest-sonar-reporter: - specifier: ^3.0.0 + specifier: 'catalog:' version: 3.0.0(vitest@4.1.5) + packages/vite-common: + dependencies: + '@vitest/coverage-v8': + specifier: 'catalog:' + version: 4.1.5(@vitest/browser@4.1.5)(vitest@4.1.5) + vitest: + specifier: 'catalog:' + version: 4.1.5(@opentelemetry/api@1.9.1)(@types/node@18.19.130)(@vitest/browser-playwright@4.1.5)(@vitest/coverage-v8@4.1.5)(jsdom@26.1.0(patch_hash=040623e87b1c8b676c2a705513c0276c0704dd1b23fc3a1bb77cde8128b64b5f))(vite@8.0.10(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) + vitest-sonar-reporter: + specifier: 'catalog:' + version: 3.0.0(vitest@4.1.5) + devDependencies: + typescript: + specifier: 'catalog:' + version: 6.0.3 + packages: 7zip-bin@5.2.0: @@ -2479,14 +2528,17 @@ packages: '@emnapi/core@1.10.0': resolution: {integrity: sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==} - '@emnapi/core@1.9.2': - resolution: {integrity: sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA==} + '@emnapi/core@1.4.5': + resolution: {integrity: sha512-XsLw1dEOpkSX/WucdqUhPWP7hDxSvZiY+fsUC14h+FtQ2Ifni4znbBt8punRX+Uj2JG/uDb8nEHVKvrVlvdZ5Q==} '@emnapi/runtime@1.10.0': resolution: {integrity: sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==} - '@emnapi/runtime@1.9.2': - resolution: {integrity: sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw==} + '@emnapi/runtime@1.4.5': + resolution: {integrity: sha512-++LApOtY0pEEz1zrd9vy1/zXVaVJJ/EbAF3u0fXIzPJEDtnITsBGbbK0EkM72amhl/R5b+5xx0Y/QhcVOpuulg==} + + '@emnapi/wasi-threads@1.0.4': + resolution: {integrity: sha512-PJR+bOmMOPH8AtcTGAyYNiuJ3/Fcoj2XN/gBEWzDIKh254XO+mM9XoXHk5GNEhodxeMznbg7BlRojVbKN+gC6g==} '@emnapi/wasi-threads@1.2.1': resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==} @@ -2822,6 +2874,10 @@ packages: node-notifier: optional: true + '@jest/diff-sequences@30.0.1': + resolution: {integrity: sha512-n5H8QLDJ47QqbCNn5SuFjCRDrOLEZ0h8vAHCK5RL9Ls7Xa8AQLa/YxAc9UjFqoEDM48muwtBGjtMY5cr0PLDCw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + '@jest/diff-sequences@30.3.0': resolution: {integrity: sha512-cG51MVnLq1ecVUaQ3fr6YuuAOitHK1S4WUJHnsPFE/quQr33ADUx1FfrTCpMCRxvy0Yr9BThKpDjSlcTi91tMA==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -2968,50 +3024,50 @@ packages: peerDependencies: tslib: '2' - '@jsonjoy.com/fs-core@4.56.10': - resolution: {integrity: sha512-PyAEA/3cnHhsGcdY+AmIU+ZPqTuZkDhCXQ2wkXypdLitSpd6d5Ivxhnq4wa2ETRWFVJGabYynBWxIijOswSmOw==} + '@jsonjoy.com/fs-core@4.57.2': + resolution: {integrity: sha512-SVjwklkpIV5wrynpYtuYnfYH1QF4/nDuLBX7VXdb+3miglcAgBVZb/5y0cOsehRV/9Vb+3UqhkMq3/NR3ztdkQ==} engines: {node: '>=10.0'} peerDependencies: tslib: '2' - '@jsonjoy.com/fs-fsa@4.56.10': - resolution: {integrity: sha512-/FVK63ysNzTPOnCCcPoPHt77TOmachdMS422txM4KhxddLdbW1fIbFMYH0AM0ow/YchCyS5gqEjKLNyv71j/5Q==} + '@jsonjoy.com/fs-fsa@4.57.2': + resolution: {integrity: sha512-fhO8+iR2I+OCw668ISDJdn1aArc9zx033sWejIyzQ8RBeXa9bDSaUeA3ix0poYOfrj1KdOzytmYNv2/uLDfV6g==} engines: {node: '>=10.0'} peerDependencies: tslib: '2' - '@jsonjoy.com/fs-node-builtins@4.56.10': - resolution: {integrity: sha512-uUnKz8R0YJyKq5jXpZtkGV9U0pJDt8hmYcLRrPjROheIfjMXsz82kXMgAA/qNg0wrZ1Kv+hrg7azqEZx6XZCVw==} + '@jsonjoy.com/fs-node-builtins@4.57.2': + resolution: {integrity: sha512-xhiegylRmhw43Ki2HO1ZBL7DQ5ja/qpRsL29VtQ2xuUHiuDGbgf2uD4p9Qd8hJI5P6RCtGYD50IXHXVq/Ocjcg==} engines: {node: '>=10.0'} peerDependencies: tslib: '2' - '@jsonjoy.com/fs-node-to-fsa@4.56.10': - resolution: {integrity: sha512-oH+O6Y4lhn9NyG6aEoFwIBNKZeYy66toP5LJcDOMBgL99BKQMUf/zWJspdRhMdn/3hbzQsZ8EHHsuekbFLGUWw==} + '@jsonjoy.com/fs-node-to-fsa@4.57.2': + resolution: {integrity: sha512-18LmWTSONhoAPW+IWRuf8w/+zRolPFGPeGwMxlAhhfY11EKzX+5XHDBPAw67dBF5dxDErHJbl40U+3IXSDRXSQ==} engines: {node: '>=10.0'} peerDependencies: tslib: '2' - '@jsonjoy.com/fs-node-utils@4.56.10': - resolution: {integrity: sha512-8EuPBgVI2aDPwFdaNQeNpHsyqPi3rr+85tMNG/lHvQLiVjzoZsvxA//Xd8aB567LUhy4QS03ptT+unkD/DIsNg==} + '@jsonjoy.com/fs-node-utils@4.57.2': + resolution: {integrity: sha512-rsPSJgekz43IlNbLyAM/Ab+ouYLWGp5DDBfYBNNEqDaSpsbXfthBn29Q4muFA9L0F+Z3mKo+CWlgSCXrf+mOyQ==} engines: {node: '>=10.0'} peerDependencies: tslib: '2' - '@jsonjoy.com/fs-node@4.56.10': - resolution: {integrity: sha512-7R4Gv3tkUdW3dXfXiOkqxkElxKNVdd8BDOWC0/dbERd0pXpPY+s2s1Mino+aTvkGrFPiY+mmVxA7zhskm4Ue4Q==} + '@jsonjoy.com/fs-node@4.57.2': + resolution: {integrity: sha512-nX2AdL6cOFwLdju9G4/nbRnYevmCJbh7N7hvR3gGm97Cs60uEjyd0rpR+YBS7cTg175zzl22pGKXR5USaQMvKg==} engines: {node: '>=10.0'} peerDependencies: tslib: '2' - '@jsonjoy.com/fs-print@4.56.10': - resolution: {integrity: sha512-JW4fp5mAYepzFsSGrQ48ep8FXxpg4niFWHdF78wDrFGof7F3tKDJln72QFDEn/27M1yHd4v7sKHHVPh78aWcEw==} + '@jsonjoy.com/fs-print@4.57.2': + resolution: {integrity: sha512-wK9NSow48i4DbDl9F1CQE5TqnyZOJ04elU3WFG5aJ76p+YxO/ulyBBQvKsessPxdo381Bc2pcEoyPujMOhcRqQ==} engines: {node: '>=10.0'} peerDependencies: tslib: '2' - '@jsonjoy.com/fs-snapshot@4.56.10': - resolution: {integrity: sha512-DkR6l5fj7+qj0+fVKm/OOXMGfDFCGXLfyHkORH3DF8hxkpDgIHbhf/DwncBMs2igu/ST7OEkexn1gIqoU6Y+9g==} + '@jsonjoy.com/fs-snapshot@4.57.2': + resolution: {integrity: sha512-GdduDZuoP5V/QCgJkx9+BZ6SC0EZ/smXAdTS7PfMqgMTGXLlt/bH/FqMYaqB9JmLf05sJPtO0XRbAwwkEEPbVw==} engines: {node: '>=10.0'} peerDependencies: tslib: '2' @@ -3286,55 +3342,109 @@ packages: cpu: [arm64] os: [darwin] + '@nx/nx-darwin-arm64@22.7.0': + resolution: {integrity: sha512-WczfOkv7bVSi0i4bLFmMLqdcRllJr+CLO0ibVapGHxdaOkNPCgSDxBVYC/0eg8yyMsPXQu8daaTvOlODNJw9GQ==} + cpu: [arm64] + os: [darwin] + '@nx/nx-darwin-x64@22.6.5': resolution: {integrity: sha512-9jICxb7vfJ56y/7Yuh3b/n1QJqWxO9xnXKYEs6SO8xPoW/KomVckILGc1C6RQSs6/3ixVJC7k1Dh1wm5tKPFrg==} cpu: [x64] os: [darwin] + '@nx/nx-darwin-x64@22.7.0': + resolution: {integrity: sha512-KGBq43lJbDjIpIAU5/sJFJQokGgCu/0KVe5pO2u9hMO2A9VaVtud5bGbDpkBiWoYBK9BLM3YpbG/RB74unxWxg==} + cpu: [x64] + os: [darwin] + '@nx/nx-freebsd-x64@22.6.5': resolution: {integrity: sha512-6B1wEKpqz5dI3AGMqttAVnA6M3DB/besAtuGyQiymK9ROlta1iuWgCcIYwcCQyhLn2Rx7vqj447KKcgCa8HlVw==} cpu: [x64] os: [freebsd] + '@nx/nx-freebsd-x64@22.7.0': + resolution: {integrity: sha512-DKOHaFC7Ko4+nlin/us8FEfBBgnpifZie4LELgo2nI044GlazGHJArtRSdLq3qKfWXqtieOKO3R4YV09Ax37Rg==} + cpu: [x64] + os: [freebsd] + '@nx/nx-linux-arm-gnueabihf@22.6.5': resolution: {integrity: sha512-xV50B8mnDPboct7JkAHftajI02s+8FszA8WTzhore+YGR+lEKHTLpucwGEaQuMlSdLplH7pQix4B4uK5pcMhZw==} cpu: [arm] os: [linux] + '@nx/nx-linux-arm-gnueabihf@22.7.0': + resolution: {integrity: sha512-LaZSE4FGk8KFyKF8grlhcoBWJabxkn7moO2QfULm3i1GY1AYu7bbxVZG42cAdGENqliCB8V2ARBWrMKq6mqW2g==} + cpu: [arm] + os: [linux] + '@nx/nx-linux-arm64-gnu@22.6.5': resolution: {integrity: sha512-2JkWuMGj+HpW6oPAvU5VdAx1afTnEbiM10Y3YOrl3fipWV4BiP5VDx762QTrfCraP4hl6yqTgvTe7F9xaby+jQ==} cpu: [arm64] os: [linux] libc: [glibc] + '@nx/nx-linux-arm64-gnu@22.7.0': + resolution: {integrity: sha512-g0GJ2avp0wuxHCQ2fjlMVjez4TWBtfj0jLCzO2I9M3B6ui0V+9Wh66DO5pLwyQFGbxODgwz9vwrEEZKlbDrchA==} + cpu: [arm64] + os: [linux] + libc: [glibc] + '@nx/nx-linux-arm64-musl@22.6.5': resolution: {integrity: sha512-Z/zMqFClnEyqDXouJKEPoWVhMQIif5F0YuECWBYjd3ZLwQsXGTItoh+6Wm3XF/nGMA2uLOHyTq/X7iFXQY3RzA==} cpu: [arm64] os: [linux] libc: [musl] + '@nx/nx-linux-arm64-musl@22.7.0': + resolution: {integrity: sha512-xb72G+LrhZNrWXOAd6aoDQmhzO5GKq6dkAYqhtOTAklCwIo5/4dkvqvFEe5RIQ7pk6RzdD2cUZMTJTr99rSGTg==} + cpu: [arm64] + os: [linux] + libc: [musl] + '@nx/nx-linux-x64-gnu@22.6.5': resolution: {integrity: sha512-FlotSyqNnaXSn0K+yWw+hRdYBwusABrPgKLyixfJIYRzsy+xPKN6pON6vZfqGwzuWF/9mEGReRz+iM8PiW0XSg==} cpu: [x64] os: [linux] libc: [glibc] + '@nx/nx-linux-x64-gnu@22.7.0': + resolution: {integrity: sha512-win7DkwxThhGWJlJ0s9HLxPzam/wz5MbQhKTQmLehHz8mgFJOu6MqzrccDxuT1E93dODedtxJuAyW4c80mpp4A==} + cpu: [x64] + os: [linux] + libc: [glibc] + '@nx/nx-linux-x64-musl@22.6.5': resolution: {integrity: sha512-RVOe2qcwhoIx6mxQURPjUfAW5SEOmT2gdhewvdcvX9ICq1hj5B2VarmkhTg0qroO7xiyqOqwq26mCzoV2I3NgQ==} cpu: [x64] os: [linux] libc: [musl] + '@nx/nx-linux-x64-musl@22.7.0': + resolution: {integrity: sha512-lU7yT+CsZFPCH3dZAVynhyJxa5w8rwCVtbDYbBa703DkNgb8/CQAthyE7NN2fjfzeQ0YpXoV3O/iX6sVY1sdjA==} + cpu: [x64] + os: [linux] + libc: [musl] + '@nx/nx-win32-arm64-msvc@22.6.5': resolution: {integrity: sha512-ZqurqI8VuYnsr2Kn4K4t+Gx6j/BZdf6qz/6Tv4A7XQQ6oNYVQgTqoNEFj+CCkVaIe6aIdCWpousFLqs+ZgBqYQ==} cpu: [arm64] os: [win32] + '@nx/nx-win32-arm64-msvc@22.7.0': + resolution: {integrity: sha512-e8P0ZSPJpaAjiIitcJAjdSCeCxAMjt5OqM6kZYUiwpHVnB31YAEe8feNvQcfvhyMjx/AXFQlBztHh8kANvdlLw==} + cpu: [arm64] + os: [win32] + '@nx/nx-win32-x64-msvc@22.6.5': resolution: {integrity: sha512-i2QFBJIuaYg9BHxrrnBV4O7W9rVL2k0pSIdk/rRp3EYJEU93iUng+qbZiY9wh1xvmXuUCE2G7TRd+8/SG/RFKg==} cpu: [x64] os: [win32] + '@nx/nx-win32-x64-msvc@22.7.0': + resolution: {integrity: sha512-NWLezNDRoZpqs8DudLCGBj214fIIG3cDhkez7eKW/i+s27O3laVCO9QgaFi07P+RSnrnZVJPfOISkE0Ql7gdAw==} + cpu: [x64] + os: [win32] + '@nx/workspace@22.6.5': resolution: {integrity: sha512-/CZtv1ESSfZ1MVqSlCsmnBWysU1z5VdNlwANlqL6BV2X6RUHKDPVj4YuNPvCK+0LsqyzfJdUt3pcnBYxnT5TIg==} @@ -3775,8 +3885,8 @@ packages: '@oxc-project/types@0.121.0': resolution: {integrity: sha512-CGtOARQb9tyv7ECgdAlFxi0Fv7lmzvmlm2rpD/RdijOO9rfk/JvB1CjT8EnoD+tjna/IYgKKw3IV7objRb+aYw==} - '@oxc-project/types@0.126.0': - resolution: {integrity: sha512-oGfVtjAgwQVVpfBrbtk4e1XDyWHRFta6BS3GWVzrF8xYBT2VGQAk39yJS/wFSMrZqoiCU4oghT3Ch0HaHGIHcQ==} + '@oxc-project/types@0.127.0': + resolution: {integrity: sha512-aIYXQBo4lCbO4z0R3FHeucQHpF46l2LbMdxRvqvuRuW2OxdnSkcng5B8+K12spgLDj93rtN3+J2Vac/TIO+ciQ==} '@oxc-resolver/binding-android-arm-eabi@11.19.1': resolution: {integrity: sha512-aUs47y+xyXHUKlbhqHUjBABjvycq6YSD7bpxSW7vplUmdzAlJ93yXY6ZR0c1o1x5A/QKbENCvs3+NlY8IpIVzg==} @@ -4371,103 +4481,106 @@ packages: resolution: {integrity: sha512-2FK1hF93Fuf1laSdfiEmJvSJPVIDHEUTz68D3Fi9s0IZrrpaEcj6pTFBTbYvsgC5du4ogrtf5re7yMMvrKNgkw==} engines: {node: ^20.9.0 || ^22.11.0 || ^24, pnpm: ^10.0.0} - '@rolldown/binding-android-arm64@1.0.0-rc.16': - resolution: {integrity: sha512-rhY3k7Bsae9qQfOtph2Pm2jZEA+s8Gmjoz4hhmx70K9iMQ/ddeae+xhRQcM5IuVx5ry1+bGfkvMn7D6MJggVSA==} + '@repobuddy/test@1.0.1': + resolution: {integrity: sha512-eNXZ6Cfs18EGQtPS/H68E0GQyQeB+al5sPcMgb70553CE0vs5Y7jDdO8Csgk/CHiuPy/rCKdN33OvvkSiGaG+A==} + + '@rolldown/binding-android-arm64@1.0.0-rc.17': + resolution: {integrity: sha512-s70pVGhw4zqGeFnXWvAzJDlvxhlRollagdCCKRgOsgUOH3N1l0LIxf83AtGzmb5SiVM4Hjl5HyarMRfdfj3DaQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@rolldown/binding-darwin-arm64@1.0.0-rc.16': - resolution: {integrity: sha512-rNz0yK078yrNn3DrdgN+PKiMOW8HfQ92jQiXxwX8yW899ayV00MLVdaCNeVBhG/TbH3ouYVObo8/yrkiectkcQ==} + '@rolldown/binding-darwin-arm64@1.0.0-rc.17': + resolution: {integrity: sha512-4ksWc9n0mhlZpZ9PMZgTGjeOPRu8MB1Z3Tz0Mo02eWfWCHMW1zN82Qz/pL/rC+yQa+8ZnutMF0JjJe7PjwasYw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@rolldown/binding-darwin-x64@1.0.0-rc.16': - resolution: {integrity: sha512-r/OmdR00HmD4i79Z//xO06uEPOq5hRXdhw7nzkxQxwSavs3PSHa1ijntdpOiZ2mzOQ3fVVu8C1M19FoNM+dMUQ==} + '@rolldown/binding-darwin-x64@1.0.0-rc.17': + resolution: {integrity: sha512-SUSDOI6WwUVNcWxd02QEBjLdY1VPHvlEkw6T/8nYG322iYWCTxRb1vzk4E+mWWYehTp7ERibq54LSJGjmouOsw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@rolldown/binding-freebsd-x64@1.0.0-rc.16': - resolution: {integrity: sha512-KcRE5w8h0OnjUatG8pldyD14/CQ5Phs1oxfR+3pKDjboHRo9+MkqQaiIZlZRpsxC15paeXme/I127tUa9TXJ6g==} + '@rolldown/binding-freebsd-x64@1.0.0-rc.17': + resolution: {integrity: sha512-hwnz3nw9dbJ05EDO/PvcjaaewqqDy7Y1rn1UO81l8iIK1GjenME75dl16ajbvSSMfv66WXSRCYKIqfgq2KCfxw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.16': - resolution: {integrity: sha512-bT0guA1bpxEJ/ZhTRniQf7rNF8ybvXOuWbNIeLABaV5NGjx4EtOWBTSRGWFU9ZWVkPOZ+HNFP8RMcBokBiZ0Kg==} + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.17': + resolution: {integrity: sha512-IS+W7epTcwANmFSQFrS1SivEXHtl1JtuQA9wlxrZTcNi6mx+FDOYrakGevvvTwgj2JvWiK8B29/qD9BELZPyXQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@rolldown/binding-linux-arm64-gnu@1.0.0-rc.16': - resolution: {integrity: sha512-+tHktCHWV8BDQSjemUqm/Jl/TPk3QObCTIjmdDy/nlupcujZghmKK2962LYrqFpWu+ai01AN/REOH3NEpqvYQg==} + '@rolldown/binding-linux-arm64-gnu@1.0.0-rc.17': + resolution: {integrity: sha512-e6usGaHKW5BMNZOymS1UcEYGowQMWcgZ71Z17Sl/h2+ZziNJ1a9n3Zvcz6LdRyIW5572wBCTH/Z+bKuZouGk9Q==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [glibc] - '@rolldown/binding-linux-arm64-musl@1.0.0-rc.16': - resolution: {integrity: sha512-3fPzdREH806oRLxpTWW1Gt4tQHs0TitZFOECB2xzCFLPKnSOy90gwA7P29cksYilFO6XVRY1kzga0cL2nRjKPg==} + '@rolldown/binding-linux-arm64-musl@1.0.0-rc.17': + resolution: {integrity: sha512-b/CgbwAJpmrRLp02RPfhbudf5tZnN9nsPWK82znefso832etkem8H7FSZwxrOI9djcdTP7U6YfNhbRnh7djErg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [musl] - '@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.16': - resolution: {integrity: sha512-EKwI1tSrLs7YVw+JPJT/G2dJQ1jl9qlTTTEG0V2Ok/RdOenRfBw2PQdLPyjhIu58ocdBfP7vIRN/pvMsPxs/AQ==} + '@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.17': + resolution: {integrity: sha512-4EII1iNGRUN5WwGbF/kOh/EIkoDN9HsupgLQoXfY+D1oyJm7/F4t5PYU5n8SWZgG0FEwakyM8pGgwcBYruGTlA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] libc: [glibc] - '@rolldown/binding-linux-s390x-gnu@1.0.0-rc.16': - resolution: {integrity: sha512-Uknladnb3Sxqu6SEcqBldQyJUpk8NleooZEc0MbRBJ4inEhRYWZX0NJu12vNf2mqAq7gsofAxHrGghiUYjhaLQ==} + '@rolldown/binding-linux-s390x-gnu@1.0.0-rc.17': + resolution: {integrity: sha512-AH8oq3XqQo4IibpVXvPeLDI5pzkpYn0WiZAfT05kFzoJ6tQNzwRdDYQ45M8I/gslbodRZwW8uxLhbSBbkv96rA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] libc: [glibc] - '@rolldown/binding-linux-x64-gnu@1.0.0-rc.16': - resolution: {integrity: sha512-FIb8+uG49sZBtLTn+zt1AJ20TqVcqWeSIyoVt0or7uAWesgKaHbiBh6OpA/k9v0LTt+PTrb1Lao133kP4uVxkg==} + '@rolldown/binding-linux-x64-gnu@1.0.0-rc.17': + resolution: {integrity: sha512-cLnjV3xfo7KslbU41Z7z8BH/E1y5mzUYzAqih1d1MDaIGZRCMqTijqLv76/P7fyHuvUcfGsIpqCdddbxLLK9rA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [glibc] - '@rolldown/binding-linux-x64-musl@1.0.0-rc.16': - resolution: {integrity: sha512-RuERhF9/EgWxZEXYWCOaViUWHIboceK4/ivdtQ3R0T44NjLkIIlGIAVAuCddFxsZ7vnRHtNQUrt2vR2n2slB2w==} + '@rolldown/binding-linux-x64-musl@1.0.0-rc.17': + resolution: {integrity: sha512-0phclDw1spsL7dUB37sIARuis2tAgomCJXAHZlpt8PXZ4Ba0dRP1e+66lsRqrfhISeN9bEGNjQs+T/Fbd7oYGw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [musl] - '@rolldown/binding-openharmony-arm64@1.0.0-rc.16': - resolution: {integrity: sha512-mXcXnvd9GpazCxeUCCnZ2+YF7nut+ZOEbE4GtaiPtyY6AkhZWbK70y1KK3j+RDhjVq5+U8FySkKRb/+w0EeUwA==} + '@rolldown/binding-openharmony-arm64@1.0.0-rc.17': + resolution: {integrity: sha512-0ag/hEgXOwgw4t8QyQvUCxvEg+V0KBcA6YuOx9g0r02MprutRF5dyljgm3EmR02O292UX7UeS6HzWHAl6KgyhA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - '@rolldown/binding-wasm32-wasi@1.0.0-rc.16': - resolution: {integrity: sha512-3Q2KQxnC8IJOLqXmUMoYwyIPZU9hzRbnHaoV3Euz+VVnjZKcY8ktnNP8T9R4/GGQtb27C/UYKABxesKWb8lsvQ==} + '@rolldown/binding-wasm32-wasi@1.0.0-rc.17': + resolution: {integrity: sha512-LEXei6vo0E5wTGwpkJ4KoT3OZJRnglwldt5ziLzOlc6qqb55z4tWNq2A+PFqCJuvWWdP53CVhG1Z9NtToDPJrA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [wasm32] - '@rolldown/binding-win32-arm64-msvc@1.0.0-rc.16': - resolution: {integrity: sha512-tj7XRemQcOcFwv7qhpUxMTBbI5mWMlE4c1Omhg5+h8GuLXzyj8HviYgR+bB2DMDgRqUE+jiDleqSCRjx4aYk/Q==} + '@rolldown/binding-win32-arm64-msvc@1.0.0-rc.17': + resolution: {integrity: sha512-gUmyzBl3SPMa6hrqFUth9sVfcLBlYsbMzBx5PlexMroZStgzGqlZ26pYG89rBb45Mnia+oil6YAIFeEWGWhoZA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@rolldown/binding-win32-x64-msvc@1.0.0-rc.16': - resolution: {integrity: sha512-PH5DRZT+F4f2PTXRXR8uJxnBq2po/xFtddyabTJVJs/ZYVHqXPEgNIr35IHTEa6bpa0Q8Awg+ymkTaGnKITw4g==} + '@rolldown/binding-win32-x64-msvc@1.0.0-rc.17': + resolution: {integrity: sha512-3hkiolcUAvPB9FLb3UZdfjVVNWherN1f/skkGWJP/fgSQhYUZpSIRr0/I8ZK9TkF3F7kxvJAk0+IcKvPHk9qQg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] - '@rolldown/pluginutils@1.0.0-rc.16': - resolution: {integrity: sha512-45+YtqxLYKDWQouLKCrpIZhke+nXxhsw+qAHVzHDVwttyBlHNBVs2K25rDXrZzhpTp9w1FlAlvweV1H++fdZoA==} + '@rolldown/pluginutils@1.0.0-rc.17': + resolution: {integrity: sha512-n8iosDOt6Ig1UhJ2AYqoIhHWh/isz0xpicHTzpKBeotdVsTEcxsSA/i3EVM7gQAj0rU27OLAxCjzlj15IWY7bg==} '@rollup/plugin-inject@5.0.5': resolution: {integrity: sha512-2+DEJbNBoPROPkgTDNe8/1YXWcqxbN5DTjASVIOx8HS+pITXushyNiBV56RB08zuptzz8gT3YfkqriTBVycepg==} @@ -5593,11 +5706,11 @@ packages: '@types/yauzl@2.10.3': resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==} - '@typescript-eslint/eslint-plugin@8.58.2': - resolution: {integrity: sha512-aC2qc5thQahutKjP+cl8cgN9DWe3ZUqVko30CMSZHnFEHyhOYoZSzkGtAI2mcwZ38xeImDucI4dnqsHiOYuuCw==} + '@typescript-eslint/eslint-plugin@8.59.0': + resolution: {integrity: sha512-HyAZtpdkgZwpq8Sz3FSUvCR4c+ScbuWa9AksK2Jweub7w4M3yTz4O11AqVJzLYjy/B9ZWPyc81I+mOdJU/bDQw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^8.58.2 + '@typescript-eslint/parser': ^8.59.0 eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' @@ -5640,8 +5753,8 @@ packages: peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/type-utils@8.58.2': - resolution: {integrity: sha512-Z7EloNR/B389FvabdGeTo2XMs4W9TjtPiO9DAsmT0yom0bwlPyRjkJ1uCdW1DvrrrYP50AJZ9Xc3sByZA9+dcg==} + '@typescript-eslint/type-utils@8.59.0': + resolution: {integrity: sha512-3TRiZaQSltGqGeNrJzzr1+8YcEobKH9rHnqIp/1psfKFmhRQDNMGP5hBufanYTGznwShzVLs3Mz+gDN7HkWfXg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 @@ -5667,13 +5780,6 @@ packages: peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/utils@8.58.2': - resolution: {integrity: sha512-QZfjHNEzPY8+l0+fIXMvuQ2sJlplB4zgDZvA+NmvZsZv3EQwOcc1DuIU1VJUTWZ/RKouBMhDyNaBMx4sWvrzRA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 - typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/utils@8.59.0': resolution: {integrity: sha512-I1R/K7V07XsMJ12Oaxg/O9GfrysGTmCRhvZJBv0RE0NcULMzjqVpR5kRRQjHsz3J/bElU7HwCO7zkqL+MSUz+g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -5798,8 +5904,8 @@ packages: '@upsetjs/venn.js@2.0.0': resolution: {integrity: sha512-WbBhLrooyePuQ1VZxrJjtLvTc4NVfpOyKx0sKqioq9bX1C1m7Jgykkn8gLrtwumBioXIqam8DLxp88Adbue6Hw==} - '@vector-im/compound-design-tokens@10.1.0': - resolution: {integrity: sha512-o+7DGx+NygpT2NPE1Jo//7NZDuyjzRH06eRchS0ZlkJicKx/impEmShmHU/XiE4P84BIFOo9eZ1Ws+rAym6Tuw==} + '@vector-im/compound-design-tokens@10.1.1': + resolution: {integrity: sha512-f2rdTilbPeOjrX7Mh9iTPcp5VergY7JLLWzKVjwMvpT0wtoFKwn59D1hwX2QInpiG70QTCxEdQFYLxQKvJQ74Q==} peerDependencies: '@types/react': ^19.2.10 react: ^17 || ^18 || ^19.0.0 @@ -6472,6 +6578,10 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + balanced-match@4.0.3: + resolution: {integrity: sha512-1pHv8LX9CpKut1Zp4EXey7Z8OfH11ONNH6Dhi2WDUt31VVZFXZzKwXcysBgqSumFCmR+0dqjMK5v5JiFHzi0+g==} + engines: {node: 20 || >=22} + balanced-match@4.0.4: resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} engines: {node: 18 || 20 || >=22} @@ -6594,6 +6704,10 @@ packages: brace-expansion@2.1.0: resolution: {integrity: sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==} + brace-expansion@5.0.2: + resolution: {integrity: sha512-Pdk8c9poy+YhOgVWw1JNN22/HcivgKWwpxKq04M/jTmHyCZn12WPJebZxdjSa5TmBqISrUSgNYU3eRORljfCCw==} + engines: {node: 20 || >=22} + brace-expansion@5.0.5: resolution: {integrity: sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==} engines: {node: 18 || 20 || >=22} @@ -7697,6 +7811,10 @@ packages: resolution: {integrity: sha512-zIHwmZPRshsCdpMDyVsqGmgyP0yT8GAgXUnkdAoJisxvf33k7yO6OuoKmcTGuXPWSsm8Oh88nZicRLA9Y0rUeA==} engines: {node: '>=12'} + dotenv-expand@12.0.3: + resolution: {integrity: sha512-uc47g4b+4k/M/SeaW1y4OApx+mtLWl92l5LMPP0GNXctZqELk+YGgOPIIC5elYmUH4OuoK3JLhuRUYegeySiFA==} + engines: {node: '>=12'} + dotenv@16.4.7: resolution: {integrity: sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==} engines: {node: '>=12'} @@ -8162,9 +8280,6 @@ packages: resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} engines: {node: '>= 0.6'} - event-target-polyfill@0.0.4: - resolution: {integrity: sha512-Gs6RLjzlLRdT8X9ZipJdIZI/Y6/HhRLyq9RdDlCsnpxr/+Nn6bU2EFGuC94GjxqhM+Nmij2Vcq98yoHrU8uNFQ==} - event-target-shim@5.0.1: resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} engines: {node: '>=6'} @@ -8665,6 +8780,10 @@ packages: resolution: {integrity: sha512-iZyKG96/JwPz1N55vj2Ie2vXbhu440zfUfJvSwEqEbeLluk7NnapfGqa7LH0mOsnDxTF85Mx8/dyR6HfqcbmbQ==} engines: {node: '>=20'} + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + hasown@2.0.3: resolution: {integrity: sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==} engines: {node: '>= 0.4'} @@ -9218,11 +9337,6 @@ packages: resolution: {integrity: sha512-u4sej9B1LPSxTGKB/HiuzvEQnXH0ECYkSVQU39koSwmFAxhlEAFl9RdTvLv4TOTQUgBS5O3O5fwUxk6byBZ+IQ==} engines: {node: '>=10'} - isomorphic-ws@5.0.0: - resolution: {integrity: sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==} - peerDependencies: - ws: '*' - istanbul-lib-coverage@3.2.2: resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} engines: {node: '>=8'} @@ -9810,8 +9924,8 @@ packages: magicast@0.5.2: resolution: {integrity: sha512-E3ZJh4J3S9KfwdjZhe2afj6R9lGIN5Pher1pF39UGrXRqq/VDaGVIGN13BjHd2u8B61hArAGOnso7nBOouW3TQ==} - mailpit-api@1.9.0: - resolution: {integrity: sha512-KoZIR7vfE7boEBLZ7fOw6SpGxn2pF207eLEWCoYePKAayFe3zYxGUgxNwxzEAp0hwVYSoe0lJQdeprHLEuelhA==} + mailpit-api@2.0.0: + resolution: {integrity: sha512-zK68B0CRVZaJS1PaX7omFhKUKSLp4JMMpAUTRYbCmcrgJ9nguIh4sS+RRUyFeW1nykDNH8PVlRz/WSXsq9ZqlQ==} engines: {node: '>=18.0.0'} make-dir@4.0.0: @@ -9863,8 +9977,8 @@ packages: matrix-events-sdk@0.0.1: resolution: {integrity: sha512-1QEOsXO+bhyCroIe2/A5OwaxHvBm7EsSQ46DEDn8RBIfQwN5HWBpFvyWWR4QY0KHPPnnJdI99wgRiAl7Ad5qaA==} - matrix-js-sdk@https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/c09d3ca9e60b32e09111a83c6c7462d5442c6774: - resolution: {tarball: https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/c09d3ca9e60b32e09111a83c6c7462d5442c6774} + matrix-js-sdk@https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/69985ee350a33ba75f1ad11f96468344f0c92a8d: + resolution: {tarball: https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/69985ee350a33ba75f1ad11f96468344f0c92a8d} version: 41.4.0 engines: {node: '>=22.0.0'} @@ -9904,8 +10018,8 @@ packages: resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} engines: {node: '>= 0.8'} - memfs@4.56.10: - resolution: {integrity: sha512-eLvzyrwqLHnLYalJP7YZ3wBe79MXktMdfQbvMrVD80K+NhrIukCVBvgP30zTJYEEDh9hZ/ep9z0KOdD7FSHo7w==} + memfs@4.57.2: + resolution: {integrity: sha512-2nWzSsJzrukurSDna4Z0WywuScK4Id3tSKejgu74u8KCdW4uNrseKRSIDg75C6Yw5ZRqBe0F0EtMNlTbUq8bAQ==} peerDependencies: tslib: '2' @@ -10303,6 +10417,18 @@ packages: '@swc/core': optional: true + nx@22.7.0: + resolution: {integrity: sha512-zjySvwwAfexdKN8utZlq5IkoRZOki/gB1KTrY7OkMjSehuiimD6A7DTySiOUkPPMDwgqxMr+eOjIIbC1uWbp8Q==} + hasBin: true + peerDependencies: + '@swc-node/register': ^1.11.1 + '@swc/core': ^1.15.8 + peerDependenciesMeta: + '@swc-node/register': + optional: true + '@swc/core': + optional: true + object-assign@4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} @@ -10486,14 +10612,6 @@ packages: resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} engines: {node: '>= 0.8'} - partysocket@1.1.18: - resolution: {integrity: sha512-SyuvH9VavWOSa14v6dYdp3yfSUDII4BQB1+TkGOFBkjfZKjnDBiba4fhdhwBlqGBkqw4ea3gTA1DYhSffX24Wg==} - peerDependencies: - react: '>=17' - peerDependenciesMeta: - react: - optional: true - pascal-case@3.1.2: resolution: {integrity: sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==} @@ -11643,8 +11761,8 @@ packages: robust-predicates@3.0.3: resolution: {integrity: sha512-NS3levdsRIUOmiJ8FZWCP7LG3QpJyrs/TE0Zpf1yvZu8cAJJ6QMW92H1c7kWpdIHo8RvmLxN/o2JXTKHp74lUA==} - rolldown@1.0.0-rc.16: - resolution: {integrity: sha512-rzi5WqKzEZw3SooTt7cgm4eqIoujPIyGcJNGFL7iPEuajQw7vxMHUkXylu4/vhCkJGXsgRmxqMKXUpT6FEgl0g==} + rolldown@1.0.0-rc.17: + resolution: {integrity: sha512-ZrT53oAKrtA4+YtBWPQbtPOxIbVDbxT0orcYERKd63VJTF13zPcgXTvD4843L8pcsI7M6MErt8QtON6lrB9tyA==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true @@ -12025,8 +12143,8 @@ packages: resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} engines: {node: '>= 0.4'} - storybook-addon-vis@4.2.1: - resolution: {integrity: sha512-ayHdkg0/NyQ0RqznkD6vpYpjRBMZCWTmRaocTCWMS4mzaTFeCyA6c62U5HO1T9UYoW4ApTsUbDk0dPYHJDhrnA==} + storybook-addon-vis@4.2.2: + resolution: {integrity: sha512-ipe1ws+0PXmCZBJ7JgZEOTpNkpevHRs8a/dwXv+fS4Cd3bu+6xyPgV4F2kvdRhUGsatLu1aJbgI0jdH8/aY+jA==} peerDependencies: '@storybook/addon-vitest': ^10.1.10 '@vitest/browser': ^4 @@ -12412,6 +12530,10 @@ packages: tmp-promise@3.0.3: resolution: {integrity: sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ==} + tmp@0.2.4: + resolution: {integrity: sha512-UdiSoX6ypifLmrfQ/XfiawN6hkjSBpCjhKxxZcWlUUmoXLaCKQU0bx4HF/tdDK2uzRuchf1txGvrWBzYREssoQ==} + engines: {node: '>=14.14'} + tmp@0.2.5: resolution: {integrity: sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==} engines: {node: '>=14.14'} @@ -12894,8 +13016,8 @@ packages: terser: optional: true - vite@8.0.9: - resolution: {integrity: sha512-t7g7GVRpMXjNpa67HaVWI/8BWtdVIQPCL2WoozXXA7LBGEFK4AkkKkHx2hAQf5x1GZSlcmEDPkVLSGahxnEEZw==} + vite@8.0.10: + resolution: {integrity: sha512-rZuUu9j6J5uotLDs+cAA4O5H4K1SfPliUlQwqa6YEwSrWDZzP4rhm00oJR5snMewjxF5V/K3D4kctsUTsIU9Mw==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -13338,6 +13460,11 @@ packages: resolution: {integrity: sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==} engines: {node: '>= 6'} + yaml@2.8.0: + resolution: {integrity: sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==} + engines: {node: '>= 14.6'} + hasBin: true + yaml@2.8.3: resolution: {integrity: sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==} engines: {node: '>= 14.6'} @@ -14950,20 +15077,22 @@ snapshots: '@emnapi/wasi-threads': 1.2.1 tslib: 2.8.1 - '@emnapi/core@1.9.2': + '@emnapi/core@1.4.5': dependencies: - '@emnapi/wasi-threads': 1.2.1 + '@emnapi/wasi-threads': 1.0.4 tslib: 2.8.1 - optional: true '@emnapi/runtime@1.10.0': dependencies: tslib: 2.8.1 - '@emnapi/runtime@1.9.2': + '@emnapi/runtime@1.4.5': + dependencies: + tslib: 2.8.1 + + '@emnapi/wasi-threads@1.0.4': dependencies: tslib: 2.8.1 - optional: true '@emnapi/wasi-threads@1.2.1': dependencies: @@ -15089,7 +15218,7 @@ snapshots: '@fetch-mock/vitest@0.2.18(vitest@4.1.5)': dependencies: fetch-mock: 12.6.0 - vitest: 4.1.5(@opentelemetry/api@1.9.1)(@types/node@18.19.130)(@vitest/browser-playwright@4.1.5)(@vitest/coverage-v8@4.1.5)(jsdom@26.1.0(patch_hash=040623e87b1c8b676c2a705513c0276c0704dd1b23fc3a1bb77cde8128b64b5f))(vite@8.0.9(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) + vitest: 4.1.5(@opentelemetry/api@1.9.1)(@types/node@18.19.130)(@vitest/browser-playwright@4.1.5)(@vitest/coverage-v8@4.1.5)(jsdom@26.1.0(patch_hash=040623e87b1c8b676c2a705513c0276c0704dd1b23fc3a1bb77cde8128b64b5f))(vite@8.0.10(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) '@figspec/components@2.1.0': {} @@ -15278,6 +15407,8 @@ snapshots: - supports-color - ts-node + '@jest/diff-sequences@30.0.1': {} + '@jest/diff-sequences@30.3.0': {} '@jest/environment-jsdom-abstract@30.3.0(jsdom@26.1.0(patch_hash=040623e87b1c8b676c2a705513c0276c0704dd1b23fc3a1bb77cde8128b64b5f))': @@ -15422,11 +15553,11 @@ snapshots: '@types/yargs': 17.0.35 chalk: 4.1.2 - '@joshwooding/vite-plugin-react-docgen-typescript@0.7.0(typescript@6.0.3)(vite@8.0.9(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))': + '@joshwooding/vite-plugin-react-docgen-typescript@0.7.0(typescript@6.0.3)(vite@8.0.10(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))': dependencies: glob: 13.0.6 react-docgen-typescript: 2.4.0(typescript@6.0.3) - vite: 8.0.9(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) + vite: 8.0.10(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) optionalDependencies: typescript: 6.0.3 @@ -15480,58 +15611,58 @@ snapshots: dependencies: tslib: 2.8.1 - '@jsonjoy.com/fs-core@4.56.10(tslib@2.8.1)': + '@jsonjoy.com/fs-core@4.57.2(tslib@2.8.1)': dependencies: - '@jsonjoy.com/fs-node-builtins': 4.56.10(tslib@2.8.1) - '@jsonjoy.com/fs-node-utils': 4.56.10(tslib@2.8.1) + '@jsonjoy.com/fs-node-builtins': 4.57.2(tslib@2.8.1) + '@jsonjoy.com/fs-node-utils': 4.57.2(tslib@2.8.1) thingies: 2.5.0(tslib@2.8.1) tslib: 2.8.1 - '@jsonjoy.com/fs-fsa@4.56.10(tslib@2.8.1)': + '@jsonjoy.com/fs-fsa@4.57.2(tslib@2.8.1)': dependencies: - '@jsonjoy.com/fs-core': 4.56.10(tslib@2.8.1) - '@jsonjoy.com/fs-node-builtins': 4.56.10(tslib@2.8.1) - '@jsonjoy.com/fs-node-utils': 4.56.10(tslib@2.8.1) + '@jsonjoy.com/fs-core': 4.57.2(tslib@2.8.1) + '@jsonjoy.com/fs-node-builtins': 4.57.2(tslib@2.8.1) + '@jsonjoy.com/fs-node-utils': 4.57.2(tslib@2.8.1) thingies: 2.5.0(tslib@2.8.1) tslib: 2.8.1 - '@jsonjoy.com/fs-node-builtins@4.56.10(tslib@2.8.1)': + '@jsonjoy.com/fs-node-builtins@4.57.2(tslib@2.8.1)': dependencies: tslib: 2.8.1 - '@jsonjoy.com/fs-node-to-fsa@4.56.10(tslib@2.8.1)': + '@jsonjoy.com/fs-node-to-fsa@4.57.2(tslib@2.8.1)': dependencies: - '@jsonjoy.com/fs-fsa': 4.56.10(tslib@2.8.1) - '@jsonjoy.com/fs-node-builtins': 4.56.10(tslib@2.8.1) - '@jsonjoy.com/fs-node-utils': 4.56.10(tslib@2.8.1) + '@jsonjoy.com/fs-fsa': 4.57.2(tslib@2.8.1) + '@jsonjoy.com/fs-node-builtins': 4.57.2(tslib@2.8.1) + '@jsonjoy.com/fs-node-utils': 4.57.2(tslib@2.8.1) tslib: 2.8.1 - '@jsonjoy.com/fs-node-utils@4.56.10(tslib@2.8.1)': + '@jsonjoy.com/fs-node-utils@4.57.2(tslib@2.8.1)': dependencies: - '@jsonjoy.com/fs-node-builtins': 4.56.10(tslib@2.8.1) + '@jsonjoy.com/fs-node-builtins': 4.57.2(tslib@2.8.1) tslib: 2.8.1 - '@jsonjoy.com/fs-node@4.56.10(tslib@2.8.1)': + '@jsonjoy.com/fs-node@4.57.2(tslib@2.8.1)': dependencies: - '@jsonjoy.com/fs-core': 4.56.10(tslib@2.8.1) - '@jsonjoy.com/fs-node-builtins': 4.56.10(tslib@2.8.1) - '@jsonjoy.com/fs-node-utils': 4.56.10(tslib@2.8.1) - '@jsonjoy.com/fs-print': 4.56.10(tslib@2.8.1) - '@jsonjoy.com/fs-snapshot': 4.56.10(tslib@2.8.1) + '@jsonjoy.com/fs-core': 4.57.2(tslib@2.8.1) + '@jsonjoy.com/fs-node-builtins': 4.57.2(tslib@2.8.1) + '@jsonjoy.com/fs-node-utils': 4.57.2(tslib@2.8.1) + '@jsonjoy.com/fs-print': 4.57.2(tslib@2.8.1) + '@jsonjoy.com/fs-snapshot': 4.57.2(tslib@2.8.1) glob-to-regex.js: 1.2.0(tslib@2.8.1) thingies: 2.5.0(tslib@2.8.1) tslib: 2.8.1 - '@jsonjoy.com/fs-print@4.56.10(tslib@2.8.1)': + '@jsonjoy.com/fs-print@4.57.2(tslib@2.8.1)': dependencies: - '@jsonjoy.com/fs-node-utils': 4.56.10(tslib@2.8.1) + '@jsonjoy.com/fs-node-utils': 4.57.2(tslib@2.8.1) tree-dump: 1.1.0(tslib@2.8.1) tslib: 2.8.1 - '@jsonjoy.com/fs-snapshot@4.56.10(tslib@2.8.1)': + '@jsonjoy.com/fs-snapshot@4.57.2(tslib@2.8.1)': dependencies: '@jsonjoy.com/buffers': 17.65.0(tslib@2.8.1) - '@jsonjoy.com/fs-node-utils': 4.56.10(tslib@2.8.1) + '@jsonjoy.com/fs-node-utils': 4.57.2(tslib@2.8.1) '@jsonjoy.com/json-pack': 17.65.0(tslib@2.8.1) '@jsonjoy.com/util': 17.65.0(tslib@2.8.1) tslib: 2.8.1 @@ -15760,13 +15891,6 @@ snapshots: '@tybys/wasm-util': 0.10.1 optional: true - '@napi-rs/wasm-runtime@1.1.4(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)': - dependencies: - '@emnapi/core': 1.9.2 - '@emnapi/runtime': 1.9.2 - '@tybys/wasm-util': 0.10.1 - optional: true - '@nicolo-ribaudo/eslint-scope-5-internals@5.1.1-v1': dependencies: eslint-scope: 5.1.1 @@ -15857,10 +15981,10 @@ snapshots: transitivePeerDependencies: - supports-color - '@nx-tools/ci-context@7.2.1(@nx/devkit@22.6.5(nx@22.6.5))(tslib@2.8.1)': + '@nx-tools/ci-context@7.2.1(@nx/devkit@22.6.5(nx@22.7.0))(tslib@2.8.1)': dependencies: '@actions/github': 8.0.1 - '@nx-tools/core': 7.2.1(@nx/devkit@22.6.5(nx@22.6.5))(tslib@2.8.1) + '@nx-tools/core': 7.2.1(@nx/devkit@22.6.5(nx@22.7.0))(tslib@2.8.1) '@octokit/openapi-types': 22.2.0 properties-file: 3.6.4 std-env: 3.10.0 @@ -15868,11 +15992,11 @@ snapshots: transitivePeerDependencies: - '@nx/devkit' - '@nx-tools/container-metadata@7.2.1(@nx/devkit@22.6.5(nx@22.6.5))(tslib@2.8.1)': + '@nx-tools/container-metadata@7.2.1(@nx/devkit@22.6.5(nx@22.7.0))(tslib@2.8.1)': dependencies: - '@nx-tools/ci-context': 7.2.1(@nx/devkit@22.6.5(nx@22.6.5))(tslib@2.8.1) - '@nx-tools/core': 7.2.1(@nx/devkit@22.6.5(nx@22.6.5))(tslib@2.8.1) - '@nx/devkit': 22.6.5(nx@22.6.5) + '@nx-tools/ci-context': 7.2.1(@nx/devkit@22.6.5(nx@22.7.0))(tslib@2.8.1) + '@nx-tools/core': 7.2.1(@nx/devkit@22.6.5(nx@22.7.0))(tslib@2.8.1) + '@nx/devkit': 22.6.5(nx@22.7.0) '@renovatebot/pep440': 4.2.1 csv-parse: 5.6.0 handlebars: 4.7.9 @@ -15880,26 +16004,26 @@ snapshots: semver: 7.7.4 tslib: 2.8.1 - '@nx-tools/core@7.2.1(@nx/devkit@22.6.5(nx@22.6.5))(tslib@2.8.1)': + '@nx-tools/core@7.2.1(@nx/devkit@22.6.5(nx@22.7.0))(tslib@2.8.1)': dependencies: '@actions/io': 1.1.3 - '@nx/devkit': 22.6.5(nx@22.6.5) + '@nx/devkit': 22.6.5(nx@22.7.0) csv-parse: 5.6.0 std-env: 3.10.0 tinyexec: 1.0.2 tinyrainbow: 3.0.3 tslib: 2.8.1 - '@nx-tools/nx-container@7.2.1(@nx/devkit@22.6.5(nx@22.6.5))(@nx/js@22.6.5(@babel/traverse@7.29.0)(nx@22.6.5))(dotenv@17.4.2)(nx@22.6.5)(tslib@2.8.1)': + '@nx-tools/nx-container@7.2.1(@nx/devkit@22.6.5(nx@22.7.0))(@nx/js@22.6.5(@babel/traverse@7.29.0)(nx@22.7.0))(dotenv@17.4.2)(nx@22.7.0)(tslib@2.8.1)': dependencies: - '@nx-tools/container-metadata': 7.2.1(@nx/devkit@22.6.5(nx@22.6.5))(tslib@2.8.1) - '@nx-tools/core': 7.2.1(@nx/devkit@22.6.5(nx@22.6.5))(tslib@2.8.1) - '@nx/devkit': 22.6.5(nx@22.6.5) - '@nx/js': 22.6.5(@babel/traverse@7.29.0)(nx@22.6.5) + '@nx-tools/container-metadata': 7.2.1(@nx/devkit@22.6.5(nx@22.7.0))(tslib@2.8.1) + '@nx-tools/core': 7.2.1(@nx/devkit@22.6.5(nx@22.7.0))(tslib@2.8.1) + '@nx/devkit': 22.6.5(nx@22.7.0) + '@nx/js': 22.6.5(@babel/traverse@7.29.0)(nx@22.7.0) csv-parse: 5.6.0 dotenv: 17.4.2 handlebars: 4.7.9 - nx: 22.6.5 + nx: 22.7.0 semver: 7.7.4 tmp: 0.2.5 tslib: 2.8.1 @@ -15915,7 +16039,18 @@ snapshots: tslib: 2.8.1 yargs-parser: 21.1.1 - '@nx/js@22.6.5(@babel/traverse@7.29.0)(nx@22.6.5)': + '@nx/devkit@22.6.5(nx@22.7.0)': + dependencies: + '@zkochan/js-yaml': 0.0.7 + ejs: 5.0.1 + enquirer: 2.3.6 + minimatch: 10.2.4 + nx: 22.7.0 + semver: 7.7.4 + tslib: 2.8.1 + yargs-parser: 21.1.1 + + '@nx/js@22.6.5(@babel/traverse@7.29.0)(nx@22.7.0)': dependencies: '@babel/core': 7.29.0 '@babel/plugin-proposal-decorators': 7.29.0(@babel/core@7.29.0) @@ -15924,7 +16059,7 @@ snapshots: '@babel/preset-env': 7.29.2(@babel/core@7.29.0) '@babel/preset-typescript': 7.28.5(@babel/core@7.29.0) '@babel/runtime': 7.29.2 - '@nx/devkit': 22.6.5(nx@22.6.5) + '@nx/devkit': 22.6.5(nx@22.7.0) '@nx/workspace': 22.6.5 '@zkochan/js-yaml': 0.0.7 babel-plugin-const-enum: 1.2.0(@babel/core@7.29.0) @@ -15954,33 +16089,63 @@ snapshots: '@nx/nx-darwin-arm64@22.6.5': optional: true + '@nx/nx-darwin-arm64@22.7.0': + optional: true + '@nx/nx-darwin-x64@22.6.5': optional: true + '@nx/nx-darwin-x64@22.7.0': + optional: true + '@nx/nx-freebsd-x64@22.6.5': optional: true + '@nx/nx-freebsd-x64@22.7.0': + optional: true + '@nx/nx-linux-arm-gnueabihf@22.6.5': optional: true + '@nx/nx-linux-arm-gnueabihf@22.7.0': + optional: true + '@nx/nx-linux-arm64-gnu@22.6.5': optional: true + '@nx/nx-linux-arm64-gnu@22.7.0': + optional: true + '@nx/nx-linux-arm64-musl@22.6.5': optional: true + '@nx/nx-linux-arm64-musl@22.7.0': + optional: true + '@nx/nx-linux-x64-gnu@22.6.5': optional: true + '@nx/nx-linux-x64-gnu@22.7.0': + optional: true + '@nx/nx-linux-x64-musl@22.6.5': optional: true + '@nx/nx-linux-x64-musl@22.7.0': + optional: true + '@nx/nx-win32-arm64-msvc@22.6.5': optional: true + '@nx/nx-win32-arm64-msvc@22.7.0': + optional: true + '@nx/nx-win32-x64-msvc@22.6.5': optional: true + '@nx/nx-win32-x64-msvc@22.7.0': + optional: true + '@nx/workspace@22.6.5': dependencies: '@nx/devkit': 22.6.5(nx@22.6.5) @@ -16447,7 +16612,7 @@ snapshots: '@oxc-project/types@0.121.0': {} - '@oxc-project/types@0.126.0': {} + '@oxc-project/types@0.127.0': {} '@oxc-resolver/binding-android-arm-eabi@11.19.1': optional: true @@ -17008,56 +17173,58 @@ snapshots: '@renovatebot/pep440@4.2.1': {} - '@rolldown/binding-android-arm64@1.0.0-rc.16': + '@repobuddy/test@1.0.1': {} + + '@rolldown/binding-android-arm64@1.0.0-rc.17': optional: true - '@rolldown/binding-darwin-arm64@1.0.0-rc.16': + '@rolldown/binding-darwin-arm64@1.0.0-rc.17': optional: true - '@rolldown/binding-darwin-x64@1.0.0-rc.16': + '@rolldown/binding-darwin-x64@1.0.0-rc.17': optional: true - '@rolldown/binding-freebsd-x64@1.0.0-rc.16': + '@rolldown/binding-freebsd-x64@1.0.0-rc.17': optional: true - '@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.16': + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.17': optional: true - '@rolldown/binding-linux-arm64-gnu@1.0.0-rc.16': + '@rolldown/binding-linux-arm64-gnu@1.0.0-rc.17': optional: true - '@rolldown/binding-linux-arm64-musl@1.0.0-rc.16': + '@rolldown/binding-linux-arm64-musl@1.0.0-rc.17': optional: true - '@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.16': + '@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.17': optional: true - '@rolldown/binding-linux-s390x-gnu@1.0.0-rc.16': + '@rolldown/binding-linux-s390x-gnu@1.0.0-rc.17': optional: true - '@rolldown/binding-linux-x64-gnu@1.0.0-rc.16': + '@rolldown/binding-linux-x64-gnu@1.0.0-rc.17': optional: true - '@rolldown/binding-linux-x64-musl@1.0.0-rc.16': + '@rolldown/binding-linux-x64-musl@1.0.0-rc.17': optional: true - '@rolldown/binding-openharmony-arm64@1.0.0-rc.16': + '@rolldown/binding-openharmony-arm64@1.0.0-rc.17': optional: true - '@rolldown/binding-wasm32-wasi@1.0.0-rc.16': + '@rolldown/binding-wasm32-wasi@1.0.0-rc.17': dependencies: - '@emnapi/core': 1.9.2 - '@emnapi/runtime': 1.9.2 - '@napi-rs/wasm-runtime': 1.1.4(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2) + '@emnapi/core': 1.10.0 + '@emnapi/runtime': 1.10.0 + '@napi-rs/wasm-runtime': 1.1.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) optional: true - '@rolldown/binding-win32-arm64-msvc@1.0.0-rc.16': + '@rolldown/binding-win32-arm64-msvc@1.0.0-rc.17': optional: true - '@rolldown/binding-win32-x64-msvc@1.0.0-rc.16': + '@rolldown/binding-win32-x64-msvc@1.0.0-rc.17': optional: true - '@rolldown/pluginutils@1.0.0-rc.16': {} + '@rolldown/pluginutils@1.0.0-rc.17': {} '@rollup/plugin-inject@5.0.5(rollup@4.60.1(patch_hash=603340e49399c6044e41a3998891667387d5ec1acbd38d4e5862f2ba3ef58de8))': dependencies: @@ -17499,21 +17666,21 @@ snapshots: axe-core: 4.11.3 storybook: 10.3.5(@testing-library/dom@10.4.1)(prettier@3.8.3)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@storybook/addon-designs@11.1.3(@storybook/addon-docs@10.3.5(@types/react@19.2.14)(esbuild@0.27.4)(rollup@4.60.1(patch_hash=603340e49399c6044e41a3998891667387d5ec1acbd38d4e5862f2ba3ef58de8))(storybook@10.3.5(@testing-library/dom@10.4.1)(prettier@3.8.3)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(vite@8.0.9(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(webpack@5.106.2(esbuild@0.27.4)))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(storybook@10.3.5(@testing-library/dom@10.4.1)(prettier@3.8.3)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))': + '@storybook/addon-designs@11.1.3(@storybook/addon-docs@10.3.5(@types/react@19.2.14)(esbuild@0.27.4)(rollup@4.60.1(patch_hash=603340e49399c6044e41a3998891667387d5ec1acbd38d4e5862f2ba3ef58de8))(storybook@10.3.5(@testing-library/dom@10.4.1)(prettier@3.8.3)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(vite@8.0.10(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(webpack@5.106.2(esbuild@0.27.4)))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(storybook@10.3.5(@testing-library/dom@10.4.1)(prettier@3.8.3)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))': dependencies: '@figspec/react': 2.0.1(@types/react@19.2.14)(react@19.2.5) storybook: 10.3.5(@testing-library/dom@10.4.1)(prettier@3.8.3)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) optionalDependencies: - '@storybook/addon-docs': 10.3.5(@types/react@19.2.14)(esbuild@0.27.4)(rollup@4.60.1(patch_hash=603340e49399c6044e41a3998891667387d5ec1acbd38d4e5862f2ba3ef58de8))(storybook@10.3.5(@testing-library/dom@10.4.1)(prettier@3.8.3)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(vite@8.0.9(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(webpack@5.106.2(esbuild@0.27.4)) + '@storybook/addon-docs': 10.3.5(@types/react@19.2.14)(esbuild@0.27.4)(rollup@4.60.1(patch_hash=603340e49399c6044e41a3998891667387d5ec1acbd38d4e5862f2ba3ef58de8))(storybook@10.3.5(@testing-library/dom@10.4.1)(prettier@3.8.3)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(vite@8.0.10(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(webpack@5.106.2(esbuild@0.27.4)) react: 19.2.5 react-dom: 19.2.5(react@19.2.5) transitivePeerDependencies: - '@types/react' - '@storybook/addon-docs@10.3.5(@types/react@19.2.14)(esbuild@0.27.4)(rollup@4.60.1(patch_hash=603340e49399c6044e41a3998891667387d5ec1acbd38d4e5862f2ba3ef58de8))(storybook@10.3.5(@testing-library/dom@10.4.1)(prettier@3.8.3)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(vite@8.0.9(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(webpack@5.106.2(esbuild@0.27.4))': + '@storybook/addon-docs@10.3.5(@types/react@19.2.14)(esbuild@0.27.4)(rollup@4.60.1(patch_hash=603340e49399c6044e41a3998891667387d5ec1acbd38d4e5862f2ba3ef58de8))(storybook@10.3.5(@testing-library/dom@10.4.1)(prettier@3.8.3)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(vite@8.0.10(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(webpack@5.106.2(esbuild@0.27.4))': dependencies: '@mdx-js/react': 3.1.1(@types/react@19.2.14)(react@19.2.5) - '@storybook/csf-plugin': 10.3.5(esbuild@0.27.4)(rollup@4.60.1(patch_hash=603340e49399c6044e41a3998891667387d5ec1acbd38d4e5862f2ba3ef58de8))(storybook@10.3.5(@testing-library/dom@10.4.1)(prettier@3.8.3)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(vite@8.0.9(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(webpack@5.106.2(esbuild@0.27.4)) + '@storybook/csf-plugin': 10.3.5(esbuild@0.27.4)(rollup@4.60.1(patch_hash=603340e49399c6044e41a3998891667387d5ec1acbd38d4e5862f2ba3ef58de8))(storybook@10.3.5(@testing-library/dom@10.4.1)(prettier@3.8.3)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(vite@8.0.10(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(webpack@5.106.2(esbuild@0.27.4)) '@storybook/icons': 2.0.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5) '@storybook/react-dom-shim': 10.3.5(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(storybook@10.3.5(@testing-library/dom@10.4.1)(prettier@3.8.3)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)) react: 19.2.5 @@ -17533,33 +17700,33 @@ snapshots: '@storybook/icons': 2.0.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5) storybook: 10.3.5(@testing-library/dom@10.4.1)(prettier@3.8.3)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) optionalDependencies: - '@vitest/browser': 4.1.5(vite@8.0.9(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vitest@4.1.5) - '@vitest/browser-playwright': 4.1.5(playwright@1.59.1)(vite@8.0.9(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vitest@4.1.5) + '@vitest/browser': 4.1.5(vite@8.0.10(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vitest@4.1.5) + '@vitest/browser-playwright': 4.1.5(playwright@1.59.1)(vite@8.0.10(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vitest@4.1.5) '@vitest/runner': 4.1.5 - vitest: 4.1.5(@opentelemetry/api@1.9.1)(@types/node@18.19.130)(@vitest/browser-playwright@4.1.5)(@vitest/coverage-v8@4.1.5)(jsdom@26.1.0(patch_hash=040623e87b1c8b676c2a705513c0276c0704dd1b23fc3a1bb77cde8128b64b5f))(vite@8.0.9(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) + vitest: 4.1.5(@opentelemetry/api@1.9.1)(@types/node@18.19.130)(@vitest/browser-playwright@4.1.5)(@vitest/coverage-v8@4.1.5)(jsdom@26.1.0(patch_hash=040623e87b1c8b676c2a705513c0276c0704dd1b23fc3a1bb77cde8128b64b5f))(vite@8.0.10(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) transitivePeerDependencies: - react - react-dom - '@storybook/builder-vite@10.3.5(esbuild@0.27.4)(rollup@4.60.1(patch_hash=603340e49399c6044e41a3998891667387d5ec1acbd38d4e5862f2ba3ef58de8))(storybook@10.3.5(@testing-library/dom@10.4.1)(prettier@3.8.3)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(vite@8.0.9(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(webpack@5.106.2(esbuild@0.27.4))': + '@storybook/builder-vite@10.3.5(esbuild@0.27.4)(rollup@4.60.1(patch_hash=603340e49399c6044e41a3998891667387d5ec1acbd38d4e5862f2ba3ef58de8))(storybook@10.3.5(@testing-library/dom@10.4.1)(prettier@3.8.3)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(vite@8.0.10(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(webpack@5.106.2(esbuild@0.27.4))': dependencies: - '@storybook/csf-plugin': 10.3.5(esbuild@0.27.4)(rollup@4.60.1(patch_hash=603340e49399c6044e41a3998891667387d5ec1acbd38d4e5862f2ba3ef58de8))(storybook@10.3.5(@testing-library/dom@10.4.1)(prettier@3.8.3)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(vite@8.0.9(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(webpack@5.106.2(esbuild@0.27.4)) + '@storybook/csf-plugin': 10.3.5(esbuild@0.27.4)(rollup@4.60.1(patch_hash=603340e49399c6044e41a3998891667387d5ec1acbd38d4e5862f2ba3ef58de8))(storybook@10.3.5(@testing-library/dom@10.4.1)(prettier@3.8.3)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(vite@8.0.10(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(webpack@5.106.2(esbuild@0.27.4)) storybook: 10.3.5(@testing-library/dom@10.4.1)(prettier@3.8.3)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) ts-dedent: 2.2.0 - vite: 8.0.9(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) + vite: 8.0.10(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) transitivePeerDependencies: - esbuild - rollup - webpack - '@storybook/csf-plugin@10.3.5(esbuild@0.27.4)(rollup@4.60.1(patch_hash=603340e49399c6044e41a3998891667387d5ec1acbd38d4e5862f2ba3ef58de8))(storybook@10.3.5(@testing-library/dom@10.4.1)(prettier@3.8.3)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(vite@8.0.9(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(webpack@5.106.2(esbuild@0.27.4))': + '@storybook/csf-plugin@10.3.5(esbuild@0.27.4)(rollup@4.60.1(patch_hash=603340e49399c6044e41a3998891667387d5ec1acbd38d4e5862f2ba3ef58de8))(storybook@10.3.5(@testing-library/dom@10.4.1)(prettier@3.8.3)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(vite@8.0.10(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(webpack@5.106.2(esbuild@0.27.4))': dependencies: storybook: 10.3.5(@testing-library/dom@10.4.1)(prettier@3.8.3)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) unplugin: 2.3.11 optionalDependencies: esbuild: 0.27.4 rollup: 4.60.1(patch_hash=603340e49399c6044e41a3998891667387d5ec1acbd38d4e5862f2ba3ef58de8) - vite: 8.0.9(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) + vite: 8.0.10(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) webpack: 5.106.2(esbuild@0.27.4) '@storybook/global@5.0.0': {} @@ -17575,11 +17742,11 @@ snapshots: react-dom: 19.2.5(react@19.2.5) storybook: 10.3.5(@testing-library/dom@10.4.1)(prettier@3.8.3)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@storybook/react-vite@10.3.5(esbuild@0.27.4)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(rollup@4.60.1(patch_hash=603340e49399c6044e41a3998891667387d5ec1acbd38d4e5862f2ba3ef58de8))(storybook@10.3.5(@testing-library/dom@10.4.1)(prettier@3.8.3)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(typescript@6.0.3)(vite@8.0.9(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(webpack@5.106.2(esbuild@0.27.4))': + '@storybook/react-vite@10.3.5(esbuild@0.27.4)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(rollup@4.60.1(patch_hash=603340e49399c6044e41a3998891667387d5ec1acbd38d4e5862f2ba3ef58de8))(storybook@10.3.5(@testing-library/dom@10.4.1)(prettier@3.8.3)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(typescript@6.0.3)(vite@8.0.10(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(webpack@5.106.2(esbuild@0.27.4))': dependencies: - '@joshwooding/vite-plugin-react-docgen-typescript': 0.7.0(typescript@6.0.3)(vite@8.0.9(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) + '@joshwooding/vite-plugin-react-docgen-typescript': 0.7.0(typescript@6.0.3)(vite@8.0.10(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) '@rollup/pluginutils': 5.3.0(rollup@4.60.1(patch_hash=603340e49399c6044e41a3998891667387d5ec1acbd38d4e5862f2ba3ef58de8)) - '@storybook/builder-vite': 10.3.5(esbuild@0.27.4)(rollup@4.60.1(patch_hash=603340e49399c6044e41a3998891667387d5ec1acbd38d4e5862f2ba3ef58de8))(storybook@10.3.5(@testing-library/dom@10.4.1)(prettier@3.8.3)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(vite@8.0.9(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(webpack@5.106.2(esbuild@0.27.4)) + '@storybook/builder-vite': 10.3.5(esbuild@0.27.4)(rollup@4.60.1(patch_hash=603340e49399c6044e41a3998891667387d5ec1acbd38d4e5862f2ba3ef58de8))(storybook@10.3.5(@testing-library/dom@10.4.1)(prettier@3.8.3)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(vite@8.0.10(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(webpack@5.106.2(esbuild@0.27.4)) '@storybook/react': 10.3.5(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(storybook@10.3.5(@testing-library/dom@10.4.1)(prettier@3.8.3)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(typescript@6.0.3) empathic: 2.0.0 magic-string: 0.30.21 @@ -17589,7 +17756,7 @@ snapshots: resolve: 1.22.12 storybook: 10.3.5(@testing-library/dom@10.4.1)(prettier@3.8.3)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) tsconfig-paths: 4.2.0 - vite: 8.0.9(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) + vite: 8.0.10(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) transitivePeerDependencies: - esbuild - rollup @@ -18313,14 +18480,14 @@ snapshots: '@types/node': 18.19.130 optional: true - '@typescript-eslint/eslint-plugin@8.58.2(@typescript-eslint/parser@8.58.2(eslint@8.57.1)(typescript@6.0.3))(eslint@8.57.1)(typescript@6.0.3)': + '@typescript-eslint/eslint-plugin@8.59.0(@typescript-eslint/parser@8.58.2(eslint@8.57.1)(typescript@6.0.3))(eslint@8.57.1)(typescript@6.0.3)': dependencies: '@eslint-community/regexpp': 4.12.2 '@typescript-eslint/parser': 8.58.2(eslint@8.57.1)(typescript@6.0.3) - '@typescript-eslint/scope-manager': 8.58.2 - '@typescript-eslint/type-utils': 8.58.2(eslint@8.57.1)(typescript@6.0.3) - '@typescript-eslint/utils': 8.58.2(eslint@8.57.1)(typescript@6.0.3) - '@typescript-eslint/visitor-keys': 8.58.2 + '@typescript-eslint/scope-manager': 8.59.0 + '@typescript-eslint/type-utils': 8.59.0(eslint@8.57.1)(typescript@6.0.3) + '@typescript-eslint/utils': 8.59.0(eslint@8.57.1)(typescript@6.0.3) + '@typescript-eslint/visitor-keys': 8.59.0 eslint: 8.57.1 ignore: 7.0.5 natural-compare: 1.4.0 @@ -18377,11 +18544,11 @@ snapshots: dependencies: typescript: 6.0.3 - '@typescript-eslint/type-utils@8.58.2(eslint@8.57.1)(typescript@6.0.3)': + '@typescript-eslint/type-utils@8.59.0(eslint@8.57.1)(typescript@6.0.3)': dependencies: - '@typescript-eslint/types': 8.58.2 - '@typescript-eslint/typescript-estree': 8.58.2(typescript@6.0.3) - '@typescript-eslint/utils': 8.58.2(eslint@8.57.1)(typescript@6.0.3) + '@typescript-eslint/types': 8.59.0 + '@typescript-eslint/typescript-estree': 8.59.0(typescript@6.0.3) + '@typescript-eslint/utils': 8.59.0(eslint@8.57.1)(typescript@6.0.3) debug: 4.4.3 eslint: 8.57.1 ts-api-utils: 2.5.0(typescript@6.0.3) @@ -18423,17 +18590,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.58.2(eslint@8.57.1)(typescript@6.0.3)': - dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@8.57.1) - '@typescript-eslint/scope-manager': 8.58.2 - '@typescript-eslint/types': 8.58.2 - '@typescript-eslint/typescript-estree': 8.58.2(typescript@6.0.3) - eslint: 8.57.1 - typescript: 6.0.3 - transitivePeerDependencies: - - supports-color - '@typescript-eslint/utils@8.59.0(eslint@8.57.1)(typescript@6.0.3)': dependencies: '@eslint-community/eslint-utils': 4.9.1(eslint@8.57.1) @@ -18521,12 +18677,12 @@ snapshots: d3-selection: 3.0.0 d3-transition: 3.0.1(d3-selection@3.0.0) - '@vector-im/compound-design-tokens@10.1.0(@types/react@19.2.14)(react@19.2.5)': + '@vector-im/compound-design-tokens@10.1.1(@types/react@19.2.14)(react@19.2.5)': optionalDependencies: '@types/react': 19.2.14 react: 19.2.5 - '@vector-im/compound-web@9.2.1(@fontsource/inconsolata@5.2.8)(@fontsource/inter@5.2.8)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(@vector-im/compound-design-tokens@10.1.0(@types/react@19.2.14)(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + '@vector-im/compound-web@9.2.1(@fontsource/inconsolata@5.2.8)(@fontsource/inter@5.2.8)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(@vector-im/compound-design-tokens@10.1.1(@types/react@19.2.14)(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': dependencies: '@floating-ui/react': 0.27.17(react-dom@19.2.5(react@19.2.5))(react@19.2.5) '@fontsource/inconsolata': 5.2.8 @@ -18537,7 +18693,7 @@ snapshots: '@radix-ui/react-progress': 1.1.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) '@radix-ui/react-separator': 1.1.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) '@radix-ui/react-slot': 1.2.4(@types/react@19.2.14)(react@19.2.5) - '@vector-im/compound-design-tokens': 10.1.0(@types/react@19.2.14)(react@19.2.5) + '@vector-im/compound-design-tokens': 10.1.1(@types/react@19.2.14)(react@19.2.5) classnames: 2.5.1 react: 19.2.5 vaul: 1.1.2(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) @@ -18556,29 +18712,29 @@ snapshots: vite: 5.4.21(@types/node@18.19.130)(lightningcss@1.32.0)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1) vue: 3.5.31(typescript@6.0.3) - '@vitest/browser-playwright@4.1.5(playwright@1.59.1)(vite@8.0.9(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vitest@4.1.5)': + '@vitest/browser-playwright@4.1.5(playwright@1.59.1)(vite@8.0.10(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vitest@4.1.5)': dependencies: - '@vitest/browser': 4.1.5(vite@8.0.9(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vitest@4.1.5) - '@vitest/mocker': 4.1.5(vite@8.0.9(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) + '@vitest/browser': 4.1.5(vite@8.0.10(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vitest@4.1.5) + '@vitest/mocker': 4.1.5(vite@8.0.10(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) playwright: 1.59.1 tinyrainbow: 3.1.0 - vitest: 4.1.5(@opentelemetry/api@1.9.1)(@types/node@18.19.130)(@vitest/browser-playwright@4.1.5)(@vitest/coverage-v8@4.1.5)(jsdom@26.1.0(patch_hash=040623e87b1c8b676c2a705513c0276c0704dd1b23fc3a1bb77cde8128b64b5f))(vite@8.0.9(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) + vitest: 4.1.5(@opentelemetry/api@1.9.1)(@types/node@18.19.130)(@vitest/browser-playwright@4.1.5)(@vitest/coverage-v8@4.1.5)(jsdom@26.1.0(patch_hash=040623e87b1c8b676c2a705513c0276c0704dd1b23fc3a1bb77cde8128b64b5f))(vite@8.0.10(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) transitivePeerDependencies: - bufferutil - msw - utf-8-validate - vite - '@vitest/browser@4.1.5(vite@8.0.9(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vitest@4.1.5)': + '@vitest/browser@4.1.5(vite@8.0.10(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vitest@4.1.5)': dependencies: '@blazediff/core': 1.9.1 - '@vitest/mocker': 4.1.5(vite@8.0.9(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) + '@vitest/mocker': 4.1.5(vite@8.0.10(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) '@vitest/utils': 4.1.5 magic-string: 0.30.21 pngjs: 7.0.0 sirv: 3.0.2 tinyrainbow: 3.1.0 - vitest: 4.1.5(@opentelemetry/api@1.9.1)(@types/node@18.19.130)(@vitest/browser-playwright@4.1.5)(@vitest/coverage-v8@4.1.5)(jsdom@26.1.0(patch_hash=040623e87b1c8b676c2a705513c0276c0704dd1b23fc3a1bb77cde8128b64b5f))(vite@8.0.9(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) + vitest: 4.1.5(@opentelemetry/api@1.9.1)(@types/node@18.19.130)(@vitest/browser-playwright@4.1.5)(@vitest/coverage-v8@4.1.5)(jsdom@26.1.0(patch_hash=040623e87b1c8b676c2a705513c0276c0704dd1b23fc3a1bb77cde8128b64b5f))(vite@8.0.10(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) ws: 8.20.0 transitivePeerDependencies: - bufferutil @@ -18598,9 +18754,9 @@ snapshots: obug: 2.1.1 std-env: 4.1.0 tinyrainbow: 3.1.0 - vitest: 4.1.5(@opentelemetry/api@1.9.1)(@types/node@18.19.130)(@vitest/browser-playwright@4.1.5)(@vitest/coverage-v8@4.1.5)(jsdom@26.1.0(patch_hash=040623e87b1c8b676c2a705513c0276c0704dd1b23fc3a1bb77cde8128b64b5f))(vite@8.0.9(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) + vitest: 4.1.5(@opentelemetry/api@1.9.1)(@types/node@18.19.130)(@vitest/browser-playwright@4.1.5)(@vitest/coverage-v8@4.1.5)(jsdom@26.1.0(patch_hash=040623e87b1c8b676c2a705513c0276c0704dd1b23fc3a1bb77cde8128b64b5f))(vite@8.0.10(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) optionalDependencies: - '@vitest/browser': 4.1.5(vite@8.0.9(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vitest@4.1.5) + '@vitest/browser': 4.1.5(vite@8.0.10(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vitest@4.1.5) '@vitest/expect@3.2.4': dependencies: @@ -18619,13 +18775,13 @@ snapshots: chai: 6.2.2 tinyrainbow: 3.1.0 - '@vitest/mocker@4.1.5(vite@8.0.9(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))': + '@vitest/mocker@4.1.5(vite@8.0.10(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))': dependencies: '@vitest/spy': 4.1.5 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 8.0.9(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) + vite: 8.0.10(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) '@vitest/pretty-format@3.2.4': dependencies: @@ -19391,6 +19547,8 @@ snapshots: balanced-match@1.0.2: {} + balanced-match@4.0.3: {} + balanced-match@4.0.4: {} bare-events@2.8.2: {} @@ -19522,6 +19680,10 @@ snapshots: dependencies: balanced-match: 1.0.2 + brace-expansion@5.0.2: + dependencies: + balanced-match: 4.0.4 + brace-expansion@5.0.5: dependencies: balanced-match: 4.0.4 @@ -20765,6 +20927,10 @@ snapshots: dependencies: dotenv: 16.6.1 + dotenv-expand@12.0.3: + dependencies: + dotenv: 16.6.1 + dotenv@16.4.7: {} dotenv@16.6.1: {} @@ -21159,12 +21325,12 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-plugin-jest@29.15.2(@typescript-eslint/eslint-plugin@8.58.2(@typescript-eslint/parser@8.58.2(eslint@8.57.1)(typescript@6.0.3))(eslint@8.57.1)(typescript@6.0.3))(eslint@8.57.1)(jest@30.3.0(@types/node@18.19.130)(babel-plugin-macros@3.1.0))(typescript@6.0.3): + eslint-plugin-jest@29.15.2(@typescript-eslint/eslint-plugin@8.59.0(@typescript-eslint/parser@8.58.2(eslint@8.57.1)(typescript@6.0.3))(eslint@8.57.1)(typescript@6.0.3))(eslint@8.57.1)(jest@30.3.0(@types/node@18.19.130)(babel-plugin-macros@3.1.0))(typescript@6.0.3): dependencies: '@typescript-eslint/utils': 8.59.0(eslint@8.57.1)(typescript@6.0.3) eslint: 8.57.1 optionalDependencies: - '@typescript-eslint/eslint-plugin': 8.58.2(@typescript-eslint/parser@8.58.2(eslint@8.57.1)(typescript@6.0.3))(eslint@8.57.1)(typescript@6.0.3) + '@typescript-eslint/eslint-plugin': 8.59.0(@typescript-eslint/parser@8.58.2(eslint@8.57.1)(typescript@6.0.3))(eslint@8.57.1)(typescript@6.0.3) jest: 30.3.0(@types/node@18.19.130)(babel-plugin-macros@3.1.0) typescript: 6.0.3 transitivePeerDependencies: @@ -21189,20 +21355,20 @@ snapshots: safe-regex-test: 1.1.0 string.prototype.includes: 2.0.1 - eslint-plugin-matrix-org@3.0.0(756bde0db757d9adac5fccdfadf6b9bb): + eslint-plugin-matrix-org@3.0.0(e6b158d923c3b8a20a719d9049b4bf8c): dependencies: '@babel/core': 7.29.0 '@babel/eslint-parser': 7.28.6(@babel/core@7.29.0)(eslint@8.57.1) '@babel/eslint-plugin': 7.27.1(@babel/eslint-parser@7.28.6(@babel/core@7.29.0)(eslint@8.57.1))(eslint@8.57.1) '@stylistic/eslint-plugin': 5.10.0(eslint@8.57.1) - '@typescript-eslint/eslint-plugin': 8.58.2(@typescript-eslint/parser@8.58.2(eslint@8.57.1)(typescript@6.0.3))(eslint@8.57.1)(typescript@6.0.3) + '@typescript-eslint/eslint-plugin': 8.59.0(@typescript-eslint/parser@8.58.2(eslint@8.57.1)(typescript@6.0.3))(eslint@8.57.1)(typescript@6.0.3) '@typescript-eslint/parser': 8.58.2(eslint@8.57.1)(typescript@6.0.3) eslint: 8.57.1 eslint-config-google: 0.14.0(eslint@8.57.1) eslint-config-prettier: 10.1.8(eslint@8.57.1) eslint-plugin-deprecate: 0.9.0(eslint@8.57.1) eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.58.2(eslint@8.57.1)(typescript@6.0.3))(eslint@8.57.1) - eslint-plugin-jest: 29.15.2(@typescript-eslint/eslint-plugin@8.58.2(@typescript-eslint/parser@8.58.2(eslint@8.57.1)(typescript@6.0.3))(eslint@8.57.1)(typescript@6.0.3))(eslint@8.57.1)(jest@30.3.0(@types/node@18.19.130)(babel-plugin-macros@3.1.0))(typescript@6.0.3) + eslint-plugin-jest: 29.15.2(@typescript-eslint/eslint-plugin@8.59.0(@typescript-eslint/parser@8.58.2(eslint@8.57.1)(typescript@6.0.3))(eslint@8.57.1)(typescript@6.0.3))(eslint@8.57.1)(jest@30.3.0(@types/node@18.19.130)(babel-plugin-macros@3.1.0))(typescript@6.0.3) eslint-plugin-jsx-a11y: 6.10.2(eslint@8.57.1) eslint-plugin-react: 7.37.5(eslint@8.57.1) eslint-plugin-react-hooks: 7.1.1(eslint@8.57.1) @@ -21410,8 +21576,6 @@ snapshots: etag@1.8.1: {} - event-target-polyfill@0.0.4: {} - event-target-shim@5.0.1: {} eventemitter3@4.0.7: {} @@ -22011,6 +22175,10 @@ snapshots: dependencies: hookified: 1.15.1 + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + hasown@2.0.3: dependencies: function-bind: 1.1.2 @@ -22540,10 +22708,6 @@ snapshots: isomorphic-timers-promises@1.0.1: {} - isomorphic-ws@5.0.0(ws@8.20.0): - dependencies: - ws: 8.20.0 - istanbul-lib-coverage@3.2.2: {} istanbul-lib-instrument@6.0.3: @@ -23323,17 +23487,7 @@ snapshots: '@babel/types': 7.29.0 source-map-js: 1.2.1 - mailpit-api@1.9.0(react@19.2.5): - dependencies: - axios: 1.15.2 - isomorphic-ws: 5.0.0(ws@8.20.0) - partysocket: 1.1.18(react@19.2.5) - ws: 8.20.0 - transitivePeerDependencies: - - bufferutil - - debug - - react - - utf-8-validate + mailpit-api@2.0.0: {} make-dir@4.0.0: dependencies: @@ -23424,7 +23578,7 @@ snapshots: matrix-events-sdk@0.0.1: {} - matrix-js-sdk@https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/c09d3ca9e60b32e09111a83c6c7462d5442c6774: + matrix-js-sdk@https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/69985ee350a33ba75f1ad11f96468344f0c92a8d: dependencies: '@babel/runtime': 7.29.2 '@matrix-org/matrix-sdk-crypto-wasm': 18.2.0 @@ -23487,16 +23641,16 @@ snapshots: media-typer@1.1.0: {} - memfs@4.56.10(tslib@2.8.1): + memfs@4.57.2(tslib@2.8.1): dependencies: - '@jsonjoy.com/fs-core': 4.56.10(tslib@2.8.1) - '@jsonjoy.com/fs-fsa': 4.56.10(tslib@2.8.1) - '@jsonjoy.com/fs-node': 4.56.10(tslib@2.8.1) - '@jsonjoy.com/fs-node-builtins': 4.56.10(tslib@2.8.1) - '@jsonjoy.com/fs-node-to-fsa': 4.56.10(tslib@2.8.1) - '@jsonjoy.com/fs-node-utils': 4.56.10(tslib@2.8.1) - '@jsonjoy.com/fs-print': 4.56.10(tslib@2.8.1) - '@jsonjoy.com/fs-snapshot': 4.56.10(tslib@2.8.1) + '@jsonjoy.com/fs-core': 4.57.2(tslib@2.8.1) + '@jsonjoy.com/fs-fsa': 4.57.2(tslib@2.8.1) + '@jsonjoy.com/fs-node': 4.57.2(tslib@2.8.1) + '@jsonjoy.com/fs-node-builtins': 4.57.2(tslib@2.8.1) + '@jsonjoy.com/fs-node-to-fsa': 4.57.2(tslib@2.8.1) + '@jsonjoy.com/fs-node-utils': 4.57.2(tslib@2.8.1) + '@jsonjoy.com/fs-print': 4.57.2(tslib@2.8.1) + '@jsonjoy.com/fs-snapshot': 4.57.2(tslib@2.8.1) '@jsonjoy.com/json-pack': 1.21.0(tslib@2.8.1) '@jsonjoy.com/util': 1.9.0(tslib@2.8.1) glob-to-regex.js: 1.2.0(tslib@2.8.1) @@ -23970,6 +24124,132 @@ snapshots: transitivePeerDependencies: - debug + nx@22.7.0: + dependencies: + '@emnapi/core': 1.4.5 + '@emnapi/runtime': 1.4.5 + '@emnapi/wasi-threads': 1.0.4 + '@jest/diff-sequences': 30.0.1 + '@napi-rs/wasm-runtime': 0.2.4 + '@tybys/wasm-util': 0.9.0 + '@yarnpkg/lockfile': 1.1.0 + '@zkochan/js-yaml': 0.0.7 + ansi-colors: 4.1.3 + ansi-regex: 5.0.1 + ansi-styles: 4.3.0 + argparse: 2.0.1 + asynckit: 0.4.0 + axios: 1.15.0 + balanced-match: 4.0.3 + base64-js: 1.5.1 + bl: 4.1.0 + brace-expansion: 5.0.2 + buffer: 5.7.1 + call-bind-apply-helpers: 1.0.2 + chalk: 4.1.2 + cli-cursor: 3.1.0 + cli-spinners: 2.6.1 + cliui: 8.0.1 + clone: 1.0.4 + color-convert: 2.0.1 + color-name: 1.1.4 + combined-stream: 1.0.8 + defaults: 1.0.4 + define-lazy-prop: 2.0.0 + delayed-stream: 1.0.0 + dotenv: 16.4.7 + dotenv-expand: 12.0.3 + dunder-proto: 1.0.1 + ejs: 5.0.1 + emoji-regex: 8.0.0 + end-of-stream: 1.4.5 + enquirer: 2.3.6 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + es-set-tostringtag: 2.1.0 + escalade: 3.2.0 + escape-string-regexp: 1.0.5 + figures: 3.2.0 + flat: 5.0.2 + follow-redirects: 1.16.0 + form-data: 4.0.5 + fs-constants: 1.0.0 + function-bind: 1.1.2 + get-caller-file: 2.0.5 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + gopd: 1.2.0 + has-flag: 4.0.0 + has-symbols: 1.1.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + ieee754: 1.2.1 + ignore: 7.0.5 + inherits: 2.0.4 + is-docker: 2.2.1 + is-fullwidth-code-point: 3.0.0 + is-interactive: 1.0.0 + is-unicode-supported: 0.1.0 + is-wsl: 2.2.0 + json5: 2.2.3 + jsonc-parser: 3.2.0 + lines-and-columns: 2.0.3 + log-symbols: 4.1.0 + math-intrinsics: 1.1.0 + mime-db: 1.52.0 + mime-types: 2.1.35 + mimic-fn: 2.1.0 + minimatch: 10.2.4 + minimist: 1.2.8 + npm-run-path: 4.0.1 + once: 1.4.0 + onetime: 5.1.2 + open: 8.4.2 + ora: 5.3.0 + path-key: 3.1.1 + picocolors: 1.1.1 + proxy-from-env: 2.1.0 + readable-stream: 3.6.2 + require-directory: 2.1.1 + resolve.exports: 2.0.3 + restore-cursor: 3.1.0 + safe-buffer: 5.2.1 + semver: 7.7.4 + signal-exit: 3.0.7 + smol-toml: 1.6.1 + string-width: 4.2.3 + string_decoder: 1.3.0 + strip-ansi: 6.0.1 + strip-bom: 3.0.0 + supports-color: 7.2.0 + tar-stream: 2.2.0 + tmp: 0.2.4 + tree-kill: 1.2.2 + tsconfig-paths: 4.2.0 + tslib: 2.8.1 + util-deprecate: 1.0.2 + wcwidth: 1.0.1 + wrap-ansi: 7.0.0 + wrappy: 1.0.2 + y18n: 5.0.8 + yaml: 2.8.0 + yargs: 17.7.2 + yargs-parser: 21.1.1 + optionalDependencies: + '@nx/nx-darwin-arm64': 22.7.0 + '@nx/nx-darwin-x64': 22.7.0 + '@nx/nx-freebsd-x64': 22.7.0 + '@nx/nx-linux-arm-gnueabihf': 22.7.0 + '@nx/nx-linux-arm64-gnu': 22.7.0 + '@nx/nx-linux-arm64-musl': 22.7.0 + '@nx/nx-linux-x64-gnu': 22.7.0 + '@nx/nx-linux-x64-musl': 22.7.0 + '@nx/nx-win32-arm64-msvc': 22.7.0 + '@nx/nx-win32-x64-msvc': 22.7.0 + transitivePeerDependencies: + - debug + object-assign@4.1.1: {} object-inspect@1.13.4: {} @@ -24242,12 +24522,6 @@ snapshots: parseurl@1.3.3: {} - partysocket@1.1.18(react@19.2.5): - dependencies: - event-target-polyfill: 0.0.4 - optionalDependencies: - react: 19.2.5 - pascal-case@3.1.2: dependencies: no-case: 3.0.4 @@ -25472,26 +25746,26 @@ snapshots: robust-predicates@3.0.3: {} - rolldown@1.0.0-rc.16: + rolldown@1.0.0-rc.17: dependencies: - '@oxc-project/types': 0.126.0 - '@rolldown/pluginutils': 1.0.0-rc.16 + '@oxc-project/types': 0.127.0 + '@rolldown/pluginutils': 1.0.0-rc.17 optionalDependencies: - '@rolldown/binding-android-arm64': 1.0.0-rc.16 - '@rolldown/binding-darwin-arm64': 1.0.0-rc.16 - '@rolldown/binding-darwin-x64': 1.0.0-rc.16 - '@rolldown/binding-freebsd-x64': 1.0.0-rc.16 - '@rolldown/binding-linux-arm-gnueabihf': 1.0.0-rc.16 - '@rolldown/binding-linux-arm64-gnu': 1.0.0-rc.16 - '@rolldown/binding-linux-arm64-musl': 1.0.0-rc.16 - '@rolldown/binding-linux-ppc64-gnu': 1.0.0-rc.16 - '@rolldown/binding-linux-s390x-gnu': 1.0.0-rc.16 - '@rolldown/binding-linux-x64-gnu': 1.0.0-rc.16 - '@rolldown/binding-linux-x64-musl': 1.0.0-rc.16 - '@rolldown/binding-openharmony-arm64': 1.0.0-rc.16 - '@rolldown/binding-wasm32-wasi': 1.0.0-rc.16 - '@rolldown/binding-win32-arm64-msvc': 1.0.0-rc.16 - '@rolldown/binding-win32-x64-msvc': 1.0.0-rc.16 + '@rolldown/binding-android-arm64': 1.0.0-rc.17 + '@rolldown/binding-darwin-arm64': 1.0.0-rc.17 + '@rolldown/binding-darwin-x64': 1.0.0-rc.17 + '@rolldown/binding-freebsd-x64': 1.0.0-rc.17 + '@rolldown/binding-linux-arm-gnueabihf': 1.0.0-rc.17 + '@rolldown/binding-linux-arm64-gnu': 1.0.0-rc.17 + '@rolldown/binding-linux-arm64-musl': 1.0.0-rc.17 + '@rolldown/binding-linux-ppc64-gnu': 1.0.0-rc.17 + '@rolldown/binding-linux-s390x-gnu': 1.0.0-rc.17 + '@rolldown/binding-linux-x64-gnu': 1.0.0-rc.17 + '@rolldown/binding-linux-x64-musl': 1.0.0-rc.17 + '@rolldown/binding-openharmony-arm64': 1.0.0-rc.17 + '@rolldown/binding-wasm32-wasi': 1.0.0-rc.17 + '@rolldown/binding-win32-arm64-msvc': 1.0.0-rc.17 + '@rolldown/binding-win32-x64-msvc': 1.0.0-rc.17 rollup-plugin-external-globals@0.13.0(rollup@4.60.1(patch_hash=603340e49399c6044e41a3998891667387d5ec1acbd38d4e5862f2ba3ef58de8)): dependencies: @@ -26001,18 +26275,19 @@ snapshots: es-errors: 1.3.0 internal-slot: 1.1.0 - storybook-addon-vis@4.2.1(@storybook/addon-vitest@10.3.5(@vitest/browser-playwright@4.1.5)(@vitest/browser@4.1.5)(@vitest/runner@4.1.5)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(storybook@10.3.5(@testing-library/dom@10.4.1)(prettier@3.8.3)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(vitest@4.1.5))(@vitest/browser-playwright@4.1.5)(@vitest/browser@4.1.5)(babel-plugin-macros@3.1.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(storybook@10.3.5(@testing-library/dom@10.4.1)(prettier@3.8.3)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(typescript@6.0.3)(vitest@4.1.5): + storybook-addon-vis@4.2.2(@storybook/addon-vitest@10.3.5(@vitest/browser-playwright@4.1.5)(@vitest/browser@4.1.5)(@vitest/runner@4.1.5)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(storybook@10.3.5(@testing-library/dom@10.4.1)(prettier@3.8.3)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(vitest@4.1.5))(@vitest/browser-playwright@4.1.5)(@vitest/browser@4.1.5)(babel-plugin-macros@3.1.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(storybook@10.3.5(@testing-library/dom@10.4.1)(prettier@3.8.3)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(typescript@6.0.3)(vitest@4.1.5): dependencies: + '@repobuddy/test': 1.0.1 '@storybook/addon-vitest': 10.3.5(@vitest/browser-playwright@4.1.5)(@vitest/browser@4.1.5)(@vitest/runner@4.1.5)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(storybook@10.3.5(@testing-library/dom@10.4.1)(prettier@3.8.3)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(vitest@4.1.5) '@storybook/icons': 2.0.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@vitest/browser': 4.1.5(vite@8.0.9(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vitest@4.1.5) + '@vitest/browser': 4.1.5(vite@8.0.10(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vitest@4.1.5) glob: 13.0.6 is-ci: 4.1.0 memoize: 11.0.0 pathe: 2.0.3 storybook: 10.3.5(@testing-library/dom@10.4.1)(prettier@3.8.3)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) type-plus: 8.0.0-beta.8(typescript@6.0.3) - vitest: 4.1.5(@opentelemetry/api@1.9.1)(@types/node@18.19.130)(@vitest/browser-playwright@4.1.5)(@vitest/coverage-v8@4.1.5)(jsdom@26.1.0(patch_hash=040623e87b1c8b676c2a705513c0276c0704dd1b23fc3a1bb77cde8128b64b5f))(vite@8.0.9(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) + vitest: 4.1.5(@opentelemetry/api@1.9.1)(@types/node@18.19.130)(@vitest/browser-playwright@4.1.5)(@vitest/coverage-v8@4.1.5)(jsdom@26.1.0(patch_hash=040623e87b1c8b676c2a705513c0276c0704dd1b23fc3a1bb77cde8128b64b5f))(vite@8.0.10(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) vitest-plugin-vis: 5.1.0(@vitest/browser-playwright@4.1.5)(@vitest/browser@4.1.5)(babel-plugin-macros@3.1.0)(typescript@6.0.3)(vitest@4.1.5) transitivePeerDependencies: - '@vitest/browser-playwright' @@ -26551,6 +26826,8 @@ snapshots: dependencies: tmp: 0.2.5 + tmp@0.2.4: {} + tmp@0.2.5: {} tmpl@1.0.5: {} @@ -26821,7 +27098,7 @@ snapshots: unpipe@1.0.0: {} - unplugin-dts@1.0.0-beta.6(patch_hash=c71b9e0f229ad1d76152030e8eceff26ee0b23c18619c205aa55fee274f89073)(@microsoft/api-extractor@7.58.5(@types/node@18.19.130))(esbuild@0.27.4)(rollup@4.60.1(patch_hash=603340e49399c6044e41a3998891667387d5ec1acbd38d4e5862f2ba3ef58de8))(typescript@6.0.3)(vite@8.0.9(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(webpack@5.106.2(esbuild@0.27.4)): + unplugin-dts@1.0.0-beta.6(patch_hash=c71b9e0f229ad1d76152030e8eceff26ee0b23c18619c205aa55fee274f89073)(@microsoft/api-extractor@7.58.5(@types/node@18.19.130))(esbuild@0.27.4)(rollup@4.60.1(patch_hash=603340e49399c6044e41a3998891667387d5ec1acbd38d4e5862f2ba3ef58de8))(typescript@6.0.3)(vite@8.0.10(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(webpack@5.106.2(esbuild@0.27.4)): dependencies: '@rollup/pluginutils': 5.3.0(rollup@4.60.1(patch_hash=603340e49399c6044e41a3998891667387d5ec1acbd38d4e5862f2ba3ef58de8)) '@volar/typescript': 2.4.28 @@ -26836,7 +27113,7 @@ snapshots: '@microsoft/api-extractor': 7.58.5(@types/node@18.19.130) esbuild: 0.27.4 rollup: 4.60.1(patch_hash=603340e49399c6044e41a3998891667387d5ec1acbd38d4e5862f2ba3ef58de8) - vite: 8.0.9(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) + vite: 8.0.10(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) webpack: 5.106.2(esbuild@0.27.4) transitivePeerDependencies: - supports-color @@ -26983,11 +27260,11 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.3 - vite-plugin-node-polyfills@0.26.0(rollup@4.60.1(patch_hash=603340e49399c6044e41a3998891667387d5ec1acbd38d4e5862f2ba3ef58de8))(vite@8.0.9(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)): + vite-plugin-node-polyfills@0.26.0(rollup@4.60.1(patch_hash=603340e49399c6044e41a3998891667387d5ec1acbd38d4e5862f2ba3ef58de8))(vite@8.0.10(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)): dependencies: '@rollup/plugin-inject': 5.0.5(rollup@4.60.1(patch_hash=603340e49399c6044e41a3998891667387d5ec1acbd38d4e5862f2ba3ef58de8)) node-stdlib-browser: 1.3.1 - vite: 8.0.9(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) + vite: 8.0.10(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) transitivePeerDependencies: - rollup @@ -27003,12 +27280,12 @@ snapshots: sugarss: 5.0.1(postcss@8.5.10) terser: 5.46.1 - vite@8.0.9(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3): + vite@8.0.10(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3): dependencies: lightningcss: 1.32.0 picomatch: 4.0.4 postcss: 8.5.10 - rolldown: 1.0.0-rc.16 + rolldown: 1.0.0-rc.17 tinyglobby: 0.2.16 optionalDependencies: '@types/node': 18.19.130 @@ -27078,7 +27355,7 @@ snapshots: vitest-plugin-vis@5.1.0(@vitest/browser-playwright@4.1.5)(@vitest/browser@4.1.5)(babel-plugin-macros@3.1.0)(typescript@6.0.3)(vitest@4.1.5): dependencies: - '@vitest/browser': 4.1.5(vite@8.0.9(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vitest@4.1.5) + '@vitest/browser': 4.1.5(vite@8.0.10(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vitest@4.1.5) dedent: 1.7.2(babel-plugin-macros@3.1.0) glob: 13.0.6 is-ci: 4.1.0 @@ -27089,21 +27366,21 @@ snapshots: rimraf: 6.1.3 ssim.js: 3.5.0 type-plus: 8.0.0-beta.8(typescript@6.0.3) - vitest: 4.1.5(@opentelemetry/api@1.9.1)(@types/node@18.19.130)(@vitest/browser-playwright@4.1.5)(@vitest/coverage-v8@4.1.5)(jsdom@26.1.0(patch_hash=040623e87b1c8b676c2a705513c0276c0704dd1b23fc3a1bb77cde8128b64b5f))(vite@8.0.9(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) + vitest: 4.1.5(@opentelemetry/api@1.9.1)(@types/node@18.19.130)(@vitest/browser-playwright@4.1.5)(@vitest/coverage-v8@4.1.5)(jsdom@26.1.0(patch_hash=040623e87b1c8b676c2a705513c0276c0704dd1b23fc3a1bb77cde8128b64b5f))(vite@8.0.10(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) optionalDependencies: - '@vitest/browser-playwright': 4.1.5(playwright@1.59.1)(vite@8.0.9(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vitest@4.1.5) + '@vitest/browser-playwright': 4.1.5(playwright@1.59.1)(vite@8.0.10(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vitest@4.1.5) transitivePeerDependencies: - babel-plugin-macros - typescript vitest-sonar-reporter@3.0.0(vitest@4.1.5): dependencies: - vitest: 4.1.5(@opentelemetry/api@1.9.1)(@types/node@18.19.130)(@vitest/browser-playwright@4.1.5)(@vitest/coverage-v8@4.1.5)(jsdom@26.1.0(patch_hash=040623e87b1c8b676c2a705513c0276c0704dd1b23fc3a1bb77cde8128b64b5f))(vite@8.0.9(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) + vitest: 4.1.5(@opentelemetry/api@1.9.1)(@types/node@18.19.130)(@vitest/browser-playwright@4.1.5)(@vitest/coverage-v8@4.1.5)(jsdom@26.1.0(patch_hash=040623e87b1c8b676c2a705513c0276c0704dd1b23fc3a1bb77cde8128b64b5f))(vite@8.0.10(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) - vitest@4.1.5(@opentelemetry/api@1.9.1)(@types/node@18.19.130)(@vitest/browser-playwright@4.1.5)(@vitest/coverage-v8@4.1.5)(jsdom@26.1.0(patch_hash=040623e87b1c8b676c2a705513c0276c0704dd1b23fc3a1bb77cde8128b64b5f))(vite@8.0.9(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)): + vitest@4.1.5(@opentelemetry/api@1.9.1)(@types/node@18.19.130)(@vitest/browser-playwright@4.1.5)(@vitest/coverage-v8@4.1.5)(jsdom@26.1.0(patch_hash=040623e87b1c8b676c2a705513c0276c0704dd1b23fc3a1bb77cde8128b64b5f))(vite@8.0.10(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)): dependencies: '@vitest/expect': 4.1.5 - '@vitest/mocker': 4.1.5(vite@8.0.9(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) + '@vitest/mocker': 4.1.5(vite@8.0.10(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) '@vitest/pretty-format': 4.1.5 '@vitest/runner': 4.1.5 '@vitest/snapshot': 4.1.5 @@ -27120,12 +27397,12 @@ snapshots: tinyexec: 1.1.1 tinyglobby: 0.2.16 tinyrainbow: 3.1.0 - vite: 8.0.9(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) + vite: 8.0.10(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) why-is-node-running: 2.3.0 optionalDependencies: '@opentelemetry/api': 1.9.1 '@types/node': 18.19.130 - '@vitest/browser-playwright': 4.1.5(playwright@1.59.1)(vite@8.0.9(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vitest@4.1.5) + '@vitest/browser-playwright': 4.1.5(playwright@1.59.1)(vite@8.0.10(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vitest@4.1.5) '@vitest/coverage-v8': 4.1.5(@vitest/browser@4.1.5)(vitest@4.1.5) jsdom: 26.1.0(patch_hash=040623e87b1c8b676c2a705513c0276c0704dd1b23fc3a1bb77cde8128b64b5f) transitivePeerDependencies: @@ -27248,7 +27525,7 @@ snapshots: webpack-dev-middleware@7.4.5(tslib@2.8.1)(webpack@5.106.2): dependencies: colorette: 2.0.20 - memfs: 4.56.10(tslib@2.8.1) + memfs: 4.57.2(tslib@2.8.1) mime-types: 3.0.2 on-finished: 2.4.1 range-parser: 1.2.1 @@ -27554,6 +27831,8 @@ snapshots: yaml@1.10.3: {} + yaml@2.8.0: {} + yaml@2.8.3: {} yargs-parser@18.1.3: diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index e25bc040e1..6da463bce7 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -16,12 +16,17 @@ catalog: "@playwright/test": 1.59.1 "playwright-core": 1.59.1 # Compound - "@vector-im/compound-design-tokens": 10.1.0 + "@vector-im/compound-design-tokens": 10.1.1 "@vector-im/compound-web": 9.2.1 # i18n matrix-web-i18n: 3.6.0 # fonts "@fontsource/inter": 5.2.8 + # vite + vite: 8.0.10 + vitest: 4.1.5 + vitest-sonar-reporter: 3.0.0 + "@vitest/coverage-v8": 4.1.5 packageExtensions: fdir: dependencies: diff --git a/sonar-project.properties b/sonar-project.properties index d5e990eb83..7a25cf4571 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -5,8 +5,14 @@ sonar.organization=element-hq #sonar.sourceEncoding=UTF-8 sonar.sources=. -sonar.tests=apps/web/test,apps/web/playwright,apps/desktop/playwright,packages -sonar.test.inclusions=apps/web/test/*,apps/web/playwright/*,apps/desktop/playwright/*,packages/*/src/**/*.test.*,packages/*/src/test/**/* +sonar.tests=apps/web/test,apps/web/playwright,apps/desktop,packages +sonar.test.inclusions=\ + apps/web/test/*,\ + apps/web/playwright/*,\ + apps/desktop/playwright/*,\ + apps/desktop/src/**/*.test.ts,\ + packages/*/src/**/*.test.*,\ + packages/*/src/test/**/* sonar.exclusions=\ apps/web/__mocks__,\ docs,\ @@ -40,16 +46,23 @@ sonar.coverage.exclusions=\ apps/web/playwright/**/*,\ apps/web/res/**/*,\ apps/web/scripts/**/*,\ - apps/desktop/scripts/**/*,\ - apps/desktop/playwright/**/*,\ - apps/desktop/hak/**/*,\ - scripts/**/*,\ + apps/web/__mocks__/**/*,\ + apps/web/I18nWebpackPlugin.ts,\ + apps/web/recorder-worklet-loader.cjs,\ apps/web/src/components/views/dialogs/devtools/**/*,\ apps/web/src/utils/SessionLock.ts,\ apps/web/src/**/*.d.ts,\ apps/web/src/vector/mobile_guide/**/*,\ + apps/desktop/electron-builder.ts,\ + apps/desktop/scripts/**/*,\ + apps/desktop/playwright/**/*,\ + apps/desktop/hak/**/*,\ packages/shared-components/src/test/**/*,\ packages/shared-components/src/**/*.stories.tsx,\ + packages/shared-components/scripts/**/*,\ packages/playwright-common/**/*,\ - **/*.config.ts + scripts/**/*,\ + docs/**/*,\ + **/*.config.*,\ + knip.ts sonar.testExecutionReportPaths=apps/web/coverage/jest-sonar-report.xml