Compare commits

...

4 Commits

Author SHA1 Message Date
9d457caefd fix
Some checks failed
Deploy / deploy (build, debian, 22) (push) Has been cancelled
Deploy / deploy (build:staging, school, 8822) (push) Has been cancelled
2026-04-16 01:49:49 -06:00
3fb59b9c89 add theme
Some checks failed
Deploy / deploy (build, debian, 22) (push) Has been cancelled
Deploy / deploy (build:staging, school, 8822) (push) Has been cancelled
2026-04-16 01:39:30 -06:00
879f308871 update
Some checks failed
Deploy / deploy (build, debian, 22) (push) Has been cancelled
Deploy / deploy (build:staging, school, 8822) (push) Has been cancelled
2026-04-02 06:25:27 -06:00
bbe843fc54 update
Some checks failed
Deploy / deploy (build, debian, 22) (push) Has been cancelled
Deploy / deploy (build:staging, school, 8822) (push) Has been cancelled
2026-04-02 06:22:13 -06:00
9 changed files with 1061 additions and 527 deletions

View File

@@ -1,7 +1,7 @@
VITE_OJ=https://oj.xuyue.cc VITE_OJ=https://oj.xuyue.cc
VITE_CODE=https://code.xuyue.cc VITE_CODE=https://code.xuyue.cc
VITE_WEB=https://web.xuyue.cc VITE_WEB=https://web.xuyue.cc
VITE_SHUATI=https://ti.xuyue.cc VITE_SHUATI=http://47.115.221.142:5000
VITE_BOOK=https://book.xuyue.cc VITE_BOOK=https://book.xuyue.cc
VITE_HUABU=https://huabu.xuyue.cc VITE_HUABU=https://huabu.xuyue.cc
VITE_PPT=https://ppt.xuyue.cc/py VITE_PPT=https://ppt.xuyue.cc/py

View File

@@ -27,7 +27,7 @@ jobs:
with: with:
node-version: 24 node-version: 24
cache: npm cache: npm
- run: npm ci - run: npm install
- run: npm run ${{ matrix.build_command }} - run: npm run ${{ matrix.build_command }}
env: env:
CI: false CI: false

34
CLAUDE.md Normal file
View File

@@ -0,0 +1,34 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Commands
```bash
npm start # Dev server (production mode)
npm run build # Production build
npm run build:staging # Staging build
npm run fmt # Format with Prettier (*.js, style.css, index.html)
```
No test suite exists. Prettier is the only code quality tool.
## Architecture
This is a static IoT learning portal — a single-page app built with vanilla JS and Vite. No framework.
**Module responsibilities:**
- `main.js` — entry point, calls `initApp()`
- `app.js` — application lifecycle: reads localStorage, wires event listeners, orchestrates everything
- `data.js` — registry of external learning platform links (OJ, code runners, books, etc.)
- `render.js` — generates HTML for site cards and pins from `data.js` entries
- `i18n.js` — translation system with a `t()` function; supports 11 language variants (Chinese variants, EN, JA, KO, plus joke languages: wenyan, mars, garbled, binary, meow, emoji)
- `theme.js` — manages 4 design themes (Fluent, Material You, Terminal, Cyberpunk) and dark/light toggle
**Data flow:** `app.js` → reads prefs from `localStorage` → calls `render.js` to build the card grid from `data.js` → applies theme via `theme.js` → uses `i18n.js` for all UI strings.
**State persistence:** language, design theme, and dark/light preference are all stored in `localStorage`.
**Site URLs** are injected at build time via `import.meta.env.VITE_*` environment variables.
**Themes:** Terminal and Cyberpunk force dark mode; the light/dark toggle is disabled for those themes.

11
i18n.js
View File

