AiCoderv2 commited on
Commit
a919902
·
verified ·
1 Parent(s): 70a8ce7

Upload folder using huggingface_hub

Browse files
Files changed (1) hide show
  1. index.html +294 -213
index.html CHANGED
@@ -175,6 +175,60 @@
175
  height: 18px;
176
  }
177
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
178
  /* Chat area */
179
  .chat {
180
  position: relative;
@@ -435,6 +489,29 @@
435
  margin-top: 4px;
436
  }
437
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
438
  /* Utility */
439
  .row {
440
  display: flex;
@@ -467,6 +544,10 @@
467
  .send {
468
  justify-content: flex-end;
469
  }
 
 
 
 
470
  }
471
  </style>
472
  </head>
@@ -478,10 +559,17 @@
478
  <div class="logo">AI</div>
479
  <div>
480
  <h1>AnyCoder Chat</h1>
481
- <small>Powered by Poe.com API</small>
482
  </div>
483
  </div>
484
  <div class="header-actions">
 
 
 
 
 
 
 
485
  <button class="btn small ghost" id="newChatBtn" title="Start a new chat">
486
  <svg class="icon" viewBox="0 0 24 24" fill="none"><path d="M12 5v14M5 12h14" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg>
487
  New
@@ -505,13 +593,46 @@
505
  </div>
506
  </header>
507
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
508
  <main class="chat" id="chat">
509
  <div class="messages" id="messages"></div>
510
  <div class="empty" id="empty">
511
  <div class="empty-inner">
512
- <h2 style="margin:0 0 8px 0">Hi! Im AnyCoder Chat powered by Claude on Poe.com 👋</h2>
513
- <p style="margin:0;color:var(--muted)">Ask me to explain code, brainstorm ideas, draft emails, or just chat. I
514
- can format responses with Markdown, code blocks, and links. Connected to the claude-haiku-cheap model.</p>
515
  <div class="chips" id="chips">
516
  <div class="chip">Explain closures in JavaScript</div>
517
  <div class="chip">Help me write a Python script</div>
@@ -526,7 +647,7 @@
526
  <footer class="composer">
527
  <div class="composer-inner">
528
  <div class="input-wrap">
529
- <textarea id="input" rows="1" placeholder="Message AnyCoder..."></textarea>
530
  </div>
531
  <div class="send">
532
  <div class="row">
@@ -543,21 +664,16 @@
543
  </div>
544
  <div class="hint">
545
  <div class="row" style="gap:6px">
546
- <span class="kbd">Enter</span> to send • <span class="kbd">Shift + Enter</span> for newline • Connected to Poe.com API
547
  </div>
548
  <div class="spacer"></div>
549
- <div>Using claude-haiku-cheap model via Poe.com</div>
550
  </div>
551
  </footer>
552
  </div>
553
 
554
  <script>
555
  // Configuration
556
- const POE_API_KEY = '5AvcWZHjdjrxOuRodUjyTZ2TE_D0tdrN-XantWcBY4E';
557
- const POE_BASE_URL = 'https://api.poe.com/v1';
558
- const MODEL_NAME = 'claude-haiku-cheap';
559
-
560
- // Utility: local storage wrapper
561
  const store = {
562
  get(key, fallback) { try { return JSON.parse(localStorage.getItem(key)) ?? fallback; } catch { return fallback; } },
563
  set(key, value) { localStorage.setItem(key, JSON.stringify(value)); },
@@ -624,7 +740,15 @@
624
  exportBtn: document.getElementById('exportBtn'),
625
  themeBtn: document.getElementById('themeBtn'),
626
  themeIcon: document.getElementById('themeIcon'),
627
- chat: document.getElementById('chat')
 
 
 
 
 
 
 
 
628
  };
629
 
630
  // Theme
@@ -643,6 +767,63 @@
643
  applyTheme(next);
644
  });
645
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
646
  // Chat state
