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

zdashboard

v2.8.0

Published

ZCode skill dashboard platform — pluggable viewers for zdesign/zview/zreview/zgoal

Downloads

4,346

Readme

zdashboard

ZCode skill 的通用 dashboard 平台。核心不动,skill 自带 viewer,未来 N 个 dashboard 自然接入。

架构 (v2.0)

zdashboard/                    ← 一个核心包,永远不写业务逻辑
├── src/
│   ├── core/
│   │   ├── server.ts           ← HTTP server + route/SSE 注册表
│   │   ├── reload.ts           ← fs.watch → SSE broadcast
│   │   ├── tree.ts             ← /__files 文件树
│   │   └── manifest.ts         ← /__plugins 插件清单
│   ├── plugins/                ← 内置 plugins(随核心发版)
│   │   ├── stats/                  ← 项目统计(文件/目录/大小/扩展名 Top10)
│   │   ├── just/               ← Just Runner 任务执行
│   │   ├── bugs/               ← 禅道 Bugs 只读列表
│   │   ├── review/             ← 文档评审状态流转
│   │   ├── apply/              ← OpenSpec change 执行进度
│   │   ├── design/             ← 设计资产分类浏览
│   │   └── view/               ← 项目浏览(文件预览)
│   ├── web/                    ← SPA 前端(Vite + React)
│   │   ├── App.tsx             ← Shell:Topbar + IconRail + Workspace + StatusBar
│   │   ├── layout/             ← IconRail、StatusBar
│   │   ├── home/               ← HomeGrid 插件卡片
│   │   ├── components/         ← shared UI(button、card、tooltip...)
│   │   ├── viewers/            ← Markdown/Image/Code/Unsupported 预览器
│   │   └── hooks/              ← useSSE
│   └── server/                 ← 后端 helpers(detect、scan、just-runner...)
├── package.json
└── README.md

