diff --git a/src/lib/components/Markdown.svelte b/src/lib/components/Markdown.svelte
index de0f48b..81f94da 100644
--- a/src/lib/components/Markdown.svelte
+++ b/src/lib/components/Markdown.svelte
@@ -17,7 +17,20 @@
renderer.code = ({ text, lang }) => {
const language = lang && hljs.getLanguage(lang) ? lang : "plaintext";
const highlighted = hljs.highlight(text, { language }).value;
- return `
${highlighted}
`;
+ const escapedText = text.replace(/"/g, """).replace(//g, ">");
+ return ``;
};
renderer.codespan = ({ text }) => {
@@ -123,6 +136,28 @@
}
}
+ async function handleCopyClick(event: MouseEvent) {
+ const target = event.target as HTMLElement;
+ const copyBtn = target.closest(".copy-code-btn") as HTMLButtonElement;
+ if (copyBtn) {
+ event.preventDefault();
+ const code = copyBtn.dataset.code
+ ?.replace(/"/g, '"')
+ .replace(/</g, "<")
+ .replace(/>/g, ">");
+ if (code) {
+ await navigator.clipboard.writeText(code);
+ const textSpan = copyBtn.querySelector(".copy-text");
+ if (textSpan) {
+ textSpan.textContent = "Copied!";
+ setTimeout(() => {
+ textSpan.textContent = "Copy";
+ }, 2000);
+ }
+ }
+ }
+ }
+
onMount(() => {
if (containerElement) {
containerElement.querySelectorAll("pre code:not(.hljs)").forEach((block) => {
@@ -138,6 +173,7 @@
onclick={(e) => {
handleSpoilerClick(e);
handleLinkClick(e);
+ handleCopyClick(e);
}}
onkeydown={handleSpoilerKeydown}
role="presentation"
@@ -163,13 +199,59 @@
margin-bottom: 0;
}
+ .markdown-content :global(.code-block-wrapper) {
+ margin: 0.75em 0;
+ border-radius: 6px;
+ border: 1px solid var(--border-color);
+ overflow: hidden;
+ }
+
+ .markdown-content :global(.code-block-header) {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ background: var(--bg-secondary);
+ padding: 0.4em 0.75em;
+ border-bottom: 1px solid var(--border-color);
+ font-size: 0.8em;
+ }
+
+ .markdown-content :global(.code-block-lang) {
+ color: var(--text-secondary);
+ font-family: "JetBrains Mono", "Fira Code", monospace;
+ text-transform: lowercase;
+ }
+
+ .markdown-content :global(.copy-code-btn) {
+ display: flex;
+ align-items: center;
+ gap: 0.4em;
+ background: transparent;
+ border: none;
+ color: var(--text-secondary);
+ cursor: pointer;
+ padding: 0.25em 0.5em;
+ border-radius: 4px;
+ font-size: 0.9em;
+ transition: all 0.15s ease;
+ }
+
+ .markdown-content :global(.copy-code-btn:hover) {
+ background: var(--bg-hover);
+ color: var(--text-primary);
+ }
+
+ .markdown-content :global(.copy-code-btn svg) {
+ flex-shrink: 0;
+ }
+
.markdown-content :global(.hljs-code-block) {
background: var(--bg-code, #1e1e2e);
- border-radius: 6px;
+ border-radius: 0;
padding: 1em;
- margin: 0.75em 0;
+ margin: 0;
overflow-x: auto;
- border: 1px solid var(--border-color);
+ border: none;
}
.markdown-content :global(.hljs-code-block code) {