mirror of
https://github.com/misskey-dev/misskey.git
synced 2026-06-25 17:10:43 +00:00
Merge commit from fork
* fix(frontend): avoid recursive reference on theme variables * fix(theme): filter compiled theme properties to include only valid themeProps
This commit is contained in:
parent
04f18fe919
commit
602a46cb78
1 changed files with 53 additions and 28 deletions
|
|
@ -29,6 +29,8 @@ export type Theme = {
|
|||
|
||||
export type CompiledTheme = Record<string, string>;
|
||||
|
||||
const MAX_THEME_REFERENCE_DEPTH = 4;
|
||||
|
||||
export const themeProps = Object.keys(lightTheme.props).filter(key => !key.startsWith('X'));
|
||||
|
||||
export const getBuiltinThemes = () => Promise.all(
|
||||
|
|
@ -56,45 +58,68 @@ export const getBuiltinThemes = () => Promise.all(
|
|||
].map(name => import(`@@/themes/${name}.json5`).then(({ default: _default }): Theme => _default)),
|
||||
);
|
||||
|
||||
export function compile(theme: Theme): CompiledTheme {
|
||||
function getColor(val: string): tinycolor.Instance {
|
||||
if (val[0] === '@') { // ref (prop)
|
||||
return getColor(theme.props[val.substring(1)]);
|
||||
} else if (val[0] === '$') { // ref (const)
|
||||
return getColor(theme.props[val]);
|
||||
} else if (val[0] === ':') { // func
|
||||
const parts = val.split('<');
|
||||
const funcTxt = parts.shift();
|
||||
const argTxt = parts.shift();
|
||||
|
||||
if (funcTxt && argTxt) {
|
||||
const func = funcTxt.substring(1);
|
||||
const arg = parseFloat(argTxt);
|
||||
const color = getColor(parts.join('<'));
|
||||
|
||||
switch (func) {
|
||||
case 'darken': return color.darken(arg);
|
||||
case 'lighten': return color.lighten(arg);
|
||||
case 'alpha': return color.setAlpha(arg);
|
||||
case 'hue': return color.spin(arg);
|
||||
case 'saturate': return color.saturate(arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// other case
|
||||
return tinycolor(val);
|
||||
function getThemeReferenceColor(theme: Theme, key: string, stack: string[], depth: number): tinycolor.Instance {
|
||||
if (depth >= MAX_THEME_REFERENCE_DEPTH) {
|
||||
throw new Error('Theme reference limit exceeded');
|
||||
}
|
||||
|
||||
if (stack.includes(key)) {
|
||||
throw new Error('Theme contains circular references');
|
||||
}
|
||||
|
||||
const nextValue = theme.props[key];
|
||||
if (typeof nextValue !== 'string') {
|
||||
throw new Error(`Theme references missing property: ${key}`);
|
||||
}
|
||||
|
||||
return getColor(theme, nextValue, [...stack, key], depth + 1);
|
||||
}
|
||||
|
||||
function getColor(theme: Theme, val: string, stack: string[] = [], depth = 0): tinycolor.Instance {
|
||||
if (depth >= MAX_THEME_REFERENCE_DEPTH) {
|
||||
throw new Error('Theme reference limit exceeded');
|
||||
}
|
||||
|
||||
if (val[0] === '@') { // ref (prop)
|
||||
return getThemeReferenceColor(theme, val.substring(1), stack, depth);
|
||||
} else if (val[0] === '$') { // ref (const)
|
||||
return getThemeReferenceColor(theme, val, stack, depth);
|
||||
} else if (val[0] === ':') { // func
|
||||
const parts = val.split('<');
|
||||
const funcTxt = parts.shift();
|
||||
const argTxt = parts.shift();
|
||||
|
||||
if (funcTxt && argTxt) {
|
||||
const func = funcTxt.substring(1);
|
||||
const arg = parseFloat(argTxt);
|
||||
const color = getColor(theme, parts.join('<'), stack, depth + 1);
|
||||
|
||||
switch (func) {
|
||||
case 'darken': return color.darken(arg);
|
||||
case 'lighten': return color.lighten(arg);
|
||||
case 'alpha': return color.setAlpha(arg);
|
||||
case 'hue': return color.spin(arg);
|
||||
case 'saturate': return color.saturate(arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// other case
|
||||
return tinycolor(val);
|
||||
}
|
||||
|
||||
export function compile(theme: Theme): CompiledTheme {
|
||||
const props = {} as CompiledTheme;
|
||||
|
||||
for (const [k, v] of Object.entries(theme.props)) {
|
||||
if (k.startsWith('$')) continue; // ignore const
|
||||
|
||||
props[k] = v.startsWith('"') ? v.replace(/^"\s*/, '') : genValue(getColor(v));
|
||||
props[k] = v.startsWith('"') ? v.replace(/^"\s*/, '') : genValue(getColor(theme, v));
|
||||
}
|
||||
|
||||
return props;
|
||||
return Object.fromEntries(
|
||||
Object.entries(props).filter(([key]) => themeProps.includes(key)),
|
||||
) as CompiledTheme;
|
||||
}
|
||||
|
||||
function genValue(c: tinycolor.Instance): string {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue