From ad8c548792a227a13147df1097ed5ae549e75b2b Mon Sep 17 00:00:00 2001 From: xianrenge <34586161+xianrenge@users.noreply.github.com> Date: Tue, 18 Aug 2026 01:48:25 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E7=9F=A5=E8=AF=86=E5=BA=93=E5=AA=92?= =?UTF-8?q?=E4=BD=93=E6=96=87=E6=A1=A3=E9=A2=84=E8=A7=88=E5=B1=95=E7=A4=BA?= =?UTF-8?q?=E5=AE=9E=E9=99=85=E5=86=85=E5=AE=B9=E2=80=94=E2=80=94=E5=90=AF?= =?UTF-8?q?=E7=94=A8=20asset=20protocol=EF=BC=8C=E5=9B=BE=E7=89=87?= =?UTF-8?q?=E5=8E=9F=E5=9B=BE=E6=98=BE=E7=A4=BA=E3=80=81=E8=A7=86=E9=A2=91?= =?UTF-8?q?/=E9=9F=B3=E9=A2=91=E5=8E=9F=E7=94=9F=E6=92=AD=E6=94=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.lock | 7 +++ apps/desktop/Cargo.toml | 2 +- apps/desktop/tauri.conf.json | 7 ++- docs/ARCHITECTURE.md | 1 + docs/FEATURES.md | 2 + ui/src/api.ts | 2 + ui/src/pages/KnowledgeBasePage.tsx | 77 ++++++++++++++++++++++++++++-- 7 files changed, 92 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cfd1d20..46ee41b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1780,6 +1780,12 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "http-range" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21dec9db110f5f872ed9699c3ecf50cf16f423502706ba5c72462e28d3157573" + [[package]] name = "httparse" version = "1.10.1" @@ -4125,6 +4131,7 @@ dependencies = [ "gtk", "heck 0.5.0", "http", + "http-range", "jni", "libc", "log", diff --git a/apps/desktop/Cargo.toml b/apps/desktop/Cargo.toml index 502f6e3..d659939 100644 --- a/apps/desktop/Cargo.toml +++ b/apps/desktop/Cargo.toml @@ -17,7 +17,7 @@ custom-protocol = ["tauri/custom-protocol"] tauri-build = { version = "2", features = [] } [dependencies] -tauri = { version = "2", features = [] } +tauri = { version = "2", features = ["protocol-asset"] } tauri-plugin-opener = "2" tauri-plugin-autostart = "2" tauri-plugin-dialog = "2" diff --git a/apps/desktop/tauri.conf.json b/apps/desktop/tauri.conf.json index 4afeb21..ce11ec8 100644 --- a/apps/desktop/tauri.conf.json +++ b/apps/desktop/tauri.conf.json @@ -21,7 +21,11 @@ } ], "security": { - "csp": null + "csp": null, + "assetProtocol": { + "enable": true, + "scope": ["**"] + } } }, "bundle": { @@ -35,4 +39,3 @@ ] } } - diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 7ed235b..0f3ebd2 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -98,6 +98,7 @@ ui/src/ - `run()`:初始化数据目录(`%APPDATA%\XianrenStudio`)、日志、panic 钩子;构建 Tauri 应用并注册 `invoke_handler`。 - 注册插件:`tauri-plugin-opener`、`tauri-plugin-autostart`(开机启动,Windows 写注册表启动项)、`tauri-plugin-dialog`(原生文件/文件夹选择对话框,知识库目录来源「浏览…」使用)。 +- `tauri.conf.json` 启用 `assetProtocol`(scope `**`):知识库媒体文档(图片/视频/音频)预览通过 `convertFileSrc` 直读本地文件渲染/播放。 - `setup` 末尾启动**自动加载模型**任务(`auto_load_models`:按设置列表顺序逐个部署,最后一个保持运行)。 - `setup` 末尾启动**定时计划后台调度循环**(tokio interval 每 30 秒调用 `run_due_scheduled_tasks`)。 - 数据目录:`logs/`、`models/`、`engines/`、`recommendations/`。 diff --git a/docs/FEATURES.md b/docs/FEATURES.md index 1946775..217d08d 100644 --- a/docs/FEATURES.md +++ b/docs/FEATURES.md @@ -141,6 +141,7 @@ - 同一目录重复添加自动更新配置不产生重复来源;按文件路径去重,可一键「重新扫描」增量导入新增文件;删除目录来源会同时删除它导入的文档。 - **扫描进度**:添加目录 / 重新扫描时显示进度条与处理统计(处理 X / Y 个文件、成功、失败、忽略、分块数),失败文件在结尾汇总提示。 - **文档操作**:文档列表每页 10 条,带分页控件;展示类型 / 大小 / 字数 / 分块数;支持全文预览、「重切」(按最新分块设置重新切分,适用于修改设置后)、单条删除;标题「文档(共 x 个)」右侧提供「清空」按钮,一键清空该知识库全部文档(含分块与检索索引),清空前弹窗警告确认。 +- **媒体预览**:图片 / 视频 / 音频文档点击「预览」直接展示实际内容——图片原图显示、视频/音频用原生播放器播放(经 Tauri asset protocol 直接读取本地文件,无需整文件过 IPC);文件被移动/删除时提示无法加载。 - **检索**:全文检索基于 SQLite FTS5(trigram 分词,对中文友好),按 BM25 相关性排序;支持单库检索(也可全库检索),结果每页 10 条带分页控件,展示来源文档与分块序号,可一键复制分块内容用于对话。 - **页面布局**:知识库主区内自上而下依次为「目录来源」→「文档列表」→「检索」。 - 数据表:`knowledge_bases`、`kb_documents`(含提取后的纯文本)、`kb_chunks`(分块)、`kb_chunks_fts`(FTS5 全文索引,contentless-delete 模式 + 触发器同步)。 @@ -232,6 +233,7 @@ ### 2026-08-17 +- 知识库文档预览支持多媒体:图片/视频/音频点击「预览」直接展示实际内容(asset protocol 直读本地文件,视频音频可播放)。 - 知识库文档列表标题增加「清空」按钮:一键清空当前知识库全部文档(分块与索引级联删除),清空前弹出警告确认。 - 知识库页调整:文档列表与检索结果增加分页控件;目录来源、文档列表区块移到检索上方;目录扫描增加进度条与处理统计(处理/成功/失败/忽略/分块数,事件驱动实时刷新)。 - 修复:知识库「添加目录 → 浏览…」无响应——为 dialog 插件在 capabilities 中补充权限(`dialog:default`),并让选择器异常在前端可见。 diff --git a/ui/src/api.ts b/ui/src/api.ts index 7ef715c..72a292e 100644 --- a/ui/src/api.ts +++ b/ui/src/api.ts @@ -221,6 +221,8 @@ export interface KbDocument { file_size: number; char_count: number; chunk_count: number; + file_path: string; + source_id: string | null; created_at: string; updated_at: string; } diff --git a/ui/src/pages/KnowledgeBasePage.tsx b/ui/src/pages/KnowledgeBasePage.tsx index f58d41a..ceb2abb 100644 --- a/ui/src/pages/KnowledgeBasePage.tsx +++ b/ui/src/pages/KnowledgeBasePage.tsx @@ -1,5 +1,6 @@ import { useEffect, useMemo, useRef, useState } from "react"; import { open as openDialog } from "@tauri-apps/plugin-dialog"; +import { convertFileSrc } from "@tauri-apps/api/core"; import { api, KbDocument, @@ -16,6 +17,24 @@ import Icon from "../components/Icon"; const DOC_PAGE_SIZE = 10; const SEARCH_PAGE_SIZE = 10; +const IMAGE_EXTS = [ + "png", "jpg", "jpeg", "gif", "webp", "bmp", "svg", "tiff", "tif", "ico", "avif", "heic", +]; +const VIDEO_EXTS = [ + "mp4", "mkv", "avi", "mov", "webm", "flv", "wmv", "m4v", "mpg", "mpeg", "3gp", +]; +const AUDIO_EXTS = [ + "mp3", "wav", "flac", "ogg", "oga", "m4a", "aac", "wma", "opus", "aiff", "aif", +]; + +function mediaKind(ext: string): "image" | "video" | "audio" | null { + const e = ext.toLowerCase(); + if (IMAGE_EXTS.includes(e)) return "image"; + if (VIDEO_EXTS.includes(e)) return "video"; + if (AUDIO_EXTS.includes(e)) return "audio"; + return null; +} + function fmtSize(bytes: number) { if (bytes >= 1024 * 1024) return `${(bytes / 1024 / 1024).toFixed(1)} MB`; if (bytes >= 1024) return `${(bytes / 1024).toFixed(0)} KB`; @@ -697,9 +716,28 @@ export default function KnowledgeBasePage() { 关闭 -
-              {preview.content}
-            
+ {(() => { + const kind = mediaKind(preview.file_type); + if (kind && preview.file_path) { + return ( +
+ +
+ ); + } + if (kind) { + return ( +
+ 该文件没有本地路径(非目录导入),无法直接预览 +
+ ); + } + return ( +
+                  {preview.content}
+                
+ ); + })()} ) : null} @@ -742,6 +780,39 @@ function Pager({ ); } +function MediaView({ + kind, + filePath, + name, +}: { + kind: "image" | "video" | "audio"; + filePath: string; + name: string; +}) { + const [error, setError] = useState(null); + const src = convertFileSrc(filePath); + const commonProps = { + src, + onError: () => setError("无法加载文件,文件可能已被移动或删除"), + }; + if (error) { + return
{error}
; + } + if (kind === "image") { + return ( + {name} + ); + } + if (kind === "video") { + return