perf: 设置页改为按需创建

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-24 00:20:00 +08:00
co-authored by Codex
parent 6ea57c6375
commit 8f8244af05
17 changed files with 51 additions and 30 deletions
+1 -1
View File
@@ -2,7 +2,7 @@
## 项目范围 ## 项目范围
MeloASR 是仅面向 Linux 的 Fcitx5 桌面语音输入工具。项目名为 `MeloASR`,分发包名为 `meloasr`,当前版本为 `0.1.12`。 MeloASR 是仅面向 Linux 的 Fcitx5 桌面语音输入工具。项目名为 `MeloASR`,分发包名为 `meloasr`,当前版本为 `0.1.13`。
不得恢复 Windows、PowerShell 或 Tauri 输出路径。发行版差异只能存在于依赖说明和打包配置,不能进入语音输入核心逻辑。 不得恢复 Windows、PowerShell 或 Tauri 输出路径。发行版差异只能存在于依赖说明和打包配置,不能进入语音输入核心逻辑。
+6
View File
@@ -2,6 +2,12 @@
本项目遵循 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/) 的格式。 本项目遵循 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/) 的格式。
## [0.1.13] - 2026-08-24
### 变更
- 设置页改为按需创建,关闭窗口后立即销毁其 renderer 以降低常驻内存。
## [0.1.12] - 2026-08-23 ## [0.1.12] - 2026-08-23
### 变更 ### 变更
+1 -1
View File
@@ -2,7 +2,7 @@
## 项目范围 ## 项目范围
MeloASR 是仅面向 Linux 的 Fcitx5 桌面语音输入工具。项目名为 `MeloASR`,分发包名为 `meloasr`,当前版本为 `0.1.12`。 MeloASR 是仅面向 Linux 的 Fcitx5 桌面语音输入工具。项目名为 `MeloASR`,分发包名为 `meloasr`,当前版本为 `0.1.13`。
不得恢复 Windows、PowerShell 或 Tauri 输出路径。发行版差异只能存在于依赖说明和打包配置,不能进入语音输入核心逻辑。 不得恢复 Windows、PowerShell 或 Tauri 输出路径。发行版差异只能存在于依赖说明和打包配置,不能进入语音输入核心逻辑。
+1 -1
View File
@@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.16) cmake_minimum_required(VERSION 3.16)
project(meloasr-fcitx5 VERSION 0.1.12 LANGUAGES CXX) project(meloasr-fcitx5 VERSION 0.1.13 LANGUAGES CXX)
include(GNUInstallDirs) include(GNUInstallDirs)
include(CTest) include(CTest)
+1 -1
View File
@@ -6,7 +6,7 @@ Comment[zh_CN]=将 MeloASR 网页后端连接到当前输入上下文
Type=SharedLibrary Type=SharedLibrary
Library=@MELOASR_LIBRARY_PATH@ Library=@MELOASR_LIBRARY_PATH@
Category=Module Category=Module
Version=0.1.12 Version=0.1.13
OnDemand=False OnDemand=False
Configurable=False Configurable=False
+2 -2
View File
@@ -1,12 +1,12 @@
{ {
"name": "meloasr", "name": "meloasr",
"version": "0.1.12", "version": "0.1.13",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "meloasr", "name": "meloasr",
"version": "0.1.12", "version": "0.1.13",
"devDependencies": { "devDependencies": {
"@types/node": "^24.0.0", "@types/node": "^24.0.0",
"electron": "42.3.0", "electron": "42.3.0",
+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"name": "meloasr", "name": "meloasr",
"productName": "MeloASR", "productName": "MeloASR",
"version": "0.1.12", "version": "0.1.13",
"private": true, "private": true,
"author": "MeloASR contributors", "author": "MeloASR contributors",
"description": "基于网页语音识别后端和 Fcitx5 的 Linux 实时语音输入工具", "description": "基于网页语音识别后端和 Fcitx5 的 Linux 实时语音输入工具",
+2 -2
View File
@@ -1,6 +1,6 @@
# Linux 分发打包 # Linux 分发打包
MeloASR 0.1.12 的系统包由两部分组成: MeloASR 0.1.13 的系统包由两部分组成:
- Electron 应用安装到 `/opt/meloasr`,命令入口为 `/usr/bin/meloasr`; - Electron 应用安装到 `/opt/meloasr`,命令入口为 `/usr/bin/meloasr`;
- Fcitx5 常驻 addon 由目标发行版中的 CMake/Fcitx5 开发包编译,并安装到该环境报告的 addon 目录。 - Fcitx5 常驻 addon 由目标发行版中的 CMake/Fcitx5 开发包编译,并安装到该环境报告的 addon 目录。
@@ -40,7 +40,7 @@ npm run build:linux:dir
packaging/deb/build-deb.sh packaging/deb/build-deb.sh
``` ```
产物位于 `packaging/out/meloasr_0.1.12_<arch>.deb`。依赖名称按 Debian/Ubuntu 系列填写,但尚未在各发行版完成安装验证。 产物位于 `packaging/out/meloasr_0.1.13_<arch>.deb`。依赖名称按 Debian/Ubuntu 系列填写,但尚未在各发行版完成安装验证。
## rpm ## rpm
+1 -1
View File
@@ -1,5 +1,5 @@
pkgname=meloasr pkgname=meloasr
pkgver=0.1.12 pkgver=0.1.13
pkgrel=1 pkgrel=1
pkgdesc='Web-backed voice input for Fcitx5' pkgdesc='Web-backed voice input for Fcitx5'
arch=('x86_64') arch=('x86_64')
+1 -1
View File
@@ -1,6 +1,6 @@
# Arch Linux 构建 # Arch Linux 构建
发布时把项目源码归档为 `meloasr-0.1.12.tar.gz`,与 `PKGBUILD` 放在同一目录,随后运行: 发布时把项目源码归档为 `meloasr-0.1.13.tar.gz`,与 `PKGBUILD` 放在同一目录,随后运行:
```bash ```bash
makepkg --syncdeps --cleanbuild makepkg --syncdeps --cleanbuild
+1
View File
@@ -16,6 +16,7 @@
<binary>meloasr</binary> <binary>meloasr</binary>
</provides> </provides>
<releases> <releases>
<release version="0.1.13" date="2026-08-24"/>
<release version="0.1.12" date="2026-08-23"/> <release version="0.1.12" date="2026-08-23"/>
<release version="0.1.11" date="2026-08-23"/> <release version="0.1.11" date="2026-08-23"/>
<release version="0.1.10" date="2026-08-23"/> <release version="0.1.10" date="2026-08-23"/>
+1 -1
View File
@@ -2,7 +2,7 @@
set -euo pipefail set -euo pipefail
project_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/../.." && pwd)" project_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/../.." && pwd)"
version="${MELOASR_VERSION:-0.1.12}" version="${MELOASR_VERSION:-0.1.13}"
arch="${MELOASR_DEB_ARCH:-$(dpkg --print-architecture)}" arch="${MELOASR_DEB_ARCH:-$(dpkg --print-architecture)}"
app_dir="${1:-${MELOASR_ELECTRON_APP_DIR:-${project_dir}/release/linux-unpacked}}" app_dir="${1:-${MELOASR_ELECTRON_APP_DIR:-${project_dir}/release/linux-unpacked}}"
work_dir="${project_dir}/packaging/out/deb-root" work_dir="${project_dir}/packaging/out/deb-root"
+1 -1
View File
@@ -2,7 +2,7 @@
set -euo pipefail set -euo pipefail
project_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/../.." && pwd)" project_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/../.." && pwd)"
version="${MELOASR_VERSION:-0.1.12}" version="${MELOASR_VERSION:-0.1.13}"
app_dir="${1:-${MELOASR_ELECTRON_APP_DIR:-${project_dir}/release/linux-unpacked}}" app_dir="${1:-${MELOASR_ELECTRON_APP_DIR:-${project_dir}/release/linux-unpacked}}"
stage_dir="${project_dir}/packaging/out/rpm-root" stage_dir="${project_dir}/packaging/out/rpm-root"
top_dir="${project_dir}/packaging/out/rpmbuild" top_dir="${project_dir}/packaging/out/rpmbuild"
+3
View File
@@ -36,6 +36,9 @@ tar -C %{buildroot} -xzf %{SOURCE0}
/etc/xdg/autostart/meloasr.desktop /etc/xdg/autostart/meloasr.desktop
%changelog %changelog
* Mon Aug 24 2026 MeloASR contributors - 0.1.13-1
- Create the settings renderer only when it is opened and destroy it on close.
* Sun Aug 23 2026 MeloASR contributors - 0.1.12-1 * Sun Aug 23 2026 MeloASR contributors - 0.1.12-1
- Disable Electron GPU acceleration for background web renderers. - Disable Electron GPU acceleration for background web renderers.
+1 -3
View File
@@ -39,9 +39,7 @@ async function bootstrap(): Promise<void> {
const repositoryUrl = process.env.MELOASR_REPOSITORY_URL?.trim() || const repositoryUrl = process.env.MELOASR_REPOSITORY_URL?.trim() ||
'https://github.com/Melorise/MeloASR'; 'https://github.com/Melorise/MeloASR';
const settingsWindow = new SettingsWindow(settings, backends, overlay, sessions, repositoryUrl); const settingsWindow = new SettingsWindow(settings, backends, overlay, sessions, repositoryUrl);
const settingsWindowReady = settingsWindow.create();
registerIpc({ settings, autoStart, bridge, backends, overlay, sessions, settingsWindow }); registerIpc({ settings, autoStart, bridge, backends, overlay, sessions, settingsWindow });
await settingsWindowReady;
const tray = new TrayController( const tray = new TrayController(
backends, backends,
settingsWindow, settingsWindow,
@@ -68,7 +66,7 @@ async function bootstrap(): Promise<void> {
await backends.ensure(settings.get().backendId); await backends.ensure(settings.get().backendId);
tray.refresh(); tray.refresh();
app.on('second-instance', () => settingsWindow.show()); app.on('second-instance', () => void settingsWindow.show());
app.on('before-quit', () => { (app as typeof app & { isQuitting?: boolean }).isQuitting = true; }); app.on('before-quit', () => { (app as typeof app & { isQuitting?: boolean }).isQuitting = true; });
app.on('will-quit', () => { app.on('will-quit', () => {
tray.destroy(); tray.destroy();
+25 -12
View File
@@ -9,6 +9,7 @@ import type { SettingsStore } from './settings-store';
export class SettingsWindow { export class SettingsWindow {
window: BrowserWindow | null = null; window: BrowserWindow | null = null;
private creating: Promise<BrowserWindow> | null = null;
constructor( constructor(
private readonly settings: SettingsStore, private readonly settings: SettingsStore,
@@ -18,8 +19,19 @@ export class SettingsWindow {
private readonly repositoryUrl: string | null private readonly repositoryUrl: string | null
) {} ) {}
async create(): Promise<void> { private async ensureWindow(): Promise<BrowserWindow> {
this.window = new BrowserWindow({ if (this.window && !this.window.isDestroyed()) return this.window;
if (this.creating) return this.creating;
this.creating = this.createWindow();
try {
return await this.creating;
} finally {
this.creating = null;
}
}
private async createWindow(): Promise<BrowserWindow> {
const window = new BrowserWindow({
width: 720, width: 720,
height: 820, height: 820,
minWidth: 640, minWidth: 640,
@@ -35,18 +47,19 @@ export class SettingsWindow {
sandbox: true sandbox: true
} }
}); });
this.window.on('close', (event) => { this.window = window;
if ((app as typeof app & { isQuitting?: boolean }).isQuitting) return; window.on('closed', () => {
event.preventDefault(); this.overlay.endPositioning();
this.window?.hide(); if (this.window === window) this.window = null;
}); });
this.window.on('hide', () => this.overlay.endPositioning()); await window.loadFile(path.join(__dirname, '..', 'renderer', 'settings.html'));
await this.window.loadFile(path.join(__dirname, '..', 'renderer', 'settings.html')); return window;
} }
show(): void { async show(): Promise<void> {
this.window?.show(); const window = await this.ensureWindow();
this.window?.focus(); window.show();
window.focus();
this.publish(); this.publish();
} }
@@ -60,7 +73,7 @@ export class SettingsWindow {
const definition = this.backends.activeDefinition(); const definition = this.backends.activeDefinition();
const decision = resolveDebugLoginAction(Boolean(current.loginNoticeShown[definition.id]), confirmed); const decision = resolveDebugLoginAction(Boolean(current.loginNoticeShown[definition.id]), confirmed);
if (decision.action === 'prompt') { if (decision.action === 'prompt') {
this.show(); await this.show();
this.window?.webContents.send('settings:login-notice', { backendLabel: definition.label }); this.window?.webContents.send('settings:login-notice', { backendLabel: definition.label });
return; return;
} }
+2 -2
View File
@@ -31,7 +31,7 @@ export class TrayController {
create(): void { create(): void {
this.tray = new Tray(trayImage('loading')); this.tray = new Tray(trayImage('loading'));
this.tray.setToolTip('MeloASR · 正在加载'); this.tray.setToolTip('MeloASR · 正在加载');
this.tray.on('click', () => this.settingsWindow.show()); this.tray.on('click', () => void this.settingsWindow.show());
this.refresh(); this.refresh();
} }
@@ -44,7 +44,7 @@ export class TrayController {
this.tray.setImage(trayImage(state)); this.tray.setImage(trayImage(state));
this.tray.setToolTip(`MeloASR · ${status.detail}`); this.tray.setToolTip(`MeloASR · ${status.detail}`);
this.tray.setContextMenu(Menu.buildFromTemplate([ this.tray.setContextMenu(Menu.buildFromTemplate([
{ label: '设置', click: () => this.settingsWindow.show() }, { label: '设置', click: () => void this.settingsWindow.show() },
{ label: '打开登录/调试页面', click: () => void this.settingsWindow.openDebug() }, { label: '打开登录/调试页面', click: () => void this.settingsWindow.openDebug() },
{ type: 'separator' }, { type: 'separator' },
{ label: '重启', click: this.onRestart }, { label: '重启', click: this.onRestart },