Merge branch 'themes3-grand-finale-maybe' into shigusegubu-themes3

This commit is contained in:
Henry Jameson 2024-11-26 02:02:02 +02:00
commit eb5f47ebf9
23 changed files with 290 additions and 110 deletions

1
changelog.d/custom.add Normal file
View file

@ -0,0 +1 @@
Added support for fetching /{resource}.custom.ext to allow adding instance-specific themes without altering sourcetree

View file

@ -0,0 +1 @@
Fix whitespaces for multiple status mute reasons, display bot status reason

1
changelog.d/tabs.change Normal file
View file

@ -0,0 +1 @@
Tabs now have indentation for better visibility of which tab is currently active

1
changelog.d/themes3.add Normal file
View file

@ -0,0 +1 @@
UI for making v3 themes and palettes, support for bundling v3 themes

View file

@ -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,

View file

@ -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: {

View file

@ -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"
> >

View file

@ -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,

View file

@ -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">

View file

@ -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,

View file

@ -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') }}

View file

@ -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

View file

@ -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

View file

@ -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,

View file

@ -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"

View file

@ -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",

View file

@ -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',

View file

@ -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]
} }
}) })
) }
try {
const builtinData = await window.fetch(url, { cache })
const builtinResources = await builtinData.json()
builtin = resourceTransform(builtinResources)
} catch (e) { } 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))
} }

1
static/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
*.custom.*

View 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
}

View file

@ -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
}

View file

@ -1,3 +1,4 @@
{ {
"RedmondDX": "/static/styles/Redmond DX.piss" "RedmondDX": "/static/styles/Redmond DX.piss",
"BreezyDX": "/static/styles/Breezy DX.piss"
} }