1+ <!DOCTYPE html>
2+ < html lang ="en ">
3+
4+ < head >
5+ < meta charset ="UTF-8 ">
6+ < meta name ="viewport " content ="width=device-width, initial-scale=1.0 ">
7+ < title > Chat Demo</ title >
8+ < link href ="https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600&display=swap " rel ="stylesheet ">
9+ < style >
10+ body {
11+ margin : 0 ;
12+ background-color : # 1f2325 ;
13+ font-family : 'Poppins' , sans-serif;
14+ color : # d6deeb ;
15+ display : flex;
16+ flex-direction : column;
17+ height : 100vh ;
18+ overflow : hidden;
19+ }
20+
21+ # chat-container {
22+ flex : 1 ;
23+ padding : 20px ;
24+ display : flex;
25+ flex-direction : column;
26+ gap : 15px ;
27+ overflow-y : auto;
28+ scroll-behavior : smooth;
29+ }
30+
31+ .message {
32+ display : flex;
33+ flex-direction : row;
34+ max-width : 85% ;
35+ opacity : 0 ;
36+ transform : translateY (10px );
37+ animation : fadeIn 0.3s forwards;
38+ gap : 8px ;
39+ }
40+
41+ @keyframes fadeIn {
42+ to {
43+ opacity : 1 ;
44+ transform : translateY (0 );
45+ }
46+ }
47+
48+ .message .user {
49+ align-self : flex-end;
50+ align-items : flex-end;
51+ }
52+
53+ .message .others {
54+ align-self : flex-start;
55+ align-items : flex-end;
56+ }
57+
58+ .avatar {
59+ width : 30px ;
60+ min-width : 30px ;
61+ height : 30px ;
62+ border-radius : 50% ;
63+ background-color : # 333 ;
64+ /* margin-bottom: 5px; */
65+ display : flex;
66+ align-items : center;
67+ justify-content : center;
68+ font-size : 12px ;
69+ font-weight : bold;
70+ color : # fff ;
71+ }
72+
73+ .avatar .user {
74+ background-color : # 00dc68 ;
75+ color : # 1f2325 ;
76+ }
77+
78+ .avatar .helper1 {
79+ background-color : # f78c6c ;
80+ }
81+
82+ .avatar .helper2 {
83+ background-color : # 82aaff ;
84+ }
85+
86+ .bubble {
87+ padding : 10px 15px ;
88+ border-radius : 12px ;
89+ font-size : 14px ;
90+ line-height : 1.4 ;
91+ position : relative;
92+ word-wrap : break-word;
93+ }
94+
95+ .message .user .bubble {
96+ background-color : # 00dc68 ;
97+ color : # 003719 ;
98+ border-bottom-right-radius : 2px ;
99+ }
100+
101+ .message .others .bubble {
102+ background-color : # 2c3033 ;
103+ color : # d6deeb ;
104+ border-bottom-left-radius : 2px ;
105+ }
106+
107+ .bubble a {
108+ color : # 82aaff ;
109+ text-decoration : underline;
110+ }
111+
112+ .message .user .bubble a {
113+ color : # 003719 ;
114+ font-weight : 600 ;
115+ }
116+
117+ /* Code Block Styling */
118+ .code-block {
119+ background-color : # 111 ;
120+ padding : 8px ;
121+ border-radius : 6px ;
122+ font-family : 'Consolas' , 'Monaco' , monospace;
123+ font-size : 12px ;
124+ margin-top : 5px ;
125+ color : # d6deeb ;
126+ white-space : pre-wrap;
127+ border : 1px solid # 444 ;
128+ }
129+
130+ .message .user .code-block {
131+ background-color : # 003719 ;
132+ /* Dark green bg for code in user bubble */
133+ color : # a3f7bf ;
134+ border-color : # 005c2a ;
135+ }
136+
137+ .syntax-kw {
138+ color : # c792ea ;
139+ }
140+
141+ .syntax-str {
142+ color : # ecc48d ;
143+ }
144+
145+ .syntax-fn {
146+ color : # 82aaff ;
147+ }
148+
149+ .syntax-comment {
150+ color : # 637777 ;
151+ font-style : italic;
152+ }
153+
154+ /* Scrollbar */
155+ ::-webkit-scrollbar {
156+ width : 8px ;
157+ }
158+
159+ ::-webkit-scrollbar-track {
160+ background : # 1f2325 ;
161+ }
162+
163+ ::-webkit-scrollbar-thumb {
164+ background : # 333 ;
165+ border-radius : 4px ;
166+ }
167+
168+ ::-webkit-scrollbar-thumb : hover {
169+ background : # 444 ;
170+ }
171+ </ style >
172+ </ head >
173+
174+ < body >
175+ < div id ="chat-container "> </ div >
176+
177+ < script >
178+ const container = document . getElementById ( 'chat-container' ) ;
179+
180+ const messages = [
181+ {
182+ type : 'user' ,
183+ name : 'Markus' ,
184+ content : 'Hey, I tried this code but it doesn\'t work:' ,
185+ code : `import gint\ngint.dclear(gint.C_WHITE)\ngint.dtext("Hello", 10, 10, gint.C_BLACK)\n# Nothing happens on screen :/`
186+ } ,
187+ {
188+ type : 'others' ,
189+ id : 'helper1' ,
190+ name : 'Dante' ,
191+ content : 'You forgot <code>gint.dupdate()</code>! The screen buffer isn\'t flushed to the display automatically.' ,
192+ delay : 2000
193+ } ,
194+ {
195+ type : 'others' ,
196+ id : 'helper3' ,
197+ name : 'John' ,
198+ content : 'Check this: <a href="https://classpaddev.github.io/wiki/python/reference/gint/#dupdate---none" target="_blank">gint.dupdate() Reference</a>' ,
199+ delay : 3500
200+ } ,
201+ {
202+ type : 'others' ,
203+ id : 'helper2' ,
204+ name : 'Phoebe' ,
205+ content : 'Yeah, drawing operations only update VRAM. You need to push it to the screen. See: <a href="https://classpaddev.github.io/wiki/python/examples/rectangle/#wait-what-is-a-pixel" target="_blank">Wait, what is a pixel?</a>' ,
206+ delay : 5500
207+ } ,
208+ {
209+ type : 'user' ,
210+ name : 'Me' ,
211+ content : 'Ah, I see! Thanks guys, it works now.' ,
212+ delay : 8000
213+ }
214+ ] ;
215+
216+ // Render Logic
217+ function createMessageElement ( msg ) {
218+ const wrapper = document . createElement ( 'div' ) ;
219+ wrapper . className = `message ${ msg . type } ` ;
220+
221+ const avatar = document . createElement ( 'div' ) ;
222+ avatar . className = `avatar ${ msg . type === 'user' ? 'user' : msg . id } ` ;
223+ avatar . innerText = msg . name [ 0 ] ;
224+
225+ const contentDiv = document . createElement ( 'div' ) ;
226+ contentDiv . style . display = 'flex' ;
227+ contentDiv . style . flexDirection = 'column' ;
228+ contentDiv . style . maxWidth = '100%' ;
229+
230+ if ( msg . type === 'others' ) {
231+ // Name label
232+ const nameLabel = document . createElement ( 'span' ) ;
233+ nameLabel . style . fontSize = '11px' ;
234+ nameLabel . style . color = '#888' ;
235+ nameLabel . style . marginLeft = '10px' ;
236+ nameLabel . style . marginBottom = '2px' ;
237+ nameLabel . innerText = msg . name ;
238+ contentDiv . appendChild ( nameLabel ) ;
239+ }
240+
241+ const bubble = document . createElement ( 'div' ) ;
242+ bubble . className = 'bubble' ;
243+ bubble . innerHTML = msg . content ;
244+
245+ if ( msg . code ) {
246+ const codeBlock = document . createElement ( 'div' ) ;
247+ codeBlock . className = 'code-block' ;
248+ // Simple syntax highlighting
249+ codeBlock . innerHTML = msg . code
250+ // .replace(/import|gint/g, '<span class="syntax-kw">$&</span>')
251+ // .replace(/"(.*?)"/g, '<span class="syntax-str">"$1"</span>')
252+ // .replace(/dclear|dtext/g, '<span class="syntax-fn">$&</span>')
253+ // .replace(/#.*/g, '<span class="syntax-comment">$&</span>');
254+ bubble . appendChild ( codeBlock ) ;
255+ }
256+
257+ contentDiv . appendChild ( bubble ) ;
258+
259+ if ( msg . type === 'others' ) {
260+ wrapper . appendChild ( avatar ) ;
261+ wrapper . appendChild ( contentDiv ) ;
262+ } else {
263+ wrapper . appendChild ( contentDiv ) ;
264+ wrapper . appendChild ( avatar ) ;
265+ }
266+
267+ return wrapper ;
268+ }
269+
270+ function startSequence ( ) {
271+ container . innerHTML = '' ;
272+
273+ messages . forEach ( ( msg , index ) => {
274+ // Use the 'delay' property as the timestamp for when this message should appear
275+ // Defaulting to a cascade if not precise (but we set precise delays in array)
276+ const appearTime = msg . delay !== undefined ? msg . delay : index * 2000 ;
277+
278+ setTimeout ( ( ) => {
279+ const el = createMessageElement ( msg ) ;
280+ container . appendChild ( el ) ;
281+ container . scrollTop = container . scrollHeight ; // Auto scroll
282+
283+ // If it's the last message, schedule reset
284+ if ( index === messages . length - 1 ) {
285+ setTimeout ( startSequence , 60000 ) ; // Reset after 60s
286+ }
287+ } , appearTime ) ;
288+ } ) ;
289+ }
290+
291+ // Start
292+ startSequence ( ) ;
293+
294+ </ script >
295+ </ body >
296+
297+ </ html >
0 commit comments