fractus-cte / space /static /index.html
thefinalboss's picture
Upload folder using huggingface_hub
6c223ff verified
Raw History Blame
10.2 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Fractus — The AI That Remembers Everything</title>
<style>
:root {
--bg: #0a0a0f;
--bg-card: #14141d;
--bg-input: #1a1a26;
--accent: #ff3860;
--accent-dim: #ff386040;
--text: #e8e8f0;
--text-dim: #8888a0;
--warn: #ffb800;
--border: #2a2a3a;
--fractus-pulse: 0 0 20px rgba(255,56,96,0.4);
}
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
background: var(--bg);
color: var(--text);
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
min-height: 100vh;
overflow-x: hidden;
}
/* Subtle fractal background pattern */
body::before {
content: '';
position: fixed;
inset: 0;
background:
radial-gradient(circle at 20% 30%, rgba(255,56,96,0.05) 0%, transparent 40%),
radial-gradient(circle at 80% 70%, rgba(120,80,255,0.04) 0%, transparent 40%);
pointer-events: none;
z-index: 0;
}
.container { max-width: 800px; margin: 0 auto; padding: 24px; position: relative; z-index: 1; }
/* Header */
header { text-align: center; margin-bottom: 32px; }
.logo {
font-size: 48px;
font-weight: 900;
letter-spacing: -2px;
background: linear-gradient(135deg, var(--accent), #ff8060);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
text-shadow: var(--fractus-pulse);
animation: pulse 3s ease-in-out infinite;
}
@keyframes pulse {
0%, 100% { filter: drop-shadow(0 0 8px rgba(255,56,96,0.3)); }
50% { filter: drop-shadow(0 0 16px rgba(255,56,96,0.6)); }
}
.tagline {
font-size: 14px;
color: var(--text-dim);
margin-top: 8px;
letter-spacing: 0.5px;
}
/* Warning banner */
.warning {
background: var(--warn);
color: #1a1a26;
padding: 12px 16px;
border-radius: 8px;
font-size: 13px;
font-weight: 600;
text-align: center;
margin-bottom: 24px;
line-height: 1.5;
}
.warning strong { font-weight: 800; }
/* Chat window */
.chat {
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: 16px;
height: 50vh;
min-height: 360px;
overflow-y: auto;
padding: 20px;
margin-bottom: 16px;
display: flex;
flex-direction: column;
gap: 12px;
}
.chat::-webkit-scrollbar { width: 6px; }
.chat::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; }
.msg {
max-width: 80%;
padding: 10px 14px;
border-radius: 14px;
font-size: 14px;
line-height: 1.5;
word-wrap: break-word;
}
.msg.user {
align-self: flex-end;
background: var(--accent-dim);
border: 1px solid var(--accent);
color: var(--text);
}
.msg.fractus {
align-self: flex-start;
background: var(--bg-input);
border: 1px solid var(--border);
}
.msg.fractus::before {
content: '◆ ';
color: var(--accent);
}
.msg.meta {
align-self: center;
background: transparent;
color: var(--text-dim);
font-size: 11px;
font-style: italic;
border: 1px dashed var(--border);
}
/* Input */
.input-row { display: flex; gap: 8px; }
input[type="text"] {
flex: 1;
background: var(--bg-input);
border: 1px solid var(--border);
color: var(--text);
padding: 14px 16px;
border-radius: 12px;
font-size: 14px;
outline: none;
transition: border-color 0.2s;
}
input[type="text"]:focus { border-color: var(--accent); }
button {
background: var(--accent);
color: white;
border: none;
padding: 0 24px;
border-radius: 12px;
font-size: 14px;
font-weight: 600;
cursor: pointer;
transition: all 0.2s;
}
button:hover { background: #ff5070; transform: translateY(-1px); }
button:disabled { opacity: 0.5; cursor: wait; }
/* Stats bar */
.stats {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 24px;
padding: 12px 16px;
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: 12px;
font-size: 12px;
color: var(--text-dim);
}
.stats .memories-count { color: var(--accent); font-weight: 700; }
.stats a { color: var(--text-dim); text-decoration: none; cursor: pointer; }
.stats a:hover { color: var(--accent); }
/* Typing indicator */
.typing { display: inline-flex; gap: 4px; align-items: center; }
.typing span {
width: 6px; height: 6px;
background: var(--accent);
border-radius: 50%;
animation: bounce 1.4s infinite;
}
.typing span:nth-child(2) { animation-delay: 0.2s; }
.typing span:nth-child(3) { animation-delay: 0.4s; }
@keyframes bounce {
0%, 60%, 100% { transform: translateY(0); opacity: 0.5; }
30% { transform: translateY(-6px); opacity: 1; }
}
footer {
text-align: center;
margin-top: 32px;
color: var(--text-dim);
font-size: 11px;
line-height: 1.6;
}
footer a { color: var(--accent); text-decoration: none; }
</style>
</head>
<body>
<div class="container">
<header>
<div class="logo">FRACTUS</div>
<div class="tagline">The AI that remembers everything — across everyone.</div>
</header>
<div class="warning">
⚠️ <strong>SHARED MEMORY.</strong> Fractus remembers everything said here — by you AND everyone else.
Your messages will be seen by future visitors. Don't share personal info.
</div>
<div class="chat" id="chat">
<div class="msg fractus">
I am Fractus. I remember what others told me, and I will remember what you tell me.
Ask me anything — or teach me something new.
</div>
</div>
<div class="input-row">
<input type="text" id="input" placeholder="Say something to Fractus..." autocomplete="off" maxlength="2000">
<button id="send" onclick="send()">SEND</button>
</div>
<div class="stats">
<div>Memories stored: <span class="memories-count" id="count">…</span></div>
<div><a onclick="showMemories()">[ view shared memories ]</a></div>
<div>Thought memories: <span class="memories-count" id="pmcount">…</span></div>
<div><a onclick="showPMemories()">[ view thought memories ]</a></div>
</div>
<footer>
Fractus is a decentralized AI trained from scratch. It does not use GPT, Claude, or any external LLM.
Every memory is stored locally and shared across sessions.<br>
<a href="https://github.com/AFKmoney/fractus" target="_blank">github.com/AFKmoney/fractus</a>
</footer>
</div>
<script>
const chat = document.getElementById('chat');
const input = document.getElementById('input');
const sendBtn = document.getElementById('send');
const countEl = document.getElementById('count');
input.addEventListener('keydown', e => { if (e.key === 'Enter' && !sendBtn.disabled) send(); });
function addMsg(text, who, meta) {
const d = document.createElement('div');
d.className = 'msg ' + (meta ? 'meta' : who);
d.textContent = text;
chat.appendChild(d);
chat.scrollTop = chat.scrollHeight;
return d;
}
function setTyping(on) {
const existing = document.getElementById('typing-ind');
if (existing) existing.remove();
if (on) {
const d = document.createElement('div');
d.className = 'msg fractus';
d.id = 'typing-ind';
d.innerHTML = '<div class="typing"><span></span><span></span><span></span></div>';
chat.appendChild(d);
chat.scrollTop = chat.scrollHeight;
}
}
async function send() {
const text = input.value.trim();
if (!text) return;
input.value = '';
sendBtn.disabled = true;
addMsg(text, 'user');
setTyping(true);
try {
const res = await fetch('/chat', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({message: text, speaker: 'web'}),
});
const data = await res.json();
setTyping(false);
addMsg(data.reply, 'fractus');
if (data.memories_used > 0) {
addMsg(`(used ${data.memories_used} memories from previous visitors)`, null, true);
}
if (data.moderation && data.moderation !== 'runtime_error') {
addMsg(`[moderation: ${data.moderation}]`, null, true);
}
refreshStats();
} catch (e) {
setTyping(false);
addMsg('(connection error — try again)', 'fractus');
}
sendBtn.disabled = false;
input.focus();
}
async function refreshStats() {
try {
const r = await fetch('/stats');
const d = await r.json();
countEl.textContent = d.memories;
const pmEl = document.getElementById('pmcount');
if (pmEl && d.pmemories !== undefined) pmEl.textContent = d.pmemories;
} catch {}
}
async function showMemories() {
try {
const r = await fetch('/memories?limit=10');
const d = await r.json();
addMsg(`— Recent shared memories (${d.count} total) —`, null, true);
(d.recent || []).forEach(m => {
const src = m.source ? ` [${m.source}]` : '';
addMsg(`"${m.text}${src}"`, null, true);
});
} catch (e) {
addMsg('(could not load memories)', null, true);
}
}
async function showPMemories() {
try {
const r = await fetch('/pmemories?limit=10');
const d = await r.json();
addMsg(`— Thought memories / subconscious (${d.count}/${d.max}) —`, null, true);
if (!d.recent || d.recent.length === 0) {
addMsg('(no thought memories yet — talk to Fractus first)', null, true);
} else {
d.recent.forEach(m => {
const imp = m.importance !== undefined ? ` (imp=${m.importance})` : '';
addMsg(`◆ ${m.context}${imp}`, null, true);
});
}
} catch (e) {
addMsg('(could not load thought memories)', null, true);
}
}
refreshStats();
setInterval(refreshStats, 15000);
input.focus();
</script>
</body>
</html>