From 8db56e2211f290dda88dfee3e630e49e780a63ee Mon Sep 17 00:00:00 2001 From: eafonyang Date: Wed, 9 Sep 2026 18:26:24 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D&=E4=BC=98=E5=8C=96=E5=90=AC?= =?UTF-8?q?=E5=8A=9B=E8=A1=A5=E5=81=BF=E6=A8=A1=E5=9D=97=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/App.tsx | 28 +++++++++++++++++-- .../components/HearingCompensationChart.tsx | 19 +++++++++---- client/src/lib/hearingCurve.ts | 18 +++++++++++- client/src/pages/EffectsPage.tsx | 2 +- client/src/pages/SelectPage.tsx | 13 +++++++++ scripts/deploy.sh | 4 +++ 6 files changed, 73 insertions(+), 11 deletions(-) diff --git a/client/src/App.tsx b/client/src/App.tsx index aaa8f8d..7c7227b 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -1,7 +1,7 @@ -import { lazy, Suspense } from "react"; +import { lazy, Suspense, useEffect, useRef } from "react"; import { Toaster } from "@/components/ui/sonner"; import { TooltipProvider } from "@/components/ui/tooltip"; -import { Route, Switch, Router } from "wouter"; +import { Route, Switch, Router, useLocation } from "wouter"; import { useHashLocation } from "wouter/use-hash-location"; import ErrorBoundary from "./components/ErrorBoundary"; import PageLoader from "./components/PageLoader"; @@ -15,7 +15,7 @@ import BluetoothPage from "./pages/BluetoothPage"; import SystemPage from "./pages/SystemPage"; import IOPage from "./pages/IOPage"; import VUPage from "./pages/VUPage"; -import SelectPage from "./pages/SelectPage"; +import SelectPage, { consumeSelectReturnScroll } from "./pages/SelectPage"; import CommunityPage from "./pages/CommunityPage"; import ChangelogPage from "./pages/ChangelogPage"; import { AIDrawerProvider } from "./contexts/AIDrawerContext"; @@ -54,6 +54,27 @@ function EffectsPageRoute() { ); } +/** + * Restores the calling page's scroll position when returning from /select. + * The select page is short, so the browser clamps window scroll to 0 while on + * it; navigateToSelect() captures the offset before navigating and we re-apply + * it once the calling page (e.g. Effects) has remounted. + */ +function SelectScrollRestore() { + const [location] = useLocation(); + const prevRef = useRef(location); + useEffect(() => { + const prev = prevRef.current; + prevRef.current = location; + if (prev !== "/select" || location === "/select") return; + const y = consumeSelectReturnScroll(); + if (y == null || y <= 0) return; + // Wait a frame so the returning page has rendered and the document has height. + requestAnimationFrame(() => window.scrollTo(0, y)); + }, [location]); + return null; +} + function AppRouter() { return ( @@ -85,6 +106,7 @@ function App() { +
diff --git a/client/src/components/HearingCompensationChart.tsx b/client/src/components/HearingCompensationChart.tsx index 0cd46d7..ef55262 100644 --- a/client/src/components/HearingCompensationChart.tsx +++ b/client/src/components/HearingCompensationChart.tsx @@ -1,8 +1,8 @@ import { useEffect, useMemo, useRef } from "react"; import * as echarts from "echarts"; import { - HEARING_FREQ_LABELS, buildHearingSplineSeries, + resolveHearingFreqLabels, } from "@/lib/hearingCurve"; const LEFT_COLOR = "rgb(0, 255, 246)"; @@ -29,6 +29,11 @@ export default function HearingCompensationChart({ const leftSeries = useMemo(() => buildHearingSplineSeries(left), [left]); const rightSeries = useMemo(() => buildHearingSplineSeries(right), [right]); + // X axis adapts to the control-point count: 7-band (legacy) or 10-band (adds 3k/5k/6k). + const pointCount = Math.max(left.length, right.length); + const freqLabels = useMemo(() => resolveHearingFreqLabels(pointCount), [pointCount]); + const xMax = Math.max(0, pointCount - 1); + useEffect(() => { const el = containerRef.current; if (!el) return; @@ -76,13 +81,13 @@ export default function HearingCompensationChart({ xAxis: { type: "value", min: 0, - max: HEARING_FREQ_LABELS.length - 1, + max: xMax, interval: 1, axisLabel: { formatter: (value: number) => { const idx = Math.round(value); if (Math.abs(value - idx) > 1e-6) return ""; - return HEARING_FREQ_LABELS[idx] ?? ""; + return freqLabels[idx] ?? ""; }, fontSize: 14, color: "#999", @@ -98,7 +103,9 @@ export default function HearingCompensationChart({ yAxis: { type: "value", min: -80, - max: 0, + // Top raised to +20 dB: levels above 70 map to positive dB and were + // clipped at the old max of 0, leaving gaps at the top of the curve. + max: 20, interval: 10, name: "dB", nameLocation: "end", @@ -157,7 +164,7 @@ export default function HearingCompensationChart({ ro.disconnect(); window.removeEventListener("resize", onResize); }; - }, [leftSeries, rightSeries, leftLabel, rightLabel]); + }, [leftSeries, rightSeries, leftLabel, rightLabel, freqLabels, xMax]); useEffect(() => { return () => { @@ -171,7 +178,7 @@ export default function HearingCompensationChart({ ref={containerRef} className={ className - ?? "w-full h-[min(42vw,200px)] min-h-[140px] md:h-[260px] lg:h-[300px] rounded-lg bg-black/30" + ?? "w-full h-[min(52vw,240px)] min-h-[180px] md:h-[320px] lg:h-[380px] rounded-lg bg-black/30" } aria-hidden /> diff --git a/client/src/lib/hearingCurve.ts b/client/src/lib/hearingCurve.ts index 44b6747..afa79f3 100644 --- a/client/src/lib/hearingCurve.ts +++ b/client/src/lib/hearingCurve.ts @@ -1,7 +1,22 @@ /** Hearing compensation curve helpers: map levels → dB and cubic-spline smooth. */ +/** 7-band control points (legacy): 125/250/500/1k/2k/4k/8k. */ export const HEARING_FREQ_LABELS = ["125", "250", "500", "1k", "2k", "4k", "8k"] as const; +/** 10-band control points: adds 3k/5k/6k → 125/250/500/1k/2k/3k/4k/5k/6k/8k (ascending). */ +export const HEARING_FREQ_LABELS_10 = [ + "125", "250", "500", "1k", "2k", "3k", "4k", "5k", "6k", "8k", +] as const; + +/** + * Resolve the X-axis frequency labels for a given control-point `count`. + * 10 points → the extended 3k/5k/6k set; any other count → the legacy 7-band + * set. Keeps the chart compatible with both 7-point and 10-point hearing data. + */ +export function resolveHearingFreqLabels(count: number): readonly string[] { + return count === HEARING_FREQ_LABELS_10.length ? HEARING_FREQ_LABELS_10 : HEARING_FREQ_LABELS; +} + /** Y = n + (-70). Values outside [-80, 0] are kept as-is (axis still -80…0). */ export function mapHearingLevelToDb(n: number): number { return n - 70; @@ -92,7 +107,8 @@ export function createNaturalCubicSpline( /** * Build densely sampled [xIndex, dB] pairs for a smooth ECharts line. - * X uses equal spacing 0…6 matching HEARING_FREQ_LABELS. + * X uses equal spacing 0…N-1 where N = `levels.length`, so it adapts to both + * 7-point and 10-point hearing data (see `resolveHearingFreqLabels`). */ export function buildHearingSplineSeries( levels: number[], diff --git a/client/src/pages/EffectsPage.tsx b/client/src/pages/EffectsPage.tsx index f26a4d0..eddd9e3 100644 --- a/client/src/pages/EffectsPage.tsx +++ b/client/src/pages/EffectsPage.tsx @@ -917,7 +917,7 @@ export default function EffectsPage() { {hearingOn && hearingProfiles[hearingIdx] && (