适配b9
@@ -3,7 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1" />
|
||||||
<title>Luxsin X9 Controller</title>
|
<title>Luxsin B9 Controller</title>
|
||||||
<style>
|
<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; }
|
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; }
|
.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; }
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1" />
|
||||||
<title>Luxsin X9 Controller (Legacy)</title>
|
<title>Luxsin B9 Controller (Legacy)</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1" />
|
||||||
<title>Luxsin X9 Controller</title>
|
<title>Luxsin B9 Controller</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 195 KiB After Width: | Height: | Size: 208 KiB |
|
Before Width: | Height: | Size: 166 KiB After Width: | Height: | Size: 54 KiB |
|
Before Width: | Height: | Size: 88 KiB After Width: | Height: | Size: 386 KiB |
|
Before Width: | Height: | Size: 90 KiB After Width: | Height: | Size: 60 KiB |
|
Before Width: | Height: | Size: 90 KiB After Width: | Height: | Size: 101 KiB |
|
Before Width: | Height: | Size: 141 KiB After Width: | Height: | Size: 90 KiB |
|
After Width: | Height: | Size: 141 KiB |
|
Before Width: | Height: | Size: 208 KiB After Width: | Height: | Size: 195 KiB |
|
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 166 KiB |
|
Before Width: | Height: | Size: 386 KiB After Width: | Height: | Size: 88 KiB |
|
Before Width: | Height: | Size: 60 KiB After Width: | Height: | Size: 90 KiB |
@@ -11,7 +11,6 @@ import Home from "./pages/Home";
|
|||||||
import NotFound from "./pages/NotFound";
|
import NotFound from "./pages/NotFound";
|
||||||
import Dashboard from "./pages/Dashboard";
|
import Dashboard from "./pages/Dashboard";
|
||||||
import AudioPage from "./pages/AudioPage";
|
import AudioPage from "./pages/AudioPage";
|
||||||
import BluetoothPage from "./pages/BluetoothPage";
|
|
||||||
import SystemPage from "./pages/SystemPage";
|
import SystemPage from "./pages/SystemPage";
|
||||||
import IOPage from "./pages/IOPage";
|
import IOPage from "./pages/IOPage";
|
||||||
import VUPage from "./pages/VUPage";
|
import VUPage from "./pages/VUPage";
|
||||||
@@ -61,7 +60,6 @@ function AppRouter() {
|
|||||||
<Route path="/audio" component={AudioPage} />
|
<Route path="/audio" component={AudioPage} />
|
||||||
<Route path="/eq" component={EQPageRoute} />
|
<Route path="/eq" component={EQPageRoute} />
|
||||||
<Route path="/effects" component={EffectsPageRoute} />
|
<Route path="/effects" component={EffectsPageRoute} />
|
||||||
<Route path="/bluetooth" component={BluetoothPage} />
|
|
||||||
<Route path="/system" component={SystemPage} />
|
<Route path="/system" component={SystemPage} />
|
||||||
<Route path="/io" component={IOPage} />
|
<Route path="/io" component={IOPage} />
|
||||||
<Route path="/vu" component={VUPage} />
|
<Route path="/vu" component={VUPage} />
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
import { useDevice } from "@/contexts/DeviceContext";
|
import { useDevice } from "@/contexts/DeviceContext";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import {
|
import {
|
||||||
Bluetooth,
|
|
||||||
ChevronRight,
|
ChevronRight,
|
||||||
Gauge,
|
Gauge,
|
||||||
Layers,
|
Layers,
|
||||||
@@ -27,7 +26,6 @@ const NAV_ITEMS: NavItem[] = [
|
|||||||
{ path: "/audio", icon: Volume2, label: "Audio" },
|
{ path: "/audio", icon: Volume2, label: "Audio" },
|
||||||
{ path: "/eq", icon: Sliders, label: "Parametric EQ" },
|
{ path: "/eq", icon: Sliders, label: "Parametric EQ" },
|
||||||
{ path: "/effects", icon: Layers, label: "Effects" },
|
{ path: "/effects", icon: Layers, label: "Effects" },
|
||||||
{ path: "/bluetooth", icon: Bluetooth, label: "Bluetooth" },
|
|
||||||
{ path: "/system", icon: Settings, label: "System" },
|
{ path: "/system", icon: Settings, label: "System" },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -257,7 +257,7 @@ export interface PeqSoftPayload {
|
|||||||
// ============================================================
|
// ============================================================
|
||||||
// Input/Output Labels
|
// Input/Output Labels
|
||||||
// ============================================================
|
// ============================================================
|
||||||
export const INPUT_LABELS = ["USB", "USB-C", "Coaxial", "Optical", "Bluetooth", "HDMI-EARC", "RCA"];
|
export const INPUT_LABELS = ["USB", "USB-C", "Coaxial", "Optical", "Bluetooth", "IIS", "RCA"];
|
||||||
export const OUTPUT_LABELS = ["XLR", "RCA", "Headset", "XLR/RCA"];
|
export const OUTPUT_LABELS = ["XLR", "RCA", "Headset", "XLR/RCA"];
|
||||||
|
|
||||||
/** Device `input` index for Bluetooth source. */
|
/** Device `input` index for Bluetooth source. */
|
||||||
|
|||||||
@@ -55,7 +55,7 @@
|
|||||||
"pageTitle": "I/O",
|
"pageTitle": "I/O",
|
||||||
"inputSection": "Eingang",
|
"inputSection": "Eingang",
|
||||||
"outputSection": "Ausgang",
|
"outputSection": "Ausgang",
|
||||||
"inputOptions": ["USB-B", "USB-C", "Koaxial", "Optisch", "Bluetooth", "HDMI-EARC", "RCA", "USB Driver"],
|
"inputOptions": ["USB-B", "USB-C", "Koaxial", "Optisch", "Bluetooth", "IIS", "RCA", "USB Driver"],
|
||||||
"outputOptions": ["XLR", "RCA", "XLR/RCA", "Kopfhörer"],
|
"outputOptions": ["XLR", "RCA", "XLR/RCA", "Kopfhörer"],
|
||||||
"input": {
|
"input": {
|
||||||
"coaxial": "KOAXIAL",
|
"coaxial": "KOAXIAL",
|
||||||
|
|||||||
@@ -55,7 +55,7 @@
|
|||||||
"pageTitle": "I/O",
|
"pageTitle": "I/O",
|
||||||
"inputSection": "Input",
|
"inputSection": "Input",
|
||||||
"outputSection": "Output",
|
"outputSection": "Output",
|
||||||
"inputOptions": ["USB-B", "USB-C", "Coaxial", "Optical", "Bluetooth", "HDMI-EARC", "RCA", "USB Driver"],
|
"inputOptions": ["USB-B", "USB-C", "Coaxial", "Optical", "Bluetooth", "IIS", "RCA", "USB Driver"],
|
||||||
"outputOptions": ["XLR", "RCA", "XLR/RCA", "Headset"],
|
"outputOptions": ["XLR", "RCA", "XLR/RCA", "Headset"],
|
||||||
"input": {
|
"input": {
|
||||||
"coaxial": "COAXIAL",
|
"coaxial": "COAXIAL",
|
||||||
|
|||||||
@@ -55,7 +55,7 @@
|
|||||||
"pageTitle": "E/S",
|
"pageTitle": "E/S",
|
||||||
"inputSection": "Entrada",
|
"inputSection": "Entrada",
|
||||||
"outputSection": "Salida",
|
"outputSection": "Salida",
|
||||||
"inputOptions": ["USB-B", "USB-C", "Coaxial", "Óptico", "Bluetooth", "HDMI-EARC", "RCA", "USB Driver"],
|
"inputOptions": ["USB-B", "USB-C", "Coaxial", "Óptico", "Bluetooth", "IIS", "RCA", "USB Driver"],
|
||||||
"outputOptions": ["XLR", "RCA", "XLR/RCA", "Auriculares"],
|
"outputOptions": ["XLR", "RCA", "XLR/RCA", "Auriculares"],
|
||||||
"input": {
|
"input": {
|
||||||
"coaxial": "COAXIAL",
|
"coaxial": "COAXIAL",
|
||||||
|
|||||||
@@ -55,7 +55,7 @@
|
|||||||
"pageTitle": "E/S",
|
"pageTitle": "E/S",
|
||||||
"inputSection": "Entrée",
|
"inputSection": "Entrée",
|
||||||
"outputSection": "Sortie",
|
"outputSection": "Sortie",
|
||||||
"inputOptions": ["USB-B", "USB-C", "Coaxial", "Optique", "Bluetooth", "HDMI-EARC", "RCA", "USB Driver"],
|
"inputOptions": ["USB-B", "USB-C", "Coaxial", "Optique", "Bluetooth", "IIS", "RCA", "USB Driver"],
|
||||||
"outputOptions": ["XLR", "RCA", "XLR/RCA", "Casque"],
|
"outputOptions": ["XLR", "RCA", "XLR/RCA", "Casque"],
|
||||||
"input": {
|
"input": {
|
||||||
"coaxial": "COAXIAL",
|
"coaxial": "COAXIAL",
|
||||||
|
|||||||
@@ -55,7 +55,7 @@
|
|||||||
"pageTitle": "輸入/輸出",
|
"pageTitle": "輸入/輸出",
|
||||||
"inputSection": "輸入",
|
"inputSection": "輸入",
|
||||||
"outputSection": "輸出",
|
"outputSection": "輸出",
|
||||||
"inputOptions": ["USB-B", "USB-C", "同軸", "光纖", "藍牙", "HDMI-EARC", "類比RCA", "USB隨身碟"],
|
"inputOptions": ["USB-B", "USB-C", "同軸", "光纖", "藍牙", "IIS", "類比RCA", "USB隨身碟"],
|
||||||
"outputOptions": ["XLR", "RCA", "XLR/RCA", "耳機"],
|
"outputOptions": ["XLR", "RCA", "XLR/RCA", "耳機"],
|
||||||
"input": { "coaxial": "同軸", "optical": "光纖", "bluetooth": "藍牙", "rca": "類比RCA" },
|
"input": { "coaxial": "同軸", "optical": "光纖", "bluetooth": "藍牙", "rca": "類比RCA" },
|
||||||
"output": { "headset": "耳機" }
|
"output": { "headset": "耳機" }
|
||||||
|
|||||||
@@ -55,7 +55,7 @@
|
|||||||
"pageTitle": "输入输出",
|
"pageTitle": "输入输出",
|
||||||
"inputSection": "输入",
|
"inputSection": "输入",
|
||||||
"outputSection": "输出",
|
"outputSection": "输出",
|
||||||
"inputOptions": ["USB-B", "USB-C", "同轴", "光纤", "蓝牙", "HDMI-EARC", "模拟RCA", "U盘"],
|
"inputOptions": ["USB-B", "USB-C", "同轴", "光纤", "蓝牙", "IIS", "模拟RCA", "U盘"],
|
||||||
"outputOptions": ["XLR", "RCA", "XLR/RCA", "耳机"],
|
"outputOptions": ["XLR", "RCA", "XLR/RCA", "耳机"],
|
||||||
"input": { "coaxial": "同轴", "optical": "光纤", "bluetooth": "蓝牙", "rca": "模拟RCA" },
|
"input": { "coaxial": "同轴", "optical": "光纤", "bluetooth": "蓝牙", "rca": "模拟RCA" },
|
||||||
"output": { "headset": "耳机" }
|
"output": { "headset": "耳机" }
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
Sections:
|
Sections:
|
||||||
- 平衡与灵敏度: 左右平衡, VU灵敏度, 模拟输入增益 sliders
|
- 平衡与灵敏度: 左右平衡, VU灵敏度, 模拟输入增益 sliders
|
||||||
- 输出特性: 滤波特性, 耳机增益, 音量幅度
|
- 输出特性: 滤波特性, 耳机增益, 音量幅度
|
||||||
|
- IIS设置: IIS静音电平, IIS模式
|
||||||
- 系统与接口: 开机音量, XLR极性
|
- 系统与接口: 开机音量, XLR极性
|
||||||
All list rows → navigate to /select page
|
All list rows → navigate to /select page
|
||||||
============================================================ */
|
============================================================ */
|
||||||
@@ -107,6 +108,7 @@ type LocaleDac = {
|
|||||||
dac?: {
|
dac?: {
|
||||||
sectionBalanceSensitivity?: string;
|
sectionBalanceSensitivity?: string;
|
||||||
sectionOutput?: string;
|
sectionOutput?: string;
|
||||||
|
sectionIisSettings?: string;
|
||||||
sectionSystemInterface?: string;
|
sectionSystemInterface?: string;
|
||||||
balance?: string;
|
balance?: string;
|
||||||
sensitivity?: string;
|
sensitivity?: string;
|
||||||
@@ -119,6 +121,8 @@ type LocaleDac = {
|
|||||||
volumeStep?: string;
|
volumeStep?: string;
|
||||||
bootVolume?: string | { label?: string; options?: Array<{ index: number; label: string }> };
|
bootVolume?: string | { label?: string; options?: Array<{ index: number; label: string }> };
|
||||||
xlr?: { label?: string; options?: Array<{ index: number; label: string }> };
|
xlr?: { label?: string; options?: Array<{ index: number; label: string }> };
|
||||||
|
mutePolar?: { label?: string; options?: Array<{ index: number; label: string }> };
|
||||||
|
IISMode?: { label?: string; options?: Array<{ index: number; label: string }> };
|
||||||
};
|
};
|
||||||
home?: { audioSet?: string };
|
home?: { audioSet?: string };
|
||||||
};
|
};
|
||||||
@@ -222,6 +226,8 @@ export default function AudioPage() {
|
|||||||
const stepIdx = ds?.soundStep ?? 1;
|
const stepIdx = ds?.soundStep ?? 1;
|
||||||
const filterIdx = ds?.pcm ?? 0;
|
const filterIdx = ds?.pcm ?? 0;
|
||||||
const xlrIdx = ds?.xlr ?? 0;
|
const xlrIdx = ds?.xlr ?? 0;
|
||||||
|
const mutePolarIdx = (ds as ({ hdmimutepolar?: number } | null))?.hdmimutepolar ?? 0;
|
||||||
|
const iisModeIdx = (ds as ({ hdmiType?: number } | null))?.hdmiType ?? 0;
|
||||||
const bootSoundVal = normalizeBootSound(localBootSound);
|
const bootSoundVal = normalizeBootSound(localBootSound);
|
||||||
const firmwareVersion = parseFirmwareVersion(ds?.version);
|
const firmwareVersion = parseFirmwareVersion(ds?.version);
|
||||||
const bootSoundMaxIdx = getBootSoundMaxIndex(firmwareVersion);
|
const bootSoundMaxIdx = getBootSoundMaxIndex(firmwareVersion);
|
||||||
@@ -233,6 +239,8 @@ export default function AudioPage() {
|
|||||||
const DAC_VOLUME_DIRECT_OPTIONS = optionLabels(dacText.dacVolumeDirect?.options, ["关闭", "0dB", "-12dB"]);
|
const DAC_VOLUME_DIRECT_OPTIONS = optionLabels(dacText.dacVolumeDirect?.options, ["关闭", "0dB", "-12dB"]);
|
||||||
const DAC_ARC_OPTIONS = optionLabels(dacText.dacArc?.options, ["ARC", "EARC"]);
|
const DAC_ARC_OPTIONS = optionLabels(dacText.dacArc?.options, ["ARC", "EARC"]);
|
||||||
const XLR_OPTIONS = optionLabels(dacText.xlr?.options, ["正常", "反转"]);
|
const XLR_OPTIONS = optionLabels(dacText.xlr?.options, ["正常", "反转"]);
|
||||||
|
const MUTE_POLAR_OPTIONS = optionLabels(dacText.mutePolar?.options, ["低电平", "高电平"]);
|
||||||
|
const IIS_MODE_OPTIONS = optionLabels(dacText.IISMode?.options, ["模式1", "模式2", "模式3", "模式4", "模式5", "模式6", "模式7", "模式8"]);
|
||||||
|
|
||||||
const pageTitle = usePageTitleWithConnection(localeData.home?.audioSet ?? "Audio");
|
const pageTitle = usePageTitleWithConnection(localeData.home?.audioSet ?? "Audio");
|
||||||
|
|
||||||
@@ -380,6 +388,21 @@ export default function AudioPage() {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* ── IIS 设置 ── */}
|
||||||
|
<SectionHeader title={dacText.sectionIisSettings ?? "IIS设置"} />
|
||||||
|
<div className="ios-list-group">
|
||||||
|
<ListRow
|
||||||
|
label={dacText.mutePolar?.label ?? "IIS 静音电平"}
|
||||||
|
value={MUTE_POLAR_OPTIONS[mutePolarIdx] ?? MUTE_POLAR_OPTIONS[0] ?? "—"}
|
||||||
|
onClick={() => goSelect(dacText.mutePolar?.label ?? "选择 IIS 静音电平", MUTE_POLAR_OPTIONS, mutePolarIdx, "mutePolar")}
|
||||||
|
/>
|
||||||
|
<ListRow
|
||||||
|
label={dacText.IISMode?.label ?? "IIS 模式"}
|
||||||
|
value={IIS_MODE_OPTIONS[iisModeIdx] ?? IIS_MODE_OPTIONS[0] ?? "—"}
|
||||||
|
onClick={() => goSelect(dacText.IISMode?.label ?? "选择 IIS 模式", IIS_MODE_OPTIONS, iisModeIdx, "IISMode")}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* ── 系统与接口 ── */}
|
{/* ── 系统与接口 ── */}
|
||||||
<SectionHeader title={dacText.sectionSystemInterface ?? "系统与接口"} />
|
<SectionHeader title={dacText.sectionSystemInterface ?? "系统与接口"} />
|
||||||
<div className="ios-list-group">
|
<div className="ios-list-group">
|
||||||
|
|||||||
@@ -1,148 +0,0 @@
|
|||||||
/* ============================================================
|
|
||||||
BLUETOOTH PAGE — BT status, transport controls
|
|
||||||
============================================================ */
|
|
||||||
import Layout from "@/components/Layout";
|
|
||||||
import { useDevice } from "@/contexts/DeviceContext";
|
|
||||||
import { cn } from "@/lib/utils";
|
|
||||||
import { BT_STATUS_LABELS } from "@/lib/luxsinApi";
|
|
||||||
import { Bluetooth, Music, SkipBack, SkipForward, Volume2 } from "lucide-react";
|
|
||||||
|
|
||||||
export default function BluetoothPage() {
|
|
||||||
const { deviceState, updateSetting } = useDevice();
|
|
||||||
|
|
||||||
if (!deviceState) {
|
|
||||||
return (
|
|
||||||
<Layout title="Bluetooth" subtitle="Bluetooth audio source">
|
|
||||||
<div className="glass-card p-8 text-center text-white/30">Connect to device</div>
|
|
||||||
</Layout>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const { bt_status, bt_title, bt_artist, bt_srcname } = deviceState;
|
|
||||||
const isPlaying = bt_status === 1;
|
|
||||||
const isConnected = bt_status > 0;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Layout title="Bluetooth" subtitle="Bluetooth audio source">
|
|
||||||
<div className="max-w-2xl mx-auto space-y-4">
|
|
||||||
|
|
||||||
{/* Status card */}
|
|
||||||
<div className={cn(
|
|
||||||
"glass-card p-6 animate-fade-in-up transition-all duration-300",
|
|
||||||
isConnected && "glass-cyan"
|
|
||||||
)}>
|
|
||||||
<div className="flex items-center gap-4 mb-6">
|
|
||||||
<div className={cn(
|
|
||||||
"w-14 h-14 rounded-2xl flex items-center justify-center",
|
|
||||||
isConnected ? "glass-cyan" : "bg-white/5"
|
|
||||||
)}>
|
|
||||||
<Bluetooth size={24} className={isConnected ? "text-[#00FFF6]" : "text-white/30"} />
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<h2 className={cn(
|
|
||||||
"text-lg font-bold",
|
|
||||||
isConnected ? "text-[#00FFF6]" : "text-white/50"
|
|
||||||
)} style={{ fontFamily: "'Space Grotesk', sans-serif" }}>
|
|
||||||
{isConnected ? bt_srcname || "Connected" : "Not Connected"}
|
|
||||||
</h2>
|
|
||||||
<p className="text-white/40 text-sm">{BT_STATUS_LABELS[bt_status]}</p>
|
|
||||||
</div>
|
|
||||||
{isConnected && <div className="ml-auto dot-active" />}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{isConnected ? (
|
|
||||||
<>
|
|
||||||
{/* Track info */}
|
|
||||||
<div className="glass-card p-4 mb-5 rounded-[16px]">
|
|
||||||
<div className="flex items-center gap-3">
|
|
||||||
<div className="w-10 h-10 rounded-[10px] bg-white/5 flex items-center justify-center flex-shrink-0">
|
|
||||||
<Music size={16} className="text-white/30" />
|
|
||||||
</div>
|
|
||||||
<div className="flex-1 min-w-0">
|
|
||||||
<p className="text-white font-semibold text-sm truncate"
|
|
||||||
style={{ fontFamily: "'Space Grotesk', sans-serif" }}>
|
|
||||||
{bt_title || "Unknown Track"}
|
|
||||||
</p>
|
|
||||||
<p className="text-white/50 text-xs truncate mt-0.5">
|
|
||||||
{bt_artist || "Unknown Artist"}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
{isPlaying && (
|
|
||||||
<div className="flex items-end gap-0.5 h-5 flex-shrink-0">
|
|
||||||
{[1, 2, 3, 4].map(i => (
|
|
||||||
<div
|
|
||||||
key={i}
|
|
||||||
className="w-1 rounded-sm bg-[#00FFF6]"
|
|
||||||
style={{
|
|
||||||
height: `${30 + Math.random() * 70}%`,
|
|
||||||
animation: `vu-pulse ${0.3 + i * 0.1}s ease-in-out infinite alternate`,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Transport controls */}
|
|
||||||
<div className="flex items-center justify-center gap-6">
|
|
||||||
<button
|
|
||||||
onClick={() => updateSetting({ bt_next: 0 })}
|
|
||||||
className="btn-glass w-12 h-12 flex items-center justify-center"
|
|
||||||
>
|
|
||||||
<SkipBack size={18} className="text-white/60" />
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<button
|
|
||||||
onClick={() => updateSetting({ bt_play: 1 })}
|
|
||||||
className={cn(
|
|
||||||
"w-16 h-16 rounded-full flex items-center justify-center transition-all duration-200",
|
|
||||||
isPlaying ? "btn-cyan" : "btn-glass"
|
|
||||||
)}
|
|
||||||
style={isPlaying ? {
|
|
||||||
boxShadow: "0 0 30px rgba(0,255,246,0.4)"
|
|
||||||
} : {}}
|
|
||||||
>
|
|
||||||
{isPlaying ? (
|
|
||||||
<div className="flex gap-1">
|
|
||||||
<div className="w-1.5 h-5 bg-current rounded-sm" />
|
|
||||||
<div className="w-1.5 h-5 bg-current rounded-sm" />
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div className="w-0 h-0 border-l-[14px] border-l-current border-y-[9px] border-y-transparent ml-1" />
|
|
||||||
)}
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<button
|
|
||||||
onClick={() => updateSetting({ bt_next: 1 })}
|
|
||||||
className="btn-glass w-12 h-12 flex items-center justify-center"
|
|
||||||
>
|
|
||||||
<SkipForward size={18} className="text-white/60" />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<div className="flex flex-col items-center py-8 text-white/30">
|
|
||||||
<Bluetooth size={40} className="mb-3 opacity-20" />
|
|
||||||
<p className="text-sm">No Bluetooth device connected</p>
|
|
||||||
<p className="text-xs mt-1 opacity-60">Switch input to Bluetooth to use this feature</p>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Volume info */}
|
|
||||||
{isConnected && (
|
|
||||||
<div className="glass-card p-4 flex items-center gap-3 animate-fade-in-up" style={{ animationDelay: "60ms" }}>
|
|
||||||
<Volume2 size={14} className="text-[#00FFF6]" />
|
|
||||||
<span className="text-white/60 text-sm flex-1">Current Volume</span>
|
|
||||||
<span className="text-[#00FFF6] font-bold text-lg"
|
|
||||||
style={{ fontFamily: "'JetBrains Mono', monospace" }}>
|
|
||||||
{deviceState.volume}
|
|
||||||
</span>
|
|
||||||
<span className="text-white/30 text-xs">/ 200</span>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</Layout>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -36,7 +36,6 @@ import {
|
|||||||
Sliders,
|
Sliders,
|
||||||
Activity,
|
Activity,
|
||||||
Gauge,
|
Gauge,
|
||||||
Undo2,
|
|
||||||
Bluetooth,
|
Bluetooth,
|
||||||
SkipBack,
|
SkipBack,
|
||||||
SkipForward,
|
SkipForward,
|
||||||
@@ -635,7 +634,7 @@ export default function Home() {
|
|||||||
</div>
|
</div>
|
||||||
{/* Brand label */}
|
{/* Brand label */}
|
||||||
<div className="absolute top-5 left-8">
|
<div className="absolute top-5 left-8">
|
||||||
<span className="text-[11px] font-bold tracking-widest text-white/30">LUXSIN</span>
|
<span className="text-[11px] font-bold tracking-widest text-white/30">{deviceState?.device ?? "LUXSIN"}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -965,15 +964,6 @@ export default function Home() {
|
|||||||
thumbnail={`${import.meta.env.BASE_URL}images/vu/vu${(ds.vu ?? 0) + 1}.png`}
|
thumbnail={`${import.meta.env.BASE_URL}images/vu/vu${(ds.vu ?? 0) + 1}.png`}
|
||||||
onClick={() => setLocation("/vu")}
|
onClick={() => setLocation("/vu")}
|
||||||
/>
|
/>
|
||||||
<ListRow
|
|
||||||
icon={<Undo2 size={18} />}
|
|
||||||
label={homeText.backToLegacy ?? "返回旧版"}
|
|
||||||
onClick={() => {
|
|
||||||
const target = ip.trim();
|
|
||||||
if (!target) return;
|
|
||||||
window.location.href = `https://am.luxsinaudio.com/i.html?ip=${encodeURIComponent(target)}`;
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -164,7 +164,7 @@ const OUTPUT_OPTION_DEFS: IOOptionDef[] = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const FALLBACK_INPUT_LABELS = ["USB-B", "USB-C", "同轴", "光纤", "蓝牙", "HDMI-EARC", "RCA", "USB Driver"];
|
const FALLBACK_INPUT_LABELS = ["USB-B", "USB-C", "同轴", "光纤", "蓝牙", "IIS", "RCA", "USB Driver"];
|
||||||
const FALLBACK_OUTPUT_LABELS = ["XLR", "RCA", "XLR/RCA", "耳机"];
|
const FALLBACK_OUTPUT_LABELS = ["XLR", "RCA", "XLR/RCA", "耳机"];
|
||||||
|
|
||||||
function withLabels(defs: IOOptionDef[], labels: string[] | undefined, fallback: string[]): IOOption[] {
|
function withLabels(defs: IOOptionDef[], labels: string[] | undefined, fallback: string[]): IOOption[] {
|
||||||
|
|||||||