VizTyp/AGENTS.md

825 lines
39 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# AGENTS.md
> Agent 工作指南 / Agent Working Guide
> 本文件原为**新项目创建蓝图**,现已记录 VizTyp 的完整实现(全部功能已完成)。
> This file was a **greenfield blueprint**, now documents VizTyp's full implementation (all 5 phases complete).
---
## 项目概述 / Project Overview
VizTyp 是一个基于 Typst 的**个人与团队知识管理系统**,融合语雀的结构化知识管理与 MrDoc 的私有化部署理念,采用**思源笔记**风格 IDE 布局,面向 Web 和 Tauri 桌面双平台。
VizTyp is a **personal and team knowledge management system** based on Typst, combining Yuque's structured knowledge management with MrDoc's self-hosted philosophy, with a **SiYuan-note** style IDE layout, targeting both Web and Tauri desktop platforms.
### 核心定位 / Core Positioning
> **"可本地优先的语雀" + "实时可视化的 MrDoc"**
> "Local-first Yuque + real-time visual MrDoc"
| 特性 / Feature | 说明 / Description |
|---|---|
| **本地优先 / Local-first** | Typst 文件存储在本地磁盘,支持 Git 版本控制,不依赖云端 (vs 语雀/MrDoc 的云端存储) |
| **实时编译 / Real-time compilation** | Typst 源码通过 typst.ts WASM 在浏览器/webview 中实时编译,生成 SVG 预览 (语雀/MrDoc 均无此能力) |
| **Typst 作为规范格式 / Typst as canonical format** | 开放、可编译、可版本控制,非私有格式 (vs 语雀的 Lake 私有格式 / MrDoc 的 Markdown 字符串) |
| **可视化知识图谱 / Visual knowledge graph** | 基于 `#include`/`#import` 的依赖图可视化,超越语雀和 MrDoc 的隐式引用 |
| **思源风格 IDE / SiYuan-style IDE** | ActivityBar + LeftDock + Center(Tabs + Editor/Preview Split) + RightDock + StatusBar |
| **双平台共享 / Dual-platform sharing** | Web 和桌面使用同一套 Svelte 5 前端,零重复代码 |
### 竞品对比 / Competitive Analysis
| 维度 / Dimension | 语雀 / Yuque | MrDoc (觅思文档) | **VizTyp** |
|---|---|---|---|
| **层级结构 / Hierarchy** | Team → Book → Doc (严格三层) | Project → Doc (`parent_doc` 自引用) | Workspace → KB → Doc (Typst `#include` 树) |
| **内容/结构 / Content/Structure** | **解耦**: 文档扁平存储, TOC 独立树 | **双重**: `parent_doc` + `ProjectToc` JSON | `refs.ts` 解析 `#include` 树 (已实现) |
| **文档格式 / Doc format** | Lake (私有 block/card 格式) | Markdown 字符串 (无 AST) | **Typst** (开放, 可编译, 可 Git) |
| **草稿/发布 / Draft/Publish** | `body_draft` vs `body`, `status` 0/1 | `pre_content` vs `content` | `.typ.draft` sidecar + publish (已实现) |
| **权限 / Permissions** | Team: Admin/Member/ReadOnly; KB: reader/writer | 4 级: 公开/私密/指定用户/访问码 | 4 级 + JWT 认证 + 协作者角色 (已实现) |
| **实时协作 / Real-time collab** | 实时协同 → 降级为锁模式 | 无 (异步编辑 + 版本快照) | typst.ts 实时 SVG 预览 + presence 轮询 |
| **知识图谱 / Knowledge graph** | 隐式 (文档嵌入), 无可视化 | 无 | **DependencyGraph.svelte** 手写 SVG (已实现) |
| **搜索 / Search** | 分层 scope (user→team→book) | 权限感知 (预过滤 project IDs), Whoosh+jieba | 权限感知全文搜索 (预过滤可见 KB, 已实现) |
| **部署模式 / Deployment** | SaaS 云端 | 私有化部署 (Django 单体) | **本地优先** (文件 + 可选同步) |
| **技术栈 / Tech stack** | Java 后端 + React 前端 | Django SSR + jQuery/LayUI | **Svelte 5 SPA + Tauri** |
#### 借鉴语雀的模式 / Patterns Adopted from Yuque
1. **解耦内容/结构 / Decoupled content/structure** — 文档扁平存储, TOC 独立树, 支持重组不触碰内容
2. **草稿/发布分离 / Draft/publish separation**`body_draft` vs `body`, `status` 0/1
3. **卡片/块扩展 / Card/block extensibility** — Typst 的 CeTZ/Fletcher 块已天然支持
4. **分层搜索 scope / Hierarchical search scope** — user → team → team/book
5. **文档嵌入与引用 / Document embedding**`#include` 实现内容嵌入
#### 借鉴 MrDoc 的模式 / Patterns Adopted from MrDoc
1. **4 级可见性 / 4-tier visibility** — 公开/私密/指定用户/访问码
2. **协作者角色 / Collaborator roles** — 0=仅自身文档, 1=所有文档
3. **双重表示 / Dual representation**`parent_doc` + 序列化 TOC JSON, 支持拖拽排序
4. **权限感知搜索 / Permission-aware search** — 预过滤可见 ID 列表
5. **文档分享令牌 / DocShare token** — 独立于项目权限的单文档分享
6. **软删除 / Soft delete**`status` 枚举 + 回收站
#### VizTyp 的差异化优势 / VizTyp's Differentiators
1. **实时 SVG 渲染 / Real-time SVG rendering** — typst.ts WASM 编译,语雀和 MrDoc 均无
2. **Typst 规范格式 / Typst canonical format** — 开放、可编译、可 Git,非 Lake 私有
3. **可视化知识图谱 / Visual knowledge graph**`DependencyGraph.svelte` 超越两者
4. **本地优先 / Local-first** — 文件存储在磁盘,天然 Git 兼容
---
## 知识管理架构 / Knowledge Management Architecture
### 三层结构 / Three-Layer Structure
借鉴语雀和 MrDoc 的核心模式,VizTyp 采用三层知识管理结构:
Drawing from Yuque and MrDoc's core patterns, VizTyp adopts a three-layer knowledge management structure:
```
┌─────────────────────────────────────────────────────────────────┐
│ Workspace (工作空间) │
│ ├── KnowledgeBase (知识库) ← 一个 .typ 项目文件夹 / Typst project folder
│ │ ├── Document (文档) ← 单个 .typ 文件 / single Typst file
│ │ │ ├── Sub-document ← #include 的子文件 / included child
│ │ │ └── (无限嵌套) ← 基于 #include 的自然树
│ │ ├── Document ...
│ │ └── TOC (目录树) ← 解耦的独立结构树 / Decoupled structure tree
│ ├── KnowledgeBase ...
│ └── (知识库集合) / KB collection
└─────────────────────────────────────────────────────────────────┘
```
### 核心数据模型设计 / Core Data Model Design
> ✅ **以下类型已在 `types.ts` + 后端 `models.rs` 完整实现。前端与后端通过 `api/` 客户端交互。**
> ✅ **These types are fully implemented in `types.ts` + backend `models.rs`. Frontend↔backend via `api/` client.**
#### 类型定义 / Type Definitions (planned for `types.ts`)
```typescript
// 工作空间 / Workspace — 顶层容器 / Top-level container
interface Workspace {
id: string;
name: string;
knowledgeBases: KnowledgeBase[];
members?: WorkspaceMember[]; // 团队成员 / Team members (future)
createdAt: number;
}
// 知识库 / KnowledgeBase — 类似语雀的 Book 或 MrDoc 的 Project
interface KnowledgeBase {
id: string;
name: string;
icon?: string;
description: string;
rootPath: string; // 本地文件夹路径 / Local folder path
visibility: KBVisibility; // 可见性 / Visibility level
collaborators?: Collaborator[]; // 协作者 / Collaborators (future)
toc: TOCNode[]; // 解耦的目录树 / Decoupled TOC tree
tags: Tag[]; // 标签 / Tags
createdAt: number;
updatedAt: number;
}
// 可见性 / Visibility — 借鉴 MrDoc 4 级权限
type KBVisibility =
| 'public' // 公开 — 任何人可读 / Anyone can read (role=0)
| 'private' // 私密 — 仅所有者 + 协作者 / Owner + collaborators only (role=1)
| 'specified' // 指定用户可见 / Specified users (role=2)
| 'access_code'; // 访问码可见 / Access code required (role=3)
// 目录树节点 / TOC Node — 借鉴语雀的 TOC 解耦模式
interface TOCNode {
uuid: string;
type: 'DOC' | 'TITLE' | 'LINK'; // 文档引用 / 纯分组 / 外部链接
title: string;
docId?: string; // type=DOC 时的文档 ID
parentId?: string; // 父节点 UUID (构建树)
level: number; // 深度, 0=root
sortOrder: number; // 同级排序 / Ordering among siblings
openByDefault?: boolean; // 默认展开 / Expand by default
}
// 文档 / Document — 单个 Typst 文件
interface Document {
id: string;
kbId: string; // 所属知识库 / Parent KB
title: string;
filePath: string; // 本地文件路径 / Local file path
content: string; // 发布内容 / Published content
draftContent?: string; // 草稿内容 / Draft content (借鉴 MrDoc pre_content)
status: 'draft' | 'published' | 'deleted'; // 软删除 / Soft delete
editorMode: 'typst'; // 编辑器模式 (未来可扩展)
tags: string[]; // 标签 / Tags
wordCount: number;
createdAt: number;
updatedAt: number;
publishedAt?: number;
}
// 协作者 / Collaborator (future)
interface Collaborator {
userId: string;
kbId: string;
role: 'reader' | 'writer' | 'admin';
}
// 标签 / Tag
interface Tag {
id: string;
name: string;
color?: string;
}
// 文档分享 / Document Share — 借鉴 MrDoc DocShare
interface DocShare {
token: string;
docId: string;
shareType: 'public' | 'private'; // private 需访问码
shareCode?: string; // 私密分享的访问码
isEnabled: boolean;
createdAt: number;
}
// 版本历史 / Version History
interface DocVersion {
id: string;
docId: string;
content: string; // 完整快照 / Full snapshot (MrDoc 模式)
creator: string;
createdAt: number;
}
```
### 权限系统设计 / Permission System Design
#### 知识库可见性 (借鉴 MrDoc) / KB Visibility (from MrDoc)
| 可见性 / Visibility | 说明 / Description | 访问控制 / Access Control |
|---|---|---|
| `public` | 公开 / Public | 任何人可读 / Anyone can read |
| `private` | 私密 / Private | 仅所有者 + 协作者 / Owner + collaborators only |
| `specified` | 指定用户 / Specified users | 白名单用户 / Whitelisted user IDs |
| `access_code` | 访问码 / Access code | 需输入访问码 (存储于 cookie/localStorage) |
#### 协作者角色 (借鉴 MrDoc + 语雀) / Collaborator Roles (from MrDoc + Yuque)
| 角色 / Role | 能力 / Capabilities |
|---|---|
| `reader` | 只读 / Read only |
| `writer` | 创建文档, 编辑/删除自身文档 / Create docs, edit/delete own docs |
| `admin` | 创建文档, 编辑所有文档, 管理知识库 / Create docs, edit all docs, manage KB |
#### 权限感知搜索 (借鉴 MrDoc) / Permission-Aware Search (from MrDoc)
```
搜索流程 / Search flow:
1. 获取用户可见的知识库 ID 列表 / Get visible KB ID list for user
2. 在可见范围内执行全文搜索 / Execute full-text search within visible scope
3. 返回结果带知识库上下文 / Return results with KB context
```
---
## 技术栈 / Tech Stack
| 层 / Layer | 选型 / Choice | 版本 / Version |
|---|---|---|
| 前端框架 / Frontend | Svelte 5 (runes 模式) + Vite | Svelte `^5.55`, Vite `^8.0` |
| 语言 / Language | TypeScript | `~6.0` |
| 代码编辑器 / Code editor | CodeMirror 6 (basicSetup + 自定义思源主题) | `^6.0` |
| Typst 集成 / Typst | typst.ts 运行时编译 (Mode B) | `@myriaddreamin/*` 锁定 `0.7.0` |
| 桌面外壳 / Desktop | Tauri v2 | `@tauri-apps/cli ^2` |
| 包管理器 / Package manager | Yarn (classic) | — |
| Typst 可视化 / Typst viz | CeTZ + Fletcher (消费) + 自研库 | — |
| 全文搜索 / Full-text search | (规划中 / planned) flexsearch 或 lunr | — |
---
## 布局架构 / Layout Architecture
```
┌─ ActivityBar (40px) ─┬─ LeftDock (220px) ─┬─ Center Area ─────────────┬─ RightDock (260px) ─┐
│ │ │ TabBar (32px) │ DockPanel │
│ 📁 知识库树 │ DocTree │ ┌─────────┬─────────┐ │ (Outline/Diag/Mark) │
│ 🔍 搜索 │ (文件导航 + │ │ Editor │ Preview │ │ │
│ 🔖 书签 │ 右键菜单 + │ │ (CM6) │ (SVG) │ │ · 大纲 Outline │
│ 🕸 依赖图 │ #include 树) │ │ │ │ │ · 诊断 Diagnostics │
│ 📤 分享 (规划中) │ │ │ │ │ │ · 书签 Bookmarks │
│ │ │ └─────────┴─────────┘ │ │
新建 │ │ │ │
│ 📂 打开 │ │ (拖拽分隔条调整比例) │ │
│ 💾 保存 │ │ │ │
│ ⚙️ 设置 │ │ │ │
│ 🌙 主题 │ │ │ │
├──────────────────────┴─────────────────────┴───────────────────────────┴─────────────────────┤
│ StatusBar (26px): 子块统计 | 字符/词/行 | 光标 Ln/Col | 选区计数 | 同步状态 │
└──────────────────────────────────────────────────────────────────────────────────────────────┘
```
### 布局演进计划 / Layout Evolution Plan
| 版本 / Version | LeftDock 内容 / Content | 说明 |
|---|---|---|
| ~~早期 / Early~~ | ~~DocTree (已打开 Tab 列表)~~ | 多 Tab 编辑, 每个 Tab 一个 .typ 文件 |
| **当前 / Current** | **KBTree + DocTree (顶部 tab 切换)** | 类似语雀: 左侧显示所有知识库, 点击展开文档树; 顶部切换"知识库/已打开" |
| **未来 / Future** | KBTree + 搜索 + 收藏 + 标签 | 类似语雀工作台 |
---
## 目录结构 / Directory Structure
```
viztyp/
├── packages/
│ └── frontend/ # Svelte 前端 (Web + Tauri 共享)
│ ├── package.json # 前端依赖
│ ├── vite.config.ts # Vite 配置 (COOP/COEP headers, WASM exclude)
│ ├── tsconfig.json / tsconfig.app.json / tsconfig.node.json
│ ├── index.html
│ └── src/
│ ├── main.ts # Svelte 5 mount 入口
│ ├── App.svelte # 主编排器 (布局 + 状态 + 全局快捷键)
│ ├── app.css # CSS 变量系统 (--viztyp-*)
│ ├── lib/
│ │ ├── types.ts # Tab + Document/KB/Workspace 接口 (已实现全部)
│ │ ├── typst-init.ts # typst.ts WASM 初始化 (手动 compiler+renderer)
│ │ ├── Editor.svelte # CodeMirror 6 编辑器 (+ view 逃逸口 + search 扩展)
│ │ ├── Preview.svelte # SVG 实时预览 (debounced, 错误状态)
│ │ ├── cm-theme.ts # CodeMirror 思源风格主题 (安全 tags)
│ │ ├── ActivityBar.svelte # 最左侧垂直图标栏 (12 图标 + dock 切换)
│ │ ├── TabBar.svelte # 多标签栏 (center 区域内)
│ │ ├── DocTree.svelte # 已打开 Tab 文档树
│ │ ├── kb-tree.svelte # 左侧知识库树 (KB → Doc 两级展开) ✅
│ │ ├── DockPanel.svelte # 右侧 Dock (大纲/诊断/依赖图/历史/书签 5 tab)
│ │ ├── OutlinePanel.svelte # Typst #heading 大纲
│ │ ├── DiagnosticsPanel.svelte # 编译诊断 (typst.ts)
│ │ ├── DependencyGraph.svelte # SVG 依赖图 (知识图谱核心, 手写 SVG)
│ │ ├── HistoryPanel.svelte # 版本历史 (快照 + 恢复)
│ │ ├── StatusBar.svelte # 底部状态栏 (统计 + 在线协作者)
│ │ ├── CommandPalette.svelte # Ctrl+K 命令面板 (模糊搜索)
│ │ ├── SearchPanel.svelte # 全局搜索 (KB 权限感知 + Tabs 双模式)
│ │ ├── FindReplace.svelte # 查找替换 (CM6 内置面板)
│ │ ├── PluginManager.svelte # 插件管理 (Alt+P, 开关切换)
│ │ ├── TagEditor.svelte # 文档标签编辑 modal
│ │ ├── CollaboratorManager.svelte # KB 协作者管理 modal ✅
│ │ ├── SnippetSettings.svelte # CSS/JS 片段编辑器
│ │ ├── Login.svelte # 登录/注册全屏覆盖 ✅
│ │ ├── Settings.svelte # 设置面板 (Ctrl+,)
│ │ ├── command-registry.ts # 命令注册系统 (去重 + 模糊搜索)
│ │ ├── theme.svelte.ts # 主题状态 (light/dark/system)
│ │ ├── settings.svelte.ts # 设置状态 (localStorage 持久化)
│ │ ├── auth.svelte.ts # 认证状态 (JWT, login/register/logout) ✅
│ │ ├── kb-store.svelte.ts # 知识库状态 (Workspace/KB/Doc, 接后端) ✅
│ │ ├── collab.svelte.ts # 协作状态 (presence 轮询) ✅
│ │ ├── snippets.svelte.ts # CSS/JS 片段管理 (CSS 动态注入)
│ │ ├── plugin-loader.svelte.ts # 插件加载器 + 钩子体系 (onCompile/onSave/...) ✅
│ │ ├── outline.ts # parseOutline() — 解析 #heading
│ │ ├── refs.ts # parseReferences() + buildDependencyGraph()
│ │ ├── stats.ts # parseStats() — 文档统计 + CursorInfo
│ │ ├── history.ts # 版本快照 (接后端 API)
│ │ ├── share-manager.ts # 文档分享令牌 (接后端 API) ✅
│ │ ├── search-engine.ts # 权限感知全文搜索 (接后端 API) ✅
│ │ ├── workspace.ts # 导出/导入工作区 JSON (接后端 API) ✅
│ │ ├── find-replace.ts # CM6 搜索面板辅助
│ │ ├── file-ops.ts # openTypstFile/saveTypstFile/exportPdf/exportSvg
│ │ ├── typst-snippets.ts # 30+ Typst slash 命令片段
│ │ ├── api/ # ── Rust 后端 HTTP 客户端 ──
│ │ │ ├── client.ts # fetch 封装 (JWT Bearer + access_code)
│ │ │ ├── kb-api.ts # KB/文档/版本/标签/搜索/分享 API
│ │ │ ├── auth-api.ts # 注册/登录/me API ✅
│ │ │ └── types.ts # 后端对应 TS 类型
│ │ └── plugins/ # ── 内置插件 ──
│ │ ├── word-count-pro.ts # 字数统计 Pro (onCompile 钩子)
│ │ └── auto-outline.ts # 自动大纲 (onSave 钩子)
│ └── typst/
│ └── main.typ.ts # 默认欢迎文档 (TS 导出, 注入虚拟 FS /main.typ)
├── server/ # Rust HTTP 后端 (tokio + hyper, Web/Tauri 共用)
│ ├── Cargo.toml # tokio + hyper + serde + jsonwebtoken + sha2
│ ├── README.md # 后端文档 + API 表 + 打包流程
│ └── src/
│ ├── main.rs # tokio main + hyper server (端口 7480)
│ ├── handlers.rs # 手写路由 + 所有 API 端点
│ ├── store.rs # JSON 文件存储 (workspace/users/versions/...)
│ ├── auth.rs # JWT 签发/验证 + 权限校验 (4 级可见性 + 角色)
│ └── models.rs # Rust struct 镜像前端类型
├── typst-pkg/ # 自研 Typst 库 (有独立 typst.toml)
│ ├── typst.toml # 包清单: name="viztyp" version="0.1.0" compiler="0.14.0"
│ └── src/lib.typ # 库入口 (version + get-version)
├── src-tauri/ # Tauri v2 桌面外壳 (sidecar 自动启动后端)
│ ├── Cargo.toml # tauri + dialog/fs/shell/log 插件
│ ├── tauri.conf.json # CSP + externalBin (sidecar) + COOP/COEP notes
│ ├── capabilities/default.json # core/dialog/fs/shell 权限
│ ├── COOP_COEP_NOTES.md # 跨源隔离处理说明
│ └── src/{lib.rs, main.rs} # Tauri 入口 (sidecar 启动 + RunEvent::Exit 清理)
├── scripts/
│ └── build-sidecar.sh # 构建 server + 带 target-triple 后缀复制
├── package.json # 根级 (Tauri CLI + server/frontend 脚本)
├── README.md / README.zh.md # 双语文档对
└── AGENTS.md # 本文件
```
---
## 数据流 / Data Flow
### 编辑器数据流 (核心) / Editor data flow (core)
```
.typ 源码 → CodeMirror 编辑器 → (onInput 回调) → Tab 状态更新
→ typst.ts 编译器 (WASM) → Uint8Array artifact → 渲染器 (WASM) → SVG
```
源码被添加到**虚拟文件系统** (`/main.typ`),不是真实磁盘路径。
Source is added to a **virtual filesystem** (`/main.typ`), not the real disk.
### 知识库数据流 (目标) / KB data flow (target)
```
Workspace → KnowledgeBase → Document (.typ 文件)
┌──────────┼──────────┐
▼ ▼ ▼
CodeMirror typst.ts refs.ts
(编辑) (编译→SVG) (依赖树→TOC)
│ │ │
└──────────┼──────────┘
DocTree (TOC) + Outline + DependencyGraph
┌──────────┼──────────┐
▼ ▼ ▼
Preview(SVG) Search Version History
```
---
## 状态管理 / State Management
项目使用 **Svelte 5 runes** 模式,不使用 stores。
### 全局状态文件 / Global state files
| 文件 / File | 用途 / Purpose | 存储 / Storage |
|---|---|---|
| `theme.svelte.ts` | 主题模式 (light/dark/system) | localStorage `viztyp:theme` |
| `settings.svelte.ts` | 编辑器/预览设置 | localStorage `viztyp:settings` |
| `snippets.svelte.ts` | CSS/JS 片段管理 (CSS 动态注入) | localStorage `viztyp:snippets` |
| `kb-store.svelte.ts` | 知识库状态 (Workspace/KB/Doc, 接后端) | 后端 API (server/) |
| `auth.svelte.ts` | 认证状态 (JWT, login/register/logout) | localStorage `viztyp:token` |
| `collab.svelte.ts` | 协作状态 (presence 10s 轮询) | 后端 API |
| `plugin-loader.svelte.ts` | 插件启用状态 | localStorage `viztyp:plugins-enabled` |
### App.svelte 核心状态 / Core state
```ts
tabs: Tab[] // 所有打开的标签页
activeId: string // 当前活跃标签 ID
showLeftDock: boolean // 左 dock 可见性
showRightDock: boolean // 右 dock 可见性
dockTab: DockTab // 右 dock 活跃 tab (outline/diagnostics/deps/history/bookmarks)
cursor: CursorInfo // 光标位置 {line, col, selectionLength}
bookmarks: number[] // 书签行号列表
splitPercent: number // 编辑器/预览分栏比例 (15-85)
leftDockView: 'tabs'|'kb' // 左 dock 视图切换 (已打开 Tab | 知识库 KB 树)
diagnostics: string[] // typst.ts 编译诊断
// 模态可见性 / Modal visibility:
showCommandPalette, showSettings, showGlobalSearch, showTagEditor,
showPluginManager, showCollaborators, showSnippets: boolean
// 通过 kb-store/auth/collab 模块管理:
// workspace, activeKbId, versions, docTags, currentUser, onlineCount
```
### 编辑器回调注册模式 / Editor callback registration pattern
Editor.svelte 使用**注册回调**模式与父组件通信:
- `onInput(value)` — 文档变更
- `onCursorChange(info)` — 光标移动
- `registerScroll(fn)` — 父组件注册跳转函数
- `registerFold(fn)` — 父组件注册折叠函数
```svelte
<Editor
content={activeTab.content}
onInput={handleContentChange}
onCursorChange={(info) => cursor = info}
{registerScroll}
{registerFold}
/>
```
---
## 项目初始化 / Project Initialization
> 从零创建项目的脚手架步骤 / Scaffolding steps to create the project from scratch
### 1. 创建 Monorepo 结构 / Create Monorepo Structure
```bash
mkdir viztyp && cd viztyp
git init
# 根级 package.json — 仅 Tauri CLI / Root package.json — Tauri CLI only
yarn init -y
yarn add -D @tauri-apps/cli@^2
```
### 2. 创建前端 / Create Frontend (Svelte 5 + Vite)
```bash
mkdir -p packages/frontend
cd packages/frontend
# Vite + Svelte 5 + TypeScript / Scaffold with Vite
yarn create vite . --template svelte-ts
# 安装核心依赖 / Install core dependencies
yarn add @myriaddreamin/typst.ts@0.7.0 \
@myriaddreamin/typst.svelte@0.7.0 \
@myriaddreamin/typst-ts-renderer@0.7.0 \
@myriaddreamin/typst-ts-web-compiler@0.7.0
yarn add @codemirror/view @codemirror/state @codemirror/commands \
@codemirror/language @codemirror/autocomplete @codemirror/search \
@codemirror/language-data @codemirror/basic-setup @lezer/highlight
yarn add -D @tsconfig/svelte svelte-check
```
### 3. 初始化 Tauri / Initialize Tauri
```bash
cd ../.. # 回到根目录 / Back to root
yarn tauri init
# 配置 / Configure:
# - frontend dev path: packages/frontend
# - frontend build path: packages/frontend/dist
# - dev server URL: http://localhost:5173
```
### 4. 关键配置文件 / Key Configuration Files
#### `packages/frontend/vite.config.ts`
```typescript
import { defineConfig } from 'vite';
import { svelte } from '@sveltejs/vite-plugin-svelte';
export default defineConfig({
plugins: [svelte()],
server: {
port: 5173,
strictPort: true, // 与 Tauri 约定一致 / Match Tauri convention
headers: {
'Cross-Origin-Opener-Policy': 'same-origin',
'Cross-Origin-Embedder-Policy': 'require-corp',
},
},
optimizeDeps: {
exclude: ['@myriaddreamin/typst-ts-web-compiler', '@myriaddreamin/typst-ts-renderer'],
},
worker: {
format: 'es',
},
});
```
#### `src-tauri/tauri.conf.json` (关键片段 / Key excerpts)
```json
{
"build": {
"frontendDist": "../packages/frontend/dist",
"devUrl": "http://localhost:5173"
},
"app": {
"security": {
"csp": "script-src 'self' 'wasm-unsafe-eval'",
"headers": {
"Cross-Origin-Opener-Policy": "same-origin",
"Cross-Origin-Embedder-Policy": "require-corp"
}
}
}
}
```
#### `typst-pkg/typst.toml`
```toml
name = "viztyp"
version = "0.1.0"
compiler = "0.14.0"
entrypoint = "src/lib.typ"
```
---
## 功能清单 / Feature Checklist
### 📝 文档编辑
- [x] 项目脚手架 (Vite + Svelte 5 + Tauri)
- [x] typst.ts WASM 初始化 + 字体加载
- [x] CodeMirror 6 编辑器 + 思源主题
- [x] 实时 SVG 预览 (debounced)
- [x] 多 Tab 编辑
- [x] 文件 I/O (打开/保存/导出 PDF/SVG)
- [x] 查找替换 (Ctrl+H)
- [x] 可拖拽编辑器/预览分栏
### 📚 知识库管理
- [x] Workspace/KB/Document/TOCNode 类型定义
- [x] 知识库状态管理 (接 Rust 后端)
- [x] 知识库树 (KB 列表 → 文档树两级展开)
- [x] Typst `#include` 树解析 → TOC 构建 (借鉴语雀解耦模式)
- [x] 草稿/发布分离 (`.typ.draft` sidecar)
- [x] 版本历史 (快照 + 恢复, MrDoc 式完整快照)
- [x] 标签系统 (文档级标签)
- [ ] 文档模板 *(未实现, 优先级低)*
### 🔍 搜索与发现
- [x] 大纲解析 (#heading) + 跳转
- [x] 依赖图可视化 (DependencyGraph.svelte, 手写 SVG)
- [x] 编译诊断 (typst.ts)
- [x] 权限感知全文搜索 (预过滤可见 KB)
- [x] 全局搜索 (Ctrl+Shift+F, KB/Tabs 双模式)
- [x] 文档分享令牌 (DocShare)
### 👥 协作与权限
- [x] 用户认证 (JWT + SHA-256 密码哈希)
- [x] 权限系统 (4 级可见性 + 协作者角色)
- [x] KB 协作者管理 (reader/writer/admin)
- [x] 协作状态 (presence 轮询)
### 🔌 系统扩展
- [x] IDE 布局 (ActivityBar/LeftDock/RightDock/StatusBar)
- [x] 命令面板 (Ctrl+K)
- [x] 插件系统 (内置 TS 插件 + 钩子体系: onCompile/onSave/onPublish/onRender)
- [x] CSS/JS 片段管理
- [x] 工作区导出/导入 (JSON 含文档内容)
- [x] 主题与设置 (light/dark/system + 编辑器设置)
- [x] Tauri sidecar 自动启动/清理
> ✅ **实现状态**: 除"文档模板"外全部功能已实现并通过验证 (`svelte-check` 0/0 + `cargo build` 0 warnings + `vite build` 通过)。
---
## CSS 变量系统 / CSS Variable System
所有颜色/尺寸/字体通过 `--viztyp-*` CSS 变量管理,定义在 `app.css`
All colors/sizes/fonts managed via `--viztyp-*` CSS variables, defined in `app.css`.
### 思源色板 (目标值, 从思源源码提取) / SiYuan Palette (target values from SiYuan source)
```css
/* 暗色 / Dark */
--viztyp-bg-primary: #1e1e1e; /* 编辑器背景 / Editor background */
--viztyp-bg-secondary: #262626; /* 面板/工具栏 / Panels/toolbars */
--viztyp-bg-activity: #1e1e1e; /* ActivityBar */
--viztyp-border-primary: #363636; /* 边框, 0.5px 发丝线 / Borders, 0.5px hairline */
--viztyp-accent: #3573f0; /* 强调蓝 / Accent blue */
--viztyp-text-primary: #dadada; /* 主文字 / Primary text */
--viztyp-text-secondary: #9aa0a6; /* 次文字 / Secondary text */
/* 尺寸 / Sizing */
--viztyp-radius: 6px; /* 标准圆角 / Standard radius */
--viztyp-radius-large: 12px; /* 大圆角 / Large radius */
--viztyp-activitybar-width: 42px;
--viztyp-tab-height: 42px;
--viztyp-toolbar-height: 32px;
--viztyp-statusbar-height: 32px;
/* 动画 / Animation */
--viztyp-transition: 0.2s cubic-bezier(0, 0, 0.2, 1);
```
### 思源交互设计规范 / SiYuan Interaction Design Specs
```
Dock 活跃态: 实心蓝药丸 (#3573f0 背景 + 白色图标), 非左边竖条
Tab 活跃态: 3px 蓝色下划线
背景分层: background #1e1e1e vs surface #262626 (两色分层)
边框: #363636, 0.5px 发丝线 (不是 1px)
```
---
## Typst.ts 集成关键约定 / typst.ts Integration Rules
### ⚠️ 版本锁定 / Version Pinning
所有 `@myriaddreamin/*` 包**必须锁定 `0.7.0`**。混用版本会导致静默失败。
All `@myriaddreamin/*` packages **MUST be pinned to `0.7.0`**. Mixing causes silent failures.
```json
"@myriaddreamin/typst-ts-renderer": "0.7.0",
"@myriaddreamin/typst-ts-web-compiler": "0.7.0",
"@myriaddreamin/typst.svelte": "0.7.0",
"@myriaddreamin/typst.ts": "0.7.0"
```
### WASM 资源加载 (必须用 Vite `?url`) / WASM loading (MUST use Vite `?url`)
```ts
import compilerWasm from '@myriaddreamin/typst-ts-web-compiler/pkg/typst_ts_web_compiler_bg.wasm?url';
import rendererWasm from '@myriaddreamin/typst-ts-renderer/pkg/typst_ts_renderer_bg.wasm?url';
```
❌ 硬编码路径在 Tauri 生产构建中会失效 / Hardcoded paths break in Tauri production builds.
### 初始化守卫 / Init Guard
`setCompilerInitOptions` / `setRendererInitOptions` **必须只调用一次**,用模块级布尔标志防止 HMR 重复初始化。
Must be called exactly once. Guard with module-level boolean flag.
### ⚠️ 字体不内置 / Fonts Not Bundled
typst.ts **不包含字体**。必须通过 `loadFonts([], { assets: ['text', 'cjk'] })` 显式加载 CJK 字体。
typst.ts does **NOT** bundle fonts. Load CJK explicitly via `loadFonts([], { assets: ['text', 'cjk'] })`.
---
## Tauri 桌面关键约定 / Tauri Desktop Rules
### ⚠️ COOP/COEP Headers (线程化 WASM 必须)
```json
{
"app": {
"security": {
"headers": {
"Cross-Origin-Opener-Policy": "same-origin",
"Cross-Origin-Embedder-Policy": "require-corp"
}
}
}
}
```
Vite dev server 也需镜像这两个 header (已在 `vite.config.ts` 配置)。
Vite dev server must mirror these headers too (already configured in `vite.config.ts`).
### CSP 必须允许 WASM / CSP Must Allow WASM
```
script-src 'self' 'wasm-unsafe-eval'
```
---
## CodeMirror 6 约定 / CodeMirror 6 Conventions
### 自定义主题 / Custom Theme
编辑器使用 `cm-theme.ts` 中定义的 `siyuanFullTheme`,不使用 `@codemirror/theme-one-dark`
Editor uses `siyuanFullTheme` from `cm-theme.ts`, does NOT use `@codemirror/theme-one-dark`.
### ⚠️ HighlightStyle 标签陷阱 / HighlightStyle Tag Pitfall
`@lezer/highlight``tags` 对象中,**`t.functionName` 不存在**(不是直接属性)。会导致 `TypeError: Cannot read properties of undefined (reading 'id')`
The `tags` object from `@lezer/highlight` does **NOT** have `t.functionName` (not a direct property). Causes `TypeError: Cannot read properties of undefined (reading 'id')`.
安全的标签 / Safe tags: `t.comment`, `t.keyword`, `t.string`, `t.number`, `t.variableName`, `t.typeName`, `t.operator`, `t.meta`, `t.invalid`.
❌ 嵌套标签修饰器也不安全: `t.function(t.variableName)` 在当前版本可能未定义。
❌ Nested tag modifiers also unsafe: `t.function(t.variableName)` may be undefined in current version.
---
## 键盘快捷键 / Keyboard Shortcuts
| 快捷键 / Shortcut | 动作 / Action |
|---|---|
| `Ctrl+K` | 命令面板 / Command palette |
| `Ctrl+S` | 保存 / Save |
| `Ctrl+N` | 新建标签 / New tab |
| `Ctrl+O` | 打开文件 / Open file |
| `Ctrl+W` | 关闭标签 / Close tab |
| `Ctrl+J` | 切换主题 / Toggle theme |
| `Ctrl+H` | 查找替换 / Find & replace |
| `Ctrl+B` | 切换左 dock / Toggle left dock |
| `Ctrl+,` | 设置 / Settings |
| `Ctrl+Shift+F` | 全局搜索 / Global search |
| `Ctrl+Shift+D` | 切换右 dock 到诊断 / Dock → diagnostics |
| `Ctrl+Shift+B` | 切换书签 / Toggle bookmark |
| `Alt+1` | 切换右 dock / Toggle right dock |
| `Alt+G` | 依赖图 / Dependency graph |
| `Alt+H` | 版本历史 / Version history |
| `Alt+P` | 插件管理 / Plugin manager |
---
## 开发命令 / Development Commands
```bash
# 根级 / Root
yarn install # 安装 Tauri CLI
# 前端 / Frontend
cd packages/frontend && yarn install # 安装前端依赖
cd packages/frontend && yarn dev # Web 开发服务器 (Vite, port 5173)
cd packages/frontend && yarn build # Web 生产构建
cd packages/frontend && npx svelte-check --tsconfig ./tsconfig.json # 类型检查
# 桌面 / Desktop
yarn tauri dev # 桌面开发 (Tauri + Vite)
yarn tauri build # 桌面生产构建 (安装包)
```
---
## 文档约定 / Documentation Convention ⚠️ 强制 / Mandatory
- **双语并行**:所有文档维护中英文两个版本,文件名成对出现。
All documentation is maintained in bilingual EN/ZH pairs.
- `README.md` + `README.zh.md`
- `AGENTS.md` (单文件双语 / single file bilingual)
- **代码注释双语**:源码中的注释使用中英文双语,格式为:
Code comments are bilingual, formatted as:
```ts
// 初始化 typst.ts 编译器 / Initialize typst.ts compiler
```
- **同步更新规则**:修改代码时,**必须同步更新对应的文档**(EN + ZH)。
When modifying code, you **MUST update the corresponding docs** (both EN and ZH).
---
## 常见陷阱 / Common Pitfalls
1. **Svelte 5 runes `.svelte.ts` 文件**:状态管理文件必须用 `.svelte.ts` 后缀才能使用 `$state`/`$derived` 等 runes。
State management files MUST use `.svelte.ts` suffix to use `$state`/`$derived` runes.
2. **`ViewUpdate` 导入**:必须从 `@codemirror/view` 导入,umbrella 包 `codemirror` 不重新导出它。
Must import from `@codemirror/view`, NOT from umbrella `codemirror`.
3. **Uint8Array → Blob**:TypeScript 6 严格类型下需用 `pdfData.buffer as ArrayBuffer`
Under TS6 strict mode, use `pdfData.buffer as ArrayBuffer`.
4. **Vite 端口锁定**:Vite dev server 锁定 port 5173 (`strictPort: true`),与 Tauri 约定一致。
Vite dev server is locked to port 5173 (`strictPort: true`), matching Tauri convention.
5. **Vite dev server 后台运行**:用 `nohup ... &``setsid bash -c '...' </dev/null` 确保进程不被 shell 超时杀死。
Use `nohup` or `setsid` with `</dev/null` to keep dev server alive after shell timeout.
6. **TOC 与内容解耦** (借鉴语雀):不要在文档内容中硬编码目录结构,应维护独立的 TOC 树。
Following Yuque: don't hardcode TOC structure in document content; maintain a separate TOC tree.
7. **权限感知搜索** (借鉴 MrDoc):搜索前必须预过滤用户可见的知识库 ID 列表。
Following MrDoc: always pre-filter visible KB IDs before executing search queries.
---
## Agent 工作检查清单 / Agent Checklist
修改代码时必须 / When modifying code, you MUST:
- [ ] 同步更新 `README.md``README.zh.md` / Sync both READMEs
- [ ] 双语注释保持一致 / Keep bilingual comments consistent
- [ ] 如果改了 typst.ts 相关代码,检查 `@myriaddreamin/*` 版本是否一致 (0.7.0) / Check version sync
- [ ] 如果改了 WASM/渲染逻辑,验证 Web 和 Tauri 两个目标都正常 / Verify both targets
- [ ] 如果新增 Typst 库 API,更新 `typst.toml` 版本号 / Bump typst.toml version
- [ ] 如果改了 CodeMirror 主题/高亮,验证 `@lezer/highlight` tags 是否存在 / Validate tag existence
- [ ] 如果改了布局/CSS,验证 `--viztyp-*` 变量在 light + dark 都正常 / Verify both themes
- [ ] 如果新增知识管理模块,确保 TOC 与内容解耦 (语雀模式) / Keep TOC decoupled from content
- [ ] 如果新增权限/搜索功能,确保权限预过滤 / Ensure permission pre-filtering
- [ ] 运行 `svelte-check` 确保 0 errors / Run svelte-check for 0 errors