Gracefully handle unknown widgets in the settings.json in both the TUI and statusline rendering

This commit is contained in:
Matthew Breedlove committed 2025-08-27 01:03:09 -04:00
1 parent 077dc0f5eb
commit 9767fc6399
5 files changed
+52 -65

No files matched your search

+11 -11
View File
@@ -43,13 +43,9 @@ export const ColorMenu: React.FC<ColorMenuProps> = ({ widgets, lineIndex, settin
return showSeparators;
}
// Use the widget's supportsColors method
try {
const widgetInstance = getWidget(widget.type);
return widgetInstance.supportsColors(widget);
} catch {
// If widget type is not found, exclude it
return false;
}
const widgetInstance = getWidget(widget.type);
// Include unknown widgets (they might support colors, we just don't know)
return widgetInstance ? widgetInstance.supportsColors(widget) : true;
});
const [highlightedItemId, setHighlightedItemId] = useState<string | null>(colorableWidgets[0]?.id ?? null);
const [editingBackground, setEditingBackground] = useState(false);
@@ -259,7 +255,9 @@ export const ColorMenu: React.FC<ColorMenuProps> = ({ widgets, lineIndex, settin
let defaultColor = 'white';
if (widget.type !== 'separator' && widget.type !== 'flex-separator') {
const widgetImpl = getWidget(widget.type);
defaultColor = widgetImpl.getDefaultColor();
if (widgetImpl) {
defaultColor = widgetImpl.getDefaultColor();
}
}
let currentColor = widget.color ?? defaultColor;
// If color is 'dim', treat as if no color was set
@@ -313,7 +311,7 @@ export const ColorMenu: React.FC<ColorMenuProps> = ({ widgets, lineIndex, settin
}
const widgetImpl = getWidget(widget.type);
return widgetImpl.getDisplayName();
return widgetImpl ? widgetImpl.getDisplayName() : `Unknown: ${widget.type}`;
};
// Color list for cycling
@@ -333,7 +331,9 @@ export const ColorMenu: React.FC<ColorMenuProps> = ({ widgets, lineIndex, settin
let defaultColor = 'white';
if (widget.type !== 'separator' && widget.type !== 'flex-separator') {
const widgetImpl = getWidget(widget.type);
defaultColor = widgetImpl.getDefaultColor();
if (widgetImpl) {
defaultColor = widgetImpl.getDefaultColor();
}
}
const styledLabel = applyColors(label, widget.color ?? defaultColor, widget.backgroundColor, widget.bold, level);
return {
@@ -363,7 +363,7 @@ export const ColorMenu: React.FC<ColorMenuProps> = ({ widgets, lineIndex, settin
: (selectedWidget ? (selectedWidget.color ?? (() => {
if (selectedWidget.type !== 'separator' && selectedWidget.type !== 'flex-separator') {
const widgetImpl = getWidget(selectedWidget.type);
return widgetImpl.getDefaultColor();
return widgetImpl ? widgetImpl.getDefaultColor() : 'white';
}
return 'white';
})()) : 'white');
+14 -17
View File
@@ -263,8 +263,8 @@ export const ItemsEditor: React.FC<ItemsEditorProps> = ({ widgets, onUpdate, onB
// Check for custom widget keybinds
const currentWidget = widgets[selectedIndex];
if (currentWidget && currentWidget.type !== 'separator' && currentWidget.type !== 'flex-separator') {
try {
const widgetImpl = getWidget(currentWidget.type);
const widgetImpl = getWidget(currentWidget.type);
if (widgetImpl) {
if (widgetImpl.getCustomKeybinds) {
const customKeybinds = widgetImpl.getCustomKeybinds();
const matchedKeybind = customKeybinds.find(kb => kb.key === input);
@@ -288,8 +288,6 @@ export const ItemsEditor: React.FC<ItemsEditorProps> = ({ widgets, onUpdate, onB
}
}
}
} catch {
// Widget not found or doesn't support custom keybinds
}
}
}
@@ -309,10 +307,13 @@ export const ItemsEditor: React.FC<ItemsEditorProps> = ({ widgets, onUpdate, onB
// Handle regular widgets - delegate to widget for display
const widgetImpl = getWidget(widget.type);
const { displayText, modifierText } = widgetImpl.getEditorDisplay(widget);
// Return plain text without colors
return displayText + (modifierText ? ` ${modifierText}` : '');
if (widgetImpl) {
const { displayText, modifierText } = widgetImpl.getEditorDisplay(widget);
// Return plain text without colors
return displayText + (modifierText ? ` ${modifierText}` : '');
}
// Unknown widget type
return `Unknown: ${widget.type}`;
};
const hasFlexSeparator = widgets.some(widget => widget.type === 'flex-separator');
@@ -327,14 +328,14 @@ export const ItemsEditor: React.FC<ItemsEditorProps> = ({ widgets, onUpdate, onB
let canToggleRaw = false;
let customKeybinds: { key: string; label: string; action: string }[] = [];
if (currentWidget && !isSeparator && !isFlexSeparator) {
try {
const widgetImpl = getWidget(currentWidget.type);
const widgetImpl = getWidget(currentWidget.type);
if (widgetImpl) {
canToggleRaw = widgetImpl.supportsRawValue();
// Get custom keybinds from the widget
if (widgetImpl.getCustomKeybinds) {
customKeybinds = widgetImpl.getCustomKeybinds();
}
} catch {
} else {
canToggleRaw = false;
}
}
@@ -448,12 +449,8 @@ export const ItemsEditor: React.FC<ItemsEditorProps> = ({ widgets, onUpdate, onB
} else if (currentWidget.type === 'flex-separator') {
return 'Expands to fill available terminal width';
} else {
try {
const widgetImpl = getWidget(currentWidget.type);
return widgetImpl.getDescription();
} catch {
return 'Widget description not available';
}
const widgetImpl = getWidget(currentWidget.type);
return widgetImpl ? widgetImpl.getDescription() : 'Unknown widget type';
}
})()}
</Text>
+12 -4
View File
@@ -60,7 +60,9 @@ export const TerminalOptionsMenu: React.FC<TerminalOptionsMenuProps> = ({ settin
if (widget.color?.startsWith('hex:')) {
if (widget.type !== 'separator' && widget.type !== 'flex-separator') {
const widgetImpl = getWidget(widget.type);
newWidget.color = widgetImpl.getDefaultColor();
if (widgetImpl) {
newWidget.color = widgetImpl.getDefaultColor();
}
}
}
if (widget.backgroundColor?.startsWith('hex:')) {
@@ -71,7 +73,9 @@ export const TerminalOptionsMenu: React.FC<TerminalOptionsMenuProps> = ({ settin
if (widget.color?.startsWith('ansi256:')) {
if (widget.type !== 'separator' && widget.type !== 'flex-separator') {
const widgetImpl = getWidget(widget.type);
newWidget.color = widgetImpl.getDefaultColor();
if (widgetImpl) {
newWidget.color = widgetImpl.getDefaultColor();
}
}
}
if (widget.backgroundColor?.startsWith('ansi256:')) {
@@ -82,7 +86,9 @@ export const TerminalOptionsMenu: React.FC<TerminalOptionsMenuProps> = ({ settin
if (widget.color?.startsWith('ansi256:') || widget.color?.startsWith('hex:')) {
if (widget.type !== 'separator' && widget.type !== 'flex-separator') {
const widgetImpl = getWidget(widget.type);
newWidget.color = widgetImpl.getDefaultColor();
if (widgetImpl) {
newWidget.color = widgetImpl.getDefaultColor();
}
}
}
if (widget.backgroundColor?.startsWith('ansi256:') || widget.backgroundColor?.startsWith('hex:')) {
@@ -118,7 +124,9 @@ export const TerminalOptionsMenu: React.FC<TerminalOptionsMenuProps> = ({ settin
if (widget.color?.startsWith('ansi256:') || widget.color?.startsWith('hex:')) {
if (widget.type !== 'separator' && widget.type !== 'flex-separator') {
const widgetImpl = getWidget(widget.type);
newWidget.color = widgetImpl.getDefaultColor();
if (widgetImpl) {
newWidget.color = widgetImpl.getDefaultColor();
}
}
}
if (widget.backgroundColor?.startsWith('ansi256:') || widget.backgroundColor?.startsWith('hex:')) {
+12 -26
View File
@@ -134,11 +134,9 @@ function renderPowerlineStatusLine(
if (preRendered?.content) {
widgetText = preRendered.content;
// Get default color from widget impl for consistency
try {
const widgetImpl = getWidget(widget.type);
const widgetImpl = getWidget(widget.type);
if (widgetImpl) {
defaultColor = widgetImpl.getDefaultColor();
} catch {
// Keep default
}
}
@@ -481,20 +479,14 @@ export function preRenderAllWidgets(
continue;
}
let widgetText = '';
try {
const widgetImpl = getWidget(widget.type);
widgetText = widgetImpl.render(widget, context, settings) ?? '';
} catch {
// Unknown widget type - store empty content
preRenderedLine.push({
content: '',
plainLength: 0,
widget
});
const widgetImpl = getWidget(widget.type);
if (!widgetImpl) {
// Unknown widget type - skip it entirely
continue;
}
const widgetText = widgetImpl.render(widget, context, settings) ?? '';
// Store the rendered content without padding (padding is applied later)
const plainLength = widgetText.replace(ANSI_REGEX, '').length;
preRenderedLine.push({
@@ -664,12 +656,8 @@ export function renderStatusLine(
// Get the previous widget's colors
let widgetColor = prevWidget.color;
if (!widgetColor) {
try {
const widgetImpl = getWidget(prevWidget.type);
widgetColor = widgetImpl.getDefaultColor();
} catch {
widgetColor = 'white';
}
const widgetImpl = getWidget(prevWidget.type);
widgetColor = widgetImpl ? widgetImpl.getDefaultColor() : 'white';
}
separatorColor = widgetColor;
separatorBg = prevWidget.backgroundColor;
@@ -697,11 +685,9 @@ export function renderStatusLine(
if (preRendered?.content) {
widgetText = preRendered.content;
// Get default color from widget impl for consistency
try {
const widgetImpl = getWidget(widget.type);
const widgetImpl = getWidget(widget.type);
if (widgetImpl) {
defaultColor = widgetImpl.getDefaultColor();
} catch {
// Keep default
}
}
@@ -790,7 +776,7 @@ export function renderStatusLine(
let widgetColor = prevElem.widget.color;
if (!widgetColor && prevElem.widget.type !== 'separator' && prevElem.widget.type !== 'flex-separator') {
const widgetImpl = getWidget(prevElem.widget.type);
widgetColor = widgetImpl.getDefaultColor();
widgetColor = widgetImpl ? widgetImpl.getDefaultColor() : 'white';
}
const coloredSep = applyColorsWithOverride(defaultSep, widgetColor, prevElem.widget.backgroundColor, prevElem.widget.bold);
finalElements.push(coloredSep);
+3 -7
View File
@@ -28,12 +28,8 @@ const widgetRegistry = new Map<WidgetItemType, Widget>([
['custom-command', new widgets.CustomCommandWidget()]
]);
export function getWidget(type: WidgetItemType): Widget {
const widget = widgetRegistry.get(type);
if (!widget) {
throw new Error(`Unknown widget type: ${type}`);
}
return widget;
export function getWidget(type: WidgetItemType): Widget | null {
return widgetRegistry.get(type) ?? null;
}
export function getAllWidgetTypes(settings: Settings): WidgetItemType[] {
@@ -50,7 +46,7 @@ export function getAllWidgetTypes(settings: Settings): WidgetItemType[] {
return allTypes;
}
export function isWidgetType(type: string): boolean {
export function isKnownWidgetType(type: string): boolean {
return widgetRegistry.has(type)
|| type === 'separator'
|| type === 'flex-separator';