From cd5803d2740056fbe5dce5fd0d76fe72290f1a5e Mon Sep 17 00:00:00 2001
From: Beast
Date: Thu, 22 Jan 2026 21:24:48 +0800
Subject: [PATCH 1/3] feat: add desktop audit
---
.github/workflows/seo.yml | 26 +++++++++++++++-----------
website/unlighthouse.config.ts | 17 ++++++++++++++---
2 files changed, 29 insertions(+), 14 deletions(-)
diff --git a/.github/workflows/seo.yml b/.github/workflows/seo.yml
index 6cd40c4..4c9e1f8 100644
--- a/.github/workflows/seo.yml
+++ b/.github/workflows/seo.yml
@@ -60,35 +60,39 @@ jobs:
npx --yes linkinator http://localhost:3000 --recurse --skip "^(?!http://localhost:3000)"
# 4. DEEP AUDIT: Unlighthouse
- # Scans all pages and fails if SEO score is below 100 (configured in unlighthouse.config.ts)
+ # Scans all pages for both desktop and mobile devices
+ # Fails if SEO score is below 100 (configured in unlighthouse.config.ts)
# The '--build-static' flag generates the HTML report files.
# PUPPETEER_SKIP_CHROMIUM_DOWNLOAD tells Puppeteer to use the system Chromium from setup-chromium
# CHROMIUM_PATH is automatically set by the setup-chromium action
- - name: Run Unlighthouse Site-Wide Audit
+ - name: Run Unlighthouse Mobile Audit
working-directory: website
env:
PUPPETEER_SKIP_CHROMIUM_DOWNLOAD: "true"
PUPPETEER_EXECUTABLE_PATH: ${{ env.CHROMIUM_PATH }}
run: |
- npx --yes @unlighthouse/cli@latest --build-static
+ npx --yes @unlighthouse/cli@latest --build-static --output-path ./.unlighthouse/mobile
+
+ - name: Run Unlighthouse Desktop Audit
+ working-directory: website
+ env:
+ PUPPETEER_SKIP_CHROMIUM_DOWNLOAD: "true"
+ PUPPETEER_EXECUTABLE_PATH: ${{ env.CHROMIUM_PATH }}
+ run: |
+ npx --yes @unlighthouse/cli@latest --build-static --desktop --output-path ./.unlighthouse/desktop
# 5. UPLOAD ARTIFACT
# This takes the generated report and saves it as a zip file.
# Note: Unlighthouse runs in website/ directory, so output is at website/.unlighthouse
- - name: Debug - List Unlighthouse output
- if: always()
- run: |
- echo "Checking for Unlighthouse output..."
- ls -la website/.unlighthouse 2>/dev/null || echo "website/.unlighthouse not found"
- ls -la website/.unlighthouse/client 2>/dev/null || echo "website/.unlighthouse/client not found"
- find website -name ".unlighthouse" -type d 2>/dev/null || echo "No .unlighthouse directories found"
-
+ # Includes both mobile and desktop reports
- name: Create SEO Report Zip
if: always()
run: |
cd website
zip -r seo-report.zip .unlighthouse/ || echo "Failed to create zip, but continuing..."
ls -lh seo-report.zip || echo "Zip file not created"
+ echo "Report includes:"
+ ls -la .unlighthouse/ 2>/dev/null || echo "No .unlighthouse directory"
- name: Upload SEO Report
uses: actions/upload-artifact@v4
diff --git a/website/unlighthouse.config.ts b/website/unlighthouse.config.ts
index 0733959..f9172cb 100644
--- a/website/unlighthouse.config.ts
+++ b/website/unlighthouse.config.ts
@@ -1,6 +1,7 @@
import { defineUnlighthouseConfig } from 'unlighthouse/config'
-export default defineUnlighthouseConfig({
+// Base configuration shared by both desktop and mobile scans
+const baseConfig = {
site: 'http://localhost:3000',
// Fail the build if SEO score is below 100
// Other categories (performance, accessibility, best-practices) will be audited but won't fail the build
@@ -9,12 +10,11 @@ export default defineUnlighthouseConfig({
seo: 100,
},
},
- // Output path for reports
- outputPath: './.unlighthouse',
puppeteerClusterOptions: {
maxConcurrency: 2,
},
scanner: {
+ throttle: true,
exclude: [
'/zh-CN/.*',
'/ko-KR/.*',
@@ -26,4 +26,15 @@ export default defineUnlighthouseConfig({
'/hi-IN/.*',
],
},
+}
+
+// Default to mobile (can be overridden via CLI flags)
+export default defineUnlighthouseConfig({
+ ...baseConfig,
+ // Output path for reports (will be device-specific)
+ outputPath: './.unlighthouse',
+ scanner: {
+ ...baseConfig.scanner,
+ device: 'mobile', // Default to mobile
+ },
})
From d80373f1e619f05ee9a527810b3b11571f08f699 Mon Sep 17 00:00:00 2001
From: Beast
Date: Thu, 22 Jan 2026 21:48:57 +0800
Subject: [PATCH 2/3] feat: format run, cleaner unlighthouse config
---
website/astro.config.mjs | 7 +-
.../src/components/features/blog/BlogList.tsx | 4 +-
.../components/features/blog/BlogPost.astro | 2 +-
.../src/components/features/blog/Card.astro | 2 +-
.../navbar/mobile-menu/MobileMenu.astro | 5 +-
website/src/components/ui/react/Alert.tsx | 33 ++++---
website/src/components/ui/react/Button.tsx | 49 +++++-----
.../src/components/ui/react/Pagination.tsx | 63 +++++++------
website/src/components/ui/react/Select.tsx | 46 +++++-----
website/src/env.d.ts | 12 +--
website/src/pages/[lang]/app.astro | 5 +-
website/src/pages/[lang]/quests/raid.astro | 5 +-
website/src/pages/app.astro | 5 +-
website/src/pages/oauth.astro | 3 +-
website/src/pages/quests/raid.astro | 5 +-
website/src/styles/size.css | 2 +-
website/src/utils/build-json-ld.ts | 2 +-
website/src/utils/create-metadata.ts | 91 ++++++++++---------
website/unlighthouse.config.ts | 44 ++++-----
19 files changed, 191 insertions(+), 194 deletions(-)
diff --git a/website/astro.config.mjs b/website/astro.config.mjs
index 9dbb0e5..01bafe7 100644
--- a/website/astro.config.mjs
+++ b/website/astro.config.mjs
@@ -49,7 +49,8 @@ export default defineConfig({
serialize: (item) => {
// Read environment variable at runtime (during build)
// The serialize function runs during sitemap generation, so process.env should be available
- const envBaseUrl = typeof process !== 'undefined' && process.env?.SITE_BASE_URL;
+ const envBaseUrl =
+ typeof process !== "undefined" && process.env?.SITE_BASE_URL;
const baseUrl = envBaseUrl || SITE_BASE_URL;
// Replace production URLs with the correct base URL if env var is set
@@ -69,6 +70,6 @@ export default defineConfig({
return item;
},
}),
- react()
+ react(),
],
-});
\ No newline at end of file
+});
diff --git a/website/src/components/features/blog/BlogList.tsx b/website/src/components/features/blog/BlogList.tsx
index 4d28b8a..13a22dd 100644
--- a/website/src/components/features/blog/BlogList.tsx
+++ b/website/src/components/features/blog/BlogList.tsx
@@ -159,7 +159,7 @@ export const BlogList: React.FC = ({
{featuredPost.data.tags.length > 0 && (
-