Merge branch 'develop' of github.com:vector-im/element-web into langley/use_list_view_with_room_list

This commit is contained in:
David Langley 2025-08-07 13:37:30 +01:00
commit 27fbf6487b
229 changed files with 2845 additions and 1087 deletions

View File

@ -37,14 +37,14 @@ jobs:
install: true
- name: Login to Docker Hub
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3
uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3
if: github.event_name != 'pull_request'
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GitHub Container Registry
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3
uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3
if: github.event_name != 'pull_request'
with:
registry: ghcr.io
@ -96,7 +96,7 @@ jobs:
- name: Docker meta
id: meta
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5
uses: docker/metadata-action@c1e51972afc2121e065aed6d45c65596fe445f3f # v5
if: github.event_name != 'pull_request'
with:
images: |

View File

@ -70,5 +70,13 @@ module.exports = {
],
},
],
"property-no-deprecated": [
true,
{
ignoreProperties: ["-webkit-box-orient", "word-wrap"],
},
],
"nesting-selector-no-missing-scoping-root": null,
"no-invalid-position-declaration": null,
},
};

View File

@ -1,7 +1,7 @@
# syntax=docker.io/docker/dockerfile:1.17-labs@sha256:9187104f31e3a002a8a6a3209ea1f937fb7486c093cbbde1e14b0fa0d7e4f1b5
# Builder
FROM --platform=$BUILDPLATFORM node:22-bullseye@sha256:a80324457a2c8d09c83ff9edf2bdf71f378d3288de920e68a358bd3c484b8c4a AS builder
FROM --platform=$BUILDPLATFORM node:22-bullseye@sha256:2d63e0f812d023c4c764e83d7e30dc94949304443ebc372d5c445e63a5ae49c1 AS builder
# Support custom branch of the js-sdk. This also helps us build images of element-web develop.
ARG USE_CUSTOM_SDKS=false
@ -19,7 +19,7 @@ RUN /src/scripts/docker-package.sh
RUN cp /src/config.sample.json /src/webapp/config.json
# App
FROM nginxinc/nginx-unprivileged:alpine-slim@sha256:86df552d36eb24c45d3f5becf6423bd056a3fd235d7085fe3d5ea28ba89a8232
FROM nginxinc/nginx-unprivileged:alpine-slim@sha256:e61b77b27c8f3124fad6d19e894ca5b603bcaf6a34a2df035511299dfa6fad35
# Need root user to install packages & manipulate the usr directory
USER root

View File

