Skip to content

Polish pass: shared request pipeline, tab persistence, UX safety, dedup - #16

Merged
mohnjiles merged 2 commits into
mainfrom
polish-pass
Aug 22, 2026
Merged

Polish pass: shared request pipeline, tab persistence, UX safety, dedup#16
mohnjiles merged 2 commits into
mainfrom
polish-pass

Conversation

@mohnjiles

Copy link
Copy Markdown
Member

Summary

A broad polish sweep across the app — four bundles of bug fixes, UX improvements, and consolidation (net −241 lines of source before test additions).

Bug fixes

  • SSE streaming, WebSocket connect, and GraphQL introspection now resolve {{variables}} and apply auth through the shared applyAuthToHeaders — three of five hand-rolled auth copies skipped substitution entirely, so a streamed {{baseUrl}} with a {{token}} bearer went out verbatim.
  • New utils/requestBuilder.ts (buildSendRequestOptions) is the single substitution → auth → cookies → pre-request scripts → network pipeline, shared by useRequest and CollectionRunner. Fixes the runner double-sending user-typed cookie headers and deletes the weaker utils/auth.ts twin.
  • updateTab bails out on no-op updates. RequestPanel's streaming-sync effect fires every render via an inline lambda; unconditionally returning a fresh tabs array kept the app in a permanent ~200Hz render loop (measured 416 debounce resets in 2s while idle) that also starved the new tab-persistence debounce.
  • Enter no longer fires the request behind an open dialog; saving to a collection keeps the tab's (possibly user-set) name and gives feedback.

Tabs

  • Open tabs persist across restarts (debounced localStorage + synchronous pagehide flush); manual renames survive URL edits via a nameEdited flag.
  • Ctrl+Enter sends from anywhere including Monaco (editor command), Ctrl+T/W, Ctrl(+Shift)+Tab, Ctrl+1–9, middle-click close, right-click menu (Duplicate / Close Others / Close to the Right), keyboard-reachable close button, 240px width cap with title tooltip, overflow-only fade, active tab scrolls into view.

UX safety & a11y

  • Destructive deletes (collection, environment) get the same confirm dialog as clear-history; saves/exports toast; CopyButton is labeled and surfaces clipboard failures; KeyValueList gets real aria labels and Enter-adds-row; panel widths persist; import modals share theming, inline errors, reset-on-close, and Ctrl+Enter submit.

Overflow fixes — long unbroken strings (URLs, hashes) blew past panel edges in several places: Radix ScrollArea's display: table viewport wrapper is forced back to block (all our ScrollAreas are vertical-only), dialog grid tracks are pinned with minmax(0,1fr), and the palette's sub-label is capped.

Dedup — one methodColors module (was 7 copies across 3 palettes), one formatBytes (was 4 formatters; a 5 MB response rendered as "5120.0KB"), one post_oauth_form helper in oauth.rs (was 5 identical POSTs), dead code removed (useSession, unused utils, react-router-dom, @shadcn/ui), and the remaining anys at persistence/OpenAPI boundaries replaced with unknown + narrow shapes.

Test plan

  • 430 vitest tests green (9 new: dialog Enter guard, Ctrl+Enter, delete confirms, middle-click close, KeyValueList Enter-adds-row, save-dialog name field)
  • 24 cargo tests green, cargo clippy --all-targets clean
  • tsc strict clean; eslint 0 errors, no-explicit-any warnings gone
  • Production npm run build succeeds
  • Browser-preview verification of overflow fixes with seeded hostile data (64-char hash names, 200-char URLs) — no horizontal overflow anywhere
  • Native WebView2 verification on the dev build: real sends through the new pipeline, byte-format badges, tab truncation, tab persistence across two real app restarts, and hands-on Ctrl+K/T/W/Esc

🤖 Generated with Claude Code

Bug fixes:
- SSE streaming, WebSocket connect, and GraphQL introspection now resolve
  {{variables}} and apply auth via the shared applyAuthToHeaders — three of
  five hand-rolled auth copies skipped substitution entirely, so a streamed
  {{baseUrl}} with a {{token}} bearer went out verbatim
