From caaeca6a881ff06512b591580d42863fae386328 Mon Sep 17 00:00:00 2001 From: Daniel Dada Date: Thu, 5 Feb 2026 16:48:53 +0300 Subject: [PATCH] fixed sizing --- list-manager/list-manager.css | 167 +++++++++++++--------- list-manager/list-manager.html | 34 ++--- src/background.ts | 4 +- src/list-manager/ListManagerController.ts | 155 +++++++++++++++++--- src/popup/PopupController.ts | 4 +- 5 files changed, 257 insertions(+), 107 deletions(-) diff --git a/list-manager/list-manager.css b/list-manager/list-manager.css index ac9a97e..6eb3b05 100644 --- a/list-manager/list-manager.css +++ b/list-manager/list-manager.css @@ -5,14 +5,18 @@ box-sizing: border-box; } +html { + height: 100%; +} + body { font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; margin: 0; background: radial-gradient(120% 120% at 10% 0%, rgba(204, 106, 42, 0.08) 0%, transparent 45%), linear-gradient(180deg, var(--bg-color) 0%, #f5efe9 100%); color: var(--text-color); - min-width: 800px; - min-height: 600px; + height: 100%; + overflow: hidden; } html.dark body, @@ -25,6 +29,7 @@ body.dark { display: flex; flex-direction: column; height: 100vh; + overflow: hidden; } .topbar { @@ -70,10 +75,12 @@ body.dark .topbar { .layout { display: grid; - grid-template-columns: 320px 1fr; + grid-template-columns: minmax(280px, 25%) 1fr; gap: 16px; padding: 16px; - height: calc(100vh - 72px); + flex: 1; + overflow: hidden; + min-height: 0; } .panel { @@ -85,6 +92,7 @@ body.dark .topbar { flex-direction: column; gap: 12px; overflow: hidden; + min-height: 0; } .panel-header { @@ -149,6 +157,8 @@ body.dark .topbar { gap: 10px; overflow-y: auto; padding-right: 4px; + flex: 1; + min-height: 0; } /* Custom scrollbar styling - List Manager Specific */ @@ -291,37 +301,87 @@ body.dark .list-item.selected.active { display: flex; flex-direction: column; gap: 12px; + min-height: 0; + overflow: hidden; } .list-settings { - display: grid; - gap: 12px; - grid-template-columns: repeat(2, minmax(0, 1fr)); - align-items: end; + display: none; + gap: 8px; + padding: 8px; + background: rgba(204, 106, 42, 0.05); + border-radius: 8px; + border: 1px solid var(--input-border); } -.list-settings label { +html.dark .list-settings, +body.dark .list-settings { + background: rgba(242, 168, 101, 0.08); +} + +.list-settings.expanded { display: flex; flex-direction: column; - gap: 6px; - font-size: 0.85rem; } .list-settings input[type="text"] { width: 100%; + padding: 6px 10px; + font-size: 0.9rem; } -.colors { +.color-row { display: flex; - gap: 12px; - grid-column: span 2; - align-items: end; + gap: 8px; + align-items: center; } -.colors label { +.compact-label { + display: flex; + align-items: center; + gap: 6px; + font-size: 0.85rem; flex: 1; } +.compact-label span { + min-width: 24px; + font-weight: 500; + opacity: 0.8; +} + +.compact-label input[type="color"] { + flex: 1; + height: 32px; +} + +.compact-btn { + padding: 6px 12px; + min-width: auto; + height: 32px; +} + +.compact-header { + flex-direction: row; + align-items: center; + justify-content: space-between; +} + +.list-title-section { + display: flex; + align-items: center; + gap: 8px; +} + +.list-title-section h2 { + margin: 0; + font-size: 1rem; + max-width: 300px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + .word-controls { display: flex; @@ -371,6 +431,8 @@ body.dark .list-item.selected.active { display: flex; flex-direction: column; gap: 8px; + flex: 1 1 0; + min-height: 0; } html.dark .word-list, @@ -502,13 +564,16 @@ input[type="color"] { .pagination-container { display: flex; - flex-direction: column; + flex-direction: row; + align-items: center; + justify-content: space-between; gap: 12px; - padding: 12px; + padding: 8px 12px; background: #f9f4f0; border: 1px solid var(--input-border); border-radius: 12px; - margin-top: 8px; + flex-shrink: 0; + flex-wrap: wrap; } html.dark .pagination-container, @@ -517,32 +582,31 @@ body.dark .pagination-container { } .pagination-info { - font-size: 0.85rem; + font-size: 0.8rem; opacity: 0.8; - text-align: center; + white-space: nowrap; } .pagination-controls { display: flex; align-items: center; - justify-content: center; - gap: 8px; + gap: 6px; } .pagination-btn { background: var(--button-bg); border: 1px solid var(--input-border); color: var(--button-text); - border-radius: 8px; - padding: 6px 8px; + border-radius: 6px; + padding: 4px 6px; cursor: pointer; transition: all 0.2s ease; display: flex; align-items: center; justify-content: center; - min-width: 32px; - min-height: 32px; - font-size: 0.8rem; + min-width: 28px; + min-height: 28px; + font-size: 0.75rem; } .pagination-btn:hover:not(:disabled) { @@ -558,11 +622,11 @@ body.dark .pagination-container { .pagination-pages { display: flex; align-items: center; - padding: 0 12px; + padding: 0 8px; } .page-info { - font-size: 0.85rem; + font-size: 0.8rem; opacity: 0.9; font-weight: 500; } @@ -570,23 +634,23 @@ body.dark .pagination-container { .page-size-controls { display: flex; align-items: center; - justify-content: center; - gap: 8px; - font-size: 0.85rem; + gap: 6px; + font-size: 0.8rem; } .page-size-controls label { opacity: 0.8; + white-space: nowrap; } .page-size-select { background: var(--input-bg); color: var(--text-color); border: 1px solid var(--input-border); - border-radius: 8px; - padding: 4px 8px; - font-size: 0.85rem; - min-width: 60px; + border-radius: 6px; + padding: 3px 6px; + font-size: 0.8rem; + min-width: 55px; } .page-size-select:focus { @@ -596,31 +660,4 @@ body.dark .pagination-container { -@media (max-width: 920px) { - body { - min-width: 100%; - } - - .layout { - grid-template-columns: 1fr; - height: auto; - } - - .pagination-container { - flex-wrap: wrap; - gap: 8px; - } - - .pagination-controls { - order: 2; - } - - .pagination-info { - order: 1; - width: 100%; - } - - .page-size-controls { - order: 3; - } -} +/* Removed single column layout - minimum width enforced for two-column layout */ diff --git a/list-manager/list-manager.html b/list-manager/list-manager.html index 5af26f0..a7caf9d 100644 --- a/list-manager/list-manager.html +++ b/list-manager/list-manager.html @@ -3,7 +3,7 @@ - + Goose Highlighter - List Manager @@ -46,31 +46,33 @@ -
Drag lists to reorder
+
Drag lists to reorder • Ctrl+Click for multi-select
-
-

Selected List

+
+
+

Selected List

+ +
0 words
-
- -
-
diff --git a/src/background.ts b/src/background.ts index ed43f63..42c87f0 100644 --- a/src/background.ts +++ b/src/background.ts @@ -52,8 +52,8 @@ class BackgroundService { chrome.windows.create({ url: chrome.runtime.getURL('list-manager/list-manager.html'), type: 'popup', - width: 800, - height: 600, + width: 1280, + height: 700, focused: true }); } diff --git a/src/list-manager/ListManagerController.ts b/src/list-manager/ListManagerController.ts index edaf480..82dd072 100644 --- a/src/list-manager/ListManagerController.ts +++ b/src/list-manager/ListManagerController.ts @@ -47,6 +47,7 @@ async initialize(): Promise { document.getElementById('deleteListsBtn')?.addEventListener('click', () => this.deleteSelectedLists()); document.getElementById('activateListsBtn')?.addEventListener('click', () => this.setSelectedListsActive(true)); document.getElementById('deactivateListsBtn')?.addEventListener('click', () => this.setSelectedListsActive(false)); + document.getElementById('editListNameBtn')?.addEventListener('click', () => this.toggleListSettings()); document.getElementById('applyListSettingsBtn')?.addEventListener('click', () => this.applyListSettings()); document.getElementById('importListBtn')?.addEventListener('click', () => this.triggerImport()); document.getElementById('exportListBtn')?.addEventListener('click', () => this.exportCurrentList()); @@ -83,6 +84,8 @@ const wordSearch = document.getElementById('wordSearch') as HTMLInputElement; wordList?.addEventListener('change', (e) => this.handleWordListChange(e)); wordList?.addEventListener('keydown', (e) => this.handleWordListKeydown(e)); wordList?.addEventListener('blur', (e) => this.handleWordListBlur(e), true); + wordList?.addEventListener('dragstart', (e) => this.handleWordDragStart(e)); + wordList?.addEventListener('dragend', () => this.clearDragState()); } private setupStorageSync(): void { @@ -208,9 +211,24 @@ const wordSearch = document.getElementById('wordSearch') as HTMLInputElement; list.background = listBg.value; list.foreground = listFg.value; + this.toggleListSettings(); this.save(); } + private toggleListSettings(): void { + const panel = document.getElementById('listSettingsPanel'); + if (!panel) return; + + if (panel.classList.contains('expanded')) { + panel.classList.remove('expanded'); + } else { + panel.classList.add('expanded'); + const listName = document.getElementById('listName') as HTMLInputElement; + listName?.focus(); + listName?.select(); + } + } + private exportCurrentList(): void { const list = this.lists[this.currentListIndex]; if (!list) return; @@ -427,6 +445,7 @@ const wordSearch = document.getElementById('wordSearch') as HTMLInputElement; this.currentListIndex = index; this.selectedLists.clear(); this.selectedWords.clear(); + this.currentPage = 1; // Reset to first page when selecting a list this.render(); } @@ -436,15 +455,26 @@ const wordSearch = document.getElementById('wordSearch') as HTMLInputElement; const index = Number(target.dataset.index); if (Number.isNaN(index)) return; - event.dataTransfer?.setData('text/plain', index.toString()); + event.dataTransfer?.setData('text/plain', JSON.stringify({ type: 'list', index })); event.dataTransfer?.setDragImage(target, 10, 10); } private handleDragOver(event: DragEvent): void { event.preventDefault(); const target = (event.target as HTMLElement).closest('.list-item') as HTMLElement | null; - if (!target) return; - target.classList.add('drag-over'); + if (!target) { + this.clearDragState(); + return; + } + + // Clear drag state from all items first + this.clearDragState(); + + // Check if we're dragging words or lists + const data = event.dataTransfer?.types.includes('text/plain'); + if (data) { + target.classList.add('drag-over'); + } } private handleDrop(event: DragEvent): void { @@ -452,32 +482,105 @@ const wordSearch = document.getElementById('wordSearch') as HTMLInputElement; const target = (event.target as HTMLElement).closest('.list-item') as HTMLElement | null; if (!target) return; - const sourceIndex = Number(event.dataTransfer?.getData('text/plain')); const targetIndex = Number(target.dataset.index); - if (Number.isNaN(sourceIndex) || Number.isNaN(targetIndex) || sourceIndex === targetIndex) { + if (Number.isNaN(targetIndex)) { this.clearDragState(); return; } - const [moved] = this.lists.splice(sourceIndex, 1); - this.lists.splice(targetIndex, 0, moved); + try { + const dataStr = event.dataTransfer?.getData('text/plain'); + if (!dataStr) { + this.clearDragState(); + return; + } - if (this.currentListIndex === sourceIndex) { - this.currentListIndex = targetIndex; - } else if (sourceIndex < this.currentListIndex && targetIndex >= this.currentListIndex) { - this.currentListIndex -= 1; - } else if (sourceIndex > this.currentListIndex && targetIndex <= this.currentListIndex) { - this.currentListIndex += 1; + const data = JSON.parse(dataStr); + + // Handle word drag + if (data.type === 'words') { + this.dropWordsOnList(data.wordIndices, targetIndex); + this.clearDragState(); + return; + } + + // Handle list drag (reordering) + if (data.type === 'list') { + const sourceIndex = data.index; + if (sourceIndex === targetIndex) { + this.clearDragState(); + return; + } + + const [moved] = this.lists.splice(sourceIndex, 1); + this.lists.splice(targetIndex, 0, moved); + + if (this.currentListIndex === sourceIndex) { + this.currentListIndex = targetIndex; + } else if (sourceIndex < this.currentListIndex && targetIndex >= this.currentListIndex) { + this.currentListIndex -= 1; + } else if (sourceIndex > this.currentListIndex && targetIndex <= this.currentListIndex) { + this.currentListIndex += 1; + } + + this.selectedLists.clear(); + this.save(); + } + } catch (error) { + console.error('Drop error:', error); } - this.selectedLists.clear(); - this.save(); + this.clearDragState(); } private clearDragState(): void { document.querySelectorAll('.list-item.drag-over').forEach(item => item.classList.remove('drag-over')); } + private handleWordDragStart(event: DragEvent): void { + const target = (event.target as HTMLElement).closest('.word-item') as HTMLElement | null; + if (!target) return; + + const index = Number(target.dataset.index); + if (Number.isNaN(index)) return; + + // If dragging a selected word, drag all selected words + let wordIndices: number[]; + if (this.selectedWords.has(index)) { + wordIndices = Array.from(this.selectedWords); + } else { + wordIndices = [index]; + } + + event.dataTransfer?.setData('text/plain', JSON.stringify({ type: 'words', wordIndices })); + if (event.dataTransfer) { + event.dataTransfer.effectAllowed = 'copy'; + } + } + + private dropWordsOnList(wordIndices: number[], targetListIndex: number): void { + const sourceList = this.lists[this.currentListIndex]; + const targetList = this.lists[targetListIndex]; + + if (!sourceList || !targetList) return; + if (targetListIndex === this.currentListIndex) return; // Can't drop on same list + + const wordsToCopy = wordIndices + .map(index => sourceList.words[index]) + .filter(Boolean) + .map(word => ({ ...word })); // Create copies + + if (wordsToCopy.length === 0) return; + + targetList.words.push(...wordsToCopy); + this.save(); + + // Show feedback + const count = wordsToCopy.length; + const message = `Copied ${count} word${count > 1 ? 's' : ''} to "${targetList.name}"`; + console.log(message); + } + private handleWordListClick(event: Event): void { const target = event.target as HTMLElement; const list = this.lists[this.currentListIndex]; @@ -541,12 +644,9 @@ const wordSearch = document.getElementById('wordSearch') as HTMLInputElement; return; } - // Regular click - toggle selection - if (this.selectedWords.has(index)) { - this.selectedWords.delete(index); - } else { - this.selectedWords.add(index); - } + // Regular click - clear all and select only this one + this.selectedWords.clear(); + this.selectedWords.add(index); this.renderWords(); } @@ -664,6 +764,11 @@ const wordSearch = document.getElementById('wordSearch') as HTMLInputElement; const list = this.lists[this.currentListIndex]; if (!list) return; + const selectedListName = document.getElementById('selectedListName'); + if (selectedListName) { + selectedListName.textContent = list.name; + } + (document.getElementById('listName') as HTMLInputElement).value = list.name; (document.getElementById('listBg') as HTMLInputElement).value = list.background; (document.getElementById('listFg') as HTMLInputElement).value = list.foreground; @@ -675,6 +780,12 @@ const wordSearch = document.getElementById('wordSearch') as HTMLInputElement; stats.textContent = `${list.words.length} words • ${activeCount} active • ${inactiveCount} inactive`; } + // Collapse settings panel when switching lists + const panel = document.getElementById('listSettingsPanel'); + if (panel) { + panel.classList.remove('expanded'); + } + this.renderTargetListOptions(); } @@ -714,7 +825,7 @@ private renderWords(): void { const index = entry.index; const isSelected = this.selectedWords.has(index); return ` -
+
${DOMUtils.escapeHtml(word.wordStr)}
diff --git a/src/popup/PopupController.ts b/src/popup/PopupController.ts index 0108a9f..8b7006d 100644 --- a/src/popup/PopupController.ts +++ b/src/popup/PopupController.ts @@ -693,8 +693,8 @@ export class PopupController { chrome.windows.create({ url: chrome.runtime.getURL('list-manager/list-manager.html'), type: 'popup', - width: 800, - height: 600, + width: 1280, + height: 700, focused: true }); }