24 Commits

Author SHA1 Message Date
ssurui
b918a0c53f fix: 拍照默认摄像头按标签精确匹配后置,不再依赖枚举顺序
之前默认摄像头是"设备列表第一个",依赖操作系统 enumerateDevices()
返回顺序,不可靠。真机测出后置摄像头(朝外拍摄那颗)的 label 是
"Hy-8M(8079)-Camera",改成按标签里的型号数字串精确匹配。

摄像头 label 在 getUserMedia 拿到权限前是空的(浏览器隐私限制),
没法一开始就按标签选,所以流程改成:先用默认设备开流拿到权限,
权限到手后 label 才是真的,这时候如果不是用户/配置显式指定的摄像头,
再检查当前是不是已经是后置、不是就切过去。

真机验证:清空之前手动切换保留下来的摄像头偏好后重新打开,稳定落在
后置摄像头(桌面/Mac mini 视角),不再随机。

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-03 16:06:28 +08:00
ssurui
0d19be58c3 fix: 拍照提问摄像头画面倒置、下拉菜单换成前后切换按钮
真机拍照实测画面上下颠倒(Mac mini 的苹果标志倒着)。CameraCapture.vue
的 <video> 加 transform: rotate(180deg) 转正预览;capture() 画到 canvas
时同步做相同的 180° 旋转(translate 到右下角再 rotate(Math.PI)),
不然预览是正的、实际拍下来的照片还是倒的。

摄像头选择从下拉菜单换成一个悬浮在预览右上角的圆形切换按钮
(switchCamera(),在设备列表里循环切换,只有 >1 个摄像头时才显示)。
默认摄像头沿用之前"取设备列表第一个"的逻辑不变——真机实测这个正好是
朝外拍摄的那颗(另一颗是朝向操作者的),语义上更接近用户要的默认
"后置摄像头",暂不改动。

真机验证:预览转正、切换按钮能真实切到另一颗摄像头且画面同样是正的、
拍照后的照片方向跟预览一致(不会出现预览正但存图倒的情况)。

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-03 16:02:31 +08:00
ssurui
8054fcdad1 fix: 语音输出恢复"开始播报"、消除加粗文本重复朗读;输入框真正对齐
真机复测又发现三个问题,逐一定位修复:

1. 流式播报接上后"开始播报"提示语丢了。TtsPlayer 新增 announceStart(),
   只入队播放不碰流式缓冲区状态,beginQuery() 里 stop()+配置同步之后
   调用。真机验证:连续问答里从第2轮开始稳定播报"开始播报"(第1轮偶发
   缺失,是启动时批量测试自动开始跟语音配置同步的时序竞争,非本次改动
   引入的逻辑问题)。

2. 加粗文本(**xxx**)被朗读两遍。根因:markdownToPlain 每次都从头
   重新解析整段 rawText,** 闭合符号到达前后同一段文字的纯文本渲染
   结果不一样(未闭合时星号当普通字符、闭合后星号被去掉),提前把这段
   不稳定文字提交为 lastSpokenPlain 会在闭合符号到达后被重新判定成
   "待朗读"造成重复。新增 findStableBoundary(),只处理到最后一个已闭合
   的 ** 为止,未闭合的尾部留到下次 append 再处理;flush() 收尾时因为
   生成已结束,不再有这个顾虑,直接处理全部剩余内容。真机验证:连续
   3 轮问答,每轮都有加粗小标题,全部按顺序推进朗读新内容,无重复。

3. 输入框和按钮行的对不齐问题:之前一直在改 .q-input(文本框本身)的
   高度,但真实 DOM 测量显示文本框本身一直是精确的 96px,真正超高的
   是它的父容器 .q-input-wrap(渲染成 102.4px,比 96px 多 6.4px),
   文本框贴容器顶部导致整体被顶高。给 .q-input-wrap 也显式定死
   height:96px。真机像素测量确认对齐。

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-03 15:54:34 +08:00
ssurui
48a9fb0f6d fix: TTS append() 高频小增量下丢字、输入框 max-height 兜底
真机复测发现两个问题:

1. 语音输出改成流式后实测只播一个孤零零的句号,正文完全没读出来。
   根因:TtsPlayer.append() 每次调用都用 this.buffer = pending 直接
   覆盖缓冲区,而不是累加。LLM 流式返回的增量通常很小(经常几个字一
   个 chunk),大部分时候 processBuffer() 没能凑出完整句子就被下一次
   append() 覆盖掉,只有 flush() 时残留的最后一小段才被朗读。改成
   this.buffer += pending,累加未处理完的内容,不再丢字。真机验证:
   同一次问答里生成过程中(回复结束前)已经出现 4 条完整正文的 speak
   调用,不再是只有收尾的空句号。

