Skip to content

feat(config-converter): JSON Schema 検証パネルに Cmd/Ctrl+Enter ショートカットを追加 - #148

Merged
fumtas1k merged 3 commits into
developfrom
feat/137-config-converter-schema-shortcut
Apr 30, 2026
Merged

feat(config-converter): JSON Schema 検証パネルに Cmd/Ctrl+Enter ショートカットを追加#148
fumtas1k merged 3 commits into
developfrom
feat/137-config-converter-schema-shortcut

Conversation

@fumtas1k

Copy link
Copy Markdown
Owner

概要

JSON Schema 検証パネルのスキーマ textarea にフォーカスした状態で Cmd+Enter(Mac)/ Ctrl+Enter(Windows/Linux)を押すと検証が実行されるキーボードショートカットを追加します。

変更内容

  • src/components/ui/InputField.tsx
    • optional な onKeyDown prop を追加し、<textarea> / <input> 両方に forward
  • src/components/tools/ConfigConverter.tsx
    • スキーマ用 InputFieldonKeyDown ハンドラを追加
    • ボタンの disabled 条件(!output || !schemaText || isValidating)と完全に一致させてショートカットのガードを実装
    • ボタン横に "Cmd/Ctrl+Enter" のヒントテキストを追加(caption + colors.muted を使用)

テスト

  • npm run test: 256 件 pass
  • npm run test:e2e: 127 件 pass(1 skipped)
  • 新規 E2E テスト 1 件追加
    • スキーマ textarea にフォーカスした状態で Meta+Enter / Control+Enter を送信し、スキーマ検証成功メッセージが表示されることを確認

Closes #137

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Apr 30, 2026

Copy link
Copy Markdown

Deploying devtools with  Cloudflare Pages  Cloudflare Pages

Latest commit: 66fedb8
Status: ✅  Deploy successful!
Preview URL: https://88fac5de.devtools-d9w.pages.dev
Branch Preview URL: https://feat-137-config-converter-sc.devtools-d9w.pages.dev

View logs

@fumtas1k fumtas1k self-assigned this Apr 30, 2026
@fumtas1k

Copy link
Copy Markdown
Owner Author

レビュー概要

JSON Schema 検証パネルのスキーマ textarea にフォーカスした状態で Cmd/Ctrl+Enter で検証を実行できるショートカットを追加する PR です。Issue #137 の要件をスコープどおりに実装しており、変更は 53 行 / 0 削除と最小限。InputField への onKeyDown 追加、ConfigConverter 側のハンドラ実装、ヒント文字列追加、E2E テスト 1 件追加で構成されています。

良い点

  • InputField.tsx の変更が破壊的変更でない(optional prop の追加のみ)。textarea / input の双方に forward しており、他ツールでも再利用可能な汎用拡張になっている。
  • ショートカット発火条件 !output || !schemaText || isValidating がボタン disabled 条件と完全に一致しており、ボタン押下時とキーボード操作時の挙動が一貫する。e.preventDefault() をガード後に置いて textarea の改行抑制も正しく実装されている。
  • ヒントテキストが Tailwind カラークラスではなく caption + colors.muted を使用しており、docs/shared-agent-rules.md 7 章のスタイルルールに準拠している。
  • E2E テストで process.platform === 'darwin' を判別して Meta+Enter / Control+Enter を出し分けており、CI / ローカル双方で動作する設計になっている。Issue feat(config-converter): JSON Schema 検証パネルに Cmd/Ctrl+Enter ショートカットを追加 #137 の AC(ショートカットによる検証実行)も検証されている。
  • コミットメッセージ・PR 説明文・テストコメントいずれも日本語で記述されており、6 章のルールに準拠。

指摘事項

🔴 必須(Must)

特になし。機能・規約遵守ともに合格水準。

🟡 推奨(Should)

1. IME 変換確定 Enter での誤発火リスク

日本語 IME で変換確定のために Enter を押した瞬間 metaKey/ctrlKey を同時に押している状況は通常ないため実害は低いものの、IME 関連は OS / ブラウザ実装差が大きく、念のため e.nativeEvent.isComposing のガードを入れておくと安全です。

onKeyDown={(e) => {
  if (e.nativeEvent.isComposing) return;
  if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) {
    if (!output || !schemaText || isValidating) return;
    e.preventDefault();
    handleValidate();
  }
}}

スキーマ JSON は通常コピペ前提で日本語入力する場面はほぼないため、本 PR スコープ外として別 issue 化(docs/shared-agent-rules.md 6.4 に従う)でも構いません。

2. ヒントテキストとボタンの関連付け(アクセシビリティ)

<span style={{ ...caption, color: colors.muted }}>Cmd/Ctrl+Enter</span> は視覚的にショートカットを示しますが、スクリーンリーダ利用者には「ボタンに紐づくショートカット」という関係が伝わりません。以下のいずれかを推奨します。

  • 検証ボタンに aria-keyshortcuts="Meta+Enter Control+Enter" を付与(W3C ARIA 1.2 準拠の値形式)
  • もしくは span に id を持たせ、ボタンの aria-describedby にその id を指定

ヒント span は装飾的なので aria-hidden="true" を併用するとさらに整理されます。

3. プラットフォーム判定によるヒント表記の最適化(軽微)

ヒントテキストが常に Cmd/Ctrl+Enter と両表記になっていますが、Windows ユーザーには Cmd が冗長で、Mac ユーザーには Ctrl が冗長です。navigator.platform または navigator.userAgent から判定して片側だけ出す方が UX 上わかりやすいです。ただし Issue #137 にこの要件はないため、対応は任意です。SSR (Astro) では hydration 後に判定する必要がある点に注意してください。

🟢 提案(Nit)

1. ハンドラのインライン化と useCallback 化

現状 onKeyDown ハンドラは JSX 内インライン定義されており、schemaText 等の state 変化のたびに新しい関数が生成されます。InputField は memo 化されていないため実害はありませんが、将来 InputFieldReact.memo で最適化する場合に備え、useCallback 化または関数として切り出しておくとキレイです。優先度は低いです。

2. ヒント span のセマンティクス

<kbd> 要素を使うと「キーボード入力」というセマンティクスが伝わり、ブラウザのデフォルトスタイル(等幅フォント・枠線)も付与されます。

<kbd style={{ ...caption, color: colors.muted, fontFamily: 'monospace' }}>
  Cmd/Ctrl+Enter
</kbd>

スタイルポリシーには反しないため、見た目を整える方向の改善案としてご検討ください。

3. E2E テストの待機方法

page.waitForFunctiontextarea[readonly] の値が "name" を含むまで待機していますが、既存テスト群(64 行目以降)は expect(page.getByLabel('JSON', { exact: true })).not.toHaveValue('') のように Playwright の retry 付き assertion で記述されています。プロジェクト内の慣習に揃えると一貫性が増します。

await expect(page.getByLabel('JSON', { exact: true })).toHaveValue(/"name"/);

テスト

  • 新規 E2E ケース 1 件で「ショートカットによる検証成功」を検証しており、Issue feat(config-converter): JSON Schema 検証パネルに Cmd/Ctrl+Enter ショートカットを追加 #137 の AC をカバー。
  • 一方で次のケースは未カバーです。スコープ判断としては妥当ですが、追加可能であれば堅牢性が増します。
    • disabled 条件下(output 未生成 or schemaText 空)でショートカットを送信しても検証が走らないこと
    • 通常の Enter(修飾キーなし)では改行のみが入り、検証が走らないこと
  • npm run test 256 件 / npm run test:e2e 127 件 pass の記載あり。差分はキーボード経路の追加のみで、既存ロジックへの影響は限定的。

総合的に、規約遵守・実装の正確さ・テストカバレッジともに必要水準を満たしており、必須の修正項目はありません。Should 項目(特に IME ガードと aria-keyshortcuts)は次回以降のブラッシュアップ対象として検討してください。

@fumtas1k

Copy link
Copy Markdown
Owner Author

Should 1〜2 および Nit 2〜3 を対応しました(コミット c2b36be)。

  • e.nativeEvent.isComposing ガードを追加(IME 変換確定 Enter での誤発火防止)
  • 検証ボタンに aria-keyshortcuts="Meta+Enter Control+Enter" を付与
  • ヒントテキスト span に aria-hidden="true" を追加
  • <span><kbd style={{ fontFamily: 'monospace' }}> に変更(キーボード入力のセマンティクス)
  • E2E の waitForFunctionexpect(page.getByLabel('JSON', { exact: true })).toHaveValue(/"name"/) に置換

Should 3(プラットフォーム別ヒント表記)は Issue #137 のスコープ外かつ SSR hydration の考慮が必要なため今回は見送りました。
Nit 1(useCallback 化)は InputField が未 memo 化のため現時点では実効性がなく、優先度低として保留します。

@fumtas1k

Copy link
Copy Markdown
Owner Author

再レビュー結果

最新差分(+57 / -0)と該当コミット c2b36be fix(config-converter): レビュー指摘対応(isComposing ガード・アクセシビリティ・E2E 修正) を確認しました。

前回指摘事項の対応状況

🟡 推奨

  • [✅] IME ガード: if (e.nativeEvent.isComposing) return; がハンドラ冒頭に追加され、Cmd/Ctrl+Enter 判定の前段で確実に短絡している。位置・条件ともに想定どおり。
  • [✅] aria-keyshortcuts: 「検証する」ボタンに aria-keyshortcuts="Meta+Enter Control+Enter" を付与済み。値も W3C ARIA 1.2 準拠(修飾子 + キー名、複数はスペース区切り)。
  • [⏭️] Mac/Win ヒント表記の出し分け: 採用見送り。Astro の SSR / hydration を考慮するとコストが見合わないため、両表記据え置きは妥当な判断。

🟢 提案

  • [⏭️] useCallback 化: 採用見送り。InputField は memo 化されておらず実害がないため、現状の判断に同意。
  • [✅] <kbd> 要素: span → <kbd> に変更され、aria-hidden="true" も併用されており、視覚的・スクリーンリーダ双方で意図どおりに機能する形。fontFamily: 'monospace' も明示されている。
  • [✅] E2E 待機方法の統一: page.waitForFunction(...)await expect(page.getByLabel('JSON', { exact: true })).toHaveValue(/"name"/) に置き換え、既存テスト群と一貫したパターンに揃った。

追加の指摘

確認した範囲では新規問題なし。 補足として小さな観察のみ:

  • aria-keyshortcuts<kbd aria-hidden> の組み合わせは ARIA Authoring Practices と一致しており、スクリーンリーダ利用者とそれ以外の利用者で重複読み上げを避ける構成として適切。
  • ヒント要素はボタンの右隣に配置されているが、<kbd> のデフォルト styling(ブラウザ依存)は等幅フォントを継承しつつ枠線等は付かない実装が多いため、現状のインラインスタイルで OS 間差異を吸収できている。
  • e.nativeEvent.isComposing のガードは React の KeyboardEvent 型では isComposing が直接生えていないため nativeEvent 経由参照が必須で、書き方として適切。

