Sfoglia il codice sorgente

[web] Recreate workbench with Overleaf styles (#29651)

GitOrigin-RevId: 52ca336f70b29edf6e39cf95aa164f3ae32c0a79
Mathias Jakobsen 10 mesi fa
parent
commit
7b331b0222

File diff suppressed because it is too large
+ 252 - 1782
package-lock.json


+ 0 - 1
services/web/.jenkinsIncludeFile

@@ -2,7 +2,6 @@
 .eslint*
 .prettier*
 libraries/access-token-encryptor/**
-libraries/ai/**
 libraries/eslint-plugin/**
 libraries/fetch-utils/**
 libraries/logger/**

+ 0 - 3
services/web/Dockerfile

@@ -24,7 +24,6 @@ FROM base AS deps-prod
 
 COPY package.json package-lock.json /overleaf/
 COPY libraries/access-token-encryptor/package.json /overleaf/libraries/access-token-encryptor/package.json
-COPY libraries/ai/package.json /overleaf/libraries/ai/package.json
 COPY libraries/eslint-plugin/package.json /overleaf/libraries/eslint-plugin/package.json
 COPY libraries/fetch-utils/package.json /overleaf/libraries/fetch-utils/package.json
 COPY libraries/logger/package.json /overleaf/libraries/logger/package.json
@@ -59,7 +58,6 @@ FROM deps AS dev
 ARG SENTRY_RELEASE
 ENV SENTRY_RELEASE=$SENTRY_RELEASE
 COPY libraries/access-token-encryptor/ /overleaf/libraries/access-token-encryptor/
-COPY libraries/ai/ /overleaf/libraries/ai/
 COPY libraries/eslint-plugin/ /overleaf/libraries/eslint-plugin/
 COPY libraries/fetch-utils/ /overleaf/libraries/fetch-utils/
 COPY libraries/logger/ /overleaf/libraries/logger/
@@ -97,7 +95,6 @@ RUN nice find /overleaf/services/web/public -name '*.js.map' -delete
 # copy source code and precompile pug images
 FROM deps-prod AS pug
 COPY libraries/access-token-encryptor/ /overleaf/libraries/access-token-encryptor/
-COPY libraries/ai/ /overleaf/libraries/ai/
 COPY libraries/eslint-plugin/ /overleaf/libraries/eslint-plugin/
 COPY libraries/fetch-utils/ /overleaf/libraries/fetch-utils/
 COPY libraries/logger/ /overleaf/libraries/logger/

+ 0 - 1
services/web/Makefile

@@ -570,7 +570,6 @@ IMAGE_SCRATCH ?= $(IMAGE_REPO):do-not-use-this-tag-for-deploys--it-is-used-for-e
 IMAGE_CACHE ?= $(IMAGE_REPO):cache-$(shell cat \
 	$(MONOREPO)/package.json \
 	$(MONOREPO)/package-lock.json \
-	$(MONOREPO)/libraries/ai/package.json \
 	$(MONOREPO)/libraries/access-token-encryptor/package.json \
   $(MONOREPO)/libraries/eslint-plugin/package.json \
   $(MONOREPO)/libraries/fetch-utils/package.json \

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


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

@@ -37,6 +37,7 @@ export default /** @type {const} */ ([
   'integration_instructions',
   'lightbulb',
   'more_vert',
+  'neurology',
   'note_add',
   'open_in_new',
   'password',

+ 1 - 1
services/web/frontend/js/features/ide-react/context/editor-manager-context.tsx

@@ -55,7 +55,7 @@ export type EditorManager = {
   getCurrentDocumentId: () => DocId | null
   setIgnoringExternalUpdates: (value: boolean) => void
   openDocWithId: (docId: string, options?: OpenDocOptions) => void
-  openDoc: (document: Doc, options?: OpenDocOptions) => void
+  openDoc: (document: Doc, options?: OpenDocOptions) => Promise<Doc | undefined>
   openDocs: OpenDocuments
   openFileWithId: (fileId: string) => void
   openInitialDoc: (docId?: string) => void

+ 7 - 3
services/web/frontend/js/features/ide-react/context/react-context-root.tsx

@@ -30,6 +30,7 @@ import { UserFeaturesProvider } from '@/shared/context/user-features-context'
 import { UserSettingsProvider } from '@/shared/context/user-settings-context'
 import { IdeRedesignSwitcherProvider } from './ide-redesign-switcher-context'
 import { CommandRegistryProvider } from './command-registry-context'
+import { EditorSelectionProvider } from '@/shared/context/editor-selection-context'
 import importOverleafModules from '../../../../macros/import-overleaf-module.macro'
 
 const rootContextProviders = importOverleafModules('rootContextProviders') as {
@@ -74,6 +75,7 @@ export const ReactContextRoot: FC<
     IdeRedesignSwitcherProvider,
     CommandRegistryProvider,
     UserFeaturesProvider,
+    EditorSelectionProvider,
     ...providers,
   }
 
@@ -121,9 +123,11 @@ export const ReactContextRoot: FC<
                                                             <Providers.OutlineProvider>
                                                               <Providers.IdeRedesignSwitcherProvider>
                                                                 <Providers.CommandRegistryProvider>
-                                                                  {
-                                                                    childrenWrappedWithDynamicProviders
-                                                                  }
+                                                                  <Providers.EditorSelectionProvider>
+                                                                    {
+                                                                      childrenWrappedWithDynamicProviders
+                                                                    }
+                                                                  </Providers.EditorSelectionProvider>
                                                                 </Providers.CommandRegistryProvider>
                                                               </Providers.IdeRedesignSwitcherProvider>
                                                             </Providers.OutlineProvider>

+ 2 - 0
services/web/frontend/js/features/source-editor/extensions/index.ts

@@ -54,6 +54,7 @@ import { historyOT } from './history-ot'
 import { trackDetachedComments } from './track-detached-comments'
 import { reviewTooltip } from './review-tooltip'
 import { tooltipsReposition } from './tooltips-reposition'
+import { selectionListener } from '@/features/source-editor/extensions/selection-listener'
 
 const moduleExtensions: Array<(options: Record<string, any>) => Extension> =
   importOverleafModules('sourceEditorExtensions').map(
@@ -172,4 +173,5 @@ export const createExtensions = (options: Record<string, any>): Extension[] => [
   geometryChangeEvent(),
   fileTreeItemDrop(),
   tooltipsReposition(),
+  selectionListener(options.setEditorSelection),
 ]

+ 22 - 0
services/web/frontend/js/features/source-editor/extensions/selection-listener.ts

@@ -0,0 +1,22 @@
+import { ViewPlugin } from '@codemirror/view'
+import { EditorSelection } from '@codemirror/state'
+import { debounce } from 'lodash'
+
+export const selectionListener = (
+  setEditorSelection: (value: EditorSelection | undefined) => void
+) => {
+  const debouncedSetEditorSelection = debounce(setEditorSelection, 250)
+
+  return ViewPlugin.define(() => {
+    return {
+      update(update) {
+        if (update.selectionSet) {
+          debouncedSetEditorSelection(update.state.selection)
+        }
+      },
+      destroy() {
+        setEditorSelection(undefined)
+      },
+    }
+  })
+}

+ 5 - 1
services/web/frontend/js/features/source-editor/hooks/use-codemirror-scope.ts

@@ -60,6 +60,7 @@ import { useEditorPropertiesContext } from '@/features/ide-react/context/editor-
 import { SearchQuery } from '@codemirror/search'
 import { beforeChangeDocEffect } from '@/features/source-editor/extensions/before-change-doc'
 import { useActiveOverallTheme } from '@/shared/hooks/use-active-overall-theme'
+import { useEditorSelectionContext } from '@/shared/context/editor-selection-context'
 
 function useCodeMirrorScope(view: EditorView) {
   const { fileTreeData } = useFileTreeData()
@@ -106,6 +107,8 @@ function useCodeMirrorScope(view: EditorView) {
 
   const { referenceKeys, searchLocalReferences } = useReferencesContext()
 
+  const { setEditorSelection } = useEditorSelectionContext()
+
   const ranges = useRangesContext()
   const threads = useThreadsContext()
 
@@ -335,6 +338,7 @@ function useCodeMirrorScope(view: EditorView) {
           initialSearchQuery: searchQueryRef.current,
           showBoundary,
           handleException,
+          setEditorSelection,
         }),
       })
       view.setState(state)
@@ -364,7 +368,7 @@ function useCodeMirrorScope(view: EditorView) {
     }
     // IMPORTANT: This effect must not depend on anything variable apart from currentDocument,
     // as the editor state is recreated when the effect runs.
-  }, [view, currentDocument, showBoundary, handleException])
+  }, [view, currentDocument, showBoundary, handleException, setEditorSelection])
 
   useEffect(() => {
     if (openDocName) {

+ 1 - 1
services/web/frontend/js/shared/components/types/icon-button-props.ts

@@ -3,7 +3,7 @@ import { ButtonProps } from './button-props'
 
 type BaseIconButtonProps = ButtonProps & {
   accessibilityLabel?: string
-  type?: 'button' | 'submit'
+  type?: 'button' | 'submit' | 'reset'
 }
 
 type FilledIconButtonProps = BaseIconButtonProps & {

+ 47 - 0
services/web/frontend/js/shared/context/editor-selection-context.tsx

@@ -0,0 +1,47 @@
+import {
+  createContext,
+  type Dispatch,
+  type FC,
+  type PropsWithChildren,
+  type SetStateAction,
+  useContext,
+  useMemo,
+  useState,
+} from 'react'
+import type { EditorSelection } from '@codemirror/state'
+
+export const EditorSelectionContext = createContext<
+  | {
+      editorSelection: EditorSelection | undefined
+      setEditorSelection: Dispatch<SetStateAction<EditorSelection | undefined>>
+    }
+  | undefined
+>(undefined)
+
+export const EditorSelectionProvider: FC<PropsWithChildren> = ({
+  children,
+}) => {
+  const [editorSelection, setEditorSelection] = useState<EditorSelection>()
+
+  const value = useMemo(() => {
+    return { editorSelection, setEditorSelection }
+  }, [editorSelection])
+
+  return (
+    <EditorSelectionContext.Provider value={value}>
+      {children}
+    </EditorSelectionContext.Provider>
+  )
+}
+
+export const useEditorSelectionContext = () => {
+  const context = useContext(EditorSelectionContext)
+
+  if (!context) {
+    throw new Error(
+      'useEditorSelectionContext is only available inside EditorSelectionProvider'
+    )
+  }
+
+  return context
+}

+ 12 - 4
services/web/package.json

@@ -81,8 +81,8 @@
     "safari > 14"
   ],
   "dependencies": {
-    "@ai-sdk/mcp": "^1.0.0-beta.13",
-    "@ai-sdk/openai": "^3.0.0-beta.44",
+    "@ai-sdk/mcp": "^1.0.0-beta.15",
+    "@ai-sdk/openai": "^3.0.0-beta.59",
     "@aws-sdk/client-ses": "^3.864.0",
     "@contentful/rich-text-html-renderer": "^16.0.2",
     "@contentful/rich-text-types": "^16.0.2",
@@ -109,7 +109,7 @@
     "@stripe/stripe-js": "^7.7.0",
     "@xmldom/xmldom": "^0.7.13",
     "accepts": "^1.3.7",
-    "ai": "^6.0.0-beta.84",
+    "ai": "^6.0.0-beta.99",
     "ajv": "^8.12.0",
     "archiver": "^5.3.0",
     "async": "^3.2.5",
@@ -199,6 +199,7 @@
   },
   "devDependencies": {
     "5to6-codemod": "^1.8.0",
+    "@ai-sdk/react": "^3.0.0-beta.99",
     "@babel/cli": "^7.27.0",
     "@babel/core": "^7.26.10",
     "@babel/plugin-proposal-decorators": "^7.27.0",
@@ -221,7 +222,6 @@
     "@lezer/highlight": "^1.2.1",
     "@lezer/lr": "^1.4.2",
     "@lezer/markdown": "^1.4.3",
-    "@overleaf/ai": "^1.0.0",
     "@overleaf/codemirror-tree-view": "^0.1.3",
     "@overleaf/dictionaries": "https://github.com/overleaf/dictionaries/archive/refs/tags/v0.0.3.tar.gz",
     "@overleaf/eslint-plugin": "*",
@@ -258,6 +258,7 @@
     "@types/chai": "^4.3.0",
     "@types/dateformat": "^5.0.3",
     "@types/diff": "^5.2.3",
+    "@types/dom-speech-recognition": "^0.0.7",
     "@types/events": "^3.0.3",
     "@types/express": "^4.17.23",
     "@types/mocha": "^9.1.0",
@@ -345,6 +346,7 @@
     "jscodeshift": "^17.0.0",
     "jsdom": "^19.0.0",
     "jsdom-global": "^3.0.2",
+    "katex": "^0.16.25",
     "knip": "^5.64.1",
     "match-sorter": "^6.2.0",
     "mathjax": "^3.2.2",
@@ -378,10 +380,15 @@
     "react-google-recaptcha": "^3.1.0",
     "react-i18next": "^13.3.1",
     "react-linkify": "^1.0.0-alpha",
+    "react-markdown": "^10.1.0",
     "react-refresh": "^0.14.0",
     "react-resizable-panels": "^2.1.1",
     "react-syntax-highlighter": "^15.6.6",
     "reflect-metadata": "^0.2.2",
+    "rehype-external-links": "^3.0.0",
+    "rehype-katex": "^7.0.1",
+    "rehype-sanitize": "^6.0.0",
+    "remark-math": "^6.0.0",
     "resolve-url-loader": "^5.0.0",
     "samlp": "^7.0.2",
     "sandboxed-module": "overleaf/node-sandboxed-module#cafa2d60f17ce75cc023e6f296eb8de79d92d35d",
@@ -401,6 +408,7 @@
     "ts-loader": "^9.5.2",
     "tty-browserify": "^0.0.1",
     "typescript": "^5.8.3",
+    "use-stick-to-bottom": "^1.1.1",
     "uuid": "^9.0.1",
     "vitest": "^3.1.2",
     "w3c-keyname": "^2.2.8",

+ 2 - 1
services/web/tsconfig.json

@@ -29,7 +29,8 @@
       "@testing-library/cypress",
       "reflect-metadata",
       "vitest/globals",
-      "@testing-library/jest-dom"
+      "@testing-library/jest-dom",
+      "@types/dom-speech-recognition"
     ]
   },
   "include": [

+ 0 - 21
services/web/webpack.config.js

@@ -10,7 +10,6 @@ const {
 
 const PackageVersions = require('./app/src/infrastructure/PackageVersions.js')
 const invalidateBabelCacheIfNeeded = require('./frontend/macros/invalidate-babel-cache-if-needed')
-const { dirname } = require('node:path')
 
 // Make sure that babel-macros are re-evaluated after changing the modules config
 invalidateBabelCacheIfNeeded()
@@ -259,26 +258,6 @@ module.exports = {
               },
             ],
           },
-          {
-            // CSS from AI module
-            include: dirname(require.resolve('@overleaf/ai')),
-            use: [
-              {
-                loader: 'css-loader',
-                options: {
-                  exportType: 'css-style-sheet',
-                },
-              },
-              {
-                loader: 'postcss-loader',
-                options: {
-                  postcssOptions: {
-                    config: require.resolve('@overleaf/ai/postcss.config.js'),
-                  },
-                },
-              },
-            ],
-          },
           {
             // Standard CSS processing (extracted into separate file)
             use: [MiniCssExtractPlugin.loader, 'css-loader'],

Some files were not shown because too many files changed in this diff