DEV Community

Cover image for A very basis chat app with Ollama
artydev
artydev

Posted on

A very basis chat app with Ollama

#ai
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8" />
  <title>Artydev Coding Terminal</title>

  <!-- External Stylesheets -->
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@webtui/css/dist/base.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@webtui/theme-catppuccin/dist/catppuccin.css">

  <style>
    body {
      margin: 0;
      background: #0b0f14;
      font-family: monospace;
      height: 100vh;
      display: flex;
      flex-direction: column;
      overflow: hidden;
    }

    /* --- FILLED BLOCK ASCII HEADER (CENTERED) --- */
    .banner-header {
      background: #0e121a;
      border-bottom: 1px solid rgba(255, 255, 255, 0.04);
      padding: 14px 16px 10px 16px;
      user-select: none;
      display: flex;
      flex-direction: column;
      align-items: center; 
      text-align: center;
    }
    .ascii-title {
      margin: 0;
      font-family: monospace;
      white-space: pre;
      line-height: 1.1;
      font-size: 11px;
      color: #89b4fa; /* Catppuccin Blue */
      text-shadow: 0 0 10px rgba(137, 180, 250, 0.25);
    }
    .sub-tagline {
      margin-top: 6px;
      font-size: 11px;
      color: #585b70;
    }

    /* --- TMUX STYLE MULTIPLEXER BAR --- */
    .session-bar {
      background: #11151c;
      border-bottom: 1px solid rgba(255, 255, 255, 0.08);
      padding: 6px 12px;
      display: flex;
      gap: 15px;
      font-size: 13px;
      user-select: none;
      align-items: center;
      overflow-x: auto;
    }

    .session-wrapper {
      display: inline-flex;
      align-items: center;
      gap: 4px;
      color: #7f849c;
    }

    .session-tab {
      cursor: pointer;
      white-space: nowrap;
    }

    .session-tab.active {
      color: #a6e3a1; /* Catppuccin Green */
      font-weight: bold;
    }

    .session-tab.active::before { content: "["; color: #7aa2f7; }
    .session-tab.active::after { content: "]*"; color: #7aa2f7; }

    /* Inline Rename Editor Box Styling */
    .rename-input {
      background: #1e1e2e;
      border: 1px solid #7aa2f7;
      color: #a6e3a1;
      font-family: monospace;
      font-size: 13px;
      padding: 0 4px;
      width: 100px;
      outline: none;
    }

    /* Subtle Delete Action Elements */
    .delete-btn {
      color: #585b70;
      cursor: pointer;
      font-size: 11px;
      padding: 0 2px;
      transition: color 0.15s ease;
      display: none; 
    }
    .session-wrapper:hover .delete-btn {
      display: inline-block;
    }
    .delete-btn:hover {
      color: #f38ba8 !important; 
    }

    .bar-actions-group {
      margin-left: auto;
      display: flex;
      gap: 16px;
      align-items: center;
    }

    /* Unintrusive Model Selector UX */
    .model-selector-wrapper {
      display: flex;
      align-items: center;
      gap: 6px;
      color: #94e2d5; /* Teal styling */
      font-size: 12px;
    }
    .model-select-native {
      background: #1e1e2e;
      color: #94e2d5;
      border: 1px solid rgba(148, 226, 213, 0.2);
      font-family: monospace;
      font-size: 11px;
      padding: 2px 6px;
      border-radius: 3px;
      outline: none;
      cursor: pointer;
    }
    .model-select-native:focus {
      border-color: #94e2d5;
    }

    .new-session-btn {
      color: #f5c2e7;
      cursor: pointer;
      font-weight: bold;
      white-space: nowrap;
    }

    .wipe-workspace-btn {
      color: #f38ba8; /* Warning Red */
      cursor: pointer;
      font-weight: normal;
      white-space: nowrap;
      opacity: 0.6;
      transition: opacity 0.15s ease;
    }
    .wipe-workspace-btn:hover {
      opacity: 1;
      text-shadow: 0 0 8px rgba(243, 139, 168, 0.2);
    }

    /* --- PRIMARY TERMINAL AREA --- */
    .terminal {
      flex: 1;
      display: flex;
      flex-direction: column;
      padding: 16px;
      box-sizing: border-box;
      overflow: hidden;
    }

    #log {
      flex: 1;
      overflow-y: auto;
      padding-right: 8px;
    }

    .line {
      white-space: pre-wrap;
      margin: 2px 0;
    }

    .user { color: #a6e3a1; }
    .ai { color: #f9e2af; }
    .sys { color: #7aa2f7; }
    .err { color: #f38ba8; }
    .tool { color: #f5c2e7; }

    .input-row {
      display: flex;
      gap: 10px;
      align-items: center;
      border-top: 1px solid rgba(255,255,255,0.08);
      padding-top: 10px;
    }

    .prompt { color: #cdd6f4; white-space: nowrap; }

    #input {
      flex: 1;
      background: transparent;
      border: none;
      outline: none;
      color: #cdd6f4;
      font-size: 14px;
      font-family: monospace;
    }

    /* --- BOTTOM HELP LEGEND --- */
    .footer-help {
      background: #11151c;
      border-top: 1px solid rgba(255, 255, 255, 0.05);
      padding: 6px 12px;
      font-size: 11px;
      color: #585b70;
      display: flex;
      gap: 20px;
      overflow-x: auto;
      white-space: nowrap;
    }
    .footer-help span strong {
      color: #cdd6f4;
    }
  </style>
</head>

<body data-webtui-theme="catppuccin-mocha">

  <!-- Block Character Filled ASCII Header -->
  <div class="banner-header">
    <pre class="ascii-title">
 β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ•—   β–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ•—   β–ˆβ–ˆβ•—     β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ•—   β–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— 
β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—β•šβ•β•β–ˆβ–ˆβ•”β•β•β•β•šβ–ˆβ–ˆβ•— β–ˆβ–ˆβ•”β•β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—β–ˆβ–ˆβ•”β•β•β•β•β•β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘    β–ˆβ–ˆβ•”β•β•β•β•β•β–ˆβ–ˆβ•”β•β•β•β–ˆβ–ˆβ•—β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—β–ˆβ–ˆβ•‘β–ˆβ–ˆβ–ˆβ–ˆβ•—  β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β•β•β•β•β• 
β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•‘β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•   β–ˆβ–ˆβ•‘    β•šβ–ˆβ–ˆβ–ˆβ–ˆβ•”β• β–ˆβ–ˆβ•‘  β–ˆβ–ˆβ•‘β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—  β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘    β–ˆβ–ˆβ•‘     β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘  β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β–ˆβ–ˆβ•— β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘  β–ˆβ–ˆβ–ˆβ•—
β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—   β–ˆβ–ˆβ•‘     β•šβ–ˆβ–ˆβ•”β•  β–ˆβ–ˆβ•‘  β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β•β•β•  β•šβ–ˆβ–ˆβ•— β–ˆβ–ˆβ•”β•    β–ˆβ–ˆβ•‘     β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘  β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘β•šβ–ˆβ–ˆβ•—β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘
β–ˆβ–ˆβ•‘  β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘  β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘      β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— β•šβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•     β•šβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β•šβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘ β•šβ–ˆβ–ˆβ–ˆβ–ˆβ•‘β•šβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•
β•šβ•β•  β•šβ•β•β•šβ•β•  β•šβ•β•   β•šβ•β•      β•šβ•β•   β•šβ•β•β•β•β•β• β•šβ•β•β•β•β•β•β•  β•šβ•β•β•β•       β•šβ•β•β•β•β•β• β•šβ•β•β•β•β•β• β•šβ•β•β•β•β•β• β•šβ•β•β•šβ•β•  β•šβ•β•β•β• β•šβ•β•β•β•β•β• </pre>
    <div class="sub-tagline">Local Agent Orchestration Terminal β€’ Core Runtime: Qwen2.5 + Dynamic Toolkits</div>
  </div>

  <!-- Multiplexer Navigation Bar -->
  <div class="session-bar" id="sessionBar"></div>

  <!-- Primary Interface Window -->
  <div class="terminal">
    <div id="log"></div>

    <div class="input-row">
      <span class="prompt" id="terminalPrompt">user@qwen:[~]~$</span>
      <input id="input" autocomplete="off" autofocus placeholder="Type a message or /command..." />
    </div>
  </div>

  <!-- Footer Keyboard Legend -->
  <div class="footer-help">
    <span><strong>Double-Click Tab</strong> Rename</span>
    <span><strong>Hover + [x]</strong> Delete single</span>
    <span><strong>/model [name]</strong> Switch core model</span>
    <span><strong>[Wipe All]</strong> Factory reset data</span>
    <span><strong>/new [name]</strong> Create context</span>
    <span><strong>/clear</strong> Clear view</span>
  </div>

<script type="module">
import { get, set, del } from 'https://cdn.jsdelivr.net/npm/idb-keyval@6/+esm';

const log = document.getElementById("log");
const input = document.getElementById("input");
const sessionBar = document.getElementById("sessionBar");
const terminalPrompt = document.getElementById("terminalPrompt");

const OLLAMA_URL = "http://localhost:11434/api/chat";
const REGISTRY_KEY = "multiplexer_registry_config";
const SESSION_PREFIX = "multiplexer_session_";

// List of available target models on your Ollama layer
const POPULAR_MODELS = [
  "qwen2.5:7b",
  "qwen2.5:14b",
  "qwen2.5:latest",
  "llama3.1:latest",
  "mistral:latest",
  "phi3:latest"
];

/* ---------------------------
   MEMORY SYSTEM STRUCTURES
----------------------------*/
const SYSTEM_PROMPT = {
  role: "system",
  content: `You are a tool-using assistant.
When you need to use a tool to look up information or perform calculations, you MUST respond ONLY with a raw valid JSON object matching this schema:
{
  "tool": "tool_name",
  "arguments": { "key": "value" }
}

Do not include conversational text when calling a tool.
Otherwise, respond normally to the user in plain text.

Available tools:
- get_time: Returns the current date and time. No arguments needed.
- calc: Calculates a mathematical expression. Requires argument "expression" (string).`
};

let registry = {
  currentActiveId: null,
  activeModel: "qwen2.5:7b", // Default fallback model
  list: []
};

let activeMessages = [SYSTEM_PROMPT]; 
let fullPersistentHistory = [];       

/* ---------------------------
   CONTEXT PRUNING (Sliding Window)
----------------------------*/
function pruneActiveMemory() {
  const systemPrompt = activeMessages[0];
  let chatHistory = activeMessages.slice(1);
  const MAX_ACTIVE_ITEMS = 14; 

  if (chatHistory.length > MAX_ACTIVE_ITEMS) {
    chatHistory = chatHistory.slice(chatHistory.length - MAX_ACTIVE_ITEMS);
    if (chatHistory[0].role === "user" && chatHistory[0].content.startsWith("TOOL RESULT")) {
      chatHistory.shift(); 
    }
  }
  activeMessages = [systemPrompt, ...chatHistory];
}

/* ---------------------------
   PERSISTENCE LAYER (IndexedDB)
----------------------------*/
async function saveAllToBrowser() {
  try {
    await set(REGISTRY_KEY, registry);
    if (registry.currentActiveId) {
      await set(SESSION_PREFIX + registry.currentActiveId, fullPersistentHistory);
    }
  } catch (err) {
    console.error("Storage save operation encountered errors:", err);
  }
}

async function loadSessionData(sessionId) {
  try {
    const savedData = await get(SESSION_PREFIX + sessionId);
    fullPersistentHistory = savedData || [];

    activeMessages = [SYSTEM_PROMPT, ...fullPersistentHistory.filter(m => !m.isToolActivityLog)];
    pruneActiveMemory();

    renderTerminalScreen();
    renderTopMultiplexerBar();
  } catch (err) {
    print("Database retrieval block failure: " + err.message, "err");
  }
}

/* ---------------------------
   UI RENDERING MANAGERS
----------------------------*/
function renderTopMultiplexerBar() {
  sessionBar.innerHTML = "";

  registry.list.forEach((session, index) => {
    const wrapper = document.createElement("div");
    wrapper.className = "session-wrapper";

    const tab = document.createElement("span");
    const isActive = session.id === registry.currentActiveId;
    tab.className = `session-tab ${isActive ? 'active' : ''}`;
    tab.textContent = `${index}: ${session.name}`;

    tab.addEventListener("click", (e) => {
      if (!isActive && tab.tagName === 'SPAN') switchSession(index);
    });

    tab.addEventListener("dblclick", (e) => {
      e.stopPropagation();
      const editorInput = document.createElement("input");
      editorInput.type = "text";
      editorInput.className = "rename-input";
      editorInput.value = session.name;

      const finishRename = async () => {
        const freshName = editorInput.value.trim();
        if (freshName && freshName !== session.name) {
          session.name = freshName;
          await saveAllToBrowser();
          renderTerminalScreen();
        }
        renderTopMultiplexerBar();
      };

      editorInput.addEventListener("keydown", (ke) => {
        if (ke.key === "Enter") finishRename();
        if (ke.key === "Escape") renderTopMultiplexerBar();
      });

      editorInput.addEventListener("blur", finishRename);

      wrapper.replaceChild(editorInput, tab);
      editorInput.focus();
      editorInput.select();
    });

    wrapper.appendChild(tab);

    const delBtn = document.createElement("span");
    delBtn.className = "delete-btn";
    delBtn.textContent = "[x]";
    delBtn.title = "Delete Session";
    delBtn.addEventListener("click", (e) => {
      e.stopPropagation();
      handleDeleteSessionConfirmation(index);
    });
    wrapper.appendChild(delBtn);

    sessionBar.appendChild(wrapper);
  });

  const actionsGroup = document.createElement("div");
  actionsGroup.className = "bar-actions-group";

  // Unintrusive Model Selection Element
  const modelWrapper = document.createElement("div");
  modelWrapper.className = "model-selector-wrapper";
  modelWrapper.innerHTML = `<span>model:</span>`;

  const modelSelect = document.createElement("select");
  modelSelect.className = "model-select-native";

  // Make sure the active model is always in the list options
  const modelsToRender = [...new Set([registry.activeModel, ...POPULAR_MODELS])];
  modelsToRender.forEach(modelName => {
    const opt = document.createElement("option");
    opt.value = modelName;
    opt.textContent = modelName;
    if (modelName === registry.activeModel) opt.selected = true;
    modelSelect.appendChild(opt);
  });

  modelSelect.addEventListener("change", async (e) => {
    await switchActiveModel(e.target.value);
  });

  modelWrapper.appendChild(modelSelect);
  actionsGroup.appendChild(modelWrapper);

  const createBtn = document.createElement("span");
  createBtn.className = "new-session-btn";
  createBtn.textContent = "+ [New]";
  createBtn.addEventListener("click", () => handleCreateSession());
  actionsGroup.appendChild(createBtn);

  const wipeBtn = document.createElement("span");
  wipeBtn.className = "wipe-workspace-btn";
  wipeBtn.textContent = "[Wipe All]";
  wipeBtn.title = "Irreversibly delete all database history records";
  wipeBtn.addEventListener("click", () => handleWipeAllSessionsConfirmation());
  actionsGroup.appendChild(wipeBtn);

  sessionBar.appendChild(actionsGroup);
}

function renderTerminalScreen() {
  log.innerHTML = "";
  const currentSession = registry.list.find(s => s.id === registry.currentActiveId);
  const sessionName = currentSession ? currentSession.name : "none";

  terminalPrompt.textContent = `user@qwen:[${sessionName}]~$`;

  if (!currentSession) {
    print("No active environment available. Please click '+ [New]' or type /new.", "sys");
    return;
  }

  if (fullPersistentHistory.length === 0) {
    print(`Welcome to session [${sessionName}]. Current Core: ${registry.activeModel}. Type your message or run /help.`, "sys");
    return;
  }

  for (const msg of fullPersistentHistory) {
    if (msg.role === "user" && !msg.content.startsWith("TOOL RESULT")) {
      print(`user@qwen:[${sessionName}]~$ ${msg.content}`, "user");
    } else if (msg.role === "assistant") {
      if (!msg.content.trim().startsWith("{")) {
        print(msg.content, "ai");
      }
    } else if (msg.role === "system" && msg.isToolActivityLog) {
      print(msg.content, "tool");
    }
  }
}

function print(text, cls="sys") {
  const div = document.createElement("div");
  div.className = "line " + cls;
  div.textContent = text;
  log.appendChild(div);
  log.scrollTop = log.scrollHeight;
  return div;
}

/* ---------------------------
   MULTIPLEXER OPERATIONS
----------------------------*/
async function switchActiveModel(newModelName) {
  if (!newModelName || !newModelName.trim()) return;
  registry.activeModel = newModelName.trim();
  await saveAllToBrowser();
  print(`Engine target processing runtime hot-swapped to: ${registry.activeModel}`, "sys");
  renderTopMultiplexerBar();
}

async function handleCreateSession(customName = null) {
  const id = "s_" + Date.now();
  const name = customName ? customName.trim() : `session-${registry.list.length}`;

  registry.list.push({ id, name });
  registry.currentActiveId = id;

  fullPersistentHistory = [];
  activeMessages = [SYSTEM_PROMPT];

  await saveAllToBrowser();
  renderTopMultiplexerBar();
  renderTerminalScreen();
}

async function switchSession(index) {
  const target = registry.list[index];
  if (!target) {
    print(`Error: Session index context [${index}] not tracked inside current workspace registry profiles.`, "err");
    return;
  }
  registry.currentActiveId = target.id;
  await saveAllToBrowser();
  await loadSessionData(target.id);
}

async function handleDeleteSessionConfirmation(index) {
  const targetSession = registry.list[index];
  if (!targetSession) return;

  if (confirm(`Are you sure you want to permanently delete session "${targetSession.name}"?`)) {
    await del(SESSION_PREFIX + targetSession.id);
    registry.list.splice(index, 1);

    if (registry.currentActiveId === targetSession.id) {
      if (registry.list.length > 0) {
        const nextIndexFallback = Math.max(0, index - 1);
        registry.currentActiveId = registry.list[nextIndexFallback].id;
        await set(REGISTRY_KEY, registry);
        await loadSessionData(registry.currentActiveId);
      } else {
        registry.currentActiveId = null;
        fullPersistentHistory = [];
        activeMessages = [SYSTEM_PROMPT];
        await set(REGISTRY_KEY, registry);
        renderTopMultiplexerBar();
        renderTerminalScreen();
      }
    } else {
      await set(REGISTRY_KEY, registry);
      renderTopMultiplexerBar();
    }
    print(`Successfully removed terminal workspace tracking profile target container: [${targetSession.name}]`, "sys");
  }
}

async function handleWipeAllSessionsConfirmation() {
  const explicitPromptText = "⚠️ WARNING: This will permanently delete ALL sessions and history logs. This action cannot be undone.\n\nType 'WIPE' to confirm initialization:";
  const confirmationInput = prompt(explicitPromptText);

  if (confirmationInput === "WIPE") {
    print("Initiating full environment memory database purge...", "err");

    for (const session of registry.list) {
      await del(SESSION_PREFIX + session.id);
    }

    await del(REGISTRY_KEY);

    registry.list = [];
    registry.currentActiveId = null;
    registry.activeModel = "qwen2.5:7b";
    fullPersistentHistory = [];
    activeMessages = [SYSTEM_PROMPT];

    print("Database scrub complete. Re-initializing factory general configuration environments.", "sys");

    await handleCreateSession("general");
  } else {
    print("Workspace wipe sequence aborted by user action.", "sys");
  }
}

/* ---------------------------
   SLASH COMMAND INTERCEPTOR
----------------------------*/
async function handleSlashCommand(rawInput) {
  const parts = rawInput.trim().split(" ");
  const cmd = parts[0].toLowerCase();
  const args = parts.slice(1).join(" ");

  print(`user@qwen:[command]~$ ${rawInput}`, "user");

  switch (cmd) {
    case "/new":
      await handleCreateSession(args || null);
      break;

    case "/model":
      if (!args.trim()) {
        print(`Current active context orchestration engine model target: ${registry.activeModel}`, "sys");
        print(`Usage option: /model [ollama-model-tag] (e.g., /model llama3.1:latest)`, "sys");
      } else {
        await switchActiveModel(args);
      }
      break;

    case "/switch":
      const targetIndex = parseInt(args, 10);
      if (isNaN(targetIndex)) {
        print("Usage error syntax structural rule: /switch [numerical-index-id]", "err");
      } else {
        await switchSession(targetIndex);
      }
      break;

    case "/rename":
      if (!args.trim()) {
        print("Usage error syntax: /rename [new-session-tag-name]", "err");
      } else {
        const activeItem = registry.list.find(s => s.id === registry.currentActiveId);
        if (activeItem) {
          activeItem.name = args.trim();
          await saveAllToBrowser();
          renderTopMultiplexerBar();
          renderTerminalScreen();
        }
      }
      break;

    case "/delete":
      const delIdx = parseInt(args, 10);
      if (isNaN(delIdx)) {
        print("Usage error syntax: /delete [numerical-index-id]", "err");
      } else {
        await handleDeleteSessionConfirmation(delIdx);
      }
      break;

    case "/wipeall":
      await handleWipeAllSessionsConfirmation();
      break;

    case "/ls":
      print("--- Active Workspace Environments ---", "sys");
      registry.list.forEach((s, idx) => {
        const activeMarker = s.id === registry.currentActiveId ? " (active)" : "";
        print(`  [${idx}]: ${s.name}${activeMarker}`, "sys");
      });
      break;

    case "/clear":
      log.innerHTML = "";
      break;

    case "/help":
      print("Available workspace commands:", "sys");
      print("  /model [tag]    - Hot-swap core backend model parsing engine dynamically", "sys");
      print("  /new [name]     - Create a brand new independent conversation container", "sys");
      print("  /switch [index] - Hot-swap active screen output to alternate target history", "sys");
      print("  /rename [name]  - Changes character pointer tracking tag assigned to window row", "sys");
      print("  /delete [index] - Deletes and drops tracked IndexedDB context elements from storage", "sys");
      print("  /wipeall        - Destroys and cleans entire memory workspace databases", "sys");
      print("  /ls             - Lists out all tracking logs captured via IndexedDB system paths", "sys");
      print("  /clear          - Standard display layer terminal clear operation layout reset", "sys");
      break;

    default:
      print(`Unrecognized multiplexer control sequence pipeline call target '${cmd}'. Try calling /help.`, "err");
  }
}

/* ---------------------------
   LOCAL ENVIRONMENT TOOLS
----------------------------*/
function runTool(name, args) {
  if (name === "get_time") {
    return { time: new Date().toISOString() };
  }
  if (name === "calc") {
    try {
      const evaluate = new Function(`return (${args.expression})`);
      return { result: evaluate() };
    } catch {
      return { error: "invalid expression" };
    }
  }
  return { error: "unknown tool" };
}

function tryParseTool(text) {
  try {
    const obj = JSON.parse(text.trim());
    if (obj.tool && obj.arguments) return obj;
  } catch {}
  return null;
}

/* ---------------------------
   STREAM INTERFACE DRIVER
----------------------------*/
async function streamAI(res) {
  const div = print("", "ai");
  const reader = res.body.getReader();
  const decoder = new TextDecoder();

  let buffer = "";
  let fullText = "";

  while (true) {
    const { value, done } = await reader.read();
    if (done) break;

    buffer += decoder.decode(value, { stream: true });
    const lines = buffer.split("\n");
    buffer = lines.pop();

    for (const line of lines) {
      if (!line.trim()) continue;

      try {
        const json = JSON.parse(line);
        const token = json.message?.content || "";

        div.textContent += token;
        fullText += token;
        log.scrollTop = log.scrollHeight;
      } catch {}
    }
  }
  return fullText;
}

async function callModel() {
  const res = await fetch(OLLAMA_URL, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      model: registry.activeModel, // Dynamically injection based on selection config
      stream: true,
      messages: activeMessages
    })
  });

  if (!res.ok || !res.body) throw new Error(`Ollama communication pipeline break during request using model ${registry.activeModel}`);
  return res;
}

/* ---------------------------
   AGENTIC EXECUTION RUNTIME ENGINE
----------------------------*/
async function askAI(prompt) {
  const currentSession = registry.list.find(s => s.id === registry.currentActiveId);
  const sessionLabel = currentSession ? currentSession.name : "~";

  print(`user@qwen:[${sessionLabel}]~$ ${prompt}`, "user");

  const userMsg = { role: "user", content: prompt };
  activeMessages.push(userMsg);
  fullPersistentHistory.push(userMsg);

  let isAgentProcessing = true;
  let executionDepthGuard = 5; 

  try {
    while (isAgentProcessing && executionDepthGuard > 0) {
      executionDepthGuard--;

      const responseStream = await callModel();
      const aiResponseText = await streamAI(responseStream);

      const assistantMsg = { role: "assistant", content: aiResponseText };
      activeMessages.push(assistantMsg);
      fullPersistentHistory.push(assistantMsg);

      const parsedToolRequest = tryParseTool(aiResponseText);

      if (parsedToolRequest) {
        const actionMessage = `πŸ”§ Executing tool: ${parsedToolRequest.tool}...`;
        print(actionMessage, "tool");
        fullPersistentHistory.push({ role: "system", content: actionMessage, isToolActivityLog: true });

        const toolOutcome = runTool(parsedToolRequest.tool, parsedToolRequest.arguments);
        const outcomeString = `πŸ“¦ Response: ${JSON.stringify(toolOutcome)}`;
        print(outcomeString, "tool");
        fullPersistentHistory.push({ role: "system", content: outcomeString, isToolActivityLog: true });

        const toolResultFeedback = {
          role: "user",
          content: `TOOL RESULT for '${parsedToolRequest.tool}': ${JSON.stringify(toolOutcome)}`
        };
        activeMessages.push(toolResultFeedback);
        fullPersistentHistory.push(toolResultFeedback);
      } else {
        isAgentProcessing = false;
      }
    }

    await saveAllToBrowser();
    pruneActiveMemory();

  } catch (err) {
    print("Error encountered inside tracking node loops: " + err.message, "err");
  }
}

/* ---------------------------
   DOM INTERFACES INTERCEPT
----------------------------*/
input.addEventListener("keydown", async (e) => {
  if (e.key === "Enter") {
    const value = input.value.trim();
    if (!value) return;

    input.value = "";

    if (value.startsWith("/")) {
      await handleSlashCommand(value);
    } else {
      if (!registry.currentActiveId) {
        print("No active environment available. Please execute '/new' command to spawn a session first.", "err");
        return;
      }
      await askAI(value);
    }
  }
});

/* ---------------------------
   SYSTEM ENTRY BOOT STRAPPER
----------------------------*/
async function boot() {
  try {
    const savedRegistry = await get(REGISTRY_KEY);
    if (savedRegistry && savedRegistry.list.length > 0) {
      registry = savedRegistry;
      // Ensure configuration object structure compatibility if updating from previous version
      if (!registry.activeModel) registry.activeModel = "qwen2.5:7b";
      renderTopMultiplexerBar();
      await loadSessionData(registry.currentActiveId);
    } else {
      await handleCreateSession("general");
    }
  } catch (e) {
    console.warn("Storage profile structure state clean initialization step required:", e);
    await handleCreateSession("general");
  }
}

boot();
</script>

</body>
</html>
Enter fullscreen mode Exit fullscreen mode

Top comments (1)

Collapse
 
leob profile image
leob

Nice, looks solid!