const CustomTheme = { data: { id: 'dark', name: '', author: '', customized: false, borders: false, main_font: '', headline_font: '', code_font: '', css: '', colors: {}, }, themes: [ ...CustomThemeOptions ], defaultColors: { ui: '#282c34', back: '#21252b', dark: '#17191d', border: '#181a1f', selected: '#474d5d', button: '#3a3f4b', bright_ui: '#f4f3ff', accent: '#3e90ff', frame: '#181a1f', text: '#cacad4', light: '#f4f3ff', accent_text: '#000006', bright_ui_text: '#000006', subtle_text: '#848891', grid: '#495061', wireframe: '#576f82', checkerboard: '#1c2026', }, sideloaded_themes: [], setup() { function saveChanges() { localStorage.setItem('theme', JSON.stringify(CustomTheme.data)); } if (isApp && localStorage.getItem('themes_sideloaded')) { try { let sideloaded = JSON.parse(localStorage.getItem('themes_sideloaded')); if (sideloaded instanceof Array && sideloaded.length) { CustomTheme.sideloaded_themes = sideloaded; CustomTheme.sideloaded_themes.forEachReverse(path => { if (!fs.existsSync(path)) { CustomTheme.sideloaded_themes.remove(path); } }) localStorage.setItem('themes_sideloaded', JSON.stringify(CustomTheme.sideloaded_themes)); Blockbench.read(CustomTheme.sideloaded_themes, {errorbox: false}, files => { files.forEach(file => { let data = JSON.parse(file.content); data.id = file.name.replace(/\.\w+$/, ''); if (!data.name) data.name = data.id; data.sideloaded = true; data.path = file.path; CustomTheme.themes.push(data); }) }) } } catch (err) {} } CustomTheme.dialog = new Dialog({ id: 'theme', title: 'dialog.settings.theme', singleButton: true, width: 920, title_menu: new Menu([ 'settings_window', 'keybindings_window', 'theme_window', 'about_window', ]), sidebar: { pages: { select: tl('layout.select'), options: tl('layout.options'), color: tl('layout.color'), css: tl('layout.css'), }, page: 'select', actions: [ { name: 'layout.documentation', icon: 'fa-book', click() { Blockbench.openLink('https://www.blockbench.net/wiki/blockbench/themes'); } }, 'import_theme', 'export_theme', ], onPageSwitch(page) { CustomTheme.dialog.content_vue.open_category = page; if (page == 'color' && !CustomTheme.dialog_is_setup) { CustomTheme.setupDialog() } } }, onOpen() { if (!CustomTheme.remote_themes_loaded) { CustomTheme.remote_themes_loaded = true; $.getJSON('https://api.github.com/repos/JannisX11/blockbench-themes/contents/themes').then(files => { files.forEach(async file => { try { let {content} = await $.getJSON(file.git_url); let theme = JSON.parse(patchedAtob(content)); theme.id = file.name.replace(/\.\w+/, ''); CustomTheme.themes.push(theme); } catch (err) { console.error(err); } }) }).catch(console.error) } }, component: { data: { backup: '', data: CustomTheme.data, open_category: 'select', themes: CustomTheme.themes }, components: { VuePrismEditor }, watch: { 'data.main_font'() { CustomTheme.updateSettings(); saveChanges(); }, 'data.headline_font'() { CustomTheme.updateSettings(); saveChanges(); }, 'data.code_font'() { CustomTheme.updateSettings(); saveChanges(); }, 'data.borders'() { CustomTheme.updateSettings(); saveChanges(); }, 'data.css'() { CustomTheme.updateSettings(); saveChanges(); }, 'data.colors': { handler() { CustomTheme.updateColors(); saveChanges(); }, deep: true } }, methods: { selectTheme(theme) { CustomTheme.loadTheme(theme); saveChanges(); }, loadBackup() { CustomTheme.loadTheme(JSON.parse(CustomTheme.backup_data)); CustomTheme.data.customized = true; this.clearBackup(); }, clearBackup() { this.backup = ''; CustomTheme.backup_data = null; }, customizeTheme() { CustomTheme.customizeTheme(); }, getThemeThumbnailStyle(theme) { let style = {}; for (let key in theme.colors) { style[`--color-${key}`] = theme.colors[key]; } return style; }, openContextMenu(theme, event) { if (!theme.sideloaded) return; let menu = new Menu([ { name: 'generic.remove', icon: 'clear', click: () => { this.themes.remove(theme); CustomTheme.sideloaded_themes.remove(theme.path); localStorage.setItem('themes_sideloaded', JSON.stringify(CustomTheme.sideloaded_themes)); } } ]) menu.open(event); }, tl }, computed: { listed_themes() { let themes = this.themes.slice(); if (this.data.customized) { themes.splice(0, 0, this.data); } return themes; } }, template: `
{{ tl('layout.restore_backup', [backup]) }} clear

${tl('layout.select')}

{{ theme.name }}
{{ theme.author }}

${tl('layout.color')}

{{ tl('layout.color.'+key) }}

{{ tl('layout.color.'+key+'.desc') }}

${tl('layout.options')}


${tl('layout.css')}

` }, onButton() { Settings.save(); } }) }, setupDialog() { var wrapper = $('#color_wrapper'); for (var key in CustomTheme.defaultColors) { (() => { var scope_key = key; var hex = CustomTheme.data.colors[scope_key]; var last_color = hex; var field = wrapper.find(`#color_field_${scope_key} .layout_color_preview`); field.spectrum({ preferredFormat: "hex", color: hex, showAlpha: false, showInput: true, defaultColor: CustomTheme.defaultColors[key], resetText: tl('generic.reset'), cancelText: tl('dialog.cancel'), chooseText: tl('dialog.confirm'), move(c) { CustomTheme.data.colors[scope_key] = c.toHexString(); CustomTheme.customizeTheme(); }, change(c) { last_color = c.toHexString(); }, hide(c) { CustomTheme.data.colors[scope_key] = last_color; field.spectrum('set', last_color); }, beforeShow(a, b) { last_color = CustomTheme.data.colors[scope_key]; field.spectrum('set', last_color); } }); })() } CustomTheme.dialog_is_setup = true; }, customizeTheme() { if (!CustomTheme.data.customized) { CustomTheme.data.customized = true; CustomTheme.data.name = CustomTheme.data.name ? ('Copy of ' + CustomTheme.data.name) : 'Custom Theme'; CustomTheme.data.author = settings.username.value; CustomTheme.data.id = 'custom_theme'; let i = 0; while (CustomTheme.themes.find(theme => theme.id == CustomTheme.data.id)) { i++; CustomTheme.data.id = 'custom_theme_'+i; } localStorage.setItem('theme', JSON.stringify(CustomTheme.data)); } }, updateColors() { for (var key in CustomTheme.data.colors) { var hex = CustomTheme.data.colors[key]; document.body.style.setProperty('--color-'+key, hex); } $('meta[name=theme-color]').attr('content', CustomTheme.data.colors.frame); document.body.classList.toggle('light_mode', new tinycolor(CustomTheme.data.colors.ui).isLight()); if (typeof gizmo_colors != 'undefined') { let preview_style = window.getComputedStyle(document.getElementById('preview')); function update(three_color, variable) { let string = preview_style.getPropertyValue(variable).trim(); three_color.set(string); } update(gizmo_colors.r, '--color-axis-x'); update(gizmo_colors.g, '--color-axis-y'); update(gizmo_colors.b, '--color-axis-z'); update(gizmo_colors.grid, '--color-grid'); update(Canvas.gridMaterial.color, '--color-grid'); update(Canvas.wireframeMaterial.color, '--color-wireframe'); update(gizmo_colors.solid, '--color-solid'); update(gizmo_colors.outline, '--color-outline'); update(gizmo_colors.gizmo_hover, '--color-gizmohover'); update(Canvas.outlineMaterial.color, '--color-outline'); update(Canvas.ground_plane.material.color, '--color-ground'); Canvas.pivot_marker.children.forEach(c => { c.updateColors(); }) } }, updateSettings() { document.body.style.setProperty('--font-custom-main', CustomTheme.data.main_font); document.body.style.setProperty('--font-custom-headline', CustomTheme.data.headline_font); document.body.style.setProperty('--font-custom-code', CustomTheme.data.code_font); document.body.classList.toggle('theme_borders', !!CustomTheme.data.borders); $('style#theme_css').text(CustomTheme.data.css); CustomTheme.updateColors(); }, loadTheme(theme) { var app = CustomTheme.data; if (app.customized && app.name) { // Backup if (!CustomTheme.dialog.content_vue) CustomTheme.dialog.build(); CustomTheme.dialog.content_vue.backup = app.name; CustomTheme.backup_data = JSON.stringify(app); } app.id = ''; app.name = ''; app.author = ''; app.main_font = ''; app.headline_font = ''; app.code_font = ''; app.borders = false; app.customized = false; Merge.string(app, theme, 'id') Merge.string(app, theme, 'name') Merge.string(app, theme, 'author') Merge.boolean(app, theme, 'borders') Merge.string(app, theme, 'main_font') Merge.string(app, theme, 'headline_font') Merge.string(app, theme, 'code_font') for (var key in app.colors) { if (theme.colors[key]) { Merge.string(app.colors, theme.colors, key); } else { CustomTheme.data.colors[key] = CustomTheme.defaultColors[key]; } } Merge.string(app, theme, 'css'); this.updateColors(); this.updateSettings(); }, import(file) { var data = JSON.parse(file.content) if (data && data.colors) { data.id = file.name.replace(/\.\w+$/, ''); if (!data.name) data.name = data.id; data.sideloaded = true; data.path = file.path; CustomTheme.loadTheme(data); CustomTheme.themes.push(data); if (isApp) { CustomTheme.sideloaded_themes.push(file.path); localStorage.setItem('themes_sideloaded', JSON.stringify(CustomTheme.sideloaded_themes)); } } } }; (function() { var stored_theme = 0; try { if (localStorage.getItem('theme')) { stored_theme = JSON.parse(localStorage.getItem('theme')) } } catch (err) {} for (var key in CustomTheme.defaultColors) { CustomTheme.data.colors[key] = CustomTheme.defaultColors[key]; } if (stored_theme) { CustomTheme.loadTheme(stored_theme); if (stored_theme.customized) CustomTheme.data.customized = true; } })() BARS.defineActions(function() { new Action('theme_window', { name: tl('dialog.settings.theme') + '...', icon: 'style', category: 'blockbench', click: function () { CustomTheme.dialog.show(); } }) new Action('import_theme', { icon: 'folder', category: 'blockbench', click: function () { Blockbench.import({ resource_id: 'config', extensions: ['bbtheme'], type: 'Blockbench Theme' }, function(files) { CustomTheme.import(files[0]); }) } }) new Action('export_theme', { icon: 'style', category: 'blockbench', click: function () { let theme = {}; Object.assign(theme, CustomTheme.data); delete theme.customized; delete theme.id; Blockbench.export({ resource_id: 'config', type: 'Blockbench Theme', extensions: ['bbtheme'], name: theme.id, content: compileJSON(theme) }) } }) BarItems.import_theme.toElement('#layout_title_bar') BarItems.export_theme.toElement('#layout_title_bar') })