Merge branch 'appearance-tab' into shigusegubu-themes3
This commit is contained in:
commit
6447f89d77
16 changed files with 267 additions and 111 deletions
|
|
@ -6,7 +6,46 @@ import { getCssRules } from '../theme_data/css_utils.js'
|
|||
import { defaultState } from '../../modules/config.js'
|
||||
import { chunk } from 'lodash'
|
||||
|
||||
export const generateTheme = async (input, callbacks) => {
|
||||
// On platforms where this is not supported, it will return undefined
|
||||
// Otherwise it will return an array
|
||||
const supportsAdoptedStyleSheets = !!document.adoptedStyleSheets
|
||||
|
||||
const createStyleSheet = (id) => {
|
||||
if (supportsAdoptedStyleSheets) {
|
||||
return {
|
||||
el: null,
|
||||
sheet: new CSSStyleSheet(),
|
||||
rules: []
|
||||
}
|
||||
}
|
||||
|
||||
const el = document.getElementById(id)
|
||||
// Clear all rules in it
|
||||
for (let i = el.sheet.cssRules.length - 1; i >= 0; --i) {
|
||||
el.sheet.deleteRule(i)
|
||||
}
|
||||
|
||||
return {
|
||||
el,
|
||||
sheet: el.sheet,
|
||||
rules: []
|
||||
}
|
||||
}
|
||||
|
||||
const EAGER_STYLE_ID = 'pleroma-eager-styles'
|
||||
const LAZY_STYLE_ID = 'pleroma-lazy-styles'
|
||||
|
||||
const adoptStyleSheets = (styles) => {
|
||||
if (supportsAdoptedStyleSheets) {
|
||||
document.adoptedStyleSheets = styles.map(s => s.sheet)
|
||||
}
|
||||
// Some older browsers do not support document.adoptedStyleSheets.
|
||||
// In this case, we use the <style> elements.
|
||||
// Since the <style> elements we need are already in the DOM, there
|
||||
// is nothing to do here.
|
||||
}
|
||||
|
||||
export const generateTheme = async (input, callbacks, debug) => {
|
||||
const {
|
||||
onNewRule = (rule, isLazy) => {},
|
||||
onLazyFinished = () => {},
|
||||
|
|
@ -22,9 +61,11 @@ export const generateTheme = async (input, callbacks) => {
|
|||
}
|
||||
|
||||
// Assuming that "worst case scenario background" is panel background since it's the most likely one
|
||||
const themes3 = init(extraRules, extraRules[0].directives['--bg'].split('|')[1].trim())
|
||||
const themes3 = init(extraRules, extraRules[0].directives['--bg'].split('|')[1].trim(), debug)
|
||||
|
||||
getCssRules(themes3.eager, themes3.staticVars).forEach(rule => {
|
||||
console.log('DEBUG 2 IS', debug)
|
||||
|
||||
getCssRules(themes3.eager, debug).forEach(rule => {
|
||||
// Hacks to support multiple selectors on same component
|
||||
if (rule.match(/::-webkit-scrollbar-button/)) {
|
||||
const parts = rule.split(/[{}]/g)
|
||||
|
|
@ -54,7 +95,7 @@ export const generateTheme = async (input, callbacks) => {
|
|||
const processChunk = () => {
|
||||
const chunk = chunks[counter]
|
||||
Promise.all(chunk.map(x => x())).then(result => {
|
||||
getCssRules(result.filter(x => x), themes3.staticVars).forEach(rule => {
|
||||
getCssRules(result.filter(x => x), debug).forEach(rule => {
|
||||
if (rule.match(/\.modal-view/)) {
|
||||
const parts = rule.split(/[{}]/g)
|
||||
const newRule = [
|
||||
|
|
@ -98,13 +139,13 @@ export const tryLoadCache = () => {
|
|||
return false
|
||||
}
|
||||
if (cache.engineChecksum === getEngineChecksum()) {
|
||||
const styleSheet = new CSSStyleSheet()
|
||||
const lazyStyleSheet = new CSSStyleSheet()
|
||||
const eagerStyles = createStyleSheet(EAGER_STYLE_ID)
|
||||
const lazyStyles = createStyleSheet(LAZY_STYLE_ID)
|
||||
|
||||
cache.data[0].forEach(rule => styleSheet.insertRule(rule, 'index-max'))
|
||||
cache.data[1].forEach(rule => lazyStyleSheet.insertRule(rule, 'index-max'))
|
||||
cache.data[0].forEach(rule => eagerStyles.sheet.insertRule(rule, 'index-max'))
|
||||
cache.data[1].forEach(rule => lazyStyles.sheet.insertRule(rule, 'index-max'))
|
||||
|
||||
document.adoptedStyleSheets = [styleSheet, lazyStyleSheet]
|
||||
adoptStyleSheets([eagerStyles, lazyStyles])
|
||||
|
||||
return true
|
||||
} else {
|
||||
|
|
@ -113,34 +154,35 @@ export const tryLoadCache = () => {
|
|||
}
|
||||
}
|
||||
|
||||
export const applyTheme = async (input, onFinish = (data) => {}) => {
|
||||
const styleSheet = new CSSStyleSheet()
|
||||
const styleArray = []
|
||||
const lazyStyleSheet = new CSSStyleSheet()
|
||||
const lazyStyleArray = []
|
||||
export const applyTheme = async (input, onFinish = (data) => {}, debug) => {
|
||||
const eagerStyles = createStyleSheet(EAGER_STYLE_ID)
|
||||
const lazyStyles = createStyleSheet(LAZY_STYLE_ID)
|
||||
|
||||
console.log('DEBUG IS', debug)
|
||||
|
||||
const { lazyProcessFunc } = await generateTheme(
|
||||
input,
|
||||
{
|
||||
onNewRule (rule, isLazy) {
|
||||
if (isLazy) {
|
||||
lazyStyleSheet.insertRule(rule, 'index-max')
|
||||
lazyStyleArray.push(rule)
|
||||
lazyStyles.sheet.insertRule(rule, 'index-max')
|
||||
lazyStyles.rules.push(rule)
|
||||
} else {
|
||||
styleSheet.insertRule(rule, 'index-max')
|
||||
styleArray.push(rule)
|
||||
eagerStyles.sheet.insertRule(rule, 'index-max')
|
||||
eagerStyles.rules.push(rule)
|
||||
}
|
||||
},
|
||||
onEagerFinished () {
|
||||
document.adoptedStyleSheets = [styleSheet]
|
||||
adoptStyleSheets([eagerStyles])
|
||||
},
|
||||
onLazyFinished () {
|
||||
document.adoptedStyleSheets = [styleSheet, lazyStyleSheet]
|
||||
const cache = { engineChecksum: getEngineChecksum(), data: [styleArray, lazyStyleArray] }
|
||||
adoptStyleSheets([eagerStyles, lazyStyles])
|
||||
const cache = { engineChecksum: getEngineChecksum(), data: [eagerStyles.rules, lazyStyles.rules] }
|
||||
onFinish(cache)
|
||||
localStorage.setItem('pleroma-fe-theme-cache', JSON.stringify(cache))
|
||||
}
|
||||
}
|
||||
},
|
||||
debug
|
||||
)
|
||||
|
||||
setTimeout(lazyProcessFunc, 0)
|
||||
|
|
@ -153,22 +195,41 @@ const extractStyleConfig = ({
|
|||
contentColumnWidth,
|
||||
notifsColumnWidth,
|
||||
emojiReactionsScale,
|
||||
roundnessOverride,
|
||||
emojiSize,
|
||||
navbarSize,
|
||||
panelHeaderSize,
|
||||
textSize
|
||||
}) => ({
|
||||
sidebarColumnWidth,
|
||||
contentColumnWidth,
|
||||
notifsColumnWidth,
|
||||
emojiReactionsScale,
|
||||
roundnessOverride,
|
||||
emojiSize,
|
||||
navbarSize,
|
||||
panelHeaderSize,
|
||||
textSize
|
||||
})
|
||||
textSize,
|
||||
forcedRoundness
|
||||
}) => {
|
||||
const result = {
|
||||
sidebarColumnWidth,
|
||||
contentColumnWidth,
|
||||
notifsColumnWidth,
|
||||
emojiReactionsScale,
|
||||
emojiSize,
|
||||
navbarSize,
|
||||
panelHeaderSize,
|
||||
textSize
|
||||
}
|
||||
|
||||
console.log(forcedRoundness)
|
||||
switch (forcedRoundness) {
|
||||
case 'disable':
|
||||
break
|
||||
case '0':
|
||||
result.forcedRoundness = '0'
|
||||
break
|
||||
case '1':
|
||||
result.forcedRoundness = '1px'
|
||||
break
|
||||
case '2':
|
||||
result.forcedRoundness = '0.4rem'
|
||||
break
|
||||
default:
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
const defaultStyleConfig = extractStyleConfig(defaultState)
|
||||
|
||||
|
|
@ -188,13 +249,21 @@ export const applyConfig = (input) => {
|
|||
.filter(([k, v]) => v)
|
||||
.map(([k, v]) => `--${k}: ${v}`).join(';')
|
||||
|
||||
document.getElementById('style-config')?.remove()
|
||||
const styleEl = document.createElement('style')
|
||||
styleEl.id = 'style-config'
|
||||
head.appendChild(styleEl)
|
||||
const styleSheet = styleEl.sheet
|
||||
|
||||
styleSheet.toString()
|
||||
styleSheet.insertRule(`:root { ${rules} }`, 'index-max')
|
||||
|
||||
if (Object.prototype.hasOwnProperty.call(config, 'forcedRoundness')) {
|
||||
styleSheet.insertRule(` * {
|
||||
--roundness: var(--forcedRoundness) !important;
|
||||
}`, 'index-max')
|
||||
}
|
||||
|
||||
body.classList.remove('hidden')
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,11 +2,6 @@ import { convert } from 'chromatism'
|
|||
|
||||
import { hex2rgb, rgba2css } from '../color_convert/color_convert.js'
|
||||
|
||||
// This changes what backgrounds are used to "stacked" solid colors so you can see
|
||||
// what theme engine "thinks" is actual background color is for purposes of text color
|
||||
// generation and for when --stacked variable is used
|
||||
const DEBUG = false
|
||||
|
||||
export const parseCssShadow = (text) => {
|
||||
const dimensions = /(\d[a-z]*\s?){2,4}/.exec(text)?.[0]
|
||||
const inset = /inset/.exec(text)?.[0]
|
||||
|
|
@ -66,7 +61,10 @@ export const getCssShadowFilter = (input) => {
|
|||
.join(' ')
|
||||
}
|
||||
|
||||
export const getCssRules = (rules) => rules.map(rule => {
|
||||
// `debug` changes what backgrounds are used to "stacked" solid colors so you can see
|
||||
// what theme engine "thinks" is actual background color is for purposes of text color
|
||||
// generation and for when --stacked variable is used
|
||||
export const getCssRules = (rules, debug) => rules.map(rule => {
|
||||
let selector = rule.selector
|
||||
if (!selector) {
|
||||
selector = 'html'
|
||||
|
|
@ -93,7 +91,7 @@ export const getCssRules = (rules) => rules.map(rule => {
|
|||
].join(';\n ')
|
||||
}
|
||||
case 'background': {
|
||||
if (DEBUG) {
|
||||
if (debug) {
|
||||
return `
|
||||
--background: ${getCssColorString(rule.dynamicVars.stacked)};
|
||||
background-color: ${getCssColorString(rule.dynamicVars.stacked)};
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import { sortBy } from 'lodash'
|
||||
|
||||
// "Unrolls" a tree structure of item: { parent: { ...item2, parent: { ...item3, parent: {...} } }}
|
||||
// into an array [item2, item3] for iterating
|
||||
export const unroll = (item) => {
|
||||
|
|
@ -24,7 +26,7 @@ export const getAllPossibleCombinations = (array) => {
|
|||
})
|
||||
const flatCombos = newCombos.reduce((acc, x) => [...acc, ...x], [])
|
||||
const uniqueComboStrings = new Set()
|
||||
const uniqueCombos = flatCombos.map(x => x.toSorted()).filter(x => {
|
||||
const uniqueCombos = flatCombos.map(sortBy).filter(x => {
|
||||
if (uniqueComboStrings.has(x.join())) {
|
||||
return false
|
||||
} else {
|
||||
|
|
@ -64,7 +66,7 @@ export const genericRuleToSelector = components => (rule, ignoreOutOfTreeSelecto
|
|||
}
|
||||
|
||||
const selectors = [realSelector, applicableVariant, ...applicableStates]
|
||||
.toSorted((a, b) => {
|
||||
.sort((a, b) => {
|
||||
if (a.startsWith(':')) return 1
|
||||
if (/^[a-z]/.exec(a)) return -1
|
||||
else return 0
|
||||
|
|
|
|||
|
|
@ -138,7 +138,7 @@ export const convertTheme2To3 = (data) => {
|
|||
Object.keys(data.opacity || {}).forEach(key => {
|
||||
if (!opacityKeys.has(key) || data.opacity[key] === undefined) return null
|
||||
const originalOpacity = data.opacity[key]
|
||||
const rule = {}
|
||||
const rule = { source: '2to3' }
|
||||
|
||||
switch (key) {
|
||||
case 'alert':
|
||||
|
|
@ -213,7 +213,7 @@ export const convertTheme2To3 = (data) => {
|
|||
Object.keys(data.radii || {}).forEach(key => {
|
||||
if (!radiiKeys.has(key) || data.radii[key] === undefined) return null
|
||||
const originalRadius = data.radii[key]
|
||||
const rule = {}
|
||||
const rule = { source: '2to3' }
|
||||
|
||||
switch (key) {
|
||||
case 'btn':
|
||||
|
|
@ -266,7 +266,7 @@ export const convertTheme2To3 = (data) => {
|
|||
Object.keys(data.fonts || {}).forEach(key => {
|
||||
if (!fontsKeys.has(key)) return
|
||||
const originalFont = data.fonts[key].family
|
||||
const rule = {}
|
||||
const rule = { source: '2to3' }
|
||||
|
||||
switch (key) {
|
||||
case 'interface':
|
||||
|
|
@ -300,7 +300,7 @@ export const convertTheme2To3 = (data) => {
|
|||
Object.keys(data.shadows || {}).forEach(key => {
|
||||
if (!shadowsKeys.has(key)) return
|
||||
const originalShadow = data.shadows[key]
|
||||
const rule = {}
|
||||
const rule = { source: '2to3' }
|
||||
|
||||
switch (key) {
|
||||
case 'panel':
|
||||
|
|
@ -369,7 +369,7 @@ export const convertTheme2To3 = (data) => {
|
|||
|
||||
const extendedRules = Object.entries(extendedBaseKeys).map(([prefix, keys]) => {
|
||||
if (nonComponentPrefixes.has(prefix)) return null
|
||||
const rule = {}
|
||||
const rule = { source: '2to3' }
|
||||
if (prefix === 'alertPopup') {
|
||||
rule.component = 'Alert'
|
||||
rule.parent = { component: 'Popover' }
|
||||
|
|
@ -402,7 +402,7 @@ export const convertTheme2To3 = (data) => {
|
|||
const leftoverKey = key.replace(prefix, '')
|
||||
const parts = (leftoverKey || 'Bg').match(/[A-Z][a-z]*/g)
|
||||
const last = parts.slice(-1)[0]
|
||||
let newRule = { directives: {} }
|
||||
let newRule = { source: '2to3', directives: {} }
|
||||
let variantArray = []
|
||||
|
||||
switch (last) {
|
||||
|
|
@ -462,12 +462,12 @@ export const convertTheme2To3 = (data) => {
|
|||
|
||||
if (prefix === 'popover' && variantArray[0] === 'Post') {
|
||||
newRule.component = 'Post'
|
||||
newRule.parent = { component: 'Popover' }
|
||||
newRule.parent = { source: '2to3hack', component: 'Popover' }
|
||||
variantArray = variantArray.filter(x => x !== 'Post')
|
||||
}
|
||||
|
||||
if (prefix === 'selectedMenu' && variantArray[0] === 'Popover') {
|
||||
newRule.parent = { component: 'Popover' }
|
||||
newRule.parent = { source: '2to3hack', component: 'Popover' }
|
||||
variantArray = variantArray.filter(x => x !== 'Popover')
|
||||
}
|
||||
|
||||
|
|
@ -477,12 +477,12 @@ export const convertTheme2To3 = (data) => {
|
|||
case 'alert': {
|
||||
const hasPanel = variantArray.find(x => x === 'Panel')
|
||||
if (hasPanel) {
|
||||
newRule.parent = { component: 'PanelHeader' }
|
||||
newRule.parent = { source: '2to3hack', component: 'PanelHeader', parent: newRule.parent }
|
||||
variantArray = variantArray.filter(x => x !== 'Panel')
|
||||
}
|
||||
const hasTop = variantArray.find(x => x === 'Top') // TopBar
|
||||
if (hasTop) {
|
||||
newRule.parent = { component: 'TopBar' }
|
||||
newRule.parent = { source: '2to3hack', component: 'TopBar', parent: newRule.parent }
|
||||
variantArray = variantArray.filter(x => x !== 'Top' && x !== 'Bar')
|
||||
}
|
||||
break
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { convert, brightness } from 'chromatism'
|
||||
import sum from 'hash-sum'
|
||||
import { flattenDeep } from 'lodash'
|
||||
import { flattenDeep, sortBy } from 'lodash'
|
||||
import {
|
||||
alphaBlend,
|
||||
getTextColor,
|
||||
|
|
@ -149,14 +149,14 @@ const ruleToSelector = genericRuleToSelector(components)
|
|||
|
||||
export const getEngineChecksum = () => engineChecksum
|
||||
|
||||
export const init = (extraRuleset, ultimateBackgroundColor) => {
|
||||
export const init = (extraRuleset, ultimateBackgroundColor, debug) => {
|
||||
const staticVars = {}
|
||||
const stacked = {}
|
||||
const computed = {}
|
||||
|
||||
const rulesetUnsorted = [
|
||||
...Object.values(components)
|
||||
.map(c => (c.defaultRules || []).map(r => ({ component: c.name, ...r })))
|
||||
.map(c => (c.defaultRules || []).map(r => ({ component: c.name, ...r, source: 'Built-in' })))
|
||||
.reduce((acc, arr) => [...acc, ...arr], []),
|
||||
...extraRuleset
|
||||
].map(rule => {
|
||||
|
|
@ -226,7 +226,7 @@ export const init = (extraRuleset, ultimateBackgroundColor) => {
|
|||
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())
|
||||
...sortBy(combination.state.filter(x => x !== 'normal')).map(state => state[0].toUpperCase() + state.slice(1).toLowerCase())
|
||||
].join('')
|
||||
|
||||
let inheritedTextColor = computedDirectives.textColor
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue