فهرست منبع

Merge pull request #23848 from overleaf/dp-settings

Add initial components for new editor settings modal

GitOrigin-RevId: e3eb511d2af9265e0fc1cf54178b3e2953717950
Mathias Jakobsen 1 سال پیش
والد
کامیت
733bc26ed1
20فایلهای تغییر یافته به همراه438 افزوده شده و 30 حذف شده
  1. 8 5
      services/web/frontend/extracted-translations.json
  2. BIN
      services/web/frontend/fonts/material-symbols/MaterialSymbolsRoundedUnfilledPartialSlice.woff2
  3. 1 0
      services/web/frontend/fonts/material-symbols/unfilled-symbols.mjs
  4. 10 0
      services/web/frontend/js/features/ide-redesign/components/settings/appearance-settings/appearance-settings.tsx
  5. 45 0
      services/web/frontend/js/features/ide-redesign/components/settings/appearance-settings/overall-theme-setting.tsx
  6. 90 0
      services/web/frontend/js/features/ide-redesign/components/settings/dropdown-setting.tsx
  7. 19 0
      services/web/frontend/js/features/ide-redesign/components/settings/editor-settings/auto-close-brackets-setting.tsx
  8. 18 0
      services/web/frontend/js/features/ide-redesign/components/settings/editor-settings/auto-complete-setting.tsx
  9. 18 0
      services/web/frontend/js/features/ide-redesign/components/settings/editor-settings/code-check-setting.tsx
  10. 25 0
      services/web/frontend/js/features/ide-redesign/components/settings/editor-settings/editor-settings.tsx
  11. 46 0
      services/web/frontend/js/features/ide-redesign/components/settings/editor-settings/editor-theme-setting.tsx
  12. 18 0
      services/web/frontend/js/features/ide-redesign/components/settings/editor-settings/math-preview-setting.tsx
  13. 23 0
      services/web/frontend/js/features/ide-redesign/components/settings/setting.tsx
  14. 10 14
      services/web/frontend/js/features/ide-redesign/components/settings/settings-modal-body.tsx
  15. 18 0
      services/web/frontend/js/features/ide-redesign/components/settings/settings-section.tsx
  16. 34 0
      services/web/frontend/js/features/ide-redesign/components/settings/toggle-setting.tsx
  17. 43 2
      services/web/frontend/stylesheets/bootstrap-5/pages/editor/settings.scss
  18. 9 6
      services/web/locales/en.json
  19. 2 2
      services/web/test/frontend/components/editor-left-menu/editor-left-menu.spec.tsx
  20. 1 1
      services/web/test/frontend/features/editor-left-menu/components/settings/settings-auto-close-brackets.test.tsx

+ 8 - 5
services/web/frontend/extracted-translations.json

@@ -134,6 +134,7 @@
   "anyone_with_link_can_edit": "",
   "anyone_with_link_can_view": "",
   "app_on_x": "",
+  "appearance": "",
   "apply_suggestion": "",
   "archive": "",
   "archive_projects": "",
@@ -159,6 +160,7 @@
   "autocompile_disabled_reason": "",
   "autocomplete": "",
   "autocomplete_references": "",
+  "automatically_inserts_closing_brackets_and_parentheses": "",
   "available_with_group_professional": "",
   "back": "",
   "back_to_configuration": "",
@@ -235,6 +237,7 @@
   "change_the_ownership_of_your_personal_projects": "",
   "change_to_group_plan": "",
   "change_to_this_plan": "",
+  "changes_the_color_scheme_of_the_code_editor": "",
   "changing_the_position_of_your_figure": "",
   "changing_the_position_of_your_table": "",
   "chat": "",
@@ -317,6 +320,7 @@
   "continue_to": "",
   "continue_using_free_features": "",
   "continue_with_free_plan": "",
+  "controls_the_theme_of_the_application_interface": "",
   "copied": "",
   "copy": "",
   "copy_code": "",
@@ -469,7 +473,6 @@
   "editor_limit_exceeded_in_this_project": "",
   "editor_only": "",
   "editor_only_hide_pdf": "",
-  "editor_settings": "",
   "editor_theme": "",
   "educational_disclaimer": "",
   "educational_disclaimer_heading": "",
@@ -494,6 +497,8 @@
   "enable_stop_on_first_error_under_recompile_dropdown_menu": "",
   "enable_stop_on_first_error_under_recompile_dropdown_menu_v2": "",
   "enabled": "",
+  "enables_a_real_time_equation_preview_in_the_editor": "",
+  "enables_real_time_syntax_checking_in_the_editor": "",
   "enabling": "",
   "end_of_document": "",
   "enter_6_digit_code": "",
@@ -592,7 +597,6 @@
   "full_width": "",
   "future_payments": "",
   "general": "",
-  "general_settings": "",
   "generate_token": "",
   "generic_if_problem_continues_contact_us": "",
   "generic_linked_file_compile_error": "",
@@ -806,8 +810,6 @@
   "integrations": "",
   "integrations_like_github": "",
   "interested_in_cheaper_personal_plan": "",
-  "interface": "",
-  "interface_settings": "",
   "invalid_confirmation_code": "",
   "invalid_email": "",
   "invalid_file_name": "",
@@ -1165,7 +1167,6 @@
   "pdf_only_hide_editor": "",
   "pdf_preview_error": "",
   "pdf_rendering_error": "",
-  "pdf_settings": "",
   "pdf_unavailable_for_download": "",
   "pdf_viewer": "",
   "pdf_viewer_error": "",
@@ -1627,6 +1628,7 @@
   "suggested": "",
   "suggested_fix_for_error_in_path": "",
   "suggestion_applied": "",
+  "suggests_code_completions_while_typing": "",
   "support_for_your_browser_is_ending_soon": "",
   "supports_up_to_x_users": "",
   "sure_you_want_to_cancel_plan_change": "",
@@ -1790,6 +1792,7 @@
   "toolbar_toggle_symbol_palette": "",
   "toolbar_undo": "",
   "toolbar_undo_redo_actions": "",
+  "tools": "",
   "tooltip_hide_filetree": "",
   "tooltip_hide_panel": "",
   "tooltip_hide_pdf": "",

BIN
services/web/frontend/fonts/material-symbols/MaterialSymbolsRoundedUnfilledPartialSlice.woff2


+ 1 - 0
services/web/frontend/fonts/material-symbols/unfilled-symbols.mjs

