@farberg/reveal-template 1.1.18 → 1.1.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -1,154 +1,383 @@
|
|
|
1
1
|
/*
|
|
2
2
|
Create a zip from existing files for download and display a tree of files
|
|
3
3
|
|
|
4
|
-
<pre class="dirtree"
|
|
4
|
+
Attributes on <pre class="dirtree">:
|
|
5
|
+
data-zipname="archive.zip" — name of the downloaded zip (default: download.zip)
|
|
6
|
+
data-line-height="1.6" — line spacing (default: 1.85)
|
|
7
|
+
data-width="auto" — container width, e.g. "400px", "60%", "auto" (default: auto)
|
|
8
|
+
|
|
9
|
+
<pre class="dirtree" data-zipname="k8s-Exercise.zip" data-line-height="1.6" data-width="480px">
|
|
5
10
|
code/k8s-demo-app/01-mariadb-deployment.yaml
|
|
6
11
|
code/k8s-demo-app/02-k8s-mariadb-service.yaml
|
|
7
|
-
code/k8s-demo-app/03-memcached-deployment-and-service.yaml
|
|
8
|
-
code/k8s-demo-app/04-app-deployment-minikube.yaml
|
|
9
|
-
code/k8s-demo-app/04-app-service-and-ingress.yaml
|
|
10
12
|
code/k8s-demo-app/Dockerfile
|
|
11
13
|
code/k8s-demo-app/package.json
|
|
12
|
-
code/k8s-demo-app/server.js
|
|
13
14
|
</pre>
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
15
|
*/
|
|
18
16
|
|
|
19
17
|
const dir_tree = {
|
|
20
18
|
id: 'dir_tree',
|
|
21
19
|
init: (deck) => {
|
|
22
20
|
|
|
23
|
-
function
|
|
24
|
-
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
21
|
+
function injectStyles() {
|
|
22
|
+
if (document.getElementById('dirtree-styles')) return;
|
|
23
|
+
const style = document.createElement('style');
|
|
24
|
+
style.id = 'dirtree-styles';
|
|
25
|
+
style.textContent = `
|
|
26
|
+
.dirtree-container {
|
|
27
|
+
font-family: 'JetBrains Mono', 'Fira Code', 'Cascadia Code', 'Courier New', monospace;
|
|
28
|
+
background: transparent;
|
|
29
|
+
border-radius: 10px;
|
|
30
|
+
padding: 38px 18px 14px 18px;
|
|
31
|
+
border: 1px solid #d0d7de;
|
|
32
|
+
display: inline-block;
|
|
33
|
+
min-width: 280px;
|
|
34
|
+
max-width: 90%;
|
|
35
|
+
text-align: left;
|
|
36
|
+
position: relative !important;
|
|
37
|
+
}
|
|
28
38
|
|
|
29
|
-
|
|
39
|
+
ul.dirtree {
|
|
40
|
+
list-style: none !important;
|
|
41
|
+
padding: 0 !important;
|
|
42
|
+
margin: 0 !important;
|
|
43
|
+
}
|
|
30
44
|
|
|
31
|
-
|
|
32
|
-
|
|
45
|
+
ul.dirtree ul.dirtree {
|
|
46
|
+
padding-left: 0 !important;
|
|
47
|
+
}
|
|
33
48
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
49
|
+
li.dirtree {
|
|
50
|
+
color: #1f2328;
|
|
51
|
+
position: relative;
|
|
52
|
+
white-space: nowrap;
|
|
53
|
+
display: flex;
|
|
54
|
+
align-items: center;
|
|
37
55
|
}
|
|
38
56
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
57
|
+
li.dirtree::before,
|
|
58
|
+
li.dirtree::after {
|
|
59
|
+
display: none !important;
|
|
60
|
+
content: none !important;
|
|
61
|
+
border: none !important;
|
|
42
62
|
}
|
|
43
63
|
|
|
44
|
-
|
|
45
|
-
|
|
64
|
+
li.dirtree.root {
|
|
65
|
+
color: #0969da;
|
|
66
|
+
font-weight: 600;
|
|
67
|
+
margin-bottom: 2px;
|
|
68
|
+
}
|
|
46
69
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
70
|
+
.dirtree-dir-name {
|
|
71
|
+
color: #0969da;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
a.dirtree-file-link {
|
|
75
|
+
color: #116329;
|
|
76
|
+
text-decoration: none;
|
|
77
|
+
transition: color 0.15s;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
a.dirtree-file-link:hover {
|
|
81
|
+
color: #6639ba;
|
|
82
|
+
text-decoration: underline;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/* Copy button — hidden by default, shown on li hover */
|
|
86
|
+
.dirtree-copy-btn {
|
|
87
|
+
display: inline-flex;
|
|
88
|
+
align-items: center;
|
|
89
|
+
margin-left: 8px;
|
|
90
|
+
padding: 0px 6px;
|
|
91
|
+
background: transparent;
|
|
92
|
+
border: 1px solid #d0d7de;
|
|
93
|
+
border-radius: 4px;
|
|
94
|
+
color: #57606a;
|
|
95
|
+
font-size: 0.78em;
|
|
96
|
+
cursor: pointer;
|
|
97
|
+
opacity: 0 !important;
|
|
98
|
+
pointer-events: none !important;
|
|
99
|
+
transition: opacity 0.15s, background 0.15s, color 0.15s;
|
|
100
|
+
user-select: none;
|
|
101
|
+
line-height: 1.6;
|
|
102
|
+
font-family: inherit;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
li.dirtree:hover > .dirtree-copy-btn {
|
|
106
|
+
opacity: 1 !important;
|
|
107
|
+
pointer-events: auto !important;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.dirtree-copy-btn:hover {
|
|
111
|
+
background: #f6f8fa;
|
|
112
|
+
border-color: #8c959f;
|
|
113
|
+
color: #1f2328;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
.dirtree-copy-btn.copied {
|
|
117
|
+
color: #116329;
|
|
118
|
+
border-color: #116329;
|
|
119
|
+
background: #dafbe1;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/* Download button */
|
|
123
|
+
.dirtree-download-btn {
|
|
124
|
+
position: absolute !important;
|
|
125
|
+
top: 10px !important;
|
|
126
|
+
right: 14px !important;
|
|
127
|
+
padding: 2px 10px;
|
|
128
|
+
background: transparent;
|
|
129
|
+
border: 1px solid #d0d7de;
|
|
130
|
+
border-radius: 6px;
|
|
131
|
+
color: #cf222e !important;
|
|
132
|
+
font-size: 0.85em;
|
|
133
|
+
cursor: pointer;
|
|
134
|
+
text-decoration: none !important;
|
|
135
|
+
transition: background 0.15s, border-color 0.15s;
|
|
136
|
+
font-family: inherit;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
.dirtree-download-btn:hover {
|
|
140
|
+
background: #fff5f5 !important;
|
|
141
|
+
border-color: #cf222e !important;
|
|
142
|
+
color: #cf222e !important;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
.dirtree-prefix {
|
|
146
|
+
color: #8c959f;
|
|
147
|
+
user-select: none;
|
|
148
|
+
white-space: pre;
|
|
149
|
+
flex-shrink: 0;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
.dirtree-icon {
|
|
153
|
+
margin-right: 4px;
|
|
154
|
+
flex-shrink: 0;
|
|
155
|
+
}
|
|
156
|
+
`;
|
|
157
|
+
document.head.appendChild(style);
|
|
158
|
+
}
|
|
51
159
|
|
|
52
|
-
|
|
53
|
-
|
|
160
|
+
function getFileIcon(name, ext) {
|
|
161
|
+
if (name.toLowerCase() === 'dockerfile') return '🐳';
|
|
162
|
+
const icons = {
|
|
163
|
+
js: '📜', ts: '📜', jsx: '📜', tsx: '📜',
|
|
164
|
+
json: '📋', yaml: '📋', yml: '📋', toml: '📋', xml: '📋',
|
|
165
|
+
md: '📝', txt: '📝', rst: '📝',
|
|
166
|
+
html: '🌐', css: '🎨', scss: '🎨',
|
|
167
|
+
sh: '⚙️', bash: '⚙️', zsh: '⚙️',
|
|
168
|
+
py: '🐍', rb: '💎', go: '🔷', rs: '🦀', java: '☕',
|
|
169
|
+
png: '🖼️', jpg: '🖼️', jpeg: '🖼️', svg: '🖼️', gif: '🖼️',
|
|
170
|
+
zip: '📦', tar: '📦', gz: '📦',
|
|
171
|
+
sql: '🗄️',
|
|
172
|
+
};
|
|
173
|
+
return icons[ext] || '📄';
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function writeToClipboard(text) {
|
|
177
|
+
if (navigator.clipboard && navigator.clipboard.writeText) {
|
|
178
|
+
return navigator.clipboard.writeText(text);
|
|
54
179
|
}
|
|
55
|
-
|
|
180
|
+
// Fallback for HTTP / non-secure contexts
|
|
181
|
+
const ta = document.createElement('textarea');
|
|
182
|
+
ta.value = text;
|
|
183
|
+
ta.style.cssText = 'position:fixed;opacity:0;pointer-events:none';
|
|
184
|
+
document.body.appendChild(ta);
|
|
185
|
+
ta.focus();
|
|
186
|
+
ta.select();
|
|
187
|
+
try {
|
|
188
|
+
document.execCommand('copy');
|
|
189
|
+
return Promise.resolve();
|
|
190
|
+
} catch (e) {
|
|
191
|
+
return Promise.reject(e);
|
|
192
|
+
} finally {
|
|
193
|
+
document.body.removeChild(ta);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
56
196
|
|
|
197
|
+
function makeCopyButton(filePath) {
|
|
198
|
+
const btn = document.createElement('button');
|
|
199
|
+
btn.classList.add('dirtree-copy-btn');
|
|
200
|
+
btn.title = 'Copy file contents';
|
|
201
|
+
btn.textContent = 'copy';
|
|
202
|
+
btn.addEventListener('click', e => {
|
|
203
|
+
e.preventDefault();
|
|
204
|
+
btn.textContent = '…';
|
|
205
|
+
fetch(filePath, { credentials: 'include' })
|
|
206
|
+
.then(r => {
|
|
207
|
+
if (!r.ok) throw new Error(`HTTP ${r.status}`);
|
|
208
|
+
return r.text();
|
|
209
|
+
})
|
|
210
|
+
.then(text => writeToClipboard(text))
|
|
211
|
+
.then(() => {
|
|
212
|
+
btn.textContent = '✓ copied';
|
|
213
|
+
btn.classList.add('copied');
|
|
214
|
+
setTimeout(() => {
|
|
215
|
+
btn.textContent = 'copy';
|
|
216
|
+
btn.classList.remove('copied');
|
|
217
|
+
}, 1500);
|
|
218
|
+
})
|
|
219
|
+
.catch(() => {
|
|
220
|
+
btn.textContent = '✗ error';
|
|
221
|
+
setTimeout(() => { btn.textContent = 'copy'; }, 1500);
|
|
222
|
+
});
|
|
223
|
+
});
|
|
224
|
+
return btn;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function getFileList(el) {
|
|
228
|
+
return el.innerText.split('\n').filter(e => e.trim().length > 0);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function createTree(files) {
|
|
232
|
+
function getPath(tree, path) {
|
|
233
|
+
let node = tree[path[0]];
|
|
234
|
+
if (!node) { node = {}; tree[path[0]] = node; }
|
|
235
|
+
const rest = path.slice(1);
|
|
236
|
+
return rest.length > 0 ? getPath(node, rest) : node;
|
|
237
|
+
}
|
|
238
|
+
const tree = {};
|
|
239
|
+
for (const file of files) {
|
|
240
|
+
const parts = file.split('/');
|
|
241
|
+
const filename = parts.pop();
|
|
242
|
+
getPath(tree, parts)[filename] = filename;
|
|
243
|
+
}
|
|
244
|
+
return tree;
|
|
57
245
|
}
|
|
58
246
|
|
|
59
247
|
function download(files, zipName) {
|
|
60
|
-
Promise.all(files.map(
|
|
61
|
-
.then(
|
|
62
|
-
|
|
63
|
-
const hasUnauthorized = promises.some(p => p.status === 401);
|
|
64
|
-
if (hasUnauthorized) {
|
|
65
|
-
console.log("Authentication required (dir-tree), reloading page");
|
|
248
|
+
Promise.all(files.map(f => fetch(f, { credentials: 'include' })))
|
|
249
|
+
.then(responses => {
|
|
250
|
+
if (responses.some(r => r.status === 401)) {
|
|
66
251
|
window.location.reload();
|
|
67
252
|
return;
|
|
68
253
|
}
|
|
69
|
-
return Promise.all(
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
})
|
|
76
|
-
})
|
|
77
|
-
})
|
|
78
|
-
|
|
254
|
+
return Promise.all(responses.map(r => r.text())).then(values => {
|
|
255
|
+
const zip = new JSZip();
|
|
256
|
+
values.forEach((text, i) => zip.file(files[i], text));
|
|
257
|
+
zip.generateAsync({ type: 'blob' }).then(blob => saveAs(blob, zipName));
|
|
258
|
+
});
|
|
259
|
+
});
|
|
79
260
|
}
|
|
80
261
|
|
|
81
|
-
function displayTree(el, treeNode, path,
|
|
82
|
-
|
|
83
|
-
for (let name of Object.getOwnPropertyNames(treeNode)) {
|
|
84
|
-
const o = treeNode[name]
|
|
85
|
-
const isDir = typeof o === "object"
|
|
262
|
+
function displayTree(el, treeNode, path, isRoot, files, zipName, ancestorLast) {
|
|
263
|
+
const names = Object.getOwnPropertyNames(treeNode);
|
|
86
264
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
265
|
+
names.forEach((name, index) => {
|
|
266
|
+
const o = treeNode[name];
|
|
267
|
+
const isDir = typeof o === 'object';
|
|
268
|
+
const isLast = index === names.length - 1;
|
|
91
269
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
li.innerHTML = name + " "
|
|
270
|
+
const li = document.createElement('li');
|
|
271
|
+
li.classList.add('dirtree');
|
|
95
272
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
a.innerHTML = `(download ${zipName})`
|
|
99
|
-
a.href = "#zip/" + zipName
|
|
100
|
-
a.style = "color: #e10217";
|
|
101
|
-
a.addEventListener('click', e => {
|
|
102
|
-
e.preventDefault()
|
|
103
|
-
download(files, zipName)
|
|
104
|
-
})
|
|
273
|
+
if (isRoot) {
|
|
274
|
+
li.classList.add('root');
|
|
105
275
|
|
|
276
|
+
const icon = document.createElement('span');
|
|
277
|
+
icon.classList.add('dirtree-icon');
|
|
278
|
+
icon.textContent = '📁';
|
|
106
279
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
280
|
+
const nameSpan = document.createElement('span');
|
|
281
|
+
nameSpan.classList.add('dirtree-dir-name');
|
|
282
|
+
nameSpan.textContent = name;
|
|
110
283
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
284
|
+
li.appendChild(icon);
|
|
285
|
+
li.appendChild(nameSpan);
|
|
286
|
+
el.appendChild(li);
|
|
114
287
|
|
|
115
|
-
|
|
288
|
+
const ul = document.createElement('ul');
|
|
289
|
+
ul.classList.add('dirtree');
|
|
290
|
+
li.appendChild(ul);
|
|
291
|
+
displayTree(ul, o, `${path}/${name}`, false, files, zipName, []);
|
|
116
292
|
|
|
117
293
|
} else {
|
|
294
|
+
const contPrefix = ancestorLast.map(last => last ? ' ' : '│ ').join('');
|
|
295
|
+
const connector = isLast ? '└── ' : '├── ';
|
|
296
|
+
|
|
297
|
+
const prefixSpan = document.createElement('span');
|
|
298
|
+
prefixSpan.classList.add('dirtree-prefix');
|
|
299
|
+
prefixSpan.textContent = contPrefix + connector;
|
|
300
|
+
li.appendChild(prefixSpan);
|
|
301
|
+
|
|
302
|
+
if (isDir) {
|
|
303
|
+
const icon = document.createElement('span');
|
|
304
|
+
icon.classList.add('dirtree-icon');
|
|
305
|
+
icon.textContent = '📂';
|
|
118
306
|
|
|
119
|
-
|
|
120
|
-
|
|
307
|
+
const nameSpan = document.createElement('span');
|
|
308
|
+
nameSpan.classList.add('dirtree-dir-name');
|
|
309
|
+
nameSpan.textContent = name;
|
|
121
310
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
311
|
+
li.appendChild(icon);
|
|
312
|
+
li.appendChild(nameSpan);
|
|
313
|
+
el.appendChild(li);
|
|
125
314
|
|
|
126
|
-
|
|
315
|
+
const ul = document.createElement('ul');
|
|
316
|
+
ul.classList.add('dirtree');
|
|
317
|
+
li.appendChild(ul);
|
|
318
|
+
displayTree(ul, o, `${path}/${name}`, false, files, zipName, [...ancestorLast, isLast]);
|
|
319
|
+
|
|
320
|
+
} else {
|
|
321
|
+
const ext = name.includes('.') ? name.split('.').pop().toLowerCase() : '';
|
|
322
|
+
|
|
323
|
+
const icon = document.createElement('span');
|
|
324
|
+
icon.classList.add('dirtree-icon');
|
|
325
|
+
icon.textContent = getFileIcon(name, ext);
|
|
326
|
+
|
|
327
|
+
const finalPath = `${path}/${o}`.replace(/^\//, '');
|
|
328
|
+
const a = document.createElement('a');
|
|
329
|
+
a.classList.add('dirtree-file-link');
|
|
330
|
+
a.href = finalPath;
|
|
331
|
+
a.textContent = o;
|
|
332
|
+
|
|
333
|
+
li.appendChild(icon);
|
|
334
|
+
li.appendChild(a);
|
|
335
|
+
li.appendChild(makeCopyButton(finalPath));
|
|
336
|
+
el.appendChild(li);
|
|
337
|
+
}
|
|
127
338
|
}
|
|
128
|
-
}
|
|
339
|
+
});
|
|
129
340
|
}
|
|
130
341
|
|
|
131
342
|
deck.on('ready', () => {
|
|
132
|
-
|
|
133
|
-
const zipName = el.getAttribute("data-zipname") || "download.zip"
|
|
134
|
-
const stripPrefix = el.getAttribute("data-strip-prefix")
|
|
343
|
+
injectStyles();
|
|
135
344
|
|
|
136
|
-
|
|
137
|
-
const
|
|
345
|
+
for (const el of deck.getRevealElement().querySelectorAll('pre.dirtree')) {
|
|
346
|
+
const zipName = el.getAttribute('data-zipname') || 'download.zip';
|
|
347
|
+
const lineHeight = el.getAttribute('data-line-height') || '1.85';
|
|
348
|
+
const width = el.getAttribute('data-width') || 'auto';
|
|
138
349
|
|
|
139
|
-
const
|
|
140
|
-
|
|
350
|
+
const files = getFileList(el);
|
|
351
|
+
const tree = createTree(files);
|
|
141
352
|
|
|
142
|
-
|
|
353
|
+
const container = document.createElement('div');
|
|
354
|
+
container.classList.add('dirtree-container');
|
|
355
|
+
container.style.lineHeight = lineHeight;
|
|
356
|
+
if (width !== 'auto') container.style.width = width;
|
|
143
357
|
|
|
144
|
-
|
|
145
|
-
|
|
358
|
+
// Forward font-size (and other inline styles) from the <pre>
|
|
359
|
+
if (el.getAttribute('style')) container.setAttribute('style', el.getAttribute('style'));
|
|
360
|
+
// Re-apply data-driven styles on top (inline style from <pre> must not override these)
|
|
361
|
+
container.style.lineHeight = lineHeight;
|
|
362
|
+
if (width !== 'auto') container.style.width = width;
|
|
363
|
+
|
|
364
|
+
const btn = document.createElement('a');
|
|
365
|
+
btn.classList.add('dirtree-download-btn');
|
|
366
|
+
btn.textContent = '⬇ ' + zipName;
|
|
367
|
+
btn.href = '#zip/' + zipName;
|
|
368
|
+
btn.addEventListener('click', e => { e.preventDefault(); download(files, zipName); });
|
|
369
|
+
container.appendChild(btn);
|
|
146
370
|
|
|
147
|
-
|
|
371
|
+
const ul = document.createElement('ul');
|
|
372
|
+
ul.classList.add('dirtree');
|
|
373
|
+
container.appendChild(ul);
|
|
148
374
|
|
|
375
|
+
displayTree(ul, tree, '', true, files, zipName, []);
|
|
149
376
|
|
|
377
|
+
el.parentElement.replaceChild(container, el);
|
|
378
|
+
}
|
|
379
|
+
});
|
|
150
380
|
}
|
|
151
381
|
}
|
|
152
382
|
|
|
153
|
-
|
|
154
|
-
export default dir_tree;
|
|
383
|
+
export default dir_tree;
|
|
@@ -105,21 +105,43 @@ function injectStyles() {
|
|
|
105
105
|
document.head.appendChild(style);
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
+
function writeToClipboard(text) {
|
|
109
|
+
if (navigator.clipboard && navigator.clipboard.writeText)
|
|
110
|
+
return navigator.clipboard.writeText(text);
|
|
111
|
+
const ta = document.createElement('textarea');
|
|
112
|
+
ta.value = text;
|
|
113
|
+
ta.style.cssText = 'position:fixed;opacity:0;pointer-events:none';
|
|
114
|
+
document.body.appendChild(ta);
|
|
115
|
+
ta.focus();
|
|
116
|
+
ta.select();
|
|
117
|
+
try {
|
|
118
|
+
document.execCommand('copy');
|
|
119
|
+
return Promise.resolve();
|
|
120
|
+
} catch (e) {
|
|
121
|
+
return Promise.reject(e);
|
|
122
|
+
} finally {
|
|
123
|
+
document.body.removeChild(ta);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
108
127
|
function addCopyButton(preEl) {
|
|
109
128
|
preEl.classList.add('with-copy-btn');
|
|
110
129
|
const btn = document.createElement('button');
|
|
111
130
|
btn.className = 'copy-code-btn';
|
|
112
|
-
btn.textContent = '
|
|
131
|
+
btn.textContent = 'copy';
|
|
113
132
|
btn.addEventListener('click', () => {
|
|
114
133
|
const code = preEl.querySelector('code');
|
|
115
134
|
const text = code ? code.innerText : preEl.innerText;
|
|
116
|
-
|
|
117
|
-
btn.textContent = '
|
|
135
|
+
writeToClipboard(text).then(() => {
|
|
136
|
+
btn.textContent = '✓ copied';
|
|
118
137
|
btn.classList.add('copied');
|
|
119
138
|
setTimeout(() => {
|
|
120
|
-
btn.textContent = '
|
|
139
|
+
btn.textContent = 'copy';
|
|
121
140
|
btn.classList.remove('copied');
|
|
122
141
|
}, 1500);
|
|
142
|
+
}).catch(() => {
|
|
143
|
+
btn.textContent = '✗ error';
|
|
144
|
+
setTimeout(() => { btn.textContent = 'copy'; }, 1500);
|
|
123
145
|
});
|
|
124
146
|
});
|
|
125
147
|
preEl.appendChild(btn);
|
|
@@ -148,6 +170,7 @@ export default () => {
|
|
|
148
170
|
for (let el of deck.getRevealElement().querySelectorAll("pre code[class]")) {
|
|
149
171
|
if (!el.classList.contains("mermaid")) {
|
|
150
172
|
highlightPlugin.hljs.highlightElement(el);
|
|
173
|
+
addCopyButton(el.closest('pre'));
|
|
151
174
|
}
|
|
152
175
|
}
|
|
153
176
|
|
|
@@ -191,8 +214,10 @@ export default () => {
|
|
|
191
214
|
code = outdent(code)
|
|
192
215
|
|
|
193
216
|
const newEl = showCode(el, language, code, showLink ? url : null, outdent)
|
|
194
|
-
if (language !== 'mermaid')
|
|
217
|
+
if (language !== 'mermaid') {
|
|
195
218
|
highlightPlugin.hljs.highlightElement(newEl.querySelector('code') ?? newEl)
|
|
219
|
+
addCopyButton(newEl)
|
|
220
|
+
}
|
|
196
221
|
} catch (err) {
|
|
197
222
|
console.error(`show-code-snippets: failed to load ${url}:`, err)
|
|
198
223
|
showError(el, `Failed to load ${url}: ${err.message}`)
|