diff --git a/src/features/backlinks/BacklinksSlice.ts b/src/features/backlinks/BacklinksSlice.ts index c20136b..59ac201 100644 --- a/src/features/backlinks/BacklinksSlice.ts +++ b/src/features/backlinks/BacklinksSlice.ts @@ -353,6 +353,7 @@ export class BacklinksSlice implements IPluginSlice, IBacklinksSlice { */ setOptions(options: { sort?: boolean; + sortDescending?: boolean; collapsed?: boolean; strategy?: string; theme?: string; diff --git a/src/features/backlinks/ui/BacklinksViewController.ts b/src/features/backlinks/ui/BacklinksViewController.ts index 223c4d0..19c5616 100644 --- a/src/features/backlinks/ui/BacklinksViewController.ts +++ b/src/features/backlinks/ui/BacklinksViewController.ts @@ -199,6 +199,7 @@ export class BacklinksViewController { */ setOptions(options: { sort?: boolean; + sortDescending?: boolean; collapsed?: boolean; strategy?: string; theme?: string; @@ -210,6 +211,8 @@ export class BacklinksViewController { const updatedState = this.headerController.updateStateFromOptions(options); this.renderOptions.collapsed = updatedState.isCollapsed; + this.renderOptions.sortByPath = updatedState.sortByPath; + this.renderOptions.sortDescending = updatedState.sortDescending; this.currentTheme = updatedState.currentTheme; this.applyCurrentOptions(); @@ -260,9 +263,22 @@ export class BacklinksViewController { const newState = this.headerController.toggleSort(); + // Keep render options in sync with header state + this.renderOptions.sortByPath = newState.sortByPath; + this.renderOptions.sortDescending = newState.sortDescending; + if (this.lastRenderContext) { this.applySortingToDOM(this.lastRenderContext.container, newState.sortDescending); } + + // Persist sort direction to settings via DOM custom event + const event = new CustomEvent('coalesce-settings-sort-changed', { + detail: { + sortByPath: newState.sortByPath, + descending: newState.sortDescending + } + }); + document.dispatchEvent(event); } private handleCollapseToggle(): void { @@ -302,6 +318,12 @@ export class BacklinksViewController { this.currentTheme = newState.currentTheme; this.applyThemeToContainer(newState.currentTheme); + + // Persist theme selection to settings via DOM custom event + const event = new CustomEvent('coalesce-settings-theme-changed', { + detail: { theme: newState.currentTheme } + }); + document.dispatchEvent(event); } private handleHeaderStyleChange(style: string): void { diff --git a/src/features/backlinks/ui/HeaderController.ts b/src/features/backlinks/ui/HeaderController.ts index fa4c65a..3ad781a 100644 --- a/src/features/backlinks/ui/HeaderController.ts +++ b/src/features/backlinks/ui/HeaderController.ts @@ -290,6 +290,7 @@ export class HeaderController { */ updateStateFromOptions(options: { sort?: boolean; + sortDescending?: boolean; collapsed?: boolean; strategy?: string; theme?: string; @@ -304,6 +305,10 @@ export class HeaderController { updated.sortByPath = options.sort; } + if (options.sortDescending !== undefined) { + updated.sortDescending = options.sortDescending; + } + if (options.collapsed !== undefined) { updated.isCollapsed = options.collapsed; } diff --git a/src/features/settings/core/SettingsCore.ts b/src/features/settings/core/SettingsCore.ts index 874f3e1..edaadd5 100644 --- a/src/features/settings/core/SettingsCore.ts +++ b/src/features/settings/core/SettingsCore.ts @@ -282,16 +282,23 @@ export class SettingsCore { /** * Handle sort state change from header slice. + * + * Persists both the sort direction and whether path-based sorting is enabled, + * mapping the in-memory header state to the persisted settings fields. */ async handleSortStateChange(payload: { sortByPath: boolean; descending: boolean }): Promise { - const descending = payload?.descending || false; - this.logger.debug('Handling sort state change', { descending }); + const descending = payload?.descending ?? false; + const sortByPath = payload?.sortByPath ?? false; + this.logger.debug('Handling sort state change', { sortByPath, descending }); try { - await this.updateSetting('sortDescending', descending); - this.logger.debug('Sort direction saved to settings', { descending }); + await this.updateSettings({ + sortDescending: descending, + sortByFullPath: sortByPath + }); + this.logger.debug('Sort settings saved', { sortByPath, descending }); } catch (error) { - this.logger.error('Failed to save sort direction to settings', { descending, error }); + this.logger.error('Failed to save sort settings', { sortByPath, descending, error }); } } diff --git a/src/orchestrator/PluginEvents.ts b/src/orchestrator/PluginEvents.ts index d633af0..8e3f0ec 100644 --- a/src/orchestrator/PluginEvents.ts +++ b/src/orchestrator/PluginEvents.ts @@ -45,6 +45,62 @@ export function registerPluginEvents( } }); + // coalesce-settings-sort-changed + document.addEventListener('coalesce-settings-sort-changed', (event: CustomEvent) => { + const { sortByPath, descending } = event.detail || {}; + logger?.debug?.('Received coalesce-settings-sort-changed event', { + sortByPath, + descending, + }); + + try { + const settingsSlice = orchestrator.getSlice('settings') as any; + if (settingsSlice && typeof settingsSlice.handleSortStateChange === 'function') { + settingsSlice.handleSortStateChange({ + sortByPath: !!sortByPath, + descending: !!descending, + }); + } else { + logger?.warn?.( + 'Settings slice not available or handleSortStateChange method not found', + ); + } + } catch (error) { + logger?.error?.('Failed to handle coalesce-settings-sort-changed event', { + sortByPath, + descending, + error, + }); + } + }); + + // coalesce-settings-theme-changed + document.addEventListener('coalesce-settings-theme-changed', (event: CustomEvent) => { + const { theme } = event.detail || {}; + logger?.debug?.('Received coalesce-settings-theme-changed event', { theme }); + + if (!theme) { + return; + } + + try { + const settingsSlice = orchestrator.getSlice('settings') as any; + if (settingsSlice && typeof settingsSlice.updateSetting === 'function') { + // Fire-and-forget persistence of theme changes from the header UI + void settingsSlice.updateSetting('theme', theme); + } else { + logger?.warn?.( + 'Settings slice not available or updateSetting method not found for theme change', + ); + } + } catch (error) { + logger?.error?.('Failed to handle coalesce-settings-theme-changed event', { + theme, + error, + }); + } + }); + // coalesce-logging-state-changed document.addEventListener('coalesce-logging-state-changed', (event: CustomEvent) => { const { enabled } = event.detail; @@ -202,6 +258,7 @@ export function registerPluginEvents( (backlinks as any)?.setOptions?.({ sort: settings.sortByFullPath || false, + sortDescending: settings.sortDescending ?? true, collapsed: settings.blocksCollapsed || false, strategy: 'default', theme: settings.theme || 'default', diff --git a/src/orchestrator/PluginViewInitializer.ts b/src/orchestrator/PluginViewInitializer.ts index a6de91a..c082929 100644 --- a/src/orchestrator/PluginViewInitializer.ts +++ b/src/orchestrator/PluginViewInitializer.ts @@ -143,6 +143,7 @@ export class PluginViewInitializer { if (uiAttached) { backlinksSlice.setOptions?.({ sort: settings.sortByFullPath || false, + sortDescending: settings.sortDescending ?? true, collapsed: settings.blocksCollapsed || false, strategy: 'default', // Default strategy theme: settings.theme || 'default',