Testing347 commited on
Commit
85c9219
·
verified ·
1 Parent(s): 513dd02

Update chat.html

Browse files
Files changed (1) hide show
  1. chat.html +39 -66
chat.html CHANGED
@@ -26,10 +26,6 @@
26
  color: transparent;
27
  }
28
 
29
- .neural-bg {
30
- background: radial-gradient(circle at center, #0f172a 0%, #020617 100%);
31
- }
32
-
33
  .conscious-element { transition: all 0.3s ease; }
34
  .conscious-element:hover { transform: scale(1.02); }
35
 
@@ -60,7 +56,6 @@
60
  .modal-hidden { opacity: 0; transform: translateY(20px); pointer-events: none; }
61
  .modal-visible { opacity: 1; transform: translateY(0); }
62
 
63
- /* Subtle “console aura” layer */
64
  .aura {
65
  background:
66
  radial-gradient(circle at 25% 15%, rgba(99,102,241,0.20), transparent 42%),
@@ -68,7 +63,6 @@
68
  radial-gradient(circle at 50% 45%, rgba(139,92,246,0.10), transparent 55%);
69
  }
70
 
71
- /* Keep focus rings consistent */
72
  .focus-ring:focus { outline: none; box-shadow: 0 0 0 2px rgba(99,102,241,0.65); }
73
  </style>
74
  </head>
@@ -77,7 +71,7 @@
77
  <!-- Animated background -->
78
  <div id="vanta-bg" class="fixed top-0 left-0 w-full h-full z-0"></div>
79
 
80
- <!-- Top bar: minimal + glyph trigger -->
81
  <nav class="relative z-10 py-6 px-8 flex justify-between items-center backdrop-blur-sm">
82
  <a href="index.html" class="flex items-center space-x-2">
83
  <div class="w-8 h-8 rounded-full bg-indigo-600 flex items-center justify-center">
@@ -100,7 +94,7 @@
100
  </div>
101
  </nav>
102
 
103
- <!-- Console Body -->
104
  <section class="relative z-10 px-6 py-16">
105
  <div class="max-w-5xl mx-auto">
106
  <div class="text-center mb-10">
@@ -118,9 +112,7 @@
118
  </p>
119
  </div>
120
 
121
- <!-- The Console Window -->
122
  <div class="relative rounded-2xl border border-gray-800 bg-gray-900/30 overflow-hidden aura">
123
- <!-- “window controls” header -->
124
  <div class="bg-gray-800/40 px-6 py-4 border-b border-gray-800 flex items-center justify-between">
125
  <div class="flex items-center">
126
  <div class="w-3 h-3 rounded-full bg-red-500 mr-2"></div>
@@ -138,20 +130,18 @@
138
  </div>
139
  </div>
140
 
141
- <!-- Small mode badge -->
142
  <div class="text-xs px-2.5 py-1 rounded-full border border-indigo-500/30 text-indigo-200 bg-indigo-900/15">
143
  DRAFT
144
  </div>
145
  </div>
146
 
147
- <!-- Messages -->
148
  <div id="chat-messages" class="chat-container p-6 space-y-4" aria-live="polite" aria-label="Chat messages">
149
- <div class="flex items-start">
150
  <div class="w-8 h-8 rounded-full bg-indigo-600 flex-shrink-0 flex items-center justify-center mr-3">
151
  <i class="fas fa-robot text-white text-sm"></i>
152
  </div>
153
  <div class="bg-gray-800/70 rounded-lg p-4 max-w-[85%]">
154
- <p class="text-gray-100">
155
  Acknowledged. This console is a controlled interface. State your objective; I will respond with constraints, assumptions, and next steps.
156
  </p>
157
  <p class="text-gray-400 text-xs mt-2">Note: do not paste secrets or API keys into chat.</p>
@@ -159,7 +149,6 @@
159
  </div>
160
  </div>
161
 
162
- <!-- Input -->
163
  <div class="px-6 py-4 border-t border-gray-800 bg-black/10">
164
  <form id="chat-form" class="flex items-center" autocomplete="off" aria-label="Send message">
165
  <input id="chat-input" type="text"
@@ -187,21 +176,18 @@
187
  </div>
188
  </div>
189
 
190
- <!-- Small implementation note (kept subtle) -->
191
  <div class="mt-3 text-xs text-gray-600">
192
- Integration note: do not call OpenAI directly from the browser. Use a server endpoint (example: <span class="text-gray-400">/api/chat</span>) to keep keys private.
 
193
  </div>
194
  </div>
195
  </div>
196
  </div>
197
  </section>
198
 
199
- <!-- Minimal Footer -->
200
  <footer class="relative z-10 px-6 pb-10">
201
  <div class="max-w-5xl mx-auto border-t border-gray-800/60 pt-8 flex flex-col md:flex-row justify-between items-center gap-4">
202
- <div class="text-sm text-gray-500">
203
- © 2025 SILENTPATTERN. All rights reserved.
204
- </div>
205
  <div class="text-sm text-gray-500 flex gap-6">
206
  <a href="research.html" class="hover:text-indigo-400 transition">Research</a>
207
  <a href="privacy.html" class="hover:text-indigo-400 transition">Privacy</a>
@@ -211,7 +197,7 @@
211
  </div>
212
  </footer>
213
 
214
- <!-- ACCESS MODAL (same behavior as index) -->
215
  <div id="access-modal"
216
  class="fixed inset-0 z-50 flex items-center justify-center bg-black/80 backdrop-blur-sm modal modal-hidden"
217
  role="dialog" aria-modal="true" aria-labelledby="access-modal-title" tabindex="-1">
@@ -231,18 +217,15 @@
231
  <form id="access-form" class="space-y-4">
232
  <div>
233
  <label for="name" class="block text-sm font-medium mb-1">Full Name</label>
234
- <input type="text" id="name"
235
- class="w-full bg-gray-800/50 border border-gray-700 rounded-lg px-4 py-2 focus-ring">
236
  </div>
237
  <div>
238
  <label for="email" class="block text-sm font-medium mb-1">Email</label>
239
- <input type="email" id="email"
240
- class="w-full bg-gray-800/50 border border-gray-700 rounded-lg px-4 py-2 focus-ring">
241
  </div>
242
  <div>
243
  <label for="institution" class="block text-sm font-medium mb-1">Institution/Organization</label>
244
- <input type="text" id="institution"
245
- class="w-full bg-gray-800/50 border border-gray-700 rounded-lg px-4 py-2 focus-ring">
246
  </div>
247
  <div>
248
  <label for="purpose" class="block text-sm font-medium mb-1">Purpose of Access</label>
@@ -266,7 +249,7 @@
266
  </div>
267
  </div>
268
 
269
- <!-- LAB NAVIGATOR (same as index, trimmed actions for this page) -->
270
  <div id="lab-navigator"
271
  class="fixed inset-0 z-[60] bg-black/80 backdrop-blur-md modal modal-hidden"
272
  role="dialog" aria-modal="true" aria-label="Lab Navigator" tabindex="-1">
@@ -386,9 +369,7 @@
386
  </div>
387
 
388
  <script>
389
- /* -------------------------------------------------------------
390
- VANTA BACKGROUND
391
- ------------------------------------------------------------- */
392
  const vantaEffect = VANTA.NET({
393
  el: "#vanta-bg",
394
  mouseControls: true,
@@ -406,9 +387,7 @@
406
  });
407
  window.addEventListener('resize', () => vantaEffect.resize());
408
 
409
- /* -------------------------------------------------------------
410
- MODAL ACCESSIBILITY HELPERS
411
- ------------------------------------------------------------- */
412
  function trapFocus(modal) {
413
  const focusable = modal.querySelectorAll('a, button, input, select, textarea, [tabindex]:not([tabindex="-1"])');
414
  if (!focusable.length) return;
@@ -429,14 +408,12 @@
429
  modal.addEventListener('keydown', handler);
430
  modal._focusHandler = handler;
431
  }
432
-
433
  function untrapFocus(modal) {
434
  if (modal._focusHandler) {
435
  modal.removeEventListener('keydown', modal._focusHandler);
436
  delete modal._focusHandler;
437
  }
438
  }
439
-
440
  const toggleModal = (modal, show) => {
441
  if (show) {
442
  modal.classList.remove('modal-hidden');
@@ -451,9 +428,7 @@
451
  }
452
  };
453
 
454
- /* -------------------------------------------------------------
455
- ACCESS MODAL
456
- ------------------------------------------------------------- */
457
  const accessModal = document.getElementById('access-modal');
458
  const accessBtn = document.getElementById('access-btn');
459
  const closeAccessModal = document.getElementById('close-access-modal');
@@ -462,12 +437,8 @@
462
  toggleModal(accessModal, true);
463
  setTimeout(() => document.getElementById('name').focus(), 50);
464
  });