2. 输入框 rows="1" + min-height:0 没能完全消除跟按钮行的高度差
   (真机截图像素测量仍有约10px gap),补充 max-height:96px 硬性封顶,
   重启(不是 HMR 热更)后截图确认对齐。

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-03 15:34:01 +08:00
ssurui
a78c88c86a fix: 输入框与旁边的按钮行高度对不齐
<textarea class="q-input"> 没设 rows 属性,浏览器按默认 2 行的最小
内容高度处理,字体度量四舍五入后比显式 height:96px 略高几像素,
把整个输入框撑高,导致跟同高度的语音/拍照按钮顶边对不齐(底边因为
input-bar 是 align-items:flex-end 对齐所以没发现,只有顶边露馅)。
加 rows="1" 让最小内容高度不再跟显式高度打架,同时加 min-height:0
防御 UA 默认样式。

用像素网格量过真机截图确认:这是 textarea 本来就有的问题,不是这次
加拍照按钮引入的,只是三个按钮排一起后更显眼。

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-03 15:14:04 +08:00
ssurui
ec634c4133 fix: 语音输出改为边生成边播报,不再等整段答案完成
TtsPlayer 本来就实现了完整的流式播报能力(append() 按句子边界切分
入队、flush() 收尾剩余文本),只是 ChatPanel 一直没接上,只在
querying 结束后调用一次 speakWithAnnouncement(全文)。

改为在 onDelta 里实时 append(delta),onDone/onError 里 flush()
收尾。删除了原来 querying 结束时整段朗读的 watch(不删会导致
整段答案被朗读两遍)。效果:不用等生成完,第一句话生成出来就
开始播,播放速度跟不上文字输出时 TtsPlayer 自带的自适应语速会
自动降速跟上。

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-03 15:07:01 +08:00
ssurui
ab01effd5b docs: 用真机验证结果更新拍照提问设计文档
记录 vue-tsc/构建/端到端全部验证通过,修正设计与实现的几处出入:
ChatPanel 实际是单轮问答不是消息列表、vision 配置字段做了裁剪(去掉
maxImageDimension/jpegQuality/defaultPrompt 改成硬编码)、真实照片
完整回答实测 ~31s(早期用1x1测试图估算的3-8s严重偏低)。风险清单
标注哪些已解决、哪些还没验证,新增"后续可选优化"区分阻塞项和锦上添花项。

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-03 14:59:46 +08:00
ssurui
21f9c7e453 feat: 实现拍照提问模式 MVP(阶段1+2)
按 docs/feature-pic-design.md 的方案实现:

- AppConfig 新增 vision 字段(enabled 总开关、cameraDeviceId 记住上次
  选用的摄像头),主进程/渲染层类型两处同步
- generator.ts 抽出共享的 streamChatCompletion(),新增
  streamGenerateVision() 支持 OpenAI 视觉 API 的 image_url content-part,
  文字问答路径不受影响
- 新增 visionHandler.ts(vision-query IPC,跳过向量检索,直接转发
  图片+问题给 er_backend 代理),注册进 ipc/index.ts
- 新增 CameraCapture.vue:getUserMedia 取景/拍照/重拍/多摄像头切换,
  纯交互组件不掺业务逻辑
- ChatPanel.vue 接入拍照入口(仅 vision.enabled 时显示)、待发送图片
  缩略图、问题气泡里的图片展示;抽出 beginQuery() 复用文字/拍照两条
  路径共同的状态重置逻辑
- SettingsModal.vue 加拍照提问开关
- index.ts 权限处理器注释说明 getUserMedia 摄像头请求也走 'media'

不做检索、不做照片持久化、不做多摄像头预配置界面(MVP 范围见设计文档 §3)。
本地未跑 vue-tsc,接下来在真机上验证类型检查 + 构建 + 端到端联调。

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-03 14:50:52 +08:00
ssurui
2ae01c2050 docs: 复查确认 er_backend 已支持多模态,解除拍照提问的阻塞项
现场重新查询 er_backend 状态,确认已换成带 --mmproj 的多模态配置
(allow_image=1,vision 896x896),并对 ER_BACKEND_URL/v1/chat/completions
做了端到端真实请求验证(HTTP 200,正确识别测试图片颜色),证明前端
可以直接按现有代理路径发送 image_url content-part,不需要额外适配层。

