レシピ#6-7: コラボレーションでアプリを進化させる

06章で作った個人用アプリは「動く」状態です。しかし、実際に使い始めると、改善点や「こんな機能があったら」というアイデアが次々と浮かんできます。

一人で作り、みんなで育てる。

このレシピでは、コラボレーションを通じてアプリを進化させる方法を学びます。特に、特化型機能の追加は、BYOAの真価を発揮する重要な進化の手段です。


なぜコラボレーションが重要か

06章で作ったアプリの状態

✅ 基本機能は動く
✅ 自分の用途には足りる
✅ LocalStorageでデータ保存

でも...
❓ バグがあるかもしれない
❓ もっと良い書き方があるかも
❓ 他の人も同じ課題を持っているのでは?
❓ 「こんな機能があったら」が次々浮かぶ

コラボレーションがもたらすもの

【視点の多様化】
- 一人では気づかないバグを発見
- 使い方の工夫を共有
- 新しいアイデアの創発

【品質の向上】
- コードレビューで可読性改善
- エッジケースへの対応
- セキュリティの強化

【機能の進化】
- ユーザーの声から機能拡張
- 特化型機能の開発
- コミュニティの成長

コラボレーションの3つのパターン

パターン1: セルフ改善 + AI

自分 + Claude で改善を重ねる

あなたが:
「このToDoアプリ、もっと良くしたい」

Claude に:
1. 既存コードをレビュー依頼
2. 「フィルター機能を追加したい」と相談
3. バグ修正の方法を質問
4. パフォーマンス改善を依頼

メリット: - 自分のペースで進められる - 即座にフィードバックが得られる - 学びながら改善できる

具体的な流れ:

# Claude Projects を作成

1. Project に既存コードをアップロード
2. カスタム指示を設定:
   「このToDoアプリのコードレビューと改善提案をしてください。
    特に、可読性とパフォーマンスに注目してください。」

3. 段階的に改善:
   - 「変数名をもっとわかりやすくして」
   - 「この関数を分割した方がいい?」
   - 「エラーハンドリングを追加して」

パターン2: 非開発者 ⇄ 開発者

GitHubで公開し、開発者の力を借りる

【あなた(非開発者)の役割】
✅ アプリの目的・用途を明確に説明
✅ 欲しい機能をわかりやすく伝える
✅ テスト・フィードバック

【開発者の役割】
✅ コードレビュー
✅ パフォーマンス改善
✅ 難しい機能の実装
✅ ベストプラクティスの提案

具体的な流れ:

# Step 1: GitHub にリポジトリ作成
# まずローカルでコミットを作成
git init
git add .
git commit -m "Initial commit: Simple ToDo app"

# 次に GitHub 上で空のリポジトリを作成(Webの「New repository」から)
# 作成後に表示されるURLをリモートとして登録し、プッシュする
git remote add origin https://github.com/<ユーザー名>/<リポジトリ名>.git
git push -u origin main

# Step 2: README に明確な説明
## このアプリについて
- 目的: 個人用のシンプルなToDoリスト
- 対象: プログラミング初心者
- 技術: HTML/CSS/JavaScript + LocalStorage

## 改善したいこと
- [ ] フィルター機能(全て/未完了/完了)
- [ ] コードの整理
- [ ] バグ修正

# Step 3: Issue テンプレート作成
.github/ISSUE_TEMPLATE/feature_request.md

開発者からのフィードバック例:

【Issue: コードレビュー】

素晴らしいアプリですね!いくつか改善提案があります:

1. 関数の分割
   現在の `addTask()` が長すぎるので、以下に分割することを提案します:
   - `createTaskElement()`
   - `saveToLocalStorage()`
   - `renderTaskList()`

2. エラーハンドリング
   LocalStorage が使えない環境への対応を追加しましょう

3. アクセシビリティ
   キーボード操作への対応を改善できます

Pull Request を送りますので、レビューお願いします!

パターン3: コミュニティ駆動

同じ課題を持つ人たちと協力して育てる

【育児アプリの例】

最初: あなたが自分の子供のために作成

公開後:
- Issue: 「双子の記録を分けたい」
- Issue: 「祖父母と共有したい」
- Issue: 「成長グラフを追加して欲しい」

コミュニティで:
→ 保育士さんがUI改善を提案
→ 別の親が翻訳を追加
→ 開発者が技術的な改善
→ みんなで使いながら育てる

コミュニティの力:

一人で作る:
- 視点が限定的
- 気づかないバグ
- モチベーション維持が難しい

コミュニティで育てる:
- 多様な視点・ニーズ
- バグを早期発見
- 「誰かの役に立つ」喜び
- 継続的な改善のサイクル

コラボで実現する進化の種類

1. 品質向上

コードレビューで可読性改善

