diff --git a/.codesandbox/Dockerfile b/.codesandbox/Dockerfile new file mode 100644 index 0000000000..fd5b38d1e8 --- /dev/null +++ b/.codesandbox/Dockerfile @@ -0,0 +1,5 @@ +FROM node:18-bullseye + +# Vite wants to open the browser using `open`, so we +# need to install those utils. +RUN apt update -y && apt install -y xdg-utils diff --git a/.codesandbox/tasks.json b/.codesandbox/tasks.json index 360636c4c0..51c6e4e16f 100644 --- a/.codesandbox/tasks.json +++ b/.codesandbox/tasks.json @@ -27,7 +27,10 @@ "start": { "name": "Start Excalidraw", "command": "yarn start", - "runAtStart": true + "runAtStart": true, + "preview": { + "port": 3000 + } }, "test": { "name": "Run Tests", @@ -37,7 +40,11 @@ "install-deps": { "name": "Install Dependencies", "command": "yarn install", - "restartOn": { "files": ["yarn.lock"] } + "restartOn": { + "files": ["yarn.lock"], + "branch": false, + "resume": false + } } } } diff --git a/.dockerignore b/.dockerignore index 7a01509475..6472839a78 100644 --- a/.dockerignore +++ b/.dockerignore @@ -4,8 +4,16 @@ !.eslintrc.json !.npmrc !.prettierrc +!excalidraw-app/ !package.json !public/ -!src/ +!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 c56b62b360..bf641c34c8 100644 --- a/.env.development +++ b/.env.development @@ -1,30 +1,55 @@ -REACT_APP_BACKEND_V2_GET_URL=https://json-dev.excalidraw.com/api/v2/ -REACT_APP_BACKEND_V2_POST_URL=https://json-dev.excalidraw.com/api/v2/post/ +MODE="development" -REACT_APP_LIBRARY_URL=https://libraries.excalidraw.com -REACT_APP_LIBRARY_BACKEND=https://us-central1-excalidraw-room-persistence.cloudfunctions.net/libraries +VITE_APP_BACKEND_V2_GET_URL=https://json-dev.excalidraw.com/api/v2/ +VITE_APP_BACKEND_V2_POST_URL=https://json-dev.excalidraw.com/api/v2/post/ + +VITE_APP_LIBRARY_URL=https://libraries.excalidraw.com +VITE_APP_LIBRARY_BACKEND=https://us-central1-excalidraw-room-persistence.cloudfunctions.net/libraries # collaboration WebSocket server (https://github.com/excalidraw/excalidraw-room) -REACT_APP_WS_SERVER_URL=http://localhost:3002 +VITE_APP_WS_SERVER_URL=http://localhost:3002 -# set this only if using the collaboration workflow we use on excalidraw.com -REACT_APP_PORTAL_URL= +VITE_APP_PLUS_LP=https://plus.excalidraw.com +VITE_APP_PLUS_APP=http://localhost:3000 -REACT_APP_FIREBASE_CONFIG='{"apiKey":"AIzaSyCMkxA60XIW8KbqMYL7edC4qT5l4qHX2h8","authDomain":"excalidraw-oss-dev.firebaseapp.com","projectId":"excalidraw-oss-dev","storageBucket":"excalidraw-oss-dev.appspot.com","messagingSenderId":"664559512677","appId":"1:664559512677:web:a385181f2928d328a7aa8c"}' +VITE_APP_AI_BACKEND=http://localhost:3015 + +VITE_APP_FIREBASE_CONFIG='{"apiKey":"AIzaSyCMkxA60XIW8KbqMYL7edC4qT5l4qHX2h8","authDomain":"excalidraw-oss-dev.firebaseapp.com","projectId":"excalidraw-oss-dev","storageBucket":"excalidraw-oss-dev.appspot.com","messagingSenderId":"664559512677","appId":"1:664559512677:web:a385181f2928d328a7aa8c"}' # 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 -REACT_APP_DEV_ENABLE_SW= # whether to disable live reload / HMR. Usuaully what you want to do when # debugging Service Workers. -REACT_APP_DEV_DISABLE_LIVE_RELOAD= -REACT_APP_DISABLE_TRACKING=true +VITE_APP_DEV_DISABLE_LIVE_RELOAD= +VITE_APP_ENABLE_TRACKING=true FAST_REFRESH=false +# The port the run the dev server +VITE_APP_PORT=3000 + #Debug flags # To enable bounding box for text containers -REACT_APP_DEBUG_ENABLE_TEXT_CONTAINER_BOUNDING_BOX= +VITE_APP_DEBUG_ENABLE_TEXT_CONTAINER_BOUNDING_BOX= + +# Set this flag to false if you want to open the overlay by default +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 b86aa4bcc5..72dd24d6ec 100644 --- a/.env.production +++ b/.env.production @@ -1,15 +1,34 @@ -REACT_APP_BACKEND_V2_GET_URL=https://json.excalidraw.com/api/v2/ -REACT_APP_BACKEND_V2_POST_URL=https://json.excalidraw.com/api/v2/post/ +MODE="production" -REACT_APP_LIBRARY_URL=https://libraries.excalidraw.com -REACT_APP_LIBRARY_BACKEND=https://us-central1-excalidraw-room-persistence.cloudfunctions.net/libraries +VITE_APP_BACKEND_V2_GET_URL=https://json.excalidraw.com/api/v2/ +VITE_APP_BACKEND_V2_POST_URL=https://json.excalidraw.com/api/v2/post/ -REACT_APP_PORTAL_URL=https://portal.excalidraw.com -# Fill to set socket server URL used for collaboration. -# Meant for forks only: excalidraw.com uses custom REACT_APP_PORTAL_URL flow -REACT_APP_WS_SERVER_URL= +VITE_APP_LIBRARY_URL=https://libraries.excalidraw.com +VITE_APP_LIBRARY_BACKEND=https://us-central1-excalidraw-room-persistence.cloudfunctions.net/libraries -REACT_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_PLUS_LP=https://plus.excalidraw.com +VITE_APP_PLUS_APP=https://app.excalidraw.com + +VITE_APP_AI_BACKEND=https://oss-ai.excalidraw.com + +# socket server URL used for collaboration +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_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 -REACT_APP_PLUS_APP=https://app.excalidraw.com -REACT_APP_DISABLE_TRACKING= diff --git a/.eslintignore b/.eslintignore index b238ce5f7b..8b4f458dee 100644 --- a/.eslintignore +++ b/.eslintignore @@ -5,4 +5,7 @@ package-lock.json firebase/ dist/ public/workbox -src/packages/excalidraw/types +packages/excalidraw/types +examples/**/public +dev-dist +coverage diff --git a/.eslintrc.json b/.eslintrc.json index fbb12f59d4..89f8227361 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,7 +1,43 @@ { "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\")." + } + ], + "react/jsx-no-target-blank": [ + "error", + { + "allowReferrer": true + } + ] } } diff --git a/.github/assets/logo.png b/.github/assets/logo.png deleted file mode 100644 index d9b8953ebd..0000000000 Binary files a/.github/assets/logo.png and /dev/null differ diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000000..aebac52a08 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,45 @@ +# Project coding standards + +## Generic Communication Guidelines + +- Be succint and be aware that expansive generative AI answers are costly and slow +- Avoid providing explanations, trying to teach unless asked for, your chat partner is an expert +- Stop apologising if corrected, just provide the correct information or code +- Prefer code unless asked for explanation +- Stop summarizing what you've changed after modifications unless asked for + +## TypeScript Guidelines + +- Use TypeScript for all new code +- Where possible, prefer implementations without allocation +- When there is an option, opt for more performant solutions and trade RAM usage for less CPU cycles +- Prefer immutable data (const, readonly) +- Use optional chaining (?.) and nullish coalescing (??) operators + +## React Guidelines + +- Use functional components with hooks +- Follow the React hooks rules (no conditional hooks) +- Keep components small and focused +- Use CSS modules for component styling + +## Naming Conventions + +- Use PascalCase for component names, interfaces, and type aliases +- Use camelCase for variables, functions, and methods +- Use ALL_CAPS for constants + +## Error Handling + +- Use try/catch blocks for async operations +- Implement proper error boundaries in React components +- Always log errors with contextual information + +## Testing + +- Always attempt to fix #problems +- Always offer to run `yarn test:app` in the project root after modifications are complete and attempt fixing the issues reported + +## Types + +- Always include `packages/math/src/types.ts` in the context when your write math related code and always use the Point type instead of { x, y} diff --git a/.github/workflows/autorelease-excalidraw.yml b/.github/workflows/autorelease-excalidraw.yml index ad0a0a7e9c..6e2c0d00e0 100644 --- a/.github/workflows/autorelease-excalidraw.yml +++ b/.github/workflows/autorelease-excalidraw.yml @@ -12,10 +12,10 @@ jobs: - uses: actions/checkout@v2 with: fetch-depth: 2 - - name: Setup Node.js 14.x + - name: Setup Node.js 18.x uses: actions/setup-node@v2 with: - node-version: 14.x + node-version: 18.x - name: Set up publish access run: | npm config set //registry.npmjs.org/:_authToken ${NPM_TOKEN} @@ -23,5 +23,5 @@ jobs: NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - name: Auto release run: | - yarn add @actions/core - yarn autorelease + yarn add @actions/core -W + yarn release --tag=next --non-interactive diff --git a/.github/workflows/autorelease-preview.yml b/.github/workflows/autorelease-preview.yml deleted file mode 100644 index 8fe7f40b58..0000000000 --- a/.github/workflows/autorelease-preview.yml +++ /dev/null @@ -1,55 +0,0 @@ -name: Auto release excalidraw preview -on: - issue_comment: - types: [created, edited] - -jobs: - Auto-release-excalidraw-preview: - name: Auto release preview - if: github.event.comment.body == '@excalibot trigger release' && github.event.issue.pull_request - runs-on: ubuntu-latest - steps: - - name: React to release comment - uses: peter-evans/create-or-update-comment@v1 - with: - token: ${{ secrets.PUSH_TRANSLATIONS_COVERAGE_PAT }} - comment-id: ${{ github.event.comment.id }} - reactions: "+1" - - name: Get PR SHA - id: sha - uses: actions/github-script@v4 - with: - result-encoding: string - script: | - const { owner, repo, number } = context.issue; - const pr = await github.pulls.get({ - owner, - repo, - pull_number: number, - }); - return pr.data.head.sha - - uses: actions/checkout@v2 - with: - ref: ${{ steps.sha.outputs.result }} - fetch-depth: 2 - - name: Setup Node.js 14.x - uses: actions/setup-node@v2 - with: - node-version: 14.x - - name: Set up publish access - run: | - npm config set //registry.npmjs.org/:_authToken ${NPM_TOKEN} - env: - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - - name: Auto release preview - id: "autorelease" - run: | - yarn add @actions/core - yarn autorelease preview ${{ github.event.issue.number }} - - name: Post comment post release - if: always() - uses: peter-evans/create-or-update-comment@v1 - with: - token: ${{ secrets.PUSH_TRANSLATIONS_COVERAGE_PAT }} - issue-number: ${{ github.event.issue.number }} - body: "@${{ github.event.comment.user.login }} ${{ steps.autorelease.outputs.result }}" diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 77d2ef4d2c..82f826361c 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -9,14 +9,14 @@ jobs: steps: - uses: actions/checkout@v2 - - name: Setup Node.js 14.x + - name: Setup Node.js 18.x uses: actions/setup-node@v2 with: - node-version: 14.x + node-version: 18.x - name: Install and lint run: | - yarn --frozen-lockfile + yarn install yarn test:other yarn test:code yarn test:typecheck diff --git a/.github/workflows/locales-coverage.yml b/.github/workflows/locales-coverage.yml index 924dc9e973..957e9bc37c 100644 --- a/.github/workflows/locales-coverage.yml +++ b/.github/workflows/locales-coverage.yml @@ -10,23 +10,23 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: token: ${{ secrets.PUSH_TRANSLATIONS_COVERAGE_PAT }} - - name: Setup Node.js 14.x + - name: Setup Node.js 18.x uses: actions/setup-node@v2 with: - node-version: 14.x + node-version: 18.x - name: Create report file run: | yarn locales-coverage - FILE_CHANGED=$(git diff src/locales/percentages.json) + FILE_CHANGED=$(git diff packages/excalidraw/locales/percentages.json) if [ ! -z "${FILE_CHANGED}" ]; then git config --global user.name 'Excalidraw Bot' git config --global user.email 'bot@excalidraw.com' - git add src/locales/percentages.json + git add packages/excalidraw/locales/percentages.json git commit -am "Auto commit: Calculate translation coverage" git push fi diff --git a/.github/workflows/publish-docker.yml b/.github/workflows/publish-docker.yml index a4a8a4c5ff..68eee27755 100644 --- a/.github/workflows/publish-docker.yml +++ b/.github/workflows/publish-docker.yml @@ -17,9 +17,14 @@ jobs: with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 - name: Build and push - uses: docker/build-push-action@v3 + uses: docker/build-push-action@v5 with: context: . push: true tags: excalidraw/excalidraw:latest + platforms: linux/amd64, linux/arm64, linux/arm/v7 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/sentry-production.yml b/.github/workflows/sentry-production.yml index 6f53f91eb7..cea4cf63d6 100644 --- a/.github/workflows/sentry-production.yml +++ b/.github/workflows/sentry-production.yml @@ -10,10 +10,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - name: Setup Node.js 14.x + - name: Setup Node.js 18.x uses: actions/setup-node@v2 with: - node-version: 14.x + node-version: 18.x - name: Install and build run: | yarn --frozen-lockfile diff --git a/.github/workflows/size-limit.yml b/.github/workflows/size-limit.yml index 8ced8ee037..5bd3c0d92b 100644 --- a/.github/workflows/size-limit.yml +++ b/.github/workflows/size-limit.yml @@ -15,16 +15,14 @@ jobs: uses: actions/setup-node@v3 with: node-version: 18.x - - name: Install - run: yarn --frozen-lockfile - - name: Install in src/packages/excalidraw - run: yarn --frozen-lockfile - working-directory: src/packages/excalidraw + - name: Install in packages/excalidraw + run: yarn + working-directory: packages/excalidraw env: CI: true - uses: andresz1/size-limit-action@v1 with: github_token: ${{ secrets.GITHUB_TOKEN }} - build_script: build:umd + build_script: build:esm skip_step: install - directory: src/packages/excalidraw + directory: packages/excalidraw diff --git a/.github/workflows/test-coverage-pr.yml b/.github/workflows/test-coverage-pr.yml new file mode 100644 index 0000000000..7ff40ad5d2 --- /dev/null +++ b/.github/workflows/test-coverage-pr.yml @@ -0,0 +1,26 @@ +name: Test Coverage PR +on: + pull_request: + +jobs: + coverage: + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: write + + steps: + - uses: actions/checkout@v2 + - name: "Install Node" + uses: actions/setup-node@v2 + with: + node-version: "18.x" + - name: "Install Deps" + run: yarn install + - name: "Test Coverage" + run: yarn test:coverage + - name: "Report Coverage" + if: always() # Also generate the report if tests are failing + uses: davelosert/vitest-coverage-report-action@v2 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b64ea47352..7d454ecfc4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,17 +1,19 @@ name: Tests -on: pull_request +on: + push: + branches: master jobs: test: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - name: Setup Node.js 14.x - uses: actions/setup-node@v2 + - uses: actions/checkout@v4 + - name: Setup Node.js 18.x + uses: actions/setup-node@v4 with: - node-version: 14.x + node-version: 18.x - name: Install and test run: | - yarn --frozen-lockfile + yarn install yarn test:app diff --git a/.gitignore b/.gitignore index e637a8c0f9..6f3a62bba3 100644 --- a/.gitignore +++ b/.gitignore @@ -21,8 +21,9 @@ npm-debug.log* package-lock.json yarn-debug.log* yarn-error.log* -src/packages/excalidraw/types -src/packages/excalidraw/example/public/bundle.js -src/packages/excalidraw/example/public/excalidraw-assets-dev -src/packages/excalidraw/example/public/excalidraw.development.js +packages/excalidraw/types coverage +dev-dist +html +meta*.json +.claude diff --git a/.nvmrc b/.nvmrc index 8351c19397..3c032078a4 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -14 +18 diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index a6506e9a0f..0000000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,3 +0,0 @@ -## 2020-10-13 - -- Added ability to embed scene source into exported PNG/SVG files so you can import the scene from them (open via `Load` button or drag & drop). #2219 diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000000..4faf291ec8 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,34 @@ +# CLAUDE.md + +## Project Structure + +Excalidraw is a **monorepo** with a clear separation between the core library and the application: + +- **`packages/excalidraw/`** - Main React component library published to npm as `@excalidraw/excalidraw` +- **`excalidraw-app/`** - Full-featured web application (excalidraw.com) that uses the library +- **`packages/`** - Core packages: `@excalidraw/common`, `@excalidraw/element`, `@excalidraw/math`, `@excalidraw/utils` +- **`examples/`** - Integration examples (NextJS, browser script) + +## Development Workflow + +1. **Package Development**: Work in `packages/*` for editor features +2. **App Development**: Work in `excalidraw-app/` for app-specific features +3. **Testing**: Always run `yarn test:update` before committing +4. **Type Safety**: Use `yarn test:typecheck` to verify TypeScript + +## Development Commands + +```bash +yarn test:typecheck # TypeScript type checking +yarn test:update # Run all tests (with snapshot updates) +yarn fix # Auto-fix formatting and linting issues +``` + +## Architecture Notes + +### Package System + +- Uses Yarn workspaces for monorepo management +- Internal packages use path aliases (see `vitest.config.mts`) +- Build system uses esbuild for packages, Vite for the app +- TypeScript throughout with strict configuration diff --git a/Dockerfile b/Dockerfile index d1fa424e53..c08385d654 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,17 +1,20 @@ -FROM node:14-alpine AS build +FROM --platform=${BUILDPLATFORM} 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 --mount=type=cache,target=/root/.cache/yarn \ + npm_config_target_arch=${TARGETARCH} yarn --network-timeout 600000 ARG NODE_ENV=production -COPY . . -RUN yarn build:app:docker +RUN npm_config_target_arch=${TARGETARCH} yarn build:app:docker -FROM nginx:1.21-alpine +FROM --platform=${TARGETPLATFORM} 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 48529165e6..f1cf030539 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,13 @@ - - Excalidraw + + Excalidraw

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

@@ -25,12 +25,18 @@ Excalidraw is released under the MIT license. + + npm downloads/month + PRs welcome! Chat on Discord + + Ask DeepWiki + Follow Excalidraw on Twitter @@ -60,7 +66,7 @@ The Excalidraw editor (npm package) supports: - 🏗️ Customizable. - 📷 Image support. - 😀 Shape libraries support. -- 👅 Localization (i18n) support. +- 🌐 Localization (i18n) support. - 🖼️ Export to PNG, SVG & clipboard. - 💾 Open format - export drawings as an `.excalidraw` json file. - ⚒️ Wide range of tools - rectangle, circle, diamond, arrow, line, free-draw, eraser... @@ -70,7 +76,7 @@ The Excalidraw editor (npm package) supports: ## Excalidraw.com -The app hosted at [excalidraw.com](https://excalidraw.com) is a minimal showcase of what you can build with Excalidraw. Its [source code](https://github.com/excalidraw/excalidraw/tree/master/src/excalidraw-app) is part of this repository as well, and the app features: +The app hosted at [excalidraw.com](https://excalidraw.com) is a minimal showcase of what you can build with Excalidraw. Its [source code](https://github.com/excalidraw/excalidraw/tree/master/excalidraw-app) is part of this repository as well, and the app features: - 📡 PWA support (works offline). - 🤼 Real-time collaboration. @@ -82,19 +88,17 @@ We'll be adding these features as drop-in plugins for the npm package in the fut ## Quick start -Install the [Excalidraw npm package](https://www.npmjs.com/package/@excalidraw/excalidraw): +**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 ``` -Don't forget to check out our [Documentation](https://docs.excalidraw.com)! +Check out our [documentation](https://docs.excalidraw.com/docs/@excalidraw/excalidraw/installation) for more details! ## Contributing diff --git a/crowdin.yml b/crowdin.yml index a08b939c60..ccb8a17aa7 100644 --- a/crowdin.yml +++ b/crowdin.yml @@ -1,3 +1,3 @@ files: - - source: /src/locales/en.json - translation: /src/locales/%locale%.json + - source: /packages/excalidraw/locales/en.json + translation: /packages/excalidraw/locales/%locale%.json diff --git a/dev-docs/docs/@excalidraw/excalidraw/api/children-components/children-components-intro.mdx b/dev-docs/docs/@excalidraw/excalidraw/api/children-components/children-components-intro.mdx index 706adb594c..06bb1cbaf6 100644 --- a/dev-docs/docs/@excalidraw/excalidraw/api/children-components/children-components-intro.mdx +++ b/dev-docs/docs/@excalidraw/excalidraw/api/children-components/children-components-intro.mdx @@ -17,5 +17,6 @@ Below are the currently supported components: - [MainMenu](/docs/@excalidraw/excalidraw/api/children-components/main-menu) - [WelcomeScreen](/docs/@excalidraw/excalidraw/api/children-components/welcome-screen) +- [Sidebar](/docs/@excalidraw/excalidraw/api/children-components/sidebar) - [Footer](/docs/@excalidraw/excalidraw/api/children-components/footer) - [LiveCollaborationTrigger](/docs/@excalidraw/excalidraw/api/children-components/live-collaboration-trigger) diff --git a/dev-docs/docs/@excalidraw/excalidraw/api/children-components/footer.mdx b/dev-docs/docs/@excalidraw/excalidraw/api/children-components/footer.mdx index cdd5ea5a49..e7852cee94 100644 --- a/dev-docs/docs/@excalidraw/excalidraw/api/children-components/footer.mdx +++ b/dev-docs/docs/@excalidraw/excalidraw/api/children-components/footer.mdx @@ -2,7 +2,7 @@ Earlier we were using `renderFooter` prop to render custom footer which was removed in [#5970](https://github.com/excalidraw/excalidraw/pull/5970). Now you can pass a `Footer` component instead to render the custom UI for footer. -You will need to import the `Footer` component from the package and wrap your component with the Footer component. The `Footer` should a valid React Node. +You will need to import the `Footer` component from the package and wrap your component with the Footer component. The `Footer` should be a valid React Node. **Usage** @@ -25,7 +25,7 @@ function App() { } ``` -This will only for `Desktop` devices. +This will only work for `Desktop` devices. For `mobile` you will need to render it inside the [MainMenu](#mainmenu). You can use the [`useDevice`](#useDevice) hook to check the type of device, this will be available only inside the `children` of `Excalidraw` component. @@ -34,7 +34,7 @@ Open the `Menu` in the below playground and you will see the `custom footer` ren ```jsx live noInline const MobileFooter = ({}) => { const device = useDevice(); - if (device.isMobile) { + if (device.editor.isMobile) { return (