647
  const ChatState = {
648
  data: store.get('anycoder_chat', []),
@@ -668,7 +849,7 @@
668
 
669
  const meta = document.createElement('div');
670
  meta.className = 'meta';
671
- meta.textContent = isUser ? 'You' : 'Claude (via Poe.com)';
672
 
673
  const content = document.createElement('div');
674
  content.className = 'content';
@@ -682,7 +863,6 @@
682
  bubble.appendChild(content);
683
 
684
  if (isUser) {
685
- // user: [avatar right]
686
  el.appendChild(bubble);
687
  el.appendChild(avatar);
688
  } else {
@@ -719,88 +899,113 @@
719
  scrollToBottom();
720
  }
721
 
722
- // Poe.com API integration
723
- const PoeAPI = {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
724
  abortController: null,
725
 
726
- async chatCompletion(messages) {
 
 
 
 
 
 
 
 
 
 
 
727
  this.abortController = new AbortController();
728
 
729
  try {
730
- const response = await fetch(`${POE_BASE_URL}/chat/completions`, {
731
- method: 'POST',
732
- headers: {
733
- 'Content-Type': 'application/json',
734
- 'Authorization': `Bearer ${POE_API_KEY}`
735
- },
736
- body: JSON.stringify({
737
- model: MODEL_NAME,
738
- messages: messages,
739
- stream: true,
740
- temperature: 0.7,
741
- max_tokens: 2000
742
- }),
743
- signal: this.abortController.signal
744
- });
745
-
746
- if (!response.ok) {
747
- const errorData = await response.json().catch(() => ({}));
748
- throw new Error(errorData.error?.message || `API request failed with status ${response.status}`);
749
  }
750
 
751
- const reader = response.body.getReader();
752
- const decoder = new TextDecoder();
753
- let buffer = '';
754
- let fullContent = '';
755
-
756
- const processChunk = async () => {
757
- while (true) {
758
- const { done, value } = await reader.read();
759
- if (done) break;
760
-
761
- buffer += decoder.decode(value, { stream: true });
762
- const lines = buffer.split('\n');
763
- buffer = lines.pop() || '';
764
-
765
- for (const line of lines) {
766
- const trimmed = line.trim();
767
- if (!trimmed) continue;
768
-
769
- if (trimmed.startsWith('data: ')) {
770
- const data = trimmed.slice(6);
771
- if (data === '[DONE]') {
772
- return { content: fullContent, done: true };
773
- }
774
-
775
- try {
776
- const parsed = JSON.parse(data);
777
- if (parsed.choices?.[0]?.delta?.content) {
778
- fullContent += parsed.choices[0].delta.content;
779
- yield { content: fullContent, done: false };
780
- }
781
- } catch (e) {
782
- // Ignore JSON parse errors for incomplete chunks
783
- }
784
- }
785
- }
786
- }
787
- return { content: fullContent, done: true };
788
- };
789
-
790
- return {
791
- [Symbol.asyncIterator]: async function* () {
792
- for await (const chunk of processChunk()) {
793
- yield chunk;
794
- }
795
- }
796
- };
797
 
798
  } catch (error) {
799
  if (error.name === 'AbortError') {
800
  throw new Error('Request was cancelled');
801
  }
802
  throw error;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
803
  }
 
 
 
 
804
  },
805
 
806
  stop() {
@@ -858,19 +1063,12 @@
858
  content: msg.content
859
  }));
860
 
861
- // Get streaming response
862
- const stream = await PoeAPI.chatCompletion(apiMessages);
863
-
864
- let finalContent = '';
865
- for await (const chunk of stream) {
866
- finalContent = chunk.content;
867
- contentEl.innerHTML = renderMarkdown(finalContent);
868
- scrollToBottom();
869
- }
870
 
871
- // Final update
872
- tempMsg.content = finalContent;
873
- contentEl.innerHTML = renderMarkdown(finalContent);
874
 
875
  // Remove typing indicator
876
  const typingIndicator = el.querySelector('.meta:last-child .typing');
@@ -878,121 +1076,4 @@
878
  typingIndicator.parentNode.remove();
879
  }
880
 
