0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AIとのチャットで Enter を押した瞬間に、書きかけの文章が飛んでいく。あれを止める。

0
Posted at

この記事では Claude / Xcode / Visual Studio Code の3つで Enter と Shift+Enter の役割を入れ替えます。所要時間は10分ほど、ターミナルはコピー&ペーストだけで完結します。

対象読者

  • Enter 一発送信のUIで誤送信を繰り返している人
  • アプリ側に「Enterで送信」トグルが無くて詰んでいる人
  • macOS のキーカスタマイズを初めて触る人

仕組み

macOS には「特定のアプリでだけキーの意味を変える」標準機能がありません。そこで Karabiner-Elements(無料・オープンソースの定番キーカスタマイズツール)を使い、対象アプリが最前面にあるときだけキーを入れ替えます。

押すキー 変換後 結果
Enter Shift+Enter 改行
Shift+Enter Enter 送信

対象アプリ以外では一切影響がありません。

ステップ1: Karabiner-Elements をインストール

Homebrew を使う場合:

brew install --cask karabiner-elements

使わない場合は 公式サイト から .dmg をダウンロードし、通常どおりインストールします。

ステップ2: 一度アプリを起動する

必ずここで一度 Karabiner-Elements.app を開いてください。
初回起動時に設定ファイル ~/.config/karabiner/karabiner.json が生成されます。これが無いと後のステップが失敗します。

ステップ3: システムの許可を与える

Karabiner はキー入力を横取りするため、macOS の許可が2か所必要です。ここを飛ばすと、設定を書き込んでもキーは入れ替わりません。

A. ドライバ機能拡張

システム設定一般ログイン項目と機能拡張 → 一番下の ドライバ機能拡張 → Karabiner をオン

再起動を求められたら従ってください。

B. 入力監視

システム設定プライバシーとセキュリティ入力監視karabiner_grabberkarabiner_observer にチェック

確認

pgrep -fl karabiner_grabber

プロセスIDとパスが表示されれば稼働中です。何も出なければ A に戻ってください。

ステップ4: セットアップスクリプト

karabiner.json を直接編集してもいいのですが、JSONの階層が深くて手作業だと事故ります。バックアップと冪等性を持たせたスクリプトを用意しました。

setup-enter-swap.sh
#!/bin/bash
#
# Karabiner-Elements に「Enter=改行 / Shift+Enter=送信」ルールを適用します。
# 対象アプリ: Claude / Xcode / Visual Studio Code
#
#   実行:     bash setup-enter-swap.sh
#   元に戻す: bash setup-enter-swap.sh --uninstall
#
set -euo pipefail

CONFIG="$HOME/.config/karabiner/karabiner.json"
RULE_DESC="Enter=改行, Shift+Enter=送信 (Claude/Xcode/VS Code)"
MODE="install"
[[ "${1:-}" == "--uninstall" ]] && MODE="uninstall"

echo "==> Enter/Shift+Enter 入れ替えセットアップ (${MODE})"

# 1. Karabiner-Elements の確認 / インストール
if [[ ! -d "/Applications/Karabiner-Elements.app" ]]; then
  if [[ "$MODE" == "uninstall" ]]; then
    echo "Karabiner-Elements が見つかりません。何もせず終了します。"
    exit 0
  fi
  echo "==> Karabiner-Elements が未インストールです。"
  if command -v brew >/dev/null 2>&1; then
    echo "    Homebrew でインストールします..."
    brew install --cask karabiner-elements
  else
    echo "    Homebrew がありません。以下から手動でインストールしてください:"
    echo "    https://karabiner-elements.pqrs.org/"
    exit 1
  fi
  echo
  echo "    ※ 初回はシステム設定でドライバ/入力監視の許可が必要です。"
  echo "      Karabiner-Elements.app を一度起動し、許可を与えてから"
  echo "      このスクリプトを再実行してください。"
  open -a "Karabiner-Elements" 2>/dev/null || true
  exit 0
fi

# 2. 設定ファイルの確認
if [[ ! -f "$CONFIG" ]]; then
  echo "設定ファイルが見つかりません: $CONFIG"
  echo "Karabiner-Elements.app を一度起動してから再実行してください。"
  open -a "Karabiner-Elements" 2>/dev/null || true
  exit 1
fi

# 3. バックアップ
BACKUP="${CONFIG}.bak.$(date +%Y%m%d-%H%M%S)"
cp "$CONFIG" "$BACKUP"
echo "==> バックアップ: $BACKUP"

# 4. ルールの適用 / 削除
CONFIG="$CONFIG" RULE_DESC="$RULE_DESC" MODE="$MODE" python3 <<'PY'
import json, os, sys

config_path = os.environ["CONFIG"]
rule_desc   = os.environ["RULE_DESC"]
mode        = os.environ["MODE"]

BUNDLES = [
    r"^com\.anthropic\.claudefordesktop$",
    r"^com\.apple\.dt\.Xcode$",
    r"^com\.microsoft\.VSCode$",
]

CONDITION = [{
    "type": "frontmost_application_if",
    "bundle_identifiers": BUNDLES,
}]

