109 lines
7.6 KiB
HTML
109 lines
7.6 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="de" data-theme="light">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>{% block title %}AI Workspace{% endblock %} · Bifröst</title>
|
||
<!-- Theme vor dem ersten Rendern, ohne Inline-Skript (strenge CSP) -->
|
||
<script src="{{ asset('js/theme.min.js') }}"></script>
|
||
<!-- 422 (Eingabefehler) wird wie eine normale Antwort eingesetzt, damit die Meldung erscheint -->
|
||
<meta name="htmx-config" content='{"includeIndicatorStyles": false, "allowEval": false, "allowScriptTags": false, "responseHandling": [{"code": "204", "swap": false}, {"code": "[23]..", "swap": true}, {"code": "422", "swap": true}, {"code": "[45]..", "swap": false, "error": true}]}'>
|
||
<link rel="stylesheet" href="{{ asset('css/fonts.css') }}">
|
||
<link rel="stylesheet" href="{{ asset('css/main.min.css') }}">
|
||
<link rel="stylesheet" href="{{ asset('css/app.css') }}">
|
||
<script src="{{ asset('js/htmx.min.js') }}" defer></script>
|
||
<script src="{{ asset('js/framework.min.js') }}" defer></script>
|
||
<script src="{{ asset('js/app.js') }}" defer></script>
|
||
</head>
|
||
<body{% if p %} hx-headers='{"X-CSRF-Token": "{{ p.csrf }}"}'{% endif %}>
|
||
{% if p %}
|
||
<div id="sidebar-backdrop" class="sidebar-backdrop" data-action="sidebar-close"></div>
|
||
|
||
<div class="with-sidebar aiw-layout">
|
||
<aside class="sidebar aiw-sidebar" id="sidebar">
|
||
<div class="sidebar-header">
|
||
<a href="/workspaces" class="sidebar-brand">Bifröst</a>
|
||
</div>
|
||
<nav class="sidebar-nav">
|
||
{% set path = request.url.path %}
|
||
<a href="/workspaces" {% if path == '/workspaces' %}class="active"{% endif %}>Workspaces</a>
|
||
<a href="/approvals" {% if path.startswith('/approvals') %}class="active"{% endif %}>Freigaben <span hx-get="/approvals/count" hx-trigger="load" hx-swap="outerHTML"></span></a>
|
||
{% if p.is_admin %}
|
||
<div class="sidebar-nav-section">Administration</div>
|
||
<a href="/admin/teams" {% if path.startswith('/admin/teams') %}class="active"{% endif %}>Teams</a>
|
||
<a href="/admin/users" {% if path.startswith('/admin/users') %}class="active"{% endif %}>Nutzer</a>
|
||
<a href="/admin/models" {% if path.startswith('/admin/models') %}class="active"{% endif %}>Modelle und Zugänge</a>
|
||
<a href="/admin/tools" {% if path.startswith('/admin/tools') %}class="active"{% endif %}>Tools</a>
|
||
<a href="/admin/mcp" {% if path.startswith('/admin/mcp') %}class="active"{% endif %}>MCP-Server</a>
|
||
<a href="/admin/databases" {% if path.startswith('/admin/databases') %}class="active"{% endif %}>Datenbank-Treiber</a>
|
||
<a href="/admin/hugin" {% if path.startswith('/admin/hugin') %}class="active"{% endif %}>Hugin</a>
|
||
<a href="/admin/usage" {% if path.startswith('/admin/usage') %}class="active"{% endif %}>Nutzung und Kosten</a>
|
||
<a href="/admin/runs" {% if path.startswith('/admin/runs') %}class="active"{% endif %}>Runs</a>
|
||
<a href="/admin/audit" {% if path.startswith('/admin/audit') %}class="active"{% endif %}>Audit-Log</a>
|
||
<a href="/admin/notifications" {% if path.startswith('/admin/notifications') %}class="active"{% endif %}>Benachrichtigungen</a>
|
||
{% endif %}
|
||
{% if ws_nav is defined %}
|
||
{% set wbase = '/workspaces/' ~ current_workspace.id %}
|
||
<div class="sidebar-nav-section">{{ current_workspace.name }}</div>
|
||
<a href="{{ wbase }}" {% if path == wbase %}class="active"{% endif %}>Übersicht</a>
|
||
{% if ws_nav.repo %}<a href="{{ wbase }}/repo" {% if path.startswith(wbase ~ '/repo') %}class="active"{% endif %}>Repository</a>{% endif %}
|
||
<a href="{{ wbase }}/data" {% if path.startswith(wbase ~ '/data') or path.startswith(wbase ~ '/sources') or path.startswith('/sources/') %}class="active"{% endif %}>Daten</a>
|
||
{% if ws_nav.memory %}<a href="{{ wbase }}/memory" {% if path.startswith(wbase ~ '/memory') %}class="active"{% endif %}>Gedächtnis</a>{% endif %}
|
||
{% if ws_nav.is_admin %}
|
||
<a href="{{ wbase }}/settings" {% if path.startswith(wbase ~ '/settings') %}class="active"{% endif %}>Einstellungen</a>
|
||
<a href="{{ wbase }}/usage" {% if path.startswith(wbase ~ '/usage') %}class="active"{% endif %}>Nutzung und Kosten</a>
|
||
<a href="{{ wbase }}/runs" {% if path.startswith(wbase ~ '/runs') %}class="active"{% endif %}>Runs</a>
|
||
<a href="{{ wbase }}/audit" {% if path.startswith(wbase ~ '/audit') %}class="active"{% endif %}>Audit-Log</a>
|
||
{% endif %}
|
||
{% if ws_nav.chats %}
|
||
<div class="sidebar-nav-section">Letzte Chats</div>
|
||
{% for chat_id, title in ws_nav.chats %}
|
||
<a href="/chats/{{ chat_id }}" class="aiw-nav-chat {% if path == '/chats/' ~ chat_id %}active{% endif %}" title="{{ title }}">{{ title }}</a>
|
||
{% endfor %}
|
||
{% endif %}
|
||
{% endif %}
|
||
</nav>
|
||
</aside>
|
||
|
||
<div class="min-w-0">
|
||
<header class="aiw-topbar">
|
||
<button class="btn btn-ghost btn-icon aiw-menu" data-action="sidebar-open" aria-label="Menü">☰</button>
|
||
<a href="/workspaces" class="aiw-topbar-brand" title="Bifröst – AI Workspace">Bifröst</a>
|
||
{% if trail %}
|
||
<nav class="breadcrumbs aiw-crumbs" aria-label="Pfad">
|
||
<ol>
|
||
{% for c in trail %}
|
||
<li>{% if loop.last %}<span aria-current="page"{% if c.workspace %} class="aiw-crumb-ws"{% endif %} title="{{ c.label }}">{{ c.label }}</span>{% elif c.href %}<a href="{{ c.href }}"{% if c.workspace %} class="aiw-crumb-ws"{% endif %} title="{{ c.label }}">{{ c.label }}</a>{% else %}<span class="aiw-crumb-plain">{{ c.label }}</span>{% endif %}</li>
|
||
{% endfor %}
|
||
</ol>
|
||
</nav>
|
||
{% endif %}
|
||
<nav class="aiw-topbar-end" aria-label="Konto">
|
||
<a class="btn btn-ghost btn-sm aiw-help-link" href="{{ help_link(request.url.path) }}" title="Hilfe zu dieser Seite: Anleitungen, Begriffe, häufige Fragen"{% if request.url.path.startswith('/hilfe') %} aria-current="page"{% endif %}>
|
||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><circle cx="12" cy="12" r="10"/><path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>
|
||
<span>Hilfe</span></a>
|
||
<a class="text-sm text-muted aiw-topbar-name" href="/profile" title="Profil und Passwort">{{ p.display_name }}</a>
|
||
<button class="btn btn-ghost btn-icon btn-sm" data-action="theme-toggle" aria-label="Hell/Dunkel umschalten">
|
||
<svg class="icon-theme-light" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/></svg>
|
||
<svg class="icon-theme-dark" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="5"/><line x1="12" y1="1" x2="12" y2="3"/><line x1="12" y1="21" x2="12" y2="23"/><line x1="1" y1="12" x2="3" y2="12"/><line x1="21" y1="12" x2="23" y2="12"/></svg>
|
||
</button>
|
||
<form method="post" action="/logout">
|
||
<input type="hidden" name="_csrf" value="{{ p.csrf }}">
|
||
<button class="btn btn-secondary btn-sm" type="submit">Abmelden</button>
|
||
</form>
|
||
</nav>
|
||
</header>
|
||
|
||
<main class="stack stack-lg aiw-main {% block main_class %}{% endblock %}">
|
||
{% block content %}{% endblock %}
|
||
</main>
|
||
</div>
|
||
</div>
|
||
{% else %}
|
||
<main class="container container-sm stack stack-lg aiw-single">
|
||
{% block single %}{% endblock %}
|
||
</main>
|
||
{% endif %}
|
||
<div class="toast-container" id="toast-container"></div>
|
||
</body>
|
||
</html>
|