diff --git a/README.md b/README.md index 34dd081..289fdac 100644 --- a/README.md +++ b/README.md @@ -4,17 +4,26 @@ English | 中文 -## Latest Version -[0.5.0](https://github.com/infiolab/infio-copilot/releases/tag/0.5.0) Enhanced performance and stability improvements, added MCP support +## ✨ What's New +[0.7.2](https://github.com/infiolab/infio-copilot/releases/tag/0.7.2) +We're excited to announce a major update packed with new features to streamline your workflow and supercharge your knowledge management within Obsidian. +--- -## Recent Updates -[0.2.4](https://github.com/infiolab/infio-copilot/releases/tag/0.2.4) Added multilingual support +* **🚀 Out-of-the-Box Embedding Model** +To help you get started faster, we now include a default local embedding model (`bge-micro-v2`). No more manual setup is required to use powerful semantic features! -[0.2.3](https://github.com/infiolab/infio-copilot/releases/tag/0.2.3) Add custom mode config, you can create you own agent now +* **🗂️ Workspaces** +Organize your projects, research, and personal notes with the new **Workspaces** feature. Keep your context clean and switch between different setups seamlessly. -[0.1.7](https://github.com/infiolab/infio-copilot/releases/tag/0.1.7) Added image selector modal, allowing users to search, select, and upload images in obsidian vault or local file browser +* **💡 Insights** +Go beyond simple notes with our new **Insights** feature. Synthesize information, discover connections, and gain a deeper understanding of your knowledge base. + +* **🔍 Advanced Multi-Dimensional Queries** +Converse with your notes! You can now perform complex queries based on various dimensions like time, tasks, and other metadata. Finding the exact piece of information has never been easier. + +* **✍️ New "Write" Mode** +We've rebuilt our **Write** mode from the ground up to provide a more intuitive, powerful, and distraction-free writing experience. -[0.1.6](https://github.com/infiolab/infio-copilot/releases/tag/0.1.6) update apply view, you can edit content in apply view ## Features @@ -24,10 +33,15 @@ | 📝 Autocomplete | Receive context-aware writing suggestions as you type | | ✏️ Inline Editing | Edit your notes directly within the current file | | 🔍 Vault Chat | Interact with your entire Obsidian vault using AI | -| 🖼️ Image Analysis | Upload and analyze images from your vault or local system | +| 🔍 Vault Search | Use semantic search to explore your entire vault | | ⌨️ Commands | Create and manage custom commands for quick actions | | 🎯 Custom Mode | Define personalized AI modes with specific behaviors | | 🔌 MCP | Manage Model Context Protocol integrations | +| 🗂️ Workspaces | Organize projects, research, and personal notes with seamless context switching | +| 💡 Insights | Synthesize information, discover connections, and gain deeper understanding | +| 🔍 dataview query | Perform complex queries based on time, tasks, and metadata | +| ✍️ New Write Mode | Rebuilt writing experience with intuitive, powerful, and distraction-free interface | + ### Chat & Edit Flow @@ -83,8 +97,8 @@ Leverage the power of AI to interact with your entire Obsidian vault, gaining in * Infio Copilot: Infio add selection to chat -> cmd + shift + L * Infio Copilot: Infio Inline Edit -> cmd + shift + K ![autocomplte](asserts/doc-set-hotkey.png) -7. If you need to chat with documents, you must configure an embedding model. - - Currently, only SiliconFlow, Alibaba, Google, and OpenAI platforms support embedding models. +7. **NEW: Out-of-the-Box Embedding Model** - The plugin now includes a default local embedding model (`bge-micro-v2`), so you can start using semantic features immediately! For enhanced performance, you can still configure additional embedding models: + - Currently, SiliconFlow, Alibaba, Google, and OpenAI platforms support embedding models. ## Feedback and Support We value your input and want to ensure you can easily share your thoughts and report any issues: diff --git a/README_zh-CN.md b/README_zh-CN.md index 1dd674e..435eea2 100644 --- a/README_zh-CN.md +++ b/README_zh-CN.md @@ -10,17 +10,20 @@ Infio Copilot 是一款可高度个人定制化的 Obsidian AI 插件,旨在 [![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/felixduan) -## 最新版本 -[0.5.0](https://github.com/infiolab/infio-copilot/releases/tag/0.5.0) 增强性能和稳定性改进, 增加了 MC P支持 +# 🚀 新版本发布:引入工作区、洞察与本地模型! -## 最近更新 -[0.2.4](https://github.com/infiolab/infio-copilot/releases/tag/0.2.4) 增加了多语言支持 +我们很高兴地宣布一个重要更新,它将彻底改变您的知识管理体验。此版本引入了强大的新功能,如工作区、洞察、以及开箱即用的本地嵌入模型,让您更深入地与笔记互动。 -[0.2.3](https://github.com/infiolab/infio-copilot/releases/tag/0.2.3) 增加了自定义模式配置,现在无法创建自己的 agent +--- +* **🧠 内置本地嵌入模型**:现在默认包含 `LocalProdver(bge-micro-v2)` 模型。无需任何额外配置,即可享受强大的本地语义搜索和分析功能。 -[0.1.7](https://github.com/infiolab/infio-copilot/releases/tag/0.1.7) 增加了图片选择器模态框,允许用户在 Obsidian vault 或本地文件浏览器中搜索、选择和上传图片 +* **🗂️ 工作区 (Workspaces)**:引入全新的工作区功能,帮助您更好地组织和隔离不同的项目和知识领域,让您的工作流更加清晰。 -[0.1.6](https://github.com/infiolab/infio-copilot/releases/tag/0.1.6) 更新了应用视图 (apply view),现在可以在应用视图中编辑内容 +* **💡 洞察 (Insights)**:我们增加了强大的“洞察”功能。您可以从笔记中提取关键摘要、进行反思或生成内容大纲,从您的知识库中发现深层联系。 + +* **🔍 多维度查询与对话**:像与人交谈一样与您的笔记互动。现在您可以根据时间、任务状态等多种维度进行查询,轻松找到所需信息。 + +* **✍️ 全新 `write` 模式**:一个专为写作而生的新模式,提供更专注、更流畅的创作体验,帮助您将想法转化为结构清晰的文档。 ## 功能特点 @@ -30,10 +33,14 @@ Infio Copilot 是一款可高度个人定制化的 Obsidian AI 插件,旨在 | 📝 智能补全 | 在输入时获取上下文感知的写作建议 | | ✏️ 内联编辑 | 直接在当前文件中编辑笔记 | | 🔍 全库对话 | 使用 AI 与整个 Obsidian vault 交互 | -| 🖼️ 图片分析 | 上传并分析来自 vault 或本地系统的图片 | +| 语义搜索 | | | ⌨️ 快捷命令 | 创建和管理自定义快捷命令,实现快速操作 | | 🎯 自定义Mode | 定义具有特定行为的个性化 AI 模式 | | 🔌 MCP | 管理模型上下文协议集成 | +| 🗂️ 工作空间 | 组织项目、研究和个人笔记,无缝切换上下文 | +| 💡 深度洞察 | 综合信息、发现连接、获得更深层次的理解 | +| 🔍 多维查询 | 基于时间、任务和元数据执行复杂查询 | +| ✍️ 新写作模式 | 重构的写作体验,提供直观、强大且无干扰的界面 | ### 🖋️ 内联编辑 @@ -95,8 +102,6 @@ Infio Copilot 是一款可高度个人定制化的 Obsidian AI 插件,旨在 * Infio Copilot: Infio add selection to chat -> cmd + shift + L * Infio Copilot: Infio Inline Edit -> cmd + shift + K ![autocomplte](asserts/doc-set-hotkey.png) -7. 如果需要 跟文档聊天 , 需要配置 embedding 模型 - - 目前之后 SiliconFlow Alibaba Google OpenAI 平台支持嵌入模型 ## 反馈与支持 我们重视您的意见,并希望确保您能轻松分享想法和报告问题: diff --git a/package.json b/package.json index f578ff5..843df11 100644 --- a/package.json +++ b/package.json @@ -120,11 +120,13 @@ "rehype-raw": "^7.0.0", "remark-gfm": "^4.0.0", "remove-markdown": "^0.6.2", + "sanitize-basename": "^2.0.2", "shell-env": "^4.0.1", "simple-git": "^3.27.0", "smart-embed-model": "^1.0.7", "string-similarity": "^4.0.4", "styled-components": "^6.1.19", + "unsanitize-basename": "^2.0.1", "uuid": "^10.0.0", "zod": "3.24.2" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b27d8da..33d1241 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -153,9 +153,6 @@ importers: lucide-react: specifier: ^0.447.0 version: 0.447.0(react@18.3.1) - markdown-to-text: - specifier: ^0.1.1 - version: 0.1.1 mermaid: specifier: ^11.6.0 version: 11.8.0 @@ -213,6 +210,9 @@ importers: remove-markdown: specifier: ^0.6.2 version: 0.6.2 + sanitize-basename: + specifier: ^2.0.2 + version: 2.0.2 shell-env: specifier: ^4.0.1 version: 4.0.1 @@ -228,6 +228,9 @@ importers: styled-components: specifier: ^6.1.19 version: 6.1.19(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + unsanitize-basename: + specifier: ^2.0.1 + version: 2.0.1 uuid: specifier: ^10.0.0 version: 10.0.0 @@ -580,8 +583,8 @@ packages: '@codemirror/language@6.11.2': resolution: {integrity: sha512-p44TsNArL4IVXDTbapUmEkAlvWs2CFQbcfc0ymDsis1kH2wh0gcY96AS29c/vp2d0y2Tquk1EDSaawpzilUiAw==} - '@codemirror/language@git+https://git@github.com:lishid/cm-language.git#6c1c5f5b677f6f6503d1ca2ec47f62f6406cda67': - resolution: {commit: 6c1c5f5b677f6f6503d1ca2ec47f62f6406cda67, repo: git@github.com:lishid/cm-language.git, type: git} + '@codemirror/language@https://codeload.github.com/lishid/cm-language/tar.gz/6c1c5f5b677f6f6503d1ca2ec47f62f6406cda67': + resolution: {tarball: https://codeload.github.com/lishid/cm-language/tar.gz/6c1c5f5b677f6f6503d1ca2ec47f62f6406cda67} version: 6.10.8 '@codemirror/lint@0.20.3': @@ -2436,9 +2439,6 @@ packages: '@types/babel__traverse@7.20.7': resolution: {integrity: sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng==} - '@types/chai@4.3.20': - resolution: {integrity: sha512-/pC9HAB5I/xMlc5FP77qjCnI16ChlJfW0tGa0IUcFn38VJrTV6DeZ60NU5KZBtaOZqjdpwTWohz5HU1RrhiYxQ==} - '@types/codemirror@5.60.8': resolution: {integrity: sha512-VjFgDF/eB+Aklcy15TtOTLQeMjTo07k7KAjql8OK5Dirr7a6sJY4T1uVBDuTVG9VEmn1uUsohOpYnVfgC6/jyw==} @@ -2608,9 +2608,6 @@ packages: resolution: {integrity: sha512-AlvLWYer6u4BkO4QzMkHo0t9RkvVIgqggVZmO+5snUiuX2caTKqtdqygX6GeE1VQa/TnXw9WoH0spcmHtG0inQ==} deprecated: This is a stub types definition. mermaid provides its own type definitions, so you do not need this installed. - '@types/mocha@8.2.3': - resolution: {integrity: sha512-ekGvFhFgrc2zYQoX4JeZPmVzZxw6Dtllga7iGHzfbYIYkAMUx/sAFP2GdFpLff+vdHXu5fl7WX9AT+TtqYcsyw==} - '@types/ms@2.1.0': resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} @@ -5027,9 +5024,6 @@ packages: markdown-table@3.0.4: resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==} - markdown-to-text@0.1.1: - resolution: {integrity: sha512-co/J5l8mJ2RK9wD/nQRGwO7JxoeyfvVNtOZll016EdAX2qYkwCWMdtYvJO42b41Ho7GFEJMuly9llf0Nj+ReQw==} - marked@15.0.12: resolution: {integrity: sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA==} engines: {node: '>= 18'} @@ -5454,8 +5448,8 @@ packages: '@codemirror/state': ^6.0.0 '@codemirror/view': ^6.0.0 - obsidian@git+https://git@github.com:obsidianmd/obsidian-api.git#103ff76a0712a02dd0da28646b5a6b0ba6686d66: - resolution: {commit: 103ff76a0712a02dd0da28646b5a6b0ba6686d66, repo: git@github.com:obsidianmd/obsidian-api.git, type: git} + obsidian@https://codeload.github.com/obsidianmd/obsidian-api/tar.gz/103ff76a0712a02dd0da28646b5a6b0ba6686d66: + resolution: {tarball: https://codeload.github.com/obsidianmd/obsidian-api/tar.gz/103ff76a0712a02dd0da28646b5a6b0ba6686d66} version: 1.8.7 peerDependencies: '@codemirror/state': ^6.0.0 @@ -6017,6 +6011,9 @@ packages: safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + sanitize-basename@2.0.2: + resolution: {integrity: sha512-zaOQiK4PPsUQZ0Nx3KCrT9p0oEx1dTef14qflYq7TdxPRI2RY9faLYuExCiF8LiJdeKVYb5o/KdF0fGEvvEZUA==} + saxes@6.0.0: resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} engines: {node: '>=v12.22.7'} @@ -6514,6 +6511,9 @@ packages: resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} engines: {node: '>= 0.8'} + unsanitize-basename@2.0.1: + resolution: {integrity: sha512-ywgB/ygePvtjszN3ZTK9H+OWF7Bt2AFbeLDxkx01/EjWuB4DhmdFpkLv2sg0twaZbELi+JQjLUwjZrVbgQSuJg==} + update-browserslist-db@1.1.3: resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==} hasBin: true @@ -7089,7 +7089,7 @@ snapshots: '@lezer/lr': 1.4.2 style-mod: 4.1.2 - '@codemirror/language@git+https://git@github.com:lishid/cm-language.git#6c1c5f5b677f6f6503d1ca2ec47f62f6406cda67': + '@codemirror/language@https://codeload.github.com/lishid/cm-language/tar.gz/6c1c5f5b677f6f6503d1ca2ec47f62f6406cda67': dependencies: '@codemirror/state': 6.5.2 '@codemirror/view': 6.38.0 @@ -8967,8 +8967,6 @@ snapshots: dependencies: '@babel/types': 7.28.0 - '@types/chai@4.3.20': {} - '@types/codemirror@5.60.8': dependencies: '@types/tern': 0.23.9 @@ -9175,8 +9173,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@types/mocha@8.2.3': {} - '@types/ms@2.1.0': {} '@types/node-fetch@2.6.12': @@ -12055,11 +12051,6 @@ snapshots: markdown-table@3.0.4: {} - markdown-to-text@0.1.1: - dependencies: - '@types/chai': 4.3.20 - '@types/mocha': 8.2.3 - marked@15.0.12: {} matcher@3.0.0: @@ -12604,7 +12595,7 @@ snapshots: obsidian-daily-notes-interface@0.8.4(@codemirror/state@6.5.2)(@codemirror/view@6.38.0): dependencies: - obsidian: git+https://git@github.com:obsidianmd/obsidian-api.git#103ff76a0712a02dd0da28646b5a6b0ba6686d66(@codemirror/state@6.5.2)(@codemirror/view@6.38.0) + obsidian: https://codeload.github.com/obsidianmd/obsidian-api/tar.gz/103ff76a0712a02dd0da28646b5a6b0ba6686d66(@codemirror/state@6.5.2)(@codemirror/view@6.38.0) tslib: 2.1.0 transitivePeerDependencies: - '@codemirror/state' @@ -12612,7 +12603,7 @@ snapshots: obsidian-dataview@0.5.68: dependencies: - '@codemirror/language': git+https://git@github.com:lishid/cm-language.git#6c1c5f5b677f6f6503d1ca2ec47f62f6406cda67 + '@codemirror/language': https://codeload.github.com/lishid/cm-language/tar.gz/6c1c5f5b677f6f6503d1ca2ec47f62f6406cda67 '@codemirror/state': 6.5.2 '@codemirror/view': 6.38.0 emoji-regex: 10.4.0 @@ -12631,7 +12622,7 @@ snapshots: '@types/codemirror': 5.60.8 moment: 2.29.4 - obsidian@git+https://git@github.com:obsidianmd/obsidian-api.git#103ff76a0712a02dd0da28646b5a6b0ba6686d66(@codemirror/state@6.5.2)(@codemirror/view@6.38.0): + obsidian@https://codeload.github.com/obsidianmd/obsidian-api/tar.gz/103ff76a0712a02dd0da28646b5a6b0ba6686d66(@codemirror/state@6.5.2)(@codemirror/view@6.38.0): dependencies: '@codemirror/state': 6.5.2 '@codemirror/view': 6.38.0 @@ -13336,6 +13327,8 @@ snapshots: safer-buffer@2.1.2: {} + sanitize-basename@2.0.2: {} + saxes@6.0.0: dependencies: xmlchars: 2.2.0 @@ -13991,6 +13984,8 @@ snapshots: unpipe@1.0.0: {} + unsanitize-basename@2.0.1: {} + update-browserslist-db@1.1.3(browserslist@4.25.1): dependencies: browserslist: 4.25.1 diff --git a/src/database/json/chat/ChatManager.ts b/src/database/json/chat/ChatManager.ts index 63298b4..7ed650c 100644 --- a/src/database/json/chat/ChatManager.ts +++ b/src/database/json/chat/ChatManager.ts @@ -1,5 +1,7 @@ import { App } from 'obsidian' import { v4 as uuidv4 } from 'uuid' +import sanitize from 'sanitize-basename' +import unsanitize from 'unsanitize-basename' import { ChatConversationMeta } from '../../../types/chat' import { AbstractJsonRepository } from '../base' @@ -12,7 +14,6 @@ import { ChatConversation } from './types' - export class ChatManager extends AbstractJsonRepository< ChatConversation, ChatConversationMeta @@ -25,35 +26,89 @@ export class ChatManager extends AbstractJsonRepository< } protected generateFileName(chat: ChatConversation): string { - // 新格式: v{schemaVersion}_{title}_{updatedAt}_{id}_{workspaceId}.json + // 新格式 v2: v{schemaVersion}_{sanitizedTitle}_{updatedAt}_{id}_{workspaceId}.json + const sanitizedTitle = sanitize(chat.title, { maxLength: 100 }) // 如果没有工作区,使用 'vault' 作为默认值 - const encodedTitle = encodeURIComponent(chat.title) const workspaceId = chat.workspace || 'vault' - return `v${chat.schemaVersion}_${encodedTitle}_${chat.updatedAt}_${chat.id}_${workspaceId}.json` + return `v${chat.schemaVersion}_${sanitizedTitle}_${chat.updatedAt}_${chat.id}_${workspaceId}.json` } protected parseFileName(fileName: string): ChatConversationMeta | null { - // 使用一个正则表达式,工作区部分为可选: v{schemaVersion}_{title}_{updatedAt}_{id}_{workspaceId}?.json + // 通过头两个字符判断版本 + if (fileName.startsWith('v2_')) { + return this.parseFileNameV2(fileName) + } else if (fileName.startsWith('v1_')) { + return this.parseFileNameV1(fileName) + } + + return null + } + + /** + * 解析新版本 (v2) 文件名 + * 格式: v2_{sanitizedTitle}_{updatedAt}_{id}_{workspaceId}.json + */ + private parseFileNameV2(fileName: string): ChatConversationMeta | null { const regex = new RegExp( - `^v${CHAT_SCHEMA_VERSION}_(.+)_(\\d+)_([0-9a-f-]+)(?:_([^_]+))?\\.json$`, + `^v2_(.+)_(\\d+)_([0-9a-f-]+)(?:_([^_]+))?\\.json$`, ) const match = fileName.match(regex) if (!match) return null - const title = decodeURIComponent(match[1]) - const updatedAt = parseInt(match[2], 10) - const id = match[3] - const workspaceId = match[4] // 可能为undefined(老格式) + try { + // 使用 unsanitize-basename 还原原始标题 + const title = unsanitize(match[1]) + const updatedAt = parseInt(match[2], 10) + const id = match[3] + const workspaceId = match[4] // 可能为undefined(老格式) - return { - id, - schemaVersion: CHAT_SCHEMA_VERSION, - title, - updatedAt, - createdAt: 0, - // 如果没有工作区信息(老格式),则认为是vault(全局消息) - workspace: workspaceId === 'vault' ? undefined : workspaceId, + return { + id, + schemaVersion: 2, + title, + updatedAt, + createdAt: 0, + // 如果没有工作区信息(老格式),则认为是vault(全局消息) + workspace: workspaceId === 'vault' ? undefined : workspaceId, + } + } catch (error) { + console.warn('Failed to unsanitize filename:', fileName, error) + return null + } + } + + /** + * 解析旧版本 (v1) 文件名 + * 格式: v1_{encodedTitle}_{updatedAt}_{id}_{workspaceId}.json + */ + private parseFileNameV1(fileName: string): ChatConversationMeta | null { + const regex = new RegExp( + `^v1_(.+)_(\\d+)_([0-9a-f-]+)(?:_([^_]+))?\\.json$`, + ) + const match = fileName.match(regex) + + if (!match) return null + + try { + // 旧版本使用 decodeURIComponent + const title = decodeURIComponent(match[1]) + const updatedAt = parseInt(match[2], 10) + const id = match[3] + const workspaceId = match[4] // 可能为undefined(老格式) + + return { + id, + schemaVersion: 1, + title, + updatedAt, + createdAt: 0, + // 如果没有工作区信息(老格式),则认为是vault(全局消息) + workspace: workspaceId === 'vault' ? undefined : workspaceId, + } + } catch (error) { + console.warn('Failed to decode v1 filename:', fileName, error) + return null } } diff --git a/src/database/json/chat/types.ts b/src/database/json/chat/types.ts index 60e74e8..b30052a 100644 --- a/src/database/json/chat/types.ts +++ b/src/database/json/chat/types.ts @@ -1,6 +1,6 @@ import { SerializedChatMessage } from '../../../types/chat' -export const CHAT_SCHEMA_VERSION = 1 +export const CHAT_SCHEMA_VERSION = 2 export type ChatConversation = { id: string