Skip to content

Commit

Permalink
feat(core): init cmd+f in-text search function
Browse files Browse the repository at this point in the history
  • Loading branch information
JimmFly committed May 23, 2024
1 parent 6278523 commit b0cd32e
Show file tree
Hide file tree
Showing 6 changed files with 173 additions and 3 deletions.
134 changes: 134 additions & 0 deletions packages/frontend/core/src/components/affine/find-in-page/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import { cmdFind } from '@affine/electron-api';
import type React from 'react';
import { useDeferredValue, useEffect, useState } from 'react';

type Result = {
requestId: number;
activeMatchOrdinal: number;
matches: number;
finalUpdate: boolean;
};

export const FindInPage: React.FC = () => {
const [visible, setVisible] = useState(false);
const [searchText, setSearchText] = useState('');
const deferredSearchText = useDeferredValue(searchText);
const [result, setResult] = useState<Result | null>(null);

useEffect(() => {
const handleKeyDown = (event: KeyboardEvent) => {
if (event.key === 'f' && (event.ctrlKey || event.metaKey)) {
setVisible(true);
const input = document.getElementById('find-input') as HTMLInputElement;
input.focus();
input.select();
}
};

window.addEventListener('keydown', handleKeyDown);

return () => {
window.removeEventListener('keydown', handleKeyDown);
};
}, []);

useEffect(() => {
if (!visible) {
cmdFind?.stopFindInPage('clearSelection');
setSearchText('');
}
}, [visible]);

const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setSearchText(event.target.value);
};

const handleResult = (data: Result) => {
setResult(data);
};

const handleKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
if (event.key === 'Escape') {
cmdFind?.stopFindInPage('clearSelection');
setSearchText('');
setResult(null);
setVisible(false);
}
if (event.key === 'Enter') {
cmdFind?.findInPage(deferredSearchText);
cmdFind?.onFindInPageResult(handleResult);
}
};

const handleNextClick = () => {
cmdFind?.findInPage(deferredSearchText, { forward: true });
};

const handlePrevClick = () => {
cmdFind?.findInPage(deferredSearchText, { forward: false });
};

