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

dsh-cad-viewer

v0.2.0

Published

dsh plugin: a 3D model library and a three-cad-viewer workbench in a '3D模型' tab, plus inline model cards and CadQuery agent tools. Models are stored server-side, so the library is shared across devices.

Downloads

681

Readme

dsh-cad-viewer

把 3D 模型库和完整的 CAD 查看器嵌入 dsh Web GUI:会话区多出一个 「3D模型」tab,agent 用 CadQuery 建好的模型直接落进库里,可在线查看、导出成 10 种 CAD / 网格 / 图纸格式。

npm npm downloads License Platform Node AI-generated

npm 包页 · 更新日志 · AI 生成声明 · MIT 许可证

截图

「3D模型」tab:模型库、卡片菜单,以及列出全部 10 种格式的导出子菜单

模型库 —— tab 的默认视图。文件夹、全库搜索、拖拽移动;卡片菜单里有重命名 / 移动到… / 导出 / 删除。图中子菜单头部显示「仅有网格 · 由三角网格重建几何」,因为该条目由 add_3dmodel 创建(没有保存 CadQuery 源码);build_3dmodel 的条目会显示「已保存 CadQuery 源码 · 导出为精确几何」。

工作台:查看器工具栏、导航树与 3D 画布

工作台 —— 每个模型一个标签页,挂载上游 three-cad-viewer 的完整界面(工具栏 / 导航树 / 画布),文案已中文化,工具栏上多了 导出 按钮(在 ? 之前)。

这个插件是什么

  • 模型库 —— 文件夹、全库搜索、拖拽移动、重命名 / 移动 / 删除,以及 10 种格式导出。
  • 工作台 —— 为库中任意模型挂载 three-cad-viewer 完整界面;左 / 右键旋转、中键平移、滚轮缩放。
  • 对话内嵌卡片 —— 调用过模型工具的那条消息末尾出现可折叠卡片,全屏 即以工作台打开同一模型。
  • 五个 agent 工具 —— add_3dmodel / build_3dmodel(建模入库)、list_3dmodels(读列表)、delete_3dmodel(删条目)、cadquery_env(探测 CadQuery 环境)。
  • 服务端存储 —— 一个条目一个 JSON 文件,同一 dsh 实例下所有设备看到同一份库;不用 IndexedDB、不依赖外部服务、没有构建步骤。

安装

三条路等价,装完都要做启用与验证。

从 npm 安装(推荐)

dsh plugin --profile web add dsh-cad-viewer

包页 https://www.npmjs.com/package/dsh-cad-viewer。tarball 与仓库源码一致,只含运行所需的文件——lib/、assets/、docs/、cordis.patch.yml、CHANGELOG.md、THIRD-PARTY-NOTICES.md,加上 npm 必含的 package.json / README.md / LICENSE;dev/ 与 screenshots.json 不随包分发。

从 GitHub 或本地目录安装

dsh plugin --profile web add github:CMoyuer/dsh-cad-viewer

git clone https://github.com/CMoyuer/dsh-cad-viewer.git
cd dsh-cad-viewer && pnpm install
dsh plugin --profile web add .

dsh plugin add 会在 profile 目录里执行 pnpm,再按实际安装结果校正 dsh.profile.bundles——声明了 dsh.bundle 的包自动进插件层,不用手工编辑。插件目录不在 profile 下时请用绝对路径。

启用与验证

服务端插件只在启动时加载,改完要重启 dsh web。

dsh --profile web --dump-config    # 组合树里应出现 id: cad-viewer
curl -s -o /dev/null -w "%{http_code} %{content_type}\n" http://127.0.0.1:3080/3dmodel/api/items
curl -s -o /dev/null -w "%{http_code} %{content_type}\n" http://127.0.0.1:3080/tcv/three-cad-viewer.esm.min.js

期望 200 application/json; charset=utf-8 与 200 text/javascript; charset=utf-8。路由缺失时不是 404——SPA fallback 会返回 200 text/html,所以要看 content-type,不能只看状态码。之后刷新 GUI,打开 3D模型 tab(在「画廊」之后)。

用法

模型库

agent 生成的模型会立刻出现(按创建时间排序);点卡片即在工作台打开。

| 操作 | 方式 | |---|---| | 打开模型 | 点击卡片 | | 重命名 | 双击名称,或 ⋯ → 重命名 | | 移动 | 拖到目标文件夹(或「返回上一级」),或 ⋯ → 移动到… | | 导出 | ⋯ → 悬停「导出」(右键卡片后悬停或点击同样可以),再选格式 | | 删除 | ⋯ → 删除 | | 新建文件夹 | 在模型库面板空白处右键 → 新建文件夹(右键卡片会自动收起它) | | 搜索 | 搜索框(全库) | | 返回上一层 | 「返回上一级」卡片,或把卡片拖到它上面 |