// 改善前
function a(x) {
  let y = []
  for(let i=0;i<x.length;i++){
    if(!x[i].d)y.push(x[i])
  }
  return y
}

// 改善後(コラボで)
function getIncompleteTasks(tasks) {
  return tasks.filter(task => !task.isDone)
}

エラーハンドリングの追加

// 改善前
function saveTasks(tasks) {
  localStorage.setItem('tasks', JSON.stringify(tasks))
}

// 改善後(コラボで)
function saveTasks(tasks) {
  try {
    localStorage.setItem('tasks', JSON.stringify(tasks))
  } catch (error) {
    if (error.name === 'QuotaExceededError') {
      alert('保存容量が不足しています。古いタスクを削除してください。')
    } else {
      console.error('保存に失敗しました:', error)
      alert('データの保存に失敗しました。')
    }
  }
}

2. 機能拡張

ユーザーからの要望を実装

【ToDoアプリの進化例】

v1.0(06章で作成)
- タスクの追加
- 完了チェック
- 削除

↓ コミュニティからの要望

v1.1
- フィルター(全て/未完了/完了)
- 検索機能
- 並び替え(日付/優先度)

v1.2
- 期限設定
- 優先度(高/中/低)
- カテゴリー分類

v1.3
- データエクスポート(CSV/JSON)
- テーマ切り替え(ライト/ダーク)
- タスクのアーカイブ

3. 特化型機能の追加(重要な進化の手段)

ここがBYOAの真価!

汎用的なアプリから、自分の領域に特化した価値を生み出すのがBYOAの最大の強みです。

なぜ特化型機能が重要か

✅ データを自分で持っているから自由に加工できる
✅ 既製品では実現できない独自の価値
✅ 当事者だからこそ見える「本当に欲しい機能」
✅ コミュニティの成長エンジンになる

【例1】育児アプリ → アルバム作成機能

基本アプリ(06章で作成)

<!-- 日々の記録 -->
<div class="record">
  <input type="date" id="date">
  <input type="number" placeholder="身長(cm)">
  <input type="number" placeholder="体重(g)">
  <textarea placeholder="今日のできごと"></textarea>
  <input type="file" accept="image/*">
  <button>記録</button>
</div>

↓ コラボで特化型機能を追加

アルバム自動生成機能

// 記録した写真から月別アルバムを自動生成
function generateMonthlyAlbum(year, month) {
  const records = getRecordsByMonth(year, month)

  return `
<!DOCTYPE html>
<html>
<head>
  <title>${year}年${month}月の成長記録</title>
  <style>
    .album-page {
      width: 210mm;
      height: 297mm; /* A4サイズ */
      padding: 20mm;
      page-break-after: always;
    }
    .photo-grid {
      display: grid;
      grid-template-columns: repeat(2, 1fr);
      gap: 10mm;
    }
    .record-item {
      border: 1px solid #ddd;
      padding: 10px;
    }
    .growth-chart {
      margin-top: 20px;
    }
  </style>
</head>
<body>
  <div class="album-page">
    <h1>${year}年${month}月の成長</h1>

    <!-- 成長グラフ -->
    <div class="growth-chart">
      <canvas id="growthChart"></canvas>
    </div>

    <!-- 写真グリッド -->
    <div class="photo-grid">
      ${records.map(r => `
        <div class="record-item">
          <img src="${r.photo}" alt="${r.date}">
          <p>${r.date}: ${r.note}</p>
          <p>身長: ${r.height}cm / 体重: ${r.weight}g</p>
        </div>
      `).join('')}
    </div>
  </div>

  <script>
    // 成長グラフを描画
    drawGrowthChart(${JSON.stringify(records)})
  </script>
</body>
</html>
  `
}

// ダウンロードボタン
function downloadAlbum() {
  const html = generateMonthlyAlbum(2025, 12)
  const blob = new Blob([html], { type: 'text/html' })
  const url = URL.createObjectURL(blob)
  const a = document.createElement('a')
  a.href = url
  a.download = '2025年12月_成長アルバム.html'
  a.click()
}

なぜ既製品にない?

既製品の育児アプリ:
- 記録はできる
- でも、アルバム印刷は別サービス
- データを移行する手間
- 自由なカスタマイズ不可

BYOAの育児アプリ:
✅ 記録データを直接活用
✅ 好きなレイアウトで印刷
✅ 成長グラフを自動挿入
✅ 祖父母共有用のWeb版も簡単

【例2】栽培アプリ → 販売用Web自動生成

基本アプリ(06章で作成)

<!-- 栽培記録 -->
<div class="cultivation-record">
  <input type="date" id="date">
  <select id="crop">
    <option>トマト</option>
    <option>キュウリ</option>
    <option>ナス</option>
  </select>
  <textarea placeholder="今日の作業"></textarea>
  <input type="file" accept="image/*">
  <input type="number" placeholder="収穫量(kg)">
  <button>記録</button>