465
-
466
  closeAccessModal.addEventListener('click', () => toggleModal(accessModal, false));
467
-
468
- accessModal.addEventListener('click', (e) => {
469
- if (e.target === accessModal) toggleModal(accessModal, false);
470
- });
471
 
472
  document.getElementById('access-form').addEventListener('submit', (e) => {
473
  e.preventDefault();
@@ -475,7 +446,6 @@
475
  const email = document.getElementById('email').value.trim();
476
  const institution = document.getElementById('institution').value.trim();
477
  const purpose = document.getElementById('purpose').value;
478
-
479
  if (!name || !email || !institution || !purpose) {
480
  alert('Please fill in all fields.');
481
  return;
@@ -485,9 +455,7 @@
485
  toggleModal(accessModal, false);
486
  });
487
 
488
- /* -------------------------------------------------------------
489
- LAB NAVIGATOR
490
- ------------------------------------------------------------- */
491
  const labNav = document.getElementById('lab-navigator');
492
  const labNavBtn = document.getElementById('lab-nav-btn');
493
  const labNavClose = document.getElementById('lab-nav-close');
@@ -497,7 +465,6 @@
497
 
498
  labNavBtn.addEventListener('click', openLabNav);
499
  labNavClose.addEventListener('click', closeLabNav);
500
-
501
  labNav.addEventListener('click', (e) => {
502
  const shouldClose = e.target && e.target.getAttribute('data-lab-close') === 'true';
503
  if (shouldClose) closeLabNav();
@@ -568,7 +535,6 @@
568
  function renderDossier(key) {
569
  const d = DOSSIERS[key];
570
  if (!d) return;
571
-
572
  dossierTitle.textContent = d.title;
573
  dossierSubtitle.textContent = d.subtitle;
574
  dossierStatus.textContent = d.status;
@@ -594,12 +560,9 @@
594
  btn.addEventListener('click', () => renderDossier(btn.getAttribute('data-dossier')));
595
  });
596
 
597
- /* -------------------------------------------------------------
598
- CHAT (SECURE-BY-DESIGN CLIENT)
599
- - This page does NOT embed API keys.
600
- - It calls a server endpoint: POST /api/chat
601
- - If not available, it returns a local “demo” response.
602
- ------------------------------------------------------------- */
603
  const chatForm = document.getElementById('chat-form');
604
  const chatInput = document.getElementById('chat-input');
605
  const chatMessages = document.getElementById('chat-messages');
@@ -610,6 +573,14 @@
610
 
611
  const transcript = []; // {role:'user'|'system', content:string, ts:number}
612
 
 
 
 
 
 
 
 
 
613
  function escapeHtml(str) {
614
  return str
615
  .replaceAll('&', '&amp;')
@@ -658,12 +629,11 @@
658
  }
659
 
660
  async function callServerChat(userMessage) {
661
- const res = await fetch('/api/chat', {
662
  method: 'POST',
663
  headers: { 'Content-Type': 'application/json' },
664
  body: JSON.stringify({
665
  message: userMessage,
666
- // Optional: pass context or selected program later
667
  meta: { page: 'chat.html', product: 'silentpattern' }
668
  })
669
  });
@@ -704,11 +674,9 @@
704
  setBusy(true);
705
 
706
  try {
707
- // Preferred path: server endpoint
708
  const reply = await callServerChat(message);
709
  addMessage(reply, false);
710
  } catch (err) {
711
- // Fallback: demo response (keeps UI usable during setup)
712
  addMessage(localDemoResponse(message), false);
713
  } finally {
714
  setBusy(false);
@@ -716,11 +684,15 @@
716
  });
717
 
718
  clearBtn.addEventListener('click', () => {
719
- // Keep the first system greeting, clear the rest
720
  const nodes = Array.from(chatMessages.children);
721
  for (let i = 1; i < nodes.length; i++) nodes[i].remove();
 
 
722
  transcript.length = 0;
723
- transcript.push({ role: 'system', content: 'Session cleared.', ts: Date.now() });
 
 
724
  addMessage('Session cleared.', false);
725
  });
726
 
@@ -742,15 +714,16 @@
742
  URL.revokeObjectURL(url);
743
  });