@@ -139,66 +139,77 @@ export const DESIGN_THEME_LABELS = {
"material-you": "Material You", "material-you": "Material You",
terminal: "终端", terminal: "终端",
cyberpunk: "赛博朋克", cyberpunk: "赛博朋克",
nord: "Nord",
}, },
"zh-Hant": { "zh-Hant": {
fluent: "Fluent", fluent: "Fluent",
"material-you": "Material You", "material-you": "Material You",
terminal: "終端", terminal: "終端",
cyberpunk: "賽博龐克", cyberpunk: "賽博龐克",
nord: "Nord",
}, },
en: { en: {
fluent: "Fluent", fluent: "Fluent",
"material-you": "Material You", "material-you": "Material You",
terminal: "Terminal", terminal: "Terminal",
cyberpunk: "Cyberpunk", cyberpunk: "Cyberpunk",
nord: "Nord",
}, },
ja: { ja: {
fluent: "Fluent", fluent: "Fluent",
"material-you": "Material You", "material-you": "Material You",
terminal: "ターミナル", terminal: "ターミナル",
cyberpunk: "サイバーパンク", cyberpunk: "サイバーパンク",
nord: "Nord",
}, },
ko: { ko: {
fluent: "Fluent", fluent: "Fluent",
"material-you": "Material You", "material-you": "Material You",
terminal: "터미널", terminal: "터미널",
cyberpunk: "사이버펑크", cyberpunk: "사이버펑크",
nord: "Nord",
}, },
wenyan: { wenyan: {
fluent: "流光", fluent: "流光",
"material-you": "物材", "material-you": "物材",
terminal: "终端", terminal: "终端",
cyberpunk: "赛博", cyberpunk: "赛博",
nord: "清寒",
}, },
mars: { mars: {
fluent: "流↗光", fluent: "流↗光",
"material-you": "材↘質", "material-you": "材↘質",
terminal: "終↗★端", terminal: "終↗★端",
cyberpunk: "賽↘!博", cyberpunk: "賽↘!博",
nord: "清↗寒★",
}, },
garbled: { garbled: {
fluent: "◼è▦", fluent: "◼è▦",
"material-you": "拷▤屯ä锟◽", "material-you": "拷▤屯ä锟◽",
terminal: "¥¬▤▨¿¿", terminal: "¥¬▤▨¿¿",
cyberpunk: "◼çæ¥烫¥", cyberpunk: "◼çæ¥烫¥",
nord: "æ◽屯¿",
}, },
bin: { bin: {
fluent: "0101", fluent: "0101",
"material-you": "010101", "material-you": "010101",
terminal: "01010101", terminal: "01010101",
cyberpunk: "0101010101", cyberpunk: "0101010101",
nord: "0101010",
}, },
meow: { meow: {
fluent: "喵喵", fluent: "喵喵",
"material-you": "喵喵喵", "material-you": "喵喵喵",
terminal: "喵喵", terminal: "喵喵",
cyberpunk: "喵喵喵喵", cyberpunk: "喵喵喵喵",
nord: "喵喵喵",
}, },
emoji: { emoji: {
fluent: "💧", fluent: "💧",
"material-you": "🧱", "material-you": "🧱",
terminal: "⌨️", terminal: "⌨️",
cyberpunk: "⚡", cyberpunk: "⚡",
nord: "❄️",
}, },
} }

View File

@@ -42,6 +42,9 @@
<li role="option" data-value="cyberpunk" aria-selected="false"> <li role="option" data-value="cyberpunk" aria-selected="false">
Cyberpunk Cyberpunk
</li> </li>
<li role="option" data-value="nord" aria-selected="false">
Nord
</li>
</ul> </ul>
</label> </label>
<label class="design-theme language-switch"> <label class="design-theme language-switch">

1353
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -10,7 +10,7 @@
"fmt": "prettier --write *.js style.css index.html" "fmt": "prettier --write *.js style.css index.html"
}, },
"devDependencies": { "devDependencies": {
"prettier": "^3.7.4", "prettier": "^3.8.2",
"vite": "^7.3.0" "vite": "^8.0.8"
} }
} }

175
style.css
View File