return (
<div
style={{
display: visible ? 'flex' : 'none',
position: 'fixed',
top: '10px',
left: '80%',
transform: 'translateX(-50%)',
zIndex: 99,
backgroundColor: 'white',
border: '1px solid #ccc',
borderRadius: '4px',
padding: '5px',
boxShadow: '0 2px 5px rgba(0, 0, 0, 0.3)',
}}
>
<input
id="find-input"
type="text"
placeholder="Find in page…"
value={searchText}
onChange={handleInputChange}
onKeyDown={handleKeyDown}
style={{
padding: '5px 10px',
marginRight: '5px',
border: '1px solid #ccc',
borderRadius: '4px',
}}
/>
{result ? (
<div>
{result?.activeMatchOrdinal}/{result?.matches}
</div>
) : null}
<button
onClick={handlePrevClick}
style={{
cursor: 'pointer',
padding: '5px 10px',
backgroundColor: '#007BFF',
color: '#ffffff',
border: 'none',
borderRadius: '4px',
}}
>
Prev
</button>
<button
onClick={handleNextClick}
style={{
cursor: 'pointer',
padding: '5px 10px',
backgroundColor: '#007BFF',
color: '#ffffff',
border: 'none',
borderRadius: '4px',
}}
>
Next
</button>
</div>
);
};
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Scrollable } from '@affine/component';
import { PageDetailSkeleton } from '@affine/component/page-detail-skeleton';
import { PageAIOnboarding } from '@affine/core/components/affine/ai-onboarding';
import { FindInPage } from '@affine/core/components/affine/find-in-page';
import { useAppSettingHelper } from '@affine/core/hooks/affine/use-app-setting-helper';
import type { PageRootService } from '@blocksuite/blocks';
import {
Expand Down Expand Up @@ -240,6 +241,7 @@ const DetailPageImpl = memo(function DetailPageImpl() {
<>
<ViewHeaderIsland>
<DetailPageHeader page={doc.blockSuiteDoc} workspace={workspace} />
{environment.isDesktop && <FindInPage />}
</ViewHeaderIsland>
<ViewBodyIsland>
<div className={styles.mainContainer}>
Expand Down
4 changes: 4 additions & 0 deletions packages/frontend/electron-api/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import type {
import type {
affine as exposedAffineGlobal,
appInfo as exposedAppInfo,
cmdFind as exposedCmdFind,
} from '@affine/electron/preload/electron-api';

type MainHandlers = typeof mainHandlers;
Expand Down Expand Up @@ -39,5 +40,8 @@ export const events = (globalThis as any).events as ClientEvents | null;
export const affine = (globalThis as any).affine as
| typeof exposedAffineGlobal
| null;
export const cmdFind = (globalThis as any).cmdFind as
| typeof exposedCmdFind
| null;

export type { UpdateMeta } from '@affine/electron/main/updater/event';
21 changes: 20 additions & 1 deletion packages/frontend/electron/src/main/main-window.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import assert from 'node:assert';
import { join } from 'node:path';

import type { CookiesSetDetails } from 'electron';
import { BrowserWindow, nativeTheme } from 'electron';
import { BrowserWindow, ipcMain, nativeTheme } from 'electron';
import electronWindowState from 'electron-window-state';

import { isLinux, isMacOS, isWindows } from '../shared/utils';
Expand Down Expand Up @@ -169,6 +169,25 @@ async function createWindow(additionalArguments: string[]) {
uiSubjects.onFullScreen$.next(false);
});

browserWindow.webContents.on('found-in-page', (_event, result) => {
const { requestId, activeMatchOrdinal, matches, finalUpdate } = result;
browserWindow.webContents.send('found-in-page-result', {
requestId,
activeMatchOrdinal,
matches,
finalUpdate,
});
});

ipcMain.on('find-in-page', (_event, text, options) => {
browserWindow.webContents.findInPage(text, options);
});

ipcMain.on('stop-find-in-page', (_event, action) => {
browserWindow.webContents.stopFindInPage(action);
});
// get-find-in-page-results

/**
* URL for main window.
*/
Expand Down
3 changes: 2 additions & 1 deletion packages/frontend/electron/src/preload/bootstrap.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { contextBridge } from 'electron';

import { affine, appInfo, getElectronAPIs } from './electron-api';
import { affine, appInfo, cmdFind, getElectronAPIs } from './electron-api';

const { apis, events } = getElectronAPIs();

Expand All @@ -10,6 +10,7 @@ contextBridge.exposeInMainWorld('events', events);

try {
contextBridge.exposeInMainWorld('affine', affine);
contextBridge.exposeInMainWorld('cmdFind', cmdFind);
} catch (error) {
console.error('Failed to expose affine APIs to window object!', error);
}
12 changes: 11 additions & 1 deletion packages/frontend/electron/src/preload/electron-api.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Please add modules to `external` in `rollupOptions` to avoid wrong bundling.
import type { EventBasedChannel } from 'async-call-rpc';
import { AsyncCall } from 'async-call-rpc';
import { ipcRenderer } from 'electron';
import { ipcRenderer, type IpcRendererEvent } from 'electron';

Check failure on line 4 in packages/frontend/electron/src/preload/electron-api.ts

View workflow job for this annotation

GitHub Actions / Lint

'IpcRendererEvent' is declared but its value is never read.
import { Subject } from 'rxjs';
import { z } from 'zod';

Expand Down Expand Up @@ -47,6 +47,16 @@ export const affine = {
},
};

export const cmdFind = {
findInPage: (text: string, options?: Electron.FindInPageOptions) =>
ipcRenderer.send('find-in-page', text, options),
stopFindInPage: (
action: 'clearSelection' | 'keepSelection' | 'activateSelection'
) => ipcRenderer.send('stop-find-in-page', action),
onFindInPageResult: (callBack: (data: any) => void) =>
ipcRenderer.on('found-in-page-result', (event, data) => callBack(data)),

Check failure on line 57 in packages/frontend/electron/src/preload/electron-api.ts

View workflow job for this annotation

GitHub Actions / Lint

'event' is declared but its value is never read.
};

export function getElectronAPIs() {
const mainAPIs = getMainAPIs();
const helperAPIs = getHelperAPIs();
Expand Down

0 comments on commit b0cd32e

Please sign in to comment.