1076 lines
35 KiB
HTML
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>
|