右键卡片时菜单在鼠标位置弹出。删除文件夹不会删除内容:其中的模型与子文件夹上移一层。

库也可以交给 agent 操作:list_3dmodels 读回条目与 id,delete_3dmodel 删条目(见 Agent 工具)。

导出

同一个格式子菜单出现在两处:模型库卡片的 ⋯ → 导出,以及工作台工具栏的 导出 按钮(在 ? 之前)。

| 分组 | 格式 | |---|---| | 精确几何 | STEP .step、BREP .brep | | 网格 | STL .stl、3MF .3mf、AMF .amf、VRML .wrl、VTP .vtp、TJS .json | | 二维图纸 | SVG .svg、DXF .dxf |

导出什么,取决于该条目有没有保存 CadQuery 源码(子菜单顶部会写明是哪种):

  • 保存过源码的条目(build_3dmodel 创建):重跑脚本再交给 CadQuery 自己的 exporter,STEP / BREP 是真正的实体,文件也小。
  • 只有网格的条目(add_3dmodel 创建,或旧版本入库):由三角网格重建。STEP / BREP 把每个三角面做成平面片再缝合成壳——CAD 能打开,但表面是面片化的,文件也大(约 1 万面 → 约 20 MB);网格格式则与查看器里看到的三角形完全一致。SVG 用「轮廓 + 特征边」投影(对三角面汤做隐藏线消除要几分钟,因此不做)。

导出在服务端串行执行,界面有进度对话框(可点「后台继续」,下载仍会完成)。单次上限由 cadqueryExportTimeout 控制(默认 5 分钟)。

工作台与内嵌卡片

每个模型一个标签页,× 关闭并回到模型库。查看器的工具栏与导航树与上游一致,区别是:Pin as PNG 按钮被移除、文案中文化、多一个 导出 按钮。

某轮对话调用过模型工具时,消息末尾出现可折叠卡片:左侧标题,收起 折叠,全屏 在工作台打开同一模型;加载失败会显示重试入口。删除库里的条目不会清空已经渲染出来的卡片。

Agent 工具

| 工具 | 作用 | |---|---| | add_3dmodel | 把 three-cad-viewer Shape(cad-format JSON)存入模型库,并在消息末尾渲染内嵌卡片。 | | build_3dmodel | 运行 CadQuery 脚本,tessellate 后入库,并保存脚本(上限 256 KB)以便日后导出精确几何。装配体会拆成每个零件一个树节点,可单独显示/隐藏。 | | list_3dmodels | 只读读回模型库:id、标题、所属文件夹路径、入库时间、零件/三角面数、是否存了 CadQuery 源码,以及文件夹清单。支持 query(标题/名称/id 子串)、folder(文件夹 id、名称或路径片段;/ 表示根目录)、limit(默认 100,上限 500)。 | | delete_3dmodel | 从模型库删除条目(连同网格与已保存的 CadQuery 源码,没有撤销)。用 id 精确删除,或用 title 精确匹配(忽略大小写与首尾空格);标题匹配到多条时一条都不删,而是把候选列回来,确认后才用 all: true 全删。文件夹本身不受影响。 | | cadquery_env | 只读探测 CadQuery 环境(解释器是否在、四个包能否导入),并给出补齐缺口的准确命令。 |

// add_3dmodel:model 是 { version, parts[] },每个 part 带 shape:{ vertices, triangles, normals, edges }
{ "model": { /* … */ }, "title": "Bracket", "height": 320 }

// build_3dmodel:脚本必须 import cadquery as cq 并给 model 赋值
{ "script": "import cadquery as cq\nmodel = cq.Workplane('XY').box(20, 20, 10)", "title": "Box", "tolerance": 0.1 }

// list_3dmodels:不带参数就是整个库;下面的写法只看「零件」文件夹里标题含“齿轮”的前 10 条
{ "query": "齿轮", "folder": "零件", "limit": 10 }