744
 
745
- /* -------------------------------------------------------------
746
- GLOBAL ESC: close whichever overlay is open
747
- ------------------------------------------------------------- */
748
  document.addEventListener('keydown', (e) => {
749
  if (e.key === 'Escape') {
750
  if (labNav && !labNav.classList.contains('modal-hidden')) closeLabNav();
751
  if (accessModal && !accessModal.classList.contains('modal-hidden')) toggleModal(accessModal, false);
752
  }
753
  });
 
 
 
754
  </script>
755
  </body>
756
  </html>
 
26
  color: transparent;
27
  }
28
 
 
 
 
 
29
  .conscious-element { transition: all 0.3s ease; }
30
  .conscious-element:hover { transform: scale(1.02); }
31
 
 
56
  .modal-hidden { opacity: 0; transform: translateY(20px); pointer-events: none; }
57
  .modal-visible { opacity: 1; transform: translateY(0); }
58
 
 
59
  .aura {
60
  background:
61
  radial-gradient(circle at 25% 15%, rgba(99,102,241,0.20), transparent 42%),
 
63
  radial-gradient(circle at 50% 45%, rgba(139,92,246,0.10), transparent 55%);
64
  }
65
 
 
66
  .focus-ring:focus { outline: none; box-shadow: 0 0 0 2px rgba(99,102,241,0.65); }
