Jelajahi Sumber

Merge pull request #24391 from overleaf/jdt-move-wf-features-to-ol

Move Writefull table + equation generator to OL toolbar

GitOrigin-RevId: b7bc5b1cde5687360c4f60cb64924da139ccfbe9
Jimmy Domagala-Tang 1 tahun lalu
induk
melakukan
0e49a5d9b0

+ 1 - 0
services/web/app/src/Features/Project/ProjectController.js

@@ -355,6 +355,7 @@ const _ProjectController = {
       'editor-redesign',
       'editor-redesign',
       'paywall-change-compile-timeout',
       'paywall-change-compile-timeout',
       'overleaf-assist-bundle',
       'overleaf-assist-bundle',
+      'wf-feature-rebrand',
     ].filter(Boolean)
     ].filter(Boolean)
 
 
     const getUserValues = async userId =>
     const getUserValues = async userId =>

+ 7 - 0
services/web/frontend/extracted-translations.json

@@ -405,6 +405,7 @@
   "discount_of": "",
   "discount_of": "",
   "discover_the_fastest_way_to_search_and_cite": "",
   "discover_the_fastest_way_to_search_and_cite": "",
   "dismiss_error_popup": "",
   "dismiss_error_popup": "",
+  "display": "",
   "display_deleted_user": "",
   "display_deleted_user": "",
   "display_math": "",
   "display_math": "",
   "do_you_need_edit_access": "",
   "do_you_need_edit_access": "",
@@ -608,6 +609,7 @@
   "full_doc_history": "",
   "full_doc_history": "",
   "full_width": "",
   "full_width": "",
   "future_payments": "",
   "future_payments": "",
+  "generate_from_text_or_image": "",
   "generate_token": "",
   "generate_token": "",
   "generic_if_problem_continues_contact_us": "",
   "generic_if_problem_continues_contact_us": "",
   "generic_linked_file_compile_error": "",
   "generic_linked_file_compile_error": "",
@@ -789,6 +791,7 @@
   "include_the_error_message_and_ai_response": "",
   "include_the_error_message_and_ai_response": "",
   "increase_indent": "",
   "increase_indent": "",
   "increased_compile_timeout": "",
   "increased_compile_timeout": "",
+  "inline": "",
   "inline_math": "",
   "inline_math": "",
   "inr_discount_modal_info": "",
   "inr_discount_modal_info": "",
   "inr_discount_modal_title": "",
   "inr_discount_modal_title": "",
@@ -1483,6 +1486,7 @@
   "select_image_from_project_files": "",
   "select_image_from_project_files": "",
   "select_project": "",
   "select_project": "",
   "select_projects": "",
   "select_projects": "",
+  "select_size": "",
   "select_tag": "",
   "select_tag": "",
   "select_user": "",
   "select_user": "",
   "selected": "",
   "selected": "",
@@ -1789,6 +1793,8 @@
   "toolbar_editor": "",
   "toolbar_editor": "",
   "toolbar_format_bold": "",
   "toolbar_format_bold": "",
   "toolbar_format_italic": "",
   "toolbar_format_italic": "",
+  "toolbar_generate_math": "",
+  "toolbar_generate_table": "",
   "toolbar_increase_indent": "",
   "toolbar_increase_indent": "",
   "toolbar_insert_citation": "",
   "toolbar_insert_citation": "",
   "toolbar_insert_cross_reference": "",
   "toolbar_insert_cross_reference": "",
@@ -1798,6 +1804,7 @@
   "toolbar_insert_link": "",
   "toolbar_insert_link": "",
   "toolbar_insert_math": "",
   "toolbar_insert_math": "",
   "toolbar_insert_math_and_symbols": "",
   "toolbar_insert_math_and_symbols": "",
+  "toolbar_insert_math_lowercase": "",
   "toolbar_insert_misc": "",
   "toolbar_insert_misc": "",
   "toolbar_insert_table": "",
   "toolbar_insert_table": "",
   "toolbar_list_indentation": "",
   "toolbar_list_indentation": "",

+ 36 - 2
services/web/frontend/js/features/source-editor/components/toolbar/math-dropdown.tsx

