diff --git a/src/Makefile b/src/Makefile index ede36b9..6752479 100644 --- a/src/Makefile +++ b/src/Makefile @@ -16,11 +16,11 @@ deps: prettier: $(PRETTIER) $(PRETTIER_FILES) $(PRETTIER) --write $(PRETTIER_FILES) -css/popup.dist.css: $(LESSC) popup/popup.less +css/popup.dist.css: $(LESSC) popup/*.less [ -d css ] || mkdir -p css $(LESSC) popup/popup.less css/popup.dist.css -css/options.dist.css: $(LESSC) options/options.less +css/options.dist.css: $(LESSC) options/*.less [ -d css ] || mkdir -p css $(LESSC) options/options.less css/options.dist.css diff --git a/src/background.js b/src/background.js index 6b16ea3..9df0fcb 100644 --- a/src/background.js +++ b/src/background.js @@ -24,7 +24,8 @@ var defaultSettings = { gpgPath: null, stores: {}, foreignFills: {}, - username: null + username: null, + theme: "dark" }; var authListeners = {}; diff --git a/src/options/interface.js b/src/options/interface.js index b865b55..4e75c43 100644 --- a/src/options/interface.js +++ b/src/options/interface.js @@ -58,6 +58,14 @@ function view(ctl, params) { nodes.push(createInput.call(this, "username", "Default username", "john.smith")); nodes.push(createInput.call(this, "gpgPath", "Custom gpg binary", "/path/to/gpg")); + nodes.push(m("h3", "Theme")); + nodes.push( + createDropdown.call(this, "theme", [ + m("option", { value: "dark" }, "Dark"), + m("option", { value: "light" }, "Light") + ]) + ); + nodes.push(m("h3", "Custom store locations")); nodes.push( m("div", { class: "notice" }, "(this overrides default store and $PASSWORD_STORE_DIR)") @@ -152,6 +160,29 @@ function createInput(key, title, placeholder) { ]); } +/** + * Generates vnode for a dropdown setting + * + * @since 3.3.1 + * + * @param string key Settings key + * @param array options Array of objects with value and text fields + * @return Vnode + */ +function createDropdown(key, options) { + return m( + "select", + { + value: this.settings[key], + onchange: e => { + this.settings[key] = e.target.value; + this.saveEnabled = true; + } + }, + options + ); +} + /** * Generates vnode for a checkbox setting * diff --git a/src/popup/colors-dark.less b/src/popup/colors-dark.less new file mode 100644 index 0000000..7e42ede --- /dev/null +++ b/src/popup/colors-dark.less @@ -0,0 +1,20 @@ +@import "colors.less"; + +.colors-dark { + .colors( + @bg-color: #414141, + @default-bg-color: #393939, + @hover-bg-color: #363636, + @text-color: #c4c4c4, + @error-text-color: #f00, + @dim-text-color: #a0a0a0, + @badge-color: #626262, + @input-bg-color: #4a4a4a, + @active-input-bg-color: #4f4f4f, + @input-text-color: #eee, + @hint-bg-color: #d79921, + @hint-color: #363636, + @match-text-bg-color: transparent, + @match-text-color: #d79921 + ); +} diff --git a/src/popup/colors-light.less b/src/popup/colors-light.less new file mode 100644 index 0000000..273f62d --- /dev/null +++ b/src/popup/colors-light.less @@ -0,0 +1,36 @@ +@import "colors.less"; + +.colors-light { + .colors( + @bg-color: #f1f3f5, + @default-bg-color: #d8dee3, + @hover-bg-color: #ced4da, + @text-color: #343a40, + @error-text-color: #e03131, + @dim-text-color: #868e96, + @badge-color: #b8b8b8, + @input-bg-color: #eaeef1, + @active-input-bg-color: #e8ebee, + @input-text-color: #343a40, + @hint-bg-color: #1c7ed6, + @hint-color: #e7f5ff, + @match-text-bg-color: #cfecff, + @match-text-color: #1873ea + ); + + .part.login .name .line1 .recent, + .part.login .action.copy-password, + .part.login .action.copy-user { + filter: invert(85%); + } + + .part.login .name .line1 .recent:focus, + .part.login .name .line1 .recent:hover, + .part.login .action.copy-password:focus, + .part.login .action.copy-password:hover, + .part.login .action.copy-user:focus, + .part.login .action.copy-user:hover { + // colour such that invert(85%) ~= @hover-bg-color + background-color: #0c0804; + } +} diff --git a/src/popup/colors.less b/src/popup/colors.less new file mode 100644 index 0000000..e28c839 --- /dev/null +++ b/src/popup/colors.less @@ -0,0 +1,77 @@ +.colors( + @bg-color, + @default-bg-color, + @hover-bg-color, + @text-color, + @error-text-color, + @dim-text-color, + @badge-color, + @input-bg-color, + @active-input-bg-color, + @input-text-color, + @hint-bg-color, + @hint-color, + @match-text-bg-color, + @match-text-color) { + html, + body { + background-color: @bg-color; + color: @text-color; + } + + .badge { + background-color: @badge-color; + } + + .part.error { + color: @error-text-color; + } + + .part.search { + background-color: @input-bg-color; + } + + .part.search:focus-within { + background-color: @active-input-bg-color; + } + + .part.search > .hint { + background-color: @hint-bg-color; + color: @hint-color; + } + + .part.search > input[type="text"] { + background-color: transparent; + color: @input-text-color; + } + + .part.search > input[type="text"]::placeholder { + color: @dim-text-color; + } + + .logins:not(:hover):not(:focus-within) .part.login:first-child > .name { + background-color: @default-bg-color; + } + + .part.login > .name:hover, + .part.login > .name:focus, + .part.login > .action:hover, + .part.login > .action:focus, + .part.login:focus > .name { + background-color: @hover-bg-color; + } + + .part.login em { + background-color: @match-text-bg-color; + color: @match-text-color; + } + + .updates { + border-top: 1px solid @hover-bg-color; + + span, + a { + color: @error-text-color; + } + } +} diff --git a/src/popup/popup.html b/src/popup/popup.html index cf39c26..99ff448 100644 --- a/src/popup/popup.html +++ b/src/popup/popup.html @@ -1,5 +1,5 @@ - + diff --git a/src/popup/popup.js b/src/popup/popup.js index 5f57df3..7d08a1d 100644 --- a/src/popup/popup.js +++ b/src/popup/popup.js @@ -43,6 +43,10 @@ async function run() { } var settings = response.settings; + var root = document.getElementsByTagName("html")[0]; + root.classList.remove("colors-dark"); + root.classList.add(`colors-${settings.theme}`); + if (settings.hasOwnProperty("hostError")) { throw new Error(settings.hostError.params.message); } diff --git a/src/popup/popup.less b/src/popup/popup.less index 4be78a2..9ca9a3e 100644 --- a/src/popup/popup.less +++ b/src/popup/popup.less @@ -1,18 +1,5 @@ -@bg-color: #414141; -@default-bg-color: #393939; -@hover-bg-color: #363636; -@text-color: #c4c4c4; -@error-text-color: #f00; -@dim-text-color: #a0a0a0; - -@input-bg-color: #4a4a4a; -@active-input-bg-color: #4f4f4f; -@input-text-color: #eee; - -@hint-bg-color: #d79921; -@hint-color: #363636; - -@match-text-color: @hint-bg-color; +@import "colors-dark.less"; +@import "colors-light.less"; @login-height: 53px; @max-logins-height: @login-height * 7; @@ -42,8 +29,6 @@ body { min-width: 260px; overflow-x: hidden; white-space: nowrap; - background-color: @bg-color; - color: @text-color; } @media (min-resolution: 192dpi) { @@ -75,7 +60,6 @@ body { .badge { display: flex; align-items: center; - background-color: #626262; border-radius: 4px; font-size: 12px; margin-right: 8px; @@ -98,7 +82,6 @@ body { } .part.error { - color: @error-text-color; white-space: normal; padding: 7px; } @@ -114,16 +97,9 @@ body { background-position: top 6px right 6px; background-repeat: no-repeat; background-size: 18px; - background-color: @input-bg-color; -} - -.part.search:focus-within { - background-color: @active-input-bg-color; } .part.search > .hint { - background-color: @hint-bg-color; - color: @hint-color; line-height: 19px; } @@ -138,29 +114,21 @@ body { } .part.search > input[type="text"] { - background-color: transparent; border: none; outline: none; width: 100%; - color: @input-text-color; font-family: "Open Sans"; } .part.search > input[type="text"]::placeholder { - color: @dim-text-color; opacity: 1; } -.logins:not(:hover):not(:focus-within) .part.login:first-child > .name { - background-color: @default-bg-color; -} - .part.login > .name:hover, .part.login > .name:focus, .part.login > .action:hover, .part.login > .action:focus, .part.login:focus > .name { - background-color: @hover-bg-color; outline: none; } @@ -199,6 +167,7 @@ body { .line2 { font-size: 18px; + margin-top: 2px; } } @@ -221,16 +190,9 @@ body { } .part.login em { - color: @match-text-color; font-style: normal; } .updates { padding: @login-part-padding; - border-top: 1px solid @hover-bg-color; - - span, - a { - color: @error-text-color; - } }