FrederickSundeep commited on
Commit
b8ee8a7
·
1 Parent(s): bad72f7

commit 0019

Browse files
Files changed (2) hide show
  1. app.py +3 -3
  2. templates/index.html +56 -220
app.py CHANGED
@@ -162,9 +162,9 @@ def chat_stream():
162
  else:
163
  reply = generate_full_reply(message, history)
164
  # print(reply)
165
- for char in reply:
166
- yield char
167
- time.sleep(0.01) # Smooth typing speed
168
 
169
  return Response(generate(), mimetype='text/plain')
170
 
 
162
  else:
163
  reply = generate_full_reply(message, history)
164
  # print(reply)
165
+ for token in reply.splitlines(keepends=True):
166
+ yield token
167
+ time.sleep(0.05)
168
 
169
  return Response(generate(), mimetype='text/plain')
170
 
templates/index.html CHANGED
@@ -2,239 +2,75 @@
2
  <html lang="en">
3
  <head>
4
  <meta charset="UTF-8" />
5
- <title>Chat Mate</title>
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
- <link rel="stylesheet" href="/static/style.css" />
8
- <!-- MDC Web & Icons -->
9
- <link rel="stylesheet" href="https://unpkg.com/material-components-web@latest/dist/material-components-web.min.css">
10
- <script src="https://unpkg.com/material-components-web@latest/dist/material-components-web.min.js"></script>
11
 
12
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/prismjs/themes/prism-tomorrow.css">
13
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/prismjs/plugins/line-numbers/prism-line-numbers.css">
14
- <script src="https://cdn.jsdelivr.net/npm/prismjs/prism.js"></script>
15
- <script src="https://cdn.jsdelivr.net/npm/prismjs/plugins/line-numbers/prism-line-numbers.min.js"></script>
16
- <!-- Prism Core -->
17
- <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/prism.min.js"></script>
18
- <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-markup.min.js"></script>
19
- <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-markup-templating.min.js"></script>
20
- <!-- Language Components -->
21
- <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-python.min.js"></script>
22
- <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-javascript.min.js"></script>
23
- <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-typescript.min.js"></script>
24
- <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-java.min.js"></script>
25
- <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-c.min.js"></script>
26
- <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-cpp.min.js"></script>
27
- <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-bash.min.js"></script>
28
- <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-shell-session.min.js"></script> <!-- for 'shell' -->
29
- <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-sql.min.js"></script>
30
- <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-css.min.js"></script>
31
- <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-go.min.js"></script>
32
 
33
- <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-php.min.js"></script>
34
- <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-ruby.min.js"></script>
35
- <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-kotlin.min.js"></script>
36
- <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-swift.min.js"></script>
37
- <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-rust.min.js"></script>
38
- <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-scala.min.js"></script>
39
- <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-dart.min.js"></script>
40
-
41
- <script src="https://unpkg.com/vue@3"></script>
 
 
 
42
  </head>
43
-
44
  <body>
45
- <div id="app" class="chat-container">
46
- <h2>💬 Chat Mate</h2>
47
-
48
- <div class="chat-box" ref="chatbox">
49
- <div v-for="(msg, i) in history" :key="i" class="message" :class="msg.role">
50
- <template v-if="msg.role === 'assistant' && msg.content.includes('```')">
51
- <div class="message-content" v-html="renderCode(msg.content)"></div>
52
- {% raw %}
53
- <div class="timestamp" :class="msg.role">{{ msg.time }}</div>
54
- {% endraw %}
55
- </template>
56
- <template v-else>
57
- <div class="text-content" v-html="formatText(msg.content)"></div>
58
- {% raw %}
59
- <div class="timestamp" :class="msg.role">{{ msg.time }}</div>
60
- {% endraw %}
61
- </template>
62
- </div>
63
 
64
- <div v-if="loading" class="typing-indicator">
65
- <span class="dot"></span><span class="dot"></span><span class="dot"></span>
66
- </div>
67
- </div>
68
 
69
- <div class="input-area">
70
- <textarea v-model="message" placeholder="Ask something..." rows="2"></textarea>
71
- <button @click="sendMessage" :disabled="!message.trim() || loading">Send</button>
72
- </div>
73
- </div>
74
 
75
  <script>
