825 lines
39 KiB
Markdown
825 lines
39 KiB
Markdown
# 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
|