|
101 | 101 | markdownToHtml = markdown: "{{ markdown_to_html(${toJSON markdown}) }}";
|
102 | 102 |
|
103 | 103 | render = {
|
| 104 | + # A code snippet that is copyable and optionally downloadable |
| 105 | + codeSnippet.one = |
| 106 | + { |
| 107 | + filename, |
| 108 | + language ? "nix", |
| 109 | + relative ? false, |
| 110 | + downloadable ? false, |
| 111 | + }: |
| 112 | + '' |
| 113 | + <div class="code-block"> |
| 114 | + {{ include_code("${language}", "${filename}" ${optionalString relative ", relative_path=True"}) }} |
| 115 | + <div class="code-buttons"> |
| 116 | + ${optionalString downloadable '' |
| 117 | + <a class="button download" href="${filename}" download>Download</a> |
| 118 | + ''} |
| 119 | + <button class="button copy" onclick="copyToClipboard(this, '${filename}')"> |
| 120 | + ${optionalString (!relative) '' |
| 121 | + <script type="application/json"> |
| 122 | + ${toJSON (readFile filename)} |
| 123 | + </script> |
| 124 | + ''} |
| 125 | + Copy |
| 126 | + </button> |
| 127 | + </div> |
| 128 | + </div> |
| 129 | + ''; |
104 | 130 | options = rec {
|
105 | 131 | one =
|
106 | 132 | prefixLength: option:
|
|
148 | 174 | one = example: ''
|
149 | 175 | <section><details><summary>${example.description}</summary>
|
150 | 176 |
|
151 |
| - {{ include_code("nix", "${example.module}")}} |
| 177 | + ${render.codeSnippet.one { filename = example.module; }} |
152 | 178 |
|
153 | 179 | </details></section>
|
154 | 180 | '';
|
|
274 | 300 | <strong>Download this Nix file to your computer.</strong>
|
275 | 301 | It obtains the NGIpkgs source code and declares a basic service configuration
|
276 | 302 | to be run in a virtual machine.
|
277 |
| - {{ include_code("nix", "default.nix", relative_path=True) }} |
| 303 | + ${render.codeSnippet.one { |
| 304 | + filename = "default.nix"; |
| 305 | + relative = true; |
| 306 | + downloadable = true; |
| 307 | + }} |
278 | 308 | </li>
|
279 | 309 | <li>
|
280 | 310 | <strong>Build the virtual machine</strong> defined in <code>default.nix</code> and run it:
|
|
377 | 407 | <link rel="stylesheet" href="/style.css">
|
378 | 408 | </head>
|
379 | 409 | <body>
|
380 |
| - ${args.content} |
| 410 | + ${args.content} |
| 411 | + <script> |
| 412 | + async function copyToClipboard(button, url) { |
| 413 | + let code; |
| 414 | + const firstChild = Array.from(button.children).find(child => child.tagName === "SCRIPT"); |
| 415 | + if (firstChild) { |
| 416 | + // JSON is just used for string escaping |
| 417 | + code = JSON.parse(firstChild.textContent); |
| 418 | + } else { |
| 419 | + const response = await fetch(url); |
| 420 | + if (!response.ok) { |
| 421 | + throw new Error("Failed to fetch file: " + response.statusText); |
| 422 | + } |
| 423 | + code = await response.text(); |
| 424 | + } |
| 425 | + await navigator.clipboard.writeText(code); |
| 426 | + button.textContent = "Copied ✓"; |
| 427 | + setTimeout(() => button.textContent = "Copy", 2000); |
| 428 | + } |
| 429 | + </script> |
381 | 430 | </body>
|
382 | 431 | </html>
|
383 | 432 | '';
|
|
0 commit comments