初始提交:浏览器首页 MyHomePage 全栈项目

# 项目概述
个人浏览器首页导航应用,支持书签分类管理、搜索引擎快捷搜索、
必应每日壁纸轮播、前后端分离部署,适配 1Panel 服务器(Docker 模式)。

# 技术栈
- 前端:Vue 3 + TypeScript + Vite + Pinia + Capacitor(Android 打包)
- 后端:.NET 8 + SqlSugar(多数据库) + SQLite/MySQL + Swashbuckle
- 部署:1Panel 应用商店自定义应用(Docker Compose 模式)

# 项目结构
- backend/    .NET 8 API 后端(8 个 Controller + 15 个 Service)
- frontend/   Vue 3 前端(19 个组件 + 9 个 API 模块 + 5 个 Store)
- docker/     Docker 部署文件(后端镜像 + Nginx 反代)
- docs/       部署手册(1Panel 实战版)
- scripts/    E2E 测试脚本

# 已实现功能
- 书签管理:增删改查 + 树形分类 + 拖拽排序 + 主色自适应
- 搜索引擎:8 个内置引擎 + 自定义引擎 + favicon 自动抓取
- 必应壁纸:每日轮播 + 多分辨率自动选择 + 1.6MP 质量优先
- 全局设置:主题/行为/数据/工具 4 分类 + 跨设备同步
- 文件上传:图标/书签/通用(容器持久化 + 跨域 URL 拼接)
- 同步:基于变更日志的设备间数据同步
- 跨域部署:前后端分离 + runtime config.json 无需重新编译

# 进度记录
- 已完成 P0~P52 共 53 个开发节点(详细见 说明文档.md)
- 当前版本:v1.0 部署就绪

