Merge pull request #34110 from overleaf/mg-toolbar-language-suggestions

Add language suggestion controls to editor toolbar

GitOrigin-RevId: fe52b909b44ef8ccfc1449366bbf62f2d9d7ee4e
This commit is contained in:
Malik Glossop
2026-06-10 11:58:20 +00:00
committed by Copybot
parent 60624f2ee7
commit 6794af0ede
7 changed files with 44 additions and 13 deletions
@@ -470,6 +470,7 @@ const _ProjectController = {
'ai-workbench-release',
'compile-timeout-target-plans',
'writefull-figure-generator',
'writefull-toolbar-migration',
'wf-citations-checker',
'wf-citations-checker-on-selection',
'writefull-asymetric-queue-size-per-model',
+1
View File
@@ -1036,6 +1036,7 @@ module.exports = {
diagnosticActions: [],
sourceEditorCompletionSources: [],
sourceEditorSymbolPalette: [],
sourceEditorToolbarButtonGroups: [],
sourceEditorToolbarComponents: [],
sourceEditorToolbarEndButtons: [],
rootContextProviders: [],
@@ -1,3 +1,7 @@
// Prime the writefull IoC container before other imports to avoid a circular
// dependency TDZ error (command.ts → ioc.ts → handler.ts → command.ts).
import '@wf/infrastructure/ioc'
import 'cypress-plugin-tab'
import { resetMeta } from './ct/window' // needs to be before i18n
import localesPromise from '@/i18n'
@@ -44,6 +44,7 @@
"accept_invitation": "",
"accept_or_reject_individual_edits": "",
"accept_selected_changes": "",
"accept_suggestion": "",
"accept_terms_and_conditions": "",
"accepted_invite": "",
"accepting": "",
@@ -1075,6 +1076,7 @@
"language_model_gpt_custom_prompt_description": "",
"language_model_writefull_english": "",
"language_model_writefull_english_description": "",
"language_suggestion_actions": "",
"language_suggestions": "",
"last_active": "",
"last_active_description": "",
@@ -1297,6 +1299,7 @@
"next": "",
"next_page": "",
"next_payment_of_x_collectected_on_y": "",
"next_suggestion": "",
"no_actions": "",
"no_add_on_purchase_while_paused": "",
"no_audit_logs_found": "",
@@ -1505,6 +1508,7 @@
"preview": "",
"preview_editor_tabs": "",
"previous_page": "",
"previous_suggestion": "",
"price": "",
"primarily_work_study_question": "",
"primarily_work_study_question_company": "",
@@ -1630,6 +1634,7 @@
"reject": "",
"reject_change": "",
"reject_selected_changes": "",
"reject_suggestion": "",
"relink_your_account": "",
"reload_editor": "",
"remind_before_trial_ends": "",
@@ -125,12 +125,20 @@ const Toolbar = memo(function Toolbar() {
if (resizeRef.current) {
buildOverflow(resizeRef.current.element)
}
}, [buildOverflow, languageName, resizeRef, visual])
}, [buildOverflow, languageName, listDepth, resizeRef, visual])
// calculate overflow when buttons change
// calculate overflow when toolbar content changes
const observerRef = useRef<MutationObserver | null>(null)
const handleButtons = useCallback(
const handleToolbar = useCallback(
(node: HTMLDivElement) => {
// register the resize observer on the toolbar node
elementRef(node)
if (observerRef.current) {
observerRef.current.disconnect()
observerRef.current = null
}
if (!('MutationObserver' in window)) {
return
}
@@ -142,12 +150,10 @@ const Toolbar = memo(function Toolbar() {
}
})
observerRef.current.observe(node, { childList: true })
} else if (observerRef.current) {
observerRef.current.disconnect()
observerRef.current.observe(node, { childList: true, subtree: true })
}
},
[buildOverflow, resizeRef]
[buildOverflow, elementRef, resizeRef]
)
// calculate overflow when active element changes to/from inside a table
@@ -181,7 +187,7 @@ const Toolbar = memo(function Toolbar() {
role="toolbar"
aria-label={t('toolbar_editor')}
className="ol-cm-toolbar toolbar-editor"
ref={elementRef}
ref={handleToolbar}
>
{showActions && (
<ToolbarItems
@@ -211,10 +217,7 @@ const Toolbar = memo(function Toolbar() {
)}
</div>
<div
className="ol-cm-toolbar-button-group ol-cm-toolbar-end"
ref={handleButtons}
>
<div className="ol-cm-toolbar-button-group ol-cm-toolbar-end">
{!visualPreviewEnabled && <EditorSwitch />}
{sourceEditorToolbarEndButtons.map(
({ import: { default: Component }, path }) => (
@@ -1,4 +1,4 @@
import { FC, memo } from 'react'
import { ComponentType, FC, memo } from 'react'
import { EditorState } from '@codemirror/state'
import { useEditorContext } from '../../../../shared/context/editor-context'
import { ToolbarButton } from './toolbar-button'
@@ -18,6 +18,14 @@ import { useProjectContext } from '@/shared/context/project-context'
import { useEditorPropertiesContext } from '@/features/ide-react/context/editor-properties-context'
import { usePermissionsContext } from '@/features/ide-react/context/permissions-context'
import { isCursorOnEmptyLine } from '@/features/source-editor/utils/is-cursor-on-empty-line'
import importOverleafModules from '../../../../../macros/import-overleaf-module.macro'
const sourceEditorToolbarButtonGroups = importOverleafModules(
'sourceEditorToolbarButtonGroups'
) as {
import: { default: ComponentType; overflowGroupId: string }
path: string
}[]
const addCommentFromToolbar = () => commands.addComment('toolbar')
@@ -70,6 +78,10 @@ export const ToolbarItems: FC<{
/>
</div>
)}
{sourceEditorToolbarButtonGroups.map(
({ import: { default: Component, overflowGroupId }, path }) =>
showGroup(overflowGroupId) && <Component key={path} />
)}
{languageName === 'latex' && (
<>
{showGroup('group-section') && (
+5
View File
@@ -49,6 +49,7 @@
"accept_invitation": "Accept invitation",
"accept_or_reject_individual_edits": "Accept or reject individual edits",
"accept_selected_changes": "Accept selected changes",
"accept_suggestion": "Accept suggestion",
"accept_terms_and_conditions": "Accept terms and conditions",
"accepted_invite": "Accepted invite",
"accepting_invite_as": "You are accepting this invite as",
@@ -1394,6 +1395,7 @@
"language_model_gpt_custom_prompt_description": "Customize your edits with a prompt, no matter your language, text type, or editing needs.",
"language_model_writefull_english": "Writefull",
"language_model_writefull_english_description": "Our custom model for research writing. It doesn’t store your data or train on your texts.",
"language_suggestion_actions": "Language suggestion actions",
"language_suggestions": "Language suggestions",
"larger_discounts_available": "Larger discounts available",
"larger_discounts_for_groups_of_20_plus": "Larger discounts for groups of 20+",
@@ -1689,6 +1691,7 @@
"next": "Next",
"next_page": "Next page",
"next_payment_of_x_collectected_on_y": "The next payment of <0>__paymentAmmount__</0> will be collected on <1>__collectionDate__</1>.",
"next_suggestion": "Next suggestion",
"nl": "Dutch",
"no": "Norwegian",
"no_actions": "No actions",
@@ -1984,6 +1987,7 @@
"preview_editor_tabs": "Preview editor tabs",
"previous_24_hours_only": "previous 24 hours only",
"previous_page": "Previous page",
"previous_suggestion": "Previous suggestion",
"price": "Price",
"pricing": "Pricing",
"primarily_work_study_question": "Where do you primarily work or study?",
@@ -2137,6 +2141,7 @@
"reject": "Reject",
"reject_change": "Reject change",
"reject_selected_changes": "Reject selected changes",
"reject_suggestion": "Reject suggestion",
"related_tags": "Related Tags",
"relink_your_account": "Re-link your account",
"reload_editor": "Reload editor",