</div>

↓ コラボで特化型機能を追加

販売促進ツール

// 栽培記録から商品ページを自動生成
function generateProductPage(crop) {
  const records = getCropRecords(crop)
  const story = generateStory(records) // AI で生成

  return `
<!DOCTYPE html>
<html>
<head>
  <title>${crop} - 生産者直送</title>
  <style>
    .product-hero {
      background: url('${records[0].photo}');
      background-size: cover;
      height: 400px;
    }
    .story-section {
      max-width: 800px;
      margin: 0 auto;
      padding: 40px 20px;
    }
    .cultivation-timeline {
      margin: 40px 0;
    }
  </style>
</head>
<body>
  <!-- ヒーロー画像 -->
  <div class="product-hero">
    <h1>${crop}</h1>
    <p>農薬不使用・露地栽培</p>
  </div>

  <!-- 生産者ストーリー -->
  <div class="story-section">
    <h2>こだわりの栽培方法</h2>
    <p>${story}</p>

    <!-- 栽培タイムライン -->
    <div class="cultivation-timeline">
      <h3>栽培の記録</h3>
      ${records.map(r => `
        <div class="timeline-item">
          <span class="date">${r.date}</span>
          <img src="${r.photo}" alt="${r.work}">
          <p>${r.work}</p>
        </div>
      `).join('')}
    </div>

    <!-- 購入リンク -->
    <a href="https://example.com/buy" class="buy-button">
      購入する(1kg 800円)
    </a>
  </div>
</body>
</html>
  `
}

// メルカリ/BASE 用の商品説明文も生成
function generateProductDescription(crop) {
  const records = getCropRecords(crop)

  return `
【農薬不使用・露地栽培の${crop}】

=== こだわりポイント ===
・完全無農薬栽培
・有機肥料のみ使用
・朝採れ当日発送

=== 栽培の様子 ===
${records.slice(0, 5).map(r =>
  `${r.date}: ${r.work}`
).join('\n')}

=== お届け内容 ===
・${crop} 1kg
・栽培記録カード付き
・レシピブック同梱

※収穫後24時間以内に発送いたします
  `.trim()
}

なぜ既製品にない?

既製品の栽培管理アプリ:
- 作業記録はできる
- でも、販売は別のプラットフォーム
- ストーリーテリングは自分で書く

BYOAの栽培アプリ:
✅ 栽培記録を販売促進に直接活用
✅ 写真でストーリーを自動構成
✅ こだわりが伝わる商品ページ
✅ 付加価値を可視化

【例3】家計簿アプリ → 投資分析機能

基本アプリ(06章で作成)

// 収支記録
const expense = {
  date: '2025-12-22',
  category: '食費',
  amount: 3500,
  note: 'スーパー'
}

↓ コラボで特化型機能を追加

// 投資可能額の自動算出
function calculateInvestmentCapacity() {
  const monthlyData = getMonthlyFinancialData()

  return {
    income: monthlyData.totalIncome,
    fixedExpenses: monthlyData.fixedExpenses, // 家賃、光熱費等
    variableExpenses: monthlyData.variableExpenses, // 食費、交通費等
    emergencyFund: monthlyData.totalIncome * 0.2, // 緊急資金20%
    investableAmount: monthlyData.totalIncome
                    - monthlyData.fixedExpenses
                    - monthlyData.variableExpenses
                    - (monthlyData.totalIncome * 0.2),

    recommendation: generateInvestmentRecommendation()
  }
}

// リスク許容度診断
function assessRiskTolerance() {
  const data = getUserFinancialData()

  const stability = data.incomeStability // 収入の安定性
  const expenses = data.expenseRatio // 支出比率
  const emergency = data.emergencyFundMonths // 緊急資金(月数)

  if (emergency >= 6 && stability === 'high' && expenses < 0.7) {
    return {
      level: 'aggressive',
      suggestion: '株式 70% / 債券 20% / 現金 10%',
      reason: '収入が安定し、緊急資金も十分です'
    }
  } else if (emergency >= 3 && stability === 'medium') {
    return {
      level: 'balanced',
      suggestion: '株式 50% / 債券 30% / 現金 20%',
      reason: 'バランス型がおすすめです'
    }
  } else {
    return {
      level: 'conservative',
      suggestion: 'まずは緊急資金を6ヶ月分貯めましょう',
      reason: '投資の前に生活防衛資金を確保してください'
    }
  }
}

【例4】読書記録 → 書評ブログ自動生成

基本アプリ(06章で作成)

// 読書記録
const book = {
  title: 'Clean Code',
  author: 'Robert C. Martin',
  finishedDate: '2025-12-20',
  rating: 5,
  notes: '可読性の重要性を再認識...'
}

↓ コラボで特化型機能を追加

