Commit f5d42ce
authored
feat(url-state): adopt nuqs for type-safe URL query-param state (#5163)
* feat(url-state): introduce nuqs for type-safe query-param state
Add nuqs and migrate ad-hoc URL query-param handling to typed parsers.
- Wrap the provider tree in `NuqsAdapter` (app/layout.tsx).
- Co-locate typed param modules:
- logs/search-params.ts — timeRange/level/workflowIds/folderIds/triggers/
search parsers (history: 'replace', clearOnDefault) preserving the exact
prior wire encoding (kebab time-range tokens, comma-joined arrays).
- integrations/[block]/search-params.ts — ephemeral `connect` literal param.
- Replace the logs filter store's hand-rolled URL sync (initializeFromURL /
syncWithURL / popstate) with a URL-backed `useLogFilters` hook over
useQueryStates; the zustand store now holds only the non-URL viewMode toggle.
- Migrate logs.tsx (executionId + search), logs-toolbar, dashboard, and the
integration detail `connect` deep-link (read-then-strip) to nuqs.
URL keys, defaults, and history semantics are unchanged.
* feat(url-state): migrate deferred sites to nuqs + add url-state rule
Migrate the deferred query-param sites to typed nuqs parsers, each with a
co-located search-params.ts single source of truth:
- settings/[section]: mcpServerId deep-link
- files: folderId (history: push) + new compose flag
- knowledge/[id]: addConnector read-then-strip deep-link
- knowledge/[id]/[documentId]: page (int, default 1) + chunk deep-link
Workflow editor intentionally left store-backed (socket-synced / high-frequency
/ persisted-preference view-state); documented in the rule's carve-out.
Add .claude/rules/sim-url-state.md (decision framework, conventions, server
cache + debounced-input patterns, editor carve-out); cross-link from CLAUDE.md
and sim-queries.md.
* feat(url-state): migrate remaining view-state to nuqs + harness updates
Make the URL the single source of truth for shareable view-state across the
remaining sweep-confirmed sites:
- settings/mcp: replace initialServerId prop + effect-sync with a direct
useQueryState (mcpServerId, history: push); stop prop-drilling from settings
- integrations: selectedCategory + debounced search; add Suspense boundary
- tables: debounced search + sort/dir + row-count/owner filters (activeTable
stays route state — selecting a table navigates to tables/[tableId]); wire
the existing loading.tsx as the Suspense fallback
- knowledge/[id]: pagination page param
- settings/recently-deleted: tab + sort/dir + debounced search
- settings/admin: committed search (q) + pagination offset
- settings/mothership: tab + environment
- skills: editingSkill object -> skillId deep-link (derive from useSkills); add
Suspense boundary
- files: shareFileId deep-link added to files/search-params
- landing integrations + models directories: debounced search + category/
provider filter; add Suspense boundaries
Harness: add a When-to-use decision table, the sort (sort+dir) convention, the
selected-entity deep-link pattern, and nuqs doc links to sim-url-state.md; add
the /you-might-not-need-url-state command and wire it into /cleanup.
* fix(nuqs): revert landing-page param migrations and tighten workspace URL-state
- Revert integrations/models landing pages to static SEO HTML (drop nuqs migration + their search-params files)
- MCP settings: refresh tools only for an initial deep-linked server id, not on subsequent user selections
- Add a Suspense boundary with real chrome around the nuqs-using integration detail page
- Trim inaccurate "server component reads these params" TSDoc from search-params files (createSearchParamsCache is unused)
- Export mcpServerIdUrlKeys from the settings search-params file instead of inlining the options in mcp.tsx
- Convert two new relative imports (logs use-log-filters, tables loading) to absolute
- Wrap setSelectedCategory in useCallback; clear active skillId edit param when opening the create-skill form
* fix(logs): add logs-page Suspense boundary and co-locate nuqs params
- Wrap <Logs/> in <Suspense fallback={<LogsLoading/>}> so the nuqs reads
(useLogFilters, executionId) have a boundary ancestor like sibling pages.
- Co-locate the executionId param in logs/search-params.ts (read-only,
intentionally not stripped) and consume it in logs.tsx.
- Migrate log-details activeTab to a deep-linkable nuqs tab param (single
LogDetails instance; preview path uses ExecutionSnapshot, not LogDetails).
- Align cleanup.md description pass order with the numbered steps.
- Replace mothership.tsx local Tab type with exported MothershipTab.
* fix(url-state): honor deep-linked log-details tab on first mount
* improvement(nuqs): adopt limitUrlUpdates debounce + add eq to array parser
Replace the hand-rolled debounced-search pattern (local useState mirror +
useDebounce + URL write-back effect + ref-guarded reconcile effect) with
nuqs's built-in limitUrlUpdates: debounce() across logs, integrations,
tables, and recently-deleted. The input is now controlled directly by the
instant nuqs value; only the URL write is debounced. Query keys / expensive
filters still derive a debounced value off the instant value; cheap in-memory
filters read it directly. admin (commit-on-submit) intentionally left alone.
Add an eq to parseAsTriggers (TriggerType[]) so clearOnDefault can detect the
empty-array default and strip it from the URL, per nuqs createParser docs.
Update .claude/rules/sim-url-state.md to prescribe the debounce pattern and the
createParser eq requirement for array/object/Date values.
* feat(nuqs): migrate table-detail sort, KB document filters, and calendar view to URL state
- Table detail: sort+dir to nuqs; Filter stays in useState (recursive/nested, too large for URL)
- Knowledge base: search (debounced), enabled filter, sort+dir to nuqs; tagFilterEntries stays in useState (rich rule objects)
- Scheduled-tasks calendar: scope + date-only anchor (parseAsIsoDate, nullable, derive-today) to nuqs
- Add Suspense boundaries to table-detail and scheduled-tasks pages
- Document parseAsIsoDate / nullable-dynamic-default pattern in sim-url-state.md
* fix(nuqs): resolve 7 PR review findings on URL query-param state
- logs: clear the log-details `tab` param when the sidebar closes so a
lingering `?tab=trace` no longer carries into the next opened log;
deep-linked tabs still open on first mount.
- logs dashboard: drive the in-memory workflow filtering off the same
debounced search value the stats query uses (passed as a prop) so the
chart and list stay consistent while typing.
- knowledge document: make the URL `chunk` param the single source of
truth for the open chunk (back/forward, deep links, and external
navigation now drive the editor) instead of a one-time useState seed.
- logs: drop the redundant `setUrlSearchQuery('')` after `resetFilters()`
(resetFilters already clears `search`).
- files: use a per-call `{ history: 'replace' }` override for the
`shareFileId` share-modal open/close writes so toggling the modal does
not pollute the back/forward stack; folder navigation keeps `push`.
- tables + recently-deleted: trim search input before deriving the URL
value so whitespace-only input no longer writes `?search=%20`.
* fix(url-state): trim whitespace-only search in integrations filter
* fix(url-state): clear log tab on all close paths + trim KB search
* fix(scheduled-tasks): use local-time date parser for calendar anchor (avoid UTC day-shift)
* feat(home): migrate ?resource deep-link to nuqs (URL as source of truth)
Replace the banned window.history.replaceState effect on the home/Chat
surface with a nuqs useQueryState('resource') binding. The URL is now the
single source of truth for the selected resource.
- Add co-located home/search-params.ts (resource param, history: replace)
- useChat accepts a controlled activeResourceState binding; home passes the
nuqs-backed tuple. The workflow editor copilot keeps internal useState so
its resource selection stays out of the URL (editor carve-out)
- Preserve the old effect's url.hash='' fragment strip in the binding setter
(fragment-only rewrite, not a param mutation)
- Drop initialResourceId SSR prop from both page entries (nuqs reads the URL
on mount; no dual source) and wrap Home in Suspense for useSearchParams
* docs(home): note nuqs deferred-flush ordering in resource hash-strip
* docs(url-state): convert inline comments to TSDoc1 parent 9a2e06e commit f5d42ce
57 files changed
Lines changed: 2081 additions & 774 deletions
File tree
- .claude
- commands
- rules
- apps/sim
- app
- workspace/[workspaceId]
- chat/[chatId]
- files
- home
- hooks
- integrations
- [block]
- knowledge/[id]
- [documentId]
- logs
- components
- dashboard
- log-details
- logs-toolbar
- hooks
- scheduled-tasks
- hooks
- settings
- [section]
- components
- admin
- mcp
- mothership
- recently-deleted
- skills
- tables
- [tableId]
- stores/logs/filters
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | | - | |
| 2 | + | |
3 | 3 | | |
4 | 4 | | |
5 | 5 | | |
| |||
21 | 21 | | |
22 | 22 | | |
23 | 23 | | |
| 24 | + | |
24 | 25 | | |
25 | | - | |
| 26 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
7 | 7 | | |
8 | 8 | | |
9 | 9 | | |
| 10 | + | |
| 11 | + | |
10 | 12 | | |
11 | 13 | | |
12 | 14 | | |
| |||
0 commit comments