Add theme support, light theme, and edgeless light

This commit is contained in:
Cadence Ember 2021-12-28 16:32:11 +13:00
parent 4e1f2b3607
commit 0d23d66700
45 changed files with 433 additions and 212 deletions

View File

@ -24,7 +24,7 @@ module.exports = [
video.watched = watchedVideos.includes(video.videoId)
})
}
return render(200, "pug/channel.pug", {url, data, subscribed, instanceOrigin})
return render(200, "pug/channel.pug", {settings, url, data, subscribed, instanceOrigin})
}
}
]

View File

@ -9,8 +9,7 @@ const {Matcher, PatternCompileError} = require("../utils/matcher")
const filterMaxLength = 160
const regexpEnabledText = constants.server_setup.allow_regexp_filters ? "" : "not"
function getCategories(req) {
const user = getUser(req)
function getCategories(user) {
const filters = user.getFilters()
// Sort filters into categories for display. Titles are already sorted.
@ -39,7 +38,9 @@ function getCategories(req) {
module.exports = [
{
route: "/filters", methods: ["GET"], code: async ({req, url}) => {
const categories = getCategories(req)
const user = getUser(req)
const categories = getCategories(user)
const settings = user.getSettingsOrDefaults()
let referrer = url.searchParams.get("referrer") || null
let type = null
@ -54,7 +55,7 @@ module.exports = [
label = url.searchParams.get("label")
}
return render(200, "pug/filters.pug", {categories, type, contents, label, referrer, filterMaxLength, regexpEnabledText})
return render(200, "pug/filters.pug", {settings, categories, type, contents, label, referrer, filterMaxLength, regexpEnabledText})
}
},
{
@ -100,8 +101,10 @@ module.exports = [
return true
}, state => {
const {type, contents, label, compileError} = state
const categories = getCategories(req)
return render(400, "pug/filters.pug", {categories, type, contents, label, compileError, filterMaxLength, regexpEnabledText})
const user = getUser(req)
const categories = getCategories(user)
const settings = user.getSettingsOrDefaults()
return render(400, "pug/filters.pug", {settings, categories, type, contents, label, compileError, filterMaxLength, regexpEnabledText})
})
.last(state => {
const {type, contents, label} = state

View File

@ -1,16 +1,35 @@
const {render} = require("pinski/plugins")
const {getUser} = require("../utils/getuser")
module.exports = [
{
route: "/", methods: ["GET"], code: async ({req}) => {
const userAgent = req.headers["user-agent"] || ""
const mobile = userAgent.toLowerCase().includes("mobile")
return render(200, "pug/home.pug", {mobile})
const user = getUser(req)
const settings = user.getSettingsOrDefaults()
return render(200, "pug/home.pug", {settings, mobile})
}
},
{
route: "/js-licenses", methods: ["GET"], code: async () => {
return render(200, "pug/js-licenses.pug")
route: "/(?:js-)?licenses", methods: ["GET"], code: async ({req}) => {
const user = getUser(req)
const settings = user.getSettingsOrDefaults()
return render(200, "pug/licenses.pug", {settings})
}
},
{
route: "/cant-think", methods: ["GET"], code: async ({req}) => {
const user = getUser(req)
const settings = user.getSettingsOrDefaults()
return render(200, "pug/cant-think.pug", {settings})
}
},
{
route: "/privacy", methods: ["GET"], code: async ({req}) => {
const user = getUser(req)
const settings = user.getSettingsOrDefaults()
return render(200, "pug/privacy.pug", {settings})
}
}
]

View File

@ -26,7 +26,7 @@ module.exports = [
const filters = user.getFilters()
results = converters.applyVideoFilters(results, filters).videos
return render(200, "pug/search.pug", {url, query, results, instanceOrigin})
return render(200, "pug/search.pug", {settings, url, query, results, instanceOrigin})
}
}
]

View File

@ -111,7 +111,7 @@ module.exports = [
// Check if playback is allowed
const videoTakedownInfo = db.prepare("SELECT id, org, url FROM TakedownVideos WHERE id = ?").get(id)
if (videoTakedownInfo) {
return render(451, "pug/takedown-video.pug", videoTakedownInfo)
return render(451, "pug/takedown-video.pug", Object.assign({settings}, videoTakedownInfo))
}
// Media fragment
@ -129,7 +129,7 @@ module.exports = [
// Work out how to fetch the video
if (req.method === "GET") {
if (settings.local) { // skip to the local fetching page, which will then POST video data in a moment
return render(200, "pug/local-video.pug", {id})
return render(200, "pug/local-video.pug", {settings, id})
}
var instanceOrigin = settings.instance
var outURL = `${instanceOrigin}/api/v1/videos/${id}`
@ -153,7 +153,7 @@ module.exports = [
// automatically add the entry to the videos list, so it won't be fetched again
const args = {id, ...channelTakedownInfo}
db.prepare("INSERT INTO TakedownVideos (id, org, url) VALUES (@id, @org, @url)").run(args)
return render(451, "pug/takedown-video.pug", channelTakedownInfo)
return render(451, "pug/takedown-video.pug", Object.assign({settings}, channelTakedownInfo))
}
// process stream list ordering
@ -225,7 +225,7 @@ module.exports = [
// Create appropriate formatted message
const message = render(0, `pug/errors/${errorType}.pug`, locals).content
return render(500, "pug/video.pug", {video: {videoId: id}, error: true, message})
return render(500, "pug/video.pug", {video: {videoId: id}, error: true, message, settings})
}
}
}

View File

Before

Width:  |  Height:  |  Size: 225 B

After

Width:  |  Height:  |  Size: 225 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="8" viewBox="0 0 5.821 2.117"><path d="M1.269.53l.767.793h.161L2.964.53h.211v.265L2.117 1.852 1.058.794V.529z" fill="#202020" paint-order="markers stroke fill"/></svg>

After

Width:  |  Height:  |  Size: 226 B

View File

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="25" height="25" viewBox="0 0 6.615 6.615"><path d="M2.64 0s-.03.94-.424 1.215C1.823 1.49.885.894.885.894L.112 2.232.109 2.23l.003.002c.01.006.797.499.838.974.042.478-.944.992-.944.992l.77 1.34s.83-.442 1.265-.24c.435.204.387 1.314.387 1.314l1.546.003s.032-.94.425-1.215c.393-.275 1.331.321 1.331.321l.775-1.338s-.798-.496-.84-.974c-.041-.478.944-.993.944-.993l-.77-1.34s-.83.443-1.265.24C4.14 1.113 4.187.002 4.187.002zm.688 2.25a1.106 1.106 0 110 2.211 1.106 1.106 0 010-2.21z" fill="#c4c4c4" paint-order="fill markers stroke"/></svg>
<svg fill="currentColor" xmlns="http://www.w3.org/2000/svg" width="25" height="25" viewBox="0 0 6.615 6.615"><title>Settings</title><path d="M2.64 0s-.03.94-.424 1.215C1.823 1.49.885.894.885.894L.112 2.232.109 2.23l.003.002c.01.006.797.499.838.974.042.478-.944.992-.944.992l.77 1.34s.83-.442 1.265-.24c.435.204.387 1.314.387 1.314l1.546.003s.032-.94.425-1.215c.393-.275 1.331.321 1.331.321l.775-1.338s-.798-.496-.84-.974c-.041-.478.944-.993.944-.993l-.77-1.34s-.83.443-1.265.24C4.14 1.113 4.187.002 4.187.002zm.688 2.25a1.106 1.106 0 110 2.211 1.106 1.106 0 010-2.21z" paint-order="fill markers stroke"/></svg>

Before

Width:  |  Height:  |  Size: 582 B

After

Width:  |  Height:  |  Size: 611 B

View File

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="30" height="25" viewBox="0 0 7.937 6.615"><path d="M2.373 0h3.191a.52.52 0 01.521.521H1.852A.52.52 0 012.373 0zm-.91.794h5.011c.371 0 .67.298.67.67v.123l-6.35-.016v-.108c0-.37.298-.67.67-.67zm-.405 1.058C.472 1.852 0 2.184 0 2.77v2.868c0 .586.472.977 1.058.977H6.88c.586 0 1.059-.39 1.059-.977V2.77c0-.586-.473-.918-1.059-.918zM5.3 4.017c.167.099.19.276.012.366l-2.098.985c-.131.077-.304-.002-.302-.19V3.29c0-.203.18-.333.34-.245z" fill="#c4c4c4" paint-order="fill markers stroke"/></svg>
<svg fill="currentColor" xmlns="http://www.w3.org/2000/svg" width="30" height="25" viewBox="0 0 7.937 6.615"><title>Subscriptions</title><path d="M2.373 0h3.191a.52.52 0 01.521.521H1.852A.52.52 0 012.373 0zm-.91.794h5.011c.371 0 .67.298.67.67v.123l-6.35-.016v-.108c0-.37.298-.67.67-.67zm-.405 1.058C.472 1.852 0 2.184 0 2.77v2.868c0 .586.472.977 1.058.977H6.88c.586 0 1.059-.39 1.059-.977V2.77c0-.586-.473-.918-1.059-.918zM5.3 4.017c.167.099.19.276.012.366l-2.098.985c-.131.077-.304-.002-.302-.19V3.29c0-.203.18-.333.34-.245z" paint-order="fill markers stroke"/></svg>

Before

Width:  |  Height:  |  Size: 535 B

After

Width:  |  Height:  |  Size: 569 B

View File

@ -3,7 +3,8 @@ html
head
meta(charset="utf-8")
meta(name="viewport" content="width=device-width, initial-scale=1")
link(rel="stylesheet" type="text/css" href=getStaticURL("sass", "/main.sass"))
- const theme = settings && ["dark", "light", "edgeless-light"][settings.theme] || "dark"
link(rel="stylesheet" type="text/css" href=getStaticURL("sass", `/${theme}.sass`))
script(type="module" src=getStaticURL("html", "/static/js/focus.js"))
block head
@ -15,13 +16,9 @@ html
.links
a(href="/").link.home CloudTube
a(href="/subscriptions" title="Subscriptions").link.icon-link
svg(width=30 height=25)
image(href=getStaticURL("html", "/static/images/subscriptions.svg") alt="Subscriptions.").icon
title Subscriptions
!= icons.get("subscriptions")
a(href="/settings" title="Settings").link.icon-link
svg(width=25 height=25)
image(href=getStaticURL("html", "/static/images/settings.svg") alt="Settings.").icon
title Settings
!= icons.get("settings")
form(method="get" action="/search").search-form
input(type="text" placeholder="Search" aria-label="Search a video" name="q" autocomplete="off" value=query).search

View File

@ -36,6 +36,16 @@ block content
form(method="post" action="/settings")
+fieldset("Settings")
+select({
id: "theme",
label: "Theme",
options: [
{value: "0", text: "Standard dark"},
{value: "1", text: "Standard light"},
{value: "2", text: "Edgeless light"}
]
})
+input({
id: "instance",
label: "Instance",

View File

@ -31,8 +31,8 @@ block content
if settings.save_history
input(type="checkbox" id="watched-videos-display")
.watched-videos-display-container
label(for="watched-videos-display").watched-videos-display-label Hide watched videos
.checkbox-hider__container
label(for="watched-videos-display").checkbox-hider__label Hide watched videos
each video in videos
+video_list_item("subscriptions-video", video, instanceOrigin, {showMarkWatched: settings.save_history && !video.watched})

View File

@ -21,6 +21,7 @@ block content
noscript
meta(http-equiv="refresh" content=`${video.lengthSeconds+5};url=/watch?v=${first.videoId}&continuous=1&session-watched=${sessionWatchedNext}`)
.video-page(class={
"video-page--recommended-side": settings.recommended_mode === 0,
"video-page--recommended-below": settings.recommended_mode === 1,
"video-page--recommended-hidden": settings.recommended_mode === 2
})

9
sass/dark.sass Normal file
View File

@ -0,0 +1,9 @@
@use "themes/dark" as *
@use "includes/main" with ($_theme: $theme)
@use "theme-modules/edgeless" with ($_theme: $theme)
// navigation shadow
.main-nav
position: relative // needed for box shadow to overlap related videos section
box-shadow: 0px 0px 20px 5px rgba(0, 0, 0, 0.1)

4
sass/edgeless-light.sass Normal file
View File

@ -0,0 +1,4 @@
@use "themes/edgeless-light" as *
@use "includes/main" with ($_theme: $theme)
@use "theme-modules/edgeless" with ($_theme: $theme)

View File

@ -1,8 +1,10 @@
@use "colors.sass" as c
$_theme: () !default
@use "sass:map"
body
background-color: c.$bg-dark
color: c.$fg-main
background-color: map.get($_theme, "bg-2")
color: map.get($_theme, "fg-main")
font-family: "Bariol", sans-serif
font-size: 18px
margin: 0
@ -13,13 +15,13 @@ body
flex-direction: column
a
color: c.$link
color: map.get($_theme, "link")
pre, code
font-size: 0.88em
code
background: c.$bg-darker
background: map.get($_theme, "bg-1")
padding: 3px 5px
border-radius: 4px
@ -32,7 +34,7 @@ button
cursor: pointer
::placeholder
color: #c4c4c4
color: map.get($_theme, "placeholder")
opacity: 1
// focus section
@ -48,19 +50,20 @@ button
select:-moz-focusring
color: transparent
text-shadow: 0 0 0 c.$fg-bright
text-shadow: 0 0 0 map.get($_theme, "fg-bright")
body.show-focus
a, select, button, input, video, summary
&:focus
outline: 2px dotted #ddd
outline: 2px dotted map.get($_theme, "fg-main")
video
background-color: black
details
background-color: c.$bg-accent-x
background-color: map.get($_theme, "bg-3")
padding: 12px
border: 1px solid map.get($_theme, "edge-grey")
border-radius: 8px
summary
@ -68,20 +71,22 @@ details
line-height: 1
margin-bottom: 0
user-select: none
color: c.$fg-main
color: map.get($_theme, "fg-main")
&[open] summary
margin-bottom: 16px
padding-bottom: 12px
border-bottom: 1px solid map.get($_theme, "edge-grey")
margin-bottom: 8px
table
background-color: c.$bg-darker
background-color: map.get($_theme, "bg-1")
table, td, th
border: 1px solid c.$edge-grey
border: 1px solid map.get($_theme, "edge-grey")
border-collapse: collapse
td, th
padding: 4px 8px
thead, tr:nth-child(even)
background-color: c.$bg-darkest
background-color: map.get($_theme, "bg-0")

View File

@ -1,10 +1,12 @@
$_theme: () !default
@use "sass:selector"
@use "colors.sass" as c
@use "sass:map"
@mixin button-base
-webkit-appearance: none
-moz-appearance: none
color: c.$fg-bright
color: map.get($_theme, "fg-bright")
border: none
border-radius: 4px
padding: 8px
@ -14,7 +16,7 @@
@at-root #{selector.unify(&, "select")}
padding: 8px 27px 8px 8px
background: url(/static/images/arrow-down-wide.svg) right 53% no-repeat c.$bg-accent-x
background: map.get($_theme, "image-dropdown") right 53% no-repeat map.get($_theme, "bg-4")
@at-root #{selector.unify(&, "a")}
padding: 7px 8px
@ -31,12 +33,12 @@
@mixin button-bg
@include button-base
background-color: c.$bg-accent-x
background-color: map.get($_theme, "bg-4")
@mixin border-button
@include button-bg
border: 1px solid c.$edge-grey
border: 1px solid map.get($_theme, "edge-grey")
@mixin button-size
margin: 4px
@ -44,10 +46,10 @@
@mixin button-hover
&:hover
background-color: c.$bg-accent
background-color: map.get($_theme, "bg-3")
&:active
background-color: c.$bg-dark
background-color: map.get($_theme, "bg-2")
.base-border-look
@include border-button
@ -62,13 +64,13 @@
@include button-size
-webkit-appearance: none
-moz-appearance: none
color: c.$fg-bright
color: map.get($_theme, "fg-bright")
text-decoration: none
line-height: 1.25
margin: 0
padding: 8px 20px
background: c.$bg-accent
border: solid c.$bg-darker
background: map.get($_theme, "bg-3")
border: solid map.get($_theme, "edge-grey")
border-width: 1px 0px 0px
text-align: left
@ -76,7 +78,7 @@
border-width: 1px 0px 1px
&:hover
background: c.$bg-accent-x
background: map.get($_theme, "bg-4")
&:active
background: c.$bg-darker
background: map.get($_theme, "bg-1")

View File

@ -1,5 +1,7 @@
$_theme: () !default
@use "sass:list"
@use "colors.sass" as c
@use "sass:map"
.cant-think-page
.main-nav
@ -13,7 +15,7 @@
box-sizing: border-box
.page-narration
background-color: c.$bg-accent
background-color: map.get($_theme, "bg-3")
border: 1px solid #aaa
color: #fff
border-radius: 0
@ -28,7 +30,7 @@
.leave
margin: 26px 32px !important
color: #aaa
color: map.get($_theme, "fg-dim")
$sizes: 14px 16px 21px 30px 72px
@each $size in $sizes
@ -37,7 +39,7 @@
&.leave__final
font-weight: bold
color: #f2f2f2
color: map.get($_theme, "fg-bright")
text-align: center
.leave__actions

View File

@ -1,6 +1,8 @@
@use "colors.sass" as c
@use "video-list-item.sass" as *
@use "_dimensions.sass" as dimensions
$_theme: () !default
@use "sass:map"
@use "_dimensions" as dimensions
@use "video-list-item" as *
.channel-page
padding: 40px 20px 20px
@ -17,7 +19,7 @@
align-self: flex-start
.channel-data
background-color: c.$bg-darker
background-color: map.get($_theme, "bg-1")
padding: 24px
margin: 12px 0px 24px
border-radius: 8px
@ -44,11 +46,11 @@
.name
font-size: 30px
font-weight: normal
color: c.$fg-bright
color: map.get($_theme, "fg-bright")
margin: 0
.subscribers
color: c.$fg-main
color: map.get($_theme, "fg-main")
font-size: 18px
.subscribe-form
@ -61,7 +63,8 @@
line-height: 1
border-radius: 8px
font-size: 22px
background-color: c.$power-deep
background-color: map.get($_theme, "power-deep")
color: map.get($_theme, "power-fg")
border: none
.description

View File

@ -1,10 +1,12 @@
@use "colors.sass" as c
$_theme: () !default
@use "sass:map"
@mixin filter-notice
margin-top: 24px
padding: 12px
border-radius: 8px
background-color: c.$bg-darker
background-color: map.get($_theme, "bg-1")
white-space: pre-line
.filters-page
@ -20,23 +22,23 @@
.filter-confirmation-notice
@include filter-notice
color: c.$fg-warning
color: map.get($_theme, "fg-warning")
.filter-compile-error
@include filter-notice
&__header
color: c.$fg-warning
color: map.get($_theme, "fg-warning")
&__trace
background-color: c.$bg-darkest
background-color: map.get($_theme, "bg-0")
padding: 6px
.save-filter
margin-top: 12px
.border-look
background-color: c.$bg-darker
background-color: map.get($_theme, "bg-1")
font-size: 22px
padding: 7px 16px 8px
font-size: 24px
@ -48,17 +50,17 @@
.filter
display: flex
padding: 5px 0
border-top: 1px solid c.$edge-grey
border-top: 1px solid map.get($_theme, "edge-grey")
&:last-child
border-bottom: 1px solid c.$edge-grey
border-bottom: 1px solid map.get($_theme, "edge-grey")
&__details
flex: 1
&__type
font-size: 15px
color: c.$fg-dim
color: map.get($_theme, "fg-dim")
&__remove
flex-shrink: 0

View File

@ -1,4 +1,6 @@
@use "./colors.sass" as c
$_theme: () !default
@use "sass:map"
.footer__container
flex: 1
@ -10,9 +12,10 @@
display: flex
flex-direction: column
align-items: center
background-color: c.$bg-darkest
background-color: map.get($_theme, "bg-dim")
margin: 40px 0 0
padding: 10px 10px 30px
border-top: 1px solid map.get($_theme, "edge-grey")
.footer__cols
display: flex

View File

@ -1,7 +1,9 @@
@use "colors.sass" as c
$_theme: () !default
@use "sass:map"
@mixin disabled
background-color: c.$bg-dark
background-color: map.get($_theme, "bg-2")
color: #808080
fieldset
@ -20,7 +22,7 @@ fieldset
font-size: 28px
font-weight: bold
padding: 0
border-bottom: 1px solid #333
border-bottom: 1px solid map.get($_theme, "edge-grey") // TODO: originally contrasted more
line-height: 1.56
@media screen and (max-width: 400px)
@ -36,7 +38,7 @@ fieldset
position: relative
padding-bottom: 5px
margin-bottom: 5px
border-bottom: 1px solid #999
border-bottom: 1px solid map.get($_theme, "edge-grey")
@media screen and (max-width: 400px)
flex-direction: column
@ -52,7 +54,7 @@ fieldset
&__label
grid-area: label
padding: 8px 8px 8px 0px
color: #fff
color: map.get($_theme, "fg-main")
&__input
grid-area: input
@ -63,7 +65,7 @@ fieldset
white-space: pre-line
margin: 12px 0px 18px
font-size: 16px
color: #ccc
color: map.get($_theme, "fg-dim")
line-height: 1.2
//
@ -79,7 +81,7 @@ fieldset
width: 16px
height: 16px
padding: 0px
border: 1px solid #666
border: 1px solid map.get($_theme, "edge-grey")
border-radius: 3px
margin-left: 8px
position: relative
@ -110,18 +112,19 @@ fieldset
height: 42px
margin: 0
.#{$base}-container
position: relative
display: grid // why does the default not work???
top: -42px
background: c.$bg-accent-x
line-height: 1
border-radius: 8px
margin-bottom: -18px
.checkbox-hider__container
position: relative
display: grid // why does the default not work???
top: -42px
background: map.get($_theme, "bg-3")
line-height: 1
border: 1px solid map.get($_theme, "edge-grey")
border-radius: 8px
margin-bottom: -18px
.#{$base}-label
padding: 12px 0px 12px 32px
cursor: pointer
.checkbox-hider__label
padding: 12px 0px 12px 32px
cursor: pointer
@mixin single-button-form
display: inline-block

View File

@ -1,4 +1,6 @@
@use "colors.sass" as c
$_theme: () !default
@use "sass:map"
.home-page
padding: 40px
@ -18,8 +20,8 @@
padding: 16px
border-radius: 4px
font-size: 20px
background-color: c.$bg-darker
color: c.$fg-main
background-color: map.get($_theme, "bg-1")
color: map.get($_theme, "fg-main")
p
margin: 0 32px

View File

@ -1,3 +1,5 @@
$_theme: () !default
.js-licenses-page
max-width: 800px
margin: 0 auto

32
sass/includes/_main.sass Normal file
View File

@ -0,0 +1,32 @@
$_theme: () !default
@use "sass:selector"
// preload second-level includes with the theme (there will be conflicts due to reconfiguration they are loaded individually)
// this isn't _exactly_ what @forward is supposed to be used for, but it's the best option here
@forward "video-list-item" show _ with ($_theme: $_theme)
@forward "forms" show _ with ($_theme: $_theme)
@forward "buttons" show _ with ($_theme: $_theme)
@use "base" with ($_theme: $_theme)
@use "video-page" with ($_theme: $_theme)
@use "search-page" with ($_theme: $_theme)
@use "home-page" with ($_theme: $_theme)
@use "channel-page" with ($_theme: $_theme)
@use "subscriptions-page" with ($_theme: $_theme)
@use "settings-page" with ($_theme: $_theme)
@use "cant-think-page" with ($_theme: $_theme)
@use "privacy-page" with ($_theme: $_theme)
@use "licenses-page" with ($_theme: $_theme)
@use "filters-page" with ($_theme: $_theme)
@use "takedown-page" with ($_theme: $_theme)
@use "nav" with ($_theme: $_theme)
@use "footer" with ($_theme: $_theme)
@font-face
font-family: "Bariol"
src: url(/static/fonts/bariol.woff?statichash=1)
.button-container
display: flex
flex-wrap: wrap

View File

@ -1,12 +1,14 @@
@use "colors.sass" as c
@use "buttons.sass" as *
@use "_dimensions.sass" as dimensions
$_theme: () !default
@use "sass:map"
@use "buttons" as *
@use "_dimensions" as dimensions
.main-nav
background-color: c.$bg-accent
background-color: map.get($_theme, "bg-nav")
display: flex
padding: 8px
box-shadow: 0px 0px 20px 5px rgba(0, 0, 0, 0.1)
border-bottom: 1px solid map.get($_theme, "edge-grey")
+dimensions.thin
display: block
@ -30,10 +32,16 @@
font-weight: bold
&, &:visited
color: #fff
color: map.get($_theme, "fg-bright")
&:focus, &:hover
background-color: c.$bg-accent-x
background-color: map.get($_theme, "bg-4")
&.icon-link
color: map.get($_theme, "fg-dim")
&:hover, &:focus
color: map.get($_theme, "fg-bright")
.search-form
display: flex
@ -44,8 +52,7 @@
@include button-bg
padding: 10px
flex: 1
margin: 1px
border: 1px solid map.get($_theme, "bg-nav")
&:hover, &:focus
border: 1px solid c.$edge-grey
margin: 0px
border-color: map.get($_theme, "edge-grey")

View File

@ -1,3 +1,5 @@
$_theme: () !default
.privacy-page
max-width: 600px
margin: 0 auto

View File

@ -1,5 +1,7 @@
@use "video-list-item.sass" as *
@use "colors.sass" as c
$_theme: () !default
@use "sass:map"
@use "video-list-item" as *
.search-page
padding: 40px 20px 20px

View File

@ -1,5 +1,7 @@
@use "forms.sass" as forms
@use "colors.sass" as c
$_theme: () !default
@use "sass:map"
@use "forms" as forms
.settings-page
padding: 40px 20px 20px
@ -19,8 +21,9 @@
.more-settings
margin-top: 24px
padding: 12px
border: 1px solid map.get($_theme, "edge-grey")
border-radius: 8px
background-color: c.$bg-accent-x
background-color: map.get($_theme, "bg-3")
&__list
margin: 0
@ -34,7 +37,7 @@
margin-top: 24px
.delete-confirm-container
background: c.$bg-darker
background: map.get($_theme, "bg-1")
margin-bottom: -36px
@include forms.checkbox-hider("delete-confirm")

View File

@ -1,6 +1,8 @@
@use "colors.sass" as c
@use "video-list-item.sass" as *
@use "forms.sass" as forms
$_theme: () !default
@use "sass:map"
@use "forms" as forms
@use "video-list-item" as *
.subscriptions-page
padding: 40px 20px 20px
@ -33,7 +35,7 @@
.name
font-size: 22px
color: c.$fg-main
color: map.get($_theme, "fg-main")
@include forms.checkbox-hider("watched-videos-display")

View File

@ -0,0 +1,16 @@
$_theme: () !default
@use "sass:map"
.takedown-page
max-width: 700px
margin: 0 auto
.new-section
margin-top: 200px
.important-section
padding: 4px 20px
border: 1px solid map.get($_theme, "edge-grey")
color: map.get($_theme, "fg-bright")
background-color: map.get($_theme, "bg-1")

View File

@ -1,5 +1,7 @@
@use "colors.sass" as c
@use "_dimensions.sass" as dimensions
$_theme: () !default
@use "sass:map"
@use "_dimensions" as dimensions
// navigator hacks
.thumbnail > .thumbnail__options-container
@ -30,6 +32,7 @@
&__show-more
display: block
height: $more-size
color: #fff
line-height: 16px
font-size: 25px
text-align: center
@ -52,7 +55,7 @@
&__options-list
pointer-events: auto
display: grid
background-color: c.$bg-accent
background-color: map.get($_theme, "bg-3")
padding: 8px 0px
border-radius: 8px
box-shadow: 0 2px 6px 2px #000
@ -67,7 +70,7 @@
right: 0
transform: translate(-6px, -1px) rotate(-45deg)
clip-path: polygon(-5% -20%, 120% -20%, 120% 125%)
background-color: c.$bg-accent
background-color: map.get($_theme, "bg-3")
box-shadow: 0px 0px 4px 0px #000
pointer-events: none
@ -80,7 +83,7 @@
margin-bottom: 12px
@at-root .video-list-item--watched#{&}
background: c.$bg-darker
background: map.get($_theme, "bg-dim")
padding: 4px 4px 0px
margin: -4px -4px 8px
@ -93,7 +96,7 @@
.thumbnail
position: relative
display: flex
background: c.$bg-darkest
background: map.get($_theme, "bg-0")
&__link
font-size: 0 // remove whitespace around the image
@ -106,7 +109,7 @@
position: absolute
bottom: 3px
right: 3px
color: c.$fg-bright
color: #fff
font-size: 14px
background: rgba(20, 20, 20, 0.85)
line-height: 1
@ -119,20 +122,20 @@
line-height: 1.2
.title-link
color: c.$fg-main
color: map.get($_theme, "fg-main")
text-decoration: none
.author-line
margin-top: 4px
font-size: 15px
color: c.$fg-dim
color: map.get($_theme, "fg-dim")
.author
color: c.$fg-dim
color: map.get($_theme, "fg-dim")
text-decoration: none
&:hover, &:active
color: c.$fg-bright
color: map.get($_theme, "fg-bright")
text-decoration: underline
@mixin recommendation-item
@ -176,15 +179,15 @@
.author-line
font-size: 15px
color: c.$fg-main
color: map.get($_theme, "fg-main")
.author
color: c.$fg-main
color: map.get($_theme, "fg-main")
.description
margin-top: 16px
font-size: 15px
color: c.$fg-dim
color: map.get($_theme, "fg-dim")
+dimensions.thin
.description
@ -195,7 +198,7 @@
.description b
font-weight: normal
color: c.$fg-main
color: map.get($_theme, "fg-main")
@mixin channel-video
@include large-item

View File

@ -1,11 +1,11 @@
@use "colors.sass" as c
@use "video-list-item.sass" as *
$_theme: () !default
@use "sass:map"
@use "video-list-item" as *
.video-page
display: grid
grid-auto-flow: row
padding: 20px
grid-gap: 16px
@media screen and (min-width: 1000px)
grid-template-columns: 1fr 400px
@ -13,10 +13,25 @@
&--recommended-below, &--recommended-hidden
grid-template-columns: none
&--recommended-hidden .related-videos
display: none
&--recommended-side
.related-videos
border-left: 1px solid map.get($_theme, "edge-grey")
padding-left: 12px
padding-right: 20px
background-color: map.get($_theme, "bg-4")
padding-top: 12px
&--recommended-below
.related-videos
padding: 20px
&--recommended-hidden
.related-videos
display: none
.main-video-section
padding: 20px
.video-container
text-align: center
@ -27,7 +42,7 @@
max-height: 80vh
.stream-notice
background: c.$bg-darkest
background: map.get($_theme, "bg-0")
padding: 4px
.info
@ -45,15 +60,15 @@
margin: 0px 0px 4px
font-size: 30px
font-weight: normal
color: c.$fg-bright
color: map.get($_theme, "fg-bright")
word-break: break-word
.author-link
color: c.$fg-main
color: map.get($_theme, "fg-main")
text-decoration: none
&:hover, &:active
color: c.$fg-bright
color: map.get($_theme, "fg-bright")
text-decoration: underline
.info-secondary
@ -75,7 +90,7 @@
margin: 16px 4px
padding: 12px
border-radius: 4px
background-color: c.$bg-darkest
background-color: map.get($_theme, "bg-0")
&__description
margin-left: 12px
@ -85,7 +100,7 @@
&__script-warning
font-size: 15px
color: c.$fg-warning
color: map.get($_theme, "fg-warning")
&__buttons
display: flex
@ -97,12 +112,12 @@
line-height: 1.4
word-break: break-word
margin: 16px 4px 4px 4px
background-color: c.$bg-accent-area
background-color: map.get($_theme, "bg-5")
padding: 12px
border-radius: 4px
--regular-background: #{c.$bg-accent-area}
--highlight-background: #{c.$bg-darker}
--regular-background: #{map.get($_theme, "bg-5")}
--highlight-background: #{map.get($_theme, "bg-1")}
.subscribe-form
display: inline-block

View File

@ -1,17 +0,0 @@
$bg-darkest: #202123
$bg-darker: #303336
$bg-dark: #36393f
$bg-accent: #4f5359
$bg-accent-x: #3f4247
$bg-accent-area: #44474b
$fg-bright: #fff
$fg-main: #ddd
$fg-dim: #bbb
$fg-warning: #fdca6d
$edge-grey: #a0a0a0
$link: #8ac2f9
$power-deep: #c62727

View File

@ -1,14 +0,0 @@
@use "colors.sass" as c
.takedown-page
max-width: 700px
margin: 0 auto
.new-section
margin-top: 200px
.important-section
padding: 4px 20px
border: 1px solid c.$edge-grey
color: c.$fg-bright
background-color: c.$bg-darker

2
sass/light.sass Normal file
View File

@ -0,0 +1,2 @@
@use "themes/light" as *
@use "includes/main" with ($_theme: $theme)

View File

@ -1,30 +0,0 @@
@use "includes/colors.sass" as c
@use "includes/base.sass"
@use "sass:selector"
@use "includes/video-page.sass"
@use "includes/search-page.sass"
@use "includes/home-page.sass"
@use "includes/channel-page.sass"
@use "includes/subscriptions-page.sass"
@use "includes/settings-page.sass"
@use "includes/cant-think-page.sass"
@use "includes/privacy-page.sass"
@use "includes/licenses-page.sass"
@use "includes/filters-page.sass"
@use "includes/takedown-page.sass"
@use "includes/forms.sass"
@use "includes/nav.sass"
@use "includes/footer.sass"
@font-face
font-family: "Bariol"
src: url(/static/fonts/bariol.woff?statichash=1)
.icon-link:hover, .icon-link:focus
.icon
filter: brightness(2)
.button-container
display: flex
flex-wrap: wrap

View File

@ -0,0 +1,27 @@
$_theme: () !default
@use "sass:map"
// remove separating edges
.main-nav, .footer__center, .video-page--recommended-side .related-videos
border: none
// no background change to recommended videos sidebar
.video-page--recommended-side .related-videos
background-color: map.get($_theme, "bg-2")
// navigation shadow
.main-nav
position: relative // needed for box shadow to overlap related videos section
box-shadow: 0px 0px 20px 5px rgba(0, 0, 0, 0.1)
// thumbnail dropdown menu dividers
.menu-look
border-color: map.get($_theme, "bg-0")
// details areas
details, .checkbox-hider__container, .more-settings
border: none
details[open] summary
border: none
margin-bottom: 4px

38
sass/themes/_dark.scss Normal file
View File

@ -0,0 +1,38 @@
// Defined in scss file instead of sass because indented syntax does not have multiline maps
// https://github.com/sass/sass/issues/216
@use "sass:map";
// This section is for colour shades
$theme: (
// darker
"bg-0": #252628,
"bg-1": #303336,
// regular
"bg-2": #36393f,
// lighter
"bg-3": #3f4247, // slightly
"bg-4": #44474b, // noticably
"bg-5": #4f5359, // brightly
"fg-bright": #fff,
"fg-main": #ddd,
"fg-dim": #bbb,
"fg-warning": #fdca6d,
"edge-grey": #a0a0a0,
"placeholder": #c4c4c4,
"link": #8ac2f9,
"power-deep": #c62727,
"power-fg": "#fff",
"image-dropdown": url(/static/images/arrow-down-wide-dark.svg)
);
// This section is for colour meanings
$theme: map.merge($theme, (
"bg-dim": map.get($theme, "bg-0"),
"bg-nav": map.get($theme, "bg-5"),
));

View File

@ -0,0 +1,8 @@
// extend regular light theme to change a couple of shades
@use "light";
@use "sass:map";
// this section is for colour meanings
$theme: map.merge(light.$theme, (
"edge-grey": #c0c0c0,
));

38
sass/themes/_light.scss Normal file
View File

@ -0,0 +1,38 @@
// Defined in scss file instead of sass because indented syntax does not have multiline maps
// https://github.com/sass/sass/issues/216
@use "sass:map";
// this section is for colour shades
$theme: (
// lighter
"bg-0": #fff,
"bg-1": #fff,
// regular
"bg-2": #f2f2f2,
// darker
"bg-3": #e8e8e8, // slightly
"bg-4": #dadada, // noticably
"bg-5": #d0d0d0, // brightly
"fg-bright": #000,
"fg-main": #202020,
"fg-dim": #454545,
"fg-warning": #ce8600,
"edge-grey": #909090,
"placeholder": #636363,
"link": #0b51d4,
"power-deep": #c62727,
"power-fg": #fff,
"image-dropdown": url(/static/images/arrow-down-wide-light.svg)
);
// this section is for colour meanings
$theme: map.merge($theme, (
"bg-dim": map.get($theme, "bg-4"),
"bg-nav": map.get($theme, "bg-0")
));

View File

@ -1,9 +1,11 @@
const {Pinski} = require("pinski")
const {setInstance} = require("pinski/plugins")
const constants = require("./utils/constants")
const iconLoader = require("./utils/icon-loader").icons
;(async () => {
await require("./utils/upgradedb")()
const icons = await iconLoader
const server = new Pinski({
port: 10412,
@ -13,19 +15,19 @@ const constants = require("./utils/constants")
setInstance(server)
server.pugDefaultLocals.constants = constants
server.pugDefaultLocals.icons = icons
server.muteLogsStartingWith("/vi/")
server.muteLogsStartingWith("/favicon")
server.muteLogsStartingWith("/static")
server.addSassDir("sass", ["sass/includes"])
server.addRoute("/static/css/main.css", "sass/main.sass", "sass")
server.addSassDir("sass", ["sass/includes", "sass/themes", "sass/theme-modules"])
server.addRoute("/static/css/dark.css", "sass/dark.sass", "sass")
server.addRoute("/static/css/light.css", "sass/light.sass", "sass")
server.addRoute("/static/css/edgeless-light.css", "sass/edgeless-light.sass", "sass")
server.addPugDir("pug", ["pug/includes"])
server.addPugDir("pug/errors")
server.addRoute("/cant-think", "pug/cant-think.pug", "pug")
server.addRoute("/privacy", "pug/privacy.pug", "pug")
server.addRoute("/licenses", "pug/licenses.pug", "pug")
server.addStaticHashTableDir("html/static/js")
server.addStaticHashTableDir("html/static/js/elemjs")

View File

@ -9,6 +9,10 @@ let constants = {
type: "string",
default: "http://localhost:3000"
},
theme: {
type: "integer",
default: 0
},
save_history: {
type: "boolean",
default: false

8
utils/icon-loader.js Normal file
View File

@ -0,0 +1,8 @@
const fs = require("fs").promises
const names = ["subscriptions", "settings"]
const icons = names.map(name => fs.readFile(`html/static/images/${name}.svg`, "utf8"))
module.exports.icons = Promise.all(icons).then(resolvedIcons => {
return new Map(names.map((name, index) => [name, resolvedIcons[index]]))
})

View File

@ -70,6 +70,11 @@ const deltas = [
.run()
db.prepare("CREATE TABLE TakedownChannels (ucid TEXT NOT NULL, org TEXT, url TEXT, PRIMARY KEY (ucid))")
.run()
},
// 11: Settings +theme
function() {
db.prepare("ALTER TABLE Settings ADD COLUMN theme INTEGER DEFAULT 0")
.run()
}
]
@ -82,7 +87,7 @@ async function createBackup(entry) {
/**
* @param {number} entry
* @param {boolean} log
* @param {boolean} [log]
*/
function runDelta(entry, log) {
process.stdout.write(`Upgrading database to version ${entry}... `)