Skip to content

Commit a4c4538

Browse files
committed
refactor(playground): clarify landing route states
1 parent f05ae07 commit a4c4538

2 files changed

Lines changed: 73 additions & 68 deletions

File tree

apps/web/src/components/playground/PlaygroundLanding.tsx

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { Show } from 'solid-js'
12
import CodeBlock from '../CodeBlock'
23
import Badge from '../ui/Badge'
34
import SurfaceCard from '../ui/SurfaceCard'
@@ -51,8 +52,8 @@ export default function PlaygroundLanding(props: PlaygroundLandingProps) {
5152
, then keep iterating with live previews, screenshots, and structured error feedback.
5253
</p>
5354

54-
<div class="mt-8 space-y-3">
55-
<div class="flex gap-4 rounded-2xl border border-surface-card-border/80 bg-surface-primary/70 px-4 py-4">
55+
<ol class="mt-8 space-y-3">
56+
<li class="flex gap-4 rounded-2xl border border-surface-card-border/80 bg-surface-primary/70 px-4 py-4">
5657
<span class="font-mono text-xs font-semibold tracking-[0.18em] text-accent">01</span>
5758
<p class="text-sm leading-6 text-text-secondary">
5859
Point your MCP client at
@@ -61,9 +62,9 @@ export default function PlaygroundLanding(props: PlaygroundLandingProps) {
6162
{' '}
6263
so it can access the remote ShaderBase tools.
6364
</p>
64-
</div>
65+
</li>
6566

66-
<div class="flex gap-4 rounded-2xl border border-surface-card-border/80 bg-surface-primary/70 px-4 py-4">
67+
<li class="flex gap-4 rounded-2xl border border-surface-card-border/80 bg-surface-primary/70 px-4 py-4">
6768
<span class="font-mono text-xs font-semibold tracking-[0.18em] text-accent">02</span>
6869
<p class="text-sm leading-6 text-text-secondary">
6970
Start a session with
@@ -76,9 +77,9 @@ export default function PlaygroundLanding(props: PlaygroundLandingProps) {
7677
{' '}
7778
URL in a browser tab.
7879
</p>
79-
</div>
80+
</li>
8081

81-
<div class="flex gap-4 rounded-2xl border border-surface-card-border/80 bg-surface-primary/70 px-4 py-4">
82+
<li class="flex gap-4 rounded-2xl border border-surface-card-border/80 bg-surface-primary/70 px-4 py-4">
8283
<span class="font-mono text-xs font-semibold tracking-[0.18em] text-accent">03</span>
8384
<p class="text-sm leading-6 text-text-secondary">
8485
Iterate from your agent with
@@ -92,8 +93,8 @@ export default function PlaygroundLanding(props: PlaygroundLandingProps) {
9293
<code class="font-mono text-text-primary">get_errors</code>
9394
.
9495
</p>
95-
</div>
96-
</div>
96+
</li>
97+
</ol>
9798

9899
<div class="mt-8 grid gap-4 sm:grid-cols-2">
99100
<div class="rounded-2xl border border-surface-card-border/80 bg-surface-primary/70 p-4">
@@ -150,11 +151,11 @@ export default function PlaygroundLanding(props: PlaygroundLandingProps) {
150151
Manual sessions are useful for quick experiments, but they are not the primary MCP workflow.
151152
</p>
152153

153-
{props.error ? (
154+
<Show when={props.error}>
154155
<div class="mt-4 rounded-2xl border border-danger/20 bg-danger-dim/35 px-4 py-3 text-sm text-danger">
155-
{props.error}
156+
<p role="alert" aria-live="assertive">{props.error}</p>
156157
</div>
157-
) : null}
158+
</Show>
158159
</div>
159160

160161
<div class="space-y-4">

apps/web/src/routes/playground.tsx

Lines changed: 61 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { createFileRoute, useNavigate } from '@tanstack/solid-router'
22
import { createServerFn } from '@tanstack/solid-start'
33
import { useServerFn } from '@tanstack/solid-start'
4-
import { createSignal, onMount, Show } from 'solid-js'
4+
import { createSignal, Match, onMount, Switch } from 'solid-js'
55
import PlaygroundLanding from '../components/playground/PlaygroundLanding'
66
import PlaygroundLayout from '../components/playground/PlaygroundLayout'
77
import SurfaceCard from '../components/ui/SurfaceCard'
@@ -74,7 +74,6 @@ function PlaygroundPage() {
7474
const result = await startManualSession({ data: {} })
7575
const nextSession = result as PlaygroundSession
7676
setSession(nextSession)
77-
setLoading(false)
7877
navigate({ search: { session: nextSession.id }, replace: true })
7978
} catch (e) {
8079
setError(e instanceof Error ? e.message : 'Failed to create playground session.')
@@ -83,67 +82,72 @@ function PlaygroundPage() {
8382
}
8483
}
8584

86-
const shouldShowLanding = () => !search().session && !session()
85+
const currentState = () => {
86+
if (session()) return 'session'
87+
if (loading()) return 'loading'
88+
if (!search().session) return 'landing'
89+
return 'unavailable'
90+
}
8791

8892
return (
89-
<>
90-
<Show when={shouldShowLanding()}>
93+
<Switch>
94+
<Match when={currentState() === 'landing'}>
9195
<PlaygroundLanding
9296
creatingSession={creatingManualSession()}
9397
error={error()}
9498
onStartManualSession={handleStartManualSession}
9599
/>
96-
</Show>
97-
98-
<Show when={!shouldShowLanding()}>
99-
<Show
100-
when={session()}
101-
keyed
102-
fallback={
103-
loading() ? (
104-
<div class="flex min-h-[calc(100dvh-56px)] items-center justify-center">
105-
<div class="h-6 w-6 animate-spin rounded-full border-2 border-accent border-t-transparent" />
106-
</div>
107-
) : (
108-
<main class="mx-auto flex min-h-[calc(100dvh-56px)] w-full max-w-3xl items-center px-4 py-10">
109-
<SurfaceCard class="w-full rounded-[2rem] p-8">
110-
<p class="text-[0.68rem] font-semibold uppercase tracking-[0.2em] text-text-muted">
111-
Playground session
112-
</p>
113-
<h1 class="mt-3 text-3xl font-semibold tracking-tight text-text-primary">
114-
Session unavailable
115-
</h1>
116-
<p class="mt-3 text-sm leading-7 text-text-secondary">
117-
{error() || 'This playground session could not be loaded.'}
118-
</p>
119-
<div class="mt-6 flex flex-col gap-3 sm:flex-row">
120-
<button
121-
type="button"
122-
onClick={() => {
123-
setError('')
124-
navigate({ search: {}, replace: true })
125-
}}
126-
class="inline-flex items-center justify-center rounded-xl border border-surface-card-border bg-surface-card px-4 py-3 text-sm font-semibold text-text-primary transition hover:-translate-y-[1px] hover:border-accent/30 hover:text-accent active:translate-y-0 active:scale-[0.98]"
127-
>
128-
Back to MCP instructions
129-
</button>
130-
<button
131-
type="button"
132-
onClick={() => void handleStartManualSession()}
133-
disabled={creatingManualSession()}
134-
class="inline-flex items-center justify-center rounded-xl bg-accent px-4 py-3 text-sm font-semibold text-white transition hover:-translate-y-[1px] hover:bg-accent/90 disabled:cursor-wait disabled:opacity-70 active:translate-y-0 active:scale-[0.98]"
135-
>
136-
{creatingManualSession() ? 'Starting manual session...' : 'Start manual session'}
137-
</button>
138-
</div>
139-
</SurfaceCard>
140-
</main>
141-
)
142-
}
143-
>
144-
{(loadedSession) => <PlaygroundLayout session={loadedSession} />}
145-
</Show>
146-
</Show>
147-
</>
100+
</Match>
101+
102+
<Match when={currentState() === 'loading'}>
103+
<div class="flex min-h-[calc(100dvh-56px)] items-center justify-center">
104+
<div class="h-6 w-6 animate-spin rounded-full border-2 border-accent border-t-transparent" />
105+
</div>
106+
</Match>
107+
108+
<Match when={currentState() === 'session'}>
109+
<PlaygroundLayout session={session() as PlaygroundSession} />
110+
</Match>
111+
112+
<Match when={currentState() === 'unavailable'}>
113+
<main class="mx-auto flex min-h-[calc(100dvh-56px)] w-full max-w-3xl items-center px-4 py-10">
114+
<SurfaceCard class="w-full rounded-[2rem] p-8">
115+
<p class="text-[0.68rem] font-semibold uppercase tracking-[0.2em] text-text-muted">
116+
Playground session
117+
</p>
118+
<h1 class="mt-3 text-3xl font-semibold tracking-tight text-text-primary">
119+
Session unavailable
120+
</h1>
121+
<p
122+
role="alert"
123+
aria-live="assertive"
124+
class="mt-3 text-sm leading-7 text-text-secondary"
125+
>
126+
{error() || 'This playground session could not be loaded.'}
127+
</p>
128+
<div class="mt-6 flex flex-col gap-3 sm:flex-row">
129+
<button
130+
type="button"
131+
onClick={() => {
132+
setError('')
133+
navigate({ search: {}, replace: true })
134+
}}
135+
class="inline-flex items-center justify-center rounded-xl border border-surface-card-border bg-surface-card px-4 py-3 text-sm font-semibold text-text-primary transition hover:-translate-y-[1px] hover:border-accent/30 hover:text-accent active:translate-y-0 active:scale-[0.98]"
136+
>
137+
Back to MCP instructions
138+
</button>
139+
<button
140+
type="button"
141+
onClick={() => void handleStartManualSession()}
142+
disabled={creatingManualSession()}
143+
class="inline-flex items-center justify-center rounded-xl bg-accent px-4 py-3 text-sm font-semibold text-white transition hover:-translate-y-[1px] hover:bg-accent/90 disabled:cursor-wait disabled:opacity-70 active:translate-y-0 active:scale-[0.98]"
144+
>
145+
{creatingManualSession() ? 'Starting manual session...' : 'Start manual session'}
146+
</button>
147+
</div>
148+
</SurfaceCard>
149+
</main>
150+
</Match>
151+
</Switch>
148152
)
149153
}

0 commit comments

Comments
 (0)