前回挙げた E2E カバレッジの未補填項目(disabled 条件下 / 修飾なし Enter)は本 PR では追加されていないが、Issue #137 の AC 範囲外であり、スコープ判断として妥当。必要なら別 issue 化が docs/shared-agent-rules.md 6.4 に沿った扱い。

総評

前回の 🟡 推奨 3 件のうち実質対応すべき 2 件(IME・aria-keyshortcuts)が両方対応済み、🟢 提案 3 件のうち採用された 2 件(kbd・E2E 統一)も実装品質が水準を満たしている。残りの見送り項目(Mac/Win 出し分け・useCallback)も判断として合理的。

マージ可能と判断します。 追加対応は不要です。

JSON Schema 検証 textarea にフォーカスした状態で Cmd+Enter (Mac) /
Ctrl+Enter (Win/Linux) を押すと検証が実行されるよう onKeyDown ハンドラを追加。
InputField に optional な onKeyDown prop を追加して forward する形で実装。
ボタン横に "Cmd/Ctrl+Enter" のヒントテキストも表示。

Closes #137
beforeEach が既に /tools/config-converter へ遷移済みのため、
新規テスト内の page.goto('/') と waitForReactHydration を削除。
- onKeyDown に isComposing ガードを追加(IME 誤発火防止)
- 検証ボタンに aria-keyshortcuts 属性を追加
- ヒントテキストに aria-hidden="true" を追加
- ヒント span を kbd 要素に変更(キーボード入力のセマンティクス)
- E2E テストの waitForFunction を expect().toHaveValue() に置換
@fumtas1k
fumtas1k force-pushed the feat/137-config-converter-schema-shortcut branch from c2b36be to 66fedb8 Compare April 30, 2026 10:03
@fumtas1k
fumtas1k merged commit a8966c9 into develop Apr 30, 2026
3 checks passed
@fumtas1k
fumtas1k deleted the feat/137-config-converter-schema-shortcut branch April 30, 2026 10:06
fumtas1k added a commit that referenced this pull request Apr 30, 2026
* docs: Cloudflare Web Analytics 導入を却下した決定を [051] として記録

プロジェクトの「外部送信ゼロ・外部リソースゼロ・トラッキングなし」方針と
Cloudflare Web Analytics(beacon 自動注入)が直接矛盾するため、有効化しない判断とした。
その経緯と却下理由を decisions.md に [051] として追記する。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* docs: [051] サーバー側集計ログに関する誤記を修正

Cloudflare Pages のトラフィック集計は Web Analytics を有効化しないと
表示されない。beacon なしで見られるという誤った記述を削除し、
正確に「把握する手段が無い」と修正する。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* chore: dev server / Playwright をサンドボックス除外コマンドに追加

`.claude/settings.json` のサンドボックスは listen() を拒否するため、
`npm run dev` / `npm run test:e2e` 経由の Astro dev server が
EPERM で起動できなかった。`excludedCommands` に dev / preview /
e2e / playwright / astro 系コマンドを追加し、ローカル listen を
必要とするコマンドだけサンドボックス外で実行できるようにする。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* chore: .claude/worktrees/ を gitignore に追加

Claude Code の worktrees ディレクトリは既に `.worktrees/` を ignore
しているが、Agent Teams 機能で生成される `.claude/worktrees/` が
別パスのため取り残されていた。誤コミット防止のため追加する。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* feat(tools): QRリーダーツールを新設(qr-reader)

カメラまたは画像ファイルからQRコードを読み取り、テキストを表示するツールを追加。

- `src/utils/qr-reader.ts`: URL/テキスト判別純粋関数 `detectQrContent()`
- `src/components/tools/QrReader.tsx`: カメラ+画像アップロード対応コンポーネント
- `src/pages/tools/qr-reader.astro`: Astro ページラッパー
- `src/data/tools.ts`: `qr-reader` エントリ追加(category: convert)
- ユニットテスト 15件(URL/テキスト判別の境界値を網羅)
- E2E テスト(.claude/settings.json 修正後に実行予定)
- SPEC.md / README.md / docs/decisions.md 更新

HTTP/HTTPS URL のみ検出してフィッシング警告付きリンクを表示。
javascript: 等の危険スキームは text として扱い自動リンク化しない([051])。
既存の useQrCamera フック・jsQR ライブラリを再利用して実装量を最小化。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* test(e2e): qr-reader の Playwright strict mode 違反を修正

- 'カメラ' ボタンの locator に exact: true を追加('カメラを起動' との多重マッチを解消)
- 'example.com' テキストの locator に exact: true を追加(URL 全体を含む <pre> との多重マッチを解消)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(qr-reader): コピーボタンと再スキャンボタンの高さ・スタイルを統一

- rescanButtonStyle を CopyButton と同じ視覚パラメータに合わせる
  (lineHeight 1 / padding 0.5rem 0.75rem / borderRadius 0.25rem / fontWeight 700)
- 両ボタンを flex flex-wrap items-center gap-2 でラップし
  横並びレイアウトを明示的に制御

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(qr-reader): ToolIcon.astro に qr-reader SVGアイコンを追加

QRの3つのファインダーパターン + データモジュール2点 + 水平スキャン線で構成。
qr-code(データモジュール4点)と造形を共有しつつ、スキャン線で「読取」を表現。

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(qr-reader): レビュー指摘を修正(必須2件 + 推奨5件)

必須対応:
- canvas.getContext('2d') の non-null assertion を削除し、null 時に
  エラーメッセージを表示するガードを追加(iOS Safari OOM 対策)
- 長辺 1600px 超の画像を jsQR 前にダウンスケールし、高解像度
  スマホ写真での OOM・フリーズを防止

推奨対応:
- input[type=file] を display:none から visually-hidden に変更し
  キーボード・スクリーンリーダーから操作可能にする(A11y)
- モード切替時に decoded / decodeError / cameraError をクリアし
  古い結果との並存を防止
- useEffect 依存配列の参照を const { stopCamera } = camera で
  分割代入し、exhaustive-deps 警告を回避
- E2E: not.toBeVisible() → toHaveCount(0) に変更(DOM から
  削除されることの検証+auto-retry の安定化)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* docs(qr-reader): e.target.value クリアの意図コメントを追加

同名ファイルの再選択を可能にするパターンであることを明示し、
onload 完了前にクリアする理由(File は file 変数で参照済み)を説明。

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat: 設定ファイル相互変換ツール (config-converter) を追加 (#133)

* feat: config-converter ユーティリティ層とユニットテストを実装

JSON/YAML/TOML/dotenv の相互変換・整形ユーティリティと
JSON Schema バリデーション(ajv draft-04/7対応)を追加。
TDD でテストを先行作成し、全 224 テストがパスすることを確認済み。

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat: config-converter 依存パッケージ追加とユーティリティ層の品質改善

- yaml, smol-toml, ajv, ajv-formats を package.json に明示追加
- toml.ts: stringifyToml に入力型ガードを追加
- dotenv.ts: quoteValue ヘルパで " を含む値のエスケープを修正
- yaml.ts: formatYaml にエラーハンドリングを追加
- schema-validator.ts: 非オブジェクトスキーマの入力ガードを追加
- テスト追加: schema-validator.test.ts 新規作成 (4件)、toml/dotenv テスト追加

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat: ConfigConverter React コンポーネントを実装

From/To ToggleGroup・InputField/OutputField・警告バナー・
JSON Schema 検証パネル(dynamic import)・DownloadButton を含む

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat: config-converter をツール一覧・Astro ページ・アイコンに追加

- src/data/tools.ts に Tool エントリ追加 (category: convert)
- src/pages/tools/config-converter.astro を作成
- src/components/ui/ToolIcon.astro に {} アイコンを追加

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* test(e2e): config-converter の E2E テストを追加

6ケース: YAML→JSON変換、JSON→YAML変換、ネスト拒否エラー、
クリア動作、JSON Schema 検証成功・失敗

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* docs: config-converter のドキュメントを更新

- README.md: 変換・解析テーブルに設定ファイル相互変換を追加
- SPEC.md: ライブラリテーブル・ツール一覧・ディレクトリ構成・実装チェックリストを更新
- docs/decisions.md: [051] yaml/smol-toml/ajv 選定理由・HCL後送り判断を記録

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: develop の変更をマージ(Cloudflare Analytics 決定 [051]、.gitignore 更新)

- docs/decisions.md: develop で追加された [051] Cloudflare Web Analytics 却下エントリを挿入し、
  当ブランチの config-converter ライブラリ選定エントリを [052] に繰り下げ
- .gitignore: develop で追加された `.claude/worktrees/` 除外ルールを追加

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(e2e): strict mode violation を修正(exact: true、パス文字列を使用)

- getByLabel('JSON') → getByLabel('JSON', { exact: true }): from=to=JSON 時に
  入力ラベル「JSON (整形)」と出力ラベル「JSON」の2要素にマッチする問題を解消
- getByRole('button', { name: '検証する' }) → { exact: true }: 「▶ JSON Schema で検証する」
  ボタンにも部分マッチしていた問題を解消
- getByText(/age/) → getByText('/age'): 正規表現がテキストエリア内の "age" を含む
  全要素にマッチしていた問題を解消(パスは "/age" とスラッシュ付きなので区別可能)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: レビュー指摘対応(Critical 1-3、推奨 4-5-7、提案 10)

[Critical 1] ajv-draft-04 を dependencies に明示登録
- package.json に "ajv-draft-04": "^1.0.0" を追加
- dev tree の transitive dep に依存していたためクリーンインストール時にビルド失敗の恐れがあった
- docs/decisions.md [052] にも理由を追記

[Critical 2] handleValidate: convert() の .output を参照していなかったバグを修正
- data = convert(output, to, 'json') → data = JSON.parse(convert(output, to, 'json').output)
- to=yaml/toml/dotenv 時にスキーマ検証対象が ConvertResult オブジェクトになっていた
- to=yaml でスキーマ検証が動作することを確認する E2E テストを追加

[Critical 3] .env パーサーがインラインコメントを無視していた問題を修正
- KEY=value # comment → value として取り込むよう \s+# パターンで除去
- クォート内の # は除去しない("value # not a comment" → そのまま保持)

[推奨 4] convert() の二重呼び出しを解消
- useRef<string[]> で warnings をキャプチャし、別 useEffect での再 convert() を廃止

[推奨 5] E2E テストの waitForTimeout(500) を削除
- expect(...).toBeVisible() の auto-retry に委ねる(7件全パス確認)

[推奨 7] formatLabel ヘルパー関数を削除
- FORMAT_LABELS[f] を直接参照するよう変更

[提案 10] ajv 起因の英語エラーメッセージを日本語に統一
- 日本語文字を含まないエラーメッセージは "JSON Schema の解析に失敗しました" で上書き

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: 再レビュー指摘対応(推奨 A・B・C、提案 D)

[推奨 A] parseDotenv インラインコメントのユニットテスト追加
- クォートなし値の # 除去、クォート内保持、URL フラグメント保持、
  クォート付き値+末尾コメントの 5 ケースを dotenv.test.ts に追加
- tests: 231 → 236 件

[推奨 B] クォート付き値 + 末尾コメント混在ケースを修正
- KEY="hello" # comment → "hello"(クォート残存)だったバグを修正
- 正規表現で /^"((?:[^"\\]|\\.)*)"\\s*(?:#.*)?$/ により
  クォート内容抽出と末尾コメント除去を一括処理
