diff --git a/client/public/changelog.md b/client/public/changelog.md new file mode 100644 index 0000000..aae8ec8 --- /dev/null +++ b/client/public/changelog.md @@ -0,0 +1,10 @@ +# What's New + +> This document records feature updates and bug fixes for the Luxsin X9 controller. A new entry is appended here after each release. + +## 2026.08.06 + +### New Features +- Added a "What's New" entry on the home page, allowing you to view release notes online. +- Added Undo support on the EQ page: tap the Undo button to revert your most recent change. It covers band parameter edits (frequency / gain / Q / filter type), slider drag adjustments, preamp and auto-preamp changes, A/B mode copy & switch, and batch edits. Each undo shows a toast describing exactly what was reverted (e.g. "A Band 3 GAIN +2.0 → 0.0"). + diff --git a/client/src/App.tsx b/client/src/App.tsx index 33e4af8..aaa8f8d 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -17,6 +17,7 @@ import IOPage from "./pages/IOPage"; import VUPage from "./pages/VUPage"; import SelectPage from "./pages/SelectPage"; import CommunityPage from "./pages/CommunityPage"; +import ChangelogPage from "./pages/ChangelogPage"; import { AIDrawerProvider } from "./contexts/AIDrawerContext"; import DesktopAIShell from "./components/DesktopAIShell"; import { appShellColumnClass, appShellOuterClass } from "./lib/appShell"; @@ -68,6 +69,7 @@ function AppRouter() { + diff --git a/client/src/locales/data-de.json b/client/src/locales/data-de.json index 7dea444..22fcfcd 100644 --- a/client/src/locales/data-de.json +++ b/client/src/locales/data-de.json @@ -31,7 +31,9 @@ "powerOffToastDemo": "Demomodus: Vom Gerät getrennt.", "powerOffToastSent": "Abschaltbefehl gesendet.", "doubleClickHint": "Doppelklick zum Eintreten", - "backToLegacy": "Alte Version" + "backToLegacy": "Alte Version", + "changelog": "Neuerungen", + "changelogLoadFailed": "Laden fehlgeschlagen, bitte später erneut versuchen" }, "vuPage": { "title": "VU-Meter Galerie" diff --git a/client/src/locales/data-en.json b/client/src/locales/data-en.json index 5dd7a10..9bf18d8 100644 --- a/client/src/locales/data-en.json +++ b/client/src/locales/data-en.json @@ -31,7 +31,9 @@ "powerOffToastDemo": "Demo mode: disconnected from the device.", "powerOffToastSent": "Shutdown command sent.", "doubleClickHint": "Double-click to enter", - "backToLegacy": "Legacy version" + "backToLegacy": "Legacy version", + "changelog": "What's New", + "changelogLoadFailed": "Failed to load, please try again later" }, "vuPage": { "title": "VU meter gallery" diff --git a/client/src/locales/data-es.json b/client/src/locales/data-es.json index 88b9957..3c24edc 100644 --- a/client/src/locales/data-es.json +++ b/client/src/locales/data-es.json @@ -31,7 +31,9 @@ "powerOffToastDemo": "Modo demo: desconectado del dispositivo.", "powerOffToastSent": "Comando de apagado enviado.", "doubleClickHint": "Doble clic para entrar", - "backToLegacy": "Versión anterior" + "backToLegacy": "Versión anterior", + "changelog": "Novedades", + "changelogLoadFailed": "Error al cargar, inténtelo de nuevo más tarde" }, "vuPage": { "title": "Galería de VU-metro" diff --git a/client/src/locales/data-fr.json b/client/src/locales/data-fr.json index 4ff674d..6378126 100644 --- a/client/src/locales/data-fr.json +++ b/client/src/locales/data-fr.json @@ -31,7 +31,9 @@ "powerOffToastDemo": "Mode démo : déconnecté de l'appareil.", "powerOffToastSent": "Commande d'extinction envoyée.", "doubleClickHint": "Double-clic pour entrer", - "backToLegacy": "Ancienne version" + "backToLegacy": "Ancienne version", + "changelog": "Nouveautés", + "changelogLoadFailed": "Échec du chargement, veuillez réessayer plus tard" }, "vuPage": { "title": "Galerie VU-mètre" diff --git a/client/src/locales/data-zh-HK.json b/client/src/locales/data-zh-HK.json index 6c8a8b5..2de0f33 100644 --- a/client/src/locales/data-zh-HK.json +++ b/client/src/locales/data-zh-HK.json @@ -31,7 +31,9 @@ "powerOffToastDemo": "演示模式:已中斷與裝置的連線", "powerOffToastSent": "已傳送關機指令", "doubleClickHint": "按兩下進入", - "backToLegacy": "返回舊版" + "backToLegacy": "返回舊版", + "changelog": "更新說明", + "changelogLoadFailed": "載入失敗,請稍後再試" }, "vuPage": { "title": "VU 表庫" diff --git a/client/src/locales/data-zh.json b/client/src/locales/data-zh.json index ec50d1a..c95b3f5 100644 --- a/client/src/locales/data-zh.json +++ b/client/src/locales/data-zh.json @@ -31,7 +31,9 @@ "powerOffToastDemo": "演示模式:已断开与设备的连接", "powerOffToastSent": "已发送关机指令", "doubleClickHint": "双击进入", - "backToLegacy": "返回旧版" + "backToLegacy": "返回旧版", + "changelog": "更新说明", + "changelogLoadFailed": "加载失败,请稍后重试" }, "vuPage": { "title": "VU 表库" diff --git a/client/src/pages/ChangelogPage.tsx b/client/src/pages/ChangelogPage.tsx new file mode 100644 index 0000000..d529532 --- /dev/null +++ b/client/src/pages/ChangelogPage.tsx @@ -0,0 +1,92 @@ +/* ============================================================ + CHANGELOG PAGE — Release notes / 更新说明 + Content is loaded from /changelog.md (public dir) at runtime, + so updating the notes only requires editing that markdown file. + ============================================================ */ +import BottomNav from "@/components/BottomNav"; +import { useDevice } from "@/contexts/DeviceContext"; +import { usePageTitleWithConnection } from "@/hooks/usePageTitleWithConnection"; +import { resolveLocalePack } from "@/locales/resolveLocale"; +import localeZh from "@/locales/data-zh.json"; +import { ChevronLeft } from "lucide-react"; +import { useEffect, useMemo, useState } from "react"; +import { useLocation } from "wouter"; +import { Streamdown } from "streamdown"; + +type HomeLocale = NonNullable<(typeof localeZh)["home"]>; + +export default function ChangelogPage() { + const [, setLocation] = useLocation(); + const { deviceState } = useDevice(); + + const homeText = useMemo((): HomeLocale => { + const pack = resolveLocalePack(deviceState?.language); + return (pack.home ?? {}) as HomeLocale; + }, [deviceState?.language]); + + const pageTitle = usePageTitleWithConnection(homeText.changelog ?? "更新说明"); + + const [content, setContent] = useState(null); + const [failed, setFailed] = useState(false); + + useEffect(() => { + let cancelled = false; + fetch(`${import.meta.env.BASE_URL}changelog.md`) + .then((res) => { + if (!res.ok) throw new Error(`HTTP ${res.status}`); + return res.text(); + }) + .then((text) => { + if (!cancelled) setContent(text); + }) + .catch(() => { + if (!cancelled) setFailed(true); + }); + return () => { + cancelled = true; + }; + }, []); + + return ( +
+
+ +

{pageTitle}

+
+
+ +
+
+ {failed ? ( +

{homeText.changelogLoadFailed ?? "加载失败,请稍后重试"}

+ ) : content ? ( +
+ {content} +
+ ) : ( +
+
+
+ )} +
+
+ + +
+ ); +} diff --git a/client/src/pages/Home.tsx b/client/src/pages/Home.tsx index 561973d..e852b1f 100644 --- a/client/src/pages/Home.tsx +++ b/client/src/pages/Home.tsx @@ -45,6 +45,7 @@ import { SquareArrowRightEnter, SquareArrowRightExit, FileMusic, + BookOpenCheck, Minus, Plus, } from "lucide-react"; @@ -964,6 +965,11 @@ export default function Home() { window.location.href = `https://am.luxsinaudio.com/i.html?ip=${encodeURIComponent(target)}`; }} /> + } + label={homeText.changelog ?? "更新说明"} + onClick={() => setLocation("/changelog")} + />