76
- const { createApp, ref, nextTick } = Vue;
77
-
78
- createApp({
79
- setup() {
80
- const message = ref('');
81
- const history = ref([]);
82
- const loading = ref(false);
83
- const chatbox = ref(null);
84
-
85
- const scrollToBottom = () => {
86
- nextTick(() => {
87
- if (chatbox.value) chatbox.value.scrollTop = chatbox.value.scrollHeight;
88
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
89
  };
90
 
91
- const language_keywords = {
92
- python: ['def ', 'print(', 'import ', 'class '],
93
- javascript: ['function ', 'console.log(', 'let ', 'const ', 'document.getElementById'],
94
- typescript: ['interface ', 'type ', 'let ', 'const ', ': string', ': number'],
95
- java: ['import java.', 'ArrayList<', 'System.out', 'void main(', 'public class', 'new '],
96
- c: ['#include <stdio.h>', 'printf(', 'scanf(', 'int main('],
97
- cpp: ['#include', 'std::', 'cout <<', 'cin >>'],
98
- bash: ['#!/bin/bash', 'echo ', 'cd ', 'ls', 'pwd'],
99
- shell: ['#!/bin/sh', 'echo ', 'export ', 'fi'],
100
- sql: ['SELECT ', 'INSERT ', 'UPDATE ', 'FROM ', 'WHERE ', 'JOIN ', 'DELETE '],
101
- html: ['<!DOCTYPE html>', '<html>', '<div>', '<script>'],
102
- css: ['color:', 'font-size:', 'margin:', 'padding:'],
103
- go: ['package main', 'fmt.Println', 'func main()'],
104
- php: ['<?php', 'echo ', '$_', '->'],
105
- ruby: ['def ', 'puts ', 'end', 'class '],
106
- kotlin: ['fun main(', 'val ', 'var ', 'println('],
107
- swift: ['import SwiftUI', 'struct ', 'var body:', 'Text('],
108
- rust: ['fn main()', 'println!', 'let mut'],
109
- scala: ['object ', 'def ', 'val ', 'println('],
110
- dart: ['void main()', 'print(', 'var ', 'class '],
111
- };
112
-
113
- const detectLanguageByKeywords = (code) => {
114
- let bestMatch = 'plaintext';
115
- let maxScore = 0;
116
-
117
- for (const [lang, keywords] of Object.entries(language_keywords)) {
118
- let score = 0;
119
-
120
- for (const keyword of keywords) {
121
- const regex = new RegExp(keyword.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'), 'g');
122
- const matches = (code.match(regex) || []).length;
123
 
124
- // Boost score by keyword length and frequency
125
- score += matches * Math.max(1, keyword.length / 4);
126
- }
127
-
128
- if (score > maxScore) {
129
- maxScore = score;
130
- bestMatch = lang;
131
- }
132
- }
133
-
134
- return bestMatch;
135
- };
136
-
137
-
138
- const renderCode = (text) => {
139
- const codeBlocks = text.split(/```/);
140
- let output = '';
141
-
142
- for (let i = 0; i < codeBlocks.length; i++) {
143
- if (i % 2 === 1) {
144
- const lines = codeBlocks[i].split('\n');
145
- let langGuess = /^[a-zA-Z]+$/.test(lines[0]) ? lines[0].trim().toLowerCase() : '';
146
- const codeLines = langGuess ? lines.slice(1) : lines;
147
- const rawCode = codeLines.join('\n');
148
-
149
- if (!langGuess) {
150
- langGuess = detectLanguageByKeywords(rawCode);
151
  }
152
- const escapeHTML = (str) =>
153
- str
154
- .replace(/&/g, "&amp;")
155
- .replace(/</g, "&lt;")
156
- .replace(/>/g, "&gt;")
157
- .replace(/"/g, "&quot;")
158
- .replace(/'/g, "&#39;");
159
-
160
- const escapedCode = escapeHTML(rawCode);
161
-
162
- output += `
163
- <div class="code-block-wrapper">
164
- <div class="language-label">${langGuess.toUpperCase() || 'CODE'}</div>
165
- <pre class="line-numbers language-${langGuess}"><code class="language-${langGuess}">${escapedCode}</code></pre>
166
- </div>
167
- `;
168
- } else {
169
- output += `<div class="text-content">${formatText(codeBlocks[i])}</div>`;
170
- }
171
- }
172
-
173
- // highlight all new code blocks
174
- nextTick(() => {
175
- setTimeout(() => Prism.highlightAll(), 0); // slight delay ensures DOM is painted
176
- });
177
-
178
- return output;
179
- };
180
-
181
-
182
- const formatText = (text) => {
183
- return text
184
- .replace(/\n/g, "<br>")
185
- .replace(/\*\*(.*?)\*\*/g, "<strong>$1</strong>")
186
- .replace(/\*(.*?)\*/g, "<em>$1</em>");
187
- };
188
-
189
- const sendMessage = async () => {
190
- if (!message.value.trim()) return;
191
- history.value.push({
192
- role: 'user',
193
- content: message.value,
194
- time: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })
195
- });
196
- scrollToBottom();
197
-
198
- const assistant = { role: 'assistant', content: '', time: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }) };
199
- history.value.push(assistant);
200
- loading.value = true;
201
-
202
- const payload = { message: message.value, history: history.value.slice(0, -1) };
203
- message.value = '';
204
-
205
- const response = await fetch("/chat-stream", {
206
- method: "POST",
207
- headers: { "Content-Type": "application/json" },
208
- body: JSON.stringify(payload)
209
- });
210
-
211
- const reader = response.body.getReader();
212
- const decoder = new TextDecoder();
213
- let done = false;
214
-
215
- while (!done) {
216
- const { value, done: isDone } = await reader.read();
217
- if (value) {
218
- assistant.content += decoder.decode(value);
219
- scrollToBottom();
220
- }
221
- done = isDone;
222
- }
223
-
224
- loading.value = false;
225
- };
226
-
227
- return { message, history, sendMessage, renderCode, formatText, loading, chatbox };
228
- }
229
- }).mount('#app');
230
-
231
- function copyToClipboard(code, btn) {
232
- navigator.clipboard.writeText(code.replace(/\\n/g, '\n')).then(() => {
233
- btn.textContent = 'Copied!';
234
- setTimeout(() => (btn.textContent = 'Copy'), 2000);
235
- });
236
- }
237
-
238
  </script>
 
239
  </body>
240
  </html>
 
2
  <html lang="en">
3
  <head>
4
  <meta charset="UTF-8" />
5
+ <title>Chat Mate with NLUX</title>
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 
 
 
 
7
 
8
+ <!-- ✅ NLUX CSS -->
9
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/nlux@latest/dist/style.css" />
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
 
11
+ <style>
12
+ body {
13
+ font-family: 'Segoe UI', sans-serif;
14
+ margin: 0;
15
+ background: #f9f9f9;
16
+ }
17
+ #nlux-app {
18
+ height: 100vh;
19
+ display: flex;
20
+ flex-direction: column;
21
+ }
22
+ </style>
23
  </head>
 
24
  <body>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
 
26
+ <!-- ✅ NLUX Container -->
27
+ <div id="nlux-app"></div>
 
 
28
 
29
+ <!-- ✅ NLUX JavaScript -->
30
+ <script src="https://cdn.jsdelivr.net/npm/nlux@latest/dist/nlux.umd.min.js"></script>
 
 
 
31
 
32
  <script>
33
+ const app = nlux.createChat({
34
+ target: '#nlux-app',
35
+ title: '💬 Chat Mate',
36
+ placeholder: 'Ask me something...',
37
+ intro: 'Hi! I\'m Chat Mate. Ask me anything.',
38
+ theme: 'light',
39
+ onSend: async (userText, ctx) => {
40
+ ctx.addMessage({ type: 'user', text: userText });
41
+
42
+ const controller = new AbortController();
43
+ ctx.addMessage({ type: 'loading', id: 'stream' });
44
+
45
+ const response = await fetch('/chat-stream', {
46
+ method: 'POST',
47
+ signal: controller.signal,
48
+ headers: { 'Content-Type': 'application/json' },
49
+ body: JSON.stringify({
50
+ message: userText,
51
+ history: ctx.getHistory().map(m => ({ role: m.type === 'user' ? 'user' : 'assistant', content: m.text }))
52
+ })
53
+ });
54
+
55
+ const reader = response.body.getReader();
56
+ const decoder = new TextDecoder();
57
+ let text = '';
58
+ const update = (chunk) => {
59
+ text += chunk;
60
+ ctx.updateMessage('stream', { type: 'bot', text });
61
  };
62
 
63
+ while (true) {
64
+ const { value, done } = await reader.read();
65
+ if (done) break;
66
+ update(decoder.decode(value));
67
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
68
 
69
+ ctx.removeMessage('stream');
70
+ ctx.addMessage({ type: 'bot', text });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
71
  }
72
+ });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73
  </script>
74
+
75
  </body>
76
  </html>