mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-11-14 22:23:42 +01:00
Merge pull request #35056 from overleaf/mj-command-palette-improvements
[web] Command palette improvements GitOrigin-RevId: 3d6e11697ed4726b7ecada1744d24a15291ad646
This commit is contained in:
committed by
Copybot
parent
4ef7324263
commit
912fb2dcd8
+6
-6
@@ -111,17 +111,17 @@ const CommandPaletteBody: FC<CommandPaletteBodyProps> = ({ show, onHide }) => {
|
||||
>
|
||||
<button
|
||||
role="menuitem"
|
||||
onMouseEnter={() => setSelectedIndex(index)}
|
||||
onMouseMove={() => setSelectedIndex(index)}
|
||||
onClick={() => runResult(result)}
|
||||
>
|
||||
<div className="command-palette-result-title">
|
||||
{result.title}
|
||||
{result.description && (
|
||||
<span className="command-palette-result-description">
|
||||
{result.description}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
{result.description && (
|
||||
<div className="command-palette-result-description">
|
||||
{result.description}
|
||||
</div>
|
||||
)}
|
||||
</button>
|
||||
</li>
|
||||
))}
|
||||
|
||||
+30
-10
@@ -1,7 +1,7 @@
|
||||
import useDebounce from '@/shared/hooks/use-debounce'
|
||||
import useCommandPaletteSources from './use-command-palette-sources'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { CommandPaletteSearchResult } from '../types'
|
||||
import { CommandPaletteSearchResult, CommandPaletteSource } from '../types'
|
||||
|
||||
const useCommandPaletteResults = (query: string) => {
|
||||
const debouncedQuery = useDebounce(query, 25)
|
||||
@@ -9,17 +9,37 @@ const useCommandPaletteResults = (query: string) => {
|
||||
const [results, setResults] = useState<CommandPaletteSearchResult[]>([])
|
||||
|
||||
useEffect(() => {
|
||||
if (debouncedQuery) {
|
||||
const res = sources.map(source => source.search(debouncedQuery))
|
||||
setResults(res.flat().sort((a, b) => b.score - a.score))
|
||||
} else {
|
||||
const res = sources
|
||||
.filter(s => 'defaults' in s)
|
||||
.map(source => source.defaults!())
|
||||
setResults(res.flat().sort((a, b) => b.score - a.score))
|
||||
}
|
||||
const res = getSourcesMatchingQuery(debouncedQuery, sources).map(
|
||||
({ source, query }) =>
|
||||
query ? source.search(query) : (source.defaults?.() ?? [])
|
||||
)
|
||||
setResults(res.flat().sort((a, b) => b.score - a.score))
|
||||
}, [debouncedQuery, sources])
|
||||
return results
|
||||
}
|
||||
|
||||
const getSourcesMatchingQuery = (
|
||||
query: string,
|
||||
sources: CommandPaletteSource[]
|
||||
): { query: string; source: CommandPaletteSource }[] => {
|
||||
// No query matches all sources
|
||||
if (!query) return sources.map(source => ({ source, query }))
|
||||
|
||||
// If the query matches a prefix, return all the sources with that prefix
|
||||
const matchingSources = sources.filter(
|
||||
source => source.prefix && query.startsWith(source.prefix)
|
||||
)
|
||||
if (matchingSources.length > 0) {
|
||||
return matchingSources.map(source => ({
|
||||
source,
|
||||
query: query.slice(source.prefix!.length).trim(),
|
||||
}))
|
||||
}
|
||||
|
||||
// otherwise return all sources that don't require a prefix match
|
||||
return sources
|
||||
.filter(source => !source.prefixRequired)
|
||||
.map(source => ({ source, query: query.trim() }))
|
||||
}
|
||||
|
||||
export default useCommandPaletteResults
|
||||
|
||||
+5
-2
@@ -2,13 +2,16 @@ import { useMemo } from 'react'
|
||||
import { CommandPaletteSource } from '../types'
|
||||
import useCommandRegistrySource from './use-command-registry-source'
|
||||
import useFileTreeCommandSource from './use-file-tree-command-source'
|
||||
import { useJumpToLineCommandSource } from './use-jump-to-line-command-source'
|
||||
|
||||
const useCommandPaletteSources = (): CommandPaletteSource[] => {
|
||||
const fileTreeSource = useFileTreeCommandSource()
|
||||
const commandRegistrySource = useCommandRegistrySource()
|
||||
const jumpToLineSource = useJumpToLineCommandSource()
|
||||
|
||||
const sources = useMemo(
|
||||
() => [fileTreeSource, commandRegistrySource],
|
||||
[fileTreeSource, commandRegistrySource]
|
||||
() => [fileTreeSource, commandRegistrySource, jumpToLineSource],
|
||||
[fileTreeSource, commandRegistrySource, jumpToLineSource]
|
||||
)
|
||||
return sources
|
||||
}
|
||||
|
||||
+77
-33
@@ -5,42 +5,85 @@ import {
|
||||
Command,
|
||||
useCommandRegistry,
|
||||
} from '@/features/ide-react/context/command-registry-context'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
const ENABLED_COMMANDS: string[] = [
|
||||
'new_file',
|
||||
'new_folder',
|
||||
'upload_file',
|
||||
'open-settings',
|
||||
'show_version_history',
|
||||
'word_count',
|
||||
'view-pdf-presentation-mode',
|
||||
'comment',
|
||||
'compile',
|
||||
'stop-compile',
|
||||
'recompile-from-scratch',
|
||||
'synctex-sync-to-pdf',
|
||||
'synctex-sync-to-code',
|
||||
'insert-inline-math',
|
||||
'insert-display-math',
|
||||
'insert-figure-from-computer',
|
||||
'insert-figure-from-project-files',
|
||||
'insert-figure-from-another-project',
|
||||
'insert-figure-from-url',
|
||||
'insert-table',
|
||||
'insert-citation',
|
||||
'insert-link',
|
||||
'insert-cross-reference',
|
||||
]
|
||||
type CommandGroup = {
|
||||
label: string
|
||||
actions: string[]
|
||||
}
|
||||
|
||||
const useCommandRegistrySource = (): CommandPaletteSource => {
|
||||
const { t } = useTranslation()
|
||||
const elements: (CommandGroup | string)[] = useMemo(
|
||||
() => [
|
||||
{
|
||||
label: t('file'),
|
||||
actions: ['new_file', 'new_folder', 'upload_file'],
|
||||
},
|
||||
{
|
||||
label: t('view'),
|
||||
actions: [
|
||||
'change-layout-side-by-side',
|
||||
'change-layout-editor-only',
|
||||
'change-layout-pdf-only',
|
||||
'change-layout-detached-pdf',
|
||||
'change-layout-focus-mode',
|
||||
],
|
||||
},
|
||||
{
|
||||
label: t('insert'),
|
||||
actions: [
|
||||
'insert-inline-math',
|
||||
'insert-display-math',
|
||||
'insert-figure-from-computer',
|
||||
'insert-figure-from-project-files',
|
||||
'insert-figure-from-another-project',
|
||||
'insert-figure-from-url',
|
||||
'insert-table',
|
||||
'insert-citation',
|
||||
'insert-link',
|
||||
'insert-cross-reference',
|
||||
],
|
||||
},
|
||||
'open-settings',
|
||||
'show_version_history',
|
||||
'word_count',
|
||||
'view-pdf-presentation-mode',
|
||||
'comment',
|
||||
'compile',
|
||||
'stop-compile',
|
||||
'recompile-from-scratch',
|
||||
'synctex-sync-to-pdf',
|
||||
'synctex-sync-to-code',
|
||||
'close-tab',
|
||||
'close-other-tabs',
|
||||
],
|
||||
[t]
|
||||
)
|
||||
const { registry } = useCommandRegistry()
|
||||
|
||||
const commands = useMemo(() => {
|
||||
const enabled = new Set(ENABLED_COMMANDS)
|
||||
return [...registry.values()].filter(
|
||||
c => enabled.has(c.id) && !c.disabled && c.handler
|
||||
)
|
||||
}, [registry])
|
||||
const ret: Command[] = []
|
||||
for (const element of elements) {
|
||||
if (typeof element === 'string') {
|
||||
const command = registry.get(element)
|
||||
if (command && !command.disabled && command.handler) {
|
||||
ret.push(command)
|
||||
}
|
||||
} else {
|
||||
for (const action of element.actions) {
|
||||
const command = registry.get(action)
|
||||
if (command && !command.disabled && command.handler) {
|
||||
ret.push({
|
||||
...command,
|
||||
label: `${element.label}: ${command.label}`,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}, [registry, elements])
|
||||
|
||||
const defaults = useCallback((): CommandPaletteSearchResult[] => {
|
||||
return commands.map(command => ({
|
||||
@@ -53,7 +96,7 @@ const useCommandRegistrySource = (): CommandPaletteSource => {
|
||||
const index = useMemo(() => {
|
||||
const miniSearch = new MiniSearch<Command>({
|
||||
fields: ['label'],
|
||||
storeFields: ['id'],
|
||||
storeFields: ['id', 'label'],
|
||||
idField: 'id',
|
||||
})
|
||||
miniSearch.addAll(commands)
|
||||
@@ -63,17 +106,18 @@ const useCommandRegistrySource = (): CommandPaletteSource => {
|
||||
return useMemo<CommandPaletteSource>(
|
||||
() => ({
|
||||
id: 'command-registry',
|
||||
prefix: '>',
|
||||
search(query) {
|
||||
const results = index.search(query, {
|
||||
prefix: true,
|
||||
fuzzy: term => (term.length > 3 ? 0.2 : false),
|
||||
})
|
||||
return results.flatMap(({ id, score }) => {
|
||||
return results.flatMap(({ id, label, score }) => {
|
||||
const command = registry.get(id)
|
||||
if (!command?.handler) return []
|
||||
return [
|
||||
{
|
||||
title: command.label,
|
||||
title: label,
|
||||
onSelect: () => command.handler!({ location: 'command-palette' }),
|
||||
score,
|
||||
},
|
||||
|
||||
+30
-6
@@ -9,9 +9,12 @@ import { useEditorManagerContext } from '@/features/ide-react/context/editor-man
|
||||
|
||||
type FlatFileTree = { path: string; name: string; id: string }[]
|
||||
|
||||
const FILE_POSITION_REGEX = /:(\d+)(?:,(\d+))?$/
|
||||
|
||||
const useFileTreeCommandSource = (): CommandPaletteSource => {
|
||||
const { fileTreeData } = useFileTreeData()
|
||||
const { openDocWithId, openFileWithId } = useEditorManagerContext()
|
||||
|
||||
const flatFileTree = useMemo(
|
||||
() => flattenFileTree(fileTreeData),
|
||||
[fileTreeData]
|
||||
@@ -22,7 +25,11 @@ const useFileTreeCommandSource = (): CommandPaletteSource => {
|
||||
)
|
||||
|
||||
const onSelect = useCallback(
|
||||
async (id: string) => {
|
||||
async (
|
||||
id: string,
|
||||
line: number | undefined,
|
||||
column: number | undefined
|
||||
) => {
|
||||
if (!fileTreeData) {
|
||||
return
|
||||
}
|
||||
@@ -31,7 +38,10 @@ const useFileTreeCommandSource = (): CommandPaletteSource => {
|
||||
return
|
||||
}
|
||||
if (file.type === 'doc') {
|
||||
await openDocWithId(file.entity._id)
|
||||
await openDocWithId(file.entity._id, {
|
||||
gotoLine: line,
|
||||
gotoColumn: column,
|
||||
})
|
||||
} else if (file.type === 'fileRef') {
|
||||
openFileWithId(file.entity._id)
|
||||
} else {
|
||||
@@ -49,8 +59,9 @@ const useFileTreeCommandSource = (): CommandPaletteSource => {
|
||||
.slice(0, 10)
|
||||
.map(({ path, name, id }) => ({
|
||||
title: name,
|
||||
description: path === name ? undefined : path,
|
||||
onSelect: () => onSelect(id),
|
||||
description:
|
||||
path === name ? undefined : path.split('/').slice(0, -1).join('/'),
|
||||
onSelect: () => onSelect(id, undefined, undefined),
|
||||
score: 1,
|
||||
}))
|
||||
|
||||
@@ -64,14 +75,27 @@ const useFileTreeCommandSource = (): CommandPaletteSource => {
|
||||
if (!index) {
|
||||
return []
|
||||
}
|
||||
const fileLocationMatch = query.match(FILE_POSITION_REGEX)
|
||||
let line: number | undefined = undefined
|
||||
let column: number | undefined = undefined
|
||||
if (fileLocationMatch) {
|
||||
line = parseInt(fileLocationMatch[1], 10)
|
||||
column = fileLocationMatch[2]
|
||||
? parseInt(fileLocationMatch[2], 10)
|
||||
: undefined
|
||||
query = query.slice(0, fileLocationMatch.index)
|
||||
}
|
||||
|
||||
const result = index.search(query, {
|
||||
prefix: true,
|
||||
fuzzy: term => (term.length > 3 ? 0.2 : false),
|
||||
})
|
||||
|
||||
return result.map(({ path, name, id, score }) => ({
|
||||
title: name,
|
||||
description: path === name ? undefined : path,
|
||||
onSelect: () => onSelect(id),
|
||||
description:
|
||||
path === name ? undefined : path.split('/').slice(0, -1).join('/'),
|
||||
onSelect: () => onSelect(id, line, column),
|
||||
score,
|
||||
}))
|
||||
},
|
||||
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
import { useEditorManagerContext } from '@/features/ide-react/context/editor-manager-context'
|
||||
import { CommandPaletteSource } from '../types'
|
||||
import { useMemo } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
const JUMP_REGEX = /^(\d+)(?:,(\d+))?$/
|
||||
|
||||
export const useJumpToLineCommandSource = (): CommandPaletteSource => {
|
||||
const { jumpToLine } = useEditorManagerContext()
|
||||
const { t } = useTranslation()
|
||||
return useMemo(
|
||||
() => ({
|
||||
id: 'jump-to-line-source',
|
||||
prefix: ':',
|
||||
prefixRequired: true,
|
||||
search: (query: string) => {
|
||||
const match = query.match(JUMP_REGEX)
|
||||
if (!match) {
|
||||
return []
|
||||
}
|
||||
const line = parseInt(match[1], 10)
|
||||
const column = match[2] ? parseInt(match[2], 10) : undefined
|
||||
|
||||
return [
|
||||
{
|
||||
onSelect: () => jumpToLine({ gotoLine: line, gotoColumn: column }),
|
||||
title:
|
||||
column != null
|
||||
? t('jump_to_line_x_column_y', {
|
||||
line,
|
||||
column,
|
||||
})
|
||||
: t('jump_to_line_x', {
|
||||
line,
|
||||
}),
|
||||
score: 1,
|
||||
},
|
||||
]
|
||||
},
|
||||
}),
|
||||
[jumpToLine, t]
|
||||
)
|
||||
}
|
||||
@@ -9,4 +9,6 @@ export type CommandPaletteSource = {
|
||||
id: string
|
||||
search(query: string): CommandPaletteSearchResult[]
|
||||
defaults?(): CommandPaletteSearchResult[]
|
||||
prefix?: string
|
||||
prefixRequired?: boolean
|
||||
}
|
||||
|
||||
+16
-5
@@ -1,4 +1,4 @@
|
||||
import { useRef, useEffect } from 'react'
|
||||
import { useRef, useEffect, useCallback } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useFileTreeCreateName } from '../../contexts/file-tree-create-name'
|
||||
import {
|
||||
@@ -36,6 +36,7 @@ export default function FileTreeCreateNameInput({
|
||||
|
||||
// the value is stored in a context provider, so it's available elsewhere in the form
|
||||
const { name, setName, touchedName, validName } = useFileTreeCreateName()
|
||||
const nameFocusedOnce = useRef(false)
|
||||
|
||||
// focus the first part of the filename if needed
|
||||
const inputRef = useRef<HTMLInputElement>(null)
|
||||
@@ -45,15 +46,24 @@ export default function FileTreeCreateNameInput({
|
||||
window.requestAnimationFrame(() => {
|
||||
if (inputRef.current) {
|
||||
inputRef.current.focus()
|
||||
inputRef.current.setSelectionRange(
|
||||
0,
|
||||
inputRef.current.value.lastIndexOf('.')
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
}, [focusName])
|
||||
|
||||
const onFocus = useCallback(() => {
|
||||
if (nameFocusedOnce.current) return
|
||||
if (!focusName) return
|
||||
window.requestAnimationFrame(() => {
|
||||
if (!inputRef.current) return
|
||||
nameFocusedOnce.current = true
|
||||
inputRef.current.setSelectionRange(
|
||||
0,
|
||||
inputRef.current.value.lastIndexOf('.')
|
||||
)
|
||||
})
|
||||
}, [focusName])
|
||||
|
||||
return (
|
||||
<OLFormGroup controlId="new-doc-name" className={classes.formGroup}>
|
||||
<OLFormLabel>{label || t('file_name')}</OLFormLabel>
|
||||
@@ -65,6 +75,7 @@ export default function FileTreeCreateNameInput({
|
||||
onChange={event => setName(event.target.value)}
|
||||
ref={inputRef}
|
||||
disabled={inFlight}
|
||||
onFocus={onFocus}
|
||||
/>
|
||||
{touchedName && !validName && (
|
||||
<div className="notification-list">
|
||||
|
||||
+69
@@ -8,6 +8,10 @@ import {
|
||||
import ChangeLayoutOptions from './change-layout-options'
|
||||
import MaterialIcon from '@/shared/components/material-icon'
|
||||
import OLTooltip from '@/shared/components/ol/ol-tooltip'
|
||||
import { useCommandProvider } from '../../hooks/use-command-provider'
|
||||
import { useFeatureFlag } from '@/shared/context/split-test-context'
|
||||
import { useEditorAnalytics } from '@/shared/hooks/use-editor-analytics'
|
||||
import { useLayoutContext } from '@/shared/context/layout-context'
|
||||
|
||||
export default function ChangeLayoutButton() {
|
||||
const { t } = useTranslation()
|
||||
@@ -17,6 +21,71 @@ export default function ChangeLayoutButton() {
|
||||
'ide-redesign-toolbar-button-icon'
|
||||
)
|
||||
|
||||
const {
|
||||
detachIsLinked,
|
||||
detachRole,
|
||||
handleChangeLayout,
|
||||
handleDetach,
|
||||
focusMode,
|
||||
setFocusMode,
|
||||
} = useLayoutContext()
|
||||
|
||||
const { sendEvent } = useEditorAnalytics()
|
||||
const focusModeEnabled = useFeatureFlag('focus-mode')
|
||||
|
||||
const detachable = 'BroadcastChannel' in window
|
||||
|
||||
const waitingForDetachedLink = !detachIsLinked && detachRole === 'detacher'
|
||||
|
||||
useCommandProvider(
|
||||
() => [
|
||||
{
|
||||
id: 'change-layout-side-by-side',
|
||||
handler: () => handleChangeLayout('sideBySide'),
|
||||
label: t('split_view'),
|
||||
},
|
||||
{
|
||||
id: 'change-layout-editor-only',
|
||||
handler: () => handleChangeLayout('flat', 'editor'),
|
||||
label: t('editor_only'),
|
||||
},
|
||||
{
|
||||
id: 'change-layout-pdf-only',
|
||||
handler: () => handleChangeLayout('flat', 'pdf'),
|
||||
label: t('pdf_only'),
|
||||
},
|
||||
{
|
||||
id: 'change-layout-detached-pdf',
|
||||
handler: () => handleDetach(),
|
||||
disabled: !detachable || waitingForDetachedLink,
|
||||
label: t('pdf_in_different_tab'),
|
||||
},
|
||||
...(focusModeEnabled
|
||||
? [
|
||||
{
|
||||
id: 'change-layout-focus-mode',
|
||||
handler: () => {
|
||||
setFocusMode(!focusMode)
|
||||
sendEvent('focus-mode-toggle', { focusMode: !focusMode })
|
||||
},
|
||||
label: t('toggle_focus_mode'),
|
||||
},
|
||||
]
|
||||
: []),
|
||||
],
|
||||
[
|
||||
handleChangeLayout,
|
||||
t,
|
||||
detachable,
|
||||
handleDetach,
|
||||
waitingForDetachedLink,
|
||||
focusModeEnabled,
|
||||
focusMode,
|
||||
setFocusMode,
|
||||
sendEvent,
|
||||
]
|
||||
)
|
||||
|
||||
return (
|
||||
<div className="ide-redesign-toolbar-button-container">
|
||||
<OLDropdown className="toolbar-item layout-dropdown" align="end">
|
||||
|
||||
@@ -10,6 +10,8 @@ import { useCallback, useMemo, useState } from 'react'
|
||||
import { throttle } from 'lodash'
|
||||
import { debugConsole } from '@/utils/debugging'
|
||||
import classNames from 'classnames'
|
||||
import { useCommandProvider } from '@/features/ide-react/hooks/use-command-provider'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
export const TabsContainer = () => {
|
||||
const {
|
||||
@@ -20,9 +22,11 @@ export const TabsContainer = () => {
|
||||
makeTabPermanent,
|
||||
setContextMenuTarget,
|
||||
setHeaderSlot,
|
||||
closeOtherTabs,
|
||||
} = useTabsContext()
|
||||
const { openEntity } = useFileTreeOpenContext()
|
||||
const [hovered, setHovered] = useState<boolean>(false)
|
||||
const { t } = useTranslation()
|
||||
|
||||
const openContextMenu = useCallback(
|
||||
(coords: { top: number; left: number }, tabId: string) => {
|
||||
@@ -86,6 +90,32 @@ export const TabsContainer = () => {
|
||||
}
|
||||
}, [])
|
||||
|
||||
useCommandProvider(() => {
|
||||
if (tabs.length === 0) {
|
||||
return
|
||||
}
|
||||
if (!openEntity?.entity._id) {
|
||||
return
|
||||
}
|
||||
|
||||
return [
|
||||
{
|
||||
id: 'close-tab',
|
||||
label: t('close_tab'),
|
||||
handler: () => {
|
||||
closeTab(openEntity.entity._id)
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'close-other-tabs',
|
||||
label: t('close_other_tabs'),
|
||||
handler: () => {
|
||||
closeOtherTabs(openEntity.entity._id)
|
||||
},
|
||||
},
|
||||
]
|
||||
}, [tabs, t, openEntity, closeTab, closeOtherTabs])
|
||||
|
||||
return (
|
||||
<div className="editor-tabs-container">
|
||||
<div className="review-panel-header-slot" ref={setHeaderSlot} />
|
||||
|
||||
+10
-14
@@ -102,7 +102,7 @@ export const useToolbarMenuBarEditorCommands = () => {
|
||||
{
|
||||
id: 'insert-inline-math',
|
||||
menuLabel: t('inline_math'),
|
||||
label: t('insert_inline_math'),
|
||||
label: t('inline_math'),
|
||||
handler: () => {
|
||||
commands.wrapInInlineMath(view)
|
||||
view.focus()
|
||||
@@ -111,7 +111,7 @@ export const useToolbarMenuBarEditorCommands = () => {
|
||||
{
|
||||
id: 'insert-display-math',
|
||||
menuLabel: t('display_math'),
|
||||
label: t('insert_display_math'),
|
||||
label: t('display_math'),
|
||||
handler: () => {
|
||||
commands.wrapInDisplayMath(view)
|
||||
view.focus()
|
||||
@@ -119,7 +119,7 @@ export const useToolbarMenuBarEditorCommands = () => {
|
||||
},
|
||||
{
|
||||
menuLabel: t('upload_from_computer'),
|
||||
label: t('insert_figure_from_computer'),
|
||||
label: t('figure_from_computer'),
|
||||
id: 'insert-figure-from-computer',
|
||||
handler: () => {
|
||||
openFigureModal(FigureModalSource.FILE_UPLOAD)
|
||||
@@ -127,7 +127,7 @@ export const useToolbarMenuBarEditorCommands = () => {
|
||||
},
|
||||
{
|
||||
menuLabel: t('from_project_files'),
|
||||
label: t('insert_figure_from_project_files'),
|
||||
label: t('figure_from_project_files'),
|
||||
id: 'insert-figure-from-project-files',
|
||||
handler: () => {
|
||||
openFigureModal(FigureModalSource.FILE_TREE)
|
||||
@@ -149,7 +149,7 @@ export const useToolbarMenuBarEditorCommands = () => {
|
||||
return [
|
||||
{
|
||||
menuLabel: t('from_another_project'),
|
||||
label: t('insert_figure_from_another_project'),
|
||||
label: t('figure_from_another_project'),
|
||||
id: 'insert-figure-from-another-project',
|
||||
handler: () => {
|
||||
openFigureModal(FigureModalSource.OTHER_PROJECT)
|
||||
@@ -179,7 +179,7 @@ export const useToolbarMenuBarEditorCommands = () => {
|
||||
return [
|
||||
{
|
||||
menuLabel: t('from_url'),
|
||||
label: t('insert_figure_from_url'),
|
||||
label: t('figure_from_url'),
|
||||
id: 'insert-figure-from-url',
|
||||
handler: () => {
|
||||
openFigureModal(FigureModalSource.FROM_URL)
|
||||
@@ -206,8 +206,7 @@ export const useToolbarMenuBarEditorCommands = () => {
|
||||
return [
|
||||
{
|
||||
id: 'insert-table',
|
||||
menuLabel: t('table'),
|
||||
label: t('insert_table'),
|
||||
label: t('table'),
|
||||
handler: () => {
|
||||
commands.insertTable(view, 3, 3)
|
||||
view.focus()
|
||||
@@ -215,8 +214,7 @@ export const useToolbarMenuBarEditorCommands = () => {
|
||||
},
|
||||
{
|
||||
id: 'insert-citation',
|
||||
menuLabel: t('citation'),
|
||||
label: t('insert_citation'),
|
||||
label: t('citation'),
|
||||
handler: () => {
|
||||
commands.insertCite(view)
|
||||
view.focus()
|
||||
@@ -224,8 +222,7 @@ export const useToolbarMenuBarEditorCommands = () => {
|
||||
},
|
||||
{
|
||||
id: 'insert-link',
|
||||
menuLabel: t('link'),
|
||||
label: t('insert_link'),
|
||||
label: t('link'),
|
||||
handler: () => {
|
||||
commands.wrapInHref(view)
|
||||
view.focus()
|
||||
@@ -233,8 +230,7 @@ export const useToolbarMenuBarEditorCommands = () => {
|
||||
},
|
||||
{
|
||||
id: 'insert-cross-reference',
|
||||
menuLabel: t('cross_reference'),
|
||||
label: t('insert_cross_reference'),
|
||||
label: t('cross_reference'),
|
||||
handler: () => {
|
||||
commands.insertRef(view)
|
||||
view.focus()
|
||||
|
||||
Reference in New Issue
Block a user