Explorar el Código

Merge branch 'release/1.1.0'

Book Pauk hace 2 años
padre
commit
2f380dce1b
Se han modificado 40 ficheros con 493 adiciones y 306 borrados
  1. 188 61
      client/components/App.vue
  2. 4 3
      client/components/ExternalLibs/BookmarkSettings/BookmarkSettings.vue
  3. 18 9
      client/components/ExternalLibs/ExternalLibs.vue
  4. 21 16
      client/components/Reader/ContentsPage/ContentsPage.vue
  5. 2 2
      client/components/Reader/HelpPage/CommonHelpPage/CommonHelpPage.vue
  6. 6 6
      client/components/Reader/HelpPage/HelpPage.vue
  7. 1 1
      client/components/Reader/HelpPage/VersionHistoryPage/VersionHistoryPage.vue
  8. 5 5
      client/components/Reader/LoaderPage/LoaderPage.vue
  9. 14 3
      client/components/Reader/LoaderPage/PasteTextPage/PasteTextPage.vue
  10. 37 20
      client/components/Reader/Reader.vue
  11. 4 4
      client/components/Reader/ReaderDialogs/ReaderDialogs.vue
  12. 16 13
      client/components/Reader/RecentBooksPage/RecentBooksPage.vue
  13. 1 0
      client/components/Reader/SearchPage/SearchPage.vue
  14. 1 1
      client/components/Reader/SetPositionPage/SetPositionPage.vue
  15. 2 2
      client/components/Reader/SettingsPage/ConvertTab/ConvertTab.vue
  16. 5 5
      client/components/Reader/SettingsPage/KeysTab/KeysTab.vue
  17. 5 5
      client/components/Reader/SettingsPage/KeysTab/UserHotKeys/UserHotKeys.vue
  18. 0 14
      client/components/Reader/SettingsPage/OthersTab/OthersTab.vue
  19. 2 2
      client/components/Reader/SettingsPage/PageMoveTab/PageMoveTab.vue
  20. 8 7
      client/components/Reader/SettingsPage/ProfilesTab/ProfilesTab.vue
  21. 1 1
      client/components/Reader/SettingsPage/ResetTab/ResetTab.vue
  22. 4 3
      client/components/Reader/SettingsPage/SettingsPage.vue
  23. 2 2
      client/components/Reader/SettingsPage/UpdateTab/UpdateTab.vue
  24. 3 0
      client/components/Reader/SettingsPage/ViewTab/Color/Color.vue
  25. 4 4
      client/components/Reader/SettingsPage/ViewTab/Font/Font.vue
  26. 24 9
      client/components/Reader/SettingsPage/ViewTab/Mode/Mode.vue
  27. 3 2
      client/components/Reader/SettingsPage/ViewTab/Status/Status.vue
  28. 8 8
      client/components/Reader/SettingsPage/ViewTab/Text/Text.vue
  29. 13 5
      client/components/Reader/SettingsPage/ViewTab/ViewTab.vue
  30. 0 36
      client/components/Reader/TextPage/TextPage.vue
  31. 14 0
      client/components/Reader/versionHistory.js
  32. 1 1
      client/components/share/Dialog.vue
  33. 4 11
      client/components/share/NumInput.vue
  34. 5 5
      client/components/share/StdDialog.vue
  35. 3 3
      client/components/share/Window.vue
  36. 0 2
      client/store/index.js
  37. 61 7
      client/store/modules/reader.js
  38. 0 25
      client/store/modules/uistate.js
  39. 2 2
      package-lock.json
  40. 1 1
      package.json

+ 188 - 61
client/components/App.vue

@@ -20,7 +20,6 @@ import StdDialog from './share/StdDialog.vue';
 import sanitizeHtml from 'sanitize-html';
 
 import miscApi from '../api/misc';
-import * as utils from '../share/utils';
 
 const componentOptions = {
     components: {
@@ -31,7 +30,10 @@ const componentOptions = {
         mode: function() {
             this.setAppTitle();
             this.redirectIfNeeded();
-        }
+        },
+        nightMode() {
+            this.setNightMode();
+        },
     },
 
 };
