2026-01-24 22:59:20 +00:00
|
|
|
<template>
|
2026-02-17 19:19:54 +00:00
|
|
|
<div class="min-h-screen flex relative dashboard-view" :class="{ 'glass-throw-active': showZoomIn }">
|
2026-03-11 13:11:45 +00:00
|
|
|
<!-- Skip to main content link for keyboard users -->
|
2026-03-11 13:45:59 +00:00
|
|
|
<a href="#main-content" class="skip-to-content">{{ t('common.skipToContent') }}</a>
|
2026-02-17 19:19:54 +00:00
|
|
|
<!-- Background container with 3D perspective - full width to avoid letterboxing -->
|
2026-01-24 22:59:20 +00:00
|
|
|
<div class="bg-perspective-container">
|
2026-03-01 18:07:35 +00:00
|
|
|
<!-- Background - primary layer (visible for all routes, transitions out only for detail pages) -->
|
2026-02-17 19:19:54 +00:00
|
|
|
<div
|
2026-01-24 22:59:20 +00:00
|
|
|
ref="bgDefault"
|
2026-02-17 19:19:54 +00:00
|
|
|
class="bg-layer bg-fullwidth"
|
|
|
|
|
:class="[
|
2026-03-01 18:07:35 +00:00
|
|
|
{ 'bg-transitioning-out': showAltBackground },
|
2026-02-17 19:19:54 +00:00
|
|
|
{ 'zoom-reveal-bg': showZoomIn }
|
|
|
|
|
]"
|
2026-03-01 18:07:35 +00:00
|
|
|
:style="{ backgroundImage: `url(/assets/img/${backgroundImage})` }"
|
2026-02-17 19:19:54 +00:00
|
|
|
/>
|
2026-03-01 18:07:35 +00:00
|
|
|
<!-- Background - detail layer (only visible during app/marketplace detail 3D transition) -->
|
2026-01-24 22:59:20 +00:00
|
|
|
<div
|
|
|
|
|
ref="bgAlt"
|
2026-02-17 19:19:54 +00:00
|
|
|
class="bg-layer bg-fullwidth"
|
2026-03-01 18:07:35 +00:00
|
|
|
:class="{ 'bg-transitioning-in': showAltBackground }"
|
|
|
|
|
style="background-image: url(/assets/img/bg-intro-3.jpg)"
|
2026-01-24 22:59:20 +00:00
|
|
|
/>
|
|
|
|
|
<!-- Glitch overlays - trigger on background change -->
|
2026-03-22 03:30:21 +00:00
|
|
|
<div
|
2026-01-24 22:59:20 +00:00
|
|
|
class="bg-glitch-layer-1"
|
|
|
|
|
:class="{ 'glitch-active': isGlitching }"
|
2026-03-01 18:07:35 +00:00
|
|
|
:style="{ backgroundImage: `url(/assets/img/${backgroundImage})` }"
|
2026-01-24 22:59:20 +00:00
|
|
|
/>
|
2026-03-22 03:30:21 +00:00
|
|
|
<div
|
2026-01-24 22:59:20 +00:00
|
|
|
class="bg-glitch-layer-2"
|
|
|
|
|
:class="{ 'glitch-active': isGlitching }"
|
2026-03-01 18:07:35 +00:00
|
|
|
:style="{ backgroundImage: `url(/assets/img/${backgroundImage})` }"
|
2026-01-24 22:59:20 +00:00
|
|
|
/>
|
2026-03-22 03:30:21 +00:00
|
|
|
<div
|
2026-01-24 22:59:20 +00:00
|
|
|
class="bg-glitch-scan"
|
|
|
|
|
:class="{ 'glitch-active': isGlitching }"
|
|
|
|
|
/>
|
feat: AIUI chat mode integration with iframe, context broker, overnight loop
- Chat mode: AIUI loads in sandboxed iframe at /dashboard/chat with transparent bg
- Mode switcher: Easy + Pro tabs only, Chat is a launcher button
- Keyboard shortcuts: Cmd+1 (Easy), Cmd+2 (Pro), Cmd+3 (Chat), Cmd+M (cycle)
- Directional transitions: chat slides from/to left, dashboard from/to right
- Context broker: postMessage protocol for quarantined AIUI communication
- AI permissions store: user-controlled toggles for data access categories
- Settings UI: AI Data Access section with per-category toggles
- AIUI container manifest and nginx proxy config for /aiui/
- Deploy script builds AIUI with /aiui/ base path
- Overnight loop infrastructure (loop.sh, prepare.sh, plan.md, prompt.md)
- Security hooks for autonomous overnight runs
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 12:06:20 +00:00
|
|
|
<!-- Glitch overlays removed — only intro glitch plays (via isGlitching) -->
|
2026-01-24 22:59:20 +00:00
|
|
|
</div>
|
|
|
|
|
|
2026-02-17 19:19:54 +00:00
|
|
|
<!-- Oomph accent - brief impact flash when dashboard loads -->
|
|
|
|
|
<div
|
|
|
|
|
v-if="showZoomIn"
|
|
|
|
|
class="fixed inset-0 pointer-events-none z-[100] oomph-flash"
|
|
|
|
|
aria-hidden="true"
|
|
|
|
|
/>
|
|
|
|
|
<!-- Reveal flashes and glitch overlay - enthralling entrance -->
|
|
|
|
|
<div
|
|
|
|
|
v-if="showZoomIn"
|
|
|
|
|
class="fixed inset-0 pointer-events-none z-[99] reveal-flash-glitch"
|
|
|
|
|
aria-hidden="true"
|
|
|
|
|
/>
|
|
|
|
|
|
2026-03-14 17:12:41 +00:00
|
|
|
<!-- Background overlay - uniform 0.2 opacity -->
|
|
|
|
|
<div
|
|
|
|
|
class="fixed inset-0 pointer-events-none bg-black/20"
|
2026-01-24 22:59:20 +00:00
|
|
|
style="z-index: -5;"
|
|
|
|
|
/>
|
|
|
|
|
|
2026-03-22 03:30:21 +00:00
|
|
|
<!-- Sidebar - Desktop Only -->
|
|
|
|
|
<DashboardSidebar :show-zoom-in="showZoomIn" @logout="handleLogout" />
|
2026-01-24 22:59:20 +00:00
|
|
|
|
2026-02-17 19:19:54 +00:00
|
|
|
<!-- Main Content (Xbox: Right goes here from sidebar) -->
|
|
|
|
|
<main
|
2026-03-11 13:11:45 +00:00
|
|
|
id="main-content"
|
2026-02-17 19:19:54 +00:00
|
|
|
data-controller-zone="main"
|
2026-03-14 17:12:41 +00:00
|
|
|
class="flex-1 overflow-hidden relative pb-0 glass-piece z-10"
|
2026-02-17 19:19:54 +00:00
|
|
|
:class="{ 'glass-throw-main': showZoomIn }"
|
|
|
|
|
>
|
2026-02-18 11:29:05 +00:00
|
|
|
<div data-controller-main-entry class="absolute top-4 right-4 md:top-6 md:right-8 z-20">
|
2026-03-04 05:23:42 +00:00
|
|
|
<!-- Controller zone entry point - no switcher -->
|
2026-02-18 10:26:33 +00:00
|
|
|
</div>
|
|
|
|
|
|
2026-03-22 03:30:21 +00:00
|
|
|
<!-- Connection Status Banners -->
|
|
|
|
|
<ConnectionBanner />
|
2026-01-24 22:59:20 +00:00
|
|
|
|
2026-03-05 10:14:10 +00:00
|
|
|
<div class="perspective-container-wrapper glass-piece" :class="{ 'glass-throw-content': showZoomIn && !isHomeRoute }">
|
2026-01-24 22:59:20 +00:00
|
|
|
<div class="perspective-container">
|
|
|
|
|
<RouterView v-slot="{ Component, route }">
|
|
|
|
|
<Transition :name="getTransitionName(route)">
|
|
|
|
|
<div :key="route.path" class="view-wrapper">
|
|
|
|
|
<div
|
feat: v1.2.0-alpha — E2E encrypted mesh relay, steganography, relay status polling
Phase 5 mesh networking:
- E2E encrypted TX relay (X25519 + ChaCha20-Poly1305) — non-Archy nodes
relay encrypted blobs transparently via Meshcore native routing
- Steganographic encoding modes (WeatherStation, SensorNetwork) — traffic
looks like sensor data on the wire, 0xAA marker, configurable per-node
- Pre-flight Bitcoin Core health check on relay node — specific error codes
(bitcoin_unreachable, bitcoin_syncing, tx_rejected) instead of generic fails
- mesh.relay-status RPC endpoint — frontend polls for relay result every 3s
- On-Chain / Lightning tabs in Off-Grid Bitcoin panel
- Archy Peers vs Mesh Broadcast relay mode selector
- Mesh view fills viewport (no page scroll), internal panel scrolling
- Version bump to 1.2.0-alpha
Also includes: deploy hardening, container fixes, IndeedHub updates,
boot screen, dashboard improvements, MASTER_PLAN task tracking
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 23:56:37 +00:00
|
|
|
v-if="route.path === '/dashboard/chat' || route.path === '/dashboard/mesh'"
|
security+feat: v1.3.0 — pentest remediation, container reliability, UI overhaul
Security (33 pentest findings addressed):
- CRITICAL: backend binds 127.0.0.1, path traversal in tor.rs/dwn fixed
- HIGH: federation requires signatures, XSS login redirect, RBAC viewer restricted
- HIGH: tar slip prevention, S3 SSRF validation, backup ID validation
- MEDIUM: remember-me random secret, TOTP session rotation, password re-auth
- LOW: CSP unsafe-inline removed, CORS dev-only, onion/webhook validation
Container reliability:
- Memory limits on all 37 containers (OOM prevention)
- Exited vs stopped state distinction with health-aware status badges
- Crash recovery coordination (no more restart cascade)
- User-stopped tracking survives reboots
- Tiered boot recovery (databases → core → services → apps)
UI:
- Wallet TransactionsModal, health-aware app status badges
- Restart button on containers, exited/crashed red state
- Mesh view overhaul, glass button updates, BaseModal/ToggleSwitch
- Apps sticky header removed, dev faucet, mutable mock wallet
Infrastructure:
- LND REST port 8080 exposed over Tor (LND Connect fix)
- Nginx cookie_session fix, deploy script Tor config updated
- Dev environment: podman auto-start, boot mode simulation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 12:44:31 +00:00
|
|
|
:class="['h-full', mobileTabPaddingTop ? 'overflow-y-auto' : '']"
|
|
|
|
|
:style="mobileTabPaddingTop ? { paddingTop: (mobileTabPaddingTop + 16) + 'px' } : undefined"
|
feat: AIUI chat mode integration with iframe, context broker, overnight loop
- Chat mode: AIUI loads in sandboxed iframe at /dashboard/chat with transparent bg
- Mode switcher: Easy + Pro tabs only, Chat is a launcher button
- Keyboard shortcuts: Cmd+1 (Easy), Cmd+2 (Pro), Cmd+3 (Chat), Cmd+M (cycle)
- Directional transitions: chat slides from/to left, dashboard from/to right
- Context broker: postMessage protocol for quarantined AIUI communication
- AI permissions store: user-controlled toggles for data access categories
- Settings UI: AI Data Access section with per-category toggles
- AIUI container manifest and nginx proxy config for /aiui/
- Deploy script builds AIUI with /aiui/ base path
- Overnight loop infrastructure (loop.sh, prepare.sh, plan.md, prompt.md)
- Security hooks for autonomous overnight runs
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 12:06:20 +00:00
|
|
|
>
|
|
|
|
|
<component :is="Component" />
|
|
|
|
|
</div>
|
|
|
|
|
<div
|
|
|
|
|
v-else
|
2026-01-24 22:59:20 +00:00
|
|
|
:class="[
|
2026-03-14 17:12:41 +00:00
|
|
|
'absolute inset-0 px-4 pt-4 md:pt-8 md:px-8 overflow-y-auto',
|
feat: AIUI chat mode integration with iframe, context broker, overnight loop
- Chat mode: AIUI loads in sandboxed iframe at /dashboard/chat with transparent bg
- Mode switcher: Easy + Pro tabs only, Chat is a launcher button
- Keyboard shortcuts: Cmd+1 (Easy), Cmd+2 (Pro), Cmd+3 (Chat), Cmd+M (cycle)
- Directional transitions: chat slides from/to left, dashboard from/to right
- Context broker: postMessage protocol for quarantined AIUI communication
- AI permissions store: user-controlled toggles for data access categories
- Settings UI: AI Data Access section with per-category toggles
- AIUI container manifest and nginx proxy config for /aiui/
- Deploy script builds AIUI with /aiui/ base path
- Overnight loop infrastructure (loop.sh, prepare.sh, plan.md, prompt.md)
- Security hooks for autonomous overnight runs
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 12:06:20 +00:00
|
|
|
needsMobileBackButtonSpace
|
2026-03-14 17:12:41 +00:00
|
|
|
? 'mobile-scroll-pad-back'
|
|
|
|
|
: 'mobile-scroll-pad'
|
2026-01-24 22:59:20 +00:00
|
|
|
]"
|
2026-03-05 10:14:10 +00:00
|
|
|
:style="mobileTabPaddingTop ? { paddingTop: (mobileTabPaddingTop + 16) + 'px' } : undefined"
|
2026-01-24 22:59:20 +00:00
|
|
|
>
|
2026-03-14 17:12:41 +00:00
|
|
|
<component :is="Component" class="view-container flex-none" />
|
|
|
|
|
<!-- Bottom spacer — scroll clearance on all pages -->
|
|
|
|
|
<div class="shrink-0 h-6 md:h-12" aria-hidden="true"></div>
|
2026-01-24 22:59:20 +00:00
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</Transition>
|
|
|
|
|
</RouterView>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2026-03-16 12:58:35 +00:00
|
|
|
|
|
|
|
|
<!-- Panel mode app session — renders alongside current page content -->
|
|
|
|
|
<Transition name="panel-slide">
|
|
|
|
|
<div v-if="appLauncher.panelAppId" class="app-panel-container">
|
|
|
|
|
<AppSession :app-id-prop="appLauncher.panelAppId" @close="appLauncher.closePanel()" />
|
|
|
|
|
</div>
|
|
|
|
|
</Transition>
|
2026-01-24 22:59:20 +00:00
|
|
|
</main>
|
|
|
|
|
|
2026-03-25 18:13:22 +00:00
|
|
|
<!-- Persistent Mobile Tabs + Bottom Tab Bar — outside <main> so position:fixed isn't broken by will-change:transform -->
|
|
|
|
|
<DashboardMobileNav ref="mobileNavRef" :show-zoom-in="showZoomIn" />
|
|
|
|
|
|
2026-03-11 13:04:31 +00:00
|
|
|
<!-- Health Notifications Toast -->
|
2026-03-22 03:30:21 +00:00
|
|
|
<HealthNotifications />
|
2026-01-24 22:59:20 +00:00
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
2026-03-22 03:30:21 +00:00
|
|
|
import { computed, ref, watch, onMounted, onBeforeUnmount } from 'vue'
|
|
|
|
|
import { RouterView, useRouter, useRoute } from 'vue-router'
|
2026-03-11 13:45:59 +00:00
|
|
|
import { useI18n } from 'vue-i18n'
|
2026-01-24 22:59:20 +00:00
|
|
|
import { useAppStore } from '../stores/app'
|
2026-03-16 12:58:35 +00:00
|
|
|
import { useAppLauncherStore } from '../stores/appLauncher'
|
|
|
|
|
import AppSession from '@/views/AppSession.vue'
|
2026-02-17 19:19:54 +00:00
|
|
|
import { useLoginTransitionStore } from '../stores/loginTransition'
|
|
|
|
|
import { playDashboardLoadOomph } from '@/composables/useLoginSounds'
|
2026-03-04 07:09:31 +00:00
|
|
|
|
2026-03-22 03:30:21 +00:00
|
|
|
import DashboardSidebar from '@/views/dashboard/DashboardSidebar.vue'
|
|
|
|
|
import DashboardMobileNav from '@/views/dashboard/DashboardMobileNav.vue'
|
|
|
|
|
import ConnectionBanner from '@/views/dashboard/ConnectionBanner.vue'
|
|
|
|
|
import HealthNotifications from '@/views/dashboard/HealthNotifications.vue'
|
|
|
|
|
import { useRouteTransitions, isDetailRoute, ROUTE_BACKGROUNDS } from '@/views/dashboard/useRouteTransitions'
|
|
|
|
|
import '@/views/dashboard/dashboard-styles.css'
|
feat: AIUI chat mode integration with iframe, context broker, overnight loop
- Chat mode: AIUI loads in sandboxed iframe at /dashboard/chat with transparent bg
- Mode switcher: Easy + Pro tabs only, Chat is a launcher button
- Keyboard shortcuts: Cmd+1 (Easy), Cmd+2 (Pro), Cmd+3 (Chat), Cmd+M (cycle)
- Directional transitions: chat slides from/to left, dashboard from/to right
- Context broker: postMessage protocol for quarantined AIUI communication
- AI permissions store: user-controlled toggles for data access categories
- Settings UI: AI Data Access section with per-category toggles
- AIUI container manifest and nginx proxy config for /aiui/
- Deploy script builds AIUI with /aiui/ base path
- Overnight loop infrastructure (loop.sh, prepare.sh, plan.md, prompt.md)
- Security hooks for autonomous overnight runs
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 12:06:20 +00:00
|
|
|
|
2026-03-22 03:30:21 +00:00
|
|
|
const { t } = useI18n()
|
feat: AIUI chat mode integration with iframe, context broker, overnight loop
- Chat mode: AIUI loads in sandboxed iframe at /dashboard/chat with transparent bg
- Mode switcher: Easy + Pro tabs only, Chat is a launcher button
- Keyboard shortcuts: Cmd+1 (Easy), Cmd+2 (Pro), Cmd+3 (Chat), Cmd+M (cycle)
- Directional transitions: chat slides from/to left, dashboard from/to right
- Context broker: postMessage protocol for quarantined AIUI communication
- AI permissions store: user-controlled toggles for data access categories
- Settings UI: AI Data Access section with per-category toggles
- AIUI container manifest and nginx proxy config for /aiui/
- Deploy script builds AIUI with /aiui/ base path
- Overnight loop infrastructure (loop.sh, prepare.sh, plan.md, prompt.md)
- Security hooks for autonomous overnight runs
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 12:06:20 +00:00
|
|
|
|
2026-01-24 22:59:20 +00:00
|
|
|
const router = useRouter()
|
|
|
|
|
const route = useRoute()
|
|
|
|
|
const store = useAppStore()
|
2026-03-16 12:58:35 +00:00
|
|
|
const appLauncher = useAppLauncherStore()
|
2026-02-17 19:19:54 +00:00
|
|
|
const loginTransition = useLoginTransitionStore()
|
2026-03-22 03:30:21 +00:00
|
|
|
const { getTransitionName } = useRouteTransitions()
|
2026-02-17 19:19:54 +00:00
|
|
|
|
|
|
|
|
const showZoomIn = ref(false)
|
2026-03-01 18:07:35 +00:00
|
|
|
const pendingTimers: ReturnType<typeof setTimeout>[] = []
|
|
|
|
|
|
|
|
|
|
function scheduledTimeout(fn: () => void, ms: number) {
|
|
|
|
|
const id = setTimeout(fn, ms)
|
|
|
|
|
pendingTimers.push(id)
|
|
|
|
|
return id
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-24 22:59:20 +00:00
|
|
|
const showAltBackground = ref(false)
|
2026-02-17 19:19:54 +00:00
|
|
|
const isHomeRoute = computed(() => route.path === '/dashboard' || route.path === '/dashboard/')
|
2026-01-24 22:59:20 +00:00
|
|
|
const isGlitching = ref(false)
|
|
|
|
|
|
2026-03-01 18:07:35 +00:00
|
|
|
const backgroundImage = computed(() => {
|
|
|
|
|
if (isDetailRoute(route.path)) return 'bg-intro.jpg'
|
|
|
|
|
return ROUTE_BACKGROUNDS[route.path] || 'bg-home.jpg'
|
2026-01-24 22:59:20 +00:00
|
|
|
})
|
|
|
|
|
|
2026-03-01 18:07:35 +00:00
|
|
|
const isDarkRoute = computed(() => {
|
|
|
|
|
const p = route.path
|
|
|
|
|
return p.includes('/dashboard/web5') ||
|
|
|
|
|
p.includes('/dashboard/server') ||
|
|
|
|
|
p.includes('/dashboard/settings') ||
|
|
|
|
|
(p.includes('/dashboard/apps') && !isDetailRoute(p)) ||
|
|
|
|
|
p.includes('/dashboard/marketplace') ||
|
2026-03-19 16:12:01 +00:00
|
|
|
p.includes('/dashboard/discover') ||
|
2026-03-01 18:07:35 +00:00
|
|
|
p.includes('/dashboard/cloud')
|
2026-01-24 22:59:20 +00:00
|
|
|
})
|
|
|
|
|
|
2026-03-01 18:07:35 +00:00
|
|
|
const showDarkOverlay = ref(isDarkRoute.value)
|
|
|
|
|
let overlayTimer: ReturnType<typeof setTimeout> | null = null
|
|
|
|
|
|
|
|
|
|
watch(isDarkRoute, (dark) => {
|
|
|
|
|
if (overlayTimer) { clearTimeout(overlayTimer); overlayTimer = null }
|
|
|
|
|
if (dark) {
|
|
|
|
|
showDarkOverlay.value = true
|
|
|
|
|
} else {
|
|
|
|
|
overlayTimer = scheduledTimeout(() => { showDarkOverlay.value = false }, 450)
|
|
|
|
|
}
|
2026-01-24 22:59:20 +00:00
|
|
|
})
|
2026-03-01 18:07:35 +00:00
|
|
|
|
2026-03-22 03:30:21 +00:00
|
|
|
const needsMobileBackButtonSpace = computed(() => isDetailRoute(route.path))
|
|
|
|
|
|
|
|
|
|
const mobileNavRef = ref<InstanceType<typeof DashboardMobileNav> | null>(null)
|
2026-01-24 22:59:20 +00:00
|
|
|
|
2026-03-22 03:30:21 +00:00
|
|
|
const mobileTabPaddingTop = computed(() => {
|
|
|
|
|
return mobileNavRef.value?.mobileTabPaddingTop ?? 0
|
|
|
|
|
})
|
2026-01-24 22:59:20 +00:00
|
|
|
|
|
|
|
|
watch(() => route.path, (newPath) => {
|
|
|
|
|
const isAppDetails = isDetailRoute(newPath)
|
|
|
|
|
const wasAppDetails = showAltBackground.value
|
2026-03-01 18:07:35 +00:00
|
|
|
|
2026-01-24 22:59:20 +00:00
|
|
|
showAltBackground.value = isAppDetails
|
2026-03-01 18:07:35 +00:00
|
|
|
|
2026-01-24 22:59:20 +00:00
|
|
|
if (isAppDetails && !wasAppDetails) {
|
2026-03-01 18:07:35 +00:00
|
|
|
scheduledTimeout(() => {
|
2026-01-24 22:59:20 +00:00
|
|
|
isGlitching.value = true
|
2026-03-01 18:07:35 +00:00
|
|
|
scheduledTimeout(() => { isGlitching.value = false }, 375)
|
|
|
|
|
}, 500)
|
2026-01-24 22:59:20 +00:00
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
onMounted(() => {
|
2026-02-17 19:19:54 +00:00
|
|
|
document.body.classList.add('dashboard-active')
|
|
|
|
|
if (loginTransition.justLoggedIn) {
|
|
|
|
|
playDashboardLoadOomph()
|
|
|
|
|
showZoomIn.value = true
|
|
|
|
|
loginTransition.setPendingWelcomeTyping(true)
|
|
|
|
|
loginTransition.setJustLoggedIn(false)
|
|
|
|
|
const triggerRevealGlitch = () => {
|
|
|
|
|
isGlitching.value = true
|
2026-03-01 18:07:35 +00:00
|
|
|
scheduledTimeout(() => { isGlitching.value = false }, 380)
|
2026-02-17 19:19:54 +00:00
|
|
|
}
|
2026-03-01 18:07:35 +00:00
|
|
|
scheduledTimeout(triggerRevealGlitch, 500)
|
|
|
|
|
scheduledTimeout(triggerRevealGlitch, 1200)
|
|
|
|
|
scheduledTimeout(triggerRevealGlitch, 2000)
|
|
|
|
|
scheduledTimeout(() => { showZoomIn.value = false }, 8000)
|
|
|
|
|
scheduledTimeout(() => {
|
2026-02-17 19:19:54 +00:00
|
|
|
loginTransition.setStartWelcomeTyping(true)
|
|
|
|
|
loginTransition.setPendingWelcomeTyping(false)
|
|
|
|
|
}, 4000)
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-11 13:04:31 +00:00
|
|
|
window.addEventListener('keydown', handleKioskShortcuts)
|
2026-01-24 22:59:20 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
|
|
onBeforeUnmount(() => {
|
2026-02-17 19:19:54 +00:00
|
|
|
document.body.classList.remove('dashboard-active')
|
2026-03-11 13:04:31 +00:00
|
|
|
window.removeEventListener('keydown', handleKioskShortcuts)
|
2026-03-01 18:07:35 +00:00
|
|
|
for (const id of pendingTimers) clearTimeout(id)
|
|
|
|
|
pendingTimers.length = 0
|
|
|
|
|
if (overlayTimer) { clearTimeout(overlayTimer); overlayTimer = null }
|
2026-01-24 22:59:20 +00:00
|
|
|
})
|
|
|
|
|
|
2026-03-11 13:04:31 +00:00
|
|
|
function isKioskMode(): boolean {
|
|
|
|
|
try {
|
|
|
|
|
return localStorage.getItem('kiosk') === 'true' || new URLSearchParams(window.location.search).has('kiosk')
|
|
|
|
|
} catch { return false }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function handleKioskShortcuts(e: KeyboardEvent) {
|
|
|
|
|
if (!isKioskMode()) return
|
|
|
|
|
if (e.ctrlKey && e.shiftKey) {
|
|
|
|
|
if (e.key === 'R' || e.key === 'r') {
|
|
|
|
|
e.preventDefault()
|
|
|
|
|
router.push('/recovery')
|
|
|
|
|
} else if (e.key === 'H' || e.key === 'h') {
|
|
|
|
|
e.preventDefault()
|
|
|
|
|
router.push('/dashboard')
|
|
|
|
|
} else if (e.key === 'Q' || e.key === 'q') {
|
|
|
|
|
e.preventDefault()
|
|
|
|
|
if (confirm('Reboot the server?')) {
|
|
|
|
|
fetch('/rpc/', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ method: 'system.reboot' }) }).catch(() => {})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-24 22:59:20 +00:00
|
|
|
async function handleLogout() {
|
2026-03-01 17:53:18 +00:00
|
|
|
try {
|
|
|
|
|
await store.logout()
|
|
|
|
|
} catch {
|
|
|
|
|
/* proceed to login regardless */
|
|
|
|
|
}
|
|
|
|
|
router.push('/login').catch(() => {
|
|
|
|
|
window.location.href = '/login'
|
|
|
|
|
})
|
2026-01-24 22:59:20 +00:00
|
|
|
}
|
|
|
|
|
</script>
|
|
|
|
|
|
2026-03-22 03:30:21 +00:00
|
|
|
<!-- Styles extracted to dashboard/dashboard-styles.css -->
|