顺带记录实测性能数据(极简图片 prompt_tokens=808,prefill ~2s)用于
后续加载态 UI 设计参考,更新分阶段计划:阶段0 已完成,阶段1/2 可并行开始。

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-03 14:33:58 +08:00
ssurui
ee9b1153d5 docs: 新增拍照提问模式设计方案(feature_pic 分支)
只有设计文档,未修改任何业务代码。核心结论:摄像头硬件已具备
(两个标准 USB UVC 摄像头),但当前 er_backend 加载的模型是纯文本
模型(现场查证 llama-server 日志 allow_image=0),视觉能力是否可用
需要跨团队确认,是本功能唯一的硬阻塞项。

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-03 14:26:29 +08:00
ssurui
6faf48fff4 docs: 补充打包体积精简的技术记录
记录 789M→664M 精简过程中的三个点:node_modules 排除 glob 必须带
**/ 前缀(真机踩过的坑)、Piper 引擎从未被实际调用只需保留
espeak-ng-data、bge-base/bge-large/punct 模型已从仓库删除的原因。

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-23 00:56:19 +08:00
ssurui
d686e600be chore: 删除未使用的 bge-base/bge-large/punct 模型
- bge-base-zh-v1.5、bge-large-zh-v1.5:只有 tokenizer/config 元数据,
  onnx 权重从未提交(README 一直写"需自行下载”),代码里也完全没有
  任何地方引用这两个目录,纯占位死文件。
- punct(标点恢复模型,75M onnx):electron-builder.config.ts 的
  extraResources 从未把它纳入打包,装出来的应用里从来没有过这份模型,
  语音识别的标点恢复功能实际一直是禁用状态。用户确认现阶段不需要,
  直接删除源文件;代码逻辑保留(punctuationModelDir 为空时优雅跳过),
  以后要用可以自行准备模型再在设置里指定目录。

models/ 从 ~475M 降到 399M。README 目录说明与模型文件章节同步更新。

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-23 00:36:17 +08:00
ssurui
33ce4ec09c fix: node_modules 排除 glob 需要 **/ 前缀才生效
上次提交(d512ad4)里 '!node_modules/onnxruntime-web/**' 这种不带
**/ 前缀的排除规则在真机验证时基本没生效(体积没降)。electron-builder
自动收录 node_modules 的内部匹配逻辑按完整相对路径比对,需要写成
'!**/node_modules/onnxruntime-web/**' 才能正确排除。真机重新打包验证:
node_modules 从 597M 降到 373M,符合预期。

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-23 00:28:36 +08:00
ssurui
d512ad41f9 perf: 精简打包体积,砍掉从未被使用的 Piper 引擎和渲染层专用依赖
排查确认两处真实浪费:

1. resources/tts/piper/ 里除 espeak-ng-data 外的所有文件(Piper 神经
   语音模型、二进制、自带 onnxruntime、phonemize 库、tashkeel 阿拉伯语
   音标模型,合计 ~113M)从未被实际调用——detectBackend() 第一优先级
   永远命中 VITS(小雅),Piper 引擎分支代码走不到,只有它 bundle 里的
   espeak-ng-data 目录被 VITS 借用当 sherpa-onnx 校验的 dataDir。
   systemTts.ts 新增 findPiperEspeakDataDir(),VITS 分支改为只依赖这一
   个子目录,不再要求 Piper 引擎的 bin/model/config 存在。

2. electron-builder.config.ts 的 files 补充排除 onnxruntime-web(
   @huggingface/transformers 的硬依赖但项目强制走 onnxruntime-node,
   完全未用)、naive-ui/date-fns/highlight.js/lodash 系(只在
   src/renderer/ 被引用,electron-vite build 时已被 Vite 打包进
   out/renderer/*.js,主进程不需要原始 node_modules),约省 227M。

两项合计预计单包体积从 ~789M 降到 ~560M 左右,待真机重新打包安装、
实际听一次语音播报确认功能未受影响。

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-23 00:19:14 +08:00
ssurui
b512857629 docs: 更新文档以反映 er_backend 代理重构、arm64 打包脚本与图标修复
- applyPlatformOverrides 章节改为描述统一走 er_backend 代理的现状,
  删除已废弃的按 cardType 推导端口的旧逻辑说明
- AppConfig 里 voice 字段结构更新为实际的 asr/tts 嵌套结构
- 新增"应用图标打包踩坑":files 数组遗漏 resources/icon.png、
  桌面菜单图标需要装全常规尺寸这两个真实踩过的坑
- 版本号管理章节补充三处版本号解析保持一致的说明
- 打包命令统一指向验证过的 package:kylin:arm64:deb(真机 fpm bug 的
  绕行方案),标注旧 package:kylin:arm64 在真机上会失败
- README 数据存储路径修正为实际的 ~/.config/elec_rag_ui/(原文档
  写的 simple-rag-app 是错的,真机验证过实际路径)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-22 23:52:33 +08:00
ssurui
1cb953eeb3 fix: 打包应用图标补齐常规尺寸,解决菜单栏图标不显示问题
之前只装了 hicolor/1024x1024 一个尺寸,UKUI 等桌面菜单按 48x48 等
常规尺寸查找图标时找不到,直接回退成缺省图标而不会从 1024 缩放。
现在按 16/24/32/48/64/128/256/512/1024 全部装进 hicolor 主题,并
额外在 /usr/share/pixmaps 放一份 48x48,兼容不认图标主题的老式菜单。

resources/icons/ 下是从 resources/icon.png 用 sips 预生成的各尺寸
PNG,随图标一起更新即可,不需要在打包时现场依赖 ImageMagick。

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-22 12:16:13 +08:00
ssurui
598922dd16 fix: 修正打包产物缺失窗口图标的问题
electron-builder.config.ts 的 files 只包含 out/**/*,没有把顶层
resources/icon.png 带进应用包。src/main/index.ts 用相对路径
__dirname/../../resources/icon.png 定位窗口图标(打包后 __dirname
是 <app>/out/main),文件缺失导致任务栏/菜单栏图标显示不出来。

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-22 11:31:58 +08:00
ssurui
0ae330c4ca feat: 替换应用图标为新版"电子手册"图标
同时替换 main 进程窗口图标源(resources/icon.png,也是打包脚本里
桌面快捷方式图标的来源)和渲染层内嵌 logo 源(src/renderer/public/icon.png)。

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-22 10:35:32 +08:00
surui
836d25cad2 refactor: 推理请求统一走 er_backend 代理(127.0.0.1:17700)
- 嵌入/生成服务地址不再由前端按 cardType 推导端口,改为经 er_backend
  的 /v1/* 代理转发,服务端口以后端配置为唯一真相来源
- config.ts: 新增 ER_BACKEND_URL 统一常量,删除端口推导逻辑
- generator/statusHandler/queryHandler/erBackendHandler 跟随改造
- fix: package-kylin-arm64-deb.sh 从 .app-version 读版本号时剥离 v 前缀,
  修复无 git 环境下 dpkg-deb 报"版本号没有以数字开头"
2026-08-16 21:16:01 +08:00
ssurui
a319caeff6 feat: 新增 Kylin arm64 deb 打包脚本,替代有 bug 的 fpm 路径
electron-builder 自带的 fpm 打包在 arm64 Kylin 板子上必现
"tar failed (exit code 2)",改用 electron-builder 的 dir target
产出 unpacked 目录后手工用 dpkg-deb 拼装,包含 chrome-sandbox
setuid 修复(缺失会导致 Electron 沙箱初始化失败,启动即崩溃)。
版本号解析与 electron-builder.config.ts / electron.vite.config.ts
保持一致(git tag -> .app-version -> package.json 三级回退)。

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-15 20:33:12 +08:00
ssurui
7a4f149177 docs: 新增 README.md 和 CLAUDE.md 项目文档
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
v8.1.0
2026-08-15 20:10:47 +08:00
ssurui
16a19a12f5 fix: 修正 electron-builder 打包 ASR 模型资源的相对路径
extraResources 中 ASR 模型的 from 路径沿用了旧项目两层嵌套目录结构,
在当前扁平的 elec_rag_ui 项目中指向不存在的路径,导致打包产物缺失
语音识别模型文件。

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-15 20:07:03 +08:00
ssurui
71dce3b37b docs: 新增批量测试问题示例文件 batch_questions.txt
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
v8.0.0
2026-08-15 19:56:22 +08:00
ssurui
6f93912c86 feat: 初始提交——elec_rag_ui 独立前端工程
基于 Electron + Vue3 + TypeScript + LanceDB 的本地化 RAG 问答桌面应用。
仅支持 Linux(Kylin V10 / Ubuntu),适配 ER160(揽月 M50 NPU)和 ER25(芯动力 RPP)计算卡。
服务进程管理委托给 er_backend(HTTP 17700),前端专注 RAG 逻辑与用户交互。

包含:
- src/elec_rag_ui 前端工程(Electron + Vue3)
- models/ 本地嵌入模型(bge-small/base/large,ASR,标点恢复,onnx 文件由 Git LFS 管理)
- docs/ 示例文档(用于测试索引与问答)
2026-08-15 19:25:28 +08:00