From 81d9537f9d00ab18cab4305344d7563fb6418ddd Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Thu, 3 Oct 2024 23:03:33 +0300 Subject: [PATCH 1/7] show warning about palettes being unsupported when using v2 theme --- .../settings_modal/tabs/appearance_tab.js | 18 +++++--- .../settings_modal/tabs/appearance_tab.scss | 4 ++ .../settings_modal/tabs/appearance_tab.vue | 45 +++++++++++-------- src/i18n/en.json | 3 +- src/modules/interface.js | 5 ++- 5 files changed, 49 insertions(+), 26 deletions(-) diff --git a/src/components/settings_modal/tabs/appearance_tab.js b/src/components/settings_modal/tabs/appearance_tab.js index 1e48c7e88..bba4647c6 100644 --- a/src/components/settings_modal/tabs/appearance_tab.js +++ b/src/components/settings_modal/tabs/appearance_tab.js @@ -34,7 +34,7 @@ const AppearanceTab = { return { availableStyles: [], availablePalettes: [], - themeImporter: newImporter({ + fileImporter: newImporter({ accept: '.json, .piss', validator: this.importValidator, onImport: this.onImport, @@ -179,6 +179,10 @@ const AppearanceTab = { this.$store.dispatch('setOption', { name: 'interfaceLanguage', value: val }) } }, + customThemeVersion () { + const { themeVersion } = this.$store.state.interface + return themeVersion + }, isCustomThemeUsed () { const { theme } = this.mergedConfig return theme === 'custom' @@ -202,8 +206,8 @@ const AppearanceTab = { } }) }, - importTheme () { - this.themeImporter.importData() + importFile () { + this.fileImporter.importData() }, onImport (parsed, filename) { if (filename.endsWith('.json')) { @@ -234,14 +238,18 @@ const AppearanceTab = { const { palette } = this.mergedConfig return key === palette }, - importStyle () { - + setStyle (name) { + this.$store.dispatch('resetThemeV2') + this.$store.dispatch('setTheme', name) + this.$store.dispatch('applyTheme') }, setTheme (name) { + this.$store.dispatch('resetThemeV3') this.$store.dispatch('setTheme', name) this.$store.dispatch('applyTheme') }, setPalette (name) { + this.$store.dispatch('resetThemeV2') this.$store.dispatch('setPalette', name) this.$store.dispatch('applyTheme') }, diff --git a/src/components/settings_modal/tabs/appearance_tab.scss b/src/components/settings_modal/tabs/appearance_tab.scss index 77d668ecb..b95ef07db 100644 --- a/src/components/settings_modal/tabs/appearance_tab.scss +++ b/src/components/settings_modal/tabs/appearance_tab.scss @@ -24,6 +24,10 @@ display: grid; grid-template-columns: 1fr 1fr; grid-gap: 0.5em; + + .unsupported-theme-v2 { + grid-column: 1 / span 2; + } } .palette-entry { diff --git a/src/components/settings_modal/tabs/appearance_tab.vue b/src/components/settings_modal/tabs/appearance_tab.vue index d27f43fe1..a65c52310 100644 --- a/src/components/settings_modal/tabs/appearance_tab.vue +++ b/src/components/settings_modal/tabs/appearance_tab.vue @@ -7,7 +7,7 @@

{{ $t('settings.theme') }}

+ +
diff --git a/src/i18n/en.json b/src/i18n/en.json index 0da8d4f00..603bc4910 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -771,7 +771,8 @@ "cOrange": "Orange color", "extra1": "Extra 1", "extra2": "Extra 2", - "extra3": "Extra 3" + "extra3": "Extra 3", + "v2_unsupported": "Older v2 themes don't support palettes. Switch to v3 theme to make use of palettes", }, "editor": { "title": "Style", diff --git a/src/modules/interface.js b/src/modules/interface.js index 96d23cbcd..6daa4a1b2 100644 --- a/src/modules/interface.js +++ b/src/modules/interface.js @@ -5,6 +5,7 @@ import { convertTheme2To3 } from 'src/services/theme_data/theme2_to_theme3.js' const defaultState = { localFonts: null, themeApplied: false, + themeVersion: 'v3', temporaryChangesTimeoutId: null, // used for temporary options that revert after a timeout temporaryChangesConfirm: () => {}, // used for applying temporary options temporaryChangesRevert: () => {}, // used for reverting temporary options @@ -307,7 +308,7 @@ const interfaceMod = { commit('setOption', { name: 'customThemeSource', value: null }) }, async applyTheme ( - { dispatch, commit, rootState }, + { dispatch, commit, rootState, state }, { recompile = true } = {} ) { // If we're not not forced to recompile try using @@ -398,6 +399,8 @@ const interfaceMod = { majorVersionUsed = 'v3' } + state.themeVersion = majorVersionUsed + let styleDataUsed = null let styleNameUsed = null let paletteDataUsed = null From 9e3e4ed429e11594ffeccbe8000afeaebe066cb0 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Thu, 3 Oct 2024 23:03:56 +0300 Subject: [PATCH 2/7] rearrange palettes so that Pleroma-dark is default (first) and bird is next to Pleroma-light in UI --- static/palettes/index.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/static/palettes/index.json b/static/palettes/index.json index acb4e4d98..63fbad5a5 100644 --- a/static/palettes/index.json +++ b/static/palettes/index.json @@ -1,6 +1,6 @@ { - "pleroma-light": [ "Pleroma Light", "#f2f4f6", "#dbe0e8", "#304055", "#f86f0f", "#d31014", "#0fa00f", "#0095ff", "#ffa500" ], "pleroma-dark": [ "Pleroma Dark", "#121a24", "#182230", "#b9b9ba", "#d8a070", "#d31014", "#0fa00f", "#0095ff", "#ffa500" ], + "pleroma-light": [ "Pleroma Light", "#f2f4f6", "#dbe0e8", "#304055", "#f86f0f", "#d31014", "#0fa00f", "#0095ff", "#ffa500" ], "classic-dark": { "name": "Classic Dark", "background": "#161c20", @@ -12,6 +12,7 @@ "cBlue": "#0095ff", "cOrange": "#ffa500" }, + "bird": [ "Bird", "#f8fafd", "#e6ecf0", "#14171a", "#0084b8", "#e0245e", "#17bf63", "#1b95e0", "#fab81e"], "pleroma-amoled": [ "Pleroma Dark AMOLED", "#000000", "#111111", "#b0b0b1", "#d8a070", "#aa0000", "#0fa00f", "#0095ff", "#d59500"], "tomorrow-night": { "name": "Tomorrow Night", @@ -26,7 +27,6 @@ "_cYellow": "#f0c674", "_cPurple": "#b294bb" }, - "bird": [ "Bird", "#f8fafd", "#e6ecf0", "#14171a", "#0084b8", "#e0245e", "#17bf63", "#1b95e0", "#fab81e"], "ir-black": [ "Ir Black", "#000000", "#242422", "#b5b3aa", "#ff6c60", "#FF6C60", "#A8FF60", "#96CBFE", "#FFFFB6" ], "monokai": [ "Monokai", "#272822", "#383830", "#f8f8f2", "#f92672", "#F92672", "#a6e22e", "#66d9ef", "#f4bf75" ] } From c937736feab589fb123b8208792d5f12e3a056de Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Fri, 4 Oct 2024 00:27:53 +0300 Subject: [PATCH 3/7] shadow editor now can handle expressions (functions and variables) --- .../tabs/style_tab/style_tab.js | 37 +- .../tabs/style_tab/style_tab.vue | 2 +- .../shadow_control/shadow_control.js | 80 ++-- .../shadow_control/shadow_control.scss | 10 +- .../shadow_control/shadow_control.vue | 353 ++++++++++-------- src/i18n/en.json | 3 + .../theme_data/theme_data_3.service.js | 7 +- 7 files changed, 285 insertions(+), 207 deletions(-) diff --git a/src/components/settings_modal/tabs/style_tab/style_tab.js b/src/components/settings_modal/tabs/style_tab/style_tab.js index ecddf9d56..cbd25dfc2 100644 --- a/src/components/settings_modal/tabs/style_tab/style_tab.js +++ b/src/components/settings_modal/tabs/style_tab/style_tab.js @@ -219,9 +219,13 @@ export default { return selectors.map(x => x.substring(1)).join('') }) const previewCss = computed(() => { - const scoped = getCssRules(previewRules) - .map(simulatePseudoSelectors) - return scoped.join('\n') + try { + const scoped = getCssRules(previewRules).map(simulatePseudoSelectors) + return scoped.join('\n') + } catch (e) { + console.error('Invalid ruleset', e) + return null + } }) // ### Rules stuff aka meat and potatoes @@ -415,17 +419,22 @@ export default { }) const updatePreview = () => { - previewRules.splice(0, previewRules.length) - previewRules.push(...init({ - inputRuleset: editorFriendlyToOriginal.value, - initialStaticVars: { - ...palette.value - }, - ultimateBackgroundColor: '#000000', - rootComponentName: selectedComponentName.value, - editMode: true, - debug: true - }).eager) + try { + const rules = init({ + inputRuleset: editorFriendlyToOriginal.value, + initialStaticVars: { + ...palette.value + }, + ultimateBackgroundColor: '#000000', + rootComponentName: selectedComponentName.value, + editMode: true, + debug: true + }).eager + previewRules.splice(0, previewRules.length) + previewRules.push(...rules) + } catch (e) { + console.error('Could not compile preview theme', e) + } } const updateSelectedComponent = () => { diff --git a/src/components/settings_modal/tabs/style_tab/style_tab.vue b/src/components/settings_modal/tabs/style_tab/style_tab.vue index cd9d3e474..590624ffd 100644 --- a/src/components/settings_modal/tabs/style_tab/style_tab.vue +++ b/src/components/settings_modal/tabs/style_tab/style_tab.vue @@ -125,7 +125,7 @@ :shadow-control="isShadowTabOpen" :preview-class="previewClass" :preview-style="editorHintStyle" - :disabled="!editedSubShadow" + :disabled="!editedSubShadow && typeof editedShadow !== 'string'" :shadow="editedSubShadow" @update:shadow="({ axis, value }) => updateSubShadow(axis, value)" /> diff --git a/src/components/shadow_control/shadow_control.js b/src/components/shadow_control/shadow_control.js index 4521305ea..2befb8ba2 100644 --- a/src/components/shadow_control/shadow_control.js +++ b/src/components/shadow_control/shadow_control.js @@ -21,16 +21,22 @@ library.add( faPlus ) -const toModel = (object = {}) => ({ - x: 0, - y: 0, - blur: 0, - spread: 0, - inset: false, - color: '#000000', - alpha: 1, - ...object -}) +const toModel = (input) => { + if (typeof input === 'object') { + return { + x: 0, + y: 0, + blur: 0, + spread: 0, + inset: false, + color: '#000000', + alpha: 1, + ...input + } + } else if (typeof input === 'string') { + return input + } +} export default { props: [ @@ -56,12 +62,29 @@ export default { this.cValue = (this.modelValue ?? this.fallback ?? []).map(toModel) }, computed: { - selected () { - const selected = this.cValue[this.selectedId] - if (selected) { - return { ...selected } + selectedType: { + get () { + return typeof this.selected + }, + set (newType) { + this.selected = toModel(newType === 'object' ? {} : '') + } + }, + selected: { + get () { + const selected = this.cValue[this.selectedId] + console.log('SELECTED', selected) + if (selected && typeof selected === 'object') { + return { ...selected } + } else if (typeof selected === 'string') { + return selected + } + return null + }, + set (value) { + this.cValue[this.selectedId] = toModel(value) + this.$emit('update:modelValue', this.cValue) } - return null }, present () { return this.selected != null && !this.usingFallback @@ -82,14 +105,20 @@ export default { return this.modelValue == null }, style () { - if (this.separateInset) { - return { - filter: getCssShadowFilter(this.cValue), - boxShadow: getCssShadow(this.cValue, true) + try { + if (this.separateInset) { + return { + filter: getCssShadowFilter(this.cValue), + boxShadow: getCssShadow(this.cValue, true) + } + } + return { + boxShadow: getCssShadow(this.cValue) + } + } catch (e) { + return { + border: '1px solid red' } - } - return { - boxShadow: getCssShadow(this.cValue) } } }, @@ -99,6 +128,13 @@ export default { } }, methods: { + getSubshadowLabel (shadow, index) { + if (typeof shadow === 'object') { + return shadow?.name ?? this.$t('settings.style.shadows.shadow_id', { value: index }) + } else if (typeof shadow === 'string') { + return shadow || this.$t('settings.style.shadows.empty_expression') + } + }, updateProperty: throttle(function (prop, value) { this.cValue[this.selectedId][prop] = value if (prop === 'inset' && value === false && this.separateInset) { diff --git a/src/components/shadow_control/shadow_control.scss b/src/components/shadow_control/shadow_control.scss index dd0490235..067895eee 100644 --- a/src/components/shadow_control/shadow_control.scss +++ b/src/components/shadow_control/shadow_control.scss @@ -4,6 +4,7 @@ justify-content: stretch; grid-gap: 0.25em; margin-bottom: 1em; + width: 100%; .shadow-switcher { order: 1; @@ -37,6 +38,9 @@ min-width: 10em; margin-left: 0.125em; margin-right: 0.125em; + display: grid; + grid-template-rows: auto 1fr; + grid-gap: 0.25em; /* hack */ .input-boolean { @@ -52,6 +56,11 @@ flex: 1 0 5em; } + .shadow-expression { + width: 100%; + height: 100%; + } + .id-control { align-items: stretch; @@ -100,6 +109,5 @@ } .inset-tooltip { - padding: 0.5em; max-width: 30em; } diff --git a/src/components/shadow_control/shadow_control.vue b/src/components/shadow_control/shadow_control.vue index e1d201914..669de36e1 100644 --- a/src/components/shadow_control/shadow_control.vue +++ b/src/components/shadow_control/shadow_control.vue @@ -8,7 +8,6 @@ class="shadow-preview" :shadow-control="true" :shadow="selected" - :preview-style="style" :disabled="disabled || !present" @update:shadow="({ axis, value }) => updateProperty(axis, value)" /> @@ -18,7 +17,7 @@ v-model="selectedId" class="shadow-list" size="10" - :disabled="shadowsAreNull" + :disabled="disabled || shadowsAreNull" >
-
-
diff --git a/src/i18n/en.json b/src/i18n/en.json index 64d6201df..df0f8d6db 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -966,6 +966,9 @@ "blur": "Blur", "spread": "Spread", "inset": "Inset", + "raw": "Plain shadow", + "expression": "Expression (advanced)", + "empty_expression": "Empty expression", "hintV3": "For shadows you can also use the {0} notation to use other color slot.", "filter_hint": { "always_drop_shadow": "Warning, this shadow always uses {0} when browser supports it.", diff --git a/src/services/theme_data/theme_data_3.service.js b/src/services/theme_data/theme_data_3.service.js index 4765a7733..e45d2cefd 100644 --- a/src/services/theme_data/theme_data_3.service.js +++ b/src/services/theme_data/theme_data_3.service.js @@ -44,8 +44,8 @@ const findShadow = (shadows, { dynamicVars, staticVars }) => { if (shadow.startsWith('$')) { targetShadow = process(shadow, shadowFunctions, { findColor, findShadow }, { dynamicVars, staticVars }) } else if (shadow.startsWith('--')) { - const [variable] = shadow.split(/,/g).map(str => str.trim()) // discarding modifier since it's not supported - const variableSlot = variable.substring(2) + // modifiers are completely unsupported here + const variableSlot = shadow.substring(2) return findShadow(staticVars[variableSlot], { dynamicVars, staticVars }) } else { targetShadow = parseShadow(shadow) @@ -66,6 +66,7 @@ const findColor = (color, { dynamicVars, staticVars }) => { if (typeof color !== 'string' || (!color.startsWith('--') && !color.startsWith('$'))) return color let targetColor = null if (color.startsWith('--')) { + // Modifier support is pretty much for v2 themes only const [variable, modifier] = color.split(/,/g).map(str => str.trim()) const variableSlot = variable.substring(2) if (variableSlot === 'stack') { @@ -421,7 +422,7 @@ export const init = ({ break } case 'shadow': { - const shadow = value.split(/,/g).map(s => s.trim()) + const shadow = value.split(/,/g).map(s => s.trim()).filter(x => x) dynamicVars[k] = shadow if (combination.component === rootComponentName) { staticVars[k.substring(2)] = shadow From 3d77860e57892569cfe98bccfdc65d3a9c95dbf6 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Fri, 4 Oct 2024 02:49:20 +0300 Subject: [PATCH 4/7] moved the select motion stuff into its own component --- src/components/select/select_motion.vue | 115 +++++ .../tabs/style_tab/style_tab.vue | 432 +++++++++--------- .../shadow_control/shadow_control.js | 45 +- .../shadow_control/shadow_control.vue | 55 +-- 4 files changed, 356 insertions(+), 291 deletions(-) create mode 100644 src/components/select/select_motion.vue diff --git a/src/components/select/select_motion.vue b/src/components/select/select_motion.vue new file mode 100644 index 000000000..1b9f4041a --- /dev/null +++ b/src/components/select/select_motion.vue @@ -0,0 +1,115 @@ + + + + + diff --git a/src/components/settings_modal/tabs/style_tab/style_tab.vue b/src/components/settings_modal/tabs/style_tab/style_tab.vue index 590624ffd..7026f7afb 100644 --- a/src/components/settings_modal/tabs/style_tab/style_tab.vue +++ b/src/components/settings_modal/tabs/style_tab/style_tab.vue @@ -51,231 +51,241 @@
-
-
- - -
+
- - - {{ fallbackI18n($t(getVariantPath(selectedComponentName, variant)), variant) }} - - -
-
- -
    + {{ fallbackI18n($t(getFriendlyNamePath(componentsMap.get(key).name)), componentsMap.get(key).name) }} + + +
+
-
  • + {{ $t('settings.style.themes3.editor.variant_selector') }} + + +
  • +
    + +
      +
    • + + {{ fallbackI18n($t(getStatePath(selectedComponentName, state)), state) }} + +
    • +
    +
    +
    + + + + +
    + +
    + + + + + + + + + + + + +
    + + +
    + + + +
    + +
    +
    + +
    + + + + + + + + +
    +
    - {{ fallbackI18n($t(getStatePath(selectedComponentName, state)), state) }} - - - + {{ $t('settings.style.themes3.editor.include_in_rule') }} + + +
    +
    -
    - - - - -
    - -
    - - - - - - - - - - + + + - - - -
    - - - -
    - -
    -
    - -
    - - - - - - - - + {{ $t('settings.style.themes3.palette.light') }} + + -
    - - {{ $t('settings.style.themes3.editor.include_in_rule') }} - - -
    -
    - -
    -
    - - +
    - -
    + diff --git a/src/components/shadow_control/shadow_control.js b/src/components/shadow_control/shadow_control.js index 2befb8ba2..fc227b5be 100644 --- a/src/components/shadow_control/shadow_control.js +++ b/src/components/shadow_control/shadow_control.js @@ -1,6 +1,7 @@ import ColorInput from 'src/components/color_input/color_input.vue' import OpacityInput from 'src/components/opacity_input/opacity_input.vue' import Select from 'src/components/select/select.vue' +import SelectMotion from 'src/components/select/select_motion.vue' import Checkbox from 'src/components/checkbox/checkbox.vue' import Popover from 'src/components/popover/popover.vue' import ComponentPreview from 'src/components/component_preview/component_preview.vue' @@ -54,13 +55,11 @@ export default { ColorInput, OpacityInput, Select, + SelectMotion, Checkbox, Popover, ComponentPreview }, - beforeUpdate () { - this.cValue = (this.modelValue ?? this.fallback ?? []).map(toModel) - }, computed: { selectedType: { get () { @@ -73,7 +72,6 @@ export default { selected: { get () { const selected = this.cValue[this.selectedId] - console.log('SELECTED', selected) if (selected && typeof selected === 'object') { return { ...selected } } else if (typeof selected === 'string') { @@ -95,12 +93,6 @@ export default { currentFallback () { return this.fallback?.[this.selectedId] }, - moveUpValid () { - return this.selectedId > 0 - }, - moveDnValid () { - return this.selectedId < this.cValue.length - 1 - }, usingFallback () { return this.modelValue == null }, @@ -123,11 +115,20 @@ export default { } }, watch: { + modelValue (value) { + if (!value) this.cValue = (this.modelValue ?? this.fallback ?? []).map(toModel) + }, selected (value) { this.$emit('subShadowSelected', this.selectedId) } }, methods: { + getNewSubshadow () { + return toModel(this.selected) + }, + onSelectChange (id) { + this.selectedId = id + }, getSubshadowLabel (shadow, index) { if (typeof shadow === 'object') { return shadow?.name ?? this.$t('settings.style.shadows.shadow_id', { value: index }) @@ -141,28 +142,6 @@ export default { this.cValue[this.selectedId].spread = 0 } this.$emit('update:modelValue', this.cValue) - }, 100), - add () { - this.cValue.push(toModel(this.selected)) - this.selectedId = Math.max(this.cValue.length - 1, 0) - this.$emit('update:modelValue', this.cValue) - }, - del () { - this.cValue.splice(this.selectedId, 1) - this.selectedId = this.cValue.length === 0 ? undefined : Math.max(this.selectedId - 1, 0) - this.$emit('update:modelValue', this.cValue) - }, - moveUp () { - const movable = this.cValue.splice(this.selectedId, 1)[0] - this.cValue.splice(this.selectedId - 1, 0, movable) - this.selectedId -= 1 - this.$emit('update:modelValue', this.cValue) - }, - moveDn () { - const movable = this.cValue.splice(this.selectedId, 1)[0] - this.cValue.splice(this.selectedId + 1, 0, movable) - this.selectedId += 1 - this.$emit('update:modelValue', this.cValue) - } + }, 100) } } diff --git a/src/components/shadow_control/shadow_control.vue b/src/components/shadow_control/shadow_control.vue index 669de36e1..6a5cd1861 100644 --- a/src/components/shadow_control/shadow_control.vue +++ b/src/components/shadow_control/shadow_control.vue @@ -28,53 +28,14 @@ {{ getSubshadowLabel(shadow, index) }} -
    - - - - -
    +
    - + />