// Claude API で書評記事を生成
async function generateBookReview(book) {
  const prompt = `
以下の読書記録から、ブログ用の書評記事を生成してください:

【書籍情報】
タイトル: ${book.title}
著者: ${book.author}
評価: ${book.rating}/5
読書メモ: ${book.notes}

【記事要件】
- 800-1000文字
- 読みやすい構成(導入/内容/感想/おすすめ読者)
- 具体的なエピソード
- note/はてなブログ向けMarkdown
  `

  const response = await callClaudeAPI(prompt)
  return response
}

// テーマ別引用集の生成
function generateThematicQuotes(theme) {
  const allBooks = getAllBooks()
  const relevantNotes = allBooks
    .flatMap(book => book.notes)
    .filter(note => note.includes(theme))

  return `
# ${theme}に関する引用集

${relevantNotes.map((note, i) => `
## ${i + 1}. ${note.book.title}

> ${note.quote}

*${note.book.author}*

### 私の考察
${note.myThoughts}
`).join('\n')}
  `
}

// note 投稿用HTML生成
function exportToNote(review) {
  return `
<div class="note-article">
  ${review.content}

  <div class="book-info">
    <img src="${review.book.coverImage}" alt="書籍カバー">
    <p>${review.book.title}</p>
    <p>${review.book.author}</p>
    <a href="${review.book.amazonLink}">Amazonで見る</a>
  </div>
</div>
  `
}

実践:ToDoアプリの進化過程

Phase 0: 初期版(06章で作成)

<!DOCTYPE html>
<html lang="ja">
<head>
  <title>ToDoリスト</title>
</head>
<body>
  <h1>ToDoリスト</h1>
  <input type="text" id="taskInput">
  <button onclick="addTask()">追加</button>
  <ul id="taskList"></ul>

  <script>
    let tasks = JSON.parse(localStorage.getItem('tasks') || '[]')

    function addTask() {
      const input = document.getElementById('taskInput')
      tasks.push({ text: input.value, done: false })
      localStorage.setItem('tasks', JSON.stringify(tasks))
      renderTasks()
      input.value = ''
    }

    function renderTasks() {
      const list = document.getElementById('taskList')
      list.innerHTML = tasks.map((t, i) => `
        <li>
          <input type="checkbox" ${t.done ? 'checked' : ''}
                 onchange="toggleTask(${i})">
          <span>${t.text}</span>
          <button onclick="deleteTask(${i})">削除</button>
        </li>
      `).join('')
    }

    function toggleTask(i) {
      tasks[i].done = !tasks[i].done
      localStorage.setItem('tasks', JSON.stringify(tasks))
      renderTasks()
    }

    function deleteTask(i) {
      tasks.splice(i, 1)
      localStorage.setItem('tasks', JSON.stringify(tasks))
      renderTasks()
    }

    renderTasks()
  </script>
</body>
</html>

Phase 1: コード品質向上(Claude レビュー)

Claude に依頼: 「このToDoアプリのコードをレビューして、改善案を教えてください」

Claude の提案: 1. グローバル変数を避ける 2. 関数を分割して責務を明確に 3. エラーハンドリング追加 4. アクセシビリティ改善

改善後:

// モジュール化
const TodoApp = (() => {
  let tasks = []

  // 初期化
  function init() {
    loadTasks()
    renderTasks()
    attachEventListeners()
  }

  // LocalStorage からロード(エラーハンドリング付き)
  function loadTasks() {
    try {
      const stored = localStorage.getItem('tasks')
      tasks = stored ? JSON.parse(stored) : []
    } catch (error) {
      console.error('タスクの読み込みに失敗:', error)
      tasks = []
    }
  }

  // LocalStorage へ保存(エラーハンドリング付き)
  function saveTasks() {
    try {
      localStorage.setItem('tasks', JSON.stringify(tasks))
      return true
    } catch (error) {
      if (error.name === 'QuotaExceededError') {
        alert('保存容量が不足しています')
      } else {
        alert('保存に失敗しました')
      }
      return false
    }
  }

  // タスク追加
  function addTask(text) {
    if (!text || !text.trim()) {
      alert('タスクを入力してください')
      return false
    }

    tasks.push({
      id: Date.now(),
      text: text.trim(),
      done: false,
      createdAt: new Date().toISOString()
    })

    if (saveTasks()) {
      renderTasks()
      return true
    }
    return false
  }

  // レンダリング
  function renderTasks() {
    const list = document.getElementById('taskList')
    list.innerHTML = tasks.map(task => createTaskHTML(task)).join('')
  }

  // タスクHTML生成
  function createTaskHTML(task) {
    return `
      <li class="task-item ${task.done ? 'done' : ''}">
        <input type="checkbox"
               ${task.done ? 'checked' : ''}
               onchange="TodoApp.toggleTask(${task.id})"
               aria-label="タスク完了チェック">
        <span class="task-text">${escapeHTML(task.text)}</span>
        <button onclick="TodoApp.deleteTask(${task.id})"
                aria-label="タスク削除">
          削除
        </button>
      </li>
    `
  }

  // XSS対策
  function escapeHTML(str) {
    const div = document.createElement('div')
    div.textContent = str
    return div.innerHTML
  }

  // イベントリスナー
  function attachEventListeners() {
    const input = document.getElementById('taskInput')
    const addButton = document.getElementById('addButton')

    addButton.addEventListener('click', () => {
      if (addTask(input.value)) {
        input.value = ''
      }
    })

    // Enter キーで追加
    input.addEventListener('keypress', (e) => {
      if (e.key === 'Enter') {
        addButton.click()
      }
    })
  }

  // 公開API
  return {
    init,
    addTask,
    toggleTask: (id) => {
      const task = tasks.find(t => t.id === id)
      if (task) {
        task.done = !task.done
        saveTasks()
        renderTasks()
      }
    },
    deleteTask: (id) => {
      tasks = tasks.filter(t => t.id !== id)
      saveTasks()
      renderTasks()
    }
  }
})()

