Keeps embedded sessions aligned with parent theme Prevents iframe reloads on theme changes Supports theme hotkeys from focused iframes
72 lines
2.7 KiB
TypeScript
72 lines
2.7 KiB
TypeScript
import type { Theme } from '@/types/theme';
|
|
|
|
const getNested = (value: unknown, path: string[]): unknown =>
|
|
path.reduce<unknown>((acc, key) => (acc && typeof acc === 'object' ? (acc as Record<string, unknown>)[key] : undefined), value);
|
|
|
|
const isNonEmptyString = (value: unknown): value is string =>
|
|
typeof value === 'string' && value.trim().length > 0;
|
|
|
|
export const isValidTheme = (value: unknown): value is Theme => {
|
|
if (!value || typeof value !== 'object') {
|
|
return false;
|
|
}
|
|
|
|
const requiredPaths = [
|
|
['metadata', 'id'],
|
|
['metadata', 'name'],
|
|
['metadata', 'variant'],
|
|
['colors', 'primary', 'base'],
|
|
['colors', 'primary', 'foreground'],
|
|
['colors', 'surface', 'background'],
|
|
['colors', 'surface', 'foreground'],
|
|
['colors', 'surface', 'muted'],
|
|
['colors', 'surface', 'mutedForeground'],
|
|
['colors', 'surface', 'elevated'],
|
|
['colors', 'surface', 'elevatedForeground'],
|
|
['colors', 'surface', 'subtle'],
|
|
['colors', 'interactive', 'border'],
|
|
['colors', 'interactive', 'selection'],
|
|
['colors', 'interactive', 'selectionForeground'],
|
|
['colors', 'interactive', 'focusRing'],
|
|
['colors', 'interactive', 'hover'],
|
|
['colors', 'status', 'error'],
|
|
['colors', 'status', 'errorForeground'],
|
|
['colors', 'status', 'errorBackground'],
|
|
['colors', 'status', 'errorBorder'],
|
|
['colors', 'status', 'warning'],
|
|
['colors', 'status', 'warningForeground'],
|
|
['colors', 'status', 'warningBackground'],
|
|
['colors', 'status', 'warningBorder'],
|
|
['colors', 'status', 'success'],
|
|
['colors', 'status', 'successForeground'],
|
|
['colors', 'status', 'successBackground'],
|
|
['colors', 'status', 'successBorder'],
|
|
['colors', 'status', 'info'],
|
|
['colors', 'status', 'infoForeground'],
|
|
['colors', 'status', 'infoBackground'],
|
|
['colors', 'status', 'infoBorder'],
|
|
['colors', 'syntax', 'base', 'background'],
|
|
['colors', 'syntax', 'base', 'foreground'],
|
|
['colors', 'syntax', 'base', 'keyword'],
|
|
['colors', 'syntax', 'base', 'string'],
|
|
['colors', 'syntax', 'base', 'number'],
|
|
['colors', 'syntax', 'base', 'function'],
|
|
['colors', 'syntax', 'base', 'variable'],
|
|
['colors', 'syntax', 'base', 'type'],
|
|
['colors', 'syntax', 'base', 'comment'],
|
|
['colors', 'syntax', 'base', 'operator'],
|
|
['colors', 'syntax', 'highlights', 'diffAdded'],
|
|
['colors', 'syntax', 'highlights', 'diffRemoved'],
|
|
['colors', 'syntax', 'highlights', 'lineNumber'],
|
|
];
|
|
|
|
for (const path of requiredPaths) {
|
|
if (!isNonEmptyString(getNested(value, path))) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
const variant = getNested(value, ['metadata', 'variant']);
|
|
return variant === 'light' || variant === 'dark';
|
|
};
|