perf: 缓存会话编辑器并发布 0.1.14

AI-assisted-by: Codex

AI-model: gpt-5.6-sol

AI-reasoning-effort: medium

Co-authored-by: Codex <codex@openai.com>
This commit is contained in:
2026-08-26 20:36:16 +08:00
co-authored by Codex
parent cbe34e91ef
commit e6f67be337
15 changed files with 34 additions and 20 deletions
+1 -1
View File
@@ -2,7 +2,7 @@
## 项目范围
MeloASR 是仅面向 Linux 的 Fcitx5 桌面语音输入工具。项目名为 `MeloASR`,分发包名为 `meloasr`,当前版本为 `0.1.13`。
MeloASR 是仅面向 Linux 的 Fcitx5 桌面语音输入工具。项目名为 `MeloASR`,分发包名为 `meloasr`,当前版本为 `0.1.14`。
不得恢复 Windows、PowerShell 或 Tauri 输出路径。发行版差异只能存在于依赖说明和打包配置,不能进入语音输入核心逻辑。
+7
View File
@@ -2,6 +2,13 @@
本项目遵循 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/) 的格式。
## [0.1.14] - 2026-08-26
### 性能
- 语音会话开始时缓存网页编辑器根元素,后续文本更新直接序列化该元素,避免每次更新重复查询 DOM、计算样式和读取布局。
- 复用会话启动时的初始文本序列化结果,减少一次重复的完整编辑器遍历。
## [0.1.13] - 2026-08-24
### 变更
+1 -1
View File
@@ -2,7 +2,7 @@
## 项目范围
MeloASR 是仅面向 Linux 的 Fcitx5 桌面语音输入工具。项目名为 `MeloASR`,分发包名为 `meloasr`,当前版本为 `0.1.13`。
MeloASR 是仅面向 Linux 的 Fcitx5 桌面语音输入工具。项目名为 `MeloASR`,分发包名为 `meloasr`,当前版本为 `0.1.14`。
不得恢复 Windows、PowerShell 或 Tauri 输出路径。发行版差异只能存在于依赖说明和打包配置,不能进入语音输入核心逻辑。
+1 -1
View File
@@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.16)
project(meloasr-fcitx5 VERSION 0.1.13 LANGUAGES CXX)
project(meloasr-fcitx5 VERSION 0.1.14 LANGUAGES CXX)
include(GNUInstallDirs)
include(CTest)
+1 -1
View File
@@ -6,7 +6,7 @@ Comment[zh_CN]=将 MeloASR 网页后端连接到当前输入上下文
Type=SharedLibrary
Library=@MELOASR_LIBRARY_PATH@
Category=Module
Version=0.1.13
Version=0.1.14
OnDemand=False
Configurable=False
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "meloasr",
"version": "0.1.13",
"version": "0.1.14",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "meloasr",
"version": "0.1.13",
"version": "0.1.14",
"devDependencies": {
"@types/node": "^24.0.0",
"electron": "42.3.0",
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "meloasr",
"productName": "MeloASR",
"version": "0.1.13",
"version": "0.1.14",
"private": true,
"author": "MeloASR contributors",
"description": "基于网页语音识别后端和 Fcitx5 的 Linux 实时语音输入工具",
+2 -2
View File
@@ -1,6 +1,6 @@
# Linux 分发打包
MeloASR 0.1.13 的系统包由两部分组成:
MeloASR 0.1.14 的系统包由两部分组成:
- Electron 应用安装到 `/opt/meloasr`,命令入口为 `/usr/bin/meloasr`;
- Fcitx5 常驻 addon 由目标发行版中的 CMake/Fcitx5 开发包编译,并安装到该环境报告的 addon 目录。
@@ -40,7 +40,7 @@ npm run build:linux:dir
packaging/deb/build-deb.sh
```
产物位于 `packaging/out/meloasr_0.1.13_<arch>.deb`。依赖名称按 Debian/Ubuntu 系列填写,但尚未在各发行版完成安装验证。
产物位于 `packaging/out/meloasr_0.1.14_<arch>.deb`。依赖名称按 Debian/Ubuntu 系列填写,但尚未在各发行版完成安装验证。
## rpm
+1 -1
View File
@@ -1,5 +1,5 @@
pkgname=meloasr
pkgver=0.1.13
pkgver=0.1.14
pkgrel=1
pkgdesc='Web-backed voice input for Fcitx5'
arch=('x86_64')
+1 -1
View File
@@ -1,6 +1,6 @@
# Arch Linux 构建
发布时把项目源码归档为 `meloasr-0.1.13.tar.gz`,与 `PKGBUILD` 放在同一目录,随后运行:
发布时把项目源码归档为 `meloasr-0.1.14.tar.gz`,与 `PKGBUILD` 放在同一目录,随后运行:
```bash
makepkg --syncdeps --cleanbuild
+1
View File
@@ -16,6 +16,7 @@
<binary>meloasr</binary>
</provides>
<releases>
<release version="0.1.14" date="2026-08-26"/>
<release version="0.1.13" date="2026-08-24"/>
<release version="0.1.12" date="2026-08-23"/>
<release version="0.1.11" date="2026-08-23"/>
+1 -1
View File
@@ -2,7 +2,7 @@
set -euo pipefail
project_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/../.." && pwd)"
version="${MELOASR_VERSION:-0.1.13}"
version="${MELOASR_VERSION:-0.1.14}"
arch="${MELOASR_DEB_ARCH:-$(dpkg --print-architecture)}"
app_dir="${1:-${MELOASR_ELECTRON_APP_DIR:-${project_dir}/release/linux-unpacked}}"
work_dir="${project_dir}/packaging/out/deb-root"
+1 -1
View File
@@ -2,7 +2,7 @@
set -euo pipefail
project_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/../.." && pwd)"
version="${MELOASR_VERSION:-0.1.13}"
version="${MELOASR_VERSION:-0.1.14}"
app_dir="${1:-${MELOASR_ELECTRON_APP_DIR:-${project_dir}/release/linux-unpacked}}"
stage_dir="${project_dir}/packaging/out/rpm-root"
top_dir="${project_dir}/packaging/out/rpmbuild"
+3
View File
@@ -36,6 +36,9 @@ tar -C %{buildroot} -xzf %{SOURCE0}
/etc/xdg/autostart/meloasr.desktop
%changelog
* Wed Aug 26 2026 MeloASR contributors - 0.1.14-1
- Cache the active web editor for the duration of each speech session.
* Mon Aug 24 2026 MeloASR contributors - 0.1.13-1
- Create the settings renderer only when it is opened and destroy it on close.
+10 -7
View File
@@ -11,6 +11,7 @@ const adapter = getBackendWebAdapter(backendId);
let editorObserver: MutationObserver | undefined;
let pageObserver: MutationObserver | undefined;
let activeEditor: HTMLElement | null = null;
let active = false;
let stopping = false;
let lastSpeechText = '';
@@ -68,12 +69,11 @@ function schedulePageStatus(): void {
function emitCurrentEditorText(): void {
emitScheduled = false;
if (!active || !adapter) return;
const editor = adapter.findEditor(document);
if (!editor) {
if (!activeEditor?.isConnected) {
fail(`${adapter.definition.label}输入框在录音期间消失,本轮已停止`);
return;
}
const text = adapter.serialize(editor);
const text = adapter.serialize(activeEditor);
if (text === lastSpeechText) return;
lastSpeechText = text;
transcriptRevision += 1;
@@ -119,6 +119,7 @@ function resetSession(): void {
stopping = false;
lastTranscriptAt = 0;
editorObserver?.disconnect();
activeEditor = null;
diagnose('session-reset');
}
@@ -147,28 +148,30 @@ function startSpeech(): void {
return;
}
const initialText = adapter.serialize(editor);
diagnose('start-targets', {
editorLength: adapter.serialize(editor).length,
editorLength: initialText.length,
microphone: controlSnapshot(microphone)
});
clearTimeout(stopTimer);
placeCaretAtEnd(editor);
lastSpeechText = adapter.serialize(editor);
lastSpeechText = initialText;
transcriptRevision = 0;
lastTranscriptAt = Date.now();
active = true;
stopping = false;
activeEditor = editor;
observeEditor(editor);
microphone.click();
diagnose('start-clicked', { microphone: controlSnapshot(adapter.findMicrophone(document)) });
window.setTimeout(() => diagnose('start-after-250ms', {
microphone: controlSnapshot(adapter.findMicrophone(document)),
editorLength: adapter.findEditor(document) ? adapter.serialize(adapter.findEditor(document)!).length : null
editorLength: active && editor.isConnected ? adapter.serialize(editor).length : null
}), 250);
window.setTimeout(() => diagnose('start-after-1000ms', {
microphone: controlSnapshot(adapter.findMicrophone(document)),
editorLength: adapter.findEditor(document) ? adapter.serialize(adapter.findEditor(document)!).length : null
editorLength: active && editor.isConnected ? adapter.serialize(editor).length : null
}), 1_000);
}