// HistoryPopup 팝업 스크립트 — v4 (2026-09-28) // 커서 곁에 최근 받아쓰기 목록. ↑↓ 이동, Enter 붙여넣기, 1-9 바로 고르기, Esc 닫기(키는 main 이 전역으로 받아 넘긴다). // 문구는 전부 main 의 _i18n. 영어 폴백("just now", "No history yet")을 두지 않는다. // 행 높이(40px)는 main/windows/popup-list-geometry.ts 와 짝이다. ;(function () { 'use strict' var container = document.getElementById('container') var itemsContainer = document.getElementById('items') var items = [] var selectedIndex = 0 var currentEntries = [] var i18n = {} function tr(key) { return (i18n && i18n[key]) || '' } function applyI18nToHtml() { document.querySelectorAll('[data-i18n]').forEach(function (el) { el.textContent = tr(el.getAttribute('data-i18n')) }) } function relativeTime(timestamp) { var sec = Math.floor((Date.now() - timestamp) / 1000) if (sec < 60) return tr('timeJustNow') var min = Math.floor(sec / 60) if (min < 60) return tr('timeMinutesAgo').replace('{{m}}', min) var hr = Math.floor(min / 60) if (hr < 24) return tr('timeHoursAgo').replace('{{h}}', hr) return tr('timeDaysAgo').replace('{{d}}', Math.floor(hr / 24)) } function renderItems(entries) { currentEntries = entries itemsContainer.innerHTML = '' items = [] if (entries.length === 0) { var empty = document.createElement('div') empty.className = 'list-empty' empty.textContent = tr('historyEmpty') itemsContainer.appendChild(empty) return } entries.forEach(function (entry, index) { var row = document.createElement('div') row.className = 'd3-item d3-list-item' + (index === selectedIndex ? ' selected' : '') row.id = 'history-item-' + index row.setAttribute('role', 'option') row.setAttribute('aria-selected', index === selectedIndex ? 'true' : 'false') var num = document.createElement('kbd') num.className = 'item-number' num.textContent = index < 9 ? String(index + 1) : '' var text = document.createElement('span') text.className = 'item-text' text.textContent = entry.polishedText || entry.originalText var time = document.createElement('span') time.className = 'item-meta d3-numeral' time.textContent = relativeTime(entry.createdAt) row.appendChild(num) row.appendChild(text) row.appendChild(time) row.addEventListener('click', function () { selectAndInsert(index) }) itemsContainer.appendChild(row) items.push(row) }) itemsContainer.setAttribute('aria-activedescendant', 'history-item-' + selectedIndex) } 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) { item.classList.toggle('selected', i === newIndex) item.setAttribute('aria-selected', i === newIndex ? 'true' : 'false') }) selectedIndex = newIndex itemsContainer.setAttribute('aria-activedescendant', 'history-item-' + selectedIndex) if (items[selectedIndex]) items[selectedIndex].scrollIntoView({ block: 'nearest' }) } function selectAndInsert(index) { if (index < 0 || index >= currentEntries.length) return var entry = currentEntries[index] window.popupAPI.send('history:itemSelected', { id: entry.id, text: entry.polishedText || entry.originalText }) } window.popupAPI.on('history:showItems', function (data) { selectedIndex = 0 if (data._i18n) i18n = data._i18n applyI18nToHtml() renderItems(data.entries || []) container.classList.remove('hiding') }) 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') }) 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) } }) })()