StrawberryJelly commited on
Commit
70244d4
·
verified ·
1 Parent(s): cb3cb4d

Create static/index.html

Browse files
Files changed (1) hide show
  1. static/index.html +169 -0
static/index.html ADDED
@@ -0,0 +1,169 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="pl">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>KAI STUDIO</title>
7
+ <style>
8
+ * { margin: 0; padding: 0; box-sizing: border-box; }
9
+ body {
10
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
11
+ background: #0a0a0a; color: #e0e0e0; height: 100vh; display: flex; flex-direction: column;
12
+ }
13
+ header {
14
+ padding: 12px 20px; border-bottom: 1px solid #222; display: flex;
15
+ justify-content: space-between; align-items: center; background: #111;
16
+ }
17
+ .logo { font-weight: 700; font-size: 18px; color: #4ade80; }
18
+ .model-badge {
19
+ font-size: 12px; padding: 4px 8px; border-radius: 6px;
20
+ background: #1f2937; color: #9ca3af;
21
+ }
22
+ .model-badge.qwen { background: #3b82f6; color: white; }
23
+ .model-badge.mistral { background: #8b5cf6; color: white; }
24
+ #chat {
25
+ flex: 1; overflow-y: auto; padding: 20px; display: flex;
26
+ flex-direction: column; gap: 16px;
27
+ }
28
+ .msg { max-width: 80%; padding: 12px 16px; border-radius: 12px; line-height: 1.5; }
29
+ .msg.user { background: #1e40af; align-self: flex-end; }
30
+ .msg.assistant { background: #1f2937; align-self: flex-start; }
31
+ .msg pre { background: #0a0a0a; padding: 12px; border-radius: 8px; overflow-x: auto; margin: 8px 0; }
32
+ .msg code { font-family: 'Courier New', monospace; }
33
+ footer { padding: 16px; border-top: 1px solid #222; background: #111; }
34
+ .input-wrap { display: flex; gap: 8px; }
35
+ #input {
36
+ flex: 1; padding: 12px; border-radius: 8px; border: 1px solid #333;
37
+ background: #0a0a0a; color: #e0e0e0; font-size: 16px; resize: none;
38
+ }
39
+ #send {
40
+ padding: 0 20px; border-radius: 8px; border: none;
41
+ background: #4ade80; color: #0a0a0a; font-weight: 600; cursor: pointer;
42
+ }
43
+ #send:disabled { opacity: 0.5; cursor: not-allowed; }
44
+ .typing { display: inline-block; width: 8px; height: 8px; border-radius: 50%; background: #9ca3af; animation: blink 1s infinite; }
45
+ @keyframes blink { 50% { opacity: 0; } }
46
+ </style>
47
+ </head>
48
+ <body>
49
+ <header>
50
+ <div class="logo">KAI STUDIO</div>
51
+ <div id="modelBadge" class="model-badge">Auto</div>
52
+ </header>
53
+
54
+ <div id="chat"></div>
55
+
56
+ <footer>
57
+ <div class="input-wrap">
58
+ <textarea id="input" rows="1" placeholder="Napisz wiadomość... Shift+Enter = nowa linia"></textarea>
59
+ <button id="send">Wyślij</button>
60
+ </div>
61
+ </footer>
62
+
63
+ <script>
64
+ const chat = document.getElementById('chat');
65
+ const input = document.getElementById('input');
66
+ const send = document.getElementById('send');
67
+ const modelBadge = document.getElementById('modelBadge');
68
+
69
+ let messages = [];
70
+ let isStreaming = false;
71
+
72
+ // Auto-resize textarea
73
+ input.addEventListener('input', () => {
74
+ input.style.height = 'auto';
75
+ input.style.height = input.scrollHeight + 'px';
76
+ });
77
+
78
+ // Send on Enter, new line on Shift+Enter
79
+ input.addEventListener('keydown', (e) => {
80
+ if (e.key === 'Enter' &&!e.shiftKey) {
81
+ e.preventDefault();
82
+ sendMessage();
83
+ }
84
+ });
85
+
86
+ send.onclick = sendMessage;
87
+
88
+ function addMessage(role, content) {
89
+ const div = document.createElement('div');
90
+ div.className = `msg ${role}`;
91
+ div.innerHTML = content.replace(/```(\w+)?\n([\s\S]*?)```/g, '<pre><code>$2</code></pre>');
92
+ chat.appendChild(div);
93
+ chat.scrollTop = chat.scrollHeight;
94
+ return div;
95
+ }
96
+
97
+ async function sendMessage() {
98
+ const text = input.value.trim();
99
+ if (!text || isStreaming) return;
100
+
101
+ input.value = '';
102
+ input.style.height = 'auto';
103
+ addMessage('user', text);
104
+ messages.push({role: 'user', content: text});
105
+
106
+ isStreaming = true;
107
+ send.disabled = true;
108
+
109
+ const assistantDiv = addMessage('assistant', '<span class="typing"></span>');
110
+ let fullResponse = '';
111
+ let currentModel = 'auto';
112
+
113
+ try {
114
+ const res = await fetch('/v1/chat/completions', {
115
+ method: 'POST',
116
+ headers: {'Content-Type': 'application/json'},
117
+ body: JSON.stringify({
118
+ messages: messages,
119
+ stream: true,
120
+ max_tokens: 600
121
+ })
122
+ });
123
+
124
+ const reader = res.body.getReader();
125
+ const decoder = new TextDecoder();
126
+
127
+ while (true) {
128
+ const {done, value} = await reader.read();
129
+ if (done) break;
130
+
131
+ const chunk = decoder.decode(value);
132
+ const lines = chunk.split('\n');
133
+
134
+ for (const line of lines) {
135
+ if (line.startsWith(': x-kai-model-used ')) {
136
+ currentModel = line.split(' ')[2];
137
+ modelBadge.textContent = currentModel.toUpperCase();
138
+ modelBadge.className = `model-badge ${currentModel}`;
139
+ }
140
+ if (line.startsWith('data: ')) {
141
+ const data = line.slice(6);
142
+ if (data === '[DONE]') continue;
143
+ try {
144
+ const json = JSON.parse(data);
145
+ const delta = json.choices[0]?.delta?.content || '';
146
+ fullResponse += delta;
147
+ assistantDiv.innerHTML = fullResponse.replace(/```(\w+)?\n([\s\S]*?)```/g, '<pre><code>$2</code></pre>');
148
+ chat.scrollTop = chat.scrollHeight;
149
+ } catch (e) {}
150
+ }
151
+ }
152
+ }
153
+
154
+ messages.push({role: 'assistant', content: fullResponse});
155
+
156
+ } catch (err) {
157
+ assistantDiv.innerHTML = `Error: ${err.message}`;
158
+ } finally {
159
+ isStreaming = false;
160
+ send.disabled = false;
161
+ input.focus();
162
+ }
163
+ }
164
+
165
+ // Focus on load
166
+ input.focus();
167
+ </script>
168
+ </body>
169
+ </html>