diff --git a/components/SettingsUI/SynchronizationSettings.tsx b/components/SettingsUI/SynchronizationSettings.tsx index 15a1f8c..fb300b3 100644 --- a/components/SettingsUI/SynchronizationSettings.tsx +++ b/components/SettingsUI/SynchronizationSettings.tsx @@ -1,4 +1,4 @@ -import { PluginSettings } from "@/types/pluginSettings"; +import { PluginSettings, SyncGroup } from "@/types/pluginSettings"; import React from "react"; import { SettingItem } from "./SettingItem"; import settingsService from "@/core/settingsService"; @@ -22,18 +22,52 @@ export const SynchronizationSettings: React.FC = ({ alert("Plugin settings imported successfully."); }; + const toggleGroup = (group: SyncGroup) => { + const currentGroups = [...settings.syncGroups]; + const index = currentGroups.indexOf(group); + if (index === -1) { + currentGroups.push(group); + } else { + currentGroups.splice(index, 1); + } + onChange("syncGroups", currentGroups); + }; + + const syncGroups: { id: SyncGroup; label: string; description: string }[] = + [ + { + id: "statuses", + label: "Statuses & Templates", + description: "Custom statuses, templates, and workflow rules.", + }, + { + id: "appearance", + label: "Appearance", + description: "Colors, icons, and UI element styles.", + }, + { + id: "behavior", + label: "Behavior & Storage", + description: "Frontmatter keys, mappings, and general logic.", + }, + { + id: "features", + label: "Commands & Features", + description: "Quick commands and experimental feature toggles.", + }, + ]; + return (

Multi-device Synchronization

- Keep all your plugin settings (statuses, templates, UI - preferences, etc.) in sync across devices using a file in your - vault. + Keep your plugin settings in sync across devices using a file in + your vault.

= ({ /> +
+

Selective Synchronization

+

+ Choose which groups of settings should be included in the + synchronization. +

+
+ {syncGroups.map((group) => ( +
toggleGroup(group.id)} + > +
+ {}} // Handled by parent div onClick + style={{ marginRight: "8px" }} + /> + {group.label} +
+
+ {group.description} +
+
+ ))} +
+
+
- - + +
); diff --git a/constants/defaultSettings.ts b/constants/defaultSettings.ts index 773759a..d70f5f0 100644 --- a/constants/defaultSettings.ts +++ b/constants/defaultSettings.ts @@ -57,4 +57,5 @@ export const DEFAULT_PLUGIN_SETTINGS: PluginSettings = { writeMappedTagsToDefault: false, enableExternalStatusSync: false, externalStatusSyncPath: "_note-status-sync.json", + syncGroups: ["statuses", "appearance", "behavior", "features"], }; diff --git a/core/settingsService.ts b/core/settingsService.ts index 9c859e5..09e418f 100644 --- a/core/settingsService.ts +++ b/core/settingsService.ts @@ -1,8 +1,61 @@ import { DEFAULT_PLUGIN_SETTINGS } from "@/constants/defaultSettings"; -import { PluginSettings } from "@/types/pluginSettings"; +import { PluginSettings, SyncGroup } from "@/types/pluginSettings"; import { Plugin, TFile, normalizePath } from "obsidian"; import eventBus from "./eventBus"; +export const SETTINGS_GROUPS: Record = { + statuses: [ + "templates", + "customStatuses", + "enabledTemplates", + "useCustomStatusesOnly", + "useMultipleStatuses", + "singleStatusStorageMode", + "strictStatuses", + ], + appearance: [ + "fileExplorerIconPosition", + "fileExplorerIconFrame", + "fileExplorerIconColorMode", + "statusColors", + "showStatusBar", + "autoHideStatusBar", + "statusBarShowTemplateName", + "statusBarBadgeStyle", + "statusBarBadgeContentMode", + "showStatusIconsInExplorer", + "hideUnknownStatusInExplorer", + "fileExplorerColorFileName", + "fileExplorerColorBlock", + "fileExplorerLeftBorder", + "fileExplorerStatusDot", + "fileExplorerUnderlineFileName", + "unknownStatusIcon", + "unknownStatusLucideIcon", + "unknownStatusColor", + "statusBarNoStatusText", + "statusBarShowNoStatusIcon", + "statusBarShowNoStatusText", + "showEditorToolbarButton", + "editorToolbarButtonPosition", + "editorToolbarButtonDisplay", + ], + behavior: [ + "tagPrefix", + "statusFrontmatterMappings", + "writeMappedTagsToDefault", + "applyStatusRecursivelyToSubfolders", + "vaultSizeLimit", + "enableStatusOverviewPopup", + ], + features: [ + "quickStatusCommands", + "enableExperimentalFeatures", + "enableStatusDashboard", + "enableGroupedStatusView", + ], +}; + class SettingsService { private plugin: Plugin; public settings: PluginSettings; @@ -84,8 +137,22 @@ class SettingsService { if (!this.settings.enableExternalStatusSync && !force) return; const path = normalizePath(this.settings.externalStatusSyncPath); + + // Filter settings based on selected groups + const keysToSync = new Set(); + this.settings.syncGroups.forEach((group) => { + SETTINGS_GROUPS[group].forEach((key) => keysToSync.add(key)); + }); + + const filteredSettings: Partial = {}; + keysToSync.forEach((key) => { + (filteredSettings as Record)[key] = + this.settings[key]; + }); + const data = { - ...this.settings, + ...filteredSettings, + syncGroups: this.settings.syncGroups, // Always include meta-settings updatedAt: new Date().toISOString(), }; @@ -108,13 +175,19 @@ class SettingsService { const content = await this.plugin.app.vault.adapter.read(path); const data = JSON.parse(content); + // Determine which keys we should actually apply based on CURRENT settings + const allowedKeys = new Set(); + this.settings.syncGroups.forEach((group) => { + SETTINGS_GROUPS[group].forEach((key) => allowedKeys.add(key)); + }); + let hasChanged = false; const keys = Object.keys(data).filter( - (k) => k !== "updatedAt", + (k) => k !== "updatedAt" && k !== "syncGroups", ) as Array; for (const key of keys) { - if (!(key in this.settings)) continue; + if (!(key in this.settings) || !allowedKeys.has(key)) continue; const newValue = data[key]; const currentValue = this.settings[key]; diff --git a/types/pluginSettings.ts b/types/pluginSettings.ts index 843979a..fc25208 100644 --- a/types/pluginSettings.ts +++ b/types/pluginSettings.ts @@ -22,6 +22,8 @@ export interface StatusTemplate { statuses: NoteStatus[]; } +export type SyncGroup = "statuses" | "appearance" | "behavior" | "features"; + export type PluginSettings = { fileExplorerIconPosition: | "absolute-right" @@ -72,5 +74,6 @@ export type PluginSettings = { writeMappedTagsToDefault: boolean; // Whether mapped tags should also write to the default tag enableExternalStatusSync: boolean; // Whether to sync statuses to an external file externalStatusSyncPath: string; // Path to the external sync file + syncGroups: SyncGroup[]; // Selected groups of settings to synchronize [key: string]: unknown; };