From f86334750ebe40f3c2398d662058abd36244e3ff Mon Sep 17 00:00:00 2001 From: Mathias Jakobsen Date: Thu, 16 Jul 2026 11:06:58 +0100 Subject: [PATCH] Merge pull request #35465 from overleaf/mj-download-file-button [web] Add download file button during network stall GitOrigin-RevId: f85c69c5ce5bc2a0c28c3b5205f108f983fe4c8b --- .../web/frontend/extracted-translations.json | 1 + .../toolbar/download-file-button.tsx | 32 +++++++++++++++++++ .../components/toolbar/toolbar-layout.tsx | 4 +++ .../components/editor-toolbar.scss | 9 ++++++ .../stylesheets/pages/editor/toolbar.scss | 13 +++----- services/web/locales/en.json | 1 + 6 files changed, 51 insertions(+), 9 deletions(-) create mode 100644 services/web/frontend/js/features/source-editor/components/toolbar/download-file-button.tsx diff --git a/services/web/frontend/extracted-translations.json b/services/web/frontend/extracted-translations.json index 390149bcfa..4ee3ba3812 100644 --- a/services/web/frontend/extracted-translations.json +++ b/services/web/frontend/extracted-translations.json @@ -535,6 +535,7 @@ "download_as_pdf": "", "download_as_source_zip": "", "download_csv": "", + "download_file": "", "download_metadata": "", "download_pdf": "", "download_zip_file": "", diff --git a/services/web/frontend/js/features/source-editor/components/toolbar/download-file-button.tsx b/services/web/frontend/js/features/source-editor/components/toolbar/download-file-button.tsx new file mode 100644 index 0000000000..4a687ebb22 --- /dev/null +++ b/services/web/frontend/js/features/source-editor/components/toolbar/download-file-button.tsx @@ -0,0 +1,32 @@ +import OLButton from '@/shared/components/ol/ol-button' +import { useCodeMirrorViewContext } from '../codemirror-context' +import { useCallback } from 'react' +import { useEditorOpenDocContext } from '@/features/ide-react/context/editor-open-doc-context' +import { useTranslation } from 'react-i18next' + +const DownloadFileButton = () => { + const view = useCodeMirrorViewContext() + const { openDocName } = useEditorOpenDocContext() + const { t } = useTranslation() + + const downloadFile = useCallback(() => { + const doc = view.state.doc.toString() + const blob = new Blob([doc], { type: 'application/octet-stream' }) + const url = URL.createObjectURL(blob) + const link = document.createElement('a') + link.href = url + link.download = openDocName ?? 'document.txt' + document.body.appendChild(link) + link.click() + document.body.removeChild(link) + setTimeout(() => URL.revokeObjectURL(url), 0) + }, [view, openDocName]) + + return ( + + {t('download_file')} + + ) +} + +export default DownloadFileButton diff --git a/services/web/frontend/js/features/source-editor/components/toolbar/toolbar-layout.tsx b/services/web/frontend/js/features/source-editor/components/toolbar/toolbar-layout.tsx index 8b0524c4fc..9c95e59d3d 100644 --- a/services/web/frontend/js/features/source-editor/components/toolbar/toolbar-layout.tsx +++ b/services/web/frontend/js/features/source-editor/components/toolbar/toolbar-layout.tsx @@ -12,6 +12,8 @@ import { ToggleSearchButton } from './toggle-search-button' import SwitchToPDFButton from '../switch-to-pdf-button' import importOverleafModules from '../../../../../macros/import-overleaf-module.macro' import { useFeatureFlag } from '@/shared/context/split-test-context' +import useIsNetworkStalled from '@/features/ide-react/hooks/use-is-network-stalled' +import DownloadFileButton from './download-file-button' type ToolbarLayoutProps = { children: ReactNode @@ -81,8 +83,10 @@ function Right({ const visualPreviewEnabled = useFeatureFlag('visual-preview') const isToolbarMigration = useFeatureFlag('writefull-toolbar-migration') const { features } = useProjectContext() + const networkIsStalled = useIsNetworkStalled() return (
+ {networkIsStalled && } {!visualPreviewEnabled && } {/* trackChangesVisible controls provider/UI availability; trackChanges (checked inside the switcher) controls the actual feature entitlement. diff --git a/services/web/frontend/stylesheets/components/editor-toolbar.scss b/services/web/frontend/stylesheets/components/editor-toolbar.scss index 62af076f7e..3682a970d4 100644 --- a/services/web/frontend/stylesheets/components/editor-toolbar.scss +++ b/services/web/frontend/stylesheets/components/editor-toolbar.scss @@ -60,6 +60,7 @@ margin-left: auto; display: flex; align-items: center; + gap: var(--spacing-03); .ol-cm-toolbar-button { display: inline-flex; @@ -70,6 +71,14 @@ width: 24px; height: 24px; } + + &::before { + content: ''; + display: block; + width: 1px; + height: 16px; + background-color: var(--toolbar-dropdown-divider-color); + } } .ol-toolbar-layout-left { diff --git a/services/web/frontend/stylesheets/pages/editor/toolbar.scss b/services/web/frontend/stylesheets/pages/editor/toolbar.scss index 0743ff79ee..619dc79dd4 100644 --- a/services/web/frontend/stylesheets/pages/editor/toolbar.scss +++ b/services/web/frontend/stylesheets/pages/editor/toolbar.scss @@ -381,15 +381,6 @@ align-items: center; white-space: nowrap; - &::before { - content: ''; - display: block; - width: 1px; - height: 16px; - background-color: var(--toolbar-dropdown-divider-color); - margin-right: var(--spacing-02); - } - .toggle-switch-label span { background: none; transition: background 0.12s ease-out; @@ -403,6 +394,10 @@ padding-right: var(--spacing-04); border-right: none; } + + .toggle-switch { + margin-right: 0; + } } /************************************** diff --git a/services/web/locales/en.json b/services/web/locales/en.json index a9fe621591..a8e3df9086 100644 --- a/services/web/locales/en.json +++ b/services/web/locales/en.json @@ -693,6 +693,7 @@ "download_as_pdf": "Download as PDF", "download_as_source_zip": "Download as source (.zip)", "download_csv": "Download CSV", + "download_file": "Download file", "download_metadata": "Download Overleaf metadata", "download_pdf": "Download PDF", "download_zip_file": "Download .zip file",