├── .gitignore ├── README.md ├── index.html ├── package-lock.json ├── package.json ├── src ├── DrawContext.ts ├── TextRenderer.ts ├── controls.ts ├── gl.ts ├── main.ts ├── math.ts ├── program.ts ├── prop_dealer.ts ├── schemas.ts ├── tessellator.ts ├── types.ts ├── uis.ts ├── uiscreen.ts ├── vars.ts ├── vertex_format.ts └── vite-env.d.ts ├── static ├── ascii.png ├── features.txt ├── font.json ├── font.png ├── font │ └── codicon.ttf ├── grass.png ├── icon.png ├── icons │ ├── default_file.svg │ ├── file_type_image.svg │ ├── file_type_json.svg │ ├── file_type_text.svg │ └── github.png ├── lang │ └── en_us.json ├── shaders │ ├── grayscale_position_texture.fsh │ ├── grayscale_position_texture.vsh │ ├── grayscale_position_texture_color.fsh │ ├── grayscale_position_texture_color.vsh │ ├── position.fsh │ ├── position.vsh │ ├── position_color.fsh │ ├── position_color.vsh │ ├── position_texture.fsh │ ├── position_texture.vsh │ ├── position_texture_color.fsh │ └── position_texture_color.vsh └── ui │ ├── global_variables.json │ ├── play_screen.json │ ├── screen_defs.json │ ├── start_screen.json │ └── ui_defs.json ├── style.css ├── tsconfig.json └── vite.config.ts /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | dist -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JSON UI Editor 2 | A simple test editor for minecraft bugrock json ui to do simple tests. 3 | 4 | ## Disclaimer 5 | 6 | I'm not currently working on it and doesn't support most complex features. There's no point in using it. 7 | 8 | Just use minecraft itself to test your json ui data. It ain't that hard. 9 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | JSON UI Editor 10 | 11 | 12 | 13 | 39 |
40 |
41 |
42 |
43 | Explorer 44 |
45 | 46 | 47 |
48 |
49 |
50 |
51 |

Drag and drop files here

52 |

UI files have a schema. Hover over properties to see their description

53 |

Press Shift+S to reload UI

54 |

Press Shift+L to show debug lines

55 |
56 | 61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 | 69 |
70 |
71 |
72 |
73 |
74 | 75 | 80 |
81 |
82 | 83 | 84 |
85 |

60 FPS

