fix(ipad): ship iPad P0 layout/globe fixes, edit-last-input, drop clipboard commands
Adapt typing/voice surfaces for iPad width and height, add the system globe key and last-input editing flow, harden host-only Rime deployment, and remove clipboard voice commands. Bump build to 61.
This commit is contained in:
+27
-2
@@ -7,11 +7,36 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
- **iPad system globe key**: add the system 🌐 key on both iPad voice and typing surfaces — tap to switch to the next keyboard, long-press to open the system input-mode picker. The key uses UIKit's standard all-touch-events input-mode action and the same SwiftUI chrome as adjacent action keys; iPhone relies on its system-provided switch below the keyboard. / **iPad 系统地球键**:在 iPad 语音面与打字面均新增系统 🌐 键——轻点切换下一个键盘,长按打开系统键盘列表。按键采用 UIKit 标准全触摸事件输入模式动作,并与相邻功能键共用同一套 SwiftUI 键面;iPhone 则使用键盘下方由系统提供的切换入口。
|
||||
- **iPad typing layout**: the typing keyboard now adapts to iPad — taller 54 pt key rows, a second-row inset that widens in landscape (40 pt) vs portrait (30 pt), a small grey number overlay (1–0) on the top letter row mirroring iOS, and a content-driven height (≈300 pt on iPad vs 281 pt on iPhone) so the bottom row is never clipped. Metrics live in `TypingLayoutMetrics` and are selected from one controller-owned device-idiom + horizontal-size-class decision. / **iPad 打字布局**:打字键盘现已适配 iPad——键行加高至 54pt,第二行缩进在横屏(40pt)比竖屏(30pt)更宽,首字母行叠加 1–0 小号灰数字(对齐 iOS),并改为内容驱动高度(iPad 约 300pt,iPhone 281pt),底部行不再被裁切。尺寸由控制器统一结合设备类型与水平尺寸类判定。
|
||||
- **Editing toolbar (iPad)**: the typing top bar gains an iOS-style undo / redo / copy / cut cluster on iPad. Undo/redo track the last voice insertion (redo re-applies it); copy/cut read the host selection. Availability is refreshed from `textDidChange` / `selectionDidChange`. / **编辑工具栏(iPad)**:打字面顶栏在 iPad 上新增类 iOS 的撤销/重做/拷贝/剪切簇。撤销/重做跟踪上次听写插入(重做可复原),拷贝/剪切读取宿主选区;可用性随 `textDidChange` / `selectionDidChange` 刷新。
|
||||
- **Edit last input**: long-press the microphone to describe changes to the last verified OSGKeyboard insertion, then preview and replace or append the validated result without exposing unrelated field text. / **编辑上次输入**:长按麦克风口述对最近一次已验证 OSGKeyboard 输入的修改,预览后可替换原文或追加结果,且不暴露无关输入框内容。
|
||||
|
||||
### Removed
|
||||
- **Clipboard voice commands**: remove the iOS keyboard's long-press clipboard capture, persisted resume state, clipboard prompt pipeline, and related pasteboard access; normal dictation, last-input editing, main-app/macOS copy features, and keyboard copy/cut remain available. / **剪贴板语音指令**:移除 iOS 键盘长按读取剪贴板、持久化恢复状态、剪贴板提示词链路及相关粘贴板访问;普通听写、编辑上次输入、主 App/macOS 独立复制功能与键盘拷贝/剪切继续保留。
|
||||
|
||||
### Fixed
|
||||
- **Focused recording chrome and cancellation**: edit mode now aligns its logo and close button to the same outer inset as the normal voice surface. During normal voice startup, recording, and processing, the capsule tabs and translation button are replaced by a high-visibility cancel button; cancelling aborts ASR and polish and discards any late result. Disabled undo and bottom action keys stay hidden so the live microphone remains the unambiguous focus. / **聚焦录音界面与取消入口**:编辑模式的 Logo 与关闭按钮现与普通语音界面使用相同外边距。普通语音从启动、录音到处理期间,胶囊标签与翻译按钮会替换为高可见度取消按钮;取消会终止 ASR 与润色,并丢弃任何迟到结果。不可用的撤销键和底部操作键保持隐藏,让正在工作的麦克风成为明确焦点。
|
||||
- **Repeat edit sessions**: completed edits no longer re-adopt stale recording snapshots and enter an eight-second busy quarantine, so the next long-press starts ASR immediately with the latest applied text. The edit surface enlarges its comparison swipe area, shows live ASR text and an audio-level waveform instead of status copy, widens the centred edit microphone into a three-times-wide capsule, and keeps the green “hold to edit” hint visible for ten seconds. / **连续编辑会话**:已完成的编辑不再重新接管过期录音快照并进入八秒忙碌隔离,下一次长按会立即基于最新已应用文本启动 ASR。编辑界面同时扩大对比滑动热区,以实时 ASR 文本与音量波形替代状态文案,将居中的编辑麦克风加宽为原宽度三倍的胶囊按钮,并将绿色“长按编辑”提示延长至十秒。
|
||||
- **Faster first mic and stable edit review**: each host process performs one bounded post-PiP audio health check (real first-frame proof, one soft-dead rebuild, then release), while touching the main mic primes the same single-flight capture for the imminent tap/hold utterance. Edit review now keeps its Flow identity until confirm/close, deduplicates final results by utterance+revision, and uses native horizontal paging; a simulator XCUITest verifies left/right swipes over the text area. / **更快首录与稳定编辑预览**:每个宿主进程在 PiP 就绪后执行一次有界音频健康检查(真实首帧证明、软死时最多重建一次、随后释放),手指按下主麦克风时则预热同一个单飞 capture,供即将成立的短按/长按 utterance 直接接管。编辑预览在确认/关闭前持续保留 Flow 身份,按 utterance+revision 去重结果,并改用原生横向分页;模拟器 XCUITest 已验证文字区域左右滑动。
|
||||
- **Unified edit-mode recording**: dictation and last-input editing now share one `FlowUtteranceRequest` start transaction for host handoff, microphone activation, recording confirmation, stop, timeout, and ASR; edit mode no longer pre-allocates a ghost utterance or runs its own confirmation polling. The initiating long press is consumed before switching views, preventing a replayed finger-up from sending `stopRecording`. / **统一编辑模式录音**:普通听写与编辑上次输入现共用同一个 `FlowUtteranceRequest` 启动事务,统一处理宿主交接、麦克风激活、录音确认、停止、超时与 ASR;编辑模式不再预创建幽灵 utterance,也不再维护独立确认轮询。切换界面前先消费发起编辑的长按,避免抬手被重放为 `stopRecording`。
|
||||
- **iPad keyboard width**: the typing keyboard was clamped to a 700 pt centred column on every device, leaving 67 pt of dead space per side on an 11" iPad in portrait and 247 pt in landscape (51% of the screen on a 13" in landscape), so no key sat where the system keyboard puts it. The cap was a voice-surface ergonomics constraint (`contentMaxWidth`) that the key grid had inherited; it is now voice-only (`voiceContentMaxWidth`) and the typing grid spans the full host width. Letter keys grow from 61 pt to 75 pt wide on an 11" iPad in portrait. / **iPad 键盘宽度**:打字键盘此前在所有设备上都被钳制为 700pt 居中列,11 寸 iPad 竖屏每侧留白 67pt、横屏 247pt(13 寸横屏占屏幕 51%),导致没有一个按键落在系统键盘的位置上。该上限原是语音面的可达性约束(`contentMaxWidth`),却被键盘网格一并继承;现已改为语音面专用(`voiceContentMaxWidth`),打字网格铺满宿主宽度。11 寸 iPad 竖屏字母键宽由 61pt 增至 75pt。
|
||||
- **iPad keyboard height ignored orientation**: `contentHeight` hardcoded `isLandscape: true` while orientation only fed the second-row inset, so portrait and landscape resolved to an identical 300 pt. Height is now driven by available width (394 pt at iPad landscape widths vs 300 pt in portrait, against roughly 398 pt / 313 pt for the system keyboard), which also fixes `traitCollectionDidChange` never firing on iPad rotation — both orientations are `regular`, so the resize is now picked up in `viewDidLayoutSubviews`. / **iPad 键盘高度不随方向变化**:`contentHeight` 硬编码 `isLandscape: true`,而方向仅用于第二行缩进,横竖屏因此解析出完全相同的 300pt。现改由可用宽度驱动(iPad 横屏宽度下 394pt、竖屏 300pt,系统键盘约为 398pt / 313pt),同时修复 iPad 旋转不触发 `traitCollectionDidChange` 的问题——横竖屏同为 `regular`,改在 `viewDidLayoutSubviews` 捕获尺寸变化。
|
||||
- **Second-row indent no longer a fixed constant**: the ASDF row used a hardcoded 30 / 40 pt inset tuned for a 700 pt column, which stops reading as a deliberate indent once the grid fills an iPad. It is now derived so second-row keys are exactly as wide as first-row keys, leaving a half key at each end — the system keyboard's own rule, and correct at any width. / **第二行缩进不再是固定常量**:ASDF 行原用为 700pt 列调校的 30 / 40pt 硬编码缩进,网格铺满 iPad 后已无法读作有意的缩进。现改为推导值,使第二行键宽与第一行完全一致、两端各留半个键——即系统键盘自身的规则,在任意宽度下都成立。
|
||||
- **iPad bottom row gains comma / period**: filling the width turned the phone row's 50% centre fraction into a 663 pt space bar on a 13" iPad in landscape. iPad now uses a six-slot row `[globe · 123 · , · space · . · return]` (9/13/9/38/9/22), spending the extra width on keys the way the system keyboard does; space settles at 432 pt on an 11" in landscape against roughly 430 pt for the system. Punctuation routes through the engine, so a pending composition commits first. / **iPad 底行新增逗号/句号**:铺满宽度后,手机版 50% 的中央比例会使 13 寸 iPad 横屏空格键达到 663pt。iPad 现采用六槽底行 `[globe · 123 · , · space · . · return]`(9/13/9/38/9/22),像系统键盘那样把多出的宽度用于增加按键;11 寸横屏空格为 432pt,系统约为 430pt。标点经由引擎提交,未上屏的编码会先行确认。
|
||||
- **Voice surface matches the typing surface**: the voice surface also fills the host width, and both surfaces now resolve to one content-driven height, so switching between them no longer resizes the keyboard (a 113 pt jump on an iPad in landscape). Surplus height is parked above the action cluster, keeping its keys on the typing bottom row's baseline. Its bottom row uses a flatter iPad split (10/24/40/26), widening the primary return key without letting the phone's 50% centre fraction hand it ~577 pt at full width. / **语音面与打字面对齐**:语音面同样铺满宿主宽度,且两面现在解析出同一个内容驱动的高度,互相切换不再改变键盘尺寸(iPad 横屏原有 113pt 跳变)。多余高度置于操作簇上方,使其按键与打字面底行保持同一基线;底行在 iPad 上采用更平缓的比例(10/24/40/26),加宽主要回车键,同时避免手机版 50% 的中央比例让其在铺满时达到约 577pt。
|
||||
- **Layout metrics are now testable**: typing size decisions moved from `TypingRootView` (extension target) into `TypingSurfaceMetrics` (shared), so the UIKit height constraint and the SwiftUI grid provably read the same source and unit tests can cover them without the extension. / **布局尺寸可测**:打字面尺寸决策由 `TypingRootView`(扩展 target)移至 `TypingSurfaceMetrics`(共享层),UIKit 高度约束与 SwiftUI 网格可证明地读取同一数据源,且单测无需依赖扩展即可覆盖。
|
||||
- **Chinese input never initializes**: the keyboard could show 「请先打开 OSGKeyboard 完成输入法初始化」 indefinitely even after opening the app, because Rime deployment was scheduled only as opportunistic warmup — a 45 s delay that additionally required the app to stay foregrounded and to clear a memory gate, and silently dropped the work otherwise. Deployment is now also run deterministically at the moments the user is actually waiting on it: during the onboarding "Add Keyboard" step (with an inline progress / retry status row), immediately on onboarding completion, and on demand via a new `osgkeyboard://deployrime` deeplink. / **中文输入始终无法初始化**:即使已打开主 App,键盘仍可能长期显示「请先打开 OSGKeyboard 完成输入法初始化」——因为 Rime 部署只作为机会性预热调度:延迟 45 秒,且要求 App 保持前台并通过内存闸门,否则静默放弃。现在在用户真正等待结果的时机改为确定性执行:引导「添加键盘」步骤内(附行内进度 / 重试状态)、引导完成时立即执行,以及通过新增的 `osgkeyboard://deployrime` deeplink 按需触发。
|
||||
- **Keyboard recovers without a restart**: a keyboard already showing the setup error now retries engine preparation when the host posts its post-deployment App Group notification, and the error text itself became a tappable jump into the host app (only for failures host deployment can actually fix). Previously the error persisted until the user switched surfaces or relaunched the keyboard. / **键盘无需重启即可恢复**:已显示初始化错误的键盘,会在宿主发出部署完成的 App Group 通知后自动重试引擎准备;错误文案本身也变为可点击跳转(仅对确实能由宿主部署修复的故障开放)。此前该错误会一直保留,直到用户切换面板或重新拉起键盘。
|
||||
- **Rime deployment stays in the host app**: the shared installer now rejects every `.appex` process, and personal-dictionary callbacks skip deployment when invoked by the keyboard extension. Returning users trigger idempotent deployment immediately when the host opens instead of waiting for a 45-second opportunistic warmup. The host now clears its heavy-work flag before notifying the keyboard, so the extension's automatic retry cannot lose the only completion event to stale busy state. / **Rime 部署仅限主 App**:共享安装器现会拒绝所有 `.appex` 进程,个人词库回调若来自键盘扩展也会跳过部署。老用户打开主 App 时立即触发幂等部署,不再等待 45 秒的机会性预热;宿主还会先清除重任务标记再通知键盘,避免扩展自动重试因读到过期忙碌状态而错失唯一完成事件。
|
||||
- **iPad globe key at bottom-left**: the system 🌐 key now lives at the bottom-left of both iPad surfaces — voice (`KeyboardRootView.micActionRow`) and typing (`TypingRootView.typingKeySurface`) — while iPhone removes the duplicate custom key and redistributes its three remaining actions across the row. UIKit owns the iPad key's complete native tap / long-press event chain, and its visible chrome matches adjacent keys. / **iPad 地球键移至左下角**:系统 🌐 键现位于 iPad 语音面与打字面的最左下角;iPhone 删除重复的自定义地球键,并将剩余三个操作键重新铺满底行。iPad 地球键由 UIKit 完整接管原生轻点/长按事件链,可见键面与相邻按键保持一致。
|
||||
- **iPad typing height**: `TypingRootView.totalHeight` is now a function `totalHeight(isIPad:)`; `KeyboardViewController` resolves one layout mode from device idiom plus horizontal size class and publishes it to SwiftUI, keeping compact iPad multitasking on phone metrics and wide iPhones off iPad metrics. Trait changes refresh the height constraint so UIKit and SwiftUI stay aligned. / **iPad 打字高度**:`TypingRootView.totalHeight` 改为函数 `totalHeight(isIPad:)`;`KeyboardViewController` 结合设备类型与水平尺寸类统一解析布局模式并发布给 SwiftUI,使 iPad 紧凑分屏使用手机尺寸、宽屏 iPhone 不误用 iPad 尺寸;尺寸类变化时同步刷新高度约束,确保 UIKit 与 SwiftUI 始终一致。
|
||||
- **iPad sidebar breathing room**: on the iPad / regular-width sidebar, the gap between the brand logo and the first menu item is roughly doubled (brand header bottom padding `Spacing.md` → `Spacing.xxxl`), and each sidebar row is taller with larger text (font 13 → 15, vertical padding 7 → 12) so rows hit the Apple HIG 44pt touch target. / **iPad 侧栏留白**:iPad / 横屏规则宽度侧栏里,logo 与首个菜单项之间的间距大约翻倍(`brandHeader.bottom` 由 `Spacing.md` 改为 `Spacing.xxxl`),每行菜单项更高、字号更大(字号 13 → 15、垂直内边距 7 → 12),行高达到 Apple HIG 44pt 触摸目标。
|
||||
- **iPad sidebar row alignment**: sidebar titles started at a different x on every row because `Label`'s default style sizes the icon to each SF Symbol's intrinsic width, which ranges from 13 pt (`character.book.closed`) to 17.5 pt (`house`) across this menu — pushing 「键盘」 5.2 pt right of 「词库」. A `LabelStyle` now reserves a uniform 22 pt icon column (the alignment `List` provides for free and this hand-rolled `VStack` sidebar lacked), and the selected row's corner radius moves off a stray hardcoded 7 pt onto the design scale at `Radius.medium` (12 pt). / **iPad 侧栏行对齐**:侧栏各行文字起点不一致——`Label` 默认样式按每个 SF Symbol 的固有宽度排布图标,本菜单内该宽度从 13pt(`character.book.closed`)到 17.5pt(`house`)不等,使「键盘」比「词库」右移 5.2pt。现由 `LabelStyle` 预留统一的 22pt 图标列(`List` 自带、而手写 `VStack` 侧栏缺失的对齐),选中行圆角也从游离的硬编码 7pt 收回设计标度 `Radius.medium`(12pt)。
|
||||
- **iPad Home hint position**: the keyboard-setup hint (and other flow-session extras) on the iPad / regular-width home view used to render below the usage-stats cards, burying the most actionable guidance beneath the stats. The hint now sits right after the hero header and above the stats, matching the phone layout, so it is the first thing a user notices at the top of the page. / **iPad 首页提示位置**:iPad / 横屏规则宽度首页的「去系统设置添加键盘」提示(以及其他 Flow 会话附注)原本位于使用统计卡片之下,最关键的引导被压在统计之下。现已将其移到 hero 标题与统计卡片之间,与手机端布局一致,使其位于页面顶部最显眼处。
|
||||
- **Polish never answers a question draft**: dictating 「你能听到我说话吗?」 could come back as 「能听到,你说。」. The never-answer boundary and the question guard now ship with every style, every intensity, and custom packs (heavy fun styles previously bypassed both), question detection covers A-not-A forms such as 「你在不在」, and a deterministic validator rejects any result that stops asking the draft's question and falls back to a local clean of the user's own words. A 800-request live matrix across 10 styles × 2 intensities × 20 short-to-long phrasings now delivers zero answers. / **润色不再代答问句**:口述「你能听到我说话吗?」可能被润色成「能听到,你说。」。现在「不可协商边界」与问句守卫覆盖全部风格、全部强度与自定义风格包(此前重度趣味风格会绕过两者),问句识别补齐「你在不在」等正反问句式,并新增确定性校验:结果若不再是问句则丢弃,回退为用户原话的本地清理。10 风格 × 2 强度 × 20 种长短句式、共 800 次真实模型压测下代答为 0。
|
||||
- **Polish diagnostics**: `polish.config` now records style, intensity, sampling temperature, and which safeguard layers reached the model, and `polish.skippedLLM` records locally short-circuited utterances. / **润色诊断**:`polish.config` 记录风格、强度、采样温度以及本次真正发给模型的防线层级,`polish.skippedLLM` 记录本地短路的语句。
|
||||
- **Clipboard intent continuity**: one persisted intent now survives paste consent and host cold-start, automatically begins recording when ready, and can always be cancelled; preparing no longer swallows taps and recording accepts either tap or hold to finish. / **剪贴板意图连续性**:单一持久化意图跨越粘贴授权与宿主冷启动,就绪后自动开录且始终可取消;准备态不再吞点击,录音中点按或长按均可结束。
|
||||
- **Clipboard "reply in a language" command**: "帮我用英文进行回复" (reply in English) no longer collapses into a plain translation of the clipboard. The command contract now treats "reply in X" as one action where the language only picks the reply's wording, adds a reply-in-English few-shot, and a deterministic reply-intent guard hard-injects the reply directive whenever the instruction asks to reply. / **剪贴板「用某语言回复」指令**:「帮我用英文进行回复」不再塌缩成只把剪贴板译成英文。指令契约现把「用 X 语言回复」视为一步动作(语言只决定回信用什么语言写),新增「用英文回复」反例,并在检测到回复意图时确定性地强制注入回复指令。
|
||||
|
||||
## [1.6.6] - 2026-08-08
|
||||
|
||||
|
||||
Reference in New Issue
Block a user