881
- ChatState.save();
882
-
883
- } catch (error) {
884
- console.error('API Error:', error);
885
-
886
- // Show error message
887
- tempMsg.content = `❌ **Error**: ${error.message}\n\nPlease check your internet connection and try again.`;
888
- contentEl.innerHTML = renderMarkdown(tempMsg.content);
889
-
890
- // Remove typing indicator
891
- const typingIndicator = el.querySelector('.meta:last-child .typing');
892
- if (typingIndicator) {
893
- typingIndicator.parentNode.remove();
894
- }
895
-
896
- // Show visual error on input
897
- els.input.classList.add('error');
898
- const errorMsg = document.createElement('div');
899
- errorMsg.className = 'error-message';
900
- errorMsg.textContent = `API Error: ${error.message}`;
901
- els.input.parentNode.appendChild(errorMsg);
902
-
903
- ChatState.save();
904
- } finally {
905
- els.stopBtn.style.display = 'none';
906
- els.sendBtn.disabled = false;
907
- }
908
- }
909
-
910
- function newChat() {
911
- ChatState.data = [];
912
- ChatState.save();
913
- renderAll();
914
- }
915
-
916
- function exportChat() {
917
- const blob = new Blob([JSON.stringify(ChatState.data, null, 2)], { type: 'application/json' });
918
- const url = URL.createObjectURL(blob);
919
- const a = document.createElement('a');
920
- a.href = url;
921
- a.download = `anycoder_chat_${new Date().toISOString().replace(/[:.]/g, '-')}.json`;
922
- document.body.appendChild(a);
923
- a.click();
924
- a.remove();
925
- URL.revokeObjectURL(url);
926
- }
927
-
928
- // Input behaviors
929
- function autoResizeTextarea() {
930
- const ta = els.input;
931
- ta.style.height = 'auto';
932
- ta.style.height = Math.min(180, ta.scrollHeight) + 'px';
933
- }
934
- els.input.addEventListener('input', autoResizeTextarea);
935
- els.input.addEventListener('keydown', (e) => {
936
- if (e.key === 'Enter' && !e.shiftKey) {
937
- e.preventDefault();
938
- sendMessage();
939
- }
940
- });
941
- els.sendBtn.addEventListener('click', sendMessage);
942
- els.newChatBtn.addEventListener('click', newChat);
943
- els.exportBtn.addEventListener('click', exportChat);
944
-
945
- // Stop button
946
- els.stopBtn.addEventListener('click', () => {
947
- PoeAPI.stop();
948
- els.stopBtn.style.display = 'none';
949
- els.sendBtn.disabled = false;
950
- });
951
-
952
- // Chips
953
- els.chips.addEventListener('click', (e) => {
954
- const chip = e.target.closest('.chip');
955
- if (!chip) return;
956
- els.input.value = chip.textContent.trim();
957
- autoResizeTextarea();
958
- els.input.focus();
959
- });
960
-
961
- // Stop generation when switching tabs (saves API calls)
962
- document.addEventListener('visibilitychange', () => {
963
- if (document.hidden) {
964
- PoeAPI.stop();
965
- els.stopBtn.style.display = 'none';
966
- els.sendBtn.disabled = false;
967
- }
968
- });
969
-
970
- // Initialize
971
- renderAll();
972
- autoResizeTextarea();
973
-
974
- // Preload a welcome message if empty
975
- if (ChatState.isEmpty) {
976
- const welcome = {
977
- id: uid(),
978
- role: 'assistant',
979
- content:
980
- `Hello! I'm connected to the Poe.com API using the claude-haiku-cheap model.
981
-
982
- I can help you with:
983
- - Code explanations and programming help
984
- - Writing and editing text
985
- - Brainstorming ideas
986
- - Answering questions
987
- - And much more!
988
-
989
- Just type your message and I'll respond in real-time. Use Markdown for formatting.`
990
- };
991
- ChatState.data.push(welcome);
992
- ChatState.save();
993
- renderAll();
994
- }
995
- </script>
996
- </body>
997
-
998
- </html>
 
175
  height: 18px;
176
  }
177
 
