diff --git a/config/README.md b/config/README.md index 61b86cb..155796d 100644 --- a/config/README.md +++ b/config/README.md @@ -121,7 +121,7 @@ MeNav 配置系统采用“完全替换”策略(不合并),按以下优 5. **导航** - `navigation[]`:页面入口列表,`id` 需唯一,并与 `pages/` 中配置文件名对应(例如 `id: home` 对应 `pages/home.yml`) - - 只允许一个导航项 `active: true` 作为默认页 + - 默认首页由 `navigation` 数组顺序决定:**第一项即为首页(默认打开页)**,不再使用 `active` 字段 - 图标使用 Font Awesome 类名字符串(例如 `fas fa-home`、`fab fa-github`) - 导航显示顺序与数组顺序一致,可通过调整数组顺序改变导航顺序 @@ -230,7 +230,6 @@ navigation: - name: "首页" icon: "fas fa-home" id: "home" - active: true - name: "项目" icon: "fas fa-project-diagram" id: "projects" diff --git a/config/_default/site.yml b/config/_default/site.yml index a7c6c9a..efa85ac 100644 --- a/config/_default/site.yml +++ b/config/_default/site.yml @@ -50,12 +50,11 @@ social: url: https://steam.com icon: fab fa-steam -# 导航配置 +# 导航配置(顺序第一项即首页/默认打开页) navigation: - - name: 首页 # 菜单名称 - icon: fas fa-home # Font Awesome 图标类 - id: home # 页面标识符(唯一,需与 pages/home.yml 对应) - active: true # 是否默认激活(全局仅一个 true) + - name: 常用 # 菜单名称 + icon: fas fa-star # Font Awesome 图标类 + id: home # 页面标识符(唯一,需与 pages/.yml 对应) - name: 项目 icon: fas fa-project-diagram id: projects diff --git a/src/bookmark-processor.js b/src/bookmark-processor.js index 37c4039..12b45af 100644 --- a/src/bookmark-processor.js +++ b/src/bookmark-processor.js @@ -794,8 +794,7 @@ function updateNavigationFile(filePath) { navConfig.push({ name: '书签', icon: 'fas fa-bookmark', - id: 'bookmarks', - active: false + id: 'bookmarks' }); // 更新文件 diff --git a/src/generator.js b/src/generator.js index c9ef9d1..3d1714a 100644 --- a/src/generator.js +++ b/src/generator.js @@ -462,13 +462,6 @@ function prepareRenderData(config) { // 移除警告日志,数据处理逻辑保留 } - // 添加序列化的配置数据,用于浏览器扩展 - renderData.configJSON = JSON.stringify({ - version: process.env.npm_package_version || '1.0.0', - timestamp: new Date().toISOString(), - data: renderData // 使用经过处理的renderData而不是原始config - }); - // 添加导航项的活动状态标记和子菜单 if (Array.isArray(renderData.navigation)) { renderData.navigation = renderData.navigation.map((item, index) => { @@ -476,7 +469,7 @@ function prepareRenderData(config) { ...item, isActive: index === 0, // 默认第一项为活动项 id: item.id || `nav-${index}`, - active: index === 0 // 兼容原有逻辑 + active: index === 0 // 保持旧模板兼容(由顺序决定,不读取配置的 active 字段) }; // 使用辅助函数获取子菜单 @@ -489,6 +482,16 @@ function prepareRenderData(config) { }); } + // 首页(默认页)规则:navigation 顺序第一项即首页 + renderData.homePageId = renderData.navigation && renderData.navigation[0] ? renderData.navigation[0].id : null; + + // 添加序列化的配置数据,用于浏览器扩展(确保包含 homePageId 等处理结果) + renderData.configJSON = JSON.stringify({ + version: process.env.npm_package_version || '1.0.0', + timestamp: new Date().toISOString(), + data: renderData // 使用经过处理的renderData而不是原始config + }); + // 为Handlebars模板特别准备navigationData数组 renderData.navigationData = renderData.navigation; @@ -814,6 +817,17 @@ function renderPage(pageId, config) { Object.assign(data, config[pageId]); } + // 首页标题规则:使用 site.yml 的 profile 覆盖首页(导航第一项)的 title/subtitle 显示 + const homePageId = config.homePageId + || (Array.isArray(config.navigation) && config.navigation[0] ? config.navigation[0].id : null) + || 'home'; + // 供模板判断“当前是否首页” + data.homePageId = homePageId; + if (pageId === homePageId && config.profile) { + if (config.profile.title !== undefined) data.title = config.profile.title; + if (config.profile.subtitle !== undefined) data.subtitle = config.profile.subtitle; + } + // 检查页面配置中是否指定了模板 let templateName = pageId; if (config[pageId] && config[pageId].template) { @@ -844,11 +858,6 @@ function generateAllPagesHTML(config) { }); } - // 确保首页存在 - if (!pages.home) { - pages.home = renderPage('home', config); - } - // 确保搜索结果页存在 if (!pages['search-results']) { pages['search-results'] = renderPage('search-results', config); @@ -989,7 +998,9 @@ function main() { } } -main(); +if (require.main === module) { + main(); +} // 导出供测试使用的函数 module.exports = { @@ -1002,4 +1013,3 @@ module.exports = { renderTemplate, generateAllPagesHTML }; - diff --git a/src/script.js b/src/script.js index 62dddfa..21c09b2 100644 --- a/src/script.js +++ b/src/script.js @@ -641,7 +641,41 @@ function extractNestedData(element) { document.addEventListener('DOMContentLoaded', () => { // 先声明所有状态变量 let isSearchActive = false; - let currentPageId = 'home'; + // 首页不再固定为 "home":以导航顺序第一项为准 + const homePageId = (() => { + // 1) 优先从生成端注入的配置数据读取(保持与实际导航顺序一致) + try { + const config = window.MeNav && typeof window.MeNav.getConfig === 'function' + ? window.MeNav.getConfig() + : null; + const injectedHomePageId = config && config.data && config.data.homePageId + ? String(config.data.homePageId).trim() + : ''; + if (injectedHomePageId) return injectedHomePageId; + const nav = config && config.data && Array.isArray(config.data.navigation) + ? config.data.navigation + : null; + const firstId = nav && nav[0] && nav[0].id ? String(nav[0].id).trim() : ''; + if (firstId) return firstId; + } catch (error) { + // 忽略解析错误,继续使用 DOM 推断 + } + + // 2) 回退到 DOM:取首个导航项的 data-page + const firstNavItem = document.querySelector('.nav-item[data-page]'); + if (firstNavItem) { + const id = String(firstNavItem.getAttribute('data-page') || '').trim(); + if (id) return id; + } + + // 3) 最后兜底:取首个页面容器 id + const firstPage = document.querySelector('.page[id]'); + if (firstPage && firstPage.id) return firstPage.id; + + return 'home'; + })(); + + let currentPageId = homePageId; let isInitialLoad = true; let isSidebarOpen = false; let isSearchOpen = false; @@ -1257,9 +1291,9 @@ document.addEventListener('DOMContentLoaded', () => { } } else { // 如果没有激活的导航项,默认显示首页 - currentPageId = 'home'; + currentPageId = homePageId; pages.forEach(page => { - page.classList.toggle('active', page.id === 'home'); + page.classList.toggle('active', page.id === homePageId); }); } } catch (resetError) { @@ -1489,7 +1523,7 @@ document.addEventListener('DOMContentLoaded', () => { // 立即执行初始化,不再使用requestAnimationFrame延迟 // 显示首页 - showPage('home'); + showPage(homePageId); // 添加载入动画 categories.forEach((category, index) => { diff --git a/templates/pages/articles.hbs b/templates/pages/articles.hbs index 37aef95..976cf01 100644 --- a/templates/pages/articles.hbs +++ b/templates/pages/articles.hbs @@ -1,7 +1,14 @@ +{{#ifEquals pageId @root.homePageId}} +
+

