diff --git a/services/web/app/views/project/editor/meta.pug b/services/web/app/views/project/editor/meta.pug index a4559fa7b7..034bc7ba2d 100644 --- a/services/web/app/views/project/editor/meta.pug +++ b/services/web/app/views/project/editor/meta.pug @@ -26,6 +26,11 @@ meta(name="ol-galileoEnabled" data-type="string" content=galileoEnabled) meta(name="ol-galileoPromptWords" data-type="string" content=galileoPromptWords) meta(name="ol-galileoFeatures" data-type="json" content=galileoFeatures) meta(name="ol-detachRole" data-type="string" content=detachRole) +meta(name="ol-allowedImageNames" data-type="json" content=allowedImageNames) +meta(name="ol-languages" data-type="json" content=languages) +meta(name="ol-dictionaryEditorEnabled" data-type="boolean" content=dictionaryEditorEnabled) +meta(name="ol-editorThemes" data-type="json" content=editorThemes) +meta(name="ol-legacyEditorThemes" data-type="json" content=legacyEditorThemes) meta(name="ol-showUpgradePrompt" data-type="boolean" content=showUpgradePrompt) meta(name="ol-useOpenTelemetry" data-type="boolean" content=useOpenTelemetry) meta(name="ol-showSupport", data-type="boolean" content=showSupport) diff --git a/services/web/frontend/extracted-translations.json b/services/web/frontend/extracted-translations.json index 1a059fe8a2..0f39ad8af0 100644 --- a/services/web/frontend/extracted-translations.json +++ b/services/web/frontend/extracted-translations.json @@ -36,7 +36,9 @@ "ask_proj_owner_to_upgrade_for_git_bridge": "", "ask_proj_owner_to_upgrade_for_longer_compiles": "", "ask_proj_owner_to_upgrade_for_references_search": "", + "auto_close_brackets": "", "auto_compile": "", + "auto_complete": "", "autocompile_disabled": "", "autocompile_disabled_reason": "", "autocomplete": "", @@ -47,6 +49,7 @@ "beta_program_not_participating": "", "blank_project": "", "blocked_filename": "", + "browser": "", "can_edit": "", "can_link_institution_email_acct_to_institution_acct": "", "can_link_your_institution_acct_2": "", @@ -88,11 +91,13 @@ "commit": "", "common": "", "commons_plan_tooltip": "", + "compact": "", "compile_error_entry_description": "", "compile_error_handling": "", "compile_larger_projects": "", "compile_mode": "", "compile_terminated_by_user": "", + "compiler": "", "compiling": "", "confirm": "", "confirm_affiliation": "", @@ -131,6 +136,7 @@ "department": "", "descending": "", "description": "", + "dictionary": "", "did_you_know_institution_providing_professional": "", "disable_stop_on_first_error": "", "dismiss": "", @@ -162,6 +168,7 @@ "duplicate_file": "", "duplicate_projects": "", "easily_manage_your_project_files_everywhere": "", + "edit": "", "edit_dictionary": "", "edit_dictionary_empty": "", "edit_dictionary_remove": "", @@ -169,6 +176,7 @@ "editing": "", "editor_and_pdf": "", "editor_only_hide_pdf": "", + "editor_theme": "", "email": "", "email_or_password_wrong_try_again": "", "emails_and_affiliations_explanation": "", @@ -198,6 +206,8 @@ "first_name": "", "fold_line": "", "following_paths_conflict": "", + "font_family": "", + "font_size": "", "free_accounts_have_timeout_upgrade_to_increase": "", "free_plan_label": "", "free_plan_tooltip": "", @@ -323,6 +333,7 @@ "is_email_affiliated": "", "join_project": "", "joining": "", + "keybindings": "", "labs_program_already_participating": "", "labs_program_benefits": "<0>", "labs_program_not_participating": "", @@ -339,6 +350,7 @@ "leave_projects": "", "let_us_know": "", "limited_offer": "", + "line_height": "", "link": "", "link_account": "", "link_accounts": "", @@ -368,6 +380,7 @@ "login_with_service": "", "logs_and_output_files": "", "looks_like_youre_at": "", + "main_document": "", "main_file_not_found": "", "make_a_copy": "", "make_email_primary_description": "", @@ -433,6 +446,8 @@ "or": "", "other_logs_and_files": "", "other_output_files": "", + "overall_theme": "", + "overleaf": "", "overleaf_labs": "", "owned_by_x": "", "owner": "", @@ -448,6 +463,7 @@ "pdf_only_hide_editor": "", "pdf_preview_error": "", "pdf_rendering_error": "", + "pdf_viewer": "", "pdf_viewer_error": "", "plan_tooltip": "", "please_change_primary_to_remove": "", @@ -579,6 +595,7 @@ "session_error": "", "session_expired_redirecting_to_login": "", "sessions": "", + "settings": "", "share": "", "share_project": "", "share_with_your_collabs": "", @@ -605,6 +622,7 @@ "sort_by": "", "sort_by_x": "", "source": "", + "spell_check": "", "sso_link_error": "", "start_by_adding_your_email": "", "start_free_trial": "", @@ -624,6 +642,7 @@ "sync_project_to_github_explanation": "", "sync_to_dropbox": "", "sync_to_github": "", + "syntax_validation": "", "tab_connecting": "", "tab_no_longer_connected": "", "tag_name_cannot_exceed_characters": "", @@ -634,6 +653,7 @@ "template_approved_by_publisher": "", "templates": "", "terminated": "", + "tex_live_version": "", "thank_you_exclamation": "", "thanks_settings_updated": "", "this_action_cannot_be_undone": "", @@ -709,6 +729,7 @@ "we_cant_find_any_sections_or_subsections_in_this_file": "", "we_logged_you_in": "", "welcome_to_sl": "", + "wide": "", "with_premium_subscription_you_also_get": "", "word_count": "", "work_offline": "", diff --git a/services/web/frontend/js/features/editor-left-menu/components/editor-left-menu.tsx b/services/web/frontend/js/features/editor-left-menu/components/editor-left-menu.tsx index f64676445c..3e00fd058d 100644 --- a/services/web/frontend/js/features/editor-left-menu/components/editor-left-menu.tsx +++ b/services/web/frontend/js/features/editor-left-menu/components/editor-left-menu.tsx @@ -4,6 +4,7 @@ import HelpMenu from './help-menu' import { useLayoutContext } from '../../../shared/context/layout-context' import classNames from 'classnames' import SyncMenu from './sync-menu' +import SettingsMenu from './settings-menu' export default function EditorLeftMenu() { const { leftMenuShown, setLeftMenuShown } = useLayoutContext() @@ -17,6 +18,7 @@ export default function EditorLeftMenu() { + {leftMenuShown ? ( diff --git a/services/web/frontend/js/features/editor-left-menu/components/settings-menu-dropdown.tsx b/services/web/frontend/js/features/editor-left-menu/components/settings-menu-dropdown.tsx new file mode 100644 index 0000000000..e69de29bb2 diff --git a/services/web/frontend/js/features/editor-left-menu/components/settings-menu.tsx b/services/web/frontend/js/features/editor-left-menu/components/settings-menu.tsx new file mode 100644 index 0000000000..5504419e2f --- /dev/null +++ b/services/web/frontend/js/features/editor-left-menu/components/settings-menu.tsx @@ -0,0 +1,50 @@ +import { Form } from 'react-bootstrap' +import { useTranslation } from 'react-i18next' +import getMeta from '../../../utils/meta' +import SettingsAutoCloseBrackets from './settings/settings-auto-close-brackets' +import SettingsAutoComplete from './settings/settings-auto-complete' +import SettingsCompiler from './settings/settings-compiler' +import SettingsDictionary from './settings/settings-dictionary' +import SettingsDocument from './settings/settings-document' +import SettingsEditorTheme from './settings/settings-editor-theme' +import SettingsFontFamily from './settings/settings-font-family' +import SettingsFontSize from './settings/settings-font-size' +import SettingsImageName from './settings/settings-image-name' +import SettingsKeybindings from './settings/settings-keybindings' +import SettingsLineHeight from './settings/settings-line-height' +import SettingsOverallTheme from './settings/settings-overall-theme' +import SettingsPdfViewer from './settings/settings-pdf-viewer' +import SettingsSpellCheckLanguage from './settings/settings-spell-check-language' +import SettingsSyntaxValidation from './settings/settings-syntax-validation' + +export default function SettingsMenu() { + const { t } = useTranslation() + const anonymous = getMeta('ol-anonymous') as boolean | undefined + + if (anonymous === true || anonymous === undefined) { + return null + } + + return ( + <> +

{t('settings')}

+
+ + + + + + + + + + + + + + + + + + ) +} diff --git a/services/web/frontend/js/features/editor-left-menu/components/settings/settings-auto-close-brackets.tsx b/services/web/frontend/js/features/editor-left-menu/components/settings/settings-auto-close-brackets.tsx new file mode 100644 index 0000000000..5bc2903fb5 --- /dev/null +++ b/services/web/frontend/js/features/editor-left-menu/components/settings/settings-auto-close-brackets.tsx @@ -0,0 +1,23 @@ +import { useTranslation } from 'react-i18next' +import SettingsMenuSelect from './settings-menu-select' + +export default function SettingsAutoCloseBrackets() { + const { t } = useTranslation() + + return ( + + ) +} diff --git a/services/web/frontend/js/features/editor-left-menu/components/settings/settings-auto-complete.tsx b/services/web/frontend/js/features/editor-left-menu/components/settings/settings-auto-complete.tsx new file mode 100644 index 0000000000..eeca5b6658 --- /dev/null +++ b/services/web/frontend/js/features/editor-left-menu/components/settings/settings-auto-complete.tsx @@ -0,0 +1,23 @@ +import { useTranslation } from 'react-i18next' +import SettingsMenuSelect from './settings-menu-select' + +export default function SettingsAutoComplete() { + const { t } = useTranslation() + + return ( + + ) +} diff --git a/services/web/frontend/js/features/editor-left-menu/components/settings/settings-compiler.tsx b/services/web/frontend/js/features/editor-left-menu/components/settings/settings-compiler.tsx new file mode 100644 index 0000000000..bf7307a146 --- /dev/null +++ b/services/web/frontend/js/features/editor-left-menu/components/settings/settings-compiler.tsx @@ -0,0 +1,37 @@ +import { useTranslation } from 'react-i18next' +import { useEditorContext } from '../../../../shared/context/editor-context' +import SettingsMenuSelect from './settings-menu-select' + +export default function SettingsCompiler() { + const { t } = useTranslation() + const { permissionsLevel } = useEditorContext() + + if (permissionsLevel === 'readOnly') { + return null + } + + return ( + + ) +} diff --git a/services/web/frontend/js/features/editor-left-menu/components/settings/settings-dictionary.tsx b/services/web/frontend/js/features/editor-left-menu/components/settings/settings-dictionary.tsx new file mode 100644 index 0000000000..3cbe57f1f3 --- /dev/null +++ b/services/web/frontend/js/features/editor-left-menu/components/settings/settings-dictionary.tsx @@ -0,0 +1,31 @@ +import { useState } from 'react' +import { Button } from 'react-bootstrap' +import { useTranslation } from 'react-i18next' +import getMeta from '../../../../utils/meta' +import DictionaryModal from '../../../dictionary/components/dictionary-modal' + +export default function SettingsDictionary() { + const { t } = useTranslation() + const [showModal, setShowModal] = useState(false) + const dictionaryEditorEnabled = getMeta( + 'ol-dictionaryEditorEnabled' + ) as boolean + + if (!dictionaryEditorEnabled) { + return null + } + + return ( +
+ + + + setShowModal(false)} + /> +
+ ) +} diff --git a/services/web/frontend/js/features/editor-left-menu/components/settings/settings-document.tsx b/services/web/frontend/js/features/editor-left-menu/components/settings/settings-document.tsx new file mode 100644 index 0000000000..76227fc5bb --- /dev/null +++ b/services/web/frontend/js/features/editor-left-menu/components/settings/settings-document.tsx @@ -0,0 +1,53 @@ +import { useMemo } from 'react' +import { useTranslation } from 'react-i18next' +import isValidTeXFile from '../../../../main/is-valid-tex-file' +import { useEditorContext } from '../../../../shared/context/editor-context' +import { useProjectContext } from '../../../../shared/context/project-context' +import useScopeValue from '../../../../shared/hooks/use-scope-value' +import SettingsMenuSelect from './settings-menu-select' +import type { Option } from './settings-menu-select' + +type Doc = { + doc: { + name: string + id: string + type: string + selected: boolean + } + path: string +} + +export default function SettingsDocument() { + const { t } = useTranslation() + + const { permissionsLevel } = useEditorContext() + + const { rootDocId } = useProjectContext() + const [docs] = useScopeValue('docs') + + const validDocsOptions = useMemo(() => { + const filteredDocs = + docs?.filter( + doc => isValidTeXFile(doc.doc.name) || rootDocId === doc.doc.id + ) ?? [] + + const mappedDocs: Array