Merge branch 'themes3-grand-finale-maybe' into shigusegubu-themes3
This commit is contained in:
commit
eb5f47ebf9
23 changed files with 290 additions and 110 deletions
1
changelog.d/custom.add
Normal file
1
changelog.d/custom.add
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Added support for fetching /{resource}.custom.ext to allow adding instance-specific themes without altering sourcetree
|
1
changelog.d/multiple-status-mute-reasons.fix
Normal file
1
changelog.d/multiple-status-mute-reasons.fix
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Fix whitespaces for multiple status mute reasons, display bot status reason
|
1
changelog.d/tabs.change
Normal file
1
changelog.d/tabs.change
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Tabs now have indentation for better visibility of which tab is currently active
|
1
changelog.d/themes3.add
Normal file
1
changelog.d/themes3.add
Normal file
|
@ -0,0 +1 @@
|
||||||
|
UI for making v3 themes and palettes, support for bundling v3 themes
|
|
@ -26,6 +26,7 @@
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: nowrap;
|
flex-wrap: nowrap;
|
||||||
|
gap: 1ex;
|
||||||
|
|
||||||
& .status-username,
|
& .status-username,
|
||||||
& .mute-thread,
|
& .mute-thread,
|
||||||
|
|
|
@ -64,6 +64,9 @@ const SettingsModalContent = {
|
||||||
},
|
},
|
||||||
bodyLock () {
|
bodyLock () {
|
||||||
return this.$store.state.interface.settingsModalState === 'visible'
|
return this.$store.state.interface.settingsModalState === 'visible'
|
||||||
|
},
|
||||||
|
expertLevel () {
|
||||||
|
return this.$store.state.config.expertLevel
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|
|
@ -28,7 +28,8 @@
|
||||||
<StyleTab />
|
<StyleTab />
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
:label="$t('settings.theme')"
|
v-if="expertLevel > 0"
|
||||||
|
:label="$t('settings.theme_old')"
|
||||||
icon="paint-brush"
|
icon="paint-brush"
|
||||||
data-tab-name="theme"
|
data-tab-name="theme"
|
||||||
>
|
>
|
||||||
|
|
|
@ -34,7 +34,8 @@ library.add(
|
||||||
const AppearanceTab = {
|
const AppearanceTab = {
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
availableStyles: [],
|
availableThemesV3: [],
|
||||||
|
availableThemesV2: [],
|
||||||
bundledPalettes: [],
|
bundledPalettes: [],
|
||||||
compilationCache: {},
|
compilationCache: {},
|
||||||
fileImporter: newImporter({
|
fileImporter: newImporter({
|
||||||
|
@ -108,13 +109,13 @@ const AppearanceTab = {
|
||||||
updateIndex('style').then(styles => {
|
updateIndex('style').then(styles => {
|
||||||
styles.forEach(([key, stylePromise]) => stylePromise.then(data => {
|
styles.forEach(([key, stylePromise]) => stylePromise.then(data => {
|
||||||
const meta = data.find(x => x.component === '@meta')
|
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 => {
|
updateIndex('theme').then(themes => {
|
||||||
themes.forEach(([key, themePromise]) => themePromise.then(data => {
|
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: {
|
computed: {
|
||||||
|
availableStyles () {
|
||||||
|
return [
|
||||||
|
...this.availableThemesV3,
|
||||||
|
...this.availableThemesV2
|
||||||
|
]
|
||||||
|
},
|
||||||
availablePalettes () {
|
availablePalettes () {
|
||||||
return [
|
return [
|
||||||
...this.bundledPalettes,
|
...this.bundledPalettes,
|
||||||
|
|
|
@ -55,7 +55,7 @@
|
||||||
:data-theme-key="style.key"
|
:data-theme-key="style.key"
|
||||||
class="button-default theme-preview"
|
class="button-default theme-preview"
|
||||||
:class="{ toggled: isThemeActive(style.key) }"
|
: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)"
|
||||||
>
|
>
|
||||||
<!-- eslint-disable vue/no-v-text-v-html-on-component -->
|
<!-- eslint-disable vue/no-v-text-v-html-on-component -->
|
||||||
<div v-if="style.ready || noIntersectionObserver">
|
<div v-if="style.ready || noIntersectionObserver">
|
||||||
|
|
|
@ -82,7 +82,12 @@ export default {
|
||||||
const exports = {}
|
const exports = {}
|
||||||
const store = useStore()
|
const store = useStore()
|
||||||
// All rules that are made by editor
|
// 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(() => {
|
exports.isActive = computed(() => {
|
||||||
const tabSwitcher = getCurrentInstance().parent.ctx
|
const tabSwitcher = getCurrentInstance().parent.ctx
|
||||||
|
@ -171,6 +176,8 @@ export default {
|
||||||
exports.selectedPalette = selectedPalette
|
exports.selectedPalette = selectedPalette
|
||||||
provide('selectedPalette', selectedPalette)
|
provide('selectedPalette', selectedPalette)
|
||||||
|
|
||||||
|
watch([selectedPalette], () => updateOverallPreview())
|
||||||
|
|
||||||
exports.getNewPalette = () => ({
|
exports.getNewPalette = () => ({
|
||||||
name: 'new palette',
|
name: 'new palette',
|
||||||
bg: '#121a24',
|
bg: '#121a24',
|
||||||
|
@ -291,7 +298,7 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasChildren) {
|
if (hasChildren) {
|
||||||
acc._children = acc._children ?? {}
|
output._children = output._children ?? {}
|
||||||
const {
|
const {
|
||||||
component: cComponent,
|
component: cComponent,
|
||||||
variant: cVariant = 'normal',
|
variant: cVariant = 'normal',
|
||||||
|
@ -300,7 +307,7 @@ export default {
|
||||||
} = child
|
} = child
|
||||||
|
|
||||||
const cPath = `${cComponent}.${cVariant}.${normalizeStates(cState)}`
|
const cPath = `${cComponent}.${cVariant}.${normalizeStates(cState)}`
|
||||||
set(output._children, cPath, directives)
|
set(output._children, cPath, { directives })
|
||||||
} else {
|
} else {
|
||||||
output.directives = parent.directives
|
output.directives = parent.directives
|
||||||
}
|
}
|
||||||
|
@ -338,7 +345,7 @@ export default {
|
||||||
// Templates for directives
|
// Templates for directives
|
||||||
const isElementPresent = (component, directive, defaultValue = '') => computed({
|
const isElementPresent = (component, directive, defaultValue = '') => computed({
|
||||||
get () {
|
get () {
|
||||||
return get(allEditedRules, getPath(component, directive)) != null
|
return get(allEditedRules.value, getPath(component, directive)) != null
|
||||||
},
|
},
|
||||||
set (value) {
|
set (value) {
|
||||||
if (value) {
|
if (value) {
|
||||||
|
@ -346,10 +353,11 @@ export default {
|
||||||
editorFriendlyFallbackStructure.value,
|
editorFriendlyFallbackStructure.value,
|
||||||
getPath(component, directive)
|
getPath(component, directive)
|
||||||
)
|
)
|
||||||
set(allEditedRules, getPath(component, directive), fallback ?? defaultValue)
|
set(allEditedRules.value, getPath(component, directive), fallback ?? defaultValue)
|
||||||
} else {
|
} else {
|
||||||
unset(allEditedRules, getPath(component, directive))
|
unset(allEditedRules.value, getPath(component, directive))
|
||||||
}
|
}
|
||||||
|
exports.updateOverallPreview()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -357,7 +365,7 @@ export default {
|
||||||
get () {
|
get () {
|
||||||
let usedRule
|
let usedRule
|
||||||
const fallback = editorFriendlyFallbackStructure.value
|
const fallback = editorFriendlyFallbackStructure.value
|
||||||
const real = allEditedRules
|
const real = allEditedRules.value
|
||||||
const path = getPath(component, directive)
|
const path = getPath(component, directive)
|
||||||
|
|
||||||
usedRule = get(real, path) // get real
|
usedRule = get(real, path) // get real
|
||||||
|
@ -369,10 +377,11 @@ export default {
|
||||||
},
|
},
|
||||||
set (value) {
|
set (value) {
|
||||||
if (value) {
|
if (value) {
|
||||||
set(allEditedRules, getPath(component, directive), value)
|
set(allEditedRules.value, getPath(component, directive), value)
|
||||||
} else {
|
} else {
|
||||||
unset(allEditedRules, getPath(component, directive))
|
unset(allEditedRules.value, getPath(component, directive))
|
||||||
}
|
}
|
||||||
|
exports.updateOverallPreview()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -520,7 +529,7 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
componentsMap.values().forEach(({ name }) => {
|
componentsMap.values().forEach(({ name }) => {
|
||||||
convert(name, allEditedRules[name])
|
convert(name, allEditedRules.value[name])
|
||||||
})
|
})
|
||||||
|
|
||||||
return resultRules
|
return resultRules
|
||||||
|
@ -540,8 +549,8 @@ export default {
|
||||||
const [valType, valVal] = value.split('|')
|
const [valType, valVal] = value.split('|')
|
||||||
return {
|
return {
|
||||||
name: name.substring(2),
|
name: name.substring(2),
|
||||||
valType: valType.trim(),
|
valType: valType?.trim(),
|
||||||
value: valVal.trim()
|
value: valVal?.trim()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -599,14 +608,7 @@ export default {
|
||||||
getExportedObject: () => exportStyleData.value
|
getExportedObject: () => exportStyleData.value
|
||||||
})
|
})
|
||||||
|
|
||||||
const styleImporter = newImporter({
|
const onImport = parsed => {
|
||||||
accept: '.piss',
|
|
||||||
parser (string) { return deserialize(string) },
|
|
||||||
onImportFailure (result) {
|
|
||||||
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 editorComponents = parsed.filter(x => x.component.startsWith('@'))
|
||||||
const rootComponent = parsed.find(x => x.component === 'Root')
|
const rootComponent = parsed.find(x => x.component === 'Root')
|
||||||
const rules = parsed.filter(x => !x.component.startsWith('@') && x.component !== 'Root')
|
const rules = parsed.filter(x => !x.component.startsWith('@') && x.component !== 'Root')
|
||||||
|
@ -628,17 +630,19 @@ export default {
|
||||||
|
|
||||||
onPalettesUpdate(palettesIn.map(x => ({ name: x.variant, ...x.directives })))
|
onPalettesUpdate(palettesIn.map(x => ({ name: x.variant, ...x.directives })))
|
||||||
|
|
||||||
Object.keys(allEditedRules).forEach((k) => delete allEditedRules[k])
|
allEditedRules.value = rulesToEditorFriendly(rules)
|
||||||
|
|
||||||
rules.forEach(rule => {
|
|
||||||
rulesToEditorFriendly(
|
|
||||||
[rule],
|
|
||||||
allEditedRules
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
exports.updateOverallPreview()
|
exports.updateOverallPreview()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const styleImporter = newImporter({
|
||||||
|
accept: '.piss',
|
||||||
|
parser (string) { return deserialize(string) },
|
||||||
|
onImportFailure (result) {
|
||||||
|
console.error('Failure importing style:', result)
|
||||||
|
this.$store.dispatch('pushGlobalNotice', { messageKey: 'settings.invalid_theme_imported', level: 'error' })
|
||||||
|
},
|
||||||
|
onImport
|
||||||
})
|
})
|
||||||
|
|
||||||
// Raw format
|
// Raw format
|
||||||
|
@ -659,6 +663,10 @@ export default {
|
||||||
].join('\n\n')
|
].join('\n\n')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
exports.clearStyle = () => {
|
||||||
|
onImport(store.state.interface.styleDataUsed)
|
||||||
|
}
|
||||||
|
|
||||||
exports.exportStyle = () => {
|
exports.exportStyle = () => {
|
||||||
styleExporter.exportData()
|
styleExporter.exportData()
|
||||||
}
|
}
|
||||||
|
@ -691,16 +699,26 @@ export default {
|
||||||
const updateOverallPreview = throttle(() => {
|
const updateOverallPreview = throttle(() => {
|
||||||
try {
|
try {
|
||||||
overallPreviewRules.value = init({
|
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',
|
ultimateBackgroundColor: '#000000',
|
||||||
liteMode: true,
|
|
||||||
debug: true
|
debug: true
|
||||||
}).eager
|
}).eager
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Could not compile preview theme', e)
|
console.error('Could not compile preview theme', e)
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
}, 1000)
|
}, 5000)
|
||||||
//
|
//
|
||||||
// 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.
|
||||||
|
@ -803,7 +821,7 @@ export default {
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
[
|
[
|
||||||
allEditedRules,
|
allEditedRules.value,
|
||||||
palettes,
|
palettes,
|
||||||
selectedPalette,
|
selectedPalette,
|
||||||
selectedState,
|
selectedState,
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
<div class="style-actions">
|
<div class="style-actions">
|
||||||
<button
|
<button
|
||||||
class="btn button-default button-new"
|
class="btn button-default button-new"
|
||||||
@click="clearTheme"
|
@click="clearStyle"
|
||||||
>
|
>
|
||||||
<FAIcon icon="arrows-rotate" />
|
<FAIcon icon="arrows-rotate" />
|
||||||
{{ $t('settings.style.themes3.editor.reset_style') }}
|
{{ $t('settings.style.themes3.editor.reset_style') }}
|
||||||
|
|
|
@ -138,7 +138,6 @@ export default {
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
mounted () {
|
mounted () {
|
||||||
this.loadThemeFromLocalStorage()
|
|
||||||
if (typeof this.shadowSelected === 'undefined') {
|
if (typeof this.shadowSelected === 'undefined') {
|
||||||
this.shadowSelected = this.shadowsAvailable[0]
|
this.shadowSelected = this.shadowsAvailable[0]
|
||||||
}
|
}
|
||||||
|
@ -296,6 +295,9 @@ export default {
|
||||||
return {}
|
return {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
themeDataUsed () {
|
||||||
|
return this.$store.state.interface.themeDataUsed
|
||||||
|
},
|
||||||
shadowsAvailable () {
|
shadowsAvailable () {
|
||||||
return Object.keys(DEFAULT_SHADOWS).sort()
|
return Object.keys(DEFAULT_SHADOWS).sort()
|
||||||
},
|
},
|
||||||
|
@ -478,15 +480,11 @@ export default {
|
||||||
this.dismissWarning()
|
this.dismissWarning()
|
||||||
},
|
},
|
||||||
loadThemeFromLocalStorage (confirmLoadSource = false, forceSnapshot = false) {
|
loadThemeFromLocalStorage (confirmLoadSource = false, forceSnapshot = false) {
|
||||||
const {
|
const theme = this.themeDataUsed?.source
|
||||||
customTheme: theme,
|
if (theme) {
|
||||||
customThemeSource: source
|
|
||||||
} = this.$store.getters.mergedConfig
|
|
||||||
if (theme || source) {
|
|
||||||
this.loadTheme(
|
this.loadTheme(
|
||||||
{
|
{
|
||||||
theme,
|
theme
|
||||||
source: forceSnapshot ? theme : source
|
|
||||||
},
|
},
|
||||||
'localStorage',
|
'localStorage',
|
||||||
confirmLoadSource
|
confirmLoadSource
|
||||||
|
@ -705,6 +703,9 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
|
themeDataUsed () {
|
||||||
|
this.loadThemeFromLocalStorage()
|
||||||
|
},
|
||||||
currentRadii () {
|
currentRadii () {
|
||||||
try {
|
try {
|
||||||
this.previewTheme.radii = generateRadii({ radii: this.currentRadii }).theme.radii
|
this.previewTheme.radii = generateRadii({ radii: this.currentRadii }).theme.radii
|
||||||
|
|
|
@ -958,6 +958,7 @@
|
||||||
:separate-inset="shadowSelected === 'avatar' || shadowSelected === 'avatarStatus'"
|
:separate-inset="shadowSelected === 'avatar' || shadowSelected === 'avatarStatus'"
|
||||||
:fallback="currentShadowFallback"
|
:fallback="currentShadowFallback"
|
||||||
:static-vars="previewTheme.colors"
|
:static-vars="previewTheme.colors"
|
||||||
|
:compact="true"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
|
|
|
@ -281,6 +281,7 @@
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: nowrap;
|
flex-wrap: nowrap;
|
||||||
|
gap: 1ex;
|
||||||
|
|
||||||
& .status-username,
|
& .status-username,
|
||||||
& .mute-thread,
|
& .mute-thread,
|
||||||
|
|
|
@ -36,17 +36,24 @@
|
||||||
>
|
>
|
||||||
{{ $t('status.sensitive_muted') }}
|
{{ $t('status.sensitive_muted') }}
|
||||||
</small>
|
</small>
|
||||||
|
<small
|
||||||
|
v-if="muteBotStatuses && botStatus"
|
||||||
|
class="mute-thread"
|
||||||
|
>
|
||||||
|
{{ $t('status.bot_muted') }}
|
||||||
|
</small>
|
||||||
<small
|
<small
|
||||||
v-if="showReasonMutedThread"
|
v-if="showReasonMutedThread"
|
||||||
class="mute-thread"
|
class="mute-thread"
|
||||||
>
|
>
|
||||||
|
<span>
|
||||||
{{ $t('status.thread_muted') }}
|
{{ $t('status.thread_muted') }}
|
||||||
</small>
|
</span>
|
||||||
<small
|
<span
|
||||||
v-if="showReasonMutedThread && muteWordHits.length > 0"
|
v-if="muteWordHits.length > 0"
|
||||||
class="mute-thread"
|
|
||||||
>
|
>
|
||||||
{{ $t('status.thread_muted_and_words') }}
|
{{ $t('status.thread_muted_and_words') }}
|
||||||
|
</span>
|
||||||
</small>
|
</small>
|
||||||
<small
|
<small
|
||||||
class="mute-words"
|
class="mute-words"
|
||||||
|
|
|
@ -701,6 +701,7 @@
|
||||||
"use_websockets": "Use websockets (Realtime updates)",
|
"use_websockets": "Use websockets (Realtime updates)",
|
||||||
"text": "Text",
|
"text": "Text",
|
||||||
"theme": "Theme",
|
"theme": "Theme",
|
||||||
|
"theme_old": "Theme (old)",
|
||||||
"theme_help": "Use hex color codes (#rrggbb) to customize your color theme.",
|
"theme_help": "Use hex color codes (#rrggbb) to customize your color theme.",
|
||||||
"theme_help_v2_1": "You can also override certain component's colors and opacity by toggling the checkbox, use \"Clear all\" button to clear all overrides.",
|
"theme_help_v2_1": "You can also override certain component's colors and opacity by toggling the checkbox, use \"Clear all\" button to clear all overrides.",
|
||||||
"theme_help_v2_2": "Icons underneath some entries are background/text contrast indicators, hover over for detailed info. Please keep in mind that when using transparency contrast indicators show the worst possible case.",
|
"theme_help_v2_2": "Icons underneath some entries are background/text contrast indicators, hover over for detailed info. Please keep in mind that when using transparency contrast indicators show the worst possible case.",
|
||||||
|
@ -1228,6 +1229,7 @@
|
||||||
"thread_muted": "Thread muted",
|
"thread_muted": "Thread muted",
|
||||||
"thread_muted_and_words": ", has words:",
|
"thread_muted_and_words": ", has words:",
|
||||||
"sensitive_muted": "Muting sensitive content",
|
"sensitive_muted": "Muting sensitive content",
|
||||||
|
"bot_muted": "Muting bot content",
|
||||||
"show_full_subject": "Show full subject",
|
"show_full_subject": "Show full subject",
|
||||||
"hide_full_subject": "Hide full subject",
|
"hide_full_subject": "Hide full subject",
|
||||||
"show_content": "Show content",
|
"show_content": "Show content",
|
||||||
|
|
|
@ -321,7 +321,6 @@ const interfaceMod = {
|
||||||
commit('setOption', { name: 'customThemeSource', value: null })
|
commit('setOption', { name: 'customThemeSource', value: null })
|
||||||
},
|
},
|
||||||
async getThemeData ({ dispatch, commit, rootState, state }) {
|
async getThemeData ({ dispatch, commit, rootState, state }) {
|
||||||
console.log('GET THEME DATA CALLED')
|
|
||||||
const getData = async (resource, index, customData, name) => {
|
const getData = async (resource, index, customData, name) => {
|
||||||
const capitalizedResource = resource[0].toUpperCase() + resource.slice(1)
|
const capitalizedResource = resource[0].toUpperCase() + resource.slice(1)
|
||||||
const result = {}
|
const result = {}
|
||||||
|
@ -449,6 +448,8 @@ const interfaceMod = {
|
||||||
)
|
)
|
||||||
state.paletteNameUsed = palette.nameUsed
|
state.paletteNameUsed = palette.nameUsed
|
||||||
state.paletteDataUsed = palette.dataUsed
|
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)) {
|
if (Array.isArray(state.paletteDataUsed)) {
|
||||||
const [
|
const [
|
||||||
name,
|
name,
|
||||||
|
@ -461,7 +462,18 @@ const interfaceMod = {
|
||||||
cBlue = '#0000FF',
|
cBlue = '#0000FF',
|
||||||
cOrange = '#E3FF00'
|
cOrange = '#E3FF00'
|
||||||
] = palette.dataUsed
|
] = 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)
|
console.debug('Palette data used', palette.dataUsed)
|
||||||
|
|
||||||
|
@ -473,12 +485,6 @@ const interfaceMod = {
|
||||||
)
|
)
|
||||||
state.styleNameUsed = style.nameUsed
|
state.styleNameUsed = style.nameUsed
|
||||||
state.styleDataUsed = style.dataUsed
|
state.styleDataUsed = style.dataUsed
|
||||||
|
|
||||||
console.log(
|
|
||||||
'GOT THEME DATA',
|
|
||||||
state.styleDataUsed,
|
|
||||||
state.paletteDataUsed
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
const theme = await getData(
|
const theme = await getData(
|
||||||
'theme',
|
'theme',
|
||||||
|
|
|
@ -228,12 +228,12 @@ export const applyConfig = (input, i18n) => {
|
||||||
|
|
||||||
export const getResourcesIndex = async (url, parser = JSON.parse) => {
|
export const getResourcesIndex = async (url, parser = JSON.parse) => {
|
||||||
const cache = 'no-store'
|
const cache = 'no-store'
|
||||||
|
const customUrl = url.replace(/\.(\w+)$/, '.custom.$1')
|
||||||
|
let builtin
|
||||||
|
let custom
|
||||||
|
|
||||||
try {
|
const resourceTransform = (resources) => {
|
||||||
const data = await window.fetch(url, { cache })
|
return Object
|
||||||
const resources = await data.json()
|
|
||||||
return Object.fromEntries(
|
|
||||||
Object
|
|
||||||
.entries(resources)
|
.entries(resources)
|
||||||
.map(([k, v]) => {
|
.map(([k, v]) => {
|
||||||
if (typeof v === 'object') {
|
if (typeof v === 'object') {
|
||||||
|
@ -255,8 +255,29 @@ export const getResourcesIndex = async (url, parser = JSON.parse) => {
|
||||||
return [k, null]
|
return [k, null]
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
)
|
|
||||||
} catch (e) {
|
|
||||||
return Promise.reject(e)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const builtinData = await window.fetch(url, { cache })
|
||||||
|
const builtinResources = await builtinData.json()
|
||||||
|
builtin = resourceTransform(builtinResources)
|
||||||
|
} catch (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))
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ export const deserializeShadow = string => {
|
||||||
// spread (optional)
|
// spread (optional)
|
||||||
'(?:(-?[0-9]+(?:\\.[0-9]+)?)\\s+)?',
|
'(?:(-?[0-9]+(?:\\.[0-9]+)?)\\s+)?',
|
||||||
// either hex, variable or function
|
// 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)
|
// opacity (optional)
|
||||||
'(?:\\s+\\/\\s+([0-9]+(?:\\.[0-9]+)?)\\s*)?',
|
'(?:\\s+\\/\\s+([0-9]+(?:\\.[0-9]+)?)\\s*)?',
|
||||||
// name
|
// name
|
||||||
|
|
1
static/.gitignore
vendored
Normal file
1
static/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
*.custom.*
|
63
static/styles/Breezy DX.piss
Normal file
63
static/styles/Breezy DX.piss
Normal file
|
@ -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
|
||||||
|
}
|
|
@ -48,8 +48,9 @@ Root {
|
||||||
--bevelDark: color | $brightness(--bg -20);
|
--bevelDark: color | $brightness(--bg -20);
|
||||||
--bevelExtraDark: color | #404040;
|
--bevelExtraDark: color | #404040;
|
||||||
--buttonDefaultBevel: shadow | $borderSide(--bevelExtraDark bottom-right 1 1), $borderSide(--bevelLight top-left 1 1), $borderSide(--bevelDark bottom-right 1 2);
|
--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;
|
--buttonPressedFocusedBevel: 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);
|
--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 {
|
Button:toggled {
|
||||||
|
@ -80,12 +81,44 @@ Button:pressed:hover {
|
||||||
shadow: --buttonPressedBevel
|
shadow: --buttonPressedBevel
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Button:hover:pressed:focused {
|
||||||
|
shadow: --buttonPressedFocusedBevel
|
||||||
|
}
|
||||||
|
|
||||||
|
Button:pressed:focused {
|
||||||
|
shadow: --buttonPressedFocusedBevel
|
||||||
|
}
|
||||||
|
|
||||||
|
Button:toggled:pressed {
|
||||||
|
shadow: --buttonPressedFocusedBevel
|
||||||
|
}
|
||||||
|
|
||||||
Input {
|
Input {
|
||||||
background: $mod(--bg -80);
|
background: $mod(--bg -80);
|
||||||
shadow: --defaultInputBevel;
|
shadow: --defaultInputBevel;
|
||||||
roundness: 0
|
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 {
|
Panel {
|
||||||
shadow: --buttonDefaultBevel;
|
shadow: --buttonDefaultBevel;
|
||||||
roundness: 0
|
roundness: 0
|
||||||
|
@ -106,7 +139,8 @@ Tab:active {
|
||||||
}
|
}
|
||||||
|
|
||||||
Tab:active:hover {
|
Tab:active:hover {
|
||||||
background: --bg
|
background: --bg;
|
||||||
|
shadow: --defaultButtonBevel
|
||||||
}
|
}
|
||||||
|
|
||||||
Tab:active:hover:disabled {
|
Tab:active:hover:disabled {
|
||||||
|
@ -125,3 +159,11 @@ Tab {
|
||||||
background: --bg;
|
background: --bg;
|
||||||
shadow: --buttonDefaultBevel
|
shadow: --buttonDefaultBevel
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Tab:hover:active {
|
||||||
|
shadow: --buttonDefaultBevel
|
||||||
|
}
|
||||||
|
|
||||||
|
TopBar Link {
|
||||||
|
textColor: #ffffff
|
||||||
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
{
|
{
|
||||||
"RedmondDX": "/static/styles/Redmond DX.piss"
|
"RedmondDX": "/static/styles/Redmond DX.piss",
|
||||||
|
"BreezyDX": "/static/styles/Breezy DX.piss"
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue