yo give code snippets for this bro

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <title>Ultimate AI Assistant - Extended 100+ Features</title>

  <!-- Core CSS/JS Libraries -->

  <!-- 1. Bootstrap 5 -->
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" />
  <script defer src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>

  <!-- 2. Animate.css -->
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/animate.css@4.1.1/animate.min.css" />

  <!-- 3. SweetAlert2 -->
  <script defer src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>

  <!-- 4. Chart.js -->
  <script defer src="https://cdn.jsdelivr.net/npm/chart.js@4.3.0/dist/chart.umd.min.js"></script>

  <!-- 5. Emoji Button -->
  <script defer src="https://cdn.jsdelivr.net/npm/@joeattardi/emoji-button@4.6.2/dist/index.min.js"></script>

  <!-- 6. Markdown-it -->
  <script defer src="https://cdn.jsdelivr.net/npm/markdown-it@13.0.1/dist/markdown-it.min.js"></script>

  <!-- 7. Highlight.js -->
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/highlight.js@11.8.0/styles/default.min.css" />
  <script defer src="https://cdn.jsdelivr.net/npm/highlight.js@11.8.0/lib/core.min.js"></script>

  <!-- 8. jQuery -->
  <script defer src="https://cdn.jsdelivr.net/npm/jquery@3.7.0/dist/jquery.min.js"></script>

  <!-- 9. Typed.js -->
  <script defer src="https://cdn.jsdelivr.net/npm/typed.js@2.0.12/lib/typed.min.js"></script>

  <!-- 10. Clipboard.js -->
  <script defer src="https://cdn.jsdelivr.net/npm/clipboard@2.0.11/dist/clipboard.min.js"></script>

  <!-- 11. Tippy.js -->
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/tippy.js@6.3.7/dist/tippy.css" />
  <script defer src="https://cdn.jsdelivr.net/npm/popper.js@2.11.6/dist/umd/popper.min.js"></script>
  <script defer src="https://cdn.jsdelivr.net/npm/tippy.js@6.3.7/dist/tippy-bundle.umd.min.js"></script>

  <!-- 12. Fuse.js -->
  <script defer src="https://cdn.jsdelivr.net/npm/fuse.js@6.6.2"></script>

  <!-- 13. Leaflet.js -->
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/leaflet@1.9.4/dist/leaflet.css" />
  <script defer src="https://cdn.jsdelivr.net/npm/leaflet@1.9.4/dist/leaflet.js"></script>

  <!-- 14. QRCode.js -->
  <script defer src="https://cdn.jsdelivr.net/npm/qrcode@1.5.1/build/qrcode.min.js"></script>

  <!-- 15. SortableJS -->
  <script defer src="https://cdn.jsdelivr.net/npm/sortablejs@1.15.0/Sortable.min.js"></script>

  <!-- 16. jsPDF -->
  <script defer src="https://cdn.jsdelivr.net/npm/jspdf@2.5.1/dist/jspdf.umd.min.js"></script>

  <!-- 17. JSZip -->
  <script defer src="https://cdn.jsdelivr.net/npm/jszip@3.10.1/dist/jszip.min.js"></script>

  <!-- 18. PapaParse -->
  <script defer src="https://cdn.jsdelivr.net/npm/papaparse@5.4.1/papaparse.min.js"></script>

  <!-- 19. Handlebars.js -->
  <script defer src="https://cdn.jsdelivr.net/npm/handlebars@4.7.7/dist/handlebars.min.js"></script>

  <!-- 20. Axios -->
  <script defer src="https://cdn.jsdelivr.net/npm/axios@1.3.5/dist/axios.min.js"></script>

  <!-- 21. Web Speech API Polyfill -->
  <script defer src="https://cdn.jsdelivr.net/npm/web-speech-cognitive-services@0.2.7/dist/web-speech-cognitive-services.min.js"></script>

  <!-- 22. DOMPurify -->
  <script defer src="https://cdn.jsdelivr.net/npm/dompurify@2.4.3/dist/purify.min.js"></script>

  <!-- 23. Mousetrap.js -->
  <script defer src="https://cdn.jsdelivr.net/npm/mousetrap@1.6.5/mousetrap.min.js"></script>

  <!-- 24. Day.js -->
  <script defer src="https://cdn.jsdelivr.net/npm/dayjs@1.11.9/dayjs.min.js"></script>

  <!-- 25. Luxon (date/time) -->
  <script defer src="https://cdn.jsdelivr.net/npm/luxon@3.3.0/build/global/luxon.min.js"></script>

  <!-- 26. Chartjs-plugin-datalabels -->
  <script defer src="https://cdn.jsdelivr.net/npm/chartjs-plugin-datalabels@2.2.0/dist/chartjs-plugin-datalabels.min.js"></script>

  <!-- 27. Perfect Scrollbar -->
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/perfect-scrollbar@1.5.5/css/perfect-scrollbar.css" />
  <script defer src="https://cdn.jsdelivr.net/npm/perfect-scrollbar@1.5.5/dist/perfect-scrollbar.min.js"></script>

  <!-- 28. Lodash -->
  <script defer src="https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js"></script>

  <!-- 29. Granim.js -->
  <script defer src="https://cdn.jsdelivr.net/npm/granim@2.0.0/dist/granim.min.js"></script>

  <!-- 30. Typed-Audio -->
  <script defer src="https://cdn.jsdelivr.net/npm/typed-audio@0.3.4/typed-audio.min.js"></script>

  <!-- 31. TensorFlow.js -->
  <script defer src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@4.8.0/dist/tf.min.js"></script>

  <!-- 32. ml5.js -->
  <script defer src="https://cdn.jsdelivr.net/npm/ml5@0.12.2/dist/ml5.min.js"></script>

  <!-- 33. Three.js -->
  <script defer src="https://cdn.jsdelivr.net/npm/three@0.154.0/build/three.min.js"></script>

  <style>
    /* Reset and base styles */
    * {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
    }

    body,
    html {
      height: 100%;
      background: #121212;
      color: #eee;
      font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
      user-select: none;
    }

    a {
      color: #67f;
      text-decoration: underline;
    }

    a:hover {
      color: #449;
    }

    #app {
      display: flex;
      height: 100vh;
      max-width: 100vw;
      overflow: hidden;
      user-select: text;
    }

    /* SIDEBAR */
    #sidebar {
      width: 380px;
      background: #1e1f29;
      border-right: 1px solid #384155;
      display: flex;
      flex-direction: column;
      user-select: none;
    }

    #sidebar header {
      font-size: 1.6rem;
      text-align: center;
      padding: 15px 10px;
      border-bottom: 2px solid #4a90e2;
      color: #52aaff;
      user-select: none;
      font-weight: 700;
      background: linear-gradient(90deg, #4158d0, #c850c0);
      background-clip: text;
      -webkit-background-clip: text;
      color: transparent;
      text-shadow: 1px 1px 3px rgba(0, 0, 0, 0.4);
    }

    #tabs {
      display: flex;
      flex-wrap: wrap;
      justify-content: space-around;
      border-bottom: 1px solid #384155;
      background: #293044;
      user-select: none;
      gap: 0.2rem;
      padding: 4px 6px;
      max-height: 48vh;
      overflow-y: auto;
    }

    .tabbtn {
      flex: 1 1 48%;
      padding: 10px 8px;
      border: none;
      cursor: pointer;
      font-weight: 700;
      color: #aaa;
      background: #293044;
      outline-offset: 0;
      transition: background-color 0.25s, color 0.25s;
      user-select: none;
      border-radius: 8px;
      font-size: 0.85rem;
      text-align: center;
    }

    .tabbtn:hover,
    .tabbtn:focus-visible {
      background: #4169e1;
      color: #fff;
      outline: none;
    }

    .tabbtn.active {
      background: #4a73ff;
      color: white;
      pointer-events: none;
      box-shadow: 0 0 8px #4a73ffaa;
    }

    .tabcontent {
      flex: 1 1 auto;
      overflow-y: auto;
      padding: 15px 18px;
      font-size: 14px;
      background: #1b1f2e;
      user-select: text;
      display: flex;
      flex-direction: column;
    }

    /* CHAT AREA */
    #chat-area {
      flex-grow: 1;
      display: flex;
      flex-direction: column;
      background: #141727;
      position: relative;
      user-select: text;
    }

    #msgs {
      flex-grow: 1;
      overflow-y: auto;
      padding: 20px 28px;
      font-size: 16px;
      line-height: 1.45em;
      scroll-behavior: smooth;
      background: linear-gradient(135deg, #161926 0%, #222b42 100%);
      font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
    }

    .msg {
      max-width: 70%;
      margin-bottom: 14px;
      padding: 12px 20px;
      border-radius: 16px;
      white-space: pre-wrap;
      word-wrap: break-word;
      border: 1px solid transparent;
      font-weight: 500;
      user-select: text;
    }

    .msg.user {
      background: #0f9b43dd;
      color: #d6efd6;
      margin-left: auto;
      border-bottom-right-radius: 0;
      border-color: #1acc65;
    }

    .msg.ai {
      background: #2f6eccbb;
      color: #dde9ff;
      border-bottom-left-radius: 0;
      border-color: #1f4697;
      font-style: normal;
    }

    /* Animations */
    .msg.ai.animate__animated {
      animation-name: fadeInUp;
      animation-duration: 0.7s;
      animation-fill-mode: both;
      animation-timing-function: cubic-bezier(.4, 0, .2, 1);
    }

    /* INPUT FORM */
    #input-form {
      border-top: 1px solid #293044;
      display: flex;
      background: #1e253b;
      padding: 12px 20px;
      align-items: center;
      gap: 0.6rem;
    }

    #user-input {
      flex-grow: 1;
      font-size: 17px;
      padding: 12px 16px;
      border-radius: 30px;
      background: #293046;
      border: none;
      color: #dde9ff;
      outline: none;
      resize: none;
      min-height: 36px;
      max-height: 80px;
    }

    #user-input:disabled {
      background: #1f273b;
      color: #607abfaa;
      cursor: not-allowed;
    }

    #send-btn {
      border: none;
      background: #4169e1;
      color: white;
      font-weight: 700;
      font-size: 17px;
      padding: 0 22px;
      border-radius: 30px;
      cursor: pointer;
      transition: background-color 0.3s;
      user-select: none;
      height: 40px;
    }

    #send-btn:hover:not(:disabled) {
      background: #548dff;
    }

    #send-btn:disabled {
      background: #667dbf66;
      cursor: default;
    }

    /* TYPING INDICATOR */
    #typing-indicator {
      margin: 6px 22px;
      font-style: italic;
      color: #7a90bf;
      display: flex;
      align-items: center;
      height: 24px;
      user-select: none;
    }

    .typing-dot {
      width: 8px;
      height: 8px;
      margin: 0 3px;
      background: #7a90bf;
      border-radius: 50%;
      display: inline-block;
      animation: bounce 1.4s infinite ease-in-out both;
    }

    .typing-dot:nth-child(1) {
      animation-delay: -0.32s;
    }

    .typing-dot:nth-child(2) {
      animation-delay: -0.16s;
    }

    .typing-dot:nth-child(3) {
      animation-delay: 0s;
    }

    @keyframes bounce {
      0%,
      80%,
      100% {
        transform: scale(0);
      }
      40% {
        transform: scale(1);
      }
    }

    /* PROXY SEARCH */
    #proxy-search-input {
      width: 70%;
      padding: 8px 14px;
      border-radius: 6px;
      border: none;
      outline: none;
      font-size: 14px;
      margin-bottom: 8px;
      flex-grow: 1;
    }

    #proxy-search-btn {
      padding: 8px 16px;
      font-weight: 700;
      font-size: 14px;
      border-radius: 6px;
      border: none;
      background-color: #4a73ff;
      color: white;
      cursor: pointer;
      transition: background-color 0.3s;
      margin-left: 6px;
      white-space: nowrap;
    }

    #proxy-search-btn:hover:not(:disabled) {
      background-color: #2669ff;
    }

    #proxy-search-results {
      max-height: 220px;
      overflow-y: auto;
      font-size: 14px;
      color: #ddefff;
      margin-top: 8px;
      user-select: text;
      line-height: 1.4em;
    }

    #proxy-search-results p {
      margin-bottom: 8px;
      cursor: pointer;
    }

    #proxy-iframe-wrapper {
      position: relative;
      flex-grow: 1;
      background: #000;
      border-top: 1px solid #4169e1;
      border-radius: 4px;
      overflow: hidden;
      box-shadow: inset 0 0 14px rgba(255, 255, 255, 0.1);
      margin-top: 8px;
      min-height: 50vh;
    }

    #proxy-iframe-wrapper iframe {
      width: 100%;
      height: 100%;
      border: none;
      background: #fff;
      display: block;
      border-radius: 4px;
    }

    #proxy-loading {
      position: absolute;
      top: 40%;
      left: 50%;
      transform: translate(-50%, -50%);
      color: #4a73ff;
      font-weight: 700;
      font-size: 20px;
      user-select: none;
      display: none;
      pointer-events: none;
    }

    /* QUIZ AREA */
    #quiz-area {
      white-space: pre-wrap;
      font-size: 14px;
      margin-top: 10px;
      background: #24324a;
      border-radius: 8px;
      padding: 12px;
      color: #e0e7ff;
      max-height: 220px;
      overflow-y: auto;
      user-select: text;
      font-family: monospace;
    }

    /* SETTINGS PANELS */
    .form-check-label,
    label {
      cursor: pointer;
      user-select: none;
    }

    .form-range {
      width: 100%;
    }

    /* Media Upload & Preview Tab Styles */
    #media-tab {
      flex-grow: 1;
      display: flex;
      flex-direction: column;
      gap: 8px;
    }

    #image-upload-label {
      background-color: #4169e1;
      padding: 10px;
      color: white;
      font-weight: 600;
      border-radius: 8px;
      text-align: center;
      cursor: pointer;
      user-select: none;
    }

    #image-upload-label:hover {
      background-color: #548dff;
    }

    #image-upload-input {
      display: none;
    }

    #image-preview-container {
      border: 1px solid #4169e1;
      border-radius: 8px;
      padding: 12px;
      background-color: #222b44;
      overflow-y: auto;
      max-height: 200px;
      user-select: text;
    }

    #image-preview-container img {
      max-width: 100%;
      border-radius: 8px;
      margin-bottom: 8px;
      box-shadow: 0 0 10px #4169e1aa;
    }

    /* HTML Editor Tab Styles */
    #html-editor-tab {
      flex-grow: 1;
      display: flex;
      flex-direction: column;
      gap: 6px;
    }

    #html-editor-textarea {
      width: 100%;
      height: 200px;
      border-radius: 8px;
      font-family: monospace;
      background-color: #222b44;
      color: #ddeeff;
      border: 1px solid #4a73ff;
      padding: 10px;
      resize: vertical;
    }

    #html-preview {
      flex-grow: 1;
      border-radius: 8px;
      background-color: #12141b;
      border: 1px solid #4a73ff;
      overflow-y: auto;
      padding: 12px;
      color: #ddeeff;
      min-height: 150px;
      user-select: text;
    }

    #html-toggle-preview {
      background-color: #4a73ff;
      color: white;
      border: none;
      padding: 6px 14px;
      border-radius: 6px;
      align-self: flex-start;
      cursor: pointer;
    }

    #html-toggle-preview:hover {
      background-color: #669dff;
    }

    /* Text editor tab (like note with markdown) */
    #note-editor {
      flex-grow: 1;
      width: 100%;
      resize: vertical;
      border-radius: 8px;
      line-height: 1.5em;
      padding: 14px 16px;
      font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
      font-size: 14px;
      background-color: #222b44;
      color: #ddeeff;
      border: 1px solid #4a73ff;
      min-height: 140px;
    }

    /* Dark Mode Overrides */
    body.light-mode {
      background: #f5f7fa;
      color: #222;
    }

    body.light-mode #sidebar {
      background: #e4ecff;
      color: #222;
      border-right-color: #c1c9d6;
    }

    body.light-mode #chat-area {
      background: #eef2ff;
      color: #222;
    }

    body.light-mode #msgs {
      background: linear-gradient(135deg, #fff 0%, #dbeafe 100%);
      color: #222;
    }

    body.light-mode .msg.user {
      background: #aff3afcc;
      color: #064e00;
    }

    body.light-mode .msg.ai {
      background: #8cc8ffcc;
      color: #02266f;
    }

    body.light-mode #input-form {
      background: #d6dbfb;
    }

    body.light-mode #user-input {
      background: #f5f7fa;
      color: #222;
    }

    body.light-mode #proxy-search-input {
      background: #fff;
      color: #222;
      border: 1px solid #ccc;
    }

    body.light-mode #proxy-search-results {
      color: #222;
    }

    body.light-mode a {
      color: #3366cc;
    }

    /* Scrollbars */
    ::-webkit-scrollbar {
      width: 8px;
      height: 8px;
    }

    ::-webkit-scrollbar-track {
      background: transparent;
    }

    ::-webkit-scrollbar-thumb {
      background: #4a73ff88;
      border-radius: 20px;
    }

    /* Responsive for smaller viewports */
    @media (max-width: 720px) {
      #app {
        flex-direction: column;
      }

      #sidebar {
        width: 100%;
        height: 50vh;
      }

      #chat-area {
        height: 50vh;
      }

      #tabs {
        flex-wrap: nowrap;
        overflow-x: auto;
      }

      .tabbtn {
        flex: 1 0 auto;
        font-size: 13px;
        padding: 8px 6px;
      }
    }
  </style>
