1076 lines
35 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Chat Interface Mockup</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=Playfair+Display:wght@400;500;600&family=Space+Grotesk:wght@400;500;600&display=swap');
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
/* Twilight-inspired palette */
--ctp-base: #1a1236;
--ctp-mantle: #211641;
--ctp-crust: #120a28;
--ctp-surface0: #2a1f58;
--ctp-surface1: #3a2a70;
--ctp-surface2: #4a3c8f;
--ctp-overlay0: #766ca3;
--ctp-overlay1: #a197d2;
--ctp-overlay2: #c4bbf3;
--ctp-text: #f5f1ff;
--ctp-subtext1: #dad2ff;
--ctp-subtext0: #b6ace5;
--ctp-lavender: #c8b6ff;
--ctp-blue: #adb0ff;
--ctp-sapphire: #8fb7ff;
--ctp-sky: #9fe7ff;
--ctp-teal: #7fe8d4;
--ctp-green: #b5f5a1;
--ctp-yellow: #f5e5b4;
--ctp-peach: #f2b9a3;
--ctp-maroon: #f09bb0;
--ctp-red: #ff86a6;
--ctp-mauve: #d4b0ff;
--ctp-pink: #ffb8e1;
--ctp-flamingo: #ffc7d9;
--ctp-rosewater: #fbd8ec;
--font-heading: 'Playfair Display', 'Times New Roman', serif;
--font-body: 'Space Grotesk', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
--font-accent: 'Microtype', 'Space Grotesk', sans-serif;
}
body {
font-family: var(--font-body);
background: var(--ctp-base);
color: var(--ctp-text);
/* Keep the entire app within the viewport */
--page-pad: clamp(12px, 2vw, 20px);
height: 100vh;
padding: var(--page-pad);
overflow: hidden;
}
.main-content {
width: min(1200px, 100%);
margin: 0 auto;
display: flex;
flex-direction: column;
background: transparent;
border-radius: 20px;
border: 1px solid rgba(255, 255, 255, 0.04);
backdrop-filter: blur(12px);
box-shadow: 0 20px 80px rgba(7, 4, 23, 0.45);
overflow: clip;
padding: clamp(16px, 4vw, 32px);
/* Fill available viewport minus page padding */
height: calc(100vh - var(--page-pad) * 2);
}
.chat-container {
flex: 1;
padding: clamp(16px, 3vw, 32px);
display: flex;
flex-direction: column;
gap: 8px;
overflow-y: auto;
}
.chat-stream {
width: min(960px, 100%);
margin: 0 auto;
display: flex;
flex-direction: column;
gap: 12px;
}
.page-actions {
width: min(960px, 100%);
margin: 0 auto 16px;
display: flex;
justify-content: flex-end;
gap: 12px;
align-items: center;
}
.page-icons {
display: flex;
gap: 6px;
}
.page-icons button {
width: 30px;
height: 30px;
border-radius: 8px;
border: 1px solid rgba(255, 255, 255, 0.16);
background: rgba(255, 255, 255, 0.04);
color: var(--ctp-text);
font-size: 14px;
cursor: pointer;
}
.empty-state {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 24px;
}
.empty-state h1 {
font-size: 48px;
font-weight: 500;
font-family: var(--font-heading);
color: var(--ctp-text);
}
.empty-state p {
font-size: 16px;
color: var(--ctp-subtext0);
}
.message {
margin-bottom: 10px;
display: flex;
flex-direction: column;
gap: 4px;
animation: slideIn 0.25s ease;
}
.message.user .message-content,
.message.assistant .message-content {
border-radius: 18px;
padding: 18px 24px;
border: 1px solid rgba(255, 255, 255, 0.06);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.04);
background: rgba(255, 255, 255, 0.02);
}
.message.user .message-content {
background: rgba(181, 110, 255, 0.08);
border-color: rgba(200, 150, 255, 0.18);
}
.message.assistant {
padding: 8px 0;
}
.assistant-footer {
display: flex;
align-items: center;
justify-content: space-between;
margin-top: 12px;
padding-top: 10px;
border-top: 1px solid var(--ctp-surface0);
}
.footer-left {
display: flex;
align-items: center;
gap: 8px;
}
.app-icon {
width: 24px;
height: 24px;
background: linear-gradient(135deg, #e89a7d 0%, #d87d5f 100%);
border-radius: 6px;
display: flex;
align-items: center;
justify-content: center;
font-size: 14px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.35);
}
.disclaimer-text {
font-size: 12px;
color: var(--ctp-overlay2);
font-family: var(--font-accent);
letter-spacing: 0.04em;
text-transform: uppercase;
}
.footer-right {
display: flex;
align-items: center;
gap: 8px;
}
.footer-btn {
width: 28px;
height: 28px;
background: transparent;
border: none;
color: var(--ctp-subtext1);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
border-radius: 6px;
transition: all 0.2s ease;
font-size: 14px;
}
.footer-btn:hover {
background: var(--ctp-surface0);
color: var(--ctp-text);
}
.footer-btn:focus-visible,
.retry-btn:focus-visible,
.input-btn:focus-visible,
.model-selector:focus-visible,
.copy-btn:focus-visible {
outline: 2px solid var(--ctp-blue);
outline-offset: 2px;
}
.retry-btn {
padding: 6px 10px;
background: transparent;
border: none;
color: var(--ctp-subtext1);
cursor: pointer;
display: flex;
align-items: center;
gap: 4px;
border-radius: 6px;
transition: all 0.2s ease;
font-size: 12px;
}
.retry-btn:hover {
background: var(--ctp-surface0);
color: var(--ctp-text);
}
.ghost-pill {
border-radius: 999px;
border: 1px solid rgba(255, 255, 255, 0.16);
padding: 6px 14px;
background: rgba(255, 255, 255, 0.05);
color: var(--ctp-text);
font-size: 12px;
font-family: var(--font-accent);
letter-spacing: 0.08em;
text-transform: uppercase;
display: inline-flex;
align-items: center;
gap: 6px;
cursor: pointer;
}
.ghost-pill--mini {
padding: 4px 10px;
border-radius: 12px;
font-size: 11px;
}
@keyframes slideIn {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.message-content {}
.message-header {
display: flex;
align-items: center;
gap: 10px;
margin-bottom: 6px;
}
.message-meta {
display: flex;
flex-direction: column;
gap: 2px;
}
.message-pin {
width: 20px;
height: 28px;
border-radius: 8px;
background: rgba(255, 255, 255, 0.08);
border: 1px solid rgba(255, 255, 255, 0.08);
display: inline-flex;
align-items: center;
justify-content: center;
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1);
}
.message-pin::before {
content: '';
width: 4px;
height: 12px;
border-radius: 999px;
background: rgba(255, 255, 255, 0.4);
}
.message.assistant .message-header {
margin-bottom: 12px;
}
.message.user .message-author {
color: var(--ctp-blue);
}
.message-author {
font-size: 12px;
font-weight: 600;
color: var(--ctp-text);
font-family: var(--font-heading);
text-transform: uppercase;
letter-spacing: 0.12em;
}
.message-time {
font-size: 11px;
color: var(--ctp-overlay0);
font-family: var(--font-accent);
text-transform: uppercase;
letter-spacing: 0.08em;
}
.message-text {
font-size: 15px;
line-height: 1.65;
color: var(--ctp-text);
}
.message-text h3 {
font-size: 18px;
font-weight: 600;
margin-top: 16px;
margin-bottom: 8px;
color: var(--ctp-text);
font-family: var(--font-heading);
}
.message-text p {
margin-bottom: 12px;
}
.message-text code {
background: rgba(255, 255, 255, 0.08);
padding: 2px 6px;
border-radius: 4px;
font-family: 'Courier New', monospace;
font-size: 13px;
color: var(--ctp-pink);
}
.message-text pre {
background: rgba(255, 255, 255, 0.02);
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 16px;
padding: 20px;
overflow-x: auto;
margin: 12px 0;
}
.message-text pre code {
background: transparent;
padding: 0;
color: var(--ctp-text);
}
.message-text ul,
.message-text ol {
margin-left: 20px;
margin-bottom: 12px;
}
.message-text li {
margin-bottom: 6px;
}
.thought-bubble {
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.16);
padding: 6px 12px;
border-radius: 999px;
margin-bottom: 12px;
font-size: 11px;
color: var(--ctp-overlay2);
display: inline-flex;
align-items: center;
gap: 6px;
font-family: var(--font-accent);
}
.thought-bubble::before {
content: '⌄';
font-size: 12px;
opacity: 0.7;
}
.tool-call {
background: rgba(255, 255, 255, 0.02);
border: 1px dashed rgba(255, 255, 255, 0.12);
border-radius: 12px;
padding: 12px 16px;
margin-bottom: 12px;
font-size: 13px;
}
.tool-call-header {
display: flex;
align-items: center;
gap: 8px;
color: var(--ctp-blue);
font-weight: 600;
margin-bottom: 8px;
}
.tool-call-content {
color: var(--ctp-subtext1);
font-family: 'Courier New', monospace;
font-size: 12px;
}
.file-attachment {
background: rgba(255, 255, 255, 0.02);
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 14px;
padding: 12px 16px;
margin-bottom: 12px;
display: flex;
align-items: center;
gap: 12px;
transition: all 0.2s ease;
cursor: pointer;
}
.file-attachment:hover {
background: rgba(255, 255, 255, 0.08);
}
.file-icon {
width: 40px;
height: 40px;
background: linear-gradient(135deg, #fdb5ff 0%, #aab8ff 100%);
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
font-size: 20px;
flex-shrink: 0;
}
.file-info {
flex: 1;
}
.file-name {
font-size: 14px;
font-weight: 600;
color: var(--ctp-text);
margin-bottom: 2px;
}
.file-meta {
font-size: 12px;
color: var(--ctp-overlay0);
font-family: var(--font-accent);
letter-spacing: 0.08em;
text-transform: uppercase;
}
.message-actions {
display: flex;
gap: 8px;
margin-top: 10px;
}
.message-actions--below {
align-self: flex-end;
margin-top: 8px;
}
.copy-btn {
padding: 6px 10px;
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.16);
border-radius: 8px;
color: var(--ctp-subtext1);
font-size: 12px;
cursor: pointer;
display: flex;
align-items: center;
gap: 6px;
transition: all 0.2s ease;
}
.copy-btn:hover {
background: rgba(255, 255, 255, 0.12);
color: var(--ctp-text);
}
/* Minimal square icon button for message actions */
.copy-btn.icon-btn {
width: 30px;
height: 30px;
padding: 0;
display: inline-flex;
align-items: center;
justify-content: center;
border-radius: 10px;
}
.copy-btn.icon-btn:hover {
background: rgba(255, 255, 255, 0.18);
color: var(--ctp-text);
}
.copy-btn::before {
content: '📋';
font-size: 14px;
}
.copy-btn.copied::before {
content: '✓';
}
.copy-btn.copied {
color: var(--ctp-green);
border-color: var(--ctp-green);
}
.steps-indicator {
display: inline-flex;
align-items: center;
gap: 6px;
color: var(--ctp-overlay0);
font-size: 12px;
margin-bottom: 12px;
font-family: var(--font-accent);
letter-spacing: 0.08em;
text-transform: uppercase;
}
.steps-indicator::before {
content: '↑';
transform: rotate(45deg);
}
.input-container {
padding: clamp(12px, 2.5vw, 24px) clamp(12px, 4vw, 40px);
background: transparent;
flex-shrink: 0;
}
.input-wrapper {
width: min(900px, 100%);
margin: 0 auto;
position: relative;
background: rgba(255, 255, 255, 0.02);
border: 1px solid rgba(255, 255, 255, 0.12);
border-radius: 22px;
padding: 14px 20px;
display: flex;
align-items: center;
gap: 12px;
box-shadow: 0 10px 40px rgba(7, 4, 23, 0.45);
}
.input-left-actions {
display: flex;
gap: 8px;
flex-shrink: 0;
}
.chat-input {
flex: 1;
background: transparent;
border: none;
color: var(--ctp-text);
font-size: 14px;
font-family: inherit;
resize: none;
outline: none;
min-height: 24px;
max-height: 200px;
}
.chat-input::placeholder {
color: rgba(255, 255, 255, 0.4);
}
.input-right-actions {
display: flex;
align-items: center;
gap: 8px;
flex-shrink: 0;
}
.model-selector {
padding: 6px 12px;
background: rgba(255, 255, 255, 0.08);
border: 1px solid rgba(255, 255, 255, 0.2);
color: var(--ctp-text);
font-size: 12px;
cursor: pointer;
display: flex;
align-items: center;
gap: 6px;
transition: all 0.2s ease;
border-radius: 999px;
font-family: var(--font-accent);
letter-spacing: 0.08em;
text-transform: uppercase;
}
.model-selector:hover {
background: rgba(255, 255, 255, 0.12);
}
.input-btn {
width: 32px;
height: 32px;
border-radius: 6px;
border: none;
background: rgba(255, 255, 255, 0.06);
color: var(--ctp-overlay2);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.2s ease;
font-size: 16px;
flex-shrink: 0;
}
.input-btn:hover {
background: rgba(255, 255, 255, 0.12);
}
.send-btn {
background: linear-gradient(120deg, #fdb5ff 0%, #9bb8ff 100%);
color: var(--ctp-base);
border: 0;
}
.send-btn:hover {
filter: brightness(1.05);
}
.token-count {
display: inline-flex;
align-items: center;
gap: 4px;
font-family: var(--font-accent);
font-size: 10px;
letter-spacing: 0.12em;
text-transform: uppercase;
padding: 4px 10px;
border-radius: 999px;
border: 1px solid rgba(255, 255, 255, 0.12);
color: var(--ctp-overlay2);
}
.session-pill {
width: min(900px, 100%);
margin: 12px auto 0;
display: flex;
align-items: center;
gap: 10px;
color: var(--ctp-overlay1);
font-size: 12px;
font-family: var(--font-accent);
letter-spacing: 0.08em;
text-transform: uppercase;
}
.status-dot {
width: 8px;
height: 8px;
border-radius: 50%;
background: #6df0a6;
box-shadow: 0 0 8px rgba(109, 240, 166, 0.8);
}
.session-icon {
width: 26px;
height: 26px;
border-radius: 8px;
background: rgba(255, 255, 255, 0.08);
display: inline-flex;
align-items: center;
justify-content: center;
border: 1px solid rgba(255, 255, 255, 0.1);
}
::-webkit-scrollbar {
width: 8px;
}
::-webkit-scrollbar-track {
background: transparent;
}
::-webkit-scrollbar-thumb {
background: var(--ctp-surface1);
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: var(--ctp-surface2);
}
/* Toast */
.toast {
position: fixed;
left: 50%;
bottom: 20px;
transform: translateX(-50%) translateY(20px);
background: var(--ctp-surface1);
color: var(--ctp-text);
border: 1px solid var(--ctp-surface2);
border-radius: 8px;
padding: 10px 14px;
font-size: 13px;
opacity: 0;
pointer-events: none;
transition: opacity .2s ease, transform .2s ease;
box-shadow: 0 6px 18px rgba(0,0,0,.25);
}
.toast.show {
opacity: 1;
transform: translateX(-50%) translateY(0);
}
@media (prefers-reduced-motion: reduce) {
* {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}
/* Use dynamic viewport units where supported (mobile address bar) */
@supports (height: 100dvh) {
body { height: 100dvh; }
.main-content { height: calc(100dvh - var(--page-pad) * 2); }
}
</style>
</head>
<body>
<div class="main-content">
<div class="chat-container">
<div class="page-actions">
<button class="ghost-pill">Hello!</button>
<div class="page-icons">
<button aria-label="Edit card">✏️</button>
<button aria-label="Delete card">🗑</button>
</div>
</div>
<div class="chat-stream">
<div class="message user">
<div class="message-content">
<div class="message-header">
<span class="message-pin" aria-hidden="true"></span>
<div class="message-meta">
<div class="message-author">Nicholai</div>
<div class="message-time">Nov 13, 2025 · 1:47 PM</div>
</div>
</div>
<div class="message-text">
could you build a mockup llm chat interface please? just in a single html artifact is fine. I'm
trying to restyle an app and find it's easier to do little mockups in html.
</div>
</div>
<div class="message-actions message-actions--below" aria-label="Message actions">
<button class="copy-btn icon-btn" aria-label="Copy message" title="Copy message" onclick="copyMessage(this)"></button>
</div>
</div>
<div class="message assistant">
<div class="message-content">
<div class="message-header">
<span class="message-pin" aria-hidden="true"></span>
<div class="message-meta">
<div class="message-author">Jan</div>
<div class="message-time">Nov 13, 2025 · 1:47 PM</div>
</div>
<button class="ghost-pill ghost-pill--mini" type="button">Thought</button>
</div>
<div class="thought-bubble">
analyzing reference screenshots and combining design elements...
</div>
<div class="message-text">
<p>I'll create a sleek chat interface mockup inspired by your screenshots. Here's what I'll
include:</p>
<ul>
<li>Clean Catppuccin color scheme</li>
<li>Floating centered window</li>
<li>Minimal design with focus on content</li>
</ul>
</div>
<div class="assistant-footer">
<div class="footer-left">
<div class="app-icon"></div>
<span class="disclaimer-text">Claude can make mistakes. Please double-check
responses.</span>
</div>
<div class="footer-right">
<button class="footer-btn" title="Copy conversation" aria-label="Copy conversation" data-action="copy-conversation">📋</button>
<button class="footer-btn" title="Thumbs up" aria-label="Thumbs up" data-action="thumbs-up">👍</button>
<button class="footer-btn" title="Thumbs down" aria-label="Thumbs down" data-action="thumbs-down">👎</button>
<button class="retry-btn">
Retry
<span></span>
</button>
</div>
</div>
</div>
</div>
<div class="message user">
<div class="message-content">
<div class="message-header">
<span class="message-pin" aria-hidden="true"></span>
<div class="message-meta">
<div class="message-author">Nicholai</div>
<div class="message-time">Nov 13, 2025 · 1:49 PM</div>
</div>
</div>
<div class="message-text">
Can you show me some examples with code blocks and tool usage?
</div>
</div>
<div class="message-actions message-actions--below" aria-label="Message actions">
<button class="copy-btn icon-btn" aria-label="Copy message" title="Copy message" onclick="copyMessage(this)"></button>
</div>
</div>
<div class="message assistant">
<div class="message-content">
<div class="message-header">
<span class="message-pin" aria-hidden="true"></span>
<div class="message-meta">
<div class="message-author">Jan</div>
<div class="message-time">Nov 13, 2025 · 1:49 PM</div>
</div>
<button class="ghost-pill ghost-pill--mini" type="button">Thought</button>
</div>
<div class="steps-indicator">3 steps</div>
<div class="thought-bubble">
I'll demonstrate various UI elements including markdown formatting, code blocks, and tool usage
examples...
</div>
<div class="tool-call">
<div class="tool-call-header">
🔧 web_search
</div>
<div class="tool-call-content">query: "catppuccin color palette documentation"</div>
</div>
<div class="tool-call">
<div class="tool-call-header">
🔧 create_file
</div>
<div class="tool-call-content">path: /mnt/user-data/outputs/chat-interface-mockup.html</div>
</div>
<div class="message-text">
<p>Here's a comprehensive example showing different UI elements:</p>
<h3>Code Example</h3>
<p>Here's how to set up the Catppuccin colors in CSS:</p>
<pre><code>:root {
--ctp-base: #1e1e2e;
--ctp-mantle: #181825;
--ctp-text: #cdd6f4;
--ctp-mauve: #cba6f7;
}</code></pre>
<p>You can use inline code like <code>background: var(--ctp-base)</code> for referencing colors.
</p>
<h3>Key Features</h3>
<ul>
<li><strong>Thinking blocks</strong> - Shows the model's reasoning process</li>
<li><strong>Tool calls</strong> - Displays function invocations</li>
<li><strong>File attachments</strong> - Clean file preview cards</li>
</ul>
</div>
<div class="file-attachment">
<div class="file-icon">📄</div>
<div class="file-info">
<div class="file-name">chat-interface-mockup.html</div>
<div class="file-meta">HTML • 12 KB</div>
</div>
</div>
<div class="assistant-footer">
<div class="footer-left">
<div class="app-icon"></div>
<span class="disclaimer-text">Claude can make mistakes. Please double-check
responses.</span>
</div>
<div class="footer-right">
<button class="footer-btn" title="Copy conversation" aria-label="Copy conversation" data-action="copy-conversation">📋</button>
<button class="footer-btn" title="Thumbs up" aria-label="Thumbs up" data-action="thumbs-up">👍</button>
<button class="footer-btn" title="Thumbs down" aria-label="Thumbs down" data-action="thumbs-down">👎</button>
<button class="retry-btn">
Retry
<span></span>
</button>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="input-container">
<div class="input-wrapper">
<div class="input-left-actions">
<button class="input-btn" aria-label="Add attachment">+</button>
<button class="input-btn" aria-label="Insert template"></button>
<button class="input-btn" aria-label="Clear input"></button>
</div>
<textarea class="chat-input" placeholder="How can I help you today?" rows="1"></textarea>
<div class="input-right-actions">
<button class="model-selector" aria-haspopup="listbox" aria-expanded="false" aria-label="Change model" data-action="model-select">
Sonnet 4.5
<span></span>
</button>
<div class="token-count">
<span>0.1%</span>
<span></span>
</div>
<button class="input-btn send-btn" aria-label="Send message"></button>
</div>
</div>
<div class="session-pill">
<span class="session-icon"></span>
Jan v1.2509-04_K_M
<span class="status-dot" aria-label="Online"></span>
Ready
</div>
</div>
</div>
<div id="toast" class="toast" role="status" aria-live="polite" aria-atomic="true"></div>
<script>
// Auto-resize textarea
const textarea = document.querySelector('.chat-input');
textarea.addEventListener('input', function () {
this.style.height = 'auto';
this.style.height = Math.min(this.scrollHeight, 200) + 'px';
});
// Send message on Ctrl/Cmd + Enter
textarea.addEventListener('keydown', function (e) {
if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') {
console.log('Send message:', this.value);
this.value = '';
this.style.height = 'auto';
}
});
// Send button click
document.querySelector('.send-btn').addEventListener('click', function () {
console.log('Send message:', textarea.value);
textarea.value = '';
textarea.style.height = 'auto';
});
// Toast helper
const toastEl = document.getElementById('toast');
let toastTimer;
function showToast(message) {
toastEl.textContent = message;
toastEl.classList.add('show');
clearTimeout(toastTimer);
toastTimer = setTimeout(() => toastEl.classList.remove('show'), 1800);
}
// Copy message function with fallback + feedback
async function copyMessage(button) {
const container = button.closest('.message');
const messageContent = container ? container.querySelector('.message-content') : button.closest('.message-content');
const messageText = messageContent.querySelector('.message-text');
let textToCopy = '';
if (messageText) {
textToCopy = messageText.innerText;
} else {
textToCopy = messageContent.innerText.split('\n').slice(1).join('\n').trim();
}
const setState = (ok, label) => {
if (ok) button.classList.add('copied');
const isIcon = button.classList.contains('icon-btn');
if (!isIcon) {
button.textContent = label;
} else {
button.setAttribute('aria-label', label);
button.setAttribute('title', label);
}
setTimeout(() => {
button.classList.remove('copied');
if (!isIcon) button.textContent = 'Copy';
button.setAttribute('aria-label', 'Copy message');
button.setAttribute('title', 'Copy message');
}, 1600);
};
try {
if (navigator.clipboard && window.isSecureContext) {
await navigator.clipboard.writeText(textToCopy);
} else {
// Fallback using a temporary textarea
const t = document.createElement('textarea');
t.value = textToCopy;
t.style.position = 'fixed';
t.style.left = '-9999px';
document.body.appendChild(t);
t.select();
document.execCommand('copy');
document.body.removeChild(t);
}
setState(true, 'Copied!');
showToast('Message copied to clipboard');
} catch (err) {
setState(false, 'Failed');
showToast('Clipboard blocked — select text to copy');
}
}
// Stub simple mock interactions for demo buttons
document.addEventListener('click', (e) => {
const btn = e.target.closest('[data-action]');
if (!btn) return;
const action = btn.getAttribute('data-action');
if (action === 'copy-conversation') showToast('Conversation copied (mock)');
if (action === 'thumbs-up') showToast('Feedback recorded (mock)');
if (action === 'thumbs-down') showToast('Feedback recorded (mock)');
if (action === 'model-select') {
const expanded = btn.getAttribute('aria-expanded') === 'true';
btn.setAttribute('aria-expanded', String(!expanded));
showToast('Switched model (mock)');
}
});
</script>
</body>
</html>