2026-01-24 22:59:20 +00:00
|
|
|
<template>
|
|
|
|
|
<div id="app">
|
|
|
|
|
<!-- Splash Screen (only on first visit) -->
|
|
|
|
|
<SplashScreen v-if="showSplash" @complete="handleSplashComplete" />
|
|
|
|
|
|
|
|
|
|
<!-- Main App Content - only show after splash and routing is complete -->
|
2026-03-28 23:41:40 +00:00
|
|
|
<div v-if="!showSplash && !isReady" class="min-h-screen bg-black" />
|
|
|
|
|
<RouterView v-else-if="!showSplash && isReady" />
|
2026-01-24 22:59:20 +00:00
|
|
|
|
2026-02-17 15:03:34 +00:00
|
|
|
<!-- Spotlight command palette (Cmd+K / Ctrl+K) -->
|
|
|
|
|
<SpotlightSearch />
|
|
|
|
|
|
2026-03-09 17:09:59 +00:00
|
|
|
<!-- CLI popup (F key) -->
|
2026-02-18 10:26:33 +00:00
|
|
|
<CLIPopup />
|
|
|
|
|
|
2026-02-17 21:10:16 +00:00
|
|
|
<!-- App launcher overlay (iframe popup) -->
|
|
|
|
|
<AppLauncherOverlay />
|
|
|
|
|
|
2026-03-09 07:43:12 +00:00
|
|
|
<!-- Global toast notifications -->
|
|
|
|
|
<ToastStack />
|
|
|
|
|
|
2026-02-17 21:10:16 +00:00
|
|
|
<!-- Screensaver -->
|
|
|
|
|
<Screensaver />
|
|
|
|
|
|
2026-02-17 15:03:34 +00:00
|
|
|
<!-- Help guide modal (from spotlight) -->
|
|
|
|
|
<HelpGuideModal
|
|
|
|
|
:show="spotlightStore.helpModal.show"
|
|
|
|
|
:title="spotlightStore.helpModal.title"
|
|
|
|
|
:content="spotlightStore.helpModal.content"
|
|
|
|
|
:related-path="spotlightStore.helpModal.relatedPath"
|
|
|
|
|
@close="spotlightStore.closeHelpModal()"
|
|
|
|
|
/>
|
|
|
|
|
|
2026-01-24 22:59:20 +00:00
|
|
|
<!-- PWA Update Prompt -->
|
|
|
|
|
<PWAUpdatePrompt />
|
2026-02-17 19:19:54 +00:00
|
|
|
|
2026-02-18 13:48:45 +00:00
|
|
|
<!-- PWA Install Prompt (Install app, not just Add to Home Screen) -->
|
|
|
|
|
<PWAInstallPrompt />
|
|
|
|
|
|
2026-02-17 19:19:54 +00:00
|
|
|
<!-- Toast notifications - top right, glass style, any page -->
|
|
|
|
|
<Teleport to="body">
|
|
|
|
|
<Transition name="toast">
|
|
|
|
|
<div
|
|
|
|
|
v-if="toastMessage.show"
|
|
|
|
|
@click="messageToast.dismissToastAndOpenMessages"
|
|
|
|
|
class="fixed top-20 right-4 left-4 z-[100] w-auto max-w-md cursor-pointer rounded-xl p-4 transition-all hover:border-white/30 hover:shadow-2xl md:top-6 md:right-6 md:left-auto md:max-w-md toast-glass"
|
|
|
|
|
>
|
|
|
|
|
<div class="flex items-start gap-3">
|
|
|
|
|
<div class="flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-orange-500/20">
|
|
|
|
|
<svg class="h-5 w-5 text-orange-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
|
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
|
|
|
|
|
</svg>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="min-w-0 flex-1">
|
|
|
|
|
<p class="text-sm font-medium text-white">New message</p>
|
|
|
|
|
<p class="mt-0.5 text-sm text-white/70 line-clamp-2">{{ toastMessage.text }}</p>
|
|
|
|
|
<p class="mt-1 text-xs text-orange-400">Click to view</p>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</Transition>
|
|
|
|
|
</Teleport>
|
2026-01-24 22:59:20 +00:00
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
2026-02-17 19:19:54 +00:00
|
|
|
import { ref, onMounted, onBeforeUnmount, watch } from 'vue'
|
2026-01-24 22:59:20 +00:00
|
|
|
import { useRouter, useRoute } from 'vue-router'
|
|
|
|
|
import SplashScreen from './components/SplashScreen.vue'
|
|
|
|
|
import PWAUpdatePrompt from './components/PWAUpdatePrompt.vue'
|
2026-02-18 13:48:45 +00:00
|
|
|
import PWAInstallPrompt from './components/PWAInstallPrompt.vue'
|
2026-02-17 15:03:34 +00:00
|
|
|
import SpotlightSearch from './components/SpotlightSearch.vue'
|
2026-02-18 10:26:33 +00:00
|
|
|
import CLIPopup from './components/CLIPopup.vue'
|
2026-02-17 21:10:16 +00:00
|
|
|
import AppLauncherOverlay from './components/AppLauncherOverlay.vue'
|
2026-03-09 07:43:12 +00:00
|
|
|
import ToastStack from './components/ToastStack.vue'
|
2026-02-17 21:10:16 +00:00
|
|
|
import Screensaver from './components/Screensaver.vue'
|
2026-02-17 15:03:34 +00:00
|
|
|
import HelpGuideModal from './components/HelpGuideModal.vue'
|
2026-04-02 11:41:54 +01:00
|
|
|
|
2026-02-17 15:03:34 +00:00
|
|
|
import { useControllerNav } from '@/composables/useControllerNav'
|
2026-02-18 10:37:26 +00:00
|
|
|
import { playKeyboardTypingSound } from '@/composables/useLoginSounds'
|
2026-02-17 15:03:34 +00:00
|
|
|
import { useSpotlightStore } from '@/stores/spotlight'
|
2026-02-18 10:26:33 +00:00
|
|
|
import { useCLIStore } from '@/stores/cli'
|
2026-02-17 19:19:54 +00:00
|
|
|
import { useMessageToast } from '@/composables/useMessageToast'
|
|
|
|
|
import { useAppStore } from '@/stores/app'
|
2026-02-17 21:10:16 +00:00
|
|
|
import { useScreensaverStore } from '@/stores/screensaver'
|
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
|
|
|
import { useUIModeStore } from '@/stores/uiMode'
|
2026-04-02 11:10:08 +01:00
|
|
|
import { startRemoteRelay, stopRemoteRelay } from '@/api/remote-relay'
|
2026-01-24 22:59:20 +00:00
|
|
|
|
|
|
|
|
const router = useRouter()
|
2026-02-17 21:10:16 +00:00
|
|
|
const screensaverStore = useScreensaverStore()
|
2026-02-17 15:03:34 +00:00
|
|
|
const spotlightStore = useSpotlightStore()
|
2026-02-18 10:26:33 +00:00
|
|
|
const cliStore = useCLIStore()
|
2026-02-17 19:19:54 +00:00
|
|
|
const appStore = useAppStore()
|
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
|
|
|
const uiModeStore = useUIModeStore()
|
2026-02-17 19:19:54 +00:00
|
|
|
const messageToast = useMessageToast()
|
|
|
|
|
const toastMessage = messageToast.toastMessage
|
|
|
|
|
|
2026-02-17 15:03:34 +00:00
|
|
|
useControllerNav()
|
|
|
|
|
|
2026-04-02 11:10:08 +01:00
|
|
|
// Start/stop message polling and remote relay when auth state changes
|
2026-02-17 19:19:54 +00:00
|
|
|
watch(() => appStore.isAuthenticated, (authenticated) => {
|
|
|
|
|
if (authenticated) {
|
|
|
|
|
messageToast.startPolling()
|
2026-02-17 21:10:16 +00:00
|
|
|
screensaverStore.resetInactivityTimer()
|
2026-04-02 11:10:08 +01:00
|
|
|
startRemoteRelay()
|
2026-02-17 19:19:54 +00:00
|
|
|
} else {
|
|
|
|
|
messageToast.stopPolling()
|
|
|
|
|
toastMessage.value = { show: false, text: '' }
|
2026-02-17 21:10:16 +00:00
|
|
|
screensaverStore.clearInactivityTimer()
|
|
|
|
|
screensaverStore.deactivate()
|
2026-04-02 11:10:08 +01:00
|
|
|
stopRemoteRelay()
|
2026-02-17 19:19:54 +00:00
|
|
|
}
|
|
|
|
|
}, { immediate: true })
|
|
|
|
|
|
2026-02-17 21:10:16 +00:00
|
|
|
// Reset screensaver inactivity on user activity (when authenticated)
|
|
|
|
|
function onUserActivity() {
|
|
|
|
|
if (appStore.isAuthenticated && !screensaverStore.isActive) {
|
|
|
|
|
screensaverStore.resetInactivityTimer()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-17 15:03:34 +00:00
|
|
|
function onKeyDown(e: KeyboardEvent) {
|
|
|
|
|
const isMac = navigator.platform.toUpperCase().includes('MAC')
|
|
|
|
|
const mod = isMac ? e.metaKey : e.ctrlKey
|
2026-03-02 08:34:13 +00:00
|
|
|
// Cmd+K / Ctrl+K only (modifier required - avoids accidental trigger when typing)
|
2026-02-17 22:10:38 +00:00
|
|
|
const target = e.target as HTMLElement
|
|
|
|
|
const isInput = target.tagName === 'INPUT' || target.tagName === 'TEXTAREA' || target.isContentEditable
|
2026-03-02 08:34:13 +00:00
|
|
|
if (mod && e.key === 'k') {
|
2026-02-17 15:03:34 +00:00
|
|
|
e.preventDefault()
|
|
|
|
|
spotlightStore.toggle()
|
2026-02-17 22:10:38 +00:00
|
|
|
return
|
|
|
|
|
}
|
2026-03-09 17:09:59 +00:00
|
|
|
// F key - CLI popup (skip when in input or modifier held)
|
|
|
|
|
if ((e.key === 'f' || e.key === 'F') && !isInput && !mod && !e.altKey) {
|
2026-02-18 10:26:33 +00:00
|
|
|
e.preventDefault()
|
|
|
|
|
cliStore.toggle()
|
|
|
|
|
return
|
|
|
|
|
}
|
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
|
|
|
// Cmd+1/2/3 - switch UI mode (skip when in input)
|
|
|
|
|
if (mod && !isInput && appStore.isAuthenticated) {
|
|
|
|
|
if (e.key === '1') { e.preventDefault(); uiModeStore.setMode('easy'); router.push('/dashboard'); return }
|
|
|
|
|
if (e.key === '2') { e.preventDefault(); uiModeStore.setMode('gamer'); router.push('/dashboard'); return }
|
|
|
|
|
if (e.key === '3') { e.preventDefault(); router.push('/dashboard/chat'); return }
|
|
|
|
|
}
|
|
|
|
|
// Cmd+M / Ctrl+M - cycle UI mode (skip when in input)
|
|
|
|
|
if (mod && (e.key === 'm' || e.key === 'M') && !isInput && appStore.isAuthenticated) {
|
|
|
|
|
e.preventDefault()
|
|
|
|
|
uiModeStore.cycleMode()
|
|
|
|
|
router.push('/dashboard')
|
|
|
|
|
return
|
|
|
|
|
}
|
2026-02-17 22:10:38 +00:00
|
|
|
// 's' key activates screensaver when authenticated (skip if typing in input)
|
|
|
|
|
if (e.key === 's' || e.key === 'S') {
|
|
|
|
|
if (!isInput && appStore.isAuthenticated && !screensaverStore.isActive) {
|
|
|
|
|
e.preventDefault()
|
|
|
|
|
screensaverStore.activate()
|
|
|
|
|
}
|
2026-02-17 15:03:34 +00:00
|
|
|
}
|
2026-02-18 10:37:26 +00:00
|
|
|
// Keyboard typing sound - plays on any character typed in inputs (global)
|
|
|
|
|
if (isInput && e.key.length === 1 && !e.ctrlKey && !e.metaKey && !e.altKey) {
|
|
|
|
|
playKeyboardTypingSound()
|
|
|
|
|
}
|
2026-02-17 15:03:34 +00:00
|
|
|
}
|
|
|
|
|
|
2026-01-24 22:59:20 +00:00
|
|
|
const route = useRoute()
|
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
|
|
|
// Start with splash hidden — onMounted decides whether to show it
|
|
|
|
|
const showSplash = ref(false)
|
2026-01-24 22:59:20 +00:00
|
|
|
const isReady = ref(false)
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Determine if splash screen should be shown
|
|
|
|
|
* Splash is skipped if:
|
|
|
|
|
* - User has already seen the intro
|
|
|
|
|
* - User is on a direct route (refresh/bookmark)
|
|
|
|
|
*/
|
2026-03-26 20:06:09 +00:00
|
|
|
// Fix Chromium backdrop-filter rendering bug: when tab loses/regains focus,
|
|
|
|
|
// the compositor fails to repaint backdrop-filter layers over animated
|
|
|
|
|
// fixed-position overlays (body::before/after with mix-blend-mode).
|
|
|
|
|
// On return: strip backdrop-filter via class, wait a frame, then restore.
|
|
|
|
|
function onVisibilityChange() {
|
|
|
|
|
if (document.hidden) {
|
|
|
|
|
document.documentElement.classList.add('tab-hidden')
|
|
|
|
|
} else {
|
fix: production onboarding, CI tests, container security, keyboard nav
Install & Onboarding:
- Remove DEV_MODE=true from production ISO service file (auto-created
users, skipped password setup)
- Auto-install no longer overwrites rootfs service file with bad template
- Login.vue always checks auth.isSetup — shows password creation form
on fresh install without requiring dev build flag
- Deploy image-versions.sh to /opt/archipelago/scripts/ on installed nodes
- First-boot-containers sources image-versions.sh, runs podman as
archipelago user (rootless), enables linger + podman.socket
- Correct volume ownership (100000:100000 for rootless UID mapping)
Container Security:
- FileBrowser: add --cap-add=DAC_OVERRIDE for rootless podman volume access
- FileBrowser: add --read-only, /data volume for database, proper cmd args
- First-boot script matches backend config (security hardening + health check)
CI Pipeline:
- Add vue-tsc type check + vitest run to build-iso.yml (runs every push)
- Add post-install-tests.yml workflow (workflow_dispatch, SSH to target)
- Build report: set +eo pipefail, fix rootfs path, add || true guards
- Bundle run-post-install-tests.sh into ISO
E2E Test Suite (scripts/run-post-install-tests.sh):
- Phase 1: Install verification (files, services, podman, linger, DEV_MODE check)
- Phase 2: Onboarding flow (auth.isSetup, auth.setup, login, DID, complete)
- Phase 3: Container lifecycle (install 3 apps via package.install RPC,
verify running, stop, verify stopped, restart, verify running, health)
- Phase 4: Log verification (first-boot log, diagnostics, journal errors)
- Correct package.install params: {"id", "dockerImage"}
Frontend:
- Fix backdrop-filter tab-switch bug (keep animations paused during rebuild)
- Dashboard glitch animations paused during tab-hidden
- Gamepad nav: auto-focus first container on route change
- Tab roving: Left/Right on role="tab" cycles and activates sibling tabs
- ContainerApps: data-controller-launch on running app cards
- 515 tests passing (fixed 30 broken, added 19 new keyboard nav tests)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 16:16:57 +00:00
|
|
|
// Step 1: strip backdrop-filter while animations stay paused (tab-hidden)
|
2026-03-26 20:06:09 +00:00
|
|
|
document.documentElement.classList.add('no-backdrop')
|
fix: production onboarding, CI tests, container security, keyboard nav
Install & Onboarding:
- Remove DEV_MODE=true from production ISO service file (auto-created
users, skipped password setup)
- Auto-install no longer overwrites rootfs service file with bad template
- Login.vue always checks auth.isSetup — shows password creation form
on fresh install without requiring dev build flag
- Deploy image-versions.sh to /opt/archipelago/scripts/ on installed nodes
- First-boot-containers sources image-versions.sh, runs podman as
archipelago user (rootless), enables linger + podman.socket
- Correct volume ownership (100000:100000 for rootless UID mapping)
Container Security:
- FileBrowser: add --cap-add=DAC_OVERRIDE for rootless podman volume access
- FileBrowser: add --read-only, /data volume for database, proper cmd args
- First-boot script matches backend config (security hardening + health check)
CI Pipeline:
- Add vue-tsc type check + vitest run to build-iso.yml (runs every push)
- Add post-install-tests.yml workflow (workflow_dispatch, SSH to target)
- Build report: set +eo pipefail, fix rootfs path, add || true guards
- Bundle run-post-install-tests.sh into ISO
E2E Test Suite (scripts/run-post-install-tests.sh):
- Phase 1: Install verification (files, services, podman, linger, DEV_MODE check)
- Phase 2: Onboarding flow (auth.isSetup, auth.setup, login, DID, complete)
- Phase 3: Container lifecycle (install 3 apps via package.install RPC,
verify running, stop, verify stopped, restart, verify running, health)
- Phase 4: Log verification (first-boot log, diagnostics, journal errors)
- Correct package.install params: {"id", "dockerImage"}
Frontend:
- Fix backdrop-filter tab-switch bug (keep animations paused during rebuild)
- Dashboard glitch animations paused during tab-hidden
- Gamepad nav: auto-focus first container on route change
- Tab roving: Left/Right on role="tab" cycles and activates sibling tabs
- ContainerApps: data-controller-launch on running app cards
- 515 tests passing (fixed 30 broken, added 19 new keyboard nav tests)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 16:16:57 +00:00
|
|
|
// Step 2: restore backdrop-filter over static content (clean compositor rebuild)
|
|
|
|
|
// Use setTimeout — Chromium batches rAFs on tab return
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
document.documentElement.classList.remove('no-backdrop')
|
|
|
|
|
// Step 3: resume animations after backdrop-filter layers are established
|
2026-03-26 20:06:09 +00:00
|
|
|
requestAnimationFrame(() => {
|
fix: production onboarding, CI tests, container security, keyboard nav
Install & Onboarding:
- Remove DEV_MODE=true from production ISO service file (auto-created
users, skipped password setup)
- Auto-install no longer overwrites rootfs service file with bad template
- Login.vue always checks auth.isSetup — shows password creation form
on fresh install without requiring dev build flag
- Deploy image-versions.sh to /opt/archipelago/scripts/ on installed nodes
- First-boot-containers sources image-versions.sh, runs podman as
archipelago user (rootless), enables linger + podman.socket
- Correct volume ownership (100000:100000 for rootless UID mapping)
Container Security:
- FileBrowser: add --cap-add=DAC_OVERRIDE for rootless podman volume access
- FileBrowser: add --read-only, /data volume for database, proper cmd args
- First-boot script matches backend config (security hardening + health check)
CI Pipeline:
- Add vue-tsc type check + vitest run to build-iso.yml (runs every push)
- Add post-install-tests.yml workflow (workflow_dispatch, SSH to target)
- Build report: set +eo pipefail, fix rootfs path, add || true guards
- Bundle run-post-install-tests.sh into ISO
E2E Test Suite (scripts/run-post-install-tests.sh):
- Phase 1: Install verification (files, services, podman, linger, DEV_MODE check)
- Phase 2: Onboarding flow (auth.isSetup, auth.setup, login, DID, complete)
- Phase 3: Container lifecycle (install 3 apps via package.install RPC,
verify running, stop, verify stopped, restart, verify running, health)
- Phase 4: Log verification (first-boot log, diagnostics, journal errors)
- Correct package.install params: {"id", "dockerImage"}
Frontend:
- Fix backdrop-filter tab-switch bug (keep animations paused during rebuild)
- Dashboard glitch animations paused during tab-hidden
- Gamepad nav: auto-focus first container on route change
- Tab roving: Left/Right on role="tab" cycles and activates sibling tabs
- ContainerApps: data-controller-launch on running app cards
- 515 tests passing (fixed 30 broken, added 19 new keyboard nav tests)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 16:16:57 +00:00
|
|
|
document.documentElement.classList.remove('tab-hidden')
|
2026-03-26 20:06:09 +00:00
|
|
|
})
|
fix: production onboarding, CI tests, container security, keyboard nav
Install & Onboarding:
- Remove DEV_MODE=true from production ISO service file (auto-created
users, skipped password setup)
- Auto-install no longer overwrites rootfs service file with bad template
- Login.vue always checks auth.isSetup — shows password creation form
on fresh install without requiring dev build flag
- Deploy image-versions.sh to /opt/archipelago/scripts/ on installed nodes
- First-boot-containers sources image-versions.sh, runs podman as
archipelago user (rootless), enables linger + podman.socket
- Correct volume ownership (100000:100000 for rootless UID mapping)
Container Security:
- FileBrowser: add --cap-add=DAC_OVERRIDE for rootless podman volume access
- FileBrowser: add --read-only, /data volume for database, proper cmd args
- First-boot script matches backend config (security hardening + health check)
CI Pipeline:
- Add vue-tsc type check + vitest run to build-iso.yml (runs every push)
- Add post-install-tests.yml workflow (workflow_dispatch, SSH to target)
- Build report: set +eo pipefail, fix rootfs path, add || true guards
- Bundle run-post-install-tests.sh into ISO
E2E Test Suite (scripts/run-post-install-tests.sh):
- Phase 1: Install verification (files, services, podman, linger, DEV_MODE check)
- Phase 2: Onboarding flow (auth.isSetup, auth.setup, login, DID, complete)
- Phase 3: Container lifecycle (install 3 apps via package.install RPC,
verify running, stop, verify stopped, restart, verify running, health)
- Phase 4: Log verification (first-boot log, diagnostics, journal errors)
- Correct package.install params: {"id", "dockerImage"}
Frontend:
- Fix backdrop-filter tab-switch bug (keep animations paused during rebuild)
- Dashboard glitch animations paused during tab-hidden
- Gamepad nav: auto-focus first container on route change
- Tab roving: Left/Right on role="tab" cycles and activates sibling tabs
- ContainerApps: data-controller-launch on running app cards
- 515 tests passing (fixed 30 broken, added 19 new keyboard nav tests)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 16:16:57 +00:00
|
|
|
}, 50)
|
2026-03-26 20:06:09 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-17 19:42:59 +00:00
|
|
|
onMounted(async () => {
|
2026-03-26 20:06:09 +00:00
|
|
|
document.addEventListener('visibilitychange', onVisibilityChange)
|
2026-02-18 10:35:04 +00:00
|
|
|
window.addEventListener('keydown', onKeyDown, true)
|
2026-02-17 21:10:16 +00:00
|
|
|
window.addEventListener('mousemove', onUserActivity)
|
|
|
|
|
window.addEventListener('mousedown', onUserActivity)
|
|
|
|
|
window.addEventListener('keydown', onUserActivity)
|
|
|
|
|
window.addEventListener('touchstart', onUserActivity)
|
2026-01-24 22:59:20 +00:00
|
|
|
const seenIntro = localStorage.getItem('neode_intro_seen') === '1'
|
|
|
|
|
const isDirectRoute = route.path !== '/'
|
feat: rootless podman, session hardening, boot stability, sidebar fix
Rootless podman migration (TASK-11):
- Remove sudo from all podman calls in PodmanClient + 8 backend files
- Remove sudo from all podman/docker calls in deploy script
- Restore full systemd security hardening: NoNewPrivileges,
RestrictAddressFamilies, MemoryDenyWriteExecute, RestrictRealtime,
RestrictNamespaces, RestrictSUIDSGID, SystemCallFilter, ProtectSystem=strict
- Enable loginctl linger for rootless container persistence
- Remove Ollama from auto-deploy (marketplace-only)
Session & auth hardening:
- Increase MAX_CONCURRENT_SESSIONS 20→50 (prevents eviction storms)
- Debounced 401 redirect in rpc-client.ts (prevents redirect storms)
Boot stability:
- optimize-debian.sh: adds chrony, swap, removes policy-rc.d
- deploy script: pre-restart chrony + swap setup
- ISO build: chrony package, swap file creation
- BootScreen: no longer clears localStorage (prevents splash replay)
- RootRedirect: sole owner of localStorage clearing on server ready
UI fixes:
- Sidebar opacity default changed from 0→visible (fixes missing sidebar
after page-persistence login without entrance animation)
- Console.log/error wrapped in import.meta.env.DEV guards
- Remove unused route import from RootRedirect
Beta tracking:
- CLAUDE.md: beta freeze protocol added
- MASTER_PLAN.md: TASK-11, TASK-17, phase structure
- BETA-PROGRESS.md: initial tracking doc
- Tagged v1.2.0-alpha.1 as pre-rootless baseline
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 13:53:27 +00:00
|
|
|
const fromBoot = sessionStorage.getItem('archipelago_from_boot') === '1'
|
|
|
|
|
if (fromBoot) sessionStorage.removeItem('archipelago_from_boot')
|
|
|
|
|
if (import.meta.env.DEV) console.log('[App] onMounted — seenIntro:', seenIntro, 'fromBoot:', fromBoot)
|
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
|
|
|
|
|
|
|
|
if (fromBoot && !seenIntro) {
|
feat: rootless podman, session hardening, boot stability, sidebar fix
Rootless podman migration (TASK-11):
- Remove sudo from all podman calls in PodmanClient + 8 backend files
- Remove sudo from all podman/docker calls in deploy script
- Restore full systemd security hardening: NoNewPrivileges,
RestrictAddressFamilies, MemoryDenyWriteExecute, RestrictRealtime,
RestrictNamespaces, RestrictSUIDSGID, SystemCallFilter, ProtectSystem=strict
- Enable loginctl linger for rootless container persistence
- Remove Ollama from auto-deploy (marketplace-only)
Session & auth hardening:
- Increase MAX_CONCURRENT_SESSIONS 20→50 (prevents eviction storms)
- Debounced 401 redirect in rpc-client.ts (prevents redirect storms)
Boot stability:
- optimize-debian.sh: adds chrony, swap, removes policy-rc.d
- deploy script: pre-restart chrony + swap setup
- ISO build: chrony package, swap file creation
- BootScreen: no longer clears localStorage (prevents splash replay)
- RootRedirect: sole owner of localStorage clearing on server ready
UI fixes:
- Sidebar opacity default changed from 0→visible (fixes missing sidebar
after page-persistence login without entrance animation)
- Console.log/error wrapped in import.meta.env.DEV guards
- Remove unused route import from RootRedirect
Beta tracking:
- CLAUDE.md: beta freeze protocol added
- MASTER_PLAN.md: TASK-11, TASK-17, phase structure
- BETA-PROGRESS.md: initial tracking doc
- Tagged v1.2.0-alpha.1 as pre-rootless baseline
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 13:53:27 +00:00
|
|
|
// Coming from boot screen — show the full splash intro (Enter to Exit → typing → logo)
|
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
|
|
|
showSplash.value = true
|
|
|
|
|
} else if (!seenIntro && !isDirectRoute && import.meta.env.VITE_DEV_MODE !== 'boot') {
|
|
|
|
|
// Normal first visit (not boot mode) — show splash intro
|
|
|
|
|
showSplash.value = true
|
|
|
|
|
} else {
|
|
|
|
|
// Already seen intro, direct route, or boot mode (boot screen handles intro)
|
2026-03-28 23:41:40 +00:00
|
|
|
// Set isReady BEFORE hiding splash to prevent flash of partial content
|
2026-02-17 19:42:59 +00:00
|
|
|
await router.isReady()
|
2026-01-24 22:59:20 +00:00
|
|
|
isReady.value = true
|
2026-03-28 23:41:40 +00:00
|
|
|
showSplash.value = false
|
|
|
|
|
document.body.classList.add('splash-complete')
|
2026-01-24 22:59:20 +00:00
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
2026-02-17 15:03:34 +00:00
|
|
|
onBeforeUnmount(() => {
|
2026-03-26 20:06:09 +00:00
|
|
|
document.removeEventListener('visibilitychange', onVisibilityChange)
|
2026-02-18 10:35:04 +00:00
|
|
|
window.removeEventListener('keydown', onKeyDown, true)
|
2026-02-17 21:10:16 +00:00
|
|
|
window.removeEventListener('mousemove', onUserActivity)
|
|
|
|
|
window.removeEventListener('mousedown', onUserActivity)
|
|
|
|
|
window.removeEventListener('keydown', onUserActivity)
|
|
|
|
|
window.removeEventListener('touchstart', onUserActivity)
|
2026-02-17 15:03:34 +00:00
|
|
|
})
|
|
|
|
|
|
2026-01-24 22:59:20 +00:00
|
|
|
/**
|
|
|
|
|
* Handle splash screen completion
|
2026-02-17 15:03:34 +00:00
|
|
|
* Routes user directly to appropriate screen based on onboarding status (from backend)
|
2026-01-24 22:59:20 +00:00
|
|
|
*/
|
2026-02-17 15:03:34 +00:00
|
|
|
async function handleSplashComplete() {
|
2026-01-24 22:59:20 +00:00
|
|
|
showSplash.value = false
|
|
|
|
|
document.body.classList.add('splash-complete')
|
2026-01-24 23:09:46 +00:00
|
|
|
isReady.value = true
|
2026-02-17 19:19:54 +00:00
|
|
|
sessionStorage.setItem('archipelago_from_splash', '1')
|
2026-02-17 15:03:34 +00:00
|
|
|
|
2026-01-24 23:09:46 +00:00
|
|
|
const devMode = import.meta.env.VITE_DEV_MODE
|
2026-02-17 15:03:34 +00:00
|
|
|
if (devMode === 'setup' || devMode === 'existing') {
|
|
|
|
|
router.push('/login').catch(() => {})
|
|
|
|
|
return
|
2026-01-24 23:09:46 +00:00
|
|
|
}
|
2026-02-17 15:03:34 +00:00
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const { isOnboardingComplete } = await import('@/composables/useOnboarding')
|
|
|
|
|
const seenOnboarding = await isOnboardingComplete()
|
|
|
|
|
const destination = seenOnboarding ? '/login' : '/onboarding/intro'
|
|
|
|
|
router.push(destination).catch(() => {})
|
|
|
|
|
} catch {
|
|
|
|
|
router.push('/onboarding/intro').catch(() => {})
|
2026-01-24 23:09:46 +00:00
|
|
|
}
|
2026-01-24 22:59:20 +00:00
|
|
|
}
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<style>
|
|
|
|
|
/* Global styles are in style.css */
|
|
|
|
|
</style>
|