From f78122b5c5dfa79b371d4c246b15cf5b8712a879 Mon Sep 17 00:00:00 2001 From: Robin <129759474+robinmiau@users.noreply.github.com> Date: Tue, 21 Apr 2026 01:52:44 +0200 Subject: [PATCH 1/5] Update panic component for better error detail display Refactor panic component to improve error detail handling. --- web/packages/core/src/internal/ui/panic.tsx | 48 ++++++++++++++++----- 1 file changed, 38 insertions(+), 10 deletions(-) diff --git a/web/packages/core/src/internal/ui/panic.tsx b/web/packages/core/src/internal/ui/panic.tsx index 2127d05a5c54..eaadd1e09d96 100644 --- a/web/packages/core/src/internal/ui/panic.tsx +++ b/web/packages/core/src/internal/ui/panic.tsx @@ -49,9 +49,9 @@ function createPanicAction({ errorText: string; }) { if (action.type === "show_details") { - const onClick = () => { + const onClick = (event: MouseEvent) => { + event.preventDefault(); showDetails(); - return false; }; return (
  • @@ -396,21 +396,17 @@ export function showPanicScreen( const errorText = errorArray.join(""); const { body, actions } = createPanicError(error); - const panicBody = createRef(); + const detailsModal = createRef(); + const copyButton = createRef(); const showDetails = () => { - panicBody.current!.classList.add("details"); - panicBody.current!.replaceChildren( - , - ); + detailsModal.current!.classList.remove("hidden"); }; container.textContent = ""; container.appendChild(
    {text("panic-title")}
    -
    - {body} -
    +
    {body}
    +
    , ); } From e7f887cd758a338c75c5378c83d9170f93db5f76 Mon Sep 17 00:00:00 2001 From: Robin <129759474+robinmiau@users.noreply.github.com> Date: Tue, 21 Apr 2026 02:05:53 +0200 Subject: [PATCH 2/5] Add checked property and improve context menu handling --- .../core/src/internal/player/inner.tsx | 126 +++++++++++++++--- 1 file changed, 105 insertions(+), 21 deletions(-) diff --git a/web/packages/core/src/internal/player/inner.tsx b/web/packages/core/src/internal/player/inner.tsx index a42de959096a..25291f25979b 100644 --- a/web/packages/core/src/internal/player/inner.tsx +++ b/web/packages/core/src/internal/player/inner.tsx @@ -93,6 +93,12 @@ interface ContextMenuItem { * @default true */ enabled?: boolean; + + /** + * Whether this item has a checkmark next to it. + * When defined, a checkmark column is shown for all items in the group. + */ + checked?: boolean; } /** @@ -178,6 +184,9 @@ export class InnerPlayer { // Allows the user to permanently disable the context menu. private contextMenuForceDisabled = false; + // The client position where the custom context menu was last opened. + private contextMenuOpenPosition: { x: number; y: number } | null = null; + // Whether the most recent pointer event was from a touch (or pen). private isTouch = false; // Whether this device sends contextmenu events. @@ -187,6 +196,8 @@ export class InnerPlayer { // When set to `true`, the next context menu event will // not show the context menu. private _suppressContextMenu = false; + private hideContextMenuOnWheel: ((event: WheelEvent) => void) | null = + null; // The effective config loaded upon `.load()`. public loadedConfig?: URLLoadOptions | DataLoadOptions; @@ -382,7 +393,7 @@ export class InnerPlayer { } }; - modalElement.parentNode!.addEventListener("click", hideModal); + modalElement.addEventListener("click", hideModal); const modalArea = modalElement.querySelector(".modal-area"); if (modalArea) { modalArea.addEventListener("click", (event) => @@ -447,6 +458,10 @@ export class InnerPlayer { volumeMuteCheckbox.checked = this.volumeSettings.isMuted; volumeSlider.disabled = volumeMuteCheckbox.checked; volumeSlider.valueAsNumber = this.volumeSettings.volume; + volumeSlider.style.setProperty( + "--volume-pct", + `${this.volumeSettings.volume}%`, + ); volumeSliderText.textContent = volumeSlider.value + "%"; setVolumeIcon(); @@ -459,6 +474,10 @@ export class InnerPlayer { }); volumeSlider.addEventListener("input", () => { volumeSliderText.textContent = volumeSlider.value + "%"; + volumeSlider.style.setProperty( + "--volume-pct", + `${volumeSlider.value}%`, + ); this.volumeSettings.volume = volumeSlider.valueAsNumber; this.instance?.set_volume(this.volumeSettings.get_volume()); setVolumeIcon(); @@ -750,7 +769,17 @@ export class InnerPlayer { this.rendererDebugInfo = this.instance!.renderer_debug_info(); - if (this.rendererDebugInfo.includes("Adapter Device Type: Cpu")) { + // Show a hardware acceleration warning when software rendering is detected. + // The device type is unreliable through WebGL/ANGLE (always "Other"), so we + // also match known software renderer names (WARP, SwiftShader, Mesa llvmpipe). + const isSoftwareRenderer = + this.rendererDebugInfo.includes("Adapter Device Type: Cpu") || + this.rendererDebugInfo.includes("Adapter Device Type: VirtualGpu") || + this.rendererDebugInfo.includes("Microsoft Basic Render Driver") || + this.rendererDebugInfo.includes("SwiftShader") || + this.rendererDebugInfo.includes("llvmpipe") || + this.rendererDebugInfo.includes("softpipe"); + if (isSoftwareRenderer) { this.container.addEventListener( "mouseover", this.openHardwareAccelerationModal.bind(this), @@ -1503,7 +1532,6 @@ export class InnerPlayer { } private contextMenuItems(): Array { - const CHECKMARK = String.fromCharCode(0x2713); const items: Array = []; const addSeparator = () => { // Don't start with or duplicate separators. @@ -1524,12 +1552,11 @@ export class InnerPlayer { addSeparator(); } items.push({ - // TODO: better checkboxes - text: - item.caption + (item.checked ? ` (${CHECKMARK})` : ``), + text: item.caption, onClick: async () => this.instance?.run_context_menu_callback(index), enabled: item.enabled, + checked: item.checked, }); }); @@ -1669,6 +1696,22 @@ export class InnerPlayer { return; } + // If the custom menu is already open and the user right-clicks again + // at roughly the same position, hide our menu and let the browser show + // its native context menu. A 8px threshold accounts for slight mouse drift. + if ( + event.type === "contextmenu" && + !this.contextMenuOverlay.classList.contains("hidden") && + this.contextMenuOpenPosition !== null + ) { + const dx = event.clientX - this.contextMenuOpenPosition.x; + const dy = event.clientY - this.contextMenuOpenPosition.y; + if (dx * dx + dy * dy <= 64) { + this.hideContextMenu(); + return; + } + } + event.preventDefault(); if (this._suppressContextMenu) { @@ -1700,6 +1743,20 @@ export class InnerPlayer { ); event.stopPropagation(); } + this.hideContextMenuOnWheel = (event: WheelEvent) => { + const rect = this.contextMenuElement.getBoundingClientRect(); + if ( + event.clientX < rect.left || + event.clientX > rect.right || + event.clientY < rect.top || + event.clientY > rect.bottom + ) { + this.hideContextMenu(); + } + }; + document.addEventListener("wheel", this.hideContextMenuOnWheel, { + capture: true, + }); if ( [false, ContextMenu.Off].includes( @@ -1720,8 +1777,17 @@ export class InnerPlayer { ); } + const items = this.contextMenuItems(); + const hasCheckmarks = items.some( + (item) => item !== null && item.checked !== undefined, + ); + this.contextMenuElement.classList.toggle( + "has-checkmarks", + hasCheckmarks, + ); + // Populate context menu items. - for (const item of this.contextMenuItems()) { + for (const item of items) { if (item === null) { this.contextMenuElement.appendChild(
  • , ); } else { - const { text, onClick, enabled } = item; + const { text, onClick, enabled, checked } = item; const menuItem = (
  • @@ -1771,6 +1838,8 @@ export class InnerPlayer { this.contextMenuOverlay.classList.remove("hidden"); + this.contextMenuOpenPosition = { x: event.clientX, y: event.clientY }; + const playerRect = this.element.getBoundingClientRect(); const contextMenuRect = this.contextMenuElement.getBoundingClientRect(); @@ -1786,19 +1855,25 @@ export class InnerPlayer { // mode and get the body when it's null. const viewportElement = document.scrollingElement || document.body; - // Keep the entire context menu inside the viewport. - const overflowX = Math.max( - 0, - event.clientX + contextMenuRect.width - viewportElement.clientWidth, - ); - const overflowY = Math.max( - 0, - event.clientY + - contextMenuRect.height - - viewportElement.clientHeight, - ); - const x = event.clientX - playerRect.x - overflowX; - const y = event.clientY - playerRect.y - overflowY; + const menuWidth = contextMenuRect.width; + const menuHeight = contextMenuRect.height; + const vw = viewportElement.clientWidth; + const vh = viewportElement.clientHeight; + + // Flip the menu above/left of the cursor (like native context menus) + // when it would overflow, falling back to clamping if there's no room. + let cx = event.clientX; + if (cx + menuWidth > vw) { + cx = event.clientX - menuWidth >= 0 ? event.clientX - menuWidth : vw - menuWidth; + } + + let cy = event.clientY; + if (cy + menuHeight > vh) { + cy = event.clientY - menuHeight >= 0 ? event.clientY - menuHeight : vh - menuHeight; + } + + const x = cx - playerRect.x; + const y = cy - playerRect.y; const isRtl = getComputedStyle(this.contextMenuElement).direction === "rtl"; @@ -1816,6 +1891,15 @@ export class InnerPlayer { private hideContextMenu(): void { this.instance?.clear_custom_menu_items(); this.contextMenuOverlay.classList.add("hidden"); + this.contextMenuOpenPosition = null; + if (this.hideContextMenuOnWheel) { + document.removeEventListener( + "wheel", + this.hideContextMenuOnWheel, + { capture: true }, + ); + this.hideContextMenuOnWheel = null; + } } /** From 0260b1ce0fa2fe8a298daee6d887f20858acb436 Mon Sep 17 00:00:00 2001 From: Robin <129759474+robinmiau@users.noreply.github.com> Date: Tue, 21 Apr 2026 02:13:33 +0200 Subject: [PATCH 3/5] Enhance static styles with new variables and updates Added new CSS variables for dark mode and modal styles. Updated styles for panic overlay and modal components. --- .../core/src/internal/ui/static-styles.css | 344 ++++++++++++++---- 1 file changed, 274 insertions(+), 70 deletions(-) diff --git a/web/packages/core/src/internal/ui/static-styles.css b/web/packages/core/src/internal/ui/static-styles.css index 445ecec63f9d..36e24d6ec4c6 100644 --- a/web/packages/core/src/internal/ui/static-styles.css +++ b/web/packages/core/src/internal/ui/static-styles.css @@ -3,7 +3,11 @@ pointer-events: inherit; --ruffle-blue: #37528c; + --ruffle-blue-dark: #253559; --ruffle-orange: #ffad33; + --modal-background: #fafafa; + --modal-foreground-rgb: 0, 0, 0; + --modal-foreground-filter: none; display: inline-block; position: relative; @@ -86,50 +90,119 @@ scale: 0.8; } -/* Includes inverted colors from play button! */ #panic { - font-size: 20px; + font-size: 15px; text-align: center; - background: linear-gradient(180deg, #fd3a40 0%, #fda138 100%); + background: var(--ruffle-blue); color: white; display: flex; flex-flow: column; - justify-content: space-around; + align-items: center; + justify-content: center; + gap: 8px; + padding: 16px; overflow: auto; a { - color: var(--ruffle-blue); - font-weight: bold; + color: white; + text-underline-offset: 2px; } } #panic-title { - font-size: xxx-large; + font-size: 28px; font-weight: bold; + letter-spacing: -0.5px; } #panic-body { - &.details { - flex: 0.9; - margin: 0 10px; + max-width: 480px; + width: 100%; +} + +#panic-details-modal { + position: absolute; + inset: 0; + background: #0008; + display: flex; + align-items: center; + justify-content: center; + padding: 8px; + box-sizing: border-box; + z-index: 1; +} + +#panic-details-content { + position: relative; + background-color: var(--modal-background); + color: rgb(var(--modal-foreground-rgb)); + width: 100%; + max-width: 720px; + height: 80%; + border-radius: 12px; + overflow: hidden; + box-shadow: 0 2px 6px 0 #0008; + padding: 44px 12px 12px; + box-sizing: border-box; + + .panic-copy-button { + position: absolute; + top: 14px; + right: 40px; + width: 16px; + height: 16px; + background-image: url("data:image/svg+xml,"); + cursor: pointer; + filter: var(--modal-foreground-filter); + border-radius: 4px; + opacity: 0.6; + transition: + opacity 0.15s, + background-image 0s; + + &:hover { + opacity: 1; + } + + &.copied { + background-image: url("data:image/svg+xml,"); + filter: none; + opacity: 1; + pointer-events: none; + cursor: default; + } } textarea { width: 100%; height: 100%; + font-family: monospace; + font-size: 12px; resize: none; + background: rgb(var(--modal-foreground-rgb), 0.07); + border: none; + border-radius: 8px; + color: rgb(var(--modal-foreground-rgb)); + padding: 10px; + box-sizing: border-box; + outline: none; } -} -#panic ul { - padding: 0; - display: flex; - list-style-type: none; - justify-content: space-evenly; + textarea::-webkit-scrollbar { + width: 6px; + } + + textarea::-webkit-scrollbar-thumb { + background: rgb(var(--modal-foreground-rgb), 0.25); + border-radius: 3px; + } + + textarea::-webkit-scrollbar-track { + background: transparent; + } } #message-overlay { - position: absolute; background: var(--ruffle-blue); color: var(--ruffle-orange); opacity: 1; @@ -165,34 +238,49 @@ color: var(--ruffle-orange); border: 2px solid var(--ruffle-orange); font-weight: bold; - font-size: 1.25em; - border-radius: 0.6em; - padding: 10px; + font-size: 16px; + font-family: inherit; + border-radius: 8px; + padding: 10px 16px; text-decoration: none; - margin: 2% 0; - } - - a:hover, - button:hover { - background: #ffffff4c; + margin: 8px 0; + transition: background 0.15s; } } -#continue-btn { - cursor: pointer; - background: var(--ruffle-blue); - color: var(--ruffle-orange); - border: 2px solid var(--ruffle-orange); - font-weight: bold; - font-size: 20px; - border-radius: 20px; - padding: 10px; +#panic ul { + padding: 0; + margin: 0; + display: flex; + list-style-type: none; + justify-content: center; + flex-wrap: wrap; + gap: 8px; + + li a { + display: inline-block; + background: transparent; + border: 1px solid var(--ruffle-orange); + border-radius: 8px; + padding: 8px 16px; + color: var(--ruffle-orange); + text-decoration: none; + font-weight: bold; + font-size: 13px; + font-family: inherit; + transition: background 0.15s; - &:hover { - background: #ffffff4c; + &:hover { + background: rgb(255 173 51 / 15%); + } } } +#message-overlay a:hover, +#message-overlay button:hover { + background: #ffffff4c; +} + #context-menu-overlay, .modal { width: 100%; @@ -204,19 +292,38 @@ #context-menu { color: rgb(var(--modal-foreground-rgb)); background-color: var(--modal-background); - border: 1px solid gray; - box-shadow: 0 5px 10px -5px black; + box-shadow: 0 4px 16px #0006; + border-radius: 8px; + overflow: hidden; position: absolute; font-size: 14px; text-align: start; list-style: none; white-space: nowrap; - padding: 3px 0; + padding: 5px 0; margin: 0; .menu-item { - padding: 5px 10px; - color: rgb(var(--modal-foreground-rgb)); + padding: 7px 12px; + } + + &.has-checkmarks .menu-item { + padding-left: 32px; + position: relative; + } + + &.has-checkmarks .menu-item.checked::before { + content: ""; + width: 16px; + height: 16px; + position: absolute; + left: 8px; + top: 50%; + transform: translateY(-50%); + background-image: url("data:image/svg+xml,"); + background-repeat: no-repeat; + background-size: contain; + filter: var(--modal-foreground-filter); } .menu-item.disabled { @@ -231,7 +338,7 @@ .menu-separator hr { border: none; border-bottom: 1px solid rgb(var(--modal-foreground-rgb), 0.2); - margin: 3px; + margin: 4px 0; } } @@ -251,7 +358,7 @@ max-width: 316px; max-height: 10px; height: 20%; - background: #253559; + background: var(--ruffle-blue-dark); } .loadbar-inner { @@ -299,31 +406,44 @@ .modal { background-color: #0008; + display: flex; + align-items: center; + justify-content: center; + padding: 8px; + box-sizing: border-box; + overflow: auto; } .modal-area { position: relative; - left: 50%; - transform: translateX(-50%); background-color: var(--modal-background); color: rgb(var(--modal-foreground-rgb)); width: fit-content; - padding: 8px 12px; + padding: 40px 16px 16px; border-radius: 12px; + overflow: hidden; box-shadow: 0 2px 6px 0 #0008; } #modal-area { width: 450px; height: 300px; + padding: 0; } .close-modal { width: 16px; height: 16px; - background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' height='16px' viewBox='0 -960 960 960' width='16px' fill='black'%3E%3Cpath d='M480-392 300-212q-18 18-44 18t-44-18q-18-18-18-44t18-44l180-180-180-180q-18-18-18-44t18-44q18-18 44-18t44 18l180 180 180-180q18-18 44-18t44 18q18 18 18 44t-18 44L568-480l180 180q18 18 18 44t-18 44q-18 18-44 18t-44-18L480-392Z'/%3E%3C/svg%3E"); + background-image: url("data:image/svg+xml,"); cursor: pointer; filter: var(--modal-foreground-filter); + border-radius: 4px; + opacity: 0.6; + transition: opacity 0.15s; + + &:hover { + opacity: 1; + } } .modal-button { @@ -334,6 +454,11 @@ padding: 4px 8px; border-radius: 6px; cursor: pointer; + transition: background-color 0.15s; + + &:hover { + background-color: rgb(var(--modal-foreground-rgb), 0.3); + } } :not(#volume-controls) > .close-modal { @@ -343,9 +468,12 @@ } .general-save-options { - text-align: center; - padding-bottom: 8px; - border-bottom: 2px solid rgb(var(--modal-foreground-rgb), 0.3); + display: flex; + align-items: center; + justify-content: center; + height: 44px; + padding: 0 44px; + border-bottom: 1px solid rgb(var(--modal-foreground-rgb), 0.15); } #local-saves { @@ -353,18 +481,40 @@ border-collapse: collapse; overflow-y: auto; display: block; - height: calc(100% - 45px); + height: calc(100% - 44px); min-height: 30px; td { - border-bottom: 2px solid rgb(var(--modal-foreground-rgb), 0.15); - height: 30px; + border-bottom: 1px solid rgb(var(--modal-foreground-rgb), 0.08); + height: 34px; + padding: 0 6px; } td:nth-child(1) { width: 100%; word-break: break-all; } + + td:not(:first-child) { + padding: 0 2px; + } + + > tr:hover td { + background-color: rgb(var(--modal-foreground-rgb), 0.05); + } + + &::-webkit-scrollbar { + width: 8px; + } + + &::-webkit-scrollbar-thumb { + background: rgb(var(--modal-foreground-rgb), 0.25); + border-radius: 3px; + } + + &::-webkit-scrollbar-track { + background: transparent; + } } .save-option { @@ -374,11 +524,16 @@ cursor: pointer; filter: var(--modal-foreground-filter); vertical-align: middle; - opacity: 0.4; -} + opacity: 0.35; + transition: opacity 0.12s; -#local-saves > tr:hover .save-option { - opacity: 1; + :where(#local-saves tr:hover) & { + opacity: 0.65; + } + + &:hover { + opacity: 1; + } } #download-save { @@ -415,6 +570,10 @@ background-color: black; } +#volume-controls-modal .modal-area { + padding: 12px 16px; +} + #volume-controls { display: flex; align-items: center; @@ -449,6 +608,51 @@ label[for="mute-checkbox"] { background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' height='24px' viewBox='9 -960 960 960' width='24px' fill='black'%3E%3Cpath d='M754.22-480.5q0-78.52-41.88-143.9-41.88-65.38-111.91-98.62-14.47-6.74-20.47-20.96-6-14.22-.53-28.93 5.74-15.72 20.34-22.46t29.58 0q92.48 42.46 147.97 127.05 55.48 84.6 55.48 187.82t-55.48 187.82q-55.49 84.59-147.97 127.05-14.98 6.74-29.58 0-14.6-6.74-20.34-22.46-5.47-14.71.53-28.93 6-14.22 20.47-20.96 70.03-33.24 111.91-98.62 41.88-65.38 41.88-143.9ZM286.98-357.87H170.2q-17.66 0-30.33-12.67-12.67-12.68-12.67-30.33v-158.26q0-17.65 12.67-30.33 12.67-12.67 30.33-12.67h116.78L416.65-731.8q20.63-20.64 47.1-9.57 26.47 11.07 26.47 39.65v443.44q0 28.58-26.47 39.65t-47.1-9.57L286.98-357.87ZM670.8-480q0 42.48-20.47 80.09-20.48 37.61-54.94 60.82-10.22 5.98-20.19.25-9.98-5.73-9.98-17.44v-248.44q0-11.71 9.98-17.32 9.97-5.61 20.19.37 34.46 23.71 54.94 61.45Q670.8-522.48 670.8-480Z'/%3E%3C/svg%3E"); } +#volume-slider { + appearance: none; + height: 4px; + background: linear-gradient( + to right, + var(--ruffle-orange) var(--volume-pct, 100%), + rgb(var(--modal-foreground-rgb), 0.2) var(--volume-pct, 100%) + ); + border-radius: 2px; + cursor: pointer; + outline: none; + border: none; + + &::-webkit-slider-thumb { + appearance: none; + width: 14px; + height: 14px; + border-radius: 50%; + background: var(--ruffle-orange); + cursor: pointer; + border: none; + } + + &::-moz-range-thumb { + width: 14px; + height: 14px; + border-radius: 50%; + background: var(--ruffle-orange); + border: none; + cursor: pointer; + } + + &:focus-visible::-webkit-slider-thumb { + box-shadow: + 0 0 0 3px var(--modal-background), + 0 0 0 5px var(--ruffle-orange); + } + + &:focus-visible::-moz-range-thumb { + box-shadow: + 0 0 0 3px var(--modal-background), + 0 0 0 5px var(--ruffle-orange); + } +} + #volume-slider-text { width: 4.8ch; text-align: center; @@ -457,9 +661,7 @@ label[for="mute-checkbox"] { #hardware-acceleration-modal .modal-area { text-align: center; - padding: 16px 48px; - width: 95%; - box-sizing: border-box; + padding: 48px 48px 24px; } #acceleration-text { @@ -468,21 +670,23 @@ label[for="mute-checkbox"] { } #clipboard-modal h2 { - margin-top: 4px; - margin-right: 36px; + margin-top: 0; + margin-right: 24px; } #clipboard-modal p:last-child { margin-bottom: 2px; } -/* Handle preferred color scheme. */ -@media (prefers-color-scheme: light) { - :host { - --modal-background: #fafafa; - --modal-foreground-rgb: 0, 0, 0; - --modal-foreground-filter: none; - } +#clipboard-modal b { + display: inline-block; + background-color: rgb(var(--modal-foreground-rgb), 0.1); + border: 1px solid rgb(var(--modal-foreground-rgb), 0.25); + border-radius: 4px; + padding: 1px 6px; + font-family: monospace; + font-size: 0.85em; + margin-right: 6px; } @media (prefers-color-scheme: dark) { From 0ebf72305b21836b6ec456d442d9611fddff7d13 Mon Sep 17 00:00:00 2001 From: Robin <129759474+robinmiau@users.noreply.github.com> Date: Tue, 21 Apr 2026 18:22:15 +0200 Subject: [PATCH 4/5] Fix formatting in inner.tsx --- .../core/src/internal/player/inner.tsx | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/web/packages/core/src/internal/player/inner.tsx b/web/packages/core/src/internal/player/inner.tsx index 25291f25979b..863b47649e59 100644 --- a/web/packages/core/src/internal/player/inner.tsx +++ b/web/packages/core/src/internal/player/inner.tsx @@ -196,8 +196,7 @@ export class InnerPlayer { // When set to `true`, the next context menu event will // not show the context menu. private _suppressContextMenu = false; - private hideContextMenuOnWheel: ((event: WheelEvent) => void) | null = - null; + private hideContextMenuOnWheel: ((event: WheelEvent) => void) | null = null; // The effective config loaded upon `.load()`. public loadedConfig?: URLLoadOptions | DataLoadOptions; @@ -774,7 +773,9 @@ export class InnerPlayer { // also match known software renderer names (WARP, SwiftShader, Mesa llvmpipe). const isSoftwareRenderer = this.rendererDebugInfo.includes("Adapter Device Type: Cpu") || - this.rendererDebugInfo.includes("Adapter Device Type: VirtualGpu") || + this.rendererDebugInfo.includes( + "Adapter Device Type: VirtualGpu", + ) || this.rendererDebugInfo.includes("Microsoft Basic Render Driver") || this.rendererDebugInfo.includes("SwiftShader") || this.rendererDebugInfo.includes("llvmpipe") || @@ -1864,12 +1865,18 @@ export class InnerPlayer { // when it would overflow, falling back to clamping if there's no room. let cx = event.clientX; if (cx + menuWidth > vw) { - cx = event.clientX - menuWidth >= 0 ? event.clientX - menuWidth : vw - menuWidth; + cx = + event.clientX - menuWidth >= 0 + ? event.clientX - menuWidth + : vw - menuWidth; } let cy = event.clientY; if (cy + menuHeight > vh) { - cy = event.clientY - menuHeight >= 0 ? event.clientY - menuHeight : vh - menuHeight; + cy = + event.clientY - menuHeight >= 0 + ? event.clientY - menuHeight + : vh - menuHeight; } const x = cx - playerRect.x; @@ -1893,11 +1900,9 @@ export class InnerPlayer { this.contextMenuOverlay.classList.add("hidden"); this.contextMenuOpenPosition = null; if (this.hideContextMenuOnWheel) { - document.removeEventListener( - "wheel", - this.hideContextMenuOnWheel, - { capture: true }, - ); + document.removeEventListener("wheel", this.hideContextMenuOnWheel, { + capture: true, + }); this.hideContextMenuOnWheel = null; } } From da0d73675c7fb4e4da4dcb9fdea028ed9e46fd67 Mon Sep 17 00:00:00 2001 From: Robin <129759474+robinmiau@users.noreply.github.com> Date: Tue, 21 Apr 2026 18:22:53 +0200 Subject: [PATCH 5/5] Fix formatting in panic.tsx --- web/packages/core/src/internal/ui/panic.tsx | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/web/packages/core/src/internal/ui/panic.tsx b/web/packages/core/src/internal/ui/panic.tsx index eaadd1e09d96..10de8631468a 100644 --- a/web/packages/core/src/internal/ui/panic.tsx +++ b/web/packages/core/src/internal/ui/panic.tsx @@ -420,14 +420,8 @@ export function showPanicScreen( )} -