Merge pull request #66 from devonthesofa/fix/file-explorer-icon-colors

[Fix/] file explorer icon colors
This commit is contained in:
Aleix Soler 2025-11-17 20:09:00 +01:00 committed by GitHub
commit abd68d00e6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 107 additions and 1434 deletions

View file

@ -10,6 +10,8 @@ type Props = {
icon: string;
color: string;
};
iconFrameMode?: "always" | "never";
iconColorMode?: "status" | "theme";
};
export const FileExplorerIcon: FC<Props> = memo(
@ -19,6 +21,8 @@ export const FileExplorerIcon: FC<Props> = memo(
onMouseEnter,
hideUnknownStatus,
unknownStatusConfig,
iconFrameMode = "never",
iconColorMode = "status",
}) => {
const statusEntries = Object.entries(statuses);
const totalStatuses = statusEntries.reduce(
@ -26,13 +30,32 @@ export const FileExplorerIcon: FC<Props> = memo(
0,
);
const useStatusColors = iconColorMode === "status";
const getStatusColor = (color?: string) =>
(color && color.trim()) || "var(--text-accent)";
if (totalStatuses === 0) {
// If hideUnknownStatus is enabled, don't show anything for files without status
if (hideUnknownStatus) return null;
// Use config passed from integration, with fallbacks
const icon = unknownStatusConfig?.icon || "❓";
const color = unknownStatusConfig?.color || "#8b949e";
const color = useStatusColors
? unknownStatusConfig?.color?.trim() || "#8b949e"
: undefined;
const shouldFrameUnknown = iconFrameMode === "always";
const unknownStyles: React.CSSProperties = {};
if (color) {
unknownStyles.color = color;
}
if (shouldFrameUnknown) {
const frameColor = color || "currentColor";
unknownStyles.boxShadow = `0 0 0 1px ${frameColor}`;
unknownStyles.borderRadius = "var(--radius-s)";
}
return (
<div className="status-wrapper">
@ -40,13 +63,9 @@ export const FileExplorerIcon: FC<Props> = memo(
className="status-minimal status-minimal--no-status"
onMouseEnter={() => onMouseEnter({})}
onMouseLeave={() => onMouseLeave({})}
style={
{
"--primary-color": color,
} as React.CSSProperties
}
style={unknownStyles}
>
<span className="status-icon">{icon}</span>
<span className="status-minimal__icon">{icon}</span>
</div>
</div>
);
@ -54,6 +73,19 @@ export const FileExplorerIcon: FC<Props> = memo(
const primaryStatus = statusEntries[0]?.[1]?.[0];
if (!primaryStatus) return null;
const iconColor = useStatusColors
? getStatusColor(primaryStatus.color)
: undefined;
const shouldShowFrame = iconFrameMode === "always";
const iconStyles: React.CSSProperties = {};
if (iconColor) {
iconStyles.color = iconColor;
}
if (shouldShowFrame) {
const frameColor = iconColor || "currentColor";
iconStyles.boxShadow = `0 0 0 1px ${frameColor}`;
iconStyles.borderRadius = "var(--radius-s)";
}
return (
<div className="status-wrapper">
@ -61,16 +93,22 @@ export const FileExplorerIcon: FC<Props> = memo(
className="status-minimal"
onMouseEnter={() => onMouseEnter(statuses)}
onMouseLeave={() => onMouseLeave(statuses)}
style={
{
"--primary-color":
primaryStatus.color || "var(--text-accent)",
} as React.CSSProperties
}
style={iconStyles}
>
<span className="status-icon">{primaryStatus.icon}</span>
<span className="status-minimal__icon">
{primaryStatus.icon}
</span>
{totalStatuses > 1 && (
<span className="status-count">{totalStatuses}</span>
<span
className="status-minimal__count"
style={
iconColor
? { backgroundColor: iconColor }
: undefined
}
>
{totalStatuses}
</span>
)}
</div>
</div>

View file

@ -224,6 +224,46 @@ export const UISettings: React.FC<Props> = ({ settings, onChange }) => {
/>
</SettingItem>
<SettingItem
name="Status icon frame"
description="Choose whether to display a frame around the status icon inside the file explorer."
>
<Select
options={[
{ value: "never", display: "Never show a frame" },
{ value: "always", display: "Always show a frame" },
]}
defaultValue={settings.fileExplorerIconFrame || "never"}
onChange={(value) =>
onChange("fileExplorerIconFrame", value)
}
/>
</SettingItem>
<SettingItem
name="Status icon color"
description="Decide whether icons use their configured status colors or inherit the theme default."
>
<Select
options={[
{
value: "status",
display: "Use custom status colors",
},
{
value: "theme",
display: "Use theme default colors",
},
]}
defaultValue={
settings.fileExplorerIconColorMode || "status"
}
onChange={(value) =>
onChange("fileExplorerIconColorMode", value)
}
/>
</SettingItem>
<h3>Behavior & Other</h3>
<SettingItem

View file

@ -6,6 +6,8 @@ import {
export const DEFAULT_PLUGIN_SETTINGS: PluginSettings = {
fileExplorerIconPosition: "absolute-right",
fileExplorerIconFrame: "never",
fileExplorerIconColorMode: "status",
statusColors: {
active: "var(--text-success)",
onHold: "var(--text-warning)",

View file

@ -56,6 +56,8 @@ export class FileExplorerIntegration implements IElementProcessor {
key === "tagPrefix" ||
key === "strictStatuses" ||
key === "fileExplorerIconPosition" ||
key === "fileExplorerIconFrame" ||
key === "fileExplorerIconColorMode" ||
key === "unknownStatusIcon" ||
key === "unknownStatusColor" ||
key === "templates"
@ -148,6 +150,13 @@ export class FileExplorerIntegration implements IElementProcessor {
settingsService.settings.hideUnknownStatusInExplorer
}
unknownStatusConfig={this.getUnknownStatusConfig()}
iconFrameMode={
settingsService.settings.fileExplorerIconFrame || "never"
}
iconColorMode={
settingsService.settings.fileExplorerIconColorMode ||
"status"
}
/>,
);

1420
styles.css

File diff suppressed because one or more lines are too long

View file

@ -12,6 +12,8 @@ export type PluginSettings = {
| "absolute-right"
| "file-name-left"
| "file-name-right";
fileExplorerIconFrame: "always" | "never";
fileExplorerIconColorMode: "status" | "theme";
statusColors: Record<string, string>;
showStatusBar: boolean;
autoHideStatusBar: boolean;