178
+ .btn:disabled {
179
+ opacity: 0.5;
180
+ cursor: not-allowed;
181
+ transform: none;
182
+ }
183
+
184
+ /* API Key Modal */
185
+ .modal {
186
+ position: fixed;
187
+ inset: 0;
188
+ background: rgba(0, 0, 0, 0.8);
189
+ display: none;
190
+ align-items: center;
191
+ justify-content: center;
192
+ z-index: 1000;
193
+ }
194
+
195
+ .modal.show {
196
+ display: flex;
197
+ }
198
+
199
+ .modal-content {
200
+ background: var(--panel);
201
+ border: 1px solid var(--panel-strong);
202
+ border-radius: var(--radius);
203
+ padding: 24px;
204
+ max-width: 500px;
205
+ width: 90%;
206
+ box-shadow: var(--shadow);
207
+ backdrop-filter: blur(var(--blur));
208
+ }
209
+
210
+ .modal-content h3 {
211
+ margin: 0 0 16px 0;
212
+ color: var(--text);
213
+ }
214
+
215
+ .modal-content input {
216
+ width: 100%;
217
+ padding: 12px;
218
+ border: 1px solid var(--panel-strong);
219
+ border-radius: 8px;
220
+ background: var(--glass);
221
+ color: var(--text);
222
+ font: inherit;
223
+ margin-bottom: 16px;
224
+ }
225
+
226
+ .modal-actions {
227
+ display: flex;
228
+ gap: 10px;
229
+ justify-content: flex-end;
230
+ }
231
+
232
  /* Chat area */
233
  .chat {
234
  position: relative;
 
489
  margin-top: 4px;
490
  }
491
 
492
+ /* Status indicator */
493
+ .status {
494
+ position: fixed;
495
+ top: 20px;
496
+ right: 20px;
497
+ padding: 8px 12px;
498
+ border-radius: 8px;
499
+ font-size: 12px;
500
+ z-index: 100;
501
+ backdrop-filter: blur(var(--blur));
502
+ border: 1px solid var(--panel-strong);
503
+ }
504
+
505
+ .status.connected {
506
+ background: var(--success);
507
+ color: white;
508
+ }
509
+
510
+ .status.disconnected {
511
+ background: var(--danger);
512
+ color: white;
513
+ }
514
+
515
  /* Utility */
516
  .row {
517
  display: flex;
 
544
  .send {
545
  justify-content: flex-end;
546
  }
547
+
548
+ .modal-content {
549
+ padding: 16px;
550
+ }
551
  }
552
  </style>
553
  </head>
 
559
  <div class="logo">AI</div>
560
  <div>
561
  <h1>AnyCoder Chat</h1>
562
+ <small>AI Chatbot Interface</small>
563
  </div>
564
  </div>
565
  <div class="header-actions">
566
+ <button class="btn small ghost" id="settingsBtn" title="API Settings">
567
+ <svg class="icon" viewBox="0 0 24 24" fill="none">
568
+ <path d="M12 15a3 3 0 100-6 3 3 0 000 6z" stroke="currentColor" stroke-width="2"/>
569
+ <path d="M19.4 15a1.65 1.65 0 00.33 1.82l.06.06a2 2 0 01-2.83 2.83l-.06-.06a1.65 1.65 0 00-1.82-.33 1.65 1.65 0 00-1 1.51V21a2 2 0 01-4 0v-.09a1.65 1.65 0 00-1-1.51 1.65 1.65 0 00-1.82.33l-.06.06a2 2 0 01-2.83-2.83l.06-.06a1.65 1.65 0 00.33-1.82 1.65 1.65 0 00-1.51-1H3a2 2 0 010-4h.09a1.65 1.65 0 001.51-1 1.65 1.65 0 00-.33-1.82l-.06-.06a2 2 0 012.83-2.83l.06.06a1.65 1.65 0 001.82.33H9a1.65 1.65 0 001-1.51V3a2 2 0 014 0v.09a1.65 1.65 0 001 1.51 1.65 1.65 0 001.82-.33l.06-.06a2 2 0 012.83 2.83l-.06.06a1.65 1.65 0 00-.33 1.82V9a1.65 1.65 0 001.51 1H21a2 2 0 010 4h-.09a1.65 1.65 0 00-1.51 1z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
570
+ </svg>
571
+ Settings
572
+ </button>
573
  <button class="btn small ghost" id="newChatBtn" title="Start a new chat">
574
  <svg class="icon" viewBox="0 0 24 24" fill="none"><path d="M12 5v14M5 12h14" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg>
575
  New
 
593
  </div>
594
  </header>
595
 
