main 267ccca74304 cached
664 files
4.4 MB
1.2M tokens
3880 symbols
1 requests
Download .txt
Showing preview only (4,803K chars total). Download the full file or copy to clipboard to get everything.
Repository: Infinite-Chess/infinitechess.org
Branch: main
Commit: 267ccca74304
Files: 664
Total size: 4.4 MB

Directory structure:
gitextract_2jn2wipu/

├── .github/
│   ├── copilot-instructions.md
│   ├── pull_request_template.md
│   └── workflows/
│       ├── ci.yml
│       └── deploy.yml
├── .gitignore
├── .husky/
│   └── pre-commit
├── .prettierignore
├── .prettierrc.json
├── CLAUDE.md
├── LICENSE
├── README.md
├── build/
│   ├── client.ts
│   ├── engine-wasm.ts
│   ├── env.ts
│   ├── index.ts
│   ├── plugins.ts
│   ├── server.ts
│   └── views.ts
├── dev-utils/
│   ├── ICN_METADATA_TRANSLATIONS.md
│   ├── REDESIGN/
│   │   ├── design.md
│   │   ├── runner_setup.md
│   │   ├── stack.md
│   │   └── todo.md
│   ├── SKELETON.css
│   ├── SKELETON.html
│   ├── live-game-persistence.md
│   ├── pieces/
│   │   ├── spritesheet 512/
│   │   │   └── How to create spritesheet.md
│   │   └── svg/
│   │       └── Converting PNG to SVG.md
│   ├── post_processing_effects/
│   │   ├── posterize/
│   │   │   ├── PosterizePass.ts
│   │   │   └── fragment.glsl
│   │   ├── radial_distortion/
│   │   │   ├── RadialDistortionPass.ts
│   │   │   └── fragment.glsl
│   │   └── rolling_hills/
│   │       ├── RollingHillsPass.ts
│   │       └── fragment.glsl
│   ├── readme.md
│   ├── scripts/
│   │   ├── PatreonAPI.ts
│   │   ├── audio/
│   │   │   └── processors/
│   │   │       └── bitcrusher/
│   │   │           ├── BitcrusherNode.ts
│   │   │           └── BitcrusherProcessor.ts
│   │   ├── clientEventDispatcher.ts
│   │   ├── events.ts
│   │   ├── gl-matrix.js
│   │   ├── icn-regex-matching.ts
│   │   ├── meshSimplification.ts
│   │   ├── positionnormalizer/
│   │   │   ├── moveexpander.ts
│   │   │   ├── normalizertester.ts
│   │   │   ├── positioncompressor.ts
│   │   │   ├── positioncompressorplusintersections.ts
│   │   │   └── unusedpositionnormalizermethods.ts
│   │   └── vertexdatatotexture.ts
│   ├── shaders/
│   │   ├── texture/
│   │   │   ├── instanced/
│   │   │   │   └── tint/
│   │   │   │       ├── fragment.glsl
│   │   │   │       └── vertex.glsl
│   │   │   └── tint/
│   │   │       ├── fragment.glsl
│   │   │       └── vertex.glsl
│   │   └── voronoi/
│   │       └── fragment.glsl
│   ├── sounds/
│   │   └── SoundscapeGenerator.html
│   └── spritesheet_generator/
│       ├── spritesheet.ts
│       └── spritesheetGenerator.ts
├── docs/
│   ├── COPYING.md
│   ├── GRAPHICS.md
│   ├── GUIDELINES.md
│   ├── NAVIGATING.md
│   ├── SETUP.md
│   └── TRANSLATIONS.md
├── ecosystem.config.cjs
├── eslint.config.js
├── nodemon.json
├── package.json
├── scripts/
│   ├── add-file-paths.ts
│   ├── generate-translation-types.ts
│   ├── optimize-images.ts
│   ├── organize-imports.ts
│   └── readme.md
├── src/
│   ├── client/
│   │   ├── css/
│   │   │   ├── 404.css
│   │   │   ├── admin.css
│   │   │   ├── createaccount.css
│   │   │   ├── credits.css
│   │   │   ├── footer.css
│   │   │   ├── guide.css
│   │   │   ├── header.css
│   │   │   ├── icnvalidator.css
│   │   │   ├── index.css
│   │   │   ├── leaderboard.css
│   │   │   ├── login.css
│   │   │   ├── member.css
│   │   │   ├── news.css
│   │   │   ├── play.css
│   │   │   └── termsofservice.css
│   │   ├── img/
│   │   │   ├── badges/
│   │   │   │   ├── checkmate-badge-bronze.avif
│   │   │   │   ├── checkmate-badge-gold.avif
│   │   │   │   └── checkmate-badge-silver.avif
│   │   │   ├── blank_board.avif
│   │   │   ├── game/
│   │   │   │   └── guide/
│   │   │   │       ├── arrowindicators.avif
│   │   │   │       ├── fairy/
│   │   │   │       │   ├── amazon.avif
│   │   │   │       │   ├── archbishop.avif
│   │   │   │       │   ├── centaur.avif
│   │   │   │       │   ├── chancellor.avif
│   │   │   │       │   ├── guard.avif
│   │   │   │       │   ├── hawk.avif
│   │   │   │       │   ├── huygen.avif
│   │   │   │       │   ├── knightrider.avif
│   │   │   │       │   ├── obstacle.avif
│   │   │   │       │   ├── rose.avif
│   │   │   │       │   └── void.avif
│   │   │   │       ├── kingrookfork.avif
│   │   │   │       └── promotionlines.avif
│   │   │   ├── king_w.avif
│   │   │   ├── logo/
│   │   │   │   ├── dark-theme.avif
│   │   │   │   └── light-theme.avif
│   │   │   ├── member_default.avif
│   │   │   └── queen_w.avif
│   │   ├── scripts/
│   │   │   ├── cjs/
│   │   │   │   └── game/
│   │   │   │       └── htmlscript.ts
│   │   │   └── esm/
│   │   │       ├── audio/
│   │   │       │   ├── AudioEffects.ts
│   │   │       │   ├── AudioManager.ts
│   │   │       │   ├── AudioUtils.ts
│   │   │       │   ├── LFOFactory.ts
│   │   │       │   ├── SoundLayer.ts
│   │   │       │   ├── SoundscapePlayer.ts
│   │   │       │   └── processors/
│   │   │       │       ├── downsampler/
│   │   │       │       │   ├── DownsamplerNode.ts
│   │   │       │       │   └── DownsamplerProcessor.ts
│   │   │       │       └── worklet-types.ts
│   │   │       ├── chess/
│   │   │       │   └── rendering/
│   │   │       │       ├── checkerboardgenerator.ts
│   │   │       │       ├── imagecache.ts
│   │   │       │       ├── svgcache.ts
│   │   │       │       └── texturecache.ts
│   │   │       ├── components/
│   │   │       │   └── header/
│   │   │       │       ├── currpage-greyer.ts
│   │   │       │       ├── dropdowns/
│   │   │       │       │   ├── appearancedropdown.ts
│   │   │       │       │   ├── gameplaydropdown.ts
│   │   │       │       │   ├── languagedropdown.ts
│   │   │       │       │   ├── legalmovedropdown.ts
│   │   │       │       │   ├── perspectivedropdown.ts
│   │   │       │       │   └── sounddropdown.ts
│   │   │       │       ├── faviconselector.ts
│   │   │       │       ├── header.ts
│   │   │       │       ├── news-notification.ts
│   │   │       │       ├── pingmeter.ts
│   │   │       │       ├── preferences.ts
│   │   │       │       ├── settings.ts
│   │   │       │       └── spacing.ts
│   │   │       ├── game/
│   │   │       │   ├── GameBus.ts
│   │   │       │   ├── boardeditor/
│   │   │       │   │   ├── actions/
│   │   │       │   │   │   ├── eactions.ts
│   │   │       │   │   │   ├── eautosave.ts
│   │   │       │   │   │   ├── ecloud.ts
│   │   │       │   │   │   ├── editorSavesAPI.ts
│   │   │       │   │   │   └── esave.ts
│   │   │       │   │   ├── boardeditor.ts
│   │   │       │   │   ├── eclipboard.ts
│   │   │       │   │   ├── edithistory.ts
│   │   │       │   │   ├── editortypes.ts
│   │   │       │   │   ├── egamerules.ts
│   │   │       │   │   └── tools/
│   │   │       │   │       ├── drawingtool.ts
│   │   │       │   │       ├── etoolmanager.ts
│   │   │       │   │       ├── normaltool.ts
│   │   │       │   │       └── selection/
│   │   │       │   │           ├── scursor.ts
│   │   │       │   │           ├── sdrag.ts
│   │   │       │   │           ├── selectiontool.ts
│   │   │       │   │           ├── sfill.ts
│   │   │       │   │           ├── stoolgraphics.ts
│   │   │       │   │           └── stransformations.ts
│   │   │       │   ├── chess/
│   │   │       │   │   ├── checkmatepractice.ts
│   │   │       │   │   ├── clientmetadatautil.ts
│   │   │       │   │   ├── copygame.ts
│   │   │       │   │   ├── engines/
│   │   │       │   │   │   ├── engine.ts
│   │   │       │   │   │   ├── engineCheckmatePractice.ts
│   │   │       │   │   │   ├── enginecards/
│   │   │       │   │   │   │   └── hydrochess_card.ts
│   │   │       │   │   │   └── hydrochess.ts
│   │   │       │   │   ├── game.ts
│   │   │       │   │   ├── gamecompressor.ts
│   │   │       │   │   ├── gamecompressor.unit.test.ts
│   │   │       │   │   ├── gameformulator.ts
│   │   │       │   │   ├── gameloader.ts
│   │   │       │   │   ├── gameslot.ts
│   │   │       │   │   ├── graphicalchanges.ts
│   │   │       │   │   ├── movesequence.ts
│   │   │       │   │   ├── pastegame.ts
│   │   │       │   │   ├── premoves.ts
│   │   │       │   │   └── selection.ts
│   │   │       │   ├── config.ts
│   │   │       │   ├── gui/
│   │   │       │   │   ├── boardeditor/
│   │   │       │   │   │   ├── actions/
│   │   │       │   │   │   │   ├── guiclearposition.ts
│   │   │       │   │   │   │   ├── guigamerules.ts
│   │   │       │   │   │   │   ├── guiresetposition.ts
│   │   │       │   │   │   │   ├── guistartenginegame.ts
│   │   │       │   │   │   │   ├── guistartlocalgame.ts
│   │   │       │   │   │   │   └── loadposition/
│   │   │       │   │   │   │       ├── guiloadposition.ts
│   │   │       │   │   │   │       ├── guiloadpositionmodal.ts
│   │   │       │   │   │   │       └── guiloadpositionsavelist.ts
│   │   │       │   │   │   ├── guiboardeditor.ts
│   │   │       │   │   │   ├── guifloatingwindow.ts
│   │   │       │   │   │   ├── guipalette.ts
│   │   │       │   │   │   ├── guipositionheader.ts
│   │   │       │   │   │   └── guitoolbar.ts
│   │   │       │   │   ├── gui.ts
│   │   │       │   │   ├── guiclock.ts
│   │   │       │   │   ├── guidrawoffer.ts
│   │   │       │   │   ├── guigameinfo.ts
│   │   │       │   │   ├── guiloading.ts
│   │   │       │   │   ├── guinavigation.ts
│   │   │       │   │   ├── guipause.ts
│   │   │       │   │   ├── guiplay.ts
│   │   │       │   │   ├── guipractice.ts
│   │   │       │   │   ├── guipromotion.ts
│   │   │       │   │   ├── guititle.ts
│   │   │       │   │   ├── loadingscreen.ts
│   │   │       │   │   ├── stats.ts
│   │   │       │   │   ├── style.ts
│   │   │       │   │   └── toast.ts
│   │   │       │   ├── input.ts
│   │   │       │   ├── main.ts
│   │   │       │   ├── misc/
│   │   │       │   │   ├── controls.ts
│   │   │       │   │   ├── enginegame.ts
│   │   │       │   │   ├── gamesound.ts
│   │   │       │   │   ├── invites.ts
│   │   │       │   │   ├── keybinds.ts
│   │   │       │   │   ├── loadbalancer.ts
│   │   │       │   │   ├── onlinegame/
│   │   │       │   │   │   ├── afk.ts
│   │   │       │   │   │   ├── disconnect.ts
│   │   │       │   │   │   ├── drawoffers.ts
│   │   │       │   │   │   ├── movesendreceive.ts
│   │   │       │   │   │   ├── onlinegame.ts
│   │   │       │   │   │   ├── onlinegamerouter.ts
│   │   │       │   │   │   ├── resyncer.ts
│   │   │       │   │   │   └── tabnameflash.ts
│   │   │       │   │   └── space.ts
│   │   │       │   ├── rendering/
│   │   │       │   │   ├── ColorFlowRenderer.ts
│   │   │       │   │   ├── WaterRipples.ts
│   │   │       │   │   ├── animation.ts
│   │   │       │   │   ├── area.ts
│   │   │       │   │   ├── arrows/
│   │   │       │   │   │   ├── arrowlegalmovehighlights.ts
│   │   │       │   │   │   ├── arrows.ts
│   │   │       │   │   │   ├── arrowscalculator.ts
│   │   │       │   │   │   ├── arrowsgraphics.ts
│   │   │       │   │   │   └── arrowshifts.ts
│   │   │       │   │   ├── boarddrag.ts
│   │   │       │   │   ├── boardpos.ts
│   │   │       │   │   ├── boardtiles.ts
│   │   │       │   │   ├── border.ts
│   │   │       │   │   ├── camera.ts
│   │   │       │   │   ├── coordinates.ts
│   │   │       │   │   ├── dragging/
│   │   │       │   │   │   ├── draganimation.ts
│   │   │       │   │   │   ├── dragarrows.ts
│   │   │       │   │   │   └── droparrows.ts
│   │   │       │   │   ├── effect_zone/
│   │   │       │   │   │   ├── EffectZoneManager.ts
│   │   │       │   │   │   ├── soundscapes/
│   │   │       │   │   │   │   ├── IridescenceSoundscape.ts
│   │   │       │   │   │   │   └── UndercurrentSoundscape.ts
│   │   │       │   │   │   └── zones/
│   │   │       │   │   │       ├── AshfallVocsZone.ts
│   │   │       │   │   │       ├── ContortionFieldZone.ts
│   │   │       │   │   │       ├── DustyWastesZone.ts
│   │   │       │   │   │       ├── EchoRiftZone.ts
│   │   │       │   │   │       ├── EmberVergeZone.ts
│   │   │       │   │   │       ├── IridescenceZone.ts
│   │   │       │   │   │       ├── OceanZone.ts
│   │   │       │   │   │       ├── SpectralEdgeZone.ts
│   │   │       │   │   │       ├── StaticZone.ts
│   │   │       │   │   │       ├── TheBeginningZone.ts
│   │   │       │   │   │       └── UndercurrentZone.ts
│   │   │       │   │   ├── frameratelimiter.ts
│   │   │       │   │   ├── frametracker.ts
│   │   │       │   │   ├── gl-matrix.js
│   │   │       │   │   ├── highlights/
│   │   │       │   │   │   ├── annotations/
│   │   │       │   │   │   │   ├── annotations.ts
│   │   │       │   │   │   │   ├── drawarrows.ts
│   │   │       │   │   │   │   ├── drawrays.ts
│   │   │       │   │   │   │   └── drawsquares.ts
│   │   │       │   │   │   ├── checkhighlight.ts
│   │   │       │   │   │   ├── highlightline.ts
│   │   │       │   │   │   ├── highlights.ts
│   │   │       │   │   │   ├── legalmovehighlights.ts
│   │   │       │   │   │   ├── legalmovemodel.ts
│   │   │       │   │   │   ├── movehints.ts
│   │   │       │   │   │   ├── selectedpiecehighlightline.ts
│   │   │       │   │   │   ├── snapping.ts
│   │   │       │   │   │   ├── specialrighthighlights.ts
│   │   │       │   │   │   └── squarerendering.ts
│   │   │       │   │   ├── instancedshapes.ts
│   │   │       │   │   ├── meshes.ts
│   │   │       │   │   ├── miniimage.ts
│   │   │       │   │   ├── perspective.ts
│   │   │       │   │   ├── piecemodels.ts
│   │   │       │   │   ├── pieces.ts
│   │   │       │   │   ├── primitives.ts
│   │   │       │   │   ├── promotionlines.ts
│   │   │       │   │   ├── screenshake.ts
│   │   │       │   │   ├── starfield.ts
│   │   │       │   │   ├── text/
│   │   │       │   │   │   ├── glyphatlas.ts
│   │   │       │   │   │   └── textrenderer.ts
│   │   │       │   │   ├── transitions/
│   │   │       │   │   │   └── Transition.ts
│   │   │       │   │   └── webgl.ts
│   │   │       │   └── websocket/
│   │   │       │       ├── socketclose.ts
│   │   │       │       ├── socketman.ts
│   │   │       │       ├── socketmessages.ts
│   │   │       │       ├── socketrouter.ts
│   │   │       │       ├── socketschemas.ts
│   │   │       │       └── socketsubs.ts
│   │   │       ├── util/
│   │   │       │   ├── ImageLoader.ts
│   │   │       │   ├── IndexedDB.ts
│   │   │       │   ├── LocalStorage.ts
│   │   │       │   ├── PerlinNoise.ts
│   │   │       │   ├── compression.ts
│   │   │       │   ├── docutil.ts
│   │   │       │   ├── httputils.ts
│   │   │       │   ├── indexeddb.unit.test.ts
│   │   │       │   ├── mouse.ts
│   │   │       │   ├── pingManager.ts
│   │   │       │   ├── splines.ts
│   │   │       │   ├── svgtoimageconverter.ts
│   │   │       │   ├── thread.ts
│   │   │       │   ├── tooltips.ts
│   │   │       │   ├── usernamecontainer.ts
│   │   │       │   └── validatorama.ts
│   │   │       ├── views/
│   │   │       │   ├── admin.ts
│   │   │       │   ├── createaccount.ts
│   │   │       │   ├── guide.ts
│   │   │       │   ├── icnvalidator.ts
│   │   │       │   ├── index.ts
│   │   │       │   ├── leaderboard.ts
│   │   │       │   ├── login.ts
│   │   │       │   ├── member.ts
│   │   │       │   ├── news.ts
│   │   │       │   └── resetpassword.ts
│   │   │       ├── webgl/
│   │   │       │   ├── BufferUtil.ts
│   │   │       │   ├── ProgramManager.ts
│   │   │       │   ├── Renderable.ts
│   │   │       │   ├── ShaderProgram.ts
│   │   │       │   ├── TextureLoader.ts
│   │   │       │   ├── maskedDraw.ts
│   │   │       │   └── post_processing/
│   │   │       │       ├── PostProcessingPipeline.ts
│   │   │       │       └── passes/
│   │   │       │           ├── ColorGradePass.ts
│   │   │       │           ├── GlitchPass.ts
│   │   │       │           ├── HeatWavePass.ts
│   │   │       │           ├── PassThroughPass.ts
│   │   │       │           ├── SineWavePass.ts
│   │   │       │           ├── VignettePass.ts
│   │   │       │           ├── VoronoiDistortionPass.ts
│   │   │       │           ├── WaterPass.ts
│   │   │       │           └── WaterRipplePass.ts
│   │   │       └── workers/
│   │   │           └── icnvalidator.worker.ts
│   │   ├── shaders/
│   │   │   ├── arrow_images/
│   │   │   │   ├── fragment.glsl
│   │   │   │   └── vertex.glsl
│   │   │   ├── arrows/
│   │   │   │   └── vertex.glsl
│   │   │   ├── board_uber_shader/
│   │   │   │   ├── fragment.glsl
│   │   │   │   └── vertex.glsl
│   │   │   ├── color/
│   │   │   │   ├── fragment.glsl
│   │   │   │   ├── instanced/
│   │   │   │   │   └── vertex.glsl
│   │   │   │   └── vertex.glsl
│   │   │   ├── color_grade/
│   │   │   │   └── fragment.glsl
│   │   │   ├── color_texture/
│   │   │   │   ├── fragment.glsl
│   │   │   │   └── vertex.glsl
│   │   │   ├── fullscreen_colorflow/
│   │   │   │   └── fragment.glsl
│   │   │   ├── glitch/
│   │   │   │   └── fragment.glsl
│   │   │   ├── heat_wave/
│   │   │   │   └── fragment.glsl
│   │   │   ├── highlights/
│   │   │   │   └── vertex.glsl
│   │   │   ├── mini_images/
│   │   │   │   ├── fragment.glsl
│   │   │   │   └── vertex.glsl
│   │   │   ├── post_pass/
│   │   │   │   ├── fragment.glsl
│   │   │   │   └── vertex.glsl
│   │   │   ├── sine_wave/
│   │   │   │   └── fragment.glsl
│   │   │   ├── starfield/
│   │   │   │   └── vertex.glsl
│   │   │   ├── texture/
│   │   │   │   ├── fragment.glsl
│   │   │   │   ├── instanced/
│   │   │   │   │   └── vertex.glsl
│   │   │   │   └── vertex.glsl
│   │   │   ├── vignette/
│   │   │   │   └── fragment.glsl
│   │   │   ├── voronoi_distortion/
│   │   │   │   └── fragment.glsl
│   │   │   ├── water/
│   │   │   │   └── fragment.glsl
│   │   │   └── water_ripple/
│   │   │       └── fragment.glsl
│   │   ├── sounds/
│   │   │   └── spritesheet/
│   │   │       ├── note.txt
│   │   │       └── soundspritesheet.opus
│   │   └── views/
│   │       ├── admin.ejs
│   │       ├── components/
│   │       │   ├── footer.ejs
│   │       │   └── header.ejs
│   │       ├── createaccount.ejs
│   │       ├── credits.ejs
│   │       ├── errors/
│   │       │   ├── 400.ejs
│   │       │   ├── 401.ejs
│   │       │   ├── 404.ejs
│   │       │   ├── 409.ejs
│   │       │   └── 500.ejs
│   │       ├── guide.ejs
│   │       ├── icnvalidator.html
│   │       ├── index.ejs
│   │       ├── leaderboard.ejs
│   │       ├── login.ejs
│   │       ├── member.ejs
│   │       ├── news.ejs
│   │       ├── play.ejs
│   │       ├── resetpassword.ejs
│   │       └── termsofservice.ejs
│   ├── server/
│   │   ├── api/
│   │   │   ├── AdminPanel.ts
│   │   │   ├── EditorSavesAPI.int.test.ts
│   │   │   ├── EditorSavesAPI.ts
│   │   │   ├── GitHub.ts
│   │   │   ├── LeaderboardAPI.ts
│   │   │   ├── MemberAPI.ts
│   │   │   ├── NewsAPI.ts
│   │   │   ├── PracticeProgress.int.test.ts
│   │   │   ├── PracticeProgress.ts
│   │   │   ├── Prefs.int.test.ts
│   │   │   └── Prefs.ts
│   │   ├── app.ts
│   │   ├── config/
│   │   │   ├── certOptions.ts
│   │   │   ├── dateLocales.ts
│   │   │   ├── generateCert.ts
│   │   │   ├── i18n.ts
│   │   │   ├── paths.ts
│   │   │   ├── setupDev.ts
│   │   │   └── translationLoader.ts
│   │   ├── controllers/
│   │   │   ├── authController.ts
│   │   │   ├── authRatelimiter.ts
│   │   │   ├── authenticationTokens/
│   │   │   │   ├── accessTokenIssuer.ts
│   │   │   │   ├── sessionManager.ts
│   │   │   │   ├── tokenSigner.ts
│   │   │   │   └── tokenValidator.ts
│   │   │   ├── awsWebhook.ts
│   │   │   ├── browserIDManager.ts
│   │   │   ├── createAccountController.ts
│   │   │   ├── createAccountController.unit.test.ts
│   │   │   ├── deleteAccountController.ts
│   │   │   ├── deployController.ts
│   │   │   ├── emailController.ts
│   │   │   ├── loginController.int.test.ts
│   │   │   ├── loginController.ts
│   │   │   ├── logoutController.ts
│   │   │   ├── passwordResetController.ts
│   │   │   ├── roles.ts
│   │   │   └── verifyAccountController.ts
│   │   ├── database/
│   │   │   ├── backupManager.ts
│   │   │   ├── blacklistManager.ts
│   │   │   ├── cleanupTasks.ts
│   │   │   ├── database.ts
│   │   │   ├── databaseTables.ts
│   │   │   ├── editorSavesManager.ts
│   │   │   ├── gamesManager.ts
│   │   │   ├── leaderboardsManager.ts
│   │   │   ├── liveGamesManager.ts
│   │   │   ├── livePlayerGamesManager.ts
│   │   │   ├── memberManager.ts
│   │   │   ├── playerGamesManager.ts
│   │   │   ├── ratingAbuseManager.ts
│   │   │   └── refreshTokenManager.ts
│   │   ├── game/
│   │   │   ├── gamemanager/
│   │   │   │   ├── abortresigngame.ts
│   │   │   │   ├── activeplayers.ts
│   │   │   │   ├── afkdisconnect.ts
│   │   │   │   ├── cheatreport.ts
│   │   │   │   ├── drawoffers.ts
│   │   │   │   ├── gamecount.ts
│   │   │   │   ├── gamelogger.ts
│   │   │   │   ├── gamemanager.ts
│   │   │   │   ├── gamerouter.ts
│   │   │   │   ├── gameutility.ts
│   │   │   │   ├── joingame.ts
│   │   │   │   ├── liveGameRestore.ts
│   │   │   │   ├── liveGameValues.ts
│   │   │   │   ├── movesubmission.ts
│   │   │   │   ├── onAFK.ts
│   │   │   │   ├── onOfferDraw.ts
│   │   │   │   ├── pastereport.ts
│   │   │   │   ├── ratingabuse.ts
│   │   │   │   ├── ratingcalculation.ts
│   │   │   │   └── resync.ts
│   │   │   ├── invitesmanager/
│   │   │   │   ├── acceptinvite.ts
│   │   │   │   ├── cancelinvite.ts
│   │   │   │   ├── createinvite.ts
│   │   │   │   ├── invitesmanager.ts
│   │   │   │   ├── invitesrouter.ts
│   │   │   │   ├── invitessubscribers.ts
│   │   │   │   └── inviteutility.ts
│   │   │   ├── servermetadatautil.ts
│   │   │   ├── statlogger.ts
│   │   │   └── timecontrol.ts
│   │   ├── middleware/
│   │   │   ├── banned.ts
│   │   │   ├── errorHandler.ts
│   │   │   ├── logEvents.ts
│   │   │   ├── middleware.ts
│   │   │   ├── rateLimit.ts
│   │   │   ├── rateLimiters.ts
│   │   │   ├── secureRedirect.ts
│   │   │   ├── send404.ts
│   │   │   └── verifyJWT.ts
│   │   ├── routes/
│   │   │   └── root.ts
│   │   ├── server.ts
│   │   ├── socket/
│   │   │   ├── closeSocket.ts
│   │   │   ├── echoTracker.ts
│   │   │   ├── generalrouter.ts
│   │   │   ├── openSocket.ts
│   │   │   ├── receiveSocketMessage.ts
│   │   │   ├── sendSocketMessage.ts
│   │   │   ├── socketManager.ts
│   │   │   ├── socketRouter.ts
│   │   │   ├── socketServer.ts
│   │   │   └── socketUtility.ts
│   │   ├── types.ts
│   │   └── utility/
│   │       ├── IP.ts
│   │       ├── errorGuard.ts
│   │       ├── generateDependancyGraph.ts
│   │       ├── lockFile.ts
│   │       ├── mailer.ts
│   │       ├── newsUtil.ts
│   │       ├── startupLogger.ts
│   │       ├── translate.ts
│   │       ├── urlUtils.ts
│   │       └── zodlogger.ts
│   ├── shared/
│   │   ├── chess/
│   │   │   ├── logic/
│   │   │   │   ├── boardchanges.ts
│   │   │   │   ├── checkdetection.ts
│   │   │   │   ├── checkmate.ts
│   │   │   │   ├── checkresolver.ts
│   │   │   │   ├── clock.ts
│   │   │   │   ├── fourdimensionalmoves.ts
│   │   │   │   ├── gamefile.ts
│   │   │   │   ├── icn/
│   │   │   │   │   ├── icncommentutils.ts
│   │   │   │   │   └── icnconverter.ts
│   │   │   │   ├── initvariant.ts
│   │   │   │   ├── insufficientmaterial.ts
│   │   │   │   ├── legalmoves.ts
│   │   │   │   ├── movepiece.ts
│   │   │   │   ├── movesets.ts
│   │   │   │   ├── movevalidation.ts
│   │   │   │   ├── organizedpieces.ts
│   │   │   │   ├── repetition.ts
│   │   │   │   ├── specialdetect.ts
│   │   │   │   ├── specialmove.ts
│   │   │   │   ├── state.ts
│   │   │   │   └── wincondition.ts
│   │   │   ├── util/
│   │   │   │   ├── bdcoords.ts
│   │   │   │   ├── boardutil.ts
│   │   │   │   ├── clockutil.ts
│   │   │   │   ├── coordutil.ts
│   │   │   │   ├── gamefileutility.ts
│   │   │   │   ├── gamerules.ts
│   │   │   │   ├── metadatautil.ts
│   │   │   │   ├── moveutil.ts
│   │   │   │   ├── typeutil.ts
│   │   │   │   ├── validcheckmates.ts
│   │   │   │   └── winconutil.ts
│   │   │   └── variants/
│   │   │       ├── fourdimensionalgenerator.ts
│   │   │       ├── omega3generator.ts
│   │   │       ├── omega4generator.ts
│   │   │       ├── servervalidation.ts
│   │   │       ├── validleaderboard.ts
│   │   │       ├── variant.ts
│   │   │       └── variantdictionary.ts
│   │   ├── components/
│   │   │   └── header/
│   │   │       ├── pieceThemes.ts
│   │   │       └── themes.ts
│   │   ├── game_version.ts
│   │   ├── types.ts
│   │   └── util/
│   │       ├── EventBus.ts
│   │       ├── editorutil.ts
│   │       ├── isprime.ts
│   │       ├── jsutil.ts
│   │       ├── math/
│   │       │   ├── bimath.ts
│   │       │   ├── bounds.ts
│   │       │   ├── geometry.ts
│   │       │   ├── math.ts
│   │       │   └── vectors.ts
│   │       ├── timeutil.ts
│   │       ├── tokenConfig.ts
│   │       ├── uuid.ts
│   │       ├── validators.ts
│   │       └── wsutil.ts
│   ├── tests/
│   │   ├── integrationUtils.ts
│   │   ├── testRequest.ts
│   │   └── tests-setup.ts
│   └── types/
│       ├── globals.d.ts
│       ├── shaders.d.ts
│       └── translations.ts
├── translation/
│   ├── changes.json
│   ├── de-DE.toml
│   ├── el-GR.toml
│   ├── en-US.toml
│   ├── es-ES.toml
│   ├── fi-FI.toml
│   ├── fr-FR.toml
│   ├── news/
│   │   ├── en-US/
│   │   │   ├── 2024-01-29.md
│   │   │   ├── 2024-05-14.md
│   │   │   ├── 2024-05-24.md
│   │   │   ├── 2024-05-27.md
│   │   │   ├── 2024-07-09.md
│   │   │   ├── 2024-07-13.md
│   │   │   ├── 2024-07-22.md
│   │   │   ├── 2024-08-01.md
│   │   │   ├── 2024-09-11.md
│   │   │   ├── 2024-11-22.md
│   │   │   ├── 2025-03-12.md
│   │   │   ├── 2025-03-17.md
│   │   │   ├── 2025-05-21.md
│   │   │   ├── 2025-06-16.md
│   │   │   ├── 2025-11-28.md
│   │   │   ├── 2026-01-08.md
│   │   │   ├── 2026-03-09.md
│   │   │   └── 2026-04-24.md
│   │   ├── es-ES/
│   │   │   ├── 2024-01-29.md
│   │   │   ├── 2024-05-14.md
│   │   │   ├── 2024-05-24.md
│   │   │   ├── 2024-05-27.md
│   │   │   ├── 2024-07-09.md
│   │   │   ├── 2024-07-13.md
│   │   │   ├── 2024-07-22.md
│   │   │   ├── 2024-08-01.md
│   │   │   ├── 2024-09-11.md
│   │   │   ├── 2024-11-22.md
│   │   │   ├── 2025-03-12.md
│   │   │   ├── 2025-03-17.md
│   │   │   ├── 2025-05-21.md
│   │   │   ├── 2025-06-16.md
│   │   │   └── 2025-11-28.md
│   │   ├── fi-FI/
│   │   │   ├── 2026-01-08.md
│   │   │   ├── 2026-03-09.md
│   │   │   └── 2026-04-24.md
│   │   ├── fr-FR/
│   │   │   ├── 2024-01-29.md
│   │   │   ├── 2024-05-14.md
│   │   │   ├── 2024-05-24.md
│   │   │   ├── 2024-05-27.md
│   │   │   ├── 2024-07-09.md
│   │   │   ├── 2024-07-13.md
│   │   │   ├── 2024-07-22.md
│   │   │   ├── 2024-08-01.md
│   │   │   ├── 2024-09-11.md
│   │   │   ├── 2024-11-22.md
│   │   │   ├── 2025-03-12.md
│   │   │   ├── 2025-03-17.md
│   │   │   ├── 2025-05-21.md
│   │   │   ├── 2025-06-16.md
│   │   │   ├── 2025-11-28.md
│   │   │   ├── 2026-01-08.md
│   │   │   └── 2026-03-09.md
│   │   ├── pl-PL/
│   │   │   ├── 2024-01-29.md
│   │   │   ├── 2024-05-14.md
│   │   │   ├── 2024-05-24.md
│   │   │   ├── 2024-05-27.md
│   │   │   ├── 2024-07-09.md
│   │   │   ├── 2024-07-13.md
│   │   │   ├── 2024-07-22.md
│   │   │   ├── 2024-08-01.md
│   │   │   └── 2024-09-11.md
│   │   ├── pt-BR/
│   │   │   ├── 2024-01-29.md
│   │   │   ├── 2024-05-14.md
│   │   │   ├── 2024-05-24.md
│   │   │   ├── 2024-05-27.md
│   │   │   ├── 2024-07-09.md
│   │   │   ├── 2024-07-13.md
│   │   │   ├── 2024-07-22.md
│   │   │   ├── 2024-08-01.md
│   │   │   └── 2024-09-11.md
│   │   ├── zh-CN/
│   │   │   ├── 2024-01-29.md
│   │   │   ├── 2024-05-14.md
│   │   │   ├── 2024-05-24.md
│   │   │   ├── 2024-05-27.md
│   │   │   ├── 2024-07-09.md
│   │   │   ├── 2024-07-13.md
│   │   │   ├── 2024-07-22.md
│   │   │   ├── 2024-08-01.md
│   │   │   └── 2024-09-11.md
│   │   └── zh-TW/
│   │       ├── 2024-01-29.md
│   │       ├── 2024-05-14.md
│   │       ├── 2024-05-24.md
│   │       ├── 2024-05-27.md
│   │       ├── 2024-07-09.md
│   │       ├── 2024-07-13.md
│   │       ├── 2024-07-22.md
│   │       ├── 2024-08-01.md
│   │       └── 2024-09-11.md
│   ├── pl-PL.toml
│   ├── pt-BR.toml
│   ├── ru-RU.toml
│   ├── zh-CN.toml
│   └── zh-TW.toml
├── tsconfig.json
└── vitest.config.ts

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/copilot-instructions.md
================================================
# Copilot Instructions for infinitechess.org

### ABOVE ALL: Follow the requirements and guidelines for pull requests found in `docs/GUIDELINES.md`!

Each non-local session requires installing dependancies via `npm i --silent`. Check the working directory: if it contains Users, it's local; if it contains /home/runner/ or /github/, it's a GitHub Actions runner.

BEFORE commiting any new changes, and before responding to review feedback, always ensure all workflow checks pass: `npm run lint --silent`, `npx tsc --noEmit`, and `npm test`. You must repeat each of these commands, even if you only made a minor code change since your last check to fix one of their errors.

## Key Guidelines

1. Follow industry standards and best code practices of today.
2. Maintain existing code structure, organization, and consistency.
3. Perform testing for new complex functions to ensure their output is as expected.
4. Actual unit/integration tests are not required, unless explicitly asked for.
5. Remember before committing changes, that all pull requests must follow the guidelines in `docs/GUIDELINES.md`.
6. No types should ever be re-exported inside scripts. All imports of a type should reference the source.

## Project Architecture

- **Frontend:** TS, CSS, and assets in `src/client`. No major frameworks detected; uses vanilla and modular scripts.
- **Backend:** Node.js server in `src/server/server.js`, with API, game logic, and socket communication.

## Key Files & Directories

- `src/client/` — Frontend code
- `src/server/` — Backend code
- `src/shared/` — Shared utilities and chess logic
- `dev-utils/` — Depricated code. Do not maintain. It is not imported by the source code.
- `translation/` — Localization

## Conventions & Patterns

- **Translations:** TOML files in `translation/` for i18n. News per locale in `translation/news/`. Any modification to the en-US.toml requires you update the version number at the top of the file, and reflect the change in `translation/changes.json`. Change notes in `changes.json` should be clear and concise, not containing more information than necessary, and always indicate the line numbers of the removed/added keys.
- **UI Changes:** When asked to make UI changes, please verify the changes look good via the integrated browser.
- **Rendering:** When asked to add new graphics and visuals to the game (canvas), refer to the Graphics Rendering Guide in `docs/GRAPHICS.md`.
- When determining which imports can safely be removed, the command `npm run lint --silent` automatically tells you what imports are unused.

## Integration Points

- **Database:** Uses SQLite via the `better-sqlite3` package.
- **Socket Communication:** Real-time features via `src/server/socket/`.

## VS Code Tool Notes

- **Rename Symbol:** To rename a symbol across all files that import it, point the rename symbol tool at the symbol's name inside a named `export { }` or `export type { }` block — this works for named exports only; `export default { }` object-style exports require manual renaming of all external call sites regardless of where the rename is applied.

## Integrated Browser

- **Game interaction:** The infinite chess game board & pieces are on a canvas, which contents is only visible to you in screenshots. drag_element won't work on the canvas as it requires a DOM ref. Use run_playwright_code to probe board coordinates: hover page.mouse.move(sx, sy) at candidate screen positions and read await page.locator('#x').inputValue() / await page.locator('#y').inputValue() to map screen pixels to board squares.

- **Moving pieces:** Use explicit mouse.down()+mouse.up() pairs, not page.mouse.click() — the game's input loop polls isKeyDown per frame and click() is too fast. After clicking "Start Game" to start a local game, wait at least 2000ms before making any moves — the canvas game loop needs time to initialize.

- **Reading the board position:** press Digit5 (hold down for ~200ms so the game loop detects it) to trigger a clipboard copy of the ICN position string. Intercept it via: (1) inject `window._capturedClipboard=null; const orig=navigator.clipboard.writeText.bind(navigator.clipboard); navigator.clipboard.writeText=async(t)=>{window._capturedClipboard=t;navigator.clipboard.writeText=orig;return orig(t);}` into the page before pressing the key, then (2) `await page.keyboard.down('Digit5'); await page.waitForTimeout(200); await page.keyboard.up('Digit5');`, then (3) read `await page.evaluate(()=>window._capturedClipboard)`. navigator.clipboard.readText() will fail with permission denied — do not use it.


================================================
FILE: .github/pull_request_template.md
================================================
### Type of Change (new feature, quality of life, bug fix, refactor, tooling, chore, tests, translation, or documentation):

### Scope (what part of the website or game does it apply to):

### Details of what it does (if it makes css style changes, include screenshots of the before & after):


================================================
FILE: .github/workflows/ci.yml
================================================
name: CI

on:
  push:
    branches:
      - main
      - prod
      - distance-display
    paths-ignore:
      - '*.md'
      - 'docs/**'
      - 'LICENSE'
  pull_request:
    paths-ignore:
      - '*.md'
      - 'docs/**'
      - 'LICENSE'

concurrency:
  group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
  cancel-in-progress: true

jobs:
  lint:
    name: Lint & Format
    runs-on: ubuntu-latest
    timeout-minutes: 10

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Check formatting
        run: npm run format:check

      - name: Check linter rules
        run: npm run lint

  type-check:
    name: Type Check
    runs-on: ubuntu-latest
    timeout-minutes: 10

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Run TypeScript compiler
        run: npx tsc --noEmit

  test:
    name: Tests
    runs-on: ubuntu-latest
    timeout-minutes: 15

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Run tests
        run: npm test

  build:
    name: Build
    runs-on: ubuntu-latest
    timeout-minutes: 15

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Run build
        run: npm run build

      - name: Upload build artifact
        uses: actions/upload-artifact@v4
        with:
          name: dist
          path: dist/
          retention-days: 1

  smoke-test:
    name: Server Smoke Test
    runs-on: ubuntu-latest
    timeout-minutes: 10
    needs: build

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Download build artifact
        uses: actions/download-artifact@v4
        with:
          name: dist
          path: dist/

      - name: Smoke test server startup
        run: |
          node dist/server/server.js > server.log 2>&1 &
          SERVER_PID=$!
          sleep 5

          if ! kill -0 $SERVER_PID 2>/dev/null; then
            echo "Server process exited prematurely. Log output:"
            cat server.log
            exit 1
          fi

          response=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:$HTTPPORT_LOCAL/)
          kill $SERVER_PID 2>/dev/null || true
          wait $SERVER_PID 2>/dev/null || true

          if [ "$response" = "000" ]; then
            echo "Health check failed (no response). Log output:"
            cat server.log
            exit 1
          fi
          echo "Server is healthy."
        env:
          NODE_ENV: development
          HTTPPORT_LOCAL: 3000
          ACCESS_TOKEN_SECRET: ci-smoke-test-placeholder
          REFRESH_TOKEN_SECRET: ci-smoke-test-placeholder


================================================
FILE: .github/workflows/deploy.yml
================================================
name: Deploy

on:
  push:
    branches:
      - prod
    paths-ignore:
      - '*.md'
      - 'docs/**'
      - 'LICENSE'
  workflow_dispatch:

# Only one deploy may run at a time. If a second is triggered while one is in
# progress, it waits in the queue (cancel-in-progress: false) rather than
# being dropped — so no deploy is ever silently skipped.
# Deploys are rare anyway, only ever happening when code is merged into `prod`
# or triggered manually / from HydroChess via workflow_dispatch.
concurrency:
  group: deploy
  cancel-in-progress: false

# The deploy job only runs shell commands on the self-hosted machine and makes
# no calls to the GitHub API, so no token permissions are needed.
permissions: {}

jobs:
  deploy:
    name: Deploy to Production
    runs-on: self-hosted
    timeout-minutes: 15

    steps:
      # Step 1 — Pre-deploy DB backup.
      # Calls the live server over loopback. The server validates the secret,
      # performs a SQLite backup, and returns 200 on success.
      # The runner aborts the deploy if the backup fails.
      # Skipped for manual and API-triggered deploys.
      - name: Pre-deploy DB backup
        if: github.event_name == 'push'
        run: |
          response=$(curl -sk -o /dev/null -w "%{http_code}" \
            -X POST "https://localhost:${{ vars.HTTPSPORT }}/api/prepare-restart" \
            -H "X-Restart-Secret: ${{ secrets.RESTART_SECRET }}")
          if [ "$response" != "200" ]; then
            echo "Pre-deploy backup failed (HTTP $response). Aborting deploy."
            exit 1
          fi
          echo "Pre-deploy backup succeeded."

      # Step 2 — Pull and install.
      # Skipped for manual and API-triggered deploys.
      - name: Pull latest code and install dependencies
        if: github.event_name == 'push'
        working-directory: ${{ secrets.DEPLOY_DIR }}
        run: git pull && npm ci --silent

      # Step 3 — Build.
      # Always runs. For HydroChess-triggered deploys this re-runs esbuild so
      # the new WASM binaries (fetched at build time) are bundled into the output.
      - name: Build
        working-directory: ${{ secrets.DEPLOY_DIR }}
        run: npm run build

      # Step 4 — Reload.
      # pm2 reload sends SIGINT to the current process, waits for it to exit
      # gracefully, then starts a new process from the freshly built files.
      # Clients whose WebSockets drop reconnect automatically within ~2.5s.
      - name: Reload server
        working-directory: ${{ secrets.DEPLOY_DIR }}
        run: pm2 reload infinitechess

      # Step 5 — Health check.
      # Waits 5 seconds for the new process to start, then hits the homepage
      # over HTTPS. -k skips hostname verification (cert is issued for the
      # domain, not localhost). Only HTTP 200 is treated as healthy.
      - name: Health check
        run: |
          sleep 5
          response=$(curl -sk -o /dev/null -w "%{http_code}" \
            "https://localhost:${{ vars.HTTPSPORT }}/")
          if [ "$response" != "200" ]; then
            echo "Health check failed (HTTP $response)."
            exit 1
          fi
          echo "Server is healthy."


================================================
FILE: .gitignore
================================================
# .gitignore

.env
cert
logs
dist
# Old json data storage "database"
/database

# SQLite Database Files
*.db
*.sqlite
*.sqlite3

# SQLite Journal Files
*.db-journal
*.sqlite-journal
*.sqlite3-journal
# SQLite WAL mode sidecar files (created when journal_mode = WAL is enabled)
*.db-wal
*.db-shm
*.sqlite-wal
*.sqlite-shm
*.sqlite3-wal
*.sqlite3-shm

# DB backups directory
backups/

# Visual Studio automatically generated directories
node_modules
.vs
# VSCode automatically generated directory
.vscode

# JetBrains IDEs
.idea/

# PNPM
pnpm-lock.yaml

# Hidden mac file storing attributes of the directory
.DS_Store

# Speckit - FirePlank
.specify
specs
.agent

# HydroChess Engine build output
src/client/pkg/

================================================
FILE: .husky/pre-commit
================================================
npx lint-staged


================================================
FILE: .prettierignore
================================================
# .prettierignore

dist/
dev-utils/
src/client/pkg/
*.ejs

================================================
FILE: .prettierrc.json
================================================
{
  "semi": true,
  "useTabs": true,
  "tabWidth": 4,
  "singleQuote": true,
  "bracketSpacing": true,
  "trailingComma": "all",
  "arrowParens": "always",
  "printWidth": 100,
  "endOfLine": "lf",
  "overrides": [
    {
      "files": ["*.yml", "*.json"],
      "options": {
        "tabWidth": 2,
        "useTabs": false
      }
    }
  ]
}


================================================
FILE: CLAUDE.md
================================================
# Claude Instructions for infinitechess.org

### ABOVE ALL: Follow the requirements and guidelines for pull requests found in `docs/GUIDELINES.md`!

When you finish making any new changes, always ensure all workflow checks pass: `npm run lint --silent`, `npx tsc --noEmit`, and `npm test`. You must repeat each of these commands, even if you only made a minor code change since your last check to fix one of their errors.

## Key Guidelines

1. Follow industry standards and best code practices of today.
2. Maintain existing code structure, organization, and consistency.
3. Perform testing for new complex functions to ensure their output is as expected.
4. Actual unit/integration tests are not required, unless explicitly asked for.
5. Remember before committing changes, that all pull requests must follow the guidelines in `docs/GUIDELINES.md`.
6. No types should ever be re-exported inside scripts. All imports of a type should reference the source.

## Project Architecture

- **Frontend:** TS, CSS, and assets in `src/client`. No major frameworks detected; uses vanilla and modular scripts.
- **Backend:** Node.js server in `src/server/server.js`, with API, game logic, and socket communication.

## Key Files & Directories

- `src/client/` — Frontend code
- `src/server/` — Backend code
- `src/shared/` — Shared utilities and chess logic
- `dev-utils/` — Depricated code. Do not maintain. It is not imported by the source code.
- `translation/` — Localization

## Conventions & Patterns

- **Translations:** TOML files in `translation/` for i18n. News per locale in `translation/news/`. Any modification to the en-US.toml requires you update the version number at the top of the file, and reflect the change in `translation/changes.json`. Change notes in `changes.json` should be clear and concise, not containing more information than necessary, and always indicate the line numbers of the removed/added keys.
- **Rendering:** When asked to add new graphics and visuals to the game (canvas), refer to the Graphics Rendering Guide in `docs/GRAPHICS.md`.
- When determining which imports can safely be removed, the command `npm run lint --silent` automatically tells you what imports are unused.

## Integration Points

- **Database:** Uses SQLite via the `better-sqlite3` package.
- **Socket Communication:** Real-time features via `src/server/socket/`.


================================================
FILE: LICENSE
================================================
                    GNU AFFERO GENERAL PUBLIC LICENSE
                       Version 3, 19 November 2007

 Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
 Everyone is permitted to copy and distribute verbatim copies
 of this license document, but changing it is not allowed.

                            Preamble

  The GNU Affero General Public License is a free, copyleft license for
software and other kinds of works, specifically designed to ensure
cooperation with the community in the case of network server software.

  The licenses for most software and other practical works are designed
to take away your freedom to share and change the works.  By contrast,
our General Public Licenses are intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users.

  When we speak of free software, we are referring to freedom, not
price.  Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.

  Developers that use our General Public Licenses protect your rights
with two steps: (1) assert copyright on the software, and (2) offer
you this License which gives you legal permission to copy, distribute
and/or modify the software.

  A secondary benefit of defending all users' freedom is that
improvements made in alternate versions of the program, if they
receive widespread use, become available for other developers to
incorporate.  Many developers of free software are heartened and
encouraged by the resulting cooperation.  However, in the case of
software used on network servers, this result may fail to come about.
The GNU General Public License permits making a modified version and
letting the public access it on a server without ever releasing its
source code to the public.

  The GNU Affero General Public License is designed specifically to
ensure that, in such cases, the modified source code becomes available
to the community.  It requires the operator of a network server to
provide the source code of the modified version running there to the
users of that server.  Therefore, public use of a modified version, on
a publicly accessible server, gives the public access to the source
code of the modified version.

  An older license, called the Affero General Public License and
published by Affero, was designed to accomplish similar goals.  This is
a different license, not a version of the Affero GPL, but Affero has
released a new version of the Affero GPL which permits relicensing under
this license.

  The precise terms and conditions for copying, distribution and
modification follow.

                       TERMS AND CONDITIONS

  0. Definitions.

  "This License" refers to version 3 of the GNU Affero General Public License.

  "Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.

  "The Program" refers to any copyrightable work licensed under this
License.  Each licensee is addressed as "you".  "Licensees" and
"recipients" may be individuals or organizations.

  To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy.  The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.

  A "covered work" means either the unmodified Program or a work based
on the Program.

  To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy.  Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.

  To "convey" a work means any kind of propagation that enables other
parties to make or receive copies.  Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.

  An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License.  If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.

  1. Source Code.

  The "source code" for a work means the preferred form of the work
for making modifications to it.  "Object code" means any non-source
form of a work.

  A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.

  The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form.  A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.

  The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities.  However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work.  For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.

  The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.

  The Corresponding Source for a work in source code form is that
same work.

  2. Basic Permissions.

  All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met.  This License explicitly affirms your unlimited
permission to run the unmodified Program.  The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work.  This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.

  You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force.  You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright.  Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.

  Conveying under any other circumstances is permitted solely under
the conditions stated below.  Sublicensing is not allowed; section 10
makes it unnecessary.

  3. Protecting Users' Legal Rights From Anti-Circumvention Law.

  No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.

  When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.

  4. Conveying Verbatim Copies.

  You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.

  You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.

  5. Conveying Modified Source Versions.

  You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:

    a) The work must carry prominent notices stating that you modified
    it, and giving a relevant date.

    b) The work must carry prominent notices stating that it is
    released under this License and any conditions added under section
    7.  This requirement modifies the requirement in section 4 to
    "keep intact all notices".

    c) You must license the entire work, as a whole, under this
    License to anyone who comes into possession of a copy.  This
    License will therefore apply, along with any applicable section 7
    additional terms, to the whole of the work, and all its parts,
    regardless of how they are packaged.  This License gives no
    permission to license the work in any other way, but it does not
    invalidate such permission if you have separately received it.

    d) If the work has interactive user interfaces, each must display
    Appropriate Legal Notices; however, if the Program has interactive
    interfaces that do not display Appropriate Legal Notices, your
    work need not make them do so.

  A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit.  Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.

  6. Conveying Non-Source Forms.

  You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:

    a) Convey the object code in, or embodied in, a physical product
    (including a physical distribution medium), accompanied by the
    Corresponding Source fixed on a durable physical medium
    customarily used for software interchange.

    b) Convey the object code in, or embodied in, a physical product
    (including a physical distribution medium), accompanied by a
    written offer, valid for at least three years and valid for as
    long as you offer spare parts or customer support for that product
    model, to give anyone who possesses the object code either (1) a
    copy of the Corresponding Source for all the software in the
    product that is covered by this License, on a durable physical
    medium customarily used for software interchange, for a price no
    more than your reasonable cost of physically performing this
    conveying of source, or (2) access to copy the
    Corresponding Source from a network server at no charge.

    c) Convey individual copies of the object code with a copy of the
    written offer to provide the Corresponding Source.  This
    alternative is allowed only occasionally and noncommercially, and
    only if you received the object code with such an offer, in accord
    with subsection 6b.

    d) Convey the object code by offering access from a designated
    place (gratis or for a charge), and offer equivalent access to the
    Corresponding Source in the same way through the same place at no
    further charge.  You need not require recipients to copy the
    Corresponding Source along with the object code.  If the place to
    copy the object code is a network server, the Corresponding Source
    may be on a different server (operated by you or a third party)
    that supports equivalent copying facilities, provided you maintain
    clear directions next to the object code saying where to find the
    Corresponding Source.  Regardless of what server hosts the
    Corresponding Source, you remain obligated to ensure that it is
    available for as long as needed to satisfy these requirements.

    e) Convey the object code using peer-to-peer transmission, provided
    you inform other peers where the object code and Corresponding
    Source of the work are being offered to the general public at no
    charge under subsection 6d.

  A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.

  A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling.  In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage.  For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product.  A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.

  "Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source.  The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.

  If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information.  But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).

  The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed.  Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.

  Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.

  7. Additional Terms.

  "Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law.  If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.

  When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it.  (Additional permissions may be written to require their own
removal in certain cases when you modify the work.)  You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.

  Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:

    a) Disclaiming warranty or limiting liability differently from the
    terms of sections 15 and 16 of this License; or

    b) Requiring preservation of specified reasonable legal notices or
    author attributions in that material or in the Appropriate Legal
    Notices displayed by works containing it; or

    c) Prohibiting misrepresentation of the origin of that material, or
    requiring that modified versions of such material be marked in
    reasonable ways as different from the original version; or

    d) Limiting the use for publicity purposes of names of licensors or
    authors of the material; or

    e) Declining to grant rights under trademark law for use of some
    trade names, trademarks, or service marks; or

    f) Requiring indemnification of licensors and authors of that
    material by anyone who conveys the material (or modified versions of
    it) with contractual assumptions of liability to the recipient, for
    any liability that these contractual assumptions directly impose on
    those licensors and authors.

  All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10.  If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term.  If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.

  If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.

  Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.

  8. Termination.

  You may not propagate or modify a covered work except as expressly
provided under this License.  Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).

  However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.

  Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.

  Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License.  If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.

  9. Acceptance Not Required for Having Copies.

  You are not required to accept this License in order to receive or
run a copy of the Program.  Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance.  However,
nothing other than this License grants you permission to propagate or
modify any covered work.  These actions infringe copyright if you do
not accept this License.  Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.

  10. Automatic Licensing of Downstream Recipients.

  Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License.  You are not responsible
for enforcing compliance by third parties with this License.

  An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations.  If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.

  You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License.  For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.

  11. Patents.

  A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based.  The
work thus licensed is called the contributor's "contributor version".

  A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version.  For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.

  Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.

  In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement).  To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.

  If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients.  "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.

  If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.

  A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License.  You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.

  Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.

  12. No Surrender of Others' Freedom.

  If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License.  If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all.  For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.

  13. Remote Network Interaction; Use with the GNU General Public License.

  Notwithstanding any other provision of this License, if you modify the
Program, your modified version must prominently offer all users
interacting with it remotely through a computer network (if your version
supports such interaction) an opportunity to receive the Corresponding
Source of your version by providing access to the Corresponding Source
from a network server at no charge, through some standard or customary
means of facilitating copying of software.  This Corresponding Source
shall include the Corresponding Source for any work covered by version 3
of the GNU General Public License that is incorporated pursuant to the
following paragraph.

  Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU General Public License into a single
combined work, and to convey the resulting work.  The terms of this
License will continue to apply to the part which is the covered work,
but the work with which it is combined will remain governed by version
3 of the GNU General Public License.

  14. Revised Versions of this License.

  The Free Software Foundation may publish revised and/or new versions of
the GNU Affero General Public License from time to time.  Such new versions
will be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.

  Each version is given a distinguishing version number.  If the
Program specifies that a certain numbered version of the GNU Affero General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation.  If the Program does not specify a version number of the
GNU Affero General Public License, you may choose any version ever published
by the Free Software Foundation.

  If the Program specifies that a proxy can decide which future
versions of the GNU Affero General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.

  Later license versions may give you additional or different
permissions.  However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.

  15. Disclaimer of Warranty.

  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. Limitation of Liability.

  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.

  17. Interpretation of Sections 15 and 16.

  If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.

                     END OF TERMS AND CONDITIONS

            How to Apply These Terms to Your New Programs

  If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.

  To do so, attach the following notices to the program.  It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.

    <one line to give the program's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published
    by the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.

Also add information on how to contact you by electronic and paper mail.

  If your software can interact with users remotely through a computer
network, you should also make sure that it provides a way for users to
get its source.  For example, if your program is a web application, its
interface could display a "Source" link that leads users to an archive
of the code.  There are many ways you could offer source, and different
solutions will be better for different programs; see section 13 for the
specific requirements.

  You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU AGPL, see
<https://www.gnu.org/licenses/>.


================================================
FILE: README.md
================================================
# Infinite Chess Web Server

[InfiniteChess.org](https://www.infinitechess.org) is a free and ad-less website for playing several variants on an infinite, boundless board.

What began as an indie project by [Naviary](https://www.youtube.com/@Naviary) in 2022 has been growing since. What drives this project is the concept of Chess and Infinity intertwined! There shall be no more limits, only freedom! That being said, there are many interesting tactics that arise from the size of the board, not to mention lots of wild mathematics surrounding it, just check out a couple of the [YouTube videos](https://www.youtube.com/@Naviary)!

[Join us on Discord](https://discord.gg/NFWFGZeNh5) for more info, or just to chat about the game!

## Contributing

This project is open source! If you have skills in HTML, CSS, JavaScript, TypeScript, or Node.js, we welcome contributions!

| Document                                                                 | Description                               |
| ------------------------------------------------------------------------ | ----------------------------------------- |
| **[Setup Guide](./docs/SETUP.md)**                                       | Setup your contribution workflow          |
| **[Navigation Guide](./docs/NAVIGATING.md)**                             | Get an overview of the codebase           |
| **[Contributing Guide](./docs/GUIDELINES.md)**                           | PR requirements and guidelines            |
| **[Issues](https://github.com/Infinite-Chess/infinitechess.org/issues)** | Inquire available tasks                   |
| **[Translation Guide](./docs/TRANSLATIONS.md)**                          | Translate the website to another language |
| **[Graphics Guide](./docs/GRAPHICS.md)**                                 | Learn how the game renders graphics       |

## Roadmap

There are still MANY more items I have planned for this project. Just a few of them are:

- Modern website design
- Analysis Board
- Spectating
- Games with infinitely many pieces
- 4 Player
- Massive Multiplayer Online

Because I want Infinite Chess to grow to as big of an audience as possible, it's licensed with a goal of keeping this game free forever! Check out [Copying](./docs/COPYING.md) for more details.


================================================
FILE: build/client.ts
================================================
// build/client.ts

import fs from 'fs';
import swc from '@swc/core';
import path from 'node:path';
import { glob } from 'glob';
import { readFile } from 'node:fs/promises';
import browserslist from 'browserslist';
// @ts-ignore this package doesn't have a declaration file
import stripComments from 'glsl-strip-comments';
import { transform, browserslistToTargets } from 'lightningcss';
import esbuild, { BuildOptions, Plugin, PluginBuild } from 'esbuild';

import { getESBuildLogStatusLogger } from './plugins.js';

// ================================= CONSTANTS =================================

// Targetted browsers for CSS transpilation
// Format: https://github.com/browserslist/browserslist?tab=readme-ov-file#query-composition
const cssTargets = browserslistToTargets(browserslist('defaults'));

/**
 * Any ES Module that any HTML document IMPORTS directly!
 * ADD TO THIS when we create new modules that nothing else depends on!
 * ESBuild has to build each of them and their dependancies
 * into their own bundle!
 */
const ESMEntryPoints = [
	'src/client/scripts/esm/game/main.ts',
	'src/client/scripts/esm/audio/processors/downsampler/DownsamplerProcessor.ts',
	'src/client/scripts/esm/components/header/header.ts',
	'src/client/scripts/esm/views/index.ts',
	'src/client/scripts/esm/views/member.ts',
	'src/client/scripts/esm/views/leaderboard.ts',
	'src/client/scripts/esm/views/login.ts',
	'src/client/scripts/esm/views/news.ts',
	'src/client/scripts/esm/views/createaccount.ts',
	'src/client/scripts/esm/views/resetpassword.ts',
	'src/client/scripts/esm/views/guide.ts',
	'src/client/scripts/esm/views/admin.ts',
	'src/client/scripts/esm/views/icnvalidator.ts',
	'src/client/scripts/esm/game/chess/engines/engineCheckmatePractice.ts',
	'src/client/scripts/esm/game/chess/engines/hydrochess.ts',
	'src/client/scripts/esm/workers/icnvalidator.worker.ts',
];

/** CommonJS modules imported by html pages. */
const CJSEntryPoints = ['src/client/scripts/cjs/game/htmlscript.ts'];

// ================================= PLUGINS ===================================

const ESMBuildPlugin = getESBuildLogStatusLogger(
	'✅ Client ESM Build successful.',
	'❌ Client ESM Build failed.',
);

const CJSBuildPlugin = getESBuildLogStatusLogger(
	'✅ Client CJS Build successful.',
	'❌ Client CJS Build failed.',
);

/**
 * Returns an esbuild plugin that resolves a promise once the initial esbuild build is complete.
 * BuildContext.watch() resolves only once the watch mode is SET UP, not when the first build is DONE,
 * so this provides a promise that ONLY resolves once the first build is done.
 * @returns An object containing the esbuild plugin and the promise to await.
 */
function getInitialBuildPlugin(): { plugin: Plugin; initialBuild: Promise<void> } {
	let isFirstBuild = true;

	let resolve: () => void;
	const promise = new Promise<void>((r) => {
		resolve = r;
	});

	const plugin: Plugin = {
		name: 'initial-build-waiter',
		setup(build: PluginBuild) {
			// This hook runs when a build has finished
			build.onEnd(() => {
				if (!isFirstBuild) return;
				isFirstBuild = false;
				// Signal that the first build is done, even if
				// there was an error, so that watch mode can continue.
				resolve();
			});
		},
	};

	return { plugin, initialBuild: promise };
}

/** An esbuild plugin object that minifies GLSL shader files by stripping comments. */
const GLSLMinifyPlugin = {
	name: 'glsl-minify',
	setup(build: PluginBuild) {
		// Intercept .glsl files and minify them
		build.onLoad({ filter: /\.glsl$/ }, async (args) => {
			try {
				// Read the GLSL file
				const source = await readFile(args.path, 'utf8');
				// Strip comments from the GLSL source
				const minified = stripComments(source);
				// Return the minified content as text
				return {
					contents: minified,
					loader: 'text',
				};
			} catch (error: unknown) {
				return {
					errors: [
						{
							text: `Failed to minify GLSL file: ${error instanceof Error ? error.message : String(error)}`,
							location: { file: args.path },
						},
					],
				};
			}
		});
	},
};

const ESMBuildOptions: BuildOptions = {
	bundle: true,
	entryPoints: ESMEntryPoints,
	outdir: './dist/client/scripts/esm',
	/**
	 * Enable code splitting, which means if multiple entry points require the same module,
	 * that dependancy will be separated out of both of them which means it isn't duplicated,
	 * and there's only one instance of it per page.
	 * This also means more requests to the server, but not many.
	 * If this is false, multiple copies of the same code may be loaded onto a page,
	 * each belonging to a separate entry point module.
	 */
	splitting: true,
	format: 'esm',
	sourcemap: true, // Enables sourcemaps for debugging in the browser.
	// minify: true, // Enable minification. SWC is more compact so we don't use esbuild's
	loader: { '.wasm': 'file' },
};

const CJSBuildOptions: BuildOptions = {
	bundle: true,
	entryPoints: CJSEntryPoints,
	outdir: './dist/client/scripts/cjs',
	outbase: 'src/client/scripts/cjs', // Without this, htmlscript.js gets put in cjs/ instead of cjs/game/
	format: 'cjs',
	sourcemap: true,
};

// ================================= BUILDING ===================================

/** Builds the client's scripts and minifies css. */
export async function buildClient(isDev: boolean): Promise<void> {
	// console.log(`Building client in ${isDev ? 'DEVELOPMENT' : 'PRODUCTION'} mode...`);

	// Create signaling plugins to wait for the initial build in watch mode
	const { plugin: esmInitialBuildPlugin, initialBuild: esmInitialBuild } =
		getInitialBuildPlugin();
	const { plugin: cjsInitialBuildPlugin, initialBuild: cjsInitialBuild } =
		getInitialBuildPlugin();

	const ESMContext = await esbuild.context({
		...ESMBuildOptions,
		legalComments: isDev ? undefined : 'none', // Only strip copyright notices in production.
		plugins: [ESMBuildPlugin, GLSLMinifyPlugin, esmInitialBuildPlugin],
	});
	const CJSContext = await esbuild.context({
		...CJSBuildOptions,
		legalComments: isDev ? undefined : 'none', // Only strip copyright notices in production.
		plugins: [CJSBuildPlugin, GLSLMinifyPlugin, cjsInitialBuildPlugin],
	});

	if (isDev) {
		// Start watch mode. This kicks off the initial builds
		ESMContext.watch();
		CJSContext.watch();

		// Wait for both of the initial builds to complete
		await Promise.all([esmInitialBuild, cjsInitialBuild]);
	} else {
		/**
		 * ESBuild takes each entry point and all of their dependencies and merges them bundling them into one file.
		 * If multiple entry points share dependencies, then those dependencies will be split into separate modules,
		 * which means they aren't duplicated, and there's only one instance of it per page.
		 * This also means more requests to the server, but not many.
		 */
		// Production
		// Build once and exit since not in watch mode
		await ESMContext.rebuild();
		ESMContext.dispose();

		await CJSContext.rebuild();
		CJSContext.dispose();

		// Minify JS and CSS
		// console.log('Minifying production assets...');
		// Further minify them. This cuts off their size a further 60%!!!
		await minifyScriptDirectory(
			'./dist/client/scripts/cjs/',
			'./dist/client/scripts/cjs/',
			false,
		);
		await minifyScriptDirectory(
			'./dist/client/scripts/esm/',
			'./dist/client/scripts/esm/',
			true,
		);
		await minifyCSSFiles();
	}
}

/**
 * Minifies all JavaScript files in a directory and writes them to an output directory.
 * @param inputDir - The directory to scan for scripts.
 * @param outputDir - The directory where the minified files will be written.
 * @param module - True if the scripts are ES Modules instead of CommonJS.
 * @returns Resolves when all files are minified.
 */
async function minifyScriptDirectory(
	inputDir: string,
	outputDir: string,
	module: boolean,
): Promise<void> {
	const files = await glob('**/*.js', { cwd: inputDir, nodir: true });

	for (const file of files) {
		const inputFilePath = path.join(inputDir, file);
		const outputFilePath = path.join(outputDir, file);

		const content = await readFile(inputFilePath, 'utf-8');
		const minified = await swc.minify(content, {
			mangle: true, // Enable variable name mangling
			compress: true, // Enable compression
			sourceMap: false,
			module, // Include if we're minifying ES Modules instead of Common JS
		});

		// Write the minified file to the output directory
		fs.mkdirSync(path.dirname(outputFilePath), { recursive: true });
		fs.writeFileSync(outputFilePath, minified.code);
		// console.log(`Minified: ${outputFilePath}`);
	}
}

/**
 * Minifies all CSS files from src/client/css/ directory
 * to the distribution directory, preserving the original structure.
 * @returns Resolves when all CSS files are processed.
 */
async function minifyCSSFiles(): Promise<void> {
	// Bundle and compress all css files
	const cssFiles = await glob('**/*.css', { cwd: './dist/client/css', nodir: true });
	for (const file of cssFiles) {
		// Minify css files
		const outputFilePath = `./dist/client/css/${file}`;
		const { code } = transform({
			targets: cssTargets,
			code: Buffer.from(await readFile(outputFilePath, 'utf8')),
			minify: true,
			filename: path.basename(outputFilePath),
		});
		// Write into /dist
		fs.mkdirSync(path.dirname(outputFilePath), { recursive: true });
		fs.writeFileSync(outputFilePath, code);
	}
}


================================================
FILE: build/engine-wasm.ts
================================================
// build/engine-wasm.ts

/**
 * HydroChess WASM Engine Setup Script
 *
 * This ensures that the HydroChess WASM engine is available.
 */

import fs from 'node:fs';
import path from 'node:path';
import * as z from 'zod';

import { logZodError } from '../src/server/utility/zodlogger';

// Constants -------------------------------------------------------------------

/** Absolute path to the HydroChess WASM engine pkg directory */
const HYDROCHESS_WASM_DIR = path.join(process.cwd(), 'src', 'client', 'pkg', 'hydrochess');

/** API URL to check the latest released version */
const LATEST_RELEASE_API_URL =
	'https://api.github.com/repos/Infinite-Chess/hydrochess/releases/latest';

/** Zod schema for validating GitHub release API response */
const releaseDataSchema = z.object({
	tag_name: z.string(),
	assets: z.array(
		z.object({
			name: z.string(),
			browser_download_url: z.string(),
		}),
	),
});

// Functions -------------------------------------------------------------------

/**
 * Ensures the HydroChess WASM engine is available and up-to-date.
 * Automatically downloads the pre-built WASM if there is a new release.
 */
export async function setupEngineWasm(): Promise<void> {
	const label = '[hydrochess]';
	const pkgDir = path.join(HYDROCHESS_WASM_DIR, 'pkg');
	const wasmFile = path.join(pkgDir, 'hydrochess_wasm_bg.wasm');
	const jsFile = path.join(pkgDir, 'hydrochess_wasm.js');
	// Note: If you are manually rebuilding the engine binaries on a separate
	// vscode window with the hydrochess repo open, and have setup a symlink
	// for this submodule to point to that project, then this file will be innacurate.
	// But it works because the local build process thinks we're already on the latest version.
	const versionFile = path.join(pkgDir, '.engine-version');

	// Download pre-built binary if new version available
	let localVersion = '';
	if (fs.existsSync(versionFile)) {
		localVersion = fs.readFileSync(versionFile, 'utf-8').trim();
	}

	let releaseData: z.infer<typeof releaseDataSchema>;

	try {
		const response = await fetch(LATEST_RELEASE_API_URL, {
			headers: { 'User-Agent': 'Infinite-Chess-Build-Script' },
		});
		if (!response.ok) throw new Error(`GitHub API failed: ${response.statusText}`);

		const rawReleaseData = await response.json();
		const parseResult = releaseDataSchema.safeParse(rawReleaseData);
		if (!parseResult.success) {
			logZodError(
				rawReleaseData,
				parseResult.error,
				`${label} GitHub API returned invalid data.`,
			);
			throw new Error(`GitHub API returned invalid data: ${parseResult.error.message}`);
		}

		releaseData = parseResult.data;
	} catch (error: unknown) {
		console.warn(
			`${label} Could not check for new version:`,
			error instanceof Error ? error.message : String(error),
		);
		if (fs.existsSync(wasmFile)) {
			console.log(`${label} Using existing local version.`);
			return;
		}
		// If we can't check and have no local copy, fail and inform the user.
		console.error(`${label} Automatic download failed and no local copy exists.`);
		return;
	}

	const remoteVersion = releaseData.tag_name;

	if (
		localVersion &&
		localVersion === remoteVersion &&
		fs.existsSync(wasmFile) &&
		fs.existsSync(jsFile)
	) {
		console.log(`${label} Engine is up-to-date (${localVersion}).`);
		return;
	}

	console.log(`${label} New version detected (${remoteVersion}). Downloading release...`);

	// Extract dynamic download URLs from the API response
	const wasmAsset = releaseData.assets.find((a) => a.name === 'hydrochess_wasm_bg.wasm');
	const jsAsset = releaseData.assets.find((a) => a.name === 'hydrochess_wasm.js');

	if (!wasmAsset || !jsAsset) {
		console.error(`${label} Release ${remoteVersion} is missing required asset files.`);
		return;
	}

	try {
		await fs.promises.mkdir(pkgDir, { recursive: true });

		const downloadFile = async (url: string, dest: string): Promise<void> => {
			const response = await fetch(url);
			if (!response.ok) throw new Error(`Failed to download ${url}: ${response.statusText}`);
			const buffer = Buffer.from(await response.arrayBuffer());
			await fs.promises.writeFile(dest, buffer);
			console.log(`${label} Downloaded ${path.basename(dest)}`);
		};

		await Promise.all([
			downloadFile(wasmAsset.browser_download_url, wasmFile),
			downloadFile(jsAsset.browser_download_url, jsFile),
		]);

		// Stamp the downloaded version
		await fs.promises.writeFile(versionFile, remoteVersion);

		console.log(`${label} Hydrochess engine is ready (${remoteVersion}).`);
	} catch (error) {
		console.error(
			`${label} Automatic download failed:`,
			error instanceof Error ? error.message : String(error),
		);
	}
}


================================================
FILE: build/env.ts
================================================
// build/env.ts

/**
 * Ensures the .env file exists, generating it with default values if it doesn't.
 * And ensures its contents are valid.
 */

import fs from 'fs';
import crypto from 'crypto';
import dotenv from 'dotenv';

const envPath = '.env';

/** Ensure .env file exists and is valid. */
export function setupEnv(): void {
	ensureExists();
	ensureValid();
}

/** Ensure .env exists, generating it with default values if it doesn't. */
function ensureExists(): void {
	if (fs.existsSync(envPath)) return;

	// Doesn't exist, generate it with default values

	const ACCESS_TOKEN_SECRET = generateSecret(32); // 32 bytes = 64 characters in hex
	const REFRESH_TOKEN_SECRET = generateSecret(32);

	const content = `
NODE_ENV=development
ACCESS_TOKEN_SECRET=${ACCESS_TOKEN_SECRET}
REFRESH_TOKEN_SECRET=${REFRESH_TOKEN_SECRET}
RESTART_SECRET=
CERT_PATH=
AWS_REGION=
EMAIL_FROM_ADDRESS=
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
HTTPPORT=80
# In production, must match the HTTPSPORT repository variable for the Deploy workflow to work correctly.
HTTPSPORT=443
HTTPPORT_LOCAL=3000
HTTPSPORT_LOCAL=3443
GITHUB_API_KEY=
GITHUB_REPO=Infinite-Chess/infinitechess.org
APP_BASE_URL=https://www.infinitechess.org
	`;

	fs.writeFileSync(envPath, content.trim());

	console.log('Generated .env file');

	// Immediately UPDATE the contents of process.env
	dotenv.config();
}

/**
 * Generate a random string of specified length.
 * @param length - The length of the generated string, in bytes. The resulting string will be double this amount in characters.
 * @returns The generated random string
 */
function generateSecret(length: number): string {
	return crypto.randomBytes(length).toString('hex');
}

/** Ensures some existing environment variables are valid. */
function ensureValid(): void {
	const NODE_ENV = process.env['NODE_ENV'];
	const validValues = ['development', 'production', 'test']; // 'test' only appears during Vitest unit testing.

	if (NODE_ENV === undefined || !validValues.includes(NODE_ENV)) {
		throw new Error(
			`NODE_ENV environment variable must be either 'development', 'production', or 'test', received '${NODE_ENV}'.`,
		);
	}
}


================================================
FILE: build/index.ts
================================================
// build/index.ts

/**
 * This script deploys all files and assets from /src/client to /dist in order to run the website.
 *
 * Development mode: Transpile all TypeScript files to JavaScript.
 * Production mode: Transpile and bundle all TypeScript files to JavaScript, and minify via @swc/core.
 * 					Further, all css files are minified by lightningcss.
 */

import { setupEnv } from './env';
import { buildViews } from './views';
import { buildClient } from './client';
import { buildServer } from './server';
import { setupEngineWasm } from './engine-wasm';

import 'dotenv/config'; // Imports all properties of process.env, if it exists

// Ensure .env file exists and has valid contents
setupEnv();

/** Whether additional minifying of bundled scripts and css files should be skipped. */
const USE_DEVELOPMENT_BUILD = process.argv.includes('--dev');

if (USE_DEVELOPMENT_BUILD && process.env['NODE_ENV'] === 'production') {
	throw new Error(
		"Cannot run build process with --dev flag when NODE_ENV environment variable is 'production'!",
	);
}

// Ensure the HydroChess WASM engine is available
// Must be awaited since client build has a .wasm dependency on it.
await setupEngineWasm();

// Build both client and server scripts
// Await all so the script doesn't finish and node terminate before esbuild is done.
await Promise.all([buildClient(USE_DEVELOPMENT_BUILD), buildServer(USE_DEVELOPMENT_BUILD)]);

// Generate Static Views (HTML)
await buildViews();

// console.log('Build process finished.');


================================================
FILE: build/plugins.ts
================================================
// build/plugins.ts

/**
 * Contains shared esbuild plugins used in both client and server builds.
 */

import type { Plugin, PluginBuild } from 'esbuild';

/** Returns an esbuild plugin that logs whenever a build finishes/fails. */
export function getESBuildLogStatusLogger(successMessage: string, failureMessage: string): Plugin {
	return {
		name: 'log-rebuild',
		setup(build: PluginBuild) {
			// This hook runs when a build has finished
			build.onEnd((result) => {
				if (result.errors.length > 0) console.error(failureMessage);
				else console.log(successMessage);
			});
		},
	};
}


================================================
FILE: build/server.ts
================================================
// build/server.ts

import { glob } from 'glob';
import esbuild, { BuildOptions } from 'esbuild';

import { getESBuildLogStatusLogger } from './plugins.js';

// ================================= CONSTANTS =================================

const entryPoints = await glob(['src/server/**/*.{ts,js}', 'src/shared/**/*.{ts,js}'], {
	ignore: ['**/*.test.{ts,js}'],
});

// ================================= BUILDING ===================================

const esbuildServerRebuildPlugin = getESBuildLogStatusLogger(
	'✅ Server Build successful.',
	'❌ Server Build failed.',
);

const esbuildOptions: BuildOptions = {
	// Transpile all TS files from BOTH directories
	entryPoints: entryPoints,
	platform: 'node',
	bundle: false, // No bundling for the server. Just transpile each file individually
	outdir: 'dist',
	format: 'esm',
	sourcemap: true, // Patches file paths from server console errors to the correct src/ file
	plugins: [esbuildServerRebuildPlugin],
};

// ================================= BUILDING ===================================

/** Builds the server's scripts, transpiling them all into javascript (no bundling). */
export async function buildServer(isDev: boolean): Promise<void> {
	// console.log(`Building server in ${isDev ? 'DEVELOPMENT' : 'PRODUCTION'} mode...`);

	const context = await esbuild.context(esbuildOptions);

	if (isDev) {
		await context.watch();
		// console.log('esbuild is watching for SERVER changes...');
	} else {
		await context.rebuild();
		context.dispose();
		// console.log('Server build complete.');
	}
}


================================================
FILE: build/views.ts
================================================
// build/views.ts

/**
 * Generates static HTML views from EJS templates and translation files.
 */

import fs from 'fs';
import path from 'path';
import i18next from 'i18next';
import ejs, { Data } from 'ejs';
import { fileURLToPath } from 'node:url';

import editorutil from '../src/shared/util/editorutil.js';

import translationLoader from '../src/server/config/translationLoader.js';
import { DEFAULT_LANGUAGE } from '../src/server/utility/translate.js';
import { UNCERTAIN_LEADERBOARD_RD } from '../src/server/game/gamemanager/ratingcalculation.js';

// Constants -----------------------------------------------------------------

const __dirname = path.dirname(fileURLToPath(import.meta.url));

/**
 * Templates without any external data other than translations.
 * Don't insert names with file extensions.
 */
const staticTranslatedTemplates = [
	'createaccount',
	'credits',
	'guide',
	'index',
	'login',
	'member',
	'news',
	'leaderboard',
	'play',
	'termsofservice',
	'resetpassword',
	'admin',
	'errors/400',
	'errors/401',
	'errors/404',
	'errors/409',
	'errors/500',
];

// Functions -----------------------------------------------------------------

/** Generates translated versions of templates in {@link staticTranslatedTemplates}. */
export async function buildViews(): Promise<void> {
	// Load data
	const translations = translationLoader.loadTranslations();
	// Grab supported languages from the loaded translations
	const supportedLanguages = Object.keys(translations);
	const news = translationLoader.loadNews(supportedLanguages);

	// Initialize i18next for the build process so the 't' function works during render
	await i18next.init({
		resources: translations,
		defaultNS: 'default',
		fallbackLng: DEFAULT_LANGUAGE,
		// debug: true, // Enable debug mode to see logs for missing keys and other details
	});

	const languages_list = Object.entries(translations).map(
		([languageCode, languageTranslations]) => ({
			code: languageCode,
			name: languageTranslations.default['name'] as string,
			englishName: languageTranslations.default['english_name'] as string,
		}),
	);

	const templatesPath = path.join(__dirname, '../dist/client/views');

	for (const languageCode of Object.keys(translations)) {
		// Specific ejsOptions for rendering this language
		const ejsData: Data = {
			// Function for translations
			t: function (key: string, options: Record<string, any> = {}) {
				options['lng'] = languageCode; // Make sure language is correct
				return i18next.t(key, options);
			},
			languages: languages_list,
			language: languageCode,

			distfolder: path.join(__dirname, '../dist'),
			viewsfolder: templatesPath,

			// Inject the news HTML
			newsHTML: news[languageCode],

			// Custom included variables
			ratingDeviationUncertaintyThreshold: UNCERTAIN_LEADERBOARD_RD,
			editorPositionNameMaxLength: editorutil.MAX_POSITION_NAME_LENGTH,
		};

		// The output directory for this language's rendered templates
		const renderDirectory = path.join(templatesPath, languageCode);

		// Render each of this language's static translated templates
		for (const template of staticTranslatedTemplates) {
			const templatePath = path.join(templatesPath, template + '.ejs');
			const templateFile = fs.readFileSync(templatePath).toString();

			const renderedPath = path.join(renderDirectory, template + '.html');
			const renderedFile = ejs.render(templateFile, ejsData); // Render the file

			fs.mkdirSync(path.dirname(renderedPath), { recursive: true }); // Ensure directory exists
			fs.writeFileSync(renderedPath, renderedFile); // Write the rendered file
		}
	}
}


================================================
FILE: dev-utils/ICN_METADATA_TRANSLATIONS.md
================================================
# English Translations Required for ICN Metadata

ICN metadata must **always** be written in English, regardless of the user's language.

---

## 1. Player Names (`White` / `Black` metadata)

Used when a player is not signed in, or is the local user in engine games.

| TOML Key                          | English Value | Used In                                                                         |
| --------------------------------- | ------------- | ------------------------------------------------------------------------------- |
| `play.javascript.you_indicator`   | `(You)`       | Engine & board-editor engine games — assigned to the human player's color       |
| `play.javascript.guest_indicator` | `(Guest)`     | Online games — assigned to non-signed-in players (server-side, already English) |

---

## 2. Variant Names (`Variant` metadata / `Event` metadata)

The variant code (e.g. `CoaIP`) is translated to its English spoken name when writing the `Event` string
and when copying a game to ICN (the `Variant` metadata field).

| TOML Key                            | English Value                                      |
| ----------------------------------- | -------------------------------------------------- |
| `play.play-menu.Classical`          | `Classical`                                        |
| `play.play-menu.Confined_Classical` | `Confined Classical`                               |
| `play.play-menu.Classical_Plus`     | `Classical+`                                       |
| `play.play-menu.CoaIP`              | `Chess on an Infinite Plane`                       |
| `play.play-menu.Pawndard`           | `Pawndard`                                         |
| `play.play-menu.Knighted_Chess`     | `Knighted Chess`                                   |
| `play.play-menu.Palace`             | `Palace`                                           |
| `play.play-menu.Knightline`         | `Knightline`                                       |
| `play.play-menu.Core`               | `Core`                                             |
| `play.play-menu.Standarch`          | `Standarch`                                        |
| `play.play-menu.Pawn_Horde`         | `Pawn Horde`                                       |
| `play.play-menu.Space_Classic`      | `Space Classic`                                    |
| `play.play-menu.Space`              | `Space`                                            |
| `play.play-menu.Obstocean`          | `Obstocean`                                        |
| `play.play-menu.Abundance`          | `Abundance`                                        |
| `play.play-menu.Amazon_Chandelier`  | `Amazon Chandelier`                                |
| `play.play-menu.Containment`        | `Containment`                                      |
| `play.play-menu.Classical_Limit_7`  | `Classical - Limit 7`                              |
| `play.play-menu.CoaIP_Limit_7`      | `Coaip - Limit 7`                                  |
| `play.play-menu.Chess`              | `Chess`                                            |
| `play.play-menu.Classical_KOTH`     | `Experimental: Classical - KOTH`                   |
| `play.play-menu.CoaIP_KOTH`         | `Experimental: Coaip - KOTH`                       |
| `play.play-menu.CoaIP_HO`           | `Chess on an Infinite Plane - Huygens Option`      |
| `play.play-menu.CoaIP_RO`           | `Chess on an Infinite Plane - Roses Option`        |
| `play.play-menu.CoaIP_NO`           | `Chess on an Infinite Plane - Knightriders Option` |
| `play.play-menu.Omega`              | `Showcase: Omega`                                  |
| `play.play-menu.Omega_Squared`      | `Showcase: Omega^2`                                |
| `play.play-menu.Omega_Cubed`        | `Showcase: Omega^3`                                |
| `play.play-menu.Omega_Fourth`       | `Showcase: Omega^4`                                |
| `play.play-menu.4x4x4x4_Chess`      | `4×4×4×4 Chess`                                    |
| `play.play-menu.5D_Chess`           | `5D Chess`                                         |

---

## In the Future

During the website redesign, all of these required keys should be better restructured (they should more apparently be for the javascript, and not for any play-menu).

In addition, these are the only keys for which all English translations should be sent to the client, on top of their existing language-specific translations which should already be sent.


================================================
FILE: dev-utils/REDESIGN/design.md
================================================
# Summary of what should go on each page/component

## Header

- Site name + logo -> Home page
- News
- Practice
- Editor
- Analysis
- Leaderboard
- Donate
- Profile/Login
- Register/Logout
- Settings

## Footer

- About Infinite Chess
- Contact
- Terms of Service
- Privacy
- GitHub
- Discord
- Youtube


## Homepage
- Scrolling perspective mode board? Generally across the site though, a static 2D checkerboard background like that of the chess stack exchange.
- Lobby sits on the homepage.
- Below that: Spectate live games.

## Lobby
- [ ] Determine the maximum piece count where images are barely below recognizable. Convert that to characters
- Modal for creating an invite. Public/Private option. Private creates a url your friend can visit to view the invite and its properties. Option to provide custom position via ICN. Button to take selected variant to the editor. Maximum piece count prevents dirty images. Game modes available: Chess, 4 Dimensions, Showcases. Each has their own dropdowns with respective variants.
- Hovering over invites renders a small tooltip-popup window that previews the board, and custom gamerules, if any.

## Games
- Online games navigatable to via a link. Allows spectating if still live. Allows accepting a private invite if not yet joined.
- One vertical bar with clocks, moves, and chat, and material lost per side. Does chat have reporting? Do laws require it have reporting?
- The moves bar uses silhouettes of the piece svgs.

## Analysis Board
- Make, undo, change move history to perform analysis on positions.
- Turn on the engine to display the top move, and the score.

## Board Editor
- Share games via url. Next to the link to copy notation. Maximum piece count / icn length prevents dirty images.
- Create an invite from the position. Maximum piece count / icn length prevents dirty images. Same model popup as creating an invite from the lobby.
- Move to Analysis Board

## Profile

- Game history
- Change username

## Donation Page

Anyone that becomes a patron gets a cool badge next to their username.
Any monthly donation gives you the badge. $1+
When monthly dontations stop, badge is removed.
Maybe a lifetime donation amount where the badge is permanent?
Lichess offers golden wings after 5 years of active patron status. And instantly after a liftime donation, unlocking all colors.

## Light and Dark Theme

For light and dark themes, store colors once per theme as a small set of semantic variables, and every element in the entire codebase references those variables.

EXAMPLE THEME (for us we will have significantly fewer variables to start out):

/* src/client/css/themes.css */

:root,
[data-theme="light"] {
  --c-bg:            #f0efea;
  --c-surface:       #ffffff;
  --c-surface-raise: #e8e7e2;
  --c-surface-sink:  #dddcd6;
  --c-text:          #1a1a1a;
  --c-text-2:        #4a4a4a;
  --c-text-muted:    #757575;
  --c-text-inv:      #f0efea;
  --c-border:        #cccccc;
  --c-border-focus:  #5a9a5a;
  --c-brand:         #5a9a5a;
  --c-brand-hover:   #4a8a4a;
  --c-link:          #2060a0;
  --c-focus-ring:    rgba(90, 154, 90, 0.4);
  --c-error:         #cc2222;
  --c-warning:       #b06000;
  --c-success:       #2a7a2a;
}

[data-theme="dark"] {
  --c-bg:            #18181a;
  --c-surface:       #222226;
  --c-surface-raise: #2c2c30;
  --c-surface-sink:  #141416;
  --c-text:          #e2e2da;
  --c-text-2:        #b0b0a8;
  --c-text-muted:    #787870;
  --c-text-inv:      #18181a;
  --c-border:        #3a3a3e;
  --c-border-focus:  #70ba70;
  --c-brand:         #6aaa6a;
  --c-brand-hover:   #7aba7a;
  --c-link:          #6090d0;
  --c-focus-ring:    rgba(106, 170, 106, 0.4);
  --c-error:         #ee5555;
  --c-warning:       #e09020;
  --c-success:       #50aa50;
}

================================================
FILE: dev-utils/REDESIGN/runner_setup.md
================================================
# GitHub Actions Runner Setup

[← Back to Navigation Guide](./NAVIGATING.md)

This guide covers everything needed to bring up automated deployment for `infinitechess.org`:

1. [Install the self-hosted runner on the production Mac](#part-1-install-the-self-hosted-runner)
2. [Configure repository secrets and variables](#part-2-configure-repository-secrets-and-variables)
3. [Add `RESTART_SECRET` to the production `.env`](#part-3-add-restart_secret-to-the-production-env)
4. [Add the `workflow_dispatch` trigger to the HydroChess workflow](#part-4-add-the-workflow_dispatch-trigger-to-the-hydrochess-workflow)
5. [Verify all three triggers work](#part-5-verify-each-trigger)

> **Note:** PM2 is assumed to be fully configured and running `infinitechess` before starting these steps.

---

## Part 1: Install the Self-Hosted Runner

The self-hosted runner is a small background process that holds a persistent long-poll connection to GitHub. When a deploy workflow is triggered, GitHub wakes the runner and it executes the workflow steps as shell commands on the production machine.

### 1.1 Open the runner registration page

1. Go to `https://github.com/Infinite-Chess/infinitechess.org` on GitHub.
2. Click **Settings → Actions → Runners → New self-hosted runner**.
3. Select **macOS** as the operating system and choose the correct architecture:
    - **x64** for Intel Macs
    - **ARM64** for Apple Silicon (M1/M2/M3)
4. GitHub displays a set of shell commands with the exact download URL, checksum, and a one-time registration token. **Do not close this page** — the token expires after one hour.

### 1.2 Create the runner directory and download the runner

Run the commands shown on the GitHub setup page. They will look like the following (use the exact URLs and checksums from GitHub, not these examples):

```bash
# Create a dedicated directory for the runner, outside the production code directory
mkdir ~/actions-runner && cd ~/actions-runner

# Download the runner package (use the URL shown on the GitHub page)
curl -o actions-runner-osx-arm64-X.Y.Z.tar.gz -L https://github.com/actions/runner/releases/download/vX.Y.Z/actions-runner-osx-arm64-X.Y.Z.tar.gz

# Verify the downloaded file's integrity (use the checksum shown on the GitHub page)
echo "CHECKSUM  actions-runner-osx-arm64-X.Y.Z.tar.gz" | shasum -a 256 -c

# Extract the archive
tar xzf ./actions-runner-osx-arm64-X.Y.Z.tar.gz
```

- **`curl -o … -L`**: Downloads the runner binary. `-o` sets the output filename; `-L` follows redirects.
- **`shasum -a 256 -c`**: Verifies the SHA-256 checksum to ensure the download was not corrupted.
- **`tar xzf`**: Extracts the gzipped tar archive into the current directory.

### 1.3 Configure the runner

Run the configuration command shown on the GitHub setup page. It will look like:

```bash
./config.sh --url https://github.com/Infinite-Chess/infinitechess.org --token <TOKEN>
```

- **`--url`**: The repository the runner will serve jobs for.
- **`--token`**: The one-time registration token from step 1.1.

When prompted:

- **Runner name**: Press **Enter** to accept the default (the machine's hostname), or type a custom name.
- **Additional labels**: Press **Enter** to skip.
- **Work folder**: Press **Enter** to accept the default (`_work`).

### 1.4 Install the runner as a launchd service

Installing as a service means the runner starts automatically on login and survives terminal sessions.

```bash
# Register the runner as a macOS launchd user service
./svc.sh install

# Start the service immediately (no need to log out and back in)
./svc.sh start
```

- **`./svc.sh install`**: Creates a `launchd` plist under `~/Library/LaunchAgents/` so the runner starts every time you log in.
- **`./svc.sh start`**: Starts the service right now.

To check the runner's status at any time (make sure you're cd'd into `~/actions-runner/`):

```bash
./svc.sh status
```

To view runner logs if something goes wrong:

```bash
# Tail the last 50 lines of the runner log
tail -50 ~/actions-runner/_diag/Runner_*.log
```

Once installed, the runner appears as **Online** in **Settings → Actions → Runners** on GitHub.

---

## Part 2: Configure Repository Secrets and Variables

Go to **Settings → Secrets and variables → Actions** on the `infinitechess.org` repository to add the following.

### 2.1 `RESTART_SECRET` (Secret)

The server's `deployController.ts` checks the `X-Restart-Secret` header against this value before performing the pre-deploy database backup. Only the runner (which has this secret injected as an environment variable at runtime) can call that endpoint.

**Generate the secret** by running this command in any terminal:

```bash
openssl rand -hex 32
```

- **`openssl rand -hex 32`**: Generates 32 cryptographically random bytes and encodes them as a 64-character hexadecimal string. Never share or commit this value.

Add the output as a **Secret** named `RESTART_SECRET`. You will also need to add the same value to the production `.env` file — see [Part 3](#part-3-add-restart_secret-to-the-production-env).

### 2.2 `DEPLOY_DIR` (Secret)

The absolute path to the production code directory on the server — the directory where PM2 currently runs the app from and where `git pull` / `npm ci` / `npm run build` should execute.

Example value: `/Users/naviary/infinitechess.org`

Add this as a **Secret** (under the "Secrets" tab) named `DEPLOY_DIR`. Storing it as a secret keeps the server's filesystem layout out of public workflow logs.

### 2.3 `HTTPSPORT` (Variable — not a secret)

The HTTPS port the production server listens on. The deploy workflow uses when connecting to the server — it hits `https://localhost:$HTTPSPORT/[endpoint]`.

This must match the `HTTPSPORT` value in the production `.env` file.

Add this as an Actions **Variable** named `HTTPSPORT`.

---

## Part 3: Add `RESTART_SECRET` to the Production `.env`

Open the production `.env` file (in the root of the `DEPLOY_DIR` directory) and add:

```
RESTART_SECRET=<the same value you added to GitHub Actions secrets>
```

Then reload the server and force PM2 to flush its environment cache, ensuring your app's `dotenv` package picks up the changes:

```bash
pm2 reload infinitechess --update-env
pm2 save
```

- `pm2 reload infinitechess --update-env`: Performs a graceful reload — starts a new process with the freshly updated system environment, then shuts down the old one.
- `pm2 save`: Freezes the current PM2 state so that if the Mac server ever reboots, the app starts back up with the correct environment variables.

---

## Part 4: Add the `workflow_dispatch` Trigger to the HydroChess Workflow

The HydroChess `build-wasm.yml` workflow must trigger the `deploy.yml` workflow on `infinitechess.org` **after** the engine release is fully published. Placing this as the very last step guarantees the new WASM binaries are available before the infinitechess.org build runs.

### 4.1 Create a fine-grained Personal Access Token (PAT)

The dispatch API call needs a token with permission to trigger Actions workflows on `infinitechess.org`.

1. Go to **GitHub.com → Settings → Developer settings → Personal access tokens → Fine-grained tokens**.
2. Click **Generate new token**.
3. Fill in:
    - **Token name**: `hydrochess-dispatch`
    - **Expiration**: Set to **1 year** and add a calendar reminder to rotate it before it expires. If the token expires, the dispatch step in HydroChess will silently fail with no other visible error.
    - **Resource owner**: `Infinite-Chess`
    - **Repository access**: Only selected repositories → `infinitechess.org`
    - **Repository permissions**: Set **Actions** to **Read and write** (this automatically selects Metadata: Read). Do **not** grant Contents access — it is not needed and would allow pushing code to the repository.
4. Click **Generate token** and **copy the value immediately** — it is shown only once.

### 4.2 Add the PAT as a secret in the HydroChess repository

1. Go to `https://github.com/Infinite-Chess/HydroChess` → **Settings → Secrets and variables → Actions**.
2. Click **New repository secret**.
3. **Name**: `INFINITECHESS_DISPATCH_TOKEN`
4. **Value**: the PAT generated in step 4.1.

### 4.3 Add the dispatch step to `build-wasm.yml`

Open `.github/workflows/build-wasm.yml` in the HydroChess repository. Append the following as the **last step** of the `build-and-release` job, after the "Create Release" step:

```yaml
- name: Trigger infinitechess.org deploy
  run: |
      curl -s -X POST \
        -H "Authorization: Bearer ${{ secrets.INFINITECHESS_DISPATCH_TOKEN }}" \
        -H "Accept: application/vnd.github.v3+json" \
        https://api.github.com/repos/Infinite-Chess/infinitechess.org/actions/workflows/deploy.yml/dispatches \
        -d '{"ref":"prod"}'
```

**What this does**: Sends an authenticated `POST` to GitHub's API directly triggering the `deploy.yml` workflow on `infinitechess.org`. This uses the `workflow_dispatch` endpoint, which only requires `Actions: Read and write` — unlike the `repository_dispatch` endpoint which requires `Contents: Read and write` (a far broader permission that allows pushing code). The self-hosted runner skips `git pull`/`npm ci` (since no new commits or dependencies changed on this repo), re-runs the build (which fetches the freshly published WASM files), and reloads PM2.

---

## Part 5: Verify Each Trigger

### 5.1 Trigger 1 — push to `prod`

Merge any real (non-markdown) change from `main` into `prod`. Watch the **Actions** tab on the `infinitechess.org` repository — the "Deploy" workflow should appear, run on the self-hosted runner, and complete successfully.

### 5.2 Trigger 2 — `workflow_dispatch`

1. Go to **Actions → Deploy** on the `infinitechess.org` repository.
2. Click **Run workflow → Run workflow**.
3. Confirm the workflow runs on `self-hosted` and succeeds.

### 5.3 Trigger 3 — HydroChess `workflow_dispatch`

Push a commit to the `main` branch of HydroChess (or manually trigger the `build-wasm.yml` workflow via `workflow_dispatch`). After the HydroChess workflow finishes and the release is published, the "Deploy" workflow on `infinitechess.org` should appear in the Actions tab and run.

### 5.4 Verify near-zero downtime

While a deploy is in progress, open the play page in a browser with the console visible. You should observe the WebSocket disconnect and reconnect within approximately 2.5 seconds, with the game resuming normally.

---


================================================
FILE: dev-utils/REDESIGN/stack.md
================================================
# Website Redesign Plan

The website will undergo a complete redesign to modernize its look and feel, making it much more professional and expandable. Every single page is going to be overhauled- their look, and their underlying code.

## Deployment Environment

Self-hosted on a Mac, no VPS. SSD storage. Cloudflare in front. Low traffic — a few hundred unique visitors per day.

## Technical Stack & Decisions

### Build Pipeline

- **esbuild:** Extend the existing pipeline in `build/`. Two additions to `build/client.ts`: (1) `entryNames: '[dir]/[name]-[hash]'` for content-hashed output filenames; (2) `metafile: true` plus a `writeManifest()` post-build function that reads esbuild's input→output map and writes `dist/manifest.json`. The server loads this manifest at startup and injects the correct hashed filenames into every Nunjucks render.

- **Content-hashed asset caching:** JS and CSS files are emitted as `main.[hash].js` / `styles.[hash].css` and served with `Cache-Control: immutable, max-age=31536000` — browsers cache them forever and fetch a new URL automatically when content (and thus the file fingerprint) changes. HTML is served with `Cache-Control: no-store` and always embeds the current hashed filesnames. For images and other static assets referenced directly in templates (e.g. `<img src="/img/king_w.png">`), use `Cache-Control: max-age=31536000` (without `immutable`) and append a `?v=2` query string manually in the template when the file changes. Reserve `immutable` only for build-pipeline-hashed files.

- **Nunjucks** replaces EJS as the server-side templating engine. Layout inheritance is the key benefit: one `layout.njk` defines the full `<html>`/`<head>`/`<body>` shell with named `{% block %}` slots, and every page file just `{% extends "layout.njk" %}` and fills in its title, styles, and body content. Changing a favicon or global meta tag means editing one file. Logic stays in route handlers; templates only use `{% for %}` / `{% if %}`. `build/views.ts` is deleted entirely — it only existed to pre-render every EJS template × every language to static `.html` files because the old server had no SSR capability. With Nunjucks, HTML is rendered at request time, `dist/client/views/` no longer exists, `root.ts` switches from `res.sendFile()` to `res.render()`, and `nodemon.json` no longer needs to watch `src/client/views`.

### Page Architecture

- **Proper MPA.** Each major feature lives on its own page — no cramming everything into one giant page. Pages are bandwidth-aware: each page only loads the JS it needs. This matters slightly less now that scripts are indefinitely cached after the first load, but it still keeps things clean and fast on first visit.

- **SSR (server-side render) everything that affects the first paint.** The server renders the full HTML — header auth state, notification badge count, member profile data, news "NEW" badges — before sending the response. The client never needs to fetch these or patch the DOM on load. Use client-side fetching only for things triggered by user interaction or that need live updates (e.g. leaderboard "Show More", editor saves, preferences writes).

- **Snabbdom for data-driven in-page reactivity.** Use it when DOM content is generated from data at runtime — leaderboard lists, chat windows, live game panels. Don't use it for static content known at author time (e.g. the fairy piece carousel in the Guide), or for pre-authored fixed elements like modals and tab panels that are simply shown/hidden. Each Snabbdom component needs a plain module-level `state` object and a `render(state)` function that should return a virtual DOM tree via `h()`. State is a plain JS object updated directly on socket events or user interactions.

### CSS & Styling

- **CSS methodology:** One shared stylesheet for global styles, plus a per-page stylesheet for each page. Short, descriptive class names scoped with native CSS nesting — no BEM, no prefixes. Each page's stylesheet has one top-level block matching its `<main>` class (e.g. `.login { .form-field {} }`), preventing any bleed between pages. lightningcss in the existing build pipeline handles transpilation for older browsers. Utility classes (`.hidden`, `.italic`, `.flex`, etc.) are hand-rolled and added to the shared stylesheet when redundancy appears — no Tailwind. CSS files are colocated with the component they style (e.g. `src/client/components/header/header.css`).

- **CSS custom property light/dark theme system.** A `[data-theme]` attribute on `<html>` (e.g. `data-theme="dark"`) selects a block of several semantic CSS variables (`--c-bg`, `--c-surface`, `--c-text`, `--c-brand`, `--c-border`, etc.) defined in the shared stylesheet. Switching themes is one `setAttribute` call plus a `localStorage` write. A small inline `<script>` in `<head>` reads `localStorage` and sets the attribute before any CSS loads, preventing a flash of the wrong light/dark theme on page load.

- **Font stack:** `"Noto Sans", Verdana, sans-serif` (Lichess font). Self-host Noto Sans (not loaded from Google Fonts CDN, but via `@font-face`) to avoid the extra DNS/connection overhead and the 1-day CSS cache expiry that Google Fonts carries. Font files are served with `Cache-Control: immutable, max-age=31536000` alongside JS/CSS. Ensure our middleware is capable of serving fonts, with the same cache-control as other static assets.

- **Header layout without JS measurement.** The header renders its correct auth state (logged-in vs. logged-out) entirely server-side. CSS container queries or a CSS-only overflow fallback handle layout at different widths. Language-width variation is the remaining open challenge.

### Auth & Session

- **Keep the existing dual-token auth system unchanged** — no backend migration needed. The refresh token (`jwt`) is already an `httpOnly` cookie sent on every request including page navigations. `verifyJWT` middleware already reads it and sets `req.memberInfo`, so Nunjucks SSR gets full auth context for free. Short-lived access tokens (managed by `validatorama.ts`) are kept for API calls — they encapsulate the DB-skipping refresh logic and have only 3 call sites. The one known limitation: pages without a websocket (currently only the editor) can go stale after logout in another tab, which we accept.

- **Logout in another tab:** When a socket-connected page receives the logout event, call `window.location.reload()` rather than trying to swap out header elements on the client. This lets the server re-render the correct logged-out state with no need to hide/show DOM elements in JS.

- **Defer non-critical DB writes with `res.on('finish')`.** Since SSR means the server is doing more DB work per request, use `res.on('finish')` to delay writes that don't affect the response — e.g. updating the user's last-active timestamp or marking notifications as read — until after the response has already been sent.

### Localization

- **Weblate-compatible translation system.** One TOML file per website feature (header nav, game UI, settings, leaderboard, profile, etc.) — dozens of components total. Weblate automatically marks strings in other languages as stale when the English source changes. No `removeOutdated()` function is needed. Stale translations are rendered as-is rather than falling back to English. `deepMerge()` is still kept for strings that are entirely absent in a given language (fallback to English). Markdown/article content is not run through Weblate but can optionally be translated manually by trnaslators. Components no longer need a version field for versioning, `loadTranslations()` should not support versioning.

- **No translations for ToS or Privacy Policy** — English only, sourced from markdown files. Optionally include a notice in the document that the English version takes precedence if they are ever translated. Markdown is too hard to version-control for translators in a way that communicates exactly what changed, so these pages are excluded from the translation pipeline.

### Late-Stage Polish

- **`<link rel="modulepreload">`** for each page's JS entry points, injected into the page HTML. This lets the browser fetch all ES modules in parallel from the first response, eliminating the waterfall of sequential import round trips. Add this last, once each page's import graph is finalized. Lower priority since scripts are cached indefinitely after the first load anyway.

- **White flash on navigation.** `@view-transition { navigation: auto }` with `::view-transition-old(root), ::view-transition-new(root) { animation-duration: 0s }` in the shared stylesheet eliminates a potential white flash between page loads by holding the old page visible until the new one is ready to paint — no crossfade, but an instant cut.

- **Audio autoplay fallback.** If the browser blocks the first move's sound, when navigating to tbr game page, before the first user gesture, refer to Lichess's approach as a reference: they show a small red mute icon in the header when audio is blocked. See: https://lichess.org/faq#autoplay

================================================
FILE: dev-utils/REDESIGN/todo.md
================================================
# Redesign TODO

---

## Build Pipeline

- Add `entryNames: '[dir]/[name]-[hash]'` to the esbuild client build options in `build/client.ts` so JS and CSS output filenames are content-hashed.

- Add `metafile: true` to the esbuild client build and write a `writeManifest()` post-build function that reads esbuild's input→output map and writes `dist/manifest.json`.

- At server startup, load `dist/manifest.json` and expose the hashed filenames to the Nunjucks render context so templates can reference them.

- Update static asset middleware: serve hashed JS/CSS with `Cache-Control: immutable, max-age=31536000`; serve HTML with `Cache-Control: no-store`; serve images/fonts with `Cache-Control: max-age=31536000` (without `immutable`).

- Add to the Pull Request Requirements and Guidelines that whenever an image or font asset changes, we must append a `?v=2` manually in the template so browsers know to fetch the new version instead of using the cached one. (Not needed for JS/CSS since those are content-hashed).

---

## Nunjucks Migration

- Install `nunjucks` and `@types/nunjucks`; configure the Nunjucks environment in the Express app (set views directory, autoescape, etc.).

- Create `layout.njk` — the full HTML shell (`<html>`, `<head>`, `<body>`) with `{% block %}` slots for: page title, extra `<head>` tags, page stylesheet, body content, and page script.

- Delete `build/views.ts`; remove the `copy:views` script from `package.json`; remove `src/client/views` from `nodemon.json`'s watch list.

- Migrate all existing route handlers from `res.sendFile()` to `res.render()`, pointing each to a minimal placeholder `.njk` file that extends `layout.njk`. This keeps the site functional while individual pages are redesigned.

---

## CSS Foundation

- Create the shared stylesheet (`src/client/css/global.css`) with some CSS custom property variables for both `[data-theme="dark"]` and `[data-theme="light"]` (e.g. `--c-bg`, `--c-surface`, `--c-text`, `--c-brand`, `--c-border`).

- Add the inline `<script>` to `layout.njk <head>` that reads `localStorage` and sets `data-theme` on `<html>` before any CSS loads, preventing a flash of the wrong theme.

- Create a `@font-face` declaration for Noto Sans and the font-stack CSS into the shared stylesheet.

- Ensure our middleware is capable of serving fonts, with the same cache-control as other static assets.

- Add other CSS rules we think will be shared across all pages.

---

## Translation System Refactor

- Restructure TOML translation files from one-file-per-page to one-file-per-feature-component (header nav, game UI, settings, leaderboard, profile, etc.). Do not migrate all existing keys, create new ones as we go, in the appropriate component. Do away with the `version` field.

- Update `loadTranslations()` to remove versioning support entirely; delete `removeOutdated()`. Keep `deepMerge()` (fallback to English for missing keys).

---

## Shared Components

*All page redesigns depend on these being done first.*

- Redesign and implement the shared header component (`src/client/components/header/`) — Nunjucks partial, CSS (CSS-only responsive layout, no JS measurement), and TS. Server receives auth state via `req.memberInfo` and passes it to the template.

- Redesign and implement the shared footer component — Nunjucks partial and CSS.

- Implement logout-in-another-tab handling: on all socket-connected pages, call `window.location.reload()` when the socket logout event is received so the server re-renders the correct logged-out state.

- Install `snabbdom` — required before any page that uses it for reactive lists.

---

## Page Redesigns

*Each page: new Nunjucks template extending `layout.njk`, new colocated CSS file, updated route handler with full SSR context, and updated/new TS where needed.*

- Redesign the **home (index)** page.

- Redesign other pages as you go. SSR all profile data (username, rating, join date, etc.). SSR initla batch of leaderboard rows; Snabbdom for the "Show More" interaction. SSR for news post "NEW" badges.

- Add the **Terms of Service** page — English only, rendered from a Markdown file, with an optional notice that the English version is authoritative.

- Add the **Privacy Policy** page — English only, same approach as ToS.

---

## Deferred DB Writes

- Audit all route handlers and wrap non-critical DB writes (last-active timestamp updates, marking notifications as read, etc.) in `res.on('finish')` so they run after the response is sent. *(Best done after all pages are complete)*

---

## Late-Stage Polish

- Add `<link rel="modulepreload">` for each page's JS entry points in its Nunjucks template. *(Do last, once every page's import graph is finalized)*

- Consider `@view-transition` if there's white flashes between page loads.

- Implement the audio autoplay fallback: detect when the browser has blocked audio before the first user gesture and display a muted indicator in the header (similar to Lichess's approach).


================================================
FILE: dev-utils/SKELETON.css
================================================
* {
    margin: 0;
    padding: 0;
    font-family: Verdana;
    border: 0;
    /* Enable temporarily during dev to see the borders of all elements */
    /* outline: 1px solid rgba(0, 0, 0, 0.191); */
}

html {
    height: 100%;
    background-color: rgb(33, 33, 33);
}



header {
    position: fixed;
    left: 0;
    top: 0;
    right: 0;
    box-shadow: 0px 1px 5px rgb(107, 107, 107);
    height: 40px;
    font-size: 0;
    overflow: scroll;
    white-space: nowrap;
    text-align: center;
    background-color: white;
    z-index: 1;
}

header a {
    display: inline-block;
    text-decoration: none;
    line-height: 40px;
    margin-left: 4px;
    min-width: 70px;
    font-size: 16px;
    color: black;
}

header a:hover {
    background-color: rgb(211, 235, 255);
}

header p {
    padding: 0 10px;
}



main {
        background-color: #fff;
    background-image: url('/img/blank_board.png');
    @supports (background-image: url('/img/blank_board.webp')) {
        background-image: url('/img/blank_board.webp');
    }
    @supports (background-image: url('/img/blank_board.avif')) {
        background-image: url('/img/blank_board.avif');
    }
    background-position: center;
    background-repeat: no-repeat;
    background-size: cover;
    -webkit-background-size: cover;
    -moz-background-size: cover;
    -o-background-size: cover;
    background-attachment: fixed;

    margin-top: 40px;
    min-height: 400px;
}

#content {
    background-color: rgba(255, 255, 255, 0.805);
    min-height: 450px;
    margin: auto;
    box-shadow: 0 0 10px rgba(0, 0, 0, 0.522);
    padding: 30px 20px;
}

#content h1 {
    font-size: 40px;
    font-family: georgia;
}



footer {
    text-align: center;
    padding: 10px 0;
}

footer a {
    display: inline-block;
    color: rgb(207, 207, 207);
    margin: 10px 10px;
    text-decoration: underline;
}



.center {
    text-align: center;
}

a {
    -webkit-tap-highlight-color: rgba(0, 0, 0, 0.099);
}



/* Start increasing header links width */
@media only screen and (min-width: 450px) {
    header {
        overflow: unset;
    }

    header a {
        min-width: calc(70px + (100vw - 450px) * 0.15);
    }
}

/* Stop increasing header links width */
@media only screen and (min-width: 715px) {
    header a {
        min-width: 110px;
    }
}

/* Cap content width size, revealing image on the sides */
@media only screen and (min-width: 810px) {
    #content {
        max-width: calc(810px - 60px); /* 60px less than 810 to account for padding */
        padding: 40px 30px;
        min-height: 800px;
    }
}


================================================
FILE: dev-utils/SKELETON.html
================================================
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <meta http-equiv="x-ua-compatible" content="ie=edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <title>Infinite Chess | Home - The Official Website</title>
        <link rel='stylesheet' href='/css/SKELETON.css'>
        <link rel="icon" type="image/png" href="/img/favicon.png">
    </head>
    <body>
        <header>
            <a href="/">
                <p>Home</p>
            </a>
            <a href="/play">
                <p>Play</p>
            </a>
            <a href="/news">
                <p>News</p>
            </a>
            <a href="/leaderboard">
                <p>Leaderboard</p>
            </a>
            <a href="/login" id="loginlink">
                <p id="logintext">Log In</p>
            </a>
            <a href="/createaccount" id="createaccountlink">
                <p id="createaccounttext">Create Account</p>
            </a>
            <script src="/scripts/memberHeader.js"></script>
        </header>
        <main>
            <div id="content">
                <h1 class="center">Skeleton</h1>
            </div>
        </main>
        <footer>
            <a href="mailto:support@infinitechess.org">Contact us.</a>
            <a href="/termsofservice">Terms of Service</a>
        </footer>
    </body>
</html>

================================================
FILE: dev-utils/live-game-persistence.md
================================================
# Live Game Persistence

Active games are persisted to the database so they survive server restarts instead of being aborted. This document describes the two-table schema, what each column stores, and the event matrix that drives every DB write.

---

## Database Schema: Two Tables

Following the pattern of `games` + `player_games` for ended games, live state is split across two tables to support an arbitrary number of players per game:

- **`live_games`** — One row per active game. Contains game-level state.
- **`live_player_games`** — One row per player per active game. Contains per-player state.

### Table 1: `live_games`

#### Group 1: Game Identity

| Column         | Type                                       | Notes                                    |
| -------------- | ------------------------------------------ | ---------------------------------------- |
| `game_id`      | INTEGER PRIMARY KEY                        | Unique across live and logged games      |
| `time_created` | INTEGER NOT NULL                           | Epoch milliseconds                       |
| `variant`      | TEXT NOT NULL                              | e.g. `"Classical"`, `"Omega^3"`          |
| `clock`        | TEXT NOT NULL                              | e.g. `"600+5"` or `"-"` for untimed     |
| `rated`        | BOOLEAN NOT NULL CHECK (rated IN (0, 1))   | 0 = casual, 1 = rated                    |
| `private`      | BOOLEAN NOT NULL CHECK (private IN (0, 1)) | 0 = public, 1 = private                 |

#### Group 2: Move History

| Column  | Type                       | Notes                                                                                                                      |
| ------- | -------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
| `moves` | TEXT NOT NULL DEFAULT `''` | Pipe-delimited compact moves with embedded clock comments via ICN format (e.g. `1,2>3,4{[%clk 0:09:56.7]}`). See below. |

**Move format:** Produced by `getShortFormMovesFromMoves()` in `icnconverter.ts` with `{ compact: true, spaces: false, comments: true, move_numbers: false }`. Each move encodes `startCoords > endCoords`, optional promotion, and a clock comment. Parsed back via `parseShortFormMoves()`. The entire column is rewritten on each move submission.

#### Group 3: Clock State

| Column                | Type    | Notes                                                                               |
| --------------------- | ------- | ----------------------------------------------------------------------------------- |
| `color_ticking`       | INTEGER | Player number whose clock is running. NULL if untimed, < 2 moves, or game over.    |
| `clock_snapshot_time` | INTEGER | Epoch ms when clock values were snapshotted. Used to adjust the ticking player's time on restoration: `actual = stored_remaining - (Date.now() - clock_snapshot_time)`. |

Per-player `time_remaining_ms` lives in `live_player_games`.

#### Group 4: Draw Offer State

| Column             | Type    | Notes                                                         |
| ------------------ | ------- | ------------------------------------------------------------- |
| `draw_offer_state` | INTEGER | Player number who extended the current offer. NULL if none.   |

Per-player `last_draw_offer_ply` lives in `live_player_games`.

#### Group 5: Game Conclusion

| Column                 | Type    | Notes                                                                                              |
| ---------------------- | ------- | -------------------------------------------------------------------------------------------------- |
| `conclusion_condition` | TEXT    | e.g. `"checkmate"`, `"time"`, `"resignation"`, `"aborted"`, `"agreement"`. NULL if ongoing.        |
| `conclusion_victor`    | INTEGER | Winning player number. NULL for draw, ongoing, or aborted.                                         |
| `time_ended`           | INTEGER | Epoch ms when game concluded. NULL if ongoing.                                                     |

#### Group 6: Timer State

| Column            | Type    | Notes                                                                                                                                                  |
| ----------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `afk_resign_time` | INTEGER | Epoch ms when the AFK auto-resign fires. NULL if no AFK timer active. On restoration, remaining = `stored - Date.now()`; if ≤ 0, immediately resign.   |
| `delete_time`     | INTEGER | Epoch ms when the concluded game is deleted and logged. NULL if ongoing. Set to `timeEnded + timeBeforeGameDeletionMillis`. On restoration, if elapsed, immediately run logging. |

#### Group 7: Flags

| Column            | Type                                                         | Notes                                                                                                            |
| ----------------- | ------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------- |
| `position_pasted` | BOOLEAN NOT NULL DEFAULT 0 CHECK (position_pasted IN (0, 1)) | Whether a custom position was pasted. Pasted games are never logged to the permanent `games` table.              |
| `validate_moves`  | BOOLEAN NOT NULL DEFAULT 1 CHECK (validate_moves IN (0, 1))  | Whether server-side move validation is active (`boardsim` is defined). Set to 0 when a position is pasted.       |

---

### Table 2: `live_player_games`

One row per player per live game.

| Column                        | Type             | Notes                                                                                                                                                                  |
| ----------------------------- | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `game_id`                     | INTEGER NOT NULL | FK → `live_games.game_id` ON DELETE CASCADE                                                                                                                            |
| `player_number`               | INTEGER NOT NULL | 1 = White, 2 = Black, etc. Supports future multi-player games.                                                                                                         |
| `user_id`                     | INTEGER          | NULL if guest.                                                                                                                                                         |
| `browser_id`                  | TEXT NOT NULL    | Always present (guests are identified by `browser_id` alone).                                                                                                         |
| `elo`                         | TEXT             | Snapshot at game start (e.g. `"1500"` or `"1200?"`). NULL if guest.                                                                                                   |
| `last_draw_offer_ply`         | INTEGER          | Ply (0-based) of the player's last draw offer. NULL if never offered.                                                                                                  |
| `time_remaining_ms`           | INTEGER          | Milliseconds remaining at time of snapshot. NULL if untimed.                                                                                                           |
| `disconnect_cushion_end_time` | INTEGER          | Epoch ms when the 5-second reconnection cushion expires. NULL if no cushion is active.                                                                                 |
| `disconnect_resign_time`      | INTEGER          | Epoch ms when the auto-resign fires. NULL if no active disconnect timer.                                                                                               |
| `disconnect_by_choice`        | BOOLEAN          | 1 = intentional disconnect (20s timer), 0 = network drop (60s timer). NULL if player was connected. CHECK (disconnect_by_choice IN (0, 1)).                            |

**Three-case disconnect restoration:**
- `disconnect_resign_time` non-NULL → auto-resign timer was active; restore from that timestamp.
- `disconnect_cushion_end_time` non-NULL, `disconnect_resign_time` NULL → still in the 5-second cushion; revive it (or start the auto-resign timer if elapsed).
- All disconnect columns NULL → player was connected before the restart; start a fresh 60-second timer (server restart counts as not-by-choice).

---

## Event Matrix: When Each Column Is Written

| Event                       | `live_games` Columns Updated                                                                                     | `live_player_games` Columns Updated                                                                  |
| --------------------------- | ---------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
| **Game created**            | INSERT full row (all Group 1 columns, defaults for the rest)                                                     | INSERT one row per player (identity, elo, defaults)                                                  |
| **Move submitted**          | `moves`, `color_ticking`, `clock_snapshot_time`, `validate_moves`                                                | `time_remaining_ms` (both players)                                                                   |
| **Draw offer extended**     | `draw_offer_state`                                                                                               | `last_draw_offer_ply` (offering player)                                                              |
| **Draw offer declined**     | `draw_offer_state` → NULL                                                                                        | —                                                                                                    |
| **Draw accepted**           | `conclusion_condition`, `conclusion_victor`, `time_ended`, `draw_offer_state`, `delete_time`                     | —                                                                                                    |
| **Resignation**             | `conclusion_condition`, `conclusion_victor`, `time_ended`, `delete_time`                                         | —                                                                                                    |
| **Abort**                   | `conclusion_condition`, `time_ended`, `delete_time`                                                              | —                                                                                                    |
| **Time loss**               | `conclusion_condition`, `conclusion_victor`, `time_ended`, `color_ticking`, `clock_snapshot_time`, `delete_time` | `time_remaining_ms`                                                                                  |
| **Disconnect loss**         | `conclusion_condition`, `conclusion_victor`, `time_ended`, `delete_time`                                         | —                                                                                                    |
| **Player disconnects**      | —                                                                                                                | `disconnect_cushion_end_time`, `disconnect_resign_time`, `disconnect_by_choice`                      |
| **Player reconnects**       | —                                                                                                                | `disconnect_cushion_end_time` → NULL, `disconnect_resign_time` → NULL, `disconnect_by_choice` → NULL |
| **Player goes AFK**         | `afk_resign_time`                                                                                                | —                                                                                                    |
| **Player returns from AFK** | `afk_resign_time` → NULL                                                                                         | —                                                                                                    |
| **AFK auto-resign**         | `conclusion_condition`, `conclusion_victor`, `time_ended`, `afk_resign_time` → NULL, `delete_time`               | —                                                                                                    |
| **Position pasted**         | `position_pasted`, `validate_moves` → 0                                                                         | —                                                                                                    |
| **Game deleted/logged**     | DELETE row (cascades to `live_player_games`)                                                                     | —                                                                                                    |


================================================
FILE: dev-utils/pieces/spritesheet 512/How to create spritesheet.md
================================================
# How to generate the game's spritesheet

1. Go to [Stitches](https://draeton.github.io/stitches/).

2. Make sure there are 64 images (not including this guide). If there are not, duplicate the empty placeholder until you do.

3. Drag all files inside [this folder](../spritesheet%20512/) and upload them, except this guide.

4. Make sure they are in the correct order.

5. Decrease the padding to 0px.

================================================
FILE: dev-utils/pieces/svg/Converting PNG to SVG.md
================================================
# Steps to converting a PNG to SVG #

This is the best method I've found, to retain high quality, yet remain highly compact!

1. Go to [SVG Trace](https://svgtrace.com/png-to-svg)

2. Drag in your desired PNG, approximately 512x512. Larger will lead to a larger ending file size.

3. Do NOT change any of the settings

4. Convert & Export

5. Open [Compress or Die](https://compress-or-die.com/svg)

6. Upload your new SVG

7. Drag "Decimal precision" to exactly 1. Checkmark "Extreme compression (experimental). 

8. Click "Generate Optimized Image". Download optimized image.

9. Open your SVG's code, find all `fill` attributes. Change the ones super close to white to `#ffffff`, and the ones super close to black to `#000000`. Remove unneeded external links.

10. See if it can further be compressed by running it through [SVG Minify](https://www.svgminify.com/).

11. Enjoy your optimized SVG.

================================================
FILE: dev-utils/post_processing_effects/posterize/PosterizePass.ts
================================================
// dev-utils/post_processing_effects/posterize/PosterizePass.ts

import type { PostProcessPass } from '../PostProcessingPipeline';
import type { ProgramManager, ProgramMap } from '../../ProgramManager';

/**
 * A post-processing pass that reduces the number of colors in the scene
 * to create a "posterized" effect.
 */
export class PosterizePass implements PostProcessPass {
	readonly program: ProgramMap['posterize'];

	// --- Public Properties for Control ---

	/** A master control for the strength of the entire pass. 0.0 is off, 1.0 is full effect. */
	public masterStrength: number = 1.0;

	/**
	 * The number of distinct color levels per channel (red, green, blue).
	 * A value of 4, for example, means each channel can only be one of 4 values.
	 * Set 1 or less to effectively disable the effect.
	 */
	public levels: number = 8;

	constructor(programManager: ProgramManager) {
		this.program = programManager.get('posterize');
	}

	/**
	 * Renders the posterization effect.
	 * @param gl - The WebGL2 rendering context.
	 * @param inputTexture - The texture to process (usually the output of the previous pass).
	 */
	render(gl: WebGL2RenderingContext, inputTexture: WebGLTexture): void {
		this.program.use();

		// Bind the input texture to texture unit 0
		gl.activeTexture(gl.TEXTURE0);
		gl.bindTexture(gl.TEXTURE_2D, inputTexture);

		// Set the uniforms for the shader
		gl.uniform1i(this.program.getUniformLocation('u_sceneTexture'), 0);
		gl.uniform1f(this.program.getUniformLocation('u_masterStrength'), this.masterStrength);
		gl.uniform1f(this.program.getUniformLocation('u_levels'), this.levels);
	}
}


================================================
FILE: dev-utils/post_processing_effects/posterize/fragment.glsl
================================================
#version 300 es
precision highp float;

// The texture containing the scene to be posterized
uniform sampler2D u_sceneTexture;

uniform float u_masterStrength; // 0.0 = no effect, 1.0 = full effect
uniform float u_levels; // The number of color levels per channel

// The texture coordinates passed from the vertex shader
in vec2 v_uv;

// The final output color
out vec4 out_color;

void main() {
    // Sample the original color from the input texture
    vec4 originalColor = texture(u_sceneTexture, v_uv);

    // Calculate the fully posterized color
    vec3 posterizedColor;

    // If levels are 1.0 or less, the "posterized" color is just the original color.
    // This prevents division by zero and provides an easy way to toggle the effect.
    if (u_levels <= 1.0) {
        posterizedColor = originalColor.rgb;
    } else {
        // Apply the posterization formula
        float numLevels = u_levels - 1.0;
        posterizedColor = floor(originalColor.rgb * numLevels) / numLevels;
    }

    // Blend between the original and the posterized color using master strength.
    vec3 finalRgb = mix(originalColor.rgb, posterizedColor, u_masterStrength);

    // Output the final color, preserving the original alpha
    out_color = vec4(finalRgb, originalColor.a);
}

================================================
FILE: dev-utils/post_processing_effects/radial_distortion/RadialDistortionPass.ts
================================================
import type { ProgramManager, ProgramMap } from "../../ProgramManager";
import type { PostProcessPass } from "../PostProcessingPipeline";


/**
 * A post-processing pass that applies radial distortion.
 * Used for both barrel and pincushion effects.
 */
export class RadialDistortionPass implements PostProcessPass {
	readonly program: ProgramMap['radial_distortion'];

	// --- Public Properties to Control the Effect ---

	/**
	 * The strength of the distortion.
	 * Positive values create a barrel (bulging) effect.
	 * Negative values create a pincushion (pinching) effect.
	 */
	public strength: number = 0.0;

	/** The center of the distortion, in UV coordinates [0, 1]. */
	public center: [number, number] = [0.5, 0.5];


	constructor(programManager: ProgramManager) {
		this.program = programManager.get('radial_distortion');
	}

	render(gl: WebGL2RenderingContext, inputTexture: WebGLTexture): void {
		this.program.use();
		
		gl.activeTexture(gl.TEXTURE0);
		gl.bindTexture(gl.TEXTURE_2D, inputTexture);
		
		gl.uniform1i(this.program.getUniformLocation('u_sceneTexture'), 0);
		gl.uniform1f(this.program.getUniformLocation('u_strength'), this.strength);
		gl.uniform2fv(this.program.getUniformLocation('u_center'), this.center);
	}
}

================================================
FILE: dev-utils/post_processing_effects/radial_distortion/fragment.glsl
================================================
#version 300 es
precision highp float;

uniform sampler2D u_sceneTexture;

// --- Distortion Controls ---
uniform float u_strength; // Positive for barrel, negative for pincushion
uniform vec2 u_center;    // The center point of the distortion

in vec2 v_uv;
out vec4 out_color;

void main() {
    // Vector from the current UV coordinate to the distortion center
	vec2 to_center = v_uv - u_center;

    // Calculate the distance squared from the center.
    // Using dot product is often faster than length() -> sqrt().
    float dist_sq = dot(to_center, to_center);

    // Calculate the displacement factor.
    // This is the core of the effect.
    float displacement = 1.0 + u_strength * dist_sq;

    // Apply the displacement to the vector from the center
    vec2 displaced_uv = u_center + to_center * displacement;

    // Look up the color from the original texture at the new, distorted coordinate
	out_color = texture(u_sceneTexture, displaced_uv);
}

================================================
FILE: dev-utils/post_processing_effects/rolling_hills/RollingHillsPass.ts
================================================
import type { ProgramManager, ProgramMap } from "../../ProgramManager";
import type { PostProcessPass } from "../PostProcessingPipeline";


/**
 * A post-processing pass that applies a single-axis sine wave distortion,
 * creating a "rolling hills" or flag-waving effect.
 */
export class RollingHillsPass implements PostProcessPass {
	readonly program: ProgramMap['rolling_hills'];

	// --- Public Properties to Control the Effect ---

	/** The strength of the wave (how far pixels are displaced). */
	public amplitude: number = 0.1;

	/** The number of full waves across the screen. */
	public frequency: number = 1.0;

	/** The angle of the wave crests in degrees. 0 creates vertical waves, 90 creates horizontal waves. */
	public angle: number = 0.0;

	/** The current time, used to animate the waves. */
	public time: number = 0.0;

	
	constructor(programManager: ProgramManager) {
		this.program = programManager.get('rolling_hills');
	}

	render(gl: WebGL2RenderingContext, inputTexture: WebGLTexture): void {
		this.program.use();
		
		gl.activeTexture(gl.TEXTURE0);
		gl.bindTexture(gl.TEXTURE_2D, inputTexture);

		// Convert user-friendly degrees to radians for the shader
		const angleInRadians = this.angle * (Math.PI / 180.0);
		
		// Set all the uniforms
		gl.uniform1i(this.program.getUniformLocation('u_sceneTexture'), 0);
		gl.uniform1f(this.program.getUniformLocation('u_amplitude'), this.amplitude);
		gl.uniform1f(this.program.getUniformLocation('u_frequency'), this.frequency);
		gl.uniform1f(this.program.getUniformLocation('u_angle'), angleInRadians);
		gl.uniform1f(this.program.getUniformLocation('u_time'), this.time);
	}
}

================================================
FILE: dev-utils/post_processing_effects/rolling_hills/fragment.glsl
================================================
#version 300 es
precision highp float;

uniform sampler2D u_sceneTexture;

// --- Distortion Controls ---
uniform float u_amplitude; // The strength of the wave
uniform float u_frequency; // The number of waves across the screen
uniform float u_angle;     // The angle of the waves in radians
uniform float u_time;      // Animate the waves over time

in vec2 v_uv;
out vec4 out_color;

const float PI = 3.1415926535;

void main() {
    // Center the coordinates before rotation
    vec2 centeredUV = v_uv - 0.5;

    // Define the direction the pixels will be displaced.
    // This is perpendicular to the wave crests.
    vec2 displaceDir = vec2(cos(u_angle), sin(u_angle));

    // Define the axis along which the wave's crests lie.
    // This is perpendicular to the displacement direction.
    vec2 waveAxis = vec2(-displaceDir.y, displaceDir.x);

    // Calculate the input for the sine function.
    // We project the UV coordinate onto the wave's axis. This tells us "how far along"
    // the wave we are for any given pixel, creating straight, parallel wave crests.
    float waveInput = dot(centeredUV, waveAxis);


    // --- NEW: Get the SIGNED distance from the center. ---
    // This value will be negative on one side of the center and positive on the other.
    float signedDist = dot(centeredUV, displaceDir);

    // --- NEW: Create a linear multiplier from the signed distance. ---
    // The distance is roughly -0.5 to 0.5, so multiplying by 2.0 scales it to a nice -1.0 to 1.0 range.
    float amplitudeMultiplier = signedDist * 2.0;

    // // Calculate the amplitude multiplier based on distance from center.
    // // Get the distance from the center along the wave's travel direction.
    // float distFromCenter = abs(dot(centeredUV, displaceDir));
    // // Create a smooth multiplier that goes from 0.0 (at center) to 1.0 (at screen edge, ~0.5 distance).
    // // smoothstep gives a nice ease-in/out effect.
    // float amplitudeMultiplier = smoothstep(0.0, 0.5, distFromCenter);

    // Calculate the offset amount using the sine function, and apply the multiplier to it.
    float offset = sin(waveInput * u_frequency * 2.0 * PI + u_time) * u_amplitude * amplitudeMultiplier;

    // Apply the offset to the UVs in the displacement direction.
    vec2 distortedUV = v_uv + displaceDir * offset;

	out_color = texture(u_sceneTexture, distortedUV);
}

================================================
FILE: dev-utils/readme.md
================================================
# Dev Utils

This directory contains both depricated scripts that we believe might be useful in the future, as well as assets useful for development but not production.

No source code script imports and runs any code from this directory, it is completely isolated from the production codebase.

For this reason, code in here does not have to follow linting or formatting rules.

================================================
FILE: dev-utils/scripts/PatreonAPI.ts
================================================
// dev-utils/scripts/PatreonAPI.ts

/*
 * This module, in the future, will be where we connect to Patreon's API
 * to dynamically refresh the list of Patreon-specific patrons on the website.
 */

/** A list of patrons on Naviary's [patreon](https://www.patreon.com/Naviary) page.
 * This should be periodically refreshed. */
const patrons: string[] = [];
/** An object, containing patron usernames for the key, and their preferred
 * name on the website's patron list for the value. */
const replacementNames: Record<string, string> = {};

/** The interval, in milliseconds, to use Patreon's API to refresh the patron list. */
// const intervalToRefreshPatreonPatronsMillis = 1000 * 60 * 60; // 1 hour

// /**
//  * Uses Patreon's API to fetch all patrons on Naviary's
//  * [patreon](https://www.patreon.com/Naviary) page, and updates our list!
//  *
//  * STILL TO BE WRITTEN
//  */
// function refreshPatreonPatronList() {

// }

/**
 * Returns a list of patrons on Naviary's [patreon](https://www.patreon.com/Naviary) page,
 * updated every {@link intervalToRefreshPatreonPatronsMillis}.
 */
export function getPatreonPatrons(): string[] {
	// Replace their true usernames with replacements
	const patronsWithReplacedNames = patrons.map((patron) => {
		return replacementNames[patron] || patron;
	});

	return patronsWithReplacedNames;
}


================================================
FILE: dev-utils/scripts/audio/processors/bitcrusher/BitcrusherNode.ts
================================================
// dev-utils/scripts/audio/processors/bitcrusher/BitcrusherNode.ts

export class BitcrusherNode extends AudioWorkletNode {
	constructor(context: AudioContext) {
		super(context, 'bitcrusher-processor');
	}

	/**
	 * Factory method to asynchronously create and initialize a BitcrusherNode.
	 * @param context The AudioContext to create the node in.
	 * @param workletUrl The URL to the compiled bitcrusher-processor.js file.
	 * @returns A promise that resolves with a fully initialized BitcrusherNode instance.
	 */
	public static async create(context: AudioContext): Promise<BitcrusherNode> {
		try {
			// Load the worklet processor from the specified URL
			await context.audioWorklet.addModule(
				'scripts/esm/audio/processors/bitcrusher/BitcrusherProcessor.js',
			);
			// Once loaded, create an instance of the node
			return new BitcrusherNode(context);
		} catch (e) {
			console.error('Failed to load bitcrusher audio worklet', e);
			throw e;
		}
	}

	/**
	 * The number of bits to quantize the audio signal to.
	 * Range: 1 to 16. Lower = more distortion.
	 */
	get bitDepth(): AudioParam | undefined {
		return this.parameters.get('bitDepth');
	}

	/**
	 * The factor by which to reduce the sample rate.
	 * A value of 1 means no downsampling.
	 * Range: 1 to 40.
	 */
	get downsampling(): AudioParam | undefined {
		return this.parameters.get('downsampling');
	}
}


================================================
FILE: dev-utils/scripts/audio/processors/bitcrusher/BitcrusherProcessor.ts
================================================
// dev-utils/scripts/audio/processors/bitcrusher/BitcrusherProcessor.ts

import type { AudioParamDescriptor } from '../worklet-types';

/*
 * These need to be declared in every audio worklet processor file,
 * because apparently our typescript setup doesn't have the
 * AudioWorkletGlobalScope, and nothing I do will add it.
 */

declare abstract class AudioWorkletProcessor {
	static get parameterDescriptors(): AudioParamDescriptor[];
	constructor(options?: any);
	abstract process(
		inputs: Float32Array[][],
		outputs: Float32Array[][],
		parameters: Record<string, Float32Array>,
	): boolean;
}

declare function registerProcessor(name: string, processorCtor: typeof AudioWorkletProcessor): void;

/** Parameters for the BitcrusherProcessor. */
interface BitcrusherParameters extends Record<string, Float32Array> {
	bitDepth: Float32Array;
	downsampling: Float32Array;
}

/** An AudioWorkletProcessor that applies a bitcrusher and/or downsampling effect to audio. */
class BitcrusherProcessor extends AudioWorkletProcessor {
	static override get parameterDescriptors(): AudioParamDescriptor[] {
		return [
			{
				name: 'bitDepth',
				defaultValue: 8,
				minValue: 1,
				maxValue: 16,
				automationRate: 'k-rate',
			},
			{
				name: 'downsampling',
				defaultValue: 1,
				minValue: 1,
				maxValue: 40,
				automationRate: 'k-rate',
			},
		];
	}

	private phase = 0;
	private lastSampleValue = 0;

	process(
		inputs: Float32Array[][],
		outputs: Float32Array[][],
		parameters: BitcrusherParameters,
	): boolean {
		const input = inputs[0];
		const output = outputs[0];
		if (!input || !output) return true; // Nothing to process

		const bitDepth = parameters['bitDepth'];
		const downsampling = parameters['downsampling'];

		for (let channel = 0; channel < input.length; ++channel) {
			const inputChannel = input[channel];
			const outputChannel = output[channel];
			if (!inputChannel || !outputChannel) continue;

			for (let i = 0; i < inputChannel.length; ++i) {
				const bitDepthValue = bitDepth.length > 1 ? bitDepth[i]! : bitDepth[0]!;
				const downsamplingValue =
					downsampling.length > 1 ? downsampling[i]! : downsampling[0]!;

				// Downsampling
				if (this.phase % downsamplingValue < 1) this.lastSampleValue = inputChannel[i]!;

				// Bit-depth reduction
				const step = Math.pow(0.5, bitDepthValue);
				outputChannel[i] = step * Math.floor(this.lastSampleValue / step + 0.5);

				this.phase++;
			}
		}

		return true;
	}
}

registerProcessor('bitcrusher-processor', BitcrusherProcessor);


================================================
FILE: dev-utils/scripts/clientEventDispatcher.ts
================================================

/**
 * This event dispatcher will only dispatch events in the browser environment.
 * 
 * NOTHING will happen if it is imported, or its methods are called, in the Node.js environment.
 */


/** Whether the current environment is a browser. */
const isBrowser = typeof window !== 'undefined' && typeof window.dispatchEvent === 'function';
const target: Window = isBrowser ? window : null!;


/**
 * Dispatches an event with the given name. If data is provided, a CustomEvent is dispatched
 * with the data in the detail property. Otherwise, a standard Event is dispatched.
 * @param eventName The name of the event to dispatch.
 * @param [data] Optional data to include in the event's detail property.
 */
function dispatch(eventName: string, data?: any): void {
	if (!isBrowser) return;
	if (data !== undefined) target.dispatchEvent(new CustomEvent(eventName, { detail: data }));
	else target.dispatchEvent(new Event(eventName));
}

/**
 * Listens for an event with the given name.
 * @param eventName The name of the event to listen for.
 * @param callback The callback function to invoke when the event occurs.
 */
function listen(eventName: string, callback: (event: CustomEvent) => void): void {
	if (!isBrowser) return;
	target.addEventListener(eventName, callback as EventListener);
}

/**
 * Removes a previously added event listener.
 * @param eventName The name of the event.
 * @param callback The callback function to remove.
 */
function removeListener(eventName: string, callback: (event: CustomEvent) => void): void {
	if (!isBrowser) return;
	target.removeEventListener(eventName, callback as EventListener);
}

export default {
	dispatch,
	listen,
	removeListener
};

================================================
FILE: dev-utils/scripts/events.ts
================================================
// dev-utils/scripts/events.ts


/**
 * A script that was intended for managing gamefile events for games
 * on both client and server ends.
 * 
 * @author Idontuse
 */

// Disabling this  cause will be using func types lots
/* eslint-disable no-unused-vars */

import type gamefile from "../../src/client/scripts/esm/chess/logic/gamefile";

type ExtractArr<T extends any[]> = T extends (infer U)[] ? U : never
 
interface Eventlist {
	[eventName: string]: ((...args: any[]) => boolean)[]
}

function runEvent<E extends Eventlist, N extends keyof E, A extends Parameters<ExtractArr<E[N]>>>(eventlist: E, event: N, ...args: A): boolean {
	const funcs = eventlist[event];
	if (funcs === undefined) return false;
	for (const f of funcs) {
		// @ts-ignore ts thinks that the paramters of the function "could" not match the parameters of the function
		if (f(...args)) {
			return true;
		}
	}
	return false;
}

function addEventListener<E extends Eventlist, N extends keyof E, L extends ExtractArr<E[N]>>(eventlist: E, event: N, listener: L): void {
	const listeners = eventlist[event];
	if (listeners === undefined) {
		// @ts-ignore it should work but ts thinks there could be a specific subtype where this errors
		// IT WILL ONLY BE AN ARRAY OF FUNCTIONS NO SUBTYPES NEEDED
		eventlist[event] = [listener];
		return;
	}
	listeners.push(listener);
	return;
}

function removeEventListener<E extends Eventlist, N extends keyof E, L extends ExtractArr<E[N]>>(eventlist: E, event: N, listener: L): boolean {
	const listeners = eventlist[event];
	if (listeners === undefined) {
		return false;
	}
	for (let i = 0; i < listeners.length; i++ ) {
		if (listeners[i] !== listener) continue;
		listeners.splice(i, 1);
		return true;
	}
	return false;
}

// import type { RegenerateHook } from "./organizedpieces";

// interface GameEvents extends Eventlist {
// 	// Runs when organizedPieces regenerate, DO NOT INTERRUPT.
// 	regenerateLists: RegenerateHook[]
// }

export type {
	Eventlist,

	// GameEvents
};

export default {
	addEventListener,
	removeEventListener,
	
	runEvent,
};

================================================
FILE: dev-utils/scripts/gl-matrix.js
================================================

/*!
@fileoverview gl-matrix - High performance matrix and vector operations
@author Brandon Jones
@author Colin MacKenzie IV
@version 3.4.0

Copyright (c) 2015-2021, Brandon Jones, Colin MacKenzie IV.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

*/
(function (global, factory) {
  typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
  typeof define === 'function' && define.amd ? define(['exports'], factory) :
  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.glMatrix = {}));
})(this, (function (exports) { 'use strict';

  /**
   * Common utilities
   * @module glMatrix
   */
  // Configuration Constants
  var EPSILON = 0.000001;
  var ARRAY_TYPE = typeof Float32Array !== "undefined" ? Float32Array : Array;
  var RANDOM = Math.random;
  var ANGLE_ORDER = "zyx";
  /**
   * Sets the type of array used when creating new vectors and matrices
   *
   * @param {Float32ArrayConstructor | ArrayConstructor} type Array type, such as Float32Array or Array
   */

  function setMatrixArrayType(type) {
    ARRAY_TYPE = type;
  }
  var degree = Math.PI / 180;
  /**
   * Convert Degree To Radian
   *
   * @param {Number} a Angle in Degrees
   */

  function toRadian(a) {
    return a * degree;
  }
  /**
   * Tests whether the arguments have approximately the same value, within an absolute
   * or relative tolerance of glMatrix.EPSILON (an absolute tolerance is used for values less
   * than or equal to 1.0, and a relative tolerance is used for larger values)
   *
   * @param {Number} a The first number to test.
   * @param {Number} b The second number to test.
   * @returns {Boolean} True if the numbers are approximately equal, false otherwise.
   */

  function equals$9(a, b) {
    return Math.abs(a - b) <= EPSILON * Math.max(1.0, Math.abs(a), Math.abs(b));
  }
  if (!Math.hypot) Math.hypot = function () {
    var y = 0,
        i = arguments.length;

    while (i--) {
      y += arguments[i] * arguments[i];
    }

    return Math.sqrt(y);
  };

  var common = /*#__PURE__*/Object.freeze({
    __proto__: null,
    EPSILON: EPSILON,
    get ARRAY_TYPE () { return ARRAY_TYPE; },
    RANDOM: RANDOM,
    ANGLE_ORDER: ANGLE_ORDER,
    setMatrixArrayType: setMatrixArrayType,
    toRadian: toRadian,
    equals: equals$9
  });

  /**
   * 2x2 Matrix
   * @module mat2
   */

  /**
   * Creates a new identity mat2
   *
   * @returns {mat2} a new 2x2 matrix
   */

  function create$8() {
    var out = new ARRAY_TYPE(4);

    if (ARRAY_TYPE != Float32Array) {
      out[1] = 0;
      out[2] = 0;
    }

    out[0] = 1;
    out[3] = 1;
    return out;
  }
  /**
   * Creates a new mat2 initialized with values from an existing matrix
   *
   * @param {ReadonlyMat2} a matrix to clone
   * @returns {mat2} a new 2x2 matrix
   */

  function clone$8(a) {
    var out = new ARRAY_TYPE(4);
    out[0] = a[0];
    out[1] = a[1];
    out[2] = a[2];
    out[3] = a[3];
    return out;
  }
  /**
   * Copy the values from one mat2 to another
   *
   * @param {mat2} out the receiving matrix
   * @param {ReadonlyMat2} a the source matrix
   * @returns {mat2} out
   */

  function copy$8(out, a) {
    out[0] = a[0];
    out[1] = a[1];
    out[2] = a[2];
    out[3] = a[3];
    return out;
  }
  /**
   * Set a mat2 to the identity matrix
   *
   * @param {mat2} out the receiving matrix
   * @returns {mat2} out
   */

  function identity$5(out) {
    out[0] = 1;
    out[1] = 0;
    out[2] = 0;
    out[3] = 1;
    return out;
  }
  /**
   * Create a new mat2 with the given values
   *
   * @param {Number} m00 Component in column 0, row 0 position (index 0)
   * @param {Number} m01 Component in column 0, row 1 position (index 1)
   * @param {Number} m10 Component in column 1, row 0 position (index 2)
   * @param {Number} m11 Component in column 1, row 1 position (index 3)
   * @returns {mat2} out A new 2x2 matrix
   */

  function fromValues$8(m00, m01, m10, m11) {
    var out = new ARRAY_TYPE(4);
    out[0] = m00;
    out[1] = m01;
    out[2] = m10;
    out[3] = m11;
    return out;
  }
  /**
   * Set the components of a mat2 to the given values
   *
   * @param {mat2} out the receiving matrix
   * @param {Number} m00 Component in column 0, row 0 position (index 0)
   * @param {Number} m01 Component in column 0, row 1 position (index 1)
   * @param {Number} m10 Component in column 1, row 0 position (index 2)
   * @param {Number} m11 Component in column 1, row 1 position (index 3)
   * @returns {mat2} out
   */

  function set$8(out, m00, m01, m10, m11) {
    out[0] = m00;
    out[1] = m01;
    out[2] = m10;
    out[3] = m11;
    return out;
  }
  /**
   * Transpose the values of a mat2
   *
   * @param {mat2} out the receiving matrix
   * @param {ReadonlyMat2} a the source matrix
   * @returns {mat2} out
   */

  function transpose$2(out, a) {
    // If we are transposing ourselves we can skip a few steps but have to cache
    // some values
    if (out === a) {
      var a1 = a[1];
      out[1] = a[2];
      out[2] = a1;
    } else {
      out[0] = a[0];
      out[1] = a[2];
      out[2] = a[1];
      out[3] = a[3];
    }

    return out;
  }
  /**
   * Inverts a mat2
   *
   * @param {mat2} out the receiving matrix
   * @param {ReadonlyMat2} a the source matrix
   * @returns {mat2} out
   */

  function invert$5(out, a) {
    var a0 = a[0],
        a1 = a[1],
        a2 = a[2],
        a3 = a[3]; // Calculate the determinant

    var det = a0 * a3 - a2 * a1;

    if (!det) {
      return null;
    }

    det = 1.0 / det;
    out[0] = a3 * det;
    out[1] = -a1 * det;
    out[2] = -a2 * det;
    out[3] = a0 * det;
    return out;
  }
  /**
   * Calculates the adjugate of a mat2
   *
   * @param {mat2} out the receiving matrix
   * @param {ReadonlyMat2} a the source matrix
   * @returns {mat2} out
   */

  function adjoint$2(out, a) {
    // Caching this value is necessary if out == a
    var a0 = a[0];
    out[0] = a[3];
    out[1] = -a[1];
    out[2] = -a[2];
    out[3] = a0;
    return out;
  }
  /**
   * Calculates the determinant of a mat2
   *
   * @param {ReadonlyMat2} a the source matrix
   * @returns {Number} determinant of a
   */

  function determinant$3(a) {
    return a[0] * a[3] - a[2] * a[1];
  }
  /**
   * Multiplies two mat2's
   *
   * @param {mat2} out the receiving matrix
   * @param {ReadonlyMat2} a the first operand
   * @param {ReadonlyMat2} b the second operand
   * @returns {mat2} out
   */

  function multiply$8(out, a, b) {
    var a0 = a[0],
        a1 = a[1],
        a2 = a[2],
        a3 = a[3];
    var b0 = b[0],
        b1 = b[1],
        b2 = b[2],
        b3 = b[3];
    out[0] = a0 * b0 + a2 * b1;
    out[1] = a1 * b0 + a3 * b1;
    out[2] = a0 * b2 + a2 * b3;
    out[3] = a1 * b2 + a3 * b3;
    return out;
  }
  /**
   * Rotates a mat2 by the given angle
   *
   * @param {mat2} out the receiving matrix
   * @param {ReadonlyMat2} a the matrix to rotate
   * @param {Number} rad the angle to rotate the matrix by
   * @returns {mat2} out
   */

  function rotate$4(out, a, rad) {
    var a0 = a[0],
        a1 = a[1],
        a2 = a[2],
        a3 = a[3];
    var s = Math.sin(rad);
    var c = Math.cos(rad);
    out[0] = a0 * c + a2 * s;
    out[1] = a1 * c + a3 * s;
    out[2] = a0 * -s + a2 * c;
    out[3] = a1 * -s + a3 * c;
    return out;
  }
  /**
   * Scales the mat2 by the dimensions in the given vec2
   *
   * @param {mat2} out the receiving matrix
   * @param {ReadonlyMat2} a the matrix to rotate
   * @param {ReadonlyVec2} v the vec2 to scale the matrix by
   * @returns {mat2} out
   **/

  function scale$8(out, a, v) {
    var a0 = a[0],
        a1 = a[1],
        a2 = a[2],
        a3 = a[3];
    var v0 = v[0],
        v1 = v[1];
    out[0] = a0 * v0;
    out[1] = a1 * v0;
    out[2] = a2 * v1;
    out[3] = a3 * v1;
    return out;
  }
  /**
   * Creates a matrix from a given angle
   * This is equivalent to (but much faster than):
   *
   *     mat2.identity(dest);
   *     mat2.rotate(dest, dest, rad);
   *
   * @param {mat2} out mat2 receiving operation result
   * @param {Number} rad the angle to rotate the matrix by
   * @returns {mat2} out
   */

  function fromRotation$4(out, rad) {
    var s = Math.sin(rad);
    var c = Math.cos(rad);
    out[0] = c;
    out[1] = s;
    out[2] = -s;
    out[3] = c;
    return out;
  }
  /**
   * Creates a matrix from a vector scaling
   * This is equivalent to (but much faster than):
   *
   *     mat2.identity(dest);
   *     mat2.scale(dest, dest, vec);
   *
   * @param {mat2} out mat2 receiving operation result
   * @param {ReadonlyVec2} v Scaling vector
   * @returns {mat2} out
   */

  function fromScaling$3(out, v) {
    out[0] = v[0];
    out[1] = 0;
    out[2] = 0;
    out[3] = v[1];
    return out;
  }
  /**
   * Returns a string representation of a mat2
   *
   * @param {ReadonlyMat2} a matrix to represent as a string
   * @returns {String} string representation of the matrix
   */

  function str$8(a) {
    return "mat2(" + a[0] + ", " + a[1] + ", " + a[2] + ", " + a[3] + ")";
  }
  /**
   * Returns Frobenius norm of a mat2
   *
   * @param {ReadonlyMat2} a the matrix to calculate Frobenius norm of
   * @returns {Number} Frobenius norm
   */

  function frob$3(a) {
    return Math.hypot(a[0], a[1], a[2], a[3]);
  }
  /**
   * Returns L, D and U matrices (Lower triangular, Diagonal and Upper triangular) by factorizing the input matrix
   * @param {ReadonlyMat2} L the lower triangular matrix
   * @param {ReadonlyMat2} D the diagonal matrix
   * @param {ReadonlyMat2} U the upper triangular matrix
   * @param {ReadonlyMat2} a the input matrix to factorize
   */

  function LDU(L, D, U, a) {
    L[2] = a[2] / a[0];
    U[0] = a[0];
    U[1] = a[1];
    U[3] = a[3] - L[2] * U[1];
    return [L, D, U];
  }
  /**
   * Adds two mat2's
   *
   * @param {mat2} out the receiving matrix
   * @param {ReadonlyMat2} a the first operand
   * @param {ReadonlyMat2} b the second operand
   * @returns {mat2} out
   */

  function add$8(out, a, b) {
    out[0] = a[0] + b[0];
    out[1] = a[1] + b[1];
    out[2] = a[2] + b[2];
    out[3] = a[3] + b[3];
    return out;
  }
  /**
   * Subtracts matrix b from matrix a
   *
   * @param {mat2} out the receiving matrix
   * @param {ReadonlyMat2} a the first operand
   * @param {ReadonlyMat2} b the second operand
   * @returns {mat2} out
   */

  function subtract$6(out, a, b) {
    out[0] = a[0] - b[0];
    out[1] = a[1] - b[1];
    out[2] = a[2] - b[2];
    out[3] = a[3] - b[3];
    return out;
  }
  /**
   * Returns whether the matrices have exactly the same elements in the same position (when compared with ===)
   *
   * @param {ReadonlyMat2} a The first matrix.
   * @param {ReadonlyMat2} b The second matrix.
   * @returns {Boolean} True if the matrices are equal, false otherwise.
   */

  function exactEquals$8(a, b) {
    return a[0] === b[0] && a[1] === b[1] && a[2] === b[2] && a[3] === b[3];
  }
  /**
   * Returns whether the matrices have approximately the same elements in the same position.
   *
   * @param {ReadonlyMat2} a The first matrix.
   * @param {ReadonlyMat2} b The second matrix.
   * @returns {Boolean} True if the matrices are equal, false otherwise.
   */

  function equals$8(a, b) {
    var a0 = a[0],
        a1 = a[1],
        a2 = a[2],
        a3 = a[3];
    var b0 = b[0],
        b1 = b[1],
        b2 = b[2],
        b3 = b[3];
    return Math.abs(a0 - b0) <= EPSILON * Math.max(1.0, Math.abs(a0), Math.abs(b0)) && Math.abs(a1 - b1) <= EPSILON * Math.max(1.0, Math.abs(a1), Math.abs(b1)) && Math.abs(a2 - b2) <= EPSILON * Math.max(1.0, Math.abs(a2), Math.abs(b2)) && Math.abs(a3 - b3) <= EPSILON * Math.max(1.0, Math.abs(a3), Math.abs(b3));
  }
  /**
   * Multiply each element of the matrix by a scalar.
   *
   * @param {mat2} out the receiving matrix
   * @param {ReadonlyMat2} a the matrix to scale
   * @param {Number} b amount to scale the matrix's elements by
   * @returns {mat2} out
   */

  function multiplyScalar$3(out, a, b) {
    out[0] = a[0] * b;
    out[1] = a[1] * b;
    out[2] = a[2] * b;
    out[3] = a[3] * b;
    return out;
  }
  /**
   * Adds two mat2's after multiplying each element of the second operand by a scalar value.
   *
   * @param {mat2} out the receiving vector
   * @param {ReadonlyMat2} a the first operand
   * @param {ReadonlyMat2} b the second operand
   * @param {Number} scale the amount to scale b's elements by before adding
   * @returns {mat2} out
   */

  function multiplyScalarAndAdd$3(out, a, b, scale) {
    out[0] = a[0] + b[0] * scale;
    out[1] = a[1] + b[1] * scale;
    out[2] = a[2] + b[2] * scale;
    out[3] = a[3] + b[3] * scale;
    return out;
  }
  /**
   * Alias for {@link mat2.multiply}
   * @function
   */

  var mul$8 = multiply$8;
  /**
   * Alias for {@link mat2.subtract}
   * @function
   */

  var sub$6 = subtract$6;

  var mat2 = /*#__PURE__*/Object.freeze({
    __proto__: null,
    create: create$8,
    clone: clone$8,
    copy: copy$8,
    identity: identity$5,
    fromValues: fromValues$8,
    set: set$8,
    transpose: transpose$2,
    invert: invert$5,
    adjoint: adjoint$2,
    determinant: determinant$3,
    multiply: multiply$8,
    rotate: rotate$4,
    scale: scale$8,
    fromRotation: fromRotation$4,
    fromScaling: fromScaling$3,
    str: str$8,
    frob: frob$3,
    LDU: LDU,
    add: add$8,
    subtract: subtract$6,
    exactEquals: exactEquals$8,
    equals: equals$8,
    multiplyScalar: multiplyScalar$3,
    multiplyScalarAndAdd: multiplyScalarAndAdd$3,
    mul: mul$8,
    sub: sub$6
  });

  /**
   * 2x3 Matrix
   * @module mat2d
   * @description
   * A mat2d contains six elements defined as:
   * <pre>
   * [a, b,
   *  c, d,
   *  tx, ty]
   * </pre>
   * This is a short form for the 3x3 matrix:
   * <pre>
   * [a, b, 0,
   *  c, d, 0,
   *  tx, ty, 1]
   * </pre>
   * The last column is ignored so the array is shorter and operations are faster.
   */

  /**
   * Creates a new identity mat2d
   *
   * @returns {mat2d} a new 2x3 matrix
   */

  function create$7() {
    var out = new ARRAY_TYPE(6);

    if (ARRAY_TYPE != Float32Array) {
      out[1] = 0;
      out[2] = 0;
      out[4] = 0;
      out[5] = 0;
    }

    out[0] = 1;
    out[3] = 1;
    return out;
  }
  /**
   * Creates a new mat2d initialized with values from an existing matrix
   *
   * @param {ReadonlyMat2d} a matrix to clone
   * @returns {mat2d} a new 2x3 matrix
   */

  function clone$7(a) {
    var out = new ARRAY_TYPE(6);
    out[0] = a[0];
    out[1] = a[1];
    out[2] = a[2];
    out[3] = a[3];
    out[4] = a[4];
    out[5] = a[5];
    return out;
  }
  /**
   * Copy the values from one mat2d to another
   *
   * @param {mat2d} out the receiving matrix
   * @param {ReadonlyMat2d} a the source matrix
   * @returns {mat2d} out
   */

  function copy$7(out, a) {
    out[0] = a[0];
    out[1] = a[1];
    out[2] = a[2];
    out[3] = a[3];
    out[4] = a[4];
    out[5] = a[5];
    return out;
  }
  /**
   * Set a mat2d to the identity matrix
   *
   * @param {mat2d} out the receiving matrix
   * @returns {mat2d} out
   */

  function identity$4(out) {
    out[0] = 1;
    out[1] = 0;
    out[2] = 0;
    out[3] = 1;
    out[4] = 0;
    out[5] = 0;
    return out;
  }
  /**
   * Create a new mat2d with the given values
   *
   * @param {Number} a Component A (index 0)
   * @param {Number} b Component B (index 1)
   * @param {Number} c Component C (index 2)
   * @param {Number} d Component D (index 3)
   * @param {Number} tx Component TX (index 4)
   * @param {Number} ty Component TY (index 5)
   * @returns {mat2d} A new mat2d
   */

  function fromValues$7(a, b, c, d, tx, ty) {
    var out = new ARRAY_TYPE(6);
    out[0] = a;
    out[1] = b;
    out[2] = c;
    out[3] = d;
    out[4] = tx;
    out[5] = ty;
    return out;
  }
  /**
   * Set the components of a mat2d to the given values
   *
   * @param {mat2d} out the receiving matrix
   * @param {Number} a Component A (index 0)
   * @param {Number} b Component B (index 1)
   * @param {Number} c Component C (index 2)
   * @param {Number} d Component D (index 3)
   * @param {Number} tx Component TX (index 4)
   * @param {Number} ty Component TY (index 5)
   * @returns {mat2d} out
   */

  function set$7(out, a, b, c, d, tx, ty) {
    out[0] = a;
    out[1] = b;
    out[2] = c;
    out[3] = d;
    out[4] = tx;
    out[5] = ty;
    return out;
  }
  /**
   * Inverts a mat2d
   *
   * @param {mat2d} out the receiving matrix
   * @param {ReadonlyMat2d} a the source matrix
   * @returns {mat2d} out
   */

  function invert$4(out, a) {
    var aa = a[0],
        ab = a[1],
        ac = a[2],
        ad = a[3];
    var atx = a[4],
        aty = a[5];
    var det = aa * ad - ab * ac;

    if (!det) {
      return null;
    }

    det = 1.0 / det;
    out[0] = ad * det;
    out[1] = -ab * det;
    out[2] = -ac * det;
    out[3] = aa * det;
    out[4] = (ac * aty - ad * atx) * det;
    out[5] = (ab * atx - aa * aty) * det;
    return out;
  }
  /**
   * Calculates the determinant of a mat2d
   *
   * @param {ReadonlyMat2d} a the source matrix
   * @returns {Number} determinant of a
   */

  function determinant$2(a) {
    return a[0] * a[3] - a[1] * a[2];
  }
  /**
   * Multiplies two mat2d's
   *
   * @param {mat2d} out the receiving matrix
   * @param {ReadonlyMat2d} a the first operand
   * @param {ReadonlyMat2d} b the second operand
   * @returns {mat2d} out
   */

  function multiply$7(out, a, b) {
    var a0 = a[0],
        a1 = a[1],
        a2 = a[2],
        a3 = a[3],
        a4 = a[4],
        a5 = a[5];
    var b0 = b[0],
        b1 = b[1],
        b2 = b[2],
        b3 = b[3],
        b4 = b[4],
        b5 = b[5];
    out[0] = a0 * b0 + a2 * b1;
    out[1] = a1 * b0 + a3 * b1;
    out[2] = a0 * b2 + a2 * b3;
    out[3] = a1 * b2 + a3 * b3;
    out[4] = a0 * b4 + a2 * b5 + a4;
    out[5] = a1 * b4 + a3 * b5 + a5;
    return out;
  }
  /**
   * Rotates a mat2d by the given angle
   *
   * @param {mat2d} out the receiving matrix
   * @param {ReadonlyMat2d} a the matrix to rotate
   * @param {Number} rad the angle to rotate the matrix by
   * @returns {mat2d} out
   */

  function rotate$3(out, a, rad) {
    var a0 = a[0],
        a1 = a[1],
        a2 = a[2],
        a3 = a[3],
        a4 = a[4],
        a5 = a[5];
    var s = Math.sin(rad);
    var c = Math.cos(rad);
    out[0] = a0 * c + a2 * s;
    out[1] = a1 * c + a3 * s;
    out[2] = a0 * -s + a2 * c;
    out[3] = a1 * -s + a3 * c;
    out[4] = a4;
    out[5] = a5;
    return out;
  }
  /**
   * Scales the mat2d by the dimensions in the given vec2
   *
   * @param {mat2d} out the receiving matrix
   * @param {ReadonlyMat2d} a the matrix to translate
   * @param {ReadonlyVec2} v the vec2 to scale the matrix by
   * @returns {mat2d} out
   **/

  function scale$7(out, a, v) {
    var a0 = a[0],
        a1 = a[1],
        a2 = a[2],
        a3 = a[3],
        a4 = a[4],
        a5 = a[5];
    var v0 = v[0],
        v1 = v[1];
    out[0] = a0 * v0;
    out[1] = a1 * v0;
    out[2] = a2 * v1;
    out[3] = a3 * v1;
    out[4] = a4;
    out[5] = a5;
    return out;
  }
  /**
   * Translates the mat2d by the dimensions in the given vec2
   *
   * @param {mat2d} out the receiving matrix
   * @param {ReadonlyMat2d} a the matrix to translate
   * @param {ReadonlyVec2} v the vec2 to translate the matrix by
   * @returns {mat2d} out
   **/

  function translate$3(out, a, v) {
    var a0 = a[0],
        a1 = a[1],
        a2 = a[2],
        a3 = a[3],
        a4 = a[4],
        a5 = a[5];
    var v0 = v[0],
        v1 = v[1];
    out[0] = a0;
    out[1] = a1;
    out[2] = a2;
    out[3] = a3;
    out[4] = a0 * v0 + a2 * v1 + a4;
    out[5] = a1 * v0 + a3 * v1 + a5;
    return out;
  }
  /**
   * Creates a matrix from a given angle
   * This is equivalent to (but much faster than):
   *
   *     mat2d.identity(dest);
   *     mat2d.rotate(dest, dest, rad);
   *
   * @param {mat2d} out mat2d receiving operation result
   * @param {Number} rad the angle to rotate the matrix by
   * @returns {mat2d} out
   */

  function fromRotation$3(out, rad) {
    var s = Math.sin(rad),
        c = Math.cos(rad);
    out[0] = c;
    out[1] = s;
    out[2] = -s;
    out[3] = c;
    out[4] = 0;
    out[5] = 0;
    return out;
  }
  /**
   * Creates a matrix from a vector scaling
   * This is equivalent to (but much faster than):
   *
   *     mat2d.identity(dest);
   *     mat2d.scale(dest, dest, vec);
   *
   * @param {mat2d} out mat2d receiving operation result
   * @param {ReadonlyVec2} v Scaling vector
   * @returns {mat2d} out
   */

  function fromScaling$2(out, v) {
    out[0] = v[0];
    out[1] = 0;
    out[2] = 0;
    out[3] = v[1];
    out[4] = 0;
    out[5] = 0;
    return out;
  }
  /**
   * Creates a matrix from a vector translation
   * This is equivalent to (but much faster than):
   *
   *     mat2d.identity(dest);
   *     mat2d.translate(dest, dest, vec);
   *
   * @param {mat2d} out mat2d receiving operation result
   * @param {ReadonlyVec2} v Translation vector
   * @returns {mat2d} out
   */

  function fromTranslation$3(out, v) {
    out[0] = 1;
    out[1] = 0;
    out[2] = 0;
    out[3] = 1;
    out[4] = v[0];
    out[5] = v[1];
    return out;
  }
  /**
   * Returns a string representation of a mat2d
   *
   * @param {ReadonlyMat2d} a matrix to represent as a string
   * @returns {String} string representation of the matrix
   */

  function str$7(a) {
    return "mat2d(" + a[0] + ", " + a[1] + ", " + a[2] + ", " + a[3] + ", " + a[4] + ", " + a[5] + ")";
  }
  /**
   * Returns Frobenius norm of a mat2d
   *
   * @param {ReadonlyMat2d} a the matrix to calculate Frobenius norm of
   * @returns {Number} Frobenius norm
   */

  function frob$2(a) {
    return Math.hypot(a[0], a[1], a[2], a[3], a[4], a[5], 1);
  }
  /**
   * Adds two mat2d's
   *
   * @param {mat2d} out the receiving matrix
   * @param {ReadonlyMat2d} a the first operand
   * @param {ReadonlyMat2d} b the second operand
   * @returns {mat2d} out
   */

  function add$7(out, a, b) {
    out[0] = a[0] + b[0];
    out[1] = a[1] + b[1];
    out[2] = a[2] + b[2];
    out[3] = a[3] + b[3];
    out[4] = a[4] + b[4];
    out[5] = a[5] + b[5];
    return out;
  }
  /**
   * Subtracts matrix b from matrix a
   *
   * @param {mat2d} out the receiving matrix
   * @param {ReadonlyMat2d} a the first operand
   * @param {ReadonlyMat2d} b the second operand
   * @returns {mat2d} out
   */

  function subtract$5(out, a, b) {
    out[0] = a[0] - b[0];
    out[1] = a[1] - b[1];
    out[2] = a[2] - b[2];
    out[3] = a[3] - b[3];
    out[4] = a[4] - b[4];
    out[5] = a[5] - b[5];
    return out;
  }
  /**
   * Multiply each element of the matrix by a scalar.
   *
   * @param {mat2d} out the receiving matrix
   * @param {ReadonlyMat2d} a the matrix to scale
   * @param {Number} b amount to scale the matrix's elements by
   * @returns {mat2d} out
   */

  function multiplyScalar$2(out, a, b) {
    out[0] = a[0] * b;
    out[1] = a[1] * b;
    out[2] = a[2] * b;
    out[3] = a[3] * b;
    out[4] = a[4] * b;
    out[5] = a[5] * b;
    return out;
  }
  /**
   * Adds two mat2d's after multiplying each element of the second operand by a scalar value.
   *
   * @param {mat2d} out the receiving vector
   * @param {ReadonlyMat2d} a the first operand
   * @param {ReadonlyMat2d} b the second operand
   * @param {Number} scale the amount to scale b's elements by before adding
   * @returns {mat2d} out
   */

  function multiplyScalarAndAdd$2(out, a, b, scale) {
    out[0] = a[0] + b[0] * scale;
    out[1] = a[1] + b[1] * scale;
    out[2] = a[2] + b[2] * scale;
    out[3] = a[3] + b[3] * scale;
    out[4] = a[4] + b[4] * scale;
    out[5] = a[5] + b[5] * scale;
    return out;
  }
  /**
   * Returns whether the matrices have exactly the same elements in the same position (when compared with ===)
   *
   * @param {ReadonlyMat2d} a The first matrix.
   * @param {ReadonlyMat2d} b The second matrix.
   * @returns {Boolean} True if the matrices are equal, false otherwise.
   */

  function exactEquals$7(a, b) {
    return a[0] === b[0] && a[1] === b[1] && a[2] === b[2] && a[3] === b[3] && a[4] === b[4] && a[5] === b[5];
  }
  /**
   * Returns whether the matrices have approximately the same elements in the same position.
   *
   * @param {ReadonlyMat2d} a The first matrix.
   * @param {ReadonlyMat2d} b The second matrix.
   * @returns {Boolean} True if the matrices are equal, false otherwise.
   */

  function equals$7(a, b) {
    var a0 = a[0],
        a1 = a[1],
        a2 = a[2],
        a3 = a[3],
        a4 = a[4],
        a5 = a[5];
    var b0 = b[0],
        b1 = b[1],
        b2 = b[2],
        b3 = b[3],
        b4 = b[4],
        b5 = b[5];
    return Math.abs(a0 - b0) <= EPSILON * Math.max(1.0, Math.abs(a0), Math.abs(b0)) && Math.abs(a1 - b1) <= EPSILON * Math.max(1.0, Math.abs(a1), Math.abs(b1)) && Math.abs(a2 - b2) <= EPSILON * Math.max(1.0, Math.abs(a2), Math.abs(b2)) && Math.abs(a3 - b3) <= EPSILON * Math.max(1.0, Math.abs(a3), Math.abs(b3)) && Math.abs(a4 - b4) <= EPSILON * Math.max(1.0, Math.abs(a4), Math.abs(b4)) && Math.abs(a5 - b5) <= EPSILON * Math.max(1.0, Math.abs(a5), Math.abs(b5));
  }
  /**
   * Alias for {@link mat2d.multiply}
   * @function
   */

  var mul$7 = multiply$7;
  /**
   * Alias for {@link mat2d.subtract}
   * @function
   */

  var sub$5 = subtract$5;

  var mat2d = /*#__PURE__*/Object.freeze({
    __proto__: null,
    create: create$7,
    clone: clone$7,
    copy: copy$7,
    identity: identity$4,
    fromValues: fromValues$7,
    set: set$7,
    invert: invert$4,
    determinant: determinant$2,
    multiply: multiply$7,
    rotate: rotate$3,
    scale: scale$7,
    translate: translate$3,
    fromRotation: fromRotation$3,
    fromScaling: fromScaling$2,
    fromTranslation: fromTranslation$3,
    str: str$7,
    frob: frob$2,
    add: add$7,
    subtract: subtract$5,
    multiplyScalar: multiplyScalar$2,
    multiplyScalarAndAdd: multiplyScalarAndAdd$2,
    exactEquals: exactEquals$7,
    equals: equals$7,
    mul: mul$7,
    sub: sub$5
  });

  /**
   * 3x3 Matrix
   * @module mat3
   */

  /**
   * Creates a new identity mat3
   *
   * @returns {mat3} a new 3x3 matrix
   */

  function create$6() {
    var out = new ARRAY_TYPE(9);

    if (ARRAY_TYPE != Float32Array) {
      out[1] = 0;
      out[2] = 0;
      out[3] = 0;
      out[5] = 0;
      out[6] = 0;
      out[7] = 0;
    }

    out[0] = 1;
    out[4] = 1;
    out[8] = 1;
    return out;
  }
  /**
   * Copies the upper-left 3x3 values into the given mat3.
   *
   * @param {mat3} out the receiving 3x3 matrix
   * @param {ReadonlyMat4} a   the source 4x4 matrix
   * @returns {mat3} out
   */

  function fromMat4$1(out, a) {
    out[0] = a[0];
    out[1] = a[1];
    out[2] = a[2];
    out[3] = a[4];
    out[4] = a[5];
    out[5] = a[6];
    out[6] = a[8];
    out[7] = a[9];
    out[8] = a[10];
    return out;
  }
  /**
   * Creates a new mat3 initialized with values from an existing matrix
   *
   * @param {ReadonlyMat3} a matrix to clone
   * @returns {mat3} a new 3x3 matrix
   */

  function clone$6(a) {
    var out = new ARRAY_TYPE(9);
    out[0] = a[0];
    out[1] = a[1];
    out[2] = a[2];
    out[3] = a[3];
    out[4] = a[4];
    out[5] = a[5];
    out[6] = a[6];
    out[7] = a[7];
    out[8] = a[8];
    return out;
  }
  /**
   * Copy the values from one mat3 to another
   *
   * @param {mat3} out the receiving matrix
   * @param {ReadonlyMat3} a the source matrix
   * @returns {mat3} out
   */

  function copy$6(out, a) {
    out[0] = a[0];
    out[1] = a[1];
    out[2] = a[2];
    out[3] = a[3];
    out[4] = a[4];
    out[5] = a[5];
    out[6] = a[6];
    out[7] = a[7];
    out[8] = a[8];
    return out;
  }
  /**
   * Create a new mat3 with the given values
   *
   * @param {Number} m00 Component in column 0, row 0 position (index 0)
   * @param {Number} m01 Component in column 0, row 1 position (index 1)
   * @param {Number} m02 Component in column 0, row 2 position (index 2)
   * @param {Number} m10 Component in column 1, row 0 position (index 3)
   * @param {Number} m11 Component in column 1, row 1 position (index 4)
   * @param {Number} m12 Component in column 1, row 2 position (index 5)
   * @param {Number} m20 Component in column 2, row 0 position (index 6)
   * @param {Number} m21 Component in column 2, row 1 position (index 7)
   * @param {Number} m22 Component in column 2, row 2 position (index 8)
   * @returns {mat3} A new mat3
   */

  function fromValues$6(m00, m01, m02, m10, m11, m12, m20, m21, m22) {
    var out = new ARRAY_TYPE(9);
    out[0] = m00;
    out[1] = m01;
    out[2] = m02;
    out[3] = m10;
    out[4] = m11;
    out[5] = m12;
    out[6] = m20;
    out[7] = m21;
    out[8] = m22;
    return out;
  }
  /**
   * Set the components of a mat3 to the given values
   *
   * @param {mat3} out the receiving matrix
   * @param {Number} m00 Component in column 0, row 0 position (index 0)
   * @param {Number} m01 Component in column 0, row 1 position (index 1)
   * @param {Number} m02 Component in column 0, row 2 position (index 2)
   * @param {Number} m10 Component in column 1, row 0 position (index 3)
   * @param {Number} m11 Component in column 1, row 1 position (index 4)
   * @param {Number} m12 Component in column 1, row 2 position (index 5)
   * @param {Number} m20 Component in column 2, row 0 position (index 6)
   * @param {Number} m21 Component in column 2, row 1 position (index 7)
   * @param {Number} m22 Component in column 2, row 2 position (index 8)
   * @returns {mat3} out
   */

  function set$6(out, m00, m01, m02, m10, m11, m12, m20, m21, m22) {
    out[0] = m00;
    out[1] = m01;
    out[2] = m02;
    out[3] = m10;
    out[4] = m11;
    out[5] = m12;
    out[6] = m20;
    out[7] = m21;
    out[8] = m22;
    return out;
  }
  /**
   * Set a mat3 to the identity matrix
   *
   * @param {mat3} out the receiving matrix
   * @returns {mat3} out
   */

  function identity$3(out) {
    out[0] = 1;
    out[1] = 0;
    out[2] = 0;
    out[3] = 0;
    out[4] = 1;
    out[5] = 0;
    out[6] = 0;
    out[7] = 0;
    out[8] = 1;
    return out;
  }
  /**
   * Transpose the values of a mat3
   *
   * @param {mat3} out the receiving matrix
   * @param {ReadonlyMat3} a the source matrix
   * @returns {mat3} out
   */

  function transpose$1(out, a) {
    // If we are transposing ourselves we can skip a few steps but have to cache some values
    if (out === a) {
      var a01 = a[1],
          a02 = a[2],
          a12 = a[5];
      out[1] = a[3];
      out[2] = a[6];
      out[3] = a01;
      out[5] = a[7];
      out[6] = a02;
      out[7] = a12;
    } else {
      out[0] = a[0];
      out[1] = a[3];
      out[2] = a[6];
      out[3] = a[1];
      out[4] = a[4];
      out[5] = a[7];
      out[6] = a[2];
      out[7] = a[5];
      out[8] = a[8];
    }

    return out;
  }
  /**
   * Inverts a mat3
   *
   * @param {mat3} out the receiving matrix
   * @param {ReadonlyMat3} a the source matrix
   * @returns {mat3} out
   */

  function invert$3(out, a) {
    var a00 = a[0],
        a01 = a[1],
        a02 = a[2];
    var a10 = a[3],
        a11 = a[4],
        a12 = a[5];
    var a20 = a[6],
        a21 = a[7],
        a22 = a[8];
    var b01 = a22 * a11 - a12 * a21;
    var b11 = -a22 * a10 + a12 * a20;
    var b21 = a21 * a10 - a11 * a20; // Calculate the determinant

    var det = a00 * b01 + a01 * b11 + a02 * b21;

    if (!det) {
      return null;
    }

    det = 1.0 / det;
    out[0] = b01 * det;
    out[1] = (-a22 * a01 + a02 * a21) * det;
    out[2] = (a12 * a01 - a02 * a11) * det;
    out[3] = b11 * det;
    out[4] = (a22 * a00 - a02 * a20) * det;
    out[5] = (-a12 * a00 + a02 * a10) * det;
    out[6] = b21 * det;
    out[7] = (-a21 * a00 + a01 * a20) * det;
    out[8] = (a11 * a00 - a01 * a10) * det;
    return out;
  }
  /**
   * Calculates the adjugate of a mat3
   *
   * @param {mat3} out the receiving matrix
   * @param {ReadonlyMat3} a the source matrix
   * @returns {mat3} out
   */

  function adjoint$1(out, a) {
    var a00 = a[0],
        a01 = a[1],
        a02 = a[2];
    var a10 = a[3],
        a11 = a[4],
        a12 = a[5];
    var a20 = a[6],
        a21 = a[7],
        a22 = a[8];
    out[0] = a11 * a22 - a12 * a21;
    out[1] = a02 * a21 - a01 * a22;
    out[2] = a01 * a12 - a02 * a11;
    out[3] = a12 * a20 - a10 * a22;
    out[4] = a00 * a22 - a02 * a20;
    out[5] = a02 * a10 - a00 * a12;
    out[6] = a10 * a21 - a11 * a20;
    out[7] = a01 * a20 - a00 * a21;
    out[8] = a00 * a11 - a01 * a10;
    return out;
  }
  /**
   * Calculates the determinant of a mat3
   *
   * @param {ReadonlyMat3} a the source matrix
   * @returns {Number} determinant of a
   */

  function determinant$1(a) {
    var a00 = a[0],
        a01 = a[1],
        a02 = a[2];
    var a10 = a[3],
        a11 = a[4],
        a12 = a[5];
    var a20 = a[6],
        a21 = a[7],
        a22 = a[8];
    return a00 * (a22 * a11 - a12 * a21) + a01 * (-a22 * a10 + a12 * a20) + a02 * (a21 * a10 - a11 * a20);
  }
  /**
   * Multiplies two mat3's
   *
   * @param {mat3} out the receiving matrix
   * @param {ReadonlyMat3} a the first operand
   * @param {ReadonlyMat3} b the second operand
   * @returns {mat3} out
   */

  function multiply$6(out, a, b) {
    var a00 = a[0],
        a01 = a[1],
        a02 = a[2];
    var a10 = a[3],
        a11 = a[4],
        a12 = a[5];
    var a20 = a[6],
        a21 = a[7],
        a22 = a[8];
    var b00 = b[0],
        b01 = b[1],
        b02 = b[2];
    var b10 = b[3],
        b11 = b[4],
        b12 = b[5];
    var b20 = b[6],
        b21 = b[7],
        b22 = b[8];
    out[0] = b00 * a00 + b01 * a10 + b02 * a20;
    out[1] = b00 * a01 + b01 * a11 + b02 * a21;
    out[2] = b00 * a02 + b01 * a12 + b02 * a22;
    out[3] = b10 * a00 + b11 * a10 + b12 * a20;
    out[4] = b10 * a01 + b11 * a11 + b12 * a21;
    out[5] = b10 * a02 + b11 * a12 + b12 * a22;
    out[6] = b20 * a00 + b21 * a10 + b22 * a20;
    out[7] = b20 * a01 + b21 * a11 + b22 * a21;
    out[8] = b20 * a02 + b21 * a12 + b22 * a22;
    return out;
  }
  /**
   * Translate a mat3 by the given vector
   *
   * @param {mat3} out the receiving matrix
   * @param {ReadonlyMat3} a the matrix to translate
   * @param {ReadonlyVec2} v vector to translate by
   * @returns {mat3} out
   */

  function translate$2(out, a, v) {
    var a00 = a[0],
        a01 = a[1],
        a02 = a[2],
        a10 = a[3],
        a11 = a[4],
        a12 = a[5],
        a20 = a[6],
        a21 = a[7],
        a22 = a[8],
        x = v[0],
        y = v[1];
    out[0] = a00;
    out[1] = a01;
    out[2] = a02;
    out[3] = a10;
    out[4] = a11;
    out[5] = a12;
    out[6] = x * a00 + y * a10 + a20;
    out[7] = x * a01 + y * a11 + a21;
    out[8] = x * a02 + y * a12 + a22;
    return out;
  }
  /**
   * Rotates a mat3 by the given angle
   *
   * @param {mat3} out the receiving matrix
   * @param {ReadonlyMat3} a the matrix to rotate
   * @param {Number} rad the angle to rotate the matrix by
   * @returns {mat3} out
   */

  function rotate$2(out, a, rad) {
    var a00 = a[0],
        a01 = a[1],
        a02 = a[2],
        a10 = a[3],
        a11 = a[4],
        a12 = a[5],
        a20 = a[6],
        a21 = a[7],
        a22 = a[8],
        s = Math.sin(rad),
        c = Math.cos(rad);
    out[0] = c * a00 + s * a10;
    out[1] = c * a01 + s * a11;
    out[2] = c * a02 + s * a12;
    out[3] = c * a10 - s * a00;
    out[4] = c * a11 - s * a01;
    out[5] = c * a12 - s * a02;
    out[6] = a20;
    out[7] = a21;
    out[8] = a22;
    return out;
  }
  /**
   * Scales the mat3 by the dimensions in the given vec2
   *
   * @param {mat3} out the receiving matrix
   * @param {ReadonlyMat3} a the matrix to rotate
   * @param {ReadonlyVec2} v the vec2 to scale the matrix by
   * @returns {mat3} out
   **/

  function scale$6(out, a, v) {
    var x = v[0],
        y = v[1];
    out[0] = x * a[0];
    out[1] = x * a[1];
    out[2] = x * a[2];
    out[3] = y * a[3];
    out[4] = y * a[4];
    out[5] = y * a[5];
    out[6] = a[6];
    out[7] = a[7];
    out[8] = a[8];
    return out;
  }
  /**
   * Creates a matrix from a vector translation
   * This is equivalent to (but much faster than):
   *
   *     mat3.identity(dest);
   *     mat3.translate(dest, dest, vec);
   *
   * @param {mat3} out mat3 receiving operation result
   * @param {ReadonlyVec2} v Translation vector
   * @returns {mat3} out
   */

  function fromTranslation$2(out, v) {
    out[0] = 1;
    out[1] = 0;
    out[2] = 0;
    out[3] = 0;
    out[4] = 1;
    out[5] = 0;
    out[6] = v[0];
    out[7] = v[1];
    out[8] = 1;
    return out;
  }
  /**
   * Creates a matrix from a given angle
   * This is equivalent to (but much faster than):
   *
   *     mat3.identity(dest);
   *     mat3.rotate(dest, dest, rad);
   *
   * @param {mat3} out mat3 receiving operation result
   * @param {Number} rad the angle to rotate the matrix by
   * @returns {mat3} out
   */

  function fromRotation$2(out, rad) {
    var s = Math.sin(rad),
        c = Math.cos(rad);
    out[0] = c;
    out[1] = s;
    out[2] = 0;
    out[3] = -s;
    out[4] = c;
    out[5] = 0;
    out[6] = 0;
    out[7] = 0;
    out[8] = 1;
    return out;
  }
  /**
   * Creates a matrix from a vector scaling
   * This is equivalent to (but much faster than):
   *
   *     mat3.identity(dest);
   *     mat3.scale(dest, dest, vec);
   *
   * @param {mat3} out mat3 receiving operation result
   * @param {ReadonlyVec2} v Scaling vector
   * @returns {mat3} out
   */

  function fromScaling$1(out, v) {
    out[0] = v[0];
    out[1] = 0;
    out[2] = 0;
    out[3] = 0;
    out[4] = v[1];
    out[5] = 0;
    out[6] = 0;
    out[7] = 0;
    out[8] = 1;
    return out;
  }
  /**
   * Copies the values from a mat2d into a mat3
   *
   * @param {mat3} out the receiving matrix
   * @param {ReadonlyMat2d} a the matrix to copy
   * @returns {mat3} out
   **/

  function fromMat2d(out, a) {
    out[0] = a[0];
    out[1] = a[1];
    out[2] = 0;
    out[3] = a[2];
    out[4] = a[3];
    out[5] = 0;
    out[6] = a[4];
    out[7] = a[5];
    out[8] = 1;
    return out;
  }
  /**
   * Calculates a 3x3 matrix from the given quaternion
   *
   * @param {mat3} out mat3 receiving operation result
   * @param {ReadonlyQuat} q Quaternion to create matrix from
   *
   * @returns {mat3} out
   */

  function fromQuat$1(out, q) {
    var x = q[0],
        y = q[1],
        z = q[2],
        w = q[3];
    var x2 = x + x;
    var y2 = y + y;
    var z2 = z + z;
    var xx = x * x2;
    var yx = y * x2;
    var yy = y * y2;
    var zx = z * x2;
    var zy = z * y2;
    var zz = z * z2;
    var wx = w * x2;
    var wy = w * y2;
    var wz = w * z2;
    out[0] = 1 - yy - zz;
    out[3] = yx - wz;
    out[6] = zx + wy;
    out[1] = yx + wz;
    out[4] = 1 - xx - zz;
    out[7] = zy - wx;
    out[2] = zx - wy;
    out[5] = zy + wx;
    out[8] = 1 - xx - yy;
    return out;
  }
  /**
   * Calculates a 3x3 normal matrix (transpose inverse) from the 4x4 matrix
   *
   * @param {mat3} out mat3 receiving operation result
   * @param {ReadonlyMat4} a Mat4 to derive the normal matrix from
   *
   * @returns {mat3} out
   */

  function normalFromMat4(out, a) {
    var a00 = a[0],
        a01 = a[1],
        a02 = a[2],
        a03 = a[3];
    var a10 = a[4],
        a11 = a[5],
        a12 = a[6],
        a13 = a[7];
    var a20 = a[8],
        a21 = a[9],
        a22 = a[10],
        a23 = a[11];
    var a30 = a[12],
        a31 = a[13],
        a32 = a[14],
        a33 = a[15];
    var b00 = a00 * a11 - a01 * a10;
    var b01 = a00 * a12 - a02 * a10;
    var b02 = a00 * a13 - a03 * a10;
    var b03 = a01 * a12 - a02 * a11;
    var b04 = a01 * a13 - a03 * a11;
    var b05 = a02 * a13 - a03 * a12;
    var b06 = a20 * a31 - a21 * a30;
    var b07 = a20 * a32 - a22 * a30;
    var b08 = a20 * a33 - a23 * a30;
    var b09 = a21 * a32 - a22 * a31;
    var b10 = a21 * a33 - a23 * a31;
    var b11 = a22 * a33 - a23 * a32; // Calculate the determinant

    var det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06;

    if (!det) {
      return null;
    }

    det = 1.0 / det;
    out[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det;
    out[1] = (a12 * b08 - a10 * b11 - a13 * b07) * det;
    out[2] = (a10 * b10 - a11 * b08 + a13 * b06) * det;
    out[3] = (a02 * b10 - a01 * b11 - a03 * b09) * det;
    out[4] = (a00 * b11 - a02 * b08 + a03 * b07) * det;
    out[5] = (a01 * b08 - a00 * b10 - a03 * b06) * det;
    out[6] = (a31 * b05 - a32 * b04 + a33 * b03) * det;
    out[7] = (a32 * b02 - a30 * b05 - a33 * b01) * det;
    out[8] = (a30 * b04 - a31 * b02 + a33 * b00) * det;
    return out;
  }
  /**
   * Generates a 2D projection matrix with the given bounds
   *
   * @param {mat3} out mat3 frustum matrix will be written into
   * @param {number} width Width of your gl context
   * @param {number} height Height of gl context
   * @returns {mat3} out
   */

  function projection(out, width, height) {
    out[0] = 2 / width;
    out[1] = 0;
    out[2] = 0;
    out[3] = 0;
    out[4] = -2 / height;
    out[5] = 0;
    out[6] = -1;
    out[7] = 1;
    out[8] = 1;
    return out;
  }
  /**
   * Returns a string representation of a mat3
   *
   * @param {ReadonlyMat3} a matrix to represent as a string
   * @returns {String} string representation of the matrix
   */

  function str$6(a) {
    return "mat3(" + a[0] + ", " + a[1] + ", " + a[2] + ", " + a[3] + ", " + a[4] + ", " + a[5] + ", " + a[6] + ", " + a[7] + ", " + a[8] + ")";
  }
  /**
   * Returns Frobenius norm of a mat3
   *
   * @param {ReadonlyMat3} a the matrix to calculate Frobenius norm of
   * @returns {Number} Frobenius norm
   */

  function frob$1(a) {
    return Math.hypot(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8]);
  }
  /**
   * Adds two mat3's
   *
   * @param {mat3} out the receiving matrix
   * @param {ReadonlyMat3} a the first operand
   * @param {ReadonlyMat3} b the second operand
   * @returns {mat3} out
   */

  function add$6(out, a, b) {
    out[0] = a[0] + b[0];
    out[1] = a[1] + b[1];
    out[2] = a[2] + b[2];
    out[3] = a[3] + b[3];
    out[4] = a[4] + b[4];
    out[5] = a[5] + b[5];
    out[6] = a[6] + b[6];
    out[7] = a[7] + b[7];
    out[8] = a[8] + b[8];
    return out;
  }
  /**
   * Subtracts matrix b from matrix a
   *
   * @param {mat3} out the receiving matrix
   * @param {ReadonlyMat3} a the first operand
   * @param {ReadonlyMat3} b the second operand
   * @returns {mat3} out
   */

  function subtract$4(out, a, b) {
    out[0] = a[0] - b[0];
    out[1] = a[1] - b[1];
    out[2] = a[2] - b[2];
    out[3] = a[3] - b[3];
    out[4] = a[4] - b[4];
    out[5] = a[5] - b[5];
    out[6] = a[6] - b[6];
    out[7] = a[7] - b[7];
    out[8] = a[8] - b[8];
    return out;
  }
  /**
   * Multiply each element of the matrix by a scalar.
   *
   * @param {mat3} out the receiving matrix
   * @param {ReadonlyMat3} a the matrix to scale
   * @param {Number} b amount to scale the matrix's elements by
   * @returns {mat3} out
   */

  function multiplyScalar$1(out, a, b) {
    out[0] = a[0] * b;
    out[1] = a[1] * b;
    out[2] = a[2] * b;
    out[3] = a[3] * b;
    out[4] = a[4] * b;
    out[5] = a[5] * b;
    out[6] = a[6] * b;
    out[7] = a[7] * b;
    out[8] = a[8] * b;
    return out;
  }
  /**
   * Adds two mat3's after multiplying each element of the second operand by a scalar value.
   *
   * @param {mat3} out the receiving vector
   * @param {ReadonlyMat3} a the first operand
   * @param {ReadonlyMat3} b the second operand
   * @param {Number} scale the amount to scale b's elements by before adding
   * @returns {mat3} out
   */

  function multiplyScalarAndAdd$1(out, a, b, scale) {
    out[0] = a[0] + b[0] * scale;
    out[1] = a[1] + b[1] * scale;
    out[2] = a[2] + b[2] * scale;
    out[3] = a[3] + b[3] * scale;
    out[4] = a[4] + b[4] * scale;
    out[5] = a[5] + b[5] * scale;
    out[6] = a[6] + b[6] * scale;
    out[7] = a[7] + b[7] * scale;
    out[8] = a[8] + b[8] * scale;
    return out;
  }
  /**
   * Returns whether the matrices have exactly the same elements in the same position (when compared with ===)
   *
   * @param {ReadonlyMat3} a The first matrix.
   * @param {ReadonlyMat3} b The second matrix.
   * @returns {Boolean} True if the matrices are equal, false otherwise.
   */

  function exactEquals$6(a, b) {
    return a[0] === b[0] && a[1] === b[1] && a[2] === b[2] && a[3] === b[3] && a[4] === b[4] && a[5] === b[5] && a[6] === b[6] && a[7] === b[7] && a[8] === b[8];
  }
  /**
   * Returns whether the matrices have approximately the same elements in the same position.
   *
   * @param {ReadonlyMat3} a The first matrix.
   * @param {ReadonlyMat3} b The second matrix.
   * @returns {Boolean} True if the matrices are equal, false otherwise.
   */

  function equals$6(a, b) {
    var a0 = a[0],
        a1 = a[1],
        a2 = a[2],
        a3 = a[3],
        a4 = a[4],
        a5 = a[5],
        a6 = a[6],
        a7 = a[7],
        a8 = a[8];
    var b0 = b[0],
        b1 = b[1],
        b2 = b[2],
        b3 = b[3],
        b4 = b[4],
        b5 = b[5],
        b6 = b[6],
        b7 = b[7],
        b8 = b[8];
    return Math.abs(a0 - b0) <= EPSILON * Math.max(1.0, Math.abs(a0), Math.abs(b0)) && Math.abs(a1 - b1) <= EPSILON * Math.max(1.0, Math.abs(a1), Math.abs(b1)) && Math.abs(a2 - b2) <= EPSILON * Math.max(1.0, Math.abs(a2), Math.abs(b2)) && Math.abs(a3 - b3) <= EPSILON * Math.max(1.0, Math.abs(a3), Math.abs(b3)) && Math.abs(a4 - b4) <= EPSILON * Math.max(1.0, Math.abs(a4), Math.abs(b4)) && Math.abs(a5 - b5) <= EPSILON * Math.max(1.0, Math.abs(a5), Math.abs(b5)) && Math.abs(a6 - b6) <= EPSILON * Math.max(1.0, Math.abs(a6), Math.abs(b6)) && Math.abs(a7 - b7) <= EPSILON * Math.max(1.0, Math.abs(a7), Math.abs(b7)) && Math.abs(a8 - b8) <= EPSILON * Math.max(1.0, Math.abs(a8), Math.abs(b8));
  }
  /**
   * Alias for {@link mat3.multiply}
   * @function
   */

  var mul$6 = multiply$6;
  /**
   * Alias for {@link mat3.subtract}
   * @function
   */

  var sub$4 = subtract$4;

  var mat3 = /*#__PURE__*/Object.freeze({
    __proto__: null,
    create: create$6,
    fromMat4: fromMat4$1,
    clone: clone$6,
    copy: copy$6,
    fromValues: fromValues$6,
    set: set$6,
    identity: identity$3,
    transpose: transpose$1,
    invert: invert$3,
    adjoint: adjoint$1,
    determinant: determinant$1,
    multiply: multiply$6,
    translate: translate$2,
    rotate: rotate$2,
    scale: scale$6,
    fromTranslation: fromTranslation$2,
    fromRotation: fromRotation$2,
    fromScaling: fromScaling$1,
    fromMat2d: fromMat2d,
    fromQuat: fromQuat$1,
    normalFromMat4: normalFromMat4,
    projection: projection,
    str: str$6,
    frob: frob$1,
    add: add$6,
    subtract: subtract$4,
    multiplyScalar: multiplyScalar$1,
    multiplyScalarAndAdd: multiplyScalarAndAdd$1,
    exactEquals: exactEquals$6,
    equals: equals$6,
    mul: mul$6,
    sub: sub$4
  });

  /**
   * 4x4 Matrix<br>Format: column-major, when typed out it looks like row-major<br>The matrices are being post multiplied.
   * @module mat4
   */

  /**
   * Creates a new identity mat4
   *
   * @returns {mat4} a new 4x4 matrix
   */

  function create$5() {
    var out = new ARRAY_TYPE(16);

    if (ARRAY_TYPE != Float32Array) {
      out[1] = 0;
      out[2] = 0;
      out[3] = 0;
      out[4] = 0;
      out[6] = 0;
      out[7] = 0;
      out[8] = 0;
      out[9] = 0;
      out[11] = 0;
      out[12] = 0;
      out[13] = 0;
      out[14] = 0;
    }

    out[0] = 1;
    out[5] = 1;
    out[10] = 1;
    out[15] = 1;
    return out;
  }
  /**
   * Creates a new mat4 initialized with values from an existing matrix
   *
   * @param {ReadonlyMat4} a matrix to clone
   * @returns {mat4} a new
Download .txt
gitextract_2jn2wipu/

├── .github/
│   ├── copilot-instructions.md
│   ├── pull_request_template.md
│   └── workflows/
│       ├── ci.yml
│       └── deploy.yml
├── .gitignore
├── .husky/
│   └── pre-commit
├── .prettierignore
├── .prettierrc.json
├── CLAUDE.md
├── LICENSE
├── README.md
├── build/
│   ├── client.ts
│   ├── engine-wasm.ts
│   ├── env.ts
│   ├── index.ts
│   ├── plugins.ts
│   ├── server.ts
│   └── views.ts
├── dev-utils/
│   ├── ICN_METADATA_TRANSLATIONS.md
│   ├── REDESIGN/
│   │   ├── design.md
│   │   ├── runner_setup.md
│   │   ├── stack.md
│   │   └── todo.md
│   ├── SKELETON.css
│   ├── SKELETON.html
│   ├── live-game-persistence.md
│   ├── pieces/
│   │   ├── spritesheet 512/
│   │   │   └── How to create spritesheet.md
│   │   └── svg/
│   │       └── Converting PNG to SVG.md
│   ├── post_processing_effects/
│   │   ├── posterize/
│   │   │   ├── PosterizePass.ts
│   │   │   └── fragment.glsl
│   │   ├── radial_distortion/
│   │   │   ├── RadialDistortionPass.ts
│   │   │   └── fragment.glsl
│   │   └── rolling_hills/
│   │       ├── RollingHillsPass.ts
│   │       └── fragment.glsl
│   ├── readme.md
│   ├── scripts/
│   │   ├── PatreonAPI.ts
│   │   ├── audio/
│   │   │   └── processors/
│   │   │       └── bitcrusher/
│   │   │           ├── BitcrusherNode.ts
│   │   │           └── BitcrusherProcessor.ts
│   │   ├── clientEventDispatcher.ts
│   │   ├── events.ts
│   │   ├── gl-matrix.js
│   │   ├── icn-regex-matching.ts
│   │   ├── meshSimplification.ts
│   │   ├── positionnormalizer/
│   │   │   ├── moveexpander.ts
│   │   │   ├── normalizertester.ts
│   │   │   ├── positioncompressor.ts
│   │   │   ├── positioncompressorplusintersections.ts
│   │   │   └── unusedpositionnormalizermethods.ts
│   │   └── vertexdatatotexture.ts
│   ├── shaders/
│   │   ├── texture/
│   │   │   ├── instanced/
│   │   │   │   └── tint/
│   │   │   │       ├── fragment.glsl
│   │   │   │       └── vertex.glsl
│   │   │   └── tint/
│   │   │       ├── fragment.glsl
│   │   │       └── vertex.glsl
│   │   └── voronoi/
│   │       └── fragment.glsl
│   ├── sounds/
│   │   └── SoundscapeGenerator.html
│   └── spritesheet_generator/
│       ├── spritesheet.ts
│       └── spritesheetGenerator.ts
├── docs/
│   ├── COPYING.md
│   ├── GRAPHICS.md
│   ├── GUIDELINES.md
│   ├── NAVIGATING.md
│   ├── SETUP.md
│   └── TRANSLATIONS.md
├── ecosystem.config.cjs
├── eslint.config.js
├── nodemon.json
├── package.json
├── scripts/
│   ├── add-file-paths.ts
│   ├── generate-translation-types.ts
│   ├── optimize-images.ts
│   ├── organize-imports.ts
│   └── readme.md
├── src/
│   ├── client/
│   │   ├── css/
│   │   │   ├── 404.css
│   │   │   ├── admin.css
│   │   │   ├── createaccount.css
│   │   │   ├── credits.css
│   │   │   ├── footer.css
│   │   │   ├── guide.css
│   │   │   ├── header.css
│   │   │   ├── icnvalidator.css
│   │   │   ├── index.css
│   │   │   ├── leaderboard.css
│   │   │   ├── login.css
│   │   │   ├── member.css
│   │   │   ├── news.css
│   │   │   ├── play.css
│   │   │   └── termsofservice.css
│   │   ├── img/
│   │   │   ├── badges/
│   │   │   │   ├── checkmate-badge-bronze.avif
│   │   │   │   ├── checkmate-badge-gold.avif
│   │   │   │   └── checkmate-badge-silver.avif
│   │   │   ├── blank_board.avif
│   │   │   ├── game/
│   │   │   │   └── guide/
│   │   │   │       ├── arrowindicators.avif
│   │   │   │       ├── fairy/
│   │   │   │       │   ├── amazon.avif
│   │   │   │       │   ├── archbishop.avif
│   │   │   │       │   ├── centaur.avif
│   │   │   │       │   ├── chancellor.avif
│   │   │   │       │   ├── guard.avif
│   │   │   │       │   ├── hawk.avif
│   │   │   │       │   ├── huygen.avif
│   │   │   │       │   ├── knightrider.avif
│   │   │   │       │   ├── obstacle.avif
│   │   │   │       │   ├── rose.avif
│   │   │   │       │   └── void.avif
│   │   │   │       ├── kingrookfork.avif
│   │   │   │       └── promotionlines.avif
│   │   │   ├── king_w.avif
│   │   │   ├── logo/
│   │   │   │   ├── dark-theme.avif
│   │   │   │   └── light-theme.avif
│   │   │   ├── member_default.avif
│   │   │   └── queen_w.avif
│   │   ├── scripts/
│   │   │   ├── cjs/
│   │   │   │   └── game/
│   │   │   │       └── htmlscript.ts
│   │   │   └── esm/
│   │   │       ├── audio/
│   │   │       │   ├── AudioEffects.ts
│   │   │       │   ├── AudioManager.ts
│   │   │       │   ├── AudioUtils.ts
│   │   │       │   ├── LFOFactory.ts
│   │   │       │   ├── SoundLayer.ts
│   │   │       │   ├── SoundscapePlayer.ts
│   │   │       │   └── processors/
│   │   │       │       ├── downsampler/
│   │   │       │       │   ├── DownsamplerNode.ts
│   │   │       │       │   └── DownsamplerProcessor.ts
│   │   │       │       └── worklet-types.ts
│   │   │       ├── chess/
│   │   │       │   └── rendering/
│   │   │       │       ├── checkerboardgenerator.ts
│   │   │       │       ├── imagecache.ts
│   │   │       │       ├── svgcache.ts
│   │   │       │       └── texturecache.ts
│   │   │       ├── components/
│   │   │       │   └── header/
│   │   │       │       ├── currpage-greyer.ts
│   │   │       │       ├── dropdowns/
│   │   │       │       │   ├── appearancedropdown.ts
│   │   │       │       │   ├── gameplaydropdown.ts
│   │   │       │       │   ├── languagedropdown.ts
│   │   │       │       │   ├── legalmovedropdown.ts
│   │   │       │       │   ├── perspectivedropdown.ts
│   │   │       │       │   └── sounddropdown.ts
│   │   │       │       ├── faviconselector.ts
│   │   │       │       ├── header.ts
│   │   │       │       ├── news-notification.ts
│   │   │       │       ├── pingmeter.ts
│   │   │       │       ├── preferences.ts
│   │   │       │       ├── settings.ts
│   │   │       │       └── spacing.ts
│   │   │       ├── game/
│   │   │       │   ├── GameBus.ts
│   │   │       │   ├── boardeditor/
│   │   │       │   │   ├── actions/
│   │   │       │   │   │   ├── eactions.ts
│   │   │       │   │   │   ├── eautosave.ts
│   │   │       │   │   │   ├── ecloud.ts
│   │   │       │   │   │   ├── editorSavesAPI.ts
│   │   │       │   │   │   └── esave.ts
│   │   │       │   │   ├── boardeditor.ts
│   │   │       │   │   ├── eclipboard.ts
│   │   │       │   │   ├── edithistory.ts
│   │   │       │   │   ├── editortypes.ts
│   │   │       │   │   ├── egamerules.ts
│   │   │       │   │   └── tools/
│   │   │       │   │       ├── drawingtool.ts
│   │   │       │   │       ├── etoolmanager.ts
│   │   │       │   │       ├── normaltool.ts
│   │   │       │   │       └── selection/
│   │   │       │   │           ├── scursor.ts
│   │   │       │   │           ├── sdrag.ts
│   │   │       │   │           ├── selectiontool.ts
│   │   │       │   │           ├── sfill.ts
│   │   │       │   │           ├── stoolgraphics.ts
│   │   │       │   │           └── stransformations.ts
│   │   │       │   ├── chess/
│   │   │       │   │   ├── checkmatepractice.ts
│   │   │       │   │   ├── clientmetadatautil.ts
│   │   │       │   │   ├── copygame.ts
│   │   │       │   │   ├── engines/
│   │   │       │   │   │   ├── engine.ts
│   │   │       │   │   │   ├── engineCheckmatePractice.ts
│   │   │       │   │   │   ├── enginecards/
│   │   │       │   │   │   │   └── hydrochess_card.ts
│   │   │       │   │   │   └── hydrochess.ts
│   │   │       │   │   ├── game.ts
│   │   │       │   │   ├── gamecompressor.ts
│   │   │       │   │   ├── gamecompressor.unit.test.ts
│   │   │       │   │   ├── gameformulator.ts
│   │   │       │   │   ├── gameloader.ts
│   │   │       │   │   ├── gameslot.ts
│   │   │       │   │   ├── graphicalchanges.ts
│   │   │       │   │   ├── movesequence.ts
│   │   │       │   │   ├── pastegame.ts
│   │   │       │   │   ├── premoves.ts
│   │   │       │   │   └── selection.ts
│   │   │       │   ├── config.ts
│   │   │       │   ├── gui/
│   │   │       │   │   ├── boardeditor/
│   │   │       │   │   │   ├── actions/
│   │   │       │   │   │   │   ├── guiclearposition.ts
│   │   │       │   │   │   │   ├── guigamerules.ts
│   │   │       │   │   │   │   ├── guiresetposition.ts
│   │   │       │   │   │   │   ├── guistartenginegame.ts
│   │   │       │   │   │   │   ├── guistartlocalgame.ts
│   │   │       │   │   │   │   └── loadposition/
│   │   │       │   │   │   │       ├── guiloadposition.ts
│   │   │       │   │   │   │       ├── guiloadpositionmodal.ts
│   │   │       │   │   │   │       └── guiloadpositionsavelist.ts
│   │   │       │   │   │   ├── guiboardeditor.ts
│   │   │       │   │   │   ├── guifloatingwindow.ts
│   │   │       │   │   │   ├── guipalette.ts
│   │   │       │   │   │   ├── guipositionheader.ts
│   │   │       │   │   │   └── guitoolbar.ts
│   │   │       │   │   ├── gui.ts
│   │   │       │   │   ├── guiclock.ts
│   │   │       │   │   ├── guidrawoffer.ts
│   │   │       │   │   ├── guigameinfo.ts
│   │   │       │   │   ├── guiloading.ts
│   │   │       │   │   ├── guinavigation.ts
│   │   │       │   │   ├── guipause.ts
│   │   │       │   │   ├── guiplay.ts
│   │   │       │   │   ├── guipractice.ts
│   │   │       │   │   ├── guipromotion.ts
│   │   │       │   │   ├── guititle.ts
│   │   │       │   │   ├── loadingscreen.ts
│   │   │       │   │   ├── stats.ts
│   │   │       │   │   ├── style.ts
│   │   │       │   │   └── toast.ts
│   │   │       │   ├── input.ts
│   │   │       │   ├── main.ts
│   │   │       │   ├── misc/
│   │   │       │   │   ├── controls.ts
│   │   │       │   │   ├── enginegame.ts
│   │   │       │   │   ├── gamesound.ts
│   │   │       │   │   ├── invites.ts
│   │   │       │   │   ├── keybinds.ts
│   │   │       │   │   ├── loadbalancer.ts
│   │   │       │   │   ├── onlinegame/
│   │   │       │   │   │   ├── afk.ts
│   │   │       │   │   │   ├── disconnect.ts
│   │   │       │   │   │   ├── drawoffers.ts
│   │   │       │   │   │   ├── movesendreceive.ts
│   │   │       │   │   │   ├── onlinegame.ts
│   │   │       │   │   │   ├── onlinegamerouter.ts
│   │   │       │   │   │   ├── resyncer.ts
│   │   │       │   │   │   └── tabnameflash.ts
│   │   │       │   │   └── space.ts
│   │   │       │   ├── rendering/
│   │   │       │   │   ├── ColorFlowRenderer.ts
│   │   │       │   │   ├── WaterRipples.ts
│   │   │       │   │   ├── animation.ts
│   │   │       │   │   ├── area.ts
│   │   │       │   │   ├── arrows/
│   │   │       │   │   │   ├── arrowlegalmovehighlights.ts
│   │   │       │   │   │   ├── arrows.ts
│   │   │       │   │   │   ├── arrowscalculator.ts
│   │   │       │   │   │   ├── arrowsgraphics.ts
│   │   │       │   │   │   └── arrowshifts.ts
│   │   │       │   │   ├── boarddrag.ts
│   │   │       │   │   ├── boardpos.ts
│   │   │       │   │   ├── boardtiles.ts
│   │   │       │   │   ├── border.ts
│   │   │       │   │   ├── camera.ts
│   │   │       │   │   ├── coordinates.ts
│   │   │       │   │   ├── dragging/
│   │   │       │   │   │   ├── draganimation.ts
│   │   │       │   │   │   ├── dragarrows.ts
│   │   │       │   │   │   └── droparrows.ts
│   │   │       │   │   ├── effect_zone/
│   │   │       │   │   │   ├── EffectZoneManager.ts
│   │   │       │   │   │   ├── soundscapes/
│   │   │       │   │   │   │   ├── IridescenceSoundscape.ts
│   │   │       │   │   │   │   └── UndercurrentSoundscape.ts
│   │   │       │   │   │   └── zones/
│   │   │       │   │   │       ├── AshfallVocsZone.ts
│   │   │       │   │   │       ├── ContortionFieldZone.ts
│   │   │       │   │   │       ├── DustyWastesZone.ts
│   │   │       │   │   │       ├── EchoRiftZone.ts
│   │   │       │   │   │       ├── EmberVergeZone.ts
│   │   │       │   │   │       ├── IridescenceZone.ts
│   │   │       │   │   │       ├── OceanZone.ts
│   │   │       │   │   │       ├── SpectralEdgeZone.ts
│   │   │       │   │   │       ├── StaticZone.ts
│   │   │       │   │   │       ├── TheBeginningZone.ts
│   │   │       │   │   │       └── UndercurrentZone.ts
│   │   │       │   │   ├── frameratelimiter.ts
│   │   │       │   │   ├── frametracker.ts
│   │   │       │   │   ├── gl-matrix.js
│   │   │       │   │   ├── highlights/
│   │   │       │   │   │   ├── annotations/
│   │   │       │   │   │   │   ├── annotations.ts
│   │   │       │   │   │   │   ├── drawarrows.ts
│   │   │       │   │   │   │   ├── drawrays.ts
│   │   │       │   │   │   │   └── drawsquares.ts
│   │   │       │   │   │   ├── checkhighlight.ts
│   │   │       │   │   │   ├── highlightline.ts
│   │   │       │   │   │   ├── highlights.ts
│   │   │       │   │   │   ├── legalmovehighlights.ts
│   │   │       │   │   │   ├── legalmovemodel.ts
│   │   │       │   │   │   ├── movehints.ts
│   │   │       │   │   │   ├── selectedpiecehighlightline.ts
│   │   │       │   │   │   ├── snapping.ts
│   │   │       │   │   │   ├── specialrighthighlights.ts
│   │   │       │   │   │   └── squarerendering.ts
│   │   │       │   │   ├── instancedshapes.ts
│   │   │       │   │   ├── meshes.ts
│   │   │       │   │   ├── miniimage.ts
│   │   │       │   │   ├── perspective.ts
│   │   │       │   │   ├── piecemodels.ts
│   │   │       │   │   ├── pieces.ts
│   │   │       │   │   ├── primitives.ts
│   │   │       │   │   ├── promotionlines.ts
│   │   │       │   │   ├── screenshake.ts
│   │   │       │   │   ├── starfield.ts
│   │   │       │   │   ├── text/
│   │   │       │   │   │   ├── glyphatlas.ts
│   │   │       │   │   │   └── textrenderer.ts
│   │   │       │   │   ├── transitions/
│   │   │       │   │   │   └── Transition.ts
│   │   │       │   │   └── webgl.ts
│   │   │       │   └── websocket/
│   │   │       │       ├── socketclose.ts
│   │   │       │       ├── socketman.ts
│   │   │       │       ├── socketmessages.ts
│   │   │       │       ├── socketrouter.ts
│   │   │       │       ├── socketschemas.ts
│   │   │       │       └── socketsubs.ts
│   │   │       ├── util/
│   │   │       │   ├── ImageLoader.ts
│   │   │       │   ├── IndexedDB.ts
│   │   │       │   ├── LocalStorage.ts
│   │   │       │   ├── PerlinNoise.ts
│   │   │       │   ├── compression.ts
│   │   │       │   ├── docutil.ts
│   │   │       │   ├── httputils.ts
│   │   │       │   ├── indexeddb.unit.test.ts
│   │   │       │   ├── mouse.ts
│   │   │       │   ├── pingManager.ts
│   │   │       │   ├── splines.ts
│   │   │       │   ├── svgtoimageconverter.ts
│   │   │       │   ├── thread.ts
│   │   │       │   ├── tooltips.ts
│   │   │       │   ├── usernamecontainer.ts
│   │   │       │   └── validatorama.ts
│   │   │       ├── views/
│   │   │       │   ├── admin.ts
│   │   │       │   ├── createaccount.ts
│   │   │       │   ├── guide.ts
│   │   │       │   ├── icnvalidator.ts
│   │   │       │   ├── index.ts
│   │   │       │   ├── leaderboard.ts
│   │   │       │   ├── login.ts
│   │   │       │   ├── member.ts
│   │   │       │   ├── news.ts
│   │   │       │   └── resetpassword.ts
│   │   │       ├── webgl/
│   │   │       │   ├── BufferUtil.ts
│   │   │       │   ├── ProgramManager.ts
│   │   │       │   ├── Renderable.ts
│   │   │       │   ├── ShaderProgram.ts
│   │   │       │   ├── TextureLoader.ts
│   │   │       │   ├── maskedDraw.ts
│   │   │       │   └── post_processing/
│   │   │       │       ├── PostProcessingPipeline.ts
│   │   │       │       └── passes/
│   │   │       │           ├── ColorGradePass.ts
│   │   │       │           ├── GlitchPass.ts
│   │   │       │           ├── HeatWavePass.ts
│   │   │       │           ├── PassThroughPass.ts
│   │   │       │           ├── SineWavePass.ts
│   │   │       │           ├── VignettePass.ts
│   │   │       │           ├── VoronoiDistortionPass.ts
│   │   │       │           ├── WaterPass.ts
│   │   │       │           └── WaterRipplePass.ts
│   │   │       └── workers/
│   │   │           └── icnvalidator.worker.ts
│   │   ├── shaders/
│   │   │   ├── arrow_images/
│   │   │   │   ├── fragment.glsl
│   │   │   │   └── vertex.glsl
│   │   │   ├── arrows/
│   │   │   │   └── vertex.glsl
│   │   │   ├── board_uber_shader/
│   │   │   │   ├── fragment.glsl
│   │   │   │   └── vertex.glsl
│   │   │   ├── color/
│   │   │   │   ├── fragment.glsl
│   │   │   │   ├── instanced/
│   │   │   │   │   └── vertex.glsl
│   │   │   │   └── vertex.glsl
│   │   │   ├── color_grade/
│   │   │   │   └── fragment.glsl
│   │   │   ├── color_texture/
│   │   │   │   ├── fragment.glsl
│   │   │   │   └── vertex.glsl
│   │   │   ├── fullscreen_colorflow/
│   │   │   │   └── fragment.glsl
│   │   │   ├── glitch/
│   │   │   │   └── fragment.glsl
│   │   │   ├── heat_wave/
│   │   │   │   └── fragment.glsl
│   │   │   ├── highlights/
│   │   │   │   └── vertex.glsl
│   │   │   ├── mini_images/
│   │   │   │   ├── fragment.glsl
│   │   │   │   └── vertex.glsl
│   │   │   ├── post_pass/
│   │   │   │   ├── fragment.glsl
│   │   │   │   └── vertex.glsl
│   │   │   ├── sine_wave/
│   │   │   │   └── fragment.glsl
│   │   │   ├── starfield/
│   │   │   │   └── vertex.glsl
│   │   │   ├── texture/
│   │   │   │   ├── fragment.glsl
│   │   │   │   ├── instanced/
│   │   │   │   │   └── vertex.glsl
│   │   │   │   └── vertex.glsl
│   │   │   ├── vignette/
│   │   │   │   └── fragment.glsl
│   │   │   ├── voronoi_distortion/
│   │   │   │   └── fragment.glsl
│   │   │   ├── water/
│   │   │   │   └── fragment.glsl
│   │   │   └── water_ripple/
│   │   │       └── fragment.glsl
│   │   ├── sounds/
│   │   │   └── spritesheet/
│   │   │       ├── note.txt
│   │   │       └── soundspritesheet.opus
│   │   └── views/
│   │       ├── admin.ejs
│   │       ├── components/
│   │       │   ├── footer.ejs
│   │       │   └── header.ejs
│   │       ├── createaccount.ejs
│   │       ├── credits.ejs
│   │       ├── errors/
│   │       │   ├── 400.ejs
│   │       │   ├── 401.ejs
│   │       │   ├── 404.ejs
│   │       │   ├── 409.ejs
│   │       │   └── 500.ejs
│   │       ├── guide.ejs
│   │       ├── icnvalidator.html
│   │       ├── index.ejs
│   │       ├── leaderboard.ejs
│   │       ├── login.ejs
│   │       ├── member.ejs
│   │       ├── news.ejs
│   │       ├── play.ejs
│   │       ├── resetpassword.ejs
│   │       └── termsofservice.ejs
│   ├── server/
│   │   ├── api/
│   │   │   ├── AdminPanel.ts
│   │   │   ├── EditorSavesAPI.int.test.ts
│   │   │   ├── EditorSavesAPI.ts
│   │   │   ├── GitHub.ts
│   │   │   ├── LeaderboardAPI.ts
│   │   │   ├── MemberAPI.ts
│   │   │   ├── NewsAPI.ts
│   │   │   ├── PracticeProgress.int.test.ts
│   │   │   ├── PracticeProgress.ts
│   │   │   ├── Prefs.int.test.ts
│   │   │   └── Prefs.ts
│   │   ├── app.ts
│   │   ├── config/
│   │   │   ├── certOptions.ts
│   │   │   ├── dateLocales.ts
│   │   │   ├── generateCert.ts
│   │   │   ├── i18n.ts
│   │   │   ├── paths.ts
│   │   │   ├── setupDev.ts
│   │   │   └── translationLoader.ts
│   │   ├── controllers/
│   │   │   ├── authController.ts
│   │   │   ├── authRatelimiter.ts
│   │   │   ├── authenticationTokens/
│   │   │   │   ├── accessTokenIssuer.ts
│   │   │   │   ├── sessionManager.ts
│   │   │   │   ├── tokenSigner.ts
│   │   │   │   └── tokenValidator.ts
│   │   │   ├── awsWebhook.ts
│   │   │   ├── browserIDManager.ts
│   │   │   ├── createAccountController.ts
│   │   │   ├── createAccountController.unit.test.ts
│   │   │   ├── deleteAccountController.ts
│   │   │   ├── deployController.ts
│   │   │   ├── emailController.ts
│   │   │   ├── loginController.int.test.ts
│   │   │   ├── loginController.ts
│   │   │   ├── logoutController.ts
│   │   │   ├── passwordResetController.ts
│   │   │   ├── roles.ts
│   │   │   └── verifyAccountController.ts
│   │   ├── database/
│   │   │   ├── backupManager.ts
│   │   │   ├── blacklistManager.ts
│   │   │   ├── cleanupTasks.ts
│   │   │   ├── database.ts
│   │   │   ├── databaseTables.ts
│   │   │   ├── editorSavesManager.ts
│   │   │   ├── gamesManager.ts
│   │   │   ├── leaderboardsManager.ts
│   │   │   ├── liveGamesManager.ts
│   │   │   ├── livePlayerGamesManager.ts
│   │   │   ├── memberManager.ts
│   │   │   ├── playerGamesManager.ts
│   │   │   ├── ratingAbuseManager.ts
│   │   │   └── refreshTokenManager.ts
│   │   ├── game/
│   │   │   ├── gamemanager/
│   │   │   │   ├── abortresigngame.ts
│   │   │   │   ├── activeplayers.ts
│   │   │   │   ├── afkdisconnect.ts
│   │   │   │   ├── cheatreport.ts
│   │   │   │   ├── drawoffers.ts
│   │   │   │   ├── gamecount.ts
│   │   │   │   ├── gamelogger.ts
│   │   │   │   ├── gamemanager.ts
│   │   │   │   ├── gamerouter.ts
│   │   │   │   ├── gameutility.ts
│   │   │   │   ├── joingame.ts
│   │   │   │   ├── liveGameRestore.ts
│   │   │   │   ├── liveGameValues.ts
│   │   │   │   ├── movesubmission.ts
│   │   │   │   ├── onAFK.ts
│   │   │   │   ├── onOfferDraw.ts
│   │   │   │   ├── pastereport.ts
│   │   │   │   ├── ratingabuse.ts
│   │   │   │   ├── ratingcalculation.ts
│   │   │   │   └── resync.ts
│   │   │   ├── invitesmanager/
│   │   │   │   ├── acceptinvite.ts
│   │   │   │   ├── cancelinvite.ts
│   │   │   │   ├── createinvite.ts
│   │   │   │   ├── invitesmanager.ts
│   │   │   │   ├── invitesrouter.ts
│   │   │   │   ├── invitessubscribers.ts
│   │   │   │   └── inviteutility.ts
│   │   │   ├── servermetadatautil.ts
│   │   │   ├── statlogger.ts
│   │   │   └── timecontrol.ts
│   │   ├── middleware/
│   │   │   ├── banned.ts
│   │   │   ├── errorHandler.ts
│   │   │   ├── logEvents.ts
│   │   │   ├── middleware.ts
│   │   │   ├── rateLimit.ts
│   │   │   ├── rateLimiters.ts
│   │   │   ├── secureRedirect.ts
│   │   │   ├── send404.ts
│   │   │   └── verifyJWT.ts
│   │   ├── routes/
│   │   │   └── root.ts
│   │   ├── server.ts
│   │   ├── socket/
│   │   │   ├── closeSocket.ts
│   │   │   ├── echoTracker.ts
│   │   │   ├── generalrouter.ts
│   │   │   ├── openSocket.ts
│   │   │   ├── receiveSocketMessage.ts
│   │   │   ├── sendSocketMessage.ts
│   │   │   ├── socketManager.ts
│   │   │   ├── socketRouter.ts
│   │   │   ├── socketServer.ts
│   │   │   └── socketUtility.ts
│   │   ├── types.ts
│   │   └── utility/
│   │       ├── IP.ts
│   │       ├── errorGuard.ts
│   │       ├── generateDependancyGraph.ts
│   │       ├── lockFile.ts
│   │       ├── mailer.ts
│   │       ├── newsUtil.ts
│   │       ├── startupLogger.ts
│   │       ├── translate.ts
│   │       ├── urlUtils.ts
│   │       └── zodlogger.ts
│   ├── shared/
│   │   ├── chess/
│   │   │   ├── logic/
│   │   │   │   ├── boardchanges.ts
│   │   │   │   ├── checkdetection.ts
│   │   │   │   ├── checkmate.ts
│   │   │   │   ├── checkresolver.ts
│   │   │   │   ├── clock.ts
│   │   │   │   ├── fourdimensionalmoves.ts
│   │   │   │   ├── gamefile.ts
│   │   │   │   ├── icn/
│   │   │   │   │   ├── icncommentutils.ts
│   │   │   │   │   └── icnconverter.ts
│   │   │   │   ├── initvariant.ts
│   │   │   │   ├── insufficientmaterial.ts
│   │   │   │   ├── legalmoves.ts
│   │   │   │   ├── movepiece.ts
│   │   │   │   ├── movesets.ts
│   │   │   │   ├── movevalidation.ts
│   │   │   │   ├── organizedpieces.ts
│   │   │   │   ├── repetition.ts
│   │   │   │   ├── specialdetect.ts
│   │   │   │   ├── specialmove.ts
│   │   │   │   ├── state.ts
│   │   │   │   └── wincondition.ts
│   │   │   ├── util/
│   │   │   │   ├── bdcoords.ts
│   │   │   │   ├── boardutil.ts
│   │   │   │   ├── clockutil.ts
│   │   │   │   ├── coordutil.ts
│   │   │   │   ├── gamefileutility.ts
│   │   │   │   ├── gamerules.ts
│   │   │   │   ├── metadatautil.ts
│   │   │   │   ├── moveutil.ts
│   │   │   │   ├── typeutil.ts
│   │   │   │   ├── validcheckmates.ts
│   │   │   │   └── winconutil.ts
│   │   │   └── variants/
│   │   │       ├── fourdimensionalgenerator.ts
│   │   │       ├── omega3generator.ts
│   │   │       ├── omega4generator.ts
│   │   │       ├── servervalidation.ts
│   │   │       ├── validleaderboard.ts
│   │   │       ├── variant.ts
│   │   │       └── variantdictionary.ts
│   │   ├── components/
│   │   │   └── header/
│   │   │       ├── pieceThemes.ts
│   │   │       └── themes.ts
│   │   ├── game_version.ts
│   │   ├── types.ts
│   │   └── util/
│   │       ├── EventBus.ts
│   │       ├── editorutil.ts
│   │       ├── isprime.ts
│   │       ├── jsutil.ts
│   │       ├── math/
│   │       │   ├── bimath.ts
│   │       │   ├── bounds.ts
│   │       │   ├── geometry.ts
│   │       │   ├── math.ts
│   │       │   └── vectors.ts
│   │       ├── timeutil.ts
│   │       ├── tokenConfig.ts
│   │       ├── uuid.ts
│   │       ├── validators.ts
│   │       └── wsutil.ts
│   ├── tests/
│   │   ├── integrationUtils.ts
│   │   ├── testRequest.ts
│   │   └── tests-setup.ts
│   └── types/
│       ├── globals.d.ts
│       ├── shaders.d.ts
│       └── translations.ts
├── translation/
│   ├── changes.json
│   ├── de-DE.toml
│   ├── el-GR.toml
│   ├── en-US.toml
│   ├── es-ES.toml
│   ├── fi-FI.toml
│   ├── fr-FR.toml
│   ├── news/
│   │   ├── en-US/
│   │   │   ├── 2024-01-29.md
│   │   │   ├── 2024-05-14.md
│   │   │   ├── 2024-05-24.md
│   │   │   ├── 2024-05-27.md
│   │   │   ├── 2024-07-09.md
│   │   │   ├── 2024-07-13.md
│   │   │   ├── 2024-07-22.md
│   │   │   ├── 2024-08-01.md
│   │   │   ├── 2024-09-11.md
│   │   │   ├── 2024-11-22.md
│   │   │   ├── 2025-03-12.md
│   │   │   ├── 2025-03-17.md
│   │   │   ├── 2025-05-21.md
│   │   │   ├── 2025-06-16.md
│   │   │   ├── 2025-11-28.md
│   │   │   ├── 2026-01-08.md
│   │   │   ├── 2026-03-09.md
│   │   │   └── 2026-04-24.md
│   │   ├── es-ES/
│   │   │   ├── 2024-01-29.md
│   │   │   ├── 2024-05-14.md
│   │   │   ├── 2024-05-24.md
│   │   │   ├── 2024-05-27.md
│   │   │   ├── 2024-07-09.md
│   │   │   ├── 2024-07-13.md
│   │   │   ├── 2024-07-22.md
│   │   │   ├── 2024-08-01.md
│   │   │   ├── 2024-09-11.md
│   │   │   ├── 2024-11-22.md
│   │   │   ├── 2025-03-12.md
│   │   │   ├── 2025-03-17.md
│   │   │   ├── 2025-05-21.md
│   │   │   ├── 2025-06-16.md
│   │   │   └── 2025-11-28.md
│   │   ├── fi-FI/
│   │   │   ├── 2026-01-08.md
│   │   │   ├── 2026-03-09.md
│   │   │   └── 2026-04-24.md
│   │   ├── fr-FR/
│   │   │   ├── 2024-01-29.md
│   │   │   ├── 2024-05-14.md
│   │   │   ├── 2024-05-24.md
│   │   │   ├── 2024-05-27.md
│   │   │   ├── 2024-07-09.md
│   │   │   ├── 2024-07-13.md
│   │   │   ├── 2024-07-22.md
│   │   │   ├── 2024-08-01.md
│   │   │   ├── 2024-09-11.md
│   │   │   ├── 2024-11-22.md
│   │   │   ├── 2025-03-12.md
│   │   │   ├── 2025-03-17.md
│   │   │   ├── 2025-05-21.md
│   │   │   ├── 2025-06-16.md
│   │   │   ├── 2025-11-28.md
│   │   │   ├── 2026-01-08.md
│   │   │   └── 2026-03-09.md
│   │   ├── pl-PL/
│   │   │   ├── 2024-01-29.md
│   │   │   ├── 2024-05-14.md
│   │   │   ├── 2024-05-24.md
│   │   │   ├── 2024-05-27.md
│   │   │   ├── 2024-07-09.md
│   │   │   ├── 2024-07-13.md
│   │   │   ├── 2024-07-22.md
│   │   │   ├── 2024-08-01.md
│   │   │   └── 2024-09-11.md
│   │   ├── pt-BR/
│   │   │   ├── 2024-01-29.md
│   │   │   ├── 2024-05-14.md
│   │   │   ├── 2024-05-24.md
│   │   │   ├── 2024-05-27.md
│   │   │   ├── 2024-07-09.md
│   │   │   ├── 2024-07-13.md
│   │   │   ├── 2024-07-22.md
│   │   │   ├── 2024-08-01.md
│   │   │   └── 2024-09-11.md
│   │   ├── zh-CN/
│   │   │   ├── 2024-01-29.md
│   │   │   ├── 2024-05-14.md
│   │   │   ├── 2024-05-24.md
│   │   │   ├── 2024-05-27.md
│   │   │   ├── 2024-07-09.md
│   │   │   ├── 2024-07-13.md
│   │   │   ├── 2024-07-22.md
│   │   │   ├── 2024-08-01.md
│   │   │   └── 2024-09-11.md
│   │   └── zh-TW/
│   │       ├── 2024-01-29.md
│   │       ├── 2024-05-14.md
│   │       ├── 2024-05-24.md
│   │       ├── 2024-05-27.md
│   │       ├── 2024-07-09.md
│   │       ├── 2024-07-13.md
│   │       ├── 2024-07-22.md
│   │       ├── 2024-08-01.md
│   │       └── 2024-09-11.md
│   ├── pl-PL.toml
│   ├── pt-BR.toml
│   ├── ru-RU.toml
│   ├── zh-CN.toml
│   └── zh-TW.toml
├── tsconfig.json
└── vitest.config.ts
Download .txt
Showing preview only (361K chars total). Download the full file or copy to clipboard to get everything.
SYMBOL INDEX (3880 symbols across 415 files)

FILE: build/client.ts
  function getInitialBuildPlugin (line 68) | function getInitialBuildPlugin(): { plugin: Plugin; initialBuild: Promis...
  method setup (line 96) | setup(build: PluginBuild) {
  function buildClient (line 154) | async function buildClient(isDev: boolean): Promise<void> {
  function minifyScriptDirectory (line 220) | async function minifyScriptDirectory(
  function minifyCSSFiles (line 251) | async function minifyCSSFiles(): Promise<void> {

FILE: build/engine-wasm.ts
  constant HYDROCHESS_WASM_DIR (line 18) | const HYDROCHESS_WASM_DIR = path.join(process.cwd(), 'src', 'client', 'p...
  constant LATEST_RELEASE_API_URL (line 21) | const LATEST_RELEASE_API_URL =
  function setupEngineWasm (line 41) | async function setupEngineWasm(): Promise<void> {

FILE: build/env.ts
  function setupEnv (line 15) | function setupEnv(): void {
  function ensureExists (line 21) | function ensureExists(): void {
  function generateSecret (line 62) | function generateSecret(length: number): string {
  function ensureValid (line 67) | function ensureValid(): void {

FILE: build/index.ts
  constant USE_DEVELOPMENT_BUILD (line 23) | const USE_DEVELOPMENT_BUILD = process.argv.includes('--dev');

FILE: build/plugins.ts
  function getESBuildLogStatusLogger (line 10) | function getESBuildLogStatusLogger(successMessage: string, failureMessag...

FILE: build/server.ts
  function buildServer (line 35) | async function buildServer(isDev: boolean): Promise<void> {

FILE: build/views.ts
  function buildViews (line 50) | async function buildViews(): Promise<void> {

FILE: dev-utils/post_processing_effects/posterize/PosterizePass.ts
  class PosterizePass (line 10) | class PosterizePass implements PostProcessPass {
    method constructor (line 25) | constructor(programManager: ProgramManager) {
    method render (line 34) | render(gl: WebGL2RenderingContext, inputTexture: WebGLTexture): void {

FILE: dev-utils/post_processing_effects/radial_distortion/RadialDistortionPass.ts
  class RadialDistortionPass (line 9) | class RadialDistortionPass implements PostProcessPass {
    method constructor (line 25) | constructor(programManager: ProgramManager) {
    method render (line 29) | render(gl: WebGL2RenderingContext, inputTexture: WebGLTexture): void {

FILE: dev-utils/post_processing_effects/rolling_hills/RollingHillsPass.ts
  class RollingHillsPass (line 9) | class RollingHillsPass implements PostProcessPass {
    method constructor (line 27) | constructor(programManager: ProgramManager) {
    method render (line 31) | render(gl: WebGL2RenderingContext, inputTexture: WebGLTexture): void {

FILE: dev-utils/scripts/PatreonAPI.ts
  function getPatreonPatrons (line 32) | function getPatreonPatrons(): string[] {

FILE: dev-utils/scripts/audio/processors/bitcrusher/BitcrusherNode.ts
  class BitcrusherNode (line 3) | class BitcrusherNode extends AudioWorkletNode {
    method constructor (line 4) | constructor(context: AudioContext) {
    method create (line 14) | public static async create(context: AudioContext): Promise<BitcrusherN...
    method bitDepth (line 32) | get bitDepth(): AudioParam | undefined {
    method downsampling (line 41) | get downsampling(): AudioParam | undefined {

FILE: dev-utils/scripts/audio/processors/bitcrusher/BitcrusherProcessor.ts
  type BitcrusherParameters (line 24) | interface BitcrusherParameters extends Record<string, Float32Array> {
  class BitcrusherProcessor (line 30) | class BitcrusherProcessor extends AudioWorkletProcessor {
    method parameterDescriptors (line 31) | static override get parameterDescriptors(): AudioParamDescriptor[] {
    method process (line 53) | process(

FILE: dev-utils/scripts/clientEventDispatcher.ts
  function dispatch (line 20) | function dispatch(eventName: string, data?: any): void {
  function listen (line 31) | function listen(eventName: string, callback: (event: CustomEvent) => voi...
  function removeListener (line 41) | function removeListener(eventName: string, callback: (event: CustomEvent...

FILE: dev-utils/scripts/events.ts
  type ExtractArr (line 16) | type ExtractArr<T extends any[]> = T extends (infer U)[] ? U : never
  type Eventlist (line 18) | interface Eventlist {
  function runEvent (line 22) | function runEvent<E extends Eventlist, N extends keyof E, A extends Para...
  function addEventListener (line 34) | function addEventListener<E extends Eventlist, N extends keyof E, L exte...
  function removeEventListener (line 46) | function removeEventListener<E extends Eventlist, N extends keyof E, L e...

FILE: dev-utils/scripts/gl-matrix.js
  function setMatrixArrayType (line 50) | function setMatrixArrayType(type) {
  function toRadian (line 60) | function toRadian(a) {
  function equals$9 (line 73) | function equals$9(a, b) {
  method ARRAY_TYPE (line 90) | get ARRAY_TYPE () { return ARRAY_TYPE; }
  function create$8 (line 109) | function create$8() {
  function clone$8 (line 128) | function clone$8(a) {
  function copy$8 (line 144) | function copy$8(out, a) {
  function identity$5 (line 158) | function identity$5(out) {
  function fromValues$8 (line 175) | function fromValues$8(m00, m01, m10, m11) {
  function set$8 (line 194) | function set$8(out, m00, m01, m10, m11) {
  function transpose$2 (line 209) | function transpose$2(out, a) {
  function invert$5 (line 233) | function invert$5(out, a) {
  function adjoint$2 (line 260) | function adjoint$2(out, a) {
  function determinant$3 (line 276) | function determinant$3(a) {
  function multiply$8 (line 288) | function multiply$8(out, a, b) {
  function rotate$4 (line 312) | function rotate$4(out, a, rad) {
  function scale$8 (line 334) | function scale$8(out, a, v) {
  function fromRotation$4 (line 359) | function fromRotation$4(out, rad) {
  function fromScaling$3 (line 380) | function fromScaling$3(out, v) {
  function str$8 (line 394) | function str$8(a) {
  function frob$3 (line 404) | function frob$3(a) {
  function LDU (line 415) | function LDU(L, D, U, a) {
  function add$8 (line 431) | function add$8(out, a, b) {
  function subtract$6 (line 447) | function subtract$6(out, a, b) {
  function exactEquals$8 (line 462) | function exactEquals$8(a, b) {
  function equals$8 (line 473) | function equals$8(a, b) {
  function multiplyScalar$3 (line 493) | function multiplyScalar$3(out, a, b) {
  function multiplyScalarAndAdd$3 (line 510) | function multiplyScalarAndAdd$3(out, a, b, scale) {
  function create$7 (line 585) | function create$7() {
  function clone$7 (line 606) | function clone$7(a) {
  function copy$7 (line 624) | function copy$7(out, a) {
  function identity$4 (line 640) | function identity$4(out) {
  function fromValues$7 (line 661) | function fromValues$7(a, b, c, d, tx, ty) {
  function set$7 (line 684) | function set$7(out, a, b, c, d, tx, ty) {
  function invert$4 (line 701) | function invert$4(out, a) {
  function determinant$2 (line 730) | function determinant$2(a) {
  function multiply$7 (line 742) | function multiply$7(out, a, b) {
  function rotate$3 (line 772) | function rotate$3(out, a, rad) {
  function scale$7 (line 798) | function scale$7(out, a, v) {
  function translate$3 (line 824) | function translate$3(out, a, v) {
  function fromRotation$3 (line 853) | function fromRotation$3(out, rad) {
  function fromScaling$2 (line 876) | function fromScaling$2(out, v) {
  function fromTranslation$3 (line 897) | function fromTranslation$3(out, v) {
  function str$7 (line 913) | function str$7(a) {
  function frob$2 (line 923) | function frob$2(a) {
  function add$7 (line 935) | function add$7(out, a, b) {
  function subtract$5 (line 953) | function subtract$5(out, a, b) {
  function multiplyScalar$2 (line 971) | function multiplyScalar$2(out, a, b) {
  function multiplyScalarAndAdd$2 (line 990) | function multiplyScalarAndAdd$2(out, a, b, scale) {
  function exactEquals$7 (line 1007) | function exactEquals$7(a, b) {
  function equals$7 (line 1018) | function equals$7(a, b) {
  function create$6 (line 1086) | function create$6() {
  function fromMat4$1 (line 1111) | function fromMat4$1(out, a) {
  function clone$6 (line 1130) | function clone$6(a) {
  function copy$6 (line 1151) | function copy$6(out, a) {
  function fromValues$6 (line 1178) | function fromValues$6(m00, m01, m02, m10, m11, m12, m20, m21, m22) {
  function set$6 (line 1207) | function set$6(out, m00, m01, m02, m10, m11, m12, m20, m21, m22) {
  function identity$3 (line 1226) | function identity$3(out) {
  function transpose$1 (line 1246) | function transpose$1(out, a) {
  function invert$3 (line 1280) | function invert$3(out, a) {
  function adjoint$1 (line 1320) | function adjoint$1(out, a) {
  function determinant$1 (line 1348) | function determinant$1(a) {
  function multiply$6 (line 1369) | function multiply$6(out, a, b) {
  function translate$2 (line 1408) | function translate$2(out, a, v) {
  function rotate$2 (line 1440) | function rotate$2(out, a, rad) {
  function scale$6 (line 1472) | function scale$6(out, a, v) {
  function fromTranslation$2 (line 1498) | function fromTranslation$2(out, v) {
  function fromRotation$2 (line 1522) | function fromRotation$2(out, rad) {
  function fromScaling$1 (line 1548) | function fromScaling$1(out, v) {
  function fromMat2d (line 1568) | function fromMat2d(out, a) {
  function fromQuat$1 (line 1589) | function fromQuat$1(out, q) {
  function normalFromMat4 (line 1626) | function normalFromMat4(out, a) {
  function projection (line 1683) | function projection(out, width, height) {
  function str$6 (line 1702) | function str$6(a) {
  function frob$1 (line 1712) | function frob$1(a) {
  function add$6 (line 1724) | function add$6(out, a, b) {
  function subtract$4 (line 1745) | function subtract$4(out, a, b) {
  function multiplyScalar$1 (line 1766) | function multiplyScalar$1(out, a, b) {
  function multiplyScalarAndAdd$1 (line 1788) | function multiplyScalarAndAdd$1(out, a, b, scale) {
  function exactEquals$6 (line 1808) | function exactEquals$6(a, b) {
  function equals$6 (line 1819) | function equals$6(a, b) {
  function create$5 (line 1900) | function create$5() {
  function clone$5 (line 1931) | function clone$5(a) {
  function copy$5 (line 1959) | function copy$5(out, a) {
  function fromValues$5 (line 2000) | function fromValues$5(m00, m01, m02, m03, m10, m11, m12, m13, m20, m21, ...
  function set$5 (line 2043) | function set$5(out, m00, m01, m02, m03, m10, m11, m12, m13, m20, m21, m2...
  function identity$2 (line 2069) | function identity$2(out) {
  function transpose (line 2096) | function transpose(out, a) {
  function invert$2 (line 2146) | function invert$2(out, a) {
  function adjoint (line 2209) | function adjoint(out, a) {
  function determinant (line 2263) | function determinant(a) {
  function multiply$5 (line 2302) | function multiply$5(out, a, b) {
  function translate$1 (line 2363) | function translate$1(out, a, v) {
  function scale$5 (line 2418) | function scale$5(out, a, v) {
  function rotate$1 (line 2450) | function rotate$1(out, a, rad, axis) {
  function rotateX$3 (line 2529) | function rotateX$3(out, a, rad) {
  function rotateY$3 (line 2573) | function rotateY$3(out, a, rad) {
  function rotateZ$3 (line 2617) | function rotateZ$3(out, a, rad) {
  function fromTranslation$1 (line 2664) | function fromTranslation$1(out, v) {
  function fromScaling (line 2695) | function fromScaling(out, v) {
  function fromRotation$1 (line 2727) | function fromRotation$1(out, rad, axis) {
  function fromXRotation (line 2776) | function fromXRotation(out, rad) {
  function fromYRotation (line 2810) | function fromYRotation(out, rad) {
  function fromZRotation (line 2844) | function fromZRotation(out, rad) {
  function fromRotationTranslation$1 (line 2882) | function fromRotationTranslation$1(out, q, v) {
  function fromQuat2 (line 2926) | function fromQuat2(out, a) {
  function getTranslation$1 (line 2961) | function getTranslation$1(out, mat) {
  function getScaling (line 2978) | function getScaling(out, mat) {
  function getRotation (line 3003) | function getRotation(out, mat) {
  function decompose (line 3059) | function decompose(out_r, out_t, out_s, mat) {
  function fromRotationTranslationScale (line 3136) | function fromRotationTranslationScale(out, q, v, s) {
  function fromRotationTranslationScaleOrigin (line 3196) | function fromRotationTranslationScaleOrigin(out, q, v, s, o) {
  function fromQuat (line 3256) | function fromQuat(out, q) {
  function frustum (line 3304) | function frustum(out, left, right, bottom, top, near, far) {
  function perspectiveNO (line 3340) | function perspectiveNO(out, fovy, aspect, near, far) {
  function perspectiveZO (line 3388) | function perspectiveZO(out, fovy, aspect, near, far) {
  function perspectiveFromFieldOfView (line 3428) | function perspectiveFromFieldOfView(out, fov, near, far) {
  function orthoNO (line 3468) | function orthoNO(out, left, right, bottom, top, near, far) {
  function orthoZO (line 3511) | function orthoZO(out, left, right, bottom, top, near, far) {
  function lookAt (line 3544) | function lookAt(out, eye, center, up) {
  function targetTo (line 3627) | function targetTo(out, eye, target, up) {
  function str$5 (line 3683) | function str$5(a) {
  function frob (line 3693) | function frob(a) {
  function add$5 (line 3705) | function add$5(out, a, b) {
  function subtract$3 (line 3733) | function subtract$3(out, a, b) {
  function multiplyScalar (line 3761) | function multiplyScalar(out, a, b) {
  function multiplyScalarAndAdd (line 3790) | function multiplyScalarAndAdd(out, a, b, scale) {
  function exactEquals$5 (line 3817) | function exactEquals$5(a, b) {
  function equals$5 (line 3828) | function equals$5(a, b) {
  function create$4 (line 3943) | function create$4() {
  function clone$4 (line 3961) | function clone$4(a) {
  function length$4 (line 3975) | function length$4(a) {
  function fromValues$4 (line 3990) | function fromValues$4(x, y, z) {
  function copy$4 (line 4005) | function copy$4(out, a) {
  function set$4 (line 4021) | function set$4(out, x, y, z) {
  function add$4 (line 4036) | function add$4(out, a, b) {
  function subtract$2 (line 4051) | function subtract$2(out, a, b) {
  function multiply$4 (line 4066) | function multiply$4(out, a, b) {
  function divide$2 (line 4081) | function divide$2(out, a, b) {
  function ceil$2 (line 4095) | function ceil$2(out, a) {
  function floor$2 (line 4109) | function floor$2(out, a) {
  function min$2 (line 4124) | function min$2(out, a, b) {
  function max$2 (line 4139) | function max$2(out, a, b) {
  function round$2 (line 4153) | function round$2(out, a) {
  function scale$4 (line 4168) | function scale$4(out, a, b) {
  function scaleAndAdd$2 (line 4184) | function scaleAndAdd$2(out, a, b, scale) {
  function distance$2 (line 4198) | function distance$2(a, b) {
  function squaredDistance$2 (line 4212) | function squaredDistance$2(a, b) {
  function squaredLength$4 (line 4225) | function squaredLength$4(a) {
  function negate$2 (line 4239) | function negate$2(out, a) {
  function inverse$2 (line 4253) | function inverse$2(out, a) {
  function normalize$4 (line 4267) | function normalize$4(out, a) {
  function dot$4 (line 4291) | function dot$4(a, b) {
  function cross$2 (line 4303) | function cross$2(out, a, b) {
  function lerp$4 (line 4325) | function lerp$4(out, a, b, t) {
  function slerp$1 (line 4344) | function slerp$1(out, a, b, t) {
  function hermite (line 4366) | function hermite(out, a, b, c, d, t) {
  function bezier (line 4389) | function bezier(out, a, b, c, d, t) {
  function random$3 (line 4410) | function random$3(out, scale) {
  function transformMat4$2 (line 4430) | function transformMat4$2(out, a, m) {
  function transformMat3$1 (line 4450) | function transformMat3$1(out, a, m) {
  function transformQuat$1 (line 4469) | function transformQuat$1(out, a, q) {
  function rotateX$2 (line 4511) | function rotateX$2(out, a, b, rad) {
  function rotateY$2 (line 4537) | function rotateY$2(out, a, b, rad) {
  function rotateZ$2 (line 4563) | function rotateZ$2(out, a, b, rad) {
  function angle$1 (line 4587) | function angle$1(a, b) {
  function zero$2 (line 4605) | function zero$2(out) {
  function str$4 (line 4618) | function str$4(a) {
  function exactEquals$4 (line 4629) | function exactEquals$4(a, b) {
  function equals$4 (line 4640) | function equals$4(a, b) {
  function create$3 (line 4801) | function create$3() {
  function clone$3 (line 4820) | function clone$3(a) {
  function fromValues$3 (line 4838) | function fromValues$3(x, y, z, w) {
  function copy$3 (line 4854) | function copy$3(out, a) {
  function set$3 (line 4872) | function set$3(out, x, y, z, w) {
  function add$3 (line 4888) | function add$3(out, a, b) {
  function subtract$1 (line 4904) | function subtract$1(out, a, b) {
  function multiply$3 (line 4920) | function multiply$3(out, a, b) {
  function divide$1 (line 4936) | function divide$1(out, a, b) {
  function ceil$1 (line 4951) | function ceil$1(out, a) {
  function floor$1 (line 4966) | function floor$1(out, a) {
  function min$1 (line 4982) | function min$1(out, a, b) {
  function max$1 (line 4998) | function max$1(out, a, b) {
  function round$1 (line 5013) | function round$1(out, a) {
  function scale$3 (line 5029) | function scale$3(out, a, b) {
  function scaleAndAdd$1 (line 5046) | function scaleAndAdd$1(out, a, b, scale) {
  function distance$1 (line 5061) | function distance$1(a, b) {
  function squaredDistance$1 (line 5076) | function squaredDistance$1(a, b) {
  function length$3 (line 5090) | function length$3(a) {
  function squaredLength$3 (line 5104) | function squaredLength$3(a) {
  function negate$1 (line 5119) | function negate$1(out, a) {
  function inverse$1 (line 5134) | function inverse$1(out, a) {
  function normalize$3 (line 5149) | function normalize$3(out, a) {
  function dot$3 (line 5174) | function dot$3(a, b) {
  function cross$1 (line 5187) | function cross$1(out, u, v, w) {
  function lerp$3 (line 5214) | function lerp$3(out, a, b, t) {
  function random$2 (line 5233) | function random$2(out, scale) {
  function transformMat4$1 (line 5269) | function transformMat4$1(out, a, m) {
  function transformQuat (line 5289) | function transformQuat(out, a, q) {
  function zero$1 (line 5316) | function zero$1(out) {
  function str$3 (line 5330) | function str$3(a) {
  function exactEquals$3 (line 5341) | function exactEquals$3(a, b) {
  function equals$3 (line 5352) | function equals$3(a, b) {
  function create$2 (line 5509) | function create$2() {
  function identity$1 (line 5528) | function identity$1(out) {
  function setAxisAngle (line 5545) | function setAxisAngle(out, axis, rad) {
  function getAxisAngle (line 5568) | function getAxisAngle(out_axis, q) {
  function getAngle (line 5593) | function getAngle(a, b) {
  function multiply$2 (line 5606) | function multiply$2(out, a, b) {
  function rotateX$1 (line 5630) | function rotateX$1(out, a, rad) {
  function rotateY$1 (line 5653) | function rotateY$1(out, a, rad) {
  function rotateZ$1 (line 5676) | function rotateZ$1(out, a, rad) {
  function calculateW (line 5700) | function calculateW(out, a) {
  function exp (line 5718) | function exp(out, a) {
  function ln (line 5740) | function ln(out, a) {
  function pow (line 5762) | function pow(out, a, b) {
  function slerp (line 5778) | function slerp(out, a, b, t) {
  function random$1 (line 5829) | function random$1(out) {
  function invert$1 (line 5851) | function invert$1(out, a) {
  function conjugate$1 (line 5874) | function conjugate$1(out, a) {
  function fromMat3 (line 5893) | function fromMat3(out, m) {
  function fromEuler (line 5938) | function fromEuler(out, x, y, z) {
  function str$2 (line 6007) | function str$2(a) {
  function equals$2 (line 6162) | function equals$2(a, b) {
  function create$1 (line 6312) | function create$1() {
  function clone$1 (line 6336) | function clone$1(a) {
  function fromValues$1 (line 6363) | function fromValues$1(x1, y1, z1, w1, x2, y2, z2, w2) {
  function fromRotationTranslationValues (line 6389) | function fromRotationTranslationValues(x1, y1, z1, w1, x2, y2, z2) {
  function fromRotationTranslation (line 6414) | function fromRotationTranslation(out, q, t) {
  function fromTranslation (line 6441) | function fromTranslation(out, t) {
  function fromRotation (line 6461) | function fromRotation(out, q) {
  function fromMat4 (line 6481) | function fromMat4(out, a) {
  function copy$1 (line 6499) | function copy$1(out, a) {
  function identity (line 6517) | function identity(out) {
  function set$1 (line 6544) | function set$1(out, x1, y1, z1, w1, x2, y2, z2, w2) {
  function getDual (line 6570) | function getDual(out, a) {
  function setDual (line 6596) | function setDual(out, q) {
  function getTranslation (line 6610) | function getTranslation(out, a) {
  function translate (line 6633) | function translate(out, a, v) {
  function rotateX (line 6664) | function rotateX(out, a, rad) {
  function rotateY (line 6697) | function rotateY(out, a, rad) {
  function rotateZ (line 6730) | function rotateZ(out, a, rad) {
  function rotateByQuatAppend (line 6763) | function rotateByQuatAppend(out, a, q) {
  function rotateByQuatPrepend (line 6795) | function rotateByQuatPrepend(out, q, a) {
  function rotateAroundAxis (line 6828) | function rotateAroundAxis(out, a, axis, rad) {
  function add$1 (line 6869) | function add$1(out, a, b) {
  function multiply$1 (line 6889) | function multiply$1(out, a, b) {
  function scale$1 (line 6932) | function scale$1(out, a, b) {
  function lerp$1 (line 6964) | function lerp$1(out, a, b, t) {
  function invert (line 6985) | function invert(out, a) {
  function conjugate (line 7006) | function conjugate(out, a) {
  function normalize$1 (line 7056) | function normalize$1(out, a) {
  function str$1 (line 7089) | function str$1(a) {
  function exactEquals$1 (line 7100) | function exactEquals$1(a, b) {
  function equals$1 (line 7111) | function equals$1(a, b) {
  function create (line 7185) | function create() {
  function clone (line 7202) | function clone(a) {
  function fromValues (line 7216) | function fromValues(x, y) {
  function copy (line 7230) | function copy(out, a) {
  function set (line 7244) | function set(out, x, y) {
  function add (line 7258) | function add(out, a, b) {
  function subtract (line 7272) | function subtract(out, a, b) {
  function multiply (line 7286) | function multiply(out, a, b) {
  function divide (line 7300) | function divide(out, a, b) {
  function ceil (line 7313) | function ceil(out, a) {
  function floor (line 7326) | function floor(out, a) {
  function min (line 7340) | function min(out, a, b) {
  function max (line 7354) | function max(out, a, b) {
  function round (line 7367) | function round(out, a) {
  function scale (line 7381) | function scale(out, a, b) {
  function scaleAndAdd (line 7396) | function scaleAndAdd(out, a, b, scale) {
  function distance (line 7409) | function distance(a, b) {
  function squaredDistance (line 7422) | function squaredDistance(a, b) {
  function length (line 7434) | function length(a) {
  function squaredLength (line 7446) | function squaredLength(a) {
  function negate (line 7459) | function negate(out, a) {
  function inverse (line 7472) | function inverse(out, a) {
  function normalize (line 7485) | function normalize(out, a) {
  function dot (line 7507) | function dot(a, b) {
  function cross (line 7520) | function cross(out, a, b) {
  function lerp (line 7536) | function lerp(out, a, b, t) {
  function random (line 7551) | function random(out, scale) {
  function transformMat2 (line 7567) | function transformMat2(out, a, m) {
  function transformMat2d (line 7583) | function transformMat2d(out, a, m) {
  function transformMat3 (line 7600) | function transformMat3(out, a, m) {
  function transformMat4 (line 7618) | function transformMat4(out, a, m) {
  function rotate (line 7634) | function rotate(out, a, b, rad) {
  function angle (line 7652) | function angle(a, b) {
  function zero (line 7671) | function zero(out) {
  function str (line 7683) | function str(a) {
  function exactEquals (line 7694) | function exactEquals(a, b) {
  function equals (line 7705) | function equals(a, b) {

FILE: dev-utils/scripts/icn-regex-matching.ts
  function ShortToLong_Format (line 58) | function ShortToLong_Format(icn: string): LongFormatOut {

FILE: dev-utils/scripts/meshSimplification.ts
  function simplifyMesh (line 15) | function simplifyMesh(voidList: PooledArray<Coords>): BoundingBox[] { //...

FILE: dev-utils/scripts/positionnormalizer/moveexpander.ts
  function expandMove (line 31) | function expandMove(AllAxisOrders: AxisOrders, pieceTransformations: Pie...
  function trueEndCoordsDeterminer (line 166) | function trueEndCoordsDeterminer(movementLine: LineCoefficients, axisOfI...

FILE: dev-utils/scripts/positionnormalizer/positioncompressor.ts
  type CompressionInfo (line 25) | interface CompressionInfo {
  type PieceTransform (line 39) | type PieceTransform = {
  type AxisOrders (line 55) | type AxisOrders = Record<Vec2Key, AxisOrder>;
  type AxisOrder (line 61) | type AxisOrder = AxisGroup[];
  type AxisGroup (line 67) | type AxisGroup = {
  type AxisDeterminer (line 78) | type AxisDeterminer = (_coords: Coords) => bigint;
  type OrthoAxis (line 81) | type OrthoAxis = '1,0' | '0,1';
  type DiagAxis (line 83) | type DiagAxis = '1,1' | '1,-1';
  type Axis (line 85) | type Axis = OrthoAxis | DiagAxis;
  type VariableName (line 94) | type VariableName = `x-${number}` | `y-${number}` | `u-${number}` | `v-$...
  type Column (line 99) | type Column = {
  constant UNSAFE_BOUND_BIGINT (line 121) | const UNSAFE_BOUND_BIGINT = BigInt(Math.trunc(Number.MAX_SAFE_INTEGER * ...
  constant MIN_ARBITRARY_DISTANCE (line 151) | const MIN_ARBITRARY_DISTANCE = 10n;
  constant AXIS_DETERMINERS (line 160) | const AXIS_DETERMINERS = {
  function compressPosition (line 188) | function compressPosition(position: Map<CoordsKey, number>, mode: 'ortho...
  function getVariableName (line 550) | function getVariableName(axis: Axis, index: number): VariableName {
  function getConstraintName (line 555) | function getConstraintName(varName: VariableName) {
  function addConstraintToModel (line 565) | function addConstraintToModel(model: Model, constraint_name: string, col...
  function RecenterTransformedPosition (line 591) | function RecenterTransformedPosition(allPieces: PieceTransform[], allAxi...

FILE: dev-utils/scripts/positionnormalizer/positioncompressorplusintersections.ts
  type CompressionInfo (line 26) | interface CompressionInfo {
  type PieceTransform (line 40) | type PieceTransform = {
  type AxisOrders (line 55) | type AxisOrders = Record<Vec2Key, AxisOrder>;
  type AxisOrder (line 61) | type AxisOrder = AxisGroup[];
  type AxisGroup (line 67) | type AxisGroup = {
  type AxisDeterminer (line 77) | type AxisDeterminer = (_coords: Coords) => bigint;
  type OrthoAxis (line 80) | type OrthoAxis = '1,0' | '0,1';
  type DiagAxis (line 82) | type DiagAxis = '1,1' | '1,-1';
  type Axis (line 84) | type Axis = OrthoAxis | DiagAxis;
  type VariableName (line 92) | type VariableName = `x-${number}` | `y-${number}` | `u-${number}` | `v-$...
  type Column (line 97) | type Column = {
  constant UNSAFE_BOUND_BIGINT (line 117) | const UNSAFE_BOUND_BIGINT = BigInt(Math.trunc(Number.MAX_SAFE_INTEGER * ...
  constant MIN_ARBITRARY_DISTANCE (line 146) | const MIN_ARBITRARY_DISTANCE = 10n;
  constant MIN_ARBITRARY_DISTANCE_BD (line 147) | const MIN_ARBITRARY_DISTANCE_BD = bd.fromBigInt(MIN_ARBITRARY_DISTANCE);
  constant AXIS_DETERMINERS (line 155) | const AXIS_DETERMINERS = {
  constant AXIS_DETERMINERS_BD (line 166) | const AXIS_DETERMINERS_BD = {
  constant INTERSECTION_TYPE (line 182) | const INTERSECTION_TYPE = -1;
  function compressPosition (line 197) | function compressPosition(
  function addIntersectionsToPieces (line 613) | function addIntersectionsToPieces(
  function stringifyBDCoords (line 688) | function stringifyBDCoords(coords: BDCoords): string {
  function getVariableName (line 699) | function getVariableName(axis: Axis, index: number): VariableName {
  function getConstraintName (line 715) | function getConstraintName(varName: VariableName) {
  function addConstraintToModel (line 725) | function addConstraintToModel(
  function RecenterTransformedPosition (line 754) | function RecenterTransformedPosition(allPieces: PieceTransform[], allAxi...

FILE: dev-utils/scripts/vertexdatatotexture.ts
  function convertVertexDataToTexture (line 23) | function convertVertexDataToTexture(gl: WebGL2RenderingContext, vertexDa...

FILE: dev-utils/spritesheet_generator/spritesheet.ts
  type TextureData (line 22) | interface TextureData {
  function getSpritesheet (line 63) | function getSpritesheet(): WebGLTexture {
  function getSpritesheetDataPieceWidth (line 68) | function getSpritesheetDataPieceWidth(): number {
  function getSpritesheetDataTexLocation (line 74) | function getSpritesheetDataTexLocation(type: number): DoubleCoords {
  function initSpritesheetForGame (line 85) | async function initSpritesheetForGame(gl: WebGL2RenderingContext, boards...
  function deleteSpritesheet (line 113) | function deleteSpritesheet(): void {
  function getTexDataOfType (line 123) | function getTexDataOfType(type: number, rotation: number = 1): TextureDa...
  function getTexDataGeneric (line 133) | function getTexDataGeneric(rotation = 1): TextureData {
  function getTexDataFromLocationAndWidth (line 142) | function getTexDataFromLocationAndWidth(

FILE: dev-utils/spritesheet_generator/spritesheetGenerator.ts
  type SpritesheetData (line 10) | type SpritesheetData = {
  constant PREFERRED_IMG_SIZE (line 21) | const PREFERRED_IMG_SIZE = 512;
  function generateSpritesheet (line 31) | async function generateSpritesheet(
  function generateSpriteSheetData (line 109) | function generateSpriteSheetData(images: HTMLImageElement[], gridSize: n...
  function roundUpToNextPowerOf2 (line 147) | function roundUpToNextPowerOf2(num: number): number {

FILE: scripts/add-file-paths.ts
  function extractPathFromComment (line 19) | function extractPathFromComment(line: string): string | null {
  function processFile (line 32) | function processFile(filePath: string): void {
  function main (line 106) | function main(): void {

FILE: scripts/generate-translation-types.ts
  function generateDotPaths (line 26) | function generateDotPaths(obj: TomlTable | any, prefix = ''): string[] {
  function generateNestedType (line 50) | function generateNestedType(obj: TomlTable | any, indentLevel = 1): stri...
  function generateTypes (line 76) | function generateTypes(): void {

FILE: scripts/optimize-images.ts
  function getAllImagePaths (line 56) | function getAllImagePaths(dirPath: string): string[] {
  function logProgress (line 105) | function logProgress(imageName: string, format: string): void {

FILE: scripts/organize-imports.ts
  constant FROM_WITH_QUOTE_PATTERN (line 49) | const FROM_WITH_QUOTE_PATTERN = /\sfrom\s+['"]/;
  constant SHARED_DIR (line 52) | const SHARED_DIR = path.resolve(process.cwd(), 'src/shared');
  constant CLIENT_DIR (line 54) | const CLIENT_DIR = path.resolve(process.cwd(), 'src/client');
  constant TESTS_DIR (line 56) | const TESTS_DIR = path.resolve(process.cwd(), 'src/tests');
  constant SERVER_DIR (line 58) | const SERVER_DIR = path.resolve(process.cwd(), 'src/server');
  type Import (line 62) | interface Import {
  function resolveImportSourceDir (line 81) | function resolveImportSourceDir(
  function parseImport (line 120) | function parseImport(importText: string, hasTsIgnore: boolean, currentFi...
  function compareImports (line 159) | function compareImports(a: Import, b: Import): number {
  function findImportBoundaries (line 171) | function findImportBoundaries(lines: string[]): { start: number; end: nu...
  function extractImports (line 205) | function extractImports(
  function organizeImports (line 311) | function organizeImports(imports: Import[]): string {
  function processFile (line 405) | function processFile(filePath: string): boolean {
  function main (line 447) | function main(): void {

FILE: src/client/scripts/cjs/game/htmlscript.ts
  function callback_LoadingError (line 24) | function callback_LoadingError(): void {
  function removeOnerror (line 58) | function removeOnerror(this: HTMLElement): void {
  function closeLoadingScreenListeners (line 69) | function closeLoadingScreenListeners(): void {
  function callback_Offline (line 74) | function callback_Offline(): void {
  function callback_Online (line 79) | function callback_Online(): void {

FILE: src/client/scripts/esm/audio/AudioEffects.ts
  type NodeChain (line 10) | interface NodeChain {
  type EffectConfigBase (line 16) | interface EffectConfigBase {
  type EffectConfig (line 29) | type EffectConfig = EffectConfigBase & { type: 'reverb'; durationSecs: n...
  function createEffectNode (line 41) | function createEffectNode(audioContext: AudioContext, config: EffectConf...
  function generateConvolverNode (line 86) | function generateConvolverNode(audioContext: AudioContext, durationSecs:...
  function impulseResponse (line 92) | function impulseResponse(audioContext: AudioContext, duration: number): ...

FILE: src/client/scripts/esm/audio/AudioManager.ts
  type AudioBufferWithGainNode (line 14) | type AudioBufferWithGainNode = AudioBufferSourceNode & { gainNode: GainN...
  type SoundObject (line 16) | interface SoundObject {
  type PlaySoundOptions (line 43) | interface PlaySoundOptions {
  constant VOLUME_DANGER_THRESHOLD (line 68) | const VOLUME_DANGER_THRESHOLD = 4;
  function getContext (line 144) | function getContext(): AudioContext {
  function getDestination (line 153) | function getDestination(): AudioNode {
  function fadeInDownsampler (line 160) | function fadeInDownsampler(durationMillis: number): void {
  function fadeOutDownsampler (line 170) | function fadeOutDownsampler(durationMillis: number): void {
  function playAudio (line 182) | function playAudio(
  function scheduleDisconnection (line 262) | function scheduleDisconnection(
  function createBufferSource (line 301) | function createBufferSource(
  function generateGainNode (line 320) | function generateGainNode(audioContext: AudioContext, volume: number): G...
  function connectNodeChain (line 337) | function connectNodeChain(
  function fadeIn (line 360) | function fadeIn(source: AudioBufferWithGainNode, targetVolume: number, e...
  function fadeOut (line 375) | function fadeOut(source: AudioBufferWithGainNode, endTime: number): void {
  function decodeAudioData (line 388) | function decodeAudioData(buffer: ArrayBuffer): Promise<AudioBuffer> {

FILE: src/client/scripts/esm/audio/AudioUtils.ts
  constant FADE_CURVE_RESOLUTION (line 10) | const FADE_CURVE_RESOLUTION = 100;
  constant FADE_RAMP_CURVATURE (line 18) | const FADE_RAMP_CURVATURE = 0.4;
  function applyPerceptualFade (line 30) | function applyPerceptualFade(

FILE: src/client/scripts/esm/audio/LFOFactory.ts
  type LFOConfig (line 10) | interface LFOConfig {
  type LFOUnit (line 17) | interface LFOUnit {
  function createLFO (line 31) | function createLFO(context: AudioContext, config: LFOConfig): LFOUnit {
  function createPerlinLFO (line 49) | function createPerlinLFO(context: AudioContext, rate: number): AudioBuff...

FILE: src/client/scripts/esm/audio/SoundLayer.ts
  type LayerConfig (line 18) | interface LayerConfig {
  type SourceConfig (line 25) | type SourceConfig = NoiseSourceConfig | OscillatorSourceConfig;
  type NoiseSourceConfig (line 28) | interface NoiseSourceConfig {
  type OscillatorSourceConfig (line 33) | interface OscillatorSourceConfig {
  type FilterConfig (line 41) | interface FilterConfig {
  type ModulatedParamConfig (line 59) | interface ModulatedParamConfig {
  class SoundLayer (line 69) | class SoundLayer {
    method constructor (line 74) | constructor(
    method connect (line 148) | public connect(destination: AudioNode): void {
    method start (line 153) | public start(): void {
    method stop (line 160) | public stop(): void {

FILE: src/client/scripts/esm/audio/SoundscapePlayer.ts
  type SoundscapeConfig (line 17) | interface SoundscapeConfig {
  constant NOISE_DURATION_SECS (line 28) | const NOISE_DURATION_SECS = 10;
  class SoundscapePlayer (line 33) | class SoundscapePlayer {
    method constructor (line 51) | constructor(config: SoundscapeConfig) {
    method initializeAndPlay (line 61) | private initializeAndPlay(): void {
    method stop (line 100) | public stop(): void {
    method fadeIn (line 116) | public fadeIn(durationMillis: number): void {
    method fadeOut (line 131) | public fadeOut(durationMillis: number): void {

FILE: src/client/scripts/esm/audio/processors/downsampler/DownsamplerNode.ts
  class DownsamplerNode (line 3) | class DownsamplerNode extends AudioWorkletNode {
    method constructor (line 4) | constructor(context: AudioContext) {
    method create (line 13) | public static async create(context: AudioContext): Promise<Downsampler...
    method downsampling (line 32) | get downsampling(): AudioParam | undefined {

FILE: src/client/scripts/esm/audio/processors/downsampler/DownsamplerProcessor.ts
  type DownsamplerParameters (line 24) | interface DownsamplerParameters extends Record<string, Float32Array> {
  class DownsamplerProcessor (line 29) | class DownsamplerProcessor extends AudioWorkletProcessor {
    method parameterDescriptors (line 30) | static override get parameterDescriptors(): AudioParamDescriptor[] {
    method process (line 45) | process(

FILE: src/client/scripts/esm/audio/processors/worklet-types.ts
  type AudioParamDescriptor (line 9) | interface AudioParamDescriptor {

FILE: src/client/scripts/esm/chess/rendering/checkerboardgenerator.ts
  function createCheckerboardIMG (line 15) | function createCheckerboardIMG(

FILE: src/client/scripts/esm/chess/rendering/imagecache.ts
  function initImagesForGame (line 43) | async function initImagesForGame(boardsim: Board): Promise<void> {
  function getPieceImage (line 119) | function getPieceImage(type: number): HTMLImageElement {
  function deleteImageCache (line 135) | function deleteImageCache(): void {

FILE: src/client/scripts/esm/chess/rendering/svgcache.ts
  function getSVGElements (line 33) | async function getSVGElements(
  function fetchMissingTypes (line 48) | async function fetchMissingTypes(locations: Set<string>): Promise<void> {
  function fetchLocation (line 57) | async function fetchLocation(location: string): Promise<void> {
  function tintSVG (line 95) | function tintSVG(svgElement: SVGElement, color: Color): SVGElement {
  function getSVGColorPriority (line 158) | function getSVGColorPriority(color: Player): string[] {
  function getNeededSVGLocations (line 186) | function getNeededSVGLocations(types: number[]): Set<string> {
  function getSVGIDs (line 210) | function getSVGIDs(types: number[], width?: number, height?: number): SV...
  function showCache (line 249) | function showCache(): void {

FILE: src/client/scripts/esm/chess/rendering/texturecache.ts
  function initTexturesForGame (line 30) | async function initTexturesForGame(gl: WebGL2RenderingContext, boardsim:...
  function getTexture (line 63) | function getTexture(type: number): WebGLTexture {

FILE: src/client/scripts/esm/components/header/currpage-greyer.ts
  function greyBackgroundOfCurrPage (line 16) | function greyBackgroundOfCurrPage(): void {
  function updateColorOfProfileButton (line 30) | function updateColorOfProfileButton(): void {
  function initListeners (line 42) | function initListeners(): void {

FILE: src/client/scripts/esm/components/header/dropdowns/appearancedropdown.ts
  function showCheckmarkOnSelectedOptions (line 32) | function showCheckmarkOnSelectedOptions(): void {
  function addThemesToThemesDropdown (line 38) | async function addThemesToThemesDropdown(): Promise<void> {
  function open (line 63) | function open(): void {
  function close (line 67) | function close(): void {
  function initListeners (line 72) | function initListeners(): void {
  function closeListeners (line 82) | function closeListeners(): void {
  function initThemeChangeListeners (line 92) | function initThemeChangeListeners(): void {
  function closeThemeChangeListeners (line 98) | function closeThemeChangeListeners(): void {
  function selectTheme (line 105) | function selectTheme(event: Event): void {
  function updateThemeSelectedStyling (line 117) | function updateThemeSelectedStyling(): void {
  function toggleCoordinates (line 126) | function toggleCoordinates(): void {
  function toggleStarfield (line 130) | function toggleStarfield(): void {
  function toggleAdvancedEffects (line 134) | function toggleAdvancedEffects(): void {

FILE: src/client/scripts/esm/components/header/dropdowns/gameplaydropdown.ts
  function showCheckmarkOnSelectedOptions (line 32) | function showCheckmarkOnSelectedOptions(): void {
  function open (line 40) | function open(): void {
  function close (line 45) | function close(): void {
  function initListeners (line 51) | function initListeners(): void {
  function closeListeners (line 59) | function closeListeners(): void {
  function toggleDrag (line 68) | function toggleDrag(): void {
  function togglePremove (line 71) | function togglePremove(): void {
  function toggleAnimations (line 74) | function toggleAnimations(): void {
  function toggleFastTransitions (line 77) | function toggleFastTransitions(): void {
  function toggleLingeringAnnotations (line 80) | function toggleLingeringAnnotations(): void {

FILE: src/client/scripts/esm/components/header/dropdowns/languagedropdown.ts
  function addLngQueryParamToLink (line 38) | function addLngQueryParamToLink(href: string): string {
  function removeLngQueryParam (line 54) | function removeLngQueryParam(): void {
  function open (line 65) | function open(): void {
  function close (line 70) | function close(): void {
  function initListeners (line 76) | function initListeners(): void {
  function closeListeners (line 82) | function closeListeners(): void {
  function onLanguageClicked (line 89) | function onLanguageClicked(event: Event): void {

FILE: src/client/scripts/esm/components/header/dropdowns/legalmovedropdown.ts
  function showCheckmarkOnSelectedOption (line 26) | function showCheckmarkOnSelectedOption(): void {
  function open (line 34) | function open(): void {
  function close (line 39) | function close(): void {
  function initListeners (line 45) | function initListeners(): void {
  function closeListeners (line 50) | function closeListeners(): void {
  function toggleSquares (line 56) | function toggleSquares(): void {
  function toggleDots (line 65) | function toggleDots(): void {
  function hideAllCheckmarks (line 74) | function hideAllCheckmarks(): void {
  function dispatchLegalMoveChangeEvent (line 80) | function dispatchLegalMoveChangeEvent(): void {

FILE: src/client/scripts/esm/components/header/dropdowns/perspectivedropdown.ts
  function setInitialValues (line 47) | function setInitialValues(): void {
  function open (line 55) | function open(): void {
  function close (line 60) | function close(): void {
  function initListeners (line 66) | function initListeners(): void {
  function closeListeners (line 72) | function closeListeners(): void {
  function onMouseSensitivityChange (line 79) | function onMouseSensitivityChange(event: Event): void {
  function onFOVChange (line 84) | function onFOVChange(event: Event): void {
  function setMouseSensitivity (line 90) | function setMouseSensitivity(value: number): void {
  function setFOV (line 94) | function setFOV(value: number): void {
  function updateMouseSensitivityOutput (line 99) | function updateMouseSensitivityOutput(): void {
  function updateFOVOutput (line 103) | function updateFOVOutput(): void {
  function updateFOVResetDefaultButton (line 108) | function updateFOVResetDefaultButton(value: number): void {
  function resetFOVDefault (line 113) | function resetFOVDefault(): void {

FILE: src/client/scripts/esm/components/header/dropdowns/sounddropdown.ts
  function setInitialValues (line 33) | function setInitialValues(): void {
  function open (line 40) | function open(): void {
  function close (line 45) | function close(): void {
  function initListeners (line 51) | function initListeners(): void {
  function closeListeners (line 56) | function closeListeners(): void {
  function onMasterVolumeChange (line 62) | function onMasterVolumeChange(event: Event): void {
  function toggleAmbience (line 68) | function toggleAmbience(): void {
  function updateMasterVolumeOutput (line 72) | function updateMasterVolumeOutput(): void {

FILE: src/client/scripts/esm/components/header/faviconselector.ts
  function switchFavicon (line 8) | function switchFavicon(theme: 'dark' | 'light'): void {

FILE: src/client/scripts/esm/components/header/header.ts
  function initListeners (line 34) | function initListeners(): void {
  function updateNavigationLinks (line 44) | function updateNavigationLinks(): void {
  function updateViewportHeight (line 99) | function updateViewportHeight(): void {

FILE: src/client/scripts/esm/components/header/news-notification.ts
  function createNotificationBadge (line 18) | function createNotificationBadge(count: number): HTMLSpanElement {
  function fetchUnreadNewsCount (line 51) | async function fetchUnreadNewsCount(): Promise<number> {
  function updateNotificationBadge (line 75) | async function updateNotificationBadge(): Promise<void> {
  function showNotificationBadge (line 96) | function showNotificationBadge(count: number): void {
  function removeNotificationBadge (line 114) | function removeNotificationBadge(): void {
  function init (line 124) | function init(): void {

FILE: src/client/scripts/esm/components/header/pingmeter.ts
  function initEventListeners (line 22) | function initEventListeners(): void {
  function updatePing (line 29) | function updatePing(event: CustomEvent): void {
  function updateBarCount (line 37) | function updateBarCount(ping: number): void {
  function removeAllColor (line 47) | function removeAllColor(): void {
  function getBarCount (line 60) | function getBarCount(ping: number): number {
  function showPing_hideLoadingAnim (line 67) | function showPing_hideLoadingAnim(): void {
  function openMeterAndDisplayLoading (line 74) | function openMeterAndDisplayLoading(): void {
  function socketClosed (line 86) | function socketClosed(event: CustomEvent): void {
  function closeMeter (line 95) | function closeMeter(): void {

FILE: src/client/scripts/esm/components/header/preferences.ts
  type ClientSidePreferences (line 30) | interface ClientSidePreferences {
  type ServerSidePreferences (line 45) | interface ServerSidePreferences {
  type Preferences (line 53) | type Preferences = ServerSidePreferences & ClientSidePreferences;
  function loadPreferences (line 88) | function loadPreferences(): void {
  function savePreferences (line 117) | function savePreferences(): void {
  function onChangeMade (line 125) | function onChangeMade(): void {
  function sendPrefsToServer (line 130) | async function sendPrefsToServer(): Promise<void> {
  function POSTPrefs (line 140) | async function POSTPrefs(preparedPrefs: ServerSidePreferences): Promise<...
  function preparePrefs (line 174) | function preparePrefs(): ServerSidePreferences {
  function getTheme (line 184) | function getTheme(): string {
  function setTheme (line 188) | function setTheme(theme: string): void {
  function getCoordinatesEnabled (line 195) | function getCoordinatesEnabled(): boolean {
  function setCoordinatesEnabled (line 199) | function setCoordinatesEnabled(value: boolean): void {
  function getStarfieldMode (line 204) | function getStarfieldMode(): boolean {
  function setStarfieldMode (line 208) | function setStarfieldMode(value: boolean): void {
  function getLegalMovesShape (line 216) | function getLegalMovesShape(): 'dots' | 'squares' {
  function setLegalMovesShape (line 220) | function setLegalMovesShape(legal_moves: 'dots' | 'squares'): void {
  function getDragEnabled (line 228) | function getDragEnabled(): boolean {
  function setDragEnabled (line 232) | function setDragEnabled(drag_enabled: boolean): void {
  function getPremoveEnabled (line 239) | function getPremoveEnabled(): boolean {
  function setPremoveMode (line 243) | function setPremoveMode(value: boolean): void {
  function getFastTransitionsMode (line 251) | function getFastTransitionsMode(): boolean {
  function setFastTransitionsMode (line 255) | function setFastTransitionsMode(value: boolean): void {
  function getAnimationsMode (line 263) | function getAnimationsMode(): boolean {
  function setAnimationsMode (line 267) | function setAnimationsMode(animations_enabled: boolean): void {
  function getPerspectiveSensitivity (line 273) | function getPerspectiveSensitivity(): number {
  function setPerspectiveSensitivity (line 277) | function setPerspectiveSensitivity(perspective_sensitivity: number): void {
  function getPerspectiveFOV (line 284) | function getPerspectiveFOV(): number {
  function getDefaultPerspectiveFOV (line 288) | function getDefaultPerspectiveFOV(): number {
  function setPerspectiveFOV (line 292) | function setPerspectiveFOV(perspective_fov: number): void {
  function getLingeringAnnotationsMode (line 300) | function getLingeringAnnotationsMode(): boolean {
  function setLingeringAnnotationsMode (line 304) | function setLingeringAnnotationsMode(value: boolean): void {
  function getAdvancedEffectsMode (line 314) | function getAdvancedEffectsMode(): boolean {
  function setAdvancedEffectsMode (line 318) | function setAdvancedEffectsMode(value: boolean): void {
  function getMasterVolume (line 323) | function getMasterVolume(): number {
  function setMasterVolume (line 327) | function setMasterVolume(master_volume: number): void {
  function getAmbienceEnabled (line 339) | function getAmbienceEnabled(): boolean {
  function setAmbienceEnabled (line 343) | function setAmbienceEnabled(ambience_enabled: boolean): void {
  function getColorOfLightTiles (line 355) | function getColorOfLightTiles(): Color {
  function getColorOfDarkTiles (line 360) | function getColorOfDarkTiles(): Color {
  function getLegalMoveHighlightColor (line 365) | function getLegalMoveHighlightColor({
  function getLastMoveHighlightColor (line 380) | function getLastMoveHighlightColor(): Color {
  function getCheckHighlightColor (line 385) | function getCheckHighlightColor(): Color {
  function getBoxOutlineColor (line 390) | function getBoxOutlineColor(): Color {
  function getAnnoteSquareColor (line 395) | function getAnnoteSquareColor(): Color {
  function getAnnoteArrowColor (line 400) | function getAnnoteArrowColor(): Color {
  function getTintColorOfType (line 406) | function getTintColorOfType(type: number): Color {

FILE: src/client/scripts/esm/components/header/settings.ts
  function toggleSettingsDropdown (line 73) | function toggleSettingsDropdown(): void {
  function openSettingsDropdown (line 77) | function openSettingsDropdown(): void {
  function closeAllSettingsDropdowns (line 84) | function closeAllSettingsDropdowns(): void {
  function closeMainSettingsDropdown (line 91) | function closeMainSettingsDropdown(): void {
  function closeAllSettingsDropdownsExceptMainOne (line 96) | function closeAllSettingsDropdownsExceptMainOne(): void {
  function initSettingsListeners (line 105) | function initSettingsListeners(): void {
  function closeSettingsListeners (line 119) | function closeSettingsListeners(): void {
  function closeSettingsDropdownIfClickedAway (line 137) | function closeSettingsDropdownIfClickedAway(event: MouseEvent | TouchEve...
  function didEventClickAnyDropdown (line 147) | function didEventClickAnyDropdown(event: MouseEvent | TouchEvent): boole...
  function updateBackgroundColor (line 157) | function updateBackgroundColor(): void {

FILE: src/client/scripts/esm/components/header/spacing.ts
  function updateSpacing (line 27) | function updateSpacing(): void {
  function updatePadding (line 48) | function updatePadding(): void {
  function updateMode (line 69) | function updateMode(): void {
  function getSpaceBetweenHeaderFlexElements (line 89) | function getSpaceBetweenHeaderFlexElements(): number {

FILE: src/client/scripts/esm/game/GameBus.ts
  type GameBusEvents (line 8) | interface GameBusEvents {

FILE: src/client/scripts/esm/game/boardeditor/actions/eactions.ts
  constant PIECE_LIMIT_KEEP_TRACK_OF_GLOBAL_SPECIAL_RIGHTS (line 70) | const PIECE_LIMIT_KEEP_TRACK_OF_GLOBAL_SPECIAL_RIGHTS = 2_000_000;
  function reset (line 75) | async function reset(): Promise<void> {
  function clearAll (line 87) | async function clearAll(): Promise<void> {
  function load (line 118) | async function load(editorSaveState: EditorSaveState, storage_type: Stor...
  function copy (line 149) | function copy(): void {
  function paste (line 171) | async function paste(): Promise<undefined> {
  function startLocalGame (line 201) | function startLocalGame(): void {
  function startEngineGame (line 222) | function startEngineGame(engineUIConfig: EngineUIConfig): void {
  function isPositionIllegal (line 309) | function isPositionIllegal(variantOptions: VariantOptions): boolean {
  function queueRemovalOfAllPieces (line 325) | function queueRemovalOfAllPieces(gamefile: FullGame, edit: Edit, pieces:...
  function getCurrentPositionInformation (line 336) | function getCurrentPositionInformation(revokeRedundantRights: boolean): ...
  function revokeRedundantSpecialRights (line 379) | function revokeRedundantSpecialRights(boardsim: Board, specialRights: Se...
  function loadFromLongformat (line 398) | async function loadFromLongformat(longformOut: LongFormatIn): Promise<vo...

FILE: src/client/scripts/esm/game/boardeditor/actions/eautosave.ts
  constant EDITOR_AUTOSAVE_NAME (line 20) | const EDITOR_AUTOSAVE_NAME = 'editor-autosave';
  function markPositionDirty (line 44) | function markPositionDirty(): void {
  function autosaveCurrentPositionOnce (line 49) | async function autosaveCurrentPositionOnce(): Promise<void> {
  function startPositionAutosave (line 95) | function startPositionAutosave(): void {
  function stopPositionAutosave (line 111) | function stopPositionAutosave(): void {
  function clearAutosave (line 118) | function clearAutosave(): void {
  function loadAutosave (line 131) | async function loadAutosave(): Promise<EditorAutosaveState | undefined> {

FILE: src/client/scripts/esm/game/boardeditor/actions/ecloud.ts
  function parseCloudPosition (line 34) | async function parseCloudPosition(
  function saveCloudState (line 80) | async function saveCloudState(
  function saveCloud (line 143) | async function saveCloud(position_name: string): Promise<void> {
  function readCloud (line 175) | async function readCloud(position_name: string): Promise<EditorSaveState...
  function deleteCloud (line 192) | async function deleteCloud(position_name: string): Promise<CloudSaveList...
  function transferPositionToCloud (line 207) | async function transferPositionToCloud(
  function removePositionFromCloud (line 233) | async function removePositionFromCloud(
  function getAllCloudSaveInfos (line 266) | async function getAllCloudSaveInfos(): Promise<CloudSaveListRecord[]> {

FILE: src/client/scripts/esm/game/boardeditor/actions/editorSavesAPI.ts
  type CloudSaveListRecord (line 14) | interface CloudSaveListRecord {
  type CloudPositionRecord (line 21) | interface CloudPositionRecord {
  function buildAuthHeaders (line 35) | async function buildAuthHeaders(): Promise<Record<string, string>> {
  function getSavedPositions (line 52) | async function getSavedPositions(): Promise<CloudSaveListRecord[]> {
  function savePosition (line 71) | async function savePosition(
  function getPosition (line 107) | async function getPosition(position_name: string): Promise<CloudPosition...
  function deletePosition (line 126) | async function deletePosition(position_name: string): Promise<CloudSaveL...

FILE: src/client/scripts/esm/game/boardeditor/actions/esave.ts
  constant EDITOR_SAVE_PREFIX (line 20) | const EDITOR_SAVE_PREFIX = 'editor-save-' as const;
  constant EDITOR_SAVEINFO_PREFIX (line 23) | const EDITOR_SAVEINFO_PREFIX = 'editor-saveinfo-' as const;
  function saveKey (line 35) | function saveKey(position_name: string): string {
  function saveinfoKey (line 40) | function saveinfoKey(position_name: string): string {
  function saveLocal (line 47) | async function saveLocal(position_name: string): Promise<void> {
  function saveState (line 96) | async function saveState(editorSaveState: EditorSaveState): Promise<void> {
  function deleteLocal (line 111) | async function deleteLocal(position_name: string): Promise<void> {
  function localSaveExists (line 119) | async function localSaveExists(position_name: string): Promise<boolean> {
  function getAllLocalSaveInfos (line 128) | async function getAllLocalSaveInfos(): Promise<EditorAbridgedSaveState[]> {
  function readLocal (line 154) | async function readLocal(position_name: string): Promise<EditorSaveState...

FILE: src/client/scripts/esm/game/boardeditor/boardeditor.ts
  type ActivePosition (line 32) | type ActivePosition =
  type StorageType (line 37) | type StorageType = (typeof editortypes)['STORAGE_TYPES'][number];
  function initBoardEditor (line 57) | async function initBoardEditor(
  function closeBoardEditor (line 117) | function closeBoardEditor(): void {
  function update (line 137) | function update(): void {
  function render (line 150) | function render(): void {
  function areInBoardEditor (line 160) | function areInBoardEditor(): boolean {
  function isPositionDirty (line 165) | function isPositionDirty(): boolean {
  function markPositionDirty (line 173) | function markPositionDirty(): void {
  function markPositionClean (line 181) | function markPositionClean(): void {
  function getActivePosition (line 188) | function getActivePosition(): ActivePosition | undefined {
  function isActivePosition (line 193) | function isActivePosition(name: string, storage_type: StorageType): bool...
  function setActivePosition (line 202) | function setActivePosition(new_position: ActivePosition): void {
  function clearActivePosition (line 209) | function clearActivePosition(): void {
  function flushActivePositionToAutosave (line 220) | function flushActivePositionToAutosave(): void {

FILE: src/client/scripts/esm/game/boardeditor/eclipboard.ts
  function addEventListeners (line 21) | function addEventListeners(): void {
  function removeEventListeners (line 30) | function removeEventListeners(): void {
  function onCopy (line 41) | function onCopy(): void {
  function onCut (line 57) | function onCut(): void {
  function onPaste (line 72) | function onPaste(): void {
  function onCopyGame (line 89) | function onCopyGame(): void {
  function onPasteGame (line 94) | function onPasteGame(): void {

FILE: src/client/scripts/esm/game/boardeditor/edithistory.ts
  type EditWithRules (line 39) | interface EditWithRules extends Edit {
  constant EDIT_HISTORY_MAX_CHANGES (line 56) | const EDIT_HISTORY_MAX_CHANGES = 8_000_000;
  function init (line 72) | function init(pawnDoublePush: boolean | undefined, castling: boolean | u...
  function reset (line 81) | function reset(): void {
  function runEdit (line 89) | function runEdit(gamefile: FullGame, mesh: Mesh, edit: Edit, forward: bo...
  function addEditToHistory (line 122) | function addEditToHistory(edit: Edit): void {
  function undo (line 144) | function undo(): void {
  function redo (line 176) | function redo(): void {
  function canUndo (line 199) | function canUndo(): boolean {
  function canRedo (line 205) | function canRedo(): boolean {
  function queueRemovePiece (line 213) | function queueRemovePiece(gamefile: FullGame, edit: Edit, piece: Piece):...
  function queueAddPiece (line 222) | function queueAddPiece(
  function queueSpecialRights (line 235) | function queueSpecialRights(gamefile: FullGame, edit: Edit, coords: Coor...

FILE: src/client/scripts/esm/game/boardeditor/editortypes.ts
  constant STORAGE_TYPES (line 19) | const STORAGE_TYPES = ['local', 'cloud'] as const;
  type EditorAbridgedSaveState (line 24) | interface EditorAbridgedSaveState {
  type EditorPositionData (line 31) | interface EditorPositionData {
  type EditorSaveState (line 40) | interface EditorSaveState extends EditorPositionData {
  type EditorAutosaveState (line 48) | interface EditorAutosaveState extends EditorPositionData {

FILE: src/client/scripts/esm/game/boardeditor/egamerules.ts
  type GameRulesGUIinfo (line 30) | interface GameRulesGUIinfo {
  function getPlayerToMove (line 70) | function getPlayerToMove(): 'white' | 'black' {
  function getCurrentGamerulesAndState (line 74) | function getCurrentGamerulesAndState(): {
  function setGamerulesGUIinfo (line 138) | function setGamerulesGUIinfo(
  function setPositionDependentGameRules (line 222) | function setPositionDependentGameRules(
  function getPositionDependentGameRules (line 231) | function getPositionDependentGameRules(): {
  function updateGamerulesGUIinfo (line 242) | function updateGamerulesGUIinfo(new_gamerulesGUIinfo: GameRulesGUIinfo):...
  function updateGamerulesUponQueueToggleSpecialRight (line 252) | function updateGamerulesUponQueueToggleSpecialRight(type: number, future...
  function queueToggleGlobalPawnDoublePush (line 274) | function queueToggleGlobalPawnDoublePush(pawnDoublePush: boolean, edit: ...
  function queueToggleGlobalCastlingWithRooks (line 286) | function queueToggleGlobalCastlingWithRooks(castling: boolean, edit: Edi...
  function updateGamefileProperties (line 308) | function updateGamefileProperties(

FILE: src/client/scripts/esm/game/boardeditor/tools/drawingtool.ts
  function init (line 61) | function init(): void {
  function onCloseEditor (line 66) | function onCloseEditor(): void {
  function resetState (line 71) | function resetState(): void {
  function beginEdit (line 81) | function beginEdit(): void {
  function endEdit (line 88) | function endEdit(): void {
  function cancelEdit (line 95) | function cancelEdit(): void {
  function update (line 106) | function update(currentTool: Tool): void {
  function queueToggleSpecialRight (line 174) | function queueToggleSpecialRight(
  function onToolChange (line 194) | function onToolChange(tool: Tool): void {
  function isEditInProgress (line 204) | function isEditInProgress(): boolean {
  function isToolADrawingTool (line 208) | function isToolADrawingTool(tool: Tool): boolean {
  function stealPointer (line 212) | function stealPointer(pointerIdToSteal: string): void {
  function setPiece (line 218) | function setPiece(pieceType: number): void {
  function getPiece (line 222) | function getPiece(): number {
  function setColor (line 226) | function setColor(color: Player): void {
  function getColor (line 230) | function getColor(): Player {

FILE: src/client/scripts/esm/game/boardeditor/tools/etoolmanager.ts
  type Tool (line 21) | type Tool = (typeof validTools)[number];
  function reset (line 36) | function reset(): void {
  function getTool (line 44) | function getTool(): Tool {
  function setTool (line 49) | function setTool(tool: string): void {
  function isLeftMouseReserved (line 66) | function isLeftMouseReserved(): boolean {
  function stealPointer (line 72) | function stealPointer(pointerIdToSteal: string): void {
  function testShortcuts (line 83) | function testShortcuts(): void {

FILE: src/client/scripts/esm/game/boardeditor/tools/normaltool.ts
  function makeMoveEdit (line 29) | function makeMoveEdit(gamefile: FullGame, mesh: Mesh | undefined, moveCo...
  function generateMoveEdit (line 47) | function generateMoveEdit(boardsim: Board, moveCoords: MoveCoords): Edit {

FILE: src/client/scripts/esm/game/boardeditor/tools/selection/scursor.ts
  type Cursor (line 14) | type Cursor = 'grab' | 'grabbing' | 'crosshair';
  function addCursor (line 29) | function addCursor(cursor: Cursor): void {
  function removeCursor (line 35) | function removeCursor(cursor: Cursor): void {
  function updateCursor (line 41) | function updateCursor(): void {

FILE: src/client/scripts/esm/game/boardeditor/tools/selection/sdrag.ts
  constant GRABBABLE_DIST (line 31) | const GRABBABLE_DIST = {
  function getGrabbableDist (line 53) | function getGrabbableDist(): number {
  function update (line 61) | function update(): void {
  function isMouseHoveringOverSelectionEdge (line 100) | function isMouseHoveringOverSelectionEdge(): boolean {
  function resetState (line 127) | function resetState(): void {
  function pickUpSelection (line 138) | function pickUpSelection(): void {
  function dropSelection (line 156) | function dropSelection(): void {
  function render (line 192) | function render(): void {

FILE: src/client/scripts/esm/game/boardeditor/tools/selection/selectiontool.ts
  function update (line 52) | function update(): void {
  function testShortcuts (line 82) | function testShortcuts(): void {
  function beginSelection (line 92) | function beginSelection(): void {
  function endSelection (line 106) | function endSelection(): void {
  function resetState (line 121) | function resetState(): void {
  function isACurrentSelection (line 133) | function isACurrentSelection(): boolean {
  function isExistingSelection (line 141) | function isExistingSelection(): boolean {
  function render (line 145) | function render(): void {
  function getSelectionIntBox (line 169) | function getSelectionIntBox(): BoundingBox | undefined {
  function getSelectionWorldBox (line 182) | function getSelectionWorldBox(): DoubleBoundingBox | undefined {
  function convertIntBoxToWorldBox (line 193) | function convertIntBoxToWorldBox(intBox: BoundingBox): DoubleBoundingBox {
  function getSelectionCorners (line 205) | function getSelectionCorners(): [Coords, Coords] {
  function setSelection (line 217) | function setSelection(corner1: Coords, corner2: Coords): void {
  function selectAll (line 225) | function selectAll(): void {

FILE: src/client/scripts/esm/game/boardeditor/tools/selection/sfill.ts
  function areWeFilling (line 45) | function areWeFilling(): boolean {
  function update (line 53) | function update(): void {
  function isMouseHoveringOverFillHandle (line 92) | function isMouseHoveringOverFillHandle(): boolean {
  function resetState (line 114) | function resetState(): void {
  function startFill (line 123) | function startFill(): void {
  function executeFill (line 129) | function executeFill(): void {
  function calculateFillBox (line 146) | function calculateFillBox(): BoundingBox | undefined {
  function render (line 206) | function render(): void {

FILE: src/client/scripts/esm/game/boardeditor/tools/selection/stoolgraphics.ts
  constant OUTLINE_COLOR (line 29) | const OUTLINE_COLOR: Color = [0, 0, 0, 1];
  constant FILL_COLOR (line 31) | const FILL_COLOR: Color = [0, 0, 0, 0.08];
  constant CORNER_DOT_WIDTH (line 34) | const CORNER_DOT_WIDTH = 6;
  constant DASHED_WIDTH (line 37) | const DASHED_WIDTH = 1;
  constant DASHED_LENGTH (line 39) | const DASHED_LENGTH = 6;
  function outlineRankAndFile (line 47) | function outlineRankAndFile(): void {
  function renderSelectionBoxWireframe (line 82) | function renderSelectionBoxWireframe(worldBox: DoubleBoundingBox): void {
  function renderSelectionBoxWireframeDashed (line 102) | function renderSelectionBoxWireframeDashed(worldBox: DoubleBoundingBox):...
  function renderSelectionBoxFill (line 129) | function renderSelectionBoxFill(worldBox: DoubleBoundingBox): void {
  function renderCornerSquare (line 149) | function renderCornerSquare(worldBox: DoubleBoundingBox): void {

FILE: src/client/scripts/esm/game/boardeditor/tools/selection/stransformations.ts
  type StatePiece (line 32) | interface StatePiece extends Piece {
  constant NEGONE (line 38) | const NEGONE = bd.fromBigInt(-1n, 1);
  constant HALF (line 39) | const HALF = bd.fromNumber(0.5, 1);
  constant ONE (line 40) | const ONE = bd.fromBigInt(1n, 1);
  constant TWO (line 41) | const TWO = bd.fromBigInt(2n, 1);
  function Translate (line 60) | function Translate(
  function Fill (line 82) | function Fill(
  function Delete (line 179) | function Delete(gamefile: FullGame, mesh: Mesh, box: BoundingBox): void {
  function Copy (line 187) | function Copy(gamefile: FullGame, box: BoundingBox): void {
  function Paste (line 208) | function Paste(gamefile: FullGame, mesh: Mesh, targetBox: BoundingBox): ...
  function FlipHorizontal (line 279) | function FlipHorizontal(gamefile: FullGame, mesh: Mesh, box: BoundingBox...
  function FlipVertical (line 284) | function FlipVertical(gamefile: FullGame, mesh: Mesh, box: BoundingBox):...
  function Reflect (line 292) | function Reflect(gamefile: FullGame, mesh: Mesh, box: BoundingBox, axis:...
  function RotateLeft (line 331) | function RotateLeft(gamefile: FullGame, mesh: Mesh, box: BoundingBox): v...
  function RotateRight (line 336) | function RotateRight(gamefile: FullGame, mesh: Mesh, box: BoundingBox): ...
  function Rotate (line 341) | function Rotate(gamefile: FullGame, mesh: Mesh, box: BoundingBox, clockw...
  function rotatePoint (line 401) | function rotatePoint(point: Coords, pivot: BDCoords, clockwise: Boolean)...
  function InvertColor (line 427) | function InvertColor(gamefile: FullGame, mesh: Mesh, box: BoundingBox): ...
  function Transform (line 451) | function Transform(
  function removeAllPieces (line 514) | function removeAllPieces(gamefile: FullGame, edit: Edit, pieces: Piece[]...
  function applyEdit (line 521) | function applyEdit(gamefile: FullGame, mesh: Mesh, edit: Edit): void {
  function getPiecesInBox (line 530) | function getPiecesInBox(gamefile: FullGame, intBox: BoundingBox): Piece[] {
  function resetState (line 596) | function resetState(): void {

FILE: src/client/scripts/esm/game/chess/checkmatepractice.ts
  function setUndoingIsLegal (line 80) | function setUndoingIsLegal(value: boolean): void {
  function areInCheckmatePractice (line 85) | function areInCheckmatePractice(): boolean {
  function startCheckmatePractice (line 92) | function startCheckmatePractice(checkmateSelectedID: string): void {
  function onGameUnload (line 126) | function onGameUnload(): void {
  function initListeners (line 132) | function initListeners(): void {
  function closeListeners (line 137) | function closeListeners(): void {
  function generateCheckmateStartingPosition (line 147) | function generateCheckmateStartingPosition(checkmateID: string): Map<Coo...
  function squareNotInSight (line 222) | function squareNotInSight(square: CoordsKey, position: Map<CoordsKey, nu...
  function eraseCheckmatePracticeProgressFromLocalStorage (line 244) | function eraseCheckmatePracticeProgressFromLocalStorage(): void {
  function updateCompletedCheckmates (line 255) | function updateCompletedCheckmates(): void {
  function markCheckmateBeaten (line 272) | async function markCheckmateBeaten(checkmatePracticeID: string): Promise...
  function onEngineGameConclude (line 350) | function onEngineGameConclude(): void {
  function registerHumanMove (line 374) | function registerHumanMove(): void {
  function registerEngineMove (line 390) | function registerEngineMove(): void {
  function undoMove (line 400) | function undoMove(): void {
  function restartGame (line 424) | function restartGame(): void {

FILE: src/client/scripts/esm/game/chess/clientmetadatautil.ts
  constant YOU_NAME_ICN_METADATA (line 23) | const YOU_NAME_ICN_METADATA = '(You)' as const;
  function resolveTimestampFromMetadata (line 32) | function resolveTimestampFromMetadata(UTCDate?: string, UTCTime?: string...
  function buildBaseGameMetadata (line 46) | function buildBaseGameMetadata(
  function copyMetadataField (line 66) | function copyMetadataField<K extends MetadataKey>(
  function getGameConclusionFromResultAndTermination (line 76) | function getGameConclusionFromResultAndTermination(
  function getRatingFromWhiteBlackElo (line 106) | function getRatingFromWhiteBlackElo(whiteBlackElo: string): Rating {

FILE: src/client/scripts/esm/game/chess/copygame.ts
  function copyGame (line 31) | function copyGame(copySinglePosition: boolean): void {

FILE: src/client/scripts/esm/game/chess/engines/engine.ts
  type Engine (line 13) | interface Engine {
  type ValidEngine (line 32) | type ValidEngine = keyof typeof engineDictionary;
  function getFormattedEngineName (line 62) | function getFormattedEngineName(engineName: ValidEngine, strengthLevel?:...

FILE: src/client/scripts/esm/game/chess/engines/engineCheckmatePractice.ts
  function invertPieceNameDictionary (line 132) | function invertPieceNameDictionary(json: { [key: string]: number }): { [...
  function initEvalWeightsAndSearchProperties (line 214) | function initEvalWeightsAndSearchProperties(): void {
  function diagonalNorm (line 558) | function diagonalNorm(square: DoubleCoords): number {
  function diagonalNormSquared (line 563) | function diagonalNormSquared(square: DoubleCoords): number {
  function manhattanNorm (line 568) | function manhattanNorm(square: DoubleCoords): number {
  function manhattanDistance (line 573) | function manhattanDistance(square1: DoubleCoords, square2: DoubleCoords)...
  function specialNorm (line 578) | function specialNorm(square: DoubleCoords): number {
  function pawnNorm (line 583) | function pawnNorm(square: DoubleCoords): number {
  function vincinityNorm (line 589) | function vincinityNorm(square: DoubleCoords): number {
  function get_center_of_mass (line 597) | function get_center_of_mass(
  function is_natural_multiple (line 621) | function is_natural_multiple(v: DoubleCoords, direction: DoubleCoords): ...
  function rider_threatens (line 633) | function rider_threatens(
  function add_move (line 672) | function add_move(square: DoubleCoords, v: DoubleCoords): DoubleCoords {
  function rescaleVector (line 677) | function rescaleVector(scalar: number, v: DoubleCoords): DoubleCoords {
  function crossProduct (line 682) | function crossProduct(v1: DoubleCoords, v2: DoubleCoords): number {
  function squares_are_equal (line 687) | function squares_are_equal(square_1: DoubleCoords, square_2: DoubleCoord...
  function tuplelist_contains_tuple (line 692) | function tuplelist_contains_tuple(tuplelist: DoubleCoords[], tuple: Doub...
  function square_is_occupied (line 697) | function square_is_occupied(
  function piece_threatens_square (line 708) | function piece_threatens_square(
  function square_is_threatened (line 768) | function square_is_threatened(
  function get_black_legal_moves (line 782) | function get_black_legal_moves(
  function get_black_legal_move_amount (line 798) | function get_black_legal_move_amount(
  function is_check (line 807) | function is_check(piecelist: number[], coordlist: DoubleCoords[]): boole...
  function isBlackInTrap (line 827) | function isBlackInTrap(piecelist: number[], coordlist: DoubleCoords[]): ...
  function isBlackNearProtectedRider (line 841) | function isBlackNearProtectedRider(piecelist: number[], coordlist: Doubl...
  function get_white_piece_candidate_squares (line 863) | function get_white_piece_candidate_squares(
  function add_suitable_squares_to_candidate_list (line 982) | function add_suitable_squares_to_candidate_list(
  function get_white_candidate_moves (line 1094) | function get_white_candidate_moves(
  function make_white_move (line 1114) | function make_white_move(
  function make_black_move (line 1134) | function make_black_move(
  function get_position_evaluation (line 1166) | function get_position_evaluation(
  function alphabeta (line 1235) | function alphabeta(
  function runIterativeDeepening (line 1592) | function runIterativeDeepening(
  function cyrb128 (line 1720) | function cyrb128(str: string): number[] {
  function mulberry32 (line 1744) | function mulberry32(a: number): () => number {
  function move_to_gamefile_move (line 1756) | function move_to_gamefile_move(target_square: DoubleCoords): string {
  function doesTypeExist (line 1770) | function doesTypeExist(boardsim: Board, type: number): boolean {
  function getFirstOfType (line 1778) | function getFirstOfType(boardsim: Board, type: number): DoubleCoords | u...
  function convertBigIntCoordsToFloating (line 1805) | function convertBigIntCoordsToFloating(coords: Coords): DoubleCoords {
  function runEngine (line 1812) | async function runEngine(): Promise<void> {

FILE: src/client/scripts/esm/game/chess/engines/enginecards/hydrochess_card.ts
  type SupportedResult (line 14) | type SupportedResult = { supported: true } | { supported: false; reason:...
  constant I64_MAX (line 19) | const I64_MAX = 2n ** 63n - 1n;
  constant BORDER_CAP (line 22) | const BORDER_CAP = I64_MAX - 1000n;
  constant SUPPORTED_VARIANTS (line 24) | const SUPPORTED_VARIANTS = new Set([
  function isPositionSupported (line 52) | function isPositionSupported(variantOptions: VariantOptions): SupportedR...

FILE: src/client/scripts/esm/game/chess/engines/hydrochess.ts
  type EngineConfig (line 23) | interface EngineConfig {
  type EngineWorkerMessage (line 28) | interface EngineWorkerMessage {
  type WasmBestMoveResult (line 40) | interface WasmBestMoveResult {
  function initWasm (line 48) | async function initWasm(): Promise<boolean> {
  function mapRustPromotionToSiteAbbr (line 152) | function mapRustPromotionToSiteAbbr(

FILE: src/client/scripts/esm/game/chess/game.ts
  function init (line 94) | function init(): void {
  function update (line 120) | function update(): void {
  function testIfEmptyBoardRegionClicked (line 222) | function testIfEmptyBoardRegionClicked(gamefile: FullGame, mesh: Mesh | ...
  function render (line 237) | function render(): void {
  function renderScene (line 262) | function renderScene(): void {
  function renderTilesAndPromoteLines (line 332) | function renderTilesAndPromoteLines(): void {
  function renderOutlineofScreenBox (line 341) | function renderOutlineofScreenBox(): void {
  function getFurthestTileVisible (line 352) | function getFurthestTileVisible(): bigint {
  function getOverlay (line 363) | function getOverlay(): HTMLElement {

FILE: src/client/scripts/esm/game/chess/gamecompressor.ts
  type SimplifiedGameState (line 31) | interface SimplifiedGameState {
  function compressGamefile (line 53) | function compressGamefile(
  function convertMovesToICNConverterInMove (line 96) | function convertMovesToICNConverterInMove(moves: MoveFull[]): MovePrepri...
  function GameToPosition (line 123) | function GameToPosition(

FILE: src/client/scripts/esm/game/chess/gameformulator.ts
  function formulateGame (line 25) | function formulateGame(longformIn: LongFormatIn, validateMoves?: true): ...

FILE: src/client/scripts/esm/game/chess/gameloader.ts
  function areInAGame (line 74) | function areInAGame(): boolean {
  function getTypeOfGameWeIn (line 79) | function getTypeOfGameWeIn(): typeof typeOfGameWeAreIn {
  function areInLocalGame (line 83) | function areInLocalGame(): boolean {
  function isItOurTurn (line 87) | function isItOurTurn(): boolean {
  function getOurColor (line 101) | function getOurColor(): Player | undefined {
  function areWeLoadingGame (line 115) | function areWeLoadingGame(): boolean {
  function update (line 122) | function update(): void {
  function startLocalGame (line 129) | async function startLocalGame(options: {
  function startOnlineGame (line 166) | async function startOnlineGame(options: {
  function startEngineGame (line 232) | async function startEngineGame(options: {
  function startBoardEditor (line 311) | async function startBoardEditor(): Promise<void> {
  function startCustomLocalGame (line 350) | async function startCustomLocalGame(options: {
  function startCustomEngineGame (line 389) | async function startCustomEngineGame(options: {
  function startBoardEditorFromCustomPosition (line 457) | async function startBoardEditorFromCustomPosition(
  function pasteGame (line 512) | async function pasteGame(options: {
  function onFinishedLoading (line 554) | function onFinishedLoading(): void {
  function onCatchLoadingError (line 570) | function onCatchLoadingError(err: Error): void {
  function openGameinfoBarAndConcludeGameIfOver (line 580) | function openGameinfoBarAndConcludeGameIfOver(
  function unloadLogicalAndRendering (line 588) | function unloadLogicalAndRendering(): void {
  function unloadGame (line 595) | function unloadGame(): void {

FILE: src/client/scripts/esm/game/chess/gameslot.ts
  type LoadOptions (line 58) | interface LoadOptions {
  function getGamefile (line 99) | function getGamefile(): FullGame | undefined {
  function getMesh (line 104) | function getMesh(): Mesh | undefined {
  function areInGame (line 108) | function areInGame(): boolean {
  function isLoadedGameViewingWhitePerspective (line 112) | function isLoadedGameViewingWhitePerspective(): boolean {
  function loadGamefile (line 126) | function loadGamefile(loadOptions: LoadOptions): Promise<void> {
  function loadLogical (line 155) | function loadLogical(loadOptions: LoadOptions): void {
  function loadGraphical (line 187) | async function loadGraphical(loadOptions: LoadOptions): Promise<void> {
  function unloadGame (line 227) | function unloadGame(): void {
  function startStartingTransition (line 248) | function startStartingTransition(): void {
  function initCopyPastGameListeners (line 262) | function initCopyPastGameListeners(): void {
  function removeCopyPasteGameListeners (line 270) | function removeCopyPasteGameListeners(): void {
  function callbackCopy (line 277) | function callbackCopy(_event: Event): void {
  function concludeGame (line 287) | function concludeGame(): void {
  function unConcludeGame (line 318) | function unConcludeGame(): void {

FILE: src/client/scripts/esm/game/chess/graphicalchanges.ts
  function addMeshPiece (line 43) | function addMeshPiece(mesh: Mesh, change: Change): void {
  function deleteMeshPiece (line 47) | function deleteMeshPiece(mesh: Mesh, change: Change): void {
  function moveMeshPiece (line 51) | function moveMeshPiece(mesh: Mesh, change: Change): void {
  function returnMeshPiece (line 61) | function returnMeshPiece(mesh: Mesh, change: Change): void {
  function animateMove (line 76) | function animateMove(

FILE: src/client/scripts/esm/game/chess/movesequence.ts
  function makeMove (line 41) | function makeMove(
  function makeMoveAndAnimate (line 77) | function makeMoveAndAnimate(
  function runMeshChanges (line 99) | function runMeshChanges(
  function rewindMove (line 113) | function rewindMove(gamefile: FullGame, mesh: Mesh | undefined): void {
  function viewMove (line 139) | function viewMove(
  function viewIndex (line 157) | function viewIndex(gamefile: FullGame, mesh: Mesh | undefined, index: nu...
  function viewFront (line 167) | function viewFront(gamefile: FullGame, mesh: Mesh | undefined): void {
  function navigateMove (line 181) | function navigateMove(gamefile: FullGame, mesh: Mesh | undefined, forwar...
  function updateGui (line 204) | function updateGui(showMoveCounter: boolean): void {

FILE: src/client/scripts/esm/game/chess/pastegame.ts
  function callbackPaste (line 61) | async function callbackPaste(_event: Event): Promise<void> {
  function pasteGame (line 123) | function pasteGame(longformOut: LongFormatOut): void {
  function getPositionAndSpecialRightsFromLongFormat (line 243) | function getPositionAndSpecialRightsFromLongFormat(

FILE: src/client/scripts/esm/game/chess/premoves.ts
  type Premove (line 39) | interface Premove extends Edit, MoveTagged {
  function hasAtleastOnePremove (line 103) | function hasAtleastOnePremove(): boolean {
  function arePremovesApplied (line 108) | function arePremovesApplied(): boolean {
  function addPremove (line 113) | function addPremove(gamefile: FullGame, mesh: Mesh | undefined, moveTagg...
  function applyPremove (line 131) | function applyPremove(
  function generatePremove (line 143) | function generatePremove(gamefile: FullGame, moveTagged: MoveTagged): Pr...
  function clearPremoves (line 179) | function clearPremoves(): void {
  function cancelPremoves (line 188) | function cancelPremoves(gamefile: FullGame, mesh?: Mesh): void {
  function rewindPremoves (line 208) | function rewindPremoves(gamefile: FullGame, mesh?: Mesh): void {
  function applyPremoves (line 229) | function applyPremoves(gamefile: FullGame, mesh?: Mesh): void {
  function processPremoves (line 281) | function processPremoves(gamefile: FullGame, mesh?: Mesh): void {
  function premoveIsLegal (line 336) | function premoveIsLegal(
  function onYourMove (line 380) | function onYourMove(gamefile: FullGame, mesh?: Mesh): void {
  function performWithUnapplied (line 392) | function performWithUnapplied(
  function update (line 414) | function update(gamefile: FullGame, mesh?: Mesh): void {
  function render (line 430) | function render(): void {

FILE: src/client/scripts/esm/game/chess/selection.ts
  function getPieceSelected (line 95) | function getPieceSelected(): Piece | undefined {
  function isAPieceSelected (line 100) | function isAPieceSelected(): boolean {
  function isOpponentPieceSelected (line 105) | function isOpponentPieceSelected(): boolean {
  function arePremoving (line 110) | function arePremoving(): boolean {
  function getLegalMovesOfSelectedPiece (line 115) | function getLegalMovesOfSelectedPiece(): LegalMoves | undefined {
  function getSquarePawnIsCurrentlyPromotingOn (line 120) | function getSquarePawnIsCurrentlyPromotingOn(): CoordsTagged | undefined {
  function promoteToType (line 128) | function promoteToType(type: number): void {
  function getEditMode (line 132) | function getEditMode(): boolean {
  function toggleEditMode (line 138) | function toggleEditMode(): void {
  function disableEditMode (line 152) | function disableEditMode(): void {
  function enableEditMode (line 156) | function enableEditMode(): void {
  function update (line 163) | function update(): void {
  function updateHoverSquareLegal (line 226) | function updateHoverSquareLegal(gamefile: FullGame): void {
  function testIfPieceSelected (line 261) | function testIfPieceSelected(gamefile: FullGame, mesh: Mesh | undefined)...
  function testIfPieceDropped (line 313) | function testIfPieceDropped(gamefile: FullGame, mesh: Mesh | undefined):...
  function testIfPieceMoved (line 332) | function testIfPieceMoved(gamefile: FullGame, mesh: Mesh | undefined): v...
  function viewFrontIfNotViewingLatestMove (line 348) | function viewFrontIfNotViewingLatestMove(gamefile: FullGame, mesh: Mesh ...
  function canSelectPieceType (line 369) | function canSelectPieceType(basegame: Game, type: number | undefined): 0...
  function canMovePieceType (line 387) | function canMovePieceType(pieceType: number): boolean {
  function isOpponentType (line 398) | function isOpponentType(basegame: Game, type: number): boolean {
  function selectPiece (line 413) | function selectPiece(
  function reselectPiece (line 443) | function reselectPiece(): void {
  function unselectPiece (line 495) | function unselectPiece(): void {
  function initSelectedPieceInfo (line 511) | function initSelectedPieceInfo(gamefile: FullGame, mesh: Mesh | undefine...
  function moveGamefilePiece (line 543) | function moveGamefilePiece(gamefile: FullGame, mesh: Mesh | undefined, c...
  function onPromoteTrigger (line 577) | function onPromoteTrigger(coords: CoordsTagged): void {
  function makePromotionMove (line 587) | function makePromotionMove(gamefile: FullGame, mesh: Mesh | undefined): ...
  function stealPointer (line 596) | function stealPointer(pointerIdToSteal: string): void {
  function renderGhostPiece (line 608) | function renderGhostPiece(): void {

FILE: src/client/scripts/esm/game/config.ts
  constant VIDEO_MODE (line 8) | const VIDEO_MODE: boolean = false;
  constant DEV_BUILD (line 15) | const DEV_BUILD: boolean = docutil.isLocalEnvironment();

FILE: src/client/scripts/esm/game/gui/boardeditor/actions/guiclearposition.ts
  function onOpen (line 37) | function onOpen(): void {
  function onClose (line 42) | function onClose(resetPositioning: boolean): void {
  function initClearPositionUIListeners (line 50) | function initClearPositionUIListeners(): void {
  function closeClearPositionUIListeners (line 56) | function closeClearPositionUIListeners(): void {
  function onKeyDown (line 64) | function onKeyDown(e: KeyboardEvent): void {
  function onYesButtonPress (line 74) | function onYesButtonPress(): void {
  function onNoButtonPress (line 79) | function onNoButtonPress(): void {

FILE: src/client/scripts/esm/game/gui/boardeditor/actions/guigamerules.ts
  function onOpen (line 112) | function onOpen(): void {
  function onClose (line 117) | function onClose(resetPositioning: boolean): void {
  function initGameRulesListeners (line 125) | function initGameRulesListeners(): void {
  function closeGameRulesListeners (line 131) | function closeGameRulesListeners(): void {
  function readGameRules (line 140) | function readGameRules(): void {
  function setGameRules (line 410) | function setGameRules(gamerulesGUIinfo: GameRulesGUIinfo): void {

FILE: src/client/scripts/esm/game/gui/boardeditor/actions/guiresetposition.ts
  function onOpen (line 37) | function onOpen(): void {
  function onClose (line 42) | function onClose(resetPositioning: boolean): void {
  function initResetPositionUIListeners (line 50) | function initResetPositionUIListeners(): void {
  function closeResetPositionUIListeners (line 56) | function closeResetPositionUIListeners(): void {
  function onKeyDown (line 64) | function onKeyDown(e: KeyboardEvent): void {
  function onYesButtonPress (line 74) | function onYesButtonPress(): void {
  function onNoButtonPress (line 79) | function onNoButtonPress(): void {

FILE: src/client/scripts/esm/game/gui/boardeditor/actions/guistartenginegame.ts
  type EngineUIConfig (line 21) | interface EngineUIConfig {
  function onOpen (line 83) | function onOpen(): void {
  function onClose (line 89) | function onClose(resetPositioning = false): void {
  function initEngineGameUIListeners (line 97) | function initEngineGameUIListeners(): void {
  function closeEngineGameUIListeners (line 106) | function closeEngineGameUIListeners(): void {
  function onKeyDown (line 117) | function onKeyDown(e: KeyboardEvent): void {
  function onYesButtonPress (line 128) | function onYesButtonPress(): void {
  function onNoButtonPress (line 133) | function onNoButtonPress(): void {
  function updateEngineUIcontents (line 138) | function updateEngineUIcontents(): void {
  function readEngineUIConfig (line 145) | function readEngineUIConfig(): EngineUIConfig {

FILE: src/client/scripts/esm/game/gui/boardeditor/actions/guistartlocalgame.ts
  function onOpen (line 37) | function onOpen(): void {
  function onClose (line 42) | function onClose(resetPositioning: boolean): void {
  function initLocalGameUIListeners (line 50) | function initLocalGameUIListeners(): void {
  function closeLocalGameUIListeners (line 56) | function closeLocalGameUIListeners(): void {
  function onKeyDown (line 64) | function onKeyDown(e: KeyboardEvent): void {
  function onYesButtonPress (line 74) | function onYesButtonPress(): void {
  function onNoButtonPress (line 78) | function onNoButtonPress(): void {

FILE: src/client/scripts/esm/game/gui/boardeditor/actions/loadposition/guiloadposition.ts
  function onOpen (line 55) | function onOpen(): void {
  function openLoadPosition (line 59) | function openLoadPosition(): void {
  function openSavePositionAs (line 68) | function openSavePositionAs(): void {
  function onClose (line 79) | function onClose(resetPositioning = false): void {
  function getMode (line 92) | function getMode(): typeof mode {
  function initSavePositionUIListeners (line 98) | function initSavePositionUIListeners(): void {
  function closeSavePositionUIListeners (line 103) | function closeSavePositionUIListeners(): void {
  function onSaveKeyDown (line 108) | function onSaveKeyDown(e: KeyboardEvent): void {
  function onSaveButtonPress (line 116) | async function onSaveButtonPress(): Promise<void> {

FILE: src/client/scripts/esm/game/gui/boardeditor/actions/loadposition/guiloadpositionmodal.ts
  type ModalMode (line 14) | type ModalMode = 'load' | 'delete' | 'overwrite_save';
  type ModalConfig (line 17) | type ModalConfig = {
  function openModal (line 44) | function openModal(
  function closeModal (line 77) | function closeModal(): void {
  function initModalListeners (line 85) | function initModalListeners(): void {
  function closeModalListeners (line 92) | function closeModalListeners(): void {
  function onModalKeyDown (line 99) | function onModalKeyDown(e: KeyboardEvent): void {
  function onModalYesButtonPress (line 111) | function onModalYesButtonPress(): void {
  function isOpen (line 123) | function isOpen(): boolean {

FILE: src/client/scripts/esm/game/gui/boardeditor/actions/loadposition/guiloadpositionsavelist.ts
  type ButtonHandlerPair (line 26) | type ButtonHandlerPair = {
  type UnifiedSave (line 32) | type UnifiedSave = { storage_type: StorageType } & EditorAbridgedSaveState;
  type PreloadedCloudSaves (line 35) | type PreloadedCloudSaves = CloudSaveListRecord[] | undefined;
  function withRequest (line 78) | async function withRequest<T>(fn: () => Promise<T>): Promise<T> {
  function registerButtonClick (line 92) | function registerButtonClick(button: HTMLButtonElement, handler: (e: Mou...
  function unregisterAllPositionButtonListeners (line 97) | function unregisterAllPositionButtonListeners(): void {
  function clearSavedPositionList (line 105) | function clearSavedPositionList(): void {
  function performLoad (line 112) | async function performLoad(position_name: string, storage_type: StorageT...
  function onCloudButtonPress (line 136) | async function onCloudButtonPress(
  function performDelete (line 156) | async function performDelete(position_name: string, storage_type: Storag...
  function updateSavedPositionListUI (line 175) | async function updateSavedPositionListUI(preloadedCloudSaves?: Preloaded...
  function generateRowForSavedPositionsElement (line 244) | function generateRowForSavedPositionsElement(
  function createButtonElement (line 335) | function createButtonElement(svgHref: string): HTMLButtonElement {

FILE: src/client/scripts/esm/game/gui/boardeditor/guiboardeditor.ts
  function open (line 68) | async function open(): Promise<void> {
  function isOpen (line 101) | function isOpen(): boolean {
  function close (line 105) | function close(): void {
  function initListeners (line 117) | function initListeners(): void {
  function closeListeners (line 126) | function closeListeners(): void {
  function closeAllFloatingWindows (line 136) | function closeAllFloatingWindows(resetPositioning: boolean): void {
  function callback_ToggleMenu (line 147) | function callback_ToggleMenu(): void {
  function setSidebarExpanded (line 155) | function setSidebarExpanded(expanded: boolean): void {
  function callback_Action (line 165) | function callback_Action(e: Event): void {

FILE: src/client/scripts/esm/game/gui/boardeditor/guifloatingwindow.ts
  constant NARROW_THRESHOLD (line 28) | const NARROW_THRESHOLD = 727;
  type FloatingWindowHandle (line 33) | interface FloatingWindowHandle {
  type FloatingWindowOptions (line 42) | interface FloatingWindowOptions {
  function create (line 65) | function create(opts: FloatingWindowOptions): FloatingWindowHandle {

FILE: src/client/scripts/esm/game/gui/boardeditor/guipalette.ts
  function initUI (line 74) | async function initUI(): Promise<void> {
  function markPiece (line 171) | function markPiece(type: number | null): void {
  function updatePieceColors (line 182) | function updatePieceColors(newColor: Player): void {
  function nextColor (line 218) | function nextColor(): void {
  function initListeners (line 225) | function initListeners(): void {
  function closeListeners (line 231) | function closeListeners(): void {
  function _getPlayersInOrder (line 240) | function _getPlayersInOrder(): Player[] {
  function _getActivePieceElements (line 247) | function _getActivePieceElements(): Element[] {
  function callback_ChangePieceType (line 254) | function callback_ChangePieceType(e: Event): void {

FILE: src/client/scripts/esm/game/gui/boardeditor/guipositionheader.ts
  function updateActivePositionElement (line 24) | function updateActivePositionElement(positionname: string | undefined): ...
  function updateDirtyIndicator (line 37) | function updateDirtyIndicator(dirty: boolean): void {
  function onNewSelection (line 43) | function onNewSelection(): void {
  function onClearSelection (line 51) | function onClearSelection(): void {

FILE: src/client/scripts/esm/game/gui/boardeditor/guitoolbar.ts
  function markTool (line 24) | function markTool(tool: Tool): void {
  function initListeners (line 32) | function initListeners(): void {
  function closeListeners (line 38) | function closeListeners(): void {
  function callback_ChangeTool (line 46) | function callback_ChangeTool(e: Event): void {

FILE: src/client/scripts/esm/game/gui/gui.ts
  function prepareForOpen (line 25) | function prepareForOpen(): void {
  function randomizePanVelDir (line 34) | function randomizePanVelDir(): void {
  function displayStatus_FeaturePlanned (line 41) | function displayStatus_FeaturePlanned(): void {

FILE: src/client/scripts/esm/game/gui/guiclock.ts
  function hideClocks (line 98) | function hideClocks(): void {
  function showClocks (line 104) | function showClocks(): void {
  function stopClocks (line 113) | function stopClocks(basegame?: Game): void {
  function cancelSoundEffectTimers (line 122) | function cancelSoundEffectTimers(): void {
  function resetClocks (line 145) | function resetClocks(): void {
  function update (line 150) | function update(basegame: Game): void {
  function edit (line 164) | function edit(basegame: Game): void {
  function rescheduleSoundEffects (line 178) | function rescheduleSoundEffects(clocks: ClockData): void {
  function removeBorder (line 188) | function removeBorder(element: HTMLElement): void {
  function updateBorderColor (line 195) | function updateBorderColor(
  function updateTextContent (line 237) | function updateTextContent(clocks: ClockData): void {
  function scheduleMinuteTick (line 249) | function scheduleMinuteTick(clocks: ClockData): void {
  function playMinuteTick (line 258) | function playMinuteTick(color: Player): void {
  function set (line 263) | function set(basegame: Game): void {
  function scheduleCountdown (line 271) | function scheduleCountdown(clocks: ClockData): void {
  function push (line 277) | function push(clocks: ClockData): void {
  function scheduleDrum (line 288) | function scheduleDrum(clocks: ClockData): void {
  function scheduleTicking (line 307) | function scheduleTicking(clocks: ClockData): void {
  function scheduleTick (line 322) | function scheduleTick(clocks: ClockData): void {
  function playDrumAndQueueNext (line 334) | function playDrumAndQueueNext(clocks: ClockData, secsRemaining: number):...
  function playTickingEffect (line 354) | function playTickingEffect(offset: number): void {
  function playTickEffect (line 359) | function playTickEffect(offset: number): void {

FILE: src/client/scripts/esm/game/gui/guidrawoffer.ts
  function open (line 29) | function open(): void {
  function close (line 38) | function close(): void {
  function initDrawOfferListeners (line 51) | function initDrawOfferListeners(): void {
  function closeDrawOfferListeners (line 56) | function closeDrawOfferListeners(): void {
  function updateVisibilityOfNamesAndClocksWithDrawOffer (line 66) | function updateVisibilityOfNamesAndClocksWithDrawOffer(): void {
  function isDrawOfferUICramped (line 90) | function isDrawOfferUICramped(): boolean {

FILE: src/client/scripts/esm/game/gui/guigameinfo.ts
  function open (line 60) | function open(metadata: MetaData, showGameControlButtons?: boolean): void {
  function embedUsernameContainers (line 82) | function embedUsernameContainers(gameMetadata: MetaData): void {
  function close (line 143) | function close(): void {
  function clearUsernameContainers (line 160) | function clearUsernameContainers(): void {
  function initListeners_Gamecontrol (line 174) | function initListeners_Gamecontrol(): void {
  function closeListeners_Gamecontrol (line 181) | function closeListeners_Gamecontrol(): void {
  function undoMove (line 187) | function undoMove(): void {
  function restartGame (line 192) | function restartGame(): void {
  function update_GameControlButtons (line 200) | function update_GameControlButtons(undoingIsLegal: boolean): void {
  function preventFocus (line 212) | function preventFocus(event: Event): void {
  function revealPlayerNames (line 217) | function revealPlayerNames(): void {
  function hidePlayerNames (line 223) | function hidePlayerNames(): void {
  function toggle (line 228) | function toggle(): void {
  function getPlayerNamesForGame (line 239) | function getPlayerNamesForGame(metadata: MetaData): {
  function updateWhosTurn (line 311) | function updateWhosTurn(): void {
  function gameEnd (line 336) | function gameEnd(conclusion?: GameConclusion): void {
  function getHeightOfGameInfoBar (line 419) | function getHeightOfGameInfoBar(): number {
  function updateAlignmentUsernames (line 428) | function updateAlignmentUsernames(): void {
  function addRatingChangeToExistingUsernameContainers (line 454) | function addRatingChangeToExistingUsernameContainers(

FILE: src/client/scripts/esm/game/gui/guiloading.ts
  function closeAnimation (line 14) | function closeAnimation(): void {

FILE: src/client/scripts/esm/game/gui/guinavigation.ts
  constant TELEPORT_LIMIT (line 74) | const TELEPORT_LIMIT: bigint = 10n ** 30n;
  function isOpen (line 116) | function isOpen(): boolean {
  function toggle (line 121) | function toggle(): void {
  function open (line 128) | function open({ allowEditCoords = true }: { allowEditCoords?: boolean })...
  function initCoordinates (line 151) | function initCoordinates({ allowEditCoords }: { allowEditCoords: boolean...
  function close (line 165) | function close(): void {
  function updateElement_Coords (line 175) | function updateElement_Coords(): void {
  function displayBigIntInInput (line 196) | function displayBigIntInInput(
  function parseStringToBigInt (line 218) | function parseStringToBigInt(value: string): bigint {
  function isCoordinateActive (line 275) | function isCoordinateActive(): boolean {
  function initListeners_Navigation (line 279) | function initListeners_Navigation(): void {
  function closeListeners_Navigation (line 328) | function closeListeners_Navigation(): void {
  function callback_CoordsXChange (line 378) | function callback_CoordsXChange(): void {
  function callback_CoordsYChange (line 384) | function callback_CoordsYChange(): void {
  function callback_CoordsChange (line 389) | function callback_CoordsChange(index: 0 | 1): void {
  function callback_Back (line 417) | function callback_Back(): void {
  function callback_Expand (line 421) | function callback_Expand(): void {
  function recenter (line 453) | function recenter(): void {
  function callback_Annotations (line 459) | function callback_Annotations(): void {
  function isAnnotationsButtonEnabled (line 466) | function isAnnotationsButtonEnabled(): boolean {
  function callback__Collapse (line 470) | function callback__Collapse(): void {
  function showCollapse (line 481) | function showCollapse(): void {
  function hideCollapse (line 487) | function hideCollapse(): void {
  function areCoordsAllowedToBeEdited (line 498) | function areCoordsAllowedToBeEdited(): boolean {
  function getHeightOfNavBar (line 503) | function getHeightOfNavBar(): number {
  function callback_Pause (line 507) | function callback_Pause(): void {
  function update (line 512) | function update(): void {
  function callback_MoveRewind (line 524) | function callback_MoveRewind(): void {
  function callback_MoveForward (line 531) | function callback_MoveForward(): void {
  function isItOkayToRewindOrForward (line 537) | function isItOkayToRewindOrForward(): boolean {
  function update_MoveButtons (line 546) | function update_MoveButtons(): void {
  function callback_MoveRewindMouseDown (line 560) | function callback_MoveRewindMouseDown(): void {
  function callback_MoveRewindMouseLeave (line 568) | function callback_MoveRewindMouseLeave(): void {
  function callback_MoveRewindMouseUp (line 573) | function callback_MoveRewindMouseUp(): void {
  function callback_MoveForwardMouseDown (line 578) | function callback_MoveForwardMouseDown(): void {
  function callback_MoveForwardMouseLeave (line 586) | function callback_MoveForwardMouseLeave(): void {
  function callback_MoveForwardMouseUp (line 591) | function callback_MoveForwardMouseUp(): void {
  function callback_MoveRewindTouchStart (line 598) | function callback_MoveRewindTouchStart(): void {
  function callback_MoveRewindTouchMove (line 608) | function callback_MoveRewindTouchMove(event: TouchEvent): void {
  function callback_MoveRewindTouchEnd (line 625) | function callback_MoveRewindTouchEnd(): void {
  function callback_MoveForwardTouchStart (line 631) | function callback_MoveForwardTouchStart(): void {
  function callback_MoveForwardTouchMove (line 641) | function callback_MoveForwardTouchMove(event: TouchEvent): void {
  function callback_MoveForwardTouchEnd (line 659) | function callback_MoveForwardTouchEnd(): void {
  function lockRewind (line 669) | function lockRewind(): void {
  function testIfRewindMove (line 681) | function testIfRewindMove(): void {
  function testIfForwardMove (line 688) | function testIfForwardMove(): void {
  function rewindMove (line 694) | function rewindMove(): void {
  function forwardMove (line 713) | function forwardMove(): void {
  function isItOkayToUndoEditOrRedoEdit (line 726) | function isItOkayToUndoEditOrRedoEdit(): boolean {
  function update_EditButtons (line 735) | function update_EditButtons(): void {
  function callback_UndoEditMouseDown (line 745) | function callback_UndoEditMouseDown(): void {
  function callback_UndoEditMouseLeave (line 753) | function callback_UndoEditMouseLeave(): void {
  function callback_UndoEditMouseUp (line 758) | function callback_UndoEditMouseUp(): void {
  function callback_RedoEditMouseDown (line 763) | function callback_RedoEditMouseDown(): void {
  function callback_RedoEditMouseLeave (line 771) | function callback_RedoEditMouseLeave(): void {
  function callback_RedoEditMouseUp (line 776) | function callback_RedoEditMouseUp(): void {
  function callback_UndoEditTouchStart (line 783) | function callback_UndoEditTouchStart(): void {
  function callback_UndoEditTouchMove (line 793) | function callback_UndoEditTouchMove(event: TouchEvent): void {
  function callback_UndoEditTouchEnd (line 810) | function callback_UndoEditTouchEnd(): void {
  function callback_RedoEditTouchStart (line 816) | function callback_RedoEditTouchStart(): void {
  function callback_RedoEditTouchMove (line 826) | function callback_RedoEditTouchMove(event: TouchEvent): void {
  function callback_RedoEditTouchEnd (line 844) | function callback_RedoEditTouchEnd(): void {
  function testIfUndoEdit (line 851) | function testIfUndoEdit(): void {
  function testIfRedoEdit (line 857) | function testIfRedoEdit(): void {
  function callback_UndoEdit (line 863) | function callback_UndoEdit(): void {
  function callback_RedoEdit (line 870) | function callback_RedoEdit(): void {

FILE: src/client/scripts/esm/game/gui/guipause.ts
  constant MAIN_MENU_BUTTON_CHANGE_FREEZE_DURATION_MILLIS (line 45) | const MAIN_MENU_BUTTON_CHANGE_FREEZE_DURATION_MILLIS: number = 1000;
  function areWePaused (line 63) | function areWePaused(): boolean {
  function getelement_perspective (line 68) | function getelement_perspective(): HTMLElement {
  function open (line 73) | function open(): void {
  function toggle (line 97) | function toggle(): void {
  function updatePasteButtonTransparency (line 103) | function updatePasteButtonTransparency(): void {
  function updatePerspectiveButtonTransparency (line 118) | function updatePerspectiveButtonTransparency(): void {
  function updateDrawOfferButton (line 127) | function updateDrawOfferButton(): void {
  function onReceiveOpponentsMove (line 142) | function onReceiveOpponentsMove(): void {
  function updateTextOfMainMenuButton (line 154) | function updateTextOfMainMenuButton(freezeMainMenuButtonUponChange?: tru...
  function freezeMainMenuButton (line 191) | function freezeMainMenuButton(): void {
  function initListeners (line 201) | function initListeners(): void {
  function closeListeners (line 213) | function closeListeners(): void {
  function callback_CopyGame (line 225) | function callback_CopyGame(_event: Event): void {
  function callback_PasteGame (line 230) | function callback_PasteGame(_event: Event): void {
  function callback_Resume (line 235) | function callback_Resume(): void {
  function callback_MainMenu (line 244) | function callback_MainMenu(): void {
  function callback_PracticeMenu (line 260) | function callback_PracticeMenu(): void {
  function callback_OfferDraw (line 268) | function callback_OfferDraw(): void {
  function callback_ToggleArrows (line 287) | function callback_ToggleArrows(): void {
  function callback_Perspective (line 300) | function callback_Perspective(): void {

FILE: src/client/scripts/esm/game/gui/guiplay.ts
  constant TIME_CONTROL_IDXS (line 60) | const TIME_CONTROL_IDXS = {
  function isOpen (line 102) | function isOpen(): boolean {
  function getModeSelected (line 107) | function getModeSelected(): typeof modeSelected {
  function hideElement_joinPrivate (line 111) | function hideElement_joinPrivate(): void {
  function showElement_joinPrivate (line 114) | function showElement_joinPrivate(): void {
  function hideElement_inviteCode (line 117) | function hideElement_inviteCode(): void {
  function showElement_inviteCode (line 120) | function showElement_inviteCode(): void {
  function open (line 124) | function open(): void {
  function close (line 133) | function close(): void {
  function initListeners (line 145) | function initListeners(): void {
  function closeListeners (line 161) | function closeListeners(): void {
  function changePlayMode (line 177) | function changePlayMode(mode: typeof modeSelected): void {
  function callback_playBack (line 273) | function callback_playBack(): void {
  function callback_online (line 278) | function callback_online(): void {
  function callback_local (line 282) | function callback_local(): void {
  function callback_computer (line 286) | function callback_computer(): void {
  function callback_createInvite (line 291) | function callback_createInvite(): void {
  function getInviteOptions (line 329) | function getInviteOptions(): InviteOptions {
  function getEngineDifficultyConfig (line 344) | function getEngineDifficultyConfig(): { strengthLevel: number } {
  function callback_updateOptions (line 361) | function callback_updateOptions(): void {
  function savePreferredClockOption (line 385) | function savePreferredClockOption(clockIndex: number): void {
  function savePreferredRatedOption (line 395) | function savePreferredRatedOption(ratedValue: string): void {
  function callback_joinPrivate (line 403) | function callback_joinPrivate(): void {
  function callback_textboxPrivateEnter (line 414) | function callback_textboxPrivateEnter(event: KeyboardEvent): void {
  function callback_copyInviteCode (line 421) | function callback_copyInviteCode(): void {
  function initListeners_Invites (line 433) | function initListeners_Invites(): void {
  function closeListeners_Invites (line 443) | function closeListeners_Invites(): void {
  function callback_inviteMouseEnter (line 453) | function callback_inviteMouseEnter(event: Event): void {
  function callback_inviteMouseLeave (line 457) | function callback_inviteMouseLeave(event: Event): void {
  function callback_inviteClicked (line 461) | function callback_inviteClicked(event: Event): void {
  function lockCreateInviteButton (line 474) | function lockCreateInviteButton(): void {
  function unlockCreateInviteButton (line 487) | function unlockCreateInviteButton(): void {
  function disableCreateInviteButton (line 493) | function disableCreateInviteButton(): void {
  function enableCreateInviteButton (line 496) | function enableCreateInviteButton(): void {
  function setElement_CreateInviteTextContent (line 499) | function setElement_CreateInviteTextContent(text: string): void {
  function isCreateInviteButtonLocked (line 504) | function isCreateInviteButtonLocked(): boolean {
  function lockAcceptInviteButton (line 512) | function lockAcceptInviteButton(): void {
  function unlockAcceptInviteButton (line 522) | function unlockAcceptInviteButton(): void {
  function isAcceptInviteButtonLocked (line 531) | function isAcceptInviteButtonLocked(): boolean {

FILE: src/client/scripts/esm/game/gui/guipractice.ts
  constant SCROLL (line 60) | const SCROLL: {
  function getCheckmateSelectedID (line 95) | function getCheckmateSelectedID(): string {
  function open (line 99) | function open(): void {
  function close (line 110) | function close(): void {
  function createPracticeHTML (line 121) | function createPracticeHTML(): void {
  function addPieceIcons (line 180) | async function addPieceIcons(): Promise<void> {
  function removePieceIcons (line 222) | function removePieceIcons(): void {
  function initListeners (line 243) | function initListeners(): void {
  function closeListeners (line 257) | function closeListeners(): void {
  function callback_mouseDown (line 273) | function callback_mouseDown(event: MouseEvent): void {
  function callback_mouseUp (line 283) | function callback_mouseUp(event: MouseEvent): void {
  function callback_mouseMove (line 294) | function callback_mouseMove(event: MouseEvent): void {
  function applyMomentum (line 306) | function applyMomentum(): void {
  function clearScrollMomentumInterval (line 317) | function clearScrollMomentumInterval(): void {
  function changeCheckmateSelected (line 324) | function changeCheckmateSelected(checkmateid: string): void {
  function updateCheckmatesBeaten (line 341) | function updateCheckmatesBeaten(completedCheckmates: string[]): void {
  function updateBadges (line 370) | function updateBadges(numCompleted: number, numTotal: number): void {
  function callback_practiceBack (line 417) | function callback_practiceBack(_event: Event): void {
  function callback_practicePlay (line 422) | function callback_practicePlay(): void {
  function callback_keyPress (line 428) | function callback_keyPress(event: KeyboardEvent): void {
  function moveDownSelection (line 434) | function moveDownSelection(event: Event): void {
  function moveUpSelection (line 443) | function moveUpSelection(event: Event): void {

FILE: src/client/scripts/esm/game/gui/guipromotion.ts
  function isUIOpen (line 48) | function isUIOpen(): boolean {
  function open (line 52) | function open(color: Player): void {
  function close (line 61) | function close(): void {
  function initUI (line 75) | async function initUI(promotionsAllowed: PlayerGroup<RawType[]> | undefi...
  function resetUI (line 102) | function resetUI(): void {
  function callback_promote (line 112) | function callback_promote(event: Event): void {
  function update (line 121) | function update(): void {

FILE: src/client/scripts/esm/game/gui/guititle.ts
  function open (line 27) | function open(): void {
  function close (line 33) | function close(): void {
  function initListeners (line 39) | function initListeners(): void {
  function closeListeners (line 48) | function closeListeners(): void {
  function callback_Play (line 57) | function callback_Play(_event: Event): void {
  function callback_Practice (line 62) | function callback_Practice(_event: Event): void {
  function callback_Guide (line 67) | function callback_Guide(_event: Event): void {
  function callback_BoardEditor (line 72) | function callback_BoardEditor(_event: Event): void {

FILE: src/client/scripts/esm/game/gui/loadingscreen.ts
  function initColorOfLoadingBackground (line 31) | function initColorOfLoadingBackground(): void {
  function open (line 50) | async function open(): Promise<void> {
  function close (line 58) | async function close(): Promise<void> {
  function onError (line 69) | async function onError(): Promise<void> {

FILE: src/client/scripts/esm/game/gui/stats.ts
  function showMoves (line 43) | function showMoves(durationSecs: number = 2.5): void {
  function hideMoves (line 54) | function hideMoves(): void {
  function updateTextContentOfMoves (line 59) | function updateTextContentOfMoves(): void {
  function updateStatsCSS (line 66) | function updateStatsCSS(): void {
  function toggleFPS (line 72) | function toggleFPS(): void {
  function showFPS (line 78) | function showFPS(): void {
  function hideFPS (line 83) | function hideFPS(): void {
  function updateFPS (line 87) | function updateFPS(fps: number): void {

FILE: src/client/scripts/esm/game/gui/style.ts
  type HSLColor (line 15) | interface HSLColor {
  constant SVG_NS (line 27) | const SVG_NS = 'http://www.w3.org/2000/svg';
  function setNavStyle (line 41) | function setNavStyle(cssStyle: string): void {
  function getElementIndexWithinItsParent (line 52) | function getElementIndexWithinItsParent(element: Element): number {
  function getChildByIndexInParent (line 69) | function getChildByIndexInParent(parent: Element, index: number): Elemen...
  function arrayToCssColor (line 81) | function arrayToCssColor(colorArray: Color): string {
  function rgbToHsl (line 105) | function rgbToHsl(r: number, g: number, b: number): HSLColor {
  function rgbToCssString (line 145) | function rgbToCssString(r: number, g: number, b: number): string {
  function hslToCssString (line 154) | function hslToCssString(hsl: HSLColor): string {

FILE: src/client/scripts/esm/game/gui/toast.ts
  type ToastOptions (line 9) | interface ToastOptions {
  constant DURATION_BASE (line 26) | const DURATION_BASE = 900;
  constant DURATION_MULTIPLIER (line 28) | const DURATION_MULTIPLIER = 45;
  constant FADE_DURATION (line 31) | const FADE_DURATION = 1000;
  function show (line 43) | function show(text: string, options: ToastOptions = {}): void {
  function fadeAfter (line 77) | function fadeAfter(ms: number): void {
  function hideAfter (line 90) | function hideAfter(ms: number): void {
  function showPleaseWaitForTask (line 100) | function showPleaseWaitForTask(): void {

FILE: src/client/scripts/esm/game/input.ts
  type MouseButton (line 36) | type MouseButton = (typeof Mouse)[keyof typeof Mouse];
  type KeyDownInfo (line 39) | interface KeyDownInfo {
  type InputListener (line 48) | interface InputListener {
  type PointerHistory (line 144) | type PointerHistory = { pos: DoubleCoords; time: number }[];
  constant CLICK_THRESHOLDS (line 147) | const CLICK_THRESHOLDS = {
  constant MOUSE_POS_HISTORY_WINDOW_MILLIS (line 167) | const MOUSE_POS_HISTORY_WINDOW_MILLIS = 80;
  type PhysicalPointer (line 174) | type PhysicalPointer = {
  type LogicalPointer (line 193) | type LogicalPointer = {
  type ClickInfo (line 206) | interface ClickInfo {
  function CreateInputListener (line 260) | function CreateInputListener(
  function getPhysicalPointerId (line 906) | function getPhysicalPointerId(e: MouseEvent | Touch): string {
  function getLogicalPointerId (line 912) | function getLogicalPointerId(e: MouseEvent | Touch, button: MouseButton)...
  function getRelativeMousePosition (line 921) | function getRelativeMousePosition(

FILE: src/client/scripts/esm/game/main.ts
  function start (line 24) | function start(): void {
  function initListeners (line 41) | function initListeners(): void {
  function gameLoop (line 55) | function gameLoop(runtime: number): void {
  function render (line 69) | function render(): void {

FILE: src/client/scripts/esm/game/misc/controls.ts
  function updateNavControls (line 70) | function updateNavControls(): void {
  function detectPanning (line 83) | function detectPanning(): void {
  function accelPanVel (line 127) | function accelPanVel(panVel: DoubleCoords, angleDegs: number): DoubleCoo...
  function deccelPanVel (line 139) | function deccelPanVel(panVel: DoubleCoords): DoubleCoords {
  function detectZooming (line 156) | function detectZooming(): void {
  function deccelerateScaleVel (line 194) | function deccelerateScaleVel(scaleVel: number): number {
  function testOutGameToggles (line 214) | function testOutGameToggles(): void {
  function testInGameToggles (line 222) | function testInGameToggles(gamefile: FullGame, mesh: Mesh | undefined): ...

FILE: src/client/scripts/esm/game/misc/enginegame.ts
  type EngineConfig (line 30) | interface EngineConfig {
  function areInEngineGame (line 68) | function areInEngineGame(): boolean {
  function getOurColor (line 72) | function getOurColor(): Player | undefined {
  function isItOurTurn (line 77) | function isItOurTurn(): boolean {
  function getCurrentEngine (line 83) | function getCurrentEngine(): string | undefined {
  function initEngineGame (line 92) | function initEngineGame(options: {
  function closeEngineGame (line 140) | function closeEngineGame(): void {
  function areWeColor (line 161) | function areWeColor(color: Player): boolean {
  function onMovePlayed (line 169) | function onMovePlayed(): void {
  function handleEngineMessage (line 223) | function handleEngineMessage(data: any): void {
  function makeEngineMove (line 253) | function makeEngineMove(tokenMove: unknown): void {
  function toggleDebug (line 302) | function toggleDebug(): void {
  function onViewMove (line 312) | function onViewMove(): void {
  function requestMovesForCurrentPosition (line 321) | function requestMovesForCurrentPosition(): void {
  function render (line 347) | function render(): void {

FILE: src/client/scripts/esm/game/misc/gamesound.ts
  type SoundName (line 51) | type SoundName = keyof typeof soundStamps;
  type SoundTimeSnippet (line 53) | type SoundTimeSnippet = readonly [number, number];
  constant SUCCESSIVE_MOVES_CONFIG (line 58) | const SUCCESSIVE_MOVES_CONFIG = {
  constant REVERB_CONFIG (line 68) | const REVERB_CONFIG = {
  constant BELL_CONFIG (line 80) | const BELL_CONFIG = {
  constant RIPPLE_CONFIG (line 88) | const RIPPLE_CONFIG = {
  constant SHAKE_CONFIG (line 108) | const SHAKE_CONFIG = {
  constant PREMOVE_CONFIG (line 119) | const PREMOVE_CONFIG = {
  function getSoundStamp (line 152) | function getSoundStamp(soundName: SoundName): SoundTimeSnippet {
  function getStampDuration (line 159) | function getStampDuration(stamp: SoundTimeSnippet): number {
  function getSoundTimeSnippet (line 165) | function getSoundTimeSnippet(soundName: SoundName): { startTime: number;...
  function playSoundEffect (line 180) | function playSoundEffect(
  function playMove (line 241) | function playMove(
  function calculateReverb (line 314) | function calculateReverb(
  function playGamestart (line 335) | function playGamestart(): SoundObject | undefined {
  function playWin (line 339) | function playWin(delay?: number): SoundObject | undefined {
  function playDraw (line 343) | function playDraw(delay?: number): SoundObject | undefined {
  function playLoss (line 347) | function playLoss(delay?: number): SoundObject | undefined {
  function playLowtime (line 351) | function playLowtime(): SoundObject | undefined {
  function playDrum (line 355) | function playDrum(): SoundObject | undefined {
  function playTick (line 360) | function playTick({ volume, offset }: { volume?: number; offset?: number...
  function playTicking (line 366) | function playTicking({ volume, offset }: { volume?: number; offset?: num...
  function playViola_c3 (line 372) | function playViola_c3({ volume }: { volume?: number } = {}): SoundObject...
  function playViolin_c4 (line 376) | function playViolin_c4(): SoundObject | undefined {
  function playMarimba (line 380) | function playMarimba(): SoundObject | undefined {
  function playBase (line 385) | function playBase(): SoundObject | undefined {
  function playGlassCrack (line 389) | function playGlassCrack(): SoundObject | undefined {

FILE: src/client/scripts/esm/game/misc/invites.ts
  type InviteOptions (line 30) | interface InviteOptions {
  function gelement_iCodeCode (line 54) | function gelement_iCodeCode(): HTMLElement {
  function update (line 58) | function update(): void {
  function unsubIfWeNotHave (line 64) | function unsubIfWeNotHave(): void {
  function unsubFromInvites (line 71) | function unsubFromInvites(): void {
  function onmessage (line 81) | function onmessage(contents: InvitesMessage): void {
  function create (line 105) | function create(variantOptions: InviteOptions): void {
  function cancel (line 130) | function cancel(inviteID = ourInviteID): void {
  function generateTagForInvite (line 148) | function generateTagForInvite(inviteOptions: {
  function updateInviteList (line 166) | function updateInviteList(list: Invite[]): void {
  function playSoundNewOpponentInvite (line 291) | function playSoundNewOpponentInvite(): void {
  function clear (line 300) | function clear(resetRecentUsersCache?: true): void {
  function clearIfOnPlayPage (line 312) | function clearIfOnPlayPage(): void {
  function isInviteOurs (line 319) | function isInviteOurs(invite: Invite): boolean {
  function getInviteFromElement (line 338) | function getInviteFromElement(inviteElement: HTMLElement): Invite {
  function createDiv (line 360) | function createDiv(
  function accept (line 372) | function accept(inviteID: string, isPrivate: boolean): void {
  function click (line 384) | function click(element: HTMLElement): void {
  function updateCreateInviteButton (line 397) | function updateCreateInviteButton(): void {
  function updatePrivateInviteCode (line 404) | function updatePrivateInviteCode(privateInviteID: string | undefined): v...
  function updateActiveGameCount (line 432) | function updateActiveGameCount(newCount: number): void {
  function doWeHave (line 437) | function doWeHave(): boolean {
  function subscribeToInvites (line 446) | async function subscribeToInvites(ignoreAlreadySubbed?: boolean): Promis...

FILE: src/client/scripts/esm/game/misc/keybinds.ts
  function getBoardDragMouseButton (line 18) | function getBoardDragMouseButton(): MouseButton | undefined {
  function getAnnotationMouseButton (line 27) | function getAnnotationMouseButton(): MouseButton | undefined {
  function getCollapseMouseButton (line 35) | function getCollapseMouseButton(): MouseButton | undefined {
  function getPieceSelectionMouseButton (line 42) | function getPieceSelectionMouseButton(): MouseButton | undefined {
  function getEffectiveDragEnabled (line 52) | function getEffectiveDragEnabled(): boolean {

FILE: src/client/scripts/esm/game/misc/loadbalancer.ts
  function getRunTime (line 51) | function getRunTime(): number {
  function getDeltaTime (line 56) | function getDeltaTime(): number {
  function getTimeUntilAFK (line 60) | function getTimeUntilAFK(): number {
  function areWeAFK (line 64) | function areWeAFK(): boolean {
  function areWeHibernating (line 68) | function areWeHibernating(): boolean {
  function isPageHidden (line 72) | function isPageHidden(): boolean {
  function update (line 76) | function update(runtime: number): void {
  function updateDeltaTime (line 90) | function updateDeltaTime(runtime: number): void {
  function trimFrames (line 97) | function trimFrames(): void {
  function updateFPS (line 108) | function updateFPS(): void {
  function updateMonitorRefreshRate (line 114) | function updateMonitorRefreshRate(): void {
  function updateAFK (line 119) | function updateAFK(): void {
  function onReturnFromAFK (line 124) | function onReturnFromAFK(): void {
  function restartAFKTimer (line 134) | function restartAFKTimer(): void {
  function restartHibernateTimer (line 139) | function restartHibernateTimer(): void {
  function onAFK (line 144) | function onAFK(): void {
  function onHibernate (line 150) | function onHibernate(): void {
  function cancelTimerToDeleteInviteAfterLeavingPage (line 197) | function cancelTimerToDeleteInviteAfterLeavingPage(): void {

FILE: src/client/scripts/esm/game/misc/onlinegame/afk.ts
  function isOurAFKAutoResignTimerRunning (line 65) | function isOurAFKAutoResignTimerRunning(): boolean {
  function onGameStart (line 70) | function onGameStart(): void {
  function onGameClose (line 75) | function onGameClose(): void {
  function onMovePlayed (line 86) | function onMovePlayed({ isOpponents }: { isOpponents: boolean }): void {
  function updateAFK (line 92) | function updateAFK(): void {
  function rescheduleAlertServerWeAFK (line 104) | function rescheduleAlertServerWeAFK(): void {
  function cancelAFKTimer (line 124) | function cancelAFKTimer(): void {
  function tellServerWeAFK (line 131) | function tellServerWeAFK(): void {
  function tellServerWeBackFromAFK (line 144) | function tellServerWeBackFromAFK(): void {
  function displayWeAFK (line 153) | function displayWeAFK(secsRemaining: number): void {
  function playStaccatoNote (line 168) | function playStaccatoNote(note: 'c3' | 'c4', secsRemaining: number): void {
  function startOpponentAFKCountdown (line 185) | function startOpponentAFKCountdown(millisUntilAutoAFKResign: number): vo...
  function stopOpponentAFKCountdown (line 199) | function stopOpponentAFKCountdown(): void {
  function displayOpponentAFK (line 204) | function displayOpponentAFK(secsRemaining: number): void {

FILE: src/client/scripts/esm/game/misc/onlinegame/disconnect.ts
  type OpponentDisconnectValue (line 21) | interface OpponentDisconnectValue {
  function startOpponentDisconnectCountdown (line 41) | function startOpponentDisconnectCountdown({
  function stopOpponentDisconnectCountdown (line 56) | function stopOpponentDisconnectCountdown(): void {
  function displayOpponentDisconnect (line 61) | function displayOpponentDisconnect(secsRemaining: number, wasByChoice: b...

FILE: src/client/scripts/esm/game/misc/onlinegame/drawoffers.ts
  function isOfferingDrawLegal (line 45) | function isOfferingDrawLegal(): boolean {
  function isTooSoonToOfferDraw (line 58) | function isTooSoonToOfferDraw(): boolean {
  function areWeAcceptingDraw (line 70) | function areWeAcceptingDraw(): boolean {
  function onOpponentExtendedOffer (line 75) | function onOpponentExtendedOffer(): void {
  function onOpponentDeclinedOffer (line 83) | function onOpponentDeclinedOffer(): void {
  function extendOffer (line 91) | function extendOffer(): void {
  function callback_AcceptDraw (line 103) | function callback_AcceptDraw(): void {
  function callback_declineDraw (line 118) | function callback_declineDraw(): void {
  function closeDraw (line 130) | function closeDraw(): void {
  function set (line 140) | function set(drawOffer: DrawOfferInfo): void {
  function onMovePlayed (line 148) | function onMovePlayed({ isOpponents }: { isOpponents: boolean }): void {
  function onGameClose (line 158) | function onGameClose(): void {

FILE: src/client/scripts/esm/game/misc/onlinegame/movesendreceive.ts
  function sendMove (line 44) | function sendMove(): void {
  function handleOpponentsMove (line 73) | function handleOpponentsMove(
  function checkAndReportIllegalOpponentMove (line 146) | function checkAndReportIllegalOpponentMove(
  function applyClockValues (line 173) | function applyClockValues(gamefile: FullGame, clockValues: ClockValues |...

FILE: src/client/scripts/esm/game/misc/onlinegame/onlinegame.ts
  function areInOnlineGame (line 92) | function areInOnlineGame(): boolean {
  function getGameID (line 97) | function getGameID(): number {
  function getIsPrivate (line 103) | function getIsPrivate(): boolean {
  function isRated (line 109) | function isRated(): boolean {
  function doWeHaveRole (line 115) | function doWeHaveRole(): boolean {
  function getOurColor (line 123) | function getOurColor(): Player | undefined {
  function getPlayerRatings (line 129) | function getPlayerRatings(): PlayerGroup<Rating> | undefined {
  function areWeColorInOnlineGame (line 134) | function areWeColorInOnlineGame(color: Player): boolean {
  function isItOurTurn (line 139) | function isItOurTurn(): boolean {
  function hasPlayerPressedAbortOrResignButton (line 146) | function hasPlayerPressedAbortOrResignButton(): boolean {
  function areInSync (line 154) | function areInSync(): boolean {
  function hasServerConcludedGame (line 164) | function hasServerConcludedGame(): boolean {
  function setInSyncTrue (line 172) | function setInSyncTrue(): void {
  function setInSyncFalse (line 176) | function setInSyncFalse(): void {
  function initOnlineGame (line 183) | function initOnlineGame(options: {
  function set_DrawOffers_DisconnectInfo_AutoAFKResign (line 213) | function set_DrawOffers_DisconnectInfo_AutoAFKResign(participantState?: ...
  function closeOnlineGame (line 230) | function closeOnlineGame(): void {
  function initEventListeners (line 246) | function initEventListeners(): void {
  function closeEventListeners (line 263) | function closeEventListeners(): void {
  function confirmNavigationAwayFromGame (line 278) | function confirmNavigationAwayFromGame(event: MouseEvent): void {
  function update (line 299) | function update(): void {
  function resyncToGame (line 306) | function resyncToGame(): void {
  function onMovePlayed (line 312) | function onMovePlayed({ isOpponents }: { isOpponents: boolean }): void {
  function reportOpponentsMove (line 320) | function reportOpponentsMove(reason: string): void {
  function onAbortOrResignButtonPress (line 333) | function onAbortOrResignButtonPress(): void {
  function onMainMenuButtonPress (line 350) | function onMainMenuButtonPress(): void {
  function deleteCustomVariantOptions (line 361) | function deleteCustomVariantOptions(): void {
  function requestRemovalFromPlayersInActiveGames (line 377) | function requestRemovalFromPlayersInActiveGames(): void {
  function adjustClockValuesForPing (line 394) | function adjustClockValuesForPing(clockValues: ClockValues): ClockValues {
  function getKeyForOnlineGameVariantOptions (line 426) | function getKeyForOnlineGameVariantOptions(gameID: number): string {

FILE: src/client/scripts/esm/game/misc/onlinegame/onlinegamerouter.ts
  type LoggedGameInfo (line 39) | type LoggedGameInfo = Required<
  function routeMessage (line 50) | function routeMessage(contents: GameMessage): void {
  function handleJoinGame (line 132) | function handleJoinGame(message: JoinGameMessage): void {
  function handleLoggedGameInfo (line 149) | function handleLoggedGameInfo(message: LoggedGameInfo): void {
  function handleUpdatedClock (line 220) | function handleUpdatedClock(basegame: Game, clockValues: ClockValues): v...
  function handleUnsubbing (line 237) | function handleUnsubbing(): void {
  function handleLogin (line 246) | function handleLogin(basegame: Game): void {
  function handleNoGame (line 265) | function handleNoGame(basegame: Game): void {
  function handleLeaveGame (line 280) | function handleLeaveGame(): void {

FILE: src/client/scripts/esm/game/misc/onlinegame/resyncer.ts
  function handleServerGameUpdate (line 40) | function handleServerGameUpdate(
  function synchronizeMovesList (line 87) | function synchronizeMovesList(
  function findLastestMatchingMoveIndex (line 220) | function findLastestMatchingMoveIndex(ourMoves: MoveRecord[], serverMove...

FILE: src/client/scripts/esm/game/misc/onlinegame/tabnameflash.ts
  function onGameStart (line 29) | function onGameStart({ isOurMove }: { isOurMove: boolean }): void {
  function onGameClose (line 35) | function onGameClose(): void {
  function onMovePlayed (line 40) | function onMovePlayed({ isOpponents }: { isOpponents: boolean }): void {
  function flashTabNameYOUR_MOVE (line 57) | function flashTabNameYOUR_MOVE(parity: boolean): void {
  function cancelFlashTabTimer (line 70) | function cancelFlashTabTimer(): void {
  function scheduleMoveSound_timeoutID (line 76) | function scheduleMoveSound_timeoutID(): void {
  function cancelMoveSound (line 87) | function cancelMoveSound(): void {

FILE: src/client/scripts/esm/game/misc/space.ts
  constant HALF (line 22) | const HALF: BigDecimal = bd.fromNumber(0.5);
  function convertWorldSpaceToCoords (line 28) | function convertWorldSpaceToCoords(worldCoords: DoubleCoords): BDCoords {
  function convertWorldSpaceToCoords_Axis (line 38) | function convertWorldSpaceToCoords_Axis(
  function convertWorldSpaceToCoords_Rounded (line 48) | function convertWorldSpaceToCoords_Rounded(worldCoords: DoubleCoords): C...
  function roundCoord (line 54) | function roundCoord(coord: BigDecimal): bigint {
  function roundCoords (line 60) | function roundCoords(coords: BDCoords): Coords {
  function convertCoordToWorldSpace (line 65) | function convertCoordToWorldSpace(
  function convertCoordToWorldSpace_IgnoreSquareCenter (line 85) | function convertCoordToWorldSpace_IgnoreSquareCenter(
  function convertPixelsToWorldSpace_Virtual (line 100) | function convertPixelsToWorldSpace_Virtual(value: number): number {
  function convertWorldSpaceToPixels_Virtual (line 106) | function convertWorldSpaceToPixels_Virtual(value: number): number {
  function convertWorldSpaceToGrid (line 112) | function convertWorldSpaceToGrid(value: number): BigDecimal {

FILE: src/client/scripts/esm/game/rendering/ColorFlowRenderer.ts
  class ColorFlowRenderer (line 15) | class ColorFlowRenderer {
    method constructor (line 82) | constructor(gl: WebGL2RenderingContext) {
    method init (line 87) | private init(): void {
    method createShader (line 133) | private createShader(type: number, source: string): WebGLShader | null {
    method render (line 150) | public render(deltaTime: number): void {

FILE: src/client/scripts/esm/game/rendering/WaterRipples.ts
  constant RIPPLE_DIST_FROM_EDGE (line 30) | const RIPPLE_DIST_FROM_EDGE = 0.54;
  constant ELAPSED_TIME_OFFSET (line 32) | const ELAPSED_TIME_OFFSET = -230;
  constant RIPPLE_LIFETIME_BASE (line 38) | const RIPPLE_LIFETIME_BASE = 1.1;
  constant RIPPLE_LIFETIME_MULTIPLIER (line 40) | const RIPPLE_LIFETIME_MULTIPLIER = 0.5;
  function init (line 56) | function init(programManager: ProgramManager, width: number, height: num...
  function updateRippleLifetime (line 70) | function updateRippleLifetime(width: number, height: number): void {
  function addRipple (line 79) | function addRipple(sourceCoords: Coords): void {
  function update (line 146) | function update(): void {
  function getPass (line 174) | function getPass(): PostProcessPass[] {

FILE: src/client/scripts/esm/game/rendering/animation.ts
  type AnimationSegment (line 38) | interface AnimationSegment {
  type SegmentInfo (line 53) | type SegmentInfo = {
  type Animation (line 71) | interface Animation {
  constant ZERO (line 102) | const ZERO = bd.fromBigInt(0n);
  constant ONE (line 103) | const ONE = bd.fromBigInt(1n);
  constant SPLINES (line 106) | const SPLINES: {
  constant TRANSPARENT_SQUARE_Z (line 125) | const TRANSPARENT_SQUARE_Z: number = 0.01;
  constant SOUND_OFFSET (line 127) | const SOUND_OFFSET: number = 0;
  constant MAX_DISTANCE_BEFORE_TELEPORT (line 129) | const MAX_DISTANCE_BEFORE_TELEPORT: number = 80;
  constant MOVE_ANIMATION_DURATION (line 132) | const MOVE_ANIMATION_DURATION = {
  constant DEBUG (line 153) | let DEBUG = false;
  function animatePiece (line 174) | function animatePiece(
  function clearAnimations (line 241) | function clearAnimations(playSounds = false): void {
  function toggleDebug (line 250) | function toggleDebug(): void {
  function stretchKeyframesForResolution (line 261) | function stretchKeyframesForResolution<T>(
  function createAnimationSegments (line 275) | function createAnimationSegments(waypoints: BDCoords[]): AnimationSegmen...
  function calculateAnimationDuration (line 298) | function calculateAnimationDuration(totalDistance: BigDecimal, waypointC...
  function scheduleSoundPlayback (line 320) | function scheduleSoundPlayback(animation: Animation): void {
  function scheduleAnimationRemoval (line 326) | function scheduleAnimationRemoval(animation: Animation): void {
  function playAnimationSound (line 340) | function playAnimationSound(animation: Animation): void {
  function update (line 353) | function update(): void {
  function shiftArrowIndicatorOfAnimatedPiece (line 363) | function shiftArrowIndicatorOfAnimatedPiece(animation: Animation): void {
  function renderTransparentSquares (line 388) | function renderTransparentSquares(): void {
  function renderAnimations (line 406) | function renderAnimations(): void {
  function getCurrentSegment (line 504) | function getCurrentSegment(
  function getCurrentAnimationPosition (line 578) | function getCurrentAnimationPosition(
  function forEachActiveKeyframe (line 610) | function forEachActiveKeyframe<T>(

FILE: src/client/scripts/esm/game/rendering/area.ts
  type Area (line 30) | interface Area {
  constant TWO (line 39) | const TWO = bd.fromNumber(2.0);
  function applyPaddingToBox (line 60) | function applyPaddingToBox(box: BoundingBoxBD): BoundingBoxBD {
  function calculateFromBox (line 110) | function calculateFromBox(box: BoundingBoxBD): Area {
  function getBoundingBoxHalfDimensions (line 131) | function getBoundingBoxHalfDimensions(boundingBox: BoundingBoxBD): {
  function calcScaleToMatchSides (line 149) | function calcScaleToMatchSides(boundingBox: BoundingBoxBD): BigDecimal {
  function calculateFromCoordsList (line 175) | function calculateFromCoordsList(coordsList: Coords[]): Area {
  function calculateFromUnpaddedBox (line 189) | function calculateFromUnpaddedBox(box: BoundingBoxBD): Area {
  function initTransitionFromArea (line 200) | function initTransitionFromArea(thisArea: Area, ignoreHistory: boolean):...

FILE: src/client/scripts/esm/game/rendering/arrows/arrowlegalmovehighlights.ts
  type ArrowLegalMoves (line 32) | interface ArrowLegalMoves {
  function update (line 73) | function update(): void {
  function onPieceIndicatorHover (line 111) | function onPieceIndicatorHover(arrowPiece: ArrowPiece): void {
  function renderEachHoveredPieceLegalMoves (line 171) | function renderEachHoveredPieceLegalMoves(): void {
  function regenModelsOfHoveredPieces (line 193) | function regenModelsOfHoveredPieces(): void {
  function reset (line 215) | function reset(): void {

FILE: src/client/scripts/esm/game/rendering/arrows/arrows.ts
  type SlideArrows (line 30) | interface SlideArrows {
  type ArrowsLine (line 50) | interface ArrowsLine {
  type Arrow (line 61) | interface Arrow extends BaseArrow {
  type ArrowPiece (line 81) | interface ArrowPiece {
  type BaseArrow (line 90) | interface BaseArrow {
  type HoveredArrow (line 98) | interface HoveredArrow {
  type HintArrow (line 116) | interface HintArrow extends BaseArrow {
  constant MAX_PIECES (line 126) | const MAX_PIECES = 40_000;
  constant MAX_LINES (line 128) | const MAX_LINES = 8;
  function getMode (line 175) | function getMode(): typeof mode {
  function setMode (line 180) | function setMode(value: typeof mode): void {
  function toggleArrows (line 189) | function toggleArrows(): void {
  function getAllArrows (line 207) | function getAllArrows(): Arrow[] {
  function getSlideArrows (line 219) | function getSlideArrows(): SlideArrows {
  function getAnimatedArrows (line 224) | function getAnimatedArrows(): Arrow[] {
  function getHoveredArrows (line 232) | function getHoveredArrows(): HoveredArrow[] {
  function getHintArrows (line 237) | function getHintArrows(): HintArrow[] {
  function areHoveringAtleastOneArrow (line 245) | function areHoveringAtleastOneArrow(): boolean {
  function getAllArrowWorldLocations (line 253) | function getAllArrowWorldLocations(): DoubleCoords[] {
  function areArrowsActiveThisFrame (line 261) | function areArrowsActiveThisFrame(): boolean {
  function reset (line 271) | function reset(): void {
  function update (line 286) | function update(): void {

FILE: src/client/scripts/esm/game/rendering/arrows/arrowscalculator.ts
  type SlideArrowsDraft (line 62) | interface SlideArrowsDraft {
  type ArrowsLineDraft (line 83) | interface ArrowsLineDraft {
  type ArrowDraft (line 97) | type ArrowDraft = {
  constant WIDTH (line 107) | const WIDTH = 0.65;
  constant EDGE_GAP (line 110) | const EDGE_GAP = 0.15;
  constant IMAGE_EDGE_DIST (line 112) | const IMAGE_EDGE_DIST: BigDecimal = bd.fromNumber(WIDTH / 2 + EDGE_GAP);
  constant STACK_PADDING (line 115) | const STACK_PADDING = 0.35;
  constant OPACITY (line 117) | const OPACITY = 0.6;
  constant MIN_SQUARE_SIZE (line 120) | const MIN_SQUARE_SIZE: BigDecimal = bd.fromBigInt(12n);
  constant PERSPECTIVE_EDGE_DIST (line 123) | const PERSPECTIVE_EDGE_DIST = 17;
  constant HALF (line 125) | const HALF = bd.fromNumber(0.5);
  function getBoundingBoxFloat (line 143) | function getBoundingBoxFloat(): BoundingBoxBD | undefined {
  function areZoomedInEnoughForArrows (line 148) | function areZoomedInEnoughForArrows(): boolean {
  function getArrowIndicatorHalfWidth (line 156) | function getArrowIndicatorHalfWidth(): number {
  function calculateArrows (line 167) | function calculateArrows(mode: 0 | 1 | 2 | 3): {
  function updateBoundingBoxesOfVisibleScreen (line 199) | function updateBoundingBoxesOfVisibleScreen(): void {
  function generateArrowsDraft (line 244) | function generateArrowsDraft(): SlideArrowsDraft {
  function calcArrowsLineDraft (line 294) | function calcArrowsLineDraft(
  function removeUnnecessaryArrows (line 491) | function removeUnnecessaryArrows(slideArrowsDraft: SlideArrowsDraft, mod...
  function isAnimatedArrowUnnecessary (line 513) | function isAnimatedArrowUnnecessary(
  function getSlideExceptions (line 537) | function getSlideExceptions(mode: 0 | 1 | 2 | 3): Vec2Key[] {
  function removeTypesThatCantSlideOntoScreenFromLineDraft (line 548) | function removeTypesThatCantSlideOntoScreenFromLineDraft(line: ArrowsLin...
  function calculatePieceArrows (line 567) | function calculatePieceArrows(slideArrowsDraft: SlideArrowsDraft): {
  function convertLineDraftToLine (line 601) | function convertLineDraftToLine(
  function processPiece (line 655) | function processPiece(
  function transitionTowardTargetIfClicked (line 692) | function transitionTowardTargetIfClicked(
  function updateHintArrows (line 751) | function updateHintArrows(): HintArrow[] {

FILE: src/client/scripts/esm/game/rendering/arrows/arrowsgraphics.ts
  constant ARROW_SIZE_RATIO (line 31) | const ARROW_SIZE_RATIO = 0.3;
  constant ATTRIB_INFO_PICTURES (line 34) | const ATTRIB_INFO_PICTURES: AttributeInfoInstanced = {
  constant ATTRIB_INFO_ARROWS (line 46) | const ATTRIB_INFO_ARROWS: AttributeInfoInstanced = {
  function render (line 58) | function render(): void {
  function concatData (line 171) | function concatData(
  function getVertexDataOfArrow (line 215) | function getVertexDataOfArrow(halfWorldWidth: number): number[] {

FILE: src/client/scripts/esm/game/rendering/arrows/arrowshifts.ts
  type Shift (line 40) | type Shift =
  constant ONE (line 64) | const ONE = bd.fromBigInt(1n);
  function reset (line 77) | function reset(): void {
  function deleteArrow (line 85) | function deleteArrow(start: Coords): void {
  function moveArrow (line 95) | function moveArrow(start: Coords, end: Coords): void {
  function animateArrow (line 111) | function animateArrow(start: Coords, end: BDCoords, type: number): void {
  function addArrow (line 121) | function addArrow(type: number, end: Coords): void {
  function overwriteArrows (line 130) | function overwriteArrows(start: Coords): void {
  function executeArrowShifts (line 152) | function executeArrowShifts(): void {
  function recalculateLinesThroughCoords (line 269) | function recalculateLinesThroughCoords(

FILE: src/client/scripts/esm/game/rendering/boarddrag.ts
  type PositionHistoryEntry (line 34) | interface PositionHistoryEntry {
  function isBoardDragging (line 67) | function isBoardDragging(): boolean {
  function getBoardDraggablePointersDown (line 77) | function getBoardDraggablePointersDown(): string[] {
  function getBoardDraggablePointers (line 94) | function getBoardDraggablePointers(): string[] {
  function checkIfBoardPinched (line 110) | function checkIfBoardPinched(): void {
  function checkIfBoardSingleGrabbed (line 153) | function checkIfBoardSingleGrabbed(): void {
  function stealPointer (line 172) | function stealPointer(pointerId: string): void {
  function initSinglePointerDrag (line 180) | function initSinglePointerDrag(pointerId: string): void {
  function initDoublePointerDrag (line 193) | function initDoublePointerDrag(pointerId: string): void {
  function checkIfBoardDropped (line 225) | function checkIfBoardDropped(): void {
  function cancelBoardDrag (line 279) | function cancelBoardDrag(): void {
  function throwBoard (line 292) | function throwBoard(time: number): void {
  function throwScale (line 314) | function throwScale(time: number): void {
  function dragBoard (line 328) | function dragBoard(): void {
  function addCurrentPositionToHistory (line 410) | function addCurrentPositionToHistory(): void {
  function removeOldPositions (line 424) | function removeOldPositions(now: number): void {

FILE: src/client/scripts/esm/game/rendering/boardpos.ts
  constant ZERO (line 25) | const ZERO = bd.fromBigInt(0n);
  constant ONE (line 26) | const ONE = bd.fromBigInt(1n);
  function getBoardPos (line 58) | function getBoardPos(): BDCoords {
  function getBoardScale (line 62) | function getBoardScale(): BigDecimal {
  function getBoardScaleAsNumber (line 74) | function getBoardScaleAsNumber(): number {
  function getPanVel (line 78) | function getPanVel(): DoubleCoords {
  function getRelativePanVelCap (line 82) | function getRelativePanVelCap(): number {
  function getScaleVel (line 86) | function getScaleVel(): number {
  function glimitToDampScale (line 90) | function glimitToDampScale(): number {
  function setBoardPos (line 96) | function setBoardPos(newPos: BDCoords): void {
  function setBoardScale (line 112) | function setBoardScale(newScale: BigDecimal): void {
  function setPanVel (line 127) | function setPanVel(newPanVel: DoubleCoords): void {
  function setScaleVel (line 137) | function setScaleVel(newScaleVel: number): void {
  function eraseMomentum (line 147) | function eraseMomentum(): void {
  function boardHasMomentum (line 152) | function boardHasMomentum(): boolean {
  function areZoomedOut (line 163) | function areZoomedOut(): boolean {
  function isScaleSmallForInvisibleTiles (line 172) | function isScaleSmallForInvisibleTiles(): boolean {
  function update (line 179) | function update(): void {
  function panBoard (line 189) | function panBoard(): void {
  function recalcScale (line 211) | function recalcScale(): void {

FILE: src/client/scripts/esm/game/rendering/boardtiles.ts
  type NoiseTextures (line 43) | type NoiseTextures = { perlinNoise?: WebGLTexture; whiteNoise?: WebGLTex...
  constant ONE (line 54) | const ONE = bd.fromNumber(1.0);
  constant TWO (line 55) | const TWO = bd.fromNumber(2.0);
  constant TEN (line 56) | const TEN = bd.fromNumber(10);
  function init (line 126) | function init(): void {
  function initTextures (line 139) | async function initTextures(): Promise<void> {
  function recalcVariables (line 160) | function recalcVariables(): void {
  function recalcBoundingBox (line 164) | function recalcBoundingBox(): void {
  function getSquareCenter (line 182) | function getSquareCenter(): BigDecimal {
  function getSquareCenterAsNumber (line 186) | function getSquareCenterAsNumber(): number {
  function gtileWidth_Pixels (line 190) | function gtileWidth_Pixels(debugMode = camera.getDebug()): BigDecimal {
  function gboundingBox (line 208) | function gboundingBox(debugMode = camera.getDebug()): BoundingBox {
  function gboundingBoxFloat (line 218) | function gboundingBoxFloat(): BoundingBoxBD {
  function getBoundingBoxOfBoard (line 236) | function getBoundingBoxOfBoard(
  function generatePerspectiveBoundingBox (line 262) | function generatePerspectiveBoundingBox(rangeOfView: number): BoundingBo...
  function roundAwayBoundingBox (line 284) | function roundAwayBoundingBox(src: BoundingBoxBD): BoundingBox {
  function updateTheme (line 297) | function updateTheme(): void {
  function resetColor (line 306) | function resetColor(
  function updateSkyColor (line 317) | function updateSkyColor(): void {
  function updateNavColor (line 339) | function updateNavColor(): void {
  function darkenColor (line 370) | function darkenColor(): void {
  function render (line 388) | function render(noiseTextures?: NoiseTextures, uniforms?: Record<string,...
  function renderSolidCover (line 398) | function renderSolidCover(): void {
  function renderFractalBoards (line 417) | function renderFractalBoards(noiseTextures?: NoiseTextures, uniforms?: R...
  function getRelativeZ (line 466) | function getRelativeZ(): number {
  function generateBoardModel (line 476) | function generateBoardModel(

FILE: src/client/scripts/esm/game/rendering/border.ts
  function drawPlayableRegionMask (line 25) | function drawPlayableRegionMask(worldBorder: UnboundedRectangle | undefi...

FILE: src/client/scripts/esm/game/rendering/camera.ts
  type Mat4 (line 33) | type Mat4 = Float32Array;
  constant DEBUG (line 36) | let DEBUG: boolean = false;
  function getPosition (line 80) | function getPosition(ignoreDevmode?: boolean): Vec3 {
  function getZFar (line 84) | function getZFar(): number {
  function getCanvasWidthVirtualPixels (line 88) | function getCanvasWidthVirtualPixels(): number {
  function getCanvasHeightVirtualPixels (line 92) | function getCanvasHeightVirtualPixels(): number {
  function toggleDebug (line 96) | function toggleDebug(): void {
  function getDebug (line 104) | function getDebug(): boolean {
  function getScreenBoundingBox (line 115) | function getScreenBoundingBox(debugMode: boolean = DEBUG, pad: boolean =...
  function getRespectiveScreenBox (line 143) | function getRespectiveScreenBox(): DoubleBoundingBox {
  function getPerspectiveScreenBox (line 149) | function getPerspectiveScreenBox(): DoubleBoundingBox {
  function getScreenHeightWorld (line 160) | function getScreenHeightWorld(debugMode: boolean = DEBUG): number {
  function getViewMatrix (line 169) | function getViewMatrix(): Mat4 {
  function getProjAndViewMatrixes (line 176) | function getProjAndViewMatrixes(): { projMatrix: Mat4; viewMatrix: Mat4 } {
  function init (line 184) | function init(): void {
  function initMatrixes (line 192) | function initMatrixes(): void {
  function initPerspective (line 204) | function initPerspective(): void {
  function updateCanvasDimensions (line 209) | function updateCanvasDimensions(): void {
  function recalcCanvasVariables (line 228) | function recalcCanvasVariables(): void {
  function setViewMatrix (line 236) | function setViewMatrix(newMatrix: Mat4): void {
  function initViewMatrix (line 241) | function initViewMatrix(ignoreRotations?: boolean): void {
  function initProjMatrix (line 264) | function initProjMatrix(): void {
  function initScreenBoundingBox (line 272) | function initScreenBoundingBox(): void {
  function onScreenResize (line 306) | function onScreenResize(): void {
  function initFOV (line 318) | function initFOV(): void {
  function onFOVChange (line 322) | function onFOVChange(): void {
  function onPositionChange (line 331) | function onPositionChange(): void {
  function getScaleWhenTilesInvisible (line 338) | function getScaleWhenTilesInvisible(): BigDecimal {
  function getScaleWhenZoomedOut (line 347) | function getScaleWhenZoomedOut(): BigDecimal {

FILE: src/client/scripts/esm/game/rendering/coordinates.ts
  constant LABEL_SIZE_PX (line 42) | const LABEL_SIZE_PX = 24;
  constant LABEL_SHRINK (line 47) | const LABEL_SHRINK = {
  constant LABEL_PADDING_PX (line 62) | const LABEL_PADDING_PX = 5;
  constant LABEL_COLOR (line 64) | const LABEL_COLOR: Color = [0, 0, 0, 0.65];
  constant MAX_FULL_DISPLAY_LENGTH (line 66) | const MAX_FULL_DISPLAY_LENGTH = 7;
  constant LABEL_GAP_SIZE (line 68) | const LABEL_GAP_SIZE = 0.4;
  constant LABEL_ARROW_PADDING_PX (line 73) | const LABEL_ARROW_PADDING_PX = 6;
  constant DEBUG_RENDER_LABEL_BOUNDS (line 76) | const DEBUG_RENDER_LABEL_BOUNDS = false;
  function calcLabelSizePx (line 81) | function calcLabelSizePx(): number {
  function formatCoord (line 95) | function formatCoord(coord: bigint): string {
  function computeStep (line 106) | function computeStep(threshold: number, scale: BigDecimal): bigint {
  function ceilToMultiple (line 123) | function ceilToMultiple(n: bigint, multiple: bigint): bigint {
  function render (line 131) | function render(): void {
  function renderLabel (line 227) | function renderLabel(
  function renderLabelBoundsOutline (line 256) | function renderLabelBoundsOutline(labelBounds: DoubleBoundingBox): void {

FILE: src/client/scripts/esm/game/rendering/dragging/draganimation.ts
  function areDraggingPiece (line 109) | function areDraggingPiece(): boolean {
  function setForceRankFileOutline (line 114) | function setForceRankFileOutline(value: boolean): void {
  function getDragParity (line 119) | function getDragParity(): boolean {
  function pickUpPiece (line 128) | function pickUpPiece(piece: Piece, resetParity: boolean): void {
  function updateDragLocation (line 150) | function updateDragLocation(): void {
  function setDragLocationAndHoverSquare (line 175) | function setDragLocationAndHoverSquare(worldLoc: DoubleCoords, hoverSqua...
  function getPointerIdDraggingPiece (line 181) | function getPointerIdDraggingPiece(): string | undefined {
  function getHoveredCoords (line 191) | function getHoveredCoords(): Coords | undefined {
  function hasPointerReleased (line 196) | function hasPointerReleased(): boolean {
  function dropPiece (line 211) | function dropPiece(): void {
  function cancelDragging (line 232) | function cancelDragging(): void {
  function renderTransparentSquare (line 240) | function renderTransparentSquare(): void {
  function renderPiece (line 249) | function renderPiece(): void {
  function renderPieceModel (line 257) | function renderPieceModel(): void {
  function renderOutline (line 309) | function renderOutline(): void {

FILE: src/client/scripts/esm/game/rendering/dragging/dragarrows.ts
  type CandidateArrow (line 59) | interface CandidateArrow {
  constant CANDIDATE_ANIM (line 73) | const CANDIDATE_ANIM = {
  constant SLIDE_ZONE_WIDTH (line 85) | const SLIDE_ZONE_WIDTH = 1.7;
  constant SLIDE_ZONE_GRADIENT (line 87) | const SLIDE_ZONE_GRADIENT = {
  function update (line 122) | function update(): void {
  function updateActiveDrag (line 150) | function updateActiveDrag(): void {
  function updateCandidate (line 173) | function updateCandidate(): void {
  function detectCandidateArrow (line 206) | function detectCandidateArrow(): void {
  function findCandidateHoveredArrow (line 249) | function findCandidateHoveredArrow(): HoveredArrow | undefined {
  function manageActiveDrag (line 265) | function manageActiveDrag(mouseWorld: DoubleCoords): void {
  function updateSlideZoneDrag (line 292) | function updateSlideZoneDrag(mouseWorld: DoubleCoords): void {
  function updateOnScreenDrag (line 326) | function updateOnScreenDrag(): void {
  function reset (line 337) | function reset(): void {
  function render (line 349) | function render(): void {
  function renderCandidateArrows (line 360) | function renderCandidateArrows(): void {
  function renderSlideZone (line 429) | function renderSlideZone(): void {
  function renderRadialGradient (line 473) | function renderRadialGradient(colors: Color[], spacing: number, phase: n...
  function renderSlideMoveHighlights (line 488) | function renderSlideMoveHighlights(): void {

FILE: src/client/scripts/esm/game/rendering/dragging/droparrows.ts
  constant LEGAL_CAPTURE_PULSATE (line 28) | const LEGAL_CAPTURE_PULSATE = {
  function updateCapturedPiece (line 49) | function updateCapturedPiece(): void {
  function getCaptureCoords (line 96) | function getCaptureCoords(): Coords | undefined {
  function shiftArrows (line 105) | function shiftArrows(): void {
  function updateLegalCaptureArrows (line 137) | function updateLegalCaptureArrows(): void {
  function onDragTermination (line 182) | function onDragTermination(): void {

FILE: src/client/scripts/esm/game/rendering/effect_zone/EffectZoneManager.ts
  type EffectZone (line 25) | interface EffectZone {
  type ZoneName (line 38) | type ZoneName = (typeof EffectZoneManager.ZONES)[number]['name'];
  type Zone (line 44) | interface Zone {
  class EffectZoneManager (line 63) | class EffectZoneManager {
    method constructor (line 118) | constructor(gl: WebGL2RenderingContext, programManager: ProgramManager) {
    method findZoneForDistance (line 176) | private findZoneForDistance(distance: bigint): Zone {
    method update (line 198) | public update(distanceFromOrigin: bigint): void {
    method renderBoard (line 281) | public renderBoard(): void {
    method getActivePostProcessPasses (line 314) | public getActivePostProcessPasses(): PostProcessPass[] {

FILE: src/client/scripts/esm/game/rendering/effect_zone/zones/AshfallVocsZone.ts
  class AshfallVocsZone (line 13) | class AshfallVocsZone implements Zone {
    method constructor (line 27) | constructor(programManager: ProgramManager, noise: Promise<WebGLTextur...
    method update (line 82) | public update(): void {
    method getUniforms (line 87) | public getUniforms(): Record<string, any> {
    method getPasses (line 91) | public getPasses(): PostProcessPass[] {
    method fadeInAmbience (line 97) | public fadeInAmbience(transitionDurationMillis: number): void {
    method fadeOutAmbience (line 101) | public fadeOutAmbience(transitionDurationMillis: number): void {

FILE: src/client/scripts/esm/game/rendering/effect_zone/zones/ContortionFieldZone.ts
  class ContortionFieldZone (line 12) | class ContortionFieldZone implements Zone {
    method constructor (line 28) | constructor(programManager: ProgramManager) {
    method update (line 37) | public update(): void {
    method getUniforms (line 44) | public getUniforms(): Record<string, any> {
    method getPasses (line 48) | public getPasses(): PostProcessPass[] {
    method fadeInAmbience (line 52) | public fadeInAmbience(transitionDurationMillis: number): void {
    method fadeOutAmbience (line 56) | public fadeOutAmbience(transitionDurationMillis: number): void {

FILE: src/client/scripts/esm/game/rendering/effect_zone/zones/DustyWastesZone.ts
  class DustyWastesZone (line 12) | class DustyWastesZone implements Zone {
    method constructor (line 78) | constructor(programManager: ProgramManager) {
    method update (line 169) | public update(): void {
    method randomizeNextGlitchTimer (line 230) | private randomizeNextGlitchTimer(): void {
    method getUniforms (line 235) | public getUniforms(): Record<string, any> {
    method getPasses (line 245) | public getPasses(): PostProcessPass[] {
    method fadeInAmbience (line 249) | public fadeInAmbience(transitionDurationMillis: number): void {
    method fadeOutAmbience (line 253) | public fadeOutAmbience(transitionDurationMillis: number): void {

FILE: src/client/scripts/esm/game/rendering/effect_zone/zones/EchoRiftZone.ts
  class EchoRiftZone (line 15) | class EchoRiftZone implements Zone {
    method constructor (line 45) | constructor(programManager: ProgramManager) {
    method update (line 105) | public update(): void {
    method getUniforms (line 126) | public getUniforms(): Record<string, any> {
    method getPasses (line 130) | public getPasses(): PostProcessPass[] {
    method fadeInAmbience (line 135) | public fadeInAmbience(transitionDurationMillis: number): void {
    method fadeOutAmbience (line 140) | public fadeOutAmbience(transitionDurationMillis: number): void {

FILE: src/client/scripts/esm/game/rendering/effect_zone/zones/EmberVergeZone.ts
  class EmberVergeZone (line 10) | class EmberVergeZone implements Zone {
    method constructor (line 49) | constructor() {
    method update (line 53) | public update(): void {
    method getUniforms (line 62) | public getUniforms(): Record<string, any> {
    method getPasses (line 89) | public getPasses(): PostProcessPass[] {
    method fadeInAmbience (line 93) | public fadeInAmbience(transitionDurationMillis: number): void {
    method fadeOutAmbience (line 97) | public fadeOutAmbience(transitionDurationMillis: number): void {

FILE: src/client/scripts/esm/game/rendering/effect_zone/zones/IridescenceZone.ts
  class IridescenceZone (line 10) | class IridescenceZone implements Zone {
    method constructor (line 49) | constructor() {
    method update (line 61) | public update(): void {
    method getUniforms (line 70) | public getUniforms(): Record<string, any> {
    method getPasses (line 97) | public getPasses(): PostProcessPass[] {
    method fadeInAmbience (line 101) | public fadeInAmbience(transitionDurationMillis: number): void {
    method fadeOutAmbience (line 105) | public fadeOutAmbience(transitionDurationMillis: number): void {

FILE: src/client/scripts/esm/game/rendering/effect_zone/zones/OceanZone.ts
  class OceanZone (line 14) | class OceanZone implements Zone {
    method constructor (line 43) | constructor(programManager: ProgramManager) {
    method update (line 66) | public update(): void {
    method getUniforms (line 107) | public getUniforms(): Record<string, any> {
    method getPasses (line 113) | public getPasses(): PostProcessPass[] {
    method fadeInAmbience (line 118) | public fadeInAmbience(transitionDurationMillis: number): void {
    method fadeOutAmbience (line 122) | public fadeOutAmbience(transitionDurationMillis: number): void {

FILE: src/client/scripts/esm/game/rendering/effect_zone/zones/SpectralEdgeZone.ts
  class SpectralEdgeZone (line 11) | class SpectralEdgeZone implements Zone {
    method constructor (line 50) | constructor() {
    method update (line 74) | public update(): void {
    method getUniforms (line 83) | public getUniforms(): Record<string, any> {
    method getPasses (line 110) | public getPasses(): PostProcessPass[] {
    method fadeInAmbience (line 114) | public fadeInAmbience(transitionDurationMillis: number): void {
    method fadeOutAmbience (line 118) | public fadeOutAmbience(transitionDurationMillis: number): void {

FILE: src/client/scripts/esm/game/rendering/effect_zone/zones/StaticZone.ts
  class StaticZone (line 11) | class StaticZone implements Zone {
    method constructor (line 38) | constructor(programManager: ProgramManager) {
    method update (line 77) | public update(): void {
    method getUniforms (line 90) | public getUniforms(): Record<string, any> {
    method getPasses (line 99) | public getPasses(): PostProcessPass[] {
    method fadeInAmbience (line 103) | public fadeInAmbience(transitionDurationMillis: number): void {
    method fadeOutAmbience (line 108) | public fadeOutAmbience(transitionDurationMillis: number): void {

FILE: src/client/scripts/esm/game/rendering/effect_zone/zones/TheBeginningZone.ts
  class TheBeginningZone (line 6) | class TheBeginningZone implements Zone {
    method update (line 10) | public update(): void {
    method getUniforms (line 14) | public getUniforms(): Record<string, any> {
    method getPasses (line 18) | public getPasses(): PostProcessPass[] {
    method fadeInAmbience (line 22) | public fadeInAmbience(_transitionDurationMillis: number): void {}
    method fadeOutAmbience (line 24) | public fadeOutAmbience(_transitionDurationMillis: number): void {}

FILE: src/client/scripts/esm/game/rendering/effect_zone/zones/UndercurrentZone.ts
  class UndercurrentZone (line 15) | class UndercurrentZone implements Zone {
    method constructor (line 22) | constructor() {
    method update (line 29) | public update(): void {
    method getUniforms (line 33) | public getUniforms(): Record<string, any> {
    method getPasses (line 37) | public getPasses(): PostProcessPass[] {
    method fadeInAmbience (line 41) | public fadeInAmbience(transitionDurationMillis: number): void {
    method fadeOutAmbience (line 45) | public fadeOutAmbience(transitionDurationMillis: number): void {

FILE: src/client/scripts/esm/game/rendering/frameratelimiter.ts
  constant TARGET_FPS_TITLE_SCREEN (line 19) | const TARGET_FPS_TITLE_SCREEN = 30;
  function requestFrame (line 43) | function requestFrame(callback: FrameRequestCallback): void {

FILE: src/client/scripts/esm/game/rendering/frametracker.ts
  function onVisualChange (line 15) | function onVisualChange(): void {
  function doWeRenderNextFrame (line 21) | function doWeRenderNextFrame(): boolean {
  function onFrameRender (line 29) | function onFrameRender(): void {

FILE: src/client/scripts/esm/game/rendering/gl-matrix.js
  function create$5 (line 54) | function create$5() {
  function clone$5 (line 85) | function clone$5(a) {
  function copy$5 (line 113) | function copy$5(out, a) {
  function fromValues$5 (line 154) | function fromValues$5(
  function set$5 (line 214) | function set$5(
  function identity$2 (line 258) | function identity$2(out) {
  function transpose (line 285) | function transpose(out, a) {
  function invert$2 (line 335) | function invert$2(out, a) {
  function adjoint (line 398) | function adjoint(out, a) {
  function determinant (line 452) | function determinant(a) {
  function multiply$5 (line 491) | function multiply$5(out, a, b) {
  function translate$1 (line 552) | function translate$1(out, a, v) {
  function scale$5 (line 607) | function scale$5(out, a, v) {
  function rotate$1 (line 639) | function rotate$1(out, a, rad, axis) {
  function rotateX$3 (line 718) | function rotateX$3(out, a, rad) {
  function rotateY$3 (line 761) | function rotateY$3(out, a, rad) {
  function rotateZ$3 (line 804) | function rotateZ$3(out, a, rad) {
  function fromTranslation$1 (line 850) | function fromTranslation$1(out, v) {
  function fromScaling (line 881) | function fromScaling(out, v) {
  function fromRotation$1 (line 913) | function fromRotation$1(out, rad, axis) {
  function fromXRotation (line 962) | function fromXRotation(out, rad) {
  function fromYRotation (line 996) | function fromYRotation(out, rad) {
  function fromZRotation (line 1030) | function fromZRotation(out, rad) {
  function fromRotationTranslation$1 (line 1068) | function fromRotationTranslation$1(out, q, v) {
  function fromQuat2 (line 1112) | function fromQuat2(out, a) {
  function getTranslation$1 (line 1147) | function getTranslation$1(out, mat) {
  function getScaling (line 1164) | function getScaling(out, mat) {
  function getRotation (line 1189) | function getRotation(out, mat) {
  function decompose (line 1245) | function decompose(out_r, out_t, out_s, mat) {
  function fromRotationTranslationScale (line 1322) | function fromRotationTranslationScale(out, q, v, s) {
  function fromRotationTranslationScaleOrigin (line 1382) | function fromRotationTranslationScaleOrigin(out, q, v, s, o) {
  function fromQuat (line 1442) | function fromQuat(out, q) {
  function frustum (line 1490) | function frustum(out, left, right, bottom, top, near, far) {
  function perspectiveNO (line 1526) | function perspectiveNO(out, fovy, aspect, near, far) {
  function perspectiveZO (line 1574) | function perspectiveZO(out, fovy, aspect, near, far) {
  function perspectiveFromFieldOfView (line 1614) | function perspectiveFromFieldOfView(out, fov, near, far) {
  function orthoNO (line 1654) | function orthoNO(out, left, right, bottom, top, near, far) {
  function orthoZO (line 1697) | function orthoZO(out, left, right, bottom, top, near, far) {
  function lookAt (line 1730) | function lookAt(out, eye, center, up) {
  function targetTo (line 1817) | function targetTo(out, eye, target, up) {
  function str$5 (line 1873) | function str$5(a) {
  function frob (line 1884) | function frob(a) {
  function add$5 (line 1897) | function add$5(out, a, b) {
  function subtract$3 (line 1925) | function subtract$3(out, a, b) {
  function multiplyScalar (line 1953) | function multiplyScalar(out, a, b) {
  function multiplyScalarAndAdd (line 1982) | function multiplyScalarAndAdd(out, a, b, scale) {
  function exactEquals$5 (line 2010) | function exactEquals$5(a, b) {
  function equals$5 (line 2022) | function equals$5(a, b) {

FILE: src/client/scripts/esm/game/rendering/highlights/annotations/annotations.ts
  type Annotes (line 30) | interface Annotes {
  type Square (line 42) | type Square = Coords;
  type Arrow (line 45) | interface Arrow {
  function getSquares (line 83) | function getSquares(): Coords[] {
  function getArrows (line 88) | function getArrows(): Arrow[] {
  function getRays (line 93) | function getRays(): Ray[] {
  function getRelevantAnnotes (line 104) | function getRelevantAnnotes(): Annotes {
  function getEmptyAnnotes (line 132) | function getEmptyAnnotes(): Annotes {
  function clearAnnotes (line 137) | function clearAnnotes(annotes: Annotes): void {
  function update (line 146) | function update(): void {
  function Collapse (line 167) | function Collapse(): void {
  function resetState (line 186) | function resetState(): void {
  function render_belowPieces (line 198) | function render_belowPieces(): void {
  function render_abovePieces (line 204) | function render_abovePieces(): void {

FILE: src/client/scripts/esm/game/rendering/highlights/annotations/drawarrows.ts
  constant ARROW (line 39) | const ARROW = {
  constant ZERO (line 59) | const ZERO = bd.fromBigInt(0n);
  constant ONE (line 60) | const ONE = bd.fromBigInt(1n);
  function update (line 77) | function update(arrows: Arrow[]): void {
  function stopDrawing (line 119) | function stopDrawing(): void {
  function stealPointer (line 126) | function stealPointer(pointerIdToSteal: string): void {
  function addDrawnArrow (line 137) | function addDrawnArrow(arrows: Arrow[]): { changed: boolean; deletedArro...
  function render (line 197) | function render(arrows: Arrow[]): void {
  function getDataArrow (line 226) | function getDataArrow(arrow: Arrow, color: Color): number[] {

FILE: src/client/scripts/esm/game/rendering/highlights/annotations/drawrays.ts
  constant PRESET_RAY_COLOR (line 37) | const PRESET_RAY_COLOR: Color = [1, 0.2, 0, 0.24];
  function areDrawing (line 55) | function areDrawing(): boolean {
  function getPresetRays (line 60) | function getPresetRays(): Ray[] {
  function update (line 80) | function update(rays: Ray[]): void {
  function getPointerId (line 142) | function getPointerId(): string {
  function stopDrawing (line 150) | function stopDrawing(): void {
  function stealPointer (line 157) | function stealPointer(pointerIdToSteal: string): void {
  function getLines (line 163) | function getLines(rays: Ray[], color: Color): Line[] {
  function addDrawnRay (line 205) | function addDrawnRay(rays: Ray[]): { added: boolean; deletedRays?: Ray[]...
  function findClosestPredefinedVector (line 284) | function findClosestPredefinedVector(targetVector: BDCoords, searchHippo...
  function collapseRays (line 341) | function collapseRays(rays_drawn: Ray[], trimDecimals: boolean): BDCoord...
  function dispatchRayCountEvent (line 411) | function dispatchRayCountEvent(rays: Ray[]): void {
  function setPresetOverrides (line 419) | function setPresetOverrides(prs: BaseRay[]): void {
  function getPresetOverrides (line 426) | function getPresetOverrides(): BaseRay[] | undefined {
  function clearPresetOverrides (line 431) | function clearPresetOverrides(): void {
  function render (line 438) | function render(rays: Ray[]): void {
  function genAndRenderRays (line 457) | function genAndRenderRays(rays: Ray[], color: Color): void {

FILE: src/client/scripts/esm/game/rendering/highlights/annotations/drawsquares.ts
  constant PRESET_SQUARE_COLOR (line 31) | const PRESET_SQUARE_COLOR: Color = [1, 0.2, 0, 0.24];
  constant OPACITY_OFFSET (line 37) | const OPACITY_OFFSET = 0.08;
  constant HOVER_OPACITY (line 40) | const HOVER_OPACITY = 0.5;
  function getAllSquaresHovered (line 53) | function getAllSquaresHovered(highlights: Square[]): Coords[] {
  function getSquaresBelowWorld (line 68) | function getSquaresBelowWorld(
  function update (line 99) | function update(highlights: Square[]): void {
  function setPresetOverrides (line 149) | function setPresetOverrides(pss: Coords[]): void {
  function getPresetOverrides (line 158) | function getPresetOverrides(): Coords[] | undefined {
  function clearPresetOverrides (line 163) | function clearPresetOverrides(): void {
  function render (line 169) | function render(highlights: Square[]): void {

FILE: src/client/scripts/esm/game/rendering/highlights/checkhighlight.ts
  function render (line 26) | function render(boardsim: Board): void {
  function genCheckHighlightModel (line 37) | function genCheckHighlightModel(royalsInCheck: Coords[]): Renderable {

FILE: src/client/scripts/esm/game/rendering/highlights/highlightline.ts
  type Line (line 27) | interface Line {
  function getRenderRange (line 47) | function getRenderRange(): BoundingBoxBD {
  function genLinesModel (line 70) | function genLinesModel(lines: Line[]): Renderable {
  function getLineData (line 75) | function getLineData(line: Line): number[] {

FILE: src/client/scripts/esm/game/rendering/highlights/highlights.ts
  function render (line 36) | function render(boardsim: Board): void {
  function highlightLastMove (line 52) | function highlightLastMove(boardsim: Board): void {

FILE: src/client/scripts/esm/game/rendering/highlights/legalmovehighlights.ts
  function render (line 88) | function render(): void {
  function regenerateAll (line 107) | function regenerateAll(): void {
  function regenSelectedPieceLegalMovesHighlightsModel (line 113) | function regenSelectedPieceLegalMovesHighlightsModel(): void {
  function renderSelectedPieceLegalMoves (line 156) | function renderSelectedPieceLegalMoves(): void {

FILE: src/client/scripts/esm/game/rendering/highlights/legalmovemodel.ts
  type RayIterationInfo (line 56) | type RayIterationInfo = {
  constant ATTRIB_INFO (line 68) | const ATTRIB_INFO: AttributeInfoInstanced = {
  constant PERSPECTIVE_VIEW_RANGE (line 97) | const PERSPECTIVE_VIEW_RANGE = 1000;
  constant ZERO (line 105) | const ZERO: BigDecimal = bd.fromBigInt(0n);
  function getOffset (line 135) | function getOffset(): Coords {
  function updateRenderRange (line 144) | function updateRenderRange(): boolean {
  function isViewRangeContainedInRenderRange (line 184) | function isViewRangeContainedInRenderRange(): boolean {
  function getBoundingBoxOfPerspectiveView (line 213) | function getBoundingBoxOfPerspectiveView(): BoundingBoxBD {
  function getDimensionsOfPerspectiveViewRange (line 225) | function getDimensionsOfPerspectiveViewRange(): DoubleCoords {
  function getDimensionsOfOrthographicViewRange (line 232) | function getDimensionsOfOrthographicViewRange(): DoubleCoords {
  function generateModelsForPiecesLegalMoveHighlights (line 258) | function generateModelsForPiecesLegalMoveHighlights(
  function generateModelForSlideHighlightOutlines (line 320) | function generateModelForSlideHighlightOutlines(
  function pushIndividual (line 353) | function pushIndividual(
  function pushSliding (line 382) | function pushSliding(
  function pushSlide (line 425) | function pushSlide(
  function pushRay (line 503) | function pushRay(
  function getRayIterationInfo (line 561) | function getRayIterationInfo(
  function genModelForRays (line 656) | function genModelForRays(rays: Ray[], color: Color): RenderableInstanced {
  function renderOutlineOfRenderBox (line 701) | function renderOutlineOfRenderBox(): void {
  function renderOutlineofFloatingBox (line 713) | function renderOutlineofFloatingBox(box: BoundingBoxBD): void {

FILE: src/client/scripts/esm/game/rendering/highlights/movehints.ts
  function updateIndividualMoves (line 55) | function updateIndividualMoves(legalMoves: LegalMoves): void {
  function clearIndividualMoves (line 77) | function clearIndividualMoves(): void {
  function getPieceCoords (line 85) | function getPieceCoords(): Coords | undefined {
  function getSquares (line 90) | function getSquares(): Coords[] {
  function render (line 97) | function render(): void {

FILE: src/client/scripts/esm/game/rendering/highlights/selectedpiecehighlightline.ts
  function getLines (line 31) | function getLines(): Line[] {
  type Segment (line 95) | type Segment = {
  function getLineComponents (line 107) | function getLineComponents(): { rays: Ray[]; segments: Segment[] } {
  function render (line 160) | function render(): void {

FILE: src/client/scripts/esm/game/rendering/highlights/snapping.ts
  constant ENTITY_WIDTH_VPIXELS (line 49) | const ENTITY_WIDTH_VPIXELS = 40;
  constant SNAP_LINE_COLOR (line 52) | const SNAP_LINE_COLOR = [0, 0, 1, 0.3] as const;
  constant GLOW_DOT (line 55) | const GLOW_DOT = {
  constant GHOST_IMAGE_OPACITY (line 64) | const GHOST_IMAGE_OPACITY = 1;
  constant THRESHOLD_TO_SNAP_PIECES (line 70) | const THRESHOLD_TO_SNAP_PIECES = 5_000;
  type Snap (line 72) | type Snap = {
  function getEntityWidthWorld (line 90) | function getEntityWidthWorld(): number {
  function getAllEntitiesWorldHovers (line 94) | function getAllEntitiesWorldHovers(world: DoubleCoords): Coords[] {
  type ClosestEntity (line 101) | type ClosestEntity = {
  function getClosestEntityToWorld (line 111) | function getClosestEntityToWorld(world: DoubleCoords): ClosestEntity | u...
  function teleportToEntitiesIfClicked (line 144) | function teleportToEntitiesIfClicked(): void {
  function isSnappingEnabledThisFrame (line 170) | function isSnappingEnabledThisFrame(): boolean {
  function getWorldSnapCoords (line 179) | function getWorldSnapCoords(world: DoubleCoords): Coords | undefined {
  type LineSnapPoint (line 187) | type LineSnapPoint = {
  function snapPointerWorld (line 198) | function snapPointerWorld(world: DoubleCoords): Snap | undefined {
  function teleportToSnapIfClicked (line 384) | function teleportToSnapIfClicked(): void {
  function findClosestEntityOfGroup (line 405) | function findClosestEntityOfGroup(
  function getAllLinesSegmented (line 462) | function getAllLinesSegmented(drawnRays: Ray[], presetRays: Ray[]): Line...
  function getAnnoteSnapPoints (line 485) | function getAnnoteSnapPoints(trimDecimals: boolean): BDCoords[] {
  function render (line 498) | function render(): void {
  function generateGhostImageModel (line 560) | function generateGhostImageModel(type: number, coords: DoubleCoords): Re...

FILE: src/client/scripts/esm/game/rendering/highlights/specialrighthighlights.ts
  constant SPECIAL_RIGHTS_COLOR (line 29) | const SPECIAL_RIGHTS_COLOR: Color = [0, 1, 0.5, 0.3];
  constant ENPASSANT_COLOR (line 31) | const ENPASSANT_COLOR: Color = [0.5, 0, 1, 0.3];
  function enable (line 52) | function enable(): void {
  function disable (line 58) | function disable(): void {
  function toggle (line 63) | function toggle(): void {
  function render (line 70) | function render(): void {
  function regenModel (line 77) | function regenModel(): void {
  function renderSpecialRights (line 102) | function renderSpecialRights(): void {
  function renderEnPassant (line 110) | function renderEnPassant(): void {

FILE: src/client/scripts/esm/game/rendering/highlights/squarerendering.ts
  function genModel (line 29) | function genModel(highlights: Coords[], color: Color): RenderableInstanc...

FILE: src/client/scripts/esm/game/rendering/instancedshapes.ts
  constant DOTS (line 27) | const DOTS = {
  constant CORNER_TRIS (line 44) | const CORNER_TRIS = {
  constant BOX_OUTLINE (line 56) | const BOX_OUTLINE = {
  constant PLUS_SIGN (line 65) | const PLUS_SIGN = {
  function getDataLegalMoveSquare (line 83) | function getDataLegalMoveSquare(color: Color): number[] {
  function getDataLegalMoveDot (line 95) | function getDataLegalMoveDot(color: Color): number[] {
  function getDataLegalMoveCornerTris (line 115) | function getDataLegalMoveCornerTris(color: [number, number, number, numb...
  function getDataPlusSign (line 152) | function getDataPlusSign(color: Color): number[] {
  function getDataBoxOutline (line 240) | function getDataBoxOutline(): number[] {
  function getDataTexture (line 329) | function getDataTexture(inverted: boolean): number[] {
  function getDataColoredTexture (line 342) | function getDataColoredTexture(color: Color, inverted: boolean): number[] {

FILE: src/client/scripts/esm/game/rendering/meshes.ts
  constant ONE (line 36) | const ONE = bd.fromBigInt(1n);
  function getCoordBoxModel (line 44) | function getCoordBoxModel(coords: DoubleCoords): DoubleBoundingBox {
  function getCoordBoxWorld (line 56) | function getCoordBoxWorld(coords: Coords): DoubleBoundingBox {
  function expandTileBoundingBoxToEncompassWholeSquare (line 89) | function expandTileBoundingBoxToEncompassWholeSquare(boundingBox: Boundi...
  function expandTileBoundingBoxToEncompassWholeSquareBD (line 97) | function expandTileBoundingBoxToEncompassWholeSquareBD(boundingBox: Boun...
  function applyWorldTransformationsToBoundingBox (line 115) | function applyWorldTransformationsToBoundingBox(boundingBox: BoundingBox...
  function QuadModel_Color (line 132) | function QuadModel_Color(coords: DoubleCoords, color: Color): number[] {
  function QuadWorld_Color (line 140) | function QuadWorld_Color(coords: Coords, color: Color): number[] {
  function QuadWorld_ColorTexture (line 148) | function QuadWorld_ColorTexture(coords: Coords, color: Color): number[] {
  function getPieceTexCoords (line 161) | function getPieceTexCoords(): {
  function RectWorld (line 179) | function RectWorld(boundingBox: BoundingBox, color: Color): number[] {
  function getBoardRenderTransform (line 218) | function getBoardRenderTransform(offset: Coords, z: number = 0): { posit...
  function getModelPosition (line 234) | function getModelPosition(boardPos: BDCoords, modelOffset: Coords, z: nu...

FILE: src/client/scripts/esm/game/rendering/miniimage.ts
  constant MINI_IMAGE_OPACITY (line 55) | const MINI_IMAGE_OPACITY: number = 0.6;
  constant MAX_ANIM_DIST_VPIXELS (line 57) | const MAX_ANIM_DIST_VPIXELS = bd.fromBigInt(2300n);
  function isDisabled (line 81) | function isDisabled(): boolean {
  function enable (line 85) | function enable(): void {
  function disable (line 89) | function disable(): void {
  function toggle (line 93) | function toggle(): void {
  function forEachRenderablePiece (line 104) | function forEachRenderablePiece(callback: (_coords: BDCoords, _type: num...
  function getImageInstanceData (line 153) | function getImageInstanceData(): {
  function getImagesBelowWorld (line 206) | function getImagesBelowWorld(
  function getAllPiecesBelowAnnotePoints (line 246) | function getAllPiecesBelowAnnotePoints(): Piece[] {
  function render (line 352) | function render(): void {

FILE: src/client/scripts/esm/game/rendering/perspective.ts
  function getEnabled (line 46) | function getEnabled(): boolean {
  function getRotX (line 49) | function getRotX(): number {
  function getRotZ (line 52) | function getRotZ(): number {
  function getIsViewingBlackPerspective (line 55) | function getIsViewingBlackPerspective(): boolean {
  function toggle (line 59) | function toggle(): void {
  function enable (line 67) | function enable(): void {
  function disable (line 83) | function disable(): void {
  function resetRotations (line 100) | function resetRotations(viewWhitePerspective = true): void {
  function relockMouse (line 110) | function relockMouse(): void {
  function lockMouse (line 119) | function lockMouse(): void {
  function update (line 125) | function update(): void {
  function applyRotations (line 155) | function applyRotations(viewMatrix: Mat4): void {
  function haveZeroRotation (line 179) | function haveZeroRotation(): boolean {
  function isLookingUp (line 184) | function isLookingUp(): boolean {
  function capRotations (line 189) | function capRotations(): void {
  function isMouseLocked (line 196) | function isMouseLocked(): boolean {
  function initCrosshairModel (line 201) | function initCrosshairModel(): void {
  function renderCrosshair (line 225) | function renderCrosshair(): void {
  function renderWithoutPerspectiveRotations (line 242) | function renderWithoutPerspectiveRotations(func: Function): void {
  function unlockMouse (line 254) | function unlockMouse(): void {
  function updateIsViewingBlackPerspective (line 259) | function updateIsViewingBlackPerspective(): void {

FILE: src/client/scripts/esm/game/rendering/piecemodels.ts
  type InstanceData (line 42) | type InstanceData = (bigint | null)[];
  type MeshData (line 45) | interface MeshData {
  type Mesh (line 53) | interface Mesh {
  constant REGEN_RANGE (line 80) | const REGEN_RANGE = 10_000n;
  constant STRIDE_PER_PIECE (line 89) | const STRIDE_PER_PIECE = 2;
  constant ATTRIBUTE_INFO (line 92) | const ATTRIBUTE_INFO: AttributeInfoInstanced = {
  function regenAll (line 108) | function regenAll(boardsim: Board, mesh: Mesh | undefined): void {
  function regenType (line 139) | function regenType(boardsim: Board, mesh: Mesh, type: number): void {
  function genTypeModel (line 158) | function genTypeModel(boardsim: Board, mesh: Mesh, type: number): MeshDa...
  function genVoidModel (line 182) | function genVoidModel(boardsim: Board, mesh: Mesh, type: number): MeshDa...
  function getInstanceDataForTypeRange (line 205) | function getInstanceDataForTypeRange(boardsim: Board, mesh: Mesh, type: ...
  function castInstanceDataToFloat32 (line 237) | function castInstanceDataToFloat32(instanceData: InstanceData): Float32A...
  function castBigIntArrayToFloat32 (line 265) | function castBigIntArrayToFloat32(instanceData: bigint[]): Float32Array {
  function shiftAll (line 287) | function shiftAll(boardsim: Board, mesh: Mesh): void {
  function shiftModel (line 317) | function shiftModel(meshData: MeshData, diffXOffset: bigint, diffYOffset...
  function rotateAll (line 342) | function rotateAll(mesh: Mesh, newInverted: boolean): void {
  function overwritebufferdata (line 372) | function overwritebufferdata(mesh: Mesh, piece: Piece): void {
  function deletebufferdata (line 395) | function deletebufferdata(mesh: Mesh, piece: Piece): void {
  function renderAll (line 416) | function renderAll(boardsim: Board, mesh: Mesh | undefined): void {
  function renderVoids (line 446) | function renderVoids(mesh: Mesh | undefined): void {
  function isOffsetOutOfRangeOfRegenRange (line 458) | function isOffsetOutOfRangeOfRegenRange(offset: Coords): boolean {

FILE: src/client/scripts/esm/game/rendering/pieces.ts
  function renderPiecesInGame (line 29) | function renderPiecesInGame(boardsim: Board, mesh: Mesh | undefined): vo...
  function renderGhostPiece (line 35) | function renderGhostPiece(type: number, coords: Coords): void {

FILE: src/client/scripts/esm/game/rendering/primitives.ts
  function Quad (line 15) | function Quad(left: number, bottom: number, right: number, top: number):...
  function Quad_Color (line 30) | function Quad_Color(left: number, bottom: number, right: number, top: nu...
  function Quad_Color3D (line 45) | function Quad_Color3D(left: number, bottom: number, right: number, top: ...
  function Quad_Texture (line 60) | function Quad_Texture(left: number, bottom: number, right: number, top: ...
  function Quad_ColorTexture (line 75) | function Quad_ColorTexture(left: number, bottom: number, right: number, ...
  function Quad_ColorTexture3D (line 90) | function Quad_ColorTexture3D(left: number, bottom: number, right: number...
  function Rect (line 105) | function Rect(left: number, bottom: number, right: number, top: number, ...
  function DashedRect (line 117) | function DashedRect(left: number, bottom: number, right: number, top: nu...
  function Circle (line 185) | function Circle(x: number, y: number, radius: number, resolution: number...
  function GlowDot (line 213) | function GlowDot(x: number, y: number, radius: number, resolution: numbe...
  function Ring (line 266) | function Ring(x: number, y: number, inRad: number, outRad: number, resol...
  function RadialGradient (line 305) | function RadialGradient(x: number, y: number, radius: number, colors: Co...
  function BoxTunnel (line 385) | function BoxTunnel(left: number, bottom: number, startZ: number, right: ...

FILE: src/client/scripts/esm/game/rendering/promotionlines.ts
  constant EXTRA_LENGTH (line 24) | const EXTRA_LENGTH = 2;
  constant THICKNESS (line 26) | const THICKNESS = 0.01;
  function render (line 30) | function render(): void {

FILE: src/client/scripts/esm/game/rendering/screenshake.ts
  constant MAX_ROTATION_DEGREES (line 20) | const MAX_ROTATION_DEGREES = 1.7;
  constant MAX_TRANSLATION (line 22) | const MAX_TRANSLATION = 0.23;
  constant TRAUMA_DECAY (line 25) | const TRAUMA_DECAY = 1.2;
  function trigger (line 43) | function trigger(amount: number): void {
  function clear (line 51) | function clear(): void {
  function update (line 60) | function update(): void {
  function getShakeMatrix (line 73) | function getShakeMatrix(): Mat4 {

FILE: src/client/scripts/esm/game/rendering/starfield.ts
  type Star (line 31) | type Star = {
  constant ATTRIB_INFO (line 48) | const ATTRIB_INFO: AttributeInfoInstanced = {
  constant CONFIG (line 58) | const CONFIG = {
  function init (line 142) | function init(): void {
  function terminate (line 162) | function terminate(): void {
  function createStar (line 174) | function createStar(randomizeAge: boolean): Star {
  function getDesiredNumStars (line 211) | function getDesiredNumStars(): number {
  function applyVariance (line 225) | function applyVariance(base: number, variance: number): number {
  function update (line 232) | function update(): void {
  function couldStarfieldEverBeVisible (line 282) | function couldStarfieldEverBeVisible(): boolean {
  function isStarfieldVisible (line 303) | function isStarfieldVisible(): boolean {
  function render (line 331) | function render(): void {

FILE: src/client/scripts/esm/game/rendering/text/glyphatlas.ts
  type GlyphMetrics (line 25) | interface GlyphMetrics {
  constant CELL_HEIGHT (line 47) | const CELL_HEIGHT = 64;
  constant FONT_SIZE (line 50) | const FONT_SIZE = Math.round(CELL_HEIGHT * 0.8);
  constant FONT_FAMILY (line 52) | const FONT_FAMILY = 'sans-serif';
  constant ATLAS_DESCENDER_FRACTION (line 60) | const ATLAS_DESCENDER_FRACTION = 0.5 - 0.3 * (FONT_SIZE / CELL_HEIGHT);
  constant ATLAS_ASCENT_FRACTION (line 68) | const ATLAS_ASCENT_FRACTION = 0.42 * (FONT_SIZE / CELL_HEIGHT);
  constant CELL_PADDING (line 74) | const CELL_PADDING = 2;
  constant ATLAS_WIDTH (line 81) | const ATLAS_WIDTH = 512;
  constant REPLACEMENT_CHAR (line 87) | const REPLACEMENT_CHAR = '\uFFFD';
  constant SUPPORTED_CHARS (line 95) | const SUPPORTED_CHARS: string[] = [
  function nextPowerOfTwo (line 114) | function nextPowerOfTwo(n: number): number {
  function initGlyphAtlas (line 126) | function initGlyphAtlas(): void {
  function getAtlasTexture (line 253) | function getAtlasTexture(): WebGLTexture {
  function getGlyphMetrics (line 264) | function getGlyphMetrics(char: string): GlyphMetrics {

FILE: src/client/scripts/esm/game/rendering/text/textrenderer.ts
  function getTextWidth (line 30) | function getTextWidth(text: string, size: number): number {
  function getTextBounds (line 48) | function getTextBounds(
  function render (line 80) | function render(

FILE: src/client/scripts/esm/game/rendering/transitions/Transition.ts
  type Transition (line 40) | type Transition =
  type ZoomTransition (line 49) | type ZoomTransition = {
  type PanTransition (line 56) | type PanTransition = {
  constant HISTORY_CAP (line 64) | const HISTORY_CAP = 20;
  constant PAN_TRANSITION_CONFIG (line 67) | const PAN_TRANSITION_CONFIG = {
  method DURATION_MILLIS (line 69) | get DURATION_MILLIS() {
  method MAX_PAN_DISTANCE (line 77) | get MAX_PAN_DISTANCE() {
  constant ZOOM_TRANSITION_CONFIG (line 83) | const ZOOM_TRANSITION_CONFIG = {
  method MIN_DURATION (line 85) | get MIN_DURATION() {
  method MAX_DURATION (line 89) | get MAX_DURATION() {
  method EDGE_ACCELERATION (line 95) | get EDGE_ACCELERATION() {
  constant ONE (line 106) | const ONE = bd.fromBigInt(1n);
  constant NEGONE (line 107) | const NEGONE = bd.fromBigInt(-1n);
  function onTransitionStart (line 216) | function onTransitionStart(): void {
  function startZoomTransition (line 227) | function startZoomTransition(
  function setupCInfinityModel (line 292) | function setupCInfinityModel(natural_duration_c_inf_millis: number, maxD...
  function setupCOne2StageModel (line 312) | function setupCOne2StageModel(natural_duration_c_one_millis: number, edg...
  function setupCOne3StageModel (line 335) | function setupCOne3StageModel(edgeAccel: number, maxDuration: number): v...
  function startPanTransition (line 389) | function startPanTransition(endCoord: BDCoords, ignoreHistory: boolean):...
  function zoomToCoordsBox (line 407) | function zoomToCoordsBox(box: BoundingBox): void {
  function singleZoomToCoordsList (line 417) | function singleZoomToCoordsList(coordsList: Coords[]): void {
  function singleZoomToBDCoords (line 426) | function singleZoomToBDCoords(coords: BDCoords): void {
  function zoomTransitionToArea (line 444) | function zoomTransitionToArea(theArea: Area): void {
  function pushToTelHistory (line 453) | function pushToTelHistory(trans: Transition): void {
  function undoTransition (line 459) | function undoTransition(): void {
  function update (line 483) | function update(): void {
  function updateZoomingTransition (line 506) | function updateZoomingTransition(elapsedTime: number): void {
  function updateCInfinityTransition (line 519) | function updateCInfinityTransition(t_sec: number): number {
  function updateCOne2StageTransition (line 531) | function updateCOne2StageTransition(t_sec: number, elapsedTime: number):...
  function updateCOne3StageTransition (line 544) | function updateCOne3StageTransition(t_sec: number, elapsedTime: number):...
  function applyZoomState (line 574) | function applyZoomState(currentE: number, elapsedTime: number): void {
  function updatePanningTransition (line 612) | function updatePanningTransition(
  function finishTransition (line 675) | function finishTransition(): void {
  function areTransitioning (line 689) | function areTransitioning(): boolean {
  function eraseTelHist (line 694) | function eraseTelHist(): void {
  function terminate (line 699) | function terminate(): void {

FILE: src/client/scripts/esm/game/rendering/webgl.ts
  function setClearColor (line 55) | function setClearColor(newClearColor: Vec3): void {
  function init (line 62) | function init(): void {
  function clearScreen (line 109) | function clearScreen(): void {
  function toggleNormalBlending (line 119) | function toggleNormalBlending(): void {
  function enableBlending_Inverse (line 130) | function enableBlending_Inverse(): void {
  function executeWithDepthFunc_ALWAYS (line 139) | function executeWithDepthFunc_ALWAYS(func: Function): void {
  function executeWithInverseBlending (line 153) | function executeWithInverseBlending(func: Function): void {
  function enableDepthTest (line 257) | function enableDepthTest(): void {
  function disableDepthTest (line 266) | function disableDepthTest(): void {

FILE: src/client/scripts/esm/game/websocket/socketclose.ts
  function isInTimeout (line 38) | function isInTimeout(): boolean {
  function onclose (line 51) | function onclose(event: CloseEvent, socketWasDefined: boolean): void {
  function enterTimeout (line 149) | function enterTimeout(timeMillis: number): void {
  function leaveTimeout (line 159) | function leaveTimeout(): void {
  function onAuthenticationNeed
Condensed preview — 664 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (4,966K chars).
[
  {
    "path": ".github/copilot-instructions.md",
    "chars": 4581,
    "preview": "# Copilot Instructions for infinitechess.org\n\n### ABOVE ALL: Follow the requirements and guidelines for pull requests fo"
  },
  {
    "path": ".github/pull_request_template.md",
    "chars": 293,
    "preview": "### Type of Change (new feature, quality of life, bug fix, refactor, tooling, chore, tests, translation, or documentatio"
  },
  {
    "path": ".github/workflows/ci.yml",
    "chars": 3650,
    "preview": "name: CI\n\non:\n  push:\n    branches:\n      - main\n      - prod\n      - distance-display\n    paths-ignore:\n      - '*.md'\n"
  },
  {
    "path": ".github/workflows/deploy.yml",
    "chars": 3161,
    "preview": "name: Deploy\n\non:\n  push:\n    branches:\n      - prod\n    paths-ignore:\n      - '*.md'\n      - 'docs/**'\n      - 'LICENSE"
  },
  {
    "path": ".gitignore",
    "chars": 710,
    "preview": "# .gitignore\n\n.env\ncert\nlogs\ndist\n# Old json data storage \"database\"\n/database\n\n# SQLite Database Files\n*.db\n*.sqlite\n*."
  },
  {
    "path": ".husky/pre-commit",
    "chars": 16,
    "preview": "npx lint-staged\n"
  },
  {
    "path": ".prettierignore",
    "chars": 57,
    "preview": "# .prettierignore\n\ndist/\ndev-utils/\nsrc/client/pkg/\n*.ejs"
  },
  {
    "path": ".prettierrc.json",
    "chars": 344,
    "preview": "{\n  \"semi\": true,\n  \"useTabs\": true,\n  \"tabWidth\": 4,\n  \"singleQuote\": true,\n  \"bracketSpacing\": true,\n  \"trailingComma\""
  },
  {
    "path": "CLAUDE.md",
    "chars": 2364,
    "preview": "# Claude Instructions for infinitechess.org\n\n### ABOVE ALL: Follow the requirements and guidelines for pull requests fou"
  },
  {
    "path": "LICENSE",
    "chars": 34523,
    "preview": "                    GNU AFFERO GENERAL PUBLIC LICENSE\n                       Version 3, 19 November 2007\n\n Copyright (C)"
  },
  {
    "path": "README.md",
    "chars": 2272,
    "preview": "# Infinite Chess Web Server\n\n[InfiniteChess.org](https://www.infinitechess.org) is a free and ad-less website for playin"
  },
  {
    "path": "build/client.ts",
    "chars": 9352,
    "preview": "// build/client.ts\n\nimport fs from 'fs';\nimport swc from '@swc/core';\nimport path from 'node:path';\nimport { glob } from"
  },
  {
    "path": "build/engine-wasm.ts",
    "chars": 4652,
    "preview": "// build/engine-wasm.ts\n\n/**\n * HydroChess WASM Engine Setup Script\n *\n * This ensures that the HydroChess WASM engine i"
  },
  {
    "path": "build/env.ts",
    "chars": 2154,
    "preview": "// build/env.ts\n\n/**\n * Ensures the .env file exists, generating it with default values if it doesn't.\n * And ensures it"
  },
  {
    "path": "build/index.ts",
    "chars": 1512,
    "preview": "// build/index.ts\n\n/**\n * This script deploys all files and assets from /src/client to /dist in order to run the website"
  },
  {
    "path": "build/plugins.ts",
    "chars": 593,
    "preview": "// build/plugins.ts\n\n/**\n * Contains shared esbuild plugins used in both client and server builds.\n */\n\nimport type { Pl"
  },
  {
    "path": "build/server.ts",
    "chars": 1553,
    "preview": "// build/server.ts\n\nimport { glob } from 'glob';\nimport esbuild, { BuildOptions } from 'esbuild';\n\nimport { getESBuildLo"
  },
  {
    "path": "build/views.ts",
    "chars": 3607,
    "preview": "// build/views.ts\n\n/**\n * Generates static HTML views from EJS templates and translation files.\n */\n\nimport fs from 'fs'"
  },
  {
    "path": "dev-utils/ICN_METADATA_TRANSLATIONS.md",
    "chars": 4496,
    "preview": "# English Translations Required for ICN Metadata\n\nICN metadata must **always** be written in English, regardless of the "
  },
  {
    "path": "dev-utils/REDESIGN/design.md",
    "chars": 3759,
    "preview": "# Summary of what should go on each page/component\n\n## Header\n\n- Site name + logo -> Home page\n- News\n- Practice\n- Edito"
  },
  {
    "path": "dev-utils/REDESIGN/runner_setup.md",
    "chars": 10433,
    "preview": "# GitHub Actions Runner Setup\n\n[← Back to Navigation Guide](./NAVIGATING.md)\n\nThis guide covers everything needed to bri"
  },
  {
    "path": "dev-utils/REDESIGN/stack.md",
    "chars": 9061,
    "preview": "# Website Redesign Plan\n\nThe website will undergo a complete redesign to modernize its look and feel, making it much mor"
  },
  {
    "path": "dev-utils/REDESIGN/todo.md",
    "chars": 4976,
    "preview": "# Redesign TODO\n\n---\n\n## Build Pipeline\n\n- Add `entryNames: '[dir]/[name]-[hash]'` to the esbuild client build options i"
  },
  {
    "path": "dev-utils/SKELETON.css",
    "chars": 2583,
    "preview": "* {\n    margin: 0;\n    padding: 0;\n    font-family: Verdana;\n    border: 0;\n    /* Enable temporarily during dev to see "
  },
  {
    "path": "dev-utils/SKELETON.html",
    "chars": 1387,
    "preview": "<!DOCTYPE html>\n<html>\n    <head>\n        <meta charset=\"utf-8\" />\n        <meta http-equiv=\"x-ua-compatible\" content=\"i"
  },
  {
    "path": "dev-utils/live-game-persistence.md",
    "chars": 13501,
    "preview": "# Live Game Persistence\n\nActive games are persisted to the database so they survive server restarts instead of being abo"
  },
  {
    "path": "dev-utils/pieces/spritesheet 512/How to create spritesheet.md",
    "chars": 402,
    "preview": "# How to generate the game's spritesheet\n\n1. Go to [Stitches](https://draeton.github.io/stitches/).\n\n2. Make sure there "
  },
  {
    "path": "dev-utils/pieces/svg/Converting PNG to SVG.md",
    "chars": 898,
    "preview": "# Steps to converting a PNG to SVG #\n\nThis is the best method I've found, to retain high quality, yet remain highly comp"
  },
  {
    "path": "dev-utils/post_processing_effects/posterize/PosterizePass.ts",
    "chars": 1626,
    "preview": "// dev-utils/post_processing_effects/posterize/PosterizePass.ts\n\nimport type { PostProcessPass } from '../PostProcessing"
  },
  {
    "path": "dev-utils/post_processing_effects/posterize/fragment.glsl",
    "chars": 1315,
    "preview": "#version 300 es\r\nprecision highp float;\r\n\r\n// The texture containing the scene to be posterized\r\nuniform sampler2D u_sce"
  },
  {
    "path": "dev-utils/post_processing_effects/radial_distortion/RadialDistortionPass.ts",
    "chars": 1244,
    "preview": "import type { ProgramManager, ProgramMap } from \"../../ProgramManager\";\nimport type { PostProcessPass } from \"../PostPro"
  },
  {
    "path": "dev-utils/post_processing_effects/radial_distortion/fragment.glsl",
    "chars": 963,
    "preview": "#version 300 es\nprecision highp float;\n\nuniform sampler2D u_sceneTexture;\n\n// --- Distortion Controls ---\nuniform float "
  },
  {
    "path": "dev-utils/post_processing_effects/rolling_hills/RollingHillsPass.ts",
    "chars": 1650,
    "preview": "import type { ProgramManager, ProgramMap } from \"../../ProgramManager\";\nimport type { PostProcessPass } from \"../PostPro"
  },
  {
    "path": "dev-utils/post_processing_effects/rolling_hills/fragment.glsl",
    "chars": 2385,
    "preview": "#version 300 es\nprecision highp float;\n\nuniform sampler2D u_sceneTexture;\n\n// --- Distortion Controls ---\nuniform float "
  },
  {
    "path": "dev-utils/readme.md",
    "chars": 378,
    "preview": "# Dev Utils\n\nThis directory contains both depricated scripts that we believe might be useful in the future, as well as a"
  },
  {
    "path": "dev-utils/scripts/PatreonAPI.ts",
    "chars": 1342,
    "preview": "// dev-utils/scripts/PatreonAPI.ts\n\n/*\n * This module, in the future, will be where we connect to Patreon's API\n * to dy"
  },
  {
    "path": "dev-utils/scripts/audio/processors/bitcrusher/BitcrusherNode.ts",
    "chars": 1381,
    "preview": "// dev-utils/scripts/audio/processors/bitcrusher/BitcrusherNode.ts\n\nexport class BitcrusherNode extends AudioWorkletNode"
  },
  {
    "path": "dev-utils/scripts/audio/processors/bitcrusher/BitcrusherProcessor.ts",
    "chars": 2535,
    "preview": "// dev-utils/scripts/audio/processors/bitcrusher/BitcrusherProcessor.ts\n\nimport type { AudioParamDescriptor } from '../w"
  },
  {
    "path": "dev-utils/scripts/clientEventDispatcher.ts",
    "chars": 1683,
    "preview": "\n/**\n * This event dispatcher will only dispatch events in the browser environment.\n * \n * NOTHING will happen if it is "
  },
  {
    "path": "dev-utils/scripts/events.ts",
    "chars": 2075,
    "preview": "// dev-utils/scripts/events.ts\n\n\n/**\n * A script that was intended for managing gamefile events for games\n * on both cli"
  },
  {
    "path": "dev-utils/scripts/gl-matrix.js",
    "chars": 206510,
    "preview": "\n/*!\n@fileoverview gl-matrix - High performance matrix and vector operations\n@author Brandon Jones\n@author Colin MacKenz"
  },
  {
    "path": "dev-utils/scripts/icn-regex-matching.ts",
    "chars": 8204,
    "preview": "// dev-utils/scripts/icn-regex-matching.ts\n\n\n/**\n * This stores a monster regex I made for matching ICN.\n * \n * It has a"
  },
  {
    "path": "dev-utils/scripts/meshSimplification.ts",
    "chars": 4856,
    "preview": "\n\n/**\n * This stores a mesh simplification algorithm Naviary designed to simplify the void mesh.\n * \n * It can't be used"
  },
  {
    "path": "dev-utils/scripts/positionnormalizer/moveexpander.ts",
    "chars": 10151,
    "preview": "\n// src/client/scripts/esm/chess/logic/positionnormalizer/moveexpander.ts\n\n/**\n * This script takes a chosen move from a"
  },
  {
    "path": "dev-utils/scripts/positionnormalizer/normalizertester.ts",
    "chars": 5586,
    "preview": "// dev-utils/scripts/positionnormalizer/normalizertester.ts\n\n\n/**\n * ONLY FOR TESTING COMPRESSING POSITIONS\n */\n\n\n// ==="
  },
  {
    "path": "dev-utils/scripts/positionnormalizer/positioncompressor.ts",
    "chars": 23930,
    "preview": "// dev-utils/scripts/positionnormalizer/positioncompressor.ts\n\n/**\n * This script contains an algorithm that can take an"
  },
  {
    "path": "dev-utils/scripts/positionnormalizer/positioncompressorplusintersections.ts",
    "chars": 29371,
    "preview": "// src/client/scripts/esm/chess/logic/positionnormalizer/positioncompressor.ts\n\n/**\n * This script contains an algorithm"
  },
  {
    "path": "dev-utils/scripts/positionnormalizer/unusedpositionnormalizermethods.ts",
    "chars": 12413,
    "preview": "\n\n\n// ======================================== ORTHOGONAL SOLVER ========================================\n\n\n// /**\n//  *"
  },
  {
    "path": "dev-utils/scripts/vertexdatatotexture.ts",
    "chars": 3997,
    "preview": "\n/**\n * This script converts an array of vertex data into a renderable WebGL texture.\n * \n * TODO:\n * \n * POLISH AND CLE"
  },
  {
    "path": "dev-utils/shaders/texture/instanced/tint/fragment.glsl",
    "chars": 443,
    "preview": "#version 300 es\r\n\r\nprecision highp float;\r\n\r\nin vec2 vTextureCoord;          // From vertex shader\r\nuniform sampler2D u_"
  },
  {
    "path": "dev-utils/shaders/texture/instanced/tint/vertex.glsl",
    "chars": 753,
    "preview": "#version 300 es\r\n\r\n// This shader is capable of tinting all textures\r\n// a specific color via a uniform\r\n\r\nin vec4 a_pos"
  },
  {
    "path": "dev-utils/shaders/texture/tint/fragment.glsl",
    "chars": 307,
    "preview": "`#version 300 es\r\n\r\nprecision highp float;\r\n\r\nin vec2 vTextureCoord;\r\n\r\nuniform vec4 uTintColor;\r\nuniform sampler2D u_sa"
  },
  {
    "path": "dev-utils/shaders/texture/tint/vertex.glsl",
    "chars": 325,
    "preview": "#version 300 es\r\n\r\n// This shader is capable of tinting all textures\r\n// a specific color via a uniform\r\n\r\nin vec4 a_pos"
  },
  {
    "path": "dev-utils/shaders/voronoi/fragment.glsl",
    "chars": 4764,
    "preview": "#version 300 es\r\nprecision highp float;\r\n\r\n// This shader was replaced by a voronoi_distortion shader\r\n// for the Echo R"
  },
  {
    "path": "dev-utils/sounds/SoundscapeGenerator.html",
    "chars": 33173,
    "preview": "<!-- Interactive Soundscape Generator, made by AI. -->\n<!-- Standalone. Just open the html in your browser. -->\n<!-- Thi"
  },
  {
    "path": "dev-utils/spritesheet_generator/spritesheet.ts",
    "chars": 5974,
    "preview": "// dev-utils/spritesheet_generator/spritesheet.ts\n\n/**\n * This script stores the spritesheet FOR THE CURRENT GAME,\n * an"
  },
  {
    "path": "dev-utils/spritesheet_generator/spritesheetGenerator.ts",
    "chars": 5466,
    "preview": "// dev-utils/spritesheet_generator/spritesheetGenerator.ts\n\n/**\n * This script takes a list of images, and converts it i"
  },
  {
    "path": "docs/COPYING.md",
    "chars": 3307,
    "preview": "# Copying Infinite Chess\n\nAny file in this project that does not state otherwise and is not listed as an exception below"
  },
  {
    "path": "docs/GRAPHICS.md",
    "chars": 9623,
    "preview": "# Graphics Rendering Guide\n\n[← Back to Navigation Guide](./NAVIGATING.md) | [Contributing Guide](./GUIDELINES.md)\n\nThis "
  },
  {
    "path": "docs/GUIDELINES.md",
    "chars": 7176,
    "preview": "# Pull Request Requirements and Guidelines\n\n[← Back to Navigation Guide](./NAVIGATING.md) | [Setup Guide](./SETUP.md) | "
  },
  {
    "path": "docs/NAVIGATING.md",
    "chars": 12063,
    "preview": "# Navigation Guide\n\nThis guide gives you several pointers on the project structure, to help you get started with collabo"
  },
  {
    "path": "docs/SETUP.md",
    "chars": 9898,
    "preview": "# Setting up your workspace\n\nThis guide walks you through the initial setup phase of the infinitechess.org server on you"
  },
  {
    "path": "docs/TRANSLATIONS.md",
    "chars": 4485,
    "preview": "# Translation guide\n\nThis guide will walk you through the process of translating [InfiniteChess.org](https://www.infinit"
  },
  {
    "path": "ecosystem.config.cjs",
    "chars": 253,
    "preview": "// ecosystem.config.cjs\n\n/*\n * PM2 process configuration for the Infinite Chess production server.\n */\n\nmodule.exports ="
  },
  {
    "path": "eslint.config.js",
    "chars": 5454,
    "preview": "// eslint.config.js\n\nimport globals from 'globals';\nimport pluginJs from '@eslint/js';\nimport pluginTypescript from '@ty"
  },
  {
    "path": "nodemon.json",
    "chars": 234,
    "preview": "{\n  \"watch\": [\n    \"dist/server\",\n    \"dist/shared\",\n    \"src/client/views\",\n    \"src/client/scripts/cjs/game/htmlscript"
  },
  {
    "path": "package.json",
    "chars": 3923,
    "preview": "{\n  \"name\": \"infinite-chess-server\",\n  \"version\": \"1.4.3\",\n  \"description\": \"infinitechess.org server\",\n  \"author\": \"Nav"
  },
  {
    "path": "scripts/add-file-paths.ts",
    "chars": 3722,
    "preview": "// scripts/add-file-paths.ts\n\n/**\n * This script ensures all .js and .ts files have their relative file path\n * on the f"
  },
  {
    "path": "scripts/generate-translation-types.ts",
    "chars": 3588,
    "preview": "// scripts/generate-translation-types.ts\n\n/**\n * Generates TypeScript types from the English translation TOML file.\n * C"
  },
  {
    "path": "scripts/optimize-images.ts",
    "chars": 4980,
    "preview": "// scripts/optimize-images.ts\n\n/**\n * This script automatically finds and compresses all images from the source\n * direc"
  },
  {
    "path": "scripts/organize-imports.ts",
    "chars": 13865,
    "preview": "// scripts/organize-imports.ts\n\n/**\n * TypeScript Import Organizer\n *\n * PREREQUISITES:\n * - All import statements must "
  },
  {
    "path": "scripts/readme.md",
    "chars": 160,
    "preview": "This directory contains scripts used explicitly by project configuration, such as npm scripts, or pre-commit hooks. They"
  },
  {
    "path": "src/client/css/404.css",
    "chars": 159,
    "preview": "* {\n\tmargin: 0;\n\tpadding: 0;\n}\n\nbody {\n\tpadding-top: 30px;\n\tbackground-color: aliceblue;\n\ttext-align: center;\n}\n\nh1 {\n\tf"
  },
  {
    "path": "src/client/css/admin.css",
    "chars": 271,
    "preview": "* {\n\tbackground-color: rgb(20, 20, 20);\n\tcolor: white;\n}\nbody {\n\tpadding: 30px 40px;\n}\ntextarea {\n\twidth: 100%;\n\theight:"
  },
  {
    "path": "src/client/css/createaccount.css",
    "chars": 3902,
    "preview": "* {\n\tmargin: 0;\n\tpadding: 0;\n\tfont-family: Verdana;\n\tborder: 0;\n\t/* Enable temporarily during dev to see the borders of "
  },
  {
    "path": "src/client/css/credits.css",
    "chars": 1728,
    "preview": "* {\n\tmargin: 0;\n\tpadding: 0;\n\tfont-family: Verdana;\n\tborder: 0;\n\t/* Enable temporarily during dev to see the borders of "
  },
  {
    "path": "src/client/css/footer.css",
    "chars": 617,
    "preview": "footer {\n\ttext-align: center;\n\tpadding: 10px 0;\n}\n\nfooter a {\n\tdisplay: inline-block;\n\tcolor: rgb(207, 207, 207);\n\tmargi"
  },
  {
    "path": "src/client/css/guide.css",
    "chars": 4385,
    "preview": "/* Guide page styles */\n\n* {\n\tmargin: 0;\n\tpadding: 0;\n\tfont-family: Verdana;\n\tborder: 0;\n}\n\nhtml {\n\theight: 100%;\n\tbackg"
  },
  {
    "path": "src/client/css/header.css",
    "chars": 17258,
    "preview": ":root {\n\t/* THIS GETS OVERWRITTEN BY JAVASCRIPT in header.js that correctly sets the\n    viewport height based on how mu"
  },
  {
    "path": "src/client/css/icnvalidator.css",
    "chars": 7166,
    "preview": ":root {\n\t--bg-color: #191919;\n\t--text-color: #d4d4d4;\n\t--primary-color: #4a90e2;\n\t--secondary-color: #252526;\n\t--border-"
  },
  {
    "path": "src/client/css/index.css",
    "chars": 4703,
    "preview": "* {\n\tmargin: 0;\n\tpadding: 0;\n\tfont-family: Verdana;\n\tborder: 0;\n\t/* Enable temporarily during dev to see the borders of "
  },
  {
    "path": "src/client/css/leaderboard.css",
    "chars": 2712,
    "preview": "* {\n\tmargin: 0;\n\tpadding: 0;\n\tfont-family: Verdana;\n\tborder: 0;\n\t/* Enable temporarily during dev to see the borders of "
  },
  {
    "path": "src/client/css/login.css",
    "chars": 3827,
    "preview": "* {\n\tmargin: 0;\n\tpadding: 0;\n\tfont-family: Verdana;\n\tborder: 0;\n\t/* Enable temporarily during dev to see the borders of "
  },
  {
    "path": "src/client/css/member.css",
    "chars": 3972,
    "preview": "* {\n\tmargin: 0;\n\tpadding: 0;\n\tfont-family: Verdana;\n\tborder: 0;\n\t/* Enable temporarily during dev to see the borders of "
  },
  {
    "path": "src/client/css/news.css",
    "chars": 2497,
    "preview": "* {\n\tmargin: 0;\n\tpadding: 0;\n\tfont-family: Verdana;\n\tborder: 0;\n\t/* Enable temporarily during dev to see the borders of "
  },
  {
    "path": "src/client/css/play.css",
    "chars": 41579,
    "preview": "* {\n\tmargin: 0;\n\tpadding: 0;\n\tfont-family: Verdana;\n\tborder: 0;\n\t-webkit-tap-highlight-color: transparent; /* Suppress b"
  },
  {
    "path": "src/client/css/termsofservice.css",
    "chars": 1719,
    "preview": "* {\n\tmargin: 0;\n\tpadding: 0;\n\tfont-family: Verdana; /* Helvetica */\n\tborder: 0;\n\t/* Enable temporarily during dev to see"
  },
  {
    "path": "src/client/scripts/cjs/game/htmlscript.ts",
    "chars": 3498,
    "preview": "// src/client/scripts/cjs/game/htmlscript.ts\n\n/* global main */\n\n/**\n * The server injects this script directly into the"
  },
  {
    "path": "src/client/scripts/esm/audio/AudioEffects.ts",
    "chars": 3800,
    "preview": "// src/client/scripts/esm/audio/AudioEffects.ts\n\n/**\n * This module is responsible for creating and managing audio effec"
  },
  {
    "path": "src/client/scripts/esm/audio/AudioManager.ts",
    "chars": 15943,
    "preview": "// src/client/scripts/esm/audio/AudioManager.ts\n\n/**\n * This module is responsible for creating and playing sounds using"
  },
  {
    "path": "src/client/scripts/esm/audio/AudioUtils.ts",
    "chars": 3069,
    "preview": "// src/client/scripts/esm/audio/AudioUtils.ts\n\n/**\n * This module provides generic, reusable utility functions for worki"
  },
  {
    "path": "src/client/scripts/esm/audio/LFOFactory.ts",
    "chars": 2396,
    "preview": "// src/client/scripts/esm/audio/LFOFactory.ts\n\n/**\n * A factory for creating Low-Frequency Oscillator (LFO) units for mo"
  },
  {
    "path": "src/client/scripts/esm/audio/SoundLayer.ts",
    "chars": 5481,
    "preview": "// src/client/scripts/esm/audio/SoundLayer.ts\n\n/**\n * This module implements the audio graph for individual sound layers"
  },
  {
    "path": "src/client/scripts/esm/audio/SoundscapePlayer.ts",
    "chars": 4690,
    "preview": "// src/client/scripts/esm/audio/SoundscapePlayer.ts\n\n/**\n * This module implements a soundscape player that can play com"
  },
  {
    "path": "src/client/scripts/esm/audio/processors/downsampler/DownsamplerNode.ts",
    "chars": 1122,
    "preview": "// src/client/scripts/esm/audio/processors/downsampler/DownsamplerNode.ts\n\nexport class DownsamplerNode extends AudioWor"
  },
  {
    "path": "src/client/scripts/esm/audio/processors/downsampler/DownsamplerProcessor.ts",
    "chars": 2270,
    "preview": "// src/client/scripts/esm/audio/processors/downsampler/DownsamplerProcessor.ts\n\nimport type { AudioParamDescriptor } fro"
  },
  {
    "path": "src/client/scripts/esm/audio/processors/worklet-types.ts",
    "chars": 408,
    "preview": "// src/client/scripts/esm/audio/processors/worklet-types.ts\n\n/**\n * Stores missing audio worklet typescript types that a"
  },
  {
    "path": "src/client/scripts/esm/chess/rendering/checkerboardgenerator.ts",
    "chars": 1906,
    "preview": "// src/client/scripts/esm/chess/rendering/checkerboardgenerator.ts\n\n/**\n * This script can create a 2x2 checkerboard tex"
  },
  {
    "path": "src/client/scripts/esm/chess/rendering/imagecache.ts",
    "chars": 5423,
    "preview": "// src/client/scripts/esm/chess/rendering/imagecache.ts\n\n/**\n * This script caches the HTMLImageElement objects for the "
  },
  {
    "path": "src/client/scripts/esm/chess/rendering/svgcache.ts",
    "chars": 9783,
    "preview": "// src/client/scripts/esm/chess/rendering/svgcache.ts\n\n/**\n * This module handles fetching and caching of chess piece SV"
  },
  {
    "path": "src/client/scripts/esm/chess/rendering/texturecache.ts",
    "chars": 4014,
    "preview": "// src/client/scripts/esm/chess/rendering/texturecache.ts\n\n/**\n * This module handles the caching of WebGL textures of t"
  },
  {
    "path": "src/client/scripts/esm/components/header/currpage-greyer.ts",
    "chars": 1744,
    "preview": "// src/client/scripts/esm/components/header/currpage-greyer.ts\n\n// Greys the background color of the header navigation l"
  },
  {
    "path": "src/client/scripts/esm/components/header/dropdowns/appearancedropdown.ts",
    "chars": 4964,
    "preview": "// src/client/scripts/esm/components/header/dropdowns/appearancedropdown.ts\n\nimport themes from '../../../../../../share"
  },
  {
    "path": "src/client/scripts/esm/components/header/dropdowns/gameplaydropdown.ts",
    "chars": 3396,
    "preview": "// src/client/scripts/esm/components/header/dropdowns/gameplaydropdown.ts\n\n// This script allows us to enable or disable"
  },
  {
    "path": "src/client/scripts/esm/components/header/dropdowns/languagedropdown.ts",
    "chars": 3626,
    "preview": "// src/client/scripts/esm/components/header/dropdowns/languagedropdown.ts\n\n// This script selects new languages when we "
  },
  {
    "path": "src/client/scripts/esm/components/header/dropdowns/legalmovedropdown.ts",
    "chars": 3369,
    "preview": "// src/client/scripts/esm/components/header/dropdowns/legalmovedropdown.ts\n\n// This script selects new languages when we"
  },
  {
    "path": "src/client/scripts/esm/components/header/dropdowns/perspectivedropdown.ts",
    "chars": 4345,
    "preview": "// src/client/scripts/esm/components/header/dropdowns/perspectivedropdown.ts\n\n// This script allows us to adjust the mou"
  },
  {
    "path": "src/client/scripts/esm/components/header/dropdowns/sounddropdown.ts",
    "chars": 2685,
    "preview": "// src/client/scripts/esm/components/header/dropdowns/sounddropdown.ts\n\n// This script manages the sound settings dropdo"
  },
  {
    "path": "src/client/scripts/esm/components/header/faviconselector.ts",
    "chars": 1028,
    "preview": "// src/client/scripts/esm/components/header/faviconselector.ts\n\n// This script auto detects device theme and adjusts the"
  },
  {
    "path": "src/client/scripts/esm/components/header/header.ts",
    "chars": 4500,
    "preview": "// src/client/scripts/esm/components/header/header.ts\n\n// This script contains the code related to the\n// header that ru"
  },
  {
    "path": "src/client/scripts/esm/components/header/news-notification.ts",
    "chars": 3516,
    "preview": "// src/client/scripts/esm/components/header/news-notification.ts\n\n/**\n * This script handles the unread news notificatio"
  },
  {
    "path": "src/client/scripts/esm/components/header/pingmeter.ts",
    "chars": 3672,
    "preview": "// src/client/scripts/esm/components/header/pingmeter.ts\n\n/**\n * This script manages the display and updates of the ping"
  },
  {
    "path": "src/client/scripts/esm/components/header/preferences.ts",
    "chars": 19927,
    "preview": "// src/client/scripts/esm/components/header/preferences.ts\n\nimport type { Color } from '../../../../../shared/util/math/"
  },
  {
    "path": "src/client/scripts/esm/components/header/settings.ts",
    "chars": 8511,
    "preview": "// src/client/scripts/esm/components/header/settings.ts\n\n// This script opens and closes our settings drop-down menu whe"
  },
  {
    "path": "src/client/scripts/esm/components/header/spacing.ts",
    "chars": 3671,
    "preview": "// src/client/scripts/esm/components/header/spacing.ts\n\n// Spacing: This script handles the spacing of our header elemen"
  },
  {
    "path": "src/client/scripts/esm/game/GameBus.ts",
    "chars": 1487,
    "preview": "// src/client/scripts/esm/game/GameBus.ts\n\nimport type { Piece } from '../../../../shared/chess/util/boardutil';\nimport "
  },
  {
    "path": "src/client/scripts/esm/game/boardeditor/actions/eactions.ts",
    "chars": 18796,
    "preview": "// src/client/scripts/esm/game/boardeditor/actions/eactions.ts\n\n/**\n * Editor Actions\n *\n * Contains handlers for the on"
  },
  {
    "path": "src/client/scripts/esm/game/boardeditor/actions/eautosave.ts",
    "chars": 5117,
    "preview": "// src/client/scripts/esm/game/boardeditor/actions/eautosave.ts\n\n/**\n * This script handles autosaving the board editor "
  },
  {
    "path": "src/client/scripts/esm/game/boardeditor/actions/ecloud.ts",
    "chars": 9519,
    "preview": "// src/client/scripts/esm/game/boardeditor/actions/ecloud.ts\n\n/**\n * Handles cloud (server) save/load operations for the"
  },
  {
    "path": "src/client/scripts/esm/game/boardeditor/actions/editorSavesAPI.ts",
    "chars": 4490,
    "preview": "// src/client/scripts/esm/game/boardeditor/actions/editorSavesAPI.ts\n\n/**\n * Client-side wrappers for the editor saves s"
  },
  {
    "path": "src/client/scripts/esm/game/boardeditor/actions/esave.ts",
    "chars": 5701,
    "preview": "// src/client/scripts/esm/game/boardeditor/actions/esave.ts\n\n/**\n * Handles the saving of positions in boardeditor\n */\n\n"
  },
  {
    "path": "src/client/scripts/esm/game/boardeditor/boardeditor.ts",
    "chars": 8198,
    "preview": "// src/client/scripts/esm/game/boardeditor/boardeditor.ts\n\n/**\n * Core manager for the Board Editor.\n *\n * Handles the l"
  },
  {
    "path": "src/client/scripts/esm/game/boardeditor/eclipboard.ts",
    "chars": 3968,
    "preview": "// src/client/scripts/esm/game/boardeditor/eclipboard.ts\n\n/**\n * Clipboard handlers for the Board Editor.\n *\n * Manages "
  },
  {
    "path": "src/client/scripts/esm/game/boardeditor/edithistory.ts",
    "chars": 9316,
    "preview": "// src/client/scripts/esm/game/boardeditor/edithistory.ts\n\n/**\n * Edit History for the Board Editor.\n *\n * Manages the u"
  },
  {
    "path": "src/client/scripts/esm/game/boardeditor/editortypes.ts",
    "chars": 3238,
    "preview": "// src/client/scripts/esm/game/boardeditor/editortypes.ts\n\n/**\n * All TypeScript types, constants, and Zod schemas for t"
  },
  {
    "path": "src/client/scripts/esm/game/boardeditor/egamerules.ts",
    "chars": 13637,
    "preview": "// src/client/scripts/esm/game/boardeditor/egamerules.ts\n\n/**\n * Editor Game Rules\n *\n * Manages the game rules of the b"
  },
  {
    "path": "src/client/scripts/esm/game/boardeditor/tools/drawingtool.ts",
    "chars": 8291,
    "preview": "// src/client/scripts/esm/game/boardeditor/tools/drawingtool.ts\n\n/**\n * Editor Drawing Tool\n *\n * Manages all drawing to"
  },
  {
    "path": "src/client/scripts/esm/game/boardeditor/tools/etoolmanager.ts",
    "chars": 4194,
    "preview": "// src/client/scripts/esm/game/boardeditor/tools/etoolmanager.ts\n\n/**\n * Tool Manager for the Board Editor.\n *\n * Tracks"
  },
  {
    "path": "src/client/scripts/esm/game/boardeditor/tools/normaltool.ts",
    "chars": 3176,
    "preview": "// src/client/scripts/esm/game/boardeditor/tools/normaltool.ts\n\n/**\n * Normal Tool for the Board Editor\n *\n * This tool "
  },
  {
    "path": "src/client/scripts/esm/game/boardeditor/tools/selection/scursor.ts",
    "chars": 1925,
    "preview": "// src/client/scripts/esm/game/boardeditor/tools/selection/scursor.ts\n\n/**\n * Selection Tool Cursor Style\n *\n * Handles "
  },
  {
    "path": "src/client/scripts/esm/game/boardeditor/tools/selection/sdrag.ts",
    "chars": 8300,
    "preview": "// src/client/scripts/esm/game/boardeditor/tools/selection/sdrag.ts\n\n/**\n * Selection Tool Drag\n *\n * This handles when "
  },
  {
    "path": "src/client/scripts/esm/game/boardeditor/tools/selection/selectiontool.ts",
    "chars": 8386,
    "preview": "// src/client/scripts/esm/game/boardeditor/tools/selection/selectiontool.ts\n\n/**\n * The Selection Tool for the Board Edi"
  },
  {
    "path": "src/client/scripts/esm/game/boardeditor/tools/selection/sfill.ts",
    "chars": 7164,
    "preview": "// src/client/scripts/esm/game/boardeditor/tools/selection/sfill.ts\n\n/**\n * Selection Tool Fill\n *\n * This handles the f"
  },
  {
    "path": "src/client/scripts/esm/game/boardeditor/tools/selection/stoolgraphics.ts",
    "chars": 6154,
    "preview": "// src/client/scripts/esm/game/boardeditor/tools/selection/stoolgraphics.ts\n\n/**\n * Selection Tool Graphics\n *\n * Contai"
  },
  {
    "path": "src/client/scripts/esm/game/boardeditor/tools/selection/stransformations.ts",
    "chars": 25104,
    "preview": "// src/client/scripts/esm/game/boardeditor/tools/selection/stransformations.ts\n\n/**\n * Selection Tool Transformations\n *"
  },
  {
    "path": "src/client/scripts/esm/game/chess/checkmatepractice.ts",
    "chars": 16118,
    "preview": "// src/client/scripts/esm/game/chess/checkmatepractice.ts\n\n/**\n * This script handles checkmate practice logic\n */\n\nimpo"
  },
  {
    "path": "src/client/scripts/esm/game/chess/clientmetadatautil.ts",
    "chars": 4349,
    "preview": "// src/client/scripts/esm/game/chess/clientmetadatautil.ts\n\n/**\n * Client-side helpers for building and parsing ICN game"
  },
  {
    "path": "src/client/scripts/esm/game/chess/copygame.ts",
    "chars": 2421,
    "preview": "// src/client/scripts/esm/game/chess/copygame.ts\n\n/**\n * This script handles copying games\n */\n\nimport type { VariantCod"
  },
  {
    "path": "src/client/scripts/esm/game/chess/engines/engine.ts",
    "chars": 2570,
    "preview": "// src/client/scripts/esm/game/chess/engines/engine.ts\n\n/*\n * This module contains the centralized data structure for al"
  },
  {
    "path": "src/client/scripts/esm/game/chess/engines/engineCheckmatePractice.ts",
    "chars": 65126,
    "preview": "// src/client/scripts/esm/game/chess/engines/engineCheckmatePractice.ts\n\n/**\n * This script runs a chess engine for chec"
  },
  {
    "path": "src/client/scripts/esm/game/chess/engines/enginecards/hydrochess_card.ts",
    "chars": 4389,
    "preview": "// src/client/scripts/esm/game/chess/engines/enginecards/hydrochess_card.ts\n\nimport type { VariantOptions } from '../../"
  },
  {
    "path": "src/client/scripts/esm/game/chess/engines/hydrochess.ts",
    "chars": 5153,
    "preview": "// src/client/scripts/esm/game/chess/engines/hydrochess.ts\n\n/**\n * HydroChess Engine\n * A JavaScript wrapper for the WAS"
  },
  {
    "path": "src/client/scripts/esm/game/chess/game.ts",
    "chars": 16381,
    "preview": "// src/client/scripts/esm/game/chess/game.ts\n\n/**\n * This script prepares our game.\n *\n * And contains our main update()"
  },
  {
    "path": "src/client/scripts/esm/game/chess/gamecompressor.ts",
    "chars": 6583,
    "preview": "// src/client/scripts/esm/game/chess/gamecompressor.ts\n\n/**\n * This script handles the compression of a gamefile into a "
  },
  {
    "path": "src/client/scripts/esm/game/chess/gamecompressor.unit.test.ts",
    "chars": 2036,
    "preview": "// src/client/scripts/esm/game/chess/gamecompressor.unit.test.ts\n\nimport type { FullGame } from '../../../../../shared/c"
  },
  {
    "path": "src/client/scripts/esm/game/chess/gameformulator.ts",
    "chars": 2329,
    "preview": "// src/client/scripts/esm/game/chess/gameformulator.ts\n\n/**\n * This script takes an ICN, or a compressed abridged gamefi"
  },
  {
    "path": "src/client/scripts/esm/game/chess/gameloader.ts",
    "chars": 21449,
    "preview": "// src/client/scripts/esm/game/chess/gameloader.ts\n\n/**\n * This script contains the logic for loading any kind of game o"
  },
  {
    "path": "src/client/scripts/esm/game/chess/gameslot.ts",
    "chars": 12816,
    "preview": "// src/client/scripts/esm/game/chess/gameslot.ts\n\n/**\n * Whether we're in a local game, online game, analysis board, or "
  },
  {
    "path": "src/client/scripts/esm/game/chess/graphicalchanges.ts",
    "chars": 5747,
    "preview": "// src/client/scripts/esm/game/chess/graphicalchanges.ts\n\n/**\n * This script contains the functions that know what mesh "
  },
  {
    "path": "src/client/scripts/esm/game/chess/movesequence.ts",
    "chars": 8242,
    "preview": "// src/client/scripts/esm/game/chess/movesequence.ts\n\n/**\n * This is a client-side script that executes global and local"
  },
  {
    "path": "src/client/scripts/esm/game/chess/pastegame.ts",
    "chars": 9964,
    "preview": "// src/client/scripts/esm/game/chess/pastegame.ts\n\n/**\n * This script handles pasting games\n */\n\nimport type { MetaData "
  },
  {
    "path": "src/client/scripts/esm/game/chess/premoves.ts",
    "chars": 16210,
    "preview": "// src/client/scripts/esm/game/chess/premoves.ts\n\n/**\n * This script handles the processing and execution of premoves\n *"
  },
  {
    "path": "src/client/scripts/esm/game/chess/selection.ts",
    "chars": 27585,
    "preview": "// src/client/scripts/esm/game/chess/selection.ts\n\n/**\n * This script tests for piece selection and keeps track of the s"
  },
  {
    "path": "src/client/scripts/esm/game/config.ts",
    "chars": 599,
    "preview": "// src/client/scripts/esm/game/config.ts\n\n/** This script contains our game configurations. */\n\nimport docutil from '../"
  },
  {
    "path": "src/client/scripts/esm/game/gui/boardeditor/actions/guiclearposition.ts",
    "chars": 2831,
    "preview": "// src/client/scripts/esm/game/gui/boardeditor/actions/guiclearposition.ts\n\n/**\n * Manages the GUI popup window for the "
  },
  {
    "path": "src/client/scripts/esm/game/gui/boardeditor/actions/guigamerules.ts",
    "chars": 19987,
    "preview": "// src/client/scripts/esm/game/gui/boardeditor/actions/guigamerules.ts\n\n/**\n * Manages the GUI popup window for the Game"
  },
  {
    "path": "src/client/scripts/esm/game/gui/boardeditor/actions/guiresetposition.ts",
    "chars": 2825,
    "preview": "// src/client/scripts/esm/game/gui/boardeditor/actions/guiresetposition.ts\n\n/**\n * Manages the GUI popup window for the "
  },
  {
    "path": "src/client/scripts/esm/game/gui/boardeditor/actions/guistartenginegame.ts",
    "chars": 6422,
    "preview": "// src/client/scripts/esm/game/gui/boardeditor/actions/guistartenginegame.ts\n\n/**\n * Manages the GUI popup window for th"
  },
  {
    "path": "src/client/scripts/esm/game/gui/boardeditor/actions/guistartlocalgame.ts",
    "chars": 2806,
    "preview": "// src/client/scripts/esm/game/gui/boardeditor/actions/guistartlocalgame.ts\n\n/**\n * Manages the GUI popup window for the"
  },
  {
    "path": "src/client/scripts/esm/game/gui/boardeditor/actions/loadposition/guiloadposition.ts",
    "chars": 5664,
    "preview": "// src/client/scripts/esm/game/gui/boardeditor/actions/loadposition/guiloadposition.ts\n\n/**\n * Manages the GUI popup win"
  },
  {
    "path": "src/client/scripts/esm/game/gui/boardeditor/actions/loadposition/guiloadpositionmodal.ts",
    "chars": 4721,
    "preview": "// src/client/scripts/esm/game/gui/boardeditor/actions/loadposition/guiloadpositionmodal.ts\n\n/**\n * Manages the confirma"
  },
  {
    "path": "src/client/scripts/esm/game/gui/boardeditor/actions/loadposition/guiloadpositionsavelist.ts",
    "chars": 12655,
    "preview": "// src/client/scripts/esm/game/gui/boardeditor/actions/loadposition/guiloadpositionsavelist.ts\n\n/**\n * Manages the saved"
  },
  {
    "path": "src/client/scripts/esm/game/gui/boardeditor/guiboardeditor.ts",
    "chars": 10040,
    "preview": "// src/client/scripts/esm/game/gui/boardeditor/guiboardeditor.ts\n\n/**\n * Manages the board editor GUI lifecycle: opening"
  },
  {
    "path": "src/client/scripts/esm/game/gui/boardeditor/guifloatingwindow.ts",
    "chars": 10135,
    "preview": "// src/client/scripts/esm/game/gui/boardeditor/guifloatingwindow.ts\n\n/**\n * Handles reusable floating window behavior in"
  },
  {
    "path": "src/client/scripts/esm/game/gui/boardeditor/guipalette.ts",
    "chars": 9270,
    "preview": "// src/client/scripts/esm/game/gui/boardeditor/guipalette.ts\n\n/**\n * Manages the piece palette in the board editor GUI.\n"
  },
  {
    "path": "src/client/scripts/esm/game/gui/boardeditor/guipositionheader.ts",
    "chars": 2488,
    "preview": "// src/client/scripts/esm/game/gui/boardeditor/guipositionheader.ts\n\n/**\n * Manages the active position name display, th"
  },
  {
    "path": "src/client/scripts/esm/game/gui/boardeditor/guitoolbar.ts",
    "chars": 1794,
    "preview": "// src/client/scripts/esm/game/gui/boardeditor/guitoolbar.ts\n\n/**\n * Manages the tool selection toolbar in the board edi"
  },
  {
    "path": "src/client/scripts/esm/game/gui/gui.ts",
    "chars": 1624,
    "preview": "// src/client/scripts/esm/game/gui/gui.ts\n\n/**\n * This script adds event listeners for our main overlay html element tha"
  },
  {
    "path": "src/client/scripts/esm/game/gui/guiclock.ts",
    "chars": 12074,
    "preview": "// src/client/scripts/esm/game/gui/guiclock.ts\n\nimport type { Game } from '../../../../../shared/chess/logic/gamefile.js"
  },
  {
    "path": "src/client/scripts/esm/game/gui/guidrawoffer.ts",
    "chars": 3512,
    "preview": "// src/client/scripts/esm/game/gui/guidrawoffer.ts\n\n/**\n * This script opens and closes our Draw Offer UI\n * on the bott"
  },
  {
    "path": "src/client/scripts/esm/game/gui/guigameinfo.ts",
    "chars": 21184,
    "preview": "// src/client/scripts/esm/game/gui/guigameinfo.ts\n\n/**\n * This script handles the game info bar, during a game,\n * displ"
  },
  {
    "path": "src/client/scripts/esm/game/gui/guiloading.ts",
    "chars": 633,
    "preview": "// src/client/scripts/esm/game/gui/guiloading.ts\n\n/**\n * This script hides the loading animation when the page fully loa"
  },
  {
    "path": "src/client/scripts/esm/game/gui/guinavigation.ts",
    "chars": 32016,
    "preview": "// src/client/scripts/esm/game/gui/guinavigation.ts\n\nimport type { BDCoords } from '../../../../../shared/chess/util/coo"
  },
  {
    "path": "src/client/scripts/esm/game/gui/guipause.ts",
    "chars": 12107,
    "preview": "// src/client/scripts/esm/game/gui/guipause.ts\n\n/**\n * This script handles our Pause menu.\n */\n\nimport moveutil from '.."
  },
  {
    "path": "src/client/scripts/esm/game/gui/guiplay.ts",
    "chars": 21936,
    "preview": "// src/client/scripts/esm/game/gui/guiplay.ts\n\n/**\n * This script handles our Play page, containing our invite creation "
  },
  {
    "path": "src/client/scripts/esm/game/gui/guipractice.ts",
    "chars": 17333,
    "preview": "// src/client/scripts/esm/game/gui/guipractice.ts\n\n/*\n * This script handles our Practice page, containing\n * our practi"
  },
  {
    "path": "src/client/scripts/esm/game/gui/guipromotion.ts",
    "chars": 4452,
    "preview": "// src/client/scripts/esm/game/gui/guipromotion.ts\n\n/**\n * This script handles our promotion menu, when\n * pawns reach t"
  },
  {
    "path": "src/client/scripts/esm/game/gui/guititle.ts",
    "chars": 2625,
    "preview": "// src/client/scripts/esm/game/gui/guititle.ts\n\n/**\n * This script handles our Title Screen\n */\n\nimport guiplay from './"
  },
  {
    "path": "src/client/scripts/esm/game/gui/loadingscreen.ts",
    "chars": 3548,
    "preview": "// src/client/scripts/esm/game/gui/loadingscreen.ts\n\n/**\n * This script manages the spinny pawn loading animation\n * whi"
  },
  {
    "path": "src/client/scripts/esm/game/gui/stats.ts",
    "chars": 2775,
    "preview": "// src/client/scripts/esm/game/gui/stats.ts\n\n/**\n * This script renders the stats in the corner of the screen (Similar t"
  },
  {
    "path": "src/client/scripts/esm/game/gui/style.ts",
    "chars": 4841,
    "preview": "// src/client/scripts/esm/game/gui/style.ts\n\n/**\n * Utility function for html elements and styles.\n *\n * It also keeps t"
  },
  {
    "path": "src/client/scripts/esm/game/gui/toast.ts",
    "chars": 3095,
    "preview": "// src/client/scripts/esm/game/gui/toast.ts\n\n/**\n * This script displays the toast (status message) on the bottom of the"
  },
  {
    "path": "src/client/scripts/esm/game/input.ts",
    "chars": 40270,
    "preview": "// src/client/scripts/esm/game/input.ts\n\n/**\n * This script can attach input listeners to individual elements.\n *\n * Typ"
  },
  {
    "path": "src/client/scripts/esm/game/main.ts",
    "chars": 2898,
    "preview": "// src/client/scripts/esm/game/main.ts\n\n/*\n * This is the main script. This is where the game begins running.\n\n * This i"
  },
  {
    "path": "src/client/scripts/esm/game/misc/controls.ts",
    "chars": 9454,
    "preview": "// src/client/scripts/esm/game/misc/controls.ts\n\n/**\n * This script controls the board navigation\n * via the WASD keys, "
  },
  {
    "path": "src/client/scripts/esm/game/misc/enginegame.ts",
    "chars": 14678,
    "preview": "// src/client/scripts/esm/game/misc/enginegame.ts\n\n// This module keeps track of the data of the engine game we are curr"
  },
  {
    "path": "src/client/scripts/esm/game/misc/gamesound.ts",
    "chars": 14349,
    "preview": "// src/client/scripts/esm/game/misc/gamesound.ts\n\n/**\n * This script is in charge of storing our audio\n * spritesheet, a"
  },
  {
    "path": "src/client/scripts/esm/game/misc/invites.ts",
    "chars": 16186,
    "preview": "// src/client/scripts/esm/game/misc/invites.ts\n\n/**\n * This script manages the invites on the Play page.\n */\n\nimport typ"
  },
  {
    "path": "src/client/scripts/esm/game/misc/keybinds.ts",
    "chars": 2654,
    "preview": "// src/client/scripts/esm/game/misc/keybinds.ts\n\n/**\n * This script will store the keybinds for various game actions.\n *"
  },
  {
    "path": "src/client/scripts/esm/game/misc/loadbalancer.ts",
    "chars": 6146,
    "preview": "// src/client/scripts/esm/game/misc/loadbalancer.ts\n\n/**\n * This script keeps track of our deltaTime, FPS, AFK status, a"
  },
  {
    "path": "src/client/scripts/esm/game/misc/onlinegame/afk.ts",
    "chars": 9551,
    "preview": "// src/client/scripts/esm/game/misc/onlinegame/afk.ts\n\n/**\n * This script keeps track of how long we have been afk in th"
  },
  {
    "path": "src/client/scripts/esm/game/misc/onlinegame/disconnect.ts",
    "chars": 3853,
    "preview": "// src/client/scripts/esm/game/misc/onlinegame/disconnect.ts\n\n/**\n * This script displays a countdown on screen, when ou"
  },
  {
    "path": "src/client/scripts/esm/game/misc/onlinegame/drawoffers.ts",
    "chars": 5843,
    "preview": "// src/client/scripts/esm/game/misc/onlinegame/drawoffers.ts\n\n/**\n * This script stores the logic surrounding draw exten"
  },
  {
    "path": "src/client/scripts/esm/game/misc/onlinegame/movesendreceive.ts",
    "chars": 6743,
    "preview": "// src/client/scripts/esm/game/misc/onlinegame/movesendreceive.ts\n\n/**\n * This script handles sending our move in online"
  },
  {
    "path": "src/client/scripts/esm/game/misc/onlinegame/onlinegame.ts",
    "chars": 16185,
    "preview": "// src/client/scripts/esm/game/misc/onlinegame/onlinegame.ts\n\n/**\n * This module keeps trap of the data of the onlinegam"
  },
  {
    "path": "src/client/scripts/esm/game/misc/onlinegame/onlinegamerouter.ts",
    "chars": 10354,
    "preview": "// src/client/scripts/esm/game/misc/onlinegame/onlinegamerouter.ts\n\nimport type { Game } from '../../../../../../shared/"
  },
  {
    "path": "src/client/scripts/esm/game/misc/onlinegame/resyncer.ts",
    "chars": 9890,
    "preview": "// src/client/scripts/esm/game/misc/onlinegame/resyncer.ts\n\n/**\n * This script handles game updates and recyning an onli"
  },
  {
    "path": "src/client/scripts/esm/game/misc/onlinegame/tabnameflash.ts",
    "chars": 3108,
    "preview": "// src/client/scripts/esm/game/misc/onlinegame/tabnameflash.ts\n\n/**\n * This script controls the flashing of the tab name"
  },
  {
    "path": "src/client/scripts/esm/game/misc/space.ts",
    "chars": 4839,
    "preview": "// src/client/scripts/esm/game/misc/space.ts\n\n/**\n * This script converts world-space coordinates to square coordinates,"
  },
  {
    "path": "src/client/scripts/esm/game/rendering/ColorFlowRenderer.ts",
    "chars": 8204,
    "preview": "// src/client/scripts/esm/game/rendering/ColorFlowRenderer.ts\n\n/**\n * A modular renderer that paints a color flow effect"
  }
]

// ... and 464 more files (download for full content)

About this extraction

This page contains the full source code of the Infinite-Chess/infinitechess.org GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 664 files (4.4 MB), approximately 1.2M tokens, and a symbol index with 3880 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!