Merge branch 'fix-style-editors' into shigusegubu-themes3

This commit is contained in:
Henry Jameson 2025-07-02 22:55:29 +03:00
commit a55d571a44
5 changed files with 86 additions and 106 deletions

View file

@ -11,13 +11,7 @@
<link rel="preload" href="/static/pleromatan_apology_fox_small.webp" as="image" /> <link rel="preload" href="/static/pleromatan_apology_fox_small.webp" as="image" />
<!-- putting styles here to avoid having to wait for styles to load up --> <!-- putting styles here to avoid having to wait for styles to load up -->
<link rel="stylesheet" id="splashscreen" href="/static/splash.css" /> <link rel="stylesheet" id="splashscreen" href="/static/splash.css" />
<link rel="stylesheet" id="pleroma-eager-styles" type="text/css" href="/static/empty.css" /> <link rel="stylesheet" id="custom-styles-holder" type="text/css" href="/static/empty.css" />
<link rel="stylesheet" id="pleroma-lazy-styles" type="text/css" href="/static/empty.css" />
<link rel="stylesheet" id="theme-holder" type="text/css" href="/static/empty.css" />
<link rel="stylesheet" id="theme-preview-holder" type="text/css" href="/static/empty.css" />
<link rel="stylesheet" id="component-style-holder" type="text/css" href="/static/empty.css" />
<link rel="stylesheet" id="editor-overall-holder" type="text/css" href="/static/empty.css" />
<link rel="stylesheet" id="old-editor-overall-holder" type="text/css" href="/static/empty.css" />
<!--server-generated-meta--> <!--server-generated-meta-->
</head> </head>
<body> <body>

View file

@ -2,6 +2,7 @@ import Checkbox from 'src/components/checkbox/checkbox.vue'
import ColorInput from 'src/components/color_input/color_input.vue' import ColorInput from 'src/components/color_input/color_input.vue'
import genRandomSeed from 'src/services/random_seed/random_seed.service.js' import genRandomSeed from 'src/services/random_seed/random_seed.service.js'
import { createStyleSheet, adoptStyleSheets } from 'src/services/style_setter/style_setter.js'
export default { export default {
components: { components: {
@ -51,15 +52,11 @@ export default {
this.$emit('update:shadow', { axis, value: Number(value) }) this.$emit('update:shadow', { axis, value: Number(value) })
}, },
update () { update () {
const styleEl = document.getElementById('component-style-holder') const sheet = createStyleSheet('style-component-preview')
const styleSheet = styleEl.sheet
for (let i = styleSheet.cssRules.length - 1; i >= 0; --i) { sheet.clear()
styleSheet.deleteRule(i)
}
const result = []
const result = [this.previewCss]
if (this.colorOverride) result.push(`--background: ${this.colorOverride}`) if (this.colorOverride) result.push(`--background: ${this.colorOverride}`)
const styleRule = [ const styleRule = [
@ -71,13 +68,15 @@ export default {
'\n}' '\n}'
].join('') ].join('')
styleSheet.insertRule(styleRule) sheet.addRule(styleRule)
styleSheet.insertRule([ sheet.addRule([
'#component-preview-', this.randomSeed, ' {\n', '#component-preview-', this.randomSeed, ' {\n',
this.previewCss,
...result, ...result,
'\n}' '\n}'
].join(''), 'index-max') ].join(''))
sheet.ready = true
adoptStyleSheets()
} }
} }
} }

View file

@ -15,6 +15,7 @@ import {
getCssRules getCssRules
} from 'src/services/theme_data/css_utils.js' } from 'src/services/theme_data/css_utils.js'
import { deserialize } from 'src/services/theme_data/iss_deserializer.js' import { deserialize } from 'src/services/theme_data/iss_deserializer.js'
import { createStyleSheet, adoptStyleSheets } from 'src/services/style_setter/style_setter.js'
import SharedComputedObject from '../helpers/shared_computed_object.js' import SharedComputedObject from '../helpers/shared_computed_object.js'
import ProfileSettingIndicator from '../helpers/profile_setting_indicator.vue' import ProfileSettingIndicator from '../helpers/profile_setting_indicator.vue'
@ -410,16 +411,17 @@ const AppearanceTab = {
this.compilationCache[key] = theme3 this.compilationCache[key] = theme3
} }
const styleEl = document.getElementById('theme-preview-holder')
const styleSheet = styleEl.sheet const sheet = createStyleSheet('appearance-tab-previews')
styleSheet.insertRule([ sheet.addRule([
'#theme-preview-', '#theme-preview-',
key, key,
' {\n', ' {\n',
getCssRules(theme3.eager).join('\n'), getCssRules(theme3.eager).join('\n'),
'\n}' '\n}'
].join(''), 'index-max') ].join(''))
sheet.ready = true
adoptStyleSheets()
} }
} }
} }

View file

