start on instance theming
This commit is contained in:
parent
eaaacdeef5
commit
f69797544d
58
bin/build-sass.js
Executable file
58
bin/build-sass.js
Executable file
|
@ -0,0 +1,58 @@
|
||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
const sass = require('node-sass')
|
||||||
|
const chokidar = require('chokidar')
|
||||||
|
const argv = require('yargs').argv
|
||||||
|
const path = require('path')
|
||||||
|
const debounce = require('lodash/debounce')
|
||||||
|
const fs = require('fs')
|
||||||
|
const pify = require('pify')
|
||||||
|
const writeFile = pify(fs.writeFile.bind(fs))
|
||||||
|
const readdir = pify(fs.readdir.bind(fs))
|
||||||
|
const render = pify(sass.render.bind(sass))
|
||||||
|
|
||||||
|
const globalScss = path.join(__dirname, '../scss/global.scss')
|
||||||
|
const defaultThemeScss = path.join(__dirname, '../scss/themes/_default.scss')
|
||||||
|
const globalCss = path.join(__dirname, '../assets/global.css')
|
||||||
|
const scssDir = path.join(__dirname, '../scss')
|
||||||
|
const themesScssDir = path.join(__dirname, '../scss/themes')
|
||||||
|
const assetsDir = path.join(__dirname, '../assets')
|
||||||
|
|
||||||
|
function doWatch() {
|
||||||
|
chokidar.watch(scssDir).on('change', debounce(() => {
|
||||||
|
compileGlobalSass()
|
||||||
|
compileThemesSass()
|
||||||
|
}, 500))
|
||||||
|
chokidar.watch()
|
||||||
|
}
|
||||||
|
|
||||||
|
async function compileGlobalSass() {
|
||||||
|
let results = await Promise.all([
|
||||||
|
render({file: defaultThemeScss}),
|
||||||
|
render({file: globalScss})
|
||||||
|
])
|
||||||
|
|
||||||
|
let css = results.map(_ => _.css).join('\n')
|
||||||
|
|
||||||
|
await writeFile(globalCss, css, 'utf8')
|
||||||
|
}
|
||||||
|
|
||||||
|
async function compileThemesSass() {
|
||||||
|
let files = (await readdir(themesScssDir)).filter(file => !path.basename(file).startsWith('_'))
|
||||||
|
await Promise.all(files.map(async file => {
|
||||||
|
let res = await render({file: path.join(themesScssDir, file)})
|
||||||
|
await writeFile(path.join(assetsDir, path.basename(file).replace(/\.scss$/, '.css')), res.css, 'utf8')
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
await Promise.all([compileGlobalSass(), compileThemesSass()])
|
||||||
|
if (argv.watch) {
|
||||||
|
doWatch()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Promise.resolve().then(main).catch(err => {
|
||||||
|
console.error(err)
|
||||||
|
process.exit(1)
|
||||||
|
})
|
1590
package-lock.json
generated
1590
package-lock.json
generated
File diff suppressed because it is too large
Load diff
10
package.json
10
package.json
|
@ -6,13 +6,14 @@
|
||||||
"dev": "concurrently --kill-others \"npm run build-sass-watch\" \"node server.js\"",
|
"dev": "concurrently --kill-others \"npm run build-sass-watch\" \"node server.js\"",
|
||||||
"build": "npm run build-sass && sapper build",
|
"build": "npm run build-sass && sapper build",
|
||||||
"start": "cross-env NODE_ENV=production node server.js",
|
"start": "cross-env NODE_ENV=production node server.js",
|
||||||
"build-sass": "node-sass scss -o assets",
|
"build-sass": "node ./bin/build-sass",
|
||||||
"build-sass-watch": "npm run build-sass && node-sass -w scss -o assets",
|
"build-sass-watch": "node ./bin/build-sass --watch",
|
||||||
"cy:run": "cypress run",
|
"cy:run": "cypress run",
|
||||||
"cy:open": "cypress open",
|
"cy:open": "cypress open",
|
||||||
"test": "run-p --race dev cy:run"
|
"test": "run-p --race dev cy:run"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"chokidar": "^2.0.0",
|
||||||
"compression": "^1.7.1",
|
"compression": "^1.7.1",
|
||||||
"concurrently": "^3.5.1",
|
"concurrently": "^3.5.1",
|
||||||
"cross-env": "^5.1.3",
|
"cross-env": "^5.1.3",
|
||||||
|
@ -22,9 +23,11 @@
|
||||||
"font-awesome-svg-png": "^1.2.2",
|
"font-awesome-svg-png": "^1.2.2",
|
||||||
"glob": "^7.1.2",
|
"glob": "^7.1.2",
|
||||||
"idb": "^2.0.4",
|
"idb": "^2.0.4",
|
||||||
|
"lodash": "^4.17.4",
|
||||||
"node-fetch": "^1.7.3",
|
"node-fetch": "^1.7.3",
|
||||||
"node-sass": "^4.7.2",
|
"node-sass": "^4.7.2",
|
||||||
"npm-run-all": "^4.1.2",
|
"npm-run-all": "^4.1.2",
|
||||||
|
"pify": "^3.0.0",
|
||||||
"sapper": "^0.3.1",
|
"sapper": "^0.3.1",
|
||||||
"serve-static": "^1.13.1",
|
"serve-static": "^1.13.1",
|
||||||
"style-loader": "^0.19.1",
|
"style-loader": "^0.19.1",
|
||||||
|
@ -32,7 +35,8 @@
|
||||||
"svelte-loader": "^2.3.3",
|
"svelte-loader": "^2.3.3",
|
||||||
"uglifyjs-webpack-plugin": "^1.1.5",
|
"uglifyjs-webpack-plugin": "^1.1.5",
|
||||||
"url-search-params": "^0.10.0",
|
"url-search-params": "^0.10.0",
|
||||||
"webpack": "^3.10.0"
|
"webpack": "^3.10.0",
|
||||||
|
"yargs": "^10.1.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 8"
|
"node": ">= 8"
|
||||||
|
|
16
routes/_static/themes.js
Normal file
16
routes/_static/themes.js
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
const themes = [
|
||||||
|
{
|
||||||
|
name: 'default',
|
||||||
|
label: 'Royal (default)'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'scarlet',
|
||||||
|
label: 'Scarlet'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'hotpants',
|
||||||
|
label: 'Hotpants'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
export { themes }
|
|
@ -74,6 +74,14 @@ store.compute(
|
||||||
}, loggedInInstances[currentInstance])
|
}, loggedInInstances[currentInstance])
|
||||||
})
|
})
|
||||||
|
|
||||||
|
store.compute(
|
||||||
|
'currentTheme',
|
||||||
|
['currentInstance', 'instanceThemes'],
|
||||||
|
(currentInstance, instanceThemes) => {
|
||||||
|
return instanceThemes[currentInstance] || 'default'
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
if (process.browser && process.env.NODE_ENV !== 'production') {
|
if (process.browser && process.env.NODE_ENV !== 'production') {
|
||||||
window.store = store // for debugging
|
window.store = store // for debugging
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,20 +15,14 @@
|
||||||
</div>
|
</div>
|
||||||
<h2>Theme:</h2>
|
<h2>Theme:</h2>
|
||||||
<form class="theme-chooser">
|
<form class="theme-chooser">
|
||||||
<div class="theme-group">
|
{{#each themes as theme}}
|
||||||
<input type="radio" name="current-theme" id="theme-default" value="default">
|
<div class="theme-group">
|
||||||
<label for="theme-default">Royal (default)</label>
|
<input type="radio" id="choice-theme-{{theme.name}}"
|
||||||
</div>
|
value="{{theme.name}}" checked="$currentTheme === theme.name"
|
||||||
<div class="theme-group">
|
bind:group="selectedTheme" on:change="onThemeChange()">
|
||||||
<input type="radio" name="current-theme" id="theme-crimson"
|
<label for="choice-theme-{{theme.name}}">{{theme.label}}</label>
|
||||||
value="crimson">
|
</div>
|
||||||
<label for="theme-crimson">Crimson and Clover</label>
|
{{/each}}
|
||||||
</div>
|
|
||||||
<div class="theme-group">
|
|
||||||
<input type="radio" name="current-theme" id="theme-hotpants"
|
|
||||||
value="hotpants">
|
|
||||||
<label for="theme-hotpants">Hot Pants</label>
|
|
||||||
</div>
|
|
||||||
</form>
|
</form>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</SettingsLayout>
|
</SettingsLayout>
|
||||||
|
@ -66,6 +60,7 @@
|
||||||
import Layout from '../../_components/Layout.html'
|
import Layout from '../../_components/Layout.html'
|
||||||
import SettingsLayout from '../_components/SettingsLayout.html'
|
import SettingsLayout from '../_components/SettingsLayout.html'
|
||||||
import { getCurrentUser } from '../../_utils/mastodon/user'
|
import { getCurrentUser } from '../../_utils/mastodon/user'
|
||||||
|
import { themes } from '../../_static/themes'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
|
@ -73,10 +68,26 @@
|
||||||
SettingsLayout
|
SettingsLayout
|
||||||
},
|
},
|
||||||
store: () => store,
|
store: () => store,
|
||||||
|
data: () => ({
|
||||||
|
themes: themes
|
||||||
|
}),
|
||||||
oncreate: async function () {
|
oncreate: async function () {
|
||||||
let currentInstanceData = this.store.get('currentInstanceData')
|
let currentInstanceData = this.store.get('currentInstanceData')
|
||||||
let currentUser = await getCurrentUser(currentInstanceData.name, currentInstanceData.access_token)
|
let currentUser = await getCurrentUser(currentInstanceData.name, currentInstanceData.access_token)
|
||||||
this.set({currentUser: currentUser})
|
this.set({
|
||||||
|
currentUser: currentUser,
|
||||||
|
selectedTheme: this.store.get('currentTheme')
|
||||||
|
})
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onThemeChange () {
|
||||||
|
let newTheme = this.get('selectedTheme')
|
||||||
|
let instanceName = this.store.get('currentInstance')
|
||||||
|
let instanceThemes = this.store.get('instanceThemes')
|
||||||
|
instanceThemes[instanceName] = newTheme
|
||||||
|
this.store.set({instanceThemes: instanceThemes})
|
||||||
|
this.store.save()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
|
@ -1,11 +1,4 @@
|
||||||
:root {
|
@mixin baseTheme() {
|
||||||
$main-theme-color: royalblue; // also: crimson, forestgreen, hotpink
|
|
||||||
$anchor-color: $main-theme-color;
|
|
||||||
$main-text-color: #333;
|
|
||||||
$border-color: #dadada;
|
|
||||||
$main-bg-color: white;
|
|
||||||
$secondary-text-color: white;
|
|
||||||
|
|
||||||
--button-primary-bg: lighten($main-theme-color, 7%);
|
--button-primary-bg: lighten($main-theme-color, 7%);
|
||||||
--button-primary-text: $secondary-text-color;
|
--button-primary-text: $secondary-text-color;
|
||||||
--button-primary-border: darken($main-theme-color, 30%);
|
--button-primary-border: darken($main-theme-color, 30%);
|
12
scss/themes/_default.scss
Normal file
12
scss/themes/_default.scss
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
$main-theme-color: royalblue;
|
||||||
|
$anchor-color: $main-theme-color;
|
||||||
|
$main-text-color: #333;
|
||||||
|
$border-color: #dadada;
|
||||||
|
$main-bg-color: white;
|
||||||
|
$secondary-text-color: white;
|
||||||
|
|
||||||
|
@import "_base.scss";
|
||||||
|
|
||||||
|
:root {
|
||||||
|
@include baseTheme()
|
||||||
|
}
|
12
scss/themes/hotpants.scss
Normal file
12
scss/themes/hotpants.scss
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
$main-theme-color: hotpink;
|
||||||
|
$anchor-color: $main-theme-color;
|
||||||
|
$main-text-color: #333;
|
||||||
|
$border-color: #dadada;
|
||||||
|
$main-bg-color: white;
|
||||||
|
$secondary-text-color: white;
|
||||||
|
|
||||||
|
@import "_base.scss";
|
||||||
|
|
||||||
|
body.theme-hotpants {
|
||||||
|
@include baseTheme()
|
||||||
|
}
|
12
scss/themes/scarlet.scss
Normal file
12
scss/themes/scarlet.scss
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
$main-theme-color: crimson;
|
||||||
|
$anchor-color: $main-theme-color;
|
||||||
|
$main-text-color: #333;
|
||||||
|
$border-color: #dadada;
|
||||||
|
$main-bg-color: white;
|
||||||
|
$secondary-text-color: white;
|
||||||
|
|
||||||
|
@import "_base.scss";
|
||||||
|
|
||||||
|
body.theme-crimson {
|
||||||
|
@include baseTheme()
|
||||||
|
}
|
|
@ -5,7 +5,6 @@
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<meta name='theme-color' content='#4169e1'>
|
<meta name='theme-color' content='#4169e1'>
|
||||||
|
|
||||||
<link rel='stylesheet' href='/theme-default.css'>
|
|
||||||
<link rel='stylesheet' href='/global.css'>
|
<link rel='stylesheet' href='/global.css'>
|
||||||
<link rel='manifest' href='/manifest.json'>
|
<link rel='manifest' href='/manifest.json'>
|
||||||
<link rel='icon' type='image/png' href='/favicon.png'>
|
<link rel='icon' type='image/png' href='/favicon.png'>
|
||||||
|
|
Loading…
Reference in a new issue