From feaf3fc4486d3d361f8f896914ea44556aab7dbc Mon Sep 17 00:00:00 2001 From: Aleix Soler Date: Sun, 20 Jul 2025 20:02:43 +0200 Subject: [PATCH] feat: add settings to customize the status bar when unknown status; custom unknown icon --- components/FileExplorer/FileExplorerIcon.tsx | 21 +++++- components/SettingsUI.tsx/UISettings.tsx | 73 +++++++++++++++++++ components/StatusBar/StatusBar.tsx | 32 +++++++- constants/defaultSettings.ts | 6 ++ .../file-explorer-integration.tsx | 13 +++- integrations/status-bar/status-bar.tsx | 21 ++++++ types/pluginSettings.ts | 6 ++ 7 files changed, 165 insertions(+), 7 deletions(-) diff --git a/components/FileExplorer/FileExplorerIcon.tsx b/components/FileExplorer/FileExplorerIcon.tsx index d1448f7..b88e0e8 100644 --- a/components/FileExplorer/FileExplorerIcon.tsx +++ b/components/FileExplorer/FileExplorerIcon.tsx @@ -6,10 +6,20 @@ type Props = { onMouseEnter: (statuses: GroupedStatuses) => void; onMouseLeave: (statuses: GroupedStatuses) => void; hideUnknownStatus?: boolean; + unknownStatusConfig?: { + icon: string; + color: string; + }; }; export const FileExplorerIcon: FC = memo( - ({ statuses, onMouseLeave, onMouseEnter, hideUnknownStatus }) => { + ({ + statuses, + onMouseLeave, + onMouseEnter, + hideUnknownStatus, + unknownStatusConfig, + }) => { const statusEntries = Object.entries(statuses); const totalStatuses = statusEntries.reduce( (acc, [, list]) => acc + list.length, @@ -20,7 +30,10 @@ export const FileExplorerIcon: FC = memo( // If hideUnknownStatus is enabled, don't show anything for files without status if (hideUnknownStatus) return null; - // Show "no status" icon + // Use config passed from integration, with fallbacks + const icon = unknownStatusConfig?.icon || "❓"; + const color = unknownStatusConfig?.color || "#8b949e"; + return (
= memo( onMouseLeave={() => onMouseLeave({})} style={ { - "--primary-color": "var(--text-muted)", + "--primary-color": color, } as React.CSSProperties } > - + {icon}
); diff --git a/components/SettingsUI.tsx/UISettings.tsx b/components/SettingsUI.tsx/UISettings.tsx index d4b370b..ce6e7cf 100644 --- a/components/SettingsUI.tsx/UISettings.tsx +++ b/components/SettingsUI.tsx/UISettings.tsx @@ -15,6 +15,18 @@ export const UISettings: React.FC = ({ settings, onChange }) => { onChange(key, e.target.checked); }; + const handleInputChange = + (key: keyof PluginSettings) => + (e: React.ChangeEvent) => { + onChange(key, e.target.value); + }; + + const handleTextInputChange = + (key: keyof PluginSettings) => + (e: React.ChangeEvent) => { + onChange(key, e.target.value); + }; + return (

User interface

@@ -90,6 +102,67 @@ export const UISettings: React.FC = ({ settings, onChange }) => { onChange={handleChange("excludeUnknownStatus")} /> + +

Unknown Status Customization

+ + + + + + + + + + + + + + + + + + + +
); }; diff --git a/components/StatusBar/StatusBar.tsx b/components/StatusBar/StatusBar.tsx index 45ff688..de8cb87 100644 --- a/components/StatusBar/StatusBar.tsx +++ b/components/StatusBar/StatusBar.tsx @@ -11,25 +11,53 @@ export type Props = { templates?: { [key: string]: { description: string } }; hideIfNotStatuses?: boolean; onStatusClick: StatusBarContextProps["onStatusClick"]; + noStatusConfig?: { + text: string; + showIcon: boolean; + showText: boolean; + icon: string; + color: string; + }; }; export const StatusBar: FC = ({ statuses, hideIfNotStatuses, onStatusClick, + noStatusConfig, }) => { const statusEntries = Object.entries(statuses); const hasStatuses = statusEntries.flatMap((s) => s[1]).length > 0; if (!hasStatuses) { if (hideIfNotStatuses) return null; + + if ( + !noStatusConfig || + (!noStatusConfig.showIcon && !noStatusConfig.showText) + ) { + return null; + } + return ( onStatusClick({ name: "", icon: "" })} - style={{ cursor: "pointer" }} + style={{ + cursor: "pointer", + color: noStatusConfig.color, + }} > - No status + {noStatusConfig.showIcon && ( + + {noStatusConfig.icon} + + )} + {noStatusConfig.showText && noStatusConfig.text} ); } diff --git a/constants/defaultSettings.ts b/constants/defaultSettings.ts index a0e81e5..2dbdbb4 100644 --- a/constants/defaultSettings.ts +++ b/constants/defaultSettings.ts @@ -22,4 +22,10 @@ export const DEFAULT_PLUGIN_SETTINGS: PluginSettings = { strictStatuses: false, // Default to show all statuses from frontmatter excludeUnknownStatus: true, // Default to exclude unknown status files for better performance quickStatusCommands: ["active", "completed"], // Add default quick commands + // Unknown status customization + unknownStatusIcon: "❓", + unknownStatusColor: "#8b949e", + statusBarNoStatusText: "No status", + statusBarShowNoStatusIcon: false, + statusBarShowNoStatusText: true, }; diff --git a/integrations/file-explorer/file-explorer-integration.tsx b/integrations/file-explorer/file-explorer-integration.tsx index f06de9f..debbb7c 100644 --- a/integrations/file-explorer/file-explorer-integration.tsx +++ b/integrations/file-explorer/file-explorer-integration.tsx @@ -55,7 +55,9 @@ export class FileExplorerIntegration implements IElementProcessor { key === "useMultipleStatuses" || key === "tagPrefix" || key === "strictStatuses" || - key === "fileExplorerIconPosition" + key === "fileExplorerIconPosition" || + key === "unknownStatusIcon" || + key === "unknownStatusColor" ) { this.destroy(); this.integrate().catch((r) => console.error(r)); @@ -108,6 +110,14 @@ export class FileExplorerIntegration implements IElementProcessor { } } + private getUnknownStatusConfig() { + const settings = settingsService.settings; + return { + icon: settings.unknownStatusIcon || "❓", + color: settings.unknownStatusColor || "#8b949e", + }; + } + render(element: Element, statuses: GroupedStatuses): void { // Remove existing icon const existingIcon = element.querySelector(`.${this.ICON_CLASS}`); @@ -136,6 +146,7 @@ export class FileExplorerIntegration implements IElementProcessor { hideUnknownStatus={ settingsService.settings.hideUnknownStatusInExplorer } + unknownStatusConfig={this.getUnknownStatusConfig()} />, ); diff --git a/integrations/status-bar/status-bar.tsx b/integrations/status-bar/status-bar.tsx index 0cf7a80..dfc9436 100644 --- a/integrations/status-bar/status-bar.tsx +++ b/integrations/status-bar/status-bar.tsx @@ -61,6 +61,15 @@ export class StatusBarIntegration { if (key === "customStatuses") { this.handleActiveFileChange().catch(console.error); // INFO: Force a re-read of the statuses and render } + if ( + key === "unknownStatusIcon" || + key === "unknownStatusColor" || + key === "statusBarNoStatusText" || + key === "statusBarShowNoStatusIcon" || + key === "statusBarShowNoStatusText" + ) { + this.render(); // INFO: Force a render for unknown status customization + } }, "statusBarIntegrationSubscription2", ); @@ -108,6 +117,17 @@ export class StatusBarIntegration { }); } + private getNoStatusConfig() { + const settings = settingsService.settings; + return { + text: settings.statusBarNoStatusText || "No status", + showIcon: settings.statusBarShowNoStatusIcon || false, + showText: settings.statusBarShowNoStatusText ?? true, + icon: settings.unknownStatusIcon || "❓", + color: settings.unknownStatusColor || "#8b949e", + }; + } + private render() { if (!this.root) { this.root = createRoot(this.statusBarContainer); @@ -129,6 +149,7 @@ export class StatusBarIntegration { settingsService.settings.autoHideStatusBar } onStatusClick={() => this.openStatusModal()} + noStatusConfig={this.getNoStatusConfig()} />, ); } diff --git a/types/pluginSettings.ts b/types/pluginSettings.ts index 706de04..b338ffc 100644 --- a/types/pluginSettings.ts +++ b/types/pluginSettings.ts @@ -25,5 +25,11 @@ export type PluginSettings = { strictStatuses: boolean; // Whether to only show known statuses excludeUnknownStatus: boolean; // Whether to exclude files with unknown status from the status pane quickStatusCommands: string[]; + // Unknown status customization + unknownStatusIcon: string; // Custom icon for unknown status + unknownStatusColor: string; // Custom hex color for unknown status + statusBarNoStatusText: string; // Custom text for status bar when no status + statusBarShowNoStatusIcon: boolean; // Whether to show icon in status bar for no status + statusBarShowNoStatusText: boolean; // Whether to show text in status bar for no status [key: string]: unknown; };