@ -1,4 +1,4 @@
import { ref, reactive, computed, watch, watchEffect, provide, getCurrentInstance } from 'vue' import { ref, reactive, computed, watch, provide, getCurrentInstance } from 'vue'
import { useInterfaceStore } from 'src/stores/interface' import { useInterfaceStore } from 'src/stores/interface'
import { get, set, unset, throttle } from 'lodash' import { get, set, unset, throttle } from 'lodash'
@ -19,6 +19,7 @@ import Preview from '../theme_tab/theme_preview.vue'
import VirtualDirectivesTab from './virtual_directives_tab.vue' import VirtualDirectivesTab from './virtual_directives_tab.vue'
import { createStyleSheet, adoptStyleSheets } from 'src/services/style_setter/style_setter.js'
import { init, findColor } from 'src/services/theme_data/theme_data_3.service.js' import { init, findColor } from 'src/services/theme_data/theme_data_3.service.js'
import { getCssRules } from 'src/services/theme_data/css_utils.js' import { getCssRules } from 'src/services/theme_data/css_utils.js'
import { serialize } from 'src/services/theme_data/iss_serializer.js' import { serialize } from 'src/services/theme_data/iss_serializer.js'
@ -694,29 +695,19 @@ export default {
return return
} }
const styleEl = document.getElementById('editor-overall-holder') const sheet = createStyleSheet('style-tab-overall-preview')
const styleSheet = styleEl.sheet
console.log(styleSheet) sheet.clear()
console.log('BEFORE', styleSheet.cssRules) sheet.addRule([
for (let i = styleSheet.cssRules.length - 1; i >= 0; --i) {
styleSheet.deleteRule(i)
}
styleSheet.insertRule([
'#edited-style-preview {\n', '#edited-style-preview {\n',
css.join('\n'), css.join('\n'),
'\n}' '\n}'
].join(''), 'index-max') ].join(''))
styleSheet.insertRule([ sheet.ready = true
'#edited-style-preview {\n', adoptStyleSheets()
css.join('\n'),
'\n}'
].join(''), 'index-max')
console.log('AFTER', styleSheet.cssRules)
}) })
const updateOverallPreview = () => { const updateOverallPreview = throttle(() => {
try { try {
overallPreviewRules.value = init({ overallPreviewRules.value = init({
inputRuleset: [ inputRuleset: [
@ -738,7 +729,7 @@ export default {
console.error('Could not compile preview theme', e) console.error('Could not compile preview theme', e)
return null return null
} }
} }, 1000)
// //
// Apart from "hover" we can't really show how component looks like in // Apart from "hover" we can't really show how component looks like in
// certain states, so we have to fake them. // certain states, so we have to fake them.

View file

@ -1,47 +1,65 @@
import { init, getEngineChecksum } from '../theme_data/theme_data_3.service.js' import { init, getEngineChecksum } from '../theme_data/theme_data_3.service.js'
import { getCssRules } from '../theme_data/css_utils.js' import { getCssRules } from '../theme_data/css_utils.js'
import { defaultState } from 'src/modules/default_config_state.js' import { defaultState } from 'src/modules/default_config_state.js'
import { chunk } from 'lodash' import { chunk, throttle } from 'lodash'
import localforage from 'localforage' import localforage from 'localforage'
// On platforms where this is not supported, it will return undefined // On platforms where this is not supported, it will return undefined
// Otherwise it will return an array // Otherwise it will return an array
const supportsAdoptedStyleSheets = !!document.adoptedStyleSheets const supportsAdoptedStyleSheets = !!document.adoptedStyleSheets
const createStyleSheet = (id) => { const stylesheets = {}
if (supportsAdoptedStyleSheets) {
return { export const createStyleSheet = (id) => {
el: null, if (stylesheets[id]) return stylesheets[id]
sheet: new CSSStyleSheet(), const newStyleSheet = {
rules: [] rules: [],
ready: false,
clear () {
this.rules = []
},
addRule (rule) {
this.rules.push(
rule
.replace(/backdrop-filter:[^;]+;/g, '') // Remove backdrop-filter
.replace(/var\(--shadowFilter\)[^;]*;/g, '') // Remove shadowFilter references
)
} }
} }
const el = document.getElementById(id) stylesheets[id] = newStyleSheet
// Clear all rules in it return newStyleSheet
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) => { export const adoptStyleSheets = throttle(() => {
if (supportsAdoptedStyleSheets) { if (supportsAdoptedStyleSheets) {
document.adoptedStyleSheets = styles.map(s => s.sheet) document.adoptedStyleSheets = Object
.values(stylesheets)
.filter(x => x.ready)
.map(x => {
const css = new CSSStyleSheet()
x.rules.forEach(r => css.insertRule(r, css.cssRules.length))
return css
})
} else {
const holder = document.getElementById('custom-styles-holder')
Object
.values(stylesheets)
.forEach(sheet => {
sheet.rules.forEach(r => holder.sheet.insertRule(r))
})
} }
// Some older browsers do not support document.adoptedStyleSheets. // Some older browsers do not support document.adoptedStyleSheets.
// In this case, we use the <style> elements. // In this case, we use the <style> elements.
// Since the <style> elements we need are already in the DOM, there // Since the <style> elements we need are already in the DOM, there
// is nothing to do here. // is nothing to do here.
} }, 500)
const EAGER_STYLE_ID = 'pleroma-eager-styles'
const LAZY_STYLE_ID = 'pleroma-lazy-styles'
export const generateTheme = (inputRuleset, callbacks, debug) => { export const generateTheme = (inputRuleset, callbacks, debug) => {
const { const {
@ -97,10 +115,13 @@ export const tryLoadCache = async () => {
const eagerStyles = createStyleSheet(EAGER_STYLE_ID) const eagerStyles = createStyleSheet(EAGER_STYLE_ID)
const lazyStyles = createStyleSheet(LAZY_STYLE_ID) const lazyStyles = createStyleSheet(LAZY_STYLE_ID)
cache.data[0].forEach(rule => eagerStyles.sheet.insertRule(rule, 'index-max')) cache.data[0].forEach(rule => eagerStyles.addRule(rule, 'index-max'))
cache.data[1].forEach(rule => lazyStyles.sheet.insertRule(rule, 'index-max')) cache.data[1].forEach(rule => lazyStyles.addRule(rule, 'index-max'))
adoptStyleSheets([eagerStyles, lazyStyles]) eagerStyles.ready = true
lazyStyles.ready = true
adoptStyleSheets()
console.info(`Loaded theme from cache`) console.info(`Loaded theme from cache`)
return true return true
@ -123,54 +144,26 @@ export const applyTheme = (
const eagerStyles = createStyleSheet(EAGER_STYLE_ID) const eagerStyles = createStyleSheet(EAGER_STYLE_ID)
const lazyStyles = createStyleSheet(LAZY_STYLE_ID) const lazyStyles = createStyleSheet(LAZY_STYLE_ID)
const insertRule = (styles, rule) => {
try {
// Try to use modern syntax first
try {
styles.sheet.insertRule(rule, 'index-max')
styles.rules.push(rule)
} catch {
// Fallback for older browsers that don't support 'index-max'
styles.sheet.insertRule(rule, styles.sheet.cssRules.length)
styles.rules.push(rule)
}
} catch (e) {
console.warn('Can\'t insert rule due to lack of support', e, rule)
// Try to sanitize the rule for better compatibility
try {
// Remove any potentially problematic CSS features
let sanitizedRule = rule
.replace(/backdrop-filter:[^;]+;/g, '') // Remove backdrop-filter
.replace(/var\(--shadowFilter\)[^;]*;/g, '') // Remove shadowFilter references
if (sanitizedRule !== rule) {
styles.sheet.insertRule(sanitizedRule, styles.sheet.cssRules.length)
styles.rules.push(sanitizedRule)
}
} catch (e2) {
console.error('Failed to insert even sanitized rule', e2)
}
}
}
const { lazyProcessFunc } = generateTheme( const { lazyProcessFunc } = generateTheme(
input, input,
{ {
onNewRule (rule, isLazy) { onNewRule (rule, isLazy) {
if (isLazy) { if (isLazy) {
insertRule(lazyStyles, rule) lazyStyles.addRule(rule)
} else { } else {
insertRule(eagerStyles, rule) eagerStyles.addRule(rule)
} }
}, },
onEagerFinished () { onEagerFinished () {
adoptStyleSheets([eagerStyles]) eagerStyles.ready = true
adoptStyleSheets()
onEagerFinish() onEagerFinish()
console.info('Eager part of theme finished, waiting for lazy part to finish to store cache') console.info('Eager part of theme finished, waiting for lazy part to finish to store cache')
}, },
onLazyFinished () { onLazyFinished () {
adoptStyleSheets([eagerStyles, lazyStyles]) eagerStyles.ready = true
adoptStyleSheets()
const cache = { engineChecksum: getEngineChecksum(), data: [eagerStyles.rules, lazyStyles.rules] } const cache = { engineChecksum: getEngineChecksum(), data: [eagerStyles.rules, lazyStyles.rules] }
onFinish(cache) onFinish(cache)
localforage.setItem('pleromafe-theme-cache', cache) localforage.setItem('pleromafe-theme-cache', cache)
@ -239,17 +232,18 @@ export const applyConfig = (input) => {
.filter(([, v]) => v) .filter(([, v]) => v)
.map(([k, v]) => `--${k}: ${v}`).join(';') .map(([k, v]) => `--${k}: ${v}`).join(';')
const styleEl = document.getElementById('theme-holder') const styleSheet = createStyleSheet('theme-holder')
const styleSheet = styleEl.sheet
styleSheet.insertRule(`:root { ${rules} }`, 'index-max') styleSheet.addRule(`:root { ${rules} }`, 'index-max')
// TODO find a way to make this not apply to theme previews // TODO find a way to make this not apply to theme previews
if (Object.prototype.hasOwnProperty.call(config, 'forcedRoundness')) { if (Object.prototype.hasOwnProperty.call(config, 'forcedRoundness')) {
styleSheet.insertRule(` *:not(.preview-block) { styleSheet.addRule(` *:not(.preview-block) {
--roundness: var(--forcedRoundness) !important; --roundness: var(--forcedRoundness) !important;
}`, 'index-max') }`, 'index-max')
} }
styleSheet.ready = true
adoptStyleSheets()
} }
export const getResourcesIndex = async (url, parser = JSON.parse) => { export const getResourcesIndex = async (url, parser = JSON.parse) => {