Skip to content

Deep-link lookup banner for #?s= fragments - #3

Merged
remcoros merged 2 commits into
remcoros:mainfrom
tetragrammalol:feat/deeplink-lookup-banner
Oct 17, 2026
Merged

remcoros merged 2 commits into
remcoros:mainfrom
tetragrammalol:feat/deeplink-lookup-banner

Conversation

@tetragrammalol

Copy link
Copy Markdown
Contributor

Implements the design agreed in #2.

Behavior

  1. A #?s=<txid|outpoint|address> fragment is read once at load and again on
    hash change. In-app anchors such as #main-workspace are ignored because
    only the #?s= shape is matched.
  2. The fragment is cleared immediately with history.replaceState, before any
    click, so the reference never re-enters the address bar, history, or logs.
  3. A global banner shows the reference with an add action and a dismiss
    action, regardless of workspace state, as specified in Feature proposal: fragment-based deep links for lookups (e.g. #?tx=<txid>) #2.
  4. Adding goes through the existing manual lookup path (graph.lookup.submit).
    On success the banner dismisses. On failure the global error toast shows
    and the banner stays open so the user can switch workspaces and retry.
  5. With no unlocked workspace the click explains that a workspace is needed
    and the banner stays open. submit() returns silently in that case today,
    so the banner handles it before calling through.

Census facts behind the design

  • useWorkspace already publishes graph.lookup.submit, so the banner needs
    no Core changes (src/App/Workspace/useWorkspace.tsx).
  • Task failures surface through useWorkspaceOperation.run, which routes
    errors to the global error toast (src/App/Workspace/useWorkspaceOperation.ts).
  • Already loaded references resolve offline through resolveLoaded, which the
    banner reuses for offline adds and success detection
    (src/App/Workspace/Workbenches/Graph/Navigation/useGraphLookupState.ts).

Files

  • src/App/deepLink.ts: pure fragment parser.
  • src/App/deepLink.test.ts: parser unit tests.
  • src/App/DeepLinkBanner.tsx: banner component.
  • src/App/deeplink-banner.css plus a component-styles.ts import: banner styles
    following the per-feature CSS pattern.
  • src/App/useAppState.ts: deepLinkRef state and the consume-and-clear effect.
  • src/App/App.tsx: banner rendered beside the toast.
  • tests/e2e/deeplink.spec.ts: fragment to banner to graph, the no-workspace
    path, and dismissal.

Validation

  • npm run check (formatting, portability, lint, build, unit and integration).
  • Playwright e2e suite when Chromium is available locally.
  • Visual placement of the banner relative to the toast was not checked in a
    browser beyond the e2e assertions; happy to adjust position and styling.

Follow-up owed: a user-guide sentence once the interaction wording settles.

Read a #?s=<txid|outpoint|address> reference from the URL fragment at load
or on hash change, clear the fragment from the address bar, and show a
global banner that adds the reference through the existing graph lookup
path. On failure the error toast shows and the banner stays open; with no
workspace unlocked the click explains that a workspace is needed.

Implements the design agreed in remcoros#2.
@tetragrammalol
tetragrammalol force-pushed the feat/deeplink-lookup-banner branch from d7fbb48 to f4e6b32 Compare October 17, 2026 13:31
@tetragrammalol

Copy link
Copy Markdown
Contributor Author

Correction force-pushed to this branch; the first push carried one unintended change.

What slipped: in src/App/useAppState.ts the workspace-tabs reveal line lost its guard and flipped direction (else if (item.right > bounds.right) tabs.scrollLeft += item.right - bounds.right; became else tabs.scrollLeft += bounds.right - item.right;). A transcription error on my side. It was type-valid and lint-clean, and the jsdom suites could not catch it because getBoundingClientRect() is all zeros there, so the full 2110-test run stayed green with the bug present.

What now guards it: the file's delta against main is gated mechanically before anything is committed or pushed (exactly 3 hunks, 17 additions, 0 deletions, no scroll lines in the delta). The file now differs from main only by the fragment parser import, the deepLinkRef state, the consume-and-clear effect, and the two returned fields.

Also fixed while at it: the first e2e test unlocked a workspace and then used page.goto to set the fragment, which reloads and locks every workspace, so the click never exercised the intended path. The spec now seeds storage, lands on the fragment, unlocks, then clicks. Validation: npm run check green (211 files, 2110 tests) and Playwright 5 of 5 green (three deep-link legs plus both smoke tests). The /api/networks ECONNREFUSED lines in a local run are the backendless dev server; the pre-existing smoke tests log the same.

Still owed as stated in the description: a browser look at banner placement next to the toast, and the user-guide sentence.

@remcoros

Copy link
Copy Markdown
Owner

Thanks for the contribution, @tetragrammalol! I added 0b2aa6e to make banner dismissal follow actual lookup completion, preserve newer links and retries, and show feedback for unavailable lookups. Successful lookups from Wallet or Analysis now open Graph with a return route.

Also added regression coverage and updated the user guide. npm run check passes: 212 test files, 2,131 tests.

@remcoros

Copy link
Copy Markdown
Owner

awesome and thanks!

@tetragrammalol

Copy link
Copy Markdown
Contributor Author

Great stuff, thanks for prompt response on this issue @remcoros

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.

2 participants