- Extract buildSendRequestOptions (utils/requestBuilder.ts): one substitution
  -> auth -> cookies -> scripts -> network pipeline shared by useRequest and
  CollectionRunner, fixing the runner's cookie double-send and killing ~90
  drifted duplicate lines; delete the weaker utils/auth.ts twin
- updateTab bails out when values are unchanged — RequestPanel's streaming
  sync effect fires per render via an inline lambda, and returning a fresh
  array for a no-op update kept the app in a permanent render loop that
  starved any debounce keyed on tabs identity
- Enter no longer sends the request behind an open dialog; saving keeps the
  tab's (possibly user-set) name instead of a URL-derived one

Tabs:
- Persist open tabs to localStorage (debounced + pagehide flush) and restore
  on launch; nameEdited flag keeps manual renames across URL edits
- Ctrl+Enter sends from anywhere (registered as a Monaco command in both
  editors), Ctrl+T/W new/close, Ctrl(+Shift)+Tab cycle, Ctrl+1-9 jump
- Middle-click close, context menu (duplicate/close others/close right),
  keyboard-reachable close button, 240px cap with title tooltip,
  overflow-only edge fade, scroll active tab into view

UX safety and a11y:
- Confirm dialogs for delete collection/environment (matching clear-history);
  save/export toasts; CopyButton gets labels and surfaces clipboard failures
- KeyValueList: real aria labels, Enter adds the next row; empty state for
  environments; panel widths persist; resize handles get separator roles
- Import modals share theming, inline errors, reset-on-close, Ctrl+Enter

Overflow fixes for long unbroken strings (URLs, hashes):
- ScrollArea viewport wrapper forced to block — Radix's display:table sizing
  let content blow past panel edges and defeat truncate everywhere
- Dialog grid track pinned with minmax(0,1fr); palette sub-label capped

Dedup:
- One methodColors module (seven copies, three palettes disagreed on GET's
  hue), one formatBytes (four formatters; 5 MB rendered as "5120.0KB"),
  one post_oauth_form helper for oauth.rs's five identical token POSTs
- Remove dead useSession/Session, unused utils, react-router-dom and
  @shadcn/ui deps; replace the remaining any types at persistence and
  OpenAPI boundaries with unknown + narrow shapes

Verified: 430 vitest + 24 cargo green, clippy clean, tsc strict clean, and
native WebView2 checks (send pipeline, shortcuts, persistence, truncation).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@lykos-reviewer

lykos-reviewer Bot commented Aug 22, 2026

Copy link
Copy Markdown

Lykos Reviewer finished — 2 comment(s). See the review below.

@lykos-reviewer lykos-reviewer Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

heya, Lykos here :3 poking through this with the pack. this is a massive and beautiful clean-up! you've really smoothed out the rough edges, especially with the request pipeline unification and that sneaky render loop fix in the tab store. the app feels way more robust and professional now 💕

} catch {
// Not a parseable URL — send as typed and let the backend report it
}
connectUrl = applyAuthToHeaders(auth, headerRecord, connectUrl, substituteVariables)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium correctness

glad to see variable substitution and auth finally landing here too! before this, a websocket URL with a {{token}} would just go out verbatim and fail... so this is a big fix for anyone using dynamic environments with sockets. :bcnod:

Comment thread src/hooks/useTabs.ts
streaming: null,
cancelStream: undefined,
testResults: null,
})

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

low performance

this Object.is bail-out is a lifesaver. starving the persistence debounce with an accidental render loop is one of those bugs that's invisible until the app starts eating 100% CPU. great catch :3

@mohnjiles
mohnjiles merged commit 84cd829 into main Aug 22, 2026
2 checks passed
@mohnjiles
mohnjiles deleted the polish-pass branch August 22, 2026 06:31
@mohnjiles mohnjiles mentioned this pull request Aug 22, 2026
1 task
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants