Initial Vite migration setup

This commit is contained in:
BlackWolfNews 2025-04-13 17:46:38 -06:00
parent 7c58477382
commit 35171070a1
88 changed files with 3341 additions and 3664 deletions

13
.github/workflows/deploy.yml vendored Normal file
View file

@ -0,0 +1,13 @@
name: Deploy
on: push
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: yarn && yarn build
- uses: amondnet/vercel-action@v30
with:
vercel-token: ${{ secrets.VERCEL_TOKEN }}
vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}

1
.gitignore vendored
View file

@ -26,3 +26,4 @@ coverage
dev-dist dev-dist
html html
meta*.json meta*.json
.vercel

15
index.html Normal file
View file

@ -0,0 +1,15 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>ExcalidrawFork</title>
<link rel="stylesheet" href="/excalidraw.css" />
</head>
<body>
<div id="app"></div>
<script>
window.EXCALIDRAW_ASSET_PATH = '/assets/';
</script>
<script type="module" src="/src/index.tsx"></script>
</body>
</html>

0
memory-bank Normal file
View file

View file

@ -1,88 +1,64 @@
{ {
"name": "excalidraw-fork",
"type": "module",
"private": true, "private": true,
"name": "excalidraw-monorepo", "version": "1.0.0",
"packageManager": "yarn@1.22.22", "packageManager": "yarn@1.22.22",
"workspaces": [ "workspaces": [
"excalidraw-app", "excalidraw-app",
"packages/*", "packages/*",
"examples/*" "examples/*"
], ],
"nohoist": [],
"scripts": {
"dev": "vite",
"build": "yarn build:excalidraw && vite build",
"preview": "vite preview",
"lint": "eslint . --ext .ts,.tsx",
"typecheck": "tsc --noEmit",
"build:excalidraw": "cd packages/excalidraw && yarn build",
"build:package": "vite build --config vite.package.config.ts",
"prebuild": "yarn build:package"
},
"dependencies": {
"@excalidraw/excalidraw": "^0.18.0",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"devDependencies": { "devDependencies": {
"@babel/preset-env": "7.26.9", "@babel/preset-env": "^7.26.9",
"@excalidraw/eslint-config": "1.0.3", "@excalidraw/eslint-config": "^1.0.3",
"@excalidraw/prettier-config": "1.0.2", "@excalidraw/prettier-config": "^1.0.2",
"@types/chai": "4.3.0", "@testing-library/jest-dom": "^6.6.3",
"@types/jest": "27.4.0", "@testing-library/react": "^16.3.0",
"@types/lodash.throttle": "4.1.7", "@types/chai": "^4.3.0",
"@types/react": "19.0.10", "@types/jest": "^27.4.0",
"@types/react-dom": "19.0.4", "@types/lodash.throttle": "^4.1.7",
"@types/socket.io-client": "3.0.0", "@types/node": "22.14.0",
"@vitejs/plugin-react": "3.1.0", "@types/react": "^18.2.66",
"@vitest/coverage-v8": "3.0.7", "@types/react-dom": "^18.2.22",
"@vitest/ui": "2.0.5", "@typescript-eslint/eslint-plugin": "^7.6.0",
"chai": "4.3.6", "@typescript-eslint/parser": "^7.6.0",
"dotenv": "16.0.1", "@vitejs/plugin-react": "^4.2.1",
"eslint-config-prettier": "8.5.0", "eslint": "^8.56.0",
"eslint-config-react-app": "7.0.1", "eslint-plugin-react": "^7.33.2",
"eslint-plugin-import": "2.31.0", "eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-prettier": "3.3.1", "jest": "^29.7.0",
"http-server": "14.1.1", "prettier": "^3.2.5",
"husky": "7.0.4", "sass": "^1.71.1",
"jsdom": "22.1.0", "typescript": "^5.3.3",
"lint-staged": "12.3.7", "vite": "^5.1.4",
"pepjs": "0.5.3", "vite-plugin-checker": "^0.9.1",
"prettier": "2.6.2", "vitest": "3.1.1"
"rewire": "6.0.0",
"typescript": "4.9.4",
"vite": "5.0.12",
"vite-plugin-checker": "0.7.2",
"vite-plugin-ejs": "1.7.0",
"vite-plugin-pwa": "0.21.1",
"vite-plugin-svgr": "4.2.0",
"vitest": "3.0.6",
"vitest-canvas-mock": "0.3.3"
}, },
"engines": { "engines": {
"node": "18.0.0 - 22.x.x" "node": ">=18.0.0"
}, },
"homepage": ".", "homepage": ".",
"prettier": "@excalidraw/prettier-config", "prettier": "@excalidraw/prettier-config",
"scripts": {
"build-node": "node ./scripts/build-node.js",
"build:app:docker": "yarn --cwd ./excalidraw-app build:app:docker",
"build:app": "yarn --cwd ./excalidraw-app build:app",
"build:package": "yarn --cwd ./packages/excalidraw build:esm",
"build:version": "yarn --cwd ./excalidraw-app build:version",
"build": "yarn --cwd ./excalidraw-app build",
"build:preview": "yarn --cwd ./excalidraw-app build:preview",
"start": "yarn --cwd ./excalidraw-app start",
"start:production": "yarn --cwd ./excalidraw-app start:production",
"start:example": "yarn build:package && yarn --cwd ./examples/with-script-in-browser start",
"test:all": "yarn test:typecheck && yarn test:code && yarn test:other && yarn test:app --watch=false",
"test:app": "vitest",
"test:code": "eslint --max-warnings=0 --ext .js,.ts,.tsx .",
"test:other": "yarn prettier --list-different",
"test:typecheck": "tsc",
"test:update": "yarn test:app --update --watch=false",
"test": "yarn test:app",
"test:coverage": "vitest --coverage",
"test:coverage:watch": "vitest --coverage --watch",
"test:ui": "yarn test --ui --coverage.enabled=true",
"fix:code": "yarn test:code --fix",
"fix:other": "yarn prettier --write",
"fix": "yarn fix:other && yarn fix:code",
"locales-coverage": "node scripts/build-locales-coverage.js",
"locales-coverage:description": "node scripts/locales-coverage-description.js",
"prepare": "husky install",
"prettier": "prettier \"**/*.{css,scss,json,md,html,yml}\" --ignore-path=.eslintignore",
"autorelease": "node scripts/autorelease.js",
"prerelease:excalidraw": "node scripts/prerelease.js",
"release:excalidraw": "node scripts/release.js",
"rm:build": "rm -rf excalidraw-app/{build,dist,dev-dist} && rm -rf packages/*/{dist,build} && rm -rf examples/*/{build,dist}",
"rm:node_modules": "rm -rf node_modules && rm -rf excalidraw-app/node_modules && rm -rf packages/*/node_modules",
"clean-install": "yarn rm:node_modules && yarn install"
},
"resolutions": { "resolutions": {
"react": "^18.2.0",
"react-dom": "^18.2.0",
"strip-ansi": "6.0.1" "strip-ansi": "6.0.1"
} }
} }

1
packages/common/index.js Normal file
View file

@ -0,0 +1 @@
export {}

View file

@ -2,18 +2,18 @@
"name": "@excalidraw/common", "name": "@excalidraw/common",
"version": "0.1.0", "version": "0.1.0",
"type": "module", "type": "module",
"main": "./dist/common.umd.js",
"module": "./dist/common.es.js",
"types": "./dist/types/common/src/index.d.ts", "types": "./dist/types/common/src/index.d.ts",
"main": "./dist/prod/index.js", "dependencies": {
"module": "./dist/prod/index.js", "@excalidraw/math": "0.1.0"
},
"exports": { "exports": {
".": { ".": {
"types": "./dist/types/common/src/index.d.ts", "types": "./dist/types/common/src/index.d.ts",
"development": "./dist/dev/index.js", "import": "./dist/common.es.js",
"production": "./dist/prod/index.js", "require": "./dist/common.umd.js",
"default": "./dist/prod/index.js" "default": "./dist/common.es.js"
},
"./*": {
"types": "./../common/dist/types/common/src/*.d.ts"
} }
}, },
"files": [ "files": [
@ -50,7 +50,12 @@
"bugs": "https://github.com/excalidraw/excalidraw/issues", "bugs": "https://github.com/excalidraw/excalidraw/issues",
"repository": "https://github.com/excalidraw/excalidraw", "repository": "https://github.com/excalidraw/excalidraw",
"scripts": { "scripts": {
"gen:types": "rm -rf types && tsc", "gen:types": "if exist types rmdir /s /q types && tsc",
"build:esm": "rm -rf dist && node ../../scripts/buildBase.js && yarn gen:types" "build:esm": "if exist dist rmdir /s /q dist && node ../../scripts/buildBase.js && yarn gen:types",
"build": "vite build",
"test": "jest",
"lint": "eslint . --ext .ts,.tsx",
"prettier": "prettier --check .",
"prettier:fix": "prettier --write ."
} }
} }

View file

@ -4,6 +4,8 @@ import {
type LocalPoint, type LocalPoint,
} from "@excalidraw/math"; } from "@excalidraw/math";
console.log(pointFromPair);
import type { NullableGridSize } from "@excalidraw/excalidraw/types"; import type { NullableGridSize } from "@excalidraw/excalidraw/types";
export const getSizeFromPoints = ( export const getSizeFromPoints = (

View file

@ -0,0 +1,34 @@
import { defineConfig } from 'vite';
import { fileURLToPath } from 'node:url';
import path from 'path';
console.log('Alias set to:', path.resolve(__dirname, '../math/dist/math.es.js'));
export default defineConfig({
build: {
lib: {
entry: fileURLToPath(new URL('./src/index.ts', import.meta.url)),
name: 'ExcalidrawCommon',
fileName: (format) => `common.${format}.js`
},
rollupOptions: {
external: ['react', 'react-dom'],
output: {
globals: {
react: 'React',
'react-dom': 'ReactDOM'
},
format: 'es'
},
onwarn: (warning, warn) => {
if (warning.code === 'CIRCULAR_DEPENDENCY') return;
warn(warning);
}
}
},
resolve: {
alias: {
'@excalidraw/math': path.resolve(__dirname, '../math/dist/math.es.js')
}
}
});

View file

@ -2,23 +2,33 @@
"name": "@excalidraw/element", "name": "@excalidraw/element",
"version": "0.1.0", "version": "0.1.0",
"type": "module", "type": "module",
"types": "./dist/types/element/src/index.d.ts",
"main": "./dist/prod/index.js", "main": "./dist/prod/index.js",
"module": "./dist/prod/index.js", "module": "./dist/prod/index.js",
"types": "./dist/types/element/src/index.d.ts",
"exports": { "exports": {
".": { ".": {
"types": "./dist/types/element/src/index.d.ts", "types": "./dist/types/element/src/index.d.ts",
"development": "./dist/dev/index.js", "import": "./dist/prod/index.js",
"production": "./dist/prod/index.js", "require": "./dist/prod/index.js",
"default": "./dist/prod/index.js" "default": "./dist/prod/index.js"
}, },
"./*": { "./textElement": {
"types": "./../element/dist/types/element/src/*.d.ts" "types": "./dist/types/element/src/textElement.d.ts",
"import": "./dist/prod/index.js",
"require": "./dist/prod/index.js",
"default": "./dist/prod/index.js"
},
"./sizeHelpers": {
"types": "./dist/types/element/src/sizeHelpers.d.ts",
"import": "./dist/prod/index.js",
"require": "./dist/prod/index.js",
"default": "./dist/prod/index.js"
} }
}, },
"files": [ "files": [
"dist/*" "dist/*"
], ],
"description": "Excalidraw elements-related logic", "description": "Excalidraw elements-related logic",
"publishConfig": { "publishConfig": {
"access": "public" "access": "public"
@ -50,7 +60,7 @@
"bugs": "https://github.com/excalidraw/excalidraw/issues", "bugs": "https://github.com/excalidraw/excalidraw/issues",
"repository": "https://github.com/excalidraw/excalidraw", "repository": "https://github.com/excalidraw/excalidraw",
"scripts": { "scripts": {
"gen:types": "rm -rf types && tsc", "gen:types": "if exist types rmdir /s /q types && tsc",
"build:esm": "rm -rf dist && node ../../scripts/buildBase.js && yarn gen:types" "build:esm": "if exist dist rmdir /s /q dist && node ../../scripts/buildBase.cjs && yarn gen:types"
} }
} }

View file

@ -1,5 +1,6 @@
import { isInvisiblySmallElement } from "./sizeHelpers"; import { isInvisiblySmallElement } from "./sizeHelpers";
import { isLinearElementType } from "./typeChecks"; import { isLinearElementType } from "./typeChecks";
import * as textElement from "./textElement";
import type { import type {
ExcalidrawElement, ExcalidrawElement,
@ -71,3 +72,6 @@ export const clearElementsForExport = (
export const clearElementsForLocalStorage = ( export const clearElementsForLocalStorage = (
elements: readonly ExcalidrawElement[], elements: readonly ExcalidrawElement[],
) => _clearElements(elements); ) => _clearElements(elements);
export { textElement };
export * from "./sizeHelpers";

View file

@ -1,4 +1,4 @@
@import "../css/variables.module.scss"; @use "../css/styles" as *;
.excalidraw { .excalidraw {
.Avatar { .Avatar {

View file

@ -1,7 +1,7 @@
@import "../css/theme"; @use "../css/styles" as styles;
.excalidraw { .excalidraw {
.excalidraw-button { .excalidraw-button {
@include outlineButtonStyles; @include styles.outlineButtonStyles;
} }
} }

View file

@ -1,4 +1,4 @@
@import "../css/theme"; @use "../css/styles" as *;
.excalidraw { .excalidraw {
button.standalone { button.standalone {

View file

@ -1,4 +1,4 @@
@import "../css/variables.module.scss"; @use "../css/styles" as *;
.excalidraw { .excalidraw {
.Card { .Card {

View file

@ -1,4 +1,4 @@
@import "../css/variables.module.scss"; @use "../css/styles" as *;
.excalidraw { .excalidraw {
.Checkbox { .Checkbox {

View file

@ -1,4 +1,4 @@
@import "../../css/variables.module.scss"; @use "../../css/styles" as *;
.excalidraw { .excalidraw {
.focus-visible-none { .focus-visible-none {

View file

@ -1,4 +1,4 @@
@import "../../css/variables.module.scss"; @use "../../css/styles" as *;
$verticalBreakpoint: 861px; $verticalBreakpoint: 861px;

View file

@ -1,4 +1,4 @@
@import "../css/variables.module.scss"; @use "../css/styles" as *;
.excalidraw { .excalidraw {
.confirm-dialog { .confirm-dialog {

View file

@ -1,10 +1,10 @@
@import "../css/variables.module.scss"; @use "../css/styles" as styles;
.excalidraw { .excalidraw {
.context-menu { .context-menu {
position: relative; position: relative;
border-radius: 4px; border-radius: 4px;
box-shadow: 0 3px 10px transparentize($oc-black, 0.8); box-shadow: 0 3px 10px transparentize(styles.$oc-black, 0.8);
padding: 0; padding: 0;
list-style: none; list-style: none;
user-select: none; user-select: none;
@ -45,7 +45,7 @@
&.dangerous { &.dangerous {
.context-menu-item__label { .context-menu-item__label {
color: $oc-red-7; color: styles.$oc-red-7;
} }
} }
@ -69,7 +69,7 @@
.context-menu-item__label { .context-menu-item__label {
color: var(--popup-bg-color); color: var(--popup-bg-color);
} }
background-color: $oc-red-6; background-color: styles.$oc-red-6;
} }
} }
@ -93,6 +93,6 @@
.context-menu-item-separator { .context-menu-item-separator {
border: none; border: none;
border-top: 1px solid $oc-gray-5; border-top: 1px solid styles.$oc-gray-5;
} }
} }

View file

@ -1,4 +1,4 @@
@import "../css/variables.module.scss"; @use "../css/styles" as styles;
.excalidraw { .excalidraw {
.Dialog { .Dialog {

View file

@ -1,4 +1,4 @@
@import "../css/variables.module.scss"; @use "../css/styles" as *;
.excalidraw { .excalidraw {
.ElementLinkDialog { .ElementLinkDialog {

View file

@ -1,4 +1,4 @@
@import "../css/variables.module.scss"; @use "../css/styles" as styles;
.excalidraw { .excalidraw {
.ExportDialog__preview { .ExportDialog__preview {

View file

@ -1,18 +1,4 @@
@import "../css/variables.module.scss"; @use "../css/styles" as styles;
@keyframes successStatusAnimation {
0% {
transform: scale(0.35);
}
50% {
transform: scale(1.25);
}
100% {
transform: scale(1);
}
}
.excalidraw { .excalidraw {
.ExcButton { .ExcButton {
@ -315,3 +301,17 @@
} }
} }
} }
@keyframes successStatusAnimation {
0% {
transform: scale(0.35);
}
50% {
transform: scale(1.25);
}
100% {
transform: scale(1);
}
}

View file

@ -1,4 +1,4 @@
@import "../css/variables.module.scss"; @use "../css/styles" as *;
.excalidraw { .excalidraw {
.FixedSideContainer { .FixedSideContainer {

View file

@ -1,4 +1,4 @@
@import "../../css/variables.module.scss"; @use "../../css/styles" as *;
.excalidraw { .excalidraw {
.FontPicker__container { .FontPicker__container {

View file

@ -1,4 +1,4 @@
@import "../css/variables.module.scss"; @use "../css/styles" as *;
.excalidraw { .excalidraw {
.HelpDialog { .HelpDialog {

View file

@ -1,4 +1,4 @@
@import "../css/variables.module.scss"; @use "../css/styles" as *;
// this is loosely based on the longest hint text // this is loosely based on the longest hint text
$wide-viewport-width: 1000px; $wide-viewport-width: 1000px;

View file

@ -1,4 +1,4 @@
@import "../css/variables.module.scss"; @use "../css/styles" as *;
.excalidraw { .excalidraw {
.picker { .picker {

View file

@ -1,4 +1,4 @@
@import "../css/variables.module.scss"; @use "../css/styles" as *;
.excalidraw { .excalidraw {
--ImageExportModal-preview-border: #d6d6d6; --ImageExportModal-preview-border: #d6d6d6;

View file

@ -1,5 +1,5 @@
@import "open-color/open-color"; @use "open-color/open-color.scss" as oc;
@import "../css/variables.module.scss"; @use "../css/styles" as styles;
.excalidraw { .excalidraw {
.layer-ui__wrapper.animate { .layer-ui__wrapper.animate {

View file

@ -1,4 +1,5 @@
@import "open-color/open-color"; @use "open-color/open-color.scss" as oc;
@use "../css/styles" as styles;
.excalidraw { .excalidraw {
.layer-ui__library { .layer-ui__library {

View file

@ -1,4 +1,4 @@
@import "open-color/open-color"; @use "../css/styles" as *;
.excalidraw { .excalidraw {
--container-padding-y: 1.5rem; --container-padding-y: 1.5rem;
@ -6,13 +6,13 @@
.library-menu-items__no-items { .library-menu-items__no-items {
text-align: center; text-align: center;
color: var(--color-gray-70); color: $oc-gray-7;
line-height: 1.5; line-height: 1.5;
font-size: 0.875rem; font-size: 0.875rem;
width: 100%; width: 100%;
&__label { &__label {
color: var(--color-primary); color: $oc-blue-7;
font-weight: 700; font-weight: 700;
font-size: 1.125rem; font-size: 1.125rem;
margin-bottom: 0.75rem; margin-bottom: 0.75rem;
@ -21,7 +21,7 @@
&.theme--dark { &.theme--dark {
.library-menu-items__no-items { .library-menu-items__no-items {
color: var(--color-gray-40); color: $oc-gray-5;
} }
} }
@ -60,7 +60,7 @@
} }
&__header { &__header {
color: var(--color-primary); color: $oc-blue-7;
font-size: 1.125rem; font-size: 1.125rem;
font-weight: 700; font-weight: 700;
margin-bottom: 0.75rem; margin-bottom: 0.75rem;
@ -86,7 +86,7 @@
font-weight: 500; font-weight: 500;
font-size: 0.9rem; font-size: 0.9rem;
margin: 0.6em 0.2em; margin: 0.6em 0.2em;
color: var(--text-primary-color); color: $text-primary-color;
} }
} }

View file

@ -1,4 +1,4 @@
@import "../css/variables.module.scss"; @use "../css/styles" as *;
.excalidraw { .excalidraw {
.library-unit { .library-unit {

View file

@ -1,4 +1,4 @@
@import "../css/variables.module.scss"; @use "../css/styles" as *;
.excalidraw { .excalidraw {
&.excalidraw-modal-container { &.excalidraw-modal-container {

View file

@ -1,4 +1,4 @@
@import "../../css/variables.module.scss"; @use "../../css/styles" as *;
.excalidraw { .excalidraw {
.OverwriteConfirm { .OverwriteConfirm {

View file

@ -1,4 +1,4 @@
@import "../css/variables.module.scss"; @use "../css/styles" as *;
.excalidraw { .excalidraw {
.PasteChartDialog { .PasteChartDialog {

View file

@ -1,3 +1,5 @@
@use "../css/styles" as *;
.excalidraw { .excalidraw {
.popover { .popover {
position: absolute; position: absolute;

View file

@ -1,4 +1,4 @@
@import "../css/variables.module.scss"; @use "../css/styles" as *;
.excalidraw { .excalidraw {
.publish-library { .publish-library {
@ -14,7 +14,7 @@
span { span {
font-weight: 500; font-weight: 500;
font-size: 1rem; font-size: 1rem;
color: $oc-gray-6; color: oc.$oc-gray-6;
} }
input, input,
textarea { textarea {
@ -24,7 +24,7 @@
} }
.required { .required {
color: $oc-red-8; color: oc.$oc-red-8;
margin: 0.2rem; margin: 0.2rem;
} }
} }
@ -48,22 +48,22 @@
} }
&--confirm.ToolIcon_type_button { &--confirm.ToolIcon_type_button {
background-color: $oc-blue-6; background-color: oc.$oc-blue-6;
&:hover { &:hover {
background-color: $oc-blue-8; background-color: oc.$oc-blue-8;
} }
} }
&--cancel.ToolIcon_type_button { &--cancel.ToolIcon_type_button {
background-color: $oc-gray-5; background-color: oc.$oc-gray-5;
&:hover { &:hover {
background-color: $oc-gray-6; background-color: oc.$oc-gray-6;
} }
} }
.ToolIcon__icon { .ToolIcon__icon {
color: $oc-white; color: oc.$oc-white;
.Spinner { .Spinner {
--spinner-color: #fff; --spinner-color: #fff;
svg { svg {
@ -83,7 +83,7 @@
} }
&-warning { &-warning {
color: $oc-red-6; color: oc.$oc-red-6;
} }
&-note { &-note {
@ -102,14 +102,14 @@
top: 0.3rem; top: 0.3rem;
left: 0.3rem; left: 0.3rem;
font-size: 0.7rem; font-size: 0.7rem;
color: $oc-red-7; color: oc.$oc-red-7;
background: rgba(255, 255, 255, 0.9); background: rgba(255, 255, 255, 0.9);
padding: 0.1rem 0.2rem; padding: 0.1rem 0.2rem;
border-radius: 0.2rem; border-radius: 0.2rem;
} }
&__svg { &__svg {
background-color: $oc-white; background-color: oc.$oc-white;
padding: 0.3rem; padding: 0.3rem;
width: 7.5rem; width: 7.5rem;
height: 7.5rem; height: 7.5rem;
@ -121,7 +121,7 @@
} }
.ToolIcon__icon { .ToolIcon__icon {
background-color: $oc-white; background-color: oc.$oc-white;
width: auto; width: auto;
height: auto; height: auto;
margin: 0 0.5rem; margin: 0 0.5rem;
@ -132,7 +132,7 @@
} }
.required, .required,
.error { .error {
color: $oc-red-8; color: oc.$oc-red-8;
font-weight: 700; font-weight: 700;
font-size: 1rem; font-size: 1rem;
margin: 0.2rem; margin: 0.2rem;
@ -152,16 +152,16 @@
margin: 0; margin: 0;
} }
.ToolIcon__icon { .ToolIcon__icon {
background-color: $oc-red-6; background-color: oc.$oc-red-6;
&:hover { &:hover {
background-color: $oc-red-7; background-color: oc.$oc-red-7;
} }
&:active { &:active {
background-color: $oc-red-8; background-color: oc.$oc-red-8;
} }
} }
svg { svg {
color: $oc-white; color: oc.$oc-white;
padding: 0.26rem; padding: 0.26rem;
border-radius: 0.3em; border-radius: 0.3em;
width: 1rem; width: 1rem;

View file

@ -1,4 +1,4 @@
@import "../css/variables.module.scss"; @use "../css/styles" as *;
.excalidraw { .excalidraw {
--RadioGroup-background: var(--island-bg-color); --RadioGroup-background: var(--island-bg-color);

View file

@ -1,4 +1,4 @@
@import "../css/variables.module.scss"; @use "../css/styles" as *;
.excalidraw { .excalidraw {
--slider-thumb-size: 16px; --slider-thumb-size: 16px;

View file

@ -1,4 +1,4 @@
@import "../css/variables.module.scss"; @use "../css/styles" as *;
.excalidraw { .excalidraw {
.SVGLayer { .SVGLayer {

View file

@ -1,4 +1,4 @@
@import "open-color/open-color"; @use "../css/styles" as *;
.excalidraw { .excalidraw {
.layer-ui__search { .layer-ui__search {

View file

@ -1,4 +1,4 @@
@import "../css/variables.module.scss"; @use "../css/styles" as *;
.excalidraw { .excalidraw {
.ShareableLinkDialog { .ShareableLinkDialog {

View file

@ -1,5 +1,5 @@
@import "open-color/open-color"; @use "open-color/open-color.scss" as oc;
@import "../../css/variables.module.scss"; @use "../../css/styles" as *;
.excalidraw { .excalidraw {
.sidebar { .sidebar {

View file

@ -1,4 +1,4 @@
@import "../../css/variables.module.scss"; @use "../../css/styles" as *;
.excalidraw { .excalidraw {
.sidebar-trigger { .sidebar-trigger {

View file

@ -1,6 +1,4 @@
@import "open-color/open-color.scss"; @use "../css/styles" as *;
$duration: 1.6s;
.excalidraw { .excalidraw {
.Spinner { .Spinner {

View file

@ -1,4 +1,4 @@
@import "../css/variables.module.scss"; @use "../css/styles" as *;
.excalidraw { .excalidraw {
--Switch-disabled-color: var(--color-border-outline); --Switch-disabled-color: var(--color-border-outline);

View file

@ -1,4 +1,4 @@
@import "../../css/variables.module.scss"; @use "../../css/styles" as *;
$verticalBreakpoint: 861px; $verticalBreakpoint: 861px;
@ -241,7 +241,7 @@ $verticalBreakpoint: 861px;
height: 2.5rem; height: 2.5rem;
font-size: 12px; font-size: 12px;
color: $oc-white; color: oc.$oc-white;
background-color: var(--color-primary); background-color: var(--color-primary);
width: 100%; width: 100%;

View file

@ -1,4 +1,4 @@
@import "../css/variables.module.scss"; @use "../css/styles" as *;
.excalidraw { .excalidraw {
--ExcTextField--color: var(--color-on-surface); --ExcTextField--color: var(--color-on-surface);

View file

@ -1,4 +1,4 @@
@import "../css/variables.module.scss"; @use "../css/styles" as *;
.excalidraw { .excalidraw {
.TextInput { .TextInput {

View file

@ -1,4 +1,4 @@
@import "../css/variables.module.scss"; @use "../css/styles" as *;
.excalidraw { .excalidraw {
.Toast { .Toast {

View file

@ -1,5 +1,5 @@
@import "open-color/open-color.scss"; @use "open-color/open-color.scss" as oc;
@import "../css/variables.module.scss"; @use "../css/styles" as *;
.excalidraw { .excalidraw {
.ToolIcon { .ToolIcon {

View file

@ -1,5 +1,5 @@
@import "open-color/open-color.scss"; @use "open-color/open-color.scss" as oc;
@import "../css/variables.module.scss"; @use "../css/styles" as *;
.excalidraw { .excalidraw {
.App-toolbar { .App-toolbar {

View file

@ -1,4 +1,4 @@
@import "../css/variables.module.scss"; @use "../css/styles" as *;
// container in body where the actual tooltip is appended to // container in body where the actual tooltip is appended to
.excalidraw-tooltip { .excalidraw-tooltip {
@ -14,13 +14,13 @@
pointer-events: none; pointer-events: none;
word-wrap: break-word; word-wrap: break-word;
background: $oc-black; background: oc.$oc-black;
line-height: 1.5; line-height: 1.5;
text-align: center; text-align: center;
font-size: 13px; font-size: 13px;
font-weight: 500; font-weight: 500;
color: $oc-white; color: oc.$oc-white;
display: none; display: none;

View file

@ -1,4 +1,4 @@
@import "../css/variables.module.scss"; @use "../css/styles" as *;
.excalidraw { .excalidraw {
--avatar-size: 1.75rem; --avatar-size: 1.75rem;

View file

@ -1,4 +1,4 @@
@import "../../css/variables.module.scss"; @use "../../css/styles" as *;
.excalidraw { .excalidraw {
.dropdown-menu { .dropdown-menu {

View file

@ -1,4 +1,4 @@
@import "../../css/variables.module.scss"; @use "../../css/styles" as *;
.excalidraw-hyperlinkContainer { .excalidraw-hyperlinkContainer {
display: flex; display: flex;

View file

@ -1,4 +1,4 @@
@import "../../css/variables.module.scss"; @use "../../css/styles" as styles;
.excalidraw { .excalidraw {
.collab-button { .collab-button {

View file

@ -1,4 +1,5 @@
@import "open-color/open-color.scss"; @use "open-color/open-color.scss" as oc;
@use "./styles" as *;
.visually-hidden { .visually-hidden {
position: absolute !important; position: absolute !important;

View file

@ -1,5 +1,10 @@
@import "./variables.module.scss"; @use "open-color/open-color.scss" as oc;
@import "./theme"; @forward "open-color/open-color.scss" as oc-* with (
$oc-gray-2: oc.$oc-gray-2,
$oc-red-1: oc.$oc-red-1,
$oc-red-9: oc.$oc-red-9
);
@forward "./_variables.scss" as *;
:root { :root {
--zIndex-canvas: 1; --zIndex-canvas: 1;
@ -201,7 +206,7 @@ body.excalidraw-cursor-resize * {
.divider { .divider {
width: 1px; width: 1px;
background-color: $oc-gray-2; background-color: oc.$oc-gray-2;
margin: 1px; margin: 1px;
} }
@ -227,7 +232,7 @@ body.excalidraw-cursor-resize * {
label, label,
button, button,
.zIndexButton { .zIndexButton {
@include outlineButtonIconStyles; @include variables.outlineButtonIconStyles;
} }
} }
@ -469,8 +474,8 @@ body.excalidraw-cursor-resize * {
} }
.help-icon { .help-icon {
@include outlineButtonStyles; @include variables.outlineButtonStyles;
@include filledButtonOnCanvas; @include variables.filledButtonOnCanvas;
width: var(--lg-button-size); width: var(--lg-button-size);
height: var(--lg-button-size); height: var(--lg-button-size);
@ -503,7 +508,7 @@ body.excalidraw-cursor-resize * {
margin-inline-start: 0.6em; margin-inline-start: 0.6em;
} }
@include isMobile { @include variables.isMobile {
aside { aside {
display: none; display: none;
} }
@ -652,7 +657,7 @@ body.excalidraw-cursor-resize * {
} }
.main-menu-trigger { .main-menu-trigger {
@include filledButtonOnCanvas; @include variables.filledButtonOnCanvas;
} }
.App-mobile-menu, .App-mobile-menu,
@ -705,8 +710,8 @@ body.excalidraw-cursor-resize * {
justify-content: center; justify-content: center;
padding: 40px; padding: 40px;
background-color: $oc-red-1; background-color: oc.$oc-red-1;
border: 3px solid $oc-red-9; border: 3px solid oc.$oc-red-9;
} }
.ErrorSplash-paragraph { .ErrorSplash-paragraph {

View file

@ -1,33 +1,34 @@
@import "open-color/open-color.scss"; @use "sass:color";
@import "./variables.module.scss"; @use "open-color/open-color.scss";
@use "variables.module.scss";
.excalidraw { .excalidraw {
--theme-filter: none; --theme-filter: none;
--button-destructive-bg-color: #{$oc-red-1}; --button-destructive-bg-color: #{open-color.$oc-red-1};
--button-destructive-color: #{$oc-red-9}; --button-destructive-color: #{open-color.$oc-red-9};
--button-gray-1: #{$oc-gray-2}; --button-gray-1: #{open-color.$oc-gray-2};
--button-gray-2: #{$oc-gray-4}; --button-gray-2: #{open-color.$oc-gray-4};
--button-gray-3: #{$oc-gray-5}; --button-gray-3: #{open-color.$oc-gray-5};
--button-special-active-bg-color: #{$oc-green-0}; --button-special-active-bg-color: #{open-color.$oc-green-0};
--dialog-border-color: var(--color-gray-20); --dialog-border-color: var(--color-gray-20);
--dropdown-icon: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="292.4" height="292.4" viewBox="0 0 292 292"><path d="M287 197L159 69c-4-3-8-5-13-5s-9 2-13 5L5 197c-3 4-5 8-5 13s2 9 5 13c4 4 8 5 13 5h256c5 0 9-1 13-5s5-8 5-13-1-9-5-13z"/></svg>'); --dropdown-icon: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="292.4" height="292.4" viewBox="0 0 292 292"><path d="M287 197L159 69c-4-3-8-5-13-5s-9 2-13 5L5 197c-3 4-5 8-5 13s2 9 5 13c4 4 8 5 13 5h256c5 0 9-1 13-5s5-8 5-13-1-9-5-13z"/></svg>');
--focus-highlight-color: #{$oc-blue-2}; --focus-highlight-color: #{open-color.$oc-blue-2};
--icon-fill-color: var(--color-on-surface); --icon-fill-color: var(--color-on-surface);
--icon-green-fill-color: #{$oc-green-9}; --icon-green-fill-color: #{open-color.$oc-green-9};
--default-bg-color: #{$oc-white}; --default-bg-color: #{open-color.$oc-white};
--input-bg-color: #{$oc-white}; --input-bg-color: #{open-color.$oc-white};
--input-border-color: #{$oc-gray-4}; --input-border-color: #{open-color.$oc-gray-4};
--input-hover-bg-color: #{$oc-gray-1}; --input-hover-bg-color: #{open-color.$oc-gray-1};
--input-label-color: #{$oc-gray-7}; --input-label-color: #{open-color.$oc-gray-7};
--island-bg-color: #ffffff; --island-bg-color: #ffffff;
--keybinding-color: var(--color-gray-40); --keybinding-color: var(--color-gray-40);
--link-color: #{$oc-blue-7}; --link-color: #{open-color.$oc-blue-7};
--overlay-bg-color: #{transparentize($oc-white, 0.12)}; --overlay-bg-color: #{color.adjust(open-color.$oc-white, $alpha: -0.12)};
--popup-bg-color: var(--island-bg-color); --popup-bg-color: var(--island-bg-color);
--popup-secondary-bg-color: #{$oc-gray-1}; --popup-secondary-bg-color: #{open-color.$oc-gray-1};
--popup-text-color: #{$oc-black}; --popup-text-color: #{open-color.$oc-black};
--popup-text-inverted-color: #{$oc-white}; --popup-text-inverted-color: #{open-color.$oc-white};
--select-highlight-color: #{$oc-blue-5}; --select-highlight-color: #{open-color.$oc-blue-5};
--shadow-island: 0px 0px 0.9310142993927002px 0px rgba(0, 0, 0, 0.17), --shadow-island: 0px 0px 0.9310142993927002px 0px rgba(0, 0, 0, 0.17),
0px 0px 3.1270833015441895px 0px rgba(0, 0, 0, 0.08), 0px 0px 3.1270833015441895px 0px rgba(0, 0, 0, 0.08),
0px 7px 14px 0px rgba(0, 0, 0, 0.05); 0px 7px 14px 0px rgba(0, 0, 0, 0.05);
@ -80,7 +81,7 @@
--color-selection: #6965db; --color-selection: #6965db;
--color-icon-white: #{$oc-white}; --color-icon-white: #{open-color.$oc-white};
--color-primary: #6965db; --color-primary: #6965db;
--color-primary-darker: #5b57d1; --color-primary-darker: #5b57d1;
@ -172,7 +173,7 @@
&.theme--dark { &.theme--dark {
--theme-filter: invert(93%) hue-rotate(180deg); --theme-filter: invert(93%) hue-rotate(180deg);
--button-destructive-bg-color: #5a0000; --button-destructive-bg-color: #5a0000;
--button-destructive-color: #{$oc-red-3}; --button-destructive-color: #{open-color.$oc-red-3};
--button-gray-1: #363636; --button-gray-1: #363636;
--button-gray-2: #272727; --button-gray-2: #272727;
@ -180,21 +181,21 @@
--button-special-active-bg-color: #204624; --button-special-active-bg-color: #204624;
--dialog-border-color: var(--color-gray-80); --dialog-border-color: var(--color-gray-80);
--dropdown-icon: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="292.4" height="292.4" viewBox="0 0 292 292"><path fill="%23ced4da" d="M287 197L159 69c-4-3-8-5-13-5s-9 2-13 5L5 197c-3 4-5 8-5 13s2 9 5 13c4 4 8 5 13 5h256c5 0 9-1 13-5s5-8 5-13-1-9-5-13z"/></svg>'); --dropdown-icon: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="292.4" height="292.4" viewBox="0 0 292 292"><path fill="%23ced4da" d="M287 197L159 69c-4-3-8-5-13-5s-9 2-13 5L5 197c-3 4-5 8-5 13s2 9 5 13c4 4 8 5 13 5h256c5 0 9-1 13-5s5-8 5-13-1-9-5-13z"/></svg>');
--focus-highlight-color: #{$oc-blue-6}; --focus-highlight-color: #{open-color.$oc-blue-6};
--icon-green-fill-color: #{$oc-green-4}; --icon-green-fill-color: #{open-color.$oc-green-4};
--default-bg-color: #121212; --default-bg-color: #121212;
--input-bg-color: #121212; --input-bg-color: #121212;
--input-border-color: #2e2e2e; --input-border-color: #2e2e2e;
--input-hover-bg-color: #181818; --input-hover-bg-color: #181818;
--input-label-color: #{$oc-gray-2}; --input-label-color: #{open-color.$oc-gray-2};
--island-bg-color: #232329; --island-bg-color: #232329;
--keybinding-color: var(--color-gray-60); --keybinding-color: var(--color-gray-60);
--link-color: #{$oc-blue-4}; --link-color: #{open-color.$oc-blue-4};
--overlay-bg-color: #{transparentize($oc-gray-8, 0.88)}; --overlay-bg-color: #{color.adjust(open-color.$oc-gray-8, $alpha: -0.88)};
--popup-secondary-bg-color: #222; --popup-secondary-bg-color: #222;
--popup-text-color: #{$oc-gray-4}; --popup-text-color: #{open-color.$oc-gray-4};
--popup-text-inverted-color: #2c2c2c; --popup-text-inverted-color: #2c2c2c;
--select-highlight-color: #{$oc-blue-4}; --select-highlight-color: #{open-color.$oc-blue-4};
--shadow-island: 0px 0px 0.9310142993927002px 0px rgba(0, 0, 0, 0.17), --shadow-island: 0px 0px 0.9310142993927002px 0px rgba(0, 0, 0, 0.17),
0px 0px 3.1270833015441895px 0px rgba(0, 0, 0, 0.08), 0px 0px 3.1270833015441895px 0px rgba(0, 0, 0, 0.08),
0px 7px 14px 0px rgba(0, 0, 0, 0.05); 0px 7px 14px 0px rgba(0, 0, 0, 0.05);
@ -207,8 +208,8 @@
0px 2.76726px 2.21381px rgba(0, 0, 0, 0.0196802); 0px 2.76726px 2.21381px rgba(0, 0, 0, 0.0196802);
--avatar-border-color: var(--color-gray-85); --avatar-border-color: var(--color-gray-85);
--scrollbar-thumb: #{$oc-gray-8}; --scrollbar-thumb: #{open-color.$oc-gray-8};
--scrollbar-thumb-hover: #{$oc-gray-7}; --scrollbar-thumb-hover: #{open-color.$oc-gray-7};
--color-slider-track: hsl(244, 23%, 39%); --color-slider-track: hsl(244, 23%, 39%);

View file

@ -1,190 +1,79 @@
@import "open-color/open-color.scss"; :root {
--color-primary: #6965db;
@mixin isMobile() { --color-primary-darker: #5653c0;
@at-root .excalidraw--mobile#{&} { --color-primary-darkest: #4c44b4;
@content; --color-primary-hover: #7a75d3;
} --color-primary-light: #8c7cd5;
--color-primary-lighter: #a29be8;
--color-primary-lightest: #c3c0f9;
--color-on-primary: #ffffff;
--color-on-primary-container: #000000;
--color-surface-lowest: #f7f7f7;
--color-surface-low: #f2f2f2;
--color-surface: #ffffff;
--color-surface-high: #f9f9f9;
--color-surface-highest: #f7f7f7;
--color-surface-primary-container: #4c44b4;
--color-disabled: #c3c0f9;
--color-gray-90: #333333;
--color-on-surface: #333333;
--color-on-surface-high: #333333;
--color-on-surface-highest: #333333;
--color-on-surface-lowest: #333333;
--color-on-surface-low: #333333;
--color-on-surface-inverse: #ffffff;
--color-on-surface-inverse-high: #ffffff;
--color-on-surface-inverse-highest: #ffffff;
--color-on-surface-inverse-lowest: #ffffff;
--color-on-surface-inverse-low: #ffffff;
--color-on-surface-inverse-disabled: #c3c0f9;
--color-on-surface-disabled: #c3c0f9;
--color-on-surface-high-disabled: #c3c0f9;
--color-on-surface-highest-disabled: #c3c0f9;
--color-on-surface-lowest-disabled: #c3c0f9;
--color-on-surface-low-disabled: #c3c0f9;
--color-on-surface-inverse-disabled: #c3c0f9;
--color-on-surface-inverse-high-disabled: #c3c0f9;
--color-on-surface-inverse-highest-disabled: #c3c0f9;
--color-on-surface-inverse-lowest-disabled: #c3c0f9;
--color-on-surface-inverse-low-disabled: #c3c0f9;
--color-on-surface-disabled: #c3c0f9;
--color-on-surface-high-disabled: #c3c0f9;
--color-on-surface-highest-disabled: #c3c0f9;
--color-on-surface-lowest-disabled: #c3c0f9;
--color-on-surface-low-disabled: #c3c0f9;
--color-on-surface-inverse-disabled: #c3c0f9;
--color-on-surface-inverse-high-disabled: #c3c0f9;
--color-on-surface-inverse-highest-disabled: #c3c0f9;
--color-on-surface-inverse-lowest-disabled: #c3c0f9;
--color-on-surface-inverse-low-disabled: #c3c0f9;
--button-width: 2.5rem;
--button-height: 2.5rem;
--default-button-size: 2.5rem;
--default-icon-size: 1.5rem;
--lg-icon-size: 2rem;
--avatar-size: 1.5rem;
--border-radius-lg: 0.5rem;
--island-bg-color: #ffffff;
--text-primary-color: #333333;
--button-bg: #ffffff;
--button-color: #333333;
--button-border: #cccccc;
--button-hover-bg: #f7f7f7;
--button-hover-color: #333333;
--button-hover-border: #cccccc;
--button-active-bg: #f2f2f2;
--button-active-color: #333333;
--button-active-border: #cccccc;
--button-selected-bg: #4c44b4;
--button-selected-color: #ffffff;
--button-selected-border: #4c44b4;
--button-selected-hover-bg: #5653c0;
--button-selected-hover-color: #ffffff;
--button-selected-hover-border: #5653c0;
--ui-font: Arial, sans-serif;
} }
@mixin toolbarButtonColorStates { // Theme variables
&.fillable { $theme-filter: none !default;
.ToolIcon_type_radio, $theme-filter-darker: none !default;
.ToolIcon_type_checkbox {
&:checked + .ToolIcon__icon {
--icon-fill-color: var(--color-on-primary-container);
svg {
fill: var(--icon-fill-color);
}
}
}
}
.ToolIcon_type_radio,
.ToolIcon_type_checkbox {
&:checked + .ToolIcon__icon {
background: var(--color-surface-primary-container);
--keybinding-color: var(--color-on-primary-container);
svg {
color: var(--color-on-primary-container);
}
}
}
.ToolIcon__keybinding {
bottom: 4px;
right: 4px;
}
.ToolIcon__icon {
&:hover {
background: var(--button-hover-bg);
}
&:active {
background: var(--button-hover-bg);
border: 1px solid var(--button-active-border);
svg {
color: var(--color-on-primary-container);
}
}
&[aria-disabled="true"] {
background: initial;
border: none;
svg {
color: var(--color-disabled);
}
}
}
}
@mixin outlineButtonStyles {
display: flex;
justify-content: center;
align-items: center;
padding: 0.625rem;
width: var(--button-width, var(--default-button-size));
height: var(--button-height, var(--default-button-size));
box-sizing: border-box;
border-width: 1px;
border-style: solid;
border-color: var(--button-border, var(--default-border-color));
border-radius: var(--border-radius-lg);
cursor: pointer;
background-color: var(--button-bg, var(--island-bg-color));
color: var(--button-color, var(--color-on-surface));
font-family: var(--ui-font);
svg {
width: var(--button-width, var(--lg-icon-size));
height: var(--button-height, var(--lg-icon-size));
}
&:hover {
background-color: var(--button-hover-bg, var(--island-bg-color));
border-color: var(
--button-hover-border,
var(--button-border, var(--default-border-color))
);
color: var(
--button-hover-color,
var(--button-color, var(--text-primary-color, inherit))
);
}
&:active {
background-color: var(--button-active-bg, var(--island-bg-color));
border-color: var(--button-active-border, var(--color-primary-darkest));
}
&.active {
background-color: var(
--button-selected-bg,
var(--color-surface-primary-container)
);
border-color: var(
--button-selected-border,
var(--color-surface-primary-container)
);
&:hover {
background-color: var(
--button-selected-hover-bg,
var(--color-surface-primary-container)
);
}
svg {
color: var(--button-color, var(--color-on-primary-container));
}
}
}
@mixin outlineButtonIconStyles {
@include outlineButtonStyles;
padding: 0;
svg {
width: var(--default-icon-size);
height: var(--default-icon-size);
}
}
@mixin avatarStyles {
width: var(--avatar-size, 1.5rem);
height: var(--avatar-size, 1.5rem);
position: relative;
border-radius: 100%;
outline-offset: 2px;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
font-size: 0.75rem;
font-weight: 700;
line-height: 1;
color: var(--color-gray-90);
flex: 0 0 auto;
&:active {
transform: scale(0.94);
}
&-img {
width: 100%;
height: 100%;
border-radius: 100%;
}
&::before {
content: "";
position: absolute;
top: -3px;
right: -3px;
bottom: -3px;
left: -3px;
border-radius: 100%;
}
&.is-followed::before {
border-color: var(--color-primary-hover);
box-shadow: 0 0 0 1px var(--color-primary-hover);
}
&.is-current-user {
cursor: auto;
}
}
@mixin filledButtonOnCanvas {
border: none;
box-shadow: 0 0 0 1px var(--color-surface-lowest);
background-color: var(--color-surface-low);
&:active {
box-shadow: 0 0 0 1px var(--color-brand-active);
}
}

View file

@ -87,8 +87,8 @@
"image-blob-reduce": "3.0.1", "image-blob-reduce": "3.0.1",
"jotai": "2.11.0", "jotai": "2.11.0",
"jotai-scope": "0.7.2", "jotai-scope": "0.7.2",
"lodash.throttle": "4.1.1",
"lodash.debounce": "4.0.8", "lodash.debounce": "4.0.8",
"lodash.throttle": "4.1.1",
"nanoid": "3.3.3", "nanoid": "3.3.3",
"open-color": "1.9.1", "open-color": "1.9.1",
"pako": "2.0.3", "pako": "2.0.3",
@ -100,7 +100,7 @@
"points-on-curve": "1.0.1", "points-on-curve": "1.0.1",
"pwacompat": "2.0.17", "pwacompat": "2.0.17",
"roughjs": "4.6.4", "roughjs": "4.6.4",
"sass": "1.51.0", "sass": "^1.71.1",
"tunnel-rat": "0.1.2" "tunnel-rat": "0.1.2"
}, },
"devDependencies": { "devDependencies": {
@ -116,20 +116,24 @@
"autoprefixer": "10.4.7", "autoprefixer": "10.4.7",
"cross-env": "7.0.3", "cross-env": "7.0.3",
"dotenv": "16.0.1", "dotenv": "16.0.1",
"esbuild": "0.19.10", "esbuild": "0.25.2",
"esbuild-sass-plugin": "2.16.0", "esbuild-sass-plugin": "3.3.1",
"eslint-plugin-react": "7.32.2", "eslint-plugin-react": "7.32.2",
"fake-indexeddb": "3.1.7", "fake-indexeddb": "3.1.7",
"fonteditor-core": "2.4.1", "fonteditor-core": "2.4.1",
"harfbuzzjs": "0.3.6", "harfbuzzjs": "0.3.6",
"jest-diff": "29.7.0", "jest-diff": "29.7.0",
"sass-embedded": "1.86.3",
"typescript": "4.9.4" "typescript": "4.9.4"
}, },
"repository": "https://github.com/excalidraw/excalidraw", "repository": "https://github.com/excalidraw/excalidraw",
"bugs": "https://github.com/excalidraw/excalidraw/issues", "bugs": "https://github.com/excalidraw/excalidraw/issues",
"homepage": "https://github.com/excalidraw/excalidraw/tree/master/packages/excalidraw", "homepage": "https://github.com/excalidraw/excalidraw/tree/master/packages/excalidraw",
"scripts": { "scripts": {
"gen:types": "rm -rf types && tsc", "gen:types": "if exist types rmdir /s /q types && tsc",
"build:esm": "rm -rf dist && node ../../scripts/buildPackage.js && yarn gen:types" "build:esm": "if exist dist rmdir /s /q dist && node ../../scripts/buildPackage.mjs && yarn gen:types",
"build": "vite build",
"dev": "vite",
"preview": "vite preview"
} }
} }

View file

@ -1,7 +1,9 @@
{ {
"extends": "../tsconfig.base.json", "extends": "../tsconfig.base.json",
"compilerOptions": { "compilerOptions": {
"outDir": "./dist/types" "noEmit": true,
"skipLibCheck": true,
"incremental": false
}, },
"include": ["**/*"], "include": ["**/*"],
"exclude": ["**/*.test.*", "tests", "types", "examples", "dist"] "exclude": ["**/*.test.*", "tests", "types", "examples", "dist"]

View file

@ -0,0 +1,22 @@
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { fileURLToPath } from 'node:url';
export default defineConfig({
build: {
lib: {
entry: fileURLToPath(new URL('./index.tsx', import.meta.url)),
name: 'Excalidraw',
fileName: (format) => `excalidraw.${format}.js`
},
rollupOptions: {
external: ['react', 'react-dom'],
output: {
globals: {
react: 'React',
'react-dom': 'ReactDOM'
}
}
}
}
});

View file

@ -3,14 +3,14 @@
"version": "0.1.0", "version": "0.1.0",
"type": "module", "type": "module",
"types": "./dist/types/math/src/index.d.ts", "types": "./dist/types/math/src/index.d.ts",
"main": "./dist/prod/index.js", "main": "./dist/math.umd.js",
"module": "./dist/prod/index.js", "module": "./dist/math.es.js",
"exports": { "exports": {
".": { ".": {
"types": "./dist/types/math/src/index.d.ts", "types": "./dist/types/math/src/index.d.ts",
"development": "./dist/dev/index.js", "development": "./dist/math.es.js",
"production": "./dist/prod/index.js", "production": "./dist/math.umd.js",
"default": "./dist/prod/index.js" "default": "./dist/math.umd.js"
}, },
"./*": { "./*": {
"types": "./../math/dist/types/math/src/*.d.ts" "types": "./../math/dist/types/math/src/*.d.ts"
@ -55,6 +55,11 @@
"repository": "https://github.com/excalidraw/excalidraw", "repository": "https://github.com/excalidraw/excalidraw",
"scripts": { "scripts": {
"gen:types": "rm -rf types && tsc", "gen:types": "rm -rf types && tsc",
"build:esm": "rm -rf dist && node ../../scripts/buildBase.js && yarn gen:types" "build:esm": "rm -rf dist && node ../../scripts/buildBase.js && yarn gen:types",
"build": "vite build",
"test": "jest",
"lint": "eslint . --ext .ts,.tsx",
"prettier": "prettier --check .",
"prettier:fix": "prettier --write ."
} }
} }

View file

@ -0,0 +1,27 @@
import { defineConfig } from 'vite';
import { fileURLToPath } from 'node:url';
export default defineConfig({
build: {
lib: {
entry: fileURLToPath(new URL('./src/index.ts', import.meta.url)),
name: 'ExcalidrawMath',
fileName: () => 'index.js', // Changed to match package.json
},
outDir: 'dist/prod', // Added to match package.json
rollupOptions: {
external: ['react', 'react-dom'],
output: {
globals: {
react: 'React',
'react-dom': 'ReactDOM'
},
format: 'es' // Still produces ESM; UMD needs separate config if required
},
onwarn: (warning, warn) => {
if (warning.code === 'CIRCULAR_DEPENDENCY') return;
warn(warning);
}
}
}
});

View file

@ -8,12 +8,15 @@
"exports": { "exports": {
".": { ".": {
"types": "./dist/types/utils/src/index.d.ts", "types": "./dist/types/utils/src/index.d.ts",
"development": "./dist/dev/index.js", "import": "./dist/prod/index.js",
"production": "./dist/prod/index.js", "require": "./dist/prod/index.js",
"default": "./dist/prod/index.js" "default": "./dist/prod/index.js"
}, },
"./*": { "./export": {
"types": "./../utils/dist/types/utils/src/*.d.ts" "types": "./dist/types/utils/src/export.d.ts",
"import": "./dist/prod/index.js",
"require": "./dist/prod/index.js",
"default": "./dist/prod/index.js"
} }
}, },
"files": [ "files": [
@ -61,6 +64,8 @@
}, },
"devDependencies": { "devDependencies": {
"cross-env": "7.0.3", "cross-env": "7.0.3",
"esbuild": "0.25.2",
"esbuild-sass-plugin": "3.3.1",
"fonteditor-core": "2.4.0", "fonteditor-core": "2.4.0",
"typescript": "4.9.4", "typescript": "4.9.4",
"wawoff2": "2.0.1", "wawoff2": "2.0.1",
@ -69,7 +74,7 @@
"bugs": "https://github.com/excalidraw/excalidraw/issues", "bugs": "https://github.com/excalidraw/excalidraw/issues",
"repository": "https://github.com/excalidraw/excalidraw", "repository": "https://github.com/excalidraw/excalidraw",
"scripts": { "scripts": {
"gen:types": "rm -rf types && tsc", "gen:types": "if exist types rmdir /s /q types && tsc",
"build:esm": "rm -rf dist && node ../../scripts/buildUtils.js && yarn gen:types" "build:esm": "if exist dist rmdir /s /q dist && node ../../scripts/buildUtils.mjs && yarn gen:types"
} }
} }

View file

@ -2,3 +2,6 @@ export * from "./export";
export * from "./withinBounds"; export * from "./withinBounds";
export * from "./bbox"; export * from "./bbox";
export { getCommonBounds } from "@excalidraw/element/bounds"; export { getCommonBounds } from "@excalidraw/element/bounds";
export * from "./collision";
export * from "./shape";
export * from "./test-utils";

1
public/excalidraw.css Normal file

File diff suppressed because one or more lines are too long

12
public/index.html Normal file
View file

@ -0,0 +1,12 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Excalidraw Fork</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/index.tsx"></script>
</body>
</html>

View file

@ -1,8 +1,8 @@
const path = require("path"); const path = require("path");
const { build } = require("esbuild"); const { build } = require("esbuild");
// contains all dependencies bundled inside console.log("Starting build...");
const getConfig = (outdir) => ({ const getConfig = (outdir) => ({
outdir, outdir,
bundle: true, bundle: true,
@ -20,6 +20,7 @@ const getConfig = (outdir) => ({
}); });
function buildDev(config) { function buildDev(config) {
console.log("Building dev...");
return build({ return build({
...config, ...config,
sourcemap: true, sourcemap: true,
@ -30,6 +31,7 @@ function buildDev(config) {
} }
function buildProd(config) { function buildProd(config) {
console.log("Building prod...");
return build({ return build({
...config, ...config,
minify: true, minify: true,
@ -40,11 +42,9 @@ function buildProd(config) {
} }
const createESMRawBuild = async () => { const createESMRawBuild = async () => {
// development unminified build with source maps
await buildDev(getConfig("dist/dev")); await buildDev(getConfig("dist/dev"));
// production minified build without sourcemaps
await buildProd(getConfig("dist/prod")); await buildProd(getConfig("dist/prod"));
console.log("Build complete.");
}; };
(async () => { (async () => {

View file

@ -1,83 +0,0 @@
const path = require("path");
const { build } = require("esbuild");
const { sassPlugin } = require("esbuild-sass-plugin");
const { parseEnvVariables } = require("../packages/excalidraw/env.cjs");
const ENV_VARS = {
development: {
...parseEnvVariables(`${__dirname}/../.env.development`),
DEV: true,
},
production: {
...parseEnvVariables(`${__dirname}/../.env.production`),
PROD: true,
},
};
// excludes all external dependencies and bundles only the source code
const getConfig = (outdir) => ({
outdir,
bundle: true,
splitting: true,
format: "esm",
packages: "external",
plugins: [sassPlugin()],
target: "es2020",
assetNames: "[dir]/[name]",
chunkNames: "[dir]/[name]-[hash]",
alias: {
"@excalidraw/common": path.resolve(__dirname, "../packages/common/src"),
"@excalidraw/element": path.resolve(__dirname, "../packages/element/src"),
"@excalidraw/excalidraw": path.resolve(__dirname, "../packages/excalidraw"),
"@excalidraw/math": path.resolve(__dirname, "../packages/math/src"),
"@excalidraw/utils": path.resolve(__dirname, "../packages/utils/src"),
},
loader: {
".woff2": "file",
},
});
function buildDev(config) {
return build({
...config,
sourcemap: true,
define: {
"import.meta.env": JSON.stringify(ENV_VARS.development),
},
});
}
function buildProd(config) {
return build({
...config,
minify: true,
define: {
"import.meta.env": JSON.stringify(ENV_VARS.production),
},
});
}
const createESMRawBuild = async () => {
const chunksConfig = {
entryPoints: ["index.tsx", "**/*.chunk.ts"],
entryNames: "[name]",
};
// development unminified build with source maps
await buildDev({
...getConfig("dist/dev"),
...chunksConfig,
});
// production minified buld without sourcemaps
await buildProd({
...getConfig("dist/prod"),
...chunksConfig,
});
};
(async () => {
await createESMRawBuild();
})();

164
scripts/buildPackage.mjs Normal file
View file

@ -0,0 +1,164 @@
import { build } from "esbuild";
import { sassPlugin } from "esbuild-sass-plugin";
import { fileURLToPath } from "url";
import path from "path";
import fs from "fs";
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const parseEnvVariables = (filePath) => {
try {
const envContent = fs.readFileSync(filePath, "utf8");
const envVars = {};
envContent.split("\n").forEach((line) => {
const [key, value] = line.split("=");
if (key && value) envVars[key.trim()] = value.trim();
});
return envVars;
} catch {
return {};
}
};
const ENV_VARS = {
development: {
...parseEnvVariables(path.join(__dirname, "../.env.development")),
DEV: true,
},
production: {
...parseEnvVariables(path.join(__dirname, "../.env.production")),
PROD: true,
},
};
const getConfig = (outdir) => ({
outdir,
bundle: true,
splitting: true,
format: "esm",
target: "es2020",
plugins: [
sassPlugin({
loadPaths: [
path.resolve(__dirname, "../node_modules"),
path.resolve(__dirname, "../node_modules/open-color"),
path.resolve(__dirname, "../packages/excalidraw/css"),
path.resolve(__dirname, "../packages/excalidraw/src"),
path.resolve(__dirname, "../packages/excalidraw/components"),
],
precompile: (source, pathname) => {
// Skip precompile for open-color and css files to avoid loops
if (
pathname.includes("node_modules/open-color") ||
pathname.includes("css/variables.module.scss") ||
pathname.includes("css/styles.scss") ||
pathname.includes("css/theme.scss")
) {
return source;
}
if (pathname.endsWith(".scss")) {
return `
@use '../css/variables.module.scss' as vars;
@use '../css/styles.scss' as styles;
${source}
`;
}
return source;
},
type: "css",
sourceMap: false,
logger: {
warn: () => {},
error: (msg) => {
console.error("Sass compilation error:", msg);
throw new Error(msg);
},
},
}),
],
assetNames: "[dir]/[name]",
chunkNames: "[dir]/[name]-[hash]",
alias: {
"@excalidraw/common": path.resolve(__dirname, "../packages/common/src"),
"@excalidraw/element": path.resolve(__dirname, "../packages/element/src"),
"@excalidraw/excalidraw": path.resolve(__dirname, "../packages/excalidraw"),
"@excalidraw/math": path.resolve(__dirname, "../packages/math/src"),
"@excalidraw/utils": path.resolve(__dirname, "../packages/utils/src"),
},
loader: { ".woff2": "file" },
});
async function buildDev(config) {
console.log("Starting dev build to", config.outdir);
try {
await build({
...config,
sourcemap: true,
define: { "import.meta.env": JSON.stringify(ENV_VARS.development) },
});
console.log("Dev build completed successfully");
} catch (err) {
console.error("Dev build failed:", err);
throw err;
}
}
async function buildProd(config) {
console.log("Starting prod build to", config.outdir);
try {
await build({
...config,
minify: true,
define: { "import.meta.env": JSON.stringify(ENV_VARS.production) },
});
console.log("Prod build completed successfully");
} catch (err) {
console.error("Prod build failed:", err);
throw err;
}
}
const createESMRawBuild = async () => {
console.log("Creating ESM raw build...");
const prodDir = path.resolve(__dirname, "../packages/excalidraw/dist/prod");
const devDir = path.resolve(__dirname, "../packages/excalidraw/dist/dev");
if (!fs.existsSync(prodDir)) {
fs.mkdirSync(prodDir, { recursive: true });
}
if (!fs.existsSync(devDir)) {
fs.mkdirSync(devDir, { recursive: true });
}
const chunksConfig = {
entryPoints: [
path.resolve(__dirname, "../packages/excalidraw/index.tsx"),
...fs
.readdirSync(path.resolve(__dirname, "../packages/excalidraw"), { recursive: true })
.filter((file) => file.endsWith(".chunk.ts"))
.map((file) => path.resolve(__dirname, "../packages/excalidraw", file)),
],
entryNames: "[name]",
};
await buildDev({
...getConfig(path.resolve(__dirname, "../packages/excalidraw/dist/dev")),
...chunksConfig,
});
await buildProd({
...getConfig(path.resolve(__dirname, "../packages/excalidraw/dist/prod")),
...chunksConfig,
});
console.log("ESM raw build finished");
};
(async () => {
console.log("Build script started");
try {
await createESMRawBuild();
console.log("Build script completed successfully");
} catch (err) {
console.error("Build script failed:", err);
process.exit(1);
}
})();

View file

@ -1,62 +0,0 @@
const path = require("path");
const { build } = require("esbuild");
const { sassPlugin } = require("esbuild-sass-plugin");
const { woff2ServerPlugin } = require("./woff2/woff2-esbuild-plugins");
// contains all dependencies bundled inside
const getConfig = (outdir) => ({
outdir,
bundle: true,
format: "esm",
entryPoints: ["src/index.ts"],
entryNames: "[name]",
assetNames: "[dir]/[name]",
alias: {
"@excalidraw/common": path.resolve(__dirname, "../packages/common/src"),
"@excalidraw/element": path.resolve(__dirname, "../packages/element/src"),
"@excalidraw/excalidraw": path.resolve(__dirname, "../packages/excalidraw"),
"@excalidraw/math": path.resolve(__dirname, "../packages/math/src"),
"@excalidraw/utils": path.resolve(__dirname, "../packages/utils/src"),
},
});
function buildDev(config) {
return build({
...config,
sourcemap: true,
plugins: [sassPlugin(), woff2ServerPlugin()],
define: {
"import.meta.env": JSON.stringify({ DEV: true }),
},
});
}
function buildProd(config) {
return build({
...config,
minify: true,
plugins: [
sassPlugin(),
woff2ServerPlugin({
outdir: `${config.outdir}/assets`,
}),
],
define: {
"import.meta.env": JSON.stringify({ PROD: true }),
},
});
}
const createESMRawBuild = async () => {
// development unminified build with source maps
await buildDev(getConfig("dist/dev"));
// production minified build without sourcemaps
await buildProd(getConfig("dist/prod"));
};
(async () => {
await createESMRawBuild();
})();

104
scripts/buildUtils.mjs Normal file
View file

@ -0,0 +1,104 @@
import { build } from "esbuild";
import { sassPlugin } from "esbuild-sass-plugin";
import { fileURLToPath } from "url";
import path from "path";
import fs from "fs";
import { woff2ServerPlugin } from "./woff2/woff2-esbuild-plugins.mjs";
const __dirname = path.dirname(fileURLToPath(import.meta.url));
console.log("Starting build (ESM) - DEBUG VERSION...");
const ensureDir = (dir) => {
console.log(`[DEBUG] Ensuring directory: ${dir}`);
try {
if (!fs.existsSync(dir)) {
console.log(`[DEBUG] Creating directory: ${dir}`);
fs.mkdirSync(dir, { recursive: true });
console.log(`[DEBUG] Successfully created directory: ${dir}`);
} else {
console.log(`[DEBUG] Directory already exists: ${dir}`);
}
return true;
} catch (err) {
console.error(`[ERROR] Failed to create directory ${dir}:`, err);
return false;
}
};
const getConfig = (outdir) => {
ensureDir(outdir);
return {
outdir,
bundle: true,
format: "esm",
entryPoints: ["src/index.ts"],
entryNames: "[name]",
assetNames: "[dir]/[name]",
alias: {
"@excalidraw/common": path.resolve(__dirname, "../packages/common/src"),
"@excalidraw/element": path.resolve(__dirname, "../packages/element/src"),
"@excalidraw/excalidraw": path.resolve(__dirname, "../packages/excalidraw"),
"@excalidraw/math": path.resolve(__dirname, "../packages/math/src"),
"@excalidraw/utils": path.resolve(__dirname, "../packages/utils/src"),
},
};
};
async function buildDev(config) {
console.log("Building dev version...");
try {
await build({
...config,
sourcemap: true,
plugins: [sassPlugin(), woff2ServerPlugin()],
define: {
"import.meta.env": JSON.stringify({ DEV: true }),
},
});
console.log("Dev build completed successfully");
} catch (err) {
console.error("Dev build failed:", err);
throw err;
}
}
async function buildProd(config) {
console.log("Building prod version...");
try {
await build({
...config,
minify: true,
plugins: [
sassPlugin(),
woff2ServerPlugin({
outdir: `${config.outdir}/assets`,
}),
],
define: {
"import.meta.env": JSON.stringify({ PROD: true }),
},
});
console.log("Prod build completed successfully");
} catch (err) {
console.error("Prod build failed:", err);
throw err;
}
}
const createESMRawBuild = async () => {
// development unminified build with source maps
await buildDev(getConfig("dist/dev"));
// production minified build without sourcemaps
await buildProd(getConfig("dist/prod"));
console.log("All builds complete.");
};
(async () => {
try {
await createESMRawBuild();
} catch (error) {
console.error("Build failed:", error);
process.exit(1);
}
})();

View file

@ -1,10 +1,7 @@
const { execSync } = require("child_process"); import { createHash } from "crypto";
const fs = require("fs"); import fs from "fs";
const path = require("path"); import path from "path";
import { promisify } from "util";
const { Font } = require("fonteditor-core");
const wawoff = require("wawoff2");
const which = require("which");
/** /**
* Custom esbuild plugin to: * Custom esbuild plugin to:
@ -17,29 +14,27 @@ const which = require("which");
* *
* @returns {import("esbuild").Plugin} * @returns {import("esbuild").Plugin}
*/ */
module.exports.woff2ServerPlugin = (options = {}) => { export const woff2ServerPlugin = (options = {}) => {
return { return {
name: "woff2ServerPlugin", name: "woff2-server-plugin",
setup(build) { setup(build) {
const fonts = new Map(); const fonts = new Map();
build.onResolve({ filter: /\.woff2$/ }, (args) => { build.onResolve({ filter: /\.(woff|woff2)$/ }, (args) => {
const resolvedPath = path.resolve(args.resolveDir, args.path);
return { return {
path: resolvedPath, path: path.isAbsolute(args.path)
namespace: "woff2ServerPlugin", ? args.path
: path.join(path.dirname(args.importer), args.path),
namespace: "woff2-asset",
}; };
}); });
build.onLoad( build.onLoad({ filter: /.*/, namespace: "woff2-asset" }, async (args) => {
{ filter: /.*/, namespace: "woff2ServerPlugin" },
async (args) => {
let woff2Buffer; let woff2Buffer;
if (path.isAbsolute(args.path)) { if (path.isAbsolute(args.path)) {
// read local woff2 as a buffer (WARN: `readFileSync` does not work!) // read local woff2 as a buffer (WARN: `readFileSync` does not work!)
woff2Buffer = await fs.promises.readFile(args.path); woff2Buffer = await promisify(fs.readFile)(args.path);
} else { } else {
throw new Error(`Font path has to be absolute! "${args.path}"`); throw new Error(`Font path has to be absolute! "${args.path}"`);
} }
@ -89,8 +84,7 @@ module.exports.woff2ServerPlugin = (options = {}) => {
)}`, )}`,
loader: "text", loader: "text",
}; };
}, });
);
build.onEnd(async () => { build.onEnd(async () => {
const { outdir } = options; const { outdir } = options;

158
src/Sidebar.tsx Normal file
View file

@ -0,0 +1,158 @@
import React from 'react';
import type { ExcalidrawImperativeAPI } from '@excalidraw/excalidraw/dist/types/excalidraw/types';
import {
FillStyle,
StrokeStyle,
Arrowhead,
ExcalidrawElement,
FractionalIndex,
PointBinding,
ExcalidrawTextElement,
ExcalidrawArrowElement
} from '../packages/element/src/types';
import { Radians, LocalPoint } from '../packages/math/src/types';
import { pointFrom } from '../packages/math/src/point';
interface SidebarProps {
excalidrawAPI: ExcalidrawImperativeAPI | null;
}
const Sidebar: React.FC<SidebarProps> = ({ excalidrawAPI }) => {
const addRectangle = () => {
if (!excalidrawAPI) return;
const elements = [
{
id: 'a1',
type: 'rectangle',
x: 100,
y: 100,
width: 200,
height: 100,
text: '',
strokeColor: '#000000',
backgroundColor: '#ffffff',
fillStyle: 'solid' as FillStyle,
strokeWidth: 1,
strokeStyle: 'solid' as StrokeStyle,
roughness: 0,
opacity: 100,
seed: 1,
angle: 0 as Radians,
startBinding: null,
endBinding: null,
lastCommittedPoint: null,
startArrowhead: null,
endArrowhead: 'arrow',
roundness: null,
version: 1,
versionNonce: 1,
isDeleted: false,
groupIds: [],
frameId: null,
boundElements: [],
updated: Date.now(),
link: null,
locked: false,
index: 'a1' as FractionalIndex
} as ExcalidrawElement,
] as readonly ExcalidrawElement[];
excalidrawAPI.updateScene({ elements });
};
const addTemplate = () => {
if (!excalidrawAPI) return;
const elements = [
{
id: 'text-1',
type: 'text',
x: 150,
y: 150,
width: 100,
height: 30,
text: 'Hello World',
strokeColor: '#000000',
backgroundColor: 'transparent',
fillStyle: 'solid' as FillStyle,
strokeWidth: 1,
strokeStyle: 'solid' as StrokeStyle,
roughness: 1,
opacity: 100,
seed: 1,
angle: 0 as Radians,
originalText: 'Hello World',
lineHeight: 25 as number & { _brand: 'unitlessLineHeight' },
fontSize: 20,
fontFamily: 1,
textAlign: 'left',
verticalAlign: 'top',
containerId: null,
autoResize: true,
roundness: null,
version: 1,
versionNonce: 1,
isDeleted: false,
groupIds: [],
frameId: null,
boundElements: [],
updated: Date.now(),
link: null,
locked: false,
index: 'a0' as FractionalIndex
} as ExcalidrawTextElement,
{
id: 'arrow-1',
type: 'arrow',
x: 100,
y: 100,
width: 100,
height: 100,
strokeColor: '#000000',
backgroundColor: 'transparent',
fillStyle: 'solid' as FillStyle,
strokeWidth: 1,
strokeStyle: 'solid' as StrokeStyle,
roughness: 1,
opacity: 100,
seed: 1,
angle: 0 as Radians,
points: [pointFrom(100, 100), pointFrom(200, 200)] as readonly LocalPoint[],
lastCommittedPoint: pointFrom(200, 200),
startBinding: null,
endBinding: null,
startArrowhead: null,
endArrowhead: null,
elbowed: false,
roundness: null,
version: 1,
versionNonce: 1,
isDeleted: false,
groupIds: [],
frameId: null,
boundElements: [],
updated: Date.now(),
link: null,
locked: false,
customData: {},
index: 'a1' as FractionalIndex
} as ExcalidrawArrowElement
];
excalidrawAPI.updateScene({ elements });
};
const saveDiagram = () => {
if (!excalidrawAPI) return;
const elements = excalidrawAPI.getSceneElements();
console.log('Saving diagram:', elements);
};
return (
<div style={{ width: '200px', padding: '10px', borderRight: '1px solid #ccc' }}>
<button onClick={addRectangle}>Add Rectangle</button>
<button onClick={addTemplate}>Add Template</button>
<button onClick={saveDiagram}>Save Diagram</button>
</div>
);
};
export default Sidebar;

89
src/index.tsx Normal file
View file

@ -0,0 +1,89 @@
import * as React from 'react';
import * as ReactDOM from 'react-dom/client';
import { Excalidraw } from '@excalidraw/excalidraw';
import type { ExcalidrawImperativeAPI, AppState } from '@excalidraw/excalidraw/dist/types/excalidraw/types';
import type { ExcalidrawElement as OrderedExcalidrawElement } from '@excalidraw/element/types';
import type { BinaryFiles } from '@excalidraw/excalidraw/dist/types/excalidraw/types';
import Sidebar from './Sidebar';
// Import CSS directly
import './styles/excalidraw-overrides.css';
import '@excalidraw/excalidraw/dist/excalidraw.css';
console.log('Script loaded - looking for root element');
const rootElement = document.getElementById('app');
if (!rootElement) {
console.error('Failed to find root element');
throw new Error('Failed to find the root element');
}
console.log('Root element found, creating React root');
const root = ReactDOM.createRoot(rootElement);
const App = () => {
console.log('Rendering App component');
const [excalidrawAPI, setExcalidrawAPI] = React.useState<ExcalidrawImperativeAPI | null>(null);
const [loading, setLoading] = React.useState(true);
const [error, setError] = React.useState<Error | null>(null);
React.useEffect(() => {
try {
// Initialize any required Excalidraw dependencies
setLoading(false);
} catch (err) {
setError(err instanceof Error ? err : new Error(String(err)));
setLoading(false);
}
}, []);
if (loading) {
return <div>Loading Excalidraw...</div>;
}
if (error) {
return <div>Error loading Excalidraw: {error.message}</div>;
}
const onChange = (
elements: readonly OrderedExcalidrawElement[],
appState: AppState,
files: BinaryFiles
) => {
console.log('Excalidraw state changed:', elements, appState, files);
};
const onPointerUpdate = (payload: { pointer: { x: number; y: number } }) => {
console.log('Excalidraw pointer update:', payload);
};
return (
<div style={{ display: 'flex' }}>
<Sidebar excalidrawAPI={excalidrawAPI} />
<div style={{ height: '500px', width: '800px' }}>
<Excalidraw
excalidrawAPI={(api: ExcalidrawImperativeAPI) => {
setExcalidrawAPI(api);
}}
onChange={onChange}
onPointerUpdate={onPointerUpdate}
initialData={{
appState: {
viewBackgroundColor: '#ffffff',
width: 800,
height: 500,
},
}}
/>
</div>
</div>
);
};
console.log('Starting React render');
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
console.log('Render completed');

View file

@ -0,0 +1,16 @@
/* Fix font-face descriptors */
@font-face {
font-family: 'Virgil';
src: url('/node_modules/@excalidraw/excalidraw/dist/Virgil.woff2') format('woff2');
}
/* Fix align-items rule */
.excalidraw {
align-items: normal !important;
}
/* Remove problematic rules */
.excalidraw .invalid-selector,
.excalidraw .another-invalid-selector {
/* These intentionally left blank to override bad selectors */
}

View file

@ -3,7 +3,7 @@
"rootDir": "./", "rootDir": "./",
"target": "ESNext", "target": "ESNext",
"lib": ["dom", "dom.iterable", "esnext"], "lib": ["dom", "dom.iterable", "esnext"],
"types": ["vitest/globals", "@testing-library/jest-dom"], "types": ["vitest/globals", "@testing-library/jest-dom", "node"],
"allowJs": true, "allowJs": true,
"skipLibCheck": true, "skipLibCheck": true,
"esModuleInterop": true, "esModuleInterop": true,
@ -21,8 +21,8 @@
"paths": { "paths": {
"@excalidraw/common": ["./packages/common/src/index.ts"], "@excalidraw/common": ["./packages/common/src/index.ts"],
"@excalidraw/common/*": ["./packages/common/src/*"], "@excalidraw/common/*": ["./packages/common/src/*"],
"@excalidraw/excalidraw": ["./packages/excalidraw/index.tsx"], "@excalidraw/excalidraw": ["./packages/excalidraw/dist/dev/index.js"],
"@excalidraw/excalidraw/*": ["./packages/excalidraw/*"], "@excalidraw/excalidraw/*": ["./packages/excalidraw/dist/types/*"],
"@excalidraw/element": ["./packages/element/src/index.ts"], "@excalidraw/element": ["./packages/element/src/index.ts"],
"@excalidraw/element/*": ["./packages/element/src/*"], "@excalidraw/element/*": ["./packages/element/src/*"],
"@excalidraw/math": ["./packages/math/src/index.ts"], "@excalidraw/math": ["./packages/math/src/index.ts"],
@ -31,6 +31,6 @@
"@excalidraw/utils/*": ["./packages/utils/src/*"] "@excalidraw/utils/*": ["./packages/utils/src/*"]
} }
}, },
"include": ["packages", "excalidraw-app"], "include": ["src/**/*", "vite.config.ts", "packages/**/*", "excalidraw-app/**/*"],
"exclude": ["examples", "dist", "types", "tests"] "exclude": ["examples", "dist", "tests"]
} }

1
tsconfig.tsbuildinfo Normal file

File diff suppressed because one or more lines are too long

4
types/esbuild-sass-plugin.d.ts vendored Normal file
View file

@ -0,0 +1,4 @@
declare
module
esbuild-sass-plugin
import { Plugin } from 'esbuild'; export function sassPlugin(options?: any): Plugin;

View file

@ -1,4 +1,5 @@
{ {
"version": 2,
"public": true, "public": true,
"headers": [ "headers": [
{ {
@ -65,6 +66,16 @@
"destination": "https://marketplace.visualstudio.com/items?itemName=pomdtr.excalidraw-editor" "destination": "https://marketplace.visualstudio.com/items?itemName=pomdtr.excalidraw-editor"
} }
], ],
"outputDirectory": "excalidraw-app/build", "builds": [
{
"src": "package.json",
"use": "@vercel/static-build",
"config": {
"distDir": "packages/excalidraw/dist",
"framework": "vite"
}
}
],
"outputDirectory": "packages/excalidraw/dist",
"installCommand": "yarn install" "installCommand": "yarn install"
} }

50
vite.config.ts Normal file
View file

@ -0,0 +1,50 @@
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { fileURLToPath, URL } from 'node:url';
export default defineConfig({
plugins: [react()],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url)),
'@excalidraw/excalidraw': fileURLToPath(
new URL('node_modules/@excalidraw/excalidraw/dist/excalidraw.production.min.js', import.meta.url)
),
'@excalidraw/excalidraw/types': fileURLToPath(
new URL('node_modules/@excalidraw/excalidraw/dist/types', import.meta.url)
)
},
extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json']
},
server: {
port: 3000,
open: true,
strictPort: true,
hmr: {
overlay: false
},
headers: {
'Content-Type': 'text/javascript',
'Content-Security-Policy': "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self' data:"
}
},
build: {
sourcemap: true,
rollupOptions: {
input: {
main: './index.html'
}
}
},
css: {
preprocessorOptions: {
css: {
additionalData: `@import './src/styles/excalidraw-overrides.css';`
}
}
},
optimizeDeps: {
include: ['@excalidraw/excalidraw'],
exclude: ['@excalidraw/excalidraw/dist/excalidraw.production.min.js']
}
});

42
vite.package.config.ts Normal file
View file

@ -0,0 +1,42 @@
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { fileURLToPath } from 'node:url';
import { execSync } from 'node:child_process';
export default defineConfig({
plugins: [
{
name: 'build-packages-first',
async config() {
// First build all packages
await Promise.all([
execSync('cd packages/common && yarn build', { stdio: 'inherit' }),
execSync('cd packages/math && yarn build', { stdio: 'inherit' }),
execSync('cd packages/excalidraw && yarn build', { stdio: 'inherit' })
]);
}
},
react()
],
build: {
lib: {
entry: fileURLToPath(new URL('./packages/excalidraw/src/index.tsx', import.meta.url)),
name: 'Excalidraw',
fileName: (format) => `excalidraw.${format}.js`
},
rollupOptions: {
external: ['react', 'react-dom'],
output: {
globals: {
react: 'React',
'react-dom': 'ReactDOM'
},
format: 'es'
},
onwarn: (warning, warn) => {
if (warning.code === 'CIRCULAR_DEPENDENCY') return;
warn(warning);
}
}
}
});

5162
yarn.lock

File diff suppressed because it is too large Load diff