fix(sidebar): preserve results when enabling regex search

This commit is contained in:
OOMEcho
2026-09-29 11:56:31 +08:00
committed by GitHub
parent 77c542248d
commit 03b3f4c4fa
3 changed files with 22 additions and 3 deletions
@@ -25,7 +25,7 @@ import {
localTableSearchParentTypes,
} from "@/lib/sidebar/sidebarSearchTree";
import { createSidebarLabelMatcher } from "@/lib/sidebar/sidebarSearch";
import { collectSidebarRegexIndexScopes, resolveSidebarRemoteSearchQuery, resolveSidebarSearchDispatchMode } from "@/lib/sidebar/sidebarRegexSearchIndex";
import { collectSidebarRegexIndexScopes, resolveSidebarRemoteSearchQuery, resolveSidebarSearchDispatchMode, shouldRestoreTrackedSidebarSearchTargetsInRegexMode } from "@/lib/sidebar/sidebarRegexSearchIndex";
import { needsSidebarObjectGroupDiscovery } from "@/lib/sidebar/sidebarSearchDiscovery";
import { createSidebarSearchExpansionState } from "@/lib/sidebar/sidebarSearchExpansionState";
import { createSidebarSearchLoadingTracker } from "@/lib/sidebar/sidebarSearchLoadingTracker";
@@ -301,7 +301,10 @@ watch([deferredSearchQuery, regexMode], ([newQuery, isRegexMode], [oldQuery, was
if (dispatchMode === "regex") {
// Regex search is a read-only projection over live nodes and the local
// table index. It must never trigger ensureConnected/listTables.
const restoreTasks = restoreTrackedSearchTargets();
// Keep results already loaded by an ordinary query until the regex is
// cleared. Restoring them here can empty the tree when this connection has
// no complete local table index yet (for example: "user" -> Regex mode).
const restoreTasks = shouldRestoreTrackedSidebarSearchTargetsInRegexMode(newQuery) ? restoreTrackedSearchTargets() : [];
const searchGeneration = sidebarSearchLoadingTracker.begin();
isSidebarSearchLoading.value = true;
void Promise.allSettled([loadRegexTableSearchIndexes(), runSidebarSearchTasks(restoreTasks)])
@@ -106,3 +106,13 @@ export function resolveSidebarSearchDispatchMode(transition: SidebarSearchTransi
if (transition.wasRegexMode && !transition.query) return "none";
return "ordinary";
}
/**
* Keep an ordinary-search projection available while a non-empty regex is
* filtering it. Restoring those tracked nodes on the mode switch can remove
* the only currently loaded matches before a local table index exists. The
* projection is restored once the regex query is cleared.
*/
export function shouldRestoreTrackedSidebarSearchTargetsInRegexMode(query: string): boolean {
return query.length === 0;
}
+7 -1
View File
@@ -1,6 +1,6 @@
import { strict as assert } from "node:assert";
import { test, vi } from "vitest";
import { collectSidebarRegexIndexScopes, regexTableSearchParents, resolveSidebarRemoteSearchQuery, resolveSidebarSearchDispatchMode } from "../../apps/desktop/src/lib/sidebar/sidebarRegexSearchIndex.ts";
import { collectSidebarRegexIndexScopes, regexTableSearchParents, resolveSidebarRemoteSearchQuery, resolveSidebarSearchDispatchMode, shouldRestoreTrackedSidebarSearchTargetsInRegexMode } from "../../apps/desktop/src/lib/sidebar/sidebarRegexSearchIndex.ts";
import type { SidebarRegexIndexScope } from "../../apps/desktop/src/lib/sidebar/sidebarSearchTree.ts";
import type { TableInfo, TreeNode } from "../../apps/desktop/src/types/database.ts";
@@ -40,6 +40,12 @@ test("local search on keeps ordinary queries local and local search off enables
assert.equal(resolveSidebarSearchDispatchMode({ query: "", regexMode: false, wasRegexMode: false }, { localSearchEnabled: false }), "ordinary");
});
test("keeps ordinary search results available until the regex query is cleared", () => {
assert.equal(shouldRestoreTrackedSidebarSearchTargetsInRegexMode("user"), false);
assert.equal(shouldRestoreTrackedSidebarSearchTargetsInRegexMode("user|role"), false);
assert.equal(shouldRestoreTrackedSidebarSearchTargetsInRegexMode(""), true);
});
test("collects manifest scopes and live-tree backfill scopes through the read-only reader", async () => {
const manifestScope = scope("conn-1:app:public:__tables", { path: [{ id: "conn-1", label: "c", type: "connection", connectionId: "conn-1" }] });
const live: TreeNode[] = [