67
  </style>
68
  </head>
 
71
  <!-- Animated background -->
72
  <div id="vanta-bg" class="fixed top-0 left-0 w-full h-full z-0"></div>
73
 
74
+ <!-- Top bar -->
75
  <nav class="relative z-10 py-6 px-8 flex justify-between items-center backdrop-blur-sm">
76
  <a href="index.html" class="flex items-center space-x-2">
77
  <div class="w-8 h-8 rounded-full bg-indigo-600 flex items-center justify-center">
 
94
  </div>
95
  </nav>
96
 
97
+ <!-- Console -->
98
  <section class="relative z-10 px-6 py-16">
99
  <div class="max-w-5xl mx-auto">
100
  <div class="text-center mb-10">
 
112
  </p>
113
  </div>
114
 
 
115
  <div class="relative rounded-2xl border border-gray-800 bg-gray-900/30 overflow-hidden aura">
 
116
  <div class="bg-gray-800/40 px-6 py-4 border-b border-gray-800 flex items-center justify-between">
117
  <div class="flex items-center">
118
  <div class="w-3 h-3 rounded-full bg-red-500 mr-2"></div>
 
130
  </div>
131
  </div>
132
 
 
133
  <div class="text-xs px-2.5 py-1 rounded-full border border-indigo-500/30 text-indigo-200 bg-indigo-900/15">
134
  DRAFT
135
  </div>
136
  </div>
137
 
 
138
  <div id="chat-messages" class="chat-container p-6 space-y-4" aria-live="polite" aria-label="Chat messages">
139
+ <div class="flex items-start" data-seed="system">
140
  <div class="w-8 h-8 rounded-full bg-indigo-600 flex-shrink-0 flex items-center justify-center mr-3">
141
  <i class="fas fa-robot text-white text-sm"></i>
142
  </div>
143
  <div class="bg-gray-800/70 rounded-lg p-4 max-w-[85%]">
144
+ <p class="text-gray-100" id="seed-system-text">
145
  Acknowledged. This console is a controlled interface. State your objective; I will respond with constraints, assumptions, and next steps.
146
  </p>
147
  <p class="text-gray-400 text-xs mt-2">Note: do not paste secrets or API keys into chat.</p>
 
149
  </div>
150
  </div>
151
 
 
152
  <div class="px-6 py-4 border-t border-gray-800 bg-black/10">
153
  <form id="chat-form" class="flex items-center" autocomplete="off" aria-label="Send message">
154
  <input id="chat-input" type="text"
 
