diff --git a/.dockerignore b/.dockerignore index 1f38a978cf..6472839a78 100644 --- a/.dockerignore +++ b/.dockerignore @@ -4,8 +4,16 @@ !.eslintrc.json !.npmrc !.prettierrc +!excalidraw-app/ !package.json !public/ !packages/ +!scripts/ !tsconfig.json !yarn.lock + +# keep (sub)sub directories at the end to exclude from explicit included +# e.g. ./packages/excalidraw/{dist,node_modules} +**/build +**/dist +**/node_modules diff --git a/.env.development b/.env.development index 95e21ff87c..387ab6204d 100644 --- a/.env.development +++ b/.env.development @@ -8,7 +8,7 @@ VITE_APP_LIBRARY_BACKEND=https://us-central1-excalidraw-room-persistence.cloudfu VITE_APP_WS_SERVER_URL=http://localhost:3002 VITE_APP_PLUS_LP=https://plus.excalidraw.com -VITE_APP_PLUS_APP=https://app.excalidraw.com +VITE_APP_PLUS_APP=http://localhost:3000 VITE_APP_AI_BACKEND=http://localhost:3015 @@ -17,12 +17,10 @@ VITE_APP_FIREBASE_CONFIG='{"apiKey":"AIzaSyCMkxA60XIW8KbqMYL7edC4qT5l4qHX2h8","a # put these in your .env.local, or make sure you don't commit! # must be lowercase `true` when turned on # -# whether to enable Service Workers in development -VITE_APP_DEV_ENABLE_SW= # whether to disable live reload / HMR. Usuaully what you want to do when # debugging Service Workers. VITE_APP_DEV_DISABLE_LIVE_RELOAD= -VITE_APP_DISABLE_TRACKING=true +VITE_APP_ENABLE_TRACKING=true FAST_REFRESH=false @@ -39,3 +37,17 @@ VITE_APP_COLLAPSE_OVERLAY=true # Set this flag to false to disable eslint VITE_APP_ENABLE_ESLINT=true + +# Enable PWA in dev server +VITE_APP_ENABLE_PWA=false + +VITE_APP_PLUS_EXPORT_PUBLIC_KEY='MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAm2g5T+Rub6Kbf1Mf57t0 +7r2zeHuVg4dla3r5ryXMswtzz6x767octl6oLThn33mQsPSy3GKglFZoCTXJR4ij +ba8SxB04sL/N8eRrKja7TFWjCVtRwTTfyy771NYYNFVJclkxHyE5qw4m27crHF1y +UNWEjuqNMi/lwAErS9fFa2oJlWyT8U7zzv/5kQREkxZI6y9v0AF3qcbsy2731FnD +s9ChJvOUW9toIab2gsIdrKW8ZNpu084ZFVKb6LNjvIXI1Se4oMTHeszXzNptzlot +kdxxjOoaQMAyfljFSot1F1FlU6MQlag7UnFGvFjRHN1JI5q4K+n3a67DX+TMyRqS +HQIDAQAB' + +# set to true in .env.development.local to disable the prevent unload dialog +VITE_APP_DISABLE_PREVENT_UNLOAD= diff --git a/.env.production b/.env.production index 0c715854a8..11e9fd84bb 100644 --- a/.env.production +++ b/.env.production @@ -14,4 +14,19 @@ VITE_APP_WS_SERVER_URL=https://oss-collab.excalidraw.com VITE_APP_FIREBASE_CONFIG='{"apiKey":"AIzaSyAd15pYlMci_xIp9ko6wkEsDzAAA0Dn0RU","authDomain":"excalidraw-room-persistence.firebaseapp.com","databaseURL":"https://excalidraw-room-persistence.firebaseio.com","projectId":"excalidraw-room-persistence","storageBucket":"excalidraw-room-persistence.appspot.com","messagingSenderId":"654800341332","appId":"1:654800341332:web:4a692de832b55bd57ce0c1"}' -VITE_APP_DISABLE_TRACKING= +VITE_APP_ENABLE_TRACKING=false + +VITE_APP_PLUS_EXPORT_PUBLIC_KEY='MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApQ0jM9Qz8TdFLzcuAZZX +/WvuKSOJxiw6AR/ZcE3eFQWM/mbFdhQgyK8eHGkKQifKzH1xUZjCxyXcxW6ZO02t +kPOPxhz+nxUrIoWCD/V4NGmUA1lxwHuO21HN1gzKrN3xHg5EGjyouR9vibT9VDGF +gq6+4Ic/kJX+AD2MM7Yre2+FsOdysrmuW2Fu3ahuC1uQE7pOe1j0k7auNP0y1q53 +PrB8Ts2LUpepWC1l7zIXFm4ViDULuyWXTEpUcHSsEH8vpd1tckjypxCwkipfZsXx +iPszy0o0Dx2iArPfWMXlFAI9mvyFCyFC3+nSvfyAUb2C4uZgCwAuyFh/ydPF4DEE +PQIDAQAB' + +# Set the below flags explicitly to false in production mode since vite loads and merges .env.local vars when running the build command +VITE_APP_DEBUG_ENABLE_TEXT_CONTAINER_BOUNDING_BOX=false +VITE_APP_COLLAPSE_OVERLAY=false +# Enable eslint in dev server +VITE_APP_ENABLE_ESLINT=false + diff --git a/.eslintignore b/.eslintignore index ab3aa6c763..8b4f458dee 100644 --- a/.eslintignore +++ b/.eslintignore @@ -6,3 +6,6 @@ firebase/ dist/ public/workbox packages/excalidraw/types +examples/**/public +dev-dist +coverage diff --git a/.eslintrc.json b/.eslintrc.json index fbb12f59d4..8263b08a9a 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,7 +1,37 @@ { "extends": ["@excalidraw/eslint-config", "react-app"], "rules": { + "import/order": [ + "warn", + { + "groups": ["builtin", "external", "internal", "parent", "sibling", "index", "object", "type"], + "pathGroups": [ + { + "pattern": "@excalidraw/**", + "group": "external", + "position": "after" + } + ], + "newlines-between": "always-and-inside-groups", + "warnOnUnassignedImports": true + } + ], "import/no-anonymous-default-export": "off", - "no-restricted-globals": "off" + "no-restricted-globals": "off", + "@typescript-eslint/consistent-type-imports": [ + "error", + { + "prefer": "type-imports", + "disallowTypeAnnotations": false, + "fixStyle": "separate-type-imports" + } + ], + "no-restricted-imports": [ + "error", + { + "name": "jotai", + "message": "Do not import from \"jotai\" directly. Use our app-specific modules (\"editor-jotai\" or \"app-jotai\")." + } + ] } } diff --git a/.github/workflows/locales-coverage.yml b/.github/workflows/locales-coverage.yml index 3cce93e041..957e9bc37c 100644 --- a/.github/workflows/locales-coverage.yml +++ b/.github/workflows/locales-coverage.yml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: token: ${{ secrets.PUSH_TRANSLATIONS_COVERAGE_PAT }} diff --git a/.github/workflows/semantic-pr-title.yml b/.github/workflows/semantic-pr-title.yml index 969d236407..34a6413fe2 100644 --- a/.github/workflows/semantic-pr-title.yml +++ b/.github/workflows/semantic-pr-title.yml @@ -11,6 +11,6 @@ jobs: semantic: runs-on: ubuntu-latest steps: - - uses: amannn/action-semantic-pull-request@v3.0.0 + - uses: amannn/action-semantic-pull-request@v5 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2c458a8108..7d454ecfc4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,14 +1,16 @@ name: Tests -on: pull_request +on: + push: + branches: master jobs: test: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Setup Node.js 18.x - uses: actions/setup-node@v2 + uses: actions/setup-node@v4 with: node-version: 18.x - name: Install and test diff --git a/.gitignore b/.gitignore index 21d2730a2c..6f9407fad0 100644 --- a/.gitignore +++ b/.gitignore @@ -25,4 +25,4 @@ packages/excalidraw/types coverage dev-dist html -examples/**/bundle.* \ No newline at end of file +meta*.json \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index a044f40f64..2716803fa6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,16 +2,18 @@ FROM node:18 AS build WORKDIR /opt/node_app -COPY package.json yarn.lock ./ -RUN yarn --ignore-optional --network-timeout 600000 +COPY . . + +# do not ignore optional dependencies: +# Error: Cannot find module @rollup/rollup-linux-x64-gnu +RUN yarn --network-timeout 600000 ARG NODE_ENV=production -COPY . . RUN yarn build:app:docker -FROM nginx:1.21-alpine +FROM nginx:1.27-alpine -COPY --from=build /opt/node_app/build /usr/share/nginx/html +COPY --from=build /opt/node_app/excalidraw-app/build /usr/share/nginx/html HEALTHCHECK CMD wget -q -O /dev/null http://localhost || exit 1 diff --git a/README.md b/README.md index e8cd3b06fa..9458779ce9 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@

Excalidraw Editor | - Blog | + Blog | Documentation | Excalidraw+

@@ -87,13 +87,11 @@ We'll be adding these features as drop-in plugins for the npm package in the fut **Note:** following instructions are for installing the Excalidraw [npm package](https://www.npmjs.com/package/@excalidraw/excalidraw) when integrating Excalidraw into your own app. To run the repository locally for development, please refer to our [Development Guide](https://docs.excalidraw.com/docs/introduction/development). -``` +Use `npm` or `yarn` to install the package. + +```bash npm install react react-dom @excalidraw/excalidraw -``` - -or via yarn - -``` +# or yarn add react react-dom @excalidraw/excalidraw ``` diff --git a/dev-docs/docs/@excalidraw/excalidraw/api/children-components/main-menu.mdx b/dev-docs/docs/@excalidraw/excalidraw/api/children-components/main-menu.mdx index e601a24715..b0062d9621 100644 --- a/dev-docs/docs/@excalidraw/excalidraw/api/children-components/main-menu.mdx +++ b/dev-docs/docs/@excalidraw/excalidraw/api/children-components/main-menu.mdx @@ -133,7 +133,7 @@ function App() { } ``` -Here is a [complete list](https://github.com/excalidraw/excalidraw/blob/master/packages/excalidraw/components/mainMenu/DefaultItems.tsx) of the default items. +Here is a [complete list](https://github.com/excalidraw/excalidraw/blob/master/packages/excalidraw/components/main-menu/DefaultItems.tsx) of the default items. ### MainMenu.Group diff --git a/dev-docs/docs/@excalidraw/excalidraw/api/children-components/welcome-screen.mdx b/dev-docs/docs/@excalidraw/excalidraw/api/children-components/welcome-screen.mdx index 8150508bf1..88348706a6 100644 --- a/dev-docs/docs/@excalidraw/excalidraw/api/children-components/welcome-screen.mdx +++ b/dev-docs/docs/@excalidraw/excalidraw/api/children-components/welcome-screen.mdx @@ -31,7 +31,7 @@ The welcome screen consists of two main groups of subcomponents: Excalidraw logo: Sketch handrawn like diagrams. ### Center diff --git a/dev-docs/docs/@excalidraw/excalidraw/api/constants.mdx b/dev-docs/docs/@excalidraw/excalidraw/api/constants.mdx index 029f5b05ca..36fbfa7582 100644 --- a/dev-docs/docs/@excalidraw/excalidraw/api/constants.mdx +++ b/dev-docs/docs/@excalidraw/excalidraw/api/constants.mdx @@ -8,15 +8,15 @@ import { FONT_FAMILY } from "@excalidraw/excalidraw"; ``` -`FONT_FAMILY` contains all the font families used in `Excalidraw` as explained below +`FONT_FAMILY` contains all the font families used in `Excalidraw`. The default families are the following: | Font Family | Description | | ----------- | ---------------------- | -| `Virgil` | The `handwritten` font | -| `Helvetica` | The `Normal` Font | -| `Cascadia` | The `Code` Font | +| `Excalifont` | The `Hand-drawn` font | +| `Nunito` | The `Normal` Font | +| `Comic Shanns` | The `Code` Font | -Defaults to `FONT_FAMILY.Virgil` unless passed in `initialData.appState.currentItemFontFamily`. +Pre-selected family is `FONT_FAMILY.Excalifont`, unless it's overriden with `initialData.appState.currentItemFontFamily`. ### THEME diff --git a/dev-docs/docs/@excalidraw/excalidraw/api/props/excalidraw-api.mdx b/dev-docs/docs/@excalidraw/excalidraw/api/props/excalidraw-api.mdx index ffff19fb09..b7a3bab5f4 100644 --- a/dev-docs/docs/@excalidraw/excalidraw/api/props/excalidraw-api.mdx +++ b/dev-docs/docs/@excalidraw/excalidraw/api/props/excalidraw-api.mdx @@ -13,7 +13,7 @@ Once the callback is triggered, you will need to store the api in state to acces ```jsx showLineNumbers export default function App() { const [excalidrawAPI, setExcalidrawAPI] = useState(null); - return setExcalidrawAPI(api)}} />; + return setExcalidrawAPI(api)} />; } ``` @@ -22,7 +22,7 @@ You can use this prop when you want to access some [Excalidraw APIs](https://git | API | Signature | Usage | | --- | --- | --- | | [updateScene](#updatescene) | `function` | updates the scene with the sceneData | -| [updateLibrary](#updatelibrary) | `function` | updates the scene with the sceneData | +| [updateLibrary](#updatelibrary) | `function` | updates the library | | [addFiles](#addfiles) | `function` | add files data to the appState | | [resetScene](#resetscene) | `function` | Resets the scene. If `resetLoadingState` is passed as true then it will also force set the loading state to false. | | [getSceneElementsIncludingDeleted](#getsceneelementsincludingdeleted) | `function` | Returns all the elements including the deleted in the scene | @@ -65,7 +65,7 @@ You can use this function to update the scene with the sceneData. It accepts the | `elements` | [`ImportedDataState["elements"]`](https://github.com/excalidraw/excalidraw/blob/master/packages/excalidraw/data/types.ts#L38) | The `elements` to be updated in the scene | | `appState` | [`ImportedDataState["appState"]`](https://github.com/excalidraw/excalidraw/blob/master/packages/excalidraw/data/types.ts#L39) | The `appState` to be updated in the scene. | | `collaborators` | MapCollaborator> | The list of collaborators to be updated in the scene. | -| `commitToHistory` | `boolean` | Implies if the `history (undo/redo)` should be recorded. Defaults to `false`. | +| `captureUpdate` | [`CaptureUpdateAction`](https://github.com/excalidraw/excalidraw/blob/master/packages/excalidraw/store.ts#L40) | Controls which updates should be captured by the `Store`. Captured updates are emmitted and listened to by other components, such as `History` for undo / redo purposes. | ```jsx live function App() { @@ -105,6 +105,7 @@ function App() { appState: { viewBackgroundColor: "#edf2ff", }, + captureUpdate: CaptureUpdateAction.IMMEDIATELY, }; excalidrawAPI.updateScene(sceneData); }; @@ -115,12 +116,24 @@ function App() { - setExcalidrawAPI(api)} /> + setExcalidrawAPI(api)} /> ); } ``` +#### captureUpdate + +You can use the `captureUpdate` to influence undo / redo behaviour. + +> **NOTE**: Some updates are not observed by the store / history - i.e. updates to `collaborators` object or parts of `AppState` which are not observed (not `ObservedAppState`). Such updates will never make it to the undo / redo stacks, regardless of the passed `captureUpdate` value. + +| | `captureUpdate` value | Notes | +| --- | --- | --- | +| _Immediately undoable_ | `CaptureUpdateAction.IMMEDIATELY` | Use for updates which should be captured. Should be used for most of the local updates. These updates will _immediately_ make it to the local undo / redo stacks. | +| _Eventually undoable_ | `CaptureUpdateAction.EVENTUALLY` | Use for updates which should not be captured immediately - likely exceptions which are part of some async multi-step process. Otherwise, all such updates would end up being captured with the next `CaptureUpdateAction.IMMEDIATELY` - triggered either by the next `updateScene` or internally by the editor. These updates will _eventually_ make it to the local undo / redo stacks. | +| _Never undoable_ | `CaptureUpdateAction.NEVER` | Use for updates which should never be recorded, such as remote updates or scene initialization. These updates will _never_ make it to the local undo / redo stacks. | + ### updateLibrary
@@ -188,7 +201,7 @@ function App() {
         Update Library
       
        setExcalidrawAPI(api)}
