scroll and highlight selected line
added line numbers to source view, fixed css selectors
This commit is contained in:
+70
-71
@@ -286,124 +286,123 @@ body { font-family: 'Source Sans 3', sans-serif; }
|
||||
}
|
||||
|
||||
/* highlight.js — Atom One Dark (dark theme) */
|
||||
.source-viewer code.hljs {
|
||||
.source-viewer .hljs {
|
||||
color: #abb2bf;
|
||||
background: transparent;
|
||||
padding: 0;
|
||||
}
|
||||
.source-viewer code.hljs .hljs-comment,
|
||||
.source-viewer code.hljs .hljs-quote {
|
||||
.source-viewer .hljs .hljs-comment,
|
||||
.source-viewer .hljs .hljs-quote {
|
||||
color: #5c6370;
|
||||
font-style: italic;
|
||||
}
|
||||
.source-viewer code.hljs .hljs-doctag,
|
||||
.source-viewer code.hljs .hljs-keyword,
|
||||
.source-viewer code.hljs .hljs-formula {
|
||||
.source-viewer .hljs .hljs-doctag,
|
||||
.source-viewer .hljs .hljs-keyword,
|
||||
.source-viewer .hljs .hljs-formula {
|
||||
color: #c678dd;
|
||||
}
|
||||
.source-viewer code.hljs .hljs-section,
|
||||
.source-viewer code.hljs .hljs-name,
|
||||
.source-viewer code.hljs .hljs-selector-tag,
|
||||
.source-viewer code.hljs .hljs-deletion,
|
||||
.source-viewer code.hljs .hljs-subst {
|
||||
.source-viewer .hljs .hljs-section,
|
||||
.source-viewer .hljs .hljs-name,
|
||||
.source-viewer .hljs .hljs-selector-tag,
|
||||
.source-viewer .hljs .hljs-deletion,
|
||||
.source-viewer .hljs .hljs-subst {
|
||||
color: #e06c75;
|
||||
}
|
||||
.source-viewer code.hljs .hljs-literal {
|
||||
.source-viewer .hljs .hljs-literal {
|
||||
color: #56b6c2;
|
||||
}
|
||||
.source-viewer code.hljs .hljs-string,
|
||||
.source-viewer code.hljs .hljs-regexp,
|
||||
.source-viewer code.hljs .hljs-addition,
|
||||
.source-viewer code.hljs .hljs-attribute,
|
||||
.source-viewer code.hljs .hljs-meta .hljs-string {
|
||||
.source-viewer .hljs .hljs-string,
|
||||
.source-viewer .hljs .hljs-regexp,
|
||||
.source-viewer .hljs .hljs-addition,
|
||||
.source-viewer .hljs .hljs-attribute,
|
||||
.source-viewer .hljs .hljs-meta .hljs-string {
|
||||
color: #98c379;
|
||||
}
|
||||
.source-viewer code.hljs .hljs-attr,
|
||||
.source-viewer code.hljs .hljs-variable,
|
||||
.source-viewer code.hljs .hljs-template-variable,
|
||||
.source-viewer code.hljs .hljs-type,
|
||||
.source-viewer code.hljs .hljs-selector-class,
|
||||
.source-viewer code.hljs .hljs-selector-attr,
|
||||
.source-viewer code.hljs .hljs-selector-pseudo,
|
||||
.source-viewer code.hljs .hljs-number {
|
||||
.source-viewer .hljs .hljs-attr,
|
||||
.source-viewer .hljs .hljs-variable,
|
||||
.source-viewer .hljs .hljs-template-variable,
|
||||
.source-viewer .hljs .hljs-type,
|
||||
.source-viewer .hljs .hljs-selector-class,
|
||||
.source-viewer .hljs .hljs-selector-attr,
|
||||
.source-viewer .hljs .hljs-selector-pseudo,
|
||||
.source-viewer .hljs .hljs-number {
|
||||
color: #d19a66;
|
||||
}
|
||||
.source-viewer code.hljs .hljs-symbol,
|
||||
.source-viewer code.hljs .hljs-link,
|
||||
.source-viewer code.hljs .hljs-meta,
|
||||
.source-viewer code.hljs .hljs-selector-id,
|
||||
.source-viewer code.hljs .hljs-title {
|
||||
.source-viewer .hljs .hljs-symbol,
|
||||
.source-viewer .hljs .hljs-link,
|
||||
.source-viewer .hljs .hljs-meta,
|
||||
.source-viewer .hljs .hljs-selector-id,
|
||||
.source-viewer .hljs .hljs-title {
|
||||
color: #61aeee;
|
||||
}
|
||||
.source-viewer code.hljs .hljs-built_in,
|
||||
.source-viewer code.hljs .hljs-title.class_,
|
||||
.source-viewer code.hljs .hljs-class .hljs-title {
|
||||
.source-viewer .hljs .hljs-built_in,
|
||||
.source-viewer .hljs .hljs-title.class_,
|
||||
.source-viewer .hljs .hljs-class .hljs-title {
|
||||
color: #e6c07b;
|
||||
}
|
||||
.source-viewer code.hljs .hljs-emphasis {
|
||||
.source-viewer .hljs .hljs-emphasis {
|
||||
font-style: italic;
|
||||
}
|
||||
.source-viewer code.hljs .hljs-strong {
|
||||
.source-viewer .hljs .hljs-strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* highlight.js — Atom One Light (light theme) */
|
||||
[data-theme="light"] .source-viewer code.hljs {
|
||||
[data-theme="light"] .source-viewer .hljs {
|
||||
color: #383a42;
|
||||
background: transparent;
|
||||
}
|
||||
[data-theme="light"] .source-viewer code.hljs .hljs-comment,
|
||||
[data-theme="light"] .source-viewer code.hljs .hljs-quote {
|
||||
[data-theme="light"] .source-viewer .hljs .hljs-comment,
|
||||
[data-theme="light"] .source-viewer .hljs .hljs-quote {
|
||||
color: #a0a1a7;
|
||||
font-style: italic;
|
||||
}
|
||||
[data-theme="light"] .source-viewer code.hljs .hljs-doctag,
|
||||
[data-theme="light"] .source-viewer code.hljs .hljs-keyword,
|
||||
[data-theme="light"] .source-viewer code.hljs .hljs-formula {
|
||||
[data-theme="light"] .source-viewer .hljs .hljs-doctag,
|
||||
[data-theme="light"] .source-viewer .hljs .hljs-keyword,
|
||||
[data-theme="light"] .source-viewer .hljs .hljs-formula {
|
||||
color: #a626a4;
|
||||
}
|
||||
[data-theme="light"] .source-viewer code.hljs .hljs-section,
|
||||
[data-theme="light"] .source-viewer code.hljs .hljs-name,
|
||||
[data-theme="light"] .source-viewer code.hljs .hljs-selector-tag,
|
||||
[data-theme="light"] .source-viewer code.hljs .hljs-deletion,
|
||||
[data-theme="light"] .source-viewer code.hljs .hljs-subst {
|
||||
[data-theme="light"] .source-viewer .hljs .hljs-section,
|
||||
[data-theme="light"] .source-viewer .hljs .hljs-name,
|
||||
[data-theme="light"] .source-viewer .hljs .hljs-selector-tag,
|
||||
[data-theme="light"] .source-viewer .hljs .hljs-deletion,
|
||||
[data-theme="light"] .source-viewer .hljs .hljs-subst {
|
||||
color: #e45649;
|
||||
}
|
||||
[data-theme="light"] .source-viewer code.hljs .hljs-literal {
|
||||
[data-theme="light"] .source-viewer .hljs .hljs-literal {
|
||||
color: #0184bc;
|
||||
}
|
||||
[data-theme="light"] .source-viewer code.hljs .hljs-string,
|
||||
[data-theme="light"] .source-viewer code.hljs .hljs-regexp,
|
||||
[data-theme="light"] .source-viewer code.hljs .hljs-addition,
|
||||
[data-theme="light"] .source-viewer code.hljs .hljs-attribute,
|
||||
[data-theme="light"] .source-viewer code.hljs .hljs-meta .hljs-string {
|
||||
[data-theme="light"] .source-viewer .hljs .hljs-string,
|
||||
[data-theme="light"] .source-viewer .hljs .hljs-regexp,
|
||||
[data-theme="light"] .source-viewer .hljs .hljs-addition,
|
||||
[data-theme="light"] .source-viewer .hljs .hljs-attribute,
|
||||
[data-theme="light"] .source-viewer .hljs .hljs-meta .hljs-string {
|
||||
color: #50a14f;
|
||||
}
|
||||
[data-theme="light"] .source-viewer code.hljs .hljs-attr,
|
||||
[data-theme="light"] .source-viewer code.hljs .hljs-variable,
|
||||
[data-theme="light"] .source-viewer code.hljs .hljs-template-variable,
|
||||
[data-theme="light"] .source-viewer code.hljs .hljs-type,
|
||||
[data-theme="light"] .source-viewer code.hljs .hljs-selector-class,
|
||||
[data-theme="light"] .source-viewer code.hljs .hljs-selector-attr,
|
||||
[data-theme="light"] .source-viewer code.hljs .hljs-selector-pseudo,
|
||||
[data-theme="light"] .source-viewer code.hljs .hljs-number {
|
||||
[data-theme="light"] .source-viewer .hljs .hljs-attr,
|
||||
[data-theme="light"] .source-viewer .hljs .hljs-variable,
|
||||
[data-theme="light"] .source-viewer .hljs .hljs-template-variable,
|
||||
[data-theme="light"] .source-viewer .hljs .hljs-type,
|
||||
[data-theme="light"] .source-viewer .hljs .hljs-selector-class,
|
||||
[data-theme="light"] .source-viewer .hljs .hljs-selector-attr,
|
||||
[data-theme="light"] .source-viewer .hljs .hljs-selector-pseudo,
|
||||
[data-theme="light"] .source-viewer .hljs .hljs-number {
|
||||
color: #986801;
|
||||
}
|
||||
[data-theme="light"] .source-viewer code.hljs .hljs-symbol,
|
||||
[data-theme="light"] .source-viewer code.hljs .hljs-link,
|
||||
[data-theme="light"] .source-viewer code.hljs .hljs-meta,
|
||||
[data-theme="light"] .source-viewer code.hljs .hljs-selector-id,
|
||||
[data-theme="light"] .source-viewer code.hljs .hljs-title {
|
||||
[data-theme="light"] .source-viewer .hljs .hljs-symbol,
|
||||
[data-theme="light"] .source-viewer .hljs .hljs-link,
|
||||
[data-theme="light"] .source-viewer .hljs .hljs-meta,
|
||||
[data-theme="light"] .source-viewer .hljs .hljs-selector-id,
|
||||
[data-theme="light"] .source-viewer .hljs .hljs-title {
|
||||
color: #4078f2;
|
||||
}
|
||||
[data-theme="light"] .source-viewer code.hljs .hljs-built_in,
|
||||
[data-theme="light"] .source-viewer code.hljs .hljs-title.class_,
|
||||
[data-theme="light"] .source-viewer code.hljs .hljs-class .hljs-title {
|
||||
[data-theme="light"] .source-viewer .hljs .hljs-built_in,
|
||||
[data-theme="light"] .source-viewer .hljs .hljs-title.class_,
|
||||
[data-theme="light"] .source-viewer .hljs .hljs-class .hljs-title {
|
||||
color: #c18401;
|
||||
}
|
||||
[data-theme="light"] .source-viewer code.hljs .hljs-emphasis {
|
||||
[data-theme="light"] .source-viewer .hljs .hljs-emphasis {
|
||||
font-style: italic;
|
||||
}
|
||||
[data-theme="light"] .source-viewer code.hljs .hljs-strong {
|
||||
[data-theme="light"] .source-viewer .hljs .hljs-strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* Attached to the `#source-viewer` div via `phx-hook="SourceViewer"`.
|
||||
* Reads `data-highlighted-line` to find the target line element and
|
||||
* scrolls it into view with a smooth animation.
|
||||
* Also applies highlight.js syntax highlighting to code blocks.
|
||||
* Applies highlight.js syntax highlighting to individual line content elements.
|
||||
*/
|
||||
import hljs from "highlight.js/lib/core"
|
||||
|
||||
@@ -20,10 +20,33 @@ export const SourceViewer = {
|
||||
},
|
||||
|
||||
applySyntaxHighlighting() {
|
||||
const codeEl = this.el.querySelector("code")
|
||||
if (codeEl && !codeEl.querySelector(".hljs")) {
|
||||
hljs.highlightElement(codeEl)
|
||||
}
|
||||
const container = this.el.querySelector(".sv-lines")
|
||||
if (!container || container.querySelector(".hljs-on")) return;
|
||||
|
||||
// Collect the raw text from all line content spans
|
||||
const lineContentEls = container.querySelectorAll(".sv-line-content")
|
||||
if (lineContentEls.length === 0) return;
|
||||
|
||||
const rawText = Array.from(lineContentEls).map(el => el.textContent).join("\n")
|
||||
|
||||
// Create a temp element for highlight.js to process
|
||||
const temp = document.createElement("code")
|
||||
temp.textContent = rawText
|
||||
hljs.highlightElement(temp)
|
||||
|
||||
// Split the highlighted HTML back into lines
|
||||
// highlight.js innerHTML uses actual newlines between lines of code
|
||||
const highlightedLines = temp.innerHTML.split("\n")
|
||||
|
||||
// Apply each highlighted line to the corresponding .sv-line-content
|
||||
lineContentEls.forEach((el, i) => {
|
||||
if (highlightedLines[i]) {
|
||||
el.innerHTML = highlightedLines[i]
|
||||
}
|
||||
})
|
||||
|
||||
// Mark as highlighted so we don't re-apply on subsequent updates
|
||||
container.classList.add("hljs", "hljs-on")
|
||||
},
|
||||
|
||||
scrollToHighlighted() {
|
||||
@@ -35,4 +58,4 @@ export const SourceViewer = {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user