hansakafernando commited on
Commit
29ea1ed
·
verified ·
1 Parent(s): 9382292

Create index.html

Browse files
Files changed (1) hide show
  1. index.html +598 -0
index.html ADDED
@@ -0,0 +1,598 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="si">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Olya.ai ✨</title>
7
+ <link href="https://fonts.googleapis.com/css2?family=Google+Sans:wght@300;400;500;600&family=Google+Sans+Display:wght@300;400;500&family=Noto+Sans+Sinhala:wght@300;400;500;600&display=swap" rel="stylesheet" />
8
+ <script src="https://cdn.tailwindcss.com"></script>
9
+ <style>
10
+ :root {
11
+ --grad-start: #4f86f7;
12
+ --grad-end: #9b59f7;
13
+ --surface: #ffffff;
14
+ --bg: #f8f9fc;
15
+ --bubble: #f0f2f7;
16
+ --border: #e5e8f0;
17
+ --text-primary: #1a1d2e;
18
+ --text-secondary: #6b7280;
19
+ --text-muted: #9ca3af;
20
+ --input-bg: #ffffff;
21
+ --shadow-soft: 0 4px 24px rgba(79, 134, 247, 0.08), 0 1px 6px rgba(0,0,0,0.05);
22
+ --shadow-input: 0 8px 40px rgba(79, 134, 247, 0.12), 0 2px 12px rgba(0,0,0,0.06);
23
+ }
24
+
25
+ * { box-sizing: border-box; margin: 0; padding: 0; }
26
+
27
+ body {
28
+ font-family: 'Google Sans', 'Noto Sans Sinhala', sans-serif;
29
+ background: var(--bg);
30
+ color: var(--text-primary);
31
+ min-height: 100vh;
32
+ display: flex;
33
+ flex-direction: column;
34
+ overflow: hidden;
35
+ }
36
+
37
+ /* Subtle background mesh */
38
+ body::before {
39
+ content: '';
40
+ position: fixed;
41
+ inset: 0;
42
+ background:
43
+ radial-gradient(ellipse 60% 40% at 20% 10%, rgba(79,134,247,0.06) 0%, transparent 60%),
44
+ radial-gradient(ellipse 50% 50% at 80% 90%, rgba(155,89,247,0.05) 0%, transparent 60%);
45
+ pointer-events: none;
46
+ z-index: 0;
47
+ }
48
+
49
+ .gradient-text {
50
+ background: linear-gradient(135deg, var(--grad-start) 0%, var(--grad-end) 100%);
51
+ -webkit-background-clip: text;
52
+ -webkit-text-fill-color: transparent;
53
+ background-clip: text;
54
+ }
55
+
56
+ /* ── Header ── */
57
+ header {
58
+ position: fixed;
59
+ top: 0;
60
+ left: 0;
61
+ right: 0;
62
+ z-index: 100;
63
+ background: rgba(248, 249, 252, 0.85);
64
+ backdrop-filter: blur(20px);
65
+ -webkit-backdrop-filter: blur(20px);
66
+ border-bottom: 1px solid var(--border);
67
+ }
68
+
69
+ .header-inner {
70
+ max-width: 780px;
71
+ margin: 0 auto;
72
+ padding: 14px 24px;
73
+ display: flex;
74
+ align-items: center;
75
+ justify-content: space-between;
76
+ }
77
+
78
+ .logo-block { display: flex; flex-direction: column; gap: 2px; }
79
+
80
+ .logo-title {
81
+ font-family: 'Google Sans Display', 'Google Sans', sans-serif;
82
+ font-size: 1.35rem;
83
+ font-weight: 500;
84
+ letter-spacing: -0.02em;
85
+ line-height: 1;
86
+ }
87
+
88
+ .logo-caption {
89
+ font-size: 0.68rem;
90
+ color: var(--text-muted);
91
+ font-weight: 400;
92
+ letter-spacing: 0.01em;
93
+ }
94
+
95
+ .header-badge {
96
+ font-size: 0.7rem;
97
+ font-weight: 500;
98
+ color: var(--text-muted);
99
+ background: var(--bubble);
100
+ border: 1px solid var(--border);
101
+ padding: 4px 10px;
102
+ border-radius: 99px;
103
+ letter-spacing: 0.02em;
104
+ }
105
+
106
+ /* ── Chat scroll area ── */
107
+ #chat-scroll {
108
+ flex: 1;
109
+ overflow-y: auto;
110
+ padding: 90px 24px 140px;
111
+ scroll-behavior: smooth;
112
+ position: relative;
113
+ z-index: 1;
114
+ }
115
+
116
+ #chat-scroll::-webkit-scrollbar { width: 4px; }
117
+ #chat-scroll::-webkit-scrollbar-track { background: transparent; }
118
+ #chat-scroll::-webkit-scrollbar-thumb { background: var(--border); border-radius: 99px; }
119
+
120
+ #messages {
121
+ max-width: 780px;
122
+ margin: 0 auto;
123
+ display: flex;
124
+ flex-direction: column;
125
+ gap: 28px;
126
+ }
127
+
128
+ /* ── Welcome screen ── */
129
+ #welcome {
130
+ display: flex;
131
+ flex-direction: column;
132
+ align-items: center;
133
+ justify-content: center;
134
+ text-align: center;
135
+ padding: 80px 24px 40px;
136
+ gap: 12px;
137
+ animation: fadeUp 0.6s ease both;
138
+ }
139
+
140
+ .welcome-orb {
141
+ width: 64px; height: 64px;
142
+ border-radius: 50%;
143
+ background: linear-gradient(135deg, #4f86f7 0%, #9b59f7 100%);
144
+ display: flex; align-items: center; justify-content: center;
145
+ font-size: 28px;
146
+ box-shadow: 0 8px 32px rgba(79,134,247,0.25);
147
+ margin-bottom: 8px;
148
+ animation: float 4s ease-in-out infinite;
149
+ }
150
+
151
+ @keyframes float {
152
+ 0%, 100% { transform: translateY(0); }
153
+ 50% { transform: translateY(-6px); }
154
+ }
155
+
156
+ .welcome-title {
157
+ font-family: 'Google Sans Display', sans-serif;
158
+ font-size: 2rem;
159
+ font-weight: 400;
160
+ letter-spacing: -0.03em;
161
+ line-height: 1.2;
162
+ }
163
+
164
+ .welcome-sub {
165
+ font-size: 0.92rem;
166
+ color: var(--text-secondary);
167
+ max-width: 360px;
168
+ line-height: 1.6;
169
+ }
170
+
171
+ /* Sinhala sub text */
172
+ .welcome-si {
173
+ font-family: 'Noto Sans Sinhala', sans-serif;
174
+ font-size: 0.88rem;
175
+ color: var(--text-muted);
176
+ margin-top: 4px;
177
+ }
178
+
179
+ /* Suggestion chips */
180
+ .chips {
181
+ display: flex;
182
+ flex-wrap: wrap;
183
+ gap: 8px;
184
+ justify-content: center;
185
+ margin-top: 20px;
186
+ }
187
+
188
+ .chip {
189
+ font-size: 0.82rem;
190
+ font-weight: 400;
191
+ color: var(--text-secondary);
192
+ background: var(--input-bg);
193
+ border: 1px solid var(--border);
194
+ border-radius: 99px;
195
+ padding: 8px 16px;
196
+ cursor: pointer;
197
+ transition: all 0.2s ease;
198
+ white-space: nowrap;
199
+ box-shadow: var(--shadow-soft);
200
+ }
201
+
202
+ .chip:hover {
203
+ border-color: #4f86f7;
204
+ color: #4f86f7;
205
+ transform: translateY(-1px);
206
+ box-shadow: 0 4px 16px rgba(79,134,247,0.15);
207
+ }
208
+
209
+ .chip.si { font-family: 'Noto Sans Sinhala', sans-serif; font-size: 0.78rem; }
210
+
211
+ /* ── Messages ── */
212
+ .msg-row { display: flex; flex-direction: column; gap: 4px; animation: fadeUp 0.35s ease both; }
213
+
214
+ @keyframes fadeUp {
215
+ from { opacity: 0; transform: translateY(12px); }
216
+ to { opacity: 1; transform: translateY(0); }
217
+ }
218
+
219
+ /* User message */
220
+ .msg-user {
221
+ align-self: flex-end;
222
+ max-width: 68%;
223
+ }
224
+
225
+ .msg-user .bubble {
226
+ background: var(--bubble);
227
+ border-radius: 20px 20px 4px 20px;
228
+ padding: 12px 18px;
229
+ font-size: 0.93rem;
230
+ line-height: 1.6;
231
+ color: var(--text-primary);
232
+ font-weight: 400;
233
+ }
234
+
235
+ .msg-user .bubble.si { font-family: 'Noto Sans Sinhala', sans-serif; font-size: 0.88rem; }
236
+
237
+ .msg-user .meta {
238
+ text-align: right;
239
+ font-size: 0.68rem;
240
+ color: var(--text-muted);
241
+ padding-right: 6px;
242
+ margin-top: 4px;
243
+ }
244
+
245
+ /* Olya (AI) message */
246
+ .msg-ai {
247
+ align-self: flex-start;
248
+ max-width: 85%;
249
+ }
250
+
251
+ .msg-ai .ai-header {
252
+ display: flex;
253
+ align-items: center;
254
+ gap: 8px;
255
+ margin-bottom: 8px;
256
+ }
257
+
258
+ .ai-avatar {
259
+ width: 26px; height: 26px;
260
+ border-radius: 50%;
261
+ background: linear-gradient(135deg, #4f86f7 0%, #9b59f7 100%);
262
+ display: flex; align-items: center; justify-content: center;
263
+ font-size: 11px;
264
+ flex-shrink: 0;
265
+ }
266
+
267
+ .ai-name {
268
+ font-size: 0.78rem;
269
+ font-weight: 500;
270
+ color: var(--text-secondary);
271
+ letter-spacing: 0.01em;
272
+ }
273
+
274
+ .msg-ai .ai-body {
275
+ font-size: 0.95rem;
276
+ line-height: 1.75;
277
+ color: var(--text-primary);
278
+ font-weight: 400;
279
+ padding-left: 34px;
280
+ }
281
+
282
+ .msg-ai .ai-body.si {
283
+ font-family: 'Noto Sans Sinhala', sans-serif;
284
+ font-size: 0.9rem;
285
+ line-height: 1.9;
286
+ white-space: pre-wrap; /* Preserve line breaks from AI */
287
+ }
288
+
289
+ .msg-ai .ai-body strong { font-weight: 600; }
290
+
291
+ /* Typing indicator */
292
+ .typing-dots {
293
+ display: inline-flex;
294
+ align-items: center;
295
+ gap: 4px;
296
+ padding-left: 34px;
297
+ height: 24px;
298
+ }
299
+
300
+ .typing-dots span {
301
+ width: 6px; height: 6px;
302
+ border-radius: 50%;
303
+ background: linear-gradient(135deg, #4f86f7, #9b59f7);
304
+ animation: blink 1.2s ease-in-out infinite;
305
+ }
306
+
307
+ .typing-dots span:nth-child(2) { animation-delay: 0.2s; }
308
+ .typing-dots span:nth-child(3) { animation-delay: 0.4s; }
309
+
310
+ @keyframes blink {
311
+ 0%, 80%, 100% { opacity: 0.2; transform: scale(0.8); }
312
+ 40% { opacity: 1; transform: scale(1); }
313
+ }
314
+
315
+ /* ── Input bar ── */
316
+ #input-area {
317
+ position: fixed;
318
+ bottom: 0;
319
+ left: 0;
320
+ right: 0;
321
+ z-index: 100;
322
+ padding: 16px 24px 28px;
323
+ background: linear-gradient(to top, rgba(248,249,252,1) 60%, rgba(248,249,252,0));
324
+ }
325
+
326
+ .input-wrap {
327
+ max-width: 780px;
328
+ margin: 0 auto;
329
+ display: flex;
330
+ align-items: flex-end;
331
+ gap: 0;
332
+ background: var(--input-bg);
333
+ border-radius: 28px;
334
+ border: 1.5px solid var(--border);
335
+ box-shadow: var(--shadow-input);
336
+ padding: 8px 8px 8px 20px;
337
+ transition: border-color 0.2s, box-shadow 0.2s;
338
+ }
339
+
340
+ .input-wrap:focus-within {
341
+ border-color: rgba(79,134,247,0.4);
342
+ box-shadow: 0 8px 40px rgba(79,134,247,0.18), 0 2px 12px rgba(0,0,0,0.06);
343
+ }
344
+
345
+ #user-input {
346
+ flex: 1;
347
+ border: none;
348
+ outline: none;
349
+ background: transparent;
350
+ font-family: 'Google Sans', 'Noto Sans Sinhala', sans-serif;
351
+ font-size: 0.92rem;
352
+ color: var(--text-primary);
353
+ resize: none;
354
+ min-height: 28px;
355
+ max-height: 160px;
356
+ line-height: 1.6;
357
+ padding: 4px 0;
358
+ }
359
+
360
+ #user-input::placeholder { color: var(--text-muted); }
361
+
362
+ .input-actions {
363
+ display: flex;
364
+ align-items: flex-end;
365
+ gap: 4px;
366
+ padding-bottom: 2px;
367
+ }
368
+
369
+ .send-btn {
370
+ width: 40px; height: 40px;
371
+ border-radius: 50%;
372
+ background: linear-gradient(135deg, #4f86f7 0%, #9b59f7 100%);
373
+ border: none;
374
+ cursor: pointer;
375
+ display: flex;
376
+ align-items: center;
377
+ justify-content: center;
378
+ transition: all 0.2s ease;
379
+ flex-shrink: 0;
380
+ }
381
+
382
+ .send-btn:hover {
383
+ transform: scale(1.06);
384
+ box-shadow: 0 4px 16px rgba(79,134,247,0.4);
385
+ }
386
+
387
+ .send-btn:active { transform: scale(0.97); }
388
+
389
+ .send-btn svg { width: 18px; height: 18px; fill: white; }
390
+
391
+ .input-hint {
392
+ text-align: center;
393
+ font-size: 0.68rem;
394
+ color: var(--text-muted);
395
+ margin-top: 10px;
396
+ }
397
+
398
+ /* Responsive */
399
+ @media (max-width: 600px) {
400
+ .msg-user { max-width: 85%; }
401
+ .msg-ai { max-width: 95%; }
402
+ .welcome-title { font-size: 1.5rem; }
403
+ }
404
+ </style>
405
+ </head>
406
+ <body>
407
+
408
+ <header>
409
+ <div class="header-inner">
410
+ <div class="logo-block">
411
+ <span class="logo-title gradient-text">Olya.ai ✨</span>
412
+ <span class="logo-caption">Generated By Hansaka P. Fernando.</span>
413
+ </div>
414
+ <span class="header-badge">සිංහල AI</span>
415
+ </div>
416
+ </header>
417
+
418
+ <div id="chat-scroll">
419
+ <div id="messages">
420
+
421
+ <div id="welcome">
422
+ <div class="welcome-orb">✨</div>
423
+ <div class="welcome-title">
424
+ Hello, I'm <span class="gradient-text">Olya</span>
425
+ </div>
426
+ <div class="welcome-sub">Your intelligent Sinhala AI assistant. Ask me anything in Sinhala or English.</div>
427
+ <div class="welcome-si">ඔබේ සිංහල AI සහකාරිය. ඕනෑම දෙයක් අසන්න.</div>
428
+
429
+ <div class="chips">
430
+ <div class="chip" onclick="useSuggestion(this)">Explain quantum physics simply</div>
431
+ <div class="chip si" onclick="useSuggestion(this)">ශ්‍රී ලංකාවේ ඉතිහාසය කියන්න</div>
432
+ <div class="chip" onclick="useSuggestion(this)">Write a short poem</div>
433
+ <div class="chip si" onclick="useSuggestion(this)">AI ගැන විස්තර කරන්න</div>
434
+ <div class="chip" onclick="useSuggestion(this)">Help me learn Sinhala</div>
435
+ <div class="chip si" onclick="useSuggestion(this)">අද කාලගුණය කොහොමද?</div>
436
+ </div>
437
+ </div>
438
+
439
+ </div>
440
+ </div>
441
+
442
+ <div id="input-area">
443
+ <div class="input-wrap">
444
+ <textarea
445
+ id="user-input"
446
+ rows="1"
447
+ placeholder="Message Olya… / Olya ට message කරන්න…"
448
+ onkeydown="handleKey(event)"
449
+ oninput="autoResize(this)"
450
+ ></textarea>
451
+ <div class="input-actions">
452
+ <button class="send-btn" onclick="sendMessage()" title="Send">
453
+ <svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
454
+ <path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z"/>
455
+ </svg>
456
+ </button>
457
+ </div>
458
+ </div>
459
+ <div class="input-hint">Olya can make mistakes. Verify important info.</div>
460
+ </div>
461
+
462
+ <script>
463
+ const messagesEl = document.getElementById('messages');
464
+ const welcomeEl = document.getElementById('welcome');
465
+ const inputEl = document.getElementById('user-input');
466
+ let chatStarted = false;
467
+
468
+ function isSinhala(text) {
469
+ return /[\u0D80-\u0DFF]/.test(text);
470
+ }
471
+
472
+ function now() {
473
+ return new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
474
+ }
475
+
476
+ function autoResize(el) {
477
+ el.style.height = 'auto';
478
+ el.style.height = Math.min(el.scrollHeight, 160) + 'px';
479
+ }
480
+
481
+ function handleKey(e) {
482
+ if (e.key === 'Enter' && !e.shiftKey) {
483
+ e.preventDefault();
484
+ sendMessage();
485
+ }
486
+ }
487
+
488
+ function useSuggestion(chip) {
489
+ inputEl.value = chip.textContent;
490
+ autoResize(inputEl);
491
+ inputEl.focus();
492
+ sendMessage();
493
+ }
494
+
495
+ // අලුත් API එකත් එක්ක වැඩ කරන sendMessage function එක
496
+ async function sendMessage() {
497
+ const text = inputEl.value.trim();
498
+ if (!text) return;
499
+
500
+ /* පළමු පණිවිඩය යැවූ පසු Welcome screen එක සැඟවීම */
501
+ if (!chatStarted) {
502
+ welcomeEl.style.display = 'none';
503
+ chatStarted = true;
504
+ }
505
+
506
+ /* User ගේ පණිවිඩය පෙන්වීම */
507
+ const si = isSinhala(text);
508
+ const userRow = document.createElement('div');
509
+ userRow.className = 'msg-row msg-user';
510
+ userRow.innerHTML = `
511
+ <div class="bubble${si ? ' si' : ''}">${escHtml(text)}</div>
512
+ <div class="meta">${now()}</div>
513
+ `;
514
+ messagesEl.appendChild(userRow);
515
+ inputEl.value = '';
516
+ inputEl.style.height = 'auto';
517
+ scrollBottom();
518
+
519
+ /* AI Typing Indicator එක පෙන්වීම */
520
+ const typingRow = document.createElement('div');
521
+ typingRow.className = 'msg-row msg-ai';
522
+ typingRow.id = 'typing';
523
+ typingRow.innerHTML = `
524
+ <div class="ai-header">
525
+ <div class="ai-avatar">✨</div>
526
+ <span class="ai-name">Olya</span>
527
+ </div>
528
+ <div class="typing-dots">
529
+ <span></span><span></span><span></span>
530
+ </div>
531
+ `;
532
+ messagesEl.appendChild(typingRow);
533
+ scrollBottom();
534
+
535
+ /* Backend එකට පණිවිඩය යැවීම */
536
+ try {
537
+ const response = await fetch('/api/chat', {
538
+ method: 'POST',
539
+ headers: {
540
+ 'Content-Type': 'application/json'
541
+ },
542
+ body: JSON.stringify({ message: text })
543
+ });
544
+
545
+ if (!response.ok) {
546
+ throw new Error('Network response error');
547
+ }
548
+
549
+ const data = await response.json();
550
+
551
+ // Typing indicator එක ඉවත් කිරීම
552
+ const typingIndicator = document.getElementById('typing');
553
+ if (typingIndicator) typingIndicator.remove();
554
+
555
+ // Olya ගේ පිළිතුර පෙන්වීම
556
+ addAiMessage(data.reply, isSinhala(data.reply));
557
+ scrollBottom();
558
+
559
+ } catch (error) {
560
+ console.error('Error fetching response:', error);
561
+ const typingIndicator = document.getElementById('typing');
562
+ if (typingIndicator) typingIndicator.remove();
563
+
564
+ addAiMessage("කණගාටුයි, සර්වර් එක හා සම්බන්ධ වීමේ දෝෂයක්. කරුණාකර නැවත උත්සාහ කරන්න.", true);
565
+ scrollBottom();
566
+ }
567
+ }
568
+
569
+ function addAiMessage(text, si = false) {
570
+ const row = document.createElement('div');
571
+ row.className = 'msg-row msg-ai';
572
+
573
+ // Convert markdown bold to simple HTML bold for better display
574
+ const formattedText = escHtml(text).replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>');
575
+
576
+ row.innerHTML = `
577
+ <div class="ai-header">
578
+ <div class="ai-avatar">✨</div>
579
+ <span class="ai-name">Olya &middot; ${now()}</span>
580
+ </div>
581
+ <div class="ai-body${si ? ' si' : ''}">${formattedText}</div>
582
+ `;
583
+ messagesEl.appendChild(row);
584
+ }
585
+
586
+ function scrollBottom() {
587
+ const sc = document.getElementById('chat-scroll');
588
+ sc.scrollTo({ top: sc.scrollHeight, behavior: 'smooth' });
589
+ }
590
+
591
+ function escHtml(s) {
592
+ const d = document.createElement('div');
593
+ d.appendChild(document.createTextNode(s));
594
+ return d.innerHTML;
595
+ }
596
+ </script>
597
+ </body>
598
+ </html>