// delete_3dmodel:id 与 title 都给时会互相校验,对不上就不删
{ "id": "8fb74096-b054-4a49-b4d4-6bd5a6e6f804", "title": "支架" }
  • 去重键是「几何 + 标题」:相同几何 + 相同标题再存一次只会更新原条目,不会多出第二条。
  • 标题不唯一:add_3dmodel 的去重键包含几何,所以几何不同的模型可以同名。按标题删除前先用 list_3dmodels 看清楚有几条。
  • 改现有模型必须先问用户:用户要求修改库里已有的模型时,agent 要先问是替换原条目、还是保留原条目并另存一份副本。注意同标题重存不等于替换——去重键是「几何 + 标题」,几何一变就会多出一条、原条目原样保留;替换的正确顺序是先存修改版,再按 id 删掉旧条目(中间失败也不会丢东西)。这条约定写在 add_3dmodel 与 build_3dmodel 的工具描述里,delete_3dmodel 的描述也指明它是替换的后半步。
  • build_3dmodel 的 model 必须是带 .tessellate() 的 cq.Workplane / cq.Shape;装配体请先 model = assy.toCompound()。脚本报错或没有赋值时返回 { "ok": false, "error": … },不会入库一个空模型。
  • 装配体自动分件:用 cq.Assembly 建模时不必额外做什么——插件会从脚本命名空间里的 assy 取回 toCompound() 丢掉的零件名、颜色和位置,每个零件成为模型树里的一个节点,可以单独显示/隐藏。想自己控制时,给脚本加一个模块级 parts:[(名称, 形状, 颜色), …],或 {name, shape, color} 字典列表。零件名必须唯一:树的路径是 /<模型名>/<名称>,网格也按同一个键查找,重名会让两个零件塌成一个节点(插件会自动加 _2、_3 后缀)。既没有 parts 也没有 Assembly 时,多实体的 Compound 会按实体拆成 Part_1、Part_2…
  • 单位就是 CadQuery 的单位(通常 mm),插件不做换算;单次构建超时 60 秒。

环境要求

| | | |---|---| | dsh | 可用的 dsh 安装,且已初始化 web profile(dsh web);开发环境为 @deepseek-ai/dsh 0.1.5-rc.1。 | | Node.js | ≥ 20(开发环境 Node 24)。 | | pnpm | 需在 PATH:dsh plugin 本质是 pnpm 的转发器。 | | CadQuery | 仅 build_3dmodel 与导出需要,且只装在运行 dsh web 的那台机器上。add_3dmodel 与模型库浏览不需要。 |

插件没有构建步骤,也没有 prepare 脚本,因此从 git 安装不需要在 pnpm 的 allowBuilds 里放行任何构建。

CadQuery 安装(可以让 agent 做)

CadQuery 以 Apache-2.0 发布、不随本插件分发:插件通过 cadqueryPython 配置项调用你机器上已安装的那一份。对 agent 说「装一下 CadQuery / 我要能导出 STEP」,它会按下面这个流程做完。

  1. 探测:调用 cadquery_env(只读)。它返回解释器路径与 Python 版本、cadquery / OCP / vtkmodules / ezdxf 各自能否导入、缺哪些、以及应该跑的确切命令。手工等价物:<python> lib/cadquery_probe.py。

  2. 安装:

    # Windows
    python -m venv D:\AI\3DModels\.venv
    "D:\AI\3DModels\.venv\Scripts\python.exe" -m pip install --upgrade pip
    "D:\AI\3DModels\.venv\Scripts\python.exe" -m pip install cadquery vtk
    
    # Linux / macOS
    python3 -m venv ~/cq-venv
    ~/cq-venv/bin/python -m pip install --upgrade pip
    ~/cq-venv/bin/python -m pip install cadquery vtk

    网速慢或被墙时加镜像:-i https://pypi.tuna.tsinghua.edu.cn/simple。请一次装上两个包——VTK 并不在 cadquery 的依赖声明里,但 cadquery 2.4.0 的 occ_impl.exporters 在导入期就会 import vtkmodules,缺了它连 import cadquery 都过不去,建模与全部导出都会不可用。

  3. 指向它:把解释器路径写进 profile 的 cordis.patch.yml(按插件 id 覆盖配置)。路径含空格时保留单引号:

    - id: cad-viewer
      config:
        cadqueryPython: 'D:\AI\3DModels\.venv\Scripts\python.exe'
  4. 重启 dsh web(配置只在启动时读取),再调用一次 cadquery_env 复验,应显示四个包都有版本。

| | | |---|---| | Python | ≥ 3.8;开发与验证环境为 Python 3.12 + cadquery 2.4.0 + cadquery-ocp 7.7.2 + VTK 9.7.0 + ezdxf 1.4.4。 | | 轮子平台 | cadquery-ocp 提供 Windows / Linux / macOS(arm64) 轮子;没有匹配轮子的平台改用 conda(mamba install -c conda-forge cadquery),再把 cadqueryPython 指过去。 | | 磁盘与时间 | cadquery-ocp 与 vtk 都是数百 MB 级轮子,首次安装请留出时间与空间。 |