# 部署文档
- README.md:项目说明 + 快速开始
- 说明文档.md:完整开发进度(中文)
- docs/DEPLOY.md:1Panel 部署手册(Docker 模式)
This commit is contained in:
2026-07-05 05:09:56 +08:00
commit 68be41e7a2
129 changed files with 15900 additions and 0 deletions
+57
View File
@@ -0,0 +1,57 @@
{
"data": [
{
"id": "page-desktop",
"title": "浏览器首页 - 桌面端",
"type": "page",
"version": 1,
"createdAt": 1751620800000,
"canvasData": {
"x": 0,
"y": 0,
"group": 0
},
"devMetadata": {
"htmlSrc": "pages/desktop.html",
"interactions": []
}
},
{
"id": "page-desktop-settings",
"title": "浏览器首页 - 桌面端 - 设置面板",
"type": "page",
"version": 1,
"createdAt": 1751620800000,
"canvasData": {
"x": 620,
"y": 0,
"group": 0
},
"devMetadata": {
"htmlSrc": "pages/desktop-settings.html",
"interactions": []
}
},
{
"id": "page-mobile",
"title": "浏览器首页 - 移动端",
"type": "page",
"version": 1,
"createdAt": 1751620800000,
"canvasData": {
"x": 1240,
"y": 0,
"group": 0
},
"devMetadata": {
"htmlSrc": "pages/mobile.html",
"interactions": []
}
}
],
"config": {
"autoLayout": true,
"deviceType": "desktop",
"projectName": "浏览器首页"
}
}
+101
View File
@@ -0,0 +1,101 @@
/* ===== Browser Homepage - Brand CSS ===== */
/* Dark theme with glassmorphism, inspired by gaming browser start pages */
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Noto+Sans+SC:wght@300;400;500;600;700&display=swap');
:root {
/* ---- Brand Colors ---- */
--color-bg-primary: #0d0d12;
--color-bg-secondary: #16161e;
--color-bg-tertiary: #1e1e2a;
--color-bg-card: rgba(30, 30, 42, 0.65);
--color-bg-card-hover: rgba(40, 40, 56, 0.75);
--color-bg-sidebar: rgba(13, 13, 18, 0.85);
--color-bg-search: rgba(22, 22, 30, 0.8);
--color-bg-input: rgba(30, 30, 42, 0.5);
--color-bg-overlay: rgba(0, 0, 0, 0.5);
/* Text */
--color-text-primary: #e8e8f0;
--color-text-secondary: #9494a8;
--color-text-muted: #5e5e72;
--color-text-inverse: #0d0d12;
/* Brand accent */
--color-brand: #6c5ce7;
--color-brand-light: #a29bfe;
--color-brand-hover: #7d6ff0;
/* Border */
--color-border: rgba(255, 255, 255, 0.06);
--color-border-hover: rgba(255, 255, 255, 0.12);
--color-border-active: rgba(108, 92, 231, 0.5);
/* State colors */
--state-success: #00b894;
--state-warning: #fdcb6e;
--state-error: #e17055;
--state-info: #74b9ff;
/* ---- Typography ---- */
--font-sans: 'Inter', 'Noto Sans SC', -apple-system, BlinkMacSystemFont, sans-serif;
--font-heading: 'Inter', 'Noto Sans SC', sans-serif;
--text-xs: 11px;
--text-sm: 13px;
--text-base: 14px;
--text-lg: 16px;
--text-xl: 20px;
--text-2xl: 24px;
--text-3xl: 32px;
--leading-tight: 1.25;
--leading-normal: 1.5;
--leading-relaxed: 1.625;
--tracking-tight: -0.01em;
--tracking-normal: 0em;
/* ---- Spacing ---- */
--space-1: 4px;
--space-2: 8px;
--space-3: 12px;
--space-4: 16px;
--space-5: 20px;
--space-6: 24px;
--space-8: 32px;
--space-10: 40px;
--space-12: 48px;
/* ---- Radius ---- */
--radius-sm: 4px;
--radius-md: 8px;
--radius-lg: 12px;
--radius-xl: 16px;
--radius-full: 9999px;
/* ---- Shadows ---- */
--shadow-card: 0 1px 3px rgba(0, 0, 0, 0.05);
--shadow-float: 0 8px 32px rgba(0, 0, 0, 0.25);
--shadow-dropdown: 0 12px 48px rgba(0, 0, 0, 0.35);
/* ---- Glassmorphism ---- */
--glass-bg: rgba(30, 30, 42, 0.65);
--glass-blur: 12px;
--glass-border: 1px solid rgba(255, 255, 255, 0.08);
/* ---- Transitions ---- */
--transition-fast: 150ms ease;
--transition-normal: 250ms ease;
--transition-slow: 400ms ease;
/* ---- Sidebar ---- */
--sidebar-width: 240px;
--sidebar-collapsed-width: 64px;
/* ---- Z-index ---- */
--z-sidebar: 100;
--z-dropdown: 200;
--z-modal: 300;
--z-tooltip: 400;
}
+68
View File
@@ -0,0 +1,68 @@
{
"root": {
"nodeId": "gen-project-shell",
"kind": "project-shell",
"pageIds": [
"page-desktop",
"page-desktop-settings",
"page-mobile"
],
"sharedRegions": [
"brand css variables",
"color palette",
"glassmorphism mixin"
],
"privateRegions": [],
"mutableSlots": ["pageTitle", "viewportLayout"],
"status": "generated",
"children": [
{
"nodeId": "gen-page-desktop",
"kind": "page-leaf",
"pageIds": ["page-desktop"],
"output": "pages/desktop.html",
"sharedRegions": [],
"privateRegions": [
"sidebar with two-level nav",
"search bar with engine selector",
"link card grid"
],
"mutableSlots": ["pageTitle"],
"status": "generated",
"children": []
},
{
"nodeId": "gen-page-desktop-settings",
"kind": "page-leaf",
"pageIds": ["page-desktop-settings"],
"output": "pages/desktop-settings.html",
"sharedRegions": [],
"privateRegions": [
"settings popover panel",
"theme switch (light/dark/auto)",
"accent color picker",
"background image picker"
],
"mutableSlots": ["pageTitle"],
"status": "generated",
"children": []
},
{
"nodeId": "gen-page-mobile",
"kind": "page-leaf",
"pageIds": ["page-mobile"],
"output": "pages/mobile.html",
"sharedRegions": [],
"privateRegions": [
"top row: hamburger + search + settings gear",
"navigation drawer (avatar/profile relocated here)",
"vertical link list",
"FAB"
],
"mutableSlots": ["pageTitle"],
"status": "generated",
"children": []
}
]
}
}
+208
View File
@@ -0,0 +1,208 @@
{
"project": {
"name": "浏览器首页",
"path": "D:\\Code\\MyHomePage\\browser-homepage",
"operation": "create",
"deviceType": "desktop",
"language": "zh-CN",
"dashboardMode": false,
"replicationMode": null,
"sourceUrl": null,
"visualSpecExcerpt": null,
"styleDefinitionBrief": "Dark glassmorphism browser start page, cool neutral dark palette with purple accent, Inter + Noto Sans SC, dense information layout with sidebar navigation and card grid, gaming-inspired aesthetic",
"designRead": "Browser homepage / power users / dark glassmorphism utility / medium-high density / avoid pastel or light themes",
"designDials": {
"layoutVariance": 2,
"motionIntensity": 2,
"visualDensity": 4
},
"styleContinuityAnchors": {
"colorSystem": {
"primaryColorRole": "Purple accent (#6c5ce7) for active states and interactive elements",
"brandHuePolicy": "Dark base palette with single purple brand hue; categories and identities use text/icons/neutral tints",
"stateColors": "success/warning/error/info semantic tokens for status indicators"
},
"shapeSystem": "Rounded corners, radius scale 4-16px, glassmorphism cards with subtle borders",
"typographySystem": "Inter + Noto Sans SC, sans-serif only, clean weight hierarchy 300-700",
"spacingSystem": "Compact 4-8-12-16-20-24 rhythm, dense sidebar + spacious card grid",
"componentLanguage": "Glass cards with translucent backgrounds, subtle borders, no heavy shadows on static elements",
"surfaceAndDepth": "Static surfaces use border + translucent bg; floating layers (dropdown, modal) use deeper shadows alpha 0.25-0.35",
"imageryAndIconography": "SVG icons for navigation, brand favicons for link cards, dark background aesthetic",
"interactionTone": "Subtle hover feedback, smooth transitions, clean focus states"
},
"specsConstraints": null,
"sharedProjectShellContract": {
"navigationShell": "Left sidebar with two-level category navigation, collapsible on mobile",
"primaryColorSystem": "Single purple accent hue #6c5ce7 for active states and primary interactions",
"typographySystem": "Inter + Noto Sans SC, text-xs(11px) to text-3xl(32px), weights 300-700",
"radiusScale": "sm:4px, md:8px, lg:12px, xl:16px, full:9999px",
"surfaceDepthModel": "Static surfaces: border + translucent bg, shadow alpha <=0.05. Floating layers: shadow alpha 0.25-0.35",
"ctaStyle": "Rounded buttons with glass effect, subtle hover transitions",
"alignmentRules": "Left-aligned content areas, centered search bar, grid layout for cards"
},
"generationTree": {
"root": {
"nodeId": "gen-project-shell",
"kind": "project-shell",
"pageIds": ["page-desktop", "page-desktop-settings", "page-mobile"],
"output": "partials/project-shell.html",
"sharedRegions": ["brand css variables", "color palette", "glassmorphism mixin"],
"privateRegions": [],
"mutableSlots": ["pageTitle", "viewportLayout"],
"status": "generated",
"children": [
{
"nodeId": "gen-page-desktop",
"kind": "page-leaf",
"pageIds": ["page-desktop"],
"output": "pages/desktop.html",
"sharedRegions": [],
"privateRegions": ["sidebar with two-level nav", "search bar with engine selector", "link card grid"],
"mutableSlots": ["pageTitle"],
"status": "generated",
"children": []
},
{
"nodeId": "gen-page-desktop-settings",
"kind": "page-leaf",
"pageIds": ["page-desktop-settings"],
"output": "pages/desktop-settings.html",
"sharedRegions": [],
"privateRegions": ["settings popover panel", "theme switch (light/dark/auto)", "accent color picker", "background image picker"],
"mutableSlots": ["pageTitle"],
"status": "generated",
"children": []
},
{
"nodeId": "gen-page-mobile",
"kind": "page-leaf",
"pageIds": ["page-mobile"],
"output": "pages/mobile.html",
"sharedRegions": [],
"privateRegions": ["top row hamburger + search + settings gear", "navigation drawer with avatar/profile", "vertical link list", "FAB"],
"mutableSlots": ["pageTitle"],
"status": "generated",
"children": []
}
]
}
}
},
"designSource": {
"operatingMode": "free-explore",
"libraryIdentity": {
"name": null,
"id": null,
"version": null,
"scope": null,
"path": null,
"versionSource": null
},
"cssFilePath": "D:\\Code\\MyHomePage\\browser-homepage\\colors_and_type.css",
"brandPrefix": "bh",
"themeMode": "dark",
"designDecisionSummary": "Dark glassmorphism theme with purple accent, translucent cards, two-level sidebar navigation, Inter+Noto Sans SC typography",
"styleConstraints": {
"radiusMax": 16,
"spacingBase": 4,
"fontSizeBody": 14,
"fontSizeMin": 11,
"controlHeightDefault": 36,
"controlHeightLarge": 42
},
"productContext": {
"kitType": null,
"productType": "Browser homepage / navigation tool"
},
"actualTokenNameReference": []
},
"pages": [
{
"nodeId": "page-desktop",
"slug": "desktop",
"title": "浏览器首页 - 桌面端",
"htmlSrc": "pages/desktop.html",
"pageIndex": 1,
"stateGroupId": null,
"stateRole": null,
"baseStatePageId": null,
"sharedShellContract": [],
"mutableRegions": [],
"derivedFromHtmlSrc": null,
"derivationType": "original",
"sourcePageId": null,
"sourceHtmlSrc": null,
"pageType": "information-dense",
"businessScenario": "Desktop browser homepage with two-level category sidebar, search bar with engine selector, and link card grid",
"visualNorthStar": "Dark glassmorphism sidebar + translucent card grid, dense information architecture, purple accent highlights",
"compositionPattern": "Two-column asymmetric layout: fixed sidebar (20% width) + scrollable main content (80% width)",
"continuityAnchors": ["dark glassmorphism card style", "purple accent for active states", "Inter + Noto Sans SC typography"],
"libraryRestraintMode": false,
"uiKitPath": null,
"componentPlan": [],
"imagePlan": [],
"chartsRequired": false,
"miniProgramStyle": false,
"qualityRisks": ["dense sidebar navigation may need careful spacing", "two-level nav expand/collapse interaction"]
},
{
"nodeId": "page-desktop-settings",
"slug": "desktop-settings",
"title": "浏览器首页 - 桌面端 - 设置面板",
"htmlSrc": "pages/desktop-settings.html",
"pageIndex": 2,
"stateGroupId": null,
"stateRole": null,
"baseStatePageId": null,
"sharedShellContract": [],
"mutableRegions": [],
"derivedFromHtmlSrc": "pages/desktop.html",
"derivationType": "comparison-from-source",
"sourcePageId": "page-desktop",
"sourceHtmlSrc": "pages/desktop.html",
"pageType": "information-dense",
"businessScenario": "Desktop browser homepage with the settings popover panel opened, showing theme/accent/background controls",
"visualNorthStar": "Same dark glassmorphism base as desktop page, with floating settings popover anchored to the settings button, dimmed background",
"compositionPattern": "Source page layout skeleton + floating settings popover (right-aligned) anchored from the settings button",
"continuityAnchors": ["dark glassmorphism card style", "purple accent for active states", "Inter + Noto Sans SC typography"],
"libraryRestraintMode": false,
"uiKitPath": null,
"componentPlan": [],
"imagePlan": [],
"chartsRequired": false,
"miniProgramStyle": false,
"qualityRisks": ["popover must not overlap sidebar critical content", "background dim layer should not fully hide source context"]
},
{
"nodeId": "page-mobile",
"slug": "mobile",
"title": "浏览器首页 - 移动端",
"htmlSrc": "pages/mobile.html",
"pageIndex": 3,
"stateGroupId": null,
"stateRole": null,
"baseStatePageId": null,
"sharedShellContract": [],
"mutableRegions": [],
"derivedFromHtmlSrc": null,
"derivationType": "original",
"sourcePageId": null,
"sourceHtmlSrc": null,
"pageType": "information-dense",
"businessScenario": "Mobile browser homepage with single-row top bar (hamburger + search + settings gear), avatar/profile relocated into hamburger drawer, vertical link list and FAB",
"visualNorthStar": "Compact mobile-first dark layout, single-row top bar with hamburger + search + gear, drawer for categories + profile",
"compositionPattern": "Single-column mobile layout: single-row top bar + horizontal category tabs + vertical link list + bottom-right FAB",
"continuityAnchors": ["dark glassmorphism card style", "purple accent for active states", "Inter + Noto Sans SC typography"],
"libraryRestraintMode": false,
"uiKitPath": null,
"componentPlan": [],
"imagePlan": [],
"chartsRequired": false,
"miniProgramStyle": false,
"qualityRisks": ["top row three elements must not crowd search input", "touch targets must be >= 44px"]
}
],
"assets": [],
"wiringPlan": [],
"hiddenInteractionPlan": []
}
@@ -0,0 +1,493 @@
<!DOCTYPE html>
<html lang="zh-CN" class="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>浏览器首页 - 设置</title>
<style id="theme-vars">
/* ===== Browser Homepage - Brand CSS ===== */
/* Dark theme with glassmorphism, inspired by gaming browser start pages */
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Noto+Sans+SC:wght@300;400;500;600;700&display=swap');
:root {
/* ---- Brand Colors ---- */
--color-bg-primary: #0d0d12;
--color-bg-secondary: #16161e;
--color-bg-tertiary: #1e1e2a;
--color-bg-card: rgba(30, 30, 42, 0.65);
--color-bg-card-hover: rgba(40, 40, 56, 0.75);
--color-bg-sidebar: rgba(13, 13, 18, 0.85);
--color-bg-search: rgba(22, 22, 30, 0.8);
--color-bg-input: rgba(30, 30, 42, 0.5);
--color-bg-overlay: rgba(0, 0, 0, 0.5);
/* Text */
--color-text-primary: #e8e8f0;
--color-text-secondary: #9494a8;
--color-text-muted: #5e5e72;
--color-text-inverse: #0d0d12;
/* Brand accent */
--color-brand: #6c5ce7;
--color-brand-light: #a29bfe;
--color-brand-hover: #7d6ff0;
/* Border */
--color-border: rgba(255, 255, 255, 0.06);
--color-border-hover: rgba(255, 255, 255, 0.12);
--color-border-active: rgba(108, 92, 231, 0.5);
/* State colors */
--state-success: #00b894;
--state-warning: #fdcb6e;
--state-error: #e17055;
--state-info: #74b9ff;
/* ---- Typography ---- */
--font-sans: 'Inter', 'Noto Sans SC', -apple-system, BlinkMacSystemFont, sans-serif;
--font-heading: 'Inter', 'Noto Sans SC', sans-serif;
--text-xs: 11px;
--text-sm: 13px;
--text-base: 14px;
--text-lg: 16px;
--text-xl: 20px;
--text-2xl: 24px;
--text-3xl: 32px;
--leading-tight: 1.25;
--leading-normal: 1.5;
--leading-relaxed: 1.625;
--tracking-tight: -0.01em;
--tracking-normal: 0em;
/* ---- Spacing ---- */
--space-1: 4px;
--space-2: 8px;
--space-3: 12px;
--space-4: 16px;
--space-5: 20px;
--space-6: 24px;
--space-8: 32px;
--space-10: 40px;
--space-12: 48px;
/* ---- Radius ---- */
--radius-sm: 4px;
--radius-md: 8px;
--radius-lg: 12px;
--radius-xl: 16px;
--radius-full: 9999px;
/* ---- Shadows ---- */
--shadow-card: 0 1px 3px rgba(0, 0, 0, 0.05);
--shadow-float: 0 8px 32px rgba(0, 0, 0, 0.25);
--shadow-dropdown: 0 12px 48px rgba(0, 0, 0, 0.35);
/* ---- Glassmorphism ---- */
--glass-bg: rgba(30, 30, 42, 0.65);
--glass-blur: 12px;
--glass-border: 1px solid rgba(255, 255, 255, 0.08);
/* ---- Transitions ---- */
--transition-fast: 150ms ease;
--transition-normal: 250ms ease;
--transition-slow: 400ms ease;
/* ---- Sidebar ---- */
--sidebar-width: 240px;
--sidebar-collapsed-width: 64px;
/* ---- Z-index ---- */
--z-sidebar: 100;
--z-dropdown: 200;
--z-modal: 300;
--z-tooltip: 400;
}
</style>
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4.3.1/dist/index.global.js"></script>
<script src="https://unpkg.com/lucide@1.8.0/dist/umd/lucide.min.js"></script>
<style type="text/tailwindcss">
@theme inline {
--color-border: var(--color-border);
}
@layer base {
body { background: var(--color-bg-primary); color: var(--color-text-primary); }
td, th { @apply break-words; word-break: break-all; word-break: auto-phrase; }
th { @apply whitespace-nowrap; }
}
</style>
<style>
.no-scrollbar::-webkit-scrollbar { display: none; }
.no-scrollbar { -ms-overflow-style: none; scrollbar-width: none; }
[data-icon] {
display: inline-flex;
align-items: center;
justify-content: center;
-webkit-mask-size: contain;
mask-size: contain;
-webkit-mask-repeat: no-repeat;
mask-repeat: no-repeat;
-webkit-mask-position: center;
mask-position: center;
background-color: currentColor;
}
</style>
</head>
<body class="min-h-screen font-sans antialiased" style="font-family: var(--font-sans);">
<main class="flex min-h-screen relative">
<!-- ===== SOURCE PAGE (DIMMED) ===== -->
<div class="source-page relative w-full" style="filter: brightness(0.55); pointer-events: none;">
<!-- LEFT SIDEBAR -->
<aside class="fixed left-0 top-0 bottom-0 z-[var(--z-sidebar)] flex flex-col" style="width: var(--sidebar-width); background: var(--color-bg-sidebar); border-right: var(--glass-border); backdrop-filter: blur(var(--glass-blur)); -webkit-backdrop-filter: blur(var(--glass-blur));">
<!-- User avatar area -->
<div class="flex items-center gap-3 px-5 py-5" style="border-bottom: var(--glass-border);">
<div class="w-9 h-9 rounded-full flex items-center justify-center shrink-0" style="background: var(--color-brand); color: var(--color-text-inverse); font-size: var(--text-sm); font-weight: 600;">U</div>
<div class="min-w-0 flex-1">
<p class="truncate" style="font-size: var(--text-sm); font-weight: 600; color: var(--color-text-primary);">User</p>
<p class="truncate" style="font-size: 11px; color: var(--color-text-muted);">myhomepage</p>
</div>
</div>
<!-- Navigation tree -->
<nav class="flex-1 overflow-y-auto py-3 px-3 no-scrollbar" aria-label="Category navigation">
<a href="#" class="nav-item active flex items-center gap-3 px-3 py-2 rounded-lg mb-1 transition-colors duration-150" data-dom-id="nav-home" style="color: var(--color-brand); background: rgba(108, 92, 231, 0.12);" data-nav-key="home" data-active="true">
<i data-lucide="home" class="w-5 h-5 shrink-0"></i>
<span style="font-size: var(--text-base); font-weight: 500;">主页</span>
</a>
<div class="mb-1">
<button class="nav-item flex items-center gap-3 px-3 py-2 rounded-lg w-full transition-colors duration-150 cursor-pointer" style="color: var(--color-text-secondary); background: transparent;" aria-expanded="true">
<i data-lucide="wrench" class="w-5 h-5 shrink-0"></i>
<span class="flex-1 text-left" style="font-size: var(--text-base); font-weight: 500;">常用工具</span>
<i data-lucide="chevron-down" class="w-4 h-4 shrink-0"></i>
</button>
<div class="sub-nav ml-5 mt-1 space-y-0.5">
<a href="#" class="sub-nav-item flex items-center gap-3 px-3 py-1.5 rounded-md transition-colors duration-150" style="color: var(--color-text-muted); background: transparent;">
<i data-lucide="search" class="w-4 h-4 shrink-0"></i>
<span style="font-size: var(--text-sm);">搜索引擎</span>
</a>
<a href="#" class="sub-nav-item flex items-center gap-3 px-3 py-1.5 rounded-md transition-colors duration-150" style="color: var(--color-text-muted); background: transparent;">
<i data-lucide="bot" class="w-4 h-4 shrink-0"></i>
<span style="font-size: var(--text-sm);">AI工具</span>
</a>
<a href="#" class="sub-nav-item flex items-center gap-3 px-3 py-1.5 rounded-md transition-colors duration-150" style="color: var(--color-text-muted); background: transparent;">
<i data-lucide="code-2" class="w-4 h-4 shrink-0"></i>
<span style="font-size: var(--text-sm);">开发工具</span>
</a>
</div>
</div>
<a href="#" class="nav-item flex items-center gap-3 px-3 py-2 rounded-lg mb-1 transition-colors duration-150" style="color: var(--color-text-secondary); background: transparent;">
<i data-lucide="shopping-bag" class="w-5 h-5 shrink-0"></i>
<span style="font-size: var(--text-base); font-weight: 500;">购物</span>
</a>
<a href="#" class="nav-item flex items-center gap-3 px-3 py-2 rounded-lg mb-1 transition-colors duration-150" style="color: var(--color-text-secondary); background: transparent;">
<i data-lucide="play-circle" class="w-5 h-5 shrink-0"></i>
<span style="font-size: var(--text-base); font-weight: 500;">视频</span>
</a>
<a href="#" class="nav-item flex items-center gap-3 px-3 py-2 rounded-lg mb-1 transition-colors duration-150" style="color: var(--color-text-secondary); background: transparent;">
<i data-lucide="newspaper" class="w-5 h-5 shrink-0"></i>
<span style="font-size: var(--text-base); font-weight: 500;">资讯</span>
</a>
<a href="#" class="nav-item flex items-center gap-3 px-3 py-2 rounded-lg mb-1 transition-colors duration-150" style="color: var(--color-text-secondary); background: transparent;">
<i data-lucide="message-circle" class="w-5 h-5 shrink-0"></i>
<span style="font-size: var(--text-base); font-weight: 500;">社交</span>
</a>
</nav>
<!-- Settings at bottom (ACTIVE STATE) -->
<div class="px-3 pb-4" style="border-top: var(--glass-border);">
<a href="#" class="nav-item flex items-center gap-3 px-3 py-2 rounded-lg mt-3 transition-colors duration-150" data-dom-id="nav-settings-active" style="color: var(--color-brand); background: rgba(108, 92, 231, 0.18);" data-nav-key="settings" data-active="true">
<i data-lucide="settings" class="w-5 h-5 shrink-0"></i>
<span style="font-size: var(--text-base); font-weight: 500;">设置</span>
</a>
</div>
</aside>
<!-- RIGHT MAIN AREA -->
<div class="flex-1 flex flex-col min-h-screen" style="margin-left: var(--sidebar-width);">
<section class="flex items-center justify-center px-8 pt-10 pb-6" aria-label="Search">
<div class="relative w-full" style="max-width: 640px;">
<div class="flex items-center rounded-lg overflow-hidden" style="background: var(--color-bg-search); border: var(--glass-border); backdrop-filter: blur(var(--glass-blur)); -webkit-backdrop-filter: blur(var(--glass-blur)); height: 46px;">
<button class="flex items-center gap-2 px-3 h-full shrink-0" style="border-right: var(--glass-border); color: var(--color-text-secondary); background: transparent;">
<svg class="w-5 h-5" viewBox="0 0 24 24" fill="none" style="color: var(--color-brand);">
<circle cx="12" cy="12" r="4" stroke="currentColor" stroke-width="2"/>
<path d="M12 8V4" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
</svg>
<span style="font-size: var(--text-sm); font-weight: 500;">百度</span>
<i data-lucide="chevron-down" class="w-3.5 h-3.5"></i>
</button>
<input type="text" placeholder="搜索或输入网址" class="flex-1 h-full bg-transparent outline-none px-4" style="color: var(--color-text-primary); font-size: var(--text-base);">
<button class="flex items-center justify-center w-10 h-full shrink-0" style="color: var(--color-text-muted); background: transparent;" aria-label="Search">
<i data-lucide="search" class="w-5 h-5"></i>
</button>
</div>
</div>
</section>
<section class="flex-1 overflow-y-auto px-8 pb-8" aria-label="Link cards">
<div class="flex items-center justify-between mb-5" style="max-width: 1120px; margin-left: auto; margin-right: auto;">
<h1 style="font-size: var(--text-xl); font-weight: 600; color: var(--color-text-primary); text-wrap: balance; word-break: keep-all;">常用工具</h1>
<span style="font-size: var(--text-sm); color: var(--color-text-muted);">8 个链接</span>
</div>
<div class="grid gap-4" style="grid-template-columns: repeat(4, 1fr); max-width: 1120px; margin-left: auto; margin-right: auto;">
<a href="#" class="link-card group flex flex-col gap-3 rounded-lg p-4 transition-all duration-150" style="background: var(--color-bg-card); border: var(--glass-border); backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);">
<div class="flex items-center gap-3">
<div class="w-9 h-9 rounded-lg flex items-center justify-center shrink-0" style="background: rgba(16, 163, 127, 0.15);">
<svg class="w-5 h-5" viewBox="0 0 24 24" fill="none" style="color: #10a37f;"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z" fill="currentColor"/></svg>
</div>
<h3 style="font-size: var(--text-base); font-weight: 600; color: var(--color-text-primary);">ChatGPT</h3>
</div>
<p style="font-size: var(--text-sm); color: var(--color-text-secondary); line-height: var(--leading-normal);">AI对话助手,智能问答</p>
</a>
<a href="#" class="link-card group flex flex-col gap-3 rounded-lg p-4 transition-all duration-150" style="background: var(--color-bg-card); border: var(--glass-border); backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);">
<div class="flex items-center gap-3">
<div class="w-9 h-9 rounded-lg flex items-center justify-center shrink-0" style="background: rgba(230, 230, 230, 0.1);">
<i data-lucide="github" class="w-5 h-5" style="color: var(--color-text-primary);"></i>
</div>
<h3 style="font-size: var(--text-base); font-weight: 600; color: var(--color-text-primary);">GitHub</h3>
</div>
<p style="font-size: var(--text-sm); color: var(--color-text-secondary); line-height: var(--leading-normal);">代码托管与协作平台</p>
</a>
<a href="#" class="link-card group flex flex-col gap-3 rounded-lg p-4 transition-all duration-150" style="background: var(--color-bg-card); border: var(--glass-border); backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);">
<div class="flex items-center gap-3">
<div class="w-9 h-9 rounded-lg flex items-center justify-center shrink-0" style="background: rgba(244, 128, 36, 0.12);">
<svg class="w-5 h-5" viewBox="0 0 24 24" fill="none" style="color: #f48024;"><path d="M15.73 21.02l-.6-2.35h-6.26l-.6 2.35H4.67L8.2 7h7.6l3.53 14.02h-3.6zm-1.33-5.33l-2.2-8.52h-.2l-2.2 8.52h4.6zM2 22h20v2H2v-2z" fill="currentColor"/></svg>
</div>
<h3 style="font-size: var(--text-base); font-weight: 600; color: var(--color-text-primary);">Stack Overflow</h3>
</div>
<p style="font-size: var(--text-sm); color: var(--color-text-secondary); line-height: var(--leading-normal);">开发者问答社区</p>
</a>
<a href="#" class="link-card group flex flex-col gap-3 rounded-lg p-4 transition-all duration-150" style="background: var(--color-bg-card); border: var(--glass-border); backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);">
<div class="flex items-center gap-3">
<div class="w-9 h-9 rounded-lg flex items-center justify-center shrink-0" style="background: rgba(121, 79, 169, 0.15);">
<svg class="w-5 h-5" viewBox="0 0 24 24" fill="none" style="color: #794fa9;"><path d="M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>
</div>
<h3 style="font-size: var(--text-base); font-weight: 600; color: var(--color-text-primary);">MDN Web Docs</h3>
</div>
<p style="font-size: var(--text-sm); color: var(--color-text-secondary); line-height: var(--leading-normal);">Web 技术文档参考</p>
</a>
<a href="#" class="link-card group flex flex-col gap-3 rounded-lg p-4 transition-all duration-150" style="background: var(--color-bg-card); border: var(--glass-border); backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);">
<div class="flex items-center gap-3">
<div class="w-9 h-9 rounded-lg flex items-center justify-center shrink-0" style="background: rgba(0, 122, 204, 0.15);">
<i data-lucide="code-2" class="w-5 h-5" style="color: #007ac1;"></i>
</div>
<h3 style="font-size: var(--text-base); font-weight: 600; color: var(--color-text-primary);">VS Code</h3>
</div>
<p style="font-size: var(--text-sm); color: var(--color-text-secondary); line-height: var(--leading-normal);">轻量级代码编辑器</p>
</a>
<a href="#" class="link-card group flex flex-col gap-3 rounded-lg p-4 transition-all duration-150" style="background: var(--color-bg-card); border: var(--glass-border); backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);">
<div class="flex items-center gap-3">
<div class="w-9 h-9 rounded-lg flex items-center justify-center shrink-0" style="background: rgba(230, 230, 230, 0.08);">
<svg class="w-5 h-5" viewBox="0 0 24 24" fill="none" style="color: var(--color-text-primary);"><path d="M4.5 4.5h15v15h-15z" stroke="currentColor" stroke-width="1.5" rx="2"/><path d="M8 9h8M8 12h5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>
</div>
<h3 style="font-size: var(--text-base); font-weight: 600; color: var(--color-text-primary);">Notion</h3>
</div>
<p style="font-size: var(--text-sm); color: var(--color-text-secondary); line-height: var(--leading-normal);">笔记与协作空间</p>
</a>
<a href="#" class="link-card group flex flex-col gap-3 rounded-lg p-4 transition-all duration-150" style="background: var(--color-bg-card); border: var(--glass-border); backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);">
<div class="flex items-center gap-3">
<div class="w-9 h-9 rounded-lg flex items-center justify-center shrink-0" style="background: rgba(242, 78, 30, 0.12);">
<svg class="w-5 h-5" viewBox="0 0 24 24" fill="none" style="color: #f24e1e;"><path d="M8 2a3 3 0 100 6 3 3 0 000-6zm0 8a3 3 0 100 6 3 3 0 000-6zm0 8a3 3 0 100 6 3 3 0 000-6zm8-8a3 3 0 100 6 3 3 0 000-6z" stroke="currentColor" stroke-width="1.5"/><path d="M16 2a3 3 0 110 6V2z" fill="currentColor"/><path d="M16 8a3 3 0 110 6V8z" stroke="currentColor" stroke-width="1.5"/></svg>
</div>
<h3 style="font-size: var(--text-base); font-weight: 600; color: var(--color-text-primary);">Figma</h3>
</div>
<p style="font-size: var(--text-sm); color: var(--color-text-secondary); line-height: var(--leading-normal);">在线设计协作工具</p>
</a>
<a href="#" class="link-card group flex flex-col gap-3 rounded-lg p-4 transition-all duration-150" style="background: var(--color-bg-card); border: var(--glass-border); backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);">
<div class="flex items-center gap-3">
<div class="w-9 h-9 rounded-lg flex items-center justify-center shrink-0" style="background: rgba(140, 103, 255, 0.12);">
<svg class="w-5 h-5" viewBox="0 0 24 24" fill="none" style="color: #8c67ff;"><circle cx="12" cy="12" r="6" stroke="currentColor" stroke-width="1.5"/><path d="M12 9v3l2 2" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
</div>
<h3 style="font-size: var(--text-base); font-weight: 600; color: var(--color-text-primary);">Canva</h3>
</div>
<p style="font-size: var(--text-sm); color: var(--color-text-secondary); line-height: var(--leading-normal);">在线平面设计平台</p>
</a>
</div>
</section>
</div>
</div>
<!-- ===== DIM BACKDROP (covers source page, clickable to close) ===== -->
<div class="fixed inset-0 z-[var(--z-modal)]" data-dom-id="settings-backdrop" aria-label="Close settings" role="button" tabindex="0" style="background: rgba(0, 0, 0, 0.4); backdrop-filter: blur(2px); -webkit-backdrop-filter: blur(2px); cursor: pointer;"></div>
<!-- ===== SETTINGS POPOVER (OPEN STATE) ===== -->
<div class="settings-popover fixed z-[calc(var(--z-modal)+1)] flex flex-col" data-dom-id="settings-popover" role="dialog" aria-modal="true" aria-labelledby="settings-title"
style="left: calc(var(--sidebar-width) + 8px); bottom: 80px; width: 360px; max-height: calc(100vh - 100px);
background: rgba(22, 22, 30, 0.92); border: 1px solid rgba(255, 255, 255, 0.10); border-radius: var(--radius-xl);
box-shadow: var(--shadow-dropdown); backdrop-filter: blur(20px); -webkit-backdrop-filter: blur(20px);
overflow: hidden;">
<!-- Popover Header -->
<header class="flex items-center justify-between px-5 py-4 shrink-0" style="border-bottom: 1px solid var(--color-border);">
<div class="flex items-center gap-2">
<i data-lucide="settings" class="w-4 h-4" style="color: var(--color-brand);"></i>
<h2 id="settings-title" style="font-size: var(--text-base); font-weight: 600; color: var(--color-text-primary);">设置</h2>
</div>
<button class="settings-close flex items-center justify-center w-8 h-8 rounded-md transition-colors duration-150" data-dom-id="settings-close-btn" aria-label="Close settings"
style="color: var(--color-text-secondary); background: transparent;">
<i data-lucide="x" class="w-4 h-4"></i>
</button>
</header>
<!-- Popover Body (scrollable) -->
<div class="flex-1 overflow-y-auto px-5 py-5 no-scrollbar" style="scrollbar-width: thin;">
<!-- SECTION: Theme Mode -->
<section class="mb-6" aria-label="Theme mode">
<h3 class="uppercase mb-3" style="font-size: 11px; font-weight: 600; color: var(--color-text-muted); letter-spacing: 0.08em;">主题模式</h3>
<div class="grid grid-cols-3 gap-2">
<!-- Dark (selected) -->
<button class="theme-card selected flex flex-col items-center justify-center gap-2 py-3 rounded-lg transition-all duration-150" data-theme="dark" aria-pressed="true" data-dom-id="theme-dark"
style="background: rgba(108, 92, 231, 0.10); border: 1.5px solid var(--color-brand);">
<i data-lucide="moon" class="w-5 h-5" style="color: var(--color-brand);"></i>
<span style="font-size: var(--text-sm); font-weight: 500; color: var(--color-brand);">暗色</span>
</button>
<!-- Light -->
<button class="theme-card flex flex-col items-center justify-center gap-2 py-3 rounded-lg transition-all duration-150" data-theme="light" aria-pressed="false" data-dom-id="theme-light"
style="background: rgba(255, 255, 255, 0.03); border: 1px solid var(--color-border);">
<i data-lucide="sun" class="w-5 h-5" style="color: var(--color-text-secondary);"></i>
<span style="font-size: var(--text-sm); font-weight: 500; color: var(--color-text-secondary);">亮色</span>
</button>
<!-- Auto -->
<button class="theme-card flex flex-col items-center justify-center gap-2 py-3 rounded-lg transition-all duration-150" data-theme="auto" aria-pressed="false" data-dom-id="theme-auto"
style="background: rgba(255, 255, 255, 0.03); border: 1px solid var(--color-border);">
<i data-lucide="monitor" class="w-5 h-5" style="color: var(--color-text-secondary);"></i>
<span style="font-size: var(--text-sm); font-weight: 500; color: var(--color-text-secondary);">跟随系统</span>
</button>
</div>
</section>
<!-- SECTION: Accent Color -->
<section class="mb-6" aria-label="Accent color">
<h3 class="uppercase mb-3" style="font-size: 11px; font-weight: 600; color: var(--color-text-muted); letter-spacing: 0.08em;">主色调</h3>
<div class="flex items-center gap-4 px-1 py-1">
<!-- Purple (selected) -->
<button class="color-swatch selected relative w-8 h-8 rounded-full transition-all duration-150" data-color="#6c5ce7" aria-label="Purple accent" aria-pressed="true" data-dom-id="color-purple"
style="background: #6c5ce7; box-shadow: 0 0 0 2px var(--color-bg-tertiary), 0 0 0 4px #6c5ce7; outline: 2px solid #6c5ce7; outline-offset: -1px;">
<i data-lucide="check" class="w-4 h-4 absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2" style="color: #ffffff;"></i>
</button>
<!-- Blue -->
<button class="color-swatch relative w-8 h-8 rounded-full transition-all duration-150" data-color="#0984e3" aria-label="Blue accent" aria-pressed="false" data-dom-id="color-blue"
style="background: #0984e3;"></button>
<!-- Green -->
<button class="color-swatch relative w-8 h-8 rounded-full transition-all duration-150" data-color="#00b894" aria-label="Green accent" aria-pressed="false" data-dom-id="color-green"
style="background: #00b894;"></button>
<!-- Orange -->
<button class="color-swatch relative w-8 h-8 rounded-full transition-all duration-150" data-color="#e17055" aria-label="Orange accent" aria-pressed="false" data-dom-id="color-orange"
style="background: #e17055;"></button>
<!-- Pink -->
<button class="color-swatch relative w-8 h-8 rounded-full transition-all duration-150" data-color="#e84393" aria-label="Pink accent" aria-pressed="false" data-dom-id="color-pink"
style="background: #e84393;"></button>
</div>
</section>
<!-- SECTION: Background Image -->
<section aria-label="Background image">
<h3 class="uppercase mb-3" style="font-size: 11px; font-weight: 600; color: var(--color-text-muted); letter-spacing: 0.08em;">背景图</h3>
<!-- Wallpaper Grid (3 cols x 2 rows) -->
<div class="grid grid-cols-3 gap-2 mb-3">
<!-- WP1: Radial purple -->
<button class="wallpaper-thumb relative w-full rounded-md overflow-hidden transition-all duration-150 cursor-pointer" data-wp="wp1" aria-label="Wallpaper 1"
style="aspect-ratio: 16/10; background: radial-gradient(circle at 30% 30%, #6c5ce7 0%, #2d1b69 60%, #0d0d12 100%); border: 1.5px solid var(--color-brand);">
<span class="absolute top-1 right-1 flex items-center justify-center w-4 h-4 rounded-full" style="background: var(--color-brand);">
<i data-lucide="check" class="w-2.5 h-2.5" style="color: #ffffff;"></i>
</span>
</button>
<!-- WP2: Linear teal/blue -->
<button class="wallpaper-thumb relative w-full rounded-md overflow-hidden transition-all duration-150 cursor-pointer" data-wp="wp2" aria-label="Wallpaper 2"
style="aspect-ratio: 16/10; background: linear-gradient(135deg, #0984e3 0%, #74b9ff 50%, #0d0d12 100%); border: 1px solid var(--color-border);"></button>
<!-- WP3: Conic green/purple -->
<button class="wallpaper-thumb relative w-full rounded-md overflow-hidden transition-all duration-150 cursor-pointer" data-wp="wp3" aria-label="Wallpaper 3"
style="aspect-ratio: 16/10; background: conic-gradient(from 45deg at 50% 50%, #00b894 0deg, #6c5ce7 180deg, #0d0d12 360deg); border: 1px solid var(--color-border);"></button>
<!-- WP4: Sunset orange -->
<button class="wallpaper-thumb relative w-full rounded-md overflow-hidden transition-all duration-150 cursor-pointer" data-wp="wp4" aria-label="Wallpaper 4"
style="aspect-ratio: 16/10; background: linear-gradient(180deg, #e17055 0%, #fdcb6e 50%, #2d1b69 100%); border: 1px solid var(--color-border);"></button>
<!-- WP5: Pink mesh -->
<button class="wallpaper-thumb relative w-full rounded-md overflow-hidden transition-all duration-150 cursor-pointer" data-wp="wp5" aria-label="Wallpaper 5"
style="aspect-ratio: 16/10; background: radial-gradient(circle at 70% 80%, #e84393 0%, #6c5ce7 50%, #0d0d12 100%); border: 1px solid var(--color-border);"></button>
<!-- WP6: Deep blue radial -->
<button class="wallpaper-thumb relative w-full rounded-md overflow-hidden transition-all duration-150 cursor-pointer" data-wp="wp6" aria-label="Wallpaper 6"
style="aspect-ratio: 16/10; background: radial-gradient(ellipse at 50% 0%, #4834d4 0%, #19196e 50%, #0d0d12 100%); border: 1px solid var(--color-border);"></button>
<!-- Upload Custom (spans 2 cols) -->
<button class="wallpaper-upload col-span-2 flex items-center justify-center gap-2 rounded-md transition-colors duration-150 cursor-pointer" data-wp="upload" aria-label="Upload custom wallpaper" data-dom-id="upload-wallpaper"
style="aspect-ratio: 16/10; background: rgba(255, 255, 255, 0.03); border: 1.5px dashed var(--color-border-hover);">
<i data-lucide="upload-cloud" class="w-4 h-4" style="color: var(--color-text-muted);"></i>
<span style="font-size: var(--text-sm); color: var(--color-text-muted);">上传自定义</span>
</button>
</div>
<!-- Clear wallpaper button -->
<button class="clear-wallpaper flex items-center justify-center gap-2 w-full py-2 rounded-md transition-colors duration-150 cursor-pointer" data-dom-id="clear-wallpaper-btn" aria-label="Clear wallpaper (solid color)"
style="background: transparent; border: 1px solid var(--color-border);">
<i data-lucide="image-off" class="w-4 h-4" style="color: var(--color-text-secondary);"></i>
<span style="font-size: var(--text-sm); color: var(--color-text-secondary);">纯色背景</span>
</button>
</section>
</div>
</div>
</main>
<script>lucide.createIcons();</script>
<script>
// Theme mode switching
document.querySelectorAll('.theme-card').forEach(function(card) {
card.addEventListener('click', function() {
document.querySelectorAll('.theme-card').forEach(function(c) {
c.classList.remove('selected');
c.setAttribute('aria-pressed', 'false');
c.style.background = 'rgba(255, 255, 255, 0.03)';
c.style.border = '1px solid var(--color-border)';
var icon = c.querySelector('i[data-lucide]');
var span = c.querySelector('span');
if (icon) icon.style.color = 'var(--color-text-secondary)';
if (span) span.style.color = 'var(--color-text-secondary)';
});
this.classList.add('selected');
this.setAttribute('aria-pressed', 'true');
this.style.background = 'rgba(108, 92, 231, 0.10)';
this.style.border = '1.5px solid var(--color-brand)';
var icon = this.querySelector('i[data-lucide]');
var span = this.querySelector('span');
if (icon) icon.style.color = 'var(--color-brand)';
if (span) span.style.color = 'var(--color-brand)';
});
});
// Color swatch selection
document.querySelectorAll('.color-swatch').forEach(function(sw) {
sw.addEventListener('click', function() {
document.querySelectorAll('.color-swatch').forEach(function(s) {
s.classList.remove('selected');
s.setAttribute('aria-pressed', 'false');
s.style.boxShadow = 'none';
s.style.outline = 'none';
});
this.classList.add('selected');
this.setAttribute('aria-pressed', 'true');
var color = this.getAttribute('data-color');
this.style.boxShadow = '0 0 0 2px var(--color-bg-tertiary), 0 0 0 4px ' + color;
this.style.outline = '2px solid ' + color;
this.style.outlineOffset = '-1px';
});
});
// Wallpaper thumbnail selection
document.querySelectorAll('.wallpaper-thumb').forEach(function(wp) {
wp.addEventListener('click', function() {
document.querySelectorAll('.wallpaper-thumb').forEach(function(w) {
w.style.border = '1px solid var(--color-border)';
var check = w.querySelector('span');
if (check) check.style.display = 'none';
});
this.style.border = '1.5px solid var(--color-brand)';
var check = this.querySelector('span');
if (check) check.style.display = 'flex';
});
});
</script>
</body>
</html>
+576
View File
@@ -0,0 +1,576 @@
<!DOCTYPE html>
<html lang="zh-CN" class="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>浏览器首页 - 桌面端</title>
<style id="theme-vars">
/* ===== Browser Homepage - Brand CSS ===== */
/* Dark theme with glassmorphism, inspired by gaming browser start pages */
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Noto+Sans+SC:wght@300;400;500;600;700&display=swap');
:root {
/* ---- Brand Colors ---- */
--color-bg-primary: #0d0d12;
--color-bg-secondary: #16161e;
--color-bg-tertiary: #1e1e2a;
--color-bg-card: rgba(30, 30, 42, 0.65);
--color-bg-card-hover: rgba(40, 40, 56, 0.75);
--color-bg-sidebar: rgba(13, 13, 18, 0.85);
--color-bg-search: rgba(22, 22, 30, 0.8);
--color-bg-input: rgba(30, 30, 42, 0.5);
--color-bg-overlay: rgba(0, 0, 0, 0.5);
/* Text */
--color-text-primary: #e8e8f0;
--color-text-secondary: #9494a8;
--color-text-muted: #5e5e72;
--color-text-inverse: #0d0d12;
/* Brand accent */
--color-brand: #6c5ce7;
--color-brand-light: #a29bfe;
--color-brand-hover: #7d6ff0;
/* Border */
--color-border: rgba(255, 255, 255, 0.06);
--color-border-hover: rgba(255, 255, 255, 0.12);
--color-border-active: rgba(108, 92, 231, 0.5);
/* State colors */
--state-success: #00b894;
--state-warning: #fdcb6e;
--state-error: #e17055;
--state-info: #74b9ff;
/* ---- Typography ---- */
--font-sans: 'Inter', 'Noto Sans SC', -apple-system, BlinkMacSystemFont, sans-serif;
--font-heading: 'Inter', 'Noto Sans SC', sans-serif;
--text-xs: 11px;
--text-sm: 13px;
--text-base: 14px;
--text-lg: 16px;
--text-xl: 20px;
--text-2xl: 24px;
--text-3xl: 32px;
--leading-tight: 1.25;
--leading-normal: 1.5;
--leading-relaxed: 1.625;
--tracking-tight: -0.01em;
--tracking-normal: 0em;
/* ---- Spacing ---- */
--space-1: 4px;
--space-2: 8px;
--space-3: 12px;
--space-4: 16px;
--space-5: 20px;
--space-6: 24px;
--space-8: 32px;
--space-10: 40px;
--space-12: 48px;
/* ---- Radius ---- */
--radius-sm: 4px;
--radius-md: 8px;
--radius-lg: 12px;
--radius-xl: 16px;
--radius-full: 9999px;
/* ---- Shadows ---- */
--shadow-card: 0 1px 3px rgba(0, 0, 0, 0.05);
--shadow-float: 0 8px 32px rgba(0, 0, 0, 0.25);
--shadow-dropdown: 0 12px 48px rgba(0, 0, 0, 0.35);
/* ---- Glassmorphism ---- */
--glass-bg: rgba(30, 30, 42, 0.65);
--glass-blur: 12px;
--glass-border: 1px solid rgba(255, 255, 255, 0.08);
/* ---- Transitions ---- */
--transition-fast: 150ms ease;
--transition-normal: 250ms ease;
--transition-slow: 400ms ease;
/* ---- Sidebar ---- */
--sidebar-width: 240px;
--sidebar-collapsed-width: 64px;
/* ---- Z-index ---- */
--z-sidebar: 100;
--z-dropdown: 200;
--z-modal: 300;
--z-tooltip: 400;
}
</style>
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4.3.1/dist/index.global.js"></script>
<script src="https://unpkg.com/lucide@1.8.0/dist/umd/lucide.min.js"></script>
<style type="text/tailwindcss">
@theme inline {
--color-border: var(--color-border);
}
@layer base {
body { background: var(--color-bg-primary); color: var(--color-text-primary); }
td, th { @apply break-words; word-break: break-all; word-break: auto-phrase; }
th { @apply whitespace-nowrap; }
}
</style>
<style>
.no-scrollbar::-webkit-scrollbar { display: none; }
.no-scrollbar { -ms-overflow-style: none; scrollbar-width: none; }
[data-icon] {
display: inline-flex;
align-items: center;
justify-content: center;
-webkit-mask-size: contain;
mask-size: contain;
-webkit-mask-repeat: no-repeat;
mask-repeat: no-repeat;
-webkit-mask-position: center;
mask-position: center;
background-color: currentColor;
}
</style>
</head>
<body class="min-h-screen font-sans antialiased" style="font-family: var(--font-sans);">
<main class="flex min-h-screen">
<!-- ===== LEFT SIDEBAR ===== -->
<aside class="fixed left-0 top-0 bottom-0 z-[var(--z-sidebar)] flex flex-col" style="width: var(--sidebar-width); background: var(--color-bg-sidebar); border-right: var(--glass-border); backdrop-filter: blur(var(--glass-blur)); -webkit-backdrop-filter: blur(var(--glass-blur));">
<!-- User avatar area -->
<div class="flex items-center gap-3 px-5 py-5" style="border-bottom: var(--glass-border);">
<div class="w-9 h-9 rounded-full flex items-center justify-center shrink-0" style="background: var(--color-brand); color: var(--color-text-inverse); font-size: var(--text-sm); font-weight: 600;">U</div>
<div class="min-w-0 flex-1">
<p class="truncate" style="font-size: var(--text-sm); font-weight: 600; color: var(--color-text-primary);">User</p>
<p class="truncate" style="font-size: 11px; color: var(--color-text-muted);">myhomepage</p>
</div>
</div>
<!-- Navigation tree -->
<nav class="flex-1 overflow-y-auto py-3 px-3 no-scrollbar" aria-label="Category navigation">
<!-- 主页 -->
<a href="#" class="nav-item active flex items-center gap-3 px-3 py-2 rounded-lg mb-1 transition-colors duration-150" data-dom-id="nav-home" style="color: var(--color-brand); background: rgba(108, 92, 231, 0.12);" data-nav-key="home" data-active="true">
<i data-lucide="home" class="w-5 h-5 shrink-0"></i>
<span style="font-size: var(--text-base); font-weight: 500;">主页</span>
</a>
<!-- 常用工具 (expandable) -->
<div class="mb-1">
<button class="nav-item flex items-center gap-3 px-3 py-2 rounded-lg w-full transition-colors duration-150 cursor-pointer" data-dom-id="nav-tools-toggle" style="color: var(--color-text-secondary); background: transparent;" data-nav-key="tools" data-active="false" aria-expanded="true">
<i data-lucide="wrench" class="w-5 h-5 shrink-0"></i>
<span class="flex-1 text-left" style="font-size: var(--text-base); font-weight: 500;">常用工具</span>
<i data-lucide="chevron-down" class="w-4 h-4 shrink-0 transition-transform duration-200" style="transform: rotate(0deg);"></i>
</button>
<div class="sub-nav ml-5 mt-1 space-y-0.5" data-dom-id="nav-tools-sub">
<a href="#" class="sub-nav-item flex items-center gap-3 px-3 py-1.5 rounded-md transition-colors duration-150" data-dom-id="nav-search-engines" style="color: var(--color-text-muted); background: transparent;">
<i data-lucide="search" class="w-4 h-4 shrink-0"></i>
<span style="font-size: var(--text-sm);">搜索引擎</span>
</a>
<a href="#" class="sub-nav-item flex items-center gap-3 px-3 py-1.5 rounded-md transition-colors duration-150" data-dom-id="nav-ai-tools" style="color: var(--color-text-muted); background: transparent;">
<i data-lucide="bot" class="w-4 h-4 shrink-0"></i>
<span style="font-size: var(--text-sm);">AI工具</span>
</a>
<a href="#" class="sub-nav-item flex items-center gap-3 px-3 py-1.5 rounded-md transition-colors duration-150" data-dom-id="nav-dev-tools" style="color: var(--color-text-muted); background: transparent;">
<i data-lucide="code-2" class="w-4 h-4 shrink-0"></i>
<span style="font-size: var(--text-sm);">开发工具</span>
</a>
</div>
</div>
<!-- 购物 (expandable) -->
<div class="mb-1">
<button class="nav-item flex items-center gap-3 px-3 py-2 rounded-lg w-full transition-colors duration-150 cursor-pointer" data-dom-id="nav-shopping-toggle" style="color: var(--color-text-secondary); background: transparent;" data-nav-key="shopping" data-active="false" aria-expanded="false">
<i data-lucide="shopping-bag" class="w-5 h-5 shrink-0"></i>
<span class="flex-1 text-left" style="font-size: var(--text-base); font-weight: 500;">购物</span>
<i data-lucide="chevron-down" class="w-4 h-4 shrink-0 transition-transform duration-200" style="transform: rotate(-90deg);"></i>
</button>
<div class="sub-nav ml-5 mt-1 space-y-0.5 hidden" data-dom-id="nav-shopping-sub">
<a href="#" class="sub-nav-item flex items-center gap-3 px-3 py-1.5 rounded-md transition-colors duration-150" style="color: var(--color-text-muted); background: transparent;">
<i data-lucide="store" class="w-4 h-4 shrink-0"></i>
<span style="font-size: var(--text-sm);">电商</span>
</a>
<a href="#" class="sub-nav-item flex items-center gap-3 px-3 py-1.5 rounded-md transition-colors duration-150" style="color: var(--color-text-muted); background: transparent;">
<i data-lucide="smartphone" class="w-4 h-4 shrink-0"></i>
<span style="font-size: var(--text-sm);">数码</span>
</a>
</div>
</div>
<!-- 视频 (expandable) -->
<div class="mb-1">
<button class="nav-item flex items-center gap-3 px-3 py-2 rounded-lg w-full transition-colors duration-150 cursor-pointer" data-dom-id="nav-video-toggle" style="color: var(--color-text-secondary); background: transparent;" data-nav-key="video" data-active="false" aria-expanded="false">
<i data-lucide="play-circle" class="w-5 h-5 shrink-0"></i>
<span class="flex-1 text-left" style="font-size: var(--text-base); font-weight: 500;">视频</span>
<i data-lucide="chevron-down" class="w-4 h-4 shrink-0 transition-transform duration-200" style="transform: rotate(-90deg);"></i>
</button>
<div class="sub-nav ml-5 mt-1 space-y-0.5 hidden" data-dom-id="nav-video-sub">
<a href="#" class="sub-nav-item flex items-center gap-3 px-3 py-1.5 rounded-md transition-colors duration-150" style="color: var(--color-text-muted); background: transparent;">
<i data-lucide="tv" class="w-4 h-4 shrink-0"></i>
<span style="font-size: var(--text-sm);">影视</span>
</a>
<a href="#" class="sub-nav-item flex items-center gap-3 px-3 py-1.5 rounded-md transition-colors duration-150" style="color: var(--color-text-muted); background: transparent;">
<i data-lucide="radio" class="w-4 h-4 shrink-0"></i>
<span style="font-size: var(--text-sm);">直播</span>
</a>
</div>
</div>
<!-- 资讯 (expandable) -->
<div class="mb-1">
<button class="nav-item flex items-center gap-3 px-3 py-2 rounded-lg w-full transition-colors duration-150 cursor-pointer" data-dom-id="nav-news-toggle" style="color: var(--color-text-secondary); background: transparent;" data-nav-key="news" data-active="false" aria-expanded="false">
<i data-lucide="newspaper" class="w-5 h-5 shrink-0"></i>
<span class="flex-1 text-left" style="font-size: var(--text-base); font-weight: 500;">资讯</span>
<i data-lucide="chevron-down" class="w-4 h-4 shrink-0 transition-transform duration-200" style="transform: rotate(-90deg);"></i>
</button>
<div class="sub-nav ml-5 mt-1 space-y-0.5 hidden" data-dom-id="nav-news-sub">
<a href="#" class="sub-nav-item flex items-center gap-3 px-3 py-1.5 rounded-md transition-colors duration-150" style="color: var(--color-text-muted); background: transparent;">
<i data-lucide="cpu" class="w-4 h-4 shrink-0"></i>
<span style="font-size: var(--text-sm);">科技</span>
</a>
<a href="#" class="sub-nav-item flex items-center gap-3 px-3 py-1.5 rounded-md transition-colors duration-150" style="color: var(--color-text-muted); background: transparent;">
<i data-lucide="globe" class="w-4 h-4 shrink-0"></i>
<span style="font-size: var(--text-sm);">新闻</span>
</a>
</div>
</div>
<!-- 社交 -->
<a href="#" class="nav-item flex items-center gap-3 px-3 py-2 rounded-lg mb-1 transition-colors duration-150" data-dom-id="nav-social" style="color: var(--color-text-secondary); background: transparent;" data-nav-key="social" data-active="false">
<i data-lucide="message-circle" class="w-5 h-5 shrink-0"></i>
<span style="font-size: var(--text-base); font-weight: 500;">社交</span>
</a>
</nav>
<!-- Settings at bottom -->
<div class="px-3 pb-4" style="border-top: var(--glass-border);">
<a href="#" class="nav-item flex items-center gap-3 px-3 py-2 rounded-lg mt-3 transition-colors duration-150" data-dom-id="nav-settings" style="color: var(--color-text-muted); background: transparent;" data-nav-key="settings" data-active="false">
<i data-lucide="settings" class="w-5 h-5 shrink-0"></i>
<span style="font-size: var(--text-base); font-weight: 500;">设置</span>
</a>
</div>
</aside>
<!-- ===== RIGHT MAIN AREA ===== -->
<div class="flex-1 flex flex-col min-h-screen" style="margin-left: var(--sidebar-width);">
<!-- Search Bar Section -->
<section class="flex items-center justify-center px-8 pt-10 pb-6" aria-label="Search">
<div class="relative w-full" style="max-width: 640px;">
<div class="flex items-center rounded-lg overflow-hidden" style="background: var(--color-bg-search); border: var(--glass-border); backdrop-filter: blur(var(--glass-blur)); -webkit-backdrop-filter: blur(var(--glass-blur)); height: 46px;" data-dom-id="search-bar">
<!-- Engine Selector -->
<button class="flex items-center gap-2 px-3 h-full shrink-0 transition-colors duration-150" style="border-right: var(--glass-border); color: var(--color-text-secondary); background: transparent;" data-dom-id="search-engine-btn" aria-label="Select search engine" aria-haspopup="listbox">
<svg class="w-5 h-5" viewBox="0 0 24 24" fill="none" style="color: var(--color-brand);">
<circle cx="12" cy="12" r="4" stroke="currentColor" stroke-width="2"/>
<path d="M12 8V4" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
<path d="M15.5 14.5L18 17" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
<path d="M8.5 14.5L6 17" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
</svg>
<span style="font-size: var(--text-sm); font-weight: 500;">百度</span>
<i data-lucide="chevron-down" class="w-3.5 h-3.5"></i>
</button>
<!-- Search Input -->
<input type="text" placeholder="搜索或输入网址" class="flex-1 h-full bg-transparent outline-none px-4" style="color: var(--color-text-primary); font-size: var(--text-base);" data-dom-id="search-input" aria-label="Search input">
<!-- Search Button -->
<button class="flex items-center justify-center w-10 h-full shrink-0 transition-colors duration-150" style="color: var(--color-text-muted); background: transparent;" data-dom-id="search-btn" aria-label="Search">
<i data-lucide="search" class="w-5 h-5"></i>
</button>
</div>
<!-- Engine Dropdown (hidden by default, shown on engine button click) -->
<div class="absolute left-0 top-full mt-1 w-48 rounded-lg py-1 hidden" style="background: var(--color-bg-tertiary); border: var(--glass-border); box-shadow: var(--shadow-dropdown); backdrop-filter: blur(16px); -webkit-backdrop-filter: blur(16px); z-index: var(--z-dropdown);" data-dom-id="search-engine-dropdown" role="listbox">
<button class="engine-option flex items-center gap-3 w-full px-4 py-2.5 transition-colors duration-150" style="color: var(--color-text-primary); background: transparent;" role="option" aria-selected="true" data-engine="baidu">
<svg class="w-4 h-4" viewBox="0 0 24 24" fill="none" style="color: var(--color-brand);"><circle cx="12" cy="12" r="4" stroke="currentColor" stroke-width="2"/><path d="M12 8V4" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg>
<span style="font-size: var(--text-sm);">百度</span>
</button>
<button class="engine-option flex items-center gap-3 w-full px-4 py-2.5 transition-colors duration-150" style="color: var(--color-text-secondary); background: transparent;" role="option" aria-selected="false" data-engine="google">
<svg class="w-4 h-4" viewBox="0 0 24 24" fill="none" style="color: var(--color-text-secondary);"><circle cx="12" cy="12" r="6" stroke="currentColor" stroke-width="2"/></svg>
<span style="font-size: var(--text-sm);">Google</span>
</button>
<button class="engine-option flex items-center gap-3 w-full px-4 py-2.5 transition-colors duration-150" style="color: var(--color-text-secondary); background: transparent;" role="option" aria-selected="false" data-engine="bing">
<svg class="w-4 h-4" viewBox="0 0 24 24" fill="none" style="color: var(--color-text-secondary);"><circle cx="12" cy="12" r="6" stroke="currentColor" stroke-width="1.5"/><path d="M8 16l4-4 4 4" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
<span style="font-size: var(--text-sm);">Bing</span>
</button>
<button class="engine-option flex items-center gap-3 w-full px-4 py-2.5 transition-colors duration-150" style="color: var(--color-text-secondary); background: transparent;" role="option" aria-selected="false" data-engine="duckduckgo">
<svg class="w-4 h-4" viewBox="0 0 24 24" fill="none" style="color: var(--color-text-secondary);"><circle cx="12" cy="10" r="5" stroke="currentColor" stroke-width="1.5"/><path d="M9 16c0 2 1.5 4 3 4s3-2 3-4" stroke="currentColor" stroke-width="1.5"/></svg>
<span style="font-size: var(--text-sm);">DuckDuckGo</span>
</button>
</div>
</div>
</section>
<!-- Link Card Grid -->
<section class="flex-1 overflow-y-auto px-8 pb-8" aria-label="Link cards">
<!-- Section header -->
<div class="flex items-center justify-between mb-5" style="max-width: 1120px; margin-left: auto; margin-right: auto;">
<h1 class="truncate" style="font-size: var(--text-xl); font-weight: 600; color: var(--color-text-primary); text-wrap: balance; word-break: keep-all;">常用工具</h1>
<span class="shrink-0" style="font-size: var(--text-sm); color: var(--color-text-muted);">8 个链接</span>
</div>
<!-- Card Grid -->
<div class="grid gap-4" style="grid-template-columns: repeat(4, 1fr); max-width: 1120px; margin-left: auto; margin-right: auto;">
<!-- Card: ChatGPT -->
<a href="#" class="link-card group flex flex-col gap-3 rounded-lg p-4 transition-all duration-150" style="background: var(--color-bg-card); border: var(--glass-border); backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);" data-dom-id="card-chatgpt">
<div class="flex items-center gap-3">
<div class="w-9 h-9 rounded-lg flex items-center justify-center shrink-0" style="background: rgba(16, 163, 127, 0.15);">
<svg class="w-5 h-5" viewBox="0 0 24 24" fill="none" style="color: #10a37f;"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z" fill="currentColor"/></svg>
</div>
<h3 class="truncate" style="font-size: var(--text-base); font-weight: 600; color: var(--color-text-primary);">ChatGPT</h3>
</div>
<p class="truncate" style="font-size: var(--text-sm); color: var(--color-text-secondary); line-height: var(--leading-normal);">AI对话助手,智能问答</p>
</a>
<!-- Card: GitHub -->
<a href="#" class="link-card group flex flex-col gap-3 rounded-lg p-4 transition-all duration-150" style="background: var(--color-bg-card); border: var(--glass-border); backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);" data-dom-id="card-github">
<div class="flex items-center gap-3">
<div class="w-9 h-9 rounded-lg flex items-center justify-center shrink-0" style="background: rgba(230, 230, 230, 0.1);">
<i data-lucide="github" class="w-5 h-5" style="color: var(--color-text-primary);"></i>
</div>
<h3 class="truncate" style="font-size: var(--text-base); font-weight: 600; color: var(--color-text-primary);">GitHub</h3>
</div>
<p class="truncate" style="font-size: var(--text-sm); color: var(--color-text-secondary); line-height: var(--leading-normal);">代码托管与协作平台</p>
</a>
<!-- Card: Stack Overflow -->
<a href="#" class="link-card group flex flex-col gap-3 rounded-lg p-4 transition-all duration-150" style="background: var(--color-bg-card); border: var(--glass-border); backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);" data-dom-id="card-stackoverflow">
<div class="flex items-center gap-3">
<div class="w-9 h-9 rounded-lg flex items-center justify-center shrink-0" style="background: rgba(244, 128, 36, 0.12);">
<svg class="w-5 h-5" viewBox="0 0 24 24" fill="none" style="color: #f48024;"><path d="M15.73 21.02l-.6-2.35h-6.26l-.6 2.35H4.67L8.2 7h7.6l3.53 14.02h-3.6zm-1.33-5.33l-2.2-8.52h-.2l-2.2 8.52h4.6zM2 22h20v2H2v-2z" fill="currentColor"/></svg>
</div>
<h3 class="truncate" style="font-size: var(--text-base); font-weight: 600; color: var(--color-text-primary);">Stack Overflow</h3>
</div>
<p class="truncate" style="font-size: var(--text-sm); color: var(--color-text-secondary); line-height: var(--leading-normal);">开发者问答社区</p>
</a>
<!-- Card: MDN Web Docs -->
<a href="#" class="link-card group flex flex-col gap-3 rounded-lg p-4 transition-all duration-150" style="background: var(--color-bg-card); border: var(--glass-border); backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);" data-dom-id="card-mdn">
<div class="flex items-center gap-3">
<div class="w-9 h-9 rounded-lg flex items-center justify-center shrink-0" style="background: rgba(121, 79, 169, 0.15);">
<svg class="w-5 h-5" viewBox="0 0 24 24" fill="none" style="color: #794fa9;"><path d="M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>
</div>
<h3 class="truncate" style="font-size: var(--text-base); font-weight: 600; color: var(--color-text-primary);">MDN Web Docs</h3>
</div>
<p class="truncate" style="font-size: var(--text-sm); color: var(--color-text-secondary); line-height: var(--leading-normal);">Web 技术文档参考</p>
</a>
<!-- Card: VS Code -->
<a href="#" class="link-card group flex flex-col gap-3 rounded-lg p-4 transition-all duration-150" style="background: var(--color-bg-card); border: var(--glass-border); backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);" data-dom-id="card-vscode">
<div class="flex items-center gap-3">
<div class="w-9 h-9 rounded-lg flex items-center justify-center shrink-0" style="background: rgba(0, 122, 204, 0.15);">
<i data-lucide="code-2" class="w-5 h-5" style="color: #007ac1;"></i>
</div>
<h3 class="truncate" style="font-size: var(--text-base); font-weight: 600; color: var(--color-text-primary);">VS Code</h3>
</div>
<p class="truncate" style="font-size: var(--text-sm); color: var(--color-text-secondary); line-height: var(--leading-normal);">轻量级代码编辑器</p>
</a>
<!-- Card: Notion -->
<a href="#" class="link-card group flex flex-col gap-3 rounded-lg p-4 transition-all duration-150" style="background: var(--color-bg-card); border: var(--glass-border); backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);" data-dom-id="card-notion">
<div class="flex items-center gap-3">
<div class="w-9 h-9 rounded-lg flex items-center justify-center shrink-0" style="background: rgba(230, 230, 230, 0.08);">
<svg class="w-5 h-5" viewBox="0 0 24 24" fill="none" style="color: var(--color-text-primary);"><path d="M4.5 4.5h15v15h-15z" stroke="currentColor" stroke-width="1.5" rx="2"/><path d="M8 9h8M8 12h5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>
</div>
<h3 class="truncate" style="font-size: var(--text-base); font-weight: 600; color: var(--color-text-primary);">Notion</h3>
</div>
<p class="truncate" style="font-size: var(--text-sm); color: var(--color-text-secondary); line-height: var(--leading-normal);">笔记与协作空间</p>
</a>
<!-- Card: Figma -->
<a href="#" class="link-card group flex flex-col gap-3 rounded-lg p-4 transition-all duration-150" style="background: var(--color-bg-card); border: var(--glass-border); backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);" data-dom-id="card-figma">
<div class="flex items-center gap-3">
<div class="w-9 h-9 rounded-lg flex items-center justify-center shrink-0" style="background: rgba(242, 78, 30, 0.12);">
<svg class="w-5 h-5" viewBox="0 0 24 24" fill="none" style="color: #f24e1e;"><path d="M8 2a3 3 0 100 6 3 3 0 000-6zm0 8a3 3 0 100 6 3 3 0 000-6zm0 8a3 3 0 100 6 3 3 0 000-6zm8-8a3 3 0 100 6 3 3 0 000-6z" stroke="currentColor" stroke-width="1.5"/><path d="M16 2a3 3 0 110 6V2z" fill="currentColor"/><path d="M16 8a3 3 0 110 6V8z" stroke="currentColor" stroke-width="1.5"/></svg>
</div>
<h3 class="truncate" style="font-size: var(--text-base); font-weight: 600; color: var(--color-text-primary);">Figma</h3>
</div>
<p class="truncate" style="font-size: var(--text-sm); color: var(--color-text-secondary); line-height: var(--leading-normal);">在线设计协作工具</p>
</a>
<!-- Card: Canva -->
<a href="#" class="link-card group flex flex-col gap-3 rounded-lg p-4 transition-all duration-150" style="background: var(--color-bg-card); border: var(--glass-border); backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);" data-dom-id="card-canva">
<div class="flex items-center gap-3">
<div class="w-9 h-9 rounded-lg flex items-center justify-center shrink-0" style="background: rgba(140, 103, 255, 0.12);">
<svg class="w-5 h-5" viewBox="0 0 24 24" fill="none" style="color: #8c67ff;"><circle cx="12" cy="12" r="6" stroke="currentColor" stroke-width="1.5"/><path d="M12 9v3l2 2" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
</div>
<h3 class="truncate" style="font-size: var(--text-base); font-weight: 600; color: var(--color-text-primary);">Canva</h3>
</div>
<p class="truncate" style="font-size: var(--text-sm); color: var(--color-text-secondary); line-height: var(--leading-normal);">在线平面设计平台</p>
</a>
<!-- Add-link button card -->
<a href="#" class="link-card flex flex-col items-center justify-center gap-2 rounded-lg p-4 transition-all duration-150" style="background: var(--color-bg-card); border: var(--glass-border); border-style: dashed; backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px); min-height: 96px;" data-dom-id="card-add-link">
<i data-lucide="plus" class="w-6 h-6" style="color: var(--color-text-muted);"></i>
<span style="font-size: var(--text-sm); color: var(--color-text-muted);">添加链接</span>
</a>
</div>
</section>
</div>
</main>
<style>
/* ---- Nav item interaction states ---- */
.nav-item:hover {
color: var(--color-text-primary) !important;
background: rgba(255, 255, 255, 0.04) !important;
}
.nav-item:focus-visible {
outline: 2px solid var(--color-brand);
outline-offset: -2px;
}
.nav-item.active {
color: var(--color-brand) !important;
background: rgba(108, 92, 231, 0.12) !important;
}
.sub-nav-item:hover {
color: var(--color-text-primary) !important;
background: rgba(255, 255, 255, 0.03) !important;
}
.sub-nav-item:focus-visible {
outline: 2px solid var(--color-brand);
outline-offset: -2px;
}
/* ---- Link card hover ---- */
.link-card:hover {
background: var(--color-bg-card-hover) !important;
border-color: var(--color-border-hover) !important;
transform: translateY(-2px);
}
.link-card:focus-visible {
outline: 2px solid var(--color-brand);
outline-offset: 2px;
}
.link-card:active {
transform: translateY(0);
}
/* ---- Search input focus ---- */
[data-dom-id="search-input"]:focus {
box-shadow: inset 0 0 0 0;
}
[data-dom-id="search-bar"]:focus-within {
border-color: var(--color-border-active) !important;
}
[data-dom-id="search-input"]::placeholder {
color: var(--color-text-muted);
}
/* ---- Engine button hover ---- */
[data-dom-id="search-engine-btn"]:hover {
color: var(--color-text-primary) !important;
background: rgba(255, 255, 255, 0.04) !important;
}
[data-dom-id="search-btn"]:hover {
color: var(--color-brand) !important;
}
/* ---- Engine option hover ---- */
.engine-option:hover {
color: var(--color-text-primary) !important;
background: rgba(255, 255, 255, 0.06) !important;
}
/* ---- Add-link card hover ---- */
[data-dom-id="card-add-link"]:hover {
border-color: var(--color-brand) !important;
background: rgba(108, 92, 231, 0.08) !important;
}
[data-dom-id="card-add-link"]:hover i,
[data-dom-id="card-add-link"]:hover span {
color: var(--color-brand) !important;
}
/* ---- Sidebar chevron rotation ---- */
[aria-expanded="true"] > i[data-lucide="chevron-down"],
.nav-item[aria-expanded="true"] i[data-lucide="chevron-down"] {
transform: rotate(0deg) !important;
}
[aria-expanded="false"] i[data-lucide="chevron-down"] {
transform: rotate(-90deg) !important;
}
/* ---- Responsive grid ---- */
@media (max-width: 1280px) {
.grid[style*="repeat(4"] {
grid-template-columns: repeat(3, 1fr) !important;
}
}
@media (max-width: 1024px) {
.grid[style*="repeat(4"] {
grid-template-columns: repeat(2, 1fr) !important;
}
}
@media (max-width: 768px) {
aside {
width: var(--sidebar-collapsed-width) !important;
}
aside .nav-item span,
aside .sub-nav-item span,
aside .nav-item .flex-1,
aside .sub-nav,
aside .user-info-text {
display: none !important;
}
aside .nav-item {
justify-content: center !important;
}
div[style*="margin-left: var(--sidebar-width)"] {
margin-left: var(--sidebar-collapsed-width) !important;
}
}
/* ---- Reduced motion ---- */
@media (prefers-reduced-motion: reduce) {
.link-card,
.nav-item,
.sub-nav-item,
.engine-option,
i[data-lucide="chevron-down"] {
transition-duration: 0.01ms !important;
}
}
</style>
<script>
// Sidebar category expand/collapse toggle
document.querySelectorAll('.nav-item[data-nav-key][aria-expanded]').forEach(function(btn) {
btn.addEventListener('click', function() {
var expanded = this.getAttribute('aria-expanded') === 'true';
this.setAttribute('aria-expanded', String(!expanded));
var subNav = this.nextElementSibling;
if (subNav && subNav.classList.contains('sub-nav')) {
subNav.classList.toggle('hidden', expanded);
}
});
});
// Search engine dropdown toggle
var engineBtn = document.querySelector('[data-dom-id="search-engine-btn"]');
var engineDropdown = document.querySelector('[data-dom-id="search-engine-dropdown"]');
if (engineBtn && engineDropdown) {
engineBtn.addEventListener('click', function(e) {
e.stopPropagation();
engineDropdown.classList.toggle('hidden');
});
document.addEventListener('click', function(e) {
if (!engineDropdown.contains(e.target)) {
engineDropdown.classList.add('hidden');
}
});
engineDropdown.querySelectorAll('.engine-option').forEach(function(opt) {
opt.addEventListener('click', function() {
var name = this.querySelector('span').textContent;
engineBtn.querySelector('span').textContent = name;
engineDropdown.querySelectorAll('.engine-option').forEach(function(o) {
o.setAttribute('aria-selected', 'false');
o.style.color = 'var(--color-text-secondary)';
});
this.setAttribute('aria-selected', 'true');
this.style.color = 'var(--color-text-primary)';
engineDropdown.classList.add('hidden');
});
});
}
</script>
<script>lucide.createIcons();</script>
</body>
</html>
+597
View File
@@ -0,0 +1,597 @@
<!DOCTYPE html>
<html lang="zh-CN" class="light">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>浏览器首页</title>
<style id="theme-vars">
/* ===== Browser Homepage - Brand CSS ===== */
/* Dark theme with glassmorphism, inspired by gaming browser start pages */
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Noto+Sans+SC:wght@300;400;500;600;700&display=swap');
:root {
/* ---- Brand Colors ---- */
--color-bg-primary: #0d0d12;
--color-bg-secondary: #16161e;
--color-bg-tertiary: #1e1e2a;
--color-bg-card: rgba(30, 30, 42, 0.65);
--color-bg-card-hover: rgba(40, 40, 56, 0.75);
--color-bg-sidebar: rgba(13, 13, 18, 0.85);
--color-bg-search: rgba(22, 22, 30, 0.8);
--color-bg-input: rgba(30, 30, 42, 0.5);
--color-bg-overlay: rgba(0, 0, 0, 0.5);
/* Text */
--color-text-primary: #e8e8f0;
--color-text-secondary: #9494a8;
--color-text-muted: #5e5e72;
--color-text-inverse: #0d0d12;
/* Brand accent */
--color-brand: #6c5ce7;
--color-brand-light: #a29bfe;
--color-brand-hover: #7d6ff0;
/* Border */
--color-border: rgba(255, 255, 255, 0.06);
--color-border-hover: rgba(255, 255, 255, 0.12);
--color-border-active: rgba(108, 92, 231, 0.5);
/* State colors */
--state-success: #00b894;
--state-warning: #fdcb6e;
--state-error: #e17055;
--state-info: #74b9ff;
/* ---- Typography ---- */
--font-sans: 'Inter', 'Noto Sans SC', -apple-system, BlinkMacSystemFont, sans-serif;
--font-heading: 'Inter', 'Noto Sans SC', sans-serif;
--text-xs: 11px;
--text-sm: 13px;
--text-base: 14px;
--text-lg: 16px;
--text-xl: 20px;
--text-2xl: 24px;
--text-3xl: 32px;
--leading-tight: 1.25;
--leading-normal: 1.5;
--leading-relaxed: 1.625;
--tracking-tight: -0.01em;
--tracking-normal: 0em;
/* ---- Spacing ---- */
--space-1: 4px;
--space-2: 8px;
--space-3: 12px;
--space-4: 16px;
--space-5: 20px;
--space-6: 24px;
--space-8: 32px;
--space-10: 40px;
--space-12: 48px;
/* ---- Radius ---- */
--radius-sm: 4px;
--radius-md: 8px;
--radius-lg: 12px;
--radius-xl: 16px;
--radius-full: 9999px;
/* ---- Shadows ---- */
--shadow-card: 0 1px 3px rgba(0, 0, 0, 0.05);
--shadow-float: 0 8px 32px rgba(0, 0, 0, 0.25);
--shadow-dropdown: 0 12px 48px rgba(0, 0, 0, 0.35);
/* ---- Glassmorphism ---- */
--glass-bg: rgba(30, 30, 42, 0.65);
--glass-blur: 12px;
--glass-border: 1px solid rgba(255, 255, 255, 0.08);
/* ---- Transitions ---- */
--transition-fast: 150ms ease;
--transition-normal: 250ms ease;
--transition-slow: 400ms ease;
/* ---- Sidebar ---- */
--sidebar-width: 240px;
--sidebar-collapsed-width: 64px;
/* ---- Z-index ---- */
--z-sidebar: 100;
--z-dropdown: 200;
--z-modal: 300;
--z-tooltip: 400;
}
</style>
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4.3.1/dist/index.global.js"></script>
<script src="https://unpkg.com/lucide@1.8.0/dist/umd/lucide.min.js"></script>
<style type="text/tailwindcss">
@theme inline {
--color-border: var(--color-border);
}
@layer base {
body { background: var(--color-bg-primary); color: var(--color-text-primary); }
td, th { @apply break-words; word-break: break-all; word-break: auto-phrase; }
th { @apply whitespace-nowrap; }
}
</style>
<style>
.no-scrollbar::-webkit-scrollbar { display: none; }
.no-scrollbar { -ms-overflow-style: none; scrollbar-width: none; }
[data-icon] {
display: inline-flex;
align-items: center;
justify-content: center;
-webkit-mask-size: contain;
mask-size: contain;
-webkit-mask-repeat: no-repeat;
mask-repeat: no-repeat;
-webkit-mask-position: center;
mask-position: center;
background-color: currentColor;
}
</style>
</head>
<body class="min-h-screen font-sans antialiased">
<main class="relative max-w-[375px] mx-auto min-h-screen" style="font-family: var(--font-sans);">
<!-- ===== TOP BAR (single row: hamburger | search | gear) ===== -->
<header class="sticky top-0 z-50 flex items-center gap-2 px-4 h-14"
style="background: var(--color-bg-secondary); border-bottom: var(--color-border);">
<!-- Hamburger menu button (44x44) -->
<button id="drawer-toggle" type="button"
class="inline-flex items-center justify-center w-11 h-11 shrink-0 rounded-lg transition-colors duration-150"
style="color: var(--color-text-primary);"
aria-label="打开导航菜单"
data-dom-id="hamburger-menu">
<i data-lucide="menu" class="w-5 h-5"></i>
</button>
<!-- Search bar (engine selector + input) -->
<div class="flex items-center gap-2 flex-1 min-w-0 rounded-xl px-2.5 h-11"
style="background: var(--color-bg-search); border: var(--glass-border); backdrop-filter: blur(var(--glass-blur));">
<!-- Search engine selector -->
<button id="engine-selector" type="button"
class="inline-flex items-center justify-center shrink-0 h-7 px-2 rounded-md text-xs font-medium whitespace-nowrap"
style="background: var(--color-bg-tertiary); color: var(--color-brand); border: 1px solid var(--color-border-active);"
aria-label="选择搜索引擎"
data-dom-id="engine-selector">
<i data-lucide="search" class="w-3 h-3 mr-1"></i>
<span>Google</span>
<i data-lucide="chevron-down" class="w-3 h-3 ml-1"></i>
</button>
<!-- Search input -->
<input type="text" placeholder="搜索或输入网址"
class="flex-1 min-w-0 bg-transparent outline-none text-sm"
style="color: var(--color-text-primary);"
aria-label="搜索输入框"
data-dom-id="search-input" />
</div>
<!-- Settings gear button (44x44) — replaces top-bar avatar -->
<button id="settings-gear-toggle" type="button"
class="inline-flex items-center justify-center w-11 h-11 shrink-0 rounded-lg transition-colors duration-150"
style="color: var(--color-text-primary);"
aria-label="设置"
data-dom-id="settings-gear">
<i data-lucide="settings" class="w-5 h-5"></i>
</button>
</header>
<!-- ===== CATEGORY TABS ===== -->
<nav class="px-4 pt-3 pb-3" aria-label="分类导航">
<div class="flex gap-2 overflow-x-auto no-scrollbar pb-1">
<button class="shrink-0 inline-flex items-center justify-center h-8 px-4 rounded-lg text-xs font-semibold whitespace-nowrap"
style="background: var(--color-brand); color: var(--color-text-inverse);"
data-tab-key="all" data-active="true"
data-dom-id="tab-all">
全部
</button>
<button class="shrink-0 inline-flex items-center justify-center h-8 px-4 rounded-lg text-xs font-medium whitespace-nowrap"
style="background: var(--color-bg-tertiary); color: var(--color-text-secondary); border: 1px solid var(--color-border);"
data-tab-key="tools"
data-dom-id="tab-tools">
常用工具
</button>
<button class="shrink-0 inline-flex items-center justify-center h-8 px-4 rounded-lg text-xs font-medium whitespace-nowrap"
style="background: var(--color-bg-tertiary); color: var(--color-text-secondary); border: 1px solid var(--color-border);"
data-tab-key="shopping"
data-dom-id="tab-shopping">
购物
</button>
<button class="shrink-0 inline-flex items-center justify-center h-8 px-4 rounded-lg text-xs font-medium whitespace-nowrap"
style="background: var(--color-bg-tertiary); color: var(--color-text-secondary); border: 1px solid var(--color-border);"
data-tab-key="video"
data-dom-id="tab-video">
视频
</button>
<button class="shrink-0 inline-flex items-center justify-center h-8 px-4 rounded-lg text-xs font-medium whitespace-nowrap"
style="background: var(--color-bg-tertiary); color: var(--color-text-secondary); border: 1px solid var(--color-border);"
data-tab-key="news"
data-dom-id="tab-news">
资讯
</button>
<button class="shrink-0 inline-flex items-center justify-center h-8 px-4 rounded-lg text-xs font-medium whitespace-nowrap"
style="background: var(--color-bg-tertiary); color: var(--color-text-secondary); border: 1px solid var(--color-border);"
data-tab-key="social"
data-dom-id="tab-social">
社交
</button>
</div>
</nav>
<!-- ===== LINK CARD LIST ===== -->
<section class="px-4 pb-24 space-y-2" aria-label="链接列表">
<!-- ChatGPT -->
<a href="#" class="flex items-center gap-3 px-3 py-3 rounded-xl transition-colors duration-150"
style="background: var(--color-bg-card); border: var(--glass-border); backdrop-filter: blur(var(--glass-blur));"
data-dom-id="link-chatgpt">
<div class="w-10 h-10 rounded-lg shrink-0 flex items-center justify-center"
style="background: rgba(16, 163, 127, 0.15);">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="color: #10a37f;">
<path d="M12 2a10 10 0 0 1 0 20 10 10 0 0 1 0-20z"/>
<path d="M7 12h10M12 7v10"/>
</svg>
</div>
<div class="flex-1 min-w-0">
<p class="text-sm font-semibold truncate" style="color: var(--color-text-primary);">ChatGPT</p>
<p class="text-xs truncate mt-0.5" style="color: var(--color-text-secondary);">AI 对话助手</p>
</div>
<i data-lucide="external-link" class="w-4 h-4 shrink-0" style="color: var(--color-text-muted);"></i>
</a>
<!-- GitHub -->
<a href="#" class="flex items-center gap-3 px-3 py-3 rounded-xl transition-colors duration-150"
style="background: var(--color-bg-card); border: var(--glass-border); backdrop-filter: blur(var(--glass-blur));"
data-dom-id="link-github">
<div class="w-10 h-10 rounded-lg shrink-0 flex items-center justify-center"
style="background: rgba(255, 255, 255, 0.08);">
<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor" style="color: var(--color-text-primary);">
<path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/>
</svg>
</div>
<div class="flex-1 min-w-0">
<p class="text-sm font-semibold truncate" style="color: var(--color-text-primary);">GitHub</p>
<p class="text-xs truncate mt-0.5" style="color: var(--color-text-secondary);">代码托管平台</p>
</div>
<i data-lucide="external-link" class="w-4 h-4 shrink-0" style="color: var(--color-text-muted);"></i>
</a>
<!-- Stack Overflow -->
<a href="#" class="flex items-center gap-3 px-3 py-3 rounded-xl transition-colors duration-150"
style="background: var(--color-bg-card); border: var(--glass-border); backdrop-filter: blur(var(--glass-blur));"
data-dom-id="link-stackoverflow">
<div class="w-10 h-10 rounded-lg shrink-0 flex items-center justify-center"
style="background: rgba(244, 128, 36, 0.15);">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="color: #f48024;">
<path d="M4 17l6-6 4 4 6-8"/>
<path d="M14 7h6v6"/>
</svg>
</div>
<div class="flex-1 min-w-0">
<p class="text-sm font-semibold truncate" style="color: var(--color-text-primary);">Stack Overflow</p>
<p class="text-xs truncate mt-0.5" style="color: var(--color-text-secondary);">开发者问答社区</p>
</div>
<i data-lucide="external-link" class="w-4 h-4 shrink-0" style="color: var(--color-text-muted);"></i>
</a>
<!-- MDN -->
<a href="#" class="flex items-center gap-3 px-3 py-3 rounded-xl transition-colors duration-150"
style="background: var(--color-bg-card); border: var(--glass-border); backdrop-filter: blur(var(--glass-blur));"
data-dom-id="link-mdn">
<div class="w-10 h-10 rounded-lg shrink-0 flex items-center justify-center"
style="background: rgba(83, 40, 209, 0.15);">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="color: #5328d1;">
<path d="M4 4h6v6H4z"/>
<path d="M14 4h6v6h-6z"/>
<path d="M4 14h6v6H4z"/>
<path d="M14 14h6v6h-6z"/>
</svg>
</div>
<div class="flex-1 min-w-0">
<p class="text-sm font-semibold truncate" style="color: var(--color-text-primary);">MDN Web Docs</p>
<p class="text-xs truncate mt-0.5" style="color: var(--color-text-secondary);">Web 技术文档</p>
</div>
<i data-lucide="external-link" class="w-4 h-4 shrink-0" style="color: var(--color-text-muted);"></i>
</a>
<!-- VS Code -->
<a href="#" class="flex items-center gap-3 px-3 py-3 rounded-xl transition-colors duration-150"
style="background: var(--color-bg-card); border: var(--glass-border); backdrop-filter: blur(var(--glass-blur));"
data-dom-id="link-vscode">
<div class="w-10 h-10 rounded-lg shrink-0 flex items-center justify-center"
style="background: rgba(0, 120, 215, 0.15);">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="color: #0078d7;">
<path d="M17.5 2.5l-10 8 4 2 6-10z"/>
<path d="M17.5 21.5l-10-8 4-2 6 10z"/>
</svg>
</div>
<div class="flex-1 min-w-0">
<p class="text-sm font-semibold truncate" style="color: var(--color-text-primary);">VS Code</p>
<p class="text-xs truncate mt-0.5" style="color: var(--color-text-secondary);">代码编辑器</p>
</div>
<i data-lucide="external-link" class="w-4 h-4 shrink-0" style="color: var(--color-text-muted);"></i>
</a>
<!-- Notion -->
<a href="#" class="flex items-center gap-3 px-3 py-3 rounded-xl transition-colors duration-150"
style="background: var(--color-bg-card); border: var(--glass-border); backdrop-filter: blur(var(--glass-blur));"
data-dom-id="link-notion">
<div class="w-10 h-10 rounded-lg shrink-0 flex items-center justify-center"
style="background: rgba(255, 255, 255, 0.08);">
<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor" style="color: var(--color-text-primary);">
<path d="M4.459 4.208c.746.606 1.026.56 2.428.466l13.215-.793c.28 0 .047-.28-.046-.326L17.86 2.685c-.42-.326-.98-.7-2.055-.607L3.39 3.553c-.466.046-.56.28-.374.466zm.793 3.26v13.917c0 .747.373 1.027 1.214.98l14.523-.84c.842-.046.935-.56.935-1.167V6.354c0-.606-.233-.933-.747-.886l-15.178.886c-.56.047-.747.327-.747.933zm14.337.42c.093.42 0 .84-.42.886l-.7.14v10.264c-.608.327-1.168.514-1.635.514-.747 0-.934-.234-1.495-.934l-4.573-7.186v6.953l1.448.327s0 .84-1.214.84l-3.356.187c-.093-.187 0-.653.327-.746l.84-.233V9.854L7.822 9.9c-.093-.42.14-1.026.793-1.073l3.593-.233 4.76 7.28v-6.44l-1.215-.14c-.093-.514.28-.886.747-.933zM2.61.947l10.873-.654c1.355-.093 1.682-.046 2.52.56L18.79 2.87c.56.374.747.47.747.887v16.203c0 .98-.374 1.587-1.682 1.68l-14.946.84c-.98.047-1.448-.093-1.962-.747L.46 19.08C-.094 18.38 0 17.82 0 17.213V2.407c0-.84.374-1.4 1.215-1.493z"/>
</svg>
</div>
<div class="flex-1 min-w-0">
<p class="text-sm font-semibold truncate" style="color: var(--color-text-primary);">Notion</p>
<p class="text-xs truncate mt-0.5" style="color: var(--color-text-secondary);">协作笔记工具</p>
</div>
<i data-lucide="external-link" class="w-4 h-4 shrink-0" style="color: var(--color-text-muted);"></i>
</a>
</section>
<!-- ===== FAB BUTTON ===== -->
<button class="fixed bottom-6 right-6 w-12 h-12 rounded-full flex items-center justify-center z-40 shadow-lg transition-transform duration-150 active:scale-95"
style="background: var(--color-brand); color: var(--color-text-inverse); box-shadow: var(--shadow-float);"
aria-label="添加链接"
data-dom-id="fab-add-link">
<i data-lucide="plus" class="w-5 h-5"></i>
</button>
<!-- ===== NAVIGATION DRAWER OVERLAY ===== -->
<div id="drawer-backdrop" class="fixed inset-0 z-50 hidden"
style="background: var(--color-bg-overlay); backdrop-filter: blur(2px);"
data-dom-id="drawer-backdrop"></div>
<!-- ===== NAVIGATION DRAWER (closed by default — avatar/profile moved here) ===== -->
<aside id="nav-drawer" class="fixed top-0 left-0 bottom-0 z-50 w-[280px] flex flex-col -translate-x-full transition-transform duration-250"
style="background: var(--color-bg-secondary); border-right: var(--glass-border); transform: translateX(-100%);"
aria-label="导航菜单"
data-dom-id="nav-drawer">
<!-- User profile (avatar + name + email) -->
<div class="flex items-center gap-3 px-4 h-16 shrink-0"
style="border-bottom: var(--color-border);">
<div class="w-10 h-10 rounded-full flex items-center justify-center shrink-0"
style="background: var(--color-brand); color: var(--color-text-inverse);">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/>
<circle cx="12" cy="7" r="4"/>
</svg>
</div>
<div class="min-w-0">
<p class="text-sm font-semibold truncate" style="color: var(--color-text-primary);">用户</p>
<p class="text-xs truncate" style="color: var(--color-text-muted);">浏览主页</p>
</div>
</div>
<!-- Two-level category navigation -->
<nav class="flex-1 overflow-y-auto px-2 py-3" aria-label="分类导航">
<!-- 常用工具 -->
<div class="mb-1">
<button class="drawer-cat-toggle flex items-center gap-2 w-full px-3 py-2.5 rounded-lg text-sm font-medium transition-colors duration-150"
style="color: var(--color-text-primary);"
data-dom-id="drawer-cat-tools">
<i data-lucide="wrench" class="w-4 h-4 shrink-0" style="color: var(--color-text-muted);"></i>
<span class="flex-1 text-left">常用工具</span>
<i data-lucide="chevron-down" class="w-4 h-4 shrink-0 transition-transform duration-150" style="color: var(--color-text-muted);"></i>
</button>
<div class="drawer-subitems ml-9 hidden">
<a href="#" class="block px-3 py-2 rounded-md text-xs transition-colors duration-150"
style="color: var(--color-text-secondary);">搜索引擎</a>
<a href="#" class="block px-3 py-2 rounded-md text-xs transition-colors duration-150"
style="color: var(--color-text-secondary);">AI 工具</a>
<a href="#" class="block px-3 py-2 rounded-md text-xs transition-colors duration-150"
style="color: var(--color-text-secondary);">开发工具</a>
</div>
</div>
<!-- 购物 -->
<div class="mb-1">
<button class="drawer-cat-toggle flex items-center gap-2 w-full px-3 py-2.5 rounded-lg text-sm font-medium transition-colors duration-150"
style="color: var(--color-text-primary);"
data-dom-id="drawer-cat-shopping">
<i data-lucide="shopping-bag" class="w-4 h-4 shrink-0" style="color: var(--color-text-muted);"></i>
<span class="flex-1 text-left">购物</span>
<i data-lucide="chevron-down" class="w-4 h-4 shrink-0 transition-transform duration-150" style="color: var(--color-text-muted);"></i>
</button>
<div class="drawer-subitems ml-9 hidden">
<a href="#" class="block px-3 py-2 rounded-md text-xs transition-colors duration-150"
style="color: var(--color-text-secondary);">电商</a>
<a href="#" class="block px-3 py-2 rounded-md text-xs transition-colors duration-150"
style="color: var(--color-text-secondary);">数码</a>
</div>
</div>
<!-- 视频 -->
<div class="mb-1">
<button class="drawer-cat-toggle flex items-center gap-2 w-full px-3 py-2.5 rounded-lg text-sm font-medium transition-colors duration-150"
style="color: var(--color-text-primary);"
data-dom-id="drawer-cat-video">
<i data-lucide="play-circle" class="w-4 h-4 shrink-0" style="color: var(--color-text-muted);"></i>
<span class="flex-1 text-left">视频</span>
<i data-lucide="chevron-down" class="w-4 h-4 shrink-0 transition-transform duration-150" style="color: var(--color-text-muted);"></i>
</button>
<div class="drawer-subitems ml-9 hidden">
<a href="#" class="block px-3 py-2 rounded-md text-xs transition-colors duration-150"
style="color: var(--color-text-secondary);">影视</a>
<a href="#" class="block px-3 py-2 rounded-md text-xs transition-colors duration-150"
style="color: var(--color-text-secondary);">直播</a>
</div>
</div>
<!-- 资讯 -->
<div class="mb-1">
<button class="drawer-cat-toggle flex items-center gap-2 w-full px-3 py-2.5 rounded-lg text-sm font-medium transition-colors duration-150"
style="color: var(--color-text-primary);"
data-dom-id="drawer-cat-news">
<i data-lucide="newspaper" class="w-4 h-4 shrink-0" style="color: var(--color-text-muted);"></i>
<span class="flex-1 text-left">资讯</span>
<i data-lucide="chevron-down" class="w-4 h-4 shrink-0 transition-transform duration-150" style="color: var(--color-text-muted);"></i>
</button>
<div class="drawer-subitems ml-9 hidden">
<a href="#" class="block px-3 py-2 rounded-md text-xs transition-colors duration-150"
style="color: var(--color-text-secondary);">科技</a>
<a href="#" class="block px-3 py-2 rounded-md text-xs transition-colors duration-150"
style="color: var(--color-text-secondary);">新闻</a>
</div>
</div>
<!-- 社交 -->
<div class="mb-1">
<button class="drawer-cat-toggle flex items-center gap-2 w-full px-3 py-2.5 rounded-lg text-sm font-medium transition-colors duration-150"
style="color: var(--color-text-primary);"
data-dom-id="drawer-cat-social">
<i data-lucide="message-circle" class="w-4 h-4 shrink-0" style="color: var(--color-text-muted);"></i>
<span class="flex-1 text-left">社交</span>
<i data-lucide="chevron-down" class="w-4 h-4 shrink-0 transition-transform duration-150" style="color: var(--color-text-muted);"></i>
</button>
<div class="drawer-subitems ml-9 hidden">
<a href="#" class="block px-3 py-2 rounded-md text-xs transition-colors duration-150"
style="color: var(--color-text-secondary);">即时通讯</a>
<a href="#" class="block px-3 py-2 rounded-md text-xs transition-colors duration-150"
style="color: var(--color-text-secondary);">社区论坛</a>
</div>
</div>
</nav>
<!-- Settings link -->
<div class="shrink-0 px-2 py-3" style="border-top: var(--color-border);">
<a href="#" class="flex items-center gap-2 px-3 py-2.5 rounded-lg text-sm transition-colors duration-150"
style="color: var(--color-text-secondary);"
data-dom-id="drawer-settings">
<i data-lucide="settings" class="w-4 h-4 shrink-0" style="color: var(--color-text-muted);"></i>
<span>设置</span>
</a>
</div>
</aside>
</main>
<style>
/* Drawer slide-in animation */
#nav-drawer.open {
transform: translateX(0) !important;
}
/* Active tab style for category tabs */
[data-tab-key][data-active="true"] {
background: var(--color-brand) !important;
color: var(--color-text-inverse) !important;
border-color: var(--color-brand) !important;
}
[data-tab-key]:not([data-active="true"]):hover {
background: var(--color-bg-card-hover) !important;
}
/* Link card hover */
a[style*="background: var(--color-bg-card)"]:hover {
background: var(--color-bg-card-hover) !important;
}
/* Drawer category toggle hover */
.drawer-cat-toggle:hover {
background: var(--color-bg-tertiary);
}
.drawer-cat-toggle[aria-expanded="true"] > svg:last-child {
transform: rotate(180deg);
}
/* Drawer sub-items visible */
.drawer-subitems:not(.hidden) {
display: block;
}
/* FAB hover */
button[style*="background: var(--color-brand)"][data-dom-id="fab-add-link"]:hover {
background: var(--color-brand-hover) !important;
}
/* Top-bar icon hover */
#drawer-toggle:hover,
#settings-gear-toggle:hover {
background: var(--color-bg-tertiary) !important;
}
/* Reduced motion */
@media (prefers-reduced-motion: reduce) {
#nav-drawer,
.drawer-cat-toggle,
.drawer-cat-toggle > svg:last-child,
a[style*="background: var(--color-bg-card)"],
button[style*="background: var(--color-brand)"][data-dom-id="fab-add-link"],
#drawer-toggle,
#settings-gear-toggle {
transition-duration: 0.01ms !important;
}
}
/* Drawer backdrop animation */
#drawer-backdrop.show {
opacity: 1 !important;
pointer-events: auto !important;
}
#drawer-backdrop {
opacity: 0;
pointer-events: none;
transition: opacity var(--transition-normal);
}
#nav-drawer {
transition: transform var(--transition-normal);
}
</style>
<script>
/* Drawer open/close (hamburger toggles the drawer that holds avatar/profile) */
const drawer = document.getElementById('nav-drawer');
const backdrop = document.getElementById('drawer-backdrop');
const toggle = document.getElementById('drawer-toggle');
const gearToggle = document.getElementById('settings-gear-toggle');
function openDrawer() {
drawer.classList.add('open');
backdrop.classList.remove('hidden');
backdrop.classList.add('show');
document.body.style.overflow = 'hidden';
}
function closeDrawer() {
drawer.classList.remove('open');
backdrop.classList.remove('show');
document.body.style.overflow = '';
setTimeout(function() { backdrop.classList.add('hidden'); }, 250);
}
toggle.addEventListener('click', openDrawer);
backdrop.addEventListener('click', closeDrawer);
/* Gear button also opens the drawer (settings lives at the bottom of the drawer) */
if (gearToggle) {
gearToggle.addEventListener('click', openDrawer);
}
/* Drawer category toggle */
document.querySelectorAll('.drawer-cat-toggle').forEach(function(btn) {
btn.addEventListener('click', function() {
var sub = this.nextElementSibling;
var expanded = this.getAttribute('aria-expanded') === 'true';
this.setAttribute('aria-expanded', String(!expanded));
if (expanded) {
sub.classList.add('hidden');
} else {
sub.classList.remove('hidden');
}
});
});
/* Category tab switching */
document.querySelectorAll('[data-tab-key]').forEach(function(tab) {
tab.addEventListener('click', function() {
document.querySelectorAll('[data-tab-key]').forEach(function(t) {
t.removeAttribute('data-active');
t.style.background = 'var(--color-bg-tertiary)';
t.style.color = 'var(--color-text-secondary)';
t.style.border = '1px solid var(--color-border)';
});
this.setAttribute('data-active', 'true');
this.style.background = 'var(--color-brand)';
this.style.color = 'var(--color-text-inverse)';
this.style.border = '1px solid var(--color-brand)';
});
});
</script>
<script>lucide.createIcons();</script>
</body>
</html>