Refactor UI styling, messaging, and SVG icon rendering across task settings and views

• 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.
This commit is contained in:
Callum Alpass 2025-06-03 22:16:47 +10:00
parent 8905686a69
commit 9dd3eebcb4
9 changed files with 227 additions and 142 deletions

View file

@ -204,7 +204,7 @@ export class PriorityManager {
return {
id,
value: 'new-priority',
label: 'New Priority',
label: 'New priority',
color: '#808080',
weight
};

View file

@ -170,7 +170,7 @@ export class StatusManager {
return {
id,
value: 'new-status',
label: 'New Status',
label: 'New status',
color: '#808080',
isCompleted: false,
order

View file

@ -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 = `
<strong> Warning:</strong> 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 () => {

View file

@ -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 = {};

View file

@ -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,

View file

@ -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<string>;
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');

View file

@ -131,10 +131,8 @@ export class NotesView extends ItemView {
// Add loading indicator
this.loadingIndicator = notesList.createDiv({ cls: 'loading-indicator' });
this.loadingIndicator.innerHTML = `
<div class="loading-spinner"></div>
<div class="loading-text">Loading notes...</div>
`;
this.loadingIndicator.createDiv({ cls: 'loading-spinner' });
this.loadingIndicator.createDiv({ cls: 'loading-text', text: 'Loading notes...' });
this.loadingIndicator.addClass('is-hidden');
// Show loading state

View file

@ -166,7 +166,8 @@ export class TaskListView extends ItemView {
'aria-expanded': 'true'
}
});
toggleOrgButton.innerHTML = '<svg viewBox="0 0 24 24" width="16" height="16"><path fill="currentColor" d="M7 10l5 5 5-5z"></path></svg>';
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 = '<svg viewBox="0 0 24 24" width="16" height="16"><path fill="currentColor" d="M7 14l5-5 5 5z"></path></svg>';
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
? '<svg viewBox="0 0 24 24" width="16" height="16"><path fill="currentColor" d="M7 10l5 5 5-5z"></path></svg>'
: '<svg viewBox="0 0 24 24" width="16" height="16"><path fill="currentColor" d="M7 14l5-5 5 5z"></path></svg>';
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 = `
<div class="loading-spinner"></div>
<div class="loading-text">Loading tasks...</div>
`;
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
? '<svg viewBox="0 0 24 24" width="16" height="16"><path fill="currentColor" d="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"></path></svg>'
: '<svg viewBox="0 0 24 24" width="16" height="16"><path fill="currentColor" d="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"></path></svg>';
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
? '<svg viewBox="0 0 24 24" width="16" height="16"><path fill="currentColor" d="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"></path></svg>'
: '<svg viewBox="0 0 24 24" width="16" height="16"><path fill="currentColor" d="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"></path></svg>';
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');
}

View file

@ -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);