Repository navigation
feat(app): show a sandbox-loading state on Work instead of an empty session list - #299
Merged
Merged
Conversation
quickbeard
force-pushed
the
work-loading-state
branch
from
August 22, 2026 10:47
6d57519 to
6992164
Compare
Work answered a wait that can run for minutes with one static sentence — "Your sandbox is not running yet. It appears here once it is ready." — so a download, an unpack, a boot and a reset all looked identical, and identical to a side that was simply empty. Both halves of the screen now show the loading treatment the rest of the app uses. The sidebar's empty state gets placeholder rows sized to HomeProjectRow under its status line; the session list swaps its "Nothing here yet / create a session" fallback for the same HomeSessionSkeleton it already shows while a server's sessions load. Code is untouched: the wait is undefined there, so upstream's empty state still renders. The status line says what is happening rather than that something is: sandboxCreatePhase (the Add sandbox dialog's own phase logic, reused so the two can never disagree) splits the image download from the unpack behind it, so it counts MB while bytes arrive and then names the job. A destroy keeps its line but loses the placeholders — nothing arrives at the end of it — and the offer to add a sandbox is hidden while one is already being created, since that sandbox is not in the list until it finishes. The derivation moves to a fork-only workspace/empty-state.ts: side.ts imports the tabs context, which pulls in the router and cannot be loaded from a unit test, and this logic is worth pinning down. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Scope back to the one place the wait is misleading: the session list, which offered "create a session to get started" while the sandbox that would run it was still coming up. It now renders the page's own HomeSessionSkeleton with the same common.loading label the branch above it uses, so Work's wait and a Code server's session load are the same element and the same words. The sidebar keeps its original message and gains nothing, so side.ts and both i18n dictionaries are back to what they were — side.ts only routes its starting branch through the shared predicate. What is left of the derivation is workspace/waiting.ts: a boolean, since no caller needs the job detail now. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The state rendered, but nothing in it moved: the label sat alone and the four placeholder rows are a flat fill barely above the background, so a Work side waiting minutes on a sandbox read as a screen that had given up. HomeSessionSkeleton now leads its label with the Spinner this page already imports, and its rows pulse the way the sidebar's own SessionSkeleton does. The spinner is one step brighter than the muted label beside it — at the label's own colour it disappears into the background, which is what the first attempt did. Both changes are in the shared component, so the Code-side loading state this was asked to match keeps matching it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
quickbeard
force-pushed
the
work-loading-state
branch
from
August 22, 2026 11:11
6992164 to
574d56c
Compare
Three changes to the waiting state, all Work-only: - The search box is hidden while the sandbox comes up. It sat above an empty pane over sessions that do not exist yet, so it could only ever return nothing. - The wait gets its own view instead of borrowing HomeSessionSkeleton: a size-8 spinner over its label, centred in the pane. The skeleton stands in for rows about to arrive, which is true of a server's session load and false here, where nothing arrives for minutes — and the centred view leaves nothing else on screen to read as available. - The label is "Loading sandbox..." (new workspace.loading.sandbox key), not the bare "Loading", which said nothing about what was being waited on. HomeSessionSkeleton goes back to upstream: the spinner and pulse added to it last commit were serving the Work case, and Work no longer uses it. The centred view carries its wrapper's own container-query height rather than min-h-full — the wrapper has a min-height and an auto height, so a percentage resolves against the content and centres nothing. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.
What
While a sandbox is coming up, the Work session pane showed a search box over "Nothing here yet / Create a session to get started" — the only action offered could not work, since the server that would run the session did not exist yet, and the search had nothing to search.
It now shows a spinner over "Loading sandbox…", centred in the pane, with the search box hidden, until the sandbox is ready.
The sidebar is deliberately untouched — it keeps saying "Your sandbox is not running yet. It appears here once it is ready."
How
packages/app/src/pages/home.tsx+ a new i18n key (upstream files, one row inDIVERGENCES.md):NewHomegainsuseWorkspaceSide()+useSandboxes()and awaitingForSandbox()memo, which drives two independentShows: the session list's empty fallback, and whetherHomeSessionSearchrenders at all.HomeSandboxLoadingis a fork-only view —Spinneratsize-8over theworkspace.loading.sandboxlabel, centred. It is notHomeSessionSkeleton: that skeleton stands in for rows about to arrive, which is true of a server's session load and false here, where nothing arrives for minutes.HomeSessionSkeletonand the Code-side branch that uses it are untouched.min-h-[calc(100cqh-72px)] lg:min-h-[calc(100cqh-96px)]rather thanmin-h-full— the wrapper has a min-height and an auto height, so a percentage resolves against the content and centres nothing. (Found by rendering it, not by reading it.)The predicate lives in a new fork-only
packages/app/src/workspace/waiting.ts— a boolean over the sandbox list: any sandbox still booting, or any job in flight (a sandbox mid-create is not in the list yet, and one mid-reset reads "stopped" the whole time, so the job list is the only thing that says either wait is happening).side.tsroutes its own "starting" branch through the same predicate so the sidebar and the session pane cannot drift apart; its behaviour is unchanged.waiting.test.tscovers it: boots, jobs whatever the sandboxes say, and the three states that are not waits — Code, stopped, failed. It is its own module becauseside.tsimports the tabs context, which pulls@solidjs/routerinto any importer and cannot be loaded from a unit test.Verification
bun run test:unitinpackages/app: 695 pass / 0 fail (5 new).bun turbo typecheck: 33/33.bun run build: clean.bun run lint: byte-identical totals with and without the change.min-h-fullbug and, before it, a spinner so dim it was invisible.Rebased onto current
main.