diff --git a/components/SettingsUI/UISettings.tsx b/components/SettingsUI/UISettings.tsx index 1a2ac0d..d39a76b 100644 --- a/components/SettingsUI/UISettings.tsx +++ b/components/SettingsUI/UISettings.tsx @@ -322,6 +322,43 @@ export const UISettings: React.FC = ({ settings, onChange }) => { /> +

Experimental features

+ + + + + + + + + + + + +

Behavior & Other

{ - await this.openStatusPane(); - }, - }); - this.registeredCommands.add("open-status-pane"); + // Open Status Pane command (experimental) + if (this.shouldEnableGroupedView()) { + this.plugin.addCommand({ + id: "open-status-pane", + name: "Open status pane", + callback: async () => { + await this.openStatusPane(); + }, + }); + this.registeredCommands.add("open-status-pane"); + } } private async openStatusPane(): Promise { @@ -356,6 +359,10 @@ export class CommandsService { this.registeredCommands.clear(); } + private shouldEnableGroupedView(): boolean { + return isExperimentalFeatureEnabled("groupedStatusView"); + } + public destroy(): void { // Remove all registered commands properly this.removeRegisteredCommands(); diff --git a/integrations/commands/commandsIntegration.ts b/integrations/commands/commandsIntegration.ts index 07d0f0e..2e28690 100644 --- a/integrations/commands/commandsIntegration.ts +++ b/integrations/commands/commandsIntegration.ts @@ -32,7 +32,9 @@ export class CommandsIntegration { if ( key === "quickStatusCommands" || key === "useMultipleStatuses" || - key === "templates" + key === "templates" || + key === "enableExperimentalFeatures" || + key === "enableGroupedStatusView" ) { this.commandsService.destroy(); /// BUG: if removed a command will persist because is not removed, you need the oldStates to send it to be disabled // const oldValue = this.settings[key]; // TODO: Send the old value diff --git a/main.tsx b/main.tsx index 81aa831..2ea5ed9 100644 --- a/main.tsx +++ b/main.tsx @@ -19,6 +19,7 @@ import { } from "./integrations/views/status-dashboard-view"; import { StatusesInfoPopup } from "./integrations/popups/statusesInfoPopupIntegration"; import statusStoreManager from "./core/statusStoreManager"; +import { isExperimentalFeatureEnabled } from "@/utils/experimentalFeatures"; export default class NoteStatusPlugin extends Plugin { private statusBarIntegration: StatusBarIntegration; @@ -27,6 +28,7 @@ export default class NoteStatusPlugin extends Plugin { private fileExplorerIntegration: FileExplorerIntegration; private commandsIntegration: CommandsIntegration; private editorToolbarIntegration: EditorToolbarIntegration; + private experimentalRibbonShortcuts: Map = new Map(); async onload() { BaseNoteStatusService.initialize(this.app); @@ -53,13 +55,7 @@ export default class NoteStatusPlugin extends Plugin { (leaf) => new StatusDashboardView(leaf), ); - this.addRibbonIcon("list-tree", "Open grouped status view", () => { - this.activateView(); - }); - - this.addRibbonIcon("activity", "Open status dashboard", () => { - this.activateDashboard(); - }); + this.syncExperimentalFeatureShortcuts(); } async onunload() { @@ -70,6 +66,8 @@ export default class NoteStatusPlugin extends Plugin { this.commandsIntegration?.destroy(); this.editorToolbarIntegration?.destroy(); this.pluginSettingsIntegration?.destroy(); + this.experimentalRibbonShortcuts.forEach((el) => el.remove()); + this.experimentalRibbonShortcuts.clear(); // Clean up event subscriptions eventBus.unsubscribe( @@ -148,6 +146,58 @@ export default class NoteStatusPlugin extends Plugin { } } + private syncExperimentalFeatureShortcuts(): void { + const shortcuts = this.getExperimentalShortcuts(); + Object.entries(shortcuts).forEach(([key, config]) => { + this.toggleExperimentalRibbon(key, config); + }); + } + + private getExperimentalShortcuts() { + return { + groupedStatusView: { + feature: "groupedStatusView" as const, + icon: "list-tree", + title: "Open grouped status view", + handler: () => this.activateView(), + }, + statusDashboard: { + feature: "statusDashboard" as const, + icon: "activity", + title: "Open status dashboard", + handler: () => this.activateDashboard(), + }, + }; + } + + private toggleExperimentalRibbon( + key: string, + config: { + feature: Parameters[0]; + icon: string; + title: string; + handler: () => void; + }, + ): void { + const isEnabled = isExperimentalFeatureEnabled(config.feature); + const existingIcon = this.experimentalRibbonShortcuts.get(key); + + if (isEnabled && !existingIcon) { + const ribbonEl = this.addRibbonIcon( + config.icon, + config.title, + config.handler, + ); + this.experimentalRibbonShortcuts.set(key, ribbonEl); + return; + } + + if (!isEnabled && existingIcon) { + existingIcon.remove(); + this.experimentalRibbonShortcuts.delete(key); + } + } + private async loadEventBus() { // Propagate to custom event bus the new active file this.app.workspace.on( @@ -179,6 +229,13 @@ export default class NoteStatusPlugin extends Plugin { if (key === "enableStatusOverviewPopup" && value === false) { StatusesInfoPopup.close(); } + if ( + key === "enableExperimentalFeatures" || + key === "enableStatusDashboard" || + key === "enableGroupedStatusView" + ) { + this.syncExperimentalFeatureShortcuts(); + } }, "main-status-popup-setting-subscriptor", ); diff --git a/types/pluginSettings.ts b/types/pluginSettings.ts index c8e9a52..90af6f0 100644 --- a/types/pluginSettings.ts +++ b/types/pluginSettings.ts @@ -28,6 +28,9 @@ export type PluginSettings = { fileExplorerLeftBorder: boolean; // Whether to display a colored border on the explorer item fileExplorerStatusDot: boolean; // Whether to append a small colored dot next to the filename fileExplorerUnderlineFileName: boolean; // Whether to underline the filename using the status color + enableExperimentalFeatures: boolean; // Gate for experimental features + enableStatusDashboard: boolean; // Toggle for the status dashboard experiment + enableGroupedStatusView: boolean; // Toggle for the grouped status view experiment enabledTemplates: string[]; // IDs of enabled templates useCustomStatusesOnly: boolean; // Whether to use only custom statuses or include templates useMultipleStatuses: boolean; // Whether to allow multiple statuses per note diff --git a/utils/experimentalFeatures.ts b/utils/experimentalFeatures.ts new file mode 100644 index 0000000..98e04a7 --- /dev/null +++ b/utils/experimentalFeatures.ts @@ -0,0 +1,23 @@ +import settingsService from "@/core/settingsService"; + +export type ExperimentalFeature = "statusDashboard" | "groupedStatusView"; + +const FEATURE_SETTING_MAP: Record< + ExperimentalFeature, + keyof typeof settingsService.settings +> = { + statusDashboard: "enableStatusDashboard", + groupedStatusView: "enableGroupedStatusView", +}; + +export function isExperimentalFeatureEnabled( + feature: ExperimentalFeature, +): boolean { + const settings = settingsService.settings; + if (!settings.enableExperimentalFeatures) { + return false; + } + + const flagKey = FEATURE_SETTING_MAP[feature]; + return Boolean(settings[flagKey]); +}