505 lines
28 KiB
PHP
Executable File
505 lines
28 KiB
PHP
Executable File
<div x-data="foxHud(<?php echo \Illuminate\Support\Js::from($cities)->toHtml() ?>, <?php echo \Illuminate\Support\Js::from($homeCity)->toHtml() ?>)" 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><div id="map"></div></div>
|
||
|
||
|
||
<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 · O · 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'}"
|
||
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>
|
||
|
||
|
||
<circle class="fox-halo" cx="220" cy="220" r="180" fill="url(#fox-halo-grad)"/>
|
||
|
||
|
||
<circle cx="220" cy="220" r="100" fill="none"
|
||
stroke="rgba(34,211,238,0.09)" stroke-width="1"/>
|
||
|
||
|
||
<g class="fox-outer-ring">
|
||
|
||
<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"/>
|
||
|
||
|
||
<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>
|
||
|
||
|
||
<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));"/>
|
||
|
||
|
||
<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));"/>
|
||
|
||
|
||
<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));"/>
|
||
|
||
|
||
<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>
|
||
<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>
|
||
|
||
|
||
<div class="cards-scene" x-show="placeDesk.visible" x-cloak wire:ignore>
|
||
|
||
|
||
<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>
|
||
|
||
|
||
<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>
|
||
|
||
|
||
<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>
|
||
|
||
|
||
<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>
|
||
|
||
<?php /**PATH /var/www/resources/views/livewire/fox-hud.blade.php ENDPATH**/ ?>
|