Desktop UI: functional & reliability fixes (look preserved) - #808
Closed
cowboycoderhq wants to merge 20 commits into
Closed
Desktop UI: functional & reliability fixes (look preserved)#808cowboycoderhq wants to merge 20 commits into
cowboycoderhq wants to merge 20 commits into
Conversation
Grafts the fork's main-process changes onto dev (no renderer/aesthetic changes): - Register send-mor/send-eth IPC (were never bound -> sends hung to the 750s timeout); postSend throws on failure instead of swallowing it into a false success; get-proxy-router-derived-config handler. - react-query guards: getAllModels/getTransactions return [] on !ok. - Orchestrator hardening: generation-race guard (no more 'service dies minutes later'), install-integrity + orphan-reaping, ipfs dynamic port, graceful before-quit, non-fatal IPFS/container, always-rewrite env, models-config merge, stale-model cleanup. - downloader completeness-check; unzipper executable-bit preservation (llama-server was extracted non-executable -> permission denied forever). - Router endpoint pre-resolve + foreign-router relocation; pid-reaper; secret redaction; resetWallet reorder (fixes bricked half-state); router-wallet self-heal; onboarding-completed timeout headroom. typecheck:node passes. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… UI) Additive helpers the functional deltas depend on, grafted ahead of the components that consume them: - utils/coinValue: formatMor (nullable, magnitude-scaled) so tiny real stakes stop rendering as 0.00. - utils/marketplace (new): precision-safe morToWei/weiToMor + live Diamond param reads over eth_call (no ethers dep). - chat/utils: formatModelName (display prettify) + modelMatchesQuery (token search). - store/queries: buildModelsWithBids (merge registry with active bids via an injected provider-walking fetcher) + localModels query key. typecheck passes; no consumer wired yet (safe additive exports). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The send path was broken on several axes; this grafts the fork's fixes onto
dev's existing Dashboard/SendForm (which already hand mor/eth down to the modal):
- client: rename the dead sendLmr -> sendMor so the renderer emits the
send-mor channel the main-process now handles (pairs with the IPC
registration); raise the onboarding-completed IPC timeout; add
get-proxy-router-derived-config.
- tx-modal state: send { to, amount: toWei(sanitize(amount)) } (router reads
amount in wei; the old payload sent "value" so amount arrived undefined and
the router rejected it); drop the Lumerin-era gas state that made validate()
fail every send; price USD off mor.rate not the legacy LMR rate; read the
balance from the same mor/eth props the form renders; add insufficient-funds
and zero-address guards.
typecheck passes; payload contract verified end-to-end (renderer -> send-mor
IPC -> postSend -> router). Two-step confirm UI lands next.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ontent Applied onto dev's original modal, dev's look preserved: - Destination address was read as e.targetValue (a nonexistent property) while SendForm passes the raw string value, so the shown destination was always undefined. Take the value directly. - Render the modal via createPortal(document.body) so it escapes parent stacking/overflow contexts. - Body: fixed height 500px -> min-height 500px + max-height 88vh + scroll, so a taller body (the upcoming confirm panel) can't push the action button off screen. typecheck passes.
Graft the fork's confirm flow onto dev's SendForm (dev's look preserved): - First press validates and shows a confirmation panel (amount + full destination address + an irreversible-transfer warning); only the second press broadcasts. - Editing the amount or address resets the confirmation so a stale review can never send an edited value. - Treat a missing tx hash from onSubmit as failure instead of showing success. Money-surface hardening beyond the fork (adversarial reviewer pass): - Confirm panel shows props.toAddress (the exact whitespace-stripped bytes that get broadcast), not the raw display copy. - if (isPending) return re-entrancy guard. - Reset confirming on a failed send so an ambiguous failure can't be one-click re-broadcast into a double-spend. react-select menu restyle intentionally deferred to the reskin PR. typecheck passes; reviewer verdict SENDFORM-SAFE.
dev's wallet had no way to OPEN the send modal (only Receive + Staking tiles;
onTabSwitch('send') was never called), so the send feature was unreachable.
Add a Send ActionTile (matches dev's existing tile style) that opens the modal,
and invalidate the balances + transactions queries on a completed transfer
(now, +4s, +12s) so the wallet reflects the send once the router indexes it.
typecheck + build pass; confirm panel verified visually via an isolated render.
Graft the models-screen deltas onto dev's components (dev's look preserved): - Models: a search box that client-side filters the registry by name/tag (modelMatchesQuery) with a live visible/total count. - ModelsTable + PinnedFilesTable: show a Secure pill for TEE models (isSecureModel), hide the raw 'tee' tag from the tag list, and render the model name through formatModelName. - ModelsTable: the download-progress overlay stops click propagation so clicking it no longer selects the card. - PinnedFilesTable: render ModelCard as a JSX element (<ModelCard/>) instead of calling it as a function, so hooks/reconciliation behave correctly. typecheck + build pass; verified visually (Secure badge, filtered tags, formatted names) via an isolated render.
Both wallet-reset paths were a single click that called logout({}) and erased
the device wallet with no confirmation:
- Login 'Or setup new wallet' now reveals a warning + an explicit 'Erase and
set up new' / 'Keep my wallet' choice; logout only fires from the second
deliberate press.
- Settings 'Reset' now expands an inline warning + 'Erase wallet' / 'Cancel'
before logout runs.
Verified: driving the Login gate, the first press fires logout zero times and
reveals the confirm; cancel returns to the initial state. dev's look preserved
(danger styled with the theme's red).
- common/View: the page shell clips overflow and View was a fixed 100vh, so tall screens (Settings, Models) were cut off at the fold; add overflow-y auto. - contracts modal: render via createPortal(document.body) so it escapes the shell's stacking context instead of painting under the sidebar.
- SuccessForm: the post-send amount field is read-only (was an editable input on a display-only screen). - Empty transaction list shows an 'Add funds' button that opens Receive (NoTxPlaceholder gains an onReceiveClick, threaded through TxList). - Dashboard opens a modal from router navigation state (location.state.openModal) so another screen can deep-link straight to Receive.
- withProvidersState.claimFunds referenced bare 'props' (a ReferenceError in a class method) so every provider Claim threw and was swallowed by the catch — the button silently did nothing. Use this.props like every sibling method. - Agents: show a short placeholder in each section (access/allowance/all) when it's empty instead of a blank gap. Deferred (not in this PR): the ProvidersList accordion->collapse rewrite (low value, replaces working markup) and the Agents tx-modal loading/error branches (inert — the state HOC only emits pending/success).
…nderer
- Help now opens the project documentation (nodedocs.mor.org) instead of an
unverified support-chat invite; removed the SUPPORT_URL constant entirely.
- Router Main gets isolation:isolate so a screen's overlay can't stack above the
sidebar rail in the narrow overlay layout.
- Settings config read moves from a renderer fetch to the main process
(getProxyRouterDerivedConfig), so a transient 500 during router boot no longer
paints an error into devtools; returns {} (an object) on failure, not [].
Deferred: the models+bids prefetch (depends on the chat HOC, lands with the chat
slice) and the Settings react-bootstrap->custom Tabs swap (rewrite, low value).
The onboarding import field accepts a private key OR a recovery phrase, but the completion handler always sent a non-empty value to the hex-only privateKey endpoint, so a pasted 12/24-word phrase was rejected. Route by content: if the trimmed value contains whitespace it's a phrase -> mnemonic endpoint (sanitized), otherwise a private key. Deferred (reskinned step UIs -> PR3): two-level-back, password-meter tone, terms copy. NOT shipped without maintainer opt-in (security posture): the tap-to-verify mnemonic step (weakens backup verification vs retyping) and the clipboard/print seed export (new plaintext-seed exposure channels).
On-chain money/session fixes grafted onto dev's Chat (reviewer-verified, unit trace settled to wei against the router structs + Solidity): - Staked MOR reads activeSession.Stake (via formatMor), not the (EndsAt-OpenedAt)*PricePerSecond cost formula that was ~321x too small and showed real stakes as 0.00; same fix for the min/max stake-requirement copy. - MIN_REQUEST_SECONDS=360 floors the requested duration so it clears the contract's MIN_SESSION_DURATION (300s) after integer truncation, instead of reverting with SessionTooShort(). - Affordability guards: a session only reads affordable once the requirement is actually known (min>0), with >= so an exact balance qualifies; direct-pay is priced off the dearest bid * MIN_REQUEST_SECONDS, not the priceless formula that was trivially true for any balance. - Marketplace bids fetched by walking providers (buildModelsWithBids) instead of one call per model; model names shown via formatModelName; token-based model search (multi-word, any order). reviewer verdict CHAT-MONEY-SOUND, no must-fix. typecheck passes.
- Remove ui-desktop/verify/shots/*.png (review-harness output that slipped in via git add -A; not product assets). - Reword the dev-only CSP-warning-suppression comment so it doesn't spell out the hardening gap in plain sight; the follow-up intent is unchanged.
Ship the full prior transcript with each prompt so the model has conversation memory. dev sent only the new turn (the router was meant to prepend history but a type assertion silently dropped it), so every message was answered in isolation. The router's own prepend is disabled (PROXY_FORWARD_CHAT_CONTEXT = false, in the main-process config) so the two can't double up. Verified live: turn 1 sends [user], turn 2 sends [user, assistant, user], and the model correctly recalls earlier turns.
…ank page) The 'config-proxy-router' step (after the phrase is verified, while the wallet provisions) had no case in the onboarding switch and fell through to 'return null', leaving a blank white page. Render a loading screen instead.
Make reopening a saved session work correctly and never strand the user:
- Reopen no longer crashes: onOpenSession falls back to the chat's model when
selectedModel is unset (a saved chat opened directly), guards a missing/
bid-less model with a toast, and selectChat's bid lookup is null-safe.
- handleReopen only clears read-only if the session actually opened.
- Pre-check that the wallet can cover the stake BEFORE the on-chain open, so an
unaffordable attempt shows a clear 'Not enough MOR' toast instead of reverting
('transfer amount exceeds balance') and leaving a dead session. Skips itself
when the stake can't be priced yet (meta unloaded) so it can't false-block.
- Recompute requiredStake whenever the model + marketplace meta are known, so a
reopen gets the real requirement instead of the {0} default that forced a 24h
duration (also fixes the 'needs at least 0.00 MOR' display).
- When neither staking nor direct pay is affordable, show a 'You'll need some
MOR' screen with a Receive action instead of two dead greyed-out buttons.
reviewer-verified (money guard GUARD-SOUND, reopen REOPEN-SOUND); live-tested.
- Add a step-back control to onboarding: AltLayout renders a back arrow when given onBack, and withOnboardingState reverses only the flag that produced the current step (entered secrets are preserved; no wallet is re-generated). - ImportFlow gets a two-level back: from account selection it returns to phrase entry (clearing the derived accounts), from phrase entry it exits the import. - The first step (Terms) has no back, correctly. - Also: fix the 'derivied'->'derived' typo, make the import-mode <select> controlled (value instead of selected on option), and add the missing 'import-flow'/'set-custom-eth' to Onboarding's currentStep propTypes.
app.relaunch() reloads the packaged renderer path, which electron-vite dev serves from memory, so a dev-mode reset showed a blank screen. Gate it: dev reloads the window (the wallet is already cleared, so it re-initialises to onboarding); packaged builds keep the full relaunch. Dev-experience only, no production behaviour change.
Author
|
Closing briefly to bundle the verification/test artifacts with the change — will reopen shortly. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Desktop UI: functional & reliability fixes (look preserved)
This is PR 2 of 3 in the desktop contribution series:
Every commit passes
typecheck+electron-vite build. No visual/theme changes — new UI reuses the current palette. Base:dev. ~51 files, self-contained per commit.What's fixed
Send / wallet (money surface)
send-mor/send-ethIPC handlers were never registered, so a transfer hung until the 750s timeout. The payload now sends{to, amount}in wei (the old shape sentvalue, so the router receivedamount: undefinedand rejected it), the handler throws on failure instead of swallowing it into a false "success", and the amount USD is priced off the MOR rate rather than the legacy LMR rate.e.targetValue(alwaysundefined) instead of the value.Chat
Stakeinstead of a cost formula that was ~321× too small and displayed real stakes as0.00.MIN_SESSION_DURATION(300s) after integer truncation, instead of reverting withSessionTooShort()..bidsoff an unset model), an unaffordable reopen shows a clear message instead of letting the chain revert, and when neither staking nor direct pay is affordable a "You'll need some MOR" screen with a Receive action replaces two dead greyed-out buttons.Wallet safety
propsvsthis.propsreference error swallowed by a catch); fixed.Onboarding
<select>, the completecurrentSteppropTypes, and a "derivied" → "derived" typo.Reliability (main process)
Small fixes
createPortalso they don't paint under the sidebar; Help opens the documentation.Notes for review
Money-surface and session-pricing logic was independently reviewed and unit-traced (all quantities in wei) against the router structs and the Solidity
SessionRouter/SessionStorage.