</head>
<body>
  <div id="app" role="main" aria-label="Ultimate AI Assistant Extended">

    <!-- Sidebar: Control Panel with Extended Tabs -->
    <section id="sidebar" role="complementary" aria-label="Control panel">
      <header>Ultimate AI Assistant</header>
      <nav id="tabs" role="tablist" aria-label="Settings tabs">
        <button class="tabbtn active" role="tab" aria-selected="true" aria-controls="tab-ai" id="tab-btn-ai">AI Chat</button>
        <button class="tabbtn" role="tab" aria-selected="false" aria-controls="tab-data" id="tab-btn-data">Data APIs</button>
        <button class="tabbtn" role="tab" aria-selected="false" aria-controls="tab-quiz" id="tab-btn-quiz">Quiz Tools</button>
        <button class="tabbtn" role="tab" aria-selected="false" aria-controls="tab-search" id="tab-btn-search">Search Proxy</button>
        <button class="tabbtn" role="tab" aria-selected="false" aria-controls="tab-voice" id="tab-btn-voice">Voice Chat</button>
        <button class="tabbtn" role="tab" aria-selected="false" aria-controls="tab-export" id="tab-btn-export">Export Chat</button>
        <button class="tabbtn" role="tab" aria-selected="false" aria-controls="tab-media" id="tab-btn-media">Media Upload</button>
        <button class="tabbtn" role="tab" aria-selected="false" aria-controls="tab-html" id="tab-btn-html">HTML Editor</button>
        <button class="tabbtn" role="tab" aria-selected="false" aria-controls="tab-text" id="tab-btn-text">Text Notes</button>
        <!-- Additional tabs for extended features -->
        <button class="tabbtn" role="tab" aria-selected="false" aria-controls="tab-analytics" id="tab-btn-analytics">Analytics & Graphs</button>
        <button class="tabbtn" role="tab" aria-selected="false" aria-controls="tab-map" id="tab-btn-map">Geo & Mapping</button>
        <button class="tabbtn" role="tab" aria-selected="false" aria-controls="tab-code" id="tab-btn-code">Code Helper</button>
        <button class="tabbtn" role="tab" aria-selected="false" aria-controls="tab-tools" id="tab-btn-tools">Utility Tools</button>
        <button class="tabbtn" role="tab" aria-selected="false" aria-controls="tab-settings" id="tab-btn-settings">Advanced Settings</button>
      </nav>

      <!-- Tab contents -->
      <div id="tab-ai" class="tabcontent" role="tabpanel" aria-labelledby="tab-btn-ai">
        <h5>Personality</h5>
        <select id="personality-select" class="form-select form-select-sm" aria-label="Select personality tone">
          <option value="friendly" selected>Friendly & Simple</option>
          <option value="formal">Formal & Precise</option>
          <option value="funny">Funny & Casual</option>
          <option value="robotic">Robotic & Technical</option>
          <option value="friendly-funny">Friendly-Funny Mix</option>
          <option value="philosophical">Philosophical & Reflective</option>
          <option value="motivational">Motivational & Uplifting</option>
        </select>
        <div class="form-check my-2">
          <input class="form-check-input" type="checkbox" id="use-context" checked />
          <label class="form-check-label" for="use-context">Enable Context Awareness</label>
        </div>
        <div class="form-check my-2">
          <input class="form-check-input" type="checkbox" id="use-external" checked />
          <label class="form-check-label" for="use-external">Use Online Knowledge APIs</label>
        </div>
        <div class="form-check my-2">
          <input class="form-check-input" type="checkbox" id="spellcheck" checked />
          <label class="form-check-label" for="spellcheck">Enable Spell Correction</label>
        </div>
        <div class="form-check my-2">
          <input class="form-check-input" type="checkbox" id="auto-translate" />
          <label class="form-check-label" for="auto-translate">Auto Translate Responses (detect language)</label>
        </div>
        <div class="form-check my-2">
          <input class="form-check-input" type="checkbox" id="typing-indicator-checkbox" checked />
          <label class="form-check-label" for="typing-indicator-checkbox">Show Typing Indicator</label>
        </div>
        <h5 class="mt-4">Performance</h5>
        <label for="typing-speed" class="form-label">Typing Speed (ms per char): <span id="typing-speed-val">30</span></label>
        <input type="range" id="typing-speed" class="form-range" min="10" max="80" value="30" />
        <label for="context-length" class="form-label mt-3">Max Memory Steps: <span id="context-length-val">5</span></label>
        <input type="range" id="context-length" class="form-range" min="1" max="20" value="5" />
        <label for="max-response-words" class="form-label mt-3">Max Words in Response: <span id="max-response-words-val">200</span></label>
        <input type="range" id="max-response-words" class="form-range" min="20" max="500" value="200" />
        <h5 class="mt-4">User Experience</h5>
        <div class="form-check mb-3">
          <input class="form-check-input" type="checkbox" id="dark-mode" checked />
          <label class="form-check-label" for="dark-mode">Enable Dark Mode</label>
        </div>
        <label for="font-size" class="form-label">Font Size: <span id="font-size-val">16</span> px</label>
        <input type="range" id="font-size" class="form-range" min="12" max="24" value="16" />
        <div class="form-check mb-3">
          <input class="form-check-input" type="checkbox" id="enable-emoji" />
          <label class="form-check-label" for="enable-emoji">Translate Emotions to Emojis</label>
        </div>
        <button class="btn btn-danger w-100 mt-4" id="clear-chat">Clear Chat History</button>
      </div>

      <div id="tab-data" class="tabcontent" role="tabpanel" aria-labelledby="tab-btn-data" hidden>
        <h5>Free & Demo APIs Integrated:</h5>
        <ul class="list-group list-group-flush" style="font-size: 14px;">
          <li class="list-group-item bg-transparent">• DuckDuckGo Instant Answer API (Knowledge & Search)</li>
          <li class="list-group-item bg-transparent">• Wikipedia REST API (Article summaries)</li>
          <li class="list-group-item bg-transparent">• Numbers API (Trivia & facts about numbers)</li>
          <li class="list-group-item bg-transparent">• Open Trivia DB (Quiz questions)</li>
          <li class="list-group-item bg-transparent">• Free Dictionary API (Word definitions & synonyms)</li>
          <li class="list-group-item bg-transparent">• NewsAPI demo (Tech news headlines)</li>
          <li class="list-group-item bg-transparent">• Weather APIs (Open-Meteo) for current weather</li>
          <li class="list-group-item bg-transparent">• Currency conversion (exchangerate.host)</li>
          <li class="list-group-item bg-transparent">• Unsplash + Flickr image search integration</li>
          <li class="list-group-item bg-transparent">• YouTube API preview (via embeds)</li>
          <li class="list-group-item bg-transparent">• Google Translate API demo (for translation)</li>
          <li class="list-group-item bg-transparent">• Merriam-Webster Thesaurus & Dictionary (demo)</li>
          <li class="list-group-item bg-transparent">• Oxford Dictionaries API (demo)</li>
          <li class="list-group-item bg-transparent">• Wikipedia GeoSearch (Places info)</li>
          <li class="list-group-item bg-transparent">• TimezoneDB API (Timezone info)</li>
          <li class="list-group-item bg-transparent">• IP Geolocation API (Your IP info)</li>
          <li class="list-group-item bg-transparent">• QR Code generator & scanner</li>
          <li class="list-group-item bg-transparent">• OpenAI GPT Q&A (requires API key)</li>
          <li class="list-group-item bg-transparent">• Free Text-to-Speech & Speech-to-Text APIs</li>
          <li class="list-group-item bg-transparent">• Handlebars.js templates for dynamic UI</li>
          <li class="list-group-item bg-transparent">• ML-powered sentiment analysis (ml5.js)</li>
          <li class="list-group-item bg-transparent">• Image recognition (ml5.js MobileNet demo)</li>
          <li class="list-group-item bg-transparent">• 3D rendering via Three.js for AI data visualization</li>
          <li class="list-group-item bg-transparent">• Local NLP utilities (tfjs)</li>
          <!-- Extendable for more APIs -->
        </ul>
        <p class="mt-3" style="font-style: italic;">Easily add more by editing code - all modular & modern!</p>
      </div>

      <div id="tab-quiz" class="tabcontent" role="tabpanel" aria-labelledby="tab-btn-quiz" hidden>
        <h5>Quiz & Learning Tools</h5>
        <button type="button" id="start-quiz-btn" class="btn btn-primary btn-sm mb-3" aria-label="Start random quiz">Start Random Quiz</button>
        <div id="quiz-area" aria-live="polite" aria-atomic="true" tabindex="0"></div>
        <small class="text-muted mt-2 d-block">Correct answers increase your score!</small>
        <button type="button" id="start-math-quiz-btn" class="btn btn-success btn-sm mt-3">Start Math Quiz</button>
        <div id="math-quiz-area" style="white-space: pre-wrap; font-family: monospace; margin-top: 8px; max-height: 180px; overflow-y: auto; background:#2a344e; border-radius:8px; padding: 10px; color:#cfffbf;" tabindex="0"></div>
      </div>

      <div id="tab-search" class="tabcontent" role="tabpanel" aria-labelledby="tab-btn-search" hidden>
        <h5>Search Proxy Browser</h5>
        <div class="d-flex flex-wrap mb-2">
          <input 
            id="proxy-search-input" 
            type="search" 
            class="form-control me-2" 
            placeholder="Search DuckDuckGo or enter URL..." 
            aria-label="Proxy search or direct URL input" 
            style="flex-grow:1; min-width:200px" 
            spellcheck="false" 
            autocomplete="off"
          />
          <button id="proxy-search-btn" class="btn btn-primary mb-2" title="Search or Load URL" aria-label="Search or load URL">Search / Load</button>
        </div>
        <div id="proxy-search-results" aria-live="polite" aria-atomic="false" tabindex="0"></div>
        <div id="proxy-iframe-wrapper" aria-live="off">
          <iframe 
            id="proxy-iframe" 
            srcdoc="<p style='font-family:sans-serif;color:#666;text-align:center;padding:2rem;'>Search results or embedded website will show here.</p>" 
            title="Proxy Browser Frame" 
            sandbox="allow-same-origin allow-scripts allow-forms allow-modals allow-popups allow-popups-to-escape-sandbox" 
            loading="lazy" 
            frameborder="0" 
            tabindex="0"
            aria-label="Embedded browsing area"
          ></iframe>
          <div id="proxy-loading" aria-hidden="true">Loading...</div>
        </div>
      </div>

      <div id="tab-voice" class="tabcontent" role="tabpanel" aria-labelledby="tab-btn-voice" hidden>
        <h5>Voice Chat</h5>
        <p>Use mic button to speak to AI, and AI will talk back.</p>
        <button id="voice-start-btn" class="btn btn-success mb-3" aria-label="Start voice chat" >🎤 Start Voice Chat</button>
        <button id="voice-stop-btn" class="btn btn-danger mb-3" disabled aria-label="Stop voice chat">⏹ Stop Voice Chat</button>
        <div id="voice-text" style="background:#182135; padding:12px; border-radius:6px; min-height:36px; color:#8ab4f8; font-family: monospace;" aria-live="polite" aria-atomic="true" tabindex="0"></div>
      </div>

      <div id="tab-export" class="tabcontent" role="tabpanel" aria-labelledby="tab-btn-export" hidden>
        <h5>Export & Import Chat History</h5>
        <button id="export-json-btn" class="btn btn-outline-primary mb-2" aria-label="Export chat history JSON">Export as JSON</button>
        <button id="export-txt-btn" class="btn btn-outline-secondary mb-2" aria-label="Export chat history text">Export as Text</button>
        <button id="export-html-btn" class="btn btn-outline-success mb-2" aria-label="Export chat as HTML page">Export as HTML (.ai file)</button>
        <hr />
        <label for="import-json-input" class="form-label">Import chat from JSON file</label>
        <input type="file" id="import-json-input" accept="application/json" class="form-control mb-2" aria-label="Import chat JSON file" />
        <small class="form-text text-muted">Imported chat messages will replace current history.</small>
        <button id="import-html-btn" class="btn btn-outline-warning mt-3" aria-label="Import chat from HTML (.ai) file">Import .ai File</button>
        <input type="file" id="import-html-input" accept=".ai,.html" style="display:none" />
      </div>

      <div id="tab-media" class="tabcontent" role="tabpanel" aria-labelledby="tab-btn-media" hidden>
        <h5>Media Upload & AI Analysis</h5>
        <label id="image-upload-label" for="image-upload-input" tabindex="0" aria-controls="image-preview-container">Click or press Enter to upload images</label>
        <input type="file" id="image-upload-input" accept="image/*" multiple aria-describedby="upload-help" />
        <small id="upload-help" class="text-muted mb-2">Supports PNG, JPG, GIF. AI caption coming soon!</small>
        <div id="image-preview-container" aria-live="polite" aria-atomic="true" style="min-height:100px;">
          <p>No images uploaded yet.</p>
        </div>
        <hr />
        <button id="analyze-images-btn" class="btn btn-info" aria-label="Analyze uploaded images">Analyze Images (ML-powered)</button>
        <div id="image-analysis-results" style="margin-top:10px; font-size: 14px; min-height: 48px;" aria-live="polite" aria-atomic="true"></div>
      </div>

      <div id="tab-html" class="tabcontent" role="tabpanel" aria-labelledby="tab-btn-html" hidden>
        <h5>HTML Editor with Live Preview</h5>
        <textarea id="html-editor-textarea" placeholder="Write raw HTML here..." aria-label="HTML editor textarea"></textarea>
        <button id="html-toggle-preview" aria-pressed="false" aria-label="Toggle live HTML preview">Toggle Preview</button>
        <div id="html-preview" hidden aria-live="polite" aria-atomic="true" tabindex="0">Live preview will show here.</div>
      </div>

      <div id="tab-text" class="tabcontent" role="tabpanel" aria-labelledby="tab-btn-text" hidden>
        <h5>Notes & Markdown Editor</h5>
        <textarea id="note-editor" placeholder="Write notes or markdown here..." spellcheck="true" aria-label="Markdown notes editor"></textarea>
        <button id="render-markdown-btn" class="btn btn-secondary mt-2" aria-label="Render markdown preview">Render Markdown Preview</button>
        <div id="markdown-preview" style="margin-top: 10px; background:#212837; padding:12px; border-radius:8px; color:#ddd; max-height: 300px; overflow-y:auto;" aria-live="polite" aria-atomic="true" tabindex="0"></div>
      </div>

      <div id="tab-analytics" class="tabcontent" role="tabpanel" aria-labelledby="tab-btn-analytics" hidden>
        <h5>Analytics & Graphs</h5>
        <canvas id="analytics-chart" aria-label="Analytics data chart" role="img" style="max-height: 300px; background:#222d44; border-radius:8px;"></canvas>
        <button id="refresh-analytics-btn" class="btn btn-primary mt-3" aria-label="Refresh analytics data">Refresh Analytics</button>
        <small class="text-muted mt-2 d-block">Tracks user queries and AI response sentiment.</small>
      </div>

      <div id="tab-map" class="tabcontent" role="tabpanel" aria-labelledby="tab-btn-map" hidden>
        <h5>Geo & Mapping</h5>
        <input id="map-location-input" type="text" class="form-control mb-2" placeholder="Enter location to search" aria-label="Map location input" autocomplete="off"/>
        <button id="map-search-btn" class="btn btn-primary mb-3" aria-label="Search location on map">Search Location</button>
        <div id="map-container" style="height: 350px; border-radius:8px;" tabindex="0" aria-label="Map display"></div>
      </div>

      <div id="tab-code" class="tabcontent" role="tabpanel" aria-labelledby="tab-btn-code" hidden>
        <h5>Code Helper</h5>
        <textarea id="code-input" placeholder="Write or paste code here..." spellcheck="false" aria-label="Code input editor" style="width:100%; height: 160px; font-family: monospace; background:#222b44; color:#ddeeff; border-radius:8px; padding:10px;"></textarea>
        <button id="run-code-btn" class="btn btn-success mt-2" aria-label="Run JavaScript code">Run JS Code</button>
        <button id="format-code-btn" class="btn btn-secondary mt-2 ms-2" aria-label="Format JavaScript code">Format Code</button>
        <pre id="code-output" aria-live="polite" aria-atomic="true" style="background:#12141b; padding:10px; margin-top:8px; min-height: 100px; border-radius:8px; color:#aff;"></pre>
      </div>

      <div id="tab-tools" class="tabcontent" role="tabpanel" aria-labelledby="tab-btn-tools" hidden>
        <h5>Utility Tools</h5>
        <button id="generate-qr-btn" class="btn btn-info mb-2" aria-label="Generate QR Code from text input">Generate QR Code</button>
        <input id="qr-text-input" class="form-control mb-2" placeholder="Text/URL for QR Code" aria-label="QR code text input" />
        <canvas id="qr-canvas" style="border: 1px solid #4a73ff; border-radius: 8px; max-width: 100%; height: auto;"></canvas>
        <hr />
        <button id="generate-random-password-btn" class="btn btn-warning mb-2" aria-label="Generate random secure password">Generate Password</button>
        <input type="text" id="password-output" class="form-control mb-2" readonly aria-label="Generated password output"/>
        <button id="copy-password-btn" class="btn btn-outline-primary mb-2" aria-label="Copy password to clipboard">Copy Password</button>
        <hr />
        <button id="fetch-ip-btn" class="btn btn-primary" aria-label="Fetch client IP info">Get My IP Info</button>
        <pre id="ip-info-output" style="background:#12141b; margin-top: 8px; padding: 10px; min-height: 80px; border-radius:8px; color:#aff;" aria-live="polite" tabindex="0"></pre>
      </div>

      <div id="tab-settings" class="tabcontent" role="tabpanel" aria-labelledby="tab-btn-settings" hidden>
        <h5>Advanced Settings & Debug</h5>
        <div class="form-check">
          <input class="form-check-input" type="checkbox" id="enable-logging" />
          <label class="form-check-label" for="enable-logging">Enable Debug Logging</label>
        </div>
        <div class="form-check mt-2">
          <input class="form-check-input" type="checkbox" id="auto-clear-on-export" />
          <label class="form-check-label" for="auto-clear-on-export">Clear Chat on Export</label>
        </div>
        <label for="openai-key" class="form-label mt-3">OpenAI API Key (optional):</label>
        <input type="password" class="form-control" id="openai-key" autocomplete="off" placeholder="Paste API key here" aria-label="OpenAI API key input"/>
        <button id="save-openai-key-btn" class="btn btn-primary mt-2" aria-label="Save OpenAI API key">Save Key</button>
        <hr />
        <button id="clear-localstorage-btn" class="btn btn-danger mt-2" aria-label="Clear all saved data">Clear All Local Storage Data</button>
        <small class="text-muted mt-2 d-block">Advanced: reset all preferences and chat history.</small>
      </div>
    </section>

    <!-- Chat Area -->
    <section id="chat-area" role="main" aria-label="Chat conversation with AI">
      <div id="msgs" tabindex="0" aria-live="polite" aria-atomic="false" role="log"></div>
      <div id="typing-indicator" aria-live="assertive" aria-atomic="true" style="display:none;">
        AI is typing &nbsp;
        <span class="typing-dot"></span>
        <span class="typing-dot"></span>
        <span class="typing-dot"></span>
      </div>

      <form id="input-form" autocomplete="off" role="search" aria-label="Chat input form" style="padding:10px 20px; background:#1f263b; display:flex; align-items: center;">
        <textarea 
          id="user-input" 
          name="message" 
          placeholder="Ask me anything..." 
          aria-label="Message input" 
          autocomplete="off" 
          rows="1" 
          style="flex-grow:1; font-size:16px; padding: 12px 16px; border-radius: 30px; border:none; color:#ddeeff; background:#293046; resize:none;"
          spellcheck="true"
        ></textarea>
        <button id="send-btn" type="submit" aria-label="Send message"
          style="margin-left:12px; padding:0 20px; background:#3a70ff; border:none; color:white; font-weight:700; font-size:16px; border-radius:30px; cursor:pointer;">Send</button>
      </form>
    </section>

  </div>

  <script>
    window.addEventListener("load", () => {
      /////////////////////////////
      // --- DOM Elements ---
      /////////////////////////////
      // Tabs and pages
      const tabs = [...document.querySelectorAll(".tabbtn")];
      const tabContents = [...document.querySelectorAll(".tabcontent")];

      // Chat elements
      const msgs = document.getElementById("msgs");
      const inputForm = document.getElementById("input-form");
      const userInput = document.getElementById("user-input");
      const typingInd = document.getElementById("typing-indicator");
      const sendBtn = document.getElementById("send-btn");

      // Settings
      const personalitySelect = document.getElementById("personality-select");
      const useContextCheckbox = document.getElementById("use-context");
      const useExternalCheckbox = document.getElementById("use-external");
      const spellCheckCheckbox = document.getElementById("spellcheck");
      const typingIndicatorCheckbox = document.getElementById("typing-indicator-checkbox");
      const darkModeCheckbox = document.getElementById("dark-mode");
      const fontSizeSlider = document.getElementById("font-size");
      const fontSizeVal = document.getElementById("font-size-val");
      const typingSpeedSlider = document.getElementById("typing-speed");
      const typingSpeedVal = document.getElementById("typing-speed-val");
      const contextLengthSlider = document.getElementById("context-length");
      const contextLengthVal = document.getElementById("context-length-val");
      const clearChatBtn = document.getElementById("clear-chat");
      const maxResponseWordsSlider = document.getElementById("max-response-words");
      const maxResponseWordsVal = document.getElementById("max-response-words-val");
      const autoTranslateCheckbox = document.getElementById("auto-translate");
      const enableEmojiCheckbox = document.getElementById("enable-emoji");

      // Quiz tab elements
      const startQuizBtn = document.getElementById("start-quiz-btn");
      const quizArea = document.getElementById("quiz-area");
      const startMathQuizBtn = document.getElementById("start-math-quiz-btn");
      const mathQuizArea = document.getElementById("math-quiz-area");

      // Proxy tab elements
      const proxyInput = document.getElementById("proxy-search-input");
      const proxyBtn = document.getElementById("proxy-search-btn");
      const proxyResults = document.getElementById("proxy-search-results");
      const proxyIframeWrapper = document.getElementById("proxy-iframe-wrapper");
      const proxyIframe = document.getElementById("proxy-iframe");
      const proxyLoading = document.getElementById("proxy-loading");

      // Voice
      const voiceStartBtn = document.getElementById("voice-start-btn");
      const voiceStopBtn = document.getElementById("voice-stop-btn");
      const voiceText = document.getElementById("voice-text");

      // Export & Import
      const exportJsonBtn = document.getElementById("export-json-btn");
      const exportTxtBtn = document.getElementById("export-txt-btn");
      const exportHtmlBtn = document.getElementById("export-html-btn");
      const importJsonInput = document.getElementById("import-json-input");
      const importHtmlBtn = document.getElementById("import-html-btn");
      const importHtmlInput = document.getElementById("import-html-input");

      // Media
      const imageUploadInput = document.getElementById("image-upload-input");
      const imagePreviewContainer = document.getElementById("image-preview-container");
      const analyzeImagesBtn = document.getElementById("analyze-images-btn");
      const imageAnalysisResults = document.getElementById("image-analysis-results");

      // HTML editor
      const htmlEditorTextarea = document.getElementById("html-editor-textarea");
      const htmlPreview = document.getElementById("html-preview");
      const htmlTogglePreviewBtn = document.getElementById("html-toggle-preview");

      // Notes / Markdown
      const noteEditor = document.getElementById("note-editor");
      const renderMarkdownBtn = document.getElementById("render-markdown-btn");
      const markdownPreview = document.getElementById("markdown-preview");

      // Analytics
      const analyticsChartCtx = document.getElementById("analytics-chart")?.getContext("2d");
      const refreshAnalyticsBtn = document.getElementById("refresh-analytics-btn");

      // Map
      const mapLocationInput = document.getElementById("map-location-input");
      const mapSearchBtn = document.getElementById("map-search-btn");
      const mapContainer = document.getElementById("map-container");

      // Code helper
      const codeInput = document.getElementById("code-input");
      const runCodeBtn = document.getElementById("run-code-btn");
      const formatCodeBtn = document.getElementById("format-code-btn");
      const codeOutput = document.getElementById("code-output");

      // Tools tab
      const generateQrBtn = document.getElementById("generate-qr-btn");
      const qrTextInput = document.getElementById("qr-text-input");
      const qrCanvas = document.getElementById("qr-canvas");
      const generatePasswordBtn = document.getElementById("generate-random-password-btn");
      const passwordOutput = document.getElementById("password-output");
      const copyPasswordBtn = document.getElementById("copy-password-btn");
      const fetchIpBtn = document.getElementById("fetch-ip-btn");
      const ipInfoOutput = document.getElementById("ip-info-output");

      // Advanced settings
      const enableLoggingCheckbox = document.getElementById("enable-logging");
      const autoClearOnExportCheckbox = document.getElementById("auto-clear-on-export");
      const openaiKeyInput = document.getElementById("openai-key");
      const saveOpenaiKeyBtn = document.getElementById("save-openai-key-btn");
      const clearLocalStorageBtn = document.getElementById("clear-localstorage-btn");

      ////////////////////////////////
      // --- Global State / Variables
      ////////////////////////////////
      let settings = {
        personality: "friendly",
        useContext: true,
        useExternalData: true,
        enableSpellCheck: true,
        useTypingDelay: true,
        darkMode: true,
        fontSize: 16,
        typingSpeedMs: 30,
        maxMemorySteps: 5,
        maxResponseWords: 200,
        autoTranslate: false,
        enableEmoji: false,
        enableLogging: false,
        openaiKey: "",
        autoClearOnExport: false,
      };

      let chatMemory = [];
      let quizState = null;
      let mathQuizState = null;
      let userPreferences = {};

      let analyticsData = {
        totalQueries: 0,
        sentimentScores: [], // Store simple sentiment scores [-1,1]
        queriesByCategory: {}, // Tracking trivia, math, weather, etc
      };

      /////////////////////////////
      // --- Helper Functions ---
      /////////////////////////////
      function logDebug(...args) {
        if (settings.enableLogging) {
          console.debug("[DEBUG]", ...args);
        }
      }

      function sanitize(html) {
        return DOMPurify.sanitize(html);
      }

      function linkify(text) {
        const urlRegex = /(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/gi;
        return text.replace(urlRegex, (url) => {
          return `<a href="${url}" target="_blank" rel="noopener noreferrer">${url}</a>`;
        });
      }

      function truncateWords(text, maxWords) {
        const words = text.split(/\s+/);
        if (words.length <= maxWords) return text;
        return words.slice(0, maxWords).join(" ") + " ...";
      }

      function parseSentiment(text) {
        // Very basic sentiment estimation (positive: +1, neutral: 0, negative: -1)
        // Extensible with ML models if needed.
        const positives = ["good", "nice", "great", "love", "happy", "awesome", "nice", "correct", "well done", "thank"];
        const negatives = ["bad", "hate", "wrong", "sad", "fail", "awful", "no", "not"];
        let score = 0;
        const txt = text.toLowerCase();
        for (const p of positives) {
          if (txt.includes(p)) score += 1;
        }
        for (const n of negatives) {
          if (txt.includes(n)) score -= 1;
        }
        if (score > 0) return 1;
        if (score < 0) return -1;
        return 0;
      }

      // Improved spell correction dictionary (could expand or better use API)
      function spellCorrect(text) {
        const corrections = {
          adn: "and",
          teh: "the",
          beleive: "believe",
          realy: "really",
          recieve: "receive",
          exmaple: "example",
          langauge: "language",
          becuase: "because",
          definately: "definitely",
          seperate: "separate",
          occured: "occurred",
          unwant: "unwanted",
        };
        return text.replace(/\b\w+\b/g, (word) => corrections[word.toLowerCase()] || word);
      }

      // Shuffle array helper
      function shuffle(array) {
        for (let i = array.length - 1; i > 0; i--) {
          let j = Math.floor(Math.random() * (i + 1));
          [array[i], array[j]] = [array[j], array[i]];
        }
      }

      function saveChatMemory() {
        try {
          localStorage.setItem(
            "ultimateAIChatMem",
            JSON.stringify(chatMemory.slice(-settings.maxMemorySteps * 2))
          );
        } catch (e) {
          logDebug("Failed to save chat memory", e);
        }
      }

      function loadChatMemory() {
        try {
          const mem = localStorage.getItem("ultimateAIChatMem");
          if (mem) {
            chatMemory = JSON.parse(mem);
            for (const m of chatMemory) {
              appendMsg(m.text, m.role === "user" ? "user" : "ai", false);
            }
          }
        } catch (e) {
          logDebug("Failed to load chat memory", e);
        }
      }

      function savePreferences() {
        try {
          localStorage.setItem("ultimateAIUserPrefs", JSON.stringify(userPreferences));
        } catch {}
      }

      function loadPreferences() {
        try {
          const prefs = localStorage.getItem("ultimateAIUserPrefs");
          if (prefs) userPreferences = JSON.parse(prefs);
        } catch {}
      }

      function saveSettings() {
        try {
          localStorage.setItem("ultimateAISettings", JSON.stringify(settings));
        } catch {}
      }

      function loadSettings() {
        try {
          const stored = localStorage.getItem("ultimateAISettings");
          if (stored) Object.assign(settings, JSON.parse(stored));
        } catch {}
        applySettingsUI();
      }

      function applySettingsUI() {
        personalitySelect.value = settings.personality;
        useContextCheckbox.checked = settings.useContext;
        useExternalCheckbox.checked = settings.useExternalData;
        spellCheckCheckbox.checked = settings.enableSpellCheck;
        typingIndicatorCheckbox.checked = settings.useTypingDelay;
        darkModeCheckbox.checked = settings.darkMode;
        fontSizeSlider.value = settings.fontSize;
        fontSizeVal.textContent = settings.fontSize;
        typingSpeedSlider.value = settings.typingSpeedMs;
        typingSpeedVal.textContent = settings.typingSpeedMs;
        contextLengthSlider.value = settings.maxMemorySteps;
        contextLengthVal.textContent = settings.maxMemorySteps;
        maxResponseWordsSlider.value = settings.maxResponseWords;
        maxResponseWordsVal.textContent = settings.maxResponseWords;
        autoTranslateCheckbox.checked = settings.autoTranslate;
        enableEmojiCheckbox.checked = settings.enableEmoji;
        enableLoggingCheckbox.checked = settings.enableLogging;
        autoClearOnExportCheckbox.checked = settings.autoClearOnExport;
        openaiKeyInput.value = settings.openaiKey || "";
        applyAppearance();
      }

      function applyAppearance() {
        if (settings.darkMode) {
          document.body.classList.remove("light-mode");
        } else {
          document.body.classList.add("light-mode");
        }
        msgs.style.fontSize = settings.fontSize + "px";
        userInput.style.fontSize = settings.fontSize + "px";
      }

      function attachSettingsListeners() {
        personalitySelect.addEventListener("change", (e) => {
          settings.personality = e.target.value;
          saveSettings();
          savePreferences();
        });
        useContextCheckbox.addEventListener("change", (e) => {
          settings.useContext = e.target.checked;
          saveSettings();
        });
        useExternalCheckbox.addEventListener("change", (e) => {
          settings.useExternalData = e.target.checked;
          saveSettings();
        });
        spellCheckCheckbox.addEventListener("change", (e) => {
          settings.enableSpellCheck = e.target.checked;
          saveSettings();
        });
        typingIndicatorCheckbox.addEventListener("change", (e) => {
          settings.useTypingDelay = e.target.checked;
          saveSettings();
        });
        darkModeCheckbox.addEventListener("change", (e) => {
          settings.darkMode = e.target.checked;
          applyAppearance();
          saveSettings();
        });
        fontSizeSlider.addEventListener("input", (e) => {
          settings.fontSize = Number(e.target.value);
          fontSizeVal.textContent = settings.fontSize;
          applyAppearance();
          saveSettings();
        });
        typingSpeedSlider.addEventListener("input", (e) => {
          settings.typingSpeedMs = Number(e.target.value);
          typingSpeedVal.textContent = settings.typingSpeedMs;
          saveSettings();
        });
        contextLengthSlider.addEventListener("input", (e) => {
          settings.maxMemorySteps = Number(e.target.value);
          contextLengthVal.textContent = settings.maxMemorySteps;
          saveSettings();
        });
        maxResponseWordsSlider.addEventListener("input", (e) => {
          settings.maxResponseWords = Number(e.target.value);
          maxResponseWordsVal.textContent = settings.maxResponseWords;
          saveSettings();
        });
        autoTranslateCheckbox.addEventListener("change", (e) => {
          settings.autoTranslate = e.target.checked;
          saveSettings();
        });
        enableEmojiCheckbox.addEventListener("change", (e) => {
          settings.enableEmoji = e.target.checked;
          saveSettings();
        });
        enableLoggingCheckbox.addEventListener("change", (e) => {
          settings.enableLogging = e.target.checked;
          saveSettings();
        });
        autoClearOnExportCheckbox.addEventListener("change", (e) => {
          settings.autoClearOnExport = e.target.checked;
          saveSettings();
        });
        clearChatBtn.addEventListener("click", () => {
          if (confirm("Clear chat history permanently?")) {
            chatMemory = [];
            msgs.innerHTML = "";
            saveChatMemory();
            quizState = null;
            mathQuizState = null;
            quizArea.textContent = "";
            mathQuizArea.textContent = "";
          }
        });
        saveOpenaiKeyBtn.addEventListener("click", () => {
          settings.openaiKey = openaiKeyInput.value.trim();
          if (settings.openaiKey.length > 0) {
            Swal.fire("Success", "OpenAI API key saved locally.", "success");
          } else {
            Swal.fire("Info", "OpenAI key is cleared.", "info");
          }
          saveSettings();
        });
        clearLocalStorageBtn.addEventListener("click", () => {
          if (
            confirm(
              "This will clear all local storage data (chat, settings, preferences). Are you sure?"
            )
          ) {
            localStorage.clear();
            location.reload();
          }
        });
      }

      ////////////////////
      // Tabs navigation
      ////////////////////
      function initTabs() {
        tabs.forEach((btn) => {
          btn.addEventListener("click", () => {
            tabs.forEach((b) => {
              b.classList.remove("active");
              const tid = b.getAttribute("aria-controls");
              const tabc = document.getElementById(tid);
              if (tabc) {
                tabc.hidden = true;
                tabc.setAttribute("aria-hidden", "true");
              }
              b.setAttribute("aria-selected", "false");
            });
            btn.classList.add("active");
            const showid = btn.getAttribute("aria-controls");
            const showTab = document.getElementById(showid);
            if (showTab) {
              showTab.hidden = false;
              showTab.setAttribute("aria-hidden", "false");
            }
            btn.setAttribute("aria-selected", "true");
          });
        });
      }

      ////////////////////
      // Message display
      ////////////////////
      function appendMsg(text, role = "ai", saveAfter = true) {
        if (!text) return;
        if (settings.enableEmoji && role === "ai") {
          text = text.replace(/happy|great|love/gi, "😊").replace(/sad|bad|hate/gi, "😞");
        }
        // Truncate long reply for user experience, not saved
        if (role === "ai" && settings.maxResponseWords > 0) {
          text = truncateWords(text, settings.maxResponseWords);
        }
        const div = document.createElement("div");
        div.className =
          "msg " + (role === "user" ? "user" : "ai") + " animate__animated animate__fadeInUp";
        div.innerHTML = sanitize(linkify(text));
        msgs.appendChild(div);
        msgs.scrollTo({ top: msgs.scrollHeight, behavior: "smooth" });
        if (saveAfter) {
          addToMemory(role, text);
        }
      }

      function addToMemory(role, text) {
        chatMemory.push({ role, text });
        let maxLen = settings.maxMemorySteps * 2;
        if (chatMemory.length > maxLen) {
          chatMemory.splice(0, chatMemory.length - maxLen);
        }
        saveChatMemory();
      }

      function contextText() {
        if (!settings.useContext) return "";
        let relevant = chatMemory.slice(-settings.maxMemorySteps * 2);
        return relevant.map((m) => (m.role === "user" ? "User: " : "AI: ") + m.text).join("\n");
      }

      /////////////////////////////
      // AI Response Generators and APIs
      /////////////////////////////
      const OPENAI_API_KEY = ""; // will override from saved settings if provided

      // Helpers for OpenAI
      async function openaiChatCompletion(messages) {
        let apiKey = settings.openaiKey || OPENAI_API_KEY;
        if (!apiKey) return null;
        try {
          const res = await fetch("https://api.openai.com/v1/chat/completions", {
            method: "POST",
            headers: { 
              "Content-Type": "application/json",
              "Authorization": `Bearer ${apiKey}`
            },
            body: JSON.stringify({
              model: "gpt-3.5-turbo",
              messages: messages,
              max_tokens: 300,
              temperature: 0.7,
            }),
          });
          if (!res.ok) return null;
          const data = await res.json();
          if (data.choices && data.choices.length) {
            return data.choices[0].message.content.trim();
          }
          return null;
        } catch (e) {
          logDebug("OpenAI API error", e);
          return null;
        }
      }

      // Simple helper - translate text (reusing LibreTranslate)
      async function translateText(text, target = "en") {
        try {
          if (target === "en") return text; // no translation needed
          const res = await fetch("https://libretranslate.com/translate", {
            method: "POST",
            headers: { "Content-Type": "application/json" },
            body: JSON.stringify({ q: text, source: "en", target }),
          });
          if (!res.ok) return text;
          const data = await res.json();
          return data.translatedText || text;
        } catch {
          return text;
        }
      }

      // Detect language from text with a lightweight heuristic
      // (only checking if contains some accented letters or non-latin letters; for demo)
      function detectLanguage(text) {
        if (!text) return "en";
        // Very naive checks for demonstration
        if (/[áéíóúñü]/i.test(text)) return "es"; // Spanish demo
        if (/[а-яё]/i.test(text)) return "ru";
        if (/[äöüß]/i.test(text)) return "de";
        return "en";
      }

      // DuckDuckGo Instant Answer API
      async function duckDuckGoQuery(query) {
        try {
          const url = `https://api.duckduckgo.com/?q=${encodeURIComponent(
            query
          )}&format=json&skip_disambig=1&no_redirect=1&kl=us-en`;
          const res = await fetch(url);
          if (!res.ok) return null;
          return await res.json();
        } catch {
          return null;
        }
      }

      // Wikipedia summary API
      async function wikiSummary(query) {
        try {
          const url = `https://en.wikipedia.org/api/rest_v1/page/summary/${encodeURIComponent(
            query
          )}`;
          const res = await fetch(url);
          if (!res.ok) return null;
          const data = await res.json();
          if (data.extract) {
            let link =
              data.content_urls?.desktop?.page ||
              `https://en.wikipedia.org/wiki/${encodeURIComponent(query)}`;
            return `${data.extract}\n\n<a href="${link}" target="_blank" rel="noopener noreferrer">More on Wikipedia</a>`;
          }
          return null;
        } catch {
          return null;
        }
      }

      // Numbers API trivia fetch
      async function numbersFact(num) {
        try {
          const res = await fetch(`https://numbersapi.com/${num}/trivia?json`);
          if (!res.ok) return null;
          const data = await res.json();
          return data.text || null;
        } catch {
          return null;
        }
      }

      // Open Trivia DB random question
      async function openTrivia() {
        try {
          const res = await fetch(
            "https://opentdb.com/api.php?amount=1&type=multiple&difficulty=easy"
          );
          if (!res.ok) return null;
          const data = await res.json();
          if (!data.results || !data.results.length) return null;
          return data.results[0];
        } catch {
          return null;
        }
      }

      // Open-Meteo weather API demo
      async function fetchWeather(city = "London") {
        try {
          const geo = await fetch(
            `https://geocoding-api.open-meteo.com/v1/search?name=${encodeURIComponent(
              city
            )}&count=1`
          );
          const geoData = await geo.json();
          if (!geoData.results || !geoData.results.length) return null;
          const lat = geoData.results[0].latitude;
          const lon = geoData.results[0].longitude;
          const weatherRes = await fetch(
            `https://api.open-meteo.com/v1/forecast?latitude=${lat}&longitude=${lon}&current_weather=true`
          );
          if (!weatherRes.ok) return null;
          const weatherData = await weatherRes.json();
          if (!weatherData.current_weather) return null;
          return `Current temperature in ${city}: ${weatherData.current_weather.temperature}°C, wind speed: ${weatherData.current_weather.windspeed} km/h`;
        } catch {
          return null;
        }
      }

      // Currency exchange (EXR host)
      async function currencyConvert(from = "USD", to = "EUR") {
        try {
          const url = `https://api.exchangerate.host/convert?from=${from.toUpperCase()}&to=${to.toUpperCase()}`;
          const res = await fetch(url);
          if (!res.ok) return null;
          const data = await res.json();
          if (!data.result) return null;
          return `1 ${from.toUpperCase()} = ${data.result.toFixed(4)} ${to.toUpperCase()}`;
        } catch {
          return null;
        }
      }

      // Grammar check demo (LanguageTool)
      async function grammarCheck(text) {
        try {
          const res = await fetch("https://api.languagetool.org/v2/check", {
            method: "POST",
            headers: { "Content-Type": "application/x-www-form-urlencoded" },
            body: `text=${encodeURIComponent(text)}&language=en-US`,
          });
          if (!res.ok) return null;
          const data = await res.json();
          if (data.matches && data.matches.length) {
            return data.matches
              .map(
                (m) =>
                  `Issue: ${m.message} at ${m.offset}-${m.offset + m.length}`
              )
              .join("\n");
          }
          return "No grammar errors found.";
        } catch {
          return null;
        }
      }

      // OpenAI powered chat fallback or advanced conversational AI handling
      async function askOpenAI(prompt) {
        if (!settings.openaiKey) return null;

        const messages = [
          {
            role: "system",
            content: `You are an AI assistant with a "${settings.personality}" personality. Keep responses concise and useful.`,
          },
          ...chatMemory.map(m => ({
            role: m.role === 'user' ? 'user' : 'assistant',
            content: m.text
          })),
          { role: "user", content: prompt },
        ];

        // Limit messages length < 20 to avoid API issues
        if (messages.length > 20) messages.splice(0, messages.length - 20);

        return await openaiChatCompletion(messages);
      }

      /////////////////////////////
      // Generate AI reply logic
      /////////////////////////////
      async function generateReply(input) {
        let text = input.trim();
        if (settings.enableSpellCheck) text = spellCorrect(text);

        // Quiz answering first
        if (quizState) {
          const choice = Number(text);
          if (!isNaN(choice) && choice >= 1 && choice <= quizState.options.length) {
            const answer = quizState.options[choice - 1];
            if (answer === quizState.correct_answer) {
              quizState = null;
              quizArea.textContent = "";
              return "Correct! Nice job. Ask me for another quiz anytime.";
            } else {
              return "Oops, wrong answer. Try again or type 'skip' to skip the quiz.";
            }
          }
          if (text.toLowerCase() === "skip") {
            quizState = null;
            quizArea.textContent = "";
            return "Quiz skipped. Ask me anything else or start a new quiz.";
          }
          return "Please respond with the option number or type 'skip'.";
        }

        // Math quiz mode
        if (mathQuizState) {
          if (text.toLowerCase() === "skip") {
            mathQuizState = null;
            mathQuizArea.textContent = "";
            return "Math quiz skipped. Let's try something else!";
          }
          try {
            const userAns = Number(text);
            if (isNaN(userAns)) return "Please provide a valid number answer or type 'skip'.";

            if (Math.abs(userAns - mathQuizState.answer) < 0.01) {
              mathQuizState = null;
              mathQuizArea.textContent = "";
              return "Correct! Well done.";
            } else {
              return "That's not correct. Try again or type 'skip'.";
            }
          } catch {
            return "Error processing your answer. Try again or type 'skip'.";
          }
        }

        // Greetings
        if (/^(hello|hi|hey|yo|hola|greetings)/i.test(text)) {
          switch (settings.personality) {
            case "friendly": return "Hello! Ask me anything—from trivia to math or coding.";
            case "formal": return "Good day. Please ask your inquiry.";
            case "funny": return "Yo! Ready to rock some knowledge?";
            case "robotic": return "Greetings, human. Awaiting commands.";
            case "friendly-funny": return "Hey buddy! Brain juice incoming!";
            case "philosophical": return "Greetings. What wisdom do you seek today?";
            case "motivational": return "Hey! Let's achieve greatness together!";
          }
        }

        // Math evaluation
        const mathResult = parseMath(text);
        if (mathResult !== null) return `The answer is ${mathResult}.`;

        const spellMatch = text.match(/\bspell\s+(\w+)/i);
        if (spellMatch) {
          const w = spellMatch[1].toUpperCase();
          return `${w.split("").join("-")} spells ${spellMatch[1]}.`;
        }

        const numbMatch = text.match(/number fact for (\d+)/i);
        if (numbMatch) {
          const fact = await numbersFact(numbMatch[1]);
          if (fact) return `Did you know? ${fact}`;
        }

        if (/\b(quiz|trivia|question)\b/i.test(text)) {
          const trivia = await openTrivia();
          if (trivia) {
            let options = [...trivia.incorrect_answers, trivia.correct_answer];
            options = options.map(s => s.replace(/&quot;/g, '"')
              .replace(/&#039;/g, "'")
              .replace(/&amp;/g, "&"));
            shuffle(options);
            quizState = {
              question: trivia.question,
              correct_answer: trivia.correct_answer,
              options,
            };
            let msg = `Trivia question:\n${quizState.question}\nOptions:\n`;
            options.forEach((opt, i) => {
              msg += `${i + 1}: ${opt}\n`;
            });
            msg += '\nType your answer option number or "skip".';
            quizArea.textContent = msg;
            return msg;
          }
        }

        // Math Quiz start command
        if (/^start math quiz$/i.test(text)) {
          mathQuizState = generateMathQuestion();
          mathQuizArea.textContent = `Math Quiz:\n${mathQuizState.question}\nAnswer as a number or type 'skip' to cancel.`;
          return "Math quiz started! Check the Math Quiz panel.";
        }

        // Spell word query (extended)
        if (/^define\s+(\w+)/i.test(text)) {
          let word = text.match(/^define\s+(\w+)/i)[1];
          try {
            const def = await fetchFreeDictionary(word);
            if (def) return def;
          } catch {}
          return `Sorry, I couldn't find the definition of "${word}".`;
        }

        // API external queries
        if (settings.useExternalData) {
          // DuckDuckGo instant answer
          const ddgData = await duckDuckGoQuery(text);
          if (ddgData && ddgData.AbstractText && ddgData.AbstractText.trim().length > 3) {
            let ans = ddgData.AbstractText;
            if (ddgData.AbstractURL) {
              ans += `\n\n<a href="${ddgData.AbstractURL}" target="_blank" rel="noopener">Read more on DuckDuckGo</a>`;
            }
            return ans;
          }

          // Wikipedia fallback
          const wiki = await wikiSummary(text);
          if (wiki) return wiki;

          // Weather query
          const weatherMatch = text.match(/weather in (.+)/i);
          if (weatherMatch) {
            const weatherInfo = await fetchWeather(weatherMatch[1]);
            if (weatherInfo) return weatherInfo;
          }

          // Currency convert "convert 100 usd to eur"
          const currencyMatch = text.match(
            /convert\s+(\d+(?:\.\d+)?)\s+(\w{3})\s+to\s+(\w{3})/i
          );
          if (currencyMatch) {
            const v = Number(currencyMatch[1]);
            const from = currencyMatch[2];
            const to = currencyMatch[3];
            const rate = await currencyConvert(from, to);
            if (rate) {
              const rateValue = Number(rate.split('=')[1].trim().split(' ')[0]);
              if (!isNaN(rateValue)) {
                return `${v} ${from.toUpperCase()} is approx ${(rateValue * v).toFixed(4)} ${to.toUpperCase()} \n(Rate: ${rate})`;
              }
              return rate;
            }
          }

          // Grammar check if command starts with "check grammar:"
          const grammarMatch = text.match(/^check grammar:\s*(.+)$/i);
          if (grammarMatch) {
            const checkRes = await grammarCheck(grammarMatch[1]);
            if (checkRes) return checkRes;
          }
        }

        // Fallback dictionary definition
        const fallbackQA = {
          "how many continents are there": "There are 7 continents on Earth.",
          "what color do you get when you mix red and blue":
            "You get purple.",
          "who was abraham lincoln":
            "He was the 16th President of the United States.",
          "what is the largest planet": "Jupiter is the largest planet.",
          "how many legs does a spider have": "A spider has 8 legs.",
          "chemical formula of water": "H₂O is the chemical formula of water.",
          "what is photosynthesis":
            "Photosynthesis is the process plants use to turn sunlight into energy.",
          "who is the president of the united states": "Joe Biden",
        };
        const key = text.toLowerCase().replace(/[?.!]/g, "").trim();
        if (fallbackQA[key]) return fallbackQA[key];

        // Try OpenAI integration if API key provided
        if (settings.openaiKey) {
          const response = await askOpenAI(text);
          if (response) return response;
        }

        // Personality fallback
        const fallbackMsgs = {
          friendly:
            "Hmm... I don't know that one. Try asking me a math question or trivia!",
          formal:
            "Apologies, the information is unavailable. Please ask something else.",
          funny:
            "I can't help you with that... maybe take a snack break and try again!",
          robotic:
            "Processing incomplete. Cannot compute response.",
          "friendly-funny":
            "Whoa, that's beyond me. Let's keep it simple, friend!",
          philosophical:
            "Sometimes not knowing is the greatest wisdom. What else can I assist with?",
          motivational:
            "Don't be discouraged! Ask me anything else and we'll find an answer.",
        };
        return fallbackMsgs[settings.personality] || fallbackMsgs.friendly;
      }

      /////////////////////////////
      // Math parse from input
      /////////////////////////////
      function parseMath(text) {
        let expr = text.toLowerCase().replace(/[^0-9+\-*/()., ]/g, "").replace(/,/g, "");
        try {
          let val = eval(expr);
          if (typeof val === "number" && isFinite(val)) return val;
        } catch {}
        return null;
      }

      /////////////////////////////
      // Free Dictionary API fetch
      /////////////////////////////
      async function fetchFreeDictionary(word) {
        try {
          const res = await fetch(`https://api.dictionaryapi.dev/api/v2/entries/en/${word}`);
          if (!res.ok) return null;
          const data = await res.json();
          if (Array.isArray(data) && data.length > 0) {
            const defs = data[0]?.meanings
              ?.map((m) => `${m.partOfSpeech}: ${m.definitions[0].definition}`)
              .join("; ");
            return `Definition of "${word}": ${defs}`;
          }
          return null;
        } catch {
          return null;
        }
      }

      /////////////////////////////
      // Generate math quiz question
      /////////////////////////////
      function generateMathQuestion() {
        const ops = ["+", "-", "*", "/"];
        const op = ops[Math.floor(Math.random() * ops.length)];
        let a = Math.floor(Math.random() * 50) + 1;
        let b = Math.floor(Math.random() * 50) + 1;

        if (op === "/") {
          a = a * b; // ensures divisible
        }
        const question = `Calculate: ${a} ${op} ${b}`;
        let answer;
        switch (op) {
          case "+": answer = a + b; break;
          case "-": answer = a - b; break;
          case "*": answer = a * b; break;
          case "/": answer = a / b; break;
        }
        return { question, answer };
      }

      ///////////////////////
      // Typing indicator + bot reply display
      ///////////////////////
      async function botReplyWithTyping(text) {
        if (!settings.useTypingDelay) {
          appendMsg(text, "ai");
          return;
        }
        typingInd.style.display = "flex";
        sendBtn.disabled = true;
        userInput.disabled = true;

        const delay = Math.max(700, text.length * settings.typingSpeedMs);
        await new Promise((r) => setTimeout(r, delay));

        typingInd.style.display = "none";
        appendMsg(text, "ai");
        sendBtn.disabled = false;
        userInput.disabled = false;
        userInput.focus();
      }

      ////////////////////////
      // Chat form submit handler
      ////////////////////////
      inputForm.addEventListener("submit", async (e) => {
        e.preventDefault();
        let val = userInput.value.trim();
        if (!val) return;

        appendMsg(val, "user");
        addToMemory("user", val);
        userInput.value = "";
        userInput.disabled = true;

        try {
          // Special command: image caption or analyze request
          if (/^(analyze|caption) image$/i.test(val)) {
            if (uploadedImages.length) {
              await botReplyWithTyping(
                "Analyzing your uploaded images with ML models..."
              );
              // ML image analysis demo
              analyzeUploadedImages();
            } else {
              await botReplyWithTyping(
                "No images uploaded yet. Please upload images in the Media tab."
              );
            }
            userInput.disabled = false;
            userInput.focus();
            return;
          }

          // Start math quiz command
          if (/^start math quiz$/i.test(val)) {
            if (mathQuizState) {
              Swal.fire("Info", "Math quiz already in progress. Please answer or type 'skip'.", "info");
              userInput.disabled = false;
              userInput.focus();
              return;
            }
            mathQuizState = generateMathQuestion();
            mathQuizArea.textContent = `Math Quiz:\n${mathQuizState.question}\nAnswer as a number or type 'skip' to cancel.`;
            await botReplyWithTyping("Math quiz started. Check the Math Quiz panel.");
            userInput.disabled = false;
            userInput.focus();
            return;
          }

          // Start random quiz command
          if (/^(start )?quiz$/i.test(val)) {
            const trivia = await openTrivia();
            if (trivia) {
              let options = [...trivia.incorrect_answers, trivia.correct_answer];
              options = options.map(s => s.replace(/&quot;/g, '"')
                .replace(/&#039;/g, "'")
                .replace(/&amp;/g, "&"));
              shuffle(options);
              quizState = {
                question: trivia.question,
                correct_answer: trivia.correct_answer,
                options,
              };
              let msg = `Trivia question:\n${quizState.question}\nOptions:\n`;
              options.forEach((opt, i) => {
                msg += `${i + 1}: ${opt}\n`;
              });
              msg += '\nType your answer option number or "skip".';
              quizArea.textContent = msg;
              userInput.disabled = false;
              userInput.focus();
              return;
            }
          }

          let reply = await generateReply(val);

          // Auto translation if enabled and user language detected different from en
          if (settings.autoTranslate) {
            const userLang = detectLanguage(val);
            if (userLang !== "en") {
              const translated = await translateText(reply, userLang);
              reply = translated;
            }
          }

          await botReplyWithTyping(reply);

          // Sentiment tracking for analytics
          const s = parseSentiment(reply);
          analyticsData.sentimentScores.push(s);
          analyticsData.totalQueries++;
          // Simple categorization (very light)
          if (/quiz|trivia|question/.test(val.toLowerCase())) {
            analyticsData.queriesByCategory["quiz"] = (analyticsData.queriesByCategory["quiz"] || 0) + 1;
          } else if (/\d/.test(val) && /calculate|solve|math|plus|minus|times|divide|\+|\-|\*|\//i.test(val)) {
            analyticsData.queriesByCategory["math"] = (analyticsData.queriesByCategory["math"] || 0) + 1;
          } else {
            analyticsData.queriesByCategory["other"] = (analyticsData.queriesByCategory["other"] || 0) + 1;
          }

          addToMemory("ai", reply);

          // Track queries in preferences for "learning"
          userPreferences.lastPersonality = settings.personality;
          userPreferences.lastQuery = val;
          savePreferences();

        } catch (ex) {
          console.error(ex);
          await botReplyWithTyping(
            "Sorry, something went wrong. Please try again."
          );
        }

        userInput.disabled = false;
        userInput.focus();
      });

      ////////////////////////////////
      // --- Proxy Search Handling ---
      ////////////////////////////////
      proxyBtn.addEventListener("click", async () => {
        const q = proxyInput.value.trim();
        if (!q) return;

        // Detect if it's URL-like: starts with http(s) or has a dot and no spaces
        const isUrl = /^(https?:\/\/)/i.test(q) || (/^[^\s]+\.[^\s]+$/.test(q) && !/\s/.test(q));

        if (isUrl) {
          let urlToLoad = q;
          if (!/^https?:\/\//i.test(q)) {
            // Add https if no protocol
            urlToLoad = "https://" + q;
          }
          loadInIframe(urlToLoad);
          proxyResults.innerHTML = "";
          return;
        }

        proxyResults.innerHTML =
          'Searching <span class="spinner-border spinner-border-sm"></span>';
        proxyIframe.srcdoc =
          "<p style='font-family:sans-serif;color:#666;text-align:center;padding:2rem;'>Search results or embedded website will show here.</p>";
        proxyIframe.style.display = "none";
        proxyLoading.style.display = "block";

        try {
          const ddgData = await duckDuckGoQuery(q);
          if (ddgData && ddgData.RelatedTopics && ddgData.RelatedTopics.length) {
            proxyResults.innerHTML = "";
            for (const item of ddgData.RelatedTopics.slice(0, 10)) {
              if (item.Text && item.FirstURL) {
                const p = document.createElement("p");
                p.innerHTML = `<a href="${item.FirstURL}" target="_blank" rel="noopener noreferrer">${item.Text}</a>`;
                p.style.cursor = "pointer";
                p.setAttribute('tabindex', '0');
                p.addEventListener("click", (e) => {
                  e.preventDefault();
                  loadInIframe(item.FirstURL);
                });
                p.addEventListener("keydown", (e) => {
                  if (e.key === "Enter" || e.key === " ") {
                    e.preventDefault();
                    loadInIframe(item.FirstURL);
                  }
                });
                proxyResults.appendChild(p);
              } else if (item.Topics) {
                item.Topics.slice(0, 3).forEach((topic) => {
                  const p = document.createElement("p");
                  p.innerHTML = `<a href="${topic.FirstURL}" target="_blank" rel="noopener noreferrer">${topic.Text}</a>`;
                  p.style.cursor = "pointer";
                  p.setAttribute('tabindex', '0');
                  p.addEventListener("click", (e) => {
                    e.preventDefault();
                    loadInIframe(topic.FirstURL);
                  });
                  p.addEventListener("keydown", (e) => {
                    if (e.key === "Enter" || e.key === " ") {
                      e.preventDefault();
                      loadInIframe(topic.FirstURL);
                    }
                  });
                  proxyResults.appendChild(p);
                });
              }
            }
            proxyLoading.style.display = "none";
            proxyIframe.style.display = "none";
          } else {
            proxyResults.textContent = "No results found.";
            proxyLoading.style.display = "none";
            proxyIframe.style.display = "none";
          }
        } catch (error) {
          proxyResults.textContent = "Error during search.";
          proxyLoading.style.display = "none";
          proxyIframe.style.display = "none";
        }
      });

      function loadInIframe(url) {
        showProxyLoading(true);
        proxyIframe.src = url;
        proxyResults.innerHTML = "";
        proxyIframe.style.display = "block";

        proxyIframe.onload = () => {
          showProxyLoading(false);
        };
        proxyIframe.onerror = () => {
          proxyLoading.style.display = "none";
          proxyIframe.style.display = "none";
          proxyResults.textContent = "Failed to load page in proxy iframe.";
        };
      }

      function showProxyLoading(show = true) {
        proxyLoading.style.display = show ? "block" : "none";
        proxyIframe.style.display = show ? "none" : "block";
      }

      /////////////////////////////
      // --- Quiz Tools ---
      /////////////////////////////
      startQuizBtn.addEventListener("click", async () => {
        quizArea.textContent = "Loading quiz...";
        const trivia = await openTrivia();
        if (!trivia) {
          quizArea.textContent = "Failed to load question. Try again later.";
          return;
        }
        let options = [...trivia.incorrect_answers, trivia.correct_answer].map(
          s => s.replace(/&quot;/g, '"')
            .replace(/&#039;/g, "'")
            .replace(/&amp;/g, "&")
        );
        shuffle(options);
        quizState = {
          question: trivia.question,
          correct_answer: trivia.correct_answer,
          options,
        };
        let msg = `Trivia question:\n${quizState.question}\nOptions:\n`;
        quizState.options.forEach((o, i) => (msg += `${i + 1}: ${o}\n`));
        msg += `\nType your answer option number in chat or 'skip'.`;
        quizArea.textContent = msg;
      });

      startMathQuizBtn.addEventListener("click", () => {
        if (mathQuizState) {
          Swal.fire("Info", "Math quiz already in progress. Please answer or type 'skip'.", "info");
          return;
        }
        mathQuizState = generateMathQuestion();
        mathQuizArea.textContent = `Math Quiz:\n${mathQuizState.question}\nAnswer as a number or type 'skip' to cancel.`;
      });

      /////////////////////////////
      // --- Voice chat ---
      /////////////////////////////

      let recognition = null;
      let speechSynthesisObj = window.speechSynthesis;
      voiceStopBtn.disabled = true;

      if ("webkitSpeechRecognition" in window || "SpeechRecognition" in window) {
        const SpeechRecognition =
          window.SpeechRecognition || window.webkitSpeechRecognition;
        recognition = new SpeechRecognition();
        recognition.lang = "en-US";
        recognition.interimResults = false;
        recognition.maxAlternatives = 1;

        recognition.onresult = (evt) => {
          const spoken = evt.results[0][0].transcript;
          voiceText.textContent = "You: " + spoken;
          appendMsg(spoken, "user");
          addToMemory("user", spoken);
          replyViaAI(spoken);
        };
        recognition.onerror = (evt) => {
          voiceText.textContent = "Speech recognition error: " + evt.error;
          voiceStopBtn.disabled = true;
          voiceStartBtn.disabled = false;
        };
        recognition.onend = () => {
          voiceText.textContent += " (Stopped)";
          voiceStopBtn.disabled = true;
          voiceStartBtn.disabled = false;
        };
      } else {
        voiceText.textContent = "Speech recognition not supported in this browser.";
        voiceStartBtn.disabled = true;
      }

      async function replyViaAI(text) {
        try {
          const reply = await generateReply(text);
          await botReplyWithTyping(reply);
          addToMemory("ai", reply);
          if (speechSynthesisObj) {
            const utterance = new SpeechSynthesisUtterance(reply);
            utterance.lang = "en-US";
            speechSynthesisObj.speak(utterance);
          }
        } catch {
          await botReplyWithTyping("Sorry, I couldn't process your voice input.");
        }
      }

      voiceStartBtn.addEventListener("click", () => {
        if (recognition) {
          recognition.start();
          voiceText.textContent = "Listening... Speak now";
          voiceStartBtn.disabled = true;
          voiceStopBtn.disabled = false;
        }
      });
      voiceStopBtn.addEventListener("click", () => {
        if (recognition) {
          recognition.stop();
          voiceStartBtn.disabled = false;
          voiceStopBtn.disabled = true;
        }
      });

      /////////////////////////////////
      // --- Export/Import Chat ---
      /////////////////////////////////
      exportJsonBtn.addEventListener("click", () => {
        if (chatMemory.length === 0) {
          Swal.fire("Info", "No chat history to export.", "info");
          return;
        }
        const dataStr = JSON.stringify(chatMemory, null, 2);
        const blob = new Blob([dataStr], { type: "application/json" });
        const url = URL.createObjectURL(blob);
        const a = document.createElement("a");
        a.href = url;
        a.download = `chat-history-${Date.now()}.json`;
        a.click();
        URL.revokeObjectURL(url);
        if (settings.autoClearOnExport) {
          clearChatBtn.click();
        }
      });

      exportTxtBtn.addEventListener("click", () => {
        if (chatMemory.length === 0) {
          Swal.fire("Info", "No chat history to export.", "info");
          return;
        }
        const txt = chatMemory
          .map((m) => `${m.role.toUpperCase()}:\n${m.text}\n`)
          .join("\n");
        const blob = new Blob([txt], { type: "text/plain" });
        const url = URL.createObjectURL(blob);
        const a = document.createElement("a");
        a.href = url;
        a.download = `chat-history-${Date.now()}.txt`;
        a.click();
        URL.revokeObjectURL(url);
        if (settings.autoClearOnExport) {
          clearChatBtn.click();
        }
      });

      // Export as a single HTML .ai file that contains chat memory and app state (embeds entire conversation)
      exportHtmlBtn.addEventListener("click", () => {
        if (chatMemory.length === 0) {
          Swal.fire("Info", "No chat history to export.", "info");
          return;
        }
        const aiData = {
          chatMemory,
          settings,
          userPreferences,
        };
        const blobContent = `<!-- Ultimate AI Assistant Data Embed -->\n` +
          `<script type="application/json" id="ultimate-ai-data">\n${JSON.stringify(aiData)}\n</script>\n` +
          `<h1>Exported Ultimate AI Chat History</h1>\n` +
          chatMemory.map(m => `<p><strong>${m.role.toUpperCase()}:</strong> ${m.text.replace(/\n/g, '<br>')}</p>`).join('');

        const htmlBlob = new Blob([
          `<!DOCTYPE html><html lang="en"><head><title>Ultimate AI Assistant Export</title>
          <meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1" />
          <style>body{background:#121212;color:#eee;font-family:sans-serif;padding:1rem;}h1{color:#4a73ff;}</style>
          </head><body>\n${blobContent}\n</body></html>`
        ], { type: "text/html" });

        const url = URL.createObjectURL(htmlBlob);
        const a = document.createElement("a");
        a.href = url;
        a.download = `ultimate-ai-chat-export-${Date.now()}.ai`;
        a.click();
        URL.revokeObjectURL(url);
        if (settings.autoClearOnExport) {
          clearChatBtn.click();
        }
      });

      importJsonInput.addEventListener("change", (e) => {
        const file = e.target.files[0];
        if (!file) return;
        const reader = new FileReader();
        reader.onload = (evt) => {
          try {
            const mem = JSON.parse(evt.target.result);
            if (!Array.isArray(mem)) throw new Error("Invalid format");
            chatMemory = mem;
            quizState = null;
            mathQuizState = null;
            msgs.innerHTML = "";
            chatMemory.forEach((m) => appendMsg(m.text, m.role === "user" ? "user" : "ai", false));
            saveChatMemory();
            quizArea.textContent = "";
            mathQuizArea.textContent = "";
            Swal.fire("Success", "Chat history imported successfully.", "success");
            importJsonInput.value = "";
          } catch {
            Swal.fire("Error", "Invalid JSON file.", "error");
          }
        };
        reader.readAsText(file);
      });

      importHtmlBtn.addEventListener("click", () => {
        importHtmlInput.click();
      });

      importHtmlInput.addEventListener("change", (e) => {
        const file = e.target.files[0];
        if (!file) return;
        const reader = new FileReader();
        reader.onload = (evt) => {
          try {
            // Extract JSON data embedded in special <script id="ultimate-ai-data"> inside HTML
            const htmlContent = evt.target.result;
            const match = htmlContent.match(/<script[^>]*id=["']ultimate-ai-data["'][^>]*>([\s\S]*?)<\/script>/);
            if (!match || !match[1]) throw new Error("No AI data found in file");

            const aiData = JSON.parse(match[1]);
            if (!aiData.chatMemory || !Array.isArray(aiData.chatMemory)) throw new Error("Invalid chat data");

            chatMemory = aiData.chatMemory;
            quizState = null;
            mathQuizState = null;
            settings = Object.assign(settings, aiData.settings || {});
            userPreferences = aiData.userPreferences || {};
            msgs.innerHTML = "";
            chatMemory.forEach((m) => appendMsg(m.text, m.role === "user" ? "user" : "ai", false));
            saveChatMemory();
            applySettingsUI();
            quizArea.textContent = "";
            mathQuizArea.textContent = "";
            Swal.fire("Success", "Chat history imported from .ai file.", "success");
            importHtmlInput.value = "";
          } catch (err) {
            Swal.fire("Error", "Invalid or corrupted .ai file.", "error");
          }
        };
        reader.readAsText(file);
      });

      /////////////////////////////
      // --- Media Upload & ML Image Analysis ---
      /////////////////////////////
      let uploadedImages = [];

      imageUploadInput.addEventListener("change", (e) => {
        const files = Array.from(e.target.files);
        if (!files.length) return;
        for (const file of files) {
          if (!file.type.startsWith("image/")) continue;
          const reader = new FileReader();
          reader.onload = (event) => {
            const imgUrl = event.target.result;
            uploadedImages.push({ file, url: imgUrl });
            renderImagePreviews();
          };
          reader.readAsDataURL(file);
        }
        e.target.value = "";
      });

      function renderImagePreviews() {
        if (!uploadedImages.length) {
          imagePreviewContainer.innerHTML = "<p>No images uploaded yet.</p>";
          return;
        }
        const content = uploadedImages
          .map(
            (img, i) =>
              `<div tabindex="0" aria-label="Uploaded image ${i + 1}" style="margin-bottom:12px;">
                <img src="${img.url}" alt="Uploaded image ${i + 1}" />
                <button 
                  aria-label="Remove image ${i + 1}" 
                  data-index="${i}" 
                  class="btn btn-sm btn-outline-danger mt-1 remove-img-btn" 
                  type="button"
                >Remove</button>
              </div>`
          )
          .join("");
        imagePreviewContainer.innerHTML = content;
        [...imagePreviewContainer.querySelectorAll(".remove-img-btn")].forEach((btn) => {
          btn.addEventListener("click", (ev) => {
            const idx = Number(ev.target.dataset.index);
            uploadedImages.splice(idx, 1);
            renderImagePreviews();
          });
        });
      }

      analyzeImagesBtn.addEventListener("click", () => {
        if (uploadedImages.length === 0) {
          imageAnalysisResults.textContent = "No images to analyze. Please upload first.";
          return;
        }
        imageAnalysisResults.textContent = "Analyzing images...";
        analyzeUploadedImages();
      });

      async function analyzeUploadedImages() {
        if (typeof ml5 === "undefined" || !ml5.imageClassifier) {
          imageAnalysisResults.textContent = "Image analysis unavailable (ml5.js not loaded).";
          return;
        }
        try {
          const classifier = await ml5.imageClassifier("MobileNet");
          let resultsSummary = "";
          for (const imgObj of uploadedImages) {
            // Create image element
            const img = new Image();
            img.src = imgObj.url;
            await new Promise(res => {
              if (img.complete) res();
              else img.onload = res;
              img.onerror = res;
            });
            const results = await classifier.classify(img);
            if (results.length > 0) {
              resultsSummary += `Image: ${imgObj.file.name} - Top Prediction: ${results[0].label} (${(results[0].confidence * 100).toFixed(1)}%)\n`;
            } else {
              resultsSummary += `Image: ${imgObj.file.name} - No prediction available.\n`;
            }
          }
          imageAnalysisResults.textContent = resultsSummary.trim();
          addToMemory("ai", "Image analysis results:\n" + resultsSummary.trim());
          appendMsg("Image analysis results:\n" + resultsSummary.trim(), "ai");
        } catch (error) {
          imageAnalysisResults.textContent = "Error analyzing images.";
          logDebug("Image analysis error:", error);
        }
      }

      /////////////////////////////
      // HTML Editor and Preview
      /////////////////////////////
      htmlTogglePreviewBtn.addEventListener("click", () => {
        const showing = htmlPreview.hasAttribute("hidden") === false;
        if (showing) {
          htmlPreview.setAttribute("hidden", "");
          htmlTogglePreviewBtn.setAttribute("aria-pressed", "false");
          htmlTogglePreviewBtn.textContent = "Toggle Preview";
        } else {
          htmlPreview.removeAttribute("hidden");
          htmlTogglePreviewBtn.setAttribute("aria-pressed", "true");
          htmlTogglePreviewBtn.textContent = "Hide Preview";
          updateHtmlPreview();
        }
      });

      htmlEditorTextarea.addEventListener("input", () => {
        if (!htmlPreview.hasAttribute("hidden")) updateHtmlPreview();
      });

      function updateHtmlPreview() {
        htmlPreview.innerHTML = sanitize(htmlEditorTextarea.value) || "<i>Nothing to preview</i>";
      }

      /////////////////////////////
      // Markdown Notes Editor and Preview
      /////////////////////////////
      renderMarkdownBtn.addEventListener("click", () => {
        const mdText = noteEditor.value;
        const md = window.markdownit ? window.markdownit() : null;
        if (!md) {
          markdownPreview.innerHTML = "<i>Markdown-it library not loaded.</i>";
          return;
        }
        const rendered = md.render(mdText);
        markdownPreview.innerHTML = sanitize(rendered);
      });

      noteEditor.addEventListener("input", () => {
        try {
          localStorage.setItem("ultimateAINotes", noteEditor.value);
        } catch {}
      });

      function loadNotes() {
        try {
          const val = localStorage.getItem("ultimateAINotes");
          if (val) noteEditor.value = val;
        } catch {}
      }

      /////////////////////////////
      // Analytics & Graphs (Chart.js)
      /////////////////////////////
      let analyticsChart = null;

      function renderAnalytics() {
        if (!analyticsChartCtx) return;
        const categories = Object.keys(analyticsData.queriesByCategory);
        const counts = categories.map(c => analyticsData.queriesByCategory[c]);
        const sentimentAvg = analyticsData.sentimentScores.length
          ? (analyticsData.sentimentScores.reduce((a,b) => a+b,0) / analyticsData.sentimentScores.length).toFixed(2)
          : "0";

        const data = {
          labels: categories,
          datasets: [
            {
              label: "Queries Count",
              data: counts,
              backgroundColor: "rgba(74, 115, 255, 0.7)",
            },
          ],
        };

        const config = {
          type: "bar",
          data: data,
          options: {
            scales: {
              y: { beginAtZero: true },
            },
            plugins: {
              legend: { display: false },
              title: {
                display: true,
                text: `Total queries: ${analyticsData.totalQueries}, Avg Sentiment: ${sentimentAvg}`,
                color: "#ddd",
                font: { size: 16 },
              },
            },
          },
        };

        if (analyticsChart) {
          analyticsChart.destroy();
        }
        analyticsChart = new Chart(analyticsChartCtx, config);
      }

      refreshAnalyticsBtn?.addEventListener("click", () => {
        renderAnalytics();
      });

      /////////////////////////////
      // Leaflet.js Map integration
      /////////////////////////////
      let map = null;
      let mapMarker = null;

      function initMap() {
        if (!mapContainer) return;
        map = L.map("map-container").setView([51.505, -0.09], 13);
        L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
          maxZoom: 19,
          attribution: '© OpenStreetMap',
        }).addTo(map);
      }

      async function searchLocationAndSetMarker(location) {
        if (!location) return;

        const url = `https://nominatim.openstreetmap.org/search?format=json&q=${encodeURIComponent(location)}`;
        try {
          const res = await fetch(url);
          const results = await res.json();
          if (results.length > 0) {
            const loc = results[0];
            const lat = parseFloat(loc.lat);
            const lon = parseFloat(loc.lon);
            map.setView([lat, lon], 13);
            if (mapMarker) map.removeLayer(mapMarker);
            mapMarker = L.marker([lat, lon]).addTo(map).bindPopup(loc.display_name).openPopup();
          } else {
            Swal.fire("No results", "Location not found.", "info");
          }
        } catch (e) {
          Swal.fire("Error", "Failed to search location.", "error");
        }
      }

      mapSearchBtn?.addEventListener("click", () => {
        const loc = mapLocationInput.value.trim();
        if (loc) searchLocationAndSetMarker(loc);
      });

      /////////////////////////////
      // Code helper: Run and formatting
      /////////////////////////////
      runCodeBtn?.addEventListener("click", () => {
        try {
          let output = undefined;
          try {
            output = eval(codeInput.value);
          } catch (innerErr) {
            output = "Error: " + innerErr.message;
          }
          if (typeof output === "object" && output !== null) output = JSON.stringify(output, null, 2);
          codeOutput.textContent = String(output);
        } catch (e) {
          codeOutput.textContent = "Error: " + e.message;
        }
      });

      formatCodeBtn?.addEventListener("click", () => {
        try {
          if (typeof prettier === "undefined") {
            codeOutput.textContent = "Prettier library not loaded.";
            return;
          }
          const formatted = prettier.format(codeInput.value, { parser: "babel" });
          codeInput.value = formatted;
        } catch (e) {
          codeOutput.textContent = "Formatting error: " + e.message;
        }
      });

      /////////////////////////////
      // Utility Tools
      /////////////////////////////
      generateQrBtn?.addEventListener("click", () => {
        const text = qrTextInput.value.trim();
        if (!text) {
          Swal.fire("Info", "Please enter text or URL to generate QR code.", "info");
          return;
        }
        qrCanvas.width = 180;
        qrCanvas.height = 180;
        QRCode.toCanvas(qrCanvas, text, { width: 180 }, (error) => {
          if (error) {
            qrCanvas.getContext("2d").clearRect(0, 0, qrCanvas.width, qrCanvas.height);
            Swal.fire("Error", "Failed to generate QR code.", "error");
          }
        });
      });


      function randomPassword(length = 12) {
        const chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()_+[]{}|;:,.<>?";
        let pass = "";
        for (let i = 0; i < length; i++) {
          pass += chars.charAt(Math.floor(Math.random() * chars.length));
        }
        return pass;
      }

      generatePasswordBtn?.addEventListener("click", () => {
        passwordOutput.value = randomPassword(16);
      });

      copyPasswordBtn?.addEventListener("click", () => {
        if (!passwordOutput.value) {
          Swal.fire("Info", "No password to copy.", "info");
          return;
        }
        if (navigator.clipboard && window.isSecureContext) {
          navigator.clipboard.writeText(passwordOutput.value).then(() => {
            Swal.fire("Copied!", "Password copied to clipboard.", "success");
          }).catch(() => {
            fallbackCopy();
          });
        } else {
          fallbackCopy();
        }
        function fallbackCopy() {
          passwordOutput.select();
          try {
            document.execCommand("copy");
            Swal.fire("Copied!", "Password copied to clipboard.", "success");
          } catch {
            Swal.fire("Error", "Failed to copy password.", "error");
          }
          window.getSelection().removeAllRanges();
          passwordOutput.blur();
        }
      });

      fetchIpBtn?.addEventListener("click", async () => {
        ipInfoOutput.textContent = "Fetching IP info...";
        try {
          const res = await fetch("https://ipapi.co/json/");
          if (!res.ok) throw new Error("Network error");
          const data = await res.json();
          ipInfoOutput.textContent = JSON.stringify(data, null, 2);
        } catch (e) {
          ipInfoOutput.textContent = "Failed to fetch IP info.";
        }
      });

      /////////////////////////////
      // Load from settings/preferences/notes/localStorage
      /////////////////////////////

      /////////////////////////////
      // Attach keyboard shortcuts (Ctrl+1..9 alphabetical for tabs)
      /////////////////////////////
      (() => {
        const tabKeys = {
          1: "tab-ai",
          2: "tab-data",
          3: "tab-quiz",
          4: "tab-search",
          5: "tab-voice",
          6: "tab-export",
          7: "tab-media",
          8: "tab-html",
          9: "tab-text",
          0: "tab-analytics",
          "-": "tab-map",
          "=": "tab-code",
          "[": "tab-tools",
          "]": "tab-settings",
        };
        Object.entries(tabKeys).forEach(([key, id]) => {
          Mousetrap.bind("ctrl+" + key, (e) => {
            e.preventDefault();
            document.querySelector(`button[aria-controls="${id}"]`)?.click();
          });
        });
      })();

      /////////////////////////////
      // Initialization
      /////////////////////////////
      function init() {
        initTabs();
        loadSettings();
        attachSettingsListeners();
        loadChatMemory();
        loadPreferences();
        loadNotes();
        applySettingsUI();
        renderImagePreviews();
        if (mapContainer) initMap();
        renderAnalytics();

        userInput.focus();
      }

      init();
    });
  </script>
  <!-- Include prettier (optional) for code formatting -->
  <script defer src="https://cdn.jsdelivr.net/npm/prettier@2.8.8/standalone.js"></script>
  <script defer src="https://cdn.jsdelivr.net/npm/prettier@2.8.8/parser-babel.js"></script>
</body>
</html>

SyntaxError: Unexpected end of input
SyntaxError: Unexpected identifier 'HTML'
null
null
null
webgl backend was already registered. Reusing existing backend factory.
cpu backend was already registered. Reusing existing backend factory.
Platform browser has already been set. Overwriting the platform with browser.
null
Scripts "build/three.js" and "build/three.min.js" are deprecated with r150+, and will be removed with r160. Please use ES Modules or alternatives: https://threejs.org/docs/index.html#manual/en/introduction/Installation