一句话验证:"<python>" -c "import cadquery, vtkmodules, ezdxf; print(cadquery.__version__)"。

配置项

通过 dsh 配置树(schemastery)配置,一般写在 profile 的 cordis.patch.yml 里。

| 字段 | 默认值 | 说明 | |---|---|---| | apiPrefix | /3dmodel | 模型存储 API 前缀。 | | assetPrefix | /tcv | three-cad-viewer 静态资源前缀。 | | dataDir | <插件目录>/data | 模型存储目录。不要让两个 dsh 实例并发写同一个目录。 | | cadqueryPython | D:\AI\3DModels\.venv\Scripts\python.exe | 装好 CadQuery 的解释器,供 build_3dmodel 与导出使用;请改成你机器上的路径。 | | cadqueryTolerance | 0.1 | 默认 tessellation 容差(网格导出也用它)。 | | cadqueryExportTimeout | 300000 | 单次导出最长毫秒数(5 分钟)。 | | maxBodyBytes | 33554432 | JSON 请求体上限(32 MB)。 | | maxUrlLength | 2048 | 静态资源路由接受的 URL 最大长度。 |

apiPrefix / assetPrefix 同时硬编码在随包分发的 lib/client.js 里,改前缀必须一并改它。

HTTP API

路由注册在 dsh web server 上,默认前缀 /3dmodel 与 /tcv。

| 方法 | 路由 | 说明 | |---|---|---| | GET/HEAD | /tcv/<file> | 返回插件 assets/ 中的文件;拒绝路径穿越。 | | GET | /3dmodel/api/items | 列出元数据。查询参数 session、workspace、onlySession=1;每条带 hasSource(是否保存了 CadQuery 源码)。 | | POST | /3dmodel/api/items | 保存模型:{ model, title?, name?, sessionId?, workspace?, folder?, height?, script? }。 | | GET | /3dmodel/api/items/:id | 读取完整条目(含 Shape JSON 与可能的 script)。 | | PATCH | /3dmodel/api/items/:id | 更新 title / name / folder。 | | DELETE | /3dmodel/api/items/:id | 删除单个条目。 | | GET | /3dmodel/api/items/formats | 可导出格式列表({ id, ext, label, desc, group })。客户端菜单由此生成,因此不会与导出器实际支持的能力脱节。 | | GET/HEAD | /3dmodel/api/items/:id/export?format=STEP | 导出为附件下载(文件名取自标题,含 RFC 5987 filename* 以便中文标题正确落地)。未知格式 400、条目不存在 404、方法不对 405、导出失败 500。 | | GET | /3dmodel/api/items/folders | 列出全部文件夹。 | | POST | /3dmodel/api/items/folders | 新建文件夹:{ name, parent? }("" 表示根目录)。 | | PATCH | /3dmodel/api/items/folders/:id | 重命名和 / 或改父级;把文件夹移入自身或子孙会被拒绝。 | | DELETE | /3dmodel/api/items/folders/:id | 删除文件夹,其中的模型与子文件夹上移一层。 |

响应统一为 application/json; charset=utf-8,API 错误同样是 JSON({ ok: false, error: … });只有静态资源路由与少数协议层拒绝返回纯文本(400 bad id、405、414,以及 /tcv/<file> 的 403 / 404)。id 必须是 UUID 形式。

数据与存储

  • 一个模型一个文件:<dataDir>/<uuid>.json;文件夹表单独存在 <dataDir>/folders.meta.json。
  • 条目带内容指纹 h(对规范化后的 Shape JSON 取 SHA-256,忽略 parts[].name / id):相同几何 + 相同标题再存一次只会更新原条目。启动时会重算指纹,旧版本写入的条目被就地升级;启动时也会清理写入中断遗留的 *.json.tmp。
  • 写入直接落到最终文件(没有原子 rename)。重要数据请自行备份 <dataDir>;仓库的 .gitignore 排除它,正因为它属于用户数据。

常见问题

