🎮ArcadeLab

Meine KI - Smart & Modern

by BlazeKoala99
833 lines20.4 KB
▶ Play
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Meine KI - Smart & Modern</title>

<!-- Externe Bibliotheken für Markdown und Syntax Highlighting -->
<script src="https://cdn.jsdelivr.net/npm/marked/marked.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/atom-one-dark.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>

<style>
:root {
    --bg-main: #09090b;
    --bg-surface: #121217;
    --bg-header: #121217;
    --bg-input: #18181f;
    --border-color: #27272a;
    --accent: #8b5cf6;
    --accent-hover: #7c3aed;
    --cyan: #06b6d4;
    --text-main: #f4f4f5;
    --text-muted: #a1a1aa;
}

*{box-sizing:border-box;margin:0;padding:0}

body{
    height:100vh;
    background:var(--bg-main);
    color:var(--text-main);
    font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
    overflow:hidden;
}

#app{
    display:flex;
    height:100vh;
    flex-direction:row;
}

/* SIDEBAR */
aside{
    width:280px;
    background:var(--bg-surface);
    border-right:1px solid var(--border-color);
    display:flex;
    flex-direction:column;
    padding:16px;
    transition: transform 0.3s ease;
    z-index: 45;
}

aside h2{
    font-size:14px;
    margin-bottom:12px;
    color:#e9d5ff;
    display:flex;
    align-items:center;
    justify-content:space-between;
}

.memory-add-row {
    display: flex;
    gap: 6px;
    margin-bottom: 12px;
}

.memory-add-row input {
    flex: 1;
    background: var(--bg-input);
    border: 1px solid var(--border-color);
    padding: 8px 10px;
    border-radius: 6px;
    color: white;
    font-size: 13px;
    outline: none;
    transition: border-color 0.2s;
}
.memory-add-row input:focus {
    border-color: var(--accent);
}

.memory-add-row button {
    background: var(--accent);
    color: white;
    border: none;
    padding: 0 12px;
    border-radius: 6px;
    cursor: pointer;
    font-size: 14px;
    font-weight: bold;
    transition: background 0.2s;
}
.memory-add-row button:hover {
    background: var(--accent-hover);
}

#memoryList{
    flex:1;
    overflow-y:auto;
    list-style:none;
    display:flex;
    flex-direction:column;
    gap:6px;
    margin-bottom:12px;
}

.memory-item{
    background:var(--bg-input);
    padding:8px 10px;
    border-radius:6px;
    font-size:13px;
    color:#d4d4d8;
    border:1px solid var(--border-color);
    display:flex;
    justify-content:space-between;
    align-items:center;
    word-break:break-word;
}

.memory-item button{
    background:transparent;
    color:#f87171;
    padding:2px 6px;
    font-size:12px;
    cursor:pointer;
    border-radius:4px;
    border:0;
}
.memory-item button:hover{
    background:rgba(248,113,113,0.1);
}

.memory-hint{
    font-size:11px;
    color:var(--text-muted);
    line-height:1.4;
}

/* HAUPTBEREICH */
.main-container{
    flex:1;
    display:flex;
    flex-direction:column;
    height:100vh;
    background:var(--bg-main);
    position:relative;
}

header{
    height:60px;
    padding:0 20px;
    display:flex;
    align-items:center;
    justify-content:space-between;
    background:var(--bg-header);
    border-bottom:1px solid var(--border-color);
}

.header-left {
    display: flex;
    align-items: center;
    gap: 10px;
}

header h1{
    font-size:16px;
    font-weight:600;
}

.header-right {
    display: flex;
    align-items: center;
    gap: 10px;
}

.menu-toggle {
    background: transparent;
    border: 1px solid var(--border-color);
    color: #e9d5ff;
    padding: 6px 10px;
    border-radius: 6px;
    cursor: pointer;
    font-size: 14px;
    display: none;
}
.menu-toggle:hover {
    background: rgba(139, 92, 246, 0.1);
}

.online{
    display:flex;
    align-items:center;
    gap:6px;
    color:#4ade80;
    font-size:12px;
    background:rgba(74,222,128,0.1);
    padding:4px 10px;
    border-radius:20px;
    border:1px solid rgba(74,222,128,0.2);
}

