[Archived] Archived WIP of vielle.dev

Add to all .astro-code elements which contains the language (or plaintext) and a copy code button.

Note that there is currently layout shift when JS loads, although I dont think it will be a major issue except on extremely low speeds. Tested on "Regular 3G" and it took <5 seconds to load; Its not a major concern.

TODO: support lightmode for codeblocks

vielle.dev 8c1ed082 253896f4

verified
Changed files
+132
src
assets
pages
blog
+16
src/assets/copy.svg
··· 1 + <svg 2 + xmlns="http://www.w3.org/2000/svg" 3 + width="24" 4 + height="24" 5 + viewBox="0 0 24 24" 6 + fill="none" 7 + stroke="currentColor" 8 + stroke-width="2" 9 + stroke-linecap="round" 10 + stroke-linejoin="round" 11 + class="lucide lucide-copy-icon lucide-copy" 12 + > 13 + <title>Copy text</title> 14 + <rect width="14" height="14" x="8" y="8" rx="2" ry="2" /> 15 + <path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2" /> 16 + </svg>
+116
src/pages/blog/[id].astro
··· 5 5 import Sun from "@/assets/sun.svg"; 6 6 import Moon from "@/assets/moon.svg"; 7 7 8 + import Copy from "@/assets/copy.svg"; 9 + 8 10 import { blog } from "@/config"; 9 11 const { 10 12 post: { light, dark, rainbow }, ··· 49 51 <Moon data-mode-dark /> 50 52 </button> 51 53 </header> 54 + 52 55 <main style={`--accent: ${colour};`}> 53 56 <div class="content"> 54 57 <Content /> ··· 94 97 button.style.visibility = "visible"; 95 98 </script> 96 99 100 + <template id="code-heading"> 101 + <!-- slots need to be set using set:html 102 + else it is treated as astro slot --> 103 + <span class="lang"> 104 + <Fragment set:html={"<slot>Plain Text</slot>"} /> 105 + </span> 106 + <button id="copy"><Copy /></button> 107 + 108 + <style> 109 + .lang { 110 + color: white; 111 + } 112 + 113 + button { 114 + border: none; 115 + background-color: #ffffff20; 116 + 117 + border-radius: 0.5rem; 118 + padding: 0; 119 + width: 2.8rem; 120 + height: 2.8rem; 121 + 122 + & svg { 123 + stroke: var(--_dark-typo-body); 124 + margin: 0.2rem; 125 + } 126 + } 127 + 128 + :host { 129 + display: flex block; 130 + justify-content: space-between; 131 + align-items: center; 132 + /* gets overridden by * because why not ig */ 133 + padding: 0.5rem !important; 134 + position: sticky; 135 + top: 0; 136 + left: 0; 137 + background-color: color-mix( 138 + in oklab, 139 + var(--_dark-bg-main), 140 + var(--_dark-bg-secondary) 141 + ); 142 + user-select: none; 143 + } 144 + </style> 145 + </template> 146 + 147 + <script> 148 + class CodeHeading extends HTMLElement { 149 + contents = ""; 150 + static observedAttributes = ["contents"]; 151 + 152 + template: HTMLTemplateElement; 153 + content: DocumentFragment; 154 + shadowRoot: ShadowRoot; 155 + 156 + constructor() { 157 + super(); 158 + const template = document.getElementById("code-heading"); 159 + if (!template || !(template instanceof HTMLTemplateElement)) 160 + throw new Error("Could not get #code-heading"); 161 + this.template = template; 162 + this.content = template.content; 163 + 164 + this.shadowRoot = this.attachShadow({ mode: "open" }); 165 + this.shadowRoot.appendChild(this.content.cloneNode(true)); 166 + 167 + const copy = this.shadowRoot.getElementById("copy"); 168 + if (!copy) throw new Error("No #copy in #code-heading"); 169 + console.log(copy, this.contents); 170 + 171 + copy.addEventListener("click", () => { 172 + navigator.clipboard.writeText(this.contents).catch((e) => { 173 + console.error("Encountered error copying to clipboard;", e); 174 + }); 175 + }); 176 + } 177 + 178 + attributeChangedCallback(name: string, _: any, newV?: string) { 179 + if (name == "contents") { 180 + this.contents = newV ?? ""; 181 + } 182 + } 183 + } 184 + 185 + customElements.define("code-heading", CodeHeading); 186 + 187 + document.querySelectorAll(".astro-code").forEach((code) => { 188 + if (!(code instanceof HTMLElement)) return; 189 + 190 + const heading = document.createElement("code-heading"); 191 + console.log(code.innerText); 192 + heading.setAttribute("contents", code.innerText); 193 + 194 + const lang = code.dataset["language"]; 195 + if (lang && lang !== "plaintext") { 196 + const langEl = document.createElement("span"); 197 + langEl.innerText = "." + lang; 198 + heading.append(langEl); 199 + } 200 + 201 + code.prepend(heading); 202 + }); 203 + </script> 204 + 97 205 <!-- post content styles --> 98 206 <!-- TODO: REFACTOR --> 99 207 <style is:global> ··· 293 401 294 402 .astro-code { 295 403 background-color: var(--_dark-bg-secondary) !important; 404 + margin-block: 1rem; 405 + padding: 0; 406 + position: relative; 407 + 408 + & code { 409 + display: block; 410 + padding: 1rem; 411 + } 296 412 } 297 413 298 414 /* Check lists */