From 1d2d964a2eb159914a5ac62dd6a8d5423fe2a6ed Mon Sep 17 00:00:00 2001 From: Borja <158476064+borja-writefull@users.noreply.github.com> Date: Wed, 15 Oct 2025 07:43:47 +0200 Subject: [PATCH] Refactor Writefull toolbar (#28911) GitOrigin-RevId: 1d8a3addc9046dc67c0cca20d5cf4fba35d132d1 --- package-lock.json | 30 +++++++++---------- .../web/frontend/extracted-translations.json | 3 -- .../components/codemirror-editor.tsx | 9 ------ .../components/codemirror-toolbar.tsx | 19 +++++++++++- .../components/toolbar/button-menu.tsx | 7 +++++ .../components/toolbar/overflow.tsx | 16 ++++++++-- .../context/types/writefull-instance.ts | 2 +- services/web/locales/da.json | 3 -- services/web/locales/en.json | 3 -- services/web/locales/zh-CN.json | 3 -- services/web/package.json | 6 ++-- services/web/webpack.config.js | 24 +++++++++++++++ 12 files changed, 82 insertions(+), 43 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1fa7295c8d..2d837588f4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21735,17 +21735,17 @@ } }, "node_modules/@writefull/core": { - "version": "1.27.26", - "resolved": "https://registry.npmjs.org/@writefull/core/-/core-1.27.26.tgz", - "integrity": "sha512-EI8te62cSuxTLT6tv9tOuk0ddkVIcciD/a/HdTaCsEAF+vpAJHEtD4fkLHeZt+U4P5cJhQPNt6lG/Ei0O2AR9g==", + "version": "1.27.27", + "resolved": "https://registry.npmjs.org/@writefull/core/-/core-1.27.27.tgz", + "integrity": "sha512-q5UWTq7zRQb6y/n8J4k1c2DzuhGnJin6VdY/GeGd7QW7/XRoVlq2pknKQXXdJJCx7mlMahsaZcAj38iqvKii0w==", "dev": true, "license": "MIT", "dependencies": { "@bugsnag/js": "^7.23.0", "@bugsnag/plugin-react": "^7.24.0", "@growthbook/growthbook": "^1.4.1", - "@writefull/ui": "^1.27.26", - "@writefull/utils": "^1.27.26", + "@writefull/ui": "^1.27.27", + "@writefull/utils": "^1.27.27", "axios": "^1.8.3", "idb": "^8.0.2", "inversify": "^6.0.2", @@ -21757,14 +21757,14 @@ } }, "node_modules/@writefull/ui": { - "version": "1.27.26", - "resolved": "https://registry.npmjs.org/@writefull/ui/-/ui-1.27.26.tgz", - "integrity": "sha512-I9hcCKz6VE8bpmo3/MDAZPNX01TkBj63FcBpKcPQ/bkvNAwQvjJ1zaB1K65GBPIZS1FvFN6fXEO8+LPj/0Z+Kg==", + "version": "1.27.27", + "resolved": "https://registry.npmjs.org/@writefull/ui/-/ui-1.27.27.tgz", + "integrity": "sha512-hM3y2oy0leorke+dWMyBl7ur6lRR1l6qICZlwqBCJouqiZnffHTxeDipdzDlbFzyfC4qgozgAcVVEmPtbKfSGw==", "dev": true, "license": "MIT", "dependencies": { "@floating-ui/react": "^0.27.5", - "@writefull/utils": "^1.27.26" + "@writefull/utils": "^1.27.27" }, "peerDependencies": { "react": ">= 18", @@ -21772,9 +21772,9 @@ } }, "node_modules/@writefull/utils": { - "version": "1.27.26", - "resolved": "https://registry.npmjs.org/@writefull/utils/-/utils-1.27.26.tgz", - "integrity": "sha512-cb1nGLP0RBKSvwzGfEGj8xZN9jy15JPoPbNiijHlILiR2+KQ0ICu2uWSos2K2OaKO8mK/2P0nFU4rJOZ/9jc8w==", + "version": "1.27.27", + "resolved": "https://registry.npmjs.org/@writefull/utils/-/utils-1.27.27.tgz", + "integrity": "sha512-yv2135in+NQbYfr+0rmGAKFY55u1OIkOXPQyniRXPjdYwiRVC77tt5XJwqrThVPYd1mruZcFKJ9qDSK4RfAtOg==", "dev": true, "license": "MIT" }, @@ -53334,9 +53334,9 @@ "@uppy/utils": "^5.7.0", "@uppy/xhr-upload": "^3.6.0", "@vitest/eslint-plugin": "1.1.44", - "@writefull/core": "^1.27.26", - "@writefull/ui": "^1.27.26", - "@writefull/utils": "^1.27.26", + "@writefull/core": "^1.27.27", + "@writefull/ui": "^1.27.27", + "@writefull/utils": "^1.27.27", "5to6-codemod": "^1.8.0", "abort-controller": "^3.0.0", "acorn": "^7.1.1", diff --git a/services/web/frontend/extracted-translations.json b/services/web/frontend/extracted-translations.json index ba9e16e4d3..1087bddde9 100644 --- a/services/web/frontend/extracted-translations.json +++ b/services/web/frontend/extracted-translations.json @@ -991,7 +991,6 @@ "loading_github_repositories": "", "loading_prices": "", "loading_recent_github_commits": "", - "loading_writefull": "", "log_entry_description": "", "log_entry_maximum_entries": "", "log_entry_maximum_entries_enable_stop_on_first_error": "", @@ -2170,8 +2169,6 @@ "work_with_non_overleaf_users": "", "work_with_other_github_users": "", "write_faster_smarter_with_overleaf_and_writefull_ai_tools": "", - "writefull_loading_error_body": "", - "writefull_loading_error_title": "", "x_changes_in": "", "x_changes_in_plural": "", "x_libraries_accessed_in_this_project": "", diff --git a/services/web/frontend/js/features/source-editor/components/codemirror-editor.tsx b/services/web/frontend/js/features/source-editor/components/codemirror-editor.tsx index da7c808817..062652c784 100644 --- a/services/web/frontend/js/features/source-editor/components/codemirror-editor.tsx +++ b/services/web/frontend/js/features/source-editor/components/codemirror-editor.tsx @@ -27,10 +27,6 @@ const sourceEditorComponents = importOverleafModules( 'sourceEditorComponents' ) as { import: { default: ElementType }; path: string }[] -const sourceEditorToolbarComponents = importOverleafModules( - 'sourceEditorToolbarComponents' -) as { import: { default: ElementType }; path: string }[] - function CodeMirrorEditor() { // create the initial state const [state, setState] = useState(() => { @@ -77,11 +73,6 @@ function CodeMirrorEditorComponents() { - {sourceEditorToolbarComponents.map( - ({ import: { default: Component }, path }) => ( - - ) - )} diff --git a/services/web/frontend/js/features/source-editor/components/codemirror-toolbar.tsx b/services/web/frontend/js/features/source-editor/components/codemirror-toolbar.tsx index 9fe0c0b3e5..a6265ecc93 100644 --- a/services/web/frontend/js/features/source-editor/components/codemirror-toolbar.tsx +++ b/services/web/frontend/js/features/source-editor/components/codemirror-toolbar.tsx @@ -1,4 +1,11 @@ -import { memo, useCallback, useEffect, useRef, useState } from 'react' +import { + ElementType, + memo, + useCallback, + useEffect, + useRef, + useState, +} from 'react' import { createPortal } from 'react-dom' import { useCodeMirrorStateContext, @@ -27,6 +34,11 @@ import Breadcrumbs from '@/features/ide-redesign/components/breadcrumbs' import classNames from 'classnames' import { useUserSettingsContext } from '@/shared/context/user-settings-context' import { useFeatureFlag } from '@/shared/context/split-test-context' +import importOverleafModules from '../../../../macros/import-overleaf-module.macro' + +const sourceEditorToolbarComponents = importOverleafModules( + 'sourceEditorToolbarComponents' +) as { import: { default: ElementType }; path: string }[] export const CodeMirrorToolbar = () => { const view = useCodeMirrorViewContext() @@ -198,6 +210,11 @@ const Toolbar = memo(function Toolbar() { + {sourceEditorToolbarComponents.map( + ({ import: { default: Component }, path }) => ( + + ) + )} {newEditor && breadcrumbs && } diff --git a/services/web/frontend/js/features/source-editor/components/toolbar/button-menu.tsx b/services/web/frontend/js/features/source-editor/components/toolbar/button-menu.tsx index ca27ed7588..cc534d6bf1 100644 --- a/services/web/frontend/js/features/source-editor/components/toolbar/button-menu.tsx +++ b/services/web/frontend/js/features/source-editor/components/toolbar/button-menu.tsx @@ -13,6 +13,7 @@ export const ToolbarButtonMenu: FC< id: string label: string icon: React.ReactNode + disabled?: boolean disablePopover?: boolean altCommand?: (view: EditorView) => void }> @@ -21,6 +22,7 @@ export const ToolbarButtonMenu: FC< id, label, altCommand, + disabled, disablePopover, children, }) { @@ -39,11 +41,16 @@ export const ToolbarButtonMenu: FC< type="button" className="ol-cm-toolbar-button" aria-label={label} + aria-disabled={disabled} onMouseDown={event => { event.preventDefault() event.stopPropagation() }} onClick={event => { + if (disabled) { + event.preventDefault() + return + } if (event.altKey && altCommand && open === false) { emitToolbarEvent(view, id) event.preventDefault() diff --git a/services/web/frontend/js/features/source-editor/components/toolbar/overflow.tsx b/services/web/frontend/js/features/source-editor/components/toolbar/overflow.tsx index 089868e962..dc5eb24b72 100644 --- a/services/web/frontend/js/features/source-editor/components/toolbar/overflow.tsx +++ b/services/web/frontend/js/features/source-editor/components/toolbar/overflow.tsx @@ -12,8 +12,16 @@ export const ToolbarOverflow: FC< overflowOpen: boolean setOverflowOpen: (open: boolean) => void overflowRef?: React.Ref + popoverClassName?: string }> -> = ({ overflowed, overflowOpen, setOverflowOpen, overflowRef, children }) => { +> = ({ + overflowed, + overflowOpen, + setOverflowOpen, + overflowRef, + popoverClassName, + children, +}) => { const { t } = useTranslation() const buttonRef = useRef(null) const keyboardInputRef = useRef(false) @@ -104,7 +112,11 @@ export const ToolbarOverflow: FC< ref={overflowRef} role="toolbar" > -
{children}
+
+ {children} +
diff --git a/services/web/frontend/js/shared/context/types/writefull-instance.ts b/services/web/frontend/js/shared/context/types/writefull-instance.ts index 694016ca1e..8a11a4a20b 100644 --- a/services/web/frontend/js/shared/context/types/writefull-instance.ts +++ b/services/web/frontend/js/shared/context/types/writefull-instance.ts @@ -9,7 +9,7 @@ export interface WritefullEvents { } export interface WritefullAPI { - init(): Promise + init(): void addEventListener( name: eventName, callback: (detail: WritefullEvents[eventName]) => void diff --git a/services/web/locales/da.json b/services/web/locales/da.json index 5c11884a4b..8c4d10d48f 100644 --- a/services/web/locales/da.json +++ b/services/web/locales/da.json @@ -1002,7 +1002,6 @@ "loading_github_repositories": "Indlæser dit GitHub repository", "loading_prices": "Indlæser priser", "loading_recent_github_commits": "Indlæs nylige commits", - "loading_writefull": "Indlæser Writefull", "log_entry_description": "Logoptegnelse med niveau: __level__", "log_entry_maximum_entries": "Grænsen for elementer i loggen er nået", "log_entry_maximum_entries_enable_stop_on_first_error": "Prøv at fikse den første fejl og genkompilere. Ofte kan en fejl være skyld i mange efterfølgende fejlmeddelelser. Du kan Slå <0>“Stop ved første fejl” til for at fokusere på at fikse fejl. Vi anbefaler, at du fikser fejl så hurtigt som muligt; hvis de får lov at hobe sig op kan de føre til fejl, som er svære at fejlrette, og fatale fejl. <1>Lære mere", @@ -2124,8 +2123,6 @@ "work_or_university_sso": "Arbejds/universitets single sign-on", "work_with_non_overleaf_users": "Arbejd sammen med ikke-Overleaf-brugere", "writefull": "Writefull", - "writefull_loading_error_body": "Prøv at genindlæse siden. Hvis det ikke virker, prøv at deaktivere alle aktive browserudvidelser for at tjekket at de ikke blokerer Writefull for at indlæse.", - "writefull_loading_error_title": "Writefull blev ikke indlæst korrekt", "x_changes_in": "__count__ ændring i", "x_changes_in_plural": "__count__ ændringer i", "x_libraries_accessed_in_this_project": "__provider__ biblioteker tilgået i dette projekt", diff --git a/services/web/locales/en.json b/services/web/locales/en.json index 0ba3689e87..10b4d43858 100644 --- a/services/web/locales/en.json +++ b/services/web/locales/en.json @@ -1274,7 +1274,6 @@ "loading_github_repositories": "Loading your GitHub repositories", "loading_prices": "loading prices", "loading_recent_github_commits": "Loading recent commits", - "loading_writefull": "Loading Writefull", "log_entry_description": "Log entry with level: __level__", "log_entry_maximum_entries": "Maximum log entries limit hit", "log_entry_maximum_entries_enable_stop_on_first_error": "Try to fix the first error and recompile. Often one error causes many later error messages. You can <0>Enable “Stop on first error” to focus on fixing errors. We recommend fixing errors as soon as possible; letting them accumulate may lead to hard-to-debug and fatal errors. <1>Learn more", @@ -2720,8 +2719,6 @@ "work_with_other_github_users": "Work with other GitHub users", "write_faster_smarter_with_overleaf_and_writefull_ai_tools": "Write faster, smarter, and with confidence with Overleaf and Writefull AI tools", "writefull": "Writefull", - "writefull_loading_error_body": "Try refreshing the page. If this doesn’t work, try disabling any active browser extensions to check they aren’t blocking Writefull from loading.", - "writefull_loading_error_title": "Writefull didn’t load correctly", "x_changes_in": "__count__ change in", "x_changes_in_plural": "__count__ changes in", "x_libraries_accessed_in_this_project": "__provider__ libraries accessed in this project", diff --git a/services/web/locales/zh-CN.json b/services/web/locales/zh-CN.json index 35adb92b6f..73f0710398 100644 --- a/services/web/locales/zh-CN.json +++ b/services/web/locales/zh-CN.json @@ -1200,7 +1200,6 @@ "loading_github_repositories": "正在读取您的GitHub存储库", "loading_prices": "加载价格", "loading_recent_github_commits": "正在装载最近的提交", - "loading_writefull": "加载 Writefull", "log_entry_description": "级别为__level__的日志条目", "log_entry_maximum_entries": "最大日志条目限制已达到", "log_entry_maximum_entries_enable_stop_on_first_error": "尝试修复第一个错误并重新编译。通常一个错误会导致许多后续的错误消息。您可以<0>启用“第一次出现错误时停止”以专注于修复错误。我们建议尽快修复错误;让它们积累起来可能会导致难以调试和致命的错误<1> 了解更多信息", @@ -2553,8 +2552,6 @@ "work_with_non_overleaf_users": "和非Overleaf用户一起工作", "work_with_other_github_users": "与其他 GitHub 用户合作", "writefull": "Writefull", - "writefull_loading_error_body": "尝试刷新页面,如果无效,尝试禁用所有的浏览器拓展,以便检查是否他们阻止了 Writefull 的加载。", - "writefull_loading_error_title": "Writefull 加载失败", "x_changes_in": "__count__ 处变化在", "x_changes_in_plural": "__count__ 处变化在", "x_libraries_accessed_in_this_project": "本项目中访问的 __provider__ 库", diff --git a/services/web/package.json b/services/web/package.json index be6d5c54ca..1873edd580 100644 --- a/services/web/package.json +++ b/services/web/package.json @@ -275,9 +275,9 @@ "@uppy/utils": "^5.7.0", "@uppy/xhr-upload": "^3.6.0", "@vitest/eslint-plugin": "1.1.44", - "@writefull/core": "^1.27.26", - "@writefull/ui": "^1.27.26", - "@writefull/utils": "^1.27.26", + "@writefull/core": "^1.27.27", + "@writefull/ui": "^1.27.27", + "@writefull/utils": "^1.27.27", "5to6-codemod": "^1.8.0", "abort-controller": "^3.0.0", "acorn": "^7.1.1", diff --git a/services/web/webpack.config.js b/services/web/webpack.config.js index 21a08f8df9..8cad9ea47c 100644 --- a/services/web/webpack.config.js +++ b/services/web/webpack.config.js @@ -236,6 +236,30 @@ module.exports = { }, ], }, + { + // CSS from writefull module - inject directly into DOM + include: path.resolve(__dirname, 'modules/writefull/'), + use: [ + 'style-loader', + { + loader: 'css-loader', + options: { + importLoaders: 1, + }, + }, + { + loader: 'postcss-loader', + options: { + postcssOptions: { + config: path.resolve( + __dirname, + 'modules/writefull/frontend/js/integration/postcss.config.js' + ), + }, + }, + }, + ], + }, { // Standard CSS processing (extracted into separate file) use: [MiniCssExtractPlugin.loader, 'css-loader'],