diff --git a/src/parser/copycode.ts b/src/parser/copycode.ts
new file mode 100644
index 0000000..cb611b9
--- /dev/null
+++ b/src/parser/copycode.ts
@@ -0,0 +1,30 @@
+import MarkdownIt from 'markdown-it';
+import octicons from '@primer/octicons';
+
+const copyIcon = octicons['copy'].toSVG({ class: 'icon-copy' });
+const checkIcon = octicons['check'].toSVG({ class: 'icon-check' });
+const xIcon = octicons['x'].toSVG({ class: 'icon-x' });
+
+export default function copycode(md: MarkdownIt) {
+ const defaultRender = md.renderer.rules.fence!;
+ md.renderer.rules.fence = (tokens, idx, options, env, self) => {
+ const renderedPreBlock = defaultRender(tokens, idx, options, env, self);
+ const content = tokens[idx].content;
+ return `
+
+ ${renderedPreBlock}
+
+
+
+ ${checkIcon}
+
+
+ ${xIcon}
+
+
+
+`;
+ };
+}
diff --git a/src/parser/markdown.ts b/src/parser/markdown.ts
index 9a0fe9d..ffdadeb 100644
--- a/src/parser/markdown.ts
+++ b/src/parser/markdown.ts
@@ -1,5 +1,6 @@
import MarkdownIt from 'markdown-it';
import anchor from 'markdown-it-anchor';
+import copycode from './copycode.js';
import frontMatter from 'markdown-it-front-matter';
import highlight from './highlight.js';
import graphviz from './dot.js';
@@ -68,6 +69,7 @@ mdit.use(anchor, {
placement: 'before',
}),
});
+mdit.use(copycode);
mdit.use(graphviz);
mdit.use(githubAlerts);
mdit.use(mermaid);
diff --git a/src/routes/viewer.ts b/src/routes/viewer.ts
index b53e80a..ec569c4 100644
--- a/src/routes/viewer.ts
+++ b/src/routes/viewer.ts
@@ -88,6 +88,8 @@ router.get(/.*/, async (req: Request, res: Response) => {
${config.scripts ? `` : ''}
+
+