@@ -1405,3 +1405,178 @@ html[data-design-theme="material-you"] .beian a:hover {
transform: none !important; transform: none !important;
} }
} }
/* ─── Nord (nordtheme.com) ──────────────────────────────── */
/* Default: Polar Night (dark) */
html[data-design-theme="nord"] {
color-scheme: dark;
--accent: #88c0d0;
--accent-rgb: 136, 192, 208;
--accent-2: #81a1c1;
--accent-3: #5e81ac;
--accent-secondary-rgb: 129, 161, 193;
--page-gradient: #2e3440;
--page-texture: none;
--title-gradient: linear-gradient(135deg, #88c0d0 0%, #81a1c1 50%, #5e81ac 100%);
--control-bg: rgba(59, 66, 82, 0.85);
--control-border: rgba(76, 86, 106, 0.6);
--control-inset: rgba(67, 76, 94, 0.5);
--control-fg: #eceff4;
}
/* Re-assert Nord dark variables after generic dark mode overrides them */
html[data-theme="dark"][data-design-theme="nord"] {
color-scheme: dark;
--accent: #88c0d0;
--accent-rgb: 136, 192, 208;
--accent-2: #81a1c1;
--accent-3: #5e81ac;
--accent-secondary-rgb: 129, 161, 193;
--page-gradient: #2e3440;
--page-texture: none;
--title-gradient: linear-gradient(135deg, #88c0d0 0%, #81a1c1 50%, #5e81ac 100%);
--control-bg: rgba(59, 66, 82, 0.85);
--control-border: rgba(76, 86, 106, 0.6);
--control-inset: rgba(67, 76, 94, 0.5);
--control-fg: #eceff4;
}
html[data-theme="dark"][data-design-theme="nord"] body {
color: #eceff4;
}
/* Snow Storm (light mode) */
html[data-theme="light"][data-design-theme="nord"] {
color-scheme: light;
--accent: #5e81ac;
--accent-rgb: 94, 129, 172;
--accent-2: #81a1c1;
--accent-3: #88c0d0;
--accent-secondary-rgb: 94, 129, 172;
--page-gradient: #eceff4;
--page-texture: none;
--title-gradient: linear-gradient(135deg, #5e81ac 0%, #81a1c1 50%, #88c0d0 100%);
--control-bg: rgba(229, 233, 240, 0.85);
--control-border: rgba(216, 222, 233, 0.8);
--control-inset: rgba(236, 239, 244, 0.9);
--control-fg: #2e3440;
}
html[data-theme="light"][data-design-theme="nord"] body {
color: #2e3440;
}
/* Dark cards (Polar Night) */
html[data-theme="dark"][data-design-theme="nord"] .card {
background: #3b4252;
backdrop-filter: none;
-webkit-backdrop-filter: none;
border: 1px solid #4c566a;
border-radius: 6px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
}
html[data-theme="dark"][data-design-theme="nord"] .card::before {
display: none;
}
html[data-theme="dark"][data-design-theme="nord"] .card h2 {
color: #eceff4;
}
html[data-theme="dark"][data-design-theme="nord"] .card:hover h2,
html[data-theme="dark"][data-design-theme="nord"] .card:focus h2 {
color: #88c0d0;
}
html[data-theme="dark"][data-design-theme="nord"] .card p {
color: #d8dee9;
}
html[data-theme="dark"][data-design-theme="nord"] .card:hover p,
html[data-theme="dark"][data-design-theme="nord"] .card:focus p {
color: #e5e9f0;
}
html[data-theme="dark"][data-design-theme="nord"] .card:hover,
html[data-theme="dark"][data-design-theme="nord"] .card:focus {
border-color: #88c0d0;
box-shadow: 0 4px 16px rgba(136, 192, 208, 0.2);
}
html[data-theme="dark"][data-design-theme="nord"] .card.pin {
background: #434c5e;
backdrop-filter: none;
-webkit-backdrop-filter: none;
}
/* Light cards (Snow Storm) */
html[data-theme="light"][data-design-theme="nord"] .card {
background: #e5e9f0;
backdrop-filter: none;
-webkit-backdrop-filter: none;
border: 1px solid #d8dee9;
border-radius: 6px;
box-shadow: 0 2px 8px rgba(46, 52, 64, 0.1);
}
html[data-theme="light"][data-design-theme="nord"] .card::before {
display: none;
}
html[data-theme="light"][data-design-theme="nord"] .card h2 {
color: #2e3440;
}
html[data-theme="light"][data-design-theme="nord"] .card:hover h2,
html[data-theme="light"][data-design-theme="nord"] .card:focus h2 {
color: #5e81ac;
}
html[data-theme="light"][data-design-theme="nord"] .card p {
color: #4c566a;
}
html[data-theme="light"][data-design-theme="nord"] .card:hover p,
html[data-theme="light"][data-design-theme="nord"] .card:focus p {
color: #3b4252;
}
html[data-theme="light"][data-design-theme="nord"] .card:hover,
html[data-theme="light"][data-design-theme="nord"] .card:focus {
border-color: #5e81ac;
box-shadow: 0 4px 16px rgba(94, 129, 172, 0.2);
}
html[data-theme="light"][data-design-theme="nord"] .card.pin {
background: #eceff4;
backdrop-filter: none;
-webkit-backdrop-filter: none;
border-color: #d8dee9;
}
/* Beian links */
html[data-theme="dark"][data-design-theme="nord"] .beian a {
color: #d8dee9;
}
html[data-theme="dark"][data-design-theme="nord"] .beian a:hover {
color: #88c0d0;
}
html[data-theme="light"][data-design-theme="nord"] .beian a {
color: #4c566a;
}
html[data-theme="light"][data-design-theme="nord"] .beian a:hover {
color: #5e81ac;
}

View File

@@ -1,4 +1,4 @@
const DESIGN_THEMES = ["fluent", "material-you", "terminal", "cyberpunk"] const DESIGN_THEMES = ["fluent", "material-you", "terminal", "cyberpunk", "nord"]
const FORCED_DARK_DESIGN_THEMES = new Set(["terminal", "cyberpunk"]) const FORCED_DARK_DESIGN_THEMES = new Set(["terminal", "cyberpunk"])
const THEME_BEFORE_FORCED_KEY = "themeBeforeForcedDark" const THEME_BEFORE_FORCED_KEY = "themeBeforeForcedDark"