forked from Rockachopa/Timmy-time-dashboard
123 lines
4.5 KiB
HTML
123 lines
4.5 KiB
HTML
{% extends "base.html" %}
|
|
|
|
{% block title %}Nexus{% endblock %}
|
|
|
|
{% block extra_styles %}{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="container-fluid nexus-layout py-3">
|
|
|
|
<div class="nexus-header mb-3">
|
|
<div class="nexus-title">// NEXUS</div>
|
|
<div class="nexus-subtitle">
|
|
Persistent conversational awareness — always present, always learning.
|
|
</div>
|
|
</div>
|
|
|
|
<div class="nexus-grid">
|
|
|
|
<!-- ── LEFT: Conversation ────────────────────────────────── -->
|
|
<div class="nexus-chat-col">
|
|
<div class="card mc-panel nexus-chat-panel">
|
|
<div class="card-header mc-panel-header d-flex justify-content-between align-items-center">
|
|
<span>// CONVERSATION</span>
|
|
<button class="mc-btn mc-btn-sm"
|
|
hx-delete="/nexus/history"
|
|
hx-target="#nexus-chat-log"
|
|
hx-swap="beforeend"
|
|
hx-confirm="Clear nexus conversation?">
|
|
CLEAR
|
|
</button>
|
|
</div>
|
|
|
|
<div class="card-body p-2" id="nexus-chat-log">
|
|
{% for msg in messages %}
|
|
<div class="chat-message {{ 'user' if msg.role == 'user' else 'agent' }}">
|
|
<div class="msg-meta">
|
|
{{ 'YOU' if msg.role == 'user' else 'TIMMY' }} // {{ msg.timestamp }}
|
|
</div>
|
|
<div class="msg-body {% if msg.role == 'assistant' %}timmy-md{% endif %}">
|
|
{{ msg.content | e }}
|
|
</div>
|
|
</div>
|
|
{% else %}
|
|
<div class="nexus-empty-state">
|
|
Nexus is ready. Start a conversation — memories will surface in real time.
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
|
|
<div class="card-footer p-2">
|
|
<form hx-post="/nexus/chat"
|
|
hx-target="#nexus-chat-log"
|
|
hx-swap="beforeend"
|
|
hx-on::after-request="this.reset(); document.getElementById('nexus-chat-log').scrollTop = 999999;">
|
|
<div class="d-flex gap-2">
|
|
<input type="text"
|
|
name="message"
|
|
id="nexus-input"
|
|
class="mc-search-input flex-grow-1"
|
|
placeholder="Talk to Timmy..."
|
|
autocomplete="off"
|
|
required>
|
|
<button type="submit" class="mc-btn mc-btn-primary">SEND</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ── RIGHT: Memory sidebar ─────────────────────────────── -->
|
|
<div class="nexus-sidebar-col">
|
|
|
|
<!-- Live memory context (updated with each response) -->
|
|
<div class="card mc-panel nexus-memory-panel mb-3">
|
|
<div class="card-header mc-panel-header">
|
|
<span>// LIVE MEMORY</span>
|
|
<span class="badge ms-2" style="background:var(--purple-dim); color:var(--purple);">
|
|
{{ stats.total_entries }} stored
|
|
</span>
|
|
</div>
|
|
<div class="card-body p-2">
|
|
<div id="nexus-memory-panel" class="nexus-memory-hits">
|
|
<div class="nexus-memory-label">Relevant memories appear here as you chat.</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Teaching panel -->
|
|
<div class="card mc-panel nexus-teach-panel">
|
|
<div class="card-header mc-panel-header">// TEACH TIMMY</div>
|
|
<div class="card-body p-2">
|
|
<form hx-post="/nexus/teach"
|
|
hx-target="#nexus-teach-response"
|
|
hx-swap="innerHTML"
|
|
hx-on::after-request="this.reset()">
|
|
<div class="d-flex gap-2 mb-2">
|
|
<input type="text"
|
|
name="fact"
|
|
class="mc-search-input flex-grow-1"
|
|
placeholder="e.g. I prefer dark themes"
|
|
required>
|
|
<button type="submit" class="mc-btn mc-btn-primary">TEACH</button>
|
|
</div>
|
|
</form>
|
|
<div id="nexus-teach-response"></div>
|
|
|
|
<div class="nexus-facts-header mt-3">// KNOWN FACTS</div>
|
|
<ul class="nexus-facts-list" id="nexus-facts-list">
|
|
{% for fact in facts %}
|
|
<li class="nexus-fact-item">{{ fact.content | e }}</li>
|
|
{% else %}
|
|
<li class="nexus-fact-empty">No personal facts stored yet.</li>
|
|
{% endfor %}
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
|
|
</div><!-- /sidebar -->
|
|
</div><!-- /nexus-grid -->
|
|
|
|
</div>
|
|
{% endblock %}
|