diff --git a/customize.dist/src/less2/include/colortheme-all.less b/customize.dist/src/less2/include/colortheme-all.less index be048d7bc..c212ac9ff 100644 --- a/customize.dist/src/less2/include/colortheme-all.less +++ b/customize.dist/src/less2/include/colortheme-all.less @@ -2,5 +2,4 @@ // create a file: customize/src/less2/include/colortheme.less // override whatever colors you want. When you update, the new colors will be // added ok because the original file is pulled in first. -@import (reference) "/customize.dist/src/less2/include/colortheme.less"; @import (reference) "/customize/src/less2/include/colortheme.less"; diff --git a/customize.dist/src/less2/include/colortheme-dark.less b/customize.dist/src/less2/include/colortheme-dark.less new file mode 100644 index 000000000..0028af616 --- /dev/null +++ b/customize.dist/src/less2/include/colortheme-dark.less @@ -0,0 +1,130 @@ +@colortheme_font: 'Open Sans', 'Helvetica Neue', sans-serif; +@colortheme_app-font-size: 16px; +@colortheme_app-font-size-small: 13px; +@colortheme_app-font: @colortheme_app-font-size @colortheme_font; + +@colortheme_logo-1: #326599; +@colortheme_logo-2: #0087FF; +@colortheme_logo_2_light: lighten(@colortheme_logo-2, 40%); +@colortheme_loading_bg: #E7E7E7; + +@colortheme_link-color: #0275D8; +@colortheme_link-color-visited: #005999; +@colortheme_info-background: #eeeeee; + +@colortheme_old-base: #302B28; +@colortheme_old-fore: #fafafa; + +@colortheme_base: #fff; +@colortheme_light-base: lighten(@colortheme_base, 20%); + +@colortheme_cp-red: #FA5858; // remove red +@colortheme_cp-green: #46E981; + +@colortheme_form-border: #bbbbbb; +@colortheme_form-bg: @colortheme_logo-2; +@colortheme_form-color: #ffffff; +@colortheme_form-bg-alt: #ffffff; +@colortheme_form-color-alt: @colortheme_logo-2; +@colortheme_form-warning: #f49842; +@colortheme_form-warning-hov: darken(@colortheme_form-warning, 5%); + +@colortheme_context-menu-icon-color: #7b7b7b; + +@colortheme_modal-bg: @colortheme_form-bg-alt; // TODO Modals bg +@colortheme_modal-fg: @colortheme_form-color-alt; +@colortheme_modal-link: @colortheme_link-color; +@colortheme_modal-link-visited: lighten(@colortheme_modal-link, 10%); +@colortheme_modal-dim: fade(@cryptpad_text_col, 50%); // TODO transparent background behind modals +@colortheme_modal-input: @colortheme_form-bg; +@colortheme_modal-input-fg: @colortheme_form-color; + +@colortheme_loading-bg: @colortheme_logo-1; +@colortheme_loading-bg-alt: @colortheme_logo-2; +@colortheme_loading-color: @colortheme_old-fore; + + +// TODO modals buttons +@colortheme_alertify-red: #E55236; +@colortheme_alertify-red-color: #FFF; +@colortheme_alertify-red-border: transparent; +@colortheme_alertify-green: #77C825; +@colortheme_alertify-green-color: #FFF; +@colortheme_alertify-green-border: transparent; +@colortheme_alertify-primary: @colortheme_form-bg; +@colortheme_alertify-primary-text: @colortheme_form-color; +@colortheme_alertify-primary-border: transparent; +@colortheme_alertify-disabled: #6c757d; +@colortheme_alertify-disabled-text: #ffffff; +@colortheme_alertify-disabled-border: #6c757d; +@colortheme_alertify-cancel: @colortheme_modal-bg; +@colortheme_alertify-cancel-border: #949494; + +@colortheme_notification-log: fade(@colortheme_logo-2, 90%); +@colortheme_notification-color: #fff;; +@colortheme_notification-warn: rgba(205, 37, 50, 0.8); + +@colortheme_dropdown-bg: #f9f9f9; +@colortheme_dropdown-color: black; +@colortheme_dropdown-bg-hover: #f1f1f1; +@colortheme_dropdown-bg-active: #e8e8e8; + +// Apps, these colors are used for customizing the toolbar for the apps. +@colortheme_toolbar-warn: @colortheme_alertify-red; +@colortheme_pad-toolbar-bg: #eeeeee; +@colortheme_help-bg: #ddd; +@colortheme_userlist-bg: #eee; +@colortheme_pad-chat-bg: #AAA; + +@colortheme_apps: { + default: #000000; + drive: #0087FF; // Used as icon color in index.js (index.html) + pad: #256ad5; + code: #EAA000; + slide: #e57614; + poll: #2c9e98; + whiteboard: #a72ba7; + kanban: #8C4; + sheet: #40865c; + oodoc: #5170B5; + ooslide: #C65D27; + file: #CD2532; +} + +@colortheme_static_apps: { + default: #000000; + teams: #4A3BBD; + contacts: #607B8D; +} + +@colortheme_poll-color: #fff; +@colortheme_poll-help-bg: #bbffbb; +@colortheme_poll-th-bg: #005bef; +@colortheme_poll-th-fg: #fff; + +// Sidebar layout (profile / settings) +@colortheme_sidebar-active: #fff; +@colortheme_sidebar-left-bg: #eee; +@colortheme_sidebar-left-fg: #000; +@colortheme_sidebar-left-branch: #888; +@colortheme_sidebar-right-bg: #fff; +@colortheme_sidebar-right-fg: #000; +@colortheme_sidebar-description: #777; +@colortheme_sidebar-button-bg: #3066e5; +@colortheme_sidebar-button-red-bg: #e54e4e; +@colortheme_sidebar-button-alt-bg: #fff; + +@cryptpad_color_blue: #4591C4; +@cryptpad_color_grey: #999999; +@cryptpad_color_light_grey: #e0e0e0; +@cryptpad_header_col: #1E1F1F; +@cryptpad_text_col: #3F4141; +@cryptpad_color_light_blue: #00b7d8; + +@colortheme_checkmark-back0: @colortheme_form-bg-alt; +@colortheme_checkmark-back0-active: @colortheme_form-border; +@colortheme_checkmark-back1: @colortheme_form-bg; +@colortheme_checkmark-col1: @colortheme_form-color; +@colortheme_checkmark-back2: @colortheme_form-bg-alt; +@colortheme_checkmark-col2: @colortheme_form-color-alt; +@colortheme_checkmark-disabled: #AAA; diff --git a/customize.dist/src/less2/include/colortheme.less b/customize.dist/src/less2/include/colortheme.less index 4b7cc97bf..700ee8d91 100644 --- a/customize.dist/src/less2/include/colortheme.less +++ b/customize.dist/src/less2/include/colortheme.less @@ -78,7 +78,7 @@ @colortheme_apps: { default: #0087FF; - drive: #0087FF; + drive: #0087FF; // Used as icon color in index.js (index.html) pad: #256ad5; code: #EAA000; slide: #e57614; diff --git a/www/common/LessLoader.js b/www/common/LessLoader.js index 5b52b3d2c..cabf5411b 100644 --- a/www/common/LessLoader.js +++ b/www/common/LessLoader.js @@ -93,6 +93,13 @@ define([ xhr.send(null); }; + var COLORTHEME = '/customize/src/less2/include/colortheme.less'; + var COLORTHEME_DARK = '/customize/src/less2/include/colortheme-dark.less'; + var getColorthemeURL = function () { + if (window.CryptPad_theme === 'dark') { return COLORTHEME_DARK; } + return COLORTHEME; + }; + var lessEngine; var tempCache = { key: Math.random() }; var getLessEngine = function (cb) { @@ -108,6 +115,13 @@ define([ }); var doXHR = lessEngine.FileManager.prototype.doXHR; lessEngine.FileManager.prototype.doXHR = function (url, type, callback, errback) { + console.error(url, COLORTHEME); // XXX + var col = false; + if (url === COLORTHEME) { + col = true; + url = getColorthemeURL(); + console.warn(url); + } url = fixURL(url); var cached = tempCache[url]; if (cached && cached.res) { @@ -117,6 +131,10 @@ define([ if (cached) { return void cached.queue.push(callback); } cached = tempCache[url] = { queue: [ callback ], res: undefined }; return doXHR(url, type, function (text, lastModified) { + if (col) { + console.warn(text, lastModified); + // XXX COLOR: append custom theme here + } cached.res = [ text, lastModified ]; var queue = cached.queue; cached.queue = []; diff --git a/www/common/boot2.js b/www/common/boot2.js index 30f776a25..8ed428cbb 100644 --- a/www/common/boot2.js +++ b/www/common/boot2.js @@ -1,3 +1,41 @@ +(function () { +try { + var isDarkOS = function () { + try { + return window.matchMedia('(prefers-color-scheme: dark)').matches; + } catch (e) { return false; } + }; + var flush = window.CryptPad_flushCache = function () { + Object.keys(localStorage).forEach(function (k) { + if (k.indexOf('CRYPTPAD_CACHE|') !== 0 && k.indexOf('LESS_CACHE') !== 0) { return; } + delete localStorage[k]; + }); + }; + var os = isDarkOS() ? 'dark' : 'light'; + var key = 'CRYPTPAD_STORE|colortheme'; + window.CryptPad_theme = localStorage[key] || os; + if (!localStorage[key]) { + // We're using OS theme, check if we need to change + if (os !== localStorage[key+'_default']) { + console.warn('New OS theme, flush cache'); + flush(); + localStorage[key+'_default'] = os; + } + } + //localStorage[key] = theme; + // If the wrong theme is built, flush it + /* XXX flush cache when change + if (theme !== built) { + console.warn('New theme, flush cache'); + Object.keys(localStorage).forEach(function (k) { + if (k.indexOf('CRYPTPAD_CACHE|') !== 0 && k.indexOf('LESS_CACHE') !== 0) { return; } + delete localStorage[k]; + }); + } + */ +} catch (e) { console.error(e); } +})(); + // This is stage 1, it can be changed but you must bump the version of the project. define([ '/common/requireconfig.js' diff --git a/www/common/sframe-boot2.js b/www/common/sframe-boot2.js index 66a93545f..828c244a8 100644 --- a/www/common/sframe-boot2.js +++ b/www/common/sframe-boot2.js @@ -1,3 +1,13 @@ +(function () { +try { + if (!window.cryptpadStore) { return; } + var theme = window.cryptpadStore.store['colortheme']; + var os = window.cryptpadStore.store['colortheme_default']; + window.CryptPad_theme = theme || os; + console.error(theme, os); +} catch (e) { console.error(e); } +})(); + // This is stage 1, it can be changed but you must bump the version of the project. // Note: This must only be loaded from inside of a sandbox-iframe. define([ diff --git a/www/settings/inner.js b/www/settings/inner.js index 38a4f4b88..f94287b41 100644 --- a/www/settings/inner.js +++ b/www/settings/inner.js @@ -62,6 +62,10 @@ define([ 'cp-settings-userfeedback', 'cp-settings-cache', ], + 'style': [ + 'cp-settings-colortheme', + 'cp-settings-custom-theme', + ], 'drive': [ 'cp-settings-resettips', 'cp-settings-drive-duplicate', @@ -408,6 +412,110 @@ define([ ]); }, true); + // XXX + Messages.settings_colorthemeTitle = "Color theme"; + Messages.settings_colorthemeHint = "Change the overall colors of CryptPad on this machine."; + Messages.settings_colortheme_default = "Default ({0})"; + Messages.settings_colortheme_light = "Light"; + Messages.settings_colortheme_dark = "Dark"; + Messages.settings_colortheme_custom = "Custom"; + + makeBlock('colortheme', function (cb) { + var store = window.cryptpadStore; + + var theme = window.cryptpadStore.store['colortheme'] || 'default'; + var os = window.cryptpadStore.store['colortheme_default'] || 'light'; + var values = ['default', 'light', 'dark', 'custom']; + + var defaultTheme = Messages['settings_colortheme_'+os]; + var opts = h('div.cp-settings-radio-container', [ + values.map(function (key, i) { + return UI.createRadio('cp-colortheme-radio', 'cp-colortheme-radio-'+key, + Messages._getKey('settings_colortheme_' + key, [defaultTheme]), + key === theme, { + input: { value: key }, + label: { class: 'noTitle' } + }); + }) + ]); + + cb(opts); + + var spinner = UI.makeSpinner($(opts)); + $(opts).find('input[name="cp-colortheme-radio"]').change(function () { + var val = this.value; + if (values.indexOf(val) === -1) { return; } + if (val === theme) { return; } + spinner.spin(); + + // Check if we need to flush cache + var flush = false; + if (val === "default" && os === theme) { + // Switch from a theme to default without changing value: nothing to do + } else if (theme === "default" && os === val) { + // Switch from default to a selected value without any change: nothing to do + } else { + // The theme is different, flush cache + flush = true; + } + + if (val === 'default') { val = ''; } + sframeChan.query('Q_COLORTHEME_CHANGE', { + theme: val, + flush: flush + }, function () { + window.cryptpadStore.store['colortheme'] = val; + theme = val || 'default'; + spinner.done(); + }); + }); + + return; + var $cbox = $(UI.createCheckbox('cp-settings-cache', + Messages.settings_cacheCheckbox, + false, { label: { class: 'noTitle' } })); + var spinner = UI.makeSpinner($cbox); + + // Checkbox: "Enable safe links" + var $checkbox = $(opts).find('.cp-radio-colortheme input').on('change', function() { + spinner.spin(); + var val = !$checkbox.is(':checked') ? '1' : undefined; + store.put('disableCache', val, function () { + sframeChan.query('Q_CACHE_DISABLE', { + disabled: Boolean(val) + }, function () { + spinner.done(); + }); + }); + }); + + store.get('disableCache', function (val) { + if (!val) { + $checkbox.attr('checked', 'checked'); + } + }); + + var button = h('button.btn.btn-danger', [ + h('i.fa.fa-trash-o'), + h('span', Messages.settings_cacheButton) + ]); + var buttonContainer = h('div.cp-settings-clear-cache', button); + var spinner2 = UI.makeSpinner($(buttonContainer)); + UI.confirmButton(button, { + classes: 'btn-danger' + }, function () { + spinner2.spin(); + sframeChan.query('Q_CLEAR_CACHE', null, function() { + spinner2.done(); + }); + }); + + cb([ + $cbox[0], + buttonContainer + ]); + }, true); + create['delete'] = function() { if (!common.isLoggedIn()) { return; } var $div = $('
', { 'class': 'cp-settings-delete cp-sidebarlayout-element' }); diff --git a/www/settings/main.js b/www/settings/main.js index 750423a1d..be8018ca7 100644 --- a/www/settings/main.js +++ b/www/settings/main.js @@ -69,6 +69,15 @@ define([ sframeChan.on('Q_SETTINGS_DELETE_ACCOUNT', function (data, cb) { Cryptpad.deleteAccount(cb); }); + sframeChan.on('Q_COLORTHEME_CHANGE', function (data, cb) { + localStorage['CRYPTPAD_STORE|colortheme'] = data.theme; + if (data.flush && window.CryptPad_flushCache) { + window.CryptPad_flushCache(); + window.location.reload(); + return; + } + cb(); + }); }; var category; if (window.location.hash) { diff --git a/www/teams/app-team.less b/www/teams/app-team.less index 7792b0c5f..ee170ffbe 100644 --- a/www/teams/app-team.less +++ b/www/teams/app-team.less @@ -3,7 +3,7 @@ @import (reference) '../../customize/src/less2/include/messenger.less'; @import (reference) '../../customize/src/less2/include/sidebar-layout.less'; @import (reference) "../../customize/src/less2/include/tools.less"; -@import (reference) "../../customize/src/less2/include/colortheme.less"; +@import (reference) "../../customize/src/less2/include/colortheme-all.less"; @import (reference) '../../customize/src/less2/include/export.less';