// アプリ起動
document.addEventListener('DOMContentLoaded', () => {
  TodoApp.init()
})

Phase 2: 機能拡張(コミュニティからの要望)

GitHub Issue からの要望: - [ ] フィルター機能(全て/未完了/完了) - [ ] 期限設定 - [ ] 優先度

実装:

// タスクデータ構造を拡張
const task = {
  id: Date.now(),
  text: 'レシピを書く',
  done: false,
  dueDate: '2025-12-25',
  priority: 'high', // high, medium, low
  createdAt: new Date().toISOString()
}

// フィルター機能
function filterTasks(filter) {
  switch(filter) {
    case 'all':
      return tasks
    case 'active':
      return tasks.filter(t => !t.done)
    case 'completed':
      return tasks.filter(t => t.done)
    case 'today':
      return tasks.filter(t =>
        t.dueDate === new Date().toISOString().split('T')[0]
      )
    default:
      return tasks
  }
}

// 優先度で並び替え
function sortByPriority(tasks) {
  const priorityOrder = { high: 1, medium: 2, low: 3 }
  return tasks.sort((a, b) =>
    priorityOrder[a.priority] - priorityOrder[b.priority]
  )
}

Phase 3: 特化型機能(重要!)

使いながら気づいた本当に欲しい機能

パターンA: プロジェクト管理特化

// プロジェクト別グルーピング
const task = {
  id: Date.now(),
  text: 'API設計',
  project: 'cookbook-app', // プロジェクト追加
  done: false,
  dueDate: '2025-12-25',
  priority: 'high',
  estimatedHours: 4, // 見積もり時間
  actualHours: 0 // 実績時間
}

// プロジェクトダッシュボード
function generateProjectDashboard(projectId) {
  const projectTasks = tasks.filter(t => t.project === projectId)

  const stats = {
    total: projectTasks.length,
    completed: projectTasks.filter(t => t.done).length,
    remaining: projectTasks.filter(t => !t.done).length,
    estimatedHours: projectTasks.reduce((sum, t) => sum + t.estimatedHours, 0),
    actualHours: projectTasks.reduce((sum, t) => sum + t.actualHours, 0),
    progress: 0
  }

  stats.progress = (stats.completed / stats.total) * 100

  return stats
}

// ガントチャート表示
function generateGanttChart(projectId) {
  const projectTasks = tasks
    .filter(t => t.project === projectId)
    .sort((a, b) => new Date(a.dueDate) - new Date(b.dueDate))

  return `
<div class="gantt-chart">
  <h3>ガントチャート</h3>
  ${projectTasks.map(task => `
    <div class="gantt-row">
      <span class="task-name">${task.text}</span>
      <div class="timeline">
        <div class="task-bar"
             style="width: ${task.estimatedHours * 10}px;
                    background: ${task.done ? 'green' : 'blue'}">
        </div>
      </div>
      <span class="due-date">${task.dueDate}</span>
    </div>
  `).join('')}
</div>
  `
}

// 週報の自動生成
function generateWeeklyReport() {
  const thisWeek = getThisWeekTasks()

  return `
# 今週の進捗報告(${getWeekRange()})

## 完了したタスク(${thisWeek.completed.length}件)
${thisWeek.completed.map(t => `- [x] ${t.text} (${t.project})`).join('\n')}

## 進行中のタスク(${thisWeek.inProgress.length}件)
${thisWeek.inProgress.map(t => `- [ ] ${t.text} (期限: ${t.dueDate})`).join('\n')}

## 来週の予定
${getNextWeekTasks().map(t => `- ${t.text} (${t.estimatedHours}h)`).join('\n')}

## 時間集計
- 見積もり: ${thisWeek.estimatedHours}h
- 実績: ${thisWeek.actualHours}h
- 差異: ${thisWeek.actualHours - thisWeek.estimatedHours}h
  `
}

