diff --git a/README.md b/README.md index 5819442..62e882b 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ Inspired by the [invidio.us](https://github.com/omarroth/invidious) project. - Prevents Twitter from tracking your IP or JavaScript fingerprint - Unofficial API (no rate limits or developer account required) - AGPLv3 licensed, no proprietary instances permitted -- Dark theme +- Themes - Lightweight (for [@nim_lang](https://twitter.com/nim_lang), 36KB vs 580KB from twitter.com) - Native RSS feeds - Mobile support (responsive design) @@ -21,7 +21,6 @@ Inspired by the [invidio.us](https://github.com/omarroth/invidious) project. - More caching (waiting for [moigagoo/norm#19](https://github.com/moigagoo/norm/pull/19)) - Simple account system with customizable feed - Json API endpoints -- Themes - Nitter logo - Emoji support (WIP, uses native font for now) diff --git a/nitter.conf b/nitter.conf index 857b9f0..e957e68 100644 --- a/nitter.conf +++ b/nitter.conf @@ -9,3 +9,6 @@ hostname = "nitter.net" [Cache] directory = "./tmp" profileMinutes = 10 # how long to cache profiles + +[Config] +defaultTheme = "Dark" diff --git a/nitter.nimble b/nitter.nimble index 287c62e..ed1e4a7 100644 --- a/nitter.nimble +++ b/nitter.nimble @@ -11,7 +11,7 @@ bin = @["nitter"] # Dependencies requires "nim >= 0.19.9" -requires "norm >= 1.0.17" +requires "norm#head" requires "https://github.com/dom96/httpbeast#head" requires "jester >= 0.4.3" requires "regex >= 0.11.2" diff --git a/public/css/themes/dark.css b/public/css/themes/dark.css new file mode 100644 index 0000000..a44b29c --- /dev/null +++ b/public/css/themes/dark.css @@ -0,0 +1,3 @@ +body { + /* uses default values */ +} diff --git a/public/css/themes/light.css b/public/css/themes/light.css new file mode 100644 index 0000000..1f22516 --- /dev/null +++ b/public/css/themes/light.css @@ -0,0 +1,37 @@ +body { + --bg_color: #E6ECF0; + --fg_color: #0F0F0F; + --fg_faded: #657786; + --fg_dark: var(--fg_faded); + --fg_nav: var(--accent); + + --bg_panel: #FFFFFF; + --bg_elements: #FDFDFD; + --bg_overlays: #FFFFFF; + --bg_hover: #F5F8FA; + + --grey: var(--fg_faded); + --dark_grey: #D6D6D6; + --darker_grey: #CECECE; + --darkest_grey: #ECECEC; + --border_grey: #E6ECF0; + + --accent: #1DA1F2; + --accent_light: #A0EDFF; + --accent_dark: var(--accent); + --accent_border: #1DA1F296; + + --play_button: #D84D4D; + --play_button_hover: #FF6C60; + + --more_replies_dots: #0199F7; + --error_red: #FF7266; + + --verified_blue: var(--accent); + --icon_text: ##F8F8F2; + + --tab: var(--accent); + --tab_selected: #000000; + + --profile_stat: var(--fg_dark); +} diff --git a/src/config.nim b/src/config.nim index ea62b1c..359d872 100644 --- a/src/config.nim +++ b/src/config.nim @@ -21,5 +21,7 @@ proc getConfig*(path: string): Config = hostname: cfg.get("Server", "hostname", "nitter.net"), cacheDir: cfg.get("Cache", "directory", "/tmp/nitter"), - profileCacheTime: cfg.get("Cache", "profileMinutes", 10) + profileCacheTime: cfg.get("Cache", "profileMinutes", 10), + + defaultTheme: cfg.get("Config", "defaultTheme", "Dark") ) diff --git a/src/prefs.nim b/src/prefs.nim index 0319f7f..8abba26 100644 --- a/src/prefs.nim +++ b/src/prefs.nim @@ -14,6 +14,10 @@ static: if missing.len > 0: raiseAssert("{$1} missing from the Prefs type" % missing.join(", ")) +template safeAddColumn(field: typedesc): untyped = + try: field.addColumn + except DbError: discard + dbFromTypes("prefs.db", "", "", "", [Prefs]) withDb: @@ -21,10 +25,12 @@ withDb: createTables() except DbError: discard + Prefs.theme.safeAddColumn -proc getDefaultPrefs(hostname: string): Prefs = +proc getDefaultPrefs(cfg: Config): Prefs = result = genDefaultPrefs() - result.replaceTwitter = hostname + result.replaceTwitter = cfg.hostname + result.theme = cfg.defaultTheme proc cache*(prefs: var Prefs) = withDb: @@ -35,18 +41,18 @@ proc cache*(prefs: var Prefs) = except AssertionError, KeyError: prefs.insert() -proc getPrefs*(id, hostname: string): Prefs = +proc getPrefs*(id: string; cfg: Config): Prefs = if id.len == 0: - return getDefaultPrefs(hostname) + return getDefaultPrefs(cfg) withDb: try: result.getOne("id = ?", id) except KeyError: - result = getDefaultPrefs(hostname) + result = getDefaultPrefs(cfg) -proc resetPrefs*(prefs: var Prefs; hostname: string) = - var defPrefs = getDefaultPrefs(hostname) +proc resetPrefs*(prefs: var Prefs; cfg: Config) = + var defPrefs = getDefaultPrefs(cfg) defPrefs.id = prefs.id cache(defPrefs) prefs = defPrefs diff --git a/src/prefs_impl.nim b/src/prefs_impl.nim index 96f8479..4978b70 100644 --- a/src/prefs_impl.nim +++ b/src/prefs_impl.nim @@ -51,6 +51,9 @@ const prefList*: OrderedTable[string, seq[Pref]] = { ], "Display": @[ + Pref(kind: select, name: "theme", label: "Theme", + defaultOption: "Dark"), + Pref(kind: checkbox, name: "hideTweetStats", label: "Hide tweet stats (replies, retweets, likes)", defaultState: false), @@ -94,10 +97,12 @@ macro genUpdatePrefs*(): untyped = of input: result.add quote do: prefs.`ident` = xmltree.escape(strip(`value`)) of select: + let name = pref.name let options = pref.options let default = pref.defaultOption result.add quote do: - if `value` in `options`: prefs.`ident` = `value` + if `name` == "theme": prefs.`ident` = `value` + elif `value` in `options`: prefs.`ident` = `value` else: prefs.`ident` = `default` result.add quote do: diff --git a/src/routes/preferences.nim b/src/routes/preferences.nim index 47bd5c3..96cd51e 100644 --- a/src/routes/preferences.nim +++ b/src/routes/preferences.nim @@ -1,4 +1,4 @@ -import strutils, uri +import strutils, uri, os, algorithm import jester @@ -8,13 +8,18 @@ import ../views/[general, preferences] export preferences +proc findThemes*(dir: string): seq[string] = + for kind, path in walkDir(dir / "css" / "themes"): + result.add path.splitFile.name.capitalizeAscii + sort(result) + proc createPrefRouter*(cfg: Config) = router preferences: template savePrefs(): untyped = setCookie("preferences", $prefs.id, daysForward(360), httpOnly=true, secure=cfg.useHttps) get "/settings": - let html = renderPreferences(cookiePrefs(), refPath()) + let html = renderPreferences(cookiePrefs(), refPath(), findThemes(cfg.staticDir)) resp renderMain(html, request, cfg, "Preferences") get "/settings/@i?": @@ -28,7 +33,7 @@ proc createPrefRouter*(cfg: Config) = post "/resetprefs": var prefs = cookiePrefs() - resetPrefs(prefs, cfg.hostname) + resetPrefs(prefs, cfg) savePrefs() redirect($(parseUri("/settings") ? filterParams(request.params))) diff --git a/src/routes/router_utils.nim b/src/routes/router_utils.nim index 44ffba8..2aedd6c 100644 --- a/src/routes/router_utils.nim +++ b/src/routes/router_utils.nim @@ -2,7 +2,7 @@ import ../utils, ../prefs export utils, prefs template cookiePrefs*(): untyped {.dirty.} = - getPrefs(request.cookies.getOrDefault("preferences"), cfg.hostname) + getPrefs(request.cookies.getOrDefault("preferences"), cfg) template getPath*(): untyped {.dirty.} = $(parseUri(request.path) ? filterParams(request.params)) diff --git a/src/sass/general.scss b/src/sass/general.scss index a92c6e3..c4759b0 100644 --- a/src/sass/general.scss +++ b/src/sass/general.scss @@ -7,15 +7,15 @@ } .error-panel { - @include center-panel($error_red); + @include center-panel(var(--error_red)); } .search-bar > form { - @include center-panel($darkest-grey); + @include center-panel(var(--darkest_grey)); button { - background: #303030; - color: $fg_color; + background: var(--bg_elements); + color: var(--fg_color); border: 0; border-radius: 3px; cursor: pointer; @@ -27,8 +27,8 @@ input { font-size: 16px; width: 100%; - background: $bg_elements; - color: $fg_color; + background: var(--bg_elements); + color: var(--fg_color); border: 0; border-radius: 4px; padding: 4px; diff --git a/src/sass/include/_mixins.css b/src/sass/include/_mixins.css index 01e42b7..94e11ee 100644 --- a/src/sass/include/_mixins.css +++ b/src/sass/include/_mixins.css @@ -19,11 +19,11 @@ &:hover { .overlay-circle { - border-color: $accent; + border-color: var(--play_button_hover); } .overlay-triangle { - border-color: transparent transparent transparent $accent; + border-color: transparent transparent transparent var(--play_button_hover); } } } @@ -51,11 +51,11 @@ @mixin input-colors { &:hover { - border-color: $accent; + border-color: var(--accent); } &:active { - border-color: $accent_light; + border-color: var(--accent_light); } } diff --git a/src/sass/include/_variables.scss b/src/sass/include/_variables.scss index a0f7f0c..0f81235 100644 --- a/src/sass/include/_variables.scss +++ b/src/sass/include/_variables.scss @@ -2,11 +2,13 @@ $bg_color: #0F0F0F; $fg_color: #F8F8F2; $fg_faded: #F8F8F2CF; -$fg_dark: #9d9da0; +$fg_dark: #FF6C60; +$fg_nav: #FF6C60; $bg_panel: #161616; $bg_elements: #121212; $bg_overlays: #1F1F1F; +$bg_hover: #1A1A1A; $grey: #888889; $dark_grey: #404040; @@ -19,11 +21,17 @@ $accent_light: #FFACA0; $accent_dark: #8A3731; $accent_border: #FF6C6091; -$play_button_red: #D8574D; +$play_button: #D8574D; +$play_button_hover: #FF6C60; + $more_replies_dots: #AD433B; $error_red: #420A05; $verified_blue: #1DA1F2; +$icon_text: $fg_color; + +$tab: $fg_color; +$tab_selected: $accent; $shadow: rgba(0,0,0,.6); $shadow_dark: rgba(0,0,0,.2); diff --git a/src/sass/index.scss b/src/sass/index.scss index ad157d9..ba675a7 100644 --- a/src/sass/index.scss +++ b/src/sass/index.scss @@ -9,8 +9,45 @@ @import 'search'; body { - background-color: $bg_color; - color: $fg_color; + // colors + --bg_color: #{$bg_color}; + --fg_color: #{$fg_color}; + --fg_faded: #{$fg_faded}; + --fg_dark: #{$fg_dark}; + --fg_nav: #{$fg_nav}; + + --bg_panel: #{$bg_panel}; + --bg_elements: #{$bg_elements}; + --bg_overlays: #{$bg_overlays}; + --bg_hover: #{$bg_hover}; + + --grey: #{$grey}; + --dark_grey: #{$dark_grey}; + --darker_grey: #{$darker_grey}; + --darkest_grey: #{$darkest_grey}; + --border_grey: #{$border_grey}; + + --accent: #{$accent}; + --accent_light: #{$accent_light}; + --accent_dark: #{$accent_dark}; + --accent_border: #{$accent_border}; + + --play_button: #{$play_button}; + --play_button_hover: #{$play_button_hover}; + + --more_replies_dots: #{$more_replies_dots}; + --error_red: #{$error_red}; + + --verified_blue: #{$verified_blue}; + --icon_text: #{$icon_text}; + + --tab: #{$fg_color}; + --tab_selected: #{$accent}; + + --profile_stat: #{$fg_color}; + + background-color: var(--bg_color); + color: var(--fg_color); font-family: $font_0, $font_1, $font_2, $font_3; font-size: 14px; line-height: 1.3; @@ -36,7 +73,7 @@ p { } a { - color: $accent; + color: var(--accent); &:hover { text-decoration: underline; @@ -54,7 +91,7 @@ legend { padding: .6em 0 .3em 0; border: 0; font-size: 16px; - border-bottom: 1px solid $border_grey; + border-bottom: 1px solid var(--border_grey); margin-bottom: 8px; } @@ -80,7 +117,7 @@ ul { width: 100%; margin: 0 auto; margin-top: 10px; - background-color: $bg_overlays; + background-color: var(--bg_overlays); padding: 10px 15px; align-self: start; @@ -91,8 +128,8 @@ ul { } .verified-icon { - color: $fg_color; - background-color: $verified_blue; + color: var(--icon_text); + background-color: var(--verified_blue); border-radius: 50%; flex-shrink: 0; margin: 2px 0 3px 3px; diff --git a/src/sass/inputs.scss b/src/sass/inputs.scss index 0cea64b..a06343f 100644 --- a/src/sass/inputs.scss +++ b/src/sass/inputs.scss @@ -3,9 +3,9 @@ button { @include input-colors; - background-color: $bg_elements; - color: $fg_color; - border: 1px solid $accent_border; + background-color: var(--bg_elements); + color: var(--fg_color); + border: 1px solid var(--accent_border); padding: 3px 6px; font-size: 14px; cursor: pointer; @@ -13,12 +13,13 @@ button { } input[type="text"], -input[type="date"] { +input[type="date"], +select { @include input-colors; - background-color: $bg_elements; + background-color: var(--bg_elements); padding: 1px 4px; - color: $fg_color; - border: 1px solid $accent_border; + color: var(--fg_color); + border: 1px solid var(--accent_border); border-radius: 0; font-size: 14px; } @@ -40,8 +41,8 @@ input::-webkit-calendar-picker-indicator { input::-webkit-datetime-edit-day-field:focus, input::-webkit-datetime-edit-month-field:focus, input::-webkit-datetime-edit-year-field:focus { - background-color: $accent; - color: $fg_color; + background-color: var(--accent); + color: var(--fg_color); outline: none; } @@ -64,7 +65,7 @@ input::-webkit-datetime-edit-year-field:focus { } .icon-button button { - color: $accent; + color: var(--accent); text-decoration: none; background: none; border: none; @@ -73,7 +74,7 @@ input::-webkit-datetime-edit-year-field:focus { padding-left: 4px; &:hover { - color: $accent_light; + color: var(--accent_light); } } @@ -83,8 +84,8 @@ input::-webkit-datetime-edit-year-field:focus { right: 0; height: 17px; width: 17px; - background-color: $bg_elements; - border: 1px solid $accent_border; + background-color: var(--bg_elements); + border: 1px solid var(--accent_border); &:after { content: ""; @@ -114,11 +115,11 @@ input::-webkit-datetime-edit-year-field:focus { } &:hover input ~ .checkbox { - border-color: $accent; + border-color: var(--accent); } &:active input ~ .checkbox { - border-color: $accent_light; + border-color: var(--accent_light); } .checkbox:after { @@ -143,6 +144,16 @@ input::-webkit-datetime-edit-year-field:focus { padding-right: 135px; } + select { + position: absolute; + top: 0; + right: 0; + display: block; + -moz-appearance: none; + -webkit-appearance: none; + appearance: none; + } + input[type="text"] { position: absolute; right: 0; diff --git a/src/sass/navbar.scss b/src/sass/navbar.scss index 45b2fa2..b8c1e17 100644 --- a/src/sass/navbar.scss +++ b/src/sass/navbar.scss @@ -4,12 +4,16 @@ nav { display: flex; align-items: center; position: fixed; - background-color: $bg_overlays; + background-color: var(--bg_overlays); box-shadow: 0 0 4px $shadow; padding: 0; width: 100%; height: 50px; z-index: 1000; + + a, .icon-button button { + color: var(--fg_nav); + } } .inner-nav { @@ -26,7 +30,7 @@ nav { font-weight: 600; &:hover { - color: $accent_light; + color: var(--accent_light); text-decoration: unset; } } @@ -54,7 +58,7 @@ nav { padding-left: 4px; &:hover { - color: $accent_light; + color: var(--accent_light); text-decoration: unset; } } diff --git a/src/sass/profile/card.scss b/src/sass/profile/card.scss index 2281911..f775a0b 100644 --- a/src/sass/profile/card.scss +++ b/src/sass/profile/card.scss @@ -3,7 +3,7 @@ .profile-card { flex-wrap: wrap; - background: $bg_panel; + background: var(--bg_panel); padding: 12px; display: flex; } @@ -20,14 +20,14 @@ .profile-card-username { @include breakable; - color: $fg_color; + color: var(--fg_color); font-size: 14px; display: block; } .profile-card-fullname { @include breakable; - color: $fg_color; + color: var(--fg_color); font-size: 16px; font-weight: bold; text-shadow: none; @@ -45,8 +45,8 @@ width: calc(100% - 8px); height: 100%; margin: 0; - border: 4px solid $darker_grey; - background: $bg_color; + border: 4px solid var(--darker_grey); + background: var(--bg_color); } } @@ -67,7 +67,7 @@ } .profile-joindate, .profile-location, profile-website { - color: $fg_faded; + color: var(--fg_faded); margin: 2px 0; width: 100%; } @@ -94,10 +94,12 @@ .profile-stat-header { font-weight: bold; + color: var(--profile_stat); } .profile-stat-num { display: block; + color: var(--profile_stat); } @media(max-width: 600px) { diff --git a/src/sass/profile/photo-rail.scss b/src/sass/profile/photo-rail.scss index 503125c..1cc78d7 100644 --- a/src/sass/profile/photo-rail.scss +++ b/src/sass/profile/photo-rail.scss @@ -3,7 +3,7 @@ .photo-rail { &-card { float: left; - background: $bg_panel; + background: var(--bg_panel); border-radius: 0 0 4px 4px; width: 100%; margin: 5px 0; @@ -18,7 +18,7 @@ display: none; width: calc(100% - 24px); float: unset; - color: $accent; + color: var(--accent); justify-content: space-between; } diff --git a/src/sass/search.scss b/src/sass/search.scss index 40e70f3..0311fb0 100644 --- a/src/sass/search.scss +++ b/src/sass/search.scss @@ -29,9 +29,9 @@ > label { display: inline; - background-color: #121212; - color: #F8F8F2; - border: 1px solid #FF6C6091; + background-color: var(--bg_elements); + color: var(--fg_color); + border: 1px solid var(--accent_border); padding: 1px 6px 2px 6px; font-size: 14px; cursor: pointer; diff --git a/src/sass/timeline.scss b/src/sass/timeline.scss index dcbbb92..02e4423 100644 --- a/src/sass/timeline.scss +++ b/src/sass/timeline.scss @@ -5,15 +5,15 @@ } .timeline { - background-color: $bg_panel; + background-color: var(--bg_panel); > div:not(:first-child) { - border-top: 1px solid $border_grey; + border-top: 1px solid var(--border_grey); } } .timeline-header { - background-color: $bg_panel; + background-color: var(--bg_panel); text-align: center; padding: 8px; display: block; @@ -31,7 +31,7 @@ flex-wrap: wrap; list-style: none; margin: 0 0 5px 0; - background-color: $bg_panel; + background-color: var(--bg_panel); padding: 0; } @@ -42,7 +42,7 @@ a { border-bottom: .1rem solid transparent; - color: inherit; + color: var(--tab); display: block; padding: 8px 0; text-decoration: none; @@ -53,14 +53,14 @@ } &.active { - border-bottom-color: $accent; - color: $accent; + border-bottom-color: var(--tab_selected); + color: var(--tab_selected); } } &.active a { - border-bottom-color: $accent; - color: $accent; + border-bottom-color: var(--tab_selected); + color: var(--tab_selected); } &.wide { @@ -69,7 +69,7 @@ } .timeline-footer { - background-color: $bg_panel; + background-color: var(--bg_panel); padding: 6px 0; } @@ -81,48 +81,48 @@ } h2 { - color: $accent; + color: var(--accent); font-size: 20px; font-weight: 600; } } .timeline-none { - color: $accent; + color: var(--accent); font-size: 20px; font-weight: 600; text-align: center; } .timeline-end { - background-color: $bg_panel; - color: $accent; + background-color: var(--bg_panel); + color: var(--accent); font-size: 16px; font-weight: 600; text-align: center; } .show-more { - background-color: $bg_panel; + background-color: var(--bg_panel); text-align: center; padding: .75em 0; display: block !important; a { - background-color: $darkest_grey; + background-color: var(--darkest_grey); display: inline-block; height: 2em; padding: 0 2em; line-height: 2em; &:hover { - background-color: $darker_grey; + background-color: var(--darker_grey); } } } .top-ref { - background-color: #0f0f0f; + background-color: var(--bg_color); border-top: none !important; .icon-down { @@ -132,7 +132,7 @@ text-decoration: none; &:hover { - color: $accent_light; + color: var(--accent_light); } &::before { diff --git a/src/sass/tweet/_base.scss b/src/sass/tweet/_base.scss index 46319e2..add0d5b 100644 --- a/src/sass/tweet/_base.scss +++ b/src/sass/tweet/_base.scss @@ -52,7 +52,7 @@ max-width: 80%; font-size: 14px; font-weight: 700; - color: $fg_color; + color: var(--fg_color); } .username { @@ -68,10 +68,14 @@ margin-left: 4px; } +.tweet-date a, .username, .show-more a { + color: var(--fg_dark); +} + .tweet-published { margin: 0; margin-top: 5px; - color: $grey; + color: var(--grey); pointer-events: all; } @@ -89,7 +93,7 @@ } .replying-to { - color: $fg_dark; + color: var(--fg_faded); margin: -2px 0 4px; a { @@ -99,7 +103,7 @@ .retweet-header, .pinned, .tweet-stats { align-content: center; - color: $grey; + color: var(--grey); display: flex; flex-shrink: 0; flex-wrap: wrap; @@ -134,9 +138,9 @@ width: 100%; height: 100%; padding: 12px; - border: solid 1px $dark_grey; + border: solid 1px var(--dark_grey); border-radius: 10px; - background-color: $bg_color; + background-color: var(--bg_color); } .tweet-link { @@ -147,6 +151,6 @@ position: absolute; &:hover { - background-color: #1a1a1a; + background-color: var(--bg_hover); } } diff --git a/src/sass/tweet/card.scss b/src/sass/tweet/card.scss index 11039f7..7ebd37e 100644 --- a/src/sass/tweet/card.scss +++ b/src/sass/tweet/card.scss @@ -10,15 +10,15 @@ border-radius: 10px; border-width: 1px; border-style: solid; - border-color: $dark_grey; - background-color: $bg_elements; + border-color: var(--dark_grey); + background-color: var(--bg_elements); overflow: hidden; color: inherit; display: flex; text-decoration: none !important; &:hover { - border-color: $grey; + border-color: var(--grey); } .attachments { @@ -43,7 +43,7 @@ .card-destination { @include ellipsis; - color: $grey; + color: var(--grey); display: block; } @@ -73,7 +73,7 @@ left: 0; bottom: 0; right: 0; - background-color: $fg_color; + background-color: var(--fg_color); img { width: 100%; @@ -107,7 +107,7 @@ .card-image { position: unset; border-style: solid; - border-color: $dark_grey; + border-color: var(--dark_grey); border-width: 0; border-bottom-width: 1px; } diff --git a/src/sass/tweet/media.scss b/src/sass/tweet/media.scss index 921f85d..f29c717 100644 --- a/src/sass/tweet/media.scss +++ b/src/sass/tweet/media.scss @@ -26,7 +26,7 @@ border-radius: 7px; overflow: hidden; flex-flow: column; - background-color: $bg_color; + background-color: var(--bg_color); align-items: center; pointer-events: all; @@ -81,13 +81,13 @@ .overlay-circle { border-radius: 50%; - background-color: $dark_grey; + background-color: var(--dark_grey); width: 40px; height: 40px; align-items: center; display: flex; border-width: 5px; - border-color: $play_button_red; + border-color: var(--play_button); border-style: solid; } @@ -96,7 +96,7 @@ height: 0; border-style: solid; border-width: 12px 0 12px 17px; - border-color: transparent transparent transparent $play_button_red; + border-color: transparent transparent transparent var(--play_button); margin-left: 14px; } diff --git a/src/sass/tweet/poll.scss b/src/sass/tweet/poll.scss index 2709ba5..57590c8 100644 --- a/src/sass/tweet/poll.scss +++ b/src/sass/tweet/poll.scss @@ -5,7 +5,7 @@ position: relative; margin: 6px 0; height: 26px; - background: $bg_color; + background: var(--bg_color); border-radius: 5px; display: flex; align-items: center; @@ -14,7 +14,7 @@ .poll-choice-bar { height: 100%; position: absolute; - background: $dark_grey; + background: var(--dark_grey); } .poll-choice-value { @@ -33,10 +33,10 @@ } .poll-info { - color: $grey; + color: var(--grey); pointer-events: all; } .leader .poll-choice-bar { - background: $accent_dark; + background: var(--accent_dark); } diff --git a/src/sass/tweet/quote.scss b/src/sass/tweet/quote.scss index 7c435bc..02e9b56 100644 --- a/src/sass/tweet/quote.scss +++ b/src/sass/tweet/quote.scss @@ -2,20 +2,20 @@ .quote { margin-top: 10px; - border: solid 1px $dark_grey; + border: solid 1px var(--dark_grey); border-radius: 10px; - background-color: $bg_elements; + background-color: var(--bg_elements); overflow: auto; padding: 6px; position: relative; pointer-events: all; &:hover { - border-color: $grey; + border-color: var(--grey); } &.unavailable:hover { - border-color: $dark_grey; + border-color: var(--dark_grey); } } @@ -85,7 +85,7 @@ } .quote-sensitive { - background: $darker_grey; + background: var(--darker_grey); width: 102px; height: 102px; border-radius: 12px; @@ -96,7 +96,7 @@ .quote-sensitive-icon { font-size: 40px; - color: $grey; + color: var(--grey); } @media(max-width: 600px) { diff --git a/src/sass/tweet/thread.scss b/src/sass/tweet/thread.scss index 5557684..5dd3cc2 100644 --- a/src/sass/tweet/thread.scss +++ b/src/sass/tweet/thread.scss @@ -11,7 +11,7 @@ .main-thread { margin-bottom: 20px; - background-color: $bg_panel; + background-color: var(--bg_panel); } .main-tweet, .replies { @@ -24,14 +24,14 @@ } .reply { - background-color: $bg_panel; + background-color: var(--bg_panel); margin-bottom: 10px; } .thread-line { .timeline-item::before, &.timeline-item::before { - background: $accent_dark; + background: var(--accent_dark); content: ''; position: relative; min-width: 3px; @@ -53,7 +53,7 @@ .more-replies::before { content: '...'; background: unset; - color: $more_replies_dots; + color: var(--more_replies_dots); font-weight: bold; font-size: 20px; line-height: 0.25em; diff --git a/src/types.nim b/src/types.nim index d4965fd..c0d9492 100644 --- a/src/types.nim +++ b/src/types.nim @@ -177,6 +177,7 @@ type hostname*: string cacheDir*: string profileCacheTime*: int + defaultTheme*: string proc contains*(thread: Chain; tweet: Tweet): bool = thread.content.anyIt(it.id == tweet.id) diff --git a/src/views/general.nim b/src/views/general.nim index af650e7..49f911a 100644 --- a/src/views/general.nim +++ b/src/views/general.nim @@ -29,11 +29,13 @@ proc renderNavbar*(title, rss: string; req: Request): VNode = proc renderMain*(body: VNode; req: Request; cfg: Config; titleText=""; desc=""; rss=""; `type`="article"; video=""; images: seq[string] = @[]): string = - let prefs = getPrefs(req.cookies.getOrDefault("preferences"), cfg.hostname) + let prefs = getPrefs(req.cookies.getOrDefault("preferences"), cfg) + let theme = "/css/themes/" & toLowerAscii(prefs.theme) & ".css" let node = buildHtml(html(lang="en")): head: link(rel="stylesheet", `type`="text/css", href="/css/style.css") link(rel="stylesheet", `type`="text/css", href="/css/fontello.css") + link(rel="stylesheet", `type`="text/css", href=theme) link(rel="apple-touch-icon", sizes="180x180", href="/apple-touch-icon.png") link(rel="icon", type="image/png", sizes="32x32", href="/favicon-32x32.png") diff --git a/src/views/preferences.nim b/src/views/preferences.nim index c503cbc..7a415f6 100644 --- a/src/views/preferences.nim +++ b/src/views/preferences.nim @@ -1,4 +1,4 @@ -import tables, macros, strutils +import tables, macros, strutils, os import karax/[karaxdsl, vdom, vstyles] import renderutils @@ -22,12 +22,16 @@ macro renderPrefs*(): untyped = case pref.kind of checkbox: discard - of select: stmt[0].add newLit(pref.options) of input: stmt[0].add newLit(pref.placeholder) + of select: + if pref.name == "theme": + stmt[0].add ident("themes") + else: + stmt[0].add newLit(pref.options) result[2].add stmt -proc renderPreferences*(prefs: Prefs; path: string): VNode = +proc renderPreferences*(prefs: Prefs; path: string; themes: seq[string]): VNode = buildHtml(tdiv(class="overlay-panel")): fieldset(class="preferences"): form(`method`="post", action="/saveprefs"): diff --git a/src/views/renderutils.nim b/src/views/renderutils.nim index 7612acb..dc2194a 100644 --- a/src/views/renderutils.nim +++ b/src/views/renderutils.nim @@ -71,7 +71,7 @@ proc genInput*(pref, label, state, placeholder: string; class=""; autofocus=fals verbatim &"" proc genSelect*(pref, label, state: string; options: seq[string]): VNode = - buildHtml(tdiv(class="pref-group")): + buildHtml(tdiv(class="pref-group pref-input")): label(`for`=pref): text label select(name=pref): for opt in options: