Compare commits

..

No commits in common. "6fd1cf9ba7e2376a0bd3e8087c11512c30ef2e9a" and "b11c6c17d13f63d10b9adb793e7c0684e580adcd" have entirely different histories.

12 changed files with 84 additions and 101 deletions

View File

@ -1,11 +1,5 @@
# Changelog
## v1.7.87-alpha (2026-06-12)
- Bitcoin receive now calls LND's on-chain address endpoint with the correct REST method, and backend failures keep the specific address-generation error instead of collapsing into the generic operation-failed message.
- App launch credential interstitials now render as true full-screen overlays, and the launcher loading indicator uses the neutral brand palette instead of a blue spinner.
- Validation passed with `git diff --check`, `npm run type-check`, and the focused frontend tests for `bitcoinReceive` and `AppIconGrid`.
## v1.7.86-alpha (2026-06-12)
- Fleet now preserves the last known node list, alerts, and selection locally while telemetry refreshes in the background, so the dashboard no longer blanks on tab switches or update scans.

View File

@ -1,6 +1,6 @@
[package]
name = "archipelago"
version = "1.7.87-alpha"
version = "1.7.86-alpha"
edition = "2021"
description = "Archipelago Bitcoin Node OS - Native backend"
authors = ["Archipelago Team"]

View File

