This commit is contained in:
Sebastian Whincop 2025-03-12 12:53:04 +08:00 committed by GitHub
commit 807a36acd5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 122 additions and 1 deletions

View File

@ -6,6 +6,10 @@
import { onMount, tick, getContext } from 'svelte';
import type { Writable } from 'svelte/store';
import type { i18n as i18nType } from 'i18next';
import { marked } from 'marked';
import markedExtension from '$lib/utils/marked/extension';
import markedKatexExtension from '$lib/utils/marked/katex-extension';
import hljs from 'highlight.js';
const i18n = getContext<Writable<i18nType>>('i18n');
@ -155,6 +159,97 @@
}
};
const copyFormattedToClipboard = async (text) => {
// Configure marked with the same extensions used in Markdown.svelte
const options = {
throwOnError: false,
highlight: function(code, lang) {
const language = hljs.getLanguage(lang) ? lang : 'plaintext';
return hljs.highlight(code, { language }).value;
}
};
marked.use(markedKatexExtension(options));
marked.use(markedExtension(options));
// Convert markdown to HTML using marked
const htmlContent = marked.parse(text);
// Add basic styling to make the content look better when pasted
const styledHtml = `
<div style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; line-height: 1.6; color: #333; max-width: 800px; margin: 0 auto; padding: 16px;">
<style>
pre {
background-color: #f6f8fa;
border-radius: 6px;
padding: 16px;
overflow: auto;
}
code {
font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
font-size: 14px;
}
.hljs-keyword { color: #d73a49; }
.hljs-string { color: #032f62; }
.hljs-comment { color: #6a737d; }
.hljs-function { color: #6f42c1; }
.hljs-number { color: #005cc5; }
.hljs-operator { color: #d73a49; }
.hljs-class { color: #6f42c1; }
.hljs-title { color: #6f42c1; }
.hljs-params { color: #24292e; }
.hljs-built_in { color: #005cc5; }
blockquote {
border-left: 4px solid #dfe2e5;
padding-left: 16px;
color: #6a737d;
margin-left: 0;
margin-right: 0;
}
table {
border-collapse: collapse;
width: 100%;
margin-bottom: 16px;
}
table, th, td {
border: 1px solid #dfe2e5;
}
th, td {
padding: 8px 12px;
}
th {
background-color: #f6f8fa;
}
</style>
${htmlContent}
<div style="font-size: 12px; margin-top: 20px; color: #888; border-top: 1px solid #eee; padding-top: 8px;">
</div>
</div>
`;
// Create a blob with HTML content
const blob = new Blob([styledHtml], { type: 'text/html' });
try {
// Create a ClipboardItem with HTML content
const data = new ClipboardItem({
'text/html': blob,
'text/plain': new Blob([text], { type: 'text/plain' })
});
// Write to clipboard
await navigator.clipboard.write([data]);
toast.success($i18n.t('Formatted content copied to clipboard!'));
} catch (err) {
console.error('Error copying formatted content:', err);
// Fallback to plain text
const res = await _copyToClipboard(text);
if (res) {
toast.success($i18n.t('Copied as plain text (formatted copy not supported in this browser)'));
}
}
};
const playAudio = (idx: number) => {
return new Promise<void>((res) => {
speakingIdx = idx;
@ -933,6 +1028,32 @@
</button>
</Tooltip>
<Tooltip content={$i18n.t('Copy Formatted')} placement="bottom">
<button
class="{isLastMessage
? 'visible'
: 'invisible group-hover:visible'} p-1.5 hover:bg-black/5 dark:hover:bg-white/5 rounded-lg dark:hover:text-white hover:text-black transition copy-formatted-button"
on:click={() => {
copyFormattedToClipboard(message.content);
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="2.3"
stroke="currentColor"
class="w-4 h-4"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M8 7v8a2 2 0 002 2h6M8 7V5a2 2 0 012-2h4.586a1 1 0 01.707.293l4.414 4.414a1 1 0 01.293.707V15a2 2 0 01-2 2h-2M8 7H6a2 2 0 00-2 2v10a2 2 0 002 2h8a2 2 0 002-2v-2"
/>
</svg>
</button>
</Tooltip>
<Tooltip content={$i18n.t('Read Aloud')} placement="bottom">
<button
id="speak-button-{message.id}"
@ -989,7 +1110,7 @@
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M17.25 9.75 19.5 12m0 0 2.25 2.25M19.5 12l2.25-2.25M19.5 12l-2.25 2.25m-10.5-6 4.72-4.72a.75.75 0 0 1 1.28.53v15.88a.75.75 0 0 1-1.28.53l-4.72-4.72H4.51c-.88 0-1.704-.507-1.938-1.354A9.009 9.009 0 0 1 2.25 12c0-.83.112-1.633.322-2.396C2.806 8.756 3.63 8.25 4.51 8.25H6.75Z"
d="M17.25 9.75 19.5 12m0 0 2.25 2.25M19.5 12l2.25-2.25M19.5 12l-2.25 2.25m-10.5-6 4.72-4.72a.75.75 0 0 1 1.28.53v15.88a.75.75 0 0 1-1.28.53l-4.72-4.72H4.51c-.88 0-1.704-.507-1.938-1.354A9.01 9.01 0 0 1 2.25 12c0-.83.112-1.633.322-2.396C2.806 8.756 3.63 8.25 4.51 8.25H6.75Z"
/>
</svg>
{:else}