diff options
71 files changed, 6397 insertions, 3525 deletions
diff --git a/.editorconfig b/.editorconfig index 119b50a10..2b1a9fb1f 100644 --- a/.editorconfig +++ b/.editorconfig @@ -4,3 +4,6 @@ insert_final_newline = true [*.php] indent_style = tab + +[*.js] +indent_style = tab @@ -20,6 +20,3 @@ You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. Copyright (c) 2005 Andrew Dolgov (unless explicitly stated otherwise). - -Uses Silk icons by Mark James: http://www.famfamfam.com/lab/icons/silk/ - diff --git a/backend.php b/backend.php index 98c8eace6..b2ff46dca 100644 --- a/backend.php +++ b/backend.php @@ -2,7 +2,7 @@ set_include_path(__DIR__ ."/include" . PATH_SEPARATOR . get_include_path()); - $op = $_REQUEST["op"]; + $op = $_REQUEST['op'] ?? ''; $method = !empty($_REQUEST['subop']) ? $_REQUEST['subop'] : $_REQUEST["method"] ?? false; diff --git a/classes/api.php b/classes/api.php index a1ed7968c..72d43228f 100755 --- a/classes/api.php +++ b/classes/api.php @@ -132,49 +132,48 @@ class API extends Handler { // TODO do not return empty categories, return Uncategorized and standard virtual cats - if ($enable_nested) - $nested_qpart = "parent_cat IS NULL"; - else - $nested_qpart = "true"; - - $sth = $this->pdo->prepare("SELECT - id, title, order_id, (SELECT COUNT(id) FROM - ttrss_feeds WHERE - ttrss_feed_categories.id IS NOT NULL AND cat_id = ttrss_feed_categories.id) AS num_feeds, - (SELECT COUNT(id) FROM - ttrss_feed_categories AS c2 WHERE - c2.parent_cat = ttrss_feed_categories.id) AS num_cats - FROM ttrss_feed_categories - WHERE $nested_qpart AND owner_uid = ?"); - $sth->execute([$_SESSION['uid']]); + $categories = ORM::for_table('ttrss_feed_categories') + ->select_many('id', 'title', 'order_id') + ->select_many_expr([ + 'num_feeds' => '(SELECT COUNT(id) FROM ttrss_feeds WHERE ttrss_feed_categories.id IS NOT NULL AND cat_id = ttrss_feed_categories.id)', + 'num_cats' => '(SELECT COUNT(id) FROM ttrss_feed_categories AS c2 WHERE c2.parent_cat = ttrss_feed_categories.id)', + ]) + ->where('owner_uid', $_SESSION['uid']); + + if ($enable_nested) { + $categories->where_null('parent_cat'); + } - $cats = array(); + $cats = []; - while ($line = $sth->fetch()) { - if ($include_empty || $line["num_feeds"] > 0 || $line["num_cats"] > 0) { - $unread = getFeedUnread($line["id"], true); + foreach ($categories->find_many() as $category) { + if ($include_empty || $category->num_feeds > 0 || $category->num_cats > 0) { + $unread = getFeedUnread($category->id, true); if ($enable_nested) - $unread += Feeds::_get_cat_children_unread($line["id"]); + $unread += Feeds::_get_cat_children_unread($category->id); if ($unread || !$unread_only) { - array_push($cats, array("id" => (int) $line["id"], - "title" => $line["title"], - "unread" => (int) $unread, - "order_id" => (int) $line["order_id"], - )); + array_push($cats, [ + 'id' => (int) $category->id, + 'title' => $category->title, + 'unread' => (int) $unread, + 'order_id' => (int) $category->order_id, + ]); } } } - foreach (array(-2,-1,0) as $cat_id) { + foreach ([-2,-1,0] as $cat_id) { if ($include_empty || !$this->_is_cat_empty($cat_id)) { $unread = getFeedUnread($cat_id, true); if ($unread || !$unread_only) { - array_push($cats, array("id" => $cat_id, - "title" => Feeds::_get_cat_title($cat_id), - "unread" => (int) $unread)); + array_push($cats, [ + 'id' => $cat_id, + 'title' => Feeds::_get_cat_title($cat_id), + 'unread' => (int) $unread, + ]); } } } @@ -300,60 +299,58 @@ class API extends Handler { } function getArticle() { - - $article_ids = explode(",", clean($_REQUEST["article_id"])); - $sanitize_content = !isset($_REQUEST["sanitize"]) || - self::_param_to_bool($_REQUEST["sanitize"]); - - if (count($article_ids) > 0) { - - $article_qmarks = arr_qmarks($article_ids); - - $sth = $this->pdo->prepare("SELECT id,guid,title,link,content,feed_id,comments,int_id, - marked,unread,published,score,note,lang, - ".SUBSTRING_FOR_DATE."(updated,1,16) as updated, - author,(SELECT title FROM ttrss_feeds WHERE id = feed_id) AS feed_title, - (SELECT site_url FROM ttrss_feeds WHERE id = feed_id) AS site_url, - (SELECT hide_images FROM ttrss_feeds WHERE id = feed_id) AS hide_images - FROM ttrss_entries,ttrss_user_entries - WHERE id IN ($article_qmarks) AND ref_id = id AND owner_uid = ?"); - - $sth->execute(array_merge($article_ids, [$_SESSION['uid']])); - - $articles = array(); - - while ($line = $sth->fetch()) { - - $article = array( - "id" => $line["id"], - "guid" => $line["guid"], - "title" => $line["title"], - "link" => $line["link"], - "labels" => Article::_get_labels($line['id']), - "unread" => self::_param_to_bool($line["unread"]), - "marked" => self::_param_to_bool($line["marked"]), - "published" => self::_param_to_bool($line["published"]), - "comments" => $line["comments"], - "author" => $line["author"], - "updated" => (int) strtotime($line["updated"]), - "feed_id" => $line["feed_id"], - "attachments" => Article::_get_enclosures($line['id']), - "score" => (int)$line["score"], - "feed_title" => $line["feed_title"], - "note" => $line["note"], - "lang" => $line["lang"] - ); + $article_ids = explode(',', clean($_REQUEST['article_id'] ?? '')); + $sanitize_content = self::_param_to_bool($_REQUEST['sanitize'] ?? true); + + if (count($article_ids)) { + $entries = ORM::for_table('ttrss_entries') + ->table_alias('e') + ->select_many('e.id', 'e.guid', 'e.title', 'e.link', 'e.author', 'e.content', 'e.lang', 'e.comments', + 'ue.feed_id', 'ue.int_id', 'ue.marked', 'ue.unread', 'ue.published', 'ue.score', 'ue.note') + ->select_many_expr([ + 'updated' => SUBSTRING_FOR_DATE.'(updated,1,16)', + 'feed_title' => '(SELECT title FROM ttrss_feeds WHERE id = ue.feed_id)', + 'site_url' => '(SELECT site_url FROM ttrss_feeds WHERE id = ue.feed_id)', + 'hide_images' => '(SELECT hide_images FROM ttrss_feeds WHERE id = feed_id)', + ]) + ->join('ttrss_user_entries', [ 'ue.ref_id', '=', 'e.id'], 'ue') + ->where_in('e.id', array_map('intval', $article_ids)) + ->where('ue.owner_uid', $_SESSION['uid']) + ->find_many(); + + $articles = []; + + foreach ($entries as $entry) { + $article = [ + 'id' => $entry->id, + 'guid' => $entry->guid, + 'title' => $entry->title, + 'link' => $entry->link, + 'labels' => Article::_get_labels($entry->id), + 'unread' => self::_param_to_bool($entry->unread), + 'marked' => self::_param_to_bool($entry->marked), + 'published' => self::_param_to_bool($entry->published), + 'comments' => $entry->comments, + 'author' => $entry->author, + 'updated' => (int) strtotime($entry->updated), + 'feed_id' => $entry->feed_id, + 'attachments' => Article::_get_enclosures($entry->id), + 'score' => (int) $entry->score, + 'feed_title' => $entry->feed_title, + 'note' => $entry->note, + 'lang' => $entry->lang, + ]; if ($sanitize_content) { - $article["content"] = Sanitizer::sanitize( - $line["content"], - self::_param_to_bool($line['hide_images']), - false, $line["site_url"], false, $line["id"]); + $article['content'] = Sanitizer::sanitize( + $entry->content, + self::_param_to_bool($entry->hide_images), + false, $entry->site_url, false, $entry->id); } else { - $article["content"] = $line["content"]; + $article['content'] = $entry->content; } - $hook_object = ["article" => &$article]; + $hook_object = ['article' => &$article]; PluginHost::getInstance()->chain_hooks_callback(PluginHost::HOOK_RENDER_ARTICLE_API, function ($result) use (&$article) { @@ -364,13 +361,12 @@ class API extends Handler { $article['content'] = DiskCache::rewrite_urls($article['content']); array_push($articles, $article); - } $this->_wrap(self::STATUS_OK, $articles); // @phpstan-ignore-next-line } else { - $this->_wrap(self::STATUS_ERR, array("error" => self::E_INCORRECT_USAGE)); + $this->_wrap(self::STATUS_ERR, ['error' => self::E_INCORRECT_USAGE]); } } @@ -382,12 +378,9 @@ class API extends Handler { $config["daemon_is_running"] = file_is_locked("update_daemon.lock"); - $sth = $this->pdo->prepare("SELECT COUNT(*) AS cf FROM - ttrss_feeds WHERE owner_uid = ?"); - $sth->execute([$_SESSION['uid']]); - $row = $sth->fetch(); - - $config["num_feeds"] = $row["cf"]; + $config["num_feeds"] = ORM::for_table('ttrss_feeds') + ->where('owner_uid', $_SESSION['uid']) + ->count(); $this->_wrap(self::STATUS_OK, $config); } @@ -422,36 +415,36 @@ class API extends Handler { } function getLabels() { - $article_id = (int)clean($_REQUEST['article_id']); + $article_id = (int)clean($_REQUEST['article_id'] ?? -1); - $rv = array(); + $rv = []; - $sth = $this->pdo->prepare("SELECT id, caption, fg_color, bg_color - FROM ttrss_labels2 - WHERE owner_uid = ? ORDER BY caption"); - $sth->execute([$_SESSION['uid']]); + $labels = ORM::for_table('ttrss_labels2') + ->where('owner_uid', $_SESSION['uid']) + ->order_by_asc('caption') + ->find_many(); if ($article_id) $article_labels = Article::_get_labels($article_id); else - $article_labels = array(); - - while ($line = $sth->fetch()) { + $article_labels = []; + foreach ($labels as $label) { $checked = false; foreach ($article_labels as $al) { - if (Labels::feed_to_label_id($al[0]) == $line['id']) { + if (Labels::feed_to_label_id($al[0]) == $label->id) { $checked = true; break; } } - array_push($rv, array( - "id" => (int)Labels::label_to_feed_id($line['id']), - "caption" => $line['caption'], - "fg_color" => $line['fg_color'], - "bg_color" => $line['bg_color'], - "checked" => $checked)); + array_push($rv, [ + 'id' => (int) Labels::label_to_feed_id($label->id), + 'caption' => $label->caption, + 'fg_color' => $label->fg_color, + 'bg_color' => $label->bg_color, + 'checked' => $checked, + ]); } $this->_wrap(self::STATUS_OK, $rv); @@ -512,10 +505,7 @@ class API extends Handler { } private static function _api_get_feeds($cat_id, $unread_only, $limit, $offset, $include_nested = false) { - - $feeds = array(); - - $pdo = Db::pdo(); + $feeds = []; $limit = (int) $limit; $offset = (int) $offset; @@ -528,17 +518,15 @@ class API extends Handler { $counters = Counters::get_labels(); foreach (array_values($counters) as $cv) { - - $unread = $cv["counter"]; + $unread = $cv['counter']; if ($unread || !$unread_only) { - - $row = array( - "id" => (int) $cv["id"], - "title" => $cv["description"], - "unread" => $cv["counter"], - "cat_id" => -2, - ); + $row = [ + 'id' => (int) $cv['id'], + 'title' => $cv['description'], + 'unread' => $cv['counter'], + 'cat_id' => -2, + ]; array_push($feeds, $row); } @@ -548,45 +536,45 @@ class API extends Handler { /* Virtual feeds */ if ($cat_id == -4 || $cat_id == -1) { - foreach (array(-1, -2, -3, -4, -6, 0) as $i) { + foreach ([-1, -2, -3, -4, -6, 0] as $i) { $unread = getFeedUnread($i); if ($unread || !$unread_only) { $title = Feeds::_get_title($i); - $row = array( - "id" => $i, - "title" => $title, - "unread" => $unread, - "cat_id" => -1, - ); + $row = [ + 'id' => $i, + 'title' => $title, + 'unread' => $unread, + 'cat_id' => -1, + ]; + array_push($feeds, $row); } - } } /* Child cats */ if ($include_nested && $cat_id) { - $sth = $pdo->prepare("SELECT - id, title, order_id FROM ttrss_feed_categories - WHERE parent_cat = ? AND owner_uid = ? ORDER BY order_id, title"); - - $sth->execute([$cat_id, $_SESSION['uid']]); + $categories = ORM::for_table('ttrss_feed_categories') + ->where(['parent_cat' => $cat_id, 'owner_uid' => $_SESSION['uid']]) + ->order_by_asc('order_id') + ->order_by_asc('title') + ->find_many(); - while ($line = $sth->fetch()) { - $unread = getFeedUnread($line["id"], true) + - Feeds::_get_cat_children_unread($line["id"]); + foreach ($categories as $category) { + $unread = getFeedUnread($category->id, true) + + Feeds::_get_cat_children_unread($category->id); if ($unread || !$unread_only) { - $row = array( - "id" => (int) $line["id"], - "title" => $line["title"], - "unread" => $unread, - "is_cat" => true, - "order_id" => (int) $line["order_id"] - ); + $row = [ + 'id' => (int) $category->id, + 'title' => $category->title, + 'unread' => $unread, + 'is_cat' => true, + 'order_id' => (int) $category->order_id, + ]; array_push($feeds, $row); } } @@ -594,51 +582,36 @@ class API extends Handler { /* Real feeds */ - if ($limit) { - $limit_qpart = "LIMIT $limit OFFSET $offset"; - } else { - $limit_qpart = ""; - } - /* API only: -3 All feeds, excluding virtual feeds (e.g. Labels and such) */ - if ($cat_id == -4 || $cat_id == -3) { - $sth = $pdo->prepare("SELECT - id, feed_url, cat_id, title, order_id, ". - SUBSTRING_FOR_DATE."(last_updated,1,19) AS last_updated - FROM ttrss_feeds WHERE owner_uid = ? - ORDER BY order_id, title " . $limit_qpart); - $sth->execute([$_SESSION['uid']]); - - } else { - - $sth = $pdo->prepare("SELECT - id, feed_url, cat_id, title, order_id, ". - SUBSTRING_FOR_DATE."(last_updated,1,19) AS last_updated - FROM ttrss_feeds WHERE - (cat_id = :cat OR (:cat = 0 AND cat_id IS NULL)) - AND owner_uid = :uid - ORDER BY order_id, title " . $limit_qpart); - $sth->execute([":uid" => $_SESSION['uid'], ":cat" => $cat_id]); + $feeds_obj = ORM::for_table('ttrss_feeds') + ->select_many('id', 'feed_url', 'cat_id', 'title', 'order_id') + ->select_expr(SUBSTRING_FOR_DATE.'(last_updated,1,19)', 'last_updated') + ->where('owner_uid', $_SESSION['uid']) + ->order_by_asc('order_id') + ->order_by_asc('title'); + + if ($limit) $feeds_obj->limit($limit); + if ($offset) $feeds_obj->offset($offset); + + if ($cat_id != -3 && $cat_id != -4) { + $feeds_obj->where_raw('(cat_id = ? OR (? = 0 AND cat_id IS NULL))', [$cat_id, $cat_id]); } - while ($line = $sth->fetch()) { - - $unread = getFeedUnread($line["id"]); - - $has_icon = Feeds::_has_icon($line['id']); + foreach ($feeds_obj->find_many() as $feed) { + $unread = getFeedUnread($feed->id); + $has_icon = Feeds::_has_icon($feed->id); if ($unread || !$unread_only) { - - $row = array( - "feed_url" => $line["feed_url"], - "title" => $line["title"], - "id" => (int)$line["id"], - "unread" => (int)$unread, - "has_icon" => $has_icon, - "cat_id" => (int)$line["cat_id"], - "last_updated" => (int) strtotime($line["last_updated"]), - "order_id" => (int) $line["order_id"], - ); + $row = [ + 'feed_url' => $feed->feed_url, + 'title' => $feed->title, + 'id' => (int) $feed->id, + 'unread' => (int) $unread, + 'has_icon' => $has_icon, + 'cat_id' => (int) $feed->cat_id, + 'last_updated' => (int) strtotime($feed->last_updated), + 'order_id' => (int) $feed->order_id, + ]; array_push($feeds, $row); } @@ -653,26 +626,24 @@ class API extends Handler { $search = "", $include_nested = false, $sanitize_content = true, $force_update = false, $excerpt_length = 100, $check_first_id = false, $skip_first_id_check = false) { - $pdo = Db::pdo(); - if ($force_update && $feed_id > 0 && is_numeric($feed_id)) { // Update the feed if required with some basic flood control - $sth = $pdo->prepare( - "SELECT cache_images,".SUBSTRING_FOR_DATE."(last_updated,1,19) AS last_updated - FROM ttrss_feeds WHERE id = ?"); - $sth->execute([$feed_id]); + $feed = ORM::for_table('ttrss_feeds') + ->select_many('id', 'cache_images') + ->select_expr(SUBSTRING_FOR_DATE.'(last_updated,1,19)', 'last_updated') + ->find_one($feed_id); - if ($row = $sth->fetch()) { - $last_updated = strtotime($row["last_updated"]); - $cache_images = self::_param_to_bool($row["cache_images"]); + if ($feed) { + $last_updated = strtotime($feed->last_updated); + $cache_images = self::_param_to_bool($feed->cache_images); if (!$cache_images && time() - $last_updated > 120) { RSSUtils::update_rss_feed($feed_id, true); } else { - $sth = $pdo->prepare("UPDATE ttrss_feeds SET last_updated = '1970-01-01', last_update_started = '1970-01-01' - WHERE id = ?"); - $sth->execute([$feed_id]); + $feed->last_updated = '1970-01-01'; + $feed->last_update_started = '1970-01-01'; + $feed->save(); } } } @@ -792,7 +763,7 @@ class API extends Handler { list ($flavor_image, $flavor_stream, $flavor_kind) = Article::_get_image($enclosures, $line["content"], // unsanitized - $line["site_url"], + $line["site_url"] ?? "", // could be null if archived article $headline_row); $headline_row["flavor_image"] = $flavor_image; @@ -823,15 +794,15 @@ class API extends Handler { function unsubscribeFeed() { $feed_id = (int) clean($_REQUEST["feed_id"]); - $sth = $this->pdo->prepare("SELECT id FROM ttrss_feeds WHERE - id = ? AND owner_uid = ?"); - $sth->execute([$feed_id, $_SESSION['uid']]); + $feed_exists = ORM::for_table('ttrss_feeds') + ->where(['id' => $feed_id, 'owner_uid' => $_SESSION['uid']]) + ->count(); - if ($row = $sth->fetch()) { - Pref_Feeds::remove_feed($feed_id, $_SESSION["uid"]); - $this->_wrap(self::STATUS_OK, array("status" => "OK")); + if ($feed_exists) { + Pref_Feeds::remove_feed($feed_id, $_SESSION['uid']); + $this->_wrap(self::STATUS_OK, ['status' => 'OK']); } else { - $this->_wrap(self::STATUS_ERR, array("error" => self::E_OPERATION_FAILED)); + $this->_wrap(self::STATUS_ERR, ['error' => self::E_OPERATION_FAILED]); } } @@ -864,27 +835,21 @@ class API extends Handler { // only works for labels or uncategorized for the time being private function _is_cat_empty($id) { - if ($id == -2) { - $sth = $this->pdo->prepare("SELECT COUNT(id) AS count FROM ttrss_labels2 - WHERE owner_uid = ?"); - $sth->execute([$_SESSION['uid']]); - $row = $sth->fetch(); - - return $row["count"] == 0; + $label_count = ORM::for_table('ttrss_labels2') + ->where('owner_uid', $_SESSION['uid']) + ->count(); + return $label_count == 0; } else if ($id == 0) { - $sth = $this->pdo->prepare("SELECT COUNT(id) AS count FROM ttrss_feeds - WHERE cat_id IS NULL AND owner_uid = ?"); - $sth->execute([$_SESSION['uid']]); - $row = $sth->fetch(); - - return $row["count"] == 0; + $uncategorized_count = ORM::for_table('ttrss_feeds') + ->where_null('cat_id') + ->where('owner_uid', $_SESSION['uid']) + ->count(); + return $uncategorized_count == 0; } return false; } - - } diff --git a/classes/config.php b/classes/config.php index 567a019c6..6e8d4533f 100644 --- a/classes/config.php +++ b/classes/config.php @@ -6,9 +6,22 @@ class Config { const T_STRING = 2; const T_INT = 3; - const SCHEMA_VERSION = 144; + const SCHEMA_VERSION = 145; - // override defaults, defined below in _DEFAULTS[], via environment: DB_TYPE becomes TTRSS_DB_TYPE, etc + /* override defaults, defined below in _DEFAULTS[], prefixing with _ENVVAR_PREFIX: + + DB_TYPE becomes: + + .env: + + TTRSS_DB_TYPE=pgsql + + or config.php: + + putenv('TTRSS_DB_TYPE=pgsql'); + + etc, etc. + */ const DB_TYPE = "DB_TYPE"; const DB_HOST = "DB_HOST"; @@ -16,48 +29,148 @@ class Config { const DB_NAME = "DB_NAME"; const DB_PASS = "DB_PASS"; const DB_PORT = "DB_PORT"; + // database credentials + const MYSQL_CHARSET = "MYSQL_CHARSET"; + // connection charset for MySQL. if you have a legacy database and/or experience + // garbage unicode characters with this option, try setting it to a blank string. + const SELF_URL_PATH = "SELF_URL_PATH"; + // this should be set to a fully qualified URL used to access + // your tt-rss instance over the net, such as: https://example.com/tt-rss/ + // if your tt-rss instance is behind a reverse proxy, use external URL. + // tt-rss will likely help you pick correct value for this on startup + const SINGLE_USER_MODE = "SINGLE_USER_MODE"; + // operate in single user mode, disables all functionality related to + // multiple users and authentication. enabling this assumes you have + // your tt-rss directory protected by other means (e.g. http auth). + const SIMPLE_UPDATE_MODE = "SIMPLE_UPDATE_MODE"; + // enables fallback update mode where tt-rss tries to update feeds in + // background while tt-rss is open in your browser. + // if you don't have a lot of feeds and don't want to or can't run + // background processes while not running tt-rss, this method is generally + // viable to keep your feeds up to date. + const PHP_EXECUTABLE = "PHP_EXECUTABLE"; + // use this PHP CLI executable to start various tasks + const LOCK_DIRECTORY = "LOCK_DIRECTORY"; + // base directory for lockfiles (must be writable) + const CACHE_DIR = "CACHE_DIR"; + // base directory for local cache (must be writable) + const ICONS_DIR = "ICONS_DIR"; const ICONS_URL = "ICONS_URL"; + // directory and URL for feed favicons (directory must be writable) + const AUTH_AUTO_CREATE = "AUTH_AUTO_CREATE"; + // auto create users authenticated via external modules + const AUTH_AUTO_LOGIN = "AUTH_AUTO_LOGIN"; + // auto log in users authenticated via external modules i.e. auth_remote + const FORCE_ARTICLE_PURGE = "FORCE_ARTICLE_PURGE"; + // unconditinally purge all articles older than this amount, in days + // overrides user-controlled purge interval + const SESSION_COOKIE_LIFETIME = "SESSION_COOKIE_LIFETIME"; + // default lifetime of a session (e.g. login) cookie. In seconds, + // 0 means cookie will be deleted when browser closes. + const SMTP_FROM_NAME = "SMTP_FROM_NAME"; const SMTP_FROM_ADDRESS = "SMTP_FROM_ADDRESS"; + // send email using this name and address + const DIGEST_SUBJECT = "DIGEST_SUBJECT"; + // default subject for email digest + const CHECK_FOR_UPDATES = "CHECK_FOR_UPDATES"; + // enable built-in update checker, both for core code and plugins (using git) + const PLUGINS = "PLUGINS"; + // system plugins enabled for all users, comma separated list, no quotes + // keep at least one auth module in there (i.e. auth_internal) + const LOG_DESTINATION = "LOG_DESTINATION"; + // available options: sql (default, event log), syslog, stdout (for debugging) + const LOCAL_OVERRIDE_STYLESHEET = "LOCAL_OVERRIDE_STYLESHEET"; + // link this stylesheet on all pages (if it exists), should be placed in themes.local + + const LOCAL_OVERRIDE_JS = "LOCAL_OVERRIDE_JS"; + // same but this javascript file (you can use that for polyfills), should be placed in themes.local + const DAEMON_MAX_CHILD_RUNTIME = "DAEMON_MAX_CHILD_RUNTIME"; + // in seconds, terminate update tasks that ran longer than this interval + const DAEMON_MAX_JOBS = "DAEMON_MAX_JOBS"; + // max concurrent update jobs forking update daemon starts + const FEED_FETCH_TIMEOUT = "FEED_FETCH_TIMEOUT"; + // How long to wait for response when requesting feed from a site (seconds) + const FEED_FETCH_NO_CACHE_TIMEOUT = "FEED_FETCH_NO_CACHE_TIMEOUT"; + // Same but not cached + const FILE_FETCH_TIMEOUT = "FILE_FETCH_TIMEOUT"; + // Default timeout when fetching files from remote sites + const FILE_FETCH_CONNECT_TIMEOUT = "FILE_FETCH_CONNECT_TIMEOUT"; + // How long to wait for initial response from website when fetching files from remote sites + const DAEMON_UPDATE_LOGIN_LIMIT = "DAEMON_UPDATE_LOGIN_LIMIT"; + // stop updating feeds if user haven't logged in for X days + const DAEMON_FEED_LIMIT = "DAEMON_FEED_LIMIT"; + // how many feeds to update in one batch + const DAEMON_SLEEP_INTERVAL = "DAEMON_SLEEP_INTERVAL"; + // default sleep interval between feed updates (sec) + const MAX_CACHE_FILE_SIZE = "MAX_CACHE_FILE_SIZE"; + // do not cache files larger than that (bytes) + const MAX_DOWNLOAD_FILE_SIZE = "MAX_DOWNLOAD_FILE_SIZE"; + // do not download files larger than that (bytes) + const MAX_FAVICON_FILE_SIZE = "MAX_FAVICON_FILE_SIZE"; + // max file size for downloaded favicons (bytes) + const CACHE_MAX_DAYS = "CACHE_MAX_DAYS"; + // max age in days for various automatically cached (temporary) files + const MAX_CONDITIONAL_INTERVAL = "MAX_CONDITIONAL_INTERVAL"; + // max interval between forced unconditional updates for servers not complying with http if-modified-since (seconds) + const DAEMON_UNSUCCESSFUL_DAYS_LIMIT = "DAEMON_UNSUCCESSFUL_DAYS_LIMIT"; + // automatically disable updates for feeds which failed to + // update for this amount of days; 0 disables + const LOG_SENT_MAIL = "LOG_SENT_MAIL"; + // log all sent emails in the event log + const HTTP_PROXY = "HTTP_PROXY"; + // use HTTP proxy for requests + const FORBID_PASSWORD_CHANGES = "FORBID_PASSWORD_CHANGES"; + // prevent users from changing passwords + const SESSION_NAME = "SESSION_NAME"; + // default session cookie name + const CHECK_FOR_PLUGIN_UPDATES = "CHECK_FOR_PLUGIN_UPDATES"; + // enable plugin update checker (using git) + const ENABLE_PLUGIN_INSTALLER = "ENABLE_PLUGIN_INSTALLER"; + // allow installing first party plugins using plugin installer in prefs + + const AUTH_MIN_INTERVAL = "AUTH_MIN_INTERVAL"; + // minimum amount of seconds required between authentication attempts + // default values for all of the above: private const _DEFAULTS = [ Config::DB_TYPE => [ "pgsql", Config::T_STRING ], Config::DB_HOST => [ "db", Config::T_STRING ], @@ -87,6 +200,8 @@ class Config { Config::LOG_DESTINATION => [ Logger::LOG_DEST_SQL, Config::T_STRING ], Config::LOCAL_OVERRIDE_STYLESHEET => [ "local-overrides.css", Config::T_STRING ], + Config::LOCAL_OVERRIDE_JS => [ "local-overrides.js", + Config::T_STRING ], Config::DAEMON_MAX_CHILD_RUNTIME => [ 1800, Config::T_INT ], Config::DAEMON_MAX_JOBS => [ 2, Config::T_INT ], Config::FEED_FETCH_TIMEOUT => [ 45, Config::T_INT ], @@ -108,6 +223,7 @@ class Config { Config::SESSION_NAME => [ "ttrss_sid", Config::T_STRING ], Config::CHECK_FOR_PLUGIN_UPDATES => [ "true", Config::T_BOOL ], Config::ENABLE_PLUGIN_INSTALLER => [ "true", Config::T_BOOL ], + Config::AUTH_MIN_INTERVAL => [ 5, Config::T_INT ], ]; private static $instance; @@ -503,4 +619,16 @@ class Config { private static function format_error($msg) { return "<div class=\"alert alert-danger\">$msg</div>"; } + + static function get_override_links() { + $rv = ""; + + $local_css = get_theme_path(self::get(self::LOCAL_OVERRIDE_STYLESHEET)); + if ($local_css) $rv .= stylesheet_tag($local_css); + + $local_js = get_theme_path(self::get(self::LOCAL_OVERRIDE_JS)); + if ($local_js) $rv .= javascript_tag($local_js); + + return $rv; + } } diff --git a/classes/db.php b/classes/db.php index 008275bca..a09c44628 100755 --- a/classes/db.php +++ b/classes/db.php @@ -14,6 +14,9 @@ class Db ORM::configure('username', Config::get(Config::DB_USER)); ORM::configure('password', Config::get(Config::DB_PASS)); ORM::configure('return_result_sets', true); + if (Config::get(Config::DB_TYPE) == "mysql" && Config::get(Config::MYSQL_CHARSET)) { + ORM::configure('driver_options', array(PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES ' . Config::get(Config::MYSQL_CHARSET))); + } } static function NOW() { @@ -27,8 +30,13 @@ class Db public static function get_dsn() { $db_port = Config::get(Config::DB_PORT) ? ';port=' . Config::get(Config::DB_PORT) : ''; $db_host = Config::get(Config::DB_HOST) ? ';host=' . Config::get(Config::DB_HOST) : ''; + if (Config::get(Config::DB_TYPE) == "mysql" && Config::get(Config::MYSQL_CHARSET)) { + $db_charset = ';charset=' . Config::get(Config::MYSQL_CHARSET); + } else { + $db_charset = ''; + } - return Config::get(Config::DB_TYPE) . ':dbname=' . Config::get(Config::DB_NAME) . $db_host . $db_port; + return Config::get(Config::DB_TYPE) . ':dbname=' . Config::get(Config::DB_NAME) . $db_host . $db_port . $db_charset; } // this really shouldn't be used unless a separate PDO connection is needed diff --git a/classes/feeds.php b/classes/feeds.php index 68d535481..12d6dd65f 100755 --- a/classes/feeds.php +++ b/classes/feeds.php @@ -2054,8 +2054,7 @@ class Feeds extends Handler_Protected { } private static function _search_to_sql($search, $search_language, $owner_uid) { - - $keywords = str_getcsv(trim($search), " "); + $keywords = str_getcsv(preg_replace('/(-?\w+)\:"(\w+)/', '"${1}:${2}', trim($search)), ' '); $query_keywords = array(); $search_words = array(); $search_query_leftover = array(); diff --git a/classes/handler/public.php b/classes/handler/public.php index 2de073cc2..98042111b 100755 --- a/classes/handler/public.php +++ b/classes/handler/public.php @@ -266,19 +266,20 @@ class Handler_Public extends Handler { $rv = []; if ($login) { - $sth = $this->pdo->prepare("SELECT ttrss_settings_profiles.* FROM ttrss_settings_profiles,ttrss_users - WHERE ttrss_users.id = ttrss_settings_profiles.owner_uid AND LOWER(login) = LOWER(?) ORDER BY title"); - $sth->execute([$login]); + $profiles = ORM::for_table('ttrss_settings_profiles') + ->table_alias('p') + ->select_many('title' , 'p.id') + ->join('ttrss_users', ['owner_uid', '=', 'u.id'], 'u') + ->where_raw('LOWER(login) = LOWER(?)', [$login]) + ->order_by_asc('title') + ->find_many(); $rv = [ [ "value" => 0, "label" => __("Default profile") ] ]; - while ($line = $sth->fetch()) { - $id = $line["id"]; - $title = $line["title"]; - - array_push($rv, [ "label" => $title, "value" => $id ]); + foreach ($profiles as $profile) { + array_push($rv, [ "label" => $profile->title, "value" => $profile->id ]); } - } + } print json_encode($rv); } @@ -312,23 +313,20 @@ class Handler_Public extends Handler { UserHelper::authenticate("admin", null); } - $owner_id = false; - if ($key) { - $sth = $this->pdo->prepare("SELECT owner_uid FROM - ttrss_access_keys WHERE access_key = ? AND feed_id = ?"); - $sth->execute([$key, $feed]); - - if ($row = $sth->fetch()) - $owner_id = $row["owner_uid"]; + $access_key = ORM::for_table('ttrss_access_keys') + ->select('owner_uid') + ->where(['access_key' => $key, 'feed_id' => $feed]) + ->find_one(); + + if ($access_key) { + $this->generate_syndicated_feed($access_key->owner_uid, $feed, $is_cat, $limit, + $offset, $search, $view_mode, $format, $order, $orig_guid, $start_ts); + return; + } } - if ($owner_id) { - $this->generate_syndicated_feed($owner_id, $feed, $is_cat, $limit, - $offset, $search, $view_mode, $format, $order, $orig_guid, $start_ts); - } else { - header('HTTP/1.1 403 Forbidden'); - } + header('HTTP/1.1 403 Forbidden'); } function updateTask() { @@ -373,18 +371,13 @@ class Handler_Public extends Handler { $_SESSION["safe_mode"] = $safe_mode; if (!empty($_POST["profile"])) { - $profile = (int) clean($_POST["profile"]); - $sth = $this->pdo->prepare("SELECT id FROM ttrss_settings_profiles - WHERE id = ? AND owner_uid = ?"); - $sth->execute([$profile, $_SESSION['uid']]); + $profile_obj = ORM::for_table('ttrss_settings_profiles') + ->where(['id' => $profile, 'owner_uid' => $_SESSION['uid']]) + ->find_one(); - if ($sth->fetch()) { - $_SESSION["profile"] = $profile; - } else { - $_SESSION["profile"] = null; - } + $_SESSION["profile"] = $profile_obj ? $profile : null; } } else { @@ -415,7 +408,7 @@ class Handler_Public extends Handler { startup_gettext(); session_start(); - @$hash = clean($_REQUEST["hash"]); + $hash = clean($_REQUEST["hash"] ?? ''); header('Content-Type: text/html; charset=utf-8'); ?> @@ -448,30 +441,27 @@ class Handler_Public extends Handler { print "<h1>".__("Password recovery")."</h1>"; print "<div class='content'>"; - @$method = clean($_POST['method']); + $method = clean($_POST['method'] ?? ''); if ($hash) { $login = clean($_REQUEST["login"]); if ($login) { - $sth = $this->pdo->prepare("SELECT id, resetpass_token FROM ttrss_users - WHERE LOWER(login) = LOWER(?)"); - $sth->execute([$login]); + $user = ORM::for_table('ttrss_users') + ->select('id', 'resetpass_token') + ->where_raw('LOWER(login) = LOWER(?)', [$login]) + ->find_one(); - if ($row = $sth->fetch()) { - $id = $row["id"]; - $resetpass_token_full = $row["resetpass_token"]; - list($timestamp, $resetpass_token) = explode(":", $resetpass_token_full); + if ($user) { + list($timestamp, $resetpass_token) = explode(":", $user->resetpass_token); if ($timestamp && $resetpass_token && $timestamp >= time() - 15*60*60 && $resetpass_token === $hash) { + $user->resetpass_token = null; + $user->save(); - $sth = $this->pdo->prepare("UPDATE ttrss_users SET resetpass_token = NULL - WHERE id = ?"); - $sth->execute([$id]); - - UserHelper::reset_password($id, true); + UserHelper::reset_password($user->id, true); print "<p>"."Completed."."</p>"; @@ -520,7 +510,6 @@ class Handler_Public extends Handler { </form>"; } else if ($method == 'do') { - $login = clean($_POST["login"]); $email = clean($_POST["email"]); $test = clean($_POST["test"]); @@ -532,64 +521,51 @@ class Handler_Public extends Handler { <input type='hidden' name='op' value='forgotpass'> <button dojoType='dijit.form.Button' type='submit' class='alt-primary'>".__("Go back")."</button> </form>"; - } else { - // prevent submitting this form multiple times $_SESSION["pwdreset:testvalue1"] = rand(1, 1000); $_SESSION["pwdreset:testvalue2"] = rand(1, 1000); - $sth = $this->pdo->prepare("SELECT id FROM ttrss_users - WHERE LOWER(login) = LOWER(?) AND email = ?"); - $sth->execute([$login, $email]); + $user = ORM::for_table('ttrss_users') + ->select('id') + ->where_raw('LOWER(login) = LOWER(?)', [$login]) + ->where('email', $email) + ->find_one(); - if ($row = $sth->fetch()) { + if ($user) { print_notice("Password reset instructions are being sent to your email address."); - $id = $row["id"]; - - if ($id) { - $resetpass_token = sha1(get_random_bytes(128)); - $resetpass_link = get_self_url_prefix() . "/public.php?op=forgotpass&hash=" . $resetpass_token . - "&login=" . urlencode($login); + $resetpass_token = sha1(get_random_bytes(128)); + $resetpass_link = get_self_url_prefix() . "/public.php?op=forgotpass&hash=" . $resetpass_token . + "&login=" . urlencode($login); - $tpl = new Templator(); + $tpl = new Templator(); - $tpl->readTemplateFromFile("resetpass_link_template.txt"); + $tpl->readTemplateFromFile("resetpass_link_template.txt"); - $tpl->setVariable('LOGIN', $login); - $tpl->setVariable('RESETPASS_LINK', $resetpass_link); - $tpl->setVariable('TTRSS_HOST', Config::get(Config::SELF_URL_PATH)); + $tpl->setVariable('LOGIN', $login); + $tpl->setVariable('RESETPASS_LINK', $resetpass_link); + $tpl->setVariable('TTRSS_HOST', Config::get(Config::SELF_URL_PATH)); - $tpl->addBlock('message'); + $tpl->addBlock('message'); - $message = ""; + $message = ""; - $tpl->generateOutputToString($message); + $tpl->generateOutputToString($message); - $mailer = new Mailer(); + $mailer = new Mailer(); - $rc = $mailer->mail(["to_name" => $login, - "to_address" => $email, - "subject" => __("[tt-rss] Password reset request"), - "message" => $message]); + $rc = $mailer->mail(["to_name" => $login, + "to_address" => $email, + "subject" => __("[tt-rss] Password reset request"), + "message" => $message]); - if (!$rc) print_error($mailer->error()); + if (!$rc) print_error($mailer->error()); - $resetpass_token_full = time() . ":" . $resetpass_token; - - $sth = $this->pdo->prepare("UPDATE ttrss_users - SET resetpass_token = ? - WHERE LOWER(login) = LOWER(?) AND email = ?"); - - $sth->execute([$resetpass_token_full, $login, $email]); - - } else { - print_error("User ID not found."); - } + $user->resetpass_token = time() . ":" . $resetpass_token; + $user->save(); print "<a href='index.php'>".__("Return to Tiny Tiny RSS")."</a>"; - } else { print_error(__("Sorry, login and email combination not found.")); @@ -597,17 +573,14 @@ class Handler_Public extends Handler { <input type='hidden' name='op' value='forgotpass'> <button dojoType='dijit.form.Button' type='submit'>".__("Go back")."</button> </form>"; - } } - } print "</div>"; print "</div>"; print "</body>"; print "</html>"; - } function dbupdate() { @@ -638,9 +611,7 @@ class Handler_Public extends Handler { } ?> - <?php if (theme_exists(Config::get(Config::LOCAL_OVERRIDE_STYLESHEET))) { - echo stylesheet_tag(get_theme_path(Config::get(Config::LOCAL_OVERRIDE_STYLESHEET))); - } ?> + <?= Config::get_override_links() ?> <style type="text/css"> @media (prefers-color-scheme: dark) { @@ -755,27 +726,6 @@ class Handler_Public extends Handler { <?php } - function publishOpml() { - $key = clean($_REQUEST["key"]); - $pdo = Db::pdo(); - - $sth = $pdo->prepare( "SELECT owner_uid - FROM ttrss_access_keys WHERE - access_key = ? AND feed_id = 'OPML:Publish'"); - $sth->execute([$key]); - - if ($row = $sth->fetch()) { - $owner_uid = $row['owner_uid']; - - $opml = new OPML($_REQUEST); - $opml->opml_export("published.opml", $owner_uid, true, false); - - } else { - header($_SERVER["SERVER_PROTOCOL"]." 404 Not Found"); - echo "File not found."; - } - } - function cached() { list ($cache_dir, $filename) = explode("/", $_GET["file"], 2); diff --git a/classes/logger.php b/classes/logger.php index f8abb5f84..42ab4452c 100755 --- a/classes/logger.php +++ b/classes/logger.php @@ -46,7 +46,7 @@ class Logger { if ($this->adapter) return $this->adapter->log_error($errno, $errstr, '', 0, $context); else - return false; + return user_error($errstr, $errno); } private function __clone() { diff --git a/classes/mailer.php b/classes/mailer.php index 564338f69..8238904ee 100644 --- a/classes/mailer.php +++ b/classes/mailer.php @@ -4,7 +4,7 @@ class Mailer { function mail($params) { - $to_name = $params["to_name"]; + $to_name = $params["to_name"] ?? ""; $to_address = $params["to_address"]; $subject = $params["subject"]; $message = $params["message"]; diff --git a/classes/opml.php b/classes/opml.php index f8e9f6728..2cfc890fa 100644 --- a/classes/opml.php +++ b/classes/opml.php @@ -633,12 +633,6 @@ class OPML extends Handler_Protected { print "$msg<br/>"; } - static function get_publish_url(){ - return Config::get_self_url() . - "/public.php?op=publishOpml&key=" . - Feeds::_get_access_key('OPML:Publish', false, $_SESSION["uid"]); - } - function get_feed_category($feed_cat, $parent_cat_id = false) { $parent_cat_id = (int) $parent_cat_id; diff --git a/classes/pluginhost.php b/classes/pluginhost.php index 17d1e0c5f..ee4107ae7 100755 --- a/classes/pluginhost.php +++ b/classes/pluginhost.php @@ -352,7 +352,7 @@ class PluginHost { $method = strtolower($method); if ($this->is_system($sender)) { - if (!is_array($this->handlers[$handler])) { + if (!isset($this->handlers[$handler])) { $this->handlers[$handler] = array(); } @@ -469,6 +469,29 @@ class PluginHost { } } + // same as set(), but sets data to current preference profile + function profile_set(Plugin $sender, string $name, $value) { + $profile_id = $_SESSION["profile"] ?? null; + + if ($profile_id) { + $idx = get_class($sender); + + if (!isset($this->storage[$idx])) { + $this->storage[$idx] = []; + } + + if (!isset($this->storage[$idx][$profile_id])) { + $this->storage[$idx][$profile_id] = []; + } + + $this->storage[$idx][$profile_id][$name] = $value; + + $this->save_data(get_class($sender)); + } else { + return $this->set($sender, $name, $value); + } + } + function set(Plugin $sender, string $name, $value) { $idx = get_class($sender); @@ -492,6 +515,26 @@ class PluginHost { $this->save_data(get_class($sender)); } + // same as get(), but sets data to current preference profile + function profile_get(Plugin $sender, string $name, $default_value = false) { + $profile_id = $_SESSION["profile"] ?? null; + + if ($profile_id) { + $idx = get_class($sender); + + $this->load_data(); + + if (isset($this->storage[$idx][$profile_id][$name])) { + return $this->storage[$idx][$profile_id][$name]; + } else { + return $default_value; + } + + } else { + return $this->get($sender, $name, $default_value); + } + } + function get(Plugin $sender, string $name, $default_value = false) { $idx = get_class($sender); diff --git a/classes/pref/feeds.php b/classes/pref/feeds.php index 788104d38..5f7635736 100755 --- a/classes/pref/feeds.php +++ b/classes/pref/feeds.php @@ -1012,17 +1012,6 @@ class Pref_Feeds extends Handler_Protected { </label> </form> - <hr/> - - <h2><?= __("Published OPML") ?></h2> - - <?= format_notice("Your OPML can be published and then subscribed by anyone who knows the URL below. This won't include your settings nor authenticated feeds.") ?> - - <button dojoType='dijit.form.Button' class='alt-primary' onclick="return Helpers.OPML.publish()"> - <?= \Controls\icon("share") ?> - <?= __('Display published OPML URL') ?> - </button> - <?php PluginHost::getInstance()->run_hooks(PluginHost::HOOK_PREFS_TAB_SECTION, "prefFeedsOPML"); } @@ -1251,17 +1240,6 @@ class Pref_Feeds extends Handler_Protected { return Feeds::_clear_access_keys($_SESSION['uid']); } - function getOPMLKey() { - print json_encode(["link" => OPML::get_publish_url()]); - } - - function regenOPMLKey() { - Feeds::_update_access_key('OPML:Publish', - false, $_SESSION["uid"]); - - print json_encode(["link" => OPML::get_publish_url()]); - } - function regenFeedKey() { $feed_id = clean($_REQUEST['id']); $is_cat = clean($_REQUEST['is_cat']); diff --git a/classes/pref/prefs.php b/classes/pref/prefs.php index 16c41df9d..1eaa99345 100644 --- a/classes/pref/prefs.php +++ b/classes/pref/prefs.php @@ -54,6 +54,7 @@ class Pref_Prefs extends Handler_Protected { 'BLOCK_SEPARATOR', Prefs::COMBINED_DISPLAY_MODE, Prefs::CDM_EXPANDED, + Prefs::CDM_ENABLE_GRID, 'BLOCK_SEPARATOR', Prefs::CDM_AUTO_CATCHUP, Prefs::VFEED_GROUP_BY_FEED, @@ -117,6 +118,7 @@ class Pref_Prefs extends Handler_Protected { Prefs::HEADLINES_NO_DISTINCT => array(__("Don't enforce DISTINCT headlines"), __("May produce duplicate entries")), Prefs::DEBUG_HEADLINE_IDS => array(__("Show article and feed IDs"), __("In the headlines buffer")), Prefs::DISABLE_CONDITIONAL_COUNTERS => array(__("Disable conditional counter updates"), __("May increase server load")), + Prefs::CDM_ENABLE_GRID => array(__("Grid view"), __("On wider screens, if always expanded")), ]; // hidden in the main prefs UI (use to hide things that have description set above) @@ -229,29 +231,29 @@ class Pref_Prefs extends Handler_Protected { if ($user) { $user->full_name = clean($_POST['full_name']); - if ($user->email != $new_email) + if ($user->email != $new_email) { Logger::log(E_USER_NOTICE, "Email address of user ".$user->login." has been changed to ${new_email}."); - if ($user->email && $user->email != $new_email) { + if ($user->email) { + $mailer = new Mailer(); - $mailer = new Mailer(); + $tpl = new Templator(); - $tpl = new Templator(); + $tpl->readTemplateFromFile("mail_change_template.txt"); - $tpl->readTemplateFromFile("mail_change_template.txt"); + $tpl->setVariable('LOGIN', $user->login); + $tpl->setVariable('NEWMAIL', $new_email); + $tpl->setVariable('TTRSS_HOST', Config::get(Config::SELF_URL_PATH)); - $tpl->setVariable('LOGIN', $user->login); - $tpl->setVariable('NEWMAIL', $new_email); - $tpl->setVariable('TTRSS_HOST', Config::get(Config::SELF_URL_PATH)); - - $tpl->addBlock('message'); + $tpl->addBlock('message'); - $tpl->generateOutputToString($message); + $tpl->generateOutputToString($message); - $mailer->mail(["to_name" => $user->login, - "to_address" => $user->email, - "subject" => "[tt-rss] Email address change notification", - "message" => $message]); + $mailer->mail(["to_name" => $user->login, + "to_address" => $user->email, + "subject" => "[tt-rss] Email address change notification", + "message" => $message]); + } $user->email = $new_email; } @@ -467,8 +469,8 @@ class Pref_Prefs extends Handler_Protected { <?= \Controls\hidden_tag("method", "otpenable") ?> <fieldset> - <label><?= __("OTP Key:") ?></label> - <input dojoType='dijit.form.ValidationTextBox' disabled='disabled' value="<?= $otp_secret ?>" style='width : 215px'> + <label><?= __("OTP secret:") ?></label> + <code><?= $this->format_otp_secret($otp_secret) ?></code> </fieldset> <!-- TODO: return JSON from the backend call --> @@ -494,7 +496,7 @@ class Pref_Prefs extends Handler_Protected { </fieldset> <fieldset> - <label><?= __("One time password:") ?></label> + <label><?= __("Verification code:") ?></label> <input dojoType='dijit.form.ValidationTextBox' autocomplete='off' required='1' name='otp'> </fieldset> @@ -1434,10 +1436,10 @@ class Pref_Prefs extends Handler_Protected { <div class='panel panel-scrollable'> <table width='100%' id='app-password-list'> <tr> - <th width='2%'> </th> - <th align='left'><?= __("Description") ?></th> - <th align='right'><?= __("Created") ?></th> - <th align='right'><?= __("Last used") ?></th> + <th class="checkbox"> </th> + <th width='50%'><?= __("Description") ?></th> + <th><?= __("Created") ?></th> + <th><?= __("Last used") ?></th> </tr> <?php @@ -1448,16 +1450,16 @@ class Pref_Prefs extends Handler_Protected { foreach ($passwords as $pass) { ?> <tr data-row-id='<?= $pass['id'] ?>'> - <td align='center'> + <td class="checkbox"> <input onclick='Tables.onRowChecked(this)' dojoType='dijit.form.CheckBox' type='checkbox'> </td> <td> <?= htmlspecialchars($pass["title"]) ?> </td> - <td align='right' class='text-muted'> + <td class='text-muted'> <?= TimeHelper::make_local_datetime($pass['created'], false) ?> </td> - <td align='right' class='text-muted'> + <td class='text-muted'> <?= TimeHelper::make_local_datetime($pass['last_used'], false) ?> </td> </tr> @@ -1516,4 +1518,8 @@ class Pref_Prefs extends Handler_Protected { } return ""; } + + private function format_otp_secret($secret) { + return implode(" ", str_split($secret, 4)); + } } diff --git a/classes/pref/system.php b/classes/pref/system.php index c79b5095d..8bebcc7ce 100644 --- a/classes/pref/system.php +++ b/classes/pref/system.php @@ -42,10 +42,10 @@ class Pref_System extends Handler_Administrative { switch ($severity) { case E_USER_ERROR: - $errno_values = [ E_ERROR, E_USER_ERROR, E_PARSE ]; + $errno_values = [ E_ERROR, E_USER_ERROR, E_PARSE, E_COMPILE_ERROR ]; break; case E_USER_WARNING: - $errno_values = [ E_ERROR, E_USER_ERROR, E_PARSE, E_WARNING, E_USER_WARNING, E_DEPRECATED, E_USER_DEPRECATED ]; + $errno_values = [ E_ERROR, E_USER_ERROR, E_PARSE, E_COMPILE_ERROR, E_WARNING, E_USER_WARNING, E_DEPRECATED, E_USER_DEPRECATED ]; break; } diff --git a/classes/pref/users.php b/classes/pref/users.php index 2e3dc4b67..76a879efd 100644 --- a/classes/pref/users.php +++ b/classes/pref/users.php @@ -117,7 +117,7 @@ class Pref_Users extends Handler_Administrative { $user->login = mb_strtolower($login); $user->access_level = (int) clean($_REQUEST["access_level"]); $user->email = clean($_REQUEST["email"]); - $user->otp_enabled = checkbox_to_sql_bool($_REQUEST["otp_enabled"]); + $user->otp_enabled = checkbox_to_sql_bool($_REQUEST["otp_enabled"] ?? ""); // force new OTP secret when next enabled if (Config::get_schema_version() >= 143 && !$user->otp_enabled) { diff --git a/classes/prefs.php b/classes/prefs.php index 24f0f7a80..85e7c34db 100644 --- a/classes/prefs.php +++ b/classes/prefs.php @@ -60,6 +60,7 @@ class Prefs { const DEBUG_HEADLINE_IDS = "DEBUG_HEADLINE_IDS"; const DISABLE_CONDITIONAL_COUNTERS = "DISABLE_CONDITIONAL_COUNTERS"; const WIDESCREEN_MODE = "WIDESCREEN_MODE"; + const CDM_ENABLE_GRID = "CDM_ENABLE_GRID"; private const _DEFAULTS = [ Prefs::PURGE_OLD_DAYS => [ 60, Config::T_INT ], @@ -120,6 +121,7 @@ class Prefs { Prefs::DEBUG_HEADLINE_IDS => [ false, Config::T_BOOL ], Prefs::DISABLE_CONDITIONAL_COUNTERS => [ false, Config::T_BOOL ], Prefs::WIDESCREEN_MODE => [ false, Config::T_BOOL ], + Prefs::CDM_ENABLE_GRID => [ false, Config::T_BOOL ], ]; const _PROFILE_BLACKLIST = [ diff --git a/classes/rpc.php b/classes/rpc.php index 35125ae04..94b29ec44 100755 --- a/classes/rpc.php +++ b/classes/rpc.php @@ -431,7 +431,7 @@ class RPC extends Handler_Protected { Prefs::ENABLE_FEED_CATS, Prefs::FEEDS_SORT_BY_UNREAD, Prefs::CONFIRM_FEED_CATCHUP, Prefs::CDM_AUTO_CATCHUP, Prefs::FRESH_ARTICLE_MAX_AGE, Prefs::HIDE_READ_SHOWS_SPECIAL, - Prefs::COMBINED_DISPLAY_MODE, Prefs::DEBUG_HEADLINE_IDS] as $param) { + Prefs::COMBINED_DISPLAY_MODE, Prefs::DEBUG_HEADLINE_IDS, Prefs::CDM_ENABLE_GRID] as $param) { $params[strtolower($param)] = (int) get_pref($param); } @@ -472,6 +472,9 @@ class RPC extends Handler_Protected { $params["widescreen"] = (int) get_pref(Prefs::WIDESCREEN_MODE); $params['simple_update'] = Config::get(Config::SIMPLE_UPDATE_MODE); $params["icon_indicator_white"] = $this->image_to_base64("images/indicator_white.gif"); + $params["icon_oval"] = $this->image_to_base64("images/oval.svg"); + $params["icon_three_dots"] = $this->image_to_base64("images/three-dots.svg"); + $params["icon_blank"] = $this->image_to_base64("images/blank_icon.gif"); $params["labels"] = Labels::get_all($_SESSION["uid"]); return $params; @@ -481,6 +484,8 @@ class RPC extends Handler_Protected { if (file_exists($filename)) { $ext = pathinfo($filename, PATHINFO_EXTENSION); + if ($ext == "svg") $ext = "svg+xml"; + return "data:image/$ext;base64," . base64_encode((string)file_get_contents($filename)); } else { return ""; @@ -603,6 +608,7 @@ class RPC extends Handler_Protected { "feed_catchup" => __("Mark as read"), "feed_reverse" => __("Reverse headlines"), "feed_toggle_vgroup" => __("Toggle headline grouping"), + "feed_toggle_grid" => __("Toggle grid view"), "feed_debug_update" => __("Debug feed update"), "feed_debug_viewfeed" => __("Debug viewfeed()"), "catchup_all" => __("Mark all feeds as read"), @@ -663,6 +669,7 @@ class RPC extends Handler_Protected { "a e" => "toggle_full_text", "e" => "email_article", "a q" => "close_article", + "a s" => "article_span_grid", "a a" => "select_all", "a u" => "select_unread", "a U" => "select_marked", @@ -676,8 +683,9 @@ class RPC extends Handler_Protected { "f q" => "feed_catchup", "f x" => "feed_reverse", "f g" => "feed_toggle_vgroup", + "f G" => "feed_toggle_grid", "f D" => "feed_debug_update", - "f G" => "feed_debug_viewfeed", + "f %" => "feed_debug_viewfeed", "f C" => "toggle_combined_mode", "f c" => "toggle_cdm_expanded", "Q" => "catchup_all", diff --git a/classes/rssutils.php b/classes/rssutils.php index e6bf08ab1..216792a0e 100755 --- a/classes/rssutils.php +++ b/classes/rssutils.php @@ -269,40 +269,33 @@ class RSSUtils { return $nf; } - /** this is used when subscribing; TODO: update to ORM */ - static function update_basic_info(int $feed) { - - $pdo = Db::pdo(); - - $sth = $pdo->prepare("SELECT owner_uid,feed_url,auth_pass,auth_login - FROM ttrss_feeds WHERE id = ?"); - $sth->execute([$feed]); - - if ($row = $sth->fetch()) { - - $owner_uid = $row["owner_uid"]; - $auth_login = $row["auth_login"]; - $auth_pass = $row["auth_pass"]; - $fetch_url = $row["feed_url"]; + /** this is used when subscribing */ + static function update_basic_info(int $feed_id) { + $feed = ORM::for_table('ttrss_feeds') + ->select_many('id', 'owner_uid', 'feed_url', 'auth_pass', 'auth_login', 'title', 'site_url') + ->find_one($feed_id); + if ($feed) { $pluginhost = new PluginHost(); - $user_plugins = get_pref(Prefs::_ENABLED_PLUGINS, $owner_uid); + $user_plugins = get_pref(Prefs::_ENABLED_PLUGINS, $feed->owner_uid); $pluginhost->load(Config::get(Config::PLUGINS), PluginHost::KIND_ALL); - $pluginhost->load((string)$user_plugins, PluginHost::KIND_USER, $owner_uid); + $pluginhost->load((string)$user_plugins, PluginHost::KIND_USER, $feed->owner_uid); //$pluginhost->load_data(); $basic_info = []; $pluginhost->run_hooks_callback(PluginHost::HOOK_FEED_BASIC_INFO, function ($result) use (&$basic_info) { $basic_info = $result; - }, $basic_info, $fetch_url, $owner_uid, $feed, $auth_login, $auth_pass); + }, $basic_info, $feed->feed_url, $feed->owner_uid, $feed_id, $feed->auth_login, $feed->auth_pass); if (!$basic_info) { - $feed_data = UrlHelper::fetch($fetch_url, false, - $auth_login, $auth_pass, false, - Config::get(Config::FEED_FETCH_TIMEOUT), - 0); + $feed_data = UrlHelper::fetch([ + 'url' => $feed->feed_url, + 'login' => $feed->auth_login, + 'pass' => $feed->auth_pass, + 'timeout' => Config::get(Config::FEED_FETCH_TIMEOUT), + ]); $feed_data = trim($feed_data); @@ -310,36 +303,23 @@ class RSSUtils { $rss->init(); if (!$rss->error()) { - $basic_info = array( + $basic_info = [ 'title' => mb_substr(clean($rss->get_title()), 0, 199), - 'site_url' => mb_substr(rewrite_relative_url($fetch_url, clean($rss->get_link())), 0, 245) - ); + 'site_url' => mb_substr(UrlHelper::rewrite_relative($feed->feed_url, clean($rss->get_link())), 0, 245), + ]; } } if ($basic_info && is_array($basic_info)) { - $sth = $pdo->prepare("SELECT title, site_url FROM ttrss_feeds WHERE id = ?"); - $sth->execute([$feed]); - - if ($row = $sth->fetch()) { - - $registered_title = $row["title"]; - $orig_site_url = $row["site_url"]; - - if ($basic_info['title'] && (!$registered_title || $registered_title == "[Unknown]")) { - - $sth = $pdo->prepare("UPDATE ttrss_feeds SET - title = ? WHERE id = ?"); - $sth->execute([$basic_info['title'], $feed]); - } - - if ($basic_info['site_url'] && $orig_site_url != $basic_info['site_url']) { - $sth = $pdo->prepare("UPDATE ttrss_feeds SET - site_url = ? WHERE id = ?"); - $sth->execute([$basic_info['site_url'], $feed]); - } + if (!empty($basic_info['title']) && (!$feed->title || $feed->title == '[Unknown]')) { + $feed->title = $basic_info['title']; + } + if (!empty($basic_info['site_url']) && $feed->site_url != $basic_info['site_url']) { + $feed->site_url = $basic_info['site_url']; } + + $feed->save(); } } } @@ -1422,8 +1402,8 @@ class RSSUtils { $matches = array(); foreach ($filters as $filter) { - $match_any_rule = $filter["match_any_rule"]; - $inverse = $filter["inverse"]; + $match_any_rule = $filter["match_any_rule"] ?? false; + $inverse = $filter["inverse"] ?? false; $filter_match = false; $last_processed_rule = false; @@ -1431,7 +1411,7 @@ class RSSUtils { $match = false; $reg_exp = str_replace('/', '\/', (string)$rule["reg_exp"]); $reg_exp = str_replace("\n", "", $reg_exp); // reg_exp may be formatted with CRs now because of textarea, we need to strip those - $rule_inverse = $rule["inverse"]; + $rule_inverse = $rule["inverse"] ?? false; $last_processed_rule = $rule; if (empty($reg_exp)) @@ -1879,6 +1859,6 @@ class RSSUtils { static function function_enabled($func) { return !in_array($func, - explode(',', (string)ini_get('disable_functions'))); + explode(',', str_replace(" ", "", ini_get('disable_functions')))); } } diff --git a/classes/urlhelper.php b/classes/urlhelper.php index 55d5d1e6a..46d80a0e6 100644 --- a/classes/urlhelper.php +++ b/classes/urlhelper.php @@ -271,10 +271,15 @@ class UrlHelper { // holy shit closures in php // download & upload are *expected* sizes respectively, could be zero - curl_setopt($ch, CURLOPT_PROGRESSFUNCTION, function($curl_handle, $download_size, $downloaded, $upload_size, $uploaded) use( &$max_size) { - Debug::log("[curl progressfunction] $downloaded $max_size", Debug::$LOG_EXTENDED); + curl_setopt($ch, CURLOPT_PROGRESSFUNCTION, function($curl_handle, $download_size, $downloaded, $upload_size, $uploaded) use(&$max_size, $url) { + //Debug::log("[curl progressfunction] $downloaded $max_size", Debug::$LOG_EXTENDED); - return ($downloaded > $max_size) ? 1 : 0; // if max size is set, abort when exceeding it + if ($downloaded > $max_size) { + Debug::log("curl: reached max size of $max_size bytes requesting $url, aborting.", Debug::LOG_VERBOSE); + return 1; + } + + return 0; }); } diff --git a/classes/userhelper.php b/classes/userhelper.php index ce26e6c71..0bf67243e 100644 --- a/classes/userhelper.php +++ b/classes/userhelper.php @@ -299,7 +299,7 @@ class UserHelper { if ($user->otp_enabled) { $user->otp_secret = $salt_based_secret; } else { - $user->otp_secret = bin2hex(get_random_bytes(6)); + $user->otp_secret = bin2hex(get_random_bytes(10)); } $user->save(); diff --git a/gulpfile.js b/gulpfile.js index ca766dcce..9d09e984e 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -12,8 +12,8 @@ function swallowError(error) { gulp.task('less', function(cb) { gulp .src(['themes/compact.less', 'themes/compact_night.less', - 'themes/light.less', 'themes/night_blue.less', 'themes/night.less']) - .pipe(less()) + 'themes/light.less', 'themes/light-high-contrast.less', 'themes/night_blue.less', 'themes/night.less']) + .pipe(less({javascriptEnabled: true})) .on('error', swallowError) .pipe( gulp.dest(function(f) { diff --git a/images/favicon-512px.png b/images/favicon-512px.png Binary files differnew file mode 100644 index 000000000..ac1146be5 --- /dev/null +++ b/images/favicon-512px.png diff --git a/images/logo_small.png b/images/logo_small.png Binary files differdeleted file mode 100644 index f7f28a687..000000000 --- a/images/logo_small.png +++ /dev/null diff --git a/images/oval.svg b/images/oval.svg new file mode 100644 index 000000000..e59d5bf18 --- /dev/null +++ b/images/oval.svg @@ -0,0 +1,17 @@ +<!-- By Sam Herbert (@sherb), for everyone. More @ http://goo.gl/7AJzbL --> +<svg width="38" height="38" viewBox="0 0 38 38" xmlns="http://www.w3.org/2000/svg" stroke="#257aa7"> + <g fill="none" fill-rule="evenodd"> + <g stroke-width="8" transform="matrix(0.83009609,0,0,0.83009609,4.0582705,4.0582705)"> + <circle stroke-opacity=".5" cx="18" cy="18" r="18"/> + <path d="M 36,18 C 36,8.06 27.94,0 18,0"> + <animateTransform + attributeName="transform" + type="rotate" + from="0 18 18" + to="360 18 18" + dur="1s" + repeatCount="indefinite"/> + </path> + </g> + </g> +</svg> diff --git a/images/three-dots.svg b/images/three-dots.svg new file mode 100644 index 000000000..e724ca05e --- /dev/null +++ b/images/three-dots.svg @@ -0,0 +1,33 @@ +<!-- By Sam Herbert (@sherb), for everyone. More @ http://goo.gl/7AJzbL --> +<svg width="120" height="30" viewBox="0 0 120 30" xmlns="http://www.w3.org/2000/svg" fill="#257aa7"> + <circle cx="15" cy="15" r="15"> + <animate attributeName="r" from="15" to="15" + begin="0s" dur="0.8s" + values="15;9;15" calcMode="linear" + repeatCount="indefinite" /> + <animate attributeName="fill-opacity" from="1" to="1" + begin="0s" dur="0.8s" + values="1;.5;1" calcMode="linear" + repeatCount="indefinite" /> + </circle> + <circle cx="60" cy="15" r="9" fill-opacity="0.3"> + <animate attributeName="r" from="9" to="9" + begin="0s" dur="0.8s" + values="9;15;9" calcMode="linear" + repeatCount="indefinite" /> + <animate attributeName="fill-opacity" from="0.5" to="0.5" + begin="0s" dur="0.8s" + values=".5;1;.5" calcMode="linear" + repeatCount="indefinite" /> + </circle> + <circle cx="105" cy="15" r="15"> + <animate attributeName="r" from="15" to="15" + begin="0s" dur="0.8s" + values="15;9;15" calcMode="linear" + repeatCount="indefinite" /> + <animate attributeName="fill-opacity" from="1" to="1" + begin="0s" dur="0.8s" + values="1;.5;1" calcMode="linear" + repeatCount="indefinite" /> + </circle> +</svg> diff --git a/include/functions.php b/include/functions.php index 73d963803..e8f41d56a 100644 --- a/include/functions.php +++ b/include/functions.php @@ -420,6 +420,8 @@ $check = "themes.local/$theme"; if (file_exists($check)) return $check; + + return ""; } function theme_exists($theme) { diff --git a/include/login_form.php b/include/login_form.php index 9efe0e238..2e0eb077f 100755 --- a/include/login_form.php +++ b/include/login_form.php @@ -15,9 +15,7 @@ } ?> - <?php if (theme_exists(Config::get(Config::LOCAL_OVERRIDE_STYLESHEET))) { - echo stylesheet_tag(get_theme_path(Config::get(Config::LOCAL_OVERRIDE_STYLESHEET))); - } ?> + <?= Config::get_override_links() ?> <style type="text/css"> @media (prefers-color-scheme: dark) { @@ -35,9 +35,7 @@ } } ?> - <?php if (theme_exists(Config::get(Config::LOCAL_OVERRIDE_STYLESHEET))) { - echo stylesheet_tag(get_theme_path(Config::get(Config::LOCAL_OVERRIDE_STYLESHEET))); - } ?> + <?= Config::get_override_links() ?> <script type="text/javascript"> const __csrf_token = "<?= $_SESSION["csrf_token"]; ?>"; @@ -112,19 +110,31 @@ } </style> + <noscript> + <?= stylesheet_tag("themes/light.css") ?> + + <style type="text/css"> + body.css_loading noscript { + display : block; + margin : 16px; + } + </style> + </noscript> + <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <meta name="referrer" content="no-referrer"/> </head> <body class="flat ttrss_main ttrss_index css_loading"> -<div id="overlay" style="display : block"> +<noscript class="alert alert-error"><?= ('Javascript is disabled. Please enable it.') ?></noscript> + +<div id="overlay"> <div id="overlay_inner"> <?= __("Loading, please wait...") ?> <div dojoType="dijit.ProgressBar" places="0" style="width : 300px" id="loading_bar" progress="0" maximum="100"> </div> - <noscript><br/><?php print_error('Javascript is disabled. Please enable it.') ?></noscript> </div> </div> @@ -133,9 +143,10 @@ <div id="main" dojoType="dijit.layout.BorderContainer"> <div id="feeds-holder" dojoType="dijit.layout.ContentPane" region="leading" style="width : 20%" splitter="true"> - <div id="feedlistLoading"> - <img src='images/indicator_tiny.gif'/> - <?= __("Loading, please wait..."); ?></div> + <div id="feedlistLoading" class="text-center text-muted text-small"> + <img class="icon-three-dots" src="images/three-dots.svg?2"> + <?= __("Loading, please wait..."); ?> + </div> <?php PluginHost::getInstance()->run_hooks_callback(PluginHost::HOOK_FEED_TREE, function ($result) { echo $result; @@ -199,7 +210,7 @@ ?> </select> - <div dojoType="fox.form.ComboButton" onclick="Feeds.catchupCurrent()"> + <div class="catchup-button" dojoType="fox.form.ComboButton" onclick="Feeds.catchupCurrent()"> <span><?= __('Mark as read') ?></span> <div dojoType="dijit.DropDownMenu"> <div dojoType="dijit.MenuItem" onclick="Feeds.catchupCurrent('1day')"> @@ -514,9 +514,12 @@ const App = { this.LABEL_BASE_INDEX = parseInt(params[k]); break; case "cdm_auto_catchup": - if (params[k] == 1) { - const hl = App.byId("headlines-frame"); - if (hl) hl.addClassName("auto_catchup"); + { + const headlines = App.byId("headlines-frame"); + + // we could be in preferences + if (headlines) + headlines.setAttribute("data-auto-catchup", params[k] ? "true" : "false"); } break; case "hotkeys": @@ -685,15 +688,16 @@ const App = { checkBrowserFeatures: function() { let errorMsg = ""; - ['MutationObserver'].forEach(function(wf) { - if (!(wf in window)) { - errorMsg = `Browser feature check failed: <code>window.${wf}</code> not found.`; + ['MutationObserver', 'requestIdleCallback'].forEach((t) => { + if (!(t in window)) { + errorMsg = `Browser check failed: <code>window.${t}</code> not found.`; throw new Error(errorMsg); } }); - if (errorMsg) { - this.Error.fatal(errorMsg, {info: navigator.userAgent}); + if (typeof Promise.allSettled == "undefined") { + errorMsg = `Browser check failed: <code>Promise.allSettled</code> is not defined.`; + throw new Error(errorMsg); } return errorMsg == ""; @@ -868,41 +872,44 @@ const App = { }, setWidescreen: function(wide) { const article_id = Article.getActive(); + const headlines_frame = App.byId("headlines-frame"); + const content_insert = dijit.byId("content-insert"); + + // TODO: setStyle stuff should probably be handled by CSS if (wide) { dijit.byId("headlines-wrap-inner").attr("design", 'sidebar'); - dijit.byId("content-insert").attr("region", "trailing"); + content_insert.attr("region", "trailing"); - dijit.byId("content-insert").domNode.setStyle({width: '50%', + content_insert.domNode.setStyle({width: '50%', height: 'auto', borderTopWidth: '0px' }); if (parseInt(Cookie.get("ttrss_ci_width")) > 0) { - dijit.byId("content-insert").domNode.setStyle( + content_insert.domNode.setStyle( {width: Cookie.get("ttrss_ci_width") + "px" }); } - App.byId("headlines-frame").setStyle({ borderBottomWidth: '0px' }); - App.byId("headlines-frame").addClassName("wide"); + headlines_frame.setStyle({ borderBottomWidth: '0px' }); } else { - dijit.byId("content-insert").attr("region", "bottom"); + content_insert.attr("region", "bottom"); - dijit.byId("content-insert").domNode.setStyle({width: 'auto', + content_insert.domNode.setStyle({width: 'auto', height: '50%', borderTopWidth: '0px'}); if (parseInt(Cookie.get("ttrss_ci_height")) > 0) { - dijit.byId("content-insert").domNode.setStyle( + content_insert.domNode.setStyle( {height: Cookie.get("ttrss_ci_height") + "px" }); } - App.byId("headlines-frame").setStyle({ borderBottomWidth: '1px' }); - App.byId("headlines-frame").removeClassName("wide"); - + headlines_frame.setStyle({ borderBottomWidth: '1px' }); } + headlines_frame.setAttribute("data-is-wide-screen", wide ? "true" : "false"); + Article.close(); if (article_id) Article.view(article_id); @@ -931,16 +938,18 @@ const App = { } else { this.hotkey_actions["next_feed"] = () => { - const rv = dijit.byId("feedTree").getNextFeed( + const [feed, is_cat] = Feeds.getNextFeed( Feeds.getActive(), Feeds.activeIsCat()); - if (rv) Feeds.open({feed: rv[0], is_cat: rv[1], delayed: true}) + if (feed !== false) + Feeds.open({feed: feed, is_cat: is_cat, delayed: true}) }; this.hotkey_actions["prev_feed"] = () => { - const rv = dijit.byId("feedTree").getPreviousFeed( + const [feed, is_cat] = Feeds.getPreviousFeed( Feeds.getActive(), Feeds.activeIsCat()); - if (rv) Feeds.open({feed: rv[0], is_cat: rv[1], delayed: true}) + if (feed !== false) + Feeds.open({feed: feed, is_cat: is_cat, delayed: true}) }; this.hotkey_actions["next_article_or_scroll"] = (event) => { if (this.isCombinedMode()) @@ -1102,6 +1111,12 @@ const App = { this.hotkey_actions["feed_reverse"] = () => { Headlines.reverse(); }; + this.hotkey_actions["feed_toggle_grid"] = () => { + xhr.json("backend.php", {op: "rpc", method: "togglepref", key: "CDM_ENABLE_GRID"}, (reply) => { + App.setInitParam("cdm_enable_grid", reply.value); + Headlines.renderAgain(); + }) + }; this.hotkey_actions["feed_toggle_vgroup"] = () => { xhr.post("backend.php", {op: "rpc", method: "togglepref", key: "VFEED_GROUP_BY_FEED"}, () => { Feeds.reloadCurrent(); @@ -1194,6 +1209,9 @@ const App = { Headlines.renderAgain(); }); }; + this.hotkey_actions["article_span_grid"] = () => { + Article.cdmToggleGridSpan(Article.getActive()); + }; } }, openPreferences: function(tab) { diff --git a/js/Article.js b/js/Article.js index ed74051a6..16abfbfee 100644 --- a/js/Article.js +++ b/js/Article.js @@ -93,6 +93,16 @@ const Article = { w.opener = null; w.location = url; }, + cdmToggleGridSpan: function(id) { + const row = App.byId(`RROW-${id}`); + + if (row) { + row.toggleClassName('grid-span-row'); + + this.setActive(id); + this.cdmMoveToId(id); + } + }, cdmUnsetActive: function (event) { const row = App.byId(`RROW-${Article.getActive()}`); @@ -273,7 +283,9 @@ const Article = { row.setAttribute("data-content", row.getAttribute("data-content-original")); row.removeAttribute("data-content-original"); - row.querySelector(".content-inner").innerHTML = " "; + row.querySelector(".content-inner").innerHTML = `<div class="text-center text-muted"> + ${__("Loading, please wait...")} + </div>` } }, view: function (id, no_expand) { @@ -389,10 +401,12 @@ const Article = { const ctr = App.byId("headlines-frame"); const row = App.byId(`RROW-${id}`); - if (!row || !ctr) return; + if (ctr && row) { + const grid_gap = parseInt(window.getComputedStyle(ctr).gridGap) || 0; - if (force_to_top || !App.Scrollable.fitsInContainer(row, ctr)) { - ctr.scrollTop = row.offsetTop; + if (force_to_top || !App.Scrollable.fitsInContainer(row, ctr)) { + ctr.scrollTop = row.offsetTop - grid_gap; + } } }, setActive: function (id) { diff --git a/js/CommonDialogs.js b/js/CommonDialogs.js index ab8441cac..a68dc8068 100644 --- a/js/CommonDialogs.js +++ b/js/CommonDialogs.js @@ -33,7 +33,7 @@ const CommonDialogs = { <section> <fieldset> - <div style='float : right'><img style='display : none' id='feed_add_spinner' src='images/indicator_white.gif'></div> + <div class='pull-right'><img style='display : none' id='feed_add_spinner' src='${App.getInitParam('icon_oval')}'></div> <input style='font-size : 16px; width : 500px;' placeHolder="${__("Feed or site URL")}" dojoType='dijit.form.ValidationTextBox' diff --git a/js/CommonFilters.js b/js/CommonFilters.js index 1450458f8..8a20480f0 100644 --- a/js/CommonFilters.js +++ b/js/CommonFilters.js @@ -38,16 +38,19 @@ const Filters = { console.log("got results:" + result.length); - App.byId("prefFilterProgressMsg").innerHTML = __("Looking for articles (%d processed, %f found)...") - .replace("%f", test_dialog.results) - .replace("%d", offset); + const loading_message = test_dialog.domNode.querySelector(".loading-message"); + const results_list = test_dialog.domNode.querySelector(".filter-results-list"); + + loading_message.innerHTML = __("Looking for articles (%d processed, %f found)...") + .replace("%f", test_dialog.results) + .replace("%d", offset); console.log(offset + " " + test_dialog.max_offset); for (let i = 0; i < result.length; i++) { - const tmp = dojo.create("table", { innerHTML: result[i]}); + const tmp = dojo.create("div", { innerHTML: result[i]}); - App.byId("prefFilterTestResultList").innerHTML += tmp.innerHTML; + results_list.innerHTML += tmp.innerHTML; } if (test_dialog.results < 30 && offset < test_dialog.max_offset) { @@ -60,14 +63,15 @@ const Filters = { } else { // all done - Element.hide("prefFilterLoadingIndicator"); + test_dialog.domNode.querySelector(".loading-indicator").hide(); if (test_dialog.results == 0) { - App.byId("prefFilterTestResultList").innerHTML = `<tr><td align='center'> - ${__('No recent articles matching this filter have been found.')}</td></tr>`; - App.byId("prefFilterProgressMsg").innerHTML = "Articles matching this filter:"; + results_list.innerHTML = `<li class="text-center text-muted"> + ${__('No recent articles matching this filter have been found.')}</li>`; + + loading_message.innerHTML = __("Articles matching this filter:"); } else { - App.byId("prefFilterProgressMsg").innerHTML = __("Found %d articles matching this filter:") + loading_message.innerHTML = __("Found %d articles matching this filter:") .replace("%d", test_dialog.results); } @@ -75,7 +79,7 @@ const Filters = { } else if (!result) { console.log("getTestResults: can't parse results object"); - Element.hide("prefFilterLoadingIndicator"); + test_dialog.domNode.querySelector(".loading-indicator").hide(); Notify.error("Error while trying to get filter test results."); } else { console.log("getTestResults: dialog closed, bailing out."); @@ -86,12 +90,12 @@ const Filters = { }); }, content: ` - <div> - <img id='prefFilterLoadingIndicator' src='images/indicator_tiny.gif'> - <span id='prefFilterProgressMsg'>Looking for articles...</span> + <div class="text-muted"> + <img class="loading-indicator icon-three-dots" src="${App.getInitParam("icon_three_dots")}"> + <span class="loading-message">${__("Looking for articles...")}</span> </div> - <ul class='panel panel-scrollable list list-unstyled' id='prefFilterTestResultList'></ul> + <ul class='panel panel-scrollable list list-unstyled filter-results-list'></ul> <footer class='text-center'> <button dojoType='dijit.form.Button' type='submit' class='alt-primary'>${__('Close this window')}</button> diff --git a/js/FeedStoreModel.js b/js/FeedStoreModel.js index 736bfbed6..befc441af 100644 --- a/js/FeedStoreModel.js +++ b/js/FeedStoreModel.js @@ -54,45 +54,6 @@ define(["dojo/_base/declare", "dijit/tree/ForestStoreModel"], function (declare) if (treeItem) return this.store.setValue(treeItem, key, value); }, - getNextUnreadFeed: function (feed, is_cat) { - if (!this.store._itemsByIdentity) - return null; - - let treeItem; - - if (is_cat) { - treeItem = this.store._itemsByIdentity['CAT:' + feed]; - } else { - treeItem = this.store._itemsByIdentity['FEED:' + feed]; - } - - const items = this.store._arrayOfAllItems; - - for (let i = 0; i < items.length; i++) { - if (items[i] == treeItem) { - - for (let j = i + 1; j < items.length; j++) { - const unread = this.store.getValue(items[j], 'unread'); - const id = this.store.getValue(items[j], 'id'); - - if (unread > 0 && ((is_cat && id.match("CAT:")) || (!is_cat && id.match("FEED:")))) { - if (!is_cat || !(this.store.hasAttribute(items[j], 'parent_id') && this.store.getValue(items[j], 'parent_id') == feed)) return items[j]; - } - } - - for (let j = 0; j < i; j++) { - const unread = this.store.getValue(items[j], 'unread'); - const id = this.store.getValue(items[j], 'id'); - - if (unread > 0 && ((is_cat && id.match("CAT:")) || (!is_cat && id.match("FEED:")))) { - if (!is_cat || !(this.store.hasAttribute(items[j], 'parent_id') && this.store.getValue(items[j], 'parent_id') == feed)) return items[j]; - } - } - } - } - - return null; - }, hasCats: function () { if (this.store && this.store._itemsByIdentity) return this.store._itemsByIdentity['CAT:-1'] != undefined; diff --git a/js/FeedTree.js b/js/FeedTree.js index 17cd3deea..b81638c39 100755 --- a/js/FeedTree.js +++ b/js/FeedTree.js @@ -82,6 +82,9 @@ define(["dojo/_base/declare", "dojo/dom-construct", "dojo/_base/array", "dojo/co } if (id.match("FEED:")) { + tnode.rowNode.setAttribute('data-feed-id', bare_id); + tnode.rowNode.setAttribute('data-is-cat', "false"); + const menu = new dijit.Menu(); menu.row_id = bare_id; @@ -132,10 +135,18 @@ define(["dojo/_base/declare", "dojo/dom-construct", "dojo/_base/array", "dojo/co } if (id.match("CAT:")) { - tnode.loadingNode = dojo.create('img', { className: 'loadingNode', src: 'images/blank_icon.gif'}); + tnode.rowNode.setAttribute('data-feed-id', bare_id); + tnode.rowNode.setAttribute('data-is-cat', "true"); + + tnode.loadingNode = dojo.create('img', { className: 'loadingNode', src: App.getInitParam('icon_blank')}); domConstruct.place(tnode.loadingNode, tnode.labelNode, 'after'); } + if (id.match("FEED:")) { + tnode.loadingNode = dojo.create('img', { className: 'loadingNode', src: App.getInitParam('icon_blank')}); + domConstruct.place(tnode.loadingNode, tnode.expandoNode, 'only'); + } + if (id.match("CAT:") && bare_id == -1) { const menu = new dijit.Menu(); menu.row_id = bare_id; @@ -191,10 +202,15 @@ define(["dojo/_base/declare", "dojo/dom-construct", "dojo/_base/array", "dojo/co return (item.unread <= 0) ? "dijitTreeLabel" : "dijitTreeLabel Unread"; }, getRowClass: function (item/*, opened */) { - let rc = "dijitTreeRow"; + let rc = "dijitTreeRow dijitTreeRowFlex"; const is_cat = String(item.id).indexOf('CAT:') != -1; + if (is_cat) + rc += " Is_Cat"; + else + rc += " Is_Feed"; + if (!is_cat && item.error != '') rc += " Error"; if (item.unread > 0) rc += " Unread"; if (item.auxcounter > 0) rc += " Has_Aux"; @@ -303,7 +319,7 @@ define(["dojo/_base/declare", "dojo/dom-construct", "dojo/_base/array", "dojo/co }, 0); } }, - setFeedIcon: function(feed, is_cat, src) { + setIcon: function(feed, is_cat, src) { let treeNode; if (is_cat) @@ -313,13 +329,19 @@ define(["dojo/_base/declare", "dojo/dom-construct", "dojo/_base/array", "dojo/co if (treeNode) { treeNode = treeNode[0]; - const icon = dojo.create('img', { src: src, className: 'icon' }); - domConstruct.place(icon, treeNode.iconNode, 'only'); - return true; + + // could be <i material> + const icon = treeNode.iconNode.querySelector('img.icon'); + + if (icon) { + icon.src = src; + + return true; + } } return false; }, - setFeedExpandoIcon: function(feed, is_cat, src) { + showLoading: function(feed, is_cat, show) { let treeNode; if (is_cat) @@ -329,14 +351,17 @@ define(["dojo/_base/declare", "dojo/dom-construct", "dojo/_base/array", "dojo/co if (treeNode) { treeNode = treeNode[0]; - if (treeNode.loadingNode) { - treeNode.loadingNode.src = src; - return true; + + if (show) { + treeNode.loadingNode.addClassName("visible"); + treeNode.loadingNode.setAttribute("src", + is_cat ? App.getInitParam("icon_three_dots") : App.getInitParam("icon_oval")); } else { - const icon = dojo.create('img', { src: src, className: 'loadingExpando' }); - domConstruct.place(icon, treeNode.expandoNode, 'only'); - return true; + treeNode.loadingNode.removeClassName("visible"); + treeNode.loadingNode.setAttribute("src", App.getInitParam("icon_blank")) } + + return true } return false; @@ -360,7 +385,28 @@ define(["dojo/_base/declare", "dojo/dom-construct", "dojo/_base/array", "dojo/co } }, - getNextFeed: function (feed, is_cat) { + getNextUnread: function(feed, is_cat) { + return this.getNextFeed(feed, is_cat, true); + }, + _nextTreeItemFromIndex: function (start, unread_only) { + const items = this.model.store._arrayOfAllItems; + + for (let i = start+1; i < items.length; i++) { + const id = String(items[i].id); + const box = this._itemNodesMap[id]; + const unread = parseInt(items[i].unread); + + if (box && (!unread_only || unread > 0)) { + const row = box[0].rowNode; + const cat = box[0].rowNode.parentNode.parentNode; + + if (Element.visible(cat) && Element.visible(row)) { + return items[i]; + } + } + } + }, + getNextFeed: function (feed, is_cat, unread_only = false) { let treeItem; if (is_cat) { @@ -370,35 +416,40 @@ define(["dojo/_base/declare", "dojo/dom-construct", "dojo/_base/array", "dojo/co } const items = this.model.store._arrayOfAllItems; - let item = items[0]; + const start = items.indexOf(treeItem); - for (let i = 0; i < items.length; i++) { - if (items[i] == treeItem) { + if (start != -1) { + let item = this._nextTreeItemFromIndex(start, unread_only); - for (let j = i+1; j < items.length; j++) { - const id = String(items[j].id); - const box = this._itemNodesMap[id]; + // let's try again from the top + // 0 (instead of -1) to skip Special category + if (!item) { + item = this._nextTreeItemFromIndex(0, unread_only); + } - if (box) { - const row = box[0].rowNode; - const cat = box[0].rowNode.parentNode.parentNode; + if (item) + return [this.model.store.getValue(item, 'bare_id'), + !this.model.store.getValue(item, 'id').match('FEED:')]; + } - if (Element.visible(cat) && Element.visible(row)) { - item = items[j]; - break; - } - } + return [false, false]; + }, + _prevTreeItemFromIndex: function (start) { + const items = this.model.store._arrayOfAllItems; + + for (let i = start-1; i > 0; i--) { + const id = String(items[i].id); + const box = this._itemNodesMap[id]; + + if (box) { + const row = box[0].rowNode; + const cat = box[0].rowNode.parentNode.parentNode; + + if (Element.visible(cat) && Element.visible(row)) { + return items[i]; } - break; } } - - if (item) { - return [this.model.store.getValue(item, 'bare_id'), - !this.model.store.getValue(item, 'id').match('FEED:')]; - } else { - return false; - } }, getPreviousFeed: function (feed, is_cat) { let treeItem; @@ -410,37 +461,22 @@ define(["dojo/_base/declare", "dojo/dom-construct", "dojo/_base/array", "dojo/co } const items = this.model.store._arrayOfAllItems; - let item = items[0] == treeItem ? items[items.length-1] : items[0]; - - for (let i = 0; i < items.length; i++) { - if (items[i] == treeItem) { - - for (let j = i-1; j > 0; j--) { - const id = String(items[j].id); - const box = this._itemNodesMap[id]; - - if (box) { - const row = box[0].rowNode; - const cat = box[0].rowNode.parentNode.parentNode; + const start = items.indexOf(treeItem); - if (Element.visible(cat) && Element.visible(row)) { - item = items[j]; - break; - } - } + if (start != -1) { + let item = this._prevTreeItemFromIndex(start); - } - break; + // wrap from the bottom + if (!item) { + item = this._prevTreeItemFromIndex(items.length); } - } - if (item) { - return [this.model.store.getValue(item, 'bare_id'), - !this.model.store.getValue(item, 'id').match('FEED:')]; - } else { - return false; + if (item) + return [this.model.store.getValue(item, 'bare_id'), + !this.model.store.getValue(item, 'id').match('FEED:')]; } + return [false, false]; }, getFeedCategory: function(feed) { try { diff --git a/js/Feeds.js b/js/Feeds.js index 33a1fa3dc..befd7e46e 100644 --- a/js/Feeds.js +++ b/js/Feeds.js @@ -113,7 +113,7 @@ const Feeds = { this.hideOrShowFeeds(App.getInitParam("hide_read_feeds")); this._counters_prev = elems; - PluginHost.run(PluginHost.HOOK_COUNTERS_PROCESSED); + PluginHost.run(PluginHost.HOOK_COUNTERS_PROCESSED, elems); }, reloadCurrent: function(method) { if (this.getActive() != undefined) { @@ -133,9 +133,10 @@ const Feeds = { return Feeds.reloadCurrent(''); }, openNextUnread: function() { - const is_cat = this.activeIsCat(); - const nuf = this.getNextUnread(this.getActive(), is_cat); - if (nuf) this.open({feed: nuf, is_cat: is_cat}); + const [feed, is_cat] = this.getNextUnread(this.getActive(), this.activeIsCat()); + + if (feed !== false) + this.open({feed: feed, is_cat: is_cat}); }, toggle: function() { Element.toggle("feeds-holder"); @@ -311,18 +312,22 @@ const Feeds = { setActive: function(id, is_cat) { console.log('setActive', id, is_cat); - if ('requestIdleCallback' in window) - window.requestIdleCallback(() => { - App.Hash.set({f: id, c: is_cat ? 1 : 0}); - }); - else + window.requestIdleCallback(() => { App.Hash.set({f: id, c: is_cat ? 1 : 0}); + }); this._active_feed_id = id; this._active_feed_is_cat = is_cat; - App.byId("headlines-frame").setAttribute("feed-id", id); - App.byId("headlines-frame").setAttribute("is-cat", is_cat ? 1 : 0); + const container = App.byId("headlines-frame"); + + // TODO @deprecated: these two should be removed (replaced with data- attributes below) + container.setAttribute("feed-id", id); + container.setAttribute("is-cat", is_cat ? 1 : 0); + // ^ + + container.setAttribute("data-feed-id", id); + container.setAttribute("data-is-cat", is_cat ? "true" : "false"); this.select(id, is_cat); @@ -395,21 +400,20 @@ const Feeds = { query.m = "ForceUpdate"; } - if (!delayed) - if (!this.setExpando(feed, is_cat, - (is_cat) ? 'images/indicator_tiny.gif' : 'images/indicator_white.gif')) - Notify.progress("Loading, please wait...", true); - query.cat = is_cat; this.setActive(feed, is_cat); window.clearTimeout(this._viewfeed_wait_timeout); this._viewfeed_wait_timeout = window.setTimeout(() => { + + this.showLoading(feed, is_cat, true); + //Notify.progress("Loading, please wait...", true);*/ + xhr.json("backend.php", query, (reply) => { try { window.clearTimeout(this._infscroll_timeout); - this.setExpando(feed, is_cat, 'images/blank_icon.gif'); + this.showLoading(feed, is_cat, false); Headlines.onLoaded(reply, offset, append); PluginHost.run(PluginHost.HOOK_FEED_LOADED, [feed, is_cat]); } catch (e) { @@ -475,10 +479,10 @@ const Feeds = { // only select next unread feed if catching up entirely (as opposed to last week etc) if (show_next_feed && !mode) { - const nuf = this.getNextUnread(feed, is_cat); + const [next_feed, next_is_cat] = this.getNextUnread(feed, is_cat); - if (nuf) { - this.open({feed: nuf, is_cat: is_cat}); + if (next_feed !== false) { + this.open({feed: next_feed, is_cat: next_is_cat}); } } else if (feed == this.getActive() && is_cat == this.activeIsCat()) { this.reloadCurrent(); @@ -522,7 +526,7 @@ const Feeds = { const tree = dijit.byId("feedTree"); if (tree && tree.model) - return tree._cat_of_feed(feed); + return tree.getFeedCategory(feed); } catch (e) { // @@ -570,21 +574,35 @@ const Feeds = { setIcon: function(feed, is_cat, src) { const tree = dijit.byId("feedTree"); - if (tree) return tree.setFeedIcon(feed, is_cat, src); + if (tree) return tree.setIcon(feed, is_cat, src); }, - setExpando: function(feed, is_cat, src) { + showLoading: function(feed, is_cat, show) { const tree = dijit.byId("feedTree"); - if (tree) return tree.setFeedExpandoIcon(feed, is_cat, src); + if (tree) return tree.showLoading(feed, is_cat, show); return false; }, + getNextFeed: function(feed, is_cat) { + const tree = dijit.byId("feedTree"); + + if (tree) return tree.getNextFeed(feed, is_cat, false); + + return [false, false]; + }, + getPreviousFeed: function(feed, is_cat) { + const tree = dijit.byId("feedTree"); + + if (tree) return tree.getPreviousFeed(feed, is_cat); + + return [false, false]; + }, getNextUnread: function(feed, is_cat) { const tree = dijit.byId("feedTree"); - const nuf = tree.model.getNextUnreadFeed(feed, is_cat); - if (nuf) - return tree.model.store.getValue(nuf, 'bare_id'); + if (tree) return tree.getNextUnread(feed, is_cat); + + return [false, false]; }, search: function() { xhr.json("backend.php", diff --git a/js/Headlines.js b/js/Headlines.js index 28e43be1f..134bdad6d 100755 --- a/js/Headlines.js +++ b/js/Headlines.js @@ -17,17 +17,27 @@ const Headlines = { sticky_header_observer: new IntersectionObserver( (entries, observer) => { entries.forEach((entry) => { - const header = entry.target.nextElementSibling; + const header = entry.target.closest('.cdm').querySelector(".header"); - if (entry.intersectionRatio == 0) { - header.setAttribute("stuck", "1"); - - } else if (entry.intersectionRatio == 1) { - header.removeAttribute("stuck"); + if (entry.isIntersecting) { + header.removeAttribute("data-is-stuck"); + } else { + header.setAttribute("data-is-stuck", "true"); } - //console.log(entry.target, header, entry.intersectionRatio); + //console.log(entry.target, entry.intersectionRatio, entry.isIntersecting, entry.boundingClientRect.top); + }); + }, + {threshold: [0, 1], root: document.querySelector("#headlines-frame")} + ), + sticky_content_observer: new IntersectionObserver( + (entries, observer) => { + entries.forEach((entry) => { + const header = entry.target.closest('.cdm').querySelector(".header"); + header.style.position = entry.isIntersecting ? "sticky" : "unset"; + + //console.log(entry.target, entry.intersectionRatio, entry.isIntersecting, entry.boundingClientRect.top); }); }, {threshold: [0, 1], root: document.querySelector("#headlines-frame")} @@ -72,14 +82,13 @@ const Headlines = { } }); + PluginHost.run(PluginHost.HOOK_HEADLINE_MUTATIONS, mutations); + Headlines.updateSelectedPrompt(); - if ('requestIdleCallback' in window) - window.requestIdleCallback(() => { - Headlines.syncModified(modified); - }); - else + window.requestIdleCallback(() => { Headlines.syncModified(modified); + }); }), syncModified: function (modified) { const ops = { @@ -173,14 +182,14 @@ const Headlines = { }); } - Promise.all(promises).then((results) => { + Promise.allSettled(promises).then((results) => { let feeds = []; let labels = []; results.forEach((res) => { if (res) { try { - const obj = JSON.parse(res); + const obj = JSON.parse(res.value); if (obj.feeds) feeds = feeds.concat(obj.feeds); @@ -198,6 +207,8 @@ const Headlines = { console.log('requesting counters for', feeds, labels); Feeds.requestCounters(feeds, labels); } + + PluginHost.run(PluginHost.HOOK_HEADLINE_MUTATIONS_SYNCED, results); }); }, click: function (event, id, in_body) { @@ -340,8 +351,7 @@ const Headlines = { // invoke lazy load if last article in buffer is nearly visible OR is active if (Article.getActive() == last_row.getAttribute("data-article-id") || last_row.offsetTop - 250 <= container.scrollTop + container.offsetHeight) { - hsp.innerHTML = "<span class='loading'><img src='images/indicator_tiny.gif'> " + - __("Loading, please wait...") + "</span>"; + hsp.innerHTML = `<span class='text-muted text-small text-center'><img class="icon-three-dots" src="${App.getInitParam('icon_three_dots')}"> ${__("Loading, please wait...")}</span>`; Headlines.loadMore(); return; @@ -371,6 +381,9 @@ const Headlines = { } } } + + PluginHost.run(PluginHost.HOOK_HEADLINES_SCROLL_HANDLER); + } catch (e) { console.warn("scrollHandler", e); } @@ -378,11 +391,17 @@ const Headlines = { objectById: function (id) { return this.headlines[id]; }, - setCommonClasses: function () { - App.byId("headlines-frame").removeClassName("cdm"); - App.byId("headlines-frame").removeClassName("normal"); + setCommonClasses: function (headlines_count) { + const container = App.byId("headlines-frame"); - App.byId("headlines-frame").addClassName(App.isCombinedMode() ? "cdm" : "normal"); + container.removeClassName("cdm"); + container.removeClassName("normal"); + + container.addClassName(App.isCombinedMode() ? "cdm" : "normal"); + container.setAttribute("data-enable-grid", App.getInitParam("cdm_enable_grid") ? "true" : "false"); + container.setAttribute("data-headlines-count", parseInt(headlines_count)); + container.setAttribute("data-is-cdm", App.isCombinedMode() ? "true" : "false"); + container.setAttribute("data-is-cdm-expanded", App.getInitParam("cdm_expanded")); // for floating title because it's placed outside of headlines-frame App.byId("main").removeClassName("expandable"); @@ -393,7 +412,7 @@ const Headlines = { }, renderAgain: function () { // TODO: wrap headline elements into a knockoutjs model to prevent all this stuff - Headlines.setCommonClasses(); + Headlines.setCommonClasses(this.headlines.filter((h) => h.id).length); App.findAll("#headlines-frame > div[id*=RROW]").forEach((row) => { const id = row.getAttribute("data-article-id"); @@ -422,11 +441,18 @@ const Headlines = { this.sticky_header_observer.observe(e) }); + App.findAll(".cdm .content").forEach((e) => { + this.sticky_content_observer.observe(e) + }); + if (App.getInitParam("cdm_expanded")) App.findAll("#headlines-frame > div[id*=RROW].cdm").forEach((e) => { this.unpack_observer.observe(e) }); + dijit.byId('main').resize(); + + PluginHost.run(PluginHost.HOOK_HEADLINES_RENDERED); }, render: function (headlines, hl) { let row = null; @@ -494,9 +520,10 @@ const Headlines = { <span class="updated" title="${hl.imported}">${hl.updated}</span> <div class="right"> + <i class="material-icons icon-grid-span" title="${__("Span all columns")}" onclick="Article.cdmToggleGridSpan(${hl.id})">fullscreen</i> <i class="material-icons icon-score" title="${hl.score}" onclick="Article.setScore(${hl.id}, this)">${Article.getScorePic(hl.score)}</i> - <span style="cursor : pointer" title="${App.escapeHtml(hl.feed_title)}" onclick="Feeds.open({feed:${hl.feed_id}})"> + <span class="icon-feed" title="${App.escapeHtml(hl.feed_title)}" onclick="Feeds.open({feed:${hl.feed_id}})"> ${Feeds.renderIcon(hl.feed_id, hl.has_icon)} </span> </div> @@ -506,7 +533,9 @@ const Headlines = { <div class="content" onclick="return Headlines.click(event, ${hl.id}, true);"> ${Article.renderNote(hl.id, hl.note)} <div class="content-inner" lang="${hl.lang ? hl.lang : 'en'}"> - <img src="${App.getInitParam('icon_indicator_white')}"> + <div class="text-center text-muted"> + ${__("Loading, please wait...")} + </div> </div> <div class="intermediate"> ${Article.renderEnclosures(hl.enclosures)} @@ -560,7 +589,7 @@ const Headlines = { </div> <div class="right"> <i class="material-icons icon-score" title="${hl.score}" onclick="Article.setScore(${hl.id}, this)">${Article.getScorePic(hl.score)}</i> - <span onclick="Feeds.open({feed:${hl.feed_id}})" style="cursor : pointer" title="${App.escapeHtml(hl.feed_title)}">${Feeds.renderIcon(hl.feed_id, hl.has_icon)}</span> + <span onclick="Feeds.open({feed:${hl.feed_id}})" class="icon-feed" title="${App.escapeHtml(hl.feed_title)}">${Feeds.renderIcon(hl.feed_id, hl.has_icon)}</span> </div> </div> `; @@ -614,7 +643,7 @@ const Headlines = { </span> <span class='right'> <span id='selected_prompt'></span> - <div dojoType='fox.form.DropDownButton' title='"${__('Select articles')}'> + <div class='select-articles-dropdown' dojoType='fox.form.DropDownButton' title='"${__('Select articles')}'> <span>${__("Select...")}</span> <div dojoType='dijit.Menu' style='display: none;'> <div dojoType='dijit.MenuItem' onclick='Headlines.select("all")'>${__('All')}</div> @@ -671,11 +700,15 @@ const Headlines = { console.log('infscroll_disabled=', Feeds.infscroll_disabled); // also called in renderAgain() after view mode switch - Headlines.setCommonClasses(); + Headlines.setCommonClasses(headlines_count); + /** TODO: remove @deprecated */ App.byId("headlines-frame").setAttribute("is-vfeed", reply['headlines']['is_vfeed'] ? 1 : 0); + App.byId("headlines-frame").setAttribute("data-is-vfeed", + reply['headlines']['is_vfeed'] ? "true" : "false"); + Article.setActive(0); try { @@ -716,6 +749,9 @@ const Headlines = { hsp.id = "headlines-spacer"; } + // clear out hsp contents in case there's a power-hungry svg icon rotating there + hsp.innerHTML = ""; + dijit.byId('headlines-frame').domNode.appendChild(hsp); this.initHeadlinesMenu(); @@ -767,6 +803,9 @@ const Headlines = { hsp.id = "headlines-spacer"; } + // clear out hsp contents in case there's a power-hungry svg icon rotating there + hsp.innerHTML = ""; + c.domNode.appendChild(hsp); this.initHeadlinesMenu(); @@ -799,6 +838,10 @@ const Headlines = { this.sticky_header_observer.observe(e) }); + App.findAll(".cdm .content").forEach((e) => { + this.sticky_content_observer.observe(e) + }); + if (App.getInitParam("cdm_expanded")) App.findAll("#headlines-frame > div[id*=RROW].cdm").forEach((e) => { this.unpack_observer.observe(e) @@ -816,6 +859,10 @@ const Headlines = { // unpack visible articles, fill buffer more, etc this.scrollHandler(); + dijit.byId('main').resize(); + + PluginHost.run(PluginHost.HOOK_HEADLINES_RENDERED); + Notify.close(); }, reverse: function () { diff --git a/js/PluginHost.js b/js/PluginHost.js index caee79d58..deb7c0645 100644 --- a/js/PluginHost.js +++ b/js/PluginHost.js @@ -17,6 +17,10 @@ const PluginHost = { HOOK_HEADLINE_RENDERED: 12, HOOK_COUNTERS_RECEIVED: 13, HOOK_COUNTERS_PROCESSED: 14, + HOOK_HEADLINE_MUTATIONS: 15, + HOOK_HEADLINE_MUTATIONS_SYNCED: 16, + HOOK_HEADLINES_RENDERED: 17, + HOOK_HEADLINES_SCROLL_HANDLER: 18, hooks: [], register: function (name, callback) { if (typeof(this.hooks[name]) == 'undefined') @@ -25,7 +29,7 @@ const PluginHost = { this.hooks[name].push(callback); }, run: function (name, args) { - //console.warn('PluginHost::run ' + name); + //console.warn('PluginHost.run', name); if (typeof(this.hooks[name]) != 'undefined') for (let i = 0; i < this.hooks[name].length; i++) { diff --git a/js/PrefHelpers.js b/js/PrefHelpers.js index 3f738aa95..361b653b6 100644 --- a/js/PrefHelpers.js +++ b/js/PrefHelpers.js @@ -368,15 +368,16 @@ const Helpers = { // only user-enabled actually counts in the checkbox when saving because system plugin checkboxes are disabled (see below) container.innerHTML += ` - <li data-row-value="${App.escapeHtml(plugin.name)}" data-plugin-local="${plugin.is_local}" data-plugin-name="${App.escapeHtml(plugin.name)}" title="${plugin.is_system ? __("System plugins are enabled using global configuration.") : ""}"> + <li data-row-value="${App.escapeHtml(plugin.name)}" data-plugin-local="${plugin.is_local}" + data-plugin-name="${App.escapeHtml(plugin.name)}" title="${plugin.is_system ? __("System plugins are enabled using global configuration.") : ""}"> <label class="checkbox ${plugin.is_system ? "system text-info" : ""}"> ${App.FormFields.checkbox_tag("plugins[]", plugin.user_enabled || plugin.system_enabled, plugin.name, {disabled: plugin.is_system})}</div> <span class='name'>${plugin.name}:</span> + <span class="description ${plugin.is_system ? "text-info" : ""}"> + ${plugin.description} + </span> </label> - <div class="description ${plugin.is_system ? "text-info" : ""}"> - ${plugin.description} - </div> <div class='actions'> ${plugin.is_system ? App.FormFields.button_tag(App.FormFields.icon("security"), "", @@ -510,12 +511,10 @@ const Helpers = { search: function() { this.search_query = this.attr('value').search.toLowerCase(); - if ('requestIdleCallback' in window) - window.requestIdleCallback(() => { - this.render_contents(); - }); - else + window.requestIdleCallback(() => { this.render_contents(); + }); + }, render_contents: function() { const container = dialog.domNode.querySelector(".contents"); @@ -809,63 +808,5 @@ const Helpers = { console.log("export"); window.open("backend.php?op=opml&method=export&" + dojo.formToQuery("opmlExportForm")); }, - publish: function() { - Notify.progress("Loading, please wait...", true); - - xhr.json("backend.php", {op: "pref-feeds", method: "getOPMLKey"}, (reply) => { - try { - const dialog = new fox.SingleUseDialog({ - title: __("Public OPML URL"), - regenOPMLKey: function() { - if (confirm(__("Replace current OPML publishing address with a new one?"))) { - Notify.progress("Trying to change address...", true); - - xhr.json("backend.php", {op: "pref-feeds", method: "regenOPMLKey"}, (reply) => { - if (reply) { - const new_link = reply.link; - const target = this.domNode.querySelector('.generated_url'); - - if (new_link && target) { - target.href = new_link; - target.innerHTML = new_link; - - Notify.close(); - - } else { - Notify.error("Could not change feed URL."); - } - } - }); - } - return false; - }, - content: ` - <header>${__("Your Public OPML URL is:")}</header> - <section> - <div class='panel text-center'> - <a class='generated_url' href="${App.escapeHtml(reply.link)}" target='_blank'>${App.escapeHtml(reply.link)}</a> - </div> - </section> - <footer class='text-center'> - <button dojoType='dijit.form.Button' onclick="return App.dialogOf(this).regenOPMLKey()"> - ${App.FormFields.icon("refresh")} - ${__('Generate new URL')} - </button> - <button dojoType='dijit.form.Button' type='submit' class='alt-primary'> - ${__('Close this window')} - </button> - </footer> - ` - }); - - dialog.show(); - - Notify.close(); - - } catch (e) { - App.Error.report(e); - } - }); - }, } }; diff --git a/js/common.js b/js/common.js index 1f8318862..1299a0c64 100755 --- a/js/common.js +++ b/js/common.js @@ -432,7 +432,7 @@ const Notify = { break; case this.KIND_PROGRESS: notify.addClassName("notify_progress"); - icon = App.getInitParam("icon_indicator_white") + icon = App.getInitParam("icon_oval") break; default: icon = "notifications"; diff --git a/phpstan.neon b/phpstan.neon index 8fe36c8b4..818f99577 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -5,7 +5,8 @@ parameters: - '#Constant.*\b(SUBSTRING_FOR_DATE|SCHEMA_VERSION|SELF_USER_AGENT|LABEL_BASE_INDEX|PLUGIN_FEED_BASE_INDEX)\b.*not found#' - '#Comparison operation ">" between int<1, max> and 0 is always true.#' - '#Access to an undefined property DOMNode::\$tagName.#' - - '#Call to an undefined method DOMNode::(get|remove|set)Attribute\(\).#' + - '#Call to an undefined method DOMNode::(get|remove|set|has)Attribute\(\).#' + - '#Call to an undefined method DOMNode::(getElementsByTagName)\(\).#' - '#PHPDoc tag @param has invalid value#' - message: '##' paths: diff --git a/plugins/af_redditimgur/init.php b/plugins/af_redditimgur/init.php index 9fd93dde4..35eb40950 100755 --- a/plugins/af_redditimgur/init.php +++ b/plugins/af_redditimgur/init.php @@ -132,7 +132,7 @@ class Af_RedditImgur extends Plugin { if (!empty($media["s"]["u"])) { $media_url = htmlspecialchars_decode($media["s"]["u"]); - Debug::log("found media_metadata (gallery): $media_url", Debug::$LOG_VERBOSE); + Debug::log("found media_metadata (gallery): $media_url", Debug::LOG_VERBOSE); if ($media_url) { $this->handle_as_image($doc, $anchor, $media_url); @@ -153,7 +153,7 @@ class Af_RedditImgur extends Plugin { else $poster_url = ""; - Debug::log("found stream fallback_url: $stream_url / poster $poster_url", Debug::$LOG_VERBOSE); + Debug::log("found stream fallback_url: $stream_url / poster $poster_url", Debug::LOG_VERBOSE); $this->handle_as_video($doc, $anchor, $stream_url, $poster_url); } @@ -172,12 +172,12 @@ class Af_RedditImgur extends Plugin { else $poster_url = ""; - Debug::log("found hosted video url: $media_url / poster $poster_url, looking up fallback url...", Debug::$LOG_VERBOSE); + Debug::log("found hosted video url: $media_url / poster $poster_url, looking up fallback url...", Debug::LOG_VERBOSE); $fallback_url = $data["media"]["reddit_video"]["fallback_url"]; if ($fallback_url) { - Debug::log("found video fallback_url: $fallback_url", Debug::$LOG_VERBOSE); + Debug::log("found video fallback_url: $fallback_url", Debug::LOG_VERBOSE); $this->handle_as_video($doc, $anchor, $fallback_url, $poster_url); $found = 1; @@ -192,7 +192,7 @@ class Af_RedditImgur extends Plugin { else $poster_url = ""; - Debug::log("found video url: $media_url / poster $poster_url", Debug::$LOG_VERBOSE); + Debug::log("found video url: $media_url / poster $poster_url", Debug::LOG_VERBOSE); $this->handle_as_video($doc, $anchor, $media_url, $poster_url); $found = 1; @@ -201,7 +201,7 @@ class Af_RedditImgur extends Plugin { if (!$found && $post_hint == "image") { $media_url = $data["url"]; - Debug::log("found image url: $media_url", Debug::$LOG_VERBOSE); + Debug::log("found image url: $media_url", Debug::LOG_VERBOSE); $this->handle_as_image($doc, $anchor, $media_url); $found = 1; @@ -215,12 +215,12 @@ class Af_RedditImgur extends Plugin { if ($media_url) { if ($post_hint == "self") { - Debug::log("found preview image url: $media_url (link: $target_url)", Debug::$LOG_VERBOSE); + Debug::log("found preview image url: $media_url (link: $target_url)", Debug::LOG_VERBOSE); $this->handle_as_image($doc, $anchor, $media_url, $target_url); $found = 1; } else { // gonna use this later if nothing is found using generic link processing - Debug::log("found fallback preview image url: $media_url (link: $target_url);", Debug::$LOG_VERBOSE); + Debug::log("found fallback preview image url: $media_url (link: $target_url);", Debug::LOG_VERBOSE); array_push($this->fallback_preview_urls, $media_url); } } @@ -244,14 +244,15 @@ class Af_RedditImgur extends Plugin { $post_is_nsfw = false; $num_comments = 0; $score = 0; + $link_flairs = []; $apply_nsfw_tags = FeedItem_Common::normalize_categories($this->host->get_array($this, "apply_nsfw_tags", [])); - // embed before reddit <table> post layout + // embed anchor element, before reddit <table> post layout $anchor = $xpath->query('//body/*')->item(0); // deal with json-provided media content first if ($article["link"] && $anchor) { - Debug::log("JSON: requesting from URL: " . $article["link"] . "/.json", Debug::$LOG_VERBOSE); + Debug::log("JSON: requesting from URL: " . $article["link"] . "/.json", Debug::LOG_VERBOSE); $tmp = UrlHelper::fetch($article["link"] . "/.json"); @@ -262,7 +263,7 @@ class Af_RedditImgur extends Plugin { $json = json_decode($tmp, true); if ($json) { - Debug::log("JSON: processing media elements...", Debug::$LOG_EXTENDED); + Debug::log("JSON: processing media elements...", Debug::LOG_EXTENDED); if ($this->dump_json_data) print_r($json); @@ -275,13 +276,17 @@ class Af_RedditImgur extends Plugin { $score += $data['score'] ?? 0; $num_comments += $data["num_comments"] ?? 0; + if (!empty($data["link_flair_text"])) { + array_push($link_flairs, $data["link_flair_text"]); + } + if ($over_18) { - Debug::log("JSON: post is NSFW", Debug::$LOG_EXTENDED); + Debug::log("JSON: post is NSFW", Debug::LOG_EXTENDED); $post_is_nsfw = true; } if (isset($data["crosspost_parent_list"])) { - Debug::log("JSON: processing child crosspost_parent_list", Debug::$LOG_EXTENDED); + Debug::log("JSON: processing child crosspost_parent_list", Debug::LOG_EXTENDED); foreach ($data["crosspost_parent_list"] as $parent) { if ($this->process_post_media($parent, $doc, $xpath, $anchor)) { @@ -292,7 +297,7 @@ class Af_RedditImgur extends Plugin { } } - Debug::log("JSON: processing child data element...", Debug::$LOG_EXTENDED); + Debug::log("JSON: processing child data element...", Debug::LOG_EXTENDED); if (!$found && $this->process_post_media($data, $doc, $xpath, $anchor)) { $found = 1; @@ -302,28 +307,32 @@ class Af_RedditImgur extends Plugin { } } } else { - Debug::log("JSON: failed to parse received data.", Debug::$LOG_EXTENDED); + Debug::log("JSON: failed to parse received data.", Debug::LOG_EXTENDED); } } else { if (!$tmp) { - Debug::log("JSON: failed to fetch post:" . UrlHelper::$fetch_last_error, Debug::$LOG_EXTENDED); + Debug::log("JSON: failed to fetch post:" . UrlHelper::$fetch_last_error, Debug::LOG_EXTENDED); } } } else if (!$anchor) { - Debug::log("JSON: anchor element not found, unable to embed", Debug::$LOG_EXTENDED); + Debug::log("JSON: anchor element not found, unable to embed", Debug::LOG_EXTENDED); } if ($post_is_nsfw && count($apply_nsfw_tags) > 0) { $article["tags"] = array_merge($article["tags"], $apply_nsfw_tags); } + if (count($link_flairs) > 0) { + $article["tags"] = array_merge($article["tags"], FeedItem_Common::normalize_categories($link_flairs)); + } + $article["num_comments"] = $num_comments; if ($import_score && $score > 0) $article["score_modifier"] = ($article["score_modifier"] ?? 0) + ($score > $max_score ? $max_score : $score); if ($found) { - Debug::log("JSON: found media data, skipping further processing of content", Debug::$LOG_VERBOSE); + Debug::log("JSON: found media data, skipping further processing of content", Debug::LOG_VERBOSE); $this->remove_post_thumbnail($doc, $xpath); return true; } @@ -337,14 +346,14 @@ class Af_RedditImgur extends Plugin { /* skip links going back to reddit (and any other blacklisted stuff) */ if (!$found && $this->is_blacklisted($entry_href, ["reddit.com"])) { - Debug::log("BODY: domain of $entry_href is blacklisted, skipping", Debug::$LOG_EXTENDED); + Debug::log("BODY: domain of $entry_href is blacklisted, skipping", Debug::LOG_EXTENDED); continue; } - Debug::log("BODY: processing URL: " . $entry_href, Debug::$LOG_VERBOSE); + Debug::log("BODY: processing URL: " . $entry_href, Debug::LOG_VERBOSE); if (!$found && preg_match("/^https?:\/\/twitter.com\/(.*?)\/status\/(.*)/", $entry_href, $matches)) { - Debug::log("handling as twitter: " . $matches[1] . " " . $matches[2], Debug::$LOG_VERBOSE); + Debug::log("handling as twitter: " . $matches[1] . " " . $matches[2], Debug::LOG_VERBOSE); $oembed_result = UrlHelper::fetch("https://publish.twitter.com/oembed?url=" . urlencode($entry_href)); @@ -376,7 +385,7 @@ class Af_RedditImgur extends Plugin { if (!$found && preg_match("/https?:\/\/(www\.)?gfycat.com\/([a-z]+)$/i", $entry_href, $matches)) { - Debug::log("Handling as Gfycat", Debug::$LOG_VERBOSE); + Debug::log("Handling as Gfycat", Debug::LOG_VERBOSE); $source_stream = 'https://giant.gfycat.com/' . $matches[2] . '.mp4'; $poster_url = 'https://thumbs.gfycat.com/' . $matches[2] . '-mobile.jpg'; @@ -391,14 +400,14 @@ class Af_RedditImgur extends Plugin { // imgur .gif -> .gifv if (!$found && preg_match("/i\.imgur\.com\/(.*?)\.gif$/i", $entry_href)) { - Debug::log("Handling as imgur gif (->gifv)", Debug::$LOG_VERBOSE); + Debug::log("Handling as imgur gif (->gifv)", Debug::LOG_VERBOSE); $entry->setAttribute("href", str_replace(".gif", ".gifv", $entry_href)); } if (!$found && preg_match("/\.(gifv|mp4)$/i", $entry_href)) { - Debug::log("Handling as imgur gifv", Debug::$LOG_VERBOSE); + Debug::log("Handling as imgur gifv", Debug::LOG_VERBOSE); $source_stream = str_replace(".gifv", ".mp4", $entry_href); @@ -421,7 +430,7 @@ class Af_RedditImgur extends Plugin { $vid_id = $matches[1]; - Debug::log("Handling as youtube: $vid_id", Debug::$LOG_VERBOSE); + Debug::log("Handling as youtube: $vid_id", Debug::LOG_VERBOSE); $iframe = $doc->createElement("iframe"); $iframe->setAttribute("class", "youtube-player"); @@ -432,9 +441,15 @@ class Af_RedditImgur extends Plugin { $iframe->setAttribute("allowfullscreen", "1"); $iframe->setAttribute("frameborder", "0"); - $br = $doc->createElement('br'); - $entry->parentNode->insertBefore($iframe, $entry); - $entry->parentNode->insertBefore($br, $entry); + //$br = $doc->createElement('br'); + //$entry->parentNode->insertBefore($iframe, $entry); + //$entry->parentNode->insertBefore($br, $entry); + + // reparent generated iframe because it doesn't scale well inside <td> + if ($anchor) + $anchor->parentNode->insertBefore($iframe, $anchor); + else + $entry->parentNode->insertBefore($iframe, $entry); $found = true; } @@ -443,7 +458,7 @@ class Af_RedditImgur extends Plugin { /* mb_strpos($entry_href, "i.reddituploads.com") !== false || */ mb_strpos($this->get_content_type($entry_href), "image/") !== false)) { - Debug::log("Handling as a picture", Debug::$LOG_VERBOSE); + Debug::log("Handling as a picture", Debug::LOG_VERBOSE); $img = $doc->createElement('img'); $img->setAttribute("src", $entry_href); @@ -458,7 +473,7 @@ class Af_RedditImgur extends Plugin { // imgur via link rel="image_src" href="..." if (!$found && preg_match("/imgur/", $entry_href)) { - Debug::log("handling as imgur page/whatever", Debug::$LOG_VERBOSE); + Debug::log("handling as imgur page/whatever", Debug::LOG_VERBOSE); $content = UrlHelper::fetch(["url" => $entry_href, "http_accept" => "text/*"]); @@ -490,7 +505,7 @@ class Af_RedditImgur extends Plugin { if (!$found && preg_match("/^https?:\/\/gyazo\.com\/([^\.\/]+$)/", $entry_href, $matches)) { $img_id = $matches[1]; - Debug::log("handling as gyazo: $img_id", Debug::$LOG_VERBOSE); + Debug::log("handling as gyazo: $img_id", Debug::LOG_VERBOSE); $img = $doc->createElement('img'); $img->setAttribute("src", "https://i.gyazo.com/$img_id.jpg"); @@ -504,7 +519,7 @@ class Af_RedditImgur extends Plugin { // let's try meta properties if (!$found) { - Debug::log("looking for meta og:image", Debug::$LOG_VERBOSE); + Debug::log("looking for meta og:image", Debug::LOG_VERBOSE); $content = UrlHelper::fetch(["url" => $entry_href, "http_accept" => "text/*"]); @@ -555,7 +570,7 @@ class Af_RedditImgur extends Plugin { } if (!$found && $anchor && count($this->fallback_preview_urls) > 0) { - Debug::log("JSON: processing fallback preview urls...", Debug::$LOG_VERBOSE); + Debug::log("JSON: processing fallback preview urls...", Debug::LOG_VERBOSE); foreach ($this->fallback_preview_urls as $media_url) { $this->handle_as_image($doc, $anchor, $media_url); @@ -662,7 +677,7 @@ class Af_RedditImgur extends Plugin { private function handle_as_video($doc, $entry, $source_stream, $poster_url = false) { - Debug::log("handle_as_video: $source_stream", Debug::$LOG_VERBOSE); + Debug::log("handle_as_video: $source_stream", Debug::LOG_VERBOSE); $video = $doc->createElement('video'); $video->setAttribute("autoplay", "1"); @@ -694,12 +709,13 @@ class Af_RedditImgur extends Plugin { function testurl() { - $url = clean($_POST["url"]); - $article_url = clean($_POST["article_url"]); + $url = clean($_POST["url"] ?? ""); + $article_url = clean($_POST["article_url"] ?? ""); + $article_id = clean($_POST["article_id"] ?? ""); $this->dump_json_data = true; - if (!$url && !$article_url) { + if (!$url && !$article_url && !$article_id) { header("Content-type: text/html"); ?> <style type="text/css"> @@ -711,11 +727,16 @@ class Af_RedditImgur extends Plugin { <input type="hidden" name="method" value="testurl"> <input type="hidden" name="plugin" value="af_redditimgur"> <fieldset> - <label>URL:</label> + <label>Test URL:</label> <input name="url" size="100" value="<?= htmlspecialchars($url) ?>"></input> </fieldset> + <hr/> <fieldset> - <label>Article URL:</label> + <label>Article ID:</label> + <input name="article_id" size="10" value="<?= htmlspecialchars($article_id) ?>"></input> + </fieldset> + <fieldset> + <label>or Article URL:</label> <input name="article_url" size="100" value="<?= htmlspecialchars($article_url) ?>"></input> </fieldset> <fieldset> @@ -729,28 +750,51 @@ class Af_RedditImgur extends Plugin { header("Content-type: text/plain"); Debug::set_enabled(true); - Debug::set_loglevel(Debug::$LOG_EXTENDED); + Debug::set_loglevel(Debug::LOG_EXTENDED); + + if ($article_id) { + $stored_article = ORM::for_table('ttrss_entries') + ->table_alias('e') + ->join('ttrss_user_entries', [ 'ref_id', '=', 'e.id'], 'ue') + ->where('ue.owner_uid', $_SESSION['uid']) + ->find_one($article_id); + + if (!$stored_article) { + Debug::log("Article not found: $article_id", Debug::LOG_VERBOSE); + return; + } + + $article = [ + "link" => $stored_article->link, + "content" => $stored_article->content, + "tags" => explode(",", $stored_article->tag_cache) + ]; - Debug::log("URL: $url", Debug::$LOG_VERBOSE); + } else { + $article = [ + "link" => $article_url, + "content" => "<html><body><table><tr><td><a href=\"$url\">[link]</a></td></tr></table></body>", + "tags" => []]; + } $doc = new DOMDocument(); - @$doc->loadHTML("<html><body><table><tr><td><a href=\"$url\">[link]</a></td></tr></table></body>"); + @$doc->loadHTML($article["content"]); $xpath = new DOMXPath($doc); - $article = ["link" => $article_url, "tags" => []]; - $found = $this->inline_stuff($article, $doc, $xpath); - Debug::log("Inline result: $found", Debug::$LOG_VERBOSE); + Debug::log("Inline result: $found", Debug::LOG_VERBOSE); + + print_r($article); if (!$found) { - Debug::log("Readability result:", Debug::$LOG_VERBOSE); + Debug::log("Readability result:", Debug::LOG_VERBOSE); $article = $this->readability([], $url, $doc, $xpath); print_r($article); } else { - Debug::log("Resulting HTML:", Debug::$LOG_VERBOSE); + Debug::log("Resulting HTML:", Debug::LOG_VERBOSE); print $doc->saveHTML(); } diff --git a/plugins/af_zz_vidmute/init.js b/plugins/af_zz_vidmute/init.js index b8be8cecd..36914cbf0 100644 --- a/plugins/af_zz_vidmute/init.js +++ b/plugins/af_zz_vidmute/init.js @@ -1,24 +1,18 @@ +/* global require, PluginHost */ + require(['dojo/_base/kernel', 'dojo/ready'], function (dojo, ready) { + function mute(row) { + [...row.querySelectorAll("video")].forEach((vid) => { vid.muted = true; }); + } + ready(function () { PluginHost.register(PluginHost.HOOK_ARTICLE_RENDERED_CDM, function (row) { - if (row) { - - row.querySelectorAll("video").forEach(function (v) { - v.muted = true; - }); - } - + mute(row); return true; }); PluginHost.register(PluginHost.HOOK_ARTICLE_RENDERED, function (row) { - if (row) { - - row.querySelectorAll("video").forEach(function (v) { - v.muted = true; - }); - } - + mute(row); return true; }); }); diff --git a/plugins/auth_internal/init.php b/plugins/auth_internal/init.php index 9155f8165..3f5a2e977 100644 --- a/plugins/auth_internal/init.php +++ b/plugins/auth_internal/init.php @@ -50,7 +50,7 @@ class Auth_Internal extends Auth_Base { return false; } */ - if (UserHelper::check_otp($user_id, $otp)) + if ($this->check_password($user_id, $password) && UserHelper::check_otp($user_id, $otp)) return $user_id; else return false; @@ -109,7 +109,7 @@ class Auth_Internal extends Auth_Base { <?= \Controls\hidden_tag("op", "login") ?> <fieldset> - <label><?= __("Please enter your one time password:") ?></label> + <label><?= __("Please enter verification code (OTP):") ?></label> <input id="otp" dojoType="dijit.form.ValidationTextBox" required="1" autocomplete="off" size="6" name="otp" value=""/> <?= \Controls\submit_tag(__("Continue")) ?> </fieldset> @@ -150,6 +150,32 @@ class Auth_Internal extends Auth_Base { if ($user) { + // don't throttle app passwords + if (!$service && get_schema_version() >= 145) { + + if ($user->last_auth_attempt) { + $last_auth_attempt = strtotime($user->last_auth_attempt); + + if ($last_auth_attempt && time() - $last_auth_attempt < Config::get(Config::AUTH_MIN_INTERVAL)) { + Logger::log(E_USER_NOTICE, "Too many authentication attempts for {$user->login}, throttled."); + + // start an empty session to deliver login error message + if (session_status() != PHP_SESSION_ACTIVE) + session_start(); + + $_SESSION["login_error_msg"] = __("Too many authentication attempts, throttled."); + + $user->last_auth_attempt = Db::NOW(); + $user->save(); + + return false; + } + } + + $user->last_auth_attempt = Db::NOW(); + $user->save(); + } + $salt = $user['salt'] ?? ""; $login = $user['login']; $pwd_hash = $user['pwd_hash']; diff --git a/plugins/nsfw/init.js b/plugins/nsfw/init.js deleted file mode 100644 index 71fe4747b..000000000 --- a/plugins/nsfw/init.js +++ /dev/null @@ -1,18 +0,0 @@ -/* global Plugins */ - -Plugins.NSFW = { - toggle: function(elem) { - elem = elem.domNode || elem; - - const content = elem.closest(".nsfw-wrapper").querySelector('.nsfw-content'); - - // we can't use .toggle() here because this script could be invoked by the api client - // so it's back to vanilla js - - if (content.style.display == 'none') - content.style.display = ''; - else - content.style.display = 'none'; - } -} - diff --git a/plugins/nsfw/init.php b/plugins/nsfw/init.php index 8ace94b51..fc38fef71 100644 --- a/plugins/nsfw/init.php +++ b/plugins/nsfw/init.php @@ -20,10 +20,6 @@ class NSFW extends Plugin { } - function get_js() { - return file_get_contents(__DIR__ . "/init.js"); - } - function hook_article_image($enclosures, $content, $site_url, $article) { $tags = explode(",", $this->host->get($this, "tags")); $article_tags = $article["tags"]; @@ -35,27 +31,28 @@ class NSFW extends Plugin { } } - private function rewrite_contents($article, bool $add_api_js = false) { + private function rewrite_contents($article) { $tags = explode(",", $this->host->get($this, "tags")); $article_tags = $article["tags"]; if (count(array_intersect($tags, $article_tags)) > 0) { - $article["content"] = "<div class='nsfw-wrapper'>". - \Controls\button_tag(__("Not work safe (click to toggle)"), '', ['onclick' => 'Plugins.NSFW.toggle(this)']). - "<div class='nsfw-content' style='display : none'>".$article["content"]."</div> - </div>"; - - if ($add_api_js) { - $article["content"] .= "<script type='text/javascript'>const Plugins = {}; " . $this->get_js() . "</script>"; - } + $article["content"] = "<details class='nsfw'><summary>" . __("Not safe for work (click to toggle)") . "</summary>" . $article["content"] . "</details>"; } return $article; } + function get_css() { + return + 'details.nsfw { + cursor : pointer; + user-select : none; + }'; + } + function hook_render_article_api($row) { $article = isset($row['headline']) ? $row['headline'] : $row['article']; - return $this->rewrite_contents($article, true); + return $this->rewrite_contents($article); } function hook_render_article($article) { diff --git a/plugins/share/init.php b/plugins/share/init.php index 64a9054eb..195f9df2a 100644 --- a/plugins/share/init.php +++ b/plugins/share/init.php @@ -149,6 +149,9 @@ class Share extends Plugin { $line['content'] = DiskCache::rewrite_urls($line['content']); + if (!$og_image) + $og_image = Config::get_self_url() . "/images/favicon-512px.png"; + ob_start(); ?> @@ -180,27 +183,28 @@ class Share extends Plugin { strip_tags($content_decoded) ) ), 500, "...")) ?>"> - </head> - <?php if ($og_image) { ?> - <meta property='og:image' content="<?= htmlspecialchars($og_image) ?>"> - <?php } ?> + <?php if ($og_image) { ?> + <meta property='og:image' content="<?= htmlspecialchars($og_image) ?>"> + <?php } ?> + </head> <body class='flat ttrss_utility ttrss_zoom css_loading'> <div class='container'> - <?php if (!empty($line["link"])) { ?> - <h1> - <a target='_blank' rel='noopener noreferrer' - href="<?= htmlspecialchars($line["link"]) ?>"><?= htmlspecialchars($line["title"]) ?></a> - </h1> - <?php } else { ?> - <h1><?= $line["title"] ?></h1> - <?php } ?> - <div class='content post'> <div class='header'> <div class='row'> + <?php if (!empty($line["link"])) { ?> + <h1> + <a rel='noopener noreferrer' + href="<?= htmlspecialchars($line["link"]) ?>"><?= htmlspecialchars($line["title"]) ?></a> + </h1> + <?php } else { ?> + <h1><?= $line["title"] ?></h1> + <?php } ?> + </div> + <div class='row'> <div><?= $line['author'] ?></div> <div><?= $parsed_updated ?></div> </div> diff --git a/plugins/shorten_expanded/init.css b/plugins/shorten_expanded/init.css index 0966aa1f9..e0a209903 100644 --- a/plugins/shorten_expanded/init.css +++ b/plugins/shorten_expanded/init.css @@ -1,7 +1,8 @@ .content-shrink-wrap { overflow : hidden; text-overflow: ellipsis; - height : 800px; + height : 80vh; + margin-bottom : 8px; } .expand-prompt { diff --git a/plugins/shorten_expanded/init.js b/plugins/shorten_expanded/init.js index 0abc8c129..85d75d313 100644 --- a/plugins/shorten_expanded/init.js +++ b/plugins/shorten_expanded/init.js @@ -1,10 +1,55 @@ -/* global Plugins, __, require, PluginHost */ - -const _shorten_expanded_threshold = 1.5; //window heights +/* global Plugins, __, require, PluginHost, App, dojo */ Plugins.Shorten_Expanded = { + threshold: 1.5, // of window height + observer: new ResizeObserver((entries) => { + entries.forEach((entry) => { + const row = entry.target; + + Plugins.Shorten_Expanded.shorten_if_needed(row); + }); + }), + shorten_if_needed: function(row) { + + const content = row.querySelector(".content"); + const content_inner = row.querySelector(".content-inner"); + + //console.log('shorten_expanded', row.id, content.offsetHeight, 'vs', this.threshold * window.innerHeight); + + if (content && content_inner && !row.hasAttribute('data-already-shortened') && content.offsetHeight >= this.threshold * window.innerHeight) { + + row.setAttribute('data-already-shortened', true); + + const attachments = row.querySelector(".attachments-inline"); // optional + + content_inner.innerHTML = ` + <div class="content-shrink-wrap"> + ${content_inner.innerHTML} + ${attachments ? attachments.innerHTML : ''} + </div> + <button dojoType="dijit.form.Button" class="alt-info expand-prompt" onclick="return Plugins.Shorten_Expanded.expand('${row.id}')" href="#"> + ${App.FormFields.icon('add')} + ${__("Expand article")} + </button>`; + + if (attachments) + attachments.innerHTML = ""; + + dojo.parser.parse(content_inner); + + return true; + } + return false; + }, + process_row: function(row) { + + if (this.shorten_if_needed(row)) + return; + + this.observer.observe(row); + }, expand: function(id) { - const row = $(id); + const row = App.byId(id); if (row) { const content = row.querySelector(".content-shrink-wrap"); @@ -21,33 +66,7 @@ Plugins.Shorten_Expanded = { require(['dojo/_base/kernel', 'dojo/ready'], function (dojo, ready) { ready(function() { PluginHost.register(PluginHost.HOOK_ARTICLE_RENDERED_CDM, function(row) { - window.setTimeout(function() { - if (row) { - - const content = row.querySelector(".content-inner"); - - //console.log('shorten', row.offsetHeight, 'vs', _shorten_expanded_threshold * window.innerHeight); - - if (content && row.offsetHeight >= _shorten_expanded_threshold * window.innerHeight) { - - const attachments = row.querySelector(".attachments-inline"); // optional - - content.innerHTML = ` - <div class="content-shrink-wrap"> - ${content.innerHTML} - ${attachments ? attachments.innerHTML : ''} - </div> - <button dojoType="dijit.form.Button" class="alt-info expand-prompt" onclick="return Plugins.Shorten_Expanded.expand('${row.id}')" href="#"> - ${__("Click to expand article")}</button>`; - - if (attachments) - attachments.innerHTML = ""; - - dojo.parser.parse(content); - } - } - }, 150); - + Plugins.Shorten_Expanded.process_row(row); return true; }); }); diff --git a/plugins/shorten_expanded/init.php b/plugins/shorten_expanded/init.php index 9673f581b..c097f1a0d 100644 --- a/plugins/shorten_expanded/init.php +++ b/plugins/shorten_expanded/init.php @@ -10,22 +10,6 @@ class Shorten_Expanded extends Plugin { function init($host) { $this->host = $host; - - $host->add_hook($host::HOOK_SANITIZE, $this); - } - - // native lazy loading messes with plugin height calculation because images get loaded - // after headline is actually rendered (off screen) so we force disable it - function hook_sanitize($doc) { - $xpath = new DOMXPath($doc); - - $entries = $xpath->query('(//*[@loading="lazy"])'); - - foreach ($entries as $entry) { - $entry->removeAttribute("loading"); - } - - return $doc; } function get_css() { @@ -27,9 +27,7 @@ } } ?> - <?php if (theme_exists(Config::get(Config::LOCAL_OVERRIDE_STYLESHEET))) { - echo stylesheet_tag(get_theme_path(Config::get(Config::LOCAL_OVERRIDE_STYLESHEET))); - } ?> + <?= Config::get_override_links() ?> <script type="text/javascript"> const __csrf_token = "<?= $_SESSION["csrf_token"]; ?>"; @@ -95,11 +93,24 @@ } </style> + <noscript> + <style type="text/css"> + body.css_loading noscript { + display : block; + margin : 16px; + } + </style> + + <?= stylesheet_tag("themes/light.css") ?> + </noscript> + <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> </head> <body class="flat ttrss_main ttrss_prefs css_loading"> +<noscript class="alert alert-error"><?= ('Javascript is disabled. Please enable it.') ?></noscript> + <div id="notify" class="notify"></div> <div id="cmdline" style="display : none"></div> @@ -109,7 +120,6 @@ <div dojoType="dijit.ProgressBar" places="0" style="width : 300px" id="loading_bar" progress="0" maximum="100"> </div> - <noscript><br/><?php print_error('Javascript is disabled. Please enable it.') ?></noscript> </div> </div> diff --git a/sql/mysql/migrations/145.sql b/sql/mysql/migrations/145.sql new file mode 100644 index 000000000..b49eccafc --- /dev/null +++ b/sql/mysql/migrations/145.sql @@ -0,0 +1,2 @@ +alter table ttrss_users add column last_auth_attempt datetime; +alter table ttrss_users alter column last_auth_attempt set default null; diff --git a/sql/mysql/schema.sql b/sql/mysql/schema.sql index 7ecaff32b..d8e6e270a 100644 --- a/sql/mysql/schema.sql +++ b/sql/mysql/schema.sql @@ -54,7 +54,8 @@ create table ttrss_users (id integer primary key not null auto_increment, twitter_oauth longtext default null, otp_enabled boolean not null default false, otp_secret varchar(250) default null, - resetpass_token varchar(250) default null) ENGINE=InnoDB DEFAULT CHARSET=UTF8; + resetpass_token varchar(250) default null, + last_auth_attempt datetime default null) ENGINE=InnoDB DEFAULT CHARSET=UTF8; insert into ttrss_users (login,pwd_hash,access_level) values ('admin', 'SHA1:5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8', 10); diff --git a/sql/pgsql/migrations/145.sql b/sql/pgsql/migrations/145.sql new file mode 100644 index 000000000..754d06963 --- /dev/null +++ b/sql/pgsql/migrations/145.sql @@ -0,0 +1,2 @@ +alter table ttrss_users add column last_auth_attempt timestamp; +alter table ttrss_users alter column last_auth_attempt set default null; diff --git a/sql/pgsql/schema.sql b/sql/pgsql/schema.sql index 6130b633c..50bf375f2 100644 --- a/sql/pgsql/schema.sql +++ b/sql/pgsql/schema.sql @@ -52,6 +52,7 @@ create table ttrss_users (id serial not null primary key, otp_enabled boolean not null default false, otp_secret varchar(250) default null, resetpass_token varchar(250) default null, + last_auth_attempt timestamp default null, created timestamp default null); insert into ttrss_users (login,pwd_hash,access_level) values ('admin', diff --git a/themes/compact.css b/themes/compact.css index 5a3a00cfe..f6adece87 100644 --- a/themes/compact.css +++ b/themes/compact.css @@ -11,71 +11,81 @@ body.ttrss_prefs, } body.ttrss_main { background: white; - color: black; + color: #555; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 14px; overflow: hidden; + /*ul.compact { + list-style-type : none; + margin : 0; + padding : 0; + + li { + margin : 0; + padding : 0; + } + }*/ } body.ttrss_main :focus { outline: none; } -body.ttrss_main div.post { - padding: 0px; +body.ttrss_main .post { + padding: 0; font-size: 13px; } -body.ttrss_main div.post div.header { +body.ttrss_main .post .header { padding: 5px; - color: #909090; - border: 0px solid #ddd; + color: #555; + border: 0 solid #ddd; border-bottom-width: 1px; background: #f5f5f5; } -body.ttrss_main div.post div.header .left, -body.ttrss_main div.post div.header .right { +body.ttrss_main .post .header .left, +body.ttrss_main .post .header .right { display: flex; } -body.ttrss_main div.post div.header .row { +body.ttrss_main .post .header .row { display: flex; margin-bottom: 4px; flex-wrap: nowrap; align-items: center; justify-content: space-between; } -body.ttrss_main div.post div.header .comments { +body.ttrss_main .post .header .comments { flex-grow: 2; } -body.ttrss_main div.post div.header .date { +body.ttrss_main .post .header .date { white-space: nowrap; } -body.ttrss_main div.post div.header img, -body.ttrss_main div.post div.header i.material-icons { +body.ttrss_main .post .header img, +body.ttrss_main .post .header i.material-icons { margin: 0px 4px; vertical-align: middle; color: #777; } -body.ttrss_main div.post div.header .title { +body.ttrss_main .post .header .title { flex-grow: 2; font-size: 15px; font-weight: 600; text-rendering: optimizelegibility; - font-family: "Segoe WP Semibold", "Segoe UI Semibold", "Segoe UI Web Semibold", "Segoe UI", Ubuntu, "Helvetica Neue", Helvetica, Arial, sans-serif; + font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif; } -body.ttrss_main div.post div.content { +body.ttrss_main .post div.content { padding: 10px; font-size: 16px; } -body.ttrss_main div.post div.content img, -body.ttrss_main div.post div.content video { +body.ttrss_main .post div.content img, +body.ttrss_main .post div.content video { border-width: 0px; max-width: 98%; height: auto; } -body.ttrss_main div.post div.content div.embed-responsive { +body.ttrss_main .post div.content div.embed-responsive { overflow: hidden; padding-bottom: 56.25%; position: relative; } -body.ttrss_main div.post div.content div.embed-responsive iframe { +body.ttrss_main .post div.content div.embed-responsive iframe { border: 0; bottom: 0; height: 100%; @@ -172,6 +182,11 @@ body.ttrss_main .notify.notify_progress { border-color: #d7c47a; background-color: #fff7d5; } +body.ttrss_main .notify.notify_progress img { + width: 18px; + height: 18px; + filter: saturate(0); +} body.ttrss_main .notify.notify_info { border-color: #257aa7; background-color: #d5ebf6; @@ -217,6 +232,10 @@ body.ttrss_main .hl .right { display: flex; align-items: center; } +body.ttrss_main .hl .left img, +body.ttrss_main .hl .right img { + margin: 0 4px; +} body.ttrss_main .hl .left i.material-icons, body.ttrss_main .hl .right i.material-icons { margin-left: 2px; @@ -225,25 +244,28 @@ body.ttrss_main .hl .right i.material-icons { user-select: none; font-size: 21px; } +body.ttrss_main .hl .left input { + margin: 0 4px; +} +body.ttrss_main .hl .right { + text-align: right; +} body.ttrss_main .hl .right i.material-icons { color: #777; } -body.ttrss_main .hl div.title { +body.ttrss_main .hl .title { cursor: pointer; flex-grow: 2; overflow: hidden; text-overflow: ellipsis; } -body.ttrss_main .hl span.author { +body.ttrss_main .hl .author { white-space: nowrap; color: #555; font-size: 11px; font-weight: normal; } -body.ttrss_main .hl div.right { - text-align: right; -} -body.ttrss_main .hl span.feed a { +body.ttrss_main .hl .feed a { border-radius: 4px; display: inline-block; padding: 1px 4px; @@ -252,29 +274,22 @@ body.ttrss_main .hl span.feed a { font-weight: normal; color: #555; } -body.ttrss_main .hl span.feed a:hover { +body.ttrss_main .hl .feed a:hover { color: #257aa7; } -body.ttrss_main .hl span.updated { +body.ttrss_main .hl .updated { color: #555; text-align: right; font-size: 11px; padding-left: 10px; } -body.ttrss_main .hl span.updated div { +body.ttrss_main .hl .updated div { display: inline-block; } -body.ttrss_main .hl div.left input { - margin: 0px 4px; -} -body.ttrss_main .hl div.left img, -body.ttrss_main .hl div.right img { - margin: 0px 4px; -} body.ttrss_main .hl div.title a { font-weight: 600; text-rendering: optimizelegibility; - font-family: "Segoe UI", Ubuntu, "Helvetica Neue", Helvetica, Arial, sans-serif; + font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif; color: #555; } body.ttrss_main .hl a.title.high, @@ -287,10 +302,10 @@ body.ttrss_main .hl.vgrlf .feed { body.ttrss_main .hl.Unread { background: white; } -body.ttrss_main .hl.Unread div.title a { +body.ttrss_main .hl.Unread .title a { color: black; } -body.ttrss_main .hl.active div.title a { +body.ttrss_main .hl.active .title a { color: #257aa7; /* text-shadow : 1px 1px 2px #fff; */ } @@ -312,9 +327,6 @@ body.ttrss_main .hl.active span, body.ttrss_main .hl.Selected span { color: white; } -body.ttrss_main .hl.Grayed { - color: #909090; -} body.ttrss_main #content-insert blockquote, body.ttrss_main #headlines-frame blockquote, body.ttrss_main .dijitContentPane blockquote { @@ -360,7 +372,7 @@ body.ttrss_main .label { vertical-align: middle; background-color: #fff7d5; font-size: 9px; - color: black; + color: #555; font-weight: normal; margin-left: 2px; padding: 2px 4px; @@ -371,45 +383,6 @@ body.ttrss_main i.pub-pic { cursor: pointer; color: #ccc; } -body.ttrss_main div.errorExplained { - border: 1px solid #ddd; - margin: 5px 0px 5px 0px; - padding: 5px; -} -body.ttrss_main ul.browseFeedList { - height: 300px; - width: 100%; - overflow: auto; - border-width: 0px 1px 1px 1px; - border-color: #ddd; - border-style: solid; - margin: 0px 0px 5px 0px; - background-color: white; - list-style-type: none; - padding: 0px; -} -body.ttrss_main ul.browseFeedList li { - display: flex; - align-items: center; -} -body.ttrss_main ul.browseFeedList li > * { - margin: 2px; -} -body.ttrss_main .browseFeedList span.subscribers { - color: #808080; -} -body.ttrss_main ul.compact { - list-style-type: none; - margin: 0px; - padding: 0px; -} -body.ttrss_main ul.compact li { - margin: 0px; - padding: 0px; -} -body.ttrss_main .noborder { - border-width: 0px; -} body.ttrss_main #overlay { background: white; left: 0; @@ -423,11 +396,6 @@ body.ttrss_main #overlay_inner { font-weight: bold; margin: 1em; } -body.ttrss_main div.loadingPrompt { - padding: 1em; - text-align: center; - font-weight: bold; -} body.ttrss_main div.whiteBox { margin-left: 1px; text-align: center; @@ -436,20 +404,13 @@ body.ttrss_main div.whiteBox { border: 0px solid #ddd; border-bottom-width: 1px; } -body.ttrss_main div#headlines-frame.wide .title { - overflow: visible; - white-space: normal; -} -body.ttrss_main div#headlines-frame.wide .hl .feed { - display: none; -} body.ttrss_main .dijitDialog header, body.ttrss_main .dijitDialog .dlgSec, body.ttrss_main .dijitDialog .dlgSecHoriz { font-size: 16px; font-weight: 600; color: #555; - font-family: "Segoe WP Semibold", "Segoe UI Semibold", "Segoe UI Web Semibold", "Segoe UI", Ubuntu, "Helvetica Neue", Helvetica, Arial, sans-serif; + font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif; } body.ttrss_main .dijitDialog section, body.ttrss_main .dijitDialog .dlgSecCont { @@ -527,11 +488,6 @@ body.ttrss_main div#cmdline { padding: 3px 5px 3px 5px; z-index: 5; } -body.ttrss_main #feed_browser_spinner { - vertical-align: middle; - height: 18px; - width: 18px; -} body.ttrss_main .exception-contents h3 { color: red; } @@ -540,14 +496,15 @@ body.ttrss_main .exception-contents textarea { height: 200px; font-size: 11px; } +body.ttrss_main #headlines-wrap-inner, body.ttrss_main #content-wrap { - padding: 0px; - border-width: 0px; - margin: 0px; + padding: 0; + border: 0; + margin: 0; } body.ttrss_main #feeds-holder { - padding: 0px; - border: 0px solid #ddd; + padding: 0; + border: 0 solid #ddd; overflow: hidden; background: #f5f5f5; box-shadow: inset -1px 0px 2px -1px rgba(0, 0, 0, 0.1); @@ -557,19 +514,26 @@ body.ttrss_main #feeds-holder #feedTree { height: 100%; overflow-x: hidden; text-rendering: optimizelegibility; - font-family: "Segoe UI", Ubuntu, "Helvetica Neue", Helvetica, Arial, sans-serif; + font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif; } -body.ttrss_main #feeds-holder #feedTree .counterNode.aux, -body.ttrss_main #feeds-holder #feedTree .counterNode.marked { - background: #f5f5f5; - color: #6f6f6f; - border-color: #dcdcdc; +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow { + border: 1px solid transparent; } -body.ttrss_main #feeds-holder #feedTree .counterNode.marked { - border-color: #257aa7; - background: #ffffff; +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow.Is_Feed .loadingNode { + left: -2px; + height: 14px; + width: 14px; + position: relative; +} +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow.Is_Cat .loadingNode { + height: 11px; + width: 18px; + margin-left: 4px; + position: relative; + top: 2px; } -body.ttrss_main #feeds-holder #feedTree .counterNode { +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow .counterNode { + order: 2; font-weight: bold; display: none; font-size: 9px; @@ -578,111 +542,215 @@ body.ttrss_main #feeds-holder #feedTree .counterNode { color: white; background: #2a89bc; border-radius: 4px; - vertical-align: middle; - float: right; - position: relative; line-height: 14px; margin-right: 8px; - margin-top: 2px; min-width: 23px; height: 14px; + flex-shrink: 0; +} +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow .counterNode.aux, +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow .counterNode.marked { + background: #f5f5f5; + color: #6f6f6f; + border-color: #dcdcdc; +} +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow .counterNode.marked { + border-color: #257aa7; + background: #ffffff; +} +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow[data-feed-id="-3"][data-is-cat="false"] .counterNode.unread { + background-color: #3ea447; + border-color: #307f37; } -body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .loadingExpando { - left: -3px; - height: 22px; +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow .dijitTreeContent { + display: flex; + align-items: center; + flex-grow: 2; + min-width: 0; +} +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow .dijitTreeLabel { + cursor: pointer; + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; position: relative; - top: -3px; } -body.ttrss_main #feeds-holder #feedTree .dijitTreeRow .dijitTreeLabel.Unread { +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow .dijitTreeLabel.Unread { font-weight: bold; } -body.ttrss_main #feeds-holder #feedTree .dijitTreeRow.Error .dijitTreeLabel { +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow.Error .dijitTreeLabel { color: red; } -body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow { - border: 1px solid transparent; -} -body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRowSelected { +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow.dijitTreeRowSelected { box-shadow: -1px 0px 2px -1px rgba(0, 0, 0, 0.1); border-color: #ddd transparent; background: white; - color: #333; + color: #555; } -body.ttrss_main #feeds-holder #feedTree .dijitIcon.feed-icon { +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow .dijitIcon.feed-icon { margin-right: 2px; } -body.ttrss_main #feeds-holder #feedTree i.icon.icon-inbox { - color: #555; +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow i.icon.icon-inbox { + color: #888888; } -body.ttrss_main #feeds-holder #feedTree i.icon.icon-archive { - color: #c77b2e; +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow i.icon.icon-archive { + color: #cf7800; } -body.ttrss_main #feeds-holder #feedTree i.icon.icon-star { +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow i.icon.icon-star { position: relative; color: #ffc069; font-size: 21px; left: -2px; } -body.ttrss_main #feeds-holder #feedTree i.icon.icon-rss_feed { +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow i.icon.icon-rss_feed { color: #ff7c4b; } -body.ttrss_main #feeds-holder #feedTree i.icon.icon-whatshot { +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow i.icon.icon-whatshot { color: #69C671; } -body.ttrss_main #feeds-holder #feedTree i.icon.icon-restore { - position: relative; - top: -1px; +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow i.icon.icon-restore { font-weight: bold; color: #257aa7; } -body.ttrss_main #headlines-wrap-inner { - padding: 0px; - margin: 0px; - border-width: 0px; +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(600px, 1fr)); + padding: 8px; + grid-gap: 8px; + background-color: #f5f5f5; +} +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] > * { + /* 2 = because #headlines-spacer is the actual last child + only if odd to deal with 1) single article and 2) not break any previous rows; + 1 = spacer; + + this is outside of .cdm selector because of #headlines-spacer etc + + .grid-span-row is manually expanded RROWs + */ +} +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] > *.grid-span-row, +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] > *:nth-last-child(2):nth-child(odd), +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] > *:nth-last-child(1) { + grid-column: 1 / -1; +} +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] .cdm.expanded .header, +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] .cdm.expanded .content { + background: white; + border: 1px solid #ddd; + overflow: hidden; +} +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] .cdm.expanded .content { + border-top-width: 0; + padding: 0 4px 4px 4px; } -body.ttrss_main #headlines-frame[is-vfeed="0"] .header .feed { +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] .cdm.expanded .header[data-is-stuck] { + top: -8px; + border-bottom-width: 1px; +} +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] .cdm.expanded .header { + border-bottom-width: 0; + padding: 4px; +} +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] .cdm.expanded .header .icon-grid-span { + display: inline; +} +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] .cdm.expanded .header .feed { display: none; } +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] .cdm.expanded .footer { + border: 0; + padding: 4px; +} +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] .cdm.expanded .footer .left, +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] .cdm.expanded .footer .right { + white-space: nowrap; +} +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] .cdm.expanded .footer .left { + overflow: hidden; + text-overflow: ellipsis; +} +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] .cdm.expanded .content-inner a, +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] .cdm.expanded .intermediate a { + word-break: break-all; +} body.ttrss_main #headlines-frame { - padding: 0px; - border: 0px #ddd; - margin-top: 0px; + padding: 0; + border: 0; + margin-top: 0; -webkit-overflow-scrolling: touch; -webkit-transform: translateZ(0); + transform: translateZ(0); -webkit-backface-visibility: hidden; + backface-visibility: hidden; +} +body.ttrss_main #headlines-frame[data-is-vfeed="false"] .header .feed, +body.ttrss_main #headlines-frame[data-is-vfeed="false"] .hl .feed, +body.ttrss_main #headlines-frame[data-is-vfeed="false"] .header .icon-feed, +body.ttrss_main #headlines-frame[data-is-vfeed="false"] .hl .icon-feed { + display: none; +} +body.ttrss_main #headlines-frame[data-auto-catchup="true"] #headlines-spacer { + height: 100vh; +} +body.ttrss_main #headlines-frame .dijitCheckBox { + margin-right: 4px; +} +body.ttrss_main #headlines-frame[data-is-wide-screen="true"] .title { + overflow: visible; + white-space: normal; +} +body.ttrss_main #headlines-frame[data-is-wide-screen="true"] .hl .feed { + display: none; +} +body.ttrss_main #headlines-frame #headlines-spacer { + margin-left: 1px; + text-align: center; + color: #555; + font-size: 11px; +} +body.ttrss_main #headlines-frame #headlines-spacer a, +body.ttrss_main #headlines-frame #headlines-spacer span { + color: #555; + padding: 10px; + display: block; } -body.ttrss_main #headlines-frame div.feed-title { +body.ttrss_main #headlines-frame #headlines-spacer a:hover { + color: #257aa7; +} +body.ttrss_main #headlines-frame .feed-title { border: 0px solid #257aa7; border-bottom-width: 1px; padding: 5px 8px; } -body.ttrss_main #headlines-frame div.feed-title a.title { +body.ttrss_main #headlines-frame .feed-title a.title { color: #555; font-weight: bold; } -body.ttrss_main #headlines-frame div.feed-title a { +body.ttrss_main #headlines-frame .feed-title a { color: #555; } -body.ttrss_main #headlines-frame div.feed-title a:hover { +body.ttrss_main #headlines-frame .feed-title a:hover { color: #257aa7; } body.ttrss_main #headlines-frame span.hlMenuAttach { -webkit-touch-callout: none; -webkit-user-select: none; + user-select: none; } body.ttrss_main #toolbar-frame_splitter { display: none; } body.ttrss_main #toolbar-frame { - padding: 0px; - margin: 0px; - border-width: 0px; + padding: 0; + margin: 0; + border: 0; white-space: nowrap; - font-size: 12px; + font-size: 13px; } body.ttrss_main #toolbar-frame #toolbar { background: white; - border: 0px solid #ddd; + border: 0 solid #ddd; border-bottom-width: 1px; padding-left: 4px; height: 32px; @@ -690,13 +758,13 @@ body.ttrss_main #toolbar-frame #toolbar { flex-direction: row; flex-wrap: nowrap; color: #555; - font-size: 12px; + font-size: 13px; align-items: center; } body.ttrss_main #toolbar-frame #toolbar .dijitSelect, body.ttrss_main #toolbar-frame #toolbar .dijitDropDownButton .dijitButtonNode, body.ttrss_main #toolbar-frame #toolbar .dijitComboButton .dijitButtonNode { - border: 0px; + border: 0; } body.ttrss_main #toolbar-frame #toolbar i.net-alert, body.ttrss_main #toolbar-frame #toolbar .left i.icon-error { @@ -710,7 +778,7 @@ body.ttrss_main #toolbar-frame #toolbar i { margin: 0 4px; } body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines { - font-size: 12px; + font-size: 13px; background: transparent; padding-right: 4px; flex-grow: 2; @@ -725,6 +793,12 @@ body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines .left .feed_title, body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines .left .cancel_search { margin-left: 4px; } +@media (max-width: 768px) { + body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines .left .feed_title, + body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines .left i.icon-syndicate { + display: none; + } +} body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines .right { display: flex; align-items: center; @@ -739,20 +813,26 @@ body.ttrss_main #toolbar-frame #toolbar #selected_prompt { margin-right: 4px; color: #257aa7; } -@media (max-width: 992px) { +@media (max-width: 768px) { body.ttrss_main #toolbar-frame #toolbar #selected_prompt { display: none; } } +@media (max-width: 576px) { + body.ttrss_main #toolbar-frame #toolbar .select-articles-dropdown, + body.ttrss_main #toolbar-frame #toolbar .catchup-button { + display: none; + } +} body.ttrss_main #header { - border-width: 0px; text-align: right; color: #555; - padding: 5px 5px 0px 0px; - margin: 0px; + padding: 5px 5px 0 0px; position: absolute; - right: 0px; - top: 0px; + border: 0; + margin: 0; + right: 0; + top: 0; z-index: 5; } body.ttrss_main #header i.net-alert, @@ -771,9 +851,8 @@ body.ttrss_main #header i { margin: 0 4px; } body.ttrss_main #content-insert { - padding: 0px; - border-color: #ddd; - border-width: 0px; + padding: 0; + border: 0; line-height: 1.5; overflow: auto; -webkit-overflow-scrolling: touch; @@ -786,59 +865,11 @@ body.ttrss_main img.icon { vertical-align: middle; display: inline-block; } -body.ttrss_main .player { - display: inline-block; - color: #555; - font-size: 11px; - font-family: sans-serif; - border: 1px solid #555; - padding: 0px 4px 0px 4px; - margin: 0px 2px 0px 2px; - width: 50px; - text-align: center; - background: white; -} -body.ttrss_main .player.playing { - color: #00c000; - border-color: #00c000; -} -body.ttrss_main .player:hover { - background: #f5f5f5; - cursor: pointer; -} -body.ttrss_main #headlines-frame.auto_catchup #headlines-spacer { - height: 100%; -} -body.ttrss_main #headlines-spacer { - margin-left: 1px; - text-align: center; - color: #555; - font-size: 11px; - font-style: italic; -} -body.ttrss_main #headlines-spacer a, -body.ttrss_main #headlines-spacer span { - color: #555; - padding: 10px; - display: block; -} -body.ttrss_main #headlines-spacer a:hover { - color: #257aa7; -} body.ttrss_main ul#filterDlg_Matches, body.ttrss_main ul#filterDlg_Actions { list-style-type: none; margin: 0; padding: 0; - /*max-height : 100px; - overflow : auto; - border-style : solid; - border-color : @border-default; - border-width : 1px 1px 1px 1px; - background-color : @default-bg; - margin : 0px 0px 5px 0px; - padding : 4px; - min-height : 16px;*/ } body.ttrss_main ul#filterDlg_Matches li, body.ttrss_main ul#filterDlg_Actions li { @@ -874,9 +905,6 @@ body.ttrss_main span.highlight { background-color: #ffff00; color: #cc90cc; } -body.ttrss_main #headlines-frame .dijitCheckBox { - margin-right: 4px; -} body.ttrss_main #feedEditDlg img.feedIcon { border: 1px solid #ccc; padding: 5px; @@ -934,6 +962,11 @@ body.ttrss_main:not([view-mode="marked"])[hide-read-feeds="true"][hide-read-show body.ttrss_main:not([view-mode="marked"])[hide-read-feeds="true"][hide-read-shows-special="false"] #feeds-holder #feedTree .dijitTreeRow:not(.Unread):not(.AlwaysVisible) { display: none; } +body.ttrss_main { + /*.score-neutral i.icon-score { + opacity : 0.5; + }*/ +} body.ttrss_main #toolbar-headlines i.icon-syndicate { color: #ff7c4b; margin-right: 8px; @@ -970,10 +1003,15 @@ body.ttrss_main .score-high i.icon-score { body.ttrss_main .score-low i.icon-score { color: #500; } -body.ttrss_main .score-neutral i.icon-score { - opacity: 0.5; +body.ttrss_main i.icon-score, +body.ttrss_main i.icon-grid-span { + cursor: pointer; + color: #777; } -body.ttrss_main i.icon-score { +body.ttrss_main .icon-grid-span { + display: none; +} +body.ttrss_main .icon-feed { cursor: pointer; } body.ttrss_main .panel { @@ -1004,20 +1042,62 @@ body.ttrss_main ul.list-unstyled { body.ttrss_main .text-center { text-align: center; } -body.ttrss_main #prefFilterTestResultList .preview { +body.ttrss_main .text-right { + text-align: right; +} +body.ttrss_main .text-left { + text-align: left; +} +body.ttrss_main .dijitDialog .filter-results-list .preview { margin: 8px; } -body.ttrss_main #prefFilterTestResultList .title { +body.ttrss_main .dijitDialog .filter-results-list .title { font-weight: bold; } -body.ttrss_main #prefFilterTestResultList .feed { - color: #257aa7; +body.ttrss_main .dijitDialog #feed_add_spinner { + position: relative; + top: 5px; + width: 18px; + height: 18px; +} +body.ttrss_main .icon-three-dots { + width: 18px; + height: 18px; + vertical-align: middle; +} +body.ttrss_main, +body.ttrss_utility { + /*div.autocomplete { + position : absolute; + width : 250px; + background-color : @default-bg; + border :1px solid @border-default; + margin : 0px; + padding : 0px; + + ul { + list-style-type : none; + margin : 0px; + padding : 0px; + } + + ul li.selected { + background-color : darken(@default-bg, 10%); + } + + ul li { + list-style-type : none; + display : block; + margin : 0; + padding : 2px; + cursor : pointer; + } + }*/ } body.ttrss_main .alert, body.ttrss_utility .alert { padding: 8px 35px 8px 14px; margin-bottom: 10px; - /* text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); */ background-color: #fcf8e3; border: 1px solid #fbeed5; border-radius: 4px; @@ -1058,6 +1138,16 @@ body.ttrss_main .text-warning, body.ttrss_utility .text-warning { color: #a47e3c; } +body.ttrss_main .text-muted, +body.ttrss_utility .text-muted { + color: #555; +} +body.ttrss_main .text-small, +body.ttrss_utility .text-small, +body.ttrss_main .small, +body.ttrss_utility .small { + font-size: 11px; +} body.ttrss_main .alert, body.ttrss_utility .alert, body.ttrss_main .alert h4, @@ -1107,41 +1197,6 @@ body.ttrss_utility hr { border: 0px solid #ccc; border-bottom-width: 1px; } -body.ttrss_main .text-muted, -body.ttrss_utility .text-muted { - color: #555; -} -body.ttrss_main .small, -body.ttrss_utility .small { - font-size: 11px; -} -body.ttrss_main div.autocomplete, -body.ttrss_utility div.autocomplete { - position: absolute; - width: 250px; - background-color: white; - border: 1px solid #ddd; - margin: 0px; - padding: 0px; -} -body.ttrss_main div.autocomplete ul, -body.ttrss_utility div.autocomplete ul { - list-style-type: none; - margin: 0px; - padding: 0px; -} -body.ttrss_main div.autocomplete ul li.selected, -body.ttrss_utility div.autocomplete ul li.selected { - background-color: #e6e6e6; -} -body.ttrss_main div.autocomplete ul li, -body.ttrss_utility div.autocomplete ul li { - list-style-type: none; - display: block; - margin: 0; - padding: 2px; - cursor: pointer; -} ::selection { background: #257aa7; color: white; @@ -1155,16 +1210,13 @@ body.ttrss_utility div.autocomplete ul li { ::-webkit-scrollbar-track { background-color: #eee; } -video::-webkit-media-controls-overlay-play-button { - display: none; -} -.cdm i.material-icons { - color: #777; -} -.cdm .header { - position: sticky; - top: 0; - z-index: 3; +/*video::-webkit-media-controls-overlay-play-button { + display: none; +}*/ +.cdm { + /*i.material-icons { + color : @color-icon; + }*/ } .cdm .header, .cdm .footer { @@ -1175,13 +1227,16 @@ video::-webkit-media-controls-overlay-play-button { .cdm .header img, .cdm .footer img, .cdm .footer i.material-icons { - margin: 0px 4px; + margin: 0 4px; vertical-align: middle; } .cdm .header-sticky-guard { height: 0; } .cdm .header { + position: sticky; + top: 0; + z-index: 3; align-items: center; } .cdm .header > * { @@ -1204,7 +1259,7 @@ video::-webkit-media-controls-overlay-play-button { .cdm .header .titleWrap { flex-grow: 2; } -.cdm .header span.updated { +.cdm .header .updated { color: #555; font-weight: normal; font-size: 11px; @@ -1213,6 +1268,30 @@ video::-webkit-media-controls-overlay-play-button { .cdm .header input { margin: 0px 4px; } +.cdm .header .feed { + float: right; + font-weight: normal; + font-style: italic; +} +.cdm .header .feed a { + border-radius: 4px; + display: inline-block; + padding: 1px 4px 1px 4px; +} +.cdm .header .feed, +.cdm .header .feed a { + vertical-align: middle; + color: #555; + font-weight: normal; + font-style: italic; + font-size: 11px; +} +.cdm .header .author { + white-space: nowrap; + color: #555; + font-size: 11px; + font-weight: normal; +} .cdm .footer { height: 30px; padding-left: 5px; @@ -1221,30 +1300,37 @@ video::-webkit-media-controls-overlay-play-button { clear: both; align-items: center; } +.cdm .footer i.material-icons { + color: #777; +} .cdm .footer .left { flex-grow: 2; } .cdm .intermediate { margin-top: 10px; margin-left: 10px; + line-height: 1.5; } .cdm .content-inner { margin: 10px; line-height: 1.5; font-size: 16px; } +.cdm .intermediate iframe, +.cdm .content-inner iframe { + max-width: 98%; + width: auto; + height: auto; +} .cdm .intermediate img, .cdm .intermediate video, .cdm .content-inner img, .cdm .content-inner video { border-width: 0px; max-width: 98%; + width: auto; height: auto; } -.cdm.expanded { - /*margin-top : 4px; - margin-bottom : 4px;*/ -} .cdm.expanded .collapse, .cdm.expanded .excerpt { display: none; @@ -1257,72 +1343,54 @@ video::-webkit-media-controls-overlay-play-button { border-bottom-width: 1px; } .cdm.expanded > hr { - margin-top: 0px; - margin-bottom: 0px; + margin-top: 0; + margin-bottom: 0; } -div.cdm.expanded div.header a.title { +.cdm.expanded .header a.title { font-size: 16px; color: #999; font-weight: 600; transition: color 0.2s, background 0.2s; text-rendering: optimizelegibility; - font-family: "Segoe WP Semibold", "Segoe UI Semibold", "Segoe UI Web Semibold", "Segoe UI", Ubuntu, "Helvetica Neue", Helvetica, Arial, sans-serif; + font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif; } -div.cdm.expanded.active { - background: white; +.cdm.expanded.active .content, +.cdm.expanded.Unread .content { + color: black; } -div.cdm.expanded.active div.header a.title { +.cdm.expanded.active .header .title { color: #257aa7; } -div.cdm.expanded.Unread div.header a.title { +.cdm.expanded.Unread .header .title { color: black; } -div.cdm.expanded div.content { +.cdm.expanded .content { color: #555; } -div.cdm.expanded.Unread div.content { - color: black; -} -div.cdm.active div.content { - color: black; -} -div.cdm.vgrlf .feed { +.cdm.vgrlf .feed { display: none; } -.cdm div.feed-title { +.cdm .feed-title { border: 0px solid #257aa7; border-bottom-width: 1px; padding: 5px 3px 5px 5px; } -.cdm div.feed-title a.title { +.cdm .feed-title a.title { color: #555; font-weight: bold; } -.cdm div.feed-title a { +.cdm .feed-title a { color: #555; } -.cdm div.feed-title a:hover { +.cdm .feed-title a:hover { color: #257aa7; } -.cdm div.header span.feed { - float: right; - font-weight: normal; - font-style: italic; -} -.cdm div.header div.feed, -.cdm div.header div.feed a { - vertical-align: middle; - color: #555; - font-weight: normal; - font-style: italic; - font-size: 11px; -} -.cdm div.content-inner div.embed-responsive { +.cdm .content-inner .embed-responsive { overflow: hidden; padding-bottom: 56.25%; position: relative; } -.cdm div.content-inner div.embed-responsive iframe { +.cdm .content-inner .embed-responsive iframe { border: 0; bottom: 0; height: 100%; @@ -1331,17 +1399,6 @@ div.cdm.vgrlf .feed { top: 0; width: 100%; } -.cdm div.header span.author { - white-space: nowrap; - color: #555; - font-size: 11px; - font-weight: normal; -} -.cdm .feed a { - border-radius: 4px; - display: inline-block; - padding: 1px 4px 1px 4px; -} .cdm.expandable { background-color: #f5f5f5; border: 0px solid #ddd; @@ -1379,43 +1436,43 @@ div.cdm.vgrlf .feed { .cdm.expandable.active { background: white ! important; } -div.cdm.expandable.active div.header span.titleWrap { +.cdm.expandable.active div.header span.titleWrap { white-space: normal; } -div.cdm.expandable div.header a.title { +.cdm.expandable .header a.title { font-weight: 600; color: #555; font-size: 14px; transition: color 0.2s, background 0.2s; text-rendering: optimizelegibility; - font-family: "Segoe WP Semibold", "Segoe UI Semibold", "Segoe UI Web Semibold", "Segoe UI", Ubuntu, "Helvetica Neue", Helvetica, Arial, sans-serif; + font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif; } -div.cdm.expandable.Unread div.header a.title { +.cdm.expandable.Unread div.header a.title { color: black; } -div.cdm.expandable.active .collapse i.material-icons { +.cdm.expandable.active .collapse i.material-icons { color: #257aa7; cursor: pointer; } -div.cdm.expandable.active .excerpt { +.cdm.expandable.active .excerpt { display: none; } -div.cdm.expandable.active div.header a.title { +.cdm.expandable.active div.header a.title { color: #257aa7; font-size: 16px; font-weight: 600; text-rendering: optimizelegibility; - font-family: "Segoe WP Semibold", "Segoe UI Semibold", "Segoe UI Web Semibold", "Segoe UI", Ubuntu, "Helvetica Neue", Helvetica, Arial, sans-serif; + font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif; } -div.cdm.expandable:not(.active) { +.cdm.expandable:not(.active) { cursor: pointer; } -div.cdm.expandable:not(.active) .content, -div.cdm.expandable:not(.active) .collapse { +.cdm.expandable:not(.active) .content, +.cdm.expandable:not(.active) .collapse { display: none; } -div.cdm.expandable.active .header[stuck], -div.cdm.expanded .header[stuck] { +.cdm.expandable.active .header[data-is-stuck], +.cdm.expanded .header[data-is-stuck] { box-shadow: 0 1px 1px -1px rgba(0, 0, 0, 0.1); border: 0 solid #ddd; border-bottom-width: 1px; @@ -1432,7 +1489,7 @@ body.ttrss_prefs h1, body.ttrss_prefs h2, body.ttrss_prefs h3, body.ttrss_prefs h4 { - font-family: "Segoe WP Semibold", "Segoe UI Semibold", "Segoe UI Web Semibold", "Segoe UI", Ubuntu, "Helvetica Neue", Helvetica, Arial, sans-serif; + font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif; font-weight: 600; color: #555; } @@ -1503,14 +1560,6 @@ body.ttrss_prefs fieldset.prefs .help-text { body.ttrss_prefs fieldset.prefs .help-text-bottom { margin-top: 10px; } -body.ttrss_prefs fieldset.plugin label.description { - width: 550px; - margin-right: 150px; - display: inline-block; -} -body.ttrss_prefs fieldset.plugin label.description .dijitCheckBox { - margin-right: 10px; -} body.ttrss_prefs table th { text-align: left; } @@ -1538,17 +1587,26 @@ body.ttrss_prefs ul.prefs-plugin-list li > * { body.ttrss_prefs ul.prefs-plugin-list li label.checkbox { display: flex; align-items: center; - min-width: 300px; cursor: pointer; } body.ttrss_prefs ul.prefs-plugin-list li label.checkbox.system { cursor: auto; } body.ttrss_prefs ul.prefs-plugin-list li label.checkbox .name { + min-width: 300px; flex-grow: 2; display: inline-block; text-align: right; font-weight: bold; + margin-right: 20px; +} +@media (max-width: 992px) { + body.ttrss_prefs ul.prefs-plugin-list li label.checkbox .name { + min-width: 200px ! important; + } + body.ttrss_prefs ul.prefs-plugin-list li .version { + display: none; + } } body.ttrss_prefs ul.prefs-plugin-list li .actions { flex-grow: 2; @@ -1602,6 +1660,11 @@ body.ttrss_prefs .event-log tr .errno { body.ttrss_prefs .event-log tr .errstr { word-break: break-all; white-space: pre-wrap; + width: 30%; +} +body.ttrss_prefs .event-log tr .filename { + word-break: break-all; + width: 20%; } body.ttrss_prefs .event-log tr .filename, body.ttrss_prefs .event-log tr .login, @@ -1665,7 +1728,7 @@ body.ttrss_utility.sanity_failed { } body.ttrss_utility { background: #f5f5f5; - color: black; + color: #555; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 14px; margin: 4em; @@ -1683,7 +1746,7 @@ body.ttrss_utility .content h2, body.ttrss_utility .content h3, body.ttrss_utility .content h4 { color: #257aa7; - font-family: "Segoe WP Semibold", "Segoe UI Semibold", "Segoe UI Web Semibold", "Segoe UI", Ubuntu, "Helvetica Neue", Helvetica, Arial, sans-serif; + font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif; } body.ttrss_utility .content h2 { font-size: 18px; @@ -1702,7 +1765,7 @@ body.ttrss_utility a:focus { } body.ttrss_utility h1 { color: gray; - font-family: "Segoe WP Semibold", "Segoe UI Semibold", "Segoe UI Web Semibold", "Segoe UI", Ubuntu, "Helvetica Neue", Helvetica, Arial, sans-serif; + font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 18px; margin: 10px 0 0 0; } @@ -1758,12 +1821,16 @@ body.ttrss_utility.share_popup .content { border-width: 0; box-shadow: none; } +.flat { + /*#feedTree { + .dijitTreeContent .dijitInline { + vertical-align : baseline; + } + }*/ +} .flat li { padding: 2px; } -.flat #feedTree .dijitTreeContent .dijitInline { - vertical-align: baseline; -} .flat .dijitButton i.material-icons { position: relative; top: -1px; @@ -1787,7 +1854,8 @@ body.ttrss_utility.share_popup .content { } .flat .dijitToolbar { font-size: 13px; - padding: 0px; + font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif; + padding: 0; } .flat .dijitToolbar .dijitTextBox .dijitInputContainer .dijitInputInner { line-height: 10px; @@ -1810,12 +1878,13 @@ body.ttrss_utility.share_popup .content { .flat .dijitMenu .dijitMenuItem .dijitMenuItemLabel { padding: 4px 8px; font-size: 13px; + font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif; } .flat .dijitMenu .dijitMenuItem.dijitDisabled:not(.dijitMenuItemSelected) .dijitMenuItemLabel { color: #2e99d1; } .flat .dijitMenu .dijitMenuItem td { - padding: 0px; + padding: 0; } .flat .dijitCheckBox { margin: 1px; @@ -1825,10 +1894,33 @@ body.ttrss_utility.share_popup .content { content: "\f00c"; color: white; } +.flat .dijitTab, +.flat .dijitAccordionTitle { + font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif; +} .flat .dijitTab i.material-icons, .flat .dijitAccordionInnerContainer:not(.dijitSelected) i.material-icons { color: #257aa7; } +.flat .dijitTree { + /*.dijitTreeRow .dijitTreeExpando { + position : relative; + top : -2px; + }*/ +} +.flat .dijitTree .dijitTreeRowFlex { + display: flex; + flex-direction: row; + align-items: center; +} +.flat .dijitTree .dijitTreeRowFlex .dijitTreeIcon { + font-size: inherit; + height: auto; +} +.flat .dijitTree .dijitTreeRowFlex .dijitTreeExpando { + height: auto; + line-height: 0; +} .flat .dijitTree .dijitFolderClosed, .flat .dijitTree .dijitFolderOpened { display: none; @@ -1842,10 +1934,6 @@ body.ttrss_utility.share_popup .content { .flat .dijitTree .dijitTreeNode .dijitTreeRow.dijitTreeRowSelected { color: white; } -.flat .dijitTree .dijitTreeRow .dijitTreeExpando { - position: relative; - top: -2px; -} .flat .dijitTree .labelParam { float: right; margin-right: 16px; @@ -1874,6 +1962,7 @@ body.ttrss_utility.share_popup .content { .flat .dijitTree .dijitTreeRow { overflow: hidden; -moz-user-select: none; + user-select: none; text-overflow: ellipsis; } .flat label.dijitButton { @@ -1948,11 +2037,11 @@ body.ttrss_zoom div.post .attachments { display: none; } body.ttrss_zoom div.post div.header { - padding-bottom: 10px; - border: 0px solid #ddd; + padding-bottom: 8px; + border: 0 solid #ddd; border-bottom-width: 1px; background: white; - font-size: 12px; + font-size: 13px; color: #555; } body.ttrss_zoom div.post div.header .row { @@ -1962,32 +2051,36 @@ body.ttrss_zoom div.post div.header .row { align-items: center; justify-content: space-between; } -body.ttrss_zoom div.post div.content { +body.ttrss_zoom div.post div.header .row h1 { + margin-top: 0; + margin-bottom: 8px; +} +body.ttrss_zoom div.post .content { font-size: 15px; line-height: 1.5; border-width: 0; padding: 0; + padding-top: 8px; } -body.ttrss_zoom div.post div.content img, -body.ttrss_zoom div.post div.content video { +body.ttrss_zoom div.post .content img, +body.ttrss_zoom div.post .content video { max-width: 760px; height: auto; } -body.ttrss_zoom div.post div.content blockquote { +body.ttrss_zoom div.post .content blockquote { margin: 5px 0px 5px 0px; color: #555; - padding-left: 10px; + padding-left: 8px; border: 0px solid #ddd; border-left-width: 4px; } -body.ttrss_zoom div.post div.content code { +body.ttrss_zoom div.post .content code { color: #009900; font-family: monospace; font-size: 12px; } -body.ttrss_zoom div.post div.content pre { - margin: 5px 0px 5px 0px; - padding: 10px; +body.ttrss_zoom div.post .content pre { + padding: 8px; color: #555; font-family: monospace; font-size: 12px; diff --git a/themes/compact_night.css b/themes/compact_night.css index c9f9b12a8..2436dd911 100644 --- a/themes/compact_night.css +++ b/themes/compact_night.css @@ -15,67 +15,77 @@ body.ttrss_main { font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 14px; overflow: hidden; + /*ul.compact { + list-style-type : none; + margin : 0; + padding : 0; + + li { + margin : 0; + padding : 0; + } + }*/ } body.ttrss_main :focus { outline: none; } -body.ttrss_main div.post { - padding: 0px; +body.ttrss_main .post { + padding: 0; font-size: 13px; } -body.ttrss_main div.post div.header { +body.ttrss_main .post .header { padding: 5px; - color: #909090; - border: 0px solid #222; + color: #ccc; + border: 0 solid #222; border-bottom-width: 1px; background: #222; } -body.ttrss_main div.post div.header .left, -body.ttrss_main div.post div.header .right { +body.ttrss_main .post .header .left, +body.ttrss_main .post .header .right { display: flex; } -body.ttrss_main div.post div.header .row { +body.ttrss_main .post .header .row { display: flex; margin-bottom: 4px; flex-wrap: nowrap; align-items: center; justify-content: space-between; } -body.ttrss_main div.post div.header .comments { +body.ttrss_main .post .header .comments { flex-grow: 2; } -body.ttrss_main div.post div.header .date { +body.ttrss_main .post .header .date { white-space: nowrap; } -body.ttrss_main div.post div.header img, -body.ttrss_main div.post div.header i.material-icons { +body.ttrss_main .post .header img, +body.ttrss_main .post .header i.material-icons { margin: 0px 4px; vertical-align: middle; - color: #777; + color: #999; } -body.ttrss_main div.post div.header .title { +body.ttrss_main .post .header .title { flex-grow: 2; font-size: 15px; font-weight: 600; text-rendering: optimizelegibility; - font-family: "Segoe WP Semibold", "Segoe UI Semibold", "Segoe UI Web Semibold", "Segoe UI", Ubuntu, "Helvetica Neue", Helvetica, Arial, sans-serif; + font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif; } -body.ttrss_main div.post div.content { +body.ttrss_main .post div.content { padding: 10px; font-size: 16px; } -body.ttrss_main div.post div.content img, -body.ttrss_main div.post div.content video { +body.ttrss_main .post div.content img, +body.ttrss_main .post div.content video { border-width: 0px; max-width: 98%; height: auto; } -body.ttrss_main div.post div.content div.embed-responsive { +body.ttrss_main .post div.content div.embed-responsive { overflow: hidden; padding-bottom: 56.25%; position: relative; } -body.ttrss_main div.post div.content div.embed-responsive iframe { +body.ttrss_main .post div.content div.embed-responsive iframe { border: 0; bottom: 0; height: 100%; @@ -172,6 +182,11 @@ body.ttrss_main .notify.notify_progress { border-color: #d7c47a; background-color: #fff7d5; } +body.ttrss_main .notify.notify_progress img { + width: 18px; + height: 18px; + filter: saturate(0); +} body.ttrss_main .notify.notify_info { border-color: #b87d2c; background-color: #faf3e9; @@ -217,6 +232,10 @@ body.ttrss_main .hl .right { display: flex; align-items: center; } +body.ttrss_main .hl .left img, +body.ttrss_main .hl .right img { + margin: 0 4px; +} body.ttrss_main .hl .left i.material-icons, body.ttrss_main .hl .right i.material-icons { margin-left: 2px; @@ -225,25 +244,28 @@ body.ttrss_main .hl .right i.material-icons { user-select: none; font-size: 21px; } +body.ttrss_main .hl .left input { + margin: 0 4px; +} +body.ttrss_main .hl .right { + text-align: right; +} body.ttrss_main .hl .right i.material-icons { - color: #777; + color: #999; } -body.ttrss_main .hl div.title { +body.ttrss_main .hl .title { cursor: pointer; flex-grow: 2; overflow: hidden; text-overflow: ellipsis; } -body.ttrss_main .hl span.author { +body.ttrss_main .hl .author { white-space: nowrap; color: #ccc; font-size: 11px; font-weight: normal; } -body.ttrss_main .hl div.right { - text-align: right; -} -body.ttrss_main .hl span.feed a { +body.ttrss_main .hl .feed a { border-radius: 4px; display: inline-block; padding: 1px 4px; @@ -252,29 +274,22 @@ body.ttrss_main .hl span.feed a { font-weight: normal; color: #ccc; } -body.ttrss_main .hl span.feed a:hover { +body.ttrss_main .hl .feed a:hover { color: #b87d2c; } -body.ttrss_main .hl span.updated { +body.ttrss_main .hl .updated { color: #ccc; text-align: right; font-size: 11px; padding-left: 10px; } -body.ttrss_main .hl span.updated div { +body.ttrss_main .hl .updated div { display: inline-block; } -body.ttrss_main .hl div.left input { - margin: 0px 4px; -} -body.ttrss_main .hl div.left img, -body.ttrss_main .hl div.right img { - margin: 0px 4px; -} body.ttrss_main .hl div.title a { font-weight: 600; text-rendering: optimizelegibility; - font-family: "Segoe UI", Ubuntu, "Helvetica Neue", Helvetica, Arial, sans-serif; + font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif; color: #555; } body.ttrss_main .hl a.title.high, @@ -287,10 +302,10 @@ body.ttrss_main .hl.vgrlf .feed { body.ttrss_main .hl.Unread { background: white; } -body.ttrss_main .hl.Unread div.title a { +body.ttrss_main .hl.Unread .title a { color: black; } -body.ttrss_main .hl.active div.title a { +body.ttrss_main .hl.active .title a { color: #b87d2c; /* text-shadow : 1px 1px 2px #fff; */ } @@ -312,9 +327,6 @@ body.ttrss_main .hl.active span, body.ttrss_main .hl.Selected span { color: white; } -body.ttrss_main .hl.Grayed { - color: #909090; -} body.ttrss_main #content-insert blockquote, body.ttrss_main #headlines-frame blockquote, body.ttrss_main .dijitContentPane blockquote { @@ -371,45 +383,6 @@ body.ttrss_main i.pub-pic { cursor: pointer; color: #ccc; } -body.ttrss_main div.errorExplained { - border: 1px solid #222; - margin: 5px 0px 5px 0px; - padding: 5px; -} -body.ttrss_main ul.browseFeedList { - height: 300px; - width: 100%; - overflow: auto; - border-width: 0px 1px 1px 1px; - border-color: #222; - border-style: solid; - margin: 0px 0px 5px 0px; - background-color: white; - list-style-type: none; - padding: 0px; -} -body.ttrss_main ul.browseFeedList li { - display: flex; - align-items: center; -} -body.ttrss_main ul.browseFeedList li > * { - margin: 2px; -} -body.ttrss_main .browseFeedList span.subscribers { - color: #808080; -} -body.ttrss_main ul.compact { - list-style-type: none; - margin: 0px; - padding: 0px; -} -body.ttrss_main ul.compact li { - margin: 0px; - padding: 0px; -} -body.ttrss_main .noborder { - border-width: 0px; -} body.ttrss_main #overlay { background: #333; left: 0; @@ -423,11 +396,6 @@ body.ttrss_main #overlay_inner { font-weight: bold; margin: 1em; } -body.ttrss_main div.loadingPrompt { - padding: 1em; - text-align: center; - font-weight: bold; -} body.ttrss_main div.whiteBox { margin-left: 1px; text-align: center; @@ -436,20 +404,13 @@ body.ttrss_main div.whiteBox { border: 0px solid #222; border-bottom-width: 1px; } -body.ttrss_main div#headlines-frame.wide .title { - overflow: visible; - white-space: normal; -} -body.ttrss_main div#headlines-frame.wide .hl .feed { - display: none; -} body.ttrss_main .dijitDialog header, body.ttrss_main .dijitDialog .dlgSec, body.ttrss_main .dijitDialog .dlgSecHoriz { font-size: 16px; font-weight: 600; color: #ccc; - font-family: "Segoe WP Semibold", "Segoe UI Semibold", "Segoe UI Web Semibold", "Segoe UI", Ubuntu, "Helvetica Neue", Helvetica, Arial, sans-serif; + font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif; } body.ttrss_main .dijitDialog section, body.ttrss_main .dijitDialog .dlgSecCont { @@ -527,11 +488,6 @@ body.ttrss_main div#cmdline { padding: 3px 5px 3px 5px; z-index: 5; } -body.ttrss_main #feed_browser_spinner { - vertical-align: middle; - height: 18px; - width: 18px; -} body.ttrss_main .exception-contents h3 { color: red; } @@ -540,14 +496,15 @@ body.ttrss_main .exception-contents textarea { height: 200px; font-size: 11px; } +body.ttrss_main #headlines-wrap-inner, body.ttrss_main #content-wrap { - padding: 0px; - border-width: 0px; - margin: 0px; + padding: 0; + border: 0; + margin: 0; } body.ttrss_main #feeds-holder { - padding: 0px; - border: 0px solid #222; + padding: 0; + border: 0 solid #222; overflow: hidden; background: #222; box-shadow: inset -1px 0px 2px -1px rgba(0, 0, 0, 0.1); @@ -557,19 +514,26 @@ body.ttrss_main #feeds-holder #feedTree { height: 100%; overflow-x: hidden; text-rendering: optimizelegibility; - font-family: "Segoe UI", Ubuntu, "Helvetica Neue", Helvetica, Arial, sans-serif; + font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif; } -body.ttrss_main #feeds-holder #feedTree .counterNode.aux, -body.ttrss_main #feeds-holder #feedTree .counterNode.marked { - background: #222; - color: #e6e6e6; - border-color: #080808; +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow { + border: 1px solid transparent; } -body.ttrss_main #feeds-holder #feedTree .counterNode.marked { - border-color: #b87d2c; - background: #ffffff; +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow.Is_Feed .loadingNode { + left: -2px; + height: 14px; + width: 14px; + position: relative; +} +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow.Is_Cat .loadingNode { + height: 11px; + width: 18px; + margin-left: 4px; + position: relative; + top: 2px; } -body.ttrss_main #feeds-holder #feedTree .counterNode { +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow .counterNode { + order: 2; font-weight: bold; display: none; font-size: 9px; @@ -578,111 +542,215 @@ body.ttrss_main #feeds-holder #feedTree .counterNode { color: white; background: #cd8b31; border-radius: 4px; - vertical-align: middle; - float: right; - position: relative; line-height: 14px; margin-right: 8px; - margin-top: 2px; min-width: 23px; height: 14px; + flex-shrink: 0; +} +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow .counterNode.aux, +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow .counterNode.marked { + background: #222; + color: #e6e6e6; + border-color: #080808; +} +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow .counterNode.marked { + border-color: #b87d2c; + background: #ffffff; +} +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow[data-feed-id="-3"][data-is-cat="false"] .counterNode.unread { + background-color: #3ea447; + border-color: #307f37; +} +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow .dijitTreeContent { + display: flex; + align-items: center; + flex-grow: 2; + min-width: 0; } -body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .loadingExpando { - left: -3px; - height: 22px; +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow .dijitTreeLabel { + cursor: pointer; + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; position: relative; - top: -3px; } -body.ttrss_main #feeds-holder #feedTree .dijitTreeRow .dijitTreeLabel.Unread { +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow .dijitTreeLabel.Unread { font-weight: bold; } -body.ttrss_main #feeds-holder #feedTree .dijitTreeRow.Error .dijitTreeLabel { +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow.Error .dijitTreeLabel { color: red; } -body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow { - border: 1px solid transparent; -} -body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRowSelected { +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow.dijitTreeRowSelected { box-shadow: -1px 0px 2px -1px rgba(0, 0, 0, 0.1); border-color: #222 transparent; background: #333; - color: #333; + color: #ccc; } -body.ttrss_main #feeds-holder #feedTree .dijitIcon.feed-icon { +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow .dijitIcon.feed-icon { margin-right: 2px; } -body.ttrss_main #feeds-holder #feedTree i.icon.icon-inbox { - color: #555; +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow i.icon.icon-inbox { + color: #ffffff; } -body.ttrss_main #feeds-holder #feedTree i.icon.icon-archive { - color: #c77b2e; +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow i.icon.icon-archive { + color: #cf7800; } -body.ttrss_main #feeds-holder #feedTree i.icon.icon-star { +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow i.icon.icon-star { position: relative; color: #ffc069; font-size: 21px; left: -2px; } -body.ttrss_main #feeds-holder #feedTree i.icon.icon-rss_feed { +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow i.icon.icon-rss_feed { color: #ff7c4b; } -body.ttrss_main #feeds-holder #feedTree i.icon.icon-whatshot { +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow i.icon.icon-whatshot { color: #69C671; } -body.ttrss_main #feeds-holder #feedTree i.icon.icon-restore { - position: relative; - top: -1px; +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow i.icon.icon-restore { font-weight: bold; color: #b87d2c; } -body.ttrss_main #headlines-wrap-inner { - padding: 0px; - margin: 0px; - border-width: 0px; +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(600px, 1fr)); + padding: 8px; + grid-gap: 8px; + background-color: #222; +} +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] > * { + /* 2 = because #headlines-spacer is the actual last child + only if odd to deal with 1) single article and 2) not break any previous rows; + 1 = spacer; + + this is outside of .cdm selector because of #headlines-spacer etc + + .grid-span-row is manually expanded RROWs + */ +} +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] > *.grid-span-row, +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] > *:nth-last-child(2):nth-child(odd), +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] > *:nth-last-child(1) { + grid-column: 1 / -1; +} +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] .cdm.expanded .header, +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] .cdm.expanded .content { + background: #333; + border: 1px solid #222; + overflow: hidden; +} +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] .cdm.expanded .content { + border-top-width: 0; + padding: 0 4px 4px 4px; +} +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] .cdm.expanded .header[data-is-stuck] { + top: -8px; + border-bottom-width: 1px; +} +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] .cdm.expanded .header { + border-bottom-width: 0; + padding: 4px; +} +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] .cdm.expanded .header .icon-grid-span { + display: inline; } -body.ttrss_main #headlines-frame[is-vfeed="0"] .header .feed { +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] .cdm.expanded .header .feed { display: none; } +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] .cdm.expanded .footer { + border: 0; + padding: 4px; +} +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] .cdm.expanded .footer .left, +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] .cdm.expanded .footer .right { + white-space: nowrap; +} +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] .cdm.expanded .footer .left { + overflow: hidden; + text-overflow: ellipsis; +} +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] .cdm.expanded .content-inner a, +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] .cdm.expanded .intermediate a { + word-break: break-all; +} body.ttrss_main #headlines-frame { - padding: 0px; - border: 0px #222; - margin-top: 0px; + padding: 0; + border: 0; + margin-top: 0; -webkit-overflow-scrolling: touch; -webkit-transform: translateZ(0); + transform: translateZ(0); -webkit-backface-visibility: hidden; + backface-visibility: hidden; +} +body.ttrss_main #headlines-frame[data-is-vfeed="false"] .header .feed, +body.ttrss_main #headlines-frame[data-is-vfeed="false"] .hl .feed, +body.ttrss_main #headlines-frame[data-is-vfeed="false"] .header .icon-feed, +body.ttrss_main #headlines-frame[data-is-vfeed="false"] .hl .icon-feed { + display: none; +} +body.ttrss_main #headlines-frame[data-auto-catchup="true"] #headlines-spacer { + height: 100vh; +} +body.ttrss_main #headlines-frame .dijitCheckBox { + margin-right: 4px; +} +body.ttrss_main #headlines-frame[data-is-wide-screen="true"] .title { + overflow: visible; + white-space: normal; } -body.ttrss_main #headlines-frame div.feed-title { +body.ttrss_main #headlines-frame[data-is-wide-screen="true"] .hl .feed { + display: none; +} +body.ttrss_main #headlines-frame #headlines-spacer { + margin-left: 1px; + text-align: center; + color: #ccc; + font-size: 11px; +} +body.ttrss_main #headlines-frame #headlines-spacer a, +body.ttrss_main #headlines-frame #headlines-spacer span { + color: #ccc; + padding: 10px; + display: block; +} +body.ttrss_main #headlines-frame #headlines-spacer a:hover { + color: #b87d2c; +} +body.ttrss_main #headlines-frame .feed-title { border: 0px solid #b87d2c; border-bottom-width: 1px; padding: 5px 8px; } -body.ttrss_main #headlines-frame div.feed-title a.title { +body.ttrss_main #headlines-frame .feed-title a.title { color: #ccc; font-weight: bold; } -body.ttrss_main #headlines-frame div.feed-title a { +body.ttrss_main #headlines-frame .feed-title a { color: #ccc; } -body.ttrss_main #headlines-frame div.feed-title a:hover { +body.ttrss_main #headlines-frame .feed-title a:hover { color: #b87d2c; } body.ttrss_main #headlines-frame span.hlMenuAttach { -webkit-touch-callout: none; -webkit-user-select: none; + user-select: none; } body.ttrss_main #toolbar-frame_splitter { display: none; } body.ttrss_main #toolbar-frame { - padding: 0px; - margin: 0px; - border-width: 0px; + padding: 0; + margin: 0; + border: 0; white-space: nowrap; - font-size: 12px; + font-size: 13px; } body.ttrss_main #toolbar-frame #toolbar { background: white; - border: 0px solid #222; + border: 0 solid #222; border-bottom-width: 1px; padding-left: 4px; height: 32px; @@ -690,13 +758,13 @@ body.ttrss_main #toolbar-frame #toolbar { flex-direction: row; flex-wrap: nowrap; color: #ccc; - font-size: 12px; + font-size: 13px; align-items: center; } body.ttrss_main #toolbar-frame #toolbar .dijitSelect, body.ttrss_main #toolbar-frame #toolbar .dijitDropDownButton .dijitButtonNode, body.ttrss_main #toolbar-frame #toolbar .dijitComboButton .dijitButtonNode { - border: 0px; + border: 0; } body.ttrss_main #toolbar-frame #toolbar i.net-alert, body.ttrss_main #toolbar-frame #toolbar .left i.icon-error { @@ -710,7 +778,7 @@ body.ttrss_main #toolbar-frame #toolbar i { margin: 0 4px; } body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines { - font-size: 12px; + font-size: 13px; background: transparent; padding-right: 4px; flex-grow: 2; @@ -725,6 +793,12 @@ body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines .left .feed_title, body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines .left .cancel_search { margin-left: 4px; } +@media (max-width: 768px) { + body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines .left .feed_title, + body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines .left i.icon-syndicate { + display: none; + } +} body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines .right { display: flex; align-items: center; @@ -739,20 +813,26 @@ body.ttrss_main #toolbar-frame #toolbar #selected_prompt { margin-right: 4px; color: #b87d2c; } -@media (max-width: 992px) { +@media (max-width: 768px) { body.ttrss_main #toolbar-frame #toolbar #selected_prompt { display: none; } } +@media (max-width: 576px) { + body.ttrss_main #toolbar-frame #toolbar .select-articles-dropdown, + body.ttrss_main #toolbar-frame #toolbar .catchup-button { + display: none; + } +} body.ttrss_main #header { - border-width: 0px; text-align: right; color: #ccc; - padding: 5px 5px 0px 0px; - margin: 0px; + padding: 5px 5px 0 0px; position: absolute; - right: 0px; - top: 0px; + border: 0; + margin: 0; + right: 0; + top: 0; z-index: 5; } body.ttrss_main #header i.net-alert, @@ -771,9 +851,8 @@ body.ttrss_main #header i { margin: 0 4px; } body.ttrss_main #content-insert { - padding: 0px; - border-color: #222; - border-width: 0px; + padding: 0; + border: 0; line-height: 1.5; overflow: auto; -webkit-overflow-scrolling: touch; @@ -786,59 +865,11 @@ body.ttrss_main img.icon { vertical-align: middle; display: inline-block; } -body.ttrss_main .player { - display: inline-block; - color: #ccc; - font-size: 11px; - font-family: sans-serif; - border: 1px solid #ccc; - padding: 0px 4px 0px 4px; - margin: 0px 2px 0px 2px; - width: 50px; - text-align: center; - background: #333; -} -body.ttrss_main .player.playing { - color: #00c000; - border-color: #00c000; -} -body.ttrss_main .player:hover { - background: #222; - cursor: pointer; -} -body.ttrss_main #headlines-frame.auto_catchup #headlines-spacer { - height: 100%; -} -body.ttrss_main #headlines-spacer { - margin-left: 1px; - text-align: center; - color: #ccc; - font-size: 11px; - font-style: italic; -} -body.ttrss_main #headlines-spacer a, -body.ttrss_main #headlines-spacer span { - color: #ccc; - padding: 10px; - display: block; -} -body.ttrss_main #headlines-spacer a:hover { - color: #b87d2c; -} body.ttrss_main ul#filterDlg_Matches, body.ttrss_main ul#filterDlg_Actions { list-style-type: none; margin: 0; padding: 0; - /*max-height : 100px; - overflow : auto; - border-style : solid; - border-color : @border-default; - border-width : 1px 1px 1px 1px; - background-color : @default-bg; - margin : 0px 0px 5px 0px; - padding : 4px; - min-height : 16px;*/ } body.ttrss_main ul#filterDlg_Matches li, body.ttrss_main ul#filterDlg_Actions li { @@ -874,9 +905,6 @@ body.ttrss_main span.highlight { background-color: #ffff00; color: #cc90cc; } -body.ttrss_main #headlines-frame .dijitCheckBox { - margin-right: 4px; -} body.ttrss_main #feedEditDlg img.feedIcon { border: 1px solid #ccc; padding: 5px; @@ -934,6 +962,11 @@ body.ttrss_main:not([view-mode="marked"])[hide-read-feeds="true"][hide-read-show body.ttrss_main:not([view-mode="marked"])[hide-read-feeds="true"][hide-read-shows-special="false"] #feeds-holder #feedTree .dijitTreeRow:not(.Unread):not(.AlwaysVisible) { display: none; } +body.ttrss_main { + /*.score-neutral i.icon-score { + opacity : 0.5; + }*/ +} body.ttrss_main #toolbar-headlines i.icon-syndicate { color: #ff7c4b; margin-right: 8px; @@ -970,10 +1003,15 @@ body.ttrss_main .score-high i.icon-score { body.ttrss_main .score-low i.icon-score { color: #500; } -body.ttrss_main .score-neutral i.icon-score { - opacity: 0.5; +body.ttrss_main i.icon-score, +body.ttrss_main i.icon-grid-span { + cursor: pointer; + color: #999; } -body.ttrss_main i.icon-score { +body.ttrss_main .icon-grid-span { + display: none; +} +body.ttrss_main .icon-feed { cursor: pointer; } body.ttrss_main .panel { @@ -1004,20 +1042,62 @@ body.ttrss_main ul.list-unstyled { body.ttrss_main .text-center { text-align: center; } -body.ttrss_main #prefFilterTestResultList .preview { +body.ttrss_main .text-right { + text-align: right; +} +body.ttrss_main .text-left { + text-align: left; +} +body.ttrss_main .dijitDialog .filter-results-list .preview { margin: 8px; } -body.ttrss_main #prefFilterTestResultList .title { +body.ttrss_main .dijitDialog .filter-results-list .title { font-weight: bold; } -body.ttrss_main #prefFilterTestResultList .feed { - color: #b87d2c; +body.ttrss_main .dijitDialog #feed_add_spinner { + position: relative; + top: 5px; + width: 18px; + height: 18px; +} +body.ttrss_main .icon-three-dots { + width: 18px; + height: 18px; + vertical-align: middle; +} +body.ttrss_main, +body.ttrss_utility { + /*div.autocomplete { + position : absolute; + width : 250px; + background-color : @default-bg; + border :1px solid @border-default; + margin : 0px; + padding : 0px; + + ul { + list-style-type : none; + margin : 0px; + padding : 0px; + } + + ul li.selected { + background-color : darken(@default-bg, 10%); + } + + ul li { + list-style-type : none; + display : block; + margin : 0; + padding : 2px; + cursor : pointer; + } + }*/ } body.ttrss_main .alert, body.ttrss_utility .alert { padding: 8px 35px 8px 14px; margin-bottom: 10px; - /* text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); */ background-color: #fcf8e3; border: 1px solid #fbeed5; border-radius: 4px; @@ -1058,6 +1138,16 @@ body.ttrss_main .text-warning, body.ttrss_utility .text-warning { color: #a47e3c; } +body.ttrss_main .text-muted, +body.ttrss_utility .text-muted { + color: #ccc; +} +body.ttrss_main .text-small, +body.ttrss_utility .text-small, +body.ttrss_main .small, +body.ttrss_utility .small { + font-size: 11px; +} body.ttrss_main .alert, body.ttrss_utility .alert, body.ttrss_main .alert h4, @@ -1107,41 +1197,6 @@ body.ttrss_utility hr { border: 0px solid #ccc; border-bottom-width: 1px; } -body.ttrss_main .text-muted, -body.ttrss_utility .text-muted { - color: #ccc; -} -body.ttrss_main .small, -body.ttrss_utility .small { - font-size: 11px; -} -body.ttrss_main div.autocomplete, -body.ttrss_utility div.autocomplete { - position: absolute; - width: 250px; - background-color: #333; - border: 1px solid #222; - margin: 0px; - padding: 0px; -} -body.ttrss_main div.autocomplete ul, -body.ttrss_utility div.autocomplete ul { - list-style-type: none; - margin: 0px; - padding: 0px; -} -body.ttrss_main div.autocomplete ul li.selected, -body.ttrss_utility div.autocomplete ul li.selected { - background-color: #1a1a1a; -} -body.ttrss_main div.autocomplete ul li, -body.ttrss_utility div.autocomplete ul li { - list-style-type: none; - display: block; - margin: 0; - padding: 2px; - cursor: pointer; -} ::selection { background: #b87d2c; color: #333; @@ -1155,16 +1210,13 @@ body.ttrss_utility div.autocomplete ul li { ::-webkit-scrollbar-track { background-color: #eee; } -video::-webkit-media-controls-overlay-play-button { - display: none; -} -.cdm i.material-icons { - color: #777; -} -.cdm .header { - position: sticky; - top: 0; - z-index: 3; +/*video::-webkit-media-controls-overlay-play-button { + display: none; +}*/ +.cdm { + /*i.material-icons { + color : @color-icon; + }*/ } .cdm .header, .cdm .footer { @@ -1175,13 +1227,16 @@ video::-webkit-media-controls-overlay-play-button { .cdm .header img, .cdm .footer img, .cdm .footer i.material-icons { - margin: 0px 4px; + margin: 0 4px; vertical-align: middle; } .cdm .header-sticky-guard { height: 0; } .cdm .header { + position: sticky; + top: 0; + z-index: 3; align-items: center; } .cdm .header > * { @@ -1204,7 +1259,7 @@ video::-webkit-media-controls-overlay-play-button { .cdm .header .titleWrap { flex-grow: 2; } -.cdm .header span.updated { +.cdm .header .updated { color: #ccc; font-weight: normal; font-size: 11px; @@ -1213,6 +1268,30 @@ video::-webkit-media-controls-overlay-play-button { .cdm .header input { margin: 0px 4px; } +.cdm .header .feed { + float: right; + font-weight: normal; + font-style: italic; +} +.cdm .header .feed a { + border-radius: 4px; + display: inline-block; + padding: 1px 4px 1px 4px; +} +.cdm .header .feed, +.cdm .header .feed a { + vertical-align: middle; + color: #ccc; + font-weight: normal; + font-style: italic; + font-size: 11px; +} +.cdm .header .author { + white-space: nowrap; + color: #ccc; + font-size: 11px; + font-weight: normal; +} .cdm .footer { height: 30px; padding-left: 5px; @@ -1221,30 +1300,37 @@ video::-webkit-media-controls-overlay-play-button { clear: both; align-items: center; } +.cdm .footer i.material-icons { + color: #999; +} .cdm .footer .left { flex-grow: 2; } .cdm .intermediate { margin-top: 10px; margin-left: 10px; + line-height: 1.5; } .cdm .content-inner { margin: 10px; line-height: 1.5; font-size: 16px; } +.cdm .intermediate iframe, +.cdm .content-inner iframe { + max-width: 98%; + width: auto; + height: auto; +} .cdm .intermediate img, .cdm .intermediate video, .cdm .content-inner img, .cdm .content-inner video { border-width: 0px; max-width: 98%; + width: auto; height: auto; } -.cdm.expanded { - /*margin-top : 4px; - margin-bottom : 4px;*/ -} .cdm.expanded .collapse, .cdm.expanded .excerpt { display: none; @@ -1257,72 +1343,54 @@ video::-webkit-media-controls-overlay-play-button { border-bottom-width: 1px; } .cdm.expanded > hr { - margin-top: 0px; - margin-bottom: 0px; + margin-top: 0; + margin-bottom: 0; } -div.cdm.expanded div.header a.title { +.cdm.expanded .header a.title { font-size: 16px; color: #999; font-weight: 600; transition: color 0.2s, background 0.2s; text-rendering: optimizelegibility; - font-family: "Segoe WP Semibold", "Segoe UI Semibold", "Segoe UI Web Semibold", "Segoe UI", Ubuntu, "Helvetica Neue", Helvetica, Arial, sans-serif; + font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif; } -div.cdm.expanded.active { - background: white; +.cdm.expanded.active .content, +.cdm.expanded.Unread .content { + color: black; } -div.cdm.expanded.active div.header a.title { +.cdm.expanded.active .header .title { color: #b87d2c; } -div.cdm.expanded.Unread div.header a.title { +.cdm.expanded.Unread .header .title { color: black; } -div.cdm.expanded div.content { +.cdm.expanded .content { color: #ccc; } -div.cdm.expanded.Unread div.content { - color: black; -} -div.cdm.active div.content { - color: black; -} -div.cdm.vgrlf .feed { +.cdm.vgrlf .feed { display: none; } -.cdm div.feed-title { +.cdm .feed-title { border: 0px solid #b87d2c; border-bottom-width: 1px; padding: 5px 3px 5px 5px; } -.cdm div.feed-title a.title { +.cdm .feed-title a.title { color: #ccc; font-weight: bold; } -.cdm div.feed-title a { +.cdm .feed-title a { color: #ccc; } -.cdm div.feed-title a:hover { +.cdm .feed-title a:hover { color: #b87d2c; } -.cdm div.header span.feed { - float: right; - font-weight: normal; - font-style: italic; -} -.cdm div.header div.feed, -.cdm div.header div.feed a { - vertical-align: middle; - color: #ccc; - font-weight: normal; - font-style: italic; - font-size: 11px; -} -.cdm div.content-inner div.embed-responsive { +.cdm .content-inner .embed-responsive { overflow: hidden; padding-bottom: 56.25%; position: relative; } -.cdm div.content-inner div.embed-responsive iframe { +.cdm .content-inner .embed-responsive iframe { border: 0; bottom: 0; height: 100%; @@ -1331,17 +1399,6 @@ div.cdm.vgrlf .feed { top: 0; width: 100%; } -.cdm div.header span.author { - white-space: nowrap; - color: #ccc; - font-size: 11px; - font-weight: normal; -} -.cdm .feed a { - border-radius: 4px; - display: inline-block; - padding: 1px 4px 1px 4px; -} .cdm.expandable { background-color: #222; border: 0px solid #222; @@ -1379,43 +1436,43 @@ div.cdm.vgrlf .feed { .cdm.expandable.active { background: white ! important; } -div.cdm.expandable.active div.header span.titleWrap { +.cdm.expandable.active div.header span.titleWrap { white-space: normal; } -div.cdm.expandable div.header a.title { +.cdm.expandable .header a.title { font-weight: 600; color: #ccc; font-size: 14px; transition: color 0.2s, background 0.2s; text-rendering: optimizelegibility; - font-family: "Segoe WP Semibold", "Segoe UI Semibold", "Segoe UI Web Semibold", "Segoe UI", Ubuntu, "Helvetica Neue", Helvetica, Arial, sans-serif; + font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif; } -div.cdm.expandable.Unread div.header a.title { +.cdm.expandable.Unread div.header a.title { color: black; } -div.cdm.expandable.active .collapse i.material-icons { +.cdm.expandable.active .collapse i.material-icons { color: #b87d2c; cursor: pointer; } -div.cdm.expandable.active .excerpt { +.cdm.expandable.active .excerpt { display: none; } -div.cdm.expandable.active div.header a.title { +.cdm.expandable.active div.header a.title { color: #b87d2c; font-size: 16px; font-weight: 600; text-rendering: optimizelegibility; - font-family: "Segoe WP Semibold", "Segoe UI Semibold", "Segoe UI Web Semibold", "Segoe UI", Ubuntu, "Helvetica Neue", Helvetica, Arial, sans-serif; + font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif; } -div.cdm.expandable:not(.active) { +.cdm.expandable:not(.active) { cursor: pointer; } -div.cdm.expandable:not(.active) .content, -div.cdm.expandable:not(.active) .collapse { +.cdm.expandable:not(.active) .content, +.cdm.expandable:not(.active) .collapse { display: none; } -div.cdm.expandable.active .header[stuck], -div.cdm.expanded .header[stuck] { +.cdm.expandable.active .header[data-is-stuck], +.cdm.expanded .header[data-is-stuck] { box-shadow: 0 1px 1px -1px rgba(0, 0, 0, 0.1); border: 0 solid #222; border-bottom-width: 1px; @@ -1432,7 +1489,7 @@ body.ttrss_prefs h1, body.ttrss_prefs h2, body.ttrss_prefs h3, body.ttrss_prefs h4 { - font-family: "Segoe WP Semibold", "Segoe UI Semibold", "Segoe UI Web Semibold", "Segoe UI", Ubuntu, "Helvetica Neue", Helvetica, Arial, sans-serif; + font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif; font-weight: 600; color: #ccc; } @@ -1503,14 +1560,6 @@ body.ttrss_prefs fieldset.prefs .help-text { body.ttrss_prefs fieldset.prefs .help-text-bottom { margin-top: 10px; } -body.ttrss_prefs fieldset.plugin label.description { - width: 550px; - margin-right: 150px; - display: inline-block; -} -body.ttrss_prefs fieldset.plugin label.description .dijitCheckBox { - margin-right: 10px; -} body.ttrss_prefs table th { text-align: left; } @@ -1538,17 +1587,26 @@ body.ttrss_prefs ul.prefs-plugin-list li > * { body.ttrss_prefs ul.prefs-plugin-list li label.checkbox { display: flex; align-items: center; - min-width: 300px; cursor: pointer; } body.ttrss_prefs ul.prefs-plugin-list li label.checkbox.system { cursor: auto; } body.ttrss_prefs ul.prefs-plugin-list li label.checkbox .name { + min-width: 300px; flex-grow: 2; display: inline-block; text-align: right; font-weight: bold; + margin-right: 20px; +} +@media (max-width: 992px) { + body.ttrss_prefs ul.prefs-plugin-list li label.checkbox .name { + min-width: 200px ! important; + } + body.ttrss_prefs ul.prefs-plugin-list li .version { + display: none; + } } body.ttrss_prefs ul.prefs-plugin-list li .actions { flex-grow: 2; @@ -1602,6 +1660,11 @@ body.ttrss_prefs .event-log tr .errno { body.ttrss_prefs .event-log tr .errstr { word-break: break-all; white-space: pre-wrap; + width: 30%; +} +body.ttrss_prefs .event-log tr .filename { + word-break: break-all; + width: 20%; } body.ttrss_prefs .event-log tr .filename, body.ttrss_prefs .event-log tr .login, @@ -1660,12 +1723,16 @@ body.ttrss_utility fieldset > label.checkbox { display: inline; font-weight: normal; } +.flat { + /*#feedTree { + .dijitTreeContent .dijitInline { + vertical-align : baseline; + } + }*/ +} .flat li { padding: 2px; } -.flat #feedTree .dijitTreeContent .dijitInline { - vertical-align: baseline; -} .flat .dijitButton i.material-icons { position: relative; top: -1px; @@ -1689,7 +1756,8 @@ body.ttrss_utility fieldset > label.checkbox { } .flat .dijitToolbar { font-size: 13px; - padding: 0px; + font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif; + padding: 0; } .flat .dijitToolbar .dijitTextBox .dijitInputContainer .dijitInputInner { line-height: 10px; @@ -1712,12 +1780,13 @@ body.ttrss_utility fieldset > label.checkbox { .flat .dijitMenu .dijitMenuItem .dijitMenuItemLabel { padding: 4px 8px; font-size: 13px; + font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif; } .flat .dijitMenu .dijitMenuItem.dijitDisabled:not(.dijitMenuItemSelected) .dijitMenuItemLabel { color: #d29745; } .flat .dijitMenu .dijitMenuItem td { - padding: 0px; + padding: 0; } .flat .dijitCheckBox { margin: 1px; @@ -1727,10 +1796,33 @@ body.ttrss_utility fieldset > label.checkbox { content: "\f00c"; color: white; } +.flat .dijitTab, +.flat .dijitAccordionTitle { + font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif; +} .flat .dijitTab i.material-icons, .flat .dijitAccordionInnerContainer:not(.dijitSelected) i.material-icons { color: #b87d2c; } +.flat .dijitTree { + /*.dijitTreeRow .dijitTreeExpando { + position : relative; + top : -2px; + }*/ +} +.flat .dijitTree .dijitTreeRowFlex { + display: flex; + flex-direction: row; + align-items: center; +} +.flat .dijitTree .dijitTreeRowFlex .dijitTreeIcon { + font-size: inherit; + height: auto; +} +.flat .dijitTree .dijitTreeRowFlex .dijitTreeExpando { + height: auto; + line-height: 0; +} .flat .dijitTree .dijitFolderClosed, .flat .dijitTree .dijitFolderOpened { display: none; @@ -1744,10 +1836,6 @@ body.ttrss_utility fieldset > label.checkbox { .flat .dijitTree .dijitTreeNode .dijitTreeRow.dijitTreeRowSelected { color: white; } -.flat .dijitTree .dijitTreeRow .dijitTreeExpando { - position: relative; - top: -2px; -} .flat .dijitTree .labelParam { float: right; margin-right: 16px; @@ -1776,6 +1864,7 @@ body.ttrss_utility fieldset > label.checkbox { .flat .dijitTree .dijitTreeRow { overflow: hidden; -moz-user-select: none; + user-select: none; text-overflow: ellipsis; } .flat label.dijitButton { @@ -1851,7 +1940,7 @@ body.ttrss_utility .content h2, body.ttrss_utility .content h3, body.ttrss_utility .content h4 { color: #b87d2c; - font-family: "Segoe WP Semibold", "Segoe UI Semibold", "Segoe UI Web Semibold", "Segoe UI", Ubuntu, "Helvetica Neue", Helvetica, Arial, sans-serif; + font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif; } body.ttrss_utility .content h2 { font-size: 18px; @@ -1870,7 +1959,7 @@ body.ttrss_utility a:focus { } body.ttrss_utility h1 { color: gray; - font-family: "Segoe WP Semibold", "Segoe UI Semibold", "Segoe UI Web Semibold", "Segoe UI", Ubuntu, "Helvetica Neue", Helvetica, Arial, sans-serif; + font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 18px; margin: 10px 0 0 0; } @@ -1939,11 +2028,11 @@ body.ttrss_zoom div.post .attachments { display: none; } body.ttrss_zoom div.post div.header { - padding-bottom: 10px; - border: 0px solid #222; + padding-bottom: 8px; + border: 0 solid #222; border-bottom-width: 1px; background: #333; - font-size: 12px; + font-size: 13px; color: #ccc; } body.ttrss_zoom div.post div.header .row { @@ -1953,32 +2042,36 @@ body.ttrss_zoom div.post div.header .row { align-items: center; justify-content: space-between; } -body.ttrss_zoom div.post div.content { +body.ttrss_zoom div.post div.header .row h1 { + margin-top: 0; + margin-bottom: 8px; +} +body.ttrss_zoom div.post .content { font-size: 15px; line-height: 1.5; border-width: 0; padding: 0; + padding-top: 8px; } -body.ttrss_zoom div.post div.content img, -body.ttrss_zoom div.post div.content video { +body.ttrss_zoom div.post .content img, +body.ttrss_zoom div.post .content video { max-width: 760px; height: auto; } -body.ttrss_zoom div.post div.content blockquote { +body.ttrss_zoom div.post .content blockquote { margin: 5px 0px 5px 0px; color: #ccc; - padding-left: 10px; + padding-left: 8px; border: 0px solid #222; border-left-width: 4px; } -body.ttrss_zoom div.post div.content code { +body.ttrss_zoom div.post .content code { color: #009900; font-family: monospace; font-size: 12px; } -body.ttrss_zoom div.post div.content pre { - margin: 5px 0px 5px 0px; - padding: 10px; +body.ttrss_zoom div.post .content pre { + padding: 8px; color: #ccc; font-family: monospace; font-size: 12px; @@ -2022,26 +2115,34 @@ body.flat.ttrss_main #feeds-holder { background: #222; box-shadow: inset -1px 0px 2px -1px #666; } -body.flat.ttrss_main #feeds-holder #feedTree .counterNode.aux, -body.flat.ttrss_main #feeds-holder #feedTree .counterNode.marked { +body.flat.ttrss_main #feeds-holder #feedTree .dijitTreeRow .counterNode.aux, +body.flat.ttrss_main #feeds-holder #feedTree .dijitTreeRow .counterNode.marked { background: #222; color: #ccc; border-color: #333; } -body.flat.ttrss_main #feeds-holder #feedTree .counterNode.marked { +body.flat.ttrss_main #feeds-holder #feedTree .dijitTreeRow .counterNode.marked { border-color: #b87d2c; } -body.flat.ttrss_main #feeds-holder #feedTree .dijitTreeRowSelected { +body.flat.ttrss_main #feeds-holder #feedTree .dijitTreeRow .dijitTreeRowSelected { background: #333; border-color: #333 transparent; color: #e6e6e6; } -body.flat.ttrss_main #feeds-holder #feedTree .dijitTreeRowSelected .dijitTreeLabel { +body.flat.ttrss_main #feeds-holder #feedTree .dijitTreeRow .dijitTreeRowSelected .dijitTreeLabel { text-shadow: none; } -body.flat.ttrss_main #feeds-holder #feedTree i.icon.icon-inbox { +body.flat.ttrss_main #feeds-holder #feedTree .dijitTreeRow i.icon.icon-inbox { color: #999999; } +body.flat.ttrss_main #feeds-holder #feedTree .dijitTreeRow .loadingNode { + filter: invert(1); +} +body.flat.ttrss_main #headlines-frame { + /*.cdm.expanded { + background: @default-bg; + }*/ +} body.flat.ttrss_main #headlines-frame .hl:not(.active):not(.Selected):not(.Unread), body.flat.ttrss_main #headlines-frame .cdm.expandable:not(.active):not(.Selected):not(.Unread) { background: #333; @@ -2050,9 +2151,6 @@ body.flat.ttrss_main #headlines-frame .hl.Unread:not(.active):not(.Selected), body.flat.ttrss_main #headlines-frame .cdm.expandable.Unread:not(.active):not(.Selected) { background: #222; } -body.flat.ttrss_main #headlines-frame .cdm.expanded { - background: #333; -} body.flat.ttrss_main #headlines-frame .hl.Unread .title, body.flat.ttrss_main #headlines-frame .cdm.Unread .title { color: #e6e6e6; @@ -2225,6 +2323,9 @@ body.flat.ttrss_main #filterNewRuleDlg .dijitValidationTextAreaError { body.flat.ttrss_main #filterNewRuleDlg .dijitValidationTextArea:not(.dijitValidationTextAreaError) { background: #305030; } +body.flat.ttrss_main .dijitDialog #feed_add_spinner { + filter: invert(1); +} /* rules specific to compact.css */ body.ttrss_main.ttrss_index.flat #feedTree.dijitTree .dijitTreeLabel { font-size: 13px ! important; diff --git a/themes/light-high-contrast.css b/themes/light-high-contrast.css new file mode 100644 index 000000000..16ac51035 --- /dev/null +++ b/themes/light-high-contrast.css @@ -0,0 +1,2093 @@ +@import "../lib/flat-ttrss/flat_combined.css"; +body.ttrss_main, +body.ttrss_prefs, +#main { + position: absolute; + width: 100%; + height: 100%; + border: 0; + padding: 0; + margin: 0; +} +body.ttrss_main { + background: white; + color: black; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 14px; + overflow: hidden; + /*ul.compact { + list-style-type : none; + margin : 0; + padding : 0; + + li { + margin : 0; + padding : 0; + } + }*/ +} +body.ttrss_main :focus { + outline: none; +} +body.ttrss_main .post { + padding: 0; + font-size: 13px; +} +body.ttrss_main .post .header { + padding: 5px; + color: black; + border: 0 solid #ddd; + border-bottom-width: 1px; + background: #f5f5f5; +} +body.ttrss_main .post .header .left, +body.ttrss_main .post .header .right { + display: flex; +} +body.ttrss_main .post .header .row { + display: flex; + margin-bottom: 4px; + flex-wrap: nowrap; + align-items: center; + justify-content: space-between; +} +body.ttrss_main .post .header .comments { + flex-grow: 2; +} +body.ttrss_main .post .header .date { + white-space: nowrap; +} +body.ttrss_main .post .header img, +body.ttrss_main .post .header i.material-icons { + margin: 0px 4px; + vertical-align: middle; + color: #777; +} +body.ttrss_main .post .header .title { + flex-grow: 2; + font-size: 15px; + font-weight: 600; + text-rendering: optimizelegibility; + font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif; +} +body.ttrss_main .post div.content { + padding: 10px; + font-size: 16px; +} +body.ttrss_main .post div.content img, +body.ttrss_main .post div.content video { + border-width: 0px; + max-width: 98%; + height: auto; +} +body.ttrss_main .post div.content div.embed-responsive { + overflow: hidden; + padding-bottom: 56.25%; + position: relative; +} +body.ttrss_main .post div.content div.embed-responsive iframe { + border: 0; + bottom: 0; + height: 100%; + left: 0; + position: absolute; + top: 0; + width: 100%; +} +body.ttrss_main .inline-player { + display: flex; + align-items: center; +} +body.ttrss_main .inline-player audio { + margin-right: 8px; +} +body.ttrss_main .article-note { + background-color: #fff7d5; + margin: 5px; + border: 1px solid #e7d796; + color: #9a8c59; + display: flex; + align-items: center; +} +body.ttrss_main .article-note > * { + padding: 5px; +} +body.ttrss_main .article-note.editable { + cursor: pointer; +} +body.ttrss_main h1 { + font-size: 18px; + font-weight: 600; + text-rendering: optimizelegibility; +} +body.ttrss_main h2 { + font-size: 16px; + font-weight: 600; + text-rendering: optimizelegibility; +} +body.ttrss_main h3 { + font-size: 16px; + font-weight: 600; + text-rendering: optimizelegibility; +} +body.ttrss_main h4 { + font-size: 14px; + font-weight: 600; + text-rendering: optimizelegibility; +} +body.ttrss_main a { + color: #257aa7; + text-decoration: none; +} +body.ttrss_main a:hover { + color: #133d54; + text-decoration: underline; +} +body.ttrss_main #notify.visible { + opacity: 100; +} +body.ttrss_main #notify { + bottom: 20px; + right: 20px; + min-width: 200px; + max-width: 350px; + border-width: 1px; + border-style: solid; + position: fixed; + font-size: 14px; + z-index: 99; + display: flex; + opacity: 0; + align-items: center; + padding: 10px; + transition: opacity 0.2s linear; + box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.1); +} +body.ttrss_main #notify img { + vertical-align: middle; +} +body.ttrss_main #notify .msg { + flex-grow: 2; + padding: 0 10px; + line-height: 20px; +} +body.ttrss_main #notify .icon-close { + cursor: pointer; +} +body.ttrss_main .notify { + border-color: #d7c47a; + background-color: #fff7d5; +} +body.ttrss_main .notify.notify_progress { + border-color: #d7c47a; + background-color: #fff7d5; +} +body.ttrss_main .notify.notify_progress img { + width: 18px; + height: 18px; + filter: saturate(0); +} +body.ttrss_main .notify.notify_info { + border-color: #257aa7; + background-color: #d5ebf6; +} +body.ttrss_main .notify.notify_info i.icon-notify { + color: #257aa7; +} +body.ttrss_main .notify.notify_error { + background-color: #c00; + border-color: #900; + color: white; +} +body.ttrss_main .notify.notify_error i.icon-notify, +body.ttrss_main .notify.notify_error i.icon-close { + color: white; +} +body.ttrss_main .action-chooser .action-button .dijitButtonText { + vertical-align: unset; +} +body.ttrss_main .action-chooser .action-button .dijitArrowButtonInner { + display: none; +} +body.ttrss_main .hl { + border: 0px solid #ddd; + border-bottom-width: 1px; + transition: color 0.2s, background 0.2s; + display: flex; + flex-direction: row; + flex-wrap: nowrap; + background: #f5f5f5; + align-items: center; + user-select: none; +} +body.ttrss_main .hl > * { + white-space: nowrap; + padding: 4px; +} +body.ttrss_main .hl img { + vertical-align: middle; +} +body.ttrss_main .hl .left, +body.ttrss_main .hl .right { + display: flex; + align-items: center; +} +body.ttrss_main .hl .left img, +body.ttrss_main .hl .right img { + margin: 0 4px; +} +body.ttrss_main .hl .left i.material-icons, +body.ttrss_main .hl .right i.material-icons { + margin-left: 2px; + padding: 2px; + transition: color 0.2s linear; + user-select: none; + font-size: 21px; +} +body.ttrss_main .hl .left input { + margin: 0 4px; +} +body.ttrss_main .hl .right { + text-align: right; +} +body.ttrss_main .hl .right i.material-icons { + color: #777; +} +body.ttrss_main .hl .title { + cursor: pointer; + flex-grow: 2; + overflow: hidden; + text-overflow: ellipsis; +} +body.ttrss_main .hl .author { + white-space: nowrap; + color: black; + font-size: 11px; + font-weight: normal; +} +body.ttrss_main .hl .feed a { + border-radius: 4px; + display: inline-block; + padding: 1px 4px; + font-size: 11px; + font-style: italic; + font-weight: normal; + color: black; +} +body.ttrss_main .hl .feed a:hover { + color: #257aa7; +} +body.ttrss_main .hl .updated { + color: black; + text-align: right; + font-size: 11px; + padding-left: 10px; +} +body.ttrss_main .hl .updated div { + display: inline-block; +} +body.ttrss_main .hl div.title a { + font-weight: 600; + text-rendering: optimizelegibility; + font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif; + color: #555; +} +body.ttrss_main .hl a.title.high, +body.ttrss_main .hl span.hl-content.high .preview { + color: #00aa00; +} +body.ttrss_main .hl.vgrlf .feed { + display: none; +} +body.ttrss_main .hl.Unread { + background: white; +} +body.ttrss_main .hl.Unread .title a { + color: black; +} +body.ttrss_main .hl.active .title a { + color: #257aa7; + /* text-shadow : 1px 1px 2px #fff; */ +} +body.ttrss_main .hl.active { + background: #257aa7 ! important; +} +body.ttrss_main .hl.active, +body.ttrss_main .hl.Selected { + color: white; + background: #3f728e; +} +body.ttrss_main .hl.active a, +body.ttrss_main .hl.Selected a, +body.ttrss_main .hl.active .feed a, +body.ttrss_main .hl.Selected .feed a, +body.ttrss_main .hl.active .hl-content a.title, +body.ttrss_main .hl.Selected .hl-content a.title, +body.ttrss_main .hl.active span, +body.ttrss_main .hl.Selected span { + color: white; +} +body.ttrss_main #content-insert blockquote, +body.ttrss_main #headlines-frame blockquote, +body.ttrss_main .dijitContentPane blockquote { + margin: 5px 0px 5px 0px; + color: black; + padding-left: 10px; + border: 0px solid #ccc; + border-left-width: 4px; +} +body.ttrss_main #content-insert code, +body.ttrss_main #headlines-frame code, +body.ttrss_main .dijitContentPane code { + color: #009900; + font-family: monospace; +} +body.ttrss_main #content-insert pre, +body.ttrss_main #headlines-frame pre, +body.ttrss_main .dijitContentPane pre { + margin: 5px 0px 5px 0px; + padding: 10px; + color: black; + font-family: monospace; + font-size: 12px; + border: 0px solid #ccc; + background: #f5f5f5; + display: block; + max-width: 98%; + overflow: auto; + white-space: pre-wrap; +} +body.ttrss_main div.prefHelp { + color: black; + padding: 5px; +} +body.ttrss_main span.preview { + color: #999; + font-weight: normal; + font-size: 12px; + padding-left: 4px; +} +body.ttrss_main .label { + display: inline-block; + vertical-align: middle; + background-color: #fff7d5; + font-size: 9px; + color: black; + font-weight: normal; + margin-left: 2px; + padding: 2px 4px; + white-space: nowrap; +} +body.ttrss_main i.marked-pic, +body.ttrss_main i.pub-pic { + cursor: pointer; + color: #ccc; +} +body.ttrss_main #overlay { + background: white; + left: 0; + top: 0; + height: 100%; + width: 100%; + z-index: 100; + position: absolute; +} +body.ttrss_main #overlay_inner { + font-weight: bold; + margin: 1em; +} +body.ttrss_main div.whiteBox { + margin-left: 1px; + text-align: center; + padding: 1em 1em 0px 1em; + font-size: 11px; + border: 0px solid #ddd; + border-bottom-width: 1px; +} +body.ttrss_main .dijitDialog header, +body.ttrss_main .dijitDialog .dlgSec, +body.ttrss_main .dijitDialog .dlgSecHoriz { + font-size: 16px; + font-weight: 600; + color: black; + font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif; +} +body.ttrss_main .dijitDialog section, +body.ttrss_main .dijitDialog .dlgSecCont { + margin: 10px 20px; +} +body.ttrss_main .dijitDialog header.horizontal + section, +body.ttrss_main .dijitDialog section.horizontal, +body.ttrss_main .dijitDialog .dlgSecHoriz + .dlgSecCont { + margin: 10px 0; +} +body.ttrss_main .dijitDialog section.narrow { + margin: 0; +} +body.ttrss_main .dijitDialog section fieldset > label, +body.ttrss_main .dijitDialog div.dlgSecCont fieldset > label, +body.ttrss_main .dijitDialog div.dlgSecSimple fieldset > label { + font-weight: bold; + margin-right: 10px; + display: inline-block; + min-width: 140px; + text-align: right; +} +body.ttrss_main .dijitDialog section fieldset > label.checkbox, +body.ttrss_main .dijitDialog div.dlgSecCont fieldset > label.checkbox, +body.ttrss_main .dijitDialog div.dlgSecSimple fieldset > label.checkbox { + font-weight: normal; + display: inline; +} +body.ttrss_main .dijitDialog section fieldset > label.inline, +body.ttrss_main .dijitDialog div.dlgSecCont fieldset > label.inline, +body.ttrss_main .dijitDialog div.dlgSecSimple fieldset > label.inline { + display: inline; +} +body.ttrss_main .dijitDialog section fieldset, +body.ttrss_main .dijitDialog div.dlgSecCont fieldset, +body.ttrss_main .dijitDialog div.dlgSecSimple fieldset { + border-width: 0px; + padding: 5px 0px; +} +body.ttrss_main .dijitDialog section fieldset.narrow, +body.ttrss_main .dijitDialog div.dlgSecCont fieldset.narrow, +body.ttrss_main .dijitDialog div.dlgSecSimple fieldset.narrow { + padding: 2px 0px; +} +body.ttrss_main .dijitDialog section fieldset.align-right, +body.ttrss_main .dijitDialog div.dlgSecCont fieldset.align-right, +body.ttrss_main .dijitDialog div.dlgSecSimple fieldset.align-right { + text-align: right; +} +body.ttrss_main .dijitDialog footer, +body.ttrss_main .dijitDialog .dlgButtons { + margin-top: 5px; + text-align: right; +} +body.ttrss_main .dijitDialog footer.text-center { + text-align: center; +} +body.ttrss_main .dijitDialog textarea#tags_str { + height: 100px; + font-size: 12px; + width: 98%; +} +body.ttrss_main i.icon-label { + color: #fff7d5; +} +body.ttrss_main div#cmdline { + position: absolute; + left: 5px; + bottom: 5px; + font-size: 11px; + color: black; + font-weight: bold; + background-color: white; + border: 1px solid #257aa7; + padding: 3px 5px 3px 5px; + z-index: 5; +} +body.ttrss_main .exception-contents h3 { + color: red; +} +body.ttrss_main .exception-contents textarea { + width: 99%; + height: 200px; + font-size: 11px; +} +body.ttrss_main #headlines-wrap-inner, +body.ttrss_main #content-wrap { + padding: 0; + border: 0; + margin: 0; +} +body.ttrss_main #feeds-holder { + padding: 0; + border: 0 solid #ddd; + overflow: hidden; + background: #f5f5f5; + box-shadow: inset -1px 0px 2px -1px rgba(0, 0, 0, 0.1); + -webkit-overflow-scrolling: touch; +} +body.ttrss_main #feeds-holder #feedTree { + height: 100%; + overflow-x: hidden; + text-rendering: optimizelegibility; + font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif; +} +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow { + border: 1px solid transparent; +} +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow.Is_Feed .loadingNode { + left: -2px; + height: 14px; + width: 14px; + position: relative; +} +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow.Is_Cat .loadingNode { + height: 11px; + width: 18px; + margin-left: 4px; + position: relative; + top: 2px; +} +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow .counterNode { + order: 2; + font-weight: bold; + display: none; + font-size: 9px; + text-align: center; + border: 1px solid #2a89bc; + color: white; + background: #2a89bc; + border-radius: 4px; + line-height: 14px; + margin-right: 8px; + min-width: 23px; + height: 14px; + flex-shrink: 0; +} +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow .counterNode.aux, +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow .counterNode.marked { + background: #f5f5f5; + color: #1a1a1a; + border-color: #dcdcdc; +} +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow .counterNode.marked { + border-color: #257aa7; + background: #ffffff; +} +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow[data-feed-id="-3"][data-is-cat="false"] .counterNode.unread { + background-color: #3ea447; + border-color: #307f37; +} +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow .dijitTreeContent { + display: flex; + align-items: center; + flex-grow: 2; + min-width: 0; +} +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow .dijitTreeLabel { + cursor: pointer; + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + position: relative; +} +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow .dijitTreeLabel.Unread { + font-weight: bold; +} +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow.Error .dijitTreeLabel { + color: red; +} +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow.dijitTreeRowSelected { + box-shadow: -1px 0px 2px -1px rgba(0, 0, 0, 0.1); + border-color: #ddd transparent; + background: white; + color: black; +} +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow .dijitIcon.feed-icon { + margin-right: 2px; +} +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow i.icon.icon-inbox { + color: #333333; +} +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow i.icon.icon-archive { + color: #cf7800; +} +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow i.icon.icon-star { + position: relative; + color: #ffc069; + font-size: 21px; + left: -2px; +} +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow i.icon.icon-rss_feed { + color: #ff7c4b; +} +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow i.icon.icon-whatshot { + color: #69C671; +} +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow i.icon.icon-restore { + font-weight: bold; + color: #257aa7; +} +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(600px, 1fr)); + padding: 8px; + grid-gap: 8px; + background-color: #f5f5f5; +} +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] > * { + /* 2 = because #headlines-spacer is the actual last child + only if odd to deal with 1) single article and 2) not break any previous rows; + 1 = spacer; + + this is outside of .cdm selector because of #headlines-spacer etc + + .grid-span-row is manually expanded RROWs + */ +} +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] > *.grid-span-row, +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] > *:nth-last-child(2):nth-child(odd), +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] > *:nth-last-child(1) { + grid-column: 1 / -1; +} +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] .cdm.expanded .header, +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] .cdm.expanded .content { + background: white; + border: 1px solid #ddd; + overflow: hidden; +} +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] .cdm.expanded .content { + border-top-width: 0; + padding: 0 4px 4px 4px; +} +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] .cdm.expanded .header[data-is-stuck] { + top: -8px; + border-bottom-width: 1px; +} +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] .cdm.expanded .header { + border-bottom-width: 0; + padding: 4px; +} +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] .cdm.expanded .header .icon-grid-span { + display: inline; +} +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] .cdm.expanded .header .feed { + display: none; +} +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] .cdm.expanded .footer { + border: 0; + padding: 4px; +} +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] .cdm.expanded .footer .left, +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] .cdm.expanded .footer .right { + white-space: nowrap; +} +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] .cdm.expanded .footer .left { + overflow: hidden; + text-overflow: ellipsis; +} +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] .cdm.expanded .content-inner a, +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] .cdm.expanded .intermediate a { + word-break: break-all; +} +body.ttrss_main #headlines-frame { + padding: 0; + border: 0; + margin-top: 0; + -webkit-overflow-scrolling: touch; + -webkit-transform: translateZ(0); + transform: translateZ(0); + -webkit-backface-visibility: hidden; + backface-visibility: hidden; +} +body.ttrss_main #headlines-frame[data-is-vfeed="false"] .header .feed, +body.ttrss_main #headlines-frame[data-is-vfeed="false"] .hl .feed, +body.ttrss_main #headlines-frame[data-is-vfeed="false"] .header .icon-feed, +body.ttrss_main #headlines-frame[data-is-vfeed="false"] .hl .icon-feed { + display: none; +} +body.ttrss_main #headlines-frame[data-auto-catchup="true"] #headlines-spacer { + height: 100vh; +} +body.ttrss_main #headlines-frame .dijitCheckBox { + margin-right: 4px; +} +body.ttrss_main #headlines-frame[data-is-wide-screen="true"] .title { + overflow: visible; + white-space: normal; +} +body.ttrss_main #headlines-frame[data-is-wide-screen="true"] .hl .feed { + display: none; +} +body.ttrss_main #headlines-frame #headlines-spacer { + margin-left: 1px; + text-align: center; + color: black; + font-size: 11px; +} +body.ttrss_main #headlines-frame #headlines-spacer a, +body.ttrss_main #headlines-frame #headlines-spacer span { + color: black; + padding: 10px; + display: block; +} +body.ttrss_main #headlines-frame #headlines-spacer a:hover { + color: #257aa7; +} +body.ttrss_main #headlines-frame .feed-title { + border: 0px solid #257aa7; + border-bottom-width: 1px; + padding: 5px 8px; +} +body.ttrss_main #headlines-frame .feed-title a.title { + color: black; + font-weight: bold; +} +body.ttrss_main #headlines-frame .feed-title a { + color: black; +} +body.ttrss_main #headlines-frame .feed-title a:hover { + color: #257aa7; +} +body.ttrss_main #headlines-frame span.hlMenuAttach { + -webkit-touch-callout: none; + -webkit-user-select: none; + user-select: none; +} +body.ttrss_main #toolbar-frame_splitter { + display: none; +} +body.ttrss_main #toolbar-frame { + padding: 0; + margin: 0; + border: 0; + white-space: nowrap; + font-size: 13px; +} +body.ttrss_main #toolbar-frame #toolbar { + background: white; + border: 0 solid #ddd; + border-bottom-width: 1px; + padding-left: 4px; + height: 32px; + display: flex; + flex-direction: row; + flex-wrap: nowrap; + color: black; + font-size: 13px; + align-items: center; +} +body.ttrss_main #toolbar-frame #toolbar .dijitSelect, +body.ttrss_main #toolbar-frame #toolbar .dijitDropDownButton .dijitButtonNode, +body.ttrss_main #toolbar-frame #toolbar .dijitComboButton .dijitButtonNode { + border: 0; +} +body.ttrss_main #toolbar-frame #toolbar i.net-alert, +body.ttrss_main #toolbar-frame #toolbar .left i.icon-error { + color: red; +} +body.ttrss_main #toolbar-frame #toolbar i.log-alert { + color: #ddba1c; + cursor: pointer; +} +body.ttrss_main #toolbar-frame #toolbar i { + margin: 0 4px; +} +body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines { + font-size: 13px; + background: transparent; + padding-right: 4px; + flex-grow: 2; + display: flex; +} +body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines .left { + flex-grow: 2; + display: flex; + align-items: center; +} +body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines .left .feed_title, +body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines .left .cancel_search { + margin-left: 4px; +} +@media (max-width: 768px) { + body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines .left .feed_title, + body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines .left i.icon-syndicate { + display: none; + } +} +body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines .right { + display: flex; + align-items: center; +} +body.ttrss_main #toolbar-frame #toolbar #updates-available { + color: #69C671; + padding-right: 4px; +} +body.ttrss_main #toolbar-frame #toolbar #selected_prompt { + font-style: italic; + text-align: right; + margin-right: 4px; + color: #257aa7; +} +@media (max-width: 768px) { + body.ttrss_main #toolbar-frame #toolbar #selected_prompt { + display: none; + } +} +@media (max-width: 576px) { + body.ttrss_main #toolbar-frame #toolbar .select-articles-dropdown, + body.ttrss_main #toolbar-frame #toolbar .catchup-button { + display: none; + } +} +body.ttrss_main #header { + text-align: right; + color: black; + padding: 5px 5px 0 0px; + position: absolute; + border: 0; + margin: 0; + right: 0; + top: 0; + z-index: 5; +} +body.ttrss_main #header i.net-alert, +body.ttrss_main #header .left i.icon-error { + color: red; +} +body.ttrss_main #header i.log-alert { + color: #ddba1c; + cursor: pointer; +} +body.ttrss_main #header #updates-available { + color: #69C671; + padding-right: 4px; +} +body.ttrss_main #header i { + margin: 0 4px; +} +body.ttrss_main #content-insert { + padding: 0; + border: 0; + line-height: 1.5; + overflow: auto; + -webkit-overflow-scrolling: touch; +} +body.ttrss_main img.feed-icon, +body.ttrss_main img.icon { + width: 16px; + height: 16px; + line-height: 16px; + vertical-align: middle; + display: inline-block; +} +body.ttrss_main ul#filterDlg_Matches, +body.ttrss_main ul#filterDlg_Actions { + list-style-type: none; + margin: 0; + padding: 0; +} +body.ttrss_main ul#filterDlg_Matches li, +body.ttrss_main ul#filterDlg_Actions li { + cursor: pointer; +} +body.ttrss_main ul#filterDlg_Matches li .dijitCheckBox, +body.ttrss_main ul#filterDlg_Actions li .dijitCheckBox { + margin-right: 4px; +} +body.ttrss_main ul.hotkeys-help li { + display: flex; +} +body.ttrss_main ul.hotkeys-help li.desc { + flex-grow: 2; +} +body.ttrss_main ul.hotkeys-help .hk { + color: #257aa7; + width: 100px; +} +body.ttrss_main ul.hotkeys-help h3 { + margin: 8px 0px; +} +body.ttrss_main select.attachments { + display: block; + margin-top: 10px; + max-width: 120px; +} +body.ttrss_main #filterDlg_feeds select { + height: 150px; + width: 410px; +} +body.ttrss_main span.highlight { + background-color: #ffff00; + color: #cc90cc; +} +body.ttrss_main #feedEditDlg img.feedIcon { + border: 1px solid #ccc; + padding: 5px; + margin: 5px; + max-width: 20px; + max-height: 20px; + height: auto; + width: auto; +} +body.ttrss_main .dijitTooltipContents { + background: #1c5c7d; + color: #f5f5f5; +} +body.ttrss_main .dijitTooltipRight .dijitTooltipConnector { + border-right-color: #1c5c7d; +} +body.ttrss_main .dijitTooltipLeft .dijitTooltipConnector { + border-left-color: #1c5c7d; +} +body.ttrss_main .dijitTooltipBelow .dijitTooltipConnector { + border-bottom-color: #1c5c7d; +} +body.ttrss_main .dijitTooltipAbove .dijitTooltipConnector { + border-top-color: #1c5c7d; +} +/*body.ttrss_main .dijitDialog { + h1:first-of-type, + h2:first-of-type, + h3:first-of-type, + h4:first-of-type { + margin-top: 0px; + } +}*/ +body.ttrss_main[view-mode="marked"] #feeds-holder #feedTree .dijitTreeRow.Has_Marked .dijitTreeLabel { + color: #257aa7; +} +body.ttrss_main[view-mode="marked"] #feeds-holder #feedTree .dijitTreeRow.Has_Marked .counterNode.marked { + display: inline-block; +} +body.ttrss_main[view-mode="marked"][hide-read-feeds="true"][hide-read-shows-special="true"] #feeds-holder #feedTree .dijitTreeRow:not(.AlwaysVisible):not(.Special):not(.Has_Marked) { + display: none; +} +body.ttrss_main[view-mode="marked"][hide-read-feeds="true"][hide-read-shows-special="false"] #feeds-holder #feedTree .dijitTreeRow:not(.AlwaysVisible):not(.Has_Marked) { + display: none; +} +body.ttrss_main:not([view-mode="marked"]) #feeds-holder #feedTree .dijitTreeRow.Unread .counterNode.unread { + display: inline-block; +} +body.ttrss_main:not([view-mode="marked"]) #feeds-holder #feedTree .dijitTreeRow.Has_Aux:not(.Unread) .counterNode.aux { + display: inline-block; +} +body.ttrss_main:not([view-mode="marked"])[hide-read-feeds="true"][hide-read-shows-special="true"] #feeds-holder #feedTree .dijitTreeRow:not(.Unread):not(.AlwaysVisible):not(.Special) { + display: none; +} +body.ttrss_main:not([view-mode="marked"])[hide-read-feeds="true"][hide-read-shows-special="false"] #feeds-holder #feedTree .dijitTreeRow:not(.Unread):not(.AlwaysVisible) { + display: none; +} +body.ttrss_main { + /*.score-neutral i.icon-score { + opacity : 0.5; + }*/ +} +body.ttrss_main #toolbar-headlines i.icon-syndicate { + color: #ff7c4b; + margin-right: 8px; + border: 1px solid #ff7c4b; + border-radius: 4px; +} +body.ttrss_main #toolbar-headlines #feed_current_unread { + margin-left: 8px; + font-weight: bold; + text-align: center; + border: 1px solid #2a89bc; + color: white; + background: #2a89bc; + border-radius: 4px; + min-width: 23px; +} +body.ttrss_main i.icon-no-feed { + opacity: 0.2; +} +body.ttrss_main .dijitTreeRow.UpdatesDisabled .dijitTreeLabel { + opacity: 0.5; +} +body.ttrss_main .cdm.marked .left i.marked-pic, +body.ttrss_main .hl.marked .left i.marked-pic { + color: #ffc069; +} +body.ttrss_main .cdm.published .left i.pub-pic, +body.ttrss_main .hl.published .left i.pub-pic { + color: #ff7c4b; +} +body.ttrss_main .score-high i.icon-score { + color: #69C671; +} +body.ttrss_main .score-low i.icon-score { + color: #500; +} +body.ttrss_main i.icon-score, +body.ttrss_main i.icon-grid-span { + cursor: pointer; + color: #777; +} +body.ttrss_main .icon-grid-span { + display: none; +} +body.ttrss_main .icon-feed { + cursor: pointer; +} +body.ttrss_main .panel { + border: 1px solid #ddd; + background: #f5f5f5; + padding: 4px; +} +body.ttrss_main .dijitDialog .panel { + background: white; +} +body.ttrss_main .panel-scrollable { + overflow: auto; + height: 200px; +} +body.ttrss_main .panel-scrollable-400px { + overflow: auto; + height: 400px; +} +body.ttrss_main ul.list li { + padding: 2px; +} +body.ttrss_main ul.list { + padding: 4px; +} +body.ttrss_main ul.list-unstyled { + list-style-type: none; +} +body.ttrss_main .text-center { + text-align: center; +} +body.ttrss_main .text-right { + text-align: right; +} +body.ttrss_main .text-left { + text-align: left; +} +body.ttrss_main .dijitDialog .filter-results-list .preview { + margin: 8px; +} +body.ttrss_main .dijitDialog .filter-results-list .title { + font-weight: bold; +} +body.ttrss_main .dijitDialog #feed_add_spinner { + position: relative; + top: 5px; + width: 18px; + height: 18px; +} +body.ttrss_main .icon-three-dots { + width: 18px; + height: 18px; + vertical-align: middle; +} +body.ttrss_main, +body.ttrss_utility { + /*div.autocomplete { + position : absolute; + width : 250px; + background-color : @default-bg; + border :1px solid @border-default; + margin : 0px; + padding : 0px; + + ul { + list-style-type : none; + margin : 0px; + padding : 0px; + } + + ul li.selected { + background-color : darken(@default-bg, 10%); + } + + ul li { + list-style-type : none; + display : block; + margin : 0; + padding : 2px; + cursor : pointer; + } + }*/ +} +body.ttrss_main .alert, +body.ttrss_utility .alert { + padding: 8px 35px 8px 14px; + margin-bottom: 10px; + background-color: #fcf8e3; + border: 1px solid #fbeed5; + border-radius: 4px; +} +body.ttrss_main .alert .close, +body.ttrss_utility .alert .close { + position: relative; + top: -2px; + right: -21px; + line-height: 20px; + cursor: pointer; +} +body.ttrss_main .pull-right, +body.ttrss_utility .pull-right { + float: right; +} +body.ttrss_main .pull-left, +body.ttrss_utility .pull-left { + float: left; +} +body.ttrss_main .pre-wrap, +body.ttrss_utility .pre-wrap { + white-space: pre-wrap; +} +body.ttrss_main .text-error, +body.ttrss_utility .text-error { + color: #b94a48; +} +body.ttrss_main .text-info, +body.ttrss_utility .text-info { + color: #3a87ad; +} +body.ttrss_main .text-success, +body.ttrss_utility .text-success { + color: #468847; +} +body.ttrss_main .text-warning, +body.ttrss_utility .text-warning { + color: #a47e3c; +} +body.ttrss_main .text-muted, +body.ttrss_utility .text-muted { + color: black; +} +body.ttrss_main .text-small, +body.ttrss_utility .text-small, +body.ttrss_main .small, +body.ttrss_utility .small { + font-size: 11px; +} +body.ttrss_main .alert, +body.ttrss_utility .alert, +body.ttrss_main .alert h4, +body.ttrss_utility .alert h4 { + color: #c09853; +} +body.ttrss_main .alert h4, +body.ttrss_utility .alert h4 { + margin: 0; +} +body.ttrss_main .alert-success, +body.ttrss_utility .alert-success { + color: #468847; + background-color: #dff0d8; + border-color: #d6e9c6; +} +body.ttrss_main .alert-success h4, +body.ttrss_utility .alert-success h4 { + color: #468847; +} +body.ttrss_main .alert-danger, +body.ttrss_utility .alert-danger, +body.ttrss_main .alert-error, +body.ttrss_utility .alert-error { + color: #b94a48; + background-color: #f2dede; + border-color: #eed3d7; +} +body.ttrss_main .alert-danger h4, +body.ttrss_utility .alert-danger h4, +body.ttrss_main .alert-error h4, +body.ttrss_utility .alert-error h4 { + color: #b94a48; +} +body.ttrss_main .alert-info, +body.ttrss_utility .alert-info { + color: #3a87ad; + background-color: #d9edf7; + border-color: #bce8f1; +} +body.ttrss_main .alert-info h4, +body.ttrss_utility .alert-info h4 { + color: #3a87ad; +} +body.ttrss_main hr, +body.ttrss_utility hr { + border: 0px solid #ccc; + border-bottom-width: 1px; +} +::selection { + background: #257aa7; + color: white; +} +::-webkit-scrollbar { + width: 4px; +} +::-webkit-scrollbar-thumb { + background-color: #257aa7; +} +::-webkit-scrollbar-track { + background-color: #eee; +} +/*video::-webkit-media-controls-overlay-play-button { + display: none; +}*/ +.cdm { + /*i.material-icons { + color : @color-icon; + }*/ +} +.cdm .header, +.cdm .footer { + display: flex; + flex-direction: row; + flex-wrap: nowrap; +} +.cdm .header img, +.cdm .footer img, +.cdm .footer i.material-icons { + margin: 0 4px; + vertical-align: middle; +} +.cdm .header-sticky-guard { + height: 0; +} +.cdm .header { + position: sticky; + top: 0; + z-index: 3; + align-items: center; +} +.cdm .header > * { + padding: 4px; + white-space: nowrap; +} +.cdm .header .left, +.cdm .header .right { + display: flex; + align-items: center; +} +.cdm .header .left i.material-icons, +.cdm .header .right i.material-icons { + margin-left: 2px; + padding: 2px; + transition: color 0.2s linear; + user-select: none; + font-size: 21px; +} +.cdm .header .titleWrap { + flex-grow: 2; +} +.cdm .header .updated { + color: black; + font-weight: normal; + font-size: 11px; + white-space: nowrap; +} +.cdm .header input { + margin: 0px 4px; +} +.cdm .header .feed { + float: right; + font-weight: normal; + font-style: italic; +} +.cdm .header .feed a { + border-radius: 4px; + display: inline-block; + padding: 1px 4px 1px 4px; +} +.cdm .header .feed, +.cdm .header .feed a { + vertical-align: middle; + color: black; + font-weight: normal; + font-style: italic; + font-size: 11px; +} +.cdm .header .author { + white-space: nowrap; + color: black; + font-size: 11px; + font-weight: normal; +} +.cdm .footer { + height: 30px; + padding-left: 5px; + font-weight: normal; + color: black; + clear: both; + align-items: center; +} +.cdm .footer i.material-icons { + color: #777; +} +.cdm .footer .left { + flex-grow: 2; +} +.cdm .intermediate { + margin-top: 10px; + margin-left: 10px; + line-height: 1.5; +} +.cdm .content-inner { + margin: 10px; + line-height: 1.5; + font-size: 16px; +} +.cdm .intermediate iframe, +.cdm .content-inner iframe { + max-width: 98%; + width: auto; + height: auto; +} +.cdm .intermediate img, +.cdm .intermediate video, +.cdm .content-inner img, +.cdm .content-inner video { + border-width: 0px; + max-width: 98%; + width: auto; + height: auto; +} +.cdm.expanded .collapse, +.cdm.expanded .excerpt { + display: none; +} +.cdm.expanded .titleWrap { + white-space: normal; +} +.cdm.expanded .footer { + border: 0px solid #ddd; + border-bottom-width: 1px; +} +.cdm.expanded > hr { + margin-top: 0; + margin-bottom: 0; +} +.cdm.expanded .header a.title { + font-size: 16px; + color: #999; + font-weight: 600; + transition: color 0.2s, background 0.2s; + text-rendering: optimizelegibility; + font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif; +} +.cdm.expanded.active .content, +.cdm.expanded.Unread .content { + color: black; +} +.cdm.expanded.active .header .title { + color: #257aa7; +} +.cdm.expanded.Unread .header .title { + color: black; +} +.cdm.expanded .content { + color: black; +} +.cdm.vgrlf .feed { + display: none; +} +.cdm .feed-title { + border: 0px solid #257aa7; + border-bottom-width: 1px; + padding: 5px 3px 5px 5px; +} +.cdm .feed-title a.title { + color: black; + font-weight: bold; +} +.cdm .feed-title a { + color: black; +} +.cdm .feed-title a:hover { + color: #257aa7; +} +.cdm .content-inner .embed-responsive { + overflow: hidden; + padding-bottom: 56.25%; + position: relative; +} +.cdm .content-inner .embed-responsive iframe { + border: 0; + bottom: 0; + height: 100%; + left: 0; + position: absolute; + top: 0; + width: 100%; +} +.cdm.expandable { + background-color: #f5f5f5; + border: 0px solid #ddd; + border-bottom-width: 1px; +} +.cdm.expandable > hr { + display: none; +} +.cdm.expandable div.header span.titleWrap { + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; +} +.cdm.expandable .excerpt { + white-space: nowrap; + font-size: 11px; + color: #999; + font-weight: normal; + cursor: pointer; +} +.cdm.expandable:not(.active) { + user-select: none; +} +.cdm.expandable.Unread { + background: white; +} +.cdm.expandable.Selected:not(.active) { + background: #3f728e; +} +.cdm.expandable.Selected:not(.active) a, +.cdm.expandable.Selected:not(.active) .header a.title, +.cdm.expandable.Selected:not(.active) span { + color: white; +} +.cdm.expandable.active { + background: white ! important; +} +.cdm.expandable.active div.header span.titleWrap { + white-space: normal; +} +.cdm.expandable .header a.title { + font-weight: 600; + color: black; + font-size: 14px; + transition: color 0.2s, background 0.2s; + text-rendering: optimizelegibility; + font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif; +} +.cdm.expandable.Unread div.header a.title { + color: black; +} +.cdm.expandable.active .collapse i.material-icons { + color: #257aa7; + cursor: pointer; +} +.cdm.expandable.active .excerpt { + display: none; +} +.cdm.expandable.active div.header a.title { + color: #257aa7; + font-size: 16px; + font-weight: 600; + text-rendering: optimizelegibility; + font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif; +} +.cdm.expandable:not(.active) { + cursor: pointer; +} +.cdm.expandable:not(.active) .content, +.cdm.expandable:not(.active) .collapse { + display: none; +} +.cdm.expandable.active .header[data-is-stuck], +.cdm.expanded .header[data-is-stuck] { + box-shadow: 0 1px 1px -1px rgba(0, 0, 0, 0.1); + border: 0 solid #ddd; + border-bottom-width: 1px; + background: white ! important; + opacity: 0.9; + backdrop-filter: blur(6px); +} +body.ttrss_prefs { + background-color: #f5f5f5; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 14px; +} +body.ttrss_prefs h1, +body.ttrss_prefs h2, +body.ttrss_prefs h3, +body.ttrss_prefs h4 { + font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif; + font-weight: 600; + color: black; +} +body.ttrss_prefs .dijitContentPane > h1:first-of-type, +body.ttrss_prefs .dijitContentPane > h2:first-of-type, +body.ttrss_prefs .dijitContentPane > h3:first-of-type { + margin-top: 0; +} +body.ttrss_prefs #footer, +body.ttrss_prefs #header { + padding: 8px; + font-size: 13px; +} +body.ttrss_prefs #header { + float: right; +} +body.ttrss_prefs #footer_splitter { + display: none; +} +body.ttrss_prefs #footer { + background-color: #f5f5f5; + font-size: 13px; + border: 0px; + text-align: center; +} +body.ttrss_prefs #header img { + vertical-align: middle; + cursor: pointer; +} +body.ttrss_prefs .dijitTree#filterTree .dijitTreeIcon, +body.ttrss_prefs .dijitTree#labelTree .dijitTreeIcon, +body.ttrss_prefs .dijitTree#filterTree .dijitTreeIcon { + display: none; +} +body.ttrss_prefs .dijitAccordionTitle i.material-icons { + top: -1px; + position: relative; +} +body.ttrss_prefs .dijitAccordionTitleSelected i.material-icons { + color: white; +} +body.ttrss_prefs #feedsTab { + background: #f5f5f5; +} +body.ttrss_prefs .dijitDialog #pref-profiles-list .dijitInlineEditBoxDisplayMode { + padding: 0px; +} +body.ttrss_prefs #pref_feeds_errors_btn { + color: red; +} +body.ttrss_prefs .user-css-editor { + height: 300px; + width: 575px; +} +body.ttrss_prefs fieldset.prefs { + min-height: 30px; + display: flex; + flex-direction: row; + align-items: center; +} +body.ttrss_prefs fieldset.prefs label:first-of-type { + min-width: 300px; +} +body.ttrss_prefs fieldset.prefs .help-text { + display: inline-block; + margin-left: 10px; +} +body.ttrss_prefs fieldset.prefs .help-text-bottom { + margin-top: 10px; +} +body.ttrss_prefs table th { + text-align: left; +} +body.ttrss_prefs table td.checkbox { + text-align: center; + width: 32px; +} +body.ttrss_prefs ul.prefs-plugin-list { + margin: 0; + padding: 0; +} +body.ttrss_prefs ul.prefs-plugin-list li { + display: flex; + align-items: center; + border-bottom: #ddd 1px solid; + line-height: 30px; +} +body.ttrss_prefs ul.prefs-plugin-list li.text-center { + border: 0; + display: block; +} +body.ttrss_prefs ul.prefs-plugin-list li > * { + padding: 8px; +} +body.ttrss_prefs ul.prefs-plugin-list li label.checkbox { + display: flex; + align-items: center; + cursor: pointer; +} +body.ttrss_prefs ul.prefs-plugin-list li label.checkbox.system { + cursor: auto; +} +body.ttrss_prefs ul.prefs-plugin-list li label.checkbox .name { + min-width: 300px; + flex-grow: 2; + display: inline-block; + text-align: right; + font-weight: bold; + margin-right: 20px; +} +@media (max-width: 992px) { + body.ttrss_prefs ul.prefs-plugin-list li label.checkbox .name { + min-width: 200px ! important; + } + body.ttrss_prefs ul.prefs-plugin-list li .version { + display: none; + } +} +body.ttrss_prefs ul.prefs-plugin-list li .actions { + flex-grow: 2; + text-align: right; +} +body.ttrss_prefs ul.prefs-plugin-list li .version { + min-width: 200px; + text-align: right; +} +body.ttrss_prefs .plugin-installer-list li, +body.ttrss_prefs .plugin-updater-list li { + border-bottom: #ddd 1px solid; +} +body.ttrss_prefs .plugin-installer-list li > *, +body.ttrss_prefs .plugin-updater-list li > * { + padding: 8px 4px; +} +body.ttrss_prefs .plugin-installer-list li > pre, +body.ttrss_prefs .plugin-updater-list li > pre { + margin: 0; +} +body.ttrss_prefs .plugin-installer-list li h3, +body.ttrss_prefs .plugin-updater-list li h3 { + margin: 0; +} +body.ttrss_prefs .plugin-installer-list li.text-center, +body.ttrss_prefs .plugin-updater-list li.text-center { + border: 0; +} +body.ttrss_prefs .plugin-installer-list .plugin-installed > *, +body.ttrss_prefs .plugin-updater-list .plugin-installed > * { + opacity: 0.5; +} +body.ttrss_prefs .users-list td, +body.ttrss_prefs .event-log td, +body.ttrss_prefs .users-list th, +body.ttrss_prefs .event-log th { + cursor: pointer; + padding: 8px; + border-bottom: #ddd 1px solid; +} +body.ttrss_prefs .event-log tr td, +body.ttrss_prefs .event-log tr th { + vertical-align: top; +} +body.ttrss_prefs .event-log tr .errno { + font-style: italic; + font-weight: bold; + white-space: nowrap; +} +body.ttrss_prefs .event-log tr .errstr { + word-break: break-all; + white-space: pre-wrap; + width: 30%; +} +body.ttrss_prefs .event-log tr .filename { + word-break: break-all; + width: 20%; +} +body.ttrss_prefs .event-log tr .filename, +body.ttrss_prefs .event-log tr .login, +body.ttrss_prefs .event-log tr .timestamp { + color: black; +} +body.ttrss_prefs hr { + border-color: #ddd; + max-width: 100%; +} +body.ttrss_prefs .phpinfo table { + border-collapse: collapse; +} +body.ttrss_prefs .phpinfo td.e, +body.ttrss_prefs .phpinfo td.v { + border: 1px solid #ccc; +} +body.ttrss_prefs .phpinfo td.e { + font-weight: bold; +} +body.ttrss_prefs .phpinfo td.v { + font-family: monospace; + word-break: break-all; +} +body.ttrss_prefs #filterNewRuleDlg .dijitValidationTextAreaError, +body.ttrss_main #filterNewRuleDlg .dijitValidationTextAreaError { + background: #ffc0c0; +} +body.ttrss_prefs #filterNewRuleDlg .dijitValidationTextArea:not(.dijitValidationTextAreaError), +body.ttrss_main #filterNewRuleDlg .dijitValidationTextArea:not(.dijitValidationTextAreaError) { + background: #c0ffc0; +} +body.ttrss_prefs fieldset, +body.ttrss_utility fieldset { + border-width: 0px; + padding: 5px 0px; +} +body.ttrss_prefs fieldset.narrow, +body.ttrss_utility fieldset.narrow { + padding: 2px 0px; +} +body.ttrss_prefs fieldset.align-right, +body.ttrss_utility fieldset.align-right { + text-align: right; +} +body.ttrss_prefs fieldset > label:first-of-type, +body.ttrss_utility fieldset > label:first-of-type { + min-width: 140px; + margin-right: 20px; + display: inline-block; + text-align: right; + font-weight: bold; +} +body.ttrss_prefs fieldset > label.checkbox, +body.ttrss_utility fieldset > label.checkbox { + display: inline; + font-weight: normal; +} +body.ttrss_utility.sanity_failed { + background: #900; +} +body.ttrss_utility { + background: #f5f5f5; + color: black; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 14px; + margin: 4em; +} +body.ttrss_utility .content { + background: white; + border: 1px solid #ddd; + padding: 20px; + box-shadow: 0px 1px 1px -1px rgba(0, 0, 0, 0.1); +} +body.ttrss_utility .content h2:first-of-type { + margin-top: 0; +} +body.ttrss_utility .content h2, +body.ttrss_utility .content h3, +body.ttrss_utility .content h4 { + color: #257aa7; + font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif; +} +body.ttrss_utility .content h2 { + font-size: 18px; +} +body.ttrss_utility .content h3 { + font-size: 16px; +} +body.ttrss_utility a { + color: #257aa7; + text-decoration: none; +} +body.ttrss_utility a:hover, +body.ttrss_utility a:focus { + color: #133d54; + text-decoration: underline; +} +body.ttrss_utility h1 { + color: gray; + font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 18px; + margin: 10px 0 0 0; +} +body.ttrss_utility .footer { + text-align: center; + padding-top: 10px; +} +body.ttrss_utility .footer a { + color: gray; +} +body.ttrss_utility .footer a:hover { + color: #257aa7; +} +body.ttrss_utility form { + margin: 0; +} +body.ttrss_utility.otp .content fieldset > label { + display: inline; +} +body.ttrss_utility.ttrss_login { + margin: 0; + padding: 0; + width: 100%; + height: 100%; + position: absolute; + display: flex; + align-items: center; + justify-content: center; +} +body.ttrss_utility.ttrss_login .container { + max-width: 600px; + margin-left: auto; + margin-right: auto; +} +body.ttrss_utility.ttrss_login .container .content { + padding: 40px; +} +body.ttrss_utility.installer, +body.ttrss_utility.feed_debugger { + margin: 2em; +} +body.ttrss_utility.installer pre, +body.ttrss_utility.feed_debugger pre { + white-space: pre-wrap; +} +body.ttrss_utility.share_popup { + margin: 0; + padding: 0; + background: white; +} +body.ttrss_utility.share_popup .content { + padding: 15px; + border-width: 0; + box-shadow: none; +} +.flat { + /*#feedTree { + .dijitTreeContent .dijitInline { + vertical-align : baseline; + } + }*/ +} +.flat li { + padding: 2px; +} +.flat .dijitButton i.material-icons { + position: relative; + top: -1px; +} +.flat .tabLabel > i.material-icons { + position: relative; + top: -1px; +} +.flat #filterDlg_Matches .filterRule { + color: green; + word-break: break-all; +} +.flat #filterDlg_Matches .filterRule .field, +.flat #filterDlg_Matches .filterRule .feed { + word-break: break-word; + font-style: italic; +} +.flat #filterTree .filterRules li.inverse, +.flat #filterDlg_Matches span.filterRule.inverse { + color: red; +} +.flat .dijitToolbar { + font-size: 13px; + font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif; + padding: 0; +} +.flat .dijitToolbar .dijitTextBox .dijitInputContainer .dijitInputInner { + line-height: 10px; +} +.flat .dijitToolbar .dijitTextBox .dijitInputContainer .dijitPlaceHolder { + line-height: 15px; + margin-left: 0; +} +.flat .dijitToolbar label { + position: relative; + top: 2px; +} +.flat .dijitAccordionContainer { + box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.1); +} +.flat .dijitCheckBox.dijitCheckBoxChecked { + background-color: #69C671; + border-color: #45b74f; +} +.flat .dijitMenu .dijitMenuItem .dijitMenuItemLabel { + padding: 4px 8px; + font-size: 13px; + font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif; +} +.flat .dijitMenu .dijitMenuItem.dijitDisabled:not(.dijitMenuItemSelected) .dijitMenuItemLabel { + color: #2e99d1; +} +.flat .dijitMenu .dijitMenuItem td { + padding: 0; +} +.flat .dijitCheckBox { + margin: 1px; +} +.flat .dijitCheckBox:before { + font-family: "flat-icon"; + content: "\f00c"; + color: white; +} +.flat .dijitTab, +.flat .dijitAccordionTitle { + font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif; +} +.flat .dijitTab i.material-icons, +.flat .dijitAccordionInnerContainer:not(.dijitSelected) i.material-icons { + color: #257aa7; +} +.flat .dijitTree { + /*.dijitTreeRow .dijitTreeExpando { + position : relative; + top : -2px; + }*/ +} +.flat .dijitTree .dijitTreeRowFlex { + display: flex; + flex-direction: row; + align-items: center; +} +.flat .dijitTree .dijitTreeRowFlex .dijitTreeIcon { + font-size: inherit; + height: auto; +} +.flat .dijitTree .dijitTreeRowFlex .dijitTreeExpando { + height: auto; + line-height: 0; +} +.flat .dijitTree .dijitFolderClosed, +.flat .dijitTree .dijitFolderOpened { + display: none; +} +.flat .dijitTree .dijitTreeRowSelected .filterRules li { + color: white; +} +.flat .dijitTree .dijitTreeRowSelected .dijitTreeExpando { + color: #257aa7; +} +.flat .dijitTree .dijitTreeNode .dijitTreeRow.dijitTreeRowSelected { + color: white; +} +.flat .dijitTree .labelParam { + float: right; + margin-right: 16px; +} +.flat .dijitTree .dijitTreeRow.filterDisabled { + opacity: 0.5; +} +.flat .dijitTree .dijitTreeRow.filterDisabled .filterRules { + filter: saturate(0%); +} +.flat .dijitTree .feedParam { + float: right; +} +.flat .dijitTree .filterRules { + font-size: 12px; + line-height: normal; + white-space: normal; + margin-left: 28px; +} +.flat .dijitTree .filterRules li { + color: green; +} +.flat .dijitTree .dijitTreeContainer { + max-width: 100%; +} +.flat .dijitTree .dijitTreeRow { + overflow: hidden; + -moz-user-select: none; + user-select: none; + text-overflow: ellipsis; +} +.flat label.dijitButton { + border: 1px solid #ccc; + padding: 6px; + border-radius: 4px; + cursor: pointer; + position: relative; + top: 1px; +} +.flat label.dijitButton:hover { + background-color: #f5f5f5; +} +.flat .dijitTree .dijitTreeNode .dijitTreeRow { + padding: 4px 0px 4px; + border-width: 1px; + color: black; +} +.flat .dijitDialog { + width: 600px; +} +@font-face { + font-family: 'Material Icons'; + font-style: normal; + font-weight: 400; + src: url(../vendor/mervick/material-design-icons/fonts/MaterialIcons-Regular.eot); + /* For IE6-8 */ + src: local('Material Icons'), local('MaterialIcons-Regular'), url(../vendor/mervick/material-design-icons/fonts/MaterialIcons-Regular.woff2) format('woff2'), url(../vendor/mervick/material-design-icons/fonts/MaterialIcons-Regular.woff) format('woff'), url(../vendor/mervick/material-design-icons/fonts/MaterialIcons-Regular.ttf) format('truetype'); +} +.material-icons { + font-family: 'Material Icons'; + font-weight: normal; + font-style: normal; + font-size: 18px; + /* Preferred icon size */ + display: inline-block; + line-height: 1; + text-transform: none; + letter-spacing: normal; + word-wrap: normal; + white-space: nowrap; + direction: ltr; + vertical-align: middle; + /* Support for all WebKit browsers. */ + -webkit-font-smoothing: antialiased; + /* Support for Safari and Chrome. */ + text-rendering: optimizeLegibility; + /* Support for Firefox. */ + -moz-osx-font-smoothing: grayscale; + /* Support for IE. */ + font-feature-settings: 'liga'; +} +.flat .dijitDialog .dijitDialogPaneContent { + background: #f5f5f5; +} +.flat .dijitTab:not(.dijitTabChecked) { + background: #f5f5f5; +} +.flat .dijitCheckBox { + background: #ccc; +} +body.ttrss_zoom { + max-width: 900px; + margin: 2em auto; +} +body.ttrss_zoom div.post { + border: 1px solid #ddd; + background: white; + box-shadow: 0px 1px 1px -1px rgba(0, 0, 0, 0.1); +} +body.ttrss_zoom div.post .attachments { + display: none; +} +body.ttrss_zoom div.post div.header { + padding-bottom: 8px; + border: 0 solid #ddd; + border-bottom-width: 1px; + background: white; + font-size: 13px; + color: black; +} +body.ttrss_zoom div.post div.header .row { + display: flex; + margin-bottom: 4px; + flex-wrap: nowrap; + align-items: center; + justify-content: space-between; +} +body.ttrss_zoom div.post div.header .row h1 { + margin-top: 0; + margin-bottom: 8px; +} +body.ttrss_zoom div.post .content { + font-size: 15px; + line-height: 1.5; + border-width: 0; + padding: 0; + padding-top: 8px; +} +body.ttrss_zoom div.post .content img, +body.ttrss_zoom div.post .content video { + max-width: 760px; + height: auto; +} +body.ttrss_zoom div.post .content blockquote { + margin: 5px 0px 5px 0px; + color: black; + padding-left: 8px; + border: 0px solid #ddd; + border-left-width: 4px; +} +body.ttrss_zoom div.post .content code { + color: #009900; + font-family: monospace; + font-size: 12px; +} +body.ttrss_zoom div.post .content pre { + padding: 8px; + color: black; + font-family: monospace; + font-size: 12px; + border: 0px solid #ccc; + background: #f5f5f5; + display: block; + max-width: 98%; + overflow: auto; + white-space: pre-wrap; +} diff --git a/themes/light-high-contrast.less b/themes/light-high-contrast.less new file mode 100644 index 000000000..eae190d32 --- /dev/null +++ b/themes/light-high-contrast.less @@ -0,0 +1,8 @@ +@import "light/defines.less"; + +@default-fg: black; +@default-text: @default-fg; + +@import "light/dijit_light.less"; +@import "light/zoom.less"; +@import "../lib/flat-ttrss/flat_combined.css"; diff --git a/themes/light.css b/themes/light.css index 9a4ac98c0..d26e5f81b 100644 --- a/themes/light.css +++ b/themes/light.css @@ -11,71 +11,81 @@ body.ttrss_prefs, } body.ttrss_main { background: white; - color: black; + color: #555; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 14px; overflow: hidden; + /*ul.compact { + list-style-type : none; + margin : 0; + padding : 0; + + li { + margin : 0; + padding : 0; + } + }*/ } body.ttrss_main :focus { outline: none; } -body.ttrss_main div.post { - padding: 0px; +body.ttrss_main .post { + padding: 0; font-size: 13px; } -body.ttrss_main div.post div.header { +body.ttrss_main .post .header { padding: 5px; - color: #909090; - border: 0px solid #ddd; + color: #555; + border: 0 solid #ddd; border-bottom-width: 1px; background: #f5f5f5; } -body.ttrss_main div.post div.header .left, -body.ttrss_main div.post div.header .right { +body.ttrss_main .post .header .left, +body.ttrss_main .post .header .right { display: flex; } -body.ttrss_main div.post div.header .row { +body.ttrss_main .post .header .row { display: flex; margin-bottom: 4px; flex-wrap: nowrap; align-items: center; justify-content: space-between; } -body.ttrss_main div.post div.header .comments { +body.ttrss_main .post .header .comments { flex-grow: 2; } -body.ttrss_main div.post div.header .date { +body.ttrss_main .post .header .date { white-space: nowrap; } -body.ttrss_main div.post div.header img, -body.ttrss_main div.post div.header i.material-icons { +body.ttrss_main .post .header img, +body.ttrss_main .post .header i.material-icons { margin: 0px 4px; vertical-align: middle; color: #777; } -body.ttrss_main div.post div.header .title { +body.ttrss_main .post .header .title { flex-grow: 2; font-size: 15px; font-weight: 600; text-rendering: optimizelegibility; - font-family: "Segoe WP Semibold", "Segoe UI Semibold", "Segoe UI Web Semibold", "Segoe UI", Ubuntu, "Helvetica Neue", Helvetica, Arial, sans-serif; + font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif; } -body.ttrss_main div.post div.content { +body.ttrss_main .post div.content { padding: 10px; font-size: 16px; } -body.ttrss_main div.post div.content img, -body.ttrss_main div.post div.content video { +body.ttrss_main .post div.content img, +body.ttrss_main .post div.content video { border-width: 0px; max-width: 98%; height: auto; } -body.ttrss_main div.post div.content div.embed-responsive { +body.ttrss_main .post div.content div.embed-responsive { overflow: hidden; padding-bottom: 56.25%; position: relative; } -body.ttrss_main div.post div.content div.embed-responsive iframe { +body.ttrss_main .post div.content div.embed-responsive iframe { border: 0; bottom: 0; height: 100%; @@ -172,6 +182,11 @@ body.ttrss_main .notify.notify_progress { border-color: #d7c47a; background-color: #fff7d5; } +body.ttrss_main .notify.notify_progress img { + width: 18px; + height: 18px; + filter: saturate(0); +} body.ttrss_main .notify.notify_info { border-color: #257aa7; background-color: #d5ebf6; @@ -217,6 +232,10 @@ body.ttrss_main .hl .right { display: flex; align-items: center; } +body.ttrss_main .hl .left img, +body.ttrss_main .hl .right img { + margin: 0 4px; +} body.ttrss_main .hl .left i.material-icons, body.ttrss_main .hl .right i.material-icons { margin-left: 2px; @@ -225,25 +244,28 @@ body.ttrss_main .hl .right i.material-icons { user-select: none; font-size: 21px; } +body.ttrss_main .hl .left input { + margin: 0 4px; +} +body.ttrss_main .hl .right { + text-align: right; +} body.ttrss_main .hl .right i.material-icons { color: #777; } -body.ttrss_main .hl div.title { +body.ttrss_main .hl .title { cursor: pointer; flex-grow: 2; overflow: hidden; text-overflow: ellipsis; } -body.ttrss_main .hl span.author { +body.ttrss_main .hl .author { white-space: nowrap; color: #555; font-size: 11px; font-weight: normal; } -body.ttrss_main .hl div.right { - text-align: right; -} -body.ttrss_main .hl span.feed a { +body.ttrss_main .hl .feed a { border-radius: 4px; display: inline-block; padding: 1px 4px; @@ -252,29 +274,22 @@ body.ttrss_main .hl span.feed a { font-weight: normal; color: #555; } -body.ttrss_main .hl span.feed a:hover { +body.ttrss_main .hl .feed a:hover { color: #257aa7; } -body.ttrss_main .hl span.updated { +body.ttrss_main .hl .updated { color: #555; text-align: right; font-size: 11px; padding-left: 10px; } -body.ttrss_main .hl span.updated div { +body.ttrss_main .hl .updated div { display: inline-block; } -body.ttrss_main .hl div.left input { - margin: 0px 4px; -} -body.ttrss_main .hl div.left img, -body.ttrss_main .hl div.right img { - margin: 0px 4px; -} body.ttrss_main .hl div.title a { font-weight: 600; text-rendering: optimizelegibility; - font-family: "Segoe UI", Ubuntu, "Helvetica Neue", Helvetica, Arial, sans-serif; + font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif; color: #555; } body.ttrss_main .hl a.title.high, @@ -287,10 +302,10 @@ body.ttrss_main .hl.vgrlf .feed { body.ttrss_main .hl.Unread { background: white; } -body.ttrss_main .hl.Unread div.title a { +body.ttrss_main .hl.Unread .title a { color: black; } -body.ttrss_main .hl.active div.title a { +body.ttrss_main .hl.active .title a { color: #257aa7; /* text-shadow : 1px 1px 2px #fff; */ } @@ -312,9 +327,6 @@ body.ttrss_main .hl.active span, body.ttrss_main .hl.Selected span { color: white; } -body.ttrss_main .hl.Grayed { - color: #909090; -} body.ttrss_main #content-insert blockquote, body.ttrss_main #headlines-frame blockquote, body.ttrss_main .dijitContentPane blockquote { @@ -360,7 +372,7 @@ body.ttrss_main .label { vertical-align: middle; background-color: #fff7d5; font-size: 9px; - color: black; + color: #555; font-weight: normal; margin-left: 2px; padding: 2px 4px; @@ -371,45 +383,6 @@ body.ttrss_main i.pub-pic { cursor: pointer; color: #ccc; } -body.ttrss_main div.errorExplained { - border: 1px solid #ddd; - margin: 5px 0px 5px 0px; - padding: 5px; -} -body.ttrss_main ul.browseFeedList { - height: 300px; - width: 100%; - overflow: auto; - border-width: 0px 1px 1px 1px; - border-color: #ddd; - border-style: solid; - margin: 0px 0px 5px 0px; - background-color: white; - list-style-type: none; - padding: 0px; -} -body.ttrss_main ul.browseFeedList li { - display: flex; - align-items: center; -} -body.ttrss_main ul.browseFeedList li > * { - margin: 2px; -} -body.ttrss_main .browseFeedList span.subscribers { - color: #808080; -} -body.ttrss_main ul.compact { - list-style-type: none; - margin: 0px; - padding: 0px; -} -body.ttrss_main ul.compact li { - margin: 0px; - padding: 0px; -} -body.ttrss_main .noborder { - border-width: 0px; -} body.ttrss_main #overlay { background: white; left: 0; @@ -423,11 +396,6 @@ body.ttrss_main #overlay_inner { font-weight: bold; margin: 1em; } -body.ttrss_main div.loadingPrompt { - padding: 1em; - text-align: center; - font-weight: bold; -} body.ttrss_main div.whiteBox { margin-left: 1px; text-align: center; @@ -436,20 +404,13 @@ body.ttrss_main div.whiteBox { border: 0px solid #ddd; border-bottom-width: 1px; } -body.ttrss_main div#headlines-frame.wide .title { - overflow: visible; - white-space: normal; -} -body.ttrss_main div#headlines-frame.wide .hl .feed { - display: none; -} body.ttrss_main .dijitDialog header, body.ttrss_main .dijitDialog .dlgSec, body.ttrss_main .dijitDialog .dlgSecHoriz { font-size: 16px; font-weight: 600; color: #555; - font-family: "Segoe WP Semibold", "Segoe UI Semibold", "Segoe UI Web Semibold", "Segoe UI", Ubuntu, "Helvetica Neue", Helvetica, Arial, sans-serif; + font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif; } body.ttrss_main .dijitDialog section, body.ttrss_main .dijitDialog .dlgSecCont { @@ -527,11 +488,6 @@ body.ttrss_main div#cmdline { padding: 3px 5px 3px 5px; z-index: 5; } -body.ttrss_main #feed_browser_spinner { - vertical-align: middle; - height: 18px; - width: 18px; -} body.ttrss_main .exception-contents h3 { color: red; } @@ -540,14 +496,15 @@ body.ttrss_main .exception-contents textarea { height: 200px; font-size: 11px; } +body.ttrss_main #headlines-wrap-inner, body.ttrss_main #content-wrap { - padding: 0px; - border-width: 0px; - margin: 0px; + padding: 0; + border: 0; + margin: 0; } body.ttrss_main #feeds-holder { - padding: 0px; - border: 0px solid #ddd; + padding: 0; + border: 0 solid #ddd; overflow: hidden; background: #f5f5f5; box-shadow: inset -1px 0px 2px -1px rgba(0, 0, 0, 0.1); @@ -557,19 +514,26 @@ body.ttrss_main #feeds-holder #feedTree { height: 100%; overflow-x: hidden; text-rendering: optimizelegibility; - font-family: "Segoe UI", Ubuntu, "Helvetica Neue", Helvetica, Arial, sans-serif; + font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif; } -body.ttrss_main #feeds-holder #feedTree .counterNode.aux, -body.ttrss_main #feeds-holder #feedTree .counterNode.marked { - background: #f5f5f5; - color: #6f6f6f; - border-color: #dcdcdc; +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow { + border: 1px solid transparent; } -body.ttrss_main #feeds-holder #feedTree .counterNode.marked { - border-color: #257aa7; - background: #ffffff; +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow.Is_Feed .loadingNode { + left: -2px; + height: 14px; + width: 14px; + position: relative; +} +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow.Is_Cat .loadingNode { + height: 11px; + width: 18px; + margin-left: 4px; + position: relative; + top: 2px; } -body.ttrss_main #feeds-holder #feedTree .counterNode { +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow .counterNode { + order: 2; font-weight: bold; display: none; font-size: 9px; @@ -578,111 +542,215 @@ body.ttrss_main #feeds-holder #feedTree .counterNode { color: white; background: #2a89bc; border-radius: 4px; - vertical-align: middle; - float: right; - position: relative; line-height: 14px; margin-right: 8px; - margin-top: 2px; min-width: 23px; height: 14px; + flex-shrink: 0; +} +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow .counterNode.aux, +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow .counterNode.marked { + background: #f5f5f5; + color: #6f6f6f; + border-color: #dcdcdc; +} +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow .counterNode.marked { + border-color: #257aa7; + background: #ffffff; +} +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow[data-feed-id="-3"][data-is-cat="false"] .counterNode.unread { + background-color: #3ea447; + border-color: #307f37; } -body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .loadingExpando { - left: -3px; - height: 22px; +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow .dijitTreeContent { + display: flex; + align-items: center; + flex-grow: 2; + min-width: 0; +} +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow .dijitTreeLabel { + cursor: pointer; + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; position: relative; - top: -3px; } -body.ttrss_main #feeds-holder #feedTree .dijitTreeRow .dijitTreeLabel.Unread { +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow .dijitTreeLabel.Unread { font-weight: bold; } -body.ttrss_main #feeds-holder #feedTree .dijitTreeRow.Error .dijitTreeLabel { +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow.Error .dijitTreeLabel { color: red; } -body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow { - border: 1px solid transparent; -} -body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRowSelected { +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow.dijitTreeRowSelected { box-shadow: -1px 0px 2px -1px rgba(0, 0, 0, 0.1); border-color: #ddd transparent; background: white; - color: #333; + color: #555; } -body.ttrss_main #feeds-holder #feedTree .dijitIcon.feed-icon { +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow .dijitIcon.feed-icon { margin-right: 2px; } -body.ttrss_main #feeds-holder #feedTree i.icon.icon-inbox { - color: #555; +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow i.icon.icon-inbox { + color: #888888; } -body.ttrss_main #feeds-holder #feedTree i.icon.icon-archive { - color: #c77b2e; +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow i.icon.icon-archive { + color: #cf7800; } -body.ttrss_main #feeds-holder #feedTree i.icon.icon-star { +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow i.icon.icon-star { position: relative; color: #ffc069; font-size: 21px; left: -2px; } -body.ttrss_main #feeds-holder #feedTree i.icon.icon-rss_feed { +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow i.icon.icon-rss_feed { color: #ff7c4b; } -body.ttrss_main #feeds-holder #feedTree i.icon.icon-whatshot { +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow i.icon.icon-whatshot { color: #69C671; } -body.ttrss_main #feeds-holder #feedTree i.icon.icon-restore { - position: relative; - top: -1px; +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow i.icon.icon-restore { font-weight: bold; color: #257aa7; } -body.ttrss_main #headlines-wrap-inner { - padding: 0px; - margin: 0px; - border-width: 0px; +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(600px, 1fr)); + padding: 8px; + grid-gap: 8px; + background-color: #f5f5f5; +} +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] > * { + /* 2 = because #headlines-spacer is the actual last child + only if odd to deal with 1) single article and 2) not break any previous rows; + 1 = spacer; + + this is outside of .cdm selector because of #headlines-spacer etc + + .grid-span-row is manually expanded RROWs + */ +} +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] > *.grid-span-row, +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] > *:nth-last-child(2):nth-child(odd), +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] > *:nth-last-child(1) { + grid-column: 1 / -1; +} +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] .cdm.expanded .header, +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] .cdm.expanded .content { + background: white; + border: 1px solid #ddd; + overflow: hidden; +} +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] .cdm.expanded .content { + border-top-width: 0; + padding: 0 4px 4px 4px; } -body.ttrss_main #headlines-frame[is-vfeed="0"] .header .feed { +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] .cdm.expanded .header[data-is-stuck] { + top: -8px; + border-bottom-width: 1px; +} +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] .cdm.expanded .header { + border-bottom-width: 0; + padding: 4px; +} +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] .cdm.expanded .header .icon-grid-span { + display: inline; +} +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] .cdm.expanded .header .feed { display: none; } +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] .cdm.expanded .footer { + border: 0; + padding: 4px; +} +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] .cdm.expanded .footer .left, +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] .cdm.expanded .footer .right { + white-space: nowrap; +} +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] .cdm.expanded .footer .left { + overflow: hidden; + text-overflow: ellipsis; +} +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] .cdm.expanded .content-inner a, +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] .cdm.expanded .intermediate a { + word-break: break-all; +} body.ttrss_main #headlines-frame { - padding: 0px; - border: 0px #ddd; - margin-top: 0px; + padding: 0; + border: 0; + margin-top: 0; -webkit-overflow-scrolling: touch; -webkit-transform: translateZ(0); + transform: translateZ(0); -webkit-backface-visibility: hidden; + backface-visibility: hidden; +} +body.ttrss_main #headlines-frame[data-is-vfeed="false"] .header .feed, +body.ttrss_main #headlines-frame[data-is-vfeed="false"] .hl .feed, +body.ttrss_main #headlines-frame[data-is-vfeed="false"] .header .icon-feed, +body.ttrss_main #headlines-frame[data-is-vfeed="false"] .hl .icon-feed { + display: none; +} +body.ttrss_main #headlines-frame[data-auto-catchup="true"] #headlines-spacer { + height: 100vh; +} +body.ttrss_main #headlines-frame .dijitCheckBox { + margin-right: 4px; +} +body.ttrss_main #headlines-frame[data-is-wide-screen="true"] .title { + overflow: visible; + white-space: normal; +} +body.ttrss_main #headlines-frame[data-is-wide-screen="true"] .hl .feed { + display: none; +} +body.ttrss_main #headlines-frame #headlines-spacer { + margin-left: 1px; + text-align: center; + color: #555; + font-size: 11px; +} +body.ttrss_main #headlines-frame #headlines-spacer a, +body.ttrss_main #headlines-frame #headlines-spacer span { + color: #555; + padding: 10px; + display: block; } -body.ttrss_main #headlines-frame div.feed-title { +body.ttrss_main #headlines-frame #headlines-spacer a:hover { + color: #257aa7; +} +body.ttrss_main #headlines-frame .feed-title { border: 0px solid #257aa7; border-bottom-width: 1px; padding: 5px 8px; } -body.ttrss_main #headlines-frame div.feed-title a.title { +body.ttrss_main #headlines-frame .feed-title a.title { color: #555; font-weight: bold; } -body.ttrss_main #headlines-frame div.feed-title a { +body.ttrss_main #headlines-frame .feed-title a { color: #555; } -body.ttrss_main #headlines-frame div.feed-title a:hover { +body.ttrss_main #headlines-frame .feed-title a:hover { color: #257aa7; } body.ttrss_main #headlines-frame span.hlMenuAttach { -webkit-touch-callout: none; -webkit-user-select: none; + user-select: none; } body.ttrss_main #toolbar-frame_splitter { display: none; } body.ttrss_main #toolbar-frame { - padding: 0px; - margin: 0px; - border-width: 0px; + padding: 0; + margin: 0; + border: 0; white-space: nowrap; - font-size: 12px; + font-size: 13px; } body.ttrss_main #toolbar-frame #toolbar { background: white; - border: 0px solid #ddd; + border: 0 solid #ddd; border-bottom-width: 1px; padding-left: 4px; height: 32px; @@ -690,13 +758,13 @@ body.ttrss_main #toolbar-frame #toolbar { flex-direction: row; flex-wrap: nowrap; color: #555; - font-size: 12px; + font-size: 13px; align-items: center; } body.ttrss_main #toolbar-frame #toolbar .dijitSelect, body.ttrss_main #toolbar-frame #toolbar .dijitDropDownButton .dijitButtonNode, body.ttrss_main #toolbar-frame #toolbar .dijitComboButton .dijitButtonNode { - border: 0px; + border: 0; } body.ttrss_main #toolbar-frame #toolbar i.net-alert, body.ttrss_main #toolbar-frame #toolbar .left i.icon-error { @@ -710,7 +778,7 @@ body.ttrss_main #toolbar-frame #toolbar i { margin: 0 4px; } body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines { - font-size: 12px; + font-size: 13px; background: transparent; padding-right: 4px; flex-grow: 2; @@ -725,6 +793,12 @@ body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines .left .feed_title, body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines .left .cancel_search { margin-left: 4px; } +@media (max-width: 768px) { + body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines .left .feed_title, + body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines .left i.icon-syndicate { + display: none; + } +} body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines .right { display: flex; align-items: center; @@ -739,20 +813,26 @@ body.ttrss_main #toolbar-frame #toolbar #selected_prompt { margin-right: 4px; color: #257aa7; } -@media (max-width: 992px) { +@media (max-width: 768px) { body.ttrss_main #toolbar-frame #toolbar #selected_prompt { display: none; } } +@media (max-width: 576px) { + body.ttrss_main #toolbar-frame #toolbar .select-articles-dropdown, + body.ttrss_main #toolbar-frame #toolbar .catchup-button { + display: none; + } +} body.ttrss_main #header { - border-width: 0px; text-align: right; color: #555; - padding: 5px 5px 0px 0px; - margin: 0px; + padding: 5px 5px 0 0px; position: absolute; - right: 0px; - top: 0px; + border: 0; + margin: 0; + right: 0; + top: 0; z-index: 5; } body.ttrss_main #header i.net-alert, @@ -771,9 +851,8 @@ body.ttrss_main #header i { margin: 0 4px; } body.ttrss_main #content-insert { - padding: 0px; - border-color: #ddd; - border-width: 0px; + padding: 0; + border: 0; line-height: 1.5; overflow: auto; -webkit-overflow-scrolling: touch; @@ -786,59 +865,11 @@ body.ttrss_main img.icon { vertical-align: middle; display: inline-block; } -body.ttrss_main .player { - display: inline-block; - color: #555; - font-size: 11px; - font-family: sans-serif; - border: 1px solid #555; - padding: 0px 4px 0px 4px; - margin: 0px 2px 0px 2px; - width: 50px; - text-align: center; - background: white; -} -body.ttrss_main .player.playing { - color: #00c000; - border-color: #00c000; -} -body.ttrss_main .player:hover { - background: #f5f5f5; - cursor: pointer; -} -body.ttrss_main #headlines-frame.auto_catchup #headlines-spacer { - height: 100%; -} -body.ttrss_main #headlines-spacer { - margin-left: 1px; - text-align: center; - color: #555; - font-size: 11px; - font-style: italic; -} -body.ttrss_main #headlines-spacer a, -body.ttrss_main #headlines-spacer span { - color: #555; - padding: 10px; - display: block; -} -body.ttrss_main #headlines-spacer a:hover { - color: #257aa7; -} body.ttrss_main ul#filterDlg_Matches, body.ttrss_main ul#filterDlg_Actions { list-style-type: none; margin: 0; padding: 0; - /*max-height : 100px; - overflow : auto; - border-style : solid; - border-color : @border-default; - border-width : 1px 1px 1px 1px; - background-color : @default-bg; - margin : 0px 0px 5px 0px; - padding : 4px; - min-height : 16px;*/ } body.ttrss_main ul#filterDlg_Matches li, body.ttrss_main ul#filterDlg_Actions li { @@ -874,9 +905,6 @@ body.ttrss_main span.highlight { background-color: #ffff00; color: #cc90cc; } -body.ttrss_main #headlines-frame .dijitCheckBox { - margin-right: 4px; -} body.ttrss_main #feedEditDlg img.feedIcon { border: 1px solid #ccc; padding: 5px; @@ -934,6 +962,11 @@ body.ttrss_main:not([view-mode="marked"])[hide-read-feeds="true"][hide-read-show body.ttrss_main:not([view-mode="marked"])[hide-read-feeds="true"][hide-read-shows-special="false"] #feeds-holder #feedTree .dijitTreeRow:not(.Unread):not(.AlwaysVisible) { display: none; } +body.ttrss_main { + /*.score-neutral i.icon-score { + opacity : 0.5; + }*/ +} body.ttrss_main #toolbar-headlines i.icon-syndicate { color: #ff7c4b; margin-right: 8px; @@ -970,10 +1003,15 @@ body.ttrss_main .score-high i.icon-score { body.ttrss_main .score-low i.icon-score { color: #500; } -body.ttrss_main .score-neutral i.icon-score { - opacity: 0.5; +body.ttrss_main i.icon-score, +body.ttrss_main i.icon-grid-span { + cursor: pointer; + color: #777; } -body.ttrss_main i.icon-score { +body.ttrss_main .icon-grid-span { + display: none; +} +body.ttrss_main .icon-feed { cursor: pointer; } body.ttrss_main .panel { @@ -1004,20 +1042,62 @@ body.ttrss_main ul.list-unstyled { body.ttrss_main .text-center { text-align: center; } -body.ttrss_main #prefFilterTestResultList .preview { +body.ttrss_main .text-right { + text-align: right; +} +body.ttrss_main .text-left { + text-align: left; +} +body.ttrss_main .dijitDialog .filter-results-list .preview { margin: 8px; } -body.ttrss_main #prefFilterTestResultList .title { +body.ttrss_main .dijitDialog .filter-results-list .title { font-weight: bold; } -body.ttrss_main #prefFilterTestResultList .feed { - color: #257aa7; +body.ttrss_main .dijitDialog #feed_add_spinner { + position: relative; + top: 5px; + width: 18px; + height: 18px; +} +body.ttrss_main .icon-three-dots { + width: 18px; + height: 18px; + vertical-align: middle; +} +body.ttrss_main, +body.ttrss_utility { + /*div.autocomplete { + position : absolute; + width : 250px; + background-color : @default-bg; + border :1px solid @border-default; + margin : 0px; + padding : 0px; + + ul { + list-style-type : none; + margin : 0px; + padding : 0px; + } + + ul li.selected { + background-color : darken(@default-bg, 10%); + } + + ul li { + list-style-type : none; + display : block; + margin : 0; + padding : 2px; + cursor : pointer; + } + }*/ } body.ttrss_main .alert, body.ttrss_utility .alert { padding: 8px 35px 8px 14px; margin-bottom: 10px; - /* text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); */ background-color: #fcf8e3; border: 1px solid #fbeed5; border-radius: 4px; @@ -1058,6 +1138,16 @@ body.ttrss_main .text-warning, body.ttrss_utility .text-warning { color: #a47e3c; } +body.ttrss_main .text-muted, +body.ttrss_utility .text-muted { + color: #555; +} +body.ttrss_main .text-small, +body.ttrss_utility .text-small, +body.ttrss_main .small, +body.ttrss_utility .small { + font-size: 11px; +} body.ttrss_main .alert, body.ttrss_utility .alert, body.ttrss_main .alert h4, @@ -1107,41 +1197,6 @@ body.ttrss_utility hr { border: 0px solid #ccc; border-bottom-width: 1px; } -body.ttrss_main .text-muted, -body.ttrss_utility .text-muted { - color: #555; -} -body.ttrss_main .small, -body.ttrss_utility .small { - font-size: 11px; -} -body.ttrss_main div.autocomplete, -body.ttrss_utility div.autocomplete { - position: absolute; - width: 250px; - background-color: white; - border: 1px solid #ddd; - margin: 0px; - padding: 0px; -} -body.ttrss_main div.autocomplete ul, -body.ttrss_utility div.autocomplete ul { - list-style-type: none; - margin: 0px; - padding: 0px; -} -body.ttrss_main div.autocomplete ul li.selected, -body.ttrss_utility div.autocomplete ul li.selected { - background-color: #e6e6e6; -} -body.ttrss_main div.autocomplete ul li, -body.ttrss_utility div.autocomplete ul li { - list-style-type: none; - display: block; - margin: 0; - padding: 2px; - cursor: pointer; -} ::selection { background: #257aa7; color: white; @@ -1155,16 +1210,13 @@ body.ttrss_utility div.autocomplete ul li { ::-webkit-scrollbar-track { background-color: #eee; } -video::-webkit-media-controls-overlay-play-button { - display: none; -} -.cdm i.material-icons { - color: #777; -} -.cdm .header { - position: sticky; - top: 0; - z-index: 3; +/*video::-webkit-media-controls-overlay-play-button { + display: none; +}*/ +.cdm { + /*i.material-icons { + color : @color-icon; + }*/ } .cdm .header, .cdm .footer { @@ -1175,13 +1227,16 @@ video::-webkit-media-controls-overlay-play-button { .cdm .header img, .cdm .footer img, .cdm .footer i.material-icons { - margin: 0px 4px; + margin: 0 4px; vertical-align: middle; } .cdm .header-sticky-guard { height: 0; } .cdm .header { + position: sticky; + top: 0; + z-index: 3; align-items: center; } .cdm .header > * { @@ -1204,7 +1259,7 @@ video::-webkit-media-controls-overlay-play-button { .cdm .header .titleWrap { flex-grow: 2; } -.cdm .header span.updated { +.cdm .header .updated { color: #555; font-weight: normal; font-size: 11px; @@ -1213,6 +1268,30 @@ video::-webkit-media-controls-overlay-play-button { .cdm .header input { margin: 0px 4px; } +.cdm .header .feed { + float: right; + font-weight: normal; + font-style: italic; +} +.cdm .header .feed a { + border-radius: 4px; + display: inline-block; + padding: 1px 4px 1px 4px; +} +.cdm .header .feed, +.cdm .header .feed a { + vertical-align: middle; + color: #555; + font-weight: normal; + font-style: italic; + font-size: 11px; +} +.cdm .header .author { + white-space: nowrap; + color: #555; + font-size: 11px; + font-weight: normal; +} .cdm .footer { height: 30px; padding-left: 5px; @@ -1221,30 +1300,37 @@ video::-webkit-media-controls-overlay-play-button { clear: both; align-items: center; } +.cdm .footer i.material-icons { + color: #777; +} .cdm .footer .left { flex-grow: 2; } .cdm .intermediate { margin-top: 10px; margin-left: 10px; + line-height: 1.5; } .cdm .content-inner { margin: 10px; line-height: 1.5; font-size: 16px; } +.cdm .intermediate iframe, +.cdm .content-inner iframe { + max-width: 98%; + width: auto; + height: auto; +} .cdm .intermediate img, .cdm .intermediate video, .cdm .content-inner img, .cdm .content-inner video { border-width: 0px; max-width: 98%; + width: auto; height: auto; } -.cdm.expanded { - /*margin-top : 4px; - margin-bottom : 4px;*/ -} .cdm.expanded .collapse, .cdm.expanded .excerpt { display: none; @@ -1257,72 +1343,54 @@ video::-webkit-media-controls-overlay-play-button { border-bottom-width: 1px; } .cdm.expanded > hr { - margin-top: 0px; - margin-bottom: 0px; + margin-top: 0; + margin-bottom: 0; } -div.cdm.expanded div.header a.title { +.cdm.expanded .header a.title { font-size: 16px; color: #999; font-weight: 600; transition: color 0.2s, background 0.2s; text-rendering: optimizelegibility; - font-family: "Segoe WP Semibold", "Segoe UI Semibold", "Segoe UI Web Semibold", "Segoe UI", Ubuntu, "Helvetica Neue", Helvetica, Arial, sans-serif; + font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif; } -div.cdm.expanded.active { - background: white; +.cdm.expanded.active .content, +.cdm.expanded.Unread .content { + color: black; } -div.cdm.expanded.active div.header a.title { +.cdm.expanded.active .header .title { color: #257aa7; } -div.cdm.expanded.Unread div.header a.title { +.cdm.expanded.Unread .header .title { color: black; } -div.cdm.expanded div.content { +.cdm.expanded .content { color: #555; } -div.cdm.expanded.Unread div.content { - color: black; -} -div.cdm.active div.content { - color: black; -} -div.cdm.vgrlf .feed { +.cdm.vgrlf .feed { display: none; } -.cdm div.feed-title { +.cdm .feed-title { border: 0px solid #257aa7; border-bottom-width: 1px; padding: 5px 3px 5px 5px; } -.cdm div.feed-title a.title { +.cdm .feed-title a.title { color: #555; font-weight: bold; } -.cdm div.feed-title a { +.cdm .feed-title a { color: #555; } -.cdm div.feed-title a:hover { +.cdm .feed-title a:hover { color: #257aa7; } -.cdm div.header span.feed { - float: right; - font-weight: normal; - font-style: italic; -} -.cdm div.header div.feed, -.cdm div.header div.feed a { - vertical-align: middle; - color: #555; - font-weight: normal; - font-style: italic; - font-size: 11px; -} -.cdm div.content-inner div.embed-responsive { +.cdm .content-inner .embed-responsive { overflow: hidden; padding-bottom: 56.25%; position: relative; } -.cdm div.content-inner div.embed-responsive iframe { +.cdm .content-inner .embed-responsive iframe { border: 0; bottom: 0; height: 100%; @@ -1331,17 +1399,6 @@ div.cdm.vgrlf .feed { top: 0; width: 100%; } -.cdm div.header span.author { - white-space: nowrap; - color: #555; - font-size: 11px; - font-weight: normal; -} -.cdm .feed a { - border-radius: 4px; - display: inline-block; - padding: 1px 4px 1px 4px; -} .cdm.expandable { background-color: #f5f5f5; border: 0px solid #ddd; @@ -1379,43 +1436,43 @@ div.cdm.vgrlf .feed { .cdm.expandable.active { background: white ! important; } -div.cdm.expandable.active div.header span.titleWrap { +.cdm.expandable.active div.header span.titleWrap { white-space: normal; } -div.cdm.expandable div.header a.title { +.cdm.expandable .header a.title { font-weight: 600; color: #555; font-size: 14px; transition: color 0.2s, background 0.2s; text-rendering: optimizelegibility; - font-family: "Segoe WP Semibold", "Segoe UI Semibold", "Segoe UI Web Semibold", "Segoe UI", Ubuntu, "Helvetica Neue", Helvetica, Arial, sans-serif; + font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif; } -div.cdm.expandable.Unread div.header a.title { +.cdm.expandable.Unread div.header a.title { color: black; } -div.cdm.expandable.active .collapse i.material-icons { +.cdm.expandable.active .collapse i.material-icons { color: #257aa7; cursor: pointer; } -div.cdm.expandable.active .excerpt { +.cdm.expandable.active .excerpt { display: none; } -div.cdm.expandable.active div.header a.title { +.cdm.expandable.active div.header a.title { color: #257aa7; font-size: 16px; font-weight: 600; text-rendering: optimizelegibility; - font-family: "Segoe WP Semibold", "Segoe UI Semibold", "Segoe UI Web Semibold", "Segoe UI", Ubuntu, "Helvetica Neue", Helvetica, Arial, sans-serif; + font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif; } -div.cdm.expandable:not(.active) { +.cdm.expandable:not(.active) { cursor: pointer; } -div.cdm.expandable:not(.active) .content, -div.cdm.expandable:not(.active) .collapse { +.cdm.expandable:not(.active) .content, +.cdm.expandable:not(.active) .collapse { display: none; } -div.cdm.expandable.active .header[stuck], -div.cdm.expanded .header[stuck] { +.cdm.expandable.active .header[data-is-stuck], +.cdm.expanded .header[data-is-stuck] { box-shadow: 0 1px 1px -1px rgba(0, 0, 0, 0.1); border: 0 solid #ddd; border-bottom-width: 1px; @@ -1432,7 +1489,7 @@ body.ttrss_prefs h1, body.ttrss_prefs h2, body.ttrss_prefs h3, body.ttrss_prefs h4 { - font-family: "Segoe WP Semibold", "Segoe UI Semibold", "Segoe UI Web Semibold", "Segoe UI", Ubuntu, "Helvetica Neue", Helvetica, Arial, sans-serif; + font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif; font-weight: 600; color: #555; } @@ -1503,14 +1560,6 @@ body.ttrss_prefs fieldset.prefs .help-text { body.ttrss_prefs fieldset.prefs .help-text-bottom { margin-top: 10px; } -body.ttrss_prefs fieldset.plugin label.description { - width: 550px; - margin-right: 150px; - display: inline-block; -} -body.ttrss_prefs fieldset.plugin label.description .dijitCheckBox { - margin-right: 10px; -} body.ttrss_prefs table th { text-align: left; } @@ -1538,17 +1587,26 @@ body.ttrss_prefs ul.prefs-plugin-list li > * { body.ttrss_prefs ul.prefs-plugin-list li label.checkbox { display: flex; align-items: center; - min-width: 300px; cursor: pointer; } body.ttrss_prefs ul.prefs-plugin-list li label.checkbox.system { cursor: auto; } body.ttrss_prefs ul.prefs-plugin-list li label.checkbox .name { + min-width: 300px; flex-grow: 2; display: inline-block; text-align: right; font-weight: bold; + margin-right: 20px; +} +@media (max-width: 992px) { + body.ttrss_prefs ul.prefs-plugin-list li label.checkbox .name { + min-width: 200px ! important; + } + body.ttrss_prefs ul.prefs-plugin-list li .version { + display: none; + } } body.ttrss_prefs ul.prefs-plugin-list li .actions { flex-grow: 2; @@ -1602,6 +1660,11 @@ body.ttrss_prefs .event-log tr .errno { body.ttrss_prefs .event-log tr .errstr { word-break: break-all; white-space: pre-wrap; + width: 30%; +} +body.ttrss_prefs .event-log tr .filename { + word-break: break-all; + width: 20%; } body.ttrss_prefs .event-log tr .filename, body.ttrss_prefs .event-log tr .login, @@ -1665,7 +1728,7 @@ body.ttrss_utility.sanity_failed { } body.ttrss_utility { background: #f5f5f5; - color: black; + color: #555; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 14px; margin: 4em; @@ -1683,7 +1746,7 @@ body.ttrss_utility .content h2, body.ttrss_utility .content h3, body.ttrss_utility .content h4 { color: #257aa7; - font-family: "Segoe WP Semibold", "Segoe UI Semibold", "Segoe UI Web Semibold", "Segoe UI", Ubuntu, "Helvetica Neue", Helvetica, Arial, sans-serif; + font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif; } body.ttrss_utility .content h2 { font-size: 18px; @@ -1702,7 +1765,7 @@ body.ttrss_utility a:focus { } body.ttrss_utility h1 { color: gray; - font-family: "Segoe WP Semibold", "Segoe UI Semibold", "Segoe UI Web Semibold", "Segoe UI", Ubuntu, "Helvetica Neue", Helvetica, Arial, sans-serif; + font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 18px; margin: 10px 0 0 0; } @@ -1758,12 +1821,16 @@ body.ttrss_utility.share_popup .content { border-width: 0; box-shadow: none; } +.flat { + /*#feedTree { + .dijitTreeContent .dijitInline { + vertical-align : baseline; + } + }*/ +} .flat li { padding: 2px; } -.flat #feedTree .dijitTreeContent .dijitInline { - vertical-align: baseline; -} .flat .dijitButton i.material-icons { position: relative; top: -1px; @@ -1787,7 +1854,8 @@ body.ttrss_utility.share_popup .content { } .flat .dijitToolbar { font-size: 13px; - padding: 0px; + font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif; + padding: 0; } .flat .dijitToolbar .dijitTextBox .dijitInputContainer .dijitInputInner { line-height: 10px; @@ -1810,12 +1878,13 @@ body.ttrss_utility.share_popup .content { .flat .dijitMenu .dijitMenuItem .dijitMenuItemLabel { padding: 4px 8px; font-size: 13px; + font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif; } .flat .dijitMenu .dijitMenuItem.dijitDisabled:not(.dijitMenuItemSelected) .dijitMenuItemLabel { color: #2e99d1; } .flat .dijitMenu .dijitMenuItem td { - padding: 0px; + padding: 0; } .flat .dijitCheckBox { margin: 1px; @@ -1825,10 +1894,33 @@ body.ttrss_utility.share_popup .content { content: "\f00c"; color: white; } +.flat .dijitTab, +.flat .dijitAccordionTitle { + font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif; +} .flat .dijitTab i.material-icons, .flat .dijitAccordionInnerContainer:not(.dijitSelected) i.material-icons { color: #257aa7; } +.flat .dijitTree { + /*.dijitTreeRow .dijitTreeExpando { + position : relative; + top : -2px; + }*/ +} +.flat .dijitTree .dijitTreeRowFlex { + display: flex; + flex-direction: row; + align-items: center; +} +.flat .dijitTree .dijitTreeRowFlex .dijitTreeIcon { + font-size: inherit; + height: auto; +} +.flat .dijitTree .dijitTreeRowFlex .dijitTreeExpando { + height: auto; + line-height: 0; +} .flat .dijitTree .dijitFolderClosed, .flat .dijitTree .dijitFolderOpened { display: none; @@ -1842,10 +1934,6 @@ body.ttrss_utility.share_popup .content { .flat .dijitTree .dijitTreeNode .dijitTreeRow.dijitTreeRowSelected { color: white; } -.flat .dijitTree .dijitTreeRow .dijitTreeExpando { - position: relative; - top: -2px; -} .flat .dijitTree .labelParam { float: right; margin-right: 16px; @@ -1874,6 +1962,7 @@ body.ttrss_utility.share_popup .content { .flat .dijitTree .dijitTreeRow { overflow: hidden; -moz-user-select: none; + user-select: none; text-overflow: ellipsis; } .flat label.dijitButton { @@ -1948,11 +2037,11 @@ body.ttrss_zoom div.post .attachments { display: none; } body.ttrss_zoom div.post div.header { - padding-bottom: 10px; - border: 0px solid #ddd; + padding-bottom: 8px; + border: 0 solid #ddd; border-bottom-width: 1px; background: white; - font-size: 12px; + font-size: 13px; color: #555; } body.ttrss_zoom div.post div.header .row { @@ -1962,32 +2051,36 @@ body.ttrss_zoom div.post div.header .row { align-items: center; justify-content: space-between; } -body.ttrss_zoom div.post div.content { +body.ttrss_zoom div.post div.header .row h1 { + margin-top: 0; + margin-bottom: 8px; +} +body.ttrss_zoom div.post .content { font-size: 15px; line-height: 1.5; border-width: 0; padding: 0; + padding-top: 8px; } -body.ttrss_zoom div.post div.content img, -body.ttrss_zoom div.post div.content video { +body.ttrss_zoom div.post .content img, +body.ttrss_zoom div.post .content video { max-width: 760px; height: auto; } -body.ttrss_zoom div.post div.content blockquote { +body.ttrss_zoom div.post .content blockquote { margin: 5px 0px 5px 0px; color: #555; - padding-left: 10px; + padding-left: 8px; border: 0px solid #ddd; border-left-width: 4px; } -body.ttrss_zoom div.post div.content code { +body.ttrss_zoom div.post .content code { color: #009900; font-family: monospace; font-size: 12px; } -body.ttrss_zoom div.post div.content pre { - margin: 5px 0px 5px 0px; - padding: 10px; +body.ttrss_zoom div.post .content pre { + padding: 8px; color: #555; font-family: monospace; font-size: 12px; diff --git a/themes/light/cdm.less b/themes/light/cdm.less index 3a5b602f2..41d789a09 100644 --- a/themes/light/cdm.less +++ b/themes/light/cdm.less @@ -1,13 +1,7 @@ .cdm { - i.material-icons { + /*i.material-icons { color : @color-icon; - } - - .header { - position: sticky; - top : 0; - z-index: 3; - } + }*/ .header, .footer { display : flex; @@ -17,7 +11,7 @@ .header img, .footer img, .footer i.material-icons { - margin : 0px 4px; + margin : 0 4px; vertical-align: middle; } @@ -26,6 +20,9 @@ } .header { + position: sticky; + top : 0; + z-index: 3; align-items : center; > * { @@ -50,16 +47,43 @@ flex-grow : 2; } - span.updated { + .updated { color : @default-text; font-weight : normal; - font-size : 11px; + font-size : @font-size-small; white-space : nowrap; } input { margin : 0px 4px; } + + .feed { + float: right; + font-weight: normal; + font-style: italic; + + a { + border-radius : 4px; + display : inline-block; + padding : 1px 4px 1px 4px; + } + } + + .feed, .feed a { + vertical-align: middle; + color: @default-text; + font-weight: normal; + font-style: italic; + font-size: @font-size-small; + } + + .author { + white-space : nowrap; + color : @default-text; + font-size : @font-size-small; + font-weight : normal; + } } .footer { @@ -70,6 +94,10 @@ clear : both; align-items : center; + i.material-icons { + color : @color-icon; + } + .left { flex-grow : 2; } @@ -78,6 +106,7 @@ .intermediate { margin-top : 10px; margin-left : 10px; + line-height : 1.5; } .content-inner { @@ -86,112 +115,100 @@ font-size : 16px; } + .intermediate iframe, + .content-inner iframe { + max-width : 98%; + width : auto; + height : auto; + } + .intermediate img, .intermediate video, .content-inner img, .content-inner video { border-width : 0px; max-width : 98%; + width : auto; height : auto; } -} - -.cdm.expanded { - /*margin-top : 4px; - margin-bottom : 4px;*/ - - .collapse, .excerpt { - display : none; - } - .titleWrap { - white-space : normal; - } - - .footer { - border: 0px solid @border-default; - border-bottom-width: 1px; - } + &.expanded { - > hr { - margin-top : 0px; - margin-bottom : 0px; - } + .collapse, .excerpt { + display : none; + } -} + .titleWrap { + white-space : normal; + } -div.cdm.expanded div.header a.title { - font-size : 16px; - color : #999; - font-weight : 600; - transition : color 0.2s, background 0.2s; - text-rendering: optimizelegibility; - font-family : @fonts-ui-bold; -} + .footer { + border: 0px solid @border-default; + border-bottom-width: 1px; + } -div.cdm.expanded.active { - background : white; -} + > hr { + margin-top : 0; + margin-bottom : 0; + } -div.cdm.expanded.active div.header a.title { - color : @color-link; -} + .header a.title { + font-size : 16px; + color : #999; + font-weight : 600; + transition : color 0.2s, background 0.2s; + text-rendering: optimizelegibility; + font-family : @fonts-ui; + } -div.cdm.expanded.Unread div.header a.title { - color : black; -} + &.active, + &.Unread { + .content { + color : black; + } + } -div.cdm.expanded div.content { - color : @default-text; -} + &.active { + .header .title { + color : @color-link; + } + } -div.cdm.expanded.Unread div.content { - color : black; -} + &.Unread { + .header .title { + color : black; + } + } -div.cdm.active div.content { - color : black; -} + .content { + color : @default-text; + } + } -div.cdm.vgrlf .feed { - display : none; -} + &.vgrlf .feed { + display : none; + } -.cdm { - div.feed-title { + .feed-title { border: 0px solid @color-link; border-bottom-width: 1px; padding: 5px 3px 5px 5px; - } - div.feed-title a.title { - color: @default-text; - font-weight: bold; - } - - div.feed-title a { - color: @default-text; - } - - div.feed-title a:hover { - color: @color-link; - } + a.title { + color: @default-text; + font-weight: bold; + } - div.header span.feed { - float: right; - font-weight: normal; - font-style: italic; - } + a { + color: @default-text; + } - div.header div.feed, div.header div.feed a { - vertical-align: middle; - color: @default-text; - font-weight: normal; - font-style: italic; - font-size: 11px; + a:hover { + color: @color-link; + } } - div.content-inner div.embed-responsive { + .content-inner .embed-responsive { overflow : hidden; padding-bottom : @embed-responsive-padding; position : relative; @@ -207,115 +224,99 @@ div.cdm.vgrlf .feed { } } - div.header span.author { - white-space : nowrap; - color : @default-text; - font-size : 11px; - font-weight : normal; - } - - .feed a { - border-radius : 4px; - display : inline-block; - padding : 1px 4px 1px 4px; - } -} - -.cdm.expandable { - background-color : @color-panel-bg; - border: 0px solid @border-default; - border-bottom-width: 1px; - - > hr { - display : none; - } + &.expandable { + background-color : @color-panel-bg; + border: 0px solid @border-default; + border-bottom-width: 1px; - div.header span.titleWrap { - white-space : nowrap; - text-overflow : ellipsis; - overflow : hidden; - } + > hr { + display : none; + } - .excerpt { - white-space : nowrap; - font-size : 11px; - color : #999; - font-weight : normal; - cursor : pointer; - } + div.header span.titleWrap { + white-space : nowrap; + text-overflow : ellipsis; + overflow : hidden; + } -} + .excerpt { + white-space : nowrap; + font-size : @font-size-small; + color : #999; + font-weight : normal; + cursor : pointer; + } -.cdm.expandable:not(.active) { - user-select : none; -} + &:not(.active) { + user-select : none; + } -.cdm.expandable.Unread { - background : white; -} + &.Unread { + background : white; + } -.cdm.expandable.Selected:not(.active) { - background : desaturate(@color-accent, 25%); + &.Selected:not(.active) { + background : desaturate(@color-accent, 25%); - a, - .header a.title, - span { - color : white; - } -} + a, + .header a.title, + span { + color : white; + } + } -.cdm.expandable.active { - background : white ! important; -} + &.active { + background : white ! important; + } -div.cdm.expandable.active div.header span.titleWrap { - white-space : normal; -} + &.active div.header span.titleWrap { + white-space : normal; + } -div.cdm.expandable div.header a.title { - font-weight : 600; - color : @default-text; - font-size : 14px; - transition : color 0.2s, background 0.2s; - text-rendering: optimizelegibility; - font-family : @fonts-ui-bold; -} + .header a.title { + font-weight : 600; + color : @default-text; + font-size : @font-size-content; + transition : color 0.2s, background 0.2s; + text-rendering: optimizelegibility; + font-family : @fonts-ui; + } -div.cdm.expandable.Unread div.header a.title { - color : black; -} + &.Unread div.header a.title { + color : black; + } -div.cdm.expandable.active { - .collapse i.material-icons { - color : @color-accent; - cursor : pointer; - } + &.active { + .collapse i.material-icons { + color : @color-accent; + cursor : pointer; + } - .excerpt { - display: none; - } + .excerpt { + display: none; + } - div.header a.title { - color: @color-link; - font-size: 16px; - font-weight: 600; - text-rendering: optimizelegibility; - font-family: @fonts-ui-bold; - } -} + div.header a.title { + color: @color-link; + font-size: 16px; + font-weight: 600; + text-rendering: optimizelegibility; + font-family: @fonts-ui; + } + } -div.cdm.expandable:not(.active) { - cursor : pointer; + &:not(.active) { + cursor : pointer; - .content, .collapse { - display : none; + .content, .collapse { + display : none; + } + } } -} -div.cdm { &.expandable.active, &.expanded { - .header[stuck] { + .header[data-is-stuck] { box-shadow : 0 1px 1px -1px rgba(0,0,0,0.1); border: 0 solid @border-default; border-bottom-width: 1px; diff --git a/themes/light/defines.less b/themes/light/defines.less index dd3f62251..08896b4f1 100644 --- a/themes/light/defines.less +++ b/themes/light/defines.less @@ -1,7 +1,6 @@ -@fonts-ui-bold: "Segoe WP Semibold", "Segoe UI Semibold", "Segoe UI Web Semibold", "Segoe UI", Ubuntu, "Helvetica Neue", Helvetica, Arial, sans-serif; -@fonts-ui: "Segoe UI", Ubuntu, "Helvetica Neue", Helvetica, Arial, sans-serif; +@fonts-ui: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif; -@default-fg: black; +@default-fg: #555; @default-bg: white; @color-accent: #257aa7; @color-accent-light: lighten(@color-accent, 50%); @@ -12,10 +11,22 @@ @color-panel-bg: #f5f5f5; @color-checked: #69C671; @border-default : #ddd; -@default-text: #555; +@default-text: @default-fg; @color-icon: #777; +@color-hl-icon: #ccc; @color-tooltip-fg: @color-panel-bg; @color-tooltip-bg: darken(@color-accent, 10%); +@cdm-grid-padding: 8px; +@cdm-grid-col-width: 600px; +@font-size-tiny: 9px; +@font-size-small: 11px; +@font-size-toolbar: 13px; +@font-size-content: 14px; + +@breakpoint-sm: 576px; +@breakpoint-md: 768px; +@breakpoint-lg: 992px; +@breakpoint-xl: 1200px; @embed-responsive-padding: 56.25%; // Use 56.25% for 16:9 aspect ratio, 75% for 4:3. diff --git a/themes/light/dijit_basic.less b/themes/light/dijit_basic.less index da1138986..f29461530 100644 --- a/themes/light/dijit_basic.less +++ b/themes/light/dijit_basic.less @@ -4,11 +4,11 @@ padding: 2px; } - #feedTree { + /*#feedTree { .dijitTreeContent .dijitInline { vertical-align : baseline; } - } + }*/ .dijitButton i.material-icons { position: relative; @@ -38,8 +38,9 @@ } .dijitToolbar { - font-size: 13px; - padding: 0px; + font-size: @font-size-toolbar; + font-family: @fonts-ui; + padding: 0; .dijitTextBox .dijitInputContainer { .dijitInputInner { @@ -67,27 +68,35 @@ border-color : darken(@color-checked, 10%); } - .dijitMenu .dijitMenuItem .dijitMenuItemLabel { - padding: 4px 8px; - font-size: 13px; - } + .dijitMenu { + .dijitMenuItem .dijitMenuItemLabel { + padding: 4px 8px; + font-size: @font-size-toolbar; + font-family: @fonts-ui; + } - .dijitMenu .dijitMenuItem.dijitDisabled:not(.dijitMenuItemSelected) .dijitMenuItemLabel { - color : lighten(@color-accent, 10%); - } + .dijitMenuItem.dijitDisabled:not(.dijitMenuItemSelected) .dijitMenuItemLabel { + color : lighten(@color-accent, 10%); + } - .dijitMenu .dijitMenuItem td { - padding: 0px; + .dijitMenuItem td { + padding: 0; + } } .dijitCheckBox { margin : 1px; + + &:before { + font-family: "flat-icon"; + content: "\f00c"; + color: white; + } } - .dijitCheckBox:before { - font-family: "flat-icon"; - content: "\f00c"; - color: white; + .dijitTab, + .dijitAccordionTitle { + font-family: @fonts-ui; } .dijitTab, @@ -98,6 +107,23 @@ } .dijitTree { + .dijitTreeRowFlex { + display : flex; + flex-direction : row; + align-items : center; + + // AAAAAA (pushes element down) + .dijitTreeIcon { + font-size : inherit; + height : auto; + } + + .dijitTreeExpando { + height : auto; + line-height : 0; + } + } + .dijitFolderClosed, .dijitFolderOpened { display : none; @@ -117,10 +143,10 @@ color : white; } - .dijitTreeRow .dijitTreeExpando { + /*.dijitTreeRow .dijitTreeExpando { position : relative; top : -2px; - } + }*/ .labelParam { float: right; @@ -157,6 +183,7 @@ .dijitTreeRow { overflow: hidden; -moz-user-select: none; + user-select: none; text-overflow: ellipsis; } @@ -169,10 +196,10 @@ cursor : pointer; position: relative; top : 1px; - } - label.dijitButton:hover { - background-color : @color-panel-bg; + &:hover { + background-color : @color-panel-bg; + } } .dijitTree { diff --git a/themes/light/prefs.less b/themes/light/prefs.less index 15345bcb4..27e88d601 100644 --- a/themes/light/prefs.less +++ b/themes/light/prefs.less @@ -1,10 +1,10 @@ body.ttrss_prefs { background-color : @color-panel-bg; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; - font-size: 14px; + font-size: @font-size-content; h1, h2, h3, h4 { - font-family : @fonts-ui-bold; + font-family : @fonts-ui; font-weight : 600; color : @default-text; } @@ -94,18 +94,6 @@ body.ttrss_prefs { } } - fieldset.plugin { - label.description { - width : 550px; - margin-right : 150px; - display : inline-block; - - .dijitCheckBox { - margin-right : 10px; - } - } - } - table { th { text-align : left; @@ -140,7 +128,6 @@ body.ttrss_prefs { label.checkbox { display : flex; align-items : center; - min-width : 300px; cursor : pointer; &.system { @@ -148,10 +135,22 @@ body.ttrss_prefs { } .name { + min-width : 300px; flex-grow: 2; display: inline-block; text-align: right; font-weight : bold; + margin-right : 20px; + } + } + + @media (max-width: @breakpoint-lg) { + label.checkbox .name { + min-width : 200px ! important; + } + + .version { + display : none; } } @@ -217,6 +216,12 @@ body.ttrss_prefs { .errstr { word-break : break-all; white-space : pre-wrap; + width : 30%; + } + + .filename { + word-break: break-all; + width : 20%; } .filename, .login, .timestamp { diff --git a/themes/light/tt-rss.less b/themes/light/tt-rss.less index 36b73b4f4..088a3c7ff 100644 --- a/themes/light/tt-rss.less +++ b/themes/light/tt-rss.less @@ -2,21 +2,21 @@ body.ttrss_main { background : @default-bg; color : @default-fg; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; - font-size: 14px; + font-size: @font-size-content; overflow : hidden; :focus { outline: none; } - div.post { - padding : 0px; - font-size : 13px; + .post { + padding : 0; + font-size : @font-size-toolbar; - div.header { + .header { padding : 5px; - color : #909090; - border: 0px solid @border-default; + color : @default-text; + border: 0 solid @border-default; border-bottom-width: 1px; background: @color-panel-bg; @@ -51,7 +51,7 @@ body.ttrss_main { font-size : 15px; font-weight : 600; text-rendering: optimizelegibility; - font-family : @fonts-ui-bold; + font-family : @fonts-ui; } } @@ -160,7 +160,7 @@ body.ttrss_main { border-width : 1px; border-style : solid; position : fixed; - font-size : 14px; + font-size : @font-size-content; z-index : 99; display : flex; opacity: 0; @@ -192,6 +192,12 @@ body.ttrss_main { .notify.notify_progress { border-color : #d7c47a; background-color : #fff7d5; + + img { + width : 18px; + height : 18px; + filter : saturate(0); + } } .notify.notify_info { @@ -248,6 +254,10 @@ body.ttrss_main { display : flex; align-items : center; + img { + margin : 0 4px; + } + i.material-icons { margin-left : 2px; padding : 2px; @@ -257,61 +267,57 @@ body.ttrss_main { } } + .left { + input { + margin : 0 4px; + } + } + .right { + text-align : right; + i.material-icons { color : @color-icon; } } - div.title { + .title { cursor : pointer; flex-grow : 2; overflow : hidden; text-overflow : ellipsis; } - span.author { + .author { white-space : nowrap; color : @default-text; - font-size : 11px; + font-size : @font-size-small; font-weight : normal; } - div.right { - text-align : right; - } - - span.feed a { + .feed a { border-radius : 4px; display : inline-block; padding : 1px 4px; - font-size : 11px; + font-size : @font-size-small; font-style : italic; font-weight : normal; color : @default-text; - } - span.feed a:hover { - color : @color-accent; + &:hover { + color : @color-accent; + } } - span.updated { + .updated { color : @default-text; text-align : right; - font-size : 11px; + font-size : @font-size-small; padding-left : 10px; - } - - span.updated div { - display : inline-block; - } - - div.left input { - margin : 0px 4px; - } - div.left img, div.right img { - margin : 0px 4px; + div { + display : inline-block; + } } div.title a { @@ -324,44 +330,40 @@ body.ttrss_main { a.title.high, span.hl-content.high .preview { color : #00aa00; } - } - .hl.vgrlf .feed { - display : none; - } - - .hl.Unread { - background : white; - } + &.vgrlf .feed { + display : none; + } - .hl.Unread div.title a { - color : black; - } + &.Unread { + background : white; + } - .hl.active div.title a { - color : @color-accent; - /* text-shadow : 1px 1px 2px #fff; */ - } + &.Unread .title a { + color : black; + } - .hl.active { - background : @color-accent ! important; - } + &.active .title a { + color : @color-accent; + /* text-shadow : 1px 1px 2px #fff; */ + } - .hl.active, - .hl.Selected { - color : white; - background : desaturate(@color-accent, 25%); + &.active { + background : @color-accent ! important; + } - a, - .feed a, - .hl-content a.title, - span { + &.active, + &.Selected { color : white; - } - } + background : desaturate(@color-accent, 25%); - .hl.Grayed { - color : #909090; + a, + .feed a, + .hl-content a.title, + span { + color : white; + } + } } #content-insert blockquote, @@ -413,7 +415,7 @@ body.ttrss_main { display : inline-block; vertical-align: middle; background-color : #fff7d5; - font-size : 9px; + font-size : @font-size-tiny; color : @default-fg; font-weight : normal; margin-left : 2px; @@ -423,56 +425,19 @@ body.ttrss_main { i.marked-pic, i.pub-pic { cursor : pointer; - color : #ccc; - } - - div.errorExplained { - border : 1px solid @border-default; - margin : 5px 0px 5px 0px; - padding : 5px; + color : @color-hl-icon; } - ul.browseFeedList { - height : 300px; - width : 100%; - overflow : auto; - border-width : 0px 1px 1px 1px; - border-color : @border-default; - border-style : solid; - margin : 0px 0px 5px 0px; - background-color : white; - list-style-type : none; - padding : 0px; - - li { - display : flex; - align-items : center; - - > * { - margin : 2px; - } - } - } - - - .browseFeedList span.subscribers { - color : #808080; - } - - ul.compact { + /*ul.compact { list-style-type : none; - margin : 0px; - padding : 0px; + margin : 0; + padding : 0; li { - margin : 0px; - padding : 0px; + margin : 0; + padding : 0; } - } - - .noborder { - border-width : 0px; - } + }*/ #overlay { background : @default-bg; @@ -489,39 +454,22 @@ body.ttrss_main { margin : 1em; } - div.loadingPrompt { - padding : 1em; - text-align : center; - font-weight : bold; - } - div.whiteBox { margin-left : 1px; text-align : center; padding : 1em 1em 0px 1em; - font-size : 11px; + font-size : @font-size-small; border: 0px solid @border-default; border-bottom-width: 1px; } - div#headlines-frame.wide { - .title { - overflow: visible; - white-space: normal; - } - - .hl .feed { - display: none; - } - } - .dijitDialog { header, .dlgSec, .dlgSecHoriz { font-size : 16px; font-weight : 600; color : @default-text; - font-family: @fonts-ui-bold; + font-family: @fonts-ui; } section, @@ -564,14 +512,14 @@ body.ttrss_main { fieldset { border-width : 0px; padding : 5px 0px; - } - fieldset.narrow { - padding : 2px 0px; - } + &.narrow { + padding : 2px 0px; + } - fieldset.align-right { - text-align : right; + &.align-right { + text-align : right; + } } } @@ -600,7 +548,7 @@ body.ttrss_main { position : absolute; left : 5px; bottom : 5px; - font-size : 11px; + font-size : @font-size-small; color : @default-text; font-weight : bold; background-color : @default-bg; @@ -609,12 +557,6 @@ body.ttrss_main { z-index : 5; } - #feed_browser_spinner { - vertical-align : middle; - height : 18px; - width : 18px; - } - .exception-contents { h3 { color : red; @@ -622,19 +564,20 @@ body.ttrss_main { textarea { width : 99%; height : 200px; - font-size : 11px; + font-size : @font-size-small; } } + #headlines-wrap-inner, #content-wrap { - padding : 0px; - border-width : 0px; - margin : 0px; + padding : 0; + border : 0; + margin : 0; } #feeds-holder { - padding : 0px; - border: 0px solid @border-default; + padding : 0; + border: 0 solid @border-default; overflow : hidden; background : @color-panel-bg; box-shadow : inset -1px 0px 2px -1px rgba(0,0,0,0.1); @@ -646,139 +589,293 @@ body.ttrss_main { text-rendering: optimizelegibility; font-family : @fonts-ui; - .counterNode.aux, .counterNode.marked { - background : @color-panel-bg; - color : lighten(@default-text, 10%); - border-color : darken(@color-panel-bg, 10%); - } + .dijitTreeNode { + .dijitTreeRow { + border : 1px solid transparent; + + &.Is_Feed { + .loadingNode { + left : -2px; + height : 14px; + width : 14px; + position : relative; + } + } - .counterNode.marked { - border-color : @color-accent-marked; - background : lighten(@color-accent-marked, 60%); - } + &.Is_Cat { + .loadingNode { + height : 11px; + width : 18px; + margin-left : 4px; + position : relative; + top : 2px; + } + } - .counterNode { - font-weight : bold; - display : none; - font-size : 9px; - text-align : center; - border : 1px solid lighten(@color-accent, 5%);; - color : white; - background : lighten(@color-accent, 5%); - border-radius : 4px; - vertical-align : middle; - float : right; - position : relative; - line-height : 14px; - margin-right : 8px; - margin-top : 2px; - min-width : 23px; - height : 14px; - } + .counterNode { + order : 2; + font-weight : bold; + display : none; + font-size : @font-size-tiny; + text-align : center; + border : 1px solid lighten(@color-accent, 5%); + color : white; + background : lighten(@color-accent, 5%); + border-radius : 4px; + //vertical-align : middle; + //float : right; + //position : relative; + line-height : 14px; + margin-right : 8px; + //margin-top : 2px; + min-width : 23px; + height : 14px; + flex-shrink : 0; + + &.aux, &.marked { + background : @color-panel-bg; + color : lighten(@default-text, 10%); + border-color : darken(@color-panel-bg, 10%); + } + + &.marked { + border-color : @color-accent-marked; + background : lighten(@color-accent-marked, 60%); + } + } - .dijitTreeNode .loadingExpando { - left : -3px; - height : 22px; - position : relative; - top : -3px; - } + // fresh + &[data-feed-id="-3"][data-is-cat="false"] .counterNode.unread { + background-color: darken(@color-checked, 15%); + border-color: darken(@color-checked, 25%); + } - .dijitTreeRow .dijitTreeLabel.Unread { - font-weight : bold; - } + .dijitTreeContent { + display : flex; + align-items : center; + flex-grow : 2; + min-width : 0; + } - .dijitTreeRow.Error .dijitTreeLabel { - color : red; + .dijitTreeLabel { + cursor : pointer; + min-width : 0; + overflow : hidden; + text-overflow : ellipsis; + position : relative; + + // Segoe UI (@fonts-ui) seems to have wrong baseline set + // top : -0.5px; + + &.Unread { + font-weight : bold; + } + } + + &.Error .dijitTreeLabel { + color : red; + } + + &.dijitTreeRowSelected { + box-shadow : -1px 0px 2px -1px rgba(0,0,0,0.1); + border-color : @border-default transparent; + background : @default-bg; + color : @default-text; + } + + .dijitIcon.feed-icon { + margin-right : 2px; + } + + i.icon.icon-inbox { + color : lighten(@default-text, 20%); + } + + i.icon.icon-archive { + color : darken(@color-marked, 30%); + } + + i.icon.icon-star { + position : relative; + color : @color-marked; + font-size : 21px; + left : -2px; + } + + i.icon.icon-rss_feed { + color : @color-published; + } + + i.icon.icon-whatshot { + color : @color-checked; + } + + i.icon.icon-restore { + //position : relative; + //top : -1px; + font-weight : bold; + color : @color-accent; + } + } } + } + } + + #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] { + display : grid; + grid-template-columns: repeat(auto-fit, minmax(@cdm-grid-col-width, 1fr)); + padding : @cdm-grid-padding; + grid-gap : @cdm-grid-padding; + background-color: @color-panel-bg; - .dijitTreeNode .dijitTreeRow { - border : 1px solid transparent; + > * { + /* 2 = because #headlines-spacer is the actual last child + only if odd to deal with 1) single article and 2) not break any previous rows; + 1 = spacer; + + this is outside of .cdm selector because of #headlines-spacer etc + + .grid-span-row is manually expanded RROWs + */ + &.grid-span-row, + &:nth-last-child(2):nth-child(odd), + &:nth-last-child(1) { + grid-column : 1 e("/") -1; } + } - .dijitTreeNode .dijitTreeRowSelected { - box-shadow : -1px 0px 2px -1px rgba(0,0,0,0.1); - border-color : @border-default transparent; + .cdm.expanded { + .header, .content { background : @default-bg; - color : #333; + border : 1px solid @border-default; + overflow : hidden; } - .dijitIcon.feed-icon { - margin-right : 2px; + .content { + border-top-width : 0; + padding : 0 4px 4px 4px; } - i.icon.icon-inbox { - color : #555; + .header[data-is-stuck] { + top : -@cdm-grid-padding; + border-bottom-width: 1px; } - i.icon.icon-archive { - color : #c77b2e; - } + .header { + border-bottom-width : 0; + padding : 4px; - i.icon.icon-star { - position : relative; - color : @color-marked; - font-size : 21px; - left : -2px; - } + .icon-grid-span { + display : inline; + } - i.icon.icon-rss_feed { - color : @color-published; + .feed { + display : none; + } } - i.icon.icon-whatshot { - color : @color-checked; - } + .footer { + border : 0; + padding : 4px; - i.icon.icon-restore { - position : relative; - top : -1px; - font-weight : bold; - color : @color-accent; + .left, .right { + white-space: nowrap; + } + + .left { + overflow: hidden; + text-overflow: ellipsis; + } } + .content-inner, + .intermediate { + a { + word-break: break-all; + } + } } } - #headlines-wrap-inner { - padding : 0px; - margin : 0px; - border-width : 0px; - } - - #headlines-frame[is-vfeed="0"] .header .feed { - display : none; - } - #headlines-frame { - padding : 0px; - border: 0px @border-default; - margin-top : 0px; + padding : 0; + border: 0; + margin-top : 0; + -webkit-overflow-scrolling : touch; -webkit-transform: translateZ(0); + transform: translateZ(0); -webkit-backface-visibility: hidden; + backface-visibility: hidden; - div.feed-title { - border: 0px solid @color-link; - border-bottom-width: 1px; - padding: 5px 8px; + &[data-is-vfeed="false"] { + .header, + .hl { + .feed, .icon-feed { + display : none; + } + } + } + + &[data-auto-catchup="true"] #headlines-spacer { + height : 100vh; } - div.feed-title a.title { - color: @default-text; - font-weight: bold; + .dijitCheckBox { + margin-right : 4px; } - div.feed-title a { - color: @default-text; + &[data-is-wide-screen="true"] { + .title { + overflow: visible; + white-space: normal; + } + + .hl .feed { + display: none; + } } - div.feed-title a:hover { - color: @color-link; + #headlines-spacer { + margin-left : 1px; + text-align : center; + color : @default-text; + font-size : @font-size-small; + + a, span { + color : @default-text; + padding : 10px; + display : block; + } + + a:hover { + color : @color-accent; + } + } + + .feed-title { + border: 0px solid @color-link; + border-bottom-width: 1px; + padding: 5px 8px; + + a.title { + color: @default-text; + font-weight: bold; + } + + a { + color: @default-text; + + &:hover { + color: @color-link; + } + } } span.hlMenuAttach { -webkit-touch-callout: none; -webkit-user-select: none; + user-select: none; } } @@ -787,15 +884,15 @@ body.ttrss_main { } #toolbar-frame { - padding : 0px; - margin : 0px; - border-width : 0px; + padding : 0; + margin : 0; + border : 0; white-space: nowrap; - font-size : 12px; + font-size : @font-size-toolbar; #toolbar { background : white; - border: 0px solid @border-default; + border: 0 solid @border-default; border-bottom-width: 1px; padding-left : 4px; height : 32px; @@ -803,13 +900,13 @@ body.ttrss_main { flex-direction : row; flex-wrap : nowrap; color : @default-text; - font-size : 12px; + font-size : @font-size-toolbar; align-items : center; .dijitSelect, .dijitDropDownButton .dijitButtonNode, .dijitComboButton .dijitButtonNode { - border : 0px; + border : 0; } i.net-alert, .left i.icon-error { @@ -826,7 +923,7 @@ body.ttrss_main { } #toolbar-headlines { - font-size : 12px; + font-size : @font-size-toolbar; background: transparent; padding-right : 4px; flex-grow : 2; @@ -840,6 +937,12 @@ body.ttrss_main { .feed_title, .cancel_search { margin-left : 4px; } + + @media (max-width: @breakpoint-md) { + .feed_title, i.icon-syndicate { + display : none; + } + } } .right { @@ -860,23 +963,31 @@ body.ttrss_main { color : @color-accent; } - @media (max-width: 992px) { + @media (max-width: @breakpoint-md) { #selected_prompt { display : none; } } + + @media (max-width: @breakpoint-sm) { + .select-articles-dropdown, + .catchup-button { + display : none; + } + } + } } #header { - border-width : 0px; text-align : right; color : @default-text; - padding : 5px 5px 0px 0px; - margin : 0px; + padding : 5px 5px 0 0px; position : absolute; - right : 0px; - top : 0px; + border : 0; + margin : 0; + right : 0; + top : 0; z-index : 5; i.net-alert, .left i.icon-error { @@ -899,9 +1010,8 @@ body.ttrss_main { } #content-insert { - padding : 0px; - border-color : @border-default; - border-width : 0px; + padding : 0; + border : 0; line-height: 1.5; overflow : auto; -webkit-overflow-scrolling : touch; @@ -915,64 +1025,10 @@ body.ttrss_main { display : inline-block; } - .player { - display : inline-block; - color : @default-text; - font-size : 11px; - font-family : sans-serif; - border : 1px solid @default-text; - padding : 0px 4px 0px 4px; - margin : 0px 2px 0px 2px; - width : 50px; - text-align : center; - background : @default-bg; - } - - .player.playing { - color : #00c000; - border-color : #00c000; - } - - .player:hover { - background : @color-panel-bg; - cursor : pointer; - } - - #headlines-frame.auto_catchup #headlines-spacer { - height : 100%; - } - - #headlines-spacer { - margin-left : 1px; - text-align : center; - color : @default-text; - font-size : 11px; - font-style : italic; - - a, span { - color : @default-text; - padding : 10px; - display : block; - } - - a:hover { - color : @color-accent; - } - } - ul#filterDlg_Matches, ul#filterDlg_Actions { list-style-type : none; margin : 0; padding: 0; - /*max-height : 100px; - overflow : auto; - border-style : solid; - border-color : @border-default; - border-width : 1px 1px 1px 1px; - background-color : @default-bg; - margin : 0px 0px 5px 0px; - padding : 4px; - min-height : 16px;*/ } ul#filterDlg_Matches li, ul#filterDlg_Actions li { @@ -1018,10 +1074,6 @@ body.ttrss_main { color : #cc90cc; } - #headlines-frame .dijitCheckBox { - margin-right : 4px; - } - #feedEditDlg img.feedIcon { border : 1px solid #ccc; padding : 5px; @@ -1110,6 +1162,7 @@ body.ttrss_main { border: 1px solid @color-published; border-radius: 4px; } + #feed_current_unread { margin-left : 8px; font-weight : bold; @@ -1148,11 +1201,21 @@ body.ttrss_main { color : #500; } - .score-neutral i.icon-score { + /*.score-neutral i.icon-score { opacity : 0.5; + }*/ + + i.icon-score, i.icon-grid-span { + cursor : pointer; + color : @color-icon; + } + + // only shown in grid mode + .icon-grid-span { + display : none; } - i.icon-score { + .icon-feed { cursor : pointer; } @@ -1192,27 +1255,44 @@ body.ttrss_main { text-align : center; } - #prefFilterTestResultList { - .preview { - margin : 8px; - } + .text-right { + text-align : right; + } - .title { - font-weight: bold; + .text-left { + text-align : left; + } + + .dijitDialog { + .filter-results-list { + .preview { + margin : 8px; + } + + .title { + font-weight: bold; + } } - .feed { - color : @color-accent; + #feed_add_spinner { + position : relative; + top : 5px; + width : 18px; + height : 18px; } } + .icon-three-dots { + width : 18px; + height : 18px; + vertical-align : middle; + } } body.ttrss_main, body.ttrss_utility { .alert { padding: 8px 35px 8px 14px; margin-bottom: 10px; - /* text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); */ background-color: #fcf8e3; border: 1px solid #fbeed5; border-radius: 4px; @@ -1254,6 +1334,14 @@ body.ttrss_main, body.ttrss_utility { color: darken(#c09853, 10%); } + .text-muted { + color : @default-text; + } + + .text-small, .small { + font-size : @font-size-small; + } + .alert, .alert h4 { color: #c09853; @@ -1300,15 +1388,7 @@ body.ttrss_main, body.ttrss_utility { border-bottom-width: 1px; } - .text-muted { - color : @default-text; - } - - .small { - font-size : 11px; - } - - div.autocomplete { + /*div.autocomplete { position : absolute; width : 250px; background-color : @default-bg; @@ -1333,8 +1413,7 @@ body.ttrss_main, body.ttrss_utility { padding : 2px; cursor : pointer; } - } - + }*/ } ::selection { @@ -1354,7 +1433,7 @@ body.ttrss_main, body.ttrss_utility { background-color: #eee; } -video::-webkit-media-controls-overlay-play-button { +/*video::-webkit-media-controls-overlay-play-button { display: none; -} +}*/ diff --git a/themes/light/utility.less b/themes/light/utility.less index 1c30c77ab..89675ac13 100644 --- a/themes/light/utility.less +++ b/themes/light/utility.less @@ -6,7 +6,7 @@ body.ttrss_utility { background : @color-panel-bg; color : @default-fg; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; - font-size: 14px; + font-size: @font-size-content; margin : 4em; .content { @@ -21,7 +21,7 @@ body.ttrss_utility { h2, h3, h4 { color : @color-accent; - font-family: @fonts-ui-bold; + font-family: @fonts-ui; } h2 { @@ -46,7 +46,7 @@ body.ttrss_utility { h1 { color : gray; - font-family: @fonts-ui-bold; + font-family: @fonts-ui; font-size : 18px; margin : 10px 0 0 0; } diff --git a/themes/light/zoom.less b/themes/light/zoom.less index e599bc447..3318482c2 100644 --- a/themes/light/zoom.less +++ b/themes/light/zoom.less @@ -12,11 +12,11 @@ body.ttrss_zoom { } div.header { - padding-bottom : 10px; - border: 0px solid @border-default; + padding-bottom : 8px; + border: 0 solid @border-default; border-bottom-width: 1px; background : @default-bg; - font-size : 12px; + font-size : @font-size-toolbar; color : @default-text; .row { @@ -25,14 +25,20 @@ body.ttrss_zoom { flex-wrap : nowrap; align-items : center; justify-content : space-between; + + h1 { + margin-top : 0; + margin-bottom : 8px; + } } } - div.content { + .content { font-size : 15px; line-height : 1.5; border-width : 0; padding : 0; + padding-top : 8px; img, video { max-width : 760px; @@ -42,7 +48,7 @@ body.ttrss_zoom { blockquote { margin : 5px 0px 5px 0px; color : @default-text; - padding-left : 10px; + padding-left : 8px; border: 0px solid @border-default; border-left-width: 4px; } @@ -54,8 +60,7 @@ body.ttrss_zoom { } pre { - margin : 5px 0px 5px 0px; - padding : 10px; + padding : 8px; color : @default-text; font-family : monospace; font-size : 12px; diff --git a/themes/night.css b/themes/night.css index 22eb17e9f..f5e23e59a 100644 --- a/themes/night.css +++ b/themes/night.css @@ -16,67 +16,77 @@ body.ttrss_main { font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 14px; overflow: hidden; + /*ul.compact { + list-style-type : none; + margin : 0; + padding : 0; + + li { + margin : 0; + padding : 0; + } + }*/ } body.ttrss_main :focus { outline: none; } -body.ttrss_main div.post { - padding: 0px; +body.ttrss_main .post { + padding: 0; font-size: 13px; } -body.ttrss_main div.post div.header { +body.ttrss_main .post .header { padding: 5px; - color: #909090; - border: 0px solid #222; + color: #ccc; + border: 0 solid #222; border-bottom-width: 1px; background: #222; } -body.ttrss_main div.post div.header .left, -body.ttrss_main div.post div.header .right { +body.ttrss_main .post .header .left, +body.ttrss_main .post .header .right { display: flex; } -body.ttrss_main div.post div.header .row { +body.ttrss_main .post .header .row { display: flex; margin-bottom: 4px; flex-wrap: nowrap; align-items: center; justify-content: space-between; } -body.ttrss_main div.post div.header .comments { +body.ttrss_main .post .header .comments { flex-grow: 2; } -body.ttrss_main div.post div.header .date { +body.ttrss_main .post .header .date { white-space: nowrap; } -body.ttrss_main div.post div.header img, -body.ttrss_main div.post div.header i.material-icons { +body.ttrss_main .post .header img, +body.ttrss_main .post .header i.material-icons { margin: 0px 4px; vertical-align: middle; - color: #777; + color: #999; } -body.ttrss_main div.post div.header .title { +body.ttrss_main .post .header .title { flex-grow: 2; font-size: 15px; font-weight: 600; text-rendering: optimizelegibility; - font-family: "Segoe WP Semibold", "Segoe UI Semibold", "Segoe UI Web Semibold", "Segoe UI", Ubuntu, "Helvetica Neue", Helvetica, Arial, sans-serif; + font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif; } -body.ttrss_main div.post div.content { +body.ttrss_main .post div.content { padding: 10px; font-size: 16px; } -body.ttrss_main div.post div.content img, -body.ttrss_main div.post div.content video { +body.ttrss_main .post div.content img, +body.ttrss_main .post div.content video { border-width: 0px; max-width: 98%; height: auto; } -body.ttrss_main div.post div.content div.embed-responsive { +body.ttrss_main .post div.content div.embed-responsive { overflow: hidden; padding-bottom: 56.25%; position: relative; } -body.ttrss_main div.post div.content div.embed-responsive iframe { +body.ttrss_main .post div.content div.embed-responsive iframe { border: 0; bottom: 0; height: 100%; @@ -173,6 +183,11 @@ body.ttrss_main .notify.notify_progress { border-color: #d7c47a; background-color: #fff7d5; } +body.ttrss_main .notify.notify_progress img { + width: 18px; + height: 18px; + filter: saturate(0); +} body.ttrss_main .notify.notify_info { border-color: #b87d2c; background-color: #faf3e9; @@ -218,6 +233,10 @@ body.ttrss_main .hl .right { display: flex; align-items: center; } +body.ttrss_main .hl .left img, +body.ttrss_main .hl .right img { + margin: 0 4px; +} body.ttrss_main .hl .left i.material-icons, body.ttrss_main .hl .right i.material-icons { margin-left: 2px; @@ -226,25 +245,28 @@ body.ttrss_main .hl .right i.material-icons { user-select: none; font-size: 21px; } +body.ttrss_main .hl .left input { + margin: 0 4px; +} +body.ttrss_main .hl .right { + text-align: right; +} body.ttrss_main .hl .right i.material-icons { - color: #777; + color: #999; } -body.ttrss_main .hl div.title { +body.ttrss_main .hl .title { cursor: pointer; flex-grow: 2; overflow: hidden; text-overflow: ellipsis; } -body.ttrss_main .hl span.author { +body.ttrss_main .hl .author { white-space: nowrap; color: #ccc; font-size: 11px; font-weight: normal; } -body.ttrss_main .hl div.right { - text-align: right; -} -body.ttrss_main .hl span.feed a { +body.ttrss_main .hl .feed a { border-radius: 4px; display: inline-block; padding: 1px 4px; @@ -253,29 +275,22 @@ body.ttrss_main .hl span.feed a { font-weight: normal; color: #ccc; } -body.ttrss_main .hl span.feed a:hover { +body.ttrss_main .hl .feed a:hover { color: #b87d2c; } -body.ttrss_main .hl span.updated { +body.ttrss_main .hl .updated { color: #ccc; text-align: right; font-size: 11px; padding-left: 10px; } -body.ttrss_main .hl span.updated div { +body.ttrss_main .hl .updated div { display: inline-block; } -body.ttrss_main .hl div.left input { - margin: 0px 4px; -} -body.ttrss_main .hl div.left img, -body.ttrss_main .hl div.right img { - margin: 0px 4px; -} body.ttrss_main .hl div.title a { font-weight: 600; text-rendering: optimizelegibility; - font-family: "Segoe UI", Ubuntu, "Helvetica Neue", Helvetica, Arial, sans-serif; + font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif; color: #555; } body.ttrss_main .hl a.title.high, @@ -288,10 +303,10 @@ body.ttrss_main .hl.vgrlf .feed { body.ttrss_main .hl.Unread { background: white; } -body.ttrss_main .hl.Unread div.title a { +body.ttrss_main .hl.Unread .title a { color: black; } -body.ttrss_main .hl.active div.title a { +body.ttrss_main .hl.active .title a { color: #b87d2c; /* text-shadow : 1px 1px 2px #fff; */ } @@ -313,9 +328,6 @@ body.ttrss_main .hl.active span, body.ttrss_main .hl.Selected span { color: white; } -body.ttrss_main .hl.Grayed { - color: #909090; -} body.ttrss_main #content-insert blockquote, body.ttrss_main #headlines-frame blockquote, body.ttrss_main .dijitContentPane blockquote { @@ -372,45 +384,6 @@ body.ttrss_main i.pub-pic { cursor: pointer; color: #ccc; } -body.ttrss_main div.errorExplained { - border: 1px solid #222; - margin: 5px 0px 5px 0px; - padding: 5px; -} -body.ttrss_main ul.browseFeedList { - height: 300px; - width: 100%; - overflow: auto; - border-width: 0px 1px 1px 1px; - border-color: #222; - border-style: solid; - margin: 0px 0px 5px 0px; - background-color: white; - list-style-type: none; - padding: 0px; -} -body.ttrss_main ul.browseFeedList li { - display: flex; - align-items: center; -} -body.ttrss_main ul.browseFeedList li > * { - margin: 2px; -} -body.ttrss_main .browseFeedList span.subscribers { - color: #808080; -} -body.ttrss_main ul.compact { - list-style-type: none; - margin: 0px; - padding: 0px; -} -body.ttrss_main ul.compact li { - margin: 0px; - padding: 0px; -} -body.ttrss_main .noborder { - border-width: 0px; -} body.ttrss_main #overlay { background: #333; left: 0; @@ -424,11 +397,6 @@ body.ttrss_main #overlay_inner { font-weight: bold; margin: 1em; } -body.ttrss_main div.loadingPrompt { - padding: 1em; - text-align: center; - font-weight: bold; -} body.ttrss_main div.whiteBox { margin-left: 1px; text-align: center; @@ -437,20 +405,13 @@ body.ttrss_main div.whiteBox { border: 0px solid #222; border-bottom-width: 1px; } -body.ttrss_main div#headlines-frame.wide .title { - overflow: visible; - white-space: normal; -} -body.ttrss_main div#headlines-frame.wide .hl .feed { - display: none; -} body.ttrss_main .dijitDialog header, body.ttrss_main .dijitDialog .dlgSec, body.ttrss_main .dijitDialog .dlgSecHoriz { font-size: 16px; font-weight: 600; color: #ccc; - font-family: "Segoe WP Semibold", "Segoe UI Semibold", "Segoe UI Web Semibold", "Segoe UI", Ubuntu, "Helvetica Neue", Helvetica, Arial, sans-serif; + font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif; } body.ttrss_main .dijitDialog section, body.ttrss_main .dijitDialog .dlgSecCont { @@ -528,11 +489,6 @@ body.ttrss_main div#cmdline { padding: 3px 5px 3px 5px; z-index: 5; } -body.ttrss_main #feed_browser_spinner { - vertical-align: middle; - height: 18px; - width: 18px; -} body.ttrss_main .exception-contents h3 { color: red; } @@ -541,14 +497,15 @@ body.ttrss_main .exception-contents textarea { height: 200px; font-size: 11px; } +body.ttrss_main #headlines-wrap-inner, body.ttrss_main #content-wrap { - padding: 0px; - border-width: 0px; - margin: 0px; + padding: 0; + border: 0; + margin: 0; } body.ttrss_main #feeds-holder { - padding: 0px; - border: 0px solid #222; + padding: 0; + border: 0 solid #222; overflow: hidden; background: #222; box-shadow: inset -1px 0px 2px -1px rgba(0, 0, 0, 0.1); @@ -558,19 +515,26 @@ body.ttrss_main #feeds-holder #feedTree { height: 100%; overflow-x: hidden; text-rendering: optimizelegibility; - font-family: "Segoe UI", Ubuntu, "Helvetica Neue", Helvetica, Arial, sans-serif; + font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif; } -body.ttrss_main #feeds-holder #feedTree .counterNode.aux, -body.ttrss_main #feeds-holder #feedTree .counterNode.marked { - background: #222; - color: #e6e6e6; - border-color: #080808; +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow { + border: 1px solid transparent; } -body.ttrss_main #feeds-holder #feedTree .counterNode.marked { - border-color: #b87d2c; - background: #ffffff; +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow.Is_Feed .loadingNode { + left: -2px; + height: 14px; + width: 14px; + position: relative; +} +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow.Is_Cat .loadingNode { + height: 11px; + width: 18px; + margin-left: 4px; + position: relative; + top: 2px; } -body.ttrss_main #feeds-holder #feedTree .counterNode { +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow .counterNode { + order: 2; font-weight: bold; display: none; font-size: 9px; @@ -579,111 +543,215 @@ body.ttrss_main #feeds-holder #feedTree .counterNode { color: white; background: #cd8b31; border-radius: 4px; - vertical-align: middle; - float: right; - position: relative; line-height: 14px; margin-right: 8px; - margin-top: 2px; min-width: 23px; height: 14px; + flex-shrink: 0; +} +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow .counterNode.aux, +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow .counterNode.marked { + background: #222; + color: #e6e6e6; + border-color: #080808; +} +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow .counterNode.marked { + border-color: #b87d2c; + background: #ffffff; +} +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow[data-feed-id="-3"][data-is-cat="false"] .counterNode.unread { + background-color: #3ea447; + border-color: #307f37; +} +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow .dijitTreeContent { + display: flex; + align-items: center; + flex-grow: 2; + min-width: 0; } -body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .loadingExpando { - left: -3px; - height: 22px; +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow .dijitTreeLabel { + cursor: pointer; + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; position: relative; - top: -3px; } -body.ttrss_main #feeds-holder #feedTree .dijitTreeRow .dijitTreeLabel.Unread { +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow .dijitTreeLabel.Unread { font-weight: bold; } -body.ttrss_main #feeds-holder #feedTree .dijitTreeRow.Error .dijitTreeLabel { +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow.Error .dijitTreeLabel { color: red; } -body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow { - border: 1px solid transparent; -} -body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRowSelected { +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow.dijitTreeRowSelected { box-shadow: -1px 0px 2px -1px rgba(0, 0, 0, 0.1); border-color: #222 transparent; background: #333; - color: #333; + color: #ccc; } -body.ttrss_main #feeds-holder #feedTree .dijitIcon.feed-icon { +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow .dijitIcon.feed-icon { margin-right: 2px; } -body.ttrss_main #feeds-holder #feedTree i.icon.icon-inbox { - color: #555; +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow i.icon.icon-inbox { + color: #ffffff; } -body.ttrss_main #feeds-holder #feedTree i.icon.icon-archive { - color: #c77b2e; +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow i.icon.icon-archive { + color: #cf7800; } -body.ttrss_main #feeds-holder #feedTree i.icon.icon-star { +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow i.icon.icon-star { position: relative; color: #ffc069; font-size: 21px; left: -2px; } -body.ttrss_main #feeds-holder #feedTree i.icon.icon-rss_feed { +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow i.icon.icon-rss_feed { color: #ff7c4b; } -body.ttrss_main #feeds-holder #feedTree i.icon.icon-whatshot { +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow i.icon.icon-whatshot { color: #69C671; } -body.ttrss_main #feeds-holder #feedTree i.icon.icon-restore { - position: relative; - top: -1px; +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow i.icon.icon-restore { font-weight: bold; color: #b87d2c; } -body.ttrss_main #headlines-wrap-inner { - padding: 0px; - margin: 0px; - border-width: 0px; +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(600px, 1fr)); + padding: 8px; + grid-gap: 8px; + background-color: #222; +} +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] > * { + /* 2 = because #headlines-spacer is the actual last child + only if odd to deal with 1) single article and 2) not break any previous rows; + 1 = spacer; + + this is outside of .cdm selector because of #headlines-spacer etc + + .grid-span-row is manually expanded RROWs + */ +} +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] > *.grid-span-row, +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] > *:nth-last-child(2):nth-child(odd), +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] > *:nth-last-child(1) { + grid-column: 1 / -1; +} +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] .cdm.expanded .header, +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] .cdm.expanded .content { + background: #333; + border: 1px solid #222; + overflow: hidden; +} +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] .cdm.expanded .content { + border-top-width: 0; + padding: 0 4px 4px 4px; +} +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] .cdm.expanded .header[data-is-stuck] { + top: -8px; + border-bottom-width: 1px; +} +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] .cdm.expanded .header { + border-bottom-width: 0; + padding: 4px; +} +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] .cdm.expanded .header .icon-grid-span { + display: inline; } -body.ttrss_main #headlines-frame[is-vfeed="0"] .header .feed { +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] .cdm.expanded .header .feed { display: none; } +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] .cdm.expanded .footer { + border: 0; + padding: 4px; +} +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] .cdm.expanded .footer .left, +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] .cdm.expanded .footer .right { + white-space: nowrap; +} +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] .cdm.expanded .footer .left { + overflow: hidden; + text-overflow: ellipsis; +} +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] .cdm.expanded .content-inner a, +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] .cdm.expanded .intermediate a { + word-break: break-all; +} body.ttrss_main #headlines-frame { - padding: 0px; - border: 0px #222; - margin-top: 0px; + padding: 0; + border: 0; + margin-top: 0; -webkit-overflow-scrolling: touch; -webkit-transform: translateZ(0); + transform: translateZ(0); -webkit-backface-visibility: hidden; + backface-visibility: hidden; +} +body.ttrss_main #headlines-frame[data-is-vfeed="false"] .header .feed, +body.ttrss_main #headlines-frame[data-is-vfeed="false"] .hl .feed, +body.ttrss_main #headlines-frame[data-is-vfeed="false"] .header .icon-feed, +body.ttrss_main #headlines-frame[data-is-vfeed="false"] .hl .icon-feed { + display: none; +} +body.ttrss_main #headlines-frame[data-auto-catchup="true"] #headlines-spacer { + height: 100vh; +} +body.ttrss_main #headlines-frame .dijitCheckBox { + margin-right: 4px; +} +body.ttrss_main #headlines-frame[data-is-wide-screen="true"] .title { + overflow: visible; + white-space: normal; } -body.ttrss_main #headlines-frame div.feed-title { +body.ttrss_main #headlines-frame[data-is-wide-screen="true"] .hl .feed { + display: none; +} +body.ttrss_main #headlines-frame #headlines-spacer { + margin-left: 1px; + text-align: center; + color: #ccc; + font-size: 11px; +} +body.ttrss_main #headlines-frame #headlines-spacer a, +body.ttrss_main #headlines-frame #headlines-spacer span { + color: #ccc; + padding: 10px; + display: block; +} +body.ttrss_main #headlines-frame #headlines-spacer a:hover { + color: #b87d2c; +} +body.ttrss_main #headlines-frame .feed-title { border: 0px solid #b87d2c; border-bottom-width: 1px; padding: 5px 8px; } -body.ttrss_main #headlines-frame div.feed-title a.title { +body.ttrss_main #headlines-frame .feed-title a.title { color: #ccc; font-weight: bold; } -body.ttrss_main #headlines-frame div.feed-title a { +body.ttrss_main #headlines-frame .feed-title a { color: #ccc; } -body.ttrss_main #headlines-frame div.feed-title a:hover { +body.ttrss_main #headlines-frame .feed-title a:hover { color: #b87d2c; } body.ttrss_main #headlines-frame span.hlMenuAttach { -webkit-touch-callout: none; -webkit-user-select: none; + user-select: none; } body.ttrss_main #toolbar-frame_splitter { display: none; } body.ttrss_main #toolbar-frame { - padding: 0px; - margin: 0px; - border-width: 0px; + padding: 0; + margin: 0; + border: 0; white-space: nowrap; - font-size: 12px; + font-size: 13px; } body.ttrss_main #toolbar-frame #toolbar { background: white; - border: 0px solid #222; + border: 0 solid #222; border-bottom-width: 1px; padding-left: 4px; height: 32px; @@ -691,13 +759,13 @@ body.ttrss_main #toolbar-frame #toolbar { flex-direction: row; flex-wrap: nowrap; color: #ccc; - font-size: 12px; + font-size: 13px; align-items: center; } body.ttrss_main #toolbar-frame #toolbar .dijitSelect, body.ttrss_main #toolbar-frame #toolbar .dijitDropDownButton .dijitButtonNode, body.ttrss_main #toolbar-frame #toolbar .dijitComboButton .dijitButtonNode { - border: 0px; + border: 0; } body.ttrss_main #toolbar-frame #toolbar i.net-alert, body.ttrss_main #toolbar-frame #toolbar .left i.icon-error { @@ -711,7 +779,7 @@ body.ttrss_main #toolbar-frame #toolbar i { margin: 0 4px; } body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines { - font-size: 12px; + font-size: 13px; background: transparent; padding-right: 4px; flex-grow: 2; @@ -726,6 +794,12 @@ body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines .left .feed_title, body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines .left .cancel_search { margin-left: 4px; } +@media (max-width: 768px) { + body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines .left .feed_title, + body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines .left i.icon-syndicate { + display: none; + } +} body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines .right { display: flex; align-items: center; @@ -740,20 +814,26 @@ body.ttrss_main #toolbar-frame #toolbar #selected_prompt { margin-right: 4px; color: #b87d2c; } -@media (max-width: 992px) { +@media (max-width: 768px) { body.ttrss_main #toolbar-frame #toolbar #selected_prompt { display: none; } } +@media (max-width: 576px) { + body.ttrss_main #toolbar-frame #toolbar .select-articles-dropdown, + body.ttrss_main #toolbar-frame #toolbar .catchup-button { + display: none; + } +} body.ttrss_main #header { - border-width: 0px; text-align: right; color: #ccc; - padding: 5px 5px 0px 0px; - margin: 0px; + padding: 5px 5px 0 0px; position: absolute; - right: 0px; - top: 0px; + border: 0; + margin: 0; + right: 0; + top: 0; z-index: 5; } body.ttrss_main #header i.net-alert, @@ -772,9 +852,8 @@ body.ttrss_main #header i { margin: 0 4px; } body.ttrss_main #content-insert { - padding: 0px; - border-color: #222; - border-width: 0px; + padding: 0; + border: 0; line-height: 1.5; overflow: auto; -webkit-overflow-scrolling: touch; @@ -787,59 +866,11 @@ body.ttrss_main img.icon { vertical-align: middle; display: inline-block; } -body.ttrss_main .player { - display: inline-block; - color: #ccc; - font-size: 11px; - font-family: sans-serif; - border: 1px solid #ccc; - padding: 0px 4px 0px 4px; - margin: 0px 2px 0px 2px; - width: 50px; - text-align: center; - background: #333; -} -body.ttrss_main .player.playing { - color: #00c000; - border-color: #00c000; -} -body.ttrss_main .player:hover { - background: #222; - cursor: pointer; -} -body.ttrss_main #headlines-frame.auto_catchup #headlines-spacer { - height: 100%; -} -body.ttrss_main #headlines-spacer { - margin-left: 1px; - text-align: center; - color: #ccc; - font-size: 11px; - font-style: italic; -} -body.ttrss_main #headlines-spacer a, -body.ttrss_main #headlines-spacer span { - color: #ccc; - padding: 10px; - display: block; -} -body.ttrss_main #headlines-spacer a:hover { - color: #b87d2c; -} body.ttrss_main ul#filterDlg_Matches, body.ttrss_main ul#filterDlg_Actions { list-style-type: none; margin: 0; padding: 0; - /*max-height : 100px; - overflow : auto; - border-style : solid; - border-color : @border-default; - border-width : 1px 1px 1px 1px; - background-color : @default-bg; - margin : 0px 0px 5px 0px; - padding : 4px; - min-height : 16px;*/ } body.ttrss_main ul#filterDlg_Matches li, body.ttrss_main ul#filterDlg_Actions li { @@ -875,9 +906,6 @@ body.ttrss_main span.highlight { background-color: #ffff00; color: #cc90cc; } -body.ttrss_main #headlines-frame .dijitCheckBox { - margin-right: 4px; -} body.ttrss_main #feedEditDlg img.feedIcon { border: 1px solid #ccc; padding: 5px; @@ -935,6 +963,11 @@ body.ttrss_main:not([view-mode="marked"])[hide-read-feeds="true"][hide-read-show body.ttrss_main:not([view-mode="marked"])[hide-read-feeds="true"][hide-read-shows-special="false"] #feeds-holder #feedTree .dijitTreeRow:not(.Unread):not(.AlwaysVisible) { display: none; } +body.ttrss_main { + /*.score-neutral i.icon-score { + opacity : 0.5; + }*/ +} body.ttrss_main #toolbar-headlines i.icon-syndicate { color: #ff7c4b; margin-right: 8px; @@ -971,10 +1004,15 @@ body.ttrss_main .score-high i.icon-score { body.ttrss_main .score-low i.icon-score { color: #500; } -body.ttrss_main .score-neutral i.icon-score { - opacity: 0.5; +body.ttrss_main i.icon-score, +body.ttrss_main i.icon-grid-span { + cursor: pointer; + color: #999; } -body.ttrss_main i.icon-score { +body.ttrss_main .icon-grid-span { + display: none; +} +body.ttrss_main .icon-feed { cursor: pointer; } body.ttrss_main .panel { @@ -1005,20 +1043,62 @@ body.ttrss_main ul.list-unstyled { body.ttrss_main .text-center { text-align: center; } -body.ttrss_main #prefFilterTestResultList .preview { +body.ttrss_main .text-right { + text-align: right; +} +body.ttrss_main .text-left { + text-align: left; +} +body.ttrss_main .dijitDialog .filter-results-list .preview { margin: 8px; } -body.ttrss_main #prefFilterTestResultList .title { +body.ttrss_main .dijitDialog .filter-results-list .title { font-weight: bold; } -body.ttrss_main #prefFilterTestResultList .feed { - color: #b87d2c; +body.ttrss_main .dijitDialog #feed_add_spinner { + position: relative; + top: 5px; + width: 18px; + height: 18px; +} +body.ttrss_main .icon-three-dots { + width: 18px; + height: 18px; + vertical-align: middle; +} +body.ttrss_main, +body.ttrss_utility { + /*div.autocomplete { + position : absolute; + width : 250px; + background-color : @default-bg; + border :1px solid @border-default; + margin : 0px; + padding : 0px; + + ul { + list-style-type : none; + margin : 0px; + padding : 0px; + } + + ul li.selected { + background-color : darken(@default-bg, 10%); + } + + ul li { + list-style-type : none; + display : block; + margin : 0; + padding : 2px; + cursor : pointer; + } + }*/ } body.ttrss_main .alert, body.ttrss_utility .alert { padding: 8px 35px 8px 14px; margin-bottom: 10px; - /* text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); */ background-color: #fcf8e3; border: 1px solid #fbeed5; border-radius: 4px; @@ -1059,6 +1139,16 @@ body.ttrss_main .text-warning, body.ttrss_utility .text-warning { color: #a47e3c; } +body.ttrss_main .text-muted, +body.ttrss_utility .text-muted { + color: #ccc; +} +body.ttrss_main .text-small, +body.ttrss_utility .text-small, +body.ttrss_main .small, +body.ttrss_utility .small { + font-size: 11px; +} body.ttrss_main .alert, body.ttrss_utility .alert, body.ttrss_main .alert h4, @@ -1108,41 +1198,6 @@ body.ttrss_utility hr { border: 0px solid #ccc; border-bottom-width: 1px; } -body.ttrss_main .text-muted, -body.ttrss_utility .text-muted { - color: #ccc; -} -body.ttrss_main .small, -body.ttrss_utility .small { - font-size: 11px; -} -body.ttrss_main div.autocomplete, -body.ttrss_utility div.autocomplete { - position: absolute; - width: 250px; - background-color: #333; - border: 1px solid #222; - margin: 0px; - padding: 0px; -} -body.ttrss_main div.autocomplete ul, -body.ttrss_utility div.autocomplete ul { - list-style-type: none; - margin: 0px; - padding: 0px; -} -body.ttrss_main div.autocomplete ul li.selected, -body.ttrss_utility div.autocomplete ul li.selected { - background-color: #1a1a1a; -} -body.ttrss_main div.autocomplete ul li, -body.ttrss_utility div.autocomplete ul li { - list-style-type: none; - display: block; - margin: 0; - padding: 2px; - cursor: pointer; -} ::selection { background: #b87d2c; color: #333; @@ -1156,16 +1211,13 @@ body.ttrss_utility div.autocomplete ul li { ::-webkit-scrollbar-track { background-color: #eee; } -video::-webkit-media-controls-overlay-play-button { - display: none; -} -.cdm i.material-icons { - color: #777; -} -.cdm .header { - position: sticky; - top: 0; - z-index: 3; +/*video::-webkit-media-controls-overlay-play-button { + display: none; +}*/ +.cdm { + /*i.material-icons { + color : @color-icon; + }*/ } .cdm .header, .cdm .footer { @@ -1176,13 +1228,16 @@ video::-webkit-media-controls-overlay-play-button { .cdm .header img, .cdm .footer img, .cdm .footer i.material-icons { - margin: 0px 4px; + margin: 0 4px; vertical-align: middle; } .cdm .header-sticky-guard { height: 0; } .cdm .header { + position: sticky; + top: 0; + z-index: 3; align-items: center; } .cdm .header > * { @@ -1205,7 +1260,7 @@ video::-webkit-media-controls-overlay-play-button { .cdm .header .titleWrap { flex-grow: 2; } -.cdm .header span.updated { +.cdm .header .updated { color: #ccc; font-weight: normal; font-size: 11px; @@ -1214,6 +1269,30 @@ video::-webkit-media-controls-overlay-play-button { .cdm .header input { margin: 0px 4px; } +.cdm .header .feed { + float: right; + font-weight: normal; + font-style: italic; +} +.cdm .header .feed a { + border-radius: 4px; + display: inline-block; + padding: 1px 4px 1px 4px; +} +.cdm .header .feed, +.cdm .header .feed a { + vertical-align: middle; + color: #ccc; + font-weight: normal; + font-style: italic; + font-size: 11px; +} +.cdm .header .author { + white-space: nowrap; + color: #ccc; + font-size: 11px; + font-weight: normal; +} .cdm .footer { height: 30px; padding-left: 5px; @@ -1222,30 +1301,37 @@ video::-webkit-media-controls-overlay-play-button { clear: both; align-items: center; } +.cdm .footer i.material-icons { + color: #999; +} .cdm .footer .left { flex-grow: 2; } .cdm .intermediate { margin-top: 10px; margin-left: 10px; + line-height: 1.5; } .cdm .content-inner { margin: 10px; line-height: 1.5; font-size: 16px; } +.cdm .intermediate iframe, +.cdm .content-inner iframe { + max-width: 98%; + width: auto; + height: auto; +} .cdm .intermediate img, .cdm .intermediate video, .cdm .content-inner img, .cdm .content-inner video { border-width: 0px; max-width: 98%; + width: auto; height: auto; } -.cdm.expanded { - /*margin-top : 4px; - margin-bottom : 4px;*/ -} .cdm.expanded .collapse, .cdm.expanded .excerpt { display: none; @@ -1258,72 +1344,54 @@ video::-webkit-media-controls-overlay-play-button { border-bottom-width: 1px; } .cdm.expanded > hr { - margin-top: 0px; - margin-bottom: 0px; + margin-top: 0; + margin-bottom: 0; } -div.cdm.expanded div.header a.title { +.cdm.expanded .header a.title { font-size: 16px; color: #999; font-weight: 600; transition: color 0.2s, background 0.2s; text-rendering: optimizelegibility; - font-family: "Segoe WP Semibold", "Segoe UI Semibold", "Segoe UI Web Semibold", "Segoe UI", Ubuntu, "Helvetica Neue", Helvetica, Arial, sans-serif; + font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif; } -div.cdm.expanded.active { - background: white; +.cdm.expanded.active .content, +.cdm.expanded.Unread .content { + color: black; } -div.cdm.expanded.active div.header a.title { +.cdm.expanded.active .header .title { color: #b87d2c; } -div.cdm.expanded.Unread div.header a.title { +.cdm.expanded.Unread .header .title { color: black; } -div.cdm.expanded div.content { +.cdm.expanded .content { color: #ccc; } -div.cdm.expanded.Unread div.content { - color: black; -} -div.cdm.active div.content { - color: black; -} -div.cdm.vgrlf .feed { +.cdm.vgrlf .feed { display: none; } -.cdm div.feed-title { +.cdm .feed-title { border: 0px solid #b87d2c; border-bottom-width: 1px; padding: 5px 3px 5px 5px; } -.cdm div.feed-title a.title { +.cdm .feed-title a.title { color: #ccc; font-weight: bold; } -.cdm div.feed-title a { +.cdm .feed-title a { color: #ccc; } -.cdm div.feed-title a:hover { +.cdm .feed-title a:hover { color: #b87d2c; } -.cdm div.header span.feed { - float: right; - font-weight: normal; - font-style: italic; -} -.cdm div.header div.feed, -.cdm div.header div.feed a { - vertical-align: middle; - color: #ccc; - font-weight: normal; - font-style: italic; - font-size: 11px; -} -.cdm div.content-inner div.embed-responsive { +.cdm .content-inner .embed-responsive { overflow: hidden; padding-bottom: 56.25%; position: relative; } -.cdm div.content-inner div.embed-responsive iframe { +.cdm .content-inner .embed-responsive iframe { border: 0; bottom: 0; height: 100%; @@ -1332,17 +1400,6 @@ div.cdm.vgrlf .feed { top: 0; width: 100%; } -.cdm div.header span.author { - white-space: nowrap; - color: #ccc; - font-size: 11px; - font-weight: normal; -} -.cdm .feed a { - border-radius: 4px; - display: inline-block; - padding: 1px 4px 1px 4px; -} .cdm.expandable { background-color: #222; border: 0px solid #222; @@ -1380,43 +1437,43 @@ div.cdm.vgrlf .feed { .cdm.expandable.active { background: white ! important; } -div.cdm.expandable.active div.header span.titleWrap { +.cdm.expandable.active div.header span.titleWrap { white-space: normal; } -div.cdm.expandable div.header a.title { +.cdm.expandable .header a.title { font-weight: 600; color: #ccc; font-size: 14px; transition: color 0.2s, background 0.2s; text-rendering: optimizelegibility; - font-family: "Segoe WP Semibold", "Segoe UI Semibold", "Segoe UI Web Semibold", "Segoe UI", Ubuntu, "Helvetica Neue", Helvetica, Arial, sans-serif; + font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif; } -div.cdm.expandable.Unread div.header a.title { +.cdm.expandable.Unread div.header a.title { color: black; } -div.cdm.expandable.active .collapse i.material-icons { +.cdm.expandable.active .collapse i.material-icons { color: #b87d2c; cursor: pointer; } -div.cdm.expandable.active .excerpt { +.cdm.expandable.active .excerpt { display: none; } -div.cdm.expandable.active div.header a.title { +.cdm.expandable.active div.header a.title { color: #b87d2c; font-size: 16px; font-weight: 600; text-rendering: optimizelegibility; - font-family: "Segoe WP Semibold", "Segoe UI Semibold", "Segoe UI Web Semibold", "Segoe UI", Ubuntu, "Helvetica Neue", Helvetica, Arial, sans-serif; + font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif; } -div.cdm.expandable:not(.active) { +.cdm.expandable:not(.active) { cursor: pointer; } -div.cdm.expandable:not(.active) .content, -div.cdm.expandable:not(.active) .collapse { +.cdm.expandable:not(.active) .content, +.cdm.expandable:not(.active) .collapse { display: none; } -div.cdm.expandable.active .header[stuck], -div.cdm.expanded .header[stuck] { +.cdm.expandable.active .header[data-is-stuck], +.cdm.expanded .header[data-is-stuck] { box-shadow: 0 1px 1px -1px rgba(0, 0, 0, 0.1); border: 0 solid #222; border-bottom-width: 1px; @@ -1433,7 +1490,7 @@ body.ttrss_prefs h1, body.ttrss_prefs h2, body.ttrss_prefs h3, body.ttrss_prefs h4 { - font-family: "Segoe WP Semibold", "Segoe UI Semibold", "Segoe UI Web Semibold", "Segoe UI", Ubuntu, "Helvetica Neue", Helvetica, Arial, sans-serif; + font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif; font-weight: 600; color: #ccc; } @@ -1504,14 +1561,6 @@ body.ttrss_prefs fieldset.prefs .help-text { body.ttrss_prefs fieldset.prefs .help-text-bottom { margin-top: 10px; } -body.ttrss_prefs fieldset.plugin label.description { - width: 550px; - margin-right: 150px; - display: inline-block; -} -body.ttrss_prefs fieldset.plugin label.description .dijitCheckBox { - margin-right: 10px; -} body.ttrss_prefs table th { text-align: left; } @@ -1539,17 +1588,26 @@ body.ttrss_prefs ul.prefs-plugin-list li > * { body.ttrss_prefs ul.prefs-plugin-list li label.checkbox { display: flex; align-items: center; - min-width: 300px; cursor: pointer; } body.ttrss_prefs ul.prefs-plugin-list li label.checkbox.system { cursor: auto; } body.ttrss_prefs ul.prefs-plugin-list li label.checkbox .name { + min-width: 300px; flex-grow: 2; display: inline-block; text-align: right; font-weight: bold; + margin-right: 20px; +} +@media (max-width: 992px) { + body.ttrss_prefs ul.prefs-plugin-list li label.checkbox .name { + min-width: 200px ! important; + } + body.ttrss_prefs ul.prefs-plugin-list li .version { + display: none; + } } body.ttrss_prefs ul.prefs-plugin-list li .actions { flex-grow: 2; @@ -1603,6 +1661,11 @@ body.ttrss_prefs .event-log tr .errno { body.ttrss_prefs .event-log tr .errstr { word-break: break-all; white-space: pre-wrap; + width: 30%; +} +body.ttrss_prefs .event-log tr .filename { + word-break: break-all; + width: 20%; } body.ttrss_prefs .event-log tr .filename, body.ttrss_prefs .event-log tr .login, @@ -1661,12 +1724,16 @@ body.ttrss_utility fieldset > label.checkbox { display: inline; font-weight: normal; } +.flat { + /*#feedTree { + .dijitTreeContent .dijitInline { + vertical-align : baseline; + } + }*/ +} .flat li { padding: 2px; } -.flat #feedTree .dijitTreeContent .dijitInline { - vertical-align: baseline; -} .flat .dijitButton i.material-icons { position: relative; top: -1px; @@ -1690,7 +1757,8 @@ body.ttrss_utility fieldset > label.checkbox { } .flat .dijitToolbar { font-size: 13px; - padding: 0px; + font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif; + padding: 0; } .flat .dijitToolbar .dijitTextBox .dijitInputContainer .dijitInputInner { line-height: 10px; @@ -1713,12 +1781,13 @@ body.ttrss_utility fieldset > label.checkbox { .flat .dijitMenu .dijitMenuItem .dijitMenuItemLabel { padding: 4px 8px; font-size: 13px; + font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif; } .flat .dijitMenu .dijitMenuItem.dijitDisabled:not(.dijitMenuItemSelected) .dijitMenuItemLabel { color: #d29745; } .flat .dijitMenu .dijitMenuItem td { - padding: 0px; + padding: 0; } .flat .dijitCheckBox { margin: 1px; @@ -1728,10 +1797,33 @@ body.ttrss_utility fieldset > label.checkbox { content: "\f00c"; color: white; } +.flat .dijitTab, +.flat .dijitAccordionTitle { + font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif; +} .flat .dijitTab i.material-icons, .flat .dijitAccordionInnerContainer:not(.dijitSelected) i.material-icons { color: #b87d2c; } +.flat .dijitTree { + /*.dijitTreeRow .dijitTreeExpando { + position : relative; + top : -2px; + }*/ +} +.flat .dijitTree .dijitTreeRowFlex { + display: flex; + flex-direction: row; + align-items: center; +} +.flat .dijitTree .dijitTreeRowFlex .dijitTreeIcon { + font-size: inherit; + height: auto; +} +.flat .dijitTree .dijitTreeRowFlex .dijitTreeExpando { + height: auto; + line-height: 0; +} .flat .dijitTree .dijitFolderClosed, .flat .dijitTree .dijitFolderOpened { display: none; @@ -1745,10 +1837,6 @@ body.ttrss_utility fieldset > label.checkbox { .flat .dijitTree .dijitTreeNode .dijitTreeRow.dijitTreeRowSelected { color: white; } -.flat .dijitTree .dijitTreeRow .dijitTreeExpando { - position: relative; - top: -2px; -} .flat .dijitTree .labelParam { float: right; margin-right: 16px; @@ -1777,6 +1865,7 @@ body.ttrss_utility fieldset > label.checkbox { .flat .dijitTree .dijitTreeRow { overflow: hidden; -moz-user-select: none; + user-select: none; text-overflow: ellipsis; } .flat label.dijitButton { @@ -1852,7 +1941,7 @@ body.ttrss_utility .content h2, body.ttrss_utility .content h3, body.ttrss_utility .content h4 { color: #b87d2c; - font-family: "Segoe WP Semibold", "Segoe UI Semibold", "Segoe UI Web Semibold", "Segoe UI", Ubuntu, "Helvetica Neue", Helvetica, Arial, sans-serif; + font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif; } body.ttrss_utility .content h2 { font-size: 18px; @@ -1871,7 +1960,7 @@ body.ttrss_utility a:focus { } body.ttrss_utility h1 { color: gray; - font-family: "Segoe WP Semibold", "Segoe UI Semibold", "Segoe UI Web Semibold", "Segoe UI", Ubuntu, "Helvetica Neue", Helvetica, Arial, sans-serif; + font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 18px; margin: 10px 0 0 0; } @@ -1940,11 +2029,11 @@ body.ttrss_zoom div.post .attachments { display: none; } body.ttrss_zoom div.post div.header { - padding-bottom: 10px; - border: 0px solid #222; + padding-bottom: 8px; + border: 0 solid #222; border-bottom-width: 1px; background: #333; - font-size: 12px; + font-size: 13px; color: #ccc; } body.ttrss_zoom div.post div.header .row { @@ -1954,32 +2043,36 @@ body.ttrss_zoom div.post div.header .row { align-items: center; justify-content: space-between; } -body.ttrss_zoom div.post div.content { +body.ttrss_zoom div.post div.header .row h1 { + margin-top: 0; + margin-bottom: 8px; +} +body.ttrss_zoom div.post .content { font-size: 15px; line-height: 1.5; border-width: 0; padding: 0; + padding-top: 8px; } -body.ttrss_zoom div.post div.content img, -body.ttrss_zoom div.post div.content video { +body.ttrss_zoom div.post .content img, +body.ttrss_zoom div.post .content video { max-width: 760px; height: auto; } -body.ttrss_zoom div.post div.content blockquote { +body.ttrss_zoom div.post .content blockquote { margin: 5px 0px 5px 0px; color: #ccc; - padding-left: 10px; + padding-left: 8px; border: 0px solid #222; border-left-width: 4px; } -body.ttrss_zoom div.post div.content code { +body.ttrss_zoom div.post .content code { color: #009900; font-family: monospace; font-size: 12px; } -body.ttrss_zoom div.post div.content pre { - margin: 5px 0px 5px 0px; - padding: 10px; +body.ttrss_zoom div.post .content pre { + padding: 8px; color: #ccc; font-family: monospace; font-size: 12px; @@ -2023,26 +2116,34 @@ body.flat.ttrss_main #feeds-holder { background: #222; box-shadow: inset -1px 0px 2px -1px #666; } -body.flat.ttrss_main #feeds-holder #feedTree .counterNode.aux, -body.flat.ttrss_main #feeds-holder #feedTree .counterNode.marked { +body.flat.ttrss_main #feeds-holder #feedTree .dijitTreeRow .counterNode.aux, +body.flat.ttrss_main #feeds-holder #feedTree .dijitTreeRow .counterNode.marked { background: #222; color: #ccc; border-color: #333; } -body.flat.ttrss_main #feeds-holder #feedTree .counterNode.marked { +body.flat.ttrss_main #feeds-holder #feedTree .dijitTreeRow .counterNode.marked { border-color: #b87d2c; } -body.flat.ttrss_main #feeds-holder #feedTree .dijitTreeRowSelected { +body.flat.ttrss_main #feeds-holder #feedTree .dijitTreeRow .dijitTreeRowSelected { background: #333; border-color: #333 transparent; color: #e6e6e6; } -body.flat.ttrss_main #feeds-holder #feedTree .dijitTreeRowSelected .dijitTreeLabel { +body.flat.ttrss_main #feeds-holder #feedTree .dijitTreeRow .dijitTreeRowSelected .dijitTreeLabel { text-shadow: none; } -body.flat.ttrss_main #feeds-holder #feedTree i.icon.icon-inbox { +body.flat.ttrss_main #feeds-holder #feedTree .dijitTreeRow i.icon.icon-inbox { color: #999999; } +body.flat.ttrss_main #feeds-holder #feedTree .dijitTreeRow .loadingNode { + filter: invert(1); +} +body.flat.ttrss_main #headlines-frame { + /*.cdm.expanded { + background: @default-bg; + }*/ +} body.flat.ttrss_main #headlines-frame .hl:not(.active):not(.Selected):not(.Unread), body.flat.ttrss_main #headlines-frame .cdm.expandable:not(.active):not(.Selected):not(.Unread) { background: #333; @@ -2051,9 +2152,6 @@ body.flat.ttrss_main #headlines-frame .hl.Unread:not(.active):not(.Selected), body.flat.ttrss_main #headlines-frame .cdm.expandable.Unread:not(.active):not(.Selected) { background: #222; } -body.flat.ttrss_main #headlines-frame .cdm.expanded { - background: #333; -} body.flat.ttrss_main #headlines-frame .hl.Unread .title, body.flat.ttrss_main #headlines-frame .cdm.Unread .title { color: #e6e6e6; @@ -2226,3 +2324,6 @@ body.flat.ttrss_main #filterNewRuleDlg .dijitValidationTextAreaError { body.flat.ttrss_main #filterNewRuleDlg .dijitValidationTextArea:not(.dijitValidationTextAreaError) { background: #305030; } +body.flat.ttrss_main .dijitDialog #feed_add_spinner { + filter: invert(1); +} diff --git a/themes/night_base.less b/themes/night_base.less index 76ea04a7f..a3945c856 100644 --- a/themes/night_base.less +++ b/themes/night_base.less @@ -9,11 +9,9 @@ @default-fg: @default-text; @border-default: #222; @default-bg : #333; - +@color-icon: #999; @border-light : #666; -@default-text: #ccc; - @fg-text-muted: darken(@default-text, 20%); @fg-light : lighten(@default-text, 10%); @@ -65,28 +63,34 @@ body.flat.ttrss_main { box-shadow : inset -1px 0px 2px -1px @border-light; #feedTree { - .counterNode.aux, .counterNode.marked { - background: @color-panel-bg; - color: @default-text; - border-color: @default-bg; - } + .dijitTreeRow { + .counterNode.aux, .counterNode.marked { + background: @color-panel-bg; + color: @default-text; + border-color: @default-bg; + } - .counterNode.marked { - border-color : @color-accent-marked; - } + .counterNode.marked { + border-color : @color-accent-marked; + } - .dijitTreeRowSelected { - background : @default-bg; - border-color : @default-bg transparent; - color : @fg-light; - } + .dijitTreeRowSelected { + background : @default-bg; + border-color : @default-bg transparent; + color : @fg-light; + } - .dijitTreeRowSelected .dijitTreeLabel { - text-shadow : none; - } + .dijitTreeRowSelected .dijitTreeLabel { + text-shadow : none; + } + + i.icon.icon-inbox { + color : darken(@default-text, 20%); + } - i.icon.icon-inbox { - color : darken(@default-text, 20%); + .loadingNode { + filter : invert(1); + } } } } @@ -102,9 +106,9 @@ body.flat.ttrss_main { background : @color-panel-bg; } - .cdm.expanded { + /*.cdm.expanded { background: @default-bg; - } + }*/ .hl.Unread .title, .cdm.Unread .title { @@ -324,4 +328,11 @@ body.flat.ttrss_main { background : #305030; } } + + .dijitDialog { + #feed_add_spinner { + filter : invert(1); + } + } + } diff --git a/themes/night_blue.css b/themes/night_blue.css index 8a3d7f8a5..60293f52c 100644 --- a/themes/night_blue.css +++ b/themes/night_blue.css @@ -16,67 +16,77 @@ body.ttrss_main { font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 14px; overflow: hidden; + /*ul.compact { + list-style-type : none; + margin : 0; + padding : 0; + + li { + margin : 0; + padding : 0; + } + }*/ } body.ttrss_main :focus { outline: none; } -body.ttrss_main div.post { - padding: 0px; +body.ttrss_main .post { + padding: 0; font-size: 13px; } -body.ttrss_main div.post div.header { +body.ttrss_main .post .header { padding: 5px; - color: #909090; - border: 0px solid #222; + color: #ccc; + border: 0 solid #222; border-bottom-width: 1px; background: #222; } -body.ttrss_main div.post div.header .left, -body.ttrss_main div.post div.header .right { +body.ttrss_main .post .header .left, +body.ttrss_main .post .header .right { display: flex; } -body.ttrss_main div.post div.header .row { +body.ttrss_main .post .header .row { display: flex; margin-bottom: 4px; flex-wrap: nowrap; align-items: center; justify-content: space-between; } -body.ttrss_main div.post div.header .comments { +body.ttrss_main .post .header .comments { flex-grow: 2; } -body.ttrss_main div.post div.header .date { +body.ttrss_main .post .header .date { white-space: nowrap; } -body.ttrss_main div.post div.header img, -body.ttrss_main div.post div.header i.material-icons { +body.ttrss_main .post .header img, +body.ttrss_main .post .header i.material-icons { margin: 0px 4px; vertical-align: middle; - color: #777; + color: #999; } -body.ttrss_main div.post div.header .title { +body.ttrss_main .post .header .title { flex-grow: 2; font-size: 15px; font-weight: 600; text-rendering: optimizelegibility; - font-family: "Segoe WP Semibold", "Segoe UI Semibold", "Segoe UI Web Semibold", "Segoe UI", Ubuntu, "Helvetica Neue", Helvetica, Arial, sans-serif; + font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif; } -body.ttrss_main div.post div.content { +body.ttrss_main .post div.content { padding: 10px; font-size: 16px; } -body.ttrss_main div.post div.content img, -body.ttrss_main div.post div.content video { +body.ttrss_main .post div.content img, +body.ttrss_main .post div.content video { border-width: 0px; max-width: 98%; height: auto; } -body.ttrss_main div.post div.content div.embed-responsive { +body.ttrss_main .post div.content div.embed-responsive { overflow: hidden; padding-bottom: 56.25%; position: relative; } -body.ttrss_main div.post div.content div.embed-responsive iframe { +body.ttrss_main .post div.content div.embed-responsive iframe { border: 0; bottom: 0; height: 100%; @@ -173,6 +183,11 @@ body.ttrss_main .notify.notify_progress { border-color: #d7c47a; background-color: #fff7d5; } +body.ttrss_main .notify.notify_progress img { + width: 18px; + height: 18px; + filter: saturate(0); +} body.ttrss_main .notify.notify_info { border-color: #257aa7; background-color: #d5ebf6; @@ -218,6 +233,10 @@ body.ttrss_main .hl .right { display: flex; align-items: center; } +body.ttrss_main .hl .left img, +body.ttrss_main .hl .right img { + margin: 0 4px; +} body.ttrss_main .hl .left i.material-icons, body.ttrss_main .hl .right i.material-icons { margin-left: 2px; @@ -226,25 +245,28 @@ body.ttrss_main .hl .right i.material-icons { user-select: none; font-size: 21px; } +body.ttrss_main .hl .left input { + margin: 0 4px; +} +body.ttrss_main .hl .right { + text-align: right; +} body.ttrss_main .hl .right i.material-icons { - color: #777; + color: #999; } -body.ttrss_main .hl div.title { +body.ttrss_main .hl .title { cursor: pointer; flex-grow: 2; overflow: hidden; text-overflow: ellipsis; } -body.ttrss_main .hl span.author { +body.ttrss_main .hl .author { white-space: nowrap; color: #ccc; font-size: 11px; font-weight: normal; } -body.ttrss_main .hl div.right { - text-align: right; -} -body.ttrss_main .hl span.feed a { +body.ttrss_main .hl .feed a { border-radius: 4px; display: inline-block; padding: 1px 4px; @@ -253,29 +275,22 @@ body.ttrss_main .hl span.feed a { font-weight: normal; color: #ccc; } -body.ttrss_main .hl span.feed a:hover { +body.ttrss_main .hl .feed a:hover { color: #257aa7; } -body.ttrss_main .hl span.updated { +body.ttrss_main .hl .updated { color: #ccc; text-align: right; font-size: 11px; padding-left: 10px; } -body.ttrss_main .hl span.updated div { +body.ttrss_main .hl .updated div { display: inline-block; } -body.ttrss_main .hl div.left input { - margin: 0px 4px; -} -body.ttrss_main .hl div.left img, -body.ttrss_main .hl div.right img { - margin: 0px 4px; -} body.ttrss_main .hl div.title a { font-weight: 600; text-rendering: optimizelegibility; - font-family: "Segoe UI", Ubuntu, "Helvetica Neue", Helvetica, Arial, sans-serif; + font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif; color: #555; } body.ttrss_main .hl a.title.high, @@ -288,10 +303,10 @@ body.ttrss_main .hl.vgrlf .feed { body.ttrss_main .hl.Unread { background: white; } -body.ttrss_main .hl.Unread div.title a { +body.ttrss_main .hl.Unread .title a { color: black; } -body.ttrss_main .hl.active div.title a { +body.ttrss_main .hl.active .title a { color: #257aa7; /* text-shadow : 1px 1px 2px #fff; */ } @@ -313,9 +328,6 @@ body.ttrss_main .hl.active span, body.ttrss_main .hl.Selected span { color: white; } -body.ttrss_main .hl.Grayed { - color: #909090; -} body.ttrss_main #content-insert blockquote, body.ttrss_main #headlines-frame blockquote, body.ttrss_main .dijitContentPane blockquote { @@ -372,45 +384,6 @@ body.ttrss_main i.pub-pic { cursor: pointer; color: #ccc; } -body.ttrss_main div.errorExplained { - border: 1px solid #222; - margin: 5px 0px 5px 0px; - padding: 5px; -} -body.ttrss_main ul.browseFeedList { - height: 300px; - width: 100%; - overflow: auto; - border-width: 0px 1px 1px 1px; - border-color: #222; - border-style: solid; - margin: 0px 0px 5px 0px; - background-color: white; - list-style-type: none; - padding: 0px; -} -body.ttrss_main ul.browseFeedList li { - display: flex; - align-items: center; -} -body.ttrss_main ul.browseFeedList li > * { - margin: 2px; -} -body.ttrss_main .browseFeedList span.subscribers { - color: #808080; -} -body.ttrss_main ul.compact { - list-style-type: none; - margin: 0px; - padding: 0px; -} -body.ttrss_main ul.compact li { - margin: 0px; - padding: 0px; -} -body.ttrss_main .noborder { - border-width: 0px; -} body.ttrss_main #overlay { background: #333; left: 0; @@ -424,11 +397,6 @@ body.ttrss_main #overlay_inner { font-weight: bold; margin: 1em; } -body.ttrss_main div.loadingPrompt { - padding: 1em; - text-align: center; - font-weight: bold; -} body.ttrss_main div.whiteBox { margin-left: 1px; text-align: center; @@ -437,20 +405,13 @@ body.ttrss_main div.whiteBox { border: 0px solid #222; border-bottom-width: 1px; } -body.ttrss_main div#headlines-frame.wide .title { - overflow: visible; - white-space: normal; -} -body.ttrss_main div#headlines-frame.wide .hl .feed { - display: none; -} body.ttrss_main .dijitDialog header, body.ttrss_main .dijitDialog .dlgSec, body.ttrss_main .dijitDialog .dlgSecHoriz { font-size: 16px; font-weight: 600; color: #ccc; - font-family: "Segoe WP Semibold", "Segoe UI Semibold", "Segoe UI Web Semibold", "Segoe UI", Ubuntu, "Helvetica Neue", Helvetica, Arial, sans-serif; + font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif; } body.ttrss_main .dijitDialog section, body.ttrss_main .dijitDialog .dlgSecCont { @@ -528,11 +489,6 @@ body.ttrss_main div#cmdline { padding: 3px 5px 3px 5px; z-index: 5; } -body.ttrss_main #feed_browser_spinner { - vertical-align: middle; - height: 18px; - width: 18px; -} body.ttrss_main .exception-contents h3 { color: red; } @@ -541,14 +497,15 @@ body.ttrss_main .exception-contents textarea { height: 200px; font-size: 11px; } +body.ttrss_main #headlines-wrap-inner, body.ttrss_main #content-wrap { - padding: 0px; - border-width: 0px; - margin: 0px; + padding: 0; + border: 0; + margin: 0; } body.ttrss_main #feeds-holder { - padding: 0px; - border: 0px solid #222; + padding: 0; + border: 0 solid #222; overflow: hidden; background: #222; box-shadow: inset -1px 0px 2px -1px rgba(0, 0, 0, 0.1); @@ -558,19 +515,26 @@ body.ttrss_main #feeds-holder #feedTree { height: 100%; overflow-x: hidden; text-rendering: optimizelegibility; - font-family: "Segoe UI", Ubuntu, "Helvetica Neue", Helvetica, Arial, sans-serif; + font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif; } -body.ttrss_main #feeds-holder #feedTree .counterNode.aux, -body.ttrss_main #feeds-holder #feedTree .counterNode.marked { - background: #222; - color: #e6e6e6; - border-color: #080808; +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow { + border: 1px solid transparent; } -body.ttrss_main #feeds-holder #feedTree .counterNode.marked { - border-color: #257aa7; - background: #ffffff; +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow.Is_Feed .loadingNode { + left: -2px; + height: 14px; + width: 14px; + position: relative; +} +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow.Is_Cat .loadingNode { + height: 11px; + width: 18px; + margin-left: 4px; + position: relative; + top: 2px; } -body.ttrss_main #feeds-holder #feedTree .counterNode { +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow .counterNode { + order: 2; font-weight: bold; display: none; font-size: 9px; @@ -579,111 +543,215 @@ body.ttrss_main #feeds-holder #feedTree .counterNode { color: white; background: #2a89bc; border-radius: 4px; - vertical-align: middle; - float: right; - position: relative; line-height: 14px; margin-right: 8px; - margin-top: 2px; min-width: 23px; height: 14px; + flex-shrink: 0; +} +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow .counterNode.aux, +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow .counterNode.marked { + background: #222; + color: #e6e6e6; + border-color: #080808; +} +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow .counterNode.marked { + border-color: #257aa7; + background: #ffffff; +} +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow[data-feed-id="-3"][data-is-cat="false"] .counterNode.unread { + background-color: #3ea447; + border-color: #307f37; +} +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow .dijitTreeContent { + display: flex; + align-items: center; + flex-grow: 2; + min-width: 0; } -body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .loadingExpando { - left: -3px; - height: 22px; +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow .dijitTreeLabel { + cursor: pointer; + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; position: relative; - top: -3px; } -body.ttrss_main #feeds-holder #feedTree .dijitTreeRow .dijitTreeLabel.Unread { +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow .dijitTreeLabel.Unread { font-weight: bold; } -body.ttrss_main #feeds-holder #feedTree .dijitTreeRow.Error .dijitTreeLabel { +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow.Error .dijitTreeLabel { color: red; } -body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow { - border: 1px solid transparent; -} -body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRowSelected { +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow.dijitTreeRowSelected { box-shadow: -1px 0px 2px -1px rgba(0, 0, 0, 0.1); border-color: #222 transparent; background: #333; - color: #333; + color: #ccc; } -body.ttrss_main #feeds-holder #feedTree .dijitIcon.feed-icon { +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow .dijitIcon.feed-icon { margin-right: 2px; } -body.ttrss_main #feeds-holder #feedTree i.icon.icon-inbox { - color: #555; +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow i.icon.icon-inbox { + color: #ffffff; } -body.ttrss_main #feeds-holder #feedTree i.icon.icon-archive { - color: #c77b2e; +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow i.icon.icon-archive { + color: #cf7800; } -body.ttrss_main #feeds-holder #feedTree i.icon.icon-star { +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow i.icon.icon-star { position: relative; color: #ffc069; font-size: 21px; left: -2px; } -body.ttrss_main #feeds-holder #feedTree i.icon.icon-rss_feed { +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow i.icon.icon-rss_feed { color: #ff7c4b; } -body.ttrss_main #feeds-holder #feedTree i.icon.icon-whatshot { +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow i.icon.icon-whatshot { color: #69C671; } -body.ttrss_main #feeds-holder #feedTree i.icon.icon-restore { - position: relative; - top: -1px; +body.ttrss_main #feeds-holder #feedTree .dijitTreeNode .dijitTreeRow i.icon.icon-restore { font-weight: bold; color: #257aa7; } -body.ttrss_main #headlines-wrap-inner { - padding: 0px; - margin: 0px; - border-width: 0px; +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(600px, 1fr)); + padding: 8px; + grid-gap: 8px; + background-color: #222; +} +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] > * { + /* 2 = because #headlines-spacer is the actual last child + only if odd to deal with 1) single article and 2) not break any previous rows; + 1 = spacer; + + this is outside of .cdm selector because of #headlines-spacer etc + + .grid-span-row is manually expanded RROWs + */ +} +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] > *.grid-span-row, +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] > *:nth-last-child(2):nth-child(odd), +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] > *:nth-last-child(1) { + grid-column: 1 / -1; +} +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] .cdm.expanded .header, +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] .cdm.expanded .content { + background: #333; + border: 1px solid #222; + overflow: hidden; +} +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] .cdm.expanded .content { + border-top-width: 0; + padding: 0 4px 4px 4px; +} +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] .cdm.expanded .header[data-is-stuck] { + top: -8px; + border-bottom-width: 1px; +} +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] .cdm.expanded .header { + border-bottom-width: 0; + padding: 4px; +} +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] .cdm.expanded .header .icon-grid-span { + display: inline; } -body.ttrss_main #headlines-frame[is-vfeed="0"] .header .feed { +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] .cdm.expanded .header .feed { display: none; } +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] .cdm.expanded .footer { + border: 0; + padding: 4px; +} +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] .cdm.expanded .footer .left, +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] .cdm.expanded .footer .right { + white-space: nowrap; +} +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] .cdm.expanded .footer .left { + overflow: hidden; + text-overflow: ellipsis; +} +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] .cdm.expanded .content-inner a, +body.ttrss_main #headlines-frame:not([data-headlines-count="0"])[data-is-cdm="true"][data-is-cdm-expanded="true"][data-enable-grid="true"] .cdm.expanded .intermediate a { + word-break: break-all; +} body.ttrss_main #headlines-frame { - padding: 0px; - border: 0px #222; - margin-top: 0px; + padding: 0; + border: 0; + margin-top: 0; -webkit-overflow-scrolling: touch; -webkit-transform: translateZ(0); + transform: translateZ(0); -webkit-backface-visibility: hidden; + backface-visibility: hidden; +} +body.ttrss_main #headlines-frame[data-is-vfeed="false"] .header .feed, +body.ttrss_main #headlines-frame[data-is-vfeed="false"] .hl .feed, +body.ttrss_main #headlines-frame[data-is-vfeed="false"] .header .icon-feed, +body.ttrss_main #headlines-frame[data-is-vfeed="false"] .hl .icon-feed { + display: none; +} +body.ttrss_main #headlines-frame[data-auto-catchup="true"] #headlines-spacer { + height: 100vh; +} +body.ttrss_main #headlines-frame .dijitCheckBox { + margin-right: 4px; +} +body.ttrss_main #headlines-frame[data-is-wide-screen="true"] .title { + overflow: visible; + white-space: normal; } -body.ttrss_main #headlines-frame div.feed-title { +body.ttrss_main #headlines-frame[data-is-wide-screen="true"] .hl .feed { + display: none; +} +body.ttrss_main #headlines-frame #headlines-spacer { + margin-left: 1px; + text-align: center; + color: #ccc; + font-size: 11px; +} +body.ttrss_main #headlines-frame #headlines-spacer a, +body.ttrss_main #headlines-frame #headlines-spacer span { + color: #ccc; + padding: 10px; + display: block; +} +body.ttrss_main #headlines-frame #headlines-spacer a:hover { + color: #257aa7; +} +body.ttrss_main #headlines-frame .feed-title { border: 0px solid #257aa7; border-bottom-width: 1px; padding: 5px 8px; } -body.ttrss_main #headlines-frame div.feed-title a.title { +body.ttrss_main #headlines-frame .feed-title a.title { color: #ccc; font-weight: bold; } -body.ttrss_main #headlines-frame div.feed-title a { +body.ttrss_main #headlines-frame .feed-title a { color: #ccc; } -body.ttrss_main #headlines-frame div.feed-title a:hover { +body.ttrss_main #headlines-frame .feed-title a:hover { color: #257aa7; } body.ttrss_main #headlines-frame span.hlMenuAttach { -webkit-touch-callout: none; -webkit-user-select: none; + user-select: none; } body.ttrss_main #toolbar-frame_splitter { display: none; } body.ttrss_main #toolbar-frame { - padding: 0px; - margin: 0px; - border-width: 0px; + padding: 0; + margin: 0; + border: 0; white-space: nowrap; - font-size: 12px; + font-size: 13px; } body.ttrss_main #toolbar-frame #toolbar { background: white; - border: 0px solid #222; + border: 0 solid #222; border-bottom-width: 1px; padding-left: 4px; height: 32px; @@ -691,13 +759,13 @@ body.ttrss_main #toolbar-frame #toolbar { flex-direction: row; flex-wrap: nowrap; color: #ccc; - font-size: 12px; + font-size: 13px; align-items: center; } body.ttrss_main #toolbar-frame #toolbar .dijitSelect, body.ttrss_main #toolbar-frame #toolbar .dijitDropDownButton .dijitButtonNode, body.ttrss_main #toolbar-frame #toolbar .dijitComboButton .dijitButtonNode { - border: 0px; + border: 0; } body.ttrss_main #toolbar-frame #toolbar i.net-alert, body.ttrss_main #toolbar-frame #toolbar .left i.icon-error { @@ -711,7 +779,7 @@ body.ttrss_main #toolbar-frame #toolbar i { margin: 0 4px; } body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines { - font-size: 12px; + font-size: 13px; background: transparent; padding-right: 4px; flex-grow: 2; @@ -726,6 +794,12 @@ body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines .left .feed_title, body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines .left .cancel_search { margin-left: 4px; } +@media (max-width: 768px) { + body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines .left .feed_title, + body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines .left i.icon-syndicate { + display: none; + } +} body.ttrss_main #toolbar-frame #toolbar #toolbar-headlines .right { display: flex; align-items: center; @@ -740,20 +814,26 @@ body.ttrss_main #toolbar-frame #toolbar #selected_prompt { margin-right: 4px; color: #257aa7; } -@media (max-width: 992px) { +@media (max-width: 768px) { body.ttrss_main #toolbar-frame #toolbar #selected_prompt { display: none; } } +@media (max-width: 576px) { + body.ttrss_main #toolbar-frame #toolbar .select-articles-dropdown, + body.ttrss_main #toolbar-frame #toolbar .catchup-button { + display: none; + } +} body.ttrss_main #header { - border-width: 0px; text-align: right; color: #ccc; - padding: 5px 5px 0px 0px; - margin: 0px; + padding: 5px 5px 0 0px; position: absolute; - right: 0px; - top: 0px; + border: 0; + margin: 0; + right: 0; + top: 0; z-index: 5; } body.ttrss_main #header i.net-alert, @@ -772,9 +852,8 @@ body.ttrss_main #header i { margin: 0 4px; } body.ttrss_main #content-insert { - padding: 0px; - border-color: #222; - border-width: 0px; + padding: 0; + border: 0; line-height: 1.5; overflow: auto; -webkit-overflow-scrolling: touch; @@ -787,59 +866,11 @@ body.ttrss_main img.icon { vertical-align: middle; display: inline-block; } -body.ttrss_main .player { - display: inline-block; - color: #ccc; - font-size: 11px; - font-family: sans-serif; - border: 1px solid #ccc; - padding: 0px 4px 0px 4px; - margin: 0px 2px 0px 2px; - width: 50px; - text-align: center; - background: #333; -} -body.ttrss_main .player.playing { - color: #00c000; - border-color: #00c000; -} -body.ttrss_main .player:hover { - background: #222; - cursor: pointer; -} -body.ttrss_main #headlines-frame.auto_catchup #headlines-spacer { - height: 100%; -} -body.ttrss_main #headlines-spacer { - margin-left: 1px; - text-align: center; - color: #ccc; - font-size: 11px; - font-style: italic; -} -body.ttrss_main #headlines-spacer a, -body.ttrss_main #headlines-spacer span { - color: #ccc; - padding: 10px; - display: block; -} -body.ttrss_main #headlines-spacer a:hover { - color: #257aa7; -} body.ttrss_main ul#filterDlg_Matches, body.ttrss_main ul#filterDlg_Actions { list-style-type: none; margin: 0; padding: 0; - /*max-height : 100px; - overflow : auto; - border-style : solid; - border-color : @border-default; - border-width : 1px 1px 1px 1px; - background-color : @default-bg; - margin : 0px 0px 5px 0px; - padding : 4px; - min-height : 16px;*/ } body.ttrss_main ul#filterDlg_Matches li, body.ttrss_main ul#filterDlg_Actions li { @@ -875,9 +906,6 @@ body.ttrss_main span.highlight { background-color: #ffff00; color: #cc90cc; } -body.ttrss_main #headlines-frame .dijitCheckBox { - margin-right: 4px; -} body.ttrss_main #feedEditDlg img.feedIcon { border: 1px solid #ccc; padding: 5px; @@ -935,6 +963,11 @@ body.ttrss_main:not([view-mode="marked"])[hide-read-feeds="true"][hide-read-show body.ttrss_main:not([view-mode="marked"])[hide-read-feeds="true"][hide-read-shows-special="false"] #feeds-holder #feedTree .dijitTreeRow:not(.Unread):not(.AlwaysVisible) { display: none; } +body.ttrss_main { + /*.score-neutral i.icon-score { + opacity : 0.5; + }*/ +} body.ttrss_main #toolbar-headlines i.icon-syndicate { color: #ff7c4b; margin-right: 8px; @@ -971,10 +1004,15 @@ body.ttrss_main .score-high i.icon-score { body.ttrss_main .score-low i.icon-score { color: #500; } -body.ttrss_main .score-neutral i.icon-score { - opacity: 0.5; +body.ttrss_main i.icon-score, +body.ttrss_main i.icon-grid-span { + cursor: pointer; + color: #999; } -body.ttrss_main i.icon-score { +body.ttrss_main .icon-grid-span { + display: none; +} +body.ttrss_main .icon-feed { cursor: pointer; } body.ttrss_main .panel { @@ -1005,20 +1043,62 @@ body.ttrss_main ul.list-unstyled { body.ttrss_main .text-center { text-align: center; } -body.ttrss_main #prefFilterTestResultList .preview { +body.ttrss_main .text-right { + text-align: right; +} +body.ttrss_main .text-left { + text-align: left; +} +body.ttrss_main .dijitDialog .filter-results-list .preview { margin: 8px; } -body.ttrss_main #prefFilterTestResultList .title { +body.ttrss_main .dijitDialog .filter-results-list .title { font-weight: bold; } -body.ttrss_main #prefFilterTestResultList .feed { - color: #257aa7; +body.ttrss_main .dijitDialog #feed_add_spinner { + position: relative; + top: 5px; + width: 18px; + height: 18px; +} +body.ttrss_main .icon-three-dots { + width: 18px; + height: 18px; + vertical-align: middle; +} +body.ttrss_main, +body.ttrss_utility { + /*div.autocomplete { + position : absolute; + width : 250px; + background-color : @default-bg; + border :1px solid @border-default; + margin : 0px; + padding : 0px; + + ul { + list-style-type : none; + margin : 0px; + padding : 0px; + } + + ul li.selected { + background-color : darken(@default-bg, 10%); + } + + ul li { + list-style-type : none; + display : block; + margin : 0; + padding : 2px; + cursor : pointer; + } + }*/ } body.ttrss_main .alert, body.ttrss_utility .alert { padding: 8px 35px 8px 14px; margin-bottom: 10px; - /* text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); */ background-color: #fcf8e3; border: 1px solid #fbeed5; border-radius: 4px; @@ -1059,6 +1139,16 @@ body.ttrss_main .text-warning, body.ttrss_utility .text-warning { color: #a47e3c; } +body.ttrss_main .text-muted, +body.ttrss_utility .text-muted { + color: #ccc; +} +body.ttrss_main .text-small, +body.ttrss_utility .text-small, +body.ttrss_main .small, +body.ttrss_utility .small { + font-size: 11px; +} body.ttrss_main .alert, body.ttrss_utility .alert, body.ttrss_main .alert h4, @@ -1108,41 +1198,6 @@ body.ttrss_utility hr { border: 0px solid #ccc; border-bottom-width: 1px; } -body.ttrss_main .text-muted, -body.ttrss_utility .text-muted { - color: #ccc; -} -body.ttrss_main .small, -body.ttrss_utility .small { - font-size: 11px; -} -body.ttrss_main div.autocomplete, -body.ttrss_utility div.autocomplete { - position: absolute; - width: 250px; - background-color: #333; - border: 1px solid #222; - margin: 0px; - padding: 0px; -} -body.ttrss_main div.autocomplete ul, -body.ttrss_utility div.autocomplete ul { - list-style-type: none; - margin: 0px; - padding: 0px; -} -body.ttrss_main div.autocomplete ul li.selected, -body.ttrss_utility div.autocomplete ul li.selected { - background-color: #1a1a1a; -} -body.ttrss_main div.autocomplete ul li, -body.ttrss_utility div.autocomplete ul li { - list-style-type: none; - display: block; - margin: 0; - padding: 2px; - cursor: pointer; -} ::selection { background: #257aa7; color: #333; @@ -1156,16 +1211,13 @@ body.ttrss_utility div.autocomplete ul li { ::-webkit-scrollbar-track { background-color: #eee; } -video::-webkit-media-controls-overlay-play-button { - display: none; -} -.cdm i.material-icons { - color: #777; -} -.cdm .header { - position: sticky; - top: 0; - z-index: 3; +/*video::-webkit-media-controls-overlay-play-button { + display: none; +}*/ +.cdm { + /*i.material-icons { + color : @color-icon; + }*/ } .cdm .header, .cdm .footer { @@ -1176,13 +1228,16 @@ video::-webkit-media-controls-overlay-play-button { .cdm .header img, .cdm .footer img, .cdm .footer i.material-icons { - margin: 0px 4px; + margin: 0 4px; vertical-align: middle; } .cdm .header-sticky-guard { height: 0; } .cdm .header { + position: sticky; + top: 0; + z-index: 3; align-items: center; } .cdm .header > * { @@ -1205,7 +1260,7 @@ video::-webkit-media-controls-overlay-play-button { .cdm .header .titleWrap { flex-grow: 2; } -.cdm .header span.updated { +.cdm .header .updated { color: #ccc; font-weight: normal; font-size: 11px; @@ -1214,6 +1269,30 @@ video::-webkit-media-controls-overlay-play-button { .cdm .header input { margin: 0px 4px; } +.cdm .header .feed { + float: right; + font-weight: normal; + font-style: italic; +} +.cdm .header .feed a { + border-radius: 4px; + display: inline-block; + padding: 1px 4px 1px 4px; +} +.cdm .header .feed, +.cdm .header .feed a { + vertical-align: middle; + color: #ccc; + font-weight: normal; + font-style: italic; + font-size: 11px; +} +.cdm .header .author { + white-space: nowrap; + color: #ccc; + font-size: 11px; + font-weight: normal; +} .cdm .footer { height: 30px; padding-left: 5px; @@ -1222,30 +1301,37 @@ video::-webkit-media-controls-overlay-play-button { clear: both; align-items: center; } +.cdm .footer i.material-icons { + color: #999; +} .cdm .footer .left { flex-grow: 2; } .cdm .intermediate { margin-top: 10px; margin-left: 10px; + line-height: 1.5; } .cdm .content-inner { margin: 10px; line-height: 1.5; font-size: 16px; } +.cdm .intermediate iframe, +.cdm .content-inner iframe { + max-width: 98%; + width: auto; + height: auto; +} .cdm .intermediate img, .cdm .intermediate video, .cdm .content-inner img, .cdm .content-inner video { border-width: 0px; max-width: 98%; + width: auto; height: auto; } -.cdm.expanded { - /*margin-top : 4px; - margin-bottom : 4px;*/ -} .cdm.expanded .collapse, .cdm.expanded .excerpt { display: none; @@ -1258,72 +1344,54 @@ video::-webkit-media-controls-overlay-play-button { border-bottom-width: 1px; } .cdm.expanded > hr { - margin-top: 0px; - margin-bottom: 0px; + margin-top: 0; + margin-bottom: 0; } -div.cdm.expanded div.header a.title { +.cdm.expanded .header a.title { font-size: 16px; color: #999; font-weight: 600; transition: color 0.2s, background 0.2s; text-rendering: optimizelegibility; - font-family: "Segoe WP Semibold", "Segoe UI Semibold", "Segoe UI Web Semibold", "Segoe UI", Ubuntu, "Helvetica Neue", Helvetica, Arial, sans-serif; + font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif; } -div.cdm.expanded.active { - background: white; +.cdm.expanded.active .content, +.cdm.expanded.Unread .content { + color: black; } -div.cdm.expanded.active div.header a.title { +.cdm.expanded.active .header .title { color: #257aa7; } -div.cdm.expanded.Unread div.header a.title { +.cdm.expanded.Unread .header .title { color: black; } -div.cdm.expanded div.content { +.cdm.expanded .content { color: #ccc; } -div.cdm.expanded.Unread div.content { - color: black; -} -div.cdm.active div.content { - color: black; -} -div.cdm.vgrlf .feed { +.cdm.vgrlf .feed { display: none; } -.cdm div.feed-title { +.cdm .feed-title { border: 0px solid #257aa7; border-bottom-width: 1px; padding: 5px 3px 5px 5px; } -.cdm div.feed-title a.title { +.cdm .feed-title a.title { color: #ccc; font-weight: bold; } -.cdm div.feed-title a { +.cdm .feed-title a { color: #ccc; } -.cdm div.feed-title a:hover { +.cdm .feed-title a:hover { color: #257aa7; } -.cdm div.header span.feed { - float: right; - font-weight: normal; - font-style: italic; -} -.cdm div.header div.feed, -.cdm div.header div.feed a { - vertical-align: middle; - color: #ccc; - font-weight: normal; - font-style: italic; - font-size: 11px; -} -.cdm div.content-inner div.embed-responsive { +.cdm .content-inner .embed-responsive { overflow: hidden; padding-bottom: 56.25%; position: relative; } -.cdm div.content-inner div.embed-responsive iframe { +.cdm .content-inner .embed-responsive iframe { border: 0; bottom: 0; height: 100%; @@ -1332,17 +1400,6 @@ div.cdm.vgrlf .feed { top: 0; width: 100%; } -.cdm div.header span.author { - white-space: nowrap; - color: #ccc; - font-size: 11px; - font-weight: normal; -} -.cdm .feed a { - border-radius: 4px; - display: inline-block; - padding: 1px 4px 1px 4px; -} .cdm.expandable { background-color: #222; border: 0px solid #222; @@ -1380,43 +1437,43 @@ div.cdm.vgrlf .feed { .cdm.expandable.active { background: white ! important; } -div.cdm.expandable.active div.header span.titleWrap { +.cdm.expandable.active div.header span.titleWrap { white-space: normal; } -div.cdm.expandable div.header a.title { +.cdm.expandable .header a.title { font-weight: 600; color: #ccc; font-size: 14px; transition: color 0.2s, background 0.2s; text-rendering: optimizelegibility; - font-family: "Segoe WP Semibold", "Segoe UI Semibold", "Segoe UI Web Semibold", "Segoe UI", Ubuntu, "Helvetica Neue", Helvetica, Arial, sans-serif; + font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif; } -div.cdm.expandable.Unread div.header a.title { +.cdm.expandable.Unread div.header a.title { color: black; } -div.cdm.expandable.active .collapse i.material-icons { +.cdm.expandable.active .collapse i.material-icons { color: #257aa7; cursor: pointer; } -div.cdm.expandable.active .excerpt { +.cdm.expandable.active .excerpt { display: none; } -div.cdm.expandable.active div.header a.title { +.cdm.expandable.active div.header a.title { color: #257aa7; font-size: 16px; font-weight: 600; text-rendering: optimizelegibility; - font-family: "Segoe WP Semibold", "Segoe UI Semibold", "Segoe UI Web Semibold", "Segoe UI", Ubuntu, "Helvetica Neue", Helvetica, Arial, sans-serif; + font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif; } -div.cdm.expandable:not(.active) { +.cdm.expandable:not(.active) { cursor: pointer; } -div.cdm.expandable:not(.active) .content, -div.cdm.expandable:not(.active) .collapse { +.cdm.expandable:not(.active) .content, +.cdm.expandable:not(.active) .collapse { display: none; } -div.cdm.expandable.active .header[stuck], -div.cdm.expanded .header[stuck] { +.cdm.expandable.active .header[data-is-stuck], +.cdm.expanded .header[data-is-stuck] { box-shadow: 0 1px 1px -1px rgba(0, 0, 0, 0.1); border: 0 solid #222; border-bottom-width: 1px; @@ -1433,7 +1490,7 @@ body.ttrss_prefs h1, body.ttrss_prefs h2, body.ttrss_prefs h3, body.ttrss_prefs h4 { - font-family: "Segoe WP Semibold", "Segoe UI Semibold", "Segoe UI Web Semibold", "Segoe UI", Ubuntu, "Helvetica Neue", Helvetica, Arial, sans-serif; + font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif; font-weight: 600; color: #ccc; } @@ -1504,14 +1561,6 @@ body.ttrss_prefs fieldset.prefs .help-text { body.ttrss_prefs fieldset.prefs .help-text-bottom { margin-top: 10px; } -body.ttrss_prefs fieldset.plugin label.description { - width: 550px; - margin-right: 150px; - display: inline-block; -} -body.ttrss_prefs fieldset.plugin label.description .dijitCheckBox { - margin-right: 10px; -} body.ttrss_prefs table th { text-align: left; } @@ -1539,17 +1588,26 @@ body.ttrss_prefs ul.prefs-plugin-list li > * { body.ttrss_prefs ul.prefs-plugin-list li label.checkbox { display: flex; align-items: center; - min-width: 300px; cursor: pointer; } body.ttrss_prefs ul.prefs-plugin-list li label.checkbox.system { cursor: auto; } body.ttrss_prefs ul.prefs-plugin-list li label.checkbox .name { + min-width: 300px; flex-grow: 2; display: inline-block; text-align: right; font-weight: bold; + margin-right: 20px; +} +@media (max-width: 992px) { + body.ttrss_prefs ul.prefs-plugin-list li label.checkbox .name { + min-width: 200px ! important; + } + body.ttrss_prefs ul.prefs-plugin-list li .version { + display: none; + } } body.ttrss_prefs ul.prefs-plugin-list li .actions { flex-grow: 2; @@ -1603,6 +1661,11 @@ body.ttrss_prefs .event-log tr .errno { body.ttrss_prefs .event-log tr .errstr { word-break: break-all; white-space: pre-wrap; + width: 30%; +} +body.ttrss_prefs .event-log tr .filename { + word-break: break-all; + width: 20%; } body.ttrss_prefs .event-log tr .filename, body.ttrss_prefs .event-log tr .login, @@ -1661,12 +1724,16 @@ body.ttrss_utility fieldset > label.checkbox { display: inline; font-weight: normal; } +.flat { + /*#feedTree { + .dijitTreeContent .dijitInline { + vertical-align : baseline; + } + }*/ +} .flat li { padding: 2px; } -.flat #feedTree .dijitTreeContent .dijitInline { - vertical-align: baseline; -} .flat .dijitButton i.material-icons { position: relative; top: -1px; @@ -1690,7 +1757,8 @@ body.ttrss_utility fieldset > label.checkbox { } .flat .dijitToolbar { font-size: 13px; - padding: 0px; + font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif; + padding: 0; } .flat .dijitToolbar .dijitTextBox .dijitInputContainer .dijitInputInner { line-height: 10px; @@ -1713,12 +1781,13 @@ body.ttrss_utility fieldset > label.checkbox { .flat .dijitMenu .dijitMenuItem .dijitMenuItemLabel { padding: 4px 8px; font-size: 13px; + font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif; } .flat .dijitMenu .dijitMenuItem.dijitDisabled:not(.dijitMenuItemSelected) .dijitMenuItemLabel { color: #2e99d1; } .flat .dijitMenu .dijitMenuItem td { - padding: 0px; + padding: 0; } .flat .dijitCheckBox { margin: 1px; @@ -1728,10 +1797,33 @@ body.ttrss_utility fieldset > label.checkbox { content: "\f00c"; color: white; } +.flat .dijitTab, +.flat .dijitAccordionTitle { + font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif; +} .flat .dijitTab i.material-icons, .flat .dijitAccordionInnerContainer:not(.dijitSelected) i.material-icons { color: #257aa7; } +.flat .dijitTree { + /*.dijitTreeRow .dijitTreeExpando { + position : relative; + top : -2px; + }*/ +} +.flat .dijitTree .dijitTreeRowFlex { + display: flex; + flex-direction: row; + align-items: center; +} +.flat .dijitTree .dijitTreeRowFlex .dijitTreeIcon { + font-size: inherit; + height: auto; +} +.flat .dijitTree .dijitTreeRowFlex .dijitTreeExpando { + height: auto; + line-height: 0; +} .flat .dijitTree .dijitFolderClosed, .flat .dijitTree .dijitFolderOpened { display: none; @@ -1745,10 +1837,6 @@ body.ttrss_utility fieldset > label.checkbox { .flat .dijitTree .dijitTreeNode .dijitTreeRow.dijitTreeRowSelected { color: white; } -.flat .dijitTree .dijitTreeRow .dijitTreeExpando { - position: relative; - top: -2px; -} .flat .dijitTree .labelParam { float: right; margin-right: 16px; @@ -1777,6 +1865,7 @@ body.ttrss_utility fieldset > label.checkbox { .flat .dijitTree .dijitTreeRow { overflow: hidden; -moz-user-select: none; + user-select: none; text-overflow: ellipsis; } .flat label.dijitButton { @@ -1852,7 +1941,7 @@ body.ttrss_utility .content h2, body.ttrss_utility .content h3, body.ttrss_utility .content h4 { color: #257aa7; - font-family: "Segoe WP Semibold", "Segoe UI Semibold", "Segoe UI Web Semibold", "Segoe UI", Ubuntu, "Helvetica Neue", Helvetica, Arial, sans-serif; + font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif; } body.ttrss_utility .content h2 { font-size: 18px; @@ -1871,7 +1960,7 @@ body.ttrss_utility a:focus { } body.ttrss_utility h1 { color: gray; - font-family: "Segoe WP Semibold", "Segoe UI Semibold", "Segoe UI Web Semibold", "Segoe UI", Ubuntu, "Helvetica Neue", Helvetica, Arial, sans-serif; + font-family: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 18px; margin: 10px 0 0 0; } @@ -1940,11 +2029,11 @@ body.ttrss_zoom div.post .attachments { display: none; } body.ttrss_zoom div.post div.header { - padding-bottom: 10px; - border: 0px solid #222; + padding-bottom: 8px; + border: 0 solid #222; border-bottom-width: 1px; background: #333; - font-size: 12px; + font-size: 13px; color: #ccc; } body.ttrss_zoom div.post div.header .row { @@ -1954,32 +2043,36 @@ body.ttrss_zoom div.post div.header .row { align-items: center; justify-content: space-between; } -body.ttrss_zoom div.post div.content { +body.ttrss_zoom div.post div.header .row h1 { + margin-top: 0; + margin-bottom: 8px; +} +body.ttrss_zoom div.post .content { font-size: 15px; line-height: 1.5; border-width: 0; padding: 0; + padding-top: 8px; } -body.ttrss_zoom div.post div.content img, -body.ttrss_zoom div.post div.content video { +body.ttrss_zoom div.post .content img, +body.ttrss_zoom div.post .content video { max-width: 760px; height: auto; } -body.ttrss_zoom div.post div.content blockquote { +body.ttrss_zoom div.post .content blockquote { margin: 5px 0px 5px 0px; color: #ccc; - padding-left: 10px; + padding-left: 8px; border: 0px solid #222; border-left-width: 4px; } -body.ttrss_zoom div.post div.content code { +body.ttrss_zoom div.post .content code { color: #009900; font-family: monospace; font-size: 12px; } -body.ttrss_zoom div.post div.content pre { - margin: 5px 0px 5px 0px; - padding: 10px; +body.ttrss_zoom div.post .content pre { + padding: 8px; color: #ccc; font-family: monospace; font-size: 12px; @@ -2023,26 +2116,34 @@ body.flat.ttrss_main #feeds-holder { background: #222; box-shadow: inset -1px 0px 2px -1px #666; } -body.flat.ttrss_main #feeds-holder #feedTree .counterNode.aux, -body.flat.ttrss_main #feeds-holder #feedTree .counterNode.marked { +body.flat.ttrss_main #feeds-holder #feedTree .dijitTreeRow .counterNode.aux, +body.flat.ttrss_main #feeds-holder #feedTree .dijitTreeRow .counterNode.marked { background: #222; color: #ccc; border-color: #333; } -body.flat.ttrss_main #feeds-holder #feedTree .counterNode.marked { +body.flat.ttrss_main #feeds-holder #feedTree .dijitTreeRow .counterNode.marked { border-color: #257aa7; } -body.flat.ttrss_main #feeds-holder #feedTree .dijitTreeRowSelected { +body.flat.ttrss_main #feeds-holder #feedTree .dijitTreeRow .dijitTreeRowSelected { background: #333; border-color: #333 transparent; color: #e6e6e6; } -body.flat.ttrss_main #feeds-holder #feedTree .dijitTreeRowSelected .dijitTreeLabel { +body.flat.ttrss_main #feeds-holder #feedTree .dijitTreeRow .dijitTreeRowSelected .dijitTreeLabel { text-shadow: none; } -body.flat.ttrss_main #feeds-holder #feedTree i.icon.icon-inbox { +body.flat.ttrss_main #feeds-holder #feedTree .dijitTreeRow i.icon.icon-inbox { color: #999999; } +body.flat.ttrss_main #feeds-holder #feedTree .dijitTreeRow .loadingNode { + filter: invert(1); +} +body.flat.ttrss_main #headlines-frame { + /*.cdm.expanded { + background: @default-bg; + }*/ +} body.flat.ttrss_main #headlines-frame .hl:not(.active):not(.Selected):not(.Unread), body.flat.ttrss_main #headlines-frame .cdm.expandable:not(.active):not(.Selected):not(.Unread) { background: #333; @@ -2051,9 +2152,6 @@ body.flat.ttrss_main #headlines-frame .hl.Unread:not(.active):not(.Selected), body.flat.ttrss_main #headlines-frame .cdm.expandable.Unread:not(.active):not(.Selected) { background: #222; } -body.flat.ttrss_main #headlines-frame .cdm.expanded { - background: #333; -} body.flat.ttrss_main #headlines-frame .hl.Unread .title, body.flat.ttrss_main #headlines-frame .cdm.Unread .title { color: #e6e6e6; @@ -2226,3 +2324,6 @@ body.flat.ttrss_main #filterNewRuleDlg .dijitValidationTextAreaError { body.flat.ttrss_main #filterNewRuleDlg .dijitValidationTextArea:not(.dijitValidationTextAreaError) { background: #305030; } +body.flat.ttrss_main .dijitDialog #feed_add_spinner { + filter: invert(1); +} diff --git a/update.php b/update.php index 956aa8548..079069a3b 100755 --- a/update.php +++ b/update.php @@ -74,31 +74,34 @@ init_plugins(); - $longopts = array("feeds", - "daemon", - "daemon-loop", - "update-feed:", - "send-digests", - "task:", - "cleanup-tags", - "quiet", - "log:", - "log-level:", - "indexes", - "pidlock:", - "update-schema::", - "convert-filters", - "force-update", - "gen-search-idx", - "list-plugins", - "debug-feed:", - "force-refetch", - "force-rehash", - "opml-export:", - "help"); + $options_map = [ + "feeds" => "update all pending feeds", + "daemon" => "start single-process update daemon", + "daemon-loop" => "", + "update-feed:" => "", + "send-digests" => "send pending email digests", + "task:" => "", + "cleanup-tags" => "perform maintenance on tags table", + "quiet" => "don't output messages to stdout", + "log:" => ["FILE", "log messages to FILE"], + "log-level:" => ["N", "set log verbosity level (0-2)"], + "pidlock:" => "", + "update-schema::" => ["[force-yes]", "update database schema, optionally without prompting"], + "force-update" => "mark all feeds as pending update", + "gen-search-idx" => "generate basic PostgreSQL fulltext search index", + "plugins-list" => "list installed plugins", + "debug-feed:" => ["N", "update specified feed with debug output enabled"], + "debug-force-refetch" => "debug update: force refetch feed data", + "debug-force-rehash" => "debug update: force rehash articles", + "opml-export:" => ["USER:FILE", "export OPML of USER to FILE"], + "user-list" => "list all users", +# "user-add:" => ["USER[:PASSWORD]", "add USER, optionally without prompting for PASSWORD"], +# "user-remove:" => ["USERNAME", "remove specified user"], + "help" => "", + ]; foreach (PluginHost::getInstance()->get_commands() as $command => $data) { - array_push($longopts, $command . $data["suffix"]); + $options_map[$command . $data["suffix"]] = [ $data["arghelp"] ?? "", $data["description"] ]; } if (php_sapi_name() != "cli") { @@ -107,35 +110,32 @@ exit; } - $options = getopt("", $longopts); + $options = getopt("", array_keys($options_map)); if (count($options) == 0 || isset($options["help"]) ) { - print "Tiny Tiny RSS data update script.\n\n"; - print "Options:\n"; - print " --feeds - update feeds\n"; - print " --daemon - start single-process update daemon\n"; - print " --task N - create lockfile using this task id\n"; - print " --cleanup-tags - perform tags table maintenance\n"; - print " --quiet - don't output messages to stdout\n"; - print " --log FILE - log messages to FILE\n"; - print " --log-level N - log verbosity level\n"; - print " --indexes - recreate missing schema indexes\n"; - print " --update-schema[=force-yes] - update database schema (without prompting)\n"; - print " --gen-search-idx - generate basic PostgreSQL fulltext search index\n"; - print " --convert-filters - convert type1 filters to type2\n"; - print " --send-digests - send pending email digests\n"; - print " --force-update - force update of all feeds\n"; - print " --list-plugins - list all available plugins\n"; - print " --debug-feed N - perform debug update of feed N\n"; - print " --force-refetch - debug update: force refetch feed data\n"; - print " --force-rehash - debug update: force rehash articles\n"; - print " --opml-export \"USER FILE\" - export feeds of selected user to OPML\n"; - print " --help - show this help\n"; - print "Plugin options:\n"; - - foreach (PluginHost::getInstance()->get_commands() as $command => $data) { - $args = $data['arghelp']; - printf(" --%-26s - %s\n", "$command $args", $data["description"]); + print "Tiny Tiny RSS CLI management tool\n"; + print "=================================\n"; + print "Options:\n\n"; + + $options_help = []; + + foreach ($options_map as $option => $descr) { + if (substr($option, -1) === ":") + $option = substr($option, 0, -1); + + $help_key = trim(sprintf("--%s %s", + $option, is_array($descr) ? $descr[0] : "")); + $help_value = is_array($descr) ? $descr[1] : $descr; + + if ($help_value) + $options_help[$help_key] = $help_value; + } + + $max_key_len = array_reduce(array_keys($options_help), + function ($carry, $item) { $len = strlen($item); return $len > $carry ? strlen($item) : $carry; }); + + foreach ($options_help as $option => $help_text) { + printf(" %s %s\n", str_pad($option, $max_key_len + 5), $help_text); } return; @@ -263,112 +263,6 @@ Debug::log("$rc tags deleted.\n"); } - if (isset($options["indexes"])) { - Debug::log("PLEASE BACKUP YOUR DATABASE BEFORE PROCEEDING!"); - Debug::log("Type 'yes' to continue."); - - if (read_stdin() != 'yes') - exit; - - Debug::log("clearing existing indexes..."); - - if (Config::get(Config::DB_TYPE) == "pgsql") { - $sth = $pdo->query( "SELECT relname FROM - pg_catalog.pg_class WHERE relname LIKE 'ttrss_%' - AND relname NOT LIKE '%_pkey' - AND relkind = 'i'"); - } else { - $sth = $pdo->query( "SELECT index_name,table_name FROM - information_schema.statistics WHERE index_name LIKE 'ttrss_%'"); - } - - while ($line = $sth->fetch()) { - if (Config::get(Config::DB_TYPE) == "pgsql") { - $statement = "DROP INDEX " . $line["relname"]; - Debug::log($statement); - } else { - $statement = "ALTER TABLE ". - $line['table_name']." DROP INDEX ".$line['index_name']; - Debug::log($statement); - } - $pdo->query($statement); - } - - Debug::log("reading indexes from schema for: " . Config::get(Config::DB_TYPE)); - - $fp = fopen("schema/ttrss_schema_" . Config::get(Config::DB_TYPE) . ".sql", "r"); - if ($fp) { - while ($line = fgets($fp)) { - $matches = array(); - - if (preg_match("/^create index ([^ ]+) on ([^ ]+)$/i", $line, $matches)) { - $index = $matches[1]; - $table = $matches[2]; - - $statement = "CREATE INDEX $index ON $table"; - - Debug::log($statement); - $pdo->query($statement); - } - } - fclose($fp); - } else { - Debug::log("unable to open schema file."); - } - Debug::log("all done."); - } - - if (isset($options["convert-filters"])) { - Debug::log("WARNING: this will remove all existing type2 filters."); - Debug::log("Type 'yes' to continue."); - - if (read_stdin() != 'yes') - exit; - - Debug::log("converting filters..."); - - $pdo->query("DELETE FROM ttrss_filters2"); - - $res = $pdo->query("SELECT * FROM ttrss_filters ORDER BY id"); - - while ($line = $res->fetch()) { - $owner_uid = $line["owner_uid"]; - - // date filters are removed - if ($line["filter_type"] != 5) { - $filter = array(); - - if (sql_bool_to_bool($line["cat_filter"])) { - $feed_id = "CAT:" . (int)$line["cat_id"]; - } else { - $feed_id = (int)$line["feed_id"]; - } - - $filter["enabled"] = $line["enabled"] ? "on" : "off"; - $filter["rule"] = array( - json_encode(array( - "reg_exp" => $line["reg_exp"], - "feed_id" => $feed_id, - "filter_type" => $line["filter_type"]))); - - $filter["action"] = array( - json_encode(array( - "action_id" => $line["action_id"], - "action_param_label" => $line["action_param"], - "action_param" => $line["action_param"]))); - - // Oh god it's full of hacks - - $_REQUEST = $filter; - $_SESSION["uid"] = $owner_uid; - - $filters = new Pref_Filters($_REQUEST); - $filters->add(); - } - } - - } - if (isset($options["update-schema"])) { if (Config::is_migration_needed()) { @@ -431,7 +325,7 @@ } } - if (isset($options["list-plugins"])) { + if (isset($options["plugins-list"])) { $tmppluginhost = new PluginHost(); $tmppluginhost->load_all($tmppluginhost::KIND_ALL); $enabled = array_map("trim", explode(",", Config::get(Config::PLUGINS))); @@ -470,8 +364,19 @@ Digest::send_headlines_digests(); } + if (isset($options["user-list"])) { + $users = ORM::for_table('ttrss_users') + ->order_by_expr('id') + ->find_many(); + + foreach ($users as $user) { + printf ("%-4d\t%-15s\t%-20s\t%-20s\n", + $user->id, $user->login, $user->full_name, $user->email); + } + } + if (isset($options["opml-export"])) { - list ($user, $filename) = explode(" ", $options["opml-export"], 2); + list ($user, $filename) = explode(":", $options["opml-export"], 2); Debug::log("Exporting feeds of user $user to $filename as OPML..."); |