diff --git a/web/app/globals.css b/web/app/globals.css index 452d3ad..24539f0 100644 --- a/web/app/globals.css +++ b/web/app/globals.css @@ -49,6 +49,40 @@ :root { --radius: 0.65rem; + --background: oklch(0.985 0 0); + --foreground: oklch(0.205 0 0); + --card: oklch(1 0 0); + --card-foreground: oklch(0.205 0 0); + --popover: oklch(1 0 0); + --popover-foreground: oklch(0.205 0 0); + --primary: oklch(0.32 0 0); + --primary-foreground: oklch(0.985 0 0); + --secondary: oklch(0.96 0 0); + --secondary-foreground: oklch(0.24 0 0); + --muted: oklch(0.96 0 0); + --muted-foreground: oklch(0.48 0 0); + --accent: oklch(0.94 0 0); + --accent-foreground: oklch(0.24 0 0); + --destructive: oklch(0.59 0.225 25); + --border: oklch(0.89 0 0); + --input: oklch(0.89 0 0); + --ring: oklch(0.56 0 0); + --chart-1: oklch(0.44 0 0); + --chart-2: oklch(0.56 0 0); + --chart-3: oklch(0.68 0 0); + --chart-4: oklch(0.76 0 0); + --chart-5: oklch(0.84 0 0); + --sidebar: oklch(0.96 0 0); + --sidebar-foreground: oklch(0.24 0 0); + --sidebar-primary: oklch(0.32 0 0); + --sidebar-primary-foreground: oklch(0.985 0 0); + --sidebar-accent: oklch(0.92 0 0); + --sidebar-accent-foreground: oklch(0.24 0 0); + --sidebar-border: oklch(0.87 0 0); + --sidebar-ring: oklch(0.56 0 0); +} + +:root[data-palette="green"] { --background: oklch(0.973 0.011 112); --foreground: oklch(0.22 0.021 210); --card: oklch(0.996 0.004 105); @@ -151,6 +185,40 @@ } .dark { + --background: oklch(0.18 0 0); + --foreground: oklch(0.94 0 0); + --card: oklch(0.225 0 0); + --card-foreground: oklch(0.94 0 0); + --popover: oklch(0.225 0 0); + --popover-foreground: oklch(0.94 0 0); + --primary: oklch(0.82 0 0); + --primary-foreground: oklch(0.18 0 0); + --secondary: oklch(0.285 0 0); + --secondary-foreground: oklch(0.94 0 0); + --muted: oklch(0.285 0 0); + --muted-foreground: oklch(0.7 0 0); + --accent: oklch(0.32 0 0); + --accent-foreground: oklch(0.94 0 0); + --destructive: oklch(0.68 0.19 25); + --border: oklch(1 0 0 / 10%); + --input: oklch(1 0 0 / 14%); + --ring: oklch(0.62 0 0); + --chart-1: oklch(0.82 0 0); + --chart-2: oklch(0.72 0 0); + --chart-3: oklch(0.62 0 0); + --chart-4: oklch(0.52 0 0); + --chart-5: oklch(0.42 0 0); + --sidebar: oklch(0.16 0 0); + --sidebar-foreground: oklch(0.93 0 0); + --sidebar-primary: oklch(0.82 0 0); + --sidebar-primary-foreground: oklch(0.18 0 0); + --sidebar-accent: oklch(0.27 0 0); + --sidebar-accent-foreground: oklch(0.94 0 0); + --sidebar-border: oklch(1 0 0 / 9%); + --sidebar-ring: oklch(0.62 0 0); +} + +.dark[data-palette="green"] { --background: oklch(0.18 0.016 190); --foreground: oklch(0.94 0.009 155); --card: oklch(0.225 0.018 190); diff --git a/web/app/layout.tsx b/web/app/layout.tsx index 7dbf7fd..130fa8b 100644 --- a/web/app/layout.tsx +++ b/web/app/layout.tsx @@ -26,9 +26,9 @@ const geistMono = Geist_Mono({ const paletteScript = ` try { var palette = window.localStorage.getItem("dashboard_palette"); - document.documentElement.dataset.palette = palette === "blue" || palette === "gray" ? palette : "green"; + document.documentElement.dataset.palette = palette === "plain" || palette === "blue" || palette === "green" || palette === "gray" ? palette : "plain"; } catch (_) { - document.documentElement.dataset.palette = "green"; + document.documentElement.dataset.palette = "plain"; } ` diff --git a/web/components/palette-toggle.test.mjs b/web/components/palette-toggle.test.mjs new file mode 100644 index 0000000..b54e637 --- /dev/null +++ b/web/components/palette-toggle.test.mjs @@ -0,0 +1,28 @@ +import assert from "node:assert/strict" +import { readFile } from "node:fs/promises" +import test from "node:test" + +const paletteSource = await readFile( + new URL("./palette-toggle.tsx", import.meta.url), + "utf8", +) +const layoutSource = await readFile(new URL("../app/layout.tsx", import.meta.url), "utf8") +const zhMessages = JSON.parse( + await readFile(new URL("../messages/zh-CN.json", import.meta.url), "utf8"), +) +const enMessages = JSON.parse( + await readFile(new URL("../messages/en-US.json", import.meta.url), "utf8"), +) + +test("plain palette is the default dashboard palette", () => { + assert.match(paletteSource, /type PaletteMode = "plain" \| "blue" \| "green" \| "gray"/) + assert.match(paletteSource, /const DEFAULT_PALETTE: PaletteMode = "plain"/) + assert.match(layoutSource, /palette === "plain"/) + assert.match(layoutSource, /: "plain"/) +}) + +test("plain palette is available in the palette menu and messages", () => { + assert.match(paletteSource, /value: "plain"[\s\S]*labelKey: "palette\.plain"/) + assert.equal(zhMessages.palette.plain, "朴素默认") + assert.equal(enMessages.palette.plain, "Plain Default") +}) diff --git a/web/components/palette-toggle.tsx b/web/components/palette-toggle.tsx index 5d2db8c..4d721d2 100644 --- a/web/components/palette-toggle.tsx +++ b/web/components/palette-toggle.tsx @@ -1,7 +1,7 @@ "use client" import { useEffect, useState } from "react" -import { DropletsIcon, PaletteIcon } from "lucide-react" +import { CircleIcon, DropletsIcon, PaletteIcon } from "lucide-react" import { useI18n } from "@/i18n/provider" import { Button } from "@/components/ui/button" @@ -13,16 +13,21 @@ import { DropdownMenuTrigger, } from "@/components/ui/dropdown-menu" -type PaletteMode = "blue" | "green" | "gray" +type PaletteMode = "plain" | "blue" | "green" | "gray" const PALETTE_STORAGE_KEY = "dashboard_palette" -const DEFAULT_PALETTE: PaletteMode = "green" +const DEFAULT_PALETTE: PaletteMode = "plain" const paletteOptions: Array<{ value: PaletteMode labelKey: string swatch: string }> = [ + { + value: "plain", + labelKey: "palette.plain", + swatch: "bg-zinc-700", + }, { value: "green", labelKey: "palette.green", @@ -46,7 +51,10 @@ function readPalette(): PaletteMode { } const stored = window.localStorage.getItem(PALETTE_STORAGE_KEY) - return stored === "blue" || stored === "green" || stored === "gray" + return stored === "plain" || + stored === "blue" || + stored === "green" || + stored === "gray" ? stored : DEFAULT_PALETTE } @@ -68,12 +76,19 @@ export function PaletteToggle() { function handleChange(value: string) { const nextPalette: PaletteMode = - value === "blue" || value === "gray" ? value : "green" + value === "blue" || value === "green" || value === "gray" + ? value + : "plain" setPalette(nextPalette) applyPalette(nextPalette) } - const ActiveIcon = palette === "green" ? DropletsIcon : PaletteIcon + const ActiveIcon = + palette === "plain" + ? CircleIcon + : palette === "green" + ? DropletsIcon + : PaletteIcon return ( diff --git a/web/messages/en-US.json b/web/messages/en-US.json index 810241c..cede3fc 100644 --- a/web/messages/en-US.json +++ b/web/messages/en-US.json @@ -2364,6 +2364,7 @@ }, "palette": { "toggle": "Change accent color", + "plain": "Default", "green": "Service Green", "gray": "Neutral Gray", "blue": "Clear Blue" diff --git a/web/messages/zh-CN.json b/web/messages/zh-CN.json index 2076fd6..99a565b 100644 --- a/web/messages/zh-CN.json +++ b/web/messages/zh-CN.json @@ -2364,6 +2364,7 @@ }, "palette": { "toggle": "切换主题色", + "plain": "默认", "green": "温润服务绿", "gray": "中性精密灰", "blue": "清透科技蓝"