diff --git a/cps.py b/cps.py index 039c2a5e1..85c061b04 100755 --- a/cps.py +++ b/cps.py @@ -42,9 +42,26 @@ def hide_console_windows(): if __name__ == '__main__': - if os.name == "nt": + # Only hide the console window on Windows when there is no interactive + # console attached. This preserves the ability to use Ctrl+C from a + # terminal (stdin is a tty) to stop the server. + if os.name == "nt" and not sys.stdin.isatty(): hide_console_windows() - main() + + try: + main() + except KeyboardInterrupt: + # Allow stopping the server with Ctrl+C from an interactive console, after reboot of calibre-web + # (initally the SIGINT signal CTRL+C is catched in server.py via the signal callback). + try: + # Try to perform a graceful stop if web_server is available. + # Import here to avoid import-time side-effects when not needed. + from cps import web_server + web_server.stop() + except Exception: + pass + print('\nCalibre-Web: received interrupt, shutting down') + sys.exit(0) diff --git a/cps/constants.py b/cps/constants.py index ce38e6127..21fecebea 100644 --- a/cps/constants.py +++ b/cps/constants.py @@ -37,9 +37,12 @@ STATIC_DIR = os.path.join(BASE_DIR, 'cps', 'static') TEMPLATES_DIR = os.path.join(BASE_DIR, 'cps', 'templates') TRANSLATIONS_DIR = os.path.join(BASE_DIR, 'cps', 'translations') -# Cache dir - use CACHE_DIR environment variable, otherwise use the default directory: cps/cache +# Cache dir - use CACHE_DIRECTORY environment variable, otherwise CACHE_DIR (for backwards compatibility), +# otherwise use the default directory: cps/cache DEFAULT_CACHE_DIR = os.path.join(BASE_DIR, 'cps', 'cache') -CACHE_DIR = os.environ.get('CACHE_DIR', DEFAULT_CACHE_DIR) +CACHE_DIRECTORY = os.environ.get( + 'CACHE_DIRECTORY', os.environ.get('CACHE_DIR', DEFAULT_CACHE_DIR) +) if HOME_CONFIG: home_dir = os.path.join(os.path.expanduser("~"), ".calibre-web") diff --git a/cps/error_handler.py b/cps/error_handler.py index 0df983329..54942ec22 100644 --- a/cps/error_handler.py +++ b/cps/error_handler.py @@ -33,6 +33,7 @@ log = logger.create() # custom error page def error_http(error): + headers = {'WWW-Authenticate': 'Basic realm="calibre-web"'} if error.code == 401 else {} return render_template('http_error.html', error_code="Error {0}".format(error.code), error_name=error.name, @@ -40,7 +41,7 @@ def error_http(error): goto_admin=False, unconfigured=not config.db_configured, instance=config.config_calibre_web_title - ), error.code + ), error.code, headers def internal_error(error): diff --git a/cps/fs.py b/cps/fs.py index 996499c3e..1bee86a34 100644 --- a/cps/fs.py +++ b/cps/fs.py @@ -17,7 +17,7 @@ # along with this program. If not, see . from . import logger -from .constants import CACHE_DIR +from .constants import CACHE_DIRECTORY from os import makedirs, remove from os.path import isdir, isfile, join from shutil import rmtree @@ -25,7 +25,7 @@ from shutil import rmtree class FileSystem: _instance = None - _cache_dir = CACHE_DIR + _cache_dir = CACHE_DIRECTORY def __new__(cls): if cls._instance is None: diff --git a/cps/opds.py b/cps/opds.py index e8a405116..329a6a0e5 100644 --- a/cps/opds.py +++ b/cps/opds.py @@ -21,7 +21,6 @@ # along with this program. If not, see . import datetime -# import json from urllib.parse import unquote_plus from flask import Blueprint, request, render_template, make_response, abort, g, jsonify diff --git a/cps/render_template.py b/cps/render_template.py index bf0356d0d..0f0a2894c 100644 --- a/cps/render_template.py +++ b/cps/render_template.py @@ -92,7 +92,7 @@ def get_sidebar_config(kwargs=None): "visibility": constants.SIDEBAR_FORMAT, 'public': True, "page": "format", "show_text": _('Show File Formats Section'), "config_show": True}) sidebar.append( - {"glyph": "glyphicon-trash", "text": _('Archived Books'), "link": 'web.books_list', "id": "archived", + {"glyph": "glyphicon-folder-open", "text": _('Archived Books'), "link": 'web.books_list', "id": "archived", "visibility": constants.SIDEBAR_ARCHIVED, 'public': (not current_user.is_anonymous), "page": "archived", "show_text": _('Show Archived Books'), "config_show": content}) if not simple: diff --git a/cps/server.py b/cps/server.py index dca407a7e..bf53a3d46 100644 --- a/cps/server.py +++ b/cps/server.py @@ -216,6 +216,12 @@ class WebServer(object): try: sock, output = self._make_gevent_listener() log.info('Starting Gevent server on %s', output) + # Also print to stdout so interactive terminals show a clear success message + try: + print(f"Calibre-Web: server started on {output}") + except Exception: + print(f"Calibre-Web: error {output}") + pass self.wsgiserver = WSGIServer(sock, self.app, log=self.access_logger, handler_class=MyWSGIHandler, error_log=log, spawn=Pool(), **ssl_args) @@ -266,6 +272,12 @@ class WebServer(object): output = _readable_listen_address(self.listen_address, self.listen_port) http_server.listen(self.listen_port, self.listen_address) log.info('Starting Tornado server on %s', output) + # Also print to stdout so interactive terminals show a clear success message + try: + print(f"Calibre-Web: server started on {output}") + except Exception: + print(f"Calibre-Web: error {output}") + pass self.wsgiserver = IOLoop.current() self.wsgiserver.start() diff --git a/cps/static/css/caliBlur.css b/cps/static/css/caliBlur.css index c5e96bf43..cc22ae2c7 100644 --- a/cps/static/css/caliBlur.css +++ b/cps/static/css/caliBlur.css @@ -2018,7 +2018,7 @@ body > div.container-fluid > div > div.col-sm-10 > div.pagination > .page-previo display: none } -body.authorlist > div.container-fluid > div > div.col-sm-10 > div.container > div.col-xs-12, body.catlist > div.container-fluid > div > div.col-sm-10 > div.container > div.col-xs-12, body.langlist > div.container-fluid > div > div.col-sm-10 > div.container > div.col-sm-6, body.serieslist > div.container-fluid > div > div.col-sm-10 > div.container > div.col-sm-6 { +body.authorlist > div.container-fluid > div > div.col-sm-10 > div.container1 > div.col-xs-12, body.downloadlist > div.container-fluid > div > div.col-sm-10 > div.container1 > div.col-xs-12, body.catlist > div.container-fluid > div > div.col-sm-10 > div.container1 > div.col-xs-12, body.langlist > div.container-fluid > div > div.col-sm-10 > div.container1 > div.col-sm-6, body.serieslist > div.container-fluid > div > div.col-sm-10 > div.container1 > div.col-sm-6 { width: 100% } @@ -2042,7 +2042,7 @@ body.serieslist > div.container-fluid > div > div.col-sm-10:before { } } -body > div.container-fluid > div > div.col-sm-10 > div.container { +body > div.container-fluid > div > div.col-sm-10 > div.container1 { width: calc(80% - 30px); margin: 120px 0 30px 20%; border-top: 2px solid rgba(0, 0, 0, .3); @@ -2051,19 +2051,19 @@ body > div.container-fluid > div > div.col-sm-10 > div.container { max-width: calc(80% - 30px) } -body > div.container-fluid > div > div.col-sm-10 > div.container > div { +body > div.container-fluid > div > div.col-sm-10 > div.container1 > div { padding: 0 } -body > div.container-fluid > div > div.col-sm-10 > div.container > div > .row { +body > div.container-fluid > div > div.col-sm-10 > div.container1 > div > .row { padding: 4px 0 } -body > div.container-fluid > div > div.col-sm-10 > div.container > div > .row:nth-of-type(even) { +body > div.container-fluid > div > div.col-sm-10 > div.container1 > div > .row:nth-of-type(even) { background: hsla(0, 0%, 100%, .02) } -body > div.container-fluid > div > div.col-sm-10 > div.container > div > .row > .col-xs-6 { +body > div.container-fluid > div > div.col-sm-10 > div.container1 > div > .row > .col-xs-6 { width: calc(91.666% - 30px); overflow: hidden; -o-text-overflow: ellipsis; @@ -2072,7 +2072,7 @@ body > div.container-fluid > div > div.col-sm-10 > div.container > div > .row > padding-left: 60px } -body > div.container-fluid > div > div.col-sm-10 > div.container > div > .row > .col-xs-6 > a { +body > div.container-fluid > div > div.col-sm-10 > div.container1 > div > .row > .col-xs-6 > a { -o-text-overflow: ellipsis; text-overflow: ellipsis; white-space: nowrap; @@ -2082,7 +2082,7 @@ body > div.container-fluid > div > div.col-sm-10 > div.container > div > .row > } -body > div.container-fluid > div > div.col-sm-10 > div.discover > div.container { +body > div.container-fluid > div > div.col-sm-10 > div.discover > div.container1 { width: calc(80% - 30px); margin: 120px 0 30px 20%; border-top: 2px solid rgba(0, 0, 0, .3); @@ -2091,19 +2091,19 @@ body > div.container-fluid > div > div.col-sm-10 > div.discover > div.container max-width: calc(80% - 30px) } -body > div.container-fluid > div > div.col-sm-10 > div.discover > div.container > div { +body > div.container-fluid > div > div.col-sm-10 > div.discover > div.container1 > div { padding: 0 } -body > div.container-fluid > div > div.col-sm-10 > div.discover > div.container > div > .row { +body > div.container-fluid > div > div.col-sm-10 > div.discover > div.container1 > div > .row { padding: 4px 0 } -body > div.container-fluid > div > div.col-sm-10 > div.discover > div.container > div > .row:nth-of-type(even) { +body > div.container-fluid > div > div.col-sm-10 > div.discover > div.container1 > div > .row:nth-of-type(even) { background: hsla(0, 0%, 100%, .02) } -body > div.container-fluid > div > div.col-sm-10 > div.discover > div.container > div > .row > .col-xs-6 { +body > div.container-fluid > div > div.col-sm-10 > div.discover > div.container1 > div > .row > .col-xs-6 { width: calc(91.666% - 30px); overflow: hidden; -o-text-overflow: ellipsis; @@ -2112,7 +2112,7 @@ body > div.container-fluid > div > div.col-sm-10 > div.discover > div.container padding-left: 60px } -body > div.container-fluid > div > div.col-sm-10 > div.discover > div.container > div > .row > .col-xs-6 > a { +body > div.container-fluid > div > div.col-sm-10 > div.discover > div.container1 > div > .row > .col-xs-6 > a { -o-text-overflow: ellipsis; text-overflow: ellipsis; white-space: nowrap; @@ -2146,7 +2146,6 @@ body.catlist > div.container-fluid > div.row-fluid > div.col-sm-10:before { } } - body.authorlist > div.container-fluid > div.row-fluid > div.col-sm-10:before, body.langlist > div.container-fluid > div > div.col-sm-10:before { font-family: 'Glyphicons Halflings', serif; font-style: normal; @@ -2172,10 +2171,12 @@ body.authorlist > div.container-fluid > div.row-fluid > div.col-sm-10:before { } body.langlist > div.container-fluid > div > div.col-sm-10:before { - content: "\e034" + content: "\e034"; + top: 240px; } -body.authorlist > div.container-fluid > div > div.col-sm-10 > div.container:before, body.catlist > div.container-fluid > div > div.col-sm-10 > div.container:before, body.langlist > div.container-fluid > div > div.col-sm-10 > div.container:before, body.me > div.container-fluid > div > div.col-sm-10 > div.discover:before, body.serieslist > div.container-fluid > div > div.col-sm-10 > div.container:before { + +body.authorlist > div.container-fluid > div > div.col-sm-10 > div.container1:before, body.downloadlist > div.container-fluid > div > div.col-sm-10 > div.container1:before, body.catlist > div.container-fluid > div > div.col-sm-10 > div.container1:before, body.langlist > div.container-fluid > div > div.col-sm-10 > div.container1:before, body.me > div.container-fluid > div > div.col-sm-10 > div.discover:before, body.serieslist > div.container-fluid > div > div.col-sm-10 > div.container1:before { top: 60px; font-size: 24px; color: #eee; @@ -2186,63 +2187,104 @@ body.authorlist > div.container-fluid > div > div.col-sm-10 > div.container:befo font-weight: 400 } -body.authorlist > div.container-fluid > div > div.col-sm-10 > div.container:before { +body.authorlist > div.container-fluid > div > div.col-sm-10 > div.container1:before { content: "Authors"; top: 115px; + display: table; } @media screen and (max-width: 992px) { - body.authorlist > div.container-fluid > div > div.col-sm-10 > div.container:before { + body.authorlist > div.container-fluid > div > div.col-sm-10 > div.container1:before { top: 60px; } } -body.catlist > div.container-fluid > div > div.col-sm-10 > div.container:before { +@media screen and (max-width: 992px) { + body.downloadlist > div.container-fluid > div > div.col-sm-10 > div.container1:before { + top: 60px; + } +} + +body.downloadlist > div.container-fluid > div.row-fluid > div.col-sm-10:before { + font-family: Glyphicons Halflings, serif; + content: "\E026"; + font-style: normal; + font-weight: 400; + line-height: 1; + font-size: 6vw; + position: fixed; + left: 240px; + top: 240px; + width: calc(20% - 55px); + text-align: center +} + + +@media screen and (max-width: 992px) { + body.downloadlist > div.container-fluid > div.row-fluid > div.col-sm-10:before { + top: 180px; + } +} + +body.downloadlist > div.container-fluid > div > div.col-sm-10 > div.container1:before { + content: "Downloads"; + top: 115px; + display: table; +} + + +body.catlist > div.container-fluid > div > div.col-sm-10 > div.container1:before { content: "Categories"; top: 115px; + display: table; } @media screen and (max-width: 992px) { - body.catlist > div.container-fluid > div > div.col-sm-10 > div.container:before { + body.catlist > div.container-fluid > div > div.col-sm-10 > div.container1:before { top: 60px; } } -body.serieslist > div.container-fluid > div > div.col-sm-10 > div.container:before { +body.serieslist > div.container-fluid > div > div.col-sm-10 > div.container1:before { content: "Series"; top: 115px; + display: table; } @media screen and (max-width: 992px) { - body.serieslist > div.container-fluid > div > div.col-sm-10 > div.container:before { + body.serieslist > div.container-fluid > div > div.col-sm-10 > div.container1:before { top: 60px; } } -body.ratingslist > div.container-fluid > div > div.col-sm-10 > div.container:before { +body.ratingslist > div.container-fluid > div > div.col-sm-10 > div.container1:before { content: "File formats"; top: 115px; + display: table; } @media screen and (max-width: 992px) { - body.ratingslist > div.container-fluid > div > div.col-sm-10 > div.container:before { + body.ratingslist > div.container-fluid > div > div.col-sm-10 > div.container1:before { top: 60px; } } -body.formatslist > div.container-fluid > div > div.col-sm-10 > div.container:before { +body.formatslist > div.container-fluid > div > div.col-sm-10 > div.container1:before { content: "File formats"; top: 115px; + display: table; } @media screen and (max-width: 992px) { - body.formatslist > div.container-fluid > div > div.col-sm-10 > div.container:before { + body.formatslist > div.container-fluid > div > div.col-sm-10 > div.container1:before { top: 60px; } } -body.langlist > div.container-fluid > div > div.col-sm-10 > div.container:before { - content: "Languages" +body.langlist > div.container-fluid > div > div.col-sm-10 > div.container1:before { + content: "Languages"; + top: 115px; + display: table; } body.advsearch > div.container-fluid > div > div.col-sm-10 > div.col-md-10.col-lg-6 { @@ -6421,7 +6463,7 @@ body.edituser.admin > div.container-fluid > div.row-fluid > div.col-sm-10 > div. display: none !important } - .shelforder > .container-fluid > .row-fluid > .col-sm-10 > div.col-sm-6.col-lg-6.col-xs-6 > h2:before, body.advsearch > div.container-fluid > div > div.col-sm-10:before, body.authorlist > div.container-fluid > div > div.col-sm-10 > div.container:before, body.catlist > div.container-fluid > div > div.col-sm-10 > div.container:before, body.langlist > div.container-fluid > div > div.col-sm-10 > div.container:before, body.login .navbar > .container-fluid > .navbar-header > button.navbar-toggle, body.serieslist > div.container-fluid > div > div.col-sm-10 > div.container:before { + .shelforder > .container-fluid > .row-fluid > .col-sm-10 > div.col-sm-6.col-lg-6.col-xs-6 > h2:before, body.advsearch > div.container-fluid > div > div.col-sm-10:before, body.authorlist > div.container-fluid > div > div.col-sm-10 > div.container1:before, body.catlist > div.container-fluid > div > div.col-sm-10 > div.container1:before, body.langlist > div.container-fluid > div > div.col-sm-10 > div.container1:before, body.downloadlist > div.container-fluid > div > div.col-sm-10 > div.container1:before, body.login .navbar > .container-fluid > .navbar-header > button.navbar-toggle, body.serieslist > div.container-fluid > div > div.col-sm-10 > div.container1:before { display: none } @@ -6527,7 +6569,7 @@ body.edituser.admin > div.container-fluid > div.row-fluid > div.col-sm-10 > div. background-size: 30px } - body.advanced_search > div.container-fluid > div > div.col-sm-10 > div.col-sm-8:before, body.author:not(.authorlist) .undefined-img:before, body.authorlist > div.container-fluid > div.row-fluid > div.col-sm-10:before, body.catlist > div.container-fluid > div.row-fluid > div.col-sm-10:before, body.langlist > div.container-fluid > div > div.col-sm-10:before, body.ratingslist > div.container-fluid > div > div.col-sm-10:before, body.serieslist > div.container-fluid > div > div.col-sm-10:before, body.shelforder > div.container-fluid > div.row-fluid > div.col-sm-10:before { + body.advanced_search > div.container-fluid > div > div.col-sm-10 > div.col-sm-8:before, body.author:not(.authorlist) .undefined-img:before, body.authorlist > div.container-fluid > div.row-fluid > div.col-sm-10:before, body.catlist > div.container-fluid > div.row-fluid > div.col-sm-10:before, body.downloadlist > div.container-fluid > div.row-fluid > div.col-sm-10:before, body.langlist > div.container-fluid > div > div.col-sm-10:before, body.ratingslist > div.container-fluid > div > div.col-sm-10:before, body.serieslist > div.container-fluid > div > div.col-sm-10:before, body.shelforder > div.container-fluid > div.row-fluid > div.col-sm-10:before { position: fixed; top: 62px; right: 23px; @@ -6547,7 +6589,7 @@ body.edituser.admin > div.container-fluid > div.row-fluid > div.col-sm-10 > div. border-top: 1px solid hsla(0, 0%, 78%, .15) } - body > div.container-fluid > div > div.col-sm-10 > div.container { + body > div.container-fluid > div > div.col-sm-10 > div.container1 { left: 0; width: auto; margin: 0 !important; @@ -6674,7 +6716,7 @@ body.edituser.admin > div.container-fluid > div.row-fluid > div.col-sm-10 > div. margin-bottom: 5px } - body > div.container-fluid > div > div.col-sm-10 > div.container > div > .row { + body > div.container-fluid > div > div.col-sm-10 > div.container1 > div > .row { padding: 0 } @@ -7441,11 +7483,11 @@ body.edituser.admin > div.container-fluid > div.row-fluid > div.col-sm-10 > div. text-decoration: none } - body > div.container-fluid > div > div.col-sm-10 > div.container > div > .row:nth-of-type(even) { + body > div.container-fluid > div > div.col-sm-10 > div.container1 > div > .row:nth-of-type(even) { background: 0 0 } - body > div.container-fluid > div > div.col-sm-10 > div.container > div > .row { + body > div.container-fluid > div > div.col-sm-10 > div.container1 > div > .row { border-bottom: 1px solid hsla(0, 0%, 78%, .15) } @@ -7600,7 +7642,7 @@ body.formatslist > div.container-fluid > div > div.col-sm-10:before { } } -body.publisherlist > div.container-fluid > div > div.col-sm-10 > div.container:before { +body.publisherlist > div.container-fluid > div > div.col-sm-10 > div.container1:before { content: "Publishers"; top: 115px; font-size: 24px; @@ -7608,17 +7650,18 @@ body.publisherlist > div.container-fluid > div > div.col-sm-10 > div.container:b line-height: 1; padding-left: 15px; position: absolute; + display: table; font-family: Open Sans Bold, Helvetica Neue, Helvetica, Arial, sans-serif; font-weight: 400 } @media screen and (max-width: 992px) { - body.publisherlist > div.container-fluid > div > div.col-sm-10 > div.container:before { + body.publisherlist > div.container-fluid > div > div.col-sm-10 > div.container1:before { top: 60px; } } -body.ratingslist > div.container-fluid > div > div.col-sm-10 > div.container:before { +body.ratingslist > div.container-fluid > div > div.col-sm-10 > div.container1:before { content: "Ratings"; top: 115px; font-size: 24px; @@ -7626,23 +7669,24 @@ body.ratingslist > div.container-fluid > div > div.col-sm-10 > div.container:bef line-height: 1; padding-left: 15px; position: absolute; + display: table; font-family: Open Sans Bold, Helvetica Neue, Helvetica, Arial, sans-serif; font-weight: 400 } @media screen and (max-width: 992px) { - body.ratingslist > div.container-fluid > div > div.col-sm-10 > div.container:before { + body.ratingslist > div.container-fluid > div > div.col-sm-10 > div.container1:before { top: 60px; } } @media screen and (max-width: 768px) { - body.ratingslist > div.container-fluid > div > div.col-sm-10 > div.container:before { + body.ratingslist > div.container-fluid > div > div.col-sm-10 > div.container1:before { display: none; } } -body.formatslist > div.container-fluid > div > div.col-sm-10 > div.container:before { +body.formatslist > div.container-fluid > div > div.col-sm-10 > div.container1:before { content: "File formats"; top: 115px; font-size: 24px; @@ -7655,18 +7699,18 @@ body.formatslist > div.container-fluid > div > div.col-sm-10 > div.container:bef } @media screen and (max-width: 992px) { - body.formatslist > div.container-fluid > div > div.col-sm-10 > div.container:before { + body.formatslist > div.container-fluid > div > div.col-sm-10 > div.container1:before { top: 60px; } } @media screen and (max-width: 768px) { - body.formatslist > div.container-fluid > div > div.col-sm-10 > div.container:before { + body.formatslist > div.container-fluid > div > div.col-sm-10 > div.container1:before { display: none; } } -body.publisherlist > div.container-fluid > div > div.col-sm-10 > div.container > div.col-sm-6 { +body.publisherlist > div.container-fluid > div > div.col-sm-10 > div.container1 > div.col-sm-6 { width: 100% } @@ -7705,7 +7749,7 @@ body.publisherlist > div.container-fluid > div > div.col-sm-10 > div.container > color: hsla(0, 0%, 100%, .7) } - body.publisherlist > div.container-fluid > div > div.col-sm-10 > div.container:before { + body.publisherlist > div.container-fluid > div > div.col-sm-10 > div.container1:before { display: none !important } } diff --git a/cps/static/css/epub_themes.css b/cps/static/css/epub_themes.css index b9cbcedbe..209e65515 100644 --- a/cps/static/css/epub_themes.css +++ b/cps/static/css/epub_themes.css @@ -13,6 +13,11 @@ color: #000; } +.amberTheme { + background: #e8bf5e; + color: #000; +} + .blackTheme { background: #000; color: #fff; diff --git a/cps/static/css/main.css b/cps/static/css/main.css index 571f546c9..160735b82 100644 --- a/cps/static/css/main.css +++ b/cps/static/css/main.css @@ -1,11 +1,10 @@ @font-face { - font-family: 'fontello'; - src: url('fonts/fontello.eot?60518104'); - src: - url('fonts/fontello.eot?60518104#iefix') format('embedded-opentype'), - url('fonts/fontello.woff?60518104') format('woff'), - url('fonts/fontello.ttf?60518104') format('truetype'), - url('fonts/fontello.svg?60518104#fontello') format('svg'); + font-family: "fontello"; + src: url("fonts/fontello.eot?60518104"); + src: url("fonts/fontello.eot?60518104#iefix") format("embedded-opentype"), + url("fonts/fontello.woff?60518104") format("woff"), + url("fonts/fontello.ttf?60518104") format("truetype"), + url("fonts/fontello.svg?60518104#fontello") format("svg"); font-weight: normal; font-style: normal; } @@ -16,7 +15,7 @@ body { } .myselect { - overflow: visible !important; + overflow: visible !important; } #main { @@ -48,7 +47,7 @@ body { -webkit-transition: opacity 0.5s; -moz-transition: opacity 0.5s; transition: opacity 0.5s; - z-index: 10; + z-index: 100; } #titlebar:hover { @@ -93,14 +92,18 @@ body { box-shadow: inset 0 0 6px rgba(155, 155, 155, 0.8); } -#book-title { font-weight: 600; } -#title-seperator { display: none; } +#book-title { + font-weight: 600; +} +#title-seperator { + display: none; +} #viewer { width: 80%; height: 80%; margin: 0 auto; - z-index: 2; + z-index: 0; position: relative; overflow: hidden; } @@ -136,6 +139,7 @@ body { user-select: none; padding-top: 160px; padding-bottom: 160px; + z-index: 10; } .arrow:hover { @@ -200,10 +204,16 @@ body { max-width: 80%; } -#title-controls { float: right; } +#title-controls { + float: right; +} -#panels a::before { visibility: visible; } -#panels a:hover { color: #aaa; } +#panels a::before { + visibility: visible; +} +#panels a:hover { + color: #aaa; +} .list_item.currentChapter > a, .list_item a:hover { @@ -237,8 +247,12 @@ body { margin-top: -1px; } -input::-webkit-input-placeholder { color: #454545; } -input:-moz-placeholder { color: #454545; } +input::-webkit-input-placeholder { + color: #454545; +} +input:-moz-placeholder { + color: #454545; +} #divider { position: absolute; @@ -378,14 +392,14 @@ input:-moz-placeholder { color: #454545; } } .toc_toggle::before { - content: '▸'; + content: "▸"; color: #fff; margin-right: -4px; } .currentChapter > .toc_toggle::before, .openChapter > .toc_toggle::before { - content: '▾'; + content: "▾"; } .view { @@ -499,21 +513,33 @@ input:-moz-placeholder { color: #454545; } list-style-type: none; } -#settingsPanel .xsmall { font-size: x-small; } -#settingsPanel .small { font-size: small; } -#settingsPanel .medium { font-size: medium; } -#settingsPanel .large { font-size: large; } -#settingsPanel .xlarge { font-size: x-large; } +#settingsPanel .xsmall { + font-size: x-small; +} +#settingsPanel .small { + font-size: small; +} +#settingsPanel .medium { + font-size: medium; +} +#settingsPanel .large { + font-size: large; +} +#settingsPanel .xlarge { + font-size: x-large; +} -.highlight { background-color: yellow; } +.highlight { + background-color: yellow; +} .modal { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); - width: 100vw; - height: auto; + width: 97vw; + height: 90dvh; max-width: 630px; z-index: 2000; visibility: hidden; @@ -539,7 +565,7 @@ input:-moz-placeholder { color: #454545; } } .md-show ~ .overlay { - opacity: 1; + /* opacity: 1; */ visibility: visible; } @@ -554,7 +580,7 @@ input:-moz-placeholder { color: #454545; } } .md-content > div { - padding: 15px 40px 30px; + padding: 10px 20px; margin: 0; font-weight: 300; font-size: 14px; @@ -581,6 +607,7 @@ input:-moz-placeholder { color: #454545; } color: white; } +.md-content .themes button.lightTheme, .md-content .themes button.whiteTheme { background-color: white; color: black; @@ -591,6 +618,11 @@ input:-moz-placeholder { color: #454545; } color: black; } +.md-content .themes button.amberTheme { + background-color: #e8bf5e; + color: black; +} + .md-content .themes button.blackTheme { background-color: black; color: white; @@ -638,31 +670,47 @@ input:-moz-placeholder { color: #454545; } @media only screen and (max-width: 900px) { #viewer { - width: 60%; - margin-left: 20%; + width: 100%; + height: calc(100dvh - 99px); + margin-left: 0; + } + + #viewer iframe { + width: 100%; + height: 100%; } #prev { left: 20px; + z-index: 10; } #next { right: 20px; + z-index: 10; } } @media only screen and (max-width: 550px) { #viewer { - width: 80%; - margin-left: 10%; + width: 100%; + height: calc(100dvh - 99px); + margin-left: 0; + } + + #viewer iframe { + width: 100%; + height: 100%; } #prev { left: 0; + z-index: 10; } #next { right: 0; + z-index: 10; } .arrow { @@ -712,11 +760,7 @@ input:-moz-placeholder { color: #454545; } } } -@media only screen - and (min-device-width: 768px) - and (max-device-width: 1024px) - and (orientation: landscape) - /* and (-webkit-min-device-pixel-ratio: 2)*/ { +@media only screen and (min-device-width: 768px) and (max-device-width: 1024px) and (orientation: landscape) /* and (-webkit-min-device-pixel-ratio: 2) */ { #viewer { width: 80%; margin-left: 10%; @@ -739,47 +783,52 @@ input:-moz-placeholder { color: #454545; } /* For iPhone 6\6s portrait layouts only */ @media only screen and (min-device-width: 375px) and (max-device-width: 667px) and (orientation: portrait) { #viewer { - width: 300px; - height: 480px; + width: 100%; + height: calc(100dvh - 99px); } #viewer iframe { - width: 300px; - height: 480px; + width: 100%; + height: 100%; } } /* For iPhone 6\6s landscape layouts only */ @media only screen and (min-device-width: 375px) and (max-device-width: 667px) and (orientation: landscape) { #viewer { - width: 450px; - height: 300px; + width: 100%; + height: calc(100dvh - 99px); } #viewer iframe { - width: 450px; - height: 300px; + width: 100%; + height: 100%; } } /* For iPhone portrait layouts only */ @media only screen and (max-device-width: 374px) and (orientation: portrait) { #viewer { - width: 256px; - height: 432px; + width: 100%; + height: calc(100dvh - 99px); } #viewer iframe { - width: 256px; - height: 432px; + width: 100%; + height: 100%; } } /* For iPhone landscape layouts only */ @media only screen and (max-device-width: 374px) and (orientation: landscape) { + #viewer { + width: 100%; + height: calc(100dvh - 99px); + } + #viewer iframe { - width: 256px; - height: 124px; + width: 100%; + height: 100%; } } @@ -803,32 +852,69 @@ input:-moz-placeholder { color: #454545; } font-size: 112%; } -.icon-search::before { content: '\e807'; } /* '' */ -.icon-resize-full-1::before { content: '\e804'; } /* '' */ -.icon-cancel-circled2::before { content: '\e80f'; } /* '' */ -.icon-link::before { content: '\e80d'; } /* '' */ -.icon-bookmark::before { content: '\e805'; } /* '' */ -.icon-bookmark-empty::before { content: '\e806'; } /* '' */ -.icon-download-cloud::before { content: '\e811'; } /* '' */ -.icon-edit::before { content: '\e814'; } /* '' */ -.icon-menu::before { content: '\e802'; } /* '' */ -.icon-cog::before { content: '\e813'; } /* '' */ -.icon-resize-full::before { content: '\e812'; } /* '' */ -.icon-cancel-circled::before { content: '\e80e'; } /* '' */ -.icon-up-dir::before { content: '\e80c'; } /* '' */ -.icon-right-dir::before { content: '\e80b'; } /* '' */ -.icon-angle-right::before { content: '\e809'; } /* '' */ -.icon-angle-down::before { content: '\e80a'; } /* '' */ -.icon-right::before { content: '\e815'; } /* '' */ -.icon-list-1::before { content: '\e803'; } /* '' */ -.icon-list-numbered::before { content: '\e801'; } /* '' */ -.icon-columns::before { content: '\e810'; } /* '' */ -.icon-list::before { content: '\e800'; } /* '' */ -.icon-resize-small::before { content: '\e808'; } /* '' */ - -#progress{ - right: 4rem; - bottom: 4rem; - width: fit-content; - position: absolute; -} +.icon-search::before { + content: "\e807"; +} /* '' */ +.icon-resize-full-1::before { + content: "\e804"; +} /* '' */ +.icon-cancel-circled2::before { + content: "\e80f"; +} /* '' */ +.icon-link::before { + content: "\e80d"; +} /* '' */ +.icon-bookmark::before { + content: "\e805"; +} /* '' */ +.icon-bookmark-empty::before { + content: "\e806"; +} /* '' */ +.icon-download-cloud::before { + content: "\e811"; +} /* '' */ +.icon-edit::before { + content: "\e814"; +} /* '' */ +.icon-menu::before { + content: "\e802"; +} /* '' */ +.icon-cog::before { + content: "\e813"; +} /* '' */ +.icon-resize-full::before { + content: "\e812"; +} /* '' */ +.icon-cancel-circled::before { + content: "\e80e"; +} /* '' */ +.icon-up-dir::before { + content: "\e80c"; +} /* '' */ +.icon-right-dir::before { + content: "\e80b"; +} /* '' */ +.icon-angle-right::before { + content: "\e809"; +} /* '' */ +.icon-angle-down::before { + content: "\e80a"; +} /* '' */ +.icon-right::before { + content: "\e815"; +} /* '' */ +.icon-list-1::before { + content: "\e803"; +} /* '' */ +.icon-list-numbered::before { + content: "\e801"; +} /* '' */ +.icon-columns::before { + content: "\e810"; +} /* '' */ +.icon-list::before { + content: "\e800"; +} /* '' */ +.icon-resize-small::before { + content: "\e808"; +} /* '' */ diff --git a/cps/static/css/reader.css b/cps/static/css/reader.css index 7229a6e3b..e43611796 100644 --- a/cps/static/css/reader.css +++ b/cps/static/css/reader.css @@ -1,47 +1,81 @@ .fontSizeWrapper { - position: relative; + position: relative; } .slider { - position: absolute; - top: 50%; - transform: translate(0,-50%); - width: 90%; - height: 60px; - background: transparent; - border-radius: 20px; - display: flex; - align-items: center; - box-shadow: 0px 15px 40px #7E6D5766; + position: absolute; + top: 50%; + transform: translate(0, -50%); + width: 90%; + height: 60px; + background: transparent; + border-radius: 20px; + display: flex; + align-items: center; + box-shadow: 0px 15px 40px #7e6d5766; } .slider label { - font-size: 20px; - font-weight: 400; - font-family: Open Sans; - padding-right: 10px; - color: white; + font-size: 20px; + font-weight: 400; + font-family: Open Sans; + padding-right: 10px; + color: white; } .slider input[type="range"] { - width: 80%; - height: 5px; - background: black; - border: none; - outline: none; + width: 80%; + height: 5px; + background: black; + border: none; + outline: none; } .item { - font-size: 20px; - font-weight: 400; - font-family: Open Sans; - padding-right: 10px; - color: white; + font-size: 14px; + font-weight: 600; + font-family: Open Sans; + padding-right: 10px; + color: white; } -.item~button { - display: inline-block; - border: none; - text-align: center; - text-decoration: none; - margin-top: 5%; - margin-right: 1%; - font-size: 16px; -} \ No newline at end of file +.item ~ button { + display: inline-block; + border: none; + text-align: center; + text-decoration: none; + margin-top: 5%; + margin-right: 1%; + font-size: 16px; +} + +#main, +#viewer, +#sidebar, +#tocView { + touch-action: pan-y; +} + +.read-footer { + display: flex; + justify-content: space-between; + align-items: center; + padding: 0 12px; +} +.read-footer #pages-count { + font-size: 12px; + + position: absolute; + left: 12px; + bottom: 2px; +} + +#back-button { + width: 50px !important; + right: 50px; + // position: absolute; +} + +#progress { + right: 4rem; + bottom: 2px; + width: fit-content; + position: absolute; +} diff --git a/cps/static/css/style.css b/cps/static/css/style.css index 29028b366..51a70d5f4 100644 --- a/cps/static/css/style.css +++ b/cps/static/css/style.css @@ -429,6 +429,9 @@ div.log { padding: 0.5em; } +.badge-sm{ + font-size: 70%; +} #detailcover { cursor:zoom-in; } #detailcover:-webkit-full-screen { cursor:zoom-out; border: 0; } #detailcover:-moz-full-screen { cursor:zoom-out; border: 0; } @@ -477,4 +480,4 @@ body > div.container-fluid > div.row-fluid > div.col-sm-2::-webkit-scrollbar { body > div.container-fluid { padding-right: 0px; -} \ No newline at end of file +} diff --git a/cps/static/js/libs/pickr/pickr.min.css b/cps/static/js/libs/pickr/pickr.min.css new file mode 100644 index 000000000..e4ccfd28f --- /dev/null +++ b/cps/static/js/libs/pickr/pickr.min.css @@ -0,0 +1,2 @@ +/*! Pickr 1.9.1 MIT | https://github.com/Simonwep/pickr */ +.pickr{position:relative;overflow:visible;transform:translateY(0)}.pickr *{box-sizing:border-box;outline:none;border:none;-webkit-appearance:none}.pickr .pcr-button{position:relative;height:2em;width:2em;padding:.5em;cursor:pointer;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI","Roboto","Helvetica Neue",Arial,sans-serif;border-radius:.15em;background:url("data:image/svg+xml;utf8, ") no-repeat center;background-size:0;transition:all .3s}.pickr .pcr-button::before{position:absolute;content:"";top:0;left:0;width:100%;height:100%;background:url("data:image/svg+xml;utf8, ");background-size:.5em;border-radius:.15em;z-index:-1}.pickr .pcr-button::before{z-index:initial}.pickr .pcr-button::after{position:absolute;content:"";top:0;left:0;height:100%;width:100%;transition:background .3s;background:var(--pcr-color);border-radius:.15em}.pickr .pcr-button.clear{background-size:70%}.pickr .pcr-button.clear::before{opacity:0}.pickr .pcr-button.clear:focus{box-shadow:0 0 0 1px rgba(255,255,255,.85),0 0 0 3px var(--pcr-color)}.pickr .pcr-button.disabled{cursor:not-allowed}.pickr *,.pcr-app *{box-sizing:border-box;outline:none;border:none;-webkit-appearance:none}.pickr input:focus,.pickr input.pcr-active,.pickr button:focus,.pickr button.pcr-active,.pcr-app input:focus,.pcr-app input.pcr-active,.pcr-app button:focus,.pcr-app button.pcr-active{box-shadow:0 0 0 1px rgba(255,255,255,.85),0 0 0 3px var(--pcr-color)}.pickr .pcr-palette,.pickr .pcr-slider,.pcr-app .pcr-palette,.pcr-app .pcr-slider{transition:box-shadow .3s}.pickr .pcr-palette:focus,.pickr .pcr-slider:focus,.pcr-app .pcr-palette:focus,.pcr-app .pcr-slider:focus{box-shadow:0 0 0 1px rgba(255,255,255,.85),0 0 0 3px rgba(0,0,0,.25)}.pcr-app{position:fixed;display:flex;flex-direction:column;z-index:10000;border-radius:.1em;background:#fff;opacity:0;visibility:hidden;transition:opacity .3s,visibility 0s .3s;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI","Roboto","Helvetica Neue",Arial,sans-serif;box-shadow:0 .15em 1.5em 0 rgba(0,0,0,.1),0 0 1em 0 rgba(0,0,0,.03);left:0;top:0}.pcr-app.visible{transition:opacity .3s;visibility:visible;opacity:1}.pcr-app .pcr-swatches{display:flex;flex-wrap:wrap;margin-top:.75em}.pcr-app .pcr-swatches.pcr-last{margin:0}@supports(display: grid){.pcr-app .pcr-swatches{display:grid;align-items:center;grid-template-columns:repeat(auto-fit, 1.75em)}}.pcr-app .pcr-swatches>button{font-size:1em;position:relative;width:calc(1.75em - 5px);height:calc(1.75em - 5px);border-radius:.15em;cursor:pointer;margin:2.5px;flex-shrink:0;justify-self:center;transition:all .15s;overflow:hidden;background:rgba(0,0,0,0);z-index:1}.pcr-app .pcr-swatches>button::before{position:absolute;content:"";top:0;left:0;width:100%;height:100%;background:url("data:image/svg+xml;utf8, ");background-size:6px;border-radius:.15em;z-index:-1}.pcr-app .pcr-swatches>button::after{content:"";position:absolute;top:0;left:0;width:100%;height:100%;background:var(--pcr-color);border:1px solid rgba(0,0,0,.05);border-radius:.15em;box-sizing:border-box}.pcr-app .pcr-swatches>button:hover{filter:brightness(1.05)}.pcr-app .pcr-swatches>button:not(.pcr-active){box-shadow:none}.pcr-app .pcr-interaction{display:flex;flex-wrap:wrap;align-items:center;margin:0 -0.2em 0 -0.2em}.pcr-app .pcr-interaction>*{margin:0 .2em}.pcr-app .pcr-interaction input{letter-spacing:.07em;font-size:.75em;text-align:center;cursor:pointer;color:#75797e;background:#f1f3f4;border-radius:.15em;transition:all .15s;padding:.45em .5em;margin-top:.75em}.pcr-app .pcr-interaction input:hover{filter:brightness(0.975)}.pcr-app .pcr-interaction input:focus{box-shadow:0 0 0 1px rgba(255,255,255,.85),0 0 0 3px rgba(66,133,244,.75)}.pcr-app .pcr-interaction .pcr-result{color:#75797e;text-align:left;flex:1 1 8em;min-width:8em;transition:all .2s;border-radius:.15em;background:#f1f3f4;cursor:text}.pcr-app .pcr-interaction .pcr-result::-moz-selection{background:#4285f4;color:#fff}.pcr-app .pcr-interaction .pcr-result::selection{background:#4285f4;color:#fff}.pcr-app .pcr-interaction .pcr-type.active{color:#fff;background:#4285f4}.pcr-app .pcr-interaction .pcr-save,.pcr-app .pcr-interaction .pcr-cancel,.pcr-app .pcr-interaction .pcr-clear{color:#fff;width:auto}.pcr-app .pcr-interaction .pcr-save,.pcr-app .pcr-interaction .pcr-cancel,.pcr-app .pcr-interaction .pcr-clear{color:#fff}.pcr-app .pcr-interaction .pcr-save:hover,.pcr-app .pcr-interaction .pcr-cancel:hover,.pcr-app .pcr-interaction .pcr-clear:hover{filter:brightness(0.925)}.pcr-app .pcr-interaction .pcr-save{background:#4285f4}.pcr-app .pcr-interaction .pcr-clear,.pcr-app .pcr-interaction .pcr-cancel{background:#f44250}.pcr-app .pcr-interaction .pcr-clear:focus,.pcr-app .pcr-interaction .pcr-cancel:focus{box-shadow:0 0 0 1px rgba(255,255,255,.85),0 0 0 3px rgba(244,66,80,.75)}.pcr-app .pcr-selection .pcr-picker{position:absolute;height:18px;width:18px;border:2px solid #fff;border-radius:100%;-webkit-user-select:none;-moz-user-select:none;user-select:none}.pcr-app .pcr-selection .pcr-color-palette,.pcr-app .pcr-selection .pcr-color-chooser,.pcr-app .pcr-selection .pcr-color-opacity{position:relative;-webkit-user-select:none;-moz-user-select:none;user-select:none;display:flex;flex-direction:column;cursor:grab;cursor:-webkit-grab}.pcr-app .pcr-selection .pcr-color-palette:active,.pcr-app .pcr-selection .pcr-color-chooser:active,.pcr-app .pcr-selection .pcr-color-opacity:active{cursor:grabbing;cursor:-webkit-grabbing}.pcr-app[data-theme=classic]{width:28.5em;max-width:95vw;padding:.8em}.pcr-app[data-theme=classic] .pcr-selection{display:flex;justify-content:space-between;flex-grow:1}.pcr-app[data-theme=classic] .pcr-selection .pcr-color-preview{position:relative;z-index:1;width:2em;display:flex;flex-direction:column;justify-content:space-between;margin-right:.75em}.pcr-app[data-theme=classic] .pcr-selection .pcr-color-preview::before{position:absolute;content:"";top:0;left:0;width:100%;height:100%;background:url("data:image/svg+xml;utf8, ");background-size:.5em;border-radius:.15em;z-index:-1}.pcr-app[data-theme=classic] .pcr-selection .pcr-color-preview .pcr-last-color{cursor:pointer;border-radius:.15em .15em 0 0;z-index:2}.pcr-app[data-theme=classic] .pcr-selection .pcr-color-preview .pcr-current-color{border-radius:0 0 .15em .15em}.pcr-app[data-theme=classic] .pcr-selection .pcr-color-preview .pcr-last-color,.pcr-app[data-theme=classic] .pcr-selection .pcr-color-preview .pcr-current-color{background:var(--pcr-color);width:100%;height:50%}.pcr-app[data-theme=classic] .pcr-selection .pcr-color-palette{width:100%;height:8em;z-index:1}.pcr-app[data-theme=classic] .pcr-selection .pcr-color-palette .pcr-palette{flex-grow:1;border-radius:.15em}.pcr-app[data-theme=classic] .pcr-selection .pcr-color-palette .pcr-palette::before{position:absolute;content:"";top:0;left:0;width:100%;height:100%;background:url("data:image/svg+xml;utf8, ");background-size:.5em;border-radius:.15em;z-index:-1}.pcr-app[data-theme=classic] .pcr-selection .pcr-color-chooser,.pcr-app[data-theme=classic] .pcr-selection .pcr-color-opacity{margin-left:.75em}.pcr-app[data-theme=classic] .pcr-selection .pcr-color-chooser .pcr-picker,.pcr-app[data-theme=classic] .pcr-selection .pcr-color-opacity .pcr-picker{left:50%;transform:translateX(-50%)}.pcr-app[data-theme=classic] .pcr-selection .pcr-color-chooser .pcr-slider,.pcr-app[data-theme=classic] .pcr-selection .pcr-color-opacity .pcr-slider{width:8px;flex-grow:1;border-radius:50em}.pcr-app[data-theme=classic] .pcr-selection .pcr-color-chooser .pcr-slider{background:linear-gradient(to bottom, hsl(0, 100%, 50%), hsl(60, 100%, 50%), hsl(120, 100%, 50%), hsl(180, 100%, 50%), hsl(240, 100%, 50%), hsl(300, 100%, 50%), hsl(0, 100%, 50%))}.pcr-app[data-theme=classic] .pcr-selection .pcr-color-opacity .pcr-slider{background:linear-gradient(to bottom, transparent, black),url("data:image/svg+xml;utf8, ");background-size:100%,50%} diff --git a/cps/static/js/libs/pickr/pickr.min.js b/cps/static/js/libs/pickr/pickr.min.js new file mode 100644 index 000000000..5fa877d29 --- /dev/null +++ b/cps/static/js/libs/pickr/pickr.min.js @@ -0,0 +1,3 @@ +/*! Pickr 1.9.1 MIT | https://github.com/Simonwep/pickr */ +!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.Pickr=e():t.Pickr=e()}(self,(()=>(()=>{"use strict";var t={d:(e,o)=>{for(var n in o)t.o(o,n)&&!t.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:o[n]})},o:(t,e)=>Object.prototype.hasOwnProperty.call(t,e),r:t=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})}},e={};t.d(e,{default:()=>E});var o={};function n(t,e,o,n,i={}){e instanceof HTMLCollection||e instanceof NodeList?e=Array.from(e):Array.isArray(e)||(e=[e]),Array.isArray(o)||(o=[o]);for(const s of e)for(const e of o)s[t](e,n,{capture:!1,...i});return Array.prototype.slice.call(arguments,1)}t.r(o),t.d(o,{adjustableInputNumbers:()=>p,createElementFromString:()=>r,createFromTemplate:()=>a,eventPath:()=>l,off:()=>s,on:()=>i,resolveElement:()=>c});const i=n.bind(null,"addEventListener"),s=n.bind(null,"removeEventListener");function r(t){const e=document.createElement("div");return e.innerHTML=t.trim(),e.firstElementChild}function a(t){const e=(t,e)=>{const o=t.getAttribute(e);return t.removeAttribute(e),o},o=(t,n={})=>{const i=e(t,":obj"),s=e(t,":ref"),r=i?n[i]={}:n;s&&(n[s]=t);for(const n of Array.from(t.children)){const t=e(n,":arr"),i=o(n,t?{}:r);t&&(r[t]||(r[t]=[])).push(Object.keys(i).length?i:n)}return n};return o(r(t))}function l(t){let e=t.path||t.composedPath&&t.composedPath();if(e)return e;let o=t.target.parentElement;for(e=[t.target,o];o=o.parentElement;)e.push(o);return e.push(document,window),e}function c(t){return t instanceof Element?t:"string"==typeof t?t.split(/>>/g).reduce(((t,e,o,n)=>(t=t.querySelector(e),ot)){function o(o){const n=[.001,.01,.1][Number(o.shiftKey||2*o.ctrlKey)]*(o.deltaY<0?1:-1);let i=0,s=t.selectionStart;t.value=t.value.replace(/[\d.]+/g,((t,o)=>o<=s&&o+t.length>=s?(s=o,e(Number(t),n,i)):(i++,t))),t.focus(),t.setSelectionRange(s,s),o.preventDefault(),t.dispatchEvent(new Event("input"))}i(t,"focus",(()=>i(window,"wheel",o,{passive:!1}))),i(t,"blur",(()=>s(window,"wheel",o)))}const{min:u,max:h,floor:d,round:m}=Math;function f(t,e,o){e/=100,o/=100;const n=d(t=t/360*6),i=t-n,s=o*(1-e),r=o*(1-i*e),a=o*(1-(1-i)*e),l=n%6;return[255*[o,r,s,s,a,o][l],255*[a,o,o,r,s,s][l],255*[s,s,a,o,o,r][l]]}function v(t,e,o){const n=(2-(e/=100))*(o/=100)/2;return 0!==n&&(e=1===n?0:n<.5?e*o/(2*n):e*o/(2-2*n)),[t,100*e,100*n]}function b(t,e,o){const n=u(t/=255,e/=255,o/=255),i=h(t,e,o),s=i-n;let r,a;if(0===s)r=a=0;else{a=s/i;const n=((i-t)/6+s/2)/s,l=((i-e)/6+s/2)/s,c=((i-o)/6+s/2)/s;t===i?r=c-l:e===i?r=1/3+n-c:o===i&&(r=2/3+l-n),r<0?r+=1:r>1&&(r-=1)}return[360*r,100*a,100*i]}function y(t,e,o,n){e/=100,o/=100;return[...b(255*(1-u(1,(t/=100)*(1-(n/=100))+n)),255*(1-u(1,e*(1-n)+n)),255*(1-u(1,o*(1-n)+n)))]}function g(t,e,o){e/=100;const n=2*(e*=(o/=100)<.5?o:1-o)/(o+e)*100,i=100*(o+e);return[t,isNaN(n)?0:n,i]}function _(t){return b(...t.match(/.{2}/g).map((t=>parseInt(t,16))))}function w(t){t=t.match(/^[a-zA-Z]+$/)?function(t){if("black"===t.toLowerCase())return"#000";const e=document.createElement("canvas").getContext("2d");return e.fillStyle=t,"#000"===e.fillStyle?null:e.fillStyle}(t):t;const e={cmyk:/^cmyk\D+([\d.]+)\D+([\d.]+)\D+([\d.]+)\D+([\d.]+)/i,rgba:/^rgba?\D+([\d.]+)(%?)\D+([\d.]+)(%?)\D+([\d.]+)(%?)\D*?(([\d.]+)(%?)|$)/i,hsla:/^hsla?\D+([\d.]+)\D+([\d.]+)\D+([\d.]+)\D*?(([\d.]+)(%?)|$)/i,hsva:/^hsva?\D+([\d.]+)\D+([\d.]+)\D+([\d.]+)\D*?(([\d.]+)(%?)|$)/i,hexa:/^#?(([\dA-Fa-f]{3,4})|([\dA-Fa-f]{6})|([\dA-Fa-f]{8}))$/i},o=t=>t.map((t=>/^(|\d+)\.\d+|\d+$/.test(t)?Number(t):void 0));let n;t:for(const i in e)if(n=e[i].exec(t))switch(i){case"cmyk":{const[,t,e,s,r]=o(n);if(t>100||e>100||s>100||r>100)break t;return{values:y(t,e,s,r),type:i}}case"rgba":{let[,t,,e,,s,,,r]=o(n);if(t="%"===n[2]?t/100*255:t,e="%"===n[4]?e/100*255:e,s="%"===n[6]?s/100*255:s,r="%"===n[9]?r/100:r,t>255||e>255||s>255||r<0||r>1)break t;return{values:[...b(t,e,s),r],a:r,type:i}}case"hexa":{let[,t]=n;4!==t.length&&3!==t.length||(t=t.split("").map((t=>t+t)).join(""));const e=t.substring(0,6);let o=t.substring(6);return o=o?parseInt(o,16)/255:void 0,{values:[..._(e),o],a:o,type:i}}case"hsla":{let[,t,e,s,,r]=o(n);if(r="%"===n[6]?r/100:r,t>360||e>100||s>100||r<0||r>1)break t;return{values:[...g(t,e,s),r],a:r,type:i}}case"hsva":{let[,t,e,s,,r]=o(n);if(r="%"===n[6]?r/100:r,t>360||e>100||s>100||r<0||r>1)break t;return{values:[t,e,s,r],a:r,type:i}}}return{values:null,type:null}}function A(t=0,e=0,o=0,n=1){const i=(t,e)=>(o=-1)=>e(~o?t.map((t=>Number(t.toFixed(o)))):t),s={h:t,s:e,v:o,a:n,toHSVA(){const t=[s.h,s.s,s.v,s.a];return t.toString=i(t,(t=>`hsva(${t[0]}, ${t[1]}%, ${t[2]}%, ${s.a})`)),t},toHSLA(){const t=[...v(s.h,s.s,s.v),s.a];return t.toString=i(t,(t=>`hsla(${t[0]}, ${t[1]}%, ${t[2]}%, ${s.a})`)),t},toRGBA(){const t=[...f(s.h,s.s,s.v),s.a];return t.toString=i(t,(t=>`rgba(${t[0]}, ${t[1]}, ${t[2]}, ${s.a})`)),t},toCMYK(){const t=function(t,e,o){const n=f(t,e,o),i=n[0]/255,s=n[1]/255,r=n[2]/255,a=u(1-i,1-s,1-r);return[100*(1===a?0:(1-i-a)/(1-a)),100*(1===a?0:(1-s-a)/(1-a)),100*(1===a?0:(1-r-a)/(1-a)),100*a]}(s.h,s.s,s.v);return t.toString=i(t,(t=>`cmyk(${t[0]}%, ${t[1]}%, ${t[2]}%, ${t[3]}%)`)),t},toHEXA(){const t=function(t,e,o){return f(t,e,o).map((t=>m(t).toString(16).padStart(2,"0")))}(s.h,s.s,s.v),e=s.a>=1?"":Number((255*s.a).toFixed(0)).toString(16).toUpperCase().padStart(2,"0");return e&&t.push(e),t.toString=()=>`#${t.join("").toUpperCase()}`,t},clone:()=>A(s.h,s.s,s.v,s.a)};return s}const $=t=>Math.max(Math.min(t,1),0);function C(t){const e={options:Object.assign({lock:null,onchange:()=>0,onstop:()=>0},t),_keyboard(t){const{options:o}=e,{type:n,key:i}=t;if(document.activeElement===o.wrapper){const{lock:o}=e.options,s="ArrowUp"===i,r="ArrowRight"===i,a="ArrowDown"===i,l="ArrowLeft"===i;if("keydown"===n&&(s||r||a||l)){let n=0,i=0;"v"===o?n=s||r?1:-1:"h"===o?n=s||r?-1:1:(i=s?-1:a?1:0,n=l?-1:r?1:0),e.update($(e.cache.x+.01*n),$(e.cache.y+.01*i)),t.preventDefault()}else i.startsWith("Arrow")&&(e.options.onstop(),t.preventDefault())}},_tapstart(t){i(document,["mouseup","touchend","touchcancel"],e._tapstop),i(document,["mousemove","touchmove"],e._tapmove),t.cancelable&&t.preventDefault(),e._tapmove(t)},_tapmove(t){const{options:o,cache:n}=e,{lock:i,element:s,wrapper:r}=o,a=r.getBoundingClientRect();let l=0,c=0;if(t){const e=t&&t.touches&&t.touches[0];l=t?(e||t).clientX:0,c=t?(e||t).clientY:0,la.left+a.width&&(l=a.left+a.width),ca.top+a.height&&(c=a.top+a.height),l-=a.left,c-=a.top}else n&&(l=n.x*a.width,c=n.y*a.height);"h"!==i&&(s.style.left=`calc(${l/a.width*100}% - ${s.offsetWidth/2}px)`),"v"!==i&&(s.style.top=`calc(${c/a.height*100}% - ${s.offsetHeight/2}px)`),e.cache={x:l/a.width,y:c/a.height};const p=$(l/a.width),u=$(c/a.height);switch(i){case"v":return o.onchange(p);case"h":return o.onchange(u);default:return o.onchange(p,u)}},_tapstop(){e.options.onstop(),s(document,["mouseup","touchend","touchcancel"],e._tapstop),s(document,["mousemove","touchmove"],e._tapmove)},trigger(){e._tapmove()},update(t=0,o=0){const{left:n,top:i,width:s,height:r}=e.options.wrapper.getBoundingClientRect();"h"===e.options.lock&&(o=t),e._tapmove({clientX:n+s*t,clientY:i+r*o})},destroy(){const{options:t,_tapstart:o,_keyboard:n}=e;s(document,["keydown","keyup"],n),s([t.wrapper,t.element],"mousedown",o),s([t.wrapper,t.element],"touchstart",o,{passive:!1})}},{options:o,_tapstart:n,_keyboard:r}=e;return i([o.wrapper,o.element],"mousedown",n),i([o.wrapper,o.element],"touchstart",n,{passive:!1}),i(document,["keydown","keyup"],r),e}function k(t={}){t=Object.assign({onchange:()=>0,className:"",elements:[]},t);const e=i(t.elements,"click",(e=>{t.elements.forEach((o=>o.classList[e.target===o?"add":"remove"](t.className))),t.onchange(e),e.stopPropagation()}));return{destroy:()=>s(...e)}}const S={variantFlipOrder:{start:"sme",middle:"mse",end:"ems"},positionFlipOrder:{top:"tbrl",right:"rltb",bottom:"btrl",left:"lrbt"},position:"bottom",margin:8,padding:0},O=(t,e,o)=>{const n="object"!=typeof t||t instanceof HTMLElement?{reference:t,popper:e,...o}:t;return{update(t=n){const{reference:e,popper:o}=Object.assign(n,t);if(!o||!e)throw new Error("Popper- or reference-element missing.");return((t,e,o)=>{const{container:n,arrow:i,margin:s,padding:r,position:a,variantFlipOrder:l,positionFlipOrder:c}={container:document.documentElement.getBoundingClientRect(),...S,...o},{left:p,top:u}=e.style;e.style.left="0",e.style.top="0";const h=t.getBoundingClientRect(),d=e.getBoundingClientRect(),m={t:h.top-d.height-s,b:h.bottom+s,r:h.right+s,l:h.left-d.width-s},f={vs:h.left,vm:h.left+h.width/2-d.width/2,ve:h.left+h.width-d.width,hs:h.top,hm:h.bottom-h.height/2-d.height/2,he:h.bottom-d.height},[v,b="middle"]=a.split("-"),y=c[v],g=l[b],{top:_,left:w,bottom:A,right:$}=n;for(const t of y){const o="t"===t||"b"===t;let n=m[t];const[s,a]=o?["top","left"]:["left","top"],[l,c]=o?[d.height,d.width]:[d.width,d.height],[p,u]=o?[A,$]:[$,A],[v,b]=o?[_,w]:[w,_];if(!(np))for(const p of g){let m=f[(o?"v":"h")+p];if(!(mu)){if(m-=d[a],n-=d[s],e.style[a]=`${m}px`,e.style[s]=`${n}px`,i){const e=o?h.width/2:h.height/2,r=c/2,u=e>r,d=m+{s:u?r:e,m:r,e:u?r:c-e}[p],f=n+{t:l,b:0,r:0,l}[t];i.style[a]=`${d}px`,i.style[s]=`${f}px`}return t+p}}}return e.style.left=p,e.style.top=u,null})(e,o,n)}}};class E{static utils=o;static version="1.9.1";static I18N_DEFAULTS={"ui:dialog":"color picker dialog","btn:toggle":"toggle color picker dialog","btn:swatch":"color swatch","btn:last-color":"use previous color","btn:save":"Save","btn:cancel":"Cancel","btn:clear":"Clear","aria:btn:save":"save and close","aria:btn:cancel":"cancel and close","aria:btn:clear":"clear and close","aria:input":"color input field","aria:palette":"color selection area","aria:hue":"hue selection slider","aria:opacity":"selection slider"};static DEFAULT_OPTIONS={appClass:null,theme:"classic",useAsButton:!1,padding:8,disabled:!1,comparison:!0,closeOnScroll:!1,outputPrecision:0,lockOpacity:!1,autoReposition:!0,container:"body",components:{interaction:{}},i18n:{},swatches:null,inline:!1,sliders:null,default:"#42445a",defaultRepresentation:null,position:"bottom-middle",adjustableNumbers:!0,showAlways:!1,closeWithKey:"Escape"};_initializingActive=!0;_recalc=!0;_nanopop=null;_root=null;_color=A();_lastColor=A();_swatchColors=[];_setupAnimationFrame=null;_eventListener={init:[],save:[],hide:[],show:[],clear:[],change:[],changestop:[],cancel:[],swatchselect:[]};constructor(t){this.options=t=Object.assign({...E.DEFAULT_OPTIONS},t);const{swatches:e,components:o,theme:n,sliders:i,lockOpacity:s,padding:r}=t;["nano","monolith"].includes(n)&&!i&&(t.sliders="h"),o.interaction||(o.interaction={});const{preview:a,opacity:l,hue:c,palette:p}=o;o.opacity=!s&&l,o.palette=p||a||l||c,this._preBuild(),this._buildComponents(),this._bindEvents(),this._finalBuild(),e&&e.length&&e.forEach((t=>this.addSwatch(t)));const{button:u,app:h}=this._root;this._nanopop=O(u,h,{margin:r}),u.setAttribute("role","button"),u.setAttribute("aria-label",this._t("btn:toggle"));const d=this;this._setupAnimationFrame=requestAnimationFrame((function e(){if(!h.offsetWidth)return requestAnimationFrame(e);d.setColor(t.default),d._rePositioningPicker(),t.defaultRepresentation&&(d._representation=t.defaultRepresentation,d.setColorRepresentation(d._representation)),t.showAlways&&d.show(),d._initializingActive=!1,d._emit("init")}))}static create=t=>new E(t);_preBuild(){const{options:t}=this;for(const e of["el","container"])t[e]=c(t[e]);this._root=(t=>{const{components:e,useAsButton:o,inline:n,appClass:i,theme:s,lockOpacity:r}=t.options,l=t=>t?"":'style="display:none" hidden',c=e=>t._t(e),p=a(`\n
\n\n ${o?"":''}\n\n
\n
\n
\n \n
\n
\n\n
\n
\n
\n
\n\n
\n
\n
\n
\n\n
\n
\n
\n
\n
\n\n
\n\n
\n \n\n \n \n \n \n \n\n \n \n \n
\n
\n
\n `),u=p.interaction;return u.options.find((t=>!t.hidden&&!t.classList.add("active"))),u.type=()=>u.options.find((t=>t.classList.contains("active"))),p})(this),t.useAsButton&&(this._root.button=t.el),t.container.appendChild(this._root.root)}_finalBuild(){const t=this.options,e=this._root;if(t.container.removeChild(e.root),t.inline){const o=t.el.parentElement;t.el.nextSibling?o.insertBefore(e.app,t.el.nextSibling):o.appendChild(e.app)}else t.container.appendChild(e.app);t.useAsButton?t.inline&&t.el.remove():t.el.parentNode.replaceChild(e.root,t.el),t.disabled&&this.disable(),t.comparison||(e.button.style.transition="none",t.useAsButton||(e.preview.lastColor.style.transition="none")),this.hide()}_buildComponents(){const t=this,e=this.options.components,o=(t.options.sliders||"v").repeat(2),[n,i]=o.match(/^[vh]+$/g)?o:[],s=()=>this._color||(this._color=this._lastColor.clone()),r={palette:C({element:t._root.palette.picker,wrapper:t._root.palette.palette,onstop:()=>t._emit("changestop","slider",t),onchange(o,n){if(!e.palette)return;const i=s(),{_root:r,options:a}=t,{lastColor:l,currentColor:c}=r.preview;t._recalc&&(i.s=100*o,i.v=100-100*n,i.v<0&&(i.v=0),t._updateOutput("slider"));const p=i.toRGBA().toString(0);this.element.style.background=p,this.wrapper.style.background=`\n linear-gradient(to top, rgba(0, 0, 0, ${i.a}), transparent),\n linear-gradient(to left, hsla(${i.h}, 100%, 50%, ${i.a}), rgba(255, 255, 255, ${i.a}))\n `,a.comparison?a.useAsButton||t._lastColor||l.style.setProperty("--pcr-color",p):(r.button.style.setProperty("--pcr-color",p),r.button.classList.remove("clear"));const u=i.toHEXA().toString();for(const{el:e,color:o}of t._swatchColors)e.classList[u===o.toHEXA().toString()?"add":"remove"]("pcr-active");c.style.setProperty("--pcr-color",p)}}),hue:C({lock:"v"===i?"h":"v",element:t._root.hue.picker,wrapper:t._root.hue.slider,onstop:()=>t._emit("changestop","slider",t),onchange(o){if(!e.hue||!e.palette)return;const n=s();t._recalc&&(n.h=360*o),this.element.style.backgroundColor=`hsl(${n.h}, 100%, 50%)`,r.palette.trigger()}}),opacity:C({lock:"v"===n?"h":"v",element:t._root.opacity.picker,wrapper:t._root.opacity.slider,onstop:()=>t._emit("changestop","slider",t),onchange(o){if(!e.opacity||!e.palette)return;const n=s();t._recalc&&(n.a=Math.round(100*o)/100),this.element.style.background=`rgba(0, 0, 0, ${n.a})`,r.palette.trigger()}}),selectable:k({elements:t._root.interaction.options,className:"active",onchange(e){t._representation=e.target.getAttribute("data-type").toUpperCase(),t._recalc&&t._updateOutput("swatch")}})};this._components=r}_bindEvents(){const{_root:t,options:e}=this,o=[i(t.interaction.clear,"click",(()=>this._clearColor())),i([t.interaction.cancel,t.preview.lastColor],"click",(()=>{this.setHSVA(...(this._lastColor||this._color).toHSVA(),!0),this._emit("cancel")})),i(t.interaction.save,"click",(()=>{!this.applyColor()&&!e.showAlways&&this.hide()})),i(t.interaction.result,["keyup","input"],(t=>{this.setColor(t.target.value,!0)&&!this._initializingActive&&(this._emit("change",this._color,"input",this),this._emit("changestop","input",this)),t.stopImmediatePropagation()})),i(t.interaction.result,["focus","blur"],(t=>{this._recalc="blur"===t.type,this._recalc&&this._updateOutput(null)})),i([t.palette.palette,t.palette.picker,t.hue.slider,t.hue.picker,t.opacity.slider,t.opacity.picker],["mousedown","touchstart"],(()=>this._recalc=!0),{passive:!0})];if(!e.showAlways){const n=e.closeWithKey;o.push(i(t.button,"click",(()=>this.isOpen()?this.hide():this.show())),i(document,"keyup",(t=>this.isOpen()&&(t.key===n||t.code===n)&&this.hide())),i(document,["touchstart","mousedown"],(e=>{this.isOpen()&&!l(e).some((e=>e===t.app||e===t.button))&&this.hide()}),{capture:!0}))}if(e.adjustableNumbers){const e={rgba:[255,255,255,1],hsva:[360,100,100,1],hsla:[360,100,100,1],cmyk:[100,100,100,100]};p(t.interaction.result,((t,o,n)=>{const i=e[this.getColorRepresentation().toLowerCase()];if(i){const e=i[n],s=t+(e>=100?1e3*o:o);return s<=0?0:Number((s{n.isOpen()&&(e.closeOnScroll&&n.hide(),null===t?(t=setTimeout((()=>t=null),100),requestAnimationFrame((function e(){n._rePositioningPicker(),null!==t&&requestAnimationFrame(e)}))):(clearTimeout(t),t=setTimeout((()=>t=null),100)))}),{capture:!0}))}this._eventBindings=o}_rePositioningPicker(){const{options:t}=this;if(!t.inline){if(!this._nanopop.update({container:document.body.getBoundingClientRect(),position:t.position})){const t=this._root.app,e=t.getBoundingClientRect();t.style.top=(window.innerHeight-e.height)/2+"px",t.style.left=(window.innerWidth-e.width)/2+"px"}}}_updateOutput(t){const{_root:e,_color:o,options:n}=this;if(e.interaction.type()){const t=`to${e.interaction.type().getAttribute("data-type")}`;e.interaction.result.value="function"==typeof o[t]?o[t]().toString(n.outputPrecision):""}!this._initializingActive&&this._recalc&&this._emit("change",o,t,this)}_clearColor(t=!1){const{_root:e,options:o}=this;o.useAsButton||e.button.style.setProperty("--pcr-color","rgba(0, 0, 0, 0.15)"),e.button.classList.add("clear"),o.showAlways||this.hide(),this._lastColor=null,this._initializingActive||t||(this._emit("save",null),this._emit("clear"))}_parseLocalColor(t){const{values:e,type:o,a:n}=w(t),{lockOpacity:i}=this.options,s=void 0!==n&&1!==n;return e&&3===e.length&&(e[3]=void 0),{values:!e||i&&s?null:e,type:o}}_t(t){return this.options.i18n[t]||E.I18N_DEFAULTS[t]}_emit(t,...e){this._eventListener[t].forEach((t=>t(...e,this)))}on(t,e){return this._eventListener[t].push(e),this}off(t,e){const o=this._eventListener[t]||[],n=o.indexOf(e);return~n&&o.splice(n,1),this}addSwatch(t){const{values:e}=this._parseLocalColor(t);if(e){const{_swatchColors:t,_root:o}=this,n=A(...e),s=r(` {% endif %} -
+
{% for entry in entries %} {% if loop.index0 == (loop.length/2+loop.length%2)|int and loop.length > 20 %} diff --git a/cps/templates/read.html b/cps/templates/read.html index 9c6e4bcac..86d7347e9 100644 --- a/cps/templates/read.html +++ b/cps/templates/read.html @@ -29,6 +29,7 @@ TOC Bookmarks + Books
@@ -49,7 +50,6 @@
-
Menu @@ -70,37 +70,58 @@
-
0%
-
-
+ +
+ +
+
@@ -2249,13 +2249,13 @@ - + TestLoadMetadataScholar 1 - 0 1 0 0 + 0 Detail @@ -2263,31 +2263,11 @@ - +
TestLoadMetadataScholar - test_load_metadata
- -
- FAIL -
- - - - + PASS @@ -2590,12 +2570,12 @@ AssertionError: 30 != 20 - + TestFilePicker 3 - 1 + 2 + 0 0 - 1 1 Detail @@ -2639,32 +2619,11 @@ AssertionError: 30 != 20 - +
TestFilePicker - test_two_filepickers
- -
- ERROR -
- - - - + PASS @@ -4726,11 +4685,11 @@ AssertionError: 7 != 2 - + TestThumbnails 8 - 6 - 1 + 7 + 0 0 1 @@ -4803,31 +4762,11 @@ AssertionError: 7 != 2 - +
TestThumbnails - test_sideloaded_book
- -
- FAIL -
- - - - + PASS @@ -6294,9 +6233,9 @@ AssertionError: 12 != 1 Total 538 - 513 - 17 - 1 + 516 + 15 + 0 7   @@ -6325,7 +6264,7 @@ AssertionError: 12 != 1 Platform - Linux 6.8.0-88-generic #89-Ubuntu SMP PREEMPT_DYNAMIC Sat Oct 11 01:02:46 UTC 2025 x86_64 x86_64 + Linux 6.8.0-90-generic #91-Ubuntu SMP PREEMPT_DYNAMIC Tue Nov 18 14:14:30 UTC 2025 x86_64 x86_64 Basic @@ -6337,7 +6276,7 @@ AssertionError: 12 != 1 APScheduler - 3.11.1 + 3.11.2 Basic @@ -6439,7 +6378,7 @@ AssertionError: 12 != 1 pypdf - 6.4.0 + 6.4.2 Basic @@ -6469,13 +6408,13 @@ AssertionError: 12 != 1 SQLAlchemy - 2.0.44 + 2.0.45 Basic tornado - 6.5.2 + 6.5.4 Basic @@ -6487,7 +6426,7 @@ AssertionError: 12 != 1 urllib3 - 2.6.0 + 2.6.3 Basic @@ -6787,13 +6726,13 @@ AssertionError: 12 != 1 jsonschema - 4.25.1 + 4.26.0 TestKoboSync jsonschema - 4.25.1 + 4.26.0 TestKoboSyncBig @@ -6805,7 +6744,7 @@ AssertionError: 12 != 1 jsonschema - 4.25.1 + 4.26.0 TestLdapLogin @@ -6823,7 +6762,7 @@ AssertionError: 12 != 1 SQLAlchemy-Utils - 0.42.0 + 0.42.1 TestOAuthLogin @@ -6847,7 +6786,7 @@ AssertionError: 12 != 1