RULE = {
    "description": rule_desc,
    "manipulators": [
        {   # Shift+Enter -> Enter (= 送信)
            "type": "basic",
            "from": {
                "key_code": "return_or_enter",
                "modifiers": {"mandatory": ["shift"]},
            },
            "to": [{"key_code": "return_or_enter"}],
            "conditions": CONDITION,
        },
        {   # Enter -> Shift+Enter (= 改行)
            "type": "basic",
            "from": {
                "key_code": "return_or_enter",
                "modifiers": {"optional": []},
            },
            "to": [{"key_code": "return_or_enter",
                    "modifiers": ["left_shift"]}],
            "conditions": CONDITION,
        },
    ],
}

with open(config_path, encoding="utf-8") as f:
    cfg = json.load(f)

profiles = cfg.get("profiles") or []
if not profiles:
    print("プロファイルが見つかりません。", file=sys.stderr)
    sys.exit(1)

# 選択中のプロファイル(なければ先頭)
target = next((p for p in profiles if p.get("selected")), profiles[0])
cm = target.setdefault("complex_modifications", {})
rules = cm.setdefault("rules", [])

# 同名ルールを一旦除去(再実行しても重複しない)
before = len(rules)
rules[:] = [r for r in rules if r.get("description") != rule_desc]
removed = before - len(rules)

if mode == "install":
    rules.insert(0, RULE)
    action = "更新しました" if removed else "追加しました"
else:
    action = "削除しました" if removed else "見つかりませんでした(変更なし)"

with open(config_path, "w", encoding="utf-8") as f:
    json.dump(cfg, f, indent=4, ensure_ascii=False)
    f.write("\n")

print(f"==> プロファイル「{target.get('name', '?')}」のルールを{action}")
PY

# 5. 反映(Karabiner は karabiner.json の変更を自動検知して即反映)
if ! pgrep -q -f "Karabiner-Elements"; then
  open -a "Karabiner-Elements" 2>/dev/null || true
fi

echo "==> 完了"

ポイントは3つです。

  • 実行前に必ずタイムスタンプ付きでバックアップを取る
  • 同名ルールを除去してから挿入するので、何度実行しても重複しない
  • 既存の他のルールには一切触れない

保存したらターミナルで実行します。

bash ~/Downloads/setup-enter-swap.sh

Finder でダブルクリックしないでください。処理が一瞬で終わるためウインドウが即座に閉じ、A session ended very soon after starting という警告だけが残ってエラーが読めません。必ずターミナルから上記の形で実行します。

成功するとこう表示されます。

==> Enter/Shift+Enter 入れ替えセットアップ (install)
==> バックアップ: ~/.config/karabiner/karabiner.json.bak.20260719-113000
==> プロファイル「Default profile」のルールを追加しました
==> 完了

ステップ5: 確認

python3 -c "
import json,os
d=json.load(open(os.path.expanduser('~/.config/karabiner/karabiner.json')))
p=next((x for x in d['profiles'] if x.get('selected')), d['profiles'][0])
for r in p.get('complex_modifications',{}).get('rules',[]):
    print('-', r.get('description'))
"

Enter=改行, Shift+Enter=送信 (Claude/Xcode/VS Code) が表示されれば完了です。

最後に Claude の入力欄で Enter を押し、送信されずに改行されることを確かめてください。

元に戻す

bash ~/Downloads/setup-enter-swap.sh --uninstall

追加したルールだけを削除します。Karabiner 自体や他の設定は残ります。

トラブルシューティング

「設定ファイルが見つかりません」と出る

ステップ2をまだ実行していません。Karabiner-Elements.app を一度起動してください。

A session ended very soon after starting と出た

ダブルクリック実行が原因です。ターミナルから bash ~/Downloads/setup-enter-swap.sh の形で実行し直してください。

cannot execute: Permission denied

chmod +x ~/Downloads/setup-enter-swap.sh

頭に bash を付けて実行すれば権限付与は不要です。

ダウンロードしたファイルがブロックされる

Gatekeeper の隔離属性を外します。

xattr -d com.apple.quarantine ~/Downloads/setup-enter-swap.sh

設定は入っているのにキーが変わらない

ほぼ確実にステップ3の許可漏れです。pgrep -fl karabiner_grabber で稼働を確認してください。

対象アプリを変えたい・増やしたい

スクリプト内の BUNDLES を編集します。

BUNDLES = [
    r"^com\.anthropic\.claudefordesktop$",
    r"^com\.apple\.dt\.Xcode$",
    r"^com\.microsoft\.VSCode$",
]

バンドルIDは Karabiner-EventViewer の Frontmost Application タブで確認できます。対象アプリを最前面にすると実際の値が表示されるので、それをそのまま貼り付けてください。

注意点: アプリ単位でしか判定できない

Karabiner の条件判定は アプリ単位 で、ウインドウやペイン単位ではありません。

つまり Xcode や VS Code では、AIチャット欄だけでなくエディタ本体でも Enter が Shift+Enter として送られます。多くの場合は同じ「改行」として扱われるため実害はありませんが、拡張機能が Shift+Enter に別の動作を割り当てていると挙動が変わります。

エディタでの副作用を避けたい場合は、BUNDLES から Xcode / VS Code を外して Claude だけに絞り、各エディタ側のキーバインド設定(VS Code なら keybindings.json)で対応するのが安全です。

おわりに

誤送信は地味にストレスが溜まる上、相手にも中途半端な文章が届いてしまいます。一度設定してしまえば意識せず済むので、心当たりのある方はぜひ。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?