init: extract physio-tutor from qognio-bot-widget-template@d2c816f
Source files (src/) and rendered bundle (www/) extracted on 2026-04-29T01:35:48+02:00. Adds nginx:alpine Dockerfile + docker-compose.yml (Caddy-labels) so the bot runs stand-alone or as a per-customer template clone. Parent monorepo commit: d2c816f3edbc9760802a11b29ff4151c7aad4b46 Bot version: 2026-04-21
This commit is contained in:
commit
c5c6a6cd32
22 changed files with 8401 additions and 0 deletions
7
.dockerignore
Normal file
7
.dockerignore
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
.git
|
||||
.gitignore
|
||||
README.md
|
||||
bot.json
|
||||
src/
|
||||
docker-compose.yml
|
||||
*.md
|
||||
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
.DS_Store
|
||||
*.log
|
||||
*.tmp
|
||||
node_modules/
|
||||
13
Dockerfile
Normal file
13
Dockerfile
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
# Static-bundle bot — nginx:alpine serves www/ on port 80.
|
||||
FROM nginx:1.27-alpine
|
||||
|
||||
# nginx config: gzip + cache headers + index.html no-store
|
||||
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||
|
||||
# Static bundle
|
||||
COPY www/ /usr/share/nginx/html/
|
||||
|
||||
# Run as non-root via nginx's built-in unprivileged image features
|
||||
EXPOSE 80
|
||||
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
|
||||
CMD wget -q --spider http://127.0.0.1/index.html || exit 1
|
||||
67
README.md
Normal file
67
README.md
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
# Luna — PhysioTutor
|
||||
|
||||
Luna — dein KI-PhysioTutor. Gamified lernen mit Chat, Quiz, Flashcards und Fortschritts-Tracking. Läuft im deutschen Rechenzentrum.
|
||||
|
||||
```
|
||||
slug : physio-tutor
|
||||
version : 2026-04-21
|
||||
accent : #a855f7
|
||||
runtime : nginx:alpine (static bundle)
|
||||
template : qognio-bot-template-core (former qognio-bot-widget-template)
|
||||
```
|
||||
|
||||
## Layout
|
||||
|
||||
```
|
||||
.
|
||||
├── src/ source — config.yaml, welcome.html, curricula.json, etc.
|
||||
├── www/ rendered, directly servable static bundle
|
||||
├── Dockerfile nginx:alpine + www/ → port 80
|
||||
├── docker-compose.yml bot-host pattern (Caddy-labels, restart unless-stopped)
|
||||
├── nginx.conf gzip + cache + SPA fallback
|
||||
└── bot.json metadata + parent_core_commit
|
||||
```
|
||||
|
||||
## Run locally
|
||||
|
||||
```bash
|
||||
docker compose up --build
|
||||
# → http://localhost (you'll need to tweak ports for local-only use)
|
||||
```
|
||||
|
||||
## Re-render after upstream core changes
|
||||
|
||||
This repo only stores src + rendered output; the rendering engine lives in
|
||||
`qognio-bot-template-core`. To pull in core changes:
|
||||
|
||||
```bash
|
||||
cd /path/to/qognio-bot-template-core
|
||||
./scripts/render.sh physio-tutor --bot-repo /path/to/this/repo
|
||||
git -C /path/to/this/repo commit -am "render: refresh from core@<sha>"
|
||||
```
|
||||
|
||||
## Per-customer copy (template usage)
|
||||
|
||||
This repo is a **template**. To clone for a customer:
|
||||
|
||||
```bash
|
||||
git clone <this-repo> my-customer-physio-tutor
|
||||
cd my-customer-physio-tutor
|
||||
# tweak src/config.yaml (slug, bot_key_value, accent), src/welcome.html, src/curricula.json
|
||||
docker compose -f docker-compose.yml up --build
|
||||
```
|
||||
|
||||
## Deploy to qognio bot-host (.42 LXC pattern — legacy)
|
||||
|
||||
The bot-manager spawns LXC containers named after the slug. Push www/ via:
|
||||
|
||||
```bash
|
||||
ssh fmh@46.243.203.42
|
||||
sudo lxc file push /tmp/www/* physio-tutor/var/www/html/
|
||||
```
|
||||
|
||||
(Or run the docker-compose pattern on a Docker host — same network as Caddy.)
|
||||
|
||||
---
|
||||
|
||||
Generated by `qognio-bot-template-core/scripts/extract-to-repo.sh` on 2026-04-29T01:35:48+02:00.
|
||||
14
bot.json
Normal file
14
bot.json
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"slug": "physio-tutor",
|
||||
"name": "Luna",
|
||||
"title": "PhysioTutor",
|
||||
"tagline": "PhysioTutor",
|
||||
"description": "Luna — dein KI-PhysioTutor. Gamified lernen mit Chat, Quiz, Flashcards und Fortschritts-Tracking. Läuft im deutschen Rechenzentrum.",
|
||||
"version": "2026-04-21",
|
||||
"accent": "#a855f7",
|
||||
"extracted_from": "qognio-bot-widget-template",
|
||||
"parent_core_commit": "d2c816f3edbc9760802a11b29ff4151c7aad4b46",
|
||||
"extracted_at": "2026-04-29T01:35:48+02:00",
|
||||
"runtime": "nginx:alpine",
|
||||
"default_port": 80
|
||||
}
|
||||
20
docker-compose.yml
Normal file
20
docker-compose.yml
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
# Stand-alone bot container.
|
||||
# Designed for the "caddy" external network on the bot host (qognio pattern).
|
||||
# Override the hostname via SLUG env var if you reuse this template per customer.
|
||||
services:
|
||||
bot:
|
||||
build: .
|
||||
image: qognio/bot-physio-tutor:${TAG:-latest}
|
||||
container_name: bot-physio-tutor
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- caddy
|
||||
labels:
|
||||
caddy: "physio-tutor.on.qognio.com"
|
||||
caddy.reverse_proxy: "{{upstreams 80}}"
|
||||
qognio.bot.slug: "physio-tutor"
|
||||
qognio.bot.version: "2026-04-21"
|
||||
|
||||
networks:
|
||||
caddy:
|
||||
external: true
|
||||
27
nginx.conf
Normal file
27
nginx.conf
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
server {
|
||||
listen 80;
|
||||
server_name _;
|
||||
root /usr/share/nginx/html;
|
||||
index index.html;
|
||||
|
||||
# gzip
|
||||
gzip on;
|
||||
gzip_vary on;
|
||||
gzip_types text/css application/javascript application/json image/svg+xml text/plain;
|
||||
gzip_min_length 512;
|
||||
|
||||
# index.html: never cache (so welcome screen / wiring updates land instantly)
|
||||
location = /index.html {
|
||||
add_header Cache-Control "no-store, must-revalidate" always;
|
||||
}
|
||||
|
||||
# static assets: cache 1h
|
||||
location ~* \.(?:css|js|json|svg|png|jpe?g|webp|gif|ico|woff2?)$ {
|
||||
add_header Cache-Control "public, max-age=3600" always;
|
||||
try_files $uri =404;
|
||||
}
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
}
|
||||
8
src/check-badges.js
Normal file
8
src/check-badges.js
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
if (state.completedQuizzes >= 1) unlockBadge('first_quiz');
|
||||
if (state.maxQuizStreak >= 10) unlockBadge('10_quiz_streak');
|
||||
if (state.totalAnswers >= 100) unlockBadge('100_answers');
|
||||
if (state.maxStreak >= 7) unlockBadge('7_day_streak');
|
||||
if (state.completedCurricula.length >= 1) unlockBadge('curriculum_complete');
|
||||
const h = new Date().getHours();
|
||||
if (h >= 22) unlockBadge('night_owl');
|
||||
if (h < 7) unlockBadge('early_bird');
|
||||
650
src/cockpit-overlay/cockpit.css
Normal file
650
src/cockpit-overlay/cockpit.css
Normal file
|
|
@ -0,0 +1,650 @@
|
|||
/* Luna Cockpit v3 — purple #a855f7, dark, no external deps */
|
||||
|
||||
:root {
|
||||
--bg: #0a0a0f;
|
||||
--bg-2: #11111b;
|
||||
--bg-3: #1a1a26;
|
||||
--bg-4: #232336;
|
||||
--line: #2a2a3e;
|
||||
--text: #e8e8f0;
|
||||
--text-muted: #9090a8;
|
||||
--text-dim: #6a6a85;
|
||||
--accent: #a855f7;
|
||||
--accent-2: #c084fc;
|
||||
--accent-dark: #7c3aed;
|
||||
--accent-rgb: 168, 85, 247;
|
||||
--success: #10b981;
|
||||
--warn: #f59e0b;
|
||||
--danger: #ef4444;
|
||||
--info: #38bdf8;
|
||||
--shadow-sm: 0 1px 2px rgba(0,0,0,0.2);
|
||||
--shadow-md: 0 4px 12px rgba(0,0,0,0.3);
|
||||
--shadow-lg: 0 12px 32px rgba(0,0,0,0.5);
|
||||
--radius: 8px;
|
||||
--radius-lg: 12px;
|
||||
}
|
||||
|
||||
* { box-sizing: border-box; }
|
||||
html, body { margin: 0; padding: 0; }
|
||||
body {
|
||||
background: var(--bg);
|
||||
color: var(--text);
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Inter", "Helvetica Neue", Arial, sans-serif;
|
||||
font-size: 15px;
|
||||
line-height: 1.5;
|
||||
min-height: 100vh;
|
||||
}
|
||||
a { color: var(--accent-2); text-decoration: none; }
|
||||
a:hover { color: var(--accent); }
|
||||
button { font: inherit; cursor: pointer; border: none; background: transparent; color: inherit; }
|
||||
|
||||
.cockpit {
|
||||
max-width: 1280px;
|
||||
margin: 0 auto;
|
||||
padding: 1.5rem 1rem 6rem;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
/* ─── Topbar ────────────────────────────────────────────────────── */
|
||||
.topbar { display: flex; align-items: center; gap: 1rem; padding-bottom: 1.5rem; border-bottom: 1px solid var(--line); margin-bottom: 1.5rem; }
|
||||
.brand { display: flex; align-items: center; gap: 0.75rem; }
|
||||
.brand-icon {
|
||||
width: 40px; height: 40px;
|
||||
background: linear-gradient(135deg, var(--accent), var(--accent-dark));
|
||||
color: white; font-weight: 700; font-size: 22px;
|
||||
border-radius: var(--radius);
|
||||
display: grid; place-items: center;
|
||||
box-shadow: var(--shadow-md);
|
||||
}
|
||||
.brand-text { display: flex; flex-direction: column; }
|
||||
.brand-title { font-size: 1.2rem; font-weight: 600; }
|
||||
.brand-title small { color: var(--text-muted); font-weight: 400; margin-left: 0.25rem; }
|
||||
.brand-sub { font-size: 0.8rem; color: var(--text-muted); }
|
||||
.spacer { flex: 1; }
|
||||
.auth-state { display: flex; align-items: center; gap: 0.75rem; }
|
||||
.auth-user { color: var(--text-muted); font-size: 0.85rem; }
|
||||
|
||||
/* ─── Status block ─────────────────────────────────────────────── */
|
||||
.status-block {
|
||||
background: linear-gradient(135deg, rgba(var(--accent-rgb),0.08), rgba(var(--accent-rgb),0.02));
|
||||
border: 1px solid rgba(var(--accent-rgb), 0.25);
|
||||
border-radius: var(--radius-lg);
|
||||
padding: 1rem 1.25rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
.status-title { font-size: 0.85rem; color: var(--accent-2); font-weight: 600; margin: 0 0 0.5rem; text-transform: uppercase; letter-spacing: 0.04em; }
|
||||
.status-summary { font-size: 0.95rem; color: var(--text); }
|
||||
.status-summary .dim { color: var(--text-muted); }
|
||||
|
||||
/* ─── Top-level Space-Tabs ─────────────────────────────────────── */
|
||||
.space-tabs {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 0.5rem;
|
||||
background: var(--bg-2);
|
||||
padding: 0.5rem;
|
||||
border-radius: var(--radius-lg);
|
||||
border: 1px solid var(--line);
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
.space-tab {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.5rem;
|
||||
padding: 0.85rem;
|
||||
border-radius: var(--radius);
|
||||
color: var(--text-muted);
|
||||
transition: all 0.15s;
|
||||
font-size: 0.95rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
.space-tab[aria-selected="true"] {
|
||||
background: linear-gradient(135deg, rgba(var(--accent-rgb),0.18), rgba(var(--accent-rgb),0.05));
|
||||
color: var(--accent-2);
|
||||
box-shadow: inset 0 0 0 1px rgba(var(--accent-rgb), 0.4);
|
||||
}
|
||||
.space-tab:hover { background: var(--bg-3); color: var(--text); }
|
||||
.space-tab[aria-selected="true"]:hover { color: var(--accent-2); }
|
||||
.space-icon { font-size: 1.1rem; }
|
||||
|
||||
/* Each space-section is hidden unless data-active=true */
|
||||
.space { display: none; }
|
||||
.space[data-active="true"] { display: block; }
|
||||
.space-intro { margin-bottom: 1.25rem; }
|
||||
.space-intro h2 { margin: 0 0 0.4rem; font-size: 1.15rem; }
|
||||
.space-intro p { margin: 0; color: var(--text-muted); font-size: 0.9rem; }
|
||||
|
||||
/* ─── Folder tabs (Dokumente) ──────────────────────────────────── */
|
||||
.folder-tabs {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 0.5rem;
|
||||
background: var(--bg-2);
|
||||
padding: 0.5rem;
|
||||
border-radius: var(--radius-lg);
|
||||
border: 1px solid var(--line);
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.folder-tab {
|
||||
display: flex; flex-direction: column; align-items: center; gap: 0.25rem;
|
||||
padding: 0.75rem; border-radius: var(--radius);
|
||||
color: var(--text-muted);
|
||||
transition: background 0.15s, color 0.15s;
|
||||
}
|
||||
.folder-tab[aria-selected="true"] {
|
||||
background: rgba(var(--accent-rgb), 0.12);
|
||||
color: var(--text);
|
||||
box-shadow: inset 0 -2px 0 var(--accent);
|
||||
}
|
||||
.folder-tab:hover { background: var(--bg-3); color: var(--text); }
|
||||
.folder-icon { font-size: 1.25rem; }
|
||||
.folder-label { font-size: 0.85rem; font-weight: 500; }
|
||||
.folder-count {
|
||||
font-size: 0.7rem; background: var(--bg-3); color: var(--text-muted);
|
||||
padding: 0.1rem 0.5rem; border-radius: 10px; min-width: 24px; text-align: center;
|
||||
}
|
||||
.folder-tab[aria-selected="true"] .folder-count {
|
||||
background: rgba(var(--accent-rgb), 0.25);
|
||||
color: var(--accent-2);
|
||||
}
|
||||
|
||||
.folder-body {
|
||||
background: var(--bg-2); border: 1px solid var(--line);
|
||||
border-radius: var(--radius-lg); padding: 1.25rem;
|
||||
}
|
||||
.folder-help { font-size: 0.85rem; color: var(--text-muted); margin-bottom: 1rem; line-height: 1.5; }
|
||||
|
||||
.upload-zone {
|
||||
border: 2px dashed var(--line); border-radius: var(--radius-lg);
|
||||
padding: 2rem 1rem; text-align: center;
|
||||
transition: border-color 0.15s, background 0.15s;
|
||||
cursor: pointer; margin-bottom: 1rem;
|
||||
}
|
||||
.upload-zone:hover, .upload-zone:focus, .upload-zone[data-dragover="true"] {
|
||||
border-color: var(--accent); background: rgba(var(--accent-rgb), 0.05);
|
||||
outline: none;
|
||||
}
|
||||
.upload-cta { display: flex; flex-direction: column; align-items: center; gap: 0.4rem; }
|
||||
.upload-icon { font-size: 1.5rem; opacity: 0.6; }
|
||||
.upload-text { font-size: 1rem; color: var(--text); }
|
||||
.upload-hint { font-size: 0.8rem; color: var(--text-muted); }
|
||||
|
||||
.upload-progress { margin-top: 1rem; display: flex; flex-direction: column; gap: 0.5rem; }
|
||||
.upload-progress-row {
|
||||
display: flex; align-items: center; gap: 0.75rem;
|
||||
padding: 0.5rem 0.75rem; background: var(--bg-3);
|
||||
border-radius: var(--radius); font-size: 0.85rem;
|
||||
}
|
||||
.upload-progress-row .upload-bar { flex: 1; height: 4px; background: var(--bg); border-radius: 2px; overflow: hidden; }
|
||||
.upload-progress-row .upload-bar-fill { height: 100%; background: var(--accent); width: 0%; transition: width 0.2s; }
|
||||
|
||||
/* ─── Document list ────────────────────────────────────────────── */
|
||||
.doc-list { list-style: none; padding: 0; margin: 0; }
|
||||
.doc-row { display: flex; align-items: center; gap: 1rem; padding: 0.75rem; border-bottom: 1px solid var(--line); transition: background 0.1s; }
|
||||
.doc-row:hover { background: var(--bg-3); }
|
||||
.doc-row.is-disabled { opacity: 0.55; }
|
||||
.doc-meta { flex: 1; min-width: 0; }
|
||||
.doc-name { font-size: 0.95rem; color: var(--text); margin: 0; word-break: break-word; }
|
||||
.doc-info { font-size: 0.75rem; color: var(--text-muted); margin-top: 0.15rem; }
|
||||
.doc-info .ok { color: var(--success); }
|
||||
.doc-info .pending { color: var(--warn); }
|
||||
.doc-info .err { color: var(--danger); }
|
||||
.doc-actions { display: flex; gap: 0.5rem; align-items: center; }
|
||||
|
||||
.toggle {
|
||||
position: relative; width: 42px; height: 22px;
|
||||
background: var(--bg-3); border-radius: 11px;
|
||||
cursor: pointer; transition: background 0.15s; flex-shrink: 0;
|
||||
}
|
||||
.toggle::after {
|
||||
content: ""; position: absolute; top: 2px; left: 2px;
|
||||
width: 18px; height: 18px; background: var(--text-muted);
|
||||
border-radius: 50%; transition: left 0.15s, background 0.15s;
|
||||
}
|
||||
.toggle[data-on="true"] { background: rgba(var(--accent-rgb), 0.4); }
|
||||
.toggle[data-on="true"]::after { left: 22px; background: var(--accent); }
|
||||
|
||||
.btn-icon {
|
||||
width: 32px; height: 32px;
|
||||
display: grid; place-items: center;
|
||||
border-radius: var(--radius);
|
||||
color: var(--text-muted);
|
||||
transition: background 0.1s, color 0.1s;
|
||||
}
|
||||
.btn-icon:hover { background: var(--bg-3); color: var(--danger); }
|
||||
.btn-icon-x {
|
||||
width: 28px; height: 28px;
|
||||
border-radius: 50%;
|
||||
color: var(--text-muted);
|
||||
transition: background 0.1s, color 0.1s;
|
||||
}
|
||||
.btn-icon-x:hover { background: var(--bg-3); color: var(--text); }
|
||||
|
||||
.doc-empty, .empty-state {
|
||||
padding: 1.5rem; text-align: center;
|
||||
color: var(--text-muted); font-size: 0.9rem;
|
||||
}
|
||||
|
||||
/* ─── Buttons ──────────────────────────────────────────────────── */
|
||||
.btn-primary {
|
||||
background: var(--accent); color: white;
|
||||
padding: 0.6rem 1.25rem; border-radius: var(--radius);
|
||||
font-weight: 500; transition: background 0.15s;
|
||||
}
|
||||
.btn-primary:hover { background: var(--accent-dark); }
|
||||
.btn-primary:disabled { opacity: 0.5; cursor: not-allowed; }
|
||||
.btn-secondary {
|
||||
background: var(--bg-3); color: var(--text);
|
||||
padding: 0.5rem 1rem; border-radius: var(--radius);
|
||||
font-size: 0.85rem; border: 1px solid var(--line);
|
||||
}
|
||||
.btn-secondary:hover { background: var(--bg-4); }
|
||||
.btn-ghost {
|
||||
color: var(--text-muted); padding: 0.4rem 0.8rem;
|
||||
border-radius: var(--radius); font-size: 0.85rem;
|
||||
}
|
||||
.btn-ghost:hover { background: var(--bg-3); color: var(--text); }
|
||||
|
||||
/* ─── Login screen ─────────────────────────────────────────────── */
|
||||
.login-screen { display: grid; place-items: center; min-height: 60vh; }
|
||||
.login-card { width: 100%; max-width: 380px; background: var(--bg-2); border: 1px solid var(--line); border-radius: var(--radius-lg); padding: 2rem; box-shadow: var(--shadow-lg); }
|
||||
.login-card h2 { margin: 0 0 0.5rem; font-size: 1.3rem; }
|
||||
.login-hint { color: var(--text-muted); font-size: 0.9rem; margin: 0 0 1.5rem; }
|
||||
.login-form { display: flex; flex-direction: column; gap: 1rem; }
|
||||
.login-form label { display: flex; flex-direction: column; gap: 0.35rem; font-size: 0.85rem; color: var(--text-muted); }
|
||||
.login-form input { padding: 0.6rem 0.8rem; background: var(--bg); color: var(--text); border: 1px solid var(--line); border-radius: var(--radius); font-size: 0.95rem; }
|
||||
.login-form input:focus { outline: none; border-color: var(--accent); }
|
||||
.login-error { background: rgba(239, 68, 68, 0.1); border: 1px solid var(--danger); color: var(--danger); padding: 0.6rem 0.8rem; border-radius: var(--radius); font-size: 0.85rem; }
|
||||
|
||||
/* ─── Klausur cards ────────────────────────────────────────────── */
|
||||
.klausur-list { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 0.75rem; }
|
||||
.klausur-card {
|
||||
background: var(--bg-2); border: 1px solid var(--line);
|
||||
border-radius: var(--radius); padding: 1rem;
|
||||
display: flex; flex-direction: column; gap: 0.6rem;
|
||||
transition: border-color 0.15s, transform 0.1s;
|
||||
cursor: pointer;
|
||||
}
|
||||
.klausur-card:hover { border-color: var(--accent); transform: translateY(-1px); }
|
||||
.klausur-card-header { display: flex; justify-content: space-between; align-items: baseline; gap: 0.5rem; }
|
||||
.klausur-card-num { background: var(--accent); color: white; font-weight: 700; font-size: 0.75rem; padding: 0.15rem 0.5rem; border-radius: 10px; flex-shrink: 0; }
|
||||
.klausur-card-name { font-weight: 600; font-size: 0.95rem; flex: 1; word-break: break-word; }
|
||||
.klausur-card-topics { display: flex; flex-wrap: wrap; gap: 0.3rem; }
|
||||
.klausur-card-topic { background: rgba(var(--accent-rgb), 0.12); color: var(--accent-2); padding: 0.15rem 0.5rem; border-radius: 4px; font-size: 0.7rem; cursor: pointer; transition: background 0.1s; }
|
||||
.klausur-card-topic:hover { background: rgba(var(--accent-rgb), 0.25); }
|
||||
.klausur-card-notes { color: var(--text-muted); font-size: 0.8rem; font-style: italic; }
|
||||
.klausur-card-cta { margin-top: 0.25rem; font-size: 0.85rem; color: var(--accent-2); display: flex; align-items: center; gap: 0.3rem; }
|
||||
.klausur-card-actions { display: flex; gap: 0.4rem; margin-top: 0.4rem; flex-wrap: wrap; }
|
||||
.klausur-card-mini {
|
||||
font-size: 0.75rem; padding: 0.35rem 0.6rem; border-radius: 4px;
|
||||
background: var(--bg-4); color: var(--text-muted);
|
||||
border: 1px solid var(--line);
|
||||
transition: all 0.1s;
|
||||
}
|
||||
.klausur-card-mini:hover { background: var(--bg-3); color: var(--accent-2); border-color: var(--accent); }
|
||||
|
||||
/* ─── Lernen: Heatmap ──────────────────────────────────────────── */
|
||||
.heatmap-block { background: var(--bg-2); border: 1px solid var(--line); border-radius: var(--radius-lg); padding: 1.25rem; margin-bottom: 1.5rem; }
|
||||
.heatmap-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 1rem; flex-wrap: wrap; gap: 0.5rem; }
|
||||
.heatmap-title { margin: 0; font-size: 1rem; }
|
||||
.heatmap-legend { display: flex; align-items: center; gap: 0.4rem; font-size: 0.75rem; color: var(--text-muted); flex-wrap: wrap; }
|
||||
.legend-cell { display: inline-block; width: 18px; height: 12px; border-radius: 2px; }
|
||||
.legend-cell[data-level="0"] { background: var(--bg-4); }
|
||||
.legend-cell[data-level="2"] { background: rgba(245, 158, 11, 0.55); }
|
||||
.legend-cell[data-level="4"] { background: rgba(16, 185, 129, 0.6); }
|
||||
.legend-cell[data-level="5"] { background: rgba(16, 185, 129, 1); }
|
||||
|
||||
.heatmap-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: 0.5rem; }
|
||||
.heatmap-cell {
|
||||
background: var(--bg-3); border: 1px solid var(--line);
|
||||
border-radius: var(--radius); padding: 0.75rem;
|
||||
display: flex; flex-direction: column; gap: 0.3rem;
|
||||
position: relative; overflow: hidden;
|
||||
transition: transform 0.1s, border-color 0.1s;
|
||||
cursor: pointer;
|
||||
}
|
||||
.heatmap-cell:hover { transform: translateY(-1px); border-color: var(--accent); }
|
||||
.heatmap-cell .hc-bar {
|
||||
position: absolute; bottom: 0; left: 0; right: 0; height: 3px;
|
||||
background: var(--bg-4);
|
||||
}
|
||||
.heatmap-cell .hc-bar-fill {
|
||||
height: 100%; background: linear-gradient(to right, var(--warn), var(--success));
|
||||
transition: width 0.3s;
|
||||
}
|
||||
.heatmap-cell .hc-name { font-size: 0.85rem; color: var(--text); font-weight: 500; line-height: 1.3; }
|
||||
.heatmap-cell .hc-meta { font-size: 0.7rem; color: var(--text-muted); display: flex; justify-content: space-between; }
|
||||
.heatmap-cell[data-level="0"] .hc-bar-fill { background: var(--bg-4); width: 0%; }
|
||||
.heatmap-cell[data-level="1"] .hc-bar-fill { width: 20%; }
|
||||
.heatmap-cell[data-level="2"] .hc-bar-fill { width: 40%; }
|
||||
.heatmap-cell[data-level="3"] .hc-bar-fill { width: 60%; }
|
||||
.heatmap-cell[data-level="4"] .hc-bar-fill { width: 80%; }
|
||||
.heatmap-cell[data-level="5"] .hc-bar-fill { width: 100%; background: var(--success); }
|
||||
|
||||
/* ─── Lernen: Topic picker ─────────────────────────────────────── */
|
||||
.topic-picker { background: var(--bg-2); border: 1px solid var(--line); border-radius: var(--radius-lg); padding: 1.25rem; margin-bottom: 1.5rem; }
|
||||
.topic-picker-head { display: flex; justify-content: space-between; align-items: center; margin-bottom: 1rem; gap: 0.5rem; flex-wrap: wrap; }
|
||||
.topic-picker-head h3 { margin: 0; font-size: 1rem; }
|
||||
.select-mini {
|
||||
background: var(--bg-3); color: var(--text);
|
||||
border: 1px solid var(--line); border-radius: var(--radius);
|
||||
padding: 0.4rem 0.6rem; font-size: 0.85rem;
|
||||
}
|
||||
.select-mini:focus { outline: none; border-color: var(--accent); }
|
||||
.topic-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 0.5rem; }
|
||||
.topic-pill {
|
||||
background: var(--bg-3); border: 1px solid var(--line);
|
||||
border-radius: var(--radius); padding: 0.6rem 0.85rem;
|
||||
font-size: 0.85rem; color: var(--text);
|
||||
cursor: pointer; transition: all 0.1s;
|
||||
display: flex; flex-direction: column; gap: 0.25rem;
|
||||
text-align: left;
|
||||
}
|
||||
.topic-pill:hover { border-color: var(--accent); background: var(--bg-4); }
|
||||
.topic-pill .tp-label { font-weight: 500; }
|
||||
.topic-pill .tp-meta { font-size: 0.7rem; color: var(--text-muted); }
|
||||
.topic-pill[data-mastered="true"] { border-color: var(--success); }
|
||||
.topic-pill .tp-stars { color: var(--warn); font-size: 0.75rem; letter-spacing: 0.05em; }
|
||||
|
||||
/* ─── Minigame Launcher ────────────────────────────────────────── */
|
||||
.minigame-launcher {
|
||||
background: linear-gradient(135deg, rgba(var(--accent-rgb),0.06), var(--bg-2));
|
||||
border: 1px solid var(--accent);
|
||||
border-radius: var(--radius-lg);
|
||||
padding: 1.25rem;
|
||||
margin-bottom: 1.5rem;
|
||||
animation: fadein 0.2s ease-out;
|
||||
}
|
||||
.launcher-head { display: flex; justify-content: space-between; align-items: center; margin-bottom: 1rem; font-size: 0.95rem; }
|
||||
.launcher-head strong { color: var(--accent-2); }
|
||||
.launcher-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 0.75rem; }
|
||||
.minigame-card {
|
||||
background: var(--bg-3); border: 1px solid var(--line);
|
||||
border-radius: var(--radius); padding: 1rem;
|
||||
display: flex; flex-direction: column; gap: 0.4rem;
|
||||
text-align: left; transition: all 0.1s;
|
||||
}
|
||||
.minigame-card:hover { border-color: var(--accent); transform: translateY(-1px); background: var(--bg-4); }
|
||||
.mg-icon { font-size: 1.3rem; }
|
||||
.mg-title { font-weight: 600; font-size: 0.95rem; color: var(--text); }
|
||||
.mg-desc { font-size: 0.8rem; color: var(--text-muted); line-height: 1.35; }
|
||||
.mg-tag { font-size: 0.7rem; color: var(--accent-2); margin-top: 0.25rem; }
|
||||
|
||||
/* ─── Minigame Stage ──────────────────────────────────────────── */
|
||||
.minigame-stage {
|
||||
background: var(--bg-2); border: 1px solid var(--line);
|
||||
border-radius: var(--radius-lg); padding: 1.5rem;
|
||||
animation: fadein 0.2s ease-out;
|
||||
}
|
||||
.stage-head { display: flex; justify-content: space-between; align-items: center; margin-bottom: 1rem; gap: 0.5rem; flex-wrap: wrap; }
|
||||
.stage-title { margin: 0; font-size: 1.05rem; font-weight: 600; }
|
||||
.stage-progress { color: var(--text-muted); font-size: 0.85rem; }
|
||||
.stage-progress-bar { height: 4px; background: var(--bg-4); border-radius: 2px; overflow: hidden; margin-bottom: 1rem; }
|
||||
.stage-progress-bar-fill { height: 100%; background: var(--accent); transition: width 0.3s; }
|
||||
|
||||
.stage-loader { padding: 3rem 1rem; text-align: center; color: var(--text-muted); }
|
||||
.stage-spinner {
|
||||
display: inline-block; width: 28px; height: 28px;
|
||||
border: 3px solid var(--bg-4); border-top-color: var(--accent);
|
||||
border-radius: 50%; animation: spin 0.7s linear infinite;
|
||||
margin-bottom: 0.8rem;
|
||||
}
|
||||
@keyframes spin { to { transform: rotate(360deg); } }
|
||||
@keyframes fadein { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: translateY(0); } }
|
||||
|
||||
/* MCQ-style question block — used by Diagnose + Klausur quiz */
|
||||
.qa-card { background: var(--bg-3); border: 1px solid var(--line); border-radius: var(--radius-lg); padding: 1.25rem; }
|
||||
.qa-q { font-size: 1.05rem; font-weight: 500; margin: 0 0 1rem; line-height: 1.5; }
|
||||
.qa-options { display: flex; flex-direction: column; gap: 0.5rem; }
|
||||
.qa-option {
|
||||
text-align: left; padding: 0.75rem 1rem;
|
||||
background: var(--bg); border: 1px solid var(--line);
|
||||
border-radius: var(--radius); transition: all 0.1s;
|
||||
display: flex; gap: 0.75rem; align-items: flex-start;
|
||||
}
|
||||
.qa-option:not(:disabled):hover { border-color: var(--accent); background: var(--bg-4); }
|
||||
.qa-option:disabled { cursor: default; }
|
||||
.qa-option-letter { font-weight: 600; color: var(--text-muted); flex-shrink: 0; }
|
||||
.qa-option[data-state="correct"] { border-color: var(--success); background: rgba(16, 185, 129, 0.1); }
|
||||
.qa-option[data-state="wrong"] { border-color: var(--danger); background: rgba(239, 68, 68, 0.1); }
|
||||
.qa-option[data-state="reveal-correct"] { border-color: var(--success); }
|
||||
.qa-feedback {
|
||||
margin-top: 1rem; padding: 0.85rem 1rem; border-radius: var(--radius);
|
||||
background: var(--bg); border-left: 3px solid var(--accent);
|
||||
font-size: 0.9rem; line-height: 1.5;
|
||||
}
|
||||
.qa-feedback.correct { border-left-color: var(--success); }
|
||||
.qa-feedback.wrong { border-left-color: var(--danger); }
|
||||
.qa-feedback strong { color: var(--accent-2); }
|
||||
.qa-actions { display: flex; justify-content: flex-end; gap: 0.5rem; margin-top: 1rem; }
|
||||
|
||||
/* Klinikfall stage */
|
||||
.fall-patient { background: rgba(56, 189, 248, 0.1); border-left: 3px solid var(--info); padding: 1rem; border-radius: var(--radius); margin-bottom: 1rem; line-height: 1.6; }
|
||||
.fall-stage-num { color: var(--info); font-weight: 600; font-size: 0.85rem; margin-bottom: 0.25rem; }
|
||||
.fall-learning {
|
||||
margin-top: 0.75rem; padding: 0.6rem 0.85rem;
|
||||
background: rgba(168, 85, 247, 0.08);
|
||||
border-left: 3px solid var(--accent);
|
||||
border-radius: var(--radius);
|
||||
font-size: 0.85rem; color: var(--text-muted);
|
||||
}
|
||||
.fall-learning strong { color: var(--accent-2); }
|
||||
|
||||
/* Stimmt-das? */
|
||||
.stimmt-grid { display: flex; flex-direction: column; gap: 0.75rem; }
|
||||
.stimmt-card {
|
||||
background: var(--bg-3); border: 1px solid var(--line);
|
||||
border-radius: var(--radius); padding: 1rem;
|
||||
}
|
||||
.stimmt-statement { font-size: 0.95rem; margin-bottom: 0.75rem; line-height: 1.5; }
|
||||
.stimmt-buttons { display: flex; gap: 0.5rem; }
|
||||
.stimmt-btn {
|
||||
flex: 1; padding: 0.5rem 1rem;
|
||||
background: var(--bg); border: 1px solid var(--line);
|
||||
border-radius: var(--radius); font-weight: 500;
|
||||
transition: all 0.1s;
|
||||
}
|
||||
.stimmt-btn:not(:disabled):hover { border-color: var(--accent); background: var(--bg-4); }
|
||||
.stimmt-btn[data-state="correct"] { border-color: var(--success); background: rgba(16, 185, 129, 0.15); color: var(--success); }
|
||||
.stimmt-btn[data-state="wrong"] { border-color: var(--danger); background: rgba(239, 68, 68, 0.15); color: var(--danger); }
|
||||
.stimmt-explain { margin-top: 0.5rem; font-size: 0.85rem; color: var(--text-muted); padding-left: 0.6rem; border-left: 2px solid var(--line); }
|
||||
.stimmt-explain.correct { border-left-color: var(--success); }
|
||||
.stimmt-explain.wrong { border-left-color: var(--danger); }
|
||||
.trap-tag { display: inline-block; padding: 0.1rem 0.4rem; background: rgba(245, 158, 11, 0.15); color: var(--warn); border-radius: 3px; font-size: 0.7rem; margin-right: 0.4rem; }
|
||||
|
||||
/* Summary card for end of minigame */
|
||||
.summary-card {
|
||||
background: linear-gradient(135deg, rgba(var(--accent-rgb),0.1), var(--bg-3));
|
||||
border: 1px solid var(--accent);
|
||||
border-radius: var(--radius-lg);
|
||||
padding: 1.5rem;
|
||||
text-align: center;
|
||||
}
|
||||
.summary-stars {
|
||||
font-size: 2rem; letter-spacing: 0.1em; margin-bottom: 0.5rem;
|
||||
color: var(--warn);
|
||||
}
|
||||
.summary-text { font-size: 1rem; margin-bottom: 0.25rem; }
|
||||
.summary-meta { color: var(--text-muted); font-size: 0.85rem; margin-bottom: 1rem; }
|
||||
.summary-actions { display: flex; gap: 0.5rem; justify-content: center; flex-wrap: wrap; }
|
||||
|
||||
/* ─── Persona-Form ─────────────────────────────────────────────── */
|
||||
.persona-form { background: var(--bg-2); border: 1px solid var(--line); border-radius: var(--radius-lg); padding: 1.25rem; }
|
||||
.persona-label { display: flex; flex-direction: column; gap: 0.5rem; font-size: 0.85rem; color: var(--text-muted); margin-bottom: 1rem; }
|
||||
.persona-form textarea {
|
||||
background: var(--bg); color: var(--text);
|
||||
border: 1px solid var(--line); border-radius: var(--radius);
|
||||
padding: 0.75rem 1rem; font-family: inherit; font-size: 0.95rem;
|
||||
line-height: 1.5; resize: vertical; min-height: 200px;
|
||||
}
|
||||
.persona-form textarea:focus { outline: none; border-color: var(--accent); }
|
||||
.persona-actions { display: flex; justify-content: space-between; align-items: center; gap: 1rem; }
|
||||
.persona-status { color: var(--text-muted); font-size: 0.85rem; }
|
||||
.persona-status.saved { color: var(--success); }
|
||||
.hint-list { color: var(--text-muted); font-size: 0.85rem; line-height: 1.6; margin: 0.5rem 0 1rem; padding-left: 1.5rem; }
|
||||
.hint-list li { margin-bottom: 0.25rem; }
|
||||
|
||||
/* ─── Telegram-Pairing Block ──────────────────────────────────── */
|
||||
.telegram-block {
|
||||
background: var(--bg-2); border: 1px solid var(--line);
|
||||
border-radius: var(--radius-lg); padding: 1.25rem; margin-top: 1.5rem;
|
||||
}
|
||||
.tg-head { display: flex; justify-content: space-between; align-items: center; margin-bottom: 0.5rem; gap: 0.5rem; }
|
||||
.tg-head h3 { margin: 0; font-size: 1rem; }
|
||||
.tg-hint { color: var(--text-muted); font-size: 0.9rem; margin: 0 0 1rem; line-height: 1.5; }
|
||||
.tg-badge {
|
||||
font-size: 0.75rem; padding: 0.2rem 0.6rem; border-radius: 10px;
|
||||
background: var(--bg-3); color: var(--text-muted);
|
||||
border: 1px solid var(--line);
|
||||
}
|
||||
.tg-badge.linked { background: rgba(16,185,129,0.15); color: var(--success); border-color: var(--success); }
|
||||
.tg-badge.unlinked { background: var(--bg-3); color: var(--text-muted); }
|
||||
.tg-link-area { display: flex; gap: 0.5rem; flex-wrap: wrap; }
|
||||
.tg-code-display { margin-top: 1rem; }
|
||||
.tg-code-line {
|
||||
background: var(--bg); border: 1px solid var(--accent);
|
||||
border-radius: var(--radius); padding: 0.85rem 1rem;
|
||||
font-size: 1.1rem; letter-spacing: 0.05em;
|
||||
margin: 0.5rem 0;
|
||||
user-select: all;
|
||||
display: flex; justify-content: center;
|
||||
}
|
||||
.tg-code-line code { color: var(--accent-2); font-weight: 600; font-size: 1.15rem; letter-spacing: 0.1em; }
|
||||
.tg-code-hint { font-size: 0.8rem; color: var(--text-muted); margin: 0.25rem 0 0; }
|
||||
|
||||
/* ─── Chat Dock (resizable + MD) ──────────────────────────────── */
|
||||
.chat-dock {
|
||||
position: fixed;
|
||||
bottom: 1rem; right: 1rem;
|
||||
width: 460px; height: 640px;
|
||||
min-width: 340px; min-height: 420px;
|
||||
max-width: calc(100vw - 2rem);
|
||||
max-height: calc(100vh - 2rem);
|
||||
background: var(--bg-2); border: 1px solid var(--line);
|
||||
border-radius: var(--radius-lg);
|
||||
box-shadow: var(--shadow-lg);
|
||||
display: none; flex-direction: column;
|
||||
z-index: 100;
|
||||
}
|
||||
.chat-dock[data-open="true"] { display: flex; }
|
||||
.dock-resize {
|
||||
position: absolute;
|
||||
top: 0; left: 0;
|
||||
width: 14px; height: 14px;
|
||||
cursor: nwse-resize;
|
||||
background: linear-gradient(135deg, transparent 35%, var(--text-dim) 35%, var(--text-dim) 50%, transparent 50%);
|
||||
border-top-left-radius: var(--radius-lg);
|
||||
z-index: 1;
|
||||
}
|
||||
.dock-resize:hover { background-color: rgba(var(--accent-rgb), 0.1); }
|
||||
.dock-head { padding: 0.75rem 1rem; border-bottom: 1px solid var(--line); display: flex; align-items: center; gap: 0.5rem; }
|
||||
.dock-title { font-weight: 600; }
|
||||
.dock-sub { color: var(--text-muted); font-size: 0.85rem; flex: 1; }
|
||||
.dock-reset, .dock-collapse { width: 28px; height: 28px; border-radius: var(--radius); color: var(--text-muted); }
|
||||
.dock-reset:hover, .dock-collapse:hover { background: var(--bg-3); color: var(--text); }
|
||||
.dock-box { flex: 1; padding: 0.75rem 1rem; overflow-y: auto; display: flex; flex-direction: column; gap: 0.75rem; }
|
||||
|
||||
.dock-msg {
|
||||
max-width: 90%;
|
||||
padding: 0.7rem 0.9rem;
|
||||
border-radius: var(--radius);
|
||||
font-size: 0.92rem;
|
||||
line-height: 1.55;
|
||||
word-break: break-word;
|
||||
}
|
||||
.dock-msg.user { align-self: flex-end; background: var(--accent-dark); color: white; white-space: pre-wrap; }
|
||||
.dock-msg.assistant { align-self: flex-start; background: var(--bg-3); color: var(--text); }
|
||||
.dock-msg.assistant.typing { font-style: italic; opacity: 0.7; }
|
||||
|
||||
/* MD content inside chat */
|
||||
.dock-md p { margin: 0 0 0.6rem; }
|
||||
.dock-md p:last-child { margin-bottom: 0; }
|
||||
.dock-md h1, .dock-md h2, .dock-md h3 { margin: 0.6rem 0 0.4rem; line-height: 1.3; }
|
||||
.dock-md h1 { font-size: 1.05rem; color: var(--accent-2); }
|
||||
.dock-md h2 { font-size: 1rem; color: var(--accent-2); }
|
||||
.dock-md h3 { font-size: 0.95rem; color: var(--text); }
|
||||
.dock-md ul, .dock-md ol { margin: 0.4rem 0 0.6rem; padding-left: 1.4rem; }
|
||||
.dock-md li { margin-bottom: 0.2rem; }
|
||||
.dock-md code { background: var(--bg); padding: 0.1rem 0.35rem; border-radius: 3px; font-size: 0.85em; color: var(--accent-2); }
|
||||
.dock-md pre { background: var(--bg); padding: 0.7rem 0.9rem; border-radius: var(--radius); overflow-x: auto; margin: 0.5rem 0; }
|
||||
.dock-md pre code { background: none; padding: 0; color: var(--text); }
|
||||
.dock-md strong { color: var(--text); }
|
||||
.dock-md em { color: var(--text-muted); font-style: italic; }
|
||||
.dock-md a { color: var(--accent-2); text-decoration: underline; }
|
||||
.dock-md table.md-table { border-collapse: collapse; margin: 0.5rem 0; font-size: 0.85em; width: auto; }
|
||||
.dock-md table.md-table th, .dock-md table.md-table td { border: 1px solid var(--line); padding: 0.3rem 0.6rem; }
|
||||
.dock-md table.md-table th { background: var(--bg); }
|
||||
.dock-md blockquote { border-left: 3px solid var(--accent); padding-left: 0.8rem; margin: 0.5rem 0; color: var(--text-muted); }
|
||||
|
||||
.dock-msg .sources {
|
||||
margin-top: 0.6rem; padding-top: 0.5rem; border-top: 1px solid var(--line);
|
||||
font-size: 0.72rem; color: var(--text-muted);
|
||||
}
|
||||
.dock-msg .sources .src-tag {
|
||||
display: inline-block; background: rgba(var(--accent-rgb), 0.15); color: var(--accent-2);
|
||||
padding: 0.1rem 0.4rem; border-radius: 3px; margin: 0.15rem 0.25rem 0 0; font-size: 0.7rem;
|
||||
}
|
||||
|
||||
/* Inline structured renderer (quiz/flashcards/case in chat) */
|
||||
.dock-struct {
|
||||
background: var(--bg);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: var(--radius);
|
||||
padding: 0.85rem;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
.dock-struct .ds-head { font-weight: 600; color: var(--accent-2); margin-bottom: 0.4rem; font-size: 0.85rem; }
|
||||
.dock-struct .ds-q { font-size: 0.9rem; margin-bottom: 0.5rem; }
|
||||
.dock-struct .ds-options { display: flex; flex-direction: column; gap: 0.3rem; }
|
||||
.dock-struct .ds-option {
|
||||
text-align: left; padding: 0.4rem 0.6rem;
|
||||
background: var(--bg-3); border: 1px solid var(--line);
|
||||
border-radius: 4px; font-size: 0.85rem;
|
||||
}
|
||||
.dock-struct .ds-option:hover:not(:disabled) { border-color: var(--accent); }
|
||||
.dock-struct .ds-option[data-state="correct"] { border-color: var(--success); background: rgba(16, 185, 129, 0.1); }
|
||||
.dock-struct .ds-option[data-state="wrong"] { border-color: var(--danger); background: rgba(239, 68, 68, 0.1); }
|
||||
|
||||
.dock-form { border-top: 1px solid var(--line); padding: 0.5rem; display: flex; gap: 0.5rem; }
|
||||
.dock-form textarea {
|
||||
flex: 1; resize: none;
|
||||
background: var(--bg); color: var(--text);
|
||||
border: 1px solid var(--line); border-radius: var(--radius);
|
||||
padding: 0.5rem 0.7rem; font-size: 0.9rem;
|
||||
max-height: 140px; min-height: 36px;
|
||||
font-family: inherit;
|
||||
}
|
||||
.dock-form textarea:focus { outline: none; border-color: var(--accent); }
|
||||
.btn-send { background: var(--accent); color: white; width: 40px; border-radius: var(--radius); font-weight: 700; font-size: 1.1rem; }
|
||||
.btn-send:hover { background: var(--accent-dark); }
|
||||
|
||||
.dock-open {
|
||||
position: fixed; bottom: 1rem; right: 1rem;
|
||||
background: var(--accent); color: white;
|
||||
padding: 0.7rem 1.2rem; border-radius: 24px;
|
||||
font-weight: 500; box-shadow: var(--shadow-lg);
|
||||
display: flex; align-items: center; gap: 0.5rem;
|
||||
z-index: 99;
|
||||
}
|
||||
.dock-open:hover { background: var(--accent-dark); }
|
||||
.dock-open[hidden] { display: none; }
|
||||
.dock-open-dot { width: 8px; height: 8px; background: var(--success); border-radius: 50%; display: inline-block; }
|
||||
|
||||
/* ─── Footer ───────────────────────────────────────────────────── */
|
||||
.footer { margin-top: 2rem; padding-top: 1rem; border-top: 1px solid var(--line); text-align: center; font-size: 0.8rem; color: var(--text-muted); }
|
||||
|
||||
/* ─── Toasts ───────────────────────────────────────────────────── */
|
||||
.toast-stack { position: fixed; top: 1rem; right: 1rem; display: flex; flex-direction: column; gap: 0.5rem; z-index: 200; pointer-events: none; }
|
||||
.toast { padding: 0.6rem 1rem; background: var(--bg-2); border: 1px solid var(--line); border-radius: var(--radius); box-shadow: var(--shadow-md); color: var(--text); font-size: 0.85rem; pointer-events: auto; animation: toast-in 0.2s ease-out; }
|
||||
.toast.error { border-color: var(--danger); color: var(--danger); }
|
||||
.toast.success { border-color: var(--success); color: var(--success); }
|
||||
@keyframes toast-in { from { transform: translateX(20px); opacity: 0; } to { transform: translateX(0); opacity: 1; } }
|
||||
|
||||
/* Mobile */
|
||||
@media (max-width: 800px) {
|
||||
.space-tabs, .folder-tabs { grid-template-columns: repeat(2, 1fr); }
|
||||
.space-tab { font-size: 0.85rem; padding: 0.7rem 0.4rem; }
|
||||
.chat-dock { width: calc(100vw - 2rem); height: calc(100vh - 6rem); right: 1rem; bottom: 1rem; }
|
||||
.dock-resize { display: none; }
|
||||
}
|
||||
1099
src/cockpit-overlay/cockpit.js
Normal file
1099
src/cockpit-overlay/cockpit.js
Normal file
File diff suppressed because it is too large
Load diff
290
src/cockpit-overlay/index.html
Normal file
290
src/cockpit-overlay/index.html
Normal file
|
|
@ -0,0 +1,290 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Luna · Tutor-Cockpit</title>
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover">
|
||||
<meta name="theme-color" content="#0a0a0f">
|
||||
<meta name="description" content="Luna — dein Tutor-Cockpit. Eigene Unterlagen, Klausur-Plan, Kompetenz-Diagnose, Klinikfälle.">
|
||||
<link rel="stylesheet" href="cockpit.css">
|
||||
<script>
|
||||
window.__LUNA_KEY__ = '{{BOT_KEY_VALUE}}';
|
||||
window.__BOT_SLUG__ = '{{BOT_SLUG}}';
|
||||
window.__BOT_ID__ = '{{BOT_ID}}';
|
||||
window.__API_BASE__ = 'https://api.qognio.com';
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="cockpit" role="application" aria-label="Luna Cockpit">
|
||||
|
||||
<header class="topbar">
|
||||
<div class="brand">
|
||||
<span class="brand-icon" aria-hidden="true">L</span>
|
||||
<div class="brand-text">
|
||||
<span class="brand-title">Luna <small>Cockpit</small></span>
|
||||
<span class="brand-sub">Eigene Unterlagen · Klausur-Plan · Kompetenz-Diagnose</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="spacer"></div>
|
||||
<div id="auth-state" class="auth-state">
|
||||
<span id="auth-user" class="auth-user"></span>
|
||||
<button id="auth-logout" class="btn-secondary" type="button" hidden>Abmelden</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- Login overlay -->
|
||||
<section id="login-screen" class="login-screen" hidden>
|
||||
<div class="login-card">
|
||||
<h2>Bei Luna anmelden</h2>
|
||||
<p class="login-hint">Damit Luna deine eigenen Dokumente verwenden kann, brauche ich deinen Login.</p>
|
||||
<form id="login-form" class="login-form">
|
||||
<label>E-Mail<input type="email" id="login-email" autocomplete="email" required></label>
|
||||
<label>Passwort<input type="password" id="login-pw" autocomplete="current-password" required></label>
|
||||
<button type="submit" class="btn-primary">Anmelden</button>
|
||||
<div id="login-error" class="login-error" role="alert" hidden></div>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<main id="main" class="main" hidden>
|
||||
<!-- Status banner -->
|
||||
<section class="status-block">
|
||||
<h2 class="status-title">Was Luna gerade weiß</h2>
|
||||
<div id="status-summary" class="status-summary">Lade…</div>
|
||||
</section>
|
||||
|
||||
<!-- Top-level Tabs -->
|
||||
<nav class="space-tabs" role="tablist" aria-label="Cockpit-Bereiche">
|
||||
<button class="space-tab" role="tab" aria-selected="true" data-space="dokumente">
|
||||
<span class="space-icon">📁</span><span class="space-label">Dokumente</span>
|
||||
</button>
|
||||
<button class="space-tab" role="tab" aria-selected="false" data-space="klausuren">
|
||||
<span class="space-icon">📋</span><span class="space-label">Klausur-Plan</span>
|
||||
</button>
|
||||
<button class="space-tab" role="tab" aria-selected="false" data-space="lernen">
|
||||
<span class="space-icon">🧠</span><span class="space-label">Lernen</span>
|
||||
</button>
|
||||
<button class="space-tab" role="tab" aria-selected="false" data-space="steuern">
|
||||
<span class="space-icon">🎛</span><span class="space-label">Bot steuern</span>
|
||||
</button>
|
||||
</nav>
|
||||
|
||||
<!-- ─── Space: Dokumente ─── -->
|
||||
<section class="space" data-space="dokumente" data-active="true">
|
||||
<nav class="folder-tabs" role="tablist" aria-label="Dokumenten-Ordner">
|
||||
<button class="folder-tab" role="tab" aria-selected="true" data-folder="curriculum">
|
||||
<span class="folder-icon">📚</span>
|
||||
<span class="folder-label">Curriculum</span>
|
||||
<span class="folder-count" data-count="curriculum">0</span>
|
||||
</button>
|
||||
<button class="folder-tab" role="tab" aria-selected="false" data-folder="official">
|
||||
<span class="folder-icon">🏛️</span>
|
||||
<span class="folder-label">Offizielle Doks</span>
|
||||
<span class="folder-count" data-count="official">0</span>
|
||||
</button>
|
||||
<button class="folder-tab" role="tab" aria-selected="false" data-folder="own">
|
||||
<span class="folder-icon">📝</span>
|
||||
<span class="folder-label">Eigene Notizen</span>
|
||||
<span class="folder-count" data-count="own">0</span>
|
||||
</button>
|
||||
<button class="folder-tab" role="tab" aria-selected="false" data-folder="role">
|
||||
<span class="folder-icon">🎯</span>
|
||||
<span class="folder-label">Schwerpunkt</span>
|
||||
<span class="folder-count" data-count="role">0</span>
|
||||
</button>
|
||||
</nav>
|
||||
<section class="folder-body">
|
||||
<div id="folder-help" class="folder-help"></div>
|
||||
<div class="upload-zone" id="upload-zone" tabindex="0">
|
||||
<input type="file" id="file-input" hidden multiple
|
||||
accept=".pdf,.txt,.md,.csv,.json,.png,.jpg,.jpeg,.webp">
|
||||
<div class="upload-cta">
|
||||
<span class="upload-icon">⬆️</span>
|
||||
<span class="upload-text">Datei hier ablegen oder klicken</span>
|
||||
<span class="upload-hint">PDF, Markdown, Text — max 20 MB</span>
|
||||
</div>
|
||||
<div id="upload-progress" class="upload-progress" hidden></div>
|
||||
</div>
|
||||
<ul id="doc-list" class="doc-list" aria-live="polite"></ul>
|
||||
<div id="doc-empty" class="doc-empty" hidden>Noch nichts hier — leg los mit dem ersten Upload.</div>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<!-- ─── Space: Klausuren ─── -->
|
||||
<section class="space" data-space="klausuren">
|
||||
<div class="space-intro">
|
||||
<h2>📋 Dein Klausur-Plan</h2>
|
||||
<p>Aus deinem Curriculum extrahiert. Klick auf eine Klausur, um Luna gezielt darauf vorzubereiten.</p>
|
||||
</div>
|
||||
<div id="klausur-list" class="klausur-list">
|
||||
<div class="empty-state" id="klausur-empty">Noch keine Klausuren erkannt. Lade ein Klausurplan-Dokument im Curriculum-Ordner hoch.</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ─── Space: Lernen ─── -->
|
||||
<section class="space" data-space="lernen">
|
||||
<div class="space-intro">
|
||||
<h2>🧠 Lernen & Üben</h2>
|
||||
<p>Wähle ein Thema aus deinem Klausur-Plan oder Curriculum, dann starte ein Mini-Spiel.</p>
|
||||
</div>
|
||||
|
||||
<!-- Heatmap: mastery per topic -->
|
||||
<section class="heatmap-block">
|
||||
<div class="heatmap-header">
|
||||
<h3 class="heatmap-title">Dein aktueller Stand</h3>
|
||||
<div class="heatmap-legend">
|
||||
<span class="legend-cell" data-level="0"></span><span>0 noch nicht</span>
|
||||
<span class="legend-cell" data-level="2"></span><span>2 in Arbeit</span>
|
||||
<span class="legend-cell" data-level="4"></span><span>4 stark</span>
|
||||
<span class="legend-cell" data-level="5"></span><span>5 gemeistert</span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="heatmap-grid" class="heatmap-grid">
|
||||
<div class="empty-state">Klick auf ein Thema unten, um deinen Stand erstmals zu erfassen.</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Topic picker -->
|
||||
<section class="topic-picker">
|
||||
<div class="topic-picker-head">
|
||||
<h3>Themen</h3>
|
||||
<select id="topic-source" class="select-mini">
|
||||
<option value="klausur">aus Klausur-Plan</option>
|
||||
<option value="physiologie-uke">Physiologie (UKE)</option>
|
||||
<option value="physiotherapie-aprv">Physiotherapie APrV</option>
|
||||
<option value="pflegeschule-flensburg">Pflege Flensburg</option>
|
||||
<option value="medizinische-terminologie">Med. Terminologie</option>
|
||||
<option value="anatomie-grundlagen">Anatomie Grundlagen</option>
|
||||
</select>
|
||||
</div>
|
||||
<div id="topic-grid" class="topic-grid"></div>
|
||||
</section>
|
||||
|
||||
<!-- Minigame Launcher (visible nachdem Topic gewählt) -->
|
||||
<section id="minigame-launcher" class="minigame-launcher" hidden>
|
||||
<div class="launcher-head">
|
||||
<span>Was willst du mit <strong id="launcher-topic">…</strong> machen?</span>
|
||||
<button class="btn-icon-x" id="launcher-close" aria-label="Schließen">✕</button>
|
||||
</div>
|
||||
<div class="launcher-grid">
|
||||
<button class="minigame-card" data-game="diagnose">
|
||||
<span class="mg-icon">🩺</span>
|
||||
<span class="mg-title">Kompetenz-Check</span>
|
||||
<span class="mg-desc">5 adaptive Fragen — Luna ermittelt deinen Stand.</span>
|
||||
<span class="mg-tag">Diagnose · 3-5 min</span>
|
||||
</button>
|
||||
<button class="minigame-card" data-game="klinikfall">
|
||||
<span class="mg-icon">🏥</span>
|
||||
<span class="mg-title">Klinikfall</span>
|
||||
<span class="mg-desc">Patient*in mit Symptomen — du triffst 3 Entscheidungen.</span>
|
||||
<span class="mg-tag">Anwendung · 5-7 min</span>
|
||||
</button>
|
||||
<button class="minigame-card" data-game="stimmt-das">
|
||||
<span class="mg-icon">🤔</span>
|
||||
<span class="mg-title">Stimmt das?</span>
|
||||
<span class="mg-desc">6 Aussagen — wahr oder Falle? Trainiert Misconceptions.</span>
|
||||
<span class="mg-tag">Retrieval · 3 min</span>
|
||||
</button>
|
||||
<button class="minigame-card" data-game="quiz-classic">
|
||||
<span class="mg-icon">📝</span>
|
||||
<span class="mg-title">Klassisches Quiz</span>
|
||||
<span class="mg-desc">10 Multiple-Choice-Fragen via Luna's Quiz-Engine.</span>
|
||||
<span class="mg-tag">Übung · 5 min</span>
|
||||
</button>
|
||||
<button class="minigame-card" data-game="flashcards">
|
||||
<span class="mg-icon">🃏</span>
|
||||
<span class="mg-title">Karteikarten</span>
|
||||
<span class="mg-desc">Spaced-Repetition mit Eigenbewertung pro Karte.</span>
|
||||
<span class="mg-tag">Memorisierung · 3-5 min</span>
|
||||
</button>
|
||||
<button class="minigame-card" data-game="explain">
|
||||
<span class="mg-icon">🎓</span>
|
||||
<span class="mg-title">Erklär's mir</span>
|
||||
<span class="mg-desc">Du erklärst — Luna gibt Feedback zu Klarheit + Vollständigkeit.</span>
|
||||
<span class="mg-tag">Articulation · 5 min</span>
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Minigame Stage (gefüllt während ein Spiel läuft) -->
|
||||
<section id="minigame-stage" class="minigame-stage" hidden></section>
|
||||
</section>
|
||||
|
||||
<!-- ─── Space: Steuern ─── -->
|
||||
<section class="space" data-space="steuern">
|
||||
<div class="space-intro">
|
||||
<h2>🎛 Bot steuern</h2>
|
||||
<p>Schreibe hier rein, wie Luna mit dir arbeiten soll. Beispiele:</p>
|
||||
<ul class="hint-list">
|
||||
<li>„Antworte ausführlicher und gib mehr klinische Beispiele"</li>
|
||||
<li>„Stell mir öfter sokratische Rückfragen statt direkt zu erklären"</li>
|
||||
<li>„Ich lerne lieber visuell — beschreib Diagramme detailliert"</li>
|
||||
<li>„Fokus auf Klausur 1 — ignoriere andere Themen wenn ich nicht explizit frage"</li>
|
||||
</ul>
|
||||
</div>
|
||||
<form id="persona-form" class="persona-form">
|
||||
<label class="persona-label">
|
||||
Deine Anweisungen an Luna
|
||||
<textarea id="persona-overrides" rows="10"
|
||||
placeholder="Schreibe hier deine Wünsche…"></textarea>
|
||||
</label>
|
||||
<div class="persona-actions">
|
||||
<span id="persona-status" class="persona-status">—</span>
|
||||
<button type="submit" class="btn-primary">Speichern</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<!-- Telegram-Pairing -->
|
||||
<section class="telegram-block">
|
||||
<div class="tg-head">
|
||||
<h3>📱 Mobil mit Telegram</h3>
|
||||
<span id="tg-status-badge" class="tg-badge">prüfe…</span>
|
||||
</div>
|
||||
<p class="tg-hint">
|
||||
Verbinde diesen Cockpit-Account mit dem
|
||||
<a href="https://t.me/qognioLunaBot" target="_blank" rel="noopener">@qognioLunaBot</a>
|
||||
auf Telegram. Dann kannst du Luna unterwegs fragen — und sie nutzt deinen Lernstand & Dokumente.
|
||||
</p>
|
||||
<div id="tg-link-area" class="tg-link-area">
|
||||
<button id="tg-generate" class="btn-primary" type="button">Code generieren</button>
|
||||
<button id="tg-unlink" class="btn-secondary" type="button" hidden>Verbindung lösen</button>
|
||||
</div>
|
||||
<div id="tg-code-display" class="tg-code-display" hidden>
|
||||
<p>Schicke diese Nachricht an
|
||||
<a href="https://t.me/qognioLunaBot" target="_blank" rel="noopener">@qognioLunaBot</a>
|
||||
auf Telegram:</p>
|
||||
<pre class="tg-code-line"><code id="tg-code-text"></code></pre>
|
||||
<p class="tg-code-hint">Code läuft in <span id="tg-code-ttl">15</span> Min ab.</p>
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<!-- Chat-Dock (resizable + MD-rendering + structured output) -->
|
||||
<aside id="chat-dock" class="chat-dock" aria-label="Luna Chat">
|
||||
<div class="dock-resize" id="dock-resize" aria-hidden="true"></div>
|
||||
<header class="dock-head">
|
||||
<span class="dock-title">Luna</span>
|
||||
<span class="dock-sub">deine Tutorin</span>
|
||||
<button class="dock-reset" type="button" id="dock-reset" title="Chat zurücksetzen">↺</button>
|
||||
<button class="dock-collapse" type="button" id="dock-collapse" title="Einklappen">✕</button>
|
||||
</header>
|
||||
<div id="dock-box" class="dock-box" aria-live="polite"></div>
|
||||
<form id="dock-form" class="dock-form">
|
||||
<textarea id="dock-input" rows="1" placeholder="Frag Luna — z.B. „erklär mir die Nephron-Funktion""></textarea>
|
||||
<button type="submit" class="btn-send" aria-label="Senden">→</button>
|
||||
</form>
|
||||
</aside>
|
||||
|
||||
<button id="dock-open" class="dock-open" type="button" aria-label="Luna-Chat öffnen">
|
||||
<span class="dock-open-dot"></span>Luna fragen
|
||||
</button>
|
||||
|
||||
<footer class="footer">
|
||||
Sovereign AI · Deutscher Bunker · <a href="https://qognio.com">Qognio</a> · Deine Daten bleiben bei dir
|
||||
</footer>
|
||||
</div>
|
||||
<div id="toast-stack" class="toast-stack" aria-live="polite"></div>
|
||||
<script src="cockpit.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
43
src/config.yaml
Normal file
43
src/config.yaml
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
slug: physio-tutor
|
||||
bot_name: Luna
|
||||
bot_title: PhysioTutor
|
||||
brand_letter: L
|
||||
title: "Luna · PhysioTutor"
|
||||
tagline: "PhysioTutor"
|
||||
tagline_short: "PhysioTutor"
|
||||
meta_description: "Luna — dein KI-PhysioTutor. Gamified lernen mit Chat, Quiz, Flashcards und Fortschritts-Tracking. Läuft im deutschen Rechenzentrum."
|
||||
bot_key_var: __LUNA_KEY__
|
||||
bot_key_value: qb_drfbhe3w6j2r7199w2
|
||||
bot_id: r3816r760kur14i
|
||||
ls_prefix: luna
|
||||
bot_version: "2026-04-21"
|
||||
|
||||
# Color theme (Luna — purple)
|
||||
accent: "#a855f7"
|
||||
accent_2: "#a855f7"
|
||||
accent_dark: "#7c3aed"
|
||||
accent_rgb: "168, 85, 247"
|
||||
success_color: "#10b981"
|
||||
msg_strong_color: "#ddd6fe"
|
||||
|
||||
# UI Labels
|
||||
tab_flash_label: Karten
|
||||
tab_curriculum_label: Lehrplan
|
||||
curriculum_long_label: Lehrplan
|
||||
|
||||
# Bot personality
|
||||
quiz_intro_hint: "Wähle ein Modul — Luna generiert 10 Multiple-Choice-Fragen."
|
||||
quiz_verb: erstellt
|
||||
quiz_noun: "Fragen"
|
||||
flash_intro_hint: "Luna erstellt Karteikarten zu einem Thema. Bewerte dein Erinnerungsvermögen — das System wiederholt schwere Karten öfter (SM-2)."
|
||||
flash_verb: erstellt
|
||||
|
||||
# DRIFT NOTE: Luna ist V1 (pre-module-tracking) und KANN NICHT diff-clean
|
||||
# rendered werden ohne Code-Migration. Der Live-Bot:
|
||||
# - hat KEIN moduleCorrect/moduleTotal/modulePassedFlash state
|
||||
# - hat NUR 6 levels (kein 5000-XP Senior)
|
||||
# - simpler checkBadges (5 generische badges)
|
||||
# - kein Module-Completion-Bonus-XP
|
||||
# Render erzeugt V2-Code; das ist eine "feature uplift", nicht ein Bugfix.
|
||||
# Alle XP/Streak-Daten der bestehenden Luna-User bleiben kompatibel
|
||||
# (extra fields werden initialisiert mit Defaults beim Load).
|
||||
565
src/curricula.json
Normal file
565
src/curricula.json
Normal file
|
|
@ -0,0 +1,565 @@
|
|||
{
|
||||
"version": "2026-04-21",
|
||||
"updated": "2026-04-21",
|
||||
"curricula": [
|
||||
{
|
||||
"id": "physiologie-uke",
|
||||
"title": "Physiologie (UKE Hamburg)",
|
||||
"short": "UKE iMED + Biomedizin",
|
||||
"icon": "pulse",
|
||||
"color": "#ef4444",
|
||||
"description": "Integrierte Physiologie im UKE-Modellstudiengang iMED. 7 Systemmodule (A–G) mit Herz-Kreislauf, Atmung, Niere, Neuro, Endokrin.",
|
||||
"modules": [
|
||||
{
|
||||
"id": "zellphysiologie",
|
||||
"title": "Zellphysiologie & erregbare Zellen",
|
||||
"objectives": [
|
||||
"Ruhemembranpotenzial & Nernst-Gleichung erklären",
|
||||
"Aktionspotenzial-Phasen beschreiben",
|
||||
"Synaptische Übertragung (EPSP/IPSP)",
|
||||
"Na+/K+-ATPase, Kanäle, Transporter"
|
||||
],
|
||||
"topics": ["Membranpotenzial", "Aktionspotenzial", "Synapse", "Ionenkanäle", "Signaltransduktion"]
|
||||
},
|
||||
{
|
||||
"id": "muskelphysiologie",
|
||||
"title": "Muskelphysiologie",
|
||||
"objectives": [
|
||||
"Gleitfilamenttheorie & elektromechanische Kopplung",
|
||||
"Skelett-, Herz-, glatte Muskulatur differenzieren",
|
||||
"Kraft-Längen- und Kraft-Geschwindigkeits-Kurve",
|
||||
"Motorische Einheit & Rekrutierung"
|
||||
],
|
||||
"topics": ["Sarkomer", "Aktin-Myosin", "Tetanus", "Ermüdung", "Motor unit"]
|
||||
},
|
||||
{
|
||||
"id": "herz-kreislauf",
|
||||
"title": "Herz-Kreislauf-Physiologie",
|
||||
"objectives": [
|
||||
"Herzzyklus & Wiggers-Diagramm interpretieren",
|
||||
"EKG-Ableitungen & normale Komplexe erkennen",
|
||||
"Frank-Starling-Mechanismus erklären",
|
||||
"Blutdruckregulation kurz-/langfristig",
|
||||
"Mikrozirkulation & Starling-Gleichgewicht"
|
||||
],
|
||||
"topics": ["Herzzyklus", "EKG", "Kontraktilität", "RAAS", "Barorezeptoren", "Kapillaren"]
|
||||
},
|
||||
{
|
||||
"id": "atmung",
|
||||
"title": "Atmung & Gasaustausch",
|
||||
"objectives": [
|
||||
"Lungenvolumina & Spirometrie interpretieren",
|
||||
"Compliance, Resistance, Surfactant",
|
||||
"Gastransport mit O2-Bindungskurve und Bohr-Effekt",
|
||||
"Säure-Basen-Haushalt & renal/respiratorische Kompensation"
|
||||
],
|
||||
"topics": ["FRC", "Totraum", "V/Q", "Hämoglobin", "pH", "Bikarbonat"]
|
||||
},
|
||||
{
|
||||
"id": "niere",
|
||||
"title": "Niere & Wasser-Elektrolyt-Haushalt",
|
||||
"objectives": [
|
||||
"GFR-Konzept mit Clearance (Inulin, Kreatinin)",
|
||||
"Tubuläre Reabsorption & Sekretion pro Abschnitt",
|
||||
"Gegenstromprinzip & ADH",
|
||||
"RAAS, Blutvolumenregulation"
|
||||
],
|
||||
"topics": ["Nephron", "Clearance", "ADH", "Aldosteron", "Elektrolyte"]
|
||||
},
|
||||
{
|
||||
"id": "endokrin",
|
||||
"title": "Endokrinologie",
|
||||
"objectives": [
|
||||
"Hypothalamus-Hypophysen-Achse",
|
||||
"Schilddrüse, Nebenniere, Pankreas-Inseln",
|
||||
"Glucose-Homöostase Insulin/Glukagon",
|
||||
"Ca-Regulation (PTH, Calcitriol, Calcitonin)"
|
||||
],
|
||||
"topics": ["Hormone", "Insulin", "Cortisol", "Schilddrüse", "Calcium"]
|
||||
},
|
||||
{
|
||||
"id": "nervensystem",
|
||||
"title": "Nervensystem & Sinne",
|
||||
"objectives": [
|
||||
"Aufbau ZNS/PNS, vegetatives NS",
|
||||
"Somatosensorik, Schmerz, propriozeptive Wahrnehmung",
|
||||
"Visuelles, auditives und vestibuläres System",
|
||||
"Motorisches System: Pyramidenbahn + Kleinhirn"
|
||||
],
|
||||
"topics": ["Sympathikus", "Parasympathikus", "Nozizeption", "Vestibular", "Pyramidenbahn"]
|
||||
},
|
||||
{
|
||||
"id": "blut",
|
||||
"title": "Blut & Hämostase",
|
||||
"objectives": [
|
||||
"Erythropoese, Hämoglobin-Varianten",
|
||||
"Blutgruppen ABO + Rh",
|
||||
"Primäre & sekundäre Hämostase",
|
||||
"Fibrinolyse"
|
||||
],
|
||||
"topics": ["Erythrozyten", "ABO", "Thrombozyten", "Gerinnungskaskade"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "physiotherapie-aprv",
|
||||
"title": "Physiotherapie (PhysTh-APrV)",
|
||||
"short": "Staatliche Ausbildung 2900/1600 h",
|
||||
"icon": "activity",
|
||||
"color": "#a855f7",
|
||||
"description": "Bundesgesetzliche Grundlage für die 3-jährige Physiotherapie-Ausbildung. 2.900 h Theorie + 1.600 h Praxis. Staatliche Prüfung.",
|
||||
"modules": [
|
||||
{
|
||||
"id": "grundlagen",
|
||||
"title": "Berufsgrundlagen & Recht",
|
||||
"objectives": [
|
||||
"PhysTh-APrV-Struktur kennen",
|
||||
"Hygienerichtlinien anwenden",
|
||||
"Erste Hilfe sicher durchführen",
|
||||
"Berufs- und Gesetzeskunde"
|
||||
],
|
||||
"topics": ["APrV", "Hygiene", "Erste Hilfe", "Berufsrecht"],
|
||||
"hours": 100
|
||||
},
|
||||
{
|
||||
"id": "anatomie-physio-aprv",
|
||||
"title": "Anatomie (240 h) & Physiologie (140 h)",
|
||||
"objectives": [
|
||||
"Bewegungsapparat detailliert",
|
||||
"Innere Organe Situs",
|
||||
"Neuroanatomie Grundlagen",
|
||||
"Physiologische Regelkreise"
|
||||
],
|
||||
"topics": ["Muskeln", "Gelenke", "Nerven", "Organe"],
|
||||
"hours": 380
|
||||
},
|
||||
{
|
||||
"id": "krankheitslehre",
|
||||
"title": "Spezielle Krankheitslehre (360 h)",
|
||||
"objectives": [
|
||||
"Orthopädische Krankheitsbilder",
|
||||
"Neurologische Syndrome erkennen",
|
||||
"Innere Medizin Grundlagen",
|
||||
"Chirurgie und Traumatologie"
|
||||
],
|
||||
"topics": ["Innere", "Ortho", "Neuro", "Chirurgie", "Päd", "Psychiatrie", "Gyn", "Dermato", "Geriatrie", "Rheuma", "Arbeitsmed", "Sportmed"],
|
||||
"hours": 360
|
||||
},
|
||||
{
|
||||
"id": "bewegungslehre",
|
||||
"title": "Bewegungslehre & Trainingslehre",
|
||||
"objectives": [
|
||||
"Kinematik, Kinetik, biomechanische Prinzipien",
|
||||
"Trainingsplanung aufbauen",
|
||||
"Belastungssteuerung (FITT)",
|
||||
"Motorisches Lernen"
|
||||
],
|
||||
"topics": ["Biomechanik", "Ausdauer", "Kraft", "Koordination", "Beweglichkeit"],
|
||||
"hours": 160
|
||||
},
|
||||
{
|
||||
"id": "befund",
|
||||
"title": "Befund- und Untersuchungstechniken (100 h)",
|
||||
"objectives": [
|
||||
"Strukturierte Anamnese",
|
||||
"Gelenkmessung Neutral-Null",
|
||||
"Muskelfunktionstest (Janda)",
|
||||
"Spezielle Tests (z. B. Lachman, O'Brien, Thomas)",
|
||||
"ICF-Dokumentation"
|
||||
],
|
||||
"topics": ["Anamnese", "Inspektion", "Palpation", "ROM", "MFT", "Spezialtests"],
|
||||
"hours": 100
|
||||
},
|
||||
{
|
||||
"id": "kg-techniken",
|
||||
"title": "Krankengymnastische Behandlungstechniken (500 h)",
|
||||
"objectives": [
|
||||
"Manuelle Therapie (Kaltenborn, Maitland, Mulligan)",
|
||||
"PNF verstehen und anwenden",
|
||||
"Bobath, Vojta für Neuro",
|
||||
"Atemtherapie",
|
||||
"Schroth bei Skoliose, McKenzie",
|
||||
"MTT & gerätegestütztes Training"
|
||||
],
|
||||
"topics": ["MT", "PNF", "Bobath", "Vojta", "Schroth", "McKenzie", "Atemtherapie", "MTT"],
|
||||
"hours": 500
|
||||
},
|
||||
{
|
||||
"id": "massage-physik",
|
||||
"title": "Massage & Physikalische Therapie",
|
||||
"objectives": [
|
||||
"Klassische Massage & Reflexzonen",
|
||||
"Lymphdrainage nach Vodder",
|
||||
"Elektrotherapie (TENS, Iontophorese, Galvanik)",
|
||||
"Hydro-, Balneo-, Thermotherapie",
|
||||
"Ultraschall, Licht/Strahlen"
|
||||
],
|
||||
"topics": ["Massage", "MLD", "TENS", "Ultraschall", "Fango", "Inhalation"],
|
||||
"hours": 270
|
||||
},
|
||||
{
|
||||
"id": "methodische-anwendung",
|
||||
"title": "Methodische Anwendung in Fachgebieten (700 h)",
|
||||
"objectives": [
|
||||
"Behandlungsaufbau in Orthopädie, Chirurgie, Innerer Medizin",
|
||||
"Neurorehabilitation (Schlaganfall, MS, Parkinson)",
|
||||
"Pädiatrie (Bobath-Säugling, ICP)",
|
||||
"Psychiatrie (konzentrative Bewegungstherapie)",
|
||||
"Gynäkologie (Rückbildung)"
|
||||
],
|
||||
"topics": ["Ortho-Reha", "Innere-Reha", "Neuro-Reha", "Päd", "Geriatrie", "Gyn"],
|
||||
"hours": 700
|
||||
},
|
||||
{
|
||||
"id": "pruefung",
|
||||
"title": "Staatliche Prüfung",
|
||||
"objectives": [
|
||||
"4 schriftliche Aufsichtsarbeiten bestehen",
|
||||
"3 mündliche Prüfungen (Anatomie, Physiologie, spez. Krankheitslehre)",
|
||||
"3 praktische Prüfungen an Patient:innen"
|
||||
],
|
||||
"topics": ["Schriftlich", "Mündlich", "Praktisch"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "pflegeschule-flensburg",
|
||||
"title": "Pflege (ÖBiZ/DIAKO Flensburg)",
|
||||
"short": "Generalistische Pflegeausbildung",
|
||||
"icon": "heart",
|
||||
"color": "#ec4899",
|
||||
"description": "Generalistische Pflegeausbildung nach PflBG/PflAPrV: 2.100 h Theorie + 2.500 h Praxis, 11 curriculare Einheiten, 5 Kompetenzbereiche.",
|
||||
"modules": [
|
||||
{
|
||||
"id": "ce01",
|
||||
"title": "CE 01: Ausbildungsstart & wissenschaftliches Fundament",
|
||||
"objectives": [
|
||||
"Pflegeverständnis entwickeln",
|
||||
"Pflegeprozess (6 Schritte) anwenden",
|
||||
"Berufsidentität reflektieren"
|
||||
],
|
||||
"topics": ["Pflegeverständnis", "Pflegeprozess", "Berufsrolle"]
|
||||
},
|
||||
{
|
||||
"id": "ce02",
|
||||
"title": "CE 02: Hochbelastete & krisenhafte Situationen",
|
||||
"objectives": [
|
||||
"Reanimation durchführen (BLS + AED)",
|
||||
"Akute Verwirrtheit erkennen",
|
||||
"Sterbebegleitung gestalten"
|
||||
],
|
||||
"topics": ["Reanimation", "Delir", "Palliativpflege"]
|
||||
},
|
||||
{
|
||||
"id": "ce03",
|
||||
"title": "CE 03: Verstehens- und Aushandlungsprozesse",
|
||||
"objectives": [
|
||||
"Patientengespräch gestalten",
|
||||
"Beobachtung strukturieren",
|
||||
"Reflexion eigener Einstellungen"
|
||||
],
|
||||
"topics": ["Kommunikation", "Empathie", "Reflexion"]
|
||||
},
|
||||
{
|
||||
"id": "ce04",
|
||||
"title": "CE 04: Gesundheitsförderung & Prävention",
|
||||
"objectives": [
|
||||
"Primär-/Sekundär-/Tertiärprävention",
|
||||
"Lebensstil-Beratung (Bewegung, Ernährung)",
|
||||
"Impfungen nach STIKO"
|
||||
],
|
||||
"topics": ["Gesundheitsförderung", "Prävention", "Beratung"]
|
||||
},
|
||||
{
|
||||
"id": "ce05",
|
||||
"title": "CE 05: Kurative Prozesse & Patientensicherheit",
|
||||
"objectives": [
|
||||
"Medikamenten-Management",
|
||||
"OP-Vorbereitung und Nachsorge",
|
||||
"Fehlervermeidung / CIRS"
|
||||
],
|
||||
"topics": ["Medikation", "Perioperativ", "Patientensicherheit"]
|
||||
},
|
||||
{
|
||||
"id": "ce06",
|
||||
"title": "CE 06: Akutsituationen",
|
||||
"objectives": [
|
||||
"Notfälle erkennen und erstversorgen",
|
||||
"Intensivpflege-Basics",
|
||||
"Monitoring"
|
||||
],
|
||||
"topics": ["Notfall", "ITS", "Monitoring"]
|
||||
},
|
||||
{
|
||||
"id": "ce07",
|
||||
"title": "CE 07: Rehabilitation & chronische Erkrankungen",
|
||||
"objectives": [
|
||||
"Rehabilitative Pflege",
|
||||
"Krankheits-Selbstmanagement fördern",
|
||||
"Expertenstandards anwenden (Mobilität, Schmerz, Wunden)"
|
||||
],
|
||||
"topics": ["Reha", "Chronisch", "Expertenstandards"]
|
||||
},
|
||||
{
|
||||
"id": "ce08",
|
||||
"title": "CE 08: Kritische Lebenssituationen",
|
||||
"objectives": [
|
||||
"Demenzbegleitung",
|
||||
"Psychiatrische Settings",
|
||||
"Sucht, Gewalt, Suizidalität"
|
||||
],
|
||||
"topics": ["Demenz", "Psychiatrie", "Sucht"]
|
||||
},
|
||||
{
|
||||
"id": "ce09",
|
||||
"title": "CE 09: Eintritt in neue Lebensphasen",
|
||||
"objectives": [
|
||||
"Pädiatrie: Säuglings- und Kinderpflege",
|
||||
"Wochenbettpflege",
|
||||
"Geriatrische Eintritts- und Übergangsphasen"
|
||||
],
|
||||
"topics": ["Kinder", "Wochenbett", "Geriatrie"]
|
||||
},
|
||||
{
|
||||
"id": "ce10",
|
||||
"title": "CE 10: Kognitive & psychische Beeinträchtigungen",
|
||||
"objectives": [
|
||||
"Demenz-spezifische Pflege (DNQP-Standard)",
|
||||
"Herausforderndes Verhalten verstehen",
|
||||
"Milieu- und Biografiearbeit"
|
||||
],
|
||||
"topics": ["Demenz", "Depression", "Verhalten"]
|
||||
},
|
||||
{
|
||||
"id": "ce11",
|
||||
"title": "CE 11: Berufliches Selbstverständnis",
|
||||
"objectives": [
|
||||
"Professionalisierung",
|
||||
"Resilienz",
|
||||
"Supervision nutzen"
|
||||
],
|
||||
"topics": ["Professionalität", "Resilienz", "Supervision"]
|
||||
},
|
||||
{
|
||||
"id": "standards",
|
||||
"title": "Expertenstandards DNQP",
|
||||
"objectives": [
|
||||
"9 Standards kennen und anwenden",
|
||||
"Dekubitus-, Sturz-, Schmerz-, Wund-, Kontinenz-, Ernährungs-, Entlassungs-, Mobilitäts-, Demenz-Standard"
|
||||
],
|
||||
"topics": ["Dekubitus", "Sturz", "Schmerz", "Wunden", "Kontinenz", "Ernährung", "Entlassung", "Mobilität", "Demenz"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "medizinische-terminologie",
|
||||
"title": "Medizinische Terminologie",
|
||||
"short": "Latein/Griechisch + Wortbildung",
|
||||
"icon": "book",
|
||||
"color": "#f59e0b",
|
||||
"description": "Pflichtkurs 1. Semester: lateinisch/griechische Fachsprache, Wortbildung, Nomina Anatomica, klinische Terminologie.",
|
||||
"modules": [
|
||||
{
|
||||
"id": "geschichte",
|
||||
"title": "Geschichte der Fachsprache",
|
||||
"objectives": [
|
||||
"Hippokrates, Galen, Vesalius verorten",
|
||||
"Nomina Anatomica / Terminologia Anatomica kennen",
|
||||
"Rolle des Latein vs. Englisch"
|
||||
],
|
||||
"topics": ["Hippokrates", "Galen", "TA", "Nomenklatur"]
|
||||
},
|
||||
{
|
||||
"id": "grammatik",
|
||||
"title": "Lateinische Grammatik (Basics)",
|
||||
"objectives": [
|
||||
"5 Deklinationen sicher beherrschen",
|
||||
"Adjektiv-Substantiv-Kongruenz",
|
||||
"Nominativ & Genitiv im Fachbegriff"
|
||||
],
|
||||
"topics": ["Deklination", "Kasus", "Adjektiv", "Plural"]
|
||||
},
|
||||
{
|
||||
"id": "wortbildung",
|
||||
"title": "Wortbildung",
|
||||
"objectives": [
|
||||
"Präfix + Stamm + Suffix identifizieren",
|
||||
"Verbindungsvokale (o/i) richtig setzen",
|
||||
"Komposita zerlegen"
|
||||
],
|
||||
"topics": ["Präfix", "Suffix", "Kompositum", "Stamm"]
|
||||
},
|
||||
{
|
||||
"id": "praefixe",
|
||||
"title": "Wichtige Präfixe",
|
||||
"objectives": [
|
||||
"Griechische Präfixe (a-, dys-, hyper-, hypo-, tachy-, brady-)",
|
||||
"Lateinische Präfixe (sub-, supra-, inter-, intra-, retro-)",
|
||||
"Bedeutungsnuancen unterscheiden"
|
||||
],
|
||||
"topics": ["a-/an-", "dys-", "hyper-/hypo-", "tachy-/brady-", "sub-/supra-", "inter-/intra-"]
|
||||
},
|
||||
{
|
||||
"id": "suffixe",
|
||||
"title": "Wichtige Suffixe",
|
||||
"objectives": [
|
||||
"-itis vs -ose (entzündlich vs nicht)",
|
||||
"-ektomie, -otomie, -stomie (operative Eingriffe)",
|
||||
"-algie, -rrhagie, -ämie"
|
||||
],
|
||||
"topics": ["-itis", "-ose", "-ektomie", "-algie", "-rrhö"]
|
||||
},
|
||||
{
|
||||
"id": "wortstaemme",
|
||||
"title": "Wortstämme (Organe)",
|
||||
"objectives": [
|
||||
"Herz (cardi-), Niere (nephr-/ren-), Lunge (pneumo-/pulmo-)",
|
||||
"Darm (enter-), Leber (hepat-), Magen (gastr-)",
|
||||
"Synonymie Latein/Griechisch"
|
||||
],
|
||||
"topics": ["cardi-", "nephr-", "hepat-", "gastr-", "neuro-", "arthr-"]
|
||||
},
|
||||
{
|
||||
"id": "lagebeziehungen",
|
||||
"title": "Lage & Richtung",
|
||||
"objectives": [
|
||||
"superior/inferior, anterior/posterior etc.",
|
||||
"3 Ebenen (sagittal, frontal, transversal)",
|
||||
"proximal/distal am Gliedmaß"
|
||||
],
|
||||
"topics": ["Richtungen", "Ebenen", "Achsen"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "anatomie-grundlagen",
|
||||
"title": "Anatomie & medizinische Grundlagen",
|
||||
"short": "Vorklinik-Basics für alle",
|
||||
"icon": "user",
|
||||
"color": "#10b981",
|
||||
"description": "Grundlagen der Anatomie, allgemeinen Pathologie, klinischen Untersuchung, Hygiene, Pharmakologie — Querschnitt für alle 4 Kern-Curricula.",
|
||||
"modules": [
|
||||
{
|
||||
"id": "allg-anatomie",
|
||||
"title": "Allgemeine Anatomie",
|
||||
"objectives": [
|
||||
"Gewebe (Epithel, Binde, Knorpel, Knochen, Muskel, Nerv)",
|
||||
"Lagebezeichnungen und Ebenen",
|
||||
"Bewegungsrichtungen benennen"
|
||||
],
|
||||
"topics": ["Gewebe", "Ebenen", "Bewegungen"]
|
||||
},
|
||||
{
|
||||
"id": "bewegungsapparat",
|
||||
"title": "Bewegungsapparat",
|
||||
"objectives": [
|
||||
"Knochen der oberen und unteren Extremität",
|
||||
"Wichtigste Muskeln mit Ursprung, Ansatz, Funktion",
|
||||
"Gelenktypen erkennen und beschreiben"
|
||||
],
|
||||
"topics": ["Knochen", "Muskeln", "Gelenke", "Wirbelsäule"]
|
||||
},
|
||||
{
|
||||
"id": "situs",
|
||||
"title": "Situs (innere Organe)",
|
||||
"objectives": [
|
||||
"Thorax-Organe in Lagebeziehung",
|
||||
"Abdomen Ober-/Mittel-/Unterbauch",
|
||||
"Retroperitoneale Organe"
|
||||
],
|
||||
"topics": ["Thorax", "Abdomen", "Retroperitoneal"]
|
||||
},
|
||||
{
|
||||
"id": "neuroanatomie",
|
||||
"title": "Neuroanatomie",
|
||||
"objectives": [
|
||||
"Aufbau Gehirn (Lappen, Hirnstamm, Cerebellum)",
|
||||
"Rückenmark-Gliederung (31 Segmente)",
|
||||
"12 Hirnnerven benennen",
|
||||
"Plexus: cervicalis, brachialis, lumbalis, sacralis"
|
||||
],
|
||||
"topics": ["Gehirn", "Rückenmark", "Hirnnerven", "Plexus"]
|
||||
},
|
||||
{
|
||||
"id": "biochemie-basics",
|
||||
"title": "Biochemie-Basics",
|
||||
"objectives": [
|
||||
"Makromoleküle (Kohlenhydrate, Lipide, Proteine, NS)",
|
||||
"Zitratzyklus und Atmungskette",
|
||||
"Glukose-ATP-Bilanz"
|
||||
],
|
||||
"topics": ["Stoffwechsel", "ATP", "Aminosäuren", "Fettsäuren"]
|
||||
},
|
||||
{
|
||||
"id": "pathologie-basics",
|
||||
"title": "Allgemeine Pathologie",
|
||||
"objectives": [
|
||||
"Nekrose vs. Apoptose",
|
||||
"Entzündung: 5 Kardinalzeichen",
|
||||
"Tumorlehre (TNM, Grading)",
|
||||
"Ischämie, Thrombose, Ödem"
|
||||
],
|
||||
"topics": ["Zelltod", "Entzündung", "Tumor", "Infarkt"]
|
||||
},
|
||||
{
|
||||
"id": "klinische-untersuchung",
|
||||
"title": "Klinische Untersuchung",
|
||||
"objectives": [
|
||||
"Strukturierte Anamnese (OPQRST)",
|
||||
"Vitalzeichen interpretieren",
|
||||
"Inspektion, Palpation, Perkussion, Auskultation"
|
||||
],
|
||||
"topics": ["Anamnese", "Vitals", "Untersuchung"]
|
||||
},
|
||||
{
|
||||
"id": "hygiene",
|
||||
"title": "Hygiene",
|
||||
"objectives": [
|
||||
"5 Indikationen der Händedesinfektion (WHO)",
|
||||
"Isolationsarten (Kontakt/Tröpfchen/Aerogen)",
|
||||
"Sterilisation vs. Desinfektion"
|
||||
],
|
||||
"topics": ["Händehygiene", "Isolation", "Sterilisation"]
|
||||
},
|
||||
{
|
||||
"id": "pharma-basics",
|
||||
"title": "Pharmakologie-Basics",
|
||||
"objectives": [
|
||||
"LADMET-Prinzip",
|
||||
"Wichtige Wirkstoffgruppen (NSAR, Antikoagulantien, Antihypertensiva)",
|
||||
"Kontraindikationen und Interaktionen"
|
||||
],
|
||||
"topics": ["Kinetik", "Dynamik", "Wirkstoffgruppen"]
|
||||
},
|
||||
{
|
||||
"id": "notfall",
|
||||
"title": "Notfall-Basics",
|
||||
"objectives": [
|
||||
"BLS-Algorithmus (ERC)",
|
||||
"Stabile Seitenlage",
|
||||
"FAST bei Schlaganfall"
|
||||
],
|
||||
"topics": ["Reanimation", "Seitenlage", "Schlaganfall"]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"badges": [
|
||||
{"id": "first_quiz", "title": "Erster Quiz-Durchlauf", "icon": "award", "description": "Du hast dein erstes Quiz absolviert."},
|
||||
{"id": "10_quiz_streak", "title": "10er-Serie", "icon": "flame", "description": "10 richtige Antworten in Folge."},
|
||||
{"id": "100_answers", "title": "Zentner", "icon": "star", "description": "100 Antworten insgesamt gegeben."},
|
||||
{"id": "7_day_streak", "title": "Wochen-Streak", "icon": "calendar", "description": "7 Tage in Folge aktiv."},
|
||||
{"id": "curriculum_complete", "title": "Curriculum-Meister", "icon": "crown", "description": "Ein Curriculum vollständig durchgearbeitet."},
|
||||
{"id": "night_owl", "title": "Nachteule", "icon": "moon", "description": "Nach 22 Uhr gelernt."},
|
||||
{"id": "early_bird", "title": "Frühaufsteher", "icon": "sun", "description": "Vor 7 Uhr gelernt."}
|
||||
],
|
||||
"levels": [
|
||||
{"min": 0, "title": "Anfängerin"},
|
||||
{"min": 50, "title": "Einsteigerin"},
|
||||
{"min": 200, "title": "Fortgeschrittene"},
|
||||
{"min": 500, "title": "Profi"},
|
||||
{"min": 1250, "title": "Expertin"},
|
||||
{"min": 2500, "title": "Meisterin"},
|
||||
{"min": 5000, "title": "Großmeisterin"}
|
||||
]
|
||||
}
|
||||
3
src/levels-fallback.js
Normal file
3
src/levels-fallback.js
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
{ min: 0, title: 'Anfänger:in' }, { min: 50, title: 'Einsteiger:in' },
|
||||
{ min: 200, title: 'Fortgeschrittene:r' }, { min: 500, title: 'Profi' },
|
||||
{ min: 1250, title: 'Expert:in' }, { min: 2500, title: 'Meister:in' }
|
||||
25
src/welcome.html
Normal file
25
src/welcome.html
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
<h2>Willkommen bei Luna!</h2>
|
||||
<p>Ich bin dein:e KI-Tutor:in für Physiotherapie, Pflege, Physiologie & Anatomie. Alles läuft im deutschen Rechenzentrum — keine Daten verlassen Europa.</p>
|
||||
<div class="mode-grid">
|
||||
<button class="mode-card" data-goto="chat">
|
||||
<strong>💬 Chat</strong>
|
||||
<span>Frag mich alles zu deinem Stoff, Sokratisch erklärt.</span>
|
||||
</button>
|
||||
<button class="mode-card" data-goto="quiz">
|
||||
<strong>🎯 Quiz</strong>
|
||||
<span>Multiple-Choice mit Erklärungen und XP-Belohnung.</span>
|
||||
</button>
|
||||
<button class="mode-card" data-goto="flash">
|
||||
<strong>🃏 Flashcards</strong>
|
||||
<span>Karteikarten mit Spaced-Repetition.</span>
|
||||
</button>
|
||||
<button class="mode-card" data-goto="progress">
|
||||
<strong>📊 Fortschritt</strong>
|
||||
<span>XP, Streaks, Mastery, Abzeichen.</span>
|
||||
</button>
|
||||
<button class="mode-card" data-goto="curriculum">
|
||||
<strong>📚 Lehrplan</strong>
|
||||
<span>Kompletter Themenbaum — 5 Curricula.</span>
|
||||
</button>
|
||||
</div>
|
||||
<p style="font-size:.82rem;color:var(--text-mute)">In 3 Sätzen: Chat für Verständnis → Quiz zum Testen → Flashcards zum Merken. Fortschritt zeigt dir, was schon sitzt; der Lehrplan gibt Orientierung.</p>
|
||||
1803
www/app.js
Normal file
1803
www/app.js
Normal file
File diff suppressed because it is too large
Load diff
650
www/cockpit/cockpit.css
Normal file
650
www/cockpit/cockpit.css
Normal file
|
|
@ -0,0 +1,650 @@
|
|||
/* Luna Cockpit v3 — purple #a855f7, dark, no external deps */
|
||||
|
||||
:root {
|
||||
--bg: #0a0a0f;
|
||||
--bg-2: #11111b;
|
||||
--bg-3: #1a1a26;
|
||||
--bg-4: #232336;
|
||||
--line: #2a2a3e;
|
||||
--text: #e8e8f0;
|
||||
--text-muted: #9090a8;
|
||||
--text-dim: #6a6a85;
|
||||
--accent: #a855f7;
|
||||
--accent-2: #c084fc;
|
||||
--accent-dark: #7c3aed;
|
||||
--accent-rgb: 168, 85, 247;
|
||||
--success: #10b981;
|
||||
--warn: #f59e0b;
|
||||
--danger: #ef4444;
|
||||
--info: #38bdf8;
|
||||
--shadow-sm: 0 1px 2px rgba(0,0,0,0.2);
|
||||
--shadow-md: 0 4px 12px rgba(0,0,0,0.3);
|
||||
--shadow-lg: 0 12px 32px rgba(0,0,0,0.5);
|
||||
--radius: 8px;
|
||||
--radius-lg: 12px;
|
||||
}
|
||||
|
||||
* { box-sizing: border-box; }
|
||||
html, body { margin: 0; padding: 0; }
|
||||
body {
|
||||
background: var(--bg);
|
||||
color: var(--text);
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Inter", "Helvetica Neue", Arial, sans-serif;
|
||||
font-size: 15px;
|
||||
line-height: 1.5;
|
||||
min-height: 100vh;
|
||||
}
|
||||
a { color: var(--accent-2); text-decoration: none; }
|
||||
a:hover { color: var(--accent); }
|
||||
button { font: inherit; cursor: pointer; border: none; background: transparent; color: inherit; }
|
||||
|
||||
.cockpit {
|
||||
max-width: 1280px;
|
||||
margin: 0 auto;
|
||||
padding: 1.5rem 1rem 6rem;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
/* ─── Topbar ────────────────────────────────────────────────────── */
|
||||
.topbar { display: flex; align-items: center; gap: 1rem; padding-bottom: 1.5rem; border-bottom: 1px solid var(--line); margin-bottom: 1.5rem; }
|
||||
.brand { display: flex; align-items: center; gap: 0.75rem; }
|
||||
.brand-icon {
|
||||
width: 40px; height: 40px;
|
||||
background: linear-gradient(135deg, var(--accent), var(--accent-dark));
|
||||
color: white; font-weight: 700; font-size: 22px;
|
||||
border-radius: var(--radius);
|
||||
display: grid; place-items: center;
|
||||
box-shadow: var(--shadow-md);
|
||||
}
|
||||
.brand-text { display: flex; flex-direction: column; }
|
||||
.brand-title { font-size: 1.2rem; font-weight: 600; }
|
||||
.brand-title small { color: var(--text-muted); font-weight: 400; margin-left: 0.25rem; }
|
||||
.brand-sub { font-size: 0.8rem; color: var(--text-muted); }
|
||||
.spacer { flex: 1; }
|
||||
.auth-state { display: flex; align-items: center; gap: 0.75rem; }
|
||||
.auth-user { color: var(--text-muted); font-size: 0.85rem; }
|
||||
|
||||
/* ─── Status block ─────────────────────────────────────────────── */
|
||||
.status-block {
|
||||
background: linear-gradient(135deg, rgba(var(--accent-rgb),0.08), rgba(var(--accent-rgb),0.02));
|
||||
border: 1px solid rgba(var(--accent-rgb), 0.25);
|
||||
border-radius: var(--radius-lg);
|
||||
padding: 1rem 1.25rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
.status-title { font-size: 0.85rem; color: var(--accent-2); font-weight: 600; margin: 0 0 0.5rem; text-transform: uppercase; letter-spacing: 0.04em; }
|
||||
.status-summary { font-size: 0.95rem; color: var(--text); }
|
||||
.status-summary .dim { color: var(--text-muted); }
|
||||
|
||||
/* ─── Top-level Space-Tabs ─────────────────────────────────────── */
|
||||
.space-tabs {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 0.5rem;
|
||||
background: var(--bg-2);
|
||||
padding: 0.5rem;
|
||||
border-radius: var(--radius-lg);
|
||||
border: 1px solid var(--line);
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
.space-tab {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.5rem;
|
||||
padding: 0.85rem;
|
||||
border-radius: var(--radius);
|
||||
color: var(--text-muted);
|
||||
transition: all 0.15s;
|
||||
font-size: 0.95rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
.space-tab[aria-selected="true"] {
|
||||
background: linear-gradient(135deg, rgba(var(--accent-rgb),0.18), rgba(var(--accent-rgb),0.05));
|
||||
color: var(--accent-2);
|
||||
box-shadow: inset 0 0 0 1px rgba(var(--accent-rgb), 0.4);
|
||||
}
|
||||
.space-tab:hover { background: var(--bg-3); color: var(--text); }
|
||||
.space-tab[aria-selected="true"]:hover { color: var(--accent-2); }
|
||||
.space-icon { font-size: 1.1rem; }
|
||||
|
||||
/* Each space-section is hidden unless data-active=true */
|
||||
.space { display: none; }
|
||||
.space[data-active="true"] { display: block; }
|
||||
.space-intro { margin-bottom: 1.25rem; }
|
||||
.space-intro h2 { margin: 0 0 0.4rem; font-size: 1.15rem; }
|
||||
.space-intro p { margin: 0; color: var(--text-muted); font-size: 0.9rem; }
|
||||
|
||||
/* ─── Folder tabs (Dokumente) ──────────────────────────────────── */
|
||||
.folder-tabs {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 0.5rem;
|
||||
background: var(--bg-2);
|
||||
padding: 0.5rem;
|
||||
border-radius: var(--radius-lg);
|
||||
border: 1px solid var(--line);
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.folder-tab {
|
||||
display: flex; flex-direction: column; align-items: center; gap: 0.25rem;
|
||||
padding: 0.75rem; border-radius: var(--radius);
|
||||
color: var(--text-muted);
|
||||
transition: background 0.15s, color 0.15s;
|
||||
}
|
||||
.folder-tab[aria-selected="true"] {
|
||||
background: rgba(var(--accent-rgb), 0.12);
|
||||
color: var(--text);
|
||||
box-shadow: inset 0 -2px 0 var(--accent);
|
||||
}
|
||||
.folder-tab:hover { background: var(--bg-3); color: var(--text); }
|
||||
.folder-icon { font-size: 1.25rem; }
|
||||
.folder-label { font-size: 0.85rem; font-weight: 500; }
|
||||
.folder-count {
|
||||
font-size: 0.7rem; background: var(--bg-3); color: var(--text-muted);
|
||||
padding: 0.1rem 0.5rem; border-radius: 10px; min-width: 24px; text-align: center;
|
||||
}
|
||||
.folder-tab[aria-selected="true"] .folder-count {
|
||||
background: rgba(var(--accent-rgb), 0.25);
|
||||
color: var(--accent-2);
|
||||
}
|
||||
|
||||
.folder-body {
|
||||
background: var(--bg-2); border: 1px solid var(--line);
|
||||
border-radius: var(--radius-lg); padding: 1.25rem;
|
||||
}
|
||||
.folder-help { font-size: 0.85rem; color: var(--text-muted); margin-bottom: 1rem; line-height: 1.5; }
|
||||
|
||||
.upload-zone {
|
||||
border: 2px dashed var(--line); border-radius: var(--radius-lg);
|
||||
padding: 2rem 1rem; text-align: center;
|
||||
transition: border-color 0.15s, background 0.15s;
|
||||
cursor: pointer; margin-bottom: 1rem;
|
||||
}
|
||||
.upload-zone:hover, .upload-zone:focus, .upload-zone[data-dragover="true"] {
|
||||
border-color: var(--accent); background: rgba(var(--accent-rgb), 0.05);
|
||||
outline: none;
|
||||
}
|
||||
.upload-cta { display: flex; flex-direction: column; align-items: center; gap: 0.4rem; }
|
||||
.upload-icon { font-size: 1.5rem; opacity: 0.6; }
|
||||
.upload-text { font-size: 1rem; color: var(--text); }
|
||||
.upload-hint { font-size: 0.8rem; color: var(--text-muted); }
|
||||
|
||||
.upload-progress { margin-top: 1rem; display: flex; flex-direction: column; gap: 0.5rem; }
|
||||
.upload-progress-row {
|
||||
display: flex; align-items: center; gap: 0.75rem;
|
||||
padding: 0.5rem 0.75rem; background: var(--bg-3);
|
||||
border-radius: var(--radius); font-size: 0.85rem;
|
||||
}
|
||||
.upload-progress-row .upload-bar { flex: 1; height: 4px; background: var(--bg); border-radius: 2px; overflow: hidden; }
|
||||
.upload-progress-row .upload-bar-fill { height: 100%; background: var(--accent); width: 0%; transition: width 0.2s; }
|
||||
|
||||
/* ─── Document list ────────────────────────────────────────────── */
|
||||
.doc-list { list-style: none; padding: 0; margin: 0; }
|
||||
.doc-row { display: flex; align-items: center; gap: 1rem; padding: 0.75rem; border-bottom: 1px solid var(--line); transition: background 0.1s; }
|
||||
.doc-row:hover { background: var(--bg-3); }
|
||||
.doc-row.is-disabled { opacity: 0.55; }
|
||||
.doc-meta { flex: 1; min-width: 0; }
|
||||
.doc-name { font-size: 0.95rem; color: var(--text); margin: 0; word-break: break-word; }
|
||||
.doc-info { font-size: 0.75rem; color: var(--text-muted); margin-top: 0.15rem; }
|
||||
.doc-info .ok { color: var(--success); }
|
||||
.doc-info .pending { color: var(--warn); }
|
||||
.doc-info .err { color: var(--danger); }
|
||||
.doc-actions { display: flex; gap: 0.5rem; align-items: center; }
|
||||
|
||||
.toggle {
|
||||
position: relative; width: 42px; height: 22px;
|
||||
background: var(--bg-3); border-radius: 11px;
|
||||
cursor: pointer; transition: background 0.15s; flex-shrink: 0;
|
||||
}
|
||||
.toggle::after {
|
||||
content: ""; position: absolute; top: 2px; left: 2px;
|
||||
width: 18px; height: 18px; background: var(--text-muted);
|
||||
border-radius: 50%; transition: left 0.15s, background 0.15s;
|
||||
}
|
||||
.toggle[data-on="true"] { background: rgba(var(--accent-rgb), 0.4); }
|
||||
.toggle[data-on="true"]::after { left: 22px; background: var(--accent); }
|
||||
|
||||
.btn-icon {
|
||||
width: 32px; height: 32px;
|
||||
display: grid; place-items: center;
|
||||
border-radius: var(--radius);
|
||||
color: var(--text-muted);
|
||||
transition: background 0.1s, color 0.1s;
|
||||
}
|
||||
.btn-icon:hover { background: var(--bg-3); color: var(--danger); }
|
||||
.btn-icon-x {
|
||||
width: 28px; height: 28px;
|
||||
border-radius: 50%;
|
||||
color: var(--text-muted);
|
||||
transition: background 0.1s, color 0.1s;
|
||||
}
|
||||
.btn-icon-x:hover { background: var(--bg-3); color: var(--text); }
|
||||
|
||||
.doc-empty, .empty-state {
|
||||
padding: 1.5rem; text-align: center;
|
||||
color: var(--text-muted); font-size: 0.9rem;
|
||||
}
|
||||
|
||||
/* ─── Buttons ──────────────────────────────────────────────────── */
|
||||
.btn-primary {
|
||||
background: var(--accent); color: white;
|
||||
padding: 0.6rem 1.25rem; border-radius: var(--radius);
|
||||
font-weight: 500; transition: background 0.15s;
|
||||
}
|
||||
.btn-primary:hover { background: var(--accent-dark); }
|
||||
.btn-primary:disabled { opacity: 0.5; cursor: not-allowed; }
|
||||
.btn-secondary {
|
||||
background: var(--bg-3); color: var(--text);
|
||||
padding: 0.5rem 1rem; border-radius: var(--radius);
|
||||
font-size: 0.85rem; border: 1px solid var(--line);
|
||||
}
|
||||
.btn-secondary:hover { background: var(--bg-4); }
|
||||
.btn-ghost {
|
||||
color: var(--text-muted); padding: 0.4rem 0.8rem;
|
||||
border-radius: var(--radius); font-size: 0.85rem;
|
||||
}
|
||||
.btn-ghost:hover { background: var(--bg-3); color: var(--text); }
|
||||
|
||||
/* ─── Login screen ─────────────────────────────────────────────── */
|
||||
.login-screen { display: grid; place-items: center; min-height: 60vh; }
|
||||
.login-card { width: 100%; max-width: 380px; background: var(--bg-2); border: 1px solid var(--line); border-radius: var(--radius-lg); padding: 2rem; box-shadow: var(--shadow-lg); }
|
||||
.login-card h2 { margin: 0 0 0.5rem; font-size: 1.3rem; }
|
||||
.login-hint { color: var(--text-muted); font-size: 0.9rem; margin: 0 0 1.5rem; }
|
||||
.login-form { display: flex; flex-direction: column; gap: 1rem; }
|
||||
.login-form label { display: flex; flex-direction: column; gap: 0.35rem; font-size: 0.85rem; color: var(--text-muted); }
|
||||
.login-form input { padding: 0.6rem 0.8rem; background: var(--bg); color: var(--text); border: 1px solid var(--line); border-radius: var(--radius); font-size: 0.95rem; }
|
||||
.login-form input:focus { outline: none; border-color: var(--accent); }
|
||||
.login-error { background: rgba(239, 68, 68, 0.1); border: 1px solid var(--danger); color: var(--danger); padding: 0.6rem 0.8rem; border-radius: var(--radius); font-size: 0.85rem; }
|
||||
|
||||
/* ─── Klausur cards ────────────────────────────────────────────── */
|
||||
.klausur-list { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 0.75rem; }
|
||||
.klausur-card {
|
||||
background: var(--bg-2); border: 1px solid var(--line);
|
||||
border-radius: var(--radius); padding: 1rem;
|
||||
display: flex; flex-direction: column; gap: 0.6rem;
|
||||
transition: border-color 0.15s, transform 0.1s;
|
||||
cursor: pointer;
|
||||
}
|
||||
.klausur-card:hover { border-color: var(--accent); transform: translateY(-1px); }
|
||||
.klausur-card-header { display: flex; justify-content: space-between; align-items: baseline; gap: 0.5rem; }
|
||||
.klausur-card-num { background: var(--accent); color: white; font-weight: 700; font-size: 0.75rem; padding: 0.15rem 0.5rem; border-radius: 10px; flex-shrink: 0; }
|
||||
.klausur-card-name { font-weight: 600; font-size: 0.95rem; flex: 1; word-break: break-word; }
|
||||
.klausur-card-topics { display: flex; flex-wrap: wrap; gap: 0.3rem; }
|
||||
.klausur-card-topic { background: rgba(var(--accent-rgb), 0.12); color: var(--accent-2); padding: 0.15rem 0.5rem; border-radius: 4px; font-size: 0.7rem; cursor: pointer; transition: background 0.1s; }
|
||||
.klausur-card-topic:hover { background: rgba(var(--accent-rgb), 0.25); }
|
||||
.klausur-card-notes { color: var(--text-muted); font-size: 0.8rem; font-style: italic; }
|
||||
.klausur-card-cta { margin-top: 0.25rem; font-size: 0.85rem; color: var(--accent-2); display: flex; align-items: center; gap: 0.3rem; }
|
||||
.klausur-card-actions { display: flex; gap: 0.4rem; margin-top: 0.4rem; flex-wrap: wrap; }
|
||||
.klausur-card-mini {
|
||||
font-size: 0.75rem; padding: 0.35rem 0.6rem; border-radius: 4px;
|
||||
background: var(--bg-4); color: var(--text-muted);
|
||||
border: 1px solid var(--line);
|
||||
transition: all 0.1s;
|
||||
}
|
||||
.klausur-card-mini:hover { background: var(--bg-3); color: var(--accent-2); border-color: var(--accent); }
|
||||
|
||||
/* ─── Lernen: Heatmap ──────────────────────────────────────────── */
|
||||
.heatmap-block { background: var(--bg-2); border: 1px solid var(--line); border-radius: var(--radius-lg); padding: 1.25rem; margin-bottom: 1.5rem; }
|
||||
.heatmap-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 1rem; flex-wrap: wrap; gap: 0.5rem; }
|
||||
.heatmap-title { margin: 0; font-size: 1rem; }
|
||||
.heatmap-legend { display: flex; align-items: center; gap: 0.4rem; font-size: 0.75rem; color: var(--text-muted); flex-wrap: wrap; }
|
||||
.legend-cell { display: inline-block; width: 18px; height: 12px; border-radius: 2px; }
|
||||
.legend-cell[data-level="0"] { background: var(--bg-4); }
|
||||
.legend-cell[data-level="2"] { background: rgba(245, 158, 11, 0.55); }
|
||||
.legend-cell[data-level="4"] { background: rgba(16, 185, 129, 0.6); }
|
||||
.legend-cell[data-level="5"] { background: rgba(16, 185, 129, 1); }
|
||||
|
||||
.heatmap-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: 0.5rem; }
|
||||
.heatmap-cell {
|
||||
background: var(--bg-3); border: 1px solid var(--line);
|
||||
border-radius: var(--radius); padding: 0.75rem;
|
||||
display: flex; flex-direction: column; gap: 0.3rem;
|
||||
position: relative; overflow: hidden;
|
||||
transition: transform 0.1s, border-color 0.1s;
|
||||
cursor: pointer;
|
||||
}
|
||||
.heatmap-cell:hover { transform: translateY(-1px); border-color: var(--accent); }
|
||||
.heatmap-cell .hc-bar {
|
||||
position: absolute; bottom: 0; left: 0; right: 0; height: 3px;
|
||||
background: var(--bg-4);
|
||||
}
|
||||
.heatmap-cell .hc-bar-fill {
|
||||
height: 100%; background: linear-gradient(to right, var(--warn), var(--success));
|
||||
transition: width 0.3s;
|
||||
}
|
||||
.heatmap-cell .hc-name { font-size: 0.85rem; color: var(--text); font-weight: 500; line-height: 1.3; }
|
||||
.heatmap-cell .hc-meta { font-size: 0.7rem; color: var(--text-muted); display: flex; justify-content: space-between; }
|
||||
.heatmap-cell[data-level="0"] .hc-bar-fill { background: var(--bg-4); width: 0%; }
|
||||
.heatmap-cell[data-level="1"] .hc-bar-fill { width: 20%; }
|
||||
.heatmap-cell[data-level="2"] .hc-bar-fill { width: 40%; }
|
||||
.heatmap-cell[data-level="3"] .hc-bar-fill { width: 60%; }
|
||||
.heatmap-cell[data-level="4"] .hc-bar-fill { width: 80%; }
|
||||
.heatmap-cell[data-level="5"] .hc-bar-fill { width: 100%; background: var(--success); }
|
||||
|
||||
/* ─── Lernen: Topic picker ─────────────────────────────────────── */
|
||||
.topic-picker { background: var(--bg-2); border: 1px solid var(--line); border-radius: var(--radius-lg); padding: 1.25rem; margin-bottom: 1.5rem; }
|
||||
.topic-picker-head { display: flex; justify-content: space-between; align-items: center; margin-bottom: 1rem; gap: 0.5rem; flex-wrap: wrap; }
|
||||
.topic-picker-head h3 { margin: 0; font-size: 1rem; }
|
||||
.select-mini {
|
||||
background: var(--bg-3); color: var(--text);
|
||||
border: 1px solid var(--line); border-radius: var(--radius);
|
||||
padding: 0.4rem 0.6rem; font-size: 0.85rem;
|
||||
}
|
||||
.select-mini:focus { outline: none; border-color: var(--accent); }
|
||||
.topic-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 0.5rem; }
|
||||
.topic-pill {
|
||||
background: var(--bg-3); border: 1px solid var(--line);
|
||||
border-radius: var(--radius); padding: 0.6rem 0.85rem;
|
||||
font-size: 0.85rem; color: var(--text);
|
||||
cursor: pointer; transition: all 0.1s;
|
||||
display: flex; flex-direction: column; gap: 0.25rem;
|
||||
text-align: left;
|
||||
}
|
||||
.topic-pill:hover { border-color: var(--accent); background: var(--bg-4); }
|
||||
.topic-pill .tp-label { font-weight: 500; }
|
||||
.topic-pill .tp-meta { font-size: 0.7rem; color: var(--text-muted); }
|
||||
.topic-pill[data-mastered="true"] { border-color: var(--success); }
|
||||
.topic-pill .tp-stars { color: var(--warn); font-size: 0.75rem; letter-spacing: 0.05em; }
|
||||
|
||||
/* ─── Minigame Launcher ────────────────────────────────────────── */
|
||||
.minigame-launcher {
|
||||
background: linear-gradient(135deg, rgba(var(--accent-rgb),0.06), var(--bg-2));
|
||||
border: 1px solid var(--accent);
|
||||
border-radius: var(--radius-lg);
|
||||
padding: 1.25rem;
|
||||
margin-bottom: 1.5rem;
|
||||
animation: fadein 0.2s ease-out;
|
||||
}
|
||||
.launcher-head { display: flex; justify-content: space-between; align-items: center; margin-bottom: 1rem; font-size: 0.95rem; }
|
||||
.launcher-head strong { color: var(--accent-2); }
|
||||
.launcher-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 0.75rem; }
|
||||
.minigame-card {
|
||||
background: var(--bg-3); border: 1px solid var(--line);
|
||||
border-radius: var(--radius); padding: 1rem;
|
||||
display: flex; flex-direction: column; gap: 0.4rem;
|
||||
text-align: left; transition: all 0.1s;
|
||||
}
|
||||
.minigame-card:hover { border-color: var(--accent); transform: translateY(-1px); background: var(--bg-4); }
|
||||
.mg-icon { font-size: 1.3rem; }
|
||||
.mg-title { font-weight: 600; font-size: 0.95rem; color: var(--text); }
|
||||
.mg-desc { font-size: 0.8rem; color: var(--text-muted); line-height: 1.35; }
|
||||
.mg-tag { font-size: 0.7rem; color: var(--accent-2); margin-top: 0.25rem; }
|
||||
|
||||
/* ─── Minigame Stage ──────────────────────────────────────────── */
|
||||
.minigame-stage {
|
||||
background: var(--bg-2); border: 1px solid var(--line);
|
||||
border-radius: var(--radius-lg); padding: 1.5rem;
|
||||
animation: fadein 0.2s ease-out;
|
||||
}
|
||||
.stage-head { display: flex; justify-content: space-between; align-items: center; margin-bottom: 1rem; gap: 0.5rem; flex-wrap: wrap; }
|
||||
.stage-title { margin: 0; font-size: 1.05rem; font-weight: 600; }
|
||||
.stage-progress { color: var(--text-muted); font-size: 0.85rem; }
|
||||
.stage-progress-bar { height: 4px; background: var(--bg-4); border-radius: 2px; overflow: hidden; margin-bottom: 1rem; }
|
||||
.stage-progress-bar-fill { height: 100%; background: var(--accent); transition: width 0.3s; }
|
||||
|
||||
.stage-loader { padding: 3rem 1rem; text-align: center; color: var(--text-muted); }
|
||||
.stage-spinner {
|
||||
display: inline-block; width: 28px; height: 28px;
|
||||
border: 3px solid var(--bg-4); border-top-color: var(--accent);
|
||||
border-radius: 50%; animation: spin 0.7s linear infinite;
|
||||
margin-bottom: 0.8rem;
|
||||
}
|
||||
@keyframes spin { to { transform: rotate(360deg); } }
|
||||
@keyframes fadein { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: translateY(0); } }
|
||||
|
||||
/* MCQ-style question block — used by Diagnose + Klausur quiz */
|
||||
.qa-card { background: var(--bg-3); border: 1px solid var(--line); border-radius: var(--radius-lg); padding: 1.25rem; }
|
||||
.qa-q { font-size: 1.05rem; font-weight: 500; margin: 0 0 1rem; line-height: 1.5; }
|
||||
.qa-options { display: flex; flex-direction: column; gap: 0.5rem; }
|
||||
.qa-option {
|
||||
text-align: left; padding: 0.75rem 1rem;
|
||||
background: var(--bg); border: 1px solid var(--line);
|
||||
border-radius: var(--radius); transition: all 0.1s;
|
||||
display: flex; gap: 0.75rem; align-items: flex-start;
|
||||
}
|
||||
.qa-option:not(:disabled):hover { border-color: var(--accent); background: var(--bg-4); }
|
||||
.qa-option:disabled { cursor: default; }
|
||||
.qa-option-letter { font-weight: 600; color: var(--text-muted); flex-shrink: 0; }
|
||||
.qa-option[data-state="correct"] { border-color: var(--success); background: rgba(16, 185, 129, 0.1); }
|
||||
.qa-option[data-state="wrong"] { border-color: var(--danger); background: rgba(239, 68, 68, 0.1); }
|
||||
.qa-option[data-state="reveal-correct"] { border-color: var(--success); }
|
||||
.qa-feedback {
|
||||
margin-top: 1rem; padding: 0.85rem 1rem; border-radius: var(--radius);
|
||||
background: var(--bg); border-left: 3px solid var(--accent);
|
||||
font-size: 0.9rem; line-height: 1.5;
|
||||
}
|
||||
.qa-feedback.correct { border-left-color: var(--success); }
|
||||
.qa-feedback.wrong { border-left-color: var(--danger); }
|
||||
.qa-feedback strong { color: var(--accent-2); }
|
||||
.qa-actions { display: flex; justify-content: flex-end; gap: 0.5rem; margin-top: 1rem; }
|
||||
|
||||
/* Klinikfall stage */
|
||||
.fall-patient { background: rgba(56, 189, 248, 0.1); border-left: 3px solid var(--info); padding: 1rem; border-radius: var(--radius); margin-bottom: 1rem; line-height: 1.6; }
|
||||
.fall-stage-num { color: var(--info); font-weight: 600; font-size: 0.85rem; margin-bottom: 0.25rem; }
|
||||
.fall-learning {
|
||||
margin-top: 0.75rem; padding: 0.6rem 0.85rem;
|
||||
background: rgba(168, 85, 247, 0.08);
|
||||
border-left: 3px solid var(--accent);
|
||||
border-radius: var(--radius);
|
||||
font-size: 0.85rem; color: var(--text-muted);
|
||||
}
|
||||
.fall-learning strong { color: var(--accent-2); }
|
||||
|
||||
/* Stimmt-das? */
|
||||
.stimmt-grid { display: flex; flex-direction: column; gap: 0.75rem; }
|
||||
.stimmt-card {
|
||||
background: var(--bg-3); border: 1px solid var(--line);
|
||||
border-radius: var(--radius); padding: 1rem;
|
||||
}
|
||||
.stimmt-statement { font-size: 0.95rem; margin-bottom: 0.75rem; line-height: 1.5; }
|
||||
.stimmt-buttons { display: flex; gap: 0.5rem; }
|
||||
.stimmt-btn {
|
||||
flex: 1; padding: 0.5rem 1rem;
|
||||
background: var(--bg); border: 1px solid var(--line);
|
||||
border-radius: var(--radius); font-weight: 500;
|
||||
transition: all 0.1s;
|
||||
}
|
||||
.stimmt-btn:not(:disabled):hover { border-color: var(--accent); background: var(--bg-4); }
|
||||
.stimmt-btn[data-state="correct"] { border-color: var(--success); background: rgba(16, 185, 129, 0.15); color: var(--success); }
|
||||
.stimmt-btn[data-state="wrong"] { border-color: var(--danger); background: rgba(239, 68, 68, 0.15); color: var(--danger); }
|
||||
.stimmt-explain { margin-top: 0.5rem; font-size: 0.85rem; color: var(--text-muted); padding-left: 0.6rem; border-left: 2px solid var(--line); }
|
||||
.stimmt-explain.correct { border-left-color: var(--success); }
|
||||
.stimmt-explain.wrong { border-left-color: var(--danger); }
|
||||
.trap-tag { display: inline-block; padding: 0.1rem 0.4rem; background: rgba(245, 158, 11, 0.15); color: var(--warn); border-radius: 3px; font-size: 0.7rem; margin-right: 0.4rem; }
|
||||
|
||||
/* Summary card for end of minigame */
|
||||
.summary-card {
|
||||
background: linear-gradient(135deg, rgba(var(--accent-rgb),0.1), var(--bg-3));
|
||||
border: 1px solid var(--accent);
|
||||
border-radius: var(--radius-lg);
|
||||
padding: 1.5rem;
|
||||
text-align: center;
|
||||
}
|
||||
.summary-stars {
|
||||
font-size: 2rem; letter-spacing: 0.1em; margin-bottom: 0.5rem;
|
||||
color: var(--warn);
|
||||
}
|
||||
.summary-text { font-size: 1rem; margin-bottom: 0.25rem; }
|
||||
.summary-meta { color: var(--text-muted); font-size: 0.85rem; margin-bottom: 1rem; }
|
||||
.summary-actions { display: flex; gap: 0.5rem; justify-content: center; flex-wrap: wrap; }
|
||||
|
||||
/* ─── Persona-Form ─────────────────────────────────────────────── */
|
||||
.persona-form { background: var(--bg-2); border: 1px solid var(--line); border-radius: var(--radius-lg); padding: 1.25rem; }
|
||||
.persona-label { display: flex; flex-direction: column; gap: 0.5rem; font-size: 0.85rem; color: var(--text-muted); margin-bottom: 1rem; }
|
||||
.persona-form textarea {
|
||||
background: var(--bg); color: var(--text);
|
||||
border: 1px solid var(--line); border-radius: var(--radius);
|
||||
padding: 0.75rem 1rem; font-family: inherit; font-size: 0.95rem;
|
||||
line-height: 1.5; resize: vertical; min-height: 200px;
|
||||
}
|
||||
.persona-form textarea:focus { outline: none; border-color: var(--accent); }
|
||||
.persona-actions { display: flex; justify-content: space-between; align-items: center; gap: 1rem; }
|
||||
.persona-status { color: var(--text-muted); font-size: 0.85rem; }
|
||||
.persona-status.saved { color: var(--success); }
|
||||
.hint-list { color: var(--text-muted); font-size: 0.85rem; line-height: 1.6; margin: 0.5rem 0 1rem; padding-left: 1.5rem; }
|
||||
.hint-list li { margin-bottom: 0.25rem; }
|
||||
|
||||
/* ─── Telegram-Pairing Block ──────────────────────────────────── */
|
||||
.telegram-block {
|
||||
background: var(--bg-2); border: 1px solid var(--line);
|
||||
border-radius: var(--radius-lg); padding: 1.25rem; margin-top: 1.5rem;
|
||||
}
|
||||
.tg-head { display: flex; justify-content: space-between; align-items: center; margin-bottom: 0.5rem; gap: 0.5rem; }
|
||||
.tg-head h3 { margin: 0; font-size: 1rem; }
|
||||
.tg-hint { color: var(--text-muted); font-size: 0.9rem; margin: 0 0 1rem; line-height: 1.5; }
|
||||
.tg-badge {
|
||||
font-size: 0.75rem; padding: 0.2rem 0.6rem; border-radius: 10px;
|
||||
background: var(--bg-3); color: var(--text-muted);
|
||||
border: 1px solid var(--line);
|
||||
}
|
||||
.tg-badge.linked { background: rgba(16,185,129,0.15); color: var(--success); border-color: var(--success); }
|
||||
.tg-badge.unlinked { background: var(--bg-3); color: var(--text-muted); }
|
||||
.tg-link-area { display: flex; gap: 0.5rem; flex-wrap: wrap; }
|
||||
.tg-code-display { margin-top: 1rem; }
|
||||
.tg-code-line {
|
||||
background: var(--bg); border: 1px solid var(--accent);
|
||||
border-radius: var(--radius); padding: 0.85rem 1rem;
|
||||
font-size: 1.1rem; letter-spacing: 0.05em;
|
||||
margin: 0.5rem 0;
|
||||
user-select: all;
|
||||
display: flex; justify-content: center;
|
||||
}
|
||||
.tg-code-line code { color: var(--accent-2); font-weight: 600; font-size: 1.15rem; letter-spacing: 0.1em; }
|
||||
.tg-code-hint { font-size: 0.8rem; color: var(--text-muted); margin: 0.25rem 0 0; }
|
||||
|
||||
/* ─── Chat Dock (resizable + MD) ──────────────────────────────── */
|
||||
.chat-dock {
|
||||
position: fixed;
|
||||
bottom: 1rem; right: 1rem;
|
||||
width: 460px; height: 640px;
|
||||
min-width: 340px; min-height: 420px;
|
||||
max-width: calc(100vw - 2rem);
|
||||
max-height: calc(100vh - 2rem);
|
||||
background: var(--bg-2); border: 1px solid var(--line);
|
||||
border-radius: var(--radius-lg);
|
||||
box-shadow: var(--shadow-lg);
|
||||
display: none; flex-direction: column;
|
||||
z-index: 100;
|
||||
}
|
||||
.chat-dock[data-open="true"] { display: flex; }
|
||||
.dock-resize {
|
||||
position: absolute;
|
||||
top: 0; left: 0;
|
||||
width: 14px; height: 14px;
|
||||
cursor: nwse-resize;
|
||||
background: linear-gradient(135deg, transparent 35%, var(--text-dim) 35%, var(--text-dim) 50%, transparent 50%);
|
||||
border-top-left-radius: var(--radius-lg);
|
||||
z-index: 1;
|
||||
}
|
||||
.dock-resize:hover { background-color: rgba(var(--accent-rgb), 0.1); }
|
||||
.dock-head { padding: 0.75rem 1rem; border-bottom: 1px solid var(--line); display: flex; align-items: center; gap: 0.5rem; }
|
||||
.dock-title { font-weight: 600; }
|
||||
.dock-sub { color: var(--text-muted); font-size: 0.85rem; flex: 1; }
|
||||
.dock-reset, .dock-collapse { width: 28px; height: 28px; border-radius: var(--radius); color: var(--text-muted); }
|
||||
.dock-reset:hover, .dock-collapse:hover { background: var(--bg-3); color: var(--text); }
|
||||
.dock-box { flex: 1; padding: 0.75rem 1rem; overflow-y: auto; display: flex; flex-direction: column; gap: 0.75rem; }
|
||||
|
||||
.dock-msg {
|
||||
max-width: 90%;
|
||||
padding: 0.7rem 0.9rem;
|
||||
border-radius: var(--radius);
|
||||
font-size: 0.92rem;
|
||||
line-height: 1.55;
|
||||
word-break: break-word;
|
||||
}
|
||||
.dock-msg.user { align-self: flex-end; background: var(--accent-dark); color: white; white-space: pre-wrap; }
|
||||
.dock-msg.assistant { align-self: flex-start; background: var(--bg-3); color: var(--text); }
|
||||
.dock-msg.assistant.typing { font-style: italic; opacity: 0.7; }
|
||||
|
||||
/* MD content inside chat */
|
||||
.dock-md p { margin: 0 0 0.6rem; }
|
||||
.dock-md p:last-child { margin-bottom: 0; }
|
||||
.dock-md h1, .dock-md h2, .dock-md h3 { margin: 0.6rem 0 0.4rem; line-height: 1.3; }
|
||||
.dock-md h1 { font-size: 1.05rem; color: var(--accent-2); }
|
||||
.dock-md h2 { font-size: 1rem; color: var(--accent-2); }
|
||||
.dock-md h3 { font-size: 0.95rem; color: var(--text); }
|
||||
.dock-md ul, .dock-md ol { margin: 0.4rem 0 0.6rem; padding-left: 1.4rem; }
|
||||
.dock-md li { margin-bottom: 0.2rem; }
|
||||
.dock-md code { background: var(--bg); padding: 0.1rem 0.35rem; border-radius: 3px; font-size: 0.85em; color: var(--accent-2); }
|
||||
.dock-md pre { background: var(--bg); padding: 0.7rem 0.9rem; border-radius: var(--radius); overflow-x: auto; margin: 0.5rem 0; }
|
||||
.dock-md pre code { background: none; padding: 0; color: var(--text); }
|
||||
.dock-md strong { color: var(--text); }
|
||||
.dock-md em { color: var(--text-muted); font-style: italic; }
|
||||
.dock-md a { color: var(--accent-2); text-decoration: underline; }
|
||||
.dock-md table.md-table { border-collapse: collapse; margin: 0.5rem 0; font-size: 0.85em; width: auto; }
|
||||
.dock-md table.md-table th, .dock-md table.md-table td { border: 1px solid var(--line); padding: 0.3rem 0.6rem; }
|
||||
.dock-md table.md-table th { background: var(--bg); }
|
||||
.dock-md blockquote { border-left: 3px solid var(--accent); padding-left: 0.8rem; margin: 0.5rem 0; color: var(--text-muted); }
|
||||
|
||||
.dock-msg .sources {
|
||||
margin-top: 0.6rem; padding-top: 0.5rem; border-top: 1px solid var(--line);
|
||||
font-size: 0.72rem; color: var(--text-muted);
|
||||
}
|
||||
.dock-msg .sources .src-tag {
|
||||
display: inline-block; background: rgba(var(--accent-rgb), 0.15); color: var(--accent-2);
|
||||
padding: 0.1rem 0.4rem; border-radius: 3px; margin: 0.15rem 0.25rem 0 0; font-size: 0.7rem;
|
||||
}
|
||||
|
||||
/* Inline structured renderer (quiz/flashcards/case in chat) */
|
||||
.dock-struct {
|
||||
background: var(--bg);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: var(--radius);
|
||||
padding: 0.85rem;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
.dock-struct .ds-head { font-weight: 600; color: var(--accent-2); margin-bottom: 0.4rem; font-size: 0.85rem; }
|
||||
.dock-struct .ds-q { font-size: 0.9rem; margin-bottom: 0.5rem; }
|
||||
.dock-struct .ds-options { display: flex; flex-direction: column; gap: 0.3rem; }
|
||||
.dock-struct .ds-option {
|
||||
text-align: left; padding: 0.4rem 0.6rem;
|
||||
background: var(--bg-3); border: 1px solid var(--line);
|
||||
border-radius: 4px; font-size: 0.85rem;
|
||||
}
|
||||
.dock-struct .ds-option:hover:not(:disabled) { border-color: var(--accent); }
|
||||
.dock-struct .ds-option[data-state="correct"] { border-color: var(--success); background: rgba(16, 185, 129, 0.1); }
|
||||
.dock-struct .ds-option[data-state="wrong"] { border-color: var(--danger); background: rgba(239, 68, 68, 0.1); }
|
||||
|
||||
.dock-form { border-top: 1px solid var(--line); padding: 0.5rem; display: flex; gap: 0.5rem; }
|
||||
.dock-form textarea {
|
||||
flex: 1; resize: none;
|
||||
background: var(--bg); color: var(--text);
|
||||
border: 1px solid var(--line); border-radius: var(--radius);
|
||||
padding: 0.5rem 0.7rem; font-size: 0.9rem;
|
||||
max-height: 140px; min-height: 36px;
|
||||
font-family: inherit;
|
||||
}
|
||||
.dock-form textarea:focus { outline: none; border-color: var(--accent); }
|
||||
.btn-send { background: var(--accent); color: white; width: 40px; border-radius: var(--radius); font-weight: 700; font-size: 1.1rem; }
|
||||
.btn-send:hover { background: var(--accent-dark); }
|
||||
|
||||
.dock-open {
|
||||
position: fixed; bottom: 1rem; right: 1rem;
|
||||
background: var(--accent); color: white;
|
||||
padding: 0.7rem 1.2rem; border-radius: 24px;
|
||||
font-weight: 500; box-shadow: var(--shadow-lg);
|
||||
display: flex; align-items: center; gap: 0.5rem;
|
||||
z-index: 99;
|
||||
}
|
||||
.dock-open:hover { background: var(--accent-dark); }
|
||||
.dock-open[hidden] { display: none; }
|
||||
.dock-open-dot { width: 8px; height: 8px; background: var(--success); border-radius: 50%; display: inline-block; }
|
||||
|
||||
/* ─── Footer ───────────────────────────────────────────────────── */
|
||||
.footer { margin-top: 2rem; padding-top: 1rem; border-top: 1px solid var(--line); text-align: center; font-size: 0.8rem; color: var(--text-muted); }
|
||||
|
||||
/* ─── Toasts ───────────────────────────────────────────────────── */
|
||||
.toast-stack { position: fixed; top: 1rem; right: 1rem; display: flex; flex-direction: column; gap: 0.5rem; z-index: 200; pointer-events: none; }
|
||||
.toast { padding: 0.6rem 1rem; background: var(--bg-2); border: 1px solid var(--line); border-radius: var(--radius); box-shadow: var(--shadow-md); color: var(--text); font-size: 0.85rem; pointer-events: auto; animation: toast-in 0.2s ease-out; }
|
||||
.toast.error { border-color: var(--danger); color: var(--danger); }
|
||||
.toast.success { border-color: var(--success); color: var(--success); }
|
||||
@keyframes toast-in { from { transform: translateX(20px); opacity: 0; } to { transform: translateX(0); opacity: 1; } }
|
||||
|
||||
/* Mobile */
|
||||
@media (max-width: 800px) {
|
||||
.space-tabs, .folder-tabs { grid-template-columns: repeat(2, 1fr); }
|
||||
.space-tab { font-size: 0.85rem; padding: 0.7rem 0.4rem; }
|
||||
.chat-dock { width: calc(100vw - 2rem); height: calc(100vh - 6rem); right: 1rem; bottom: 1rem; }
|
||||
.dock-resize { display: none; }
|
||||
}
|
||||
1099
www/cockpit/cockpit.js
Normal file
1099
www/cockpit/cockpit.js
Normal file
File diff suppressed because it is too large
Load diff
290
www/cockpit/index.html
Normal file
290
www/cockpit/index.html
Normal file
|
|
@ -0,0 +1,290 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Luna · Tutor-Cockpit</title>
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover">
|
||||
<meta name="theme-color" content="#0a0a0f">
|
||||
<meta name="description" content="Luna — dein Tutor-Cockpit. Eigene Unterlagen, Klausur-Plan, Kompetenz-Diagnose, Klinikfälle.">
|
||||
<link rel="stylesheet" href="cockpit.css">
|
||||
<script>
|
||||
window.__LUNA_KEY__ = 'qb_drfbhe3w6j2r7199w2';
|
||||
window.__BOT_SLUG__ = 'physio-tutor';
|
||||
window.__BOT_ID__ = 'r3816r760kur14i';
|
||||
window.__API_BASE__ = 'https://api.qognio.com';
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="cockpit" role="application" aria-label="Luna Cockpit">
|
||||
|
||||
<header class="topbar">
|
||||
<div class="brand">
|
||||
<span class="brand-icon" aria-hidden="true">L</span>
|
||||
<div class="brand-text">
|
||||
<span class="brand-title">Luna <small>Cockpit</small></span>
|
||||
<span class="brand-sub">Eigene Unterlagen · Klausur-Plan · Kompetenz-Diagnose</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="spacer"></div>
|
||||
<div id="auth-state" class="auth-state">
|
||||
<span id="auth-user" class="auth-user"></span>
|
||||
<button id="auth-logout" class="btn-secondary" type="button" hidden>Abmelden</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- Login overlay -->
|
||||
<section id="login-screen" class="login-screen" hidden>
|
||||
<div class="login-card">
|
||||
<h2>Bei Luna anmelden</h2>
|
||||
<p class="login-hint">Damit Luna deine eigenen Dokumente verwenden kann, brauche ich deinen Login.</p>
|
||||
<form id="login-form" class="login-form">
|
||||
<label>E-Mail<input type="email" id="login-email" autocomplete="email" required></label>
|
||||
<label>Passwort<input type="password" id="login-pw" autocomplete="current-password" required></label>
|
||||
<button type="submit" class="btn-primary">Anmelden</button>
|
||||
<div id="login-error" class="login-error" role="alert" hidden></div>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<main id="main" class="main" hidden>
|
||||
<!-- Status banner -->
|
||||
<section class="status-block">
|
||||
<h2 class="status-title">Was Luna gerade weiß</h2>
|
||||
<div id="status-summary" class="status-summary">Lade…</div>
|
||||
</section>
|
||||
|
||||
<!-- Top-level Tabs -->
|
||||
<nav class="space-tabs" role="tablist" aria-label="Cockpit-Bereiche">
|
||||
<button class="space-tab" role="tab" aria-selected="true" data-space="dokumente">
|
||||
<span class="space-icon">📁</span><span class="space-label">Dokumente</span>
|
||||
</button>
|
||||
<button class="space-tab" role="tab" aria-selected="false" data-space="klausuren">
|
||||
<span class="space-icon">📋</span><span class="space-label">Klausur-Plan</span>
|
||||
</button>
|
||||
<button class="space-tab" role="tab" aria-selected="false" data-space="lernen">
|
||||
<span class="space-icon">🧠</span><span class="space-label">Lernen</span>
|
||||
</button>
|
||||
<button class="space-tab" role="tab" aria-selected="false" data-space="steuern">
|
||||
<span class="space-icon">🎛</span><span class="space-label">Bot steuern</span>
|
||||
</button>
|
||||
</nav>
|
||||
|
||||
<!-- ─── Space: Dokumente ─── -->
|
||||
<section class="space" data-space="dokumente" data-active="true">
|
||||
<nav class="folder-tabs" role="tablist" aria-label="Dokumenten-Ordner">
|
||||
<button class="folder-tab" role="tab" aria-selected="true" data-folder="curriculum">
|
||||
<span class="folder-icon">📚</span>
|
||||
<span class="folder-label">Curriculum</span>
|
||||
<span class="folder-count" data-count="curriculum">0</span>
|
||||
</button>
|
||||
<button class="folder-tab" role="tab" aria-selected="false" data-folder="official">
|
||||
<span class="folder-icon">🏛️</span>
|
||||
<span class="folder-label">Offizielle Doks</span>
|
||||
<span class="folder-count" data-count="official">0</span>
|
||||
</button>
|
||||
<button class="folder-tab" role="tab" aria-selected="false" data-folder="own">
|
||||
<span class="folder-icon">📝</span>
|
||||
<span class="folder-label">Eigene Notizen</span>
|
||||
<span class="folder-count" data-count="own">0</span>
|
||||
</button>
|
||||
<button class="folder-tab" role="tab" aria-selected="false" data-folder="role">
|
||||
<span class="folder-icon">🎯</span>
|
||||
<span class="folder-label">Schwerpunkt</span>
|
||||
<span class="folder-count" data-count="role">0</span>
|
||||
</button>
|
||||
</nav>
|
||||
<section class="folder-body">
|
||||
<div id="folder-help" class="folder-help"></div>
|
||||
<div class="upload-zone" id="upload-zone" tabindex="0">
|
||||
<input type="file" id="file-input" hidden multiple
|
||||
accept=".pdf,.txt,.md,.csv,.json,.png,.jpg,.jpeg,.webp">
|
||||
<div class="upload-cta">
|
||||
<span class="upload-icon">⬆️</span>
|
||||
<span class="upload-text">Datei hier ablegen oder klicken</span>
|
||||
<span class="upload-hint">PDF, Markdown, Text — max 20 MB</span>
|
||||
</div>
|
||||
<div id="upload-progress" class="upload-progress" hidden></div>
|
||||
</div>
|
||||
<ul id="doc-list" class="doc-list" aria-live="polite"></ul>
|
||||
<div id="doc-empty" class="doc-empty" hidden>Noch nichts hier — leg los mit dem ersten Upload.</div>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<!-- ─── Space: Klausuren ─── -->
|
||||
<section class="space" data-space="klausuren">
|
||||
<div class="space-intro">
|
||||
<h2>📋 Dein Klausur-Plan</h2>
|
||||
<p>Aus deinem Curriculum extrahiert. Klick auf eine Klausur, um Luna gezielt darauf vorzubereiten.</p>
|
||||
</div>
|
||||
<div id="klausur-list" class="klausur-list">
|
||||
<div class="empty-state" id="klausur-empty">Noch keine Klausuren erkannt. Lade ein Klausurplan-Dokument im Curriculum-Ordner hoch.</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ─── Space: Lernen ─── -->
|
||||
<section class="space" data-space="lernen">
|
||||
<div class="space-intro">
|
||||
<h2>🧠 Lernen & Üben</h2>
|
||||
<p>Wähle ein Thema aus deinem Klausur-Plan oder Curriculum, dann starte ein Mini-Spiel.</p>
|
||||
</div>
|
||||
|
||||
<!-- Heatmap: mastery per topic -->
|
||||
<section class="heatmap-block">
|
||||
<div class="heatmap-header">
|
||||
<h3 class="heatmap-title">Dein aktueller Stand</h3>
|
||||
<div class="heatmap-legend">
|
||||
<span class="legend-cell" data-level="0"></span><span>0 noch nicht</span>
|
||||
<span class="legend-cell" data-level="2"></span><span>2 in Arbeit</span>
|
||||
<span class="legend-cell" data-level="4"></span><span>4 stark</span>
|
||||
<span class="legend-cell" data-level="5"></span><span>5 gemeistert</span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="heatmap-grid" class="heatmap-grid">
|
||||
<div class="empty-state">Klick auf ein Thema unten, um deinen Stand erstmals zu erfassen.</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Topic picker -->
|
||||
<section class="topic-picker">
|
||||
<div class="topic-picker-head">
|
||||
<h3>Themen</h3>
|
||||
<select id="topic-source" class="select-mini">
|
||||
<option value="klausur">aus Klausur-Plan</option>
|
||||
<option value="physiologie-uke">Physiologie (UKE)</option>
|
||||
<option value="physiotherapie-aprv">Physiotherapie APrV</option>
|
||||
<option value="pflegeschule-flensburg">Pflege Flensburg</option>
|
||||
<option value="medizinische-terminologie">Med. Terminologie</option>
|
||||
<option value="anatomie-grundlagen">Anatomie Grundlagen</option>
|
||||
</select>
|
||||
</div>
|
||||
<div id="topic-grid" class="topic-grid"></div>
|
||||
</section>
|
||||
|
||||
<!-- Minigame Launcher (visible nachdem Topic gewählt) -->
|
||||
<section id="minigame-launcher" class="minigame-launcher" hidden>
|
||||
<div class="launcher-head">
|
||||
<span>Was willst du mit <strong id="launcher-topic">…</strong> machen?</span>
|
||||
<button class="btn-icon-x" id="launcher-close" aria-label="Schließen">✕</button>
|
||||
</div>
|
||||
<div class="launcher-grid">
|
||||
<button class="minigame-card" data-game="diagnose">
|
||||
<span class="mg-icon">🩺</span>
|
||||
<span class="mg-title">Kompetenz-Check</span>
|
||||
<span class="mg-desc">5 adaptive Fragen — Luna ermittelt deinen Stand.</span>
|
||||
<span class="mg-tag">Diagnose · 3-5 min</span>
|
||||
</button>
|
||||
<button class="minigame-card" data-game="klinikfall">
|
||||
<span class="mg-icon">🏥</span>
|
||||
<span class="mg-title">Klinikfall</span>
|
||||
<span class="mg-desc">Patient*in mit Symptomen — du triffst 3 Entscheidungen.</span>
|
||||
<span class="mg-tag">Anwendung · 5-7 min</span>
|
||||
</button>
|
||||
<button class="minigame-card" data-game="stimmt-das">
|
||||
<span class="mg-icon">🤔</span>
|
||||
<span class="mg-title">Stimmt das?</span>
|
||||
<span class="mg-desc">6 Aussagen — wahr oder Falle? Trainiert Misconceptions.</span>
|
||||
<span class="mg-tag">Retrieval · 3 min</span>
|
||||
</button>
|
||||
<button class="minigame-card" data-game="quiz-classic">
|
||||
<span class="mg-icon">📝</span>
|
||||
<span class="mg-title">Klassisches Quiz</span>
|
||||
<span class="mg-desc">10 Multiple-Choice-Fragen via Luna's Quiz-Engine.</span>
|
||||
<span class="mg-tag">Übung · 5 min</span>
|
||||
</button>
|
||||
<button class="minigame-card" data-game="flashcards">
|
||||
<span class="mg-icon">🃏</span>
|
||||
<span class="mg-title">Karteikarten</span>
|
||||
<span class="mg-desc">Spaced-Repetition mit Eigenbewertung pro Karte.</span>
|
||||
<span class="mg-tag">Memorisierung · 3-5 min</span>
|
||||
</button>
|
||||
<button class="minigame-card" data-game="explain">
|
||||
<span class="mg-icon">🎓</span>
|
||||
<span class="mg-title">Erklär's mir</span>
|
||||
<span class="mg-desc">Du erklärst — Luna gibt Feedback zu Klarheit + Vollständigkeit.</span>
|
||||
<span class="mg-tag">Articulation · 5 min</span>
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Minigame Stage (gefüllt während ein Spiel läuft) -->
|
||||
<section id="minigame-stage" class="minigame-stage" hidden></section>
|
||||
</section>
|
||||
|
||||
<!-- ─── Space: Steuern ─── -->
|
||||
<section class="space" data-space="steuern">
|
||||
<div class="space-intro">
|
||||
<h2>🎛 Bot steuern</h2>
|
||||
<p>Schreibe hier rein, wie Luna mit dir arbeiten soll. Beispiele:</p>
|
||||
<ul class="hint-list">
|
||||
<li>„Antworte ausführlicher und gib mehr klinische Beispiele"</li>
|
||||
<li>„Stell mir öfter sokratische Rückfragen statt direkt zu erklären"</li>
|
||||
<li>„Ich lerne lieber visuell — beschreib Diagramme detailliert"</li>
|
||||
<li>„Fokus auf Klausur 1 — ignoriere andere Themen wenn ich nicht explizit frage"</li>
|
||||
</ul>
|
||||
</div>
|
||||
<form id="persona-form" class="persona-form">
|
||||
<label class="persona-label">
|
||||
Deine Anweisungen an Luna
|
||||
<textarea id="persona-overrides" rows="10"
|
||||
placeholder="Schreibe hier deine Wünsche…"></textarea>
|
||||
</label>
|
||||
<div class="persona-actions">
|
||||
<span id="persona-status" class="persona-status">—</span>
|
||||
<button type="submit" class="btn-primary">Speichern</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<!-- Telegram-Pairing -->
|
||||
<section class="telegram-block">
|
||||
<div class="tg-head">
|
||||
<h3>📱 Mobil mit Telegram</h3>
|
||||
<span id="tg-status-badge" class="tg-badge">prüfe…</span>
|
||||
</div>
|
||||
<p class="tg-hint">
|
||||
Verbinde diesen Cockpit-Account mit dem
|
||||
<a href="https://t.me/qognioLunaBot" target="_blank" rel="noopener">@qognioLunaBot</a>
|
||||
auf Telegram. Dann kannst du Luna unterwegs fragen — und sie nutzt deinen Lernstand & Dokumente.
|
||||
</p>
|
||||
<div id="tg-link-area" class="tg-link-area">
|
||||
<button id="tg-generate" class="btn-primary" type="button">Code generieren</button>
|
||||
<button id="tg-unlink" class="btn-secondary" type="button" hidden>Verbindung lösen</button>
|
||||
</div>
|
||||
<div id="tg-code-display" class="tg-code-display" hidden>
|
||||
<p>Schicke diese Nachricht an
|
||||
<a href="https://t.me/qognioLunaBot" target="_blank" rel="noopener">@qognioLunaBot</a>
|
||||
auf Telegram:</p>
|
||||
<pre class="tg-code-line"><code id="tg-code-text"></code></pre>
|
||||
<p class="tg-code-hint">Code läuft in <span id="tg-code-ttl">15</span> Min ab.</p>
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<!-- Chat-Dock (resizable + MD-rendering + structured output) -->
|
||||
<aside id="chat-dock" class="chat-dock" aria-label="Luna Chat">
|
||||
<div class="dock-resize" id="dock-resize" aria-hidden="true"></div>
|
||||
<header class="dock-head">
|
||||
<span class="dock-title">Luna</span>
|
||||
<span class="dock-sub">deine Tutorin</span>
|
||||
<button class="dock-reset" type="button" id="dock-reset" title="Chat zurücksetzen">↺</button>
|
||||
<button class="dock-collapse" type="button" id="dock-collapse" title="Einklappen">✕</button>
|
||||
</header>
|
||||
<div id="dock-box" class="dock-box" aria-live="polite"></div>
|
||||
<form id="dock-form" class="dock-form">
|
||||
<textarea id="dock-input" rows="1" placeholder="Frag Luna — z.B. „erklär mir die Nephron-Funktion""></textarea>
|
||||
<button type="submit" class="btn-send" aria-label="Senden">→</button>
|
||||
</form>
|
||||
</aside>
|
||||
|
||||
<button id="dock-open" class="dock-open" type="button" aria-label="Luna-Chat öffnen">
|
||||
<span class="dock-open-dot"></span>Luna fragen
|
||||
</button>
|
||||
|
||||
<footer class="footer">
|
||||
Sovereign AI · Deutscher Bunker · <a href="https://qognio.com">Qognio</a> · Deine Daten bleiben bei dir
|
||||
</footer>
|
||||
</div>
|
||||
<div id="toast-stack" class="toast-stack" aria-live="polite"></div>
|
||||
<script src="cockpit.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
565
www/curricula.json
Normal file
565
www/curricula.json
Normal file
|
|
@ -0,0 +1,565 @@
|
|||
{
|
||||
"version": "2026-04-21",
|
||||
"updated": "2026-04-21",
|
||||
"curricula": [
|
||||
{
|
||||
"id": "physiologie-uke",
|
||||
"title": "Physiologie (UKE Hamburg)",
|
||||
"short": "UKE iMED + Biomedizin",
|
||||
"icon": "pulse",
|
||||
"color": "#ef4444",
|
||||
"description": "Integrierte Physiologie im UKE-Modellstudiengang iMED. 7 Systemmodule (A–G) mit Herz-Kreislauf, Atmung, Niere, Neuro, Endokrin.",
|
||||
"modules": [
|
||||
{
|
||||
"id": "zellphysiologie",
|
||||
"title": "Zellphysiologie & erregbare Zellen",
|
||||
"objectives": [
|
||||
"Ruhemembranpotenzial & Nernst-Gleichung erklären",
|
||||
"Aktionspotenzial-Phasen beschreiben",
|
||||
"Synaptische Übertragung (EPSP/IPSP)",
|
||||
"Na+/K+-ATPase, Kanäle, Transporter"
|
||||
],
|
||||
"topics": ["Membranpotenzial", "Aktionspotenzial", "Synapse", "Ionenkanäle", "Signaltransduktion"]
|
||||
},
|
||||
{
|
||||
"id": "muskelphysiologie",
|
||||
"title": "Muskelphysiologie",
|
||||
"objectives": [
|
||||
"Gleitfilamenttheorie & elektromechanische Kopplung",
|
||||
"Skelett-, Herz-, glatte Muskulatur differenzieren",
|
||||
"Kraft-Längen- und Kraft-Geschwindigkeits-Kurve",
|
||||
"Motorische Einheit & Rekrutierung"
|
||||
],
|
||||
"topics": ["Sarkomer", "Aktin-Myosin", "Tetanus", "Ermüdung", "Motor unit"]
|
||||
},
|
||||
{
|
||||
"id": "herz-kreislauf",
|
||||
"title": "Herz-Kreislauf-Physiologie",
|
||||
"objectives": [
|
||||
"Herzzyklus & Wiggers-Diagramm interpretieren",
|
||||
"EKG-Ableitungen & normale Komplexe erkennen",
|
||||
"Frank-Starling-Mechanismus erklären",
|
||||
"Blutdruckregulation kurz-/langfristig",
|
||||
"Mikrozirkulation & Starling-Gleichgewicht"
|
||||
],
|
||||
"topics": ["Herzzyklus", "EKG", "Kontraktilität", "RAAS", "Barorezeptoren", "Kapillaren"]
|
||||
},
|
||||
{
|
||||
"id": "atmung",
|
||||
"title": "Atmung & Gasaustausch",
|
||||
"objectives": [
|
||||
"Lungenvolumina & Spirometrie interpretieren",
|
||||
"Compliance, Resistance, Surfactant",
|
||||
"Gastransport mit O2-Bindungskurve und Bohr-Effekt",
|
||||
"Säure-Basen-Haushalt & renal/respiratorische Kompensation"
|
||||
],
|
||||
"topics": ["FRC", "Totraum", "V/Q", "Hämoglobin", "pH", "Bikarbonat"]
|
||||
},
|
||||
{
|
||||
"id": "niere",
|
||||
"title": "Niere & Wasser-Elektrolyt-Haushalt",
|
||||
"objectives": [
|
||||
"GFR-Konzept mit Clearance (Inulin, Kreatinin)",
|
||||
"Tubuläre Reabsorption & Sekretion pro Abschnitt",
|
||||
"Gegenstromprinzip & ADH",
|
||||
"RAAS, Blutvolumenregulation"
|
||||
],
|
||||
"topics": ["Nephron", "Clearance", "ADH", "Aldosteron", "Elektrolyte"]
|
||||
},
|
||||
{
|
||||
"id": "endokrin",
|
||||
"title": "Endokrinologie",
|
||||
"objectives": [
|
||||
"Hypothalamus-Hypophysen-Achse",
|
||||
"Schilddrüse, Nebenniere, Pankreas-Inseln",
|
||||
"Glucose-Homöostase Insulin/Glukagon",
|
||||
"Ca-Regulation (PTH, Calcitriol, Calcitonin)"
|
||||
],
|
||||
"topics": ["Hormone", "Insulin", "Cortisol", "Schilddrüse", "Calcium"]
|
||||
},
|
||||
{
|
||||
"id": "nervensystem",
|
||||
"title": "Nervensystem & Sinne",
|
||||
"objectives": [
|
||||
"Aufbau ZNS/PNS, vegetatives NS",
|
||||
"Somatosensorik, Schmerz, propriozeptive Wahrnehmung",
|
||||
"Visuelles, auditives und vestibuläres System",
|
||||
"Motorisches System: Pyramidenbahn + Kleinhirn"
|
||||
],
|
||||
"topics": ["Sympathikus", "Parasympathikus", "Nozizeption", "Vestibular", "Pyramidenbahn"]
|
||||
},
|
||||
{
|
||||
"id": "blut",
|
||||
"title": "Blut & Hämostase",
|
||||
"objectives": [
|
||||
"Erythropoese, Hämoglobin-Varianten",
|
||||
"Blutgruppen ABO + Rh",
|
||||
"Primäre & sekundäre Hämostase",
|
||||
"Fibrinolyse"
|
||||
],
|
||||
"topics": ["Erythrozyten", "ABO", "Thrombozyten", "Gerinnungskaskade"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "physiotherapie-aprv",
|
||||
"title": "Physiotherapie (PhysTh-APrV)",
|
||||
"short": "Staatliche Ausbildung 2900/1600 h",
|
||||
"icon": "activity",
|
||||
"color": "#a855f7",
|
||||
"description": "Bundesgesetzliche Grundlage für die 3-jährige Physiotherapie-Ausbildung. 2.900 h Theorie + 1.600 h Praxis. Staatliche Prüfung.",
|
||||
"modules": [
|
||||
{
|
||||
"id": "grundlagen",
|
||||
"title": "Berufsgrundlagen & Recht",
|
||||
"objectives": [
|
||||
"PhysTh-APrV-Struktur kennen",
|
||||
"Hygienerichtlinien anwenden",
|
||||
"Erste Hilfe sicher durchführen",
|
||||
"Berufs- und Gesetzeskunde"
|
||||
],
|
||||
"topics": ["APrV", "Hygiene", "Erste Hilfe", "Berufsrecht"],
|
||||
"hours": 100
|
||||
},
|
||||
{
|
||||
"id": "anatomie-physio-aprv",
|
||||
"title": "Anatomie (240 h) & Physiologie (140 h)",
|
||||
"objectives": [
|
||||
"Bewegungsapparat detailliert",
|
||||
"Innere Organe Situs",
|
||||
"Neuroanatomie Grundlagen",
|
||||
"Physiologische Regelkreise"
|
||||
],
|
||||
"topics": ["Muskeln", "Gelenke", "Nerven", "Organe"],
|
||||
"hours": 380
|
||||
},
|
||||
{
|
||||
"id": "krankheitslehre",
|
||||
"title": "Spezielle Krankheitslehre (360 h)",
|
||||
"objectives": [
|
||||
"Orthopädische Krankheitsbilder",
|
||||
"Neurologische Syndrome erkennen",
|
||||
"Innere Medizin Grundlagen",
|
||||
"Chirurgie und Traumatologie"
|
||||
],
|
||||
"topics": ["Innere", "Ortho", "Neuro", "Chirurgie", "Päd", "Psychiatrie", "Gyn", "Dermato", "Geriatrie", "Rheuma", "Arbeitsmed", "Sportmed"],
|
||||
"hours": 360
|
||||
},
|
||||
{
|
||||
"id": "bewegungslehre",
|
||||
"title": "Bewegungslehre & Trainingslehre",
|
||||
"objectives": [
|
||||
"Kinematik, Kinetik, biomechanische Prinzipien",
|
||||
"Trainingsplanung aufbauen",
|
||||
"Belastungssteuerung (FITT)",
|
||||
"Motorisches Lernen"
|
||||
],
|
||||
"topics": ["Biomechanik", "Ausdauer", "Kraft", "Koordination", "Beweglichkeit"],
|
||||
"hours": 160
|
||||
},
|
||||
{
|
||||
"id": "befund",
|
||||
"title": "Befund- und Untersuchungstechniken (100 h)",
|
||||
"objectives": [
|
||||
"Strukturierte Anamnese",
|
||||
"Gelenkmessung Neutral-Null",
|
||||
"Muskelfunktionstest (Janda)",
|
||||
"Spezielle Tests (z. B. Lachman, O'Brien, Thomas)",
|
||||
"ICF-Dokumentation"
|
||||
],
|
||||
"topics": ["Anamnese", "Inspektion", "Palpation", "ROM", "MFT", "Spezialtests"],
|
||||
"hours": 100
|
||||
},
|
||||
{
|
||||
"id": "kg-techniken",
|
||||
"title": "Krankengymnastische Behandlungstechniken (500 h)",
|
||||
"objectives": [
|
||||
"Manuelle Therapie (Kaltenborn, Maitland, Mulligan)",
|
||||
"PNF verstehen und anwenden",
|
||||
"Bobath, Vojta für Neuro",
|
||||
"Atemtherapie",
|
||||
"Schroth bei Skoliose, McKenzie",
|
||||
"MTT & gerätegestütztes Training"
|
||||
],
|
||||
"topics": ["MT", "PNF", "Bobath", "Vojta", "Schroth", "McKenzie", "Atemtherapie", "MTT"],
|
||||
"hours": 500
|
||||
},
|
||||
{
|
||||
"id": "massage-physik",
|
||||
"title": "Massage & Physikalische Therapie",
|
||||
"objectives": [
|
||||
"Klassische Massage & Reflexzonen",
|
||||
"Lymphdrainage nach Vodder",
|
||||
"Elektrotherapie (TENS, Iontophorese, Galvanik)",
|
||||
"Hydro-, Balneo-, Thermotherapie",
|
||||
"Ultraschall, Licht/Strahlen"
|
||||
],
|
||||
"topics": ["Massage", "MLD", "TENS", "Ultraschall", "Fango", "Inhalation"],
|
||||
"hours": 270
|
||||
},
|
||||
{
|
||||
"id": "methodische-anwendung",
|
||||
"title": "Methodische Anwendung in Fachgebieten (700 h)",
|
||||
"objectives": [
|
||||
"Behandlungsaufbau in Orthopädie, Chirurgie, Innerer Medizin",
|
||||
"Neurorehabilitation (Schlaganfall, MS, Parkinson)",
|
||||
"Pädiatrie (Bobath-Säugling, ICP)",
|
||||
"Psychiatrie (konzentrative Bewegungstherapie)",
|
||||
"Gynäkologie (Rückbildung)"
|
||||
],
|
||||
"topics": ["Ortho-Reha", "Innere-Reha", "Neuro-Reha", "Päd", "Geriatrie", "Gyn"],
|
||||
"hours": 700
|
||||
},
|
||||
{
|
||||
"id": "pruefung",
|
||||
"title": "Staatliche Prüfung",
|
||||
"objectives": [
|
||||
"4 schriftliche Aufsichtsarbeiten bestehen",
|
||||
"3 mündliche Prüfungen (Anatomie, Physiologie, spez. Krankheitslehre)",
|
||||
"3 praktische Prüfungen an Patient:innen"
|
||||
],
|
||||
"topics": ["Schriftlich", "Mündlich", "Praktisch"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "pflegeschule-flensburg",
|
||||
"title": "Pflege (ÖBiZ/DIAKO Flensburg)",
|
||||
"short": "Generalistische Pflegeausbildung",
|
||||
"icon": "heart",
|
||||
"color": "#ec4899",
|
||||
"description": "Generalistische Pflegeausbildung nach PflBG/PflAPrV: 2.100 h Theorie + 2.500 h Praxis, 11 curriculare Einheiten, 5 Kompetenzbereiche.",
|
||||
"modules": [
|
||||
{
|
||||
"id": "ce01",
|
||||
"title": "CE 01: Ausbildungsstart & wissenschaftliches Fundament",
|
||||
"objectives": [
|
||||
"Pflegeverständnis entwickeln",
|
||||
"Pflegeprozess (6 Schritte) anwenden",
|
||||
"Berufsidentität reflektieren"
|
||||
],
|
||||
"topics": ["Pflegeverständnis", "Pflegeprozess", "Berufsrolle"]
|
||||
},
|
||||
{
|
||||
"id": "ce02",
|
||||
"title": "CE 02: Hochbelastete & krisenhafte Situationen",
|
||||
"objectives": [
|
||||
"Reanimation durchführen (BLS + AED)",
|
||||
"Akute Verwirrtheit erkennen",
|
||||
"Sterbebegleitung gestalten"
|
||||
],
|
||||
"topics": ["Reanimation", "Delir", "Palliativpflege"]
|
||||
},
|
||||
{
|
||||
"id": "ce03",
|
||||
"title": "CE 03: Verstehens- und Aushandlungsprozesse",
|
||||
"objectives": [
|
||||
"Patientengespräch gestalten",
|
||||
"Beobachtung strukturieren",
|
||||
"Reflexion eigener Einstellungen"
|
||||
],
|
||||
"topics": ["Kommunikation", "Empathie", "Reflexion"]
|
||||
},
|
||||
{
|
||||
"id": "ce04",
|
||||
"title": "CE 04: Gesundheitsförderung & Prävention",
|
||||
"objectives": [
|
||||
"Primär-/Sekundär-/Tertiärprävention",
|
||||
"Lebensstil-Beratung (Bewegung, Ernährung)",
|
||||
"Impfungen nach STIKO"
|
||||
],
|
||||
"topics": ["Gesundheitsförderung", "Prävention", "Beratung"]
|
||||
},
|
||||
{
|
||||
"id": "ce05",
|
||||
"title": "CE 05: Kurative Prozesse & Patientensicherheit",
|
||||
"objectives": [
|
||||
"Medikamenten-Management",
|
||||
"OP-Vorbereitung und Nachsorge",
|
||||
"Fehlervermeidung / CIRS"
|
||||
],
|
||||
"topics": ["Medikation", "Perioperativ", "Patientensicherheit"]
|
||||
},
|
||||
{
|
||||
"id": "ce06",
|
||||
"title": "CE 06: Akutsituationen",
|
||||
"objectives": [
|
||||
"Notfälle erkennen und erstversorgen",
|
||||
"Intensivpflege-Basics",
|
||||
"Monitoring"
|
||||
],
|
||||
"topics": ["Notfall", "ITS", "Monitoring"]
|
||||
},
|
||||
{
|
||||
"id": "ce07",
|
||||
"title": "CE 07: Rehabilitation & chronische Erkrankungen",
|
||||
"objectives": [
|
||||
"Rehabilitative Pflege",
|
||||
"Krankheits-Selbstmanagement fördern",
|
||||
"Expertenstandards anwenden (Mobilität, Schmerz, Wunden)"
|
||||
],
|
||||
"topics": ["Reha", "Chronisch", "Expertenstandards"]
|
||||
},
|
||||
{
|
||||
"id": "ce08",
|
||||
"title": "CE 08: Kritische Lebenssituationen",
|
||||
"objectives": [
|
||||
"Demenzbegleitung",
|
||||
"Psychiatrische Settings",
|
||||
"Sucht, Gewalt, Suizidalität"
|
||||
],
|
||||
"topics": ["Demenz", "Psychiatrie", "Sucht"]
|
||||
},
|
||||
{
|
||||
"id": "ce09",
|
||||
"title": "CE 09: Eintritt in neue Lebensphasen",
|
||||
"objectives": [
|
||||
"Pädiatrie: Säuglings- und Kinderpflege",
|
||||
"Wochenbettpflege",
|
||||
"Geriatrische Eintritts- und Übergangsphasen"
|
||||
],
|
||||
"topics": ["Kinder", "Wochenbett", "Geriatrie"]
|
||||
},
|
||||
{
|
||||
"id": "ce10",
|
||||
"title": "CE 10: Kognitive & psychische Beeinträchtigungen",
|
||||
"objectives": [
|
||||
"Demenz-spezifische Pflege (DNQP-Standard)",
|
||||
"Herausforderndes Verhalten verstehen",
|
||||
"Milieu- und Biografiearbeit"
|
||||
],
|
||||
"topics": ["Demenz", "Depression", "Verhalten"]
|
||||
},
|
||||
{
|
||||
"id": "ce11",
|
||||
"title": "CE 11: Berufliches Selbstverständnis",
|
||||
"objectives": [
|
||||
"Professionalisierung",
|
||||
"Resilienz",
|
||||
"Supervision nutzen"
|
||||
],
|
||||
"topics": ["Professionalität", "Resilienz", "Supervision"]
|
||||
},
|
||||
{
|
||||
"id": "standards",
|
||||
"title": "Expertenstandards DNQP",
|
||||
"objectives": [
|
||||
"9 Standards kennen und anwenden",
|
||||
"Dekubitus-, Sturz-, Schmerz-, Wund-, Kontinenz-, Ernährungs-, Entlassungs-, Mobilitäts-, Demenz-Standard"
|
||||
],
|
||||
"topics": ["Dekubitus", "Sturz", "Schmerz", "Wunden", "Kontinenz", "Ernährung", "Entlassung", "Mobilität", "Demenz"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "medizinische-terminologie",
|
||||
"title": "Medizinische Terminologie",
|
||||
"short": "Latein/Griechisch + Wortbildung",
|
||||
"icon": "book",
|
||||
"color": "#f59e0b",
|
||||
"description": "Pflichtkurs 1. Semester: lateinisch/griechische Fachsprache, Wortbildung, Nomina Anatomica, klinische Terminologie.",
|
||||
"modules": [
|
||||
{
|
||||
"id": "geschichte",
|
||||
"title": "Geschichte der Fachsprache",
|
||||
"objectives": [
|
||||
"Hippokrates, Galen, Vesalius verorten",
|
||||
"Nomina Anatomica / Terminologia Anatomica kennen",
|
||||
"Rolle des Latein vs. Englisch"
|
||||
],
|
||||
"topics": ["Hippokrates", "Galen", "TA", "Nomenklatur"]
|
||||
},
|
||||
{
|
||||
"id": "grammatik",
|
||||
"title": "Lateinische Grammatik (Basics)",
|
||||
"objectives": [
|
||||
"5 Deklinationen sicher beherrschen",
|
||||
"Adjektiv-Substantiv-Kongruenz",
|
||||
"Nominativ & Genitiv im Fachbegriff"
|
||||
],
|
||||
"topics": ["Deklination", "Kasus", "Adjektiv", "Plural"]
|
||||
},
|
||||
{
|
||||
"id": "wortbildung",
|
||||
"title": "Wortbildung",
|
||||
"objectives": [
|
||||
"Präfix + Stamm + Suffix identifizieren",
|
||||
"Verbindungsvokale (o/i) richtig setzen",
|
||||
"Komposita zerlegen"
|
||||
],
|
||||
"topics": ["Präfix", "Suffix", "Kompositum", "Stamm"]
|
||||
},
|
||||
{
|
||||
"id": "praefixe",
|
||||
"title": "Wichtige Präfixe",
|
||||
"objectives": [
|
||||
"Griechische Präfixe (a-, dys-, hyper-, hypo-, tachy-, brady-)",
|
||||
"Lateinische Präfixe (sub-, supra-, inter-, intra-, retro-)",
|
||||
"Bedeutungsnuancen unterscheiden"
|
||||
],
|
||||
"topics": ["a-/an-", "dys-", "hyper-/hypo-", "tachy-/brady-", "sub-/supra-", "inter-/intra-"]
|
||||
},
|
||||
{
|
||||
"id": "suffixe",
|
||||
"title": "Wichtige Suffixe",
|
||||
"objectives": [
|
||||
"-itis vs -ose (entzündlich vs nicht)",
|
||||
"-ektomie, -otomie, -stomie (operative Eingriffe)",
|
||||
"-algie, -rrhagie, -ämie"
|
||||
],
|
||||
"topics": ["-itis", "-ose", "-ektomie", "-algie", "-rrhö"]
|
||||
},
|
||||
{
|
||||
"id": "wortstaemme",
|
||||
"title": "Wortstämme (Organe)",
|
||||
"objectives": [
|
||||
"Herz (cardi-), Niere (nephr-/ren-), Lunge (pneumo-/pulmo-)",
|
||||
"Darm (enter-), Leber (hepat-), Magen (gastr-)",
|
||||
"Synonymie Latein/Griechisch"
|
||||
],
|
||||
"topics": ["cardi-", "nephr-", "hepat-", "gastr-", "neuro-", "arthr-"]
|
||||
},
|
||||
{
|
||||
"id": "lagebeziehungen",
|
||||
"title": "Lage & Richtung",
|
||||
"objectives": [
|
||||
"superior/inferior, anterior/posterior etc.",
|
||||
"3 Ebenen (sagittal, frontal, transversal)",
|
||||
"proximal/distal am Gliedmaß"
|
||||
],
|
||||
"topics": ["Richtungen", "Ebenen", "Achsen"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "anatomie-grundlagen",
|
||||
"title": "Anatomie & medizinische Grundlagen",
|
||||
"short": "Vorklinik-Basics für alle",
|
||||
"icon": "user",
|
||||
"color": "#10b981",
|
||||
"description": "Grundlagen der Anatomie, allgemeinen Pathologie, klinischen Untersuchung, Hygiene, Pharmakologie — Querschnitt für alle 4 Kern-Curricula.",
|
||||
"modules": [
|
||||
{
|
||||
"id": "allg-anatomie",
|
||||
"title": "Allgemeine Anatomie",
|
||||
"objectives": [
|
||||
"Gewebe (Epithel, Binde, Knorpel, Knochen, Muskel, Nerv)",
|
||||
"Lagebezeichnungen und Ebenen",
|
||||
"Bewegungsrichtungen benennen"
|
||||
],
|
||||
"topics": ["Gewebe", "Ebenen", "Bewegungen"]
|
||||
},
|
||||
{
|
||||
"id": "bewegungsapparat",
|
||||
"title": "Bewegungsapparat",
|
||||
"objectives": [
|
||||
"Knochen der oberen und unteren Extremität",
|
||||
"Wichtigste Muskeln mit Ursprung, Ansatz, Funktion",
|
||||
"Gelenktypen erkennen und beschreiben"
|
||||
],
|
||||
"topics": ["Knochen", "Muskeln", "Gelenke", "Wirbelsäule"]
|
||||
},
|
||||
{
|
||||
"id": "situs",
|
||||
"title": "Situs (innere Organe)",
|
||||
"objectives": [
|
||||
"Thorax-Organe in Lagebeziehung",
|
||||
"Abdomen Ober-/Mittel-/Unterbauch",
|
||||
"Retroperitoneale Organe"
|
||||
],
|
||||
"topics": ["Thorax", "Abdomen", "Retroperitoneal"]
|
||||
},
|
||||
{
|
||||
"id": "neuroanatomie",
|
||||
"title": "Neuroanatomie",
|
||||
"objectives": [
|
||||
"Aufbau Gehirn (Lappen, Hirnstamm, Cerebellum)",
|
||||
"Rückenmark-Gliederung (31 Segmente)",
|
||||
"12 Hirnnerven benennen",
|
||||
"Plexus: cervicalis, brachialis, lumbalis, sacralis"
|
||||
],
|
||||
"topics": ["Gehirn", "Rückenmark", "Hirnnerven", "Plexus"]
|
||||
},
|
||||
{
|
||||
"id": "biochemie-basics",
|
||||
"title": "Biochemie-Basics",
|
||||
"objectives": [
|
||||
"Makromoleküle (Kohlenhydrate, Lipide, Proteine, NS)",
|
||||
"Zitratzyklus und Atmungskette",
|
||||
"Glukose-ATP-Bilanz"
|
||||
],
|
||||
"topics": ["Stoffwechsel", "ATP", "Aminosäuren", "Fettsäuren"]
|
||||
},
|
||||
{
|
||||
"id": "pathologie-basics",
|
||||
"title": "Allgemeine Pathologie",
|
||||
"objectives": [
|
||||
"Nekrose vs. Apoptose",
|
||||
"Entzündung: 5 Kardinalzeichen",
|
||||
"Tumorlehre (TNM, Grading)",
|
||||
"Ischämie, Thrombose, Ödem"
|
||||
],
|
||||
"topics": ["Zelltod", "Entzündung", "Tumor", "Infarkt"]
|
||||
},
|
||||
{
|
||||
"id": "klinische-untersuchung",
|
||||
"title": "Klinische Untersuchung",
|
||||
"objectives": [
|
||||
"Strukturierte Anamnese (OPQRST)",
|
||||
"Vitalzeichen interpretieren",
|
||||
"Inspektion, Palpation, Perkussion, Auskultation"
|
||||
],
|
||||
"topics": ["Anamnese", "Vitals", "Untersuchung"]
|
||||
},
|
||||
{
|
||||
"id": "hygiene",
|
||||
"title": "Hygiene",
|
||||
"objectives": [
|
||||
"5 Indikationen der Händedesinfektion (WHO)",
|
||||
"Isolationsarten (Kontakt/Tröpfchen/Aerogen)",
|
||||
"Sterilisation vs. Desinfektion"
|
||||
],
|
||||
"topics": ["Händehygiene", "Isolation", "Sterilisation"]
|
||||
},
|
||||
{
|
||||
"id": "pharma-basics",
|
||||
"title": "Pharmakologie-Basics",
|
||||
"objectives": [
|
||||
"LADMET-Prinzip",
|
||||
"Wichtige Wirkstoffgruppen (NSAR, Antikoagulantien, Antihypertensiva)",
|
||||
"Kontraindikationen und Interaktionen"
|
||||
],
|
||||
"topics": ["Kinetik", "Dynamik", "Wirkstoffgruppen"]
|
||||
},
|
||||
{
|
||||
"id": "notfall",
|
||||
"title": "Notfall-Basics",
|
||||
"objectives": [
|
||||
"BLS-Algorithmus (ERC)",
|
||||
"Stabile Seitenlage",
|
||||
"FAST bei Schlaganfall"
|
||||
],
|
||||
"topics": ["Reanimation", "Seitenlage", "Schlaganfall"]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"badges": [
|
||||
{"id": "first_quiz", "title": "Erster Quiz-Durchlauf", "icon": "award", "description": "Du hast dein erstes Quiz absolviert."},
|
||||
{"id": "10_quiz_streak", "title": "10er-Serie", "icon": "flame", "description": "10 richtige Antworten in Folge."},
|
||||
{"id": "100_answers", "title": "Zentner", "icon": "star", "description": "100 Antworten insgesamt gegeben."},
|
||||
{"id": "7_day_streak", "title": "Wochen-Streak", "icon": "calendar", "description": "7 Tage in Folge aktiv."},
|
||||
{"id": "curriculum_complete", "title": "Curriculum-Meister", "icon": "crown", "description": "Ein Curriculum vollständig durchgearbeitet."},
|
||||
{"id": "night_owl", "title": "Nachteule", "icon": "moon", "description": "Nach 22 Uhr gelernt."},
|
||||
{"id": "early_bird", "title": "Frühaufsteher", "icon": "sun", "description": "Vor 7 Uhr gelernt."}
|
||||
],
|
||||
"levels": [
|
||||
{"min": 0, "title": "Anfängerin"},
|
||||
{"min": 50, "title": "Einsteigerin"},
|
||||
{"min": 200, "title": "Fortgeschrittene"},
|
||||
{"min": 500, "title": "Profi"},
|
||||
{"min": 1250, "title": "Expertin"},
|
||||
{"min": 2500, "title": "Meisterin"},
|
||||
{"min": 5000, "title": "Großmeisterin"}
|
||||
]
|
||||
}
|
||||
121
www/index.html
Normal file
121
www/index.html
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Luna · PhysioTutor</title>
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover">
|
||||
<meta name="theme-color" content="#0a0a0f">
|
||||
<meta name="description" content="Luna — dein KI-PhysioTutor. Gamified lernen mit Chat, Quiz, Flashcards und Fortschritts-Tracking. Läuft im deutschen Rechenzentrum.">
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
<script>window.__LUNA_KEY__ = 'qb_drfbhe3w6j2r7199w2';</script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="app" role="application" aria-label="Luna PhysioTutor">
|
||||
|
||||
<header class="topbar">
|
||||
<div class="brand">
|
||||
<span class="brand-icon" aria-hidden="true">L</span>
|
||||
<span>Luna <small>PhysioTutor</small></span>
|
||||
</div>
|
||||
<div class="spacer"></div>
|
||||
<span class="status" role="status" aria-live="polite">Online</span>
|
||||
</header>
|
||||
|
||||
<nav class="tabbar" role="tablist" aria-label="Modi">
|
||||
<button class="tab" role="tab" aria-selected="true" aria-controls="view-chat" data-mode="chat">
|
||||
Chat
|
||||
<span class="tab-kbd">⌃1</span>
|
||||
</button>
|
||||
<button class="tab" role="tab" aria-selected="false" aria-controls="view-quiz" data-mode="quiz">
|
||||
Quiz
|
||||
<span class="tab-kbd">⌃2</span>
|
||||
</button>
|
||||
<button class="tab" role="tab" aria-selected="false" aria-controls="view-flash" data-mode="flash">
|
||||
Karten
|
||||
<span class="tab-kbd">⌃3</span>
|
||||
</button>
|
||||
<button class="tab" role="tab" aria-selected="false" aria-controls="view-progress" data-mode="progress">
|
||||
Fortschritt
|
||||
<span class="tab-kbd">⌃4</span>
|
||||
</button>
|
||||
<button class="tab" role="tab" aria-selected="false" aria-controls="view-curriculum" data-mode="curriculum">
|
||||
Lehrplan
|
||||
<span class="tab-kbd">⌃5</span>
|
||||
</button>
|
||||
</nav>
|
||||
|
||||
<main class="main">
|
||||
<!-- CHAT -->
|
||||
<section id="view-chat" class="view" role="tabpanel" aria-labelledby="tab-chat" data-active="true">
|
||||
<div id="welcome-screen" class="welcome hidden" aria-hidden="true">
|
||||
<h2>Willkommen bei Luna!</h2>
|
||||
<p>Ich bin dein:e KI-Tutor:in für Physiotherapie, Pflege, Physiologie & Anatomie. Alles läuft im deutschen Rechenzentrum — keine Daten verlassen Europa.</p>
|
||||
<div class="mode-grid">
|
||||
<button class="mode-card" data-goto="chat">
|
||||
<strong>💬 Chat</strong>
|
||||
<span>Frag mich alles zu deinem Stoff, Sokratisch erklärt.</span>
|
||||
</button>
|
||||
<button class="mode-card" data-goto="quiz">
|
||||
<strong>🎯 Quiz</strong>
|
||||
<span>Multiple-Choice mit Erklärungen und XP-Belohnung.</span>
|
||||
</button>
|
||||
<button class="mode-card" data-goto="flash">
|
||||
<strong>🃏 Flashcards</strong>
|
||||
<span>Karteikarten mit Spaced-Repetition.</span>
|
||||
</button>
|
||||
<button class="mode-card" data-goto="progress">
|
||||
<strong>📊 Fortschritt</strong>
|
||||
<span>XP, Streaks, Mastery, Abzeichen.</span>
|
||||
</button>
|
||||
<button class="mode-card" data-goto="curriculum">
|
||||
<strong>📚 Lehrplan</strong>
|
||||
<span>Kompletter Themenbaum — 5 Curricula.</span>
|
||||
</button>
|
||||
</div>
|
||||
<p style="font-size:.82rem;color:var(--text-mute)">In 3 Sätzen: Chat für Verständnis → Quiz zum Testen → Flashcards zum Merken. Fortschritt zeigt dir, was schon sitzt; der Lehrplan gibt Orientierung.</p>
|
||||
</div>
|
||||
|
||||
<div id="chat-box" class="chat-box" aria-live="polite" aria-label="Gespräch"></div>
|
||||
</section>
|
||||
|
||||
<!-- QUIZ -->
|
||||
<section id="view-quiz" class="view" role="tabpanel" aria-labelledby="tab-quiz">
|
||||
<div id="quiz-host"></div>
|
||||
</section>
|
||||
|
||||
<!-- FLASHCARDS -->
|
||||
<section id="view-flash" class="view" role="tabpanel" aria-labelledby="tab-flash">
|
||||
<div id="flash-host"></div>
|
||||
</section>
|
||||
|
||||
<!-- PROGRESS -->
|
||||
<section id="view-progress" class="view" role="tabpanel" aria-labelledby="tab-progress">
|
||||
<div id="progress-host"></div>
|
||||
</section>
|
||||
|
||||
<!-- CURRICULUM -->
|
||||
<section id="view-curriculum" class="view" role="tabpanel" aria-labelledby="tab-curr">
|
||||
<div id="curr-host"></div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<form id="composer-form" class="composer" aria-label="Nachricht verfassen">
|
||||
<div id="attach-strip" class="attach-strip" aria-live="polite"></div>
|
||||
<div class="composer-row">
|
||||
<button type="button" class="btn-attach" id="composer-attach" aria-label="Datei anhängen" title="Datei anhängen (PDF, Bild, Text — max 5 Dateien, 8 MB)">📎</button>
|
||||
<input type="file" id="composer-file" multiple accept=".pdf,.txt,.md,.csv,.json,.xml,.yaml,.yml,.log,.png,.jpg,.jpeg,.webp,.gif" hidden>
|
||||
<textarea id="composer" rows="1" placeholder="Frag Luna — Enter zum Senden, Shift+Enter für Zeilenumbruch" aria-label="Nachricht"></textarea>
|
||||
<button type="submit" class="btn-primary" id="composer-send">Senden</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<footer class="footer">
|
||||
Sovereign AI · Deutscher Bunker · <a href="https://qognio.com">Qognio</a> · DSGVO-konform · Keine externen Fonts · Keine Cookies
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<div id="toast-stack" class="toast-stack" aria-live="polite"></div>
|
||||
|
||||
<script src="app.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
1038
www/styles.css
Normal file
1038
www/styles.css
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue