Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions .github/workflows/macos-installer.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: macOS installer

on:
push:
branches: [develop, 'feat--**']
pull_request:
branches: [develop]

permissions:
contents: read

jobs:
installer:
runs-on: macos-14
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v6
with:
version: '0.11.29'
enable-cache: false
- run: brew install shellcheck
- run: shellcheck --shell=bash scripts/install_macos.sh
- run: /bin/bash -n scripts/install_macos.sh
- run: uv python install 3.12
- run: uvx --python 3.12 --from pytest==9.1.1 --with pytest-asyncio==1.4.0 pytest -q tests/test_macos_installer.py
41 changes: 37 additions & 4 deletions docs/macos-setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,45 @@
当前适配目标为 Apple Silicon macOS。默认使用 Paraformer 语音模型和
Punct-CT-Transformer 标点模型,服务端与客户端均在本机离线运行。

## 1. 安装环境
## 推荐:从 Release 安装源码版

[`v2.6.0-macos.1`](https://github.com/Alex-ghost599/CapsWriter-Offline-macOS/releases/tag/v2.6.0-macos.1)
是 macOS 源码安装实验版。Release 不包含预编译 App 或模型,只提供可审查的安装脚本和
SHA-256 校验文件;脚本会拉取固定 tag、以 `uv` 安装冻结依赖、校验下载模型并在本机
构建 `CapsWriter.app`。

先安装 [Homebrew](https://brew.sh),再在终端执行:

```bash
BASE_URL=https://github.com/Alex-ghost599/CapsWriter-Offline-macOS/releases/download/v2.6.0-macos.1
curl -fLO "$BASE_URL/install-macos.sh"
curl -fLO "$BASE_URL/SHA256SUMS"
shasum -a 256 -c SHA256SUMS
bash install-macos.sh
```

默认安装到 `~/CapsWriter-Offline-macOS`,并下载约 530 MB 的 Paraformer 和标点模型。
暂时不下载模型可执行:

```bash
bash install-macos.sh --skip-models
```

其他可用参数见 `bash install-macos.sh --help`。只要安装目录已经存在,安装器就会在执行
Homebrew 前停止;请改用新的 `--install-dir`,已有目录始终由用户手动管理。安装器不会
自动启动服务或客户端。每台 Mac 都需要各自执行安装,并分别授予麦克风、辅助功能和
输入监控权限。

## 1. 手动安装环境

```bash
brew install uv ffmpeg
cd ~/CapsWriter-Offline
git clone --depth 1 --branch v2.6.0-macos.1 \
https://github.com/Alex-ghost599/CapsWriter-Offline-macOS.git \
~/CapsWriter-Offline-macOS
cd ~/CapsWriter-Offline-macOS
uv python install 3.12
uv sync --all-groups
uv sync --all-groups --frozen
```

仓库固定 Python 3.12,所有解释器、虚拟环境与 Python 依赖均由 `uv` 管理。
Expand Down Expand Up @@ -60,7 +92,8 @@ Command-V 和监听全局右 Shift。按住右 Shift 说话,松开后识别文

手动构建使用 ad-hoc 签名。每次重建二进制后,macOS 可能不继承上一版的
TCC 授权,需删除旧权限项、重新添加最终 `CapsWriter.app` 并再启动。要做稳定
分发,仍需 Apple Developer 签名身份与公证。
的免警告二进制分发,仍需 Apple Developer 签名身份与公证;这不影响当前源码安装版
作为 GitHub Release 发布和使用。

## 5. 源码调试

Expand Down
21 changes: 19 additions & 2 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,23 @@
**CapsWriter-Offline** 是一个**完全离线**语音输入工具,支持 Windows;本 fork 另提供
Apple Silicon macOS 手动构建与运行适配。

## macOS 源码安装实验版

[`v2.6.0-macos.1`](https://github.com/Alex-ghost599/CapsWriter-Offline-macOS/releases/tag/v2.6.0-macos.1)
提供可审查的源码安装脚本,不包含预编译 App 或模型。安装 [Homebrew](https://brew.sh) 后执行:

```bash
BASE_URL=https://github.com/Alex-ghost599/CapsWriter-Offline-macOS/releases/download/v2.6.0-macos.1
curl -fLO "$BASE_URL/install-macos.sh"
curl -fLO "$BASE_URL/SHA256SUMS"
shasum -a 256 -c SHA256SUMS
bash install-macos.sh
```

脚本会固定安装该 tag,以 `uv` 管理 Python 3.12 和冻结依赖,默认校验下载模型并在本机
构建 `dist/CapsWriter.app`;用 `--skip-models` 可暂不下载模型。完整参数、启动方式和每台
Mac 的权限设置见 [macOS 安装与运行](docs/macos-setup.md)。

## ✨ 核心特性

- **语音输入**:按住 `CapsLock键` 或 `鼠标侧键X2` 说话,松开即输入,超低延迟,默认去除末尾逗句号。支持对讲机模式和单击录音模式。
Expand Down Expand Up @@ -79,8 +96,8 @@ CapsWriter 的特别之处在于追求:

## 🎬 快速开始

Apple Silicon macOS 请直接按 [macOS 安装与运行](docs/macos-setup.md) 操作。以下步骤为
Windows 发行包流程。
Apple Silicon macOS 请使用上面的 Release 源码安装入口,或按
[macOS 安装与运行](docs/macos-setup.md) 手动操作。以下步骤为 Windows 发行包流程。

1. **准备环境**:确保安装了 [VC++ 运行库](https://learn.microsoft.com/zh-cn/cpp/windows/latest-supported-vc-redist)。若要使用文件转录功能,还需安装 [ffmpeg](https://ffmpeg.org/download.html) 并确保其在系统 PATH 中。
2. **下载解压**:下载 [Latest Release](https://github.com/HaujetZhao/CapsWriter-Offline/releases/latest) 里的软件本体,再到 [Models Release](https://github.com/HaujetZhao/CapsWriter-Offline/releases/tag/models) 下载模型压缩包,将模型解压,放入 `models` 文件夹中对应模型的文件夹里。
Expand Down
202 changes: 202 additions & 0 deletions scripts/install_macos.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
#!/usr/bin/env bash

set -Eeuo pipefail

readonly REPO_URL='https://github.com/Alex-ghost599/CapsWriter-Offline-macOS.git'
readonly DEFAULT_REF='v2.6.0-macos.1'

INSTALL_DIR="${HOME}/CapsWriter-Offline-macOS"
REF="${DEFAULT_REF}"
SKIP_MODELS=0
SKIP_BUILD=0
SKIP_SYSTEM_DEPS=0
DRY_RUN=0

usage() {
cat <<'EOF'
Install CapsWriter-Offline-macOS from source on Apple Silicon.

Usage:
bash install-macos.sh [options]

Options:
--install-dir PATH Install directory (default: ~/CapsWriter-Offline-macOS)
--ref REF Git tag or branch to install (default: v2.6.0-macos.1)
--skip-models Skip the verified ASR and punctuation model download
--skip-build Skip building dist/CapsWriter.app
--skip-system-deps Do not run Homebrew; require uv and ffmpeg in PATH
--dry-run Print commands without changing the system
-h, --help Show this help

The installer never resets an existing checkout and never starts the app.
The install directory must not already exist.
EOF
}

die() {
printf 'Error: %s\n' "$*" >&2
exit 1
}

handle_error() {
local line="$1"
local status="$2"
printf '\nInstallation stopped at line %s (exit %s).\n' "$line" "$status" >&2
exit "$status"
}

trap 'handle_error "$LINENO" "$?"' ERR

print_command() {
printf ' $'
printf ' %q' "$@"
printf '\n'
}

run() {
print_command "$@"
if [[ "$DRY_RUN" -eq 0 ]]; then
"$@"
fi
}

run_in_dir() {
local directory="$1"
shift
printf ' $ cd %q &&' "$directory"
printf ' %q' "$@"
printf '\n'
if [[ "$DRY_RUN" -eq 0 ]]; then
(
cd "$directory"
"$@"
)
fi
}

require_command() {
local command_name="$1"
local help_text="$2"
command -v "$command_name" >/dev/null 2>&1 || die "$help_text"
}

take_value() {
local option="$1"
local value="${2:-}"
[[ -n "$value" ]] || die "$option requires a value."
[[ "$value" != -* ]] || die "$option requires a value, not another option."
printf '%s' "$value"
}

while [[ "$#" -gt 0 ]]; do
case "$1" in
--install-dir)
INSTALL_DIR="$(take_value "$1" "${2:-}")"
shift 2
;;
--ref)
REF="$(take_value "$1" "${2:-}")"
shift 2
;;
--skip-models)
SKIP_MODELS=1
shift
;;
--skip-build)
SKIP_BUILD=1
shift
;;
--skip-system-deps)
SKIP_SYSTEM_DEPS=1
shift
;;
--dry-run)
DRY_RUN=1
shift
;;
-h|--help)
usage
exit 0
;;
--)
shift
[[ "$#" -eq 0 ]] || die 'Positional arguments are not supported.'
;;
*)
printf 'Unknown option: %s\n\n' "$1" >&2
usage >&2
exit 2
;;
esac
done

