Merge branch 'livekit' into valere/default_route

This commit is contained in:
Valere
2026-03-24 18:28:54 +01:00
12 changed files with 123 additions and 138 deletions

View File

@@ -22,8 +22,8 @@ test("Start a new call then leave and show the feedback screen", async ({
await expect(page.getByTestId("lobby_joinCall")).toBeVisible(); await expect(page.getByTestId("lobby_joinCall")).toBeVisible();
// Check the button toolbar // Check the button toolbar
// await expect(page.getByRole('button', { name: 'Mute microphone' })).toBeVisible(); // await expect(page.getByRole('switch', { name: 'Mute microphone' })).toBeVisible();
// await expect(page.getByRole('button', { name: 'Stop video' })).toBeVisible(); // await expect(page.getByRole('switch', { name: 'Stop video' })).toBeVisible();
await expect(page.getByRole("button", { name: "Settings" })).toBeVisible(); await expect(page.getByRole("button", { name: "Settings" })).toBeVisible();
await expect(page.getByRole("button", { name: "End call" })).toBeVisible(); await expect(page.getByRole("button", { name: "End call" })).toBeVisible();

View File

@@ -49,12 +49,12 @@ test("can only interact with header and footer while reconnecting", async ({
).toBeVisible(); ).toBeVisible();
// Tab order should jump directly from header to footer, skipping media tiles // Tab order should jump directly from header to footer, skipping media tiles
await page.getByRole("button", { name: "Mute microphone" }).focus(); await page.getByRole("switch", { name: "Mute microphone" }).focus();
await expect( await expect(
page.getByRole("button", { name: "Mute microphone" }), page.getByRole("switch", { name: "Mute microphone" }),
).toBeFocused(); ).toBeFocused();
await page.keyboard.press("Tab"); await page.keyboard.press("Tab");
await expect(page.getByRole("button", { name: "Stop video" })).toBeFocused(); await expect(page.getByRole("switch", { name: "Stop video" })).toBeFocused();
// Most critically, we should be able to press the hangup button // Most critically, we should be able to press the hangup button
await page.getByRole("button", { name: "End call" }).click(); await page.getByRole("button", { name: "End call" }).click();
}); });

View File

@@ -55,13 +55,10 @@ widgetTest("Create and join a group call", async ({ addUser, browserName }) => {
const frame = user.page const frame = user.page
.locator('iframe[title="Element Call"]') .locator('iframe[title="Element Call"]')
.contentFrame(); .contentFrame();
// No lobby, should start with video on // No lobby, should start with video on
// The only way to know if it is muted or not is to look at the data-kind attribute.. await expect(
const videoButton = frame.getByTestId("incall_videomute"); frame.getByRole("switch", { name: "Stop video", checked: true }),
await expect(videoButton).toBeVisible(); ).toBeVisible();
// video should be on
await expect(videoButton).toHaveAttribute("aria-label", /^Stop video$/);
} }
// We should see 5 video tiles everywhere now // We should see 5 video tiles everywhere now
@@ -101,13 +98,15 @@ widgetTest("Create and join a group call", async ({ addUser, browserName }) => {
const florianFrame = florian.page const florianFrame = florian.page
.locator('iframe[title="Element Call"]') .locator('iframe[title="Element Call"]')
.contentFrame(); .contentFrame();
const florianMuteButton = florianFrame.getByTestId("incall_videomute"); const florianVideoButton = florianFrame.getByRole("switch", {
await florianMuteButton.click(); name: /video/,
});
await expect(florianVideoButton).toHaveAccessibleName("Stop video");
await expect(florianVideoButton).toBeChecked();
await florianVideoButton.click();
// Now the button should indicate we can start video // Now the button should indicate we can start video
await expect(florianMuteButton).toHaveAttribute( await expect(florianVideoButton).toHaveAccessibleName("Start video");
"aria-label", await expect(florianVideoButton).not.toBeChecked();
/^Start video$/,
);
// wait a bit for the state to propagate // wait a bit for the state to propagate
await valere.page.waitForTimeout(3000); await valere.page.waitForTimeout(3000);

View File

@@ -49,14 +49,17 @@ widgetTest("Footer interaction in PiP", async ({ addUser, browserName }) => {
{ {
// Check for a bug where the video had the wrong fit in PIP // Check for a bug where the video had the wrong fit in PIP
const hangupBtn = iFrame.getByRole("button", { name: "End call" }); const audioBtn = iFrame.getByRole("switch", { name: /microphone/ });
const audioBtn = iFrame.getByTestId("incall_mute"); const videoBtn = iFrame.getByRole("switch", { name: /video/ });
const videoBtn = iFrame.getByTestId("incall_videomute"); await expect(
await expect(hangupBtn).toBeVisible(); iFrame.getByRole("button", { name: "End call" }),
).toBeVisible();
await expect(audioBtn).toBeVisible(); await expect(audioBtn).toBeVisible();
await expect(videoBtn).toBeVisible(); await expect(videoBtn).toBeVisible();
await expect(audioBtn).toHaveAttribute("aria-label", /^Mute microphone$/); await expect(audioBtn).toHaveAccessibleName("Mute microphone");
await expect(videoBtn).toHaveAttribute("aria-label", /^Stop video$/); await expect(audioBtn).toBeChecked();
await expect(videoBtn).toHaveAccessibleName("Stop video");
await expect(videoBtn).toBeChecked();
await videoBtn.click(); await videoBtn.click();
await audioBtn.click(); await audioBtn.click();
@@ -64,7 +67,9 @@ widgetTest("Footer interaction in PiP", async ({ addUser, browserName }) => {
// stop hovering on any of the buttons // stop hovering on any of the buttons
await iFrame.getByTestId("videoTile").hover(); await iFrame.getByTestId("videoTile").hover();
await expect(audioBtn).toHaveAttribute("aria-label", /^Unmute microphone$/); await expect(audioBtn).toHaveAccessibleName("Unmute microphone");
await expect(videoBtn).toHaveAttribute("aria-label", /^Start video$/); await expect(audioBtn).toBeChecked();
await expect(videoBtn).toHaveAccessibleName("Start video");
await expect(videoBtn).not.toBeChecked();
} }
}); });

View File

@@ -45,16 +45,14 @@ widgetTest("Put call in PIP", async ({ addUser, browserName }) => {
await TestHelpers.joinCallInCurrentRoom(timo.page); await TestHelpers.joinCallInCurrentRoom(timo.page);
{ const frame = timo.page
const frame = timo.page .locator('iframe[title="Element Call"]')
.locator('iframe[title="Element Call"]') .contentFrame();
.contentFrame();
const videoButton = frame.getByTestId("incall_videomute"); // check that the video is on
await expect(videoButton).toBeVisible(); await expect(
// check that the video is on frame.getByRole("switch", { name: "Stop video", checked: true }),
await expect(videoButton).toHaveAttribute("aria-label", /^Stop video$/); ).toBeVisible();
}
// Switch to the other room, the call should go to PIP // Switch to the other room, the call should go to PIP
await TestHelpers.switchToRoomNamed(valere.page, "DoubleTask"); await TestHelpers.switchToRoomNamed(valere.page, "DoubleTask");

View File

@@ -152,10 +152,10 @@ export class TestHelpers {
} }
} }
public static async maybeDismissBackupChatsToast(page: Page): Promise<void> { private static async maybeDismissKeyBackupToast(page: Page): Promise<void> {
const toast = page const toast = page
.locator(".mx_Toast_toast") .locator(".mx_Toast_toast")
.getByRole("heading", { name: "Back up your chats" }); .getByText("Back up your chats");
try { try {
await expect(toast).toBeVisible({ timeout: 700 }); await expect(toast).toBeVisible({ timeout: 700 });
@@ -183,6 +183,7 @@ export class TestHelpers {
await page.getByRole("button", { name: "Create room" }).click(); await page.getByRole("button", { name: "Create room" }).click();
await expect(page.getByText("You created this room.")).toBeVisible(); await expect(page.getByText("You created this room.")).toBeVisible();
await expect(page.getByText("Encryption enabled")).toBeVisible(); await expect(page.getByText("Encryption enabled")).toBeVisible();
await TestHelpers.maybeDismissKeyBackupToast(page);
// Invite users if any // Invite users if any
if (andInvite.length > 0) { if (andInvite.length > 0) {
@@ -217,6 +218,7 @@ export class TestHelpers {
await expect( await expect(
page.getByRole("main").getByRole("heading", { name: roomName }), page.getByRole("main").getByRole("heading", { name: roomName }),
).toBeVisible(); ).toBeVisible();
await TestHelpers.maybeDismissKeyBackupToast(page);
} }
/** /**

View File

@@ -54,34 +54,36 @@ widgetTest(
.contentFrame(); .contentFrame();
// ASSERT the button states for whistler (the callee) // ASSERT the button states for whistler (the callee)
{ // video should be off by default in a voice call
// The only way to know if it is muted or not is to look at the data-kind attribute.. await expect(
const videoButton = whistlerFrame.getByTestId("incall_videomute"); whistlerFrame.getByRole("switch", {
// video should be off by default in a voice call name: "Start video",
await expect(videoButton).toHaveAttribute("aria-label", /^Start video$/); checked: false,
}),
const audioButton = whistlerFrame.getByTestId("incall_mute"); ).toBeVisible();
// audio should be on for the voice call // audio should be on for the voice call
await expect(audioButton).toHaveAttribute( await expect(
"aria-label", whistlerFrame.getByRole("switch", {
/^Mute microphone$/, name: "Mute microphone",
); checked: true,
} }),
).toBeVisible();
// ASSERT the button states for brools (the caller) // ASSERT the button states for brools (the caller)
{ // video should be off by default in a voice call
// The only way to know if it is muted or not is to look at the data-kind attribute.. await expect(
const videoButton = brooksFrame.getByTestId("incall_videomute"); whistlerFrame.getByRole("switch", {
// video should be off by default in a voice call name: "Start video",
await expect(videoButton).toHaveAttribute("aria-label", /^Start video$/); checked: false,
}),
const audioButton = brooksFrame.getByTestId("incall_mute"); ).toBeVisible();
// audio should be on for the voice call // audio should be on for the voice call
await expect(audioButton).toHaveAttribute( await expect(
"aria-label", whistlerFrame.getByRole("switch", {
/^Mute microphone$/, name: "Mute microphone",
); checked: true,
} }),
).toBeVisible();
// In order to confirm that the call is disconnected we will check that the message composer is shown again. // In order to confirm that the call is disconnected we will check that the message composer is shown again.
// So first we need to confirm that it is hidden when in the call. // So first we need to confirm that it is hidden when in the call.
@@ -93,10 +95,7 @@ widgetTest(
).not.toBeVisible(); ).not.toBeVisible();
// ASSERT hanging up on one side ends the call for both // ASSERT hanging up on one side ends the call for both
{ await brooksFrame.getByRole("button", { name: "End call" }).click();
const hangupButton = brooksFrame.getByTestId("incall_leave");
await hangupButton.click();
}
// The widget should be closed on both sides and the timeline should be back on screen // The widget should be closed on both sides and the timeline should be back on screen
await expect( await expect(
@@ -148,34 +147,30 @@ widgetTest(
.contentFrame(); .contentFrame();
// ASSERT the button states for whistler (the callee) // ASSERT the button states for whistler (the callee)
{ // video should be off by default in a video call
// The only way to know if it is muted or not is to look at the data-kind attribute.. await expect(
const videoButton = whistlerFrame.getByTestId("incall_videomute"); whistlerFrame.getByRole("switch", { name: "Stop video", checked: true }),
// video should be on by default in a voice call ).toBeVisible();
await expect(videoButton).toHaveAttribute("aria-label", /^Stop video$/); // audio should be on too
await expect(
const audioButton = whistlerFrame.getByTestId("incall_mute"); whistlerFrame.getByRole("switch", {
// audio should be on for the voice call name: "Mute microphone",
await expect(audioButton).toHaveAttribute( checked: true,
"aria-label", }),
/^Mute microphone$/, ).toBeVisible();
);
}
// ASSERT the button states for brools (the caller) // ASSERT the button states for brools (the caller)
{ // video should be off by default in a video call
// The only way to know if it is muted or not is to look at the data-kind attribute.. await expect(
const videoButton = brooksFrame.getByTestId("incall_videomute"); whistlerFrame.getByRole("switch", { name: "Stop video", checked: true }),
// video should be on by default in a voice call ).toBeVisible();
await expect(videoButton).toHaveAttribute("aria-label", /^Stop video$/); // audio should be on too
await expect(
const audioButton = brooksFrame.getByTestId("incall_mute"); whistlerFrame.getByRole("switch", {
// audio should be on for the voice call name: "Mute microphone",
await expect(audioButton).toHaveAttribute( checked: true,
"aria-label", }),
/^Mute microphone$/, ).toBeVisible();
);
}
// In order to confirm that the call is disconnected we will check that the message composer is shown again. // In order to confirm that the call is disconnected we will check that the message composer is shown again.
// So first we need to confirm that it is hidden when in the call. // So first we need to confirm that it is hidden when in the call.
@@ -187,10 +182,7 @@ widgetTest(
).not.toBeVisible(); ).not.toBeVisible();
// ASSERT hanging up on one side ends the call for both // ASSERT hanging up on one side ends the call for both
{ await brooksFrame.getByRole("button", { name: "End call" }).click();
const hangupButton = brooksFrame.getByTestId("incall_leave");
await hangupButton.click();
}
// The widget should be closed on both sides and the timeline should be back on screen // The widget should be closed on both sides and the timeline should be back on screen
await expect( await expect(

View File

@@ -14,7 +14,7 @@ exports[`AppBar > renders 1`] = `
> >
<button <button
aria-labelledby="_r_0_" aria-labelledby="_r_0_"
class="_icon-button_1pz9o_8" class="_icon-button_1215g_8"
data-kind="primary" data-kind="primary"
role="button" role="button"
style="--cpd-icon-button-size: 32px;" style="--cpd-icon-button-size: 32px;"

View File

@@ -37,9 +37,10 @@ export const MicButton: FC<MicButtonProps> = ({ enabled, ...props }) => {
<Tooltip label={label}> <Tooltip label={label}>
<CpdButton <CpdButton
iconOnly iconOnly
aria-label={label}
Icon={Icon} Icon={Icon}
kind={enabled ? "primary" : "secondary"} kind={enabled ? "primary" : "secondary"}
role="switch"
aria-checked={enabled}
{...props} {...props}
/> />
</Tooltip> </Tooltip>
@@ -62,9 +63,10 @@ export const VideoButton: FC<VideoButtonProps> = ({ enabled, ...props }) => {
<Tooltip label={label}> <Tooltip label={label}>
<CpdButton <CpdButton
iconOnly iconOnly
aria-label={label}
Icon={Icon} Icon={Icon}
kind={enabled ? "primary" : "secondary"} kind={enabled ? "primary" : "secondary"}
role="switch"
aria-checked={enabled}
{...props} {...props}
/> />
</Tooltip> </Tooltip>
@@ -91,6 +93,8 @@ export const ShareScreenButton: FC<ShareScreenButtonProps> = ({
iconOnly iconOnly
Icon={ShareScreenSolidIcon} Icon={ShareScreenSolidIcon}
kind={enabled ? "primary" : "secondary"} kind={enabled ? "primary" : "secondary"}
role="switch"
aria-checked={enabled}
{...props} {...props}
/> />
</Tooltip> </Tooltip>
@@ -112,7 +116,6 @@ export const EndCallButton: FC<EndCallButtonProps> = ({
<CpdButton <CpdButton
className={classNames(className, styles.endCall)} className={classNames(className, styles.endCall)}
iconOnly iconOnly
aria-label={t("hangup_button_label")}
Icon={EndCallIcon} Icon={EndCallIcon}
destructive destructive
{...props} {...props}

View File

@@ -128,24 +128,11 @@ exports[`InCallView > rendering > renders 1`] = `
width="1em" width="1em"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
> >
<g <path
clip-path="url(#a)" clip-rule="evenodd"
> d="M8.929 15.1a13.6 13.6 0 0 0 4.654 3.066q2.62 1.036 5.492.923h.008l.003-.004.003-.002-.034-3.124-3.52-.483-1.791 1.792-.645-.322a13.5 13.5 0 0 1-3.496-2.52 13.4 13.4 0 0 1-2.52-3.496l-.322-.645 1.792-1.791-.483-3.52-3.123-.033-.003.002-.003.004v.002a13.65 13.65 0 0 0 .932 5.492A13.4 13.4 0 0 0 8.93 15.1m3.92 4.926a15.6 15.6 0 0 1-5.334-3.511 15.4 15.4 0 0 1-3.505-5.346 15.6 15.6 0 0 1-1.069-6.274 1.93 1.93 0 0 1 .589-1.366c.366-.366.84-.589 1.386-.589h.01l3.163.035a1.96 1.96 0 0 1 1.958 1.694v.005l.487 3.545v.003c.043.297.025.605-.076.907a2 2 0 0 1-.485.773l-.762.762a11.3 11.3 0 0 0 1.806 2.348 11.4 11.4 0 0 0 2.348 1.806l.762-.762a2 2 0 0 1 .774-.485c.302-.1.61-.118.907-.076l3.553.487a1.96 1.96 0 0 1 1.694 1.958l.034 3.174c0 .546-.223 1.02-.588 1.386-.36.36-.827.582-1.363.588a15.3 15.3 0 0 1-6.29-1.062"
<path fill-rule="evenodd"
clip-rule="evenodd" />
d="M8.929 15.1a13.6 13.6 0 0 0 4.654 3.066q2.62 1.036 5.492.923h.008l.003-.004.003-.002-.034-3.124-3.52-.483-1.791 1.792-.645-.322a13.5 13.5 0 0 1-3.496-2.52 13.4 13.4 0 0 1-2.52-3.496l-.322-.644 1.792-1.792-.483-3.519-3.123-.034-.003.002-.003.004v.002a13.65 13.65 0 0 0 .932 5.492A13.4 13.4 0 0 0 8.93 15.1m3.92 4.926a15.6 15.6 0 0 1-5.334-3.511 15.4 15.4 0 0 1-3.505-5.346 15.6 15.6 0 0 1-1.069-6.274 1.93 1.93 0 0 1 .589-1.366c.366-.366.84-.589 1.386-.589h.01l3.163.035a1.96 1.96 0 0 1 1.958 1.694v.005l.487 3.545v.003c.043.297.025.605-.076.907a2 2 0 0 1-.485.773l-.762.762a11.4 11.4 0 0 0 3.206 3.54q.457.33.948.614l.762-.761a2 2 0 0 1 .774-.486c.302-.1.61-.118.907-.076l3.553.487a1.96 1.96 0 0 1 1.694 1.958l.034 3.174c0 .546-.223 1.02-.588 1.386-.361.36-.827.582-1.363.588a15.3 15.3 0 0 1-6.29-1.062"
fill-rule="evenodd"
/>
</g>
<defs>
<clippath
id="a"
>
<path
d="M0 0h24v24H0z"
/>
</clippath>
</defs>
</svg> </svg>
</div> </div>
<h2 <h2
@@ -285,14 +272,14 @@ exports[`InCallView > rendering > renders 1`] = `
class="buttons" class="buttons"
> >
<button <button
aria-checked="false"
aria-disabled="true" aria-disabled="true"
aria-label="Unmute microphone"
aria-labelledby="_r_8_" aria-labelledby="_r_8_"
class="_button_13vu4_8 _has-icon_13vu4_60 _icon-only_13vu4_53" class="_button_13vu4_8 _has-icon_13vu4_60 _icon-only_13vu4_53"
data-kind="secondary" data-kind="secondary"
data-size="lg" data-size="lg"
data-testid="incall_mute" data-testid="incall_mute"
role="button" role="switch"
tabindex="0" tabindex="0"
> >
<svg <svg
@@ -309,14 +296,14 @@ exports[`InCallView > rendering > renders 1`] = `
</svg> </svg>
</button> </button>
<button <button
aria-checked="false"
aria-disabled="true" aria-disabled="true"
aria-label="Start video"
aria-labelledby="_r_d_" aria-labelledby="_r_d_"
class="_button_13vu4_8 _has-icon_13vu4_60 _icon-only_13vu4_53" class="_button_13vu4_8 _has-icon_13vu4_60 _icon-only_13vu4_53"
data-kind="secondary" data-kind="secondary"
data-size="lg" data-size="lg"
data-testid="incall_videomute" data-testid="incall_videomute"
role="button" role="switch"
tabindex="0" tabindex="0"
> >
<svg <svg
@@ -354,7 +341,6 @@ exports[`InCallView > rendering > renders 1`] = `
</svg> </svg>
</button> </button>
<button <button
aria-label="End call"
aria-labelledby="_r_n_" aria-labelledby="_r_n_"
class="_button_13vu4_8 endCall _has-icon_13vu4_60 _icon-only_13vu4_53 _destructive_13vu4_110" class="_button_13vu4_8 endCall _has-icon_13vu4_60 _icon-only_13vu4_53 _destructive_13vu4_110"
data-kind="primary" data-kind="primary"

View File

@@ -234,12 +234,12 @@ exports[`DeveloperSettingsTab > renders and matches snapshot 1`] = `
class="_inline-field-control_19upo_44" class="_inline-field-control_19upo_44"
> >
<div <div
class="_container_1qhtc_10" class="_container_1ug7n_10"
> >
<input <input
aria-describedby="radix-_r_9_ radix-_r_b_ radix-_r_d_" aria-describedby="radix-_r_9_ radix-_r_b_ radix-_r_d_"
checked="" checked=""
class="_input_1qhtc_18" class="_input_1ug7n_18"
id="radix-_r_8_" id="radix-_r_8_"
name="_r_0_" name="_r_0_"
title="" title=""
@@ -247,7 +247,7 @@ exports[`DeveloperSettingsTab > renders and matches snapshot 1`] = `
value="legacy" value="legacy"
/> />
<div <div
class="_ui_1qhtc_19" class="_ui_1ug7n_19"
/> />
</div> </div>
</div> </div>
@@ -275,11 +275,11 @@ exports[`DeveloperSettingsTab > renders and matches snapshot 1`] = `
class="_inline-field-control_19upo_44" class="_inline-field-control_19upo_44"
> >
<div <div
class="_container_1qhtc_10" class="_container_1ug7n_10"
> >
<input <input
aria-describedby="radix-_r_9_ radix-_r_b_ radix-_r_d_" aria-describedby="radix-_r_9_ radix-_r_b_ radix-_r_d_"
class="_input_1qhtc_18" class="_input_1ug7n_18"
id="radix-_r_a_" id="radix-_r_a_"
name="_r_0_" name="_r_0_"
title="" title=""
@@ -287,7 +287,7 @@ exports[`DeveloperSettingsTab > renders and matches snapshot 1`] = `
value="compatibility" value="compatibility"
/> />
<div <div
class="_ui_1qhtc_19" class="_ui_1ug7n_19"
/> />
</div> </div>
</div> </div>
@@ -315,11 +315,11 @@ exports[`DeveloperSettingsTab > renders and matches snapshot 1`] = `
class="_inline-field-control_19upo_44" class="_inline-field-control_19upo_44"
> >
<div <div
class="_container_1qhtc_10" class="_container_1ug7n_10"
> >
<input <input
aria-describedby="radix-_r_9_ radix-_r_b_ radix-_r_d_" aria-describedby="radix-_r_9_ radix-_r_b_ radix-_r_d_"
class="_input_1qhtc_18" class="_input_1ug7n_18"
id="radix-_r_c_" id="radix-_r_c_"
name="_r_0_" name="_r_0_"
title="" title=""
@@ -327,7 +327,7 @@ exports[`DeveloperSettingsTab > renders and matches snapshot 1`] = `
value="matrix_2_0" value="matrix_2_0"
/> />
<div <div
class="_ui_1qhtc_19" class="_ui_1ug7n_19"
/> />
</div> </div>
</div> </div>

View File

@@ -6236,8 +6236,8 @@ __metadata:
linkType: hard linkType: hard
"@vector-im/compound-design-tokens@npm:^6.0.0": "@vector-im/compound-design-tokens@npm:^6.0.0":
version: 6.6.0 version: 6.10.2
resolution: "@vector-im/compound-design-tokens@npm:6.6.0" resolution: "@vector-im/compound-design-tokens@npm:6.10.2"
peerDependencies: peerDependencies:
"@types/react": "*" "@types/react": "*"
react: ^17 || ^18 || ^19.0.0 react: ^17 || ^18 || ^19.0.0
@@ -6246,13 +6246,13 @@ __metadata:
optional: true optional: true
react: react:
optional: true optional: true
checksum: 10c0/93b152dd1de96371f9b6b1f7dbcc381d7ab598031dbc900f52d610f015766c0d4426ae6e47d417e723bfb62d1a53099155b4d788848b78232916ba132c03c2fe checksum: 10c0/bcac6d79fcfb8cc1356d65dff576bdad217edd0df189a5dea032b0fd57cef335b73ad6d8e395709245bc1c6a8c672a83144ecea48550ca560544d2399af8f2d3
languageName: node languageName: node
linkType: hard linkType: hard
"@vector-im/compound-web@npm:^8.0.0": "@vector-im/compound-web@npm:^8.0.0":
version: 8.3.4 version: 8.4.0
resolution: "@vector-im/compound-web@npm:8.3.4" resolution: "@vector-im/compound-web@npm:8.4.0"
dependencies: dependencies:
"@floating-ui/react": "npm:^0.27.0" "@floating-ui/react": "npm:^0.27.0"
"@radix-ui/react-context-menu": "npm:^2.2.16" "@radix-ui/react-context-menu": "npm:^2.2.16"
@@ -6272,7 +6272,7 @@ __metadata:
peerDependenciesMeta: peerDependenciesMeta:
"@types/react": "@types/react":
optional: true optional: true
checksum: 10c0/44764fa64b5fce2e7181e25b50ee970eda4d921cf650b92bd5e88df0eb60872f3086b8702d18f55c3e39b3751ac19f10bafda8c4306df65c3605bd44b297d95c checksum: 10c0/31b73555c47b373d4250872bfe863a030b487197bf1198e3cf3a1ec344f2b02f0c72c1513bb598c1cbd7a91d3c6a334d0c8ae37bd7c90d4859c864fc223e059a
languageName: node languageName: node
linkType: hard linkType: hard