Skip to content

Commit df6ac72

Browse files
committed
Improved UI for claude html viewer ui resource
1 parent 9591b8c commit df6ac72

1 file changed

Lines changed: 185 additions & 81 deletions

File tree

src/adeu/templates/viewer.html

Lines changed: 185 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -3,93 +3,207 @@
33
<html>
44

55
<head>
6+
<meta charset="utf-8">
67
<meta name="color-scheme" content="light dark">
8+
<!-- Import Adeu Brand Fonts -->
9+
<link rel="preconnect" href="https://fonts.googleapis.com">
10+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
11+
<link
12+
href="https://fonts.googleapis.com/css2?family=Jost:wght@400;500;600&family=Lora:ital,wght@0,600;1,600&display=swap"
13+
rel="stylesheet">
14+
715
<style>
8-
body {
9-
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
10-
padding: 20px;
11-
line-height: 1.6;
12-
color: #333;
13-
margin: 0;
14-
background: transparent;
16+
/* Adeu Design System Tokens */
17+
:root {
18+
/* Primary Colors */
19+
--brand-sand: #e8c0a1;
20+
--brand-sand-light: #f2d9c7;
21+
--brand-sand-dark: #d4a882;
22+
--brand-mint: #e6f3f0;
23+
--brand-mint-dark: #c9e4de;
24+
--brand-navy: #1a2a2c;
25+
--brand-navy-light: #2a3a3c;
26+
--brand-mauve: #dacad2;
27+
28+
/* Neutral Palette */
29+
--neutral-50: #fafafa;
30+
--neutral-100: #f5f5f5;
31+
--neutral-200: #e5e5e5;
32+
--neutral-300: #d9d9d9;
33+
--neutral-600: #555969;
34+
--neutral-800: #1e2028;
35+
--neutral-900: #171717;
36+
37+
/* Typography */
38+
--font-lora: 'Lora', Georgia, serif;
39+
--font-jost: 'Jost', system-ui, -apple-system, BlinkMacSystemFont, sans-serif;
40+
41+
/* Semantic overrides for Light Mode */
42+
--bg-body: var(--neutral-50);
43+
--bg-card: #ffffff;
44+
--border-card: var(--neutral-200);
45+
--text-main: var(--neutral-600);
46+
--text-heading: var(--brand-navy);
47+
--bg-blockquote: rgba(232, 192, 161, 0.15);
48+
/* Sand transparent */
1549
}
1650

51+
/* Dark Mode Semantic Overrides */
1752
@media (prefers-color-scheme: dark) {
18-
body {
19-
color: #eee;
53+
:root {
54+
--bg-body: var(--neutral-900);
55+
--bg-card: var(--brand-navy);
56+
--border-card: var(--brand-navy-light);
57+
--text-main: rgba(255, 255, 255, 0.85);
58+
--text-heading: #ffffff;
59+
--bg-blockquote: rgba(232, 192, 161, 0.1);
60+
--neutral-100: var(--brand-navy-light);
2061
}
2162
}
2263

64+
/* Base Setup */
65+
body {
66+
font-family: var(--font-jost);
67+
background-color: transparent;
68+
color: var(--text-main);
69+
padding: 16px 0;
70+
/* Removed horizontal padding since shadow is gone */
71+
margin: 0;
72+
line-height: 1.6;
73+
-webkit-font-smoothing: antialiased;
74+
}
75+
76+
/* Main Card Container (Separates UI from Claude) */
77+
.adeu-card {
78+
background-color: var(--bg-card);
79+
border: 1px solid var(--border-card);
80+
border-radius: 12px;
81+
/* rounded-xl */
82+
padding: 32px;
83+
/* space-8 */
84+
transition: all 0.2s ease;
85+
}
86+
87+
/* Typography inside Card */
88+
h1,
89+
h2,
90+
h3,
91+
h4 {
92+
font-family: var(--font-lora);
93+
color: var(--text-heading);
94+
font-weight: 600;
95+
letter-spacing: -0.02em;
96+
margin-top: 1.5em;
97+
margin-bottom: 0.5em;
98+
}
99+
23100
h1 {
24-
border-bottom: 1px solid #ddd;
25-
padding-bottom: 10px;
26-
margin-bottom: 20px;
101+
font-size: 2.25rem;
102+
border-bottom: 1px solid var(--border-card);
103+
padding-bottom: 0.3em;
104+
margin-top: 0;
27105
}
28106

29107
h2 {
30-
margin-top: 30px;
31-
color: #0056b3;
108+
font-size: 1.75rem;
32109
}
33110

34-
@media (prefers-color-scheme: dark) {
35-
h2 {
36-
color: #4da3ff;
37-
}
111+
h3 {
112+
font-size: 1.25rem;
38113
}
39114

40-
h3 {
41-
margin-top: 25px;
115+
a {
116+
color: var(--brand-sand-dark);
117+
text-decoration: none;
118+
border-bottom: 1px solid transparent;
119+
transition: border 0.2s ease;
42120
}
43121

44-
h4 {
45-
margin-top: 15px;
46-
margin-bottom: 5px;
122+
a:hover {
123+
border-bottom-color: var(--brand-sand-dark);
124+
}
125+
126+
strong {
127+
font-weight: 600;
128+
color: var(--text-heading);
129+
}
130+
131+
ul,
132+
ol {
133+
padding-left: 1.5rem;
134+
margin-bottom: 1rem;
47135
}
48136

137+
li {
138+
margin-bottom: 0.5rem;
139+
}
140+
141+
/* Blockquotes */
49142
blockquote {
50-
border-left: 4px solid #ccc;
51-
padding-left: 15px;
52-
margin: 15px 0;
53-
color: #555;
54-
background: rgba(0, 0, 0, 0.03);
55-
padding-top: 5px;
56-
padding-bottom: 5px;
143+
border-left: 4px solid var(--brand-sand);
144+
padding: 12px 20px;
145+
margin: 24px 0;
146+
color: var(--text-main);
147+
background: var(--bg-blockquote);
148+
border-radius: 0 8px 8px 0;
149+
font-style: italic;
57150
}
58151

59-
@media (prefers-color-scheme: dark) {
60-
blockquote {
61-
border-color: #666;
62-
color: #bbb;
63-
background: rgba(255, 255, 255, 0.05);
64-
}
152+
/* Inline Code & Code Blocks */
153+
code {
154+
font-family: ui-monospace, SFMono-Regular, Consolas, "Liberation Mono", Menlo, monospace;
155+
font-size: 0.85em;
156+
background: var(--neutral-100);
157+
color: var(--brand-sand-dark);
158+
padding: 0.2em 0.4em;
159+
border-radius: 4px;
65160
}
66161

67-
strong {
68-
font-weight: 600;
162+
pre code {
163+
display: block;
164+
padding: 16px;
165+
background: var(--neutral-900);
166+
color: #e5e5e5;
167+
border-radius: 8px;
168+
overflow-x: auto;
169+
}
170+
171+
/* Loading Skeleton Animation */
172+
.skeleton {
173+
background: linear-gradient(90deg, var(--neutral-100) 25%, var(--border-card) 50%, var(--neutral-100) 75%);
174+
background-size: 200% 100%;
175+
animation: skeleton 1.5s ease-in-out infinite;
176+
border-radius: 4px;
177+
}
178+
179+
@keyframes skeleton {
180+
0% {
181+
background-position: 200% 0;
182+
}
183+
184+
100% {
185+
background-position: -200% 0;
186+
}
69187
}
70188

71189
/* Polished Error Component Styles */
72190
.alert {
73191
padding: 16px;
74-
border-radius: 6px;
192+
border-radius: 8px;
75193
margin-bottom: 20px;
76194
display: flex;
77195
flex-direction: column;
78196
gap: 8px;
79-
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
80-
}
81-
82-
.alert-danger {
83-
background-color: #fef0f0;
84-
border: 1px solid #f5c2c7;
85-
color: #842029;
197+
background-color: #fef2f2;
198+
border: 1px solid #fecaca;
199+
color: #991b1b;
86200
}
87201

88202
@media (prefers-color-scheme: dark) {
89-
.alert-danger {
90-
background-color: #2c0b0e;
91-
border-color: #842029;
92-
color: #ea868f;
203+
.alert {
204+
background-color: rgba(127, 29, 29, 0.2);
205+
border-color: rgba(248, 113, 113, 0.3);
206+
color: #fca5a5;
93207
}
94208
}
95209

@@ -100,6 +214,7 @@
100214
display: flex;
101215
align-items: center;
102216
gap: 8px;
217+
font-family: var(--font-lora);
103218
}
104219

105220
.alert-body {
@@ -109,33 +224,32 @@
109224

110225
.alert-details {
111226
background: rgba(0, 0, 0, 0.05);
112-
padding: 10px;
113-
border-radius: 4px;
114-
font-family: ui-monospace, SFMono-Regular, Consolas, "Liberation Mono", Menlo, monospace;
227+
padding: 12px;
228+
border-radius: 6px;
229+
font-family: ui-monospace, Consolas, monospace;
115230
font-size: 0.85em;
116231
white-space: pre-wrap;
117232
word-break: break-all;
118233
margin-top: 8px;
119234
border: 1px solid rgba(0, 0, 0, 0.1);
120235
}
121-
122-
@media (prefers-color-scheme: dark) {
123-
.alert-details {
124-
background: rgba(255, 255, 255, 0.05);
125-
border-color: rgba(255, 255, 255, 0.1);
126-
}
127-
}
128236
</style>
129237
<script id="marked-script">[[marked_js_code | safe]]</script>
130238
</head>
131239

132240
<body>
133-
<div id="app">Loading report...</div>
241+
<div id="app" class="adeu-card">
242+
<!-- Brand-aligned Loading State -->
243+
<div class="skeleton" style="height: 36px; width: 40%; margin-bottom: 24px;"></div>
244+
<div class="skeleton" style="height: 16px; width: 100%; margin-bottom: 12px;"></div>
245+
<div class="skeleton" style="height: 16px; width: 95%; margin-bottom: 12px;"></div>
246+
<div class="skeleton" style="height: 16px; width: 90%; margin-bottom: 24px;"></div>
247+
<div class="skeleton" style="height: 16px; width: 80%; margin-bottom: 12px;"></div>
248+
</div>
134249
<script>
135250
const appDiv = document.getElementById('app');
136251
const INIT_ID = 1;
137252

138-
// Helper to safely render text to prevent HTML injection in error details
139253
function escapeHtml(unsafe) {
140254
return (unsafe || "").toString()
141255
.replace(/&/g, "&amp;")
@@ -145,10 +259,9 @@
145259
.replace(/'/g, "&#039;");
146260
}
147261

148-
// Standardized UI Error Component
149262
function showError(title, message, details = null) {
150263
let html = `
151-
<div class="alert alert-danger">
264+
<div class="alert">
152265
<div class="alert-title">
153266
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="8" x2="12" y2="12"></line><line x1="12" y1="16" x2="12.01" y2="16"></line></svg>
154267
${escapeHtml(title)}
@@ -164,10 +277,12 @@
164277
}
165278

166279
function triggerResize() {
280+
// Removed height buffer since the drop shadow is gone
281+
const height = Math.min(Math.ceil(document.documentElement.getBoundingClientRect().height), 600);
167282
window.parent.postMessage({
168283
jsonrpc: "2.0",
169284
method: "ui/notifications/size-changed",
170-
params: { height: Math.min(Math.ceil(document.documentElement.getBoundingClientRect().height), 600), width: 600 }
285+
params: { height: height, width: 600 }
171286
}, "*");
172287
}
173288

@@ -176,7 +291,6 @@
176291
if (!event.data || event.data.jsonrpc !== "2.0") return;
177292
const msg = event.data;
178293

179-
// 1. Host Initialization Handshake
180294
if (msg.id === INIT_ID) {
181295
window.parent.postMessage({
182296
jsonrpc: "2.0",
@@ -186,47 +300,37 @@
186300
return;
187301
}
188302

189-
// 2. Incoming Data Render
190303
if (msg.method === "ui/notifications/tool-result") {
191-
192-
// Check for critical missing dependencies first
193304
if (window.__MARKED_ERROR || typeof marked === 'undefined') {
194-
showError(
195-
"Missing Dependency",
196-
"The Markdown renderer (marked.min.js) failed to load. The report cannot be displayed.",
197-
window.__MARKED_ERROR || "The 'marked' global variable is undefined."
198-
);
305+
showError("Missing Dependency", "The Markdown renderer failed to load.", window.__MARKED_ERROR || "marked is undefined.");
199306
return;
200307
}
201308

202309
const result = msg.params;
203-
204310
if (!result) {
205-
showError("Empty Response", "The server returned an empty tool result payload.");
311+
showError("Empty Response", "The server returned an empty payload.");
206312
return;
207313
}
208314

209315
if (result.structuredContent && result.structuredContent.markdown) {
210-
// Render successfully
211316
appDiv.innerHTML = marked.parse(result.structuredContent.markdown);
212317
triggerResize();
213318
} else if (result.content) {
214-
// Fallback content rendering
215319
if (Array.isArray(result.content)) {
216320
const txt = result.content.find(c => c.type === 'text');
217321
if (txt) { appDiv.textContent = txt.text; triggerResize(); }
218322
} else if (typeof result.content === 'string') {
219323
appDiv.textContent = result.content;
220324
triggerResize();
221325
} else {
222-
showError("Unsupported Format", "Received an unrecognizable content format.", JSON.stringify(result.content));
326+
showError("Unsupported Format", "Unrecognizable content format.", JSON.stringify(result.content));
223327
}
224328
} else {
225-
showError("No Content", "The tool result payload contained no structured content or raw text.", JSON.stringify(result));
329+
showError("No Content", "No structured content or raw text found.", JSON.stringify(result));
226330
}
227331
}
228332
} catch (error) {
229-
showError("Rendering Engine Error", "An unexpected error occurred while parsing the document.", error.toString() + "\n" + error.stack);
333+
showError("Rendering Engine Error", "An unexpected error occurred.", error.toString() + "\n" + error.stack);
230334
}
231335
});
232336

0 commit comments

Comments
 (0)