cleaned up styling of code blocks

This commit is contained in:
Michael Freno
2026-01-04 00:52:41 -05:00
parent 999ff25fc3
commit 175d5588c0
2 changed files with 157 additions and 35 deletions

View File

@@ -96,7 +96,7 @@ export default function PostBodyClient(props: PostBodyClientProps) {
let contentRef: HTMLDivElement | undefined;
const [hljs, setHljs] = createSignal<HLJSApi | null>(null);
const addCopyButtons = () => {
const processCodeBlocks = () => {
if (!contentRef) return;
const codeBlocks = contentRef.querySelectorAll("pre code");
@@ -105,17 +105,51 @@ export default function PostBodyClient(props: PostBodyClientProps) {
const pre = codeBlock.parentElement;
if (!pre || pre.querySelector(".copy-button")) return;
// Create wrapper for positioning
pre.style.position = "relative";
// Extract language from code block classes
const classes = Array.from(codeBlock.classList);
const languageClass = classes.find((cls) => cls.startsWith("language-"));
const language = languageClass?.replace("language-", "") || "";
// Create language header if language is detected and not already present
if (
language &&
pre.previousElementSibling?.classList.contains("language-header") ===
false
) {
const languageHeader = document.createElement("div");
languageHeader.className = "language-header";
languageHeader.textContent = language;
// Insert header before pre element
pre.parentElement?.insertBefore(languageHeader, pre);
}
// Add line numbers
const codeText = codeBlock.textContent || "";
const lines = codeText.split("\n");
const lineCount =
lines[lines.length - 1] === "" ? lines.length - 1 : lines.length;
if (lineCount > 0 && !pre.querySelector(".line-numbers")) {
// Create line numbers container
const lineNumbers = document.createElement("div");
lineNumbers.className = "line-numbers";
// Generate line numbers
for (let i = 1; i <= lineCount; i++) {
const lineNum = document.createElement("div");
lineNum.textContent = i.toString();
lineNumbers.appendChild(lineNum);
}
pre.appendChild(lineNumbers);
}
// Create copy button
const copyButton = document.createElement("button");
copyButton.className =
"copy-button absolute top-2 right-2 px-3 py-1.5 text-xs font-medium rounded transition-all duration-200 z-10";
copyButton.style.cssText =
"background-color: var(--color-surface0); color: var(--color-text); border: 1px solid var(--color-overlay0);";
copyButton.className = "copy-button";
copyButton.textContent = "Copy";
copyButton.dataset.codeBlock = "true"; // Mark for event delegation
copyButton.dataset.codeBlock = "true";
pre.appendChild(copyButton);
});
@@ -162,11 +196,18 @@ export default function PostBodyClient(props: PostBodyClientProps) {
}
});
// Look for the references section marker to get the custom heading name
const marker = contentRef.querySelector(
"span[id='references-section-start']"
) as HTMLElement | null;
const referencesHeadingText =
marker?.getAttribute("data-heading") || "References";
const headings = contentRef.querySelectorAll("h2");
let referencesSection: HTMLElement | null = null;
headings.forEach((heading) => {
if (heading.textContent?.trim() === "References") {
if (heading.textContent?.trim() === referencesHeadingText) {
referencesSection = heading;
}
});
@@ -278,7 +319,7 @@ export default function PostBodyClient(props: PostBodyClientProps) {
if (hljsInstance && props.hasCodeBlock && contentRef) {
setTimeout(() => {
hljsInstance.highlightAll();
addCopyButtons();
processCodeBlocks();
}, 100);
}
});
@@ -288,7 +329,7 @@ export default function PostBodyClient(props: PostBodyClientProps) {
setTimeout(() => {
processReferences();
if (props.hasCodeBlock) {
addCopyButtons();
processCodeBlocks();
}
}, 150);
@@ -297,21 +338,6 @@ export default function PostBodyClient(props: PostBodyClientProps) {
const handleCopyButtonInteraction = async (e: Event) => {
const target = e.target as HTMLElement;
// Handle mouseenter
if (
e.type === "mouseover" &&
target.classList.contains("copy-button")
) {
target.style.backgroundColor = "var(--color-surface1)";
}
// Handle mouseleave
if (e.type === "mouseout" && target.classList.contains("copy-button")) {
if (target.textContent === "Copy") {
target.style.backgroundColor = "var(--color-surface0)";
}
}
// Handle click
if (e.type === "click" && target.classList.contains("copy-button")) {
const pre = target.parentElement;
@@ -323,22 +349,20 @@ export default function PostBodyClient(props: PostBodyClientProps) {
try {
await navigator.clipboard.writeText(code);
target.textContent = "Copied!";
target.style.backgroundColor = "var(--color-green)";
target.style.color = "var(--color-base)";
target.classList.add("copied");
setTimeout(() => {
target.textContent = "Copy";
target.style.backgroundColor = "var(--color-surface0)";
target.style.color = "var(--color-text)";
target.classList.remove("copied");
}, 2000);
} catch (err) {
console.error("Failed to copy code:", err);
target.textContent = "Failed";
target.style.backgroundColor = "var(--color-red)";
target.classList.add("failed");
setTimeout(() => {
target.textContent = "Copy";
target.style.backgroundColor = "var(--color-surface0)";
target.classList.remove("failed");
}, 2000);
}
}
@@ -346,8 +370,6 @@ export default function PostBodyClient(props: PostBodyClientProps) {
// Single event listener for all copy button interactions
contentRef.addEventListener("click", handleCopyButtonInteraction);
contentRef.addEventListener("mouseover", handleCopyButtonInteraction);
contentRef.addEventListener("mouseout", handleCopyButtonInteraction);
}
});
@@ -357,7 +379,7 @@ export default function PostBodyClient(props: PostBodyClientProps) {
setTimeout(() => {
processReferences();
if (props.hasCodeBlock) {
addCopyButtons();
processCodeBlocks();
}
}, 150);
}

View File

@@ -687,6 +687,106 @@ button:active,
#post-content-body span#references-section-start {
display: none !important;
}
/* Code block styles for published posts */
#post-content-body pre {
position: relative;
overflow: auto;
max-height: 60vh;
background-color: var(--color-crust);
color: #fff;
font-family: "Source Code Pro", monospace;
border-radius: 0.5rem;
code {
overflow: visible;
max-height: none;
background: transparent;
color: inherit;
padding: 0;
font-size: 0.8rem;
display: block;
scrollbar-color: var(--color-text) transparent;
}
}
/* Language header */
#post-content-body .language-header {
padding: 0.5rem 1rem;
font-size: 0.75rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.05em;
background-color: var(--color-mantle);
color: var(--color-subtext1);
border-bottom: 1px solid var(--color-surface0);
border-top-left-radius: 0.375rem;
border-top-right-radius: 0.375rem;
margin-bottom: 0;
}
/* Pre with language header - remove top border radius */
#post-content-body .language-header + pre {
border-top-left-radius: 0;
border-top-right-radius: 0;
margin-top: 0;
}
/* Line numbers */
#post-content-body .line-numbers {
position: absolute;
left: 0;
top: 0;
bottom: 0;
padding: 1rem 0.75rem;
text-align: right;
user-select: none;
pointer-events: none;
background-color: var(--color-crust);
border-right: 1px solid var(--color-surface0);
color: var(--color-overlay0);
font-size: 0.875rem;
line-height: 1.5rem;
font-family: inherit;
min-width: 3rem;
}
/* Code with line numbers - add padding for line numbers column */
#post-content-body pre:has(.line-numbers) code {
padding-left: 4rem;
}
/* Copy button */
#post-content-body .copy-button {
position: absolute;
top: 0.5rem;
right: 0.5rem;
padding: 0.375rem 0.75rem;
font-size: 0.75rem;
font-weight: 500;
border-radius: 0.25rem;
transition: all 0.2s;
z-index: 10;
background-color: var(--color-surface0);
color: var(--color-text);
border: 1px solid var(--color-overlay0);
cursor: pointer;
}
#post-content-body .copy-button:hover {
background-color: var(--color-surface1);
}
#post-content-body .copy-button.copied {
background-color: var(--color-green);
color: var(--color-base);
}
#post-content-body .copy-button.failed {
background-color: var(--color-red);
color: var(--color-base);
}
/* Editor styles remain unchanged */
pre {
background: #0d0d0d;
color: #fff;