.clear-btn {
    background: transparent;
    border: 1px solid var(--border-color);
    color: var(--text-muted);
    padding: 6px 10px;
    border-radius: 6px;
    font-size: 12px;
    cursor: pointer;
    transition: all 0.2s;
}
.clear-btn:hover {
    background: rgba(255,255,255,0.05);
    color: white;
}

#chat{
    flex:1;
    overflow-y:auto;
    padding:24px;
    display:flex;
    flex-direction:column;
    gap:16px;
    scroll-behavior: smooth;
}

.message{
    max-width:800px;
    padding:14px 18px;
    border-radius:12px;
    line-height:1.5;
    font-size:14px;
    position: relative;
    animation: fadeIn 0.25s ease;
}

@keyframes fadeIn {
    from { opacity: 0; transform: translateY(4px); }
    to { opacity: 1; transform: translateY(0); }
}

.message p { margin-bottom: 6px; }
.message p:last-child { margin-bottom: 0; }
.message ul, .message ol { margin-left: 20px; margin-bottom: 6px; }
.message pre { background: #09090b; padding: 10px; border-radius: 6px; margin: 8px 0; overflow-x: auto; border: 1px solid var(--border-color); position: relative; }
.message code { font-family: monospace; font-size: 12.5px; }

/* Copy Code Button */
.copy-code-btn {
    position: absolute;
    top: 6px;
    right: 6px;
    background: #27272a;
    color: #d4d4d8;
    border: 1px solid var(--border-color);
    padding: 2px 6px;
    font-size: 11px;
    border-radius: 4px;
    cursor: pointer;
    transition: background 0.2s;
}
.copy-code-btn:hover {
    background: var(--accent);
    color: white;
}

.user{
    margin-left:auto;
    background:var(--accent);
    color:white;
    border-bottom-right-radius:2px;
}

.ai{
    margin-right:auto;
    background:var(--bg-surface);
    color:var(--text-main);
    border:1px solid var(--border-color);
    border-bottom-left-radius:2px;
}

.ai img, .message img{
    display:block;
    width:100%;
    max-width:500px;
    margin-top:10px;
    border-radius:8px;
    border:1px solid var(--border-color);
}

/* TYPING INDICATOR */
.typing-dots {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 4px 0;
}
.typing-dots span {
    width: 6px;
    height: 6px;
    background: var(--text-muted);
    border-radius: 50%;
    animation: bounce 1.4s infinite ease-in-out both;
}
.typing-dots span:nth-child(1) { animation-delay: -0.32s; }
.typing-dots span:nth-child(2) { animation-delay: -0.16s; }
@keyframes bounce {
    0%, 80%, 100% { transform: scale(0); }
    40% { transform: scale(1.0); }
}

/* CHAT VORSCHAU */
.chat-file-preview {
    display: flex;
    align-items: center;
    gap: 8px;
    background: rgba(255,255,255,0.05);
    padding: 6px 10px;
    border-radius: 6px;
    margin-bottom: 8px;
    font-size: 12px;
    border: 1px solid rgba(255,255,255,0.08);
}
.chat-file-preview img {
    width: 28px;
    height: 28px;
    object-fit: cover;
    border-radius: 4px;
    margin: 0 !important;
}

#bottom{
    padding:16px 24px;
    background:var(--bg-header);
    border-top:1px solid var(--border-color);
}

#inputBox{
    max-width:850px;
    margin:auto;
    display:flex;
    flex-direction:column;
    background:var(--bg-input);
    padding:8px;
    border-radius:12px;
    border:1px solid var(--border-color);
    gap:6px;
}

.input-row{
    display:flex;
    gap:6px;
    align-items:center;
}

#input{
    flex:1;
    min-width:0;
    padding:8px 10px;
    border:0;
    outline:0;
    background:transparent;
    color:white;
    font-size:14px;
}

#input::placeholder{
    color:var(--text-muted);
}

button.action-btn{
    border:0;
    border-radius:8px;
    padding:8px 14px;
    background:var(--accent);
    color:white;
    cursor:pointer;
    font-size:13px;
    font-weight:600;
    transition:background 0.2s;
    display:flex;
    align-items:center;
    gap:6px;
    white-space:nowrap;
}

button.action-btn:hover{
    background:var(--accent-hover);
}

#imageButton{
    background:#0891b2;
}
#imageButton:hover{
    background:var(--cyan);
}

#fileButton{
    background:#27272a;
    color: #c084fc;
    padding: 8px 10px;
}
#fileButton:hover{
    background:#3f3f46;
}

/* ANGEHÄNGTE DATEI */
#attachmentArea {
    display: none;
    flex-wrap: wrap;
    gap: 6px;
    padding: 4px 6px;
}

.attachment-chip {
    display: flex;
    align-items: center;
    gap: 6px;
    background: #27272a;
    padding: 4px 8px;
    border-radius: 6px;
    font-size: 12px;
    color: #f4f4f5;
    border: 1px solid var(--border-color);
}

.attachment-chip button {
    background: none;
    border: none;
    color: #f87171;
    cursor: pointer;
    font-weight: bold;
}

/* DRAG & DROP OVERLAY */
#dropZoneOverlay {
    position: absolute;
    inset: 0;
    background: rgba(9, 9, 11, 0.85);
    border: 2px dashed var(--accent);
    z-index: 50;
    display: none;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 10px;
    color: #e9d5ff;
    font-size: 16px;
    font-weight: 500;
    backdrop-filter: blur(4px);
}

/* Responsive */
@media (max-width: 768px) {
    aside {
        position: absolute;
        height: 100%;
        transform: translateX(-100%);
    }
    aside.open {
        transform: translateX(0);
    }
    .menu-toggle {
        display: block;
    }
}

/* Scrollbar */
::-webkit-scrollbar{
    width:5px;
    height:5px;
}
::-webkit-scrollbar-track{
    background:transparent;
}
::-webkit-scrollbar-thumb{
    background:var(--border-color);
    border-radius:10px;
}
::-webkit-scrollbar-thumb:hover{
    background:#3f3f46;
}
</style>
</head>

<body>

<div id="app">

    <aside id="sidebar">
        <h2>
            <span>🧠 Gedächtnis</span>
            <button class="menu-toggle" onclick="toggleSidebar()" style="display:inline-block; font-size:11px; padding:2px 5px;">✕</button>
        </h2>
        <div class="memory-add-row">
            <input type="text" id="manualMemoryInput" placeholder="Erinnerung hinzufügen..." onkeydown="if(event.key==='Enter')addManualMemory()">
            <button onclick="addManualMemory()">+</button>
        </div>
        <ul id="memoryList"></ul>
        <div class="memory-hint">
            Hinterlege wichtige Infos oder Vorlieben für zukünftige Antworten.
        </div>
    </aside>

    <div class="main-container" id="mainContainer">
        
        <div id="dropZoneOverlay">
            📁 Datei hier ablegen...
        </div>

        <header>
            <div class="header-left">
                <button class="menu-toggle" onclick="toggleSidebar()">☰</button>
                <h1>🤖 Meine KI</h1>
            </div>
            <div class="header-right">
                <button class="clear-btn" onclick="clearChat()">Chat leeren</button>
                <span class="online">● Online</span>
            </div>
        </header>

        <main id="chat">
            <div class="message ai">
Hey! 👋 Ich bin deine aktualisierte KI. Wie kann ich dir heute weiterhelfen?
            </div>
        </main>

        <div id="bottom">
            <div id="inputBox">
                <div id="attachmentArea"></div>

                <div class="input-row">
                    <input type="file" id="fileInput" style="display:none" onchange="handleFileSelect(event)" multiple>
                    
                    <button class="action-btn" id="fileButton" onclick="document.getElementById('fileInput').click()" title="Datei hochladen">
                        📎
                    </button>

                    <input
                        id="input"
                        type="text"
                        placeholder="Nachricht eingeben..."
                        autocomplete="off"
                    >
                    <button class="action-btn" onclick="sendMessage()">
                        Senden
                    </button>
                    <button
                        class="action-btn"
                        id="imageButton"
                        onclick="createImage()"
                        title="Bild generieren"
                    >
                        🖼️
                    </button>
                </div>
            </div>
        </div>
    </div>
</div>

<script>
marked.setOptions({ breaks: true, gfm: true });

document.addEventListener("DOMContentLoaded", () => {
    document.getElementById("input").focus();
});

function toggleSidebar() {
    document.getElementById("sidebar").classList.toggle("open");
}

let memories = JSON.parse(localStorage.getItem("ai_memories")) || [];

function renderMemories() {
    const list = document.getElementById("memoryList");
    list.innerHTML = "";
    if (memories.length === 0) {
        list.innerHTML = `<li style="color:var(--text-muted); font-size:12px; text-align:center; margin-top:10px;">Keine Erinnerungen.</li>`;
        return;
    }
    memories.forEach((mem, index) => {
        const li = document.createElement("li");
        li.className = "memory-item";
        li.innerHTML = `<span>${escapeHTML(mem)}</span><button onclick="deleteMemory(${index})">✕</button>`;
        list.appendChild(li);
    });
}

function addMemory(text) {
    if (text && !memories.includes(text)) {
        memories.push(text);
        localStorage.setItem("ai_memories", JSON.stringify(memories));
        renderMemories();
    }
}

function addManualMemory() {
    const inputField = document.getElementById("manualMemoryInput");
    const val = inputField.value.trim();
    if (val) {
        addMemory(val);
        inputField.value = "";
    }
}

function deleteMemory(index) {
    memories.splice(index, 1);
    localStorage.setItem("ai_memories", JSON.stringify(memories));
    renderMemories();
}

function escapeHTML(str) {
    return str.replace(/[&<>'"]/g, tag => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', "'": '&#39;', '"': '&quot;' }[tag] || tag));
}

renderMemories();

let attachedFiles = [];
const mainContainer = document.getElementById("mainContainer");
const dropZoneOverlay = document.getElementById("dropZoneOverlay");

['dragenter', 'dragover'].forEach(eventName => {
    mainContainer.addEventListener(eventName, (e) => {
        e.preventDefault(); e.stopPropagation();
        dropZoneOverlay.style.display = "flex";
    }, false);
});

['dragleave', 'drop'].forEach(eventName => {
    mainContainer.addEventListener(eventName, (e) => {
        e.preventDefault(); e.stopPropagation();
        if (eventName === 'drop') handleFiles(e.dataTransfer.files);
        dropZoneOverlay.style.display = "none";
    }, false);
});

function handleFileSelect(e) {
    handleFiles(e.target.files);
    e.target.value = ""; 
}

function handleFiles(files) {
    for (let file of files) {
        const reader = new FileReader();
        reader.onload = function(event) {
            attachedFiles.push({ name: file.name, type: file.type, size: file.size, content: event.target.result });
            renderAttachments();
        };
        if (file.type.startsWith("image/") || file.type.includes("pdf")) {
            reader.readAsDataURL(file);
        } else {
            reader.readAsText(file);
        }
    }
}

function renderAttachments() {
    const area = document.getElementById("attachmentArea");
    area.innerHTML = "";
    if (attachedFiles.length === 0) {
        area.style.display = "none";
        return;
    }
    area.style.display = "flex";
    attachedFiles.forEach((file, index) => {
        const chip = document.createElement("div");
        chip.className = "attachment-chip";
        chip.innerHTML = `<span>📄 ${escapeHTML(file.name)}</span><button onclick="removeAttachment(${index})">✕</button>`;
        area.appendChild(chip);
    });
}

function removeAttachment(index) {
    attachedFiles.splice(index, 1);
    renderAttachments();
}

const input = document.getElementById("input");
const chat = document.getElementById("chat");
let conversation = [];

input.addEventListener("keydown", function(e){
    if(e.key === "Enter" && !e.shiftKey){
        e.preventDefault();
        sendMessage();
    }
});

function clearChat() {
    if(confirm("Chat wirklich leeren?")) {
        chat.innerHTML = `<div class="message ai">Hey! 👋 Chat zurückgesetzt.</div>`;
        conversation = [];
    }
}

function addMessage(text, type, files = [], isMarkdown = false){
    const div = document.createElement("div");
    div.className = "message " + type;

    if (files && files.length > 0) {
        files.forEach(f => {
            const filePreview = document.createElement("div");
            filePreview.className = "chat-file-preview";
            if (f.type && f.type.startsWith("image/")) {
                filePreview.innerHTML = `<img src="${f.content}" alt="Bild"><span>${escapeHTML(f.name)}</span>`;
            } else {
                filePreview.innerHTML = `<span>📎 ${escapeHTML(f.name)}</span>`;
            }
            div.appendChild(filePreview);
        });
    }

    const textSpan = document.createElement("div");
    if (isMarkdown) {
        textSpan.innerHTML = marked.parse(text);
        processCodeBlocks(textSpan);
    } else {
        textSpan.textContent = text;
    }
    div.appendChild(textSpan);

    chat.appendChild(div);
    chat.scrollTop = chat.scrollHeight;
    return div;
}

