commit 08e4aeea0c47879aeaadb6299b79bee7b39badd3 Author: KiriAky 107 Date: Thu Aug 27 11:19:36 2026 +0800 初始化项目基础结构 添加了完整的前后端开发环境配置,包括: - 创建 .gitignore 文件忽略本地生成目录和环境文件 - 添加详细的 README.md 开发指南文档 - 配置 backend 目录结构和 FastAPI 应用基础框架 - 实现应用配置管理、健康检查和状态接口 - 设置 uv 依赖管理和虚拟环境配置 - 完成 CORS 中间件配置支持前端开发联调 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ee18178 --- /dev/null +++ b/.gitignore @@ -0,0 +1,18 @@ +# Frontend +frontend/node_modules/ +frontend/dist/ +frontend/*.tsbuildinfo +.pnpm-store/ + +# Backend +backend/.venv/ +backend/.uv-cache/ +backend/.pytest_cache/ +backend/**/__pycache__/ +backend/.env + +# Editors and operating systems +.idea/ +.vscode/ +.DS_Store +Thumbs.db diff --git a/README.md b/README.md new file mode 100644 index 0000000..7be6e32 --- /dev/null +++ b/README.md @@ -0,0 +1,112 @@ +# Notes Agent(暂命名) 团队开发说明 + +> 本文件用于团队开发期间快速配置环境和启动项目,不是正式的项目 README。 + +## 当前目录 + +```text +NotesAgent/ +├── frontend/ Vue 3 + TypeScript + Vite 前端 +├── backend/ FastAPI + Pydantic 后端 +├── docs/ 分工与技术栈说明 +└── server sync/ 云同步服务预留目录,当前未实现 +``` + +## 开发环境 + +当前前后端壳子需要: + +| 环境 | 要求 | 说明 | +| --- | --- | --- | +| Git | 较新稳定版 | 代码版本管理 | +| Node.js | 22 或更高版本 | 推荐使用 Node.js 24 | +| pnpm | 10 或更高版本 | 前端依赖与脚本管理 | +| Python | 3.11 或更高版本 | 推荐使用 Python 3.12 | +| uv | 较新稳定版 | 后端依赖和虚拟环境管理 | + +检查本机环境: + +```powershell +git --version +node --version +pnpm --version +python --version +uv --version +``` + +当前壳子暂不需要 Rust 和 Tauri。开始桌面端集成后,再按照 `docs/AI笔记软件技术栈说明-团队版-v2.2.md` 安装 Rust Toolchain 与 Tauri CLI。 + +## 首次初始化 + +### 后端 + +```powershell +cd backend +uv sync +cd .. +``` + +`uv sync` 会根据 `backend/pyproject.toml` 安装依赖,并自动创建和管理 `backend/.venv`,不需要手动创建或激活虚拟环境。 + +### 前端 + +```powershell +cd frontend +pnpm install +cd .. +``` + +## 启动开发环境 + +前端和后端需要在两个终端中分别启动。 + +### 终端一:启动后端 + +```powershell +cd backend +uv run uvicorn app.main:app --reload --host 127.0.0.1 --port 8000 +``` + +后端地址: + +- 健康检查: +- 服务状态: +- API 文档: +- OpenAPI JSON: + +### 终端二:启动前端 + +```powershell +cd frontend +pnpm dev +``` + +前端地址: + +开发环境中,Vite 会将 `/api` 和 `/health` 请求代理到 `http://127.0.0.1:8000`。联调时应先启动后端,再启动或刷新前端。 + +## 测试与构建 + +后端测试: + +```powershell +cd backend +uv run pytest +``` + +前端类型检查及生产构建: + +```powershell +cd frontend +pnpm build +``` + +构建产物位于 `frontend/dist`,该目录不提交到 Git。 + +## 日常开发注意事项 + +- Python 依赖统一修改 `backend/pyproject.toml`,修改后执行 `uv sync`。 +- 前端依赖统一使用 pnpm 安装,不要混用 npm 或 yarn。 +- `backend/.venv`、`frontend/node_modules`、`frontend/dist` 均为本地生成目录,不提交到 Git。 +- API 默认监听 `127.0.0.1:8000`,前端默认监听 `127.0.0.1:5173`。 +- 跨模块接口发生变化时,需要同步更新前后端类型和 `docs` 中的接口说明。 diff --git a/backend/.env.example b/backend/.env.example new file mode 100644 index 0000000..c6d6f5e --- /dev/null +++ b/backend/.env.example @@ -0,0 +1,5 @@ +APP_NAME=Notes Agent AI Core +APP_VERSION=0.1.0 +APP_ENVIRONMENT=development +APP_HOST=127.0.0.1 +APP_PORT=8000 diff --git a/backend/README.md b/backend/README.md new file mode 100644 index 0000000..4ec4808 --- /dev/null +++ b/backend/README.md @@ -0,0 +1,15 @@ +# Backend + +FastAPI + Pydantic 的最小后端壳子。项目使用 uv 管理依赖和虚拟环境。 + +```powershell +uv sync +uv run uvicorn app.main:app --reload --host 127.0.0.1 --port 8000 +``` + +`uv sync` 首次运行时会自动创建由 uv 管理的 `.venv`,无需手动执行 `python -m venv` 或激活环境。 + +启动后可访问: + +- 健康检查: +- API 文档: diff --git a/backend/app/__init__.py b/backend/app/__init__.py new file mode 100644 index 0000000..4a3b084 --- /dev/null +++ b/backend/app/__init__.py @@ -0,0 +1 @@ +"""Notes Agent AI Core.""" diff --git a/backend/app/config.py b/backend/app/config.py new file mode 100644 index 0000000..dcc4054 --- /dev/null +++ b/backend/app/config.py @@ -0,0 +1,25 @@ +import os +from dataclasses import dataclass +from functools import lru_cache + + +@dataclass(frozen=True) +class Settings: + """应用基础配置;正式环境可通过 APP_* 环境变量覆盖。""" + + name: str + version: str + environment: str + host: str + port: int + + +@lru_cache +def get_settings() -> Settings: + return Settings( + name=os.getenv("APP_NAME", "Notes Agent AI Core"), + version=os.getenv("APP_VERSION", "0.1.0"), + environment=os.getenv("APP_ENVIRONMENT", "development"), + host=os.getenv("APP_HOST", "127.0.0.1"), + port=int(os.getenv("APP_PORT", "8000")), + ) diff --git a/backend/app/main.py b/backend/app/main.py new file mode 100644 index 0000000..4b0aeae --- /dev/null +++ b/backend/app/main.py @@ -0,0 +1,35 @@ +from fastapi import FastAPI +from fastapi.middleware.cors import CORSMiddleware + +from app.config import get_settings +from app.schemas import HealthResponse, ServiceStatusResponse + +settings = get_settings() + +app = FastAPI( + title=settings.name, + version=settings.version, + description="AI 笔记软件的本地 FastAPI 服务壳子。", +) + +app.add_middleware( + CORSMiddleware, + allow_origins=["http://127.0.0.1:5173", "http://localhost:5173"], + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], +) + + +@app.get("/health", response_model=HealthResponse, tags=["System"]) +async def health() -> HealthResponse: + return HealthResponse() + + +@app.get("/api/status", response_model=ServiceStatusResponse, tags=["System"]) +async def service_status() -> ServiceStatusResponse: + return ServiceStatusResponse( + name=settings.name, + version=settings.version, + environment=settings.environment, + ) diff --git a/backend/app/schemas.py b/backend/app/schemas.py new file mode 100644 index 0000000..2ae8dd3 --- /dev/null +++ b/backend/app/schemas.py @@ -0,0 +1,13 @@ +from typing import Literal + +from pydantic import BaseModel + + +class HealthResponse(BaseModel): + status: Literal["ok"] = "ok" + + +class ServiceStatusResponse(HealthResponse): + name: str + version: str + environment: str diff --git a/backend/notes_agent_backend.egg-info/PKG-INFO b/backend/notes_agent_backend.egg-info/PKG-INFO new file mode 100644 index 0000000..a54ff14 --- /dev/null +++ b/backend/notes_agent_backend.egg-info/PKG-INFO @@ -0,0 +1,22 @@ +Metadata-Version: 2.4 +Name: notes-agent-backend +Version: 0.1.0 +Summary: Notes Agent 的 FastAPI 基础壳子 +Requires-Python: >=3.11 +Description-Content-Type: text/markdown +Requires-Dist: fastapi<1.0,>=0.116 +Requires-Dist: uvicorn[standard]<1.0,>=0.35 + +# Backend + +FastAPI + Pydantic 的最小后端壳子。 + +```powershell +uv sync +uv run uvicorn app.main:app --reload --host 127.0.0.1 --port 8000 +``` + +启动后可访问: + +- 健康检查: +- API 文档: diff --git a/backend/notes_agent_backend.egg-info/SOURCES.txt b/backend/notes_agent_backend.egg-info/SOURCES.txt new file mode 100644 index 0000000..853a10f --- /dev/null +++ b/backend/notes_agent_backend.egg-info/SOURCES.txt @@ -0,0 +1,12 @@ +README.md +pyproject.toml +app/__init__.py +app/config.py +app/main.py +app/schemas.py +notes_agent_backend.egg-info/PKG-INFO +notes_agent_backend.egg-info/SOURCES.txt +notes_agent_backend.egg-info/dependency_links.txt +notes_agent_backend.egg-info/requires.txt +notes_agent_backend.egg-info/top_level.txt +tests/test_api.py \ No newline at end of file diff --git a/backend/notes_agent_backend.egg-info/dependency_links.txt b/backend/notes_agent_backend.egg-info/dependency_links.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/backend/notes_agent_backend.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/backend/notes_agent_backend.egg-info/requires.txt b/backend/notes_agent_backend.egg-info/requires.txt new file mode 100644 index 0000000..11dbd01 --- /dev/null +++ b/backend/notes_agent_backend.egg-info/requires.txt @@ -0,0 +1,2 @@ +fastapi<1.0,>=0.116 +uvicorn[standard]<1.0,>=0.35 diff --git a/backend/notes_agent_backend.egg-info/top_level.txt b/backend/notes_agent_backend.egg-info/top_level.txt new file mode 100644 index 0000000..b80f0bd --- /dev/null +++ b/backend/notes_agent_backend.egg-info/top_level.txt @@ -0,0 +1 @@ +app diff --git a/backend/pyproject.toml b/backend/pyproject.toml new file mode 100644 index 0000000..3551f6d --- /dev/null +++ b/backend/pyproject.toml @@ -0,0 +1,19 @@ +[project] +name = "notes-agent-backend" +version = "0.1.0" +description = "Notes Agent 的 FastAPI 基础壳子" +readme = "README.md" +requires-python = ">=3.11" +dependencies = [ + "fastapi>=0.116,<1.0", + "uvicorn[standard]>=0.35,<1.0", +] + +[dependency-groups] +dev = [ + "pytest>=8.4,<9.0", +] + +[tool.pytest.ini_options] +pythonpath = ["."] +testpaths = ["tests"] diff --git a/backend/tests/test_api.py b/backend/tests/test_api.py new file mode 100644 index 0000000..5638924 --- /dev/null +++ b/backend/tests/test_api.py @@ -0,0 +1,16 @@ +import asyncio + +from app.main import health, service_status + + +def test_health() -> None: + response = asyncio.run(health()) + + assert response.model_dump() == {"status": "ok"} + + +def test_service_status() -> None: + response = asyncio.run(service_status()) + + assert response.name == "Notes Agent AI Core" + assert response.status == "ok" diff --git a/docs/AI笔记软件技术栈说明-团队版-v2.2.md b/docs/AI笔记软件技术栈说明-团队版-v2.2.md new file mode 100644 index 0000000..5971855 --- /dev/null +++ b/docs/AI笔记软件技术栈说明-团队版-v2.2.md @@ -0,0 +1,2197 @@ +# AI 笔记软件技术栈说明 + +> 文档性质:团队技术基线 +> 适用范围:桌面客户端、本地知识库、RAG、Agent、Skill、多模型接入、多模态处理与可选云同步 +> 目标读者:前端、Rust 桌面端、Python AI Core、算法、测试与后续接手项目的开发成员 + +--- + +## 1. 文档目的 + +本文档用于统一项目的技术栈、运行结构、模块边界和开发约定。团队成员可以据此确定一个功能应落在哪个模块、需要调用哪些接口、数据由谁持有,以及功能完成后应进行哪些测试。 + +项目以桌面笔记工作区为主要运行形态。用户的 Markdown 笔记和附件保存在本地 Vault 中,桌面客户端负责编辑、文件管理和交互;Python AI Core 提供知识索引、RAG、Agent、Skill 和多模态处理能力;模型访问通过统一 Provider Adapter 接入;云同步作为独立能力按需部署。 + +开发过程中遵守以下基础约定: + +1. Markdown 文件保存用户正文,是笔记内容的持久化载体。 +2. SQLite 保存可重建的结构化索引、元数据、运行记录和应用状态。 +3. Vue 前端不直接持有模型厂商协议,也不直接访问本地数据库。 +4. Python AI Core 不直接处理桌面窗口、系统密钥和操作系统 UI。 +5. Rust Host 统一处理桌面系统能力、Sidecar 生命周期和敏感资源访问。 +6. RAG、Agent、Skill、Plugin、Provider 通过项目内部接口连接,业务代码不直接依赖某一家模型厂商或插件协议的数据结构。 +7. Skill 使用已注册能力组织 AI 工作流;Plugin 可以向系统注册新的程序能力,两者分别维护 Manifest 和权限。 +8. 客户端包含完整的本地业务与 AI 能力,用户无需部署服务器即可使用笔记、检索、RAG、Agent、Skill 和 Plugin。 +9. Sync Server 作为独立基础设施提供多设备同步,可由用户自行部署,也可在后续提供托管实例。 +10. 本地可重建索引不作为默认云同步对象,各设备根据自己的 Embedding 和检索配置重新构建索引。 + +--- + +## 2. 技术栈总览 + +| 层 | 技术选型 | 在项目中的用途 | +| --- | --- | --- | +| 桌面容器 | Tauri 2 + Rust | 桌面窗口、系统 API、本地文件访问、Sidecar 管理、安全边界 | +| 前端 | Vue 3 + TypeScript + Vite | 工作区、编辑器、AI 对话、搜索、设置、扩展管理等用户界面 | +| 状态管理 | Pinia | 管理工作区、编辑器、搜索、会话、Agent、Skill、主题与模型状态 | +| UI 基础 | Reka UI / Headless Components + Design Token | 通用交互组件和主题化能力 | +| Markdown 编辑器 | Milkdown + CodeMirror 6 | 可视化 Markdown 编辑与源码编辑 | +| 本地核心服务 | Python + FastAPI + Pydantic v2 | RAG、Agent、Skill、模型访问、多模态、索引和本地 API | +| Python 打包 | PyInstaller / Nuitka | 将 Python AI Core 打包为 Tauri Sidecar | +| 笔记存储 | Markdown + Assets | 保存用户正文和附件 | +| 元数据 | SQLite | 笔记元数据、Block、标签、会话、Trace、任务、索引状态 | +| 全文检索 | SQLite FTS5 | 关键词、标题、术语、标签等文本检索 | +| 向量检索 | sqlite-vec + VectorStore | 本地语义检索 | +| Embedding | 可插拔 EmbeddingProvider,默认本地 BGE-M3 类模型 | 为 Note Block 生成向量 | +| Reranker | BGE reranker 类 Cross-Encoder | 对候选检索结果进行精排 | +| Agent | 自研 Agent Runtime | 模型推理、工具选择、工具调用、结果回灌、运行控制 | +| Skill | 自研声明式 Skill Runtime | 复用提示词、工具集合、权限和检索配置 | +| Plugin | 自研 Plugin Runtime + Plugin Manifest + MCP Bridge | 扩展程序能力、Tool、外部服务集成和受控 UI Contribution | +| LLM | 自研 Provider Adapter | 统一不同模型服务商的输入、输出、Streaming 与 Tool Calling | +| 模型协议 | OpenAI Responses / Chat Completions compatible / Anthropic Messages / Ollama | 用户自定义模型接入 | +| ASR | faster-whisper | 音频转写 | +| 说话人分离 | pyannote.audio | 课堂、会议等多人音频中的说话人区分 | +| 情感识别 | emotion2vec | 可选音频分析能力 | +| 密钥存储 | Tauri Stronghold | 保存模型 API Key 和同步凭证 | +| 云同步 | 独立 Sync Server:FastAPI + PostgreSQL + S3/MinIO | 可选自托管,多设备 Vault 同步、版本管理和设备管理 | +| 测试 | pytest + 自建 RAG / Agent Dataset | 单元、接口、检索、Agent 和模型适配器测试 | + +表中的技术选型构成当前开发基线。新增依赖时需要明确其所属层、调用方、运行位置和替换成本,避免同一功能出现多套并行实现。 + +--- + +## 3. 系统运行架构 + +```mermaid +flowchart TB + +subgraph Desktop["Tauri Desktop App"] + direction TB + + subgraph Frontend["Vue 3 / TypeScript"] + direction LR + Workspace["Workspace"] + Editor["Markdown Editor"] + Chat["AI Chat"] + SearchUI["Search"] + TraceUI["Agent Trace"] + Settings["Settings"] + SkillUI["Skill UI"] + ThemeUI["Theme Manager"] + TaskUI["Task UI"] + end + + IPC["Tauri IPC"] + + subgraph Rust["Rust Host"] + direction LR + File["File Access"] + Secret["Secret Vault"] + Sidecar["Sidecar Manager"] + OS["OS API"] + Permission["Permission"] + Protocol["Window / Protocol"] + end + + Frontend --> IPC + IPC --> Rust +end + +Rust -->|"localhost / IPC"| API + +subgraph AI["Python AI Core"] + direction TB + + API["FastAPI / Local API"] + + subgraph Core["Application Core"] + direction LR + Note["Note Core"] + RAG["RAG Engine"] + Agent["Agent Runtime"] + end + + subgraph RetrievalLayer["Retrieval Pipeline"] + direction LR + Retrieval["Hybrid Retrieval"] + Reranker["Reranker"] + end + + subgraph AgentLayer["Agent & Extension Layer"] + direction LR + Tools["Tool Registry"] + Skills["Skill Runtime"] + Plugins["Plugin Runtime"] + end + + Provider["Provider Abstraction
OpenAI · Anthropic · Ollama · …"] + Multimodal["Multimodal Processing
Whisper · Pyannote · OCR · Emotion2Vec"] + + API --> Note + API --> RAG + API --> Agent + API --> Skills + API --> Plugins + + RAG --> Retrieval + Retrieval --> Reranker + + Skills -->|"build configuration"| Agent + Agent --> RAG + Agent --> Tools + Plugins -->|"register tools"| Tools + Agent --> Provider + + Multimodal --> Note +end + +subgraph Storage["Local Knowledge & Storage"] + direction LR + + Vault[("Markdown Vault")] + + subgraph DB["SQLite"] + direction TB + Metadata["Metadata"] + FTS["FTS5"] + Vector["Vector Index"] + Trace["Agent Trace"] + end +end + +Note <--> Vault +Note <--> Metadata +Retrieval --> FTS +Retrieval --> Vector +Agent --> Trace +``` + +系统运行时包含三个主要进程边界。 + +第一部分是 Tauri Desktop App。Vue 前端运行在 Tauri WebView 中,Rust Host 与前端通过 Tauri IPC 通信。文件系统、系统密钥、窗口管理、系统通知等能力由 Rust 暴露受控命令。 + +第二部分是 Python AI Core。桌面程序启动后由 Rust Host 拉起 AI Core Sidecar,Sidecar 在 `127.0.0.1` 的随机端口监听。Rust Host 为本次进程生成临时访问令牌,并将地址和令牌提供给前端 API Client。模型流式生成、Agent Trace 等长连接数据通过 SSE 或 WebSocket 传输。 + +第三部分是本地 Vault 和 SQLite。Vault 中的 Markdown 与 Assets 属于用户数据;SQLite 保存索引和应用状态。Note Core 负责维护两者之间的一致性。文件发生变化时,由文件监听机制通知 Note Core 执行增量解析和索引更新。 + +第四部分是可选 Sync Server。Sync Server 运行在用户自行部署或项目后续提供的服务器环境中,通过 HTTPS / WebSocket 与多个客户端通信。它负责身份、设备、Vault、文件版本、同步游标和对象存储,不承载桌面端的 RAG、Agent、Embedding、Reranker 或本地模型运行。未配置 Sync Server 时,客户端保持完整的本地工作能力。 + +--- + +## 4. 代码边界与依赖方向 + +项目中的功能按照以下五个核心域组织: + +```text +Knowledge Core +Retrieval Core +Agent Core +Extension Core +Model Core +``` + +### 4.1 Knowledge Core + +Knowledge Core 管理笔记本身,主要实体包括 `Note`、`NoteBlock`、`Tag`、`Link`、`Attachment` 和 `Task`。Markdown 解析、文件路径映射、Block 生成、标题路径、附件引用、标签和双向链接都属于该模块。 + +Knowledge Core 可以调用文件访问层和 SQLite Repository。它不负责调用大模型,不包含 Provider SDK,也不实现 Agent Loop。 + +### 4.2 Retrieval Core + +Retrieval Core 负责从 Knowledge Core 已建立的索引中检索内容,包含 FTS5、Embedding、VectorStore、RRF、Reranker、Metadata Filter 和 Citation 组装。 + +Retrieval Core 接收结构化查询参数并返回 `SearchResult` 或 `RetrievedBlock`。调用方可以是搜索页面、RAG Engine 或 Agent Tool。该模块不直接向前端输出聊天文本。 + +### 4.3 Agent Core + +Agent Core 管理一次 Agent Run 的状态和执行过程。它通过 Model Core 请求模型,通过 Tool Registry 调用系统能力,并将 Tool Result 回灌到下一轮模型请求。 + +Agent Core 负责步骤数、超时、Token Budget、取消、Trace 和权限检查。具体笔记查询通过 Tool Registry 调用 Knowledge Core 或 Retrieval Core。 + +### 4.4 Extension Core + +Extension Core 管理 Skill、Plugin 和 Theme 三类扩展。 + +Skill Runtime 读取 Skill Manifest,将提示词、工具集合、权限声明、模型能力要求和检索配置转换为 Agent Configuration。Skill 复用项目已经存在的能力,本身不提供新的可执行程序能力。 + +Plugin Runtime 负责安装、启用、停用和卸载功能插件。Plugin 可以向 Tool Registry 注册新工具,也可以声明 Command、Importer、Exporter、Sidebar Panel 等 Contribution。需要运行后端逻辑的第三方 Plugin 通过独立 Plugin Host 或 MCP Bridge 与主程序通信,插件进程不直接加载进 Python AI Core。 + +Theme Runtime 由桌面前端处理,读取 Theme Manifest 和 CSS Design Token。Theme 只参与界面外观和允许开放的样式覆盖。 + +三类扩展之间允许组合使用。Plugin 提供的新 Tool 注册到 Tool Registry 后,Skill 可以在 Manifest 中声明使用这些 Tool,Agent Runtime 按正常权限流程完成调用。 + +### 4.5 Model Core + +Model Core 负责模型服务访问。内部接口覆盖文本生成、流式事件、Tool Calling、模型能力、Embedding 和错误转换。 + +OpenAI、Anthropic、Ollama 等实现都位于 Provider Adapter 内部。Agent、Chat 和 Skill 只使用项目自己的 `ModelRequest`、`ModelEvent`、`ToolDefinition` 等类型。 + +### 4.6 依赖约定 + +依赖方向固定为: + +```text +UI +↓ +Application API +↓ +Domain Service +↓ +Repository / Adapter +↓ +External Runtime +``` + +典型调用示例: + +```text +AI Chat +→ /api/chat +→ RAG Engine +→ Retrieval Core +→ VectorStore / FTS5 +→ Provider Adapter +``` + +```text +Agent +→ Tool Registry +→ notes.search +→ Retrieval Core +→ SQLite / VectorStore +``` + +```text +Skill +→ Skill Runtime +→ Agent Configuration +→ Agent Runtime +``` + +```text +Plugin +→ Plugin Runtime +→ Plugin Host / MCP Bridge +→ Register Tool / Command / Contribution +→ Tool Registry / Frontend Extension Slot +``` + +```text +Skill +→ Tool Binding +→ Plugin-provided Tool +→ Plugin Host +``` + +Provider Adapter、VectorStore、EmbeddingProvider、Plugin Host 等基础适配器由接口暴露能力,上层模块不引用具体实现类。 + +--- + +## 5. 桌面端技术说明 + +### 5.1 Tauri 2 与 Rust Host + +Tauri 2 提供应用的桌面运行环境。Rust Host 主要处理需要操作系统权限或进程控制的能力,包括: + +- Vault 目录选择和文件系统访问; +- 文件创建、移动、重命名、删除和监听; +- Stronghold 密钥读写; +- Python AI Core Sidecar 生命周期; +- 应用窗口和自定义协议; +- 系统通知、剪贴板和系统路径; +- 敏感能力授权。 + +前端需要读取文件时优先通过项目封装后的 Rust Command 或 Python API 获取,不在 Vue 组件中直接散布 Tauri 文件系统调用。文件编辑功能可以由一个统一的 `WorkspaceService` 封装,便于后续处理路径规范、文件锁、自动保存和错误提示。 + +Sidecar Manager 在应用启动时完成以下工作: + +```text +确定 ai-core 可执行文件 +→ 生成随机端口 +→ 生成本地 Session Token +→ 启动 Sidecar +→ 轮询 /health +→ 建立 API Client +``` + +应用关闭时发送正常关闭信号,超时后再终止子进程。AI Core 异常退出时,Rust Host记录退出原因并允许用户重新启动 AI 服务,Markdown 编辑功能继续可用。 + +### 5.2 Vue 3 + TypeScript + Vite + +Vue 3 负责全部用户交互界面。项目采用 Composition API 和 TypeScript,页面组件主要负责交互组织,业务请求通过 Service 层完成。 + +建议前端目录按照功能域组织: + +```text +src/ +├── features/ +│ ├── workspace/ +│ ├── editor/ +│ ├── search/ +│ ├── chat/ +│ ├── agent/ +│ ├── skills/ +│ ├── themes/ +│ ├── tasks/ +│ └── settings/ +├── components/ +├── services/ +├── stores/ +├── contracts/ +└── router/ +``` + +`features` 保存面向功能的页面和组件;`services` 封装 Tauri IPC 与 Python API;`stores` 管理跨组件状态;`contracts` 保存前端使用的统一 DTO 类型。 + +### 5.3 Pinia + +Pinia 只保存需要跨组件共享或跨页面持续存在的状态。编辑器内部临时状态尽量保留在组件或编辑器实例中,减少全局 Store 的更新频率。 + +建议保持以下 Store: + +| Store | 主要状态 | +| --- | --- | +| `workspaceStore` | 当前 Vault、目录树、打开文件、文件监听状态 | +| `noteStore` | 笔记元数据、当前 Note、最近访问记录 | +| `editorStore` | 编辑模式、活动编辑器、保存状态 | +| `searchStore` | 查询条件、搜索结果、检索模式 | +| `chatStore` | 会话、消息、Streaming 状态 | +| `agentStore` | Agent Run、步骤、Tool Call、Trace | +| `skillStore` | 已安装 Skill、启用状态、运行参数 | +| `themeStore` | 当前主题、主题元数据 | +| `taskStore` | 笔记任务和 Agent 创建的任务 | +| `providerStore` | Provider 配置、模型列表、模型能力 | +| `settingsStore` | 应用级设置 | + +Store 不直接访问数据库,也不直接拼接 OpenAI 或 Anthropic 请求体。 + +### 5.4 Reka UI 与 Design Token + +Reka UI / Headless Components 提供 Dialog、Popover、Menu、Tabs、Select、Tooltip、Command Palette 等基础交互。视觉层使用项目自己的 Design Token。 + +所有需要被主题修改的视觉属性都通过 CSS Variables 暴露,例如: + +```css +:root { + --background-primary: #ffffff; + --background-secondary: #f6f6f6; + --text-primary: #202020; + --text-secondary: #666666; + --accent-primary: #5b67f1; + + --editor-font-family: system-ui; + --editor-font-size: 16px; + + --radius-small: 4px; + --radius-medium: 8px; + --radius-large: 12px; +} +``` + +业务组件中优先引用 Design Token。主题包负责覆盖 Token 和允许开放的组件样式。主题加载器需要限制资源路径,避免主题 CSS 引用 Vault 外的任意本地文件。 + +--- + +## 6. Markdown 编辑与知识结构 + +### 6.1 Milkdown 与 CodeMirror 6 + +Milkdown 负责日常所见即所得编辑,CodeMirror 6 提供 Markdown 源码模式和代码块增强能力。 + +两个编辑器共享同一份 Markdown 文本。模式切换时通过编辑器适配层读取当前内容、同步保存并初始化目标编辑器。自动保存经过统一 `EditorDocumentService`,避免两个编辑器分别实现文件写入。 + +### 6.2 Markdown 文件 + +一个 Vault 可以采用以下结构: + +```text +Vault/ +├── Notes/ +│ ├── 数据结构/ +│ │ └── 红黑树.md +│ └── 操作系统/ +│ └── 死锁.md +├── Assets/ +│ ├── Images/ +│ ├── Audio/ +│ └── Documents/ +└── .ainote/ + ├── app.db + ├── cache/ + ├── models/ + ├── skills/ + ├── themes/ + └── logs/ +``` + +`Notes` 和 `Assets` 属于用户可见数据。`.ainote` 保存应用生成的数据,可以通过重新索引恢复的内容不写入 Markdown 正文。 + +### 6.3 Note Block + +RAG 和引用定位使用 Note Block 作为基础单位。Block 由 Markdown AST 生成,切分时保留标题层级。 + +一个 Block 至少包含: + +```text +block_id +note_id +heading_path +start_offset +end_offset +content +content_hash +token_count +``` + +例如: + +```text +heading_path = 数据结构 / 红黑树 / 删除操作 / Case 2 +``` + +`block_id` 在同一内容未发生结构性变化时保持稳定。编辑后通过 `content_hash` 判断哪些 Block 需要重新建立全文和向量索引。 + +### 6.4 文件监听与增量索引 + +Rust Host 监听 Vault 中的创建、修改、移动、重命名和删除事件。变更事件经过 debounce 后提交给 Note Core。 + +笔记修改的数据流为: + +```text +Save Markdown +→ File Change Event +→ Parse Markdown AST +→ Diff Note Blocks +→ Update Metadata +→ Update FTS5 +→ Re-embed Changed Blocks +→ Update VectorStore +``` + +外部编辑器修改文件时也走相同流程。索引任务写入 `index_jobs`,前端可以展示待处理、处理中和失败状态。 + +--- + +## 7. SQLite 与数据归属 + +SQLite 位于 `.ainote/app.db`,承担本地结构化数据和索引管理。 + +推荐至少维护以下表: + +```text +notes +blocks +tags +note_tags +links +attachments +tasks +conversations +messages +agent_runs +tool_calls +skills +providers +index_jobs +sync_state +``` + +其中 `notes` 和 `blocks` 保存 Markdown 的结构化投影;FTS5 建立全文索引;sqlite-vec 保存 Block 向量;`agent_runs` 和 `tool_calls` 保存 Agent Trace;Provider 表保存非敏感模型配置。 + +API Key、同步 Token 等机密数据不进入 SQLite,通过 `credential_id` 与 Stronghold 中的实际密钥关联。 + +数据库 Schema 使用迁移机制维护。Python 侧可以使用 SQLAlchemy + Alembic 管理表结构变更。每次修改 Schema 时提交对应 migration,禁止依赖运行时自动删表重建。 + +--- + +## 8. 本地 AI Core + +### 8.1 FastAPI 服务 + +Python AI Core 负责所有 AI 和检索功能,以独立 Sidecar 运行。FastAPI 提供本地接口,Pydantic v2 定义请求、响应和内部 DTO。 + +建议 API 按业务域组织: + +```text +/api/notes +/api/search +/api/chat +/api/agent +/api/skills +/api/providers +/api/tasks +/api/media +/api/index +``` + +普通 CRUD 使用 HTTP JSON。LLM Streaming 和 Agent 实时执行事件使用 SSE;需要双向控制的长任务可以采用 WebSocket。 + +每个请求包含本次桌面会话的本地 Token。AI Core 只监听 `127.0.0.1`。 + +### 8.2 Pydantic Contracts + +API 和内部核心数据结构使用明确 Schema。重要 Contract 包括: + +```text +Note +NoteBlock +SearchRequest +SearchResult +Citation + +ModelRequest +ModelEvent +ModelCapability + +ToolDefinition +ToolCall +ToolResult + +SkillManifest +PluginManifest +PluginContribution +PluginStatus +AgentState +AgentRun +AgentEvent + +ProviderConfig +``` + +接口字段变更需要同步更新前端 TypeScript Contract。Monorepo 中可以维护 OpenAPI 生成流程,自动生成部分 TypeScript API 类型,减少手工重复定义。 + +--- + +## 9. 检索与 RAG + +### 9.1 检索目标 + +搜索功能需要同时覆盖精确术语和语义表达。课程名、函数名、代码标识符、专有名词适合 FTS5;自然语言问题和同义表达适合向量检索。 + +RAG Engine 使用以下处理链路: + +```mermaid +flowchart LR + Q["User Query"] --> QA["Query Analyzer"] + QA --> F["FTS5 / BM25"] + QA --> V["Vector Search"] + F --> RRF["RRF Fusion"] + V --> RRF + RRF --> RR["Reranker"] + RR --> MF["Metadata Filter"] + MF --> CB["Context Builder"] + CB --> LLM["Provider Adapter"] + LLM --> AC["Answer + Citation"] +``` + +### 9.2 FTS5 + +FTS5 索引标题、Heading Path、Block 正文、标签和可检索附件文本。返回结果统一映射为 `RetrievedBlock`。 + +全文检索接口需要支持: + +- query; +- folder / note / tag 范围; +- 时间范围; +- limit; +- offset; +- 返回命中片段。 + +### 9.3 VectorStore + +向量实现通过 `VectorStore` 抽象: + +```python +class VectorStore(Protocol): + async def upsert(self, records: list[VectorRecord]) -> None: ... + async def delete(self, ids: list[str]) -> None: ... + async def search( + self, + vector: list[float], + *, + top_k: int, + filters: VectorFilter | None = None, + ) -> list[VectorHit]: ... +``` + +默认实现为 `SqliteVecStore`。RAG Engine 不读取 sqlite-vec 的内部表,不直接拼接 sqlite-vec 特有 SQL。 + +### 9.4 EmbeddingProvider + +Embedding 通过统一接口调用: + +```python +class EmbeddingProvider(Protocol): + async def embed_documents(self, texts: list[str]) -> list[list[float]]: ... + async def embed_query(self, query: str) -> list[float]: ... +``` + +默认配置使用本地 BGE-M3 类模型。模型名称和具体推理实现由配置决定,索引记录需要保存 embedding model id 和向量维度。用户更换模型后,由索引服务识别维度或模型变化并提示重新建立向量索引。 + +### 9.5 RRF 与 Reranker + +FTS5 和 Vector Search 分别产生候选集合,经 RRF 进行排名融合。融合后的候选交给 BGE reranker 类 Cross-Encoder 进行精排。 + +初始参数可以采用: + +```text +FTS5 Top 20 +Vector Top 20 +RRF Top 20 +Reranker Top 8 +Context Top 5~8 +``` + +参数保留在 Retrieval Config 中,Benchmark 完成后再依据测试结果调整。 + +### 9.6 Citation + +进入 Context Builder 的每个 Block都携带: + +```text +note_id +block_id +file_path +heading_path +content +source_metadata +``` + +LLM 返回的引用映射回 `Citation`。前端点击引用后打开对应文件并定位 Block。 + +音频生成的笔记可以额外保存: + +```text +source_audio +start_time +end_time +speaker +``` + +引用可以继续跳转至原始音频时间。 + +--- + +## 10. Agent Runtime + +### 10.1 Agent 执行模型 + +Agent Runtime 维护一次任务的完整执行状态: + +```mermaid +flowchart TB + U["User Request"] --> C["Build Agent Context"] + C --> M["Model Provider"] + M -->|Text| O["Final / Intermediate Output"] + M -->|Tool Call| P["Permission Check"] + P --> T["Tool Registry"] + T --> E["Execute Tool"] + E --> R["Tool Result"] + R --> M +``` + +AgentState 包含: + +```text +run_id +messages +available_tools +current_skill +current_model +step +max_steps +token_budget +tool_results +citations +status +cancelled +``` + +每轮模型调用都可以产生文本、Thinking、Tool Call、Usage 和错误事件。Agent Runtime 将这些事件转换为统一 AgentEvent 推送给前端。 + +### 10.2 运行控制 + +Agent Run 至少提供以下限制: + +- `max_steps`; +- 单 Tool 超时; +- 整体任务超时; +- 最大 Token Budget; +- 用户取消; +- 高风险 Tool 二次确认; +- 网络访问权限; +- 并发 Tool 数量。 + +Agent 运行过程中产生的每一步写入 `agent_runs` 和 `tool_calls`,用户可以在 Agent Trace 中查看工具名称、参数摘要、耗时、执行结果和权限状态。 + +### 10.3 Tool Registry + +Agent 可调用能力统一注册到 Tool Registry。 + +第一阶段工具集: + +```text +notes.search +notes.read +notes.create +notes.update +notes.list +notes.move + +rag.search + +tasks.create +tasks.update +tasks.list + +attachments.read + +audio.transcribe +``` + +内部 Tool 定义采用统一结构: + +```json +{ + "name": "notes.search", + "description": "Search notes in current vault", + "parameters": { + "type": "object", + "properties": { + "query": { + "type": "string" + } + }, + "required": ["query"] + } +} +``` + +Tool Executor 对参数再次进行 Pydantic 校验。文件修改类 Tool 调用 Knowledge Core,不允许 Tool 自行读取或修改 SQLite 表。 + +--- + +## 11. Skill Runtime + +Skill 用于将一组稳定的 AI 使用方式保存为可复用配置。一个 Skill 可以携带提示词、允许使用的工具、权限声明、检索参数和输出要求。 + +建议目录结构: + +```text +skills/ +└── exam-review/ + ├── skill.yaml + ├── prompt.md + ├── README.md + └── icon.svg +``` + +`skill.yaml` 示例: + +```yaml +id: exam-review +name: 期末复习助手 +version: 1.0.0 +description: 根据课程笔记生成复习内容和任务 + +permissions: + - notes.search + - notes.read + - tasks.create + +tools: + - notes.search + - notes.read + - tasks.create + +retrieval: + top_k: 10 + rerank: true + citation: true + +model: + required_capabilities: + - chat + - tool_calling +``` + +加载流程为: + +```text +Read Manifest +→ Validate Schema +→ Resolve Permissions +→ Load Prompt +→ Resolve Tool Definitions +→ Apply Retrieval Config +→ Check Model Capabilities +→ Build Agent Configuration +``` + +Skill Runtime 返回 Agent Configuration,由 Agent Runtime 执行。Skill 自身不持有独立 Agent Loop。 + +安装 Skill 时展示权限。Skill 更新后新增权限需要重新确认。比赛版本中 Skill 不包含可执行 Python 或 JavaScript 文件。 + +Skill Manifest 中引用的 Tool 由 Tool Registry 解析。Tool 可以来自应用内置模块,也可以由已启用 Plugin 注册。Skill 安装时如果缺少所需 Plugin 或 Tool,Skill 状态标记为 `dependency_missing`,界面显示缺失依赖,不进入 Agent Run。 + +--- + +## 12. Plugin Runtime + +Plugin 用于扩展应用原有代码没有提供的程序能力。典型用途包括接入第三方服务、增加 Agent Tool、增加导入导出格式、增加命令和提供受控的侧边栏面板。 + +### 12.1 Skill、Plugin 与 Theme 的职责 + +三类扩展在代码和权限上分别管理: + +| 类型 | 主要作用 | 是否提供可执行能力 | 与 Agent 的关系 | +| --- | --- | --- | --- | +| Skill | 保存 AI 工作方法、Prompt、Tool 绑定、检索和模型配置 | 不直接提供 | 生成 Agent Configuration | +| Plugin | 提供新功能、Tool、外部服务适配和 UI Contribution | 可以提供 | 向 Tool Registry 注册能力 | +| Theme | 修改界面视觉和编辑器样式 | 不提供业务执行能力 | 无直接依赖 | + +一个典型组合可以表示为: + +```text +GitHub Plugin +→ 注册 github.search_issues Tool +→ Tool Registry + +Issue Review Skill +→ 声明使用 github.search_issues +→ Skill Runtime +→ Agent Runtime +→ github.search_issues +→ GitHub Plugin +``` + +Plugin 为 Skill 和 Agent 提供能力来源,Skill 负责描述这些能力如何组合使用。 + +### 12.2 Plugin Package + +Plugin 使用 Manifest 描述基本信息、入口、权限、依赖和 Contribution。 + +建议目录: + +```text +plugins/ +└── example-plugin/ + ├── plugin.yaml + ├── README.md + ├── icon.svg + ├── backend/ + │ └── ... + └── ui/ + └── ... +``` + +`plugin.yaml` 示例: + +```yaml +id: example-plugin +name: Example Plugin +version: 1.0.0 +description: 提供示例外部服务能力 + +permissions: + - notes.read + - network.request + +contributes: + tools: + - example.search + commands: + - example.open-panel + panels: + - example-panel + +backend: + type: mcp + transport: stdio +``` + +Manifest 进入安装流程前使用 Pydantic Schema 校验。Plugin ID、版本和 Contribution ID 在本地 Plugin Registry 中保持唯一。 + +### 12.3 Plugin Contribution + +第一阶段允许 Plugin 声明以下 Contribution: + +```text +Tool +Command +Importer +Exporter +Sidebar Panel +Settings Section +``` + +其中 Tool 面向 Agent;Command 面向命令面板和快捷操作;Importer / Exporter 用于文件格式扩展;Sidebar Panel 和 Settings Section 为前端提供受控扩展位置。 + +Contribution 由宿主应用决定挂载位置。Plugin 不直接修改应用路由、Pinia Store 或核心数据库 Schema。 + +### 12.4 Plugin Host + +带后端执行逻辑的第三方 Plugin 运行在独立 Plugin Host 中。项目不将第三方 Python 模块直接 `import` 到 AI Core,也不将第三方动态库直接加载到 Rust Host。 + +运行关系: + +```text +Plugin Runtime +↓ +Read & Validate Manifest +↓ +Resolve Permission +↓ +Start Plugin Host +↓ +Establish MCP / Internal RPC Channel +↓ +Discover Contributions +↓ +Register Tools / Commands +↓ +Ready +``` + +Plugin Host 负责: + +- 启动和关闭插件后端; +- 管理插件进程; +- 建立 MCP 或内部 RPC 通道; +- 转换 Tool Definition; +- 执行超时控制; +- 收集健康状态; +- 在插件崩溃时注销对应 Tool; +- 隔离插件日志。 + +内置 Plugin 可以使用相同的 Plugin Interface 注册能力,减少内置功能和社区扩展之间的接口差异。 + +### 12.5 MCP Bridge + +MCP Bridge 用于接入具有 MCP Server 接口的插件或外部工具服务。 + +MCP Tool 进入系统后的调用路径为: + +```text +MCP Server +↓ +MCP Bridge +↓ +Plugin Runtime +↓ +Tool Registry +↓ +Agent Runtime +``` + +Tool Registry 仍使用项目自己的 `ToolDefinition` 和 `ToolResult`。MCP Bridge 负责协议转换,Agent Runtime 不直接依赖 MCP 数据结构。 + +MCP 能力首先用于 Tool 和 Resource 类扩展。需要复杂 UI 的插件通过 Frontend Extension Slot 单独处理。 + +### 12.6 Frontend Extension Slot + +前端预留受控扩展点: + +```text +Command Palette +Toolbar Action +Sidebar Panel +Settings Section +Context Menu +``` + +Plugin UI 不能直接访问 Rust Command、Stronghold、Pinia 内部状态和任意本地文件。宿主通过 Plugin Bridge 提供允许的 API,例如: + +```text +workspace.getCurrentNote +notes.read +commands.execute +events.subscribe +pluginStorage.get +pluginStorage.set +``` + +需要使用敏感能力时仍进入统一 Permission 流程。 + +比赛版本可以优先实现 Command、Tool、Settings Section 和简单 Sidebar Panel,复杂前端插件 API 在接口稳定后继续扩展。 + +### 12.7 Plugin 生命周期 + +Plugin 状态至少包括: + +```text +installed +disabled +starting +ready +error +dependency_missing +permission_required +``` + +完整生命周期: + +```text +Install +→ Validate Manifest +→ Resolve Dependencies +→ Request Permissions +→ Register Metadata +→ Enable +→ Start Host +→ Register Contributions +→ Ready +``` + +停用 Plugin 时先从 Tool Registry 和前端 Extension Registry 注销 Contribution,再关闭 Plugin Host。 + +卸载前检查是否存在启用中的 Skill 依赖该 Plugin。存在依赖时向用户显示受影响 Skill。 + +### 12.8 Plugin Storage + +每个 Plugin 获得独立的配置和数据命名空间: + +```text +.ainote/ +└── plugins/ + └── / + ├── config.json + ├── data/ + └── logs/ +``` + +Plugin Storage API 负责访问该目录。插件不能通过自身目录拼接相对路径访问其他插件数据或 Stronghold。 + +需要保存密钥的 Plugin 通过 Secret API 请求独立 Credential ID,由 Stronghold 保存实际值。 + +--- + +## 13. Provider Adapter 与模型接入 + +### 13.1 统一模型协议 + +项目内部统一使用 `ModelRequest` 和 `ModelEvent`。Provider Adapter 负责和实际模型协议转换。 + +```text +Chat / Agent / Skill + ↓ +ModelRequest + ↓ +Provider Adapter + ↓ +OpenAI / Anthropic / Ollama / Compatible API + ↓ +Provider Response + ↓ +ModelEvent +``` + +首批 Adapter: + +```text +OpenAIResponsesProvider +OpenAIChatCompletionsProvider +OpenAICompatibleProvider +AnthropicMessagesProvider +OllamaProvider +``` + +### 13.2 ModelRequest + +建议统一字段: + +```text +provider_id +model +system +messages +tools +temperature +max_tokens +response_format +attachments +metadata +``` + +Provider 只转换自己支持的字段。模型能力通过 Capability 描述: + +```text +chat +vision +tool_calling +reasoning +streaming +structured_output +embedding +``` + +Skill 或 Agent 启动前可以根据 Capability 检查当前模型是否满足任务要求。 + +### 13.3 Streaming + +所有 Provider 的流式响应映射为: + +```text +TextDelta +ThinkingDelta +ToolCallStart +ToolCallDelta +ToolCallEnd +Usage +Error +Done +``` + +前端 Streaming UI 只认识这些事件类型。 + +### 13.4 Provider 配置与 API Key + +普通 Provider 配置保存在 SQLite: + +```text +provider_id +provider_type +base_url +default_model +credential_id +enabled +``` + +Stronghold 保存 `credential_id` 对应的实际 API Key。 + +设置页面执行“测试连接”时: + +```text +读取 Provider Config +→ Rust Host 读取 Secret +→ 构造临时 Credential Context +→ AI Core 调用 Provider +→ 返回连接测试结果 +``` + +日志中不记录完整 API Key。请求异常信息在进入前端前过滤 Authorization Header 和密钥片段。 + +--- + +## 14. 多模态处理 + +### 14.1 音频处理 + +课堂或会议音频进入 Media Pipeline: + +```mermaid +flowchart LR + A["Audio"] --> D["pyannote.audio"] + D --> S["Speaker Segments"] + S --> W["faster-whisper"] + W --> T["Timestamped Transcript"] + T --> C["Content Structuring"] + C --> M["Markdown Note"] + M --> I["Index Pipeline"] +``` + +pyannote.audio 生成说话人区间;faster-whisper 对各区间进行转写。最终 Transcript 至少包含: + +```text +speaker +start_time +end_time +text +``` + +内容结构化模块将 Transcript 整理为 Markdown,同时保留音频时间信息,后续 RAG 引用可以跳回音频片段。 + +### 14.2 OCR + +OCR 作为 Media Pipeline 的输入适配能力,用于图片笔记、白板照片、PPT 截图和扫描资料。OCR 输出进入附件文本索引,也可以由用户选择生成 Markdown。 + +OCR 引擎在当前技术栈中尚未固定,调用接口先定义为 `OCRProvider`,具体实现完成 PoC 后确定。 + +### 14.3 emotion2vec + +emotion2vec 作为音频扩展分析模块。输出可以附加到音频段元数据,不参与核心 RAG 索引和 Agent 启动流程。 + +--- + +## 15. 安全与权限 + +### 15.1 敏感信息 + +模型 API Key、同步 Token 和其他凭证统一存放在 Stronghold。以下位置不保存明文密钥: + +```text +Markdown +SQLite +普通 JSON / YAML +前端 Pinia 持久化 +应用日志 +Agent Trace +``` + +### 15.2 Tool 权限 + +Tool Permission 使用命名空间形式: + +```text +notes.read +notes.search +notes.write +notes.delete + +tasks.read +tasks.write + +attachments.read + +network.request +``` + +读操作可以按 Skill 授权持续生效。删除文件、外部网络请求等高影响能力可以配置为每次确认。 + +### 15.3 Plugin 权限与隔离 + +Plugin Manifest 必须声明所需权限。插件安装、启用和权限升级时,由宿主应用展示权限范围并记录用户授权结果。 + +Plugin 与 Tool 复用统一权限命名空间,第一阶段包含: + +```text +notes.read +notes.search +notes.write +notes.delete + +tasks.read +tasks.write + +attachments.read + +network.request +secrets.use + +ui.command +ui.settings +ui.sidebar +``` + +Plugin 后端运行在独立 Plugin Host 中,通过 MCP Bridge 或内部 RPC 使用宿主提供的能力。第三方 Plugin 不直接获得 SQLite 文件路径、Stronghold 主密钥、Rust Host 内部对象或其他 Plugin 的数据目录。 + +Plugin 向 Tool Registry 注册工具后,Agent 调用该工具仍经过 Tool Permission。Skill 引用 Plugin Tool 时,需要同时满足 Skill 声明的权限、Plugin 自身获得的权限以及当前用户对高影响操作的确认策略。 + +第三方 Plugin 崩溃时,Plugin Runtime 将状态更新为 `error`,注销对应 Tool、Command 和 UI Contribution,并向正在执行的 Agent 返回 `PLUGIN_UNAVAILABLE`。其他 Plugin、AI Core 和 Markdown 编辑功能继续运行。 + +Plugin 需要使用 API Key 等敏感数据时,通过 Secret API 创建独立 `credential_id`。Plugin 只能请求使用属于自身命名空间的凭证,实际明文继续由 Stronghold 保存。 + +### 15.4 RAG 上下文 + +Agent Context 明确区分: + +```text +System Instruction +Skill Instruction +User Instruction +Retrieved Context +Tool Result +``` + +Retrieved Context 保存笔记内容和检索来源。Tool Registry 的权限来源于 Agent Configuration 和用户授权,不从检索文本中解析权限要求。 + +--- + +## 16. 云同步与多设备互联 + +### 16.1 架构定位 + +项目采用本地优先的厚客户端结构。Tauri Desktop App、Rust Host 和 Python AI Core 共同组成完整客户端,笔记编辑、Markdown 文件管理、SQLite 元数据、FTS5、向量索引、RAG、Agent、Skill、Plugin、模型接入和多模态处理都可以在用户设备上运行。 + +Sync Server 是一项独立部署的基础设施,用于在多个客户端之间同步 Vault 数据。用户没有服务器时可以直接使用本地模式;用户拥有 NAS、VPS、实验室服务器或其他可运行 Docker 的环境时,可以部署自己的 Sync Server 并连接多个客户端。 + +```mermaid +flowchart TB + + subgraph Server["Optional Self-hosted Sync Server"] + direction TB + SyncAPI["Sync API / WebSocket"] + Auth["Auth & Device"] + VaultSvc["Vault & Revision"] + Push["Change Notification"] + PG[("PostgreSQL")] + OBJ[("S3 / MinIO")] + + SyncAPI --> Auth + SyncAPI --> VaultSvc + SyncAPI --> Push + VaultSvc --> PG + VaultSvc --> OBJ + end + + subgraph A["Desktop Client A"] + direction TB + AUI["Vue + Tauri"] + ACore["Python AI Core"] + AVault[("Markdown Vault")] + ADB[("SQLite / FTS5 / Vector")] + AUI --> ACore + ACore --> AVault + ACore --> ADB + end + + subgraph B["Desktop Client B"] + direction TB + BUI["Vue + Tauri"] + BCore["Python AI Core"] + BVault[("Markdown Vault")] + BDB[("SQLite / FTS5 / Vector")] + BUI --> BCore + BCore --> BVault + BCore --> BDB + end + + A <-->|"HTTPS / WebSocket"| Server + B <-->|"HTTPS / WebSocket"| Server +``` + +每台设备维护自己的 Markdown Vault、SQLite、FTS5 和 Vector Index。服务器保存同步需要的远程版本和文件对象。设备拉取远程变化后,本地 Note Core 触发增量索引。 + +该架构支持三种运行方式: + +| 模式 | 是否需要账号 | 是否需要用户服务器 | 主要能力 | +| --- | --- | --- | --- | +| 本地模式 | 否 | 否 | 单设备完整笔记与 AI 功能 | +| 自托管同步 | 是,由自建 Server 管理 | 是 | 多设备同步、版本记录、设备管理 | +| 托管同步 | 是 | 否 | 后续可提供的官方托管实例,使用相同 Sync Protocol | + +客户端和 Sync Server 使用同一套同步协议。自托管与托管模式只改变服务器部署位置,不改变本地 Vault 和客户端核心业务结构。 + +### 16.2 Sync Server 的职责 + +Sync Server 处理跨设备数据协调。服务端主要模块包括: + +```text +Authentication +Device Management +Vault Management +Sync Coordinator +Revision History +Conflict Detection +Change Notification +Object Storage +``` + +`Authentication` 管理用户登录和访问令牌。`Device Management` 记录已授权设备和设备状态。`Vault Management` 管理远程 Vault 和成员关系。`Sync Coordinator` 接收客户端变更并计算需要拉取的 Revision。`Revision History` 保存文件版本。`Change Notification` 通过 WebSocket 或长连接通知在线设备存在新的远程变更。大文件和附件由 S3 / MinIO 保存。 + +服务器不运行以下客户端计算模块: + +```text +RAG Engine +Embedding +Reranker +FTS5 +Vector Index +Agent Runtime +Skill Runtime +本地 LLM +ASR 模型 +``` + +这些模块依赖本地设备的模型选择和计算环境。各设备可以采用不同的 Embedding Provider、模型和计算配置。 + +### 16.3 服务端存储 + +PostgreSQL 保存同步控制数据,例如: + +```text +users +devices +vaults +vault_members +files +file_revisions +sync_cursors +sessions +``` + +文件记录可以包含: + +```text +file_id +vault_id +path +revision +content_hash +size +updated_at +updated_by_device +object_key +``` + +Markdown、图片、音频、PDF 和其他附件对象保存在 S3 / MinIO。 + +服务端数据关系可以简化为: + +```text +User +├── Device +└── Vault + ├── Member + ├── File + │ └── Revision + └── Asset +``` + +客户端使用稳定 `file_id` 识别同步对象。文件路径发生移动或重命名时继续沿用原 `file_id`。 + +### 16.4 同步范围 + +默认同步: + +```text +Markdown +Assets +Task 数据 +用户创建的 Skill +Skill 配置 +Theme 配置 +基础 Workspace 配置 +``` + +可选同步: + +```text +AI Conversation +Agent History +Plugin 安装清单 +Theme 安装清单 +Workspace Layout +部分应用设置 +``` + +默认不同步: + +```text +FTS5 Index +Vector Index +Embedding +RAG Cache +模型 Cache +临时文件 +Agent 中间运行状态 +本地日志 +设备级性能配置 +``` + +例如 Client A 使用本地 BGE-M3,Client B 使用另一种 Embedding Provider。服务器只同步 Markdown。Client B 收到文件后按照自己的 Embedding 配置生成向量,并写入本机 VectorStore。 + +### 16.5 多设备同步流程 + +客户端文件保存后先完成本地写入,再异步提交同步任务: + +```text +Local Markdown Save +→ Local Revision Record +→ Sync Queue +→ Upload Metadata +→ Upload Changed Object +→ Commit Remote Revision +→ Notify Other Devices +``` + +另一设备收到远程变化后: + +```text +Remote Change Notification +→ Pull Revision Metadata +→ Download Changed Object +→ Validate Hash +→ Update Local Vault +→ File Change Event +→ Note Core +→ Incremental Reindex +``` + +同步失败不会回滚已经成功保存的本地 Markdown。失败任务留在 Sync Queue 中等待网络恢复或用户手动重试。 + +### 16.6 Revision 与冲突检测 + +文件同步携带客户端修改基于的版本号,避免使用修改时间直接覆盖远程内容。 + +上传请求至少包含: + +```json +{ + "file_id": "file_xxx", + "base_revision": 10, + "content_hash": "sha256:...", + "device_id": "device_xxx" +} +``` + +当服务器当前 Revision 与 `base_revision` 一致时,可以接受客户端提交并生成下一 Revision。 + +```text +Client base_revision = 10 +Server revision = 10 +→ Commit revision 11 +``` + +当服务器已经存在其他设备提交的新 Revision 时,服务端返回冲突: + +```text +Client base_revision = 10 +Server revision = 11 +→ Conflict +``` + +客户端保留本地版本和远程版本,并提供: + +```text +保留本地版本 +保留远程版本 +比较差异 +手动合并 +``` + +无法自动处理的冲突可以生成单独的冲突文件,例如: + +```text +死锁 (conflict-Laptop-20260826).md +``` + +第一阶段同步采用文件级 Revision 和冲突检测。实时多人协同编辑涉及操作级合并,可在后续版本评估 Yjs、Automerge 或其他 CRDT 方案。 + +### 16.7 Skill、Plugin 与 Theme 的跨设备处理 + +Skill 主要由 Manifest、Prompt 和配置组成,可以作为 Vault 配置的一部分同步。另一设备拉取后由 Skill Runtime 重新校验 Manifest、权限和 Tool 依赖。 + +Plugin 包含可执行能力,跨设备同步只同步安装清单和版本信息,例如: + +```json +{ + "plugin_id": "github-integration", + "version": "1.3.2" +} +``` + +另一设备缺少该 Plugin 时显示依赖提示,由用户确认安装。Plugin 二进制或可执行代码不通过普通 Vault 同步直接复制到其他设备。 + +Theme 可以同步启用配置和主题清单。自定义主题文件是否同步由用户配置。 + +### 16.8 Provider 与密钥的跨设备处理 + +普通 Provider 配置可以选择同步,例如: + +```text +provider_type +base_url +default_model +model_preferences +``` + +API Key、同步 Token 和 Plugin Credential 不进入普通 Vault 同步。每台设备通过 Tauri Stronghold 保存自己的密钥。 + +后续如需跨设备同步凭证,应设计独立的端到端加密 Credential Vault,并包含新设备授权、密钥恢复和密钥轮换机制。 + +### 16.9 自托管部署 + +Sync Server 提供 Docker Compose 作为标准部署方式。基础部署包含: + +```text +Sync API +PostgreSQL +MinIO +``` + +示意结构: + +```yaml +services: + sync-server: + image: project/sync-server + depends_on: + - postgres + - minio + + postgres: + image: postgres + + minio: + image: minio/minio +``` + +用户部署完成后在客户端填写: + +```text +Server URL +Username +Password / Token +``` + +客户端完成登录、设备注册和 Vault 绑定后启动同步。 + +生产部署需要配置 HTTPS。对象存储、数据库备份、域名、反向代理和邮件服务等属于部署层配置。 + +### 16.10 与 Typora、Obsidian 的同步能力区分 + +项目在 Markdown 本地存储理念上与 Typora、Obsidian 存在相似基础,主要差异集中在同步服务和 AI 运行架构。 + +#### Typora + +Typora 官方文档说明其工作内容保存为本地 Markdown 纯文本文件,跨设备同步可以使用 iCloud Drive、Google Drive、OneDrive、Dropbox 等第三方同步工具。当前 Typora 官方同步文档没有提供由 Typora 自身运行的多设备 Sync Server。 + +本项目同样直接保存 Markdown 文件,并将多设备同步协议和 Sync Server 纳入项目自身架构。用户无需把 Vault 放入第三方云盘目录即可使用应用内版本同步、设备管理和冲突检测。 + +#### Obsidian + +Obsidian 以本地 Vault 为基础,离线时仍可访问笔记。其官方文档列出了 Obsidian Sync、iCloud、OneDrive、Google Drive、Syncthing 和 Git 等多种同步方式。 + +Obsidian Sync 是官方提供的附加订阅服务。按照 2026 年 8 月的官方页面,Sync Standard 按年付费折算为每用户每月 4 美元,Sync Plus 按年付费折算为每用户每月 8 美元。官方 Sync 提供远程 Vault、端到端加密、版本历史、共享 Vault 和跨设备同步。Obsidian Headless 同样连接 Obsidian Sync 服务,并要求有效的 Sync 订阅。 + +本项目把可自托管的 Sync Server 纳入正式技术架构。用户可以在自己的 VPS、NAS、实验室服务器或私有云中部署同一套 Sync Server。客户端使用统一 Sync Protocol 连接自托管实例;后续如提供项目托管实例,也继续使用相同协议。 + +#### 技术差异汇总 + +| 项目 | 本地 Markdown | 离线使用 | 官方应用内同步方案 | 官方同步付费要求 | 项目自身提供自托管 Sync Server | AI / RAG 本地运行 | +| --- | --- | --- | --- | --- | --- | --- | +| Typora | 支持 | 支持 | 当前官方文档主要引导第三方文件同步 | 取决于第三方服务 | 当前官方文档未提供 | 不属于其核心同步架构 | +| Obsidian | 支持 | 支持 | Obsidian Sync,同时支持多种第三方同步方式 | Obsidian Sync 需要订阅 | 当前官方文档未提供 Obsidian Sync 服务端自托管方案 | 可通过插件或其他方案扩展 | +| 本项目 | 支持 | 支持 | 内置 Sync Client + Sync Protocol | 自托管不需要项目方 Sync 订阅 | 支持,作为正式架构组成部分 | RAG、Agent、Skill、Plugin 和索引位于客户端 | + +这组差异体现部署控制权和本地 AI 架构。项目不将 `Local-first` 作为独有特性;Typora 和 Obsidian 都采用本地文件工作方式。项目的同步定位为: + +```text +完整本地客户端 ++ +官方定义的 Sync Protocol ++ +可选自托管 Sync Server ++ +多设备增量同步 +``` + +### 16.11 后续安全扩展 + +同步协议后续可以加入端到端加密模式。客户端在上传前完成内容加密,服务器保存密文对象和必要的同步元数据。 + +```text +Markdown / Asset +→ Client Encryption +→ Encrypted Object +→ Sync Server +→ S3 / MinIO +``` + +端到端加密设计需要覆盖 Vault Key、密钥派生、新设备授权、恢复机制和密钥轮换。该功能在基础 Revision、冲突处理和多设备同步稳定后单独设计。 + +### 16.12 官方资料依据 + +本文对 Typora 和 Obsidian 的比较基于 2026 年 8 月可访问的官方资料: + +- Typora Support — Work with Mobile and other Devices: https://support.typora.io/Sync/ +- Obsidian Help — Sync your notes across devices: https://obsidian.md/help/sync-notes +- Obsidian Help — Introduction to Obsidian Sync: https://obsidian.md/help/sync +- Obsidian Help — Plans and storage limits: https://obsidian.md/help/sync/plans +- Obsidian Help — Headless Sync: https://obsidian.md/help/sync/headless + +--- + +## 17. 本地接口约定 + +### 17.1 API 结构 + +FastAPI 接口按业务资源组织。示例: + +```text +GET /api/notes/{note_id} +POST /api/search +POST /api/chat +POST /api/agent/runs +POST /api/agent/runs/{run_id}/cancel +GET /api/agent/runs/{run_id}/events +GET /api/skills +GET /api/plugins +POST /api/plugins/install +POST /api/plugins/{plugin_id}/enable +POST /api/plugins/{plugin_id}/disable +POST /api/providers/test +POST /api/index/rebuild +GET /health +``` + +HTTP 返回统一错误结构: + +```json +{ + "error": { + "code": "PROVIDER_TIMEOUT", + "message": "Model provider request timed out", + "details": {} + } +} +``` + +错误代码由项目定义,前端根据 `code` 决定提示和恢复操作,不解析第三方 SDK 的异常文本来判断业务逻辑。 + +### 17.2 ID + +数据库主实体使用应用生成的稳定 ID。文件路径可以修改,不能作为 Note、Block、Agent Run 等对象的唯一业务 ID。 + +推荐实体: + +```text +note_id +block_id +attachment_id +conversation_id +message_id +run_id +tool_call_id +skill_id +plugin_id +provider_id +``` + +### 17.3 时间 + +内部 API 使用 UTC ISO 8601 时间。前端根据用户系统时区显示。 + +### 17.4 日志 + +日志按模块记录: + +```text +desktop.log +ai-core.log +index.log +agent.log +sync.log +``` + +日志包含 request_id / run_id 等关联字段。日志不得写入完整正文、API Key 和未经用户允许的文件内容。 + +--- + +## 18. 工程组织 + +项目采用 Monorepo: + +```text +ainote/ +├── apps/ +│ └── desktop/ +│ ├── src/ +│ └── src-tauri/ +│ +├── services/ +│ └── ai-core/ +│ ├── app/ +│ │ ├── api/ +│ │ ├── notes/ +│ │ ├── rag/ +│ │ ├── agent/ +│ │ ├── skills/ +│ │ ├── providers/ +│ │ ├── media/ +│ │ └── database/ +│ └── tests/ +│ +├── packages/ +│ ├── contracts/ +│ ├── ui/ +│ ├── plugin-sdk/ +│ └── theme-sdk/ +│ +├── skills/ +│ └── builtin/ +│ +├── plugins/ +│ └── builtin/ +│ +├── themes/ +│ └── builtin/ +│ +├── benchmarks/ +│ ├── rag/ +│ └── agent/ +│ +├── infra/ +│ └── cloud/ +│ +└── docs/ +``` + +目录和功能归属按以下规则执行: + +- 桌面页面和交互位于 `apps/desktop/src/features`。 +- Rust Command、Sidecar、Stronghold、文件监听位于 `apps/desktop/src-tauri`。 +- Python API Endpoint 位于 `services/ai-core/app/api`。 +- 核心业务逻辑位于对应 domain 目录,不直接写在 Router 中。 +- 数据库访问集中在 database/repository 层。 +- Provider SDK 只出现在 `providers`。 +- VectorStore 具体实现只出现在 Retrieval 基础设施层。 +- 内置 Skill 放在 `skills/builtin`,不硬编码在 Agent Runtime。 +- Plugin Runtime、Plugin Host 和 MCP Bridge 位于 Extension Core,对第三方插件暴露的稳定接口放在 `packages/plugin-sdk`。 +- 内置 Plugin 放在 `plugins/builtin`,通过与第三方 Plugin 相同的 Contribution 接口注册。 +- Benchmark 数据和运行脚本放在 `benchmarks`。 + +--- + +## 19. 开发环境与启动方式 + +### 19.1 基础环境 + +团队开发机需要准备: + +```text +Node.js +pnpm +Rust toolchain +Tauri CLI +Python 3.x +uv / Poetry(团队确定一种) +SQLite +Git +``` + +Python 环境需要支持 faster-whisper、pyannote.audio、Embedding 和 Reranker 所需依赖。涉及 CUDA 的开发成员可以安装 GPU 版本,基础功能仍需提供 CPU 可运行路径。 + +### 19.2 本地开发 + +开发模式下分别启动 AI Core 和 Tauri: + +```text +Terminal A +services/ai-core +→ start FastAPI dev server + +Terminal B +apps/desktop +→ pnpm tauri dev +``` + +开发配置允许桌面端连接固定开发端口。正式构建时改为 Sidecar 随机端口模式。 + +### 19.3 配置 + +环境配置分为: + +```text +development +test +production +``` + +模型 API Key 不放入仓库 `.env` 示例。`.env.example` 只包含无敏感值的配置字段说明。 + +--- + +## 20. 测试基线 + +### 20.1 单元与接口测试 + +Python 使用 pytest。重点覆盖: + +- Markdown Parser; +- Note Block diff; +- FTS Query; +- VectorStore; +- RRF; +- Citation; +- Tool 参数校验; +- Agent step limit; +- Skill Manifest; +- Plugin Manifest; +- Plugin Lifecycle; +- Plugin Tool Registration; +- MCP Bridge; +- Provider Adapter; +- 权限判断; +- API 错误转换。 + +前端测试重点覆盖 Store、Service 和关键交互流程。Rust 侧覆盖路径处理、Sidecar 启停和 Secret 访问封装。 + +### 20.2 RAG Benchmark + +RAG Dataset 结构: + +```text +query +expected_note_id +expected_block_id +expected_citation +tags +``` + +至少比较: + +```text +FTS5 +Vector +Hybrid +Hybrid + Reranker +``` + +核心指标: + +```text +Recall@K +Hit@K +MRR +Citation Precision +Citation Recall +Latency +``` + +Benchmark 参数、Embedding 模型、Reranker、数据集版本和运行环境需要一起记录,保证不同实验结果可以复现。 + +### 20.3 Agent Benchmark + +Agent Dataset 保存任务目标、允许工具、期望工具序列和结果条件。 + +核心指标: + +```text +Task Success Rate +Tool Selection Accuracy +Tool Argument Accuracy +Average Steps +Average Latency +Token Usage +``` + +Agent Benchmark 同时记录模型 Provider 和模型版本。 + +--- + +## 21. 主要功能的数据流 + +### 21.1 编辑笔记 + +```text +User +→ Milkdown / CodeMirror +→ EditorDocumentService +→ Rust File Access +→ Markdown File +→ File Watch Event +→ Note Core +→ Block Diff +→ SQLite / FTS5 / Vector Index +``` + +### 21.2 搜索笔记 + +```text +Search UI +→ /api/search +→ Retrieval Core +→ FTS5 / VectorStore +→ Result Fusion +→ SearchResult +→ Frontend +→ Open & Highlight Note Block +``` + +### 21.3 AI 知识库问答 + +```text +AI Chat +→ RAG Engine +→ Hybrid Retrieval +→ Reranker +→ Context Builder +→ Provider Adapter +→ LLM +→ Answer + Citation +→ Frontend +``` + +### 21.4 Agent 操作笔记 + +```text +User Task +→ Agent Runtime +→ Provider Adapter +→ Tool Call +→ Permission Check +→ Tool Registry +→ Knowledge / Retrieval / Task Service +→ Tool Result +→ Agent Runtime +→ Provider Adapter +→ Final Result +``` + +### 21.5 Skill 执行 + +```text +Select Skill +→ Skill Runtime +→ Validate Manifest +→ Resolve Permission +→ Resolve Tool Dependencies +→ Bind Built-in / Plugin Tools +→ Apply Retrieval Config +→ Check Model Capability +→ Build Agent Configuration +→ Agent Runtime +``` + +### 21.6 Plugin 加载与 Tool 调用 + +```text +Install / Enable Plugin +→ Plugin Runtime +→ Validate Plugin Manifest +→ Resolve Permission +→ Start Plugin Host +→ Discover Contributions +→ Register Tool +→ Tool Registry +→ Agent Runtime Available Tools +``` + +Agent 调用插件 Tool 时: + +```text +Agent Runtime +→ Tool Registry +→ Permission Check +→ Plugin Tool Adapter +→ Plugin Host / MCP Bridge +→ Plugin Backend +→ Tool Result +→ Agent Runtime +``` + +### 21.7 课堂录音转笔记 + +```text +Audio +→ pyannote.audio +→ Speaker Segments +→ faster-whisper +→ Timestamped Transcript +→ Content Structuring +→ Markdown +→ Note Core +→ RAG Index +``` + +--- + +## 22. 异常处理与恢复 + +桌面应用需要区分编辑能力和 AI 能力的可用状态。 + +Python AI Core 启动失败时,前端显示 AI 服务不可用状态,并允许用户查看日志或重新启动 Sidecar。Markdown 编辑、文件浏览和基础本地操作保持可用。 + +Embedding 或 Reranker 模型下载失败时,对应索引任务进入失败状态。FTS5 搜索仍可运行。用户可以重新下载模型或切换 Embedding Provider 后重新建立向量索引。 + +模型 Provider 请求失败时由 Provider Adapter 转换为统一错误代码,例如: + +```text +PROVIDER_AUTH_FAILED +PROVIDER_RATE_LIMITED +PROVIDER_TIMEOUT +PROVIDER_UNAVAILABLE +MODEL_NOT_FOUND +MODEL_CAPABILITY_MISMATCH +``` + +Agent Tool 执行失败后,Tool Result 中携带可处理的错误类型。Agent Runtime 根据 Tool 是否允许重试和剩余步骤决定后续行为。 + +SQLite 索引损坏或版本不一致时,应用允许重建 `.ainote/app.db` 中的可重建索引。执行重建前保留用户任务、会话和必要应用状态的迁移策略。 + +--- + +## 23. 当前版本实施边界 + +第一阶段开发围绕能够形成完整桌面知识工作流的能力展开: + +```text +Markdown Workspace +→ 编辑与文件管理 +→ SQLite / FTS5 索引 +→ Embedding / Vector Search +→ Hybrid RAG +→ Citation +→ Provider Adapter +→ AI Chat +→ Agent Runtime +→ Tool Registry +→ Skill Runtime +→ Plugin Runtime +→ Plugin Manifest / Tool Contribution +``` + +第一阶段的 Plugin Runtime 需要完成安装、启用、停用、权限、Tool 注册和至少一个示例 Plugin,建立 Skill 调用 Plugin Tool 的完整链路。 + +第二阶段接入: + +```text +faster-whisper +pyannote.audio +主题导入与社区格式 +MCP Bridge +Plugin Command / Settings Contribution +更多 Provider +Agent Trace 可视化 +RAG / Agent Benchmark +``` + +第三阶段处理: + +```text +Sync Protocol +自托管 Sync Server +设备注册与 Vault 绑定 +文件级 Revision +冲突检测与版本历史 +Skill / Theme 配置同步 +Plugin 安装清单同步 +Skill 分发 +Plugin 分发与社区仓库 +Plugin Sidebar Panel 等前端扩展点 +Theme 社区 +OCR 深度集成 +更多多模态能力 +扩展协议 +``` + +Sync Server 按独立服务开发和部署,不进入桌面客户端核心启动依赖。第一版同步完成文件级 Revision、多设备增量同步和冲突保留后,再评估端到端加密与实时协同编辑。 + +阶段划分用于安排开发顺序。模块接口在第一阶段完成时确定基础版本,后续功能通过现有接口扩展。 + +--- + +## 24. 团队开发约定 + +一个新功能进入开发前,需要先回答以下问题: + +1. 功能属于哪个核心域。 +2. 用户数据保存在哪里。 +3. 是否需要新增数据库字段或 migration。 +4. 是否需要新增 Tauri 系统权限。 +5. 是否需要新增 Tool Permission 或 Plugin Permission。 +6. 是否应该作为内置功能、Skill 或 Plugin 实现。 +7. 是否新增 Plugin Contribution 或 Plugin SDK 接口。 +8. 是否涉及 Provider 能力差异。 +9. 是否产生新的流式事件。 +10. 是否影响 Block、FTS5 或 Vector Index。 +11. 是否需要加入 Benchmark。 +12. 失败后用户如何恢复。 + +涉及跨模块接口的 PR 同时更新对应 Contract 和本文档。Provider、Skill、Tool、VectorStore 等公共接口发生破坏性修改时,需要在 PR 中说明迁移方式。 + +技术栈选型优先服务当前功能。新增框架或基础设施需要能解决明确的项目问题,并说明它与现有模块的集成位置。团队统一维护一条主要实现路径,便于比赛版本测试、打包和现场演示。 + +--- + +## 25. 当前技术基线摘要 + +项目桌面端采用 Tauri 2、Rust、Vue 3 和 TypeScript。用户笔记以 Markdown 和 Assets 保存在本地 Vault,SQLite 管理元数据、全文索引、向量索引和 Agent Trace。 + +Python AI Core 作为 Tauri Sidecar 运行,FastAPI 提供本地接口。Knowledge Core 管理笔记结构;Retrieval Core 通过 FTS5、Embedding、sqlite-vec、RRF 和 Reranker 提供混合检索;Agent Runtime 使用 Tool Registry 操作知识库和任务;Skill Runtime 将提示词、工具、权限和检索参数组装为可复用 Agent 配置;Plugin Runtime 通过 Plugin Manifest、Plugin Host 和 MCP Bridge 扩展 Tool、Command、导入导出和受控 UI Contribution,Plugin 注册的 Tool 可以被 Agent 与 Skill 共同使用;Provider Adapter 对接 OpenAI、OpenAI-Compatible、Anthropic 和 Ollama 等模型服务。 + +多模态处理使用 faster-whisper 和 pyannote.audio 完成音频转写和说话人分离,emotion2vec 作为扩展分析能力。API Key 和同步凭证存放在 Tauri Stronghold。多设备同步由独立 Sync Server 提供,采用 FastAPI、PostgreSQL 和 S3/MinIO,可由用户自托管。客户端在没有 Sync Server 时保持完整本地功能;连接服务器后同步 Markdown、Assets 和必要配置,各设备自行维护 FTS5、Embedding 和 Vector Index。 + +该技术基线用于指导当前比赛版本的代码组织、接口设计、模块协作、测试和交付。 diff --git a/docs/第一阶段分工表.md b/docs/第一阶段分工表.md new file mode 100644 index 0000000..440bb98 --- /dev/null +++ b/docs/第一阶段分工表.md @@ -0,0 +1,55 @@ +# 第一阶段分工表 + +## 总分工表 + +| 成员 | 主要职责 | 第一阶段负责模块 | 具体工作内容 | 主要交付物 | +| ------ | -------------------------------- | ------------------------------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | +| 吉海燕 | 前端设计与交互实现 | Desktop Frontend / UI | 负责 Vue 3 + TypeScript 前端界面设计与实现;完成 Workspace、Markdown Editor、AI Chat、Search、Agent Trace、Skill/Plugin 管理、Theme Manager、Settings 等页面;维护 Design Token 与主题系统;负责前端状态管理与接口联调 | 前端页面、组件库、交互逻辑、主题系统、前端 Store、API Client、前端演示界面 | +| 范涵宇 | 主要程序开发、整体架构与代码审阅 | Agent Core / Extension Core / Model Core / 后端基础框架 | 负责 Python AI Core 与 FastAPI 后端壳子搭建;负责 Agent Runtime、Tool Registry、Permission、Trace;负责 Skill Runtime、Plugin Runtime、Plugin Host/MCP Bridge 等扩展体系;负责 Provider Adapter、多模型协议统一与 API Key 调用链;负责核心接口设计、模块集成、工程规范与代码 Review | FastAPI Core、Agent Runtime、Tool Registry、Skill Runtime、Plugin Runtime、Provider Adapter、公共接口、代码审阅与集成版本 | +| 杨星萱 | 本地知识库与检索系统开发 | Knowledge Core / Retrieval Core | 负责 Markdown Vault、Note/Block 数据模型、Markdown 解析与增量索引;负责 SQLite 元数据、FTS5 全文检索、sqlite-vec 向量检索;负责 Embedding、Hybrid Retrieval、RRF、Reranker、Metadata Filter、Citation 与笔记定位;负责 RAG 检索 Benchmark | Knowledge Core、Note Block、SQLite/FTS5、VectorStore、Embedding、Hybrid RAG、Reranker、Citation、检索测试数据 | + + + +## 细则: + +| 协作事项 | 主负责人 | 配合人员 | +| ------------------------------ | -------- | -------------- | +| 前后端 API Contract | 范涵宇 | 吉海燕 | +| Markdown 编辑与 Note Core 联动 | 杨星萱 | 吉海燕 | +| Search / Citation 前端展示 | 杨星萱 | 吉海燕 | +| Agent 调用 RAG | 范涵宇 | 杨星萱 | +| Agent 调用笔记 Tool | 范涵宇 | 杨星萱 | +| Skill 调用 Plugin Tool | 范涵宇 | 吉海燕 | +| Provider Streaming 前端显示 | 范涵宇 | 吉海燕 | +| Agent Trace 前端显示 | 范涵宇 | 吉海燕 | +| RAG Benchmark | 杨星萱 | 范涵宇 | +| Agent Benchmark | 范涵宇 | 杨星萱 | +| 整体 Demo 联调 | 范涵宇 | 吉海燕、杨星萱 | +| UI/UX 最终统一 | 吉海燕 | 全员 | +| PR / 核心代码 Review | 范涵宇 | 对应模块负责人 | + +## 验收: + +尽量通过VibeCoding在开学前把demo跑出来 + +### 分别交付: + +吉海燕:能够完整操作 Workspace、编辑 Markdown、搜索、AI Chat,并展示 Citation 和 Agent Trace。 + +范涵宇:Agent 能调用 Tool、Skill 能加载、Plugin 能注册 Tool、至少两个 Provider 可以切换运行。 + +杨星萱:Markdown 能完成 Block 化索引,FTS5 + Vector + RRF + Reranker 能完成检索,并返回可定位 Citation。 + +### 总体验收: + +```markdown +→ 自动建立本地知识索引 +→ 用户向 Agent 提问 +→ Agent 调用 RAG +→ Retrieval Core 找到 Note Block +→ Provider 生成回答 +→ 前端显示 Citation +→ 点击 Citation +→ 跳转并高亮原笔记 +``` + diff --git a/frontend/index.html b/frontend/index.html new file mode 100644 index 0000000..baa01e3 --- /dev/null +++ b/frontend/index.html @@ -0,0 +1,13 @@ + + + + + + + Notes Agent + + +
+ + + diff --git a/frontend/package.json b/frontend/package.json new file mode 100644 index 0000000..38b8605 --- /dev/null +++ b/frontend/package.json @@ -0,0 +1,21 @@ +{ + "name": "notes-agent-frontend", + "private": true, + "version": "0.1.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vue-tsc -b && vite build", + "preview": "vite preview" + }, + "dependencies": { + "vue": "latest" + }, + "devDependencies": { + "@types/node": "latest", + "@vitejs/plugin-vue": "latest", + "typescript": "~5.9.3", + "vite": "latest", + "vue-tsc": "latest" + } +} diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml new file mode 100644 index 0000000..3240b3f --- /dev/null +++ b/frontend/pnpm-lock.yaml @@ -0,0 +1,719 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + vue: + specifier: latest + version: 3.5.41(typescript@5.9.3) + devDependencies: + '@types/node': + specifier: latest + version: 26.3.0 + '@vitejs/plugin-vue': + specifier: latest + version: 6.0.8(vite@8.2.2(@types/node@26.3.0))(vue@3.5.41(typescript@5.9.3)) + typescript: + specifier: ~5.9.3 + version: 5.9.3 + vite: + specifier: latest + version: 8.2.2(@types/node@26.3.0) + vue-tsc: + specifier: latest + version: 3.3.11(typescript@5.9.3) + +packages: + + '@babel/helper-string-parser@7.29.7': + resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.29.7': + resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.29.8': + resolution: {integrity: sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/types@7.29.8': + resolution: {integrity: sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==} + engines: {node: '>=6.9.0'} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@oxc-project/types@0.146.0': + resolution: {integrity: sha512-XC0QsnnhVe7sLIWmYmdPw7x5P0h4W8vUU3Nv1ySgWXtvCz8NizoAEpGXA0sOYoJQV2Rl13LgURAHQ5cI5ILCSA==} + + '@rolldown/binding-android-arm-eabi@1.2.5': + resolution: {integrity: sha512-DLe/i+l8ynIBY7XEQ191TeZvCoowIGa18R+dIV30GW7DiOtp74i/xX8hs8GUjW5ARV7VZuie3d6AumSmCwbeRA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [android] + + '@rolldown/binding-android-arm64@1.2.5': + resolution: {integrity: sha512-zXcwKlQApYAOELHd8PwKDFkagYF9Wy4e0RJ+0qnzl9Pjnpj75TEG8ufv40p2J7kCEfwZAsNiuzRIyNNMWT38ig==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@rolldown/binding-darwin-arm64@1.2.5': + resolution: {integrity: sha512-dK4QakI42nzWgJT5sm4y4y/O//D4OxM75/cH28RLV+nzIN9AY+YsbuUVrUTjlLjXR6vpyxFbSsbmNuJ6BP9sww==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@rolldown/binding-darwin-x64@1.2.5': + resolution: {integrity: sha512-fqSALaUu1Wjd1nK2uW2kJDWdLCc8lx1IcY+MTY26Aurfdx19anlzhqXOgCFbBFQnlFDTn4TC1/7Nz4Bl2mLP3A==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@rolldown/binding-freebsd-x64@1.2.5': + resolution: {integrity: sha512-/vCnNxlkxs9tKxNDcyWUePpJ/PgTzxIaVhoM5SmG8UV+GR/IcPam4VYxi7GIMo7PSDuNqlJqvprqii9NqqVCMw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@rolldown/binding-linux-arm-gnueabihf@1.2.5': + resolution: {integrity: sha512-abk0NLA519LxRCszmbE0jYKuQ9YPocOXTiOXOo6Yr+YAT95VH+PtqYAjOJvGKt3viEd/x4qzabAlwd5bHOOARg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@rolldown/binding-linux-arm64-gnu@1.2.5': + resolution: {integrity: sha512-Y7eALiJ8lr0M2HH103Js+g7V34wf6snlpZLAsHI90uLhr3PVlNsbFVAXJC9d/V6BnPyKtpSwI+NcB/RLxsQxuA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-arm64-musl@1.2.5': + resolution: {integrity: sha512-xMvZgnbZg4YVnR/AX2b3oOPDTFYJvUVaJg5FedA/LuvexAtXibZQej4cnTkw3rjsJ/ggUROB64TdtETiim+FYA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@rolldown/binding-linux-ppc64-gnu@1.2.5': + resolution: {integrity: sha512-GRjeqTUDHTo5GwntsLaAMcBahG3nlpjftXWZLN73HiYQlhwEowvarFgQnRnQZtIp4keXX7quXFbG38uPZBa2EA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-s390x-gnu@1.2.5': + resolution: {integrity: sha512-vLNTR45F2Uwc8AufkNXPmB4VliaXs+FvcheEogIzOXzO4l+LzieXF5A/TWxLy5HtqpsRCHUfd0lPVrrdgXdLHQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-x64-gnu@1.2.5': + resolution: {integrity: sha512-Mgj59/HTuYeK9Gz2MA+mBWKnHsAgkBSec15ZMb1st3oIfFbX7gCjOae7GydHhzcyQi9Z/7M1QuN9bR3oFqF0jQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-x64-musl@1.2.5': + resolution: {integrity: sha512-mY8AP0/ichsbhAxGnLa3d3+MwV0EfgrPND2bplI3Ym8T6R2pJ0N87bvrKVwNXmdy3jnr6eQBecdqx/HMknBmpA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@rolldown/binding-openharmony-arm64@1.2.5': + resolution: {integrity: sha512-8SLssA2oweAxyRgDp789ACfRb/3P+zNRJpzZxSizxF9m8NUDQ4+3xjo8ttjhVGGw6Qxb70oZiEtIjaKikCO7Yw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@rolldown/binding-win32-arm64-msvc@1.2.5': + resolution: {integrity: sha512-vGbruD5zquhoc8D9SViXgN2FBJtNdTyQ4DtG+SWiEGlJiAzoKcZ2xp+xuXCffhubVdt0NJlTZqkeRuERy7g8Cw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@rolldown/binding-win32-x64-msvc@1.2.5': + resolution: {integrity: sha512-e/SXpgISz+IoqVcSSI0rx/d/he8zqLex+/rCWpnHpmVfmPIUjag9H6P7zotf0gJHwPUhQxZ/mF8tr6acebT9yw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@rolldown/pluginutils@1.0.1': + resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==} + + '@types/node@26.3.0': + resolution: {integrity: sha512-L3fgrnchriRC2ExBflb8j4uZZURHZfQsmQeyVzhjcHW4kkwVyo8/0h1B2MVzMTrYUJYu6G7EWs14hW/L9putqw==} + + '@vitejs/plugin-vue@6.0.8': + resolution: {integrity: sha512-0ZjgOg7oO6farnNGup7yvoM/YXZV84OZxHAwtflItNa/6zzQyVb5LNxyea3FEKEX2XlagIKzrlH7wwxkKgtiew==} + engines: {node: ^20.19.0 || >=22.12.0} + peerDependencies: + vite: ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 + vue: ^3.2.25 + + '@volar/language-core@2.4.28': + resolution: {integrity: sha512-w4qhIJ8ZSitgLAkVay6AbcnC7gP3glYM3fYwKV3srj8m494E3xtrCv6E+bWviiK/8hs6e6t1ij1s2Endql7vzQ==} + + '@volar/source-map@2.4.28': + resolution: {integrity: sha512-yX2BDBqJkRXfKw8my8VarTyjv48QwxdJtvRgUpNE5erCsgEUdI2DsLbpa+rOQVAJYshY99szEcRDmyHbF10ggQ==} + + '@volar/typescript@2.4.28': + resolution: {integrity: sha512-Ja6yvWrbis2QtN4ClAKreeUZPVYMARDYZl9LMEv1iQ1QdepB6wn0jTRxA9MftYmYa4DQ4k/DaSZpFPUfxl8giw==} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@vue/compiler-core@3.5.41': + resolution: {integrity: sha512-q0Xtv/F9w2YO/7htQhtiL+Ev2WCJbe5N2hc+XfgyKkEKqWpSxknmT8QOuGdEKNdjPq0c3F7rNpFkTo3Kfrm7pg==} + + '@vue/compiler-dom@3.5.41': + resolution: {integrity: sha512-oKacVfNglLvGjnS6BXOlGL7EyG2h8X03pqXCjzotRZUaXGjbrTJUnVAQjrCqUnS+lyu31nwQjZY/d817GmCnfw==} + + '@vue/compiler-sfc@3.5.41': + resolution: {integrity: sha512-XJhip7R2wy6vX3knCxdZN4KracFaZUef58s1KYewqluedHIJaPIVfXoYT7MF1F8nCvv6k8bWWxDC8opMkg1VTQ==} + + '@vue/compiler-ssr@3.5.41': + resolution: {integrity: sha512-U3v5OejKEGqOI0Wy0+Sz7hGuIFZHA4LSXzrNM3IMIeDyJEBBfTpX26n3SDgToRpP2bLc9FfI2j/kSgcJ8Emq5A==} + + '@vue/language-core@3.3.11': + resolution: {integrity: sha512-QJmpliwAVpC/OxubIByPAhNzsQPRc8/gxlN2qnVzVfIMjMDz/9RnXRFoetjz5yEgXVXyp4LqhXq3V53PjmNzFw==} + + '@vue/reactivity@3.5.41': + resolution: {integrity: sha512-rznsqKM0np0x18EjzF8x88MpEhdNsffbvFbckLL5+oUKz1BxAImEmO7J1ArRYSyo6aQaVoBDp7jEkT91OOxydA==} + + '@vue/runtime-core@3.5.41': + resolution: {integrity: sha512-Vcry58hiAKwGen9Z1jUZE0feFsNArPCMOImYI8el48A9Idf6DuQYD0U05zZIF2Iad1hGhPSvcbBbAOhNr55fhg==} + + '@vue/runtime-dom@3.5.41': + resolution: {integrity: sha512-3vVBahVBS9+U6cmXBLyb8nE6/yYo4J/CGI9eVFs3KiMc0YHuudwKyShTD65jtJy/L9PUUxNAFu4cj4LiJ0UFbw==} + + '@vue/server-renderer@3.5.41': + resolution: {integrity: sha512-n6hx/pNFfbD6SuyeuMVkvqox8bwf/ET9JlA/kAz/imw8sw++wkqKe2mHX5KutjPpbKE4Z56yTHszoOjGMI9igQ==} + + '@vue/shared@3.5.41': + resolution: {integrity: sha512-IOnwSCma8j+9xJT6b8H0dEYidC80NsYmNMlZxRsukYcSoGaDBohog5hDxzeUXdFeGWFA++vWvxqOmrr96VlqMA==} + + alien-signals@3.2.1: + resolution: {integrity: sha512-I8FjmltrfnDFoZedi5CG8DghVYNhzb/Ijluz7tCSJH0xpd0484Kowhbb1XDYOxfJpU1p5wnM2X54dA+IfGyD1g==} + + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + + entities@7.0.1: + resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==} + engines: {node: '>=0.12'} + + estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + lightningcss-android-arm64@1.33.0: + resolution: {integrity: sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] + + lightningcss-darwin-arm64@1.33.0: + resolution: {integrity: sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.33.0: + resolution: {integrity: sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.33.0: + resolution: {integrity: sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.33.0: + resolution: {integrity: sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.33.0: + resolution: {integrity: sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + lightningcss-linux-arm64-musl@1.33.0: + resolution: {integrity: sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + + lightningcss-linux-x64-gnu@1.33.0: + resolution: {integrity: sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + + lightningcss-linux-x64-musl@1.33.0: + resolution: {integrity: sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [musl] + + lightningcss-win32-arm64-msvc@1.33.0: + resolution: {integrity: sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.33.0: + resolution: {integrity: sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.33.0: + resolution: {integrity: sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==} + engines: {node: '>= 12.0.0'} + + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + + muggle-string@0.4.1: + resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==} + + nanoid@3.3.18: + resolution: {integrity: sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + path-browserify@1.0.1: + resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@4.0.7: + resolution: {integrity: sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==} + engines: {node: '>=12'} + + postcss@8.5.26: + resolution: {integrity: sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==} + engines: {node: ^10 || ^12 || >=14} + + rolldown@1.2.5: + resolution: {integrity: sha512-VD2IE5PUG4Oj8zz2VGykiYd5wbnjdIiSsNQb8Qu5B+noEp+A78mu2iVvpp27g8es14Tk9rofNs5Tku9iQCS4fA==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + tinyglobby@0.2.17: + resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} + engines: {node: '>=12.0.0'} + + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + + undici-types@8.3.0: + resolution: {integrity: sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==} + + vite@8.2.2: + resolution: {integrity: sha512-cFKLV/PRgAUlIRm5WjMjJ86jrftzpqcgH+Us+DS8mI3CDNiH30Whrz8uHL3+MOLPAgqbMBAqWdAHAphOAM+z/Q==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + '@vitejs/devtools': ^0.4.0 || ^0.5.0 + esbuild: ^0.27.0 || ^0.28.0 + jiti: '>=1.21.0' + less: ^4.0.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + '@vitejs/devtools': + optional: true + esbuild: + optional: true + jiti: + optional: true + less: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + vscode-uri@3.2.0: + resolution: {integrity: sha512-m2gXo3bn0G1kT9InzMf07fTbqMbGtyckj3bH5ktLO+1Ssv+yiATZ4dhwaQv9UZWxJh6E9IFGnQyjgWVDWVBDrg==} + + vue-tsc@3.3.11: + resolution: {integrity: sha512-gOb0B9rtU2+f1dszwPqSH5kAieIF9ReeLhD3kSRNHv5WZZUQz/JdVXW0RTdqhNTMlQkqKzrTTviqKr/4FYZraQ==} + hasBin: true + peerDependencies: + typescript: '>=5.0.0' + + vue@3.5.41: + resolution: {integrity: sha512-2laE0p+aK+/AOPG/XL/WepOs/GlK755LJ1XECi9kDUrz1FKNw8rb2Xzlw9JS1rqEV55nb0ttsKxVlTCcd+R5cg==} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + +snapshots: + + '@babel/helper-string-parser@7.29.7': {} + + '@babel/helper-validator-identifier@7.29.7': {} + + '@babel/parser@7.29.8': + dependencies: + '@babel/types': 7.29.8 + + '@babel/types@7.29.8': + dependencies: + '@babel/helper-string-parser': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@oxc-project/types@0.146.0': {} + + '@rolldown/binding-android-arm-eabi@1.2.5': + optional: true + + '@rolldown/binding-android-arm64@1.2.5': + optional: true + + '@rolldown/binding-darwin-arm64@1.2.5': + optional: true + + '@rolldown/binding-darwin-x64@1.2.5': + optional: true + + '@rolldown/binding-freebsd-x64@1.2.5': + optional: true + + '@rolldown/binding-linux-arm-gnueabihf@1.2.5': + optional: true + + '@rolldown/binding-linux-arm64-gnu@1.2.5': + optional: true + + '@rolldown/binding-linux-arm64-musl@1.2.5': + optional: true + + '@rolldown/binding-linux-ppc64-gnu@1.2.5': + optional: true + + '@rolldown/binding-linux-s390x-gnu@1.2.5': + optional: true + + '@rolldown/binding-linux-x64-gnu@1.2.5': + optional: true + + '@rolldown/binding-linux-x64-musl@1.2.5': + optional: true + + '@rolldown/binding-openharmony-arm64@1.2.5': + optional: true + + '@rolldown/binding-win32-arm64-msvc@1.2.5': + optional: true + + '@rolldown/binding-win32-x64-msvc@1.2.5': + optional: true + + '@rolldown/pluginutils@1.0.1': {} + + '@types/node@26.3.0': + dependencies: + undici-types: 8.3.0 + + '@vitejs/plugin-vue@6.0.8(vite@8.2.2(@types/node@26.3.0))(vue@3.5.41(typescript@5.9.3))': + dependencies: + '@rolldown/pluginutils': 1.0.1 + vite: 8.2.2(@types/node@26.3.0) + vue: 3.5.41(typescript@5.9.3) + + '@volar/language-core@2.4.28': + dependencies: + '@volar/source-map': 2.4.28 + + '@volar/source-map@2.4.28': {} + + '@volar/typescript@2.4.28(typescript@5.9.3)': + dependencies: + '@volar/language-core': 2.4.28 + path-browserify: 1.0.1 + vscode-uri: 3.2.0 + optionalDependencies: + typescript: 5.9.3 + + '@vue/compiler-core@3.5.41': + dependencies: + '@babel/parser': 7.29.8 + '@vue/shared': 3.5.41 + entities: 7.0.1 + estree-walker: 2.0.2 + source-map-js: 1.2.1 + + '@vue/compiler-dom@3.5.41': + dependencies: + '@vue/compiler-core': 3.5.41 + '@vue/shared': 3.5.41 + + '@vue/compiler-sfc@3.5.41': + dependencies: + '@babel/parser': 7.29.8 + '@vue/compiler-core': 3.5.41 + '@vue/compiler-dom': 3.5.41 + '@vue/compiler-ssr': 3.5.41 + '@vue/shared': 3.5.41 + estree-walker: 2.0.2 + magic-string: 0.30.21 + postcss: 8.5.26 + source-map-js: 1.2.1 + + '@vue/compiler-ssr@3.5.41': + dependencies: + '@vue/compiler-dom': 3.5.41 + '@vue/shared': 3.5.41 + + '@vue/language-core@3.3.11': + dependencies: + '@volar/language-core': 2.4.28 + '@vue/compiler-dom': 3.5.41 + '@vue/shared': 3.5.41 + alien-signals: 3.2.1 + muggle-string: 0.4.1 + path-browserify: 1.0.1 + picomatch: 4.0.7 + + '@vue/reactivity@3.5.41': + dependencies: + '@vue/shared': 3.5.41 + + '@vue/runtime-core@3.5.41': + dependencies: + '@vue/reactivity': 3.5.41 + '@vue/shared': 3.5.41 + + '@vue/runtime-dom@3.5.41': + dependencies: + '@vue/reactivity': 3.5.41 + '@vue/runtime-core': 3.5.41 + '@vue/shared': 3.5.41 + csstype: 3.2.3 + + '@vue/server-renderer@3.5.41': + dependencies: + '@vue/compiler-ssr': 3.5.41 + '@vue/runtime-dom': 3.5.41 + '@vue/shared': 3.5.41 + + '@vue/shared@3.5.41': {} + + alien-signals@3.2.1: {} + + csstype@3.2.3: {} + + detect-libc@2.1.2: {} + + entities@7.0.1: {} + + estree-walker@2.0.2: {} + + fdir@6.5.0(picomatch@4.0.7): + optionalDependencies: + picomatch: 4.0.7 + + fsevents@2.3.3: + optional: true + + lightningcss-android-arm64@1.33.0: + optional: true + + lightningcss-darwin-arm64@1.33.0: + optional: true + + lightningcss-darwin-x64@1.33.0: + optional: true + + lightningcss-freebsd-x64@1.33.0: + optional: true + + lightningcss-linux-arm-gnueabihf@1.33.0: + optional: true + + lightningcss-linux-arm64-gnu@1.33.0: + optional: true + + lightningcss-linux-arm64-musl@1.33.0: + optional: true + + lightningcss-linux-x64-gnu@1.33.0: + optional: true + + lightningcss-linux-x64-musl@1.33.0: + optional: true + + lightningcss-win32-arm64-msvc@1.33.0: + optional: true + + lightningcss-win32-x64-msvc@1.33.0: + optional: true + + lightningcss@1.33.0: + dependencies: + detect-libc: 2.1.2 + optionalDependencies: + lightningcss-android-arm64: 1.33.0 + lightningcss-darwin-arm64: 1.33.0 + lightningcss-darwin-x64: 1.33.0 + lightningcss-freebsd-x64: 1.33.0 + lightningcss-linux-arm-gnueabihf: 1.33.0 + lightningcss-linux-arm64-gnu: 1.33.0 + lightningcss-linux-arm64-musl: 1.33.0 + lightningcss-linux-x64-gnu: 1.33.0 + lightningcss-linux-x64-musl: 1.33.0 + lightningcss-win32-arm64-msvc: 1.33.0 + lightningcss-win32-x64-msvc: 1.33.0 + + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + muggle-string@0.4.1: {} + + nanoid@3.3.18: {} + + path-browserify@1.0.1: {} + + picocolors@1.1.1: {} + + picomatch@4.0.7: {} + + postcss@8.5.26: + dependencies: + nanoid: 3.3.18 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + rolldown@1.2.5: + dependencies: + '@oxc-project/types': 0.146.0 + '@rolldown/pluginutils': 1.0.1 + optionalDependencies: + '@rolldown/binding-android-arm-eabi': 1.2.5 + '@rolldown/binding-android-arm64': 1.2.5 + '@rolldown/binding-darwin-arm64': 1.2.5 + '@rolldown/binding-darwin-x64': 1.2.5 + '@rolldown/binding-freebsd-x64': 1.2.5 + '@rolldown/binding-linux-arm-gnueabihf': 1.2.5 + '@rolldown/binding-linux-arm64-gnu': 1.2.5 + '@rolldown/binding-linux-arm64-musl': 1.2.5 + '@rolldown/binding-linux-ppc64-gnu': 1.2.5 + '@rolldown/binding-linux-s390x-gnu': 1.2.5 + '@rolldown/binding-linux-x64-gnu': 1.2.5 + '@rolldown/binding-linux-x64-musl': 1.2.5 + '@rolldown/binding-openharmony-arm64': 1.2.5 + '@rolldown/binding-win32-arm64-msvc': 1.2.5 + '@rolldown/binding-win32-x64-msvc': 1.2.5 + + source-map-js@1.2.1: {} + + tinyglobby@0.2.17: + dependencies: + fdir: 6.5.0(picomatch@4.0.7) + picomatch: 4.0.7 + + typescript@5.9.3: {} + + undici-types@8.3.0: {} + + vite@8.2.2(@types/node@26.3.0): + dependencies: + lightningcss: 1.33.0 + picomatch: 4.0.7 + postcss: 8.5.26 + rolldown: 1.2.5 + tinyglobby: 0.2.17 + optionalDependencies: + '@types/node': 26.3.0 + fsevents: 2.3.3 + + vscode-uri@3.2.0: {} + + vue-tsc@3.3.11(typescript@5.9.3): + dependencies: + '@volar/typescript': 2.4.28(typescript@5.9.3) + '@vue/language-core': 3.3.11 + typescript: 5.9.3 + + vue@3.5.41(typescript@5.9.3): + dependencies: + '@vue/compiler-dom': 3.5.41 + '@vue/compiler-sfc': 3.5.41 + '@vue/runtime-dom': 3.5.41 + '@vue/server-renderer': 3.5.41 + '@vue/shared': 3.5.41 + optionalDependencies: + typescript: 5.9.3 diff --git a/frontend/src/App.vue b/frontend/src/App.vue new file mode 100644 index 0000000..02cbee3 --- /dev/null +++ b/frontend/src/App.vue @@ -0,0 +1,95 @@ + + + diff --git a/frontend/src/api.ts b/frontend/src/api.ts new file mode 100644 index 0000000..ea07804 --- /dev/null +++ b/frontend/src/api.ts @@ -0,0 +1,16 @@ +export interface ServiceStatus { + name: string + version: string + environment: string + status: 'ok' +} + +const apiBaseUrl = import.meta.env.VITE_API_BASE_URL ?? '' + +export async function getServiceStatus(): Promise { + const response = await fetch(`${apiBaseUrl}/api/status`) + if (!response.ok) { + throw new Error(`后端请求失败:HTTP ${response.status}`) + } + return response.json() as Promise +} diff --git a/frontend/src/env.d.ts b/frontend/src/env.d.ts new file mode 100644 index 0000000..11f02fe --- /dev/null +++ b/frontend/src/env.d.ts @@ -0,0 +1 @@ +/// diff --git a/frontend/src/main.ts b/frontend/src/main.ts new file mode 100644 index 0000000..fe5bae3 --- /dev/null +++ b/frontend/src/main.ts @@ -0,0 +1,5 @@ +import { createApp } from 'vue' +import App from './App.vue' +import './style.css' + +createApp(App).mount('#app') diff --git a/frontend/src/style.css b/frontend/src/style.css new file mode 100644 index 0000000..cb30731 --- /dev/null +++ b/frontend/src/style.css @@ -0,0 +1,70 @@ +:root { + font-family: Inter, "PingFang SC", "Microsoft YaHei", system-ui, sans-serif; + color: #20211f; + background: #f4f1e9; + font-synthesis: none; + text-rendering: optimizeLegibility; + --ink: #20211f; + --muted: #77776f; + --paper: #fffdf7; + --line: #dedbd0; + --accent: #e76f3d; + --success: #307b59; +} + +* { box-sizing: border-box; } +body { margin: 0; min-width: 320px; min-height: 100vh; } +button { font: inherit; } + +.app-shell { display: grid; grid-template-columns: 240px 1fr; min-height: 100vh; } +.sidebar { + display: flex; flex-direction: column; padding: 28px 20px; + color: #f8f5ed; background: #20211f; +} +.brand { display: flex; align-items: center; gap: 12px; margin-bottom: 48px; font-weight: 700; } +.brand-mark { + display: grid; place-items: center; width: 32px; height: 32px; + border-radius: 10px; color: #20211f; background: #f2c14e; +} +nav { display: grid; gap: 6px; } +.nav-item { + padding: 11px 14px; border: 0; border-radius: 8px; text-align: left; + color: #bcbdb7; background: transparent; +} +.nav-item.active { color: white; background: #343632; } +.nav-item:disabled { cursor: not-allowed; opacity: .55; } +.sidebar-hint { margin-top: auto; color: #8f918a; font-size: 13px; } + +.workspace { padding: 64px clamp(28px, 6vw, 88px); } +.workspace header { max-width: 720px; margin-bottom: 42px; } +.eyebrow, .card-label { margin: 0 0 10px; color: var(--accent); font-size: 12px; font-weight: 800; letter-spacing: .14em; } +h1 { margin: 0; font-family: Georgia, "Noto Serif SC", serif; font-size: clamp(42px, 7vw, 76px); line-height: 1; letter-spacing: -.045em; } +.subtitle { max-width: 580px; margin: 20px 0 0; color: var(--muted); font-size: 17px; line-height: 1.7; } +.cards { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 18px; max-width: 960px; } +.card { min-height: 180px; padding: 28px; border: 1px solid var(--line); border-radius: 18px; background: var(--paper); box-shadow: 0 12px 40px rgb(46 43 36 / 6%); } +.hero-card { grid-column: 1 / -1; display: grid; grid-template-columns: 1fr auto; gap: 22px; align-items: center; } +.card h2 { margin: 0 0 12px; font-size: 21px; } +.card p { color: var(--muted); line-height: 1.6; } +.status-line { display: flex; align-items: center; gap: 12px; min-width: 250px; } +.status-line p { margin: 3px 0 0; font-size: 13px; } +.status-dot { width: 10px; height: 10px; border-radius: 50%; background: #aaa; box-shadow: 0 0 0 5px rgb(120 120 120 / 10%); } +.status-line.success .status-dot { background: var(--success); box-shadow: 0 0 0 5px rgb(48 123 89 / 12%); } +.status-line.error .status-dot { background: #b84737; box-shadow: 0 0 0 5px rgb(184 71 55 / 12%); } +.primary-button { + justify-self: end; padding: 10px 16px; border: 0; border-radius: 9px; + color: white; background: var(--ink); cursor: pointer; +} +.primary-button:disabled { cursor: wait; opacity: .6; } + +@media (max-width: 720px) { + .app-shell { grid-template-columns: 1fr; } + .sidebar { min-height: auto; padding: 18px 20px; } + .brand { margin-bottom: 18px; } + nav { grid-template-columns: repeat(4, 1fr); } + .nav-item { padding: 9px 6px; text-align: center; font-size: 13px; } + .sidebar-hint { display: none; } + .workspace { padding-top: 42px; } + .cards { grid-template-columns: 1fr; } + .hero-card { display: grid; grid-column: auto; } + .primary-button { justify-self: start; } +} diff --git a/frontend/tsconfig.app.json b/frontend/tsconfig.app.json new file mode 100644 index 0000000..4d16005 --- /dev/null +++ b/frontend/tsconfig.app.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "target": "ES2022", + "useDefineForClassFields": true, + "module": "ESNext", + "moduleResolution": "Bundler", + "strict": true, + "jsx": "preserve", + "resolveJsonModule": true, + "isolatedModules": true, + "esModuleInterop": true, + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "types": ["vite/client"], + "noEmit": true + }, + "include": ["src/**/*.ts", "src/**/*.vue"] +} diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json new file mode 100644 index 0000000..1ffef60 --- /dev/null +++ b/frontend/tsconfig.json @@ -0,0 +1,7 @@ +{ + "files": [], + "references": [ + { "path": "./tsconfig.app.json" }, + { "path": "./tsconfig.node.json" } + ] +} diff --git a/frontend/tsconfig.node.json b/frontend/tsconfig.node.json new file mode 100644 index 0000000..77a18fc --- /dev/null +++ b/frontend/tsconfig.node.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "composite": true, + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "Bundler", + "allowImportingTsExtensions": true, + "noEmit": true, + "types": ["node"] + }, + "include": ["vite.config.ts"] +} diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts new file mode 100644 index 0000000..baaeb98 --- /dev/null +++ b/frontend/vite.config.ts @@ -0,0 +1,14 @@ +import { defineConfig } from 'vite' +import vue from '@vitejs/plugin-vue' + +export default defineConfig({ + plugins: [vue()], + server: { + host: '127.0.0.1', + port: 5173, + proxy: { + '/api': 'http://127.0.0.1:8000', + '/health': 'http://127.0.0.1:8000', + }, + }, +})