跳到内容
CADCAMFUN
文档

文档

桌面用户界面

SolidJS 工作区、本地 API 客户端和拥有本机集成的 Tauri shell。

从英文自动翻译。 查看英文原文

应用架构

桌面应用程序结合了 SolidJS Webview、Vite、Three.js 和 Tauri v2 Rust shell。 webview 拥有屏幕和交互状态。 Bun sidecar 拥有 API 处理程序、数据库访问、AI 会话和项目服务。 Rust 进程拥有窗口、菜单、托盘、操作系统对话框、钥匙串访问、更新和 sidecar 生命周期。

apps/desktop/src/main.tsx 安装应用程序。 app/App.tsx 建立 API 连接并提供共享状态; app/routes.tsx 将 URL 映射到惰性页面模块。功能屏幕实时显示 features/。路由的存在并不能确定其外部服务的可用性:计费需要 Stripe 配置,而 AI 需要配置的提供商。

启动和连接恢复

  1. shell 使用生成的启动令牌以及应用程序数据和迁移目录启动捆绑的 sidecar。
  2. sidecar 绑定了一个临时端口 127.0.0.1,等待其健康路由应答,并写入 JSON ready 包含端口的事件。
  3. 网络视图调用 get_sidecar_endpoint 并创建 API 客户端和托管 Effect 运行时。
  4. 成功的运行状况请求会创建应用程序状态并安装路由器和 shell。

Web 视图允许 150 秒进行连接恢复,每次尝试有 30 秒超时和 750 毫秒重试暂停。失败的尝试将丢弃缓存的端点,以便重新启动的 sidecar 可以提供新端口。 Rust 管理程序为每次准备好的握手等待 60 秒,并允许一次自动重启。当 webview 连接预算到期时,重试变得可用。

浏览器预览使用内存中 API

在 Tauri 之外,没有浏览器 VITE_SIDECAR_URL 用途 src/testing/fake-api.ts。预览数据是运行页面本地的;不受支持的集合返回 HTTP 501。要将浏览器连接到真正的 sidecar,请同时提供 VITE_SIDECAR_URL 和 VITE_SIDECAR_TOKEN 启动Vite时。

屏幕路由

区域路线和模块
项目/projects, /projects/create, /projects/:id, 和 /projects/:id/history;屏幕中 features/projects/screens.
设计与制造/cad 坐骑 CadPage; /cam 坐骑 CamPage。保存的工程图和刀具路径具有单独的详细路线。
资源/library, /materials, /tools, /machine,其详细信息页面使用库功能模块。组件有列表和详细路由。
助手/ai 与对接的助手面板共享AI功能状态。 AI 活动切换面板。
设置/settings, /settings/companion、MCP 设置和订阅设置。
账户与协作身份验证、个人资料、组织、邀请、聊天、市场、插件、成本、分析和管理路由每个加载功能模块。

使用 app/routes.tsx 准确的参数名称和 pages/ 定位安装在 URL 上的屏幕。路由适配器故意做得很小;在诊断空视图或失败操作时读取屏幕及其 API 调用。

来源组织

  • app/:启动、路由器、全局 API 客户端、本机集成、shell 和键盘快捷键。
  • pages/:路由适配器导入或重新导出屏幕。
  • features/:具有屏幕、组件、挂钩、状态和特定领域帮助程序的产品区域。
  • shared/:可重用控件、shell 组件、应用程序状态、查询缓存和命令历史记录。
  • viewport/:画布绘制、捕捉、工具手势、Three.js 渲染、网格划分和播放。
  • i18n/, styles/, 和 testing/:翻译、全局样式和测试装置。

桌面取决于 @cadcamfun/api 对于 HTTP 合约以及 @cadcamfun/core 用于几何和 CAM 操作。数据库持久性仍然支持 sidecar API。本地编辑器历史记录与持久的绘图或刀具路径版本以及项目版本控制不同。

原生外壳

默认情况下,生产窗口为 1440 × 900,最小值为 960 × 600。 Tauri 暂存前端 apps/desktop/dist,一个特定于目标的边车 src-tauri/binaries,并生成迁移作为资源。它注册 .cadcam, .nc, 和 .gcode 文件关联。打开支持的文件或启动第二个实例会将文件路径转发到现有窗口。

src-tauri/src/sidecar.rs 监督子进程。关闭应用程序会写入 {"event":"shutdown"} 线路到孩子的标准输入并在收获之前短暂等待;孩子还观察到 stdin 关闭。启动令牌是 32 随机字节,十六进制编码为 64 字符,并且可能包含凭据的 sidecar 日志行已被编辑。日志与标准输出分开,标准输出携带就绪/错误握手。本机机密可通过以下方式访问 src-tauri/src/secrets.rs 使用操作系统钥匙串。

在 macOS 上,shell 注册 Liquid Glass 插件。 webview 跟随玻璃设置,当效果不可用时回退。 App Store 配置禁用私有 API 选项;常规配置可以启用它。这些是单独的构建配置。

安全和持久性边界

生产内容策略允许本地资产、环回 API 连接、数据/blob 图像和 WebAssembly 评估。它不授予任意远程脚本执行权限。本机权限范围为 src-tauri/capabilities;开发策略还允许 Vite 及其 websocket。

open_path 规范化路径,将其限制为允许的用户/应用程序目录,并拒绝可执行扩展,例如 .exe, .app, .sh, .bat, .msi, 和 .jar. .. 段和符号链接无法转义允许的文件夹,因为双方都已规范化。文件选取和保存使用本机对话框和验证。启动令牌授予对本地应用程序 API 的访问权限;将其与配套代币或项目范围的 AI 桥代币分开对待。

UI 设置、API 查询状态、编辑器命令、存储的项目版本和本机机密具有不同的所有者。通过其实际服务来诊断每个应用程序,而不是假设所有应用程序状态都存储在 SQLite 中。

功能集成

CAD 和 CAM 功能模块将可重用的视口组件与其自己的面板、存储和保存流程组成。助理面板接收项目和编辑器上下文,并通过 AI 功能状态流式传输工具事件。资源屏幕使用 API 客户端,而不是直接访问数据库。

对于 UI 更改,请按照路由适配器到达其功能屏幕,然后检查发送请求的功能存储或挂钩。仅当共享组件的现有契约与交互匹配时才重用共享组件。在核心包中保留纯几何形状,在 Rust shell 中保留本机操作系统行为。

路线加载和错误

每条路线都使用 Solid 的延迟导入边界。该表包括一个包罗万象的页面, /500, /offline,以及合法途径。必须通过 Vite 开发服务器或打包的 webview 提供直接的浏览器导航,以便客户端路由器可以解析 URL。

预览 API 实现了一组有限的集合,并且可以在测试中故意注入请求失败。成功的预览渲染演示了针对这些装置的 UI 行为;验证持久性、权限、流媒体以及与实时 sidecar 的外部集成。

本机命令接口

命令目的
get_sidecar_endpoint等待受监督的 sidecar 并返回其基本 URL 和启动令牌。等待涵盖一次重新启动:两次 60 秒就绪超时加上 5 秒(125 秒)。
get_logs_dir, open_path将应用程序日志目录解析为字符串,并使用操作系统打开程序打开现有的绝对路径。路径必须在应用程序数据、日志、文档、下载或桌面文件夹下解析。
secret_get, secret_set, secret_delete访问本机钥匙串条目 com.cadcamfun.app 服务。键是 1–128 字节的 ASCII 字母、数字和 . _ - : (例如 ai.anthropic.apiKey); secret_get 回报 null 对于缺少的条目。
pick_file, save_filepick_file({ filters: [{ name, extensions }] }) 返回所选路径或 null 取消。 save_file({ defaultName, contents }) 将 UTF-8 文本写入所选路径并返回它,或者 null 取消时;目录和可执行扩展被拒绝。
set_recent_projects刷新托盘的最近项目菜单。
check_for_updates, install_update请求更新程序检查,或安装待处理的更新。 install_update 接受可选的 unsavedWork 标志(默认 false)因此安装不会中断打开的编辑器。

The invoke handler is registered in src-tauri/src/lib.rs. Capabilities and generated command permissions also control availability; command registration alone does not establish webview access. Command errors are serialized as { kind, message } with kinds such as sidecar, invalid_path, io, dialog, and path; no command panics.

原生事件

外壳发射 sidecar-status 对于生命周期的改变, open-file 具有受支持的路径, menu 用于菜单选择,以及 update://status 了解更新程序进度。托盘最近项目选择由 tray.recent.<projectId> 菜单标识符。 shell 的菜单和托盘模块定义了它们完整的标识符集。

侦听器的范围应限定在其组件或应用程序生命周期内,并在清理时删除。 sidecar 重启后,webview 必须重新解析端点,而不是继续使用之前的端口。

开发验证

从存储库根运行
bun run --filter @cadcamfun/desktop dev
bun run desktop:dev
bun run --filter @cadcamfun/desktop typecheck
bun test apps/desktop/src
bun run build:desktop

第一个命令运行浏览器预览。 desktop:dev 在启动 Tauri 之前编译真正的 sidecar 并进行迁移。桌面包的 tauri:dev 相反,脚本仅在不存在二进制文件时创建存根二进制文件;将其用于隔离的 shell 工作,并在解释其 API 响应之前检查存在哪个二进制文件。

桌面 build 脚本运行 vite build 仅有的;它不会编译 sidecar 或打包 Tauri 包。使用 bun scripts/build.ts 有关完整的发布管道(请参阅 发布).

首选项和设置

共享设置存储在下保留经过验证的记录 cadcamfun.settings 在webview本地存储中。缺失、无效或不可读的字段会独立回退。存储区域已满或被阻塞不会停止编辑,但首选项更改可能无法保留。浏览器预览首选项属于浏览器源,与本机 Web 视图首选项分开。

环境接受值/默认值运行效果
主题light, dark, system;默认系统系统模式遵循操作系统颜色方案的变化。
显示单位mm 或 in;默认毫米控制演示;核心绘图坐标仍为毫米。
小数整数 0–6;默认 3长度表示精度。
默认帖子grbl, linuxcnc, smoothwb, gcode, fanuc;默认GRBL选定的输出语法是一种偏好,而不是机器已配置或验证的证明。
图纸公差经验证的核心公差;默认 0.01 毫米绘图/刀具路径使用者使用的共享精度偏好。
捕捉状态布尔值;默认启用共享状态偏好。个别编辑有自己的抢拍行为;独立的 Editor2D 目前在内部启用其网格捕捉。
玻璃布尔值;默认启用当本机主机支持时请求 macOS 窗口材料。

“设置”屏幕包含“配置文件”、“帐户”、“安全”、“订阅”、“通知”、“AI”、“设备”和“语言”选项卡。 “设备”选项卡嵌入了配套网关面板。语言设置包括玻璃开关。通用语言环境存储当前验证默认语言环境;不要从显示标签列表中推断出额外的翻译覆盖范围。

保存并重新打开 CAM 程序

  1. 在 CAM 中生成或加载 NC 代码,并从现有入口点打开保存/导出对话框。
  2. 对于文件导出,请选择 G 代码或便携式刀具路径格式。 G代码下载当前NC文本;刀具路径选择写入结构化 .cam.json 快照。
  3. 对于应用程序持久性,选择现有项目、新项目或更新已保存的程序。提供程序名称和所需的目标字段。
  4. 更新时,保存流程会在替换当前数据之前将先前的完整程序记录为刀具路径版本。
  5. 在其刀具路径详细路径中打开已保存的项目以检查或恢复版本。

保存摘要包括 NC 行、字节、移动、刀具标识符、操作名称、库存、单位、零点偏移和验证标识(当该证据属于当前 NC 时)。修改后的 NC 文本可能会使之前计算的证据变得陈旧。在依赖摘要之前检查当前的代码/证据关系。

如果创建新项目成功,但稍后的保存步骤失败,则模式会保留创建的目标以供重试,并将其标识符包含在错误中。在该模式中重试可以避免再次创建相同的项目。不要假设保存流程是一项原子跨资源事务。

文件导出和项目持久化是不同的操作

下载的 G 代码文件不会创建项目刀具路径行。便携式快照比普通 NC 具有更丰富的状态。通过从应用程序重新打开保存的程序来验证它,并通过检查下载的文件来验证导出。

键盘及故障诊断

Cmd/Ctrl+K 打开命令面板并 Cmd/Ctrl+J 切换助手面板。当焦点位于输入、文本区域、选择或内容可编辑元素时,Shell 快捷键将被忽略。编辑器画布有自己的键盘处理。一些 shell 命令回调,包括通用快捷方式表中的 Save/Export,都是占位符;使用该功能的实际保存/导出控件,而不是假设每个列出的加速器都执行文档操作。

观察到的行为查看
启动等待,然后出现重试Sidecar 错误/就绪输出、目标二进制文件、迁移资源和应用程序日志。缓存的端口可能属于先前的进程。
屏幕呈现但请求返回 501浏览器固定模式。启动真正的 sidecar 并传递两个 Vite 端点变量。
桌面数据与手动脚本不同每个进程的数据目录和token;单独的文件名并不能识别数据库。
本机命令不可用调用注册、能力权限、操作系统支持以及代码是否在 Tauri 内部运行。
重新启动后恢复设置存储可用性和验证。无效的存储字段将恢复为默认值。
保存的程序证据与 NC 不同NC生成后是否经过编辑。重新计算实际文本/程序的适当验证。

实施锚点: shared/state/settings.ts, app/shortcuts.ts, features/settings/screens/settings.tsx, 和 features/cam/components/SaveCamModal.tsx.

本机事件负载