function processCodeBlocks(container) {
    container.querySelectorAll('pre code').forEach((block) => {
        hljs.highlightElement(block);
        const pre = block.parentElement;
        if (!pre.querySelector('.copy-code-btn')) {
            const copyBtn = document.createElement('button');
            copyBtn.className = 'copy-code-btn';
            copyBtn.textContent = 'Kopieren';
            copyBtn.onclick = () => {
                navigator.clipboard.writeText(block.textContent).then(() => {
                    copyBtn.textContent = 'Kopiert!';
                    setTimeout(() => copyBtn.textContent = 'Kopieren', 2000);
                });
            };
            pre.appendChild(copyBtn);
        }
    });
}

async function sendMessage(){
    const text = input.value.trim();
    if(!text && attachedFiles.length === 0) return;

    const currentFiles = [...attachedFiles];
    addMessage(text || "Dateien gesendet:", "user", currentFiles);
    
    input.value = "";
    attachedFiles = [];
    renderAttachments();

    const answerBox = addMessage('', "ai");
    answerBox.innerHTML = '<div class="typing-dots"><span></span><span></span><span></span></div>';

    let systemInstruction = "Du bist eine smarte, hilfsbereite und präzise KI.";
    if (memories.length > 0) {
        systemInstruction += "\n\nBekannte Fakten:\n- " + memories.join("\n- ");
    }

    let enhancedText = text;
    if (currentFiles.length > 0) {
        enhancedText += "\n\n[Anhänge:]\n";
        currentFiles.forEach(f => {
            enhancedText += `--- ${f.name} ---\n${f.content}\n\n`;
        });
    }

    conversation.push({ role: "user", content: enhancedText });

    try{
        const response = await fetch("/api/chat", {
            method: "POST",
            headers: { "Content-Type": "application/json" },
            body: JSON.stringify({ messages: [{ role: "system", content: systemInstruction }, ...conversation] })
        });
        const data = await response.json();
        if(!response.ok) throw new Error(data.error || "KI-Fehler");

        answerBox.innerHTML = "";
        const parsedContent = document.createElement("div");
        parsedContent.innerHTML = marked.parse(data.answer);
        processCodeBlocks(parsedContent);
        answerBox.appendChild(parsedContent);

        conversation.push({ role: "assistant", content: data.answer });
        chat.scrollTop = chat.scrollHeight;
    }catch(error){
        answerBox.textContent = "❌ Fehler:\n\n" + error.message;
    }
}

async function createImage(){
    let prompt = input.value.trim() || window.prompt("Was soll gezeichnet werden?");
    if(!prompt) return;

    addMessage("🖼️ " + prompt, "user");
    input.value = "";

    const imageBox = addMessage('', "ai");
    imageBox.innerHTML = '<div class="typing-dots"><span></span><span></span><span></span></div>';

    try{
        const response = await fetch("/api/image", {
            method: "POST",
            headers: { "Content-Type": "application/json" },
            body: JSON.stringify({ prompt })
        });
        const data = await response.json();
        if(!response.ok) throw new Error(data.error || "Fehler beim Generieren.");

        imageBox.textContent = "🎨 Fertig!";
        const img = document.createElement("img");
        img.src = data.image;
        img.alt = prompt;
        imageBox.appendChild(img);
        chat.scrollTop = chat.scrollHeight;
    }catch(error){
        imageBox.textContent = "❌ Fehler:\n\n" + error.message;
    }
}
</script>

</body>
</html>

Game Source: Meine KI - Smart & Modern

Creator: BlazeKoala99

Libraries: none

Complexity: complex (833 lines, 20.4 KB)

The full source code is displayed above on this page.

Remix Instructions

To remix this game, copy the source code above and modify it. Add a ARCADELAB header at the top with "remix_of: meine-ki-smart-modern-blazekoala99" to link back to the original. Then publish at arcadelab.ai/publish.