86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ztest_aa", 3 | "version": "0.0.0", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "ztest_aa", 9 | "version": "0.0.0", 10 | "dependencies": { 11 | "monaco-editor": "^0.37.1", 12 | "strip-json-comments": "^5.0.0" 13 | }, 14 | "devDependencies": { 15 | "gh-pages": "^5.0.0", 16 | "typescript": "^5.0.2", 17 | "vite": "^4.3.2", 18 | "vite-plugin-monaco-editor": "^1.1.0" 19 | } 20 | }, 21 | "node_modules/@esbuild/android-arm": { 22 | "version": "0.17.18", 23 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.18.tgz", 24 | "integrity": "sha512-EmwL+vUBZJ7mhFCs5lA4ZimpUH3WMAoqvOIYhVQwdIgSpHC8ImHdsRyhHAVxpDYUSm0lWvd63z0XH1IlImS2Qw==", 25 | "cpu": [ 26 | "arm" 27 | ], 28 | "dev": true, 29 | "optional": true, 30 | "os": [ 31 | "android" 32 | ], 33 | "engines": { 34 | "node": ">=12" 35 | } 36 | }, 37 | "node_modules/@esbuild/android-arm64": { 38 | "version": "0.17.18", 39 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.18.tgz", 40 | "integrity": "sha512-/iq0aK0eeHgSC3z55ucMAHO05OIqmQehiGay8eP5l/5l+iEr4EIbh4/MI8xD9qRFjqzgkc0JkX0LculNC9mXBw==", 41 | "cpu": [ 42 | "arm64" 43 | ], 44 | "dev": true, 45 | "optional": true, 46 | "os": [ 47 | "android" 48 | ], 49 | "engines": { 50 | "node": ">=12" 51 | } 52 | }, 53 | "node_modules/@esbuild/android-x64": { 54 | "version": "0.17.18", 55 | "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.18.tgz", 56 | "integrity": "sha512-x+0efYNBF3NPW2Xc5bFOSFW7tTXdAcpfEg2nXmxegm4mJuVeS+i109m/7HMiOQ6M12aVGGFlqJX3RhNdYM2lWg==", 57 | "cpu": [ 58 | "x64" 59 | ], 60 | "dev": true, 61 | "optional": true, 62 | "os": [ 63 | "android" 64 | ], 65 | "engines": { 66 | "node": ">=12" 67 | } 68 | }, 69 | "node_modules/@esbuild/darwin-arm64": { 70 | "version": "0.17.18", 71 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.18.tgz", 72 | "integrity": "sha512-6tY+djEAdF48M1ONWnQb1C+6LiXrKjmqjzPNPWXhu/GzOHTHX2nh8Mo2ZAmBFg0kIodHhciEgUBtcYCAIjGbjQ==", 73 | "cpu": [ 74 | "arm64" 75 | ], 76 | "dev": true, 77 | "optional": true, 78 | "os": [ 79 | "darwin" 80 | ], 81 | "engines": { 82 | "node": ">=12" 83 | } 84 | }, 85 | "node_modules/@esbuild/darwin-x64": { 86 | "version": "0.17.18", 87 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.18.tgz", 88 | "integrity": "sha512-Qq84ykvLvya3dO49wVC9FFCNUfSrQJLbxhoQk/TE1r6MjHo3sFF2tlJCwMjhkBVq3/ahUisj7+EpRSz0/+8+9A==", 89 | "cpu": [ 90 | "x64" 91 | ], 92 | "dev": true, 93 | "optional": true, 94 | "os": [ 95 | "darwin" 96 | ], 97 | "engines": { 98 | "node": ">=12" 99 | } 100 | }, 101 | "node_modules/@esbuild/freebsd-arm64": { 102 | "version": "0.17.18", 103 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.18.tgz", 104 | "integrity": "sha512-fw/ZfxfAzuHfaQeMDhbzxp9mc+mHn1Y94VDHFHjGvt2Uxl10mT4CDavHm+/L9KG441t1QdABqkVYwakMUeyLRA==", 105 | "cpu": [ 106 | "arm64" 107 | ], 108 | "dev": true, 109 | "optional": true, 110 | "os": [ 111 | "freebsd" 112 | ], 113 | "engines": { 114 | "node": ">=12" 115 | } 116 | }, 117 | "node_modules/@esbuild/freebsd-x64": { 118 | "version": "0.17.18", 119 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.18.tgz", 120 | "integrity": "sha512-FQFbRtTaEi8ZBi/A6kxOC0V0E9B/97vPdYjY9NdawyLd4Qk5VD5g2pbWN2VR1c0xhzcJm74HWpObPszWC+qTew==", 121 | "cpu": [ 122 | "x64" 123 | ], 124 | "dev": true, 125 | "optional": true, 126 | "os": [ 127 | "freebsd" 128 | ], 129 | "engines": { 130 | "node": ">=12" 131 | } 132 | }, 133 | "node_modules/@esbuild/linux-arm": { 134 | "version": "0.17.18", 135 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.18.tgz", 136 | "integrity": "sha512-jW+UCM40LzHcouIaqv3e/oRs0JM76JfhHjCavPxMUti7VAPh8CaGSlS7cmyrdpzSk7A+8f0hiedHqr/LMnfijg==", 137 | "cpu": [ 138 | "arm" 139 | ], 140 | "dev": true, 141 | "optional": true, 142 | "os": [ 143 | "linux" 144 | ], 145 | "engines": { 146 | "node": ">=12" 147 | } 148 | }, 149 | "node_modules/@esbuild/linux-arm64": { 150 | "version": "0.17.18", 151 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.18.tgz", 152 | "integrity": "sha512-R7pZvQZFOY2sxUG8P6A21eq6q+eBv7JPQYIybHVf1XkQYC+lT7nDBdC7wWKTrbvMXKRaGudp/dzZCwL/863mZQ==", 153 | "cpu": [ 154 | "arm64" 155 | ], 156 | "dev": true, 157 | "optional": true, 158 | "os": [ 159 | "linux" 160 | ], 161 | "engines": { 162 | "node": ">=12" 163 | } 164 | }, 165 | "node_modules/@esbuild/linux-ia32": { 166 | "version": "0.17.18", 167 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.18.tgz", 168 | "integrity": "sha512-ygIMc3I7wxgXIxk6j3V00VlABIjq260i967Cp9BNAk5pOOpIXmd1RFQJQX9Io7KRsthDrQYrtcx7QCof4o3ZoQ==", 169 | "cpu": [ 170 | "ia32" 171 | ], 172 | "dev": true, 173 | "optional": true, 174 | "os": [ 175 | "linux" 176 | ], 177 | "engines": { 178 | "node": ">=12" 179 | } 180 | }, 181 | "node_modules/@esbuild/linux-loong64": { 182 | "version": "0.17.18", 183 | "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.18.tgz", 184 | "integrity": "sha512-bvPG+MyFs5ZlwYclCG1D744oHk1Pv7j8psF5TfYx7otCVmcJsEXgFEhQkbhNW8otDHL1a2KDINW20cfCgnzgMQ==", 185 | "cpu": [ 186 | "loong64" 187 | ], 188 | "dev": true, 189 | "optional": true, 190 | "os": [ 191 | "linux" 192 | ], 193 | "engines": { 194 | "node": ">=12" 195 | } 196 | }, 197 | "node_modules/@esbuild/linux-mips64el": { 198 | "version": "0.17.18", 199 | "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.18.tgz", 200 | "integrity": "sha512-oVqckATOAGuiUOa6wr8TXaVPSa+6IwVJrGidmNZS1cZVx0HqkTMkqFGD2HIx9H1RvOwFeWYdaYbdY6B89KUMxA==", 201 | "cpu": [ 202 | "mips64el" 203 | ], 204 | "dev": true, 205 | "optional": true, 206 | "os": [ 207 | "linux" 208 | ], 209 | "engines": { 210 | "node": ">=12" 211 | } 212 | }, 213 | "node_modules/@esbuild/linux-ppc64": { 214 | "version": "0.17.18", 215 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.18.tgz", 216 | "integrity": "sha512-3dLlQO+b/LnQNxgH4l9rqa2/IwRJVN9u/bK63FhOPB4xqiRqlQAU0qDU3JJuf0BmaH0yytTBdoSBHrb2jqc5qQ==", 217 | "cpu": [ 218 | "ppc64" 219 | ], 220 | "dev": true, 221 | "optional": true, 222 | "os": [ 223 | "linux" 224 | ], 225 | "engines": { 226 | "node": ">=12" 227 | } 228 | }, 229 | "node_modules/@esbuild/linux-riscv64": { 230 | "version": "0.17.18", 231 | "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.18.tgz", 232 | "integrity": "sha512-/x7leOyDPjZV3TcsdfrSI107zItVnsX1q2nho7hbbQoKnmoeUWjs+08rKKt4AUXju7+3aRZSsKrJtaRmsdL1xA==", 233 | "cpu": [ 234 | "riscv64" 235 | ], 236 | "dev": true, 237 | "optional": true, 238 | "os": [ 239 | "linux" 240 | ], 241 | "engines": { 242 | "node": ">=12" 243 | } 244 | }, 245 | "node_modules/@esbuild/linux-s390x": { 246 | "version": "0.17.18", 247 | "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.18.tgz", 248 | "integrity": "sha512-cX0I8Q9xQkL/6F5zWdYmVf5JSQt+ZfZD2bJudZrWD+4mnUvoZ3TDDXtDX2mUaq6upMFv9FlfIh4Gfun0tbGzuw==", 249 | "cpu": [ 250 | "s390x" 251 | ], 252 | "dev": true, 253 | "optional": true, 254 | "os": [ 255 | "linux" 256 | ], 257 | "engines": { 258 | "node": ">=12" 259 | } 260 | }, 261 | "node_modules/@esbuild/linux-x64": { 262 | "version": "0.17.18", 263 | "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.18.tgz", 264 | "integrity": "sha512-66RmRsPlYy4jFl0vG80GcNRdirx4nVWAzJmXkevgphP1qf4dsLQCpSKGM3DUQCojwU1hnepI63gNZdrr02wHUA==", 265 | "cpu": [ 266 | "x64" 267 | ], 268 | "dev": true, 269 | "optional": true, 270 | "os": [ 271 | "linux" 272 | ], 273 | "engines": { 274 | "node": ">=12" 275 | } 276 | }, 277 | "node_modules/@esbuild/netbsd-x64": { 278 | "version": "0.17.18", 279 | "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.18.tgz", 280 | "integrity": "sha512-95IRY7mI2yrkLlTLb1gpDxdC5WLC5mZDi+kA9dmM5XAGxCME0F8i4bYH4jZreaJ6lIZ0B8hTrweqG1fUyW7jbg==", 281 | "cpu": [ 282 | "x64" 283 | ], 284 | "dev": true, 285 | "optional": true, 286 | "os": [ 287 | "netbsd" 288 | ], 289 | "engines": { 290 | "node": ">=12" 291 | } 292 | }, 293 | "node_modules/@esbuild/openbsd-x64": { 294 | "version": "0.17.18", 295 | "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.18.tgz", 296 | "integrity": "sha512-WevVOgcng+8hSZ4Q3BKL3n1xTv5H6Nb53cBrtzzEjDbbnOmucEVcZeGCsCOi9bAOcDYEeBZbD2SJNBxlfP3qiA==", 297 | "cpu": [ 298 | "x64" 299 | ], 300 | "dev": true, 301 | "optional": true, 302 | "os": [ 303 | "openbsd" 304 | ], 305 | "engines": { 306 | "node": ">=12" 307 | } 308 | }, 309 | "node_modules/@esbuild/sunos-x64": { 310 | "version": "0.17.18", 311 | "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.18.tgz", 312 | "integrity": "sha512-Rzf4QfQagnwhQXVBS3BYUlxmEbcV7MY+BH5vfDZekU5eYpcffHSyjU8T0xucKVuOcdCsMo+Ur5wmgQJH2GfNrg==", 313 | "cpu": [ 314 | "x64" 315 | ], 316 | "dev": true, 317 | "optional": true, 318 | "os": [ 319 | "sunos" 320 | ], 321 | "engines": { 322 | "node": ">=12" 323 | } 324 | }, 325 | "node_modules/@esbuild/win32-arm64": { 326 | "version": "0.17.18", 327 | "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.18.tgz", 328 | "integrity": "sha512-Kb3Ko/KKaWhjeAm2YoT/cNZaHaD1Yk/pa3FTsmqo9uFh1D1Rfco7BBLIPdDOozrObj2sahslFuAQGvWbgWldAg==", 329 | "cpu": [ 330 | "arm64" 331 | ], 332 | "dev": true, 333 | "optional": true, 334 | "os": [ 335 | "win32" 336 | ], 337 | "engines": { 338 | "node": ">=12" 339 | } 340 | }, 341 | "node_modules/@esbuild/win32-ia32": { 342 | "version": "0.17.18", 343 | "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.18.tgz", 344 | "integrity": "sha512-0/xUMIdkVHwkvxfbd5+lfG7mHOf2FRrxNbPiKWg9C4fFrB8H0guClmaM3BFiRUYrznVoyxTIyC/Ou2B7QQSwmw==", 345 | "cpu": [ 346 | "ia32" 347 | ], 348 | "dev": true, 349 | "optional": true, 350 | "os": [ 351 | "win32" 352 | ], 353 | "engines": { 354 | "node": ">=12" 355 | } 356 | }, 357 | "node_modules/@esbuild/win32-x64": { 358 | "version": "0.17.18", 359 | "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.18.tgz", 360 | "integrity": "sha512-qU25Ma1I3NqTSHJUOKi9sAH1/Mzuvlke0ioMJRthLXKm7JiSKVwFghlGbDLOO2sARECGhja4xYfRAZNPAkooYg==", 361 | "cpu": [ 362 | "x64" 363 | ], 364 | "dev": true, 365 | "optional": true, 366 | "os": [ 367 | "win32" 368 | ], 369 | "engines": { 370 | "node": ">=12" 371 | } 372 | }, 373 | "node_modules/array-union": { 374 | "version": "1.0.2", 375 | "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", 376 | "integrity": "sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==", 377 | "dev": true, 378 | "dependencies": { 379 | "array-uniq": "^1.0.1" 380 | }, 381 | "engines": { 382 | "node": ">=0.10.0" 383 | } 384 | }, 385 | "node_modules/array-uniq": { 386 | "version": "1.0.3", 387 | "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", 388 | "integrity": "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==", 389 | "dev": true, 390 | "engines": { 391 | "node": ">=0.10.0" 392 | } 393 | }, 394 | "node_modules/async": { 395 | "version": "3.2.4", 396 | "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", 397 | "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==", 398 | "dev": true 399 | }, 400 | "node_modules/balanced-match": { 401 | "version": "1.0.2", 402 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 403 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", 404 | "dev": true 405 | }, 406 | "node_modules/brace-expansion": { 407 | "version": "1.1.11", 408 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 409 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 410 | "dev": true, 411 | "dependencies": { 412 | "balanced-match": "^1.0.0", 413 | "concat-map": "0.0.1" 414 | } 415 | }, 416 | "node_modules/commander": { 417 | "version": "2.20.3", 418 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", 419 | "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", 420 | "dev": true 421 | }, 422 | "node_modules/commondir": { 423 | "version": "1.0.1", 424 | "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", 425 | "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", 426 | "dev": true 427 | }, 428 | "node_modules/concat-map": { 429 | "version": "0.0.1", 430 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 431 | "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", 432 | "dev": true 433 | }, 434 | "node_modules/email-addresses": { 435 | "version": "5.0.0", 436 | "resolved": "https://registry.npmjs.org/email-addresses/-/email-addresses-5.0.0.tgz", 437 | "integrity": "sha512-4OIPYlA6JXqtVn8zpHpGiI7vE6EQOAg16aGnDMIAlZVinnoZ8208tW1hAbjWydgN/4PLTT9q+O1K6AH/vALJGw==", 438 | "dev": true 439 | }, 440 | "node_modules/esbuild": { 441 | "version": "0.17.18", 442 | "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.18.tgz", 443 | "integrity": "sha512-z1lix43jBs6UKjcZVKOw2xx69ffE2aG0PygLL5qJ9OS/gy0Ewd1gW/PUQIOIQGXBHWNywSc0floSKoMFF8aK2w==", 444 | "dev": true, 445 | "hasInstallScript": true, 446 | "bin": { 447 | "esbuild": "bin/esbuild" 448 | }, 449 | "engines": { 450 | "node": ">=12" 451 | }, 452 | "optionalDependencies": { 453 | "@esbuild/android-arm": "0.17.18", 454 | "@esbuild/android-arm64": "0.17.18", 455 | "@esbuild/android-x64": "0.17.18", 456 | "@esbuild/darwin-arm64": "0.17.18", 457 | "@esbuild/darwin-x64": "0.17.18", 458 | "@esbuild/freebsd-arm64": "0.17.18", 459 | "@esbuild/freebsd-x64": "0.17.18", 460 | "@esbuild/linux-arm": "0.17.18", 461 | "@esbuild/linux-arm64": "0.17.18", 462 | "@esbuild/linux-ia32": "0.17.18", 463 | "@esbuild/linux-loong64": "0.17.18", 464 | "@esbuild/linux-mips64el": "0.17.18", 465 | "@esbuild/linux-ppc64": "0.17.18", 466 | "@esbuild/linux-riscv64": "0.17.18", 467 | "@esbuild/linux-s390x": "0.17.18", 468 | "@esbuild/linux-x64": "0.17.18", 469 | "@esbuild/netbsd-x64": "0.17.18", 470 | "@esbuild/openbsd-x64": "0.17.18", 471 | "@esbuild/sunos-x64": "0.17.18", 472 | "@esbuild/win32-arm64": "0.17.18", 473 | "@esbuild/win32-ia32": "0.17.18", 474 | "@esbuild/win32-x64": "0.17.18" 475 | } 476 | }, 477 | "node_modules/escape-string-regexp": { 478 | "version": "1.0.5", 479 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 480 | "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", 481 | "dev": true, 482 | "engines": { 483 | "node": ">=0.8.0" 484 | } 485 | }, 486 | "node_modules/filename-reserved-regex": { 487 | "version": "2.0.0", 488 | "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz", 489 | "integrity": "sha512-lc1bnsSr4L4Bdif8Xb/qrtokGbq5zlsms/CYH8PP+WtCkGNF65DPiQY8vG3SakEdRn8Dlnm+gW/qWKKjS5sZzQ==", 490 | "dev": true, 491 | "engines": { 492 | "node": ">=4" 493 | } 494 | }, 495 | "node_modules/filenamify": { 496 | "version": "4.3.0", 497 | "resolved": "https://registry.npmjs.org/filenamify/-/filenamify-4.3.0.tgz", 498 | "integrity": "sha512-hcFKyUG57yWGAzu1CMt/dPzYZuv+jAJUT85bL8mrXvNe6hWj6yEHEc4EdcgiA6Z3oi1/9wXJdZPXF2dZNgwgOg==", 499 | "dev": true, 500 | "dependencies": { 501 | "filename-reserved-regex": "^2.0.0", 502 | "strip-outer": "^1.0.1", 503 | "trim-repeated": "^1.0.0" 504 | }, 505 | "engines": { 506 | "node": ">=8" 507 | }, 508 | "funding": { 509 | "url": "https://github.com/sponsors/sindresorhus" 510 | } 511 | }, 512 | "node_modules/find-cache-dir": { 513 | "version": "3.3.2", 514 | "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", 515 | "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", 516 | "dev": true, 517 | "dependencies": { 518 | "commondir": "^1.0.1", 519 | "make-dir": "^3.0.2", 520 | "pkg-dir": "^4.1.0" 521 | }, 522 | "engines": { 523 | "node": ">=8" 524 | }, 525 | "funding": { 526 | "url": "https://github.com/avajs/find-cache-dir?sponsor=1" 527 | } 528 | }, 529 | "node_modules/find-up": { 530 | "version": "4.1.0", 531 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", 532 | "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", 533 | "dev": true, 534 | "dependencies": { 535 | "locate-path": "^5.0.0", 536 | "path-exists": "^4.0.0" 537 | }, 538 | "engines": { 539 | "node": ">=8" 540 | } 541 | }, 542 | "node_modules/fs-extra": { 543 | "version": "8.1.0", 544 | "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", 545 | "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", 546 | "dev": true, 547 | "dependencies": { 548 | "graceful-fs": "^4.2.0", 549 | "jsonfile": "^4.0.0", 550 | "universalify": "^0.1.0" 551 | }, 552 | "engines": { 553 | "node": ">=6 <7 || >=8" 554 | } 555 | }, 556 | "node_modules/fs.realpath": { 557 | "version": "1.0.0", 558 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 559 | "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", 560 | "dev": true 561 | }, 562 | "node_modules/fsevents": { 563 | "version": "2.3.2", 564 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", 565 | "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", 566 | "dev": true, 567 | "hasInstallScript": true, 568 | "optional": true, 569 | "os": [ 570 | "darwin" 571 | ], 572 | "engines": { 573 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0" 574 | } 575 | }, 576 | "node_modules/gh-pages": { 577 | "version": "5.0.0", 578 | "resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-5.0.0.tgz", 579 | "integrity": "sha512-Nqp1SjkPIB94Xw/3yYNTUL+G2dxlhjvv1zeN/4kMC1jfViTEqhtVz/Ba1zSXHuvXCN9ADNS1dN4r5/J/nZWEQQ==", 580 | "dev": true, 581 | "dependencies": { 582 | "async": "^3.2.4", 583 | "commander": "^2.18.0", 584 | "email-addresses": "^5.0.0", 585 | "filenamify": "^4.3.0", 586 | "find-cache-dir": "^3.3.1", 587 | "fs-extra": "^8.1.0", 588 | "globby": "^6.1.0" 589 | }, 590 | "bin": { 591 | "gh-pages": "bin/gh-pages.js", 592 | "gh-pages-clean": "bin/gh-pages-clean.js" 593 | }, 594 | "engines": { 595 | "node": ">=10" 596 | } 597 | }, 598 | "node_modules/glob": { 599 | "version": "7.2.3", 600 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", 601 | "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", 602 | "dev": true, 603 | "dependencies": { 604 | "fs.realpath": "^1.0.0", 605 | "inflight": "^1.0.4", 606 | "inherits": "2", 607 | "minimatch": "^3.1.1", 608 | "once": "^1.3.0", 609 | "path-is-absolute": "^1.0.0" 610 | }, 611 | "engines": { 612 | "node": "*" 613 | }, 614 | "funding": { 615 | "url": "https://github.com/sponsors/isaacs" 616 | } 617 | }, 618 | "node_modules/globby": { 619 | "version": "6.1.0", 620 | "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", 621 | "integrity": "sha512-KVbFv2TQtbzCoxAnfD6JcHZTYCzyliEaaeM/gH8qQdkKr5s0OP9scEgvdcngyk7AVdY6YVW/TJHd+lQ/Df3Daw==", 622 | "dev": true, 623 | "dependencies": { 624 | "array-union": "^1.0.1", 625 | "glob": "^7.0.3", 626 | "object-assign": "^4.0.1", 627 | "pify": "^2.0.0", 628 | "pinkie-promise": "^2.0.0" 629 | }, 630 | "engines": { 631 | "node": ">=0.10.0" 632 | } 633 | }, 634 | "node_modules/graceful-fs": { 635 | "version": "4.2.11", 636 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", 637 | "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", 638 | "dev": true 639 | }, 640 | "node_modules/inflight": { 641 | "version": "1.0.6", 642 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 643 | "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", 644 | "dev": true, 645 | "dependencies": { 646 | "once": "^1.3.0", 647 | "wrappy": "1" 648 | } 649 | }, 650 | "node_modules/inherits": { 651 | "version": "2.0.4", 652 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 653 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 654 | "dev": true 655 | }, 656 | "node_modules/jsonfile": { 657 | "version": "4.0.0", 658 | "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", 659 | "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", 660 | "dev": true, 661 | "optionalDependencies": { 662 | "graceful-fs": "^4.1.6" 663 | } 664 | }, 665 | "node_modules/locate-path": { 666 | "version": "5.0.0", 667 | "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", 668 | "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", 669 | "dev": true, 670 | "dependencies": { 671 | "p-locate": "^4.1.0" 672 | }, 673 | "engines": { 674 | "node": ">=8" 675 | } 676 | }, 677 | "node_modules/make-dir": { 678 | "version": "3.1.0", 679 | "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", 680 | "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", 681 | "dev": true, 682 | "dependencies": { 683 | "semver": "^6.0.0" 684 | }, 685 | "engines": { 686 | "node": ">=8" 687 | }, 688 | "funding": { 689 | "url": "https://github.com/sponsors/sindresorhus" 690 | } 691 | }, 692 | "node_modules/minimatch": { 693 | "version": "3.1.2", 694 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", 695 | "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", 696 | "dev": true, 697 | "dependencies": { 698 | "brace-expansion": "^1.1.7" 699 | }, 700 | "engines": { 701 | "node": "*" 702 | } 703 | }, 704 | "node_modules/monaco-editor": { 705 | "version": "0.37.1", 706 | "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.37.1.tgz", 707 | "integrity": "sha512-jLXEEYSbqMkT/FuJLBZAVWGuhIb4JNwHE9kPTorAVmsdZ4UzHAfgWxLsVtD7pLRFaOwYPhNG9nUCpmFL1t/dIg==" 708 | }, 709 | "node_modules/nanoid": { 710 | "version": "3.3.6", 711 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", 712 | "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", 713 | "dev": true, 714 | "funding": [ 715 | { 716 | "type": "github", 717 | "url": "https://github.com/sponsors/ai" 718 | } 719 | ], 720 | "bin": { 721 | "nanoid": "bin/nanoid.cjs" 722 | }, 723 | "engines": { 724 | "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" 725 | } 726 | }, 727 | "node_modules/object-assign": { 728 | "version": "4.1.1", 729 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 730 | "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", 731 | "dev": true, 732 | "engines": { 733 | "node": ">=0.10.0" 734 | } 735 | }, 736 | "node_modules/once": { 737 | "version": "1.4.0", 738 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 739 | "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", 740 | "dev": true, 741 | "dependencies": { 742 | "wrappy": "1" 743 | } 744 | }, 745 | "node_modules/p-limit": { 746 | "version": "2.3.0", 747 | "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", 748 | "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", 749 | "dev": true, 750 | "dependencies": { 751 | "p-try": "^2.0.0" 752 | }, 753 | "engines": { 754 | "node": ">=6" 755 | }, 756 | "funding": { 757 | "url": "https://github.com/sponsors/sindresorhus" 758 | } 759 | }, 760 | "node_modules/p-locate": { 761 | "version": "4.1.0", 762 | "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", 763 | "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", 764 | "dev": true, 765 | "dependencies": { 766 | "p-limit": "^2.2.0" 767 | }, 768 | "engines": { 769 | "node": ">=8" 770 | } 771 | }, 772 | "node_modules/p-try": { 773 | "version": "2.2.0", 774 | "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", 775 | "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", 776 | "dev": true, 777 | "engines": { 778 | "node": ">=6" 779 | } 780 | }, 781 | "node_modules/path-exists": { 782 | "version": "4.0.0", 783 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", 784 | "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", 785 | "dev": true, 786 | "engines": { 787 | "node": ">=8" 788 | } 789 | }, 790 | "node_modules/path-is-absolute": { 791 | "version": "1.0.1", 792 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 793 | "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", 794 | "dev": true, 795 | "engines": { 796 | "node": ">=0.10.0" 797 | } 798 | }, 799 | "node_modules/picocolors": { 800 | "version": "1.0.0", 801 | "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", 802 | "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", 803 | "dev": true 804 | }, 805 | "node_modules/pify": { 806 | "version": "2.3.0", 807 | "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", 808 | "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", 809 | "dev": true, 810 | "engines": { 811 | "node": ">=0.10.0" 812 | } 813 | }, 814 | "node_modules/pinkie": { 815 | "version": "2.0.4", 816 | "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", 817 | "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==", 818 | "dev": true, 819 | "engines": { 820 | "node": ">=0.10.0" 821 | } 822 | }, 823 | "node_modules/pinkie-promise": { 824 | "version": "2.0.1", 825 | "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", 826 | "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==", 827 | "dev": true, 828 | "dependencies": { 829 | "pinkie": "^2.0.0" 830 | }, 831 | "engines": { 832 | "node": ">=0.10.0" 833 | } 834 | }, 835 | "node_modules/pkg-dir": { 836 | "version": "4.2.0", 837 | "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", 838 | "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", 839 | "dev": true, 840 | "dependencies": { 841 | "find-up": "^4.0.0" 842 | }, 843 | "engines": { 844 | "node": ">=8" 845 | } 846 | }, 847 | "node_modules/postcss": { 848 | "version": "8.4.23", 849 | "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.23.tgz", 850 | "integrity": "sha512-bQ3qMcpF6A/YjR55xtoTr0jGOlnPOKAIMdOWiv0EIT6HVPEaJiJB4NLljSbiHoC2RX7DN5Uvjtpbg1NPdwv1oA==", 851 | "dev": true, 852 | "funding": [ 853 | { 854 | "type": "opencollective", 855 | "url": "https://opencollective.com/postcss/" 856 | }, 857 | { 858 | "type": "tidelift", 859 | "url": "https://tidelift.com/funding/github/npm/postcss" 860 | }, 861 | { 862 | "type": "github", 863 | "url": "https://github.com/sponsors/ai" 864 | } 865 | ], 866 | "dependencies": { 867 | "nanoid": "^3.3.6", 868 | "picocolors": "^1.0.0", 869 | "source-map-js": "^1.0.2" 870 | }, 871 | "engines": { 872 | "node": "^10 || ^12 || >=14" 873 | } 874 | }, 875 | "node_modules/rollup": { 876 | "version": "3.21.1", 877 | "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.21.1.tgz", 878 | "integrity": "sha512-GpUgqWCw56OSiBKf7lcAITstYiBV1/EKaKYPl9r8HgAxc6/qYAVw1PaHWnvHWFziRaf4HsVCDLq/IGtBi1K/Zw==", 879 | "dev": true, 880 | "bin": { 881 | "rollup": "dist/bin/rollup" 882 | }, 883 | "engines": { 884 | "node": ">=14.18.0", 885 | "npm": ">=8.0.0" 886 | }, 887 | "optionalDependencies": { 888 | "fsevents": "~2.3.2" 889 | } 890 | }, 891 | "node_modules/semver": { 892 | "version": "6.3.0", 893 | "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", 894 | "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", 895 | "dev": true, 896 | "bin": { 897 | "semver": "bin/semver.js" 898 | } 899 | }, 900 | "node_modules/source-map-js": { 901 | "version": "1.0.2", 902 | "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", 903 | "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", 904 | "dev": true, 905 | "engines": { 906 | "node": ">=0.10.0" 907 | } 908 | }, 909 | "node_modules/strip-json-comments": { 910 | "version": "5.0.0", 911 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-5.0.0.tgz", 912 | "integrity": "sha512-V1LGY4UUo0jgwC+ELQ2BNWfPa17TIuwBLg+j1AA/9RPzKINl1lhxVEu2r+ZTTO8aetIsUzE5Qj6LMSBkoGYKKw==", 913 | "engines": { 914 | "node": ">=14.16" 915 | }, 916 | "funding": { 917 | "url": "https://github.com/sponsors/sindresorhus" 918 | } 919 | }, 920 | "node_modules/strip-outer": { 921 | "version": "1.0.1", 922 | "resolved": "https://registry.npmjs.org/strip-outer/-/strip-outer-1.0.1.tgz", 923 | "integrity": "sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg==", 924 | "dev": true, 925 | "dependencies": { 926 | "escape-string-regexp": "^1.0.2" 927 | }, 928 | "engines": { 929 | "node": ">=0.10.0" 930 | } 931 | }, 932 | "node_modules/trim-repeated": { 933 | "version": "1.0.0", 934 | "resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz", 935 | "integrity": "sha512-pkonvlKk8/ZuR0D5tLW8ljt5I8kmxp2XKymhepUeOdCEfKpZaktSArkLHZt76OB1ZvO9bssUsDty4SWhLvZpLg==", 936 | "dev": true, 937 | "dependencies": { 938 | "escape-string-regexp": "^1.0.2" 939 | }, 940 | "engines": { 941 | "node": ">=0.10.0" 942 | } 943 | }, 944 | "node_modules/typescript": { 945 | "version": "5.0.4", 946 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz", 947 | "integrity": "sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==", 948 | "dev": true, 949 | "bin": { 950 | "tsc": "bin/tsc", 951 | "tsserver": "bin/tsserver" 952 | }, 953 | "engines": { 954 | "node": ">=12.20" 955 | } 956 | }, 957 | "node_modules/universalify": { 958 | "version": "0.1.2", 959 | "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", 960 | "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", 961 | "dev": true, 962 | "engines": { 963 | "node": ">= 4.0.0" 964 | } 965 | }, 966 | "node_modules/vite": { 967 | "version": "4.3.3", 968 | "resolved": "https://registry.npmjs.org/vite/-/vite-4.3.3.tgz", 969 | "integrity": "sha512-MwFlLBO4udZXd+VBcezo3u8mC77YQk+ik+fbc0GZWGgzfbPP+8Kf0fldhARqvSYmtIWoAJ5BXPClUbMTlqFxrA==", 970 | "dev": true, 971 | "dependencies": { 972 | "esbuild": "^0.17.5", 973 | "postcss": "^8.4.23", 974 | "rollup": "^3.21.0" 975 | }, 976 | "bin": { 977 | "vite": "bin/vite.js" 978 | }, 979 | "engines": { 980 | "node": "^14.18.0 || >=16.0.0" 981 | }, 982 | "optionalDependencies": { 983 | "fsevents": "~2.3.2" 984 | }, 985 | "peerDependencies": { 986 | "@types/node": ">= 14", 987 | "less": "*", 988 | "sass": "*", 989 | "stylus": "*", 990 | "sugarss": "*", 991 | "terser": "^5.4.0" 992 | }, 993 | "peerDependenciesMeta": { 994 | "@types/node": { 995 | "optional": true 996 | }, 997 | "less": { 998 | "optional": true 999 | }, 1000 | "sass": { 1001 | "optional": true 1002 | }, 1003 | "stylus": { 1004 | "optional": true 1005 | }, 1006 | "sugarss": { 1007 | "optional": true 1008 | }, 1009 | "terser": { 1010 | "optional": true 1011 | } 1012 | } 1013 | }, 1014 | "node_modules/vite-plugin-monaco-editor": { 1015 | "version": "1.1.0", 1016 | "resolved": "https://registry.npmjs.org/vite-plugin-monaco-editor/-/vite-plugin-monaco-editor-1.1.0.tgz", 1017 | "integrity": "sha512-IvtUqZotrRoVqwT0PBBDIZPNraya3BxN/bfcNfnxZ5rkJiGcNtO5eAOWWSgT7zullIAEqQwxMU83yL9J5k7gww==", 1018 | "dev": true, 1019 | "peerDependencies": { 1020 | "monaco-editor": ">=0.33.0" 1021 | } 1022 | }, 1023 | "node_modules/wrappy": { 1024 | "version": "1.0.2", 1025 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 1026 | "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", 1027 | "dev": true 1028 | } 1029 | }, 1030 | "dependencies": { 1031 | "@esbuild/android-arm": { 1032 | "version": "0.17.18", 1033 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.18.tgz", 1034 | "integrity": "sha512-EmwL+vUBZJ7mhFCs5lA4ZimpUH3WMAoqvOIYhVQwdIgSpHC8ImHdsRyhHAVxpDYUSm0lWvd63z0XH1IlImS2Qw==", 1035 | "dev": true, 1036 | "optional": true 1037 | }, 1038 | "@esbuild/android-arm64": { 1039 | "version": "0.17.18", 1040 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.18.tgz", 1041 | "integrity": "sha512-/iq0aK0eeHgSC3z55ucMAHO05OIqmQehiGay8eP5l/5l+iEr4EIbh4/MI8xD9qRFjqzgkc0JkX0LculNC9mXBw==", 1042 | "dev": true, 1043 | "optional": true 1044 | }, 1045 | "@esbuild/android-x64": { 1046 | "version": "0.17.18", 1047 | "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.18.tgz", 1048 | "integrity": "sha512-x+0efYNBF3NPW2Xc5bFOSFW7tTXdAcpfEg2nXmxegm4mJuVeS+i109m/7HMiOQ6M12aVGGFlqJX3RhNdYM2lWg==", 1049 | "dev": true, 1050 | "optional": true 1051 | }, 1052 | "@esbuild/darwin-arm64": { 1053 | "version": "0.17.18", 1054 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.18.tgz", 1055 | "integrity": "sha512-6tY+djEAdF48M1ONWnQb1C+6LiXrKjmqjzPNPWXhu/GzOHTHX2nh8Mo2ZAmBFg0kIodHhciEgUBtcYCAIjGbjQ==", 1056 | "dev": true, 1057 | "optional": true 1058 | }, 1059 | "@esbuild/darwin-x64": { 1060 | "version": "0.17.18", 1061 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.18.tgz", 1062 | "integrity": "sha512-Qq84ykvLvya3dO49wVC9FFCNUfSrQJLbxhoQk/TE1r6MjHo3sFF2tlJCwMjhkBVq3/ahUisj7+EpRSz0/+8+9A==", 1063 | "dev": true, 1064 | "optional": true 1065 | }, 1066 | "@esbuild/freebsd-arm64": { 1067 | "version": "0.17.18", 1068 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.18.tgz", 1069 | "integrity": "sha512-fw/ZfxfAzuHfaQeMDhbzxp9mc+mHn1Y94VDHFHjGvt2Uxl10mT4CDavHm+/L9KG441t1QdABqkVYwakMUeyLRA==", 1070 | "dev": true, 1071 | "optional": true 1072 | }, 1073 | "@esbuild/freebsd-x64": { 1074 | "version": "0.17.18", 1075 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.18.tgz", 1076 | "integrity": "sha512-FQFbRtTaEi8ZBi/A6kxOC0V0E9B/97vPdYjY9NdawyLd4Qk5VD5g2pbWN2VR1c0xhzcJm74HWpObPszWC+qTew==", 1077 | "dev": true, 1078 | "optional": true 1079 | }, 1080 | "@esbuild/linux-arm": { 1081 | "version": "0.17.18", 1082 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.18.tgz", 1083 | "integrity": "sha512-jW+UCM40LzHcouIaqv3e/oRs0JM76JfhHjCavPxMUti7VAPh8CaGSlS7cmyrdpzSk7A+8f0hiedHqr/LMnfijg==", 1084 | "dev": true, 1085 | "optional": true 1086 | }, 1087 | "@esbuild/linux-arm64": { 1088 | "version": "0.17.18", 1089 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.18.tgz", 1090 | "integrity": "sha512-R7pZvQZFOY2sxUG8P6A21eq6q+eBv7JPQYIybHVf1XkQYC+lT7nDBdC7wWKTrbvMXKRaGudp/dzZCwL/863mZQ==", 1091 | "dev": true, 1092 | "optional": true 1093 | }, 1094 | "@esbuild/linux-ia32": { 1095 | "version": "0.17.18", 1096 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.18.tgz", 1097 | "integrity": "sha512-ygIMc3I7wxgXIxk6j3V00VlABIjq260i967Cp9BNAk5pOOpIXmd1RFQJQX9Io7KRsthDrQYrtcx7QCof4o3ZoQ==", 1098 | "dev": true, 1099 | "optional": true 1100 | }, 1101 | "@esbuild/linux-loong64": { 1102 | "version": "0.17.18", 1103 | "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.18.tgz", 1104 | "integrity": "sha512-bvPG+MyFs5ZlwYclCG1D744oHk1Pv7j8psF5TfYx7otCVmcJsEXgFEhQkbhNW8otDHL1a2KDINW20cfCgnzgMQ==", 1105 | "dev": true, 1106 | "optional": true 1107 | }, 1108 | "@esbuild/linux-mips64el": { 1109 | "version": "0.17.18", 1110 | "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.18.tgz", 1111 | "integrity": "sha512-oVqckATOAGuiUOa6wr8TXaVPSa+6IwVJrGidmNZS1cZVx0HqkTMkqFGD2HIx9H1RvOwFeWYdaYbdY6B89KUMxA==", 1112 | "dev": true, 1113 | "optional": true 1114 | }, 1115 | "@esbuild/linux-ppc64": { 1116 | "version": "0.17.18", 1117 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.18.tgz", 1118 | "integrity": "sha512-3dLlQO+b/LnQNxgH4l9rqa2/IwRJVN9u/bK63FhOPB4xqiRqlQAU0qDU3JJuf0BmaH0yytTBdoSBHrb2jqc5qQ==", 1119 | "dev": true, 1120 | "optional": true 1121 | }, 1122 | "@esbuild/linux-riscv64": { 1123 | "version": "0.17.18", 1124 | "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.18.tgz", 1125 | "integrity": "sha512-/x7leOyDPjZV3TcsdfrSI107zItVnsX1q2nho7hbbQoKnmoeUWjs+08rKKt4AUXju7+3aRZSsKrJtaRmsdL1xA==", 1126 | "dev": true, 1127 | "optional": true 1128 | }, 1129 | "@esbuild/linux-s390x": { 1130 | "version": "0.17.18", 1131 | "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.18.tgz", 1132 | "integrity": "sha512-cX0I8Q9xQkL/6F5zWdYmVf5JSQt+ZfZD2bJudZrWD+4mnUvoZ3TDDXtDX2mUaq6upMFv9FlfIh4Gfun0tbGzuw==", 1133 | "dev": true, 1134 | "optional": true 1135 | }, 1136 | "@esbuild/linux-x64": { 1137 | "version": "0.17.18", 1138 | "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.18.tgz", 1139 | "integrity": "sha512-66RmRsPlYy4jFl0vG80GcNRdirx4nVWAzJmXkevgphP1qf4dsLQCpSKGM3DUQCojwU1hnepI63gNZdrr02wHUA==", 1140 | "dev": true, 1141 | "optional": true 1142 | }, 1143 | "@esbuild/netbsd-x64": { 1144 | "version": "0.17.18", 1145 | "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.18.tgz", 1146 | "integrity": "sha512-95IRY7mI2yrkLlTLb1gpDxdC5WLC5mZDi+kA9dmM5XAGxCME0F8i4bYH4jZreaJ6lIZ0B8hTrweqG1fUyW7jbg==", 1147 | "dev": true, 1148 | "optional": true 1149 | }, 1150 | "@esbuild/openbsd-x64": { 1151 | "version": "0.17.18", 1152 | "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.18.tgz", 1153 | "integrity": "sha512-WevVOgcng+8hSZ4Q3BKL3n1xTv5H6Nb53cBrtzzEjDbbnOmucEVcZeGCsCOi9bAOcDYEeBZbD2SJNBxlfP3qiA==", 1154 | "dev": true, 1155 | "optional": true 1156 | }, 1157 | "@esbuild/sunos-x64": { 1158 | "version": "0.17.18", 1159 | "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.18.tgz", 1160 | "integrity": "sha512-Rzf4QfQagnwhQXVBS3BYUlxmEbcV7MY+BH5vfDZekU5eYpcffHSyjU8T0xucKVuOcdCsMo+Ur5wmgQJH2GfNrg==", 1161 | "dev": true, 1162 | "optional": true 1163 | }, 1164 | "@esbuild/win32-arm64": { 1165 | "version": "0.17.18", 1166 | "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.18.tgz", 1167 | "integrity": "sha512-Kb3Ko/KKaWhjeAm2YoT/cNZaHaD1Yk/pa3FTsmqo9uFh1D1Rfco7BBLIPdDOozrObj2sahslFuAQGvWbgWldAg==", 1168 | "dev": true, 1169 | "optional": true 1170 | }, 1171 | "@esbuild/win32-ia32": { 1172 | "version": "0.17.18", 1173 | "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.18.tgz", 1174 | "integrity": "sha512-0/xUMIdkVHwkvxfbd5+lfG7mHOf2FRrxNbPiKWg9C4fFrB8H0guClmaM3BFiRUYrznVoyxTIyC/Ou2B7QQSwmw==", 1175 | "dev": true, 1176 | "optional": true 1177 | }, 1178 | "@esbuild/win32-x64": { 1179 | "version": "0.17.18", 1180 | "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.18.tgz", 1181 | "integrity": "sha512-qU25Ma1I3NqTSHJUOKi9sAH1/Mzuvlke0ioMJRthLXKm7JiSKVwFghlGbDLOO2sARECGhja4xYfRAZNPAkooYg==", 1182 | "dev": true, 1183 | "optional": true 1184 | }, 1185 | "array-union": { 1186 | "version": "1.0.2", 1187 | "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", 1188 | "integrity": "sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==", 1189 | "dev": true, 1190 | "requires": { 1191 | "array-uniq": "^1.0.1" 1192 | } 1193 | }, 1194 | "array-uniq": { 1195 | "version": "1.0.3", 1196 | "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", 1197 | "integrity": "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==", 1198 | "dev": true 1199 | }, 1200 | "async": { 1201 | "version": "3.2.4", 1202 | "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", 1203 | "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==", 1204 | "dev": true 1205 | }, 1206 | "balanced-match": { 1207 | "version": "1.0.2", 1208 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 1209 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", 1210 | "dev": true 1211 | }, 1212 | "brace-expansion": { 1213 | "version": "1.1.11", 1214 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 1215 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 1216 | "dev": true, 1217 | "requires": { 1218 | "balanced-match": "^1.0.0", 1219 | "concat-map": "0.0.1" 1220 | } 1221 | }, 1222 | "commander": { 1223 | "version": "2.20.3", 1224 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", 1225 | "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", 1226 | "dev": true 1227 | }, 1228 | "commondir": { 1229 | "version": "1.0.1", 1230 | "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", 1231 | "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", 1232 | "dev": true 1233 | }, 1234 | "concat-map": { 1235 | "version": "0.0.1", 1236 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 1237 | "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", 1238 | "dev": true 1239 | }, 1240 | "email-addresses": { 1241 | "version": "5.0.0", 1242 | "resolved": "https://registry.npmjs.org/email-addresses/-/email-addresses-5.0.0.tgz", 1243 | "integrity": "sha512-4OIPYlA6JXqtVn8zpHpGiI7vE6EQOAg16aGnDMIAlZVinnoZ8208tW1hAbjWydgN/4PLTT9q+O1K6AH/vALJGw==", 1244 | "dev": true 1245 | }, 1246 | "esbuild": { 1247 | "version": "0.17.18", 1248 | "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.18.tgz", 1249 | "integrity": "sha512-z1lix43jBs6UKjcZVKOw2xx69ffE2aG0PygLL5qJ9OS/gy0Ewd1gW/PUQIOIQGXBHWNywSc0floSKoMFF8aK2w==", 1250 | "dev": true, 1251 | "requires": { 1252 | "@esbuild/android-arm": "0.17.18", 1253 | "@esbuild/android-arm64": "0.17.18", 1254 | "@esbuild/android-x64": "0.17.18", 1255 | "@esbuild/darwin-arm64": "0.17.18", 1256 | "@esbuild/darwin-x64": "0.17.18", 1257 | "@esbuild/freebsd-arm64": "0.17.18", 1258 | "@esbuild/freebsd-x64": "0.17.18", 1259 | "@esbuild/linux-arm": "0.17.18", 1260 | "@esbuild/linux-arm64": "0.17.18", 1261 | "@esbuild/linux-ia32": "0.17.18", 1262 | "@esbuild/linux-loong64": "0.17.18", 1263 | "@esbuild/linux-mips64el": "0.17.18", 1264 | "@esbuild/linux-ppc64": "0.17.18", 1265 | "@esbuild/linux-riscv64": "0.17.18", 1266 | "@esbuild/linux-s390x": "0.17.18", 1267 | "@esbuild/linux-x64": "0.17.18", 1268 | "@esbuild/netbsd-x64": "0.17.18", 1269 | "@esbuild/openbsd-x64": "0.17.18", 1270 | "@esbuild/sunos-x64": "0.17.18", 1271 | "@esbuild/win32-arm64": "0.17.18", 1272 | "@esbuild/win32-ia32": "0.17.18", 1273 | "@esbuild/win32-x64": "0.17.18" 1274 | } 1275 | }, 1276 | "escape-string-regexp": { 1277 | "version": "1.0.5", 1278 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 1279 | "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", 1280 | "dev": true 1281 | }, 1282 | "filename-reserved-regex": { 1283 | "version": "2.0.0", 1284 | "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz", 1285 | "integrity": "sha512-lc1bnsSr4L4Bdif8Xb/qrtokGbq5zlsms/CYH8PP+WtCkGNF65DPiQY8vG3SakEdRn8Dlnm+gW/qWKKjS5sZzQ==", 1286 | "dev": true 1287 | }, 1288 | "filenamify": { 1289 | "version": "4.3.0", 1290 | "resolved": "https://registry.npmjs.org/filenamify/-/filenamify-4.3.0.tgz", 1291 | "integrity": "sha512-hcFKyUG57yWGAzu1CMt/dPzYZuv+jAJUT85bL8mrXvNe6hWj6yEHEc4EdcgiA6Z3oi1/9wXJdZPXF2dZNgwgOg==", 1292 | "dev": true, 1293 | "requires": { 1294 | "filename-reserved-regex": "^2.0.0", 1295 | "strip-outer": "^1.0.1", 1296 | "trim-repeated": "^1.0.0" 1297 | } 1298 | }, 1299 | "find-cache-dir": { 1300 | "version": "3.3.2", 1301 | "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", 1302 | "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", 1303 | "dev": true, 1304 | "requires": { 1305 | "commondir": "^1.0.1", 1306 | "make-dir": "^3.0.2", 1307 | "pkg-dir": "^4.1.0" 1308 | } 1309 | }, 1310 | "find-up": { 1311 | "version": "4.1.0", 1312 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", 1313 | "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", 1314 | "dev": true, 1315 | "requires": { 1316 | "locate-path": "^5.0.0", 1317 | "path-exists": "^4.0.0" 1318 | } 1319 | }, 1320 | "fs-extra": { 1321 | "version": "8.1.0", 1322 | "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", 1323 | "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", 1324 | "dev": true, 1325 | "requires": { 1326 | "graceful-fs": "^4.2.0", 1327 | "jsonfile": "^4.0.0", 1328 | "universalify": "^0.1.0" 1329 | } 1330 | }, 1331 | "fs.realpath": { 1332 | "version": "1.0.0", 1333 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 1334 | "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", 1335 | "dev": true 1336 | }, 1337 | "fsevents": { 1338 | "version": "2.3.2", 1339 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", 1340 | "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", 1341 | "dev": true, 1342 | "optional": true 1343 | }, 1344 | "gh-pages": { 1345 | "version": "5.0.0", 1346 | "resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-5.0.0.tgz", 1347 | "integrity": "sha512-Nqp1SjkPIB94Xw/3yYNTUL+G2dxlhjvv1zeN/4kMC1jfViTEqhtVz/Ba1zSXHuvXCN9ADNS1dN4r5/J/nZWEQQ==", 1348 | "dev": true, 1349 | "requires": { 1350 | "async": "^3.2.4", 1351 | "commander": "^2.18.0", 1352 | "email-addresses": "^5.0.0", 1353 | "filenamify": "^4.3.0", 1354 | "find-cache-dir": "^3.3.1", 1355 | "fs-extra": "^8.1.0", 1356 | "globby": "^6.1.0" 1357 | } 1358 | }, 1359 | "glob": { 1360 | "version": "7.2.3", 1361 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", 1362 | "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", 1363 | "dev": true, 1364 | "requires": { 1365 | "fs.realpath": "^1.0.0", 1366 | "inflight": "^1.0.4", 1367 | "inherits": "2", 1368 | "minimatch": "^3.1.1", 1369 | "once": "^1.3.0", 1370 | "path-is-absolute": "^1.0.0" 1371 | } 1372 | }, 1373 | "globby": { 1374 | "version": "6.1.0", 1375 | "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", 1376 | "integrity": "sha512-KVbFv2TQtbzCoxAnfD6JcHZTYCzyliEaaeM/gH8qQdkKr5s0OP9scEgvdcngyk7AVdY6YVW/TJHd+lQ/Df3Daw==", 1377 | "dev": true, 1378 | "requires": { 1379 | "array-union": "^1.0.1", 1380 | "glob": "^7.0.3", 1381 | "object-assign": "^4.0.1", 1382 | "pify": "^2.0.0", 1383 | "pinkie-promise": "^2.0.0" 1384 | } 1385 | }, 1386 | "graceful-fs": { 1387 | "version": "4.2.11", 1388 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", 1389 | "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", 1390 | "dev": true 1391 | }, 1392 | "inflight": { 1393 | "version": "1.0.6", 1394 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 1395 | "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", 1396 | "dev": true, 1397 | "requires": { 1398 | "once": "^1.3.0", 1399 | "wrappy": "1" 1400 | } 1401 | }, 1402 | "inherits": { 1403 | "version": "2.0.4", 1404 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 1405 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 1406 | "dev": true 1407 | }, 1408 | "jsonfile": { 1409 | "version": "4.0.0", 1410 | "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", 1411 | "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", 1412 | "dev": true, 1413 | "requires": { 1414 | "graceful-fs": "^4.1.6" 1415 | } 1416 | }, 1417 | "locate-path": { 1418 | "version": "5.0.0", 1419 | "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", 1420 | "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", 1421 | "dev": true, 1422 | "requires": { 1423 | "p-locate": "^4.1.0" 1424 | } 1425 | }, 1426 | "make-dir": { 1427 | "version": "3.1.0", 1428 | "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", 1429 | "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", 1430 | "dev": true, 1431 | "requires": { 1432 | "semver": "^6.0.0" 1433 | } 1434 | }, 1435 | "minimatch": { 1436 | "version": "3.1.2", 1437 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", 1438 | "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", 1439 | "dev": true, 1440 | "requires": { 1441 | "brace-expansion": "^1.1.7" 1442 | } 1443 | }, 1444 | "monaco-editor": { 1445 | "version": "0.37.1", 1446 | "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.37.1.tgz", 1447 | "integrity": "sha512-jLXEEYSbqMkT/FuJLBZAVWGuhIb4JNwHE9kPTorAVmsdZ4UzHAfgWxLsVtD7pLRFaOwYPhNG9nUCpmFL1t/dIg==" 1448 | }, 1449 | "nanoid": { 1450 | "version": "3.3.6", 1451 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", 1452 | "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", 1453 | "dev": true 1454 | }, 1455 | "object-assign": { 1456 | "version": "4.1.1", 1457 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 1458 | "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", 1459 | "dev": true 1460 | }, 1461 | "once": { 1462 | "version": "1.4.0", 1463 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 1464 | "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", 1465 | "dev": true, 1466 | "requires": { 1467 | "wrappy": "1" 1468 | } 1469 | }, 1470 | "p-limit": { 1471 | "version": "2.3.0", 1472 | "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", 1473 | "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", 1474 | "dev": true, 1475 | "requires": { 1476 | "p-try": "^2.0.0" 1477 | } 1478 | }, 1479 | "p-locate": { 1480 | "version": "4.1.0", 1481 | "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", 1482 | "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", 1483 | "dev": true, 1484 | "requires": { 1485 | "p-limit": "^2.2.0" 1486 | } 1487 | }, 1488 | "p-try": { 1489 | "version": "2.2.0", 1490 | "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", 1491 | "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", 1492 | "dev": true 1493 | }, 1494 | "path-exists": { 1495 | "version": "4.0.0", 1496 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", 1497 | "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", 1498 | "dev": true 1499 | }, 1500 | "path-is-absolute": { 1501 | "version": "1.0.1", 1502 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 1503 | "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", 1504 | "dev": true 1505 | }, 1506 | "picocolors": { 1507 | "version": "1.0.0", 1508 | "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", 1509 | "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", 1510 | "dev": true 1511 | }, 1512 | "pify": { 1513 | "version": "2.3.0", 1514 | "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", 1515 | "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", 1516 | "dev": true 1517 | }, 1518 | "pinkie": { 1519 | "version": "2.0.4", 1520 | "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", 1521 | "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==", 1522 | "dev": true 1523 | }, 1524 | "pinkie-promise": { 1525 | "version": "2.0.1", 1526 | "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", 1527 | "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==", 1528 | "dev": true, 1529 | "requires": { 1530 | "pinkie": "^2.0.0" 1531 | } 1532 | }, 1533 | "pkg-dir": { 1534 | "version": "4.2.0", 1535 | "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", 1536 | "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", 1537 | "dev": true, 1538 | "requires": { 1539 | "find-up": "^4.0.0" 1540 | } 1541 | }, 1542 | "postcss": { 1543 | "version": "8.4.23", 1544 | "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.23.tgz", 1545 | "integrity": "sha512-bQ3qMcpF6A/YjR55xtoTr0jGOlnPOKAIMdOWiv0EIT6HVPEaJiJB4NLljSbiHoC2RX7DN5Uvjtpbg1NPdwv1oA==", 1546 | "dev": true, 1547 | "requires": { 1548 | "nanoid": "^3.3.6", 1549 | "picocolors": "^1.0.0", 1550 | "source-map-js": "^1.0.2" 1551 | } 1552 | }, 1553 | "rollup": { 1554 | "version": "3.21.1", 1555 | "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.21.1.tgz", 1556 | "integrity": "sha512-GpUgqWCw56OSiBKf7lcAITstYiBV1/EKaKYPl9r8HgAxc6/qYAVw1PaHWnvHWFziRaf4HsVCDLq/IGtBi1K/Zw==", 1557 | "dev": true, 1558 | "requires": { 1559 | "fsevents": "~2.3.2" 1560 | } 1561 | }, 1562 | "semver": { 1563 | "version": "6.3.0", 1564 | "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", 1565 | "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", 1566 | "dev": true 1567 | }, 1568 | "source-map-js": { 1569 | "version": "1.0.2", 1570 | "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", 1571 | "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", 1572 | "dev": true 1573 | }, 1574 | "strip-json-comments": { 1575 | "version": "5.0.0", 1576 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-5.0.0.tgz", 1577 | "integrity": "sha512-V1LGY4UUo0jgwC+ELQ2BNWfPa17TIuwBLg+j1AA/9RPzKINl1lhxVEu2r+ZTTO8aetIsUzE5Qj6LMSBkoGYKKw==" 1578 | }, 1579 | "strip-outer": { 1580 | "version": "1.0.1", 1581 | "resolved": "https://registry.npmjs.org/strip-outer/-/strip-outer-1.0.1.tgz", 1582 | "integrity": "sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg==", 1583 | "dev": true, 1584 | "requires": { 1585 | "escape-string-regexp": "^1.0.2" 1586 | } 1587 | }, 1588 | "trim-repeated": { 1589 | "version": "1.0.0", 1590 | "resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz", 1591 | "integrity": "sha512-pkonvlKk8/ZuR0D5tLW8ljt5I8kmxp2XKymhepUeOdCEfKpZaktSArkLHZt76OB1ZvO9bssUsDty4SWhLvZpLg==", 1592 | "dev": true, 1593 | "requires": { 1594 | "escape-string-regexp": "^1.0.2" 1595 | } 1596 | }, 1597 | "typescript": { 1598 | "version": "5.0.4", 1599 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz", 1600 | "integrity": "sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==", 1601 | "dev": true 1602 | }, 1603 | "universalify": { 1604 | "version": "0.1.2", 1605 | "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", 1606 | "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", 1607 | "dev": true 1608 | }, 1609 | "vite": { 1610 | "version": "4.3.3", 1611 | "resolved": "https://registry.npmjs.org/vite/-/vite-4.3.3.tgz", 1612 | "integrity": "sha512-MwFlLBO4udZXd+VBcezo3u8mC77YQk+ik+fbc0GZWGgzfbPP+8Kf0fldhARqvSYmtIWoAJ5BXPClUbMTlqFxrA==", 1613 | "dev": true, 1614 | "requires": { 1615 | "esbuild": "^0.17.5", 1616 | "fsevents": "~2.3.2", 1617 | "postcss": "^8.4.23", 1618 | "rollup": "^3.21.0" 1619 | } 1620 | }, 1621 | "vite-plugin-monaco-editor": { 1622 | "version": "1.1.0", 1623 | "resolved": "https://registry.npmjs.org/vite-plugin-monaco-editor/-/vite-plugin-monaco-editor-1.1.0.tgz", 1624 | "integrity": "sha512-IvtUqZotrRoVqwT0PBBDIZPNraya3BxN/bfcNfnxZ5rkJiGcNtO5eAOWWSgT7zullIAEqQwxMU83yL9J5k7gww==", 1625 | "dev": true, 1626 | "requires": {} 1627 | }, 1628 | "wrappy": { 1629 | "version": "1.0.2", 1630 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 1631 | "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", 1632 | "dev": true 1633 | } 1634 | } 1635 | } 1636 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ztest_aa", 3 | "private": true, 4 | "version": "0.0.0", 5 | "scripts": { 6 | "dev": "vite", 7 | "build": "tsc && vite build", 8 | "preview": "vite preview", 9 | "deploy": "gh-pages -r git@github.com:KalmeMarq/json-ui-editor.git -d dist" 10 | }, 11 | "devDependencies": { 12 | "gh-pages": "^5.0.0", 13 | "typescript": "^5.0.2", 14 | "vite": "^4.3.2", 15 | "vite-plugin-monaco-editor": "^1.1.0" 16 | }, 17 | "dependencies": { 18 | "monaco-editor": "^0.37.1", 19 | "strip-json-comments": "^5.0.0" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/DrawContext.ts: -------------------------------------------------------------------------------- 1 | import { TextRenderer } from './TextRenderer'; 2 | import { RenderSystem } from './gl'; 3 | import { PROGRAMS } from './program'; 4 | import { BufferBuilder, Tessellator } from './tessellator'; 5 | import { DrawMode, VertexFormats } from './vertex_format'; 6 | 7 | export class DrawContext { 8 | textRend: TextRenderer; 9 | 10 | constructor(textRend: TextRenderer) { 11 | this.textRend = textRend; 12 | } 13 | 14 | public drawTexture(texture: string, x: number, y: number, z: number, width: number, height: number, u: number, v: number, us: number, vs: number, tw: number, th: number, grayscale: boolean) { 15 | const tessellator = Tessellator.getInstance(); 16 | const builder = tessellator.getBufferBuilder(); 17 | RenderSystem.setShaderTexture(0, texture); 18 | RenderSystem.setShader(grayscale ? PROGRAMS.GRAYSCALE_POSITION_TEXTURE : PROGRAMS.POSITION_TEXTURE); 19 | builder.begin(DrawMode.QUADS, VertexFormats.POSITION_TEXTURE); 20 | const x1 = x + width; 21 | const y1 = y + height; 22 | const u0 = u / tw; 23 | const v0 = v / th; 24 | const u1 = (u + us) / tw; 25 | const v1 = (v + vs) / th; 26 | 27 | builder.vertex(x, y, z).texture(u0, v0).next(); 28 | builder.vertex(x, y1, z).texture(u0, v1).next(); 29 | builder.vertex(x1, y1, z).texture(u1, v1).next(); 30 | builder.vertex(x1, y, z).texture(u1, v0).next(); 31 | builder.draw(); 32 | } 33 | 34 | public drawColoredTexture( 35 | texture: string, 36 | x: number, 37 | y: number, 38 | z: number, 39 | width: number, 40 | height: number, 41 | u: number, 42 | v: number, 43 | us: number, 44 | vs: number, 45 | tw: number, 46 | th: number, 47 | color: number[], 48 | grayscale: boolean 49 | ) { 50 | const tessellator = Tessellator.getInstance(); 51 | const builder = tessellator.getBufferBuilder(); 52 | RenderSystem.setShaderTexture(0, texture); 53 | RenderSystem.setShader(grayscale ? PROGRAMS.GRAYSCALE_POSITION_TEXTURE_COLOR : PROGRAMS.POSITION_TEXTURE_COLOR); 54 | builder.begin(DrawMode.QUADS, VertexFormats.POSITION_TEXTURE_COLOR); 55 | const x1 = x + width; 56 | const y1 = y + height; 57 | const u0 = u / tw; 58 | const v0 = v / th; 59 | const u1 = (u + us) / tw; 60 | const v1 = (v + vs) / th; 61 | 62 | builder.vertex(x, y, z).texture(u0, v0).color(color[0], color[1], color[2], color[3]).next(); 63 | builder.vertex(x, y1, z).texture(u0, v1).color(color[0], color[1], color[2], color[3]).next(); 64 | builder.vertex(x1, y1, z).texture(u1, v1).color(color[0], color[1], color[2], color[3]).next(); 65 | builder.vertex(x1, y, z).texture(u1, v0).color(color[0], color[1], color[2], color[3]).next(); 66 | builder.draw(); 67 | } 68 | 69 | public drawBorder(x: number, y: number, z: number, width: number, height: number, red: number, green: number, blue: number, alpha: number) { 70 | this.drawColoredf(x, y, z, 1, height, red, green, blue, alpha); 71 | this.drawColoredf(x + width - 1, y, z, 1, height, red, green, blue, alpha); 72 | this.drawColoredf(x, y, z, width, 1, red, green, blue, alpha); 73 | this.drawColoredf(x, y + height - 1, z, width, 1, red, green, blue, alpha); 74 | } 75 | 76 | private drawBorderQuad(builder: BufferBuilder, x: number, y: number, z: number, w: number, h: number, red: number, green: number, blue: number, alpha: number) { 77 | const x1 = x + w; 78 | const y1 = y + h; 79 | builder.vertex(x, y, z).color(red, green, blue, alpha).next(); 80 | builder.vertex(x, y1, z).color(red, green, blue, alpha).next(); 81 | builder.vertex(x1, y1, z).color(red, green, blue, alpha).next(); 82 | builder.vertex(x1, y, z).color(red, green, blue, alpha).next(); 83 | } 84 | 85 | public drawColoredfv(x: number, y: number, z: number, width: number, height: number, color: number[]) { 86 | this.drawColoredf(x, y, z, width, height, color[0], color[1], color[2], color[3]); 87 | } 88 | 89 | public drawColoredf(x: number, y: number, z: number, width: number, height: number, red: number, green: number, blue: number, alpha: number) { 90 | const tessellator = Tessellator.getInstance(); 91 | const builder = tessellator.getBufferBuilder(); 92 | RenderSystem.setShader(PROGRAMS.POSITION_COLOR); 93 | builder.begin(DrawMode.QUADS, VertexFormats.POSITION_COLOR); 94 | const x1 = x + width; 95 | const y1 = y + height; 96 | builder.vertex(x, y, z).color(red, green, blue, alpha).next(); 97 | builder.vertex(x, y1, z).color(red, green, blue, alpha).next(); 98 | builder.vertex(x1, y1, z).color(red, green, blue, alpha).next(); 99 | builder.vertex(x1, y, z).color(red, green, blue, alpha).next(); 100 | RenderSystem.enableBlend(); 101 | builder.draw(); 102 | RenderSystem.disableBlend(); 103 | } 104 | 105 | public drawVGradientfv(x: number, y: number, z: number, width: number, height: number, colorStart: number[], colorEnd: number[]) { 106 | const tessellator = Tessellator.getInstance(); 107 | const builder = tessellator.getBufferBuilder(); 108 | RenderSystem.setShader(PROGRAMS.POSITION_COLOR); 109 | builder.begin(DrawMode.QUADS, VertexFormats.POSITION_COLOR); 110 | const x1 = x + width; 111 | const y1 = y + height; 112 | builder.vertex(x, y, z).color(colorStart[0], colorStart[1], colorStart[2], colorStart[3]).next(); 113 | builder.vertex(x, y1, z).color(colorEnd[0], colorEnd[1], colorEnd[2], colorEnd[3]).next(); 114 | builder.vertex(x1, y1, z).color(colorEnd[0], colorEnd[1], colorEnd[2], colorEnd[3]).next(); 115 | builder.vertex(x1, y, z).color(colorStart[0], colorStart[1], colorStart[2], colorStart[3]).next(); 116 | RenderSystem.enableBlend(); 117 | builder.draw(); 118 | RenderSystem.disableBlend(); 119 | } 120 | 121 | public drawHGradientfv(x: number, y: number, z: number, width: number, height: number, colorStart: number[], colorEnd: number[]) { 122 | const tessellator = Tessellator.getInstance(); 123 | const builder = tessellator.getBufferBuilder(); 124 | RenderSystem.setShader(PROGRAMS.POSITION_COLOR); 125 | builder.begin(DrawMode.QUADS, VertexFormats.POSITION_COLOR); 126 | const x1 = x + width; 127 | const y1 = y + height; 128 | builder.vertex(x, y, z).color(colorStart[0], colorStart[1], colorStart[2], colorStart[3]).next(); 129 | builder.vertex(x, y1, z).color(colorStart[0], colorStart[1], colorStart[2], colorStart[3]).next(); 130 | builder.vertex(x1, y1, z).color(colorEnd[0], colorEnd[1], colorEnd[2], colorEnd[3]).next(); 131 | builder.vertex(x1, y, z).color(colorEnd[0], colorEnd[1], colorEnd[2], colorEnd[3]).next(); 132 | RenderSystem.enableBlend(); 133 | builder.draw(); 134 | RenderSystem.disableBlend(); 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /src/TextRenderer.ts: -------------------------------------------------------------------------------- 1 | import { GL, RenderSystem } from './gl'; 2 | import { PROGRAMS } from './program'; 3 | import { Tessellator } from './tessellator'; 4 | import { DrawMode, VertexFormats } from './vertex_format'; 5 | 6 | export class TextRenderer { 7 | fontData: any; 8 | constructor(fontData: any) { 9 | this.fontData = fontData; 10 | } 11 | 12 | drawWithShadowfv(text: string, x: number, y: number, color: number[]) { 13 | if (text.length == 0) return; 14 | 15 | RenderSystem.enableBlend(); 16 | GL.glBlendFuncSeparate(GL.SRC_ALPHA, GL.ONE_MINUS_SRC_ALPHA, GL.SRC_ALPHA, GL.ONE_MINUS_SRC_ALPHA); 17 | const tes = Tessellator.getInstance(); 18 | const builder = tes.getBufferBuilder(); 19 | RenderSystem.setShaderTexture(0, 'ascii.png'); 20 | RenderSystem.setShader(PROGRAMS.POSITION_TEXTURE_COLOR); 21 | 22 | let xx = x; 23 | const y1 = y + 8; 24 | for (let i = 0; i < text.length; ++i) { 25 | const dt = this.fontData[text[i]]; 26 | if (text[i] == ' ') xx += 4; 27 | 28 | if (dt == null) continue; 29 | 30 | const u0 = dt.u / 128; 31 | const v0 = dt.v / 128; 32 | const u1 = (dt.u + 8) / 128; 33 | const v1 = (dt.v + 8) / 128; 34 | builder.begin(DrawMode.QUADS, VertexFormats.POSITION_TEXTURE_COLOR); 35 | 36 | builder.vertex(xx, y, 0).texture(u0, v0).color(color[0], color[1], color[2], color[3]).next(); 37 | builder.vertex(xx, y1, 0).texture(u0, v1).color(color[0], color[1], color[2], color[3]).next(); 38 | builder 39 | .vertex(xx + 8, y1, 0) 40 | .texture(u1, v1) 41 | .color(color[0], color[1], color[2], color[3]) 42 | .next(); 43 | builder 44 | .vertex(xx + 8, y, 0) 45 | .texture(u1, v0) 46 | .color(color[0], color[1], color[2], color[3]) 47 | .next(); 48 | builder.draw(); 49 | xx += dt['a']; 50 | } 51 | 52 | RenderSystem.disableBlend(); 53 | } 54 | 55 | draw(text: string, x: number, y: number, color: number[]) { 56 | this.drawWithShadowfv(text, x, y, color); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/controls.ts: -------------------------------------------------------------------------------- 1 | import { DrawContext } from './DrawContext'; 2 | import { RenderSystem } from './gl'; 3 | import { DEBUG } from './vars'; 4 | 5 | const DEBUG_COLOR = [1, 0, 0, 1]; 6 | 7 | abstract class UIProperty { 8 | name: string; 9 | protected _value: T; 10 | protected _initialValue: T; 11 | 12 | constructor(name: string, initialValue: T) { 13 | this.name = name; 14 | this._value = initialValue; 15 | this._initialValue = initialValue; 16 | } 17 | 18 | value(): T { 19 | return this._value; 20 | } 21 | 22 | protected abstract parse(value: any): T; 23 | 24 | setValue(value: any) { 25 | this._value = this.parse(value); 26 | } 27 | 28 | abstract isOfType(value: any): boolean; 29 | } 30 | 31 | class StringUIProperty extends UIProperty { 32 | protected parse(value: any): string { 33 | return value + ''; 34 | } 35 | 36 | isOfType(value: any): boolean { 37 | return typeof value == 'string'; 38 | } 39 | } 40 | 41 | class StringEnumUIProperty extends UIProperty { 42 | values: string[]; 43 | constructor(name: string, initialValue: string, values: string[]) { 44 | super(name, initialValue); 45 | this.values = values; 46 | } 47 | 48 | protected parse(value: any): string { 49 | const vl = value + ''; 50 | if (this.values.indexOf(vl) >= 0) { 51 | return vl; 52 | } 53 | return this._initialValue; 54 | } 55 | 56 | isOfType(value: any): boolean { 57 | return typeof value == 'string' && this.values.indexOf(value) >= 0; 58 | } 59 | } 60 | 61 | class BooleanUIProperty extends UIProperty { 62 | protected parse(value: any): boolean { 63 | return typeof value == 'string' ? value.toLowerCase() == 'true' : Boolean(value); 64 | } 65 | 66 | isOfType(value: any): boolean { 67 | return typeof value == 'boolean'; 68 | } 69 | } 70 | 71 | class NumberUIProperty extends UIProperty { 72 | private validate: (vl: number) => number; 73 | 74 | public constructor(name: string, initialValue: number, validate: (vl: number) => number = (vl) => vl) { 75 | super(name, initialValue); 76 | this.validate = validate; 77 | } 78 | 79 | protected parse(value: any): number { 80 | try { 81 | return this.validate(parseFloat(value)); 82 | } catch (e) { 83 | return this.validate(this._value); 84 | } 85 | } 86 | 87 | isOfType(value: any): boolean { 88 | return typeof value == 'number'; 89 | } 90 | } 91 | 92 | export abstract class CustomRenderer { 93 | abstract render(context: DrawContext, control: UICustomControl, mouseX: number, mouseY: number): void; 94 | } 95 | 96 | export class DebugCustomRenderer extends CustomRenderer { 97 | override render(context: DrawContext, control: UICustomControl, mouseX: number, mouseY: number) { 98 | context.textRend.drawWithShadowfv('Debug', 0, 0, [1, 1, 1, 1]); 99 | } 100 | } 101 | 102 | export class NametagCustomRenderer extends CustomRenderer { 103 | override render(context: DrawContext, control: UICustomControl, mouseX: number, mouseY: number) { 104 | if (control.propertyBag['#playername'] != null) { 105 | const plnm = control.propertyBag['#playername'].currentValue; 106 | const size = plnm.length * 7; 107 | const xpad = control.propertyBag['#x_padding'] == null ? 3 : control.propertyBag['#x_padding'].currentValue; 108 | const ypad = control.propertyBag['#y_padding'] == null ? 2 : control.propertyBag['#y_padding'].currentValue; 109 | context.drawColoredfv(control.x - xpad, control.y - ypad, 0, size + xpad + xpad, 8 + ypad + ypad, control.bgColor); 110 | context.textRend.drawWithShadowfv(control.propertyBag['#playername'].currentValue, control.x + xpad, control.y + ypad, control.textColor); 111 | } 112 | } 113 | } 114 | 115 | function clamp(value: number, min: number, max: number) { 116 | if (value < min) return min; 117 | if (value > max) return max; 118 | return value; 119 | } 120 | 121 | function getDurabilityColors(ratio: number): { progress_color: number[]; background_color: number[] } { 122 | return { 123 | progress_color: [(255 - 255 * ratio) / 255.0, (255 * ratio) / 255.0, 0, 1.0], 124 | background_color: [(64 - 64 * ratio) / 255.0, 64 / 255.0, 0, 1.0] 125 | }; 126 | } 127 | 128 | export class ProgBarCustomRenderer extends CustomRenderer { 129 | override render(context: DrawContext, control: UICustomControl, mouseX: number, mouseY: number) { 130 | const currentAmount = control.getPropertyBagPropertyValueOrDefault('#progress_bar_current_amount', 1); 131 | const totalAmount = control.getPropertyBagPropertyValueOrDefault('#progress_bar_total_amount', 1); 132 | const isVisible = control.getPropertyBagPropertyValueOrDefault('#progress_bar_visible', true); 133 | const isDurability = control.getPropertyBagPropertyValueOrDefault('is_durability', false); 134 | 135 | if (!isVisible) return; 136 | 137 | const ratio = clamp(currentAmount / totalAmount, 0, 1); 138 | 139 | if (isDurability) { 140 | const durColors = getDurabilityColors(ratio); 141 | context.drawColoredfv(control.x, control.y, 0, control.w, control.h, durColors.background_color); 142 | context.drawColoredfv(control.x, control.y, 0, control.w * ratio, control.h, durColors.progress_color); 143 | } else { 144 | context.drawColoredfv(control.x, control.y, 0, control.w, control.h, control.secondaryColor); 145 | context.drawColoredfv(control.x, control.y, 0, control.w * ratio, control.h, control.primaryColor); 146 | } 147 | } 148 | } 149 | 150 | export class FillCustomRenderer extends CustomRenderer { 151 | override render(context: DrawContext, control: UICustomControl, mouseX: number, mouseY: number) { 152 | context.drawColoredfv(control.x, control.y, 0, control.w, control.h, control.color); 153 | } 154 | } 155 | 156 | export class GradientCustomRenderer extends CustomRenderer { 157 | override render(context: DrawContext, control: UICustomControl, mouseX: number, mouseY: number) { 158 | if (control.gradientDir == 'vertical') { 159 | context.drawVGradientfv(control.x, control.y, 0, control.w, control.h, control.color0, control.color1); 160 | } else { 161 | context.drawHGradientfv(control.x, control.y, 0, control.w, control.h, control.color0, control.color1); 162 | } 163 | } 164 | } 165 | 166 | export type UIEvent = 167 | | { 168 | type: 'mouse'; 169 | x: number; 170 | y: number; 171 | pressed: boolean; 172 | double_pressed: boolean; 173 | emit: (buttonId: string) => void; 174 | } 175 | | { 176 | type: 'keyboard'; 177 | key: string; 178 | x: number; 179 | y: number; 180 | emit: (buttonId: string) => void; 181 | } 182 | | { 183 | type: 'button_id'; 184 | id: string; 185 | pressed: boolean; 186 | double_pressed: boolean; 187 | x: number; 188 | y: number; 189 | emit: (buttonId: string, control: UIControl) => void; 190 | }; 191 | 192 | export abstract class UIControl { 193 | parent?: UIControl; 194 | path = ''; 195 | x = 0; 196 | y = 0; 197 | w = 0; 198 | h = 0; 199 | propertyBag: Record = {}; 200 | children: UIControl[] = []; 201 | visible = new BooleanUIProperty('visible', true); 202 | anchorFrom = new StringEnumUIProperty('anchor_from', 'top_left', ['top_left', 'top_middle', 'top_right', 'left_middle', 'center', 'right_middle', 'bottom_left', 'bottom_middle', 'bottom_right']); 203 | anchorTo = new StringEnumUIProperty('anchor_from', 'top_left', ['top_left', 'top_middle', 'top_right', 'left_middle', 'center', 'right_middle', 'bottom_left', 'bottom_middle', 'bottom_right']); 204 | enabled = new BooleanUIProperty('enabled', true); 205 | debug?: number[]; 206 | buttonMappings: { 207 | from_button_id: string; 208 | to_button_id: string; 209 | mappingType: 'pressed' | 'double_pressed' | 'global'; 210 | }[] = []; 211 | 212 | isEnabled(): boolean { 213 | return this.parent != null ? this.parent.isEnabled() && this.enabled.value() : this.enabled.value(); 214 | } 215 | 216 | abstract render(context: DrawContext, mouseX: number, mouseY: number): void; 217 | 218 | getPropertyBagPropertyValueOrDefault(name: string, defaultValue: any) { 219 | return this.propertyBag[name] == null ? defaultValue : this.propertyBag[name].currentValue; 220 | } 221 | 222 | layout() {} 223 | 224 | isMouseOver(mouseX: number, mouseY: number) { 225 | return mouseX >= this.x && mouseX < this.x + this.w && mouseY >= this.y && mouseY < this.y + this.h; 226 | } 227 | 228 | onEvent(event: UIEvent) { 229 | for (const mapping of this.buttonMappings) { 230 | if (event.type == 'button_id' && mapping.from_button_id == event.id) { 231 | if (this instanceof UIButtonControl) console.log(event, mapping); 232 | if (event.double_pressed && mapping.mappingType == 'double_pressed' && this.isMouseOver(event.x, event.y)) { 233 | event.emit(mapping.to_button_id, this); 234 | return; 235 | } else if (event.pressed && mapping.mappingType == 'pressed' && this.isMouseOver(event.x, event.y)) { 236 | event.emit(mapping.to_button_id, this); 237 | return; 238 | } else if (event.pressed && mapping.mappingType == 'global') { 239 | event.emit(mapping.to_button_id, this); 240 | } 241 | } 242 | } 243 | 244 | for (let i = 0; i < this.children.length; ++i) { 245 | this.children[i].onEvent(event); 246 | } 247 | } 248 | } 249 | 250 | export class UIButtonControl extends UIControl { 251 | defaultControl = new StringUIProperty('default_control', 'default'); 252 | hoverControl = new StringUIProperty('hover_control', 'hover'); 253 | lockedControl = new StringUIProperty('locked_control', 'locked'); 254 | 255 | private dirty = true; 256 | 257 | private hovered = false; 258 | 259 | public render(context: DrawContext, mouseX: number, mouseY: number): void { 260 | if (!this.visible.value()) return; 261 | 262 | this.hovered = mouseX >= this.x && mouseX < this.x + this.w && mouseY >= this.y && mouseY < this.y + this.h; 263 | 264 | if (this.dirty) { 265 | this.dirty = false; 266 | 267 | for (let i = 0; i < this.children.length; ++i) { 268 | const control = this.children[i]; 269 | control.x += this.x; 270 | control.y += this.y; 271 | } 272 | } 273 | 274 | for (let i = 0; i < this.children.length; ++i) { 275 | const control = this.children[i]; 276 | 277 | if (control.path.endsWith('/' + this.defaultControl.value())) { 278 | control.visible.setValue(this.enabled.value() && !this.hovered); 279 | } else if (control.path.endsWith('/' + this.hoverControl.value())) { 280 | control.visible.setValue(this.enabled.value() && this.hovered); 281 | } else if (control.path.endsWith('/' + this.lockedControl.value())) { 282 | control.visible.setValue(!this.enabled.value()); 283 | } 284 | 285 | control.render(context, mouseX, mouseY); 286 | } 287 | 288 | if (DEBUG) { 289 | context.drawBorder(this.x, this.y, 0, this.w, this.h, 1, 0, 0, 1); 290 | } else if (this.debug != null) { 291 | context.drawBorder(this.x, this.y, 0, this.w, this.h, this.debug[0], this.debug[1], this.debug[2], this.debug[3]); 292 | } 293 | } 294 | } 295 | 296 | export class UIScreenControl extends UIControl { 297 | renderOnlyWhenTopMost = true; 298 | forceRenderBelow = false; 299 | 300 | public render(context: DrawContext, mouseX: number, mouseY: number): void { 301 | if (!this.visible.value()) return; 302 | 303 | for (let i = 0; i < this.children.length; ++i) { 304 | this.children[i].render(context, mouseX, mouseY); 305 | } 306 | } 307 | } 308 | 309 | export class UIStackPanelControl extends UIControl { 310 | orientation = new StringEnumUIProperty('orientation', 'vertical', ['vertical', 'horizontal']); 311 | rowgap = new NumberUIProperty('row_gap', 0, (vl) => clamp(vl, 0, Infinity)); 312 | colgap = new NumberUIProperty('column_gap', 0, (vl) => clamp(vl, 0, Infinity)); 313 | private dirty = true; 314 | 315 | public render(context: DrawContext, mouseX: number, mouseY: number): void { 316 | if (!this.visible.value()) return; 317 | 318 | if (this.dirty) { 319 | this.dirty = false; 320 | 321 | if (this.orientation.value() == 'horizontal') { 322 | let xx = this.x; 323 | 324 | for (let i = 0; i < this.children.length; ++i) { 325 | this.children[i].x = xx; 326 | this.children[i].y = this.y; 327 | xx += this.children[i].w + this.colgap.value(); 328 | } 329 | } else { 330 | let yy = this.y; 331 | 332 | for (let i = 0; i < this.children.length; ++i) { 333 | this.children[i].y = yy; 334 | this.children[i].x = this.x; 335 | yy += this.children[i].h + this.rowgap.value(); 336 | } 337 | } 338 | } 339 | 340 | for (let i = 0; i < this.children.length; ++i) { 341 | this.children[i].render(context, mouseX, mouseY); 342 | } 343 | 344 | if (DEBUG) { 345 | context.drawBorder(this.x, this.y, 0, this.w, this.h, 1, 0, 0, 1); 346 | } else if (this.debug != null) { 347 | context.drawBorder(this.x, this.y, 0, this.w, this.h, this.debug[0], this.debug[1], this.debug[2], this.debug[3]); 348 | } 349 | } 350 | } 351 | 352 | export class UICustomControl extends UIControl { 353 | renderer?: CustomRenderer; 354 | gradientDir: 'vertical' | 'horizontal' = 'vertical'; 355 | color0 = [1, 1, 1, 1]; 356 | color1 = [1, 1, 1, 1]; 357 | color = [1, 1, 1, 1]; 358 | textColor = [1, 1, 1, 1]; 359 | bgColor = [50 / 255.0, 50 / 255.0, 50 / 255.0, 0.6]; 360 | dirty = true; 361 | primaryColor = [0, 255 / 255.0, 0, 1.0]; 362 | secondaryColor = [76 / 255.0, 76 / 255.0, 76 / 255.0, 1]; 363 | 364 | public render(context: DrawContext, mouseX: number, mouseY: number) { 365 | if (!this.visible.value()) return; 366 | 367 | if (this.dirty) { 368 | this.dirty = false; 369 | } 370 | 371 | if (this.renderer != null) { 372 | this.renderer.render(context, this, mouseX, mouseY); 373 | } 374 | 375 | for (let i = 0; i < this.children.length; ++i) { 376 | this.children[i].render(context, mouseX, mouseY); 377 | } 378 | 379 | if (DEBUG) { 380 | context.drawBorder(this.x, this.y, 0, this.w, this.h, 1, 0, 0, 1); 381 | } else if (this.debug != null) { 382 | context.drawBorder(this.x, this.y, 0, this.w, this.h, this.debug[0], this.debug[1], this.debug[2], this.debug[3]); 383 | } 384 | } 385 | } 386 | 387 | export class UILabelControl extends UIControl { 388 | text = ''; 389 | color = [1, 1, 1, 1]; 390 | lockedColor = [1, 1, 1, 1]; 391 | shadow = false; 392 | 393 | public render(context: DrawContext, mouseX: number, mouseY: number): void { 394 | if (!this.visible.value()) return; 395 | 396 | if (this.text.length > 0) { 397 | context.textRend.drawWithShadowfv(this.text, this.x, this.y, this.isEnabled() ? this.color : this.lockedColor); 398 | } 399 | 400 | for (let i = 0; i < this.children.length; ++i) { 401 | this.children[i].render(context, mouseX, mouseY); 402 | } 403 | 404 | if (DEBUG) { 405 | context.drawBorder(this.x, this.y, 0, this.w, this.h, 1, 0, 0, 1); 406 | } else if (this.debug != null) { 407 | context.drawBorder(this.x, this.y, 0, this.w, this.h, this.debug[0], this.debug[1], this.debug[2], this.debug[3]); 408 | } 409 | } 410 | } 411 | 412 | export class UIImageControl extends UIControl { 413 | texture = 'null'; 414 | u = 0; 415 | v = 0; 416 | us = -1; 417 | vs = -1; 418 | tw = -1; 419 | th = -1; 420 | color = [1, 1, 1, 1]; 421 | tiled: 'x' | 'y' | 'both' | 'none' = 'none'; 422 | tiledScale = [1, 1]; 423 | clipRatio = 1; 424 | grayscale = false; 425 | 426 | public constructor() { 427 | super(); 428 | } 429 | 430 | public render(context: DrawContext, mouseX: number, mouseY: number) { 431 | if (!this.visible.value()) return; 432 | 433 | if (this.texture != 'null') { 434 | const tex = RenderSystem.getTexture(this.texture); 435 | 436 | context.drawColoredTexture( 437 | this.texture, 438 | this.x, 439 | this.y, 440 | 0, 441 | this.w, 442 | this.h, 443 | this.u, 444 | this.v, 445 | this.us == -1 ? tex.width : this.us, 446 | this.vs == -1 ? tex.height : this.vs, 447 | this.tw == -1 ? tex.width : this.tw, 448 | this.th == -1 ? tex.height : this.th, 449 | this.color, 450 | this.grayscale 451 | ); 452 | } 453 | 454 | for (let i = 0; i < this.children.length; ++i) { 455 | this.children[i].render(context, mouseX, mouseY); 456 | } 457 | 458 | if (DEBUG) { 459 | context.drawBorder(this.x, this.y, 0, this.w, this.h, 1, 0, 0, 1); 460 | } else if (this.debug != null) { 461 | context.drawBorder(this.x, this.y, 0, this.w, this.h, this.debug[0], this.debug[1], this.debug[2], this.debug[3]); 462 | } 463 | } 464 | } 465 | 466 | export class BindingObserver { 467 | ups: ((v: any) => void)[] = []; 468 | currentValue: any; 469 | 470 | constructor(up?: (v: any) => void) { 471 | if (up != null) this.ups.push(up); 472 | } 473 | 474 | addUp(up: (v: any) => void) { 475 | this.ups.push(up); 476 | } 477 | 478 | update(value: any) { 479 | this.currentValue = value; 480 | for (let i = 0; i < this.ups.length; ++i) { 481 | this.ups[i](value); 482 | } 483 | } 484 | } 485 | 486 | export class Binding { 487 | obs: BindingObserver[] = []; 488 | value: any; 489 | 490 | setValue(v: any) { 491 | this.value = v; 492 | this.notifyAll(); 493 | return this; 494 | } 495 | 496 | add(ob: BindingObserver) { 497 | this.obs.push(ob); 498 | } 499 | 500 | notifyAll() { 501 | this.obs.forEach((ob) => ob.update(this.value)); 502 | } 503 | } 504 | 505 | const typeProp = new StringEnumUIProperty('type', '', ['image', 'label', 'custom', 'stack_panel', 'button', 'screen']); 506 | 507 | export function createControl( 508 | name: string, 509 | parent: UIControl | null, 510 | parsedTrees: Record>, 511 | props: any, 512 | bindings: Record, 513 | tree: Record 514 | ) { 515 | let control: UIControl; 516 | if (props['type'] == 'image') { 517 | control = new UIImageControl(); 518 | } else if (props['type'] == 'label') { 519 | control = new UILabelControl(); 520 | } else if (props['type'] == 'custom') { 521 | control = new UICustomControl(); 522 | } else if (props['type'] == 'stack_panel') { 523 | control = new UIStackPanelControl(); 524 | } else if (props['type'] == 'button') { 525 | control = new UIButtonControl(); 526 | } else if (props['type'] == 'screen') { 527 | control = new UIScreenControl(); 528 | } else { 529 | const path = (parent != null ? parent.path : '') + '/' + name; 530 | (window as any).writeToConsole(path + ": type doesn't allow value '" + props['type'] + "'. Possible value: " + [...typeProp.values].join(', '), 'error'); 531 | return; 532 | } 533 | 534 | function validateAndSet(prop: UIProperty, value: any) { 535 | if (prop.isOfType(value)) prop.setValue(value); 536 | else (window as any).writeToConsole(control.path + ': ' + prop.name + " doesn't allow the value '" + (typeof value === 'string' ? '"' + value + '"' : value) + "'", 'error'); 537 | } 538 | 539 | control.path = (parent != null ? parent.path : '') + '/' + name; 540 | 541 | if (props['offset'] != null) { 542 | if (props['x'] == null) props['x'] = props['offset'][0]; 543 | if (props['y'] == null) props['y'] = props['offset'][1]; 544 | } 545 | 546 | if (props['x'] != null) { 547 | const vx = props['x']; 548 | 549 | if (typeof vx === 'string' && vx.startsWith('#')) { 550 | if (control.propertyBag[vx] == null) { 551 | control.propertyBag[vx] = new BindingObserver((v) => (control.x = v)); 552 | } 553 | } else { 554 | control.x = parseFloat(vx); 555 | } 556 | } 557 | 558 | if (props['y'] != null) { 559 | const vx = props['y']; 560 | 561 | if (typeof vx === 'string' && vx.startsWith('#')) { 562 | if (control.propertyBag[vx] == null) control.propertyBag[vx] = new BindingObserver((v) => (control.y = v)); 563 | } else { 564 | control.y = parseFloat(vx); 565 | } 566 | } 567 | 568 | if (props['size'] != null) { 569 | if (props['width'] == null) props['width'] = props['size'][0]; 570 | if (props['height'] == null) props['height'] = props['size'][1]; 571 | } 572 | 573 | if (props['enabled'] != null) { 574 | const vx = props['enabled']; 575 | if (typeof vx === 'string' && vx.startsWith('#')) { 576 | if (control.propertyBag[vx] == null) control.propertyBag[vx] = new BindingObserver((v) => control.enabled.setValue(v)); 577 | else control.propertyBag[vx].addUp((v) => control.enabled.setValue(v)); 578 | } else { 579 | validateAndSet(control.enabled, props['enabled']); 580 | } 581 | } 582 | 583 | if (props['visible'] != null) { 584 | const vx = props['enabled']; 585 | if (typeof vx === 'string' && vx.startsWith('#')) { 586 | if (control.propertyBag[vx] == null) control.propertyBag[vx] = new BindingObserver((v) => control.visible.setValue(v)); 587 | else control.propertyBag[vx].addUp((v) => control.visible.setValue(v)); 588 | } else { 589 | validateAndSet(control.enabled, props['visible']); 590 | } 591 | } 592 | 593 | if (props['width'] != null) { 594 | const vx = props['width']; 595 | if (typeof vx === 'string' && vx.startsWith('#')) { 596 | if (control.propertyBag[vx] == null) control.propertyBag[vx] = new BindingObserver((v) => (control.w = v)); 597 | else control.propertyBag[vx].addUp((v) => (control.w = v)); 598 | } else { 599 | control.w = props['width']; 600 | } 601 | } 602 | 603 | if (props['height'] != null) { 604 | const vx = props['height']; 605 | if (typeof vx === 'string' && vx.startsWith('#')) { 606 | if (control.propertyBag[vx] == null) control.propertyBag[vx] = new BindingObserver((v) => (control.h = v)); 607 | else control.propertyBag[vx].addUp((v) => (control.h = v)); 608 | } else { 609 | control.h = props['height']; 610 | } 611 | } 612 | 613 | if (props['debug'] != null) { 614 | if (props['debug'] == 'red') { 615 | control.debug = [1, 0, 0, 1]; 616 | } else if (props['debug'] == 'white') { 617 | control.debug = [1, 1, 1, 1]; 618 | } else if (props['debug'] == 'black') { 619 | control.debug = [0, 0, 0, 1]; 620 | } else if (props['debug'] == 'yellow') { 621 | control.debug = [1, 1, 0, 1]; 622 | } else if (props['debug'] == 'green') { 623 | control.debug = [0, 1, 0, 1]; 624 | } else if (props['debug'] == 'blue') { 625 | control.debug = [0, 0, 1, 1]; 626 | } else if (props['debug'] == 'magenta') { 627 | control.debug = [1, 0, 1, 1]; 628 | } 629 | } 630 | 631 | if (control instanceof UIImageControl) { 632 | if (props['texture'] != null) { 633 | control.texture = props['texture']; 634 | } 635 | 636 | if (props['grayscale'] != null) { 637 | control.grayscale = props['grayscale']; 638 | } 639 | 640 | if (props['color'] != null) { 641 | const p = props['color']; 642 | if (Array.isArray(p) && p.length >= 3) { 643 | if (p.length == 3) control.color = [...p, 1]; 644 | else if (p.length == 4) control.color = p; 645 | } 646 | } 647 | 648 | if (props['uv'] != null) { 649 | control.u = props['uv'][0]; 650 | control.v = props['uv'][1]; 651 | } 652 | 653 | if (props['uv_size'] != null) { 654 | if (props['u_size'] == null) props['u_size'] = props['uv_size'][0]; 655 | if (props['v_size'] == null) props['v_size'] = props['uv_size'][1]; 656 | } 657 | 658 | if (props['u_size'] != null) { 659 | control.us = props['u_size']; 660 | } 661 | 662 | if (props['v_size'] != null) { 663 | control.vs = props['v_size']; 664 | } 665 | 666 | if (props['base_size'] != null) { 667 | if (props['base_width'] == null) props['base_width'] = props['base_size'][0]; 668 | if (props['base_height'] == null) props['base_height'] = props['base_size'][1]; 669 | } 670 | 671 | if (props['base_width'] != null) { 672 | control.tw = props['base_width']; 673 | } 674 | 675 | if (props['base_height'] != null) { 676 | control.th = props['base_height']; 677 | } 678 | } else if (control instanceof UICustomControl) { 679 | if (props['renderer'] != null) { 680 | control.renderer = 681 | props['renderer'] == 'fill_renderer' 682 | ? new FillCustomRenderer() 683 | : props['renderer'] == 'name_tag_renderer' 684 | ? new NametagCustomRenderer() 685 | : props['renderer'] == 'progress_bar_renderer' 686 | ? new ProgBarCustomRenderer() 687 | : props['renderer'] == 'debug_renderer' 688 | ? new DebugCustomRenderer() 689 | : new GradientCustomRenderer(); 690 | } 691 | 692 | if (props['color'] != null) { 693 | const p = props['color']; 694 | if (Array.isArray(p) && p.length >= 3) { 695 | if (p.length == 3) control.color = [...p, 1]; 696 | else if (p.length == 4) control.color = p; 697 | } 698 | } 699 | 700 | if (props['color1'] != null) { 701 | const vx = props['color1']; 702 | if (typeof vx === 'string' && vx.startsWith('#')) { 703 | if (control.propertyBag[vx] == null) control.propertyBag[vx] = new BindingObserver((v) => ((control as UICustomControl).color0 = v)); 704 | } else { 705 | control.color0 = props['color1']; 706 | } 707 | } 708 | 709 | if (props['color2'] != null) { 710 | const vx = props['color2']; 711 | if (typeof vx === 'string' && vx.startsWith('#')) { 712 | if (control.propertyBag[vx] == null) control.propertyBag[vx] = new BindingObserver((v) => ((control as UICustomControl).color1 = v)); 713 | } else { 714 | control.color1 = props['color2']; 715 | } 716 | } 717 | 718 | if (props['gradient_direction'] != null) { 719 | control.gradientDir = props['gradient_direction']; 720 | } 721 | } else if (control instanceof UIStackPanelControl) { 722 | if (props['orientation'] != null) { 723 | validateAndSet(control.orientation, props['orientation']); 724 | } 725 | 726 | if (props['gap'] != null) { 727 | if (props['column_gap'] == null) props['column_gap'] = props['gap'][1]; 728 | if (props['row_gap'] == null) props['row_gap'] = props['gap'][0]; 729 | } 730 | 731 | if (props['row_gap'] != null) { 732 | validateAndSet(control.rowgap, props['row_gap']); 733 | } 734 | 735 | if (props['column_gap'] != null) { 736 | validateAndSet(control.colgap, props['column_gap']); 737 | } 738 | } else if (control instanceof UIScreenControl) { 739 | if (props['force_render_below'] != null) { 740 | control.forceRenderBelow = props['force_render_below']; 741 | } 742 | 743 | if (props['render_only_when_top_most'] != null) { 744 | control.renderOnlyWhenTopMost = props['render_only_when_top_most']; 745 | } 746 | } else if (control instanceof UILabelControl) { 747 | if (props['text'] != null) { 748 | const vx = props['text']; 749 | if (typeof vx === 'string' && vx.startsWith('#')) { 750 | if (control.propertyBag[vx] == null) control.propertyBag[vx] = new BindingObserver((v) => ((control as UILabelControl).text = v)); 751 | } else { 752 | control.text = props['text']; 753 | } 754 | } 755 | 756 | if (props['color'] != null) { 757 | control.color = props['color']; 758 | } 759 | 760 | if (props['locked_color'] != null) { 761 | control.lockedColor = props['locked_color']; 762 | } 763 | 764 | if (props['shadow'] != null) { 765 | control.text = props['shadow']; 766 | } 767 | } else if (control instanceof UIButtonControl) { 768 | if (props[control.defaultControl.name]) { 769 | validateAndSet(control.defaultControl, props[control.defaultControl.name]); 770 | } 771 | if (props[control.lockedControl.name]) { 772 | validateAndSet(control.lockedControl, props[control.lockedControl.name]); 773 | } 774 | if (props[control.lockedControl.name]) { 775 | validateAndSet(control.lockedControl, props[control.lockedControl.name]); 776 | } 777 | } 778 | 779 | if (props['button_mappings'] != null) { 780 | for (const mapping of props['button_mappings']) { 781 | control.buttonMappings.push({ from_button_id: mapping['from_button_id'], to_button_id: mapping['to_button_id'], mappingType: mapping['mapping_type'] }); 782 | } 783 | 784 | console.log(control.buttonMappings); 785 | } 786 | 787 | if (props['controls'] != null) { 788 | for (const child of props['controls']) { 789 | for (const [ky, vl] of Object.entries(child)) { 790 | const ctrl = createControl(ky, control, parsedTrees, vl, bindings, tree); 791 | if (ctrl) { 792 | ctrl.parent = control; 793 | tree[ctrl.path] = ctrl; 794 | control.children.push(ctrl); 795 | } 796 | break; 797 | } 798 | } 799 | } 800 | 801 | control.propertyBag['#visible'] = new BindingObserver((v) => control.visible.setValue(v)); 802 | control.propertyBag['#visible'].currentValue = true; 803 | 804 | control.propertyBag['#enabled'] = new BindingObserver((v) => control.enabled.setValue(v)); 805 | control.propertyBag['#enabled'].currentValue = true; 806 | 807 | if (props['property_bag'] != null) { 808 | Object.entries(props['property_bag']).forEach(([k, v]) => { 809 | if (control.propertyBag[k] == null) control.propertyBag[k] = new BindingObserver(); 810 | control.propertyBag[k].currentValue = v; 811 | }); 812 | } 813 | 814 | if (props['bindings'] != null) { 815 | for (const bind of props['bindings']) { 816 | const bn = bind['binding_name']; 817 | let bnov = bind['binding_name_override']; 818 | if (bnov == null) bnov = bn; 819 | 820 | if (bindings[bn] == null) bindings[bn] = new Binding(); 821 | 822 | if (control.propertyBag[bnov] == null) { 823 | control.propertyBag[bnov] = new BindingObserver(); 824 | } 825 | 826 | bindings[bn].add(control.propertyBag[bnov]); 827 | } 828 | } 829 | 830 | return control; 831 | } 832 | -------------------------------------------------------------------------------- /src/gl.ts: -------------------------------------------------------------------------------- 1 | import { Matrix4f } from './math'; 2 | 3 | export const GL = { 4 | DEPTH_BUFFER_BIT: 0x00000100, 5 | STENCIL_BUFFER_BIT: 0x00000400, 6 | COLOR_BUFFER_BIT: 0x00004000, 7 | POINTS: 0x0000, 8 | LINES: 0x0001, 9 | LINE_LOOP: 0x0002, 10 | LINE_STRIP: 0x0003, 11 | TRIANGLES: 0x0004, 12 | TRIANGLE_STRIP: 0x0005, 13 | TRIANGLE_FAN: 0x0006, 14 | ZERO: 0, 15 | ONE: 1, 16 | SRC_COLOR: 0x0300, 17 | ONE_MINUS_SRC_COLOR: 0x0301, 18 | SRC_ALPHA: 0x0302, 19 | ONE_MINUS_SRC_ALPHA: 0x0303, 20 | DST_ALPHA: 0x0304, 21 | ONE_MINUS_DST_ALPHA: 0x0305, 22 | DST_COLOR: 0x0306, 23 | ONE_MINUS_DST_COLOR: 0x0307, 24 | SRC_ALPHA_SATURATE: 0x0308, 25 | FUNC_ADD: 0x8006, 26 | BLEND_EQUATION: 0x8009, 27 | BLEND_EQUATION_RGB: 0x8009, 28 | BLEND_EQUATION_ALPHA: 0x883d, 29 | FUNC_SUBTRACT: 0x800a, 30 | FUNC_REVERSE_SUBTRACT: 0x800b, 31 | BLEND_DST_RGB: 0x80c8, 32 | BLEND_SRC_RGB: 0x80c9, 33 | BLEND_DST_ALPHA: 0x80ca, 34 | BLEND_SRC_ALPHA: 0x80cb, 35 | CONSTANT_COLOR: 0x8001, 36 | ONE_MINUS_CONSTANT_COLOR: 0x8002, 37 | CONSTANT_ALPHA: 0x8003, 38 | ONE_MINUS_CONSTANT_ALPHA: 0x8004, 39 | BLEND_COLOR: 0x8005, 40 | ARRAY_BUFFER: 0x8892, 41 | ELEMENT_ARRAY_BUFFER: 0x8893, 42 | ARRAY_BUFFER_BINDING: 0x8894, 43 | ELEMENT_ARRAY_BUFFER_BINDING: 0x8895, 44 | STREAM_DRAW: 0x88e0, 45 | STATIC_DRAW: 0x88e4, 46 | DYNAMIC_DRAW: 0x88e8, 47 | BUFFER_SIZE: 0x8764, 48 | BUFFER_USAGE: 0x8765, 49 | CURRENT_VERTEX_ATTRIB: 0x8626, 50 | FRONT: 0x0404, 51 | BACK: 0x0405, 52 | FRONT_AND_BACK: 0x0408, 53 | CULL_FACE: 0x0b44, 54 | BLEND: 0x0be2, 55 | DITHER: 0x0bd0, 56 | STENCIL_TEST: 0x0b90, 57 | DEPTH_TEST: 0x0b71, 58 | SCISSOR_TEST: 0x0c11, 59 | POLYGON_OFFSET_FILL: 0x8037, 60 | SAMPLE_ALPHA_TO_COVERAGE: 0x809e, 61 | SAMPLE_COVERAGE: 0x80a0, 62 | NO_ERROR: 0, 63 | INVALID_ENUM: 0x0500, 64 | INVALID_VALUE: 0x0501, 65 | INVALID_OPERATION: 0x0502, 66 | OUT_OF_MEMORY: 0x0505, 67 | CW: 0x0900, 68 | CCW: 0x0901, 69 | LINE_WIDTH: 0x0b21, 70 | ALIASED_POINT_SIZE_RANGE: 0x846d, 71 | ALIASED_LINE_WIDTH_RANGE: 0x846e, 72 | CULL_FACE_MODE: 0x0b45, 73 | FRONT_FACE: 0x0b46, 74 | DEPTH_RANGE: 0x0b70, 75 | DEPTH_WRITEMASK: 0x0b72, 76 | DEPTH_CLEAR_VALUE: 0x0b73, 77 | DEPTH_FUNC: 0x0b74, 78 | STENCIL_CLEAR_VALUE: 0x0b91, 79 | STENCIL_FUNC: 0x0b92, 80 | STENCIL_FAIL: 0x0b94, 81 | STENCIL_PASS_DEPTH_FAIL: 0x0b95, 82 | STENCIL_PASS_DEPTH_PASS: 0x0b96, 83 | STENCIL_REF: 0x0b97, 84 | STENCIL_VALUE_MASK: 0x0b93, 85 | STENCIL_WRITEMASK: 0x0b98, 86 | STENCIL_BACK_FUNC: 0x8800, 87 | STENCIL_BACK_FAIL: 0x8801, 88 | STENCIL_BACK_PASS_DEPTH_FAIL: 0x8802, 89 | STENCIL_BACK_PASS_DEPTH_PASS: 0x8803, 90 | STENCIL_BACK_REF: 0x8ca3, 91 | STENCIL_BACK_VALUE_MASK: 0x8ca4, 92 | STENCIL_BACK_WRITEMASK: 0x8ca5, 93 | VIEWPORT: 0x0ba2, 94 | SCISSOR_BOX: 0x0c10, 95 | COLOR_CLEAR_VALUE: 0x0c22, 96 | COLOR_WRITEMASK: 0x0c23, 97 | UNPACK_ALIGNMENT: 0x0cf5, 98 | PACK_ALIGNMENT: 0x0d05, 99 | MAX_TEXTURE_SIZE: 0x0d33, 100 | MAX_VIEWPORT_DIMS: 0x0d3a, 101 | SUBPIXEL_BITS: 0x0d50, 102 | RED_BITS: 0x0d52, 103 | GREEN_BITS: 0x0d53, 104 | BLUE_BITS: 0x0d54, 105 | ALPHA_BITS: 0x0d55, 106 | DEPTH_BITS: 0x0d56, 107 | STENCIL_BITS: 0x0d57, 108 | POLYGON_OFFSET_UNITS: 0x2a00, 109 | POLYGON_OFFSET_FACTOR: 0x8038, 110 | TEXTURE_BINDING_2D: 0x8069, 111 | SAMPLE_BUFFERS: 0x80a8, 112 | SAMPLES: 0x80a9, 113 | SAMPLE_COVERAGE_VALUE: 0x80aa, 114 | SAMPLE_COVERAGE_INVERT: 0x80ab, 115 | COMPRESSED_TEXTURE_FORMATS: 0x86a3, 116 | DONT_CARE: 0x1100, 117 | FASTEST: 0x1101, 118 | NICEST: 0x1102, 119 | GENERATE_MIPMAP_HINT: 0x8192, 120 | BYTE: 0x1400, 121 | UNSIGNED_BYTE: 0x1401, 122 | SHORT: 0x1402, 123 | UNSIGNED_SHORT: 0x1403, 124 | INT: 0x1404, 125 | UNSIGNED_INT: 0x1405, 126 | FLOAT: 0x1406, 127 | DEPTH_COMPONENT: 0x1902, 128 | ALPHA: 0x1906, 129 | RGB: 0x1907, 130 | RGBA: 0x1908, 131 | LUMINANCE: 0x1909, 132 | LUMINANCE_ALPHA: 0x190a, 133 | UNSIGNED_SHORT_4_4_4_4: 0x8033, 134 | UNSIGNED_SHORT_5_5_5_1: 0x8034, 135 | UNSIGNED_SHORT_5_6_5: 0x8363, 136 | FRAGMENT_SHADER: 0x8b30, 137 | VERTEX_SHADER: 0x8b31, 138 | MAX_VERTEX_ATTRIBS: 0x8869, 139 | MAX_VERTEX_UNIFORM_VECTORS: 0x8dfb, 140 | MAX_VARYING_VECTORS: 0x8dfc, 141 | MAX_COMBINED_TEXTURE_IMAGE_UNITS: 0x8b4d, 142 | MAX_VERTEX_TEXTURE_IMAGE_UNITS: 0x8b4c, 143 | MAX_TEXTURE_IMAGE_UNITS: 0x8872, 144 | MAX_FRAGMENT_UNIFORM_VECTORS: 0x8dfd, 145 | SHADER_TYPE: 0x8b4f, 146 | DELETE_STATUS: 0x8b80, 147 | LINK_STATUS: 0x8b82, 148 | VALIDATE_STATUS: 0x8b83, 149 | ATTACHED_SHADERS: 0x8b85, 150 | ACTIVE_UNIFORMS: 0x8b86, 151 | ACTIVE_ATTRIBUTES: 0x8b89, 152 | SHADING_LANGUAGE_VERSION: 0x8b8c, 153 | CURRENT_PROGRAM: 0x8b8d, 154 | NEVER: 0x0200, 155 | LESS: 0x0201, 156 | EQUAL: 0x0202, 157 | LEQUAL: 0x0203, 158 | GREATER: 0x0204, 159 | NOTEQUAL: 0x0205, 160 | GEQUAL: 0x0206, 161 | ALWAYS: 0x0207, 162 | KEEP: 0x1e00, 163 | REPLACE: 0x1e01, 164 | INCR: 0x1e02, 165 | DECR: 0x1e03, 166 | INVERT: 0x150a, 167 | INCR_WRAP: 0x8507, 168 | DECR_WRAP: 0x8508, 169 | VENDOR: 0x1f00, 170 | RENDERER: 0x1f01, 171 | VERSION: 0x1f02, 172 | NEAREST: 0x2600, 173 | LINEAR: 0x2601, 174 | NEAREST_MIPMAP_NEAREST: 0x2700, 175 | LINEAR_MIPMAP_NEAREST: 0x2701, 176 | NEAREST_MIPMAP_LINEAR: 0x2702, 177 | LINEAR_MIPMAP_LINEAR: 0x2703, 178 | TEXTURE_MAG_FILTER: 0x2800, 179 | TEXTURE_MIN_FILTER: 0x2801, 180 | TEXTURE_WRAP_S: 0x2802, 181 | TEXTURE_WRAP_T: 0x2803, 182 | TEXTURE_2D: 0x0de1, 183 | TEXTURE: 0x1702, 184 | TEXTURE_CUBE_MAP: 0x8513, 185 | TEXTURE_BINDING_CUBE_MAP: 0x8514, 186 | TEXTURE_CUBE_MAP_POSITIVE_X: 0x8515, 187 | TEXTURE_CUBE_MAP_NEGATIVE_X: 0x8516, 188 | TEXTURE_CUBE_MAP_POSITIVE_Y: 0x8517, 189 | TEXTURE_CUBE_MAP_NEGATIVE_Y: 0x8518, 190 | TEXTURE_CUBE_MAP_POSITIVE_Z: 0x8519, 191 | TEXTURE_CUBE_MAP_NEGATIVE_Z: 0x851a, 192 | MAX_CUBE_MAP_TEXTURE_SIZE: 0x851c, 193 | TEXTURE0: 0x84c0, 194 | TEXTURE1: 0x84c1, 195 | TEXTURE2: 0x84c2, 196 | TEXTURE3: 0x84c3, 197 | TEXTURE4: 0x84c4, 198 | TEXTURE5: 0x84c5, 199 | TEXTURE6: 0x84c6, 200 | TEXTURE7: 0x84c7, 201 | TEXTURE8: 0x84c8, 202 | TEXTURE9: 0x84c9, 203 | TEXTURE10: 0x84ca, 204 | TEXTURE11: 0x84cb, 205 | TEXTURE12: 0x84cc, 206 | TEXTURE13: 0x84cd, 207 | TEXTURE14: 0x84ce, 208 | TEXTURE15: 0x84cf, 209 | TEXTURE16: 0x84d0, 210 | TEXTURE17: 0x84d1, 211 | TEXTURE18: 0x84d2, 212 | TEXTURE19: 0x84d3, 213 | TEXTURE20: 0x84d4, 214 | TEXTURE21: 0x84d5, 215 | TEXTURE22: 0x84d6, 216 | TEXTURE23: 0x84d7, 217 | TEXTURE24: 0x84d8, 218 | TEXTURE25: 0x84d9, 219 | TEXTURE26: 0x84da, 220 | TEXTURE27: 0x84db, 221 | TEXTURE28: 0x84dc, 222 | TEXTURE29: 0x84dd, 223 | TEXTURE30: 0x84de, 224 | TEXTURE31: 0x84df, 225 | ACTIVE_TEXTURE: 0x84e0, 226 | REPEAT: 0x2901, 227 | CLAMP_TO_EDGE: 0x812f, 228 | MIRRORED_REPEAT: 0x8370, 229 | FLOAT_VEC2: 0x8b50, 230 | FLOAT_VEC3: 0x8b51, 231 | FLOAT_VEC4: 0x8b52, 232 | INT_VEC2: 0x8b53, 233 | INT_VEC3: 0x8b54, 234 | INT_VEC4: 0x8b55, 235 | BOOL: 0x8b56, 236 | BOOL_VEC2: 0x8b57, 237 | BOOL_VEC3: 0x8b58, 238 | BOOL_VEC4: 0x8b59, 239 | FLOAT_MAT2: 0x8b5a, 240 | FLOAT_MAT3: 0x8b5b, 241 | FLOAT_MAT4: 0x8b5c, 242 | SAMPLER_2D: 0x8b5e, 243 | SAMPLER_CUBE: 0x8b60, 244 | VERTEX_ATTRIB_ARRAY_ENABLED: 0x8622, 245 | VERTEX_ATTRIB_ARRAY_SIZE: 0x8623, 246 | VERTEX_ATTRIB_ARRAY_STRIDE: 0x8624, 247 | VERTEX_ATTRIB_ARRAY_TYPE: 0x8625, 248 | VERTEX_ATTRIB_ARRAY_NORMALIZED: 0x886a, 249 | VERTEX_ATTRIB_ARRAY_POINTER: 0x8645, 250 | VERTEX_ATTRIB_ARRAY_BUFFER_BINDING: 0x889f, 251 | IMPLEMENTATION_COLOR_READ_TYPE: 0x8b9a, 252 | IMPLEMENTATION_COLOR_READ_FORMAT: 0x8b9b, 253 | COMPILE_STATUS: 0x8b81, 254 | LOW_FLOAT: 0x8df0, 255 | MEDIUM_FLOAT: 0x8df1, 256 | HIGH_FLOAT: 0x8df2, 257 | LOW_INT: 0x8df3, 258 | MEDIUM_INT: 0x8df4, 259 | HIGH_INT: 0x8df5, 260 | FRAMEBUFFER: 0x8d40, 261 | RENDERBUFFER: 0x8d41, 262 | RGBA4: 0x8056, 263 | RGB5_A1: 0x8057, 264 | RGB565: 0x8d62, 265 | DEPTH_COMPONENT16: 0x81a5, 266 | STENCIL_INDEX8: 0x8d48, 267 | DEPTH_STENCIL: 0x84f9, 268 | RENDERBUFFER_WIDTH: 0x8d42, 269 | RENDERBUFFER_HEIGHT: 0x8d43, 270 | RENDERBUFFER_INTERNAL_FORMAT: 0x8d44, 271 | RENDERBUFFER_RED_SIZE: 0x8d50, 272 | RENDERBUFFER_GREEN_SIZE: 0x8d51, 273 | RENDERBUFFER_BLUE_SIZE: 0x8d52, 274 | RENDERBUFFER_ALPHA_SIZE: 0x8d53, 275 | RENDERBUFFER_DEPTH_SIZE: 0x8d54, 276 | RENDERBUFFER_STENCIL_SIZE: 0x8d55, 277 | FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE: 0x8cd0, 278 | FRAMEBUFFER_ATTACHMENT_OBJECT_NAME: 0x8cd1, 279 | FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL: 0x8cd2, 280 | FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE: 0x8cd3, 281 | COLOR_ATTACHMENT0: 0x8ce0, 282 | DEPTH_ATTACHMENT: 0x8d00, 283 | STENCIL_ATTACHMENT: 0x8d20, 284 | DEPTH_STENCIL_ATTACHMENT: 0x821a, 285 | NONE: 0, 286 | FRAMEBUFFER_COMPLETE: 0x8cd5, 287 | FRAMEBUFFER_INCOMPLETE_ATTACHMENT: 0x8cd6, 288 | FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: 0x8cd7, 289 | FRAMEBUFFER_INCOMPLETE_DIMENSIONS: 0x8cd9, 290 | FRAMEBUFFER_UNSUPPORTED: 0x8cdd, 291 | FRAMEBUFFER_BINDING: 0x8ca6, 292 | RENDERBUFFER_BINDING: 0x8ca7, 293 | MAX_RENDERBUFFER_SIZE: 0x84e8, 294 | INVALID_FRAMEBUFFER_OPERATION: 0x0506, 295 | UNPACK_FLIP_Y_WEBGL: 0x9240, 296 | UNPACK_PREMULTIPLY_ALPHA_WEBGL: 0x9241, 297 | CONTEXT_LOST_WEBGL: 0x9242, 298 | UNPACK_COLORSPACE_CONVERSION_WEBGL: 0x9243, 299 | BROWSER_DEFAULT_WEBGL: 0x9244, 300 | glBlendFuncSeparate: (a: number, b: number, c: number, d: number) => {}, 301 | glEnable: (cap: number) => {}, 302 | glDisable: (cap: number) => {}, 303 | glTexImage2D: (target: GLenum, level: GLint, internalformat: GLint, format: GLenum, type: GLenum, source: TexImageSource) => {}, 304 | glBindTexture: (target: GLenum, texture: WebGLTexture | null) => {}, 305 | glCreateTexture: (): WebGLTexture | null => { 306 | return null; 307 | }, 308 | glTexParameteri: (target: GLenum, pname: GLenum, param: GLint) => {}, 309 | glUniform4fv: (location: WebGLUniformLocation | null, v: Float32List) => {}, 310 | glUniform1i: (location: WebGLUniformLocation | null, v: number) => {}, 311 | glUniformMatrix4fv: (location: WebGLUniformLocation | null, transpose: GLboolean, value: Float32List) => {}, 312 | glDrawElements: (mode: GLenum, count: GLsizei, type: GLenum, offset: GLintptr) => {}, 313 | glUseProgram: (program: WebGLProgram) => {}, 314 | glDrawArrays: (mode: GLenum, first: GLint, count: GLsizei) => {}, 315 | glBindBuffer: (target: GLenum, buffer: WebGLBuffer | null) => {}, 316 | glBufferData: (target: GLenum, data: BufferSource | null, usage: GLenum) => {}, 317 | glBufferSubData: (target: GLenum, offset: GLintptr, data: BufferSource) => {}, 318 | glCreateBuffer: (): WebGLBuffer | null => { 319 | return null; 320 | }, 321 | glEnableVertexAttribArray: (index: GLuint) => {}, 322 | glDisableVertexAttribArray: (index: GLuint) => {}, 323 | glVertexAttribPointer: (index: GLuint, size: GLint, type: GLenum, normalized: GLboolean, stride: GLsizei, offset: GLintptr) => {} 324 | }; 325 | 326 | export function setGL(gl: WebGL2RenderingContext) { 327 | GL.glEnableVertexAttribArray = (index) => gl.enableVertexAttribArray(index); 328 | GL.glDisableVertexAttribArray = (index) => gl.disableVertexAttribArray(index); 329 | GL.glVertexAttribPointer = (index, size, type, normalized, stride, offset) => gl.vertexAttribPointer(index, size, type, normalized, stride, offset); 330 | GL.glCreateBuffer = () => gl.createBuffer(); 331 | GL.glUseProgram = (program) => gl.useProgram(program); 332 | GL.glDrawArrays = (mode, first, count) => gl.drawArrays(mode, first, count); 333 | GL.glBindBuffer = (target, buffer) => gl.bindBuffer(target, buffer); 334 | GL.glBufferData = (target, data, usage) => gl.bufferData(target, data, usage); 335 | GL.glBufferSubData = (target, offset, data) => gl.bufferSubData(target, offset, data); 336 | GL.glDrawElements = (mode, count, type, offset) => gl.drawElements(mode, count, type, offset); 337 | GL.glUniform4fv = (location: WebGLUniformLocation | null, v: Float32List) => gl.uniform4fv(location, v); 338 | GL.glUniform1i = (location: WebGLUniformLocation | null, v: number) => gl.uniform1i(location, v); 339 | GL.glUniformMatrix4fv = (location: WebGLUniformLocation | null, transpose: GLboolean, value: Float32List) => gl.uniformMatrix4fv(location, transpose, value); 340 | GL.glBindTexture = (target: GLenum, texture: WebGLTexture | null) => gl.bindTexture(target, texture); 341 | GL.glCreateTexture = (): WebGLTexture | null => { 342 | return gl.createTexture(); 343 | }; 344 | GL.glTexParameteri = (target: GLenum, pname: GLenum, param: GLint) => gl.texParameteri(target, pname, param); 345 | GL.glTexImage2D = (target, level, internalFormat, format, type, source) => gl.texImage2D(target, level, internalFormat, format, type, source); 346 | GL.glEnable = (cap) => gl.enable(cap); 347 | GL.glDisable = (cap) => gl.disable(cap); 348 | GL.glBlendFuncSeparate = (a: number, b: number, c: number, d: number) => gl.blendFuncSeparate(a, b, c, d); 349 | } 350 | 351 | export interface ShaderProgram { 352 | id: WebGLProgram | null; 353 | projectionUniform: WebGLUniformLocation | null; 354 | modelViewUniform: WebGLUniformLocation | null; 355 | colorUniform: WebGLUniformLocation | null; 356 | hasTexture: boolean; 357 | vertex: string; 358 | samplers: WebGLUniformLocation[]; 359 | fragment: string; 360 | } 361 | 362 | class Texture { 363 | id: WebGLTexture | null = null; 364 | width = 1; 365 | height = 1; 366 | } 367 | 368 | export class RenderSystem { 369 | private static _shader: ShaderProgram; 370 | private static _shadercolor = new Float32Array(4); 371 | private static _projMat = new Matrix4f().identity(); 372 | private static _mvMat = new Matrix4f().identity(); 373 | private static _texture: string; 374 | private static _idTexMap: Record = {}; 375 | private static _blendCap = false; 376 | private static _useProg: WebGLProgram | null; 377 | static getImageFromExplorer: (path: string) => HTMLImageElement | null = (path) => null; 378 | 379 | static useProgram(prog: WebGLProgram) { 380 | if (RenderSystem._useProg == prog) return; 381 | GL.glUseProgram(prog); 382 | RenderSystem._useProg = prog; 383 | } 384 | 385 | static frameReset() { 386 | RenderSystem._useProg = null; 387 | } 388 | 389 | static enableBlend() { 390 | if (RenderSystem._blendCap) return; 391 | GL.glEnable(GL.BLEND); 392 | RenderSystem._blendCap = true; 393 | } 394 | 395 | static defaultBlendFunc() { 396 | GL.glBlendFuncSeparate(GL.SRC_ALPHA, GL.ONE_MINUS_SRC_ALPHA, GL.ONE, GL.ZERO); 397 | } 398 | 399 | static disableBlend() { 400 | if (RenderSystem._blendCap) return; 401 | GL.glDisable(GL.BLEND); 402 | RenderSystem._blendCap = false; 403 | } 404 | 405 | static setProjectionMatrix(mat: Matrix4f) { 406 | this._projMat = mat; 407 | } 408 | 409 | static setModelViewMatrix(mat: Matrix4f) { 410 | this._mvMat = mat; 411 | } 412 | 413 | static getProjectionMatrix() { 414 | return this._projMat; 415 | } 416 | 417 | static getModelViewMatrix() { 418 | return this._mvMat; 419 | } 420 | 421 | static getTexture(texture: string) { 422 | if (RenderSystem._idTexMap[texture] == null) { 423 | const tex = new Texture(); 424 | tex.id = GL.glCreateTexture(); 425 | GL.glBindTexture(GL.TEXTURE_2D, tex.id); 426 | 427 | GL.glTexParameteri(GL.TEXTURE_2D, GL.TEXTURE_WRAP_S, GL.REPEAT); 428 | GL.glTexParameteri(GL.TEXTURE_2D, GL.TEXTURE_WRAP_T, GL.REPEAT); 429 | GL.glTexParameteri(GL.TEXTURE_2D, GL.TEXTURE_MIN_FILTER, GL.NEAREST); 430 | GL.glTexParameteri(GL.TEXTURE_2D, GL.TEXTURE_MAG_FILTER, GL.NEAREST); 431 | 432 | let img = RenderSystem.getImageFromExplorer(texture); 433 | 434 | console.log(img, texture); 435 | if (img == null) { 436 | img = new Image(); 437 | img.addEventListener('load', function () { 438 | tex.width = img!.width; 439 | tex.height = img!.height; 440 | 441 | GL.glBindTexture(GL.TEXTURE_2D, tex.id); 442 | GL.glTexImage2D(GL.TEXTURE_2D, 0, GL.RGBA, GL.RGBA, GL.UNSIGNED_BYTE, img!); 443 | }); 444 | img.src = './' + texture; 445 | } else { 446 | tex.width = img.width; 447 | tex.height = img.height; 448 | GL.glBindTexture(GL.TEXTURE_2D, tex.id); 449 | GL.glTexImage2D(GL.TEXTURE_2D, 0, GL.RGBA, GL.RGBA, GL.UNSIGNED_BYTE, img!); 450 | } 451 | 452 | RenderSystem._idTexMap[texture] = tex; 453 | } 454 | 455 | return RenderSystem._idTexMap[texture]; 456 | } 457 | 458 | static setShaderTexture(slot: number, texture: string) { 459 | RenderSystem._texture = texture; 460 | 461 | if (RenderSystem._idTexMap[texture] == null) { 462 | const tex = new Texture(); 463 | tex.id = GL.glCreateTexture(); 464 | GL.glBindTexture(GL.TEXTURE_2D, tex.id); 465 | 466 | GL.glTexParameteri(GL.TEXTURE_2D, GL.TEXTURE_WRAP_S, GL.REPEAT); 467 | GL.glTexParameteri(GL.TEXTURE_2D, GL.TEXTURE_WRAP_T, GL.REPEAT); 468 | GL.glTexParameteri(GL.TEXTURE_2D, GL.TEXTURE_MIN_FILTER, GL.NEAREST); 469 | GL.glTexParameteri(GL.TEXTURE_2D, GL.TEXTURE_MAG_FILTER, GL.NEAREST); 470 | 471 | let img = RenderSystem.getImageFromExplorer(texture); 472 | 473 | if (img == null) { 474 | img = new Image(); 475 | img.addEventListener('load', function () { 476 | tex.width = img!.width; 477 | tex.height = img!.height; 478 | 479 | GL.glBindTexture(GL.TEXTURE_2D, tex.id); 480 | GL.glTexImage2D(GL.TEXTURE_2D, 0, GL.RGBA, GL.RGBA, GL.UNSIGNED_BYTE, img!); 481 | }); 482 | img.src = './' + texture; 483 | } else { 484 | tex.width = img.width; 485 | tex.height = img.height; 486 | GL.glBindTexture(GL.TEXTURE_2D, tex.id); 487 | GL.glTexImage2D(GL.TEXTURE_2D, 0, GL.RGBA, GL.RGBA, GL.UNSIGNED_BYTE, img!); 488 | } 489 | 490 | RenderSystem._idTexMap[texture] = tex; 491 | } 492 | } 493 | 494 | static setShader(shader: ShaderProgram) { 495 | RenderSystem._shader = shader; 496 | } 497 | 498 | static setShaderColor(r: number, g: number, b: number, a: number) { 499 | this._shadercolor[0] = r; 500 | this._shadercolor[1] = g; 501 | this._shadercolor[2] = b; 502 | this._shadercolor[3] = a; 503 | } 504 | 505 | static getShader() { 506 | return RenderSystem._shader; 507 | } 508 | 509 | static getShaderColor() { 510 | return RenderSystem._shadercolor; 511 | } 512 | 513 | static getShaderTexture() { 514 | const tex = RenderSystem._idTexMap[RenderSystem._texture]; 515 | return tex; 516 | } 517 | } 518 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import * as monaco from 'monaco-editor'; 2 | import stripJsonComments from 'strip-json-comments'; 3 | import { DrawContext } from './DrawContext'; 4 | import { TextRenderer } from './TextRenderer'; 5 | import { Binding, UIControl, UIEvent, UIScreenControl, createControl } from './controls'; 6 | import { RenderSystem, setGL } from './gl'; 7 | import { Matrix4f } from './math'; 8 | import { preparePrograms } from './program'; 9 | import { globalVarsSchema, screenDefsSchema, uiDefsSchema, uiSchema } from './schemas'; 10 | import { ExplorerFile, ExplorerFileImage, ExplorerFileJson, ExplorerFileText } from './types'; 11 | import { namespacedSources } from './uis'; 12 | import { setDebug } from './vars'; 13 | 14 | let SHOW_ALERT = true; 15 | if (localStorage.getItem('firstTime')) { 16 | SHOW_ALERT = false; 17 | } 18 | 19 | if (SHOW_ALERT) { 20 | alert('If the screen goes black press ctrl+s. Press L to show debug outlines and press K to show the ui files source code'); 21 | localStorage.setItem('firstTime', 'true'); 22 | } 23 | 24 | const editor = monaco.editor.create(document.getElementById('editor')!, { 25 | value: '{}', 26 | language: 'json', 27 | tabSize: 2, 28 | folding: true, 29 | minimap: { 30 | enabled: false 31 | }, 32 | theme: 'vs-dark', 33 | automaticLayout: true 34 | }); 35 | 36 | let uiDefsUri = new monaco.Uri(); 37 | uiDefsUri = uiDefsUri.with({ path: '_ui_defs.json' }); 38 | 39 | let screenDefsUri = new monaco.Uri(); 40 | screenDefsUri = screenDefsUri.with({ path: '_screen_definitions.json' }); 41 | 42 | let globalVarsUri = new monaco.Uri(); 43 | globalVarsUri = globalVarsUri.with({ path: '_global_variables.json' }); 44 | 45 | let langUri = new monaco.Uri(); 46 | langUri = langUri.with({ path: 'en_us.json' }); 47 | 48 | monaco.languages.json.jsonDefaults.setDiagnosticsOptions({ 49 | validate: true, 50 | allowComments: true, 51 | schemaValidation: 'warning', 52 | schemas: [ 53 | { 54 | uri: 'ui', 55 | fileMatch: ['*', '!_ui_defs.json', '!en_us.json', '!_screen_definitions.json', '!_global_variables.json'], 56 | schema: uiSchema 57 | }, 58 | { 59 | uri: globalVarsUri.toString(), 60 | fileMatch: ['_global_variables.json'], 61 | schema: JSON.parse(stripJsonComments(globalVarsSchema)) 62 | }, 63 | { 64 | uri: globalVarsUri.toString(), 65 | fileMatch: ['en_us.json'], 66 | schema: JSON.parse(stripJsonComments(globalVarsSchema)) 67 | }, 68 | { 69 | uri: uiDefsUri.toString(), 70 | fileMatch: ['_ui_defs.json'], 71 | schema: JSON.parse(stripJsonComments(uiDefsSchema)) 72 | }, 73 | { 74 | uri: screenDefsUri.toString(), 75 | fileMatch: ['_screen_defs.json'], 76 | schema: JSON.parse(stripJsonComments(screenDefsSchema)) 77 | } 78 | ] 79 | }); 80 | 81 | monaco.languages.json.jsonDefaults.setModeConfiguration({ 82 | documentSymbols: true, 83 | colors: true, 84 | tokens: true, 85 | completionItems: true, 86 | diagnostics: true, 87 | hovers: true, 88 | documentFormattingEdits: true, 89 | documentRangeFormattingEdits: true, 90 | selectionRanges: true 91 | }); 92 | 93 | const explorer = document.getElementById('explorer-list') as HTMLDivElement; 94 | const consoleEl = document.getElementById('console') as HTMLDivElement; 95 | explorer.innerHTML = ''; 96 | const filecontextMenu = document.getElementById('file-contextmenu')!; 97 | 98 | function writeToConsole(text: string, type: 'info' | 'error' = 'info') { 99 | const p = document.createElement('p'); 100 | if (type == 'info') { 101 | p.textContent = typeof text === 'object' ? JSON.stringify(text) : text; 102 | } else { 103 | const span = document.createElement('span'); 104 | span.style.color = 'red'; 105 | span.textContent = typeof text === 'object' ? JSON.stringify(text) : text; 106 | p.appendChild(span); 107 | } 108 | consoleEl.appendChild(p); 109 | consoleEl.scrollTop = consoleEl.scrollHeight; 110 | } 111 | 112 | (window as any)['writeToConsole'] = writeToConsole; 113 | 114 | let files: ExplorerFile[] = [ 115 | { 116 | type: 'text', 117 | name: 'features.txt', 118 | contextMenu: false, 119 | model: monaco.editor.createModel(await (await fetch('features.txt')).text(), 'text') 120 | }, 121 | { 122 | type: 'json', 123 | name: '_screen_defs.json', 124 | contextMenu: false, 125 | model: monaco.editor.createModel(await (await fetch('./ui/screen_defs.json')).text(), 'json', screenDefsUri) 126 | }, 127 | { 128 | type: 'json', 129 | name: '_ui_defs.json', 130 | contextMenu: false, 131 | model: monaco.editor.createModel(await (await fetch('./ui/ui_defs.json')).text(), 'json', uiDefsUri) 132 | }, 133 | { 134 | type: 'json', 135 | name: '_global_variables.json', 136 | contextMenu: false, 137 | model: monaco.editor.createModel(await (await fetch('./ui/global_variables.json')).text(), 'json', globalVarsUri) 138 | }, 139 | { 140 | type: 'json', 141 | name: 'start_screen.json', 142 | contextMenu: false, 143 | model: monaco.editor.createModel(namespacedSources['start'], 'json') 144 | }, 145 | { 146 | type: 'json', 147 | name: 'play_screen.json', 148 | contextMenu: false, 149 | model: monaco.editor.createModel(namespacedSources['play'], 'json') 150 | }, 151 | { 152 | type: 'json', 153 | name: 'ui_common.json', 154 | contextMenu: false, 155 | model: monaco.editor.createModel(namespacedSources['common'], 'json') 156 | }, 157 | { 158 | type: 'json', 159 | name: 'start_screen.json', 160 | contextMenu: false, 161 | model: monaco.editor.createModel(namespacedSources['start'], 'json') 162 | }, 163 | { 164 | type: 'json', 165 | name: 'en_us.json', 166 | contextMenu: false, 167 | model: monaco.editor.createModel(await (await fetch('lang/en_us.json')).text(), 'json', langUri) 168 | }, 169 | { 170 | type: 'image', 171 | name: 'grass.png', 172 | contextMenu: false, 173 | element: await new Promise((res) => { 174 | const m = new Image(); 175 | m.src = './grass.png'; 176 | m.onload = () => { 177 | res(m); 178 | }; 179 | }) 180 | } 181 | ]; 182 | 183 | RenderSystem.getImageFromExplorer = (path) => { 184 | for (const file of files) { 185 | if (file.type == 'image' && file.name == path) { 186 | return file.element; 187 | } 188 | } 189 | 190 | return null; 191 | }; 192 | 193 | editor.setModel((files.find((f) => f.name === 'start_screen.json') as any).model); 194 | editor.focus(); 195 | 196 | function refreshExplorer() { 197 | explorer.innerHTML = ''; 198 | files.forEach((file) => { 199 | createFile(file); 200 | }); 201 | } 202 | 203 | let selected = 'start_screen.json'; 204 | 205 | document.getElementById('contextMenu-delete')?.addEventListener('click', () => { 206 | if (selected !== '' && selected !== '_screen_definitions.json' && selected !== '_global_variable.json' && selected !== '_ui_defs.json' && selected !== 'start_screen.json') { 207 | const file = files.find((f) => f.name === selected); 208 | 209 | if (file) { 210 | files = files.filter((f) => f.name !== selected); 211 | for (let i = files.length - 1; i >= 0; i--) { 212 | if (files[i].type === 'text') { 213 | editor.setModel((files[i] as any).model); 214 | break; 215 | } else { 216 | continue; 217 | } 218 | } 219 | refreshExplorer(); 220 | } 221 | } 222 | }); 223 | 224 | const newFilePopup = document.getElementById('new-file-popup') as HTMLDivElement; 225 | const newFileInput = document.getElementById('new-file-name') as HTMLInputElement; 226 | 227 | document.getElementById('new-file')!.addEventListener('click', () => { 228 | newFilePopup.style.display = 'block'; 229 | newFileInput.value = ''; 230 | newFileInput.focus(); 231 | }); 232 | 233 | document.getElementById('refresh-explorer')!.addEventListener('click', () => { 234 | refreshExplorer(); 235 | }); 236 | 237 | document.getElementById('create-new-file')!.addEventListener('click', () => { 238 | if (newFileInput.value.length > 0 && files.every((f) => newFileInput.value !== f.name)) { 239 | const model = monaco.editor.createModel('', 'json'); 240 | 241 | const obj: any = { 242 | type: 'json', 243 | name: newFileInput.value, 244 | data: '', 245 | contextMenu: true, 246 | model 247 | }; 248 | files.push(obj); 249 | 250 | createFile(obj); 251 | newFilePopup.style.display = 'none'; 252 | newFileInput.value = ''; 253 | 254 | editor.setModel(model); 255 | } 256 | }); 257 | 258 | document.getElementById('cancel-new-file')!.addEventListener('click', () => { 259 | newFilePopup.style.display = 'none'; 260 | newFileInput.value = ''; 261 | }); 262 | 263 | function createFile(file: ExplorerFile) { 264 | const btn = document.createElement('button'); 265 | btn.className = 'file-item'; 266 | btn.id = file.name; 267 | btn.title = file.name; 268 | 269 | btn.addEventListener('click', () => { 270 | if (file.type === 'text' || file.type === 'json') { 271 | document.getElementById('editor')!.style.display = 'block'; 272 | document.getElementById('image-previewer')!.style.display = 'none'; 273 | document.getElementById('image-previewer')!.innerHTML = ''; 274 | editor.setModel(file.model); 275 | } else if (file.type === 'image') { 276 | document.getElementById('editor')!.style.display = 'none'; 277 | document.getElementById('image-previewer')!.style.display = 'flex'; 278 | document.getElementById('image-previewer')!.innerHTML = ''; 279 | 280 | document.getElementById('image-previewer')!.appendChild(file.element); 281 | } 282 | }); 283 | 284 | btn.addEventListener('contextmenu', (e) => { 285 | const rect = btn.getBoundingClientRect(); 286 | filecontextMenu.style.top = rect.top + 'px'; 287 | filecontextMenu.style.left = e.clientX + 'px'; 288 | filecontextMenu.style.display = 'flex'; 289 | selected = file.name; 290 | 291 | filecontextMenu.classList.toggle('locked', !file.contextMenu); 292 | }); 293 | 294 | const cont = document.createElement('div'); 295 | cont.className = 'file-item-content'; 296 | const ficon = document.createElement('div'); 297 | ficon.className = 'file-icon'; 298 | ficon.innerHTML = ''; 299 | const flabel = document.createElement('div'); 300 | flabel.className = 'file-label'; 301 | flabel.textContent = file.name; 302 | 303 | cont.appendChild(ficon); 304 | cont.appendChild(flabel); 305 | btn.appendChild(cont); 306 | 307 | explorer.appendChild(btn); 308 | } 309 | 310 | refreshExplorer(); 311 | 312 | explorer.addEventListener('contextmenu', (e) => { 313 | e.preventDefault(); 314 | }); 315 | 316 | window.addEventListener('keydown', (e) => { 317 | if (e.ctrlKey && e.code === 'KeyS') { 318 | e.preventDefault(); 319 | reloadUIStuff(); 320 | } 321 | }); 322 | 323 | const canvas = document.getElementById('screen') as HTMLCanvasElement; 324 | const gl = canvas.getContext('webgl2') as WebGL2RenderingContext; 325 | setGL(gl); 326 | preparePrograms(gl); 327 | gl.viewport(0, 0, canvas.clientWidth, canvas.clientHeight); 328 | gl.clearColor(0.0, 0.0, 0.0, 1.0); 329 | gl.canvas.width = canvas.clientWidth; 330 | gl.canvas.height = canvas.clientHeight; 331 | 332 | let lastTime = performance.now(); 333 | let frames = 0; 334 | const fpsEl = document.getElementById('fps')!; 335 | 336 | const projMat = new Matrix4f().identity().setOrtho(0, canvas.clientWidth / 2, canvas.clientHeight / 2, 0, 1000, 3000); 337 | const modelViewMat = new Matrix4f().identity().translate(0, 0, -2000); 338 | 339 | let DEBUG_UIFILES = false; 340 | 341 | const keys: Record = {}; 342 | let mouseX = -1; 343 | let mouseY = -1; 344 | document.addEventListener('keydown', (e) => { 345 | keys[e.code] = true; 346 | 347 | writeToConsole('keydown[code=' + e.code + ']'); 348 | 349 | if (e.code == 'Escape') { 350 | ScreenStack_event({ 351 | type: 'button_id', 352 | id: 'button.menu_cancel', 353 | x: mouseX, 354 | y: mouseY, 355 | pressed: true, 356 | double_pressed: false, 357 | emit(buttonId, control) { 358 | if (buttonId == 'button.menu_exit') { 359 | ScreenStack_pop(); 360 | } 361 | } 362 | }); 363 | } 364 | }); 365 | document.addEventListener('keyup', (e) => { 366 | keys[e.code] = false; 367 | 368 | (window as any).writeToConsole('keyup[code=' + e.code + ']'); 369 | 370 | if (e.code == 'KeyK' && keys['ShiftLeft']) { 371 | e.preventDefault(); 372 | DEBUG_UIFILES = !DEBUG_UIFILES; 373 | document.getElementById('ui-files')!.style.display = DEBUG_UIFILES ? 'block' : 'none'; 374 | } // } else if (e.code == 'KeyP' && keys['ShiftLeft']) { 375 | // e.preventDefault(); 376 | // ScreenStack_push(createScreen('play').screen); 377 | // } else if (e.code == 'KeyO' && keys['ShiftLeft']) { 378 | // e.preventDefault(); 379 | // ScreenStack_pop(); 380 | // } 381 | }); 382 | 383 | canvas.addEventListener('mousedown', (e) => { 384 | writeToConsole('mousedown[x=' + e.offsetX / 2 + ',y=' + e.offsetY / 2 + ']'); 385 | 386 | if (e.button == 0) { 387 | ScreenStack_event({ 388 | type: 'button_id', 389 | id: 'button.menu_select', 390 | x: mouseX, 391 | y: mouseY, 392 | pressed: true, 393 | double_pressed: e.detail == 2, 394 | emit(buttonId, control) { 395 | if (buttonId == 'button.menu_exit') { 396 | ScreenStack_pop(); 397 | } 398 | 399 | if (buttonId == 'button.menu_play') { 400 | ScreenStack_push(createScreen('play').screen); 401 | } 402 | } 403 | }); 404 | } 405 | }); 406 | 407 | canvas.addEventListener('mousemove', (e) => { 408 | mouseX = e.offsetX / 2; 409 | mouseY = e.offsetY / 2; 410 | }); 411 | 412 | window.addEventListener('resize', () => { 413 | gl.viewport(0, 0, canvas.clientWidth, canvas.clientHeight); 414 | projMat.identity().setOrtho(0, canvas.clientWidth / 2, canvas.clientHeight / 2 + 1, 0, 1000, 3000); 415 | }); 416 | 417 | let cachedScreens: Record = {}; 418 | const screenStack: UIScreen[] = []; 419 | function ScreenStack_push(screen: UIScreen) { 420 | screenStack.push(screen); 421 | } 422 | function ScreenStack_pop() { 423 | const popped = screenStack.pop(); 424 | if (popped) { 425 | cachedScreens[popped.name] = popped; 426 | } 427 | } 428 | function ScreenStack_clear() { 429 | screenStack.length = 0; 430 | } 431 | function ScreenStack_event(event: UIEvent) { 432 | if (screenStack.length == 0) return; 433 | screenStack[screenStack.length - 1].screenEl?.onEvent(event); 434 | } 435 | function ScreenStack_render(context: DrawContext) { 436 | if (screenStack.length == 0) return; 437 | let renderThatShit = screenStack[screenStack.length - 1].forceRenderBelow(); 438 | 439 | for (let i = 0; i < screenStack.length; ++i) { 440 | const screen = screenStack[i]; 441 | 442 | if (i == screenStack.length - 1) { 443 | screen.render(context, mouseX, mouseY); 444 | } else if (renderThatShit || !screen.renderOnlyWhenTopMost()) { 445 | screen.render(context, mouseX, mouseY); 446 | } 447 | } 448 | } 449 | 450 | class UIScreen { 451 | name = Math.random() * 1000 + ''; 452 | screenEl?: UIScreenControl; 453 | tree: Record = {}; 454 | actions: Record void> = {}; 455 | bindings: Record = {}; 456 | 457 | public registerActions() {} 458 | 459 | public registerBindings() {} 460 | 461 | public render(context: DrawContext, mouseX: number, mouseY: number) { 462 | if (this.screenEl) { 463 | this.screenEl.render(context, mouseX, mouseY); 464 | } 465 | } 466 | 467 | getBinding(name: string) { 468 | if (this.bindings[name] == null) { 469 | this.bindings[name] = new Binding().setValue(0); 470 | } 471 | return this.bindings[name]; 472 | } 473 | 474 | setBinding(name: string, value: any) { 475 | if (this.bindings[name] == null) { 476 | this.bindings[name] = new Binding().setValue(value); 477 | } else { 478 | this.bindings[name].setValue(value); 479 | } 480 | } 481 | 482 | public forceRenderBelow() { 483 | return this.screenEl != null ? this.screenEl.forceRenderBelow : false; 484 | } 485 | 486 | public renderOnlyWhenTopMost() { 487 | return this.screenEl != null ? this.screenEl.renderOnlyWhenTopMost : true; 488 | } 489 | } 490 | 491 | class StartScreen extends UIScreen { 492 | constructor() { 493 | super(); 494 | this.name = 'start'; 495 | } 496 | 497 | public registerActions(): void { 498 | this.actions['button.menu_play'] = (control) => { 499 | writeToConsole('setScreen play'); 500 | }; 501 | } 502 | 503 | public registerBindings(): void { 504 | this.registerBinding('#fill0_size', 80); 505 | this.registerBinding('#grad1_width', 80); 506 | this.registerBinding('#grad0_color0', [1, 0, 1, 1]); 507 | this.registerBinding('#grad0_color1', [0, 0, 1, 1]); 508 | this.registerBinding('#test_x_off', 0); 509 | this.registerBinding('#test_y_off', 0); 510 | } 511 | 512 | private registerBinding(name: string, value: any) { 513 | if (this.bindings[name] == null) { 514 | this.bindings[name] = new Binding(); 515 | } 516 | this.bindings[name].setValue(value); 517 | } 518 | 519 | public render(context: DrawContext, mouseX: number, mouseY: number): void { 520 | if ((performance.now() / 1000) % 2 == 0) { 521 | this.setBinding('#grad0_color0', [Math.random(), Math.random(), Math.random(), 1]); 522 | this.setBinding('#grad0_color1', [Math.random(), Math.random(), Math.random(), 1]); 523 | } 524 | 525 | this.setBinding('#grad1_width', Math.sin(performance.now() / 1000) * 80 + 160); 526 | this.setBinding('#test_x_off', Math.sin(performance.now() / 1000) * 120 + 120); 527 | 528 | this.setBinding('#fill0_size', Math.sin(performance.now() / 1000) * 80 + 80 + 20); 529 | 530 | super.render(context, mouseX, mouseY); 531 | } 532 | } 533 | 534 | class PlayScreen extends UIScreen { 535 | constructor() { 536 | super(); 537 | this.name = 'play'; 538 | } 539 | 540 | public registerActions(): void { 541 | this.actions['button.menu_exit'] = (control) => { 542 | ScreenStack_pop(); 543 | }; 544 | } 545 | } 546 | 547 | function getJsonFileSource(filename: string) { 548 | for (const file of files) { 549 | if (file.name == filename && file.type == 'json') { 550 | return stripJsonComments(file.model.getValue()); 551 | } 552 | } 553 | 554 | return namespacedSources[filename.replace('_screen.json', '')]; 555 | } 556 | 557 | let parsedTrees: Record> = {}; 558 | 559 | function parseTrees() { 560 | parsedTrees = {}; 561 | 562 | const uiDef = JSON.parse(stripJsonComments(getJsonFileSource('_ui_defs.json'))); 563 | 564 | for (const uidef of uiDef['ui_defs']) { 565 | const data = JSON.parse(stripJsonComments(getJsonFileSource(uidef))); 566 | 567 | const parsedTree: Record = {}; 568 | 569 | let nm = typeof data['namespace'] === 'string' ? data['namespace'] : Math.random() * 1000 + ''; 570 | for (let [key, value] of Object.entries(data)) { 571 | if (key === 'namespace') { 572 | continue; 573 | } 574 | 575 | const newControls: any[] = []; 576 | if (value['controls']) { 577 | for (let child of value['controls']) { 578 | for (let [cname, cprops] of Object.entries(child)) { 579 | const p: any = {}; 580 | 581 | p[cname.includes('@') ? cname.split('@')[0] : cname] = cprops; 582 | 583 | if (cname.includes('@')) { 584 | let supn = cname.split('@')[1]; 585 | 586 | if (data[supn] != null) { 587 | p[cname.split('@')[0]] = { ...data[cname.split('@')[1]], ...cprops }; 588 | } 589 | } 590 | 591 | newControls.push(p); 592 | break; 593 | } 594 | } 595 | } 596 | value['controls'] = newControls; 597 | parsedTree[nm + '.' + key] = value; 598 | } 599 | 600 | parsedTrees[nm] = parsedTree; 601 | } 602 | } 603 | 604 | function createScreen(name: string) { 605 | if (cachedScreens[name] != null) return { screen: cachedScreens[name] }; 606 | 607 | const tree: Record = {}; 608 | const actions: Record void> = {}; 609 | const bindings: Record = {}; 610 | 611 | let screen: UIScreen; 612 | 613 | if (name == 'start') { 614 | screen = new StartScreen(); 615 | } else if (name == 'play') { 616 | screen = new PlayScreen(); 617 | } else { 618 | throw new Error('bruh'); 619 | } 620 | 621 | screen.tree = tree; 622 | screen.actions = actions; 623 | screen.bindings = bindings; 624 | 625 | try { 626 | const screenDefs = JSON.parse(stripJsonComments(getJsonFileSource('_screen_defs.json')))['screen_definitions']['screens'][name]['context']; 627 | 628 | Object.entries(screenDefs['bindings']).forEach(([k, v]) => { 629 | screen.bindings[k] = new Binding().setValue(v); 630 | }); 631 | } catch (e) { 632 | writeToConsole(e as any); 633 | } 634 | 635 | screen.registerActions(); 636 | screen.registerBindings(); 637 | 638 | const data = JSON.parse(stripJsonComments(getJsonFileSource(name + '_screen.json'))); 639 | let nm = typeof data['namespace'] === 'string' ? data['namespace'] : name; 640 | const parsedTree: Record = parsedTrees[nm]; 641 | 642 | const control = createControl(name + '_screen', null, parsedTrees, parsedTree[nm + '.' + name + '_screen'], bindings, tree); 643 | 644 | if (control) { 645 | tree[control.path] = control; 646 | } 647 | 648 | screen.screenEl = tree['/' + name + '_screen'] as UIScreenControl; 649 | screen.screenEl.x = 0; 650 | screen.screenEl.y = 0; 651 | screen.screenEl.w = canvas.clientWidth; 652 | screen.screenEl.h = canvas.clientHeight; 653 | 654 | for (const bind of Object.values(bindings)) { 655 | bind.notifyAll(); 656 | } 657 | 658 | writeToConsole('created ' + name + ' screen'); 659 | 660 | return { 661 | screen 662 | }; 663 | } 664 | 665 | const fontData = await (await fetch('./font.json')).json(); 666 | 667 | const textRenderer = new TextRenderer(fontData); 668 | 669 | function reloadUIStuff() { 670 | ScreenStack_clear(); 671 | cachedScreens = {}; 672 | parseTrees(); 673 | 674 | const firstSCreen = createScreen('start'); 675 | ScreenStack_push(firstSCreen.screen); 676 | writeToConsole('reloading ui'); 677 | } 678 | 679 | reloadUIStuff(); 680 | 681 | const ext = gl.getExtension('WEBGL_lose_context'); 682 | 683 | function loop() { 684 | requestAnimationFrame(loop); 685 | gl.clear(gl.COLOR_BUFFER_BIT); 686 | 687 | setDebug(keys['KeyL']); 688 | 689 | if (gl.isContextLost() && ext) { 690 | setTimeout(function () { 691 | writeToConsole('trying to restore context'); 692 | ext.restoreContext(); 693 | }, 1000); 694 | } 695 | 696 | RenderSystem.setShaderColor(1, 1, 1, 1); 697 | RenderSystem.setProjectionMatrix(projMat); 698 | RenderSystem.setModelViewMatrix(modelViewMat); 699 | 700 | const context = new DrawContext(textRenderer); 701 | 702 | ScreenStack_render(context); 703 | RenderSystem.frameReset(); 704 | 705 | ++frames; 706 | 707 | while (performance.now() - lastTime > 1000) { 708 | fpsEl.textContent = frames + ' FPS'; 709 | lastTime += 1000; 710 | frames = 0; 711 | } 712 | } 713 | requestAnimationFrame(loop); 714 | 715 | ['drag', 'dragover', 'dragend', 'dragenter', 'dragleave', 'drop'].forEach((name) => 716 | explorer.addEventListener(name, (e) => { 717 | e.preventDefault(); 718 | }) 719 | ); 720 | 721 | explorer.addEventListener('dragenter', (e) => { 722 | explorer.classList.add('drag-over'); 723 | }); 724 | 725 | explorer.addEventListener('dragleave', (e) => { 726 | explorer.classList.remove('drag-over'); 727 | }); 728 | 729 | explorer.addEventListener('drop', (e) => { 730 | explorer.classList.remove('drag-over'); 731 | 732 | if (e.dataTransfer) { 733 | for (let i = 0; i < e.dataTransfer.files.length; i++) { 734 | const j = e.dataTransfer.files[i]; 735 | const name = j.name; 736 | const type = j.type; 737 | 738 | if (type === 'image/png' || type === 'image/jpg' || type === 'image/jpeg') { 739 | j.arrayBuffer().then((d) => { 740 | const b = new Blob([d]); 741 | const img = new Image(); 742 | img.src = URL.createObjectURL(b); 743 | img.onload = () => { 744 | const obj: ExplorerFileImage = { 745 | type: 'image', 746 | name: name, 747 | contextMenu: true, 748 | element: img 749 | }; 750 | files.push(obj); 751 | createFile(obj); 752 | }; 753 | }); 754 | } else if (type === 'application/json' || type.startsWith('text')) { 755 | j.text().then((t) => { 756 | const model = monaco.editor.createModel(t, type === 'application/json' ? 'json' : 'plaintext'); 757 | 758 | const obj: ExplorerFileJson | ExplorerFileText = { 759 | type: type === 'application/json' ? 'json' : 'text', 760 | name, 761 | contextMenu: true, 762 | model 763 | }; 764 | files.push(obj); 765 | 766 | createFile(obj); 767 | editor.setModel(model); 768 | }); 769 | } 770 | } 771 | } 772 | }); 773 | -------------------------------------------------------------------------------- /src/math.ts: -------------------------------------------------------------------------------- 1 | export class Matrix4f { 2 | m00: number = 0; 3 | m01: number = 0; 4 | m02: number = 0; 5 | m03: number = 0; 6 | m10: number = 0; 7 | m11: number = 0; 8 | m12: number = 0; 9 | m13: number = 0; 10 | m20: number = 0; 11 | m21: number = 0; 12 | m22: number = 0; 13 | m23: number = 0; 14 | m30: number = 0; 15 | m31: number = 0; 16 | m32: number = 0; 17 | m33: number = 0; 18 | 19 | public identity(): Matrix4f { 20 | this.m00 = 1; 21 | this.m01 = 0; 22 | this.m02 = 0; 23 | this.m03 = 0; 24 | this.m10 = 0; 25 | this.m11 = 1; 26 | this.m12 = 0; 27 | this.m13 = 0; 28 | this.m20 = 0; 29 | this.m21 = 0; 30 | this.m22 = 1; 31 | this.m23 = 0; 32 | this.m30 = 0; 33 | this.m31 = 0; 34 | this.m32 = 0; 35 | this.m33 = 1; 36 | return this; 37 | } 38 | 39 | public setOrtho(left: number, right: number, bottom: number, top: number, zNear: number, zFar: number) { 40 | const rm00 = 2.0 / (right - left); 41 | const rm11 = 2.0 / (top - bottom); 42 | const rm22 = 2.0 / (zNear - zFar); 43 | const rm30 = (left + right) / (left - right); 44 | const rm31 = (top + bottom) / (bottom - top); 45 | const rm32 = (zFar + zNear) / (zNear - zFar); 46 | 47 | this.m30 = this.m00 * rm30 + this.m10 * rm31 + this.m20 * rm32 + this.m30; 48 | this.m31 = this.m01 * rm30 + this.m11 * rm31 + this.m21 * rm32 + this.m31; 49 | this.m32 = this.m02 * rm30 + this.m12 * rm31 + this.m22 * rm32 + this.m32; 50 | this.m33 = this.m03 * rm30 + this.m13 * rm31 + this.m23 * rm32 + this.m33; 51 | this.m00 *= rm00; 52 | this.m01 *= rm00; 53 | this.m02 *= rm00; 54 | this.m03 *= rm00; 55 | this.m10 *= rm11; 56 | this.m11 *= rm11; 57 | this.m12 *= rm11; 58 | this.m13 *= rm11; 59 | this.m20 *= rm22; 60 | this.m21 *= rm22; 61 | this.m22 *= rm22; 62 | this.m23 *= rm22; 63 | return this; 64 | } 65 | 66 | public translate(x: number, y: number, z: number) { 67 | this.m30 = this.m00 * x + this.m10 * y + this.m20 * z + this.m30; 68 | this.m31 = this.m01 * x + this.m11 * y + this.m21 * z + this.m31; 69 | this.m32 = this.m02 * x + this.m12 * y + this.m22 * z + this.m32; 70 | this.m33 = this.m03 * x + this.m13 * y + this.m23 * z + this.m33; 71 | return this; 72 | } 73 | 74 | public scale(x: number, y: number, z: number) { 75 | this.m00 *= x; 76 | this.m01 *= x; 77 | this.m02 *= x; 78 | this.m03 *= x; 79 | this.m10 *= y; 80 | this.m11 *= y; 81 | this.m12 *= y; 82 | this.m13 *= y; 83 | this.m20 *= z; 84 | this.m21 *= z; 85 | this.m22 *= z; 86 | this.m23 *= z; 87 | return this; 88 | } 89 | 90 | public toArray() { 91 | return [this.m00, this.m01, this.m02, this.m03, this.m10, this.m11, this.m12, this.m13, this.m20, this.m21, this.m22, this.m23, this.m30, this.m31, this.m32, this.m33]; 92 | } 93 | 94 | public get(buffer: Float32Array) { 95 | buffer[0] = this.m00; 96 | buffer[1] = this.m01; 97 | buffer[2] = this.m02; 98 | buffer[3] = this.m03; 99 | buffer[4] = this.m10; 100 | buffer[5] = this.m11; 101 | buffer[6] = this.m12; 102 | buffer[7] = this.m13; 103 | buffer[8] = this.m20; 104 | buffer[9] = this.m21; 105 | buffer[10] = this.m22; 106 | buffer[11] = this.m23; 107 | buffer[12] = this.m30; 108 | buffer[13] = this.m31; 109 | buffer[14] = this.m32; 110 | buffer[15] = this.m33; 111 | return buffer; 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/program.ts: -------------------------------------------------------------------------------- 1 | import { ShaderProgram } from './gl'; 2 | 3 | export const PROGRAMS: Record<'GRAYSCALE_POSITION_TEXTURE_COLOR' | 'POSITION_TEXTURE_COLOR' | 'POSITION' | 'POSITION_COLOR' | 'POSITION_TEXTURE' | 'GRAYSCALE_POSITION_TEXTURE', ShaderProgram> = { 4 | POSITION: { 5 | id: null, 6 | projectionUniform: null, 7 | modelViewUniform: null, 8 | colorUniform: null, 9 | hasTexture: false, 10 | samplers: [], 11 | vertex: await (await fetch('shaders/position.vsh')).text(), 12 | fragment: await (await fetch('shaders/position.fsh')).text() 13 | }, 14 | POSITION_COLOR: { 15 | id: null, 16 | projectionUniform: null, 17 | modelViewUniform: null, 18 | colorUniform: null, 19 | hasTexture: false, 20 | samplers: [], 21 | vertex: await (await fetch('shaders/position_color.vsh')).text(), 22 | fragment: await (await fetch('shaders/position_color.fsh')).text() 23 | }, 24 | POSITION_TEXTURE: { 25 | id: null, 26 | projectionUniform: null, 27 | modelViewUniform: null, 28 | colorUniform: null, 29 | hasTexture: true, 30 | samplers: [], 31 | vertex: await (await fetch('shaders/position_texture.vsh')).text(), 32 | fragment: await (await fetch('shaders/position_texture.fsh')).text() 33 | }, 34 | POSITION_TEXTURE_COLOR: { 35 | id: null, 36 | projectionUniform: null, 37 | modelViewUniform: null, 38 | colorUniform: null, 39 | hasTexture: true, 40 | samplers: [], 41 | vertex: await (await fetch('shaders/position_texture_color.vsh')).text(), 42 | fragment: await (await fetch('shaders/position_texture_color.fsh')).text() 43 | }, 44 | GRAYSCALE_POSITION_TEXTURE_COLOR: { 45 | id: null, 46 | projectionUniform: null, 47 | modelViewUniform: null, 48 | colorUniform: null, 49 | hasTexture: true, 50 | samplers: [], 51 | vertex: await (await fetch('shaders/grayscale_position_texture_color.vsh')).text(), 52 | fragment: await (await fetch('shaders/grayscale_position_texture_color.fsh')).text() 53 | }, 54 | GRAYSCALE_POSITION_TEXTURE: { 55 | id: null, 56 | projectionUniform: null, 57 | modelViewUniform: null, 58 | colorUniform: null, 59 | hasTexture: true, 60 | samplers: [], 61 | vertex: await (await fetch('shaders/grayscale_position_texture.vsh')).text(), 62 | fragment: await (await fetch('shaders/grayscale_position_texture.fsh')).text() 63 | } 64 | }; 65 | 66 | export function preparePrograms(gl: WebGL2RenderingContext) { 67 | for (const program of Object.values(PROGRAMS)) { 68 | program.id = gl.createProgram(); 69 | 70 | const vS = gl.createShader(gl.VERTEX_SHADER)!; 71 | const fS = gl.createShader(gl.FRAGMENT_SHADER)!; 72 | 73 | const transformSource = (source: string, frag: boolean) => { 74 | source = source.trim(); 75 | 76 | if (source.includes('#version 150')) { 77 | source = source.replace('#version 150', '').trim(); 78 | if (frag) source = source.replace(/in\s+/g, 'varying '); 79 | else source = source.replace(/out\s+/g, 'varying '); 80 | 81 | if (!frag) source = source.replace(/in\s+/g, 'attribute '); 82 | 83 | if (frag) { 84 | source = source.replace(/texture(?=\([a-zA-Z0-9]+\s*,\s*[a-zA-Z0-9]+\))/g, 'texture2D'); 85 | } 86 | } 87 | 88 | return source; 89 | }; 90 | 91 | gl.shaderSource(vS, transformSource(program.vertex, false)); 92 | gl.shaderSource(fS, 'precision mediump float;\n' + transformSource(program.fragment, true)); 93 | 94 | gl.compileShader(vS); 95 | let succ = gl.getShaderInfoLog(vS); 96 | if (succ != null) { 97 | console.log(succ); 98 | } 99 | 100 | gl.compileShader(fS); 101 | succ = gl.getShaderInfoLog(fS); 102 | if (succ != null) { 103 | console.log(succ); 104 | } 105 | 106 | gl.attachShader(program.id!, vS); 107 | gl.attachShader(program.id!, fS); 108 | 109 | gl.linkProgram(program.id!); 110 | succ = gl.getProgramInfoLog(program.id!); 111 | if (succ != null) { 112 | console.log(succ); 113 | } 114 | 115 | gl.validateProgram(program.id!); 116 | 117 | gl.detachShader(program.id!, vS); 118 | gl.detachShader(program.id!, fS); 119 | gl.deleteShader(vS); 120 | gl.deleteShader(fS); 121 | 122 | program.projectionUniform = gl.getUniformLocation(program.id!, 'uProjectionMatrix'); 123 | program.modelViewUniform = gl.getUniformLocation(program.id!, 'uModelViewMatrix'); 124 | program.colorUniform = gl.getUniformLocation(program.id!, 'uColor'); 125 | 126 | if (program.hasTexture) { 127 | for (let i = 0; i < 4; ++i) { 128 | const n = gl.getUniformLocation(program.id!, 'uSampler' + i); 129 | if (n != null) { 130 | program.samplers.push(n); 131 | } 132 | } 133 | } 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /src/prop_dealer.ts: -------------------------------------------------------------------------------- 1 | export function dealColorRGB(value: any) { 2 | if (Array.isArray(value) && value.length >= 3) { 3 | return value; 4 | } else if (typeof value == 'string' && value.startsWith('#')) { 5 | // value = value.substring(1); 6 | const vl = value.substring(1); 7 | 8 | if (vl.length == 3) { 9 | return vl.split('').map((m) => parseInt(m + m, 16) / 255.0); 10 | } else if (vl.length == 6) { 11 | } 12 | } 13 | 14 | return [1, 1, 1, 1]; 15 | } 16 | 17 | export function dealColorRGBA(value: any) {} 18 | 19 | export function dealSizeVl(value: any) { 20 | if (typeof value == 'number') { 21 | return value; 22 | } 23 | 24 | try { 25 | return parseFloat(value); 26 | } catch (e) { 27 | return 0; 28 | } 29 | } 30 | 31 | export function dealOffsetVl(value: any) { 32 | if (typeof value == 'number') { 33 | return value; 34 | } 35 | 36 | try { 37 | return parseFloat(value); 38 | } catch (e) { 39 | return 0; 40 | } 41 | } 42 | 43 | export function dealTexture(value: any) { 44 | if (typeof value == 'string') { 45 | return value; 46 | } 47 | return 'null'; 48 | } 49 | 50 | export function dealUV(value: any) { 51 | if (Array.isArray(value) && value.length >= 2) { 52 | return value; 53 | } 54 | 55 | return [0, 0]; 56 | } 57 | 58 | export function dealUVSize(value: any): number[] { 59 | if (Array.isArray(value) && value.length >= 2) { 60 | return value; 61 | } 62 | 63 | return [0, 0]; 64 | } 65 | -------------------------------------------------------------------------------- /src/tessellator.ts: -------------------------------------------------------------------------------- 1 | import { DrawMode, VertexFormat, VertexFormatAttribute } from './vertex_format'; 2 | 3 | export class BufferBuilder { 4 | private format?: VertexFormat; 5 | private mode?: DrawMode; 6 | private attrib?: VertexFormatAttribute; 7 | private buffer: Float32Array; 8 | private attribIdx = 0; 9 | private attribOff = 0; 10 | private vertexCount = 0; 11 | private building = false; 12 | 13 | public constructor(initialCapacity = 64) { 14 | this.buffer = new Float32Array(initialCapacity); 15 | } 16 | 17 | public begin(mode: DrawMode, format: VertexFormat) { 18 | if (this.building) { 19 | throw new Error('Already building'); 20 | } 21 | this.building = true; 22 | this.format = format; 23 | this.mode = mode; 24 | this.attrib = format.attributes[0]; 25 | this.vertexCount = 0; 26 | this.attribIdx = 0; 27 | this.attribOff = 0; 28 | } 29 | 30 | public vertex(x: number, y: number, z: number): BufferBuilder { 31 | this.buffer[this.attribOff] = x; 32 | this.buffer[this.attribOff + 1] = y; 33 | this.buffer[this.attribOff + 2] = z; 34 | this.nextAttribute(); 35 | return this; 36 | } 37 | 38 | public normal(x: number, y: number, z: number): BufferBuilder { 39 | this.buffer[this.attribOff] = x; 40 | this.buffer[this.attribOff + 1] = y; 41 | this.buffer[this.attribOff + 2] = z; 42 | this.nextAttribute(); 43 | return this; 44 | } 45 | 46 | public texture(u: number, v: number): BufferBuilder { 47 | this.buffer[this.attribOff] = u; 48 | this.buffer[this.attribOff + 1] = v; 49 | this.nextAttribute(); 50 | return this; 51 | } 52 | 53 | public color(r: number, g: number, b: number, a: number): BufferBuilder { 54 | this.buffer[this.attribOff] = r; 55 | this.buffer[this.attribOff + 1] = g; 56 | this.buffer[this.attribOff + 2] = b; 57 | this.buffer[this.attribOff + 3] = a; 58 | this.nextAttribute(); 59 | return this; 60 | } 61 | 62 | private nextAttribute() { 63 | this.attribOff += this.attrib!.size; 64 | this.attribIdx = (this.attribIdx + 1) % this.format!.attributes.length; 65 | this.attrib = this.format!.attributes[this.attribIdx]; 66 | } 67 | 68 | public next() { 69 | ++this.vertexCount; 70 | } 71 | 72 | public draw() { 73 | if (!this.building) { 74 | throw new Error('Not building'); 75 | } 76 | 77 | if (this.vertexCount > 0) { 78 | const vb = this.format!.getVertexBuffer(); 79 | vb.upload(this.buffer.subarray(0, this.attribOff), this.attribOff, this.vertexCount, this.mode!, this.format!); 80 | vb.draw(); 81 | } 82 | 83 | this.vertexCount = 0; 84 | this.attribIdx = 0; 85 | this.attrib = undefined; 86 | this.attribOff = 0; 87 | this.building = false; 88 | } 89 | } 90 | 91 | export class Tessellator { 92 | private static INSTANCE: Tessellator; 93 | 94 | public static getInstance(): Tessellator { 95 | if (Tessellator.INSTANCE == null) { 96 | Tessellator.INSTANCE = new Tessellator(); 97 | } 98 | return Tessellator.INSTANCE; 99 | } 100 | 101 | private buffer: BufferBuilder; 102 | 103 | public constructor(initialCapacity = 64) { 104 | this.buffer = new BufferBuilder(initialCapacity); 105 | } 106 | 107 | public getBufferBuilder(): BufferBuilder { 108 | return this.buffer; 109 | } 110 | 111 | public draw() { 112 | this.buffer.draw(); 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | import * as monaco from 'monaco-editor'; 2 | 3 | export type FontSize = 'small' | 'normal' | 'large' | 'extra_large'; 4 | export type TextAlignment = 'left' | 'center' | 'right'; 5 | export type ClipDirection = 'left' | 'up' | 'down' | 'right'; 6 | export type Tiled = boolean | 'x' | 'y'; 7 | export type GradientDirection = 'vertical' | 'horizontal'; 8 | export type AnchorPoint = 'top_left' | 'top_middle' | 'top_right' | 'left_middle' | 'center' | 'right_middle' | 'bottom_left' | 'bottom_middle' | 'bottom_right'; 9 | export type Color = [number, number, number, number]; 10 | 11 | export interface ExplorerFileJson { 12 | type: 'json'; 13 | name: string; 14 | contextMenu: boolean; 15 | model: monaco.editor.ITextModel; 16 | } 17 | 18 | export interface ExplorerFileText { 19 | type: 'text'; 20 | name: string; 21 | contextMenu: boolean; 22 | model: monaco.editor.ITextModel; 23 | } 24 | 25 | export interface ExplorerFileImage { 26 | type: 'image'; 27 | name: string; 28 | contextMenu: boolean; 29 | element: HTMLImageElement; 30 | } 31 | 32 | export type ExplorerFile = ExplorerFileJson | ExplorerFileText | ExplorerFileImage; 33 | -------------------------------------------------------------------------------- /src/uis.ts: -------------------------------------------------------------------------------- 1 | export const namespacedSources: Record = {}; 2 | 3 | namespacedSources['common'] = ` 4 | { 5 | "namespace": "common", 6 | 7 | "base_screen": { 8 | "type": "screen" 9 | } 10 | } 11 | `; 12 | 13 | namespacedSources['play'] = await (await fetch('./ui/play_screen.json')).text(); 14 | 15 | namespacedSources['start'] = await (await fetch('./ui/start_screen.json')).text(); 16 | -------------------------------------------------------------------------------- /src/uiscreen.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KalmeMarq/json-ui-editor/9fb4fdde892eefe64d2ffd69f140227a44bb597f/src/uiscreen.ts -------------------------------------------------------------------------------- /src/vars.ts: -------------------------------------------------------------------------------- 1 | export let DEBUG = false; 2 | 3 | export function setDebug(vl: boolean) { 4 | DEBUG = vl; 5 | } 6 | -------------------------------------------------------------------------------- /src/vertex_format.ts: -------------------------------------------------------------------------------- 1 | import { GL, RenderSystem } from './gl'; 2 | 3 | export const COMPONENT_TYPE: Record<'UBYTE' | 'FLOAT', ComponentType> = { 4 | UBYTE: { 5 | glType: GL.UNSIGNED_BYTE, 6 | byteLength: 1 7 | }, 8 | FLOAT: { 9 | glType: GL.FLOAT, 10 | byteLength: 4 11 | } 12 | }; 13 | 14 | type SetupStateTask = (index: number, size: number, type: number, stride: number, offset: number) => void; 15 | type ClearStateTask = (index: number) => void; 16 | 17 | type ComponentType = { glType: number; byteLength: number }; 18 | 19 | const _M4BUFF = new Float32Array(16); 20 | 21 | export class VertexBuffer { 22 | static lastBound: WebGLBuffer | null = null; 23 | static lastIBOBound: WebGLBuffer | null = null; 24 | 25 | format?: VertexFormat; 26 | mode?: DrawMode; 27 | vbo: WebGLBuffer; 28 | ibo: WebGLBuffer; 29 | vertexCount = 0; 30 | lastSize = -1; 31 | indicesCount = 0; 32 | 33 | constructor() { 34 | this.vbo = GL.glCreateBuffer()!; 35 | this.ibo = GL.glCreateBuffer()!; 36 | } 37 | 38 | upload(buffer: Float32Array, size: number, vertexCount: number, mode: DrawMode, format: VertexFormat) { 39 | if (VertexBuffer.lastBound != this.vbo) { 40 | VertexBuffer.lastBound = this.vbo; 41 | GL.glBindBuffer(GL.ARRAY_BUFFER, this.vbo); 42 | } 43 | 44 | if (this.lastSize < size) { 45 | this.lastSize = size; 46 | GL.glBufferData(GL.ARRAY_BUFFER, buffer, GL.DYNAMIC_DRAW); 47 | } else { 48 | GL.glBufferSubData(GL.ARRAY_BUFFER, 0, buffer); 49 | } 50 | 51 | if (VertexBuffer.lastIBOBound != this.ibo) { 52 | VertexBuffer.lastIBOBound = this.ibo; 53 | GL.glBindBuffer(GL.ELEMENT_ARRAY_BUFFER, this.ibo); 54 | } 55 | 56 | const idxCount = ~~((vertexCount / 4) * 6); 57 | 58 | if (this.indicesCount < idxCount) { 59 | this.indicesCount = idxCount; 60 | if (mode == DrawMode.QUADS) { 61 | console.log('idxc', idxCount); 62 | const b = new Uint16Array(this.indicesCount); 63 | for (let i = 0; i < b.length; i += 6) { 64 | b[i] = 0; 65 | b[i + 1] = 1; 66 | b[i + 2] = 2; 67 | b[i + 3] = 2; 68 | b[i + 4] = 3; 69 | b[i + 5] = 0; 70 | } 71 | GL.glBufferData(GL.ELEMENT_ARRAY_BUFFER, b, GL.STATIC_DRAW); 72 | } 73 | } 74 | 75 | this.lastSize = size; 76 | 77 | this.vertexCount = vertexCount; 78 | this.mode = mode; 79 | this.format = format; 80 | } 81 | 82 | public draw() { 83 | if (this.vertexCount == 0) return; 84 | 85 | this.format!.setupState(); 86 | 87 | const shader = RenderSystem.getShader(); 88 | RenderSystem.useProgram(shader.id!); 89 | 90 | if (shader.colorUniform != null) { 91 | GL.glUniform4fv(shader.colorUniform, RenderSystem.getShaderColor()); 92 | } 93 | 94 | if (shader.projectionUniform != null) { 95 | GL.glUniformMatrix4fv(shader.projectionUniform, false, RenderSystem.getProjectionMatrix().get(_M4BUFF)); 96 | } 97 | 98 | if (shader.modelViewUniform != null) { 99 | GL.glUniformMatrix4fv(shader.modelViewUniform, false, RenderSystem.getModelViewMatrix().get(_M4BUFF)); 100 | } 101 | 102 | if (shader.hasTexture) { 103 | const tex = RenderSystem.getShaderTexture(); 104 | GL.glBindTexture(GL.TEXTURE_2D, tex.id); 105 | GL.glUniform1i(shader.samplers[0], 0); 106 | } 107 | 108 | GL.glDrawElements(this.mode!.glType, this.indicesCount, GL.UNSIGNED_SHORT, 0); 109 | 110 | this.format!.clearState(); 111 | } 112 | } 113 | 114 | export class VertexFormat { 115 | attributes: VertexFormatAttribute[]; 116 | offsets: number[]; 117 | stride: number; 118 | vertexBuffer?: VertexBuffer; 119 | 120 | constructor(attributes: VertexFormatAttribute[]) { 121 | this.attributes = attributes; 122 | this.offsets = []; 123 | let s = 0; 124 | for (const attr of attributes) { 125 | this.offsets.push(s); 126 | s += attr.byteLength; 127 | } 128 | this.stride = s; 129 | } 130 | 131 | getVertexBuffer(): VertexBuffer { 132 | if (this.vertexBuffer == null) { 133 | this.vertexBuffer = new VertexBuffer(); 134 | } 135 | return this.vertexBuffer; 136 | } 137 | 138 | public setupState() { 139 | for (let i = 0; i < this.attributes.length; ++i) { 140 | this.attributes[i].setupState(i, this.stride, this.offsets[i]); 141 | } 142 | } 143 | 144 | public clearState() { 145 | for (let i = 0; i < this.attributes.length; ++i) { 146 | this.attributes[i].clearState(i); 147 | } 148 | } 149 | } 150 | 151 | export class VertexFormatAttribute { 152 | size: number; 153 | byteLength: number; 154 | type: ComponentType; 155 | setupTask: SetupStateTask; 156 | clearTask: ClearStateTask; 157 | 158 | constructor(size: number, type: ComponentType, setupTask: SetupStateTask, clearTask: ClearStateTask) { 159 | this.byteLength = size * type.byteLength; 160 | this.size = size; 161 | this.type = type; 162 | this.setupTask = setupTask; 163 | this.clearTask = clearTask; 164 | } 165 | 166 | setupState(index: number, stride: number, offset: number) { 167 | this.setupTask(index, this.size, this.type.glType, stride, offset); 168 | } 169 | 170 | clearState(index: number) { 171 | this.clearTask(index); 172 | } 173 | } 174 | 175 | export const POSITION_ATTRIBUTE = new VertexFormatAttribute( 176 | 3, 177 | COMPONENT_TYPE.FLOAT, 178 | (idx, sz, type, srtd, off) => { 179 | GL.glEnableVertexAttribArray(idx); 180 | GL.glVertexAttribPointer(idx, sz, type, false, srtd, off); 181 | }, 182 | (idx) => { 183 | GL.glDisableVertexAttribArray(idx); 184 | } 185 | ); 186 | export const NORMAL_ATTRIBUTE = new VertexFormatAttribute( 187 | 3, 188 | COMPONENT_TYPE.FLOAT, 189 | (idx, sz, type, srtd, off) => { 190 | GL.glEnableVertexAttribArray(idx); 191 | GL.glVertexAttribPointer(idx, sz, type, false, srtd, off); 192 | }, 193 | (idx) => { 194 | GL.glDisableVertexAttribArray(idx); 195 | } 196 | ); 197 | export const TEXTURE_ATTRIBUTE = new VertexFormatAttribute( 198 | 2, 199 | COMPONENT_TYPE.FLOAT, 200 | (idx, sz, type, srtd, off) => { 201 | GL.glEnableVertexAttribArray(idx); 202 | GL.glVertexAttribPointer(idx, sz, type, false, srtd, off); 203 | }, 204 | (idx) => { 205 | GL.glDisableVertexAttribArray(idx); 206 | } 207 | ); 208 | export const COLOR_ATTRIBUTE = new VertexFormatAttribute( 209 | 4, 210 | COMPONENT_TYPE.FLOAT, 211 | (idx, sz, type, srtd, off) => { 212 | GL.glEnableVertexAttribArray(idx); 213 | GL.glVertexAttribPointer(idx, sz, type, false, srtd, off); 214 | }, 215 | (idx) => { 216 | GL.glDisableVertexAttribArray(idx); 217 | } 218 | ); 219 | 220 | export const VertexFormats = { 221 | POSITION: new VertexFormat([POSITION_ATTRIBUTE]), 222 | POSITION_COLOR: new VertexFormat([POSITION_ATTRIBUTE, COLOR_ATTRIBUTE]), 223 | POSITION_TEXTURE: new VertexFormat([POSITION_ATTRIBUTE, TEXTURE_ATTRIBUTE]), 224 | POSITION_TEXTURE_COLOR: new VertexFormat([POSITION_ATTRIBUTE, TEXTURE_ATTRIBUTE, COLOR_ATTRIBUTE]), 225 | POSITION_TEXTURE_COLOR_NORMAL: new VertexFormat([POSITION_ATTRIBUTE, TEXTURE_ATTRIBUTE, COLOR_ATTRIBUTE, NORMAL_ATTRIBUTE]) 226 | } as const; 227 | 228 | export type DrawMode = { glType: number }; 229 | export const DrawMode: Record<'QUADS' | 'TRIANGLES' | 'TRIANGLE_FAN' | 'TRIANGLE_STRIP', DrawMode> = { 230 | QUADS: { glType: GL.TRIANGLES }, 231 | TRIANGLES: { glType: GL.TRIANGLES }, 232 | TRIANGLE_FAN: { glType: GL.TRIANGLE_FAN }, 233 | TRIANGLE_STRIP: { glType: GL.TRIANGLE_STRIP } 234 | } as const; 235 | -------------------------------------------------------------------------------- /src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /static/ascii.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KalmeMarq/json-ui-editor/9fb4fdde892eefe64d2ffd69f140227a44bb597f/static/ascii.png -------------------------------------------------------------------------------- /static/features.txt: -------------------------------------------------------------------------------- 1 | Exclusive 2 | - gap 3 | - row_gap 4 | - colum_gap 5 | - x/y 6 | - width/height 7 | - u/v 8 | - u_size/v_size 9 | - base_width/base_height 10 | 11 | 12 | Input 13 | [ ] button_mappings 14 | [x] mappingType[pressed] 15 | [x] mappingType[double_pressed] 16 | [x] mappingType[global] 17 | 18 | Animations 19 | [ ] anim_type[wait] 20 | [ ] anim_type[alpha] 21 | [ ] anim_type[size] 22 | [ ] anim_type[offset] 23 | [ ] anim_type[color] 24 | [ ] anim_type[flip_book] 25 | [ ] anim_type[clip_ratio] 26 | [ ] anim_type[aseprite_flip_book] 27 | // Exclusive 28 | [ ] anim_type[uv] 29 | [ ] anim_type[uv_size] 30 | [ ] anim_type[base_size] 31 | [ ] anim_type[nineslice_size] 32 | [ ] anim_type[tiled_scale] 33 | [ ] anim_type[font_scale_factor] 34 | 35 | Control 36 | [x] property_bag 37 | - [ ] inherit propertybag 38 | 39 | [ ] layer (z_order) 40 | [ ] alpha 41 | [ ] propagate_alpha 42 | [ ] clips_children 43 | [ ] allow_clipping 44 | [ ] use_child_anchors 45 | [x] visible 46 | [ ] ignored 47 | [ ] enabled 48 | 49 | Layout 50 | [x] offset (x/y) 51 | [x] size (width/height) 52 | [ ] max_size (max_width/max_height) 53 | [ ] min_size (min_width/min_height) 54 | [ ] anchor_from 55 | [ ] anchor_to 56 | 57 | Data Binding 58 | [ ] inherit bindings 59 | 60 | [x] binding_type[none] 61 | [ ] binding_type[global] 62 | [ ] binding_type[view] 63 | [ ] binding_type[collection] 64 | [ ] binding_type[collection_details] 65 | 66 | Image 67 | [x] texture 68 | [x] color 69 | [x] uv (u/v) 70 | [x] uv_size (u_size/v_size) 71 | [x] base_size (base_width/base_height) 72 | [x] grayscale 73 | [ ] fill 74 | [ ] $fit_to_width 75 | [ ] bilinear 76 | [ ] tiled 77 | [ ] tiled_scale 78 | [ ] texture_file_system 79 | [ ] allow_debug_missing_texture 80 | [ ] nineslice_size (nineslice_x0/nineslice_y0/nineslice_x1/nineslice_y1) 81 | [ ] clip_direction 82 | [ ] clip_ratio 83 | [ ] keep_ratio 84 | 85 | Label 86 | [x] text 87 | [x] color 88 | [ ] localize 89 | [ ] enable_profanity_filter 90 | [ ] font_size 91 | [ ] font_scale_factor 92 | [ ] shadow 93 | [x] locked_color 94 | [ ] notify_on_ellipses 95 | [ ] locked_alpha 96 | [ ] hide_hyphen 97 | [ ] font_type 98 | [ ] backup_font_type 99 | [ ] text_alignment (alignment) 100 | 101 | Stack Panel 102 | [x] orientation 103 | 104 | Button 105 | [x] default_control 106 | [x] hover_control 107 | [ ] pressed_control 108 | [x] locked_control 109 | 110 | Screen 111 | [x] render_only_when_topmost 112 | [ ] is_showing_menu 113 | [x] force_render_below -------------------------------------------------------------------------------- /static/font.json: -------------------------------------------------------------------------------- 1 | { 2 | ".": { "u": 0, "v": 0, "a": 7 }, 3 | ",": { "u": 0, "v": 0, "a": 7 }, 4 | ";": { "u": 0, "v": 0, "a": 7 }, 5 | ":": { "u": 0, "v": 0, "a": 7 }, 6 | "-": { "u": 0, "v": 0, "a": 7 }, 7 | "_": { "u": 0, "v": 0, "a": 7 }, 8 | "'": { "u": 0, "v": 0, "a": 7 }, 9 | "\"": { "u": 0, "v": 0, "a": 7 }, 10 | "<": { "u": 0, "v": 0, "a": 7 }, 11 | ">": { "u": 0, "v": 0, "a": 7 }, 12 | "!": { "u": 0, "v": 0, "a": 7 }, 13 | "@": { "u": 0, "v": 0, "a": 7 }, 14 | "#": { "u": 0, "v": 0, "a": 7 }, 15 | "$": { "u": 0, "v": 0, "a": 7 }, 16 | "%": { "u": 0, "v": 0, "a": 7 }, 17 | "&": { "u": 0, "v": 0, "a": 7 }, 18 | "/": { "u": 0, "v": 0, "a": 7 }, 19 | "{": { "u": 0, "v": 0, "a": 7 }, 20 | "(": { "u": 0, "v": 0, "a": 7 }, 21 | "[": { "u": 0, "v": 0, "a": 7 }, 22 | ")": { "u": 0, "v": 0, "a": 7 }, 23 | "]": { "u": 0, "v": 0, "a": 7 }, 24 | "=": { "u": 0, "v": 0, "a": 7 }, 25 | "}": { "u": 0, "v": 0, "a": 7 }, 26 | "?": { "u": 0, "v": 0, "a": 7 }, 27 | "«": { "u": 0, "v": 0, "a": 7 }, 28 | "»": { "u": 0, "v": 0, "a": 7 }, 29 | "+": { "u": 0, "v": 0, "a": 7 }, 30 | "*": { "u": 0, "v": 0, "a": 7 }, 31 | "^": { "u": 0, "v": 0, "a": 7 }, 32 | "~": { "u": 0, "v": 0, "a": 7 }, 33 | "|": { "u": 0, "v": 0, "a": 7 }, 34 | "\\": { "u": 0, "v": 0, "a": 7 }, 35 | "0": { "u": 0, "v": 24, "a": 7 }, 36 | "1": { "u": 8, "v": 0, "a": 7 }, 37 | "2": { "u": 16, "v": 0, "a": 7 }, 38 | "3": { "u": 24, "v": 0, "a": 7 }, 39 | "4": { "u": 32, "v": 0, "a": 7 }, 40 | "5": { "u": 40, "v": 0, "a": 7 }, 41 | "6": { "u": 48, "v": 0, "a": 7 }, 42 | "7": { "u": 56, "v": 0, "a": 7 }, 43 | "8": { "u": 64, "v": 0, "a": 7 }, 44 | "9": { "u": 72, "v": 0, "a": 7 }, 45 | "a": { "u": 8, "v": 48, "a": 7 }, 46 | "b": { "u": 16, "v": 48, "a": 7 }, 47 | "c": { "u": 24, "v": 48, "a": 7 }, 48 | "d": { "u": 32, "v": 48, "a": 7 }, 49 | "e": { "u": 40, "v": 48, "a": 7 }, 50 | "f": { "u": 48, "v": 48, "a": 6 }, 51 | "g": { "u": 56, "v": 48, "a": 7 }, 52 | "h": { "u": 64, "v": 48, "a": 7 }, 53 | "i": { "u": 72, "v": 48, "a": 2 }, 54 | "j": { "u": 80, "v": 48, "a": 7 }, 55 | "k": { "u": 88, "v": 48, "a": 7 }, 56 | "l": { "u": 96, "v": 48, "a": 7 }, 57 | "m": { "u": 104, "v": 48, "a": 7 }, 58 | "n": { "u": 112, "v": 48, "a": 7 }, 59 | "o": { "u": 120, "v": 48, "a": 7 }, 60 | "p": { "u": 0, "v": 56, "a": 7 }, 61 | "q": { "u": 8, "v": 56, "a": 7 }, 62 | "r": { "u": 16, "v": 56, "a": 6 }, 63 | "s": { "u": 24, "v": 56, "a": 7 }, 64 | "t": { "u": 32, "v": 56, "a": 7 }, 65 | "u": { "u": 40, "v": 56, "a": 7 }, 66 | "v": { "u": 48, "v": 56, "a": 7 }, 67 | "w": { "u": 56, "v": 56, "a": 7 }, 68 | "x": { "u": 64, "v": 56, "a": 7 }, 69 | "y": { "u": 72, "v": 56, "a": 7 }, 70 | "z": { "u": 80, "v": 56, "a": 7 }, 71 | "A": { "u": 8, "v": 32, "a": 7 }, 72 | "B": { "u": 16, "v": 32, "a": 7 }, 73 | "C": { "u": 24, "v": 32, "a": 7 }, 74 | "D": { "u": 32, "v": 32, "a": 7 }, 75 | "E": { "u": 40, "v": 32, "a": 7 }, 76 | "F": { "u": 48, "v": 32, "a": 7 }, 77 | "G": { "u": 56, "v": 32, "a": 7 }, 78 | "H": { "u": 64, "v": 32, "a": 7 }, 79 | "I": { "u": 72, "v": 32, "a": 4 }, 80 | "J": { "u": 80, "v": 32, "a": 7 }, 81 | "K": { "u": 88, "v": 32, "a": 7 }, 82 | "L": { "u": 96, "v": 32, "a": 7 }, 83 | "M": { "u": 104, "v": 32, "a": 7 }, 84 | "N": { "u": 112, "v": 32, "a": 7 }, 85 | "O": { "u": 120, "v": 32, "a": 7 }, 86 | "P": { "u": 0, "v": 40, "a": 7 }, 87 | "Q": { "u": 8, "v": 40, "a": 7 }, 88 | "R": { "u": 16, "v": 40, "a": 7 }, 89 | "S": { "u": 24, "v": 40, "a": 7 }, 90 | "T": { "u": 32, "v": 40, "a": 7 }, 91 | "U": { "u": 40, "v": 40, "a": 7 }, 92 | "V": { "u": 48, "v": 40, "a": 7 }, 93 | "W": { "u": 56, "v": 40, "a": 7 }, 94 | "X": { "u": 64, "v": 40, "a": 7 }, 95 | "Y": { "u": 72, "v": 40, "a": 7 }, 96 | "Z": { "u": 80, "v": 40, "a": 7 } 97 | } -------------------------------------------------------------------------------- /static/font.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KalmeMarq/json-ui-editor/9fb4fdde892eefe64d2ffd69f140227a44bb597f/static/font.png -------------------------------------------------------------------------------- /static/font/codicon.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KalmeMarq/json-ui-editor/9fb4fdde892eefe64d2ffd69f140227a44bb597f/static/font/codicon.ttf -------------------------------------------------------------------------------- /static/grass.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KalmeMarq/json-ui-editor/9fb4fdde892eefe64d2ffd69f140227a44bb597f/static/grass.png -------------------------------------------------------------------------------- /static/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KalmeMarq/json-ui-editor/9fb4fdde892eefe64d2ffd69f140227a44bb597f/static/icon.png -------------------------------------------------------------------------------- /static/icons/default_file.svg: -------------------------------------------------------------------------------- 1 | default_file -------------------------------------------------------------------------------- /static/icons/file_type_image.svg: -------------------------------------------------------------------------------- 1 | file_type_image -------------------------------------------------------------------------------- /static/icons/file_type_json.svg: -------------------------------------------------------------------------------- 1 | file_type_json -------------------------------------------------------------------------------- /static/icons/file_type_text.svg: -------------------------------------------------------------------------------- 1 | file_type_text -------------------------------------------------------------------------------- /static/icons/github.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KalmeMarq/json-ui-editor/9fb4fdde892eefe64d2ffd69f140227a44bb597f/static/icons/github.png -------------------------------------------------------------------------------- /static/lang/en_us.json: -------------------------------------------------------------------------------- 1 | { 2 | "menu.play": "Play" 3 | } -------------------------------------------------------------------------------- /static/shaders/grayscale_position_texture.fsh: -------------------------------------------------------------------------------- 1 | #version 150 2 | 3 | uniform sampler2D uSampler0; 4 | uniform vec4 uColor; 5 | 6 | in vec2 vUV; 7 | 8 | void main() { 9 | vec4 color = texture(uSampler0, vUV) * uColor; 10 | float avg = (color.r + color.g + color.b) / 3.0; 11 | 12 | gl_FragColor = vec4(avg, avg, avg, color.a); 13 | } -------------------------------------------------------------------------------- /static/shaders/grayscale_position_texture.vsh: -------------------------------------------------------------------------------- 1 | #version 150 2 | 3 | uniform mat4 uProjectionMatrix; 4 | uniform mat4 uModelViewMatrix; 5 | 6 | in vec3 aPosition; 7 | in vec2 aUV; 8 | 9 | out vec2 vUV; 10 | 11 | void main() { 12 | gl_Position = uProjectionMatrix * uModelViewMatrix * vec4(aPosition, 1.0); 13 | vUV = aUV; 14 | } -------------------------------------------------------------------------------- /static/shaders/grayscale_position_texture_color.fsh: -------------------------------------------------------------------------------- 1 | #version 150 2 | 3 | uniform sampler2D uSampler0; 4 | uniform vec4 uColor; 5 | 6 | in vec2 vUV; 7 | in vec4 vColor; 8 | 9 | void main() { 10 | vec4 color = texture(uSampler0, vUV) * vColor * uColor; 11 | float avg = (color.r + color.g + color.b) / 3.0; 12 | gl_FragColor = vec4(avg, avg, avg, 1.0); 13 | } -------------------------------------------------------------------------------- /static/shaders/grayscale_position_texture_color.vsh: -------------------------------------------------------------------------------- 1 | #version 150 2 | 3 | uniform mat4 uProjectionMatrix; 4 | uniform mat4 uModelViewMatrix; 5 | 6 | in vec3 aPosition; 7 | in vec2 aUV; 8 | in vec4 aColor; 9 | 10 | out vec2 vUV; 11 | out vec4 vColor; 12 | 13 | void main() { 14 | gl_Position = uProjectionMatrix * uModelViewMatrix * vec4(aPosition, 1.0); 15 | vUV = aUV; 16 | vColor = aColor; 17 | } -------------------------------------------------------------------------------- /static/shaders/position.fsh: -------------------------------------------------------------------------------- 1 | uniform vec4 uColor; 2 | 3 | void main() { 4 | gl_FragColor = uColor; 5 | } -------------------------------------------------------------------------------- /static/shaders/position.vsh: -------------------------------------------------------------------------------- 1 | #version 150 2 | 3 | uniform mat4 uProjectionMatrix; 4 | uniform mat4 uModelViewMatrix; 5 | 6 | in vec3 aPosition; 7 | 8 | void main() { 9 | gl_Position = uProjectionMatrix * uModelViewMatrix * vec4(aPosition, 1.0); 10 | } -------------------------------------------------------------------------------- /static/shaders/position_color.fsh: -------------------------------------------------------------------------------- 1 | #version 150 2 | 3 | uniform vec4 uColor; 4 | 5 | in vec4 vColor; 6 | 7 | void main() { 8 | gl_FragColor = vColor * uColor; 9 | } -------------------------------------------------------------------------------- /static/shaders/position_color.vsh: -------------------------------------------------------------------------------- 1 | #version 150 2 | 3 | uniform mat4 uProjectionMatrix; 4 | uniform mat4 uModelViewMatrix; 5 | 6 | in vec3 aPosition; 7 | in vec4 aColor; 8 | 9 | out vec4 vColor; 10 | 11 | void main() { 12 | gl_Position = uProjectionMatrix * uModelViewMatrix * vec4(aPosition, 1.0); 13 | vColor = aColor; 14 | } -------------------------------------------------------------------------------- /static/shaders/position_texture.fsh: -------------------------------------------------------------------------------- 1 | #version 150 2 | 3 | uniform sampler2D uSampler0; 4 | uniform vec4 uColor; 5 | 6 | in vec2 vUV; 7 | 8 | void main() { 9 | gl_FragColor = texture(uSampler0, vUV) * uColor; 10 | } -------------------------------------------------------------------------------- /static/shaders/position_texture.vsh: -------------------------------------------------------------------------------- 1 | #version 150 2 | 3 | uniform mat4 uProjectionMatrix; 4 | uniform mat4 uModelViewMatrix; 5 | 6 | in vec3 aPosition; 7 | in vec2 aUV; 8 | 9 | out vec2 vUV; 10 | 11 | void main() { 12 | gl_Position = uProjectionMatrix * uModelViewMatrix * vec4(aPosition, 1.0); 13 | vUV = aUV; 14 | } -------------------------------------------------------------------------------- /static/shaders/position_texture_color.fsh: -------------------------------------------------------------------------------- 1 | #version 150 2 | 3 | uniform sampler2D uSampler0; 4 | uniform vec4 uColor; 5 | 6 | in vec2 vUV; 7 | in vec4 vColor; 8 | 9 | void main() { 10 | gl_FragColor = texture(uSampler0, vUV) * vColor * uColor; 11 | } -------------------------------------------------------------------------------- /static/shaders/position_texture_color.vsh: -------------------------------------------------------------------------------- 1 | #version 150 2 | 3 | uniform mat4 uProjectionMatrix; 4 | uniform mat4 uModelViewMatrix; 5 | 6 | in vec3 aPosition; 7 | in vec2 aUV; 8 | in vec4 aColor; 9 | 10 | out vec2 vUV; 11 | out vec4 vColor; 12 | 13 | void main() { 14 | gl_Position = uProjectionMatrix * uModelViewMatrix * vec4(aPosition, 1.0); 15 | vUV = aUV; 16 | vColor = aColor; 17 | } -------------------------------------------------------------------------------- /static/ui/global_variables.json: -------------------------------------------------------------------------------- 1 | { 2 | "$minecraft": "Minecraft Bugrock", 3 | "$0_color_format": [0.0, 0.0, 0.0], // Black 4 | "$1_color_format": [0.0, 0.0, 0.667], // Dark Blue 5 | "$2_color_format": [0.0, 0.667, 0 ],// Dark Green 6 | "$3_color_format": [0.0, 0.667, 0.667], // Dark Aqua 7 | "$4_color_format": [0.667, 0.0, 0.0], // Dark Red 8 | "$5_color_format": [0.667, 0.0, 0.667], // Dark Purple 9 | "$6_color_format": [1.0, 0.667, 0.0], // Gold 10 | "$7_color_format": [0.667, 0.667, 0.667], // Gray 11 | "$8_color_format": [0.333, 0.333, 0.333], // Dark Gray 12 | "$9_color_format": [0.333, 0.333, 1.0], // Blue 13 | "$a_color_format": [0.333, 1.0, 0.333], // Green 14 | "$b_color_format": [0.333, 1.0, 1.0], // Aqua 15 | "$c_color_format": [1.0, 0.333, 0.333], // Red 16 | "$d_color_format": [1.0, 0.333, 1.0], // Light Purple 17 | "$e_color_format": [1.0, 1.0, 0.333], // Yellow 18 | "$f_color_format": [1.0, 1.0, 1.0], // White, 19 | "$dialog_title_color": [0.25, 0.25, 0.25] 20 | } -------------------------------------------------------------------------------- /static/ui/play_screen.json: -------------------------------------------------------------------------------- 1 | { 2 | "namespace": "play", 3 | 4 | "button": { 5 | "type": "button", 6 | "size": [100, 20], 7 | "default_control": "default", 8 | "hover_control": "hover", 9 | "locked_control": "locked", 10 | "enabled": "#enabled", 11 | "button_mappings": [ 12 | { 13 | "from_button_id": "button.menu_select", 14 | "to_button_id": "button.menu_exit", 15 | "mapping_type": "pressed" 16 | } 17 | ], 18 | "controls": [ 19 | { 20 | "default": { 21 | "type": "image", 22 | "texture": "grass.png", 23 | "uv": [0, 0], 24 | "uv_size": [100, 20], 25 | "size": [100, 20] 26 | } 27 | }, 28 | { 29 | "hover": { 30 | "type": "image", 31 | "texture": "grass.png", 32 | "uv": [0, 20], 33 | "uv_size": [100, 20], 34 | "size": [100, 20] 35 | } 36 | }, 37 | { 38 | "locked": { 39 | "type": "image", 40 | "texture": "grass.png", 41 | "uv": [0, 40], 42 | "uv_size": [100, 20], 43 | "size": [100, 20] 44 | } 45 | }, 46 | { 47 | "label": { 48 | "type": "label", 49 | "text": "Cancel", 50 | "offset": [34, 6] 51 | } 52 | } 53 | ] 54 | }, 55 | 56 | // Screen entrypoint. Do not remove! 57 | "play_screen": { 58 | "type": "screen", 59 | "force_render_below": false, 60 | "button_mappings": [ 61 | { 62 | "from_button_id": "button.menu_cancel", 63 | "to_button_id": "button.menu_exit", 64 | "mapping_type": "global" 65 | } 66 | ], 67 | "controls": [ 68 | { 69 | "image0": { 70 | "type": "image", 71 | "texture": "grass.png", 72 | "grayscale": true, 73 | "size": [50, 50] 74 | } 75 | }, 76 | { "button@button": {} } 77 | ] 78 | } 79 | } -------------------------------------------------------------------------------- /static/ui/screen_defs.json: -------------------------------------------------------------------------------- 1 | { 2 | "screen_definitions": { 3 | "default": "start", 4 | "context": { 5 | "variables": { 6 | "$is_trial": false, 7 | "$is_publish": true, 8 | "#playername": "Steve", 9 | "#gamertag_label": "Steve", 10 | "$desktop_screen": false, 11 | "$pocket_screen": false 12 | }, 13 | "whitelist_urls": [ 14 | "https://github.com/KalmeMarq/json-ui-editor" 15 | ] 16 | }, 17 | "screens": { 18 | "start": { 19 | "target": "start.start_screen", 20 | "button_id": "button.menu_start", 21 | "context": { 22 | "variables": { 23 | }, 24 | "bindings": { 25 | "#pb1_visible": true, 26 | "#pb1_total_amount": 150, 27 | "#pb1_current_amount": 75, 28 | "#button0_enabled": true, 29 | "#version": "Minecraft 4.13.69", 30 | "#player_nametag": "Notch", 31 | "#player_nametag_visible": true, 32 | "#development_version": "584d83f2f4b7fb96", 33 | "#online_buttons_visible": true, 34 | "#feedback_visible": true, 35 | "$play_button_target": "button.menu_play", 36 | "#marketplace_disabled": false, 37 | "#new_offers": true, 38 | "#toolbox_button_visible": true, 39 | "#realms_notification_count": 13, 40 | "#realms_notification_visible": true 41 | }, 42 | "whitelist_urls": [ 43 | "https://aka.ms/minecraftfb", 44 | "https://aka.ms/minecraftfbbeta" 45 | ] 46 | } 47 | }, 48 | "play": { 49 | "target": "play.play_screen", 50 | "button_id": "button.menu_play", 51 | "context": { 52 | "variables": { 53 | }, 54 | "bindings": { 55 | }, 56 | "whitelist_urls": [] 57 | } 58 | } 59 | } 60 | } 61 | } -------------------------------------------------------------------------------- /static/ui/start_screen.json: -------------------------------------------------------------------------------- 1 | /* 2 | Exclusive features: 3 | - renderer[fill_renderer] 4 | - x/y/width/height/u/v/u_size/v_size/base_width/base_height properties. offset/size/uv/uv_size/base_size are shortcut props here. 5 | - screens props can use bindings 6 | - color 7 | - prop can be "#RGB", "#RGBA", "#RRGGBB", "#RRGGBBAA" or [r, g, b, a] 8 | - row_gap/column_gap (gap) 9 | - renderer[name_tag_renderer] 10 | - #y_padding 11 | Missing features: 12 | - almost all 13 | - variables 14 | 15 | Go to features.txt to see which features are already "implemented" 16 | */ 17 | { 18 | "namespace": "start", 19 | 20 | "stackpanel0": { 21 | "type": "stack_panel", 22 | "orientation": "horizontal", 23 | "size": [0, 40], 24 | "column_gap": 4, 25 | "controls": [ 26 | { 27 | "0fill": { 28 | "type": "custom", 29 | "renderer": "fill_renderer", 30 | "color": [1, 0, 0, 1], 31 | "size": [40, 40] 32 | } 33 | }, 34 | { 35 | "1fill": { 36 | "type": "custom", 37 | "renderer": "fill_renderer", 38 | "color": [0, 1, 0, 1], 39 | "size": [40, 40] 40 | } 41 | }, 42 | { 43 | "2fill": { 44 | "type": "custom", 45 | "renderer": "fill_renderer", 46 | "color": [0, 0, 1, 1], 47 | "size": [40, 40] 48 | } 49 | }, 50 | { 51 | "3fill": { 52 | "type": "custom", 53 | "renderer": "fill_renderer", 54 | "color": [1, 1, 0, 1], 55 | "size": [40, 40] 56 | } 57 | }, 58 | { 59 | "4fill": { 60 | "type": "custom", 61 | "renderer": "fill_renderer", 62 | "color": [0, 1, 1, 1], 63 | "size": [40, 40] 64 | } 65 | }, 66 | { 67 | "5fill": { 68 | "type": "custom", 69 | "renderer": "fill_renderer", 70 | "color": [1, 0, 1, 1], 71 | "size": [40, 40] 72 | } 73 | } 74 | ] 75 | }, 76 | 77 | "image0": { 78 | "type": "image", 79 | "texture": "grass.png", 80 | "x": "#x_off", 81 | "y": "#y_off", 82 | "size": [100, 100], 83 | "color": [0, 0, 1, 1], 84 | "bindings": [ 85 | { 86 | "binding_name": "#test_x_off", 87 | "binding_name_override": "#x_off" 88 | }, 89 | { 90 | "binding_name": "#test_y_off", 91 | "binding_name_override": "#y_off" 92 | } 93 | ] 94 | }, 95 | 96 | "fill0": { 97 | "type": "custom", 98 | "renderer": "fill_renderer", 99 | "color": [1, 0, 0, 1], 100 | "offset": [0, 80], 101 | "width": "#size", 102 | "height": "#size", 103 | "bindings": [ 104 | { 105 | "binding_name": "#fill0_size", 106 | "binding_name_override": "#size" 107 | } 108 | ] 109 | }, 110 | 111 | "gradv0": { 112 | "type": "custom", 113 | "renderer": "gradient_renderer", 114 | "color1": "#color0", 115 | "color2": "#color1", 116 | "offset": [0, 170], 117 | "size": [80, 80], 118 | "bindings": [ 119 | { 120 | "binding_name": "#grad0_color0", 121 | "binding_name_override": "#color0" 122 | }, 123 | { 124 | "binding_name": "#grad0_color1", 125 | "binding_name_override": "#color1" 126 | } 127 | ] 128 | }, 129 | 130 | "gradv1": { 131 | "type": "custom", 132 | "renderer": "gradient_renderer", 133 | "gradient_direction": "horizontal", 134 | "color1": [1, 0, 1, 1], 135 | "color2": [0, 0, 1, 1], 136 | "offset": [90, 170], 137 | "width": "#width", 138 | "height": 80, 139 | "bindings": [ 140 | { 141 | "binding_name": "#grad1_width", 142 | "binding_name_override": "#width" 143 | } 144 | ] 145 | }, 146 | 147 | "button": { 148 | "type": "button", 149 | "size": [100, 20], 150 | "default_control": "default", 151 | "hover_control": "hover", 152 | "locked_control": "locked", 153 | "enabled": "#enabled", 154 | "offset": [20, 20], 155 | "button_mappings": [ 156 | { 157 | "from_button_id": "button.menu_select", 158 | "to_button_id": "button.menu_play", 159 | "mapping_type": "pressed" 160 | } 161 | ], 162 | "bindings": [ 163 | { 164 | "binding_name": "#button0_enabled", 165 | "binding_name_override": "#enabled" 166 | } 167 | ], 168 | "controls": [ 169 | { 170 | "default": { 171 | "type": "image", 172 | "texture": "grass.png", 173 | "uv": [0, 0], 174 | "uv_size": [100, 20], 175 | "size": [100, 20] 176 | } 177 | }, 178 | { 179 | "hover": { 180 | "type": "image", 181 | "texture": "grass.png", 182 | "uv": [0, 20], 183 | "uv_size": [100, 20], 184 | "size": [100, 20] 185 | } 186 | }, 187 | { 188 | "locked": { 189 | "type": "image", 190 | "texture": "grass.png", 191 | "uv": [0, 40], 192 | "uv_size": [100, 20], 193 | "size": [100, 20] 194 | } 195 | }, 196 | { 197 | "label": { 198 | "type": "label", 199 | "text": "Play", 200 | "offset": [40, 6] 201 | } 202 | } 203 | ] 204 | }, 205 | 206 | "label0": { 207 | "type": "label", 208 | "text": "#version", 209 | "offset": [300, 20], 210 | "bindings": [ 211 | { 212 | "binding_name": "#version" 213 | } 214 | ] 215 | }, 216 | 217 | // Screen entrypoint. Do not remove! 218 | "start_screen": { 219 | "type": "screen", 220 | "controls": [ 221 | { "stackpanel0@stackpanel0": {} }, 222 | { "image0@image0": {} }, 223 | { "fill0@fill0": {} }, 224 | { "gradv0@gradv0": {} }, 225 | { "gradv1@gradv1": {} }, 226 | { "button@button": {} }, 227 | { "label0@label0": {} }, 228 | { 229 | "nm": { 230 | "type": "custom", 231 | "renderer": "name_tag_renderer", 232 | "bindings": [ 233 | { 234 | "binding_name": "#player_nametag", 235 | "binding_name_override": "#playername" 236 | }, 237 | { 238 | "binding_name": "#player_nametag_visible", 239 | "binding_name_override": "#visible" 240 | } 241 | ] 242 | } 243 | }, 244 | { 245 | "pb": { 246 | "type": "custom", 247 | "renderer": "progress_bar_renderer", 248 | "size": [100, 4], 249 | "offset": [0, 14], 250 | "property_bag": { 251 | "#progress_bar_current_amount": 75, 252 | "#progress_bar_total_amount": 250, 253 | "#progress_bar_visible": true 254 | } 255 | } 256 | }, 257 | { 258 | "pb1": { 259 | "type": "custom", 260 | "renderer": "progress_bar_renderer", 261 | "size": [100, 4], 262 | "offset": [0, 20], 263 | "property_bag": { 264 | "is_durability": true, 265 | "#progress_bar_current_amount": 75, 266 | "#progress_bar_total_amount": 150, 267 | "#progress_bar_visible": true 268 | }, 269 | "bindings": [ 270 | { 271 | "binding_name": "#pb1_visible", 272 | "binding_name_override": "#visible" 273 | }, 274 | { 275 | "binding_name": "#pb1_total_amount", 276 | "binding_name_override": "#progress_bar_total_amount" 277 | }, 278 | { 279 | "binding_name": "#pb1_current_amount", 280 | "binding_name_override": "#progress_bar_current_amount" 281 | } 282 | ] 283 | } 284 | } 285 | ] 286 | } 287 | } -------------------------------------------------------------------------------- /static/ui/ui_defs.json: -------------------------------------------------------------------------------- 1 | { 2 | "ui_defs": [ 3 | "start_screen.json", 4 | "play_screen.json", 5 | "ui_common.json" 6 | ] 7 | } -------------------------------------------------------------------------------- /style.css: -------------------------------------------------------------------------------- 1 | 2 | #fps { 3 | position: absolute; 4 | bottom: 14px; 5 | left: 70px; 6 | font-size: 1.5rem; 7 | color: white; 8 | font-family: 'Fira Code'; 9 | z-index: 100; 10 | display: block; 11 | } 12 | /* 13 | * { 14 | margin: 0; 15 | padding: 0; 16 | box-sizing: border-box; 17 | } 18 | 19 | body { 20 | overflow: hidden; 21 | width: 100vw; 22 | overflow: hidden; 23 | } 24 | 25 | #ui-files { 26 | -webkit-user-drag: element; 27 | display: none; 28 | position: absolute; 29 | top: 0; 30 | left: 0; 31 | z-index: 100; 32 | background: #131313; 33 | } 34 | 35 | #ui-files details { 36 | color: white; 37 | } 38 | 39 | #ui-files summary { 40 | color: white; 41 | user-select: none; 42 | } 43 | 44 | #ui-files details > div { 45 | resize: both; 46 | overflow: auto; 47 | } */ 48 | 49 | * { 50 | margin: 0; 51 | padding: 0; 52 | box-sizing: border-box; 53 | image-rendering: pixelated; 54 | image-rendering: crisp-edges; 55 | text-rendering: geometricPrecision; 56 | } 57 | 58 | body { 59 | overflow: hidden; 60 | height: 100vh; 61 | background: #161616; 62 | font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; 63 | } 64 | 65 | .app { 66 | display: flex; 67 | flex-direction: row; 68 | width: 100vw; 69 | height: 100vh; 70 | } 71 | 72 | .files { 73 | display: flex; 74 | flex-direction: row; 75 | width: 55%; 76 | min-width: 100px; 77 | height: 100vh; 78 | } 79 | 80 | #explorer { 81 | width: 25%; 82 | height: 100%; 83 | background: #252526; 84 | } 85 | 86 | #editor { 87 | width: 100%; 88 | height: 100vh; 89 | } 90 | 91 | #image-previewer { 92 | width: 100%; 93 | height: 100vh; 94 | display: none; 95 | place-content: center; 96 | place-items: center; 97 | } 98 | 99 | #image-previewer img { 100 | max-width: 90%; 101 | width: 100%; 102 | } 103 | 104 | .display { 105 | display: flex; 106 | flex-direction: column; 107 | width: 50%; 108 | height: 100vh; 109 | } 110 | 111 | #screen { 112 | width: 100%; 113 | height: 70%; 114 | } 115 | 116 | #console { 117 | width: 100%; 118 | height: 30%; 119 | background: black; 120 | color: white; 121 | overflow-y: auto; 122 | } 123 | 124 | #console p { 125 | cursor: default; 126 | } 127 | 128 | #console p::selection { 129 | background: #FFFFFF44; 130 | } 131 | 132 | #console::-webkit-scrollbar { 133 | background: black; 134 | } 135 | 136 | #console::-webkit-scrollbar-thumb { 137 | background: rgb(49, 49, 49); 138 | } 139 | 140 | #console::-webkit-scrollbar-thumb:hover { 141 | background: rgb(70, 70, 70); 142 | } 143 | 144 | /* */ 145 | #explorer { 146 | display: flex; 147 | flex-direction: column; 148 | } 149 | 150 | #explorer-list { 151 | display: flex; 152 | flex-direction: column; 153 | height: 100%; 154 | border: 1px solid transparent; 155 | overflow-y: auto; 156 | } 157 | 158 | #explorer-list.drag-over { 159 | border: 1px solid #007FD4; 160 | } 161 | 162 | #explorer-list::-webkit-scrollbar { 163 | background: transparent; 164 | width: 10px; 165 | } 166 | 167 | #explorer-list::-webkit-scrollbar-thumb { 168 | background: rgba(255, 255, 255, 0); 169 | transition-duration: 200ms; 170 | transition-property: background; 171 | } 172 | 173 | #explorer-list:hover::-webkit-scrollbar-thumb { 174 | background: rgba(255, 255, 255, 0.1); 175 | } 176 | 177 | #explorer-list::-webkit-scrollbar-thumb:hover { 178 | background: rgba(255, 255, 255, 0.15); 179 | } 180 | 181 | .file-item { 182 | cursor: pointer; 183 | background: none; 184 | height: 22px; 185 | min-height: 22px; 186 | border: 1px solid transparent; 187 | } 188 | 189 | .file-item:hover { 190 | background: #2A2D2E; 191 | } 192 | 193 | .file-item:focus { 194 | outline: none; 195 | background: #094771; 196 | border-color: #007FD4; 197 | } 198 | 199 | .file-item:active { 200 | background: #094771; 201 | border-color: #007FD4; 202 | } 203 | 204 | .file-item-content { 205 | display: flex; 206 | flex-direction: row; 207 | padding: 0 1.2rem; 208 | gap: 0 0.4rem; 209 | place-items: center; 210 | } 211 | 212 | .file-icon img { 213 | position: relative; 214 | width: 16px; 215 | top: 1px; 216 | } 217 | 218 | .file-label { 219 | color: #DDD; 220 | white-space: nowrap; 221 | overflow: hidden; 222 | text-overflow: ellipsis; 223 | } 224 | 225 | .toolbar { 226 | display: flex; 227 | flex-direction: row; 228 | padding: 0.2rem 1.2rem; 229 | } 230 | 231 | .toolbar span { 232 | color: #DDD; 233 | font-size: 14px; 234 | user-select: none; 235 | } 236 | 237 | .filler { 238 | width: 100%; 239 | } 240 | 241 | .toolbar-btn { 242 | width: 20px; 243 | height: 20px; 244 | border-radius: 4px; 245 | cursor: pointer; 246 | background: none; 247 | border: 1px solid transparent; 248 | margin: 0 2px; 249 | } 250 | 251 | .toolbar-btn:hover { 252 | background: #484b4d; 253 | } 254 | 255 | .toolbar-btn:active.codicon[class*='codicon-'] { 256 | color: #DDD; 257 | } 258 | 259 | /* */ 260 | @font-face { 261 | font-family: "codicon"; 262 | font-display: block; 263 | src: url("./font/codicon.ttf?5d4d76ab2ce5108968ad644d591a16a6") format("truetype"); 264 | } 265 | 266 | .codicon[class*='codicon-'] { 267 | font: normal normal normal 16px/1 codicon; 268 | display: inline-block; 269 | text-decoration: none; 270 | text-rendering: auto; 271 | text-align: center; 272 | text-transform: none; 273 | -webkit-font-smoothing: antialiased; 274 | -moz-osx-font-smoothing: grayscale; 275 | user-select: none; 276 | -webkit-user-select: none; 277 | -ms-user-select: none; 278 | color: #BBB; 279 | } 280 | 281 | /* */ 282 | .popup-bg { 283 | position: absolute; 284 | width: 100%; 285 | height: 100%; 286 | background: #00000099; 287 | } 288 | 289 | #new-file-popup { 290 | position: absolute; 291 | display: none; 292 | width: 100%; 293 | height: 100%; 294 | top: 0; 295 | left: 0; 296 | } 297 | 298 | #new-file-name { 299 | background: #161616; 300 | border: 1px solid transparent; 301 | border-radius: 4px; 302 | color: white; 303 | padding: 0.4rem 0.3rem; 304 | } 305 | 306 | .popup-content { 307 | display: flex; 308 | flex-direction: column; 309 | gap: 0.5rem 0; 310 | position: absolute; 311 | background: #2A2D2E; 312 | top: 50%; 313 | left: 50%; 314 | padding: 1rem; 315 | border-radius: 8px; 316 | transform: translate(-50%, -50%); 317 | } 318 | 319 | #create-new-file { 320 | cursor: pointer; 321 | background: #525557; 322 | color: #EEE; 323 | border: 1px solid transparent; 324 | padding: 0.4rem 1rem; 325 | } 326 | 327 | #create-new-file:hover { 328 | background: #676b6d; 329 | } 330 | 331 | #cancel-new-file { 332 | cursor: pointer; 333 | background: #525557; 334 | color: #EEE; 335 | border: 1px solid transparent; 336 | padding: 0.4rem 1rem; 337 | } 338 | 339 | #cancel-new-file:hover { 340 | background: #676b6d; 341 | } 342 | 343 | /* */ 344 | 345 | .file-contextmenu { 346 | min-width: 200px; 347 | display: none; 348 | flex-direction: column; 349 | gap: 3px 0; 350 | position: absolute; 351 | background: #3C3C3C; 352 | top: 0; 353 | left: 0; 354 | text-align: left; 355 | padding: 0.4rem 0; 356 | } 357 | 358 | .ctxmenu-item { 359 | background: transparent; 360 | border: none; 361 | color: #DDD; 362 | cursor: pointer; 363 | padding: 0 1rem; 364 | height: 23px; 365 | text-align: left; 366 | } 367 | 368 | .ctxmenu-item:hover { 369 | background: #094771; 370 | } 371 | 372 | .file-contextmenu.locked .ctxmenu-item { 373 | cursor: inherit; 374 | background: transparent; 375 | color: #888; 376 | } 377 | 378 | .file-contextmenu.locked .ctxmenu-item:hover { 379 | background: transparent; 380 | } 381 | 382 | /* */ 383 | .links { 384 | padding: 0.6rem 1.2rem; 385 | } 386 | 387 | .links a:hover { 388 | filter: brightness(50%); 389 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "module": "ESNext", 5 | "moduleResolution": "Node", 6 | "strict": true, 7 | "strictNullChecks": true, 8 | "noImplicitThis": true, 9 | // "noImplicitOverride": true, 10 | "noImplicitAny": true, 11 | "noPropertyAccessFromIndexSignature": true, 12 | "esModuleInterop": true, 13 | "rootDir": "./src", 14 | "baseUrl": ".", 15 | 16 | // "target": "ES2020", 17 | "useDefineForClassFields": true, 18 | // "module": "ESNext", 19 | "lib": ["ES2020", "DOM", "DOM.Iterable"], 20 | "skipLibCheck": true, 21 | 22 | /* Bundler mode */ 23 | // "moduleResolution": "bundler", 24 | "allowImportingTsExtensions": true, 25 | "resolveJsonModule": true, 26 | "isolatedModules": true, 27 | "noEmit": true, 28 | 29 | /* Linting */ 30 | // "strict": true, 31 | // "noUnusedLocals": true, 32 | // "noUnusedParameters": true, 33 | "noFallthroughCasesInSwitch": true 34 | }, 35 | "include": ["src"] 36 | } 37 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite'; 2 | import monacoEditorPlugin from 'vite-plugin-monaco-editor'; 3 | 4 | export default defineConfig({ 5 | build: { 6 | target: 'esnext' 7 | }, 8 | base: './', 9 | plugins: [ 10 | monacoEditorPlugin({ 11 | languageWorkers: ['json', 'editorWorkerService'] 12 | }) 13 | ], 14 | publicDir: 'static' 15 | }); 16 | --------------------------------------------------------------------------------