diff --git a/db/demo.zip b/db/demo.zip index 00ad7c554..958fa4be2 100644 Binary files a/db/demo.zip and b/db/demo.zip differ diff --git a/db/migrations/0185__migrate_black_theme_to_dark.sql b/db/migrations/0185__migrate_black_theme_to_dark.sql index a785bb039..7285f6eb7 100644 --- a/db/migrations/0185__migrate_black_theme_to_dark.sql +++ b/db/migrations/0185__migrate_black_theme_to_dark.sql @@ -1,2 +1,4 @@ -- black theme has been removed, dark is closest replacement UPDATE options SET value = 'dark' WHERE name = 'theme' AND value = 'black'; + +UPDATE options SET value = 'light' WHERE name = 'theme' AND value = 'white'; diff --git a/src/public/app/dialogs/options/appearance.js b/src/public/app/dialogs/options/appearance.js index 874380025..31052c020 100644 --- a/src/public/app/dialogs/options/appearance.js +++ b/src/public/app/dialogs/options/appearance.js @@ -95,20 +95,12 @@ export default class ApperanceOptions { this.$detailFontSize = $("#detail-font-size"); this.$body = $("body"); - this.$themeSelect.on('change', () => { + this.$themeSelect.on('change', async () => { const newTheme = this.$themeSelect.val(); - this.toggleBodyClass("theme-", newTheme); + await server.put('options/theme/' + newTheme); - const noteId = $(this).find(":selected").attr("data-note-id"); - - if (noteId) { - // make sure the CSS is loaded - // if the CSS has been loaded and then updated then the changes won't take effect though - libraryLoader.requireCss(`api/notes/download/${noteId}`); - } - - server.put('options/theme/' + newTheme); + utils.reloadFrontendApp("theme change"); }); this.$zoomFactorSelect.on('change', () => { appContext.triggerCommand('setZoomFactorAndSave', {zoomFactor: this.$zoomFactorSelect.val()}); }); diff --git a/src/public/app/setup.js b/src/public/app/setup.js index d265888e6..b7df9ae72 100644 --- a/src/public/app/setup.js +++ b/src/public/app/setup.js @@ -23,19 +23,7 @@ function SetupModel() { this.password1 = ko.observable(); this.password2 = ko.observable(); - this.theme = ko.observable("white"); - this.theme.subscribe(function(newTheme) { - const $body = $("body"); - - for (const clazz of Array.from($body[0].classList)) { // create copy to safely iterate over while removing classes - if (clazz.startsWith("theme-")) { - $body.removeClass(clazz); - } - } - - $body.addClass("theme-" + newTheme); - }); - + this.theme = ko.observable("light"); this.syncServerHost = ko.observable(); this.syncProxy = ko.observable(); diff --git a/src/public/app/widgets/buttons/global_menu.js b/src/public/app/widgets/buttons/global_menu.js index a597a5458..f1d6c4d76 100644 --- a/src/public/app/widgets/buttons/global_menu.js +++ b/src/public/app/widgets/buttons/global_menu.js @@ -14,7 +14,7 @@ const TPL = ` } .global-menu-button { - background-image: url("images/icon-color.png"); + background-image: url("images/icon-black.png"); background-repeat: no-repeat; background-position: 50% 45%; width: 100%; diff --git a/src/public/app/widgets/note_detail.js b/src/public/app/widgets/note_detail.js index 3e50dd4ab..775f49349 100644 --- a/src/public/app/widgets/note_detail.js +++ b/src/public/app/widgets/note_detail.js @@ -229,7 +229,7 @@ export default class NoteDetailWidget extends NoteContextAwareWidget { "libraries/katex/katex.min.css", "stylesheets/print.css", "stylesheets/relation_map.css", - "stylesheets/themes.css" + "stylesheets/ckeditor-theme.css" ], debug: true }); diff --git a/src/public/stylesheets/themes.css b/src/public/stylesheets/ckeditor-theme.css similarity index 54% rename from src/public/stylesheets/themes.css rename to src/public/stylesheets/ckeditor-theme.css index ad96cddf5..f1af43c98 100644 --- a/src/public/stylesheets/themes.css +++ b/src/public/stylesheets/ckeditor-theme.css @@ -1,143 +1,3 @@ -:root { - --main-font-family: MontserratLight; - --main-font-size: normal; - - --tree-font-family: MontserratLight; - --tree-font-size: normal; - - --detail-font-family: MontserratLight; - --detail-font-size: normal; - - --font-family-monospace: JetBrainsLight; - --detail-text-font-family: MontserratLight; - - --main-background-color: white; - --main-text-color: black; - --main-border-color: #ccc; - - --accented-background-color: #f5f5f5; - --more-accented-background-color: #ddd; - - --button-background-color: transparent; - --button-disabled-background-color: #ddd; - --button-border-color: #ddd; - --button-text-color: black; - --button-border-radius: 5px; - - --primary-button-background-color: #6c757d; - --primary-button-text-color: white; - --primary-button-border-color: #6c757d; - - --muted-text-color: #666; - - --input-text-color: black; - --input-background-color: transparent; - - --hover-item-text-color: black; - --hover-item-background-color: #ddd; - - --active-item-text-color: black; - --active-item-background-color: #ddd; - - --menu-text-color: black; - --menu-background-color: white; - - --modal-background-color: white; - --modal-backdrop-color: black; - - --left-pane-background-color: #F3F3F3; - --left-pane-text-color: #333; - - --launcher-pane-background-color: #F3F3F3; - --launcher-pane-text-color: #333; - - --active-tab-background-color: #ddd; - --active-tab-text-color: black; - - --inactive-tab-background-color: #f5f5f5; - --inactive-tab-text-color: #666; - - --scrollbar-border-color: #ddd; - --tooltip-background-color: #f8f8f8; - --link-color: blue; -} - -body.theme-dark { - --main-font-family: MontserratLight; - --main-font-size: normal; - - --tree-font-family: MontserratLight; - --tree-font-size: normal; - - --detail-font-family: MontserratLight; - --detail-font-size: normal; - - --font-family-monospace: JetBrainsLight; - --detail-text-font-family: MontserratLight; - - --main-background-color: #333; - --main-text-color: #ccc; - --main-border-color: #aaa; - - --accented-background-color: #555; - --more-accented-background-color: #777; - - --button-background-color: transparent; - --button-disabled-background-color: #222; - --button-border-color: #ccc; - --button-text-color: currentColor; - --button-border-radius: 5px; - - --primary-button-background-color: #888; - --primary-button-text-color: white; - --primary-button-border-color: #999; - - --muted-text-color: #bbb; - - --input-text-color: #ccc; - --input-background-color: #333; - - --hover-item-text-color: black; - --hover-item-background-color: #777; - - --active-item-text-color: black; - --active-item-background-color: #777; - - --menu-text-color: white; - --menu-background-color: #222; - - --modal-background-color: #333; - --modal-backdrop-color: #444; - - --left-pane-background-color: #1f1f1f; - --left-pane-text-color: #AAAAAA; - - --launcher-pane-background-color: #1f1f1f; - --launcher-pane-text-color: #AAAAAA; - - --active-tab-background-color: #666; - --active-tab-text-color: #ccc; - - --inactive-tab-background-color: #444; - --inactive-tab-text-color: #bbb; - - --scrollbar-border-color: #888; - --tooltip-background-color: #333; - --link-color: lightskyblue; -} - -body.theme-dark .global-menu-button { - background-image: url("../images/icon-grey.png"); -} - -body.theme-dark ::-webkit-calendar-picker-indicator { - filter: invert(1); -} - -body.theme-dark .CodeMirror { - filter: invert(90%) hue-rotate(180deg); -} - body { /* -- Overrides generic colors. ------------------------------------------------------------- */ @@ -242,39 +102,3 @@ body { --ck-color-widget-type-around-button: var(--main-border-color); --ck-color-widget-type-around-button-hover: var(--main-border-color); } - -body { - background-color: var(--main-background-color); - color: var(--main-text-color); - font-family: var(--main-font-family); -} - -a, a:visited, a:hover { - color: var(--link-color); -} - -input, select, textarea { - color: var(--input-text-color) !important; - background: var(--input-background-color) !important; -} - -#left-pane input, select, textarea { - color: var(--left-pane-text-color) !important; - background: var(--left-pane-background-color) !important; -} - -input::placeholder { - color: var(--muted-text-color); -} - -table td, table th { - color: var(--main-text-color); -} - -.ck .todo-list__checkmark { - top: 10px !important; -} - -.modal-backdrop { - background-color: var(--modal-backdrop-color) !important; -} diff --git a/src/public/stylesheets/style.css b/src/public/stylesheets/style.css index 4f8888315..38e709fbc 100644 --- a/src/public/stylesheets/style.css +++ b/src/public/stylesheets/style.css @@ -18,6 +18,39 @@ body { on the last line of the editor. */ position: fixed; width: 100%; + background-color: var(--main-background-color); + color: var(--main-text-color); + font-family: var(--main-font-family); +} + +a, a:visited, a:hover { + color: var(--link-color); +} + +input, select, textarea { + color: var(--input-text-color) !important; + background: var(--input-background-color) !important; +} + +#left-pane input, select, textarea { + color: var(--left-pane-text-color) !important; + background: var(--left-pane-background-color) !important; +} + +input::placeholder { + color: var(--muted-text-color); +} + +table td, table th { + color: var(--main-text-color); +} + +.ck .todo-list__checkmark { + top: 10px !important; +} + +.modal-backdrop { + background-color: var(--modal-backdrop-color) !important; } .component { diff --git a/src/public/stylesheets/theme-dark.css b/src/public/stylesheets/theme-dark.css new file mode 100644 index 000000000..9b200376b --- /dev/null +++ b/src/public/stylesheets/theme-dark.css @@ -0,0 +1,75 @@ +:root { + --main-font-family: MontserratLight; + --main-font-size: normal; + + --tree-font-family: MontserratLight; + --tree-font-size: normal; + + --detail-font-family: MontserratLight; + --detail-font-size: normal; + + --font-family-monospace: JetBrainsLight; + --detail-text-font-family: MontserratLight; + + --main-background-color: #333; + --main-text-color: #ccc; + --main-border-color: #aaa; + + --accented-background-color: #555; + --more-accented-background-color: #777; + + --button-background-color: transparent; + --button-disabled-background-color: #222; + --button-border-color: #ccc; + --button-text-color: currentColor; + --button-border-radius: 5px; + + --primary-button-background-color: #888; + --primary-button-text-color: white; + --primary-button-border-color: #999; + + --muted-text-color: #bbb; + + --input-text-color: #ccc; + --input-background-color: #333; + + --hover-item-text-color: black; + --hover-item-background-color: #777; + + --active-item-text-color: black; + --active-item-background-color: #777; + + --menu-text-color: white; + --menu-background-color: #222; + + --modal-background-color: #333; + --modal-backdrop-color: #444; + + --left-pane-background-color: #1f1f1f; + --left-pane-text-color: #AAAAAA; + + --launcher-pane-background-color: #1f1f1f; + --launcher-pane-text-color: #AAAAAA; + + --active-tab-background-color: #666; + --active-tab-text-color: #ccc; + + --inactive-tab-background-color: #444; + --inactive-tab-text-color: #bbb; + + --scrollbar-border-color: #888; + --tooltip-background-color: #333; + --link-color: lightskyblue; +} + +body .global-menu-button { + background-image: url("../images/icon-grey.png"); +} + +body ::-webkit-calendar-picker-indicator { + filter: invert(1); +} + +body .CodeMirror { + filter: invert(90%) hue-rotate(180deg); +} diff --git a/src/public/stylesheets/theme-light.css b/src/public/stylesheets/theme-light.css new file mode 100644 index 000000000..6d41b9cf8 --- /dev/null +++ b/src/public/stylesheets/theme-light.css @@ -0,0 +1,66 @@ +/* Light theme is special since it's also baseline/default so when a theme does not define some variable then + value from this theme will be used. For this reason this theme uses "html" instead of ":root" + since it's less "specific" and thus serves as default */ +html { + --main-font-family: MontserratLight; + --main-font-size: normal; + + --tree-font-family: MontserratLight; + --tree-font-size: normal; + + --detail-font-family: MontserratLight; + --detail-font-size: normal; + + --font-family-monospace: JetBrainsLight; + --detail-text-font-family: MontserratLight; + + --main-background-color: white; + --main-text-color: black; + --main-border-color: #ccc; + + --accented-background-color: #f5f5f5; + --more-accented-background-color: #ddd; + + --button-background-color: transparent; + --button-disabled-background-color: #ddd; + --button-border-color: #ddd; + --button-text-color: black; + --button-border-radius: 5px; + + --primary-button-background-color: #6c757d; + --primary-button-text-color: white; + --primary-button-border-color: #6c757d; + + --muted-text-color: #666; + + --input-text-color: black; + --input-background-color: transparent; + + --hover-item-text-color: black; + --hover-item-background-color: #ddd; + + --active-item-text-color: black; + --active-item-background-color: #ddd; + + --menu-text-color: black; + --menu-background-color: white; + + --modal-background-color: white; + --modal-backdrop-color: black; + + --left-pane-background-color: #F3F3F3; + --left-pane-text-color: #333; + + --launcher-pane-background-color: #F3F3F3; + --launcher-pane-text-color: #333; + + --active-tab-background-color: #ddd; + --active-tab-text-color: black; + + --inactive-tab-background-color: #f5f5f5; + --inactive-tab-text-color: #666; + + --scrollbar-border-color: #ddd; + --tooltip-background-color: #f8f8f8; + --link-color: blue; +} diff --git a/src/routes/index.js b/src/routes/index.js index a80ed4d36..9b9be6ffc 100644 --- a/src/routes/index.js +++ b/src/routes/index.js @@ -19,7 +19,7 @@ function index(req, res) { res.render(view, { csrfToken: csrfToken, - theme: options.theme, + themeCssUrl: getThemeCssUrl(options.theme), headingStyle: options.headingStyle, mainFontSize: parseInt(options.mainFontSize), treeFontSize: parseInt(options.treeFontSize), @@ -36,8 +36,28 @@ function index(req, res) { }); } +function getThemeCssUrl(theme) { + if (theme === 'light') { + return false; // light theme is always loaded as baseline + } + + if (theme === 'dark') { + return `stylesheets/theme-dark.css`; + } + else { + const themeNote = attributeService.getNoteWithLabel('appTheme', theme); + + if (themeNote) { + return `api/notes/download/${themeNote.noteId}`; + } + else { + return false; // baseline light theme + } + } +} + function getAppCssNoteIds() { - return attributeService.getNoteIdsWithLabels(['appCss', 'appTheme']); + return attributeService.getNotesWithLabel('appCss').map(note => note.noteId); } module.exports = { diff --git a/src/services/attributes.js b/src/services/attributes.js index 90d90b936..430264756 100644 --- a/src/services/attributes.js +++ b/src/services/attributes.js @@ -69,18 +69,6 @@ function getNotesWithLabel(name, value) { }); } -function getNoteIdsWithLabels(names) { - const noteIds = new Set(); - - for (const name of names) { - for (const attr of becca.findAttributes('label', name)) { - noteIds.add(attr.noteId); - } - } - - return Array.from(noteIds); -} - // TODO: should be in search service function getNoteWithLabel(name, value) { const notes = getNotesWithLabel(name, value); @@ -179,7 +167,6 @@ function sanitizeAttributeName(origName) { module.exports = { getNotesWithLabel, - getNoteIdsWithLabels, getNoteWithLabel, createLabel, createRelation, diff --git a/src/views/desktop.ejs b/src/views/desktop.ejs index f6d42523d..a8fcb428e 100644 --- a/src/views/desktop.ejs +++ b/src/views/desktop.ejs @@ -5,7 +5,7 @@