176
  </div>
177
  </div>
178
 
 
179
  <div class="mt-3 text-xs text-gray-600">
180
+ Integration note: do not call OpenAI directly from the browser. Use a server endpoint (example:
181
+ <span class="text-gray-400">/api/chat</span>) to keep keys private.
182
  </div>
183
  </div>
184
  </div>
185
  </div>
186
  </section>
187
 
 
188
  <footer class="relative z-10 px-6 pb-10">
189
  <div class="max-w-5xl mx-auto border-t border-gray-800/60 pt-8 flex flex-col md:flex-row justify-between items-center gap-4">
190
+ <div class="text-sm text-gray-500">© 2025 SILENTPATTERN. All rights reserved.</div>
 
 
191
  <div class="text-sm text-gray-500 flex gap-6">
192
  <a href="research.html" class="hover:text-indigo-400 transition">Research</a>
193
  <a href="privacy.html" class="hover:text-indigo-400 transition">Privacy</a>
 
197
  </div>
198
  </footer>
199
 
200
+ <!-- ACCESS MODAL -->
201
  <div id="access-modal"
202
  class="fixed inset-0 z-50 flex items-center justify-center bg-black/80 backdrop-blur-sm modal modal-hidden"
203
  role="dialog" aria-modal="true" aria-labelledby="access-modal-title" tabindex="-1">
 
217
  <form id="access-form" class="space-y-4">
218
  <div>
219
  <label for="name" class="block text-sm font-medium mb-1">Full Name</label>
220
+ <input type="text" id="name" class="w-full bg-gray-800/50 border border-gray-700 rounded-lg px-4 py-2 focus-ring">
 
221
  </div>
222
  <div>
223
  <label for="email" class="block text-sm font-medium mb-1">Email</label>
224
+ <input type="email" id="email" class="w-full bg-gray-800/50 border border-gray-700 rounded-lg px-4 py-2 focus-ring">
 
225
  </div>
226
  <div>
227
  <label for="institution" class="block text-sm font-medium mb-1">Institution/Organization</label>
228
+ <input type="text" id="institution" class="w-full bg-gray-800/50 border border-gray-700 rounded-lg px-4 py-2 focus-ring">
 
229
  </div>
230
  <div>
231
  <label for="purpose" class="block text-sm font-medium mb-1">Purpose of Access</label>
 
249
  </div>
250
  </div>
251
 
252
+ <!-- LAB NAVIGATOR -->
253
  <div id="lab-navigator"
254
  class="fixed inset-0 z-[60] bg-black/80 backdrop-blur-md modal modal-hidden"
255
  role="dialog" aria-modal="true" aria-label="Lab Navigator" tabindex="-1">
 
369
  </div>
370
 
371
  <script>
372
+ /* VANTA */
 
 
373
  const vantaEffect = VANTA.NET({
374
  el: "#vanta-bg",
375
  mouseControls: true,
 
387
  });
388
  window.addEventListener('resize', () => vantaEffect.resize());
389
 
390
+ /* MODAL ACCESSIBILITY */
 
 
391
  function trapFocus(modal) {
392
  const focusable = modal.querySelectorAll('a, button, input, select, textarea, [tabindex]:not([tabindex="-1"])');
393
  if (!focusable.length) return;
 
408
  modal.addEventListener('keydown', handler);
409
  modal._focusHandler = handler;
410
  }
 
411
  function untrapFocus(modal) {
412
  if (modal._focusHandler) {
413
  modal.removeEventListener('keydown', modal._focusHandler);
414
  delete modal._focusHandler;
415
  }
416
  }
 
417
  const toggleModal = (modal, show) => {
418
  if (show) {
419
  modal.classList.remove('modal-hidden');
 
428
  }
429
  };
430
 
431
+ /* ACCESS MODAL */
 
 
432
  const accessModal = document.getElementById('access-modal');
433
  const accessBtn = document.getElementById('access-btn');
434
  const closeAccessModal = document.getElementById('close-access-modal');
 
437
  toggleModal(accessModal, true);
438
  setTimeout(() => document.getElementById('name').focus(), 50);
439
  });
 
440
  closeAccessModal.addEventListener('click', () => toggleModal(accessModal, false));