@@ -1,25 +1,59 @@
+import { DropdownHeader } from '@/features/ui/components/bootstrap-5/dropdown-menu'
 import { ToolbarButtonMenu } from './button-menu'
 import { ToolbarButtonMenu } from './button-menu'
 import { emitToolbarEvent } from '../../extensions/toolbar/utils/analytics'
 import { emitToolbarEvent } from '../../extensions/toolbar/utils/analytics'
 import MaterialIcon from '../../../../shared/components/material-icon'
 import MaterialIcon from '../../../../shared/components/material-icon'
 import { useTranslation } from 'react-i18next'
 import { useTranslation } from 'react-i18next'
 import { useCodeMirrorViewContext } from '../codemirror-context'
 import { useCodeMirrorViewContext } from '../codemirror-context'
+import { useEditorContext } from '@/shared/context/editor-context'
 import {
 import {
   wrapInDisplayMath,
   wrapInDisplayMath,
   wrapInInlineMath,
   wrapInInlineMath,
 } from '../../extensions/toolbar/commands'
 } from '../../extensions/toolbar/commands'
 import { memo } from 'react'
 import { memo } from 'react'
 import OLListGroupItem from '@/features/ui/components/ol/ol-list-group-item'
 import OLListGroupItem from '@/features/ui/components/ol/ol-list-group-item'
