From 80bebea9e624d469690498f1204e704f4ad862ae Mon Sep 17 00:00:00 2001 From: Xeltica <7106976+Xeltica@users.noreply.github.com> Date: Sat, 11 Jul 2020 12:12:35 +0900 Subject: テーマエディターの実装 (#6482) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * テーマ機能の実装 * resolve #6478 * 定数を削除できるように * 変更を破棄するか確認ダイアログを表示するように * fix code * Update theme.ts * :v: * fix path * wip * wip * wip Co-authored-by: syuilo --- src/client/app.vue | 4 + src/client/pages/preferences/theme.vue | 6 +- src/client/pages/room/room.vue | 2 +- src/client/pages/theme-editor.vue | 343 +++++++++++++++++++++++++++++++++ src/client/router.ts | 1 + src/client/scripts/theme-editor.ts | 74 +++++++ src/client/scripts/theme.ts | 2 + src/client/themes/_dark.json5 | 2 - src/client/themes/_light.json5 | 2 - src/client/themes/halloween.json5 | 1 - 10 files changed, 427 insertions(+), 10 deletions(-) create mode 100644 src/client/pages/theme-editor.vue create mode 100644 src/client/scripts/theme-editor.ts (limited to 'src/client') diff --git a/src/client/app.vue b/src/client/app.vue index 4f39183564..a751d5db44 100644 --- a/src/client/app.vue +++ b/src/client/app.vue @@ -131,6 +131,10 @@ export default Vue.extend({ computed: { keymap(): any { return { + 'd': () => { + if (this.$store.state.device.syncDeviceDarkMode) return; + this.$store.commit('device/set', { key: 'darkMode', value: !this.$store.state.device.darkMode }); + }, 'p': this.post, 'n': this.post, 's': this.search, diff --git a/src/client/pages/preferences/theme.vue b/src/client/pages/preferences/theme.vue index 246787fa58..173ccd7091 100644 --- a/src/client/pages/preferences/theme.vue +++ b/src/client/pages/preferences/theme.vue @@ -22,6 +22,7 @@ + {{ $t('syncDeviceDarkMode') }}
@@ -42,10 +43,7 @@ - {{ $t('_theme.explore') }} -
-
- {{ $t('syncDeviceDarkMode') }} + {{ $t('_theme.explore') }}{{ $t('_theme.make') }}
{{ $t('setWallpaper') }} diff --git a/src/client/pages/room/room.vue b/src/client/pages/room/room.vue index cf6850526f..05b93c04e8 100644 --- a/src/client/pages/room/room.vue +++ b/src/client/pages/room/room.vue @@ -143,7 +143,7 @@ export default Vue.extend({ if (this.changed) { this.$root.dialog({ type: 'warning', - text: this.$t('leave-confirm'), + text: this.$t('leaveConfirm'), showCancelButton: true }).then(({ canceled }) => { if (canceled) { diff --git a/src/client/pages/theme-editor.vue b/src/client/pages/theme-editor.vue new file mode 100644 index 0000000000..3a3fbfa2d7 --- /dev/null +++ b/src/client/pages/theme-editor.vue @@ -0,0 +1,343 @@ + + + + + diff --git a/src/client/router.ts b/src/client/router.ts index cf98c57bd7..a741aeb955 100644 --- a/src/client/router.ts +++ b/src/client/router.ts @@ -24,6 +24,7 @@ export const router = new VueRouter({ { path: '/about-misskey', component: page('about-misskey') }, { path: '/featured', component: page('featured') }, { path: '/docs', component: page('docs') }, + { path: '/theme-editor', component: page('theme-editor') }, { path: '/docs/:doc', component: page('doc'), props: true }, { path: '/explore', component: page('explore') }, { path: '/explore/tags/:tag', props: true, component: page('explore') }, diff --git a/src/client/scripts/theme-editor.ts b/src/client/scripts/theme-editor.ts new file mode 100644 index 0000000000..e0c3bc25bc --- /dev/null +++ b/src/client/scripts/theme-editor.ts @@ -0,0 +1,74 @@ +import { v4 as uuid} from 'uuid'; + +import { themeProps, Theme } from './theme'; + +export type Default = null; +export type Color = string; +export type FuncName = 'alpha' | 'darken' | 'lighten'; +export type Func = { type: 'func', name: FuncName, arg: number, value: string }; +export type RefProp = { type: 'refProp', key: string }; +export type RefConst = { type: 'refConst', key: string }; + +export type ThemeValue = Color | Func | RefProp | RefConst | Default; + +export type ThemeViewModel = [ string, ThemeValue ][]; + +export const fromThemeString = (str?: string) : ThemeValue => { + if (!str) return null; + if (str.startsWith(':')) { + const parts = str.slice(1).split('<'); + const name = parts[0] as FuncName; + const arg = parseFloat(parts[1]); + const value = parts[2].startsWith('@') ? parts[2].slice(1) : ''; + return { type: 'func', name, arg, value }; + } else if (str.startsWith('@')) { + return { + type: 'refProp', + key: str.slice(1), + }; + } else if (str.startsWith('$')) { + return { + type: 'refConst', + key: str.slice(1), + }; + } else { + return str; + } +}; + +export const toThemeString = (value: Color | Func | RefProp | RefConst) => { + if (typeof value === 'string') return value; + switch (value.type) { + case 'func': return `:${value.name}<${value.arg}<@${value.value}`; + case 'refProp': return `@${value.key}`; + case 'refConst': return `$${value.key}`; + } +}; + +export const convertToMisskeyTheme = (vm: ThemeViewModel, name: string, desc: string, author: string, base: 'dark' | 'light'): Theme => { + const props = { } as { [key: string]: string }; + for (const [ key, value ] of vm) { + if (value === null) continue; + props[key] = toThemeString(value); + } + + return { + id: uuid(), + name, desc, author, props, base + }; +}; + +export const convertToViewModel = (theme: Theme): ThemeViewModel => { + const vm: ThemeViewModel = []; + // プロパティの登録 + vm.push(...themeProps.map(key => [ key, fromThemeString(theme.props[key])] as [ string, ThemeValue ])); + + // 定数の登録 + const consts = Object + .keys(theme.props) + .filter(k => k.startsWith('$')) + .map(k => [ k, fromThemeString(theme.props[k]) ] as [ string, ThemeValue ]); + + vm.push(...consts); + return vm; +}; diff --git a/src/client/scripts/theme.ts b/src/client/scripts/theme.ts index d458df45f0..30eaf77e01 100644 --- a/src/client/scripts/theme.ts +++ b/src/client/scripts/theme.ts @@ -12,6 +12,8 @@ export type Theme = { export const lightTheme: Theme = require('../themes/_light.json5'); export const darkTheme: Theme = require('../themes/_dark.json5'); +export const themeProps = Object.keys(lightTheme.props).filter(key => !key.startsWith('X')); + export const builtinThemes = [ require('../themes/white.json5'), require('../themes/black.json5'), diff --git a/src/client/themes/_dark.json5 b/src/client/themes/_dark.json5 index 4e5225db36..8d7a345fd5 100644 --- a/src/client/themes/_dark.json5 +++ b/src/client/themes/_dark.json5 @@ -12,12 +12,10 @@ accent: '#86b300', accentDarken: ':darken<10<@accent', accentLighten: ':lighten<10<@accent', - accentShadow: ':alpha<0.3<@accent', focus: ':alpha<0.3<@accent', bg: '#000', fg: '#c7d1d8', fgHighlighted: ':lighten<3<@fg', - html: '@bg', divider: 'rgba(255, 255, 255, 0.1)', indicator: '@accent', panel: '#000', diff --git a/src/client/themes/_light.json5 b/src/client/themes/_light.json5 index 2317ddef65..4e499e178c 100644 --- a/src/client/themes/_light.json5 +++ b/src/client/themes/_light.json5 @@ -12,12 +12,10 @@ accent: '#86b300', accentDarken: ':darken<10<@accent', accentLighten: ':lighten<10<@accent', - accentShadow: ':alpha<0.4<@accent', focus: ':alpha<0.3<@accent', bg: '#fafafa', fg: '#5c6a73', fgHighlighted: ':darken<3<@fg', - html: '@bg', divider: 'rgba(0, 0, 0, 0.1)', indicator: '@accent', panel: '#fff', diff --git a/src/client/themes/halloween.json5 b/src/client/themes/halloween.json5 index 7cabf01d1e..1394c793ed 100644 --- a/src/client/themes/halloween.json5 +++ b/src/client/themes/halloween.json5 @@ -12,7 +12,6 @@ panel: '#1f1d30', bg: '#0f0e17', fg: '#b1bee3', - html: '@accent', renote: '@accent', }, } -- cgit v1.2.3-freya