Repository navigation
feat: iPadのメニューバーとキーボードショートカットに編集画面の操作を載せる - #125
Conversation
iPadのメニューバーとキーボードショートカットから、新規作成・復元・ .knit/PNG/PDFの書き出し・元に戻す/やり直す・使い方を使えるようにする。 ネイティブのコマンドはknittingEditorNativeCommandイベントでWeb側の 既存の操作を呼び、元に戻す/やり直すの可否はcommandStateで受け取る。 UIApplicationSupportsIndirectInputEventsをYESにする。 Refs #80 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
K0mork
left a comment
There was a problem hiding this comment.
レビュー(4ce38fd、CI run 37534057002)
CIの結果
ios (iPad (A16)) が失敗し、ci-gate も失敗しました(deploy・smoke はスキップ)。ほかのジョブ(web、ios_web、release_archive、app_update の iPhone 17・iPad (A16)、ios (iPhone 17)、app_store_docs、CodeQL)は成功しました。
ios (iPad (A16)) の「Run iOS tests」の内訳:
EditorCommandsTests(6件)とNativeBridgeMessageTestsはすべて成功。- 既存の
KnittingEditorUITestsは、iPad でもすべて成功(testTwoFingerGestureDoesNotDrawOnBoard、testUndoAndRedoRestoreBoardEditsを含む)。間接入力を有効にしたことによる、XCUITest のタップ・2本指の操作への影響は見当たりません。 KeyboardCommandUITestsは3件のうち2件が、再試行を含めて2回とも失敗しました。testFileAndHelpShortcutsOpenEditorActions: 成功。testRestoreShortcutOpensBackupPicker: 97行目。⌘O の後に Document Picker(Cancel)が出ない。testUndoAndRedoShortcutsStepBoardHistoryOnce: 115行目。⌘Nでダイアログが出ない。
原因の見立て(変更が原因): どちらの失敗も、「保存」ボタンが現れてから約1〜1.5秒でキーを送っています。この時点ではまだ webReady を受けておらず、受けた後もメニューが作り直されていないため、項目は .disabled です。そのためキーは WebView へ落ち、Web 側は ⌘O・⌘N を処理しないので捨てられます。成功した1件目は、アプリの初回起動に約20秒かかり、キーを送るのが遅かったので間に合いました。失敗時のスナップショット(45秒後)では、編集画面は準備済みでダイアログも出ていません。キーが失われたのは送った瞬間だと考えられます。手元で1度だけ起きた testUndoAndRedo… の失敗(⌘Z の後も2個のまま)も、メニューの有効・無効の切り替えが遅れることによる同じ種類の競合だと考えられます。
直し方の案:
launchEditor()で、editorLoadingOverlayが消えるのを待つ。- そのうえで、最初のショートカットは期待する画面が出るまで送り直す(または、メニュー項目が有効になってから送る)。
- ⌘Z・⇧⌘Z は、
commandStateがメニューに反映されるまでの遅れを考えて同じように待つ。
製品の動きとしては、起動直後の短い間ショートカットが効かないだけなので、許容できる範囲だと考えます。
レビューで見つけた点
- 【中・要確認】入力欄の文字の取り消し
CommandGroup(replacing: .undoRedo)で、UIKit の標準のundo:・redo:のキー割り当てが無くなります。- 盤面に戻せる編集が無い(
canUndo=false)ときに入力欄で ⌘Z を押すと、キーは WKWebView のkeydownへ届きます。useHistoryShortcutsは入力欄では何もしないので、iOS の WebKit がkeydownだけで文字を取り消すかどうか次第で、何も起きなくなるおそれがあります。 - 盤面に戻せる編集があるときは
document.execCommand('undo')に頼りますが、ユニットテストはこれをモックしているだけで、実際の WKWebView では確かめていません。 - 案: 新しい編み図のダイアログで文字を打ってから ⌘Z で戻ることを、履歴が無いときとあるときの両方について UIテストで確かめる。あるいは、入力欄にフォーカスがある間は Web から
canUndo/canRedoを true として送り、取り消しの経路をメニューの1本にまとめる。
- 【中・実機要確認】トラックパッドのピンチ
UIApplicationSupportsIndirectInputEvents=YESにすると、トラックパッドのピンチはタッチ2本として届かなくなります。- 盤面の拡大は
BoardCanvas.tsxの、ctrl・meta 付きのwheelに頼っています。iPadOS の WebKit がピンチを ctrl 付きのwheelで送らない場合(Safari は gesture イベント)、盤面ではなくページ全体が拡大されます(viewport はmaximum-scale=5)。 - #21 で、この点をはっきり確かめる項目にしてください。
- 【低】Web 版との細かな違い
runNativeCommandは、処理中(busy)の元に戻す・やり直すを止めます。Web 版のuseHistoryShortcutsは止めないので、コメントとNATIVE_BRIDGE.mdの「Web 版と同じ」とは少し違います。nativeCommands.tsのisEditableとhooks.tsのisEditableTargetで、color・range の input とSELECTの扱いが違います。どちらかに揃えるか、違う理由を書いてください。
- 【低】保存画面などを出している間の表示
- 保存画面・共有シート・Document Picker を出している間も、メニュー項目は選べる表示のままですが、
performでは何も起きません(黙って無視されます)。
- 保存画面・共有シート・Document Picker を出している間も、メニュー項目は選べる表示のままですが、
- 【低・利用者の判断】キーの割り当て
- ⌘S は押すたびに保存画面が出ます。自動保存のアプリなので、Mac の癖で押す人には煩わしいかもしれません。
- ⌘P は慣習では「印刷」です。
- ⇧⌘H はシステムと衝突しないと見ています(⌘H はシステムが使います)。
replacing: .newItemは、複数ウィンドウ(#28)に対応するときに「新規ウィンドウ」を消すことになるので、その際に見直しが必要です。
- テストの不安定さ: 上の CI の失敗と同じ原因です。メニューの有効・無効の反映を待たずにキーを送ると、取りこぼします。
確かめて問題が無かったこと
project.pbxprojは、xcodegen generateで生成し直したものと完全に一致しました。- 操作の名前は Swift と Web で一致しています。使い方ページへの遷移先は、既存のリンクと同じ
/guide/です。使い方ページの表示中と読み込み中はwebContentReadyで止まります。 NATIVE_BRIDGE.mdの記述は実装と合っています。docs/dev-log/のエントリは書式どおりで、手元の確認と CI に任せた確認を書き分けています。- 差分・PR本文・コミットメッセージに機密情報はありません。
Refs #80とラベルenhancementがあります。
満たしていない #80 の完了条件
- ⌘キーの長押しで一覧が出ることの確認。
- トラックパッド・マウスでの描画・スクロール・ピンチの確認。
どちらも実機(#21)で確かめる予定なので、Closes ではなく Refs にしているのは適切です。なお #80 には needs-device ラベルがありません。
… tests - Send canUndo/canRedo as true while a text field has focus so Cmd+Z and Shift+Cmd+Z always go through the menu and undo typing via execCommand. - Accept undo/redo while busy like the web shortcuts, and share isEditableTarget with useHistoryShortcuts. - Disable menu items while the save alert, share sheet, or document picker is presented. - Wait for the editor to be ready and resend shortcuts until the expected screen appears in KeyboardCommandUITests; add a text-field undo test. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
レビューの指摘と最初のCIの失敗を fd5d1ef で直しました。 直したこと
CI(run 37538823264): web、ios_web、ios (iPad (A16))、ios (iPhone 17)、app_update (iPad (A16))、app_update (iPhone 17)、release_archive、app_store_docs、CodeQL、ci-gate がすべて成功しました(deploy と smoke はPRのためスキップ)。iPadでは 残っていること
|
Keep both sides in WebViewContainer.swift: the launch colors and loading style from #118, and the menu command state and native UI flag from #125. The share sheet completion now clears the native UI flag and reports the finished export for the review request (#121). Regenerate project.pbxproj with XcodeGen. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The app now has a Japanese localization (#119), so the Document Picker's close button has the label キャンセル instead of the Cancel identifier on a Japanese simulator. Match either one in the ⌘O shortcut test, and record the merge from main in the development log. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With a localization in the app (#119), the Document Picker's close button loses the Cancel identifier and takes a label in the device language, so the English CI simulator failed too. Also accept the Cancel label and the picker's Browse View (Picker) identifier. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
マージ後の確認です。
|
変更内容
iPadのメニューバー(iPadOS 26以降)とキーボードショートカットから、編集画面の操作を使えるようにしました(#80)。iOS版だけの変更です。
.commands(新規ios/App/EditorCommands.swift)で項目を定義しました。選ばれたら、Swiftは操作の名前をknittingEditorNativeCommandイベントでWebViewへ送るだけです。操作の中身は画面のボタンと同じWeb側の処理(新規ios/Web/src/nativeCommands.ts)で、Swiftには複製していません。{"type":"commandState","canUndo":…,"canRedo":…}で知らせる編集履歴に合わせて、画面のボタンと同じ条件で選べます。どの項目も、編集画面が準備できるまでと、使い方ページの表示中は選べません。Info.plistにUIApplicationSupportsIndirectInputEvents(YES)を足しました。ios/docs/NATIVE_BRIDGE.mdの「メニューバーとキーボードショートカット」に書きました。packages/editor-core/ui/inputEnvironment.tsのnoteHardwareKeyboard()(とpackage.jsonのexports)だけを足しました。Web版の動きは変わりません。決めたこと
.knitで保存」、⌥⌘Sは全データ、⌘PはPDF(印刷向けの出力のため)、⇧⌘EはPNG(Export)にしました。undo:・redo:はWeb側の編集履歴に届かないためです。選べる状態のメニュー項目にキーが合うとUIKitが受け取り、ページのkeydownへは届かないので、Web側のキー操作と二重には実行されません(1回の⌘Zで1件だけ戻ることをテストで確かめています)。項目を選べないときは、キーはこれまでどおりWebViewへ届きます。document.execCommand)。標準の「取り消す」を置き換えたため、そのままでは盤面に戻せる編集が無いときにキーがページへ落ち、文字を戻せなくなります。そこでWebは入力欄にフォーカスがある間、commandStateをcanUndo・canRedoともtrueにして送り、入力欄での⌘Z・⇧⌘Zを常にメニュー経由でexecCommandへ回します。WKWebViewで効くことはUIテストで確かめました。入力欄の判定はWeb版のuseHistoryShortcutsと同じisEditableTarget(packages/editor-core/ui/hooks.tsから書き出して共有。Web版の判定は変えていません)です。EditorCommands.swiftに分け、WebViewContainer.swiftには状態のプロパティ1つとメッセージの分岐1つだけを足しました。Info.plistのキーは他のPRが触らない末尾に置き、UIテストは新しいファイル(KeyboardCommandUITests.swift)にしました。ios/docs/WEB_SYNC.mdはfeat: 編み図で使っている色の一覧から色を選べるようにする #115・feat: 最後のバックアップ日時を表示し、.knitの書き出しを勧める #120・#123と同じ行の近くを変えることになるため触っていません(App.tsxの行の「ネイティブブリッジ」に含まれる扱いです)。テスト
ios/UITests/KnittingEditorUITests/KeyboardCommandUITests.swift(新規、iPadだけで実行):typeKey(_:modifierFlags:)でハードウェアキーボードのキーを送り、⌘N・⌘S・⌥⌘S・⇧⌘E・⌘P・⇧⌘H・⌘Oが、新しい編み図のダイアログ、.knit・PNG・PDFの保存画面(ファイル名まで)、使い方ページ、復元の選択画面へ届くこと。1回の⌘Z・⇧⌘Zで1件ずつだけ戻る・進むこと。⌘Zの後に記号の一覧が「Escapeで閉じます。」を出すこと。新しい編み図のダイアログで文字を打ち、盤面に戻せる編集が無いとき・あるときの両方で、⌘Zで文字が戻り、⇧⌘Zでやり直せ、盤面は戻らないこと。webReady・commandStateがSwiftUIのメニューへ反映されるまで選べず、その間のキーは捨てられます。XCUITestからメニューの状態は読めないため、準備中の表示が消えるまで待ち、期待した画面・値の変化が起きるまでキーを送り直します(どの操作も画面を出している間は重ねて実行されません)。⌘Z・⇧⌘Zは画面のボタンが選べる状態になってから押し、記号数が変わらないときだけ押し直します。ios/Tests/KnittingEditorAppTests/EditorCommandsTests.swift(新規): 操作の名前がWeb側と一致すること、イベントのスクリプト、選べる条件(保存画面などの表示中を含む)、commandStateの解読。ios/Web/src/nativeCommands.test.ts(新規): 受け付ける名前、commandStateの送信、各操作の呼び出し、処理中・ダイアログ表示中・文字入力中の扱い、Web版と同じ入力欄の判定、入力中のメニュー状態とフォーカスの見張り、書き出しの既定値。packages/editor-core/ui/inputEnvironment.test.tsx:noteHardwareKeyboard()のテストを追加。実行した確認
レビューと最初のCI(
ios (iPad (A16))でKeyboardCommandUITestsの2件が、⌘O・⌘Nが起動直後に届かず失敗)を受けて直した後の結果です。npm run typecheck: 成功npm test: 26ファイル160件成功npm run build: 成功npm run check:dist: 成功npm run test:e2e: chromium-mobile・webkit-mobile・chromium-desktopで97件成功、2件は既存のスキップtsc -p tsconfig.app.json --noEmit: 成功、vitest run --config vite.config.ts: 4ファイル17件成功xcodegen generate --spec ios/project.ymlとアプリのビルド: 成功KeyboardCommandUITests(4件)を2回、knittingEditorTests全体(34件、1件は既存のスキップ)、既存のtestGuideNavigationReturnsToUsableEditor・testTwoFingerGestureDoesNotDrawOnBoard・testUndoAndRedoRestoreBoardEdits・testPromptDialogIgnoresBackgroundTaps・testBackupExportSheetDismissesBackToEditor・testPngAndPdfExportsReachNativeFileActions・testDocumentDialogRemainsUsableAfterFocusingInput: すべて成功判断していただきたい点
.knitでバックアップ」(自動保存とは別)、⌘PをPDF書き出しにしています。iPadOSの慣習では⌘Sは保存、⌘Pは印刷のため、このままでよいかご判断ください。isEditableTargetをそのまま共有したため、選択欄・スライダーなど文字入力でない入力欄にフォーカスがあるときも、⌘Zは盤面ではなく入力欄へ回ります(Web版のキー操作と同じ動き)。実機で確かめること(#21)
execCommandによる入力欄の文字の取り消し・やり直し(Simulatorでは確認済み)。できなかった確認
perform(withKeyModifiers:)はキーを押し続けないため一覧を出せず、メニューバーも上端からのスワイプでは出せませんでした。項目がUIKitのメインメニューに載り、キーで動くことまでは確かめています。Refs #80
🤖 Generated with Claude Code