| 现象 | 原因 / 处理 | |---|---| | 看不到 3D模型 tab | 客户端半未加载,或插件不在 dsh.profile.bundles 中。查 dsh --profile web --dump-config 与浏览器控制台。 | | /3dmodel/api/items 返回 200 text/html | 插件未挂载,拿到的是 SPA fallback;看启动日志里的加载错误。 | | 查看器一片空白 | /tcv/three-cad-viewer.esm.min.js 路由失败(看 content-type),或浏览器拦了动态 import()。面板内会显示 three-cad-viewer 加载失败: …。 | | build_3dmodel 或导出立即失败 | cadqueryPython 指的解释器没有装 CadQuery;改指向装好的 venv。 | | Windows 上报 DLL / import 错误 | lib/cadquery_build.py 规避的 OCCT/VTK 冲突——确认该文件的 import 顺序没被改动。 | | 保存模型返回 500 | 请求体超过 maxBodyBytes,或 dataDir 不可写。 | | 导出很慢或超时 | 只有网格的条目导出 STEP / BREP 要把每个三角面缝成 B-Rep(约 1 万面数秒,更多会到几十秒)。调大 cadqueryExportTimeout,或改用网格格式,或让模型由 build_3dmodel 生成(有源码后走精确几何,快得多)。 | | 改了代码没反应 | lib/index.js、lib/cadquery_build.py 要重启 dsh web;lib/cadquery_export.py 每次导出重新起进程,改完立即生效;lib/client.js 刷新页面即可。 | | 改 cadqueryPython 不生效 | 忘了重启 dsh web(配置只在启动时读)。 | | 插件换目录后模型库空了 | dataDir 默认是 <插件目录>/data;把旧的 data/ 拷过去,或把 dataDir 指过去。 |

开发

没有打包器,也没有编译步骤。

node dev/route-test.mjs           # 伪 ctx 挂载插件,跑通路由与 10 种格式的真实导出
node dev/tool-test.mjs            # 伪 ctx + 临时 dataDir,跑通 5 个 agent 工具(入库 / 列表 / 删除)
node dev/client-render-test.mjs   # 伪 React/DOM 渲染客户端,跑通 菜单 -> 导出 -> 下载
powershell -File dev/restart-verify.ps1   # 预检 + 重启 + 健康检查 + 失败自动回滚

tool-test.mjs 用的是临时 dataDir(%TEMP% 下的随机目录,跑完即删),不会碰真实模型库。

restart-verify.ps1 会杀掉正在运行的 dsh(因此也会杀掉正在跑它的 agent 会话),必须以分离进程启动,否则它会终止自己;结果写在 dev/restart-log.log:

Invoke-CimMethod -ClassName Win32_Process -MethodName Create `
  -Arguments @{ CommandLine = 'powershell.exe -NoProfile -ExecutionPolicy Bypass -File <插件绝对路径>\dev\restart-verify.ps1' }

它的健康检查除了两条基础路由,还会真实下载 STL / STEP / SVG / DXF / 3MF / VTP 各一份并检查字节数与响应头;任何一项失败就写禁用 patch 并重启,保证 GUI 可用。

改代码时守住几条:inject 必须列出 apply 中访问的每一个 ctx.* 服务(webServer、tools),漏写会抛 cannot get property "X" without inject 并让整棵树加载失败;在 ctx.effect 中注册的路由必须返回 disposer。

目录结构

lib/       服务端 index.js、客户端 client.js、三个 CadQuery Python 脚本
dev/       开发用测试与安全重启脚本(不参与运行时)
assets/    随插件分发的 three-cad-viewer 构建产物(见 assets/README.md)
docs/      README 用到的截图

cordis.patch.yml 把插件插入为 cad-viewer;服务端加载 lib/index.js,注册一个静态资源前缀路由与一个模型 API 前缀路由,并在 ctx.tools 上注册五个工具;客户端注册 conversation.view(id: "model"、order: 21)承载 tab、注册 conversation.chat.turnTail 承载内嵌卡片,并用普通 fetch 访问 API。

AI 生成声明

本仓库的代码与文档由 AI 编程助手生成,维护者负责提出需求、审阅改动并决定是否发布。每项功能改动都记在 CHANGELOG 里,主要路径有可独立运行的验证脚本(见开发);但请自行判断它是否适合你的使用场景。

第三方声明

完整声明见 THIRD-PARTY-NOTICES.md(随 npm 包分发)。要点:

  • three-cad-viewer(MIT,© Bernhard Walter)的预构建产物随插件分发在 assets/,其中内嵌 three.js(MIT,© Three.js Authors,r184);上游代码未做修改。
  • CadQuery(Apache-2.0,© 2015 Parametric Products Intellectual Holdings, LLC)不随本插件分发,由 cadqueryPython 指定的解释器提供;导出路径还会间接用到 cadquery-ocp / OCCT、ezdxf(MIT)与 VTK(BSD)。
  • 各名称与商标归各自所有者;本插件与上述项目无从属关系,也未获得其背书。

许可证

MIT © 2026 CMoyuer