diff --git a/src/components/settings_modal/tabs/appearance_tab.js b/src/components/settings_modal/tabs/appearance_tab.js
index 711b1961b..d314dc78b 100644
--- a/src/components/settings_modal/tabs/appearance_tab.js
+++ b/src/components/settings_modal/tabs/appearance_tab.js
@@ -34,7 +34,8 @@ library.add(
const AppearanceTab = {
data () {
return {
- availableStyles: [],
+ availableThemesV3: [],
+ availableThemesV2: [],
bundledPalettes: [],
compilationCache: {},
fileImporter: newImporter({
@@ -108,13 +109,13 @@ const AppearanceTab = {
updateIndex('style').then(styles => {
styles.forEach(([key, stylePromise]) => stylePromise.then(data => {
const meta = data.find(x => x.component === '@meta')
- this.availableStyles.push({ key, data, name: meta.directives.name, version: 'v3' })
+ this.availableThemesV3.push({ key, data, name: meta.directives.name, version: 'v3' })
}))
})
updateIndex('theme').then(themes => {
themes.forEach(([key, themePromise]) => themePromise.then(data => {
- this.availableStyles.push({ key, data, name: data.name, version: 'v2' })
+ this.availableThemesV2.push({ key, data, name: data.name, version: 'v2' })
}))
})
@@ -169,6 +170,12 @@ const AppearanceTab = {
})
},
computed: {
+ availableStyles () {
+ return [
+ ...this.availableThemesV3,
+ ...this.availableThemesV2
+ ]
+ },
availablePalettes () {
return [
...this.bundledPalettes,
diff --git a/src/components/settings_modal/tabs/appearance_tab.vue b/src/components/settings_modal/tabs/appearance_tab.vue
index 547b72c07..5c1c46fe3 100644
--- a/src/components/settings_modal/tabs/appearance_tab.vue
+++ b/src/components/settings_modal/tabs/appearance_tab.vue
@@ -55,7 +55,7 @@
:data-theme-key="style.key"
class="button-default theme-preview"
:class="{ toggled: isThemeActive(style.key) }"
- @click="style.version == 'v2' ? setTheme(style.key) : setStyle(style.key)"
+ @click="style.version === 'v2' ? setTheme(style.key) : setStyle(style.key)"
>
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 cea734962..533a553fa 100644
--- a/src/components/settings_modal/tabs/style_tab/style_tab.js
+++ b/src/components/settings_modal/tabs/style_tab/style_tab.js
@@ -82,7 +82,12 @@ export default {
const exports = {}
const store = useStore()
// All rules that are made by editor
- const allEditedRules = reactive({})
+ const allEditedRules = ref(store.state.interface.styleDataUsed || {})
+ const styleDataUsed = computed(() => store.state.interface.styleDataUsed)
+
+ watch([styleDataUsed], (value) => {
+ onImport(store.state.interface.styleDataUsed)
+ }, { once: true })
exports.isActive = computed(() => {
const tabSwitcher = getCurrentInstance().parent.ctx
@@ -171,6 +176,8 @@ export default {
exports.selectedPalette = selectedPalette
provide('selectedPalette', selectedPalette)
+ watch([selectedPalette], () => updateOverallPreview())
+
exports.getNewPalette = () => ({
name: 'new palette',
bg: '#121a24',
@@ -291,7 +298,7 @@ export default {
}
if (hasChildren) {
- acc._children = acc._children ?? {}
+ output._children = output._children ?? {}
const {
component: cComponent,
variant: cVariant = 'normal',
@@ -300,7 +307,7 @@ export default {
} = child
const cPath = `${cComponent}.${cVariant}.${normalizeStates(cState)}`
- set(output._children, cPath, directives)
+ set(output._children, cPath, { directives })
} else {
output.directives = parent.directives
}
@@ -338,7 +345,7 @@ export default {
// Templates for directives
const isElementPresent = (component, directive, defaultValue = '') => computed({
get () {
- return get(allEditedRules, getPath(component, directive)) != null
+ return get(allEditedRules.value, getPath(component, directive)) != null
},
set (value) {
if (value) {
@@ -346,10 +353,11 @@ export default {
editorFriendlyFallbackStructure.value,
getPath(component, directive)
)
- set(allEditedRules, getPath(component, directive), fallback ?? defaultValue)
+ set(allEditedRules.value, getPath(component, directive), fallback ?? defaultValue)
} else {
- unset(allEditedRules, getPath(component, directive))
+ unset(allEditedRules.value, getPath(component, directive))
}
+ exports.updateOverallPreview()
}
})
@@ -357,7 +365,7 @@ export default {
get () {
let usedRule
const fallback = editorFriendlyFallbackStructure.value
- const real = allEditedRules
+ const real = allEditedRules.value
const path = getPath(component, directive)
usedRule = get(real, path) // get real
@@ -369,10 +377,11 @@ export default {
},
set (value) {
if (value) {
- set(allEditedRules, getPath(component, directive), value)
+ set(allEditedRules.value, getPath(component, directive), value)
} else {
- unset(allEditedRules, getPath(component, directive))
+ unset(allEditedRules.value, getPath(component, directive))
}
+ exports.updateOverallPreview()
}
})
@@ -520,7 +529,7 @@ export default {
}
componentsMap.values().forEach(({ name }) => {
- convert(name, allEditedRules[name])
+ convert(name, allEditedRules.value[name])
})
return resultRules
@@ -540,8 +549,8 @@ export default {
const [valType, valVal] = value.split('|')
return {
name: name.substring(2),
- valType: valType.trim(),
- value: valVal.trim()
+ valType: valType?.trim(),
+ value: valVal?.trim()
}
})
@@ -599,6 +608,33 @@ export default {
getExportedObject: () => exportStyleData.value
})
+ const onImport = parsed => {
+ const editorComponents = parsed.filter(x => x.component.startsWith('@'))
+ const rootComponent = parsed.find(x => x.component === 'Root')
+ const rules = parsed.filter(x => !x.component.startsWith('@') && x.component !== 'Root')
+ const metaIn = editorComponents.find(x => x.component === '@meta').directives
+ const palettesIn = editorComponents.filter(x => x.component === '@palette')
+
+ exports.name.value = metaIn.name
+ exports.license.value = metaIn.license
+ exports.author.value = metaIn.author
+ exports.website.value = metaIn.website
+
+ const newVirtualDirectives = Object
+ .entries(rootComponent.directives)
+ .map(([name, value]) => {
+ const [valType, valVal] = value.split('|').map(x => x.trim())
+ return { name: name.substring(2), valType, value: valVal }
+ })
+ virtualDirectives.value = newVirtualDirectives
+
+ onPalettesUpdate(palettesIn.map(x => ({ name: x.variant, ...x.directives })))
+
+ allEditedRules.value = rulesToEditorFriendly(rules)
+
+ exports.updateOverallPreview()
+ }
+
const styleImporter = newImporter({
accept: '.piss',
parser (string) { return deserialize(string) },
@@ -606,39 +642,7 @@ export default {
console.error('Failure importing style:', result)
this.$store.dispatch('pushGlobalNotice', { messageKey: 'settings.invalid_theme_imported', level: 'error' })
},
- onImport (parsed, filename) {
- const editorComponents = parsed.filter(x => x.component.startsWith('@'))
- const rootComponent = parsed.find(x => x.component === 'Root')
- const rules = parsed.filter(x => !x.component.startsWith('@') && x.component !== 'Root')
- const metaIn = editorComponents.find(x => x.component === '@meta').directives
- const palettesIn = editorComponents.filter(x => x.component === '@palette')
-
- exports.name.value = metaIn.name
- exports.license.value = metaIn.license
- exports.author.value = metaIn.author
- exports.website.value = metaIn.website
-
- const newVirtualDirectives = Object
- .entries(rootComponent.directives)
- .map(([name, value]) => {
- const [valType, valVal] = value.split('|').map(x => x.trim())
- return { name: name.substring(2), valType, value: valVal }
- })
- virtualDirectives.value = newVirtualDirectives
-
- onPalettesUpdate(palettesIn.map(x => ({ name: x.variant, ...x.directives })))
-
- Object.keys(allEditedRules).forEach((k) => delete allEditedRules[k])
-
- rules.forEach(rule => {
- rulesToEditorFriendly(
- [rule],
- allEditedRules
- )
- })
-
- exports.updateOverallPreview()
- }
+ onImport
})
// Raw format
@@ -659,6 +663,10 @@ export default {
].join('\n\n')
})
+ exports.clearStyle = () => {
+ onImport(store.state.interface.styleDataUsed)
+ }
+
exports.exportStyle = () => {
styleExporter.exportData()
}
@@ -691,16 +699,26 @@ export default {
const updateOverallPreview = throttle(() => {
try {
overallPreviewRules.value = init({
- inputRuleset: exportRules.value,
+ inputRuleset: [
+ ...exportRules.value,
+ {
+ component: 'Root',
+ directives: Object.fromEntries(
+ Object
+ .entries(selectedPalette.value)
+ .filter(([k, v]) => k && v && k !== 'name')
+ .map(([k, v]) => [`--${k}`, `color | ${v}`])
+ )
+ }
+ ],
ultimateBackgroundColor: '#000000',
- liteMode: true,
debug: true
}).eager
} catch (e) {
console.error('Could not compile preview theme', e)
return null
}
- }, 1000)
+ }, 5000)
//
// Apart from "hover" we can't really show how component looks like in
// certain states, so we have to fake them.
@@ -803,7 +821,7 @@ export default {
watch(
[
- allEditedRules,
+ allEditedRules.value,
palettes,
selectedPalette,
selectedState,
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 eabbb582f..68a0193fd 100644
--- a/src/components/settings_modal/tabs/style_tab/style_tab.vue
+++ b/src/components/settings_modal/tabs/style_tab/style_tab.vue
@@ -21,7 +21,7 @@
{{ $t('status.sensitive_muted') }}
+
+ {{ $t('status.bot_muted') }}
+
- {{ $t('status.thread_muted') }}
-
-
- {{ $t('status.thread_muted_and_words') }}
+
+ {{ $t('status.thread_muted') }}
+
+
+ {{ $t('status.thread_muted_and_words') }}
+
{
const capitalizedResource = resource[0].toUpperCase() + resource.slice(1)
const result = {}
@@ -449,6 +448,8 @@ const interfaceMod = {
)
state.paletteNameUsed = palette.nameUsed
state.paletteDataUsed = palette.dataUsed
+ state.paletteDataUsed.link = state.paletteDataUsed.link || state.paletteDataUsed.accent
+ state.paletteDataUsed.accent = state.paletteDataUsed.accent || state.paletteDataUsed.link
if (Array.isArray(state.paletteDataUsed)) {
const [
name,
@@ -461,7 +462,18 @@ const interfaceMod = {
cBlue = '#0000FF',
cOrange = '#E3FF00'
] = palette.dataUsed
- state.paletteDataUsed = { name, bg, fg, text, link, cRed, cBlue, cGreen, cOrange }
+ state.paletteDataUsed = {
+ name,
+ bg,
+ fg,
+ text,
+ link,
+ accent: link,
+ cRed,
+ cBlue,
+ cGreen,
+ cOrange
+ }
}
console.debug('Palette data used', palette.dataUsed)
@@ -473,12 +485,6 @@ const interfaceMod = {
)
state.styleNameUsed = style.nameUsed
state.styleDataUsed = style.dataUsed
-
- console.log(
- 'GOT THEME DATA',
- state.styleDataUsed,
- state.paletteDataUsed
- )
} else {
const theme = await getData(
'theme',
diff --git a/src/services/style_setter/style_setter.js b/src/services/style_setter/style_setter.js
index 7ea947647..f57fd80e9 100644
--- a/src/services/style_setter/style_setter.js
+++ b/src/services/style_setter/style_setter.js
@@ -228,35 +228,56 @@ export const applyConfig = (input, i18n) => {
export const getResourcesIndex = async (url, parser = JSON.parse) => {
const cache = 'no-store'
+ const customUrl = url.replace(/\.(\w+)$/, '.custom.$1')
+ let builtin
+ let custom
+
+ const resourceTransform = (resources) => {
+ return Object
+ .entries(resources)
+ .map(([k, v]) => {
+ if (typeof v === 'object') {
+ return [k, () => Promise.resolve(v)]
+ } else if (typeof v === 'string') {
+ return [
+ k,
+ () => window
+ .fetch(v, { cache })
+ .then(data => data.text())
+ .then(text => parser(text))
+ .catch(e => {
+ console.error(e)
+ return null
+ })
+ ]
+ } else {
+ console.error(`Unknown resource format - ${k} is a ${typeof v}`)
+ return [k, null]
+ }
+ })
+ }
try {
- const data = await window.fetch(url, { cache })
- const resources = await data.json()
- return Object.fromEntries(
- Object
- .entries(resources)
- .map(([k, v]) => {
- if (typeof v === 'object') {
- return [k, () => Promise.resolve(v)]
- } else if (typeof v === 'string') {
- return [
- k,
- () => window
- .fetch(v, { cache })
- .then(data => data.text())
- .then(text => parser(text))
- .catch(e => {
- console.error(e)
- return null
- })
- ]
- } else {
- console.error(`Unknown resource format - ${k} is a ${typeof v}`)
- return [k, null]
- }
- })
- )
+ const builtinData = await window.fetch(url, { cache })
+ const builtinResources = await builtinData.json()
+ builtin = resourceTransform(builtinResources)
} catch (e) {
- return Promise.reject(e)
+ builtin = []
+ console.warn(`Builtin resources at ${url} unavailable`)
}
+
+ try {
+ const customData = await window.fetch(customUrl, { cache })
+ const customResources = await customData.json()
+ custom = resourceTransform(customResources)
+ } catch (e) {
+ custom = []
+ console.warn(`Custom resources at ${customUrl} unavailable`)
+ }
+
+ const total = [...custom, ...builtin]
+ if (total.length === 0) {
+ return Promise.reject(new Error(`Resource at ${url} and ${customUrl} completely unavailable. Panicking`))
+ }
+ return Promise.resolve(Object.fromEntries(total))
}
diff --git a/src/services/theme_data/iss_deserializer.js b/src/services/theme_data/iss_deserializer.js
index 196007e65..55a596a5f 100644
--- a/src/services/theme_data/iss_deserializer.js
+++ b/src/services/theme_data/iss_deserializer.js
@@ -15,7 +15,7 @@ export const deserializeShadow = string => {
// spread (optional)
'(?:(-?[0-9]+(?:\\.[0-9]+)?)\\s+)?',
// either hex, variable or function
- '(#[0-9a-f]{6}|--[a-z0-9\\-_]+|\\$[a-z0-9\\-()_]+)',
+ '(#[0-9a-f]{6}|--[a-z0-9\\-_]+|\\$[a-z0-9\\-()_ ]+)',
// opacity (optional)
'(?:\\s+\\/\\s+([0-9]+(?:\\.[0-9]+)?)\\s*)?',
// name
diff --git a/static/.gitignore b/static/.gitignore
new file mode 100644
index 000000000..3332292a1
--- /dev/null
+++ b/static/.gitignore
@@ -0,0 +1 @@
+*.custom.*
diff --git a/static/styles/Breezy DX.piss b/static/styles/Breezy DX.piss
new file mode 100644
index 000000000..06f0cd5f8
--- /dev/null
+++ b/static/styles/Breezy DX.piss
@@ -0,0 +1,63 @@
+@meta {
+ name: Breezy DX;
+ author: HJ;
+ license: WTFPL;
+ website: ebin.club;
+}
+
+@palette.Dark {
+ bg: #121a24;
+ fg: #182230;
+ text: #b9b9ba;
+ link: #d8a070;
+ accent: #d8a070;
+ cRed: #FF0000;
+ cBlue: #0095ff;
+ cGreen: #0fa00f;
+ cOrange: #ffa500;
+}
+
+@palette.Light {
+ bg: #f2f6f9;
+ fg: #d6dfed;
+ text: #304055;
+ underlay: #5d6086;
+ accent: #f55b1b;
+ cBlue: #0095ff;
+ cRed: #d31014;
+ cGreen: #0fa00f;
+ cOrange: #ffa500;
+ border: #d8e6f9;
+}
+
+Root {
+ --badgeNotification: color | --cRed;
+ --buttonDefaultHoverGlow: shadow | inset 0 0 0 1 --accent / 1;
+ --buttonDefaultFocusGlow: shadow | inset 0 0 0 1 --accent / 1;
+ --buttonDefaultShadow: shadow | inset 0 0 0 1 --text / 0.35, 0 5 5 -5 #000000 / 0.35;
+ --buttonDefaultBevel: shadow | inset 0 14 14 -14 #FFFFFF / 0.1;
+ --buttonPressedBevel: shadow | inset 0 -20 20 -20 #000000 / 0.05, inset 0 20 0 80 --accent / 0.2;
+ --defaultInputBevel: shadow | inset 0 0 0 1 --text / 0.35;
+ --defaultInputHoverGlow: shadow | 0 0 0 1 --accent / 1;
+ --defaultInputFocusGlow: shadow | 0 0 0 1 --link / 1;
+}
+
+Button:disabled {
+ shadow: --buttonDefaultBevel, --buttonDefaultShadow
+}
+
+Button:hover {
+ shadow: --buttonDefaultHoverGlow, --buttonDefaultBevel, --buttonDefaultShadow
+}
+
+Input {
+ shadow: --defaultInputBevel
+}
+
+PanelHeader {
+ shadow: inset 0 30 30 -30 #ffffff / 0.25
+}
+
+Tab:hover {
+ shadow: --buttonDefaultHoverGlow, --buttonDefaultBevel, --buttonDefaultShadow
+}
diff --git a/static/styles/Redmond DX.piss b/static/styles/Redmond DX.piss
index c9ce0c04d..7777b7d33 100644
--- a/static/styles/Redmond DX.piss
+++ b/static/styles/Redmond DX.piss
@@ -48,8 +48,9 @@ Root {
--bevelDark: color | $brightness(--bg -20);
--bevelExtraDark: color | #404040;
--buttonDefaultBevel: shadow | $borderSide(--bevelExtraDark bottom-right 1 1), $borderSide(--bevelLight top-left 1 1), $borderSide(--bevelDark bottom-right 1 2);
- --buttonPressedBevel: shadow | inset 0 0 0 1 #000000 / 1 #Outer , inset 0 0 0 2 --bevelExtraDark / 1 #inner;
- --defaultInputBevel: shadow | $borderSide(--bevelLight bottom-right 1), $borderSide(--bevelDark top-left 1 1), $borderSide(--bg bottom-right 1 2), $borderSide(--bevelExtraDark top-left 1 2);
+ --buttonPressedFocusedBevel: shadow | inset 0 0 0 1 #000000 / 1 #Outer , inset 0 0 0 2 --bevelExtraDark / 1 #inner;
+ --buttonPressedBevel: shadow | $borderSide(--bevelDark top-left 1 1), $borderSide(--bevelLight bottom-right 1 1), $borderSide(--bevelExtraDark top-left 1 2);
+ --defaultInputBevel: shadow | $borderSide(--bevelDark top-left 1 1), $borderSide(--bevelLight bottom-right 1 1), $borderSide(--bevelExtraDark top-left 1 2), $borderSide(--bg bottom-right 1 2);
}
Button:toggled {
@@ -80,12 +81,44 @@ Button:pressed:hover {
shadow: --buttonPressedBevel
}
+Button:hover:pressed:focused {
+ shadow: --buttonPressedFocusedBevel
+}
+
+Button:pressed:focused {
+ shadow: --buttonPressedFocusedBevel
+}
+
+Button:toggled:pressed {
+ shadow: --buttonPressedFocusedBevel
+}
+
Input {
background: $mod(--bg -80);
shadow: --defaultInputBevel;
roundness: 0
}
+Input:focused {
+ shadow: inset 0 0 0 1 #000000 / 1, --defaultInputBevel
+}
+
+Input:focused:hover {
+ shadow: --defaultInputBevel
+}
+
+Input:focused:hover:disabled {
+ shadow: --defaultInputBevel
+}
+
+Input:hover {
+ shadow: --defaultInputBevel
+}
+
+Input:disabled {
+ shadow: --defaultInputBevel
+}
+
Panel {
shadow: --buttonDefaultBevel;
roundness: 0
@@ -106,7 +139,8 @@ Tab:active {
}
Tab:active:hover {
- background: --bg
+ background: --bg;
+ shadow: --defaultButtonBevel
}
Tab:active:hover:disabled {
@@ -125,3 +159,11 @@ Tab {
background: --bg;
shadow: --buttonDefaultBevel
}
+
+Tab:hover:active {
+ shadow: --buttonDefaultBevel
+}
+
+TopBar Link {
+ textColor: #ffffff
+}
diff --git a/static/styles/index.json b/static/styles/index.json
index 7a136cbc7..52365cd3c 100644
--- a/static/styles/index.json
+++ b/static/styles/index.json
@@ -1,3 +1,4 @@
{
- "RedmondDX": "/static/styles/Redmond DX.piss"
+ "RedmondDX": "/static/styles/Redmond DX.piss",
+ "BreezyDX": "/static/styles/Breezy DX.piss"
}