Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,7 @@ describe("detectLanguage", () => {

test("detects web files", () => {
expect(detectLanguage("index.html")).toBe("html");
expect(detectLanguage("page.astro")).toBe("html");
expect(detectLanguage("styles.css")).toBe("css");
expect(detectLanguage("styles.scss")).toBe("scss");
});
Expand Down
33 changes: 25 additions & 8 deletions apps/desktop/src/main/windows/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ app.on("child-process-gone", (_event, details) => {
export async function MainWindow() {
const savedWindowState = loadWindowState();
const initialBounds = getInitialWindowBounds(savedWindowState);
let persistedZoomLevel = savedWindowState?.zoomLevel;

const isDev = env.NODE_ENV === "development";
const workspaceName = isDev ? getEnvWorkspaceName() : undefined;
Expand Down Expand Up @@ -225,6 +226,7 @@ export async function MainWindow() {
// Gated by `initialized` so the initial maximize() doesn't immediately
// write isMaximized: true back to disk before the user touches the window.
let initialized = false;
let hasCompletedFirstLoad = false;
let saveTimeout: ReturnType<typeof setTimeout> | null = null;
const debouncedSave = () => {
if (!initialized || window.isDestroyed()) return;
Expand All @@ -235,29 +237,43 @@ export async function MainWindow() {
const bounds = isMaximized
? window.getNormalBounds()
: window.getBounds();
const zoomLevel = window.webContents.getZoomLevel();
saveWindowState({
x: bounds.x,
y: bounds.y,
width: bounds.width,
height: bounds.height,
isMaximized,
zoomLevel: window.webContents.getZoomLevel(),
zoomLevel,
});
persistedZoomLevel = zoomLevel;
}, 500);
};
window.on("move", debouncedSave);
window.on("resize", debouncedSave);
window.webContents.on("zoom-changed", () => {
setTimeout(() => {
if (window.isDestroyed()) return;
persistedZoomLevel = window.webContents.getZoomLevel();
debouncedSave();
}, 0);
});
Comment thread
coderabbitai[bot] marked this conversation as resolved.

window.webContents.once("did-finish-load", async () => {
window.webContents.on("did-finish-load", () => {
console.log("[main-window] Renderer loaded successfully");
if (initialBounds.isMaximized) {
window.maximize();

if (persistedZoomLevel !== undefined) {
window.webContents.setZoomLevel(persistedZoomLevel);
}
if (savedWindowState?.zoomLevel !== undefined) {
window.webContents.setZoomLevel(savedWindowState.zoomLevel);

if (!hasCompletedFirstLoad) {
if (initialBounds.isMaximized) {
window.maximize();
}
window.show();
initialized = true;
hasCompletedFirstLoad = true;
}
window.show();
initialized = true;
});

window.webContents.on(
Expand Down Expand Up @@ -295,6 +311,7 @@ export async function MainWindow() {
isMaximized,
zoomLevel,
});
persistedZoomLevel = zoomLevel;

browserManager.unregisterAll();
server.close();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,6 @@ import {
CommandPalette,
useCommandPalette,
} from "renderer/screens/main/components/CommandPalette";
import {
KeywordSearch,
useKeywordSearch,
} from "renderer/screens/main/components/KeywordSearch";
import { UnsavedChangesDialog } from "renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/UnsavedChangesDialog";
import { useWorkspaceFileEventBridge } from "renderer/screens/main/components/WorkspaceView/hooks/useWorkspaceFileEvents";
import { useWorkspaceRenameReconciliation } from "renderer/screens/main/components/WorkspaceView/hooks/useWorkspaceRenameReconciliation";
Expand Down Expand Up @@ -420,21 +416,10 @@ function WorkspacePage() {
workspaceId,
navigate,
});
const keywordSearch = useKeywordSearch({
workspaceId,
});
const handleQuickOpen = useCallback(() => {
keywordSearch.handleOpenChange(false);
commandPalette.toggle();
}, [commandPalette.toggle, keywordSearch.handleOpenChange]);
const handleKeywordSearch = useCallback(() => {
commandPalette.handleOpenChange(false);
keywordSearch.toggle();
}, [commandPalette.handleOpenChange, keywordSearch.toggle]);
}, [commandPalette.toggle]);
useAppHotkey("QUICK_OPEN", handleQuickOpen, undefined, [handleQuickOpen]);
useAppHotkey("KEYWORD_SEARCH", handleKeywordSearch, undefined, [
handleKeywordSearch,
]);

// Toggle changes sidebar (⌘L)
useAppHotkey("TOGGLE_SIDEBAR", () => toggleSidebar(), undefined, [
Expand Down Expand Up @@ -678,21 +663,6 @@ function WorkspacePage() {
: undefined
}
/>
<KeywordSearch
open={keywordSearch.open}
onOpenChange={keywordSearch.handleOpenChange}
query={keywordSearch.query}
onQueryChange={keywordSearch.setQuery}
filtersOpen={keywordSearch.filtersOpen}
onFiltersOpenChange={keywordSearch.setFiltersOpen}
includePattern={keywordSearch.includePattern}
onIncludePatternChange={keywordSearch.setIncludePattern}
excludePattern={keywordSearch.excludePattern}
onExcludePatternChange={keywordSearch.setExcludePattern}
isLoading={keywordSearch.isFetching}
searchResults={keywordSearch.searchResults}
onSelectMatch={keywordSearch.selectMatch}
/>
<UnsavedChangesDialog
open={pendingTabClose !== null}
onOpenChange={(open) => {
Expand Down
1 change: 1 addition & 0 deletions apps/desktop/src/shared/detect-language.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export function detectLanguage(filePath: string): string {
// Web
html: "html",
htm: "html",
astro: "html",
css: "css",
scss: "scss",
less: "less",
Expand Down
7 changes: 0 additions & 7 deletions apps/desktop/src/shared/hotkeys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -784,13 +784,6 @@ export const HOTKEYS = {
category: "Navigation",
description: "Search and open files in the current workspace",
}),
KEYWORD_SEARCH: defineHotkey({
keys: "meta+shift+f",
label: "Keyword Search",
category: "Navigation",
description:
"Search for keyword matches across files in the current workspace",
}),

// Chat
FIND_IN_CHAT: defineHotkey({
Expand Down
111 changes: 111 additions & 0 deletions packages/workspace-fs/src/search.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,4 +130,115 @@ describe("patchSearchIndexesForRoot", () => {
hiddenPath,
);
});

it("rebuilds search indexes after a directory rename", async () => {
const rootPath = await createTempRoot();
const oldDirectoryPath = path.join(rootPath, "old-dir");
const newDirectoryPath = path.join(rootPath, "new-dir");
const oldFilePath = path.join(oldDirectoryPath, "target.ts");
const newFilePath = path.join(newDirectoryPath, "target.ts");

await fs.mkdir(oldDirectoryPath, { recursive: true });
await fs.writeFile(oldFilePath, "export const target = 1;\n");

await searchFiles({
rootPath,
query: "old-dir/target.ts",
});

await fs.rename(oldDirectoryPath, newDirectoryPath);

patchSearchIndexesForRoot(rootPath, [
createPatchEvent({
kind: "rename",
absolutePath: newDirectoryPath,
oldAbsolutePath: oldDirectoryPath,
isDirectory: true,
}),
]);

const oldPathResults = await searchFiles({
rootPath,
query: "old-dir/target.ts",
});
const newPathResults = await searchFiles({
rootPath,
query: "new-dir/target.ts",
});

expect(
oldPathResults.some(
(result) => result.relativePath === "old-dir/target.ts",
),
).toEqual(false);
expect(newPathResults[0]?.absolutePath).toEqual(newFilePath);
expect(newPathResults[0]?.relativePath).toEqual("new-dir/target.ts");
});
});

describe("searchFiles", () => {
it("prioritizes exact filename matches ahead of fuzzy path matches", async () => {
const rootPath = await createTempRoot();
const exactMatchPath = path.join(rootPath, "WorkspaceFiles.tsx");
const fuzzyMatchPath = path.join(rootPath, "hooks", "useWorkspaceFiles.ts");

await fs.mkdir(path.dirname(fuzzyMatchPath), { recursive: true });
await fs.writeFile(exactMatchPath, "export const exact = true;\n");
await fs.writeFile(fuzzyMatchPath, "export const fuzzy = true;\n");

const results = await searchFiles({
rootPath,
query: "WorkspaceFiles.tsx",
limit: 5,
});

expect(results[0]?.absolutePath).toEqual(exactMatchPath);
expect(results).toHaveLength(1);

const fuzzyResults = await searchFiles({
rootPath,
query: "useWorkspaceFiles",
limit: 5,
});

expect(fuzzyResults[0]?.absolutePath).toEqual(fuzzyMatchPath);
});

it("normalizes exact relative path queries before lookup", async () => {
const rootPath = await createTempRoot();
const targetPath = path.join(rootPath, "src", "file.ts");

await fs.mkdir(path.dirname(targetPath), { recursive: true });
await fs.writeFile(targetPath, "export const value = true;\n");

const results = await searchFiles({
rootPath,
query: "./src/file.ts",
limit: 5,
});

expect(results[0]?.absolutePath).toEqual(targetPath);
expect(results[0]?.relativePath).toEqual("src/file.ts");
});

it("returns every compact path collision instead of dropping later entries", async () => {
const rootPath = await createTempRoot();
const nestedPath = path.join(rootPath, "foo", "bar.ts");
const flatPath = path.join(rootPath, "foo-bar.ts");

await fs.mkdir(path.dirname(nestedPath), { recursive: true });
await fs.writeFile(nestedPath, "export const nested = true;\n");
await fs.writeFile(flatPath, "export const flat = true;\n");

const results = await searchFiles({
rootPath,
query: "foobarts",
limit: 5,
});

expect(results.map((result) => result.absolutePath)).toEqual([
flatPath,
nestedPath,
]);
});
});
Loading
Loading