Files
openchamber/packages/ui/src/contexts/theme-validation.ts
T
Bohdan Triapitsyn 0fef61e25f fix: sync context panel iframe themes
Keeps embedded sessions aligned with parent theme
Prevents iframe reloads on theme changes
Supports theme hotkeys from focused iframes
2026-06-16 23:33:29 +03:00

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';
};