@ -12,28 +12,23 @@ impl RpcHandler {
let (client, macaroon_hex) = self.lnd_client().await?;
let resp = client
.post(format!("{LND_REST_BASE_URL}/v1/newaddress"))
.get(format!("{LND_REST_BASE_URL}/v1/newaddress"))
.query(&[("type", "WITNESS_PUBKEY_HASH")])
.header("Grpc-Metadata-macaroon", &macaroon_hex)
.json(&serde_json::json!({ "type": "WITNESS_PUBKEY_HASH" }))
.send()
.await
.context("LND REST connection failed")?;
let status = resp.status();
let raw_body = resp
.text()
let body: serde_json::Value = resp
.json()
.await
.context("LND address response could not be read")?;
let body: serde_json::Value = serde_json::from_str(&raw_body).unwrap_or_else(|_| {
serde_json::json!({
"raw": raw_body,
})
});
.context("Failed to parse newaddress response")?;
if !status.is_success() {
let message = lnd_error_message(&body);
anyhow::bail!(
"Bitcoin address generation failed ({}): {}",
"LND could not generate a Bitcoin address ({}): {}",
status,
message
);
@ -44,14 +39,14 @@ impl RpcHandler {
.or_else(|| body.get("message"))
.and_then(|v| v.as_str())
{
anyhow::bail!("Bitcoin address generation failed: {}", error);
anyhow::bail!("LND could not generate a Bitcoin address: {}", error);
}
let address = body
.get("address")
.and_then(|v| v.as_str())
.filter(|addr| !addr.trim().is_empty())
.ok_or_else(|| anyhow::anyhow!("Bitcoin address generation failed: LND did not return a Bitcoin address. The wallet may still be locked, uninitialized, or waiting for Bitcoin to sync."))?
.ok_or_else(|| anyhow::anyhow!("LND did not return a Bitcoin address. The wallet may still be locked, uninitialized, or waiting for Bitcoin to sync."))?
.to_string();
Ok(serde_json::json!({ "address": address }))

View File

@ -63,7 +63,6 @@ pub(super) fn sanitize_error_message(msg: &str) -> String {
"Failed to start",
"Container",
"Image",
"Bitcoin address",
];
for prefix in &user_facing_prefixes {
if msg.starts_with(prefix) {

View File

@ -1,12 +1,12 @@
{
"name": "neode-ui",
"version": "1.7.87-alpha",
"version": "1.7.86-alpha",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "neode-ui",
"version": "1.7.87-alpha",
"version": "1.7.86-alpha",
"dependencies": {
"@types/dompurify": "^3.0.5",
"@vue-leaflet/vue-leaflet": "^0.10.1",

View File

@ -1,7 +1,7 @@
{
"name": "neode-ui",
"private": true,
"version": "1.7.87-alpha",
"version": "1.7.86-alpha",
"type": "module",
"scripts": {
"start": "./start-dev.sh",

View File

@ -3,17 +3,16 @@
<Transition name="app-launcher">
<div
v-if="store.isOpen"
class="fixed inset-0 z-[2400] flex items-stretch justify-stretch p-0"
class="fixed inset-0 z-[2400] flex items-center justify-center p-0 md:p-10"
@click.self="store.close()"
>
<!-- Backdrop - blur like spotlight -->
<div class="app-launcher-backdrop absolute inset-0 bg-black/75 backdrop-blur-md"></div>
<div class="app-launcher-backdrop absolute inset-0 bg-black/60 backdrop-blur-md"></div>
<!-- Panel - full-screen overlay -->
<!-- Panel - inset with margins, glass style like spotlight -->
<div
class="app-launcher-panel relative z-10 flex flex-col overflow-hidden rounded-none shadow-2xl"
class="app-launcher-panel relative z-10 flex flex-col overflow-hidden rounded-none md:rounded-2xl shadow-2xl"
:class="panelClasses"
style="border-radius: 0;"
>
<!-- Header bar - sticky on mobile -->
<div class="sticky top-0 z-10 flex items-center gap-3 border-b border-white/10 px-4 py-3 bg-black/60 backdrop-blur-md md:bg-transparent md:backdrop-blur-none">
@ -70,7 +69,7 @@
<!-- Loading indicator -->
<Transition name="content-fade">
<div v-if="iframeLoading" class="absolute inset-0 z-10 flex items-center justify-center bg-black/40">
<svg class="animate-spin h-8 w-8 text-white/70" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<svg class="animate-spin h-8 w-8 text-blue-400" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
@ -399,7 +398,7 @@ function injectScrollbarHideIfSameOrigin() {
const panelClasses = [
'glass-card',
'w-full h-full',
'max-w-none max-h-none',
'md:max-w-[calc(100vw-5rem)] md:max-h-[calc(100vh-5rem)]',
]
function onKeyDown(e: KeyboardEvent) {

View File

@ -17,8 +17,4 @@ describe('explainReceiveAddressFailure', () => {
it('explains lnd transport failures', () => {
expect(explainReceiveAddressFailure(new Error('LND REST connection failed'))).toContain('not responding cleanly')
})
it('keeps bitcoin address generation failures visible', () => {
expect(explainReceiveAddressFailure(new Error('Bitcoin address generation failed: LND is not ready'))).toContain('Bitcoin address generation failed')
})
})

View File

@ -244,10 +244,10 @@
<Transition name="fade">
<div
v-if="credentialModal.show"
class="credential-modal-overlay fixed inset-0 z-[2700] flex items-stretch justify-stretch bg-black/80 backdrop-blur-md p-0"
class="credential-modal-overlay fixed inset-0 z-[2700] flex items-center justify-center bg-black/60 backdrop-blur-md p-4 md:p-6"
@click.self="closeCredentialModal"
>
<div class="credential-modal-panel">
<div class="sideload-modal credential-modal">
<div class="flex items-start justify-between gap-4 mb-5">
<div>
<h2 class="text-lg font-semibold text-white">{{ credentialModal.title }}</h2>
@ -259,7 +259,7 @@
<div v-for="cred in credentialModal.credentials" :key="cred.label" class="rounded-lg border border-white/10 bg-white/[0.04] p-3">
<div class="flex items-center justify-between gap-3 mb-1">
<span class="text-white/60 text-xs uppercase tracking-wide">{{ cred.label }}</span>
<button type="button" class="text-xs text-orange-300 hover:text-orange-200" @click="copyModalCredential(cred.label, cred.value)">{{ credentialModal.copied === cred.label ? 'Copied' : 'Copy' }}</button>
<button type="button" class="text-xs text-blue-300 hover:text-blue-200" @click="copyModalCredential(cred.label, cred.value)">{{ credentialModal.copied === cred.label ? 'Copied' : 'Copy' }}</button>
</div>
<p class="font-mono text-sm text-white break-all">{{ cred.value }}</p>
</div>
@ -802,24 +802,15 @@ async function submitSideload() {
}
.sideload-input::placeholder { color: rgba(255, 255, 255, 0.38); }
.sideload-input:focus { border-color: rgba(255, 255, 255, 0.38); }
.credential-modal-panel {
.credential-modal {
display: flex;
flex-direction: column;
width: 100%;
height: 100%;
min-height: 0;
max-width: none;
max-height: none;
overflow: hidden;
border: 0;
border-radius: 0;
background: rgba(8, 10, 18, 0.98);
padding: 1.25rem;
padding-bottom: calc(1.25rem + var(--safe-area-bottom, env(safe-area-inset-bottom, 0px)));
box-shadow: none;
max-height: calc(100dvh - var(--safe-area-top, env(safe-area-inset-top, 0px)) - var(--safe-area-bottom, env(safe-area-inset-bottom, 0px)) - 2rem);
border-radius: 1.25rem;
padding-bottom: 1.25rem;
box-shadow: 0 25px 80px rgba(0, 0, 0, 0.55);
}
.credential-modal-body {
flex: 1 1 auto;
min-height: 0;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
@ -827,4 +818,11 @@ async function submitSideload() {
.credential-modal-actions {
flex-shrink: 0;
}
@media (min-width: 768px) {
.sideload-modal {
border-radius: 1.25rem;
padding: 1.5rem;
box-shadow: 0 25px 80px rgba(0, 0, 0, 0.55);
}
}
</style>

View File

@ -85,8 +85,8 @@
</div>
<Transition name="fade">
<div v-if="credentialModal.show" class="credential-modal-overlay fixed inset-0 z-[2700] flex items-stretch justify-stretch bg-black/80 backdrop-blur-md p-0" @click.self="closeCredentialModal">
<div class="credential-modal-panel">
<div v-if="credentialModal.show" class="credential-modal-overlay fixed inset-0 z-[2700] flex items-center justify-center bg-black/60 backdrop-blur-md p-4 md:p-6" @click.self="closeCredentialModal">
<div class="sideload-modal credential-modal">
<div class="flex items-start justify-between gap-4 mb-5">
<div>
<h2 class="text-lg font-semibold text-white">{{ credentialModal.title }}</h2>
@ -98,7 +98,7 @@
<div v-for="cred in credentialModal.credentials" :key="cred.label" class="rounded-lg border border-white/10 bg-white/[0.04] p-3">
<div class="flex items-center justify-between gap-3 mb-1">
<span class="text-white/60 text-xs uppercase tracking-wide">{{ cred.label }}</span>
<button type="button" class="text-xs text-orange-300 hover:text-orange-200" @click="copyModalCredential(cred.label, cred.value)">{{ credentialModal.copied === cred.label ? 'Copied' : 'Copy' }}</button>
<button type="button" class="text-xs text-blue-300 hover:text-blue-200" @click="copyModalCredential(cred.label, cred.value)">{{ credentialModal.copied === cred.label ? 'Copied' : 'Copy' }}</button>
</div>
<p class="font-mono text-sm text-white break-all">{{ cred.value }}</p>
</div>
@ -328,28 +328,24 @@ function scrollToPage(index: number) {
background: rgba(255, 255, 255, 0.06);
}
.credential-modal-body {
flex: 1 1 auto;
min-height: 0;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
}
.credential-modal-panel {
display: flex;
flex-direction: column;
width: 100%;
height: 100%;
min-height: 0;
max-width: none;
max-height: none;
overflow: hidden;
border: 0;
border-radius: 0;
background: rgba(8, 10, 18, 0.98);
padding: 1.25rem;
padding-bottom: calc(1.25rem + var(--safe-area-bottom, env(safe-area-inset-bottom, 0px)));
box-shadow: none;
.credential-modal {
max-height: calc(100dvh - var(--safe-area-top, env(safe-area-inset-top, 0px)) - var(--safe-area-bottom, env(safe-area-inset-bottom, 0px)) - 2rem);
border-radius: 1.25rem;
padding-bottom: 1.25rem;
box-shadow: 0 25px 80px rgba(0, 0, 0, 0.55);
}
.credential-modal-actions {
flex-shrink: 0;
}
@media (min-width: 768px) {
.sideload-modal {
border-radius: 1.25rem;
padding: 1.5rem;
box-shadow: 0 25px 80px rgba(0, 0, 0, 0.55);
}
}
</style>

View File

@ -1,27 +1,30 @@
{
"version": "1.7.87-alpha",
"version": "1.7.86-alpha",
"release_date": "2026-06-12",
"changelog": [
"Bitcoin receive now calls LND's on-chain address endpoint with the correct REST method, and backend failures keep the specific address-generation error instead of collapsing into the generic operation-failed message.",
"App launch credential interstitials now render as true full-screen overlays, and the launcher loading indicator uses the neutral brand palette instead of a blue spinner.",
"Validation passed with `git diff --check`, `npm run type-check`, and the focused frontend tests for `bitcoinReceive` and `AppIconGrid`."
"Fleet now preserves the last known node list, alerts, and selection locally while telemetry refreshes in the background, so the dashboard no longer blanks on tab switches or update scans.",
"Connected nodes and identities now reuse their last loaded data instead of reloading the visible list every time the user revisits the tab.",
"The Fleet matrix and detail views now show actual node names and host information instead of raw node id prefixes.",
"The network map only redraws when its graph data actually changes, which stops the D3 scene from visually resetting on every refresh tick.",
"Mobile federation and system-update actions now stack full width, and the ElectrumX app health check allows a long startup window so slow sync nodes do not restart mid-index.",
"Validation passed with `git diff --check`, focused frontend tests, and `npm run type-check`."
],
"components": [
{
"name": "archipelago",
"current_version": "1.7.87-alpha",
"new_version": "1.7.87-alpha",
"download_url": "http://146.59.87.168:3000/lfg2025/archy/releases/download/v1.7.87-alpha/archipelago",
"sha256": "a87bf3245e3ae67baf8c7018e89794e8bd43b18ee3858dda65364562f2927045",
"size_bytes": 44058416
"current_version": "1.7.86-alpha",
"new_version": "1.7.86-alpha",
"download_url": "http://146.59.87.168:3000/lfg2025/archy/releases/download/v1.7.86-alpha/archipelago",
"sha256": "2eb936fe188df25947a2d626af1b22be2f7ef9dcbc927542389de3b38e80f9e6",
"size_bytes": 44050232
},
{
"name": "archipelago-frontend-1.7.87-alpha.tar.gz",
"current_version": "1.7.87-alpha",
"new_version": "1.7.87-alpha",
"download_url": "http://146.59.87.168:3000/lfg2025/archy/releases/download/v1.7.87-alpha/archipelago-frontend-1.7.87-alpha.tar.gz",
"sha256": "df0834a4e9c45e608de2fa5f0031f939d49eec0b6b8d1fd9a422676ae96b3dbe",
"size_bytes": 184059938
"name": "archipelago-frontend-1.7.86-alpha.tar.gz",
"current_version": "1.7.86-alpha",
"new_version": "1.7.86-alpha",
"download_url": "http://146.59.87.168:3000/lfg2025/archy/releases/download/v1.7.86-alpha/archipelago-frontend-1.7.86-alpha.tar.gz",
"sha256": "9f6f146eaf709cd3e778550bb7a56877dce551cb6d631bccf08e80ed977dd17b",
"size_bytes": 184060614
}
]
}

View File

@ -1,27 +1,30 @@
{
"version": "1.7.87-alpha",
"version": "1.7.86-alpha",
"release_date": "2026-06-12",
"changelog": [
"Bitcoin receive now calls LND's on-chain address endpoint with the correct REST method, and backend failures keep the specific address-generation error instead of collapsing into the generic operation-failed message.",
"App launch credential interstitials now render as true full-screen overlays, and the launcher loading indicator uses the neutral brand palette instead of a blue spinner.",
"Validation passed with `git diff --check`, `npm run type-check`, and the focused frontend tests for `bitcoinReceive` and `AppIconGrid`."
"Fleet now preserves the last known node list, alerts, and selection locally while telemetry refreshes in the background, so the dashboard no longer blanks on tab switches or update scans.",
"Connected nodes and identities now reuse their last loaded data instead of reloading the visible list every time the user revisits the tab.",
"The Fleet matrix and detail views now show actual node names and host information instead of raw node id prefixes.",
"The network map only redraws when its graph data actually changes, which stops the D3 scene from visually resetting on every refresh tick.",
"Mobile federation and system-update actions now stack full width, and the ElectrumX app health check allows a long startup window so slow sync nodes do not restart mid-index.",
"Validation passed with `git diff --check`, focused frontend tests, and `npm run type-check`."
],
"components": [
{
"name": "archipelago",
"current_version": "1.7.87-alpha",
"new_version": "1.7.87-alpha",
"download_url": "http://146.59.87.168:3000/lfg2025/archy/releases/download/v1.7.87-alpha/archipelago",
"sha256": "a87bf3245e3ae67baf8c7018e89794e8bd43b18ee3858dda65364562f2927045",
"size_bytes": 44058416
"current_version": "1.7.86-alpha",
"new_version": "1.7.86-alpha",
"download_url": "http://146.59.87.168:3000/lfg2025/archy/releases/download/v1.7.86-alpha/archipelago",
"sha256": "2eb936fe188df25947a2d626af1b22be2f7ef9dcbc927542389de3b38e80f9e6",
"size_bytes": 44050232
},
{
"name": "archipelago-frontend-1.7.87-alpha.tar.gz",
"current_version": "1.7.87-alpha",
"new_version": "1.7.87-alpha",
"download_url": "http://146.59.87.168:3000/lfg2025/archy/releases/download/v1.7.87-alpha/archipelago-frontend-1.7.87-alpha.tar.gz",
"sha256": "df0834a4e9c45e608de2fa5f0031f939d49eec0b6b8d1fd9a422676ae96b3dbe",
"size_bytes": 184059938
"name": "archipelago-frontend-1.7.86-alpha.tar.gz",
"current_version": "1.7.86-alpha",
"new_version": "1.7.86-alpha",
"download_url": "http://146.59.87.168:3000/lfg2025/archy/releases/download/v1.7.86-alpha/archipelago-frontend-1.7.86-alpha.tar.gz",
"sha256": "9f6f146eaf709cd3e778550bb7a56877dce551cb6d631bccf08e80ed977dd17b",
"size_bytes": 184060614
}
]
}