// HistoryPopup 팝업 스크립트 // Phase 3.5: 커서 위치에 최근 전사 히스토리 팝업 // Arrow↑↓ 선택, Enter 붙여넣기, 1-9 직접 선택, ESC 닫기 ;(function () { 'use strict' var container = document.getElementById('container') var itemsContainer = document.getElementById('items') var items = [] var selectedIndex = 0 var currentEntries = [] var i18nStrings = {} // ── i18n 적용 (HTML 텍스트) ─────────────────────────── function applyI18nToHtml() { var elements = document.querySelectorAll('[data-i18n]') elements.forEach(function(el) { var key = el.getAttribute('data-i18n') if (i18nStrings[key]) { el.textContent = i18nStrings[key] } }) } // ── 상대 시간 표시 ─────────────────────────────────── function relativeTime(timestamp) { var diff = Date.now() - timestamp var sec = Math.floor(diff / 1000) if (sec < 60) return i18nStrings.timeJustNow || 'just now' var min = Math.floor(sec / 60) if (min < 60) return (i18nStrings.timeMinutesAgo || '{{m}}m ago').replace('{{m}}', min) var hr = Math.floor(min / 60) if (hr < 24) return (i18nStrings.timeHoursAgo || '{{h}}h ago').replace('{{h}}', hr) var day = Math.floor(hr / 24) return (i18nStrings.timeDaysAgo || '{{d}}d ago').replace('{{d}}', day) } // ── 텍스트 truncate ────────────────────────────────── function truncate(text, maxLen) { if (text.length <= maxLen) return text return text.substring(0, maxLen) + '...' } // ── 아이템 렌더링 ─────────────────────────────────── function renderItems(entries) { currentEntries = entries itemsContainer.innerHTML = '' items = [] if (entries.length === 0) { var empty = document.createElement('div') empty.className = 'empty-state' empty.textContent = i18nStrings.noHistory || 'No history yet' itemsContainer.appendChild(empty) return } entries.forEach(function (entry, index) { var div = document.createElement('div') div.className = 'item' + (index === selectedIndex ? ' selected' : '') div.dataset.index = String(index) var num = document.createElement('span') num.className = 'item-number' num.textContent = String(index + 1) var text = document.createElement('span') text.className = 'item-text' text.textContent = truncate(entry.polishedText || entry.originalText, 50) var time = document.createElement('span') time.className = 'item-time' time.textContent = relativeTime(entry.createdAt) div.appendChild(num) div.appendChild(text) div.appendChild(time) itemsContainer.appendChild(div) items.push(div) // 클릭으로 선택+삽입 div.addEventListener('click', function () { selectAndInsert(index) }) }) } // ── 선택 업데이트 ─────────────────────────────────── function updateSelection(newIndex) { if (currentEntries.length === 0) return if (newIndex < 0) newIndex = currentEntries.length - 1 if (newIndex >= currentEntries.length) newIndex = 0 items.forEach(function (item, i) { if (i === newIndex) { item.classList.add('selected') } else { item.classList.remove('selected') } }) selectedIndex = newIndex // 스크롤 보장 if (items[selectedIndex]) { items[selectedIndex].scrollIntoView({ block: 'nearest' }) } } // ── 선택 후 삽입 ─────────────────────────────────── function selectAndInsert(index) { if (index < 0 || index >= currentEntries.length) return var entry = currentEntries[index] var text = entry.polishedText || entry.originalText window.popupAPI.send('history:itemSelected', { id: entry.id, text: text }) } // ── 크기 측정 ─────────────────────────────────────── function measureAndReport() { requestAnimationFrame(function () { requestAnimationFrame(function () { var rect = container.getBoundingClientRect() window.popupAPI.send('history:popupMeasured', { width: Math.ceil(rect.width) + 4, height: Math.ceil(rect.height) + 4 }) }) }) } // ── IPC 리스너 ─────────────────────────────────────── window.popupAPI.on('history:showItems', function (data) { selectedIndex = 0 if (data._i18n) i18nStrings = data._i18n applyI18nToHtml() renderItems(data.entries || []) container.classList.remove('hiding') measureAndReport() }) window.popupAPI.on('history:show', function () { container.classList.add('visible') container.classList.remove('hiding') }) window.popupAPI.on('history:hide', function () { container.classList.add('hiding') container.classList.remove('visible') }) // 키보드 이벤트 (main에서 전달) window.popupAPI.on('history:keyEvent', function (data) { var key = data.key if (key === 'ArrowUp') { updateSelection(selectedIndex - 1) } else if (key === 'ArrowDown') { updateSelection(selectedIndex + 1) } else if (key === 'Enter') { selectAndInsert(selectedIndex) } else if (key === 'Escape') { window.popupAPI.send('history:popupDismissed', {}) } else if (key >= '1' && key <= '9') { var idx = parseInt(key, 10) - 1 if (idx < currentEntries.length) { selectAndInsert(idx) } } }) })()