diff --git a/code/docusaurus/.trae/documents/scroll_fix_plan.md b/code/docusaurus/.trae/documents/scroll_fix_plan.md new file mode 100644 index 00000000..94461fac --- /dev/null +++ b/code/docusaurus/.trae/documents/scroll_fix_plan.md @@ -0,0 +1,66 @@ +# 书签导入与整理计划 + +## 需求分析 + +用户希望将浏览器书签文件 `D:\bookmarks_2026_5_26.html` 中的内容导入并整理到 `docs/resources.mdx` 文件中。 + +## 书签文件结构分析 + +从读取的书签文件中识别出以下主要分组: + +### 第一层分组 + +1. **AI 智能助手** - 包含 ChatGPT、腾讯元宝、v0 等 +2. **前端开发** - 包含多个子文件夹: + + * 图标库 (expo icons, iconfont, icones, lucide) + + * 前端模板库 (uiverse, codepen, htmlrev, lottiefiles, mockuphone) + + * TinyMCE 相关 + + * Uniapp 相关 + + * 前端组件库 (Arco Design) + + * React Native 第三方库 + + * 插画库 (undraw, storyset, manypixels) + + * React Native 开发相关 +3. **Python** - PaddlePaddle 相关 + +## 实现方案 + +### 步骤 + +1. 创建一个 Node.js 脚本来解析书签 HTML 文件 +2. 将解析结果转换为资源导航所需的分组格式 +3. 更新 `docs/resources.mdx` 文件 + +### 修改文件 + +* `docs/resources.mdx` - 更新为解析后的书签内容 + +## 输出格式 + +使用现有的 `SearchableLinkGrid` 组件格式,保持分组结构清晰。 + +## 过滤规则 + +1. 跳过重复链接 +2. 清理过长的标题(超过50字符截断) +3. 提取有意义的描述信息 + +## 风险评估 + +* 文件较大(50KB+),需要处理大量数据 + +* 部分链接可能失效,建议用户后续验证 + +## 测试计划 + +1. 解析书签文件 +2. 生成新的 resources.mdx +3. 启动开发服务器验证显示效果 + diff --git a/code/docusaurus/docs/resources.mdx b/code/docusaurus/docs/resources.mdx index fc6a0a10..35a38059 100644 --- a/code/docusaurus/docs/resources.mdx +++ b/code/docusaurus/docs/resources.mdx @@ -10,7 +10,7 @@ import SearchableLinkGrid from '@site/src/components/Link/SearchableLinkGrid'; \ No newline at end of file +/> diff --git a/code/docusaurus/docusaurus.config.ts b/code/docusaurus/docusaurus.config.ts index 0ecc2c62..7f4aa0af 100644 --- a/code/docusaurus/docusaurus.config.ts +++ b/code/docusaurus/docusaurus.config.ts @@ -82,7 +82,7 @@ const config: Config = { alt: 'oneao', src: 'img/logo.png', }, - hideOnScroll: true, + hideOnScroll: false, items: [ { type: 'docSidebar', diff --git a/code/docusaurus/src/components/Link/SearchableLinkGrid.tsx b/code/docusaurus/src/components/Link/SearchableLinkGrid.tsx index e78578ac..52e34b45 100644 --- a/code/docusaurus/src/components/Link/SearchableLinkGrid.tsx +++ b/code/docusaurus/src/components/Link/SearchableLinkGrid.tsx @@ -1,5 +1,6 @@ -import React, { useState, useMemo } from 'react' -import LinkCard, { LinkCardProps } from './LinkCard' +import type { LinkCardProps } from './LinkCard' +import React, { useEffect, useMemo, useRef, useState } from 'react' +import LinkCard from './LinkCard' import styles from './styles.module.css' export interface GroupedLinkCardProps { @@ -13,17 +14,61 @@ interface SearchableLinkGridProps { export default function SearchableLinkGrid({ groups }: SearchableLinkGridProps) { const [searchTerm, setSearchTerm] = useState('') + const [activeSection, setActiveSection] = useState('') + const sidebarRef = useRef(null) + + useEffect(() => { + const navbar = document.querySelector('.navbar') + const navbarHeight = navbar ? navbar.offsetHeight : 64 + const threshold = navbarHeight + 32 + + if (sidebarRef.current) { + sidebarRef.current.style.top = `${threshold}px` + } + }, []) + + useEffect(() => { + const navbar = document.querySelector('.navbar') + const navbarHeight = navbar ? navbar.offsetHeight : 64 + const threshold = navbarHeight + 20 + + const observer = new IntersectionObserver( + (entries) => { + for (const entry of entries) { + if (entry.isIntersecting) { + setActiveSection(entry.target.id) + } + } + }, + { + rootMargin: `-${threshold}px 0px -80% 0px`, + threshold: 0.1, + }, + ) + + groups.forEach((_, index) => { + const element = document.getElementById(`group-${index}`) + if (element) { + observer.observe(element) + } + }) + + return () => { + observer.disconnect() + } + }, [groups]) const filteredGroups = useMemo(() => { - if (!searchTerm.trim()) return groups - + if (!searchTerm.trim()) + return groups + const lowerTerm = searchTerm.toLowerCase() return groups.map(group => ({ ...group, items: group.items.filter( item => - item.title.toLowerCase().includes(lowerTerm) || - item.description.toLowerCase().includes(lowerTerm), + item.title.toLowerCase().includes(lowerTerm) + || item.description.toLowerCase().includes(lowerTerm), ), })).filter(group => group.items.length > 0) }, [searchTerm, groups]) @@ -31,68 +76,99 @@ export default function SearchableLinkGrid({ groups }: SearchableLinkGridProps) const totalItems = groups.reduce((sum, group) => sum + group.items.length, 0) const filteredTotalItems = filteredGroups.reduce((sum, group) => sum + group.items.length, 0) + const scrollToSection = (index: number) => { + const element = document.getElementById(`group-${index}`) + if (element) { + element.scrollIntoView({ behavior: 'smooth', block: 'start' }) + } + } + return ( -
-
-
- - - - - setSearchTerm(e.target.value)} - className={styles.searchInput} - /> -
- - {filteredTotalItems} / {totalItems} 项 - -
- - {filteredGroups.length > 0 ? ( - filteredGroups.map((group, groupIndex) => ( -
-

{group.groupName}

-
- {group.items.map((item, itemIndex) => ( - - ))} -
+
+
+
+
+ + + + + setSearchTerm(e.target.value)} + className={styles.searchInput} + />
- )) - ) : ( -
- - - - - -

未找到匹配的资源

+ + {filteredTotalItems} + {' '} + / + {totalItems} + {' '} + 项 +
- )} + + {filteredGroups.length > 0 + ? ( + filteredGroups.map((group, groupIndex) => ( +
+

{group.groupName}

+
+ {group.items.map((item, itemIndex) => ( + + ))} +
+
+ )) + ) + : ( +
+ + + + + +

未找到匹配的资源

+
+ )} +
+ +
) -} \ No newline at end of file +} diff --git a/code/docusaurus/src/components/Link/styles.module.css b/code/docusaurus/src/components/Link/styles.module.css index 790f618e..36123f3f 100644 --- a/code/docusaurus/src/components/Link/styles.module.css +++ b/code/docusaurus/src/components/Link/styles.module.css @@ -1,3 +1,65 @@ +/* 容器布局 */ +.container { + display: flex; + gap: 24px; +} + +.mainContent { + flex: 1; +} + +/* 右侧导航 */ +.sidebar { + width: 180px; + flex-shrink: 0; + position: sticky; + top: 2rem; + height: fit-content; +} + +.sidebarHeader { + font-size: 14px; + font-weight: 600; + color: var(--ifm-color-emphasis-600); + margin-bottom: 12px; + padding-left: 8px; +} + +.sidebarLinks { + display: flex; + flex-direction: column; + gap: 4px; +} + +.sidebarLink { + display: block; + padding: 8px 12px; + font-size: 14px; + text-align: left; + border: none; + background: transparent; + color: var(--ifm-color-emphasis-700); + border-radius: 8px; + cursor: pointer; + transition: + background-color 0.2s ease, + color 0.2s ease; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.sidebarLink:hover { + background-color: var(--ifm-color-emphasis-100); + color: var(--ifm-color-emphasis-900); +} + +.sidebarLink.active { + background-color: var(--ifm-color-primary); + color: white; +} + +/* 网格布局 */ .grid { display: grid; gap: 16px; @@ -28,6 +90,13 @@ } } +/* 响应式:隐藏右侧导航 */ +@media (max-width: 1200px) { + .sidebar { + display: none; + } +} + /* 卡片 */ .card { @@ -194,6 +263,7 @@ .group { margin-bottom: 32px; + scroll-margin-top: calc(var(--ifm-navbar-height, 64px) + 1rem); } .group:last-child {