mirror of
https://github.com/CharaChorder/DeviceManager.git
synced 2026-01-21 17:32:41 +00:00
feat: code sandbox
[deploy]
This commit is contained in:
@@ -57,6 +57,8 @@ const config: IconsConfig = {
|
|||||||
"light_mode",
|
"light_mode",
|
||||||
"palette",
|
"palette",
|
||||||
"translate",
|
"translate",
|
||||||
|
"play_arrow",
|
||||||
|
"extension",
|
||||||
],
|
],
|
||||||
codePoints: {
|
codePoints: {
|
||||||
speed: "e9e4",
|
speed: "e9e4",
|
||||||
|
|||||||
168
package-lock.json
generated
168
package-lock.json
generated
@@ -10,6 +10,8 @@
|
|||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "AGPL-3.0-or-later",
|
"license": "AGPL-3.0-or-later",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@codemirror/lang-javascript": "^6.1.9",
|
||||||
|
"@codemirror/state": "^6.2.1",
|
||||||
"@fontsource-variable/material-symbols-rounded": "^5.0.4",
|
"@fontsource-variable/material-symbols-rounded": "^5.0.4",
|
||||||
"@fontsource-variable/noto-sans-mono": "^5.0.4",
|
"@fontsource-variable/noto-sans-mono": "^5.0.4",
|
||||||
"@material/material-color-utilities": "^0.2.7",
|
"@material/material-color-utilities": "^0.2.7",
|
||||||
@@ -23,6 +25,7 @@
|
|||||||
"@types/w3c-web-serial": "^1.0.3",
|
"@types/w3c-web-serial": "^1.0.3",
|
||||||
"@vite-pwa/sveltekit": "^0.2.5",
|
"@vite-pwa/sveltekit": "^0.2.5",
|
||||||
"autoprefixer": "^10.4.14",
|
"autoprefixer": "^10.4.14",
|
||||||
|
"codemirror": "^6.0.1",
|
||||||
"cypress": "^12.17.1",
|
"cypress": "^12.17.1",
|
||||||
"flexsearch": "^0.7.31",
|
"flexsearch": "^0.7.31",
|
||||||
"fontkit": "^2.0.2",
|
"fontkit": "^2.0.2",
|
||||||
@@ -1763,6 +1766,104 @@
|
|||||||
"node": ">=6.9.0"
|
"node": ">=6.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@codemirror/autocomplete": {
|
||||||
|
"version": "6.9.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.9.0.tgz",
|
||||||
|
"integrity": "sha512-Fbwm0V/Wn3BkEJZRhr0hi5BhCo5a7eBL6LYaliPjOSwCyfOpnjXY59HruSxOUNV+1OYer0Tgx1zRNQttjXyDog==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@codemirror/language": "^6.0.0",
|
||||||
|
"@codemirror/state": "^6.0.0",
|
||||||
|
"@codemirror/view": "^6.6.0",
|
||||||
|
"@lezer/common": "^1.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@codemirror/language": "^6.0.0",
|
||||||
|
"@codemirror/state": "^6.0.0",
|
||||||
|
"@codemirror/view": "^6.0.0",
|
||||||
|
"@lezer/common": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@codemirror/commands": {
|
||||||
|
"version": "6.2.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.2.4.tgz",
|
||||||
|
"integrity": "sha512-42lmDqVH0ttfilLShReLXsDfASKLXzfyC36bzwcqzox9PlHulMcsUOfHXNo2X2aFMVNUoQ7j+d4q5bnfseYoOA==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@codemirror/language": "^6.0.0",
|
||||||
|
"@codemirror/state": "^6.2.0",
|
||||||
|
"@codemirror/view": "^6.0.0",
|
||||||
|
"@lezer/common": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@codemirror/lang-javascript": {
|
||||||
|
"version": "6.1.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@codemirror/lang-javascript/-/lang-javascript-6.1.9.tgz",
|
||||||
|
"integrity": "sha512-z3jdkcqOEBT2txn2a87A0jSy6Te3679wg/U8QzMeftFt+4KA6QooMwfdFzJiuC3L6fXKfTXZcDocoaxMYfGz0w==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@codemirror/autocomplete": "^6.0.0",
|
||||||
|
"@codemirror/language": "^6.6.0",
|
||||||
|
"@codemirror/lint": "^6.0.0",
|
||||||
|
"@codemirror/state": "^6.0.0",
|
||||||
|
"@codemirror/view": "^6.0.0",
|
||||||
|
"@lezer/common": "^1.0.0",
|
||||||
|
"@lezer/javascript": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@codemirror/language": {
|
||||||
|
"version": "6.8.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.8.0.tgz",
|
||||||
|
"integrity": "sha512-r1paAyWOZkfY0RaYEZj3Kul+MiQTEbDvYqf8gPGaRvNneHXCmfSaAVFjwRUPlgxS8yflMxw2CTu6uCMp8R8A2g==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@codemirror/state": "^6.0.0",
|
||||||
|
"@codemirror/view": "^6.0.0",
|
||||||
|
"@lezer/common": "^1.0.0",
|
||||||
|
"@lezer/highlight": "^1.0.0",
|
||||||
|
"@lezer/lr": "^1.0.0",
|
||||||
|
"style-mod": "^4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@codemirror/lint": {
|
||||||
|
"version": "6.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.4.0.tgz",
|
||||||
|
"integrity": "sha512-6VZ44Ysh/Zn07xrGkdtNfmHCbGSHZzFBdzWi0pbd7chAQ/iUcpLGX99NYRZTa7Ugqg4kEHCqiHhcZnH0gLIgSg==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@codemirror/state": "^6.0.0",
|
||||||
|
"@codemirror/view": "^6.0.0",
|
||||||
|
"crelt": "^1.0.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@codemirror/search": {
|
||||||
|
"version": "6.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.5.0.tgz",
|
||||||
|
"integrity": "sha512-64/M40YeJPToKvGO6p3fijo2vwUEj4nACEAXElCaYQ50HrXSvRaK+NHEhSh73WFBGdvIdhrV+lL9PdJy2RfCYA==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@codemirror/state": "^6.0.0",
|
||||||
|
"@codemirror/view": "^6.0.0",
|
||||||
|
"crelt": "^1.0.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@codemirror/state": {
|
||||||
|
"version": "6.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.2.1.tgz",
|
||||||
|
"integrity": "sha512-RupHSZ8+OjNT38zU9fKH2sv+Dnlr8Eb8sl4NOnnqz95mCFTZUaiRP8Xv5MeeaG0px2b8Bnfe7YGwCV3nsBhbuw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/@codemirror/view": {
|
||||||
|
"version": "6.16.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.16.0.tgz",
|
||||||
|
"integrity": "sha512-1Z2HkvkC3KR/oEZVuW9Ivmp8TWLzGEd8T8TA04TTwPvqogfkHBdYSlflytDOqmkUxM2d1ywTg7X2dU5mC+SXvg==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@codemirror/state": "^6.1.4",
|
||||||
|
"style-mod": "^4.0.0",
|
||||||
|
"w3c-keyname": "^2.2.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@colors/colors": {
|
"node_modules/@colors/colors": {
|
||||||
"version": "1.5.0",
|
"version": "1.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz",
|
||||||
@@ -2466,6 +2567,40 @@
|
|||||||
"integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==",
|
"integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/@lezer/common": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-JH4wAXCgUOcCGNekQPLhVeUtIqjH0yPBs7vvUdSjyQama9618IOKFJwkv2kcqdhF0my8hQEgCTEJU0GIgnahvA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/@lezer/highlight": {
|
||||||
|
"version": "1.1.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.1.6.tgz",
|
||||||
|
"integrity": "sha512-cmSJYa2us+r3SePpRCjN5ymCqCPv+zyXmDl0ciWtVaNiORT/MxM7ZgOMQZADD0o51qOaOg24qc/zBViOIwAjJg==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@lezer/common": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@lezer/javascript": {
|
||||||
|
"version": "1.4.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@lezer/javascript/-/javascript-1.4.5.tgz",
|
||||||
|
"integrity": "sha512-FmBUHz8K1V22DgjTd6SrIG9owbzOYZ1t3rY6vGEmw+e2RVBd7sqjM8uXEVRFmfxKFn1Mx2ABJehHjrN3G2ZpmA==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@lezer/highlight": "^1.1.3",
|
||||||
|
"@lezer/lr": "^1.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@lezer/lr": {
|
||||||
|
"version": "1.3.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.3.9.tgz",
|
||||||
|
"integrity": "sha512-XPz6dzuTHlnsbA5M2DZgjflNQ+9Hi5Swhic0RULdp3oOs3rh6bqGZolosVqN/fQIT8uNiepzINJDnS39oweTHQ==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@lezer/common": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@material/material-color-utilities": {
|
"node_modules/@material/material-color-utilities": {
|
||||||
"version": "0.2.7",
|
"version": "0.2.7",
|
||||||
"resolved": "https://registry.npmjs.org/@material/material-color-utilities/-/material-color-utilities-0.2.7.tgz",
|
"resolved": "https://registry.npmjs.org/@material/material-color-utilities/-/material-color-utilities-0.2.7.tgz",
|
||||||
@@ -3999,6 +4134,21 @@
|
|||||||
"periscopic": "^3.1.0"
|
"periscopic": "^3.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/codemirror": {
|
||||||
|
"version": "6.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/codemirror/-/codemirror-6.0.1.tgz",
|
||||||
|
"integrity": "sha512-J8j+nZ+CdWmIeFIGXEFbFPtpiYacFMDR8GlHK3IyHQJMCaVRfGx9NT+Hxivv1ckLWPvNdZqndbr/7lVhrf/Svg==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@codemirror/autocomplete": "^6.0.0",
|
||||||
|
"@codemirror/commands": "^6.0.0",
|
||||||
|
"@codemirror/language": "^6.0.0",
|
||||||
|
"@codemirror/lint": "^6.0.0",
|
||||||
|
"@codemirror/search": "^6.0.0",
|
||||||
|
"@codemirror/state": "^6.0.0",
|
||||||
|
"@codemirror/view": "^6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/color-convert": {
|
"node_modules/color-convert": {
|
||||||
"version": "1.9.3",
|
"version": "1.9.3",
|
||||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
|
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
|
||||||
@@ -4117,6 +4267,12 @@
|
|||||||
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
|
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/crelt": {
|
||||||
|
"version": "1.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz",
|
||||||
|
"integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/cross-spawn": {
|
"node_modules/cross-spawn": {
|
||||||
"version": "7.0.3",
|
"version": "7.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
|
||||||
@@ -9348,6 +9504,12 @@
|
|||||||
"url": "https://github.com/sponsors/antfu"
|
"url": "https://github.com/sponsors/antfu"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/style-mod": {
|
||||||
|
"version": "4.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.0.3.tgz",
|
||||||
|
"integrity": "sha512-78Jv8kYJdjbvRwwijtCevYADfsI0lGzYJe4mMFdceO8l75DFFDoqBhR1jVDicDRRaX4//g1u9wKeo+ztc2h1Rw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/style-search": {
|
"node_modules/style-search": {
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/style-search/-/style-search-0.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/style-search/-/style-search-0.1.0.tgz",
|
||||||
@@ -10582,6 +10744,12 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/w3c-keyname": {
|
||||||
|
"version": "2.2.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz",
|
||||||
|
"integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/w3c-xmlserializer": {
|
"node_modules/w3c-xmlserializer": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz",
|
||||||
|
|||||||
@@ -25,6 +25,12 @@
|
|||||||
"@vite-pwa/sveltekit": "^0.2.5",
|
"@vite-pwa/sveltekit": "^0.2.5",
|
||||||
"@fontsource-variable/noto-sans-mono": "^5.0.4",
|
"@fontsource-variable/noto-sans-mono": "^5.0.4",
|
||||||
"@fontsource-variable/material-symbols-rounded": "^5.0.4",
|
"@fontsource-variable/material-symbols-rounded": "^5.0.4",
|
||||||
|
"@codemirror/state": "^6.2.1",
|
||||||
|
"@codemirror/commands": "^6.2.4",
|
||||||
|
"@codemirror/autocomplete": "^6.9.0",
|
||||||
|
"@codemirror/lang-javascript": "^6.1.9",
|
||||||
|
"@codemirror/language": "^6.8.0",
|
||||||
|
"codemirror": "^6.0.1",
|
||||||
"stylelint": "^15.9.0",
|
"stylelint": "^15.9.0",
|
||||||
"stylelint-config-standard-scss": "^10.0.0",
|
"stylelint-config-standard-scss": "^10.0.0",
|
||||||
"stylelint-config-prettier-scss": "^1.0.0",
|
"stylelint-config-prettier-scss": "^1.0.0",
|
||||||
|
|||||||
@@ -58,6 +58,11 @@ const de = {
|
|||||||
TITLE: "Einstellungen",
|
TITLE: "Einstellungen",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
plugin: {
|
||||||
|
editor: {
|
||||||
|
RUN: "Ausführen",
|
||||||
|
},
|
||||||
|
},
|
||||||
} satisfies Translation
|
} satisfies Translation
|
||||||
|
|
||||||
export default de
|
export default de
|
||||||
|
|||||||
@@ -56,6 +56,11 @@ const en = {
|
|||||||
TITLE: "Settings",
|
TITLE: "Settings",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
plugin: {
|
||||||
|
editor: {
|
||||||
|
RUN: "Run",
|
||||||
|
},
|
||||||
|
},
|
||||||
} satisfies BaseTranslation
|
} satisfies BaseTranslation
|
||||||
|
|
||||||
export default en
|
export default en
|
||||||
|
|||||||
186
src/routes/plugin/+page.svelte
Normal file
186
src/routes/plugin/+page.svelte
Normal file
@@ -0,0 +1,186 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import {KEYMAP_CODES} from "$lib/serial/keymap-codes"
|
||||||
|
import {onMount} from "svelte"
|
||||||
|
import {basicSetup, EditorView} from "codemirror"
|
||||||
|
import {javascript, javascriptLanguage} from "@codemirror/lang-javascript"
|
||||||
|
import {defaultKeymap} from "@codemirror/commands"
|
||||||
|
import {keymap} from "@codemirror/view"
|
||||||
|
import {HighlightStyle, syntaxHighlighting} from "@codemirror/language"
|
||||||
|
import {tags} from "@lezer/highlight"
|
||||||
|
import LL from "../../i18n/i18n-svelte"
|
||||||
|
import type {CompletionContext} from "@codemirror/autocomplete"
|
||||||
|
import {serialPort} from "$lib/serial/connection"
|
||||||
|
import type {CharaDevice} from "$lib/serial/device"
|
||||||
|
import examplePlugin from "./example-plugin.js?raw"
|
||||||
|
|
||||||
|
let theme = EditorView.baseTheme({
|
||||||
|
".cm-editor .cm-content": {
|
||||||
|
fontFamily: '"Noto Sans Mono", monospace',
|
||||||
|
},
|
||||||
|
".cm-FoldPlaceholder": {
|
||||||
|
backgroundColor: "var(--md-sys-color-surface-variant)",
|
||||||
|
color: "var(--md-sys-color-on-surface-variant)",
|
||||||
|
},
|
||||||
|
".cm-gutters": {
|
||||||
|
backgroundColor: "var(--md-sys-color-surface-variant)",
|
||||||
|
color: "var(--md-sys-color-on-surface-variant)",
|
||||||
|
borderColor: "var(--md-sys-color-outline)",
|
||||||
|
},
|
||||||
|
".cm-activeLineGutter": {
|
||||||
|
backgroundColor: "var(--md-sys-color-tertiary)",
|
||||||
|
color: "var(--md-sys-color-on-tertiary)",
|
||||||
|
},
|
||||||
|
".cm-activeLine": {
|
||||||
|
backgroundColor: "transparent",
|
||||||
|
},
|
||||||
|
".cm-cursor": {
|
||||||
|
borderColor: "var(--md-sys-color-on-background)",
|
||||||
|
},
|
||||||
|
".cm-selectionBackground": {
|
||||||
|
background: "transparent !important",
|
||||||
|
backdropFilter: "invert(0.3)",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
const highlightStyle = HighlightStyle.define(
|
||||||
|
[
|
||||||
|
{tag: tags.keyword, color: "var(--md-sys-color-primary)"},
|
||||||
|
{tag: tags.number, color: "var(--md-sys-color-secondary)"},
|
||||||
|
{tag: tags.string, color: "var(--md-sys-color-tertiary)"},
|
||||||
|
{tag: tags.comment, color: "var(--md-sys-color-on-background)", opacity: 0.6},
|
||||||
|
],
|
||||||
|
{
|
||||||
|
all: {fontFamily: '"Noto Sans Mono", monospace', fontSize: "14px"},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
const completion = javascriptLanguage.data.of({
|
||||||
|
autocomplete: function completeGlobals(context: CompletionContext) {
|
||||||
|
if (context.matchBefore(/Chara\./)) {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
editorView = new EditorView({
|
||||||
|
extensions: [
|
||||||
|
basicSetup,
|
||||||
|
javascript(),
|
||||||
|
keymap.of(defaultKeymap),
|
||||||
|
theme,
|
||||||
|
syntaxHighlighting(highlightStyle),
|
||||||
|
completion,
|
||||||
|
],
|
||||||
|
parent: editor,
|
||||||
|
doc: examplePlugin,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
const charaMethods = [
|
||||||
|
"reboot",
|
||||||
|
"bootloader",
|
||||||
|
"getRamBytesAvailable",
|
||||||
|
"getSetting",
|
||||||
|
"setSetting",
|
||||||
|
"getLayoutKey",
|
||||||
|
"setLayoutKey",
|
||||||
|
"deleteChord",
|
||||||
|
"setChord",
|
||||||
|
"getChordPhrase",
|
||||||
|
"getChordCount",
|
||||||
|
"getChord",
|
||||||
|
"send",
|
||||||
|
] satisfies Array<keyof CharaDevice>
|
||||||
|
$: channels = $serialPort
|
||||||
|
? ({
|
||||||
|
getVersion: async (..._args: unknown[]) => $serialPort.version,
|
||||||
|
getDevice: async (..._args: unknown[]) => $serialPort.device,
|
||||||
|
commit: async (..._args: unknown[]) => {
|
||||||
|
if (
|
||||||
|
confirm(
|
||||||
|
"Perform a commit? Settings are already applied until the next reboot.\n\n" +
|
||||||
|
"Excessive commits can lead to premature breakdowns, as the settings storage is only rated for 10,000-25,000 commits.\n\n" +
|
||||||
|
"Click OK to perform the commit anyways.",
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return $serialPort.commit()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
...Object.fromEntries(charaMethods.map(it => [it, $serialPort[it].bind($serialPort)] as const)),
|
||||||
|
} satisfies Record<string, Function>)
|
||||||
|
: ({} as any)
|
||||||
|
|
||||||
|
async function onMessage(event: MessageEvent) {
|
||||||
|
if (event.origin !== "null" || event.source !== frame.contentWindow) return
|
||||||
|
|
||||||
|
const [channel, params] = event.data
|
||||||
|
const response = channels[channel as keyof typeof channels](...params)
|
||||||
|
frame.contentWindow!.postMessage({response: await response}, "*")
|
||||||
|
}
|
||||||
|
|
||||||
|
function runPlugin() {
|
||||||
|
frame.contentWindow?.postMessage(
|
||||||
|
{
|
||||||
|
actionCodes: KEYMAP_CODES,
|
||||||
|
script: editorView.state.doc.toString(),
|
||||||
|
charaChannels: Object.keys(channels),
|
||||||
|
},
|
||||||
|
"*",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
let frame: HTMLIFrameElement
|
||||||
|
let editor: HTMLDivElement
|
||||||
|
let editorView: EditorView
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svelte:window on:message={onMessage} />
|
||||||
|
<section>
|
||||||
|
<button on:click={runPlugin}><span class="icon">play_arrow</span>{$LL.plugin.editor.RUN()}</button>
|
||||||
|
<div class="editor-root" bind:this={editor} />
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<iframe
|
||||||
|
aria-hidden="true"
|
||||||
|
title="code sandbox"
|
||||||
|
bind:this={frame}
|
||||||
|
src="/sandbox.html"
|
||||||
|
sandbox="allow-scripts"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
section {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
iframe {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
width: min-content;
|
||||||
|
padding-inline-start: 0;
|
||||||
|
padding-inline-end: 8px;
|
||||||
|
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: var(--md-sys-color-on-primary);
|
||||||
|
|
||||||
|
background: var(--md-sys-color-primary);
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-root {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
31
src/routes/plugin/example-plugin.js
Normal file
31
src/routes/plugin/example-plugin.js
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
/*******************************
|
||||||
|
* HOLD UP AND READ THIS FIRST *
|
||||||
|
*******************************
|
||||||
|
*
|
||||||
|
* Chara devices have a LIMITED number of commits.
|
||||||
|
* calling `Chara.commit()` can be a dangerous operation, which is why a confirmation dialog will be shown.
|
||||||
|
* Devices are only rated for 10,000-25,000 commits, exceeding that limit may result in premature breakdowns.
|
||||||
|
* `Chara.setSetting` or `Chara.setLayoutKey` is not affected by this, they last however only until the next boot.
|
||||||
|
*
|
||||||
|
* Chord writing is more forgiving, but keep in mind that excessive large-scale writing can still damage the device.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
const count = await Chara.getChordCount() // => 499
|
||||||
|
const chord = await Chara.getChord(2) // => {actions: [1, 2, 3], phrase: [4, 5, 6]}
|
||||||
|
|
||||||
|
const setting = await Chara.getSetting(5) // => 0
|
||||||
|
|
||||||
|
// This, for example, would return all chords
|
||||||
|
const chords = []
|
||||||
|
for (let i = 0; i < count; i++) {
|
||||||
|
chords.push(await Chara.getChord(i))
|
||||||
|
}
|
||||||
|
|
||||||
|
// You can also print values to the browser console (F12)
|
||||||
|
console.log("Chords:", chords)
|
||||||
|
|
||||||
|
// You can access the actions by ID!
|
||||||
|
Actions.SPACE // => {id: "SPACE", code: 32, icon: "space_bar", description: ...}
|
||||||
|
Actions[32] // This also works
|
||||||
|
Actions[0x20] // Or this!
|
||||||
42
static/sandbox.html
Normal file
42
static/sandbox.html
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>iFrame Sandbox</title>
|
||||||
|
<script>
|
||||||
|
let ongoingRequest
|
||||||
|
let resolveRequest
|
||||||
|
let source
|
||||||
|
async function post(channel, args) {
|
||||||
|
while (ongoingRequest) {
|
||||||
|
await ongoingRequest
|
||||||
|
}
|
||||||
|
ongoingRequest = new Promise(resolve => {
|
||||||
|
resolveRequest = resolve
|
||||||
|
source.postMessage([channel, args], "*")
|
||||||
|
})
|
||||||
|
ongoingRequest.then(() => {
|
||||||
|
ongoingRequest = undefined
|
||||||
|
})
|
||||||
|
return ongoingRequest
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener('message', event => {
|
||||||
|
if ("response" in event.data) {
|
||||||
|
resolveRequest(event.data.response)
|
||||||
|
} else {
|
||||||
|
source = event.source
|
||||||
|
|
||||||
|
var Action = event.data.actionCodes
|
||||||
|
Object.assign(Action, Object.fromEntries(Object.values(event.data.actionCodes).filter(it => !!it.id).map(it => [it.id, it])))
|
||||||
|
|
||||||
|
var Chara = {}
|
||||||
|
for (const fn of event.data.charaChannels) {
|
||||||
|
Chara[fn] = (...args) => post(fn, args)
|
||||||
|
}
|
||||||
|
|
||||||
|
eval(`(async function(){${event.data.script}})()`)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
</html>
|
||||||
Reference in New Issue
Block a user