Name waiting apps, align card actions, and update Angor icon
Demo images / Build & push demo images (push) Failing after 39s

This commit is contained in:
archipelago
2026-09-30 12:43:53 -04:00
parent d50be13232
commit ef8254272c
11 changed files with 62 additions and 20 deletions
+3
View File
@@ -2,6 +2,9 @@
## Unreleased
- Named the app in compact readiness messages and kept app-card actions aligned at the bottom.
- Removed duplicate Mempool cards caused by frontend container aliases in restored inventory.
- Kept installed apps visible through restarts and hard refreshes, and delayed app launches until their web interface is ready.
- Made Bitcoin version selection readable and usable in the ThinkPad kiosk, above the pruning settings.
- Restored GitWorkshop build files in installation/update payloads and made slow image-pull progress clearer.
+2 -2
View File
@@ -658,7 +658,7 @@
],
"category": "money",
"tier": "optional",
"icon": "/assets/img/app-icons/angor.svg",
"icon": "/assets/img/app-icons/angor-green.png",
"repoUrl": "https://github.com/block-core/angor"
},
{
@@ -671,7 +671,7 @@
"requires": [],
"category": "nostr",
"tier": "optional",
"icon": "/assets/img/app-icons/angor.svg",
"icon": "/assets/img/app-icons/angor-green.png",
"repoUrl": "https://github.com/hoytech/strfry"
}
]
+1 -1
View File
@@ -46,7 +46,7 @@ Mempool recreation does not require editing IP addresses or restarting this app.
No app-specific Rust installer is required.
Source documentation: [Angor's official deployment guide](https://github.com/block-core/angor/blob/869dd43cf38332dd7128a284a6bf4c1cac44c1a7/docker/DEPLOY-INDEXER-AND-RELAY.md).
The unmodified icon comes from [angor.io/images/logo-text.svg](https://angor.io/images/logo-text.svg), retrieved 2026-09-30.
The app icon is based on [Angor’s dark-mode app icon](https://angor.io/images/app-icon-dark-mode.png), retrieved 2026-09-30. At the operator’s request, the outer corners use the same green as the background. The built-in imagegen edit preserved the black mark and filled the square green; the project asset is `neode-ui/public/assets/img/app-icons/angor-green.png`.
Tests and release acceptance are recorded in the next-release checklist. The
health probe establishes backend availability, not a guarantee that every
+1 -1
View File
@@ -58,7 +58,7 @@ app:
sync_required: true
pruning_support: false
metadata:
icon: /assets/img/app-icons/angor.svg
icon: /assets/img/app-icons/angor-green.png
tier: optional
repo: https://github.com/block-core/angor
features:
+1 -1
View File
@@ -214,7 +214,7 @@ app:
protocol: http
path: /
metadata:
icon: /assets/img/app-icons/angor.svg
icon: /assets/img/app-icons/angor-green.png
tier: optional
repo: https://github.com/hoytech/strfry
features:
+22
View File
@@ -120,3 +120,25 @@ smart-HTTP response type, current branch ref and Compose contents. Its
slirp4netns route and all production app containers survived. The temporary
Portainer fixture was absent. This verifies the repaired production route
across reboot; it does not substitute for final new-runtime delivery checks.
## Follow-up acceptance: app cards and Angor icon
- Mempool duplicate traced to `archy-mempool-web` durable inventory alias being
restored beside the real `mempool` frontend. Shared scanner canonicalization
fixes live and absent-container paths without deleting installed markers.
Frontend suppresses aliases only while a canonical tile exists.
- Readiness text names the app: “Waiting for Gitea…”. It shares the status row,
with full text available through its title; card actions use bottom alignment.
- Angor uses the operator-supplied dark-mode icon with green outer corners.
Built-in imagegen prompt: fill transparent/white corners with the existing
flat green, preserve the black symbol, square opaque PNG, no added details.
- Backend alias suite: 1608 passed, 4 ignored. Focused readiness/frame UI tests:
30 passed. Production UI build passed and is live on dev. Browser checks at
1440 and 1024 pixels verified named waiting text, bottom-aligned actions,
equal row heights, no overflow and one Mempool card after hard refresh.
The 390-pixel mobile icon layout also passed hard refresh. Final-source
isolated Mempool alias regression passed after the scanner simplification.
- Managed Angor adapter acceptance: five stop/start/restart cycles, missing
prerequisite refusal, management restart and cleanup all passed. Both temporary
fixtures and their network were removed. Actual dev API verification remains
pending after removing an incomplete legacy-created adapter.
Binary file not shown.

After

Width:  |  Height:  |  Size: 948 KiB

+2 -2
View File
@@ -658,7 +658,7 @@
],
"category": "money",
"tier": "optional",
"icon": "/assets/img/app-icons/angor.svg",
"icon": "/assets/img/app-icons/angor-green.png",
"repoUrl": "https://github.com/block-core/angor"
},
{
@@ -671,7 +671,7 @@
"requires": [],
"category": "nostr",
"tier": "optional",
"icon": "/assets/img/app-icons/angor.svg",
"icon": "/assets/img/app-icons/angor-green.png",
"repoUrl": "https://github.com/hoytech/strfry"
}
]
+9 -9
View File
@@ -4,7 +4,7 @@
:data-controller-launch="canLaunch(pkg) ? '' : undefined"
tabindex="0"
role="link"
class="glass-card p-6 transition-all hover:-translate-y-1 cursor-pointer relative min-w-0 overflow-hidden"
class="glass-card flex flex-col h-full p-6 transition-all hover:-translate-y-1 cursor-pointer relative min-w-0 overflow-hidden"
:class="{ 'card-stagger': showStagger }"
:style="{ '--stagger-index': index }"
@click="$emit('goToApp', id)"
@@ -56,9 +56,9 @@
{{ description }}
</p>
<div v-if="!isInstalling && !isUninstalling && pkg.state !== 'installing'" class="flex items-center gap-2">
<div v-if="!isInstalling && !isUninstalling && pkg.state !== 'installing'" class="flex items-center gap-2 min-w-0">
<span
class="inline-flex items-center gap-1.5 px-2 py-1 rounded text-xs font-medium"
class="shrink-0 inline-flex items-center gap-1.5 px-2 py-1 rounded text-xs font-medium"
:class="getStatusClass(pkg.state, pkg.health, pkg['exit-code'])"
>
<svg
@@ -74,14 +74,14 @@
<span v-if="pkg.state === 'running' && pkg.health === 'unhealthy'" class="w-1.5 h-1.5 rounded-full bg-orange-400 animate-pulse"></span>
{{ getStatusLabel(pkg.state, pkg.health, pkg['exit-code']) }}
</span>
<p v-if="blockedReason" :title="blockedReason" class="min-w-0 truncate text-xs leading-snug text-yellow-200/80">
{{ blockedReason }}
</p>
</div>
<p v-if="blockedReason" class="mt-2 text-xs leading-snug text-yellow-200/80">
{{ blockedReason }}
</p>
<!-- Quick Actions — icon buttons in uniform dark containers -->
<!-- Installing progress — replaces action buttons -->
<div v-if="isInstalling || pkg.state === 'installing'" class="mt-4">
<div v-if="isInstalling || pkg.state === 'installing'" class="mt-auto pt-4">
<div class="flex items-center justify-between mb-1.5">
<span class="text-xs text-white/70 flex items-center gap-1.5">
<svg class="animate-spin h-3 w-3" fill="none" viewBox="0 0 24 24">
@@ -101,7 +101,7 @@
</div>
<!-- Uninstalling progress — truthful stage-driven bar (mirrors install) -->
<div v-else-if="isUninstalling" class="mt-4">
<div v-else-if="isUninstalling" class="mt-auto pt-4">
<div class="flex items-center justify-between mb-1.5">
<span class="text-xs text-white/70 flex items-center gap-1.5">
<svg class="animate-spin h-3 w-3" fill="none" viewBox="0 0 24 24">
@@ -120,7 +120,7 @@
</div>
</div>
<div v-else class="mt-4 flex gap-2">
<div v-else class="mt-auto pt-4 flex gap-2">
<!-- Update available -->
<button
v-if="pkg['available-update'] && pkg.state !== 'updating'"
@@ -167,7 +167,7 @@ describe('appsConfig service filtering', () => {
pkg.health = null
expect(isAppReadyForLaunch(pkg)).toBe(false)
expect(canLaunch(pkg)).toBe(false)
expect(launchBlockedReason(pkg.manifest.id, pkg)).toContain('Starting up')
expect(launchBlockedReason(pkg.manifest.id, pkg)).toBe('Waiting for GitWorkshop…')
pkg.health = 'healthy'
expect(canLaunch(pkg)).toBe(true)
})
@@ -202,7 +202,7 @@ describe('HTTP readiness independent of container health', () => {
pkg.health = 'healthy'
expect(canLaunch(pkg)).toBe(false)
expect(isAppReadyForLaunch(pkg)).toBe(false)
expect(launchBlockedReason(id, pkg)).toContain('Waiting')
expect(launchBlockedReason(id, pkg)).toBe(`Waiting for ${resolveAppTitle(id, pkg)}…`)
pkg['ui-ready'] = true
expect(isAppReadyForLaunch(pkg)).toBe(true)
}
@@ -224,3 +224,13 @@ describe('manifest-generated service names', () => {
expect(resolveAppTitle('unknown-service', makePkg('unknown-service', 'unknown-service', ''))).toBe('unknown-service')
})
})
it('does not display a stale Mempool frontend alias beside its live package', () => {
const main = makePkg('mempool', 'Mempool', 'money')
const alias = makePkg('mempool-web', 'Mempool', 'money')
alias.state = PackageState.Restarting
const entries: Array<[string, PackageDataEntry]> = [['mempool', main], ['mempool-web', alias]]
const shown = [...filterEntriesForTab(entries, 'apps', 'all'), ...filterEntriesForTab(entries, 'services', 'all')]
expect(shown.map(([id]) => id)).toEqual(['mempool'])
expect(filterEntriesForTab([['mempool-web', alias]], 'apps', 'all').map(([id]) => id)).toEqual(['mempool-web'])
})
+9 -2
View File
@@ -111,7 +111,12 @@ export function filterEntriesForTab(
activeTab: AppsTab,
selectedCategory: string,
): Array<[string, PackageDataEntry]> {
const hasMempool = entries.some(([id]) => id === 'mempool')
return entries.filter(([id, pkg]) => {
// Older daemons can retain the frontend manifest alias during a restart.
// Keep one tile while the updated scanner converges; a legacy-only node
// must still be able to see and operate its sole Mempool entry.
if (hasMempool && ['mempool-web', 'mempool-frontend', 'archy-mempool-web'].includes(id)) return false
if (isInternalToolingPackage(id, pkg)) return false
const wantsWebsites = activeTab === 'websites' || activeTab === 'services'
const isWebsite = isWebsitePackage(id, pkg)
@@ -231,6 +236,7 @@ function serviceParentIcon(id: string): string | undefined {
export const DEFAULT_APP_ICON = '/assets/icon/favico-black-v2.svg'
export function resolveAppIcon(id: string, pkg: PackageDataEntry, curatedIcon?: string): string {
if (id === 'angor-indexer' || id === 'angor-relay') return '/assets/img/app-icons/angor-green.png'
const rawIcon = (pkg["static-files"]?.icon || "").trim()
const icon = rawIcon === '/assets/img/favico.png' ? '' : rawIcon
if (
@@ -296,9 +302,10 @@ export function isAppReadyForLaunch(pkg: PackageDataEntry): boolean {
export function launchBlockedReason(id: string, pkg?: PackageDataEntry | null): string {
const appId = pkg?.manifest?.id || id
const title = pkg ? resolveAppTitle(appId, pkg) : id
if (pkg?.['ui-ready'] === false && !isServicePackage(appId, pkg)) {
if (pkg.state === PackageState.Stopped || pkg.state === PackageState.Exited) return 'App is stopped. Start it to open it.'
return 'Waiting for the app to be ready…'
return `Waiting for ${title}…`
}
if (
(appId === 'fedimint' || appId === 'fedimintd') &&
@@ -307,7 +314,7 @@ export function launchBlockedReason(id: string, pkg?: PackageDataEntry | null):
return 'Guardian opens a wait page until Bitcoin finishes initial sync.'
}
if (pkg && pkg.state === PackageState.Running && !isAppReadyForLaunch(pkg)) {
return 'Starting up — Launch will appear when the app is ready.'
return `Waiting for ${title}…`
}
return ''
}