diff --git a/html/static/js/channel.js b/html/static/js/channel.js
index 3e202ce..98962c5 100644
--- a/html/static/js/channel.js
+++ b/html/static/js/channel.js
@@ -1,4 +1,4 @@
import {q} from "/static/js/elemjs/elemjs.js"
-import {SubscribeButton} from "/static/js/subscribe.js"
+import {SubscribeButton} from "/static/js/modules/SubscribeButton.js"
new SubscribeButton(q("#subscribe"))
diff --git a/html/static/js/chapter-highlight.js b/html/static/js/chapter-highlight.js
new file mode 100644
index 0000000..5b44cd9
--- /dev/null
+++ b/html/static/js/chapter-highlight.js
@@ -0,0 +1,53 @@
+import {q, qa, ElemJS} from "./elemjs/elemjs.js"
+
+class Chapter {
+ constructor(linkElement) {
+ this.link = new ElemJS(linkElement)
+ this.time = +linkElement.getAttribute("data-clickable-timestamp")
+ }
+}
+
+let chapters = [...document.querySelectorAll("[data-clickable-timestamp]")].map(linkElement => new Chapter(linkElement))
+chapters.sort((a, b) => a.time - b.time)
+
+function getCurrentChapter(time) {
+ const candidates = chapters.filter(chapter => chapter.time <= time)
+ if (candidates.length > 0) {
+ return candidates[candidates.length - 1]
+ } else {
+ return null
+ }
+}
+
+const video = q("#video")
+const description = q("#description")
+const regularBackground = "var(--regular-background)"
+const highlightBackground = "var(--highlight-background)"
+const paddingWidth = 4
+let lastChapter = null
+setInterval(() => {
+ const currentChapter = getCurrentChapter(video.currentTime)
+
+ if (currentChapter !== lastChapter) {
+ // Style link
+ if (lastChapter) {
+ lastChapter.link.removeClass("timestamp--active")
+ }
+ if (currentChapter) {
+ currentChapter.link.class("timestamp--active")
+ }
+ // Style background
+ if (currentChapter) {
+ const {offsetTop, offsetHeight} = currentChapter.link.element;
+ const offsetBottom = offsetTop + offsetHeight
+ let gradient = `linear-gradient(to bottom,`
+ + ` ${regularBackground} ${offsetTop - paddingWidth}px, ${highlightBackground} ${offsetTop - paddingWidth}px,`
+ + ` ${highlightBackground} ${offsetBottom + paddingWidth}px, ${regularBackground} ${offsetBottom + paddingWidth}px)`
+ console.log(gradient)
+ description.style.background = gradient
+ } else {
+ description.style.background = ""
+ }
+ }
+ lastChapter = currentChapter
+}, 1000)
diff --git a/html/static/js/mark-watched.js b/html/static/js/modules/MarkWatchedButton.js
similarity index 100%
rename from html/static/js/mark-watched.js
rename to html/static/js/modules/MarkWatchedButton.js
diff --git a/html/static/js/subscribe.js b/html/static/js/modules/SubscribeButton.js
similarity index 100%
rename from html/static/js/subscribe.js
rename to html/static/js/modules/SubscribeButton.js
diff --git a/html/static/js/player.js b/html/static/js/player.js
index 12bd0ab..d04b34e 100644
--- a/html/static/js/player.js
+++ b/html/static/js/player.js
@@ -1,5 +1,5 @@
import {q, qa, ElemJS} from "/static/js/elemjs/elemjs.js"
-import {SubscribeButton} from "/static/js/subscribe.js"
+import {SubscribeButton} from "/static/js/modules/SubscribeButton.js"
const video = q("#video")
const audio = q("#audio")
diff --git a/html/static/js/subscriptions.js b/html/static/js/subscriptions.js
index f10fdb1..9093f9f 100644
--- a/html/static/js/subscriptions.js
+++ b/html/static/js/subscriptions.js
@@ -1,5 +1,5 @@
import {qa} from "/static/js/elemjs/elemjs.js"
-import {MarkWatchedButton} from "/static/js/mark-watched.js"
+import {MarkWatchedButton} from "/static/js/modules/MarkWatchedButton.js"
for (const button of qa(".mark-watched__button")) {
new MarkWatchedButton(button)
diff --git a/pug/video.pug b/pug/video.pug
index c56ac83..04ea3a2 100644
--- a/pug/video.pug
+++ b/pug/video.pug
@@ -9,6 +9,7 @@ block head
else
title Error - CloudTube
script(type="module" src=getStaticURL("html", "/static/js/player.js"))
+ script(type="module" src=getStaticURL("html", "/static/js/chapter-highlight.js"))
script const data = !{JSON.stringify({...video, continuous})}
block content
@@ -73,7 +74,7 @@ block content
a(href=`https://www.youtube.com/watch?v=${video.videoId}#cloudtube`).border-look YouTube
a(href=`https://redirect.invidious.io/watch?v=${video.videoId}`).border-look Invidious
- .description!= video.descriptionHtml
+ .description#description!= video.descriptionHtml
//- Standard view
aside(style=continuous ? "display: none" : "")#standard-related-videos.related-videos
diff --git a/sass/includes/video-page.sass b/sass/includes/video-page.sass
index 5b99428..6683986 100644
--- a/sass/includes/video-page.sass
+++ b/sass/includes/video-page.sass
@@ -93,13 +93,18 @@
flex-shrink: 0
.description
+ position: relative
font-size: 17px
+ line-height: 1.4
word-break: break-word
margin: 16px 4px 4px 4px
background-color: c.$bg-accent-area
padding: 12px
border-radius: 4px
+ --regular-background: #{c.$bg-accent-area}
+ --highlight-background: #{c.$bg-darker}
+
.subscribe-form
display: inline-block
@@ -149,3 +154,26 @@
.actor
margin: 4px 0px 10px
+
+// Chapter highlights
+
+.timestamp--active
+ margin: 4px 0px
+ display: inline-block
+
+ &::after
+ display: block
+ position: absolute
+ left: 0
+ right: 0
+ height: calc(1.4em + 4px)
+ transform: translateY(-1.4em) translateY(-4px)
+
+ padding-right: 6px
+ text-align: right
+ content: "⮜"
+ line-height: 1.6
+ color: #fff
+
+ border: solid black
+ border-width: 2px 0px