Feat: URL-based gem selection and deeplink support #3
Conversation
There was a problem hiding this comment.
Pull request overview
This PR replaces the hardcoded dropdown-based gem selection with URL-based routing and deeplink support. Navigating to /{gemId} auto-connects to a gem, while / shows an entry modal with a validated free-text input. Connection failures redirect back to / with the input pre-filled and a contextual error message.
Changes:
- URL-based routing via
useRoutehook andparseGemIdFromPathname, replacing theAppModestate machine with URL as the source of truth - Free-text input with inline validation against
GEM_ID_REGEXin the modal, plus error recovery flow usinghistory.state - Background reconnect timeout (15s) that redirects to
/with an error if the socket doesn't recover
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
app/src/App.tsx |
Core routing logic: derives gemId from URL, auto-connects, handles reconnect timeout, replaces mode/dropdown state |
app/src/components/RGemSelectorModal.tsx |
Replaced dropdown with validated text input; reads history.state for error pre-population |
app/src/lib/useRoute.ts |
New hook: subscribes to popstate and returns current pathname |
app/src/lib/gem-id.ts |
New utilities: isValidGemId, parseGemIdFromPathname, GEM_ID_REGEX |
app/src/lib/gem-id.test.ts |
19 unit tests covering gem ID validation and pathname parsing |
app/src/types/grid.ts |
Removed AppMode type (no longer needed) |
app/src/styles/globals.css |
Added .rgem-modal-input class alongside .rgem-modal-select |
app/SPEC.md |
New BDD specification with 12 scenarios covering all user flows |
app/CLAUDE.md |
Updated state machine docs, added configure step, updated ref-to-latest docs |
CLAUDE.md |
Added UX & Styling conventions section |
README.md |
Added prerequisite callout for ./configure.sh dev |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: bgerd <6319795+bgerd@users.noreply.github.com>
Move RECONNECT_TIMEOUT_MS to module scope
|
Pulled changes and test locally. Ready to try deploying to |
|
|
successfully deployed to |
Summary
/{gemId}now connects directly to that gem without the modal. The root path/retains the entry modal with a free-text input.GEM_ID_REGEX(/^[a-zA-Z0-9-]{1,24}$/) inline before connecting./with the input pre-filled and a contextual error message.What changed
Routing (
App.tsx)gemIdis derived fromuseRoute()+parseGemIdFromPathname()— URL is the source of truth, no mode state/{gemId}, redirects invalid paths, resets state at/staleflag guards the routing effect's async catch against React Strict Mode's double-mount (first mount's socket is torn down before itsconnectToRgemresolves)/{gemId}and does not recover, redirects to/with pre-filled error. Uses areconnectTimerRefto avoidresetting the timer on each WebSocketProvider backoff cycle (
CLOSED → CONNECTING → CLOSED)app_mountnow callsopenSocketdirectly (notensureConnected) to prevent a duplicateconnectToRgemcall racing with the routing effectModal (
RGemSelectorModal.tsx)placeholder="e.g. test-1", Enter-key submit, inline validation errorwindow.history.statevia lazyuseStateinitializers (notuseEffect) to pre-populate the input and show a contextual connection error on remount after failureNew library files
src/lib/gem-id.ts—isValidGemId,parseGemIdFromPathname,GEM_ID_REGEXsrc/lib/gem-id.test.ts— 19 pure unit tests (node-compatible)src/lib/useRoute.ts—useRoutehook (popstate subscriber)Styling
.rgem-modal-inputCSS rule alongside.rgem-modal-selectinglobals.cssDocs
CLAUDE.md: added UX & Styling conventions sectionREADME.md: prominent prerequisite callout for./configure.sh devbeforenpm run devapp/CLAUDE.md: configure step in Commands, updated State Machines and Ref-to-latest sectionsapp/SPEC.md: 12 BDD scenarios covering all tested user flows (new file)Test plan
npm run test— 47 tests pass (19 new ingem-id.test.ts)npm run lint— clean/— modal shown, input focusable, Connect disabled/{gemId}, overlay shows, grid loads/, modal empty, no error/{gemId}— grid loads directly, no modal/bad!!id— redirects to/with validation error shown/{gemId}— overlay shows, redirects to/with pre-filled error/{gemId}, set DevTools → Offline — overlay shows; after 15s redirects to/with pre-filled error