-
Notifications
You must be signed in to change notification settings - Fork 2
Description
Note: このIssueは 2026-02-20 にレビュー結果を反映して更新されました。
詳細: dev-reports/issue/321/issue-review/
概要
MemoCardコンポーネントにコピーボタンを追加し、メモのコンテンツ(content フィールド)をクリップボードにコピーできるようにする。
背景・課題
Worktreeのメモには重要なコードスニペット、コマンド、作業メモが記録されることが多い。
現在、メモ内容を他のツールやClaude CLIプロンプトに貼り付けるには、テキストを手動で選択してコピーする必要があり、操作が非効率である。
プロジェクト内の他コンポーネント(FileViewer、HistoryPane、MarkdownEditor、LogViewer)にはコピー機能が実装済みであり、MemoCardにも同等の機能を追加することで操作の一貫性を確保する。
ユースケース例:
- メモの内容をClaude CLIへのプロンプトに貼り付けたい
- メモの内容を別のworktreeのメモにコピーしたい
- メモの内容を外部ツール(Slack、エディタ等)に共有したい
提案する解決策
コピー対象
- コピー対象は
content(メモ本文)のみ とする title(メモタイトル)はコピー対象外(既存パターンに合わせ、本文コンテンツのみをコピーする方針)
UI実装パターン
FileViewer方式(パターンA)を採用する。
- Copy/Checkアイコン切替(lucide-react の
Copy/Checkアイコン) - クリック後2秒間Checkアイコンに変化し、その後Copyアイコンに戻る
- サイレントエラーハンドリング(Toast通知は使用しない)
採用理由: MemoCardは現時点で showToast propsを受け取っていないため、Toast通知方式(パターンB)を採用するとpropsの追加やMemoPane側の変更も必要になる。FileViewerパターンを踏襲することで、変更範囲を最小限に抑え、既存UIとの一貫性を保つ。
UI配置
- MemoCardのヘッダー部分(タイトル行)の右側に配置
- 既存の削除ボタンと並べて表示
- ボタンのスタイルは既存の削除ボタンと統一
使用する既存基盤
src/lib/clipboard-utils.tsのcopyToClipboard()関数を使用(ANSI除去・空文字バリデーション済み)- lucide-reactの
Copy/Checkアイコン(プロジェクト内で使用済み) src/components/worktree/FileViewer.tsxのCopy/Checkアイコン切替パターンを参考実装として踏襲
受け入れ条件
- MemoCardにコピーボタン(Copyアイコン)が表示される
- コピーボタンをクリックするとメモの
contentがクリップボードにコピーされる - クリック後2秒間、アイコンがCopyからCheckに切り替わる
- 2秒後にアイコンがCopyに戻る
-
contentが空の場合、コピーボタンが無効化(disabled)またはコピー処理が実行されない(clipboard-utils.tsの空文字バリデーション準拠) - TypeScript型チェックが通る(
npx tsc --noEmit) - ESLintエラーが0件(
npm run lint) - 単体テストが追加されパスする(
tests/unit/components/worktree/MemoCard.test.tsx) - モバイル画面幅でヘッダー部の4要素(タイトル入力 + Saving + コピーボタン + 削除ボタン)のレイアウトが崩れないこと(タイトル入力の最小幅が十分であることを確認)
テスト要件
以下のテストケースを tests/unit/components/worktree/MemoCard.test.tsx に追加する:
- コピーボタンがレンダリングされること
- コピーボタンクリック時に
copyToClipboardが正しい引数(content)で呼ばれること - コピー成功後にCheckアイコンが表示されること
- 2秒後にCopyアイコンに戻ること
contentが空のメモでコピーボタンの適切な振る舞い(disabled or no-op)
モック指針: copyToClipboard 関数は vi.mock('@/lib/clipboard-utils') でモック化する。テスト環境(jsdom)では navigator.clipboard APIが利用できないため、関数レベルのモックが必須。MarkdownEditor等の既存テストパターン(tests/unit/components/MarkdownEditor.test.tsx)を参考にすること。
影響範囲
| ファイル | 変更内容 |
|---|---|
src/components/worktree/MemoCard.tsx |
コピーボタン追加(lucide-reactのCopy/Checkアイコンimport、useState(copied)、handleCopyコールバック、ヘッダー部JSX追加) |
tests/unit/components/worktree/MemoCard.test.tsx |
コピー機能テスト追加(vi.mock('@/lib/clipboard-utils')によるモック設定含む) |
CLAUDE.md |
MemoCard.tsxのモジュール説明追加(Issue #321: コピーボタン追加、Copy/Checkアイコン切替) |
変更なしのファイル(確認済み)
| ファイル | 変更なしの根拠 |
|---|---|
src/components/worktree/MemoPane.tsx |
MemoCardPropsインタフェースの変更なし。コピー機能はMemoCard内部に閉じた状態管理(useState)とコールバックで実装されるため、propsの伝播変更は不要。 |
tests/unit/components/worktree/MemoPane.test.tsx |
aria-labelベースのボタン取得(getAllByRole('button', { name: /delete/i }))を使用しているため、MemoCardへのコピーボタン追加による既存テストの破壊は発生しない。ただし、実装時にはMemoPane.test.tsxの全テストが引き続きパスすることを確認すること。 |
src/lib/clipboard-utils.ts |
copyToClipboard()関数の提供元。MemoCardから新規importされるが、関数自体の変更は不要。 |
src/types/models.ts |
WorktreeMemo型定義への変更なし。コピー対象は既存のcontentフィールドであり、新規フィールドやDBスキーマの変更は発生しない。 |
src/components/worktree/WorktreeDetailRefactored.tsx |
MemoPaneの親コンポーネントだが、MemoCardの内部変更に影響されない。 |
i18n翻訳ファイルへの影響
翻訳ファイル(locales/en/, locales/ja/)への変更は不要。aria-labelはFileViewerの既存パターンに合わせてハードコードとする(i18n一括対応は別Issue)。
補足情報
- タイトル(
titleフィールド)のコピーは本スコープ外とする - コピー成功/失敗のトースト通知はFileViewerと同様に省略(アイコン切替のみでフィードバック)
- aria-labelはFileViewerの既存パターンに合わせてハードコードとする(i18n一括対応は別Issue)
- 推奨値:
'Copy memo content'(既存パターン: FileViewer'Copy file content'、ConversationPairCard'Copy message'。'Copy [対象] content'の命名規則に従う)
- 推奨値:
- CLAUDE.mdへのMemoCard.tsxモジュール説明追記が必要(実装PRに含めること)
- 既存の削除ボタンはインラインSVGで実装されているが、コピーボタンはlucide-react(Copy/Check)を使用する。アイコン実装方式の統一(削除ボタンのlucide-react移行)は本Issueのスコープ外とし、別途リファクタリングIssueで対応する
関連Issue / 参考実装
- Issue ファイル機能強化 #162: FileViewerへのコピーボタン追加
- Issue 履歴から過去の入力やレスポンスをコピー可能にしたい #211: clipboard-utils.ts導入
- 参考実装:
src/components/worktree/FileViewer.tsx(Copy/Checkアイコン切替パターン)
レビュー履歴
Stage 2: 通常レビュー指摘事項反映 (2026-02-20)
- Issue内容の初期レビュー結果を反映
Stage 4: 影響範囲レビュー指摘事項反映 (2026-02-20)
- F101: 影響範囲テーブルにMemoPane.tsxを「変更なし」として明記
- F102: MemoPane.test.tsxが影響を受けない根拠を明記(aria-labelベース取得のため破壊なし)
- F103: WorktreeMemo型定義・DBスキーマ変更不要の根拠を明記
- F104: i18n翻訳ファイルへの変更が不要であることを明記
- F105: CLAUDE.mdへのモジュール説明追記を影響範囲テーブルに追加
- F106: aria-labelの推奨値 'Copy memo content' を補足情報に明記
- F107: モバイルUIでの4要素レイアウト確認を受け入れ条件に追加
- F108: テスト要件にvi.mock('@/lib/clipboard-utils')によるモック指針を追記
Stage 6: 2回目通常レビュー指摘事項反映 (2026-02-20)
- F201: 削除ボタンとコピーボタンのアイコン実装方式混在について補足情報に注記を追加(lucide-react統一は別Issueスコープ)
- F202: copyToClipboard()の2段階ガード詳細はFileViewerパターン踏襲で自然に解決されるためスキップ
Stage 8: 2回目影響範囲レビュー指摘事項反映 (2026-02-20)
- F301: テスト要件のモック参考先を「FileViewer等の既存テストパターン」から「MarkdownEditor等の既存テストパターン(
tests/unit/components/MarkdownEditor.test.tsx)」に修正