diff --git a/README.md b/README.md
index ff6d2e4..c327b5b 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
# NeoCompanion
-[](#)
-[](#)
+[](#)
+[](#)
[](#)
> 一个融入桌面的 AI 助手——状态融入壁纸,轻交互近在手边,编辑留给面板。
@@ -92,7 +92,7 @@ echo '{"state":"success","description":"Build pass"}' > ~/.NeoCompanion/hooks/bu
### 🔒 本地优先,隐私可控
-所有数据存储在本地,不上传云端。窗口检测可开关,应用黑名单可配置,发送给 AI 的内容可审查。不需要注册账户。
+业务数据和索引默认存储在本地,不需要注册账户。窗口检测可开关,应用黑名单可配置;只有用户启用云端 Chat 或 Embedding Provider 后,对应的问答上下文或待向量化文本才会发送给该服务,并在设置中明确展示边界。
---
@@ -106,7 +106,7 @@ NeoCompanion 的能力由浅入深分为四层:
|-------------------------------------------------------|
| 2. Routine Layer (番茄钟 / 待办清单 / 天气 / 助手日志) |
|-------------------------------------------------------|
-| 3. Gateway Layer (内置原子能力 / OpenClaw / LLM 对话) |
+| 3. Knowledge & AI (项目 / 笔记 / 看板 / 混合检索 / AI) |
|-------------------------------------------------------|
| 4. Hook & System (安全 Hook API / 本地状态感知与记忆) |
+-------------------------------------------------------+
@@ -116,7 +116,7 @@ NeoCompanion 的能力由浅入深分为四层:
**日常琐碎陪伴层**——陪伴式番茄钟、助手待办清单、天气碎碎念、助手工作日志。
-**智能网关与对话层**——内置基础 Agent(文件读写、网页搜索、剪贴板获取);复杂跨软件任务委托给 [OpenClaw](https://github.com/openclaw/openclaw);日常提问直连大模型 API。
+**知识与 AI 层**——在单一本地工作空间中组织项目、Markdown 笔记、任务与看板;通过全文检索和向量检索为 AI 对话提供可核验的本地上下文。
**Hook 与系统层**——安全本地多通道 Hook(HTTP / UDS / File Watcher / MQTT);浮动权限审批气泡;本地隐私感知引擎;本地长期记忆。
@@ -128,13 +128,13 @@ NeoCompanion 的能力由浅入深分为四层:
桌面常驻悬浮助手的基本 2D 精灵图形态、语音 TTS 播报。助手番茄钟、待办、天气碎碎念。壁纸层 MVP(天气时间 + 专注主控盘 + 热区轻交互)。Hook 角标通知。
-### 🔌 v2:开放 Hook 极客生态
+### 📚 v2:本地知识工作空间
-安全本地 Hook API(HTTP + File Watcher 多通道推送)。零端口 UDS / Named Pipe 本地挂载。浮动权限审批气泡 + 全局热键(`Ctrl+Shift+Y` / `Ctrl+Shift+N`)。AI 对话面板完善(一键拾取剪贴板报错)。OpenClaw 配置自动打桩。
+项目、Markdown 笔记、统一任务与看板形成可用闭环。SQLite FTS5 提供本地全文搜索,`sqlite-vec` 提供可选向量检索;AI 回答展示可点击来源。Embedding 未配置或失败时自动降级为全文搜索。通用 Hook API 与权限审批能力继续独立演进。
-### 🧠 v3:完整智能网关与分布式远程挂载
+### 🧠 v3:知识增强的长期陪伴
-分布式远程宿主挂载(将 LanceDB、SQLite 托管至 NAS / 云服务器)。深度打通 OpenClaw 跨软件执行网关。助手性格记忆演进,形成有长期偏好的情感交互。
+增加本地文件夹同步、多格式导入和本地 embedding 模型;将用户主动维护的知识与助手长期记忆分开治理,并在明确授权下形成有来源、可追溯的个性化交互。
---
@@ -145,10 +145,10 @@ NeoCompanion 的能力由浅入深分为四层:
| 桌面运行时 | **Tauri v2** (Rust) |
| 前端 UI | **Vue 3** + Vite + Pinia + TanStack Query |
| 本地服务 | **Fastify** (TypeScript Sidecar) |
-| 数据库 | **SQLite** (Drizzle ORM) + 向量检索 (hnswlib-wasm / LanceDB) |
-| AI | 多模型适配器 (DeepSeek / OpenAI / Claude),用户可自定义 |
+| 数据库 | **SQLite** (Drizzle ORM + FTS5) + **sqlite-vec** |
+| AI | 聊天模型适配器 + OpenAI-compatible Embedding Adapter |
-架构核心:Tauri (Rust) 提供系统级能力 → Fastify (TypeScript) 处理业务逻辑和 AI 调度 → Vue 提供 UI → SQLite + 向量检索本地存储全部数据。
+架构核心:Tauri (Rust) 提供系统级能力 → Fastify (TypeScript) 处理业务逻辑、索引与 AI 调度 → Vue 提供 UI → SQLite 统一存储业务数据、全文索引和向量索引。
详见 [**系统架构设计**](docs/ARCHITECTURE.md)。
diff --git a/apps/desktop/package.json b/apps/desktop/package.json
index d5b149e..a2158fc 100644
--- a/apps/desktop/package.json
+++ b/apps/desktop/package.json
@@ -13,11 +13,24 @@
"dependencies": {
"@neo-companion/shared": "workspace:*",
"@tauri-apps/api": "^2.9.0",
+ "@tiptap/core": "3.26.0",
+ "@tiptap/extension-heading": "3.26.0",
+ "@tiptap/extension-list": "3.26.0",
+ "@tiptap/extensions": "3.26.0",
+ "@tiptap/markdown": "3.26.0",
+ "@tiptap/pm": "3.26.0",
+ "@tiptap/starter-kit": "3.26.0",
+ "@tiptap/suggestion": "3.26.0",
+ "@tiptap/vue-3": "3.26.0",
+ "marked": "^17.0.6",
+ "textarea-caret": "^3.1.0",
"vue": "3.5.34"
},
"devDependencies": {
"@tauri-apps/cli": "2.11.2",
+ "@types/textarea-caret": "^3.0.3",
"@vitejs/plugin-vue": "^6.0.2",
+ "jsdom": "^29.1.1",
"typescript": "6.0.3",
"vite": "8.0.14",
"vitest": "4.1.7",
diff --git a/apps/desktop/src-tauri/capabilities/default.json b/apps/desktop/src-tauri/capabilities/default.json
index 73aced1..f76d964 100644
--- a/apps/desktop/src-tauri/capabilities/default.json
+++ b/apps/desktop/src-tauri/capabilities/default.json
@@ -5,6 +5,13 @@
"windows": ["main", "panel", "wallpaper", "settings"],
"permissions": [
"core:default",
+ "core:window:allow-hide",
+ "core:window:allow-primary-monitor",
+ "core:window:allow-set-focus",
+ "core:window:allow-set-position",
+ "core:window:allow-set-size",
+ "core:window:allow-show",
+ "core:window:allow-start-dragging",
"opener:default",
"window-state:default"
]
diff --git a/apps/desktop/src-tauri/capabilities/windows-wallpaper.json b/apps/desktop/src-tauri/capabilities/windows-wallpaper.json
index ee75dad..40b87d1 100644
--- a/apps/desktop/src-tauri/capabilities/windows-wallpaper.json
+++ b/apps/desktop/src-tauri/capabilities/windows-wallpaper.json
@@ -3,7 +3,7 @@
"identifier": "windows-wallpaper",
"description": "Wallpaper plugin — Windows only",
"platforms": ["windows"],
- "windows": ["wallpaper"],
+ "windows": ["main", "wallpaper"],
"permissions": [
"wallpaper:default"
]
diff --git a/apps/desktop/src-tauri/tauri.conf.json b/apps/desktop/src-tauri/tauri.conf.json
index 8f8f81a..5f8477c 100644
--- a/apps/desktop/src-tauri/tauri.conf.json
+++ b/apps/desktop/src-tauri/tauri.conf.json
@@ -71,6 +71,22 @@
"skipTaskbar": false,
"visible": false,
"center": true
+ },
+ {
+ "title": "NeoCompanion 知识工作空间",
+ "label": "knowledge",
+ "url": "/?view=knowledge",
+ "width": 1280,
+ "height": 820,
+ "minWidth": 1024,
+ "minHeight": 680,
+ "transparent": false,
+ "decorations": true,
+ "alwaysOnTop": false,
+ "resizable": true,
+ "skipTaskbar": false,
+ "visible": false,
+ "center": true
}
],
"security": {
diff --git a/apps/desktop/src/App.vue b/apps/desktop/src/App.vue
index 307c312..ed65e78 100644
--- a/apps/desktop/src/App.vue
+++ b/apps/desktop/src/App.vue
@@ -21,7 +21,7 @@ import TopNav from "./components/panel/TopNav.vue";
import CreateFocusCard from "./components/panel/cards/CreateFocusCard.vue";
import TodayTasksCard from "./components/panel/cards/TodayTasksCard.vue";
import WeeklyFocusCard from "./components/panel/cards/WeeklyFocusCard.vue";
-import ClawGatewayCard from "./components/panel/cards/ClawGatewayCard.vue";
+import KnowledgeEntryCard from "./components/panel/cards/KnowledgeEntryCard.vue";
import AssistantStatusCard from "./components/panel/cards/AssistantStatusCard.vue";
import HookNotificationsCard from "./components/panel/cards/HookNotificationsCard.vue";
import AiChatCard from "./components/panel/cards/AiChatCard.vue";
@@ -33,20 +33,22 @@ import PageDots from "./components/panel/PageDots.vue";
import ErrorToast from "./components/shared/ErrorToast.vue";
import WallpaperView from "./views/WallpaperView.vue";
import SettingsView from "./views/SettingsView.vue";
+import KnowledgeView from "./views/KnowledgeView.vue";
import { useSettings } from "./composables/useSettings";
const viewMode = new URLSearchParams(window.location.search).get("view");
const isPetView = viewMode === "pet";
const isWallpaperView = viewMode === "wallpaper";
const isSettingsView = viewMode === "settings";
+const isKnowledgeView = viewMode === "knowledge";
const isTauriRuntime = "__TAURI_INTERNALS__" in window;
-type PanelPage = "focus" | "tasks" | "weekly" | "claw" | "assistant" | "hooks" | "chat" | "diary";
+type PanelPage = "focus" | "tasks" | "weekly" | "knowledge" | "assistant" | "hooks" | "chat" | "diary";
const panelPages: Array<{ id: PanelPage; label: string }> = [
{ id: "focus", label: "专注" },
{ id: "tasks", label: "任务" },
{ id: "weekly", label: "本周" },
- { id: "claw", label: "OpenClaw" },
+ { id: "knowledge", label: "知识库" },
{ id: "assistant", label: "助手" },
{ id: "hooks", label: "Hook" },
{ id: "chat", label: "AI 对话" },
@@ -75,7 +77,7 @@ const contextMenu = ref<{ x: number; y: number } | null>(null);
let disconnectWs: (() => void) | null = null;
onMounted(async () => {
- if (isSettingsView) {
+ if (isSettingsView || isKnowledgeView) {
return;
}
if (isWallpaperView) {
@@ -162,6 +164,13 @@ async function openSettings() {
await settings?.setFocus();
}
+async function openKnowledge() {
+ if (!isTauriRuntime) return;
+ const knowledge = await WebviewWindow.getByLabel("knowledge");
+ await knowledge?.show();
+ await knowledge?.setFocus();
+}
+
async function setWallpaperLayerVisible(visible: boolean) {
if (!isTauriRuntime) return;
@@ -294,6 +303,8 @@ function toggleImmersive() {
+
+
diff --git a/apps/desktop/tests/fixtures/markdown-corpus/preserved/block-html.md b/apps/desktop/tests/fixtures/markdown-corpus/preserved/block-html.md
new file mode 100644
index 0000000..cce1cf1
--- /dev/null
+++ b/apps/desktop/tests/fixtures/markdown-corpus/preserved/block-html.md
@@ -0,0 +1,7 @@
+Paragraph before.
+
+
+
Raw HTML block content
+
+
+Paragraph after.
diff --git a/apps/desktop/tests/fixtures/markdown-corpus/preserved/inline-html.md b/apps/desktop/tests/fixtures/markdown-corpus/preserved/inline-html.md
new file mode 100644
index 0000000..18a229a
--- /dev/null
+++ b/apps/desktop/tests/fixtures/markdown-corpus/preserved/inline-html.md
@@ -0,0 +1 @@
+Use emphasis and a line
break and spans here.
diff --git a/apps/desktop/tests/fixtures/markdown-corpus/preserved/math-block.md b/apps/desktop/tests/fixtures/markdown-corpus/preserved/math-block.md
new file mode 100644
index 0000000..b258e56
--- /dev/null
+++ b/apps/desktop/tests/fixtures/markdown-corpus/preserved/math-block.md
@@ -0,0 +1,7 @@
+Before the math.
+
+$$
+E = mc^2 \cdot \frac{1}{\sqrt{1 - v^2/c^2}}
+$$
+
+After the math.
diff --git a/apps/desktop/tests/fixtures/markdown-corpus/preserved/math-inline.md b/apps/desktop/tests/fixtures/markdown-corpus/preserved/math-inline.md
new file mode 100644
index 0000000..c279931
--- /dev/null
+++ b/apps/desktop/tests/fixtures/markdown-corpus/preserved/math-inline.md
@@ -0,0 +1 @@
+Energy is $E=mc^2$ and the index is $x_1$ in this sentence.
diff --git a/apps/desktop/tests/fixtures/markdown-corpus/preserved/table.md b/apps/desktop/tests/fixtures/markdown-corpus/preserved/table.md
new file mode 100644
index 0000000..62fdd57
--- /dev/null
+++ b/apps/desktop/tests/fixtures/markdown-corpus/preserved/table.md
@@ -0,0 +1,4 @@
+| Name | Role |
+| --- | --- |
+| Ada | Engineer |
+| Grace | Admiral |
diff --git a/apps/desktop/tests/fixtures/markdown-corpus/supported/basic.md b/apps/desktop/tests/fixtures/markdown-corpus/supported/basic.md
new file mode 100644
index 0000000..e7faea1
--- /dev/null
+++ b/apps/desktop/tests/fixtures/markdown-corpus/supported/basic.md
@@ -0,0 +1,7 @@
+# Hello memos
+
+Some **bold**, *italic*, ~~struck~~, and `inline code` text.
+
+> A blockquote with **bold** inside.
+
+A [link](https://example.com/docs) in a sentence.
diff --git a/apps/desktop/tests/fixtures/markdown-corpus/supported/blockquote-mixed.md b/apps/desktop/tests/fixtures/markdown-corpus/supported/blockquote-mixed.md
new file mode 100644
index 0000000..bf06324
--- /dev/null
+++ b/apps/desktop/tests/fixtures/markdown-corpus/supported/blockquote-mixed.md
@@ -0,0 +1,7 @@
+> Outer quote line one.
+> Still the same quote.
+
+Paragraph after.
+
+> - a list in a quote
+> - second item
diff --git a/apps/desktop/tests/fixtures/markdown-corpus/supported/cjk-emoji.md b/apps/desktop/tests/fixtures/markdown-corpus/supported/cjk-emoji.md
new file mode 100644
index 0000000..1ebf4cd
--- /dev/null
+++ b/apps/desktop/tests/fixtures/markdown-corpus/supported/cjk-emoji.md
@@ -0,0 +1,9 @@
+# 日本語の見出し
+
+これは**太字**と*斜体*のテストです。改行も確認します。
+
+- 中文列表项
+- 한국어 항목
+- emoji 🎉 in a list 🚀
+
+> 引用文です 😀
diff --git a/apps/desktop/tests/fixtures/markdown-corpus/supported/code-blocks.md b/apps/desktop/tests/fixtures/markdown-corpus/supported/code-blocks.md
new file mode 100644
index 0000000..421195e
--- /dev/null
+++ b/apps/desktop/tests/fixtures/markdown-corpus/supported/code-blocks.md
@@ -0,0 +1,16 @@
+Some text first.
+
+```go
+func main() {
+ fmt.Println("hello *not italic* #not-a-tag")
+}
+```
+
+```mermaid
+graph TD;
+ A-->B;
+```
+
+```
+no language fence
+```
diff --git a/apps/desktop/tests/fixtures/markdown-corpus/supported/headings.md b/apps/desktop/tests/fixtures/markdown-corpus/supported/headings.md
new file mode 100644
index 0000000..911de0e
--- /dev/null
+++ b/apps/desktop/tests/fixtures/markdown-corpus/supported/headings.md
@@ -0,0 +1,13 @@
+# H1
+
+## H2
+
+### H3
+
+#### H4
+
+##### H5
+
+###### H6
+
+Body under headings.
diff --git a/apps/desktop/tests/fixtures/markdown-corpus/supported/lists.md b/apps/desktop/tests/fixtures/markdown-corpus/supported/lists.md
new file mode 100644
index 0000000..9a9bc0f
--- /dev/null
+++ b/apps/desktop/tests/fixtures/markdown-corpus/supported/lists.md
@@ -0,0 +1,11 @@
+- alpha
+- beta
+ - nested one
+ - nested two
+- gamma
+
+1. first
+2. second
+ 1. second-a
+ 2. second-b
+3. third
diff --git a/apps/desktop/tests/fixtures/markdown-corpus/supported/task-lists.md b/apps/desktop/tests/fixtures/markdown-corpus/supported/task-lists.md
new file mode 100644
index 0000000..ddcd578
--- /dev/null
+++ b/apps/desktop/tests/fixtures/markdown-corpus/supported/task-lists.md
@@ -0,0 +1,8 @@
+- [ ] open task
+- [x] done task
+- [ ] task with **bold** text
+
+Regular paragraph between.
+
+- [ ] another list
+ - [x] nested done
diff --git a/apps/desktop/tests/markdown-roundtrip.test.ts b/apps/desktop/tests/markdown-roundtrip.test.ts
new file mode 100644
index 0000000..92126bc
--- /dev/null
+++ b/apps/desktop/tests/markdown-roundtrip.test.ts
@@ -0,0 +1,50 @@
+import { readdirSync, readFileSync } from "node:fs";
+import { join } from "node:path";
+import { describe, expect, it } from "vitest";
+import {
+ parseMarkdown,
+ roundTripMarkdown,
+} from "../src/components/markdown-editor/editor/markdownCodec";
+
+const CORPUS_DIR = join(__dirname, "fixtures", "markdown-corpus");
+
+function fixtures(group: string): Array<[name: string, source: string]> {
+ const dir = join(CORPUS_DIR, group);
+ return readdirSync(dir)
+ .filter((f) => f.endsWith(".md"))
+ .sort()
+ .map((f) => [f, readFileSync(join(dir, f), "utf8")]);
+}
+
+// Fidelity contract part 1: supported constructs round-trip semantically.
+// "Semantic" = the parsed document tree is identical before and after a
+// serialize cycle; marker style (e.g. `*` vs `-` bullets) may normalize.
+describe("markdown round-trip corpus: supported syntax (semantic equality)", () => {
+ for (const [name, source] of fixtures("supported")) {
+ it(`${name}: parse → serialize → parse is identity`, () => {
+ const serialized = roundTripMarkdown(source);
+ expect(parseMarkdown(serialized)).toEqual(parseMarkdown(source));
+ });
+
+ it(`${name}: serialization is idempotent`, () => {
+ const once = roundTripMarkdown(source);
+ expect(roundTripMarkdown(once)).toBe(once);
+ });
+ }
+});
+
+// Fidelity contract part 2: unmodeled constructs round-trip byte-for-byte.
+// Fixtures in preserved/ contain only preserved constructs + inert prose,
+// so the entire file must survive a round trip unchanged (modulo outer trim).
+describe("markdown round-trip corpus: preserved syntax (byte equality)", () => {
+ for (const [name, source] of fixtures("preserved")) {
+ it(`${name}: round-trips byte-for-byte`, () => {
+ expect(roundTripMarkdown(source).trim()).toBe(source.trim());
+ });
+
+ it(`${name}: serialization is idempotent`, () => {
+ const once = roundTripMarkdown(source);
+ expect(roundTripMarkdown(once)).toBe(once);
+ });
+ }
+});
diff --git a/apps/desktop/tests/setup.ts b/apps/desktop/tests/setup.ts
new file mode 100644
index 0000000..3922e8e
--- /dev/null
+++ b/apps/desktop/tests/setup.ts
@@ -0,0 +1,22 @@
+// jsdom + Tiptap/ProseMirror 兼容垫片:ProseMirror 探测的 layout API jsdom 没实现。
+// 来源参考 wynxing/memos web/tests/setup.ts。
+if (typeof document !== "undefined") {
+ if (!document.elementFromPoint) {
+ document.elementFromPoint = () => null;
+ }
+ if (typeof Range !== "undefined" && !Range.prototype.getClientRects) {
+ Range.prototype.getClientRects = () => [] as unknown as DOMRectList;
+ Range.prototype.getBoundingClientRect = () =>
+ ({
+ x: 0,
+ y: 0,
+ top: 0,
+ left: 0,
+ bottom: 0,
+ right: 0,
+ width: 0,
+ height: 0,
+ toJSON: () => ({}),
+ }) as DOMRect;
+ }
+}
diff --git a/apps/desktop/vitest.config.ts b/apps/desktop/vitest.config.ts
new file mode 100644
index 0000000..2ebb5f6
--- /dev/null
+++ b/apps/desktop/vitest.config.ts
@@ -0,0 +1,13 @@
+import vue from "@vitejs/plugin-vue";
+import { defineConfig } from "vitest/config";
+
+// Test-time configuration. Kept separate from vite.config.ts so the
+// dev/build pipelines stay lean and only tests load jsdom.
+export default defineConfig({
+ plugins: [vue()],
+ test: {
+ environment: "jsdom",
+ setupFiles: ["./tests/setup.ts"],
+ include: ["tests/**/*.test.ts"],
+ },
+});
diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md
index 42c4638..64bbb01 100644
--- a/docs/ARCHITECTURE.md
+++ b/docs/ARCHITECTURE.md
@@ -4,9 +4,9 @@
- **Title**: NeoAssistant System Architecture Document
- **Owner**: Engineering
-- **Status**: Draft (Aligned with Concept v3.0 / PRD v3.2)
-- **Version**: 1.2
-- **Last Updated**: 2026-06-12
+- **Status**: Draft (Aligned with Concept / PRD v3.3)
+- **Version**: 1.3
+- **Last Updated**: 2026-06-18
- **Audience**: Engineering, Product
- **Related Docs**:
- [`README.md`](../README.md)
@@ -37,18 +37,18 @@
### 2.1 本地优先
-NeoAssistant 是一个完全本地运行的桌面应用。
+NeoCompanion 是一个本地优先的桌面应用。
- 数据存储在用户本地;
- 业务逻辑在本地执行;
- 不依赖自建云服务;
-- AI 能力通过外部 LLM API 调用,但上下文组装和结果处理在本地完成;
+- AI 能力通过用户配置的外部 Chat / Embedding API 调用,但上下文组装、索引管理和结果处理在本地完成;
- 用户无需注册账户即可使用(v1)。
### 2.2 隐私可控
-- 敏感信息在本地处理,不上传;
-- 发送至 LLM 的内容用户可审查和过滤;
+- 敏感信息默认在本地处理;
+- 发送至 Chat / Embedding Provider 的内容范围必须在设置中明确说明,并允许用户关闭;
- 屏幕感知、窗口检测等能力基于用户授权;
- 用户可随时关闭任何感知能力;
- 无遥测、无后台上报。
@@ -74,7 +74,7 @@ NeoAssistant 是一个完全本地运行的桌面应用。
### 3.1 High-Level Architecture
-系统提供三种灵活的挂载拓扑,以满足本地低门槛开发、绝对高安全零端口冲突、以及跨物理机算力卸载等多样化极客场景:
+系统提供两种本地挂载拓扑,以满足低门槛开发和零端口通信场景。知识库数据始终由本机 SQLite 管理,远程托管与多端同步不在当前架构范围内。
#### 模式 A:本地 TCP 端口挂载 (默认模式 - Local TCP Port)
*最便利的 Web 级开发与浏览器调试生态,兼容性最佳。*
@@ -84,19 +84,19 @@ NeoAssistant 是一个完全本地运行的桌面应用。
│ │
│ ┌─ Rust Core ─────────────────────────────────────────┐ │
│ │ 窗口检测 │ 应用状态事件 │ 屏幕感知 │ 系统托盘 │ │
-│ │ Sidecar 生命周期管理 │ 文件系统访问 │ LanceDB 向量 │ │
+│ │ Sidecar 生命周期管理 │ 文件系统访问 │ 系统凭据存储 │ │
│ │ Internal HTTP Server (localhost:RUST_PORT) │ │
│ └────────┬───────────────────────────────┬─────────┘ │
│ │ Tauri IPC (invoke / events) │ │
│ ┌─ WebView (Vue) ────────────────────┐ │ │
-│ │ 陪伴 UI │ 对话面板 │ 任务面板 │ 设置│ │ │
+│ │ 陪伴 UI │ 知识库 │ 看板 │ 对话 │ 设置│ │ │
│ │ 壁纸状态层 │ Hook角标 │ │ │
│ │ Pinia (状态) │ TanStack Query │ │ │
│ └──────────────────┬─────────────────┘ │ │
│ │ HTTP (localhost:PORT) │ │
│ ┌─ Fastify Sidecar ──────────────────────┘ │
-│ │ 业务逻辑 │ AI 调度 │ 上下文管道 │ SQLite │
-│ │ Task │ Context │ Memory │ Review │ Settings │
+│ │ 业务逻辑 │ AI 调度 │ 混合检索 │ SQLite │
+│ │ Project │ Note │ Board │ Task │ Knowledge │ Settings │
│ │ ▲ Internal HTTP (localhost:RUST_PORT) → Rust Core │
│ └──────────────────┬─────────────────────────────────┘ │
│ │ │
@@ -124,14 +124,14 @@ NeoAssistant 是一个完全本地运行的桌面应用。
│ (`uds_request` 异步代理转发) (双向穿透 / 零端口) │
│ │ │ │
│ ┌─ WebView (Vue) ────────────────────┐ │ │
-│ │ 助手 UI │ 对话面板 │ 任务面板 │ 设置│ │ │
+│ │ 助手 UI │ 知识库 │ 看板 │ 对话 │ 设置│ │ │
│ │ api-client (UDS Adapter 适配器) │ │ │
│ └────────────────────────────────────┘ │ │
│ ▼ │
│ ┌─ Fastify Sidecar (绑定 UDS / 零端口) ────────────────────┐ │
-│ │ 业务逻辑 │ AI 调度 │ 上下文管道 │ SQLite │ │
+│ │ 业务逻辑 │ AI 调度 │ 混合检索 │ SQLite │ │
│ │ (监听 unix:neo-companion.sock) │ │
-│ │ ▲ 调用系统向量库 → 请求 unix:neo-core.sock (axum 侧) │ │
+│ │ FTS5 + sqlite-vec 均在 Fastify 进程内访问 │ │
│ └──────────────────┬───────────────────────────────────────┘ │
│ │ │
└─────────────────────┼─────────────────────────────────────────┘
@@ -141,53 +141,17 @@ NeoAssistant 是一个完全本地运行的桌面应用。
└────────────────┘
```
-#### 模式 C:分布式远程宿主挂载 (Distributed Remote Hosting)
-*将繁重的 SQLite 读写与 LanceDB 向量检索卸载至 NAS 或私有云服务器,降低工作机负载。*
-
-```
-┌─ Local Client (本地 Tauri 客户端) ───────────────────────────┐
-│ │
-│ ┌─ Rust Core (仅做轻量级本地系统事件抓取) ───────────┐ │
-│ │ 本地窗口检测 │ 系统托盘管理 │ 快捷键拦截 │ │
-│ └────────┬───────────────────────────────────────────┘ │
-│ │ Tauri IPC Event │
-│ ┌────────▼───────────────────────────────────────────┐ │
-│ │ WebView (Vue) │ │
-│ │ api-client (Remote HTTP/WS Adapter + JWT 授权头) │ │
-│ └────────┬───────────────────────────────────────────┘ │
-│ │ │
-└───────────┼───────────────────────────────────────────────────┘
- │
- │ HTTPS + WSS / JWT 握手安全连接 (局域网/公网)
- ▼
-┌─ Remote Server (模式 C - 远程宿主机如 NAS/私有云) ────────────┐
-│ │
-│ ┌─ Fastify Server (远程自持运行服务) ───────────────────────┐ │
-│ │ 业务逻辑主核 │ AI 编排 │ 上下文管道 │ │
-│ │ Drizzle SQLite 关系型数据库 (远程 .db 文件) │ │
-│ │ Node 原生 LanceDB 向量数据库 (本地依赖无编译打包体积限制) │ │
-│ └──────────────────┬───────────────────────────────────────┘ │
-│ │ │
-└─────────────────────┼─────────────────────────────────────────┘
- │ HTTPS (外部)
- ┌─────▼──────────┐
- │ LLM API │
- └────────────────┘
-```
-
----
-
### 3.2 Communication Patterns
-系统采用多挂载统一抽象层,三种模式下各通道的物理路由与网络流向如下表所示:
+系统采用统一传输抽象,两种本地模式下各通道的物理路由如下:
-| 通信路径 | 模式 A (本地 TCP Port) | 模式 B (零端口 UDS Socket) | 模式 C (远程宿主 Hosting) |
-|---|---|---|---|
-| **WebView → Fastify (API 请求)** | HTTP 请求 (`fetch` 协议)
目标: `http://localhost:PORT` | Tauri Rust IPC Bridge
WebView 调 `uds_request`
Rust 转发至 `neo-companion.sock` | HTTPS 请求 (`fetch` 协议)
目标: 远程 NAS URL
附带 JWT 握手鉴权头 |
-| **WebView ↔ Fastify (实时推送)** | 标准 WebSocket 连接
目标: `ws://localhost:PORT/ws` | **Tauri 原生事件广播机制**
无需网络和 WS 代理转换。WebView 通过 `tauri::event::listen` 订阅;Fastify 通过 UDS 向 Rust Core 写入推送,由 Rust Core 原生广播 | 安全 WSS 握手加密连接
目标: `wss://REMOTE_IP/ws`
附带 JWT 安全校验 |
-| **Fastify → Rust Core (系统向量检索)** | **无跨进程向量调用**
本地 Fastify 直接进程内调用 TS/WASM (hnswlib/MiniSearch) 完成向量检索 | **无跨进程向量调用**
本地 Fastify 直接进程内调用 TS/WASM (hnswlib/MiniSearch) 完成向量检索 | **无需跨机调用 Rust Core**
远程 Fastify 直接本地加载并读写 Node 版 `@lancedb/lancedb` |
-| **Rust Core → Fastify (系统事件同步)** | 内部 HTTP 推送
目标: `http://localhost:PORT/events` | UDS 套接字文件直写
目标: `neo-companion.sock` | 本地 WebView 拦截 Rust 事件,通过安全 HTTPS 转推远端服务器 |
-| **外部 Hook 注入 (如 OpenClaw)** | HTTP POST 推送
目标: `/api/hook/push` | **File Watcher Hook** (免网络文件监听)
或直连 `neo-companion.sock` | HTTPS 推送 (带 API Key/JWT)
目标: 远程主机 API |
+| 通信路径 | 模式 A (本地 TCP Port) | 模式 B (零端口 UDS Socket) |
+|---|---|---|
+| **WebView → Fastify (API 请求)** | HTTP 请求 (`fetch`)
目标: `http://localhost:PORT` | Tauri Rust IPC Bridge
WebView 调 `uds_request`
Rust 转发至 `neo-companion.sock` |
+| **WebView ↔ Fastify (实时推送)** | WebSocket
目标: `ws://localhost:PORT/ws` | Tauri 原生事件广播;Fastify 经 UDS 向 Rust Core 写入推送 |
+| **知识检索** | Fastify 进程内访问 SQLite FTS5 与 sqlite-vec | 与模式 A 相同,无跨进程向量调用 |
+| **Rust Core → Fastify (系统事件同步)** | 内部 HTTP 推送 | UDS / Windows Named Pipe |
+| **外部 Hook 注入** | 主动 HTTP POST 到 `/api/hook/push` | File Watcher 或直连本地域套接字 |
**为什么用 WebSocket?**
@@ -274,8 +238,8 @@ export interface TransportProvider {
**通信隔离红线**:
WebView 内部的业务特性与 UI 组件一律严禁直接引用 `ws` 或 `window.WebSocket`,必须统一通过 `useTransport()` 获取 `TransportProvider` 进行数据收发,以确保在 Mode B (UDS / 命名管道物理事件代理桥接) 下能够实现零代码修改的平滑平移与通信隔离。
-##### 2. 后端主服务三种“挂载绑定模式” (Backend Mounting Modes)
-系统支持在设置或环境变量中动态切换不同的后端绑定与托管形态:
+##### 2. 后端主服务绑定模式 (Backend Mounting Modes)
+系统支持在设置或环境变量中切换两种本机绑定形态:
* **A. 本地 TCP 端口挂载 (Local TCP Port - 默认模式)**
* **协议**: Local Loopback TCP (HTTP + WebSocket)
@@ -285,16 +249,10 @@ WebView 内部的业务特性与 UI 组件一律严禁直接引用 `ws` 或 `win
* **协议**: Unix Domain Sockets (UDS) / Windows Named Pipes
* **地址**: macOS/Linux: `/tmp/neo-companion.sock` | Windows: `\\.\pipe\neo-companion`
* **特点**: **100% 避免本地网络端口冲突**,彻底绕过操作系统防火墙(无网络警告弹窗),且进程间通信吞吐率和延迟表现均优于 TCP 回环。
-* **C. 分布式远程宿主挂载 (Distributed Remote Hosting - 算力与漫游)**
- * **协议**: HTTPS + WSS + JWT 握手鉴权
- * **地址**: 用户自定义远程 NAS、局域网服务器或公网主机 URL
- * **特点**: 将繁重的 LanceDB 向量检索、Drizzle SQLite 读写及 OpenClaw GUI 编排卸载到**家用 NAS 或专属服务器**中,极大降低本地工作电脑的 CPU 与内存负载。同时实现跨办公室与家庭多设备的**助手性格、记忆和待办漫游同步**。
-
-
##### 3. 多通道 Hook 挂载机制 (Multi-channel Hook Mounts)
除了标准的 HTTP REST 推送,系统提供更加优雅的第三方 Hook 注入通道:
* **文件系统哨兵挂载 (File Watcher Hook)**:Fastify 侧监听特定本地文件目录(如 `~/.neo-companion/hooks/`)的 JSON 文件覆盖写入。自定义编译/自动化脚本无需调用 curl 网络请求,直接 `echo '...' > hooks/git.json` 即可静默挂载助手动态,极大降低 Hook 编写难度并确保在无网环境下 100% 可用。
-* **标准流管道挂载 (Stdio Pipe Hook)**:在 Tauri 启动外部 Agent(如 OpenClaw)子进程时,直接将其标准输出流(stdout/stderr)挂载并拦截,实现“零端口占用”的状态提取。
+* **标准流管道挂载 (Stdio Pipe Hook)**:对用户显式启动并授权的外部进程,可将其标准输出流(stdout/stderr)转换为通用 Hook 状态;系统不自动发现或修改第三方工具。
* **局域网消息总线挂载 (MQTT / HA Hook)**:支持 Fastify 挂载作为 MQTT 客户端订阅家庭局域网消息队列,实现与实体智能家居/智能传感器的助手状态联动。
### 3.3 Capability Model → Technical Module Mapping
@@ -303,8 +261,8 @@ WebView 内部的业务特性与 UI 组件一律严禁直接引用 `ws` 或 `win
|-----------|----------|--------|
| Assistant Layer | 陪伴 UI + 状态反馈逻辑 | `apps/desktop`, `packages/server-local` |
| Assistant Layer (壁纸状态) | 壁纸层纯状态显示组件 | `apps/desktop` (独立壁纸窗口) |
+| Knowledge & AI Layer | 项目、笔记、看板、统一任务、混合检索与 RAG | `packages/server-local`, `packages/db`, `packages/ai` |
| Context Layer | 上下文管道 + Rust 感知 | `packages/server-local` (v1 内联), `crates/` |
-| Workflow Layer | 任务服务 + AI 辅助 | `packages/server-local`, `packages/ai` |
| Growth Layer | 复盘服务 + 数据分析 | `packages/server-local`, `packages/db` |
| System Layer | Tauri 运行时 + 同步 (v3) | `apps/desktop`, `crates/` |
@@ -331,7 +289,7 @@ neo-companion/
├── packages/
│ ├── server-local/ # Fastify 本地服务 (sidecar)
│ │ ├── src/
-│ │ │ ├── modules/ # 业务模块 (task, context, ai, companion, memory, review, settings)
+│ │ │ ├── modules/ # 业务模块 (project, note, board, task, knowledge, ai, hook, settings)
│ │ │ ├── plugins/ # Fastify 插件
│ │ │ └── index.ts # 入口
│ │ └── package.json
@@ -430,11 +388,11 @@ Rust 侧只负责 WebView 和 Node.js 无法直接完成的系统级能力,并
| Sidecar 管理 | 启动/停止 Fastify 进程、健康检查 | v1 |
| 应用切换事件 | 监听焦点变化、推送事件到 Fastify | v1 |
| UDS/管道 IPC 代理 | **模式 B 下的通信关键**。由于 Webview 浏览器沙箱限制无法直接访问 Unix 套接字与命名管道,由 Rust Core 建立原生 IPC 连接并作为 Bridge 双向透传 WebView 与 Fastify 间的 API 请求与推送事件 | v2 |
-| 子进程 stdio 挂载 | 当启动外部 CLI(如 OpenClaw)时,以管道挂载其标准输出,拦截捕获微观状态 | v2 |
+| 子进程 stdio 挂载 | 对用户显式启动并授权的外部进程,以管道提取通用状态事件 | v2 |
| 屏幕内容获取 | 用户授权下截图/OCR (v2) | v2 |
| 选中文本提取 | 获取用户选中内容 (v2) | v2 |
| 文件系统监听 | 监听指定目录变化 (v2)。在模式 B 中用于挂载文件监听哨兵 (File Watcher) | v2 |
-| 内部系统能力端点 | **模式 A** 下由 Rust Core 暴露 localhost-only 端点安全获取系统钥匙链中加密的 API Key(携带 APP_AUTH_TOKEN 校验,不参与任何向量检索或数据库业务);**模式 B** 下通过本地域套接字 (`unix:neo-core.sock`) 安全通信,实现 100% 绝对零网络端口占用;**模式 C** 下该端点不参与交互。 | v1 |
+| 内部系统能力端点 | **模式 A** 下由 Rust Core 暴露 localhost-only 端点安全获取系统凭据中的 API Key(携带 APP_AUTH_TOKEN 校验,不参与向量检索或数据库业务);**模式 B** 下通过本地域套接字安全通信。 | v1 |
### 5.2 Tauri IPC 接口设计
@@ -681,10 +639,13 @@ packages/server-local/src/
│ │ ├── routes.ts # /api/ai/*
│ │ ├── service.ts # LLM 调度
│ │ └── stream.ts # WebSocket 流式推送
-│ ├── memory/
-│ │ ├── routes.ts # /api/memory/*
-│ │ ├── service.ts # 记忆 CRUD + 向量检索
+│ ├── knowledge/
+│ │ ├── routes.ts # /api/knowledge/*
+│ │ ├── service.ts # 分块、索引、混合检索与 RAG 来源组装
│ │ └── schema.ts
+│ ├── project/ # 项目与默认收件箱
+│ ├── note/ # Markdown 笔记与标签
+│ ├── board/ # 看板、列与任务排序
│ ├── event/
│ │ ├── routes.ts # /api/events/*
│ │ └── service.ts # 应用事件记录
@@ -696,7 +657,7 @@ packages/server-local/src/
│ │ └── service.ts # 复盘生成
│ ├── hook/
│ │ ├── routes.ts # /api/hooks/* (包含 /api/hook/push 与 /api/hook/permission)
-│ │ ├── service.ts # Hook 自动配置扫描、免代码文件监听哨兵 (Watcher)、局域网 MQTT 订阅器、审批流程内存 Promise 挂起与状态机引擎
+│ │ ├── service.ts # 通用 Hook、文件监听哨兵、审批流程与状态机;不修改第三方配置
│ │ └── schema.ts # Hook 接口数据校验 Schema
│ └── settings/
│ ├── routes.ts # /api/settings/*
@@ -719,9 +680,9 @@ packages/server-local/src/
| `modules/ai/service.ts` | `packages/ai` | 委托模型调用和 Token 裁剪 |
| `modules/task/service.ts` | 直接实现 | 任务状态机和 CRUD,逻辑简单无需独立包 |
| `modules/companion/service.ts` | 直接实现 | 陪伴反馈决策,依赖 task + context 数据 |
-| `modules/memory/service.ts` | `packages/db` (queries) | 调用 db 包的向量检索封装 |
+| `modules/knowledge/service.ts` | `packages/db` + `packages/ai` | 分块、FTS5/sqlite-vec 检索、Embedding 调度和来源组装 |
| `modules/review/service.ts` | `packages/ai` + `packages/db` | 聚合数据后调用 AI 生成复盘 |
-| `modules/hook/service.ts` | 直接实现 | 自动配置打桩(扫描 OpenClaw 等 CLI)、内存挂起审批映射控制流 |
+| `modules/hook/service.ts` | 直接实现 | 通用 Hook 接收、文件监听和内存挂起审批控制流,不扫描或改写第三方配置 |
**判断标准**: 逻辑是否可能被多个 consumer 复用?是 → 抽为独立 package;否 → 直接写在 module service 中。
@@ -730,20 +691,54 @@ packages/server-local/src/
| Method | Path | 说明 | 阶段 |
|--------|------|------|------|
| GET | `/health` | 健康检查 | v1 |
-| POST | `/api/ai/chat` | AI 对话 (通过 WebSocket 流式推送) | v1 |
+| POST | `/api/ai/chat` | AI 对话;v2 接受 `useKnowledge/projectId` 并在完成事件返回来源 | v1/v2 |
| GET | `/api/tasks` | 任务列表 | v1 |
| POST | `/api/tasks` | 创建任务 | v1 |
| PATCH | `/api/tasks/:id` | 更新任务状态 | v1 |
+| GET/POST | `/api/projects` | 列出或创建项目 | v2 |
+| GET/POST/PATCH/DELETE | `/api/notes[/:id]` | Markdown 笔记 CRUD | v2 |
+| GET/POST/PATCH/DELETE | `/api/boards[/:id]` | 看板与列管理 | v2 |
+| POST | `/api/boards/:id/reorder` | 列或任务排序 | v2 |
| POST | `/api/events` | 写入应用事件 | v1 |
| GET | `/api/context/current` | 获取当前任务上下文 | v1 |
-| GET | `/api/memory/search` | 向量检索记忆 | v2 |
-| POST | `/api/memory` | 写入记忆 | v2 |
+| GET | `/api/knowledge/search` | 按项目范围执行全文/混合检索 | v2 |
+| GET | `/api/knowledge/index-status` | 获取索引能力、队列和失败状态 | v2 |
+| POST | `/api/knowledge/reindex` | 标记并重建知识索引 | v2 |
| GET | `/api/review/daily` | 获取日复盘 | v2 |
| POST | `/api/hook/push` | 外部用户自定义脚本/通知状态推送 | v2 |
| POST | `/api/hook/permission` | 外部 Agent 敏感指令挂起审批请求 (Permission Bubble) | v2 |
| GET | `/api/settings` | 获取设置 | v1 |
| PUT | `/api/settings` | 更新设置 | v1 |
+知识工作空间新增的跨包公共类型归属 `packages/shared`:
+
+```typescript
+export interface Project { id: string; name: string; isInbox: boolean }
+export interface Note { id: string; projectId: string; title: string; body: string; tags: string[] }
+export interface Board { id: string; projectId: string; name: string }
+export interface BoardColumn { id: string; boardId: string; name: string; position: number }
+
+export interface KnowledgeSource {
+ sourceType: 'note' | 'task';
+ sourceId: string;
+ projectId: string;
+ title: string;
+ excerpt: string;
+ chunkId: string;
+}
+
+export interface IndexStatus {
+ mode: 'hybrid' | 'fts-only';
+ pending: number;
+ failed: number;
+ stale: number;
+ providerConfigured: boolean;
+ vectorExtensionAvailable: boolean;
+}
+```
+
+任务公共类型在保留现有 `id/title/status/createdAt/completedAt` 的基础上增加 `projectId`、`boardId`、`columnId`、`description`、`position` 与 `updatedAt`。AI 完成响应统一为 `{ text, sources, retrievalMode }`;`sources` 必须由服务端检索结果生成。
+
### 6.4 Sidecar 打包策略
Fastify sidecar 需要作为独立可执行文件随 Tauri 应用分发,无需用户预装 Node.js。
@@ -841,14 +836,13 @@ export interface ChatChunk {
export interface ModelConfig {
id: string;
provider: 'deepseek' | 'openai' | 'claude' | 'custom';
- apiKey: string;
baseUrl?: string; // 自定义端点
model: string; // 模型名称
isDefault: boolean;
}
```
-内置 DeepSeek 作为默认,用户可添加其他模型。API Key 存储在本地 SQLite 中(加密存储)。
+内置 DeepSeek 聊天适配器,用户可添加其他聊天模型,并单独配置 Embedding Provider。API Key 只保存在操作系统凭据存储中,SQLite 仅保存非敏感配置。
### 7.4 Prompt Management
@@ -901,7 +895,7 @@ LLM API 不可用时(网络断开、API Key 无效、服务超时),系统
| 陪伴反馈 | 回退到规则引擎(基于任务状态 + 行为上下文的预定义反馈模板) |
| AI 对话 | 显示"当前无法连接 AI 服务"提示,不阻塞其他功能 |
| 上下文管道 | 采集和建模正常工作,仅 AI 摘要/提取能力暂停 |
-| 向量记忆 | 写入正常(embedding 生成暂停,排队等待恢复),检索使用已有索引 |
+| 知识检索 | FTS5 正常工作;Embedding 作业保留为 pending,向量不可用时显示 `fts-only` |
**设计原则**: AI 是增强能力,不是核心流程的阻塞依赖。用户在离线时仍能完成任务管理和专注跟踪。
@@ -926,7 +920,7 @@ Context Pipeline 是 NeoAssistant 的核心差异化模块,负责将分散的
└──────────────────────────────────────────┘ │ │ │ │ │ │
│ ▼ ▼ ▼ ▼ │
│ 原始事件 结构化数据 上下文模型 SQLite │
- │ (TS/WASM HNSW 内存索引) (含向量) │
+ │ FTS5 + vec │
│ Consumer ◀───────────────┘ │
│ (AI / 任务恢复 / 复盘) │
└─────────────────────────────────────────────────┘
@@ -951,12 +945,14 @@ Context Pipeline 是 NeoAssistant 的核心差异化模块,负责将分散的
4. 存储 (Store)
- 事件和上下文快照写入 SQLite (events, sessions 等表)
- - 长期记忆的语义文本写入 SQLite memories 表,同时根据运行模式生成向量索引:Mode A/B 下由 Fastify 本地调用 WASM/TS 向量库直接对该条记忆更新索引;Mode C 下写入服务端本地的 LanceDB 中。
+ - 用户知识写入 notes/tasks 与 knowledge_chunks;FTS5 同步更新,Embedding 作业异步写入 sqlite-vec。
+ - 助手长期记忆单独写入 memories,不与用户知识默认混合。
5. 消费 (Consume)
- - AI 对话时,根据运行模式对长期记忆执行相似度检索:Mode A/B 下通过 Fastify 本地内存 WASM/TS 检索服务执行语义检索,Mode C 下直查远程服务端本地的 LanceDB。获取匹配的记忆 ID 后,从 SQLite 中拉取对应记忆文本注入上下文。
+ - AI 对话按项目范围检索用户知识,融合 FTS5 与 sqlite-vec 候选,并返回服务端生成的结构化来源。
- 任务恢复时查询历史上下文和最近应用事件
- 复盘时聚合分析上下文与统计数据
+```
#### 8.2.1 极客 Hook 与权限卡片异步挂起流 (Hook Permission Approval Flow)
@@ -978,7 +974,6 @@ Context Pipeline 是 NeoAssistant 的核心差异化模块,负责将分散的
5. Fastify 内存解挂,释放挂起的 HTTP 连接
→ 响应状态码 200 (Allow) 或 403 (Deny) 返回给外部 Agent 终端继续/中止执行
```
-```
### 8.3 Context Types
@@ -1013,7 +1008,7 @@ export interface BehaviorContext {
export interface MemoryContext {
recentTasks: TaskSummary[]; // 最近相关任务
userPreferences: string[]; // 用户偏好摘要
- relevantMemories: string[]; // 向量检索结果
+ relevantKnowledge: KnowledgeSource[]; // 本次检索实际召回的笔记或任务来源
}
export interface FullContext {
@@ -1052,9 +1047,10 @@ const DEFAULT_RULES: PrivacyRule[] = [
### 9.1 Technology
-- **SQLite**: 本地关系型存储,零配置、单文件、高性能读写
-- **LanceDB (Rust Core, 内部通道)**: 嵌入式向量数据库,运行在 Rust Core 中,Fastify 通过 localhost 内部 HTTP 端点访问
-- **Drizzle ORM**: TypeScript-first ORM,类型安全、轻量
+- **SQLite + Drizzle ORM**:业务数据的唯一事实源,使用 WAL 保证桌面场景下的并发稳定性。
+- **SQLite FTS5**:标题、正文、标签和任务描述的全文检索,向量能力不可用时仍可独立工作。
+- **sqlite-vec**:与主数据库同进程加载的向量扩展,不在 Rust Core 或独立数据库中维护第二份数据。
+- **系统凭据存储**:保存 Chat / Embedding API Key;数据库只保存 Provider、端点、模型和索引元数据。
### 9.2 Core Schema
@@ -1063,18 +1059,79 @@ const DEFAULT_RULES: PrivacyRule[] = [
import { sqliteTable, text, integer, real } from 'drizzle-orm/sqlite-core';
-// 任务
-export const tasks = sqliteTable('tasks', {
+// 项目。系统迁移时创建默认“收件箱”。
+export const projects = sqliteTable('projects', {
id: text('id').primaryKey(),
name: text('name').notNull(),
- goal: text('goal'),
- status: text('status').notNull().default('pending'),
- // 'pending' | 'in_progress' | 'paused' | 'completed' | 'cancelled'
+ isInbox: integer('is_inbox', { mode: 'boolean' }).notNull().default(false),
+ createdAt: text('created_at').notNull(),
+ updatedAt: text('updated_at').notNull(),
+});
+
+export const boards = sqliteTable('boards', {
+ id: text('id').primaryKey(),
+ projectId: text('project_id').notNull().references(() => projects.id),
+ name: text('name').notNull(),
+ createdAt: text('created_at').notNull(),
+});
+
+export const boardColumns = sqliteTable('board_columns', {
+ id: text('id').primaryKey(),
+ boardId: text('board_id').notNull().references(() => boards.id),
+ name: text('name').notNull(),
+ position: integer('position').notNull(),
+});
+
+// 统一任务:简单清单和看板卡片读取同一张表。
+export const tasks = sqliteTable('tasks', {
+ id: text('id').primaryKey(),
+ projectId: text('project_id').notNull().references(() => projects.id),
+ boardId: text('board_id').references(() => boards.id),
+ columnId: text('column_id').references(() => boardColumns.id),
+ title: text('title').notNull(),
+ description: text('description'),
+ status: text('status').notNull().default('open'), // 'open' | 'done'
+ position: integer('position').notNull().default(0),
createdAt: text('created_at').notNull(),
updatedAt: text('updated_at').notNull(),
completedAt: text('completed_at'),
});
+export const notes = sqliteTable('notes', {
+ id: text('id').primaryKey(),
+ projectId: text('project_id').notNull().references(() => projects.id),
+ title: text('title').notNull(),
+ body: text('body').notNull().default(''), // Markdown
+ createdAt: text('created_at').notNull(),
+ updatedAt: text('updated_at').notNull(),
+});
+
+export const tags = sqliteTable('tags', {
+ id: text('id').primaryKey(),
+ name: text('name').notNull().unique(),
+});
+
+export const noteTags = sqliteTable('note_tags', {
+ noteId: text('note_id').notNull().references(() => notes.id),
+ tagId: text('tag_id').notNull().references(() => tags.id),
+});
+
+// FTS5 与 sqlite-vec 使用原生 SQL migration 创建虚拟表。
+export const knowledgeChunks = sqliteTable('knowledge_chunks', {
+ id: text('id').primaryKey(),
+ projectId: text('project_id').notNull().references(() => projects.id),
+ sourceType: text('source_type').notNull(), // 'note' | 'task'
+ sourceId: text('source_id').notNull(),
+ ordinal: integer('ordinal').notNull(),
+ content: text('content').notNull(),
+ contentHash: text('content_hash').notNull(),
+ embeddingModel: text('embedding_model'),
+ embeddingDimensions: integer('embedding_dimensions'),
+ indexStatus: text('index_status').notNull(), // pending | indexed | failed | stale
+ indexError: text('index_error'),
+ updatedAt: text('updated_at').notNull(),
+});
+
// 专注会话
export const sessions = sqliteTable('sessions', {
id: text('id').primaryKey(),
@@ -1099,13 +1156,12 @@ export const events = sqliteTable('events', {
timestamp: text('timestamp').notNull(),
});
-// 长期记忆 (含高维向量持久化与关系型元数据。模式 A/B 下通过 Fastify 进程内 HNSW 检索,模式 C 下通过远程 LanceDB 检索)
+// 助手长期记忆与用户知识分表管理,不进入知识库索引,除非用户后续显式开启。
export const memories = sqliteTable('memories', {
id: text('id').primaryKey(),
content: text('content').notNull(),
category: text('category').notNull(),
- // 'task_pattern' | 'user_preference' | 'knowledge' | 'context_snapshot'
- embeddingVector: text('embedding_vector'), // 向量数据的 JSON 数组或 BLOB 持久化
+ // 'task_pattern' | 'user_preference' | 'context_snapshot'
createdAt: text('created_at').notNull(),
lastAccessedAt: text('last_accessed_at'),
accessCount: integer('access_count').default(0),
@@ -1140,93 +1196,17 @@ export const settings = sqliteTable('settings', {
### 9.3 Vector Storage & Hybrid Retrieval Architecture
-为了在数据检索能力、安装包体积与跨端交叉编译 (CI) 的开发效率上取得完美平衡,系统针对不同模式采用**混合向量检索架构 (Hybrid Vector Search)**:
-
-- **模式 A (本地 TCP 端口 - 默认) & 模式 B (零端口 UDS)**:采用**轻量级本地混合向量库与 SQLite 融合持久化方案(策略甲)**。在 Fastify Sidecar (TypeScript) 侧直接运行纯 TS/WASM 版的高维向量检索组件(如 [hnswlib-wasm](https://www.npmjs.com/package/hnswlib-wasm) 或 MiniSearch 的轻量级混合检索引擎)。
- * **向量持久化机制**:高维向量 Embeddings 本身直接作为 BLOB 或 JSON 数组保存在本地主 SQLite 的 `memories` 关系表中(即 `embedding_vector` 字段)。
- * **内存索引重建**:当系统启动时,由 Fastify 在 Drizzle/SQLite 初始化完毕后秒级从 SQLite 中批量读取已有的向量,并在内存中构建 HNSW 索引树(在 1 万条以内的数据规模下重建耗时小于 100ms)。此策略不仅确保了向量数据的完整原子性持久化,更**100% 避免了在 Rust 侧打包整合重型 LanceDB 时导致的庞大编译开销 (Arrow/DataFusion) 与 CI/CD 跨平台构建故障**,同时让 Tauri 安装包体积减少约 40MB,且重启后无需再次调用 API 重新向量化。
-- **模式 C (分布式远程宿主)**:由于远程 Fastify 服务通常托管于性能更好、且不受安装包体积限制的专业服务器环境 (如 NAS Docker / Linux 云服务器),因此采用**物理 LanceDB 引擎**。Fastify 在远程服务端自持完整的SQLite 元数据与 `@lancedb/lancedb` Node 原生版,实现海量高效的语义相似度检索,并完全卸载本地工作电脑的算力。
-
-#### 9.3.1 Rust Core 内部系统能力端点 (带 APP_AUTH_TOKEN 校验)
-
-Rust 侧使用 axum 启动一个轻量 HTTP server,绑定 `127.0.0.1:RUST_PORT`,仅暴露钥匙链读取等敏感的系统级接口:
-
-```rust
-// src-tauri/src/internal_api.rs
-use axum::{
- Router, Json, routing::post, http::{StatusCode, HeaderMap},
- response::IntoResponse
-};
-use serde::{Deserialize, Serialize};
-
-#[derive(Deserialize)]
-pub struct KeychainGetRequest {
- pub key: String,
-}
-
-pub fn router() -> Router {
- Router::new()
- .route("/keychain/get", post(get_keychain_value))
-}
-
-async fn get_keychain_value(
- headers: HeaderMap,
- Json(req): Json,
-) -> impl IntoResponse {
- // 1. 从 Header 提取 Bearer Token 并与全局持有的 APP_AUTH_TOKEN 校验
- if let Some(auth_header) = headers.get("Authorization") {
- if let Ok(auth_str) = auth_header.to_str() {
- if auth_str == format!("Bearer {}", crate::get_auth_token()) {
- // 2. 调用原生 Keyring 安全获取密码
- if let Ok(value) = keyring::get_password("neo-companion", &req.key) {
- return (StatusCode::OK, value).into_response();
- }
- return (StatusCode::NOT_FOUND, "Key not found").into_response();
- }
- }
- }
- (StatusCode::UNAUTHORIZED, "Unauthorized").into_response()
-}
-```
-
-**端点范围严格限定**:仅 `POST /keychain/get` 等需要携带动态 Bearer Token 鉴权的极简内部通道,**不承载向量检索或任何常规数据库业务**,实现 100% 网络沙箱隔离。
-
-#### 9.3.2 Fastify 侧的消费逻辑
-
-Fastify 通过 HTTP 调用 Rust Core 内部端点完成向量操作,然后从 SQLite 拉取文本内容:
-
-```typescript
-// packages/server-local/src/modules/memory/service.ts
-
-// 动态匹配运行模式
-export async function querySemanticMemories(queryEmbedding: number[], limit = 5) {
- let matchedIds: string[] = [];
-
- if (process.env.RUNNING_MODE === 'distributed_hosting') {
- // Mode C: 远程 Sidecar 直接调用本地 LanceDB 库
- const lanceTable = await openLanceTable("memories");
- const results = await lanceTable.search(queryEmbedding).limit(limit).execute();
- matchedIds = results.map(r => r.id);
- } else {
- // Mode A/B: 本地模式,调用内存轻量级 WASM/TS 检索服务 (携带 APP_AUTH_TOKEN 校验)
- matchedIds = await localVectorIndex.search(queryEmbedding, limit);
- }
+知识检索遵循“全文搜索始终可用,向量搜索按配置增强”的原则:
- if (matchedIds.length === 0) return [];
+1. **分块**:Markdown 先按标题和段落切分,再合并为约 1200 字符的稳定分块并保留少量重叠;任务标题和描述组成单独知识条目。分块使用内容哈希判断是否需要重新索引。
+2. **全文索引**:每次写入内容时同步更新 FTS5。即使无网络、无 API Key 或 sqlite-vec 加载失败,搜索和 RAG 仍可使用关键词候选。
+3. **向量索引**:写入业务数据后提交进程内索引作业,调用 Embedding Adapter 后将向量写入 sqlite-vec。Provider、模型或维度变化时,将不匹配分块标记为 `stale` 后重建。
+4. **混合排序**:全文和向量分别取候选集,使用 Reciprocal Rank Fusion 合并;查询在召回前即应用 `project_id` 范围,防止跨项目泄漏。
+5. **删除一致性**:删除笔记或任务时,在同一事务中删除分块、FTS 行和向量行。失败作业保留错误摘要并可重试,不记录知识正文或 API Key。
- // 从 SQLite 中获取对应记忆文本元数据
- return db.select()
- .from(memories)
- .where(inArray(memories.id, matchedIds));
-}
-```
+#### 9.3.1 Embedding 生成策略
-#### 9.3.3 Embedding 生成策略
-
-向量记忆需要将文本转化为 embedding 向量。生成策略如下:
-
-- **v2**:复用已配置的 LLM provider 的 embedding API(如 DeepSeek / OpenAI embedding 端点),在 Fastify 侧的 `packages/ai` 中调用,生成后通过内部 HTTP 端点写入 Rust Core 的 LanceDB
-- **v3(可选)**:预留本地 embedding 模型接口(ONNX Runtime),但 v2 不实现
+聊天模型与 Embedding Provider 独立配置。首版实现 OpenAI-compatible `/embeddings` 接口;不假定当前聊天 Provider 提供 Embedding。API Key 由 Rust Core 从系统凭据存储读取,并通过受 APP_AUTH_TOKEN 保护的内部通道提供给 Fastify,绝不写入 SQLite、响应或日志。
```typescript
// packages/ai/src/adapters/types.ts
@@ -1238,13 +1218,20 @@ export interface EmbeddingAdapter {
}
```
+后续本地模型继续实现同一接口,不改变知识库表结构和检索 API。
+
+#### 9.3.2 RAG 来源约束
+
+`POST /api/ai/chat` 接受 `useKnowledge` 与可选 `projectId`。服务端先检索,再以稳定的 `sourceType/sourceId/chunkId` 组装上下文;最终响应和 `ai:done` 事件返回结构化 `sources`。来源列表由服务端根据实际召回结果生成,模型输出无权新增来源。
+
+当向量不可用时,`IndexStatus.mode` 为 `fts-only`,服务端继续用全文候选回答并在 UI 显示降级状态;不得静默退化为不使用知识库的普通聊天。
+
### 9.4 Data Location
```
用户数据目录 (Tauri app data):
├── neo-companion.db # 主 SQLite 数据库
├── neo-companion.db-wal # WAL 日志
-├── lancedb/ # LanceDB 向量数据 (v2+)
├── logs/ # 结构化日志文件
│ ├── server-YYYY-MM-DD.log # Fastify 日志 (pino JSON)
│ └── core-YYYY-MM-DD.log # Rust Core 日志 (tracing JSON)
@@ -1264,6 +1251,13 @@ pnpm --filter db drizzle-kit migrate # 执行迁移
应用启动时自动检查并执行待应用的迁移。
+知识工作空间首次迁移必须在单一事务中完成:
+
+1. 创建默认“收件箱”项目、默认看板和“待处理 / 进行中 / 已完成”三列。
+2. 为旧 `tasks` 行补齐项目、看板、列、位置和更新时间;`open` 进入“待处理”,`done` 进入“已完成”,保留原 ID、标题和完成时间。
+3. 创建 notes、tags、knowledge_chunks、FTS5 与 sqlite-vec 结构;旧任务随后进入增量索引队列。
+4. sqlite-vec 加载失败不得回滚业务表迁移;记录 `fts-only` 能力状态并继续启动。
+
### 9.6 SQLite 并发并发安全与高频事件防抖写入 (SQLite Concurrency & Debounced Writes)
由于屏幕感知引擎与窗口检测模块会高频产生用户活动状态 Ticks(如窗口切换、空闲检测、打字活跃状态),如果每次微小变化都直接进行一次 SQLite 数据库写事务,将带来极高的磁盘 I/O 压力并诱发 `SQLITE_BUSY: database is locked` 并发锁定冲突。为此,系统强制应用以下并发优化:
@@ -1285,71 +1279,24 @@ pnpm --filter db drizzle-kit migrate # 执行迁移
## 10. Hook & Permission Control System Architecture
-Hook 机制与权限控制系统是 NeoAssistant 接入外部生态(如 OpenClaw)并实现安全保护与实时状态感知的基础。
-
-### 10.1 配置文件扫描与自动打桩机制 (Auto-Registration Scanner)
+Hook 机制与权限控制系统是 NeoCompanion 接收外部状态并提供通用审批交互的基础。它与知识库相互独立,不绑定任何特定 Agent、CLI 或厂商。
-为实现开箱即用的极客体验,系统在启动时由 Fastify Sidecar 发起自动探测与非破坏性配置合并。
+### 10.1 显式接入协议 (Explicit Hook Registration)
-#### 10.1.1 探测路径与机制
-启动时,`HookService` 异步执行配置扫描:
-1. **OpenClaw 配置扫描**:
- - 默认扫描路径:用户主目录下的 `~/.openclaw/openclaw.json`。
- - 行为:若存在,解析 JSON 内容,检查 `plugins` 或 `hooks` 段中是否已注册 NeoAssistant 的 Hook 端点。
-2. **Claude Code 及其它 CLI 扫描**:
- - 扫描路径:`~/.claude/settings.json` 或 `settings.local.json`。
+NeoCompanion 只公开接入协议和用户可复制的配置片段,不扫描第三方配置文件,也不自动写入插件或 Hook。外部程序由用户主动选择以下通道:
-#### 10.1.2 非破坏性合并与打桩代码示例
-系统在写入配置时遵循**非破坏性 Patch**原则,仅在指定位置追加,且通过 ID 确保幂等。
+- HTTP:向 `/api/hook/push` 或 `/api/hook/permission` 主动发送请求。
+- File Watcher:写入 `~/.neo-companion/hooks/` 下的 JSON 文件。
+- UDS / Named Pipe:在零端口模式下使用与 HTTP 等价的消息结构。
+- WebSocket:已建立连接的客户端订阅状态和审批结果。
-```typescript
-// packages/server-local/src/modules/hook/service.ts
-import * as fs from 'fs/promises';
-import * as path from 'path';
-import os from 'os';
-
-export async function autoPatchOpenClawConfig(localServerPort: number) {
- const homeDir = os.homedir();
- const configPath = path.join(homeDir, '.openclaw', 'openclaw.json');
-
- try {
- const rawData = await fs.readFile(configPath, 'utf-8');
- const config = JSON.parse(rawData);
-
- // 初始化 hooks 结构
- if (!config.hooks) {
- config.hooks = [];
- }
-
- const hookUrl = `http://127.0.0.1:${localServerPort}/api/hook/permission`;
-
- // 检查是否已打桩
- const hasHook = config.hooks.some((h: any) => h.url === hookUrl);
- if (!hasHook) {
- config.hooks.push({
- id: "neo-companion-auth-gate",
- url: hookUrl,
- events: ["pre_execute_tool", "command_execution"],
- severity_threshold: 6, // 仅拦截敏感度 >= 6 的操作
- });
-
- // 非破坏性写回
- await fs.writeFile(configPath, JSON.stringify(config, null, 2), 'utf-8');
- }
- } catch (err: any) {
- if (err.code !== 'ENOENT') {
- // 仅记录解析错误,不阻塞主进程启动
- server.log.error(`Failed to patch OpenClaw config: ${err.message}`);
- }
- }
-}
-```
+所有接入都使用通用 `agentId`、`state`、`description` 与 `metadata` 字段。设置页可以展示端点、协议示例和连接状态,但不得枚举或修改其它应用的配置。
---
### 10.2 异步 Promise 审批挂起机制 (Promise-based Suspension Pipeline)
-当外部 Executor (如 OpenClaw) 发起涉及系统敏感指令(如危险脚本执行或文件覆写)的请求时,Fastify 端通过**内存挂起 Promise** 机制阻断 HTTP 请求,直至用户审批或超时。
+当已接入的外部程序发起涉及系统敏感指令(如危险脚本执行或文件覆写)的请求时,Fastify 端通过**内存挂起 Promise** 机制阻断请求,直至用户审批或超时。
#### 10.2.0 零端口模式 (Mode B) 下外部 Hook 的三维连通机制
@@ -1365,7 +1312,7 @@ export async function autoPatchOpenClawConfig(localServerPort: number) {
* **文件监听防抖与并发安全机制**:为避免外部程序在写入未完成时触发 Watcher 导致 JSON 解析异常,哨兵接收器必须应用 100ms 写入防抖与 try-catch 容错解析。若捕获到由于文件被锁或写入不完整抛出的 `SyntaxError`,会自动进行指数退避重试,严禁向外抛出未捕获异常导致 Sidecar 进程崩溃退出。
2. **原生本地域套接字挂载 (Native UDS Mount)**:
- 若外部 Agent (如 OpenClaw) 原生支持套接字协议,可直接通过 `unix:/tmp/neo-companion.sock:/api/hook/permission` 进行连接,实现端到端的绝对高安全零端口双向审批。
+ 支持套接字协议的外部程序可直接通过 `unix:/tmp/neo-companion.sock:/api/hook/permission` 连接,实现零端口双向审批。
3. **轻量回环 TCP 端口代理 (Local TCP Proxy Gateway)**:
为兼容不支持 UDS 协议的传统 TCP-only 工具,Tauri Rust Core 可在需要时临时且极其受限地启用一个 localhost TCP 回环网关端口,仅接收本机回环请求并高安全地路由代理给底层的 Unix 套接字 / Windows 命名管道,兼顾向后兼容与沙箱安全。
@@ -1512,7 +1459,7 @@ interface PermissionRequestMessage {
requestId: string; // 全局唯一请求 ID (UUID v4)
command: string; // 待执行的终端命令或敏感动作
severity: number; // 危险等级 (1 - 10)
- source?: string; // 来源 Agent 标识 (如 "OpenClaw")
+ source?: string; // 调用方提供的来源标识
};
}
```
@@ -1652,6 +1599,8 @@ export function usePermissionShortcuts() {
| 任务数据 | 任务名称、目标、状态 | 本地 SQLite | 是 (作为上下文) |
| 应用事件 | 窗口标题、应用名 | 本地 SQLite | 可选 (用户控制) |
| 对话历史 | 用户与 AI 的对话 | 本地 SQLite | 是 (上下文窗口内) |
+| 笔记与任务知识 | Markdown、标签、任务描述 | 本地 SQLite | 启用 Embedding 时发送分块;知识问答时发送召回片段 |
+| 全文与向量索引 | FTS5、sqlite-vec | 本地 SQLite | 否 |
| 屏幕内容 | 截图/OCR 结果 | 本地 (临时) | 仅摘要 (v2) |
| API Key | 模型密钥 | 本地加密存储 | 否 |
| 长期记忆 | AI 提取的记忆 | 本地 SQLite | 是 (检索结果) |
@@ -1685,6 +1634,8 @@ export function usePermissionShortcuts() {
- 开关应用事件记录
- 设置应用黑名单
- 查看将要发送给 AI 的上下文内容
+- 独立开关知识库检索和云端 Embedding
+- 查看索引模式、失败状态和待重建数量
- 清除所有本地数据
- 导出数据
- 关闭所有感知能力(纯聊天模式)
@@ -1749,39 +1700,37 @@ export async function getApiKey(provider: string): Promise {
- Web 端
- 完整上下文管道
- 壁纸层完整状态组件(任务清单、伴侣寄语、氛围色调 → v1.5/v2)
-- 向量记忆
+- 知识工作空间与向量索引
- 复盘系统
- 屏幕内容获取
- 移动端
-### 12.2 v2: Multi-Scenario Context Collaboration
+### 12.2 v2: Local Knowledge Workspace
-**目标**: 完整上下文理解 + 跨应用协作 + Web 管理端。
+**目标**: 完成“记录 → 组织 → 检索 → 引用回答”的本地知识闭环。
**新增技术范围**:
| 模块 | v2 新增 |
|------|---------|
-| apps/web | Web 管理端 (复盘看板、设置、历史) |
-| packages/server-local | Context/Memory/Review/Event 模块完整实现 |
-| packages/ai | 多模型支持、上下文压缩、记忆提取 |
-| packages/context-engine | 从 server-local 抽离为独立包,完整管道 (采集→处理→建模→存储→消费) |
-| packages/db | 关系表、events 表、reviews 表、迁移 |
-| crates/ | plugin-screen-context, plugin-app-events |
+| apps/desktop | 知识库入口、Markdown 编辑、项目切换、看板、搜索和来源跳转 |
+| packages/server-local | Project/Note/Board/Knowledge 模块、增量索引与 RAG 编排 |
+| packages/ai | 聊天模型与 EmbeddingAdapter 分离,OpenAI-compatible Embedding 实现 |
+| packages/db | 项目、笔记、看板、统一任务、知识分块、FTS5 与 sqlite-vec 迁移 |
+| apps/desktop/src-tauri | Chat / Embedding API Key 的系统凭据读写 |
-### 12.3 v3: Personal AI Workflow Assistant System
+### 12.3 v3: Knowledge Expansion & Long-term Companion
-**目标**: 多端协同 + 可控执行 + 完整记忆。
+**目标**: 扩展知识来源和长期个性化,同时保持本地优先、来源可见和用户可撤销。
**新增技术范围**:
| 模块 | v3 新增 |
|------|---------|
-| 移动端 | Tauri v2 Mobile (iOS/Android) |
-| 云同步 | 可选云服务、增量同步、冲突处理 |
-| 执行层 | 用户确认下的轻量自动化动作 |
-| 记忆系统 | 完整长期记忆、用户画像、个性化策略 |
-| 连接能力 | 外部工具状态接入 |
+| 内容接入 | 指定 Markdown 文件夹同步、PDF 与网页导入 |
+| Embedding | 可选本地模型实现,沿用统一 Adapter |
+| 记忆系统 | 用户知识与助手长期记忆分区治理、来源与删除控制 |
+| 连接能力 | 通用外部状态接入,不绑定特定工具 |
---
@@ -1791,7 +1740,9 @@ export async function getApiKey(provider: string): Promise {
|------|------|----------|
| Tauri sidecar 打包体积 | Node.js runtime 增加安装包大小 | Node.js SEA 打包为单文件;备选 Bun compile |
| SQLite 并发写入 | 高频事件写入可能冲突 | 使用 WAL 模式;事件批量写入;写操作队列化 |
-| LanceDB 跨平台编译分发风险 | 传统在 Rust 核心集成 LanceDB 极易在跨平台编译和 CI 中报错 | 在本地模式(Mode A/B)下彻底移除 Rust 侧 LanceDB 绑定,改用 Fastify 进程内 WASM/TS 检索组件;仅在远程模式(Mode C)下由服务端自持 Node 版原生 LanceDB,完全规避客户端的 Rust 编译分发难题 |
+| sqlite-vec 扩展加载失败 | 当前平台缺少兼容二进制时语义检索不可用 | 启动时能力探测;自动进入 `fts-only`;界面显示状态且允许重试 |
+| Embedding Provider 失败 | 新内容无法生成向量 | 保留 pending/failed 状态并指数退避;全文检索和内容编辑不受影响 |
+| Embedding 模型或维度变化 | 新旧向量不可比较 | 将旧分块标记为 stale,按模型和维度隔离后重建 |
| 内部端口管理 | Fastify + Rust Core 各需一个 localhost 端口,端口冲突或泄露 | 启动时动态分配可用端口;Rust Core 端口通过环境变量传递给 Sidecar;端口仅绑定 127.0.0.1 |
| LLM API 延迟 | 外部 API 响应慢影响体验 | WebSocket 流式展示;本地缓存常见响应;超时降级 |
| Rust 开发门槛 | 团队需要 Rust 能力 | 将 Rust 范围限制在系统级能力;业务逻辑全部在 TypeScript |
@@ -1868,17 +1819,18 @@ export async function getApiKey(provider: string): Promise {
## 17. Summary
-NeoAssistant 是一个**完全本地运行**的桌面 AI 陪伴系统。
+NeoCompanion 是一个**本地优先**的桌面 AI 陪伴与知识工作空间。
技术架构核心:
- **Tauri (Rust)** 提供系统级能力和桌面运行时;
- **Fastify (TypeScript)** 作为本地 sidecar 处理业务逻辑和 AI 调度;
- **Vue + Vite** 提供 UI;
-- **SQLite (关系型元数据) + TS/WASM 混合向量检索 (本地模式 A/B) / 物理 LanceDB (分布式模式 C)** 本地存储全部数据;
-- **外部 LLM API (DeepSeek)** 提供 AI 能力,用户可自定义模型;
-- **Context Pipeline** 将系统事件转化为结构化任务上下文,是核心差异化模块。
+- **SQLite + Drizzle + FTS5 + sqlite-vec** 统一存储业务数据与搜索索引;
+- **Chat Model Adapter + EmbeddingAdapter** 提供可替换的云端 AI 能力;
+- **Knowledge Pipeline** 将项目、笔记和统一任务转化为可检索、可引用的上下文;
+- **Context Pipeline** 继续将系统事件转化为结构化任务上下文。
-所有数据不离开用户本地,不依赖云服务,不需要账户注册。
+业务数据和索引默认不离开本机,也不需要账户注册。只有用户配置并启用云端 Provider 后,待向量化文本和问答上下文才会发送给对应服务;界面必须明确显示该边界。
-架构设计遵循渐进复杂度原则:v1 只落地最小闭环,v2 扩展完整上下文和 Web 端,v3 引入多端和执行层。
+架构设计遵循渐进复杂度原则:v1 落地陪伴最小闭环,v2 建立本地知识工作空间,v3 扩展内容来源与本地模型能力。
diff --git a/docs/PRD_overview.md b/docs/PRD_overview.md
index 93d85fa..cc97a59 100644
--- a/docs/PRD_overview.md
+++ b/docs/PRD_overview.md
@@ -4,9 +4,9 @@
- **Title**: NeoCompanion Product Requirements Document (Overview)
- **Owner**: Product
-- **Status**: Draft (Aligned with v3.0 Concept)
-- **Version**: 3.2
-- **Last Updated**: 2026-06-13
+- **Status**: Draft (Aligned with v3.3 Knowledge Workspace Decision)
+- **Version**: 3.3
+- **Last Updated**: 2026-06-18
- **Audience**: Product, Design, Engineering
- **Related Docs**:
- [`README.md`](file:///d:/character_design/NeoCompanion/README.md)
@@ -48,7 +48,7 @@
### 2.3 Why This Matters
-NeoCompanion 致力于通过**「助手情感载体 + 屏幕状态感知 + 智能网关连接 + 开放 Hook API」**的融合架构,解决这一痛点,让 AI 从“一次性应答工具”升级为“懂状态、能联动、长久相伴的数字搭子”。
+NeoCompanion 致力于通过**「助手情感载体 + 屏幕状态感知 + 本地知识工作空间 + 开放 Hook API」**的融合架构,解决这一痛点,让 AI 从“一次性应答工具”升级为“懂状态、能引用个人知识、长久相伴的数字搭子”。
---
@@ -56,9 +56,9 @@ NeoCompanion 致力于通过**「助手情感载体 + 屏幕状态感知 + 智
### 3.1 Positioning
-NeoCompanion 是一款**融合了「桌面悬浮智能助手 + 陪伴式监督学习 + 智能连接网关」的长期陪伴型 AI 系统**。
+NeoCompanion 是一款**融合了「桌面悬浮智能助手 + 陪伴式监督学习 + 本地知识工作空间」的长期陪伴型 AI 系统**。
-以可爱、有温度的悬浮助手(2D 精灵图帧动画)为前端载体,通过屏幕状态感知的温情反馈,辅以简单日常记录与智能网关协议复用,帮助用户在学习和创作中克服拖延、卡壳与中断。
+以可爱、有温度的悬浮助手(2D 精灵图帧动画)为前端载体,通过屏幕状态感知的温情反馈,辅以项目、笔记、任务与看板,将用户自己的资料转化为可检索、可引用的 AI 上下文,帮助用户在学习和创作中克服拖延、卡壳与中断。
NeoCompanion 的界面分为三层:
- **壁纸层**:沉浸式状态画布——天气、时间、任务进度、专注主控盘等信息融入桌面壁纸,支持一级轻交互(开始/暂停专注、点击任务跳转面板、沉浸模式切换);
@@ -72,10 +72,10 @@ NeoCompanion 的界面分为三层:
本文档锁定以下产品核心判断:
1. **产品形态与主轴**:以桌面悬浮助手及拟人化 TTS 语音互动为核心界面,主打”贴心但不过分亲密的陪伴监督”,绝非机械式效率看板。
2. **状态感知温情化**:屏幕识别唯一目的是为了“感知用户当前的专注/分心/卡壳/疲劳状态”,并由助手给予匹配的情绪反馈(如递虚拟咖啡、催促休息),严禁做任何商业监控。
-3. **网关架构与大模型对话**:
- - 不从零做庞大笨重的 Autonomous Agent。
- - NeoCompanion 作为**连接网关**与**对话适配器**。
- - 复杂跨软件任务代行调起 [OpenClaw](https://github.com/openclaw/openclaw);日常答疑与编程辅助直接调用标准大模型 API,融入助手极简对话流。
+3. **本地知识工作空间与 AI 对话**:
+ - 不从零做庞大笨重的 Autonomous Agent,也不把自动操作其它软件作为产品主轴。
+ - 在单一本地工作空间中,以项目关联 Markdown 笔记、看板和统一任务。
+ - AI 对话优先使用本地全文与向量混合检索,回答必须展示可点击来源;索引不可用时明确降级为全文检索。
4. **极客定制与 Hook 连接**:核心功能包含开放的 Hook API 监听接口,支持用户用自己编写的自定义程序推送编译、监控等状态提醒,助手进行拟人化播报。
5. **壁纸层状态画布 + 轻交互**:壁纸层以状态展示为主,支持一级交互(导航/触发型点击,如开始/暂停专注、点击任务跳转面板、沉浸模式切换),不承载二级及以上交互(编辑、输入、复杂选择)。二级交互(勾选任务、调整时长、处理 Hook 通知等)统一由悬浮层和面板层完成。这确保了"信息在哪里、操作就离哪里最近"的就近交互原则,同时保持壁纸层的安静氛围。
@@ -102,7 +102,7 @@ NeoCompanion 的长期能力分为四层:
+-------------------------------------------------------+
| 2. Routine Layer (番茄钟 / Todo账本 / 天气 / 助手小日记) |
+-------------------------------------------------------+
-| 3. Gateway Layer (内置原子Agent / OpenClaw / LLM 极简对话) |
+| 3. Knowledge & AI (项目 / 笔记 / 看板 / 混合检索 / AI 对话) |
+-------------------------------------------------------+
| 4. Hook & System (本地安全Hook API / 本地状态感知与记忆) |
+-------------------------------------------------------+
@@ -121,13 +121,15 @@ NeoCompanion 的长期能力分为四层:
- **日常天气与碎碎念**:基于气象的保暖带伞提醒、早晚问候、健康作息劝导。
- **助手工作日志**:日终自动生成以助手口吻撰写的温度成长记录。
-### 5.3 Gateway Layer (智能网关与大模型对话层)
-- **内置基础 Agent**:微型文件读写、网页搜索总结、剪贴板获取。
-- **OpenClaw 任务代行网关**:把繁复、长流程的跨软件操作分发给 [OpenClaw](https://github.com/openclaw/openclaw),NeoCompanion 面板进行友好状态展示。
-- **轻量 AI 对话与开发辅助**:支持通过聊天面板直连大模型(如 DeepSeek/Claude)进行日常提问、思路启发或一键拾取剪贴板报错,提供代码调优建议,保持极简免打扰。
+### 5.3 Knowledge & AI Layer (本地知识与 AI 层)
+- **单一本地工作空间**:项目是顶层组织单元;每个项目关联 Markdown 笔记、看板与任务,未分类内容进入默认“收件箱”。
+- **统一任务模型**:现有简单待办升级为任务清单视图;同一任务可归属项目、看板与列,避免维护两套任务数据。
+- **混合知识检索**:SQLite FTS5 负责标题、正文、标签和专有名词检索,`sqlite-vec` 负责语义召回,两路结果融合排序。
+- **有来源的 AI 回答**:AI 默认检索当前项目,也允许检索全部项目;回答后展示实际召回的笔记或任务来源。
+- **可用性优先**:Embedding 未配置、额度不足或索引失败时,笔记、看板和全文检索继续工作,AI 知识问答使用关键词召回并明确显示降级状态。
### 5.4 Hook & System Layer (系统与 Hook 层)
-- **安全本地多通道 Hook**:暴露本地端口监听,允许用户自定义脚本/程序向助手推送状态。同时支持自动检测主流 AI 编程终端及 Agent 配置文件进行自动打桩注入。
+- **安全本地多通道 Hook**:通过 HTTP、WebSocket、File Watcher 或本地域套接字接收用户自定义脚本/程序推送;不主动扫描或修改第三方工具配置。
- **浮动权限审查气泡**:支持当外部 Agent 执行敏感的写操作或命令时,通过助手弹出半透明审批气泡 (Permission Bubble),允许用户通过快捷热键一键审批,实现终端与桌面的温情交互。
- **隐私感知引擎**:本地运行窗口特征提取,去除敏感及隐私正文,仅用最小必要维度去推算脑力状态。
- **本地长期记忆**:记录用户专注偏好、被提醒习惯,使助手在陪伴过程中更加默契。
@@ -143,18 +145,19 @@ NeoCompanion 的长期能力分为四层:
- **壁纸层 MVP**:实现壁纸窗口嵌入桌面图标层下方,展示天气时间与专注主控盘。验证 WorkerW 技术可行性与用户体验。
- **Hook 角标**:助手右上角红点/数字角标提示待处理通知。
-### 6.2 v2: 开放 Hook 极客生态与 UDS 本地挂载 (Custom Hook, UDS & AI Chat)
-- **目标**:实现极客扩展、零端口开销与轻量陪伴能力。
+### 6.2 v2: 本地知识工作空间与知识增强对话 (Local Knowledge Workspace & RAG)
+- **目标**:完成“记录 → 组织 → 检索 → 引用回答”的本地知识闭环。
- **核心功能**:
- - 发布安全本地 Hook API,支持网络与**免代码本地文件监听哨兵 (File Watcher)** 多通道状态推送至助手提醒。
- - **实现零端口本地域套接字 (UDS) / Named Pipe 本地挂载支持**,杜绝本地 TCP 端口占用冲突。
- - 实现自动扫描并注入 OpenClaw 等工具的插件钩子配置。
- - 落地“浮动权限审批气泡”浮窗卡片,绑定全局快捷键 (`Ctrl+Shift+Y` / `Ctrl+Shift+N`) 快捷流审批。
- - 完善本地 AI 对话的多模型自定义配置,提供一键拾取剪贴板报错和高效答疑的极速体验。
-
-### 6.3 v3: 完整智能网关与分布式远程挂载生态 (Full AI Companion & Distributed Hosting OS)
-- **目标**:作为个人工作流的统一网关伙伴系统,支持主机算力解耦。
+ - 项目、Markdown 笔记、看板和统一任务管理。
+ - SQLite FTS5 + `sqlite-vec` 混合检索、增量索引、索引状态和手动重建。
+ - 可配置的聊天模型与 OpenAI-compatible Embedding Provider;密钥只进入系统凭据存储。
+ - AI 回答附可点击来源;向量能力失败时降级全文检索。
+ - 通用 Hook API、文件监听与权限审批作为独立能力保留,不绑定特定外部工具。
+
+### 6.3 v3: 本地知识扩展与长期陪伴 (Knowledge Expansion & Long-term Companion)
+- **目标**:扩展知识来源,同时保持本地优先和用户可控。
- **核心功能**:
- - **支持分布式远程宿主挂载 (Remote Server Binding)**,允许将 LanceDB 向量检索、Drizzle SQLite 等重度模块托管至 NAS 或云服务器,极大卸载本地工作机算力并支持多设备数据同步。
- - 深度接入 OpenClaw,在助手侧适配展示复杂跨应用操作进度。
- - 助手性格记忆演进,形成有长期偏好和高度默契的情感交互。
+ - 指定 Markdown 文件夹同步,以及 PDF、网页等多格式导入。
+ - 可选本地 embedding 模型,降低知识正文发送到云端的需要。
+ - 将用户知识、对话记录与助手长期记忆分区治理,提供独立开关、来源和删除能力。
+ - 在显式授权下演进助手偏好记忆,形成有依据、可撤销的个性化交互。
diff --git "a/docs/\345\205\267\344\275\223\350\203\275\345\212\233\346\236\204\346\200\235.md" "b/docs/\345\205\267\344\275\223\350\203\275\345\212\233\346\236\204\346\200\235.md"
index ddfae1d..925110e 100644
--- "a/docs/\345\205\267\344\275\223\350\203\275\345\212\233\346\236\204\346\200\235.md"
+++ "b/docs/\345\205\267\344\275\223\350\203\275\345\212\233\346\236\204\346\200\235.md"
@@ -5,9 +5,9 @@
- **Title**: NeoCompanion Product Requirements & Capability Design - Detailed Concept
- **Product Name**: NeoCompanion
- **Owner**: Product & User Vision
-- **Status**: Draft (Approved v3.0)
-- **Version**: 3.2
-- **Last Updated**: 2026-06-12
+- **Status**: Draft (Approved v3.3 Knowledge Workspace Decision)
+- **Version**: 3.3
+- **Last Updated**: 2026-06-18
- **Audience**: Product, Design, Engineering
---
@@ -114,42 +114,51 @@ NeoCompanion 是一款**融合了「桌面智能助手 + 陪伴式监督学习 +
---
-## 4. 智能网关架构与大模型对话 (Agent Gateway & LLM Interaction)
+## 4. 本地知识工作空间与 AI 对话 (Local Knowledge Workspace & AI)
-要让悬浮助手的“智能”既强大又轻量,NeoCompanion 采用**“智能连接网关 + 面板适配”**的轻量化 Agent 架构。
-
-我们不从零构建复杂的重度 Autonomous Agent,而是让 NeoCompanion 成为一个精致的**连接枢纽与控制台**,把复杂的任务分发给市面上强大的专业工具,并将执行状态优雅地收拢并展示在助手面板中。
+NeoCompanion 不把复杂跨应用自动执行作为产品主轴,而是围绕用户主动维护的知识与行动建立闭环:笔记负责沉淀信息,任务和看板负责推进工作,AI 通过检索引用这些内容提供帮助。
```
- +-------------------+
- | NeoCompanion |
- | 悬浮助手 / 网关 |
- +-------------------+
- / \
- / \ API 直连大模型
- / \
- +------------------+ +--------------------+
- | OpenClaw | | LLM API |
- | (负责复杂 GUI 执行) | | (极简聊天/代码建议) |
- +------------------+ +--------------------+
+ +-----------------------------+
+ | NeoCompanion |
+ | 项目 / 笔记 / 看板 / AI 对话 |
+ +--------------+--------------+
+ |
+ 本地全文检索 + 可选向量检索
+ |
+ +--------------v--------------+
+ | SQLite: 业务数据 / FTS5 / vec |
+ +--------------+--------------+
+ |
+ 用户授权后调用外部 API
+ |
+ +--------------v--------------+
+ | Chat Model / Embedding API |
+ +-----------------------------+
```
-### 4.1 内置基础原子 Agent 能力
-本地直接提供简单、高频的单步读写与搜索能力,以便快速响应用户:
-- **文件读写 (Read/Write)**:快速帮用户读取本地记录、保存备忘或更新待办文件。
-- **网页搜索 (Search)**:在用户卡壳时,快速静默检索解决思路并总结。
-- **剪贴板联动 (Clipboard)**:直接拾取用户复制的报错信息或文本。
-
-### 4.2 OpenClaw 复杂任务网关 (Computer Use Gateway)
-当用户给助手发送需要复杂跨应用操作、自主规划执行的长流程任务时(例如:“帮我把刚才整理的日记草稿发布到我的博客后台,并自动生成一篇总结推文发到社交平台”):
-- **网关角色**:NeoCompanion 本身不直接去模拟鼠标点击网页或跨软件流转,而是作为**智能连接网关**,直接对接并调用开源个人 AI 助手与 GUI 执行 Agent —— [OpenClaw](https://github.com/openclaw/openclaw)。
-- **面板适配展示**:NeoCompanion 通过 API/WebSocket 订阅 OpenClaw 的执行进度、规划树和截图。助手会在桌面以“助手正在后台处理”、“进度条加载”或小看板的形式,提供极其友好且不占屏幕的**面板适配展示**,让复杂 Agent 过程完全可视化、可控化。
-
-### 4.3 极简 AI 对话与开发辅助面板 (Lightweight AI Chat & Coding Assistant)
-为了确保助手的轻量与专注,我们不强塞复杂的 IDE 级编程控制面板,而是将其自然融入到助手的“极简 AI 对话框”中。
-- **自然提问与闲聊**:用户在写代码或学习过程中遇到瓶颈、卡壳、或产生焦虑情绪时,可以直接在助手的聊天面板中倾诉或提问。
-- **一键拾取与调试建议**:在核心的编程和报错排查场景中,用户可以直接使用“剪贴板自动抓取”或“一键填入报错”功能,向助手发起提问。助手将调用底层的通用大模型(如 DeepSeek/Claude)生成调优思路和标准的 Markdown 代码块。
-- **快速复写与复制**:生成的代码和解决方案会清晰呈现在对话气泡中,支持一键复制代码。这既能有效帮助用户理清思路、打破卡壳,又保持了助手作为“桌面数字陪伴者”的轻量化本色。
+### 4.1 工作空间与项目
+- **单一本地工作空间**:首版不引入账号、团队空间或云同步,所有内容保存在当前设备。
+- **项目作为组织边界**:笔记、看板和任务均可归属项目;系统提供不可缺省的“收件箱”项目承接未分类内容。
+- **统一任务系统**:简单待办与看板卡片是同一份任务数据的不同视图,避免重复录入和状态不一致。
+
+### 4.2 Markdown 笔记与看板
+- **Markdown 笔记**:首版提供标题、正文、标签和预览,不实现 Notion 式块编辑、附件、协作和版本历史。
+- **基础看板**:项目可包含看板与有序列;任务支持跨列移动、排序和完成状态。
+- **内容关联**:AI 检索覆盖笔记和任务,来源可以直接打开对应内容,形成“资料 → 行动 → 回顾”的连续工作流。
+
+### 4.3 混合检索与索引生命周期
+- **全文检索**:SQLite FTS5 索引标题、Markdown 正文、标签和任务描述,保证专有名词和精确关键词可命中。
+- **向量检索**:`sqlite-vec` 保存语义向量;Markdown 按标题和段落稳定分块,单块约 1200 字符并保留少量重叠,任务作为单独知识条目。
+- **结果融合**:全文和向量候选通过排名融合产生最终结果,并严格遵守当前项目或全部项目的检索范围。
+- **增量更新**:内容保存后异步生成索引;删除内容同步清除全文和向量记录;Embedding 模型或维度变化时将旧索引标记为待重建。
+- **明确降级**:Embedding 未配置、额度不足、网络失败或向量扩展不可用时,系统继续提供全文搜索,并在界面显示索引状态。
+
+### 4.4 有来源的 AI 对话
+- **知识增强问答**:AI 默认检索当前项目,用户可切换为全部项目;检索结果作为上下文发送给聊天模型。
+- **来源约束**:回答后的来源列表只能包含本次实际召回的笔记或任务,不允许模型虚构引用。
+- **隐私边界**:业务数据、全文索引和向量索引默认留在本地。只有用户配置并启用 Provider 后,待向量化文本和问答上下文才会发送给对应云端服务。
+- **模型解耦**:聊天模型与 Embedding Provider 分开配置,首版通过 OpenAI-compatible `/embeddings` 接口接入,不假定聊天模型具备向量能力。
---
@@ -191,26 +200,21 @@ NeoCompanion 是一款**融合了「桌面智能助手 + 陪伴式监督学习 +
3. **第三方智能家居与健康跟踪**:
- 比如番茄钟久坐、饮水机提醒、社交媒体收到新私信,皆可通过轻量 Hook 直接让悬浮助手为您播报。
-### 5.3 多通道极客 Hook 联动架构与多样传输挂载 (Advanced Multi-channel Hook & Transport Architecture)
+### 5.3 多通道 Hook 联动架构 (Advanced Multi-channel Hooks)
-为了将助手的“网关提醒”、“状态跟踪”以及通信体验做到极致,我们设计了以下高度灵活的多通道联动与挂载方案:
+Hook 是独立的外部状态入口,不绑定任何特定 Agent 或 CLI。外部程序可主动选择 HTTP、WebSocket、文件监听或本地域套接字接入,NeoCompanion 不扫描或改写第三方工具配置。
-#### 5.3.1 多物理通道主服务挂载 (Multi-Host Mounting Modes)
-系统不再局限于单一的 `localhost:PORT` 绑定,提供了多种挂载模式以适应不同的使用环境:
+#### 5.3.1 多物理通道主服务挂载 (Local Transport Modes)
+系统不局限于单一的 `localhost:PORT` 绑定,提供本机通信方式以适应不同环境:
- **UDS / Named Pipe 挂载(高安零端口)**:在对安全性要求极高的环境,或者由于端口占用导致频繁报错时,系统支持挂载到本地 Unix Domain Socket (`/tmp/neo-companion.sock`) 或 Windows Named Pipe (`\\.\pipe\neo-companion`)。**100% 避免端口冲突与防火墙弹窗警告**。
-- **分布式远程宿主挂载(NAS / 云端)**:用户可以把繁重的 LanceDB 向量检索、Drizzle 关系数据库以及后台 Agent 编排逻辑“挂载”到高性能的家用 NAS、局域网服务器或云端 VPS 上,本地桌面端(Mac/Windows)仅作为 2D 精灵图轻量渲染与交互入口,**极大卸载工作机算力,且支持多台设备间助手数据漫游同步**。
-
-#### 5.3.2 配置文件扫描与自动打桩 (Auto-Patching & Detection)
-- **零配置即用**:NeoCompanion 启动时,会自动扫描本地主流 AI 编程终端及 Agent 工具(如 OpenClaw 等)的配置文件路径(例如 `~/.openclaw/openclaw.json`)。
-- **自动注册插件**:若检测到相应工具已初始化,系统会自动将 NeoCompanion 的 Hook 接口路径作为状态插件注入其中,无需用户繁琐地手动配置。当这些 Agent 工具运行时,便会自动向 NeoCompanion 实时广播其执行状态。
-#### 5.3.3 浮动权限卡片与快捷审批 (Permission Bubble & Hotkeys)
-- **免死等终端**:当底层的 GUI 执行器(如 OpenClaw)或本地自定义进程请求执行敏感指令(如修改核心系统配置、执行高危 bash 命令、覆盖关键文件)时,外部工具会通过 HTTP API 挂起当前流程并向 NeoCompanion 发送审批请求。
+#### 5.3.2 浮动权限卡片与快捷审批 (Permission Bubble & Hotkeys)
+- **免死等终端**:当已接入的外部程序请求执行敏感指令(如修改核心系统配置、执行高危脚本、覆盖关键文件)时,可通过通用 Hook API 挂起流程并向 NeoCompanion 发送审批请求。
- **透明审批浮窗**:助手会直接在桌面弹出一个精美、半透明的”权限气泡审批卡片 (Permission Bubble)”,展示操作详情(操作类型、涉及路径、敏感度评分)。
- **极速热键反馈**:用户可以直接点击审批卡片中的 `Allow (同意)` 或 `Deny (拒绝)`。极客用户可以使用系统级全局热键:`Ctrl+Shift+Y` 批准,`Ctrl+Shift+N` 拒绝。
- **状态流控**:审批结果通过 HTTP/WebSocket 异步返回给挂起的执行进程,让终端交互实现从命令行到桌面有温度交互的跃升。
-#### 5.3.4 角标通知提醒 (Badge Notification)
+#### 5.3.3 角标通知提醒 (Badge Notification)
当外部 Agent 推送状态通知或审批请求时,助手**不会**在壁纸上弹出通知条(壁纸层不交互),而是在悬浮助手的右上角显示一个**红色角标**:
- 1 条未读:红色圆点
- 多条未读:红色圆点 + 数字
@@ -218,17 +222,17 @@ NeoCompanion 是一款**融合了「桌面智能助手 + 陪伴式监督学习 +
角标的设计隐喻来自手机 App 的未读提示——用户已有”红点=有待处理”的直觉认知。点击角标后打开面板,切换到 Hook 确认视图进行操作。角标本身不承载任何操作,只做”轻量提醒”。
-#### 5.3.5 免代码文件监听哨兵 Hook (Zero-Code File Watcher Hook)
+#### 5.3.4 免代码文件监听哨兵 Hook (Zero-Code File Watcher Hook)
为了让非开发人员或编写简单 Bash 脚本的极客也能轻松“挂载”状态,系统提供**文件监听哨兵机制**:
- 挂载后台监听 `~/.neo-companion/hooks/` 目录。
- 任何脚本在执行成功、失败或完成某项任务时,只需以标准 Shell 重定向指令写入 JSON 即可:
`echo '{"status": "success", "message": "代码同步已完成!", "tts": "代码同步完成。"}' > ~/.neo-companion/hooks/sync.json`
- 系统将秒级感知该文件覆写,自动触发助手的欢呼动作与 TTS 播报。**完全避免了在受限命令行环境里处理 curl 网络请求超时或失败的困扰**。
-#### 5.3.6 局域网消息总线挂载 (MQTT / Home Assistant Hook)
+#### 5.3.5 局域网消息总线挂载 (MQTT / Home Assistant Hook)
支持将助手服务挂载并订阅本地家庭局域网的 MQTT 消息队列(如 `home/sensors/sitting`),使得智能家居状态(如久坐报警、饮水提醒、编译网关状态)可以实时无缝驱动助手在桌面做出温馨催促。
-#### 5.3.7 微观状态映射引擎 (Event-to-Animation Mapping)
+#### 5.3.6 微观状态映射引擎 (Event-to-Animation Mapping)
助手通过 Hook 捕获到外部 Agent 的微小执行状态时,能完美映射到助手的细腻动画和动作中:
- **Thinking (思考中)**:Agent 正在梳理规划、调用模型时,助手做出托腮、思考、或转笔动作。
- **Working/Typing (工具执行中)**:Agent 正在跑工具、修改文件时,助手戴上眼镜在迷你键盘上工作。
@@ -244,19 +248,19 @@ NeoCompanion 是一款**融合了「桌面智能助手 + 陪伴式监督学习 +
- **功能**:落地助手番茄钟、助手 Todo 小账本、日常天气播报和记录日记。
- **感知**:完成基本的窗口活跃度检测,驱动助手对“专注/分心”做出语音和表情互动。
-### 6.2 Phase 2: 多通道 Hook、UDS 挂载与网关扩展 (Multi-Channel Hooks, UDS & Gateway Integration)
-- **目标**:打通高度弹性的极客连接生态。
+### 6.2 Phase 2: 本地知识工作空间 (Local Knowledge Workspace)
+- **目标**:完成“记录 → 组织 → 检索 → 引用回答”的知识闭环。
- **功能**:
- - 发布本地 Hook API,允许通过网络或**本地文件监听哨兵 (File Watcher)** 向助手推送自定义状态。
- - **实现零端口 UDS (Unix Domain Socket) / Named Pipe 挂载支持**,避免本地 TCP 端口冲突与网络防火墙弹窗警告。
- - **实现自动配置文件扫描注入**(优先支持 OpenClaw 的配置文件自动注册打桩)。
- - **落地“浮动权限审批气泡 (Permission Bubble)”机制**与全局系统热键操作(`Ctrl+Shift+Y` / `Ctrl+Shift+N`)。
- - 完善本地 AI 聊天面板的多模型自定义配置,提供一键极速答疑的开发辅助体验。
-- **验证**:实现“外部 AI 执行敏感动作、代码编译成功/失败后,助手动态反馈和桌面一键审批”的极客闭环。
-
-### 6.3 Phase 3: 智能 Agent 完整联接与分布式远程挂载 (Full Agent Gateway & Distributed Hosting Ecosystem)
-- **目标**:全面作为个人工作流的智能控制面板,支持算力彻底解耦。
+ - 项目、Markdown 笔记、统一任务与基础看板。
+ - SQLite FTS5 与 `sqlite-vec` 混合检索、增量索引、索引状态和重建。
+ - 聊天模型与 Embedding Provider 独立配置,AI 回答展示可点击来源。
+ - 保留通用 Hook API、文件监听、UDS / Named Pipe 与权限审批能力,但不自动修改第三方配置。
+- **验证**:在无向量 Provider 时可完成笔记与看板管理和全文搜索;配置 Provider 后可得到带真实来源的知识问答。
+
+### 6.3 Phase 3: 知识扩展与长期陪伴 (Knowledge Expansion & Long-term Companion)
+- **目标**:扩展知识来源和个性化能力,同时保持本地优先与用户可控。
- **功能**:
- - **支持分布式远程宿主挂载 (Remote Server Binding)**,允许将 LanceDB 向量检索、Drizzle 关系数据库、AI 上下文管线等繁重模块托管至家用 NAS、局域网服务器或云端 VPS,实现多端数据漫游同步并极大地节省本地工作电脑算力。
- - 深度打通 OpenClaw 的 GUI 执行网关,实现后台复杂 GUI 任务的代行与面板进度适配。
- - 扩展长久记忆系统,使助手的声线、语气、建议策略随着长期的陪伴而愈发懂你,形成真正独一无二的“数字搭子”。
+ - 支持指定 Markdown 文件夹同步,以及 PDF、网页等多格式导入。
+ - 提供可选本地 Embedding 模型,减少知识正文发送到云端的需要。
+ - 将用户知识和助手长期记忆分区治理,支持查看来源、独立开关与删除。
+ - 在显式授权下扩展偏好记忆,使助手逐渐形成可解释、可撤销的个性化策略。
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index a7df543..dcf7c06 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -16,7 +16,7 @@ importers:
version: 6.0.3
vitest:
specifier: 4.1.7
- version: 4.1.7(@types/node@24.12.4)(vite@8.0.14(@types/node@24.12.4)(esbuild@0.28.0)(tsx@4.22.3))
+ version: 4.1.7(@types/node@24.12.4)(jsdom@29.1.1)(vite@8.0.14(@types/node@24.12.4)(esbuild@0.28.0)(tsx@4.22.3))
apps/desktop:
dependencies:
@@ -26,6 +26,39 @@ importers:
'@tauri-apps/api':
specifier: ^2.9.0
version: 2.11.0
+ '@tiptap/core':
+ specifier: 3.26.0
+ version: 3.26.0(@tiptap/pm@3.26.0)
+ '@tiptap/extension-heading':
+ specifier: 3.26.0
+ version: 3.26.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))
+ '@tiptap/extension-list':
+ specifier: 3.26.0
+ version: 3.26.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))(@tiptap/pm@3.26.0)
+ '@tiptap/extensions':
+ specifier: 3.26.0
+ version: 3.26.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))(@tiptap/pm@3.26.0)
+ '@tiptap/markdown':
+ specifier: 3.26.0
+ version: 3.26.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))(@tiptap/pm@3.26.0)
+ '@tiptap/pm':
+ specifier: 3.26.0
+ version: 3.26.0
+ '@tiptap/starter-kit':
+ specifier: 3.26.0
+ version: 3.26.0
+ '@tiptap/suggestion':
+ specifier: 3.26.0
+ version: 3.26.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))(@tiptap/pm@3.26.0)
+ '@tiptap/vue-3':
+ specifier: 3.26.0
+ version: 3.26.0(@floating-ui/dom@1.7.6)(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))(@tiptap/pm@3.26.0)(vue@3.5.34(typescript@6.0.3))
+ marked:
+ specifier: ^17.0.6
+ version: 17.0.6
+ textarea-caret:
+ specifier: ^3.1.0
+ version: 3.1.0
vue:
specifier: 3.5.34
version: 3.5.34(typescript@6.0.3)
@@ -33,9 +66,15 @@ importers:
'@tauri-apps/cli':
specifier: 2.11.2
version: 2.11.2
+ '@types/textarea-caret':
+ specifier: ^3.0.3
+ version: 3.0.4
'@vitejs/plugin-vue':
specifier: ^6.0.2
version: 6.0.7(vite@8.0.14(@types/node@24.12.4)(esbuild@0.28.0)(tsx@4.22.3))(vue@3.5.34(typescript@6.0.3))
+ jsdom:
+ specifier: ^29.1.1
+ version: 29.1.1
typescript:
specifier: 6.0.3
version: 6.0.3
@@ -44,7 +83,7 @@ importers:
version: 8.0.14(@types/node@24.12.4)(esbuild@0.28.0)(tsx@4.22.3)
vitest:
specifier: 4.1.7
- version: 4.1.7(@types/node@24.12.4)(vite@8.0.14(@types/node@24.12.4)(esbuild@0.28.0)(tsx@4.22.3))
+ version: 4.1.7(@types/node@24.12.4)(jsdom@29.1.1)(vite@8.0.14(@types/node@24.12.4)(esbuild@0.28.0)(tsx@4.22.3))
vue-tsc:
specifier: ^3.1.5
version: 3.3.1(typescript@6.0.3)
@@ -63,7 +102,7 @@ importers:
version: 6.0.3
vitest:
specifier: 4.1.7
- version: 4.1.7(@types/node@24.12.4)(vite@8.0.14(@types/node@24.12.4)(esbuild@0.28.0)(tsx@4.22.3))
+ version: 4.1.7(@types/node@24.12.4)(jsdom@29.1.1)(vite@8.0.14(@types/node@24.12.4)(esbuild@0.28.0)(tsx@4.22.3))
packages/db:
dependencies:
@@ -88,7 +127,7 @@ importers:
version: 6.0.3
vitest:
specifier: 4.1.7
- version: 4.1.7(@types/node@24.12.4)(vite@8.0.14(@types/node@24.12.4)(esbuild@0.28.0)(tsx@4.22.3))
+ version: 4.1.7(@types/node@24.12.4)(jsdom@29.1.1)(vite@8.0.14(@types/node@24.12.4)(esbuild@0.28.0)(tsx@4.22.3))
packages/server-local:
dependencies:
@@ -134,7 +173,7 @@ importers:
version: 6.0.3
vitest:
specifier: 4.1.7
- version: 4.1.7(@types/node@24.12.4)(vite@8.0.14(@types/node@24.12.4)(esbuild@0.28.0)(tsx@4.22.3))
+ version: 4.1.7(@types/node@24.12.4)(jsdom@29.1.1)(vite@8.0.14(@types/node@24.12.4)(esbuild@0.28.0)(tsx@4.22.3))
packages/shared:
devDependencies:
@@ -143,7 +182,7 @@ importers:
version: 6.0.3
vitest:
specifier: 4.1.7
- version: 4.1.7(@types/node@24.12.4)(vite@8.0.14(@types/node@24.12.4)(esbuild@0.28.0)(tsx@4.22.3))
+ version: 4.1.7(@types/node@24.12.4)(jsdom@29.1.1)(vite@8.0.14(@types/node@24.12.4)(esbuild@0.28.0)(tsx@4.22.3))
packages/tts:
dependencies:
@@ -159,10 +198,25 @@ importers:
version: 6.0.3
vitest:
specifier: 4.1.7
- version: 4.1.7(@types/node@24.12.4)(vite@8.0.14(@types/node@24.12.4)(esbuild@0.28.0)(tsx@4.22.3))
+ version: 4.1.7(@types/node@24.12.4)(jsdom@29.1.1)(vite@8.0.14(@types/node@24.12.4)(esbuild@0.28.0)(tsx@4.22.3))
packages:
+ '@asamuzakjp/css-color@5.1.11':
+ resolution: {integrity: sha512-KVw6qIiCTUQhByfTd78h2yD1/00waTmm9uy/R7Ck/ctUyAPj+AEDLkQIdJW0T8+qGgj3j5bpNKK7Q3G+LedJWg==}
+ engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
+
+ '@asamuzakjp/dom-selector@7.1.1':
+ resolution: {integrity: sha512-67RZDnYRc8H/8MLDgQCDE//zoqVFwajkepHZgmXrbwybzXOEwOWGPYGmALYl9J2DOLfFPPs6kKCqmbzV895hTQ==}
+ engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
+
+ '@asamuzakjp/generational-cache@1.0.1':
+ resolution: {integrity: sha512-wajfB8KqzMCN2KGNFdLkReeHncd0AslUSrvHVvvYWuU8ghncRJoA50kT3zP9MVL0+9g4/67H+cdvBskj9THPzg==}
+ engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
+
+ '@asamuzakjp/nwsapi@2.3.9':
+ resolution: {integrity: sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==}
+
'@babel/helper-string-parser@7.27.1':
resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==}
engines: {node: '>=6.9.0'}
@@ -180,6 +234,46 @@ packages:
resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==}
engines: {node: '>=6.9.0'}
+ '@bramus/specificity@2.4.2':
+ resolution: {integrity: sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==}
+ hasBin: true
+
+ '@csstools/color-helpers@6.0.2':
+ resolution: {integrity: sha512-LMGQLS9EuADloEFkcTBR3BwV/CGHV7zyDxVRtVDTwdI2Ca4it0CCVTT9wCkxSgokjE5Ho41hEPgb8OEUwoXr6Q==}
+ engines: {node: '>=20.19.0'}
+
+ '@csstools/css-calc@3.2.1':
+ resolution: {integrity: sha512-DtdHlgXh5ZkA43cwBcAm+huzgJiwx3ZTWVjBs94kwz2xKqSimDA3lBgCjphYgwgVUMWatSM0pDd8TILB1yrVVg==}
+ engines: {node: '>=20.19.0'}
+ peerDependencies:
+ '@csstools/css-parser-algorithms': ^4.0.0
+ '@csstools/css-tokenizer': ^4.0.0
+
+ '@csstools/css-color-parser@4.1.7':
+ resolution: {integrity: sha512-CmjJFQTFQx/U/xNJhSjCQ0ilpesPmNQ8+eOUeM/+kDOVW33qsIjeOXc27vrQDdWVkf83ZSWwtg7kXSUvKDJ8cQ==}
+ engines: {node: '>=20.19.0'}
+ peerDependencies:
+ '@csstools/css-parser-algorithms': ^4.0.0
+ '@csstools/css-tokenizer': ^4.0.0
+
+ '@csstools/css-parser-algorithms@4.0.0':
+ resolution: {integrity: sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==}
+ engines: {node: '>=20.19.0'}
+ peerDependencies:
+ '@csstools/css-tokenizer': ^4.0.0
+
+ '@csstools/css-syntax-patches-for-csstree@1.1.5':
+ resolution: {integrity: sha512-oNjBvzLq2GPZtJphCjLqXow/cHySHSgtxvKZb7OqSZ/xHgw6NWNhfad+6AB9cLeVm6eA9d/qMll3JdEHjy6M+A==}
+ peerDependencies:
+ css-tree: ^3.2.1
+ peerDependenciesMeta:
+ css-tree:
+ optional: true
+
+ '@csstools/css-tokenizer@4.0.0':
+ resolution: {integrity: sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==}
+ engines: {node: '>=20.19.0'}
+
'@emnapi/core@1.10.0':
resolution: {integrity: sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==}
@@ -345,6 +439,15 @@ packages:
cpu: [x64]
os: [win32]
+ '@exodus/bytes@1.15.1':
+ resolution: {integrity: sha512-S6mL0yNB/Abt9Ei4tq8gDhcczc4S3+vQ4ra7vxnAf+YHC02srtqxKKZghx2Dq6p0e66THKwR6r8N6P95wEty7Q==}
+ engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
+ peerDependencies:
+ '@noble/hashes': ^1.8.0 || ^2.0.0
+ peerDependenciesMeta:
+ '@noble/hashes':
+ optional: true
+
'@fastify/ajv-compiler@4.0.5':
resolution: {integrity: sha512-KoWKW+MhvfTRWL4qrhUwAAZoaChluo0m0vbiJlGMt2GXvL4LVPQEjt8kSpHI3IBq5Rez8fg+XeH3cneztq+C7A==}
@@ -369,6 +472,15 @@ packages:
'@fastify/websocket@11.2.0':
resolution: {integrity: sha512-3HrDPbAG1CzUCqnslgJxppvzaAZffieOVbLp1DAy1huCSynUWPifSvfdEDUR8HlJLp3sp1A36uOM2tJogADS8w==}
+ '@floating-ui/core@1.7.5':
+ resolution: {integrity: sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==}
+
+ '@floating-ui/dom@1.7.6':
+ resolution: {integrity: sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==}
+
+ '@floating-ui/utils@0.2.11':
+ resolution: {integrity: sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==}
+
'@jridgewell/sourcemap-codec@1.5.5':
resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==}
@@ -564,6 +676,165 @@ packages:
engines: {node: '>= 10'}
hasBin: true
+ '@tiptap/core@3.26.0':
+ resolution: {integrity: sha512-7jTed/RirIVsp+lLdLvGzGqF3EBGpnGHGYKOwz6t28V2BIJLAFdUhfEVdWie7xPxQNWK0TP+fPlsqZS0vxfHBg==}
+ peerDependencies:
+ '@tiptap/pm': 3.26.0
+
+ '@tiptap/extension-blockquote@3.27.1':
+ resolution: {integrity: sha512-VMF7xJx6qEGiX6DTKNiL31NLqypOcd/4sNjFSe8rb41PwejBJh/nOqVIbBvWkiT6NMGFLxMhj7zJ8/zPo1hXeg==}
+ peerDependencies:
+ '@tiptap/core': 3.27.1
+
+ '@tiptap/extension-bold@3.27.1':
+ resolution: {integrity: sha512-TlC5bsS+pqETTrlz4CZz9RO/cKBYtELGIxwtKeivUn3eNfnOxQbbu4WDsiwIfzRFyd0OMnKl6BPM2KnYEehoEQ==}
+ peerDependencies:
+ '@tiptap/core': 3.27.1
+
+ '@tiptap/extension-bubble-menu@3.27.1':
+ resolution: {integrity: sha512-j/j8Qp9Z5nViade2m7zjrO/CYH/Ca80Qj7aqo0eUaei6FZQ5izlF9o4XQU5EFMAutV6mwynsPUp8FVo5sCuYfw==}
+ peerDependencies:
+ '@tiptap/core': 3.27.1
+ '@tiptap/pm': 3.27.1
+
+ '@tiptap/extension-bullet-list@3.27.1':
+ resolution: {integrity: sha512-faCUHnRP47o9Zh9VZZX6EX/569udw9Vopm2PgEKPWuKLE2qaS5WBuUVU0iItdJmKUqaWiOZkpoW4jvnDmj0dfg==}
+ peerDependencies:
+ '@tiptap/extension-list': 3.27.1
+
+ '@tiptap/extension-code-block@3.27.1':
+ resolution: {integrity: sha512-pHlzmZx2OlHfyQ0yRlT5UL4mGokz947DthZuYefN1OleVqOkHpWBG+2JQwqoNq6bmzMne92zbH32rhcJUEYSjA==}
+ peerDependencies:
+ '@tiptap/core': 3.27.1
+ '@tiptap/pm': 3.27.1
+
+ '@tiptap/extension-code@3.27.1':
+ resolution: {integrity: sha512-epOUpFfEmBzjvnqvjv2qHX7NAuLo5dlOGV690lWu+sAYMjibuJBeVvAiKPyFCfRCCTUxdbDB3jbaOA1yEcEJ7w==}
+ peerDependencies:
+ '@tiptap/core': 3.27.1
+
+ '@tiptap/extension-document@3.27.1':
+ resolution: {integrity: sha512-8FbBTkfnRP4iVaoj+2h3iWa+H0eGDD3yTyVCwrmue/sQTkqUNUoSuAZa3GDG4Sd41xdPwTJxl9nUWGgM1qDCnw==}
+ peerDependencies:
+ '@tiptap/core': 3.27.1
+
+ '@tiptap/extension-dropcursor@3.27.1':
+ resolution: {integrity: sha512-blFf9x9RG0Qr7P3FoAH/033ffa+mMLZn34trVs8Vi0Ppk6FmJAg5HpYFOtmYoeREdNDJ5rHJKV7SoACbOHgskQ==}
+ peerDependencies:
+ '@tiptap/extensions': 3.27.1
+
+ '@tiptap/extension-floating-menu@3.27.1':
+ resolution: {integrity: sha512-BmJF1VqB7dSJkgAalrpVFj88WLhxKjcWPuWHOqf2ITrUU2832BhKLXKmxjWUy1gqV8PfNNVWtGfIERy7I0y0+Q==}
+ peerDependencies:
+ '@floating-ui/dom': ^1.0.0
+ '@tiptap/core': 3.27.1
+ '@tiptap/pm': 3.27.1
+
+ '@tiptap/extension-gapcursor@3.27.1':
+ resolution: {integrity: sha512-QoezN0wdvXIwLQ4ee2ccWDaX3RG0lzgQpIMpMz55oPDhpUVax1+19ApsS53LkcktpS4EbnPL4xO4DaJk0Vp7PQ==}
+ peerDependencies:
+ '@tiptap/extensions': 3.27.1
+
+ '@tiptap/extension-hard-break@3.27.1':
+ resolution: {integrity: sha512-iv/m9hzl6jfSj9Q8UEjAxONvCoUDaP7M9SRCPx3PaLNxA230TTD6RE0Ye4zFJ8ze7ZVoJJMAqg9Qpq1iYg2JOQ==}
+ peerDependencies:
+ '@tiptap/core': 3.27.1
+
+ '@tiptap/extension-heading@3.26.0':
+ resolution: {integrity: sha512-qenEQEgzE5FjQay/H6iKOnwIt6DPO27cS+v0mGhXmrL1MjrNER4X0ZkATJbVd0WA6ffsAGaP44NKYDworGeidw==}
+ peerDependencies:
+ '@tiptap/core': 3.26.0
+
+ '@tiptap/extension-horizontal-rule@3.27.1':
+ resolution: {integrity: sha512-QlKE7qn5qMnIGVGhXQlvYedvLtNJ9z0dmit5w8vPb8tKzW4Spk6M7N2kruprrDA8GBwHfeR5wmF+njfUm34qxg==}
+ peerDependencies:
+ '@tiptap/core': 3.27.1
+ '@tiptap/pm': 3.27.1
+
+ '@tiptap/extension-italic@3.27.1':
+ resolution: {integrity: sha512-jGGeyn9uRUnNjSTHpbqhiGsp6KaYTSbV09jDXPJI9cDwfV9hpugLvpaCZd0BMBbhU1B1W6kOfX0BE15qX/HQfA==}
+ peerDependencies:
+ '@tiptap/core': 3.27.1
+
+ '@tiptap/extension-link@3.27.1':
+ resolution: {integrity: sha512-/2jBfsxBZUDGJmpZifqRQPz7f1E5qpS1BckTZ39TADzUJX+feKy7RJ3DtQ02+8y6SSMzvP9loGVjrk6zEMTk4g==}
+ peerDependencies:
+ '@tiptap/core': 3.27.1
+ '@tiptap/pm': 3.27.1
+
+ '@tiptap/extension-list-item@3.27.1':
+ resolution: {integrity: sha512-zwRl01ETfCkWUvtvK5fw9bXtAajMPkvlkE3Cq6JvH3LF7XXJwDtNj5Tj7exacMpCaSZmlNc43vFb2rAYnrnwMA==}
+ peerDependencies:
+ '@tiptap/extension-list': 3.27.1
+
+ '@tiptap/extension-list-keymap@3.27.1':
+ resolution: {integrity: sha512-OIMZNlzPSO8WRd4ic73Fxckzl4N1tesjjLL2XApaNA/uMpO0LoF6WSRPAWv+Z24Wp92ARRJAnRP7iZoI5+Jxig==}
+ peerDependencies:
+ '@tiptap/extension-list': 3.27.1
+
+ '@tiptap/extension-list@3.26.0':
+ resolution: {integrity: sha512-EM8woyHDNKLEQ+lWUEoDtA4KrwP6fei/mYX1NxseMzKHHo7LFecx7wk6sovAXZrUvdML/yFBihgiMiO5VIsfkg==}
+ peerDependencies:
+ '@tiptap/core': 3.26.0
+ '@tiptap/pm': 3.26.0
+
+ '@tiptap/extension-ordered-list@3.27.1':
+ resolution: {integrity: sha512-GYrKqD//9nHJ2r80uXqbDMzRnFpGzbaEQRTSGaO/SH7DvXWFMow8evkOdjQ7PCQO07jNjJo75+A85Jwu3Ov3AA==}
+ peerDependencies:
+ '@tiptap/extension-list': 3.27.1
+
+ '@tiptap/extension-paragraph@3.27.1':
+ resolution: {integrity: sha512-7K7eo1gruOgAsnbK+GCV23AUVUI0cL1bTig8HaPneoFMVbig7vddk8jNLKBWO8TXVbG7TuHdnDN4F98vdtwh5Q==}
+ peerDependencies:
+ '@tiptap/core': 3.27.1
+
+ '@tiptap/extension-strike@3.27.1':
+ resolution: {integrity: sha512-Y3DW1jlSlCNCyMGHP3+3qBNNPS83wuFz4RTYGjZtvRRTCRh7apZme9XRWMq1rN5mJ2Cr7fKocA2/5Bs13KgN6Q==}
+ peerDependencies:
+ '@tiptap/core': 3.27.1
+
+ '@tiptap/extension-text@3.27.1':
+ resolution: {integrity: sha512-6ZwaZwSrDh+KFFv6V1J79oO37yPs7y1bFxvk1/9Ih2rn3Xr5AWz+eMS+n8RpH3djBVVAQpdIAeYQgcn+VCSsTg==}
+ peerDependencies:
+ '@tiptap/core': 3.27.1
+
+ '@tiptap/extension-underline@3.27.1':
+ resolution: {integrity: sha512-N889J4nXN/TPfVt8uF9N1A0SY82E90zwc1y26lqOcw6KWNLmQrlhMh/9OD4ikLDbekmFpOBq/UicpHf/6S8hbQ==}
+ peerDependencies:
+ '@tiptap/core': 3.27.1
+
+ '@tiptap/extensions@3.26.0':
+ resolution: {integrity: sha512-4wajuqnO2X0+LVvsBjW/xk3/tmdb16bNL939QhicAay4YYqXITeV2v3XJsryzmG4L5GkK1yLxvRGk4aLoxWrnA==}
+ peerDependencies:
+ '@tiptap/core': 3.26.0
+ '@tiptap/pm': 3.26.0
+
+ '@tiptap/markdown@3.26.0':
+ resolution: {integrity: sha512-jg5xrwl1gTXUl5JA3+g8YYfhOzplM9CVecwKZeFtlYtPLyxLCmIDvqV/vULoGu57HwtY4819nNpMZwY6jBNtrw==}
+ peerDependencies:
+ '@tiptap/core': 3.26.0
+ '@tiptap/pm': 3.26.0
+
+ '@tiptap/pm@3.26.0':
+ resolution: {integrity: sha512-q4RDeWwVrhOL0jJCGRgGxLSdjOYwzQ4h2InURZVhC66433ipcHd6f3bqSOhcXZ4r0sFmMNsuF7aZmUntjWLc7w==}
+
+ '@tiptap/starter-kit@3.26.0':
+ resolution: {integrity: sha512-o34EtMfqtBaljdmeElZsRG/067oGx9Zcq+j2GWo71KlZe22ga/ALexeTf1c+ETsjCxSTKR6eyQ4RZvz/2JpYfg==}
+
+ '@tiptap/suggestion@3.26.0':
+ resolution: {integrity: sha512-3jxBvjmfooQroR0eCw61kSgr+g90KFVD3dM5bANhpQbCpCYRydp/iXDQmpoPHjv8FpeU5JZgcnJ3R9vhjeIM2A==}
+ peerDependencies:
+ '@tiptap/core': 3.26.0
+ '@tiptap/pm': 3.26.0
+
+ '@tiptap/vue-3@3.26.0':
+ resolution: {integrity: sha512-ephdd355RvM69T2bjEHK1VPkAnZLuskwYZ8ta9L22iRLSIGpk1ERT9W+jOe6hYVyP4AiidOwK0yOvv1a5QiyJg==}
+ peerDependencies:
+ '@floating-ui/dom': ^1.0.0
+ '@tiptap/core': 3.26.0
+ '@tiptap/pm': 3.26.0
+ vue: ^3.0.0
+
'@tybys/wasm-util@0.10.2':
resolution: {integrity: sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==}
@@ -582,6 +853,9 @@ packages:
'@types/node@24.12.4':
resolution: {integrity: sha512-GUUEShf+PBCGW2KaXwcIt3Yk+e3pkKwWKb9GSyM9WQVE+ep2jzmHdGsHzu4wgcZy5fN9FBdVzjpBQsYlpfpgLA==}
+ '@types/textarea-caret@3.0.4':
+ resolution: {integrity: sha512-epJGYB37/sNrTDbhfyRjHkXsQSAcO6zby0JBDS0QMt6HQ1f1W2E4YpSc7TQkNmWaWmYXv92zOIfN5PHA8CmThg==}
+
'@types/ws@8.18.1':
resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==}
@@ -705,6 +979,9 @@ packages:
resolution: {integrity: sha512-CyzaZRQKyHkB2ZInfTTl2nvT33EbDpjkLEbE8/Zck3Ll6O0qqvuGdrJ45HgtH+HykRg88ITY3AdreBGN70aBSQ==}
engines: {node: 20.x || 22.x || 23.x || 24.x || 25.x || 26.x}
+ bidi-js@1.0.3:
+ resolution: {integrity: sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==}
+
bindings@1.5.0:
resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==}
@@ -748,9 +1025,20 @@ packages:
resolution: {integrity: sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==}
engines: {node: '>=18'}
+ css-tree@3.2.1:
+ resolution: {integrity: sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==}
+ engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0}
+
csstype@3.2.3:
resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==}
+ data-urls@7.0.0:
+ resolution: {integrity: sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA==}
+ engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
+
+ decimal.js@10.6.0:
+ resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==}
+
decompress-response@6.0.0:
resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==}
engines: {node: '>=10'}
@@ -876,6 +1164,10 @@ packages:
resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==}
engines: {node: '>=0.12'}
+ entities@8.0.0:
+ resolution: {integrity: sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA==}
+ engines: {node: '>=20.19.0'}
+
es-module-lexer@2.1.0:
resolution: {integrity: sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==}
@@ -961,6 +1253,10 @@ packages:
resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
engines: {node: '>=8'}
+ html-encoding-sniffer@6.0.0:
+ resolution: {integrity: sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==}
+ engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
+
ieee754@1.2.1:
resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
@@ -978,6 +1274,18 @@ packages:
resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
engines: {node: '>=8'}
+ is-potential-custom-element-name@1.0.1:
+ resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==}
+
+ jsdom@29.1.1:
+ resolution: {integrity: sha512-ECi4Fi2f7BdJtUKTflYRTiaMxIB0O6zfR1fX0GXpUrf6flp8QIYn1UT20YQqdSOfk2dfkCwS8LAFoJDEppNK5Q==}
+ engines: {node: ^20.19.0 || ^22.13.0 || >=24.0.0}
+ peerDependencies:
+ canvas: ^3.0.0
+ peerDependenciesMeta:
+ canvas:
+ optional: true
+
json-schema-ref-resolver@3.0.0:
resolution: {integrity: sha512-hOrZIVL5jyYFjzk7+y7n5JDzGlU8rfWDuYyHwGa2WA8/pcmMHezp2xsVwxrebD/Q9t8Nc5DboieySDpCp4WG4A==}
@@ -1061,9 +1369,24 @@ packages:
resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==}
engines: {node: '>= 12.0.0'}
+ linkifyjs@4.3.3:
+ resolution: {integrity: sha512-P8aEP5U/D1/IlTY2OeYsErdwh9bGuLE30NcXtKEjgdHcahveQoQwM2yZNsioQHsWFz0P7KKudisbrzCgR0sDHg==}
+
+ lru-cache@11.5.1:
+ resolution: {integrity: sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A==}
+ engines: {node: 20 || >=22}
+
magic-string@0.30.21:
resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==}
+ marked@17.0.6:
+ resolution: {integrity: sha512-gB0gkNafnonOw0obSTEGZTT86IuhILt2Wfx0mWH/1Au83kybTayroZ/V6nS25mN7u8ASy+5fMhgB3XPNrOZdmA==}
+ engines: {node: '>= 20'}
+ hasBin: true
+
+ mdn-data@2.27.1:
+ resolution: {integrity: sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==}
+
mimic-response@3.1.0:
resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==}
engines: {node: '>=10'}
@@ -1099,6 +1422,12 @@ packages:
once@1.4.0:
resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
+ orderedmap@2.1.1:
+ resolution: {integrity: sha512-TvAWxi0nDe1j/rtMcWcIj94+Ffe6n7zhow33h40SKxmsmozs6dz/e+EajymfoFcHd7sxNn8yHM8839uixMOV6g==}
+
+ parse5@8.0.1:
+ resolution: {integrity: sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==}
+
path-browserify@1.0.1:
resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==}
@@ -1138,9 +1467,52 @@ packages:
process-warning@5.0.0:
resolution: {integrity: sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==}
+ prosemirror-changeset@2.4.1:
+ resolution: {integrity: sha512-96WBLhOaYhJ+kPhLg3uW359Tz6I/MfcrQfL4EGv4SrcqKEMC1gmoGrXHecPE8eOwTVCJ4IwgfzM8fFad25wNfw==}
+
+ prosemirror-commands@1.7.1:
+ resolution: {integrity: sha512-rT7qZnQtx5c0/y/KlYaGvtG411S97UaL6gdp6RIZ23DLHanMYLyfGBV5DtSnZdthQql7W+lEVbpSfwtO8T+L2w==}
+
+ prosemirror-dropcursor@1.8.2:
+ resolution: {integrity: sha512-CCk6Gyx9+Tt2sbYk5NK0nB1ukHi2ryaRgadV/LvyNuO3ena1payM2z6Cg0vO1ebK8cxbzo41ku2DE5Axj1Zuiw==}
+
+ prosemirror-gapcursor@1.4.1:
+ resolution: {integrity: sha512-pMdYaEnjNMSwl11yjEGtgTmLkR08m/Vl+Jj443167p9eB3HVQKhYCc4gmHVDsLPODfZfjr/MmirsdyZziXbQKw==}
+
+ prosemirror-history@1.5.0:
+ resolution: {integrity: sha512-zlzTiH01eKA55UAf1MEjtssJeHnGxO0j4K4Dpx+gnmX9n+SHNlDqI2oO1Kv1iPN5B1dm5fsljCfqKF9nFL6HRg==}
+
+ prosemirror-inputrules@1.5.1:
+ resolution: {integrity: sha512-7wj4uMjKaXWAQ1CDgxNzNtR9AlsuwzHfdFH1ygEHA2KHF2DOEaXl1CJfNPAKCg9qNEh4rum975QLaCiQPyY6Fw==}
+
+ prosemirror-keymap@1.2.3:
+ resolution: {integrity: sha512-4HucRlpiLd1IPQQXNqeo81BGtkY8Ai5smHhKW9jjPKRc2wQIxksg7Hl1tTI2IfT2B/LgX6bfYvXxEpJl7aKYKw==}
+
+ prosemirror-model@1.25.9:
+ resolution: {integrity: sha512-pRTklkDDMMRopyoAcrr9wV/8g/RYgrLHBuJAb5hlEuYZRdm5yqmPjWId83fpBwPpSFqEdja0H7Dfd7z1X/npcA==}
+
+ prosemirror-schema-list@1.5.1:
+ resolution: {integrity: sha512-927lFx/uwyQaGwJxLWCZRkjXG0p48KpMj6ueoYiu4JX05GGuGcgzAy62dfiV8eFZftgyBUvLx76RsMe20fJl+Q==}
+
+ prosemirror-state@1.4.4:
+ resolution: {integrity: sha512-6jiYHH2CIGbCfnxdHbXZ12gySFY/fz/ulZE333G6bPqIZ4F+TXo9ifiR86nAHpWnfoNjOb3o5ESi7J8Uz1jXHw==}
+
+ prosemirror-tables@1.8.5:
+ resolution: {integrity: sha512-V/0cDCsHKHe/tfWkeCmthNUcEp1IVO3p6vwN8XtwE9PZQLAZJigbw3QoraAdfJPir4NKJtNvOB8oYGKRl+t0Dw==}
+
+ prosemirror-transform@1.12.0:
+ resolution: {integrity: sha512-GxboyN4AMIsoHNtz5uf2r2Ru551i5hWeCMD6E2Ib4Eogqoub0NflniaBPVQ4MrGE5yZ8JV9tUHg9qcZTTrcN4w==}
+
+ prosemirror-view@1.41.9:
+ resolution: {integrity: sha512-clTunTX+eaLbr87L1V1QPheRlEQJyTlL3gXe9x3jQIk3rL0RVWxviDGz8tFaydwIVm+hKhYCyr+R/zBtWr9s6A==}
+
pump@3.0.4:
resolution: {integrity: sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==}
+ punycode@2.3.1:
+ resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
+ engines: {node: '>=6'}
+
quick-format-unescaped@4.0.4:
resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==}
@@ -1183,6 +1555,9 @@ packages:
engines: {node: ^20.19.0 || >=22.12.0}
hasBin: true
+ rope-sequence@1.3.4:
+ resolution: {integrity: sha512-UT5EDe2cu2E/6O4igUr5PSFs23nvvukicWHx6GnOPlHAiiYbzNuCRQCuiUdHJQcqKalLKlrYJnjY0ySGsXNQXQ==}
+
rxjs@7.8.2:
resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==}
@@ -1197,6 +1572,10 @@ packages:
resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==}
engines: {node: '>=10'}
+ saxes@6.0.0:
+ resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==}
+ engines: {node: '>=v12.22.7'}
+
secure-json-parse@4.1.0:
resolution: {integrity: sha512-l4KnYfEyqYJxDwlNVyRfO2E4NTHfMKAWdUuA8J0yve2Dz/E/PdBepY03RvyJpssIpRFwJoCD55wA+mEDs6ByWA==}
@@ -1264,6 +1643,9 @@ packages:
resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==}
engines: {node: '>=10'}
+ symbol-tree@3.2.4:
+ resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==}
+
tar-fs@2.1.4:
resolution: {integrity: sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==}
@@ -1271,6 +1653,9 @@ packages:
resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==}
engines: {node: '>=6'}
+ textarea-caret@3.1.0:
+ resolution: {integrity: sha512-cXAvzO9pP5CGa6NKx0WYHl+8CHKZs8byMkt3PCJBCmq2a34YA9pO1NrQET5pzeqnBjBdToF5No4rrmkDUgQC2Q==}
+
thread-stream@4.2.0:
resolution: {integrity: sha512-e2zZ96wSChazBsbENf/Pcm/4swHt2cEKQ92rhUjkL9GCKiTDJIaTBenjE/m9DXi0QBmTMDkFDdOomUy20A1tDQ==}
engines: {node: '>=20'}
@@ -1290,10 +1675,25 @@ packages:
resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==}
engines: {node: '>=14.0.0'}
+ tldts-core@7.4.3:
+ resolution: {integrity: sha512-27ep5H9PzdBrNd5OFM/j3WCU8F3kPwM9D0BOaOf7uYfxMJfyr0K5Tjj69Gri+sZlh2WXd5buIm47NuPF29CDiw==}
+
+ tldts@7.4.3:
+ resolution: {integrity: sha512-A3BDQBeeukYPzB4QdQ1DtdlUmp4x2OCH8n5UVhEWbyANxNep8GavottKzd1xYKFJKjUgMyPT7EzOfnBO55s8Sg==}
+ hasBin: true
+
toad-cache@3.7.1:
resolution: {integrity: sha512-5DXWzE4Vz7xNHsv+xQ+MGfJYyC78Aok3tEr0MNwHoRf7vZnga1mQXZ4/Nsodld4VR6Wd+VhfmqnNrsRJyYPfrQ==}
engines: {node: '>=20'}
+ tough-cookie@6.0.1:
+ resolution: {integrity: sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw==}
+ engines: {node: '>=16'}
+
+ tr46@6.0.0:
+ resolution: {integrity: sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==}
+ engines: {node: '>=20'}
+
tree-kill@1.2.2:
resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==}
hasBin: true
@@ -1317,6 +1717,10 @@ packages:
undici-types@7.16.0:
resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==}
+ undici@7.28.0:
+ resolution: {integrity: sha512-cRZYrTDwWznlnRiPjggAGxZXanty6M8RV1ff8Wm4LWXBp7/IG8v5DnOm74DtUBp9OONpK75YlPnIjQqX0dBDtA==}
+ engines: {node: '>=20.18.1'}
+
util-deprecate@1.0.2:
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
@@ -1421,6 +1825,25 @@ packages:
typescript:
optional: true
+ w3c-keyname@2.2.8:
+ resolution: {integrity: sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==}
+
+ w3c-xmlserializer@5.0.0:
+ resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==}
+ engines: {node: '>=18'}
+
+ webidl-conversions@8.0.1:
+ resolution: {integrity: sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==}
+ engines: {node: '>=20'}
+
+ whatwg-mimetype@5.0.0:
+ resolution: {integrity: sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==}
+ engines: {node: '>=20'}
+
+ whatwg-url@16.0.1:
+ resolution: {integrity: sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==}
+ engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
+
why-is-node-running@2.3.0:
resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==}
engines: {node: '>=8'}
@@ -1445,6 +1868,13 @@ packages:
utf-8-validate:
optional: true
+ xml-name-validator@5.0.0:
+ resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==}
+ engines: {node: '>=18'}
+
+ xmlchars@2.2.0:
+ resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==}
+
y18n@5.0.8:
resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
engines: {node: '>=10'}
@@ -1459,6 +1889,26 @@ packages:
snapshots:
+ '@asamuzakjp/css-color@5.1.11':
+ dependencies:
+ '@asamuzakjp/generational-cache': 1.0.1
+ '@csstools/css-calc': 3.2.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)
+ '@csstools/css-color-parser': 4.1.7(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)
+ '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0)
+ '@csstools/css-tokenizer': 4.0.0
+
+ '@asamuzakjp/dom-selector@7.1.1':
+ dependencies:
+ '@asamuzakjp/generational-cache': 1.0.1
+ '@asamuzakjp/nwsapi': 2.3.9
+ bidi-js: 1.0.3
+ css-tree: 3.2.1
+ is-potential-custom-element-name: 1.0.1
+
+ '@asamuzakjp/generational-cache@1.0.1': {}
+
+ '@asamuzakjp/nwsapi@2.3.9': {}
+
'@babel/helper-string-parser@7.27.1': {}
'@babel/helper-validator-identifier@7.28.5': {}
@@ -1472,6 +1922,34 @@ snapshots:
'@babel/helper-string-parser': 7.27.1
'@babel/helper-validator-identifier': 7.28.5
+ '@bramus/specificity@2.4.2':
+ dependencies:
+ css-tree: 3.2.1
+
+ '@csstools/color-helpers@6.0.2': {}
+
+ '@csstools/css-calc@3.2.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)':
+ dependencies:
+ '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0)
+ '@csstools/css-tokenizer': 4.0.0
+
+ '@csstools/css-color-parser@4.1.7(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)':
+ dependencies:
+ '@csstools/color-helpers': 6.0.2
+ '@csstools/css-calc': 3.2.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)
+ '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0)
+ '@csstools/css-tokenizer': 4.0.0
+
+ '@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0)':
+ dependencies:
+ '@csstools/css-tokenizer': 4.0.0
+
+ '@csstools/css-syntax-patches-for-csstree@1.1.5(css-tree@3.2.1)':
+ optionalDependencies:
+ css-tree: 3.2.1
+
+ '@csstools/css-tokenizer@4.0.0': {}
+
'@emnapi/core@1.10.0':
dependencies:
'@emnapi/wasi-threads': 1.2.1
@@ -1566,6 +2044,8 @@ snapshots:
'@esbuild/win32-x64@0.28.0':
optional: true
+ '@exodus/bytes@1.15.1': {}
+
'@fastify/ajv-compiler@4.0.5':
dependencies:
ajv: 8.20.0
@@ -1603,6 +2083,17 @@ snapshots:
- bufferutil
- utf-8-validate
+ '@floating-ui/core@1.7.5':
+ dependencies:
+ '@floating-ui/utils': 0.2.11
+
+ '@floating-ui/dom@1.7.6':
+ dependencies:
+ '@floating-ui/core': 1.7.5
+ '@floating-ui/utils': 0.2.11
+
+ '@floating-ui/utils@0.2.11': {}
+
'@jridgewell/sourcemap-codec@1.5.5': {}
'@napi-rs/wasm-runtime@1.1.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)':
@@ -1718,6 +2209,182 @@ snapshots:
'@tauri-apps/cli-win32-ia32-msvc': 2.11.2
'@tauri-apps/cli-win32-x64-msvc': 2.11.2
+ '@tiptap/core@3.26.0(@tiptap/pm@3.26.0)':
+ dependencies:
+ '@tiptap/pm': 3.26.0
+
+ '@tiptap/extension-blockquote@3.27.1(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))':
+ dependencies:
+ '@tiptap/core': 3.26.0(@tiptap/pm@3.26.0)
+
+ '@tiptap/extension-bold@3.27.1(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))':
+ dependencies:
+ '@tiptap/core': 3.26.0(@tiptap/pm@3.26.0)
+
+ '@tiptap/extension-bubble-menu@3.27.1(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))(@tiptap/pm@3.26.0)':
+ dependencies:
+ '@floating-ui/dom': 1.7.6
+ '@tiptap/core': 3.26.0(@tiptap/pm@3.26.0)
+ '@tiptap/pm': 3.26.0
+ optional: true
+
+ '@tiptap/extension-bullet-list@3.27.1(@tiptap/extension-list@3.26.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))(@tiptap/pm@3.26.0))':
+ dependencies:
+ '@tiptap/extension-list': 3.26.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))(@tiptap/pm@3.26.0)
+
+ '@tiptap/extension-code-block@3.27.1(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))(@tiptap/pm@3.26.0)':
+ dependencies:
+ '@tiptap/core': 3.26.0(@tiptap/pm@3.26.0)
+ '@tiptap/pm': 3.26.0
+
+ '@tiptap/extension-code@3.27.1(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))':
+ dependencies:
+ '@tiptap/core': 3.26.0(@tiptap/pm@3.26.0)
+
+ '@tiptap/extension-document@3.27.1(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))':
+ dependencies:
+ '@tiptap/core': 3.26.0(@tiptap/pm@3.26.0)
+
+ '@tiptap/extension-dropcursor@3.27.1(@tiptap/extensions@3.26.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))(@tiptap/pm@3.26.0))':
+ dependencies:
+ '@tiptap/extensions': 3.26.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))(@tiptap/pm@3.26.0)
+
+ '@tiptap/extension-floating-menu@3.27.1(@floating-ui/dom@1.7.6)(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))(@tiptap/pm@3.26.0)':
+ dependencies:
+ '@floating-ui/dom': 1.7.6
+ '@tiptap/core': 3.26.0(@tiptap/pm@3.26.0)
+ '@tiptap/pm': 3.26.0
+ optional: true
+
+ '@tiptap/extension-gapcursor@3.27.1(@tiptap/extensions@3.26.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))(@tiptap/pm@3.26.0))':
+ dependencies:
+ '@tiptap/extensions': 3.26.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))(@tiptap/pm@3.26.0)
+
+ '@tiptap/extension-hard-break@3.27.1(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))':
+ dependencies:
+ '@tiptap/core': 3.26.0(@tiptap/pm@3.26.0)
+
+ '@tiptap/extension-heading@3.26.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))':
+ dependencies:
+ '@tiptap/core': 3.26.0(@tiptap/pm@3.26.0)
+
+ '@tiptap/extension-horizontal-rule@3.27.1(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))(@tiptap/pm@3.26.0)':
+ dependencies:
+ '@tiptap/core': 3.26.0(@tiptap/pm@3.26.0)
+ '@tiptap/pm': 3.26.0
+
+ '@tiptap/extension-italic@3.27.1(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))':
+ dependencies:
+ '@tiptap/core': 3.26.0(@tiptap/pm@3.26.0)
+
+ '@tiptap/extension-link@3.27.1(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))(@tiptap/pm@3.26.0)':
+ dependencies:
+ '@tiptap/core': 3.26.0(@tiptap/pm@3.26.0)
+ '@tiptap/pm': 3.26.0
+ linkifyjs: 4.3.3
+
+ '@tiptap/extension-list-item@3.27.1(@tiptap/extension-list@3.26.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))(@tiptap/pm@3.26.0))':
+ dependencies:
+ '@tiptap/extension-list': 3.26.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))(@tiptap/pm@3.26.0)
+
+ '@tiptap/extension-list-keymap@3.27.1(@tiptap/extension-list@3.26.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))(@tiptap/pm@3.26.0))':
+ dependencies:
+ '@tiptap/extension-list': 3.26.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))(@tiptap/pm@3.26.0)
+
+ '@tiptap/extension-list@3.26.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))(@tiptap/pm@3.26.0)':
+ dependencies:
+ '@tiptap/core': 3.26.0(@tiptap/pm@3.26.0)
+ '@tiptap/pm': 3.26.0
+
+ '@tiptap/extension-ordered-list@3.27.1(@tiptap/extension-list@3.26.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))(@tiptap/pm@3.26.0))':
+ dependencies:
+ '@tiptap/extension-list': 3.26.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))(@tiptap/pm@3.26.0)
+
+ '@tiptap/extension-paragraph@3.27.1(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))':
+ dependencies:
+ '@tiptap/core': 3.26.0(@tiptap/pm@3.26.0)
+
+ '@tiptap/extension-strike@3.27.1(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))':
+ dependencies:
+ '@tiptap/core': 3.26.0(@tiptap/pm@3.26.0)
+
+ '@tiptap/extension-text@3.27.1(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))':
+ dependencies:
+ '@tiptap/core': 3.26.0(@tiptap/pm@3.26.0)
+
+ '@tiptap/extension-underline@3.27.1(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))':
+ dependencies:
+ '@tiptap/core': 3.26.0(@tiptap/pm@3.26.0)
+
+ '@tiptap/extensions@3.26.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))(@tiptap/pm@3.26.0)':
+ dependencies:
+ '@tiptap/core': 3.26.0(@tiptap/pm@3.26.0)
+ '@tiptap/pm': 3.26.0
+
+ '@tiptap/markdown@3.26.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))(@tiptap/pm@3.26.0)':
+ dependencies:
+ '@tiptap/core': 3.26.0(@tiptap/pm@3.26.0)
+ '@tiptap/pm': 3.26.0
+ marked: 17.0.6
+
+ '@tiptap/pm@3.26.0':
+ dependencies:
+ prosemirror-changeset: 2.4.1
+ prosemirror-commands: 1.7.1
+ prosemirror-dropcursor: 1.8.2
+ prosemirror-gapcursor: 1.4.1
+ prosemirror-history: 1.5.0
+ prosemirror-inputrules: 1.5.1
+ prosemirror-keymap: 1.2.3
+ prosemirror-model: 1.25.9
+ prosemirror-schema-list: 1.5.1
+ prosemirror-state: 1.4.4
+ prosemirror-tables: 1.8.5
+ prosemirror-transform: 1.12.0
+ prosemirror-view: 1.41.9
+
+ '@tiptap/starter-kit@3.26.0':
+ dependencies:
+ '@tiptap/core': 3.26.0(@tiptap/pm@3.26.0)
+ '@tiptap/extension-blockquote': 3.27.1(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))
+ '@tiptap/extension-bold': 3.27.1(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))
+ '@tiptap/extension-bullet-list': 3.27.1(@tiptap/extension-list@3.26.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))(@tiptap/pm@3.26.0))
+ '@tiptap/extension-code': 3.27.1(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))
+ '@tiptap/extension-code-block': 3.27.1(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))(@tiptap/pm@3.26.0)
+ '@tiptap/extension-document': 3.27.1(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))
+ '@tiptap/extension-dropcursor': 3.27.1(@tiptap/extensions@3.26.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))(@tiptap/pm@3.26.0))
+ '@tiptap/extension-gapcursor': 3.27.1(@tiptap/extensions@3.26.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))(@tiptap/pm@3.26.0))
+ '@tiptap/extension-hard-break': 3.27.1(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))
+ '@tiptap/extension-heading': 3.26.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))
+ '@tiptap/extension-horizontal-rule': 3.27.1(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))(@tiptap/pm@3.26.0)
+ '@tiptap/extension-italic': 3.27.1(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))
+ '@tiptap/extension-link': 3.27.1(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))(@tiptap/pm@3.26.0)
+ '@tiptap/extension-list': 3.26.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))(@tiptap/pm@3.26.0)
+ '@tiptap/extension-list-item': 3.27.1(@tiptap/extension-list@3.26.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))(@tiptap/pm@3.26.0))
+ '@tiptap/extension-list-keymap': 3.27.1(@tiptap/extension-list@3.26.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))(@tiptap/pm@3.26.0))
+ '@tiptap/extension-ordered-list': 3.27.1(@tiptap/extension-list@3.26.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))(@tiptap/pm@3.26.0))
+ '@tiptap/extension-paragraph': 3.27.1(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))
+ '@tiptap/extension-strike': 3.27.1(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))
+ '@tiptap/extension-text': 3.27.1(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))
+ '@tiptap/extension-underline': 3.27.1(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))
+ '@tiptap/extensions': 3.26.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))(@tiptap/pm@3.26.0)
+ '@tiptap/pm': 3.26.0
+
+ '@tiptap/suggestion@3.26.0(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))(@tiptap/pm@3.26.0)':
+ dependencies:
+ '@tiptap/core': 3.26.0(@tiptap/pm@3.26.0)
+ '@tiptap/pm': 3.26.0
+
+ '@tiptap/vue-3@3.26.0(@floating-ui/dom@1.7.6)(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))(@tiptap/pm@3.26.0)(vue@3.5.34(typescript@6.0.3))':
+ dependencies:
+ '@floating-ui/dom': 1.7.6
+ '@tiptap/core': 3.26.0(@tiptap/pm@3.26.0)
+ '@tiptap/pm': 3.26.0
+ vue: 3.5.34(typescript@6.0.3)
+ optionalDependencies:
+ '@tiptap/extension-bubble-menu': 3.27.1(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))(@tiptap/pm@3.26.0)
+ '@tiptap/extension-floating-menu': 3.27.1(@floating-ui/dom@1.7.6)(@tiptap/core@3.26.0(@tiptap/pm@3.26.0))(@tiptap/pm@3.26.0)
+
'@tybys/wasm-util@0.10.2':
dependencies:
tslib: 2.8.1
@@ -1740,6 +2407,8 @@ snapshots:
dependencies:
undici-types: 7.16.0
+ '@types/textarea-caret@3.0.4': {}
+
'@types/ws@8.18.1':
dependencies:
'@types/node': 24.12.4
@@ -1904,6 +2573,10 @@ snapshots:
bindings: 1.5.0
prebuild-install: 7.1.3
+ bidi-js@1.0.3:
+ dependencies:
+ require-from-string: 2.0.2
+
bindings@1.5.0:
dependencies:
file-uri-to-path: 1.0.0
@@ -1953,8 +2626,22 @@ snapshots:
cookie@1.1.1: {}
+ css-tree@3.2.1:
+ dependencies:
+ mdn-data: 2.27.1
+ source-map-js: 1.2.1
+
csstype@3.2.3: {}
+ data-urls@7.0.0:
+ dependencies:
+ whatwg-mimetype: 5.0.0
+ whatwg-url: 16.0.1
+ transitivePeerDependencies:
+ - '@noble/hashes'
+
+ decimal.js@10.6.0: {}
+
decompress-response@6.0.0:
dependencies:
mimic-response: 3.1.0
@@ -1987,6 +2674,8 @@ snapshots:
entities@7.0.1: {}
+ entities@8.0.0: {}
+
es-module-lexer@2.1.0: {}
esbuild@0.28.0:
@@ -2096,6 +2785,12 @@ snapshots:
has-flag@4.0.0: {}
+ html-encoding-sniffer@6.0.0:
+ dependencies:
+ '@exodus/bytes': 1.15.1
+ transitivePeerDependencies:
+ - '@noble/hashes'
+
ieee754@1.2.1: {}
inherits@2.0.4: {}
@@ -2106,6 +2801,34 @@ snapshots:
is-fullwidth-code-point@3.0.0: {}
+ is-potential-custom-element-name@1.0.1: {}
+
+ jsdom@29.1.1:
+ dependencies:
+ '@asamuzakjp/css-color': 5.1.11
+ '@asamuzakjp/dom-selector': 7.1.1
+ '@bramus/specificity': 2.4.2
+ '@csstools/css-syntax-patches-for-csstree': 1.1.5(css-tree@3.2.1)
+ '@exodus/bytes': 1.15.1
+ css-tree: 3.2.1
+ data-urls: 7.0.0
+ decimal.js: 10.6.0
+ html-encoding-sniffer: 6.0.0
+ is-potential-custom-element-name: 1.0.1
+ lru-cache: 11.5.1
+ parse5: 8.0.1
+ saxes: 6.0.0
+ symbol-tree: 3.2.4
+ tough-cookie: 6.0.1
+ undici: 7.28.0
+ w3c-xmlserializer: 5.0.0
+ webidl-conversions: 8.0.1
+ whatwg-mimetype: 5.0.0
+ whatwg-url: 16.0.1
+ xml-name-validator: 5.0.0
+ transitivePeerDependencies:
+ - '@noble/hashes'
+
json-schema-ref-resolver@3.0.0:
dependencies:
dequal: 2.0.3
@@ -2167,10 +2890,18 @@ snapshots:
lightningcss-win32-arm64-msvc: 1.32.0
lightningcss-win32-x64-msvc: 1.32.0
+ linkifyjs@4.3.3: {}
+
+ lru-cache@11.5.1: {}
+
magic-string@0.30.21:
dependencies:
'@jridgewell/sourcemap-codec': 1.5.5
+ marked@17.0.6: {}
+
+ mdn-data@2.27.1: {}
+
mimic-response@3.1.0: {}
minimist@1.2.8: {}
@@ -2195,6 +2926,12 @@ snapshots:
dependencies:
wrappy: 1.0.2
+ orderedmap@2.1.1: {}
+
+ parse5@8.0.1:
+ dependencies:
+ entities: 8.0.0
+
path-browserify@1.0.1: {}
pathe@2.0.3: {}
@@ -2248,11 +2985,87 @@ snapshots:
process-warning@5.0.0: {}
+ prosemirror-changeset@2.4.1:
+ dependencies:
+ prosemirror-transform: 1.12.0
+
+ prosemirror-commands@1.7.1:
+ dependencies:
+ prosemirror-model: 1.25.9
+ prosemirror-state: 1.4.4
+ prosemirror-transform: 1.12.0
+
+ prosemirror-dropcursor@1.8.2:
+ dependencies:
+ prosemirror-state: 1.4.4
+ prosemirror-transform: 1.12.0
+ prosemirror-view: 1.41.9
+
+ prosemirror-gapcursor@1.4.1:
+ dependencies:
+ prosemirror-keymap: 1.2.3
+ prosemirror-model: 1.25.9
+ prosemirror-state: 1.4.4
+ prosemirror-view: 1.41.9
+
+ prosemirror-history@1.5.0:
+ dependencies:
+ prosemirror-state: 1.4.4
+ prosemirror-transform: 1.12.0
+ prosemirror-view: 1.41.9
+ rope-sequence: 1.3.4
+
+ prosemirror-inputrules@1.5.1:
+ dependencies:
+ prosemirror-state: 1.4.4
+ prosemirror-transform: 1.12.0
+
+ prosemirror-keymap@1.2.3:
+ dependencies:
+ prosemirror-state: 1.4.4
+ w3c-keyname: 2.2.8
+
+ prosemirror-model@1.25.9:
+ dependencies:
+ orderedmap: 2.1.1
+
+ prosemirror-schema-list@1.5.1:
+ dependencies:
+ prosemirror-model: 1.25.9
+ prosemirror-state: 1.4.4
+ prosemirror-transform: 1.12.0
+
+ prosemirror-state@1.4.4:
+ dependencies:
+ prosemirror-model: 1.25.9
+ prosemirror-transform: 1.12.0
+ prosemirror-view: 1.41.9
+
+ prosemirror-tables@1.8.5:
+ dependencies:
+ prosemirror-keymap: 1.2.3
+ prosemirror-model: 1.25.9
+ prosemirror-state: 1.4.4
+ prosemirror-transform: 1.12.0
+ prosemirror-view: 1.41.9
+
+ prosemirror-transform@1.12.0:
+ dependencies:
+ prosemirror-model: 1.25.9
+
+ prosemirror-view@1.41.9:
+ dependencies:
+ prosemirror-model: 1.25.9
+ prosemirror-state: 1.4.4
+ prosemirror-transform: 1.12.0
+
pump@3.0.4:
dependencies:
end-of-stream: 1.4.5
once: 1.4.0
+ punycode@2.3.1: {}
+
quick-format-unescaped@4.0.4: {}
rc@1.2.8:
@@ -2303,6 +3116,8 @@ snapshots:
'@rolldown/binding-win32-arm64-msvc': 1.0.2
'@rolldown/binding-win32-x64-msvc': 1.0.2
+ rope-sequence@1.3.4: {}
+
rxjs@7.8.2:
dependencies:
tslib: 2.8.1
@@ -2315,6 +3130,10 @@ snapshots:
safe-stable-stringify@2.5.0: {}
+ saxes@6.0.0:
+ dependencies:
+ xmlchars: 2.2.0
+
secure-json-parse@4.1.0: {}
semver@7.8.1: {}
@@ -2371,6 +3190,8 @@ snapshots:
dependencies:
has-flag: 4.0.0
+ symbol-tree@3.2.4: {}
+
tar-fs@2.1.4:
dependencies:
chownr: 1.1.4
@@ -2386,6 +3207,8 @@ snapshots:
inherits: 2.0.4
readable-stream: 3.6.2
+ textarea-caret@3.1.0: {}
+
thread-stream@4.2.0:
dependencies:
real-require: 1.0.0
@@ -2401,8 +3224,22 @@ snapshots:
tinyrainbow@3.1.0: {}
+ tldts-core@7.4.3: {}
+
+ tldts@7.4.3:
+ dependencies:
+ tldts-core: 7.4.3
+
toad-cache@3.7.1: {}
+ tough-cookie@6.0.1:
+ dependencies:
+ tldts: 7.4.3
+
+ tr46@6.0.0:
+ dependencies:
+ punycode: 2.3.1
+
tree-kill@1.2.2: {}
tslib@2.8.1: {}
@@ -2421,6 +3258,8 @@ snapshots:
undici-types@7.16.0: {}
+ undici@7.28.0: {}
+
util-deprecate@1.0.2: {}
vite@8.0.14(@types/node@24.12.4)(esbuild@0.28.0)(tsx@4.22.3):
@@ -2436,7 +3275,7 @@ snapshots:
fsevents: 2.3.3
tsx: 4.22.3
- vitest@4.1.7(@types/node@24.12.4)(vite@8.0.14(@types/node@24.12.4)(esbuild@0.28.0)(tsx@4.22.3)):
+ vitest@4.1.7(@types/node@24.12.4)(jsdom@29.1.1)(vite@8.0.14(@types/node@24.12.4)(esbuild@0.28.0)(tsx@4.22.3)):
dependencies:
'@vitest/expect': 4.1.7
'@vitest/mocker': 4.1.7(vite@8.0.14(@types/node@24.12.4)(esbuild@0.28.0)(tsx@4.22.3))
@@ -2460,6 +3299,7 @@ snapshots:
why-is-node-running: 2.3.0
optionalDependencies:
'@types/node': 24.12.4
+ jsdom: 29.1.1
transitivePeerDependencies:
- msw
@@ -2481,6 +3321,24 @@ snapshots:
optionalDependencies:
typescript: 6.0.3
+ w3c-keyname@2.2.8: {}
+
+ w3c-xmlserializer@5.0.0:
+ dependencies:
+ xml-name-validator: 5.0.0
+
+ webidl-conversions@8.0.1: {}
+
+ whatwg-mimetype@5.0.0: {}
+
+ whatwg-url@16.0.1:
+ dependencies:
+ '@exodus/bytes': 1.15.1
+ tr46: 6.0.0
+ webidl-conversions: 8.0.1
+ transitivePeerDependencies:
+ - '@noble/hashes'
+
why-is-node-running@2.3.0:
dependencies:
siginfo: 2.0.0
@@ -2496,6 +3354,10 @@ snapshots:
ws@8.21.0: {}
+ xml-name-validator@5.0.0: {}
+
+ xmlchars@2.2.0: {}
+
y18n@5.0.8: {}
yargs-parser@21.1.1: {}