Skip to content

Feat: URL-based gem selection and deeplink support #3

Merged
bgerd merged 8 commits intomainfrom
feat/deeplink-rgem-id
Mar 12, 2026
Merged

Feat: URL-based gem selection and deeplink support #3
bgerd merged 8 commits intomainfrom
feat/deeplink-rgem-id

Conversation

@bgerd
Copy link
Copy Markdown
Owner

@bgerd bgerd commented Mar 12, 2026

Summary

  • Deeplinkable gem URLs: navigating to /{gemId} now connects directly to that gem without the modal. The root path / retains the entry modal with a free-text input.
  • Validated text input: replaces the hardcoded dropdown with a free-text input that validates against GEM_ID_REGEX (/^[a-zA-Z0-9-]{1,24}$/) inline before connecting.
  • Full error recovery flow: connection failures (initial navigate, network drop mid-session) redirect to / with the input pre-filled and a contextual error message.

What changed

Routing (App.tsx)

  • gemId is derived from useRoute() + parseGemIdFromPathname() — URL is the source of truth, no mode state
  • Routing effect auto-connects on valid /{gemId}, redirects invalid paths, resets state at /
  • stale flag guards the routing effect's async catch against React Strict Mode's double-mount (first mount's socket is torn down before its connectToRgem resolves)
  • Background reconnect timeout (15s): if the socket drops while at /{gemId} and does not recover, redirects to / with pre-filled error. Uses a reconnectTimerRef to avoid
    resetting the timer on each WebSocketProvider backoff cycle (CLOSED → CONNECTING → CLOSED)
  • Lifecycle effect's app_mount now calls openSocket directly (not ensureConnected) to prevent a duplicate connectToRgem call racing with the routing effect

Modal (RGemSelectorModal.tsx)

  • Free-text input with placeholder="e.g. test-1", Enter-key submit, inline validation error
  • Reads window.history.state via lazy useState initializers (not useEffect) to pre-populate the input and show a contextual connection error on remount after failure
  • Error display priority: inline validation > external routing error (prop) > history.state connection error

New library files

  • src/lib/gem-id.tsisValidGemId, parseGemIdFromPathname, GEM_ID_REGEX
  • src/lib/gem-id.test.ts — 19 pure unit tests (node-compatible)
  • src/lib/useRoute.tsuseRoute hook (popstate subscriber)

Styling

  • Added .rgem-modal-input CSS rule alongside .rgem-modal-select in globals.css

Docs

  • CLAUDE.md: added UX & Styling conventions section
  • README.md: prominent prerequisite callout for ./configure.sh dev before npm run dev
  • app/CLAUDE.md: configure step in Commands, updated State Machines and Ref-to-latest sections
  • app/SPEC.md: 12 BDD scenarios covering all tested user flows (new file)

Test plan

  • npm run test — 47 tests pass (19 new in gem-id.test.ts)
  • npm run lint — clean
  • Visit / — modal shown, input focusable, Connect disabled
  • Type valid ID, click Connect — URL becomes /{gemId}, overlay shows, grid loads
  • Browser back — returns to /, modal empty, no error
  • Hard refresh at /{gemId} — grid loads directly, no modal
  • Navigate to /bad!!id — redirects to / with validation error shown
  • Disconnect network, navigate to /{gemId} — overlay shows, redirects to / with pre-filled error
  • Connect at /{gemId}, set DevTools → Offline — overlay shows; after 15s redirects to / with pre-filled error
  • Set DevTools → Offline briefly then Online within 15s — overlay shows then clears, grid resumes

@bgerd bgerd requested a review from Copilot March 12, 2026 17:02
@bgerd bgerd self-assigned this Mar 12, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 useRoute hook and parseGemIdFromPathname, replacing the AppMode state machine with URL as the source of truth
  • Free-text input with inline validation against GEM_ID_REGEX in the modal, plus error recovery flow using history.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.

Comment thread app/src/App.tsx
Comment thread app/src/App.tsx Outdated
bgerd and others added 2 commits March 12, 2026 13:32
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

Copilot AI commented Mar 12, 2026

@bgerd I've opened a new pull request, #4, to work on those changes. Once the pull request is ready, I'll request review from you.

Copilot AI and others added 2 commits March 12, 2026 17:35
Co-authored-by: bgerd <6319795+bgerd@users.noreply.github.com>
Move RECONNECT_TIMEOUT_MS to module scope
@bgerd
Copy link
Copy Markdown
Owner Author

bgerd commented Mar 12, 2026

Pulled changes and test locally. Ready to try deploying to dev environment

@bgerd bgerd closed this Mar 12, 2026
@bgerd bgerd reopened this Mar 12, 2026
@bgerd
Copy link
Copy Markdown
Owner Author

bgerd commented Mar 12, 2026

  • Tested new branch ruleset. Merge was being blocked (E.g. At least 1 approving review is required by reviewers with write access.)
  • Updated github branch rulesets with admin bypass

@bgerd bgerd merged commit 3ef3fcf into main Mar 12, 2026
2 checks passed
@bgerd
Copy link
Copy Markdown
Owner Author

bgerd commented Mar 12, 2026

successfully deployed to dev environment. try https://app-dev.rgem.io/default

@bgerd bgerd mentioned this pull request Mar 20, 2026
6 tasks
@bgerd bgerd deleted the feat/deeplink-rgem-id branch March 21, 2026 00:28
@bgerd bgerd changed the title feat: URL-based gem selection and deeplink support Feat: URL-based gem selection and deeplink support Mar 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants