Skip to content

feat: integrate Quick Navigator search overlay#252

Merged
debba merged 5 commits into
mainfrom
feat/quick-navigator
May 29, 2026
Merged

feat: integrate Quick Navigator search overlay#252
debba merged 5 commits into
mainfrom
feat/quick-navigator

Conversation

@debba
Copy link
Copy Markdown
Collaborator

@debba debba commented May 25, 2026

Description

This PR integrates a new Quick Navigator (search overlay/dialog) for database schema elements in the sidebar of an active connection, taking inspiration from Beekeeper Studio's Quick Search.

Features

  • Customizable Shortcut: The shortcut (Cmd+P / Ctrl+P by default) is fully customizable in settings under the "Navigation" category. We prevent browser print behavior when it triggers.
  • Dynamic Search Overlay: Implemented QuickNavigatorModal.tsx that autofocuses a search input and dynamically filters tables, views, routines, and triggers.
  • All-Database Background Search: Automatically resolves and indexes all databases/schemas configured for the active connection in the background when the modal is opened.
  • Section Grouping & Separators: Results are grouped by database/schema under clean, high-readability headers, removing repetitive pills on individual rows.
  • Interactive Quick Actions: Added action icons next to search items on hover (Inspect Structure, Generate SQL, Run Query).

Implementation Details

  • Extracted item listing and search filtering logic into a separate unit-tested utility module (src/utils/quickNavigator.ts).
  • Added comprehensive unit tests in tests/utils/quickNavigator.test.ts.

Verification

  • Standard Vitest unit tests pass successfully (2432/2432 tests passed).
  • TypeScript compiler passes with 0 type-check errors.

- Added customizable 'quick_navigator' keyboard shortcut (Cmd+P / Ctrl+P).
- Created QuickNavigatorModal component to filter tables, views, routines, and triggers of the active database connection.
- Restructured code by moving extraction and filtering logic to src/utils/quickNavigator.ts.
- Added comprehensive unit tests in tests/utils/quickNavigator.test.ts.
- Integrated background metadata loading for all schemas/databases configured in the connection parameters.
- Grouped results under clean separator headers by database/schema, removing visual redundancy.
- Supported hover quick actions (Inspect, Generate SQL, Run Query) next to items.
@debba debba force-pushed the feat/quick-navigator branch from 1587b5d to 8010f5d Compare May 25, 2026 16:41
}
};

loadAll();
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

WARNING: Unhandled promise rejections in background loading

loadSchemaData and loadDatabaseData return promises that are neither awaited nor caught. If any call throws, it will surface as an unhandled promise rejection. Consider collecting the promises and attaching a catch handler.

Suggested change
loadAll();
Promise.all((hasSchemas && schemas) ? schemas.map((s) => loadSchemaData(s)) : (isMultiDb && configuredDatabases) ? configuredDatabases.map((d) => loadDatabaseData(d)) : []).catch(() => {});

@kilo-code-bot
Copy link
Copy Markdown

kilo-code-bot Bot commented May 25, 2026

Code Review Summary

Status: 3 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 3
SUGGESTION 0
Issue Details (click to expand)

WARNING

File Line Issue
src/components/modals/QuickNavigatorModal.tsx 76 Unhandled promise rejections in background load
src/components/modals/QuickNavigatorModal.tsx 350 Duplicate React keys — triggers/routines with same name produce identical keys
src/components/layout/ExplorerSidebar.tsx N/A Unescaped CSS selector injection
Other Observations (not in diff)

Issues found in unchanged code that cannot receive inline comments:

File Line Issue
src/components/layout/Sidebar.tsx isQuickNavigatorOpen state is not reset when activeConnectionId becomes null. If the modal is open when the last connection disconnects, the state remains true; when a new connection is activated later, the modal unexpectedly reopens immediately
Resolved Since Last Review
File Issue
src/components/modals/QuickNavigatorModal.tsx Search text and selected index are no longer reset when the modal re-opens
src/components/layout/ExplorerSidebar.tsx CSS selector escaping — FIXED by introducing buildTableItemSelector with escapeAttributeValue in src/utils/sidebarTableItem.ts
Files Reviewed (16 files)
  • src/components/layout/ExplorerSidebar.tsx — fixed (selector escaping via buildTableItemSelector)
  • src/components/layout/Sidebar.tsx — 1 issue (state leak, unchanged)
  • src/components/layout/sidebar/SidebarDatabaseItem.tsx — clean
  • src/components/layout/sidebar/SidebarTableItem.tsx — clean
  • src/components/modals/QuickNavigatorModal.tsx — 2 issues (unhandled rejections, duplicate keys)
  • src/config/shortcuts.json — clean
  • src/hooks/useGlobalShortcuts.ts — clean
  • src/i18n/locales/de.json — clean
  • src/i18n/locales/en.json — clean
  • src/i18n/locales/es.json — clean
  • src/i18n/locales/fr.json — clean
  • src/i18n/locales/it.json — clean
  • src/i18n/locales/ja.json — clean
  • src/i18n/locales/ru.json — clean
  • src/i18n/locales/zh.json — clean
  • src/utils/quickNavigator.ts — clean
  • src/utils/sidebarTableItem.ts — clean
  • tests/utils/quickNavigator.test.ts — clean
  • tests/utils/sidebarTableItem.test.ts — clean

Fix these issues in Kilo Cloud


Reviewed by kimi-k2.6-20260420 · 221,045 tokens

useEffect(() => {
if (!activeTable || !sidebarBodyRef.current) return;
const el = sidebarBodyRef.current.querySelector<HTMLElement>(
`[data-table-name="${activeTable}"][data-schema="${activeSchema ?? ''}"]`
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

WARNING: Unescaped CSS selector injection

activeTable or activeSchema values containing double quotes (e.g. my"table) will produce an invalid selector and cause querySelector to throw a DOMException. Use CSS.escape() to sanitize values before interpolating them into the selector.

Suggested change
`[data-table-name="${activeTable}"][data-schema="${activeSchema ?? ''}"]`
`[data-table-name="${CSS.escape(activeTable)}"][data-schema="${CSS.escape(activeSchema ?? '')}"]`

debba and others added 2 commits May 29, 2026 15:10
# Conflicts:
#	src/components/layout/ExplorerSidebar.tsx
…tables

Selecting a result in the Quick Navigator on a connection with many tables
(hundreds) froze the UI and the sidebar often failed to reveal the chosen
table. Three related issues are addressed:

- Performance: SidebarTableItem was not memoized, so every item re-rendered
  on each sidebar render (and the call sites pass freshly created inline
  callbacks). Wrap it in React.memo with a comparator that only inspects the
  data props and whether *this* item is the active one, so changing the active
  table re-renders just the two affected items instead of all of them. The
  comparator lives in src/utils/sidebarTableItem.ts and is unit tested.

- Multi-database connections: SidebarDatabaseItem only set its expanded state
  on mount, so picking a table in a different, collapsed database neither
  opened that database nor loaded its tables. It now auto-expands and lazily
  loads when it becomes the active database, mirroring SidebarSchemaItem.

- Scroll reveal: the scroll-into-view effect ran once, before a freshly
  expanded database/schema had mounted its (asynchronously loaded) table item,
  so scrolling upward to such a table silently did nothing. Retry across
  animation frames until the item is in the DOM, and build the lookup selector
  via a helper that escapes special characters in table/schema names.
@debba debba force-pushed the feat/quick-navigator branch from 19761ed to 60926f7 Compare May 29, 2026 13:17
@debba
Copy link
Copy Markdown
Collaborator Author

debba commented May 29, 2026

Hey @Lenobre, pushed a few fixes to the Quick Navigator branch, here's what changed:

The big one was the freeze when opening a result on connections with lots of tables (was testing with ~300). Turned out the sidebar table item wasn't memoized, so picking a table re-rendered every single row instead of just the two that actually change. Wrapped it in React.memo with a comparator that only looks at the data that matters and whether that specific row is the active one. Now switching tables only touches the two affected rows.

Second, on multi-database connections selecting a table in a different (collapsed) database didn't open that database. The schema item already auto-expanded when it became active, but the database item only did it on mount, so it never reacted afterwards. Made it behave like the schema one: auto-expand and lazy-load its tables when it becomes the active db.

Third, scrolling to the table worked going down but not up. The scroll ran once, before the freshly expanded db/schema had mounted its tables (they load async), so it found nothing and gave up. Now it retries over a few frames until the row is actually in the DOM. Also moved the selector into a small helper that escapes table/schema names, so weird names don't break the lookup.

Pulled the testable bits into utils/sidebarTableItem.ts and added unit tests for them. Also resolved the conflicts with main.

}

return (
<div key={`${item.type}-${item.schema || ""}-${item.name}`}>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

WARNING: Duplicate React keys for triggers/routines with same name

In PostgreSQL, trigger names are unique per table but not per schema. Two triggers with the same name on different tables in the same schema produce identical keys (trigger-<schema>-<name>), causing React warnings and incorrect rendering. Same-name routines of different types (e.g., FUNCTION vs PROCEDURE) also collide.

Suggested change
<div key={`${item.type}-${item.schema || ""}-${item.name}`}>
<div key={`${item.type}-${item.schema || ""}-${item.name}-${item.detail || ""}`}>

@debba debba merged commit 1802165 into main May 29, 2026
2 checks passed
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