feat(wallet): app icons, mobile layout, and a funding mode on the Lightning modal
Demo images / Build & push demo images (push) Successful in 3m18s

Icons: each node choice now shows its app icon (lnd.png; Core Lightning's is
vendored from the Umbrel gallery as core-lightning.svg). Vendored rather than
hotlinked on purpose — these nodes run offline/airgapped, and a remote image
would both break there and leak a request to a third-party host on every
render. A missing asset falls back to a neutral bolt glyph so a row can never
render a broken-image box.

Mobile: the choice row keeps icon + name + blurb together and drops the action
to its own full-width line under 26rem, instead of squeezing the description
into a two-word column next to a button.

Funding mode: a node that is running but has no funds / no inbound liquidity
is neither "install one" nor "start it", so the same modal gains a third mode
that explains it and routes to the run-lightning-node goal, where funding and
channel-opening already live — reusing that flow rather than duplicating it.

It fires where the user actually meets the problem: on a failed attempt.
handleLightningFailure() maps a running node's send/receive failure onto the
funding modal, matched on message text because LND surfaces "no route", "no
channels" and "insufficient balance" as plain strings with no distinct code —
and all three mean the same thing to a user: fund me.

Verified: 5 gate tests; npm run build clean.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
archipelago
2026-08-02 07:43:24 -04:00
co-authored by Claude Opus 5
parent fa26c5fc56
commit 700947ea3c
5 changed files with 199 additions and 9 deletions
@@ -0,0 +1,56 @@
<svg width="256" height="256" viewBox="0 0 256 256" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_670_3179)">
<path d="M255.871 0.125H0.128906V255.868H255.871V0.125Z" fill="url(#paint0_linear_670_3179)"/>
<g opacity="0.2" filter="url(#filter0_f_670_3179)">
<circle cx="128" cy="-68.0615" r="82.7188" fill="#F0D003"/>
</g>
<g opacity="0.2" filter="url(#filter1_f_670_3179)">
<circle cx="128" cy="323.065" r="82.7188" fill="black"/>
</g>
<g filter="url(#filter2_d_670_3179)">
<path d="M158.533 37L88.7227 130.934L168.984 110.063L120.9 110.096L158.533 37Z" fill="#F0D003"/>
</g>
<g filter="url(#filter3_d_670_3179)">
<path d="M98.4517 218.004L168.228 124.07L88 144.941L136.084 144.908L98.4517 218.004Z" fill="#FFFAE6"/>
</g>
</g>
<defs>
<filter id="filter0_f_670_3179" x="-52.7188" y="-248.78" width="361.438" height="361.438" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="49" result="effect1_foregroundBlur_670_3179"/>
</filter>
<filter id="filter1_f_670_3179" x="-52.7188" y="142.347" width="361.438" height="361.438" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="49" result="effect1_foregroundBlur_670_3179"/>
</filter>
<filter id="filter2_d_670_3179" x="69.7227" y="20" width="112.262" height="125.934" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dx="-3" dy="-1"/>
<feGaussianBlur stdDeviation="8"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.941176 0 0 0 0 0.815686 0 0 0 0 0.0117647 0 0 0 0.16 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_670_3179"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_670_3179" result="shape"/>
</filter>
<filter id="filter3_d_670_3179" x="71" y="111.07" width="114.229" height="127.934" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="4"/>
<feGaussianBlur stdDeviation="8.5"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 1 0 0 0 0 0.980392 0 0 0 0 0.901961 0 0 0 0.14 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_670_3179"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_670_3179" result="shape"/>
</filter>
<linearGradient id="paint0_linear_670_3179" x1="128" y1="0.125" x2="128" y2="255.868" gradientUnits="userSpaceOnUse">
<stop stop-color="#161616"/>
<stop offset="1"/>
</linearGradient>
<clipPath id="clip0_670_3179">
<rect width="256" height="256" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 3.2 KiB