[web] Add editor commands to command registry and toolbar (#24538)

* [web] Add editor commands to command registry and toolbar

* [web] Omit empty groups

* [web] Editor redesign: Move toolbar commands to custom hook

* [web] Disable editor commands when editor is not visible

GitOrigin-RevId: be9f4060fc44e51223e16860fdcf6698c927998c
This commit is contained in:
Mathias Jakobsen
2025-04-09 14:44:11 +01:00
committed by Copybot
parent 4ba0e97b95
commit bdf0194fc8
6 changed files with 471 additions and 91 deletions

View File

@@ -180,7 +180,9 @@
"blank_project": "",
"blocked_filename": "",
"blog": "",
"bold": "",
"browser": "",
"bullet_list": "",
"buy_licenses": "",
"buy_more_licenses": "",
"by_subscribing_you_agree_to_our_terms_of_service": "",
@@ -241,6 +243,7 @@
"choose_from_group_members": "",
"choose_how_you_search_your_references": "",
"choose_which_experiments": "",
"citation": "",
"clear_cached_files": "",
"clear_search": "",
"click_here_to_view_sl_in_lng": "",
@@ -334,6 +337,7 @@
"create_project_in_github": "",
"created": "",
"created_at": "",
"cross_reference": "",
"current_file": "",
"current_password": "",
"currently_seeing_only_24_hrs_history": "",
@@ -347,6 +351,7 @@
"dark_mode": "",
"date_and_owner": "",
"dealing_with_errors": "",
"decrease_indent": "",
"delete": "",
"delete_account": "",
"delete_account_confirmation_label": "",
@@ -401,6 +406,7 @@
"discover_the_fastest_way_to_search_and_cite": "",
"dismiss_error_popup": "",
"display_deleted_user": "",
"display_math": "",
"do_you_need_edit_access": "",
"do_you_want_to_change_your_primary_email_address_to": "",
"do_you_want_to_overwrite_it": "",
@@ -537,6 +543,7 @@
"fast": "",
"fast_draft": "",
"features_like_track_changes": "",
"figure": "",
"file": "",
"file_action_created": "",
"file_action_deleted": "",
@@ -557,6 +564,7 @@
"files_cannot_include_invalid_characters": "",
"files_selected": "",
"filter_projects": "",
"find": "",
"find_out_more": "",
"find_out_more_about_institution_login": "",
"find_out_more_about_the_file_outline": "",
@@ -776,7 +784,9 @@
"include_results_from_your_reference_manager": "",
"include_results_from_your_x_account": "",
"include_the_error_message_and_ai_response": "",
"increase_indent": "",
"increased_compile_timeout": "",
"inline_math": "",
"inr_discount_modal_info": "",
"inr_discount_modal_title": "",
"insert": "",
@@ -827,6 +837,7 @@
"it_looks_like_that_didnt_work_you_can_try_again_or_get_in_touch": "",
"it_looks_like_your_account_is_billed_manually": "",
"it_looks_like_your_payment_details_are_missing_please_update_your_billing_information": "",
"italics": "",
"join_beta_program": "",
"join_now": "",
"join_overleaf_labs": "",
@@ -974,6 +985,7 @@
"managers_management": "",
"managing_your_subscription": "",
"marked_as_resolved": "",
"math": "",
"math_display": "",
"math_inline": "",
"maximum_files_uploaded_together": "",
@@ -1075,6 +1087,7 @@
"notification_project_invite_accepted_message": "",
"notification_project_invite_message": "",
"number_of_users": "",
"numbered_list": "",
"oauth_orcid_description": "",
"of": "",
"off": "",
@@ -1137,6 +1150,7 @@
"papers_sync_description": "",
"papers_upgrade_prompt_content": "",
"papers_upgrade_prompt_title": "",
"paragraph_styles": "",
"partial_outline_warning": "",
"password": "",
"password_managed_externally": "",
@@ -1300,6 +1314,7 @@
"recurly_email_updated": "",
"redirect_to_editor": "",
"redirect_url": "",
"redo": "",
"reduce_costs_group_licenses": "",
"reference_error_relink_hint": "",
"reference_manager_searched_groups": "",
@@ -1637,6 +1652,7 @@
"switch_to_old_editor": "",
"switch_to_pdf": "",
"switch_to_standard_plan": "",
"symbol": "",
"symbol_palette": "",
"sync": "",
"sync_dropbox_github": "",
@@ -1647,6 +1663,7 @@
"syntax_validation": "",
"tab_connecting": "",
"tab_no_longer_connected": "",
"table": "",
"table_generator": "",
"tag_color": "",
"tag_name_cannot_exceed_characters": "",
@@ -1852,6 +1869,7 @@
"unconfirmed": "",
"undelete": "",
"understanding_labels": "",
"undo": "",
"unfold_line": "",
"unique_identifier_attribute": "",
"university": "",

View File

@@ -107,7 +107,12 @@ function populateSectionOrGroup<
children: children
.map(child => {
if (typeof child !== 'string') {
return populateSectionOrGroup(child, registry)
const populatedChild = populateSectionOrGroup(child, registry)
if (populatedChild.children.length === 0) {
// Skip empty groups
return undefined
}
return populatedChild
}
const command = registry.get(child)
if (command) {

View File

@@ -3,10 +3,7 @@ import {
DropdownHeader,
} from '@/features/ui/components/bootstrap-5/dropdown-menu'
import { MenuBar } from '@/shared/components/menu-bar/menu-bar'
import {
MenuBarDropdown,
NestedMenuBarDropdown,
} from '@/shared/components/menu-bar/menu-bar-dropdown'
import { MenuBarDropdown } from '@/shared/components/menu-bar/menu-bar-dropdown'
import { MenuBarOption } from '@/shared/components/menu-bar/menu-bar-option'
import { useTranslation } from 'react-i18next'
import ChangeLayoutOptions from './change-layout-options'
@@ -30,6 +27,7 @@ export const ToolbarMenuBar = () => {
useCommandProvider(
() => [
{
type: 'command',
label: t('show_version_history'),
handler: () => {
setView(view === 'history' ? 'editor' : 'history')
@@ -54,28 +52,98 @@ export const ToolbarMenuBar = () => {
[]
)
const editMenuStructure: MenuStructure = useMemo(
() => [
{
id: 'edit-undo-redo',
children: ['undo', 'redo'],
},
{
id: 'edit-search',
children: ['find', 'select-all'],
},
],
[]
)
const insertMenuStructure: MenuStructure = useMemo(
() => [
{
id: 'insert-latex',
children: [
{
id: 'insert-math-group',
title: t('math'),
children: ['insert-inline-math', 'insert-display-math'],
},
'insert-symbol',
{
id: 'insert-figure-group',
title: t('figure'),
children: [
'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',
],
},
{
id: 'insert-comment',
children: ['comment'],
},
],
[t]
)
const formatMenuStructure: MenuStructure = useMemo(
() => [
{
id: 'format-text',
children: ['format-bold', 'format-italics'],
},
{
id: 'format-list',
children: [
'format-bullet-list',
'format-numbered-list',
'format-increase-indentation',
'format-decrease-indentation',
],
},
{
id: 'format-paragraph',
title: t('paragraph_styles'),
children: [
'format-style-normal',
'format-style-section',
'format-style-subsection',
'format-style-subsubsection',
'format-style-paragraph',
'format-style-subparagraph',
],
},
],
[t]
)
return (
<MenuBar
className="ide-redesign-toolbar-menu-bar"
id="toolbar-menu-bar-item"
>
<CommandDropdown menu={fileMenuStructure} title={t('file')} id="file" />
<MenuBarDropdown
title={t('edit')}
id="edit"
className="ide-redesign-toolbar-dropdown-toggle-subdued ide-redesign-toolbar-button-subdued"
>
<MenuBarOption title="Undo" />
<MenuBarOption title="Redo" />
<DropdownDivider />
<MenuBarOption title="Cut" />
<MenuBarOption title="Copy" />
<MenuBarOption title="Paste" />
<MenuBarOption title="Paste without formatting" />
<DropdownDivider />
<MenuBarOption title="Find" />
<MenuBarOption title="Select all" />
</MenuBarDropdown>
<CommandDropdown menu={editMenuStructure} title={t('edit')} id="edit" />
<CommandDropdown
menu={insertMenuStructure}
title={t('insert')}
id="insert"
/>
<MenuBarDropdown
title={t('view')}
id="view"
@@ -92,52 +160,11 @@ export const ToolbarMenuBar = () => {
<MenuBarOption title="Fit to width" />
<MenuBarOption title="Fit to height" />
</MenuBarDropdown>
<MenuBarDropdown
title={t('insert')}
id="insert"
className="ide-redesign-toolbar-dropdown-toggle-subdued ide-redesign-toolbar-button-subdued"
>
<NestedMenuBarDropdown title="Math" id="math">
<MenuBarOption title="Generate from text or image" />
<DropdownDivider />
<MenuBarOption title="Inline math" />
<MenuBarOption title="Display math" />
</NestedMenuBarDropdown>
<MenuBarOption title="Symbol" />
<NestedMenuBarDropdown title="Figure" id="figure">
<MenuBarOption title="Upload from computer" />
<MenuBarOption title="From project files" />
<MenuBarOption title="From another project" />
<MenuBarOption title="From URL" />
</NestedMenuBarDropdown>
<MenuBarOption title="Table" />
<MenuBarOption title="Citation" />
<MenuBarOption title="Link" />
<MenuBarOption title="Cross-reference" />
<DropdownDivider />
<MenuBarOption title="Comment" />
</MenuBarDropdown>
<MenuBarDropdown
<CommandDropdown
menu={formatMenuStructure}
title={t('format')}
id="format"
className="ide-redesign-toolbar-dropdown-toggle-subdued ide-redesign-toolbar-button-subdued"
>
<MenuBarOption title="Bold" />
<MenuBarOption title="Italics" />
<DropdownDivider />
<MenuBarOption title="Bullet list" />
<MenuBarOption title="Numbered list" />
<MenuBarOption title="Increase indentation" />
<MenuBarOption title="Decrease indentation" />
<DropdownDivider />
<DropdownHeader>Paragraph styles</DropdownHeader>
<MenuBarOption title="Normal text" />
<MenuBarOption title="Section" />
<MenuBarOption title="Subsection" />
<MenuBarOption title="Subsubsection" />
<MenuBarOption title="Paragraph" />
<MenuBarOption title="Subparagraph" />
</MenuBarDropdown>
/>
<MenuBarDropdown
title={t('help')}
id="help"

View File

@@ -0,0 +1,304 @@
import { useCommandProvider } from '@/features/ide-react/hooks/use-command-provider'
import { useCodeMirrorViewContext } from '@/features/source-editor/components/codemirror-context'
import { FigureModalSource } from '@/features/source-editor/components/figure-modal/figure-modal-context'
import * as commands from '@/features/source-editor/extensions/toolbar/commands'
import { setSectionHeadingLevel } from '@/features/source-editor/extensions/toolbar/sections'
import { useEditorContext } from '@/shared/context/editor-context'
import { useLayoutContext } from '@/shared/context/layout-context'
import getMeta from '@/utils/meta'
import { redo, selectAll, undo } from '@codemirror/commands'
import { openSearchPanel } from '@codemirror/search'
import { useCallback } from 'react'
import { useTranslation } from 'react-i18next'
import { useIsNewEditorEnabled } from '../utils/new-editor-utils'
export const useToolbarMenuBarEditorCommands = () => {
const view = useCodeMirrorViewContext()
const { t } = useTranslation()
const { view: layoutView } = useLayoutContext()
const editorIsVisible = layoutView === 'editor'
const openFigureModal = useCallback((source: FigureModalSource) => {
window.dispatchEvent(
new CustomEvent('figure-modal:open', {
detail: { source },
})
)
}, [])
const newEditor = useIsNewEditorEnabled()
useCommandProvider(() => {
if (!newEditor) {
return
}
return [
/************************************
* Edit menu
************************************/
{
id: 'undo',
label: t('undo'),
handler: () => {
undo(view)
view.focus()
},
disabled: !editorIsVisible,
},
{
id: 'redo',
label: t('redo'),
handler: () => {
redo(view)
view.focus()
},
disabled: !editorIsVisible,
},
{
id: 'find',
label: t('find'),
handler: () => {
openSearchPanel(view)
},
disabled: !editorIsVisible,
},
{
id: 'select-all',
label: t('select_all'),
handler: () => {
selectAll(view)
view.focus()
},
disabled: !editorIsVisible,
},
/************************************
* Insert menu
************************************/
{
id: 'insert-inline-math',
label: t('inline_math'),
handler: () => {
commands.wrapInInlineMath(view)
view.focus()
},
disabled: !editorIsVisible,
},
{
id: 'insert-display-math',
label: t('display_math'),
handler: () => {
commands.wrapInDisplayMath(view)
view.focus()
},
disabled: !editorIsVisible,
},
{
label: t('upload_from_computer'),
id: 'insert-figure-from-computer',
handler: () => {
openFigureModal(FigureModalSource.FILE_UPLOAD)
},
disabled: !editorIsVisible,
},
{
label: t('from_project_files'),
id: 'insert-figure-from-project-files',
handler: () => {
openFigureModal(FigureModalSource.FILE_TREE)
},
disabled: !editorIsVisible,
},
{
label: t('from_another_project'),
id: 'insert-figure-from-another-project',
handler: () => {
openFigureModal(FigureModalSource.OTHER_PROJECT)
},
disabled: !editorIsVisible,
},
{
label: t('from_url'),
id: 'insert-figure-from-url',
handler: () => {
openFigureModal(FigureModalSource.FROM_URL)
},
disabled: !editorIsVisible,
},
{
id: 'insert-table',
label: t('table'),
handler: () => {
commands.insertTable(view, 3, 3)
view.focus()
},
disabled: !editorIsVisible,
},
{
id: 'insert-citation',
label: t('citation'),
handler: () => {
commands.insertCite(view)
view.focus()
},
disabled: !editorIsVisible,
},
{
id: 'insert-link',
label: t('link'),
handler: () => {
commands.wrapInHref(view)
view.focus()
},
disabled: !editorIsVisible,
},
{
id: 'insert-cross-reference',
label: t('cross_reference'),
handler: () => {
commands.insertRef(view)
view.focus()
},
disabled: !editorIsVisible,
},
{
id: 'comment',
label: t('comment'),
handler: () => {
commands.addComment()
},
disabled: !editorIsVisible,
},
/************************************
* Format menu
************************************/
{
id: 'format-bold',
label: t('bold'),
handler: () => {
commands.toggleBold(view)
view.focus()
},
disabled: !editorIsVisible,
},
{
id: 'format-italics',
label: t('italics'),
handler: () => {
commands.toggleItalic(view)
view.focus()
},
disabled: !editorIsVisible,
},
{
id: 'format-bullet-list',
label: t('bullet_list'),
handler: () => {
commands.toggleBulletList(view)
view.focus()
},
disabled: !editorIsVisible,
},
{
id: 'format-numbered-list',
label: t('numbered_list'),
handler: () => {
commands.toggleNumberedList(view)
view.focus()
},
disabled: !editorIsVisible,
},
{
id: 'format-increase-indentation',
label: t('increase_indent'),
handler: () => {
commands.indentIncrease(view)
view.focus()
},
disabled: !editorIsVisible,
},
{
id: 'format-decrease-indentation',
label: t('decrease_indent'),
handler: () => {
commands.indentDecrease(view)
view.focus()
},
disabled: !editorIsVisible,
},
{
id: 'format-style-normal',
label: t('normal'),
handler: () => {
setSectionHeadingLevel(view, 'text')
view.focus()
},
disabled: !editorIsVisible,
},
{
id: 'format-style-section',
label: 'Section',
handler: () => {
setSectionHeadingLevel(view, 'section')
view.focus()
},
disabled: !editorIsVisible,
},
{
id: 'format-style-subsection',
label: 'Subsection',
handler: () => {
setSectionHeadingLevel(view, 'subsection')
view.focus()
},
disabled: !editorIsVisible,
},
{
id: 'format-style-subsubsection',
label: 'Subsubsection',
handler: () => {
setSectionHeadingLevel(view, 'subsubsection')
view.focus()
},
disabled: !editorIsVisible,
},
{
id: 'format-style-paragraph',
label: 'Paragraph',
handler: () => {
setSectionHeadingLevel(view, 'paragraph')
view.focus()
},
disabled: !editorIsVisible,
},
{
id: 'format-style-subparagraph',
label: 'Subparagraph',
handler: () => {
setSectionHeadingLevel(view, 'subparagraph')
view.focus()
},
disabled: !editorIsVisible,
},
]
}, [view, t, editorIsVisible, openFigureModal, newEditor])
const { toggleSymbolPalette } = useEditorContext()
const symbolPaletteAvailable = getMeta('ol-symbolPaletteAvailable')
useCommandProvider(() => {
if (!symbolPaletteAvailable) {
return
}
return [
{
id: 'insert-symbol',
label: t('symbol'),
handler: () => {
toggleSymbolPalette?.()
},
disabled: !editorIsVisible,
},
]
}, [symbolPaletteAvailable, t, toggleSymbolPalette, editorIsVisible])
}

View File

@@ -19,6 +19,7 @@ import {
import MathPreviewTooltip from './math-preview-tooltip'
import Breadcrumbs from '@/features/ide-redesign/components/breadcrumbs'
import { useIsNewEditorEnabled } from '@/features/ide-redesign/utils/new-editor-utils'
import { useToolbarMenuBarEditorCommands } from '@/features/ide-redesign/hooks/use-toolbar-menu-editor-commands'
// TODO: remove this when definitely no longer used
export * from './codemirror-context'
@@ -39,8 +40,6 @@ function CodeMirrorEditor() {
const isMounted = useIsMounted()
const newEditor = useIsNewEditorEnabled()
// create the view using the initial state and intercept transactions
const viewRef = useRef<EditorView | null>(null)
if (viewRef.current === null) {
@@ -62,33 +61,42 @@ function CodeMirrorEditor() {
return (
<CodeMirrorStateContext.Provider value={state}>
<CodeMirrorViewContext.Provider value={viewRef.current}>
<ReviewPanelProviders>
<CodemirrorOutline />
<CodeMirrorView />
<FigureModal />
<CodeMirrorSearch />
<CodeMirrorToolbar />
{sourceEditorToolbarComponents.map(
({ import: { default: Component }, path }) => (
<Component key={path} />
)
)}
{newEditor && <Breadcrumbs />}
<CodeMirrorCommandTooltip />
<MathPreviewTooltip />
<ReviewTooltipMenu />
<ReviewPanelNew />
{sourceEditorComponents.map(
({ import: { default: Component }, path }) => (
<Component key={path} />
)
)}
</ReviewPanelProviders>
<CodeMirrorEditorComponents />
</CodeMirrorViewContext.Provider>
</CodeMirrorStateContext.Provider>
)
}
function CodeMirrorEditorComponents() {
const newEditor = useIsNewEditorEnabled()
useToolbarMenuBarEditorCommands()
return (
<ReviewPanelProviders>
<CodemirrorOutline />
<CodeMirrorView />
<FigureModal />
<CodeMirrorSearch />
<CodeMirrorToolbar />
{sourceEditorToolbarComponents.map(
({ import: { default: Component }, path }) => (
<Component key={path} />
)
)}
{newEditor && <Breadcrumbs />}
<CodeMirrorCommandTooltip />
<MathPreviewTooltip />
<ReviewTooltipMenu />
<ReviewPanelNew />
{sourceEditorComponents.map(
({ import: { default: Component }, path }) => (
<Component key={path} />
)
)}
</ReviewPanelProviders>
)
}
export default memo(CodeMirrorEditor)

View File

@@ -235,9 +235,11 @@
"blank_project": "Blank Project",
"blocked_filename": "This file name is blocked.",
"blog": "Blog",
"bold": "Bold",
"brl_discount_offer_plans_page_banner": "__flag__ <b>Great news!</b> Weve applied a 50% discount to premium plans on this page for our users in Brazil. Check out the new lower prices.",
"browser": "Browser",
"built_in": "Built-In",
"bullet_list": "Bullet list",
"buy_licenses": "Buy licenses",
"buy_more_licenses": "Buy more licenses",
"buy_now_no_exclamation_mark": "Buy now",
@@ -311,6 +313,7 @@
"choose_how_you_search_your_references": "Choose how you search your references",
"choose_which_experiments": "Choose which experiments youd like to try.",
"choose_your_plan": "Choose your plan",
"citation": "Citation",
"city": "City",
"clear_cached_files": "Clear cached files",
"clear_search": "clear search",
@@ -442,6 +445,7 @@
"created_at": "Created at",
"creating": "Creating",
"credit_card": "Credit Card",
"cross_reference": "Cross reference",
"cs": "Czech",
"currency": "Currency",
"current_file": "Current file",
@@ -466,6 +470,7 @@
"de": "German",
"dealing_with_errors": "Dealing with errors",
"december": "December",
"decrease_indent": "Decrease indentation",
"dedicated_account_manager": "Dedicated account manager",
"default": "Default",
"delete": "Delete",
@@ -525,6 +530,7 @@
"discover_why_over_people_worldwide_trust_overleaf": "Discover why over __count__ million people worldwide trust Overleaf with their work.",
"dismiss_error_popup": "Dismiss first error alert",
"display_deleted_user": "Display deleted users",
"display_math": "Display math",
"do_not_have_acct_or_do_not_want_to_link": "If you dont have an <b>__appName__</b> account, or if you dont want to link to your <b>__institutionName__</b> account, please click <b>__clickText__</b>.",
"do_not_link_accounts": "Dont link accounts",
"do_you_need_edit_access": "Do you need edit access?",
@@ -712,6 +718,7 @@
"features_and_benefits": "Features & Benefits",
"features_like_track_changes": "Features like real-time track changes",
"february": "February",
"figure": "Figure",
"file": "File",
"file_action_created": "Created",
"file_action_deleted": "Deleted",
@@ -734,6 +741,7 @@
"files_selected": "files selected.",
"filter_projects": "Filter projects",
"filters": "Filters",
"find": "Find",
"find_out_more": "Find out More",
"find_out_more_about_institution_login": "Find out more about institutional login",
"find_out_more_about_the_file_outline": "Find out more about the file outline",
@@ -1004,9 +1012,11 @@
"include_results_from_your_reference_manager": "Include results from your reference manager",
"include_results_from_your_x_account": "Include results from your __provider__ account",
"include_the_error_message_and_ai_response": "Include the error message and AI response",
"increase_indent": "Increase indentation",
"increased_compile_timeout": "Increased compile timeout",
"individuals": "Individuals",
"info": "Info",
"inline_math": "Inline math",
"inr_discount_modal_info": "Get document history, track changes, additional collaborators, and more at Purchasing Power Parity prices.",
"inr_discount_modal_title": "70% off all Overleaf premium plans for users in India",
"inr_discount_offer_plans_page_banner": "__flag__ <b>Great news!</b> Weve applied a <b>70% discount</b> to premium plans for our users in India. Check out the new lower prices below.",
@@ -1083,6 +1093,7 @@
"it_looks_like_that_didnt_work_you_can_try_again_or_get_in_touch": "It looks like that didnt work. You can try again or <0>get in touch</0> with our Support team for more help.",
"it_looks_like_your_account_is_billed_manually": "It looks like your account is being billed manually - adding seats or upgrading your subscription can only be done by the Support team. Please <0>get in touch</0> for help.",
"it_looks_like_your_payment_details_are_missing_please_update_your_billing_information": "It looks like your payment details are missing. Please <0>update your billing information</0>, or <1>get in touch</1> with our Support team for more help.",
"italics": "Italics",
"ja": "Japanese",
"january": "January",
"join_beta_program": "Join beta program",
@@ -1286,6 +1297,7 @@
"managing_your_subscription": "Managing your subscription",
"march": "March",
"marked_as_resolved": "Marked as resolved",
"math": "Math",
"math_display": "Math Display",
"math_inline": "Math Inline",
"maximum_files_uploaded_together": "Maximum __max__ files uploaded together",
@@ -1426,6 +1438,7 @@
"number_collab_info": "The number of people you can invite to work on a project with you. The limit is per project, so you can invite different people to each project.",
"number_of_projects": "Number of projects",
"number_of_users": "Number of users",
"numbered_list": "Numbered list",
"oauth_orcid_description": " <a href=\"__link__\">Securely establish your identity by linking your ORCID iD to your __appName__ account</a>. Submissions to participating publishers will automatically include your ORCID iD for improved workflow and visibility. ",
"october": "October",
"off": "Off",
@@ -1512,6 +1525,7 @@
"papers_sync_description": "With the Papers integration you can import your references from Papers into your __appName__ projects.",
"papers_upgrade_prompt_content": "Link your Papers account to search and add your references from Papers directly in your project—theyll automatically be added to your .bib file. Or import them as a file into your __appName__ projects.",
"papers_upgrade_prompt_title": "Cite from Papers",
"paragraph_styles": "Paragraph styles",
"partial_outline_warning": "The File outline is out of date. It will update itself as you edit the document",
"password": "Password",
"password_cant_be_the_same_as_current_one": "Password cant be the same as current one",
@@ -1723,6 +1737,7 @@
"redirect_to_editor": "Redirect to editor",
"redirect_url": "Redirect URL",
"redirecting": "Redirecting",
"redo": "Redo",
"reduce_costs_group_licenses": "You can cut down on paperwork and reduce costs with our discounted group licenses.",
"reference_error_relink_hint": "If this error persists, try re-linking your account here:",
"reference_manager_searched_groups": "__provider__ search groups",
@@ -2135,6 +2150,7 @@
"switch_to_old_editor": "Switch to old editor",
"switch_to_pdf": "Switch to PDF",
"switch_to_standard_plan": "Switch to Standard plan",
"symbol": "Symbol",
"symbol_palette": "Symbol palette",
"symbol_palette_highlighted": "<0>Symbol</0> palette",
"symbol_palette_info_new": "Insert math symbols into your document with the click of a button.",
@@ -2147,6 +2163,7 @@
"syntax_validation": "Code check",
"tab_connecting": "Connecting with the editor",
"tab_no_longer_connected": "This tab is no longer connected with the editor",
"table": "Table",
"table_generator": "Table Generator",
"tag_color": "Tag color",
"tag_name_cannot_exceed_characters": "Tag name cannot exceed __maxLength__ characters",
@@ -2385,6 +2402,7 @@
"undelete": "Undelete",
"undeleting": "Undeleting",
"understanding_labels": "Understanding labels",
"undo": "Undo",
"unfold_line": "Unfold line",
"unique_identifier_attribute": "Unique identifier attribute",
"university": "University",