Merge branch 'themes-accent' into 'develop'

Themes v3 Part 1 "2.1" codenamed "One step for themes, a giant burder for code reviewers"

Closes #750 and #774

See merge request pleroma/pleroma-fe!1037
This commit is contained in:
HJ 2020-02-18 21:20:43 +00:00
commit 3ddf7ebe2c
118 changed files with 3087 additions and 3077 deletions

View File

@ -5,6 +5,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## [Unreleased] ## [Unreleased]
### Added ### Added
- Tons of color slots including ones for hover/pressed/toggled buttons
- Experimental `--variable[,mod]` syntax support for color slots in themes. the `mod` makes color brighter/darker depending on background color (makes darker color brighter/darker depending on background color)
- Paper theme by Shpuld
- Icons in nav panel - Icons in nav panel
- Private mode support - Private mode support
- Support for 'Move' type notifications - Support for 'Move' type notifications
@ -13,10 +16,16 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Emoji reactions for statuses - Emoji reactions for statuses
- MRF keyword policy disclosure - MRF keyword policy disclosure
### Changed ### Changed
- theme engine update to 3 (themes v2.1 introduction)
- massive internal changes in theme engine - slowly away from "generate things separately with spaghetti code" towards "feed all data into single 'generateTheme' function and declare slot inheritance and all in a separate file"
- Breezy theme updates to make it closer to actual Breeze in some aspects
- when using `--variable` in shadows it no longer uses the actual CSS3 variable, instead it generates color from other slots
- theme doesn't get saved to local storage when opening FE anonymously
- Captcha now resets on failed registrations - Captcha now resets on failed registrations
- Notifications column now cleans itself up to optimize performance when tab is left open for a long time - Notifications column now cleans itself up to optimize performance when tab is left open for a long time
- 403 messaging - 403 messaging
### Fixed ### Fixed
- anon viewers won't get theme data saved to local storage, so admin changing default theme will have an effect for users coming back to instance.
- Single notifications left unread when hitting read on another device/tab - Single notifications left unread when hitting read on another device/tab
- Registration fixed - Registration fixed
- Deactivation of remote accounts from frontend - Deactivation of remote accounts from frontend

View File

