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;
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
gap: 1ex;
|
||||
|
||||
& .status-username,
|
||||
& .mute-thread,
|
||||
|
|
|
@ -64,6 +64,9 @@ const SettingsModalContent = {
|
|||
},
|
||||
bodyLock () {
|
||||
return this.$store.state.interface.settingsModalState === 'visible'
|
||||
},
|
||||
expertLevel () {
|
||||
return this.$store.state.config.expertLevel
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
|
|
@ -28,7 +28,8 @@
|
|||
<StyleTab />
|
||||
</div>
|
||||
<div
|
||||
:label="$t('settings.theme')"
|
||||
v-if="expertLevel > 0"
|
||||
:label="$t('settings.theme_old')"
|
||||
icon="paint-brush"
|
||||
data-tab-name="theme"
|
||||
>
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)"
|
||||
>
|
||||
<!-- eslint-disable vue/no-v-text-v-html-on-component -->
|
||||
<div v-if="style.ready || noIntersectionObserver">
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
<div class="style-actions">
|
||||
<button
|
||||
class="btn button-default button-new"
|
||||
@click="clearTheme"
|
||||
@click="clearStyle"
|
||||
>
|
||||
<FAIcon icon="arrows-rotate" />
|
||||
{{ $t('settings.style.themes3.editor.reset_style') }}
|
||||
|
|
|
@ -138,7 +138,6 @@ export default {
|
|||
})
|
||||
},
|
||||
mounted () {
|
||||
this.loadThemeFromLocalStorage()
|
||||
if (typeof this.shadowSelected === 'undefined') {
|
||||
this.shadowSelected = this.shadowsAvailable[0]
|
||||
}
|
||||
|
@ -296,6 +295,9 @@ export default {
|
|||
return {}
|
||||
}
|
||||
},
|
||||
themeDataUsed () {
|
||||
return this.$store.state.interface.themeDataUsed
|
||||
},
|
||||
shadowsAvailable () {
|
||||
return Object.keys(DEFAULT_SHADOWS).sort()
|
||||
},
|
||||
|
@ -478,15 +480,11 @@ export default {
|
|||
this.dismissWarning()
|
||||
},
|
||||
loadThemeFromLocalStorage (confirmLoadSource = false, forceSnapshot = false) {
|
||||
const {
|
||||
customTheme: theme,
|
||||
customThemeSource: source
|
||||
} = this.$store.getters.mergedConfig
|
||||
if (theme || source) {
|
||||
const theme = this.themeDataUsed?.source
|
||||
if (theme) {
|
||||
this.loadTheme(
|
||||
{
|
||||
theme,
|
||||
source: forceSnapshot ? theme : source
|
||||
theme
|
||||
},
|
||||
'localStorage',
|
||||
confirmLoadSource
|
||||
|
@ -705,6 +703,9 @@ export default {
|
|||
}
|
||||
},
|
||||
watch: {
|
||||
themeDataUsed () {
|
||||
this.loadThemeFromLocalStorage()
|
||||
},
|
||||
currentRadii () {
|
||||
try {
|
||||
this.previewTheme.radii = generateRadii({ radii: this.currentRadii }).theme.radii
|
||||
|
|
|
@ -958,6 +958,7 @@
|
|||
:separate-inset="shadowSelected === 'avatar' || shadowSelected === 'avatarStatus'"
|
||||
:fallback="currentShadowFallback"
|
||||
:static-vars="previewTheme.colors"
|
||||
:compact="true"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
|
|
|
@ -281,6 +281,7 @@
|
|||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
gap: 1ex;
|
||||
|
||||
& .status-username,
|
||||
& .mute-thread,
|
||||
|
|
|
@ -36,17 +36,24 @@
|
|||
>
|
||||
{{ $t('status.sensitive_muted') }}
|
||||
</small>
|
||||
<small
|
||||
v-if="muteBotStatuses && botStatus"
|
||||
class="mute-thread"
|
||||
>
|
||||
{{ $t('status.bot_muted') }}
|
||||
</small>
|
||||
<small
|
||||
v-if="showReasonMutedThread"
|
||||
class="mute-thread"
|
||||
>
|
||||
{{ $t('status.thread_muted') }}
|
||||
</small>
|
||||
<small
|
||||
v-if="showReasonMutedThread && muteWordHits.length > 0"
|
||||
class="mute-thread"
|
||||
>
|
||||
{{ $t('status.thread_muted_and_words') }}
|
||||
<span>
|
||||
{{ $t('status.thread_muted') }}
|
||||
</span>
|
||||
<span
|
||||
v-if="muteWordHits.length > 0"
|
||||
>
|
||||
{{ $t('status.thread_muted_and_words') }}
|
||||
</span>
|
||||
</small>
|
||||
<small
|
||||
class="mute-words"
|
||||
|
|
|
@ -701,6 +701,7 @@
|
|||
"use_websockets": "Use websockets (Realtime updates)",
|
||||
"text": "Text",
|
||||
"theme": "Theme",
|
||||
"theme_old": "Theme (old)",
|
||||
"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_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_and_words": ", has words:",
|
||||
"sensitive_muted": "Muting sensitive content",
|
||||
"bot_muted": "Muting bot content",
|
||||
"show_full_subject": "Show full subject",
|
||||
"hide_full_subject": "Hide full subject",
|
||||
"show_content": "Show content",
|
||||
|
|
|
@ -321,7 +321,6 @@ const interfaceMod = {
|
|||
commit('setOption', { name: 'customThemeSource', value: null })
|
||||
},
|
||||
async getThemeData ({ dispatch, commit, rootState, state }) {
|
||||
console.log('GET THEME DATA CALLED')
|
||||
const getData = async (resource, index, customData, name) => {
|
||||
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',
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
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);
|
||||
--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
|
||||
}
|
||||
|
|
|
@ -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