From 9dd3eebcb4e851cde1966b8b24f391c56e5e344f Mon Sep 17 00:00:00 2001 From: Callum Alpass Date: Tue, 3 Jun 2025 22:16:47 +1000 Subject: [PATCH] Refactor UI styling, messaging, and SVG icon rendering across task settings and views MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit • Normalize wording for new priority/status and untitled tasks:  – In PriorityManager and StatusManager, update “New Priority”/“New Status” labels to use lowercase “priority/status”.  – Change default task titles from “Untitled Task” to “Untitled task” in task info extraction. • Update settings tab UI to use centralized CSS classes rather than inline styles:  – Remove inline flex, borders, paddings from tab navigation and table elements.  – Change “General” tab name to “Basic setup”.  – Replace inline warning message styling with a “settings-warning” div.  – Refactor status/priority list rows to use new classes (e.g., “settings-item-row”, “settings-input”, “settings-delete-button”, etc.).  – Update CSS (styles.css) with new styling rules for tab nav, table, lists, input elements, and buttons. • Simplify and secure SVG icon rendering in TaskListView:  – Replace innerHTML usage for SVG icons with a new createSVGIcon helper that safely constructs SVG nodes.  – Update toggle and archive button icon rendering to use the new helper methods.  – Update text content for time icons to use textContent rather than innerHTML.  – Ensure document click handler cleanup for dropdowns is properly tracked. • Clear YAML cache on file index rebuild:  – In FileIndexer, clear the global YAML cache when the index is rebuilt. • Miscellaneous improvements:  – Remove extraneous debugging logs in CalendarView.  – Adjust minor styling and element class names for clarity and consistency. This commit brings improved UI consistency, enhanced security by avoiding innerHTML for SVGs, and better code maintainability by moving styling to CSS. --- src/services/PriorityManager.ts | 2 +- src/services/StatusManager.ts | 2 +- src/settings/settings.ts | 145 +++++++++----------------------- src/utils/FileIndexer.ts | 1 + src/utils/helpers.ts | 4 +- src/views/CalendarView.ts | 8 +- src/views/NotesView.ts | 6 +- src/views/TaskListView.ts | 75 ++++++++++++----- styles.css | 126 +++++++++++++++++++++++++++ 9 files changed, 227 insertions(+), 142 deletions(-) diff --git a/src/services/PriorityManager.ts b/src/services/PriorityManager.ts index 284007d2..a1eda595 100644 --- a/src/services/PriorityManager.ts +++ b/src/services/PriorityManager.ts @@ -204,7 +204,7 @@ export class PriorityManager { return { id, value: 'new-priority', - label: 'New Priority', + label: 'New priority', color: '#808080', weight }; diff --git a/src/services/StatusManager.ts b/src/services/StatusManager.ts index 7173df06..fce6903f 100644 --- a/src/services/StatusManager.ts +++ b/src/services/StatusManager.ts @@ -170,7 +170,7 @@ export class StatusManager { return { id, value: 'new-status', - label: 'New Status', + label: 'New status', color: '#808080', isCompleted: false, order diff --git a/src/settings/settings.ts b/src/settings/settings.ts index 8b65369d..317c6a05 100644 --- a/src/settings/settings.ts +++ b/src/settings/settings.ts @@ -147,12 +147,9 @@ export class TaskNotesSettingTab extends PluginSettingTab { // Create tab navigation const tabNav = containerEl.createDiv('settings-tab-nav'); - tabNav.style.display = 'flex'; - tabNav.style.borderBottom = '1px solid var(--background-modifier-border)'; - tabNav.style.marginBottom = '20px'; const tabs = [ - { id: 'general', name: 'General' }, + { id: 'general', name: 'Basic setup' }, { id: 'field-mapping', name: 'Field mapping' }, { id: 'statuses', name: 'Statuses' }, { id: 'priorities', name: 'Priorities' }, @@ -166,13 +163,6 @@ export class TaskNotesSettingTab extends PluginSettingTab { cls: this.activeTab === tab.id ? 'settings-tab-button active' : 'settings-tab-button' }); - tabButton.style.padding = '8px 16px'; - tabButton.style.border = 'none'; - tabButton.style.background = this.activeTab === tab.id ? 'var(--interactive-accent)' : 'transparent'; - tabButton.style.color = this.activeTab === tab.id ? 'var(--text-on-accent)' : 'var(--text-normal)'; - tabButton.style.cursor = 'pointer'; - tabButton.style.borderRadius = '4px 4px 0 0'; - tabButton.addEventListener('click', () => { this.switchTab(tab.id); }); @@ -184,7 +174,9 @@ export class TaskNotesSettingTab extends PluginSettingTab { // Create all tab content containers tabs.forEach(tab => { const tabContent = tabContentsEl.createDiv('settings-tab-content'); - tabContent.style.display = this.activeTab === tab.id ? 'block' : 'none'; + if (this.activeTab === tab.id) { + tabContent.addClass('active'); + } this.tabContents[tab.id] = tabContent; }); @@ -329,15 +321,9 @@ export class TaskNotesSettingTab extends PluginSettingTab { const container = this.tabContents['field-mapping']; // Warning message - const warning = container.createDiv(); - warning.style.padding = '12px'; - warning.style.marginBottom = '20px'; - warning.style.backgroundColor = 'var(--background-modifier-warning)'; - warning.style.borderRadius = '4px'; - warning.innerHTML = ` - ⚠️ Warning: TaskNotes will read AND write using these property names. - Changing these after creating tasks may cause inconsistencies. - `; + const warning = container.createDiv('settings-warning'); + const warningIcon = warning.createEl('strong', { text: '⚠️ Warning:' }); + warning.createSpan({ text: ' TaskNotes will read AND write using these property names. Changing these after creating tasks may cause inconsistencies.' }); container.createEl('h3', { text: 'Field mapping' }); container.createEl('p', { @@ -345,9 +331,7 @@ export class TaskNotesSettingTab extends PluginSettingTab { }); // Create mapping table - const table = container.createEl('table'); - table.style.width = '100%'; - table.style.borderCollapse = 'collapse'; + const table = container.createEl('table', { cls: 'settings-table' }); const header = table.createEl('tr'); header.createEl('th', { text: 'TaskNotes field' }); @@ -371,20 +355,14 @@ export class TaskNotesSettingTab extends PluginSettingTab { fieldMappings.forEach(([field, label]) => { const row = table.createEl('tr'); const labelCell = row.createEl('td'); - labelCell.style.padding = '8px'; - labelCell.style.borderBottom = '1px solid var(--background-modifier-border)'; labelCell.textContent = label; const inputCell = row.createEl('td'); - inputCell.style.padding = '8px'; - inputCell.style.borderBottom = '1px solid var(--background-modifier-border)'; const input = inputCell.createEl('input', { type: 'text', value: this.plugin.settings.fieldMapping[field] }); - input.style.width = '100%'; - input.style.padding = '4px'; input.addEventListener('change', async () => { this.plugin.settings.fieldMapping[field] = input.value; @@ -409,14 +387,13 @@ export class TaskNotesSettingTab extends PluginSettingTab { private renderStatusesTab(): void { const container = this.tabContents['statuses']; - container.createEl('h3', { text: 'Task Statuses' }); + container.createEl('h3', { text: 'Task statuses' }); container.createEl('p', { text: 'Define the statuses available for your tasks. The order determines the cycling sequence.' }); // Status list - const statusList = container.createDiv(); - statusList.style.marginBottom = '20px'; + const statusList = container.createDiv('settings-list'); this.renderStatusList(statusList); @@ -424,7 +401,7 @@ export class TaskNotesSettingTab extends PluginSettingTab { new Setting(container) .setName('Add new status') .addButton(button => button - .setButtonText('Add Status') + .setButtonText('Add status') .onClick(async () => { const newStatus = StatusManager.createDefaultStatus(this.plugin.settings.customStatuses); this.plugin.settings.customStatuses.push(newStatus); @@ -439,72 +416,48 @@ export class TaskNotesSettingTab extends PluginSettingTab { const sortedStatuses = [...this.plugin.settings.customStatuses].sort((a, b) => a.order - b.order); sortedStatuses.forEach((status, index) => { - const statusRow = container.createDiv(); - statusRow.style.display = 'flex'; - statusRow.style.alignItems = 'center'; - statusRow.style.marginBottom = '12px'; - statusRow.style.padding = '12px'; - statusRow.style.border = '1px solid var(--background-modifier-border)'; - statusRow.style.borderRadius = '4px'; + const statusRow = container.createDiv('settings-item-row'); // Color indicator - const colorIndicator = statusRow.createDiv(); - colorIndicator.style.width = '20px'; - colorIndicator.style.height = '20px'; - colorIndicator.style.borderRadius = '50%'; - colorIndicator.style.backgroundColor = status.color; - colorIndicator.style.marginRight = '12px'; - colorIndicator.style.flexShrink = '0'; + const colorIndicator = statusRow.createDiv('settings-color-indicator'); + colorIndicator.style.backgroundColor = status.color; // Keep this - user color // Status value input const valueInput = statusRow.createEl('input', { type: 'text', - value: status.value + value: status.value, + cls: 'settings-input value-input' }); - valueInput.style.marginRight = '12px'; - valueInput.style.width = '120px'; // Status label input const labelInput = statusRow.createEl('input', { type: 'text', - value: status.label + value: status.label, + cls: 'settings-input label-input' }); - labelInput.style.marginRight = '12px'; - labelInput.style.width = '120px'; // Color input const colorInput = statusRow.createEl('input', { type: 'color', - value: status.color + value: status.color, + cls: 'settings-input color-input' }); - colorInput.style.marginRight = '12px'; - colorInput.style.width = '40px'; // Completed checkbox - const completedLabel = statusRow.createEl('label'); - completedLabel.style.marginRight = '12px'; - completedLabel.style.display = 'flex'; - completedLabel.style.alignItems = 'center'; + const completedLabel = statusRow.createEl('label', { cls: 'settings-checkbox-label' }); const completedCheckbox = completedLabel.createEl('input', { type: 'checkbox' }); completedCheckbox.checked = status.isCompleted; - completedCheckbox.style.marginRight = '4px'; completedLabel.createSpan({ text: 'Completed' }); // Delete button const deleteButton = statusRow.createEl('button', { - text: 'Delete' + text: 'Delete', + cls: 'settings-delete-button' }); - deleteButton.style.marginLeft = 'auto'; - deleteButton.style.backgroundColor = 'var(--interactive-accent-rgb)'; - deleteButton.style.color = 'var(--text-on-accent)'; - deleteButton.style.border = 'none'; - deleteButton.style.padding = '4px 8px'; - deleteButton.style.borderRadius = '4px'; - deleteButton.style.cursor = 'pointer'; // Event listeners const updateStatus = async () => { @@ -546,8 +499,7 @@ export class TaskNotesSettingTab extends PluginSettingTab { }); // Priority list - const priorityList = container.createDiv(); - priorityList.style.marginBottom = '20px'; + const priorityList = container.createDiv('settings-list'); this.renderPriorityList(priorityList); @@ -555,7 +507,7 @@ export class TaskNotesSettingTab extends PluginSettingTab { new Setting(container) .setName('Add new priority') .addButton(button => button - .setButtonText('Add Priority') + .setButtonText('Add priority') .onClick(async () => { const newPriority = PriorityManager.createDefaultPriority(this.plugin.settings.customPriorities); this.plugin.settings.customPriorities.push(newPriority); @@ -570,66 +522,45 @@ export class TaskNotesSettingTab extends PluginSettingTab { const sortedPriorities = [...this.plugin.settings.customPriorities].sort((a, b) => b.weight - a.weight); sortedPriorities.forEach((priority, index) => { - const priorityRow = container.createDiv(); - priorityRow.style.display = 'flex'; - priorityRow.style.alignItems = 'center'; - priorityRow.style.marginBottom = '12px'; - priorityRow.style.padding = '12px'; - priorityRow.style.border = '1px solid var(--background-modifier-border)'; - priorityRow.style.borderRadius = '4px'; + const priorityRow = container.createDiv('settings-item-row'); // Color indicator - const colorIndicator = priorityRow.createDiv(); - colorIndicator.style.width = '20px'; - colorIndicator.style.height = '20px'; - colorIndicator.style.borderRadius = '50%'; - colorIndicator.style.backgroundColor = priority.color; - colorIndicator.style.marginRight = '12px'; - colorIndicator.style.flexShrink = '0'; + const colorIndicator = priorityRow.createDiv('settings-color-indicator'); + colorIndicator.style.backgroundColor = priority.color; // Keep this - user color // Priority value input const valueInput = priorityRow.createEl('input', { type: 'text', - value: priority.value + value: priority.value, + cls: 'settings-input value-input' }); - valueInput.style.marginRight = '12px'; - valueInput.style.width = '120px'; // Priority label input const labelInput = priorityRow.createEl('input', { type: 'text', - value: priority.label + value: priority.label, + cls: 'settings-input label-input' }); - labelInput.style.marginRight = '12px'; - labelInput.style.width = '120px'; // Color input const colorInput = priorityRow.createEl('input', { type: 'color', - value: priority.color + value: priority.color, + cls: 'settings-input color-input' }); - colorInput.style.marginRight = '12px'; - colorInput.style.width = '40px'; // Weight input const weightInput = priorityRow.createEl('input', { type: 'number', - value: priority.weight.toString() + value: priority.weight.toString(), + cls: 'settings-input weight-input' }); - weightInput.style.marginRight = '12px'; - weightInput.style.width = '80px'; // Delete button const deleteButton = priorityRow.createEl('button', { - text: 'Delete' + text: 'Delete', + cls: 'settings-delete-button' }); - deleteButton.style.marginLeft = 'auto'; - deleteButton.style.backgroundColor = 'var(--interactive-accent-rgb)'; - deleteButton.style.color = 'var(--text-on-accent)'; - deleteButton.style.border = 'none'; - deleteButton.style.padding = '4px 8px'; - deleteButton.style.borderRadius = '4px'; - deleteButton.style.cursor = 'pointer'; // Event listeners const updatePriority = async () => { diff --git a/src/utils/FileIndexer.ts b/src/utils/FileIndexer.ts index eda004e7..327bc5d0 100644 --- a/src/utils/FileIndexer.ts +++ b/src/utils/FileIndexer.ts @@ -721,6 +721,7 @@ export class FileIndexer { // Clear all caches this.fileIndex = null; this.calendarCache.clear(); + YAMLCache.clearCache(); // Clear global YAML cache // Clear event handlers object this.eventHandlers = {}; diff --git a/src/utils/helpers.ts b/src/utils/helpers.ts index 2cf549bd..2b7b1eb8 100644 --- a/src/utils/helpers.ts +++ b/src/utils/helpers.ts @@ -324,7 +324,7 @@ export function extractTaskInfo( // Ensure required fields have defaults const taskInfo: TaskInfo = { - title: mappedTask.title || 'Untitled Task', + title: mappedTask.title || 'Untitled task', status: mappedTask.status || 'open', priority: mappedTask.priority || 'normal', due: mappedTask.due, @@ -367,7 +367,7 @@ export function extractTaskInfo( const contexts = yaml.contexts || []; return { - title: yaml.title || 'Untitled Task', + title: yaml.title || 'Untitled task', status: yaml.status || 'open', priority: yaml.priority || 'normal', due: yaml.due, diff --git a/src/views/CalendarView.ts b/src/views/CalendarView.ts index 689900e1..8c21ac93 100644 --- a/src/views/CalendarView.ts +++ b/src/views/CalendarView.ts @@ -371,7 +371,7 @@ export class CalendarView extends ItemView { const indicator = document.createElement('div'); indicator.className = 'cache-loading-indicator'; - indicator.innerHTML = 'Loading calendar data...'; + indicator.textContent = 'Loading calendar data...'; container.prepend(indicator); } @@ -887,7 +887,6 @@ export class CalendarView extends ItemView { let dailyNotesCache: Set; if (!CalendarView.dailyNotesInitialized) { - console.debug('First call to colorizeCalendarForDailyNotes, forcing cache rebuild'); // Use the targeted rebuild method instead of rebuilding the entire index dailyNotesCache = await this.plugin.fileIndexer.rebuildDailyNotesCache(currentYear, currentMonth); CalendarView.dailyNotesInitialized = true; @@ -897,11 +896,6 @@ export class CalendarView extends ItemView { dailyNotesCache = calendarData.dailyNotes; } - // Log the number of daily notes found for debugging - console.debug(`Found ${dailyNotesCache.size} daily notes for ${currentYear}-${currentMonth+1}`); - if (dailyNotesCache.size > 0) { - console.debug('Daily notes:', Array.from(dailyNotesCache)); - } // Find all calendar days const calendarDays = this.contentEl.querySelectorAll('.calendar-day'); diff --git a/src/views/NotesView.ts b/src/views/NotesView.ts index 714e670c..6b3c5ea7 100644 --- a/src/views/NotesView.ts +++ b/src/views/NotesView.ts @@ -131,10 +131,8 @@ export class NotesView extends ItemView { // Add loading indicator this.loadingIndicator = notesList.createDiv({ cls: 'loading-indicator' }); - this.loadingIndicator.innerHTML = ` -
-
Loading notes...
- `; + this.loadingIndicator.createDiv({ cls: 'loading-spinner' }); + this.loadingIndicator.createDiv({ cls: 'loading-text', text: 'Loading notes...' }); this.loadingIndicator.addClass('is-hidden'); // Show loading state diff --git a/src/views/TaskListView.ts b/src/views/TaskListView.ts index b29d18b8..48563203 100644 --- a/src/views/TaskListView.ts +++ b/src/views/TaskListView.ts @@ -166,7 +166,8 @@ export class TaskListView extends ItemView { 'aria-expanded': 'true' } }); - toggleOrgButton.innerHTML = ''; + const downArrowIcon = this.createSVGIcon('0 0 24 24', 16, 16, 'M7 10l5 5 5-5z'); + toggleOrgButton.appendChild(downArrowIcon); // Add label next to toggle const toggleLabel = primaryFiltersRow.createEl('span', { @@ -194,7 +195,9 @@ export class TaskListView extends ItemView { if (this.plugin.settings.taskOrgFiltersCollapsed) { organizationFiltersRow.addClass('is-hidden'); toggleOrgButton.setAttribute('aria-expanded', 'false'); - toggleOrgButton.innerHTML = ''; + toggleOrgButton.empty(); + const upArrowIcon = this.createSVGIcon('0 0 24 24', 16, 16, 'M7 14l5-5 5 5z'); + toggleOrgButton.appendChild(upArrowIcon); } // Status filter (moved to collapsible section) @@ -291,10 +294,16 @@ export class TaskListView extends ItemView { }); // Close dropdown when clicking outside - document.addEventListener('click', (e) => { + const documentClickHandler = (e: Event) => { if (!contextDropdown.contains(e.target as Node)) { contextMenu.addClass('is-hidden'); } + }; + document.addEventListener('click', documentClickHandler); + + // Track for cleanup + this.listeners.push(() => { + document.removeEventListener('click', documentClickHandler); }); // Toggle filters visibility @@ -306,9 +315,11 @@ export class TaskListView extends ItemView { organizationFiltersRow.addClass('is-hidden'); } toggleOrgButton.setAttribute('aria-expanded', isHidden.toString()); - toggleOrgButton.innerHTML = isHidden - ? '' - : ''; + toggleOrgButton.empty(); + const arrowIcon = isHidden + ? this.createSVGIcon('0 0 24 24', 16, 16, 'M7 10l5 5 5-5z') + : this.createSVGIcon('0 0 24 24', 16, 16, 'M7 14l5-5 5 5z'); + toggleOrgButton.appendChild(arrowIcon); // Save the collapse state this.plugin.settings.taskOrgFiltersCollapsed = !isHidden; @@ -330,10 +341,8 @@ export class TaskListView extends ItemView { // Add loading indicator this.loadingIndicator = taskList.createDiv({ cls: 'loading-indicator' }); - this.loadingIndicator.innerHTML = ` -
-
Loading tasks...
- `; + this.loadingIndicator.createDiv({ cls: 'loading-spinner' }); + this.loadingIndicator.createDiv({ cls: 'loading-text', text: 'Loading tasks...' }); this.loadingIndicator.addClass('is-hidden'); // Show loading state if we're fetching data @@ -433,6 +442,33 @@ export class TaskListView extends ItemView { return `${prefix}-${value.replace(/[^a-zA-Z0-9-]/g, '-')}`; } + /** + * Create SVG icon element safely without innerHTML + */ + private createSVGIcon(viewBox: string, width: number, height: number, pathData: string): SVGElement { + const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); + svg.setAttribute('viewBox', viewBox); + svg.setAttribute('width', width.toString()); + svg.setAttribute('height', height.toString()); + + const path = document.createElementNS('http://www.w3.org/2000/svg', 'path'); + path.setAttribute('fill', 'currentColor'); + path.setAttribute('d', pathData); + + svg.appendChild(path); + return svg; + } + + /** + * Create archive/unarchive icon + */ + private createArchiveIcon(isArchived: boolean): SVGElement { + const unarchiveIcon = 'M20.54 5.23l-1.39-1.68C18.88 3.21 18.47 3 18 3H6c-.47 0-.88.21-1.16.55L3.46 5.23C3.17 5.57 3 6.02 3 6.5V19c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V6.5c0-.48-.17-.93-.46-1.27zM12 17.5L6.5 12H10v-2h4v2h3.5L12 17.5zM5.12 5l.81-1h12l.94 1H5.12z'; + const archiveIcon = 'M20.54 5.23l-1.39-1.68C18.88 3.21 18.47 3 18 3H6c-.47 0-.88.21-1.16.55L3.46 5.23C3.17 5.57 3 6.02 3 6.5V19c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V6.5c0-.48-.17-.93-.46-1.27zM12 9.5l5.5 5.5H14v2h-4v-2H6.5L12 9.5zM5.12 5l.81-1h12l.94 1H5.12z'; + + return this.createSVGIcon('0 0 24 24', 16, 16, isArchived ? unarchiveIcon : archiveIcon); + } + /** * Format group name for display */ @@ -562,7 +598,7 @@ export class TaskListView extends ItemView { } }); - timeIcon.innerHTML = isTracking ? '⏸' : '▶'; + timeIcon.textContent = isTracking ? '⏸' : '▶'; timeIcon.addEventListener('click', async (e) => { e.stopPropagation(); @@ -654,9 +690,8 @@ export class TaskListView extends ItemView { }); // Add icon based on archive status - archiveButton.innerHTML = task.archived - ? '' - : ''; + const archiveIcon = this.createArchiveIcon(task.archived); + archiveButton.appendChild(archiveIcon); // Add event listener for archive toggle archiveButton.addEventListener('click', async (e) => { @@ -724,7 +759,7 @@ export class TaskListView extends ItemView { // Apply custom status color if (statusConfig) { - statusBadge.style.backgroundColor = statusConfig.color; + statusBadge.style.setProperty('background', statusConfig.color, 'important'); } // Add click handler for status badge (non-recurring tasks only) @@ -1103,7 +1138,7 @@ export class TaskListView extends ItemView { // Apply custom status color if (statusConfig) { - statusBadge.style.backgroundColor = statusConfig.color; + statusBadge.style.setProperty('background', statusConfig.color, 'important'); } } @@ -1129,9 +1164,9 @@ export class TaskListView extends ItemView { if (archiveButton) { archiveButton.className = `archive-button-icon ${updatedTask.archived ? 'archived' : ''}`; archiveButton.title = updatedTask.archived ? 'Unarchive this task' : 'Archive this task'; - archiveButton.innerHTML = updatedTask.archived - ? '' - : ''; + archiveButton.empty(); + const updatedArchiveIcon = this.createArchiveIcon(updatedTask.archived); + archiveButton.appendChild(updatedArchiveIcon); } // Update archived badge visibility @@ -1204,7 +1239,7 @@ export class TaskListView extends ItemView { const isTracking = !!activeSession; timeIcon.className = `time-icon ${isTracking ? 'tracking' : 'idle'}`; - timeIcon.innerHTML = isTracking ? '⏸' : '▶'; + timeIcon.textContent = isTracking ? '⏸' : '▶'; timeIcon.setAttribute('aria-label', isTracking ? 'Stop time tracking' : 'Start time tracking'); timeIcon.setAttribute('title', isTracking ? 'Stop time tracking' : 'Start time tracking'); } diff --git a/styles.css b/styles.css index c8b91017..b3e95ca2 100644 --- a/styles.css +++ b/styles.css @@ -2947,6 +2947,132 @@ button.processing::after { text-transform: uppercase; } +/* Settings tab styling */ +.settings-tab-nav { + display: flex; + border-bottom: 1px solid var(--background-modifier-border); + margin-bottom: 20px; +} + +.settings-tab-button { + padding: 8px 16px; + border: none; + background: transparent; + color: var(--text-normal); + cursor: pointer; + border-radius: 4px 4px 0 0; + transition: all var(--cs-transition-fast); +} + +.settings-tab-button.active { + background: var(--interactive-accent); + color: var(--text-on-accent); +} + +.settings-tab-button:hover:not(.active) { + background: var(--background-modifier-hover); +} + +.settings-tab-content { + display: none; +} + +.settings-tab-content.active { + display: block; +} + +/* Settings warning box */ +.settings-warning { + padding: 12px; + margin-bottom: 20px; + background-color: var(--background-modifier-warning); + border-radius: 4px; + border: 1px solid var(--color-orange); +} + +/* Settings table */ +.settings-table { + width: 100%; + border-collapse: collapse; +} + +.settings-table td { + padding: 8px; + border-bottom: 1px solid var(--background-modifier-border); +} + +.settings-table input { + width: 100%; + padding: 4px; +} + +/* Status/Priority lists */ +.settings-list { + margin-bottom: 20px; +} + +.settings-item-row { + display: flex; + align-items: center; + margin-bottom: 12px; + padding: 12px; + border: 1px solid var(--background-modifier-border); + border-radius: 4px; +} + +.settings-color-indicator { + width: 20px; + height: 20px; + border-radius: 50%; + margin-right: 12px; + flex-shrink: 0; +} + +.settings-input { + margin-right: 12px; +} + +.settings-input.value-input { + width: 120px; +} + +.settings-input.label-input { + width: 120px; +} + +.settings-input.color-input { + width: 40px; +} + +.settings-input.weight-input { + width: 80px; +} + +.settings-checkbox-label { + margin-right: 12px; + display: flex; + align-items: center; +} + +.settings-checkbox-label input { + margin-right: 4px; +} + +.settings-delete-button { + margin-left: auto; + background-color: var(--interactive-accent); + color: var(--text-on-accent); + border: none; + padding: 4px 8px; + border-radius: 4px; + cursor: pointer; + transition: all var(--cs-transition-fast); +} + +.settings-delete-button:hover { + background-color: var(--interactive-accent-hover); +} + .priority-badge { font-size: var(--cs-text-xs); padding: 1px var(--cs-spacing-xs);