Skip to content

fix: モバイルの Ruby タブで Monaco のキーボードボタンが zoom リセットボタンと重なる(CSS 非表示 + ツールバーにキーボードトグルボタン追加) #727

Description

@takaokouji

Goal

iPadOS で Ruby タブを開くと、Monaco 組み込みの IPadShowKeyboard ウィジェット(textarea.iPadShowKeyboard、58×36px)がエディタ右下角(BOTTOM_RIGHT_CORNER)に表示され、Smalruby 独自の zoom コントロール(特に最下段の zoom リセットボタン)と重なって操作できない。

このウィジェットを CSS で無条件に非表示にし、代替としてソフトウェアキーボードの表示/非表示を切り替えるボタンを Ruby ツールバーに追加する(タッチデバイスのみ表示)。

原因調査の結果

  • monaco-editor@0.55.1IPadShowKeyboard contribution(editor.main.js で無条件登録)が原因
  • isIOS 判定(UA に Macintosh/iPad/iPhone を含む かつ maxTouchPoints > 0 — iPadOS Safari は UA が "Macintosh" なので該当)のときのみ overlay widget を右下角に追加する
  • ウィジェットの機能はタップ時の editor.focus()(iOS でソフトウェアキーボードを出すレガシーなフォールバック)
  • Smalruby の zoom コントロール(ruby-tab.css.zoomControlsWrapperbottom: 31px; right: 31px)と位置が重なる
  • Monaco standalone API には built-in contribution を除外する公開オプションがないため、CSS で非表示にする

機能設計(キーボードトグルボタン)

  • 表示: editorRef.focus() — ボタンタップ(user gesture)内で Monaco の hidden textarea にフォーカス → iOS/Android でソフトウェアキーボードが出る(Monaco ウィジェットと同じ仕組み)
  • 非表示: エディタの textarea (.inputarea) を blur() → キーボードが閉じる
  • 状態表示: onDidFocusEditorText / onDidBlurEditorText を購読して aria-pressed とアクティブスタイルを切り替え(auto-correct ボタンと同じパターン)
  • 表示条件: navigator.maxTouchPoints > 0(タッチデバイス全般 = iPad + Android。Android でも「キーボードを閉じる」用途で有用)。デスクトップでは非表示
  • 配置: Edit Part(undo/redo/search の隣)、data-testid="ruby-toolbar-keyboard"

Affected Files

  • packages/scratch-gui/src/containers/ruby-tab/ruby-tab.css.iPadShowKeyboarddisplay: none に(既存の find-widget override と同じパターン)
  • packages/scratch-gui/src/components/ruby-toolbar/ruby-toolbar.jsx — キーボードトグルボタン追加(editorRef の focus/blur 購読、タッチ判定)
  • packages/scratch-gui/src/components/ruby-toolbar/messages.jskeyboardShow / keyboardHide メッセージ
  • packages/scratch-gui/src/components/ruby-toolbar/icon--keyboard.svg — 新規アイコン
  • packages/scratch-gui/src/lib/screen-utils.jsisTouchDevice() ヘルパー追加(既存ファイル)
  • packages/scratch-gui/src/locales/ja.js / ja-Hira.js — 日本語訳
  • packages/scratch-gui/test/unit/components/ruby-toolbar-keyboard.test.jsx — 新規ユニットテスト(prettier whitelist 2 箇所更新が必要)
  • packages/scratch-gui/test/integration/ruby-tab/ — 回帰テスト
  • .claude/rules/scratch-gui/e2e-test.mdruby-toolbar-keyboard testid を一覧に追加
  • docs/(ruby-tab / mobile-ui 該当ドキュメント)— UI 変更の記載 + スクリーンショット

Implementation Steps

Phase 1: Monaco キーボードウィジェットの CSS 非表示

  • [RED] integration テスト: .monaco-editor 内に .iPadShowKeyboard 要素を注入し computed style が display: none であることを assert
  • [GREEN] ruby-tab.css にルール追加
  • [PASS] lint + 該当テスト
  • [COMMIT & PUSH] fix: hide Monaco iPad keyboard widget overlapping zoom controls
  • [MAKE PR]

Phase 2: キーボードトグルボタン(TDD)

  • [RED] ユニットテスト:
    • isTouchDevice() の判定ロジック(maxTouchPoints のモック)
    • タッチデバイス時のみボタンがレンダリングされる
    • クリックで editorRef.focus() が呼ばれる(非フォーカス時)/textarea の blur が呼ばれる(フォーカス時)
    • focus/blur イベントで aria-pressed が切り替わる
  • [GREEN] isTouchDevice() + ボタン実装 + アイコン + messages + locales
  • [PASS] lint + 該当テスト
  • [COMMIT & PUSH] feat: add software keyboard toggle button to Ruby toolbar
  • [UPDATE PR]

Phase Final: Integration テスト + ドキュメント

  • ruby-tab integration テスト(タッチエミュレーションでのボタン表示)
  • docs + e2e-test.md + prettier whitelist 更新
  • [COMMIT & PUSH][UPDATE PR]

Definition of Done

  • ユニットテスト pass(ボタン表示条件 / focus・blur / aria-pressed)
  • Integration テスト pass(.iPadShowKeyboarddisplay: none
  • lint pass / CI green
  • ブラウザ確認(Playwright、iPad UA + touch エミュレーション):
    • Monaco のキーボードウィジェットが表示されない
    • zoom 4 ボタン(screenshot / in / out / reset)がすべてクリック可能
    • ツールバーに ruby-toolbar-keyboard ボタンが表示される
    • ボタンタップでエディタにフォーカスが入る(document.activeElement が Monaco の textarea)
    • 再タップでフォーカスが外れる
  • ブラウザ確認(デスクトップ UA): キーボードボタンが表示されない、見た目に変化なし
  • 実機 iPad 確認(手動、ユーザー実施):
    • キーボードボタンタップでソフトウェアキーボードが出る
    • 再タップでキーボードが閉じる
    • エディタ直接タップでもキーボードが出る(従来動作の維持)

Test Plan

Type Timing Target
Unit (TDD) 実装前 RED→GREEN isTouchDevice 判定、ボタン表示条件、focus/blur、aria-pressed
Integration 実装後 .iPadShowKeyboard の display:none 回帰テスト
Browser (Playwright) CI green 後 iPad UA エミュレーションで DoD 確認
実機 iPad 最後 キーボード出現/消失の手動確認

Risks & Open Questions

  • blur は Monaco の textarea (.inputarea) への DOM 操作になる(Monaco API に公開の blur メソッドがないため)。read-only な操作のためリスクは低い(設計承認済み)

関連

Feedback: smalruby/smalruby3-develop#87

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions