From 30505f41ffb66df9e5cc9776d57fb78eaae3b6fe Mon Sep 17 00:00:00 2001 From: archipelago Date: Fri, 15 May 2026 17:54:32 -0400 Subject: [PATCH] chore(release): refresh v1.7.56-alpha notes and artifacts --- CHANGELOG.md | 12 +++++++++- neode-ui/src/App.vue | 2 +- .../src/stores/__tests__/screensaver.test.ts | 18 +++++++++++++++ neode-ui/src/stores/screensaver.ts | 22 +++++++++++++++++++ neode-ui/src/views/AppSession.vue | 16 ++++++++++++++ release-manifest.json | 14 ++++++++---- releases/manifest.json | 14 ++++++++---- 7 files changed, 88 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f57929fb..c43cc0e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,11 +1,21 @@ # Changelog -## v1.7.56-alpha (2026-05-14) +## v1.7.56-alpha (2026-05-15) - Health notifications now clear when an app is no longer unhealthy, including stale alerts for removed containers such as Portainer. - Quadlet environment values with spaces or shell metacharacters are quoted consistently, preventing env drift recreate loops for apps like nostr-rs-relay and Grafana. - Boot/bootstrap reconcile avoids restarting running Bitcoin containers while repairing RPC config, preserving IBD progress on active nodes. - Exit code 137 is labeled as SIGKILL instead of assuming OOM, avoiding false OOM alerts for orchestrator-managed recreates. +- Container reconcile force-recreates Podman records stuck in `Stopping`, preserving bind-mounted app data while recovering wedged containers automatically. +- Container health reporting is honest for running containers: Archipelago surfaces Podman's actual health state instead of marking every running container healthy. +- Quadlet reconciliation restarts services when stale health gates, port bindings, network aliases, exec commands, or healthchecks drift from the current manifest. +- Bitcoin Knots sync performance improves on fresh installs and updates with 8Gi container memory, a 4Gi dbcache, and full CPU parallelism. +- ElectrumX initial indexing gets more headroom: CPU caps are removed, memory is raised to 4Gi, cache is raised to 3Gi, and oversized sends are allowed for heavier wallet/indexing workloads. +- Mempool/ElectrumX lifecycle qualification respects pruned/non-archival Bitcoin nodes instead of installing a half-running stack with unhealthy dependencies. +- LND wallet/RPC helpers are more tolerant of container-owned files and updated REST port metadata, improving LND lifecycle and wallet-connect flows. +- Marketplace/catalog metadata carries richer container config so remote lifecycle tests install apps using the same settings users get from the UI. +- The app screensaver no longer activates during media-heavy app sessions such as IndeeHub, Jellyfin, Immich, PhotoPrism, and File Browser; apps can also pause/resume it with media playback messages. +- A fresh `1.7.56-alpha` unbundled installer ISO is built from the same primary VPS2 release line for easy download and USB flashing. ## v1.7.55-alpha (2026-05-13) diff --git a/neode-ui/src/App.vue b/neode-ui/src/App.vue index bc653c1d..422c3479 100644 --- a/neode-ui/src/App.vue +++ b/neode-ui/src/App.vue @@ -161,7 +161,7 @@ function onKeyDown(e: KeyboardEvent) { } // 's' key activates screensaver when authenticated (skip if typing in input) if (e.key === 's' || e.key === 'S') { - if (!isInput && appStore.isAuthenticated && !screensaverStore.isActive) { + if (!isInput && appStore.isAuthenticated && !screensaverStore.isActive && !screensaverStore.isSuppressed) { e.preventDefault() screensaverStore.activate() } diff --git a/neode-ui/src/stores/__tests__/screensaver.test.ts b/neode-ui/src/stores/__tests__/screensaver.test.ts index 22b0c784..622f31f1 100644 --- a/neode-ui/src/stores/__tests__/screensaver.test.ts +++ b/neode-ui/src/stores/__tests__/screensaver.test.ts @@ -68,6 +68,24 @@ describe('useScreensaverStore', () => { expect(store.isActive).toBe(false) }) + it('suppression prevents automatic and manual activation until resumed', () => { + const store = useScreensaverStore() + store.suppress('video') + expect(store.isSuppressed).toBe(true) + + store.resetInactivityTimer() + vi.advanceTimersByTime(5 * 60 * 1000) + expect(store.isActive).toBe(false) + + store.activate() + expect(store.isActive).toBe(false) + + store.resume('video') + expect(store.isSuppressed).toBe(false) + vi.advanceTimersByTime(3 * 60 * 1000) + expect(store.isActive).toBe(true) + }) + it('activate clears any pending timer', () => { const store = useScreensaverStore() store.deactivate() diff --git a/neode-ui/src/stores/screensaver.ts b/neode-ui/src/stores/screensaver.ts index fab8c1d8..41de6a23 100644 --- a/neode-ui/src/stores/screensaver.ts +++ b/neode-ui/src/stores/screensaver.ts @@ -6,12 +6,15 @@ const INACTIVITY_MS = 3 * 60 * 1000 // 3 minutes export const useScreensaverStore = defineStore('screensaver', () => { const isActive = ref(false) const activationCount = ref(0) + const suppressionReasons = ref>(new Set()) let inactivityTimer: ReturnType | null = null /** True when the current activation is the ASCII variant (every 3rd time) */ const isAsciiMode = computed(() => activationCount.value > 0 && activationCount.value % 3 === 0) + const isSuppressed = computed(() => suppressionReasons.value.size > 0) function activate() { + if (isSuppressed.value) return activationCount.value++ isActive.value = true clearInactivityTimer() @@ -24,8 +27,10 @@ export const useScreensaverStore = defineStore('screensaver', () => { function resetInactivityTimer() { clearInactivityTimer() + if (isSuppressed.value) return inactivityTimer = setTimeout(() => { inactivityTimer = null + if (isSuppressed.value) return isActive.value = true }, INACTIVITY_MS) } @@ -37,13 +42,30 @@ export const useScreensaverStore = defineStore('screensaver', () => { } } + function suppress(reason: string) { + suppressionReasons.value = new Set(suppressionReasons.value).add(reason) + clearInactivityTimer() + isActive.value = false + } + + function resume(reason: string) { + if (!suppressionReasons.value.has(reason)) return + const next = new Set(suppressionReasons.value) + next.delete(reason) + suppressionReasons.value = next + if (next.size === 0) resetInactivityTimer() + } + return { isActive, isAsciiMode, + isSuppressed, activationCount, activate, deactivate, resetInactivityTimer, clearInactivityTimer, + suppress, + resume, } }) diff --git a/neode-ui/src/views/AppSession.vue b/neode-ui/src/views/AppSession.vue index 78baf890..893ef532 100644 --- a/neode-ui/src/views/AppSession.vue +++ b/neode-ui/src/views/AppSession.vue @@ -91,6 +91,7 @@ import { ref, computed, onMounted, onBeforeUnmount, watch } from 'vue' import { useRoute, useRouter } from 'vue-router' import { useAppLauncherStore } from '@/stores/appLauncher' +import { useScreensaverStore } from '@/stores/screensaver' import NostrIdentityPicker from '@/components/NostrIdentityPicker.vue' import AppSessionHeader from './appSession/AppSessionHeader.vue' import AppSessionFrame from './appSession/AppSessionFrame.vue' @@ -115,6 +116,7 @@ const isInlinePanel = computed(() => !!props.appIdProp) const route = useRoute() const router = useRouter() +const screensaverStore = useScreensaverStore() const sessionRef = ref(null) const frameRef = ref | null>(null) @@ -145,6 +147,14 @@ const appId = computed(() => { const appTitle = computed(() => resolveAppTitle(appId.value)) const isMobile = typeof window !== 'undefined' && window.innerWidth < 768 const mustOpenNewTab = computed(() => NEW_TAB_APPS.has(appId.value)) +const screensaverReason = computed(() => `app-session:${appId.value}`) +const screensaverSuppressedApps = new Set([ + 'indeedhub', + 'jellyfin', + 'immich', + 'photoprism', + 'filebrowser', +]) const appUrl = computed(() => { return resolveAppUrl(appId.value, route.query.path as string | undefined) @@ -306,6 +316,8 @@ function onFullscreenChange() { function onMessage(e: MessageEvent) { if (e.data?.type === 'nostr-request') nostrBridge.handleNostrRequest(e) if (e.data?.type === 'archipelago:identity:request') identity.handleIdentityRequest() + if (e.data?.type === 'archipelago:media:playing') screensaverStore.suppress(screensaverReason.value) + if (e.data?.type === 'archipelago:media:idle') screensaverStore.resume(screensaverReason.value) } // Enter fullscreen on mount if mode is fullscreen @@ -338,6 +350,9 @@ onMounted(() => { sessionRef.value?.requestFullscreen().catch(() => {}) }) } + if (screensaverSuppressedApps.has(appId.value)) { + screensaverStore.suppress(screensaverReason.value) + } }) onBeforeUnmount(() => { @@ -347,6 +362,7 @@ onBeforeUnmount(() => { window.removeEventListener('keydown', onKeyDown, true) window.removeEventListener('message', onMessage) document.removeEventListener('fullscreenchange', onFullscreenChange) + screensaverStore.resume(screensaverReason.value) if (document.fullscreenElement) document.exitFullscreen().catch(() => {}) }) diff --git a/release-manifest.json b/release-manifest.json index 93deb75f..ed9c3919 100644 --- a/release-manifest.json +++ b/release-manifest.json @@ -1,11 +1,17 @@ { "version": "1.7.56-alpha", - "release_date": "2026-05-14", + "release_date": "2026-05-15", "changelog": [ "Health notifications now clear when an app is no longer unhealthy, including stale alerts for removed containers such as Portainer.", "Quadlet environment values with spaces or shell metacharacters are quoted consistently, preventing env drift recreate loops for apps like nostr-rs-relay and Grafana.", "Boot/bootstrap reconcile avoids restarting running Bitcoin containers while repairing RPC config, preserving IBD progress on active nodes.", - "Exit code 137 is labeled as SIGKILL instead of assuming OOM, avoiding false OOM alerts for orchestrator-managed recreates." + "Exit code 137 is labeled as SIGKILL instead of assuming OOM, avoiding false OOM alerts for orchestrator-managed recreates.", + "Container reconcile force-recreates Podman records stuck in `Stopping`, preserving bind-mounted app data while recovering wedged containers automatically.", + "Container health reporting is honest for running containers: Archipelago surfaces Podman's actual health state instead of marking every running container healthy.", + "Quadlet reconciliation restarts services when stale health gates, port bindings, network aliases, exec commands, or healthchecks drift from the current manifest.", + "Bitcoin Knots sync performance improves on fresh installs and updates with 8Gi container memory, a 4Gi dbcache, and full CPU parallelism.", + "ElectrumX initial indexing gets more headroom: CPU caps are removed, memory is raised to 4Gi, cache is raised to 3Gi, and oversized sends are allowed for heavier wallet/indexing workloads.", + "Mempool/ElectrumX lifecycle qualification respects pruned/non-archival Bitcoin nodes instead of installing a half-running stack with unhealthy dependencies." ], "components": [ { @@ -21,8 +27,8 @@ "current_version": "1.7.56-alpha", "new_version": "1.7.56-alpha", "download_url": "http://146.59.87.168:3000/lfg2025/archy/releases/download/v1.7.56-alpha/archipelago-frontend-1.7.56-alpha.tar.gz", - "sha256": "9cee503e260891267fed33e4c97f067a4a592b10695ecea4cbf730d517ff6c8b", - "size_bytes": 166493666 + "sha256": "91bfa10085696921c929ab7d216a8e617eb02e7105f21f32cdc3a2ba15158cd4", + "size_bytes": 166465069 } ] } diff --git a/releases/manifest.json b/releases/manifest.json index 93deb75f..ed9c3919 100644 --- a/releases/manifest.json +++ b/releases/manifest.json @@ -1,11 +1,17 @@ { "version": "1.7.56-alpha", - "release_date": "2026-05-14", + "release_date": "2026-05-15", "changelog": [ "Health notifications now clear when an app is no longer unhealthy, including stale alerts for removed containers such as Portainer.", "Quadlet environment values with spaces or shell metacharacters are quoted consistently, preventing env drift recreate loops for apps like nostr-rs-relay and Grafana.", "Boot/bootstrap reconcile avoids restarting running Bitcoin containers while repairing RPC config, preserving IBD progress on active nodes.", - "Exit code 137 is labeled as SIGKILL instead of assuming OOM, avoiding false OOM alerts for orchestrator-managed recreates." + "Exit code 137 is labeled as SIGKILL instead of assuming OOM, avoiding false OOM alerts for orchestrator-managed recreates.", + "Container reconcile force-recreates Podman records stuck in `Stopping`, preserving bind-mounted app data while recovering wedged containers automatically.", + "Container health reporting is honest for running containers: Archipelago surfaces Podman's actual health state instead of marking every running container healthy.", + "Quadlet reconciliation restarts services when stale health gates, port bindings, network aliases, exec commands, or healthchecks drift from the current manifest.", + "Bitcoin Knots sync performance improves on fresh installs and updates with 8Gi container memory, a 4Gi dbcache, and full CPU parallelism.", + "ElectrumX initial indexing gets more headroom: CPU caps are removed, memory is raised to 4Gi, cache is raised to 3Gi, and oversized sends are allowed for heavier wallet/indexing workloads.", + "Mempool/ElectrumX lifecycle qualification respects pruned/non-archival Bitcoin nodes instead of installing a half-running stack with unhealthy dependencies." ], "components": [ { @@ -21,8 +27,8 @@ "current_version": "1.7.56-alpha", "new_version": "1.7.56-alpha", "download_url": "http://146.59.87.168:3000/lfg2025/archy/releases/download/v1.7.56-alpha/archipelago-frontend-1.7.56-alpha.tar.gz", - "sha256": "9cee503e260891267fed33e4c97f067a4a592b10695ecea4cbf730d517ff6c8b", - "size_bytes": 166493666 + "sha256": "91bfa10085696921c929ab7d216a8e617eb02e7105f21f32cdc3a2ba15158cd4", + "size_bytes": 166465069 } ] }