441
+ accessModal.addEventListener('click', (e) => { if (e.target === accessModal) toggleModal(accessModal, false); });
 
 
 
442
 
443
  document.getElementById('access-form').addEventListener('submit', (e) => {
444
  e.preventDefault();
 
446
  const email = document.getElementById('email').value.trim();
447
  const institution = document.getElementById('institution').value.trim();
448
  const purpose = document.getElementById('purpose').value;
 
449
  if (!name || !email || !institution || !purpose) {
450
  alert('Please fill in all fields.');
451
  return;
 
455
  toggleModal(accessModal, false);
456
  });
457
 
458
+ /* LAB NAVIGATOR */
 
 
459
  const labNav = document.getElementById('lab-navigator');
460
  const labNavBtn = document.getElementById('lab-nav-btn');
461
  const labNavClose = document.getElementById('lab-nav-close');
 
465
 
466
  labNavBtn.addEventListener('click', openLabNav);
467
  labNavClose.addEventListener('click', closeLabNav);
 
468
  labNav.addEventListener('click', (e) => {
469
  const shouldClose = e.target && e.target.getAttribute('data-lab-close') === 'true';
470
  if (shouldClose) closeLabNav();
 
535
  function renderDossier(key) {
536
  const d = DOSSIERS[key];
537
  if (!d) return;
 
538
  dossierTitle.textContent = d.title;
539
  dossierSubtitle.textContent = d.subtitle;
540
  dossierStatus.textContent = d.status;
 
560
  btn.addEventListener('click', () => renderDossier(btn.getAttribute('data-dossier')));
561
  });
562
 
563
+ /* CHAT: secure-by-design client */
564
+ const API_ENDPOINT = "/api/chat"; // change if your deployment requires a prefix
565
+
 
 
 
566
  const chatForm = document.getElementById('chat-form');
567
  const chatInput = document.getElementById('chat-input');
568
  const chatMessages = document.getElementById('chat-messages');
 
573
 
574
  const transcript = []; // {role:'user'|'system', content:string, ts:number}
575
 
576
+ function seedTranscript() {
577
+ const seed = document.getElementById('seed-system-text');
578
+ if (seed && seed.textContent.trim()) {
579
+ transcript.push({ role: 'system', content: seed.textContent.trim(), ts: Date.now() });
580
+ }
581
+ }
582
+ seedTranscript();
583
+
584
  function escapeHtml(str) {
585
  return str
586
  .replaceAll('&', '&amp;')
 
629
  }
630
 
631
  async function callServerChat(userMessage) {
632
+ const res = await fetch(API_ENDPOINT, {
633
  method: 'POST',
634
  headers: { 'Content-Type': 'application/json' },
635
  body: JSON.stringify({
636
  message: userMessage,
 
637
  meta: { page: 'chat.html', product: 'silentpattern' }
638
  })
639
  });
 
674
  setBusy(true);
675
 
676
  try {
 
677
  const reply = await callServerChat(message);
678
  addMessage(reply, false);
679
  } catch (err) {
 
680
  addMessage(localDemoResponse(message), false);
681
  } finally {
682
  setBusy(false);
 
684
  });
685
 
686
  clearBtn.addEventListener('click', () => {
687
+ // Keep the seed greeting, remove everything after it
688
  const nodes = Array.from(chatMessages.children);
689
  for (let i = 1; i < nodes.length; i++) nodes[i].remove();
690
+
691
+ // Reset transcript to the seed system message only
692
  transcript.length = 0;
693
+ seedTranscript();
694
+
695
+ // Add a visible confirmation message (this will also be recorded)
696
  addMessage('Session cleared.', false);
697
  });
698
 
 
714
  URL.revokeObjectURL(url);
715
  });
716
 
717
+ /* GLOBAL ESC */
 
 
718
  document.addEventListener('keydown', (e) => {
719
  if (e.key === 'Escape') {
720
  if (labNav && !labNav.classList.contains('modal-hidden')) closeLabNav();
721
  if (accessModal && !accessModal.classList.contains('modal-hidden')) toggleModal(accessModal, false);
722
  }
723
  });
724
+
725
+ // Optional: preselect a dossier for the right pane
726
+ renderDossier('console');
727
  </script>
728
  </body>
729
  </html>