@@ -45,6 +47,34 @@ class App {
         this.uistate = this.$store.state.uistate;
         this.config = this.$store.state.config;
 
+        //dark mode
+        let darkMode = null;
+        this.$root.setDarkMode = (value) => {
+            if (darkMode !== value) {
+                const vars = [
+                    '--bg-app-color', '--text-app-color', '--bg-dialog-color', '--text-anchor-color',
+                    '--bg-loader-color', '--bg-input-color', '--bg-btn-color1', '--bg-btn-color2',
+                    '--bg-header-color1', '--bg-header-color2', '--bg-header-color3',
+                    '--bg-menu-color1', '--bg-menu-color2', '--text-menu-color', '--text-ubtn-color',
+                    '--text-tb-normal', '--bg-tb-normal', '--bg-tb-hover',
+                    '--text-tb-active', '--bg-tb-active', '--bg-tb-active-hover',
+                    '--text-tb-disabled', '--bg-tb-disabled',
+                    '--bg-selected-item-color1', '--bg-selected-item-color2',
+                ];
+
+                let root = document.querySelector(':root');
+                let cs = getComputedStyle(root);
+
+                let mode = (value ? '-dark' : '-light');
+                for (const v of vars) {
+                    const propValue = cs.getPropertyValue(`${v}${mode}`);
+                    root.style.setProperty(v, propValue);
+                }
+
+                darkMode = value;
+            }
+        };
+
         //root route
         let cachedRoute = '';
         let cachedPath = '';
@@ -56,7 +86,7 @@ class App {
 
             }
             return cachedRoute;
-        }
+        };
 
         this.$router.beforeEach((to, from, next) => {
             //распознавание хоста, если присутствует домен 3-уровня "b.", то разрешена только определенная страница
@@ -112,6 +142,8 @@ class App {
         window.addEventListener('resize', (event) => {
             this.$root.eventHook('resize', event);
         });
+
+        this.setNightMode();
     }
 
     mounted() {
@@ -145,38 +177,6 @@ class App {
         })();
     }
 
-    toggleCollapse() {
-        this.commit('uistate/setAsideBarCollapse', !this.uistate.asideBarCollapse);
-        this.$root.eventHook('resize');
-    }
-
-    get isCollapse() {
-        return this.uistate.asideBarCollapse;
-    }
-
-    get asideWidth() {
-        if (this.uistate.asideBarCollapse) {
-            return 64;
-        } else {
-            return 170;
-        }
-    }
-
-    get buttonCollapseIcon() {
-        if (this.uistate.asideBarCollapse) {
-            return 'el-icon-d-arrow-right';
-        } else {
-            return 'el-icon-d-arrow-left';
-        }
-    }
-
-    get appName() {
-        if (this.isCollapse)
-            return '<br><br>';
-        else
-            return `${this.config.name} <br>v${this.config.version}`;
-    }
-
     get apiError() {
         return this.state.apiError;
     }
@@ -185,6 +185,15 @@ class App {
         return this.$root.getRootRoute();
     }
 
+    get nightMode() {
+        return this.$store.state.reader.settings.nightMode;
+    }
+
+    setNightMode() {
+        this.$root.setDarkMode(this.nightMode);
+        this.$q.dark.set(this.nightMode);
+    }
+
     setAppTitle(title) {
         if (!title) {
             if (this.mode == 'liberama') {
@@ -207,26 +216,15 @@ class App {
         return this.$store.state.config.mode;
     }
 
-    get isReaderActive() {
-        return (this.rootRoute == '/reader' || this.rootRoute == '/external-libs');
-    }
-
     redirectIfNeeded() {
-        if ((this.mode == 'reader' || this.mode == 'omnireader' || this.mode == 'liberama')) {
-            const search = window.location.search.substr(1);
-
-            //распознавание параметра url вида "?url=<link>" и редирект при необходимости
-            if (!this.isReaderActive) {
-                const s = search.split('url=');
-                const url = s[1] || '';
-                const q = utils.parseQuery(s[0] || '');
-                if (url) {
-                    q.url = url;
-                }
-
-                window.history.replaceState({}, '', '/');
-                this.$router.replace({ path: '/reader', query: q });
-            }
+        const search = window.location.search.substr(1);
+
+        //распознавание параметра url вида "?url=<link>" и редирект при необходимости
+        const s = search.split('url=');
+        const url = s[1] || '';
+        if (url) {
+            window.history.replaceState({}, '', '/');
+            this.$router.replace({ path: '/reader', query: {url} });
         }
     }
 }
@@ -236,22 +234,151 @@ export default vueComponent(App);
 </script>
 
 <style scoped>
-.app-name {
-    margin-left: 10px;
-    margin-top: 10px;
-    text-align: center;
-    line-height: 140%;
-    font-weight: bold;
-}
 </style>
 
 <style>
+/* color schemes */
+:root {
+    /* current */
+    --bg-app-color: #fff;
+    --text-app-color: #000;
+    --bg-dialog-color: #fff;
+    --text-anchor-color: #00f;
+    --bg-loader-color: #ebe2c9;
+    --bg-input-color: #eee;
+    --bg-btn-color1: #1976d2;
+    --bg-btn-color2: #eee;
+    --bg-header-color1: #007000;
+    --bg-header-color2: #59b04f;
+    --bg-header-color3: #bbdefb;
+    --bg-menu-color1: #eee;
+    --bg-menu-color2: #e0e0e0;
+    --text-menu-color: #757575;
+    --text-ubtn-color: #bbb;
+
+    --text-tb-normal: #3e843e;
+    --bg-tb-normal: #e6edf4;
+    --bg-tb-hover: #fff;
+    --text-tb-active: #fff;
+    --bg-tb-active: #8ab45f;
+    --bg-tb-active-hover: #81c581;
+    --text-tb-disabled: #d3d3d3;
+    --bg-tb-disabled: #808080;
+
+    --bg-selected-item-color1: #b0f0b0;
+    --bg-selected-item-color2: #d0f5d0;
+
+    /* light */
+    --bg-app-color-light: #fff;
+    --text-app-color-light: #000;
+    --bg-dialog-color-light: #fff;
+    --text-anchor-color-light: #00f;
+    --bg-loader-color-light: #ebe2c9;
+    --bg-input-color-light: #eee;
+    --bg-btn-color1-light: #1976d2;
+    --bg-btn-color2-light: #eee;
+    --bg-header-color1-light: #007000;
+    --bg-header-color2-light: #59b04f;
+    --bg-header-color3-light: #bbdefb;
+    --bg-menu-color1-light: #eee;
+    --bg-menu-color2-light: #e0e0e0;
+    --text-menu-color-light: #757575;
+    --text-ubtn-color-light: #bbb;
+
+    --text-tb-normal-light: #3e843e;
+    --bg-tb-normal-light: #e6edf4;
+    --bg-tb-hover-light: #fff;
+    --text-tb-active-light: #fff;
+    --bg-tb-active-light: #8ab45f;
+    --bg-tb-active-hover-light: #81c581;
+    --text-tb-disabled-light: #d3d3d3;
+    --bg-tb-disabled-light: #808080;
+
+    --bg-selected-item-color1-light: #b0f0b0;
+    --bg-selected-item-color2-light: #d0f5d0;
+
+    /* dark */
+    --bg-app-color-dark: #222;
+    --text-app-color-dark: #ccc;
+    --bg-dialog-color-dark: #444;
+    --text-anchor-color-dark: #09f;
+    --bg-loader-color-dark: #222;
+    --bg-input-color-dark: #333;
+    --bg-btn-color1-dark: #00695c;
+    --bg-btn-color2-dark: #333;
+    --bg-header-color1-dark: #004000;
+    --bg-header-color2-dark: #29901f;
+    --bg-header-color3-dark: #335673;
+    --bg-menu-color1-dark: #333;
+    --bg-menu-color2-dark: #424242;
+    --text-menu-color-dark: #858585;
+    --text-ubtn-color-dark: #555;
+    
+    --text-tb-normal-dark: #3e843e;
+    --bg-tb-normal-dark: #ddd;
+    --bg-tb-hover-dark: #ccc;
+    --text-tb-active-dark: #ddd;
+    --bg-tb-active-dark: #7aa44f;
+    --bg-tb-active-hover-dark: #71b571;
+    --text-tb-disabled-dark: #d3d3d3;
+    --bg-tb-disabled-dark: #808080;
+
+    --bg-selected-item-color1-dark: #605020;
+    --bg-selected-item-color2-dark: #403010;
+}
+
+a {
+    color: var(--text-anchor-color);
+}
+
+.bg-app, .text-bg-app {
+    background-color: var(--bg-app-color);
+}
+
+.text-app {
+    color: var(--text-app-color);
+}
+
+.bg-dialog {
+    background-color: var(--bg-dialog-color);
+}
+
+.bg-input {
+    background-color: var(--bg-input-color);
+}
+
+.bg-btn1 {
+    background-color: var(--bg-btn-color1);
+}
+
+.bg-btn2 {
+    background-color: var(--bg-btn-color2);
+}
+
+.bg-menu-1 {
+    background-color: var(--bg-menu-color1);
+}
+
+.bg-menu-2 {
+    background-color: var(--bg-menu-color2);
+}
+
+.text-menu {
+    color: var(--text-menu-color);
+}
+
+.bg-header-3 {
+    background-color: var(--bg-header-color3);
+}
+
+/* main section */
 body, html, #app {    
     margin: 0;
     padding: 0;
     width: 100%;
     height: 100%;
     font: normal 12pt ReaderDefault;
+    background-color: #333;
 }
 
 .q-notifications__list--top {

+ 4 - 3
client/components/ExternalLibs/BookmarkSettings/BookmarkSettings.vue

@@ -5,13 +5,13 @@
         </template>
 
         <div class="col column fit">
-            <div class="row items-center top-panel bg-grey-3">
+            <div class="row items-center top-panel bg-menu-2">
                 <q-btn :disabled="!selected" class="q-mr-md" round dense color="blue" icon="la la-check" size="16px" @click.stop="openSelected">
                     <q-tooltip :delay="1500" anchor="bottom middle" content-style="font-size: 80%">
                         Открыть выбранную закладку
                     </q-tooltip>
                 </q-btn>
-                <q-input ref="search" v-model="search" class="col" outlined dense bg-color="white" placeholder="Найти">
+                <q-input ref="search" v-model="search" bg-color="input" class="col" outlined dense placeholder="Найти">
                     <template #append>
                         <q-icon v-if="search !== ''" name="la la-times" class="cursor-pointer" @click="resetSearch" />
                     </template>
@@ -19,7 +19,7 @@
             </div>
 
             <div class="col row">
-                <div class="left-panel column items-center no-wrap bg-grey-3">
+                <div class="left-panel column items-center no-wrap bg-menu-1">
                     <q-btn class="q-my-sm" round dense color="blue" icon="la la-plus" size="14px" @click.stop="addBookmark">
                         <q-tooltip :delay="1500" anchor="bottom middle" content-style="font-size: 80%">
                             Добавить закладку
@@ -62,6 +62,7 @@
                         v-model:ticked="ticked"
                         v-model:expanded="expanded"
                         class="q-my-xs"
+                        color="input" 
                         :nodes="nodes"
                         node-key="key"
                         tick-strategy="leaf"

+ 18 - 9
client/components/ExternalLibs/ExternalLibs.vue

@@ -29,6 +29,7 @@
                     ref="rootLink"
                     v-model="rootLink"
                     class="q-mr-sm"
+                    bg-color="input" 
                     :options="rootLinkOptions"
                     style="width: 230px"
                     dropdown-icon="la la-angle-down la-sm"
@@ -58,6 +59,7 @@
                     ref="selectedLink"
                     v-model="selectedLink"
                     class="q-mr-sm"
+                    bg-color="input" 
                     :options="selectedLinkOptions"
                     style="width: 50px"
                     dropdown-icon="la la-angle-down la-sm"
@@ -73,8 +75,8 @@
                     ref="input"
                     v-model="bookUrl"
                     class="col q-mr-sm"
+                    bg-color="input" 
                     outlined dense
-                    bg-color="white"
                     placeholder="Скопируйте сюда ссылку на книгу и нажмите 'Открыть'"
                     @focus="selectAllOnFocus" @keydown="bookUrlKeyDown"
                 >
@@ -108,7 +110,7 @@
             </div>
             <div class="separator"></div>
 
-            <div ref="frameBox" class="col fit" style="position: relative;">
+            <div ref="frameBox" class="col fit" style="position: relative; background-color: white">
                 <div ref="frameWrap" class="overflow-hidden">
                     <iframe v-if="frameVisible" ref="frame" :src="frameSrc" frameborder="0" allow="clipboard-read; clipboard-write"></iframe>
                 </div>
@@ -133,8 +135,8 @@
                         ref="bookmarkLink"
                         v-model="bookmarkLink"
                         class="col q-mr-sm"
+                        bg-color="input" 
                         outlined dense
-                        bg-color="white"
                         placeholder="Ссылка для закладки" maxlength="2000" @focus="selectAllOnFocus" @keydown="bookmarkLinkKeyDown"
                     >
                     </q-input>
@@ -143,6 +145,7 @@
                         ref="defaultRootLink"
                         v-model="defaultRootLink"
                         class="q-mr-sm"
+                        bg-color="input" 
                         :options="defaultRootLinkOptions"
                         style="width: 50px"
                         dropdown-icon="la la-angle-down la-sm"
@@ -159,8 +162,8 @@
                         ref="bookmarkDesc"
                         v-model="bookmarkDesc"
                         class="col q-mr-sm"
+                        bg-color="input" 
                         outlined dense
-                        bg-color="white"
                         placeholder="Описание" style="width: 400px" maxlength="100" @focus="selectAllOnFocus" @keydown="bookmarkDescKeyDown"
                     >
                     </q-input>
@@ -494,13 +497,19 @@ class ExternalLibs {
     }
 
     get header() {
-        let result = (this.ready ? 'Сетевая библиотека' : 'Загрузка...');
+        let result = [this.ready ? 'Сетевая библиотека' : 'Загрузка...'];
         if (this.ready && this.selectedLink) {
-            let title = `${(this.libs.comment ? this.libs.comment + ' ': '') + lu.removeProtocol(this.libs.startLink)}`;
-            if (this.inpxReady && this.inpxTitle)
-                title = `${this.inpxTitle} ${lu.removeProtocol(this.inpxUrl)}`;
-            result += ` | ${title}`;
+
+            if (this.inpxReady && this.inpxTitle) {
+                result.push(this.inpxTitle);
+                result.push(lu.removeProtocol(this.inpxUrl));
+            } else {
+                result.push(this.libs.comment);
+                result.push(lu.removeProtocol(this.libs.startLink));
+            }
         }
+
+        result = result.filter(s => s).join(' | ');
         this.$root.setAppTitle(result);
         return result;
     }

+ 21 - 16
client/components/Reader/ContentsPage/ContentsPage.vue

@@ -4,20 +4,20 @@
             Оглавление/закладки
         </template>
 
-        <div class="bg-grey-3 row">
+        <div class="bg-menu-1 row">
             <q-tabs
                 v-model="selectedTab"
-                active-color="black"
-                active-bg-color="white"
-                indicator-color="white"
+                active-color="app"
+                active-bg-color="app"
+                indicator-color="bg-app"
                 dense
                 no-caps
                 inline-label
-                class="no-mp bg-grey-4 text-grey-7"
+                class="no-mp bg-menu-2 text-menu"
             >
                 <q-tab name="contents" icon="la la-list" label="Оглавление" />
                 <q-tab name="images" icon="la la-image" label="Изображения" />
-                <q-tab name="bookmarks" icon="la la-bookmark" label="Закладки" />
+                <!--q-tab name="bookmarks" icon="la la-bookmark" label="Закладки" /-->
             </q-tabs>
         </div>
 
@@ -80,13 +80,13 @@
                                 <div class="image-num">
                                     {{ item.num }}
                                 </div>
-                                <div v-show="item.type == 'image/jpeg'" class="image-type it-jpg-color row justify-center">
+                                <div v-show="item.type == 'image/jpeg'" class="image-type text-black it-jpg-color row justify-center">
                                     JPG
                                 </div>
-                                <div v-show="item.type == 'image/png'" class="image-type it-png-color row justify-center">
+                                <div v-show="item.type == 'image/png'" class="image-type text-black it-png-color row justify-center">
                                     PNG
                                 </div>
-                                <div v-show="!item.local" class="image-type it-net-color row justify-center">
+                                <div v-show="!item.local" class="image-type text-black it-net-color row justify-center">
                                     INET
                                 </div>
                             </div>
@@ -250,7 +250,7 @@ class ContentsPage {
             const bin = parsed.binary[image.id];
             const type = (bin ? bin.type : '');
             
-            const label = (image.alt ? image.alt : '<span style="font-size: 90%; color: #dddddd"><i>Без названия</i></span>');
+            const label = (image.alt ? image.alt : '<span style="font-size: 90%; color: var(--bg-menu-color2)"><i>Без названия</i></span>');
             const indentStyle = getIndentStyle(1);
             const labelStyle = getLabelStyle(1);
 
@@ -466,27 +466,31 @@ export default vueComponent(ContentsPage);
 }
 
 .item, .subitem, .item-book-pos, .subitem-book-pos {
-    border-bottom: 1px solid #e0e0e0;
+    border-bottom: 1px solid var(--bg-menu-color2);
 }
 
 .item:hover, .subitem:hover {
-    background-color: #f0f0f0;
+    background-color: var(--bg-menu-color2);
 }
 
 .item-book-pos {
-    background-color: #b0f0b0;
+    opacity: 1;
+    background-color: var(--bg-selected-item-color1);
 }
 
 .subitem-book-pos {
-    background-color: #d0f5d0;
+    opacity: 1;
+    background-color: var(--bg-selected-item-color2);
 }
 
 .item-book-pos:hover {
-    background-color: #b0e0b0;
+    opacity: 0.8;
+    transition: opacity 0.2s linear;
 }
 
 .subitem-book-pos:hover {
-    background-color: #d0f0d0;
+    opacity: 0.8;
+    transition: opacity 0.2s linear;
 }
 
 .expand-button, .no-expand-button {
@@ -535,6 +539,7 @@ export default vueComponent(ContentsPage);
 
 .image-thumb {
     height: 50px;
+    background-color: white;
 }
 
 .loading-img-icon {

+ 2 - 2
client/components/Reader/HelpPage/CommonHelpPage/CommonHelpPage.vue

@@ -59,7 +59,7 @@ class CommonHelpPage {
     }
 
     get bookmarkText() {
-        return `javascript:location.href='https://${window.location.host}/?url='+location.href;`
+        return `javascript:location.href='${window.location.protocol}//${window.location.host}/#/reader?url='+location.href;`
     }
 
     async copyText(text, mes) {
@@ -88,6 +88,6 @@ export default vueComponent(CommonHelpPage);
     margin-left: 10px;
     cursor: pointer;
     font-size: 120%;
-    color: blue;
+    color: var(--text-anchor-color);
 }
 </style>

+ 6 - 6
client/components/Reader/HelpPage/HelpPage.vue

@@ -1,20 +1,20 @@
 <template>
-    <Window @close="close" style="z-index: 200">
+    <Window style="z-index: 200" @close="close">
         <template #header>
             Справка
         </template>
 
         <div class="col column" style="min-width: 600px">
-            <div class="bg-grey-3 row">
+            <div class="bg-menu-1 row">
                 <q-tabs
                     v-model="selectedTab"
-                    active-color="black"
-                    active-bg-color="white"
-                    indicator-color="white"
+                    active-color="app"
+                    active-bg-color="app"
+                    indicator-color="bg-app"
                     dense
                     no-caps
                     inline-label
-                    class="bg-grey-4 text-grey-7"
+                    class="bg-menu-2 text-menu"
                 >
                     <q-tab v-for="btn in buttons" :key="btn.value" :name="btn.value" :label="btn.label" />
                 </q-tabs>

+ 1 - 1
client/components/Reader/HelpPage/VersionHistoryPage/VersionHistoryPage.vue

@@ -72,7 +72,7 @@ p {
 }
 
 .clickable {
-    color: blue;
+    color: var(--text-anchor-color);
     text-decoration: underline;
     cursor: pointer;
 }

+ 5 - 5
client/components/Reader/LoaderPage/LoaderPage.vue

@@ -14,7 +14,7 @@
         <div class="col-auto column justify-start items-center no-wrap overflow-hidden">
             <q-input
                 ref="input" v-model="bookUrl" class="full-width q-px-sm" style="max-width: 700px" 
-                outlined dense bg-color="white" placeholder="Ссылка на книгу или веб-страницу" @keydown="onInputKeydown"
+                outlined dense bg-color="input" placeholder="Ссылка на книгу или веб-страницу" @keydown="onInputKeydown"
             >
                 <template #append>
                     <q-btn rounded flat style="width: 40px" icon="la la-check" @click="submitUrl" />
@@ -29,13 +29,13 @@
             />
 
             <div class="q-my-sm"></div>
-            <q-btn no-caps dense class="q-px-sm" color="primary" size="13px" @click="loadFileClick">
+            <q-btn no-caps dense class="q-px-sm" color="btn1" size="13px" @click="loadFileClick">
                 <q-icon class="q-mr-xs" name="la la-caret-square-up" size="24px" />
                 Загрузить файл с диска
             </q-btn>
             
             <div class="q-my-sm"></div>
-            <q-btn no-caps dense class="q-px-sm" color="primary" size="13px" @click="loadBufferClick">
+            <q-btn no-caps dense class="q-px-sm" color="btn1" size="13px" @click="loadBufferClick">
                 <q-icon class="q-mr-xs" name="la la-comment" size="24px" />
                 Из буфера обмена
             </q-btn>
@@ -158,7 +158,7 @@ class LoaderPage {
 
     loadBuffer(opts) {
         if (opts.buffer.length) {
-            const file = new File([opts.buffer], 'dummyName-PasteFromClipboard');
+            const file = new File([opts.buffer], `paste_from_clipboard_#${utils.randomHexString(10)}`);
             this.$emit('load-file', {file});
         }
     }
@@ -217,7 +217,7 @@ export default vueComponent(LoaderPage);
 }
 
 .clickable {
-    color: blue;
+    color: var(--text-anchor-color);
     text-decoration: underline;
     cursor: pointer;
 }

+ 14 - 3
client/components/Reader/LoaderPage/PasteTextPage/PasteTextPage.vue

@@ -8,9 +8,11 @@
             </span>
         </template>
 
-        <q-input v-model="bookTitle" class="q-px-sm" dense borderless placeholder="Введите название текста" />
-        <hr />
-        <textarea ref="textArea" class="text" @paste="calcTitle"></textarea>
+        <div class="fit column main">
+            <q-input v-model="bookTitle" class="q-px-sm" dense borderless placeholder="Введите название текста" />
+            <hr />
+            <textarea ref="textArea" class="main text" @paste="calcTitle"></textarea>
+        </div>
     </Window>
 </template>
 
@@ -39,6 +41,10 @@ class PasteTextPage {
         this.$refs.textArea.focus();
     }
 
+    get dark() {
+        return this.$store.state.reader.settings.nightMode;
+    }
+
     getNonEmptyLine3words(text, count) {
         let result = '';
         const lines = text.split("\n");
@@ -115,6 +121,11 @@ export default vueComponent(PasteTextPage);
     outline: none;
 }
 
+.main {
+    color: var(--text-app-color);
+    background-color: var(--bg-app-color);
+}
+
 hr {
     margin: 0;
     padding: 0;

+ 37 - 20
client/components/Reader/Reader.vue

@@ -115,6 +115,12 @@
 
                 <div class="col"></div>
 
+                <button v-show="showToolButton['nightMode']" ref="nightMode" v-ripple class="tool-button" :class="buttonActiveClass('nightMode')" @click="buttonClick('nightMode')">
+                    <q-icon name="la la-moon" size="32px" />
+                    <q-tooltip :delay="1500" anchor="bottom middle" content-style="font-size: 80%">
+                        {{ rstore.readerActions['nightMode'] }}
+                    </q-tooltip>
+                </button>
                 <button v-show="showToolButton['clickControl']" ref="clickControl" v-ripple class="tool-button" :class="buttonActiveClass('clickControl')" @click="buttonClick('clickControl')">
                     <q-icon name="la la-mouse" size="32px" />
                     <q-tooltip :delay="1500" anchor="bottom middle" content-style="font-size: 80%">
@@ -136,7 +142,7 @@
             </div>
         </div>
 
-        <div class="main col row relative-position">
+        <div class="col row relative-position main">
             <keep-alive>
                 <component 
                     :is="activePage"
@@ -290,6 +296,8 @@ class Reader {
     contentsActive = false;    
     libsActive = false;
     recentBooksActive = false;
+    
+    nightModeActive = false;
     clickControlActive = false;
     settingsActive = false;
 
@@ -462,8 +470,8 @@ class Reader {
         this.allowUrlParamBookPos = settings.allowUrlParamBookPos;
         this.copyFullText = settings.copyFullText;
         this.showClickMapPage = settings.showClickMapPage;
-        this.clickControl = settings.clickControl;
-        this.clickControlActive = this.clickControl;
+        this.nightModeActive = settings.nightMode;
+        this.clickControlActive = settings.clickControl;
         this.blinkCachedLoad = settings.blinkCachedLoad;
         this.showToolButton = settings.showToolButton;
         this.toolBarHideOnScroll = settings.toolBarHideOnScroll;
@@ -1014,10 +1022,16 @@ class Reader {
         }
     }
 
+    nightModeToggle() {
+        if (!this.nightModeActive && !utils.hasProp(this.settings.nightColorSets, 'textColor')) {
+            this.$root.notify.warning(`Ночной режим активирован впервые. Цвета заданы по умолчанию.`);
+        }
+
+        this.commit('reader/nightModeToggle');
+    }
+
     clickControlToggle() {
-        const newSettings = _.cloneDeep(this.settings);
-        newSettings.clickControl = !this.clickControl;
-        this.commit('reader/setSettings', newSettings);
+        this.commit('reader/setSettings', {clickControl: !this.clickControlActive});
     }
 
     offlineModeToggle() {
@@ -1119,6 +1133,7 @@ class Reader {
             case 'contents':
             case 'libs':
             case 'recentBooks':
+            case 'nightMode':
             case 'clickControl':
             case 'offlineMode':
             case 'settings':
@@ -1167,7 +1182,7 @@ class Reader {
     }
 
     async activateClickMapPage() {
-        if (this.clickControl && this.showClickMapPage && !this.clickMapActive) {
+        if (this.clickControlActive && this.showClickMapPage && !this.clickMapActive) {
             this.clickMapActive = true;
             await this.$refs.clickMapPage.slowDisappear();
             this.clickMapActive = false;
@@ -1525,6 +1540,9 @@ class Reader {
             case 'recentBooks':
                 this.recentBooksToggle();
                 break;
+            case 'nightMode':
+                this.nightModeToggle();
+                break;
             case 'clickControl':
                 this.clickControlToggle();
                 break;
@@ -1674,15 +1692,15 @@ export default vueComponent(Reader);
 }
 
 .main {
-    background-color: #EBE2C9;
-    color: #000;
+    background-color: var(--bg-loader-color);
+    color: var(--text-app-color);
 }
 
 .tool-button {
     margin: 0px 2px 7px 2px;
     padding: 0;
-    color: #3E843E;
-    background-color: #E6EDF4;
+    color: var(--text-tb-normal);
+    background-color: var(--bg-tb-normal);
     min-height: 38px;
     min-width: 38px;
     height: 38px;
@@ -1694,34 +1712,33 @@ export default vueComponent(Reader);
 }
 
 .tool-button:hover {
-    background-color: white;
+    background-color: var(--bg-tb-hover);
     cursor: pointer;
 }
 
 .tool-button-active {
     box-shadow: 0 0 0;
-    color: white;
-    background-color: #8AB45F;
+    color: var(--text-tb-active);
+    background-color: var(--bg-tb-active);
     position: relative;
     top: 1px;
     left: 1px;
 }
 
 .tool-button-active:hover {
-    color: white;
-    background-color: #81C581;
+    background-color: var(--bg-tb-active-hover);
     cursor: pointer;
 }
 
 .tool-button-disabled {
-    color: lightgray;
-    background-color: gray;
+    color: var(--text-tb-disabled);
+    background-color: var(--bg-tb-disabled);
     cursor: default;
 }
 
 .tool-button-disabled:hover {
-    color: lightgray;
-    background-color: gray;
+    color: var(--text-tb-disabled);
+    background-color: var(--bg-tb-disabled);
     cursor: default;
 }
 

+ 4 - 4
client/components/Reader/ReaderDialogs/ReaderDialogs.vue

@@ -12,14 +12,14 @@
             <span class="clickable" style="font-size: 13px" @click="openVersionHistory">Посмотреть историю версий</span>
 
             <template #footer>
-                <q-btn class="q-px-md" dense no-caps @click="whatsNewDisable">
+                <q-btn class="q-px-md" color="btn2" text-color="app" dense no-caps @click="whatsNewDisable">
                     Больше не показывать
                 </q-btn>
             </template>
         </Dialog>
 
         <q-dialog ref="dialog2" v-model="donationVisible" style="z-index: 100" no-route-dismiss no-esc-dismiss no-backdrop-dismiss>
-            <div class="column bg-white no-wrap q-pa-md">
+            <div class="column bg-dialog no-wrap q-pa-md">
                 <div class="row justify-center q-mb-md">
                     Здравствуйте, дорогие читатели!
                 </div>
@@ -84,7 +84,7 @@
 
             <div style="word-break: normal">
                 Если вы пытаетесь вставить текст в читалку из буфера обмена, пожалуйста воспользуйтесь кнопкой
-                <q-btn no-caps dense class="q-px-sm" color="primary" size="13px" @click="loadBufferClick">
+                <q-btn no-caps dense class="q-px-sm" color="btn1" size="13px" @click="loadBufferClick">
                     <q-icon class="q-mr-xs" name="la la-comment" size="24px" />
                     Из буфера обмена
                 </q-btn>
@@ -233,7 +233,7 @@ export default vueComponent(ReaderDialogs);
 
 <style scoped>
 .clickable {
-    color: blue;
+    color: var(--text-anchor-color);
     text-decoration: underline;
     cursor: pointer;
 }

+ 16 - 13
client/components/Reader/RecentBooksPage/RecentBooksPage.vue

@@ -36,29 +36,29 @@
         <a ref="download" style="display: none;" target="_blank"></a>
 
         <div id="vs-container" ref="vsContainer" class="recent-books-scroll col">
-            <div ref="header" class="scroll-header row bg-blue-2">
-                <q-btn class="tool-button" round @click="showSameBookClick">
+            <div ref="header" class="scroll-header row bg-header-3">
+                <q-btn class="tool-button" color="btn2" round @click="showSameBookClick">
                     <q-icon name="la la-caret-right" class="icon" :class="{'expanded-icon': showSameBook}" color="green-8" size="24px" />
                     <q-tooltip :delay="1500" anchor="bottom middle" content-style="font-size: 80%">
                         Показать/скрыть версии книг
                     </q-tooltip>
                 </q-btn>
 
-                <q-btn class="tool-button" round @click="scrollToBegin">
+                <q-btn class="tool-button" color="btn2" round @click="scrollToBegin">
                     <q-icon name="la la-arrow-up" color="green-8" size="24px" />
                     <q-tooltip :delay="1500" anchor="bottom middle" content-style="font-size: 80%">
                         В начало списка
                     </q-tooltip>
                 </q-btn>
 
-                <q-btn class="tool-button" round @click="scrollToEnd">
+                <q-btn class="tool-button" color="btn2" round @click="scrollToEnd">
                     <q-icon name="la la-arrow-down" color="green-8" size="24px" />
                     <q-tooltip :delay="1500" anchor="bottom middle" content-style="font-size: 80%">
                         В конец списка
                     </q-tooltip>
                 </q-btn>
 
-                <q-btn class="tool-button" round @click="scrollToActiveBook">
+                <q-btn class="tool-button" color="btn2" round @click="scrollToActiveBook">
                     <q-icon name="la la-location-arrow" color="green-8" size="24px" />
                     <q-tooltip :delay="1500" anchor="bottom middle" content-style="font-size: 80%">
                         На текущую книгу
@@ -71,7 +71,7 @@
                     class="q-ml-sm q-mt-xs"
                     outlined dense
                     style="width: 185px"
-                    bg-color="white"
+                    bg-color="input"
                     placeholder="Найти"
                     @click.stop
                 >
@@ -86,7 +86,7 @@
                     class="q-ml-sm q-mt-xs"
                     :options="sortMethodOptions"
                     style="width: 180px"
-                    bg-color="white"
+                    bg-color="input"
                     dropdown-icon="la la-angle-down la-sm"
                     outlined dense emit-value map-options display-value-sanitize options-sanitize
                     options-html display-value-html
@@ -140,7 +140,7 @@
                             class="col" style="border: 1px solid #cccccc; border-bottom: 0; padding: 4px; line-height: 140%;"
                             :style="{ 'width': (380 - 40*(+item.inGroup)) + 'px' }"
                         >
-                            <div class="text-green-10" style="font-size: 80%">
+                            <div :class="dark ? 'text-lime-4' : 'text-green-10'" style="font-size: 80%">
                                 {{ item.desc.author }}
                             </div>
                             <div style="font-size: 75%">
@@ -349,6 +349,10 @@ class RecentBooksPage {
         return this.$store.state.config.bucEnabled && this.bucEnabled;
     }
 
+    get dark() {
+        return this.$store.state.reader.settings.nightMode;
+    }
+
     async updateTableData() {
         if (!this.inited)
             return;
@@ -847,7 +851,7 @@ export default vueComponent(RecentBooksPage);
     position: sticky;
     z-index: 1;
     top: 0;
-    border-bottom: 2px solid #aaaaaa;
+    border-bottom: 2px solid var(--bg-menu-color2);
     padding-left: 5px;
 }
 
@@ -870,15 +874,15 @@ export default vueComponent(RecentBooksPage);
 }
 
 .even {
-    background-color: #f2f2f2;
+    background-color: var(--bg-menu-color1);
 }
 
 .active-book {
-    background-color: #b0f0b0 !important;
+    background-color: var(--bg-selected-item-color1) !important;
 }
 
 .active-parent-book {
-    background-color: #ffbbbb !important;
+    background-color: var(--bg-selected-item-color2) !important;
 }
 
 .icon {
@@ -895,7 +899,6 @@ export default vueComponent(RecentBooksPage);
     min-height: 30px;
     height: 30px;
     margin: 10px 6px 0px 3px;
-    background-color: white;
 }
 
 .row-info-bottom {

+ 1 - 0
client/components/Reader/SearchPage/SearchPage.vue

@@ -11,6 +11,7 @@
                 <q-input
                     ref="input" v-model="needle"
                     class="col" outlined dense
+                    bg-color="input"
                     placeholder="Найти"
                     @keydown="inputKeyDown"         
                 />

+ 1 - 1
client/components/Reader/SetPositionPage/SetPositionPage.vue

@@ -80,7 +80,7 @@ export default vueComponent(SetPositionPage);
 .slider {
     margin: 0 20px 0 20px;
     height: 35px;
-    background-color: #efefef;
+    background-color: var(--bg-input-color);
     border-radius: 15px;
 }
 </style>

+ 2 - 2
client/components/Reader/SettingsPage/ConvertTab/ConvertTab.vue

@@ -71,7 +71,7 @@
                     Качество
                 </div>
                 <div class="col row">
-                    <NumInput v-model="form.pdfQuality" class="col-5" :min="10" :max="100">
+                    <NumInput v-model="form.pdfQuality" bg-color="input" class="col-5" :min="10" :max="100">
                         <q-tooltip :delay="1000" anchor="top middle" self="bottom middle" content-style="font-size: 80%">
                             Качество конвертирования Pdf в Fb2. Чем значение выше, тем больше<br>
                             размер итогового файла. Если сервер отказывается конвертировать<br>
@@ -93,7 +93,7 @@
                     Качество
                 </div>
                 <div class="col row">
-                    <NumInput v-model="form.djvuQuality" class="col-5" :min="10" :max="100">
+                    <NumInput v-model="form.djvuQuality" bg-color="input" class="col-5" :min="10" :max="100">
                         <q-tooltip :delay="1000" anchor="top middle" self="bottom middle" content-style="font-size: 80%">
                             Качество конвертирования Djvu в Fb2. Чем значение выше, тем больше<br>
                             размер итогового файла. Если сервер отказывается конвертировать<br>

+ 5 - 5
client/components/Reader/SettingsPage/KeysTab/KeysTab.vue

@@ -1,14 +1,14 @@
 <template>
     <div class="fit column">
-        <div class="bg-grey-3 row">
+        <div class="bg-menu-1 row">
             <q-tabs
                 v-model="selectedTab"
-                active-color="black"
-                active-bg-color="white"
-                indicator-color="white"
+                active-color="app"
+                active-bg-color="app"
+                indicator-color="bg-app"
                 dense
                 no-caps
-                class="bg-grey-4 text-grey-7"
+                class="bg-menu-2 text-menu"
             >
                 <q-tab name="mouse" label="Мышь/тачскрин" />
                 <q-tab name="keyboard" label="Клавиатура" />

+ 5 - 5
client/components/Reader/SettingsPage/KeysTab/UserHotKeys/UserHotKeys.vue

@@ -2,10 +2,10 @@
     <div class="table col column no-wrap">
         <!-- header -->
         <div class="table-row row">
-            <div class="desc q-pa-sm bg-blue-2">
+            <div class="desc q-pa-sm bg-header-3">
                 Команда
             </div>
-            <div class="hotKeys col q-pa-sm bg-blue-2 row no-wrap">
+            <div class="hotKeys col q-pa-sm bg-header-3 row no-wrap">
                 <div style="width: 80px">
                     Сочетание клавиш
                 </div>
@@ -14,7 +14,7 @@
                     v-model="search"
                     class="q-ml-sm col"
                     outlined dense
-                    bg-color="grey-4"
+                    bg-color="input"
                     placeholder="Найти"                    
                     @click.stop
                 />
@@ -234,11 +234,11 @@ export default vueComponent(UserHotKeys);
 }
 
 .table-row:nth-child(even) {
-    background-color: #f7f7f7;
+    background-color: var(--bg-menu-color1);
 }
 
 .table-row:hover {
-    background-color: #f0f0f0;
+    background-color: var(--bg-menu-color2);
 }
 
 .desc {

+ 0 - 14
client/components/Reader/SettingsPage/OthersTab/OthersTab.vue

@@ -70,20 +70,6 @@
             Другое
         </div>
 
-        <div class="sets-item row">
-            <div class="sets-label label">
-                Обработка
-            </div>
-            <q-checkbox v-model="form.lazyParseEnabled" size="xs" label="Предварительная подготовка текста">
-                <q-tooltip :delay="1000" anchor="top middle" self="bottom middle" content-style="font-size: 80%">
-                    Включение этой опции позволяет делать предварительную<br>
-                    подготовку всего текста в ленивом режиме сразу после<br>
-                    загрузки книги. Это может повысить отзывчивость читалки,<br>
-                    но нагружает процессор каждый раз при открытии книги.
-                </q-tooltip>
-            </q-checkbox>
-        </div>
-
         <div class="sets-item row">
             <div class="sets-label label">
                 Парам. в URL

+ 2 - 2
client/components/Reader/SettingsPage/PageMoveTab/PageMoveTab.vue

@@ -10,7 +10,7 @@
                 Тип
             </div>
             <q-select
-                v-model="form.pageChangeAnimation" class="col-left" :options="pageChangeAnimationOptions"
+                v-model="form.pageChangeAnimation" bg-color="input" class="col-left" :options="pageChangeAnimationOptions"
                 dropdown-icon="la la-angle-down la-sm"
                 outlined dense emit-value map-options
             />
@@ -20,7 +20,7 @@
             <div class="sets-label label">
                 Скорость
             </div>
-            <NumInput v-model="form.pageChangeAnimationSpeed" class="col-left" :min="0" :max="100" :disable="form.pageChangeAnimation == ''" />
+            <NumInput v-model="form.pageChangeAnimationSpeed" bg-color="input" class="col-left" :min="0" :max="100" :disable="form.pageChangeAnimation == ''" />
         </div>
 
         <!---------------------------------------------->

+ 8 - 7
client/components/Reader/SettingsPage/ProfilesTab/ProfilesTab.vue

@@ -30,6 +30,7 @@
                     <q-select
                         v-model="currentProfile" :options="currentProfileOptions"
                         style="width: 275px"
+                        bg-color="input"
                         dropdown-icon="la la-angle-down la-sm"
                         outlined dense emit-value map-options display-value-sanitize options-sanitize
                     />
@@ -37,13 +38,13 @@
             </div>
             <div class="sets-item row">
                 <div class="sets-label label"></div>
-                <q-btn class="sets-button" dense no-caps @click="addProfile">
+                <q-btn class="sets-button" color="btn2" text-color="app" dense no-caps @click="addProfile">
                     Добавить
                 </q-btn>
-                <q-btn class="sets-button" dense no-caps @click="delProfile">
+                <q-btn class="sets-button" color="btn2" text-color="app" dense no-caps @click="delProfile">
                     Удалить
                 </q-btn>
-                <q-btn class="sets-button" dense no-caps @click="delAllProfiles">
+                <q-btn class="sets-button" color="btn2" text-color="app" dense no-caps @click="delAllProfiles">
                     Удалить все
                 </q-btn>
             </div>
@@ -63,7 +64,7 @@
 
             <div class="sets-item row">
                 <div class="sets-label label"></div>
-                <q-btn class="sets-button" style="width: 250px" dense no-caps @click="showServerStorageKey">
+                <q-btn class="sets-button" color="btn2" text-color="app" style="width: 250px" dense no-caps @click="showServerStorageKey">
                     <span v-show="serverStorageKeyVisible">Скрыть</span>
                     <span v-show="!serverStorageKeyVisible">Показать</span>
                     &nbsp;ключ доступа
@@ -104,13 +105,13 @@
 
             <div class="sets-item row">
                 <div class="sets-label label"></div>
-                <q-btn class="sets-button" style="width: 250px" dense no-caps @click="enterServerStorageKey">
+                <q-btn class="sets-button" color="btn2" text-color="app" style="width: 250px" dense no-caps @click="enterServerStorageKey">
                     Ввести ключ доступа
                 </q-btn>
             </div>
             <div class="sets-item row">
                 <div class="sets-label label"></div>
-                <q-btn class="sets-button" style="width: 250px" dense no-caps @click="generateServerStorageKey">
+                <q-btn class="sets-button" color="btn2" text-color="app" style="width: 250px" dense no-caps @click="generateServerStorageKey">
                     Сгенерировать новый ключ
                 </q-btn>
             </div>
@@ -357,6 +358,6 @@ export default vueComponent(ProfilesTab);
     margin-left: 5px;
     cursor: pointer;
     font-size: 120%;
-    color: blue;
+    color: var(--text-anchor-color);
 }
 </style>

+ 1 - 1
client/components/Reader/SettingsPage/ResetTab/ResetTab.vue

@@ -1,7 +1,7 @@
 <template>
     <div class="fit sets-tab-panel">
         <div class="sets-item row">
-            <q-btn class="col q-ma-sm" dense no-caps @click="setDefaults">
+            <q-btn class="col q-ma-sm" color="btn2" text-color="app" dense no-caps @click="setDefaults">
                 Установить по умолчанию
             </q-btn>
         </div>

+ 4 - 3
client/components/Reader/SettingsPage/SettingsPage.vue

@@ -9,14 +9,14 @@
                 <q-tabs
                     ref="tabs"
                     v-model="selectedTab"
-                    class="bg-grey-3 text-grey-9"
+                    class="bg-menu-1 text-menu"
                     style="max-width: 130px"
                     
                     left-icon="la la-caret-up"
                     right-icon="la la-caret-down"
                     active-color="white"
                     active-bg-color="primary"
-                    indicator-color="black"
+                    indicator-color="bg-app"
                     vertical
                     no-caps
                     stretch
@@ -35,7 +35,7 @@
                 <!-- Профили --------------------------------------------------------------------->
                 <ProfilesTab v-if="selectedTab == 'profiles'" :form="form" />
                 <!-- Вид ------------------------------------------------------------------------->                    
-                <ViewTab v-if="selectedTab == 'view'" :form="form" />
+                <ViewTab v-if="selectedTab == 'view'" :form="form" @tab-event="tabEvent" />
                 <!-- Кнопки ---------------------------------------------------------------------->
                 <ToolBarTab v-if="selectedTab == 'toolbar'" :form="form" />
                 <!-- Управление ------------------------------------------------------------------>
@@ -178,6 +178,7 @@ class SettingsPage {
 
         switch (event.action) {
             case 'set-defaults': this.setDefaults(); break;
+            case 'night-mode': this.$emit('do-action', {action: 'nightMode'}); break;
         }
     }
 

+ 2 - 2
client/components/Reader/SettingsPage/UpdateTab/UpdateTab.vue

@@ -37,7 +37,7 @@
                 Разница размеров
             </div>
             <div class="col row">
-                <NumInput v-model="form.bucSizeDiff" style="width: 200px" />
+                <NumInput v-model="form.bucSizeDiff" bg-color="input" style="width: 200px" />
 
                 <q-tooltip :delay="1000" anchor="top middle" self="bottom middle" content-style="font-size: 80%">
                     Уведомлять о наличии обновления книги в списке загруженных<br>
@@ -73,7 +73,7 @@
             <div class="sets-label label"></div>
             <div class="col-4"></div>
             <div class="col row">
-                <NumInput v-model="form.bucCancelDays" :min="1" :max="10000" />
+                <NumInput v-model="form.bucCancelDays" bg-color="input" :min="1" :max="10000" />
 
                 <q-tooltip :delay="1000" anchor="top middle" self="bottom middle" content-style="font-size: 80%">
                     Снимать флаг проверки с книги, если не было<br>

+ 3 - 0
client/components/Reader/SettingsPage/ViewTab/Color/Color.vue

@@ -13,6 +13,7 @@
                 <q-input
                     v-model="textColorFiltered"
                     class="col-left no-mp"
+                    bg-color="input" 
                     outlined dense
                     
                     :rules="['hexColor']"
@@ -43,6 +44,7 @@
                 <q-input 
                     v-model="bgColorFiltered"
                     class="col-left no-mp"
+                    bg-color="input" 
                     outlined dense
                     
                     :rules="['hexColor']"
@@ -71,6 +73,7 @@
                     v-model="form.wallpaper"
                     class="col-left no-mp"
                     :options="wallpaperOptions"
+                    bg-color="input" 
                     dropdown-icon="la la-angle-down la-sm"
                     outlined dense emit-value map-options
                 >

+ 4 - 4
client/components/Reader/SettingsPage/ViewTab/Font/Font.vue

@@ -11,14 +11,14 @@
             </div>
             <div class="col row">
                 <q-select
-                    v-model="form.fontName" class="col-left" :options="fontsOptions" :disable="form.webFontName != ''"
+                    v-model="form.fontName" class="col-left" bg-color="input" :options="fontsOptions" :disable="form.webFontName != ''"
                     dropdown-icon="la la-angle-down la-sm"
                     outlined dense emit-value map-options
                 />
 
                 <div class="q-px-sm" />
                 <q-select
-                    v-model="form.webFontName" class="col" :options="webFontsOptions"
+                    v-model="form.webFontName" class="col" bg-color="input" :options="webFontsOptions"
                     dropdown-icon="la la-angle-down la-sm"
                     outlined dense emit-value map-options
                 >
@@ -36,7 +36,7 @@
                 Размер
             </div>
             <div class="col row">
-                <NumInput v-model="form.fontSize" class="col-left" :min="5" :max="200" />
+                <NumInput v-model="form.fontSize" bg-color="input" class="col-left" :min="5" :max="200" />
 
                 <div class="col q-pt-xs text-right">
                     <a href="https://fonts.google.com/?subset=cyrillic" target="_blank">Примеры</a>
@@ -49,7 +49,7 @@
                 Сдвиг
             </div>
             <div class="col row">
-                <NumInput v-model="vertShift" class="col-left" :min="-100" :max="100">
+                <NumInput v-model="vertShift" bg-color="input" class="col-left" :min="-100" :max="100">
                     <q-tooltip :delay="1000" anchor="top middle" self="bottom middle" content-style="font-size: 80%">
                         Сдвиг шрифта по вертикали в процентах от размера.<br>
                         Отрицательное значение сдвигает вверх, положительное -<br>

+ 24 - 9
client/components/Reader/SettingsPage/ViewTab/Mode/Mode.vue

@@ -5,6 +5,13 @@
             Режим
         </div>
 
+        <div class="sets-item row">
+            <div class="sets-label label"></div>
+            <div class="col row">
+                <q-checkbox v-model="nightMode" size="xs" label="Ночной режим" @update:modelValue="nightModeToggle" />
+            </div>
+        </div>
+
         <div class="sets-item row">
             <div class="sets-label label"></div>
             <div class="col row">
@@ -20,13 +27,13 @@
                 Отступ границ
             </div>
             <div class="col row">
-                <NumInput v-model="form.indentLR" class="col-left" :min="0" :max="2000">
+                <NumInput v-model="form.indentLR" bg-color="input" class="col-left" :min="0" :max="2000">
                     <q-tooltip :delay="1000" anchor="top middle" self="bottom middle" content-style="font-size: 80%">
                         Слева/справа от края экрана
                     </q-tooltip>
                 </NumInput>
                 <div class="q-px-sm" />
-                <NumInput v-model="form.indentTB" class="col" :min="0" :max="2000">
+                <NumInput v-model="form.indentTB" bg-color="input" class="col" :min="0" :max="2000">
                     <q-tooltip :delay="1000" anchor="top middle" self="bottom middle" content-style="font-size: 80%">
                         Сверху/снизу от края экрана
                     </q-tooltip>
@@ -39,7 +46,7 @@
                 Отступ внутри
             </div>
             <div class="col row">
-                <NumInput v-model="form.dualIndentLR" class="col-left" :min="0" :max="2000">
+                <NumInput v-model="form.dualIndentLR" bg-color="input" class="col-left" :min="0" :max="2000">
                     <q-tooltip :delay="1000" anchor="top middle" self="bottom middle" content-style="font-size: 80%">
                         Слева/справа внутри страницы
                     </q-tooltip>
@@ -60,6 +67,7 @@
                     <q-input 
                         v-model="dualDivColorFiltered"
                         class="col-left no-mp"
+                        bg-color="input" 
                         outlined dense
                         :rules="['hexColor']"
                         style="max-width: 150px"
@@ -89,7 +97,7 @@
                     Прозрачность
                 </div>
                 <div class="col row">
-                    <NumInput v-model="form.dualDivColorAlpha" class="col-left" :min="0" :max="1" :digits="2" :step="0.1" />
+                    <NumInput v-model="form.dualDivColorAlpha" bg-color="input" class="col-left" :min="0" :max="1" :digits="2" :step="0.1" />
                 </div>
             </div>
 
@@ -98,7 +106,7 @@
                     Ширина (px)
                 </div>
                 <div class="col row">
-                    <NumInput v-model="form.dualDivWidth" class="col-left" :min="0" :max="100">
+                    <NumInput v-model="form.dualDivWidth" bg-color="input" class="col-left" :min="0" :max="100">
                         <q-tooltip :delay="1000" anchor="top middle" self="bottom middle" content-style="font-size: 80%">
                             Ширина разделителя
                         </q-tooltip>
@@ -111,7 +119,7 @@
                     Высота (%)
                 </div>
                 <div class="col row">
-                    <NumInput v-model="form.dualDivHeight" class="col-left" :min="0" :max="100">
+                    <NumInput v-model="form.dualDivHeight" bg-color="input" class="col-left" :min="0" :max="100">
                         <q-tooltip :delay="1000" anchor="top middle" self="bottom middle" content-style="font-size: 80%">
                             Высота разделителя
                         </q-tooltip>
@@ -124,13 +132,13 @@
                     Пунктир
                 </div>
                 <div class="col row">
-                    <NumInput v-model="form.dualDivStrokeFill" class="col-left" :min="0" :max="2000">
+                    <NumInput v-model="form.dualDivStrokeFill" bg-color="input" class="col-left" :min="0" :max="2000">
                         <q-tooltip :delay="1000" anchor="top middle" self="bottom middle" content-style="font-size: 80%">
                             Заполнение пунктира
                         </q-tooltip>
                     </NumInput>
                     <div class="q-px-sm" />
-                    <NumInput v-model="form.dualDivStrokeGap" class="col" :min="0" :max="2000">
+                    <NumInput v-model="form.dualDivStrokeGap" bg-color="input" class="col" :min="0" :max="2000">
                         <q-tooltip :delay="1000" anchor="top middle" self="bottom middle" content-style="font-size: 80%">
                             Промежуток пунктира
                         </q-tooltip>
@@ -143,7 +151,7 @@
                     Ширина тени
                 </div>
                 <div class="col row">
-                    <NumInput v-model="form.dualDivShadowWidth" class="col-left" :min="0" :max="100" />
+                    <NumInput v-model="form.dualDivShadowWidth" bg-color="input" class="col-left" :min="0" :max="100" />
                 </div>
             </div>
         </div>
@@ -185,6 +193,7 @@ class Mode {
 
     isFormChanged = false;
     dualDivColorFiltered = '';
+    nightMode = false;
 
     created() {
         this.formChanged();//no await
@@ -202,11 +211,17 @@ class Mode {
                 && (this.form.pageChangeAnimation == 'flip' || this.form.pageChangeAnimation == 'rightShift')
                 )
                 this.form.pageChangeAnimation = '';
+
+            this.nightMode = this.form.nightMode;
         } finally {
             await this.$nextTick();
             this.isFormChanged = false;
         }
     }
+
+    nightModeToggle() {
+        this.$emit('tab-event', {action: 'night-mode'});
+    }
 }
 
 export default vueComponent(Mode);

+ 3 - 2
client/components/Reader/SettingsPage/ViewTab/Status/Status.vue

@@ -23,6 +23,7 @@
                 <q-input
                     v-model="statusBarColorFiltered"
                     class="col-left no-mp"
+                    bg-color="input" 
                     outlined dense
                     :rules="['hexColor']"
                     style="max-width: 150px"
@@ -52,7 +53,7 @@
                 Прозрачность
             </div>
             <div class="col row">
-                <NumInput v-model="form.statusBarColorAlpha" class="col-left" :min="0" :max="1" :digits="2" :step="0.1" />
+                <NumInput v-model="form.statusBarColorAlpha" bg-color="input" class="col-left" :min="0" :max="1" :digits="2" :step="0.1" />
             </div>
         </div>
 
@@ -61,7 +62,7 @@
                 Высота
             </div>
             <div class="col row">
-                <NumInput v-model="form.statusBarHeight" class="col-left" :min="5" :max="100" />
+                <NumInput v-model="form.statusBarHeight" bg-color="input" class="col-left" :min="5" :max="100" />
             </div>
         </div>
 

+ 8 - 8
client/components/Reader/SettingsPage/ViewTab/Text/Text.vue

@@ -10,7 +10,7 @@
                 Интервал
             </div>
             <div class="col row">
-                <NumInput v-model="form.lineInterval" class="col-left" :min="0" :max="200" />
+                <NumInput v-model="form.lineInterval" bg-color="input" class="col-left" :min="0" :max="200" />
             </div>
         </div>
 
@@ -19,7 +19,7 @@
                 Параграф
             </div>
             <div class="col row">
-                <NumInput v-model="form.p" class="col-left" :min="0" :max="2000" />
+                <NumInput v-model="form.p" bg-color="input" class="col-left" :min="0" :max="2000" />
             </div>
         </div>
 
@@ -28,7 +28,7 @@
                 Сдвиг
             </div>
             <div class="col row">
-                <NumInput v-model="form.textVertShift" class="col-left" :min="-100" :max="100">
+                <NumInput v-model="form.textVertShift" bg-color="input" class="col-left" :min="-100" :max="100">
                     <q-tooltip :delay="1000" anchor="top middle" self="bottom middle" content-style="font-size: 80%">
                         Сдвиг текста по вертикали в процентах от размера шрифта.<br>
                         Отрицательное значение сдвигает вверх, положительное -<br>
@@ -43,7 +43,7 @@
                 Скроллинг
             </div>
             <div class="col row">
-                <NumInput v-model="form.scrollingDelay" class="col-left" :min="1" :max="10000">
+                <NumInput v-model="form.scrollingDelay" bg-color="input" class="col-left" :min="1" :max="10000">
                     <q-tooltip :delay="1000" anchor="top middle" self="bottom middle" content-style="font-size: 80%">
                         Замедление скроллинга в миллисекундах.<br>
                         Определяет время, за которое текст<br>
@@ -53,7 +53,7 @@
 
                 <div class="q-px-sm" />
                 <q-select
-                    v-model="form.scrollingType" class="col" :options="['linear', 'ease', 'ease-in', 'ease-out', 'ease-in-out']"
+                    v-model="form.scrollingType" bg-color="input" class="col" :options="['linear', 'ease', 'ease-in', 'ease-out', 'ease-in-out']"
                     dropdown-icon="la la-angle-down la-sm"
                     outlined dense emit-value map-options
                 >
@@ -81,7 +81,7 @@
                 Компактность
             </div>
             <div class="q-px-sm" />
-            <NumInput v-model="form.compactTextPerc" class="col" :min="0" :max="100">
+            <NumInput v-model="form.compactTextPerc" bg-color="input" class="col" :min="0" :max="100">
                 <q-tooltip :delay="1000" anchor="top middle" self="bottom middle" content-style="font-size: 80%">
                     Степень компактности текста в процентах.<br>
                     Чем больше компактность, тем хуже выравнивание<br>
@@ -105,7 +105,7 @@
                 Добавлять пустые
             </div>
             <div class="q-px-sm" />
-            <NumInput v-model="form.addEmptyParagraphs" class="col" :min="0" :max="2" />
+            <NumInput v-model="form.addEmptyParagraphs" bg-color="input" class="col" :min="0" :max="2" />
         </div>
 
         <div class="sets-item row">
@@ -135,7 +135,7 @@
                 Высота не более
             </div>
             <div class="q-px-sm" />
-            <NumInput v-model="form.imageHeightLines" class="col" :min="1" :max="100" :disable="!form.showImages">
+            <NumInput v-model="form.imageHeightLines" bg-color="input" class="col" :min="1" :max="100" :disable="!form.showImages">
                 <q-tooltip :delay="1000" anchor="top middle" self="bottom middle" content-style="font-size: 80%">
                     Определяет высоту изображения количеством строк.<br>
                     В случае превышения высоты, изображение будет<br>

+ 13 - 5
client/components/Reader/SettingsPage/ViewTab/ViewTab.vue

@@ -2,12 +2,12 @@
     <div class="fit column">
         <q-tabs
             v-model="selectedTab"
-            active-color="black"
-            active-bg-color="white"
-            indicator-color="white"
+            active-color="app"
+            active-bg-color="app"
+            indicator-color="bg-app"
             dense
             no-caps
-            class="no-mp bg-grey-4 text-grey-7"
+            class="no-mp bg-menu-2 text-menu"
         >
             <q-tab name="mode" label="Режим" />
             <q-tab name="color" label="Цвет" />
@@ -19,7 +19,7 @@
         <div class="q-mb-sm" />
 
         <div class="col sets-tab-panel">
-            <Mode v-if="selectedTab == 'mode'" :form="form" />
+            <Mode v-if="selectedTab == 'mode'" :form="form" @tab-event="tabEvent" />
             <Color v-if="selectedTab == 'color'" :form="form" />
             <Font v-if="selectedTab == 'font'" :form="form" />
             <Text v-if="selectedTab == 'text'" :form="form" />
@@ -61,6 +61,14 @@ class ViewTab {
     mounted() {
     }
 
+    tabEvent(event) {
+        if (!event || !event.action)
+            return;
+
+        switch (event.action) {
+            case 'night-mode': this.$emit('tab-event', {action: 'night-mode'}); break;
+        }
+    }
 }
 
 export default vueComponent(ViewTab);

+ 0 - 36
client/components/Reader/TextPage/TextPage.vue

@@ -433,10 +433,6 @@ class TextPage {
         if (this.lastBook) {
             (async() => {
                 try {
-                    //подождем ленивый парсинг
-                    this.stopLazyParse = true;
-                    while (this.doingLazyParse) await utils.sleep(10);
-
                     const isParsed = await bookManager.hasBookParsed(this.lastBook);
                     if (!isParsed) {
                         return;
@@ -460,8 +456,6 @@ class TextPage {
                     await this.calcPropsAndLoadFonts();
 
                     this.refreshTime();
-                    if (this.lazyParseEnabled)
-                        this.lazyParsePara();
                 } catch (e) {
                     this.$root.stdDialog.alert(e.message, 'Ошибка', {color: 'negative'});
                 }
@@ -838,36 +832,6 @@ class TextPage {
         this.drawStatusBar();
     }
 
-    async lazyParsePara() {
-        if (!this.parsed || this.doingLazyParse)
-            return;
-        this.doingLazyParse = true;
-        let j = 0;
-        let k = 0;
-        let prevPerc = 0;
-        this.stopLazyParse = false;
-        for (let i = 0; i < this.parsed.para.length; i++) {
-            j++;
-            if (j > 1) {
-                await utils.sleep(1);
-                j = 0;
-            }
-            if (this.stopLazyParse)
-                break;
-            this.parsed.parsePara(i);
-            k++;
-            if (k > 100) {
-                let perc = Math.round(i/this.parsed.para.length*100);
-                if (perc != prevPerc)
-                    this.drawStatusBar(`Обработка текста ${perc}%`);
-                prevPerc = perc;
-                k = 0;
-            }
-        }
-        this.drawStatusBar();
-        this.doingLazyParse = false;
-    }
-
     async refreshTime() {
         if (!this.timeRefreshing) {
             this.timeRefreshing = true;

+ 14 - 0
client/components/Reader/versionHistory.js

@@ -1,4 +1,18 @@
 export const versionHistory = [
+{
+    version: '1.1.0',
+    releaseDate: '2023-01-11',
+    showUntil: '2023-01-15',
+    content:
+`
+<ul>
+    <li>добавлена опция "Ночной режим" и кнопка на панель</li>
+    <li>исправление багов</li>
+</ul>
+
+`
+},
+
 {
     version: '1.0.0',
     releaseDate: '2022-12-18',

+ 1 - 1
client/components/share/Dialog.vue

@@ -1,6 +1,6 @@
 <template>
     <q-dialog v-model="active" no-route-dismiss @show="onShow" @hide="onHide">
-        <div class="column bg-white no-wrap">
+        <div class="column bg-dialog no-wrap">
             <div class="header row">
                 <div class="caption col row items-center q-ml-md">
                     <slot name="header"></slot>

+ 4 - 11
client/components/share/NumInput.vue

@@ -4,9 +4,9 @@
         outlined dense
         input-style="text-align: center"
         class="no-mp"
-        :class="(error ? 'error' : '')"
         :disable="disable"
         :mask="mask"
+        :error="error"
     >
         <slot></slot>
         <template #prepend>
@@ -236,23 +236,16 @@ export default vueComponent(NumInput);
     border-radius: 15px;
     width: 30px;
     height: 30px;
-    color: #bbb;
+    color: var(--text-ubtn-color);
     cursor: pointer;
 }
 
 .button:hover {
-    color: #616161;
-    background-color: #efebe9;
-}
-
-.error {
-    background-color: #ffabab;
-    border-radius: 3px;
+    filter: invert(100%);
 }
 
 .disable, .disable:hover {
     cursor: not-allowed;
-    color: #bbb;
-    background-color: white;
+    filter: invert(0%);
 }
 </style>

+ 5 - 5
client/components/share/StdDialog.vue

@@ -3,7 +3,7 @@
         <slot></slot>
 
         <!--------------------------------------------------->
-        <div v-show="type == 'alert'" class="bg-white no-wrap">
+        <div v-show="type == 'alert'" class="bg-dialog no-wrap">
             <div class="header row">
                 <div class="caption col row items-center q-ml-md">
                     <q-icon v-show="caption" class="q-mr-sm" :class="iconColor" :name="iconName" size="28px"></q-icon>
@@ -28,7 +28,7 @@
         </div>
 
         <!--------------------------------------------------->
-        <div v-show="type == 'confirm'" class="bg-white no-wrap">
+        <div v-show="type == 'confirm'" class="bg-dialog no-wrap">
             <div class="header row">
                 <div class="caption col row items-center q-ml-md">
                     <q-icon v-show="caption" class="q-mr-sm" :class="iconColor" :name="iconName" size="28px"></q-icon>
@@ -56,7 +56,7 @@
         </div>
 
         <!--------------------------------------------------->
-        <div v-show="type == 'askYesNo'" class="bg-white no-wrap">
+        <div v-show="type == 'askYesNo'" class="bg-dialog no-wrap">
             <div class="header row">
                 <div class="caption col row items-center q-ml-md">
                     <q-icon v-show="caption" class="q-mr-sm" :class="iconColor" :name="iconName" size="28px"></q-icon>
@@ -84,7 +84,7 @@
         </div>
 
         <!--------------------------------------------------->
-        <div v-show="type == 'prompt'" class="bg-white no-wrap">
+        <div v-show="type == 'prompt'" class="bg-dialog no-wrap">
             <div class="header row">
                 <div class="caption col row items-center q-ml-md">
                     <q-icon v-show="caption" class="q-mr-sm" :class="iconColor" :name="iconName" size="28px"></q-icon>
@@ -116,7 +116,7 @@
         </div>
 
         <!--------------------------------------------------->
-        <div v-show="type == 'hotKey'" class="bg-white no-wrap">
+        <div v-show="type == 'hotKey'" class="bg-dialog no-wrap">
             <div class="header row">
                 <div class="caption col row items-center q-ml-md">
                     <q-icon v-show="caption" class="q-mr-sm" :class="iconColor" :name="iconName" size="28px"></q-icon>

+ 3 - 3
client/components/share/Window.vue

@@ -148,14 +148,14 @@ export default vueComponent(Window);
 
 .window {
     margin: 10px;
-    background-color: #ffffff;
-    border: 3px double black;
+    background-color: var(--bg-app-color);
+    border: 3px double var(--text-app-color);
     border-radius: 4px;
     box-shadow: 3px 3px 5px black;
 }
 
 .header {
-    background: linear-gradient(to bottom right, #007000, #59B04F);
+    background: linear-gradient(to bottom right, var(--bg-header-color1), var(--bg-header-color2));
     align-items: center;
     height: 30px;
 }

+ 0 - 2
client/store/index.js

@@ -3,7 +3,6 @@ import { createStore } from 'vuex';
 import VuexPersistence from 'vuex-persist';
 
 import root from './root.js';
-import uistate from './modules/uistate';
 import config from './modules/config';
 import reader from './modules/reader';
 
@@ -13,7 +12,6 @@ const vuexLocal = new VuexPersistence();
 
 export default createStore(Object.assign({}, root, {
     modules: {
-        uistate,
         config,
         reader,
     },

+ 61 - 7
client/store/modules/reader.js

@@ -1,3 +1,4 @@
+import _ from 'lodash';
 import * as utils from '../../share/utils';
 import googleFonts from './fonts/fonts.json';
 
@@ -21,6 +22,7 @@ const readerActions = {
     'copyText': 'Скопировать текст со страницы',
     'convOptions': 'Настроить конвертирование',
     'refresh': 'Принудительно обновить книгу',
+    'nightMode': 'Ночной режим',
     'clickControl': 'Управление кликом',
     'offlineMode': 'Автономный режим (без интернета)',
     'contents': 'Оглавление/закладки',
@@ -57,6 +59,7 @@ const toolButtons = [
     {name: 'contents',    show: true},
     {name: 'libs',        show: true},
     {name: 'recentBooks', show: true},
+    {name: 'nightMode',   show: true},
     {name: 'clickControl', show: true},
     {name: 'offlineMode', show: true},
 ];
@@ -80,6 +83,7 @@ const hotKeys = [
     {name: 'contents', codes: ['C']},
     {name: 'libs', codes: ['L']},
     {name: 'recentBooks', codes: ['X']},
+    {name: 'nightMode',   codes: ['Equal']},
     {name: 'clickControl', codes: ['Ctrl+B']},
     {name: 'offlineMode', codes: ['O']},
 
@@ -157,6 +161,10 @@ const settingDefaults = {
     statusBarColorAlpha: 0.4,
     statusBarClickOpen: true,
 
+    nightMode: false, //ночной режим
+    dayColorSets: {},
+    nightColorSets: {},
+
     scrollingDelay: 3000,// замедление, ms
     scrollingType: 'ease-in-out', //linear, ease, ease-in, ease-out, ease-in-out
 
@@ -164,7 +172,6 @@ const settingDefaults = {
     pageChangeAnimationSpeed: 80, //0-100%
 
     allowUrlParamBookPos: false,
-    lazyParseEnabled: false,
     copyFullText: false,
     showClickMapPage: true,
     clickControl: true,
@@ -218,6 +225,8 @@ const diffExclude = [];
 for (const hotKey of hotKeys)
     diffExclude.push(`userHotKeys/${hotKey.name}`);
 diffExclude.push('userWallpapers');
+diffExclude.push('dayColorSets');
+diffExclude.push('nightColorSets');
 
 function addDefaultsToSettings(settings) {
     const diff = utils.getObjDiff(settings, settingDefaults, {exclude: diffExclude});
@@ -228,6 +237,33 @@ function addDefaultsToSettings(settings) {
     return false;
 }
 
+const colorSetsList = [
+    'textColor',
+    'backgroundColor',
+    'wallpaper',
+    'statusBarColorAsText',
+    'statusBarColor',
+    'statusBarColorAlpha',
+    'dualDivColorAsText',
+    'dualDivColor',
+    'dualDivColorAlpha',
+];
+
+function saveColorSets(nightMode, settings) {
+    const target = (nightMode ? settings.nightColorSets : settings.dayColorSets);
+    for (const prop of colorSetsList) {
+        target[prop] = settings[prop];
+    }
+}
+
+function restoreColorSets(nightMode, settings) {
+    const source = (nightMode ? settings.nightColorSets : settings.dayColorSets);
+    for (const prop of colorSetsList) {
+        if (utils.hasProp(source, prop))
+            settings[prop] = source[prop];
+    }
+}
+
 function getLibsDefaults(mode = 'reader') {
     const result = {
         startLink: '',
@@ -287,7 +323,7 @@ const state = {
     whatsNewContentHash: '',
     donationNextPopup: Date.now() + dayMs*30,
     currentProfile: '',
-    settings: Object.assign({}, settingDefaults),
+    settings: _.cloneDeep(settingDefaults),
     settingsRev: {},
     libs: false,
     libsRev: 0,
@@ -332,13 +368,31 @@ const mutations = {
         state.currentProfile = value;
     },
     setSettings(state, value) {
-        const newSettings = Object.assign({}, state.settings, value);
+        let newSettings = Object.assign({}, state.settings, value);
+
+        //при смене профиля подгружаются старые настройки, могут отсутствовать атрибуты
+        //поэтому:
         const added = addDefaultsToSettings(newSettings);
-        if (added) {
-            state.settings = added;
-        } else {
-            state.settings = newSettings;
+        if (added)
+            newSettings = added;
+
+        state.settings = newSettings;
+    },
+    nightModeToggle(state) {
+        //переключение режима день-ночь
+        const newSettings = Object.assign({}, state.settings);
+
+        saveColorSets(newSettings.nightMode, newSettings);
+        newSettings.nightMode = !newSettings.nightMode;
+
+        if (newSettings.nightMode && !utils.hasProp(newSettings.nightColorSets, 'textColor')) {
+            // Ночной режим активирован впервые. Цвета заданы по умолчанию.
+            newSettings.nightColorSets = {textColor: '#778a9e', backgroundColor: '#363131'};
         }
+
+        restoreColorSets(newSettings.nightMode, newSettings);
+
+        state.settings = newSettings;
     },
     setSettingsRev(state, value) {
         state.settingsRev = Object.assign({}, state.settingsRev, value);

+ 0 - 25
client/store/modules/uistate.js

@@ -1,25 +0,0 @@
-// initial state
-const state = {
-    asideBarCollapse: false,
-};
-
-// getters
-const getters = {};
-
-// actions
-const actions = {};
-
-// mutations
-const mutations = {
-    setAsideBarCollapse(state, value) {
-        state.asideBarCollapse = value;
-    },
-};
-
-export default {
-    namespaced: true,
-    state,
-    getters,
-    actions,
-    mutations
-};

+ 2 - 2
package-lock.json

@@ -1,12 +1,12 @@
 {
   "name": "liberama",
-  "version": "1.0.0",
+  "version": "1.1.0",
   "lockfileVersion": 2,
   "requires": true,
   "packages": {
     "": {
       "name": "liberama",
-      "version": "1.0.0",
+      "version": "1.1.0",
       "hasInstallScript": true,
       "license": "CC0-1.0",
       "dependencies": {

+ 1 - 1
package.json

@@ -1,6 +1,6 @@
 {
   "name": "liberama",
-  "version": "1.0.0",
+  "version": "1.1.0",
   "author": "Book Pauk <bookpauk@gmail.com>",
   "license": "CC0-1.0",
   "repository": "bookpauk/liberama",