From 904114740b2f87743c332e029a4c6817ebb9e17d Mon Sep 17 00:00:00 2001 From: syuilo Date: Wed, 26 Sep 2018 20:19:35 +0900 Subject: wip --- package.json | 2 ++ 1 file changed, 2 insertions(+) (limited to 'package.json') diff --git a/package.json b/package.json index 1b786d1a38..a3e3b56be4 100644 --- a/package.json +++ b/package.json @@ -75,6 +75,7 @@ "@types/single-line-log": "1.1.0", "@types/speakeasy": "2.0.2", "@types/systeminformation": "3.23.0", + "@types/tinycolor2": "1.4.1", "@types/tmp": "0.0.33", "@types/uuid": "3.4.4", "@types/webpack": "4.4.12", @@ -194,6 +195,7 @@ "systeminformation": "3.45.6", "syuilo-password-strength": "0.0.1", "textarea-caret": "3.1.0", + "tinycolor2": "1.4.1", "tmp": "0.0.33", "ts-loader": "4.4.1", "ts-node": "7.0.1", -- cgit v1.2.3-freya From cbf5d4b71d807e53e2252e8767364f7a7e761b34 Mon Sep 17 00:00:00 2001 From: syuilo Date: Fri, 28 Sep 2018 00:48:17 +0900 Subject: wip --- package.json | 1 + src/client/app/desktop/views/pages/welcome.vue | 8 +- src/client/assets/title.dark.svg | 140 ------------------------- src/client/assets/title.light.svg | 140 ------------------------- src/client/assets/title.svg | 140 +++++++++++++++++++++++++ webpack.config.ts | 2 + 6 files changed, 148 insertions(+), 283 deletions(-) delete mode 100644 src/client/assets/title.dark.svg delete mode 100644 src/client/assets/title.light.svg create mode 100644 src/client/assets/title.svg (limited to 'package.json') diff --git a/package.json b/package.json index a3e3b56be4..347e9d0c24 100644 --- a/package.json +++ b/package.json @@ -214,6 +214,7 @@ "vue-loader": "15.4.2", "vue-router": "3.0.1", "vue-style-loader": "4.1.2", + "vue-svg-inline-loader": "1.1.3", "vue-template-compiler": "2.5.17", "vuedraggable": "2.16.0", "vuewordcloud": "18.7.11", diff --git a/src/client/app/desktop/views/pages/welcome.vue b/src/client/app/desktop/views/pages/welcome.vue index 261642cab4..f8f49bf997 100644 --- a/src/client/app/desktop/views/pages/welcome.vue +++ b/src/client/app/desktop/views/pages/welcome.vue @@ -14,7 +14,7 @@

{{ name }}

-

+

