Skip to content

Commit 75f282f

Browse files
authored
overview: provide download and copy buttons for demo code (#962)
1 parent 02ce465 commit 75f282f

File tree

2 files changed

+96
-3
lines changed

2 files changed

+96
-3
lines changed

overview/default.nix

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,32 @@ let
101101
markdownToHtml = markdown: "{{ markdown_to_html(${toJSON markdown}) }}";
102102

103103
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+
'';
104130
options = rec {
105131
one =
106132
prefixLength: option:
@@ -148,7 +174,7 @@ let
148174
one = example: ''
149175
<section><details><summary>${example.description}</summary>
150176
151-
{{ include_code("nix", "${example.module}")}}
177+
${render.codeSnippet.one { filename = example.module; }}
152178
153179
</details></section>
154180
'';
@@ -274,7 +300,11 @@ let
274300
<strong>Download this Nix file to your computer.</strong>
275301
It obtains the NGIpkgs source code and declares a basic service configuration
276302
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+
}}
278308
</li>
279309
<li>
280310
<strong>Build the virtual machine</strong> defined in <code>default.nix</code> and run it:
@@ -377,7 +407,26 @@ let
377407
<link rel="stylesheet" href="/style.css">
378408
</head>
379409
<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>
381430
</body>
382431
</html>
383432
'';

overview/style.css

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,50 @@ code, pre {
286286
overflow-x: auto;
287287
}
288288

289+
.code-block {
290+
position: relative;
291+
}
292+
293+
.code-buttons {
294+
position: absolute;
295+
top: 0.5em;
296+
right: 1em;
297+
}
298+
299+
.button {
300+
display: inline-block;
301+
padding: 0.1em 0.4em;
302+
background-color: transparent;
303+
color: white;
304+
text-align: center;
305+
text-decoration: none;
306+
border-radius: 5px;
307+
border: solid 1px gray;
308+
background-color: gray;
309+
margin: 0;
310+
font-size: 0.8rem;
311+
}
312+
313+
.button.copy:before {
314+
content: "⿻";
315+
}
316+
317+
.button.download:before {
318+
content: "⭳";
319+
}
320+
321+
.button.download {
322+
background-color: green;
323+
}
324+
325+
.button.copy:hover {
326+
background-color: darkgrey;
327+
}
328+
329+
.button.download:hover {
330+
background-color: darkgreen;
331+
}
332+
289333
details > summary > h2, details > summary > h3 {
290334
display: inline;
291335
}

0 commit comments

Comments
 (0)