From 63ade8c60c814ecb6653f258bef93ce36da949f4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 15 Feb 2026 03:55:36 +0000 Subject: [PATCH 1/4] Initial plan From d0c0ae742886b58c1ca86a5949b5f132c287a1d7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 15 Feb 2026 03:58:20 +0000 Subject: [PATCH 2/4] Add interactive color palette generator with JavaScript Co-authored-by: messpy <110882522+messpy@users.noreply.github.com> --- app.js | 239 +++++++++++++++++++++++++++++++++++++++++++++++++++++ index.html | 35 ++++++++ style.css | 238 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 512 insertions(+) create mode 100644 app.js create mode 100644 index.html create mode 100644 style.css diff --git a/app.js b/app.js new file mode 100644 index 0000000..73b789f --- /dev/null +++ b/app.js @@ -0,0 +1,239 @@ +// カラーパレットジェネレーター +class ColorPaletteGenerator { + constructor() { + this.paletteSize = 5; + this.currentPalette = []; + this.lockedColors = new Set(); + this.savedPalettes = this.loadSavedPalettes(); + + this.init(); + } + + init() { + this.generatePalette(); + this.renderPalette(); + this.renderSavedPalettes(); + this.setupEventListeners(); + } + + setupEventListeners() { + // 生成ボタン + document.getElementById('generateBtn').addEventListener('click', () => { + this.generatePalette(); + this.renderPalette(); + }); + + // 保存ボタン + document.getElementById('saveBtn').addEventListener('click', () => { + this.savePalette(); + }); + + // スペースキーで生成 + document.addEventListener('keydown', (e) => { + if (e.code === 'Space' && e.target.tagName !== 'BUTTON') { + e.preventDefault(); + this.generatePalette(); + this.renderPalette(); + } + }); + } + + // ランダムな色を生成 + generateRandomColor() { + const r = Math.floor(Math.random() * 256); + const g = Math.floor(Math.random() * 256); + const b = Math.floor(Math.random() * 256); + return { r, g, b }; + } + + // RGBをHEXに変換 + rgbToHex(r, g, b) { + return '#' + [r, g, b].map(x => { + const hex = x.toString(16); + return hex.length === 1 ? '0' + hex : hex; + }).join('').toUpperCase(); + } + + // 色の明度を計算 + calculateLuminance(r, g, b) { + const a = [r, g, b].map(v => { + v /= 255; + return v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4); + }); + return a[0] * 0.2126 + a[1] * 0.7152 + a[2] * 0.0722; + } + + // パレットを生成 + generatePalette() { + const newPalette = []; + + for (let i = 0; i < this.paletteSize; i++) { + if (this.lockedColors.has(i) && this.currentPalette[i]) { + newPalette.push(this.currentPalette[i]); + } else { + const color = this.generateRandomColor(); + newPalette.push({ + ...color, + hex: this.rgbToHex(color.r, color.g, color.b) + }); + } + } + + this.currentPalette = newPalette; + } + + // パレットを描画 + renderPalette() { + const paletteElement = document.getElementById('palette'); + paletteElement.innerHTML = ''; + + this.currentPalette.forEach((color, index) => { + const colorBox = document.createElement('div'); + colorBox.className = 'color-box'; + + const isLocked = this.lockedColors.has(index); + const lockIcon = isLocked ? '🔒' : '🔓'; + + colorBox.innerHTML = ` +
+ +
+
+
${color.hex}
+
RGB(${color.r}, ${color.g}, ${color.b})
+
+ `; + + // カラーコードをクリックでコピー + colorBox.addEventListener('click', (e) => { + if (!e.target.classList.contains('lock-btn')) { + this.copyToClipboard(color.hex); + } + }); + + // ロックボタン + const lockBtn = colorBox.querySelector('.lock-btn'); + lockBtn.addEventListener('click', (e) => { + e.stopPropagation(); + this.toggleLock(index); + }); + + paletteElement.appendChild(colorBox); + }); + } + + // ロックを切り替え + toggleLock(index) { + if (this.lockedColors.has(index)) { + this.lockedColors.delete(index); + } else { + this.lockedColors.add(index); + } + this.renderPalette(); + } + + // クリップボードにコピー + copyToClipboard(text) { + navigator.clipboard.writeText(text).then(() => { + this.showNotification(`${text} をコピーしました!`); + }).catch(err => { + console.error('コピーに失敗しました:', err); + }); + } + + // 通知を表示 + showNotification(message) { + let notification = document.querySelector('.copy-notification'); + if (!notification) { + notification = document.createElement('div'); + notification.className = 'copy-notification'; + document.body.appendChild(notification); + } + + notification.textContent = message; + notification.classList.add('show'); + + setTimeout(() => { + notification.classList.remove('show'); + }, 2000); + } + + // パレットを保存 + savePalette() { + const palette = { + id: Date.now(), + colors: this.currentPalette, + date: new Date().toLocaleString('ja-JP') + }; + + this.savedPalettes.unshift(palette); + + // 最大20個まで保存 + if (this.savedPalettes.length > 20) { + this.savedPalettes = this.savedPalettes.slice(0, 20); + } + + this.savePalettesToStorage(); + this.renderSavedPalettes(); + this.showNotification('パレットを保存しました!'); + } + + // ローカルストレージに保存 + savePalettesToStorage() { + localStorage.setItem('colorPalettes', JSON.stringify(this.savedPalettes)); + } + + // ローカルストレージから読み込み + loadSavedPalettes() { + const saved = localStorage.getItem('colorPalettes'); + return saved ? JSON.parse(saved) : []; + } + + // 保存されたパレットを描画 + renderSavedPalettes() { + const savedList = document.getElementById('savedList'); + + if (this.savedPalettes.length === 0) { + savedList.innerHTML = '

まだ保存されたパレットはありません

'; + return; + } + + savedList.innerHTML = ''; + + this.savedPalettes.forEach(palette => { + const paletteDiv = document.createElement('div'); + paletteDiv.className = 'saved-palette'; + + const colorsHtml = palette.colors.map(color => + `
` + ).join(''); + + paletteDiv.innerHTML = ` +
${colorsHtml}
+
${palette.date}
+ `; + + paletteDiv.addEventListener('click', () => { + this.loadPalette(palette); + }); + + savedList.appendChild(paletteDiv); + }); + } + + // 保存されたパレットを読み込み + loadPalette(palette) { + this.currentPalette = palette.colors; + this.lockedColors.clear(); + this.renderPalette(); + this.showNotification('パレットを読み込みました!'); + window.scrollTo({ top: 0, behavior: 'smooth' }); + } +} + +// アプリケーションを初期化 +document.addEventListener('DOMContentLoaded', () => { + new ColorPaletteGenerator(); +}); diff --git a/index.html b/index.html new file mode 100644 index 0000000..dcf2700 --- /dev/null +++ b/index.html @@ -0,0 +1,35 @@ + + + + + + インタラクティブカラーパレットジェネレーター + + + +
+
+

🎨 カラーパレットジェネレーター

+

スペースキーを押すか、ボタンをクリックして新しいパレットを生成してください

+
+ +
+ +
+ +
+ + +
+ +
+

保存されたパレット

+
+ +
+
+
+ + + + diff --git a/style.css b/style.css new file mode 100644 index 0000000..e918ae4 --- /dev/null +++ b/style.css @@ -0,0 +1,238 @@ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + min-height: 100vh; + padding: 20px; +} + +.container { + max-width: 1200px; + margin: 0 auto; +} + +header { + text-align: center; + color: white; + margin-bottom: 40px; +} + +header h1 { + font-size: 3rem; + margin-bottom: 10px; + text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3); +} + +header p { + font-size: 1.1rem; + opacity: 0.9; +} + +.palette { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); + gap: 20px; + margin-bottom: 40px; +} + +.color-box { + background: white; + border-radius: 12px; + overflow: hidden; + box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3); + transition: transform 0.3s ease, box-shadow 0.3s ease; + cursor: pointer; +} + +.color-box:hover { + transform: translateY(-10px); + box-shadow: 0 15px 40px rgba(0, 0, 0, 0.4); +} + +.color-display { + height: 200px; + position: relative; +} + +.lock-btn { + position: absolute; + top: 10px; + right: 10px; + background: rgba(255, 255, 255, 0.9); + border: none; + width: 40px; + height: 40px; + border-radius: 50%; + cursor: pointer; + font-size: 1.5rem; + display: flex; + align-items: center; + justify-content: center; + transition: all 0.3s ease; +} + +.lock-btn:hover { + background: white; + transform: scale(1.1); +} + +.lock-btn.locked { + background: rgba(255, 215, 0, 0.9); +} + +.color-info { + padding: 15px; + text-align: center; +} + +.color-code { + font-family: 'Courier New', monospace; + font-size: 1.1rem; + font-weight: bold; + color: #333; + margin-bottom: 5px; +} + +.color-rgb { + font-size: 0.9rem; + color: #666; +} + +.controls { + display: flex; + justify-content: center; + gap: 20px; + margin-bottom: 40px; +} + +button { + padding: 15px 30px; + font-size: 1.1rem; + border: none; + border-radius: 8px; + cursor: pointer; + transition: all 0.3s ease; + font-weight: bold; +} + +.btn-primary { + background: #4CAF50; + color: white; +} + +.btn-primary:hover { + background: #45a049; + transform: translateY(-2px); + box-shadow: 0 5px 15px rgba(76, 175, 80, 0.4); +} + +.btn-secondary { + background: #ff9800; + color: white; +} + +.btn-secondary:hover { + background: #e68900; + transform: translateY(-2px); + box-shadow: 0 5px 15px rgba(255, 152, 0, 0.4); +} + +.saved-palettes { + background: rgba(255, 255, 255, 0.95); + border-radius: 12px; + padding: 30px; + box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2); +} + +.saved-palettes h2 { + color: #333; + margin-bottom: 20px; + text-align: center; +} + +.saved-list { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); + gap: 20px; +} + +.saved-palette { + background: white; + border-radius: 8px; + overflow: hidden; + box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1); + cursor: pointer; + transition: transform 0.3s ease; +} + +.saved-palette:hover { + transform: translateY(-5px); + box-shadow: 0 6px 15px rgba(0, 0, 0, 0.15); +} + +.saved-colors { + display: flex; + height: 80px; +} + +.saved-color { + flex: 1; +} + +.saved-info { + padding: 10px; + text-align: center; + color: #666; + font-size: 0.9rem; +} + +.copy-notification { + position: fixed; + top: 20px; + right: 20px; + background: #4CAF50; + color: white; + padding: 15px 25px; + border-radius: 8px; + box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3); + opacity: 0; + transform: translateY(-20px); + transition: all 0.3s ease; + pointer-events: none; + z-index: 1000; +} + +.copy-notification.show { + opacity: 1; + transform: translateY(0); +} + +@media (max-width: 768px) { + header h1 { + font-size: 2rem; + } + + .palette { + grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); + gap: 10px; + } + + .color-display { + height: 150px; + } + + .controls { + flex-direction: column; + align-items: center; + } + + button { + width: 100%; + max-width: 300px; + } +} From 5893d77cf49c40e28bc861e267bb9e09c68c74e4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 15 Feb 2026 03:58:53 +0000 Subject: [PATCH 3/4] Remove unused calculateLuminance method Co-authored-by: messpy <110882522+messpy@users.noreply.github.com> --- app.js | 9 --------- 1 file changed, 9 deletions(-) diff --git a/app.js b/app.js index 73b789f..b2c7834 100644 --- a/app.js +++ b/app.js @@ -54,15 +54,6 @@ class ColorPaletteGenerator { }).join('').toUpperCase(); } - // 色の明度を計算 - calculateLuminance(r, g, b) { - const a = [r, g, b].map(v => { - v /= 255; - return v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4); - }); - return a[0] * 0.2126 + a[1] * 0.7152 + a[2] * 0.0722; - } - // パレットを生成 generatePalette() { const newPalette = []; From b17c009cb1bdd954a5be491de474889f7de1f8f6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 15 Feb 2026 04:00:10 +0000 Subject: [PATCH 4/4] Update README with project documentation Co-authored-by: messpy <110882522+messpy@users.noreply.github.com> --- README.md | 40 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 2a02cb0..e24ca8e 100644 --- a/README.md +++ b/README.md @@ -1 +1,39 @@ -# github.io \ No newline at end of file +# github.io + +## 🎨 インタラクティブカラーパレットジェネレーター + +JavaScriptを使用して作成された、美しいカラーパレットを生成できるWebアプリケーションです。 + +### 主な機能 + +- 🎲 **ランダムカラー生成** - ボタンクリックまたはスペースキーで新しいパレットを生成 +- 🔒 **カラーロック機能** - お気に入りの色を固定して他の色だけを再生成 +- 💾 **パレット保存** - LocalStorageを使用して最大20個のパレットを保存 +- 📋 **クリップボードコピー** - カラーコードをクリックでコピー +- 📱 **レスポンシブデザイン** - デスクトップとモバイルに対応 +- 🌐 **日本語UI** - 完全日本語対応のインターフェース + +### 使い方 + +1. ページを開くと、ランダムなカラーパレットが表示されます +2. 「新しいパレットを生成」ボタンをクリックするか、スペースキーを押して新しいパレットを生成 +3. 🔓 アイコンをクリックして色をロックできます(ロックされた色は🔒アイコンになります) +4. 気に入ったパレットは「お気に入りに保存」ボタンで保存できます +5. カラーコードをクリックするとクリップボードにコピーされます + +### 技術スタック + +- HTML5 +- CSS3 (Grid, Flexbox, Animations) +- Vanilla JavaScript (ES6+) +- LocalStorage API +- Clipboard API + +### ファイル構成 + +``` +├── index.html # メインHTML +├── app.js # JavaScriptアプリケーションロジック +├── style.css # スタイリング +└── README.md # このファイル +``` \ No newline at end of file