兼容低内核版本的浏览器
This commit is contained in:
+37
-10
@@ -1,12 +1,39 @@
|
||||
<!doctype html>
|
||||
<html lang="en" class="dark">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1" />
|
||||
<title>Luxsin X9 Controller</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1" />
|
||||
<title>Luxsin X9 Controller</title>
|
||||
<style>
|
||||
body { margin:0; display:flex; align-items:center; justify-content:center; min-height:100vh; background:#000; color:rgba(255,255,255,.5); font-family:-apple-system,sans-serif; }
|
||||
.spinner { width:24px; height:24px; border:3px solid rgba(255,255,255,.15); border-top-color:#00FFF6; border-radius:50%; animation:spin .8s linear infinite; margin-right:12px; }
|
||||
@keyframes spin { to { transform:rotate(360deg); } }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="spinner"></div>
|
||||
<span>Loading…</span>
|
||||
<script>
|
||||
(function () {
|
||||
var ua = navigator.userAgent;
|
||||
var chromeMatch = ua.match(/Chrome\/(\d+)/);
|
||||
var isAndroid = /Android/.test(ua);
|
||||
var chromeVersion = chromeMatch ? parseInt(chromeMatch[1], 10) : 999;
|
||||
|
||||
var currentPath = location.pathname;
|
||||
var basePath = currentPath.substring(0, currentPath.lastIndexOf("/") + 1);
|
||||
|
||||
var target;
|
||||
if (isAndroid && chromeVersion < 100) {
|
||||
target = basePath + "legacy/i.legacy.html";
|
||||
} else {
|
||||
target = basePath + "modern/i.modern.html";
|
||||
}
|
||||
|
||||
var qs = location.search || "";
|
||||
var hash = location.hash || "";
|
||||
location.replace(target + qs + hash);
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
<!doctype html>
|
||||
<html lang="en" class="dark">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1" />
|
||||
<title>Luxsin X9 Controller (Legacy)</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.legacy.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,12 @@
|
||||
<!doctype html>
|
||||
<html lang="en" class="dark">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1" />
|
||||
<title>Luxsin X9 Controller</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 101 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 95 KiB After Width: | Height: | Size: 141 KiB |
+3
-5
@@ -21,7 +21,7 @@ import SelectPage from "./pages/SelectPage";
|
||||
import CommunityPage from "./pages/CommunityPage";
|
||||
import { AIDrawerProvider } from "./contexts/AIDrawerContext";
|
||||
import DesktopAIShell from "./components/DesktopAIShell";
|
||||
import { appShellColumnClass, appShellGutterClass } from "./lib/appShell";
|
||||
import { appShellColumnClass, appShellOuterClass } from "./lib/appShell";
|
||||
|
||||
const EQPage = lazy(() => import("./pages/EQPage"));
|
||||
const EffectsPage = lazy(() => import("./pages/EffectsPage"));
|
||||
@@ -87,10 +87,8 @@ function App() {
|
||||
<Toaster />
|
||||
<Router hook={useHashLocation}>
|
||||
<AIDrawerProvider>
|
||||
<div
|
||||
className={`min-h-dvh bg-[#0d1117] flex justify-center ${appShellGutterClass}`}
|
||||
>
|
||||
<div className={`min-h-dvh ${appShellColumnClass}`}>
|
||||
<div className={appShellOuterClass}>
|
||||
<div className={appShellColumnClass}>
|
||||
<DesktopAIShell>
|
||||
<AppRouter />
|
||||
</DesktopAIShell>
|
||||
|
||||
@@ -22,7 +22,7 @@ import {
|
||||
} from "lucide-react";
|
||||
import { Link, useLocation } from "wouter";
|
||||
import { useMemo } from "react";
|
||||
import { appShellColumnClass, appShellGutterClass } from "@/lib/appShell";
|
||||
import { bottomNavInnerClass, bottomNavOuterClass } from "@/lib/appShell";
|
||||
|
||||
type BottomNavLocale = NonNullable<(typeof localeZh)["bottomNav"]>;
|
||||
|
||||
@@ -56,11 +56,9 @@ export default function BottomNav() {
|
||||
}, [deviceState?.language]);
|
||||
|
||||
return (
|
||||
<nav
|
||||
className={`fixed inset-x-0 bottom-0 z-50 flex justify-center pointer-events-none ${appShellGutterClass}`}
|
||||
>
|
||||
<nav className={bottomNavOuterClass}>
|
||||
<div
|
||||
className={`pointer-events-auto ${appShellColumnClass}`}
|
||||
className={bottomNavInnerClass}
|
||||
style={{
|
||||
background: "rgba(10, 10, 12, 0.96)",
|
||||
backdropFilter: "blur(40px) saturate(200%)",
|
||||
|
||||
@@ -65,6 +65,8 @@ function SplitDivider({
|
||||
);
|
||||
}
|
||||
|
||||
const isLegacyBuild = import.meta.env.VITE_X9_LEGACY === "1";
|
||||
|
||||
/**
|
||||
* lg+: optional side-by-side AI panel with draggable divider. Mobile unchanged (router only).
|
||||
*/
|
||||
@@ -87,7 +89,9 @@ export default function DesktopAIShell({ children }: { children: ReactNode }) {
|
||||
"w-full min-w-0",
|
||||
showSplit
|
||||
? "flex h-dvh max-h-dvh flex-row overflow-hidden"
|
||||
: "min-h-dvh",
|
||||
: isLegacyBuild
|
||||
? "min-h-0"
|
||||
: "min-h-dvh",
|
||||
)}
|
||||
>
|
||||
<div
|
||||
@@ -97,7 +101,9 @@ export default function DesktopAIShell({ children }: { children: ReactNode }) {
|
||||
? "h-full min-h-0 overflow-y-auto overflow-x-hidden"
|
||||
: isLgUp
|
||||
? "min-h-dvh overflow-x-hidden"
|
||||
: "min-h-dvh max-h-dvh overflow-x-hidden overflow-y-auto overscroll-y-contain [touch-action:pan-y]",
|
||||
: isLegacyBuild
|
||||
? "w-full overflow-x-hidden"
|
||||
: "min-h-dvh max-h-dvh overflow-x-hidden overflow-y-auto overscroll-y-contain [touch-action:pan-y]",
|
||||
)}
|
||||
style={
|
||||
showSplit
|
||||
|
||||
+57
-1
@@ -119,6 +119,8 @@
|
||||
html,
|
||||
body,
|
||||
#root {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
@@ -165,6 +167,60 @@
|
||||
}
|
||||
|
||||
@layer components {
|
||||
/* App shell: mobile full-bleed; desktop centered column with gutters (lg+). */
|
||||
.x9-app-shell-outer {
|
||||
min-height: 100dvh;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
background-color: #0d1117;
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
.x9-app-shell-column {
|
||||
min-height: 100dvh;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
.x9-bottom-nav-outer {
|
||||
pointer-events: none;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 50;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
justify-content: center;
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
.x9-bottom-nav-inner {
|
||||
pointer-events: auto;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
.x9-app-shell-outer,
|
||||
.x9-bottom-nav-outer {
|
||||
padding-left: 1.5rem;
|
||||
padding-right: 1.5rem;
|
||||
}
|
||||
|
||||
.x9-app-shell-column,
|
||||
.x9-bottom-nav-inner {
|
||||
max-width: 1180px;
|
||||
}
|
||||
}
|
||||
|
||||
.container {
|
||||
width: 100%;
|
||||
margin-left: auto;
|
||||
@@ -374,7 +430,7 @@
|
||||
padding: 16px 16px 8px;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 10;
|
||||
z-index: 50;
|
||||
background: rgba(0, 0, 0, 0.85);
|
||||
backdrop-filter: blur(20px);
|
||||
-webkit-backdrop-filter: blur(20px);
|
||||
|
||||
@@ -0,0 +1,346 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
/* Legacy baseline: Chrome 91 (no dvh/svh, no color-mix, no oklch). */
|
||||
html,
|
||||
body,
|
||||
#root {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
@media (max-width: 1023px) {
|
||||
html,
|
||||
body,
|
||||
#root {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
overscroll-behavior-x: none;
|
||||
}
|
||||
|
||||
#root {
|
||||
overflow-x: hidden;
|
||||
overflow-y: visible;
|
||||
}
|
||||
|
||||
/*
|
||||
* Nested max-h-dvh + overflow-y-auto (DesktopAIShell on mobile) does not scroll
|
||||
* reliably on Chrome 91 WebView — use document scrolling instead.
|
||||
*/
|
||||
.max-h-dvh {
|
||||
max-height: none !important;
|
||||
}
|
||||
|
||||
.h-dvh {
|
||||
height: auto !important;
|
||||
}
|
||||
|
||||
.min-h-dvh {
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.x9-app-shell-outer {
|
||||
min-height: 100vh;
|
||||
min-height: -webkit-fill-available;
|
||||
}
|
||||
|
||||
.x9-app-shell-column {
|
||||
min-height: 0;
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
background: #000;
|
||||
color: #f2f2f2;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
font-family: -apple-system, "SF Pro Display", "SF Pro Text", "Helvetica Neue",
|
||||
sans-serif;
|
||||
}
|
||||
|
||||
@layer utilities {
|
||||
.min-h-dvh {
|
||||
min-height: 100vh;
|
||||
min-height: -webkit-fill-available;
|
||||
}
|
||||
.h-dvh {
|
||||
height: 100vh;
|
||||
height: -webkit-fill-available;
|
||||
}
|
||||
.max-h-dvh {
|
||||
max-height: 100vh;
|
||||
max-height: -webkit-fill-available;
|
||||
}
|
||||
.h-dvh-full {
|
||||
height: 100vh;
|
||||
height: -webkit-fill-available;
|
||||
}
|
||||
.min-h-dvh-full {
|
||||
min-height: 100vh;
|
||||
min-height: -webkit-fill-available;
|
||||
}
|
||||
}
|
||||
|
||||
@layer components {
|
||||
.x9-app-shell-outer {
|
||||
min-height: 100vh;
|
||||
min-height: -webkit-fill-available;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
background-color: #0d1117;
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
.x9-app-shell-column {
|
||||
min-height: 100vh;
|
||||
min-height: -webkit-fill-available;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
.x9-bottom-nav-outer {
|
||||
pointer-events: none;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 50;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
justify-content: center;
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
.x9-bottom-nav-inner {
|
||||
pointer-events: auto;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
.x9-app-shell-outer,
|
||||
.x9-bottom-nav-outer {
|
||||
padding-left: 1.5rem;
|
||||
padding-right: 1.5rem;
|
||||
}
|
||||
|
||||
.x9-app-shell-column,
|
||||
.x9-bottom-nav-inner {
|
||||
max-width: 1180px;
|
||||
}
|
||||
}
|
||||
|
||||
.glass-card {
|
||||
background: rgba(28, 28, 30, 0.92);
|
||||
-webkit-backdrop-filter: blur(20px) saturate(180%);
|
||||
backdrop-filter: blur(20px) saturate(180%);
|
||||
border-radius: 16px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.07);
|
||||
}
|
||||
|
||||
.ios-list-group {
|
||||
background: rgba(28, 28, 30, 0.92);
|
||||
border-radius: 16px;
|
||||
overflow: hidden;
|
||||
border: 1px solid rgba(255, 255, 255, 0.06);
|
||||
}
|
||||
|
||||
.ios-list-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 13px 16px;
|
||||
position: relative;
|
||||
transition: background 0.15s ease;
|
||||
min-height: 52px;
|
||||
}
|
||||
|
||||
.ios-list-row:active {
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
|
||||
.ios-list-row + .ios-list-row::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 16px;
|
||||
right: 0;
|
||||
height: 1px;
|
||||
transform: scaleY(0.5);
|
||||
transform-origin: top left;
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
}
|
||||
|
||||
.ios-chevron {
|
||||
color: rgba(255, 255, 255, 0.25);
|
||||
flex-shrink: 0;
|
||||
margin-left: 6px;
|
||||
}
|
||||
|
||||
.ios-row-value {
|
||||
color: rgba(255, 255, 255, 0.4);
|
||||
font-size: 15px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.cyan-slider {
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
width: 100%;
|
||||
height: 4px;
|
||||
border-radius: 2px;
|
||||
background: rgba(255, 255, 255, 0.12);
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
touch-action: pan-y;
|
||||
}
|
||||
|
||||
.cyan-slider::-webkit-slider-thumb {
|
||||
-webkit-appearance: none;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
border-radius: 9999px;
|
||||
background: #ffffff;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.5);
|
||||
cursor: pointer;
|
||||
transition: transform 0.1s ease;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.cyan-slider::-webkit-slider-thumb:active {
|
||||
transform: scale(1.15);
|
||||
}
|
||||
|
||||
.cyan-slider::-moz-range-track {
|
||||
height: 4px;
|
||||
border-radius: 2px;
|
||||
background: rgba(255, 255, 255, 0.12);
|
||||
}
|
||||
|
||||
.cyan-slider::-moz-range-thumb {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
border-radius: 9999px;
|
||||
background: #ffffff;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.5);
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.cyan-slider-thumb-only {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.cyan-slider-thumb-only::-webkit-slider-thumb {
|
||||
cursor: grab;
|
||||
}
|
||||
|
||||
.cyan-slider-thumb-only::-webkit-slider-thumb:active {
|
||||
cursor: grabbing;
|
||||
}
|
||||
|
||||
.cyan-slider-thumb-only::-moz-range-thumb {
|
||||
cursor: grab;
|
||||
}
|
||||
|
||||
.icon-circle-btn {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
border-radius: 9999px;
|
||||
background: rgba(44, 44, 46, 0.9);
|
||||
border: 1px solid rgba(255, 255, 255, 0.12);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: transform 0.15s ease, background 0.15s ease, box-shadow 0.15s ease,
|
||||
border-color 0.15s ease;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.icon-circle-btn:active {
|
||||
transform: scale(0.93);
|
||||
background: rgba(60, 60, 62, 0.95);
|
||||
}
|
||||
|
||||
.icon-circle-btn.active {
|
||||
background: rgba(0, 255, 246, 0.12);
|
||||
border-color: rgba(0, 255, 246, 0.55);
|
||||
box-shadow: 0 0 0 2px rgba(0, 255, 246, 0.18), 0 0 18px rgba(0, 255, 246, 0.28);
|
||||
}
|
||||
|
||||
.ios-toggle {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 46px;
|
||||
height: 28px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.ios-toggle input {
|
||||
opacity: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
position: absolute;
|
||||
}
|
||||
.ios-toggle-track {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
border-radius: 999px;
|
||||
background: rgba(120, 120, 128, 0.32);
|
||||
transition: background 0.25s ease;
|
||||
cursor: pointer;
|
||||
}
|
||||
.ios-toggle input:checked ~ .ios-toggle-track {
|
||||
background: #00fff6;
|
||||
box-shadow: 0 0 12px rgba(0, 255, 246, 0.5);
|
||||
}
|
||||
.ios-toggle-thumb {
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
left: 2px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-radius: 50%;
|
||||
background: #ffffff;
|
||||
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.4);
|
||||
transition: transform 0.25s cubic-bezier(0.34, 1.56, 0.64, 1);
|
||||
pointer-events: none;
|
||||
}
|
||||
.ios-toggle input:checked ~ .ios-toggle-track .ios-toggle-thumb {
|
||||
transform: translateX(18px);
|
||||
}
|
||||
|
||||
.ios-section-label {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.04em;
|
||||
color: #00fff6;
|
||||
padding: 20px 4px 8px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 16px 16px 8px;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 50;
|
||||
background: rgba(0, 0, 0, 0.85);
|
||||
-webkit-backdrop-filter: blur(20px);
|
||||
backdrop-filter: blur(20px);
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
/** Centered phone-style shell width (desktop / ultrawide). */
|
||||
export const APP_SHELL_MAX_PX = 1180;
|
||||
|
||||
/** Inner column inside horizontal gutters — use with a flex/grid centering parent. */
|
||||
export const appShellColumnClass =
|
||||
"w-full min-w-0 max-w-[min(1180px,100%)]";
|
||||
|
||||
export const appShellGutterClass = "px-4 sm:px-6";
|
||||
/** Responsive rules live in `.x9-app-shell-*` (index.css + index.legacy.css). */
|
||||
export const appShellOuterClass = "x9-app-shell-outer";
|
||||
export const appShellColumnClass = "x9-app-shell-column";
|
||||
export const bottomNavOuterClass = "x9-bottom-nav-outer";
|
||||
export const bottomNavInnerClass = "x9-bottom-nav-inner";
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* Minimal shims for Chrome 91 WebView (and similar legacy Chromium).
|
||||
* Import once from main.legacy.tsx before the app mounts.
|
||||
*/
|
||||
|
||||
if (typeof Object.hasOwn !== "function") {
|
||||
Object.hasOwn = (obj, prop) =>
|
||||
Object.prototype.hasOwnProperty.call(obj, prop);
|
||||
}
|
||||
|
||||
if (!Array.prototype.at) {
|
||||
// eslint-disable-next-line no-extend-native
|
||||
Array.prototype.at = function at(index) {
|
||||
const len = this.length;
|
||||
const k = index >= 0 ? index : len + index;
|
||||
if (k < 0 || k >= len) return undefined;
|
||||
return this[k];
|
||||
};
|
||||
}
|
||||
|
||||
if (typeof globalThis.structuredClone !== "function") {
|
||||
globalThis.structuredClone = <T>(value: T): T =>
|
||||
JSON.parse(JSON.stringify(value)) as T;
|
||||
}
|
||||
@@ -28,6 +28,12 @@ export function decodeCustomBase64(encoded: string): string {
|
||||
const index = ALPHABET_CUSTOM.indexOf(char);
|
||||
translated += index !== -1 ? ALPHABET_STANDARD.charAt(index) : char;
|
||||
}
|
||||
// Chrome 91 atob() is stricter than modern browsers.
|
||||
translated = translated.replace(/\s/g, "");
|
||||
const padNeeded = (4 - (translated.length % 4)) % 4;
|
||||
if (padNeeded > 0) {
|
||||
translated += "=".repeat(padNeeded);
|
||||
}
|
||||
const decoded = atob(translated);
|
||||
const bytes = new Uint8Array(decoded.length);
|
||||
for (let i = 0; i < decoded.length; i++) {
|
||||
@@ -168,7 +174,7 @@ export interface PeqApplyPayload {
|
||||
// ============================================================
|
||||
// Input/Output Labels
|
||||
// ============================================================
|
||||
export const INPUT_LABELS = ["USB", "USB-C", "Coaxial", "Optical", "Bluetooth", "IIS", "RCA"];
|
||||
export const INPUT_LABELS = ["USB", "USB-C", "Coaxial", "Optical", "Bluetooth", "HDMI-EARC", "RCA"];
|
||||
export const OUTPUT_LABELS = ["XLR", "RCA", "Headset", "XLR/RCA"];
|
||||
|
||||
/** Device `input` index for Bluetooth source. */
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
"home": { "audioSet": "Audioeinstellungen" },
|
||||
"source": {
|
||||
"label": "Eingang & Ausgang",
|
||||
"inputOptions": ["USB-B", "USB-C", "Koaxial", "Optisch", "Bluetooth", "HDMI-EARC", "RCA", "USB-Treiber"],
|
||||
"input": {
|
||||
"coaxial": "Koaxial",
|
||||
"optical": "Optisch",
|
||||
|
||||
@@ -55,8 +55,8 @@
|
||||
"pageTitle": "I/O",
|
||||
"inputSection": "Input",
|
||||
"outputSection": "Output",
|
||||
"inputOptions": ["USB-B", "USB-C", "Coaxial", "Optical", "Bluetooth", "IIS"],
|
||||
"outputOptions": ["XLR", "RCA", "Headset", "XLR/RCA"],
|
||||
"inputOptions": ["USB-B", "USB-C", "Coaxial", "Optical", "Bluetooth", "HDMI-EARC", "RCA", "USB Driver"],
|
||||
"outputOptions": ["XLR", "RCA", "XLR/RCA", "Headset"],
|
||||
"input": {
|
||||
"coaxial": "COAXIAL",
|
||||
"optical": "OPTICAL",
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
"home": { "audioSet": "Configuración de audio" },
|
||||
"source": {
|
||||
"label": "Entrada/Salida",
|
||||
"inputOptions": ["USB-B", "USB-C", "Coaxial", "Óptico", "Bluetooth", "HDMI-EARC", "RCA", "Controlador USB"],
|
||||
"input": {
|
||||
"coaxial": "Coaxial",
|
||||
"optical": "Óptico",
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
"home": { "audioSet": "Paramètres audio" },
|
||||
"source": {
|
||||
"label": "Entrée/Sortie",
|
||||
"inputOptions": ["USB-B", "USB-C", "Coaxial", "Optique", "Bluetooth", "HDMI-EARC", "RCA", "Clé USB"],
|
||||
"input": {
|
||||
"coaxial": "Coaxial",
|
||||
"optical": "Optique",
|
||||
|
||||
@@ -55,8 +55,8 @@
|
||||
"pageTitle": "輸入/輸出",
|
||||
"inputSection": "輸入",
|
||||
"outputSection": "輸出",
|
||||
"inputOptions": ["USB-B", "USB-C", "同軸", "光纖", "藍牙", "IIS"],
|
||||
"outputOptions": ["XLR", "RCA", "耳機", "XLR/RCA"],
|
||||
"inputOptions": ["USB-B", "USB-C", "同軸", "光纖", "藍牙", "HDMI-EARC", "類比RCA", "USB隨身碟"],
|
||||
"outputOptions": ["XLR", "RCA", "XLR/RCA", "耳機"],
|
||||
"input": { "coaxial": "同軸", "optical": "光纖", "bluetooth": "藍牙", "rca": "類比RCA" },
|
||||
"output": { "headset": "耳機" }
|
||||
},
|
||||
|
||||
@@ -55,8 +55,8 @@
|
||||
"pageTitle": "输入输出",
|
||||
"inputSection": "输入",
|
||||
"outputSection": "输出",
|
||||
"inputOptions": ["USB-B", "USB-C", "同轴", "光纤", "蓝牙", "IIS"],
|
||||
"outputOptions": ["XLR", "RCA", "耳机", "XLR/RCA"],
|
||||
"inputOptions": ["USB-B", "USB-C", "同轴", "光纤", "蓝牙", "HDMI-EARC", "模拟RCA", "U盘"],
|
||||
"outputOptions": ["XLR", "RCA", "XLR/RCA", "耳机"],
|
||||
"input": { "coaxial": "同轴", "optical": "光纤", "bluetooth": "蓝牙", "rca": "模拟RCA" },
|
||||
"output": { "headset": "耳机" }
|
||||
},
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
import "@/lib/legacyBrowser";
|
||||
import { createRoot } from "react-dom/client";
|
||||
import App from "./App";
|
||||
import "./index.legacy.css";
|
||||
|
||||
createRoot(document.getElementById("root")!).render(<App />);
|
||||
@@ -1812,10 +1812,13 @@ export default function EQPage() {
|
||||
</div>
|
||||
|
||||
{/* ── Headphone model selector ── */}
|
||||
<div className="flex items-center justify-between px-1">
|
||||
<div className="flex min-w-0 items-center gap-2 px-1">
|
||||
<div
|
||||
ref={headphoneMenuRef}
|
||||
className="relative z-40 flex-1 mr-2 rounded-[12px] overflow-visible backdrop-blur-md transition-all"
|
||||
className={cn(
|
||||
"relative min-w-0 flex-1 max-w-[calc(100%-5.5rem)] rounded-[12px] overflow-visible backdrop-blur-md transition-all",
|
||||
isHeadphoneMenuOpen && "z-30",
|
||||
)}
|
||||
style={{
|
||||
background: "linear-gradient(180deg, rgba(36,38,42,0.92) 0%, rgba(24,26,30,0.92) 100%)",
|
||||
border: "1px solid rgba(0,255,246,0.52)",
|
||||
@@ -1863,7 +1866,7 @@ export default function EQPage() {
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="flex shrink-0 items-center gap-2">
|
||||
<button
|
||||
className="w-9 h-9 rounded-full flex items-center justify-center text-white/60 active:text-white transition-colors"
|
||||
style={{ background: "rgba(44,44,46,0.8)", border: "1px solid rgba(255,255,255,0.1)" }}
|
||||
@@ -1933,7 +1936,10 @@ export default function EQPage() {
|
||||
<span className="text-[14px] text-white/50 font-medium">{peqCardLabels.filters?.label ?? "滤波器"}</span>
|
||||
<div
|
||||
ref={filterMenuRef}
|
||||
className="relative z-40 w-[136px] rounded-[12px] overflow-visible backdrop-blur-md transition-all"
|
||||
className={cn(
|
||||
"relative w-[136px] rounded-[12px] overflow-visible backdrop-blur-md transition-all",
|
||||
isFilterMenuOpen && "z-30",
|
||||
)}
|
||||
style={{
|
||||
background: "linear-gradient(180deg, rgba(36,38,42,0.92) 0%, rgba(24,26,30,0.92) 100%)",
|
||||
border: "1px solid rgba(0,255,246,0.52)",
|
||||
|
||||
+53
-21
@@ -32,11 +32,17 @@ const INPUT_OPTION_DEFS: IOOptionDef[] = [
|
||||
{
|
||||
index: 0,
|
||||
icon: (
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" className="w-7 h-7">
|
||||
<rect x="8" y="6" width="8" height="12" rx="1.5" />
|
||||
<line x1="10" y1="6" x2="10" y2="4" />
|
||||
<line x1="14" y1="6" x2="14" y2="4" />
|
||||
</svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="2em" height="2em" viewBox="0 0 24 24">
|
||||
<path d="M0 0h24v24H0z" fill="none" />
|
||||
<g fill="none">
|
||||
<path d="M3 12h18v9H3zm2-9h14v9H5z" />
|
||||
<g stroke="currentColor" stroke-width="2">
|
||||
<path d="M3 12h18v9H3zm16 0H5V3h14z" />
|
||||
<path d="M9 7.496h.004V7.5H9zm6 0h.004V7.5H15z" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
),
|
||||
},
|
||||
{
|
||||
@@ -76,15 +82,41 @@ const INPUT_OPTION_DEFS: IOOptionDef[] = [
|
||||
},
|
||||
{
|
||||
index: 5,
|
||||
icon: (
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="2em" height="2em" viewBox="0 0 16 16">
|
||||
<path d="M0 0h16v16H0z" fill="none" />
|
||||
<g fill="currentColor">
|
||||
<path d="M2.5 7a.5.5 0 0 0 0 1h11a.5.5 0 0 0 0-1z" />
|
||||
<path d="M1 5a1 1 0 0 0-1 1v3a1 1 0 0 0 1 1h.293l.707.707a1 1 0 0 0 .707.293h10.586a1 1 0 0 0 .707-.293l.707-.707H15a1 1 0 0 0 1-1V6a1 1 0 0 0-1-1zm0 1h14v3h-.293a1 1 0 0 0-.707.293l-.707.707H2.707L2 9.293A1 1 0 0 0 1.293 9H1z" />
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
),
|
||||
},
|
||||
{
|
||||
index: 6,
|
||||
icon: (
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" className="w-7 h-7">
|
||||
<rect x="3" y="8" width="18" height="10" rx="1.5" />
|
||||
<line x1="7" y1="8" x2="7" y2="18" />
|
||||
<line x1="12" y1="8" x2="12" y2="18" />
|
||||
<line x1="17" y1="8" x2="17" y2="18" />
|
||||
<circle cx="12" cy="12" r="8" />
|
||||
<circle cx="12" cy="12" r="3" fill="currentColor" stroke="none" />
|
||||
</svg>
|
||||
),
|
||||
},
|
||||
{
|
||||
index: 7,
|
||||
icon: (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="2em" height="2em" viewBox="0 0 24 24">
|
||||
<path d="M0 0h24v24H0z" fill="none" />
|
||||
<g fill="currentColor">
|
||||
<path d="M10 4.5h1v2h-1zm4 0h-1v2h1z" />
|
||||
<path fill-rule="evenodd" d="M7 8.5v-7h10v7h2v11a3 3 0 0 1-3 3H8a3 3 0 0 1-3-3v-11zm2-5h6v5H9zm8 7H7v9a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1z" clip-rule="evenodd" />
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
const OUTPUT_OPTION_DEFS: IOOptionDef[] = [
|
||||
@@ -108,16 +140,6 @@ const OUTPUT_OPTION_DEFS: IOOptionDef[] = [
|
||||
</svg>
|
||||
),
|
||||
},
|
||||
{
|
||||
index: 2,
|
||||
icon: (
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" className="w-7 h-7">
|
||||
<path d="M3 18v-6a9 9 0 0 1 18 0v6" />
|
||||
<path d="M21 19a2 2 0 0 1-2 2h-1a2 2 0 0 1-2-2v-3a2 2 0 0 1 2-2h3z" />
|
||||
<path d="M3 19a2 2 0 0 0 2 2h1a2 2 0 0 0 2-2v-3a2 2 0 0 0-2-2H3z" />
|
||||
</svg>
|
||||
),
|
||||
},
|
||||
{
|
||||
index: 3,
|
||||
icon: (
|
||||
@@ -129,10 +151,20 @@ const OUTPUT_OPTION_DEFS: IOOptionDef[] = [
|
||||
</svg>
|
||||
),
|
||||
},
|
||||
{
|
||||
index: 2,
|
||||
icon: (
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" className="w-7 h-7">
|
||||
<path d="M3 18v-6a9 9 0 0 1 18 0v6" />
|
||||
<path d="M21 19a2 2 0 0 1-2 2h-1a2 2 0 0 1-2-2v-3a2 2 0 0 1 2-2h3z" />
|
||||
<path d="M3 19a2 2 0 0 0 2 2h1a2 2 0 0 0 2-2v-3a2 2 0 0 0-2-2H3z" />
|
||||
</svg>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
const FALLBACK_INPUT_LABELS = ["USB-B", "USB-C", "同轴", "光纤", "蓝牙", "IIS"];
|
||||
const FALLBACK_OUTPUT_LABELS = ["XLR", "RCA", "耳机", "XLR/RCA"];
|
||||
const FALLBACK_INPUT_LABELS = ["USB-B", "USB-C", "同轴", "光纤", "蓝牙", "HDMI-EARC", "RCA", "USB Driver"];
|
||||
const FALLBACK_OUTPUT_LABELS = ["XLR", "RCA", "XLR/RCA", "耳机"];
|
||||
|
||||
function withLabels(defs: IOOptionDef[], labels: string[] | undefined, fallback: string[]): IOOption[] {
|
||||
return defs.map((def, i) => ({
|
||||
|
||||
Reference in New Issue
Block a user