fix(chat): Copy buttons working again

This commit is contained in:
2025-12-27 19:57:49 +01:00
parent 4e8ebc040c
commit 5655b88b31
+33 -28
View File
@@ -42,7 +42,7 @@
margin: 0; margin: 0;
font-family: "JetBrains Mono", Consolas, Menlo, monospace; font-family: "JetBrains Mono", Consolas, Menlo, monospace;
line-height: 1.6; line-height: 1.6;
font-size: 16px; font-size: 14px;
background: var(--bg-primary); background: var(--bg-primary);
color: var(--text-primary); color: var(--text-primary);
transition: background-color 0.2s, color 0.2s; transition: background-color 0.2s, color 0.2s;
@@ -128,7 +128,7 @@
border: 1px solid var(--border-color); border: 1px solid var(--border-color);
border-radius: 0.4rem; border-radius: 0.4rem;
resize: vertical; resize: vertical;
font-size: 1rem; font-size: inherit;
background: var(--bg-input); background: var(--bg-input);
color: var(--text-primary); color: var(--text-primary);
transition: background-color 0.2s, color 0.2s; transition: background-color 0.2s, color 0.2s;
@@ -137,7 +137,7 @@
color: var(--text-secondary); color: var(--text-secondary);
} }
.submit { .submit {
font-size: 1rem; font-size: inherit;
grid-area: submit; grid-area: submit;
cursor: pointer; cursor: pointer;
padding: 0.8rem 2rem; padding: 0.8rem 2rem;
@@ -229,19 +229,23 @@
} }
.spinner { .spinner {
width: 1.5rem;
height: 1.5rem;
background: #888;
display: inline-block; display: inline-block;
width: 1em;
height: 1em;
border: 3px solid var(--text-secondary);
border-top-color: transparent;
border-radius: 50%; border-radius: 50%;
vertical-align: baseline; box-sizing: border-box;
animation: spin 1s ease-in infinite; animation: grow 1s ease-in infinite;
} }
@keyframes spin { @keyframes grow {
to { 0% {
transform: rotate(360deg); transform: scale(0);
opacity: 1;
}
100% {
transform: scale(1);
opacity: 0;
} }
} }
</style> </style>
@@ -250,7 +254,7 @@
<div id="app" x-data="chatApp()" x-cloak> <div id="app" x-data="chatApp()" x-cloak>
<div class="chat-container"> <div class="chat-container">
<!-- Messages --> <!-- Messages -->
<div class="messages" x-ref="msgBox" @scroll="handleScroll"> <div class="messages" x-ref="msgBox" @click="onClickMessage" @scroll="handleScroll">
<template x-for="(msg, idx) in visibleMessages" :key="idx"> <template x-for="(msg, idx) in visibleMessages" :key="idx">
<div :class="msg.role === 'user' ? 'message user-message' : 'message system-message'"> <div :class="msg.role === 'user' ? 'message user-message' : 'message system-message'">
<template x-if="msg.role === 'user'"> <template x-if="msg.role === 'user'">
@@ -317,7 +321,7 @@
You are a helpful AI assistant. You are a helpful AI assistant.
Do not preach about implications, do not praise excessively. Do not preach about implications, do not praise excessively.
Keep a critical mindset, do not assume blindly. Keep a critical mindset, do not assume blindly.
When I ask for code, assume that I am a senior backend developer (do not mention that) and do not explain excessively, but and challenge my statements if I don't sound right, especially in coding. When I ask for code, keep in mind that I am a senior backend developer (but do not mention that explicitly), and do not explain excessively, but and challenge my statements with better alternatives if I don't sound right, especially in coding.
Answer in valid markdown only, no exceptions. Answer in valid markdown only, no exceptions.
` `
.replace(/^\s+/gm, "") .replace(/^\s+/gm, "")
@@ -341,19 +345,21 @@
async init() { async init() {
this.historyItems = loadHistory(); this.historyItems = loadHistory();
// Delegate copy button clicks
this.$refs.msgBox.addEventListener("click", (e) => {
if (e.target.classList.contains("copy-btn")) {
const code = e.target.closest("pre").querySelector("code").innerText;
copyToClipboard(code);
const original = e.target.textContent;
e.target.textContent = "Copied!";
setTimeout(() => (e.target.textContent = original), 1200);
}
});
setTimeout(() => this.$refs.promptInput.focus(), 0); setTimeout(() => this.$refs.promptInput.focus(), 0);
}, },
async onClickMessage(e) {
if (e.target.classList.contains("copy-btn")) {
const $btn = e.target;
const code = $btn.closest("pre").querySelector("code").innerText;
copyToClipboard(code);
const buttonText = $btn.textContent;
$btn.textContent = "Copied!";
setTimeout(() => ($btn.textContent = buttonText), 1200);
}
},
get visibleMessages() { get visibleMessages() {
return this.messages.filter((m) => m.role !== "system"); return this.messages.filter((m) => m.role !== "system");
}, },
@@ -455,10 +461,9 @@
const md = markdownit({ html: false }); const md = markdownit({ html: false });
function renderMarkdown(markdown) { function renderMarkdown(markdown) {
const $container = document.createElement("template"); const $placeholder = document.createElement("template");
$container.innerHTML = md.render(markdown); $placeholder.innerHTML = md.render(markdown);
this.attachCopyButtons($container); return $placeholder.innerHTML;
return $container.innerHTML;
} }
async function chatWithOpenAI({ baseUrl = "https://api.openai.com/v1", onContent, onError, messages, signal, apiKey, model, extraHeaders = {}, ...rest }) { async function chatWithOpenAI({ baseUrl = "https://api.openai.com/v1", onContent, onError, messages, signal, apiKey, model, extraHeaders = {}, ...rest }) {