diff --git a/README.md b/README.md index 58d5c8b..b901f23 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,8 @@ TypeNo uses [coli](https://github.com/marswaveai/coli) for local speech recognit npm install -g @marswave/coli ``` +This installs the `coli` CLI itself, but it does not pre-download the speech model. + If Coli is missing, TypeNo will show an in-app setup prompt with the install command. > **Node 24+:** If you get a `sherpa-onnx-node` error, build from source: @@ -60,14 +62,18 @@ TypeNo needs two one-time permissions: The app will guide you through granting these on first launch. +On the first actual transcription, `coli` will also download its speech model into `~/.coli/models/`. That is a separate step from `npm install -g @marswave/coli`. + ### Troubleshooting: Coli Model Download Fails -The speech model is downloaded from GitHub. If GitHub is inaccessible in your network, the download will fail. +The speech model is downloaded from GitHub. If GitHub is inaccessible in your network, the first transcription can fail while `coli` is downloading the model. -**Fix:** Enable **TUN mode** (also called Enhanced Mode) in your proxy tool to ensure all system-level traffic is routed correctly. Then retry the install: +**Fix:** Enable **TUN mode** (also called Enhanced Mode) in your proxy tool to ensure all system-level traffic is routed correctly. Then trigger another transcription so `coli` can retry the model download. + +If `~/.coli/models/` contains a partial `.tar.bz2` archive from a failed download, delete the leftover files in that directory before retrying. ```bash -npm install -g @marswave/coli +rm -rf ~/.coli/models ``` ### Troubleshooting: Accessibility Permission Not Working diff --git a/README_CN.md b/README_CN.md index 42696ca..a6948af 100644 --- a/README_CN.md +++ b/README_CN.md @@ -44,6 +44,8 @@ TypeNo 使用 [coli](https://github.com/marswaveai/coli) 进行本地语音识 npm install -g @marswave/coli ``` +这一步只会安装 `coli` 命令本身,不会提前下载语音模型。 + 如果未安装 Coli,TypeNo 会在应用内弹出引导提示。 > **Node 24+:** 如果遇到 `sherpa-onnx-node` 错误,请从源码编译安装: @@ -59,14 +61,18 @@ TypeNo 需要两个一次性授权: 首次启动时,应用会自动引导你完成授权。 +首次真正执行转录时,`coli` 还会从 GitHub 下载语音模型到 `~/.coli/models/`。这和 `npm install -g @marswave/coli` 是两个独立阶段。 + ### 常见问题:Coli 模型下载失败 -语音模型从 GitHub 下载。如果你的网络无法访问 GitHub,下载会失败。 +语音模型从 GitHub 下载。如果你的网络无法访问 GitHub,首次转录时的模型下载会失败。 -**解决方法:** 在代理工具中开启 **TUN 模式**(也叫增强模式),确保系统级流量正常路由。然后重试安装: +**解决方法:** 在代理工具中开启 **TUN 模式**(也叫增强模式),确保系统级流量正常路由。然后重新触发一次转录,让 `coli` 再次下载模型。 + +如果 `~/.coli/models/` 里留下了不完整的 `.tar.bz2` 文件,先删除该目录下的残留文件再重试。 ```bash -npm install -g @marswave/coli +rm -rf ~/.coli/models ``` ### 常见问题:辅助功能权限无效 diff --git a/Sources/Typeno/main.swift b/Sources/Typeno/main.swift index b2ec9f4..0100045 100644 --- a/Sources/Typeno/main.swift +++ b/Sources/Typeno/main.swift @@ -524,6 +524,13 @@ final class AppState: ObservableObject { } // Verify installation if ColiASRService.isInstalled { + phase = .installingColi( + L( + "Coli installed. The first transcription will download the speech model.", + "Coli 已安装。首次转录时会下载语音模型。" + ) + ) + try? await Task.sleep(for: .seconds(1.5)) phase = .idle onOverlayRequest?(false) } else { @@ -1518,7 +1525,7 @@ final class ColiASRService: @unchecked Sendable { let fm = FileManager.default if !fm.fileExists(atPath: senseVoiceCheckFile.path) && fm.fileExists(atPath: senseVoiceArchive.path) { - return "Coli model download looks incomplete. Delete \(senseVoiceArchive.path) and try again." + return "Coli's first model download looks incomplete in ~/.coli/models. Delete the partial archive and try transcribing again." } return nil @@ -1547,7 +1554,7 @@ final class ColiASRService: @unchecked Sendable { .joined(separator: "\n") if combined.isEmpty { - return "Transcription timed out. Coli may still be downloading its first model, or the network/proxy may be blocking GitHub." + return "Transcription timed out. Coli downloads its first model on first use into ~/.coli/models. If GitHub is blocked, enable TUN/Enhanced proxy mode and try again." } let lower = combined.lowercased() @@ -2310,6 +2317,11 @@ struct OverlayView: View { } // Text content — single line + let isError = { + if case .error = appState.phase { return true } + return false + }() + Group { if case .done(let text) = appState.phase { Text(text) @@ -2330,9 +2342,9 @@ struct OverlayView: View { .foregroundStyle(.white.opacity(0.7)) } } - .font(.system(size: 14)) - .lineLimit(1) - .truncationMode(.head) + .font(.system(size: isError ? 12 : 14)) + .lineLimit(isError ? 3 : 1) + .truncationMode(isError ? .tail : .head) Spacer(minLength: 0) @@ -2357,7 +2369,7 @@ struct OverlayView: View { } .padding(.horizontal, 14) .padding(.vertical, 10) - .frame(width: 360) + .frame(width: isError ? 420 : 360) .background( RoundedRectangle(cornerRadius: 12, style: .continuous) .fill(Color(white: 0.15))