Merge pull request #25808 from overleaf/mj-ide-full-project-search

[web] Editor redesign: Add full project search

GitOrigin-RevId: b4327c4ba0ddd7387ec8d6640e31200ca0fe4a6e
This commit is contained in:
Mathias Jakobsen
2025-06-02 15:27:18 +01:00
committed by Copybot
parent 3a96df4623
commit 48337b2e2c
11 changed files with 58 additions and 10 deletions

View File

@@ -996,6 +996,7 @@ module.exports = {
toastGenerators: [],
editorSidebarComponents: [],
fileTreeToolbarComponents: [],
fullProjectSearchPanel: [],
integrationPanelComponents: [],
referenceSearchSetting: [],
},

View File

@@ -1292,6 +1292,7 @@
"project_ownership_transfer_confirmation_2": "",
"project_renamed_or_deleted": "",
"project_renamed_or_deleted_detail": "",
"project_search": "",
"project_search_file_count": "",
"project_search_file_count_plural": "",
"project_search_result_count": "",

View File

@@ -10,6 +10,7 @@ export default /** @type {const} */ ([
'create_new_folder',
'delete',
'description',
'error',
'experiment',
'forum',
'help',
@@ -20,10 +21,10 @@ export default /** @type {const} */ ([
'picture_as_pdf',
'rate_review',
'report',
'search',
'settings',
'space_dashboard',
'table_chart',
'upload_file',
'web_asset',
'error',
])

View File

@@ -6,7 +6,7 @@ type SearchEventSegmentation = {
searchType: 'full-project'
} & (
| { method: 'keyboard' }
| { method: 'button'; location: 'toolbar' | 'search-form' }
| { method: 'button'; location: 'toolbar' | 'search-form' | 'rail' }
))
| ({
searchType: 'document'

View File

@@ -0,0 +1,19 @@
import { ElementType } from 'react'
import importOverleafModules from '../../../../macros/import-overleaf-module.macro'
const componentModule = importOverleafModules('fullProjectSearchPanel')[0] as
| {
import: { default: ElementType }
path: string
}
| undefined
export const FullProjectSearchPanel = () => {
if (!componentModule) {
return null
}
const FullProjectSearch = componentModule.import.default
return <FullProjectSearch />
}
export const hasFullProjectSearch = Boolean(componentModule)

View File

@@ -34,6 +34,11 @@ import OLTooltip from '@/features/ui/components/ol/ol-tooltip'
import OLIconButton from '@/features/ui/components/ol/ol-icon-button'
import { useChatContext } from '@/features/chat/context/chat-context'
import { useEditorAnalytics } from '@/shared/hooks/use-editor-analytics'
import {
FullProjectSearchPanel,
hasFullProjectSearch,
} from './full-project-search-panel'
import { sendSearchEvent } from '@/features/event-tracking/search-events'
type RailElement = {
icon: AvailableUnfilledIcon
@@ -106,6 +111,13 @@ export const RailLayout = () => {
title: t('file_tree'),
component: <FileTreeOutlinePanel />,
},
{
key: 'full-project-search',
icon: 'search',
title: t('project_search'),
component: <FullProjectSearchPanel />,
hide: !hasFullProjectSearch,
},
{
key: 'integrations',
icon: 'integration_instructions',
@@ -170,10 +182,17 @@ export const RailLayout = () => {
// Attempting to open a non-existent tab
return
}
const keyOrDefault = key ?? 'file-tree'
const keyOrDefault = (key ?? 'file-tree') as RailTabKey
// Change the selected tab and make sure it's open
openTab(keyOrDefault as RailTabKey)
openTab(keyOrDefault)
sendEvent('rail-click', { tab: keyOrDefault })
if (keyOrDefault === 'full-project-search') {
sendSearchEvent('search-open', {
searchType: 'full-project',
method: 'button',
location: 'rail',
})
}
if (key === 'chat') {
markMessagesAsRead()

View File

@@ -19,6 +19,7 @@ export type RailTabKey =
| 'review-panel'
| 'chat'
| 'errors'
| 'full-project-search'
export type RailModalKey = 'keyboard-shortcuts' | 'contact-us' | 'dictionary'

View File

@@ -36,7 +36,6 @@ import { getStoredSelection, setStoredSelection } from '../extensions/search'
import { debounce } from 'lodash'
import { EditorSelection, EditorState } from '@codemirror/state'
import { sendSearchEvent } from '@/features/event-tracking/search-events'
import { useIsNewEditorEnabled } from '@/features/ide-redesign/utils/new-editor-utils'
import { FullProjectSearchButton } from './full-project-search-button'
const MATCH_COUNT_DEBOUNCE_WAIT = 100 // the amount of ms to wait before counting matches
@@ -82,8 +81,6 @@ const CodeMirrorSearchForm: FC<React.PropsWithChildren> = () => {
const inputRef = useRef<HTMLInputElement | null>(null)
const replaceRef = useRef<HTMLInputElement | null>(null)
const newEditor = useIsNewEditorEnabled()
const handleInputRef = useCallback((node: HTMLInputElement) => {
inputRef.current = node
@@ -443,7 +440,7 @@ const CodeMirrorSearchForm: FC<React.PropsWithChildren> = () => {
</OLButton>
</OLButtonGroup>
{!newEditor && <FullProjectSearchButton query={query} />}
<FullProjectSearchButton query={query} />
{position !== null && (
<div className="ol-cm-search-form-position">

View File

@@ -12,6 +12,8 @@ import Close from '@/shared/components/close'
import useTutorial from '@/shared/hooks/promotions/use-tutorial'
import { useEditorContext } from '@/shared/context/editor-context'
import getMeta from '@/utils/meta'
import { useIsNewEditorEnabled } from '@/features/ide-redesign/utils/new-editor-utils'
import { useRailContext } from '@/features/ide-redesign/contexts/rail-context'
const PROMOTION_SIGNUP_CUT_OFF_DATE = new Date('2025-04-22T00:00:00Z')
@@ -19,6 +21,8 @@ export const FullProjectSearchButton = ({ query }: { query: SearchQuery }) => {
const view = useCodeMirrorViewContext()
const { t } = useTranslation()
const { setProjectSearchIsOpen } = useLayoutContext()
const newEditor = useIsNewEditorEnabled()
const { openTab } = useRailContext()
const ref = useRef<HTMLButtonElement>(null)
const { inactiveTutorials } = useEditorContext()
@@ -44,14 +48,18 @@ export const FullProjectSearchButton = ({ query }: { query: SearchQuery }) => {
}
const openFullProjectSearch = useCallback(() => {
setProjectSearchIsOpen(true)
if (newEditor) {
openTab('full-project-search')
} else {
setProjectSearchIsOpen(true)
}
closeSearchPanel(view)
window.setTimeout(() => {
window.dispatchEvent(
new CustomEvent('editor:full-project-search', { detail: query })
)
}, 200)
}, [setProjectSearchIsOpen, query, view])
}, [setProjectSearchIsOpen, query, view, newEditor, openTab])
const onClick = useCallback(() => {
sendSearchEvent('search-open', {

View File

@@ -1708,6 +1708,7 @@
"project_ownership_transfer_confirmation_2": "This action cannot be undone. The new owner will be notified and will be able to change project access settings (including removing your own access).",
"project_renamed_or_deleted": "Project Renamed or Deleted",
"project_renamed_or_deleted_detail": "This project has either been renamed or deleted by an external data source such as Dropbox. We dont want to delete your data on Overleaf, so this project still contains your history and collaborators. If the project has been renamed please look in your project list for a new project under the new name.",
"project_search": "Project search",
"project_search_file_count": "in __count__ file",
"project_search_file_count_plural": "in __count__ files",
"project_search_result_count": "__count__ result",