@ -35,6 +35,7 @@ module.exports = {
], ],
alias: { alias: {
'vue$': 'vue/dist/vue.runtime.common', 'vue$': 'vue/dist/vue.runtime.common',
'static': path.resolve(__dirname, '../static'),
'src': path.resolve(__dirname, '../src'), 'src': path.resolve(__dirname, '../src'),
'assets': path.resolve(__dirname, '../src/assets'), 'assets': path.resolve(__dirname, '../src/assets'),
'components': path.resolve(__dirname, '../src/components') 'components': path.resolve(__dirname, '../src/components')

View File

@ -31,9 +31,12 @@ h4 {
margin: auto; margin: auto;
min-height: 100vh; min-height: 100vh;
max-width: 980px; max-width: 980px;
background-color: rgba(0,0,0,0.15);
align-content: flex-start; align-content: flex-start;
} }
.underlay {
background-color: rgba(0,0,0,0.15);
background-color: var(--underlay, rgba(0,0,0,0.15));
}
.text-center { .text-center {
text-align: center; text-align: center;
@ -98,18 +101,39 @@ button {
&:active { &:active {
box-shadow: 0px 0px 4px 0px rgba(255, 255, 255, 0.3), 0px 1px 0px 0px rgba(0, 0, 0, 0.2) inset, 0px -1px 0px 0px rgba(255, 255, 255, 0.2) inset; box-shadow: 0px 0px 4px 0px rgba(255, 255, 255, 0.3), 0px 1px 0px 0px rgba(0, 0, 0, 0.2) inset, 0px -1px 0px 0px rgba(255, 255, 255, 0.2) inset;
box-shadow: var(--buttonPressedShadow); box-shadow: var(--buttonPressedShadow);
color: $fallback--text;
color: var(--btnPressedText, $fallback--text);
background-color: $fallback--fg;
background-color: var(--btnPressed, $fallback--fg);
i {
color: $fallback--text;
color: var(--btnPressedText, $fallback--text);
}
} }
&:disabled { &:disabled {
cursor: not-allowed; cursor: not-allowed;
opacity: 0.5; color: $fallback--text;
color: var(--btnDisabledText, $fallback--text);
background-color: $fallback--fg;
background-color: var(--btnDisabled, $fallback--fg);
i {
color: $fallback--text;
color: var(--btnDisabledText, $fallback--text);
}
} }
&.pressed { &.toggled {
color: $fallback--faint; color: $fallback--text;
color: var(--faint, $fallback--faint); color: var(--btnToggledText, $fallback--text);
background-color: $fallback--bg; background-color: $fallback--fg;
background-color: var(--bg, $fallback--bg) background-color: var(--btnToggled, $fallback--fg);
box-shadow: 0px 0px 4px 0px rgba(255, 255, 255, 0.3), 0px 1px 0px 0px rgba(0, 0, 0, 0.2) inset, 0px -1px 0px 0px rgba(255, 255, 255, 0.2) inset;
box-shadow: var(--buttonPressedShadow);
i {
color: $fallback--text;
color: var(--btnToggledText, $fallback--text);
}
} }
&.danger { &.danger {
@ -121,12 +145,15 @@ button {
} }
} }
label.select { input, textarea, .select, .input {
padding: 0;
&.unstyled {
border-radius: 0;
background: none;
box-shadow: none;
height: unset;
} }
input, textarea, .select {
border: none; border: none;
border-radius: $fallback--inputRadius; border-radius: $fallback--inputRadius;
border-radius: var(--inputRadius, $fallback--inputRadius); border-radius: var(--inputRadius, $fallback--inputRadius);
@ -140,13 +167,17 @@ input, textarea, .select {
font-family: var(--inputFont, sans-serif); font-family: var(--inputFont, sans-serif);
font-size: 14px; font-size: 14px;
margin: 0; margin: 0;
padding: 8px .5em;
box-sizing: border-box; box-sizing: border-box;
display: inline-block; display: inline-block;
position: relative; position: relative;
height: 28px; height: 28px;
line-height: 16px; line-height: 16px;
hyphens: none; hyphens: none;
padding: 8px .5em;
&.select {
padding: 0;
}
&:disabled, &[disabled=disabled] { &:disabled, &[disabled=disabled] {
cursor: not-allowed; cursor: not-allowed;
@ -160,7 +191,7 @@ input, textarea, .select {
right: 5px; right: 5px;
height: 100%; height: 100%;
color: $fallback--text; color: $fallback--text;
color: var(--text, $fallback--text); color: var(--inputText, $fallback--text);
line-height: 28px; line-height: 28px;
z-index: 0; z-index: 0;
pointer-events: none; pointer-events: none;
@ -198,7 +229,7 @@ input, textarea, .select {
&:checked + label::before { &:checked + label::before {
box-shadow: 0px 0px 2px black inset, 0px 0px 0px 4px $fallback--fg inset; box-shadow: 0px 0px 2px black inset, 0px 0px 0px 4px $fallback--fg inset;
box-shadow: var(--inputShadow), 0px 0px 0px 4px var(--fg, $fallback--fg) inset; box-shadow: var(--inputShadow), 0px 0px 0px 4px var(--fg, $fallback--fg) inset;
background-color: var(--link, $fallback--link); background-color: var(--accent, $fallback--link);
} }
&:disabled { &:disabled {
&, &,
@ -235,7 +266,7 @@ input, textarea, .select {
display: none; display: none;
&:checked + label::before { &:checked + label::before {
color: $fallback--text; color: $fallback--text;
color: var(--text, $fallback--text); color: var(--inputText, $fallback--text);
} }
&:disabled { &:disabled {
&, &,
@ -353,6 +384,33 @@ i[class*=icon-] {
height: 50px; height: 50px;
box-sizing: border-box; box-sizing: border-box;
button {
&, i[class*=icon-] {
color: $fallback--text;
color: var(--btnTopBarText, $fallback--text);
}
&:active {
background-color: $fallback--fg;
background-color: var(--btnPressedTopBar, $fallback--fg);
color: $fallback--text;
color: var(--btnPressedTopBarText, $fallback--text);
}
&:disabled {
color: $fallback--text;
color: var(--btnDisabledTopBarText, $fallback--text);
}
&.toggled {
color: $fallback--text;
color: var(--btnToggledTopBarText, $fallback--text);
background-color: $fallback--fg;
background-color: var(--btnToggledTopBar, $fallback--fg)
}
}
.logo { .logo {
display: flex; display: flex;
position: absolute; position: absolute;
@ -487,6 +545,10 @@ main-router {
color: $fallback--faint; color: $fallback--faint;
color: var(--panelFaint, $fallback--faint); color: var(--panelFaint, $fallback--faint);
} }
.faint-link {
color: $fallback--faint;
color: var(--faintLink, $fallback--faint);
}
.alert { .alert {
white-space: nowrap; white-space: nowrap;
@ -509,6 +571,30 @@ main-router {
align-self: stretch; align-self: stretch;
} }
button {
&, i[class*=icon-] {
color: $fallback--text;
color: var(--btnPanelText, $fallback--text);
}
&:active {
background-color: $fallback--fg;
background-color: var(--btnPressedPanel, $fallback--fg);
color: $fallback--text;
color: var(--btnPressedPanelText, $fallback--text);
}
&:disabled {
color: $fallback--text;
color: var(--btnDisabledPanelText, $fallback--text);
}
&.toggled {
color: $fallback--text;
color: var(--btnToggledPanelText, $fallback--text);
}
}
a { a {
color: $fallback--link; color: $fallback--link;
color: var(--panelLink, $fallback--link) color: var(--panelLink, $fallback--link)

View File

@ -78,7 +78,7 @@
</nav> </nav>
<div <div
id="content" id="content"
class="container" class="container underlay"
> >
<div class="sidebar-flexer mobile-hidden"> <div class="sidebar-flexer mobile-hidden">
<div class="sidebar-bounds"> <div class="sidebar-bounds">

View File

@ -5,6 +5,8 @@ import App from '../App.vue'
import { windowWidth } from '../services/window_utils/window_utils' import { windowWidth } from '../services/window_utils/window_utils'
import { getOrCreateApp, getClientToken } from '../services/new_api/oauth.js' import { getOrCreateApp, getClientToken } from '../services/new_api/oauth.js'
import backendInteractorService from '../services/backend_interactor_service/backend_interactor_service.js' import backendInteractorService from '../services/backend_interactor_service/backend_interactor_service.js'
import { CURRENT_VERSION } from '../services/theme_data/theme_data.service.js'
import { applyTheme } from '../services/style_setter/style_setter.js'
const getStatusnetConfig = async ({ store }) => { const getStatusnetConfig = async ({ store }) => {
try { try {
@ -265,7 +267,7 @@ const checkOAuthToken = async ({ store }) => {
try { try {
await store.dispatch('loginUser', store.getters.getUserToken()) await store.dispatch('loginUser', store.getters.getUserToken())
} catch (e) { } catch (e) {
console.log(e) console.error(e)
} }
} }
resolve() resolve()
@ -273,23 +275,29 @@ const checkOAuthToken = async ({ store }) => {
} }
const afterStoreSetup = async ({ store, i18n }) => { const afterStoreSetup = async ({ store, i18n }) => {
if (store.state.config.customTheme) {
// This is a hack to deal with async loading of config.json and themes
// See: style_setter.js, setPreset()
window.themeLoaded = true
store.dispatch('setOption', {
name: 'customTheme',
value: store.state.config.customTheme
})
}
const width = windowWidth() const width = windowWidth()
store.dispatch('setMobileLayout', width <= 800) store.dispatch('setMobileLayout', width <= 800)
await setConfig({ store })
const { customTheme, customThemeSource } = store.state.config
const { theme } = store.state.instance
const customThemePresent = customThemeSource || customTheme
if (customThemePresent) {
if (customThemeSource && customThemeSource.themeEngineVersion === CURRENT_VERSION) {
applyTheme(customThemeSource)
} else {
applyTheme(customTheme)
}
} else if (theme) {
// do nothing, it will load asynchronously
} else {
console.error('Failed to load any theme!')
}
// Now we can try getting the server settings and logging in // Now we can try getting the server settings and logging in
await Promise.all([ await Promise.all([
checkOAuthToken({ store }), checkOAuthToken({ store }),
setConfig({ store }),
getTOS({ store }), getTOS({ store }),
getInstancePanel({ store }), getInstancePanel({ store }),
getStickers({ store }), getStickers({ store }),

View File

@ -130,6 +130,8 @@
.placeholder { .placeholder {
margin-right: 8px; margin-right: 8px;
margin-bottom: 4px; margin-bottom: 4px;
color: $fallback--link;
color: var(--postLink, $fallback--link);
} }
.nsfw-placeholder { .nsfw-placeholder {

View File

@ -40,8 +40,8 @@
top: 100%; top: 100%;
right: 0; right: 0;
max-height: 400px; max-height: 400px;
background-color: $fallback--lightBg; background-color: $fallback--bg;
background-color: var(--lightBg, $fallback--lightBg); background-color: var(--bg, $fallback--bg);
border-style: solid; border-style: solid;
border-width: 1px; border-width: 1px;
border-color: $fallback--border; border-color: $fallback--border;

View File

@ -87,13 +87,13 @@ export default {
&:checked + .checkbox-indicator::before { &:checked + .checkbox-indicator::before {
color: $fallback--text; color: $fallback--text;
color: var(--text, $fallback--text); color: var(--inputText, $fallback--text);
} }
&:indeterminate + .checkbox-indicator::before { &:indeterminate + .checkbox-indicator::before {
content: ''; content: '';
color: $fallback--text; color: $fallback--text;
color: var(--text, $fallback--text); color: var(--inputText, $fallback--text);
} }
} }

View File

@ -0,0 +1,68 @@
@import '../../_variables.scss';
.color-input {
display: inline-flex;
&-field.input {
display: inline-flex;
flex: 0 0 0;
max-width: 9em;
align-items: stretch;
padding: .2em 8px;
input {
background: none;
color: $fallback--lightText;
color: var(--inputText, $fallback--lightText);
border: none;
padding: 0;
margin: 0;
&.textColor {
flex: 1 0 3em;
min-width: 3em;
padding: 0;
}
&.nativeColor {
flex: 0 0 2em;
min-width: 2em;
align-self: center;
height: 100%;
}
}
.computedIndicator,
.transparentIndicator {
flex: 0 0 2em;
min-width: 2em;
align-self: center;
height: 100%;
}
.transparentIndicator {
// forgot to install counter-strike source, ooops
background-color: #FF00FF;
position: relative;
&::before, &::after {
display: block;
content: '';
background-color: #000000;
position: absolute;
height: 50%;
width: 50%;
}
&::after {
top: 0;
left: 0;
}
&::before {
bottom: 0;
right: 0;
}
}
}
.label {
flex: 1 1 auto;
}
}

View File

@ -1,6 +1,6 @@
<template> <template>
<div <div
class="color-control style-control" class="color-input style-control"
:class="{ disabled: !present || disabled }" :class="{ disabled: !present || disabled }"
> >
<label <label
@ -9,46 +9,100 @@
> >
{{ label }} {{ label }}
</label> </label>
<input <Checkbox
v-if="typeof fallback !== 'undefined'" v-if="typeof fallback !== 'undefined' && showOptionalTickbox"
:id="name + '-o'"
class="opt exlcude-disabled"
type="checkbox"
:checked="present" :checked="present"
@input="$emit('input', typeof value === 'undefined' ? fallback : undefined)" :disabled="disabled"
> class="opt"
<label @change="$emit('input', typeof value === 'undefined' ? fallback : undefined)"
v-if="typeof fallback !== 'undefined'"
class="opt-l"
:for="name + '-o'"
/> />
<input <div class="input color-input-field">
:id="name"
class="color-input"
type="color"
:value="value || fallback"
:disabled="!present || disabled"
@input="$emit('input', $event.target.value)"
>
<input <input
:id="name + '-t'" :id="name + '-t'"
class="text-input" class="textColor unstyled"
type="text" type="text"
:value="value || fallback" :value="value || fallback"
:disabled="!present || disabled" :disabled="!present || disabled"
@input="$emit('input', $event.target.value)" @input="$emit('input', $event.target.value)"
> >
<input
v-if="validColor"
:id="name"
class="nativeColor unstyled"
type="color"
:value="value || fallback"
:disabled="!present || disabled"
@input="$emit('input', $event.target.value)"
>
<div
v-if="transparentColor"
class="transparentIndicator"
/>
<div
v-if="computedColor"
class="computedIndicator"
:style="{backgroundColor: fallback}"
/>
</div>
</div> </div>
</template> </template>
<style lang="scss" src="./color_input.scss"></style>
<script> <script>
import Checkbox from '../checkbox/checkbox.vue'
import { hex2rgb } from '../../services/color_convert/color_convert.js'
export default { export default {
props: [ components: {
'name', 'label', 'value', 'fallback', 'disabled' Checkbox
], },
props: {
// Name of color, used for identifying
name: {
required: true,
type: String
},
// Readable label
label: {
required: true,
type: String
},
// Color value, should be required but vue cannot tell the difference
// between "property missing" and "property set to undefined"
value: {
required: false,
type: String,
default: undefined
},
// Color fallback to use when value is not defeind
fallback: {
required: false,
type: String,
default: undefined
},
// Disable the control
disabled: {
required: false,
type: Boolean,
default: false
},
// Show "optional" tickbox, for when value might become mandatory
showOptionalTickbox: {
required: false,
type: Boolean,
default: true
}
},
computed: { computed: {
present () { present () {
return typeof this.value !== 'undefined' return typeof this.value !== 'undefined'
},
validColor () {
return hex2rgb(this.value || this.fallback)
},
transparentColor () {
return this.value === 'transparent'
},
computedColor () {
return this.value && this.value.startsWith('--')
} }
} }
} }

View File

@ -37,9 +37,17 @@
<script> <script>
export default { export default {
props: [ props: {
'large', 'contrast' large: {
], required: false
},
// TODO: Make theme switcher compute theme initially so that contrast
// component won't be called without contrast data
contrast: {
required: false,
type: Object
}
},
computed: { computed: {
hint () { hint () {
const levelVal = this.contrast.aaa ? 'aaa' : (this.contrast.aa ? 'aa' : 'bad') const levelVal = this.contrast.aaa ? 'aaa' : (this.contrast.aa ? 'aa' : 'bad')

View File

@ -75,18 +75,18 @@
.dialog-modal-content { .dialog-modal-content {
margin: 0; margin: 0;
padding: 1rem 1rem; padding: 1rem 1rem;
background-color: $fallback--lightBg; background-color: $fallback--bg;
background-color: var(--lightBg, $fallback--lightBg); background-color: var(--bg, $fallback--bg);
white-space: normal; white-space: normal;
} }
.dialog-modal-footer { .dialog-modal-footer {
margin: 0; margin: 0;
padding: .5em .5em; padding: .5em .5em;
background-color: $fallback--lightBg; background-color: $fallback--bg;
background-color: var(--lightBg, $fallback--lightBg); background-color: var(--bg, $fallback--bg);
border-top: 1px solid $fallback--bg; border-top: 1px solid $fallback--border;
border-top: 1px solid var(--bg, $fallback--bg); border-top: 1px solid var(--border, $fallback--border);
display: flex; display: flex;
justify-content: flex-end; justify-content: flex-end;

View File

@ -109,10 +109,16 @@
box-shadow: 1px 2px 4px rgba(0, 0, 0, 0.5); box-shadow: 1px 2px 4px rgba(0, 0, 0, 0.5);
box-shadow: var(--popupShadow); box-shadow: var(--popupShadow);
min-width: 75%; min-width: 75%;
background: $fallback--bg; background-color: $fallback--bg;
background: var(--bg, $fallback--bg); background-color: var(--popover, $fallback--bg);
color: $fallback--lightText; color: $fallback--link;
color: var(--lightText, $fallback--lightText); color: var(--popoverText, $fallback--link);
--faint: var(--popoverFaintText, $fallback--faint);
--faintLink: var(--popoverFaintLink, $fallback--faint);
--lightText: var(--popoverLightText, $fallback--lightText);
--postLink: var(--popoverPostLink, $fallback--link);
--postFaintLink: var(--popoverPostFaintLink, $fallback--link);
--icon: var(--popoverIcon, $fallback--icon);
} }
} }
@ -157,7 +163,12 @@
&.highlighted { &.highlighted {
background-color: $fallback--fg; background-color: $fallback--fg;
background-color: var(--lightBg, $fallback--fg); background-color: var(--selectedMenuPopover, $fallback--fg);
color: var(--selectedMenuPopoverText, $fallback--text);
--faint: var(--selectedMenuPopoverFaintText, $fallback--faint);
--faintLink: var(--selectedMenuPopoverFaintLink, $fallback--faint);
--lightText: var(--selectedMenuPopoverLightText, $fallback--lightText);
--icon: var(--selectedMenuPopoverIcon, $fallback--icon);
} }
} }
} }

View File

@ -8,6 +8,15 @@
left: 0; left: 0;
margin: 0 !important; margin: 0 !important;
z-index: 1; z-index: 1;
background-color: $fallback--bg;
background-color: var(--popover, $fallback--bg);
color: $fallback--link;
color: var(--popoverText, $fallback--link);
--lightText: var(--popoverLightText, $fallback--faint);
--faint: var(--popoverFaintText, $fallback--faint);
--faintLink: var(--popoverFaintLink, $fallback--faint);
--lightText: var(--popoverLightText, $fallback--lightText);
--icon: var(--popoverIcon, $fallback--icon);
.keep-open, .keep-open,
.too-many-emoji { .too-many-emoji {

View File

@ -128,7 +128,7 @@
} }
.picked-reaction { .picked-reaction {
border: 1px solid var(--link, $fallback--link); border: 1px solid var(--accent, $fallback--link);
margin-left: -1px; // offset the border, can't use inset shadows either margin-left: -1px; // offset the border, can't use inset shadows either
margin-right: calc(0.5em - 1px); margin-right: calc(0.5em - 1px);
} }

View File

@ -42,7 +42,7 @@ export default {
}, },
methods: { methods: {
exportData () { exportData () {
const stringified = JSON.stringify(this.exportObject) // Pretty-print and indent with 2 spaces const stringified = JSON.stringify(this.exportObject, null, 2) // Pretty-print and indent with 2 spaces
// Create an invisible link with a data url and simulate a click // Create an invisible link with a data url and simulate a click
const e = document.createElement('a') const e = document.createElement('a')

View File

@ -1,7 +1,7 @@
<template> <template>
<button <button
class="btn btn-default follow-button" class="btn btn-default follow-button"
:class="{ pressed: isPressed }" :class="{ toggled: isPressed }"
:disabled="inProgress" :disabled="inProgress"
:title="title" :title="title"
@click="onClick" @click="onClick"

View File

@ -123,7 +123,7 @@
</div> </div>
<button <button
class="btn btn-default btn-block" class="btn btn-default btn-block"
:class="{ pressed: showDropDown }" :class="{ toggled: showDropDown }"
> >
{{ $t('user_card.admin_menu.moderation') }} {{ $t('user_card.admin_menu.moderation') }}
</button> </button>

View File

@ -100,13 +100,25 @@
&:hover { &:hover {
background-color: $fallback--lightBg; background-color: $fallback--lightBg;
background-color: var(--lightBg, $fallback--lightBg); background-color: var(--selectedMenu, $fallback--lightBg);
color: $fallback--link;
color: var(--selectedMenuText, $fallback--link);
--faint: var(--selectedMenuFaintText, $fallback--faint);
--faintLink: var(--selectedMenuFaintLink, $fallback--faint);
--lightText: var(--selectedMenuLightText, $fallback--lightText);
--icon: var(--selectedMenuIcon, $fallback--icon);
} }
&.router-link-active { &.router-link-active {
font-weight: bolder; font-weight: bolder;
background-color: $fallback--lightBg; background-color: $fallback--lightBg;
background-color: var(--lightBg, $fallback--lightBg); background-color: var(--selectedMenu, $fallback--lightBg);
color: $fallback--text;
color: var(--selectedMenuText, $fallback--text);
--faint: var(--selectedMenuFaintText, $fallback--faint);
--faintLink: var(--selectedMenuFaintLink, $fallback--faint);
--lightText: var(--selectedMenuLightText, $fallback--lightText);
--icon: var(--selectedMenuIcon, $fallback--icon);
&:hover { &:hover {
text-decoration: underline; text-decoration: underline;

View File

@ -68,6 +68,9 @@
a { a {
color: var(--faintLink); color: var(--faintLink);
} }
.status-content a {
color: var(--postFaintLink);
}
} }
padding: 0; padding: 0;
.media-body { .media-body {

View File

@ -9,18 +9,12 @@
> >
{{ $t('settings.style.common.opacity') }} {{ $t('settings.style.common.opacity') }}
</label> </label>
<input <Checkbox
v-if="typeof fallback !== 'undefined'" v-if="typeof fallback !== 'undefined'"
:id="name + '-o'"
class="opt exclude-disabled"
type="checkbox"
:checked="present" :checked="present"
@input="$emit('input', !present ? fallback : undefined)" :disabled="disabled"
> class="opt"
<label @change="$emit('input', !present ? fallback : undefined)"
v-if="typeof fallback !== 'undefined'"
class="opt-l"
:for="name + '-o'"
/> />
<input <input
:id="name" :id="name"
@ -37,7 +31,11 @@
</template> </template>
<script> <script>
import Checkbox from '../checkbox/checkbox.vue'
export default { export default {
components: {
Checkbox
},
props: [ props: [
'name', 'value', 'fallback', 'disabled' 'name', 'value', 'fallback', 'disabled'
], ],

View File

@ -104,8 +104,10 @@
.result-fill { .result-fill {
height: 100%; height: 100%;
position: absolute; position: absolute;
color: $fallback--text;
color: var(--pollText, $fallback--text);
background-color: $fallback--lightBg; background-color: $fallback--lightBg;
background-color: var(--linkBg, $fallback--lightBg); background-color: var(--poll, $fallback--lightBg);
border-radius: $fallback--panelRadius; border-radius: $fallback--panelRadius;
border-radius: var(--panelRadius, $fallback--panelRadius); border-radius: var(--panelRadius, $fallback--panelRadius);
top: 0; top: 0;

View File

@ -9,7 +9,15 @@
border-radius: $fallback--btnRadius; border-radius: $fallback--btnRadius;
border-radius: var(--btnRadius, $fallback--btnRadius); border-radius: var(--btnRadius, $fallback--btnRadius);
background-color: $fallback--bg; background-color: $fallback--bg;
background-color: var(--bg, $fallback--bg); background-color: var(--popover, $fallback--bg);
color: $fallback--text;
color: var(--popoverText, $fallback--text);
--faint: var(--popoverFaintText, $fallback--faint);
--faintLink: var(--popoverFaintLink, $fallback--faint);
--lightText: var(--popoverLightText, $fallback--lightText);
--postLink: var(--popoverPostLink, $fallback--link);
--postFaintLink: var(--popoverPostFaintLink, $fallback--link);
--icon: var(--popoverIcon, $fallback--icon);
} }
.popover-arrow { .popover-arrow {
@ -129,6 +137,8 @@
width: 100%; width: 100%;
height: 100%; height: 100%;
--btnText: var(--popoverText, $fallback--text);
&-icon { &-icon {
padding-left: 0.5rem; padding-left: 0.5rem;
@ -137,11 +147,18 @@
} }
} }
&:hover { &:active, &:hover {
// TODO: improve the look on breeze themes background-color: $fallback--lightBg;
background-color: $fallback--fg; background-color: var(--selectedMenuPopover, $fallback--lightBg);
background-color: var(--btn, $fallback--fg); color: $fallback--link;
box-shadow: none; color: var(--selectedMenuPopoverText, $fallback--link);
--faint: var(--selectedMenuPopoverFaintText, $fallback--faint);
--faintLink: var(--selectedMenuPopoverFaintLink, $fallback--faint);
--lightText: var(--selectedMenuPopoverLightText, $fallback--lightText);
--icon: var(--selectedMenuPopoverIcon, $fallback--icon);
i {
color: var(--selectedMenuPopoverIcon, $fallback--icon);
}
} }
} }
} }

View File

@ -12,7 +12,7 @@
<input <input
v-if="typeof fallback !== 'undefined'" v-if="typeof fallback !== 'undefined'"
:id="name + '-o'" :id="name + '-o'"
class="opt exclude-disabled" class="opt"
type="checkbox" type="checkbox"
:checked="present" :checked="present"
@input="$emit('input', !present ? fallback : undefined)" @input="$emit('input', !present ? fallback : undefined)"

View File

@ -68,7 +68,12 @@
&-item-selected-inner { &-item-selected-inner {
background-color: $fallback--lightBg; background-color: $fallback--lightBg;
background-color: var(--lightBg, $fallback--lightBg); background-color: var(--selectedMenu, $fallback--lightBg);
color: var(--selectedMenuText, $fallback--text);
--faint: var(--selectedMenuFaintText, $fallback--faint);
--faintLink: var(--selectedMenuFaintLink, $fallback--faint);
--lightText: var(--selectedMenuLightText, $fallback--lightText);
--icon: var(--selectedMenuIcon, $fallback--icon);
} }
&-header { &-header {

View File

@ -76,7 +76,7 @@
<li> <li>
<Checkbox v-model="useStreamingApi"> <Checkbox v-model="useStreamingApi">
{{ $t('settings.useStreamingApi') }} {{ $t('settings.useStreamingApi') }}
<br/> <br>
<small> <small>
{{ $t('settings.useStreamingApiWarning') }} {{ $t('settings.useStreamingApiWarning') }}
</small> </small>

View File

@ -3,6 +3,17 @@ import OpacityInput from '../opacity_input/opacity_input.vue'
import { getCssShadow } from '../../services/style_setter/style_setter.js' import { getCssShadow } from '../../services/style_setter/style_setter.js'
import { hex2rgb } from '../../services/color_convert/color_convert.js' import { hex2rgb } from '../../services/color_convert/color_convert.js'
const toModel = (object = {}) => ({
x: 0,
y: 0,
blur: 0,
spread: 0,
inset: false,
color: '#000000',
alpha: 1,
...object
})
export default { export default {
// 'Value' and 'Fallback' can be undefined, but if they are // 'Value' and 'Fallback' can be undefined, but if they are
// initially vue won't detect it when they become something else // initially vue won't detect it when they become something else
@ -15,7 +26,7 @@ export default {
return { return {
selectedId: 0, selectedId: 0,
// TODO there are some bugs regarding display of array (it's not getting updated when deleting for some reason) // TODO there are some bugs regarding display of array (it's not getting updated when deleting for some reason)
cValue: this.value || this.fallback || [] cValue: (this.value || this.fallback || []).map(toModel)
} }
}, },
components: { components: {
@ -24,12 +35,12 @@ export default {
}, },
methods: { methods: {
add () { add () {
this.cValue.push(Object.assign({}, this.selected)) this.cValue.push(toModel(this.selected))
this.selectedId = this.cValue.length - 1 this.selectedId = this.cValue.length - 1
}, },
del () { del () {
this.cValue.splice(this.selectedId, 1) this.cValue.splice(this.selectedId, 1)
this.selectedId = this.cValue.length === 0 ? undefined : this.selectedId - 1 this.selectedId = this.cValue.length === 0 ? undefined : Math.max(this.selectedId - 1, 0)
}, },
moveUp () { moveUp () {
const movable = this.cValue.splice(this.selectedId, 1)[0] const movable = this.cValue.splice(this.selectedId, 1)[0]
@ -46,19 +57,24 @@ export default {
this.cValue = this.value || this.fallback this.cValue = this.value || this.fallback
}, },
computed: { computed: {
anyShadows () {
return this.cValue.length > 0
},
anyShadowsFallback () {
return this.fallback.length > 0
},
selected () { selected () {
if (this.ready && this.cValue.length > 0) { if (this.ready && this.anyShadows) {
return this.cValue[this.selectedId] return this.cValue[this.selectedId]
} else { } else {
return { return toModel({})
x: 0,
y: 0,
blur: 0,
spread: 0,
inset: false,
color: '#000000',
alpha: 1
} }
},
currentFallback () {
if (this.ready && this.anyShadowsFallback) {
return this.fallback[this.selectedId]
} else {
return toModel({})
} }
}, },
moveUpValid () { moveUpValid () {
@ -80,7 +96,7 @@ export default {
}, },
style () { style () {
return this.ready ? { return this.ready ? {
boxShadow: getCssShadow(this.cValue) boxShadow: getCssShadow(this.fallback)
} : {} } : {}
} }
} }

View File

@ -191,15 +191,20 @@
v-model="selected.color" v-model="selected.color"
:disabled="!present" :disabled="!present"
:label="$t('settings.style.common.color')" :label="$t('settings.style.common.color')"
:fallback="currentFallback.color"
:show-optional-tickbox="false"
name="shadow" name="shadow"
/> />
<OpacityInput <OpacityInput
v-model="selected.alpha" v-model="selected.alpha"
:disabled="!present" :disabled="!present"
/> />
<p> <i18n
{{ $t('settings.style.shadows.hint') }} path="settings.style.shadows.hintV3"
</p> tag="p"
>
<code>--variable,mod</code>
</i18n>
</div> </div>
</div> </div>
</template> </template>

View File

@ -223,7 +223,13 @@
box-shadow: 1px 1px 4px rgba(0, 0, 0, 0.6); box-shadow: 1px 1px 4px rgba(0, 0, 0, 0.6);
box-shadow: var(--panelShadow); box-shadow: var(--panelShadow);
background-color: $fallback--bg; background-color: $fallback--bg;
background-color: var(--bg, $fallback--bg); background-color: var(--popover, $fallback--bg);
color: $fallback--link;
color: var(--popoverText, $fallback--link);
--faint: var(--popoverFaintText, $fallback--faint);
--faintLink: var(--popoverFaintLink, $fallback--faint);
--lightText: var(--popoverLightText, $fallback--lightText);
--icon: var(--popoverIcon, $fallback--icon);
.button-icon:before { .button-icon:before {
width: 1.1em; width: 1.1em;
@ -289,7 +295,13 @@
&:hover { &:hover {
background-color: $fallback--lightBg; background-color: $fallback--lightBg;
background-color: var(--lightBg, $fallback--lightBg); background-color: var(--selectedMenuPopover, $fallback--lightBg);
color: $fallback--text;
color: var(--selectedMenuPopoverText, $fallback--text);
--faint: var(--selectedMenuPopoverFaintText, $fallback--faint);
--faintLink: var(--selectedMenuPopoverFaintLink, $fallback--faint);
--lightText: var(--selectedMenuPopoverLightText, $fallback--lightText);
--icon: var(--selectedMenuPopoverIcon, $fallback--icon);
} }
} }
} }

View File

@ -468,7 +468,15 @@ $status-margin: 0.75em;
&_focused { &_focused {
background-color: $fallback--lightBg; background-color: $fallback--lightBg;
background-color: var(--lightBg, $fallback--lightBg); background-color: var(--selectedPost, $fallback--lightBg);
color: $fallback--text;
color: var(--selectedPostText, $fallback--text);
--lightText: var(--selectedPostLightText, $fallback--light);
--faint: var(--selectedPostFaintText, $fallback--faint);
--faintLink: var(--selectedPostFaintLink, $fallback--faint);
--postLink: var(--selectedPostPostLink, $fallback--faint);
--postFaintLink: var(--selectedPostFaintPostLink, $fallback--faint);
--icon: var(--selectedPostIcon, $fallback--icon);
} }
.timeline & { .timeline & {
@ -596,8 +604,6 @@ $status-margin: 0.75em;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
margin: 0 0.4em 0 0.2em; margin: 0 0.4em 0 0.2em;
color: $fallback--faint;
color: var(--faint, $fallback--faint);
} }
.replies-separator { .replies-separator {
@ -659,6 +665,11 @@ $status-margin: 0.75em;
line-height: 1.4em; line-height: 1.4em;
white-space: pre-wrap; white-space: pre-wrap;
a {
color: $fallback--link;
color: var(--postLink, $fallback--link);
}
img, video { img, video {
max-width: 100%; max-width: 100%;
max-height: 400px; max-height: 400px;

View File

@ -51,7 +51,7 @@
img { img {
height: 100%; height: 100%;
&:hover { &:hover {
filter: drop-shadow(0 0 5px var(--link, $fallback--link)); filter: drop-shadow(0 0 5px var(--accent, $fallback--link));
} }
} }
} }

View File

@ -1,4 +1,6 @@
<template> <template>
<div class="preview-container">
<div class="underlay underlay-preview" />
<div class="panel dummy"> <div class="panel dummy">
<div class="panel-heading"> <div class="panel-heading">
<div class="title"> <div class="title">
@ -19,7 +21,7 @@
</div> </div>
<div class="panel-body theme-preview-content"> <div class="panel-body theme-preview-content">
<div class="post"> <div class="post">
<div class="avatar"> <div class="avatar still-image">
( ͡° ͜ʖ ͡°) ( ͡° ͜ʖ ͡°)
</div> </div>
<div class="content"> <div class="content">
@ -98,4 +100,18 @@
</div> </div>
</div> </div>
</div> </div>
</div>
</template> </template>
<style lang="scss">
.preview-container {
position: relative;
}
.underlay-preview {
position: absolute;
top: 0;
bottom: 0;
left: 10px;
right: 10px;
}
</style>

View File

@ -1,6 +1,29 @@
import { rgb2hex, hex2rgb, getContrastRatio, alphaBlend } from '../../services/color_convert/color_convert.js'
import { set, delete as del } from 'vue' import { set, delete as del } from 'vue'
import { generateColors, generateShadows, generateRadii, generateFonts, composePreset, getThemes } from '../../services/style_setter/style_setter.js' import {
rgb2hex,
hex2rgb,
getContrastRatioLayers
} from '../../services/color_convert/color_convert.js'
import {
DEFAULT_SHADOWS,
generateColors,
generateShadows,
generateRadii,
generateFonts,
composePreset,
getThemes,
shadows2to3,
colors2to3
} from '../../services/style_setter/style_setter.js'
import {
SLOT_INHERITANCE
} from '../../services/theme_data/pleromafe.js'
import {
CURRENT_VERSION,
OPACITIES,
getLayers,
getOpacitySlot
} from '../../services/theme_data/theme_data.service.js'
import ColorInput from '../color_input/color_input.vue' import ColorInput from '../color_input/color_input.vue'
import RangeInput from '../range_input/range_input.vue' import RangeInput from '../range_input/range_input.vue'
import OpacityInput from '../opacity_input/opacity_input.vue' import OpacityInput from '../opacity_input/opacity_input.vue'
@ -24,11 +47,22 @@ const v1OnlyNames = [
'cOrange' 'cOrange'
].map(_ => _ + 'ColorLocal') ].map(_ => _ + 'ColorLocal')
const colorConvert = (color) => {
if (color.startsWith('--') || color === 'transparent') {
return color
} else {
return hex2rgb(color)
}
}
export default { export default {
data () { data () {
return { return {
availableStyles: [], availableStyles: [],
selected: this.$store.getters.mergedConfig.theme, selected: this.$store.getters.mergedConfig.theme,
themeWarning: undefined,
tempImportFile: undefined,
engineVersion: 0,
previewShadows: {}, previewShadows: {},
previewColors: {}, previewColors: {},
@ -45,51 +79,13 @@ export default {
keepRoundness: false, keepRoundness: false,
keepFonts: false, keepFonts: false,
textColorLocal: '', ...Object.keys(SLOT_INHERITANCE)
linkColorLocal: '', .map(key => [key, ''])
.reduce((acc, [key, val]) => ({ ...acc, [ key + 'ColorLocal' ]: val }), {}),
bgColorLocal: '', ...Object.keys(OPACITIES)
bgOpacityLocal: undefined, .map(key => [key, ''])
.reduce((acc, [key, val]) => ({ ...acc, [ key + 'OpacityLocal' ]: val }), {}),
fgColorLocal: '',
fgTextColorLocal: undefined,
fgLinkColorLocal: undefined,
btnColorLocal: undefined,
btnTextColorLocal: undefined,
btnOpacityLocal: undefined,
inputColorLocal: undefined,
inputTextColorLocal: undefined,
inputOpacityLocal: undefined,
panelColorLocal: undefined,
panelTextColorLocal: undefined,
panelLinkColorLocal: undefined,
panelFaintColorLocal: undefined,
panelOpacityLocal: undefined,
topBarColorLocal: undefined,
topBarTextColorLocal: undefined,
topBarLinkColorLocal: undefined,
alertErrorColorLocal: undefined,
alertWarningColorLocal: undefined,
badgeOpacityLocal: undefined,
badgeNotificationColorLocal: undefined,
borderColorLocal: undefined,
borderOpacityLocal: undefined,
faintColorLocal: undefined,
faintOpacityLocal: undefined,
faintLinkColorLocal: undefined,
cRedColorLocal: '',
cBlueColorLocal: '',
cGreenColorLocal: '',
cOrangeColorLocal: '',
shadowSelected: undefined, shadowSelected: undefined,
shadowsLocal: {}, shadowsLocal: {},
@ -108,69 +104,105 @@ export default {
created () { created () {
const self = this const self = this
getThemes().then((themesComplete) => { getThemes()
.then((promises) => {
return Promise.all(
Object.entries(promises)
.map(([k, v]) => v.then(res => [k, res]))
)
})
.then(themes => themes.reduce((acc, [k, v]) => {
if (v) {
return {
...acc,
[k]: v
}
} else {
return acc
}
}, {}))
.then((themesComplete) => {
self.availableStyles = themesComplete self.availableStyles = themesComplete
}) })
}, },
mounted () { mounted () {
this.normalizeLocalState(this.$store.getters.mergedConfig.customTheme) this.loadThemeFromLocalStorage()
if (typeof this.shadowSelected === 'undefined') { if (typeof this.shadowSelected === 'undefined') {
this.shadowSelected = this.shadowsAvailable[0] this.shadowSelected = this.shadowsAvailable[0]
} }
}, },
computed: { computed: {
themeWarningHelp () {
if (!this.themeWarning) return
const t = this.$t
const pre = 'settings.style.switcher.help.'
const {
origin,
themeEngineVersion,
type,
noActionsPossible
} = this.themeWarning
if (origin === 'file') {
// Loaded v2 theme from file
if (themeEngineVersion === 2 && type === 'wrong_version') {
return t(pre + 'v2_imported')
}
if (themeEngineVersion > CURRENT_VERSION) {
return t(pre + 'future_version_imported') + ' ' +
(
noActionsPossible
? t(pre + 'snapshot_missing')
: t(pre + 'snapshot_present')
)
}
if (themeEngineVersion < CURRENT_VERSION) {
return t(pre + 'future_version_imported') + ' ' +
(
noActionsPossible
? t(pre + 'snapshot_missing')
: t(pre + 'snapshot_present')
)
}
} else if (origin === 'localStorage') {
if (type === 'snapshot_source_mismatch') {
return t(pre + 'snapshot_source_mismatch')
}
// FE upgraded from v2
if (themeEngineVersion === 2) {
return t(pre + 'upgraded_from_v2')
}
// Admin downgraded FE
if (themeEngineVersion > CURRENT_VERSION) {
return t(pre + 'fe_downgraded') + ' ' +
(
noActionsPossible
? t(pre + 'migration_snapshot_ok')
: t(pre + 'migration_snapshot_gone')
)
}
// Admin upgraded FE
if (themeEngineVersion < CURRENT_VERSION) {
return t(pre + 'fe_upgraded') + ' ' +
(
noActionsPossible
? t(pre + 'migration_snapshot_ok')
: t(pre + 'migration_snapshot_gone')
)
}
}
},
selectedVersion () { selectedVersion () {
return Array.isArray(this.selected) ? 1 : 2 return Array.isArray(this.selected) ? 1 : 2
}, },
currentColors () { currentColors () {
return { return Object.keys(SLOT_INHERITANCE)
bg: this.bgColorLocal, .map(key => [key, this[key + 'ColorLocal']])
text: this.textColorLocal, .reduce((acc, [key, val]) => ({ ...acc, [ key ]: val }), {})
link: this.linkColorLocal,
fg: this.fgColorLocal,
fgText: this.fgTextColorLocal,
fgLink: this.fgLinkColorLocal,
panel: this.panelColorLocal,
panelText: this.panelTextColorLocal,
panelLink: this.panelLinkColorLocal,
panelFaint: this.panelFaintColorLocal,
input: this.inputColorLocal,
inputText: this.inputTextColorLocal,
topBar: this.topBarColorLocal,
topBarText: this.topBarTextColorLocal,
topBarLink: this.topBarLinkColorLocal,
btn: this.btnColorLocal,
btnText: this.btnTextColorLocal,
alertError: this.alertErrorColorLocal,
alertWarning: this.alertWarningColorLocal,
badgeNotification: this.badgeNotificationColorLocal,
faint: this.faintColorLocal,
faintLink: this.faintLinkColorLocal,
border: this.borderColorLocal,
cRed: this.cRedColorLocal,
cBlue: this.cBlueColorLocal,
cGreen: this.cGreenColorLocal,
cOrange: this.cOrangeColorLocal
}
}, },
currentOpacity () { currentOpacity () {
return { return Object.keys(OPACITIES)
bg: this.bgOpacityLocal, .map(key => [key, this[key + 'OpacityLocal']])
btn: this.btnOpacityLocal, .reduce((acc, [key, val]) => ({ ...acc, [ key ]: val }), {})
input: this.inputOpacityLocal,
panel: this.panelOpacityLocal,
topBar: this.topBarOpacityLocal,
border: this.borderOpacityLocal,
faint: this.faintOpacityLocal
}
}, },
currentRadii () { currentRadii () {
return { return {
@ -193,6 +225,7 @@ export default {
}, },
// This needs optimization maybe // This needs optimization maybe
previewContrast () { previewContrast () {
try {
if (!this.previewTheme.colors.bg) return {} if (!this.previewTheme.colors.bg) return {}
const colors = this.previewTheme.colors const colors = this.previewTheme.colors
const opacity = this.previewTheme.opacity const opacity = this.previewTheme.opacity
@ -206,62 +239,52 @@ export default {
laa: ratio >= 3, laa: ratio >= 3,
laaa: ratio >= 4.5 laaa: ratio >= 4.5
}) })
const colorsConverted = Object.entries(colors).reduce((acc, [key, value]) => ({ ...acc, [key]: colorConvert(value) }), {})
// fgsfds :DDDD const ratios = Object.entries(SLOT_INHERITANCE).reduce((acc, [key, value]) => {
const fgs = { const slotIsBaseText = key === 'text' || key === 'link'
text: hex2rgb(colors.text), const slotIsText = slotIsBaseText || (
panelText: hex2rgb(colors.panelText), typeof value === 'object' && value !== null && value.textColor
panelLink: hex2rgb(colors.panelLink), )
btnText: hex2rgb(colors.btnText), if (!slotIsText) return acc
topBarText: hex2rgb(colors.topBarText), const { layer, variant } = slotIsBaseText ? { layer: 'bg' } : value
inputText: hex2rgb(colors.inputText), const background = variant || layer
const opacitySlot = getOpacitySlot(background)
const textColors = [
key,
...(background === 'bg' ? ['cRed', 'cGreen', 'cBlue', 'cOrange'] : [])
]
link: hex2rgb(colors.link), const layers = getLayers(
topBarLink: hex2rgb(colors.topBarLink), layer,
variant || layer,
opacitySlot,
colorsConverted,
opacity
)
red: hex2rgb(colors.cRed), return {
green: hex2rgb(colors.cGreen), ...acc,
blue: hex2rgb(colors.cBlue), ...textColors.reduce((acc, textColorKey) => {
orange: hex2rgb(colors.cOrange) const newKey = slotIsBaseText
? 'bg' + textColorKey[0].toUpperCase() + textColorKey.slice(1)
: textColorKey
return {
...acc,
[newKey]: getContrastRatioLayers(
colorsConverted[textColorKey],
layers,
colorsConverted[textColorKey]
)
} }
}, {})
const bgs = {
bg: hex2rgb(colors.bg),
btn: hex2rgb(colors.btn),
panel: hex2rgb(colors.panel),
topBar: hex2rgb(colors.topBar),
input: hex2rgb(colors.input),
alertError: hex2rgb(colors.alertError),
alertWarning: hex2rgb(colors.alertWarning),
badgeNotification: hex2rgb(colors.badgeNotification)
}
/* This is a bit confusing because "bottom layer" used is text color
* This is done to get worst case scenario when background below transparent
* layer matches text color, making it harder to read the lower alpha is.
*/
const ratios = {
bgText: getContrastRatio(alphaBlend(bgs.bg, opacity.bg, fgs.text), fgs.text),
bgLink: getContrastRatio(alphaBlend(bgs.bg, opacity.bg, fgs.link), fgs.link),
bgRed: getContrastRatio(alphaBlend(bgs.bg, opacity.bg, fgs.red), fgs.red),
bgGreen: getContrastRatio(alphaBlend(bgs.bg, opacity.bg, fgs.green), fgs.green),
bgBlue: getContrastRatio(alphaBlend(bgs.bg, opacity.bg, fgs.blue), fgs.blue),
bgOrange: getContrastRatio(alphaBlend(bgs.bg, opacity.bg, fgs.orange), fgs.orange),
tintText: getContrastRatio(alphaBlend(bgs.bg, 0.5, fgs.panelText), fgs.text),
panelText: getContrastRatio(alphaBlend(bgs.panel, opacity.panel, fgs.panelText), fgs.panelText),
panelLink: getContrastRatio(alphaBlend(bgs.panel, opacity.panel, fgs.panelLink), fgs.panelLink),
btnText: getContrastRatio(alphaBlend(bgs.btn, opacity.btn, fgs.btnText), fgs.btnText),
inputText: getContrastRatio(alphaBlend(bgs.input, opacity.input, fgs.inputText), fgs.inputText),
topBarText: getContrastRatio(alphaBlend(bgs.topBar, opacity.topBar, fgs.topBarText), fgs.topBarText),
topBarLink: getContrastRatio(alphaBlend(bgs.topBar, opacity.topBar, fgs.topBarLink), fgs.topBarLink)
} }
}, {})
return Object.entries(ratios).reduce((acc, [k, v]) => { acc[k] = hints(v); return acc }, {}) return Object.entries(ratios).reduce((acc, [k, v]) => { acc[k] = hints(v); return acc }, {})
} catch (e) {
console.warn('Failure computing contrasts', e)
}
}, },
previewRules () { previewRules () {
if (!this.preview.rules) return '' if (!this.preview.rules) return ''
@ -272,7 +295,7 @@ export default {
].join(';') ].join(';')
}, },
shadowsAvailable () { shadowsAvailable () {
return Object.keys(this.previewTheme.shadows).sort() return Object.keys(DEFAULT_SHADOWS).sort()
}, },
currentShadowOverriden: { currentShadowOverriden: {
get () { get () {
@ -287,7 +310,7 @@ export default {
} }
}, },
currentShadowFallback () { currentShadowFallback () {
return this.previewTheme.shadows[this.shadowSelected] return (this.previewTheme.shadows || {})[this.shadowSelected]
}, },
currentShadow: { currentShadow: {
get () { get () {
@ -309,27 +332,34 @@ export default {
!this.keepColor !this.keepColor
) )
const theme = {} const source = {
themeEngineVersion: CURRENT_VERSION
}
if (this.keepFonts || saveEverything) { if (this.keepFonts || saveEverything) {
theme.fonts = this.fontsLocal source.fonts = this.fontsLocal
} }
if (this.keepShadows || saveEverything) { if (this.keepShadows || saveEverything) {
theme.shadows = this.shadowsLocal source.shadows = this.shadowsLocal
} }
if (this.keepOpacity || saveEverything) { if (this.keepOpacity || saveEverything) {
theme.opacity = this.currentOpacity source.opacity = this.currentOpacity
} }
if (this.keepColor || saveEverything) { if (this.keepColor || saveEverything) {
theme.colors = this.currentColors source.colors = this.currentColors
} }
if (this.keepRoundness || saveEverything) { if (this.keepRoundness || saveEverything) {
theme.radii = this.currentRadii source.radii = this.currentRadii
}
const theme = {
themeEngineVersion: CURRENT_VERSION,
...this.previewTheme
} }
return { return {
// To separate from other random JSON files and possible future theme formats // To separate from other random JSON files and possible future source formats
_pleroma_theme_version: 2, theme _pleroma_theme_version: 2, theme, source
} }
} }
}, },
@ -346,10 +376,128 @@ export default {
Checkbox Checkbox
}, },
methods: { methods: {
loadTheme (
{
theme,
source,
_pleroma_theme_version: fileVersion
},
origin,
forceUseSource = false
) {
this.dismissWarning()
if (!source && !theme) {
throw new Error('Can\'t load theme: empty')
}
const version = (origin === 'localStorage' && !theme.colors)
? 'l1'
: fileVersion
const snapshotEngineVersion = (theme || {}).themeEngineVersion
const themeEngineVersion = (source || {}).themeEngineVersion || 2
const versionsMatch = themeEngineVersion === CURRENT_VERSION
const sourceSnapshotMismatch = (
theme !== undefined &&
source !== undefined &&
themeEngineVersion !== snapshotEngineVersion
)
// Force loading of source if user requested it or if snapshot
// is unavailable
const forcedSourceLoad = (source && forceUseSource) || !theme
if (!(versionsMatch && !sourceSnapshotMismatch) &&
!forcedSourceLoad &&
version !== 'l1' &&
origin !== 'defaults'
) {
if (sourceSnapshotMismatch && origin === 'localStorage') {
this.themeWarning = {
origin,
themeEngineVersion,
type: 'snapshot_source_mismatch'
}
} else if (!theme) {
this.themeWarning = {
origin,
noActionsPossible: true,
themeEngineVersion,
type: 'no_snapshot_old_version'
}
} else if (!versionsMatch) {
this.themeWarning = {
origin,
noActionsPossible: !source,
themeEngineVersion,
type: 'wrong_version'
}
}
}
this.normalizeLocalState(theme, version, source, forcedSourceLoad)
},
forceLoadLocalStorage () {
this.loadThemeFromLocalStorage(true)
},
dismissWarning () {
this.themeWarning = undefined
this.tempImportFile = undefined
},
forceLoad () {
const { origin } = this.themeWarning
switch (origin) {
case 'localStorage':
this.loadThemeFromLocalStorage(true)
break
case 'file':
this.onImport(this.tempImportFile, true)
break
}
this.dismissWarning()
},
forceSnapshot () {
const { origin } = this.themeWarning
switch (origin) {
case 'localStorage':
this.loadThemeFromLocalStorage(false, true)
break
case 'file':
console.err('Forcing snapshout from file is not supported yet')
break
}
this.dismissWarning()
},
loadThemeFromLocalStorage (confirmLoadSource = false, forceSnapshot = false) {
const {
customTheme: theme,
customThemeSource: source
} = this.$store.getters.mergedConfig
if (!theme && !source) {
// Anon user or never touched themes
this.loadTheme(
this.$store.state.instance.themeData,
'defaults',
confirmLoadSource
)
} else {
this.loadTheme(
{
theme,
source: forceSnapshot ? theme : source
},
'localStorage',
confirmLoadSource
)
}
},
setCustomTheme () { setCustomTheme () {
this.$store.dispatch('setOption', { this.$store.dispatch('setOption', {
name: 'customTheme', name: 'customTheme',
value: { value: {
themeEngineVersion: CURRENT_VERSION,
...this.previewTheme
}
})
this.$store.dispatch('setOption', {
name: 'customThemeSource',
value: {
themeEngineVersion: CURRENT_VERSION,
shadows: this.shadowsLocal, shadows: this.shadowsLocal,
fonts: this.fontsLocal, fonts: this.fontsLocal,
opacity: this.currentOpacity, opacity: this.currentOpacity,
@ -358,21 +506,27 @@ export default {
} }
}) })
}, },
onImport (parsed) { updatePreviewColorsAndShadows () {
if (parsed._pleroma_theme_version === 1) { this.previewColors = generateColors({
this.normalizeLocalState(parsed, 1) opacity: this.currentOpacity,
} else if (parsed._pleroma_theme_version === 2) { colors: this.currentColors
this.normalizeLocalState(parsed.theme, 2) })
} this.previewShadows = generateShadows(
{ shadows: this.shadowsLocal, opacity: this.previewTheme.opacity, themeEngineVersion: this.engineVersion },
this.previewColors.theme.colors,
this.previewColors.mod
)
},
onImport (parsed, forceSource = false) {
this.tempImportFile = parsed
this.loadTheme(parsed, 'file', forceSource)
}, },
importValidator (parsed) { importValidator (parsed) {
const version = parsed._pleroma_theme_version const version = parsed._pleroma_theme_version
return version >= 1 || version <= 2 return version >= 1 || version <= 2
}, },
clearAll () { clearAll () {
const state = this.$store.getters.mergedConfig.customTheme this.loadThemeFromLocalStorage()
const version = state.colors ? 2 : 'l1'
this.normalizeLocalState(this.$store.getters.mergedConfig.customTheme, version)
}, },
// Clears all the extra stuff when loading V1 theme // Clears all the extra stuff when loading V1 theme
@ -411,19 +565,37 @@ export default {
/** /**
* This applies stored theme data onto form. Supports three versions of data: * This applies stored theme data onto form. Supports three versions of data:
* v3 (version >= 3) - newest version of themes which supports snapshots for better compatiblity
* v2 (version = 2) - newer version of themes. * v2 (version = 2) - newer version of themes.
* v1 (version = 1) - older version of themes (import from file) * v1 (version = 1) - older version of themes (import from file)
* v1l (version = l1) - older version of theme (load from local storage) * v1l (version = l1) - older version of theme (load from local storage)
* v1 and v1l differ because of way themes were stored/exported. * v1 and v1l differ because of way themes were stored/exported.
* @param {Object} input - input data * @param {Object} theme - theme data (snapshot)
* @param {Number} version - version of data. 0 means try to guess based on data. "l1" means v1, locastorage type * @param {Number} version - version of data. 0 means try to guess based on data. "l1" means v1, locastorage type
* @param {Object} source - theme source - this will be used if compatible
* @param {Boolean} source - by default source won't be used if version doesn't match since it might render differently
* this allows importing source anyway
*/ */
normalizeLocalState (input, version = 0) { normalizeLocalState (theme, version = 0, source, forceSource = false) {
const colors = input.colors || input let input
if (typeof source !== 'undefined') {
if (forceSource || source.themeEngineVersion === CURRENT_VERSION) {
input = source
version = source.themeEngineVersion
} else {
input = theme
}
} else {
input = theme
}
const radii = input.radii || input const radii = input.radii || input
const opacity = input.opacity const opacity = input.opacity
const shadows = input.shadows || {} const shadows = input.shadows || {}
const fonts = input.fonts || {} const fonts = input.fonts || {}
const colors = !input.themeEngineVersion
? colors2to3(input.colors || input)
: input.colors || input
if (version === 0) { if (version === 0) {
if (input.version) version = input.version if (input.version) version = input.version
@ -437,6 +609,8 @@ export default {
} }
} }
this.engineVersion = version
// Stuff that differs between V1 and V2 // Stuff that differs between V1 and V2
if (version === 1) { if (version === 1) {
this.fgColorLocal = rgb2hex(colors.btn) this.fgColorLocal = rgb2hex(colors.btn)
@ -445,7 +619,7 @@ export default {
if (!this.keepColor) { if (!this.keepColor) {
this.clearV1() this.clearV1()
const keys = new Set(version !== 1 ? Object.keys(colors) : []) const keys = new Set(version !== 1 ? Object.keys(SLOT_INHERITANCE) : [])
if (version === 1 || version === 'l1') { if (version === 1 || version === 'l1') {
keys keys
.add('bg') .add('bg')
@ -457,7 +631,17 @@ export default {
} }
keys.forEach(key => { keys.forEach(key => {
this[key + 'ColorLocal'] = rgb2hex(colors[key]) const color = colors[key]
const hex = rgb2hex(colors[key])
this[key + 'ColorLocal'] = hex === '#aN' ? color : hex
})
}
if (opacity && !this.keepOpacity) {
this.clearOpacity()
Object.entries(opacity).forEach(([k, v]) => {
if (typeof v === 'undefined' || v === null || Number.isNaN(v)) return
this[k + 'OpacityLocal'] = v
}) })
} }
@ -472,7 +656,11 @@ export default {
if (!this.keepShadows) { if (!this.keepShadows) {
this.clearShadows() this.clearShadows()
if (version === 2) {
this.shadowsLocal = shadows2to3(shadows, this.previewTheme.opacity)
} else {
this.shadowsLocal = shadows this.shadowsLocal = shadows
}
this.shadowSelected = this.shadowsAvailable[0] this.shadowSelected = this.shadowsAvailable[0]
} }
@ -480,14 +668,6 @@ export default {
this.clearFonts() this.clearFonts()
this.fontsLocal = fonts this.fontsLocal = fonts
} }
if (opacity && !this.keepOpacity) {
this.clearOpacity()
Object.entries(opacity).forEach(([k, v]) => {
if (typeof v === 'undefined' || v === null || Number.isNaN(v)) return
this[k + 'OpacityLocal'] = v
})
}
} }
}, },
watch: { watch: {
@ -502,8 +682,9 @@ export default {
}, },
shadowsLocal: { shadowsLocal: {
handler () { handler () {
if (Object.getOwnPropertyNames(this.previewColors).length === 1) return
try { try {
this.previewShadows = generateShadows({ shadows: this.shadowsLocal }) this.updatePreviewColorsAndShadows()
this.shadowsInvalid = false this.shadowsInvalid = false
} catch (e) { } catch (e) {
this.shadowsInvalid = true this.shadowsInvalid = true
@ -526,27 +707,24 @@ export default {
}, },
currentColors () { currentColors () {
try { try {
this.previewColors = generateColors({ this.updatePreviewColorsAndShadows()
opacity: this.currentOpacity,
colors: this.currentColors
})
this.colorsInvalid = false this.colorsInvalid = false
this.shadowsInvalid = false
} catch (e) { } catch (e) {
this.colorsInvalid = true this.colorsInvalid = true
this.shadowsInvalid = true
console.warn(e) console.warn(e)
} }
}, },
currentOpacity () { currentOpacity () {
try { try {
this.previewColors = generateColors({ this.updatePreviewColorsAndShadows()
opacity: this.currentOpacity,
colors: this.currentColors
})
} catch (e) { } catch (e) {
console.warn(e) console.warn(e)
} }
}, },
selected () { selected () {
this.dismissWarning()
if (this.selectedVersion === 1) { if (this.selectedVersion === 1) {
if (!this.keepRoundness) { if (!this.keepRoundness) {
this.clearRoundness() this.clearRoundness()
@ -573,7 +751,7 @@ export default {
this.cOrangeColorLocal = this.selected[8] this.cOrangeColorLocal = this.selected[8]
} }
} else if (this.selectedVersion >= 2) { } else if (this.selectedVersion >= 2) {
this.normalizeLocalState(this.selected.theme, 2) this.normalizeLocalState(this.selected.theme, 2, this.selected.source)
} }
} }
} }

View File

@ -1,5 +1,15 @@
@import '../../_variables.scss'; @import '../../_variables.scss';
.style-switcher { .style-switcher {
.theme-warning {
display: flex;
align-items: baseline;
margin-bottom: .5em;
.buttons {
.btn {
margin-bottom: .5em;
}
}
}
.preset-switcher { .preset-switcher {
margin-right: 1em; margin-right: 1em;
} }
@ -15,10 +25,16 @@
&.disabled { &.disabled {
input, select { input, select {
&:not(.exclude-disabled) {
opacity: .5 opacity: .5
} }
} }
.opt {
margin: .5em;
}
.color-input {
flex: 0 0 0;
} }
input, select { input, select {
@ -26,15 +42,6 @@
margin: 0; margin: 0;
flex: 0; flex: 0;
&[type=color] {
padding: 1px;
cursor: pointer;
height: 29px;
min-width: 2em;
border: none;
align-self: stretch;
}
&[type=number] { &[type=number] {
min-width: 5em; min-width: 5em;
} }
@ -42,13 +49,6 @@
&[type=range] { &[type=range] {
flex: 1; flex: 1;
min-width: 3em; min-width: 3em;
}
&[type=checkbox] + label {
margin: 6px 0;
}
&:not([type=number]):not([type=text]) {
align-self: flex-start; align-self: flex-start;
} }
} }

View File

@ -2,7 +2,53 @@
<div class="style-switcher"> <div class="style-switcher">
<div class="presets-container"> <div class="presets-container">
<div class="save-load"> <div class="save-load">
<export-import <div
v-if="themeWarning"
class="theme-warning"
>
<div class="alert warning">
{{ themeWarningHelp }}
</div>
<div class="buttons">
<template v-if="themeWarning.type === 'snapshot_source_mismatch'">
<button
class="btn"
@click="forceLoad"
>
{{ $t('settings.style.switcher.use_source') }}
</button>
<button
class="btn"
@click="forceSnapshot"
>
{{ $t('settings.style.switcher.use_snapshot') }}
</button>
</template>
<template v-else-if="themeWarning.noActionsPossible">
<button
class="btn"
@click="dismissWarning"
>
{{ $t('general.dismiss') }}
</button>
</template>
<template v-else>
<button
class="btn"
@click="forceLoad"
>
{{ $t('settings.style.switcher.load_theme') }}
</button>
<button
class="btn"
@click="dismissWarning"
>
{{ $t('settings.style.switcher.keep_as_is') }}
</button>
</template>
</div>
</div>
<ExportImport
:export-object="exportedTheme" :export-object="exportedTheme"
:export-label="$t(&quot;settings.export_theme&quot;)" :export-label="$t(&quot;settings.export_theme&quot;)"
:import-label="$t(&quot;settings.import_theme&quot;)" :import-label="$t(&quot;settings.import_theme&quot;)"
@ -27,8 +73,8 @@
:key="style.name" :key="style.name"
:value="style" :value="style"
:style="{ :style="{
backgroundColor: style[1] || style.theme.colors.bg, backgroundColor: style[1] || (style.theme || style.source).colors.bg,
color: style[3] || style.theme.colors.text color: style[3] || (style.theme || style.source).colors.text
}" }"
> >
{{ style[0] || style.name }} {{ style[0] || style.name }}
@ -38,7 +84,7 @@
</label> </label>
</div> </div>
</template> </template>
</export-import> </ExportImport>
</div> </div>
<div class="save-load-options"> <div class="save-load-options">
<span class="keep-option"> <span class="keep-option">
@ -70,9 +116,7 @@
</div> </div>
</div> </div>
<div class="preview-container">
<preview :style="previewRules" /> <preview :style="previewRules" />
</div>
<keep-alive> <keep-alive>
<tab-switcher key="style-tweak"> <tab-switcher key="style-tweak">
@ -106,7 +150,7 @@
<OpacityInput <OpacityInput
v-model="bgOpacityLocal" v-model="bgOpacityLocal"
name="bgOpacity" name="bgOpacity"
:fallback="previewTheme.opacity.bg || 1" :fallback="previewTheme.opacity.bg"
/> />
<ColorInput <ColorInput
v-model="textColorLocal" v-model="textColorLocal"
@ -114,10 +158,19 @@
:label="$t('settings.text')" :label="$t('settings.text')"
/> />
<ContrastRatio :contrast="previewContrast.bgText" /> <ContrastRatio :contrast="previewContrast.bgText" />
<ColorInput
v-model="accentColorLocal"
name="accentColor"
:fallback="previewTheme.colors.link"
:label="$t('settings.accent')"
:show-optional-tickbox="typeof linkColorLocal !== 'undefined'"
/>
<ColorInput <ColorInput
v-model="linkColorLocal" v-model="linkColorLocal"
name="linkColor" name="linkColor"
:fallback="previewTheme.colors.accent"
:label="$t('settings.links')" :label="$t('settings.links')"
:show-optional-tickbox="typeof accentColorLocal !== 'undefined'"
/> />
<ContrastRatio :contrast="previewContrast.bgLink" /> <ContrastRatio :contrast="previewContrast.bgLink" />
</div> </div>
@ -148,13 +201,13 @@
name="cRedColor" name="cRedColor"
:label="$t('settings.cRed')" :label="$t('settings.cRed')"
/> />
<ContrastRatio :contrast="previewContrast.bgRed" /> <ContrastRatio :contrast="previewContrast.bgCRed" />
<ColorInput <ColorInput
v-model="cBlueColorLocal" v-model="cBlueColorLocal"
name="cBlueColor" name="cBlueColor"
:label="$t('settings.cBlue')" :label="$t('settings.cBlue')"
/> />
<ContrastRatio :contrast="previewContrast.bgBlue" /> <ContrastRatio :contrast="previewContrast.bgCBlue" />
</div> </div>
<div class="color-item"> <div class="color-item">
<ColorInput <ColorInput
@ -162,13 +215,13 @@
name="cGreenColor" name="cGreenColor"
:label="$t('settings.cGreen')" :label="$t('settings.cGreen')"
/> />
<ContrastRatio :contrast="previewContrast.bgGreen" /> <ContrastRatio :contrast="previewContrast.bgCGreen" />
<ColorInput <ColorInput
v-model="cOrangeColorLocal" v-model="cOrangeColorLocal"
name="cOrangeColor" name="cOrangeColor"
:label="$t('settings.cOrange')" :label="$t('settings.cOrange')"
/> />
<ContrastRatio :contrast="previewContrast.bgOrange" /> <ContrastRatio :contrast="previewContrast.bgCOrange" />
</div> </div>
<p>{{ $t('settings.theme_help_v2_2') }}</p> <p>{{ $t('settings.theme_help_v2_2') }}</p>
</div> </div>
@ -193,6 +246,14 @@
</button> </button>
</div> </div>
<div class="color-item"> <div class="color-item">
<h4>{{ $t('settings.style.advanced_colors.post') }}</h4>
<ColorInput
v-model="postLinkColorLocal"
name="postLinkColor"
:fallback="previewTheme.colors.accent"
:label="$t('settings.links')"
/>
<ContrastRatio :contrast="previewContrast.postLink" />
<h4>{{ $t('settings.style.advanced_colors.alert') }}</h4> <h4>{{ $t('settings.style.advanced_colors.alert') }}</h4>
<ColorInput <ColorInput
v-model="alertErrorColorLocal" v-model="alertErrorColorLocal"
@ -200,14 +261,53 @@
:label="$t('settings.style.advanced_colors.alert_error')" :label="$t('settings.style.advanced_colors.alert_error')"
:fallback="previewTheme.colors.alertError" :fallback="previewTheme.colors.alertError"
/> />
<ContrastRatio :contrast="previewContrast.alertError" /> <ColorInput
v-model="alertErrorTextColorLocal"
name="alertErrorText"
:label="$t('settings.text')"
:fallback="previewTheme.colors.alertErrorText"
/>
<ContrastRatio
:contrast="previewContrast.alertErrorText"
large="true"
/>
<ColorInput <ColorInput
v-model="alertWarningColorLocal" v-model="alertWarningColorLocal"
name="alertWarning" name="alertWarning"
:label="$t('settings.style.advanced_colors.alert_warning')" :label="$t('settings.style.advanced_colors.alert_warning')"
:fallback="previewTheme.colors.alertWarning" :fallback="previewTheme.colors.alertWarning"
/> />
<ContrastRatio :contrast="previewContrast.alertWarning" /> <ColorInput
v-model="alertWarningTextColorLocal"
name="alertWarningText"
:label="$t('settings.text')"
:fallback="previewTheme.colors.alertWarningText"
/>
<ContrastRatio
:contrast="previewContrast.alertWarningText"
large="true"
/>
<ColorInput
v-model="alertNeutralColorLocal"
name="alertNeutral"
:label="$t('settings.style.advanced_colors.alert_neutral')"
:fallback="previewTheme.colors.alertNeutral"
/>
<ColorInput
v-model="alertNeutralTextColorLocal"
name="alertNeutralText"
:label="$t('settings.text')"
:fallback="previewTheme.colors.alertNeutralText"
/>
<ContrastRatio
:contrast="previewContrast.alertNeutralText"
large="true"
/>
<OpacityInput
v-model="alertOpacityLocal"
name="alertOpacity"
:fallback="previewTheme.opacity.alert"
/>
</div> </div>
<div class="color-item"> <div class="color-item">
<h4>{{ $t('settings.style.advanced_colors.badge') }}</h4> <h4>{{ $t('settings.style.advanced_colors.badge') }}</h4>
@ -217,19 +317,30 @@
:label="$t('settings.style.advanced_colors.badge_notification')" :label="$t('settings.style.advanced_colors.badge_notification')"
:fallback="previewTheme.colors.badgeNotification" :fallback="previewTheme.colors.badgeNotification"
/> />
<ColorInput
v-model="badgeNotificationTextColorLocal"
name="badgeNotificationText"
:label="$t('settings.text')"
:fallback="previewTheme.colors.badgeNotificationText"
/>
<ContrastRatio
:contrast="previewContrast.badgeNotificationText"
large="true"
/>
</div> </div>
<div class="color-item"> <div class="color-item">
<h4>{{ $t('settings.style.advanced_colors.panel_header') }}</h4> <h4>{{ $t('settings.style.advanced_colors.panel_header') }}</h4>
<ColorInput <ColorInput
v-model="panelColorLocal" v-model="panelColorLocal"
name="panelColor" name="panelColor"
:fallback="fgColorLocal" :fallback="previewTheme.colors.panel"
:label="$t('settings.background')" :label="$t('settings.background')"
/> />
<OpacityInput <OpacityInput
v-model="panelOpacityLocal" v-model="panelOpacityLocal"
name="panelOpacity" name="panelOpacity"
:fallback="previewTheme.opacity.panel || 1" :fallback="previewTheme.opacity.panel"
:disabled="panelColorLocal === 'transparent'"
/> />
<ColorInput <ColorInput
v-model="panelTextColorLocal" v-model="panelTextColorLocal"
@ -239,7 +350,7 @@
/> />
<ContrastRatio <ContrastRatio
:contrast="previewContrast.panelText" :contrast="previewContrast.panelText"
large="1" large="true"
/> />
<ColorInput <ColorInput
v-model="panelLinkColorLocal" v-model="panelLinkColorLocal"
@ -249,7 +360,7 @@
/> />
<ContrastRatio <ContrastRatio
:contrast="previewContrast.panelLink" :contrast="previewContrast.panelLink"
large="1" large="true"
/> />
</div> </div>
<div class="color-item"> <div class="color-item">
@ -257,7 +368,7 @@
<ColorInput <ColorInput
v-model="topBarColorLocal" v-model="topBarColorLocal"
name="topBarColor" name="topBarColor"
:fallback="fgColorLocal" :fallback="previewTheme.colors.topBar"
:label="$t('settings.background')" :label="$t('settings.background')"
/> />
<ColorInput <ColorInput
@ -280,13 +391,14 @@
<ColorInput <ColorInput
v-model="inputColorLocal" v-model="inputColorLocal"
name="inputColor" name="inputColor"
:fallback="fgColorLocal" :fallback="previewTheme.colors.input"
:label="$t('settings.background')" :label="$t('settings.background')"
/> />
<OpacityInput <OpacityInput
v-model="inputOpacityLocal" v-model="inputOpacityLocal"
name="inputOpacity" name="inputOpacity"
:fallback="previewTheme.opacity.input || 1" :fallback="previewTheme.opacity.input"
:disabled="inputColorLocal === 'transparent'"
/> />
<ColorInput <ColorInput
v-model="inputTextColorLocal" v-model="inputTextColorLocal"
@ -301,13 +413,14 @@
<ColorInput <ColorInput
v-model="btnColorLocal" v-model="btnColorLocal"
name="btnColor" name="btnColor"
:fallback="fgColorLocal" :fallback="previewTheme.colors.btn"
:label="$t('settings.background')" :label="$t('settings.background')"
/> />
<OpacityInput <OpacityInput
v-model="btnOpacityLocal" v-model="btnOpacityLocal"
name="btnOpacity" name="btnOpacity"
:fallback="previewTheme.opacity.btn || 1" :fallback="previewTheme.opacity.btn"
:disabled="btnColorLocal === 'transparent'"
/> />
<ColorInput <ColorInput
v-model="btnTextColorLocal" v-model="btnTextColorLocal"
@ -316,6 +429,124 @@
:label="$t('settings.text')" :label="$t('settings.text')"
/> />
<ContrastRatio :contrast="previewContrast.btnText" /> <ContrastRatio :contrast="previewContrast.btnText" />
<ColorInput
v-model="btnPanelTextColorLocal"
name="btnPanelTextColor"
:fallback="previewTheme.colors.btnPanelText"
:label="$t('settings.style.advanced_colors.panel_header')"
/>
<ContrastRatio :contrast="previewContrast.btnPanelText" />
<ColorInput
v-model="btnTopBarTextColorLocal"
name="btnTopBarTextColor"
:fallback="previewTheme.colors.btnTopBarText"
:label="$t('settings.style.advanced_colors.top_bar')"
/>
<ContrastRatio :contrast="previewContrast.btnTopBarText" />
<h5>{{ $t('settings.style.advanced_colors.pressed') }}</h5>
<ColorInput
v-model="btnPressedColorLocal"
name="btnPressedColor"
:fallback="previewTheme.colors.btnPressed"
:label="$t('settings.background')"
/>
<ColorInput
v-model="btnPressedTextColorLocal"
name="btnPressedTextColor"
:fallback="previewTheme.colors.btnPressedText"
:label="$t('settings.text')"
/>
<ContrastRatio :contrast="previewContrast.btnPressedText" />
<ColorInput
v-model="btnPressedPanelTextColorLocal"
name="btnPressedPanelTextColor"
:fallback="previewTheme.colors.btnPressedPanelText"
:label="$t('settings.style.advanced_colors.panel_header')"
/>
<ContrastRatio :contrast="previewContrast.btnPressedPanelText" />
<ColorInput
v-model="btnPressedTopBarTextColorLocal"
name="btnPressedTopBarTextColor"
:fallback="previewTheme.colors.btnPressedTopBarText"
:label="$t('settings.style.advanced_colors.top_bar')"
/>
<ContrastRatio :contrast="previewContrast.btnPressedTopBarText" />
<h5>{{ $t('settings.style.advanced_colors.disabled') }}</h5>
<ColorInput
v-model="btnDisabledColorLocal"
name="btnDisabledColor"
:fallback="previewTheme.colors.btnDisabled"
:label="$t('settings.background')"
/>
<ColorInput
v-model="btnDisabledTextColorLocal"
name="btnDisabledTextColor"
:fallback="previewTheme.colors.btnDisabledText"
:label="$t('settings.text')"
/>
<ColorInput
v-model="btnDisabledPanelTextColorLocal"
name="btnDisabledPanelTextColor"
:fallback="previewTheme.colors.btnDisabledPanelText"
:label="$t('settings.style.advanced_colors.panel_header')"
/>
<ColorInput
v-model="btnDisabledTopBarTextColorLocal"
name="btnDisabledTopBarTextColor"
:fallback="previewTheme.colors.btnDisabledTopBarText"
:label="$t('settings.style.advanced_colors.top_bar')"
/>
<h5>{{ $t('settings.style.advanced_colors.toggled') }}</h5>
<ColorInput
v-model="btnToggledColorLocal"
name="btnToggledColor"
:fallback="previewTheme.colors.btnToggled"
:label="$t('settings.background')"
/>
<ColorInput
v-model="btnToggledTextColorLocal"
name="btnToggledTextColor"
:fallback="previewTheme.colors.btnToggledText"
:label="$t('settings.text')"
/>
<ContrastRatio :contrast="previewContrast.btnToggledText" />
<ColorInput
v-model="btnToggledPanelTextColorLocal"
name="btnToggledPanelTextColor"
:fallback="previewTheme.colors.btnToggledPanelText"
:label="$t('settings.style.advanced_colors.panel_header')"
/>
<ContrastRatio :contrast="previewContrast.btnToggledPanelText" />
<ColorInput
v-model="btnToggledTopBarTextColorLocal"
name="btnToggledTopBarTextColor"
:fallback="previewTheme.colors.btnToggledTopBarText"
:label="$t('settings.style.advanced_colors.top_bar')"
/>
<ContrastRatio :contrast="previewContrast.btnToggledTopBarText" />
</div>
<div class="color-item">
<h4>{{ $t('settings.style.advanced_colors.tabs') }}</h4>
<ColorInput
v-model="tabColorLocal"
name="tabColor"
:fallback="previewTheme.colors.tab"
:label="$t('settings.background')"
/>
<ColorInput
v-model="tabTextColorLocal"
name="tabTextColor"
:fallback="previewTheme.colors.tabText"
:label="$t('settings.text')"
/>
<ContrastRatio :contrast="previewContrast.tabText" />
<ColorInput
v-model="tabActiveTextColorLocal"
name="tabActiveTextColor"
:fallback="previewTheme.colors.tabActiveText"
:label="$t('settings.text')"
/>
<ContrastRatio :contrast="previewContrast.tabActiveText" />
</div> </div>
<div class="color-item"> <div class="color-item">
<h4>{{ $t('settings.style.advanced_colors.borders') }}</h4> <h4>{{ $t('settings.style.advanced_colors.borders') }}</h4>
@ -328,7 +559,8 @@
<OpacityInput <OpacityInput
v-model="borderOpacityLocal" v-model="borderOpacityLocal"
name="borderOpacity" name="borderOpacity"
:fallback="previewTheme.opacity.border || 1" :fallback="previewTheme.opacity.border"
:disabled="borderColorLocal === 'transparent'"
/> />
</div> </div>
<div class="color-item"> <div class="color-item">
@ -336,7 +568,7 @@
<ColorInput <ColorInput
v-model="faintColorLocal" v-model="faintColorLocal"
name="faintColor" name="faintColor"
:fallback="previewTheme.colors.faint || 1" :fallback="previewTheme.colors.faint"
:label="$t('settings.text')" :label="$t('settings.text')"
/> />
<ColorInput <ColorInput
@ -354,9 +586,146 @@
<OpacityInput <OpacityInput
v-model="faintOpacityLocal" v-model="faintOpacityLocal"
name="faintOpacity" name="faintOpacity"
:fallback="previewTheme.opacity.faint || 0.5" :fallback="previewTheme.opacity.faint"
/> />
</div> </div>
<div class="color-item">
<h4>{{ $t('settings.style.advanced_colors.underlay') }}</h4>
<ColorInput
v-model="underlayColorLocal"
name="underlay"
:label="$t('settings.style.advanced_colors.underlay')"
:fallback="previewTheme.colors.underlay"
/>
<OpacityInput
v-model="underlayOpacityLocal"
name="underlayOpacity"
:fallback="previewTheme.opacity.underlay"
:disabled="underlayOpacityLocal === 'transparent'"
/>
</div>
<div class="color-item">
<h4>{{ $t('settings.style.advanced_colors.poll') }}</h4>
<ColorInput
v-model="pollColorLocal"
name="poll"
:label="$t('settings.background')"
:fallback="previewTheme.colors.poll"
/>
<ColorInput
v-model="pollTextColorLocal"
name="pollText"
:label="$t('settings.text')"
:fallback="previewTheme.colors.pollText"
/>
</div>
<div class="color-item">
<h4>{{ $t('settings.style.advanced_colors.icons') }}</h4>
<ColorInput
v-model="iconColorLocal"
name="icon"
:label="$t('settings.style.advanced_colors.icons')"
:fallback="previewTheme.colors.icon"
/>
</div>
<div class="color-item">
<h4>{{ $t('settings.style.advanced_colors.highlight') }}</h4>
<ColorInput
v-model="highlightColorLocal"
name="highlight"
:label="$t('settings.background')"
:fallback="previewTheme.colors.highlight"
/>
<ColorInput
v-model="highlightTextColorLocal"
name="highlightText"
:label="$t('settings.text')"
:fallback="previewTheme.colors.highlightText"
/>
<ContrastRatio :contrast="previewContrast.highlightText" />
<ColorInput
v-model="highlightLinkColorLocal"
name="highlightLink"
:label="$t('settings.links')"
:fallback="previewTheme.colors.highlightLink"
/>
<ContrastRatio :contrast="previewContrast.highlightLink" />
</div>
<div class="color-item">
<h4>{{ $t('settings.style.advanced_colors.popover') }}</h4>
<ColorInput
v-model="popoverColorLocal"
name="popover"
:label="$t('settings.background')"
:fallback="previewTheme.colors.popover"
/>
<OpacityInput
v-model="popoverOpacityLocal"
name="popoverOpacity"
:fallback="previewTheme.opacity.popover"
:disabled="popoverOpacityLocal === 'transparent'"
/>
<ColorInput
v-model="popoverTextColorLocal"
name="popoverText"
:label="$t('settings.text')"
:fallback="previewTheme.colors.popoverText"
/>
<ContrastRatio :contrast="previewContrast.popoverText" />
<ColorInput
v-model="popoverLinkColorLocal"
name="popoverLink"
:label="$t('settings.links')"
:fallback="previewTheme.colors.popoverLink"
/>
<ContrastRatio :contrast="previewContrast.popoverLink" />
</div>
<div class="color-item">
<h4>{{ $t('settings.style.advanced_colors.selectedPost') }}</h4>
<ColorInput
v-model="selectedPostColorLocal"
name="selectedPost"
:label="$t('settings.background')"
:fallback="previewTheme.colors.selectedPost"
/>
<ColorInput
v-model="selectedPostTextColorLocal"
name="selectedPostText"
:label="$t('settings.text')"
:fallback="previewTheme.colors.selectedPostText"
/>
<ContrastRatio :contrast="previewContrast.selectedPostText" />
<ColorInput
v-model="selectedPostLinkColorLocal"
name="selectedPostLink"
:label="$t('settings.links')"
:fallback="previewTheme.colors.selectedPostLink"
/>
<ContrastRatio :contrast="previewContrast.selectedPostLink" />
</div>
<div class="color-item">
<h4>{{ $t('settings.style.advanced_colors.selectedMenu') }}</h4>
<ColorInput
v-model="selectedMenuColorLocal"
name="selectedMenu"
:label="$t('settings.background')"
:fallback="previewTheme.colors.selectedMenu"
/>
<ColorInput
v-model="selectedMenuTextColorLocal"
name="selectedMenuText"
:label="$t('settings.text')"
:fallback="previewTheme.colors.selectedMenuText"
/>
<ContrastRatio :contrast="previewContrast.selectedMenuText" />
<ColorInput
v-model="selectedMenuLinkColorLocal"
name="selectedMenuLink"
:label="$t('settings.links')"
:fallback="previewTheme.colors.selectedMenuLink"
/>
<ContrastRatio :contrast="previewContrast.selectedMenuLink" />
</div>
</div> </div>
<div <div
@ -491,7 +860,7 @@
{{ $t('settings.style.switcher.clear_all') }} {{ $t('settings.style.switcher.clear_all') }}
</button> </button>
</div> </div>
<shadow-control <ShadowControl
v-model="currentShadow" v-model="currentShadow"
:ready="!!currentShadowFallback" :ready="!!currentShadowFallback"
:fallback="currentShadowFallback" :fallback="currentShadowFallback"

View File

@ -52,6 +52,11 @@
margin-bottom: 6px - 99px; margin-bottom: 6px - 99px;
white-space: nowrap; white-space: nowrap;
color: $fallback--text;
color: var(--tabText, $fallback--text);
background-color: $fallback--fg;
background-color: var(--tab, $fallback--fg);
&:not(.active) { &:not(.active) {
z-index: 4; z-index: 4;
@ -63,6 +68,8 @@
&.active { &.active {
background: transparent; background: transparent;
z-index: 5; z-index: 5;
color: $fallback--text;
color: var(--tabActiveText, $fallback--text);
} }
img { img {

View File

@ -151,7 +151,7 @@
</ProgressButton> </ProgressButton>
<ProgressButton <ProgressButton
v-else v-else
class="btn btn-default pressed" class="btn btn-default toggled"
:click="unsubscribeUser" :click="unsubscribeUser"
:title="$t('user_card.unsubscribe')" :title="$t('user_card.unsubscribe')"
> >
@ -162,7 +162,7 @@
<div> <div>
<button <button
v-if="user.muted" v-if="user.muted"
class="btn btn-default btn-block pressed" class="btn btn-default btn-block toggled"
@click="unmuteUser" @click="unmuteUser"
> >
{{ $t('user_card.muted') }} {{ $t('user_card.muted') }}
@ -299,6 +299,11 @@
&-bio { &-bio {
text-align: center; text-align: center;
a {
color: $fallback--link;
color: var(--postLink, $fallback--link);
}
img { img {
object-fit: contain; object-fit: contain;
vertical-align: middle; vertical-align: middle;
@ -460,14 +465,13 @@
color: var(--text, $fallback--text); color: var(--text, $fallback--text);
} }
// TODO use proper colors
.staff { .staff {
flex: none; flex: none;
text-transform: capitalize; text-transform: capitalize;
color: $fallback--text; color: $fallback--text;
color: var(--btnText, $fallback--text); color: var(--alertNeutralText, $fallback--text);
background-color: $fallback--fg; background-color: $fallback--fg;
background-color: var(--btn, $fallback--fg); background-color: var(--alertNeutral, $fallback--fg);
} }
} }
@ -538,12 +542,6 @@
button { button {
margin: 0; margin: 0;
&.pressed {
// TODO: This should be themed.
border-bottom-color: rgba(255, 255, 255, 0.2);
border-top-color: rgba(0, 0, 0, 0.2);
}
} }
} }
} }

View File

@ -63,6 +63,7 @@
"optional": "optional", "optional": "optional",
"show_more": "Show more", "show_more": "Show more",
"show_less": "Show less", "show_less": "Show less",
"dismiss": "Dismiss",
"cancel": "Cancel", "cancel": "Cancel",
"disable": "Disable", "disable": "Disable",
"enable": "Enable", "enable": "Enable",
@ -295,6 +296,7 @@
"follow_import": "Follow import", "follow_import": "Follow import",
"follow_import_error": "Error importing followers", "follow_import_error": "Error importing followers",
"follows_imported": "Follows imported! Processing them will take a while.", "follows_imported": "Follows imported! Processing them will take a while.",
"accent": "Accent",
"foreground": "Foreground", "foreground": "Foreground",
"general": "General", "general": "General",
"hide_attachments_in_convo": "Hide attachments in conversations", "hide_attachments_in_convo": "Hide attachments in conversations",
@ -420,7 +422,24 @@
"save_load_hint": "\"Keep\" options preserve currently set options when selecting or loading themes, it also stores said options when exporting a theme. When all checkboxes unset, exporting theme will save everything.", "save_load_hint": "\"Keep\" options preserve currently set options when selecting or loading themes, it also stores said options when exporting a theme. When all checkboxes unset, exporting theme will save everything.",
"reset": "Reset", "reset": "Reset",
"clear_all": "Clear all", "clear_all": "Clear all",
"clear_opacity": "Clear opacity" "clear_opacity": "Clear opacity",
"load_theme": "Load theme",
"keep_as_is": "Keep as is",
"use_snapshot": "Old version",
"use_source": "New version",
"help": {
"upgraded_from_v2": "PleromaFE has been upgraded, theme could look a little bit different than you remember.",
"v2_imported": "File you imported was made for older FE. We try to maximize compatibility but there still could be inconsitencies.",
"future_version_imported": "File you imported was made in newer version of FE.",
"older_version_imported": "File you imported was made in older version of FE.",
"snapshot_present": "Theme snapshot is loaded, so all values are overriden. You can load theme's actual data instead.",
"snapshot_missing": "No theme snapshot was in the file so it could look different than originally envisioned.",
"fe_upgraded": "PleromaFE's theme engine upgraded after version update.",
"fe_downgraded": "PleromaFE's version rolled back.",
"migration_snapshot_ok": "Just to be safe, theme snapshot loaded. You can try loading theme data.",
"migration_napshot_gone": "For whatever reason snapshot was missing, some stuff could look different than you remember.",
"snapshot_source_mismatch": "Versions conflict: most likely FE was rolled back and updated again, if you changed theme using older version of FE you most likely want to use old version, otherwise use new version."
}
}, },
"common": { "common": {
"color": "Color", "color": "Color",
@ -449,14 +468,27 @@
"alert": "Alert background", "alert": "Alert background",
"alert_error": "Error", "alert_error": "Error",
"alert_warning": "Warning", "alert_warning": "Warning",
"alert_neutral": "Neutral",
"post": "Posts/User bios",
"badge": "Badge background", "badge": "Badge background",
"popover": "Tooltips, menus, popovers",
"badge_notification": "Notification", "badge_notification": "Notification",
"panel_header": "Panel header", "panel_header": "Panel header",
"top_bar": "Top bar", "top_bar": "Top bar",
"borders": "Borders", "borders": "Borders",
"buttons": "Buttons", "buttons": "Buttons",
"inputs": "Input fields", "inputs": "Input fields",
"faint_text": "Faded text" "faint_text": "Faded text",
"underlay": "Underlay",
"poll": "Poll graph",
"icons": "Icons",
"highlight": "Highlighted elements",
"pressed": "Pressed",
"selectedPost": "Selected post",
"selectedMenu": "Selected menu item",
"disabled": "Disabled",
"toggled": "Toggled",
"tabs": "Tabs"
}, },
"radii": { "radii": {
"_tab_label": "Roundness" "_tab_label": "Roundness"
@ -469,7 +501,7 @@
"blur": "Blur", "blur": "Blur",
"spread": "Spread", "spread": "Spread",
"inset": "Inset", "inset": "Inset",
"hint": "For shadows you can also use --variable as a color value to use CSS3 variables. Please note that setting opacity won't work in this case.", "hintV3": "For shadows you can also use the {0} notation to use other color slot.",
"filter_hint": { "filter_hint": {
"always_drop_shadow": "Warning, this shadow always uses {0} when browser supports it.", "always_drop_shadow": "Warning, this shadow always uses {0} when browser supports it.",
"drop_shadow_syntax": "{0} does not support {1} parameter and {2} keyword.", "drop_shadow_syntax": "{0} does not support {1} parameter and {2} keyword.",

View File

@ -5,6 +5,9 @@ const browserLocale = (window.navigator.language || 'en').split('-')[0]
export const defaultState = { export const defaultState = {
colors: {}, colors: {},
theme: undefined,
customTheme: undefined,
customThemeSource: undefined,
hideISP: false, hideISP: false,
// bad name: actually hides posts of muted USERS // bad name: actually hides posts of muted USERS
hideMutedPosts: undefined, // instance default hideMutedPosts: undefined, // instance default
@ -96,10 +99,10 @@ const config = {
commit('setOption', { name, value }) commit('setOption', { name, value })
switch (name) { switch (name) {
case 'theme': case 'theme':
setPreset(value, commit) setPreset(value)
break break
case 'customTheme': case 'customTheme':
applyTheme(value, commit) applyTheme(value)
} }
} }
} }

View File

@ -1,5 +1,5 @@
import { set } from 'vue' import { set } from 'vue'
import { setPreset } from '../services/style_setter/style_setter.js' import { getPreset, applyTheme } from '../services/style_setter/style_setter.js'
import { instanceDefaultProperties } from './config.js' import { instanceDefaultProperties } from './config.js'
const defaultState = { const defaultState = {
@ -10,6 +10,7 @@ const defaultState = {
textlimit: 5000, textlimit: 5000,
server: 'http://localhost:4040/', server: 'http://localhost:4040/',
theme: 'pleroma-dark', theme: 'pleroma-dark',
themeData: undefined,
background: '/static/aurora_borealis.jpg', background: '/static/aurora_borealis.jpg',
logo: '/static/logo.png', logo: '/static/logo.png',
logoMask: true, logoMask: true,
@ -96,6 +97,9 @@ const instance = {
dispatch('initializeSocket') dispatch('initializeSocket')
} }
break break
case 'theme':
dispatch('setTheme', value)
break
} }
}, },
async getStaticEmoji ({ commit }) { async getStaticEmoji ({ commit }) {
@ -147,9 +151,16 @@ const instance = {
} }
}, },
setTheme ({ commit }, themeName) { setTheme ({ commit, rootState }, themeName) {
commit('setInstanceOption', { name: 'theme', value: themeName }) commit('setInstanceOption', { name: 'theme', value: themeName })
return setPreset(themeName, commit) getPreset(themeName)
.then(themeData => {
commit('setInstanceOption', { name: 'themeData', value: themeData })
// No need to apply theme if there's user theme already
const { customTheme } = rootState.config
if (customTheme) return
applyTheme(themeData.theme)
})
}, },
fetchEmoji ({ dispatch, state }) { fetchEmoji ({ dispatch, state }) {
if (!state.customEmojiFetched) { if (!state.customEmojiFetched) {

View File

@ -1,16 +1,27 @@
import { map } from 'lodash' import { invertLightness, contrastRatio } from 'chromatism'
const rgb2hex = (r, g, b) => { // useful for visualizing color when debugging
export const consoleColor = (color) => console.log('%c##########', 'background: ' + color + '; color: ' + color)
/**
* Convert r, g, b values into hex notation. All components are [0-255]
*
* @param {Number|String|Object} r - Either red component, {r,g,b} object, or hex string
* @param {Number} [g] - Green component
* @param {Number} [b] - Blue component
*/
export const rgb2hex = (r, g, b) => {
if (r === null || typeof r === 'undefined') { if (r === null || typeof r === 'undefined') {
return undefined return undefined
} }
if (r[0] === '#') { // TODO: clean up this mess
if (r[0] === '#' || r === 'transparent') {
return r return r
} }
if (typeof r === 'object') { if (typeof r === 'object') {
({ r, g, b } = r) ({ r, g, b } = r)
} }
[r, g, b] = map([r, g, b], (val) => { [r, g, b] = [r, g, b].map(val => {
val = Math.ceil(val) val = Math.ceil(val)
val = val < 0 ? 0 : val val = val < 0 ? 0 : val
val = val > 255 ? 255 : val val = val > 255 ? 255 : val
@ -58,7 +69,7 @@ const srgbToLinear = (srgb) => {
* @param {Object} srgb - sRGB color * @param {Object} srgb - sRGB color
* @returns {Number} relative luminance * @returns {Number} relative luminance
*/ */
const relativeLuminance = (srgb) => { export const relativeLuminance = (srgb) => {
const { r, g, b } = srgbToLinear(srgb) const { r, g, b } = srgbToLinear(srgb)
return 0.2126 * r + 0.7152 * g + 0.0722 * b return 0.2126 * r + 0.7152 * g + 0.0722 * b
} }
@ -71,7 +82,7 @@ const relativeLuminance = (srgb) => {
* @param {Object} b - sRGB color * @param {Object} b - sRGB color
* @returns {Number} color ratio * @returns {Number} color ratio
*/ */
const getContrastRatio = (a, b) => { export const getContrastRatio = (a, b) => {
const la = relativeLuminance(a) const la = relativeLuminance(a)
const lb = relativeLuminance(b) const lb = relativeLuminance(b)
const [l1, l2] = la > lb ? [la, lb] : [lb, la] const [l1, l2] = la > lb ? [la, lb] : [lb, la]
@ -79,6 +90,17 @@ const getContrastRatio = (a, b) => {
return (l1 + 0.05) / (l2 + 0.05) return (l1 + 0.05) / (l2 + 0.05)
} }
/**
* Same as `getContrastRatio` but for multiple layers in-between
*
* @param {Object} text - text color (topmost layer)
* @param {[Object, Number]} layers[] - layers between text and bedrock
* @param {Object} bedrock - layer at the very bottom
*/
export const getContrastRatioLayers = (text, layers, bedrock) => {
return getContrastRatio(alphaBlendLayers(bedrock, layers), text)
}
/** /**
* This performs alpha blending between solid background and semi-transparent foreground * This performs alpha blending between solid background and semi-transparent foreground
* *
@ -87,7 +109,7 @@ const getContrastRatio = (a, b) => {
* @param {Object} bg - bottom layer color * @param {Object} bg - bottom layer color
* @returns {Object} sRGB of resulting color * @returns {Object} sRGB of resulting color
*/ */
const alphaBlend = (fg, fga, bg) => { export const alphaBlend = (fg, fga, bg) => {
if (fga === 1 || typeof fga === 'undefined') return fg if (fga === 1 || typeof fga === 'undefined') return fg
return 'rgb'.split('').reduce((acc, c) => { return 'rgb'.split('').reduce((acc, c) => {
// Simplified https://en.wikipedia.org/wiki/Alpha_compositing#Alpha_blending // Simplified https://en.wikipedia.org/wiki/Alpha_compositing#Alpha_blending
@ -97,14 +119,30 @@ const alphaBlend = (fg, fga, bg) => {
}, {}) }, {})
} }
const invert = (rgb) => { /**
* Same as `alphaBlend` but for multiple layers in-between
*
* @param {Object} bedrock - layer at the very bottom
* @param {[Object, Number]} layers[] - layers between text and bedrock
*/
export const alphaBlendLayers = (bedrock, layers) => layers.reduce((acc, [color, opacity]) => {
return alphaBlend(color, opacity, acc)
}, bedrock)
export const invert = (rgb) => {
return 'rgb'.split('').reduce((acc, c) => { return 'rgb'.split('').reduce((acc, c) => {
acc[c] = 255 - rgb[c] acc[c] = 255 - rgb[c]
return acc return acc
}, {}) }, {})
} }
const hex2rgb = (hex) => { /**
* Converts #rrggbb hex notation into an {r, g, b} object
*
* @param {String} hex - #rrggbb string
* @returns {Object} rgb representation of the color, values are 0-255
*/
export const hex2rgb = (hex) => {
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex) const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex)
return result ? { return result ? {
r: parseInt(result[1], 16), r: parseInt(result[1], 16),
@ -113,18 +151,72 @@ const hex2rgb = (hex) => {
} : null } : null
} }
const mixrgb = (a, b) => { /**
return Object.keys(a).reduce((acc, k) => { * Old somewhat weird function for mixing two colors together
*
* @param {Object} a - one color (rgb)
* @param {Object} b - other color (rgb)
* @returns {Object} result
*/
export const mixrgb = (a, b) => {
return 'rgb'.split('').reduce((acc, k) => {
acc[k] = (a[k] + b[k]) / 2 acc[k] = (a[k] + b[k]) / 2
return acc return acc
}, {}) }, {})
} }
/**
export { * Converts rgb object into a CSS rgba() color
rgb2hex, *
hex2rgb, * @param {Object} color - rgb
mixrgb, * @returns {String} CSS rgba() color
invert, */
getContrastRatio, export const rgba2css = function (rgba) {
alphaBlend return `rgba(${Math.floor(rgba.r)}, ${Math.floor(rgba.g)}, ${Math.floor(rgba.b)}, ${rgba.a})`
}
/**
* Get text color for given background color and intended text color
* This checks if text and background don't have enough color and inverts
* text color's lightness if needed. If text color is still not enough it
* will fall back to black or white
*
* @param {Object} bg - background color
* @param {Object} text - intended text color
* @param {Boolean} preserve - try to preserve intended text color's hue/saturation (i.e. no BW)
*/
export const getTextColor = function (bg, text, preserve) {
const contrast = getContrastRatio(bg, text)
if (contrast < 4.5) {
const base = typeof text.a !== 'undefined' ? { a: text.a } : {}
const result = Object.assign(base, invertLightness(text).rgb)
if (!preserve && getContrastRatio(bg, result) < 4.5) {
// B&W
return contrastRatio(bg, text).rgb
}
// Inverted color
return result
}
return text
}
/**
* Converts color to CSS Color value
*
* @param {Object|String} input - color
* @param {Number} [a] - alpha value
* @returns {String} a CSS Color value
*/
export const getCssColor = (input, a) => {
let rgb = {}
if (typeof input === 'object') {
rgb = input
} else if (typeof input === 'string') {
if (input.startsWith('#')) {
rgb = hex2rgb(input)
} else {
return input
}
}
return rgba2css({ ...rgb, a })
} }

View File

@ -1,78 +1,9 @@
import { times } from 'lodash' import { convert } from 'chromatism'
import { brightness, invertLightness, convert, contrastRatio } from 'chromatism' import { rgb2hex, hex2rgb, rgba2css, getCssColor, relativeLuminance } from '../color_convert/color_convert.js'
import { rgb2hex, hex2rgb, mixrgb, getContrastRatio, alphaBlend } from '../color_convert/color_convert.js' import { getColors, computeDynamicColor, getOpacitySlot } from '../theme_data/theme_data.service.js'
// While this is not used anymore right now, I left it in if we want to do custom export const applyTheme = (input) => {
// styles that aren't just colors, so user can pick from a few different distinct const { rules } = generatePreset(input)
// styles as well as set their own colors in the future.
const setStyle = (href, commit) => {
/***
What's going on here?
I want to make it easy for admins to style this application. To have
a good set of default themes, I chose the system from base16
(https://chriskempson.github.io/base16/) to style all elements. They
all have the base00..0F classes. So the only thing an admin needs to
do to style Pleroma is to change these colors in that one css file.
Some default things (body text color, link color) need to be set dy-
namically, so this is done here by waiting for the stylesheet to be
loaded and then creating an element with the respective classes.
It is a bit weird, but should make life for admins somewhat easier.
***/
const head = document.head
const body = document.body
body.classList.add('hidden')
const cssEl = document.createElement('link')
cssEl.setAttribute('rel', 'stylesheet')
cssEl.setAttribute('href', href)
head.appendChild(cssEl)
const setDynamic = () => {
const baseEl = document.createElement('div')
body.appendChild(baseEl)
let colors = {}
times(16, (n) => {
const name = `base0${n.toString(16).toUpperCase()}`
baseEl.setAttribute('class', name)
const color = window.getComputedStyle(baseEl).getPropertyValue('color')
colors[name] = color
})
body.removeChild(baseEl)
const styleEl = document.createElement('style')
head.appendChild(styleEl)
// const styleSheet = styleEl.sheet
body.classList.remove('hidden')
}
cssEl.addEventListener('load', setDynamic)
}
const rgb2rgba = function (rgba) {
return `rgba(${rgba.r}, ${rgba.g}, ${rgba.b}, ${rgba.a})`
}
const getTextColor = function (bg, text, preserve) {
const bgIsLight = convert(bg).hsl.l > 50
const textIsLight = convert(text).hsl.l > 50
if ((bgIsLight && textIsLight) || (!bgIsLight && !textIsLight)) {
const base = typeof text.a !== 'undefined' ? { a: text.a } : {}
const result = Object.assign(base, invertLightness(text).rgb)
if (!preserve && getContrastRatio(bg, result) < 4.5) {
return contrastRatio(bg, text).rgb
}
return result
}
return text
}
const applyTheme = (input, commit) => {
const { rules, theme } = generatePreset(input)
const head = document.head const head = document.head
const body = document.body const body = document.body
body.classList.add('hidden') body.classList.add('hidden')
@ -87,14 +18,9 @@ const applyTheme = (input, commit) => {
styleSheet.insertRule(`body { ${rules.shadows} }`, 'index-max') styleSheet.insertRule(`body { ${rules.shadows} }`, 'index-max')
styleSheet.insertRule(`body { ${rules.fonts} }`, 'index-max') styleSheet.insertRule(`body { ${rules.fonts} }`, 'index-max')
body.classList.remove('hidden') body.classList.remove('hidden')
// commit('setOption', { name: 'colors', value: htmlColors })
// commit('setOption', { name: 'radii', value: radii })
commit('setOption', { name: 'customTheme', value: input })
commit('setOption', { name: 'colors', value: theme.colors })
} }
const getCssShadow = (input, usesDropShadow) => { export const getCssShadow = (input, usesDropShadow) => {
if (input.length === 0) { if (input.length === 0) {
return 'none' return 'none'
} }
@ -132,122 +58,18 @@ const getCssShadowFilter = (input) => {
.join(' ') .join(' ')
} }
const getCssColor = (input, a) => { export const generateColors = (themeData) => {
let rgb = {} const sourceColors = !themeData.themeEngineVersion
if (typeof input === 'object') { ? colors2to3(themeData.colors || themeData)
rgb = input : themeData.colors || themeData
} else if (typeof input === 'string') {
if (input.startsWith('#')) {
rgb = hex2rgb(input)
} else if (input.startsWith('--')) {
return `var(${input})`
} else {
return input
}
}
return rgb2rgba({ ...rgb, a })
}
const generateColors = (input) => { const { colors, opacity } = getColors(sourceColors, themeData.opacity || {})
const colors = {}
const opacity = Object.assign({
alert: 0.5,
input: 0.5,
faint: 0.5
}, Object.entries(input.opacity || {}).reduce((acc, [k, v]) => {
if (typeof v !== 'undefined') {
acc[k] = v
}
return acc
}, {}))
const col = Object.entries(input.colors || input).reduce((acc, [k, v]) => {
if (typeof v === 'object') {
acc[k] = v
} else {
acc[k] = hex2rgb(v)
}
return acc
}, {})
const isLightOnDark = convert(col.bg).hsl.l < convert(col.text).hsl.l
const mod = isLightOnDark ? 1 : -1
colors.text = col.text
colors.lightText = brightness(20 * mod, colors.text).rgb
colors.link = col.link
colors.faint = col.faint || Object.assign({}, col.text)
colors.bg = col.bg
colors.lightBg = col.lightBg || brightness(5, colors.bg).rgb
colors.fg = col.fg
colors.fgText = col.fgText || getTextColor(colors.fg, colors.text)
colors.fgLink = col.fgLink || getTextColor(colors.fg, colors.link, true)
colors.border = col.border || brightness(2 * mod, colors.fg).rgb
colors.btn = col.btn || Object.assign({}, col.fg)
colors.btnText = col.btnText || getTextColor(colors.btn, colors.fgText)
colors.input = col.input || Object.assign({}, col.fg)
colors.inputText = col.inputText || getTextColor(colors.input, colors.lightText)
colors.panel = col.panel || Object.assign({}, col.fg)
colors.panelText = col.panelText || getTextColor(colors.panel, colors.fgText)
colors.panelLink = col.panelLink || getTextColor(colors.panel, colors.fgLink)
colors.panelFaint = col.panelFaint || getTextColor(colors.panel, colors.faint)
colors.topBar = col.topBar || Object.assign({}, col.fg)
colors.topBarText = col.topBarText || getTextColor(colors.topBar, colors.fgText)
colors.topBarLink = col.topBarLink || getTextColor(colors.topBar, colors.fgLink)
colors.faintLink = col.faintLink || Object.assign({}, col.link)
colors.linkBg = alphaBlend(colors.link, 0.4, colors.bg)
colors.icon = mixrgb(colors.bg, colors.text)
colors.cBlue = col.cBlue || hex2rgb('#0000FF')
colors.cRed = col.cRed || hex2rgb('#FF0000')
colors.cGreen = col.cGreen || hex2rgb('#00FF00')
colors.cOrange = col.cOrange || hex2rgb('#E3FF00')
colors.alertError = col.alertError || Object.assign({}, colors.cRed)
colors.alertErrorText = getTextColor(alphaBlend(colors.alertError, opacity.alert, colors.bg), colors.text)
colors.alertErrorPanelText = getTextColor(alphaBlend(colors.alertError, opacity.alert, colors.panel), colors.panelText)
colors.alertWarning = col.alertWarning || Object.assign({}, colors.cOrange)
colors.alertWarningText = getTextColor(alphaBlend(colors.alertWarning, opacity.alert, colors.bg), colors.text)
colors.alertWarningPanelText = getTextColor(alphaBlend(colors.alertWarning, opacity.alert, colors.panel), colors.panelText)
colors.badgeNotification = col.badgeNotification || Object.assign({}, colors.cRed)
colors.badgeNotificationText = contrastRatio(colors.badgeNotification).rgb
Object.entries(opacity).forEach(([ k, v ]) => {
if (typeof v === 'undefined') return
if (k === 'alert') {
colors.alertError.a = v
colors.alertWarning.a = v
return
}
if (k === 'faint') {
colors[k + 'Link'].a = v
colors['panelFaint'].a = v
}
if (k === 'bg') {
colors['lightBg'].a = v
}
if (colors[k]) {
colors[k].a = v
} else {
console.error('Wrong key ' + k)
}
})
const htmlColors = Object.entries(colors) const htmlColors = Object.entries(colors)
.reduce((acc, [k, v]) => { .reduce((acc, [k, v]) => {
if (!v) return acc if (!v) return acc
acc.solid[k] = rgb2hex(v) acc.solid[k] = rgb2hex(v)
acc.complete[k] = typeof v.a === 'undefined' ? rgb2hex(v) : rgb2rgba(v) acc.complete[k] = typeof v.a === 'undefined' ? rgb2hex(v) : rgba2css(v)
return acc return acc
}, { complete: {}, solid: {} }) }, { complete: {}, solid: {} })
return { return {
@ -264,7 +86,7 @@ const generateColors = (input) => {
} }
} }
const generateRadii = (input) => { export const generateRadii = (input) => {
let inputRadii = input.radii || {} let inputRadii = input.radii || {}
// v1 -> v2 // v1 -> v2
if (typeof input.btnRadius !== 'undefined') { if (typeof input.btnRadius !== 'undefined') {
@ -297,7 +119,7 @@ const generateRadii = (input) => {
} }
} }
const generateFonts = (input) => { export const generateFonts = (input) => {
const fonts = Object.entries(input.fonts || {}).filter(([k, v]) => v).reduce((acc, [k, v]) => { const fonts = Object.entries(input.fonts || {}).filter(([k, v]) => v).reduce((acc, [k, v]) => {
acc[k] = Object.entries(v).filter(([k, v]) => v).reduce((acc, [k, v]) => { acc[k] = Object.entries(v).filter(([k, v]) => v).reduce((acc, [k, v]) => {
acc[k] = v acc[k] = v
@ -332,7 +154,6 @@ const generateFonts = (input) => {
} }
} }
const generateShadows = (input) => {
const border = (top, shadow) => ({ const border = (top, shadow) => ({
x: 0, x: 0,
y: top ? 1 : -1, y: top ? 1 : -1,
@ -353,7 +174,7 @@ const generateShadows = (input) => {
alpha: 1 alpha: 1
} }
const shadows = { export const DEFAULT_SHADOWS = {
panel: [{ panel: [{
x: 1, x: 1,
y: 1, y: 1,
@ -406,15 +227,50 @@ const generateShadows = (input) => {
spread: 0, spread: 0,
color: '#000000', color: '#000000',
alpha: 1 alpha: 1
}], }]
...(input.shadows || {})
} }
export const generateShadows = (input, colors) => {
// TODO this is a small hack for `mod` to work with shadows
// this is used to get the "context" of shadow, i.e. for `mod` properly depend on background color of element
const hackContextDict = {
button: 'btn',
panel: 'bg',
top: 'topBar',
popup: 'popover',
avatar: 'bg',
panelHeader: 'panel',
input: 'input'
}
const inputShadows = input.shadows && !input.themeEngineVersion
? shadows2to3(input.shadows, input.opacity)
: input.shadows || {}
const shadows = Object.entries({
...DEFAULT_SHADOWS,
...inputShadows
}).reduce((shadowsAcc, [slotName, shadowDefs]) => {
const slotFirstWord = slotName.replace(/[A-Z].*$/, '')
const colorSlotName = hackContextDict[slotFirstWord]
const isLightOnDark = relativeLuminance(convert(colors[colorSlotName]).rgb) < 0.5
const mod = isLightOnDark ? 1 : -1
const newShadow = shadowDefs.reduce((shadowAcc, def) => [
...shadowAcc,
{
...def,
color: rgb2hex(computeDynamicColor(
def.color,
(variableSlot) => convert(colors[variableSlot]).rgb,
mod
))
}
], [])
return { ...shadowsAcc, [slotName]: newShadow }
}, {})
return { return {
rules: { rules: {
shadows: Object shadows: Object
.entries(shadows) .entries(shadows)
// TODO for v2.1: if shadow doesn't have non-inset shadows with spread > 0 - optionally // TODO for v2.2: if shadow doesn't have non-inset shadows with spread > 0 - optionally
// convert all non-inset shadows into filter: drop-shadow() to boost performance // convert all non-inset shadows into filter: drop-shadow() to boost performance
.map(([k, v]) => [ .map(([k, v]) => [
`--${k}Shadow: ${getCssShadow(v)}`, `--${k}Shadow: ${getCssShadow(v)}`,
@ -429,7 +285,7 @@ const generateShadows = (input) => {
} }
} }
const composePreset = (colors, radii, shadows, fonts) => { export const composePreset = (colors, radii, shadows, fonts) => {
return { return {
rules: { rules: {
...shadows.rules, ...shadows.rules,
@ -446,98 +302,110 @@ const composePreset = (colors, radii, shadows, fonts) => {
} }
} }
const generatePreset = (input) => { export const generatePreset = (input) => {
const shadows = generateShadows(input)
const colors = generateColors(input) const colors = generateColors(input)
const radii = generateRadii(input) return composePreset(
const fonts = generateFonts(input) colors,
generateRadii(input),
return composePreset(colors, radii, shadows, fonts) generateShadows(input, colors.theme.colors, colors.mod),
generateFonts(input)
)
} }
const getThemes = () => { export const getThemes = () => {
return window.fetch('/static/styles.json') const cache = 'no-store'
return window.fetch('/static/styles.json', { cache })
.then((data) => data.json()) .then((data) => data.json())
.then((themes) => { .then((themes) => {
return Promise.all(Object.entries(themes).map(([k, v]) => { return Object.entries(themes).map(([k, v]) => {
let promise = null
if (typeof v === 'object') { if (typeof v === 'object') {
return Promise.resolve([k, v]) promise = Promise.resolve(v)
} else if (typeof v === 'string') { } else if (typeof v === 'string') {
return window.fetch(v) promise = window.fetch(v, { cache })
.then((data) => data.json()) .then((data) => data.json())
.then((theme) => {
return [k, theme]
})
.catch((e) => { .catch((e) => {
console.error(e) console.error(e)
return [] return null
}) })
} }
})) return [k, promise]
})
}) })
.then((promises) => { .then((promises) => {
return promises return promises
.filter(([k, v]) => v)
.reduce((acc, [k, v]) => { .reduce((acc, [k, v]) => {
acc[k] = v acc[k] = v
return acc return acc
}, {}) }, {})
}) })
} }
export const colors2to3 = (colors) => {
return Object.entries(colors).reduce((acc, [slotName, color]) => {
const btnPositions = ['', 'Panel', 'TopBar']
switch (slotName) {
case 'lightBg':
return { ...acc, highlight: color }
case 'btnText':
return {
...acc,
...btnPositions
.reduce(
(statePositionAcc, position) =>
({ ...statePositionAcc, ['btn' + position + 'Text']: color })
, {}
)
}
default:
return { ...acc, [slotName]: color }
}
}, {})
}
const setPreset = (val, commit) => { /**
return getThemes().then((themes) => { * This handles compatibility issues when importing v2 theme's shadows to current format
const theme = themes[val] ? themes[val] : themes['pleroma-dark'] *
* Back in v2 shadows allowed you to use dynamic colors however those used pure CSS3 variables
*/
export const shadows2to3 = (shadows, opacity) => {
return Object.entries(shadows).reduce((shadowsAcc, [slotName, shadowDefs]) => {
const isDynamic = ({ color }) => color.startsWith('--')
const getOpacity = ({ color }) => opacity[getOpacitySlot(color.substring(2).split(',')[0])]
const newShadow = shadowDefs.reduce((shadowAcc, def) => [
...shadowAcc,
{
...def,
alpha: isDynamic(def) ? getOpacity(def) || 1 : def.alpha
}
], [])
return { ...shadowsAcc, [slotName]: newShadow }
}, {})
}
export const getPreset = (val) => {
return getThemes()
.then((themes) => themes[val] ? themes[val] : themes['pleroma-dark'])
.then((theme) => {
const isV1 = Array.isArray(theme) const isV1 = Array.isArray(theme)
const data = isV1 ? {} : theme.theme const data = isV1 ? {} : theme.theme
if (isV1) { if (isV1) {
const bgRgb = hex2rgb(theme[1]) const bg = hex2rgb(theme[1])
const fgRgb = hex2rgb(theme[2]) const fg = hex2rgb(theme[2])
const textRgb = hex2rgb(theme[3]) const text = hex2rgb(theme[3])
const linkRgb = hex2rgb(theme[4]) const link = hex2rgb(theme[4])
const cRedRgb = hex2rgb(theme[5] || '#FF0000') const cRed = hex2rgb(theme[5] || '#FF0000')
const cGreenRgb = hex2rgb(theme[6] || '#00FF00') const cGreen = hex2rgb(theme[6] || '#00FF00')
const cBlueRgb = hex2rgb(theme[7] || '#0000FF') const cBlue = hex2rgb(theme[7] || '#0000FF')
const cOrangeRgb = hex2rgb(theme[8] || '#E3FF00') const cOrange = hex2rgb(theme[8] || '#E3FF00')
data.colors = { data.colors = { bg, fg, text, link, cRed, cBlue, cGreen, cOrange }
bg: bgRgb,
fg: fgRgb,
text: textRgb,
link: linkRgb,
cRed: cRedRgb,
cBlue: cBlueRgb,
cGreen: cGreenRgb,
cOrange: cOrangeRgb
}
} }
// This is a hack, this function is only called during initial load. return { theme: data, source: theme.source }
// We want to cancel loading the theme from config.json if we're already
// loading a theme from the persisted state.
// Needed some way of dealing with the async way of things.
// load config -> set preset -> wait for styles.json to load ->
// load persisted state -> set colors -> styles.json loaded -> set colors
if (!window.themeLoaded) {
applyTheme(data, commit)
}
}) })
} }
export { export const setPreset = (val) => getPreset(val).then(data => applyTheme(data.theme))
setStyle,
setPreset,
applyTheme,
getTextColor,
generateColors,
generateRadii,
generateShadows,
generateFonts,
generatePreset,
getThemes,
composePreset,
getCssShadow,
getCssShadowFilter
}

View File

@ -0,0 +1,615 @@
import { invertLightness, brightness } from 'chromatism'
import { alphaBlend, mixrgb } from '../color_convert/color_convert.js'
/* This is a definition of all layer combinations
* each key is a topmost layer, each value represents layer underneath
* this is essentially a simplified tree
*/
export const LAYERS = {
undelay: null, // root
topBar: null, // no transparency support
badge: null, // no transparency support
fg: null,
bg: 'underlay',
highlight: 'bg',
panel: 'bg',
popover: 'bg',
selectedMenu: 'popover',
btn: 'bg',
btnPanel: 'panel',
btnTopBar: 'topBar',
input: 'bg',
inputPanel: 'panel',
inputTopBar: 'topBar',
alert: 'bg',
alertPanel: 'panel',
poll: 'bg'
}
/* By default opacity slots have 1 as default opacity
* this allows redefining it to something else
*/
export const DEFAULT_OPACITY = {
alert: 0.5,
input: 0.5,
faint: 0.5,
underlay: 0.15
}
/** SUBJECT TO CHANGE IN THE FUTURE, this is all beta
* Color and opacity slots definitions. Each key represents a slot.
*
* Short-hands:
* String beginning with `--` - value after dashes treated as sole
* dependency - i.e. `--value` equivalent to { depends: ['value']}
* String beginning with `#` - value would be treated as solid color
* defined in hexadecimal representation (i.e. #FFFFFF) and will be
* used as default. `#FFFFFF` is equivalent to { default: '#FFFFFF'}
*
* Full definition:
* @property {String[]} depends - color slot names this color depends ones.
* cyclic dependencies are supported to some extent but not recommended.
* @property {String} [opacity] - opacity slot used by this color slot.
* opacity is inherited from parents. To break inheritance graph use null
* @property {Number} [priority] - EXPERIMENTAL. used to pre-sort slots so
* that slots with higher priority come earlier
* @property {Function(mod, ...colors)} [color] - function that will be
* used to determine the color. By default it just copies first color in
* dependency list.
* @argument {Number} mod - `1` (light-on-dark) or `-1` (dark-on-light)
* depending on background color (for textColor)/given color.
* @argument {...Object} deps - each argument after mod represents each
* color from `depends` array. All colors take user customizations into
* account and represented by { r, g, b } objects.
* @returns {Object} resulting color, should be in { r, g, b } form
*
* @property {Boolean|String} [textColor] - true to mark color slot as text
* color. This enables automatic text color generation for the slot. Use
* 'preserve' string if you don't want text color to fall back to
* black/white. Use 'bw' to only ever use black or white. This also makes
* following properties required:
* @property {String} [layer] - which layer the text sit on top on - used
* to account for transparency in text color calculation
* layer is inherited from parents. To break inheritance graph use null
* @property {String} [variant] - which color slot is background (same as
* above, used to account for transparency)
*/
export const SLOT_INHERITANCE = {
bg: {
depends: [],
opacity: 'bg',
priority: 1
},
fg: {
depends: [],
priority: 1
},
text: {
depends: [],
layer: 'bg',
opacity: null,
priority: 1
},
underlay: {
default: '#000000',
opacity: 'underlay'
},
link: {
depends: ['accent'],
priority: 1
},
accent: {
depends: ['link'],
priority: 1
},
faint: {
depends: ['text'],
opacity: 'faint'
},
faintLink: {
depends: ['link'],
opacity: 'faint'
},
postFaintLink: {
depends: ['postLink'],
opacity: 'faint'
},
cBlue: '#0000ff',
cRed: '#FF0000',
cGreen: '#00FF00',
cOrange: '#E3FF00',
highlight: {
depends: ['bg'],
color: (mod, bg) => brightness(5 * mod, bg).rgb
},
highlightLightText: {
depends: ['lightText'],
layer: 'highlight',
textColor: true
},
highlightPostLink: {
depends: ['postLink'],
layer: 'highlight',
textColor: 'preserve'
},
highlightFaintText: {
depends: ['faint'],
layer: 'highlight',
textColor: true
},
highlightFaintLink: {
depends: ['faintLink'],
layer: 'highlight',
textColor: 'preserve'
},
highlightPostFaintLink: {
depends: ['postFaintLink'],
layer: 'highlight',
textColor: 'preserve'
},
highlightText: {
depends: ['text'],
layer: 'highlight',
textColor: true
},
highlightLink: {
depends: ['link'],
layer: 'highlight',
textColor: 'preserve'
},
highlightIcon: {
depends: ['highlight', 'highlightText'],
color: (mod, bg, text) => mixrgb(bg, text)
},
popover: {
depends: ['bg'],
opacity: 'popover'
},
popoverLightText: {
depends: ['lightText'],
layer: 'popover',
textColor: true
},
popoverPostLink: {
depends: ['postLink'],
layer: 'popover',
textColor: 'preserve'
},
popoverFaintText: {
depends: ['faint'],
layer: 'popover',
textColor: true
},
popoverFaintLink: {
depends: ['faintLink'],
layer: 'popover',
textColor: 'preserve'
},
popoverPostFaintLink: {
depends: ['postFaintLink'],
layer: 'popover',
textColor: 'preserve'
},
popoverText: {
depends: ['text'],
layer: 'popover',
textColor: true
},
popoverLink: {
depends: ['link'],
layer: 'popover',
textColor: 'preserve'
},
popoverIcon: {
depends: ['popover', 'popoverText'],
color: (mod, bg, text) => mixrgb(bg, text)
},
selectedPost: '--highlight',
selectedPostFaintText: {
depends: ['highlightFaintText'],
layer: 'highlight',
variant: 'selectedPost',
textColor: true
},
selectedPostLightText: {
depends: ['highlightLightText'],
layer: 'highlight',
variant: 'selectedPost',
textColor: true
},
selectedPostPostLink: {
depends: ['highlightPostLink'],
layer: 'highlight',
variant: 'selectedPost',
textColor: 'preserve'
},
selectedPostFaintLink: {
depends: ['highlightFaintLink'],
layer: 'highlight',
variant: 'selectedPost',
textColor: 'preserve'
},
selectedPostText: {
depends: ['highlightText'],
layer: 'highlight',
variant: 'selectedPost',
textColor: true
},
selectedPostLink: {
depends: ['highlightLink'],
layer: 'highlight',
variant: 'selectedPost',
textColor: 'preserve'
},
selectedPostIcon: {
depends: ['selectedPost', 'selectedPostText'],
color: (mod, bg, text) => mixrgb(bg, text)
},
selectedMenu: {
depends: ['bg'],
color: (mod, bg) => brightness(5 * mod, bg).rgb
},
selectedMenuLightText: {
depends: ['highlightLightText'],
layer: 'selectedMenu',
variant: 'selectedMenu',
textColor: true
},
selectedMenuFaintText: {
depends: ['highlightFaintText'],
layer: 'selectedMenu',
variant: 'selectedMenu',
textColor: true
},
selectedMenuFaintLink: {
depends: ['highlightFaintLink'],
layer: 'selectedMenu',
variant: 'selectedMenu',
textColor: 'preserve'
},
selectedMenuText: {
depends: ['highlightText'],
layer: 'selectedMenu',
variant: 'selectedMenu',
textColor: true
},
selectedMenuLink: {
depends: ['highlightLink'],
layer: 'selectedMenu',
variant: 'selectedMenu',
textColor: 'preserve'
},
selectedMenuIcon: {
depends: ['selectedMenu', 'selectedMenuText'],
color: (mod, bg, text) => mixrgb(bg, text)
},
selectedMenuPopover: {
depends: ['popover'],
color: (mod, bg) => brightness(5 * mod, bg).rgb
},
selectedMenuPopoverLightText: {
depends: ['selectedMenuLightText'],
layer: 'selectedMenuPopover',
variant: 'selectedMenuPopover',
textColor: true
},
selectedMenuPopoverFaintText: {
depends: ['selectedMenuFaintText'],
layer: 'selectedMenuPopover',
variant: 'selectedMenuPopover',
textColor: true
},
selectedMenuPopoverFaintLink: {
depends: ['selectedMenuFaintLink'],
layer: 'selectedMenuPopover',
variant: 'selectedMenuPopover',
textColor: 'preserve'
},
selectedMenuPopoverText: {
depends: ['selectedMenuText'],
layer: 'selectedMenuPopover',
variant: 'selectedMenuPopover',
textColor: true
},
selectedMenuPopoverLink: {
depends: ['selectedMenuLink'],
layer: 'selectedMenuPopover',
variant: 'selectedMenuPopover',
textColor: 'preserve'
},
selectedMenuPopoverIcon: {
depends: ['selectedMenuPopover', 'selectedMenuText'],
color: (mod, bg, text) => mixrgb(bg, text)
},
lightText: {
depends: ['text'],
layer: 'bg',
textColor: 'preserve',
color: (mod, text) => brightness(20 * mod, text).rgb
},
postLink: {
depends: ['link'],
layer: 'bg',
textColor: 'preserve'
},
border: {
depends: ['fg'],
opacity: 'border',
color: (mod, fg) => brightness(2 * mod, fg).rgb
},
poll: {
depends: ['accent', 'bg'],
copacity: 'poll',
color: (mod, accent, bg) => alphaBlend(accent, 0.4, bg)
},
pollText: {
depends: ['text'],
layer: 'poll',
textColor: true
},
icon: {
depends: ['bg', 'text'],
inheritsOpacity: false,
color: (mod, bg, text) => mixrgb(bg, text)
},
// Foreground
fgText: {
depends: ['text'],
layer: 'fg',
textColor: true
},
fgLink: {
depends: ['link'],
layer: 'fg',
textColor: 'preserve'
},
// Panel header
panel: {
depends: ['fg'],
opacity: 'panel'
},
panelText: {
depends: ['text'],
layer: 'panel',
textColor: true
},
panelFaint: {
depends: ['fgText'],
layer: 'panel',
opacity: 'faint',
textColor: true
},
panelLink: {
depends: ['fgLink'],
layer: 'panel',
textColor: 'preserve'
},
// Top bar
topBar: '--fg',
topBarText: {
depends: ['fgText'],
layer: 'topBar',
textColor: true
},
topBarLink: {
depends: ['fgLink'],
layer: 'topBar',
textColor: 'preserve'
},
// Tabs
tab: {
depends: ['btn']
},
tabText: {
depends: ['btnText'],
layer: 'btn',
textColor: true
},
tabActiveText: {
depends: ['text'],
layer: 'bg',
textColor: true
},
// Buttons
btn: {
depends: ['fg'],
variant: 'btn',
opacity: 'btn'
},
btnText: {
depends: ['fgText'],
layer: 'btn',
textColor: true
},
btnPanelText: {
depends: ['btnText'],
layer: 'btnPanel',
variant: 'btn',
textColor: true
},
btnTopBarText: {
depends: ['btnText'],
layer: 'btnTopBar',
variant: 'btn',
textColor: true
},
// Buttons: pressed
btnPressed: {
depends: ['btn'],
layer: 'btn'
},
btnPressedText: {
depends: ['btnText'],
layer: 'btn',
variant: 'btnPressed',
textColor: true
},
btnPressedPanel: {
depends: ['btnPressed'],
layer: 'btn'
},
btnPressedPanelText: {
depends: ['btnPanelText'],
layer: 'btnPanel',
variant: 'btnPressed',
textColor: true
},
btnPressedTopBar: {
depends: ['btnPressed'],
layer: 'btn'
},
btnPressedTopBarText: {
depends: ['btnTopBarText'],
layer: 'btnTopBar',
variant: 'btnPressed',
textColor: true
},
// Buttons: toggled
btnToggled: {
depends: ['btn'],
layer: 'btn',
color: (mod, btn) => brightness(mod * 20, btn).rgb
},
btnToggledText: {
depends: ['btnText'],
layer: 'btn',
variant: 'btnToggled',
textColor: true
},
btnToggledPanelText: {
depends: ['btnPanelText'],
layer: 'btnPanel',
variant: 'btnToggled',
textColor: true
},
btnToggledTopBarText: {
depends: ['btnTopBarText'],
layer: 'btnTopBar',
variant: 'btnToggled',
textColor: true
},
// Buttons: disabled
btnDisabled: {
depends: ['btn', 'bg'],
color: (mod, btn, bg) => alphaBlend(btn, 0.25, bg)
},
btnDisabledText: {
depends: ['btnText', 'btnDisabled'],
layer: 'btn',
variant: 'btnDisabled',
color: (mod, text, btn) => alphaBlend(text, 0.25, btn)
},
btnDisabledPanelText: {
depends: ['btnPanelText', 'btnDisabled'],
layer: 'btnPanel',
variant: 'btnDisabled',
color: (mod, text, btn) => alphaBlend(text, 0.25, btn)
},
btnDisabledTopBarText: {
depends: ['btnTopBarText', 'btnDisabled'],
layer: 'btnTopBar',
variant: 'btnDisabled',
color: (mod, text, btn) => alphaBlend(text, 0.25, btn)
},
// Input fields
input: {
depends: ['fg'],
opacity: 'input'
},
inputText: {
depends: ['text'],
layer: 'input',
textColor: true
},
inputPanelText: {
depends: ['panelText'],
layer: 'inputPanel',
variant: 'input',
textColor: true
},
inputTopbarText: {
depends: ['topBarText'],
layer: 'inputTopBar',
variant: 'input',
textColor: true
},
alertError: {
depends: ['cRed'],
opacity: 'alert'
},
alertErrorText: {
depends: ['text'],
layer: 'alert',
variant: 'alertError',
textColor: true
},
alertErrorPanelText: {
depends: ['panelText'],
layer: 'alertPanel',
variant: 'alertError',
textColor: true
},
alertWarning: {
depends: ['cOrange'],
opacity: 'alert'
},
alertWarningText: {
depends: ['text'],
layer: 'alert',
variant: 'alertWarning',
textColor: true
},
alertWarningPanelText: {
depends: ['panelText'],
layer: 'alertPanel',
variant: 'alertWarning',
textColor: true
},
alertNeutral: {
depends: ['text'],
opacity: 'alert'
},
alertNeutralText: {
depends: ['text'],
layer: 'alert',
variant: 'alertNeutral',
color: (mod, text) => invertLightness(text).rgb,
textColor: true
},
alertNeutralPanelText: {
depends: ['panelText'],
layer: 'alertPanel',
variant: 'alertNeutral',
textColor: true
},
badgeNotification: '--cRed',
badgeNotificationText: {
depends: ['text', 'badgeNotification'],
layer: 'badge',
variant: 'badgeNotification',
textColor: 'bw'
}
}

View File

@ -0,0 +1,373 @@
import { convert, brightness, contrastRatio } from 'chromatism'
import { alphaBlendLayers, getTextColor, relativeLuminance } from '../color_convert/color_convert.js'
import { LAYERS, DEFAULT_OPACITY, SLOT_INHERITANCE } from './pleromafe.js'
/*
* # What's all this?
* Here be theme engine for pleromafe. All of this supposed to ease look
* and feel customization, making widget styles and make developer's life
* easier when it comes to supporting themes. Like many other theme systems
* it operates on color definitions, or "slots" - for example you define
* "button" color slot and then in UI component Button's CSS you refer to
* it as a CSS3 Variable.
*
* Some applications allow you to customize colors for certain things.
* Some UI toolkits allow you to define colors for each type of widget.
* Most of them are pretty barebones and have no assistance for common
* problems and cases, and in general themes themselves are very hard to
* maintain in all aspects. This theme engine tries to solve all of the
* common problems with themes.
*
* You don't have redefine several similar colors if you just want to
* change one color - all color slots are derived from other ones, so you
* can have at least one or two "basic" colors defined and have all other
* components inherit and modify basic ones.
*
* You don't have to test contrast ratio for colors or pick text color for
* each element even if you have light-on-dark elements in dark-on-light
* theme.
*
* You don't have to maintain order of code for inheriting slots from othet
* slots - dependency graph resolving does it for you.
*/
/* This indicates that this version of code outputs similar theme data and
* should be incremented if output changes - for instance if getTextColor
* function changes and older themes no longer render text colors as
* author intended previously.
*/
export const CURRENT_VERSION = 3
export const getLayersArray = (layer, data = LAYERS) => {
let array = [layer]
let parent = data[layer]
while (parent) {
array.unshift(parent)
parent = data[parent]
}
return array
}
export const getLayers = (layer, variant = layer, opacitySlot, colors, opacity) => {
return getLayersArray(layer).map((currentLayer) => ([
currentLayer === layer
? colors[variant]
: colors[currentLayer],
currentLayer === layer
? opacity[opacitySlot] || 1
: opacity[currentLayer]
]))
}
const getDependencies = (key, inheritance) => {
const data = inheritance[key]
if (typeof data === 'string' && data.startsWith('--')) {
return [data.substring(2)]
} else {
if (data === null) return []
const { depends, layer, variant } = data
const layerDeps = layer
? getLayersArray(layer).map(currentLayer => {
return currentLayer === layer
? variant || layer
: currentLayer
})
: []
if (Array.isArray(depends)) {
return [...depends, ...layerDeps]
} else {
return [...layerDeps]
}
}
}
/**
* Sorts inheritance object topologically - dependant slots come after
* dependencies
*
* @property {Object} inheritance - object defining the nodes
* @property {Function} getDeps - function that returns dependencies for
* given value and inheritance object.
* @returns {String[]} keys of inheritance object, sorted in topological
* order. Additionally, dependency-less nodes will always be first in line
*/
export const topoSort = (
inheritance = SLOT_INHERITANCE,
getDeps = getDependencies
) => {
// This is an implementation of https://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm
const allKeys = Object.keys(inheritance)
const whites = new Set(allKeys)
const grays = new Set()
const blacks = new Set()
const unprocessed = [...allKeys]
const output = []
const step = (node) => {
if (whites.has(node)) {
// Make node "gray"
whites.delete(node)
grays.add(node)
// Do step for each node connected to it (one way)
getDeps(node, inheritance).forEach(step)
// Make node "black"
grays.delete(node)
blacks.add(node)
// Put it into the output list
output.push(node)
} else if (grays.has(node)) {
console.debug('Cyclic depenency in topoSort, ignoring')
output.push(node)
} else if (blacks.has(node)) {
// do nothing
} else {
throw new Error('Unintended condition in topoSort!')
}
}
while (unprocessed.length > 0) {
step(unprocessed.pop())
}
return output.sort((a, b) => {
const depsA = getDeps(a, inheritance).length
const depsB = getDeps(b, inheritance).length
if (depsA === depsB || (depsB !== 0 && depsA !== 0)) return 0
if (depsA === 0 && depsB !== 0) return -1
if (depsB === 0 && depsA !== 0) return 1
})
}
const expandSlotValue = (value) => {
if (typeof value === 'object') return value
return {
depends: value.startsWith('--') ? [value.substring(2)] : [],
default: value.startsWith('#') ? value : undefined
}
}
/**
* retrieves opacity slot for given slot. This goes up the depenency graph
* to find which parent has opacity slot defined for it.
* TODO refactor this
*/
export const getOpacitySlot = (
k,
inheritance = SLOT_INHERITANCE,
getDeps = getDependencies
) => {
const value = expandSlotValue(inheritance[k])
if (value.opacity === null) return
if (value.opacity) return value.opacity
const findInheritedOpacity = (key, visited = [k]) => {
const depSlot = getDeps(key, inheritance)[0]
if (depSlot === undefined) return
const dependency = inheritance[depSlot]
if (dependency === undefined) return
if (dependency.opacity || dependency === null) {
return dependency.opacity
} else if (dependency.depends && visited.includes(depSlot)) {
return findInheritedOpacity(depSlot, [...visited, depSlot])
} else {
return null
}
}
if (value.depends) {
return findInheritedOpacity(k)
}
}
/**
* retrieves layer slot for given slot. This goes up the depenency graph
* to find which parent has opacity slot defined for it.
* this is basically copypaste of getOpacitySlot except it checks if key is
* in LAYERS
* TODO refactor this
*/
export const getLayerSlot = (
k,
inheritance = SLOT_INHERITANCE,
getDeps = getDependencies
) => {
const value = expandSlotValue(inheritance[k])
if (LAYERS[k]) return k
if (value.layer === null) return
if (value.layer) return value.layer
const findInheritedLayer = (key, visited = [k]) => {
const depSlot = getDeps(key, inheritance)[0]
if (depSlot === undefined) return
const dependency = inheritance[depSlot]
if (dependency === undefined) return
if (dependency.layer || dependency === null) {
return dependency.layer
} else if (dependency.depends) {
return findInheritedLayer(dependency, [...visited, depSlot])
} else {
return null
}
}
if (value.depends) {
return findInheritedLayer(k)
}
}
/**
* topologically sorted SLOT_INHERITANCE
*/
export const SLOT_ORDERED = topoSort(
Object.entries(SLOT_INHERITANCE)
.sort(([aK, aV], [bK, bV]) => ((aV && aV.priority) || 0) - ((bV && bV.priority) || 0))
.reduce((acc, [k, v]) => ({ ...acc, [k]: v }), {})
)
/**
* All opacity slots used in color slots, their default values and affected
* color slots.
*/
export const OPACITIES = Object.entries(SLOT_INHERITANCE).reduce((acc, [k, v]) => {
const opacity = getOpacitySlot(k, SLOT_INHERITANCE, getDependencies)
if (opacity) {
return {
...acc,
[opacity]: {
defaultValue: DEFAULT_OPACITY[opacity] || 1,
affectedSlots: [...((acc[opacity] && acc[opacity].affectedSlots) || []), k]
}
}
} else {
return acc
}
}, {})
/**
* Handle dynamic color
*/
export const computeDynamicColor = (sourceColor, getColor, mod) => {
if (typeof sourceColor !== 'string' || !sourceColor.startsWith('--')) return sourceColor
let targetColor = null
// Color references other color
const [variable, modifier] = sourceColor.split(/,/g).map(str => str.trim())
const variableSlot = variable.substring(2)
targetColor = getColor(variableSlot)
if (modifier) {
targetColor = brightness(Number.parseFloat(modifier) * mod, targetColor).rgb
}
return targetColor
}
/**
* THE function you want to use. Takes provided colors and opacities
* value and uses inheritance data to figure out color needed for the slot.
*/
export const getColors = (sourceColors, sourceOpacity) => SLOT_ORDERED.reduce(({ colors, opacity }, key) => {
const sourceColor = sourceColors[key]
const value = expandSlotValue(SLOT_INHERITANCE[key])
const deps = getDependencies(key, SLOT_INHERITANCE)
const isTextColor = !!value.textColor
const variant = value.variant || value.layer
let backgroundColor = null
if (isTextColor) {
backgroundColor = alphaBlendLayers(
{ ...(colors[deps[0]] || convert(sourceColors[key] || '#FF00FF').rgb) },
getLayers(
getLayerSlot(key) || 'bg',
variant || 'bg',
getOpacitySlot(variant),
colors,
opacity
)
)
} else if (variant && variant !== key) {
backgroundColor = colors[variant] || convert(sourceColors[variant]).rgb
} else {
backgroundColor = colors.bg || convert(sourceColors.bg)
}
const isLightOnDark = relativeLuminance(backgroundColor) < 0.5
const mod = isLightOnDark ? 1 : -1
let outputColor = null
if (sourceColor) {
// Color is defined in source color
let targetColor = sourceColor
if (targetColor === 'transparent') {
// We take only layers below current one
const layers = getLayers(
getLayerSlot(key),
key,
getOpacitySlot(key) || key,
colors,
opacity
).slice(0, -1)
targetColor = {
...alphaBlendLayers(
convert('#FF00FF').rgb,
layers
),
a: 0
}
} else if (typeof sourceColor === 'string' && sourceColor.startsWith('--')) {
targetColor = computeDynamicColor(
sourceColor,
variableSlot => colors[variableSlot] || sourceColors[variableSlot],
mod
)
} else if (typeof sourceColor === 'string' && sourceColor.startsWith('#')) {
targetColor = convert(targetColor).rgb
}
outputColor = { ...targetColor }
} else if (value.default) {
// same as above except in object form
outputColor = convert(value.default).rgb
} else {
// calculate color
const defaultColorFunc = (mod, dep) => ({ ...dep })
const colorFunc = value.color || defaultColorFunc
if (value.textColor) {
if (value.textColor === 'bw') {
outputColor = contrastRatio(backgroundColor).rgb
} else {
let color = { ...colors[deps[0]] }
if (value.color) {
color = colorFunc(mod, ...deps.map((dep) => ({ ...colors[dep] })))
}
outputColor = getTextColor(
backgroundColor,
{ ...color },
value.textColor === 'preserve'
)
}
} else {
// background color case
outputColor = colorFunc(
mod,
...deps.map((dep) => ({ ...colors[dep] }))
)
}
}
if (!outputColor) {
throw new Error('Couldn\'t generate color for ' + key)
}
const opacitySlot = getOpacitySlot(key)
if (opacitySlot && outputColor.a === undefined) {
const dependencySlot = deps[0]
if (dependencySlot && colors[dependencySlot] === 'transparent') {
outputColor.a = 0
} else {
outputColor.a = Number(sourceOpacity[opacitySlot]) || OPACITIES[opacitySlot].defaultValue || 1
}
}
if (opacitySlot) {
return {
colors: { ...colors, [key]: outputColor },
opacity: { ...opacity, [opacitySlot]: outputColor.a }
}
} else {
return {
colors: { ...colors, [key]: outputColor },
opacity
}
}
}, { colors: {}, opacity: {} })

View File

@ -1,33 +0,0 @@
.base00-background { background-color: #090300; }
.base01-background { background-color: #3a3432; }
.base02-background { background-color: #4a4543; }
.base03-background { background-color: #5c5855; }
.base04-background { background-color: #807d7c; }
.base05-background { background-color: #a5a2a2; }
.base06-background { background-color: #d6d5d4; }
.base07-background { background-color: #f7f7f7; }
.base08-background { background-color: #db2d20; }
.base09-background { background-color: #e8bbd0; }
.base0A-background { background-color: #fded02; }
.base0B-background { background-color: #01a252; }
.base0C-background { background-color: #b5e4f4; }
.base0D-background { background-color: #01a0e4; }
.base0E-background { background-color: #a16a94; }
.base0F-background { background-color: #cdab53; }
.base00 { color: #090300; }
.base01 { color: #3a3432; }
.base02 { color: #4a4543; }
.base03 { color: #5c5855; }
.base04 { color: #807d7c; }
.base05 { color: #a5a2a2; }
.base06 { color: #d6d5d4; }
.base07 { color: #f7f7f7; }
.base08 { color: #db2d20; }
.base09 { color: #e8bbd0; }
.base0A { color: #fded02; }
.base0B { color: #01a252; }
.base0C { color: #b5e4f4; }
.base0D { color: #01a0e4; }
.base0E { color: #a16a94; }
.base0F { color: #cdab53; }

View File

@ -1,33 +0,0 @@
.base00-background { background-color: #031A16; }
.base01-background { background-color: #0B342D; }
.base02-background { background-color: #184E45; }
.base03-background { background-color: #2B685E; }
.base04-background { background-color: #5F9C92; }
.base05-background { background-color: #81B5AC; }
.base06-background { background-color: #A7CEC8; }
.base07-background { background-color: #D2E7E4; }
.base08-background { background-color: #3E9688; }
.base09-background { background-color: #3E7996; }
.base0A-background { background-color: #3E4C96; }
.base0B-background { background-color: #883E96; }
.base0C-background { background-color: #963E4C; }
.base0D-background { background-color: #96883E; }
.base0E-background { background-color: #4C963E; }
.base0F-background { background-color: #3E965B; }
.base00 { color: #031A16; }
.base01 { color: #0B342D; }
.base02 { color: #184E45; }
.base03 { color: #2B685E; }
.base04 { color: #5F9C92; }
.base05 { color: #81B5AC; }
.base06 { color: #A7CEC8; }
.base07 { color: #D2E7E4; }
.base08 { color: #3E9688; }
.base09 { color: #3E7996; }
.base0A { color: #3E4C96; }
.base0B { color: #883E96; }
.base0C { color: #963E4C; }
.base0D { color: #96883E; }
.base0E { color: #4C963E; }
.base0F { color: #3E965B; }

View File

@ -1,33 +0,0 @@
.base00-background { background-color: #1C2023; }
.base01-background { background-color: #393F45; }
.base02-background { background-color: #565E65; }
.base03-background { background-color: #747C84; }
.base04-background { background-color: #ADB3BA; }
.base05-background { background-color: #C7CCD1; }
.base06-background { background-color: #DFE2E5; }
.base07-background { background-color: #F3F4F5; }
.base08-background { background-color: #C7AE95; }
.base09-background { background-color: #C7C795; }
.base0A-background { background-color: #AEC795; }
.base0B-background { background-color: #95C7AE; }
.base0C-background { background-color: #95AEC7; }
.base0D-background { background-color: #AE95C7; }
.base0E-background { background-color: #C795AE; }
.base0F-background { background-color: #C79595; }
.base00 { color: #1C2023; }
.base01 { color: #393F45; }
.base02 { color: #565E65; }
.base03 { color: #747C84; }
.base04 { color: #ADB3BA; }
.base05 { color: #C7CCD1; }
.base06 { color: #DFE2E5; }
.base07 { color: #F3F4F5; }
.base08 { color: #C7AE95; }
.base09 { color: #C7C795; }
.base0A { color: #AEC795; }
.base0B { color: #95C7AE; }
.base0C { color: #95AEC7; }
.base0D { color: #AE95C7; }
.base0E { color: #C795AE; }
.base0F { color: #C79595; }

View File

@ -1,33 +0,0 @@
.base00-background { background-color: #19171c; }
.base01-background { background-color: #26232a; }
.base02-background { background-color: #585260; }
.base03-background { background-color: #655f6d; }
.base04-background { background-color: #7e7887; }
.base05-background { background-color: #8b8792; }
.base06-background { background-color: #e2dfe7; }
.base07-background { background-color: #efecf4; }
.base08-background { background-color: #be4678; }
.base09-background { background-color: #aa573c; }
.base0A-background { background-color: #a06e3b; }
.base0B-background { background-color: #2a9292; }
.base0C-background { background-color: #398bc6; }
.base0D-background { background-color: #576ddb; }
.base0E-background { background-color: #955ae7; }
.base0F-background { background-color: #bf40bf; }
.base00 { color: #19171c; }
.base01 { color: #26232a; }
.base02 { color: #585260; }
.base03 { color: #655f6d; }
.base04 { color: #7e7887; }
.base05 { color: #8b8792; }
.base06 { color: #e2dfe7; }
.base07 { color: #efecf4; }
.base08 { color: #be4678; }
.base09 { color: #aa573c; }
.base0A { color: #a06e3b; }
.base0B { color: #2a9292; }
.base0C { color: #398bc6; }
.base0D { color: #576ddb; }
.base0E { color: #955ae7; }
.base0F { color: #bf40bf; }

View File

@ -1,33 +0,0 @@
.base00-background { background-color: #20201d; }
.base01-background { background-color: #292824; }
.base02-background { background-color: #6e6b5e; }
.base03-background { background-color: #7d7a68; }
.base04-background { background-color: #999580; }
.base05-background { background-color: #a6a28c; }
.base06-background { background-color: #e8e4cf; }
.base07-background { background-color: #fefbec; }
.base08-background { background-color: #d73737; }
.base09-background { background-color: #b65611; }
.base0A-background { background-color: #ae9513; }
.base0B-background { background-color: #60ac39; }
.base0C-background { background-color: #1fad83; }
.base0D-background { background-color: #6684e1; }
.base0E-background { background-color: #b854d4; }
.base0F-background { background-color: #d43552; }
.base00 { color: #20201d; }
.base01 { color: #292824; }
.base02 { color: #6e6b5e; }
.base03 { color: #7d7a68; }
.base04 { color: #999580; }
.base05 { color: #a6a28c; }
.base06 { color: #e8e4cf; }
.base07 { color: #fefbec; }
.base08 { color: #d73737; }
.base09 { color: #b65611; }
.base0A { color: #ae9513; }
.base0B { color: #60ac39; }
.base0C { color: #1fad83; }
.base0D { color: #6684e1; }
.base0E { color: #b854d4; }
.base0F { color: #d43552; }

View File

@ -1,33 +0,0 @@
.base00-background { background-color: #22221b; }
.base01-background { background-color: #302f27; }
.base02-background { background-color: #5f5e4e; }
.base03-background { background-color: #6c6b5a; }
.base04-background { background-color: #878573; }
.base05-background { background-color: #929181; }
.base06-background { background-color: #e7e6df; }
.base07-background { background-color: #f4f3ec; }
.base08-background { background-color: #ba6236; }
.base09-background { background-color: #ae7313; }
.base0A-background { background-color: #a5980d; }
.base0B-background { background-color: #7d9726; }
.base0C-background { background-color: #5b9d48; }
.base0D-background { background-color: #36a166; }
.base0E-background { background-color: #5f9182; }
.base0F-background { background-color: #9d6c7c; }
.base00 { color: #22221b; }
.base01 { color: #302f27; }
.base02 { color: #5f5e4e; }
.base03 { color: #6c6b5a; }
.base04 { color: #878573; }
.base05 { color: #929181; }
.base06 { color: #e7e6df; }
.base07 { color: #f4f3ec; }
.base08 { color: #ba6236; }
.base09 { color: #ae7313; }
.base0A { color: #a5980d; }
.base0B { color: #7d9726; }
.base0C { color: #5b9d48; }
.base0D { color: #36a166; }
.base0E { color: #5f9182; }
.base0F { color: #9d6c7c; }

View File

@ -1,33 +0,0 @@
.base00-background { background-color: #1b1918; }
.base01-background { background-color: #2c2421; }
.base02-background { background-color: #68615e; }
.base03-background { background-color: #766e6b; }
.base04-background { background-color: #9c9491; }
.base05-background { background-color: #a8a19f; }
.base06-background { background-color: #e6e2e0; }
.base07-background { background-color: #f1efee; }
.base08-background { background-color: #f22c40; }
.base09-background { background-color: #df5320; }
.base0A-background { background-color: #c38418; }
.base0B-background { background-color: #7b9726; }
.base0C-background { background-color: #3d97b8; }
.base0D-background { background-color: #407ee7; }
.base0E-background { background-color: #6666ea; }
.base0F-background { background-color: #c33ff3; }
.base00 { color: #1b1918; }
.base01 { color: #2c2421; }
.base02 { color: #68615e; }
.base03 { color: #766e6b; }
.base04 { color: #9c9491; }
.base05 { color: #a8a19f; }
.base06 { color: #e6e2e0; }
.base07 { color: #f1efee; }
.base08 { color: #f22c40; }
.base09 { color: #df5320; }
.base0A { color: #c38418; }
.base0B { color: #7b9726; }
.base0C { color: #3d97b8; }
.base0D { color: #407ee7; }
.base0E { color: #6666ea; }
.base0F { color: #c33ff3; }

View File

@ -1,33 +0,0 @@
.base00-background { background-color: #1b181b; }
.base01-background { background-color: #292329; }
.base02-background { background-color: #695d69; }
.base03-background { background-color: #776977; }
.base04-background { background-color: #9e8f9e; }
.base05-background { background-color: #ab9bab; }
.base06-background { background-color: #d8cad8; }
.base07-background { background-color: #f7f3f7; }
.base08-background { background-color: #ca402b; }
.base09-background { background-color: #a65926; }
.base0A-background { background-color: #bb8a35; }
.base0B-background { background-color: #918b3b; }
.base0C-background { background-color: #159393; }
.base0D-background { background-color: #516aec; }
.base0E-background { background-color: #7b59c0; }
.base0F-background { background-color: #cc33cc; }
.base00 { color: #1b181b; }
.base01 { color: #292329; }
.base02 { color: #695d69; }
.base03 { color: #776977; }
.base04 { color: #9e8f9e; }
.base05 { color: #ab9bab; }
.base06 { color: #d8cad8; }
.base07 { color: #f7f3f7; }
.base08 { color: #ca402b; }
.base09 { color: #a65926; }
.base0A { color: #bb8a35; }
.base0B { color: #918b3b; }
.base0C { color: #159393; }
.base0D { color: #516aec; }
.base0E { color: #7b59c0; }
.base0F { color: #cc33cc; }

View File

@ -1,33 +0,0 @@
.base00-background { background-color: #161b1d; }
.base01-background { background-color: #1f292e; }
.base02-background { background-color: #516d7b; }
.base03-background { background-color: #5a7b8c; }
.base04-background { background-color: #7195a8; }
.base05-background { background-color: #7ea2b4; }
.base06-background { background-color: #c1e4f6; }
.base07-background { background-color: #ebf8ff; }
.base08-background { background-color: #d22d72; }
.base09-background { background-color: #935c25; }
.base0A-background { background-color: #8a8a0f; }
.base0B-background { background-color: #568c3b; }
.base0C-background { background-color: #2d8f6f; }
.base0D-background { background-color: #257fad; }
.base0E-background { background-color: #6b6bb8; }
.base0F-background { background-color: #b72dd2; }
.base00 { color: #161b1d; }
.base01 { color: #1f292e; }
.base02 { color: #516d7b; }
.base03 { color: #5a7b8c; }
.base04 { color: #7195a8; }
.base05 { color: #7ea2b4; }
.base06 { color: #c1e4f6; }
.base07 { color: #ebf8ff; }
.base08 { color: #d22d72; }
.base09 { color: #935c25; }
.base0A { color: #8a8a0f; }
.base0B { color: #568c3b; }
.base0C { color: #2d8f6f; }
.base0D { color: #257fad; }
.base0E { color: #6b6bb8; }
.base0F { color: #b72dd2; }

View File

@ -1,33 +0,0 @@
.base00-background { background-color: #1b1818; }
.base01-background { background-color: #292424; }
.base02-background { background-color: #585050; }
.base03-background { background-color: #655d5d; }
.base04-background { background-color: #7e7777; }
.base05-background { background-color: #8a8585; }
.base06-background { background-color: #e7dfdf; }
.base07-background { background-color: #f4ecec; }
.base08-background { background-color: #ca4949; }
.base09-background { background-color: #b45a3c; }
.base0A-background { background-color: #a06e3b; }
.base0B-background { background-color: #4b8b8b; }
.base0C-background { background-color: #5485b6; }
.base0D-background { background-color: #7272ca; }
.base0E-background { background-color: #8464c4; }
.base0F-background { background-color: #bd5187; }
.base00 { color: #1b1818; }
.base01 { color: #292424; }
.base02 { color: #585050; }
.base03 { color: #655d5d; }
.base04 { color: #7e7777; }
.base05 { color: #8a8585; }
.base06 { color: #e7dfdf; }
.base07 { color: #f4ecec; }
.base08 { color: #ca4949; }
.base09 { color: #b45a3c; }
.base0A { color: #a06e3b; }
.base0B { color: #4b8b8b; }
.base0C { color: #5485b6; }
.base0D { color: #7272ca; }
.base0E { color: #8464c4; }
.base0F { color: #bd5187; }

View File

@ -1,33 +0,0 @@
.base00-background { background-color: #171c19; }
.base01-background { background-color: #232a25; }
.base02-background { background-color: #526057; }
.base03-background { background-color: #5f6d64; }
.base04-background { background-color: #78877d; }
.base05-background { background-color: #87928a; }
.base06-background { background-color: #dfe7e2; }
.base07-background { background-color: #ecf4ee; }
.base08-background { background-color: #b16139; }
.base09-background { background-color: #9f713c; }
.base0A-background { background-color: #a07e3b; }
.base0B-background { background-color: #489963; }
.base0C-background { background-color: #1c9aa0; }
.base0D-background { background-color: #478c90; }
.base0E-background { background-color: #55859b; }
.base0F-background { background-color: #867469; }
.base00 { color: #171c19; }
.base01 { color: #232a25; }
.base02 { color: #526057; }
.base03 { color: #5f6d64; }
.base04 { color: #78877d; }
.base05 { color: #87928a; }
.base06 { color: #dfe7e2; }
.base07 { color: #ecf4ee; }
.base08 { color: #b16139; }
.base09 { color: #9f713c; }
.base0A { color: #a07e3b; }
.base0B { color: #489963; }
.base0C { color: #1c9aa0; }
.base0D { color: #478c90; }
.base0E { color: #55859b; }
.base0F { color: #867469; }

View File

@ -1,33 +0,0 @@
.base00-background { background-color: #131513; }
.base01-background { background-color: #242924; }
.base02-background { background-color: #5e6e5e; }
.base03-background { background-color: #687d68; }
.base04-background { background-color: #809980; }
.base05-background { background-color: #8ca68c; }
.base06-background { background-color: #cfe8cf; }
.base07-background { background-color: #f4fbf4; }
.base08-background { background-color: #e6193c; }
.base09-background { background-color: #87711d; }
.base0A-background { background-color: #98981b; }
.base0B-background { background-color: #29a329; }
.base0C-background { background-color: #1999b3; }
.base0D-background { background-color: #3d62f5; }
.base0E-background { background-color: #ad2bee; }
.base0F-background { background-color: #e619c3; }
.base00 { color: #131513; }
.base01 { color: #242924; }
.base02 { color: #5e6e5e; }
.base03 { color: #687d68; }
.base04 { color: #809980; }
.base05 { color: #8ca68c; }
.base06 { color: #cfe8cf; }
.base07 { color: #f4fbf4; }
.base08 { color: #e6193c; }
.base09 { color: #87711d; }
.base0A { color: #98981b; }
.base0B { color: #29a329; }
.base0C { color: #1999b3; }
.base0D { color: #3d62f5; }
.base0E { color: #ad2bee; }
.base0F { color: #e619c3; }

View File

@ -1,33 +0,0 @@
.base00-background { background-color: #202746; }
.base01-background { background-color: #293256; }
.base02-background { background-color: #5e6687; }
.base03-background { background-color: #6b7394; }
.base04-background { background-color: #898ea4; }
.base05-background { background-color: #979db4; }
.base06-background { background-color: #dfe2f1; }
.base07-background { background-color: #f5f7ff; }
.base08-background { background-color: #c94922; }
.base09-background { background-color: #c76b29; }
.base0A-background { background-color: #c08b30; }
.base0B-background { background-color: #ac9739; }
.base0C-background { background-color: #22a2c9; }
.base0D-background { background-color: #3d8fd1; }
.base0E-background { background-color: #6679cc; }
.base0F-background { background-color: #9c637a; }
.base00 { color: #202746; }
.base01 { color: #293256; }
.base02 { color: #5e6687; }
.base03 { color: #6b7394; }
.base04 { color: #898ea4; }
.base05 { color: #979db4; }
.base06 { color: #dfe2f1; }
.base07 { color: #f5f7ff; }
.base08 { color: #c94922; }
.base09 { color: #c76b29; }
.base0A { color: #c08b30; }
.base0B { color: #ac9739; }
.base0C { color: #22a2c9; }
.base0D { color: #3d8fd1; }
.base0E { color: #6679cc; }
.base0F { color: #9c637a; }

View File

@ -1,33 +0,0 @@
.base00-background { background-color: #28211c; }
.base01-background { background-color: #36312e; }
.base02-background { background-color: #5e5d5c; }
.base03-background { background-color: #666666; }
.base04-background { background-color: #797977; }
.base05-background { background-color: #8a8986; }
.base06-background { background-color: #9d9b97; }
.base07-background { background-color: #baae9e; }
.base08-background { background-color: #cf6a4c; }
.base09-background { background-color: #cf7d34; }
.base0A-background { background-color: #f9ee98; }
.base0B-background { background-color: #54be0d; }
.base0C-background { background-color: #afc4db; }
.base0D-background { background-color: #5ea6ea; }
.base0E-background { background-color: #9b859d; }
.base0F-background { background-color: #937121; }
.base00 { color: #28211c; }
.base01 { color: #36312e; }
.base02 { color: #5e5d5c; }
.base03 { color: #666666; }
.base04 { color: #797977; }
.base05 { color: #8a8986; }
.base06 { color: #9d9b97; }
.base07 { color: #baae9e; }
.base08 { color: #cf6a4c; }
.base09 { color: #cf7d34; }
.base0A { color: #f9ee98; }
.base0B { color: #54be0d; }
.base0C { color: #afc4db; }
.base0D { color: #5ea6ea; }
.base0E { color: #9b859d; }
.base0F { color: #937121; }

View File

@ -1,33 +0,0 @@
.base00-background { background-color: #0c0d0e; }
.base01-background { background-color: #2e2f30; }
.base02-background { background-color: #515253; }
.base03-background { background-color: #737475; }
.base04-background { background-color: #959697; }
.base05-background { background-color: #b7b8b9; }
.base06-background { background-color: #dadbdc; }
.base07-background { background-color: #fcfdfe; }
.base08-background { background-color: #e31a1c; }
.base09-background { background-color: #e6550d; }
.base0A-background { background-color: #dca060; }
.base0B-background { background-color: #31a354; }
.base0C-background { background-color: #80b1d3; }
.base0D-background { background-color: #3182bd; }
.base0E-background { background-color: #756bb1; }
.base0F-background { background-color: #b15928; }
.base00 { color: #0c0d0e; }
.base01 { color: #2e2f30; }
.base02 { color: #515253; }
.base03 { color: #737475; }
.base04 { color: #959697; }
.base05 { color: #b7b8b9; }
.base06 { color: #dadbdc; }
.base07 { color: #fcfdfe; }
.base08 { color: #e31a1c; }
.base09 { color: #e6550d; }
.base0A { color: #dca060; }
.base0B { color: #31a354; }
.base0C { color: #80b1d3; }
.base0D { color: #3182bd; }
.base0E { color: #756bb1; }
.base0F { color: #b15928; }

View File

@ -1,33 +0,0 @@
.base00-background { background-color: #000000; }
.base01-background { background-color: #303030; }
.base02-background { background-color: #505050; }
.base03-background { background-color: #b0b0b0; }
.base04-background { background-color: #d0d0d0; }
.base05-background { background-color: #e0e0e0; }
.base06-background { background-color: #f5f5f5; }
.base07-background { background-color: #ffffff; }
.base08-background { background-color: #fb0120; }
.base09-background { background-color: #fc6d24; }
.base0A-background { background-color: #fda331; }
.base0B-background { background-color: #a1c659; }
.base0C-background { background-color: #76c7b7; }
.base0D-background { background-color: #6fb3d2; }
.base0E-background { background-color: #d381c3; }
.base0F-background { background-color: #be643c; }
.base00 { color: #000000; }
.base01 { color: #303030; }
.base02 { color: #505050; }
.base03 { color: #b0b0b0; }
.base04 { color: #d0d0d0; }
.base05 { color: #e0e0e0; }
.base06 { color: #f5f5f5; }
.base07 { color: #ffffff; }
.base08 { color: #fb0120; }
.base09 { color: #fc6d24; }
.base0A { color: #fda331; }
.base0B { color: #a1c659; }
.base0C { color: #76c7b7; }
.base0D { color: #6fb3d2; }
.base0E { color: #d381c3; }
.base0F { color: #be643c; }

View File

@ -1,33 +0,0 @@
.base00-background { background-color: #151515; }
.base01-background { background-color: #202020; }
.base02-background { background-color: #303030; }
.base03-background { background-color: #505050; }
.base04-background { background-color: #b0b0b0; }
.base05-background { background-color: #d0d0d0; }
.base06-background { background-color: #e0e0e0; }
.base07-background { background-color: #f5f5f5; }
.base08-background { background-color: #fb9fb1; }
.base09-background { background-color: #eda987; }
.base0A-background { background-color: #ddb26f; }
.base0B-background { background-color: #acc267; }
.base0C-background { background-color: #12cfc0; }
.base0D-background { background-color: #6fc2ef; }
.base0E-background { background-color: #e1a3ee; }
.base0F-background { background-color: #deaf8f; }
.base00 { color: #151515; }
.base01 { color: #202020; }
.base02 { color: #303030; }
.base03 { color: #505050; }
.base04 { color: #b0b0b0; }
.base05 { color: #d0d0d0; }
.base06 { color: #e0e0e0; }
.base07 { color: #f5f5f5; }
.base08 { color: #fb9fb1; }
.base09 { color: #eda987; }
.base0A { color: #ddb26f; }
.base0B { color: #acc267; }
.base0C { color: #12cfc0; }
.base0D { color: #6fc2ef; }
.base0E { color: #e1a3ee; }
.base0F { color: #deaf8f; }

View File

@ -1,33 +0,0 @@
.base00-background { background-color: #232c31; }
.base01-background { background-color: #1c3657; }
.base02-background { background-color: #2a343a; }
.base03-background { background-color: #3f4944; }
.base04-background { background-color: #84898c; }
.base05-background { background-color: #9ea7a6; }
.base06-background { background-color: #a7cfa3; }
.base07-background { background-color: #b5d8f6; }
.base08-background { background-color: #2a5491; }
.base09-background { background-color: #43820d; }
.base0A-background { background-color: #a03b1e; }
.base0B-background { background-color: #237986; }
.base0C-background { background-color: #b02f30; }
.base0D-background { background-color: #484d79; }
.base0E-background { background-color: #c59820; }
.base0F-background { background-color: #c98344; }
.base00 { color: #232c31; }
.base01 { color: #1c3657; }
.base02 { color: #2a343a; }
.base03 { color: #3f4944; }
.base04 { color: #84898c; }
.base05 { color: #9ea7a6; }
.base06 { color: #a7cfa3; }
.base07 { color: #b5d8f6; }
.base08 { color: #2a5491; }
.base09 { color: #43820d; }
.base0A { color: #a03b1e; }
.base0B { color: #237986; }
.base0C { color: #b02f30; }
.base0D { color: #484d79; }
.base0E { color: #c59820; }
.base0F { color: #c98344; }

View File

@ -1,33 +0,0 @@
.base00-background { background-color: #1D2021; }
.base01-background { background-color: #32302F; }
.base02-background { background-color: #504945; }
.base03-background { background-color: #665C54; }
.base04-background { background-color: #928374; }
.base05-background { background-color: #A89984; }
.base06-background { background-color: #D5C4A1; }
.base07-background { background-color: #FDF4C1; }
.base08-background { background-color: #FB543F; }
.base09-background { background-color: #FE8625; }
.base0A-background { background-color: #FAC03B; }
.base0B-background { background-color: #95C085; }
.base0C-background { background-color: #8BA59B; }
.base0D-background { background-color: #0D6678; }
.base0E-background { background-color: #8F4673; }
.base0F-background { background-color: #A87322; }
.base00 { color: #1D2021; }
.base01 { color: #32302F; }
.base02 { color: #504945; }
.base03 { color: #665C54; }
.base04 { color: #928374; }
.base05 { color: #A89984; }
.base06 { color: #D5C4A1; }
.base07 { color: #FDF4C1; }
.base08 { color: #FB543F; }
.base09 { color: #FE8625; }
.base0A { color: #FAC03B; }
.base0B { color: #95C085; }
.base0C { color: #8BA59B; }
.base0D { color: #0D6678; }
.base0E { color: #8F4673; }
.base0F { color: #A87322; }

View File

@ -1,33 +0,0 @@
.base00-background { background-color: #181818; }
.base01-background { background-color: #282828; }
.base02-background { background-color: #383838; }
.base03-background { background-color: #585858; }
.base04-background { background-color: #b8b8b8; }
.base05-background { background-color: #d8d8d8; }
.base06-background { background-color: #e8e8e8; }
.base07-background { background-color: #f8f8f8; }
.base08-background { background-color: #ab4642; }
.base09-background { background-color: #dc9656; }
.base0A-background { background-color: #f7ca88; }
.base0B-background { background-color: #a1b56c; }
.base0C-background { background-color: #86c1b9; }
.base0D-background { background-color: #7cafc2; }
.base0E-background { background-color: #ba8baf; }
.base0F-background { background-color: #a16946; }
.base00 { color: #181818; }
.base01 { color: #282828; }
.base02 { color: #383838; }
.base03 { color: #585858; }
.base04 { color: #b8b8b8; }
.base05 { color: #d8d8d8; }
.base06 { color: #e8e8e8; }
.base07 { color: #f8f8f8; }
.base08 { color: #ab4642; }
.base09 { color: #dc9656; }
.base0A { color: #f7ca88; }
.base0B { color: #a1b56c; }
.base0C { color: #86c1b9; }
.base0D { color: #7cafc2; }
.base0E { color: #ba8baf; }
.base0F { color: #a16946; }

View File

@ -1,33 +0,0 @@
.base00-background { background-color: #f8f8f8; }
.base01-background { background-color: #e8e8e8; }
.base02-background { background-color: #d8d8d8; }
.base03-background { background-color: #b8b8b8; }
.base04-background { background-color: #585858; }
.base05-background { background-color: #383838; }
.base06-background { background-color: #282828; }
.base07-background { background-color: #181818; }
.base08-background { background-color: #ab4642; }
.base09-background { background-color: #dc9656; }
.base0A-background { background-color: #f7ca88; }
.base0B-background { background-color: #a1b56c; }
.base0C-background { background-color: #86c1b9; }
.base0D-background { background-color: #7cafc2; }
.base0E-background { background-color: #ba8baf; }
.base0F-background { background-color: #a16946; }
.base00 { color: #f8f8f8; }
.base01 { color: #e8e8e8; }
.base02 { color: #d8d8d8; }
.base03 { color: #b8b8b8; }
.base04 { color: #585858; }
.base05 { color: #383838; }
.base06 { color: #282828; }
.base07 { color: #181818; }
.base08 { color: #ab4642; }
.base09 { color: #dc9656; }
.base0A { color: #f7ca88; }
.base0B { color: #a1b56c; }
.base0C { color: #86c1b9; }
.base0D { color: #7cafc2; }
.base0E { color: #ba8baf; }
.base0F { color: #a16946; }

View File

@ -1,33 +0,0 @@
.base00-background { background-color: #2d2d2d; }
.base01-background { background-color: #393939; }
.base02-background { background-color: #515151; }
.base03-background { background-color: #747369; }
.base04-background { background-color: #a09f93; }
.base05-background { background-color: #d3d0c8; }
.base06-background { background-color: #e8e6df; }
.base07-background { background-color: #f2f0ec; }
.base08-background { background-color: #f2777a; }
.base09-background { background-color: #f99157; }
.base0A-background { background-color: #ffcc66; }
.base0B-background { background-color: #99cc99; }
.base0C-background { background-color: #66cccc; }
.base0D-background { background-color: #6699cc; }
.base0E-background { background-color: #cc99cc; }
.base0F-background { background-color: #d27b53; }
.base00 { color: #2d2d2d; }
.base01 { color: #393939; }
.base02 { color: #515151; }
.base03 { color: #747369; }
.base04 { color: #a09f93; }
.base05 { color: #d3d0c8; }
.base06 { color: #e8e6df; }
.base07 { color: #f2f0ec; }
.base08 { color: #f2777a; }
.base09 { color: #f99157; }
.base0A { color: #ffcc66; }
.base0B { color: #99cc99; }
.base0C { color: #66cccc; }
.base0D { color: #6699cc; }
.base0E { color: #cc99cc; }
.base0F { color: #d27b53; }

View File

@ -1,33 +0,0 @@
.base00-background { background-color: #16130F; }
.base01-background { background-color: #2C2620; }
.base02-background { background-color: #433B32; }
.base03-background { background-color: #5A5047; }
.base04-background { background-color: #8A8075; }
.base05-background { background-color: #A39A90; }
.base06-background { background-color: #BEB6AE; }
.base07-background { background-color: #DBD6D1; }
.base08-background { background-color: #826D57; }
.base09-background { background-color: #828257; }
.base0A-background { background-color: #6D8257; }
.base0B-background { background-color: #57826D; }
.base0C-background { background-color: #576D82; }
.base0D-background { background-color: #6D5782; }
.base0E-background { background-color: #82576D; }
.base0F-background { background-color: #825757; }
.base00 { color: #16130F; }
.base01 { color: #2C2620; }
.base02 { color: #433B32; }
.base03 { color: #5A5047; }
.base04 { color: #8A8075; }
.base05 { color: #A39A90; }
.base06 { color: #BEB6AE; }
.base07 { color: #DBD6D1; }
.base08 { color: #826D57; }
.base09 { color: #828257; }
.base0A { color: #6D8257; }
.base0B { color: #57826D; }
.base0C { color: #576D82; }
.base0D { color: #6D5782; }
.base0E { color: #82576D; }
.base0F { color: #825757; }

View File

@ -1,33 +0,0 @@
.base00-background { background-color: #2C3E50; }
.base01-background { background-color: #34495E; }
.base02-background { background-color: #7F8C8D; }
.base03-background { background-color: #95A5A6; }
.base04-background { background-color: #BDC3C7; }
.base05-background { background-color: #e0e0e0; }
.base06-background { background-color: #f5f5f5; }
.base07-background { background-color: #ECF0F1; }
.base08-background { background-color: #E74C3C; }
.base09-background { background-color: #E67E22; }
.base0A-background { background-color: #F1C40F; }
.base0B-background { background-color: #2ECC71; }
.base0C-background { background-color: #1ABC9C; }
.base0D-background { background-color: #3498DB; }
.base0E-background { background-color: #9B59B6; }
.base0F-background { background-color: #be643c; }
.base00 { color: #2C3E50; }
.base01 { color: #34495E; }
.base02 { color: #7F8C8D; }
.base03 { color: #95A5A6; }
.base04 { color: #BDC3C7; }
.base05 { color: #e0e0e0; }
.base06 { color: #f5f5f5; }
.base07 { color: #ECF0F1; }
.base08 { color: #E74C3C; }
.base09 { color: #E67E22; }
.base0A { color: #F1C40F; }
.base0B { color: #2ECC71; }
.base0C { color: #1ABC9C; }
.base0D { color: #3498DB; }
.base0E { color: #9B59B6; }
.base0F { color: #be643c; }

View File

@ -1,33 +0,0 @@
.base00-background { background-color: #ffffff; }
.base01-background { background-color: #f5f5f5; }
.base02-background { background-color: #c8c8fa; }
.base03-background { background-color: #969896; }
.base04-background { background-color: #e8e8e8; }
.base05-background { background-color: #333333; }
.base06-background { background-color: #ffffff; }
.base07-background { background-color: #ffffff; }
.base08-background { background-color: #ed6a43; }
.base09-background { background-color: #0086b3; }
.base0A-background { background-color: #795da3; }
.base0B-background { background-color: #183691; }
.base0C-background { background-color: #183691; }
.base0D-background { background-color: #795da3; }
.base0E-background { background-color: #a71d5d; }
.base0F-background { background-color: #333333; }
.base00 { color: #ffffff; }
.base01 { color: #f5f5f5; }
.base02 { color: #c8c8fa; }
.base03 { color: #969896; }
.base04 { color: #e8e8e8; }
.base05 { color: #333333; }
.base06 { color: #ffffff; }
.base07 { color: #ffffff; }
.base08 { color: #ed6a43; }
.base09 { color: #0086b3; }
.base0A { color: #795da3; }
.base0B { color: #183691; }
.base0C { color: #183691; }
.base0D { color: #795da3; }
.base0E { color: #a71d5d; }
.base0F { color: #333333; }

View File

@ -1,33 +0,0 @@
.base00-background { background-color: #1d1f21; }
.base01-background { background-color: #282a2e; }
.base02-background { background-color: #373b41; }
.base03-background { background-color: #969896; }
.base04-background { background-color: #b4b7b4; }
.base05-background { background-color: #c5c8c6; }
.base06-background { background-color: #e0e0e0; }
.base07-background { background-color: #ffffff; }
.base08-background { background-color: #CC342B; }
.base09-background { background-color: #F96A38; }
.base0A-background { background-color: #FBA922; }
.base0B-background { background-color: #198844; }
.base0C-background { background-color: #3971ED; }
.base0D-background { background-color: #3971ED; }
.base0E-background { background-color: #A36AC7; }
.base0F-background { background-color: #3971ED; }
.base00 { color: #1d1f21; }
.base01 { color: #282a2e; }
.base02 { color: #373b41; }
.base03 { color: #969896; }
.base04 { color: #b4b7b4; }
.base05 { color: #c5c8c6; }
.base06 { color: #e0e0e0; }
.base07 { color: #ffffff; }
.base08 { color: #CC342B; }
.base09 { color: #F96A38; }
.base0A { color: #FBA922; }
.base0B { color: #198844; }
.base0C { color: #3971ED; }
.base0D { color: #3971ED; }
.base0E { color: #A36AC7; }
.base0F { color: #3971ED; }

View File

@ -1,33 +0,0 @@
.base00-background { background-color: #ffffff; }
.base01-background { background-color: #e0e0e0; }
.base02-background { background-color: #c5c8c6; }
.base03-background { background-color: #b4b7b4; }
.base04-background { background-color: #969896; }
.base05-background { background-color: #373b41; }
.base06-background { background-color: #282a2e; }
.base07-background { background-color: #1d1f21; }
.base08-background { background-color: #CC342B; }
.base09-background { background-color: #F96A38; }
.base0A-background { background-color: #FBA922; }
.base0B-background { background-color: #198844; }
.base0C-background { background-color: #3971ED; }
.base0D-background { background-color: #3971ED; }
.base0E-background { background-color: #A36AC7; }
.base0F-background { background-color: #3971ED; }
.base00 { color: #ffffff; }
.base01 { color: #e0e0e0; }
.base02 { color: #c5c8c6; }
.base03 { color: #b4b7b4; }
.base04 { color: #969896; }
.base05 { color: #373b41; }
.base06 { color: #282a2e; }
.base07 { color: #1d1f21; }
.base08 { color: #CC342B; }
.base09 { color: #F96A38; }
.base0A { color: #FBA922; }
.base0B { color: #198844; }
.base0C { color: #3971ED; }
.base0D { color: #3971ED; }
.base0E { color: #A36AC7; }
.base0F { color: #3971ED; }

View File

@ -1,33 +0,0 @@
.base00-background { background-color: #101010; }
.base01-background { background-color: #252525; }
.base02-background { background-color: #464646; }
.base03-background { background-color: #525252; }
.base04-background { background-color: #ababab; }
.base05-background { background-color: #b9b9b9; }
.base06-background { background-color: #e3e3e3; }
.base07-background { background-color: #f7f7f7; }
.base08-background { background-color: #7c7c7c; }
.base09-background { background-color: #999999; }
.base0A-background { background-color: #a0a0a0; }
.base0B-background { background-color: #8e8e8e; }
.base0C-background { background-color: #868686; }
.base0D-background { background-color: #686868; }
.base0E-background { background-color: #747474; }
.base0F-background { background-color: #5e5e5e; }
.base00 { color: #101010; }
.base01 { color: #252525; }
.base02 { color: #464646; }
.base03 { color: #525252; }
.base04 { color: #ababab; }
.base05 { color: #b9b9b9; }
.base06 { color: #e3e3e3; }
.base07 { color: #f7f7f7; }
.base08 { color: #7c7c7c; }
.base09 { color: #999999; }
.base0A { color: #a0a0a0; }
.base0B { color: #8e8e8e; }
.base0C { color: #868686; }
.base0D { color: #686868; }
.base0E { color: #747474; }
.base0F { color: #5e5e5e; }

View File

@ -1,33 +0,0 @@
.base00-background { background-color: #f7f7f7; }
.base01-background { background-color: #e3e3e3; }
.base02-background { background-color: #b9b9b9; }
.base03-background { background-color: #ababab; }
.base04-background { background-color: #525252; }
.base05-background { background-color: #464646; }
.base06-background { background-color: #252525; }
.base07-background { background-color: #101010; }
.base08-background { background-color: #7c7c7c; }
.base09-background { background-color: #999999; }
.base0A-background { background-color: #a0a0a0; }
.base0B-background { background-color: #8e8e8e; }
.base0C-background { background-color: #868686; }
.base0D-background { background-color: #686868; }
.base0E-background { background-color: #747474; }
.base0F-background { background-color: #5e5e5e; }
.base00 { color: #f7f7f7; }
.base01 { color: #e3e3e3; }
.base02 { color: #b9b9b9; }
.base03 { color: #ababab; }
.base04 { color: #525252; }
.base05 { color: #464646; }
.base06 { color: #252525; }
.base07 { color: #101010; }
.base08 { color: #7c7c7c; }
.base09 { color: #999999; }
.base0A { color: #a0a0a0; }
.base0B { color: #8e8e8e; }
.base0C { color: #868686; }
.base0D { color: #686868; }
.base0E { color: #747474; }
.base0F { color: #5e5e5e; }

View File

@ -1,33 +0,0 @@
.base00-background { background-color: #001100; }
.base01-background { background-color: #003300; }
.base02-background { background-color: #005500; }
.base03-background { background-color: #007700; }
.base04-background { background-color: #009900; }
.base05-background { background-color: #00bb00; }
.base06-background { background-color: #00dd00; }
.base07-background { background-color: #00ff00; }
.base08-background { background-color: #007700; }
.base09-background { background-color: #009900; }
.base0A-background { background-color: #007700; }
.base0B-background { background-color: #00bb00; }
.base0C-background { background-color: #005500; }
.base0D-background { background-color: #009900; }
.base0E-background { background-color: #00bb00; }
.base0F-background { background-color: #005500; }
.base00 { color: #001100; }
.base01 { color: #003300; }
.base02 { color: #005500; }
.base03 { color: #007700; }
.base04 { color: #009900; }
.base05 { color: #00bb00; }
.base06 { color: #00dd00; }
.base07 { color: #00ff00; }
.base08 { color: #007700; }
.base09 { color: #009900; }
.base0A { color: #007700; }
.base0B { color: #00bb00; }
.base0C { color: #005500; }
.base0D { color: #009900; }
.base0E { color: #00bb00; }
.base0F { color: #005500; }

View File

@ -1,33 +0,0 @@
.base00-background { background-color: #0b1c2c; }
.base01-background { background-color: #223b54; }
.base02-background { background-color: #405c79; }
.base03-background { background-color: #627e99; }
.base04-background { background-color: #aabcce; }
.base05-background { background-color: #cbd6e2; }
.base06-background { background-color: #e5ebf1; }
.base07-background { background-color: #f7f9fb; }
.base08-background { background-color: #bf8b56; }
.base09-background { background-color: #bfbf56; }
.base0A-background { background-color: #8bbf56; }
.base0B-background { background-color: #56bf8b; }
.base0C-background { background-color: #568bbf; }
.base0D-background { background-color: #8b56bf; }
.base0E-background { background-color: #bf568b; }
.base0F-background { background-color: #bf5656; }
.base00 { color: #0b1c2c; }
.base01 { color: #223b54; }
.base02 { color: #405c79; }
.base03 { color: #627e99; }
.base04 { color: #aabcce; }
.base05 { color: #cbd6e2; }
.base06 { color: #e5ebf1; }
.base07 { color: #f7f9fb; }
.base08 { color: #bf8b56; }
.base09 { color: #bfbf56; }
.base0A { color: #8bbf56; }
.base0B { color: #56bf8b; }
.base0C { color: #568bbf; }
.base0D { color: #8b56bf; }
.base0E { color: #bf568b; }
.base0F { color: #bf5656; }

View File

@ -1,33 +0,0 @@
.base00-background { background-color: #f7f9fb; }
.base01-background { background-color: #e5ebf1; }
.base02-background { background-color: #cbd6e2; }
.base03-background { background-color: #aabcce; }
.base04-background { background-color: #627e99; }
.base05-background { background-color: #405c79; }
.base06-background { background-color: #223b54; }
.base07-background { background-color: #0b1c2c; }
.base08-background { background-color: #bf8b56; }
.base09-background { background-color: #bfbf56; }
.base0A-background { background-color: #8bbf56; }
.base0B-background { background-color: #56bf8b; }
.base0C-background { background-color: #568bbf; }
.base0D-background { background-color: #8b56bf; }
.base0E-background { background-color: #bf568b; }
.base0F-background { background-color: #bf5656; }
.base00 { color: #f7f9fb; }
.base01 { color: #e5ebf1; }
.base02 { color: #cbd6e2; }
.base03 { color: #aabcce; }
.base04 { color: #627e99; }
.base05 { color: #405c79; }
.base06 { color: #223b54; }
.base07 { color: #0b1c2c; }
.base08 { color: #bf8b56; }
.base09 { color: #bfbf56; }
.base0A { color: #8bbf56; }
.base0B { color: #56bf8b; }
.base0C { color: #568bbf; }
.base0D { color: #8b56bf; }
.base0E { color: #bf568b; }
.base0F { color: #bf5656; }

View File

@ -1,33 +0,0 @@
.base00-background { background-color: #322931; }
.base01-background { background-color: #433b42; }
.base02-background { background-color: #5c545b; }
.base03-background { background-color: #797379; }
.base04-background { background-color: #989498; }
.base05-background { background-color: #b9b5b8; }
.base06-background { background-color: #d5d3d5; }
.base07-background { background-color: #ffffff; }
.base08-background { background-color: #dd464c; }
.base09-background { background-color: #fd8b19; }
.base0A-background { background-color: #fdcc59; }
.base0B-background { background-color: #8fc13e; }
.base0C-background { background-color: #149b93; }
.base0D-background { background-color: #1290bf; }
.base0E-background { background-color: #c85e7c; }
.base0F-background { background-color: #b33508; }
.base00 { color: #322931; }
.base01 { color: #433b42; }
.base02 { color: #5c545b; }
.base03 { color: #797379; }
.base04 { color: #989498; }
.base05 { color: #b9b5b8; }
.base06 { color: #d5d3d5; }
.base07 { color: #ffffff; }
.base08 { color: #dd464c; }
.base09 { color: #fd8b19; }
.base0A { color: #fdcc59; }
.base0B { color: #8fc13e; }
.base0C { color: #149b93; }
.base0D { color: #1290bf; }
.base0E { color: #c85e7c; }
.base0F { color: #b33508; }

View File

@ -1,33 +0,0 @@
.base00-background { background-color: #000000; }
.base01-background { background-color: #242422; }
.base02-background { background-color: #484844; }
.base03-background { background-color: #6c6c66; }
.base04-background { background-color: #918f88; }
.base05-background { background-color: #b5b3aa; }
.base06-background { background-color: #d9d7cc; }
.base07-background { background-color: #fdfbee; }
.base08-background { background-color: #ff6c60; }
.base09-background { background-color: #e9c062; }
.base0A-background { background-color: #ffffb6; }
.base0B-background { background-color: #a8ff60; }
.base0C-background { background-color: #c6c5fe; }
.base0D-background { background-color: #96cbfe; }
.base0E-background { background-color: #ff73fd; }
.base0F-background { background-color: #b18a3d; }
.base00 { color: #000000; }
.base01 { color: #242422; }
.base02 { color: #484844; }
.base03 { color: #6c6c66; }
.base04 { color: #918f88; }
.base05 { color: #b5b3aa; }
.base06 { color: #d9d7cc; }
.base07 { color: #fdfbee; }
.base08 { color: #ff6c60; }
.base09 { color: #e9c062; }
.base0A { color: #ffffb6; }
.base0B { color: #a8ff60; }
.base0C { color: #c6c5fe; }
.base0D { color: #96cbfe; }
.base0E { color: #ff73fd; }
.base0F { color: #b18a3d; }

View File

@ -1,33 +0,0 @@
.base00-background { background-color: #000000; }
.base01-background { background-color: #404040; }
.base02-background { background-color: #606060; }
.base03-background { background-color: #808080; }
.base04-background { background-color: #c0c0c0; }
.base05-background { background-color: #d0d0d0; }
.base06-background { background-color: #e0e0e0; }
.base07-background { background-color: #ffffff; }
.base08-background { background-color: #ff0000; }
.base09-background { background-color: #ff9900; }
.base0A-background { background-color: #ff0099; }
.base0B-background { background-color: #33ff00; }
.base0C-background { background-color: #00ffff; }
.base0D-background { background-color: #0066ff; }
.base0E-background { background-color: #cc00ff; }
.base0F-background { background-color: #3300ff; }
.base00 { color: #000000; }
.base01 { color: #404040; }
.base02 { color: #606060; }
.base03 { color: #808080; }
.base04 { color: #c0c0c0; }
.base05 { color: #d0d0d0; }
.base06 { color: #e0e0e0; }
.base07 { color: #ffffff; }
.base08 { color: #ff0000; }
.base09 { color: #ff9900; }
.base0A { color: #ff0099; }
.base0B { color: #33ff00; }
.base0C { color: #00ffff; }
.base0D { color: #0066ff; }
.base0E { color: #cc00ff; }
.base0F { color: #3300ff; }

View File

@ -1,33 +0,0 @@
.base00-background { background-color: #231f20; }
.base01-background { background-color: #1c3f95; }
.base02-background { background-color: #5a5758; }
.base03-background { background-color: #737171; }
.base04-background { background-color: #959ca1; }
.base05-background { background-color: #d9d8d8; }
.base06-background { background-color: #e7e7e8; }
.base07-background { background-color: #ffffff; }
.base08-background { background-color: #ee2e24; }
.base09-background { background-color: #f386a1; }
.base0A-background { background-color: #ffd204; }
.base0B-background { background-color: #00853e; }
.base0C-background { background-color: #85cebc; }
.base0D-background { background-color: #009ddc; }
.base0E-background { background-color: #98005d; }
.base0F-background { background-color: #b06110; }
.base00 { color: #231f20; }
.base01 { color: #1c3f95; }
.base02 { color: #5a5758; }
.base03 { color: #737171; }
.base04 { color: #959ca1; }
.base05 { color: #d9d8d8; }
.base06 { color: #e7e7e8; }
.base07 { color: #ffffff; }
.base08 { color: #ee2e24; }
.base09 { color: #f386a1; }
.base0A { color: #ffd204; }
.base0B { color: #00853e; }
.base0C { color: #85cebc; }
.base0D { color: #009ddc; }
.base0E { color: #98005d; }
.base0F { color: #b06110; }

View File

@ -1,33 +0,0 @@
.base00-background { background-color: #000000; }
.base01-background { background-color: #404040; }
.base02-background { background-color: #404040; }
.base03-background { background-color: #808080; }
.base04-background { background-color: #808080; }
.base05-background { background-color: #c0c0c0; }
.base06-background { background-color: #c0c0c0; }
.base07-background { background-color: #ffffff; }
.base08-background { background-color: #dd0907; }
.base09-background { background-color: #ff6403; }
.base0A-background { background-color: #fbf305; }
.base0B-background { background-color: #1fb714; }
.base0C-background { background-color: #02abea; }
.base0D-background { background-color: #0000d3; }
.base0E-background { background-color: #4700a5; }
.base0F-background { background-color: #90713a; }
.base00 { color: #000000; }
.base01 { color: #404040; }
.base02 { color: #404040; }
.base03 { color: #808080; }
.base04 { color: #808080; }
.base05 { color: #c0c0c0; }
.base06 { color: #c0c0c0; }
.base07 { color: #ffffff; }
.base08 { color: #dd0907; }
.base09 { color: #ff6403; }
.base0A { color: #fbf305; }
.base0B { color: #1fb714; }
.base0C { color: #02abea; }
.base0D { color: #0000d3; }
.base0E { color: #4700a5; }
.base0F { color: #90713a; }

View File

@ -1,33 +0,0 @@
.base00-background { background-color: #201602; }
.base01-background { background-color: #302e00; }
.base02-background { background-color: #5f5b17; }
.base03-background { background-color: #6c6823; }
.base04-background { background-color: #86813b; }
.base05-background { background-color: #948e48; }
.base06-background { background-color: #ccc37a; }
.base07-background { background-color: #faf0a5; }
.base08-background { background-color: #c35359; }
.base09-background { background-color: #b36144; }
.base0A-background { background-color: #a88339; }
.base0B-background { background-color: #18974e; }
.base0C-background { background-color: #75a738; }
.base0D-background { background-color: #477ca1; }
.base0E-background { background-color: #8868b3; }
.base0F-background { background-color: #b3588e; }
.base00 { color: #201602; }
.base01 { color: #302e00; }
.base02 { color: #5f5b17; }
.base03 { color: #6c6823; }
.base04 { color: #86813b; }
.base05 { color: #948e48; }
.base06 { color: #ccc37a; }
.base07 { color: #faf0a5; }
.base08 { color: #c35359; }
.base09 { color: #b36144; }
.base0A { color: #a88339; }
.base0B { color: #18974e; }
.base0C { color: #75a738; }
.base0D { color: #477ca1; }
.base0E { color: #8868b3; }
.base0F { color: #b3588e; }

View File

@ -1,33 +0,0 @@
.base00-background { background-color: #263238; }
.base01-background { background-color: #2C393F; }
.base02-background { background-color: #37474F; }
.base03-background { background-color: #707880; }
.base04-background { background-color: #C9CCD3; }
.base05-background { background-color: #CDD3DE; }
.base06-background { background-color: #D5DBE5; }
.base07-background { background-color: #FFFFFF; }
.base08-background { background-color: #EC5F67; }
.base09-background { background-color: #EA9560; }
.base0A-background { background-color: #FFCC00; }
.base0B-background { background-color: #8BD649; }
.base0C-background { background-color: #80CBC4; }
.base0D-background { background-color: #89DDFF; }
.base0E-background { background-color: #82AAFF; }
.base0F-background { background-color: #EC5F67; }
.base00 { color: #263238; }
.base01 { color: #2C393F; }
.base02 { color: #37474F; }
.base03 { color: #707880; }
.base04 { color: #C9CCD3; }
.base05 { color: #CDD3DE; }
.base06 { color: #D5DBE5; }
.base07 { color: #FFFFFF; }
.base08 { color: #EC5F67; }
.base09 { color: #EA9560; }
.base0A { color: #FFCC00; }
.base0B { color: #8BD649; }
.base0C { color: #80CBC4; }
.base0D { color: #89DDFF; }
.base0E { color: #82AAFF; }
.base0F { color: #EC5F67; }

View File

@ -1,33 +0,0 @@
.base00-background { background-color: #f8f8f8; }
.base01-background { background-color: #e8e8e8; }
.base02-background { background-color: #d8d8d8; }
.base03-background { background-color: #b8b8b8; }
.base04-background { background-color: #585858; }
.base05-background { background-color: #383838; }
.base06-background { background-color: #282828; }
.base07-background { background-color: #181818; }
.base08-background { background-color: #ab4642; }
.base09-background { background-color: #dc9656; }
.base0A-background { background-color: #f79a0e; }
.base0B-background { background-color: #538947; }
.base0C-background { background-color: #4b8093; }
.base0D-background { background-color: #7cafc2; }
.base0E-background { background-color: #96609e; }
.base0F-background { background-color: #a16946; }
.base00 { color: #f8f8f8; }
.base01 { color: #e8e8e8; }
.base02 { color: #d8d8d8; }
.base03 { color: #b8b8b8; }
.base04 { color: #585858; }
.base05 { color: #383838; }
.base06 { color: #282828; }
.base07 { color: #181818; }
.base08 { color: #ab4642; }
.base09 { color: #dc9656; }
.base0A { color: #f79a0e; }
.base0B { color: #538947; }
.base0C { color: #4b8093; }
.base0D { color: #7cafc2; }
.base0E { color: #96609e; }
.base0F { color: #a16946; }

View File

@ -1,33 +0,0 @@
.base00-background { background-color: #3B3228; }
.base01-background { background-color: #534636; }
.base02-background { background-color: #645240; }
.base03-background { background-color: #7e705a; }
.base04-background { background-color: #b8afad; }
.base05-background { background-color: #d0c8c6; }
.base06-background { background-color: #e9e1dd; }
.base07-background { background-color: #f5eeeb; }
.base08-background { background-color: #cb6077; }
.base09-background { background-color: #d28b71; }
.base0A-background { background-color: #f4bc87; }
.base0B-background { background-color: #beb55b; }
.base0C-background { background-color: #7bbda4; }
.base0D-background { background-color: #8ab3b5; }
.base0E-background { background-color: #a89bb9; }
.base0F-background { background-color: #bb9584; }
.base00 { color: #3B3228; }
.base01 { color: #534636; }
.base02 { color: #645240; }
.base03 { color: #7e705a; }
.base04 { color: #b8afad; }
.base05 { color: #d0c8c6; }
.base06 { color: #e9e1dd; }
.base07 { color: #f5eeeb; }
.base08 { color: #cb6077; }
.base09 { color: #d28b71; }
.base0A { color: #f4bc87; }
.base0B { color: #beb55b; }
.base0C { color: #7bbda4; }
.base0D { color: #8ab3b5; }
.base0E { color: #a89bb9; }
.base0F { color: #bb9584; }

View File

@ -1,33 +0,0 @@
.base00-background { background-color: #272822; }
.base01-background { background-color: #383830; }
.base02-background { background-color: #49483e; }
.base03-background { background-color: #75715e; }
.base04-background { background-color: #a59f85; }
.base05-background { background-color: #f8f8f2; }
.base06-background { background-color: #f5f4f1; }
.base07-background { background-color: #f9f8f5; }
.base08-background { background-color: #f92672; }
.base09-background { background-color: #fd971f; }
.base0A-background { background-color: #f4bf75; }
.base0B-background { background-color: #a6e22e; }
.base0C-background { background-color: #a1efe4; }
.base0D-background { background-color: #66d9ef; }
.base0E-background { background-color: #ae81ff; }
.base0F-background { background-color: #cc6633; }
.base00 { color: #272822; }
.base01 { color: #383830; }
.base02 { color: #49483e; }
.base03 { color: #75715e; }
.base04 { color: #a59f85; }
.base05 { color: #f8f8f2; }
.base06 { color: #f5f4f1; }
.base07 { color: #f9f8f5; }
.base08 { color: #f92672; }
.base09 { color: #fd971f; }
.base0A { color: #f4bf75; }
.base0B { color: #a6e22e; }
.base0C { color: #a1efe4; }
.base0D { color: #66d9ef; }
.base0E { color: #ae81ff; }
.base0F { color: #cc6633; }

View File

@ -1,33 +0,0 @@
.base00-background { background-color: #2b303b; }
.base01-background { background-color: #343d46; }
.base02-background { background-color: #4f5b66; }
.base03-background { background-color: #65737e; }
.base04-background { background-color: #a7adba; }
.base05-background { background-color: #c0c5ce; }
.base06-background { background-color: #dfe1e8; }
.base07-background { background-color: #eff1f5; }
.base08-background { background-color: #bf616a; }
.base09-background { background-color: #d08770; }
.base0A-background { background-color: #ebcb8b; }
.base0B-background { background-color: #a3be8c; }
.base0C-background { background-color: #96b5b4; }
.base0D-background { background-color: #8fa1b3; }
.base0E-background { background-color: #b48ead; }
.base0F-background { background-color: #ab7967; }
.base00 { color: #2b303b; }
.base01 { color: #343d46; }
.base02 { color: #4f5b66; }
.base03 { color: #65737e; }
.base04 { color: #a7adba; }
.base05 { color: #c0c5ce; }
.base06 { color: #dfe1e8; }
.base07 { color: #eff1f5; }
.base08 { color: #bf616a; }
.base09 { color: #d08770; }
.base0A { color: #ebcb8b; }
.base0B { color: #a3be8c; }
.base0C { color: #96b5b4; }
.base0D { color: #8fa1b3; }
.base0E { color: #b48ead; }
.base0F { color: #ab7967; }

View File

@ -1,33 +0,0 @@
.base00-background { background-color: #1B2B34; }
.base01-background { background-color: #343D46; }
.base02-background { background-color: #4F5B66; }
.base03-background { background-color: #65737E; }
.base04-background { background-color: #A7ADBA; }
.base05-background { background-color: #C0C5CE; }
.base06-background { background-color: #CDD3DE; }
.base07-background { background-color: #D8DEE9; }
.base08-background { background-color: #EC5f67; }
.base09-background { background-color: #F99157; }
.base0A-background { background-color: #FAC863; }
.base0B-background { background-color: #99C794; }
.base0C-background { background-color: #5FB3B3; }
.base0D-background { background-color: #6699CC; }
.base0E-background { background-color: #C594C5; }
.base0F-background { background-color: #AB7967; }
.base00 { color: #1B2B34; }
.base01 { color: #343D46; }
.base02 { color: #4F5B66; }
.base03 { color: #65737E; }
.base04 { color: #A7ADBA; }
.base05 { color: #C0C5CE; }
.base06 { color: #CDD3DE; }
.base07 { color: #D8DEE9; }
.base08 { color: #EC5f67; }
.base09 { color: #F99157; }
.base0A { color: #FAC863; }
.base0B { color: #99C794; }
.base0C { color: #5FB3B3; }
.base0D { color: #6699CC; }
.base0E { color: #C594C5; }
.base0F { color: #AB7967; }

View File

@ -1,33 +0,0 @@
.base00-background { background-color: #2f1e2e; }
.base01-background { background-color: #41323f; }
.base02-background { background-color: #4f424c; }
.base03-background { background-color: #776e71; }
.base04-background { background-color: #8d8687; }
.base05-background { background-color: #a39e9b; }
.base06-background { background-color: #b9b6b0; }
.base07-background { background-color: #e7e9db; }
.base08-background { background-color: #ef6155; }
.base09-background { background-color: #f99b15; }
.base0A-background { background-color: #fec418; }
.base0B-background { background-color: #48b685; }
.base0C-background { background-color: #5bc4bf; }
.base0D-background { background-color: #06b6ef; }
.base0E-background { background-color: #815ba4; }
.base0F-background { background-color: #e96ba8; }
.base00 { color: #2f1e2e; }
.base01 { color: #41323f; }
.base02 { color: #4f424c; }
.base03 { color: #776e71; }
.base04 { color: #8d8687; }
.base05 { color: #a39e9b; }
.base06 { color: #b9b6b0; }
.base07 { color: #e7e9db; }
.base08 { color: #ef6155; }
.base09 { color: #f99b15; }
.base0A { color: #fec418; }
.base0B { color: #48b685; }
.base0C { color: #5bc4bf; }
.base0D { color: #06b6ef; }
.base0E { color: #815ba4; }
.base0F { color: #e96ba8; }

View File

@ -1,33 +0,0 @@
.base00-background { background-color: #061229; }
.base01-background { background-color: #2a3448; }
.base02-background { background-color: #4d5666; }
.base03-background { background-color: #717885; }
.base04-background { background-color: #9a99a3; }
.base05-background { background-color: #b8bbc2; }
.base06-background { background-color: #dbdde0; }
.base07-background { background-color: #ffffff; }
.base08-background { background-color: #d07346; }
.base09-background { background-color: #f0a000; }
.base0A-background { background-color: #fbd461; }
.base0B-background { background-color: #99bf52; }
.base0C-background { background-color: #72b9bf; }
.base0D-background { background-color: #5299bf; }
.base0E-background { background-color: #9989cc; }
.base0F-background { background-color: #b08060; }
.base00 { color: #061229; }
.base01 { color: #2a3448; }
.base02 { color: #4d5666; }
.base03 { color: #717885; }
.base04 { color: #9a99a3; }
.base05 { color: #b8bbc2; }
.base06 { color: #dbdde0; }
.base07 { color: #ffffff; }
.base08 { color: #d07346; }
.base09 { color: #f0a000; }
.base0A { color: #fbd461; }
.base0B { color: #99bf52; }
.base0C { color: #72b9bf; }
.base0D { color: #5299bf; }
.base0E { color: #9989cc; }
.base0F { color: #b08060; }

View File

@ -1,33 +0,0 @@
.base00-background { background-color: #000000; }
.base01-background { background-color: #1d2b53; }
.base02-background { background-color: #7e2553; }
.base03-background { background-color: #008751; }
.base04-background { background-color: #ab5236; }
.base05-background { background-color: #5f574f; }
.base06-background { background-color: #c2c3c7; }
.base07-background { background-color: #fff1e8; }
.base08-background { background-color: #ff004d; }
.base09-background { background-color: #ffa300; }
.base0A-background { background-color: #fff024; }
.base0B-background { background-color: #00e756; }
.base0C-background { background-color: #29adff; }
.base0D-background { background-color: #83769c; }
.base0E-background { background-color: #ff77a8; }
.base0F-background { background-color: #ffccaa; }
.base00 { color: #000000; }
.base01 { color: #1d2b53; }
.base02 { color: #7e2553; }
.base03 { color: #008751; }
.base04 { color: #ab5236; }
.base05 { color: #5f574f; }
.base06 { color: #c2c3c7; }
.base07 { color: #fff1e8; }
.base08 { color: #ff004d; }
.base09 { color: #ffa300; }
.base0A { color: #fff024; }
.base0B { color: #00e756; }
.base0C { color: #29adff; }
.base0D { color: #83769c; }
.base0E { color: #ff77a8; }
.base0F { color: #ffccaa; }

View File

@ -1,33 +0,0 @@
.base00-background { background-color: #161c20; }
.base01-background { background-color: #282e32; }
.base02-background { background-color: #343a3f; }
.base03-background { background-color: #4e5256; }
.base04-background { background-color: #ababab; }
.base05-background { background-color: #b9b9b9; }
.base06-background { background-color: #d0d0d0; }
.base07-background { background-color: #e7e7e7; }
.base08-background { background-color: #baaa9c; }
.base09-background { background-color: #999999; }
.base0A-background { background-color: #a0a0a0; }
.base0B-background { background-color: #8e8e8e; }
.base0C-background { background-color: #868686; }
.base0D-background { background-color: #686868; }
.base0E-background { background-color: #747474; }
.base0F-background { background-color: #5e5e5e; }
.base00 { color: #161c20; }
.base01 { color: #282e32; }
.base02 { color: #343a3f; }
.base03 { color: #4e5256; }
.base04 { color: #ababab; }
.base05 { color: #b9b9b9; }
.base06 { color: #d0d0d0; }
.base07 { color: #e7e7e7; }
.base08 { color: #baaa9c; }
.base09 { color: #999999; }
.base0A { color: #a0a0a0; }
.base0B { color: #8e8e8e; }
.base0C { color: #868686; }
.base0D { color: #686868; }
.base0E { color: #747474; }
.base0F { color: #5e5e5e; }

View File

@ -1,33 +0,0 @@
.base00-background { background-color: #f2f4f6; }
.base01-background { background-color: #dde2e6; }
.base02-background { background-color: #c0c6cb; }
.base03-background { background-color: #a4a4a4; }
.base04-background { background-color: #545454; }
.base05-background { background-color: #304055; }
.base06-background { background-color: #040404; }
.base07-background { background-color: #000000; }
.base08-background { background-color: #e92f2f; }
.base09-background { background-color: #e09448; }
.base0A-background { background-color: #dddd13; }
.base0B-background { background-color: #0ed839; }
.base0C-background { background-color: #23edda; }
.base0D-background { background-color: #3b48e3; }
.base0E-background { background-color: #f996e2; }
.base0F-background { background-color: #69542d; }
.base00 { color: #f2f4f6; }
.base01 { color: #dde2e6; }
.base02 { color: #c0c6cb; }
.base03 { color: #a4a4a4; }
.base04 { color: #545454; }
.base05 { color: #304055; }
.base06 { color: #040404; }
.base07 { color: #000000; }
.base08 { color: #e46f0f; }
.base09 { color: #e09448; }
.base0A { color: #dddd13; }
.base0B { color: #0ed839; }
.base0C { color: #23edda; }
.base0D { color: #3b48e3; }
.base0E { color: #f996e2; }
.base0F { color: #69542d; }

View File

@ -1,33 +0,0 @@
.base00-background { background-color: #000000; }
.base01-background { background-color: #202020; }
.base02-background { background-color: #303030; }
.base03-background { background-color: #505050; }
.base04-background { background-color: #b0b0b0; }
.base05-background { background-color: #d0d0d0; }
.base06-background { background-color: #e0e0e0; }
.base07-background { background-color: #ffffff; }
.base08-background { background-color: #eb008a; }
.base09-background { background-color: #f29333; }
.base0A-background { background-color: #f8ca12; }
.base0B-background { background-color: #37b349; }
.base0C-background { background-color: #00aabb; }
.base0D-background { background-color: #0e5a94; }
.base0E-background { background-color: #b31e8d; }
.base0F-background { background-color: #7a2d00; }
.base00 { color: #000000; }
.base01 { color: #202020; }
.base02 { color: #303030; }
.base03 { color: #505050; }
.base04 { color: #b0b0b0; }
.base05 { color: #d0d0d0; }
.base06 { color: #e0e0e0; }
.base07 { color: #ffffff; }
.base08 { color: #eb008a; }
.base09 { color: #f29333; }
.base0A { color: #f8ca12; }
.base0B { color: #37b349; }
.base0C { color: #00aabb; }
.base0D { color: #0e5a94; }
.base0E { color: #b31e8d; }
.base0F { color: #7a2d00; }

View File

@ -1,33 +0,0 @@
.base00-background { background-color: #2b2b2b; }
.base01-background { background-color: #272935; }
.base02-background { background-color: #3a4055; }
.base03-background { background-color: #5a647e; }
.base04-background { background-color: #d4cfc9; }
.base05-background { background-color: #e6e1dc; }
.base06-background { background-color: #f4f1ed; }
.base07-background { background-color: #f9f7f3; }
.base08-background { background-color: #da4939; }
.base09-background { background-color: #cc7833; }
.base0A-background { background-color: #ffc66d; }
.base0B-background { background-color: #a5c261; }
.base0C-background { background-color: #519f50; }
.base0D-background { background-color: #6d9cbe; }
.base0E-background { background-color: #b6b3eb; }
.base0F-background { background-color: #bc9458; }
.base00 { color: #2b2b2b; }
.base01 { color: #272935; }
.base02 { color: #3a4055; }
.base03 { color: #5a647e; }
.base04 { color: #d4cfc9; }
.base05 { color: #e6e1dc; }
.base06 { color: #f4f1ed; }
.base07 { color: #f9f7f3; }
.base08 { color: #da4939; }
.base09 { color: #cc7833; }
.base0A { color: #ffc66d; }
.base0B { color: #a5c261; }
.base0C { color: #519f50; }
.base0D { color: #6d9cbe; }
.base0E { color: #b6b3eb; }
.base0F { color: #bc9458; }

View File

@ -1,33 +0,0 @@
.base00-background { background-color: #151718; }
.base01-background { background-color: #8ec43d; }
.base02-background { background-color: #3B758C; }
.base03-background { background-color: #41535B; }
.base04-background { background-color: #43a5d5; }
.base05-background { background-color: #d6d6d6; }
.base06-background { background-color: #eeeeee; }
.base07-background { background-color: #ffffff; }
.base08-background { background-color: #Cd3f45; }
.base09-background { background-color: #db7b55; }
.base0A-background { background-color: #e6cd69; }
.base0B-background { background-color: #9fca56; }
.base0C-background { background-color: #55dbbe; }
.base0D-background { background-color: #55b5db; }
.base0E-background { background-color: #a074c4; }
.base0F-background { background-color: #8a553f; }
.base00 { color: #151718; }
.base01 { color: #8ec43d; }
.base02 { color: #3B758C; }
.base03 { color: #41535B; }
.base04 { color: #43a5d5; }
.base05 { color: #d6d6d6; }
.base06 { color: #eeeeee; }
.base07 { color: #ffffff; }
.base08 { color: #Cd3f45; }
.base09 { color: #db7b55; }
.base0A { color: #e6cd69; }
.base0B { color: #9fca56; }
.base0C { color: #55dbbe; }
.base0D { color: #55b5db; }
.base0E { color: #a074c4; }
.base0F { color: #8a553f; }

View File

@ -1,33 +0,0 @@
.base00-background { background-color: #f9f9f9; }
.base01-background { background-color: #e0e0e0; }
.base02-background { background-color: #ababab; }
.base03-background { background-color: #555555; }
.base04-background { background-color: #343434; }
.base05-background { background-color: #102015; }
.base06-background { background-color: #040404; }
.base07-background { background-color: #000000; }
.base08-background { background-color: #e92f2f; }
.base09-background { background-color: #e09448; }
.base0A-background { background-color: #dddd13; }
.base0B-background { background-color: #0ed839; }
.base0C-background { background-color: #23edda; }
.base0D-background { background-color: #3b48e3; }
.base0E-background { background-color: #f996e2; }
.base0F-background { background-color: #69542d; }
.base00 { color: #f9f9f9; }
.base01 { color: #e0e0e0; }
.base02 { color: #ababab; }
.base03 { color: #555555; }
.base04 { color: #343434; }
.base05 { color: #102015; }
.base06 { color: #040404; }
.base07 { color: #000000; }
.base08 { color: #e92f2f; }
.base09 { color: #e09448; }
.base0A { color: #dddd13; }
.base0B { color: #0ed839; }
.base0C { color: #23edda; }
.base0D { color: #3b48e3; }
.base0E { color: #f996e2; }
.base0F { color: #69542d; }

View File

@ -1,33 +0,0 @@
.base00-background { background-color: #18262F; }
.base01-background { background-color: #222E38; }
.base02-background { background-color: #586875; }
.base03-background { background-color: #667581; }
.base04-background { background-color: #85939E; }
.base05-background { background-color: #A6AFB8; }
.base06-background { background-color: #E8E9ED; }
.base07-background { background-color: #F5F7FA; }
.base08-background { background-color: #EF5253; }
.base09-background { background-color: #E66B2B; }
.base0A-background { background-color: #E4B51C; }
.base0B-background { background-color: #7CC844; }
.base0C-background { background-color: #52CBB0; }
.base0D-background { background-color: #33B5E1; }
.base0E-background { background-color: #A363D5; }
.base0F-background { background-color: #D73C9A; }
.base00 { color: #18262F; }
.base01 { color: #222E38; }
.base02 { color: #586875; }
.base03 { color: #667581; }
.base04 { color: #85939E; }
.base05 { color: #A6AFB8; }
.base06 { color: #E8E9ED; }
.base07 { color: #F5F7FA; }
.base08 { color: #EF5253; }
.base09 { color: #E66B2B; }
.base0A { color: #E4B51C; }
.base0B { color: #7CC844; }
.base0C { color: #52CBB0; }
.base0D { color: #33B5E1; }
.base0E { color: #A363D5; }
.base0F { color: #D73C9A; }

View File

@ -1,33 +0,0 @@
.base00-background { background-color: #002b36; }
.base01-background { background-color: #073642; }
.base02-background { background-color: #586e75; }
.base03-background { background-color: #657b83; }
.base04-background { background-color: #839496; }
.base05-background { background-color: #93a1a1; }
.base06-background { background-color: #eee8d5; }
.base07-background { background-color: #fdf6e3; }
.base08-background { background-color: #dc322f; }
.base09-background { background-color: #cb4b16; }
.base0A-background { background-color: #b58900; }
.base0B-background { background-color: #859900; }
.base0C-background { background-color: #2aa198; }
.base0D-background { background-color: #268bd2; }
.base0E-background { background-color: #6c71c4; }
.base0F-background { background-color: #d33682; }
.base00 { color: #002b36; }
.base01 { color: #073642; }
.base02 { color: #586e75; }
.base03 { color: #657b83; }
.base04 { color: #839496; }
.base05 { color: #93a1a1; }
.base06 { color: #eee8d5; }
.base07 { color: #fdf6e3; }
.base08 { color: #dc322f; }
.base09 { color: #cb4b16; }
.base0A { color: #b58900; }
.base0B { color: #859900; }
.base0C { color: #2aa198; }
.base0D { color: #268bd2; }
.base0E { color: #6c71c4; }
.base0F { color: #d33682; }

View File

@ -1,33 +0,0 @@
.base00-background { background-color: #fdf6e3; }
.base01-background { background-color: #eee8d5; }
.base02-background { background-color: #93a1a1; }
.base03-background { background-color: #839496; }
.base04-background { background-color: #657b83; }
.base05-background { background-color: #586e75; }
.base06-background { background-color: #073642; }
.base07-background { background-color: #002b36; }
.base08-background { background-color: #dc322f; }
.base09-background { background-color: #cb4b16; }
.base0A-background { background-color: #b58900; }
.base0B-background { background-color: #859900; }
.base0C-background { background-color: #2aa198; }
.base0D-background { background-color: #268bd2; }
.base0E-background { background-color: #6c71c4; }
.base0F-background { background-color: #d33682; }
.base00 { color: #fdf6e3; }
.base01 { color: #eee8d5; }
.base02 { color: #93a1a1; }
.base03 { color: #839496; }
.base04 { color: #657b83; }
.base05 { color: #586e75; }
.base06 { color: #073642; }
.base07 { color: #002b36; }
.base08 { color: #dc322f; }
.base09 { color: #cb4b16; }
.base0A { color: #b58900; }
.base0B { color: #859900; }
.base0C { color: #2aa198; }
.base0D { color: #268bd2; }
.base0E { color: #6c71c4; }
.base0F { color: #d33682; }

Some files were not shown because too many files have changed in this diff Show More