{{ host }} - @@ -377,9 +377,11 @@ export default Vue.extend({ > h1 margin 0 - > img + > svg margin -8px 0 0 -16px - max-width 280px + width 280px + height 100px + fill currentColor > .info margin 0 auto 16px auto diff --git a/src/client/assets/title.dark.svg b/src/client/assets/title.dark.svg deleted file mode 100644 index 10139024ad..0000000000 --- a/src/client/assets/title.dark.svg +++ /dev/null @@ -1,140 +0,0 @@ - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - diff --git a/src/client/assets/title.light.svg b/src/client/assets/title.light.svg deleted file mode 100644 index 95ad11c399..0000000000 --- a/src/client/assets/title.light.svg +++ /dev/null @@ -1,140 +0,0 @@ - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - diff --git a/src/client/assets/title.svg b/src/client/assets/title.svg new file mode 100644 index 0000000000..0e4e0b8b3b --- /dev/null +++ b/src/client/assets/title.svg @@ -0,0 +1,140 @@ + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + diff --git a/webpack.config.ts b/webpack.config.ts index 3822080574..3b14ee4a8a 100644 --- a/webpack.config.ts +++ b/webpack.config.ts @@ -134,6 +134,8 @@ module.exports = { preserveWhitespace: false } } + }, { + loader: 'vue-svg-inline-loader' }, { loader: 'replace', query: { -- cgit v1.2.3-freya From 6a82e94c5489d4879cbbf86091cd15c7d144f284 Mon Sep 17 00:00:00 2001 From: syuilo Date: Sat, 29 Sep 2018 00:01:11 +0900 Subject: wip --- locales/ja-JP.yml | 24 +++ package.json | 1 + src/client/app/app.vue | 12 +- src/client/app/common/scripts/theme.ts | 89 ---------- src/client/app/common/views/components/avatar.vue | 4 +- src/client/app/common/views/components/index.ts | 2 + src/client/app/common/views/components/theme.vue | 179 +++++++++++++++++++++ .../app/common/views/components/ui/button.vue | 12 +- .../app/desktop/views/components/settings.vue | 5 + src/client/app/init.ts | 35 +++- src/client/app/mobile/views/pages/settings.vue | 7 + src/client/app/store.ts | 3 + src/client/app/theme.ts | 100 ++++++++++++ src/client/theme/dark.json | 2 +- src/client/theme/halloween.json | 3 +- src/client/theme/light.json | 2 +- 16 files changed, 368 insertions(+), 112 deletions(-) delete mode 100644 src/client/app/common/scripts/theme.ts create mode 100644 src/client/app/common/views/components/theme.vue create mode 100644 src/client/app/theme.ts (limited to 'package.json') diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 0f83ba8419..46dea949d2 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -285,6 +285,28 @@ common/views/components/media-banner.vue: sensitive: "閲覧注意" click-to-show: "クリックして表示" +common/views/components/theme.vue: + light-theme: "非ダークモード時に使用するテーマ" + dark-theme: "ダークモード時に使用するテーマ" + install-a-theme: "テーマのインストール" + theme-code: "テーマコード" + install: "インストール" + create-a-theme: "テーマの作成" + save-created-theme: "テーマを保存" + primary-color: "プライマリ カラー" + secondary-color: "セカンダリ カラー" + text-color: "文字色" + base-theme: "ベーステーマ" + base-theme-light: "Light" + base-theme-dark: "Dark" + theme-name: "テーマ名" + preview-created-theme: "プレビュー" + invalid-theme: "テーマが正しくありません。" + already-installed: "既にそのテーマはインストールされています。" + saved: "保存しました" + installed-themes: "インストールされたテーマ" + select-theme: "テーマを選択してください" + common/views/components/cw-button.vue: hide: "隠す" show: "もっと見る" @@ -762,6 +784,7 @@ desktop/views/components/settings.vue: 2fa: "二段階認証" other: "その他" license: "ライセンス" + theme: "テーマ" behaviour: "動作" fetch-on-scroll: "スクロールで自動読み込み" @@ -1417,6 +1440,7 @@ mobile/views/pages/settings.vue: notification-position: "通知の表示" notification-position-bottom: "下" notification-position-top: "上" + theme: "テーマ" behavior: "動作" fetch-on-scroll: "スクロールで自動読み込み" note-visibility: "投稿の公開範囲" diff --git a/package.json b/package.json index 347e9d0c24..e19283cf64 100644 --- a/package.json +++ b/package.json @@ -208,6 +208,7 @@ "v-animate-css": "0.0.2", "vue": "2.5.17", "vue-chartjs": "3.4.0", + "vue-color": "2.6.0", "vue-cropperjs": "2.2.2", "vue-js-modal": "1.3.26", "vue-json-tree-view": "2.1.4", diff --git a/src/client/app/app.vue b/src/client/app/app.vue index 9b6af27ece..778e9f29cf 100644 --- a/src/client/app/app.vue +++ b/src/client/app/app.vue @@ -14,8 +14,7 @@ export default Vue.extend({ keymap(): any { return { 'h|slash': this.help, - 'd': this.dark, - 'x': this.test + 'd': this.dark }; } }, @@ -26,11 +25,10 @@ export default Vue.extend({ }, dark() { - applyTheme(darkTheme); - }, - - test() { - applyTheme(halloweenTheme); + this.$store.commit('device/set', { + key: 'darkmode', + value: !this.$store.state.device.darkmode + }); } } }); diff --git a/src/client/app/common/scripts/theme.ts b/src/client/app/common/scripts/theme.ts deleted file mode 100644 index 7a1c6abb76..0000000000 --- a/src/client/app/common/scripts/theme.ts +++ /dev/null @@ -1,89 +0,0 @@ -import * as tinycolor from 'tinycolor2'; -const lightTheme = require('../../../theme/light'); -const darkTheme = require('../../../theme/dark'); - -type Theme = { - meta: { - id: string; - name: string; - inherit: string; - vars: any; - }; -} & { - [key: string]: string; -}; - -export default function(theme: Theme) { - if (theme.meta.inherit) { - const inherit = [lightTheme, darkTheme].find(x => x.meta.id == theme.meta.inherit); - theme = Object.assign({}, inherit, theme); - } - - const props = compile(theme); - - Object.entries(props).forEach(([k, v]) => { - if (k == 'meta') return; - document.documentElement.style.setProperty(`--${k}`, v.toString()); - }); - - localStorage.setItem('theme', JSON.stringify(props)); -} - -function compile(theme: Theme): { [key: string]: string } { - function getColor(code: string): tinycolor.Instance { - // ref - if (code[0] == '@') { - return getColor(theme[code.substr(1)]); - } - if (code[0] == '$') { - return getColor(theme.meta.vars[code.substr(1)]); - } - - // func - if (code[0] == ':') { - const parts = code.split('<'); - const func = parts.shift().substr(1); - const arg = parseFloat(parts.shift()); - const color = getColor(parts.join('<')); - - switch (func) { - case 'darken': return color.darken(arg); - case 'lighten': return color.lighten(arg); - case 'alpha': return color.setAlpha(arg); - } - } - - return tinycolor(code); - } - - const props = {}; - - Object.entries(theme).forEach(([k, v]) => { - if (k == 'meta') return; - const c = getColor(v); - props[k] = genValue(c); - }); - - const primary = getColor(props['primary']); - - for (let i = 1; i < 10; i++) { - const color = primary.clone().setAlpha(i / 10); - props['primaryAlpha0' + i] = genValue(color); - } - - for (let i = 1; i < 100; i++) { - const color = primary.clone().lighten(i); - props['primaryLighten' + i] = genValue(color); - } - - for (let i = 1; i < 100; i++) { - const color = primary.clone().darken(i); - props['primaryDarken' + i] = genValue(color); - } - - return props; -} - -function genValue(c: tinycolor.Instance): string { - return c.toRgbString(); -} diff --git a/src/client/app/common/views/components/avatar.vue b/src/client/app/common/views/components/avatar.vue index ca09af87de..ac018abcfc 100644 --- a/src/client/app/common/views/components/avatar.vue +++ b/src/client/app/common/views/components/avatar.vue @@ -59,7 +59,9 @@ export default Vue.extend({ } }, mounted() { - this.$el.style.color = `rgb(${this.user.avatarColor.slice(0, 3).join(',')})`; + if (this.user.avatarColor) { + this.$el.style.color = `rgb(${this.user.avatarColor.slice(0, 3).join(',')})`; + } }, methods: { onClick(e) { diff --git a/src/client/app/common/views/components/index.ts b/src/client/app/common/views/components/index.ts index 4c1c0afa80..0dea38a7a1 100644 --- a/src/client/app/common/views/components/index.ts +++ b/src/client/app/common/views/components/index.ts @@ -1,5 +1,6 @@ import Vue from 'vue'; +import theme from './theme.vue'; import instance from './instance.vue'; import cwButton from './cw-button.vue'; import tagCloud from './tag-cloud.vue'; @@ -43,6 +44,7 @@ import uiSelect from './ui/select.vue'; import formButton from './ui/form/button.vue'; import formRadio from './ui/form/radio.vue'; +Vue.component('mk-theme', theme); Vue.component('mk-instance', instance); Vue.component('mk-cw-button', cwButton); Vue.component('mk-tag-cloud', tagCloud); diff --git a/src/client/app/common/views/components/theme.vue b/src/client/app/common/views/components/theme.vue new file mode 100644 index 0000000000..27888d1e85 --- /dev/null +++ b/src/client/app/common/views/components/theme.vue @@ -0,0 +1,179 @@ + + + + + diff --git a/src/client/app/common/views/components/ui/button.vue b/src/client/app/common/views/components/ui/button.vue index a165d100a4..47644b32b5 100644 --- a/src/client/app/common/views/components/ui/button.vue +++ b/src/client/app/common/views/components/ui/button.vue @@ -27,14 +27,6 @@ export default Vue.extend({ return { styl: 'fill' }; - }, - inject: { - isCardChild: { default: false } - }, - created() { - if (this.isCardChild) { - this.styl = 'line'; - } } }); @@ -43,6 +35,7 @@ export default Vue.extend({ .dmtdnykelhudezerjlfpbhgovrgnqqgr display block width 100% + min-height 40px margin 0 padding 0 font-weight normal @@ -52,6 +45,9 @@ export default Vue.extend({ outline none box-shadow none + &:not(.inline) + .dmtdnykelhudezerjlfpbhgovrgnqqgr + margin-top 16px + &.inline display inline-block width auto diff --git a/src/client/app/desktop/views/components/settings.vue b/src/client/app/desktop/views/components/settings.vue index c7d82590ea..1cb8d4d4c8 100644 --- a/src/client/app/desktop/views/components/settings.vue +++ b/src/client/app/desktop/views/components/settings.vue @@ -19,6 +19,11 @@ +
+

%i18n:@theme%

+ +
+

%i18n:@behaviour%

diff --git a/src/client/app/init.ts b/src/client/app/init.ts index 8d430ad7ff..802f7b42eb 100644 --- a/src/client/app/init.ts +++ b/src/client/app/init.ts @@ -14,11 +14,11 @@ import App from './app.vue'; import checkForUpdate from './common/scripts/check-for-update'; import MiOS, { API } from './mios'; import { version, codename, lang } from './config'; -import applyTheme from './common/scripts/theme'; -const defaultTheme = require('../theme/light.json'); +import { builtinThemes, applyTheme } from './theme'; +const lightTheme = require('../theme/light.json'); if (localStorage.getItem('theme') == null) { - applyTheme(defaultTheme); + applyTheme(lightTheme); } Vue.use(Vuex); @@ -92,6 +92,35 @@ export default (callback: (launch: (router: VueRouter, api?: (os: MiOS) => API) const launch = (router: VueRouter, api?: (os: MiOS) => API) => { os.apis = api ? api(os) : null; + //#region theme + os.store.watch(s => { + return s.device.darkmode; + }, v => { + const themes = os.store.state.device.themes.concat(builtinThemes); + const dark = themes.find(t => t.meta.id == os.store.state.device.darkTheme); + const light = themes.find(t => t.meta.id == os.store.state.device.lightTheme); + applyTheme(v ? dark : light); + }); + os.store.watch(s => { + return s.device.lightTheme; + }, v => { + const themes = os.store.state.device.themes.concat(builtinThemes); + const theme = themes.find(t => t.meta.id == v); + if (!os.store.state.device.darkmode) { + applyTheme(theme); + } + }); + os.store.watch(s => { + return s.device.darkTheme; + }, v => { + const themes = os.store.state.device.themes.concat(builtinThemes); + const theme = themes.find(t => t.meta.id == v); + if (os.store.state.device.darkmode) { + applyTheme(theme); + } + }); + //#endregion + //#region shadow const shadow = '0 3px 8px rgba(0, 0, 0, 0.2)'; if (os.store.state.settings.useShadow) document.documentElement.style.setProperty('--shadow', shadow); diff --git a/src/client/app/mobile/views/pages/settings.vue b/src/client/app/mobile/views/pages/settings.vue index b83eaf6d33..94fa38cec9 100644 --- a/src/client/app/mobile/views/pages/settings.vue +++ b/src/client/app/mobile/views/pages/settings.vue @@ -23,6 +23,13 @@ %i18n:common.use-contrast-reversi-stones%
+
+
%i18n:@theme%
+
+ +
+
+
%i18n:@timeline%
diff --git a/src/client/app/store.ts b/src/client/app/store.ts index fbcc53d7be..545261225a 100644 --- a/src/client/app/store.ts +++ b/src/client/app/store.ts @@ -44,6 +44,9 @@ const defaultDeviceSettings = { apiViaStream: true, autoPopout: false, darkmode: false, + darkTheme: 'dark', + lightTheme: 'light', + themes: [], enableSounds: true, soundVolume: 0.5, lang: null, diff --git a/src/client/app/theme.ts b/src/client/app/theme.ts new file mode 100644 index 0000000000..1147ff300d --- /dev/null +++ b/src/client/app/theme.ts @@ -0,0 +1,100 @@ +import * as tinycolor from 'tinycolor2'; + +type Theme = { + meta: { + id: string; + name: string; + author: string; + base?: string; + vars: any; + }; +} & { + [key: string]: string; +}; + +export function applyTheme(theme: Theme, persisted = true) { + if (theme.meta.base) { + const base = [lightTheme, darkTheme].find(x => x.meta.id == theme.meta.base); + theme = Object.assign({}, base, theme); + } + + const props = compile(theme); + + Object.entries(props).forEach(([k, v]) => { + if (k == 'meta') return; + document.documentElement.style.setProperty(`--${k}`, v.toString()); + }); + + if (persisted) { + localStorage.setItem('theme', JSON.stringify(props)); + } +} + +function compile(theme: Theme): { [key: string]: string } { + function getColor(code: string): tinycolor.Instance { + // ref + if (code[0] == '@') { + return getColor(theme[code.substr(1)]); + } + if (code[0] == '$') { + return getColor(theme.meta.vars[code.substr(1)]); + } + + // func + if (code[0] == ':') { + const parts = code.split('<'); + const func = parts.shift().substr(1); + const arg = parseFloat(parts.shift()); + const color = getColor(parts.join('<')); + + switch (func) { + case 'darken': return color.darken(arg); + case 'lighten': return color.lighten(arg); + case 'alpha': return color.setAlpha(arg); + } + } + + return tinycolor(code); + } + + const props = {}; + + Object.entries(theme).forEach(([k, v]) => { + if (k == 'meta') return; + const c = getColor(v); + props[k] = genValue(c); + }); + + const primary = getColor(props['primary']); + + for (let i = 1; i < 10; i++) { + const color = primary.clone().setAlpha(i / 10); + props['primaryAlpha0' + i] = genValue(color); + } + + for (let i = 1; i < 100; i++) { + const color = primary.clone().lighten(i); + props['primaryLighten' + i] = genValue(color); + } + + for (let i = 1; i < 100; i++) { + const color = primary.clone().darken(i); + props['primaryDarken' + i] = genValue(color); + } + + return props; +} + +function genValue(c: tinycolor.Instance): string { + return c.toRgbString(); +} + +export const lightTheme = require('../theme/light.json'); +export const darkTheme = require('../theme/dark.json'); +export const halloweenTheme = require('../theme/halloween.json'); + +export const builtinThemes = [ + lightTheme, + darkTheme, + halloweenTheme +]; diff --git a/src/client/theme/dark.json b/src/client/theme/dark.json index 015225ddab..74447b8f2f 100644 --- a/src/client/theme/dark.json +++ b/src/client/theme/dark.json @@ -1,6 +1,6 @@ { "meta": { - "id": "9978f7f9-5616-44fd-a704-cc5985efdd63", + "id": "dark", "name": "Dark", "author": "syuilo", "vars": { diff --git a/src/client/theme/halloween.json b/src/client/theme/halloween.json index 6e92db95ff..fb34db57a8 100644 --- a/src/client/theme/halloween.json +++ b/src/client/theme/halloween.json @@ -3,10 +3,9 @@ "id": "42e4f09b-67d5-498c-af7d-29faa54745b0", "name": "Halloween", "author": "syuilo", - "inherit": "9978f7f9-5616-44fd-a704-cc5985efdd63", + "base": "dark", "vars": { "primary": "#d67036", - "primaryForeground": "#fff", "secondary": "#1f1d30", "text": "#b1bee3" } diff --git a/src/client/theme/light.json b/src/client/theme/light.json index 3d131f066a..1b6604e642 100644 --- a/src/client/theme/light.json +++ b/src/client/theme/light.json @@ -1,6 +1,6 @@ { "meta": { - "id": "406cfea3-a4e7-486c-9057-30ede1353c3f", + "id": "light", "name": "Light", "author": "syuilo", "vars": { -- cgit v1.2.3-freya