将台式机/电脑的声音与鼠标实时无线映射到手机、平板、另一台电脑或电视上。自带原生独立桌面应用窗口(双击直接弹窗,无需浏览器):既能作为提供方把本机系统声音、麦克风、鼠标与键盘共享给对端,也能作为使用方把对端能力逐项映射到本机播放或执行。智能路由优先物理网卡(虚拟网卡会被标记、仍可在 UI 手动选择),支持在 UI 上切换通信网卡与设备映射。
- 自带原生独立桌面应用窗口(Wails 原生窗口,无需浏览器):
- 桌面窗口采用 Wails 原生窗口(macOS WKWebView / Windows WebView2 / Linux WebKitGTK):macOS 双击
AnyDevice.app、Windows 双击any_device.exe、Linux 运行any_device-linux-amd64(发布资产名;本地构建产物为bin/any_device_linux_amd64),直接弹出专属独立应用窗口——无浏览器地址栏、无控制台黑窗、无浏览器启动器竞态,关窗即安全退出。 - macOS 的
.app由scripts/build.sh打包(Info.plist带 ATS 本地网络例外与麦克风用途声明),不依赖本机安装 Chrome/Edge;窗口内的 WebSocket 直连ws://127.0.0.1:<port>,页面与/api请求仍走内置 handler。 - 窗口与局域网服务共用同一套 UI 与 API:窗口内的页面请求直达内置服务,局域网端口照常服务手机/平板/其他电脑。
- 服务先于窗口就绪:先完成端口监听再打开窗口,首屏不会因服务未就绪而白屏。
- 也可以通过
-no-ui运行于纯后台守护进程模式。 - 仅开发调试用的无标签
go build会回退为浏览器--app独立窗口模式(Chrome/Edge 独立 profile;macOS 上若没有 Chrome/Edge 则回退webview_go/WKWebView);发布产物一律是原生窗口。
- 桌面窗口采用 Wails 原生窗口(macOS WKWebView / Windows WebView2 / Linux WebKitGTK):macOS 双击
- 对等的发现与映射模型(提供方 + 使用方):
- 📥 接收侧(把对端能力映射到本机):
- 🎧 系统声音:把对端电脑的声音串流到本机/手机播放;
- 🎙️ 麦克风:把对端麦克风拾音映射到本机收听;
- 🖱️ 鼠标 / ⌨️ 键盘遥测:把对端光标坐标、按键事件实时投射到接收端卡片显示。
- 📤 共享侧(把本机能力开放给对端):
- 在「本机共享」设置里逐项开放系统声音、麦克风、物理鼠标与物理键盘,对端发现本机后自行决定是否映射;
- 电脑对电脑时双方各自打开本程序、互相映射对方设备,即可实现完整物理鼠标与键盘同步:移动、左右/中键、滚轮、按键在对端实时还原;
- 滚轮同步:捕获端滚动滚轮,对端即时还原滚动方向与幅度;跨平台按「格/行」归一化(Windows 高精度滚轮/触控板的 1/40 细分增量会累积到一格再滚,不会一格变 120 行)。
- 自动重连:映射保持期间,对端连接失败或中断会按指数退避(1s 起步、30s 上限)自动重连,重连期间卡片显示「连接中」、失败时显示「连接失败」、恢复后显示「已连接」;关闭映射即停止重连。
- ℹ️ 说明:手机端当前为纯接收端(无需安装 App,扫码即用);曾有的「手机当无线触控板反向遥控」界面已随交互重构移除,其遗留接口(
/api/peers/control、/api/mapping/mode、/api/inbound*、/api/outbound*)与整套出站设备管理子系统已一并清理。
- 📥 接收侧(把对端能力映射到本机):
- 智能网卡与 IP 路由解析:
- 采用操作系统路由探测(OS Routing Table Resolution)自动定位真实物理出网网卡,主 IP 选择优先物理网卡,并把 VMware / VirtualBox / WSL / Hyper-V / Docker 等虚拟网卡标记为「虚拟」(列表仍可见、可手动选择)。
- 支持
-ip手动指定对外广播/访问用的局域网 IP(监听始终为:port全接口,不改绑定地址)。
- 秒级设备发现与稳定在线状态:
- 节点启动时立即突发广播(0/400/800ms 三连发,通常 1 秒内全网可感知,不再等满一个心跳周期),并向每个可广播网段(含虚拟网卡网段)发送定向广播,多网卡/虚拟机环境也能可靠互发现;
- 手机/浏览器接入按来源归并(刷新页面不产生重复条目),断开后快速显示离线:浏览器/手机接入约 5 秒内、自动发现的对端约 7–9 秒内;
- 手机或另一台电脑打开控制台时,设备列表不会显示它自己(按查看者过滤)。
- 设备改名跨重启持久化:
- 本机名称与对端备注保存在本地配置文件(默认用户配置目录下
AnyDevice/config.json,可用环境变量ANY_DEVICE_CONFIG指定其他路径),重启不再丢失; - 对端备注优先于对端广播名显示,且不会被后续发现心跳覆盖。
- 本机名称与对端备注保存在本地配置文件(默认用户配置目录下
- 按客户端解码能力自适应(Opus ↔ float32 PCM):
- 对端音频默认传 Opus(码率随发送端负载自适应:平衡档 128kbps / 高负载档 96kbps / 空闲音质档 160kbps,带宽约为 float32 PCM 的 1/24);
- 本机 UI 客户端若没有 WebCodecs
AudioDecoder(macOS Wails 窗口的 WKWebView、Safari),服务端在转发对端音频时自动解码成 float32 PCM 再下发,原生窗口内播放不会因浏览器内核缺解码器而无声; - 有解码能力的客户端(Chrome/Edge/WebView2)仍收 Opus,局域网带宽不受影响(
/api/status的audio_pcm_clients可查看当前声明走 PCM 的客户端数); - 设备卡片上的「接收码率」是对端推流到本机的真实网络字节率(服务端按对端逐条统计,
/api/peers的mapping.rx_*_kbps,不同对端互不混淆):Opus 约 16 KB/s,旧版对端 PCM 约 350–380 KB/s;本机把 Opus 解码成 PCM 播放的部分不计入该数字,所以原生窗口与浏览器看到的是同一个网络码率。
- 延迟 / 音质 / 性能三者自适应(不牺牲其中一项换另一项):
- 播放端自适应抖动缓冲:环形缓冲的预缓冲目标不再固定 100ms,而是 40–200ms 自适应——初始 60ms(低延迟起步),发生欠载(缓冲被抽空)立即 +30ms 并重新预缓冲(先保住不断音),稳定运行满 5s 再每次 -5ms 逐步压低延迟;目标不会越过 [40, 200] 边界。
- 发送端编码参数自适应(
internal/audio/adaptive.go):默认平衡档 128kbps / 复杂度 5;编码队列积压(≥4 帧)、丢帧或「采集→编码完成」延迟连续两次采样偏高(≥35ms)时降档到 96kbps / 复杂度 3(优先保延迟与其它程序性能);持续低负载(≤28ms 且无积压/丢帧)15s 后升档到 160kbps / 复杂度 7(音质优先);切换带 8s 冷却,避免高负载抖动导致参数横跳。Opus 帧长仍为 20ms、采集设备周期未动——自适应用来平衡 CPU 与音质,不用帧长换延迟。 - 发送端
/api/status新增sys_audio_bitrate/sys_audio_complexity/sys_audio_adaptive_level/sys_audio_dropped(及mic_同名)反映当前编码参数、自适应档位与累计丢帧;对端卡片的延迟分项里会显示「对端编码 128kbps · 复杂度 5 · 自适应档 平衡」(旧版对端无该行)。 - 旧版兼容:
audio_stats新增bitrate/complexity/level字段对旧前端安全(未知字段忽略);旧版对端不上报时前端按「无编码信息」处理(以bitrate是否存在门控,level缺省不误判为省 CPU 档)。
- 按需订阅,空闲零空转(真·按需启停):
- 本机 UI 窗口默认不订阅音频/鼠标/键盘流,只有真正开启映射时才通过
set_mapping动态订阅; - 无人订阅时不打开音频设备、不安装系统级输入钩子:应用启动只做设备枚举与服务监听,采集引擎与
WH_MOUSE_LL/WH_KEYBOARD_LL(macOSCGEventTap)在第一个订阅到达时才启动,订阅归零并超过宽限期(音频 8s、键鼠 5s)后自动停止/卸载; - 系统声与麦克风分开订阅:只映射系统声时不会同时打开麦克风采集(Windows 采集 APO/降噪处理会白耗 CPU,麦克风常驻采集也是隐私问题);旧版对端仍按
audio并集语义兼容; - 「本机共享」开关真正门控采集:关闭系统声/麦克风/鼠标/键盘共享后,即使仍有对端订阅也会立即停止对应采集/卸载钩子(此前该开关只改发现广播);
- Opus 编码参数按负载自适应(默认平衡档 128kbps/复杂度 5,高负载降 96kbps/复杂度 3、空闲升 160kbps/复杂度 7,见上一条);音频采集线程保持 miniaudio 默认优先级——实测降优先级对游戏帧率几乎无感,却可能在 CPU 满载时导致采集 overrun(断音),收益/风险不成比例;
- 诊断:
/api/status的sys_audio_engine/mic_audio_engine/mouse_capture/keyboard_capture直接反映各采集引擎与钩子的运行状态,无人订阅时应全为false;日志会输出「无订阅者,停止…采集/卸载…钩子(空闲零空转)」。
- 本机 UI 窗口默认不订阅音频/鼠标/键盘流,只有真正开启映射时才通过
- 纯系统原生驱动(事件驱动捕获):
- 音频:Windows WASAPI Loopback / macOS CoreAudio / Linux(miniaudio:PulseAudio/ALSA;PipeWire 环境经其兼容层)。
- macOS 采集系统声需先安装虚拟回环设备(BlackHole / Soundflower / Loopback Audio 等,按设备名自动识别);未安装时系统声不可用——只提示、不会改采麦克风(可选
ANY_DEVICE_TEST_TONE=1生成调试占位音)。 - 鼠标/键盘捕获参照 Deskflow 的事件驱动模型:Windows 低级钩子(
WH_MOUSE_LL/WH_KEYBOARD_LL)、macOSCGEventTap(只听模式),实时捕获移动、按键、滚轮,未授权时自动回退轮询模式; - 注入采用现代 API:Windows
SendInput、macOSCGEventPost(字符键附带 Unicode 直通、修饰键状态实时跟踪);注入事件自带标记并在捕获钩子中过滤,杜绝捕获/注入回环。 - 输入鲁棒性与防卡键保障:
- 小键盘支持:覆盖数字小键盘(Numpad 0-9、加减乘除、小数点、小键盘回车与 NumLock),Windows 区分小键盘回车与主回车并带扩展键标志注入;
- 按键释放精准识别:鼠标事件携带具体
button标识,彻底解决右键/中键抬起因状态复位被误当左键抬起处理导致按键永久卡死的问题; - 拖拽与系统钩子自愈:macOS 捕获完整覆盖鼠标拖拽事件(
LeftMouseDragged等)并在模拟端还原拖拽状态;钩子因系统超时/交互暂停时自动重新激活(kCGEventTapDisabledByTimeout自愈); - 断开与切换防卡键保护:对端连接断开或本地映射切换关闭时,自动释放所有被跟踪按下的物理按键、修饰键与鼠标按键,杜绝卡键残留。
- 全平台零安装接收端:手机/平板端扫码即用,无需安装额外 App。
| 环境变量 | 作用 |
|---|---|
ANY_DEVICE_AUDIO_DEBUG=1 |
音频链路诊断:捕获端每 2 秒输出 chunk/字节速率与静音标记;转发端输出帧到达统计 |
ANY_DEVICE_KB_DEBUG=1 |
键鼠链路诊断:捕获端事件、执行端收到的事件与键盘模拟执行(均写日志文件) |
ANY_DEVICE_TEST_TONE=1 |
生成 20ms 占位测试音:-mode test-tone 时强制使用;macOS/Linux 未装虚拟回环设备时也会作为回退。默认关闭(不设不出声),供远程端验证音频链路 |
日志位置:原生窗口没有控制台,日志同时写入文件——macOS .app 形态写入用户配置目录 ~/Library/Application Support/AnyDevice/any_device.log(包内文件受代码签名保护,禁止写入);Windows / 裸二进制写入可执行文件同目录的 any_device.log。界面底部(页脚)会显示日志实际路径。
性能 / 与其他程序共存的自查:
- 无人映射时
curl -s http://127.0.0.1:8080/api/status(端口按实际)应看到sys_audio_engine、mic_audio_engine、mouse_capture、keyboard_capture全为false——此时应用不占用音频设备、不安装全局输入钩子; - 开启映射后对应字段变
true,关闭映射后约 5–8 秒回到false(宽限期用于避免映射开关抖动导致设备反复初始化); - 只映射系统声时
mic_audio_subs应为 0、mic_audio_engine应为false(不采麦克风); - 在「本机共享」里关闭某项后,即使对端仍订阅,对应
*_engine/*_capture也应为false(共享开关真正门控采集,而不只是改发现广播); - Windows 上怀疑影响游戏帧率时:任务管理器/Process Explorer 分别看
any_device.exe与msedgewebview2.exe(Wails 窗口的 WebView2 子进程)的 CPU/GPU;再按「只开着不映射 → 映射系统声 → 映射键鼠」逐项对比,并用 PresentMon/CapFrameX 对比帧时间而不是只看平均帧率;把窗口最小化再测可区分「窗口/GPU 合成」与「采集/推流」的影响; - 只作为提供方(把本机声音/键鼠共享给别的设备)时,可用
-no-ui无窗口运行,彻底去掉桌面窗口与 WebView2 的合成开销,再用手机/另一台电脑的浏览器打开http://<本机IP>:8080操作。 - 远端设备/浏览器连接本机 UI 时,连接动作本身不应启动采集:
sys_audio_engine/mic_audio_engine/mouse_capture/keyboard_capture应保持false(现代对端立即发送订阅声明;旧版对端最多 500ms 后按默认全订阅兜底,之后才启动)。
音频延迟显示(判断延迟在发送端还是接收端):
- 映射音频后,对端卡片的声音行会出现延迟 chip(如
≈95ms),悬停可见分项:采集块 + 编码 + 发送(对端上报的发送端耗时)+ 网络(RTT/2) + 播放缓冲 + 输出延迟; 播放缓冲一项会同时给出「实际水位(自适应目标 Yms)」,旁边的欠载 N表示接收端环形缓冲被抽空后重新预缓冲的次数。 - 判断方法:播放缓冲大 / 欠载数字持续增长 → 延迟主要在接收端(本机负载高、预缓冲与重新预缓冲);
采集块/编码/发送分项大 → 延迟主要在发送端(采集缓冲、Opus 组帧/编码或发送队列);
网络 RTT 大 → 网络问题(Wi-Fi/跨网段)。
/api/status的sys_audio_chunk_ms/sys_audio_encode_ms/audio_send_ms是发送端分项的直接来源; 发送端编码档位/码率/丢帧看sys_audio_adaptive_level/sys_audio_bitrate/sys_audio_dropped(对端卡片悬停也能看到上报值)。 - 自适应是否生效:稳定播放 20s 以上,延迟 title 里的「自适应目标」应从 60ms 逐步降到 40ms;若网络/本机偶发卡顿导致欠载,目标会 +30ms 抬高并在稳定后继续下探。
- 说明:该数字是分项估算,不是端到端时钟同步测量;旧版对端不上报采集端分项时只显示网络与本机播放侧;
部分 WebView 不提供
outputLatency时输出延迟按 0 计(总量会偏小)。 - 手动添加 IP 的对端会异步拉取其
/api/self补全能力声明与真实 OS/设备名(此前audio_format为空会触发协议防呆、禁用音频映射)。
音频映射无声时的自查:
ANY_DEVICE_AUDIO_DEBUG=1启动捕获端与播放端——若捕获端显示「捕获已停止增长」或「全零 chunk」比例很高(如全零 chunk 100%)/速率极低,说明捕获设备上确实没有声音在播放(WASAPI Loopback 只捕获默认输出设备上正在播放的声音;macOS 需先装虚拟回环设备,见核心特性);- 检查捕获端设备卡片显示的系统声/麦克风设备名,确认与你实际听声的设备一致;
- macOS 端:音频采集需要「麦克风」授权(按需启停设计下在首次真正开始音频采集时弹窗),键鼠跨端映射需要「系统设置 → 隐私与安全性 → 辅助功能」勾选
AnyDevice.app(未授权时启动有警告提示); - 浏览器场景可在开发者控制台执行
window.__audioDebug()查看 AudioContext 状态、环形缓冲水位/欠载计数、自适应目标(stats.sys.targetMs)与 AudioWorklet 加载情况。
直接双击运行:
- macOS:双击
bin/AnyDevice.app(也可先拖到「应用程序」再启动)。Apple Silicon 用 arm64 包、Intel Mac 用 amd64 包,或直接用 universal 包(一个包通吃,体积约两倍) - Windows 台式机:双击运行
any_device.exe - Linux:
- 推荐 AppImage:
chmod +x AnyDevice-*.AppImage && ./AnyDevice-*.AppImage—— 运行库已打包在内,无需安装任何依赖。 - 或用裸二进制
./any_device-linux-amd64(发布资产名;本地构建产物为bin/any_device_linux_amd64,体积小),需系统运行库(另需libpulse0/libasound2/libjack0,桌面发行版通常已随音频栈安装):sudo apt install libgtk-3-0 libwebkit2gtk-4.0-37(Ubuntu 22.04 / Debian 12)。 Fedora:sudo dnf install gtk3 webkit2gtk4.0(Fedora 41/42 仍提供独立的webkit2gtk4.0源码包)。 若发行版只提供 WebKitGTK 4.1(如 Ubuntu 24.04),请用 AppImage,或安装gtk3-devel webkit2gtk4.1-devel后自行加webkit2_41构建标签(go build -tags wails,desktop,production,webkit2_41)。 - 无图形环境时加
-no-ui以纯服务模式运行,用手机/浏览器扫码接入。
- 推荐 AppImage:
程序启动后会自动弹出独立桌面应用窗口:顶部为「扫码接入 / 添加设备 / 本机共享」三个入口,下方概览区显示本机名称、通信网卡与实时通道状态,点「扫码接入」即可显示连接二维码与本机局域网地址。
多显示器:窗口由 macOS 按系统默认策略放置,局域网服务与二维码地址不受窗口位于哪块屏幕影响;手机扫码访问的是本机局域网 IP。
拿起手机对着电脑屏幕上的二维码一扫,即可连接。手机上打开页面后,「对端设备」卡片里逐项决定把电脑的哪种能力映射到手机:
- 系统声音 → 手机当音箱(电脑扬声器原声);
- 麦克风 → 手机收听电脑麦克风;
- 物理鼠标 / 物理键盘 → 手机卡片实时显示对端光标坐标与按键遥测。
电脑对电脑时,两台都打开本程序,各自在对方的设备卡片上选择要映射的能力,即可双向同步声音、鼠标与键盘。
| 参数 | 默认值 | 说明 |
|---|---|---|
-no-ui |
false |
纯后台/命令行模式运行,不弹桌面应用窗口 |
-port |
8080 |
指定服务端口 |
-ip |
"" |
启动时指定对外广播/二维码使用的局域网 IP(不改监听地址;运行期切换网卡请在 UI 里操作) |
-mode |
auto |
音频捕获模式:当前仅 test-tone 有实际作用(强制占位测试音,仍需 ANY_DEVICE_TEST_TONE=1 才出声);auto/loopback/capture 为兼容保留,无额外行为 |
-list |
false |
列出本机所有可用音频设备并退出 |
-list-nics |
false |
列出本机所有网卡与 IP 地址并退出 |
-device |
-1 |
手动指定系统声设备序号(仅 Windows 系统声生效;macOS/Linux 系统声自动找虚拟回环设备、麦克风固定默认设备;-list 查看序号) |
-no-qr |
false |
关闭终端二维码打印 |
UI 使用 Svelte 5 + Vite(源码在 frontend/),构建产物输出到 internal/web/dist/ 并由 go:embed 打进二进制,无需任何运行时前端服务。
# 一键构建(前端 + macOS 原生窗口 .app + Windows 交叉编译)
./scripts/build.sh
# macOS 架构选择(默认 arm64):
MAC_ARCH=amd64 ./scripts/build.sh # Intel Mac
MAC_ARCH=universal ./scripts/build.sh # 双架构通吃(体积约两倍)
# Linux AppImage(需 libgtk-3-dev libwebkit2gtk-4.0-dev libopus-dev pkg-config;ImageMagick 可选,缺省时图标直接用原图):
ARCH=amd64 ./scripts/build-appimage.sh
ARCH=arm64 ./scripts/build-appimage.sh
# 只改了 Go 代码、前端未动时:
# macOS 原生窗口 -> 重新打包 .app(Info.plist 必须随包,裸二进制窗口内
# WebSocket/麦克风会因缺少 ATS/用途声明而失败):
go build -tags wails,desktop,production \
-ldflags "-s -w -extldflags '-framework UniformTypeIdentifiers'" \
-o bin/AnyDevice.app/Contents/MacOS/any_device main.go
codesign -s - -f bin/AnyDevice.app
# 无标签构建只用于快速调试(浏览器 --app 窗口回退,不是发布形态):
go build -o any_device main.go
# Linux 原生窗口(需 libgtk-3-dev libwebkit2gtk-4.0-dev libopus-dev):
go build -tags wails,desktop,production -o any_device_linux_amd64 main.gomacOS 原生窗口必须运行在
.app包内(AnyDevice.app/Contents/MacOS/any_device);直接把可执行文件拷出包外会丢失 Info.plist,窗口内 WebSocket 会被 ATS 拦截。
依赖打包:音频 Opus 编码在 macOS arm64/amd64 与 Windows x64 使用仓库内 vendored 静态库(
third_party/libopus/),.app与.exe静态链接、运行时不需要安装 Homebrew 或 opus;BSD-3-Clause 许可文本会随.app放进Contents/Resources/third_party/并在仓库third_party/libopus/COPYING保留。Linux 静态链接系统 libopus(构建需libopus-dev),运行期不再需要libopus0;但 GTK3/WebKitGTK 官方不提供静态库且 WebKit 的进程模型要求动态加载,故 Linux 包无法做到全静态,运行库仍需安装(见上文启动说明)。
推送 main 或提交 PR 时,GitHub Actions 会跑完整质量门(gofmt/vet/test/前端检查 + 三平台构建)并上传可下载的安装包(Actions 页面 run 的 Artifacts 区,保留 14 天):
| Artifact | 内容 |
|---|---|
AnyDevice_macOS |
三个 .app:_arm64(Apple Silicon)/ _amd64(Intel)/ _universal(双架构通吃);均含 Info.plist + 图标 + 许可文本 + ad-hoc 签名 |
AnyDevice_windows_amd64 |
any_device.exe(Wails 原生窗口)+ any_device_portable.exe(浏览器壳回退) |
AnyDevice_linux_amd64 |
Linux x86_64 原生窗口二进制(GTK3 + WebKitGTK,需系统运行库) |
AnyDevice_linux_arm64 |
Linux arm64 原生窗口二进制(GTK3 + WebKitGTK,需系统运行库) |
AnyDevice_linux_AppImage |
Linux AppImage(含 GTK/WebKitGTK/opus 运行库,下载即用,约 79 MB) |
版本号以 git tag 为唯一源(仓库内不维护版本文件),推 tag 即触发
.github/workflows/release.yml:三平台并行构建 → 自动创建 GitHub Release
并附上全部安装包。
# 1. 确保 main 已同步且 CI 全绿
git checkout main && git pull --ff-only
# 2. 打 annotated tag(SemVer:破坏性升 major、新功能升 minor、修复升 patch)
git tag -a v1.2.0 -m "AnyDevice v1.2.0"
# 3. 逐个推送并确认(禁止 git push --tags:超过 3 个 tag 时 GitHub 不产生事件)
git push origin v1.2.0
gh run watch $(gh run list --workflow=release.yml --limit 1 --json databaseId --jq '.[0].databaseId')Release 产物:macOS 三架构 zip、Windows 双 exe、Linux AppImage + 裸二进制。
带 -rc / -beta 后缀的 tag 会标记为预发布。
手动演练:
gh workflow run release.yml(只构建产物、不创建 Release)。
go test ./... # 默认测试:含 Opus 编解码往返保真
#(cgo libopus 已静态链接,无需额外 tag)
go test ./internal/audio -run RoundTrip -v # 只跑往返保真(TestOpusRoundTripFidelity:
# 断言解出正弦的幅度/频率与输入一致;曾因把
# float32 缓冲按 int16 传入 opus_encode 而破音)cd frontend
bun install # 安装依赖(包管理器:bun)
bun run dev # 启动 Vite 开发服务器(默认 5173)
# /api 与 /ws 自动代理到本机 8080 的 AnyDevice 服务
bun run build # 构建产物到 internal/web/dist(随后 go build 即可)全新检出后若直接
go build,internal/web/dist只有占位文件,页面为空白——请先执行前端构建(或使用scripts/build.sh)。
多显示器 / 高 DPI:窗口按 macOS 逻辑坐标渲染(Retina 自动 2x),无需额外配置;窗口出现在哪块屏幕不影响服务监听、局域网地址与扫码连接。
macOS 权限提示:
.app采用 ad-hoc 签名(codesign -s -),每次重新构建后系统签名身份会变化,「辅助功能」授权可能失效——到 系统设置 → 隐私与安全性 → 辅助功能 重新勾选AnyDevice.app即可;麦克风授权首次运行时弹窗。