596
+ <!-- API Key Modal -->
597
+ <div class="modal" id="apiModal">
598
+ <div class="modal-content">
599
+ <h3>API Configuration</h3>
600
+ <p style="color: var(--muted); margin-bottom: 16px;">
601
+ To use this chat application, you need to provide a valid API key. Choose your preferred API service:
602
+ </p>
603
+ <div style="margin-bottom: 16px;">
604
+ <label style="display: block; margin-bottom: 8px; font-weight: 500;">API Service:</label>
605
+ <select id="apiService" style="width: 100%; padding: 8px; border-radius: 6px; background: var(--glass); color: var(--text); border: 1px solid var(--panel-strong);">
606
+ <option value="demo">Demo Mode (No API calls)</option>
607
+ <option value="openai">OpenAI API</option>
608
+ <option value="poe">Poe.com API</option>
609
+ </select>
610
+ </div>
611
+ <div style="margin-bottom: 16px;">
612
+ <label style="display: block; margin-bottom: 8px; font-weight: 500;">API Key:</label>
613
+ <input type="password" id="apiKey" placeholder="Enter your API key" />
614
+ <small style="color: var(--muted); display: block; margin-top: 4px;">
615
+ Your API key is stored locally and never sent anywhere except to the API service.
616
+ </small>
617
+ </div>
618
+ <div class="modal-actions">
619
+ <button class="btn ghost" id="cancelApiBtn">Cancel</button>
620
+ <button class="btn primary" id="saveApiBtn">Save</button>
621
+ </div>
622
+ </div>
623
+ </div>
624
+
625
+ <!-- Status Indicator -->
626
+ <div class="status disconnected" id="statusIndicator" style="display: none;">
627
+ API not configured
628
+ </div>
629
+
630
  <main class="chat" id="chat">
631
  <div class="messages" id="messages"></div>
632
  <div class="empty" id="empty">
633
  <div class="empty-inner">
634
+ <h2 style="margin:0 0 8px 0">Hi! I'm AnyCoder Chat 👋</h2>
635
+ <p style="margin:0;color:var(--muted)">I'm ready to help you with coding questions, brainstorming ideas, or just having a conversation. Click "Settings" above to configure your API key, or try the demo mode!</p>
 
636
  <div class="chips" id="chips">
637
  <div class="chip">Explain closures in JavaScript</div>
638
  <div class="chip">Help me write a Python script</div>
 
647
  <footer class="composer">
648
  <div class="composer-inner">
649
  <div class="input-wrap">
650
+ <textarea id="input" rows="1" placeholder="Message AnyCoder... (Configure API in settings)"></textarea>
651
  </div>
652
  <div class="send">
653
  <div class="row">
 
664
  </div>
665
  <div class="hint">
666
  <div class="row" style="gap:6px">
667
+ <span class="kbd">Enter</span> to send • <span class="kbd">Shift + Enter</span> for newline
668
  </div>
669
  <div class="spacer"></div>
670
+ <div id="apiStatus">Demo Mode - No API calls</div>
671
  </div>
672
  </footer>
673
  </div>
674
 
675
  <script>
676
  // Configuration
 
 
 
 
 
677
  const store = {
678
  get(key, fallback) { try { return JSON.parse(localStorage.getItem(key)) ?? fallback; } catch { return fallback; } },
679
  set(key, value) { localStorage.setItem(key, JSON.stringify(value)); },
 
740
  exportBtn: document.getElementById('exportBtn'),
741
  themeBtn: document.getElementById('themeBtn'),
742
  themeIcon: document.getElementById('themeIcon'),
743
+ chat: document.getElementById('chat'),
744
+ apiModal: document.getElementById('apiModal'),
745
+ apiService: document.getElementById('apiService'),
746
+ apiKey: document.getElementById('apiKey'),
747
+ saveApiBtn: document.getElementById('saveApiBtn'),
748
+ cancelApiBtn: document.getElementById('cancelApiBtn'),
749
+ settingsBtn: document.getElementById('settingsBtn'),
750
+ statusIndicator: document.getElementById('statusIndicator'),
751
+ apiStatus: document.getElementById('apiStatus')
752
  };
753
 
754
  // Theme
 
767
  applyTheme(next);
768
  });
769
 
