bifroest/app/templates/base.html

115 lines
8.2 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
{% set collapsed = request.cookies.get('aiw_sidebar') == 'collapsed' %}
<html lang="de" data-theme="light"{% if collapsed %} data-sidebar="collapsed"{% endif %}>
<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>
<button class="btn btn-ghost btn-icon btn-sm aiw-sidebar-toggle" type="button" data-sidebar-toggle aria-controls="sidebar"
aria-expanded="{{ 'false' if collapsed else 'true' }}" aria-label="Seitenleiste ein- oder ausblenden"
title="Seitenleiste ein-/ausblenden (Strg+B)">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><rect x="3" y="4" width="18" height="16" rx="2"/><line x1="9" y1="4" x2="9" y2="20"/></svg>
</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>