+        excalidrawAPI={(api) => setExcalidrawAPI(api)}
         // initial data retrieved from https://github.com/excalidraw/excalidraw/blob/master/dev-docs/packages/excalidraw/initialData.js
         initialData={{
           libraryItems: initialData.libraryItems,
diff --git a/dev-docs/docs/@excalidraw/excalidraw/api/props/props.mdx b/dev-docs/docs/@excalidraw/excalidraw/api/props/props.mdx
index 766c723e43..5c2a5501b9 100644
--- a/dev-docs/docs/@excalidraw/excalidraw/api/props/props.mdx
+++ b/dev-docs/docs/@excalidraw/excalidraw/api/props/props.mdx
@@ -5,30 +5,31 @@ All `props` are _optional_.
 | Name | Type | Default | Description |
 | --- | --- | --- | --- |
 | [`initialData`](/docs/@excalidraw/excalidraw/api/props/initialdata) | `object` | `null` | Promise | `null` | The initial data with which app loads. |
-| [`excalidrawAPI`](/docs/@excalidraw/excalidraw/api/props/excalidraw-api) | `function` | _ | Callback triggered with the excalidraw api once rendered |
-| [`isCollaborating`](#iscollaborating) | `boolean` | _ | This indicates if the app is in `collaboration` mode |
-| [`onChange`](#onchange) | `function` | _ | This callback is triggered whenever the component updates due to any change. This callback will receive the excalidraw `elements` and the current `app state`. |
-| [`onPointerUpdate`](#onpointerupdate) | `function` | _ | Callback triggered when mouse pointer is updated. |
-| [`onPointerDown`](#onpointerdown) | `function` | _ | This prop if passed gets triggered on pointer down evenets |
-| [`onScrollChange`](#onscrollchange) | `function` | _ | This prop if passed gets triggered when scrolling the canvas. |
-| [`onPaste`](#onpaste) | `function` | _ | Callback to be triggered if passed when the something is pasted in to the scene |
-| [`onLibraryChange`](#onlibrarychange) | `function` | _ | The callback if supplied is triggered when the library is updated and receives the library items. |
-| [`onLinkOpen`](#onlinkopen) | `function` | _ | The callback if supplied is triggered when any link is opened. |
+| [`excalidrawAPI`](/docs/@excalidraw/excalidraw/api/props/excalidraw-api) | `function` | \_ | Callback triggered with the excalidraw api once rendered |
+| [`isCollaborating`](#iscollaborating) | `boolean` | \_ | This indicates if the app is in `collaboration` mode |
+| [`onChange`](#onchange) | `function` | \_ | This callback is triggered whenever the component updates due to any change. This callback will receive the excalidraw `elements` and the current `app state`. |
+| [`onPointerUpdate`](#onpointerupdate) | `function` | \_ | Callback triggered when mouse pointer is updated. |
+| [`onPointerDown`](#onpointerdown) | `function` | \_ | This prop if passed gets triggered on pointer down events |
+| [`onScrollChange`](#onscrollchange) | `function` | \_ | This prop if passed gets triggered when scrolling the canvas. |
+| [`onPaste`](#onpaste) | `function` | \_ | Callback to be triggered if passed when something is pasted into the scene |
+| [`onLibraryChange`](#onlibrarychange) | `function` | \_ | The callback if supplied is triggered when the library is updated and receives the library items. |
+| [`generateLinkForSelection`](#generatelinkforselection) | `function` | \_ | Allows you to override `url` generation when linking to Excalidraw elements. |
+| [`onLinkOpen`](#onlinkopen) | `function` | \_ | The callback if supplied is triggered when any link is opened. |
 | [`langCode`](#langcode) | `string` | `en` | Language code string to be used in Excalidraw |
-| [`renderTopRightUI`](/docs/@excalidraw/excalidraw/api/props/render-props#rendertoprightui) | `function` | _ | Render function that renders custom UI in top right corner |
-| [`renderCustomStats`](/docs/@excalidraw/excalidraw/api/props/render-props#rendercustomstats) | `function` | _ | Render function that can be used to render custom stats on the stats dialog. |
-| [`viewModeEnabled`](#viewmodeenabled) | `boolean` | _ | This indicates if the app is in `view` mode. |
-| [`zenModeEnabled`](#zenmodeenabled) | `boolean` | _ | This indicates if the `zen` mode is enabled |
-| [`gridModeEnabled`](#gridmodeenabled) | `boolean` | _ | This indicates if the `grid` mode is enabled |
-| [`libraryReturnUrl`](#libraryreturnurl) | `string` | _ | What URL should [libraries.excalidraw.com](https://libraries.excalidraw.com) be installed to |
+| [`renderTopRightUI`](/docs/@excalidraw/excalidraw/api/props/render-props#rendertoprightui) | `function` | \_ | Render function that renders custom UI in top right corner |
+| [`renderCustomStats`](/docs/@excalidraw/excalidraw/api/props/render-props#rendercustomstats) | `function` | \_ | Render function that can be used to render custom stats on the stats dialog. |
+| [`viewModeEnabled`](#viewmodeenabled) | `boolean` | \_ | This indicates if the app is in `view` mode. |
+| [`zenModeEnabled`](#zenmodeenabled) | `boolean` | \_ | This indicates if the `zen` mode is enabled |
+| [`gridModeEnabled`](#gridmodeenabled) | `boolean` | \_ | This indicates if the `grid` mode is enabled |
+| [`libraryReturnUrl`](#libraryreturnurl) | `string` | \_ | What URL should [libraries.excalidraw.com](https://libraries.excalidraw.com) be installed to |
 | [`theme`](#theme) | `"light"` | `"dark"` | `"light"` | The theme of the Excalidraw component |
 | [`name`](#name) | `string` |  | Name of the drawing |
 | [`UIOptions`](/docs/@excalidraw/excalidraw/api/props/ui-options) | `object` | [DEFAULT UI OPTIONS](https://github.com/excalidraw/excalidraw/blob/master/packages/excalidraw/constants.ts#L151) | To customise UI options. Currently we support customising [`canvas actions`](/docs/@excalidraw/excalidraw/api/props/ui-options#canvasactions) |
 | [`detectScroll`](#detectscroll) | `boolean` | `true` | Indicates whether to update the offsets when nearest ancestor is scrolled. |
 | [`handleKeyboardGlobally`](#handlekeyboardglobally) | `boolean` | `false` | Indicates whether to bind the keyboard events to document. |
-| [`autoFocus`](#autofocus) | `boolean` | `false` | indicates whether to focus the Excalidraw component on page load |
-| [`generateIdForFile`](#generateidforfile) | `function` | _ | Allows you to override `id` generation for files added on canvas |
-| [`validateEmbeddable`](#validateEmbeddable) | string[] | `boolean | RegExp | RegExp[] | ((link: string) => boolean | undefined)` | \_ | use for custom src url validation |
+| [`autoFocus`](#autofocus) | `boolean` | `false` | Indicates whether to focus the Excalidraw component on page load |
+| [`generateIdForFile`](#generateidforfile) | `function` | \_ | Allows you to override `id` generation for files added on canvas |
+| [`validateEmbeddable`](#validateembeddable) | `string[]` \| `boolean` \| `RegExp` \| `RegExp[]` \| ((link: string) => boolean | undefined) | \_ | use for custom src url validation |
 | [`renderEmbeddable`](/docs/@excalidraw/excalidraw/api/props/render-props#renderEmbeddable) | `function` | \_ | Render function that can override the built-in `