770
+ // API Configuration
771
+ const APIConfig = {
772
+ service: store.get('anycoder_api_service', 'demo'),
773
+ key: store.get('anycoder_api_key', ''),
774
+ get isConfigured() {
775
+ return this.service !== 'demo' && this.key.trim() !== '';
776
+ },
777
+ save() {
778
+ store.set('anycoder_api_service', this.service);
779
+ store.set('anycoder_api_key', this.key);
780
+ updateAPIStatus();
781
+ }
782
+ };
783
+
784
+ function updateAPIStatus() {
785
+ const statusEl = els.statusIndicator;
786
+ const apiStatusEl = els.apiStatus;
787
+
788
+ if (!APIConfig.isConfigured) {
789
+ statusEl.textContent = 'Demo Mode';
790
+ statusEl.className = 'status disconnected';
791
+ statusEl.style.display = 'block';
792
+ apiStatusEl.textContent = 'Demo Mode - No API calls';
793
+ els.input.placeholder = 'Message AnyCoder... (Configure API in settings)';
794
+ } else {
795
+ const serviceNames = {
796
+ openai: 'OpenAI API',
797
+ poe: 'Poe.com API'
798
+ };
799
+ statusEl.textContent = `Connected to ${serviceNames[APIConfig.service] || APIConfig.service}`;
800
+ statusEl.className = 'status connected';
801
+ statusEl.style.display = 'block';
802
+ apiStatusEl.textContent = `Using ${serviceNames[APIConfig.service] || APIConfig.service}`;
803
+ els.input.placeholder = `Message AnyCoder... (Connected to ${serviceNames[APIConfig.service] || APIConfig.service})`;
804
+ }
805
+ }
806
+
807
+ // API Modal
808
+ function showAPIModal() {
809
+ els.apiService.value = APIConfig.service;
810
+ els.apiKey.value = APIConfig.key;
811
+ els.apiModal.classList.add('show');
812
+ }
813
+
814
+ function hideAPIModal() {
815
+ els.apiModal.classList.remove('show');
816
+ }
817
+
818
+ els.settingsBtn.addEventListener('click', showAPIModal);
819
+ els.cancelApiBtn.addEventListener('click', hideAPIModal);
820
+ els.saveApiBtn.addEventListener('click', () => {
821
+ APIConfig.service = els.apiService.value;
822
+ APIConfig.key = els.apiKey.value.trim();
823
+ APIConfig.save();
824
+ hideAPIModal();
825
+ });
826
+
827
  // Chat state
828
  const ChatState = {
829
  data: store.get('anycoder_chat', []),
 
849
 
850
  const meta = document.createElement('div');
851
  meta.className = 'meta';
852
+ meta.textContent = isUser ? 'You' : 'AnyCoder AI';
853
 
854
  const content = document.createElement('div');
855
  content.className = 'content';
 
863
  bubble.appendChild(content);
864
 
865
  if (isUser) {
 
866
  el.appendChild(bubble);
867
  el.appendChild(avatar);
868
  } else {
 
899
  scrollToBottom();
900
  }
901
 
902
+ // Demo AI responses
903
+ const demoResponses = [
904
+ "That's a great question! I'd be happy to help you with that. Here's what I think:\n\n**Key points:**\n- This is a demo response\n- The API is not currently configured\n- You can configure your API key in the settings\n\nLet me know if you'd like to explore this further!",
905
+ "Interesting! In a real implementation, this would connect to an AI service. For now, I'm running in demo mode.\n\n**What I can do:**\n- Process your questions\n- Provide helpful responses\n- Format with markdown\n\nWould you like me to help with something specific?",
906
+ "I understand what you're asking! This interface is ready to connect to various AI APIs including OpenAI, Claude, or others. Currently, it's running in demo mode.\n\n**To get started:**\n1. Click the Settings button\n2. Choose your preferred API service\n3. Enter your API key\n4. Start chatting!\n\nWhat would you like to work on?",
907
+ "That's a thoughtful question! I'm currently operating in demo mode, which means I'm providing pre-programmed responses rather than connecting to a live AI service.\n\n**In a full implementation, I could:**\n- Answer coding questions\n- Help with debugging\n- Explain complex concepts\n- Brainstorm ideas\n\nHow can I assist you today?"
908
+ ];
909
+
910
+ function getDemoResponse(userMessage) {
911
+ const responses = demoResponses;
912
+ const randomIndex = Math.floor(Math.random() * responses.length);
913
+ const baseResponse = responses[randomIndex];
914
+
915
+ return baseResponse + `\n\n---\n\n**Your message was:** "${userMessage}"\n\n*Configure an API key in Settings to get real AI responses!*`;
916
+ }
917
+
918
+ // API Integration
919
+ const AIAPI = {
920
  abortController: null,
921
 
922
+ async generateResponse(messages) {
923
+ if (!APIConfig.isConfigured) {
924
+ // Demo mode
925
+ const lastUserMessage = messages.filter(m => m.role === 'user').pop();
926
+ const response = getDemoResponse(lastUserMessage?.content || 'Hello');
927
+
928
+ // Simulate typing delay
929
+ await new Promise(resolve => setTimeout(resolve, 1000 + Math.random() * 2000));
930
+
931
+ return response;
932
+ }
933
+
934
  this.abortController = new AbortController();
935
 
936
  try {
937
+ let response;
938
+ if (APIConfig.service === 'openai') {
939
+ response = await this.openAIRequest(messages);
940
+ } else if (APIConfig.service === 'poe') {
941
+ response = await this.poeRequest(messages);
942
+ } else {
943
+ throw new Error('Unsupported API service');
 
 
 
 
 
 
 
 
 
 
 
 
944
  }
945
 
946
+ return response;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
947
 
948
  } catch (error) {
949
  if (error.name === 'AbortError') {
950
  throw new Error('Request was cancelled');
951
  }
952
  throw error;
953
+ } finally {
954
+ this.abortController = null;
955
+ }
956
+ },
957
+
958
+ async openAIRequest(messages) {
959
+ const response = await fetch('https://api.openai.com/v1/chat/completions', {
960
+ method: 'POST',
961
+ headers: {
962
+ 'Content-Type': 'application/json',
963
+ 'Authorization': `Bearer ${APIConfig.key}`
964
+ },
965
+ body: JSON.stringify({
966
+ model: 'gpt-3.5-turbo',
967
+ messages: messages,
968
+ temperature: 0.7,
969
+ max_tokens: 2000
970
+ }),
971
+ signal: this.abortController.signal
972
+ });
973
+
974
+ if (!response.ok) {
975
+ const errorData = await response.json().catch(() => ({}));
976
+ throw new Error(errorData.error?.message || `OpenAI API request failed with status ${response.status}`);
977
+ }
978
+
979
+ const data = await response.json();
980
+ return data.choices?.[0]?.message?.content || 'No response generated';
981
+ },
982
+
983
+ async poeRequest(messages) {
984
+ // Note: Poe.com API might require different endpoints and authentication
985
+ // This is a placeholder implementation
986
+ const response = await fetch('https://api.poe.com/v1/gql_POST', {
987
+ method: 'POST',
988
+ headers: {
989
+ 'Content-Type': 'application/json',
990
+ 'Authorization': `Bearer ${APIConfig.key}`
991
+ },
992
+ body: JSON.stringify({
993
+ query: 'mutation CreateMessage($chatId: ID!, $query: String!, $source: String) { messageCreate(chatId: $chatId, query: $query, source: $source) { chat { threadId } } }',
994
+ variables: {
995
+ chatId: 'temp-chat-id',
996
+ query: messages.map(m => `${m.role}: ${m.content}`).join('\n')
997
+ }
998
+ }),
999
+ signal: this.abortController.signal
1000
+ });
1001
+
1002
+ if (!response.ok) {
1003
+ throw new Error(`Poe.com API request failed with status ${response.status}`);
1004
  }
1005
+
1006
+ const data = await response.json();
1007
+ // This would need to be adapted based on Poe.com's actual API
1008
+ return data.data?.messageCreate?.response || 'No response generated';
1009
  },
1010
 
1011
  stop() {
 
1063
  content: msg.content
1064
  }));
1065
 
1066
+ // Get response
1067
+ const response = await AIAPI.generateResponse(apiMessages);
 
 
 
 
 
 
 
1068
 
1069
+ // Update message
1070
+ tempMsg.content = response;
1071
+ contentEl.innerHTML = renderMarkdown(response);
1072
 
1073
  // Remove typing indicator
1074
  const typingIndicator = el.querySelector('.meta:last-child .typing');
 
1076
  typingIndicator.parentNode.remove();
1077
  }
1078
 
1079
+ ChatState.save();