1.8.1 在 App Store 上的更新说明是关于虚拟鼠标的四行字。它背后的这个构建是九天的 工作量,也是 iPhone 与 iPad 应用今年最大的一次跨越:带可选 AI 层的语音输入、按真实 排列数据重写的键盘、第三种终端协议的客户端、Mac 的分享链接,以及一套为了熬过自己 未来版本而设计的同步格式。这是它的长版本。
它会听了
在手机上敲 shell 命令,是在手机上用终端最难受的部分。所以 1.8.1 加了语音输入, 用大约一周时间分阶段做出来。
第一阶段是最要紧、也最不花钱的那部分:按住麦克风说话,识别在设备本地进行。免费、 离线,什么都不离开这台手机。听到的内容会作为可编辑文本落在语音板里,等你觉得对了 再注入终端 — 而不是在那之前。
第二阶段针对的是它看着不对的那些时候。录音按片段保存,所以你可以裁掉一段、只对那 一段重新识别,而不必把整句重说一遍;历史记录会留着过往录音,可以试听,也可以再试 一次。在 iPad 上,语音板像透明键盘一样悬浮、可缩放 — 因为在大屏幕上,一块 钉死在错误位置的面板本身就是个问题。
如果你愿意,它还会想
识别给你的是词。一条命令需要的不止是词 — 它需要参数顺序对,也需要不该有空格 的地方没有空格。1.8.1 里的 AI 层完全可选,也和识别完全分开。
自带提供商:支持 OpenAI、Claude、Gemini、Kimi 和 GLM 的 API 格式,你设一个对话模型, 如果这家提供商有转写模型,也一并设上。Claude 只做对话、没有音频 API,所以在它那里 录音会回落到本地识别,而不是直接失败。提供商密钥留在本机 Keychain,并被刻意排除在 同步之外。
接上提供商之后,提示词模式会把你说的话变成整洁的文本,或者一条可以直接跑的命令; 还有一个附带终端上下文的选项,让模型在回答前真正看到屏幕上的内容。另外有一个对话 面板,于是那些你本来要开一个浏览器标签页去问的问题,可以就在会话旁边问。
对不想自己管理 API 密钥的人,登录之后可以使用官方云端模型,用通过应用内购买获得的 点数支付,点数在你的各台设备之间共享。无论走哪条路,本地识别始终免费,而且它是默认项。
有了真实排列的键盘
到 1.8 为止,悬浮键盘一直是一套写死的美式排列。1.8.1 把它改成数据驱动,并一次给出 六种:美式英语、英式英语、日语 JIS、法语 AZERTY、德语 QWERTZ 和 西班牙语,在设置里和键盘的选择一起切换。
数据驱动的排列需要一种写死排列不需要的测试,因为现在一套排列就是一张有人手打出来的
表。我们加了一个字符可达性测试 — 对每一种排列,你期望的每一个字符,是否真的
能通过某种按键组合打出来?它立刻找出了两个真实的 bug:AZERTY 上根本没有
*,而 JIS 排列把 fn
锚在了错误的键上,导致整排 F 键都偏了。这两个,用英语测试的人谁也不会碰上。
按键上方现在有一条候选栏,背后是一个针对终端上下文和常用命令调过的词建议器;麦克风 则挪到了顶栏,有两种用法:轻点切换到语音板,长按就是按住说话,直接把结果送进候选栏。 这个长按手势比看起来难做 — SwiftUI 那个最顺手的长按接口没法告诉你手指什么时候 抬起,我们把它单独写成了一篇讲按住录音按钮的文章。
第三条路:Eternal Terminal
让会话穿过火车隧道还活着的是 Mosh,而它跑在 UDP 上。相当多的企业和酒店网络只放行
TCP,别的一概不放,这让那些用户手里只剩下纯 SSH,会话完全无法保持。
Eternal Terminal
在 TCP 上解决同一个问题,而且很多人本来就跑着
etserver。
我们在前一周写过这个客户端;
1.8.1 就是它落地的那个构建。
它是按一个实验性传输该有的方式进来的:先是一个隔离的 Rust crate,别的东西一概不能 依赖它;然后经由 FFI 接进 Swift 应用,藏在开关后面;等它真能用了,再补上真正的主机 编辑器界面、设置开关、能说清真实原因的错误提示,以及翻译。之后是针对失败路径的两轮 评审 — 那正是一个网络客户端最容易悄悄出错的地方。
它仍然默认关闭。在「设置」里打开 ET 传输,它就会作为第三种连接方式出现在 SSH 和 Mosh 旁边。有一点值得提前想到:和 Mosh 的 UDP 一样,ET 的数据连接是从设备直接连到 etserver 端口的,不会经过跳板机转发。
能熬过自己未来版本的配置
这段时间里,Mac 端代理长出了自己的主机列表和终端,这意味着手机和 Mac 需要就「主机 是什么」达成一致。1.8.1 引入了一个共享包,装着主机与脚本的规范化结构, 两端共用,另外还有一个配置同步客户端和驱动它的协调器。
让两个各自独立更新的应用共用一份结构,会带来一个特定的风险,我们也确实撞上了:较旧的 客户端解码到一个它从没听说过的值,然后把整条记录写回去,把它毁掉。修法 — 保留 原始值的枚举、一个装未知字段的口袋,以及钉住这份契约的测试 — 写在《前向兼容的 Codable》里。 在这个构建里,这套做法先用在主机上,随后铺到其余每一个参与同步的模型,包括已保存桌面 上按条目保留的未知字段。
什么同步、什么不同步,一如既往,且写得明明白白:主机、脚本、驱动器映射、模板和设置会 同步到服务器上你的账号下。密码、SSH 私钥和它们的口令永远不离开设备 — 它们住在 iOS Keychain 里,可以选择经由 iCloud Keychain 端到端地流转,服务器上没有任何东西可供 攻破。
把一台 Mac 分享给别人
1.8.1 补上了分享链接的查看端:一个「分享给我的」列表,一条用分享而非配对串连接的传输 支路,以及对需要机主批准才能进入的会话的处理。这些错误提示是刻意做得具体的 — 链接过期、链接被撤销、并发席位已满、账号不在允许列表里、连接时间不在预约窗口内, 每一种都会说清是哪一种,因为当修复办法取决于原因时,一句「无法连接」毫无用处。
同样在这里:断开一个远程桌面会保留它的标签页,就像 SSH 连接一直以来那样,于是你可以 从覆盖层上重连,而不必把整件事重新设置一遍。
虚拟鼠标有了落脚的地方
这就是 App Store 更新说明里写的那部分。鼠标按键原本可以拆开摆放,这很灵活,也意味着 它们从来不太在你的手所在的位置。现在它们可以停靠:屏幕底部的一整块面板,向左或向右 滑到你握着设备的那一侧,屏幕键盘一打开,它还会抬到键盘上方。
停靠还顺手治好了放大镜最坏的毛病。自由浮动时它必须躲开屏幕边缘,于是它会在你没料到 的时候移动,放大出来的画面也和光标对不上。停靠之后,它就正好待在光标上方,不再乱跑。 另有两个相关修复:手指从屏幕边缘滑出去,不再会让光标跳一下或者留下一个卡住的按键 — 被取消的触摸现在会当作被取消的触摸来处理 — 而 ⌘、⌥ 和 ⌃ 也不会再偶尔 保持按下,把你的下一次击键变成一个意外的快捷键。
在别的应用里也能听见的响铃
当某个终端响铃、而那个会话不是你正在看的那个时,iOS 现在会推送一条通知。在一次漫长的
构建后面接上
printf '\a',
跑完时手机就会告诉你。你正在看的那个会话里的响铃仍然保持安静,诀窍全在这里。
值得一提的杂项
- 键盘工作收尾时做了一次完整的本地化审计:639 个键全部检查,零重复。
- 评审带来的两个正确性修复:解码器状态里的一处竞态,以及主机被删除后可能被遗留的 Keychain 密码。
- 1.8 起步的 Opus 音频链路在这里完工 — 一个把任意 PCM 变成 20 ms 帧的缓冲编码器、查看端的播放,以及从音频缓冲列表里正确读取交错 PCM 的修复。
- 测试目标的部署版本下调到 iOS 17.0,与应用本身一致,这样应用能跑的地方测试都能跑。
这些全都是免费的。设备本地的语音识别也包括在内。
阅读其他语言版本: English · 中文 · 日本語 · Español · Français · 한국어 · Українська
关于应用本身的更多内容见 SharkTTY 使用指南,面向用户的简短更新说明见新功能,最快找到我们的方式是反馈板。返回开发博客。