事件有效载荷笔记
sidecar-status{ state: "starting" }, { state: "ready", port }, { state: "restarting" }, { state: "failed", message }后 restarting, 称呼 get_sidecar_endpoint 再次;端口发生变化。
update://statusstatus: checking, upToDate (version), available (version, notes, date, critical), downloading (downloaded, total), ready (version), failed (message);加 dismissible重要更新和挂起的重新启动不可忽略。失败的检查永远不会显示为阻塞错误。
menu菜单项ID,例如 file.new, file.open, file.save, view.devtools, help.documentation托盘物品用途 tray.quit 和 tray.recent.<projectId>.
open-file的路径 .cadcam, .nc, 或者 .gcode 文件当操作系统使用应用程序打开文件或启动第二个实例时发送。
install_update 可以通过键入原因来拒绝: unsavedWork (“编辑器有未保存的工作;保存后应用更新”), noneAvailable (没有待处理或已安装),或者 failed (下载或签名检查失败)。它仅安装上次检查宣布的更新。
从 webview 调用本机命令
import { invoke } from "@tauri-apps/api/core"
import { listen } from "@tauri-apps/api/event"

const { baseUrl, token } = await invoke<{ baseUrl: string; token: string }>("get_sidecar_endpoint")

const path = await invoke<string | null>("pick_file", {
  filters: [{ name: "G-code", extensions: ["nc", "gcode"] }],
})                                    // null when the user cancels

await invoke("save_file", { defaultName: "part.nc", contents: gcodeText })

const unlisten = await listen<{ state: string; port?: number }>("sidecar-status", (e) => {
  if (e.payload.state === "ready") reconnect()
})
// later: unlisten()

try {
  await invoke("install_update", { unsavedWork: editorIsDirty })
} catch (error) {
  // { kind, message }: the update variant carries the refusal (unsavedWork | noneAvailable | failed)
}

get_sidecar_endpoint 回报 { baseUrl, token } (以驼峰命名法序列化,来自 SidecarEndpoint)。每个命令的错误都会作为拒绝值到达 invoke, 形 { kind, message }.

键盘快捷键

捷径行动团体
Cmd/Ctrl+K打开命令面板导航
Cmd/Ctrl+Shift+P新项目导航
Cmd/Ctrl+Z / Cmd/Ctrl+Shift+Z撤消/重做编辑
Cmd/Ctrl+S, Cmd/Ctrl+Shift+E保存、导出(shell 占位符;使用该功能自己的控件)编辑
Cmd/Ctrl+0缩放以适合看法
Cmd/Ctrl+= / Cmd/Ctrl+-放大/缩小看法
Cmd/Ctrl+J切换辅助面板人工智能
Cmd 适用于 macOS 和 Ctrl 别处。每个快捷方式的 ID 也是其翻译键后缀(例如 settings.shortcut.palette),并且“设置”屏幕呈现相同的表格,因此文档和应用程序不会分离。

各画面路线

路由器在 apps/desktop/src/app/routes.tsx 声明 68 路由,每个路由都从 apps/desktop/src/pages/ (动态段是 _id_.tsx 文件)。按第一段分组:

区域路线
**
500/500
admin/admin/analytics /admin/feature-flags
ai/ai
analytics/analytics/history /analytics
auth/auth/forgot-password /auth/magic-link /auth/magic-link/verify /auth/reset-password /auth/signin /auth/signup /auth/two-factor /auth/verify-email /auth/verify
cad/cad
cam/cam
costs/costs
components/components/:id /components
drawings/drawings/:id
//
invitations/invitations/:token
legal/legal/privacy /legal/terms /legal/third-party-licenses
library/library
machine/machine/:id /machine
marketplace/marketplace /marketplace/items/:id /marketplace/purchase/success /marketplace/purchases /marketplace/seller/dashboard
materials/materials/:id /materials
mcp/mcp/directory /mcp/servers/:id
offline/offline
organizations/organizations/:id /organizations/:id/chat/:conversationId /organizations/:id/chat /organizations/:id/chat/new-group /organizations/:id/chat/new /organizations/create /organizations
plugins/plugins/:id /plugins
pricing/pricing
profile/profile /profile/settings
projects/projects/:id/history /projects/:id /projects/create /projects
settings/settings /settings/companion /settings/mcp/config-editor /settings/mcp/edit/:id /settings/mcp /settings/subscription
third-party-licenses/third-party-licenses
toolpaths/toolpaths/:id /toolpaths
tools/tools/:id /tools
waitlist/waitlist
* 呈现 404 页面并 /500 错误页面。 /offline 渲染离线屏幕;与身份验证、邀请、404、500 和购买成功页面一样,它使用独立布局,无需应用程序镶边。因为每个页面都是惰性导入,所以屏幕的代码仅在第一次访问时加载;失败的块加载表现为路由错误,而不是空白应用程序。

在 3D

桌面进程拓扑 拖动进行轨道旋转·滚动进行缩放·右键拖动进行平移
Processes at run time. The Tauri shell spawns the sidecar and waits for its {”event”:”ready”,”port”:N} line on stdout (sidecar.rs); the webview then talks HTTP to 127.0.0.1 on that port with the bearer token. The sidecar owns the SQLite database, the embedded nikcli AI host (CADCAMFUN_AI_HOST=nikcli, the default) and plugins; the mobile app reaches it through companion pairing.