@@ -5,6 +5,7 @@
 
 export default /** @type {const} */ ([
   'book_5',
+  'brush',
   'code',
   'create_new_folder',
   'description',

+ 10 - 0
services/web/frontend/js/features/ide-redesign/components/settings/appearance-settings/appearance-settings.tsx

@@ -0,0 +1,10 @@
+import SettingsSection from '../settings-section'
+import OverallThemeSetting from '../appearance-settings/overall-theme-setting'
+
+export default function AppearanceSettings() {
+  return (
+    <SettingsSection>
+      <OverallThemeSetting />
+    </SettingsSection>
+  )
+}

+ 45 - 0
services/web/frontend/js/features/ide-redesign/components/settings/appearance-settings/overall-theme-setting.tsx

@@ -0,0 +1,45 @@
+import { useProjectSettingsContext } from '@/features/editor-left-menu/context/project-settings-context'
+import DropdownSetting from '../dropdown-setting'
+import getMeta from '@/utils/meta'
+import { useMemo } from 'react'
+import type { Option } from '../dropdown-setting'
+import { useTranslation } from 'react-i18next'
+import { OverallThemeMeta } from '../../../../../../../types/project-settings'
+import { isIEEEBranded } from '@/utils/is-ieee-branded'
+import { useLayoutContext } from '@/shared/context/layout-context'
+import { OverallTheme } from '@/shared/utils/styles'
+
+export default function OverallThemeSetting() {
+  const { t } = useTranslation()
+  const overallThemes = getMeta('ol-overallThemes') as
+    | OverallThemeMeta[]
+    | undefined
+
+  const { loadingStyleSheet } = useLayoutContext()
+  const { overallTheme, setOverallTheme } = useProjectSettingsContext()
+
+  const options: Array<Option<OverallTheme>> = useMemo(
+    () =>
+      overallThemes?.map(({ name, val }) => ({
+        value: val,
+        label: name,
+      })) ?? [],
+    [overallThemes]
+  )
+
+  if (!overallThemes || isIEEEBranded()) {
+    return null
+  }
+
+  return (
+    <DropdownSetting
+      id="overallTheme"
+      label={t('overall_theme')}
+      description={t('controls_the_theme_of_the_application_interface')}
+      options={options}
+      onChange={setOverallTheme}
+      value={overallTheme}
+      loading={loadingStyleSheet}
+    />
+  )
+}

+ 90 - 0
services/web/frontend/js/features/ide-redesign/components/settings/dropdown-setting.tsx

@@ -0,0 +1,90 @@
+import OLFormSelect from '@/features/ui/components/ol/ol-form-select'
+import { ChangeEventHandler, useCallback } from 'react'
+import Setting from './setting'
+import classNames from 'classnames'
+import { Spinner } from 'react-bootstrap-5'
+
+type PossibleValue = string | number
+
+export type Option<T extends PossibleValue = string> = {
+  value: T
+  label: string
+  ariaHidden?: 'true' | 'false'
+  disabled?: boolean
+}
+
+export type Optgroup<T extends PossibleValue = string> = {
+  label: string
+  options: Array<Option<T>>
+}
+
+type SettingsMenuSelectProps<T extends PossibleValue = string> = {
+  id: string
+  label: string
+  description: string
+  options: Array<Option<T>>
+  onChange: (val: T) => void
+  value?: T
+  disabled?: boolean
+  width?: 'default' | 'wide'
+  loading?: boolean
+}
+
+export default function DropdownSetting<T extends PossibleValue = string>({
+  id,
+  label,
+  description,
+  options,
+  onChange,
+  value,
+  disabled = false,
+  width = 'default',
+  loading = false,
+}: SettingsMenuSelectProps<T>) {
+  const handleChange: ChangeEventHandler<HTMLSelectElement> = useCallback(
+    event => {
+      const selectedValue = event.target.value
+      let onChangeValue: PossibleValue = selectedValue
+      if (typeof value === 'number') {
+        onChangeValue = parseInt(selectedValue, 10)
+      }
+      onChange(onChangeValue as T)
+    },
+    [onChange, value]
+  )
+
+  return (
+    <Setting controlId={id} label={label} description={description}>
+      {loading ? (
+        <Spinner
+          animation="border"
+          aria-hidden="true"
+          size="sm"
+          role="status"
+        />
+      ) : (
+        <OLFormSelect
+          id={id}
+          className={classNames('ide-dropdown-setting', {
+            'ide-dropdown-setting-wide': width === 'wide',
+          })}
+          size="sm"
+          onChange={handleChange}
+          value={value?.toString()}
+          disabled={disabled}
+        >
+          {options.map(option => (
+            <option
+              key={`${id}-${option.value}`}
+              value={option.value.toString()}
+              aria-hidden={option.ariaHidden}
+              disabled={option.disabled}
+            >
+              {option.label}
+            </option>
+          ))}
+        </OLFormSelect>
+      )}
+    </Setting>
+  )
+}

+ 19 - 0
services/web/frontend/js/features/ide-redesign/components/settings/editor-settings/auto-close-brackets-setting.tsx

@@ -0,0 +1,19 @@
+import { useProjectSettingsContext } from '@/features/editor-left-menu/context/project-settings-context'
+import ToggleSetting from '../toggle-setting'
+import { useTranslation } from 'react-i18next'
+
+export default function AutoCloseBracketsSetting() {
+  const { autoPairDelimiters, setAutoPairDelimiters } =
+    useProjectSettingsContext()
+  const { t } = useTranslation()
+
+  return (
+    <ToggleSetting
+      id="autoPairDelimiters"
+      label={t('auto_close_brackets')}
+      description={t('automatically_inserts_closing_brackets_and_parentheses')}
+      checked={autoPairDelimiters}
+      onChange={setAutoPairDelimiters}
+    />
+  )
+}

+ 18 - 0
services/web/frontend/js/features/ide-redesign/components/settings/editor-settings/auto-complete-setting.tsx

@@ -0,0 +1,18 @@
+import { useProjectSettingsContext } from '@/features/editor-left-menu/context/project-settings-context'
+import ToggleSetting from '../toggle-setting'
+import { useTranslation } from 'react-i18next'
+
+export default function AutoCompleteSetting() {
+  const { autoComplete, setAutoComplete } = useProjectSettingsContext()
+  const { t } = useTranslation()
+
+  return (
+    <ToggleSetting
+      id="autoComplete"
+      label={t('auto_complete')}
+      description={t('suggests_code_completions_while_typing')}
+      checked={autoComplete}
+      onChange={setAutoComplete}
+    />
+  )
+}

+ 18 - 0
services/web/frontend/js/features/ide-redesign/components/settings/editor-settings/code-check-setting.tsx

@@ -0,0 +1,18 @@
+import { useProjectSettingsContext } from '@/features/editor-left-menu/context/project-settings-context'
+import ToggleSetting from '../toggle-setting'
+import { useTranslation } from 'react-i18next'
+
+export default function CodeCheckSetting() {
+  const { syntaxValidation, setSyntaxValidation } = useProjectSettingsContext()
+  const { t } = useTranslation()
+
+  return (
+    <ToggleSetting
+      id="syntaxValidation"
+      label={t('syntax_validation')}
+      description={t('enables_real_time_syntax_checking_in_the_editor')}
+      checked={syntaxValidation}
+      onChange={setSyntaxValidation}
+    />
+  )
+}

+ 25 - 0
services/web/frontend/js/features/ide-redesign/components/settings/editor-settings/editor-settings.tsx

@@ -0,0 +1,25 @@
+import EditorThemeSetting from './editor-theme-setting'
+import AutoCompleteSetting from './auto-complete-setting'
+import CodeCheckSetting from './code-check-setting'
+import AutoCloseBracketsSetting from './auto-close-brackets-setting'
+import SettingsSection from '../settings-section'
+import MathPreviewSetting from './math-preview-setting'
+import { useTranslation } from 'react-i18next'
+
+export default function EditorSettings() {
+  const { t } = useTranslation()
+
+  return (
+    <>
+      <SettingsSection>
+        <EditorThemeSetting />
+        <AutoCompleteSetting />
+        <AutoCloseBracketsSetting />
+        <CodeCheckSetting />
+      </SettingsSection>
+      <SettingsSection title={t('tools')}>
+        <MathPreviewSetting />
+      </SettingsSection>
+    </>
+  )
+}

+ 46 - 0
services/web/frontend/js/features/ide-redesign/components/settings/editor-settings/editor-theme-setting.tsx

@@ -0,0 +1,46 @@
+import { useProjectSettingsContext } from '@/features/editor-left-menu/context/project-settings-context'
+import DropdownSetting from '../dropdown-setting'
+import getMeta from '@/utils/meta'
+import { useMemo } from 'react'
+import type { Option } from '../dropdown-setting'
+import { useTranslation } from 'react-i18next'
+
+export default function EditorThemeSetting() {
+  const editorThemes = getMeta('ol-editorThemes')
+  const legacyEditorThemes = getMeta('ol-legacyEditorThemes')
+  const { editorTheme, setEditorTheme } = useProjectSettingsContext()
+  const { t } = useTranslation()
+
+  const options = useMemo(() => {
+    const editorThemeOptions: Array<Option> =
+      editorThemes?.map(theme => ({
+        value: theme,
+        label: theme.replace(/_/g, ' '),
+      })) ?? []
+
+    const dividerOption: Option = {
+      value: '-',
+      label: '—————————————————',
+      disabled: true,
+    }
+
+    const legacyEditorThemeOptions: Array<Option> =
+      legacyEditorThemes?.map(theme => ({
+        value: theme,
+        label: theme.replace(/_/g, ' ') + ' (Legacy)',
+      })) ?? []
+
+    return [...editorThemeOptions, dividerOption, ...legacyEditorThemeOptions]
+  }, [editorThemes, legacyEditorThemes])
+
+  return (
+    <DropdownSetting
+      id="editorTheme"
+      label={t('editor_theme')}
+      description={t('changes_the_color_scheme_of_the_code_editor')}
+      options={options}
+      onChange={setEditorTheme}
+      value={editorTheme}
+    />
+  )
+}

+ 18 - 0
services/web/frontend/js/features/ide-redesign/components/settings/editor-settings/math-preview-setting.tsx

@@ -0,0 +1,18 @@
+import { useProjectSettingsContext } from '@/features/editor-left-menu/context/project-settings-context'
+import ToggleSetting from '../toggle-setting'
+import { useTranslation } from 'react-i18next'
+
+export default function MathPreviewSetting() {
+  const { mathPreview, setMathPreview } = useProjectSettingsContext()
+  const { t } = useTranslation()
+
+  return (
+    <ToggleSetting
+      id="mathPreview"
+      label={t('equation_preview')}
+      description={t('enables_a_real_time_equation_preview_in_the_editor')}
+      checked={mathPreview}
+      onChange={setMathPreview}
+    />
+  )
+}

+ 23 - 0
services/web/frontend/js/features/ide-redesign/components/settings/setting.tsx

@@ -0,0 +1,23 @@
+export default function Setting({
+  label,
+  description,
+  controlId,
+  children,
+}: {
+  label: string
+  description: string
+  controlId: string
+  children: React.ReactNode
+}) {
+  return (
+    <div className="ide-setting">
+      <div>
+        <label htmlFor={controlId} className="ide-setting-title">
+          {label}
+        </label>
+        <div className="ide-setting-description">{description}</div>
+      </div>
+      {children}
+    </div>
+  )
+}

+ 10 - 14
services/web/frontend/js/features/ide-redesign/components/settings/settings-modal-body.tsx

@@ -10,6 +10,8 @@ import {
   TabPane,
 } from 'react-bootstrap-5'
 import { useTranslation } from 'react-i18next'
+import EditorSettings from './editor-settings/editor-settings'
+import AppearanceSettings from './appearance-settings/appearance-settings'
 
 export type SettingsEntry = SettingsLink | SettingsTab
 
@@ -18,7 +20,6 @@ type SettingsTab = {
   key: string
   component: ReactElement
   title: string
-  subtitle: string
 }
 
 type SettingsLink = {
@@ -35,30 +36,26 @@ export const SettingsModalBody = () => {
       {
         key: 'general',
         title: t('general'),
-        subtitle: t('general_settings'),
         icon: 'settings',
         component: <div>General</div>,
       },
       {
         key: 'editor',
         title: t('editor'),
-        subtitle: t('editor_settings'),
         icon: 'code',
-        component: <div>Editor</div>,
+        component: <EditorSettings />,
       },
       {
         key: 'pdf',
         title: t('pdf'),
-        subtitle: t('pdf_settings'),
         icon: 'picture_as_pdf',
         component: <div>PDF</div>,
       },
       {
-        key: 'interface',
-        title: t('interface'),
-        subtitle: t('interface_settings'),
-        icon: 'web_asset',
-        component: <div>Interface</div>,
+        key: 'appearance',
+        title: t('appearance'),
+        icon: 'brush',
+        component: <AppearanceSettings />,
       },
       {
         key: 'account_settings',
@@ -89,13 +86,12 @@ export const SettingsModalBody = () => {
             <SettingsNavLink entry={entry} key={entry.key} />
           ))}
         </Nav>
-        <TabContent>
+        <TabContent className="ide-settings-tab-content">
           {settingsTabs
             .filter(t => 'component' in t)
-            .map(({ key, component, subtitle }) => (
+            .map(({ key, component }) => (
               <TabPane eventKey={key} key={key}>
-                <p className="ide-settings-tab-subtitle">{subtitle}</p>
-                <div className="ide-settings-tab-content">{component}</div>
+                {component}
               </TabPane>
             ))}
         </TabContent>

+ 18 - 0
services/web/frontend/js/features/ide-redesign/components/settings/settings-section.tsx

@@ -0,0 +1,18 @@
+export default function SettingsSection({
+  children,
+  title,
+}: {
+  children: React.ReactNode | React.ReactNode[]
+  title?: string
+}) {
+  if (!children) {
+    return null
+  }
+
+  return (
+    <div className="ide-settings-section">
+      {title && <div className="ide-settings-section-title">{title}</div>}
+      {children}
+    </div>
+  )
+}

+ 34 - 0
services/web/frontend/js/features/ide-redesign/components/settings/toggle-setting.tsx

@@ -0,0 +1,34 @@
+import Setting from './setting'
+import OLFormSwitch from '@/features/ui/components/ol/ol-form-switch'
+
+export default function ToggleSetting({
+  id,
+  label,
+  description,
+  checked,
+  onChange,
+  disabled,
+}: {
+  id: string
+  label: string
+  description: string
+  checked: boolean | undefined
+  onChange: (newValue: boolean) => void
+  disabled?: boolean
+}) {
+  const handleChange = () => {
+    onChange(!checked)
+  }
+
+  return (
+    <Setting controlId={id} label={label} description={description}>
+      <OLFormSwitch
+        id={id}
+        onChange={handleChange}
+        checked={checked}
+        label={label}
+        disabled={disabled}
+      />
+    </Setting>
+  )
+}

+ 43 - 2
services/web/frontend/stylesheets/bootstrap-5/pages/editor/settings.scss

@@ -7,9 +7,11 @@
 }
 
 .ide-settings-tab-content {
-  padding: var(--spacing-06) var(--spacing-08);
   max-height: 75%;
-  height: 400px;
+  height: 500px;
+  width: 100%;
+  padding: 0 var(--spacing-06);
+  overflow: auto;
 }
 
 .ide-settings-tab-subtitle {
@@ -43,3 +45,42 @@
 .ide-settings-modal-body {
   padding: 0;
 }
+
+.ide-settings-section {
+  .ide-setting:not(:last-child) {
+    border-bottom: 1px solid var(--border-divider);
+  }
+}
+
+.ide-settings-section-title {
+  color: var(--content-secondary);
+  font-size: var(--font-size-01);
+  line-height: var(--line-height-01);
+  margin-top: var(--spacing-06);
+  margin-bottom: var(--spacing-03);
+}
+
+.ide-setting {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  padding: var(--spacing-06) 0;
+}
+
+.ide-dropdown-setting {
+  width: 120px;
+}
+
+.ide-dropdown-setting-wide {
+  width: 160px;
+}
+
+.ide-setting-title {
+  color: var(--content-primary);
+  font-size: var(--font-size-02);
+}
+
+.ide-setting-description {
+  color: var(--content-secondary);
+  font-size: var(--font-size-02);
+}

+ 9 - 6
services/web/locales/en.json

@@ -164,6 +164,7 @@
   "anyone_with_link_can_edit": "Anyone with this link can edit this project",
   "anyone_with_link_can_view": "Anyone with this link can view this project",
   "app_on_x": "__appName__ on __social__",
+  "appearance": "Appearance",
   "apply_educational_discount": "Apply educational discount",
   "apply_educational_discount_description": "40% discount for groups using __appName__ for teaching",
   "apply_suggestion": "Apply suggestion",
@@ -191,7 +192,7 @@
   "at_most_x_libraries_can_be_selected": "At most __maxCount__ libraries can be selected",
   "august": "August",
   "author": "Author",
-  "auto_close_brackets": "Auto-close Brackets",
+  "auto_close_brackets": "Auto-close brackets",
   "auto_compile": "Auto Compile",
   "auto_complete": "Auto-complete",
   "autocompile_disabled": "Autocompile disabled",
@@ -199,6 +200,7 @@
   "autocomplete": "Autocomplete",
   "autocomplete_references": "Reference Autocomplete (inside a <code>\\cite{}</code> block)",
   "automatic_user_registration_uppercase": "Automatic user registration",
+  "automatically_inserts_closing_brackets_and_parentheses": "Automatically inserts closing brackets and parentheses",
   "available_with_group_professional": "Available with Group Professional",
   "back": "Back",
   "back_to_account_settings": "Back to account settings",
@@ -300,6 +302,7 @@
   "change_the_ownership_of_your_personal_projects": "Change the ownership of your personal projects to the new account. <0>Find out how to change project owner.</0>",
   "change_to_group_plan": "Change to a group plan",
   "change_to_this_plan": "Change to this plan",
+  "changes_the_color_scheme_of_the_code_editor": "Changes the color scheme of the code editor",
   "changing_the_position_of_your_figure": "Changing the position of your figure",
   "changing_the_position_of_your_table": "Changing the position of your table",
   "chat": "Chat",
@@ -414,6 +417,7 @@
   "continue_using_free_features": "Continue using our free features",
   "continue_with_free_plan": "Continue with free plan",
   "continue_with_service": "Continue with __service__",
+  "controls_the_theme_of_the_application_interface": "Controls the theme of the application interface",
   "copied": "Copied",
   "copy": "Copy",
   "copy_code": "Copy code",
@@ -604,7 +608,6 @@
   "editor_limit_exceeded_in_this_project": "Too many editors in this project",
   "editor_only": "Editor only",
   "editor_only_hide_pdf": "Editor only <0>(hide PDF)</0>",
-  "editor_settings": "Editor settings",
   "editor_theme": "Editor theme",
   "educational_disclaimer": "I confirm that users will be students or faculty using Overleaf primarily for study and teaching, and can provide evidence of this if requested.",
   "educational_disclaimer_heading": "Educational discount confirmation",
@@ -639,6 +642,8 @@
   "enable_stop_on_first_error_under_recompile_dropdown_menu": "Enable <0>“Stop on first error”</0> under the <1>Recompile</1> drop-down menu to help you find and fix errors right away.",
   "enable_stop_on_first_error_under_recompile_dropdown_menu_v2": "Enable <0>Stop on first error</0> under the <1>Recompile</1> drop-down menu to help you find and fix errors right away.",
   "enabled": "Enabled",
+  "enables_a_real_time_equation_preview_in_the_editor": "Enables a real-time equation preview in the editor",
+  "enables_real_time_syntax_checking_in_the_editor": "Enables real-time syntax checking in the editor",
   "enabling": "Enabling",
   "end_of_document": "End of document",
   "enter_6_digit_code": "Enter 6-digit code",
@@ -787,7 +792,6 @@
   "gallery_page_title": "Gallery - Templates, Examples and Articles written in LaTeX",
   "gallery_show_more_tags": "Show more",
   "general": "General",
-  "general_settings": "General settings",
   "generate_token": "Generate token",
   "generic_if_problem_continues_contact_us": "If the problem continues please contact us",
   "generic_linked_file_compile_error": "This project’s output files are not available because it failed to compile. Please open the project to see the compilation error details.",
@@ -1042,8 +1046,6 @@
   "integrations": "Integrations",
   "integrations_like_github": "Integrations like GitHub Sync",
   "interested_in_cheaper_personal_plan": "Would you be interested in the cheaper <0>__price__</0> Personal plan?",
-  "interface": "Interface",
-  "interface_settings": "Interface settings",
   "invalid_certificate": "Invalid certificate. Please check the certificate and try again.",
   "invalid_confirmation_code": "That didn’t work. Please check the code and try again.",
   "invalid_email": "An email address is invalid",
@@ -1551,7 +1553,6 @@
   "pdf_only_hide_editor": "PDF only <0>(hide editor)</0>",
   "pdf_preview_error": "There was a problem displaying the compilation results for this project.",
   "pdf_rendering_error": "PDF Rendering Error",
-  "pdf_settings": "PDF settings",
   "pdf_unavailable_for_download": "PDF unavailable for download",
   "pdf_viewer": "PDF Viewer",
   "pdf_viewer_error": "There was a problem displaying the PDF for this project.",
@@ -2116,6 +2117,7 @@
   "suggested_fix_for_error_in_path": "Suggested fix for error in __path__",
   "suggestion": "Suggestion",
   "suggestion_applied": "Suggestion applied",
+  "suggests_code_completions_while_typing": "Suggests code completions while typing",
   "support": "Support",
   "support_for_your_browser_is_ending_soon": "Support for your browser is ending soon",
   "supports_up_to_x_users": "Supports up to <0>__count__ users</0>",
@@ -2310,6 +2312,7 @@
   "toolbar_toggle_symbol_palette": "Toggle Symbol Palette",
   "toolbar_undo": "Undo",
   "toolbar_undo_redo_actions": "Undo/Redo actions",
+  "tools": "Tools",
   "tooltip_hide_filetree": "Click to hide the file tree",
   "tooltip_hide_panel": "Click to hide the panel",
   "tooltip_hide_pdf": "Click to hide the PDF",

+ 2 - 2
services/web/test/frontend/components/editor-left-menu/editor-left-menu.spec.tsx

@@ -92,7 +92,7 @@ describe('<EditorLeftMenu />', function () {
       cy.findByLabelText('Main document')
       cy.findByLabelText('Spell check')
       cy.findByLabelText('Auto-complete')
-      cy.findByLabelText('Auto-close Brackets')
+      cy.findByLabelText('Auto-close brackets')
       cy.findByLabelText('Code check')
       cy.findByLabelText('Editor theme')
       cy.findByLabelText('Overall theme')
@@ -886,7 +886,7 @@ describe('<EditorLeftMenu />', function () {
       cy.findByLabelText('Main document').should('not.exist')
       cy.findByLabelText('Spell check').should('not.exist')
       cy.findByLabelText('Auto-complete').should('not.exist')
-      cy.findByLabelText('Auto-close Brackets').should('not.exist')
+      cy.findByLabelText('Auto-close brackets').should('not.exist')
       cy.findByLabelText('Code check').should('not.exist')
       cy.findByLabelText('Editor theme').should('not.exist')
       cy.findByLabelText('Overall theme').should('not.exist')

+ 1 - 1
services/web/test/frontend/features/editor-left-menu/components/settings/settings-auto-close-brackets.test.tsx

@@ -19,7 +19,7 @@ describe('<SettingsAutoCloseBrackets />', function () {
       </EditorProviders>
     )
 
-    const select = screen.getByLabelText('Auto-close Brackets')
+    const select = screen.getByLabelText('Auto-close brackets')
 
     const optionOn = within(select).getByText('On')
     expect(optionOn.getAttribute('value')).to.equal('true')