if [[ "$INSTALL_DIR" == \~ ]]; then
INSTALL_DIR="$HOME"
elif [[ "$INSTALL_DIR" == \~/* ]]; then
INSTALL_DIR="${HOME}/${INSTALL_DIR:2}"
fi

[[ "$(uname -s)" == 'Darwin' ]] || die 'This installer supports macOS only.'
[[ "$(uname -m)" == 'arm64' ]] || die 'This release supports Apple Silicon (arm64) only.'

printf 'CapsWriter-Offline-macOS source installer\n'
printf ' Repository: %s\n' "$REPO_URL"
printf ' Ref: %s\n' "$REF"
printf ' Directory: %s\n\n' "$INSTALL_DIR"

if [[ "$DRY_RUN" -eq 0 ]]; then
require_command git 'Git is required. Run xcode-select --install, then retry.'
git check-ref-format --branch "$REF" >/dev/null 2>&1 || die "Invalid Git ref: $REF"
fi

if [[ -e "$INSTALL_DIR" || -L "$INSTALL_DIR" ]]; then
die "Install directory already exists; choose a fresh --install-dir: $INSTALL_DIR"
fi

if [[ "$SKIP_SYSTEM_DEPS" -eq 0 ]]; then
if [[ "$DRY_RUN" -eq 0 ]] && ! command -v brew >/dev/null 2>&1; then
die 'Homebrew is required. Install it from https://brew.sh, then retry.'
fi
run env HOMEBREW_NO_AUTO_UPDATE=1 brew install uv ffmpeg
elif [[ "$DRY_RUN" -eq 0 ]]; then
require_command uv 'uv is required in PATH when --skip-system-deps is used.'
require_command ffmpeg 'ffmpeg is required in PATH when --skip-system-deps is used.'
fi

if [[ "$DRY_RUN" -eq 0 ]]; then
require_command uv 'uv installation failed or uv is not in PATH.'
require_command ffmpeg 'ffmpeg installation failed or ffmpeg is not in PATH.'
fi

run mkdir -p "$(dirname "$INSTALL_DIR")"
run git clone --depth 1 --single-branch --branch "$REF" "$REPO_URL" "$INSTALL_DIR"

run_in_dir "$INSTALL_DIR" uv python install 3.12
run_in_dir "$INSTALL_DIR" uv sync --all-groups --frozen

if [[ "$SKIP_MODELS" -eq 0 ]]; then
run_in_dir "$INSTALL_DIR" uv run python scripts/download_macos_models.py
else
printf '\nSkipping model download. Run this later:\n'
printf ' cd %q && uv run python scripts/download_macos_models.py\n' "$INSTALL_DIR"
fi

if [[ "$SKIP_BUILD" -eq 0 ]]; then
run_in_dir "$INSTALL_DIR" uv run pyinstaller --noconfirm --clean packaging/macos/CapsWriterClient.spec
if [[ "$DRY_RUN" -eq 0 ]]; then
[[ -d "$INSTALL_DIR/dist/CapsWriter.app" ]] || die 'Client build did not produce dist/CapsWriter.app.'
run codesign --verify --deep --strict "$INSTALL_DIR/dist/CapsWriter.app"
fi
else
printf '\nSkipping client build. Run this later:\n'
printf ' cd %q && uv run pyinstaller --noconfirm --clean packaging/macos/CapsWriterClient.spec\n' "$INSTALL_DIR"
fi

printf '\nInstallation steps completed. The installer did not start any process.\n'
printf 'Start the local server in one Terminal window:\n'
printf ' cd %q && CAPSWRITER_MODEL_TYPE=paraformer uv run python start_server.py\n' "$INSTALL_DIR"
if [[ "$SKIP_BUILD" -eq 0 ]]; then
printf 'Then start the client:\n'
printf ' open %q\n' "$INSTALL_DIR/dist/CapsWriter.app"
fi
printf 'On each Mac, grant Microphone, Accessibility, and Input Monitoring permissions to CapsWriter.\n'
Loading
Loading