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",