diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..e38e52c --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,38 @@ +name: Deploy + +on: + push: + branches: [main] + workflow_dispatch: + +# Make sure that you set these permissions +permissions: + contents: read + pages: write + id-token: write + +jobs: + ci: + runs-on: ubuntu-latest + + steps: + - name: Setup repo + uses: actions/checkout@v4 + + - name: Setup Deno + uses: denoland/setup-deno@v1 + + - name: Build + run: deno task build + + - name: Setup Pages + uses: actions/configure-pages@v4 + + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + path: "./demo/dist" + + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.gitignore b/.gitignore index 76add87..9538309 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ +dist +.vite node_modules -dist \ No newline at end of file diff --git a/.prettierrc b/.prettierrc deleted file mode 100644 index 92cde39..0000000 --- a/.prettierrc +++ /dev/null @@ -1,3 +0,0 @@ -{ - "singleQuote": true -} \ No newline at end of file diff --git a/README.md b/README.md index d3e16fb..2a2c9a1 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,17 @@ - How can we compose together live and visual programming notations? - Can we have lightweight visual and live scripting for web pages? +## Development + +1. Install [Deno](https://docs.deno.com/runtime/getting_started/installation/) + +```bash +deno i +# then +deno task dev +``` +All config is in `deno.json`. + ## Primitives - ``: Manipulate HTML elements in space. diff --git a/demo/chains-of-thought/main.ts b/demo/chains-of-thought/main.ts index 8518a0d..4009449 100644 --- a/demo/chains-of-thought/main.ts +++ b/demo/chains-of-thought/main.ts @@ -1,15 +1,15 @@ -import { FolkGeometry } from '../../src/canvas/fc-geometry.ts'; -import { FolkConnection } from '../../src/arrows/fc-connection.ts'; -import { FileSaver } from '../../src/persistence/file.ts'; +import { FolkGeometry } from "../../src/canvas/fc-geometry.ts"; +import { FolkConnection } from "../../src/arrows/fc-connection.ts"; +import { FileSaver } from "../../src/persistence/file.ts"; declare global { interface HTMLElementTagNameMap { - 'fc-thought': FolkThought; + "fc-thought": FolkThought; } } class FolkThought extends HTMLElement { - static tagName = 'fc-thought'; + static tagName = "fc-thought"; static register() { customElements.define(this.tagName, this); @@ -25,7 +25,7 @@ class FolkThought extends HTMLElement { constructor() { super(); - this.addEventListener('click', this); + this.addEventListener("click", this); } get text() { @@ -33,7 +33,7 @@ class FolkThought extends HTMLElement { } handleEvent(event: PointerEvent): void { - if (event.type === 'click' && event.target === this.#deleteButton) { + if (event.type === "click" && event.target === this.#deleteButton) { this.#geometry.remove(); document @@ -91,14 +91,14 @@ function renderConnection({ sourceId, targetId }: Connection) { } function renderChainOfThought({ thoughts, connections }: ChainOfThought) { - return html`${thoughts.map(renderThought).join('')}${connections + return html`${thoughts.map(renderThought).join("")}${connections .map(renderConnection) - .join('')}`; + .join("")}`; } function parseChainOfThought(): ChainOfThought { return { - thoughts: Array.from(document.querySelectorAll('fc-geometry')).map( + thoughts: Array.from(document.querySelectorAll("fc-geometry")).map( (el) => ({ id: el.id, text: (el.firstElementChild as FolkThought).text, @@ -106,7 +106,7 @@ function parseChainOfThought(): ChainOfThought { y: el.y, }) ), - connections: Array.from(document.querySelectorAll('fc-connection')).map( + connections: Array.from(document.querySelectorAll("fc-connection")).map( (el) => ({ sourceId: (el.sourceElement as FolkGeometry).id, targetId: (el.targetElement as FolkGeometry).id, @@ -118,20 +118,20 @@ function parseChainOfThought(): ChainOfThought { const openButton = document.querySelector('button[name="open"]')!; const saveButton = document.querySelector('button[name="save"]')!; const saveAsButton = document.querySelector('button[name="save-as"]')!; -const main = document.querySelector('main')!; +const main = document.querySelector("main")!; const fileSaver = new FileSaver( - 'chains-of-thought', - 'json', - 'application/json' + "chains-of-thought", + "json", + "application/json" ); -main.addEventListener('dblclick', (e) => { +main.addEventListener("dblclick", (e) => { if (e.target === main) { main.appendChild( parseHTML( renderThought({ - id: String(document.querySelectorAll('fc-thought').length + 1), - text: '', + id: String(document.querySelectorAll("fc-thought").length + 1), + text: "", x: e.clientX, y: e.clientY, }) @@ -151,20 +151,20 @@ async function openFile(showPicker = true) { } } -async function saveFile(promptNewFile = false) { +function saveFile(promptNewFile = false) { const file = JSON.stringify(parseChainOfThought(), null, 2); fileSaver.save(file, promptNewFile); } -openButton.addEventListener('click', () => { +openButton.addEventListener("click", () => { openFile(); }); -saveButton.addEventListener('click', () => { +saveButton.addEventListener("click", () => { saveFile(); }); -saveAsButton.addEventListener('click', () => { +saveAsButton.addEventListener("click", () => { saveFile(true); }); diff --git a/demo/vite.config.ts b/demo/vite.config.ts deleted file mode 100644 index 74776d2..0000000 --- a/demo/vite.config.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { resolve } from 'node:path'; -import { readdirSync } from 'node:fs'; -import { defineConfig } from 'vite'; - -export default defineConfig({ - build: { - target: 'esnext', - rollupOptions: { - input: readdirSync(__dirname) - .filter((file) => file.endsWith('.html')) - .reduce((acc, file) => { - acc[file.replace('.html', '')] = resolve(__dirname, file); - return acc; - }, {} as Record), - // input: { - // thoughts: resolve(__dirname, 'chains-of-thought/index.html'), - // }, - }, - modulePreload: { - polyfill: false, - }, - }, -}); diff --git a/deno.json b/deno.json new file mode 100644 index 0000000..83fb67c --- /dev/null +++ b/deno.json @@ -0,0 +1,21 @@ +{ + "tasks": { + "dev": "vite demo", + "build": "vite build demo --outDir dist" + }, + "imports": { + "perfect-arrows": "npm:perfect-arrows@^0.3.7", + "perfect-freehand": "npm:perfect-freehand@^1.2.2", + "leaflet": "npm:leaflet@^1.9.4", + "vite": "npm:vite@^5.4.10" + }, + "compilerOptions": { + "lib": [ + "dom", + "dom.iterable", + "dom.asynciterable", + "deno.ns" + ] + }, + "nodeModulesDir": "auto" +} \ No newline at end of file diff --git a/deno.lock b/deno.lock new file mode 100644 index 0000000..71420ba --- /dev/null +++ b/deno.lock @@ -0,0 +1,237 @@ +{ + "version": "4", + "specifiers": { + "npm:leaflet@^1.9.4": "1.9.4", + "npm:perfect-arrows@~0.3.7": "0.3.7", + "npm:perfect-freehand@^1.2.2": "1.2.2", + "npm:vite@5.4.10": "5.4.10", + "npm:vite@^5.4.10": "5.4.10" + }, + "npm": { + "@esbuild/aix-ppc64@0.21.5": { + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==" + }, + "@esbuild/android-arm64@0.21.5": { + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==" + }, + "@esbuild/android-arm@0.21.5": { + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==" + }, + "@esbuild/android-x64@0.21.5": { + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==" + }, + "@esbuild/darwin-arm64@0.21.5": { + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==" + }, + "@esbuild/darwin-x64@0.21.5": { + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==" + }, + "@esbuild/freebsd-arm64@0.21.5": { + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==" + }, + "@esbuild/freebsd-x64@0.21.5": { + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==" + }, + "@esbuild/linux-arm64@0.21.5": { + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==" + }, + "@esbuild/linux-arm@0.21.5": { + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==" + }, + "@esbuild/linux-ia32@0.21.5": { + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==" + }, + "@esbuild/linux-loong64@0.21.5": { + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==" + }, + "@esbuild/linux-mips64el@0.21.5": { + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==" + }, + "@esbuild/linux-ppc64@0.21.5": { + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==" + }, + "@esbuild/linux-riscv64@0.21.5": { + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==" + }, + "@esbuild/linux-s390x@0.21.5": { + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==" + }, + "@esbuild/linux-x64@0.21.5": { + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==" + }, + "@esbuild/netbsd-x64@0.21.5": { + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==" + }, + "@esbuild/openbsd-x64@0.21.5": { + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==" + }, + "@esbuild/sunos-x64@0.21.5": { + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==" + }, + "@esbuild/win32-arm64@0.21.5": { + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==" + }, + "@esbuild/win32-ia32@0.21.5": { + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==" + }, + "@esbuild/win32-x64@0.21.5": { + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==" + }, + "@rollup/rollup-android-arm-eabi@4.24.3": { + "integrity": "sha512-ufb2CH2KfBWPJok95frEZZ82LtDl0A6QKTa8MoM+cWwDZvVGl5/jNb79pIhRvAalUu+7LD91VYR0nwRD799HkQ==" + }, + "@rollup/rollup-android-arm64@4.24.3": { + "integrity": "sha512-iAHpft/eQk9vkWIV5t22V77d90CRofgR2006UiCjHcHJFVI1E0oBkQIAbz+pLtthFw3hWEmVB4ilxGyBf48i2Q==" + }, + "@rollup/rollup-darwin-arm64@4.24.3": { + "integrity": "sha512-QPW2YmkWLlvqmOa2OwrfqLJqkHm7kJCIMq9kOz40Zo9Ipi40kf9ONG5Sz76zszrmIZZ4hgRIkez69YnTHgEz1w==" + }, + "@rollup/rollup-darwin-x64@4.24.3": { + "integrity": "sha512-KO0pN5x3+uZm1ZXeIfDqwcvnQ9UEGN8JX5ufhmgH5Lz4ujjZMAnxQygZAVGemFWn+ZZC0FQopruV4lqmGMshow==" + }, + "@rollup/rollup-freebsd-arm64@4.24.3": { + "integrity": "sha512-CsC+ZdIiZCZbBI+aRlWpYJMSWvVssPuWqrDy/zi9YfnatKKSLFCe6fjna1grHuo/nVaHG+kiglpRhyBQYRTK4A==" + }, + "@rollup/rollup-freebsd-x64@4.24.3": { + "integrity": "sha512-F0nqiLThcfKvRQhZEzMIXOQG4EeX61im61VYL1jo4eBxv4aZRmpin6crnBJQ/nWnCsjH5F6J3W6Stdm0mBNqBg==" + }, + "@rollup/rollup-linux-arm-gnueabihf@4.24.3": { + "integrity": "sha512-KRSFHyE/RdxQ1CSeOIBVIAxStFC/hnBgVcaiCkQaVC+EYDtTe4X7z5tBkFyRoBgUGtB6Xg6t9t2kulnX6wJc6A==" + }, + "@rollup/rollup-linux-arm-musleabihf@4.24.3": { + "integrity": "sha512-h6Q8MT+e05zP5BxEKz0vi0DhthLdrNEnspdLzkoFqGwnmOzakEHSlXfVyA4HJ322QtFy7biUAVFPvIDEDQa6rw==" + }, + "@rollup/rollup-linux-arm64-gnu@4.24.3": { + "integrity": "sha512-fKElSyXhXIJ9pqiYRqisfirIo2Z5pTTve5K438URf08fsypXrEkVmShkSfM8GJ1aUyvjakT+fn2W7Czlpd/0FQ==" + }, + "@rollup/rollup-linux-arm64-musl@4.24.3": { + "integrity": "sha512-YlddZSUk8G0px9/+V9PVilVDC6ydMz7WquxozToozSnfFK6wa6ne1ATUjUvjin09jp34p84milxlY5ikueoenw==" + }, + "@rollup/rollup-linux-powerpc64le-gnu@4.24.3": { + "integrity": "sha512-yNaWw+GAO8JjVx3s3cMeG5Esz1cKVzz8PkTJSfYzE5u7A+NvGmbVFEHP+BikTIyYWuz0+DX9kaA3pH9Sqxp69g==" + }, + "@rollup/rollup-linux-riscv64-gnu@4.24.3": { + "integrity": "sha512-lWKNQfsbpv14ZCtM/HkjCTm4oWTKTfxPmr7iPfp3AHSqyoTz5AgLemYkWLwOBWc+XxBbrU9SCokZP0WlBZM9lA==" + }, + "@rollup/rollup-linux-s390x-gnu@4.24.3": { + "integrity": "sha512-HoojGXTC2CgCcq0Woc/dn12wQUlkNyfH0I1ABK4Ni9YXyFQa86Fkt2Q0nqgLfbhkyfQ6003i3qQk9pLh/SpAYw==" + }, + "@rollup/rollup-linux-x64-gnu@4.24.3": { + "integrity": "sha512-mnEOh4iE4USSccBOtcrjF5nj+5/zm6NcNhbSEfR3Ot0pxBwvEn5QVUXcuOwwPkapDtGZ6pT02xLoPaNv06w7KQ==" + }, + "@rollup/rollup-linux-x64-musl@4.24.3": { + "integrity": "sha512-rMTzawBPimBQkG9NKpNHvquIUTQPzrnPxPbCY1Xt+mFkW7pshvyIS5kYgcf74goxXOQk0CP3EoOC1zcEezKXhw==" + }, + "@rollup/rollup-win32-arm64-msvc@4.24.3": { + "integrity": "sha512-2lg1CE305xNvnH3SyiKwPVsTVLCg4TmNCF1z7PSHX2uZY2VbUpdkgAllVoISD7JO7zu+YynpWNSKAtOrX3AiuA==" + }, + "@rollup/rollup-win32-ia32-msvc@4.24.3": { + "integrity": "sha512-9SjYp1sPyxJsPWuhOCX6F4jUMXGbVVd5obVpoVEi8ClZqo52ViZewA6eFz85y8ezuOA+uJMP5A5zo6Oz4S5rVQ==" + }, + "@rollup/rollup-win32-x64-msvc@4.24.3": { + "integrity": "sha512-HGZgRFFYrMrP3TJlq58nR1xy8zHKId25vhmm5S9jETEfDf6xybPxsavFTJaufe2zgOGYJBskGlj49CwtEuFhWQ==" + }, + "@types/estree@1.0.6": { + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==" + }, + "esbuild@0.21.5": { + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dependencies": [ + "@esbuild/aix-ppc64", + "@esbuild/android-arm", + "@esbuild/android-arm64", + "@esbuild/android-x64", + "@esbuild/darwin-arm64", + "@esbuild/darwin-x64", + "@esbuild/freebsd-arm64", + "@esbuild/freebsd-x64", + "@esbuild/linux-arm", + "@esbuild/linux-arm64", + "@esbuild/linux-ia32", + "@esbuild/linux-loong64", + "@esbuild/linux-mips64el", + "@esbuild/linux-ppc64", + "@esbuild/linux-riscv64", + "@esbuild/linux-s390x", + "@esbuild/linux-x64", + "@esbuild/netbsd-x64", + "@esbuild/openbsd-x64", + "@esbuild/sunos-x64", + "@esbuild/win32-arm64", + "@esbuild/win32-ia32", + "@esbuild/win32-x64" + ] + }, + "fsevents@2.3.3": { + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==" + }, + "leaflet@1.9.4": { + "integrity": "sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA==" + }, + "nanoid@3.3.7": { + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==" + }, + "perfect-arrows@0.3.7": { + "integrity": "sha512-wEN2gerTPVWl3yqoFEF8OeGbg3aRe2sxNUi9rnyYrCsL4JcI6K2tBDezRtqVrYG0BNtsWLdYiiTrYm+X//8yLQ==" + }, + "perfect-freehand@1.2.2": { + "integrity": "sha512-eh31l019WICQ03pkF3FSzHxB8n07ItqIQ++G5UV8JX0zVOXzgTGCqnRR0jJ2h9U8/2uW4W4mtGJELt9kEV0CFQ==" + }, + "picocolors@1.1.0": { + "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==" + }, + "postcss@8.4.47": { + "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", + "dependencies": [ + "nanoid", + "picocolors", + "source-map-js" + ] + }, + "rollup@4.24.3": { + "integrity": "sha512-HBW896xR5HGmoksbi3JBDtmVzWiPAYqp7wip50hjQ67JbDz61nyoMPdqu1DvVW9asYb2M65Z20ZHsyJCMqMyDg==", + "dependencies": [ + "@rollup/rollup-android-arm-eabi", + "@rollup/rollup-android-arm64", + "@rollup/rollup-darwin-arm64", + "@rollup/rollup-darwin-x64", + "@rollup/rollup-freebsd-arm64", + "@rollup/rollup-freebsd-x64", + "@rollup/rollup-linux-arm-gnueabihf", + "@rollup/rollup-linux-arm-musleabihf", + "@rollup/rollup-linux-arm64-gnu", + "@rollup/rollup-linux-arm64-musl", + "@rollup/rollup-linux-powerpc64le-gnu", + "@rollup/rollup-linux-riscv64-gnu", + "@rollup/rollup-linux-s390x-gnu", + "@rollup/rollup-linux-x64-gnu", + "@rollup/rollup-linux-x64-musl", + "@rollup/rollup-win32-arm64-msvc", + "@rollup/rollup-win32-ia32-msvc", + "@rollup/rollup-win32-x64-msvc", + "@types/estree", + "fsevents" + ] + }, + "source-map-js@1.2.1": { + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==" + }, + "vite@5.4.10": { + "integrity": "sha512-1hvaPshuPUtxeQ0hsVH3Mud0ZanOLwVTneA1EgbAM5LhaZEqyPWGRQ7BtaMvUrTDeEaC8pxtj6a6jku3x4z6SQ==", + "dependencies": [ + "esbuild", + "fsevents", + "postcss", + "rollup" + ] + } + }, + "workspace": { + "dependencies": [ + "npm:leaflet@^1.9.4", + "npm:perfect-arrows@~0.3.7", + "npm:perfect-freehand@^1.2.2", + "npm:vite@^5.4.10" + ] + } +} diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index 83c227f..0000000 --- a/package-lock.json +++ /dev/null @@ -1,874 +0,0 @@ -{ - "name": "folk-canvas", - "version": "0.1.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "folk-canvas", - "version": "0.1.0", - "license": "MIT", - "dependencies": { - "leaflet": "^1.9.4", - "perfect-arrows": "^0.3.7", - "perfect-freehand": "^1.1.0" - }, - "devDependencies": { - "@types/leaflet": "^1.9.12", - "@types/node": "^20.12.7", - "typescript": "^5.0.0", - "vite": "^5.0.0" - } - }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", - "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", - "cpu": [ - "ppc64" - ], - "dev": true, - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", - "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", - "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", - "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", - "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", - "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", - "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", - "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", - "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", - "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", - "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", - "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", - "cpu": [ - "loong64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", - "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", - "cpu": [ - "mips64el" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", - "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", - "cpu": [ - "ppc64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", - "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", - "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", - "cpu": [ - "s390x" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", - "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", - "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", - "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", - "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", - "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", - "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", - "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.22.4.tgz", - "integrity": "sha512-Fxamp4aEZnfPOcGA8KSNEohV8hX7zVHOemC8jVBoBUHu5zpJK/Eu3uJwt6BMgy9fkvzxDaurgj96F/NiLukF2w==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.22.4.tgz", - "integrity": "sha512-VXoK5UMrgECLYaMuGuVTOx5kcuap1Jm8g/M83RnCHBKOqvPPmROFJGQaZhGccnsFtfXQ3XYa4/jMCJvZnbJBdA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.22.4.tgz", - "integrity": "sha512-xMM9ORBqu81jyMKCDP+SZDhnX2QEVQzTcC6G18KlTQEzWK8r/oNZtKuZaCcHhnsa6fEeOBionoyl5JsAbE/36Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.22.4.tgz", - "integrity": "sha512-aJJyYKQwbHuhTUrjWjxEvGnNNBCnmpHDvrb8JFDbeSH3m2XdHcxDd3jthAzvmoI8w/kSjd2y0udT+4okADsZIw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.22.4.tgz", - "integrity": "sha512-j63YtCIRAzbO+gC2L9dWXRh5BFetsv0j0va0Wi9epXDgU/XUi5dJKo4USTttVyK7fGw2nPWK0PbAvyliz50SCQ==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.22.4.tgz", - "integrity": "sha512-dJnWUgwWBX1YBRsuKKMOlXCzh2Wu1mlHzv20TpqEsfdZLb3WoJW2kIEsGwLkroYf24IrPAvOT/ZQ2OYMV6vlrg==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.22.4.tgz", - "integrity": "sha512-AdPRoNi3NKVLolCN/Sp4F4N1d98c4SBnHMKoLuiG6RXgoZ4sllseuGioszumnPGmPM2O7qaAX/IJdeDU8f26Aw==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.22.4.tgz", - "integrity": "sha512-Gl0AxBtDg8uoAn5CCqQDMqAx22Wx22pjDOjBdmG0VIWX3qUBHzYmOKh8KXHL4UpogfJ14G4wk16EQogF+v8hmA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.22.4.tgz", - "integrity": "sha512-3aVCK9xfWW1oGQpTsYJJPF6bfpWfhbRnhdlyhak2ZiyFLDaayz0EP5j9V1RVLAAxlmWKTDfS9wyRyY3hvhPoOg==", - "cpu": [ - "ppc64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.22.4.tgz", - "integrity": "sha512-ePYIir6VYnhgv2C5Xe9u+ico4t8sZWXschR6fMgoPUK31yQu7hTEJb7bCqivHECwIClJfKgE7zYsh1qTP3WHUA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.22.4.tgz", - "integrity": "sha512-GqFJ9wLlbB9daxhVlrTe61vJtEY99/xB3C8e4ULVsVfflcpmR6c8UZXjtkMA6FhNONhj2eA5Tk9uAVw5orEs4Q==", - "cpu": [ - "s390x" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.22.4.tgz", - "integrity": "sha512-87v0ol2sH9GE3cLQLNEy0K/R0pz1nvg76o8M5nhMR0+Q+BBGLnb35P0fVz4CQxHYXaAOhE8HhlkaZfsdUOlHwg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.22.4.tgz", - "integrity": "sha512-UV6FZMUgePDZrFjrNGIWzDo/vABebuXBhJEqrHxrGiU6HikPy0Z3LfdtciIttEUQfuDdCn8fqh7wiFJjCNwO+g==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.22.4.tgz", - "integrity": "sha512-BjI+NVVEGAXjGWYHz/vv0pBqfGoUH0IGZ0cICTn7kB9PyjrATSkX+8WkguNjWoj2qSr1im/+tTGRaY+4/PdcQw==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.22.4.tgz", - "integrity": "sha512-SiWG/1TuUdPvYmzmYnmd3IEifzR61Tragkbx9D3+R8mzQqDBz8v+BvZNDlkiTtI9T15KYZhP0ehn3Dld4n9J5g==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.22.4.tgz", - "integrity": "sha512-j8pPKp53/lq9lMXN57S8cFz0MynJk8OWNuUnXct/9KCpKU7DgU3bYMJhwWmcqC0UU29p8Lr0/7KEVcaM6bf47Q==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@types/estree": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", - "dev": true - }, - "node_modules/@types/geojson": { - "version": "7946.0.14", - "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.14.tgz", - "integrity": "sha512-WCfD5Ht3ZesJUsONdhvm84dmzWOiOzOAqOncN0++w0lBw1o8OuDNJF2McvvCef/yBqb/HYRahp1BYtODFQ8bRg==", - "dev": true - }, - "node_modules/@types/leaflet": { - "version": "1.9.12", - "resolved": "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.9.12.tgz", - "integrity": "sha512-BK7XS+NyRI291HIo0HCfE18Lp8oA30H1gpi1tf0mF3TgiCEzanQjOqNZ4x126SXzzi2oNSZhZ5axJp1k0iM6jg==", - "dev": true, - "dependencies": { - "@types/geojson": "*" - } - }, - "node_modules/@types/node": { - "version": "20.15.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.15.0.tgz", - "integrity": "sha512-eQf4OkH6gA9v1W0iEpht/neozCsZKMTK+C4cU6/fv7wtJCCL8LEQ4hie2Ln8ZP/0YYM2xGj7//f8xyqItkJ6QA==", - "dev": true, - "dependencies": { - "undici-types": "~6.13.0" - } - }, - "node_modules/esbuild": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", - "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", - "dev": true, - "hasInstallScript": true, - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=12" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.21.5", - "@esbuild/android-arm": "0.21.5", - "@esbuild/android-arm64": "0.21.5", - "@esbuild/android-x64": "0.21.5", - "@esbuild/darwin-arm64": "0.21.5", - "@esbuild/darwin-x64": "0.21.5", - "@esbuild/freebsd-arm64": "0.21.5", - "@esbuild/freebsd-x64": "0.21.5", - "@esbuild/linux-arm": "0.21.5", - "@esbuild/linux-arm64": "0.21.5", - "@esbuild/linux-ia32": "0.21.5", - "@esbuild/linux-loong64": "0.21.5", - "@esbuild/linux-mips64el": "0.21.5", - "@esbuild/linux-ppc64": "0.21.5", - "@esbuild/linux-riscv64": "0.21.5", - "@esbuild/linux-s390x": "0.21.5", - "@esbuild/linux-x64": "0.21.5", - "@esbuild/netbsd-x64": "0.21.5", - "@esbuild/openbsd-x64": "0.21.5", - "@esbuild/sunos-x64": "0.21.5", - "@esbuild/win32-arm64": "0.21.5", - "@esbuild/win32-ia32": "0.21.5", - "@esbuild/win32-x64": "0.21.5" - } - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/leaflet": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.9.4.tgz", - "integrity": "sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA==" - }, - "node_modules/nanoid": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/perfect-arrows": { - "version": "0.3.7", - "resolved": "https://registry.npmjs.org/perfect-arrows/-/perfect-arrows-0.3.7.tgz", - "integrity": "sha512-wEN2gerTPVWl3yqoFEF8OeGbg3aRe2sxNUi9rnyYrCsL4JcI6K2tBDezRtqVrYG0BNtsWLdYiiTrYm+X//8yLQ==", - "engines": { - "node": ">=10" - } - }, - "node_modules/perfect-freehand": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/perfect-freehand/-/perfect-freehand-1.2.2.tgz", - "integrity": "sha512-eh31l019WICQ03pkF3FSzHxB8n07ItqIQ++G5UV8JX0zVOXzgTGCqnRR0jJ2h9U8/2uW4W4mtGJELt9kEV0CFQ==" - }, - "node_modules/picocolors": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", - "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==", - "dev": true - }, - "node_modules/postcss": { - "version": "8.4.47", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", - "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "nanoid": "^3.3.7", - "picocolors": "^1.1.0", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/rollup": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.22.4.tgz", - "integrity": "sha512-vD8HJ5raRcWOyymsR6Z3o6+RzfEPCnVLMFJ6vRslO1jt4LO6dUo5Qnpg7y4RkZFM2DMe3WUirkI5c16onjrc6A==", - "dev": true, - "dependencies": { - "@types/estree": "1.0.5" - }, - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.22.4", - "@rollup/rollup-android-arm64": "4.22.4", - "@rollup/rollup-darwin-arm64": "4.22.4", - "@rollup/rollup-darwin-x64": "4.22.4", - "@rollup/rollup-linux-arm-gnueabihf": "4.22.4", - "@rollup/rollup-linux-arm-musleabihf": "4.22.4", - "@rollup/rollup-linux-arm64-gnu": "4.22.4", - "@rollup/rollup-linux-arm64-musl": "4.22.4", - "@rollup/rollup-linux-powerpc64le-gnu": "4.22.4", - "@rollup/rollup-linux-riscv64-gnu": "4.22.4", - "@rollup/rollup-linux-s390x-gnu": "4.22.4", - "@rollup/rollup-linux-x64-gnu": "4.22.4", - "@rollup/rollup-linux-x64-musl": "4.22.4", - "@rollup/rollup-win32-arm64-msvc": "4.22.4", - "@rollup/rollup-win32-ia32-msvc": "4.22.4", - "@rollup/rollup-win32-x64-msvc": "4.22.4", - "fsevents": "~2.3.2" - } - }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/typescript": { - "version": "5.5.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", - "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", - "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/undici-types": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.13.0.tgz", - "integrity": "sha512-xtFJHudx8S2DSoujjMd1WeWvn7KKWFRESZTMeL1RptAYERu29D6jphMjjY+vn96jvN3kVPDNxU/E13VTaXj6jg==", - "dev": true - }, - "node_modules/vite": { - "version": "5.4.6", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.6.tgz", - "integrity": "sha512-IeL5f8OO5nylsgzd9tq4qD2QqI0k2CQLGrWD0rCN0EQJZpBK5vJAx0I+GDkMOXxQX/OfFHMuLIx6ddAxGX/k+Q==", - "dev": true, - "dependencies": { - "esbuild": "^0.21.3", - "postcss": "^8.4.43", - "rollup": "^4.20.0" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^18.0.0 || >=20.0.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@types/node": "^18.0.0 || >=20.0.0", - "less": "*", - "lightningcss": "^1.21.0", - "sass": "*", - "sass-embedded": "*", - "stylus": "*", - "sugarss": "*", - "terser": "^5.4.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "less": { - "optional": true - }, - "lightningcss": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - } - } - } - } -} diff --git a/package.json b/package.json deleted file mode 100644 index a46173f..0000000 --- a/package.json +++ /dev/null @@ -1,42 +0,0 @@ -{ - "name": "folk-canvas", - "description": "", - "version": "0.1.0", - "type": "module", - "sideEffects": false, - "author": "Christopher Shank ", - "license": "MIT", - "repository": { - "type": "git", - "url": "git+https://github.com/ChrisShank/folk-canvas.git" - }, - "bugs": { - "url": "https://github.com/ChrisShank/folk-canvas/issues" - }, - "homepage": "https://github.com/ChrisShank/folk-canvas#readme", - "files": [ - "dist", - "src" - ], - "keywords": [ - "spatial canvas", - "custom elements" - ], - "scripts": { - "build": "tsc --build", - "dev": "vite demo", - "demo:build": "vite build demo", - "demo:preview": "pnpm demo:build && vite preview demo" - }, - "devDependencies": { - "@types/leaflet": "^1.9.12", - "@types/node": "^20.12.7", - "typescript": "^5.0.0", - "vite": "^5.0.0" - }, - "dependencies": { - "leaflet": "^1.9.4", - "perfect-arrows": "^0.3.7", - "perfect-freehand": "^1.1.0" - } -} diff --git a/src/arrows/event-propagator.ts b/src/arrows/event-propagator.ts index d032bb1..280ec91 100644 --- a/src/arrows/event-propagator.ts +++ b/src/arrows/event-propagator.ts @@ -1,9 +1,9 @@ -import { FolkConnection } from './fc-connection'; +import { FolkConnection } from "./fc-connection.ts"; export class EventPropagator extends FolkConnection { - static tagName = 'event-propagator'; + static override tagName = "event-propagator"; - #triggers = (this.getAttribute('triggers') || '').split(','); + #triggers = (this.getAttribute("triggers") || "").split(","); get triggers() { return this.#triggers; } @@ -11,23 +11,23 @@ export class EventPropagator extends FolkConnection { this.#triggers = triggers; } - #expression = ''; + #expression = ""; #function = new Function(); get expression() { return this.#expression; } set expression(expression) { this.#expression = expression; - this.#function = new Function('$source', '$target', '$event', expression); + this.#function = new Function("$source", "$target", "$event", expression); } constructor() { super(); - this.expression = this.getAttribute('expression') || ''; + this.expression = this.getAttribute("expression") || ""; } - observeSource() { + override observeSource() { super.observeSource(); for (const trigger of this.#triggers) { @@ -38,7 +38,7 @@ export class EventPropagator extends FolkConnection { this.evaluateExpression(); } - unobserveSource() { + override unobserveSource() { super.unobserveSource(); for (const trigger of this.#triggers) { @@ -47,12 +47,12 @@ export class EventPropagator extends FolkConnection { } } - observeTarget() { + override observeTarget() { super.observeTarget(); this.evaluateExpression(); } - unobserveTarget() { + override unobserveTarget() { super.unobserveTarget(); } diff --git a/src/arrows/fc-connection.ts b/src/arrows/fc-connection.ts index c897ae1..f043093 100644 --- a/src/arrows/fc-connection.ts +++ b/src/arrows/fc-connection.ts @@ -1,7 +1,7 @@ -import { getBoxToBoxArrow } from 'perfect-arrows'; -import { AbstractArrow } from './abstract-arrow'; -import { pointsOnBezierCurves } from './points-on-path'; -import getStroke, { StrokeOptions } from 'perfect-freehand'; +import { getBoxToBoxArrow } from "perfect-arrows"; +import { AbstractArrow } from "./abstract-arrow.ts"; +import { pointsOnBezierCurves } from "./points-on-path.ts"; +import { getStroke, StrokeOptions } from "perfect-freehand"; export type Arrow = [ /** The x position of the (padded) starting point. */ @@ -26,12 +26,12 @@ export type Arrow = [ declare global { interface HTMLElementTagNameMap { - 'fc-connection': FolkConnection; + "fc-connection": FolkConnection; } } export class FolkConnection extends AbstractArrow { - static tagName = 'fc-connection'; + static override tagName = "fc-connection"; #options: StrokeOptions = { size: 7, @@ -53,7 +53,7 @@ export class FolkConnection extends AbstractArrow { }, }; - render() { + override render() { const { sourceRect, targetRect } = this; const [sx, sy, cx, cy, ex, ey] = getBoxToBoxArrow( @@ -77,12 +77,12 @@ export class FolkConnection extends AbstractArrow { const stroke = getStroke(points, this.#options); const path = getSvgPathFromStroke(stroke); this.style.clipPath = `path('${path}')`; - this.style.backgroundColor = 'black'; + this.style.backgroundColor = "black"; } } function getSvgPathFromStroke(stroke: number[][]): string { - if (stroke.length === 0) return ''; + if (stroke.length === 0) return ""; for (const point of stroke) { point[0] = Math.round(point[0] * 100) / 100; @@ -95,9 +95,9 @@ function getSvgPathFromStroke(stroke: number[][]): string { acc.push(x0, y0, (x0 + x1) / 2, (y0 + y1) / 2); return acc; }, - ['M', ...stroke[0], 'Q'] + ["M", ...stroke[0], "Q"] ); - d.push('Z'); - return d.join(' '); + d.push("Z"); + return d.join(" "); } diff --git a/src/canvas/fc-geometry.ts b/src/canvas/fc-geometry.ts index 3706510..e7395d7 100644 --- a/src/canvas/fc-geometry.ts +++ b/src/canvas/fc-geometry.ts @@ -30,7 +30,7 @@ class ResizeObserverManager { } unobserve(target: Element, callback: ResizeObserverEntryCallback): void { - let callbacks = this.#elementMap.get(target); + const callbacks = this.#elementMap.get(target); if (callbacks === undefined) return; @@ -45,13 +45,13 @@ class ResizeObserverManager { const resizeObserver = new ResizeObserverManager(); -export type Shape = 'rectangle' | 'circle' | 'triangle'; +export type Shape = "rectangle" | "circle" | "triangle"; export type MoveEventDetail = { movementX: number; movementY: number }; export class MoveEvent extends CustomEvent { constructor(detail: MoveEventDetail) { - super('move', { detail, cancelable: true, bubbles: true }); + super("move", { detail, cancelable: true, bubbles: true }); } } @@ -59,7 +59,7 @@ export type ResizeEventDetail = { movementX: number; movementY: number }; export class ResizeEvent extends CustomEvent { constructor(detail: MoveEventDetail) { - super('resize', { detail, cancelable: true, bubbles: true }); + super("resize", { detail, cancelable: true, bubbles: true }); } } @@ -67,11 +67,11 @@ export type RotateEventDetail = { rotate: number }; export class RotateEvent extends CustomEvent { constructor(detail: RotateEventDetail) { - super('rotate', { detail, cancelable: true, bubbles: true }); + super("rotate", { detail, cancelable: true, bubbles: true }); } } -export type Dimension = number | 'auto'; +export type Dimension = number | "auto"; const styles = new CSSStyleSheet(); styles.replaceSync(` @@ -180,13 +180,13 @@ styles.replaceSync(` declare global { interface HTMLElementTagNameMap { - 'fc-geometry': FolkGeometry; + "fc-geometry": FolkGeometry; } } // TODO: add z coordinate? export class FolkGeometry extends HTMLElement { - static tagName = 'fc-geometry'; + static tagName = "fc-geometry"; static register() { customElements.define(this.tagName, this); @@ -194,17 +194,17 @@ export class FolkGeometry extends HTMLElement { #internals = this.attachInternals(); - #type = (this.getAttribute('type') || 'rectangle') as Shape; + #type = (this.getAttribute("type") || "rectangle") as Shape; get type(): Shape { return this.#type; } set type(type: Shape) { - this.setAttribute('type', type); + this.setAttribute("type", type); } #previousX = 0; - #x = Number(this.getAttribute('x')) || 0; + #x = Number(this.getAttribute("x")) || 0; get x() { return this.#x; } @@ -212,11 +212,11 @@ export class FolkGeometry extends HTMLElement { set x(x) { this.#previousX = this.#x; this.#x = x; - this.#requestUpdate('x'); + this.#requestUpdate("x"); } #previousY = 0; - #y = Number(this.getAttribute('y')) || 0; + #y = Number(this.getAttribute("y")) || 0; get y() { return this.#y; } @@ -224,7 +224,7 @@ export class FolkGeometry extends HTMLElement { set y(y) { this.#previousY = this.#y; this.#y = y; - this.#requestUpdate('y'); + this.#requestUpdate("y"); } #autoContentRect = this.getBoundingClientRect(); @@ -232,48 +232,48 @@ export class FolkGeometry extends HTMLElement { #previousWidth: Dimension = 0; #width: Dimension = 0; get width(): number { - if (this.#width === 'auto') { + if (this.#width === "auto") { return this.#autoContentRect.width; } return this.#width; } set width(width: Dimension) { - if (width === 'auto') { + if (width === "auto") { resizeObserver.observe(this, this.#onResize); - } else if (this.#width === 'auto' && this.#height !== 'auto') { + } else if (this.#width === "auto" && this.#height !== "auto") { resizeObserver.unobserve(this, this.#onResize); } this.#previousWidth = this.#width; this.#width = width; - this.#requestUpdate('width'); + this.#requestUpdate("width"); } #previousHeight: Dimension = 0; #height: Dimension = 0; get height(): number { - if (this.#height === 'auto') { + if (this.#height === "auto") { return this.#autoContentRect.height; } return this.#height; } set height(height: Dimension) { - if (height === 'auto') { + if (height === "auto") { resizeObserver.observe(this, this.#onResize); - } else if (this.#height === 'auto' && this.#width !== 'auto') { + } else if (this.#height === "auto" && this.#width !== "auto") { resizeObserver.unobserve(this, this.#onResize); } this.#previousHeight = this.#height; this.#height = height; - this.#requestUpdate('height'); + this.#requestUpdate("height"); } #initialRotation = 0; #startAngle = 0; #previousRotate = 0; - #rotate = Number(this.getAttribute('rotate')) || 0; + #rotate = Number(this.getAttribute("rotate")) || 0; get rotate(): number { return this.#rotate; } @@ -281,16 +281,16 @@ export class FolkGeometry extends HTMLElement { set rotate(rotate: number) { this.#previousRotate = this.#rotate; this.#rotate = rotate; - this.#requestUpdate('rotate'); + this.#requestUpdate("rotate"); } constructor() { super(); - this.addEventListener('pointerdown', this); + this.addEventListener("pointerdown", this); const shadowRoot = this.attachShadow({ - mode: 'open', + mode: "open", delegatesFocus: true, }); shadowRoot.adoptedStyleSheets.push(styles); @@ -305,12 +305,12 @@ export class FolkGeometry extends HTMLElement { `; - this.height = Number(this.getAttribute('height')) || 'auto'; - this.width = Number(this.getAttribute('width')) || 'auto'; + this.height = Number(this.getAttribute("height")) || "auto"; + this.width = Number(this.getAttribute("width")) || "auto"; } connectedCallback() { - this.#update(new Set(['type', 'x', 'y', 'height', 'width', 'rotate'])); + this.#update(new Set(["type", "x", "y", "height", "width", "rotate"])); } disconnectedCallback() { @@ -319,7 +319,7 @@ export class FolkGeometry extends HTMLElement { // Similar to `Element.getClientBoundingRect()`, but returns an SVG path that precisely outlines the shape. getBoundingPath(): string { - return ''; + return ""; } // We might also want some kind of utility function that maps a path into an approximate set of vertices. @@ -329,13 +329,13 @@ export class FolkGeometry extends HTMLElement { handleEvent(event: PointerEvent) { switch (event.type) { - case 'pointerdown': { + case "pointerdown": { if (event.button !== 0 || event.ctrlKey) return; const target = event.composedPath()[0] as HTMLElement; // Store initial angle on rotation start - if (target.getAttribute('part') === 'rotate') { + if (target.getAttribute("part") === "rotate") { // We need to store initial rotation/angle somewhere. // This is a little awkward as we'll want to do *quite a lot* of this kind of thing. // Might be an argument for making elements dumber (i.e. not have them manage their own state) and do this from the outside. @@ -351,19 +351,19 @@ export class FolkGeometry extends HTMLElement { } // ignore interactions from slotted elements. - if (target !== this && !target.hasAttribute('part')) return; + if (target !== this && !target.hasAttribute("part")) return; - target.addEventListener('pointermove', this); - this.addEventListener('lostpointercapture', this); + target.addEventListener("pointermove", this); + this.addEventListener("lostpointercapture", this); target.setPointerCapture(event.pointerId); - const interaction = target.getAttribute('part') || 'move'; + const interaction = target.getAttribute("part") || "move"; this.#internals.states.add(interaction); this.focus(); return; } - case 'pointermove': { + case "pointermove": { const target = event.target as HTMLElement; if (target === null) return; @@ -374,33 +374,33 @@ export class FolkGeometry extends HTMLElement { return; } - const part = target.getAttribute('part'); + const part = target.getAttribute("part"); if (part === null) return; - if (part.includes('resize')) { + if (part.includes("resize")) { // This triggers a move and resize event :( - if (part.includes('-n')) { + if (part.includes("-n")) { this.y += event.movementY; this.height -= event.movementY; } - if (part.endsWith('e')) { + if (part.endsWith("e")) { this.width += event.movementX; } - if (part.includes('-s')) { + if (part.includes("-s")) { this.height += event.movementY; } - if (part.endsWith('w')) { + if (part.endsWith("w")) { this.x += event.movementX; this.width -= event.movementX; } return; } - if (part === 'rotate') { + if (part === "rotate") { const centerX = this.#x + this.width / 2; const centerY = this.#y + this.height / 2; const currentAngle = Math.atan2( @@ -415,12 +415,12 @@ export class FolkGeometry extends HTMLElement { return; } - case 'lostpointercapture': { + case "lostpointercapture": { const target = event.composedPath()[0] as HTMLElement; - const interaction = target.getAttribute('part') || 'move'; + const interaction = target.getAttribute("part") || "move"; this.#internals.states.delete(interaction); - target.removeEventListener('pointermove', this); - this.removeEventListener('lostpointercapture', this); + target.removeEventListener("pointermove", this); + this.removeEventListener("lostpointercapture", this); return; } } @@ -446,14 +446,14 @@ export class FolkGeometry extends HTMLElement { // Any updates that should be batched should happen here like updating the DOM or emitting events should be executed here. #update(updatedProperties: Set) { - if (updatedProperties.has('type')) { + if (updatedProperties.has("type")) { // TODO: Update shape styles. For many shapes, we could just use clip-path to style the shape. // If we use relative values in `clip-path: polygon()`, then no JS is needed to style the shape // If `clip-path: path()` is used then we need to update the path in JS. // See https://www.smashingmagazine.com/2024/05/modern-guide-making-css-shapes/ } - if (updatedProperties.has('x') || updatedProperties.has('y')) { + if (updatedProperties.has("x") || updatedProperties.has("y")) { // Although the change in movement isn't useful inside this component, the outside world might find it helpful to calculate acceleration and other physics const notCancelled = this.dispatchEvent( new MoveEvent({ @@ -463,12 +463,12 @@ export class FolkGeometry extends HTMLElement { ); if (notCancelled) { - if (updatedProperties.has('x')) { + if (updatedProperties.has("x")) { // In the future, when CSS `attr()` is supported we could define this x/y projection in CSS. this.style.left = `${this.#x}px`; } - if (updatedProperties.has('y')) { + if (updatedProperties.has("y")) { this.style.top = `${this.#y}px`; } } else { @@ -477,26 +477,26 @@ export class FolkGeometry extends HTMLElement { } } - if (updatedProperties.has('width') || updatedProperties.has('height')) { + if (updatedProperties.has("width") || updatedProperties.has("height")) { // Although the change in resize isn't useful inside this component, the outside world might find it helpful to calculate acceleration and other physics const notCancelled = this.dispatchEvent( new ResizeEvent({ movementX: this.width - - (this.#previousWidth === 'auto' ? 0 : this.#previousWidth), + (this.#previousWidth === "auto" ? 0 : this.#previousWidth), movementY: this.height - - (this.#previousHeight === 'auto' ? 0 : this.#previousHeight), + (this.#previousHeight === "auto" ? 0 : this.#previousHeight), }) ); if (notCancelled) { - if (updatedProperties.has('width')) { - this.style.width = this.#width === 'auto' ? '' : `${this.#width}px`; + if (updatedProperties.has("width")) { + this.style.width = this.#width === "auto" ? "" : `${this.#width}px`; } - if (updatedProperties.has('height')) { + if (updatedProperties.has("height")) { this.style.height = - this.#height === 'auto' ? '' : `${this.#height}px`; + this.#height === "auto" ? "" : `${this.#height}px`; } } else { // TODO: Revert changes to position too @@ -505,14 +505,14 @@ export class FolkGeometry extends HTMLElement { } } - if (updatedProperties.has('rotate')) { + if (updatedProperties.has("rotate")) { // Although the change in resize isn't useful inside this component, the outside world might find it helpful to calculate acceleration and other physics const notCancelled = this.dispatchEvent( new RotateEvent({ rotate: this.#rotate - this.#previousRotate }) ); if (notCancelled) { - if (updatedProperties.has('rotate')) { + if (updatedProperties.has("rotate")) { this.style.rotate = `${this.#rotate}deg`; } } else { @@ -529,23 +529,23 @@ export class FolkGeometry extends HTMLElement { new ResizeEvent({ movementX: this.width - - (this.#previousWidth === 'auto' + (this.#previousWidth === "auto" ? previousRect.width : this.#previousWidth), movementY: this.height - - (this.#previousHeight === 'auto' + (this.#previousHeight === "auto" ? previousRect.height : this.#previousHeight), }) ); if (!notCancelled) { - if (this.#height === 'auto') { + if (this.#height === "auto") { this.height = previousRect?.height || 0; } - if (this.#width === 'auto') { + if (this.#width === "auto") { this.width = previousRect?.width || 0; } } diff --git a/src/canvas/fc-ink.ts b/src/canvas/fc-ink.ts index 6a20631..334b337 100644 --- a/src/canvas/fc-ink.ts +++ b/src/canvas/fc-ink.ts @@ -1,4 +1,4 @@ -import getStroke, { StrokeOptions } from 'perfect-freehand'; +import { getStroke, StrokeOptions } from "perfect-freehand"; export type Point = [x: number, y: number, pressure: number]; @@ -25,12 +25,12 @@ styles.replaceSync(` declare global { interface HTMLElementTagNameMap { - 'fc-ink': FolkInk; + "fc-ink": FolkInk; } } export class FolkInk extends HTMLElement { - static tagName = 'fc-ink'; + static tagName = "fc-ink"; static register() { customElements.define(this.tagName, this); @@ -38,10 +38,10 @@ export class FolkInk extends HTMLElement { #internals = this.attachInternals(); - #svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); - #path = document.createElementNS('http://www.w3.org/2000/svg', 'path'); + #svg = document.createElementNS("http://www.w3.org/2000/svg", "svg"); + #path = document.createElementNS("http://www.w3.org/2000/svg", "path"); - #size = Number(this.getAttribute('size') || 16); + #size = Number(this.getAttribute("size") || 16); get size() { return this.#size; @@ -51,7 +51,7 @@ export class FolkInk extends HTMLElement { this.#update(); } - #thinning = Number(this.getAttribute('thinning') || 0.5); + #thinning = Number(this.getAttribute("thinning") || 0.5); get thinning() { return this.#thinning; @@ -61,7 +61,7 @@ export class FolkInk extends HTMLElement { this.#update(); } - #smoothing = Number(this.getAttribute('smoothing') || 0.5); + #smoothing = Number(this.getAttribute("smoothing") || 0.5); get smoothing() { return this.#smoothing; @@ -71,7 +71,7 @@ export class FolkInk extends HTMLElement { this.#update(); } - #streamline = Number(this.getAttribute('streamline') || 0.5); + #streamline = Number(this.getAttribute("streamline") || 0.5); get streamline() { return this.#streamline; @@ -82,7 +82,7 @@ export class FolkInk extends HTMLElement { } #simulatePressure = - this.getAttribute('streamline') === 'false' ? false : true; + this.getAttribute("streamline") === "false" ? false : true; get simulatePressure() { return this.#simulatePressure; @@ -92,7 +92,7 @@ export class FolkInk extends HTMLElement { this.#update(); } - #points: Point[] = JSON.parse(this.getAttribute('points') || '[]'); + #points: Point[] = JSON.parse(this.getAttribute("points") || "[]"); get points() { return this.#points; @@ -106,7 +106,7 @@ export class FolkInk extends HTMLElement { super(); const shadowRoot = this.attachShadow({ - mode: 'open', + mode: "open", delegatesFocus: true, }); shadowRoot.adoptedStyleSheets.push(styles); @@ -130,10 +130,10 @@ export class FolkInk extends HTMLElement { // TODO: cancel trace? draw(event?: PointerEvent) { - if (event?.type === 'pointerdown') { + if (event?.type === "pointerdown") { this.handleEvent(event); } else { - this.addEventListener('pointerdown', this); + this.addEventListener("pointerdown", this); } this.#tracingPromise = Promise.withResolvers(); return this.#tracingPromise.promise; @@ -146,26 +146,26 @@ export class FolkInk extends HTMLElement { handleEvent(event: PointerEvent) { switch (event.type) { - case 'pointerdown': { + case "pointerdown": { if (event.button !== 0 || event.ctrlKey) return; this.points = []; this.addPoint([event.offsetX, event.offsetY, event.pressure]); - this.addEventListener('lostpointercapture', this); - this.addEventListener('pointermove', this); + this.addEventListener("lostpointercapture", this); + this.addEventListener("pointermove", this); this.setPointerCapture(event.pointerId); - this.#internals.states.add('drawing'); + this.#internals.states.add("drawing"); return; } - case 'pointermove': { + case "pointermove": { this.addPoint([event.offsetX, event.offsetY, event.pressure]); return; } - case 'lostpointercapture': { - this.removeEventListener('pointerdown', this); - this.removeEventListener('pointermove', this); - this.removeEventListener('lostpointercapture', this); - this.#internals.states.delete('drawing'); + case "lostpointercapture": { + this.removeEventListener("pointerdown", this); + this.removeEventListener("pointermove", this); + this.removeEventListener("lostpointercapture", this); + this.#internals.states.delete("drawing"); this.#tracingPromise?.resolve(); this.#tracingPromise = null; return; @@ -194,13 +194,13 @@ export class FolkInk extends HTMLElement { }, }; this.#path.setAttribute( - 'd', + "d", this.#getSvgPathFromStroke(getStroke(this.#points, options)) ); } #getSvgPathFromStroke(stroke: Stroke): string { - if (stroke.length === 0) return ''; + if (stroke.length === 0) return ""; const d = stroke.reduce( (acc, [x0, y0], i, arr) => { @@ -208,10 +208,10 @@ export class FolkInk extends HTMLElement { acc.push(x0, y0, (x0 + x1) / 2, (y0 + y1) / 2); return acc; }, - ['M', ...stroke[0], 'Q'] + ["M", ...stroke[0], "Q"] ); - d.push('Z'); - return d.join(' '); + d.push("Z"); + return d.join(" "); } } diff --git a/src/maps/index.ts b/src/maps/index.ts index 982411e..4278c1b 100644 --- a/src/maps/index.ts +++ b/src/maps/index.ts @@ -1,8 +1,15 @@ -import { LatLng, LatLngExpression, LeafletEvent, map, Map, tileLayer } from 'leaflet'; +import { + LatLng, + LatLngExpression, + LeafletEvent, + map, + Map, + tileLayer, +} from "leaflet"; // @ts-ignore // Vite specific import :( -import css from 'leaflet/dist/leaflet.css?inline'; +import css from "leaflet/dist/leaflet.css?inline"; const styles = new CSSStyleSheet(); styles.replaceSync(`${css} :host { @@ -17,24 +24,24 @@ styles.replaceSync(`${css} export class RecenterEvent extends CustomEvent { constructor(detail: LatLng) { - super('recenter', { detail, bubbles: true }); + super("recenter", { detail, bubbles: true }); } } export class LeafletMap extends HTMLElement { - static tagName = 'leaflet-map'; + static tagName = "leaflet-map"; static register() { customElements.define(this.tagName, this); } - #container = document.createElement('div'); + #container = document.createElement("div"); #map!: Map; constructor() { super(); this.handleEvent = this.handleEvent.bind(this); - const shadow = this.attachShadow({ mode: 'open' }); + const shadow = this.attachShadow({ mode: "open" }); shadow.adoptedStyleSheets.push(styles); shadow.appendChild(this.#container); } @@ -42,25 +49,26 @@ export class LeafletMap extends HTMLElement { connectedCallback() { this.#map = map(this.#container); this.#map.addLayer( - tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', { + tileLayer("https://tile.openstreetmap.org/{z}/{x}/{y}.png", { maxZoom: 19, - attribution: '© OpenStreetMap', + attribution: + '© OpenStreetMap', }) ); - const coordinates = (this.getAttribute('coordinates') - ?.split(',') + const coordinates = (this.getAttribute("coordinates") + ?.split(",") .map((str) => Number(str)) || [0, 0]) as LatLngExpression; - const zoom = Number(this.getAttribute('zoom') || 13); + const zoom = Number(this.getAttribute("zoom") || 13); this.#map.setView(coordinates, zoom); - this.#map.on('zoom', this.handleEvent); - this.#map.on('moveend', this.handleEvent); + this.#map.on("zoom", this.handleEvent); + this.#map.on("moveend", this.handleEvent); } handleEvent(event: LeafletEvent) { switch (event.type) { - case 'zoom': - case 'moveend': { + case "zoom": + case "moveend": { this.dispatchEvent(new RecenterEvent(this.#map.getCenter())); break; } diff --git a/tsconfig.json b/tsconfig.json deleted file mode 100644 index 2b7ec13..0000000 --- a/tsconfig.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "compilerOptions": { - "target": "ESNext", - "useDefineForClassFields": false, - "module": "ESNext", - "lib": ["ESNext", "DOM", "DOM.Iterable"], - "skipLibCheck": true, - - /* Bundler mode */ - "moduleResolution": "bundler", - "resolveJsonModule": true, - "isolatedModules": true, - "declaration": true, - - /* Linting */ - "strict": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "noFallthroughCasesInSwitch": true, - "experimentalDecorators": true, - - /* Transpile */ - "outDir": "dist" - }, - "include": ["src"] -}