## 概要 - [[Spoon]]版の[[🦉JINRAI]]からnative版の[[🦉JINRAI]]に向けて、ドキュメンテーションを移行する - ドキュメントにあるけど存在しない機能 - 重要な実装漏れは機能追加 - それ以外はドキュメントから削除 - ドキュメントにないけど存在する機能 - ドキュメントに追加 ## jinrai: docs を Swift 実装に合わせて全面修正 + direction.direct 実装 ### Context `docs/` 配下のドキュメントサイト(zensical)は Hammerspoon Spoon 版(../jinrai-spoon)からそのままコピーされたもので、全ページが Lua 設定・SpoonInstall・`spoon.Jinrai:setup` 前提のまま。一方 jinrai 本体は Swift 製メニューバーアプリで、設定は `~/.config/jinrai/config.jsonc`(JSONC)。設定スキーマにも実質差分が多数ある。 ユーザー決定事項: - **`window_hints.navigation.direction.direct`(ヒントを開かず修飾キー+方向キーで直接フォーカス移動)のみ Swift 側へ実装追加** - それ以外の実装に存在しない項目(`macos_native_tabs: false` 無効化、spaceBadge カスタム色、フォント名・余白等の見た目設定、`selectedArea.appearance.styles`、Lua callbacks)は**ドキュメントから削除・修正** 実装の正(設定スキーマの一次情報): `Sources/JinraiCore/Config/*.swift`(RootConfig / WindowHints / WindowMover / ApplicationHints / FocusBack / FocusBorder / JinraiMode)と `Sources/Jinrai/ConfigLoader.swift`(テンプレート)。 ### Part 1: direction.direct の実装(Swift) ヒントを開かずにグローバルホットキー(修飾キー+方向キー)で隣のウィンドウへ直接フォーカス移動する。Spoon版(`jinrai-spoon/Jinrai.spoon/window_hints.lua` の directDirectionHotkeys)と同仕様。 #### 1-1. 設定追加 — `Sources/JinraiCore/Config/WindowHintsConfig.swift` - struct に追加: ```swift public struct DirectDirectionHotkeys: Equatable, Sendable { public var modifiers: [String] public var keys: [Direction: String] // 方向 → キー名 } public var directDirectionHotkeys: DirectDirectionHotkeys? ``` - `WindowHintsConfigBuilder.build` で `navigation.direction.direct.{modifiers,keys}` をパース(既存 `navigation.direction.hints.keys` パースの直後)。バリデーション(Spoon版準拠): - 方向名は `Direction(rawValue:)` で検証、不正なら ConfigError - キーは小文字化し、重複は ConfigError - `keys` が空/未指定 → direct 全体を nil(黙って無効) - `keys` があるのに `modifiers` が空/欠落 → ConfigError - **`fn` は ConfigError にする**: Carbon `RegisterEventHotKey` は fn 修飾非対応で、`KeyCodes.carbonModifiers` では黙って無視され「fnなしホットキー」になってしまうため(許可リスト: cmd/command, alt/option, ctrl/control, shift) #### 1-2. ホットキー登録 — `Sources/Jinrai/Features/WindowHintsFeature.swift` - プロパティ `private var directDirectionHotkeys: [Hotkey] = []` を追加。 - `init` 内(既存の本体ホットキー登録 L96-103 の直後)で、方向ごとに `Hotkey(modifiers:key:)` を登録。ハンドラは `guard !self.isVisible else { return }` の防御付きで `runDirectionalMove(direction)` を呼ぶ。登録失敗は NSLog。 - `runDirectionalMove` は**変更不要**: 非表示時でも `WindowEnumerator.orderedWindows()` をその場で列挙し、`close()`/`dismissSpotlight` は非表示時 no-op。current は最前面標準ウィンドウ。 - `teardown()` で全 `directDirectionHotkeys` を unregister → 設定リロード時の再登録は既存フロー(`AppDelegate.reloadConfig` → teardown → start)で自動成立。**AppDelegate の変更は不要**。 - ヒント表示中の競合なし: 表示中は EventTap が keyDown を消費するため Carbon ホットキーには届かない(既存コメント L878 と同じ前提)。「表示中は hints.keys、非表示中は direct」の住み分けをコメントに残す。 #### 1-3. テスト — `Tests/JinraiCoreTests/ConfigTests.swift`(WindowHintsConfigTests Suite) 1. デフォルトで `directDirectionHotkeys == nil` 2. modifiers+keys 両指定でパース成功(小文字化・斜め方向 upLeft 等の受理も確認) 3. 空 keys → nil(エラーにしない) 4. keys あり・modifiers なし → ConfigError 5. 不正な方向名 → ConfigError 6. キー重複 → ConfigError 7. modifiers に "fn" → ConfigError ### Part 2: ドキュメント全面修正 #### 2-1. 全ページ共通の変換 - Lua コードブロック → JSONC(`~/.config/jinrai/config.jsonc` の該当セクション)。キーパスは基本同一(`window_hints.hint.chars` 等)だが、値の記法を JSON に(`{ red = 0.4 }` → `{ "red": 0.4 }`、`nil` → 項目省略)。 - 「Hammerspoonスクリプト」「Hammerspoon Spoon」→「macOS用ネイティブアプリ」等の表現に変更。 - `spoon.Jinrai:setup({...})` の外殻を除去し、config.jsonc のトップレベル構造で提示。 #### 2-2. ファイル別の修正内容 **docs/zensical.toml** - `site_description`: 「…hammerspoonスクリプト」→「思考の速度で素早くウィンドウ操作を行うmacOS用ツール」(README と統一)。 **docs/docs/index.md** - L6, L17 の Hammerspoon 記述を修正。他はほぼ流用可。 **docs/docs/setup.md(全面書き換え)** — README.md の内容を正として: - 前提準備(Hammerspoon/SpoonInstall)を削除。 - インストール: Homebrew `brew install tadashi-aikawa/tap/jinrai`(推奨)/ 手動 zip + Gatekeeper 手順(README L27-45 を反映)。 - 権限: アクセシビリティ必須、Window Hints のプレビューには画面収録も必要。 - 最小設定: 初回起動で `~/.config/jinrai/config.jsonc` が自動生成される(`ConfigLoader.defaultConfigTemplate` の内容を掲載)。`$XDG_CONFIG_HOME` 尊重。編集後はメニューバーの「設定を再読込」。 - アップデート: メニューバー「アップデートを確認…」(自動DL・置換・再起動)/ Homebrew 管理下は `brew upgrade --cask jinrai` 案内のみになる旨(UpdaterFeature の挙動)。 **docs/docs/configuration.md(全面書き換え)** — `Sources/JinraiCore/Config/*.swift` のデフォルト値・キーパスから再生成: - トップレベルキー: `macos_native_tabs`(snake_case に変更)、`jinrai_mode`、`focus_border`、`window_hints`、`focus_back`、`window_mover`、`application_hints`。 - 削除する項目(実装に存在しない): - window_hints: `hint.collisionOffset`、`hint.key.size / horizontalPadding / gap / fontName`、`hint.title.fontName / rowGap`、`hint.spaceBadge.*`(バッジは固定デザインで常時表示、と本文で言及)、`navigation.direction.scoring.debug`、`behavior.callbacks` - focus_back: なし(`urlEvent.name` は残す。説明を `jinrai://<名前>` に) - window_mover: `selectedArea.appearance.borderWidth / cornerRadius / styles` - application_hints: `newWindow.callback`、`behavior.callbacks` - `macosNativeTabs = false` での無効化記述 - 変更する項目: - `selectedArea.defaultScreen`: UUID 文字列参照 → **エリア名→キーのマップを直接指定**(WindowMoverConfig.swift の SelectedArea.defaultScreen: [String: String]?) - `application_hints.newWindow`: `callback` → `url`(指定時は open を最優先) - 追記する項目(実装にあり docs にない): - window_hints: `focusedWindowSpotlight.alpha`(既定 0.5)、`behavior.showFadeIn.hints`(0.05)/ `behavior.showFadeIn.spotlight`(0.4) - window_mover: `selectedArea.activeWindowSpotlight.alpha`(0.5)、`selectedArea.activeWindowHighlight.borderColor / borderWidth(13) / cornerRadius(12)` - application_hints: `appearance.keyHighlightColor` - direction.direct は Part 1 実装後の仕様で記載(fn 修飾不可の注意含む)。 - 「選択肢と共通ルール」: 修飾キーは `cmd/command`、`alt/option`、`ctrl/control`、`shift`、`fn`(KeyCodes.swift:74-98 が正)。色は「Hammerspoonの色テーブル」→ JSON オブジェクト。コールバック節は削除。 **docs/docs/window-hints.md** - Lua → JSONC。`hint.key.size / fontName`、`title.fontName`、`spaceBadge` 設定、`collisionOffset` を設定例から削除。 - `navigation.direction.direct` は新実装の仕様に合わせて記載。 - 隠れウィンドウのプレビューに画面収録権限が必要な旨を追記。 **docs/docs/application-hints.md** - 「アプリ固有の新規ウィンドウ作成」節: `newWindow.callback`(hs.urlevent)→ `newWindow.url` に書き換え(Obsidian 例は `"url": "obsidian://open?path=..."`)。 - `appearance.keyHighlightColor` に触れる(任意)。 **docs/docs/window-mover.md** - `selectedArea.defaultScreen` の説明と例を「エリア→キーのマップ直接指定」に修正。 - 「選択画面の見た目」節: `appearance.borderWidth / cornerRadius / styles` を削除し、`appearance.state.normal / dimmed`(実装で読込)+ `activeWindowHighlight / activeWindowSpotlight` に差し替え。 **docs/docs/focus-back.md** - `hammerspoon://<名前>` → `jinrai://<名前>`(Info.plist の URL scheme + AppDelegate:182 の分岐)。 **docs/docs/focus-border.md、jinrai-mode.md、window-mover-areas.md** - Lua → JSONC の書式変換のみ(スキーマは一致)。 **docs/docs/macos-native-tabs.md** - キー名を `macos_native_tabs` に。`spoon.Jinrai:setup` 外殻除去。 - 「補正を無効化(`= false`)」節を削除(実装に無効化手段なし)。 **README.md** - 「ドキュメント」節の TODO 2件を実 URL(https://tadashi-aikawa.github.io/jinrai/ と https://deepwiki.com/tadashi-aikawa/jinrai)に置換。 #### 2-3. 対象外(変更しない) - `docs/site/`(ビルド成果物)、`docs/.venv/`、`docs/.cache/` - デモ動画(YouTube)・ブログ記事リンク: そのまま残す - `docs/pyproject.toml` の description 等の雛形値 ### 検証 1. `swift build && swift test`(direction.direct のロジックテスト含む) 2. `./scripts/make-app.sh && open .build/Jinrai.app` で direction.direct の実機確認(config.jsonc に direct 設定を書き、修飾キー+hjkl で隣のウィンドウへフォーカス移動すること、Window Hints 表示中の挙動と競合しないこと) 3. docs: `cd docs && uv run zensical serve`(または build)でビルドエラー・リンク切れがないことを確認し、主要ページを目視 4. ドキュメントの JSONC 例が `RootConfigBuilder.build(text:)` で実際にパース可能なことをスポットチェック(テンプレート+代表例)