AiCoderv2 commited on
Commit
4261cb3
·
verified ·
1 Parent(s): 20d3050

Upload folder using huggingface_hub

Browse files
Files changed (1) hide show
  1. index.html +470 -169
index.html CHANGED
@@ -1,5 +1,6 @@
1
  <!DOCTYPE html>
2
  <html lang="en">
 
3
  <head>
4
  <meta charset="UTF-8" />
5
  <title>DuckDuckGo AI Chatbot</title>
@@ -9,7 +10,7 @@
9
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
10
  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
11
  <style>
12
- :root{
13
  --bg: #0b1020;
14
  --bg2: #0f1630;
15
  --panel: #0f1732;
@@ -23,13 +24,20 @@
23
  --danger: #f87171;
24
  --bubble-user: #1a254f;
25
  --bubble-bot: #141e3d;
26
- --shadow: 0 10px 30px rgba(0,0,0,.35);
27
  --radius: 14px;
28
  }
29
- *{box-sizing:border-box}
30
- html, body{
31
- margin:0; padding:0; height:100%;
32
- font-family: Inter, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, "Apple Color Emoji","Segoe UI Emoji", "Segoe UI Symbol";
 
 
 
 
 
 
 
33
  color: var(--text);
34
  background:
35
  radial-gradient(1200px 800px at 10% -20%, #20306a 0%, transparent 60%),
@@ -38,219 +46,481 @@
38
  linear-gradient(180deg, var(--bg), var(--bg2));
39
  background-attachment: fixed;
40
  }
41
- a{color: var(--accent)}
42
- .app{
43
- display:flex; flex-direction:column; min-height:100%;
44
- width:100%; max-width:1100px; margin:0 auto;
 
 
 
 
 
 
 
 
45
  }
46
- header{
47
- position: sticky; top:0; z-index:10;
 
 
 
48
  backdrop-filter: saturate(180%) blur(8px);
49
- background: linear-gradient(180deg, rgba(10,16,34,.8), rgba(10,16,34,.35));
50
- border-bottom: 1px solid rgba(255,255,255,.06);
51
  }
52
- .topbar{
53
- display:flex; align-items:center; justify-content:space-between;
54
- padding:14px 18px; gap:12px;
 
 
 
 
55
  }
56
- .brand{
57
- display:flex; align-items:center; gap:12px; min-width:0;
 
 
 
 
58
  }
59
- .logo{
60
- width:36px; height:36px; border-radius:10px;
 
 
 
61
  background: conic-gradient(from 210deg, #6ea8fe, #8ad1ff, #6ea8fe);
62
- box-shadow: 0 6px 16px rgba(110,168,254,.35), inset 0 0 18px rgba(255,255,255,.2);
63
- position:relative;
64
  }
65
- .logo::after{
66
- content:""; position:absolute; inset:3px; border-radius:8px;
67
- background: radial-gradient(120px 80px at 30% 20%, rgba(255,255,255,.5), transparent 40%),
68
- linear-gradient(180deg, rgba(255,255,255,.12), rgba(0,0,0,.18));
 
 
 
 
69
  }
70
- .titles{
71
- min-width:0;
 
72
  }
73
- .title{
74
- font-weight:800; letter-spacing:.2px; font-size:18px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
 
 
 
 
 
 
75
  }
76
- .subtitle{
77
- font-size:12px; color:var(--muted);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
78
  }
79
- .links{
80
- display:flex; gap:10px; align-items:center; flex-wrap:wrap;
 
 
81
  }
82
- .btn{
83
- appearance:none; border:none; cursor:pointer; font: inherit; color:inherit;
84
- padding:10px 14px; border-radius:10px; background: #1a254f; color:#dfe8ff;
85
- display:inline-flex; align-items:center; gap:8px;
86
- border:1px solid rgba(255,255,255,.08);
87
- transition: transform .08s ease, background .2s ease, border-color .2s ease, opacity .2s ease;
88
  }
89
- .btn:hover{ transform: translateY(-1px); border-color: rgba(255,255,255,.18) }
90
- .btn.secondary{ background: #0f1732; }
91
- .btn.ghost{ background: transparent; border-color: rgba(255,255,255,.1) }
92
- .btn:disabled{ opacity:.6; cursor:not-allowed; transform:none }
93
- .icon{
94
- width:18px; height:18px; display:inline-block;
95
  }
96
 
97
- main{
98
- flex:1; display:flex; flex-direction:column; padding: 12px;
 
 
99
  }
100
- .chat{
101
- flex:1; overflow:auto; padding: 12px; padding-bottom: 24px;
102
- scroll-behavior:smooth;
 
 
 
 
 
 
 
 
 
103
  }
104
- .messages{
105
- display:flex; flex-direction:column; gap:12px;
 
 
 
 
 
106
  }
107
- .row{
108
- display:flex; gap:10px; align-items:flex-end;
 
 
 
109
  }
110
- .row.user{ justify-content:flex-end; }
111
- .row.bot{ justify-content:flex-start; }
112
- .avatar{
113
- width:32px; height:32px; border-radius:50%; background:#172046; display:flex; align-items:center; justify-content:center; font-size:14px; font-weight:700;
114
- border:1px solid rgba(255,255,255,.08);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
115
  box-shadow: var(--shadow);
116
- flex-shrink:0;
117
  }
118
- .avatar.bot{
 
119
  background: radial-gradient(100px 80px at 20% 10%, #2a3d7a, #192555);
120
  }
121
- .avatar.user{
 
122
  background: radial-gradient(120px 90px at 30% 10%, #1a2c5f, #0e1736);
123
  }
124
- .bubble{
 
125
  max-width: min(800px, 86vw);
126
  padding: 12px 14px;
127
  border-radius: var(--radius);
128
- border: 1px solid rgba(255,255,255,.08);
129
  box-shadow: var(--shadow);
130
- line-height:1.55;
131
- position:relative;
132
- word-wrap:break-word;
133
- overflow-wrap:anywhere;
134
  }
135
- .bubble.user{
 
136
  background: linear-gradient(180deg, #152152, #101a3e);
137
  border-top-right-radius: 6px;
138
  }
139
- .bubble.bot{
 
140
  background: linear-gradient(180deg, #111a3b, #0c1431);
141
  border-bottom-left-radius: 6px;
142
  }
143
- .bubble .meta{
144
- font-size:12px; color:var(--muted); margin-bottom:6px; display:flex; gap:8px; align-items:center; flex-wrap:wrap;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
145
  }
146
- .badge{
147
- padding:2px 8px; border-radius: 999px; background:#0c1330; border:1px solid rgba(255,255,255,.08); font-size:11px; color:#c7d5ff;
 
 
148
  }
149
- .answer{
150
- font-size:15px; color:#e9efff;
 
151
  }
152
- .answer p{ margin: 0 0 10px 0; }
153
- .answer ul, .answer ol{ margin: 6px 0 10px 18px; }
154
- .answer a{ color: var(--accent-2); text-decoration: underline; }
155
- .sources{
156
- display:flex; gap:8px; flex-wrap:wrap; margin-top:8px;
157
  }
158
- .source{
159
- font-size:12px; color:#c7d5ff; background:#0c1330; border:1px solid rgba(255,255,255,.08); padding:4px 8px; border-radius:8px;
 
 
160
  }
161
- .composer{
162
- position: sticky; bottom:0; z-index:9;
163
- display:flex; gap:10px; align-items:flex-end; padding: 12px;
164
- background: linear-gradient(180deg, rgba(10,16,34,.0), rgba(10,16,34,.85));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
165
  backdrop-filter: blur(8px);
166
- border-top: 1px solid rgba(255,255,255,.06);
167
  }
168
- .input-wrap{
169
- flex:1; display:flex; gap:10px; align-items:flex-end;
 
 
 
 
170
  background: linear-gradient(180deg, #0e1633, #0b122b);
171
- border:1px solid rgba(255,255,255,.08);
172
- border-radius: 14px; padding: 8px;
 
173
  box-shadow: var(--shadow);
174
  }
175
- textarea{
176
- flex:1; resize:none; border:none; outline:none; background:transparent; color:var(--text);
177
- font: inherit; line-height:1.4; max-height:200px; min-height:40px;
 
 
 
 
 
 
 
 
 
178
  padding: 8px 10px;
179
  }
180
- .actions{ display:flex; gap:8px; align-items:center; }
181
- .small-btn{
182
- width:38px; height:38px; border-radius:10px; border:1px solid rgba(255,255,255,.1);
183
- background: #0e1633; display:flex; align-items:center; justify-content:center; cursor:pointer;
 
 
 
 
 
 
 
 
 
 
 
 
 
184
  transition: transform .08s ease, border-color .2s ease, background .2s ease;
185
  }
186
- .small-btn:hover{ transform: translateY(-1px); border-color: rgba(255,255,255,.2) }
187
- .small-btn:disabled{ opacity:.6; cursor:not-allowed; transform:none }
188
 
189
- .suggestions{
190
- position:absolute; left:12px; right:12px; bottom: 64px;
191
- display:flex; flex-wrap:wrap; gap:8px;
192
- pointer-events:none;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
193
  }
194
- .chip{
195
- pointer-events:auto;
196
- background:#0d1431; border:1px solid rgba(255,255,255,.1); color:#d8e4ff; padding:6px 10px; border-radius:999px; font-size:12px;
197
- cursor:pointer; transition: transform .08s ease, background .2s ease, border-color .2s ease;
 
 
 
 
198
  }
199
- .chip:hover{ transform: translateY(-1px); background:#121c44; border-color: rgba(255,255,255,.2) }
200
 
201
- .typing{
202
- display:inline-flex; align-items:center; gap:6px;
203
  }
204
- .dot{
205
- width:6px; height:6px; background:#c7d5ff; border-radius:50%; opacity:.8; animation: blink 1.4s infinite ease-in-out;
 
206
  }
207
- .dot:nth-child(2){ animation-delay:.2s }
208
- .dot:nth-child(3){ animation-delay:.4s }
209
  @keyframes blink {
210
- 0%, 80%, 100% { transform: translateY(0); opacity:.5 }
211
- 40% { transform: translateY(-3px); opacity:1 }
 
 
 
 
 
 
 
 
 
 
212
  }
213
 
214
- .hint{
215
- font-size:12px; color:var(--muted); padding: 4px 2px 0 2px;
 
 
216
  }
217
 
218
- .footer-note{
219
- text-align:center; font-size:12px; color:var(--muted); padding:10px 0 18px;
 
 
 
220
  }
221
 
222
  /* Responsive */
223
- @media (max-width: 720px){
224
- .topbar{ padding: 12px }
225
- .titles .title{ font-size:17px }
226
- .links{ display:none }
227
- .bubble{ max-width: min(760px, 92vw) }
228
- .suggestions{ bottom: 60px }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
229
  }
230
 
231
  /* Scrollbar */
232
- .chat::-webkit-scrollbar{ width:10px }
233
- .chat::-webkit-scrollbar-thumb{
234
- background: #16214a; border-radius:10px; border:2px solid transparent; background-clip: padding-box;
 
 
 
 
 
 
 
 
 
 
235
  }
236
- .chat::-webkit-scrollbar-track{ background: transparent }
237
 
238
  /* Simple fade-in for messages */
239
- .fade-in{
240
  animation: fadeIn .22s ease-out;
241
  }
242
- @keyframes fadeIn{
243
- from{ opacity:0; transform: translateY(6px) }
244
- to{ opacity:1; transform: translateY(0) }
 
 
 
 
 
 
 
 
245
  }
246
 
247
- .mono{
248
  font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
249
  font-size: 12px;
250
- background: #0b1434; border:1px solid rgba(255,255,255,.08); border-radius:8px; padding:10px; overflow:auto;
 
 
 
 
251
  }
252
  </style>
253
  </head>
 
254
  <body>
255
  <div class="app">
256
  <header>
@@ -259,12 +529,16 @@
259
  <div class="logo" aria-hidden="true"></div>
260
  <div class="titles">
261
  <div class="title">DuckDuckGo Answer Bot</div>
262
- <div class="subtitle">Ask anything. Powered by DuckDuckGo Instant Answers.</div>
263
  </div>
264
  </div>
265
  <div class="links">
266
  <a class="btn ghost" href="https://duckduckgo.com/" target="_blank" rel="noopener noreferrer">
267
- <svg class="icon" viewBox="0 0 24 24" fill="none"><path d="M12 2a10 10 0 1 0 .001 20.001A10 10 0 0 0 12 2Zm0 0c2.5 0 4.5 5 4.5 5s-2 5-4.5 5-4.5-5-4.5-5 2-5 4.5-5Zm0 10c4.5 0 6 5 6 5" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg>
 
 
 
 
268
  DuckDuckGo
269
  </a>
270
  <button id="exportBtn" class="btn secondary" title="Export chat as JSON">
@@ -275,7 +549,8 @@
275
  <svg class="icon" viewBox="0 0 24 24" fill="none"><path d="M3 6h18M8 6v12m8-12v12M5 6l1 14a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2L19 6M9 6V4a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2v2" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg>
276
  Clear
277
  </button>
278
- <a class="btn" href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" rel="noopener noreferrer" title="Built with anycoder">
 
279
  Built with anycoder
280
  </a>
281
  </div>
@@ -293,8 +568,10 @@
293
  <span>DuckDuckGo Instant Answers</span>
294
  </div>
295
  <div class="answer">
296
- <p>Hi! I’m your DuckDuckGo-powered assistant. Ask me anything: facts, definitions, unit conversions, weather, time, math, and more.</p>
297
- <p class="hint">Tip: Try queries like “weather in Tokyo”, “define ubiquitous”, “2pm PST to CET”, or “what is 18 celsius in fahrenheit”.</p>
 
 
298
  </div>
299
  </div>
300
  </div>
@@ -319,7 +596,7 @@
319
  </main>
320
 
321
  <div class="footer-note">
322
- Answers come from DuckDuckGo Instant Answers. Some queries may have no result—try rephrasing.
323
  </div>
324
  </div>
325
 
@@ -439,7 +716,7 @@
439
  row.appendChild(avatar);
440
  row.appendChild(bubble);
441
  messagesEl.appendChild(row);
442
- chatEl.scrollTop = chatEl.scrollHeight;
443
  return bubble;
444
  }
445
 
@@ -466,7 +743,7 @@
466
  row.appendChild(avatar);
467
  row.appendChild(bubble);
468
  messagesEl.appendChild(row);
469
- chatEl.scrollTop = chatEl.scrollHeight;
470
  return row;
471
  }
472
  function removeTyping(){
@@ -474,24 +751,56 @@
474
  if (t) t.remove();
475
  }
476
 
477
- // DuckDuckGo fetch
478
- async function fetchDDG(query){
479
  const url = new URL('https://api.duckduckgo.com/');
480
  url.searchParams.set('q', query);
481
  url.searchParams.set('format', 'json');
482
  url.searchParams.set('no_html', '1');
483
  url.searchParams.set('no_redirect', '1');
484
- // Adding these helps get more structured fields
485
  url.searchParams.set('skip_disambig', '1');
 
 
486
 
487
- const res = await fetch(url.toString(), {
488
- method: 'GET',
489
- headers: {
490
- 'Accept': 'application/json'
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
491
  }
492
- });
493
- if (!res.ok) throw new Error('Network error: ' + res.status);
494
- const data = await res.json();
 
 
 
 
 
495
  return data;
496
  }
497
 
@@ -585,14 +894,14 @@
585
  abortController = new AbortController();
586
 
587
  try{
588
- const data = await fetchDDG(text);
589
  removeTyping();
590
  const parsed = parseDDG(data, text);
591
  addMessage('bot', enhanceHTML(parsed.html), { badge: 'DuckDuckGo', note: parsed.note, sources: parsed.sources });
592
  history.push({ role: 'assistant', content: parsed.html, sources: parsed.sources });
593
  }catch(err){
594
  removeTyping();
595
- const fallback = `<p>I couldn't reach DuckDuckGo (${escapeHTML(err.message)}). Check your connection and try again.</p>`;
596
  addMessage('bot', fallback, { badge: 'Error' });
597
  history.push({ role: 'assistant', content: fallback });
598
  }finally{
@@ -663,15 +972,7 @@
663
  // Initial
664
  renderSuggestions(defaultChips);
665
  updateTextareaHeight();
666
-
667
- // Friendly: prefill a sample on first load if empty
668
- setTimeout(() => {
669
- if (!promptEl.value.trim()) {
670
- promptEl.value = 'weather in Paris';
671
- updateTextareaHeight();
672
- buildPromptChips(promptEl.value);
673
- }
674
- }, 300);
675
  </script>
676
  </body>
 
677
  </html>
 
1
  <!DOCTYPE html>
2
  <html lang="en">
3
+
4
  <head>
5
  <meta charset="UTF-8" />
6
  <title>DuckDuckGo AI Chatbot</title>
 
10
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
11
  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
12
  <style>
13
+ :root {
14
  --bg: #0b1020;
15
  --bg2: #0f1630;
16
  --panel: #0f1732;
 
24
  --danger: #f87171;
25
  --bubble-user: #1a254f;
26
  --bubble-bot: #141e3d;
27
+ --shadow: 0 10px 30px rgba(0, 0, 0, .35);
28
  --radius: 14px;
29
  }
30
+
31
+ * {
32
+ box-sizing: border-box
33
+ }
34
+
35
+ html,
36
+ body {
37
+ margin: 0;
38
+ padding: 0;
39
+ height: 100%;
40
+ font-family: Inter, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
41
  color: var(--text);
42
  background:
43
  radial-gradient(1200px 800px at 10% -20%, #20306a 0%, transparent 60%),
 
46
  linear-gradient(180deg, var(--bg), var(--bg2));
47
  background-attachment: fixed;
48
  }
49
+
50
+ a {
51
+ color: var(--accent)
52
+ }
53
+
54
+ .app {
55
+ display: flex;
56
+ flex-direction: column;
57
+ min-height: 100%;
58
+ width: 100%;
59
+ max-width: 1100px;
60
+ margin: 0 auto;
61
  }
62
+
63
+ header {
64
+ position: sticky;
65
+ top: 0;
66
+ z-index: 10;
67
  backdrop-filter: saturate(180%) blur(8px);
68
+ background: linear-gradient(180deg, rgba(10, 16, 34, .8), rgba(10, 16, 34, .35));
69
+ border-bottom: 1px solid rgba(255, 255, 255, .06);
70
  }
71
+
72
+ .topbar {
73
+ display: flex;
74
+ align-items: center;
75
+ justify-content: space-between;
76
+ padding: 14px 18px;
77
+ gap: 12px;
78
  }
79
+
80
+ .brand {
81
+ display: flex;
82
+ align-items: center;
83
+ gap: 12px;
84
+ min-width: 0;
85
  }
86
+
87
+ .logo {
88
+ width: 36px;
89
+ height: 36px;
90
+ border-radius: 10px;
91
  background: conic-gradient(from 210deg, #6ea8fe, #8ad1ff, #6ea8fe);
92
+ box-shadow: 0 6px 16px rgba(110, 168, 254, .35), inset 0 0 18px rgba(255, 255, 255, .2);
93
+ position: relative;
94
  }
95
+
96
+ .logo::after {
97
+ content: "";
98
+ position: absolute;
99
+ inset: 3px;
100
+ border-radius: 8px;
101
+ background: radial-gradient(120px 80px at 30% 20%, rgba(255, 255, 255, .5), transparent 40%),
102
+ linear-gradient(180deg, rgba(255, 255, 255, .12), rgba(0, 0, 0, .18));
103
  }
104
+
105
+ .titles {
106
+ min-width: 0;
107
  }
108
+
109
+ .title {
110
+ font-weight: 800;
111
+ letter-spacing: .2px;
112
+ font-size: 18px;
113
+ white-space: nowrap;
114
+ overflow: hidden;
115
+ text-overflow: ellipsis;
116
  }
117
+
118
+ .subtitle {
119
+ font-size: 12px;
120
+ color: var(--muted);
121
+ }
122
+
123
+ .links {
124
+ display: flex;
125
+ gap: 10px;
126
+ align-items: center;
127
+ flex-wrap: wrap;
128
+ }
129
+
130
+ .btn {
131
+ appearance: none;
132
+ border: none;
133
+ cursor: pointer;
134
+ font: inherit;
135
+ color: inherit;
136
+ padding: 10px 14px;
137
+ border-radius: 10px;
138
+ background: #1a254f;
139
+ color: #dfe8ff;
140
+ display: inline-flex;
141
+ align-items: center;
142
+ gap: 8px;
143
+ border: 1px solid rgba(255, 255, 255, .08);
144
+ transition: transform .08s ease, background .2s ease, border-color .2s ease, opacity .2s ease;
145
  }
146
+
147
+ .btn:hover {
148
+ transform: translateY(-1px);
149
+ border-color: rgba(255, 255, 255, .18)
150
  }
151
+
152
+ .btn.secondary {
153
+ background: #0f1732;
 
 
 
154
  }
155
+
156
+ .btn.ghost {
157
+ background: transparent;
158
+ border-color: rgba(255, 255, 255, .1)
 
 
159
  }
160
 
161
+ .btn:disabled {
162
+ opacity: .6;
163
+ cursor: not-allowed;
164
+ transform: none
165
  }
166
+
167
+ .icon {
168
+ width: 18px;
169
+ height: 18px;
170
+ display: inline-block;
171
+ }
172
+
173
+ main {
174
+ flex: 1;
175
+ display: flex;
176
+ flex-direction: column;
177
+ padding: 12px;
178
  }
179
+
180
+ .chat {
181
+ flex: 1;
182
+ overflow: auto;
183
+ padding: 12px;
184
+ padding-bottom: 24px;
185
+ scroll-behavior: smooth;
186
  }
187
+
188
+ .messages {
189
+ display: flex;
190
+ flex-direction: column;
191
+ gap: 12px;
192
  }
193
+
194
+ .row {
195
+ display: flex;
196
+ gap: 10px;
197
+ align-items: flex-end;
198
+ }
199
+
200
+ .row.user {
201
+ justify-content: flex-end;
202
+ }
203
+
204
+ .row.bot {
205
+ justify-content: flex-start;
206
+ }
207
+
208
+ .avatar {
209
+ width: 32px;
210
+ height: 32px;
211
+ border-radius: 50%;
212
+ background: #172046;
213
+ display: flex;
214
+ align-items: center;
215
+ justify-content: center;
216
+ font-size: 14px;
217
+ font-weight: 700;
218
+ border: 1px solid rgba(255, 255, 255, .08);
219
  box-shadow: var(--shadow);
220
+ flex-shrink: 0;
221
  }
222
+
223
+ .avatar.bot {
224
  background: radial-gradient(100px 80px at 20% 10%, #2a3d7a, #192555);
225
  }
226
+
227
+ .avatar.user {
228
  background: radial-gradient(120px 90px at 30% 10%, #1a2c5f, #0e1736);
229
  }
230
+
231
+ .bubble {
232
  max-width: min(800px, 86vw);
233
  padding: 12px 14px;
234
  border-radius: var(--radius);
235
+ border: 1px solid rgba(255, 255, 255, .08);
236
  box-shadow: var(--shadow);
237
+ line-height: 1.55;
238
+ position: relative;
239
+ word-wrap: break-word;
240
+ overflow-wrap: anywhere;
241
  }
242
+
243
+ .bubble.user {
244
  background: linear-gradient(180deg, #152152, #101a3e);
245
  border-top-right-radius: 6px;
246
  }
247
+
248
+ .bubble.bot {
249
  background: linear-gradient(180deg, #111a3b, #0c1431);
250
  border-bottom-left-radius: 6px;
251
  }
252
+
253
+ .bubble .meta {
254
+ font-size: 12px;
255
+ color: var(--muted);
256
+ margin-bottom: 6px;
257
+ display: flex;
258
+ gap: 8px;
259
+ align-items: center;
260
+ flex-wrap: wrap;
261
+ }
262
+
263
+ .badge {
264
+ padding: 2px 8px;
265
+ border-radius: 999px;
266
+ background: #0c1330;
267
+ border: 1px solid rgba(255, 255, 255, .08);
268
+ font-size: 11px;
269
+ color: #c7d5ff;
270
  }
271
+
272
+ .answer {
273
+ font-size: 15px;
274
+ color: #e9efff;
275
  }
276
+
277
+ .answer p {
278
+ margin: 0 0 10px 0;
279
  }
280
+
281
+ .answer ul,
282
+ .answer ol {
283
+ margin: 6px 0 10px 18px;
 
284
  }
285
+
286
+ .answer a {
287
+ color: var(--accent-2);
288
+ text-decoration: underline;
289
  }
290
+
291
+ .sources {
292
+ display: flex;
293
+ gap: 8px;
294
+ flex-wrap: wrap;
295
+ margin-top: 8px;
296
+ }
297
+
298
+ .source {
299
+ font-size: 12px;
300
+ color: #c7d5ff;
301
+ background: #0c1330;
302
+ border: 1px solid rgba(255, 255, 255, .08);
303
+ padding: 4px 8px;
304
+ border-radius: 8px;
305
+ }
306
+
307
+ .composer {
308
+ position: sticky;
309
+ bottom: 0;
310
+ z-index: 9;
311
+ display: flex;
312
+ gap: 10px;
313
+ align-items: flex-end;
314
+ padding: 12px;
315
+ background: linear-gradient(180deg, rgba(10, 16, 34, .0), rgba(10, 16, 34, .85));
316
  backdrop-filter: blur(8px);
317
+ border-top: 1px solid rgba(255, 255, 255, .06);
318
  }
319
+
320
+ .input-wrap {
321
+ flex: 1;
322
+ display: flex;
323
+ gap: 10px;
324
+ align-items: flex-end;
325
  background: linear-gradient(180deg, #0e1633, #0b122b);
326
+ border: 1px solid rgba(255, 255, 255, .08);
327
+ border-radius: 14px;
328
+ padding: 8px;
329
  box-shadow: var(--shadow);
330
  }
331
+
332
+ textarea {
333
+ flex: 1;
334
+ resize: none;
335
+ border: none;
336
+ outline: none;
337
+ background: transparent;
338
+ color: var(--text);
339
+ font: inherit;
340
+ line-height: 1.4;
341
+ max-height: 200px;
342
+ min-height: 40px;
343
  padding: 8px 10px;
344
  }
345
+
346
+ .actions {
347
+ display: flex;
348
+ gap: 8px;
349
+ align-items: center;
350
+ }
351
+
352
+ .small-btn {
353
+ width: 38px;
354
+ height: 38px;
355
+ border-radius: 10px;
356
+ border: 1px solid rgba(255, 255, 255, .1);
357
+ background: #0e1633;
358
+ display: flex;
359
+ align-items: center;
360
+ justify-content: center;
361
+ cursor: pointer;
362
  transition: transform .08s ease, border-color .2s ease, background .2s ease;
363
  }
 
 
364
 
365
+ .small-btn:hover {
366
+ transform: translateY(-1px);
367
+ border-color: rgba(255, 255, 255, .2)
368
+ }
369
+
370
+ .small-btn:disabled {
371
+ opacity: .6;
372
+ cursor: not-allowed;
373
+ transform: none
374
+ }
375
+
376
+ .suggestions {
377
+ position: absolute;
378
+ left: 12px;
379
+ right: 12px;
380
+ bottom: 64px;
381
+ display: flex;
382
+ flex-wrap: wrap;
383
+ gap: 8px;
384
+ pointer-events: none;
385
+ }
386
+
387
+ .chip {
388
+ pointer-events: auto;
389
+ background: #0d1431;
390
+ border: 1px solid rgba(255, 255, 255, .1);
391
+ color: #d8e4ff;
392
+ padding: 6px 10px;
393
+ border-radius: 999px;
394
+ font-size: 12px;
395
+ cursor: pointer;
396
+ transition: transform .08s ease, background .2s ease, border-color .2s ease;
397
+ }
398
+
399
+ .chip:hover {
400
+ transform: translateY(-1px);
401
+ background: #121c44;
402
+ border-color: rgba(255, 255, 255, .2)
403
+ }
404
+
405
+ .typing {
406
+ display: inline-flex;
407
+ align-items: center;
408
+ gap: 6px;
409
  }
410
+
411
+ .dot {
412
+ width: 6px;
413
+ height: 6px;
414
+ background: #c7d5ff;
415
+ border-radius: 50%;
416
+ opacity: .8;
417
+ animation: blink 1.4s infinite ease-in-out;
418
  }
 
419
 
420
+ .dot:nth-child(2) {
421
+ animation-delay: .2s
422
  }
423
+
424
+ .dot:nth-child(3) {
425
+ animation-delay: .4s
426
  }
427
+
 
428
  @keyframes blink {
429
+
430
+ 0%,
431
+ 80%,
432
+ 100% {
433
+ transform: translateY(0);
434
+ opacity: .5
435
+ }
436
+
437
+ 40% {
438
+ transform: translateY(-3px);
439
+ opacity: 1
440
+ }
441
  }
442
 
443
+ .hint {
444
+ font-size: 12px;
445
+ color: var(--muted);
446
+ padding: 4px 2px 0 2px;
447
  }
448
 
449
+ .footer-note {
450
+ text-align: center;
451
+ font-size: 12px;
452
+ color: var(--muted);
453
+ padding: 10px 0 18px;
454
  }
455
 
456
  /* Responsive */
457
+ @media (max-width: 720px) {
458
+ .topbar {
459
+ padding: 12px
460
+ }
461
+
462
+ .titles .title {
463
+ font-size: 17px
464
+ }
465
+
466
+ .links {
467
+ display: none
468
+ }
469
+
470
+ .bubble {
471
+ max-width: min(760px, 92vw)
472
+ }
473
+
474
+ .suggestions {
475
+ bottom: 60px
476
+ }
477
  }
478
 
479
  /* Scrollbar */
480
+ .chat::-webkit-scrollbar {
481
+ width: 10px
482
+ }
483
+
484
+ .chat::-webkit-scrollbar-thumb {
485
+ background: #16214a;
486
+ border-radius: 10px;
487
+ border: 2px solid transparent;
488
+ background-clip: padding-box;
489
+ }
490
+
491
+ .chat::-webkit-scrollbar-track {
492
+ background: transparent
493
  }
 
494
 
495
  /* Simple fade-in for messages */
496
+ .fade-in {
497
  animation: fadeIn .22s ease-out;
498
  }
499
+
500
+ @keyframes fadeIn {
501
+ from {
502
+ opacity: 0;
503
+ transform: translateY(6px)
504
+ }
505
+
506
+ to {
507
+ opacity: 1;
508
+ transform: translateY(0)
509
+ }
510
  }
511
 
512
+ .mono {
513
  font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
514
  font-size: 12px;
515
+ background: #0b1434;
516
+ border: 1px solid rgba(255, 255, 255, .08);
517
+ border-radius: 8px;
518
+ padding: 10px;
519
+ overflow: auto;
520
  }
521
  </style>
522
  </head>
523
+
524
  <body>
525
  <div class="app">
526
  <header>
 
529
  <div class="logo" aria-hidden="true"></div>
530
  <div class="titles">
531
  <div class="title">DuckDuckGo Answer Bot</div>
532
+ <div class="subtitle">Real-time answers via DuckDuckGo Instant Answers API (CORS-proxied).</div>
533
  </div>
534
  </div>
535
  <div class="links">
536
  <a class="btn ghost" href="https://duckduckgo.com/" target="_blank" rel="noopener noreferrer">
537
+ <svg class="icon" viewBox="0 0 24 24" fill="none">
538
+ <path
539
+ d="M12 2a10 10 0 1 0 .001 20.001A10 10 0 0 0 12 2Zm0 0c2.5 0 4.5 5 4.5 5s-2 5-4.5 5-4.5-5-4.5-5 2-5 4.5-5Zm0 10c4.5 0 6 5 6 5"
540
+ stroke="currentColor" stroke-width="2" stroke-linecap="round" />
541
+ </svg>
542
  DuckDuckGo
543
  </a>
544
  <button id="exportBtn" class="btn secondary" title="Export chat as JSON">
 
549
  <svg class="icon" viewBox="0 0 24 24" fill="none"><path d="M3 6h18M8 6v12m8-12v12M5 6l1 14a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2L19 6M9 6V4a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2v2" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg>
550
  Clear
551
  </button>
552
+ <a class="btn" href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" rel="noopener noreferrer"
553
+ title="Built with anycoder">
554
  Built with anycoder
555
  </a>
556
  </div>
 
568
  <span>DuckDuckGo Instant Answers</span>
569
  </div>
570
  <div class="answer">
571
+ <p>Hi! I’m your DuckDuckGo-powered assistant. Ask me anything: facts, definitions, unit conversions,
572
+ weather, time, math, and more.</p>
573
+ <p class="hint">Tip: Try queries like “weather in Tokyo”, “define ubiquitous”, “2pm PST to CET”, or
574
+ “what is 18 celsius in fahrenheit”.</p>
575
  </div>
576
  </div>
577
  </div>
 
596
  </main>
597
 
598
  <div class="footer-note">
599
+ Answers come from DuckDuckGo Instant Answers. CORS is bypassed via public proxies for in-browser usage.
600
  </div>
601
  </div>
602
 
 
716
  row.appendChild(avatar);
717
  row.appendChild(bubble);
718
  messagesEl.appendChild(row);
719
+ chatEl.scrollTop = chatEl.scrollHeight; // Auto scroll down
720
  return bubble;
721
  }
722
 
 
743
  row.appendChild(avatar);
744
  row.appendChild(bubble);
745
  messagesEl.appendChild(row);
746
+ chatEl.scrollTop = chatEl.scrollHeight; // Auto scroll down
747
  return row;
748
  }
749
  function removeTyping(){
 
751
  if (t) t.remove();
752
  }
753
 
754
+ // Build DuckDuckGo API URL
755
+ function buildDDGUrl(query){
756
  const url = new URL('https://api.duckduckgo.com/');
757
  url.searchParams.set('q', query);
758
  url.searchParams.set('format', 'json');
759
  url.searchParams.set('no_html', '1');
760
  url.searchParams.set('no_redirect', '1');
 
761
  url.searchParams.set('skip_disambig', '1');
762
+ return url.toString();
763
+ }
764
 
765
+ // CORS proxy list (tried in order). Using public proxies to bypass CORS in-browser.
766
+ const CORS_PROXIES = [
767
+ // https://cors.isomorphic-git.org/ simply adds permissive CORS headers to any URL
768
+ (target) => `https://cors.isomorphic-git.org/${target}`,
769
+ // https://api.codetabs.com/v1/proxy?quest= forwards the request server-side
770
+ (target) => `https://api.codetabs.com/v1/proxy?quest=${encodeURIComponent(target)}`,
771
+ // https://corsproxy.io/? provides a minimal CORS proxy
772
+ (target) => `https://corsproxy.io/?${encodeURIComponent(target)}`
773
+ ];
774
+
775
+ // Fetch with automatic CORS proxy fallback (real DuckDuckGo data only).
776
+ async function fetchWithCorsProxies(targetUrl, signal){
777
+ let lastErr = null;
778
+ for (const buildProxy of CORS_PROXIES){
779
+ const proxyUrl = buildProxy(targetUrl);
780
+ try{
781
+ const res = await fetch(proxyUrl, { signal, headers: { 'Accept': 'application/json' } });
782
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
783
+ const contentType = res.headers.get('content-type') || '';
784
+ // Some proxies may return text; we only accept JSON
785
+ if (!contentType.includes('application/json') && !contentType.includes('text/plain')) {
786
+ throw new Error('Unexpected content-type: ' + contentType);
787
+ }
788
+ // If it's text (e.g., JSON as text), still parse it
789
+ const text = await res.text();
790
+ const data = JSON.parse(text);
791
+ return data;
792
+ }catch(err){
793
+ lastErr = err;
794
+ // try next proxy
795
  }
796
+ }
797
+ throw lastErr || new Error('All CORS proxies failed');
798
+ }
799
+
800
+ // DuckDuckGo fetch (real data only, via CORS proxies)
801
+ async function fetchDDG(query, signal){
802
+ const targetUrl = buildDDGUrl(query);
803
+ const data = await fetchWithCorsProxies(targetUrl, signal);
804
  return data;
805
  }
806
 
 
894
  abortController = new AbortController();
895
 
896
  try{
897
+ const data = await fetchDDG(text, abortController.signal);
898
  removeTyping();
899
  const parsed = parseDDG(data, text);
900
  addMessage('bot', enhanceHTML(parsed.html), { badge: 'DuckDuckGo', note: parsed.note, sources: parsed.sources });
901
  history.push({ role: 'assistant', content: parsed.html, sources: parsed.sources });
902
  }catch(err){
903
  removeTyping();
904
+ const fallback = `<p>Network error or CORS proxy issue: ${escapeHTML(err.message)}. Please try again.</p>`;
905
  addMessage('bot', fallback, { badge: 'Error' });
906
  history.push({ role: 'assistant', content: fallback });
907
  }finally{
 
972
  // Initial
973
  renderSuggestions(defaultChips);
974
  updateTextareaHeight();
 
 
 
 
 
 
 
 
 
975
  </script>
976
  </body>
977
+
978
  </html>