cortico-world-asr 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +227 -0
- package/THIRD_PARTY_NOTICES.md +32 -0
- package/dist/console.css +2 -0
- package/dist/console.css.map +7 -0
- package/dist/console.js +2 -0
- package/dist/console.js.map +7 -0
- package/package.json +58 -0
- package/scripts/build-console.mjs +35 -0
- package/scripts/check-asr-runtime.ts +95 -0
- package/src/ENV_PROMPT.md +5 -0
- package/src/asr-client.ts +127 -0
- package/src/asr-server.ts +209 -0
- package/src/capture.ts +192 -0
- package/src/console/client.ts +151 -0
- package/src/console/icons.ts +40 -0
- package/src/console/listen.ts +345 -0
- package/src/console/overlay.ts +135 -0
- package/src/console/runtime.ts +131 -0
- package/src/console/style.css +79 -0
- package/src/definition.ts +16 -0
- package/src/firered-requirements.txt +9 -0
- package/src/firered-server.py +161 -0
- package/src/index.ts +12 -0
- package/src/overlay/app.js +104 -0
- package/src/overlay/overlay.html +16 -0
- package/src/overlay/styles.css +96 -0
- package/src/runtime/download.ts +29 -0
- package/src/runtime/env.ts +200 -0
- package/src/runtime/models.ts +121 -0
- package/src/segmenter.ts +284 -0
- package/src/simplify.ts +76 -0
- package/src/stream.ts +181 -0
- package/src/world.ts +1036 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Phantivia
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
# cortico-world-asr
|
|
2
|
+
|
|
3
|
+
Owner: `src/definition.ts`
|
|
4
|
+
|
|
5
|
+
[Cortico](https://github.com/Pal-AI-Lab/Cortico) 的语音识别 World,以独立 npm 包发布。
|
|
6
|
+
|
|
7
|
+
麦克风里的话经切分、识别、打包,作为外部事件进入上下文。 World **没有工具**:听是纯入站的事,
|
|
8
|
+
表达通道是嘴(cortico-world-vtuber)或文字,不在这儿。
|
|
9
|
+
|
|
10
|
+
## 与 Cortico 的关系
|
|
11
|
+
|
|
12
|
+
这是一个扩展包。它按 Cortico 的扩展契约声明自己:
|
|
13
|
+
|
|
14
|
+
```jsonc
|
|
15
|
+
"cortico": { "kind": "world", "api": 4, "consoleClient": "dist/console.js", "consoleStyle": "dist/console.css" }
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
运行时它以 `cortico/<框架 src 下的路径>` import 框架(`cortico/world.ts`、`cortico/core/types.ts`、
|
|
19
|
+
`cortico/paths.ts` …),由框架 `src/extensions/runtime.ts` 注册的模块钩子解析到框架源码本身,扩展与框架
|
|
20
|
+
共用同一份实例,所以包必须是 `"type": "module"`。开发期 `tsconfig.json` 与 `vitest.config.ts` 把同一前缀
|
|
21
|
+
指到同级的框架 checkout(`../BOT/src/`)。浏览器侧(`src/console/**`)对 `cortico/*` 只 `import type`。
|
|
22
|
+
|
|
23
|
+
## 安装
|
|
24
|
+
|
|
25
|
+
先在本目录构建面板产物(`dist/` 不进版本库,没有它控制台的语音识别页是空的):
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
corepack pnpm install
|
|
29
|
+
corepack pnpm build
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
然后二选一:控制台「扩展」页手动安装,填本目录的绝对路径;或在 `<Cortico>/extensions/` 下
|
|
33
|
+
`corepack pnpm add --ignore-workspace <本目录绝对路径>`。装完整进程重启 Cortico。
|
|
34
|
+
|
|
35
|
+
识别后端的运行时与权重另装,见下一节。
|
|
36
|
+
|
|
37
|
+
## 运行时与权重
|
|
38
|
+
|
|
39
|
+
World 不带推理代码、Python 环境,也不带权重;控制台「收听」面板下面那张「运行时与权重」卡负责把它们取来:
|
|
40
|
+
|
|
41
|
+
- **运行时**是 FireRedASR2S 的 Python 环境,装到 `<运行时根>/firered-asr/<commit>/cu128/`:
|
|
42
|
+
`git` 取上游代码并钉在 `4e7d9aaf`,`uv` 建 Python 3.12 环境、装 PyTorch 2.9.1(cu128 wheel,约 3 GB)
|
|
43
|
+
与 `src/firered-requirements.txt`。装在 `<目录>.partial` 里,写完 `cortico-runtime.json` 才整个改名到位。
|
|
44
|
+
本机要有 `git` 与 `uv` 在 PATH 上;World 不装它们,也不碰 NVIDIA 驱动。CPU 档用同一份环境。
|
|
45
|
+
- **权重**是 FireRedASR2-AED 的四个文件(`model.pth.tar` 4.7 GB、`cmvn.ark`、`dict.txt`、`train_bpe1000.model`),
|
|
46
|
+
从 HuggingFace 钉住的 revision 下到 `<模型根>/asr/FireRedASR2-AED/`。每个文件下到 `.partial` 再改名,
|
|
47
|
+
下一半的文件不会冒充齐了。来源与许可见 [`THIRD_PARTY_NOTICES.md`](THIRD_PARTY_NOTICES.md)。
|
|
48
|
+
|
|
49
|
+
运行时根与模型根是部署根下的 `runtimes/` 与 `models/`,多份部署共用;约定见框架 `docs/runtimes.md`。
|
|
50
|
+
|
|
51
|
+
自备的优先:`config.json` 的 `worlds.asr.backend` 节里
|
|
52
|
+
|
|
53
|
+
```jsonc
|
|
54
|
+
"runtimeDir": "D:/firered", // 含 .venv/ 与 FireRedASR2S/ 的目录;填了就不安装
|
|
55
|
+
"modelFile": "D:/weights/FireRedASR2-AED/model.pth.tar", // 同目录须有另外三个文件;填了就不下载
|
|
56
|
+
"runtimeRevision": "" // 托管安装取哪个 commit;空 = 包里钉住的
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
`modelFile` 也可以是相对 `<模型根>/asr/` 的路径;留空时面板下拉框列出该目录下每一组完整的权重,
|
|
60
|
+
默认挑 `FireRedASR2-AED`。运行时目录、权重都在了,后端才起得来;缺哪样,面板上那一行写得出来。
|
|
61
|
+
|
|
62
|
+
真机跑一遍全流程(联网、要 git 与 uv、要显卡):
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
tsx scripts/check-asr-runtime.ts # 装运行时、下权重、起 server、转写一段合成音
|
|
66
|
+
tsx scripts/check-asr-runtime.ts --cpu # CPU 档
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
默认装在 `scratch/asr-runtime-check/` 下,不碰真部署。
|
|
70
|
+
|
|
71
|
+
## 开发
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
corepack pnpm typecheck # tsc --noEmit,Node 侧与浏览器侧一份配置一起 check
|
|
75
|
+
corepack pnpm test # vitest run
|
|
76
|
+
corepack pnpm build # esbuild → dist/console.{js,css}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
测试全程不联网、不开声卡、不起 Python:运行时安装用假的命令执行器断言每一步与目录协议,
|
|
80
|
+
权重下载用假的 fetch,后端进程管理用 Node 自己当替身。`firered-server.py` 的协议测试是
|
|
81
|
+
`python -m unittest discover -s tests/asr -p 'test_firered_server.py'`。
|
|
82
|
+
|
|
83
|
+
## 结构
|
|
84
|
+
|
|
85
|
+
```
|
|
86
|
+
capture.ts 声卡:RtAudio 输入,16k 单声道,20ms 一帧
|
|
87
|
+
segmenter.ts 切分(能量门限状态机)+ 打包(相邻句子并成一条投递)——纯逻辑
|
|
88
|
+
asr-client.ts 转写端点客户端(OpenAI 兼容 /audio/transcriptions)+ WAV 封装 + 幻觉过滤
|
|
89
|
+
asr-server.ts FireRedASR2-AED 的进程管理(gpu / cpu 两档)
|
|
90
|
+
firered-server.py 本地端点:把 /v1/audio/transcriptions 接到 FireRedASR2-AED
|
|
91
|
+
runtime/env.ts 运行时安装:git + uv 建出的 Python 环境,一个 commit 一个目录
|
|
92
|
+
runtime/models.ts 权重下载:四个文件,固定 revision
|
|
93
|
+
stream.ts /overlay 字幕页 + SSE /stream(自有端口)
|
|
94
|
+
overlay/ 字幕页三件套(OBS 浏览器源与控制台预览是同一份)
|
|
95
|
+
console/ 控制台扩展:收听(含运行时与权重)/ Overlay 字幕 两个面板
|
|
96
|
+
world.ts 装配:配置、面板、事件投递
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
一条链四段,每段都能单独换掉:
|
|
100
|
+
|
|
101
|
+
```
|
|
102
|
+
声卡 → 切分 → 识别端点 → 打包投递
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
World 跑在主进程里,不像 cortico-world-vtuber 那样开子进程:这条链上唯一的常驻计算是每帧一次
|
|
106
|
+
均方根(20ms 一帧、320 个样本),识别本身在另一个进程/另一台机器上。没有 60Hz 的
|
|
107
|
+
同步工作要隔离,子进程只会多一层 IPC。
|
|
108
|
+
|
|
109
|
+
## 后端:FireRedASR2-AED
|
|
110
|
+
|
|
111
|
+
客户端使用 `POST /v1/audio/transcriptions`:multipart 字段 `file` 是 16 kHz、单声道、PCM16 WAV,响应为
|
|
112
|
+
`{ "text": "..." }`。`firered-server.py` 将该接口接到 FireRedASR2-AED。麦克风采集、切分、简体转换、纠错、
|
|
113
|
+
字幕和事件投递共用同一条链路,换成别的兼容端点只改 `baseUrl`。
|
|
114
|
+
|
|
115
|
+
`worlds.asr.backend.model` 默认为 `FireRedASR2-AED`,只作标签;本地服务实际加载的模型由 `modelFile`
|
|
116
|
+
决定。`baseUrl` 默认 `http://127.0.0.1:8793/v1`,自带后端就起在这个端口上。显卡档使用 CUDA,CPU 档使用
|
|
117
|
+
相同权重;`threads=0` 使用 4 线程。AED 按自身能力识别中英文,没有强制语言参数;接口接受 `language`
|
|
118
|
+
以兼容现有客户端,但该提示不改变 AED 解码。
|
|
119
|
+
|
|
120
|
+
推理参数为 FP32、beam=3、softmax_smoothing=1.25、aed_length_penalty=0.6、eos_penalty=1,无时间戳和外部
|
|
121
|
+
语言模型。请求独立,不携带前一句文本。输入直接在内存中解码,不写入临时录音文件;单段最长 60 秒,
|
|
122
|
+
与切分配置上限一致。
|
|
123
|
+
|
|
124
|
+
管理器沿用 `state/start/stop` 和 `autoStart`。模型加载与预热结束后才开放 HTTP,因此成功探活表示可识别。
|
|
125
|
+
Windows 停止托管后端会结束 Python 子进程树。已由外部启动的兼容端点仍可复用,管理器只停止自己启动的进程。
|
|
126
|
+
|
|
127
|
+
## 切分:哪一段值得送去识别
|
|
128
|
+
|
|
129
|
+
声卡给的是每 20ms 一帧,而识别的输入单位是"一句"。中间这层是能量门限状态机:
|
|
130
|
+
|
|
131
|
+
- `thresholdDb` 判为"有人在说"的门槛。安静房间底噪常在 -60 上下,人声峰值在 -20 上下。
|
|
132
|
+
- `minSpeechMs` 连续超过门槛多久才算开口——咳嗽、键盘、鼠标点击挡在这一关。
|
|
133
|
+
- `dispatchSilenceMs` 停这么久就把手上这段**送去转写**(短门限,默认 250)。
|
|
134
|
+
- `silenceMs` 停这么久才算**一句说完**(长门限,默认 500)——发不发车看它。
|
|
135
|
+
- `preRollMs` 触发点往前多带一段:**人开口的第一个字总在能量越线之前**。
|
|
136
|
+
- `maxUtteranceMs` 一直说不停时到点强切,切完接着收——她不该等一个人讲完五分钟
|
|
137
|
+
才听见第一个字。
|
|
138
|
+
- `minUtteranceMs` 短于它的碎片直接丢。
|
|
139
|
+
|
|
140
|
+
门槛调得对不对,「收听」面板上那条带刻度的电平条一眼就能看出来:说话时越过刻度、
|
|
141
|
+
安静时不越过。这是这个面板存在的主要理由,数字本身说不清这件事。
|
|
142
|
+
|
|
143
|
+
短门限让识别与剩余的收尾静音重叠。转写结果返回且收尾静音结束后才投递;
|
|
144
|
+
若识别耗时超过剩余静音,继续等待识别完成。
|
|
145
|
+
|
|
146
|
+
短门限不会把句子切碎,因为**它切的是音频、不是发车**:长门限没到之前
|
|
147
|
+
`settleRemainingMs` 一直报"还没说完",打包层照旧按着;人在窗口里接着说,后半段自然接上,
|
|
148
|
+
两半在打包层并回一条。给到 `dispatchSilenceMs >= silenceMs` 就退回原来的单级行为。
|
|
149
|
+
|
|
150
|
+
## 打包:几句话算一次开口
|
|
151
|
+
|
|
152
|
+
识别结果**不逐句投递**。人说话是一串短句,逐句唤醒等于把一段话拆成五次打断——但
|
|
153
|
+
"几句算一次开口"这件事已经由切分层的 `silenceMs` 定住了:停顿短于收尾静音的两句根本
|
|
154
|
+
不会变成两批。所以这一层只剩三个数:
|
|
155
|
+
|
|
156
|
+
- `joinGapMs` 转写落地后**额外**再空等多久(默认 0)。给多少就直接加多少响应延迟;
|
|
157
|
+
收尾静音之外真正需要它的场合已经没有了,后到的句子由总线下一批带走。
|
|
158
|
+
- `maxHoldMs` 一条最多攒这么久。只在 `joinGapMs > 0` 时才有机会当判据。
|
|
159
|
+
- `minChars` 少于这么多字的结果丢掉:噪声与语气词识别出来常是一两个字。
|
|
160
|
+
|
|
161
|
+
打包发车不用 100ms 轮询。每次转写落地都会立即检查一次并按精确 deadline 下钟。持有的
|
|
162
|
+
判据有三条:麦克风正在收音、有音频待转写或转写在途、收尾静音还没到点。前两条各自会在
|
|
163
|
+
自己完事时再叫一次;第三条有确定的到点时刻,所以由 `flushPackedIfDue` 自己下钟——短门限
|
|
164
|
+
早就把音频交出去了,那一段最后一个帧回调可能已经过去了。
|
|
165
|
+
|
|
166
|
+
## 出口纠错表
|
|
167
|
+
|
|
168
|
+
`worlds.asr.corrections` 每条为 `错=对`,用换行或分号分隔;在简体化与幻觉过滤之后、投递之前作整段替换。
|
|
169
|
+
适用于人名或专有词的固定误识别,左侧匹配文本较长的规则先执行。
|
|
170
|
+
|
|
171
|
+
## 幻觉过滤
|
|
172
|
+
|
|
173
|
+
whisper 系模型在纯噪声上会稳定地吐出训练集里的高频片段("谢谢观看"、字幕组落款、
|
|
174
|
+
`[音乐]` 这类标注)。它们与真话在文本上无从分辨,只能按名单挡
|
|
175
|
+
(`asr-client.ts` 的 `looksHallucinated`)。漏一条的代价是她收到一句没人说过的话,
|
|
176
|
+
并且会当真去回应——所以这道过滤宁可误杀。被挡下的在面板上标成 `[丢弃]`,不静默。
|
|
177
|
+
|
|
178
|
+
## 事件面
|
|
179
|
+
|
|
180
|
+
| 事件 | 触发 | 说明 |
|
|
181
|
+
| --- | --- | --- |
|
|
182
|
+
| `asr.speech` | `flush`(`worlds.asr.wake` 关掉则 `debounce`) | `[语音] <说话人>:<话>` |
|
|
183
|
+
|
|
184
|
+
识别分不出是谁在说,`worlds.asr.speaker` 就是事件里那个"谁"。
|
|
185
|
+
|
|
186
|
+
`flush` 保持事件 FIFO,整批立即发车:到达就冲洗合批窗口,把积压一起带走,
|
|
187
|
+
不打断在途的那一轮——后到的话等这轮说完再进下一批。
|
|
188
|
+
|
|
189
|
+
## Overlay 字幕
|
|
190
|
+
|
|
191
|
+
`stream.ts` 起一个自己的 HTTP 口(`worlds.asr.streamPort`,被占顺延):
|
|
192
|
+
`/overlay` 是字幕页,`/stream` 是它的 SSE 数据面。OBS 加一个浏览器源指到 `/overlay`,
|
|
193
|
+
控制台的预览用 iframe 嵌**同一个页面**(带 `?bg=dim` 给个暗底)——两份渲染意味着
|
|
194
|
+
"预览里好看、OBS 里不对"这类问题永远查不清。
|
|
195
|
+
|
|
196
|
+
URL 参数按订阅方覆盖:`?lines=3` 只留三行、`?partial=0` 不显示"正在说…"。
|
|
197
|
+
样式(字号、字重、颜色、描边、底板、字体、保留行数、停留时长)在面板上改,
|
|
198
|
+
服务端钳完值经 SSE 热推给所有订阅者,OBS 那份不用重开;同时经装配层写回 config.json。
|
|
199
|
+
|
|
200
|
+
## 控制台面板
|
|
201
|
+
|
|
202
|
+
- **收听(listen)**:麦克风选择、带门槛刻度的电平条、识别后端启停、实时识别文本,以及下面
|
|
203
|
+
那张「运行时与权重」卡(安装运行时、下载权重、看进度)。电平与文本走 `ctx.stream`
|
|
204
|
+
(World 的 `WorldConsoleDecl.stream`)实时推,计数与后端状态五秒轮一次。
|
|
205
|
+
- **Overlay 字幕(overlay)**:链接、样式、试显、预览。
|
|
206
|
+
|
|
207
|
+
## 挂载
|
|
208
|
+
|
|
209
|
+
```ts
|
|
210
|
+
if (cfg.worlds.asr.enabled) {
|
|
211
|
+
worlds.push(new AsrWorld({
|
|
212
|
+
cfg: cfg.worlds.asr,
|
|
213
|
+
timezone: cfg.timezone,
|
|
214
|
+
onDevice: (device) => { /* 写回 config.json */ },
|
|
215
|
+
onOverlayConfig: (overlay) => { /* 同上 */ },
|
|
216
|
+
onModelFile: (modelFile) => { /* 同上 */ },
|
|
217
|
+
}));
|
|
218
|
+
}
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
麦克风、权重与字幕样式是在面板上调的,调完要留住:World 只管当场生效,落盘经装配层
|
|
222
|
+
(与 cortico-world-vtuber 的 overlay/TTS 档案同一套办法)。
|
|
223
|
+
|
|
224
|
+
## 许可
|
|
225
|
+
|
|
226
|
+
MIT,见 [LICENSE](LICENSE)。框架 Cortico 也是 MIT,两者经扩展契约相连,许可各归各。
|
|
227
|
+
第三方(FireRedASR2S、权重、PyTorch)见 [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md)。
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# 第三方声明
|
|
2
|
+
|
|
3
|
+
这个包自己不带推理代码、Python 环境,也不带模型权重。下面是它在运行时会去取、或者要求操作者
|
|
4
|
+
自己装好的东西,以及各自的许可。
|
|
5
|
+
|
|
6
|
+
## 识别运行时
|
|
7
|
+
|
|
8
|
+
FireRedASR2S 的推理代码,钉在一个 commit 上,由「收听」面板取到
|
|
9
|
+
`<运行时根>/firered-asr/<commit>/cu128/FireRedASR2S/`,并在同目录 `.venv/` 里建 Python 环境。
|
|
10
|
+
|
|
11
|
+
- 上游:[FireRedTeam/FireRedASR2S](https://github.com/FireRedTeam/FireRedASR2S),Apache-2.0
|
|
12
|
+
- Python 3.12 由 uv 取用;PyTorch、torchaudio、torchvision 取自 PyTorch 官方 cu128 wheel 源,BSD-3-Clause;
|
|
13
|
+
其余依赖见 `src/firered-requirements.txt`,各随其上游许可
|
|
14
|
+
- `git` 与 `uv` 由操作者自己装好放进 PATH;本项目不修改本机的驱动、运行库与应用控制策略
|
|
15
|
+
|
|
16
|
+
## 模型权重
|
|
17
|
+
|
|
18
|
+
放在 `<模型根>/asr/FireRedASR2-AED/`,由面板按固定 revision 从 HuggingFace 下载。
|
|
19
|
+
|
|
20
|
+
| 文件 | 来源 | 许可 |
|
|
21
|
+
|---|---|---|
|
|
22
|
+
| `model.pth.tar`、`cmvn.ark`、`dict.txt`、`train_bpe1000.model` | [FireRedTeam/FireRedASR2-AED](https://huggingface.co/FireRedTeam/FireRedASR2-AED) @ `2304afe` | Apache-2.0(模型卡所载) |
|
|
23
|
+
|
|
24
|
+
## 其他运行时依赖
|
|
25
|
+
|
|
26
|
+
- **声卡**:`audify`(RtAudio 的 Node 绑定)随包安装,MIT
|
|
27
|
+
- **简繁转换**:`opencc-js`,MIT AND Apache-2.0
|
|
28
|
+
|
|
29
|
+
## 本包的许可
|
|
30
|
+
|
|
31
|
+
MIT,见 `LICENSE`。框架 [Cortico](https://github.com/Pal-AI-Lab/Cortico) 也是 MIT,两者经扩展契约相连,
|
|
32
|
+
许可各归各。
|
package/dist/console.css
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
.mountrow .mstate.asr-on{color:var(--ok)}.mountrow .mstate.asr-off{color:var(--danger)}.mountrow .mstate.asr-warn{color:var(--ink-soft)}.asr-form{display:flex;flex-wrap:wrap;align-items:flex-end;gap:8px 14px;margin:6px 0 2px}.asr-form .fieldrow{margin:0}.asr-form .fieldrow>input.field{width:8em}.asr-form .fieldrow>select.field{min-width:12em}.asr-meterrow{display:flex;align-items:center;gap:10px;margin:8px 0 4px}.asr-meter{position:relative;flex:1;height:14px;border-radius:7px;background:var(--sheet-3);border:1px solid var(--line-2);overflow:hidden}.asr-meterfill{height:100%;width:0;background:var(--line-strong);transition:width 90ms linear}.asr-meterfill.on{background:var(--accent)}.asr-metermark{position:absolute;top:0;bottom:0;width:2px;background:var(--ink);opacity:.55}.asr-readout{min-width:8.5em;font-size:12px;color:var(--ink-soft);font-variant-numeric:tabular-nums}.asr-preview{width:100%;aspect-ratio:16 / 9;border:1px solid var(--line-2);border-radius:10px;background:#1c1a17;margin-top:6px}
|
|
2
|
+
/*# sourceMappingURL=console.css.map */
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/console/style.css"],
|
|
4
|
+
"sourcesContent": ["/*\n * \u8BED\u97F3\u8BC6\u522B\u9762\u677F\u7684\u4E13\u6709\u6837\u5F0F\u3002\n *\n * \u53EA\u653E `ctx.ui` \u6CA1\u6709\u5BF9\u5E94\u539F\u8BED\u7684\u90A3\u4E09\u5757:\u7535\u5E73\u6761(\u5E26\u95E8\u69DB\u523B\u5EA6)\u3001\u4E00\u884C\u6392\u5F00\u7684\u8868\u5355\u5757\u3001\n * \u5B57\u5E55\u9884\u89C8\u6846\u3002\u5176\u4F59\u4E00\u5F8B\u7528\u539F\u8BED(sheet / rowbar / section / btn / kv / log / stat /\n * field / checkbox / select),\u90A3\u4E9B class \u5F52\u63A7\u5236\u53F0\u7BA1,\u8FD9\u91CC\u4E00\u4E2A\u5B57\u90FD\u4E0D\u8986\u76D6\u3002\n *\n * \u4E24\u6761\u81EA\u5F8B(\u4E0E mc\u3001QQ \u90A3\u4E24\u4EFD\u540C\u6E90):\u7C7B\u540D\u4E00\u5F8B `asr-` \u524D\u7F00;\u989C\u8272\u53EA\u7528\u63A7\u5236\u53F0\u7684\u4E3B\u9898\u53D8\u91CF\u3002\n *\n * \u7535\u5E73\u6761\u4E0D\u7528 `ui.progress`:\u90A3\u662F\"\u8FD9\u4E2A\u6570\u5360\u591A\u5C11\"\u7684\u8BFB\u6570\u6761,\u800C\u8FD9\u91CC\u8981\u7684\u662F**\u95E8\u69DB\u523B\u5EA6**\n * \u2014\u2014\u6761\u8D70\u5230\u54EA\u4E0D\u91CD\u8981,\u8FC7\u6CA1\u8FC7\u90A3\u9053\u7EBF\u624D\u662F\u552F\u4E00\u8981\u770B\u7684\u4E8B\u3002\n */\n\n/* ---- \u8BC6\u522B\u540E\u7AEF\u90A3\u4E00\u884C\u7684\u72B6\u6001\u8BCD\u914D\u8272(\u501F\u7528 .mountrow \u7684\u7248\u5F0F) ---- */\n.mountrow .mstate.asr-on { color: var(--ok); }\n.mountrow .mstate.asr-off { color: var(--danger); }\n.mountrow .mstate.asr-warn { color: var(--ink-soft); }\n\n/* ---- \u4E00\u884C\u6392\u5F00\u7684\u8868\u5355\u5757 ---- */\n.asr-form {\n display: flex;\n flex-wrap: wrap;\n align-items: flex-end;\n gap: 8px 14px;\n margin: 6px 0 2px;\n}\n.asr-form .fieldrow { margin: 0; }\n.asr-form .fieldrow > input.field { width: 8em; }\n.asr-form .fieldrow > select.field { min-width: 12em; }\n\n/* ---- \u7535\u5E73\u6761 ---- */\n.asr-meterrow {\n display: flex;\n align-items: center;\n gap: 10px;\n margin: 8px 0 4px;\n}\n.asr-meter {\n position: relative;\n flex: 1;\n height: 14px;\n border-radius: 7px;\n background: var(--sheet-3);\n border: 1px solid var(--line-2);\n overflow: hidden;\n}\n.asr-meterfill {\n height: 100%;\n width: 0;\n background: var(--line-strong);\n transition: width 90ms linear;\n}\n/* \u8D8A\u8FC7\u95E8\u69DB(\u5224\u4E3A\"\u5728\u8BF4\u8BDD\")\u65F6\u6362\u6210\u5F3A\u8C03\u8272:\u8FD9\u4E00\u773C\u5C31\u662F\u95E8\u69DB\u8C03\u5BF9\u6CA1\u6709 */\n.asr-meterfill.on { background: var(--accent); }\n/* \u95E8\u69DB\u523B\u5EA6 */\n.asr-metermark {\n position: absolute;\n top: 0;\n bottom: 0;\n width: 2px;\n background: var(--ink);\n opacity: 0.55;\n}\n.asr-readout {\n min-width: 8.5em;\n font-size: 12px;\n color: var(--ink-soft);\n font-variant-numeric: tabular-nums;\n}\n\n/* ---- \u5B57\u5E55\u9884\u89C8:\u5D4C\u7684\u5C31\u662F OBS \u91CC\u90A3\u4E00\u4EFD overlay \u9875 ---- */\n.asr-preview {\n width: 100%;\n aspect-ratio: 16 / 9;\n border: 1px solid var(--line-2);\n border-radius: 10px;\n background: #1c1a17;\n margin-top: 6px;\n}\n"],
|
|
5
|
+
"mappings": "AAcA,CAAC,SAAS,CAAC,MAAM,CAAC,OAAS,MAAO,IAAI,KAAO,CAC7C,CADC,SACS,CADC,MACM,CAAC,QAAU,MAAO,IAAI,SAAW,CAClD,CAFC,SAES,CAFC,MAEM,CAAC,SAAW,MAAO,IAAI,WAAa,CAGrD,CAAC,SACC,QAAS,KACT,UAAW,KACX,YAAa,SACb,IAAK,IAAI,KAvBX,OAwBU,IAAI,EAAE,GAChB,CACA,CAPC,SAOS,CAAC,SA1BX,OA0B8B,CAAG,CACjC,CARC,SAQS,CADC,QACS,CAAE,KAAK,CAAC,MAAQ,MAAO,GAAK,CAChD,CATC,SASS,CAFC,QAES,CAAE,MAAM,CADA,MACS,UAAW,IAAM,CAGtD,CAAC,aACC,QAAS,KACT,YAAa,OACb,IAAK,KAlCP,OAmCU,IAAI,EAAE,GAChB,CACA,CAAC,UACC,SAAU,SACV,KAAM,EACN,OAAQ,KAxCV,cAyCiB,IACf,WAAY,IAAI,WAChB,OAAQ,IAAI,MAAM,IAAI,UACtB,SAAU,MACZ,CACA,CAAC,cACC,OAAQ,KACR,MAAO,EACP,WAAY,IAAI,eAChB,WAAY,MAAM,KAAK,MACzB,CAEA,CAPC,aAOa,CAAC,GAAK,WAAY,IAAI,SAAW,CAE/C,CAAC,cACC,SAAU,SACV,IAAK,EACL,OAAQ,EACR,MAAO,IACP,WAAY,IAAI,OAChB,QAAS,GACX,CACA,CAAC,YACC,UAAW,MACX,UAAW,KACX,MAAO,IAAI,YACX,qBAAsB,YACxB,CAGA,CAAC,YACC,MAAO,KACP,aAAc,GAAG,EAAE,EACnB,OAAQ,IAAI,MAAM,IAAI,UA1ExB,cA2EiB,KACf,WAAY,QACZ,WAAY,GACd",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/console.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var oe="http://www.w3.org/2000/svg",ue={"folder-open":[["path",{d:"M3 6h6l2 2h10"}],["path",{d:"M3 6v13h15l3-8H6l-3 8"}]],download:[["path",{d:"M12 3v12"}],["path",{d:"m7 10 5 5 5-5"}],["path",{d:"M5 21h14"}]]};function K(t,e,y="icon"){let s=t.createElementNS(oe,"svg");s.setAttribute("viewBox","0 0 24 24"),s.setAttribute("fill","none"),s.setAttribute("stroke","currentColor"),s.setAttribute("stroke-width","1.8"),s.setAttribute("stroke-linecap","round"),s.setAttribute("stroke-linejoin","round"),s.setAttribute("aria-hidden","true"),s.classList.add(y);for(let[v,a]of ue[e]){let h=t.createElementNS(oe,v);for(let[S,C]of Object.entries(a))h.setAttribute(S,C);s.appendChild(h)}return s}var me="\u8FD0\u884C\u65F6\u662F FireRedASR2S \u7684 Python \u73AF\u5883(git \u53D6\u4EE3\u7801\u3001uv \u5EFA\u73AF\u5883\u5E76\u88C5 PyTorch),\u88C5\u5230\u90E8\u7F72\u6839\u7684 runtimes/firered-asr/ \u4E0B;\u6743\u91CD\u662F FireRedASR2-AED \u7684\u56DB\u4E2A\u6587\u4EF6,\u4E0B\u5230 models/asr/FireRedASR2-AED/\u3002\u914D\u7F6E\u91CC\u586B\u4E86\u300C\u8FD0\u884C\u65F6\u76EE\u5F55(\u81EA\u5907)\u300D\u6216\u300C\u6743\u91CD\u6587\u4EF6(\u81EA\u5907)\u300D\u7684\u90A3\u4E00\u6837\u5C31\u4E0D\u518D\u88C5\u3002\u5B89\u88C5\u8981\u672C\u673A\u6709 git \u4E0E uv\u3002";function j(t){return t>=1e9?`${(t/1e9).toFixed(2)} GB`:t>=1e6?`${Math.round(t/1e6)} MB`:`${t} B`}function ve(t,e){return e?`${j(t)} / ${j(e)}(${Math.round(t/e*100)}%)`:j(t)}function ie(t){let{ui:e}=t,y=e.sheet({title:"\u8FD0\u884C\u65F6\u4E0E\u6743\u91CD",en:"runtime",desc:me}),s=e.msgline(""),v=e.chip("\u2014"),a=e.button("\u5B89\u88C5\u8FD0\u884C\u65F6",{size:"sm",onClick:()=>{b()}}),h=e.rowbar();h.append(e.pill("\u8FD0\u884C\u65F6","plain"),v,s,e.h("span","grow"),a);let S=e.h("div","pagedesc"),C=e.h("div","pagedesc"),c=e.msgline(""),P=e.chip("\u2014"),D=e.button("\u4E0B\u8F7D\u6743\u91CD",{size:"sm",onClick:()=>{w()}}),$=e.rowbar();$.append(e.pill("\u6743\u91CD","plain"),P,c,e.h("span","grow"),D);let R=e.h("div","pagedesc"),x=e.h("div");y.body.append(h,S,C,e.section("\u6743\u91CD","FireRedASR2-AED,\u56DB\u4E2A\u6587\u4EF6\u7F3A\u4E00\u4E0D\u53EF"),$,R,x),t.root.appendChild(y.el);let g=(o,u,k=!1)=>{o.textContent=u,o.classList.toggle("bad",k)},L=!1;async function d(){let o;try{o=await t.invoke("runtime")}catch(p){t.signal.aborted||g(s,A(p),!0);return}if(t.signal.aborted)return;let u=o.install.phase,k=o.models.some(p=>p.phase==="downloading");L=u==="installing"||k,v.textContent=o.own?"\u81EA\u5907\u76EE\u5F55":o.supported?u==="installed"?o.revision.slice(0,7):u==="installing"?`\u5B89\u88C5\u4E2D \xB7 ${o.install.step??""}`:u==="error"?"\u88C5\u5931\u8D25":"\u672A\u5B89\u88C5":"\u672C\u5E73\u53F0\u65E0\u6258\u7BA1\u65B9\u6848",S.textContent=o.dir||"(\u8FD8\u6CA1\u6709\u76EE\u5F55)",C.textContent=u==="installing"?o.install.line??"":"",a.disabled=L||o.own||!o.supported,a.textContent=u==="installed"?"\u91CD\u88C5\u8FD0\u884C\u65F6":"\u5B89\u88C5\u8FD0\u884C\u65F6",o.install.detail&&g(s,o.install.detail,!0),P.textContent=o.modelsComplete?"\u9F50\u4E86":k?"\u4E0B\u8F7D\u4E2D":"\u7F3A\u6587\u4EF6",R.textContent=`${o.modelsDir} \xB7 \u6765\u6E90 ${o.modelsSource}`,D.disabled=L||o.modelsComplete,x.replaceChildren();for(let p of o.models){let O=e.rowbar(),l=p.phase==="present"?j(p.bytes):p.phase==="downloading"?ve(p.done,p.total):p.phase==="error"?p.detail??"\u4E0B\u8F7D\u5931\u8D25":"\u672A\u4E0B\u8F7D";O.append(e.chip(p.file),e.h("span",p.phase==="error"?"bad":"",l)),x.appendChild(O)}}async function b(){g(s,"\u5F00\u59CB\u5B89\u88C5:\u53D6\u4EE3\u7801\u3001\u5EFA\u73AF\u5883\u3001\u88C5 PyTorch(\u51E0 GB),\u522B\u5173\u9875\u9762"),M();try{await t.invoke("installRuntime"),g(s,"\u8FD0\u884C\u65F6\u88C5\u597D\u4E86")}catch(o){t.signal.aborted||g(s,A(o),!0)}d()}async function w(){g(c,"\u5F00\u59CB\u4E0B\u8F7D,model.pth.tar \u6709 4.7 GB"),M();try{await t.invoke("downloadModels"),g(c,"\u6743\u91CD\u9F50\u4E86")}catch(o){t.signal.aborted||g(c,A(o),!0)}d()}async function M(){await d(),L&&!t.signal.aborted&&t.timeout(()=>{M()},1e3)}d()}var ge='\u9009\u9EA6\u514B\u98CE\u3001\u770B\u7535\u5E73\u3001\u770B\u8BC6\u522B\u7ED3\u679C\u3002\u7535\u5E73\u6761\u4E0A\u90A3\u9053\u7AD6\u7EBF\u662F"\u8BF4\u8BDD\u95E8\u69DB":\u8BF4\u8BDD\u65F6\u8D8A\u8FC7\u5B83\u3001\u5B89\u9759\u65F6\u4E0D\u8D8A\u8FC7,\u95E8\u69DB\u5C31\u8C03\u5BF9\u4E86\u3002\u8BC6\u522B\u540E\u7AEF\u5728\u8FD9\u513F\u542F\u505C:FireRed \u663E\u5361\u6863\u9002\u5408\u5B9E\u65F6\u8BC6\u522B\u3001CPU \u6863\u4E0D\u5360\u663E\u5B58,\u6863\u4F4D\u5728\u672C World \u914D\u7F6E\u91CC\u5207\u3002',ae=-60;function se(t){return Math.max(0,Math.min(100,(t-ae)/-ae*100))}var le={mount(t){let{ui:e,root:y}=t,s=e.sheet({title:"\u6536\u542C",en:"listen",desc:ge}),v=s.body;y.appendChild(s.el);let a=U(t,"grow"),h=e.button("\u5F00\u59CB\u6536\u542C",{size:"sm",variant:"primary"}),S=e.button("\u505C\u6B62",{size:"sm",variant:"danger"}),C=e.rowbar();C.append(h,S,a.el),v.append(C);let c=e.select(),P=e.h("div","asr-form");P.append(e.field("\u9EA6\u514B\u98CE",c));let D=e.h("div","asr-meter"),$=e.h("div","asr-meterfill"),R=e.h("div","asr-metermark"),x=e.h("span","asr-readout","\u2014");D.append($,R);let g=e.h("div","asr-meterrow");g.append(D,x);let L=e.h("div","pagedesc");v.append(e.section("\u9EA6\u514B\u98CE","\u9009\u8BBE\u5907\u4E0E\u770B\u7535\u5E73;\u6362\u8BBE\u5907\u4F1A\u91CD\u5F00\u4E00\u6B21\u8F93\u5165\u6D41"),P,g,L);let d=e.select(),b=e.button("",{size:"sm"});b.className+=" pathpick",b.title="\u9009\u62E9\u6743\u91CD\u6587\u4EF6",b.setAttribute("aria-label",b.title),b.appendChild(K(b.ownerDocument,"folder-open"));let w=e.h("a","pathdownload");w.href="https://huggingface.co/FireRedTeam/FireRedASR2-AED/tree/2304afed56eacfee6256dee5937ed22ffa0b64ec",w.target="_blank",w.rel="noopener noreferrer",w.title="FireRedASR2-AED \u6A21\u578B\u6587\u4EF6",w.append(K(w.ownerDocument,"download"),e.h("span",null,"\u4E0B\u8F7D\u6743\u91CD"));let M=e.h("div","asr-form"),o=e.rowbar();o.append(d,b);let u=e.h("div","pathfield-meta",""),k=e.h("span",null,"");u.append(k,w);let p=e.h("div","pathfield");p.append(o,u),M.append(e.field("\u6743\u91CD",p));let O=e.h("div","mountrow"),l=e.h("span","navdot"),f=e.h("span","mstate","\u2014"),B=e.h("span","mdetail"),Q=e.h("span","macts"),I=e.button("\u505C\u6B62",{size:"sm",variant:"danger"}),V=e.button("\u542F\u52A8",{size:"sm",variant:"primary"});Q.append(I,V),O.append(l,e.h("span","mname","\u8BC6\u522B\u540E\u7AEF"),f,B,Q),v.append(e.section("\u8BC6\u522B\u540E\u7AEF","\u6743\u91CD\u6362\u5B8C\u4F1A\u628A\u540E\u7AEF\u91CD\u8D77\u4E00\u6B21;\u6863\u4F4D(\u663E\u5361/CPU)\u5728\u672C World \u914D\u7F6E\u91CC\u5207"),M,O);let J=e.statgrid(),z=e.log({max:200}),q=e.button("\u6E05\u7A7A",{size:"sm"}),X=e.actions();X.append(e.h("span","grow"),q),v.append(e.section("\u8BC6\u522B\u7ED3\u679C","\u5B9E\u65F6\u63A8\u9001;\u4E22\u6389\u7684\u90A3\u4E9B(\u592A\u77ED\u3001\u7591\u4F3C\u5E7B\u89C9)\u4E5F\u6807\u51FA\u6765"),J,z.el,X);let E=null,G=0,Y="",Z="";function H(n){let r=E?.modelFile??"",i=`${n.models.join("\0")}\0${r}`;if(i!==Z){Z=i,d.replaceChildren();let ne=e.h("option",null,"\u81EA\u52A8(\u6309\u6863\u4F4D\u6311)");ne.value="",d.appendChild(ne);for(let T of n.models){let re=e.h("option",null,T.replace(/[/\\]model\.pth\.tar$/,""));re.value=T,d.appendChild(re)}if(r&&!n.models.includes(r)){let T=e.h("option",null,r);T.value=r,d.appendChild(T)}}d.value=r,d.disabled=n.models.length===0&&!r,k.textContent=`\u6743\u91CD\u76EE\u5F55\uFF1A${n.modelsDir}`;let m=n.reachable,F=!n.installed||n.model===null,ce=m?"on":n.phase==="starting"?"warn":"off";l.className=`navdot${m?" on":n.phase==="starting"?" warn":" bad"}`,f.className=`mstate asr-${ce}`,f.textContent=m?n.pid?"\u8FD0\u884C\u4E2D":"\u8FD0\u884C\u4E2D(\u5916\u90E8)":F?"\u672A\u5B89\u88C5":{starting:"\u542F\u52A8\u4E2D",error:"\u5F02\u5E38",stopped:"\u672A\u542F\u52A8"}[n.phase]??n.phase;let te=F&&!m?n.detail??`${n.profile.toUpperCase()} \u6863\u7F3A${n.installed?"\u6743\u91CD":"\u8FD0\u884C\u73AF\u5883"}:\u5728\u4E0B\u9762\u300C\u8FD0\u884C\u65F6\u4E0E\u6743\u91CD\u300D\u5B89\u88C5`:[n.pid?`pid ${n.pid}`:null,n.url,n.model,`${n.profile.toUpperCase()} \u6863`,n.detail].filter(Boolean).join(" \xB7 ");B.textContent=te,B.title=te,V.disabled=n.phase==="starting"||m||F,I.disabled=n.pid===null&&n.phase!=="starting"}function W(n){if(t.signal.aborted)return;if(E=n,!n){a.say("\u6536\u542C\u9762\u677F\u4E0D\u53EF\u7528",!0);return}let r=n.devices.map(i=>i.name).join("\0");if(r!==Y){Y=r,c.replaceChildren();let i=e.h("option",null,"\u7CFB\u7EDF\u9ED8\u8BA4");i.value="",c.appendChild(i);for(let m of n.devices){let F=e.h("option",null,`${m.name}${m.isDefault?"(\u9ED8\u8BA4)":""}`);F.value=m.name,c.appendChild(F)}}c.value=n.devices.some(i=>i.name===n.deviceSetting)?n.deviceSetting:"",L.textContent=n.device?`\u5B9E\u9645:${n.device}`:"",n.audioAvailable||a.say("\u672C\u673A\u6CA1\u6709\u53EF\u7528\u7684\u5F55\u97F3\u8BBE\u5907(\u6216\u539F\u751F\u58F0\u5361\u6A21\u5757\u6CA1\u88C5\u4E0A)",!0),R.style.left=`${se(n.thresholdDb)}%`,ee(n.level,n.speaking,n.listening),h.disabled=n.listening||!n.audioAvailable,S.disabled=!n.listening,c.disabled=!n.audioAvailable,H(n.server),J.replaceChildren(e.stat({k:"\u8BC6\u522B\u53E5\u6570",v:n.counts.utterances}),e.stat({k:"\u5DF2\u6295\u9012",v:n.counts.delivered,accent:!0}),e.stat({k:"\u4E22\u5F03",v:n.counts.dropped}),e.stat({k:"\u7AEF\u70B9",v:n.endpoint.replace(/^https?:\/\//,"")}));for(let i of[...n.recent].reverse())i.at<=G||(G=i.at,z.append(`${new Date(i.at).toTimeString().slice(0,8)} ${i.text} (${i.ms}ms)`));n.detail&&a.say(n.detail,n.detail.includes("\u5931\u8D25"))}function ee(n,r,i){$.style.width=`${se(n)}%`,$.classList.toggle("on",r),x.textContent=i?`${Math.round(n)} dB${r?" \xB7 \u6B63\u5728\u8BF4":""}`:"\u672A\u6536\u542C"}let _=()=>t.invoke("state").then(W,()=>W(null));async function N(n,r){a.say(r);let i=e.disable(h,S),m=null;try{m=await n()}catch(F){t.signal.aborted||a.say(`\u64CD\u4F5C\u5931\u8D25: ${A(F)}`,!0)}finally{i.dispose()}m&&W(m)}h.addEventListener("click",()=>{N(()=>t.invoke("start"),"\u6253\u5F00\u9EA6\u514B\u98CE\u2026")},{signal:t.signal}),S.addEventListener("click",()=>{N(()=>t.invoke("stop"),"\u505C\u6B62\u4E2D\u2026")},{signal:t.signal}),q.addEventListener("click",()=>{z.clear(),N(()=>t.invoke("clear"),"")},{signal:t.signal}),c.addEventListener("change",()=>{N(()=>t.invoke("setDevice",[c.value]),"\u6362\u8BBE\u5907\u2026")},{signal:t.signal}),d.addEventListener("change",()=>{N(()=>t.invoke("setModel",[d.value]),"\u6362\u6743\u91CD(\u8981\u91CD\u8D77\u540E\u7AEF)\u2026")},{signal:t.signal}),b.addEventListener("click",()=>{(async()=>{let n=e.disable(b,d);try{let r=await t.pickPath({kind:"file",title:"\u9009\u62E9 FireRedASR2-AED \u7684 model.pth.tar",extensions:[".tar"],...E?.server.modelsDir?{recommendedDir:E.server.modelsDir}:{},...E?.modelFile?{currentPath:E.modelFile}:{}});if(!r||t.signal.aborted)return;let i=await t.invoke("setModel",[r]);W(i),a.say(`\u6743\u91CD\u8DEF\u5F84\u5DF2\u4FDD\u5B58: ${r}`)}catch(r){t.signal.aborted||a.say(`\u9009\u62E9\u6743\u91CD\u5931\u8D25: ${A(r)}`,!0)}finally{n.dispose()}})()},{signal:t.signal}),V.addEventListener("click",()=>{a.say("\u8BC6\u522B\u540E\u7AEF\u542F\u52A8\u4E2D\u2026(\u8981\u52A0\u8F7D\u6743\u91CD)"),t.invoke("server.start").then(n=>{H(n),pe()},n=>{t.signal.aborted||a.say(`\u542F\u52A8\u5931\u8D25: ${A(n)}`,!0)})},{signal:t.signal}),I.addEventListener("click",()=>{t.invoke("server.stop").then(n=>{H(n),a.say("\u8BC6\u522B\u540E\u7AEF\u5DF2\u505C\u6B62")},n=>{t.signal.aborted||a.say(`\u505C\u6B62\u5931\u8D25: ${A(n)}`,!0)})},{signal:t.signal});function pe(){let n=0,r=t.interval(()=>{if(++n>60){r.dispose(),a.say("\u8BC6\u522B\u540E\u7AEF\u542F\u52A8\u8D85\u65F6",!0);return}t.invoke("server.state").then(i=>{H(i),i.phase!=="starting"&&(r.dispose(),a.say(i.reachable?"\u8BC6\u522B\u540E\u7AEF\u5C31\u7EEA":`\u8BC6\u522B\u540E\u7AEF ${i.detail||i.phase}`,!i.reachable))})},2e3)}t.stream({message:n=>{let r;try{r=JSON.parse(n)}catch{return}if(r.type==="level"){ee(r.level,r.speaking,r.listening),E&&E.listening!==r.listening&&_();return}if(r.type!=="text"&&r.type!=="dropped")return;let i=r.type==="text"?r.at:Date.now();G=Math.max(G,i);let m=new Date(i).toTimeString().slice(0,8);z.append(r.type==="dropped"?`${m} [\u4E22\u5F03] ${r.text}`:`${m} ${r.text} (${r.ms}ms)`)}}),_(),t.interval(()=>{_()},5e3),ie(t)}};var he="OBS \u91CC\u52A0\u4E00\u4E2A\u6D4F\u89C8\u5668\u6E90,\u5730\u5740\u586B\u4E0B\u9762\u8FD9\u6761,\u5C3A\u5BF8\u6309\u753B\u5E03\u6765\u3002\u5B57\u5E55\u6837\u5F0F\u6539\u5B8C\u5373\u65F6\u751F\u6548,OBS \u90A3\u4EFD\u4E0D\u7528\u91CD\u5F00\u3002\u5B57\u4F53\u586B OBS \u90A3\u53F0\u673A\u5668\u4E0A\u88C5\u4E86\u7684\u5B57\u4F53\u540D\u3002",de={mount(t){let{ui:e,root:y}=t,s=e.sheet({title:"Overlay \u5B57\u5E55",en:"subtitle overlay",desc:he}),v=s.body;y.appendChild(s.el);let a=U(t,"grow"),h=e.h("div");v.append(e.section("\u63A8\u6D41\u94FE\u63A5"),h);let S=e.input({type:"number",value:"1"}),C=e.select({options:["300","400","500","600","700","800"]}),c=e.input({type:"number",value:"2"}),P=e.input({type:"number",value:"6"}),D=e.input({value:"#fffef8",cls:"mono"}),$=e.input({value:"#000000",cls:"mono"}),R=e.input({type:"number",value:"1.5"}),x=e.input({type:"number",value:"0.35"}),g=e.input({placeholder:"\u7559\u7A7A = \u9875\u9762\u9ED8\u8BA4"}),L=e.checkbox('\u663E\u793A"\u6B63\u5728\u8BF4\u2026"',{checked:!0}),d=e.h("div","asr-form");d.append(e.field("\u5B57\u53F7\u500D\u7387",S),e.field("\u5B57\u91CD",C),e.field("\u4FDD\u7559\u884C\u6570",c),e.field("\u505C\u7559(\u79D2)",P),e.field("\u5B57\u8272",D),e.field("\u63CF\u8FB9\u8272",$),e.field("\u63CF\u8FB9\u5BBD(px)",R),e.field("\u5E95\u677F\u4E0D\u900F\u660E\u5EA6",x),e.field("\u5B57\u4F53",g));let b=e.button("\u5E94\u7528",{size:"sm",variant:"primary"}),w=e.input({placeholder:"\u8BD5\u663E\u4E00\u6761\u5B57\u5E55"}),M=e.button("\u8BD5\u663E",{size:"sm"}),o=e.actions();o.append(L.el,e.h("span","grow"),w,M,b),v.append(e.section("\u5B57\u5E55\u6837\u5F0F"),d,o,a.el);let u=e.h("iframe","asr-preview");u.setAttribute("title","\u5B57\u5E55\u9884\u89C8"),v.append(e.section("\u9884\u89C8","\u4E0E OBS \u91CC\u90A3\u4EFD\u662F\u540C\u4E00\u4E2A\u9875\u9762,\u53EA\u662F\u52A0\u4E86\u6697\u5E95"),u);function k(l){if(t.signal.aborted)return;if(!l){a.say("\u5B57\u5E55\u9762\u677F\u4E0D\u53EF\u7528",!0),h.replaceChildren(e.placeholder("\u8BC6\u522B\u6D41\u6CA1\u8D77\u6765"));return}h.replaceChildren(e.kv([{k:"\u5B57\u5E55\u9875",v:l.url??"(\u672A\u542F\u52A8)"},{k:"\u8BA2\u9605\u8005",v:`${l.subscribers} \u4E2A(OBS \u6D4F\u89C8\u5668\u6E90 / \u9884\u89C8\u5404\u7B97\u4E00\u4E2A)`},{k:"\u590D\u5236",v:l.url?e.copyButton(l.url,{label:"\u590D\u5236\u94FE\u63A5"}):"\u2014"}]));let f=l.config;S.value=String(f.subtitle.scale),C.value=String(f.subtitle.weight),c.value=String(f.maxLines),P.value=String(Math.round(f.holdMs/1e3)),D.value=f.subtitle.color,$.value=f.subtitle.strokeColor,R.value=String(f.subtitle.strokeW),x.value=String(f.subtitle.plate),g.value=f.subtitle.fontFamily,L.setChecked(f.showPartial);let B=l.url?`${l.url}?bg=dim`:"";B&&u.getAttribute("src")!==B&&u.setAttribute("src",B)}let p=()=>({maxLines:Number(c.value),holdMs:Math.round(Number(P.value)*1e3),showPartial:L.checked,subtitle:{scale:Number(S.value),weight:Number(C.value),color:D.value.trim(),strokeColor:$.value.trim(),strokeW:Number(R.value),plate:Number(x.value),fontFamily:g.value.trim()}}),O=()=>t.invoke("state").then(k,()=>k(null));b.addEventListener("click",()=>{a.say("\u5E94\u7528\u4E2D\u2026"),t.invoke("setConfig",[p()]).then(l=>{k(l),a.say("\u5DF2\u751F\u6548\u5E76\u843D\u76D8;\u8BA2\u9605\u4E2D\u7684\u9875\u9762\u5DF2\u70ED\u66F4\u65B0")},l=>{t.signal.aborted||a.say(`\u5E94\u7528\u5931\u8D25: ${A(l)}`,!0)})},{signal:t.signal}),M.addEventListener("click",()=>{t.invoke("test",[w.value]).then(l=>{k(l),a.say("\u5DF2\u6295\u4E00\u6761\u5230\u5B57\u5E55\u6D41")},l=>{t.signal.aborted||a.say(`\u8BD5\u663E\u5931\u8D25: ${A(l)}`,!0)})},{signal:t.signal}),O()}};function A(t){return t instanceof Error?t.message:String(t)}function U(t,e){let y=t.ui.msgline("");return e&&y.classList.add(e),{el:y,say(s,v){y.textContent=s??"",y.classList.toggle("bad",v===!0)}}}var be={panels:{listen:le,overlay:de}},Ee=be;export{Ee as default,A as errText,U as msgLine};
|
|
2
|
+
//# sourceMappingURL=console.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/console/icons.ts", "../src/console/runtime.ts", "../src/console/listen.ts", "../src/console/overlay.ts", "../src/console/client.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * \u9762\u677F\u7528\u5230\u7684\u4E24\u679A\u56FE\u6807\u3002\u5305\u5185\u81EA\u5E26\u800C\u4E0D\u4ECE `cortico/web/client/ui/icons.ts` \u53D6:\u6D4F\u89C8\u5668\u4FA7\u5BF9\n * \u6846\u67B6\u53EA\u5141\u8BB8 `import type`,\u9762\u677F bundle \u4E0D\u6253\u8FDB\u6846\u67B6\u7684\u524D\u7AEF\u4EE3\u7801\u3002\u5F62\u72B6\u4E0E\u6846\u67B6\u90A3\u4EFD\u76F8\u540C\u3002\n */\n\nconst SVG_NS = 'http://www.w3.org/2000/svg';\n\ntype Shape = readonly [tag: 'path' | 'circle' | 'rect' | 'line', attrs: Readonly<Record<string, string>>];\n\nconst SHAPES = {\n 'folder-open': [\n ['path', { d: 'M3 6h6l2 2h10' }],\n ['path', { d: 'M3 6v13h15l3-8H6l-3 8' }],\n ],\n download: [\n ['path', { d: 'M12 3v12' }],\n ['path', { d: 'm7 10 5 5 5-5' }],\n ['path', { d: 'M5 21h14' }],\n ],\n} as const satisfies Readonly<Record<string, readonly Shape[]>>;\n\nexport type AsrIconName = keyof typeof SHAPES;\n\nexport function icon(doc: Document, name: AsrIconName, cls = 'icon'): SVGSVGElement {\n const svg = doc.createElementNS(SVG_NS, 'svg');\n svg.setAttribute('viewBox', '0 0 24 24');\n svg.setAttribute('fill', 'none');\n svg.setAttribute('stroke', 'currentColor');\n svg.setAttribute('stroke-width', '1.8');\n svg.setAttribute('stroke-linecap', 'round');\n svg.setAttribute('stroke-linejoin', 'round');\n svg.setAttribute('aria-hidden', 'true');\n svg.classList.add(cls);\n for (const [tag, attrs] of SHAPES[name]) {\n const child = doc.createElementNS(SVG_NS, tag);\n for (const [key, value] of Object.entries(attrs)) child.setAttribute(key, value);\n svg.appendChild(child);\n }\n return svg;\n}\n", "/**\n * \u300C\u8FD0\u884C\u65F6\u4E0E\u6743\u91CD\u300D:\u63A5\u5728\u300C\u6536\u542C\u300D\u9762\u677F\u4E0B\u9762\u7684\u7B2C\u4E8C\u5F20\u5361\u3002\u8FD0\u884C\u65F6\u662F FireRedASR2S \u7684 Python \u73AF\u5883,\n * \u6743\u91CD\u662F FireRedASR2-AED \u7684\u56DB\u4E2A\u6587\u4EF6;\u4E24\u6837\u90FD\u88C5\u5230\u90E8\u7F72\u6839\u4E0B,\u5B89\u88C5\u4E0E\u4E0B\u8F7D\u5404\u4E00\u9897\u952E\u3002\n */\nimport type { ConsolePanelContext } from 'cortico/web/shared/client-panel.ts';\nimport { errText, type AsrRuntimeState } from './client.ts';\n\nconst DESC =\n '\u8FD0\u884C\u65F6\u662F FireRedASR2S \u7684 Python \u73AF\u5883(git \u53D6\u4EE3\u7801\u3001uv \u5EFA\u73AF\u5883\u5E76\u88C5 PyTorch),\u88C5\u5230\u90E8\u7F72\u6839\u7684 runtimes/firered-asr/ \u4E0B;'\n + '\u6743\u91CD\u662F FireRedASR2-AED \u7684\u56DB\u4E2A\u6587\u4EF6,\u4E0B\u5230 models/asr/FireRedASR2-AED/\u3002'\n + '\u914D\u7F6E\u91CC\u586B\u4E86\u300C\u8FD0\u884C\u65F6\u76EE\u5F55(\u81EA\u5907)\u300D\u6216\u300C\u6743\u91CD\u6587\u4EF6(\u81EA\u5907)\u300D\u7684\u90A3\u4E00\u6837\u5C31\u4E0D\u518D\u88C5\u3002\u5B89\u88C5\u8981\u672C\u673A\u6709 git \u4E0E uv\u3002';\n\nfunction gb(bytes: number): string {\n return bytes >= 1e9 ? `${(bytes / 1e9).toFixed(2)} GB` : bytes >= 1e6 ? `${Math.round(bytes / 1e6)} MB` : `${bytes} B`;\n}\n\nfunction progressText(done: number, total: number | null): string {\n return total ? `${gb(done)} / ${gb(total)}(${Math.round((done / total) * 100)}%)` : gb(done);\n}\n\nexport function mountRuntimeSection(ctx: ConsolePanelContext): void {\n const { ui } = ctx;\n const card = ui.sheet({ title: '\u8FD0\u884C\u65F6\u4E0E\u6743\u91CD', en: 'runtime', desc: DESC });\n\n const msg = ui.msgline('');\n const chip = ui.chip('\u2014');\n const btnInstall = ui.button('\u5B89\u88C5\u8FD0\u884C\u65F6', { size: 'sm', onClick: () => void install() });\n const head = ui.rowbar();\n head.append(ui.pill('\u8FD0\u884C\u65F6', 'plain'), chip, msg, ui.h('span', 'grow'), btnInstall);\n const dirLine = ui.h('div', 'pagedesc');\n const stepLine = ui.h('div', 'pagedesc');\n\n const modelsMsg = ui.msgline('');\n const modelsChip = ui.chip('\u2014');\n const btnDownload = ui.button('\u4E0B\u8F7D\u6743\u91CD', { size: 'sm', onClick: () => void download() });\n const modelsHead = ui.rowbar();\n modelsHead.append(ui.pill('\u6743\u91CD', 'plain'), modelsChip, modelsMsg, ui.h('span', 'grow'), btnDownload);\n const modelsDirLine = ui.h('div', 'pagedesc');\n const rows = ui.h('div');\n\n card.body.append(head, dirLine, stepLine, ui.section('\u6743\u91CD', 'FireRedASR2-AED,\u56DB\u4E2A\u6587\u4EF6\u7F3A\u4E00\u4E0D\u53EF'), modelsHead, modelsDirLine, rows);\n ctx.root.appendChild(card.el);\n\n const say = (el: HTMLElement, text: string, bad = false): void => {\n el.textContent = text;\n el.classList.toggle('bad', bad);\n };\n\n /** \u6709\u6D3B\u5728\u8DD1\u5C31\u63D0\u9AD8\u8F6E\u8BE2\u9891\u7387,\u9759\u6B62\u65F6\u4E00\u6B21\u5C31\u591F */\n let busy = false;\n\n async function refresh(): Promise<void> {\n let st: AsrRuntimeState;\n try {\n st = await ctx.invoke<AsrRuntimeState>('runtime');\n } catch (error) {\n if (!ctx.signal.aborted) say(msg, errText(error), true);\n return;\n }\n if (ctx.signal.aborted) return;\n const phase = st.install.phase;\n const downloading = st.models.some((m) => m.phase === 'downloading');\n busy = phase === 'installing' || downloading;\n\n chip.textContent = st.own\n ? '\u81EA\u5907\u76EE\u5F55'\n : !st.supported\n ? '\u672C\u5E73\u53F0\u65E0\u6258\u7BA1\u65B9\u6848'\n : phase === 'installed'\n ? st.revision.slice(0, 7)\n : phase === 'installing'\n ? `\u5B89\u88C5\u4E2D \u00B7 ${st.install.step ?? ''}`\n : phase === 'error'\n ? '\u88C5\u5931\u8D25'\n : '\u672A\u5B89\u88C5';\n dirLine.textContent = st.dir || '(\u8FD8\u6CA1\u6709\u76EE\u5F55)';\n stepLine.textContent = phase === 'installing' ? (st.install.line ?? '') : '';\n btnInstall.disabled = busy || st.own || !st.supported;\n btnInstall.textContent = phase === 'installed' ? '\u91CD\u88C5\u8FD0\u884C\u65F6' : '\u5B89\u88C5\u8FD0\u884C\u65F6';\n if (st.install.detail) say(msg, st.install.detail, true);\n\n modelsChip.textContent = st.modelsComplete ? '\u9F50\u4E86' : downloading ? '\u4E0B\u8F7D\u4E2D' : '\u7F3A\u6587\u4EF6';\n modelsDirLine.textContent = `${st.modelsDir} \u00B7 \u6765\u6E90 ${st.modelsSource}`;\n btnDownload.disabled = busy || st.modelsComplete;\n rows.replaceChildren();\n for (const m of st.models) {\n const row = ui.rowbar();\n const state = m.phase === 'present'\n ? gb(m.bytes)\n : m.phase === 'downloading'\n ? progressText(m.done, m.total)\n : m.phase === 'error'\n ? (m.detail ?? '\u4E0B\u8F7D\u5931\u8D25')\n : '\u672A\u4E0B\u8F7D';\n row.append(ui.chip(m.file), ui.h('span', m.phase === 'error' ? 'bad' : '', state));\n rows.appendChild(row);\n }\n }\n\n async function install(): Promise<void> {\n say(msg, '\u5F00\u59CB\u5B89\u88C5:\u53D6\u4EE3\u7801\u3001\u5EFA\u73AF\u5883\u3001\u88C5 PyTorch(\u51E0 GB),\u522B\u5173\u9875\u9762');\n void poll();\n try {\n await ctx.invoke('installRuntime');\n say(msg, '\u8FD0\u884C\u65F6\u88C5\u597D\u4E86');\n } catch (error) {\n if (!ctx.signal.aborted) say(msg, errText(error), true);\n }\n void refresh();\n }\n\n async function download(): Promise<void> {\n say(modelsMsg, '\u5F00\u59CB\u4E0B\u8F7D,model.pth.tar \u6709 4.7 GB');\n void poll();\n try {\n await ctx.invoke('downloadModels');\n say(modelsMsg, '\u6743\u91CD\u9F50\u4E86');\n } catch (error) {\n if (!ctx.signal.aborted) say(modelsMsg, errText(error), true);\n }\n void refresh();\n }\n\n /** \u5B89\u88C5\u4E0E\u4E0B\u8F7D\u7684 invoke \u8981\u7B49\u5230\u7ED3\u675F\u624D\u8FD4\u56DE,\u8FDB\u5EA6\u9760\u8FD9\u91CC\u6309\u79D2\u5237 */\n async function poll(): Promise<void> {\n await refresh();\n if (busy && !ctx.signal.aborted) ctx.timeout(() => void poll(), 1000);\n }\n\n void refresh();\n}\n", "/**\n * \u9762\u677F `listen` \u2014\u2014 \u6536\u542C:\u9EA6\u514B\u98CE\u3001\u8BC6\u522B\u540E\u7AEF\u3001\u7535\u5E73\u4E0E\u5B9E\u65F6\u8BC6\u522B\u6587\u672C\u3002\n *\n * \u8FD9\u4E00\u5C4F\u662F**\u8C03\u95E8\u69DB**\u7684\u5730\u65B9\u3002\u7535\u5E73\u6761\u4E0A\u90A3\u9053\u7AD6\u7EBF\u5C31\u662F `worlds.asr.segment.thresholdDb`:\n * \u8BF4\u8BDD\u65F6\u6761\u8981\u8D8A\u8FC7\u5B83\u3001\u5B89\u9759\u65F6\u4E0D\u8D8A\u8FC7\u2014\u2014\u8FD9\u6761\u5224\u636E\u4E00\u53E5\u8BDD\u8BF4\u5F97\u6E05,\u800C\u5149\u770B\u6570\u5B57\u8BF4\u4E0D\u6E05\u3002\n * \u6240\u4EE5\u7535\u5E73\u8D70 `ctx.stream` \u5B9E\u65F6\u63A8(10Hz),\u4E0D\u662F\u8F6E\u8BE2\u56DE\u6765\u7684\u4E00\u4E2A\u9759\u6B62\u8BFB\u6570\u3002\n *\n * \u8BC6\u522B\u6587\u672C\u7528 `ui.log`(\u53EA\u5F80\u672B\u5C3E\u957F\u7684\u4E8B\u4EF6\u6D41\u3001\u5C3E\u90E8\u7C98\u6EDE),\u4E0D\u662F `ui.table`:\n * \u4E00\u6761\u6761\u6765\u7684\u4E1C\u897F\u6574\u8868\u91CD\u753B\u4F1A\u5237\u6389\u6EDA\u52A8\u4F4D\u7F6E\u3002\n */\n\nimport type {\n ConsolePanelContext,\n ConsolePanel,\n} from 'cortico/web/shared/client-panel.ts';\nimport {\n errText,\n msgLine,\n type AsrListenState,\n type AsrPanelFrame,\n type AsrServerState,\n} from './client.ts';\nimport { icon } from './icons.ts';\nimport { mountRuntimeSection } from './runtime.ts';\n\nconst DESC =\n '\u9009\u9EA6\u514B\u98CE\u3001\u770B\u7535\u5E73\u3001\u770B\u8BC6\u522B\u7ED3\u679C\u3002\u7535\u5E73\u6761\u4E0A\u90A3\u9053\u7AD6\u7EBF\u662F\"\u8BF4\u8BDD\u95E8\u69DB\":\u8BF4\u8BDD\u65F6\u8D8A\u8FC7\u5B83\u3001'\n + '\u5B89\u9759\u65F6\u4E0D\u8D8A\u8FC7,\u95E8\u69DB\u5C31\u8C03\u5BF9\u4E86\u3002\u8BC6\u522B\u540E\u7AEF\u5728\u8FD9\u513F\u542F\u505C:FireRed \u663E\u5361\u6863\u9002\u5408\u5B9E\u65F6\u8BC6\u522B\u3001'\n + 'CPU \u6863\u4E0D\u5360\u663E\u5B58,\u6863\u4F4D\u5728\u672C World \u914D\u7F6E\u91CC\u5207\u3002';\n\n/** \u7535\u5E73\u6761\u7684\u663E\u793A\u8303\u56F4:-60 dBFS \u4EE5\u4E0B\u90FD\u5F53\u9759\u97F3 */\nconst FLOOR_DB = -60;\n\nfunction pct(db: number): number {\n return Math.max(0, Math.min(100, ((db - FLOOR_DB) / -FLOOR_DB) * 100));\n}\n\nexport const listenPanel: ConsolePanel = {\n mount(ctx: ConsolePanelContext) {\n const { ui, root } = ctx;\n const card = ui.sheet({ title: '\u6536\u542C', en: 'listen', desc: DESC });\n const s = card.body;\n root.appendChild(card.el);\n\n const msg = msgLine(ctx, 'grow');\n const btnListen = ui.button('\u5F00\u59CB\u6536\u542C', { size: 'sm', variant: 'primary' });\n const btnStop = ui.button('\u505C\u6B62', { size: 'sm', variant: 'danger' });\n const bar = ui.rowbar();\n bar.append(btnListen, btnStop, msg.el);\n s.append(bar);\n\n // ---- \u9EA6\u514B\u98CE ----\n const devSel = ui.select();\n const devForm = ui.h('div', 'asr-form');\n devForm.append(ui.field('\u9EA6\u514B\u98CE', devSel));\n const meter = ui.h('div', 'asr-meter');\n const fill = ui.h('div', 'asr-meterfill');\n const mark = ui.h('div', 'asr-metermark');\n const readout = ui.h('span', 'asr-readout', '\u2014');\n meter.append(fill, mark);\n const meterRow = ui.h('div', 'asr-meterrow');\n meterRow.append(meter, readout);\n const actual = ui.h('div', 'pagedesc');\n s.append(ui.section('\u9EA6\u514B\u98CE', '\u9009\u8BBE\u5907\u4E0E\u770B\u7535\u5E73;\u6362\u8BBE\u5907\u4F1A\u91CD\u5F00\u4E00\u6B21\u8F93\u5165\u6D41'), devForm, meterRow, actual);\n\n // ---- \u8BC6\u522B\u540E\u7AEF ----\n const modelSel = ui.select();\n const btnModelFile = ui.button('', { size: 'sm' });\n btnModelFile.className += ' pathpick';\n btnModelFile.title = '\u9009\u62E9\u6743\u91CD\u6587\u4EF6';\n btnModelFile.setAttribute('aria-label', btnModelFile.title);\n btnModelFile.appendChild(icon(btnModelFile.ownerDocument, 'folder-open'));\n const modelDownload = ui.h('a', 'pathdownload');\n modelDownload.href = 'https://huggingface.co/FireRedTeam/FireRedASR2-AED/tree/2304afed56eacfee6256dee5937ed22ffa0b64ec';\n modelDownload.target = '_blank';\n modelDownload.rel = 'noopener noreferrer';\n modelDownload.title = 'FireRedASR2-AED \u6A21\u578B\u6587\u4EF6';\n modelDownload.append(icon(modelDownload.ownerDocument, 'download'), ui.h('span', null, '\u4E0B\u8F7D\u6743\u91CD'));\n const modelForm = ui.h('div', 'asr-form');\n const modelRow = ui.rowbar();\n modelRow.append(modelSel, btnModelFile);\n const modelMeta = ui.h('div', 'pathfield-meta', '');\n const modelDirNote = ui.h('span', null, '');\n modelMeta.append(modelDirNote, modelDownload);\n const modelField = ui.h('div', 'pathfield');\n modelField.append(modelRow, modelMeta);\n modelForm.append(ui.field('\u6743\u91CD', modelField));\n const srvLine = ui.h('div', 'mountrow');\n const srvDot = ui.h('span', 'navdot');\n const srvState = ui.h('span', 'mstate', '\u2014');\n const srvDetail = ui.h('span', 'mdetail');\n const srvActs = ui.h('span', 'macts');\n const btnSrvStop = ui.button('\u505C\u6B62', { size: 'sm', variant: 'danger' });\n const btnSrvStart = ui.button('\u542F\u52A8', { size: 'sm', variant: 'primary' });\n srvActs.append(btnSrvStop, btnSrvStart);\n srvLine.append(srvDot, ui.h('span', 'mname', '\u8BC6\u522B\u540E\u7AEF'), srvState, srvDetail, srvActs);\n s.append(\n ui.section('\u8BC6\u522B\u540E\u7AEF', '\u6743\u91CD\u6362\u5B8C\u4F1A\u628A\u540E\u7AEF\u91CD\u8D77\u4E00\u6B21;\u6863\u4F4D(\u663E\u5361/CPU)\u5728\u672C World \u914D\u7F6E\u91CC\u5207'),\n modelForm,\n srvLine,\n );\n\n // ---- \u8BC6\u522B\u6587\u672C ----\n const stats = ui.statgrid();\n const logView = ui.log({ max: 200 });\n const btnClear = ui.button('\u6E05\u7A7A', { size: 'sm' });\n const logBar = ui.actions();\n logBar.append(ui.h('span', 'grow'), btnClear);\n s.append(ui.section('\u8BC6\u522B\u7ED3\u679C', '\u5B9E\u65F6\u63A8\u9001;\u4E22\u6389\u7684\u90A3\u4E9B(\u592A\u77ED\u3001\u7591\u4F3C\u5E7B\u89C9)\u4E5F\u6807\u51FA\u6765'), stats, logView.el, logBar);\n\n // -----------------------------------------------------------------------\n\n let cur: AsrListenState | null = null;\n let seenAt = 0;\n /** \u4E0A\u4E00\u6B21\u94FA\u9009\u9879\u65F6\u7684\u8BBE\u5907\u540D\u5355;\u53D8\u4E86\u624D\u91CD\u94FA,\u514D\u5F97\u5237\u6389\u7528\u6237\u6B63\u5728\u9009\u7684\u90A3\u4E00\u4E0B */\n let deviceKey = '';\n /** \u540C\u4E0A,\u6743\u91CD\u90A3\u4E00\u4EFD */\n let modelKey = '';\n\n function renderServer(st: AsrServerState): void {\n const configured = cur?.modelFile ?? '';\n const key = `${st.models.join('\\0')}\\0${configured}`;\n if (key !== modelKey) {\n modelKey = key;\n modelSel.replaceChildren();\n const auto = ui.h('option', null, '\u81EA\u52A8(\u6309\u6863\u4F4D\u6311)');\n auto.value = '';\n modelSel.appendChild(auto);\n for (const m of st.models) {\n const op = ui.h('option', null, m.replace(/[/\\\\]model\\.pth\\.tar$/, ''));\n op.value = m;\n modelSel.appendChild(op);\n }\n if (configured && !st.models.includes(configured)) {\n const external = ui.h('option', null, configured);\n external.value = configured;\n modelSel.appendChild(external);\n }\n }\n // \u9009\u4E2D\u7684\u662F\u914D\u7F6E\u91CC\u9489\u7684\u90A3\u4EFD;\u771F\u6B63\u52A0\u8F7D\u7684\u90A3\u4EFD\u5199\u5728\u4E0B\u9762\u90A3\u884C(\u81EA\u52A8\u6311\u65F6\u4E24\u8005\u4E0D\u540C)\n modelSel.value = configured;\n modelSel.disabled = st.models.length === 0 && !configured;\n modelDirNote.textContent = `\u6743\u91CD\u76EE\u5F55\uFF1A${st.modelsDir}`;\n const up = st.reachable;\n const missing = !st.installed || st.model === null;\n const tone = up ? 'on' : st.phase === 'starting' ? 'warn' : 'off';\n srvDot.className = `navdot${up ? ' on' : st.phase === 'starting' ? ' warn' : ' bad'}`;\n srvState.className = `mstate asr-${tone}`;\n srvState.textContent = up\n ? (st.pid ? '\u8FD0\u884C\u4E2D' : '\u8FD0\u884C\u4E2D(\u5916\u90E8)')\n : missing\n ? '\u672A\u5B89\u88C5'\n : ({ starting: '\u542F\u52A8\u4E2D', error: '\u5F02\u5E38', stopped: '\u672A\u542F\u52A8' } as Record<string, string>)[st.phase] ?? st.phase;\n const note = missing && !up\n ? st.detail ?? (\n `${st.profile.toUpperCase()} \u6863\u7F3A${!st.installed ? '\u8FD0\u884C\u73AF\u5883' : '\u6743\u91CD'}:\u5728\u4E0B\u9762\u300C\u8FD0\u884C\u65F6\u4E0E\u6743\u91CD\u300D\u5B89\u88C5`\n )\n : [\n st.pid ? `pid ${st.pid}` : null,\n st.url,\n st.model,\n `${st.profile.toUpperCase()} \u6863`,\n st.detail,\n ].filter(Boolean).join(' \u00B7 ');\n srvDetail.textContent = note;\n srvDetail.title = note;\n btnSrvStart.disabled = st.phase === 'starting' || up || missing;\n btnSrvStop.disabled = st.pid === null && st.phase !== 'starting';\n }\n\n function render(st: AsrListenState | null): void {\n if (ctx.signal.aborted) return;\n cur = st;\n if (!st) {\n msg.say('\u6536\u542C\u9762\u677F\u4E0D\u53EF\u7528', true);\n return;\n }\n // \u8BBE\u5907\u8868\u6309**\u540D\u5B57**\u6BD4,\u4E0D\u6309\u6761\u6570:\u63D2\u62D4\u4E24\u652F\u9EA6\u514B\u98CE\u65F6\u6761\u6570\u4E0D\u53D8,\u800C\u9009\u9879\u8BE5\u6362\n const key = st.devices.map((d) => d.name).join('\\0');\n if (key !== deviceKey) {\n deviceKey = key;\n devSel.replaceChildren();\n const auto = ui.h('option', null, '\u7CFB\u7EDF\u9ED8\u8BA4');\n auto.value = '';\n devSel.appendChild(auto);\n for (const d of st.devices) {\n const op = ui.h('option', null, `${d.name}${d.isDefault ? '(\u9ED8\u8BA4)' : ''}`);\n op.value = d.name;\n devSel.appendChild(op);\n }\n }\n // \u4E0B\u62C9\u6846\u7ED1\u7684\u662F\u914D\u7F6E\u91CC\u9489\u7684\u90A3\u4E2A,'' = \u8DDF\u968F\u7CFB\u7EDF\u9ED8\u8BA4;\u771F\u6B63\u5728\u7528\u7684\u5199\u5728\u7535\u5E73\u6761\u65C1\u8FB9\n devSel.value = st.devices.some((d) => d.name === st.deviceSetting) ? st.deviceSetting : '';\n actual.textContent = st.device ? `\u5B9E\u9645:${st.device}` : '';\n if (!st.audioAvailable) {\n msg.say('\u672C\u673A\u6CA1\u6709\u53EF\u7528\u7684\u5F55\u97F3\u8BBE\u5907(\u6216\u539F\u751F\u58F0\u5361\u6A21\u5757\u6CA1\u88C5\u4E0A)', true);\n }\n mark.style.left = `${pct(st.thresholdDb)}%`;\n setLevel(st.level, st.speaking, st.listening);\n btnListen.disabled = st.listening || !st.audioAvailable;\n btnStop.disabled = !st.listening;\n devSel.disabled = !st.audioAvailable;\n renderServer(st.server);\n stats.replaceChildren(\n ui.stat({ k: '\u8BC6\u522B\u53E5\u6570', v: st.counts.utterances }),\n ui.stat({ k: '\u5DF2\u6295\u9012', v: st.counts.delivered, accent: true }),\n ui.stat({ k: '\u4E22\u5F03', v: st.counts.dropped }),\n ui.stat({ k: '\u7AEF\u70B9', v: st.endpoint.replace(/^https?:\\/\\//, '') }),\n );\n // \u9996\u6B21\u8FDB\u9762\u677F\u8865\u4E0A\u5DF2\u7ECF\u542C\u5230\u8FC7\u7684\u90A3\u4E9B(\u5B9E\u65F6\u901A\u9053\u53EA\u63A8\u6B64\u540E\u7684)\n for (const r of [...st.recent].reverse()) {\n if (r.at <= seenAt) continue;\n seenAt = r.at;\n logView.append(`${new Date(r.at).toTimeString().slice(0, 8)} ${r.text} (${r.ms}ms)`);\n }\n if (st.detail) msg.say(st.detail, st.detail.includes('\u5931\u8D25'));\n }\n\n function setLevel(db: number, speaking: boolean, listening: boolean): void {\n fill.style.width = `${pct(db)}%`;\n fill.classList.toggle('on', speaking);\n readout.textContent = listening ? `${Math.round(db)} dB${speaking ? ' \u00B7 \u6B63\u5728\u8BF4' : ''}` : '\u672A\u6536\u542C';\n }\n\n const refresh = (): Promise<void> =>\n ctx.invoke<AsrListenState>('state').then(render, () => render(null));\n\n async function act(fn: () => Promise<AsrListenState>, working: string): Promise<void> {\n msg.say(working);\n const lock = ui.disable(btnListen, btnStop);\n let next: AsrListenState | null = null;\n try {\n next = await fn();\n } catch (err) {\n if (!ctx.signal.aborted) msg.say(`\u64CD\u4F5C\u5931\u8D25: ${errText(err)}`, true);\n } finally {\n lock.dispose();\n }\n if (next) render(next);\n }\n\n btnListen.addEventListener('click', () => {\n void act(() => ctx.invoke<AsrListenState>('start'), '\u6253\u5F00\u9EA6\u514B\u98CE\u2026');\n }, { signal: ctx.signal });\n btnStop.addEventListener('click', () => {\n void act(() => ctx.invoke<AsrListenState>('stop'), '\u505C\u6B62\u4E2D\u2026');\n }, { signal: ctx.signal });\n btnClear.addEventListener('click', () => {\n logView.clear();\n void act(() => ctx.invoke<AsrListenState>('clear'), '');\n }, { signal: ctx.signal });\n devSel.addEventListener('change', () => {\n void act(() => ctx.invoke<AsrListenState>('setDevice', [devSel.value]), '\u6362\u8BBE\u5907\u2026');\n }, { signal: ctx.signal });\n modelSel.addEventListener('change', () => {\n void act(() => ctx.invoke<AsrListenState>('setModel', [modelSel.value]), '\u6362\u6743\u91CD(\u8981\u91CD\u8D77\u540E\u7AEF)\u2026');\n }, { signal: ctx.signal });\n btnModelFile.addEventListener('click', () => {\n void (async () => {\n const lock = ui.disable(btnModelFile, modelSel);\n try {\n const path = await ctx.pickPath({\n kind: 'file',\n title: '\u9009\u62E9 FireRedASR2-AED \u7684 model.pth.tar',\n extensions: ['.tar'],\n ...(cur?.server.modelsDir ? { recommendedDir: cur.server.modelsDir } : {}),\n ...(cur?.modelFile ? { currentPath: cur.modelFile } : {}),\n });\n if (!path || ctx.signal.aborted) return;\n const next = await ctx.invoke<AsrListenState>('setModel', [path]);\n render(next);\n msg.say(`\u6743\u91CD\u8DEF\u5F84\u5DF2\u4FDD\u5B58: ${path}`);\n } catch (err) {\n if (!ctx.signal.aborted) msg.say(`\u9009\u62E9\u6743\u91CD\u5931\u8D25: ${errText(err)}`, true);\n } finally {\n lock.dispose();\n }\n })();\n }, { signal: ctx.signal });\n\n btnSrvStart.addEventListener('click', () => {\n msg.say('\u8BC6\u522B\u540E\u7AEF\u542F\u52A8\u4E2D\u2026(\u8981\u52A0\u8F7D\u6743\u91CD)');\n void ctx.invoke<AsrServerState>('server.start').then(\n (st) => { renderServer(st); waitReady(); },\n (err: unknown) => { if (!ctx.signal.aborted) msg.say(`\u542F\u52A8\u5931\u8D25: ${errText(err)}`, true); },\n );\n }, { signal: ctx.signal });\n btnSrvStop.addEventListener('click', () => {\n void ctx.invoke<AsrServerState>('server.stop').then(\n (st) => { renderServer(st); msg.say('\u8BC6\u522B\u540E\u7AEF\u5DF2\u505C\u6B62'); },\n (err: unknown) => { if (!ctx.signal.aborted) msg.say(`\u505C\u6B62\u5931\u8D25: ${errText(err)}`, true); },\n );\n }, { signal: ctx.signal });\n\n /** \u7B49\u540E\u7AEF\u8D70\u5B8C starting\u3002\u8F6E\u8BE2\u8D70 ctx.interval,\u79BB\u5F00\u9762\u677F\u5373\u505C\u3002 */\n function waitReady(): void {\n let ticks = 0;\n const timer = ctx.interval(() => {\n if (++ticks > 60) {\n timer.dispose();\n msg.say('\u8BC6\u522B\u540E\u7AEF\u542F\u52A8\u8D85\u65F6', true);\n return;\n }\n void ctx.invoke<AsrServerState>('server.state').then((st) => {\n renderServer(st);\n if (st.phase === 'starting') return;\n timer.dispose();\n msg.say(st.reachable ? '\u8BC6\u522B\u540E\u7AEF\u5C31\u7EEA' : `\u8BC6\u522B\u540E\u7AEF ${st.detail || st.phase}`, !st.reachable);\n });\n }, 2000);\n }\n\n // ---- \u5B9E\u65F6\u901A\u9053:\u7535\u5E73\u4E0E\u8BC6\u522B\u6587\u672C ----\n ctx.stream({\n message: (raw) => {\n let frame: AsrPanelFrame;\n try {\n frame = JSON.parse(raw) as AsrPanelFrame;\n } catch {\n return;\n }\n // \u53EA\u8BA4\u5F97\u8FD9\u4E09\u79CD;\u522B\u7684(\u901A\u9053\u81EA\u8EAB\u7684\u9519\u8BEF\u5E27\u4E00\u7C7B)\u4E0D\u5F80\u8BC6\u522B\u6587\u672C\u91CC\u585E\u2014\u2014\n // \u90A3\u6761\u6D41\u91CC\u6DF7\u8FDB\u4E00\u53E5\u6846\u67B6\u7684\u8BDD,\u8BFB\u8D77\u6765\u5C31\u50CF\u6709\u4EBA\u771F\u8BF4\u4E86\u8FD9\u53E5\n if (frame.type === 'level') {\n setLevel(frame.level, frame.speaking, frame.listening);\n if (cur && cur.listening !== frame.listening) void refresh();\n return;\n }\n if (frame.type !== 'text' && frame.type !== 'dropped') return;\n const at = frame.type === 'text' ? frame.at : Date.now();\n seenAt = Math.max(seenAt, at);\n const stamp = new Date(at).toTimeString().slice(0, 8);\n logView.append(frame.type === 'dropped'\n ? `${stamp} [\u4E22\u5F03] ${frame.text}`\n : `${stamp} ${frame.text} (${frame.ms}ms)`);\n },\n });\n\n void refresh();\n // \u8BA1\u6570\u4E0E\u540E\u7AEF\u72B6\u6001\u4E0D\u8D70\u5B9E\u65F6\u901A\u9053,\u6162\u4E00\u70B9\u65E0\u6240\u8C13\n ctx.interval(() => { void refresh(); }, 5000);\n\n mountRuntimeSection(ctx);\n },\n};\n", "/**\n * \u9762\u677F `overlay` \u2014\u2014 \u5B57\u5E55\u5C42:\u63A8\u6D41\u94FE\u63A5\u3001\u6837\u5F0F\u4E0E\u8BD5\u663E\u3002\n *\n * \u9884\u89C8\u7528 iframe \u5D4C**\u540C\u4E00\u4E2A** overlay \u9875(\u5E26 `?bg=dim` \u7ED9\u4E2A\u6697\u5E95),\u4E0D\u53E6\u5199\u4E00\u4EFD\n * \u6E32\u67D3:\u4E24\u4EFD\u6E32\u67D3\u610F\u5473\u7740\"\u9884\u89C8\u91CC\u597D\u770B\u3001OBS \u91CC\u4E0D\u5BF9\"\u8FD9\u7C7B\u95EE\u9898\u6C38\u8FDC\u67E5\u4E0D\u6E05\u3002\n *\n * \u6837\u5F0F\u6539\u52A8\u5373\u65F6\u751F\u6548\u2014\u2014\u670D\u52A1\u7AEF\u94B3\u5B8C\u503C\u7ECF SSE \u70ED\u63A8\u7ED9\u6240\u6709\u8BA2\u9605\u8005,OBS \u91CC\u90A3\u4EFD\u4E0D\u7528\u91CD\u5F00\u3002\n */\n\nimport type {\n ConsolePanelContext,\n ConsolePanel,\n} from 'cortico/web/shared/client-panel.ts';\nimport {\n errText,\n msgLine,\n type AsrOverlayConfig,\n type AsrOverlayState,\n} from './client.ts';\n\nconst DESC =\n 'OBS \u91CC\u52A0\u4E00\u4E2A\u6D4F\u89C8\u5668\u6E90,\u5730\u5740\u586B\u4E0B\u9762\u8FD9\u6761,\u5C3A\u5BF8\u6309\u753B\u5E03\u6765\u3002\u5B57\u5E55\u6837\u5F0F\u6539\u5B8C\u5373\u65F6\u751F\u6548,'\n + 'OBS \u90A3\u4EFD\u4E0D\u7528\u91CD\u5F00\u3002\u5B57\u4F53\u586B OBS \u90A3\u53F0\u673A\u5668\u4E0A\u88C5\u4E86\u7684\u5B57\u4F53\u540D\u3002';\n\nexport const overlayPanel: ConsolePanel = {\n mount(ctx: ConsolePanelContext) {\n const { ui, root } = ctx;\n const card = ui.sheet({ title: 'Overlay \u5B57\u5E55', en: 'subtitle overlay', desc: DESC });\n const s = card.body;\n root.appendChild(card.el);\n\n const msg = msgLine(ctx, 'grow');\n const linkBox = ui.h('div');\n s.append(ui.section('\u63A8\u6D41\u94FE\u63A5'), linkBox);\n\n // ---- \u6837\u5F0F ----\n const scale = ui.input({ type: 'number', value: '1' });\n const weight = ui.select({ options: ['300', '400', '500', '600', '700', '800'] });\n const maxLines = ui.input({ type: 'number', value: '2' });\n const holdSec = ui.input({ type: 'number', value: '6' });\n const color = ui.input({ value: '#fffef8', cls: 'mono' });\n const strokeColor = ui.input({ value: '#000000', cls: 'mono' });\n const strokeW = ui.input({ type: 'number', value: '1.5' });\n const plate = ui.input({ type: 'number', value: '0.35' });\n const font = ui.input({ placeholder: '\u7559\u7A7A = \u9875\u9762\u9ED8\u8BA4' });\n const partialIn = ui.checkbox('\u663E\u793A\"\u6B63\u5728\u8BF4\u2026\"', { checked: true });\n const form = ui.h('div', 'asr-form');\n form.append(\n ui.field('\u5B57\u53F7\u500D\u7387', scale),\n ui.field('\u5B57\u91CD', weight),\n ui.field('\u4FDD\u7559\u884C\u6570', maxLines),\n ui.field('\u505C\u7559(\u79D2)', holdSec),\n ui.field('\u5B57\u8272', color),\n ui.field('\u63CF\u8FB9\u8272', strokeColor),\n ui.field('\u63CF\u8FB9\u5BBD(px)', strokeW),\n ui.field('\u5E95\u677F\u4E0D\u900F\u660E\u5EA6', plate),\n ui.field('\u5B57\u4F53', font),\n );\n const btnApply = ui.button('\u5E94\u7528', { size: 'sm', variant: 'primary' });\n const testText = ui.input({ placeholder: '\u8BD5\u663E\u4E00\u6761\u5B57\u5E55' });\n const btnTest = ui.button('\u8BD5\u663E', { size: 'sm' });\n const acts = ui.actions();\n acts.append(partialIn.el, ui.h('span', 'grow'), testText, btnTest, btnApply);\n s.append(ui.section('\u5B57\u5E55\u6837\u5F0F'), form, acts, msg.el);\n\n // ---- \u9884\u89C8 ----\n const frame = ui.h('iframe', 'asr-preview');\n frame.setAttribute('title', '\u5B57\u5E55\u9884\u89C8');\n s.append(ui.section('\u9884\u89C8', '\u4E0E OBS \u91CC\u90A3\u4EFD\u662F\u540C\u4E00\u4E2A\u9875\u9762,\u53EA\u662F\u52A0\u4E86\u6697\u5E95'), frame);\n\n // -----------------------------------------------------------------------\n\n function render(st: AsrOverlayState | null): void {\n if (ctx.signal.aborted) return;\n if (!st) {\n msg.say('\u5B57\u5E55\u9762\u677F\u4E0D\u53EF\u7528', true);\n linkBox.replaceChildren(ui.placeholder('\u8BC6\u522B\u6D41\u6CA1\u8D77\u6765'));\n return;\n }\n linkBox.replaceChildren(ui.kv([\n { k: '\u5B57\u5E55\u9875', v: st.url ?? '(\u672A\u542F\u52A8)' },\n { k: '\u8BA2\u9605\u8005', v: `${st.subscribers} \u4E2A(OBS \u6D4F\u89C8\u5668\u6E90 / \u9884\u89C8\u5404\u7B97\u4E00\u4E2A)` },\n { k: '\u590D\u5236', v: st.url ? ui.copyButton(st.url, { label: '\u590D\u5236\u94FE\u63A5' }) : '\u2014' },\n ]));\n const c = st.config;\n scale.value = String(c.subtitle.scale);\n weight.value = String(c.subtitle.weight);\n maxLines.value = String(c.maxLines);\n holdSec.value = String(Math.round(c.holdMs / 1000));\n color.value = c.subtitle.color;\n strokeColor.value = c.subtitle.strokeColor;\n strokeW.value = String(c.subtitle.strokeW);\n plate.value = String(c.subtitle.plate);\n font.value = c.subtitle.fontFamily;\n partialIn.setChecked(c.showPartial);\n const want = st.url ? `${st.url}?bg=dim` : '';\n if (want && frame.getAttribute('src') !== want) frame.setAttribute('src', want);\n }\n\n const patch = (): Partial<AsrOverlayConfig> => ({\n maxLines: Number(maxLines.value),\n holdMs: Math.round(Number(holdSec.value) * 1000),\n showPartial: partialIn.checked,\n subtitle: {\n scale: Number(scale.value),\n weight: Number(weight.value),\n color: color.value.trim(),\n strokeColor: strokeColor.value.trim(),\n strokeW: Number(strokeW.value),\n plate: Number(plate.value),\n fontFamily: font.value.trim(),\n },\n });\n\n const refresh = (): Promise<void> =>\n ctx.invoke<AsrOverlayState>('state').then(render, () => render(null));\n\n btnApply.addEventListener('click', () => {\n msg.say('\u5E94\u7528\u4E2D\u2026');\n void ctx.invoke<AsrOverlayState>('setConfig', [patch()]).then(\n (st) => { render(st); msg.say('\u5DF2\u751F\u6548\u5E76\u843D\u76D8;\u8BA2\u9605\u4E2D\u7684\u9875\u9762\u5DF2\u70ED\u66F4\u65B0'); },\n (err: unknown) => { if (!ctx.signal.aborted) msg.say(`\u5E94\u7528\u5931\u8D25: ${errText(err)}`, true); },\n );\n }, { signal: ctx.signal });\n\n btnTest.addEventListener('click', () => {\n void ctx.invoke<AsrOverlayState>('test', [testText.value]).then(\n (st) => { render(st); msg.say('\u5DF2\u6295\u4E00\u6761\u5230\u5B57\u5E55\u6D41'); },\n (err: unknown) => { if (!ctx.signal.aborted) msg.say(`\u8BD5\u663E\u5931\u8D25: ${errText(err)}`, true); },\n );\n }, { signal: ctx.signal });\n\n void refresh();\n },\n};\n", "/**\n * \u8BED\u97F3\u8BC6\u522B World \u7684\u6D4F\u89C8\u5668\u6269\u5C55 \u2014\u2014 \u6536\u542C / Overlay \u5B57\u5E55 \u4E24\u4E2A\u9762\u677F\u3002\n *\n * \u8FD9\u4E2A\u6587\u4EF6\u53EA\u505A\u88C5\u914D\u4E0E\u5171\u4EAB\u7C7B\u578B;\u9762\u677F\u672C\u4F53\u5404\u5728\u81EA\u5DF1\u7684\u6587\u4EF6\u91CC\u3002\u4E0E\u5916\u754C\u7684\u4F9D\u8D56\u53EA\u6709\n * `client-panel.ts` \u7684\u7C7B\u578B:\u6570\u636E\u9762\u4E00\u5F8B\u8D70 `ctx.invoke`,\u5B9E\u65F6\u6570\u636E\u8D70 `ctx.stream`,\n * DOM \u4E00\u5F8B\u7528 `ctx.ui` \u539F\u8BED,\u5B9A\u65F6\u5668\u4E00\u5F8B `ctx.interval`\u3002\n *\n * `style.css` \u662F\u672C provider \u81EA\u5DF1\u7684\u6837\u5F0F,\u53EA\u653E `ctx.ui` \u6CA1\u6709\u5BF9\u5E94\u539F\u8BED\u7684\u90A3\u4E24\u5757\n * (\u7535\u5E73\u6761\u3001\u5B57\u5E55\u9884\u89C8\u6846),\u7528 `asr-` \u524D\u7F00\u907F\u5F00\u63A7\u5236\u53F0\u7684\u901A\u7528 class\u3002\n */\n\nimport type {\n ConsoleClientBundle,\n ConsolePanelContext,\n} from 'cortico/web/shared/client-panel.ts';\nimport './style.css';\nimport { listenPanel } from './listen.ts';\nimport { overlayPanel } from './overlay.ts';\n\n// ---------------------------------------------------------------------------\n// \u5171\u4EAB\u7C7B\u578B:\u670D\u52A1\u7AEF `AsrWorld.invokePanel` \u5404\u65B9\u6CD5\u7684\u8FD4\u56DE\u5F62\u72B6\n// ---------------------------------------------------------------------------\n\ninterface AsrMicDevice {\n id: number;\n name: string;\n channels: number;\n isDefault: boolean;\n}\n\nexport interface AsrServerState {\n phase: string;\n url: string;\n detail: string | null;\n pid: number | null;\n reachable: boolean;\n model: string | null;\n profile: string;\n /** \u6709\u4E00\u4E2A\u80FD\u8DD1\u7684\u8FD0\u884C\u65F6\u76EE\u5F55(\u6258\u7BA1\u88C5\u7684\u6216\u81EA\u5907\u7684) */\n installed: boolean;\n modelsDir: string;\n /** \u6743\u91CD\u6839\u4E0B\u6BCF\u4E00\u7EC4\u5B8C\u6574\u6743\u91CD */\n models: string[];\n}\n\n/** `listen.runtime` */\nexport interface AsrRuntimeState {\n revision: string;\n key: string | null;\n dir: string;\n own: boolean;\n supported: boolean;\n install: { phase: 'absent' | 'installing' | 'installed' | 'error'; step: string | null; line: string | null; detail: string | null };\n models: Array<{\n file: string; path: string; phase: 'absent' | 'downloading' | 'present' | 'error';\n bytes: number; done: number; total: number | null; detail: string | null;\n }>;\n modelsDir: string;\n modelsSource: string;\n modelsComplete: boolean;\n}\n\n/** `listen.state` */\nexport interface AsrListenState {\n listening: boolean;\n audioAvailable: boolean;\n devices: AsrMicDevice[];\n /** \u914D\u7F6E\u91CC\u9489\u7684\u90A3\u4E2A;\u7A7A = \u8DDF\u968F\u7CFB\u7EDF\u9ED8\u8BA4 */\n deviceSetting: string;\n /** \u6B64\u523B\u771F\u6B63\u5728\u7528\u7684\u8BBE\u5907\u540D */\n device: string;\n /** \u6700\u8FD1\u4E00\u5E27\u7684\u7535\u5E73(dBFS) */\n level: number;\n speaking: boolean;\n thresholdDb: number;\n /** \u914D\u7F6E\u91CC\u9489\u7684\u6743\u91CD\u6587\u4EF6\u540D;\u7A7A = \u81EA\u52A8\u6311 */\n modelFile: string;\n simplified: boolean;\n recent: Array<{ text: string; at: number; ms: number }>;\n counts: { utterances: number; delivered: number; dropped: number };\n server: AsrServerState;\n endpoint: string;\n detail: string | null;\n}\n\nexport interface AsrSubtitleStyle {\n scale: number;\n weight: number;\n color: string;\n strokeColor: string;\n strokeW: number;\n plate: number;\n fontFamily: string;\n}\n\nexport interface AsrOverlayConfig {\n maxLines: number;\n holdMs: number;\n showPartial: boolean;\n subtitle: AsrSubtitleStyle;\n}\n\n/** `overlay.state` */\nexport interface AsrOverlayState {\n url: string | null;\n streamUp: boolean;\n subscribers: number;\n config: AsrOverlayConfig;\n}\n\n/** `listen` \u7684\u5B9E\u65F6\u901A\u9053\u63A8\u7684\u5E27 */\nexport type AsrPanelFrame =\n | { type: 'level'; level: number; speaking: boolean; listening: boolean }\n | { type: 'text'; text: string; at: number; ms: number }\n | { type: 'dropped'; text: string; ms: number };\n\n// ---------------------------------------------------------------------------\n// \u5171\u4EAB helper\n// ---------------------------------------------------------------------------\n\nexport function errText(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n\ninterface AsrMsgLine {\n el: HTMLDivElement;\n say(text?: string | null, bad?: boolean): void;\n}\n\nexport function msgLine(ctx: ConsolePanelContext, cls?: string): AsrMsgLine {\n const el = ctx.ui.msgline('');\n if (cls) el.classList.add(cls);\n return {\n el,\n say(text, bad) {\n el.textContent = text ?? '';\n el.classList.toggle('bad', bad === true);\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n\nconst bundle: ConsoleClientBundle = {\n panels: {\n listen: listenPanel,\n overlay: overlayPanel,\n },\n};\n\nexport default bundle;\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["SVG_NS", "SHAPES", "icon", "doc", "name", "cls", "svg", "tag", "attrs", "child", "key", "value", "DESC", "gb", "bytes", "progressText", "done", "total", "mountRuntimeSection", "ctx", "ui", "card", "msg", "chip", "btnInstall", "install", "head", "dirLine", "stepLine", "modelsMsg", "modelsChip", "btnDownload", "download", "modelsHead", "modelsDirLine", "rows", "say", "el", "text", "bad", "busy", "refresh", "st", "error", "errText", "phase", "downloading", "m", "row", "state", "poll", "DESC", "FLOOR_DB", "pct", "db", "listenPanel", "ctx", "ui", "root", "card", "s", "msg", "msgLine", "btnListen", "btnStop", "bar", "devSel", "devForm", "meter", "fill", "mark", "readout", "meterRow", "actual", "modelSel", "btnModelFile", "icon", "modelDownload", "modelForm", "modelRow", "modelMeta", "modelDirNote", "modelField", "srvLine", "srvDot", "srvState", "srvDetail", "srvActs", "btnSrvStop", "btnSrvStart", "stats", "logView", "btnClear", "logBar", "cur", "seenAt", "deviceKey", "modelKey", "renderServer", "st", "configured", "key", "auto", "m", "op", "external", "up", "missing", "tone", "note", "render", "d", "setLevel", "r", "speaking", "listening", "refresh", "act", "fn", "working", "lock", "next", "err", "errText", "path", "waitReady", "ticks", "timer", "raw", "frame", "at", "stamp", "mountRuntimeSection", "DESC", "overlayPanel", "ctx", "ui", "root", "card", "s", "msg", "msgLine", "linkBox", "scale", "weight", "maxLines", "holdSec", "color", "strokeColor", "strokeW", "plate", "font", "partialIn", "form", "btnApply", "testText", "btnTest", "acts", "frame", "render", "st", "c", "want", "patch", "refresh", "err", "errText", "errText", "err", "msgLine", "ctx", "cls", "el", "text", "bad", "bundle", "listenPanel", "overlayPanel", "client_default"]
|
|
7
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "cortico-world-asr",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Cortico World: microphone speech recognition — RtAudio capture, energy-gated segmentation, FireRedASR2-AED backend, subtitle overlay",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/Phantivia/cortico-world-asr.git"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/Phantivia/cortico-world-asr#readme",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/Phantivia/cortico-world-asr/issues"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"cortico-world",
|
|
17
|
+
"cortico",
|
|
18
|
+
"asr",
|
|
19
|
+
"speech-recognition",
|
|
20
|
+
"fireredasr"
|
|
21
|
+
],
|
|
22
|
+
"engines": {
|
|
23
|
+
"node": ">=22"
|
|
24
|
+
},
|
|
25
|
+
"packageManager": "pnpm@11.5.0",
|
|
26
|
+
"main": "./src/index.ts",
|
|
27
|
+
"cortico": {
|
|
28
|
+
"kind": "world",
|
|
29
|
+
"api": 4,
|
|
30
|
+
"consoleClient": "dist/console.js",
|
|
31
|
+
"consoleStyle": "dist/console.css"
|
|
32
|
+
},
|
|
33
|
+
"files": [
|
|
34
|
+
"src",
|
|
35
|
+
"dist",
|
|
36
|
+
"scripts",
|
|
37
|
+
"README.md",
|
|
38
|
+
"LICENSE",
|
|
39
|
+
"THIRD_PARTY_NOTICES.md"
|
|
40
|
+
],
|
|
41
|
+
"scripts": {
|
|
42
|
+
"build": "node scripts/build-console.mjs",
|
|
43
|
+
"test": "vitest run",
|
|
44
|
+
"typecheck": "tsc --noEmit"
|
|
45
|
+
},
|
|
46
|
+
"dependencies": {
|
|
47
|
+
"audify": "^1.10.1",
|
|
48
|
+
"opencc-js": "^1.4.1"
|
|
49
|
+
},
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"@types/node": "^22.10.0",
|
|
52
|
+
"esbuild": "^0.28.2",
|
|
53
|
+
"jsdom": "^30.0.1",
|
|
54
|
+
"tsx": "^4.19.2",
|
|
55
|
+
"typescript": "^5.7.2",
|
|
56
|
+
"vitest": "^2.1.8"
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 面板 bundle:`src/console/client.ts` → `dist/console.js` + `dist/console.css`。
|
|
3
|
+
* 两个产物名写死在 package.json 的 `cortico.consoleClient` / `consoleStyle` 里,
|
|
4
|
+
* 服务端自己给它们分配 URL。
|
|
5
|
+
*
|
|
6
|
+
* 不给 `cortico/*` 配 alias 也不 external:浏览器侧只允许 `import type` 框架,
|
|
7
|
+
* 那些 import 编译期就被擦掉。构建若报 "Could not resolve cortico/…",说明有一处
|
|
8
|
+
* 运行时依赖漏网,去把它本地化,而不是在这里放行。
|
|
9
|
+
*/
|
|
10
|
+
import * as esbuild from 'esbuild';
|
|
11
|
+
import { mkdirSync, rmSync } from 'node:fs';
|
|
12
|
+
import { fileURLToPath } from 'node:url';
|
|
13
|
+
|
|
14
|
+
const root = fileURLToPath(new URL('../', import.meta.url));
|
|
15
|
+
const outdir = `${root}dist`;
|
|
16
|
+
|
|
17
|
+
rmSync(outdir, { recursive: true, force: true });
|
|
18
|
+
mkdirSync(outdir, { recursive: true });
|
|
19
|
+
|
|
20
|
+
const result = await esbuild.build({
|
|
21
|
+
entryPoints: [{ in: `${root}src/console/client.ts`, out: 'console' }],
|
|
22
|
+
outdir,
|
|
23
|
+
bundle: true,
|
|
24
|
+
format: 'esm',
|
|
25
|
+
target: 'es2022',
|
|
26
|
+
platform: 'browser',
|
|
27
|
+
minify: true,
|
|
28
|
+
sourcemap: true,
|
|
29
|
+
metafile: true,
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
for (const [path, info] of Object.entries(result.metafile.outputs)) {
|
|
33
|
+
if (path.endsWith('.map')) continue;
|
|
34
|
+
console.log(`${path.replace(/\\/g, '/')} ${(info.bytes / 1024).toFixed(1)} KB`);
|
|
35
|
+
}
|