diff --git a/.dockerignore b/.dockerignore index ffa7578..d2cc3c6 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,3 +1,3 @@ Dockerfile -Makefile -README.md \ No newline at end of file +dist/ +node_modules/ \ No newline at end of file diff --git a/.gitignore b/.gitignore index 66cfb9f..2d72753 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ node_modules/ -dist/*.{html,js,css,ico} +dist/ +examples/ \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 021334c..06d4871 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,2 +1,9 @@ -FROM nginx -COPY ./src/index.html ./src/script.js ./src/main.css /usr/share/nginx/html +FROM node:20.16.0 AS build-stage +WORKDIR /build +COPY package.json package-lock.json ./ +RUN npm install +COPY . . +RUN make build + +FROM nginx AS runtime-stage +COPY --from=build-stage /build/dist/* /usr/share/nginx/html/ diff --git a/Makefile b/Makefile index 06aa75b..55d978d 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ serve: - python3 -m http.server 8080 + npx vite .PHONY: serve watch: @@ -7,7 +7,7 @@ watch: .PHONY: watch build: - echo "Not implemented yet..." + npx vite build --emptyOutDir .PHONY: build image: diff --git a/package-lock.json b/package-lock.json index 73526c1..a4255f5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,167 @@ "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", "dev": true }, + "@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==", + "dev": true, + "optional": true + }, + "@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==", + "dev": true, + "optional": true + }, + "@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==", + "dev": true, + "optional": true + }, + "@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==", + "dev": true, + "optional": true + }, + "@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==", + "dev": true, + "optional": true + }, + "@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==", + "dev": true, + "optional": true + }, + "@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==", + "dev": true, + "optional": true + }, + "@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==", + "dev": true, + "optional": true + }, + "@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==", + "dev": true, + "optional": true + }, + "@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==", + "dev": true, + "optional": true + }, + "@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==", + "dev": true, + "optional": true + }, + "@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==", + "dev": true, + "optional": true + }, + "@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==", + "dev": true, + "optional": true + }, + "@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==", + "dev": true, + "optional": true + }, + "@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==", + "dev": true, + "optional": true + }, + "@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "dev": true, + "optional": true + }, + "@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==", + "dev": true, + "optional": true + }, + "@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "dev": true, + "optional": true + }, + "@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "dev": true, + "optional": true + }, + "@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==", + "dev": true, + "optional": true + }, + "@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==", + "dev": true, + "optional": true + }, + "@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==", + "dev": true, + "optional": true + }, + "@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==", + "dev": true, + "optional": true + }, "@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -94,6 +255,124 @@ "dev": true, "optional": true }, + "@rollup/rollup-android-arm-eabi": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.20.0.tgz", + "integrity": "sha512-TSpWzflCc4VGAUJZlPpgAJE1+V60MePDQnBd7PPkpuEmOy8i87aL6tinFGKBFKuEDikYpig72QzdT3QPYIi+oA==", + "dev": true, + "optional": true + }, + "@rollup/rollup-android-arm64": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.20.0.tgz", + "integrity": "sha512-u00Ro/nok7oGzVuh/FMYfNoGqxU5CPWz1mxV85S2w9LxHR8OoMQBuSk+3BKVIDYgkpeOET5yXkx90OYFc+ytpQ==", + "dev": true, + "optional": true + }, + "@rollup/rollup-darwin-arm64": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.20.0.tgz", + "integrity": "sha512-uFVfvzvsdGtlSLuL0ZlvPJvl6ZmrH4CBwLGEFPe7hUmf7htGAN+aXo43R/V6LATyxlKVC/m6UsLb7jbG+LG39Q==", + "dev": true, + "optional": true + }, + "@rollup/rollup-darwin-x64": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.20.0.tgz", + "integrity": "sha512-xbrMDdlev53vNXexEa6l0LffojxhqDTBeL+VUxuuIXys4x6xyvbKq5XqTXBCEUA8ty8iEJblHvFaWRJTk/icAQ==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.20.0.tgz", + "integrity": "sha512-jMYvxZwGmoHFBTbr12Xc6wOdc2xA5tF5F2q6t7Rcfab68TT0n+r7dgawD4qhPEvasDsVpQi+MgDzj2faOLsZjA==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-arm-musleabihf": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.20.0.tgz", + "integrity": "sha512-1asSTl4HKuIHIB1GcdFHNNZhxAYEdqML/MW4QmPS4G0ivbEcBr1JKlFLKsIRqjSwOBkdItn3/ZDlyvZ/N6KPlw==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-arm64-gnu": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.20.0.tgz", + "integrity": "sha512-COBb8Bkx56KldOYJfMf6wKeYJrtJ9vEgBRAOkfw6Ens0tnmzPqvlpjZiLgkhg6cA3DGzCmLmmd319pmHvKWWlQ==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-arm64-musl": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.20.0.tgz", + "integrity": "sha512-+it+mBSyMslVQa8wSPvBx53fYuZK/oLTu5RJoXogjk6x7Q7sz1GNRsXWjn6SwyJm8E/oMjNVwPhmNdIjwP135Q==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.20.0.tgz", + "integrity": "sha512-yAMvqhPfGKsAxHN8I4+jE0CpLWD8cv4z7CK7BMmhjDuz606Q2tFKkWRY8bHR9JQXYcoLfopo5TTqzxgPUjUMfw==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-riscv64-gnu": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.20.0.tgz", + "integrity": "sha512-qmuxFpfmi/2SUkAw95TtNq/w/I7Gpjurx609OOOV7U4vhvUhBcftcmXwl3rqAek+ADBwSjIC4IVNLiszoj3dPA==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-s390x-gnu": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.20.0.tgz", + "integrity": "sha512-I0BtGXddHSHjV1mqTNkgUZLnS3WtsqebAXv11D5BZE/gfw5KoyXSAXVqyJximQXNvNzUo4GKlCK/dIwXlz+jlg==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-x64-gnu": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.20.0.tgz", + "integrity": "sha512-y+eoL2I3iphUg9tN9GB6ku1FA8kOfmF4oUEWhztDJ4KXJy1agk/9+pejOuZkNFhRwHAOxMsBPLbXPd6mJiCwew==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-x64-musl": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.20.0.tgz", + "integrity": "sha512-hM3nhW40kBNYUkZb/r9k2FKK+/MnKglX7UYd4ZUy5DJs8/sMsIbqWK2piZtVGE3kcXVNj3B2IrUYROJMMCikNg==", + "dev": true, + "optional": true + }, + "@rollup/rollup-win32-arm64-msvc": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.20.0.tgz", + "integrity": "sha512-psegMvP+Ik/Bg7QRJbv8w8PAytPA7Uo8fpFjXyCRHWm6Nt42L+JtoqH8eDQ5hRP7/XW2UiIriy1Z46jf0Oa1kA==", + "dev": true, + "optional": true + }, + "@rollup/rollup-win32-ia32-msvc": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.20.0.tgz", + "integrity": "sha512-GabekH3w4lgAJpVxkk7hUzUf2hICSQO0a/BLFA11/RMxQT92MabKAqyubzDZmMOC/hcJNlc+rrypzNzYl4Dx7A==", + "dev": true, + "optional": true + }, + "@rollup/rollup-win32-x64-msvc": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.20.0.tgz", + "integrity": "sha512-aJ1EJSuTdGnM6qbVC4B5DSmozPTqIag9fSzXRNNo+humQLG89XpPgdt16Ia56ORD7s+H8Pmyx44uczDQ0yDzpg==", + "dev": true, + "optional": true + }, + "@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 + }, "ansi-regex": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", @@ -128,6 +407,20 @@ "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", "dev": true }, + "autoprefixer": { + "version": "10.4.20", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz", + "integrity": "sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==", + "dev": true, + "requires": { + "browserslist": "^4.23.3", + "caniuse-lite": "^1.0.30001646", + "fraction.js": "^4.3.7", + "normalize-range": "^0.1.2", + "picocolors": "^1.0.1", + "postcss-value-parser": "^4.2.0" + } + }, "balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -158,12 +451,30 @@ "fill-range": "^7.1.1" } }, + "browserslist": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.3.tgz", + "integrity": "sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30001646", + "electron-to-chromium": "^1.5.4", + "node-releases": "^2.0.18", + "update-browserslist-db": "^1.1.0" + } + }, "camelcase-css": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", "dev": true }, + "caniuse-lite": { + "version": "1.0.30001651", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001651.tgz", + "integrity": "sha512-9Cf+Xv1jJNe1xPZLGuUXLNkE1BoDkqRqYyFJ9TDYSqhduqA4hu4oR9HluGoWYQC/aj8WHjsGVV+bwkh0+tegRg==", + "dev": true + }, "chokidar": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", @@ -247,12 +558,55 @@ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", "dev": true }, + "electron-to-chromium": { + "version": "1.5.6", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.6.tgz", + "integrity": "sha512-jwXWsM5RPf6j9dPYzaorcBSUg6AiqocPEyMpkchkvntaH9HGfOOMZwxMJjDY/XEs3T5dM7uyH1VhRMkqUU9qVw==", + "dev": true + }, "emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", "dev": true }, + "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, + "requires": { + "@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" + } + }, + "escalade": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", + "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "dev": true + }, "fast-glob": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", @@ -305,6 +659,12 @@ "signal-exit": "^4.0.1" } }, + "fraction.js": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", + "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", + "dev": true + }, "fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -483,12 +843,24 @@ "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", "dev": true }, + "node-releases": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", + "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", + "dev": true + }, "normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true }, + "normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "dev": true + }, "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -668,6 +1040,32 @@ "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", "dev": true }, + "rollup": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.20.0.tgz", + "integrity": "sha512-6rbWBChcnSGzIlXeIdNIZTopKYad8ZG8ajhl78lGRLsI2rX8IkaotQhVas2Ma+GPxJav19wrSzvRvuiv0YKzWw==", + "dev": true, + "requires": { + "@rollup/rollup-android-arm-eabi": "4.20.0", + "@rollup/rollup-android-arm64": "4.20.0", + "@rollup/rollup-darwin-arm64": "4.20.0", + "@rollup/rollup-darwin-x64": "4.20.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.20.0", + "@rollup/rollup-linux-arm-musleabihf": "4.20.0", + "@rollup/rollup-linux-arm64-gnu": "4.20.0", + "@rollup/rollup-linux-arm64-musl": "4.20.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.20.0", + "@rollup/rollup-linux-riscv64-gnu": "4.20.0", + "@rollup/rollup-linux-s390x-gnu": "4.20.0", + "@rollup/rollup-linux-x64-gnu": "4.20.0", + "@rollup/rollup-linux-x64-musl": "4.20.0", + "@rollup/rollup-win32-arm64-msvc": "4.20.0", + "@rollup/rollup-win32-ia32-msvc": "4.20.0", + "@rollup/rollup-win32-x64-msvc": "4.20.0", + "@types/estree": "1.0.5", + "fsevents": "~2.3.2" + } + }, "run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -797,9 +1195,9 @@ "dev": true }, "tailwindcss": { - "version": "3.4.9", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.9.tgz", - "integrity": "sha512-1SEOvRr6sSdV5IDf9iC+NU4dhwdqzF4zKKq3sAbasUWHEM6lsMhX+eNN5gkPx1BvLFEnZQEUFbXnGj8Qlp83Pg==", + "version": "3.4.10", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.10.tgz", + "integrity": "sha512-KWZkVPm7yJRhdu4SRSl9d4AK2wM3a50UsvgHZO7xY77NQr2V+fIrEuoDGQcbvswWvFGbS2f6e+jC/6WJm1Dl0w==", "dev": true, "requires": { "@alloc/quick-lru": "^5.2.0", @@ -859,12 +1257,34 @@ "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", "dev": true }, + "update-browserslist-db": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz", + "integrity": "sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==", + "dev": true, + "requires": { + "escalade": "^3.1.2", + "picocolors": "^1.0.1" + } + }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "dev": true }, + "vite": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.0.tgz", + "integrity": "sha512-5xokfMX0PIiwCMCMb9ZJcMyh5wbBun0zUzKib+L65vAZ8GY9ePZMXxFrHbr/Kyll2+LSCY7xtERPpxkBDKngwg==", + "dev": true, + "requires": { + "esbuild": "^0.21.3", + "fsevents": "~2.3.3", + "postcss": "^8.4.40", + "rollup": "^4.13.0" + } + }, "which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/package.json b/package.json index b93902c..f8b40c4 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,8 @@ { "devDependencies": { - "tailwindcss": "^3.4.9" + "autoprefixer": "^10.4.20", + "postcss": "^8.4.41", + "tailwindcss": "^3.4.10", + "vite": "^5.4.0" } -} +} \ No newline at end of file diff --git a/postcss.config.js b/postcss.config.js new file mode 100644 index 0000000..a42e12c --- /dev/null +++ b/postcss.config.js @@ -0,0 +1,7 @@ +// postcss.config.js +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; diff --git a/src/app.js b/src/app.js new file mode 100644 index 0000000..0f6f05b --- /dev/null +++ b/src/app.js @@ -0,0 +1,10 @@ +import * as globals from './globals.js'; +import { setupEventListeners } from './eventListeners.js'; +import { updateSerialSelect } from './uiHelpers.js'; + +document.addEventListener('DOMContentLoaded', async () => { + const ports = await navigator.serial.getPorts(); + globals.setSerialPorts(ports); + updateSerialSelect(ports); + setupEventListeners(); +}); \ No newline at end of file diff --git a/src/eventListeners.js b/src/eventListeners.js new file mode 100644 index 0000000..a7ca9a5 --- /dev/null +++ b/src/eventListeners.js @@ -0,0 +1,67 @@ +import * as globals from './globals.js'; +import { connectToSerialPort, transmitContents } from './serialCommunication.js'; +import { scrollToBottom, updateSerialSelect, onPortDisconnect } from './uiHelpers.js'; + +const uPyKeyboardInterrupt = new Uint8Array([13, 3, 3]); + +export function setupEventListeners() { + globals.addPort.addEventListener('click', async () => { + const port = await navigator.serial.requestPort(); + globals.serialPorts.push(port); + updateSerialSelect(globals.serialPorts); + }); + + globals.autoscrollCheckbox.addEventListener('change', (e) => { + globals.setAutoscroll(e.target.checked); + if (globals.autoscroll) { + scrollToBottom(); + } + }); + + globals.clearButton.addEventListener('click', () => { + while (globals.scrollableElement.firstChild) { + globals.scrollableElement.removeChild(globals.scrollableElement.firstChild); + } + }); + + globals.connectButton.addEventListener('click', async () => { + const selectedPort = globals.serialPorts[globals.select.selectedIndex]; + const baudRate = Math.round(globals.baud.value); + if (!globals.isPortConnected) { + console.log("selected port: ", selectedPort) + console.log("Baud Rate: ", baudRate) + await connectToSerialPort(selectedPort, baudRate); + } else { + console.log("Disconnecting from port...") + globals.setPortConnected(false); + onPortDisconnect(); + } + }); + + globals.refreshPorts.addEventListener('click', async () => { + const ports = await navigator.serial.getPorts(); + console.log(ports) + globals.setSerialPorts(ports) + updateSerialSelect(globals.serialPorts) + }); + + globals.transmitButton.addEventListener('click', async (event) => { + transmitContents(globals.transmitInput.innerText) + globals.transmitInput.innerHTML = '' + }); + + globals.transmitInput.addEventListener('keydown', async (event) => { + if (event.key === 'c' && event.ctrlKey) { + event.preventDefault(); + console.log("Sending interrupt ", uPyKeyboardInterrupt) + await globals.writer.write(uPyKeyboardInterrupt); + } + }); + + globals.transmitInput.addEventListener('keyup', (event) => { + if (event.key === 'Enter' && !event.shiftKey) { + transmitContents(globals.transmitInput.innerText); + globals.transmitInput.innerHTML = '' + } + }); +} \ No newline at end of file diff --git a/src/globals.js b/src/globals.js new file mode 100644 index 0000000..f2c686d --- /dev/null +++ b/src/globals.js @@ -0,0 +1,71 @@ +export const addDeviceMessage = `Add a device...         `; +export const addPort = document.getElementById('add-port'); +export const autoscrollCheckbox = document.getElementById('autoscroll-checkbox'); +export const baud = document.getElementById('baud'); +export const clearButton = document.getElementById('clear-button'); +export const connectButton = document.getElementById('connect-button'); +export const refreshPorts = document.getElementById('refresh-ports'); +export const scrollableElement = document.getElementById('scrollable-element'); +export const select = document.getElementById('serial-select'); +export const transmitInput = document.querySelector('div[contenteditable="true"]'); +export const transmitButton = document.getElementById('transmit-button'); +export const uPyKeyboardInterrupt = new Uint8Array([13, 3, 3]); + +export let controller; +export let isPortConnected = false; +export let autoscroll = true; +export let serialPorts = []; +export let encoder; +export let reader; +export let writer; + +export function setController(newController) { + controller = newController +} + +export function setPortConnected(isConnected) { + isPortConnected = isConnected; +} + +export function setAutoscroll(isSet) { + autoscroll = isSet; + console.log('Set autoscroll: ', autoscroll) +} + +export function setSerialPorts(ports) { + serialPorts = ports + console.log('Set ports: ', serialPorts) +} + +export function getEncoder() { + return encoder; +} + +export function setEncoder(newEncoder) { + encoder = newEncoder; +} + +export function getReader() { + return reader; +} + +export function setReader(newReader) { + reader = newReader; +} + +export function getWriter() { + return writer; +} + +export function setWriter(newWriter) { + writer = newWriter; +} + +// expose some globals for debuggin in webconsole +if (typeof window !== 'undefined') { + window.wsc = { + isPortConnected, + autoscroll, + serialPorts, + }; +} \ No newline at end of file diff --git a/src/index.html b/src/index.html index 32b0cea..71df1ae 100644 --- a/src/index.html +++ b/src/index.html @@ -55,17 +55,6 @@
- - - -

@@ -77,29 +66,6 @@ Send
- - - -
- + diff --git a/src/script.js b/src/script.js deleted file mode 100644 index a79f767..0000000 --- a/src/script.js +++ /dev/null @@ -1,230 +0,0 @@ -// Globals -const addDeviceMessage = `Add a device...         ` -const addPort = document.getElementById('add-port') -const autoscrollCheckbox = document.getElementById('autoscroll-checkbox'); -const baud = document.getElementById('baud'); -const clearButton = document.getElementById('clear-button'); -const connectButton = document.getElementById('connect-button') -const refreshPorts = document.getElementById('refresh-ports'); -const scrollableElement = document.getElementById('scrollable-element'); -const select = document.getElementById('serial-select'); -const transmitInput = document.querySelector('div[contenteditable="true"]'); -const transmitButton = document.getElementById('transmit-button'); -const uPyKeyboardInterrupt = new Uint8Array([13, 3, 3]) - -let isPortConnected = false; -let controller; -let autoscroll = true; -let serialPorts = []; - -let encoder; -let reader; -let writer; - -// Event Listeners -addPort.addEventListener('click', async () => { - const port = await navigator.serial.requestPort(); - serialPorts.push(port); - updateSerialSelect(serialPorts); -}); - -autoscrollCheckbox.addEventListener('change', (e) => { - autoscroll = e.target.checked; - if (autoscroll) { - scrollToBottom(); - } -}); - -clearButton.addEventListener('click', () => { - while (scrollableElement.firstChild) { - scrollableElement.removeChild(scrollableElement.firstChild); - } -}); - -connectButton.addEventListener('click', async () => { - const selectedPort = serialPorts[select.selectedIndex] - const baudRate = Math.round(baud.value) - if (!isPortConnected) { - await connectToSerialPort(selectedPort, baudRate); - } else { - isPortConnected = false; - onPortDisconnect(); - } -}); - -refreshPorts.addEventListener('click', async () => { - ports = await navigator.serial.getPorts(); - serialPorts = ports - updateSerialSelect(ports) -}); - -transmitButton.addEventListener('click', async (event) => { - transmitContents(transmitInput.innerText) - transmitInput.innerHTML = '' -}); - -transmitInput.addEventListener('keydown', (event) => { - if (event.key === 'c' && event.ctrlKey) { - event.preventDefault(); - console.log("Sending interrupt ", uPyKeyboardInterrupt) - writer.write(uPyKeyboardInterrupt); - } -}); - -transmitInput.addEventListener('keyup', (event) => { - if (event.key === 'Enter' && !event.shiftKey) { - transmitContents(transmitInput.innerText); - transmitInput.innerHTML = '' - } -}); - - -// Functions -function transmitContents(input) { - encoded_string = encoder.encode(input + '\r') - console.log("Binary Contents: ", encoded_string) - writer.write(encoded_string) -} - - -function onPortConnect() { - connectButton.classList.remove('bg-emerald-500', 'hover:bg-emerald-600', 'focus:ring-emerald-500'); - connectButton.classList.add('bg-red-500', 'hover:bg-red-600', 'focus:ring-red-500'); - connectButton.textContent = 'Disconnect'; -} - -function onPortDisconnect() { - connectButton.classList.remove('bg-red-500', 'hover:bg-red-600', 'focus:ring-red-500'); - connectButton.classList.add('bg-emerald-500', 'hover:bg-emerald-600', 'focus:ring-emerald-500'); - connectButton.textContent = 'Connect'; -} - - -function buildPortOption(port) { - const option = document.createElement('option'); - option.value = port; - - try { - const info = port.getInfo(); - if (info && 'usbVendorId' in info && 'usbProductId' in info) { - const { usbVendorId, usbProductId } = info; - option.text = `Device ${usbVendorId}:${usbProductId}`; - } else { - console.error('getInfo() did not return expected properties:', info); - option.text = 'Unknown Device'; - } - } catch (error) { - console.error('Error retrieving port information:', error); - option.text = 'Unknown Device'; - } - - return option; -} - -function addText(text) { - const newText = document.createElement('p'); - newText.textContent = `${new Date().toLocaleTimeString()} ${text}`; - scrollableElement.appendChild(newText); - - if (autoscroll) { - scrollToBottom(); - } -} - -function scrollToBottom() { - scrollableElement.scrollTop = scrollableElement.scrollHeight; -} - -// Async Functions -async function connectToSerialPort(port, baud) { - await port.open({ baudRate: baud }); - onPortConnect() - - let buffer = '' - // The TextDecoderStream interface of the Encoding API converts a stream of text in a binary encoding, - // such as UTF-8 etc., to a stream of strings - // const textEncoder = new TextEncoderStream(); - // const writableStreamClosed = textEncoder.readable.pipeTo(port.writable); - // writer = textEncoder.writable.getWriter(); - - encoder = new TextEncoder() - writer = port.writable.getWriter(); - - const textDecoder = new TextDecoderStream(); - const readableStreamClosed = port.readable.pipeTo(textDecoder.writable); - reader = textDecoder.readable.getReader(); - - controller = new AbortController(); - const signal = controller.signal; - try { - isPortConnected = true; - while (isPortConnected) { - const { value, done } = await reader.read({ signal }); - if (done || !port.readable) { - break; - } - - buffer += value; - - while (buffer.includes('\n')) { - const newlineIndex = buffer.indexOf('\n'); - const line = buffer.slice(0, newlineIndex); - buffer = buffer.slice(newlineIndex + 1); - - addText(line); - } - } - } catch (error) { - console.error('Error reading data from serial port:', error); - } finally { - // Port cleanup - controller.abort(); - writer.releaseLock(); - reader.releaseLock(); - - // try { - // writer.close(); - // await writableStreamClosed; - // } catch (error) { - // console.error("Error encountered in writeableSteamClosed:", error) - // } - - try { - reader.cancel(); - } catch (error) { - console.error("Error encountered in readableStreamClosed:", error); - } - - try { - await readableStreamClosed; - } catch (error) { - } - - try { - await readableStreamClosed; - } catch (error) { - } - - try { - await port.close(); - } catch (error) { - console.error("Error encountered closing port:", error); - } - onPortDisconnect() - } -} - -async function updateSerialSelect(ports) { - if (ports.length < 1) { - const option = document.createElement('option'); - option.text = addDeviceMessage; - select.innerHTML = '' - select.appendChild(option) - return; - } - select.innerHTML = '' - ports.forEach(port => { - const option = buildPortOption(port) - select.appendChild(option); - }); -} \ No newline at end of file diff --git a/src/serialCommunication.js b/src/serialCommunication.js new file mode 100644 index 0000000..f436792 --- /dev/null +++ b/src/serialCommunication.js @@ -0,0 +1,73 @@ +import * as globals from './globals.js'; +import { onPortConnect, onPortDisconnect, addText } from './uiHelpers.js'; + +export async function connectToSerialPort(port, baud) { + console.log("Connecting to port: ", port); + await port.open({ baudRate: baud }); + onPortConnect() + + let buffer = '' + globals.setEncoder(new TextEncoder()); + globals.setWriter(port.writable.getWriter()); + + const textDecoder = new TextDecoderStream(); + const readableStreamClosed = port.readable.pipeTo(textDecoder.writable); + globals.setReader(textDecoder.readable.getReader()); + + globals.setController(new AbortController()); + const signal = globals.controller.signal; + try { + globals.setPortConnected(true); + while (globals.isPortConnected) { + const { value, done } = await globals.reader.read({ signal }); + if (done || !port.readable) { + break; + } + + buffer += value; + + while (buffer.includes('\n')) { + const newlineIndex = buffer.indexOf('\n'); + const line = buffer.slice(0, newlineIndex); + buffer = buffer.slice(newlineIndex + 1); + + addText(line); + } + } + console.log("Exiting serial print loop") + } catch (error) { + console.error('Error reading data from serial port:', error); + } finally { + // Port cleanup + console.log("Cleaning up port.") + globals.controller.abort(); + console.log("Releasing writer lock") + globals.writer.releaseLock(); + + // Since we are using a readableSteam we should call cancel instead of globals.reader.releaseLock(); + globals.reader.cancel().then(() => { + console.log('Stream canceled'); + }).catch(error => { + console.error('Error canceling the stream:', error); + }); + + try { + await readableStreamClosed; + } catch (error) { + } + + try { + await port.close(); + } catch (error) { + console.error("Error encountered closing port:", error); + } + console.log("Port closed.") + onPortDisconnect() + } +} + +export function transmitContents(input) { + const encoded_string = globals.encoder.encode(input + '\r') + console.log("Binary Contents: ", encoded_string); + globals.writer.write(encoded_string); +} diff --git a/src/uiHelpers.js b/src/uiHelpers.js new file mode 100644 index 0000000..3ea3e7c --- /dev/null +++ b/src/uiHelpers.js @@ -0,0 +1,63 @@ +import * as globals from './globals.js'; + +export function onPortConnect() { + globals.connectButton.classList.remove('bg-emerald-500', 'hover:bg-emerald-600', 'focus:ring-emerald-500'); + globals.connectButton.classList.add('bg-red-500', 'hover:bg-red-600', 'focus:ring-red-500'); + globals.connectButton.textContent = 'Disconnect'; +} + +export function onPortDisconnect() { + globals.connectButton.classList.remove('bg-red-500', 'hover:bg-red-600', 'focus:ring-red-500'); + globals.connectButton.classList.add('bg-emerald-500', 'hover:bg-emerald-600', 'focus:ring-emerald-500'); + globals.connectButton.textContent = 'Connect'; +} + +export function buildPortOption(port) { + const option = document.createElement('option'); + option.value = port; + + try { + const info = port.getInfo(); + if (info && 'usbVendorId' in info && 'usbProductId' in info) { + const { usbVendorId, usbProductId } = info; + option.text = `Device ${usbVendorId}:${usbProductId}`; + } else { + console.error('getInfo() did not return expected properties:', info); + option.text = 'Unknown Device'; + } + } catch (error) { + console.error('Error retrieving port information:', error); + option.text = 'Unknown Device'; + } + + return option; +} + +export function addText(text) { + const newText = document.createElement('p'); + newText.textContent = `${new Date().toLocaleTimeString()} ${text}`; + globals.scrollableElement.appendChild(newText); + + if (globals.autoscroll) { + scrollToBottom(); + } +} + +export function scrollToBottom() { + globals.scrollableElement.scrollTop = globals.scrollableElement.scrollHeight; +} + +export async function updateSerialSelect(ports) { + if (ports.length < 1) { + const option = document.createElement('option'); + option.text = globals.addDeviceMessage; + globals.select.innerHTML = '' + globals.select.appendChild(option) + return; + } + globals.select.innerHTML = '' + ports.forEach(port => { + const option = buildPortOption(port) + globals.select.appendChild(option); + }); +} \ No newline at end of file diff --git a/vite.config.js b/vite.config.js new file mode 100644 index 0000000..0d2d4fb --- /dev/null +++ b/vite.config.js @@ -0,0 +1,29 @@ +import { defineConfig } from 'vite'; +import { resolve } from 'path'; + +export default defineConfig({ + root: resolve(__dirname, 'src'), + base: './', + build: { + outDir: resolve(__dirname, 'dist'), + rollupOptions: { + input: { + main: resolve(__dirname, 'src/index.html'), + }, + output: { + entryFileNames: 'script.[hash].js', + chunkFileNames: 'chunk.[hash].js', + assetFileNames: '[name].[hash].[ext]', + }, + }, + minify: 'esbuild', + }, + css: { + postcss: { + plugins: [ + require('tailwindcss'), + require('autoprefixer'), + ], + }, + }, +});