@ -73,10 +73,10 @@
"test:storybook:update": "playwright-screenshots --entrypoint yarn --with-node-modules && playwright-screenshots --entrypoint /work/node_modules/.bin/test-storybook --with-node-modules --url http://host.docker.internal:6007/ --updateSnapshot"
},
"resolutions": {
"**/pretty-format/react-is": "19.1.0",
"@playwright/test": "1.54.1",
"@types/react": "19.1.8",
"@types/react-dom": "19.1.6",
"**/pretty-format/react-is": "19.1.1",
"@playwright/test": "1.54.2",
"@types/react": "19.1.9",
"@types/react-dom": "19.1.7",
"oidc-client-ts": "3.3.0",
"jwt-decode": "4.0.0",
"caniuse-lite": "1.0.30001724",
@ -94,10 +94,10 @@
"@matrix-org/emojibase-bindings": "^1.3.4",
"@matrix-org/react-sdk-module-api": "^2.4.0",
"@matrix-org/spec": "^1.7.0",
"@sentry/browser": "^9.0.0",
"@sentry/browser": "^10.0.0",
"@types/png-chunks-extract": "^1.0.2",
"@types/react-virtualized": "^9.21.30",
"@vector-im/compound-design-tokens": "^5.0.0",
"@vector-im/compound-design-tokens": "^6.0.0",
"@vector-im/compound-web": "^8.1.2",
"@vector-im/matrix-wysiwyg": "2.39.0",
"@zxcvbn-ts/core": "^3.0.4",
@ -186,7 +186,7 @@
"@babel/preset-typescript": "^7.12.7",
"@babel/runtime": "^7.12.5",
"@casualbot/jest-sonar-reporter": "2.2.7",
"@element-hq/element-call-embedded": "0.13.1",
"@element-hq/element-call-embedded": "0.14.1",
"@element-hq/element-web-playwright-common": "^1.4.4",
"@peculiar/webcrypto": "^1.4.3",
"@playwright/test": "^1.50.1",
@ -223,9 +223,9 @@
"@types/node-fetch": "^2.6.2",
"@types/pako": "^2.0.0",
"@types/qrcode": "^1.3.5",
"@types/react": "19.1.8",
"@types/react": "19.1.9",
"@types/react-beautiful-dnd": "^13.0.0",
"@types/react-dom": "19.1.6",
"@types/react-dom": "19.1.7",
"@types/react-transition-group": "^4.4.0",
"@types/sanitize-html": "2.16.0",
"@types/semver": "^7.5.8",
@ -300,8 +300,8 @@
"semver": "^7.5.2",
"source-map-loader": "^5.0.0",
"storybook": "^9.0.12",
"stylelint": "^16.13.0",
"stylelint-config-standard": "^38.0.0",
"stylelint": "^16.23.0",
"stylelint-config-standard": "^39.0.0",
"stylelint-scss": "^6.0.0",
"stylelint-value-no-unknown-custom-properties": "^6.0.1",
"terser-webpack-plugin": "^5.3.9",

View File

@ -40,7 +40,7 @@ test.describe("Audio player", { tag: ["@no-firefox", "@no-webkit"] }, () => {
// wait for the tile to finish loading
await expect(
page
.locator(".mx_AudioPlayer_mediaName")
.getByTestId("audio-player-name")
.last()
.filter({ hasText: file.split("/").at(-1) }),
).toBeVisible();
@ -55,12 +55,10 @@ test.describe("Audio player", { tag: ["@no-firefox", "@no-webkit"] }, () => {
// Check that the audio player is rendered and its button becomes visible
const checkPlayerVisibility = async (locator: Locator) => {
// Assert that the audio player and media information are visible
const mediaInfo = locator.locator(
".mx_EventTile_mediaLine .mx_MAudioBody .mx_AudioPlayer_container .mx_AudioPlayer_mediaInfo",
);
await expect(mediaInfo.locator(".mx_AudioPlayer_mediaName", { hasText: ".ogg" })).toBeVisible(); // extension
await expect(mediaInfo.locator(".mx_AudioPlayer_byline", { hasText: "00:01" })).toBeVisible();
await expect(mediaInfo.locator(".mx_AudioPlayer_byline", { hasText: "(3.56 KB)" })).toBeVisible(); // actual size
const mediaInfo = locator.getByRole("region", { name: "Audio player" });
await expect(mediaInfo.getByText(".ogg")).toBeVisible(); // extension
await expect(mediaInfo.getByRole("time")).toHaveText("00:01"); // duration
await expect(mediaInfo.getByText("(3.56 KB)")).toBeVisible(); // actual size;
// Assert that the play button can be found and is visible
await expect(locator.getByRole("button", { name: "Play" })).toBeVisible();
@ -79,7 +77,7 @@ test.describe("Audio player", { tag: ["@no-firefox", "@no-webkit"] }, () => {
}
// Check the status of the seek bar
expect(await page.locator(".mx_AudioPlayer_seek input[type='range']").count()).toBeGreaterThan(0);
expect(await page.getByRole("region", { name: "Audio player" }).getByRole("slider").count()).toBeGreaterThan(0);
// Enable IRC layout
await app.settings.setValue("layout", null, SettingLevel.DEVICE, Layout.IRC);
@ -101,7 +99,7 @@ test.describe("Audio player", { tag: ["@no-firefox", "@no-webkit"] }, () => {
display: none !important;
}
`,
mask: [page.locator(".mx_AudioPlayer_seek")],
mask: [page.getByTestId("audio-player-seek")],
};
// Take a snapshot of mx_EventTile_last on IRC layout
@ -187,9 +185,9 @@ test.describe("Audio player", { tag: ["@no-firefox", "@no-webkit"] }, () => {
await uploadFile(page, "playwright/sample-files/1sec.ogg");
// Assert that the audio player is rendered
const container = page.locator(".mx_EventTile_last .mx_AudioPlayer_container");
const container = page.locator(".mx_EventTile_last").getByRole("region", { name: "Audio player" });
// Assert that the counter is zero before clicking the play button
await expect(container.locator(".mx_AudioPlayer_seek [role='timer']", { hasText: "00:00" })).toBeVisible();
await expect(container.getByRole("timer")).toHaveText("00:00");
// Find and click "Play" button, the wait is to make the test less flaky
await expect(container.getByRole("button", { name: "Play" })).toBeVisible();
@ -199,7 +197,7 @@ test.describe("Audio player", { tag: ["@no-firefox", "@no-webkit"] }, () => {
await expect(container.getByRole("button", { name: "Pause" })).toBeVisible();
// Assert that the timer is reset when the audio file finished playing
await expect(container.locator(".mx_AudioPlayer_seek [role='timer']", { hasText: "00:00" })).toBeVisible();
await expect(container.getByRole("timer")).toHaveText("00:00");
// Assert that "Play" button can be found
await expect(container.getByRole("button", { name: "Play" })).toBeVisible();
@ -227,7 +225,7 @@ test.describe("Audio player", { tag: ["@no-firefox", "@no-webkit"] }, () => {
await uploadFile(page, "playwright/sample-files/1sec.ogg");
// Assert the audio player is rendered
await expect(page.locator(".mx_EventTile_last .mx_AudioPlayer_container")).toBeVisible();
await expect(page.getByRole("region", { name: "Audio player" })).toBeVisible();
// Find and click "Reply" button on MessageActionBar
const tile = page.locator(".mx_EventTile_last");
@ -237,7 +235,7 @@ test.describe("Audio player", { tag: ["@no-firefox", "@no-webkit"] }, () => {
await uploadFile(page, "playwright/sample-files/1sec.ogg");
// Assert that the audio player is rendered
await expect(tile.locator(".mx_AudioPlayer_container")).toBeVisible();
await expect(tile.getByRole("region", { name: "Audio player" })).toBeVisible();
// Assert that replied audio file is rendered as file button inside ReplyChain
const button = tile.locator(".mx_ReplyChain_wrapper .mx_MFileBody_info[role='button']");
@ -262,7 +260,9 @@ test.describe("Audio player", { tag: ["@no-firefox", "@no-webkit"] }, () => {
await uploadFile(page, "playwright/sample-files/upload-first.ogg");
// Assert that the audio player is rendered
await expect(page.locator(".mx_EventTile_last .mx_AudioPlayer_container")).toBeVisible();
await expect(
page.locator(".mx_EventTile_last").getByRole("region", { name: "Audio player" }),
).toBeVisible();
await clickButtonReply(tile);
@ -270,7 +270,9 @@ test.describe("Audio player", { tag: ["@no-firefox", "@no-webkit"] }, () => {
await uploadFile(page, "playwright/sample-files/upload-second.ogg");
// Assert that the audio player is rendered
await expect(page.locator(".mx_EventTile_last .mx_AudioPlayer_container")).toBeVisible();
await expect(
page.locator(".mx_EventTile_last").getByRole("region", { name: "Audio player" }),
).toBeVisible();
await clickButtonReply(tile);
@ -278,7 +280,7 @@ test.describe("Audio player", { tag: ["@no-firefox", "@no-webkit"] }, () => {
await uploadFile(page, "playwright/sample-files/upload-third.ogg");
// Assert that the audio player is rendered
await expect(tile.locator(".mx_AudioPlayer_container")).toBeVisible();
await expect(tile.getByRole("region", { name: "Audio player" })).toBeVisible();
// Assert that there are two "mx_ReplyChain" elements
await expect(tile.locator(".mx_ReplyChain")).toHaveCount(2);
@ -314,7 +316,9 @@ test.describe("Audio player", { tag: ["@no-firefox", "@no-webkit"] }, () => {
// On the main timeline
const messageList = page.locator(".mx_RoomView_MessageList");
// Assert the audio player is rendered
await expect(messageList.locator(".mx_EventTile_last .mx_AudioPlayer_container")).toBeVisible();
await expect(
messageList.locator(".mx_EventTile_last").getByRole("region", { name: "Audio player" }),
).toBeVisible();
// Find and click "Reply in thread" button
await messageList.locator(".mx_EventTile_last").hover();
await messageList.locator(".mx_EventTile_last").getByRole("button", { name: "Reply in thread" }).click();
@ -322,10 +326,10 @@ test.describe("Audio player", { tag: ["@no-firefox", "@no-webkit"] }, () => {
// On a thread
const thread = page.locator(".mx_ThreadView");
const threadTile = thread.locator(".mx_EventTile_last");
const audioPlayer = threadTile.locator(".mx_AudioPlayer_container");
const audioPlayer = threadTile.getByRole("region", { name: "Audio player" });
// Assert that the counter is zero before clicking the play button
await expect(audioPlayer.locator(".mx_AudioPlayer_seek [role='timer']", { hasText: "00:00" })).toBeVisible();
await expect(audioPlayer.getByRole("timer")).toHaveText("00:00");
// Find and click "Play" button, the wait is to make the test less flaky
await expect(audioPlayer.getByRole("button", { name: "Play" })).toBeVisible();
@ -335,7 +339,7 @@ test.describe("Audio player", { tag: ["@no-firefox", "@no-webkit"] }, () => {
await expect(audioPlayer.getByRole("button", { name: "Pause" })).toBeVisible();
// Assert that the timer is reset when the audio file finished playing
await expect(audioPlayer.locator(".mx_AudioPlayer_seek [role='timer']", { hasText: "00:00" })).toBeVisible();
await expect(audioPlayer.getByRole("timer")).toHaveText("00:00");
// Assert that "Play" button can be found
await expect(audioPlayer.getByRole("button", { name: "Play" })).not.toBeDisabled();

View File

@ -28,7 +28,7 @@ test.describe("Composer", () => {
test.describe("CIDER", () => {
test("sends a message when you click send or press Enter", async ({ page }) => {
const composer = page.getByRole("textbox", { name: "Send a message…" });
const composer = page.getByRole("textbox", { name: "Send an unencrypted message…" });
// Type a message
await composer.pressSequentially("my message 0");
@ -52,7 +52,7 @@ test.describe("Composer", () => {
});
test("can write formatted text", async ({ page }) => {
const composer = page.getByRole("textbox", { name: "Send a message…" });
const composer = page.getByRole("textbox", { name: "Send an unencrypted message…" });
await composer.pressSequentially("my bold");
await composer.press(`${CtrlOrMeta}+KeyB`);
@ -68,7 +68,7 @@ test.describe("Composer", () => {
await page.getByTestId("mx_EmojiPicker").locator(".mx_EmojiPicker_item", { hasText: "😇" }).click();
await page.locator(".mx_ContextualMenu_background").click(); // Close emoji picker
await page.getByRole("textbox", { name: "Send a message…" }).press("Enter"); // Send message
await page.getByRole("textbox", { name: "Send an unencrypted message…" }).press("Enter"); // Send message
await expect(page.locator(".mx_EventTile_body", { hasText: "😇" })).toBeVisible();
});
@ -79,7 +79,7 @@ test.describe("Composer", () => {
});
test("only sends when you press Control+Enter", async ({ page }) => {
const composer = page.getByRole("textbox", { name: "Send a message…" });
const composer = page.getByRole("textbox", { name: "Send an unencrypted message…" });
// Type a message and press Enter
await composer.pressSequentially("my message 3");
await composer.press("Enter");

View File

@ -91,10 +91,10 @@ test.describe("Key backup reset from elsewhere", () => {
await csAPI.deleteBackupVersion(backupInfo.version);
await page.getByRole("textbox", { name: "Send an encrypted message…" }).fill("/discardsession");
await page.getByRole("textbox", { name: "Send a message…" }).fill("/discardsession");
await page.getByRole("button", { name: "Send message" }).click();
await page.getByRole("textbox", { name: "Send an encrypted message…" }).fill("Message with broken key backup");
await page.getByRole("textbox", { name: "Send a message…" }).fill("Message with broken key backup");
await page.getByRole("button", { name: "Send message" }).click();
// Should be the message we sent plus the room creation event

View File

@ -154,8 +154,8 @@ test.describe("Cryptography", function () {
await app.client.bootstrapCrossSigning(aliceCredentials);
await startDMWithBob(page, bob);
// send first message
await page.getByRole("textbox", { name: "Send a message…" }).fill("Hey!");
await page.getByRole("textbox", { name: "Send a message…" }).press("Enter");
await page.getByRole("textbox", { name: "Send an unencrypted message…" }).fill("Hey!");
await page.getByRole("textbox", { name: "Send an unencrypted message…" }).press("Enter");
await checkDMRoom(page);
const bobRoomId = await bobJoin(page, bob);
await expect(page.locator(".mx_MessageComposer_e2eIcon")).toMatchScreenshot("composer-e2e-icon-normal.png");

View File

@ -209,7 +209,7 @@ test.describe("Device verification", { tag: "@no-webkit" }, () => {
const dialog = page.locator(".mx_Dialog");
// We use `pressSequentially` here to make sure that the FocusLock isn't causing us any problems
// (cf https://github.com/element-hq/element-web/issues/30089)
await dialog.locator("textarea").pressSequentially(recoveryKey);
await dialog.getByTitle("Recovery key").pressSequentially(recoveryKey);
await dialog.getByRole("button", { name: "Continue", disabled: false }).click();
await page.getByRole("button", { name: "Done" }).click();

View File

@ -228,7 +228,7 @@ export async function logIntoElement(page: Page, credentials: Credentials, secur
await useSecurityKey.click();
}
// Fill in the recovery key
await page.locator(".mx_Dialog").locator("textarea").fill(securityKey);
await page.locator(".mx_Dialog").getByTitle("Recovery key").fill(securityKey);
await page.getByRole("button", { name: "Continue", disabled: false }).click();
await page.getByRole("button", { name: "Done" }).click();
}
@ -263,7 +263,7 @@ export async function verifySession(app: ElementAppPage, securityKey: string) {
const settings = await app.settings.openUserSettings("Encryption");
await settings.getByRole("button", { name: "Verify this device" }).click();
await app.page.getByRole("button", { name: "Verify with Recovery Key" }).click();
await app.page.locator(".mx_Dialog").locator("textarea").fill(securityKey);
await app.page.locator(".mx_Dialog").getByTitle("Recovery key").fill(securityKey);
await app.page.getByRole("button", { name: "Continue", disabled: false }).click();
await app.page.getByRole("button", { name: "Done" }).click();
await app.settings.closeDialog();

View File

@ -13,7 +13,7 @@ import { type Locator, type Page } from "@playwright/test";
import { test, expect } from "../../element-web-test";
async function sendMessage(page: Page, message: string): Promise<Locator> {
await page.getByRole("textbox", { name: "Send a message…" }).fill(message);
await page.getByRole("textbox", { name: "Send an unencrypted message…" }).fill(message);
await page.getByRole("button", { name: "Send message" }).click();
const msgTile = page.locator(".mx_EventTile_last");
@ -22,7 +22,7 @@ async function sendMessage(page: Page, message: string): Promise<Locator> {
}
async function sendMultilineMessages(page: Page, messages: string[]) {
await page.getByRole("textbox", { name: "Send a message…" }).focus();
await page.getByRole("textbox", { name: "Send an unencrypted message…" }).focus();
for (let i = 0; i < messages.length; i++) {
await page.keyboard.type(messages[i]);
if (i < messages.length - 1) await page.keyboard.press("Shift+Enter");
@ -40,7 +40,7 @@ async function replyMessage(page: Page, message: Locator, replyMessage: string):
await line.hover();
await line.getByRole("button", { name: "Reply", exact: true }).click();
await page.getByRole("textbox", { name: "Send a reply…" }).fill(replyMessage);
await page.getByRole("textbox", { name: "Send an unencrypted reply…" }).fill(replyMessage);
await page.getByRole("button", { name: "Send message" }).click();
const msgTile = page.locator(".mx_EventTile_last");

View File

@ -29,7 +29,7 @@ test.describe("Pills", () => {
// send a message using the built-in room mention functionality (autocomplete)
await page
.getByRole("textbox", { name: "Send a message…" })
.getByRole("textbox", { name: "Send an unencrypted message…" })
.pressSequentially(`Hello world! Join here: #${targetLocalpart.substring(0, 3)}`);
await page.locator(".mx_Autocomplete_Completion_title").click();
await page.getByRole("button", { name: "Send message" }).click();

View File

@ -63,9 +63,7 @@ test.describe("FilePanel", () => {
await expect(roomViewBody.locator(".mx_EventTile[data-layout='group'] img[alt='riot.png']")).toBeVisible();
// Assert that the audio player is rendered
await expect(
roomViewBody.locator(".mx_EventTile[data-layout='group'] .mx_AudioPlayer_container"),
).toBeVisible();
await expect(roomViewBody.getByRole("region", { name: "Audio player" })).toBeVisible();
// Assert that the file button exists
await expect(
@ -97,9 +95,7 @@ test.describe("FilePanel", () => {
await expect(image.locator("img[alt='riot.png']")).toBeVisible();
// Detect the audio file
const audio = filePanelMessageList.locator(
".mx_EventTile_mediaLine .mx_MAudioBody .mx_AudioPlayer_container",
);
const audio = filePanelMessageList.getByRole("region", { name: "Audio player" });
// Assert that the play button is rendered
await expect(audio.getByRole("button", { name: "Play" })).toBeVisible();
@ -130,7 +126,7 @@ test.describe("FilePanel", () => {
// Take a snapshot of file tiles list on FilePanel
await expect(filePanelMessageList).toMatchScreenshot("file-tiles-list.png", {
// Exclude timestamps & flaky seek bar from snapshot
mask: [page.locator(".mx_MessageTimestamp, .mx_AudioPlayer_seek")],
mask: [page.locator(".mx_MessageTimestamp"), page.getByTestId("audio-player-seek")],
});
});
@ -138,21 +134,19 @@ test.describe("FilePanel", () => {
// Upload an image file
await uploadFile(page, "playwright/sample-files/1sec.ogg");
const audioBody = page.locator(
".mx_FilePanel .mx_RoomView_MessageList .mx_EventTile_mediaLine .mx_MAudioBody .mx_AudioPlayer_container",
);
const audioBody = page.getByTestId("right-panel").getByRole("region", { name: "Audio player" });
// Assert that the audio player is rendered
// Assert that the audio file information is rendered
const mediaInfo = audioBody.locator(".mx_AudioPlayer_mediaInfo");
await expect(mediaInfo.locator(".mx_AudioPlayer_mediaName").getByText("1sec.ogg")).toBeVisible();
await expect(mediaInfo.locator(".mx_AudioPlayer_byline", { hasText: "00:01" })).toBeVisible();
await expect(mediaInfo.locator(".mx_AudioPlayer_byline", { hasText: "(3.56 KB)" })).toBeVisible(); // actual size
// Assert that the audio file information is rendered;
await expect(audioBody.getByText("1sec.ogg")).toBeVisible(); // extension
await expect(audioBody.getByRole("time")).toHaveText("00:01"); // duration
await expect(audioBody.getByText("(3.56 KB)")).toBeVisible(); // actual size;
// Assert that the duration counter is 00:01 before clicking the play button
await expect(audioBody.locator(".mx_AudioPlayer_mediaInfo time", { hasText: "00:01" })).toBeVisible();
await expect(audioBody.getByRole("time")).toHaveText("00:01");
// Assert that the counter is zero before clicking the play button
await expect(audioBody.locator(".mx_AudioPlayer_seek [role='timer']", { hasText: "00:00" })).toBeVisible();
await expect(audioBody.getByRole("timer")).toHaveText("00:00");
// Click the play button
await audioBody.getByRole("button", { name: "Play" }).click();
@ -161,7 +155,7 @@ test.describe("FilePanel", () => {
await expect(audioBody.getByRole("button", { name: "Pause" })).toBeVisible();
// Assert that the timer is reset when the audio file finished playing
await expect(audioBody.locator(".mx_AudioPlayer_seek [role='timer']", { hasText: "00:00" })).toBeVisible();
await expect(audioBody.getByRole("timer")).toHaveText("00:00");
// Assert that the play button is rendered
await expect(audioBody.getByRole("button", { name: "Play" })).toBeVisible();

View File

@ -30,7 +30,7 @@ async function startDM(app: ElementAppPage, page: Page, name: string): Promise<v
await result.first().click();
// send first message to start DM
const locator = page.getByRole("textbox", { name: "Send a message…" });
const locator = page.getByRole("textbox", { name: "Send an unencrypted message…" });
await expect(locator).toBeFocused();
await locator.fill("Hey!");
await locator.press("Enter");
@ -260,7 +260,7 @@ test.describe("Spotlight", () => {
// Send first message to actually start DM
await expect(roomHeaderName(page)).toHaveText(bot2.credentials.displayName);
const locator = page.getByRole("textbox", { name: "Send a message…" });
const locator = page.getByRole("textbox", { name: "Send an unencrypted message…" });
await locator.fill("Hey!");
await locator.press("Enter");

View File

@ -43,7 +43,7 @@ test.describe("Threads", () => {
const roomViewLocator = page.locator(".mx_RoomView_body");
// User sends message
const textbox = roomViewLocator.getByRole("textbox", { name: "Send a message…" });
const textbox = roomViewLocator.getByRole("textbox", { name: "Send an unencrypted message…" });
await textbox.fill("Hello Mr. Bot");
await textbox.press("Enter");
@ -108,7 +108,7 @@ test.describe("Threads", () => {
await app.settings.setValue("layout", null, SettingLevel.DEVICE, Layout.Group);
// User responds in thread
locator = page.locator(".mx_ThreadView").getByRole("textbox", { name: "Send a message…" });
locator = page.locator(".mx_ThreadView").getByRole("textbox", { name: "Send an unencrypted message…" });
await locator.fill("Test");
await locator.press("Enter");
@ -262,7 +262,7 @@ test.describe("Threads", () => {
await locator.locator(".mx_EventTile_line").click();
// User responds & asserts
locator = page.locator(".mx_ThreadView").getByRole("textbox", { name: "Send a message…" });
locator = page.locator(".mx_ThreadView").getByRole("textbox", { name: "Send an unencrypted message…" });
await locator.fill("Great!");
await locator.press("Enter");
@ -335,8 +335,8 @@ test.describe("Threads", () => {
// Send message
const locator = page.locator(".mx_RoomView_body");
await locator.getByRole("textbox", { name: "Send a message…" }).fill("Hello Mr. Bot");
await locator.getByRole("textbox", { name: "Send a message…" }).press("Enter");
await locator.getByRole("textbox", { name: "Send an unencrypted message…" }).fill("Hello Mr. Bot");
await locator.getByRole("textbox", { name: "Send an unencrypted message…" }).press("Enter");
// Create thread
const locator2 = locator.locator(".mx_EventTile[data-scroll-tokens]").filter({ hasText: "Hello Mr. Bot" });
await locator2.hover();
@ -366,7 +366,7 @@ test.describe("Threads", () => {
let locator = page.locator(".mx_RoomView_body");
// User sends message
let textbox = locator.getByRole("textbox", { name: "Send a message…" });
let textbox = locator.getByRole("textbox", { name: "Send an unencrypted message…" });
await textbox.fill("Hello Mr. Bot");
await textbox.press("Enter");
// Wait for message to send, get its ID and save as @threadId
@ -395,7 +395,7 @@ test.describe("Threads", () => {
locator = page.locator(".mx_ThreadView");
await locator.locator(".mx_EventTile_last").hover();
await locator.locator(".mx_EventTile_last").getByRole("button", { name: "Reply" }).click();
textbox = locator.getByRole("textbox", { name: "Reply to thread…" });
textbox = locator.getByRole("textbox", { name: "Reply to unencrypted thread…" });
await textbox.fill("Please come here");
await textbox.press("Enter");
// Wait until the reply is sent
@ -414,7 +414,7 @@ test.describe("Threads", () => {
// Send message
let locator = page.locator(".mx_RoomView_body");
let textbox = locator.getByRole("textbox", { name: "Send a message…" });
let textbox = locator.getByRole("textbox", { name: "Send an unencrypted message…" });
await textbox.fill("Hello Mr. Bot");
await textbox.press("Enter");
// Create thread
@ -425,7 +425,7 @@ test.describe("Threads", () => {
// Send message to thread
locator = page.locator(".mx_ThreadPanel");
textbox = locator.getByRole("textbox", { name: "Send a message…" });
textbox = locator.getByRole("textbox", { name: "Send an unencrypted message…" });
await textbox.fill("Hello Mr. User");
await textbox.press("Enter");
await expect(locator.locator(".mx_EventTile_last").getByText("Hello Mr. User")).toBeAttached();
@ -456,7 +456,7 @@ test.describe("Threads", () => {
*/
const sendMessage = async (message: string) => {
const messageComposer = page.getByRole("region", { name: "Message composer" });
const textbox = messageComposer.getByRole("textbox", { name: "Send a message…" });
const textbox = messageComposer.getByRole("textbox", { name: "Send an unencrypted message…" });
await textbox.fill(message);
await textbox.press("Enter");
};
@ -478,7 +478,7 @@ test.describe("Threads", () => {
// Send a message in the thread
const threadPanel = page.locator(".mx_ThreadPanel");
const textbox = threadPanel.getByRole("textbox", { name: "Send a message…" });
const textbox = threadPanel.getByRole("textbox", { name: "Send an unencrypted message…" });
await textbox.fill(threadMessage);
await textbox.press("Enter");
await expect(threadPanel.locator(".mx_EventTile_last").getByText(threadMessage)).toBeVisible();

View File

@ -461,11 +461,11 @@ test.describe("Timeline", () => {
// Send a emote
await page
.locator(".mx_RoomView_body")
.getByRole("textbox", { name: "Send a message…" })
.getByRole("textbox", { name: "Send an unencrypted message…" })
.fill("/me says hello to Mr. Bot");
await page
.locator(".mx_RoomView_body")
.getByRole("textbox", { name: "Send a message…" })
.getByRole("textbox", { name: "Send an unencrypted message…" })
.press("Enter");
// Check inline start margin of its avatar
// Here --right-padding is for the avatar on the message line

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.0 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.6 KiB

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.1 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.7 KiB

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.9 KiB

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.7 KiB

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.9 KiB

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.0 KiB

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.6 KiB

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 90 KiB

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 103 KiB

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.0 KiB

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 KiB

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 65 KiB

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.4 KiB

After

Width:  |  Height:  |  Size: 6.3 KiB

View File

@ -7,7 +7,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";
const TAG = "develop@sha256:2f6fff14ff23f356705abdbf2ed62c3dd6ca2103cef4ae813714ddc199bbd76a";
const TAG = "develop@sha256:a0d8db97e39321166959cecdc4c684daff36fefdc7968de47be7e0fdb1cb4541";
/**
* SynapseContainer which freezes the docker digest to stabilise tests,

View File

@ -95,7 +95,6 @@
@import "./structures/auth/_Registration.pcss";
@import "./structures/auth/_SessionLockStolenView.pcss";
@import "./structures/auth/_SetupEncryptionBody.pcss";
@import "./views/audio_messages/_AudioPlayer.pcss";
@import "./views/audio_messages/_PlayPauseButton.pcss";
@import "./views/audio_messages/_PlaybackContainer.pcss";
@import "./views/audio_messages/_SeekBar.pcss";

View File

@ -15,7 +15,7 @@ Please see LICENSE files in the repository root for full details.
display: grid;
justify-content: left;
align-items: center;
grid-gap: $spacing-8;
gap: $spacing-8;
grid-template-columns: auto auto auto;
grid-template-rows: auto;
cursor: pointer;

View File

@ -22,7 +22,7 @@ Please see LICENSE files in the repository root for full details.
display: grid;
justify-content: left;
align-items: center;
grid-gap: $spacing-8;
gap: $spacing-8;
grid-template-columns: min-content 1fr min-content;
grid-template-rows: auto;
}
@ -47,7 +47,7 @@ Please see LICENSE files in the repository root for full details.
.mx_PollListItemEnded_answers {
display: grid;
grid-gap: $spacing-8;
gap: $spacing-8;
margin-top: $spacing-12;
}

View File

@ -19,7 +19,7 @@ Please see LICENSE files in the repository root for full details.
.mx_DeviceDetailHeading_renameForm {
display: grid;
grid-gap: $spacing-16;
gap: $spacing-16;
justify-content: left;
grid-template-columns: 100%;
}

View File

@ -23,7 +23,7 @@ Please see LICENSE files in the repository root for full details.
border-bottom: 1px solid $quinary-content;
display: grid;
grid-gap: $spacing-24;
gap: $spacing-24;
justify-content: left;
grid-template-columns: 100%;

View File

@ -35,7 +35,7 @@ Please see LICENSE files in the repository root for full details.
.mx_DeviceTile_actions {
display: grid;
grid-gap: $spacing-8;
gap: $spacing-8;
grid-auto-flow: column;
margin-left: $spacing-8;
}

View File

@ -15,7 +15,7 @@ Please see LICENSE files in the repository root for full details.
.mx_FilteredDeviceList_list {
list-style-type: none;
display: grid;
grid-gap: $spacing-16;
gap: $spacing-16;
margin: 0;
padding: 0 $spacing-16;
}

View File

@ -39,7 +39,7 @@ Please see LICENSE files in the repository root for full details.
.mx_SettingsSubsection_content {
width: 100%;
display: grid;
grid-gap: $spacing-8;
gap: $spacing-8;
/* setting minwidth 0 makes columns definitely sized fixing horizontal overflow */
grid-template-columns: minmax(0, 1fr);
justify-items: flex-start;

View File

@ -1,59 +0,0 @@
/*
Copyright 2024 New Vector Ltd.
Copyright 2021 The Matrix.org Foundation C.I.C.
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.
*/
.mx_MediaBody.mx_AudioPlayer_container {
padding: 16px 12px 12px 12px;
.mx_AudioPlayer_primaryContainer {
display: flex;
.mx_PlayPauseButton {
margin-right: 8px;
}
.mx_AudioPlayer_mediaInfo {
flex: 1;
overflow: hidden; /* makes the ellipsis on the file name work */
& > * {
display: block;
}
.mx_AudioPlayer_mediaName {
color: $primary-content;
font-size: $font-15px;
line-height: $font-15px;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
padding-bottom: 4px; /* mimics the line-height differences in the Figma */
}
.mx_AudioPlayer_byline {
font-size: $font-12px;
line-height: $font-12px;
}
}
}
.mx_AudioPlayer_seek {
display: flex;
align-items: center;
.mx_SeekBar {
flex: 1;
}
.mx_Clock {
min-width: $font-42px; /* for flexbox */
padding-left: $spacing-4; /* isolate from seek bar */
text-align: justify;
white-space: nowrap;
}
}
}

View File

@ -15,6 +15,23 @@ Please see LICENSE files in the repository root for full details.
}
.mx_AccessSecretStorageDialog_primaryContainer {
.mx_AccessSecretStorageDialog_recoveryKeyEntry {
/*
* Be specific here to avoid "margin: 9px" from _common.pcss
*/
:not(.mx_textinput):not(.mx_Field):not(.mx_no_textinput) {
input {
/*
* From figma: https://www.figma.com/design/ZodBLtGnKmRTGJo5SGLnH3/ER-137--Excluding-Insecure-Devices?node-id=102-43729&t=QmewENUd7f6Tmw9U-1
*/
width: 448px;
height: 70px;
margin: 0px;
border: 1px solid;
}
}
}
.mx_AccessSecretStorageDialog_recoveryKeyFeedback {
&::before {
content: "";

View File

@ -60,7 +60,7 @@ Please see LICENSE files in the repository root for full details.
.mx_MPollBody_allOptions {
display: grid;
grid-gap: $spacing-16;
gap: $spacing-16;
margin-bottom: $spacing-8;
max-width: 550px;
}

View File

@ -21,12 +21,12 @@ Please see LICENSE files in the repository root for full details.
flex: 1 1 0;
align-content: flex-start;
display: grid;
grid-gap: $spacing-20;
gap: $spacing-20;
padding-right: $spacing-64;
margin: $spacing-32 0;
&.mx_PollHistoryList_list_ENDED {
grid-gap: $spacing-32;
gap: $spacing-32;
}
}

View File

@ -30,7 +30,7 @@ Please see LICENSE files in the repository root for full details.
.mx_SettingsSubsection_content {
margin-top: 12px;
grid-gap: 12px;
gap: 12px;
justify-items: stretch;
justify-content: stretch;
}
@ -40,7 +40,7 @@ Please see LICENSE files in the repository root for full details.
}
.mx_NotificationSettings2_flags {
grid-gap: 4px;
gap: 4px;
}
.mx_StyledRadioButton_content {

View File

@ -11,7 +11,7 @@ Please see LICENSE files in the repository root for full details.
display: grid;
grid-template-columns: auto repeat(3, 62px);
place-items: center center;
grid-gap: 8px;
gap: 8px;
/* Override StyledRadioButton default styles */
.mx_StyledRadioButton {

View File

@ -34,7 +34,7 @@ Please see LICENSE files in the repository root for full details.
.mx_SettingsSection_subSections {
display: grid;
grid-template-columns: minmax(0, 1fr);
grid-gap: $spacing-32;
gap: $spacing-32;
padding: $spacing-16 0;
}

View File

@ -84,7 +84,7 @@ Please see LICENSE files in the repository root for full details.
.mx_SettingsTab_sections {
display: grid;
grid-template-columns: 1fr;
grid-gap: $spacing-32;
gap: $spacing-32;
padding-bottom: $spacing-16;
}

View File

@ -12,7 +12,7 @@ Please see LICENSE files in the repository root for full details.
padding: 0;
width: 100%;
display: grid;
grid-gap: $spacing-4;
gap: $spacing-4;
}
.mx_KeyboardShortcut_shortcutRow,

View File

@ -6,7 +6,7 @@ sonar.organization=element-hq
sonar.sources=src,res
sonar.tests=test,playwright,src
sonar.test.inclusions=test/*,playwright/*,src/**/*.test.tsx
sonar.test.inclusions=test/*,playwright/*,src/**/*.test.*
sonar.exclusions=__mocks__,docs,element.io,nginx
sonar.cpd.exclusions=src/i18n/strings/*.json

View File

@ -13,6 +13,8 @@ import { type Optional } from "matrix-events-sdk";
import { _t, getUserLanguage } from "./languageHandler";
import { getUserTimezone } from "./TimezoneHandler";
export { formatSeconds } from "./shared-components/utils/DateUtils";
export const MINUTE_MS = 60000;
export const HOUR_MS = MINUTE_MS * 60;
export const DAY_MS = HOUR_MS * 24;
@ -180,31 +182,6 @@ export function formatTime(date: Date, showTwelveHour = false, locale?: string):
}).format(date);
}
export function formatSeconds(inSeconds: number): string {
const isNegative = inSeconds < 0;
inSeconds = Math.abs(inSeconds);
const hours = Math.floor(inSeconds / (60 * 60))
.toFixed(0)
.padStart(2, "0");
const minutes = Math.floor((inSeconds % (60 * 60)) / 60)
.toFixed(0)
.padStart(2, "0");
const seconds = Math.floor((inSeconds % (60 * 60)) % 60)
.toFixed(0)
.padStart(2, "0");
let output = "";
if (hours !== "00") output += `${hours}:`;
output += `${minutes}:${seconds}`;
if (isNegative) {
output = "-" + output;
}
return output;
}
export function formatTimeLeft(inSeconds: number): string {
const hours = Math.floor(inSeconds / (60 * 60)).toFixed(0);
const minutes = Math.floor((inSeconds % (60 * 60)) / 60).toFixed(0);

View File

@ -26,9 +26,9 @@ export interface IInviteResult {
}
/**
* Invites multiple addresses to a room
* Simpler interface to utils/MultiInviter but with
* no option to cancel.
* Invites multiple addresses to a room.
*
* Simpler interface to {@link MultiInviter}.
*
* @param {string} roomId The ID of the room to invite to
* @param {string[]} addresses Array of strings of addresses to invite. May be matrix IDs or 3pids.

View File

@ -36,9 +36,9 @@ import { RoomSettingsTab } from "./components/views/dialogs/RoomSettingsDialog";
import AccessibleButton from "./components/views/elements/AccessibleButton";
import RightPanelStore from "./stores/right-panel/RightPanelStore";
import { highlightEvent, isLocationEvent } from "./utils/EventUtils";
import { ElementCall } from "./models/Call";
import { getSenderName } from "./utils/event/getSenderName";
import PosthogTrackers from "./PosthogTrackers.ts";
import { ElementCallEventType } from "./call-types.ts";
function getRoomMemberDisplayname(client: MatrixClient, event: MatrixEvent, userId = event.getSender()): string {
const roomId = event.getRoomId();
@ -922,7 +922,7 @@ for (const evType of ALL_RULE_TYPES) {
}
// Add both stable and unstable m.call events
for (const evType of ElementCall.CALL_EVENT_TYPE.names) {
for (const evType of ElementCallEventType.names) {
stateHandlers[evType] = textForCallEvent;
}

View File

@ -15,7 +15,7 @@ import { arrayFastResample } from "../utils/arrays";
import { type IDestroyable } from "../utils/IDestroyable";
import { PlaybackClock } from "./PlaybackClock";
import { createAudioContext, decodeOgg } from "./compat";
import { clamp } from "../utils/numbers";
import { clamp } from "../shared-components/utils/numbers";
import { DEFAULT_WAVEFORM, PLAYBACK_WAVEFORM_SAMPLES } from "./consts";
import { PlaybackEncoder } from "../PlaybackEncoder";

View File

@ -7,7 +7,7 @@ Please see LICENSE files in the repository root for full details.
*/
import { type IAmplitudePayload, type ITimingPayload, PayloadEvent, WORKLET_NAME } from "./consts";
import { percentageOf } from "../utils/numbers";
import { percentageOf } from "../shared-components/utils/numbers";
// from AudioWorkletGlobalScope: https://developer.mozilla.org/en-US/docs/Web/API/AudioWorkletGlobalScope
declare const currentTime: number;

View File

@ -19,7 +19,7 @@ import { PayloadEvent, WORKLET_NAME } from "./consts";
import { UPDATE_EVENT } from "../stores/AsyncStore";
import { createAudioContext } from "./compat";
import { FixedRollingArray } from "../utils/FixedRollingArray";
import { clamp } from "../utils/numbers";
import { clamp } from "../shared-components/utils/numbers";
import recorderWorkletFactory from "./recorderWorkletFactory";
const CHANNELS = 1; // stereo isn't important

Some files were not shown because too many files have changed in this diff Show More