+import sparkleWhite from '@/shared/svgs/sparkle-small-white.svg'
+import sparkle from '@/shared/svgs/ai-sparkle-text.svg'
+import { isSplitTestEnabled } from '@/utils/splitTestUtils'
 
 
 export const MathDropdown = memo(function MathDropdown() {
 export const MathDropdown = memo(function MathDropdown() {
   const { t } = useTranslation()
   const { t } = useTranslation()
   const view = useCodeMirrorViewContext()
   const view = useCodeMirrorViewContext()
+  const { writefullInstance } = useEditorContext()
 
 
+  const wfRebrandEnabled = isSplitTestEnabled('wf-feature-rebrand')
   return (
   return (
     <ToolbarButtonMenu
     <ToolbarButtonMenu
       id="toolbar-math"
       id="toolbar-math"
       label={t('toolbar_insert_math')}
       label={t('toolbar_insert_math')}
       icon={<MaterialIcon type="calculate" />}
       icon={<MaterialIcon type="calculate" />}
     >
     >
+      {wfRebrandEnabled && writefullInstance && (
+        <>
+          <DropdownHeader className="ol-cm-toolbar-header mx-2">
+            {t('toolbar_insert_math_lowercase')}
+          </DropdownHeader>
+          <OLListGroupItem
+            aria-label={t('toolbar_generate_math')}
+            onClick={event => {
+              writefullInstance?.openEquationGenerator()
+            }}
+          >
+            <img
+              alt="sparkle"
+              className="ol-cm-toolbar-ai-sparkle-gradient"
+              src={sparkle}
+              aria-hidden="true"
+            />
+            <img
+              alt="sparkle"
+              className="ol-cm-toolbar-ai-sparkle-white"
+              src={sparkleWhite}
+              aria-hidden="true"
+            />
+            <span>{t('generate_from_text_or_image')}</span>
+          </OLListGroupItem>
+        </>
+      )}
       <OLListGroupItem
       <OLListGroupItem
         aria-label={t('toolbar_insert_inline_math')}
         aria-label={t('toolbar_insert_inline_math')}
         onClick={event => {
         onClick={event => {
@@ -30,7 +64,7 @@ export const MathDropdown = memo(function MathDropdown() {
         }}
         }}
       >
       >
         <MaterialIcon type="123" />
         <MaterialIcon type="123" />
-        <span>{t('toolbar_insert_inline_math')}</span>
+        <span>{t('inline')}</span>
       </OLListGroupItem>
       </OLListGroupItem>
       <OLListGroupItem
       <OLListGroupItem
         aria-label={t('toolbar_insert_display_math')}
         aria-label={t('toolbar_insert_display_math')}
@@ -42,7 +76,7 @@ export const MathDropdown = memo(function MathDropdown() {
         }}
         }}
       >
       >
         <MaterialIcon type="view_day" />
         <MaterialIcon type="view_day" />
-        <span>{t('toolbar_insert_display_math')}</span>
+        <span>{t('display')}</span>
       </OLListGroupItem>
       </OLListGroupItem>
     </ToolbarButtonMenu>
     </ToolbarButtonMenu>
   )
   )

+ 101 - 0
services/web/frontend/js/features/source-editor/components/toolbar/table-dropdown.tsx

@@ -0,0 +1,101 @@
+import { DropdownHeader } from '@/features/ui/components/bootstrap-5/dropdown-menu'
+import { ToolbarButtonMenu } from './button-menu'
+import MaterialIcon from '../../../../shared/components/material-icon'
+import { useTranslation } from 'react-i18next'
+import { useEditorContext } from '@/shared/context/editor-context'
+import { memo, useRef, useCallback } from 'react'
+import OLListGroupItem from '@/features/ui/components/ol/ol-list-group-item'
+import sparkleWhite from '@/shared/svgs/sparkle-small-white.svg'
+import sparkle from '@/shared/svgs/ai-sparkle-text.svg'
+import { TableInserterDropdown } from './table-inserter-dropdown'
+import OLOverlay from '@/features/ui/components/ol/ol-overlay'
+import OLPopover from '@/features/ui/components/ol/ol-popover'
+import useDropdown from '../../../../shared/hooks/use-dropdown'
+import * as commands from '../../extensions/toolbar/commands'
+import { useCodeMirrorViewContext } from '../codemirror-context'
+import { emitToolbarEvent } from '../../extensions/toolbar/utils/analytics'
+
+export const TableDropdown = memo(function TableDropdown() {
+  const { t } = useTranslation()
+  const { writefullInstance } = useEditorContext()
+  const { open, onToggle, ref } = useDropdown()
+  const target = useRef<any>(null)
+  const view = useCodeMirrorViewContext()
+
+  const onSizeSelected = useCallback(
+    (sizeX: number, sizeY: number) => {
+      onToggle(false)
+      commands.insertTable(view, sizeX, sizeY)
+      emitToolbarEvent(view, 'table-generator-insert-table')
+      view.focus()
+    },
+    [view, onToggle]
+  )
+
+  return (
+    <>
+      <div ref={target}>
+        <ToolbarButtonMenu
+          id="toolbar-table"
+          label={t('toolbar_insert_table')}
+          icon={<MaterialIcon type="table_chart" />}
+        >
+          <DropdownHeader className="ol-cm-toolbar-header mx-2">
+            {t('toolbar_table_insert_table_lowercase')}
+          </DropdownHeader>
+          <OLListGroupItem
+            aria-label={t('toolbar_generate_table')}
+            onClick={event => {
+              writefullInstance?.openTableGenerator()
+            }}
+          >
+            <img
+              alt="sparkle"
+              className="ol-cm-toolbar-ai-sparkle-gradient"
+              src={sparkle}
+              aria-hidden="true"
+            />
+            <img
+              alt="sparkle"
+              className="ol-cm-toolbar-ai-sparkle-white"
+              src={sparkleWhite}
+              aria-hidden="true"
+            />
+            <span>{t('generate_from_text_or_image')}</span>
+          </OLListGroupItem>
+          <div className="ol-cm-toolbar-dropdown-divider mx-2 my-0" />
+          <OLListGroupItem
+            aria-label={t('toolbar_insert_table')}
+            onMouseDown={event => {
+              event.preventDefault()
+              event.stopPropagation()
+            }}
+            onClick={() => {
+              onToggle(!open)
+            }}
+          >
+            <span>{t('select_size')}</span>
+          </OLListGroupItem>
+        </ToolbarButtonMenu>
+        <OLOverlay
+          show={open}
+          target={target.current}
+          placement="bottom"
+          container={view.dom}
+          containerPadding={0}
+          transition
+          rootClose
+          onHide={() => onToggle(false)}
+        >
+          <OLPopover
+            id="toolbar-table-menu"
+            ref={ref}
+            className="ol-cm-toolbar-button-menu-popover ol-cm-toolbar-button-menu-popover-unstyled"
+          >
+            <TableInserterDropdown onSizeSelected={onSizeSelected} />
+          </OLPopover>
+        </OLOverlay>
+      </div>
+    </>
+  )
+})

+ 128 - 0
services/web/frontend/js/features/source-editor/components/toolbar/table-inserter-dropdown-legacy.tsx

@@ -0,0 +1,128 @@
+import { FC, memo, useCallback, useRef, useState } from 'react'
+import * as commands from '../../extensions/toolbar/commands'
+import { useTranslation } from 'react-i18next'
+import useDropdown from '../../../../shared/hooks/use-dropdown'
+import { useCodeMirrorViewContext } from '../codemirror-context'
+import OLTooltip from '@/features/ui/components/ol/ol-tooltip'
+import OLOverlay from '@/features/ui/components/ol/ol-overlay'
+import OLPopover from '@/features/ui/components/ol/ol-popover'
+import MaterialIcon from '../../../../shared/components/material-icon'
+import classNames from 'classnames'
+import { emitToolbarEvent } from '../../extensions/toolbar/utils/analytics'
+
+export const LegacyTableDropdown = memo(() => {
+  const { t } = useTranslation()
+  const { open, onToggle, ref } = useDropdown()
+  const view = useCodeMirrorViewContext()
+  const target = useRef<any>(null)
+
+  const onSizeSelected = useCallback(
+    (sizeX: number, sizeY: number) => {
+      onToggle(false)
+      commands.insertTable(view, sizeX, sizeY)
+      emitToolbarEvent(view, 'table-generator-insert-table')
+      view.focus()
+    },
+    [view, onToggle]
+  )
+
+  return (
+    <>
+      <OLTooltip
+        hidden={open}
+        id="toolbar-table"
+        description={<div>{t('toolbar_insert_table')}</div>}
+        overlayProps={{ placement: 'bottom' }}
+      >
+        <button
+          type="button"
+          className="ol-cm-toolbar-button btn"
+          aria-label={t('toolbar_insert_table')}
+          onMouseDown={event => {
+            event.preventDefault()
+            event.stopPropagation()
+          }}
+          onClick={() => {
+            onToggle(!open)
+          }}
+          ref={target}
+        >
+          <MaterialIcon type="table_chart" />
+        </button>
+      </OLTooltip>
+      <OLOverlay
+        show={open}
+        target={target.current}
+        placement="bottom"
+        container={view.dom}
+        containerPadding={0}
+        transition
+        rootClose
+        onHide={() => onToggle(false)}
+      >
+        <OLPopover
+          id="toolbar-table-menu"
+          ref={ref}
+          className="ol-cm-toolbar-button-menu-popover ol-cm-toolbar-button-menu-popover-unstyled"
+        >
+          <div className="ol-cm-toolbar-table-grid-popover">
+            <SizeGrid sizeX={10} sizeY={10} onSizeSelected={onSizeSelected} />
+          </div>
+        </OLPopover>
+      </OLOverlay>
+    </>
+  )
+})
+LegacyTableDropdown.displayName = 'TableInserterDropdown'
+
+const range = (start: number, end: number) =>
+  Array.from({ length: end - start + 1 }, (v, k) => k + start)
+
+const SizeGrid: FC<{
+  sizeX: number
+  sizeY: number
+  onSizeSelected: (sizeX: number, sizeY: number) => void
+}> = ({ sizeX, sizeY, onSizeSelected }) => {
+  const [currentSize, setCurrentSize] = useState<{
+    sizeX: number
+    sizeY: number
+  }>({ sizeX: 0, sizeY: 0 })
+  const { t } = useTranslation()
+  let label = t('toolbar_table_insert_table_lowercase')
+  if (currentSize.sizeX > 0 && currentSize.sizeY > 0) {
+    label = t('toolbar_table_insert_size_table', {
+      size: `${currentSize.sizeY}×${currentSize.sizeX}`,
+    })
+  }
+  return (
+    <>
+      <div className="ol-cm-toolbar-table-size-label">{label}</div>
+      <table
+        className="ol-cm-toolbar-table-grid"
+        onMouseLeave={() => {
+          setCurrentSize({ sizeX: 0, sizeY: 0 })
+        }}
+      >
+        <tbody>
+          {range(1, sizeY).map(y => (
+            <tr key={y}>
+              {range(1, sizeX).map(x => (
+                // eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions
+                <td
+                  className={classNames('ol-cm-toolbar-table-cell', {
+                    active: currentSize.sizeX >= x && currentSize.sizeY >= y,
+                  })}
+                  key={x}
+                  onMouseEnter={() => {
+                    setCurrentSize({ sizeX: x, sizeY: y })
+                  }}
+                  onMouseUp={() => onSizeSelected(x, y)}
+                />
+              ))}
+            </tr>
+          ))}
+        </tbody>
+      </table>
+    </>
+  )
+}

+ 11 - 71
services/web/frontend/js/features/source-editor/components/toolbar/table-inserter-dropdown.tsx

@@ -1,78 +1,18 @@
-import { FC, memo, useCallback, useRef, useState } from 'react'
-import * as commands from '../../extensions/toolbar/commands'
-import { useTranslation } from 'react-i18next'
-import useDropdown from '../../../../shared/hooks/use-dropdown'
-import { useCodeMirrorViewContext } from '../codemirror-context'
-import OLTooltip from '@/features/ui/components/ol/ol-tooltip'
-import OLOverlay from '@/features/ui/components/ol/ol-overlay'
-import OLPopover from '@/features/ui/components/ol/ol-popover'
-import MaterialIcon from '../../../../shared/components/material-icon'
+import { FC, useState } from 'react'
 import classNames from 'classnames'
 import classNames from 'classnames'
-import { emitToolbarEvent } from '../../extensions/toolbar/utils/analytics'
-
-export const TableInserterDropdown = memo(() => {
-  const { t } = useTranslation()
-  const { open, onToggle, ref } = useDropdown()
-  const view = useCodeMirrorViewContext()
-  const target = useRef<any>(null)
-
-  const onSizeSelected = useCallback(
-    (sizeX: number, sizeY: number) => {
-      onToggle(false)
-      commands.insertTable(view, sizeX, sizeY)
-      emitToolbarEvent(view, 'table-generator-insert-table')
-      view.focus()
-    },
-    [view, onToggle]
-  )
+import { useTranslation } from 'react-i18next'
 
 
+export const TableInserterDropdown = ({
+  onSizeSelected,
+}: {
+  onSizeSelected: (sizeX: number, sizeY: number) => void
+}) => {
   return (
   return (
-    <>
-      <OLTooltip
-        hidden={open}
-        id="toolbar-table"
-        description={<div>{t('toolbar_insert_table')}</div>}
-        overlayProps={{ placement: 'bottom' }}
-      >
-        <button
-          type="button"
-          className="ol-cm-toolbar-button btn"
-          aria-label={t('toolbar_insert_table')}
-          onMouseDown={event => {
-            event.preventDefault()
-            event.stopPropagation()
-          }}
-          onClick={() => {
-            onToggle(!open)
-          }}
-          ref={target}
-        >
-          <MaterialIcon type="table_chart" />
-        </button>
-      </OLTooltip>
-      <OLOverlay
-        show={open}
-        target={target.current}
-        placement="bottom"
-        container={view.dom}
-        containerPadding={0}
-        transition
-        rootClose
-        onHide={() => onToggle(false)}
-      >
-        <OLPopover
-          id="toolbar-table-menu"
-          ref={ref}
-          className="ol-cm-toolbar-button-menu-popover ol-cm-toolbar-button-menu-popover-unstyled"
-        >
-          <div className="ol-cm-toolbar-table-grid-popover">
-            <SizeGrid sizeX={10} sizeY={10} onSizeSelected={onSizeSelected} />
-          </div>
-        </OLPopover>
-      </OLOverlay>
-    </>
+    <div className="ol-cm-toolbar-table-grid-popover">
+      <SizeGrid sizeX={10} sizeY={10} onSizeSelected={onSizeSelected} />
+    </div>
   )
   )
-})
+}
 TableInserterDropdown.displayName = 'TableInserterDropdown'
 TableInserterDropdown.displayName = 'TableInserterDropdown'
 
 
 const range = (start: number, end: number) =>
 const range = (start: number, end: number) =>

+ 12 - 3
services/web/frontend/js/features/source-editor/components/toolbar/toolbar-items.tsx

@@ -9,8 +9,10 @@ import getMeta from '../../../../utils/meta'
 import { InsertFigureDropdown } from './insert-figure-dropdown'
 import { InsertFigureDropdown } from './insert-figure-dropdown'
 import { useTranslation } from 'react-i18next'
 import { useTranslation } from 'react-i18next'
 import { MathDropdown } from './math-dropdown'
 import { MathDropdown } from './math-dropdown'
-import { TableInserterDropdown } from './table-inserter-dropdown'
+import { TableDropdown } from './table-dropdown'
+import { LegacyTableDropdown } from './table-inserter-dropdown-legacy'
 import { withinFormattingCommand } from '@/features/source-editor/utils/tree-operations/formatting'
 import { withinFormattingCommand } from '@/features/source-editor/utils/tree-operations/formatting'
+import { isSplitTestEnabled } from '@/utils/splitTestUtils'
 import { isMac } from '@/shared/utils/os'
 import { isMac } from '@/shared/utils/os'
 
 
 export const ToolbarItems: FC<{
 export const ToolbarItems: FC<{
@@ -27,12 +29,15 @@ export const ToolbarItems: FC<{
   listDepth,
   listDepth,
 }) {
 }) {
   const { t } = useTranslation()
   const { t } = useTranslation()
-  const { toggleSymbolPalette, showSymbolPalette } = useEditorContext()
+  const { toggleSymbolPalette, showSymbolPalette, writefullInstance } =
+    useEditorContext()
   const isActive = withinFormattingCommand(state)
   const isActive = withinFormattingCommand(state)
 
 
   const symbolPaletteAvailable = getMeta('ol-symbolPaletteAvailable')
   const symbolPaletteAvailable = getMeta('ol-symbolPaletteAvailable')
   const showGroup = (group: string) => !overflowed || overflowed.has(group)
   const showGroup = (group: string) => !overflowed || overflowed.has(group)
 
 
+  const wfRebrandEnabled = isSplitTestEnabled('wf-feature-rebrand')
+
   return (
   return (
     <>
     <>
       {showGroup('group-history') && (
       {showGroup('group-history') && (
@@ -142,7 +147,11 @@ export const ToolbarItems: FC<{
                 icon="book_5"
                 icon="book_5"
               />
               />
               <InsertFigureDropdown />
               <InsertFigureDropdown />
-              <TableInserterDropdown />
+              {wfRebrandEnabled && writefullInstance ? (
+                <TableDropdown />
+              ) : (
+                <LegacyTableDropdown />
+              )}
             </div>
             </div>
           )}
           )}
           {showGroup('group-list') && (
           {showGroup('group-list') && (

+ 20 - 0
services/web/frontend/js/features/source-editor/extensions/toolbar/toolbar-panel.ts

@@ -59,6 +59,26 @@ const toolbarTheme = EditorView.theme({
       },
       },
     },
     },
   },
   },
+  '.ol-cm-toolbar-header': {
+    color: 'var(--toolbar-btn-color)',
+  },
+  '.ol-cm-toolbar-dropdown-divider': {
+    borderBottom: '1px solid',
+    borderColor: 'var(--toolbar-dropdown-divider-color)',
+  },
+  // here render both the icons, and hide one depending on if its dark or light mode with &.overall-theme-dark
+  '.ol-cm-toolbar-ai-sparkle-gradient': {
+    display: 'block',
+  },
+  '.ol-cm-toolbar-ai-sparkle-white': {
+    display: 'none',
+  },
+  '&.overall-theme-dark .ol-cm-toolbar-ai-sparkle-gradient': {
+    display: 'none',
+  },
+  '&.overall-theme-dark .ol-cm-toolbar-ai-sparkle-white': {
+    display: 'block',
+  },
   '.ol-cm-toolbar-button-menu-popover': {
   '.ol-cm-toolbar-button-menu-popover': {
     backgroundColor: 'initial',
     backgroundColor: 'initial',
     '& > .popover-content, & > .popover-body': {
     '& > .popover-content, & > .popover-body': {

+ 2 - 0
services/web/frontend/js/shared/context/types/writefull-instance.ts

@@ -28,4 +28,6 @@ export interface WritefullAPI {
     name: eventName,
     name: eventName,
     callback: (detail: WritefullEvents[eventName]) => void
     callback: (detail: WritefullEvents[eventName]) => void
   ): void
   ): void
+  openTableGenerator(): void
+  openEquationGenerator(): void
 }
 }

+ 1 - 0
services/web/frontend/js/shared/svgs/sparkle-small-white.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="20" height="21" fill="none"><g fill="#fff" clip-path="url(#a)"><path d="M14.87 12.18c-4.74-1.07-5.48-1.8-6.55-6.54a.48.48 0 0 0-.93 0c-1.07 4.74-1.8 5.47-6.54 6.54a.48.48 0 0 0 0 .93c4.74 1.08 5.47 1.8 6.54 6.55a.48.48 0 0 0 .93 0c1.07-4.74 1.8-5.47 6.55-6.55a.48.48 0 0 0 0-.93Zm4.28-7.38c-2.52-.56-2.87-.92-3.44-3.44a.48.48 0 0 0-.93 0c-.57 2.52-.92 2.88-3.44 3.44a.48.48 0 0 0 0 .93c2.52.57 2.87.93 3.44 3.45a.48.48 0 0 0 .93 0c.57-2.52.92-2.88 3.44-3.45a.48.48 0 0 0 0-.93Z"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 .5h20v20H0z"/></clipPath></defs></svg>

+ 1 - 0
services/web/frontend/stylesheets/bootstrap-5/foundations/colors.scss

@@ -199,6 +199,7 @@ $link-ui-visited-dark: $blue-40;
   --border-active: var(--blue-50);
   --border-active: var(--blue-50);
   --border-danger: var(--red-50);
   --border-danger: var(--red-50);
   --border-divider: var(--neutral-20);
   --border-divider: var(--neutral-20);
+  --border-dark-divider: var(--neutral-70);
   --link-web: var(--green-50);
   --link-web: var(--green-50);
   --link-web-hover: var(--green-60);
   --link-web-hover: var(--green-60);
   --link-web-visited: var(--green-50);
   --link-web-visited: var(--green-50);

+ 2 - 0
services/web/frontend/stylesheets/bootstrap-5/pages/editor/toolbar.scss

@@ -22,6 +22,7 @@
   --editor-header-logo-background: url(../../../../../public/img/ol-brand/overleaf-o-white.svg)
   --editor-header-logo-background: url(../../../../../public/img/ol-brand/overleaf-o-white.svg)
     center / contain no-repeat;
     center / contain no-repeat;
   --editor-toolbar-bg: var(--neutral-80);
   --editor-toolbar-bg: var(--neutral-80);
+  --toolbar-dropdown-divider-color: var(--border-dark-divider);
 }
 }
 
 
 .ide-redesign-main {
 .ide-redesign-main {
@@ -70,6 +71,7 @@
   --editor-header-logo-background: url(../../../../../public/img/ol-brand/overleaf-o.svg)
   --editor-header-logo-background: url(../../../../../public/img/ol-brand/overleaf-o.svg)
     center / contain no-repeat;
     center / contain no-repeat;
   --editor-toolbar-bg: var(--white);
   --editor-toolbar-bg: var(--white);
+  --toolbar-dropdown-divider-color: var(--border-divider);
 
 
   .ide-redesign-main {
   .ide-redesign-main {
     --toolbar-alt-bg-color: var(--bg-light-secondary);
     --toolbar-alt-bg-color: var(--bg-light-secondary);

+ 7 - 0
services/web/locales/en.json

@@ -529,6 +529,7 @@
   "discover_the_fastest_way_to_search_and_cite": "Discover the fastest way to search and cite",
   "discover_the_fastest_way_to_search_and_cite": "Discover the fastest way to search and cite",
   "discover_why_over_people_worldwide_trust_overleaf": "Discover why over __count__ million people worldwide trust Overleaf with their work.",
   "discover_why_over_people_worldwide_trust_overleaf": "Discover why over __count__ million people worldwide trust Overleaf with their work.",
   "dismiss_error_popup": "Dismiss first error alert",
   "dismiss_error_popup": "Dismiss first error alert",
+  "display": "Display",
   "display_deleted_user": "Display deleted users",
   "display_deleted_user": "Display deleted users",
   "display_math": "Display math",
   "display_math": "Display math",
   "do_not_have_acct_or_do_not_want_to_link": "If you don’t have an <b>__appName__</b> account, or if you don’t want to link to your <b>__institutionName__</b> account, please click <b>__clickText__</b>.",
   "do_not_have_acct_or_do_not_want_to_link": "If you don’t have an <b>__appName__</b> account, or if you don’t want to link to your <b>__institutionName__</b> account, please click <b>__clickText__</b>.",
@@ -807,6 +808,7 @@
   "gallery_page_title": "Gallery - Templates, Examples and Articles written in LaTeX",
   "gallery_page_title": "Gallery - Templates, Examples and Articles written in LaTeX",
   "gallery_show_more_tags": "Show more",
   "gallery_show_more_tags": "Show more",
   "general": "General",
   "general": "General",
+  "generate_from_text_or_image": "From text or image",
   "generate_token": "Generate token",
   "generate_token": "Generate token",
   "generic_if_problem_continues_contact_us": "If the problem continues please contact us",
   "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.",
   "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.",
@@ -1019,6 +1021,7 @@
   "increased_compile_timeout": "Increased compile timeout",
   "increased_compile_timeout": "Increased compile timeout",
   "individuals": "Individuals",
   "individuals": "Individuals",
   "info": "Info",
   "info": "Info",
+  "inline": "Inline",
   "inline_math": "Inline math",
   "inline_math": "Inline math",
   "inr_discount_modal_info": "Get document history, track changes, additional collaborators, and more at Purchasing Power Parity prices.",
   "inr_discount_modal_info": "Get document history, track changes, additional collaborators, and more at Purchasing Power Parity prices.",
   "inr_discount_modal_title": "70% off all Overleaf premium plans for users in India",
   "inr_discount_modal_title": "70% off all Overleaf premium plans for users in India",
@@ -1948,6 +1951,7 @@
   "select_image_from_project_files": "Select image from project files",
   "select_image_from_project_files": "Select image from project files",
   "select_project": "Select __project__",
   "select_project": "Select __project__",
   "select_projects": "Select Projects",
   "select_projects": "Select Projects",
+  "select_size": "Select size",
   "select_tag": "Select tag __tagName__",
   "select_tag": "Select tag __tagName__",
   "select_user": "Select user",
   "select_user": "Select user",
   "selected": "Selected",
   "selected": "Selected",
@@ -2316,6 +2320,8 @@
   "toolbar_editor": "Editor tools",
   "toolbar_editor": "Editor tools",
   "toolbar_format_bold": "Format Bold",
   "toolbar_format_bold": "Format Bold",
   "toolbar_format_italic": "Format Italic",
   "toolbar_format_italic": "Format Italic",
+  "toolbar_generate_math": "Generate Math",
+  "toolbar_generate_table": "Generate Table",
   "toolbar_increase_indent": "Increase Indent",
   "toolbar_increase_indent": "Increase Indent",
   "toolbar_insert_citation": "Insert Citation",
   "toolbar_insert_citation": "Insert Citation",
   "toolbar_insert_cross_reference": "Insert Cross-reference",
   "toolbar_insert_cross_reference": "Insert Cross-reference",
@@ -2325,6 +2331,7 @@
   "toolbar_insert_link": "Insert Link",
   "toolbar_insert_link": "Insert Link",
   "toolbar_insert_math": "Insert Math",
   "toolbar_insert_math": "Insert Math",
   "toolbar_insert_math_and_symbols": "Insert Math and Symbols",
   "toolbar_insert_math_and_symbols": "Insert Math and Symbols",
+  "toolbar_insert_math_lowercase": "Insert math",
   "toolbar_insert_misc": "Insert Misc (links, citations, cross-references, figures, tables)",
   "toolbar_insert_misc": "Insert Misc (links, citations, cross-references, figures, tables)",
   "toolbar_insert_table": "Insert Table",
   "toolbar_insert_table": "Insert Table",
   "toolbar_list_indentation": "List and Indentation",
   "toolbar_list_indentation": "List and Indentation",