- ループ変数 raw とのシャドウイングを避けるため内部変数を rawValue に改名

[推奨 C] package.json dependencies をアルファベット順に整列
- ajv / ajv-draft-04 / ajv-formats / smol-toml を正しい位置に移動
- lockfile との整合性も改善

[提案 D] フォーマット切替時に validationResult をクリア
- handleFromChange / handleToChange に setValidationResult(null) を追加
- 切替後の出力に対して古い検証結果が残る誤認を防止

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(config-converter): 出力変更時に validationResult をクリアする

入力を編集して output が変わっても古いスキーマ検証結果(成功/失敗)が
残り続ける問題を修正。output/error を監視する既存 useEffect に
setValidationResult(null) を追加し、出力変化と同時に検証結果を無効化する。

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: サブエージェントが PR レビューコメントを投稿できる権限を追加 (#142)

サブエージェントには対話 UI が無いため、未承認の tool 呼び出しは即拒否される。
PR レビュー作業を並列化する際にレビュー本文の投稿および TMPDIR への一時ファイル
書き出しが拒否されていた問題を解消する。

- Bash(gh pr review*) / Bash(gh pr comment*) を allow に追加
  - PR メタデータを変更する gh pr edit/create/merge/close は ask に残置
- Write(/tmp/claude*/**) / Write(/private/tmp/claude*/**) を allow に追加
  - サンドボックス TMPDIR 配下への一時ファイル書き出しを許可

* docs: PR ベースは必ず develop と CLAUDE.md 最重要ルールに昇格 (#143)

`docs/shared-agent-rules.md §6.3` に既に明記されているが、Claude Code の
system prompt が毎セッション "Main branch (you will usually use this for PRs): main"
と表示してくるため、shared-agent-rules.md を読まずに main 向け PR を作る事故が
繰り返し発生していた。

CLAUDE.md は毎セッション読み込まれるため、最重要ルールの 1 行に昇格させて
即時可視化する。

* fix(qr-reader): 非HTTPS環境で専用エラーメッセージを表示 (#135)

navigator.mediaDevices が undefined の場合に getUserMedia を呼ぶと
TypeError となり「カメラの起動に失敗しました」という汎用メッセージ
が出て原因が特定できなかった。startCamera 冒頭で早期 guard を入れ、
HTTPS または localhost が必要である旨を明示する。

* fix(config-converter): parseDotenv でエスケープシーケンスを解除 (#140)

stringifyDotenv は値の \\ と \" をエスケープして書き出すが、
parseDotenv 側でアンエスケープしておらず、ラウンドトリップが
壊れていた。

unescapeDotenv ヘルパーを追加し、DQ/SQ ブランチで適用。単発走査で
クォート種・バックスラッシュのみアンエスケープするため、
\\\" のような連鎖でも順序問題なく安全に解除できる。

テスト 5 件追加(DQ 内 \"・\\・連鎖、SQ 内 \'、ラウンドトリップ)。

* test(qr-reader): HTTPS 必須メッセージの E2E テストを追加 (#135)

レビュー指摘(feedback_e2e_tests)対応。Playwright の addInitScript で
navigator.mediaDevices を undefined にし、カメラ起動時に新しいエラー
メッセージが表示されることを検証する E2E ケースを追加。

* docs(decisions): .env パーサに expand オプション追加余地を明記

レビューでの指摘を受け、ダブルクォート内のエスケープ方針と
将来 `expand` オプション(POSIX dotenv 相当の `\n` 改行展開)を
追加できる旨を 052 章 .env セクションに 1 行追記。

* docs(rules): 先送り時の issue 化を 6.4 章として追加

「別 PR で対応」「後で追記」と口頭で先送りすると形骸化して
実施されないことがレビューで指摘されたため、共通規約に
「先送り時は必ず issue を起票し PR に番号を貼る」ルールを追加。

* test(qr-reader): HTTPS guard テストの再ナビゲート意図をコメント明記

レビュー任意指摘 (#144) を受け、beforeEach で goto 済みでも
addInitScript は次回ロードから反映されるため再 goto が必要、
という意図を 1 行コメントで補足。挙動変更なし。

* ci: developブランチへのプッシュ時もカバレッジバッジを更新する (#146)

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(config-converter): 変換先フォーマット変更時に入力テキストを保持する (#147)

* fix(config-converter): 変換先変更時に入力テキストを保持する

変換先のみ変更した場合、handleToChange から reset() の呼び出しを削除し
入力テキストを保持したまま出力のみ再計算するよう修正。
変換元変更時(handleFromChange)は従来どおり入力をクリアする。

Closes #141

* test(config-converter): E2E テストのナビゲーションパターンを修正

beforeEach が既に /tools/config-converter へ遷移済みのため、
新規テスト内の page.goto('/') と waitForReactHydration を削除。

* test(config-converter): E2E テストのロケータとアサーションをプロジェクト規約に準拠させる

- waitForFunction + querySelectorAll を expect().toHaveValue() に置換
- locator('textarea[readonly]') を getByLabel() に置換
- toContainText を toHaveValue に置換(textarea は value を検証する必要があるため)

docs/ui-conventions.md 3.2 章のロケータ規約に準拠。

* feat(config-converter): JSON Schema 検証パネルに Cmd/Ctrl+Enter ショートカットを追加 (#148)

スキーマ textarea に Cmd+Enter (Mac) / Ctrl+Enter (Win/Linux) で検証を実行するショートカットを追加。InputField に optional な onKeyDown prop を追加して forward する形で実装。isComposing ガード・aria-keyshortcuts・kbd 要素によるアクセシビリティ対応も含む。

Closes #137

* ci: カバレッジバッジ更新を Gist 経由に変更 (#151)

ブランチ保護ルールにより github-actions[bot] からの develop / main への
直接 push が拒否されるため、外部 Gist に JSON を書き込み shields.io の
endpoint バッジで表示する方式に切り替える。

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
fumtas1k added a commit that referenced this pull request Apr 30, 2026
* docs: Cloudflare Web Analytics 導入を却下した決定を [051] として記録

プロジェクトの「外部送信ゼロ・外部リソースゼロ・トラッキングなし」方針と
Cloudflare Web Analytics(beacon 自動注入)が直接矛盾するため、有効化しない判断とした。
その経緯と却下理由を decisions.md に [051] として追記する。



* docs: [051] サーバー側集計ログに関する誤記を修正

Cloudflare Pages のトラフィック集計は Web Analytics を有効化しないと
表示されない。beacon なしで見られるという誤った記述を削除し、
正確に「把握する手段が無い」と修正する。



* chore: dev server / Playwright をサンドボックス除外コマンドに追加

`.claude/settings.json` のサンドボックスは listen() を拒否するため、
`npm run dev` / `npm run test:e2e` 経由の Astro dev server が
EPERM で起動できなかった。`excludedCommands` に dev / preview /
e2e / playwright / astro 系コマンドを追加し、ローカル listen を
必要とするコマンドだけサンドボックス外で実行できるようにする。



* chore: .claude/worktrees/ を gitignore に追加

Claude Code の worktrees ディレクトリは既に `.worktrees/` を ignore
しているが、Agent Teams 機能で生成される `.claude/worktrees/` が
別パスのため取り残されていた。誤コミット防止のため追加する。



* feat(tools): QRリーダーツールを新設(qr-reader)

カメラまたは画像ファイルからQRコードを読み取り、テキストを表示するツールを追加。

- `src/utils/qr-reader.ts`: URL/テキスト判別純粋関数 `detectQrContent()`
- `src/components/tools/QrReader.tsx`: カメラ+画像アップロード対応コンポーネント
- `src/pages/tools/qr-reader.astro`: Astro ページラッパー
- `src/data/tools.ts`: `qr-reader` エントリ追加(category: convert)
- ユニットテスト 15件(URL/テキスト判別の境界値を網羅)
- E2E テスト(.claude/settings.json 修正後に実行予定)
- SPEC.md / README.md / docs/decisions.md 更新

HTTP/HTTPS URL のみ検出してフィッシング警告付きリンクを表示。
javascript: 等の危険スキームは text として扱い自動リンク化しない([051])。
既存の useQrCamera フック・jsQR ライブラリを再利用して実装量を最小化。



* test(e2e): qr-reader の Playwright strict mode 違反を修正

- 'カメラ' ボタンの locator に exact: true を追加('カメラを起動' との多重マッチを解消)
- 'example.com' テキストの locator に exact: true を追加(URL 全体を含む <pre> との多重マッチを解消)



* fix(qr-reader): コピーボタンと再スキャンボタンの高さ・スタイルを統一

- rescanButtonStyle を CopyButton と同じ視覚パラメータに合わせる
  (lineHeight 1 / padding 0.5rem 0.75rem / borderRadius 0.25rem / fontWeight 700)
- 両ボタンを flex flex-wrap items-center gap-2 でラップし
  横並びレイアウトを明示的に制御



* feat(qr-reader): ToolIcon.astro に qr-reader SVGアイコンを追加

QRの3つのファインダーパターン + データモジュール2点 + 水平スキャン線で構成。
qr-code(データモジュール4点)と造形を共有しつつ、スキャン線で「読取」を表現。



* fix(qr-reader): レビュー指摘を修正(必須2件 + 推奨5件)

必須対応:
- canvas.getContext('2d') の non-null assertion を削除し、null 時に
  エラーメッセージを表示するガードを追加(iOS Safari OOM 対策)
- 長辺 1600px 超の画像を jsQR 前にダウンスケールし、高解像度
  スマホ写真での OOM・フリーズを防止

推奨対応:
- input[type=file] を display:none から visually-hidden に変更し
  キーボード・スクリーンリーダーから操作可能にする(A11y)
- モード切替時に decoded / decodeError / cameraError をクリアし
  古い結果との並存を防止
- useEffect 依存配列の参照を const { stopCamera } = camera で
  分割代入し、exhaustive-deps 警告を回避
- E2E: not.toBeVisible() → toHaveCount(0) に変更(DOM から
  削除されることの検証+auto-retry の安定化)



* docs(qr-reader): e.target.value クリアの意図コメントを追加

同名ファイルの再選択を可能にするパターンであることを明示し、
onload 完了前にクリアする理由(File は file 変数で参照済み)を説明。



* feat: 設定ファイル相互変換ツール (config-converter) を追加 (#133)

* feat: config-converter ユーティリティ層とユニットテストを実装

JSON/YAML/TOML/dotenv の相互変換・整形ユーティリティと
JSON Schema バリデーション(ajv draft-04/7対応)を追加。
TDD でテストを先行作成し、全 224 テストがパスすることを確認済み。



* feat: config-converter 依存パッケージ追加とユーティリティ層の品質改善

- yaml, smol-toml, ajv, ajv-formats を package.json に明示追加
- toml.ts: stringifyToml に入力型ガードを追加
- dotenv.ts: quoteValue ヘルパで " を含む値のエスケープを修正
- yaml.ts: formatYaml にエラーハンドリングを追加
- schema-validator.ts: 非オブジェクトスキーマの入力ガードを追加
- テスト追加: schema-validator.test.ts 新規作成 (4件)、toml/dotenv テスト追加



* feat: ConfigConverter React コンポーネントを実装

From/To ToggleGroup・InputField/OutputField・警告バナー・
JSON Schema 検証パネル(dynamic import)・DownloadButton を含む



* feat: config-converter をツール一覧・Astro ページ・アイコンに追加

- src/data/tools.ts に Tool エントリ追加 (category: convert)
- src/pages/tools/config-converter.astro を作成
- src/components/ui/ToolIcon.astro に {} アイコンを追加



* test(e2e): config-converter の E2E テストを追加

6ケース: YAML→JSON変換、JSON→YAML変換、ネスト拒否エラー、
クリア動作、JSON Schema 検証成功・失敗



* docs: config-converter のドキュメントを更新

- README.md: 変換・解析テーブルに設定ファイル相互変換を追加
- SPEC.md: ライブラリテーブル・ツール一覧・ディレクトリ構成・実装チェックリストを更新
- docs/decisions.md: [051] yaml/smol-toml/ajv 選定理由・HCL後送り判断を記録



* chore: develop の変更をマージ(Cloudflare Analytics 決定 [051]、.gitignore 更新)

- docs/decisions.md: develop で追加された [051] Cloudflare Web Analytics 却下エントリを挿入し、
  当ブランチの config-converter ライブラリ選定エントリを [052] に繰り下げ
- .gitignore: develop で追加された `.claude/worktrees/` 除外ルールを追加



* fix(e2e): strict mode violation を修正(exact: true、パス文字列を使用)

- getByLabel('JSON') → getByLabel('JSON', { exact: true }): from=to=JSON 時に
  入力ラベル「JSON (整形)」と出力ラベル「JSON」の2要素にマッチする問題を解消
- getByRole('button', { name: '検証する' }) → { exact: true }: 「▶ JSON Schema で検証する」
  ボタンにも部分マッチしていた問題を解消
- getByText(/age/) → getByText('/age'): 正規表現がテキストエリア内の "age" を含む
  全要素にマッチしていた問題を解消(パスは "/age" とスラッシュ付きなので区別可能)



* fix: レビュー指摘対応(Critical 1-3、推奨 4-5-7、提案 10)

[Critical 1] ajv-draft-04 を dependencies に明示登録
- package.json に "ajv-draft-04": "^1.0.0" を追加
- dev tree の transitive dep に依存していたためクリーンインストール時にビルド失敗の恐れがあった
- docs/decisions.md [052] にも理由を追記

[Critical 2] handleValidate: convert() の .output を参照していなかったバグを修正
- data = convert(output, to, 'json') → data = JSON.parse(convert(output, to, 'json').output)
- to=yaml/toml/dotenv 時にスキーマ検証対象が ConvertResult オブジェクトになっていた
- to=yaml でスキーマ検証が動作することを確認する E2E テストを追加

[Critical 3] .env パーサーがインラインコメントを無視していた問題を修正
- KEY=value # comment → value として取り込むよう \s+# パターンで除去
- クォート内の # は除去しない("value # not a comment" → そのまま保持)

[推奨 4] convert() の二重呼び出しを解消
- useRef<string[]> で warnings をキャプチャし、別 useEffect での再 convert() を廃止

[推奨 5] E2E テストの waitForTimeout(500) を削除
- expect(...).toBeVisible() の auto-retry に委ねる(7件全パス確認)

[推奨 7] formatLabel ヘルパー関数を削除
- FORMAT_LABELS[f] を直接参照するよう変更

[提案 10] ajv 起因の英語エラーメッセージを日本語に統一
- 日本語文字を含まないエラーメッセージは "JSON Schema の解析に失敗しました" で上書き



* fix: 再レビュー指摘対応(推奨 A・B・C、提案 D)

[推奨 A] parseDotenv インラインコメントのユニットテスト追加
- クォートなし値の # 除去、クォート内保持、URL フラグメント保持、
  クォート付き値+末尾コメントの 5 ケースを dotenv.test.ts に追加
- tests: 231 → 236 件

[推奨 B] クォート付き値 + 末尾コメント混在ケースを修正
- KEY="hello" # comment → "hello"(クォート残存)だったバグを修正
- 正規表現で /^"((?:[^"\\]|\\.)*)"\\s*(?:#.*)?$/ により
  クォート内容抽出と末尾コメント除去を一括処理
- ループ変数 raw とのシャドウイングを避けるため内部変数を rawValue に改名

[推奨 C] package.json dependencies をアルファベット順に整列
- ajv / ajv-draft-04 / ajv-formats / smol-toml を正しい位置に移動
- lockfile との整合性も改善

[提案 D] フォーマット切替時に validationResult をクリア
- handleFromChange / handleToChange に setValidationResult(null) を追加
- 切替後の出力に対して古い検証結果が残る誤認を防止



* fix(config-converter): 出力変更時に validationResult をクリアする

入力を編集して output が変わっても古いスキーマ検証結果(成功/失敗)が
残り続ける問題を修正。output/error を監視する既存 useEffect に
setValidationResult(null) を追加し、出力変化と同時に検証結果を無効化する。



---------



* chore: サブエージェントが PR レビューコメントを投稿できる権限を追加 (#142)

サブエージェントには対話 UI が無いため、未承認の tool 呼び出しは即拒否される。
PR レビュー作業を並列化する際にレビュー本文の投稿および TMPDIR への一時ファイル
書き出しが拒否されていた問題を解消する。

- Bash(gh pr review*) / Bash(gh pr comment*) を allow に追加
  - PR メタデータを変更する gh pr edit/create/merge/close は ask に残置
- Write(/tmp/claude*/**) / Write(/private/tmp/claude*/**) を allow に追加
  - サンドボックス TMPDIR 配下への一時ファイル書き出しを許可

* docs: PR ベースは必ず develop と CLAUDE.md 最重要ルールに昇格 (#143)

`docs/shared-agent-rules.md §6.3` に既に明記されているが、Claude Code の
system prompt が毎セッション "Main branch (you will usually use this for PRs): main"
と表示してくるため、shared-agent-rules.md を読まずに main 向け PR を作る事故が
繰り返し発生していた。

CLAUDE.md は毎セッション読み込まれるため、最重要ルールの 1 行に昇格させて
即時可視化する。

* fix(qr-reader): 非HTTPS環境で専用エラーメッセージを表示 (#135)

navigator.mediaDevices が undefined の場合に getUserMedia を呼ぶと
TypeError となり「カメラの起動に失敗しました」という汎用メッセージ
が出て原因が特定できなかった。startCamera 冒頭で早期 guard を入れ、
HTTPS または localhost が必要である旨を明示する。

* fix(config-converter): parseDotenv でエスケープシーケンスを解除 (#140)

stringifyDotenv は値の \\ と \" をエスケープして書き出すが、
parseDotenv 側でアンエスケープしておらず、ラウンドトリップが
壊れていた。

unescapeDotenv ヘルパーを追加し、DQ/SQ ブランチで適用。単発走査で
クォート種・バックスラッシュのみアンエスケープするため、
\\\" のような連鎖でも順序問題なく安全に解除できる。

テスト 5 件追加(DQ 内 \"・\\・連鎖、SQ 内 \'、ラウンドトリップ)。

* test(qr-reader): HTTPS 必須メッセージの E2E テストを追加 (#135)

レビュー指摘(feedback_e2e_tests)対応。Playwright の addInitScript で
navigator.mediaDevices を undefined にし、カメラ起動時に新しいエラー
メッセージが表示されることを検証する E2E ケースを追加。

* docs(decisions): .env パーサに expand オプション追加余地を明記

レビューでの指摘を受け、ダブルクォート内のエスケープ方針と
将来 `expand` オプション(POSIX dotenv 相当の `\n` 改行展開)を
追加できる旨を 052 章 .env セクションに 1 行追記。

* docs(rules): 先送り時の issue 化を 6.4 章として追加

「別 PR で対応」「後で追記」と口頭で先送りすると形骸化して
実施されないことがレビューで指摘されたため、共通規約に
「先送り時は必ず issue を起票し PR に番号を貼る」ルールを追加。

* test(qr-reader): HTTPS guard テストの再ナビゲート意図をコメント明記

レビュー任意指摘 (#144) を受け、beforeEach で goto 済みでも
addInitScript は次回ロードから反映されるため再 goto が必要、
という意図を 1 行コメントで補足。挙動変更なし。

* ci: developブランチへのプッシュ時もカバレッジバッジを更新する (#146)



* fix(config-converter): 変換先フォーマット変更時に入力テキストを保持する (#147)

* fix(config-converter): 変換先変更時に入力テキストを保持する

変換先のみ変更した場合、handleToChange から reset() の呼び出しを削除し
入力テキストを保持したまま出力のみ再計算するよう修正。
変換元変更時(handleFromChange)は従来どおり入力をクリアする。

Closes #141

* test(config-converter): E2E テストのナビゲーションパターンを修正

beforeEach が既に /tools/config-converter へ遷移済みのため、
新規テスト内の page.goto('/') と waitForReactHydration を削除。

* test(config-converter): E2E テストのロケータとアサーションをプロジェクト規約に準拠させる

- waitForFunction + querySelectorAll を expect().toHaveValue() に置換
- locator('textarea[readonly]') を getByLabel() に置換
- toContainText を toHaveValue に置換(textarea は value を検証する必要があるため)

docs/ui-conventions.md 3.2 章のロケータ規約に準拠。

* feat(config-converter): JSON Schema 検証パネルに Cmd/Ctrl+Enter ショートカットを追加 (#148)

スキーマ textarea に Cmd+Enter (Mac) / Ctrl+Enter (Win/Linux) で検証を実行するショートカットを追加。InputField に optional な onKeyDown prop を追加して forward する形で実装。isComposing ガード・aria-keyshortcuts・kbd 要素によるアクセシビリティ対応も含む。

Closes #137

* ci: カバレッジバッジ更新を Gist 経由に変更 (#151)

ブランチ保護ルールにより github-actions[bot] からの develop / main への
直接 push が拒否されるため、外部 Gist に JSON を書き込み shields.io の
endpoint バッジで表示する方式に切り替える。



---------

Co-authored-by: takasefumiyuki <71715982+fumtas1k@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(config-converter): JSON Schema 検証パネルに Cmd/Ctrl+Enter ショートカットを追加

1 participant