fox/resources/views/livewire/fox-hud.blade.php

507 lines
28 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<div x-data="foxHud(@js($cities), @js($homeCity))" x-init="init()" class="fox-hud-root">
<div class="grid-layer"></div>
<div class="scanlines"></div>
<div id="map-layer" class="map-wrap" wire:ignore
x-bind:style="mapVisible ? 'opacity:1;pointer-events:all;display:block' : 'opacity:0;pointer-events:none'"><div id="map" wire:ignore></div></div>
{{-- Fox Brain Layer --}}
<div id="brain-hover-label" class="brain-hover-label"></div>
<div id="brain-layer" class="brain-layer" wire:ignore.all>
<canvas id="fox-brain-canvas" style="position:absolute;inset:0;width:100%;height:100%;display:block;"></canvas>
<div class="brain-info" id="brain-info" style="display:none">
<div class="bi-title">F·O·X BRAIN</div>
<div class="bi-stat"><span class="bi-label">NODES</span><span class="bi-value" id="bi-nodes"></span></div>
<div class="bi-stat"><span class="bi-label">EDGES</span><span class="bi-value" id="bi-edges"></span></div>
<div class="bi-stat"><span class="bi-label">PENDING</span><span class="bi-value" style="color:#fbbf24" id="bi-pending"></span></div>
<div class="bi-legend">
<div class="bi-legend-item"><span style="background:#818cf8"></span>User</div>
<div class="bi-legend-item"><span style="background:#22d3ee"></span>Device</div>
<div class="bi-legend-item"><span style="background:#34d399"></span>Place</div>
<div class="bi-legend-item"><span style="background:#f59e0b"></span>Project</div>
<div class="bi-legend-item"><span style="background:#a78bfa"></span>Skill</div>
<div class="bi-legend-item"><span style="background:#ffffff"></span>Fact</div>
<div class="bi-legend-item"><span style="background:#fb7185"></span>Preference</div>
</div>
<button class="bi-close" onclick="window._foxHudCloseBrain && window._foxHudCloseBrain()"> CLOSE</button>
</div>
<div class="brain-popup" id="brain-popup" style="display:none">
<div class="bp-type" id="bp-type"></div>
<div class="bp-title" id="bp-title"></div>
<div class="bp-content" id="bp-content"></div>
<div class="bp-meta" id="bp-meta"></div>
<button class="bp-close" onclick="document.getElementById('brain-popup').style.display='none'"></button>
</div>
<div class="brain-scan"></div>
</div>
<div class="grid-overlay" wire:ignore></div>
<div id="zoom-rings" class="zoom-rings" wire:ignore></div>
<header class="topbar">
<div class="topbar-left">
<div class="fox-wordmark">F &middot; O &middot; X</div>
<span class="topbar-status"><span class="dot dot-green"></span> ONLINE</span>
</div>
<div class="topbar-center"><span class="clock-time" x-text="time">--:--:--</span></div>
<div class="topbar-right">
<span class="tb-stat" x-text="`SRV CPU ${stats.cpu}%`">SRV CPU </span>
<span class="tb-sep">·</span>
<span class="tb-stat" x-text="`SRV RAM ${stats.ram_gb}GB`">SRV RAM </span>
<span class="tb-sep">·</span>
<span class="tb-stat tb-stat-llm" x-text="`LLM ${model} @ MAC`">LLM @ MAC</span>
</div>
</header>
<div class="eg eg-t" :class="(edgeGlow || isSpeaking) && 'on'"></div>
<div class="eg eg-b" :class="(edgeGlow || isSpeaking) && 'on'"></div>
<div class="eg eg-l" :class="(edgeGlow || isSpeaking) && 'on'"></div>
<div class="eg eg-r" :class="(edgeGlow || isSpeaking) && 'on'"></div>
<div id="fox-center" class="fox-center" wire:ignore.self
:class="{'is-thinking': $wire.status === 'thinking', 'hide': mapVisible}"
x-on:click="mapVisible && closeMap()">
<div class="fox-jarvis"
:class="{ 'is-speaking': isSpeaking }">
<svg class="fox-arcs" viewBox="0 0 440 440" aria-hidden="true">
<defs>
<radialGradient id="fox-halo-grad" cx="50%" cy="50%" r="50%">
<stop offset="55%" stop-color="rgba(34,211,238,0)"/>
<stop offset="80%" stop-color="rgba(34,211,238,0.10)"/>
<stop offset="100%" stop-color="rgba(34,211,238,0)"/>
</radialGradient>
</defs>
{{-- Halo (radial gradient, atmet) --}}
<circle class="fox-halo" cx="220" cy="220" r="180" fill="url(#fox-halo-grad)"/>
{{-- Subtiler innerer Ring (gleich um den Kern) --}}
<circle cx="220" cy="220" r="100" fill="none"
stroke="rgba(34,211,238,0.09)" stroke-width="1"/>
{{-- Pulsierender Außenring-Wrapper (Tick-Ring + Achsen pulsieren zusammen) --}}
<g class="fox-outer-ring">
{{-- Tick-Ring: viele kleine Punkte gleichmäßig verteilt --}}
<circle cx="220" cy="220" r="158" fill="none"
stroke="rgba(34,211,238,0.28)" stroke-width="2.2"
stroke-dasharray="1 9" stroke-linecap="round"/>
{{-- Längere Markierungen alle 90° als Hauptachsen --}}
<circle cx="220" cy="220" r="158" fill="none"
stroke="rgba(255,255,255,0.42)" stroke-width="1.8"
stroke-dasharray="6 258" stroke-linecap="round"/>
</g>
{{-- Animierter Glow-Bogen (oben, äußere Bahn) --}}
<circle class="fox-arc fox-arc-top" cx="220" cy="220" r="146"
fill="none" stroke="rgba(34,211,238,0.50)" stroke-width="4"
stroke-linecap="round"
stroke-dasharray="290 800" pathLength="1000"
style="filter: drop-shadow(0 0 5px rgba(34,211,238,0.30));"/>
{{-- Mittlerer Bogen (dritter Balken, mittlere Bahn) --}}
<circle class="fox-arc fox-arc-mid" cx="220" cy="220" r="135"
fill="none" stroke="rgba(6,182,212,0.44)" stroke-width="3"
stroke-linecap="round"
stroke-dasharray="185 815" stroke-dashoffset="300" pathLength="1000"
style="filter: drop-shadow(0 0 4px rgba(6,182,212,0.26));"/>
{{-- Animierter Bogen (gegenläufig, innere Bahn) --}}
<circle class="fox-arc fox-arc-bot" cx="220" cy="220" r="124"
fill="none" stroke="rgba(103,232,249,0.38)" stroke-width="3.3"
stroke-linecap="round"
stroke-dasharray="115 885" stroke-dashoffset="500" pathLength="1000"
style="filter: drop-shadow(0 0 4px rgba(103,232,249,0.26));"/>
{{-- Vier Cardinal-Marker (subtile kurze Striche) --}}
<g stroke="rgba(34,211,238,0.18)" stroke-width="1" stroke-linecap="round">
<line x1="220" y1="38" x2="220" y2="50"/>
<line x1="220" y1="402" x2="220" y2="390"/>
<line x1="38" y1="220" x2="50" y2="220"/>
<line x1="402" y1="220" x2="390" y2="220"/>
</g>
</svg>
<div class="fox-jarvis-core">
<div class="fox-jarvis-name">F · O · X</div>
<div class="fox-jarvis-state" x-text="stateLabel().toUpperCase()">BEREIT</div>
</div>
</div>
</div>
<button type="button" class="fox-mini" x-on:click="closeMap()" wire:ignore.self
:class="{ 'show': mapVisible }">
<div class="mini-rings">
<div class="mini-ring mr1"></div>
<div class="mini-ring mr2"></div>
<div class="mini-ring mr3"></div>
<div class="mini-core"><div class="mini-name">FOX</div></div>
</div>
</button>
<form class="inp-wrap" wire:submit.prevent="send"
x-on:submit="if (tryConsumeConfirmation(($wire.input || '').trim())) { $wire.set('input', ''); $event.stopImmediatePropagation(); $event.preventDefault(); }">
<div class="mic" x-on:click="toggleMic()" :class="micActive && 'active'">🎤</div>
<textarea class="cinput" wire:model.live="input"
x-on:keydown="onKey($event)" x-on:input="resz($event.target)"
placeholder="'Zeig Wien' · 'Wo essen?' · 'Steakhaus'"
rows="1" autocomplete="off" spellcheck="false"></textarea>
<button type="submit" class="sbtn" wire:loading.attr="disabled" wire:target="send">
<span wire:loading.remove wire:target="send"></span>
<span wire:loading wire:target="send"></span>
</button>
</form>
<aside class="poi-panel" wire:ignore>
<div class="poi-head">
<span class="poi-title" x-text="poiLabel">Treffer</span>
<span class="poi-count" x-text="poiList.length + ' Orte'"></span>
<button type="button" class="poi-close" x-on:click="clearPois()">×</button>
</div>
<div class="poi-items">
<template x-if="poiLoading"><div>
<template x-for="i in 6"><div class="poi-skeleton" :style="`animation-delay: ${i * 80}ms`"></div></template>
</div></template>
<template x-for="(p, i) in poiList" :key="p.id">
<div class="poi-item" x-on:click="flyToPoi(p)" :style="`--idx: ${i}`">
<div class="poi-name" x-text="p.name"></div>
<div class="poi-meta" x-text="p.address"></div>
</div>
</template>
</div>
</aside>
<aside class="route-panel" x-show="routeStepsVisible" x-cloak wire:ignore>
<div class="route-panel-head">
<span class="route-panel-title" x-text="'→ ' + routeStepsTarget">Route</span>
<span class="route-panel-count" x-text="routeSteps.length + ' Schritte'"></span>
<button type="button" class="poi-close" x-on:click="removeRouteLayer()">×</button>
</div>
<div class="route-panel-items">
<template x-for="(step, i) in routeSteps" :key="i">
<div class="route-step" :style="`--idx: ${i}`">
<template x-if="step.mode === 'TRANSIT' && step.transit">
<div>
<div class="route-step-line">
<span class="route-line-badge"
:style="`background: ${step.transit.color}; color: ${step.transit.text_color}`"
x-text="step.transit.line">U1</span>
<span class="route-step-headsign" x-text="step.transit.headsign ? '→ ' + step.transit.headsign : ''"></span>
</div>
<div class="route-step-stops">
<div class="route-step-stop"><span class="dot dot-green"></span><span x-text="step.transit.departure_stop"></span><span class="route-step-time" x-text="step.transit.departure_time"></span></div>
<div class="route-step-meta" x-text="step.transit.num_stops + ' Stationen · ' + step.duration"></div>
<div class="route-step-stop"><span class="dot dot-red"></span><span x-text="step.transit.arrival_stop"></span><span class="route-step-time" x-text="step.transit.arrival_time"></span></div>
</div>
</div>
</template>
<template x-if="step.mode !== 'TRANSIT'">
<div>
<div class="route-step-line">
<span class="route-walk-icon" x-text="step.mode === 'WALKING' ? '🚶' : (step.mode === 'BICYCLING' ? '🚲' : '🚗')"></span>
<span class="route-step-instruction" x-text="step.instruction"></span>
</div>
<div class="route-step-meta" x-text="step.distance + ' · ' + step.duration"></div>
</div>
</template>
</div>
</template>
</div>
</aside>
<div class="city-choice" x-show="cityChoicesVisible" x-cloak wire:ignore>
<div class="city-choice-head">
<span class="city-choice-title" x-text="`„${cityChoiceQuery}“ — welches?`"></span>
<button type="button" class="poi-close" x-on:click="cancelCityChoice()">×</button>
</div>
<div class="city-choice-items">
<template x-for="(c, i) in cityChoices" :key="i">
<button type="button" class="city-choice-btn" x-on:click="flyToDynamicCity(c)">
<div class="city-choice-name" x-text="c.name"></div>
<div class="city-choice-meta" x-text="c.formatted"></div>
</button>
</template>
</div>
</div>
<div class="status-panel" x-show="statusVisible" x-cloak wire:ignore :class="statusScanning && 'scanning'">
<div class="status-head">
<span class="status-title">FOX · SYSTEM DIAGNOSTIC</span>
<span class="status-spinner" x-show="statusScanning" x-cloak>SCANNING</span>
<button type="button" class="status-close" x-on:click="closeStatus()">×</button>
</div>
<div class="status-body">
<template x-for="(line, i) in statusLines" :key="i">
<div class="status-line" :class="!line.ok && 'fail'" :style="`--idx: ${i}`">
<span class="status-mark" x-text="line.ok ? '◢' : '✕'"></span>
<span class="status-label" x-text="line.label"></span>
<span class="status-value" x-text="line.value"></span>
</div>
</template>
</div>
<div class="status-scanline" x-show="statusScanning" x-cloak></div>
</div>
<div class="place-info" x-show="placeInfoVisible" x-cloak wire:ignore :class="placeInfoExpanded && 'expanded'">
<button type="button" class="place-info-close" x-on:click="closePlaceInfo()">×</button>
<template x-if="placeInfoLoading">
<div class="place-info-loading">
<div class="place-info-skeleton-photo"></div>
<div class="place-info-skeleton-line" style="width:70%"></div>
<div class="place-info-skeleton-line" style="width:50%"></div>
</div>
</template>
<template x-if="!placeInfoLoading && placeInfo">
<div>
<template x-if="placeInfo.photo_url">
<div class="place-info-photo" :style="`background-image: url(${placeInfo.photo_url})`"></div>
</template>
<div class="place-info-body">
<h3 class="place-info-name" x-text="placeInfo.name">Name</h3>
<template x-if="placeInfo.address">
<div class="place-info-meta" x-text="placeInfo.address"></div>
</template>
<template x-if="placeInfo.rating">
<div class="place-info-rating">
<span class="place-info-stars" x-text="'★ ' + String(placeInfo.rating).replace('.', ',')"></span>
<span class="place-info-rating-count" x-text="'(' + (placeInfo.rating_count || 0) + ' Bewertungen)'"></span>
</div>
</template>
<template x-if="placeInfo.summary">
<p class="place-info-summary" x-text="placeInfo.summary"></p>
</template>
<template x-if="placeInfoExpanded && placeInfo.opening_hours && placeInfo.opening_hours.length">
<div class="place-info-section">
<h4>Öffnungszeiten</h4>
<ul>
<template x-for="line in placeInfo.opening_hours" :key="line">
<li x-text="line"></li>
</template>
</ul>
</div>
</template>
<template x-if="placeInfoExpanded && placeInfo.website">
<a class="place-info-link" :href="placeInfo.website" target="_blank" rel="noopener" x-text="placeInfo.website">Website</a>
</template>
<div class="place-info-actions">
<button type="button" x-on:click="!placeInfoExpanded ? expandPlaceInfo() : (placeInfoExpanded = false)"
x-text="placeInfoExpanded ? '↑ Weniger' : '↓ Mehr'"> Mehr</button>
<button type="button" x-on:click="if (placeInfo?.lat) { flyToPoi({name: placeInfo.name, lat: placeInfo.lat, lng: placeInfo.lng, address: placeInfo.address}); }">Auf Karte</button>
<button type="button" x-on:click="requestRoute(_userLocation ? `${_userLocation.lat},${_userLocation.lng}` : `${currentLatLng.lat},${currentLatLng.lng}`, `${placeInfo.lat},${placeInfo.lng}`, 'driving')">Route</button>
</div>
</div>
</div>
</template>
</div>
{{-- PLACE CARDS Karten erscheinen direkt im Raum, kein Tisch/Modal --}}
<div class="cards-scene" x-show="placeDesk.visible" x-cloak wire:ignore>
{{-- Top bar --}}
<div class="cards-infobar">
<span class="cards-place-name" x-text="placeDesk.data?.name ?? '—'"></span>
<div class="cards-actions">
<button type="button" class="cards-btn-map"
x-show="placeDesk.data?.lat"
x-on:click="flyToPoi({name: placeDesk.data?.name, lat: placeDesk.data?.lat, lng: placeDesk.data?.lng, address: placeDesk.data?.address})">
KARTE
</button>
<button type="button" class="cards-close-btn" x-on:click="closePlaceDesk()">×</button>
</div>
</div>
{{-- Scattered file cards --}}
<div class="cards-field" :class="placeDesk.liftedCard && 'has-open'">
<template x-if="placeDesk.data?.photo_url">
<div class="pcard" style="--rot:-6deg;--l:5%;--t:18%;--i:0" x-on:click="toggleDeskCard('photo')">
<div class="pcard-back2"></div><div class="pcard-back1"></div>
<div class="pcard-main">
<div class="pcard-icon"></div>
<div class="pcard-name">FOTOS</div>
<div class="pcard-lines"><div class="pl"></div><div class="pl"></div><div class="pl"></div><div class="pl"></div></div>
</div>
</div>
</template>
<template x-if="placeDesk.data?.summary">
<div class="pcard" style="--rot:4deg;--l:22%;--t:10%;--i:1" x-on:click="toggleDeskCard('summary')">
<div class="pcard-back2"></div><div class="pcard-back1"></div>
<div class="pcard-main">
<div class="pcard-icon"></div>
<div class="pcard-name">BESCHREIBUNG</div>
<div class="pcard-lines"><div class="pl"></div><div class="pl"></div><div class="pl"></div><div class="pl"></div></div>
</div>
</div>
</template>
<div class="pcard" style="--rot:-2deg;--l:44%;--t:14%;--i:2" x-on:click="toggleDeskCard('hours')">
<div class="pcard-back2"></div><div class="pcard-back1"></div>
<div class="pcard-main">
<div class="pcard-icon"></div>
<div class="pcard-name">ÖFFNUNGSZEITEN</div>
<div class="pcard-lines"><div class="pl"></div><div class="pl"></div><div class="pl"></div><div class="pl"></div></div>
</div>
</div>
<template x-if="placeDesk.data?.phone">
<div class="pcard" style="--rot:5deg;--l:65%;--t:10%;--i:3" x-on:click="toggleDeskCard('phone')">
<div class="pcard-back2"></div><div class="pcard-back1"></div>
<div class="pcard-main">
<div class="pcard-icon"></div>
<div class="pcard-name">TELEFON</div>
<div class="pcard-lines"><div class="pl"></div><div class="pl"></div><div class="pl"></div><div class="pl"></div></div>
</div>
</div>
</template>
<template x-if="placeDesk.data?.rating">
<div class="pcard" style="--rot:-4deg;--l:80%;--t:18%;--i:4" x-on:click="toggleDeskCard('rating')">
<div class="pcard-back2"></div><div class="pcard-back1"></div>
<div class="pcard-main">
<div class="pcard-icon"></div>
<div class="pcard-name">BEWERTUNG</div>
<div class="pcard-lines"><div class="pl"></div><div class="pl"></div><div class="pl"></div><div class="pl"></div></div>
</div>
</div>
</template>
<div class="pcard" style="--rot:3deg;--l:10%;--t:58%;--i:5" x-on:click="toggleDeskCard('address')">
<div class="pcard-back2"></div><div class="pcard-back1"></div>
<div class="pcard-main">
<div class="pcard-icon"></div>
<div class="pcard-name">ADRESSE</div>
<div class="pcard-lines"><div class="pl"></div><div class="pl"></div><div class="pl"></div><div class="pl"></div></div>
</div>
</div>
<template x-if="placeDesk.data?.website">
<div class="pcard" style="--rot:-3deg;--l:75%;--t:55%;--i:6" x-on:click="toggleDeskCard('website')">
<div class="pcard-back2"></div><div class="pcard-back1"></div>
<div class="pcard-main">
<div class="pcard-icon"></div>
<div class="pcard-name">WEBSITE</div>
<div class="pcard-lines"><div class="pl"></div><div class="pl"></div><div class="pl"></div><div class="pl"></div></div>
</div>
</div>
</template>
</div>
{{-- Opened card: full readable view --}}
<div class="cards-open" x-show="placeDesk.liftedCard" x-cloak>
<button type="button" class="cards-back-btn" x-on:click="putDeskCardDown()"> ZURÜCK</button>
<div x-show="placeDesk.liftedCard === 'photo'" class="cards-photos">
<template x-for="(url, i) in (placeDesk.data?.photo_urls?.length ? placeDesk.data.photo_urls : [placeDesk.data?.photo_url]).filter(Boolean)" :key="i">
<img :src="url" class="cards-photo" loading="lazy"/>
</template>
</div>
<div x-show="placeDesk.liftedCard === 'summary'">
<div class="cards-label">BESCHREIBUNG</div>
<p class="cards-body" x-text="placeDesk.data?.summary || 'Keine Beschreibung verfügbar.'"></p>
</div>
<div x-show="placeDesk.liftedCard === 'hours'">
<div class="cards-label">ÖFFNUNGSZEITEN</div>
<template x-if="placeDesk.data?.opening_hours?.length">
<ul class="cards-hours-list">
<template x-for="(line, i) in (placeDesk.data?.opening_hours ?? [])" :key="i">
<li x-text="line"></li>
</template>
</ul>
</template>
<p x-show="!placeDesk.data?.opening_hours?.length" class="cards-body">Keine Öffnungszeiten verfügbar.</p>
</div>
<div x-show="placeDesk.liftedCard === 'phone'">
<div class="cards-label">TELEFON</div>
<div class="cards-phone-number" x-text="placeDesk.data?.phone ?? '—'"></div>
</div>
<div x-show="placeDesk.liftedCard === 'rating'">
<div class="cards-label">BEWERTUNG</div>
<div class="cards-rating-big" x-text="'★ ' + String(placeDesk.data?.rating ?? '—').replace('.', ',')"></div>
<div class="cards-rating-count" x-text="'(' + (placeDesk.data?.rating_count || 0) + ' Bewertungen)'"></div>
</div>
<div x-show="placeDesk.liftedCard === 'address'">
<div class="cards-label">ADRESSE</div>
<div class="cards-address" x-text="placeDesk.data?.address ?? '—'"></div>
</div>
<div x-show="placeDesk.liftedCard === 'website'">
<div class="cards-label">WEBSITE</div>
<a class="cards-website-link" :href="placeDesk.data?.website" target="_blank" rel="noopener"
x-text="placeDesk.data?.website"></a>
</div>
</div>
</div>
<div class="route-info" x-show="routeInfoVisible" x-cloak wire:ignore>
<span class="route-info-icon"></span>
<span x-text="routeInfoLabel"></span>
<button type="button" class="route-info-close" x-on:click="removeRouteLayer()">×</button>
</div>
<div class="layer-panel" wire:ignore.self>
<button type="button" class="lbtn" :class="transitOn && 'on'" x-on:click="toggleTransit()">
<span class="ld" style="background:#10b981"></span>TRANSIT
</button>
</div>
<footer class="bottombar">
<div class="bb-cell">
<span class="dot" :class="ollamaOk === null ? 'dot-amber' : (ollamaOk ? 'dot-green' : (openaiOk ? 'dot-cyan' : 'dot-red'))"></span>
LLM
<span class="bb-state" x-text="ollamaOk === null ? 'PRÜFE …' : (ollamaOk ? 'OLLAMA AKTIV' : (openaiOk ? 'CLOUD-FALLBACK' : 'OFFLINE'))"></span>
</div>
<div class="bb-cell"><span class="dot dot-cyan"></span>REVERB <span class="bb-state" x-text="reverbOk ? 'VERBUNDEN' : 'OFFLINE'"></span></div>
<div class="bb-cell"><span class="dot dot-indigo"></span>SEARXNG <span class="bb-state">BEREIT</span></div>
<div class="bb-sep">|</div>
<div class="bb-cell" x-text="dateLabel"></div>
<div class="bb-sep">|</div>
<div class="bb-cell" x-text="weatherLabel"></div>
</footer>
{{-- Code Terminal Panel --}}
<div class="code-panel"
wire:ignore
x-show="codePanel.visible"
x-transition:enter="transition ease-out duration-300"
x-transition:enter-start="opacity-0 translate-y-4"
x-transition:enter-end="opacity-100 translate-y-0"
x-cloak>
<div class="code-panel-header">
<div class="code-panel-dots">
<span class="dot-red"></span>
<span class="dot-yellow"></span>
<span class="dot-green"></span>
</div>
<div class="code-panel-title" x-text="codePanel.title">fox@terminal</div>
<div class="code-panel-actions">
<button class="cp-btn" x-on:click="copyCode()" x-text="codeCopied ? '✓ Kopiert' : 'Kopieren'">Kopieren</button>
<button class="cp-btn cp-btn-close" x-on:click="closeCodePanel()"></button>
</div>
</div>
<div class="code-panel-desc" x-show="codePanel.description" x-text="codePanel.description"></div>
<div class="code-typing" x-show="codePanel.typing">
<span class="typing-cursor"></span>
<span class="typing-text" x-text="codePanel.typingText">Fox schreibt...</span>
</div>
<div class="code-panel-body" x-show="!codePanel.typing">
<pre class="code-pre"><code x-text="codePanel.code"></code></pre>
</div>
<template x-if="codePanel.files && codePanel.files.length > 1">
<div class="code-panel-files">
<template x-for="(file, i) in codePanel.files" :key="i">
<button class="cp-file-tab"
x-on:click="selectFile(i)"
:class="codePanel.activeFile === i ? 'active' : ''"
x-text="file.name">
</button>
</template>
</div>
</template>
</div>
<div class="notifications" x-ref="notif"></div>
<div class="cur" x-ref="cur"></div>
<div class="cur-dot" x-ref="curDot"></div>
</div>