68be41e7a2
# 项目概述 个人浏览器首页导航应用,支持书签分类管理、搜索引擎快捷搜索、 必应每日壁纸轮播、前后端分离部署,适配 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 模式)
577 lines
36 KiB
HTML
577 lines
36 KiB
HTML
<!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>
|