なぜ既製品にない? → 汎用ToDoアプリはプロジェクト管理に特化していない → プロジェクト管理ツールは複雑すぎる → 自分の使い方に合わせた「ちょうどいい」特化が可能


パターンB: 習慣化特化

// 習慣トラッキング
const habit = {
  id: Date.now(),
  text: '朝のランニング',
  type: 'habit', // 通常タスクと区別
  frequency: 'daily', // daily, weekly, monthly
  streak: 15, // 連続達成日数
  history: [
    { date: '2025-12-22', done: true },
    { date: '2025-12-21', done: true },
    // ...
  ]
}

// 連続達成日数の計算
function calculateStreak(habit) {
  let streak = 0
  const today = new Date()

  for (let i = 0; i < habit.history.length; i++) {
    const date = new Date(habit.history[i].date)
    const daysDiff = Math.floor((today - date) / (1000 * 60 * 60 * 24))

    if (daysDiff === i && habit.history[i].done) {
      streak++
    } else {
      break
    }
  }

  return streak
}

// モチベーショングラフ
function generateMotivationChart(habitId) {
  const habit = habits.find(h => h.id === habitId)
  const last30Days = habit.history.slice(-30)

  const completionRate = last30Days.filter(d => d.done).length / 30 * 100

  return `
<div class="motivation-chart">
  <h3>${habit.text}</h3>
  <p>連続達成: ${habit.streak}日 🔥</p>
  <p>完了率: ${completionRate.toFixed(1)}%</p>

  <div class="calendar-grid">
    ${last30Days.map(day => `
      <div class="day ${day.done ? 'completed' : 'missed'}">
        ${new Date(day.date).getDate()}
      </div>
    `).join('')}
  </div>
</div>
  `
}

// リマインダー機能
function setHabitReminder(habitId, time) {
  const habit = habits.find(h => h.id === habitId)

  // Web Notifications API を使用
  if ('Notification' in window && Notification.permission === 'granted') {
    const now = new Date()
    const reminderTime = new Date(now.toDateString() + ' ' + time)
    const delay = reminderTime - now

    if (delay > 0) {
      setTimeout(() => {
        new Notification('習慣リマインダー', {
          body: `${habit.text}の時間です!`,
          icon: '/icon.png'
        })
      }, delay)
    }
  }
}

なぜ既製品にない? → 習慣化アプリは多いが、ToDoと統合されていない → 自分の習慣に合わせたカスタマイズが可能 → データを自分で持つから長期分析も自由


GitHub でのコラボレーション実践

Step 1: リポジトリ公開

# README を充実させる
cat > README.md << 'EOF'
# ToDoアプリ

個人用のシンプルなToDoリスト管理アプリです。

## 特徴

- 📝 シンプルなタスク管理
- 🎯 優先度設定
- 📅 期限管理
- 📊 プロジェクトダッシュボード

## 使い方

1. `index.html` をブラウザで開く
2. タスクを入力して追加
3. LocalStorageに自動保存

## コラボレーション歓迎!

以下のような貢献を歓迎します:
- バグ報告
- 機能リクエスト
- コードレビュー
- ドキュメント改善

詳しくは [CONTRIBUTING.md](CONTRIBUTING.md) をご覧ください。

## ライセンス

MIT License
EOF

# プッシュ
git add .
git commit -m "Initial commit"
git push origin main

Step 2: Issue テンプレート作成

<!-- .github/ISSUE_TEMPLATE/feature_request.md -->
---
name: 機能リクエスト
about: 新しい機能の提案
title: '[機能] '
labels: enhancement
---

## 提案する機能

機能の概要を簡潔に説明してください。

## 背景・動機

なぜこの機能が必要ですか?
どのような課題を解決しますか?

## 期待する動作

この機能がどのように動作すべきか説明してください。

## 代替案

他に検討した方法があれば教えてください。

## 追加情報

スクリーンショットやモックアップがあれば添付してください。

Step 3: Pull Request を受ける

開発者からの PR 例:

## 変更内容

フィルター機能を追加しました。

## 変更詳細

- [ ] 「全て」「未完了」「完了」のフィルターボタン追加
- [ ] URL パラメータでフィルター状態を保持
- [ ] フィルター状態を LocalStorage に保存
- [ ] テストケース追加

## スクリーンショット

[Before/After の画像]

## テスト

以下の環境でテストしました:
- Chrome 120
- Firefox 121
- Safari 17

## 関連Issue

Closes #15

あなたのレビュー:

素晴らしい実装ですね!ありがとうございます。

いくつか質問があります:

1. フィルター状態をURLパラメータで保持するのは良いアイデアですが、
   LocalStorageとの優先順位はどうなっていますか?

2. モバイル表示も確認されましたか?

細かい点ですが、問題なければマージします!

Step 4: Claude を活用したコラボ

# Claude Projects での活用

## Project作成
- 名前: "ToDoアプリ改善"
- カスタム指示:
  「このToDoアプリのコードレビューと機能拡張を手伝ってください。
   特に、コミュニティからのIssueに対する実装案を提案してください。」

## 活用例

【あなた】
GitHub でこんなIssueをもらいました:
「タスクをドラッグ&ドロップで並び替えたい」

実装方法を教えてください。

【Claude】
ドラッグ&ドロップ機能の実装案を提案します:

1. HTML5 Drag and Drop API を使用
2. タッチデバイス対応も追加
3. 順序をLocalStorageに保存

以下のコードで実装できます...
[詳細な実装コード]

【あなた】
ありがとうございます!実装してみます。
PRを作成する前に、このコードをレビューしてもらえますか?

【Claude】
コードレビューします:
1. ✅ 基本機能は正しく実装されています
2. ⚠️ モバイルでのタッチイベントが不完全です
3. 💡 アニメーションを追加すると UX が向上します

改善案:
[具体的な改善コード]

特化型機能開発のプロセス

1. ニーズの発見

実際に使いながら「あったらいいな」を見つける

【育児アプリの例】

使用開始:
- 日々の記録を入力
- 写真を添付
- 身長・体重を記録

↓ 3ヶ月後

気づき:
「この写真、アルバムにまとめたいな」
「祖父母に成長を見せたいけど、毎回送るのは大変」
「成長グラフを自動で作れたらいいのに」

→ **特化型機能のニーズ発見**

2. コミュニティで相談

# GitHub Discussions で相談

【タイトル】
アルバム自動生成機能の追加を検討しています

【内容】
育児記録アプリを3ヶ月使ってきて、
記録した写真をアルバムにまとめる機能があったら
便利だと思いました。

具体的には:
- 月別で写真を自動整理
- 成長グラフを自動挿入
- 印刷用PDFまたはHTML出力
- 祖父母共有用のWeb版

同じように感じている方いますか?
他にどんな機能があると嬉しいですか?

コミュニティからの反応:

👍 10人が「いいね」

【コメント1】
素晴らしいアイデアです!
うちも双子なので、2人分のアルバムを並べて見られると嬉しいです。

【コメント2】
印刷サービス(しまうまプリント等)へのデータ出力も
できると便利かもしれません。

【コメント3】
私は開発者です。実装を手伝えます!
Issue を作成しましょうか?

3. 実装方針を決める

Claude に相談:

育児記録から自動アルバムを作りたいです。

要件:
- 月別で写真を整理
- 成長グラフ(身長・体重)を自動生成
- 印刷用レイアウト(A4サイズ)
- HTMLとして出力

どういう技術で実現できますか?

Claude の提案:

実装案を提案します:

【技術スタック】
1. Chart.js - 成長グラフの描画
2. HTML/CSS - 印刷用レイアウト
3. jsPDF(オプション)- PDF出力

【実装ステップ】
Phase 1: データ整理
- 写真を月別にグルーピング
- 成長データの集計

Phase 2: HTMLテンプレート
- A4サイズのレイアウト
- CSS印刷スタイル

Phase 3: グラフ生成
- Chart.js で成長曲線
- 標準成長曲線との比較(オプション)

Phase 4: エクスポート
- HTMLダウンロード
- PDF出力(オプション)

詳細なコード例:
[実装コードを提示]

4. 段階的に実装

Phase 1: 月別表示 ✅
├─ 写真を月ごとにグルーピング
├─ 簡単なグリッドレイアウト
└─ ブラウザで表示確認

Phase 2: グラフ追加 🔄(進行中)
├─ Chart.js 統合
├─ 身長・体重の折れ線グラフ
└─ 見た目の調整

Phase 3: 印刷対応 ⏳(予定)
├─ CSS 印刷スタイル
├─ ページブレーク調整
└─ A4サイズ最適化

Phase 4: 共有機能 ⏳(予定)
├─ Web版の生成
├─ シンプルなURL共有
└─ パスワード保護(オプション)

5. フィードバックループ

# リリース後

## v1.0 - アルバム機能追加

実装した内容:
- ✅ 月別写真表示
- ✅ 成長グラフ
- ✅ HTML出力

## ユーザーからのフィードバック

【Issue #45】
「アルバムに手書きコメントを追加したい」
→ Phase 5 で対応検討

【Issue #46】
「複数の子供(兄弟)を1つのアルバムにまとめたい」
→ 良いアイデア!次のバージョンで実装

【Issue #47】
「印刷すると画質が粗い」
→ バグ修正、v1.0.1 でリリース済み

## 改善を継続
コミュニティの声を聞きながら、
一歩ずつ進化させる

持続可能な開発

オープンソースとしての運営

# 透明性のある運営

## コストの開示
- 開発時間: 月10時間程度(趣味の範囲)
- Claude Pro: 月3,100円(個人で使用)
- サーバー費用: 無料(GitHub Pages)

## 収益について
このプロジェクトは非営利です。
もし将来的に運営費が必要になった場合:
- GitHub Sponsors での支援
- 任意の寄付
を検討するかもしれません。

## コミュニティ主導
- 重要な決定はDiscussionsで相談
- 誰でも提案・貢献できる
- メンテナーは複数人体制

必要に応じた持続可能性:

【パターン1: 趣味として継続】
- 自分が使うから維持する
- コミュニティからの貢献を歓迎
- 無理せず続けられる範囲で

【パターン2: コミュニティで分担】
- メンテナーを複数人に
- 機能ごとに担当者を決める
- バスファクター対策

【パターン3: 必要最小限の収益化】
(あくまで運営費の範囲)
- GitHub Sponsors
- 印刷サービスとの提携(アフィリエイト)
- 企業向け有料サポート

05章(Chef's Specials)への接続

いつステップアップするか?

こんなサインが出たら:

❌ Webチャットでのコード管理が限界
  → ファイルが増えて、管理が煩雑に
  → バージョン管理が必要

❌ モバイルアプリ化したい
  → WebアプリだけではUXに限界
  → プッシュ通知が必要
  → オフライン対応が必要

❌ チーム開発が必要
  → コントリビューターが増えた
  → コードレビューの体制が必要
  → CI/CD自動化が必要

❌ ユーザーが増えてきた
  → 配布が必要(App Store等)
  → 品質保証が重要
  → サポート体制が必要

05章(Chef's Specials)で学ぶこと

【ツール】
Cursor + Claude で本格開発
- AI統合エディタで効率化
- プロジェクト固有の知識統合
- チーム開発のベストプラクティス

【技術スタック】
Flutter でマルチプラットフォーム
- iOS / Android / デスクトップ / Web
- 単一コードベース
- ネイティブパフォーマンス

【開発プロセス】
- Git によるバージョン管理
- CI/CD 自動化(Xcode Cloud等)
- テスト駆動開発
- コミュニティ駆動開発

【事例研究】
- 育児・発達支援アプリの本格開発
- 野菜栽培アプリのフル実装
- 開発ツールの作成

まとめ

アプリ進化の流れ

Step 1: 個人で作る(06章)
└─ Claude Webチャットのみ
└─ シンプルな個人用ツール

Step 2: コラボで進化させる(この章)
├─ コード品質向上
├─ 機能拡張
└─ **特化型機能追加**(重要!)
    └─ データを自由に活用
    └─ 独自の価値創出
    └─ コミュニティの成長

Step 3: 本格開発へ(05章)
└─ 必要になったら
└─ Cursor + Claude + Flutter
└─ マルチプラットフォーム展開

コラボレーションの本質

一人で完璧を目指さない
↓
みんなで少しずつ育てる
↓
使いながら改善する
↓
特化型機能で独自の価値を生む
↓
コミュニティが成長する
↓
同じ課題を持つ人の助けになる

特化型機能の重要性

BYOAの真価は、特化型機能にある

汎用アプリでは実現できない価値:

【育児アプリ】
記録 → アルバム自動生成
→ 祖父母と共有
→ 成長の可視化

【栽培アプリ】
栽培記録 → 販売用ストーリー
→ 付加価値の創出
→ 顧客との信頼構築

【家計簿アプリ】
収支記録 → 投資分析
→ 最適なポートフォリオ
→ ライフプラン設計

【読書アプリ】
読書記録 → 書評自動生成
→ コンテンツ化
→ 知識の体系化

データを自分で持つから、自由に活用できる 当事者だから、本当に必要な機能がわかる コミュニティがあるから、一緒に育てられる


次のステップ

今すぐできること

1. 06章で作ったアプリを見直す
   「どんな機能があったら嬉しいか?」

2. Claude に相談する
   「このデータを使って○○ができたら便利なのですが」

3. GitHub で公開する
   同じ課題を持つ人とつながる

4. Issue を立てる
   コミュニティに意見を聞く

5. 一歩ずつ実装する
   完璧を目指さず、使いながら改善

さらに学ぶために

📘 レシピ#6-5: 育児・発達支援アプリの事例 - AIエージェント機能 - プライバシー保護設計 - コミュニティ駆動開発

📘 05_chefs_specials/ - PM能力がある非開発者向け - Cursor + Claude での本格開発 - 7つの実践的ケーススタディ

📘 GitHub Discussions - 他のBYOA開発者と交流 - アイデアを共有 - 協力して課題を解決


一人で作り、みんなで育てる。 それが、BYOA開発の醍醐味です。

次のレシピで、具体的な事例を見ていきましょう!