diff --git a/src/components/style_switcher/style_switcher.js b/src/components/style_switcher/style_switcher.js
index 9fe1bf592e..f751260aa8 100644
--- a/src/components/style_switcher/style_switcher.js
+++ b/src/components/style_switcher/style_switcher.js
@@ -1,7 +1,15 @@
import { rgb2hex, hex2rgb, getContrastRatio, getContrastRatioLayers, alphaBlend } from '../../services/color_convert/color_convert.js'
import { set, delete as del } from 'vue'
import { merge } from 'lodash'
-import { generateCompat, generateColors, generateShadows, generateRadii, generateFonts, composePreset, getThemes } from '../../services/style_setter/style_setter.js'
+import {
+ generateColors,
+ generateShadows,
+ generateRadii,
+ generateFonts,
+ composePreset,
+ getThemes,
+ CURRENT_VERSION
+} from '../../services/style_setter/style_setter.js'
import ColorInput from '../color_input/color_input.vue'
import RangeInput from '../range_input/range_input.vue'
import OpacityInput from '../opacity_input/opacity_input.vue'
@@ -135,15 +143,6 @@ export default {
selectedVersion () {
return Array.isArray(this.selected) ? 1 : 2
},
- currentCompat () {
- return generateCompat({
- shadows: this.shadowsLocal,
- fonts: this.fontsLocal,
- opacity: this.currentOpacity,
- colors: this.currentColors,
- radii: this.currentRadii
- })
- },
currentColors () {
return {
bg: this.bgColorLocal,
@@ -339,27 +338,32 @@ export default {
!this.keepColor
)
- const theme = {}
+ const source = {
+ themeEngineVersion: CURRENT_VERSION
+ }
if (this.keepFonts || saveEverything) {
- theme.fonts = this.fontsLocal
+ source.fonts = this.fontsLocal
}
if (this.keepShadows || saveEverything) {
- theme.shadows = this.shadowsLocal
+ source.shadows = this.shadowsLocal
}
if (this.keepOpacity || saveEverything) {
- theme.opacity = this.currentOpacity
+ source.opacity = this.currentOpacity
}
if (this.keepColor || saveEverything) {
- theme.colors = this.currentColors
+ source.colors = this.currentColors
}
if (this.keepRoundness || saveEverything) {
- theme.radii = this.currentRadii
+ source.radii = this.currentRadii
}
+ const theme = this.previewTheme
+
+ console.log(source)
return {
- // To separate from other random JSON files and possible future theme formats
- _pleroma_theme_version: 2, theme: merge(theme, this.currentCompat)
+ // To separate from other random JSON files and possible future source formats
+ _pleroma_theme_version: 2, theme, source
}
}
},
@@ -392,7 +396,7 @@ export default {
if (parsed._pleroma_theme_version === 1) {
this.normalizeLocalState(parsed, 1)
} else if (parsed._pleroma_theme_version >= 2) {
- this.normalizeLocalState(parsed.theme, 2)
+ this.normalizeLocalState(parsed.theme, 2, parsed.source)
}
},
importValidator (parsed) {
@@ -402,7 +406,7 @@ export default {
clearAll () {
const state = this.$store.getters.mergedConfig.customTheme
const version = state.colors ? 2 : 'l1'
- this.normalizeLocalState(this.$store.getters.mergedConfig.customTheme, version)
+ this.normalizeLocalState(this.$store.getters.mergedConfig.customTheme, version, this.$store.getters.mergedConfig.customThemeSource)
},
// Clears all the extra stuff when loading V1 theme
@@ -441,24 +445,30 @@ export default {
/**
* This applies stored theme data onto form. Supports three versions of data:
- * v3 (version = 3) - same as 2 but with some incompatible changes
+ * v3 (version >= 3) - newest version of themes which supports snapshots for better compatiblity
* v2 (version = 2) - newer version of themes.
* v1 (version = 1) - older version of themes (import from file)
* v1l (version = l1) - older version of theme (load from local storage)
* v1 and v1l differ because of way themes were stored/exported.
- * @param {Object} input - input data
+ * @param {Object} theme - theme data (snapshot)
* @param {Number} version - version of data. 0 means try to guess based on data. "l1" means v1, locastorage type
+ * @param {Object} source - theme source - this will be used if compatible
+ * @param {Boolean} source - by default source won't be used if version doesn't match since it might render differently
+ * this allows importing source anyway
*/
- normalizeLocalState (originalInput, version = 0) {
+ normalizeLocalState (theme, version = 0, source, forceSource = false) {
let input
- if (typeof originalInput.v3compat !== 'undefined') {
- version = 3
- input = merge(originalInput, originalInput.v3compat)
+ if (typeof source !== 'undefined') {
+ if (forceSource || source.themeEngineVersion === CURRENT_VERSION) {
+ input = source
+ version = source.themeEngineVersion
+ } else {
+ input = theme
+ }
} else {
- input = originalInput
+ input = theme
}
- const compat = input.v3compat
const radii = input.radii || input
const opacity = input.opacity
const shadows = input.shadows || {}
@@ -615,7 +625,7 @@ export default {
this.cOrangeColorLocal = this.selected[8]
}
} else if (this.selectedVersion >= 2) {
- this.normalizeLocalState(this.selected.theme, 2)
+ this.normalizeLocalState(this.selected.theme, 2, this.selected.source)
}
}
}
diff --git a/src/components/style_switcher/style_switcher.vue b/src/components/style_switcher/style_switcher.vue
index 38ca2017a0..2eadbe250c 100644
--- a/src/components/style_switcher/style_switcher.vue
+++ b/src/components/style_switcher/style_switcher.vue
@@ -106,7 +106,7 @@
@@ -363,7 +363,7 @@
@@ -377,7 +377,7 @@
diff --git a/src/services/style_setter/style_setter.js b/src/services/style_setter/style_setter.js
index df22f94f95..e8a645172b 100644
--- a/src/services/style_setter/style_setter.js
+++ b/src/services/style_setter/style_setter.js
@@ -2,6 +2,8 @@ import { times } from 'lodash'
import { brightness, invertLightness, convert, contrastRatio } from 'chromatism'
import { rgb2hex, hex2rgb, mixrgb, getContrastRatio, alphaBlend, alphaBlendLayers } from '../color_convert/color_convert.js'
+export const CURRENT_VERSION = 3
+
// While this is not used anymore right now, I left it in if we want to do custom
// styles that aren't just colors, so user can pick from a few different distinct
// styles as well as set their own colors in the future.
@@ -150,29 +152,13 @@ const getCssColor = (input, a) => {
return rgb2rgba({ ...rgb, a })
}
-// Generates a "patch" for theme to make it compatible with v2
-export const generateCompat = (input) => {
- const { colors } = input
- const v3compat = {
- colors: {}
- }
- const v2colorsPatch = {}
-
- // # Link became optional in v3
- if (typeof colors.link === 'undefined') {
- v2colorsPatch.link = colors.accent
- v3compat.colors.link = null
- }
-
- return {
- v3compat,
- colors: v2colorsPatch
- }
-}
-
const generateColors = (themeData) => {
const colors = {}
const rawOpacity = Object.assign({
+ panel: 1,
+ btn: 1,
+ border: 1,
+ bg: 1,
alert: 0.5,
input: 0.5,
faint: 0.5,
@@ -207,6 +193,7 @@ const generateColors = (themeData) => {
} else {
let value = v
if (v === 'transparent') {
+ // TODO: hack to keep rest of the code from complaining
value = '#FF00FF'
}
acc[k] = hex2rgb(value)
@@ -221,7 +208,7 @@ const generateColors = (themeData) => {
const isLightOnDark = convert(colors.bg).hsl.l < convert(colors.text).hsl.l
const mod = isLightOnDark ? 1 : -1
- colors.lightText = brightness(20 * mod, colors.text).rgb
+ colors.lightText = col.lightText || brightness(20 * mod, colors.text).rgb
colors.accent = col.accent || col.link
colors.link = col.link || col.accent
@@ -231,7 +218,8 @@ const generateColors = (themeData) => {
colors.lightBg = col.lightBg || brightness(5 * mod, colors.bg).rgb
const underlay = [colors.underlay, opacity.underlay]
- const fg = [col.fg, opacity.fg]
+ // Technically, foreground can't be transparent (descendants can) but let's keep it just in case
+ const fg = [col.fg, opacity.fg || 1]
const bg = [col.bg, opacity.bg]
colors.fg = col.fg
@@ -271,16 +259,16 @@ const generateColors = (themeData) => {
colors.alertError = col.alertError || Object.assign({}, colors.cRed)
const alertError = [colors.alertError, opacity.alert]
- colors.alertErrorText = getTextColor(alphaBlendLayers(colors.text, [underlay, bg, alertError]), colors.text)
- colors.alertErrorPanelText = getTextColor(alphaBlendLayers(colors.panelText, [underlay, bg, panel, panel, alertError]), colors.panelText)
+ colors.alertErrorText = col.alertErrorText || getTextColor(alphaBlendLayers(colors.text, [underlay, bg, alertError]), colors.text)
+ colors.alertErrorPanelText = col.alertErrorPanelText || getTextColor(alphaBlendLayers(colors.panelText, [underlay, bg, panel, panel, alertError]), colors.panelText)
colors.alertWarning = col.alertWarning || Object.assign({}, colors.cOrange)
const alertWarning = [colors.alertWarning, opacity.alert]
- colors.alertWarningText = getTextColor(alphaBlendLayers(colors.text, [underlay, bg, alertWarning]), colors.text)
- colors.alertWarningPanelText = getTextColor(alphaBlendLayers(colors.panelText, [underlay, bg, panel, panel, alertWarning]), colors.panelText)
+ colors.alertWarningText = col.alertWarningText || getTextColor(alphaBlendLayers(colors.text, [underlay, bg, alertWarning]), colors.text)
+ colors.alertWarningPanelText = col.alertWarningPanelText || getTextColor(alphaBlendLayers(colors.panelText, [underlay, bg, panel, panel, alertWarning]), colors.panelText)
colors.badgeNotification = col.badgeNotification || Object.assign({}, colors.cRed)
- colors.badgeNotificationText = contrastRatio(colors.badgeNotification).rgb
+ colors.badgeNotificationText = colors.badgeNotificationText || contrastRatio(colors.badgeNotification).rgb
Object.entries(opacity).forEach(([ k, v ]) => {
console.log(k)
diff --git a/static/themes/breezy-dark.json b/static/themes/breezy-dark.json
index 0ed55184fe..d447005fff 100644
--- a/static/themes/breezy-dark.json
+++ b/static/themes/breezy-dark.json
@@ -2,6 +2,218 @@
"_pleroma_theme_version": 2,
"name": "Breezy Dark (beta)",
"theme": {
+ "shadows": {
+ "panel": [
+ {
+ "x": "1",
+ "y": "2",
+ "blur": "6",
+ "spread": 0,
+ "color": "#000000",
+ "alpha": 0.6
+ }
+ ],
+ "topBar": [
+ {
+ "x": 0,
+ "y": 0,
+ "blur": 4,
+ "spread": 0,
+ "color": "#000000",
+ "alpha": 0.6
+ }
+ ],
+ "popup": [
+ {
+ "x": 2,
+ "y": 2,
+ "blur": 3,
+ "spread": 0,
+ "color": "#000000",
+ "alpha": 0.5
+ }
+ ],
+ "avatar": [
+ {
+ "x": 0,
+ "y": 1,
+ "blur": 8,
+ "spread": 0,
+ "color": "#000000",
+ "alpha": 0.7
+ }
+ ],
+ "avatarStatus": [],
+ "panelHeader": [
+ {
+ "x": 0,
+ "y": "40",
+ "blur": "40",
+ "spread": "-40",
+ "inset": true,
+ "color": "#ffffff",
+ "alpha": "0.1"
+ }
+ ],
+ "button": [
+ {
+ "x": 0,
+ "y": "0",
+ "blur": "0",
+ "spread": "1",
+ "color": "#ffffff",
+ "alpha": "0.15",
+ "inset": true
+ },
+ {
+ "x": "1",
+ "y": "1",
+ "blur": "1",
+ "spread": 0,
+ "color": "#000000",
+ "alpha": "0.3",
+ "inset": false
+ }
+ ],
+ "buttonHover": [
+ {
+ "x": 0,
+ "y": "0",
+ "blur": 0,
+ "spread": "1",
+ "color": "--accent",
+ "alpha": "0.3",
+ "inset": true
+ },
+ {
+ "x": "1",
+ "y": "1",
+ "blur": "1",
+ "spread": 0,
+ "color": "#000000",
+ "alpha": "0.3",
+ "inset": false
+ }
+ ],
+ "buttonPressed": [
+ {
+ "x": 0,
+ "y": 0,
+ "blur": "0",
+ "spread": "50",
+ "color": "--faint",
+ "alpha": 1,
+ "inset": true
+ },
+ {
+ "x": 0,
+ "y": "0",
+ "blur": 0,
+ "spread": "1",
+ "color": "#ffffff",
+ "alpha": 0.2,
+ "inset": true
+ },
+ {
+ "x": "1",
+ "y": "1",
+ "blur": 0,
+ "spread": 0,
+ "color": "#000000",
+ "alpha": "0.3",
+ "inset": false
+ }
+ ],
+ "input": [
+ {
+ "x": 0,
+ "y": "0",
+ "blur": 0,
+ "spread": "1",
+ "color": "#FFFFFF",
+ "alpha": "0.2",
+ "inset": true
+ }
+ ]
+ },
+ "colors": {
+ "bg": "#31363b",
+ "underlay": "#000000",
+ "text": "#eff0f1",
+ "lightText": "#ffffff",
+ "accent": "#3daee9",
+ "link": "#3daee9",
+ "faint": "#eff0f1",
+ "lightBg": "#3d4349",
+ "fg": "#31363b",
+ "fgText": "#eff0f1",
+ "fgLink": "#3daee9",
+ "border": "#4c545b",
+ "btn": "#31363b",
+ "btnText": "#eff0f1",
+ "input": "#232629",
+ "inputText": "#ffffff",
+ "panel": "#ff00ff",
+ "panelText": "#eff0f1",
+ "panelLink": "#3daee9",
+ "panelFaint": "#eff0f1",
+ "topBar": "#31363b",
+ "topBarText": "#eff0f1",
+ "topBarLink": "#eff0f1",
+ "faintLink": "#3daee9",
+ "linkBg": "#366681",
+ "icon": "#909396",
+ "cBlue": "#3daee9",
+ "cRed": "#da4453",
+ "cGreen": "#27ae60",
+ "cOrange": "#f67400",
+ "alertError": "#da4453",
+ "alertErrorText": "#eff0f1",
+ "alertErrorPanelText": "#eff0f1",
+ "alertWarning": "#f67400",
+ "alertWarningText": "#eff0f1",
+ "alertWarningPanelText": "#eff0f1",
+ "badgeNotification": "#da4453",
+ "badgeNotificationText": "#ffffff"
+ },
+ "opacity": {
+ "panel": 0,
+ "btn": 1,
+ "border": 1,
+ "bg": 1,
+ "alert": 0.5,
+ "input": 0.5,
+ "faint": 0.5,
+ "underlay": 0.15
+ },
+ "radii": {
+ "btn": "2",
+ "input": "2",
+ "checkbox": "1",
+ "panel": "2",
+ "avatar": "2",
+ "avatarAlt": "2",
+ "tooltip": "2",
+ "attachment": "2"
+ },
+ "fonts": {
+ "interface": {
+ "family": "sans-serif"
+ },
+ "input": {
+ "family": "inherit"
+ },
+ "post": {
+ "family": "inherit"
+ },
+ "postCode": {
+ "family": "monospace"
+ }
+ }
+ },
+ "source": {
+ "themeEngineVersion": 3,
+ "fonts": {},
"shadows": {
"panel": [
{
@@ -105,21 +317,13 @@
}
]
},
- "fonts": {},
- "opacity": {
- "input": "1"
- },
- "v3compat": {
- "colors": {
- "panel": "transparent"
- }
- },
+ "opacity": {},
"colors": {
"bg": "#31363b",
"text": "#eff0f1",
"link": "#3daee9",
"fg": "#31363b",
- "panel": "#31363b",
+ "panel": "transparent",
"input": "#232629",
"topBarLink": "#eff0f1",
"btn": "#31363b",
diff --git a/static/themes/breezy-light.json b/static/themes/breezy-light.json
index 5db185dd11..243b859397 100644
--- a/static/themes/breezy-light.json
+++ b/static/themes/breezy-light.json
@@ -2,6 +2,218 @@
"_pleroma_theme_version": 2,
"name": "Breezy Light (beta)",
"theme": {
+ "shadows": {
+ "panel": [
+ {
+ "x": "1",
+ "y": "2",
+ "blur": "6",
+ "spread": 0,
+ "color": "#000000",
+ "alpha": 0.6
+ }
+ ],
+ "topBar": [
+ {
+ "x": 0,
+ "y": 0,
+ "blur": 4,
+ "spread": 0,
+ "color": "#000000",
+ "alpha": 0.6
+ }
+ ],
+ "popup": [
+ {
+ "x": 2,
+ "y": 2,
+ "blur": 3,
+ "spread": 0,
+ "color": "#000000",
+ "alpha": 0.5
+ }
+ ],
+ "avatar": [
+ {
+ "x": 0,
+ "y": 1,
+ "blur": 8,
+ "spread": 0,
+ "color": "#000000",
+ "alpha": 0.7
+ }
+ ],
+ "avatarStatus": [],
+ "panelHeader": [
+ {
+ "x": 0,
+ "y": "40",
+ "blur": "40",
+ "spread": "-40",
+ "inset": true,
+ "color": "#ffffff",
+ "alpha": "0.1"
+ }
+ ],
+ "button": [
+ {
+ "x": 0,
+ "y": "0",
+ "blur": "0",
+ "spread": "1",
+ "color": "#000000",
+ "alpha": "0.3",
+ "inset": true
+ },
+ {
+ "x": "1",
+ "y": "1",
+ "blur": "1",
+ "spread": 0,
+ "color": "#000000",
+ "alpha": "0.3",
+ "inset": false
+ }
+ ],
+ "buttonHover": [
+ {
+ "x": 0,
+ "y": "0",
+ "blur": 0,
+ "spread": "1",
+ "color": "--accent",
+ "alpha": "0.3",
+ "inset": true
+ },
+ {
+ "x": "1",
+ "y": "1",
+ "blur": "1",
+ "spread": 0,
+ "color": "#000000",
+ "alpha": "0.3",
+ "inset": false
+ }
+ ],
+ "buttonPressed": [
+ {
+ "x": 0,
+ "y": 0,
+ "blur": "0",
+ "spread": "50",
+ "color": "--faint",
+ "alpha": 1,
+ "inset": true
+ },
+ {
+ "x": 0,
+ "y": "0",
+ "blur": 0,
+ "spread": "1",
+ "color": "#ffffff",
+ "alpha": 0.2,
+ "inset": true
+ },
+ {
+ "x": "1",
+ "y": "1",
+ "blur": 0,
+ "spread": 0,
+ "color": "#000000",
+ "alpha": "0.3",
+ "inset": false
+ }
+ ],
+ "input": [
+ {
+ "x": 0,
+ "y": "0",
+ "blur": 0,
+ "spread": "1",
+ "color": "#000000",
+ "alpha": "0.2",
+ "inset": true
+ }
+ ]
+ },
+ "colors": {
+ "bg": "#eff0f1",
+ "underlay": "#000000",
+ "text": "#232627",
+ "lightText": "#000000",
+ "accent": "#2980b9",
+ "link": "#2980b9",
+ "faint": "#232627",
+ "lightBg": "#e2e4e6",
+ "fg": "#bcc2c7",
+ "fgText": "#232627",
+ "fgLink": "#2980b9",
+ "border": "#b7bdc3",
+ "btn": "#eff0f1",
+ "btnText": "#232627",
+ "input": "#fcfcfc",
+ "inputText": "#000000",
+ "panel": "#475057",
+ "panelText": "#fcfcfc",
+ "panelLink": "#ffffff",
+ "panelFaint": "#d8dbdc",
+ "topBar": "#475057",
+ "topBarText": "#d8dbdc",
+ "topBarLink": "#eff0f1",
+ "faintLink": "#2980b9",
+ "linkBg": "#a0c4db",
+ "icon": "#898b8c",
+ "cBlue": "#2980b9",
+ "cRed": "#da4453",
+ "cGreen": "#27ae60",
+ "cOrange": "#f67400",
+ "alertError": "#da4453",
+ "alertErrorText": "#232627",
+ "alertErrorPanelText": "#fcfcfc",
+ "alertWarning": "#f67400",
+ "alertWarningText": "#232627",
+ "alertWarningPanelText": "#fcfcfc",
+ "badgeNotification": "#da4453",
+ "badgeNotificationText": "#ffffff"
+ },
+ "opacity": {
+ "panel": 1,
+ "btn": 1,
+ "border": 1,
+ "bg": 1,
+ "alert": 0.5,
+ "input": "1",
+ "faint": 0.5,
+ "underlay": 0.15
+ },
+ "radii": {
+ "btn": "2",
+ "input": "2",
+ "checkbox": "1",
+ "panel": "2",
+ "avatar": "2",
+ "avatarAlt": "2",
+ "tooltip": "2",
+ "attachment": "2"
+ },
+ "fonts": {
+ "interface": {
+ "family": "sans-serif"
+ },
+ "input": {
+ "family": "inherit"
+ },
+ "post": {
+ "family": "inherit"
+ },
+ "postCode": {
+ "family": "monospace"
+ }
+ }
+ },
+ "source": {
+ "themeEngineVersion": 3,
+ "fonts": {},
"shadows": {
"panel": [
{
@@ -105,20 +317,14 @@
}
]
},
- "fonts": {},
"opacity": {
"input": "1"
},
- "v3compat": {
- "colors": {
- "panel": "transparent"
- }
- },
"colors": {
"bg": "#eff0f1",
"text": "#232627",
- "link": "#2980b9",
"fg": "#bcc2c7",
+ "accent": "#2980b9",
"panel": "#475057",
"panelText": "#fcfcfc",
"input": "#fcfcfc",