{{title}}

+

{{subtitle}}

+
+{{else}}

{{title}}

{{subtitle}}

+{{/ifEquals}} {{#each categories}} {{> category}} -{{/each}} \ No newline at end of file +{{/each}} diff --git a/templates/pages/bookmarks.hbs b/templates/pages/bookmarks.hbs index 37aef95..976cf01 100644 --- a/templates/pages/bookmarks.hbs +++ b/templates/pages/bookmarks.hbs @@ -1,7 +1,14 @@ +{{#ifEquals pageId @root.homePageId}} +
+

{{title}}

+

{{subtitle}}

+
+{{else}}

{{title}}

{{subtitle}}

+{{/ifEquals}} {{#each categories}} {{> category}} -{{/each}} \ No newline at end of file +{{/each}} diff --git a/templates/pages/friends.hbs b/templates/pages/friends.hbs index 37aef95..976cf01 100644 --- a/templates/pages/friends.hbs +++ b/templates/pages/friends.hbs @@ -1,7 +1,14 @@ +{{#ifEquals pageId @root.homePageId}} +
+

{{title}}

+

{{subtitle}}

+
+{{else}}

{{title}}

{{subtitle}}

+{{/ifEquals}} {{#each categories}} {{> category}} -{{/each}} \ No newline at end of file +{{/each}} diff --git a/templates/pages/home.hbs b/templates/pages/home.hbs index 56e4fa1..976cf01 100644 --- a/templates/pages/home.hbs +++ b/templates/pages/home.hbs @@ -1,7 +1,14 @@ +{{#ifEquals pageId @root.homePageId}}
-

{{profile.title}}

-

{{profile.subtitle}}

+

{{title}}

+

{{subtitle}}

+{{else}} +
+

{{title}}

+

{{subtitle}}

+
+{{/ifEquals}} {{#each categories}} {{> category}} {{/each}} diff --git a/templates/pages/page.hbs b/templates/pages/page.hbs index 9ed42d1..b10dd9e 100644 --- a/templates/pages/page.hbs +++ b/templates/pages/page.hbs @@ -1,9 +1,16 @@
+ {{#ifEquals pageId @root.homePageId}} +
+

{{title}}

+

{{subtitle}}

+
+ {{else}}

{{title}}

{{subtitle}}

+ {{/ifEquals}} {{#each categories}} {{> category}} {{/each}} -
\ No newline at end of file + diff --git a/templates/pages/projects.hbs b/templates/pages/projects.hbs index 37aef95..976cf01 100644 --- a/templates/pages/projects.hbs +++ b/templates/pages/projects.hbs @@ -1,7 +1,14 @@ +{{#ifEquals pageId @root.homePageId}} +
+

{{title}}

+

{{subtitle}}

+
+{{else}}

{{title}}

{{subtitle}}

+{{/ifEquals}} {{#each categories}} {{> category}} -{{/each}} \ No newline at end of file +{{/each}} diff --git a/test/home-page-profile-override.node-test.js b/test/home-page-profile-override.node-test.js new file mode 100644 index 0000000..fa19201 --- /dev/null +++ b/test/home-page-profile-override.node-test.js @@ -0,0 +1,55 @@ +const test = require('node:test'); +const assert = require('node:assert/strict'); +const path = require('node:path'); + +const { loadHandlebarsTemplates, generateAllPagesHTML } = require('../src/generator.js'); + +test('首页(navigation 第一项)应使用 profile 覆盖 title/subtitle 显示', () => { + const originalCwd = process.cwd(); + process.chdir(path.join(__dirname, '..')); + + try { + loadHandlebarsTemplates(); + + const config = { + site: { title: 'Test Site', description: '', author: '', favicon: '', logo_text: 'Test' }, + profile: { title: 'PROFILE_TITLE', subtitle: 'PROFILE_SUBTITLE' }, + social: [], + navigation: [ + { id: 'bookmarks', name: '书签', icon: 'fas fa-bookmark' }, + { id: 'home', name: '首页', icon: 'fas fa-home' }, + { id: 'projects', name: '项目', icon: 'fas fa-project-diagram' }, + ], + bookmarks: { title: '书签页标题', subtitle: '书签页副标题', template: 'bookmarks', categories: [] }, + home: { title: 'HOME_PAGE_TITLE', subtitle: 'HOME_PAGE_SUBTITLE', template: 'home', categories: [] }, + projects: { title: '项目页标题', subtitle: '项目页副标题', template: 'projects', categories: [] }, + }; + + const pages = generateAllPagesHTML(config); + + assert.ok(typeof pages.bookmarks === 'string' && pages.bookmarks.length > 0); + assert.ok(pages.bookmarks.includes('PROFILE_TITLE')); + assert.ok(pages.bookmarks.includes('PROFILE_SUBTITLE')); + assert.ok(pages.bookmarks.includes('data-editable="profile-title"')); + assert.ok(pages.bookmarks.includes('data-editable="profile-subtitle"')); + assert.ok(pages.bookmarks.includes(' 0); + assert.ok(pages.home.includes('HOME_PAGE_TITLE')); + assert.ok(pages.home.includes('HOME_PAGE_SUBTITLE')); + assert.ok(pages.home.includes('data-editable="page-title"')); + assert.ok(pages.home.includes('data-editable="page-subtitle"')); + assert.ok(pages.home.includes('

0); + assert.ok(pages.projects.includes('项目页标题')); + assert.ok(pages.projects.includes('项目页副标题')); + assert.ok(pages.projects.includes('