npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@ricenote/plugin-navigation

v1.2.0

Published

RiceNote 导航栏插件:自定义链接、页面与文章组成的有序顶部导航

Readme

rice.navigation —— 导航栏

站点的顶部导航栏:把「自定义链接 / 页面 / 文章」编排成有序导航项, 通过 header.navigation 插槽输出到前台,并在后台提供独立管理页。

为什么单独做一个插件:导航栏是"出现在每个页面上的全局展示", 而页面与文章的知识只属于各自的内容插件。把「哪些内容进导航、第几位、显示什么文案」 收进一个统一入口,才能避免两个插件往同一个插槽写重复链接、顺序无人协调。

清单摘要

| 项 | 值 | | --- | --- | | id | rice.navigation | | version | 1.0.0 | | apiVersion | ^1.0.0 | | entry | ./dist/index.js | | provides | content.navigation@1 | | requires | 无(pages / posts 是可选依赖:未启用时自定义链接照常工作) | | capabilities | database:own、services:provide、services:consume、routes:admin、events:subscribe、slots:register、admin:menu | | migrations | ./migrations(0001_navigation.sql) | | 数据表 | rn_nav_items |

扩展点(Hook)

插槽

| 插槽 | 组件名 | 优先级 | 输出 | | --- | --- | --- | --- | | header.navigation | navigation.header | 20 | 每项一个 <a href class="rn-nav-link">文案</a> |

标记约定(与旧版 rice.pages 的输出完全一致,主题无需改动):

<a href="/page/about" class="rn-nav-link rn-nav-link--active" aria-current="page">关于</a>
<a href="https://example.com" class="rn-nav-link" target="_blank" rel="noopener noreferrer">外部站点</a>
  • 当前项判定(aria-current="page" + rn-nav-link--active)由本插槽负责, 两套官方主题据此高亮,主题不应自己计算;
  • target="_blank" 一定带 rel="noopener noreferrer";
  • 未启用、目标未发布、目标已被删除的项不输出(前台永远不会出现死链)。

管理 API

| 方法 | 路径 | 权限点 | 说明 | | --- | --- | --- | --- | | GET | /api/admin/navigation/items | nav.view | 全部导航项(含停用),附目标解析状态 | | GET | /api/admin/navigation/targets | nav.view | 目标候选项(?type=page\|post&keyword=,只列已发布内容) | | POST | /api/admin/navigation/items | nav.manage | 新建(追加到末尾) | | PUT | /api/admin/navigation/items/:itemId | nav.manage | 更新(文案 / 目标 / 新窗口 / 启用) | | POST | /api/admin/navigation/items/:itemId/move | nav.manage | 上移/下移一位,返回重排后的完整列表 | | DELETE | /api/admin/navigation/items/:itemId | nav.manage | 删除 |

服务契约(提供)

content.navigation@1:

listRenderable()                        // 前台:可直接渲染的项(已启用 + 目标可解析,已按 maxItems 截断)
listForAdmin()                          // 后台:全部项 + 目标状态(exists / published / title / url)
save({ id?, payload })                  // 新建或更新
remove(id)                              // 删除
move({ id, direction })                 // 上移/下移,返回重排后的列表
targets({ type, keyword? })             // 选择器候选项(只含已发布内容)

其他插件通常不需要直接调用它:让本插件渲染插槽即可。 确需自行渲染(例如换一种标记结构)时才读该服务。

服务契约(消费)

| 契约 | 必需性 | 用途 | | --- | --- | --- | | core.setup@1 | 必需 | 取 siteId() 作为数据分区键 | | core.html@1 | 必需 | 输出链接前转义文案与地址 | | core.theme@1 | 必需 | 导航变化后 clearCache()(导航出现在每个页面上,按标签失效覆盖不到) | | content.pages@1 | 可选 | 解析页面引用(navItems() / findForAdmin() / listForAdmin()) | | content.posts@1 | 可选 | 解析文章引用(publishedRefs() / titlesOf() / listPublished() / searchPublished()) |

事件(订阅)

| 事件 | 用途 | | --- | --- | | core.runtime.started.v1 | 上报就绪状态;存在"目标失效"的导航项时给出告警 | | content.pages.changed.v1 / content.pages.deleted.v1 | 页面变化 → 若被导航引用则清 HTML 缓存 | | content.post.published.v1 / content.post.updated.v1 / content.post.unpublished.v1 / content.post.deleted.v1 | 文章变化 → 若被导航引用则清 HTML 缓存 |

只在"该内容确实被某个已启用导航项引用"时才清缓存(referencesContent() 判定), 否则每次保存文章都会清空全站 HTML 缓存。

后台菜单

{ id: 'navigation.items', label: '导航栏', path: '/navigation', icon: 'Navigation', order: 18, permission: 'nav.view' }

后台界面(插件自带)

界面代码就在本插件目录内,零构建(纯 ESM + 模板字符串,不装依赖、不打包):

| 项 | 值 | | --- | --- | | 入口 | admin/main.js(defineAdminPage 导出页面组件) | | 托管 URL | /plugin-admin/rice.navigation/main.js(需登录;插件启用时才可取) | | 菜单声明 | manifest.json → admin.pages[]:id navigation.items、路径 /navigation、图标 Navigation、权限 nav.view | | 依赖 | 仅 /admin-sdk/v1/ricenote-admin.js(宿主与插件共用同一份 SDK 与同一份 Vue 实例) |

由后台通用宿主加载:有加载态、出错可重试、插件未启用时给出"由插件提供"的提示。 开发方式、组件清单与样式可用范围见 docs/plugin-api/v1.md 第 16 节与 ADR-014。

设置项(settingsSchema)

后台「扩展中心 → 导航栏 → 设置」。保存后需重启服务生效。

分组「通用」

| 字段 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | | maxItems | number(1–50) | 10 | 前台最多渲染多少项(超出部分按后台顺序截断,防止导航被塞爆) |

数据表

rn_nav_items(表所有权归本插件):

| 列 | 说明 | | --- | --- | | item_type | custom / page / post | | label | 展示文案;custom 必填,page/post 可留空以跟随目标标题 | | href | custom 的地址(站内路径或 http(s) 地址) | | ref_id | page / post 引用的内容 ID(不建外键、不冗余标题) | | new_tab / enabled | 是否新窗口打开 / 是否启用 | | sort_order | 顺序(间隔 10;重排时整体重编号) |

引用而非快照:page / post 项只保存 ID,标题与地址在读取时解析 —— 页面改名、改别名后导航自动跟随;标题被删除后该项自动不显示,后台给出提示。

安全要点

  • 地址白名单:只接受以 / 开头的站内路径与 http(s):// 绝对地址; javascript:、data:、vbscript:、mailto:、相对路径与协议相对地址(//evil.com) 一律拒绝(校验在 src/nav.ts,有专门的单元测试);
  • 链接文案与地址输出前都经 core.html@1 的 escape();
  • 外链固定带 rel="noopener noreferrer";
  • 目标 ID 必须真实存在才会被保存(内容插件被禁用时跳过校验并记为警告日志);
  • 写入权限点为 nav.manage,读取为 nav.view;所有写操作经 Core 守卫的 认证 → CSRF → 权限 → 审计链路。

依赖与启停

  • 无插件级硬依赖。pages / posts 被禁用时,引用类导航项全部视为"未解析" (前台跳过、后台提示"目标不存在"),自定义链接照常工作;
  • 禁用后:导航插槽、管理 API、content.navigation@1、后台菜单全部注销, 前台导航栏变空(主题里的首页等内置入口不受影响),数据保留在库中;
  • 重新启用后导航项自动恢复(ref_id 未变,目标插件重新可用即可解析)。