akhaliq HF Staff commited on
Commit
81eb58f
·
1 Parent(s): e5ed48c

refactor: redesign chat interface with sidebar, updated typography, and new dependency integrations

Browse files
Files changed (1) hide show
  1. index.html +702 -302
index.html CHANGED
@@ -1,308 +1,708 @@
1
  <!DOCTYPE html>
2
- <html lang="en">
3
  <head>
4
- <meta charset="UTF-8"/>
5
- <meta name="viewport" content="width=device-width,initial-scale=1.0"/>
6
- <title>Ling 2.6 1T · Chat</title>
7
- <meta name="description" content="Chat with inclusionAI Ling-2.6-1T, a powerful 1-trillion parameter reasoning model, via Hugging Face Inference."/>
8
- <link rel="preconnect" href="https://fonts.googleapis.com"/>
9
- <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet"/>
10
- <style>
11
- *,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
12
- :root{
13
- --bg:#08090c;
14
- --surface:#10121a;
15
- --glass:rgba(255,255,255,0.04);
16
- --border:rgba(255,255,255,0.08);
17
- --accent:#7c6aff;
18
- --accent2:#a78bfa;
19
- --glow:rgba(124,106,255,0.25);
20
- --text:#e8eaf0;
21
- --muted:#6b7280;
22
- --user-bg:rgba(124,106,255,0.15);
23
- --ai-bg:rgba(255,255,255,0.04);
24
- --radius:16px;
25
- --font:'Inter',sans-serif;
26
- --mono:'JetBrains Mono',monospace;
27
- }
28
- html,body{height:100%;background:var(--bg);color:var(--text);font-family:var(--font);overflow:hidden}
29
-
30
- /* animated gradient orbs */
31
- body::before,body::after{
32
- content:'';position:fixed;border-radius:50%;filter:blur(120px);pointer-events:none;z-index:0;
33
- }
34
- body::before{width:600px;height:600px;background:radial-gradient(circle,rgba(124,106,255,.18),transparent 70%);top:-150px;left:-100px;animation:drift1 18s ease-in-out infinite alternate}
35
- body::after{width:500px;height:500px;background:radial-gradient(circle,rgba(99,200,255,.12),transparent 70%);bottom:-100px;right:-80px;animation:drift2 22s ease-in-out infinite alternate}
36
- @keyframes drift1{to{transform:translate(80px,60px)}}
37
- @keyframes drift2{to{transform:translate(-60px,-40px)}}
38
-
39
- /* layout */
40
- #app{position:relative;z-index:1;display:flex;flex-direction:column;height:100vh;max-width:900px;margin:0 auto;padding:0 16px}
41
-
42
- /* header */
43
- header{display:flex;align-items:center;gap:14px;padding:20px 0 16px;border-bottom:1px solid var(--border);flex-shrink:0}
44
- .logo{width:42px;height:42px;border-radius:12px;background:linear-gradient(135deg,var(--accent),#a78bfa);display:flex;align-items:center;justify-content:center;font-size:20px;box-shadow:0 0 24px var(--glow)}
45
- .header-text h1{font-size:1.15rem;font-weight:700;background:linear-gradient(90deg,#fff,var(--accent2));-webkit-background-clip:text;-webkit-text-fill-color:transparent}
46
- .header-text p{font-size:.75rem;color:var(--muted);margin-top:2px}
47
- .badge{margin-left:auto;font-size:.65rem;font-weight:600;padding:4px 10px;border-radius:20px;border:1px solid var(--accent);color:var(--accent2);letter-spacing:.05em;text-transform:uppercase;white-space:nowrap}
48
-
49
- /* system prompt toggle */
50
- #sys-toggle-wrap{padding:10px 0 4px;flex-shrink:0}
51
- #sys-toggle{background:none;border:1px solid var(--border);color:var(--muted);font-size:.78rem;font-family:var(--font);padding:6px 14px;border-radius:8px;cursor:pointer;transition:all .2s;display:flex;align-items:center;gap:6px}
52
- #sys-toggle:hover{border-color:var(--accent);color:var(--accent2)}
53
- #sys-panel{display:none;margin-top:8px;background:var(--glass);border:1px solid var(--border);border-radius:var(--radius);padding:12px;backdrop-filter:blur(12px)}
54
- #sys-panel.open{display:block}
55
- #system-prompt{width:100%;background:transparent;border:none;color:var(--text);font-family:var(--font);font-size:.82rem;resize:none;outline:none;min-height:60px;line-height:1.6}
56
- #system-prompt::placeholder{color:var(--muted)}
57
-
58
- /* messages */
59
- #messages{flex:1;overflow-y:auto;padding:16px 0 8px;display:flex;flex-direction:column;gap:14px;scroll-behavior:smooth}
60
- #messages::-webkit-scrollbar{width:4px}
61
- #messages::-webkit-scrollbar-track{background:transparent}
62
- #messages::-webkit-scrollbar-thumb{background:var(--border);border-radius:4px}
63
-
64
- .msg{display:flex;gap:12px;animation:fadeUp .3s ease}
65
- @keyframes fadeUp{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}
66
- .msg.user{flex-direction:row-reverse}
67
- .avatar{width:34px;height:34px;border-radius:10px;display:flex;align-items:center;justify-content:center;font-size:15px;flex-shrink:0;align-self:flex-start;margin-top:2px}
68
- .msg.user .avatar{background:linear-gradient(135deg,var(--accent),#a78bfa)}
69
- .msg.ai .avatar{background:var(--glass);border:1px solid var(--border)}
70
- .bubble{max-width:78%;padding:12px 16px;border-radius:var(--radius);font-size:.88rem;line-height:1.7;white-space:pre-wrap;word-break:break-word}
71
- .msg.user .bubble{background:var(--user-bg);border:1px solid rgba(124,106,255,.3);border-top-right-radius:4px}
72
- .msg.ai .bubble{background:var(--ai-bg);border:1px solid var(--border);border-top-left-radius:4px}
73
-
74
- /* code blocks inside bubbles */
75
- .bubble code{font-family:var(--mono);font-size:.8rem;background:rgba(0,0,0,.4);padding:2px 6px;border-radius:4px;color:#a5f3fc}
76
- .bubble pre{background:rgba(0,0,0,.5);border:1px solid var(--border);border-radius:10px;padding:12px;margin:8px 0;overflow-x:auto}
77
- .bubble pre code{background:none;padding:0}
78
-
79
- /* typing cursor */
80
- .cursor{display:inline-block;width:2px;height:1em;background:var(--accent2);margin-left:2px;animation:blink .7s step-end infinite;vertical-align:text-bottom}
81
- @keyframes blink{0%,100%{opacity:1}50%{opacity:0}}
82
-
83
- /* empty state */
84
- #empty{display:flex;flex-direction:column;align-items:center;justify-content:center;flex:1;gap:12px;color:var(--muted);text-align:center;pointer-events:none;user-select:none}
85
- #empty .big-icon{font-size:48px;opacity:.4}
86
- #empty p{font-size:.85rem;max-width:320px;line-height:1.7}
87
- #empty.hidden{display:none}
88
-
89
- /* suggestions */
90
- #suggestions{display:flex;flex-wrap:wrap;gap:8px;padding:8px 0;flex-shrink:0}
91
- .sug{background:var(--glass);border:1px solid var(--border);border-radius:10px;padding:8px 14px;font-size:.78rem;color:var(--muted);cursor:pointer;transition:all .2s;line-height:1.4}
92
- .sug:hover{border-color:var(--accent);color:var(--accent2);background:rgba(124,106,255,.08)}
93
- #suggestions.hidden{display:none}
94
-
95
- /* input bar */
96
- #input-wrap{flex-shrink:0;padding:12px 0 20px}
97
- #input-box{display:flex;align-items:flex-end;gap:10px;background:var(--glass);border:1px solid var(--border);border-radius:20px;padding:12px 14px;backdrop-filter:blur(16px);transition:border-color .2s,box-shadow .2s}
98
- #input-box:focus-within{border-color:rgba(124,106,255,.5);box-shadow:0 0 0 3px rgba(124,106,255,.1)}
99
- #user-input{flex:1;background:none;border:none;outline:none;color:var(--text);font-family:var(--font);font-size:.9rem;resize:none;max-height:160px;line-height:1.6;overflow-y:auto}
100
- #user-input::placeholder{color:var(--muted)}
101
- #send-btn{width:38px;height:38px;border-radius:12px;background:linear-gradient(135deg,var(--accent),#a78bfa);border:none;cursor:pointer;display:flex;align-items:center;justify-content:center;flex-shrink:0;transition:transform .15s,box-shadow .2s;box-shadow:0 0 16px var(--glow)}
102
- #send-btn:hover:not(:disabled){transform:scale(1.07);box-shadow:0 0 24px var(--glow)}
103
- #send-btn:disabled{opacity:.4;cursor:not-allowed;transform:none}
104
- #send-btn svg{width:18px;height:18px;fill:none;stroke:#fff;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
105
-
106
- /* clear btn */
107
- #clear-btn{background:none;border:1px solid var(--border);color:var(--muted);font-size:.75rem;font-family:var(--font);padding:5px 12px;border-radius:8px;cursor:pointer;transition:all .2s;flex-shrink:0;align-self:center}
108
- #clear-btn:hover{border-color:#ef4444;color:#f87171}
109
-
110
- /* footer */
111
- footer{text-align:center;font-size:.68rem;color:var(--muted);padding-bottom:8px;flex-shrink:0}
112
- footer a{color:var(--accent2);text-decoration:none}
113
- </style>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
114
  </head>
115
  <body>
116
- <div id="app">
117
- <header>
118
- <div class="logo">🧠</div>
119
- <div class="header-text">
120
- <h1>Ling 2.6 · 1T</h1>
121
- <p>inclusionAI · via Hugging Face Router</p>
122
- </div>
123
- <div class="badge">novita</div>
124
- <button id="clear-btn" title="Clear conversation">Clear</button>
125
- </header>
126
-
127
- <div id="sys-toggle-wrap">
128
- <button id="sys-toggle">⚙️ System prompt <span id="sys-arrow">▾</span></button>
129
- <div id="sys-panel">
130
- <textarea id="system-prompt" rows="3" placeholder="Optional system instructions… e.g. 'You are a concise coding assistant.'"></textarea>
131
- </div>
132
- </div>
133
-
134
- <div id="messages">
135
- <div id="empty">
136
- <div class="big-icon">💬</div>
137
- <p>Ask Ling-2.6-1T anything — math, reasoning, code, or open-ended questions.</p>
138
- </div>
139
- </div>
140
-
141
- <div id="suggestions">
142
- <div class="sug" onclick="useSuggestion(this)">🔢 Prove that √2 is irrational</div>
143
- <div class="sug" onclick="useSuggestion(this)">🐍 Write a binary search in Python</div>
144
- <div class="sug" onclick="useSuggestion(this)">🌍 What is the capital of France?</div>
145
- <div class="sug" onclick="useSuggestion(this)">🧩 Explain attention in transformers</div>
146
- </div>
147
-
148
- <div id="input-wrap">
149
- <div id="input-box">
150
- <textarea id="user-input" placeholder="Message Ling…" rows="1" id="user-input"></textarea>
151
- <button id="send-btn" title="Send">
152
- <svg viewBox="0 0 24 24"><line x1="22" y1="2" x2="11" y2="13"/><polygon points="22 2 15 22 11 13 2 9 22 2"/></svg>
153
- </button>
154
- </div>
155
- </div>
156
- <footer>Powered by <a href="https://huggingface.co/inclusionAI/Ling-2.6-1T" target="_blank">inclusionAI/Ling-2.6-1T</a> · Gradio Server</footer>
157
- </div>
158
-
159
- <script>
160
- const messagesEl = document.getElementById('messages');
161
- const inputEl = document.getElementById('user-input');
162
- const sendBtn = document.getElementById('send-btn');
163
- const emptyEl = document.getElementById('empty');
164
- const sugsEl = document.getElementById('suggestions');
165
- const sysToggle = document.getElementById('sys-toggle');
166
- const sysPanel = document.getElementById('sys-panel');
167
- const sysArrow = document.getElementById('sys-arrow');
168
- const sysInput = document.getElementById('system-prompt');
169
- const clearBtn = document.getElementById('clear-btn');
170
-
171
- let history = []; // [{role,content}]
172
- let streaming = false;
173
-
174
- // System prompt toggle
175
- sysToggle.addEventListener('click', () => {
176
- sysPanel.classList.toggle('open');
177
- sysArrow.textContent = sysPanel.classList.contains('open') ? '▴' : '▾';
178
- });
179
-
180
- // Auto-resize textarea
181
- inputEl.addEventListener('input', () => {
182
- inputEl.style.height = 'auto';
183
- inputEl.style.height = Math.min(inputEl.scrollHeight, 160) + 'px';
184
- });
185
-
186
- // Send on Enter (Shift+Enter = newline)
187
- inputEl.addEventListener('keydown', e => {
188
- if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); sendMessage(); }
189
- });
190
- sendBtn.addEventListener('click', sendMessage);
191
- clearBtn.addEventListener('click', clearConversation);
192
-
193
- function useSuggestion(el) {
194
- inputEl.value = el.textContent.replace(/^[^\s]+\s/, '');
195
- inputEl.focus();
196
- sendMessage();
197
- }
198
-
199
- function clearConversation() {
200
- history = [];
201
- messagesEl.innerHTML = '<div id="empty"><div class="big-icon">💬</div><p>Ask Ling-2.6-1T anything — math, reasoning, code, or open-ended questions.</p></div>';
202
- emptyEl && (emptyEl.classList.remove('hidden'));
203
- sugsEl.classList.remove('hidden');
204
- }
205
-
206
- function appendMsg(role, content = '') {
207
- const empt = document.getElementById('empty');
208
- if (empt) empt.remove();
209
- sugsEl.classList.add('hidden');
210
-
211
- const wrap = document.createElement('div');
212
- wrap.className = `msg ${role}`;
213
- const av = document.createElement('div');
214
- av.className = 'avatar';
215
- av.textContent = role === 'user' ? '🧑' : '🤖';
216
- const bub = document.createElement('div');
217
- bub.className = 'bubble';
218
- bub.textContent = content;
219
- wrap.appendChild(av);
220
- wrap.appendChild(bub);
221
- messagesEl.appendChild(wrap);
222
- messagesEl.scrollTop = messagesEl.scrollHeight;
223
- return bub;
224
- }
225
-
226
- function setBubbleContent(bub, text) {
227
- // Simple markdown: code fences and inline code
228
- let escaped = text
229
- .replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
230
- // code fences
231
- escaped = escaped.replace(/```([\w]*)\n?([\s\S]*?)```/g,
232
- (_,lang,code) => `<pre><code>${code.trimEnd()}</code></pre>`);
233
- // inline code
234
- escaped = escaped.replace(/`([^`]+)`/g, '<code>$1</code>');
235
- // bold
236
- escaped = escaped.replace(/\*\*(.+?)\*\*/g,'<strong>$1</strong>');
237
- bub.innerHTML = escaped;
238
- }
239
-
240
- async function sendMessage() {
241
- const text = inputEl.value.trim();
242
- if (!text || streaming) return;
243
-
244
- inputEl.value = '';
245
- inputEl.style.height = 'auto';
246
- sendBtn.disabled = true;
247
- streaming = true;
248
-
249
- history.push({ role: 'user', content: text });
250
- appendMsg('user', text);
251
-
252
- const aiBub = appendMsg('ai', '');
253
- const cursor = document.createElement('span');
254
- cursor.className = 'cursor';
255
- aiBub.appendChild(cursor);
256
-
257
- let accumulated = '';
258
-
259
- try {
260
- const resp = await fetch('/stream_chat', {
261
- method: 'POST',
262
- headers: { 'Content-Type': 'application/json' },
263
- body: JSON.stringify({
264
- messages: history,
265
- system_prompt: sysInput.value,
266
- }),
267
- });
268
-
269
- const reader = resp.body.getReader();
270
- const decoder = new TextDecoder();
271
- let buf = '';
272
-
273
- while (true) {
274
- const { done, value } = await reader.read();
275
- if (done) break;
276
- buf += decoder.decode(value, { stream: true });
277
- const parts = buf.split('\n\n');
278
- buf = parts.pop();
279
- for (const part of parts) {
280
- if (!part.startsWith('data:')) continue;
281
- const raw = part.slice(5).trim();
282
- if (raw === '[DONE]') break;
283
- try {
284
- const { token } = JSON.parse(raw);
285
- accumulated += token;
286
- cursor.remove();
287
- setBubbleContent(aiBub, accumulated);
288
- aiBub.appendChild(cursor);
289
- messagesEl.scrollTop = messagesEl.scrollHeight;
290
- } catch {}
291
- }
292
- }
293
- } catch (err) {
294
- accumulated = '⚠️ Error: ' + err.message;
295
- setBubbleContent(aiBub, accumulated);
296
- }
297
-
298
- cursor.remove();
299
- setBubbleContent(aiBub, accumulated);
300
- history.push({ role: 'assistant', content: accumulated });
301
- streaming = false;
302
- sendBtn.disabled = false;
303
- inputEl.focus();
304
- messagesEl.scrollTop = messagesEl.scrollHeight;
305
- }
306
- </script>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
307
  </body>
308
  </html>
 
1
  <!DOCTYPE html>
2
+ <html lang="en" class="dark">
3
  <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Ling 2.6 · Reasoning Engine</title>
7
+ <meta name="description" content="Experience Ling-2.6-1T, a 1-trillion parameter reasoning model by inclusionAI. High-fidelity intelligence at your fingertips.">
8
+
9
+ <!-- Fonts & Icons -->
10
+ <link rel="preconnect" href="https://fonts.googleapis.com">
11
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
12
+ <link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
13
+ <script src="https://unpkg.com/lucide@latest"></script>
14
+ <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
15
+
16
+ <style>
17
+ :root {
18
+ --bg: #0a0a0c;
19
+ --sidebar: #111114;
20
+ --surface: #16161a;
21
+ --border: rgba(255, 255, 255, 0.08);
22
+ --accent: #8b5cf6;
23
+ --accent-glow: rgba(139, 92, 246, 0.3);
24
+ --text-primary: #f8fafc;
25
+ --text-secondary: #94a3b8;
26
+ --text-muted: #64748b;
27
+ --user-bubble: #2d2d35;
28
+ --ai-bubble: transparent;
29
+ --radius-lg: 20px;
30
+ --radius-md: 12px;
31
+ --font-sans: 'Plus Jakarta Sans', sans-serif;
32
+ --font-mono: 'JetBrains Mono', monospace;
33
+ --transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
34
+ }
35
+
36
+ * {
37
+ box-sizing: border-box;
38
+ margin: 0;
39
+ padding: 0;
40
+ -webkit-font-smoothing: antialiased;
41
+ }
42
+
43
+ body {
44
+ background: var(--bg);
45
+ color: var(--text-primary);
46
+ font-family: var(--font-sans);
47
+ height: 100vh;
48
+ display: flex;
49
+ overflow: hidden;
50
+ }
51
+
52
+ /* --- Sidebar --- */
53
+ #sidebar {
54
+ width: 280px;
55
+ background: var(--sidebar);
56
+ border-right: 1px solid var(--border);
57
+ display: flex;
58
+ flex-direction: column;
59
+ transition: var(--transition);
60
+ z-index: 100;
61
+ }
62
+
63
+ @media (max-width: 768px) {
64
+ #sidebar {
65
+ position: fixed;
66
+ left: -280px;
67
+ height: 100%;
68
+ }
69
+ #sidebar.open {
70
+ left: 0;
71
+ box-shadow: 20px 0 50px rgba(0,0,0,0.5);
72
+ }
73
+ }
74
+
75
+ .sidebar-header {
76
+ padding: 24px;
77
+ display: flex;
78
+ align-items: center;
79
+ gap: 12px;
80
+ }
81
+
82
+ .brand-logo {
83
+ width: 32px;
84
+ height: 32px;
85
+ background: linear-gradient(135deg, var(--accent), #d8b4fe);
86
+ border-radius: 8px;
87
+ display: flex;
88
+ align-items: center;
89
+ justify-content: center;
90
+ color: white;
91
+ box-shadow: 0 0 15px var(--accent-glow);
92
+ }
93
+
94
+ .brand-name {
95
+ font-weight: 700;
96
+ font-size: 1.1rem;
97
+ letter-spacing: -0.02em;
98
+ }
99
+
100
+ .new-chat-btn {
101
+ margin: 0 16px 20px;
102
+ padding: 12px;
103
+ background: var(--surface);
104
+ border: 1px solid var(--border);
105
+ border-radius: var(--radius-md);
106
+ color: var(--text-primary);
107
+ display: flex;
108
+ align-items: center;
109
+ gap: 10px;
110
+ cursor: pointer;
111
+ transition: var(--transition);
112
+ font-size: 0.9rem;
113
+ font-weight: 500;
114
+ }
115
+
116
+ .new-chat-btn:hover {
117
+ border-color: var(--accent);
118
+ background: rgba(139, 92, 246, 0.05);
119
+ }
120
+
121
+ .sidebar-nav {
122
+ flex: 1;
123
+ padding: 0 16px;
124
+ overflow-y: auto;
125
+ }
126
+
127
+ .nav-section {
128
+ margin-bottom: 24px;
129
+ }
130
+
131
+ .nav-label {
132
+ font-size: 0.7rem;
133
+ text-transform: uppercase;
134
+ letter-spacing: 0.05em;
135
+ color: var(--text-muted);
136
+ margin-bottom: 12px;
137
+ padding-left: 8px;
138
+ }
139
+
140
+ .nav-item {
141
+ padding: 10px 12px;
142
+ border-radius: 8px;
143
+ color: var(--text-secondary);
144
+ font-size: 0.85rem;
145
+ display: flex;
146
+ align-items: center;
147
+ gap: 10px;
148
+ cursor: pointer;
149
+ transition: var(--transition);
150
+ white-space: nowrap;
151
+ overflow: hidden;
152
+ text-overflow: ellipsis;
153
+ }
154
+
155
+ .nav-item:hover {
156
+ background: rgba(255, 255, 255, 0.03);
157
+ color: var(--text-primary);
158
+ }
159
+
160
+ .sidebar-footer {
161
+ padding: 20px;
162
+ border-top: 1px solid var(--border);
163
+ }
164
+
165
+ /* --- Main Content --- */
166
+ #main {
167
+ flex: 1;
168
+ display: flex;
169
+ flex-direction: column;
170
+ position: relative;
171
+ background: radial-gradient(circle at top right, rgba(139, 92, 246, 0.03), transparent 40%);
172
+ }
173
+
174
+ header {
175
+ height: 64px;
176
+ display: flex;
177
+ align-items: center;
178
+ padding: 0 24px;
179
+ border-bottom: 1px solid var(--border);
180
+ justify-content: space-between;
181
+ backdrop-filter: blur(10px);
182
+ z-index: 50;
183
+ }
184
+
185
+ .header-left {
186
+ display: flex;
187
+ align-items: center;
188
+ gap: 16px;
189
+ }
190
+
191
+ #menu-toggle {
192
+ display: none;
193
+ cursor: pointer;
194
+ color: var(--text-secondary);
195
+ }
196
+
197
+ @media (max-width: 768px) {
198
+ #menu-toggle { display: block; }
199
+ }
200
+
201
+ .model-badge {
202
+ background: rgba(139, 92, 246, 0.1);
203
+ color: var(--accent);
204
+ padding: 4px 10px;
205
+ border-radius: 20px;
206
+ font-size: 0.75rem;
207
+ font-weight: 600;
208
+ border: 1px solid rgba(139, 92, 246, 0.2);
209
+ }
210
+
211
+ /* --- Chat Messages --- */
212
+ #chat-container {
213
+ flex: 1;
214
+ overflow-y: auto;
215
+ padding: 40px 0;
216
+ display: flex;
217
+ flex-direction: column;
218
+ align-items: center;
219
+ scroll-behavior: smooth;
220
+ }
221
+
222
+ .message-wrap {
223
+ width: 100%;
224
+ max-width: 800px;
225
+ padding: 24px 20px;
226
+ display: flex;
227
+ gap: 20px;
228
+ animation: fadeIn 0.4s ease-out;
229
+ }
230
+
231
+ @keyframes fadeIn {
232
+ from { opacity: 0; transform: translateY(10px); }
233
+ to { opacity: 1; transform: translateY(0); }
234
+ }
235
+
236
+ .message-wrap.user {
237
+ background: transparent;
238
+ }
239
+
240
+ .avatar {
241
+ width: 36px;
242
+ height: 36px;
243
+ border-radius: 8px;
244
+ display: flex;
245
+ align-items: center;
246
+ justify-content: center;
247
+ flex-shrink: 0;
248
+ }
249
+
250
+ .user .avatar {
251
+ background: #3b82f6;
252
+ color: white;
253
+ }
254
+
255
+ .ai .avatar {
256
+ background: var(--accent);
257
+ color: white;
258
+ }
259
+
260
+ .message-content {
261
+ flex: 1;
262
+ font-size: 0.95rem;
263
+ line-height: 1.6;
264
+ color: var(--text-primary);
265
+ }
266
+
267
+ .message-content p {
268
+ margin-bottom: 12px;
269
+ }
270
+
271
+ .message-content p:last-child {
272
+ margin-bottom: 0;
273
+ }
274
+
275
+ /* Markdown Styling */
276
+ .message-content pre {
277
+ background: #0d0d0f;
278
+ border: 1px solid var(--border);
279
+ border-radius: 12px;
280
+ padding: 16px;
281
+ margin: 16px 0;
282
+ overflow-x: auto;
283
+ }
284
+
285
+ .message-content code {
286
+ font-family: var(--font-mono);
287
+ font-size: 0.85rem;
288
+ background: rgba(255,255,255,0.05);
289
+ padding: 2px 6px;
290
+ border-radius: 4px;
291
+ }
292
+
293
+ .message-content pre code {
294
+ background: none;
295
+ padding: 0;
296
+ }
297
+
298
+ /* --- Empty State --- */
299
+ #empty-state {
300
+ flex: 1;
301
+ display: flex;
302
+ flex-direction: column;
303
+ align-items: center;
304
+ justify-content: center;
305
+ text-align: center;
306
+ padding: 20px;
307
+ }
308
+
309
+ .empty-icon {
310
+ width: 64px;
311
+ height: 64px;
312
+ background: var(--surface);
313
+ border-radius: 20px;
314
+ display: flex;
315
+ align-items: center;
316
+ justify-content: center;
317
+ margin-bottom: 24px;
318
+ color: var(--accent);
319
+ border: 1px solid var(--border);
320
+ }
321
+
322
+ .empty-title {
323
+ font-size: 1.5rem;
324
+ font-weight: 700;
325
+ margin-bottom: 12px;
326
+ }
327
+
328
+ .empty-subtitle {
329
+ color: var(--text-secondary);
330
+ max-width: 400px;
331
+ line-height: 1.6;
332
+ }
333
+
334
+ /* --- Input Bar --- */
335
+ #input-area {
336
+ padding: 20px;
337
+ display: flex;
338
+ flex-direction: column;
339
+ align-items: center;
340
+ background: linear-gradient(transparent, var(--bg) 50%);
341
+ }
342
+
343
+ .input-container {
344
+ width: 100%;
345
+ max-width: 800px;
346
+ background: var(--surface);
347
+ border: 1px solid var(--border);
348
+ border-radius: 24px;
349
+ padding: 8px 12px;
350
+ display: flex;
351
+ align-items: flex-end;
352
+ gap: 12px;
353
+ transition: var(--transition);
354
+ box-shadow: 0 10px 30px rgba(0,0,0,0.2);
355
+ }
356
+
357
+ .input-container:focus-within {
358
+ border-color: rgba(139, 92, 246, 0.5);
359
+ box-shadow: 0 10px 40px rgba(139, 92, 246, 0.1);
360
+ }
361
+
362
+ #user-input {
363
+ flex: 1;
364
+ background: none;
365
+ border: none;
366
+ outline: none;
367
+ color: var(--text-primary);
368
+ padding: 12px 8px;
369
+ font-family: inherit;
370
+ font-size: 1rem;
371
+ resize: none;
372
+ max-height: 200px;
373
+ min-height: 24px;
374
+ }
375
+
376
+ .action-btn {
377
+ width: 40px;
378
+ height: 40px;
379
+ border-radius: 50%;
380
+ border: none;
381
+ background: transparent;
382
+ color: var(--text-secondary);
383
+ display: flex;
384
+ align-items: center;
385
+ justify-content: center;
386
+ cursor: pointer;
387
+ transition: var(--transition);
388
+ }
389
+
390
+ .action-btn:hover {
391
+ background: rgba(255,255,255,0.05);
392
+ color: var(--text-primary);
393
+ }
394
+
395
+ .send-btn {
396
+ background: var(--accent);
397
+ color: white;
398
+ }
399
+
400
+ .send-btn:hover {
401
+ background: #7c3aed;
402
+ transform: scale(1.05);
403
+ }
404
+
405
+ .send-btn:disabled {
406
+ background: var(--border);
407
+ color: var(--text-muted);
408
+ cursor: not-allowed;
409
+ transform: none;
410
+ }
411
+
412
+ .input-footer {
413
+ margin-top: 12px;
414
+ font-size: 0.75rem;
415
+ color: var(--text-muted);
416
+ }
417
+
418
+ /* --- Scrollbar --- */
419
+ ::-webkit-scrollbar { width: 6px; }
420
+ ::-webkit-scrollbar-track { background: transparent; }
421
+ ::-webkit-scrollbar-thumb { background: var(--border); border-radius: 10px; }
422
+ ::-webkit-scrollbar-thumb:hover { background: rgba(255,255,255,0.15); }
423
+
424
+ /* Typing Indicator */
425
+ .typing {
426
+ display: flex;
427
+ gap: 4px;
428
+ padding: 8px 0;
429
+ }
430
+ .dot {
431
+ width: 4px;
432
+ height: 4px;
433
+ background: var(--text-muted);
434
+ border-radius: 50%;
435
+ animation: bounce 1.4s infinite ease-in-out both;
436
+ }
437
+ .dot:nth-child(1) { animation-delay: -0.32s; }
438
+ .dot:nth-child(2) { animation-delay: -0.16s; }
439
+ @keyframes bounce {
440
+ 0%, 80%, 100% { transform: scale(0); }
441
+ 40% { transform: scale(1.0); }
442
+ }
443
+
444
+ /* System Prompt Panel */
445
+ #system-panel {
446
+ position: absolute;
447
+ top: 70px;
448
+ right: 24px;
449
+ width: 320px;
450
+ background: var(--surface);
451
+ border: 1px solid var(--border);
452
+ border-radius: var(--radius-md);
453
+ padding: 16px;
454
+ box-shadow: 0 10px 30px rgba(0,0,0,0.3);
455
+ display: none;
456
+ z-index: 1000;
457
+ animation: fadeIn 0.2s ease;
458
+ }
459
+ #system-panel.show { display: block; }
460
+ #system-panel h3 { font-size: 0.9rem; margin-bottom: 10px; }
461
+ #system-input {
462
+ width: 100%;
463
+ background: var(--bg);
464
+ border: 1px solid var(--border);
465
+ border-radius: 8px;
466
+ color: var(--text-primary);
467
+ padding: 10px;
468
+ font-size: 0.85rem;
469
+ resize: vertical;
470
+ min-height: 80px;
471
+ }
472
+ </style>
473
  </head>
474
  <body>
475
+ <aside id="sidebar">
476
+ <div class="sidebar-header">
477
+ <div class="brand-logo">
478
+ <i data-lucide="brain-circuit"></i>
479
+ </div>
480
+ <span class="brand-name">Ling Engine</span>
481
+ </div>
482
+
483
+ <button class="new-chat-btn" onclick="clearConversation()">
484
+ <i data-lucide="plus" size="18"></i>
485
+ New Chat
486
+ </button>
487
+
488
+ <div class="sidebar-nav">
489
+ <div class="nav-section">
490
+ <div class="nav-label">Recent</div>
491
+ <div id="history-list">
492
+ <!-- History items will appear here -->
493
+ <div class="nav-item">
494
+ <i data-lucide="message-square" size="16"></i>
495
+ <span>Introduction to Ling</span>
496
+ </div>
497
+ </div>
498
+ </div>
499
+ </div>
500
+
501
+ <div class="sidebar-footer">
502
+ <div class="nav-item">
503
+ <i data-lucide="settings" size="16"></i>
504
+ <span>Settings</span>
505
+ </div>
506
+ </div>
507
+ </aside>
508
+
509
+ <main id="main">
510
+ <header>
511
+ <div class="header-left">
512
+ <div id="menu-toggle" onclick="toggleSidebar()">
513
+ <i data-lucide="menu"></i>
514
+ </div>
515
+ <div class="model-badge">Ling-2.6-1T</div>
516
+ </div>
517
+ <div class="header-right" style="display: flex; gap: 12px;">
518
+ <button class="action-btn" title="System Prompt" onclick="toggleSystemPanel()">
519
+ <i data-lucide="sliders"></i>
520
+ </button>
521
+ <button class="action-btn" title="Clear Chat" onclick="clearConversation()">
522
+ <i data-lucide="trash-2"></i>
523
+ </button>
524
+ </div>
525
+
526
+ <div id="system-panel">
527
+ <h3>System Instructions</h3>
528
+ <textarea id="system-input" placeholder="Configure the model's behavior..."></textarea>
529
+ </div>
530
+ </header>
531
+
532
+ <div id="chat-container">
533
+ <div id="empty-state">
534
+ <div class="empty-icon">
535
+ <i data-lucide="sparkles" size="32"></i>
536
+ </div>
537
+ <h1 class="empty-title">How can I help you today?</h1>
538
+ <p class="empty-subtitle">Ling-2.6-1T is a trillion-parameter reasoning model capable of complex problem solving, coding, and creative analysis.</p>
539
+
540
+ <div style="display: flex; gap: 10px; margin-top: 32px; flex-wrap: wrap; justify-content: center;">
541
+ <div class="new-chat-btn" style="margin:0; padding: 12px 20px;" onclick="useSuggestion('Explain quantum entanglement')">
542
+ Quantum Physics
543
+ </div>
544
+ <div class="new-chat-btn" style="margin:0; padding: 12px 20px;" onclick="useSuggestion('Write a fast API with Rust')">
545
+ Coding Help
546
+ </div>
547
+ <div class="new-chat-btn" style="margin:0; padding: 12px 20px;" onclick="useSuggestion('Write a sci-fi short story')">
548
+ Creative Writing
549
+ </div>
550
+ </div>
551
+ </div>
552
+ </div>
553
+
554
+ <div id="input-area">
555
+ <div class="input-container">
556
+ <button class="action-btn">
557
+ <i data-lucide="paperclip" size="20"></i>
558
+ </button>
559
+ <textarea id="user-input" placeholder="Message Ling..." rows="1"></textarea>
560
+ <button id="send-btn" class="action-btn send-btn" onclick="sendMessage()">
561
+ <i data-lucide="arrow-up" size="20"></i>
562
+ </button>
563
+ </div>
564
+ <div class="input-footer">
565
+ Ling can make mistakes. Check important info.
566
+ </div>
567
+ </div>
568
+ </main>
569
+
570
+ <script>
571
+ lucide.createIcons();
572
+
573
+ const chatContainer = document.getElementById('chat-container');
574
+ const userInput = document.getElementById('user-input');
575
+ const sendBtn = document.getElementById('send-btn');
576
+ const emptyState = document.getElementById('empty-state');
577
+ const systemInput = document.getElementById('system-input');
578
+ const sidebar = document.getElementById('sidebar');
579
+
580
+ let history = [];
581
+ let isStreaming = false;
582
+
583
+ // Auto-resize textarea
584
+ userInput.addEventListener('input', () => {
585
+ userInput.style.height = 'auto';
586
+ userInput.style.height = userInput.scrollHeight + 'px';
587
+ });
588
+
589
+ userInput.addEventListener('keydown', (e) => {
590
+ if (e.key === 'Enter' && !e.shiftKey) {
591
+ e.preventDefault();
592
+ sendMessage();
593
+ }
594
+ });
595
+
596
+ function toggleSidebar() {
597
+ sidebar.classList.toggle('open');
598
+ }
599
+
600
+ function toggleSystemPanel() {
601
+ document.getElementById('system-panel').classList.toggle('show');
602
+ }
603
+
604
+ function useSuggestion(text) {
605
+ userInput.value = text;
606
+ userInput.style.height = 'auto';
607
+ userInput.style.height = userInput.scrollHeight + 'px';
608
+ sendMessage();
609
+ }
610
+
611
+ function clearConversation() {
612
+ history = [];
613
+ chatContainer.innerHTML = '';
614
+ chatContainer.appendChild(emptyState);
615
+ emptyState.style.display = 'flex';
616
+ }
617
+
618
+ function appendMessage(role, content = '') {
619
+ if (emptyState.style.display !== 'none') {
620
+ emptyState.style.display = 'none';
621
+ }
622
+
623
+ const wrap = document.createElement('div');
624
+ wrap.className = `message-wrap ${role}`;
625
+
626
+ const avatar = document.createElement('div');
627
+ avatar.className = 'avatar';
628
+ avatar.innerHTML = role === 'user' ? '<i data-lucide="user" size="20"></i>' : '<i data-lucide="bot" size="20"></i>';
629
+
630
+ const msgContent = document.createElement('div');
631
+ msgContent.className = 'message-content';
632
+
633
+ if (role === 'ai' && content === '') {
634
+ msgContent.innerHTML = '<div class="typing"><div class="dot"></div><div class="dot"></div><div class="dot"></div></div>';
635
+ } else {
636
+ msgContent.innerHTML = marked.parse(content);
637
+ }
638
+
639
+ wrap.appendChild(avatar);
640
+ wrap.appendChild(msgContent);
641
+ chatContainer.appendChild(wrap);
642
+ lucide.createIcons();
643
+
644
+ chatContainer.scrollTop = chatContainer.scrollHeight;
645
+ return msgContent;
646
+ }
647
+
648
+ async function sendMessage() {
649
+ const text = userInput.value.trim();
650
+ if (!text || isStreaming) return;
651
+
652
+ userInput.value = '';
653
+ userInput.style.height = 'auto';
654
+ isStreaming = true;
655
+ sendBtn.disabled = true;
656
+
657
+ appendMessage('user', text);
658
+ history.push({ role: 'user', content: text });
659
+
660
+ const aiMsgEl = appendMessage('ai', '');
661
+ let accumulated = '';
662
+
663
+ try {
664
+ const response = await fetch('/stream_chat', {
665
+ method: 'POST',
666
+ headers: { 'Content-Type': 'application/json' },
667
+ body: JSON.stringify({
668
+ messages: history,
669
+ system_prompt: systemInput.value
670
+ })
671
+ });
672
+
673
+ const reader = response.body.getReader();
674
+ const decoder = new TextDecoder();
675
+
676
+ aiMsgEl.innerHTML = ''; // Remove typing indicator
677
+
678
+ while (true) {
679
+ const { done, value } = await reader.read();
680
+ if (done) break;
681
+
682
+ const chunk = decoder.decode(value);
683
+ const lines = chunk.split('\n\n');
684
+
685
+ for (const line of lines) {
686
+ if (line.startsWith('data: ')) {
687
+ const data = line.slice(6);
688
+ if (data === '[DONE]') break;
689
+ try {
690
+ const parsed = JSON.parse(data);
691
+ accumulated += parsed.token;
692
+ aiMsgEl.innerHTML = marked.parse(accumulated);
693
+ chatContainer.scrollTop = chatContainer.scrollHeight;
694
+ } catch (e) {}
695
+ }
696
+ }
697
+ }
698
+ } catch (error) {
699
+ aiMsgEl.innerHTML = '<span style="color: #ef4444;">Error: Connection failed</span>';
700
+ } finally {
701
+ isStreaming = false;
702
+ sendBtn.disabled = false;
703
+ history.push({ role: 'assistant', content: accumulated });
704
+ }
705
+ }
706
+ </script>
707
  </body>
708
  </html>