# useImeInput

IME（Input Method Editor）を経由したテキスト入力を壁紙で受け取るためのフックです。日本語・中国語・韓国語など、変換が必要な言語の入力を壁紙のメモ帳・検索ボックス・チャット入力等に流し込めます。

`useKeyboard` がデスクトップ全体の生キーを購読するのに対し、`useImeInput` は **壁紙自身に向けて入力された** 文字のみを受信します。内部的には壁紙ごとに極小（1×1 px・透明・非アクティベート）のプロキシウィンドウが用意され、`activate()` を呼ぶとそのプロキシだけが key window 化され、IME がそこに合成テキストを配送します。壁紙ウィンドウ自体は決して key window にならないため、壁紙らしさ（最背面・フォーカスを取らない）が損なわれません。

## permissions 宣言が必須

このフックを使うには `fluxlay.yaml` で `ime-input` permission を宣言する必要があります。未宣言の場合、backend が IME 関連のリクエストを HTTP 403 で拒否します。フック自体は同じ API でマウントされますが、ストリームは流れず、`activate()` / `deactivate()` も SDK が受理するだけで効果がありません。

```yaml title="fluxlay.yaml"
schemaVersion: 1
name: My Wallpaper
slug: my-wallpaper
version: 1.0.0
permissions:
  - ime-input
```

permissions 全体の意味は [`manifest`](/ja/studio/developer/reference/cli/manifest.md#permissions) を参照してください。`keyboard` と `ime-input` は権限スコープが異なる別枠です（`keyboard` はグローバル全打鍵、`ime-input` は壁紙自身に向けられた入力のみ）。

## インポート

```tsx
import { useImeInput } from "@fluxlay/react";
```

## シグネチャ

```tsx
function useImeInput(): ImeInputApi;

interface ImeInputApi {
  composition: string | null;
  cursor: number;
  activate: (anchor?: HTMLElement | { x: number; y: number; height: number }) => void;
  deactivate: () => void;
  onCommit: (handler: (text: string) => void) => () => void;
}
```

## 使い方

```tsx
import { useImeInput } from "@fluxlay/react";
import { useEffect, useRef, useState } from "react";

function NotePad() {
  const ime = useImeInput();
  const inputRef = useRef<HTMLDivElement>(null);
  const [text, setText] = useState("");

  useEffect(() => {
    return ime.onCommit(committed => setText(prev => prev + committed));
  }, [ime]);

  return (
    <div
      ref={inputRef}
      tabIndex={0}
      onClick={() => ime.activate(inputRef.current ?? undefined)}
      onBlur={ime.deactivate}
    >
      <span>{text}</span>
      {ime.composition !== null && <span style={{ opacity: 0.5 }}>{ime.composition}</span>}
    </div>
  );
}
```

## API

| 名前          | 型                                                                           | 説明                                                                                                                                                                                                                                                                |
| ------------- | ---------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `composition` | `string \| null`                                                             | 合成中の中間文字列（例: 「漢字」を入力中の `"か"` `"かん"` `"漢"`）。未合成中は `null`。                                                                                                                                                                            |
| `cursor`      | `number`                                                                     | `composition` 内のキャレット位置。コードポイント単位。                                                                                                                                                                                                              |
| `activate`    | `(anchor?: HTMLElement \| { x: number; y: number; height: number }) => void` | プロキシウィンドウを key 化し、IME 入力受付を開始します。`HTMLElement`（通常は入力欄の ref）か viewport 相対 CSS ピクセルの `{ x, y, height }` を渡すと、IME 候補ウィンドウをキャレット付近に表示します。引数なしの場合はアクティブスクリーンの中央に表示されます。 |
| `deactivate`  | `() => void`                                                                 | プロキシウィンドウのキーを返却し、IME 入力受付を停止します。コンポーネントのアンマウント時には自動的に呼ばれます。                                                                                                                                                  |
| `onCommit`    | `(handler) => () => void`                                                    | 確定文字列（例: 「漢字」）を受け取るハンドラを登録します。返り値は登録解除関数です。`useEffect` の cleanup で呼び出してください。                                                                                                                                   |

`activate` には React の `MouseEvent` などを直接渡さないでください。受け付けるのは `HTMLElement` か `{ x, y, height }` のみです。`onClick` から呼ぶ場合は `onClick={ime.activate}` ではなく `onClick={() => ime.activate(ref.current ?? undefined)}` のようにラップしてください。

## useKeyboard との関係

`activate()` 状態の間は当該壁紙への `useKeyboard` 配信が一時停止されます。これは IME 候補ウィンドウの操作（矢印キー、Enter、Esc）が壁紙ロジックに重複配信されるのを防ぐためです。`deactivate()` で通常通り再開します。

## プラットフォーム要件

- **macOS**: アプリ全体の activation を引き起こさない `NSPanel`（`.nonactivatingPanel`）でプロキシを生成するため、Dock のハイライトやメニューバーは切り替わりません。
- **Windows**: `WS_EX_NOACTIVATE` の不可視 HWND でプロキシを生成し、taskbar の点滅を起こさずにフォーカスを取り直します。

## 制限事項

- 確定後の文字列のみ最終的に壁紙に渡るため、変換中の文字列を別途装飾する場合は `composition` を直接描画してください。
- `activate` に渡す座標は viewport 相対 CSS ピクセルです。壁紙ウィンドウのスクリーン位置は backend 側で解決するため、`window.screenX` / `window.screenY` を加算しないでください。custom-scheme で動作する壁紙では WebKit の `screenX` / `screenY` の値が信頼できません。