设计原则

  1. 核心不动src/core/ 提供 HTTP server、SSE、文件树、插件清单,不写任何业务逻辑
  2. 插件自包含:每个 mode 在 src/plugins/<mode>/ 内完成前后端(index.ts + web.tsx + Workspace.tsx
  3. Cordis 生命周期:用 ctx.effect(() => () => dispose()) 管理清理,ctx.server.route/sse 注册路由
  4. SPA 首页GET / 直接返回 index.html,前端 hash 驱动 mode 切换(#<mode>

Plugin 约定

每个内置 plugin 提供:

src/plugins/<mode>/
├── index.ts       ← 后端:注册 /__<mode>* 路由
├── web.tsx        ← 前端 manifest(mode、label、icon、Workspace)
└── Workspace.tsx  ← 前端页面组件
// src/plugins/<mode>/web.tsx
import { lazy } from 'react';
import Workspace from './Workspace.js';

export default {
  mode: '<mode>',
  label: '显示名',
  icon: '🎯',
  description: '简短描述',
  Workspace: lazy(() => import('./Workspace.js')),
} as const;

可选 Sidebar 槽(如 view、design 提供侧栏):

import Sidebar from './Sidebar.js';

export default {
  mode: '<mode>',
  label: '显示名',
  icon: '🎯',
  description: '简短描述',
  Sidebar: lazy(() => import('./Sidebar.js')),
  Workspace: lazy(() => import('./Workspace.js')),
} as const;
// src/plugins/<mode>/index.ts
export default {
  inject: ['server'] as const,
  apply(ctx: Context, config: { root: string }) {
    const server = (ctx as any).server;
    if (!server?.route) return;

    server.route('/__<mode>', async (req, res) => {
      res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8', 'Cache-Control': 'no-cache' });
      res.end(JSON.stringify({ /* ... */ }));
    });
  },
};

外部 plugin 放在 --plugins <dir> 下,结构同上,核心启动时自动扫描 index.{ts,js,mjs} 加载。

外部插件编写指南

外部插件允许自带静态 viewer 页面,通过 iframe 嵌入 dashboard。

目录结构

my-skill/
├── index.ts          ← 必填:cordis 插件定义
└── web/              ← 可选:静态 viewer 目录
    ├── index.html
    └── assets/...

index.ts 约定

import type { Context } from 'cordis';

export default {
  inject: ['server', 'dashboard'] as const,
  apply(ctx: Context, config: { root: string }) {
    const server = (ctx as any).server;
    const dashboard = (ctx as any).dashboard;
    if (!server?.route || !dashboard?.register) return;

    // 注册后端 API
    server.route('/__my-api', (req, res) => {
      res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8', 'Cache-Control': 'no-cache' });
      res.end(JSON.stringify({ ok: true }));
    });

    // 注册 manifest(mode === 目录名时,若有 web/index.html 会自动填充 viewerUrl)
    dashboard.register({
      mode: 'my-skill',   // 必须与目录名一致才能自动填充 viewerUrl
      label: 'My Skill',
      icon: '🧩',
      description: '描述',
    });
  },
};

外部插件必须 export default(cordis ctx.plugin() 只接受函数或带 apply 的 default 对象)。

web/ 约定

  • web/index.html 必须存在才会被自动服务
  • 自动挂载在 /__plugin/<目录名>/,例如目录 my-skill 对应 /__plugin/my-skill/
  • 页面内可通过相对路径引用同目录资源(js/css/图片)
  • 页面内可调用插件注册的 API(如 /__my-api),与 dashboard 同源

viewerUrl 覆盖

如果需要在 manifest 中显式指定 viewerUrl,可直接写:

dashboard.register({
  mode: 'my-skill',
  label: 'My Skill',
  icon: '🧩',
  description: '描述',
  viewerUrl: '/custom-path/',
});

显式声明的 viewerUrl 优先于自动填充值。

热刷新

外部 viewer 的 HTML 文件会自动注入 reload 脚本(与 dashboard SPA 一致),修改 web/index.html 后浏览器会自动刷新。

沙箱说明

前端以 iframe 渲染 viewer,sandbox 属性设置为:

allow-scripts allow-same-origin allow-forms allow-popups

保留同源能力(可访问 dashboard 的 /__* API);允许表单与弹窗(viewer 内 window.open 外链场景)。

无 web 目录的插件

若插件目录没有 web/index.html,前端仍会显示插件卡片,但进入时展示占位页(不报错)。

CLI 用法

# 基本用法
pnpm build
node dist/cli.js --dir <项目根> --port 4190

# 打开浏览器
node dist/cli.js --dir <项目根> --port 4190 --open

# 启动后直达某个 mode(拼 #<mode>)
node dist/cli.js --dir <项目根> --port 4190 --page design

# 加载外部 plugins
node dist/cli.js --dir <项目根> --port 4190 --plugins ./my-plugins

环境变量

内置路由

| 路由 | 方法 | 说明 | |------|------|------| | / | GET | SPA 首页 | | /__config | GET | { stopToken, version, root } | | /__stop | POST | x-stop-token 鉴权,干净退出 | | /__plugins | GET | 内置 + 外部插件清单 | | /__files | GET | 文件树(openspec/docs 过滤) | | /__reload | SSE | reload + files 事件 | | /__just/recipes | GET | just 任务列表 | | /__just/logs | SSE | just 实时日志 | | /__just/{start,stop,restart} | POST | just 任务控制 | | /__bugs | GET | 禅道 bug 列表 | | /__review | GET | 评审项列表 | | /__review/item | POST | 更新评审项 | | /__review/status | POST | 更新评审状态 | | /__docs | GET | 项目文档列表 | | /__apply | GET | OpenSpec changes 列表 | | /__apply/change | GET | ?name=<change> 详情 | | /__worktrees | GET | .zworktree/ 下的 worktree 列表(git worktree list --porcelain) | | /__design/assets | GET | 设计资产扫描 | | /__design/asset | GET | 设计资产只读代理 ?path=<rel-to-.zdev/design>;非法路径(../绝对路径/反斜杠/缺参)400,缺失 404 | | /__stats/data | GET | 项目统计(内置 stats 插件) | | /__notes/data | GET | 便签列表(examples/notes 示例) | | /__notes/save | POST | 保存便签(x-stop-token 鉴权) |

前端路由

前端用 hash 驱动 mode:

  • /#<mode> 激活对应 plugin Workspace
  • /# 或空 hash 回到 HomeGrid
  • IconRail 点击自动写 hash
  • 浏览器前进/后退由 hashchange 事件处理

技术栈

  • 运行时:Node.js 20+,ESM,node: 前缀
  • 核心框架:Cordis 4.0.0-rc.8(Context + Service + inject)
  • 前端:React 18 + Vite 5 + Tailwind 3
  • 组件库:Radix UI(tooltip、separator、scroll-area、slot)
  • HTTP:Node http 模块(无 express 依赖)
  • 构建:tsup(CLI) + Vite(SPA)
  • 包管理:pnpm

主题系统

添加主题 SOP

  1. 新建 src/web/themes/<id>.css,仅覆盖语义色变量(--background/--foreground/--card/--popover/--primary/--secondary/--muted/--accent/--destructive/--border/--input/--ring 等),不覆盖圆角/阴影变量(mode 正交性:[data-theme="..."][data-mode="dark"] 组合由机制层处理)
  2. src/web/lib/themes.tsSTYLES 数组加一条:{ id: '<id>', label: '<名>', swatch: ['#色1', '#色2', '#色3', '#色4'] }
  3. src/web/globals.css 顶部 @import 块加一行:@import './themes/<id>.css';

约束清单

  • mode(dark/light)正交,不进主题定义,由 CSS 组合选择器自然派生
  • 语义色必须走 CSS 变量令牌(hsl(var(--xxx))),不硬编码色值到组件
  • 装饰色(图标、插画、背景纹理)豁免令牌,可在主题文件中直接写值
  • font-mono 覆盖点:--font-mono 令牌,主题文件可覆盖,组件用 font-mono class
  • 主题切换选择器:data-theme,localStorage key zd-theme,legacy zdashboard-theme 已迁移

数据约定

.zdev/ 数据目录

zdashboard v2.1+ 优先读取 .zdev/ 下的数据文件(与 zskills 新协议对齐):

| 文件 | 说明 | 回退路径 | |------|------|---------| | .zdev/config.yaml | 禅道凭据(zgoal skill 创建) | .zgoal/config.yaml | | .zdev/review.yaml | 评审数据 | 根目录 review.yaml | | .zdev/*.md | 评审相关文档(brief.md 等) | — |

启动日志会打印数据目录:data -> .zdev/(仅 .zdev/ 存在时)。

worktree 执行模型

zapply 在 .zworktree/<change-name>/ 下创建独立 worktree,apply 插件会优先读取 worktree 内的 tasks.md/proposal.md/design.md,主目录兜底。Viewer 卡片会显示 worktree 执行中 badge。

--restart 端口继承

--restart 重启时,新实例会优先尝试旧实例的端口(通过 .zdev/dashboard.json 记录),避免书签/标签失效。用户显式 --port 时仍尊重用户选择。

迁移状态

  • [x] zview-dashboard → zdashboard(v1.0.0)
  • [x] zreview-dashboard → zdashboard plugin(v1.2.0)
  • [x] zdesign-dashboard → zdashboard plugin(v1.2.0)
  • [x] zapply execution progress → zdashboard plugin(v1.3.3)
  • [x] zskills skill SKILL.md 统一调用 zdashboard
  • [x] v2.0 cordis 重写:core 不动,插件自包含,SPA 首页