commit: dc22386599c77fdd5a8b88ccfd167cff36d14c67
parent ac85cdac68824efcd3bc60619b66d9e1b0f22afe
Author: Henry Jameson <me@hjkos.com>
Date: Mon, 26 Feb 2024 21:37:40 +0200
optimization and refactoring, rules are first flattened and then
processed, letting us to set individual rules as "lazy"
Diffstat:
3 files changed, 243 insertions(+), 250 deletions(-)
diff --git a/src/boot/after_store.js b/src/boot/after_store.js
@@ -327,17 +327,14 @@ const setConfig = async ({ store }) => {
}
const checkOAuthToken = async ({ store }) => {
- // eslint-disable-next-line no-async-promise-executor
- return new Promise(async (resolve, reject) => {
- if (store.getters.getUserToken()) {
- try {
- await store.dispatch('loginUser', store.getters.getUserToken())
- } catch (e) {
- console.error(e)
- }
+ if (store.getters.getUserToken()) {
+ try {
+ await store.dispatch('loginUser', store.getters.getUserToken())
+ } catch (e) {
+ console.error(e)
}
- resolve()
- })
+ }
+ return Promise.resolve()
}
const afterStoreSetup = async ({ store, i18n }) => {
diff --git a/src/services/style_setter/style_setter.js b/src/services/style_setter/style_setter.js
@@ -5,23 +5,16 @@ import { convertTheme2To3 } from '../theme_data/theme2_to_theme3.js'
import { getCssRules } from '../theme_data/css_utils.js'
import { defaultState } from '../../modules/config.js'
-export const applyTheme = (input) => {
+export const applyTheme = async (input) => {
let extraRules
if (input.themeType !== 1) {
- const t0 = performance.now()
const { theme } = generatePreset(input)
- const t1 = performance.now()
- console.debug('Themes 2 initialization took ' + (t1 - t0) + 'ms')
extraRules = convertTheme2To3(theme)
} else {
- console.debug(input)
extraRules = convertTheme2To3(input)
}
- const t1 = performance.now()
const themes3 = init(extraRules, '#FFFFFF')
- const t2 = performance.now()
- console.debug('Themes 3 (eager) initialization took ' + (t2 - t1) + 'ms')
const head = document.head
const body = document.body
body.classList.add('hidden')
@@ -47,14 +40,21 @@ export const applyTheme = (input) => {
styleSheet.insertRule(rule, 'index-max')
}
})
+
body.classList.remove('hidden')
- themes3.lazy.then(lazyRules => {
- getCssRules(lazyRules, themes3.staticVars).forEach(rule => {
- styleSheet.insertRule(rule, 'index-max')
+
+ setTimeout(() => {
+ themes3.lazy().then(lazyRules => {
+ const t2 = performance.now()
+ getCssRules(lazyRules, themes3.staticVars).forEach(rule => {
+ styleSheet.insertRule(rule, 'index-max')
+ })
+ const t3 = performance.now()
+ console.debug('Themes 3 finalization (lazy) took ' + (t3 - t2) + 'ms')
})
- const t3 = performance.now()
- console.debug('Themes 3 finalization (lazy) took ' + (t3 - t2) + 'ms')
})
+
+ return Promise.resolve()
}
const configColumns = ({ sidebarColumnWidth, contentColumnWidth, notifsColumnWidth, emojiReactionsScale }) =>
diff --git a/src/services/theme_data/theme_data_3.service.js b/src/services/theme_data/theme_data_3.service.js
@@ -151,6 +151,7 @@ export const init = (extraRuleset, ultimateBackgroundColor) => {
const eagerRules = []
const lazyRules = []
+ const lazyPromises = []
const rulesetUnsorted = [
...Object.values(components)
@@ -187,25 +188,210 @@ export const init = (extraRuleset, ultimateBackgroundColor) => {
const virtualComponents = new Set(Object.values(components).filter(c => c.virtual).map(c => c.name))
- let counter = 0
- const promises = []
- const processInnerComponent = (component, rules, parent) => {
+ const processCombination = (combination, rules) => {
const addRule = (rule) => {
rules.push(rule)
}
- const parentSelector = ruleToSelector(parent, true)
- // const parentList = parent ? unroll(parent).reverse().map(c => c.component) : []
- // if (!component.virtual) {
- // const path = [...parentList, component.name].join(' > ')
- // console.log('Component ' + path + ' process starting')
- // }
- // const t0 = performance.now()
+ const selector = ruleToSelector(combination, true)
+ const cssSelector = ruleToSelector(combination)
+
+ const parentSelector = selector.split(/ /g).slice(0, -1).join(' ')
+ const soloSelector = selector.split(/ /g).slice(-1)[0]
+
+ const lowerLevelSelector = parentSelector
+ const lowerLevelBackground = computed[lowerLevelSelector]?.background
+ const lowerLevelVirtualDirectives = computed[lowerLevelSelector]?.virtualDirectives
+ const lowerLevelVirtualDirectivesRaw = computed[lowerLevelSelector]?.virtualDirectivesRaw
+
+ const dynamicVars = computed[selector] || {
+ lowerLevelBackground,
+ lowerLevelVirtualDirectives,
+ lowerLevelVirtualDirectivesRaw
+ }
+
+ // Inheriting all of the applicable rules
+ const existingRules = ruleset.filter(findRules(combination))
+ const computedDirectives = existingRules.map(r => r.directives).reduce((acc, directives) => ({ ...acc, ...directives }), {})
+ const computedRule = {
+ ...combination,
+ directives: computedDirectives
+ }
+
+ computed[selector] = computed[selector] || {}
+ computed[selector].computedRule = computedRule
+ computed[selector].dynamicVars = dynamicVars
+
+ if (virtualComponents.has(combination.component)) {
+ const virtualName = [
+ '--',
+ combination.component.toLowerCase(),
+ combination.variant === 'normal'
+ ? ''
+ : combination.variant[0].toUpperCase() + combination.variant.slice(1).toLowerCase(),
+ ...combination.state.filter(x => x !== 'normal').toSorted().map(state => state[0].toUpperCase() + state.slice(1).toLowerCase())
+ ].join('')
+
+ let inheritedTextColor = computedDirectives.textColor
+ let inheritedTextAuto = computedDirectives.textAuto
+ let inheritedTextOpacity = computedDirectives.textOpacity
+ let inheritedTextOpacityMode = computedDirectives.textOpacityMode
+ const lowerLevelTextSelector = [...selector.split(/ /g).slice(0, -1), soloSelector].join(' ')
+ const lowerLevelTextRule = computed[lowerLevelTextSelector]
+
+ if (inheritedTextColor == null || inheritedTextOpacity == null || inheritedTextOpacityMode == null) {
+ inheritedTextColor = computedDirectives.textColor ?? lowerLevelTextRule.textColor
+ inheritedTextAuto = computedDirectives.textAuto ?? lowerLevelTextRule.textAuto
+ inheritedTextOpacity = computedDirectives.textOpacity ?? lowerLevelTextRule.textOpacity
+ inheritedTextOpacityMode = computedDirectives.textOpacityMode ?? lowerLevelTextRule.textOpacityMode
+ }
+
+ const newTextRule = {
+ ...computedRule,
+ directives: {
+ ...computedRule.directives,
+ textColor: inheritedTextColor,
+ textAuto: inheritedTextAuto ?? 'preserve',
+ textOpacity: inheritedTextOpacity,
+ textOpacityMode: inheritedTextOpacityMode
+ }
+ }
+
+ dynamicVars.inheritedBackground = lowerLevelBackground
+ dynamicVars.stacked = convert(stacked[lowerLevelSelector]).rgb
+
+ const intendedTextColor = convert(findColor(inheritedTextColor, { dynamicVars, staticVars })).rgb
+ const textColor = newTextRule.directives.textAuto === 'no-auto'
+ ? intendedTextColor
+ : getTextColor(
+ convert(stacked[lowerLevelSelector]).rgb,
+ intendedTextColor,
+ newTextRule.directives.textAuto === 'preserve'
+ )
+
+ // Updating previously added rule
+ const earlyLowerLevelRules = rules.filter(findRules(combination.parent, true))
+ const earlyLowerLevelRule = earlyLowerLevelRules.slice(-1)[0]
+
+ const virtualDirectives = earlyLowerLevelRule.virtualDirectives || {}
+ const virtualDirectivesRaw = earlyLowerLevelRule.virtualDirectivesRaw || {}
+
+ // Storing color data in lower layer to use as custom css properties
+ virtualDirectives[virtualName] = getTextColorAlpha(newTextRule.directives, textColor, dynamicVars)
+ virtualDirectivesRaw[virtualName] = textColor
+ earlyLowerLevelRule.virtualDirectives = virtualDirectives
+ earlyLowerLevelRule.virtualDirectivesRaw = virtualDirectivesRaw
+ computed[lowerLevelSelector].virtualDirectives = virtualDirectives
+ computed[lowerLevelSelector].virtualDirectivesRaw = virtualDirectivesRaw
+ } else {
+ computed[selector] = computed[selector] || {}
+
+ // TODO: DEFAULT TEXT COLOR
+ const lowerLevelStackedBackground = stacked[lowerLevelSelector] || convert(ultimateBackgroundColor).rgb
+
+ if (computedDirectives.background) {
+ let inheritRule = null
+ const variantRules = ruleset.filter(
+ findRules({
+ component: combination.component,
+ variant: combination.variant,
+ parent: combination.parent
+ })
+ )
+ const lastVariantRule = variantRules[variantRules.length - 1]
+ if (lastVariantRule) {
+ inheritRule = lastVariantRule
+ } else {
+ const normalRules = ruleset.filter(findRules({
+ component: combination.component,
+ parent: combination.parent
+ }))
+ const lastNormalRule = normalRules[normalRules.length - 1]
+ inheritRule = lastNormalRule
+ }
+
+ const inheritSelector = ruleToSelector({ ...inheritRule, parent: combination.parent }, true)
+ const inheritedBackground = computed[inheritSelector].background
+
+ dynamicVars.inheritedBackground = inheritedBackground
+
+ const rgb = convert(findColor(computedDirectives.background, { dynamicVars, staticVars })).rgb
+
+ if (!stacked[selector]) {
+ let blend
+ const alpha = computedDirectives.opacity ?? 1
+ if (alpha >= 1) {
+ blend = rgb
+ } else if (alpha <= 0) {
+ blend = lowerLevelStackedBackground
+ } else {
+ blend = alphaBlend(rgb, computedDirectives.opacity, lowerLevelStackedBackground)
+ }
+ stacked[selector] = blend
+ computed[selector].background = { ...rgb, a: computedDirectives.opacity ?? 1 }
+ }
+ }
+
+ if (computedDirectives.shadow) {
+ dynamicVars.shadow = flattenDeep(findShadow(flattenDeep(computedDirectives.shadow), { dynamicVars, staticVars }))
+ }
+
+ if (!stacked[selector]) {
+ computedDirectives.background = 'transparent'
+ computedDirectives.opacity = 0
+ stacked[selector] = lowerLevelStackedBackground
+ computed[selector].background = { ...lowerLevelStackedBackground, a: 0 }
+ }
+
+ dynamicVars.stacked = stacked[selector]
+ dynamicVars.background = computed[selector].background
+
+ const dynamicSlots = Object.entries(computedDirectives).filter(([k, v]) => k.startsWith('--'))
+
+ dynamicSlots.forEach(([k, v]) => {
+ const [type, ...value] = v.split('|').map(x => x.trim()) // woah, Extreme!
+ switch (type) {
+ case 'color': {
+ const color = findColor(value[0], { dynamicVars, staticVars })
+ dynamicVars[k] = color
+ if (combination.component === 'Root') {
+ staticVars[k.substring(2)] = color
+ }
+ break
+ }
+ case 'shadow': {
+ const shadow = value
+ dynamicVars[k] = shadow
+ if (combination.component === 'Root') {
+ staticVars[k.substring(2)] = shadow
+ }
+ break
+ }
+ case 'generic': {
+ dynamicVars[k] = value
+ if (combination.component === 'Root') {
+ staticVars[k.substring(2)] = value
+ }
+ break
+ }
+ }
+ })
+
+ addRule({
+ dynamicVars,
+ selector: cssSelector,
+ ...combination,
+ directives: computedDirectives
+ })
+ }
+ }
+
+ const processInnerComponent = (component, parent) => {
+ const combinations = []
const {
validInnerComponents = [],
states: originalStates = {},
- variants: originalVariants = {},
- name
+ variants: originalVariants = {}
} = component
// Normalizing states and variants to always include "normal"
@@ -241,233 +427,43 @@ export const init = (extraRuleset, ultimateBackgroundColor) => {
}).reduce((acc, x) => [...acc, ...x], [])
stateVariantCombination.forEach(combination => {
- counter++
- // const tt0 = performance.now()
-
combination.component = component.name
- const soloSelector = ruleToSelector(combination, true)
- const soloCssSelector = ruleToSelector(combination)
- const selector = [parentSelector, soloSelector].filter(x => x).join(' ')
- const cssSelector = [parentSelector, soloCssSelector].filter(x => x).join(' ')
-
- const lowerLevelSelector = parentSelector
- const lowerLevelBackground = computed[lowerLevelSelector]?.background
- const lowerLevelVirtualDirectives = computed[lowerLevelSelector]?.virtualDirectives
- const lowerLevelVirtualDirectivesRaw = computed[lowerLevelSelector]?.virtualDirectivesRaw
-
- const dynamicVars = computed[selector] || {
- lowerLevelBackground,
- lowerLevelVirtualDirectives,
- lowerLevelVirtualDirectivesRaw
- }
-
- // Inheriting all of the applicable rules
- const existingRules = ruleset.filter(findRules({ component: component.name, ...combination, parent }))
- const computedDirectives = existingRules.map(r => r.directives).reduce((acc, directives) => ({ ...acc, ...directives }), {})
- const computedRule = {
- component: component.name,
- ...combination,
- parent,
- directives: computedDirectives
+ combination.lazy = component.lazy || parent?.lazy
+ combination.parent = parent
+ if (combination.state.indexOf('hover') >= 0) {
+ combination.lazy = true
}
- computed[selector] = computed[selector] || {}
- computed[selector].computedRule = computedRule
- computed[selector].dynamicVars = dynamicVars
-
- if (virtualComponents.has(component.name)) {
- const virtualName = [
- '--',
- component.name.toLowerCase(),
- combination.variant === 'normal'
- ? ''
- : combination.variant[0].toUpperCase() + combination.variant.slice(1).toLowerCase(),
- ...combination.state.filter(x => x !== 'normal').toSorted().map(state => state[0].toUpperCase() + state.slice(1).toLowerCase())
- ].join('')
-
- let inheritedTextColor = computedDirectives.textColor
- let inheritedTextAuto = computedDirectives.textAuto
- let inheritedTextOpacity = computedDirectives.textOpacity
- let inheritedTextOpacityMode = computedDirectives.textOpacityMode
- const lowerLevelTextSelector = [...selector.split(/ /g).slice(0, -1), soloSelector].join(' ')
- const lowerLevelTextRule = computed[lowerLevelTextSelector]
-
- if (inheritedTextColor == null || inheritedTextOpacity == null || inheritedTextOpacityMode == null) {
- inheritedTextColor = computedDirectives.textColor ?? lowerLevelTextRule.textColor
- inheritedTextAuto = computedDirectives.textAuto ?? lowerLevelTextRule.textAuto
- inheritedTextOpacity = computedDirectives.textOpacity ?? lowerLevelTextRule.textOpacity
- inheritedTextOpacityMode = computedDirectives.textOpacityMode ?? lowerLevelTextRule.textOpacityMode
- }
-
- const newTextRule = {
- ...computedRule,
- directives: {
- ...computedRule.directives,
- textColor: inheritedTextColor,
- textAuto: inheritedTextAuto ?? 'preserve',
- textOpacity: inheritedTextOpacity,
- textOpacityMode: inheritedTextOpacityMode
- }
- }
-
- dynamicVars.inheritedBackground = lowerLevelBackground
- dynamicVars.stacked = convert(stacked[lowerLevelSelector]).rgb
-
- const intendedTextColor = convert(findColor(inheritedTextColor, { dynamicVars, staticVars })).rgb
- const textColor = newTextRule.directives.textAuto === 'no-auto'
- ? intendedTextColor
- : getTextColor(
- convert(stacked[lowerLevelSelector]).rgb,
- intendedTextColor,
- newTextRule.directives.textAuto === 'preserve'
- )
-
- // Updating previously added rule
- const earlyLowerLevelRules = rules.filter(findRules(parent, true))
- const earlyLowerLevelRule = earlyLowerLevelRules.slice(-1)[0]
-
- const virtualDirectives = earlyLowerLevelRule.virtualDirectives || {}
- const virtualDirectivesRaw = earlyLowerLevelRule.virtualDirectivesRaw || {}
-
- // Storing color data in lower layer to use as custom css properties
- virtualDirectives[virtualName] = getTextColorAlpha(newTextRule.directives, textColor, dynamicVars)
- virtualDirectivesRaw[virtualName] = textColor
- earlyLowerLevelRule.virtualDirectives = virtualDirectives
- earlyLowerLevelRule.virtualDirectivesRaw = virtualDirectivesRaw
- computed[lowerLevelSelector].virtualDirectives = virtualDirectives
- computed[lowerLevelSelector].virtualDirectivesRaw = virtualDirectivesRaw
- } else {
- computed[selector] = computed[selector] || {}
-
- // TODO: DEFAULT TEXT COLOR
- const lowerLevelStackedBackground = stacked[lowerLevelSelector] || convert(ultimateBackgroundColor).rgb
-
- if (computedDirectives.background) {
- let inheritRule = null
- const variantRules = ruleset.filter(findRules({ component: component.name, variant: combination.variant, parent }))
- const lastVariantRule = variantRules[variantRules.length - 1]
- if (lastVariantRule) {
- inheritRule = lastVariantRule
- } else {
- const normalRules = ruleset.filter(findRules({ component: component.name, parent }))
- const lastNormalRule = normalRules[normalRules.length - 1]
- inheritRule = lastNormalRule
- }
-
- const inheritSelector = ruleToSelector({ ...inheritRule, parent }, true)
- const inheritedBackground = computed[inheritSelector].background
-
- dynamicVars.inheritedBackground = inheritedBackground
-
- const rgb = convert(findColor(computedDirectives.background, { dynamicVars, staticVars })).rgb
-
- if (!stacked[selector]) {
- let blend
- const alpha = computedDirectives.opacity ?? 1
- if (alpha >= 1) {
- blend = rgb
- } else if (alpha <= 0) {
- blend = lowerLevelStackedBackground
- } else {
- blend = alphaBlend(rgb, computedDirectives.opacity, lowerLevelStackedBackground)
- }
- stacked[selector] = blend
- computed[selector].background = { ...rgb, a: computedDirectives.opacity ?? 1 }
- }
- }
-
- if (computedDirectives.shadow) {
- dynamicVars.shadow = flattenDeep(findShadow(flattenDeep(computedDirectives.shadow), { dynamicVars, staticVars }))
- }
-
- if (!stacked[selector]) {
- computedDirectives.background = 'transparent'
- computedDirectives.opacity = 0
- stacked[selector] = lowerLevelStackedBackground
- computed[selector].background = { ...lowerLevelStackedBackground, a: 0 }
- }
-
- dynamicVars.stacked = stacked[selector]
- dynamicVars.background = computed[selector].background
-
- const dynamicSlots = Object.entries(computedDirectives).filter(([k, v]) => k.startsWith('--'))
-
- dynamicSlots.forEach(([k, v]) => {
- const [type, ...value] = v.split('|').map(x => x.trim()) // woah, Extreme!
- switch (type) {
- case 'color': {
- const color = findColor(value[0], { dynamicVars, staticVars })
- dynamicVars[k] = color
- if (component.name === 'Root') {
- staticVars[k.substring(2)] = color
- }
- break
- }
- case 'shadow': {
- const shadow = value
- dynamicVars[k] = shadow
- if (component.name === 'Root') {
- staticVars[k.substring(2)] = shadow
- }
- break
- }
- case 'generic': {
- dynamicVars[k] = value
- if (component.name === 'Root') {
- staticVars[k.substring(2)] = value
- }
- break
- }
- }
- })
-
- addRule({
- dynamicVars,
- selector: cssSelector,
- component: component.name,
- ...combination,
- parent,
- directives: computedDirectives
- })
- }
+ combinations.push(combination)
innerComponents.forEach(innerComponent => {
- if (innerComponent.lazy) {
- promises.push(new Promise((resolve, reject) => {
- setTimeout(() => {
- try {
- processInnerComponent(innerComponent, lazyRules, { parent, component: name, ...combination })
- resolve()
- } catch (e) {
- reject(e)
- }
- }, 0)
- }))
- } else {
- processInnerComponent(innerComponent, rules, { parent, component: name, ...combination })
- }
+ combinations.push(...processInnerComponent(innerComponent, combination))
})
- // const tt1 = performance.now()
- // if (!component.virtual) {
- // console.log('State-variant ' + combination.variant + ' : ' + combination.state.join('+') + ' procession time: ' + (tt1 - tt0) + 'ms')
- // }
})
- // const t1 = performance.now()
- // if (!component.virtual) {
- // const path = [...parentList, component.name].join(' > ')
- // console.log('Component ' + path + ' procession time: ' + (t1 - t0) + 'ms')
- // }
+ return combinations
}
- processInnerComponent(components.Root, eagerRules)
- console.debug('Eager combinations processed:' + counter)
- const lazyExec = Promise.all(promises).then(() => {
- console.debug('Total combinations processed: ' + counter)
- }).then(() => lazyRules)
+ const t0 = performance.now()
+ const combinations = processInnerComponent(components.Root)
+ const t1 = performance.now()
+ console.debug('Tree tranveral took ' + (t1 - t0) + ' ms')
+
+ combinations.forEach((combination) => {
+ if (combination.lazy) {
+ lazyPromises.push(async () => processCombination(combination, lazyRules))
+ } else {
+ processCombination(combination, eagerRules)
+ }
+ })
+ const t2 = performance.now()
+ console.debug('Eager processing took ' + (t2 - t1) + ' ms')
return {
- lazy: lazyExec,
+ lazy: async () => {
+ await Promise.all(lazyPromises.map(x => x()))
+ return lazyRules
+ },
eager: eagerRules,
staticVars
}