diff --git a/client/public/IIS/iis_app1.png b/client/public/IIS/iis_app1.png new file mode 100644 index 0000000..c6fc89f Binary files /dev/null and b/client/public/IIS/iis_app1.png differ diff --git a/client/public/IIS/iis_app2.png b/client/public/IIS/iis_app2.png new file mode 100644 index 0000000..fff22fb Binary files /dev/null and b/client/public/IIS/iis_app2.png differ diff --git a/client/public/IIS/iis_app3.png b/client/public/IIS/iis_app3.png new file mode 100644 index 0000000..6e3543c Binary files /dev/null and b/client/public/IIS/iis_app3.png differ diff --git a/client/public/IIS/iis_app4.png b/client/public/IIS/iis_app4.png new file mode 100644 index 0000000..7c5ec72 Binary files /dev/null and b/client/public/IIS/iis_app4.png differ diff --git a/client/public/IIS/iis_app5.png b/client/public/IIS/iis_app5.png new file mode 100644 index 0000000..4e69504 Binary files /dev/null and b/client/public/IIS/iis_app5.png differ diff --git a/client/public/IIS/iis_app6.png b/client/public/IIS/iis_app6.png new file mode 100644 index 0000000..751dff0 Binary files /dev/null and b/client/public/IIS/iis_app6.png differ diff --git a/client/public/IIS/iis_app7.png b/client/public/IIS/iis_app7.png new file mode 100644 index 0000000..8401caf Binary files /dev/null and b/client/public/IIS/iis_app7.png differ diff --git a/client/public/IIS/iis_app8.png b/client/public/IIS/iis_app8.png new file mode 100644 index 0000000..77a197a Binary files /dev/null and b/client/public/IIS/iis_app8.png differ diff --git a/client/src/App.tsx b/client/src/App.tsx index 5cac1cf..ea872bb 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -22,6 +22,7 @@ import AIPage from "./pages/AIPage"; import CommunityPage from "./pages/CommunityPage"; import { AIDrawerProvider } from "./contexts/AIDrawerContext"; import DesktopAIShell from "./components/DesktopAIShell"; +import { appShellColumnClass, appShellGutterClass } from "./lib/appShell"; function AIPageRoute() { return ; @@ -59,9 +60,10 @@ function App() { - {/* Desktop: narrow shell centered in the browser with side gutters */} -
-
+
+
diff --git a/client/src/components/BottomNav.tsx b/client/src/components/BottomNav.tsx index 36d5333..d5b57e2 100644 --- a/client/src/components/BottomNav.tsx +++ b/client/src/components/BottomNav.tsx @@ -22,6 +22,7 @@ import { } from "lucide-react"; import { Link, useLocation } from "wouter"; import { useMemo } from "react"; +import { appShellColumnClass, appShellGutterClass } from "@/lib/appShell"; type BottomNavLocale = NonNullable<(typeof localeZh)["bottomNav"]>; @@ -56,16 +57,19 @@ export default function BottomNav() { return ( ); } diff --git a/client/src/components/DesktopAIShell.tsx b/client/src/components/DesktopAIShell.tsx index 4aa7805..94c9def 100644 --- a/client/src/components/DesktopAIShell.tsx +++ b/client/src/components/DesktopAIShell.tsx @@ -92,7 +92,9 @@ export default function DesktopAIShell({ children }: { children: ReactNode }) { "min-w-0", showSplit ? "h-full min-h-0 overflow-y-auto overflow-x-hidden" - : "min-h-dvh max-h-dvh overflow-x-hidden overflow-y-auto overscroll-y-contain [touch-action:pan-y]", + : 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]", )} style={ showSplit diff --git a/client/src/index.css b/client/src/index.css index 5d0869b..d35637f 100644 --- a/client/src/index.css +++ b/client/src/index.css @@ -116,12 +116,25 @@ * { @apply border-border outline-ring/50; } + html, + body, + #root { + min-height: 100%; + } + body { @apply bg-background text-foreground; font-family: -apple-system, 'SF Pro Display', 'SF Pro Text', 'Helvetica Neue', sans-serif; -webkit-font-smoothing: antialiased; background-color: #000000; } + + /* Desktop / ultrawide: page scrolls on window; shell stays centered */ + @media (min-width: 1024px) { + html { + overflow-x: hidden; + } + } button:not(:disabled), [role="button"]:not([aria-disabled="true"]), [type="button"]:not(:disabled), diff --git a/client/src/lib/appShell.ts b/client/src/lib/appShell.ts new file mode 100644 index 0000000..26d4c67 --- /dev/null +++ b/client/src/lib/appShell.ts @@ -0,0 +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"; diff --git a/client/src/locales/data-en.json b/client/src/locales/data-en.json index c26691e..c12bd38 100644 --- a/client/src/locales/data-en.json +++ b/client/src/locales/data-en.json @@ -546,19 +546,7 @@ }, { "index": 2, - "label": "Chinese" - }, - { - "index": 3, - "label": "German" - }, - { - "index": 4, - "label": "Spanish" - }, - { - "index": 5, - "label": "French" + "label": "Simplified Chinese" } ] }, diff --git a/client/src/locales/data-zh-HK.json b/client/src/locales/data-zh-HK.json index 52aced7..9990bde 100644 --- a/client/src/locales/data-zh-HK.json +++ b/client/src/locales/data-zh-HK.json @@ -384,10 +384,7 @@ "options": [ { "index": 0, "label": "英語" }, { "index": 1, "label": "中文(繁體)" }, - { "index": 2, "label": "中文(簡體)" }, - { "index": 3, "label": "德語" }, - { "index": 4, "label": "西語" }, - { "index": 5, "label": "法語" } + { "index": 2, "label": "中文(簡體)" } ] }, "Automatic": { diff --git a/client/src/locales/data-zh.json b/client/src/locales/data-zh.json index 04c680c..b743b77 100644 --- a/client/src/locales/data-zh.json +++ b/client/src/locales/data-zh.json @@ -385,10 +385,7 @@ "options": [ { "index": 0, "label": "英语" }, { "index": 1, "label": "中文(繁体)" }, - { "index": 2, "label": "中文(简体)" }, - { "index": 3, "label": "德语" }, - { "index": 4, "label": "西班牙语" }, - { "index": 5, "label": "法语" } + { "index": 2, "label": "中文(简体)" } ] }, "Automatic": { diff --git a/client/src/pages/SelectPage.tsx b/client/src/pages/SelectPage.tsx index 88c0a4f..1449479 100644 --- a/client/src/pages/SelectPage.tsx +++ b/client/src/pages/SelectPage.tsx @@ -13,8 +13,12 @@ import { useLocation } from "wouter"; import { ChevronLeft, Check } from "lucide-react"; import { useDevice } from "@/contexts/DeviceContext"; +import { cn } from "@/lib/utils"; import { useEffect, useState } from "react"; +const iisModeImageUrl = (index: number) => + `${import.meta.env.BASE_URL}IIS/iis_app${index + 1}.png`; + /* ── Key → API field mapping ── */ const KEY_TO_SETTING: Record = { effect_value: "effect_value", @@ -118,26 +122,68 @@ export default function SelectPage() {
- {/* ── Options list ── */}
-
- {options.map((opt, idx) => { - const isSelected = idx === selectedIdx; - return ( - - ); - })} -
+ {key === "IISMode" ? ( +
+ {options.map((opt, idx) => { + const isSelected = idx === selectedIdx; + return ( + + ); + })} +
+ ) : ( +
+ {options.map((opt, idx) => { + const isSelected = idx === selectedIdx; + return ( + + ); + })} +
+ )}
); diff --git a/client/src/pages/SystemPage.tsx b/client/src/pages/SystemPage.tsx index 818501f..5a033cf 100644 --- a/client/src/pages/SystemPage.tsx +++ b/client/src/pages/SystemPage.tsx @@ -107,6 +107,18 @@ function optionLabels(items: Array<{ index: number; label: string }> | undefined return sorted.map((item) => item.label); } +/** Device language index: 0=en, 1=zh-HK, 2=zh-CN */ +const SUPPORTED_LANGUAGE_INDICES = new Set([0, 1, 2]); + +function languageLabels( + items: Array<{ index: number; label: string }> | undefined, + fallback: string[], +) { + const filtered = (items ?? []).filter((o) => SUPPORTED_LANGUAGE_INDICES.has(o.index)); + if (filtered.length === 0) return fallback; + return [...filtered].sort((a, b) => a.index - b.index).map((o) => o.label); +} + export default function SystemPage() { const [, setLocation] = useLocation(); const { deviceState, updateSetting, refresh, isConnected, api, ip } = useDevice(); @@ -124,7 +136,8 @@ export default function SystemPage() { const screenOffIdx = ds?.screenOff ?? 2; const sleepIdx = ds?.sleep ?? 0; const autoHomeIdx = ds?.autoHome ?? 1; - const langIdx = ds?.language ?? 2; + const rawLangIdx = ds?.language ?? 2; + const langIdx = SUPPORTED_LANGUAGE_INDICES.has(rawLangIdx) ? rawLangIdx : 2; const knobBreathLightOn = (ds?.knob_breathlight ?? 1) === 0; const localeData = LOCALE_BY_LANG[langIdx] ?? localeZh; const settingsText = localeData.settings; @@ -134,7 +147,11 @@ export default function SystemPage() { const SCREEN_OFF_OPTIONS = optionLabels(settingsText.turnOffScreen?.options, ["常亮", "30秒", "1分钟", "3分钟", "5分钟"]); const SLEEP_OPTIONS = optionLabels(settingsText.sleepTime?.options ?? settingsText.sleep?.options, ["关闭", "1分钟", "5分钟", "10分钟"]); const AUTO_HOME_OPTIONS = optionLabels(settingsText.Automatic?.options, ["关闭", "20秒", "40秒", "60秒"]); - const LANG_OPTIONS = optionLabels(settingsText.language?.options, ["English", "繁體中文", "简体中文"]); + const LANG_OPTIONS = languageLabels(settingsText.language?.options, [ + "English", + "繁體中文", + "简体中文", + ]); const sleepRowLabel = settingsText.sleepTime?.label ??