From 1fce5ddb3b8453b1a6229b060652e47d22ef581b Mon Sep 17 00:00:00 2001 From: kkortes Date: Wed, 8 Apr 2026 18:38:41 +0200 Subject: [PATCH 001/200] cleanslate and move on --- CLAUDE.md | 83 + README.md | 57 - bun.lockb | Bin 142304 -> 2373 bytes components/Armory.html | 55 + components/Layout.html | 215 ++ components/Sidebar.html | 285 ++ components/Topbar.html | 31 + css/icons.css | 339 ++ css/index.css | 218 ++ css/stylecheat.css | 2297 ++++++++++++ eslint.config.js | 38 - index.html | 992 +++++ js/actions.js | 32 + js/app.js | 65 + js/audio.js | 58 + js/coin.js | 6 + js/combat-loop.js | 33 + src/ts/combat.ts => js/combat.js | 170 +- js/config.js | 10 + js/connectSocket.js | 56 + .../ABILITIES.ts => js/constants/ABILITIES.js | 80 +- js/constants/APP.js | 3 + js/constants/AVAILABLE_KEYS.js | 1 + .../constants/CHARACTERS.js | 33 +- .../constants/COMBAT_STATS.js | 0 js/constants/ELEMENTS.js | 7 + .../EQUIPMENT.ts => js/constants/EQUIPMENT.js | 26 +- .../FIGHTS.ts => js/constants/FIGHTS.js | 2 +- .../constants/RECRUITABLE_CHARACTERS.js | 6 +- src/constants/SFX.ts => js/constants/SFX.js | 2 +- .../constants/STATUS_EFFECTS.js | 2 +- js/constants/VFX.js | 14 + js/constants/WEAPON_TYPES.js | 1 + js/customEvent.js | 2 + js/dialog.js | 3 + js/dnd.js | 296 ++ js/entity.js | 13 + src/ts/equipment.ts => js/equipment.js | 59 +- src/helpers.ts => js/helpers.js | 143 +- js/keystrokes.js | 15 + js/level.js | 37 + js/lib/howler.js | 10 + js/lib/seedrandom.js | 9 + js/router.js | 122 + js/tooltip.js | 51 + src/ts/utils.ts => js/utils.js | 112 +- nodemodules | 1 + package.json | 47 +- pages/ability-scaling.html | 71 + pages/brawler-detail.html | 343 ++ pages/brawlers.html | 154 + pages/character-scaling.html | 63 + pages/debug.html | 44 + pages/equipment-scaling.html | 61 + pages/fight-detail.html | 140 + src/routes/+page.svelte => pages/home.html | 48 +- pages/random-duel.html | 68 + pages/reset-password.html | 31 + pages/the-arena.html | 262 ++ pages/vendor.html | 150 + patches/svelte-dnd-action@0.9.64.patch | 3177 ----------------- scripts/generate-component-typedefs.ts | 180 - scripts/icon.js | 139 - src/Iconice.d.ts | 1 - src/app.css | 79 - src/app.html | 19 - src/app.svelte.ts | 166 - src/components.d.ts | 77 - src/components/Accordion.svelte | 21 - src/components/Armory.svelte | 49 - src/components/Async.svelte | 19 - src/components/Authorization.svelte | 56 - src/components/Coin.svelte | 23 - src/components/Coins.svelte | 56 - src/components/Debug.svelte | 11 - src/components/DebugAppData.svelte | 106 - src/components/DevBar.svelte | 124 - src/components/EquipmentFilter.svelte | 57 - src/components/EquipmentLink.svelte | 78 - src/components/ForgotPassword.svelte | 35 - src/components/Frame.svelte | 41 - src/components/GameAudio.svelte | 81 - src/components/Headline.svelte | 30 - src/components/Hr.svelte | 17 - src/components/Loader.svelte | 34 - src/components/Login.svelte | 90 - src/components/Logo.svelte | 12 - src/components/MyLudus.svelte | 131 - src/components/RefillHealthTimer.svelte | 64 - src/components/Register.svelte | 38 - src/components/Topbar.svelte | 24 - src/components/Unauthorized.svelte | 35 - src/components/buttons/Clickable.svelte | 27 - src/components/buttons/Close.svelte | 10 - src/components/buttons/GoBack.svelte | 9 - src/components/buttons/Logout.svelte | 10 - src/components/character/AbilityBar.svelte | 139 - src/components/character/AbilityIcon.svelte | 65 - .../character/AbilityInventory.svelte | 78 - .../character/AbilitySelection.svelte | 304 -- .../character/CharacterAvatar.svelte | 24 - .../character/CharacterEquipment.svelte | 34 - src/components/character/CoreStats.svelte | 41 - src/components/character/Stats.svelte | 33 - .../combat/CharacterSelection.svelte | 66 - src/components/combat/CombatArena.svelte | 9 - .../combat/CombatAudioPlayer.svelte | 65 - .../combat/CombatantAbilityBar.svelte | 81 - .../combat/CombatantAudioPlayer.svelte | 49 - src/components/combat/CombatantCard.svelte | 213 -- src/components/combat/CombatantImage.svelte | 310 -- src/components/combat/HealthBar.svelte | 209 -- .../combat/ResultAnnouncement.svelte | 115 - src/components/combat/TeamBadge.svelte | 127 - src/components/combat/VictoryOrLoss.svelte | 62 - .../dialog/BasicConfirmation.svelte | 24 - src/components/form/Button.svelte | 85 - src/components/form/Checkbox.svelte | 25 - src/components/form/Dropdown.svelte | 41 - src/components/form/Input.svelte | 76 - .../global/AccountProgression.svelte | 46 - src/components/global/ClientClock.svelte | 13 - src/components/global/ConnectSocket.svelte | 22 - src/components/global/Dialog.svelte | 33 - src/components/global/InCombat.svelte | 68 - src/components/global/Keystrokes.svelte | 39 - src/components/global/Notifications.svelte | 106 - src/components/global/Overlay.svelte | 42 - src/components/overlays/CodeOfConduct.svelte | 50 - src/components/overlays/Combat.svelte | 145 - src/components/overlays/GameMenu.svelte | 111 - src/components/overlays/ReleaseNotes.svelte | 10 - src/components/tooltips/TooltipAbility.svelte | 137 - .../tooltips/TooltipEquipment.svelte | 83 - src/components/ui/Bar.svelte | 65 - src/components/ui/Icon.svelte | 52 - src/components/ui/Pill.svelte | 9 - src/components/ui/Spinner.svelte | 8 - src/components/ui/Tooltip.svelte | 114 - src/constants/APP.ts | 14 - src/constants/AVAILABLE_KEYS.ts | 34 - src/constants/ELEMENTS.ts | 50 - src/constants/ENV_VARS.ts | 19 - src/constants/VFX.ts | 53 - src/constants/WEAPON_TYPES.ts | 3 - src/crow.css | 74 - src/globals.d.ts | 19 - src/iconice-icons.json | 964 ----- src/routes/+error.svelte | 10 - src/routes/+layout.svelte | 371 -- src/routes/ability-scaling/+page.svelte | 35 - src/routes/brawlers/+page.svelte | 113 - .../brawlers/[characterIndex]/+page.svelte | 148 - src/routes/character-scaling/+page.svelte | 101 - src/routes/components/+page.svelte | 79 - src/routes/crow/+page.svelte | 82 - src/routes/debug/+page.svelte | 84 - src/routes/equipment-scaling/+page.svelte | 44 - src/routes/random-duel/+page.svelte | 56 - .../reset-password/[secret]/+page.svelte | 46 - src/routes/the-arena/+page.svelte | 154 - src/routes/the-arena/[fightId]/+page.svelte | 161 - src/routes/vendor/+page.svelte | 69 - src/ts/abilityEntity.ts | 17 - src/ts/actions.ts | 30 - src/ts/coin.ts | 9 - src/ts/customEvent.ts | 6 - src/ts/dialog.ts | 13 - src/ts/entity.ts | 22 - src/ts/level.ts | 48 - src/ts/loadLocalStorage.ts | 14 - src/ts/mediaQuery.ts | 34 - src/ts/use.ts | 259 -- src/types/ability.ts | 55 - src/types/character.ts | 31 - src/types/combat.ts | 37 - src/types/combatStats.ts | 56 - src/types/combatant.ts | 47 - src/types/common.ts | 4 - src/types/equipment.ts | 30 - src/types/sfx.ts | 10 - src/types/team.ts | 7 - src/types/vfx.ts | 12 - src/types/weaponType.ts | 1 - svelte.config.js | 31 - tsconfig.json | 25 - vercel.json | 5 + vite.config.js | 35 - 188 files changed, 7000 insertions(+), 12747 deletions(-) create mode 100644 CLAUDE.md delete mode 100644 README.md create mode 100644 components/Armory.html create mode 100644 components/Layout.html create mode 100644 components/Sidebar.html create mode 100644 components/Topbar.html create mode 100644 css/icons.css create mode 100644 css/index.css create mode 100644 css/stylecheat.css delete mode 100644 eslint.config.js create mode 100644 index.html create mode 100644 js/actions.js create mode 100644 js/app.js create mode 100644 js/audio.js create mode 100644 js/coin.js create mode 100644 js/combat-loop.js rename src/ts/combat.ts => js/combat.js (75%) create mode 100644 js/config.js create mode 100644 js/connectSocket.js rename src/constants/ABILITIES.ts => js/constants/ABILITIES.js (76%) create mode 100644 js/constants/APP.js create mode 100644 js/constants/AVAILABLE_KEYS.js rename src/constants/CHARACTERS.ts => js/constants/CHARACTERS.js (94%) rename src/constants/COMBAT_STATS.ts => js/constants/COMBAT_STATS.js (100%) create mode 100644 js/constants/ELEMENTS.js rename src/constants/EQUIPMENT.ts => js/constants/EQUIPMENT.js (87%) rename src/constants/FIGHTS.ts => js/constants/FIGHTS.js (99%) rename src/constants/RECRUITABLE_CHARACTERS.ts => js/constants/RECRUITABLE_CHARACTERS.js (98%) rename src/constants/SFX.ts => js/constants/SFX.js (99%) rename src/constants/STATUS_EFFECTS.ts => js/constants/STATUS_EFFECTS.js (97%) create mode 100644 js/constants/VFX.js create mode 100644 js/constants/WEAPON_TYPES.js create mode 100644 js/customEvent.js create mode 100644 js/dialog.js create mode 100644 js/dnd.js create mode 100644 js/entity.js rename src/ts/equipment.ts => js/equipment.js (51%) rename src/helpers.ts => js/helpers.js (55%) create mode 100644 js/keystrokes.js create mode 100644 js/level.js create mode 100644 js/lib/howler.js create mode 100644 js/lib/seedrandom.js create mode 100644 js/router.js create mode 100644 js/tooltip.js rename src/ts/utils.ts => js/utils.js (60%) create mode 120000 nodemodules create mode 100644 pages/ability-scaling.html create mode 100644 pages/brawler-detail.html create mode 100644 pages/brawlers.html create mode 100644 pages/character-scaling.html create mode 100644 pages/debug.html create mode 100644 pages/equipment-scaling.html create mode 100644 pages/fight-detail.html rename src/routes/+page.svelte => pages/home.html (60%) create mode 100644 pages/random-duel.html create mode 100644 pages/reset-password.html create mode 100644 pages/the-arena.html create mode 100644 pages/vendor.html delete mode 100644 patches/svelte-dnd-action@0.9.64.patch delete mode 100644 scripts/generate-component-typedefs.ts delete mode 100644 scripts/icon.js delete mode 100644 src/Iconice.d.ts delete mode 100644 src/app.css delete mode 100644 src/app.html delete mode 100644 src/app.svelte.ts delete mode 100644 src/components.d.ts delete mode 100644 src/components/Accordion.svelte delete mode 100644 src/components/Armory.svelte delete mode 100644 src/components/Async.svelte delete mode 100644 src/components/Authorization.svelte delete mode 100644 src/components/Coin.svelte delete mode 100644 src/components/Coins.svelte delete mode 100644 src/components/Debug.svelte delete mode 100644 src/components/DebugAppData.svelte delete mode 100644 src/components/DevBar.svelte delete mode 100644 src/components/EquipmentFilter.svelte delete mode 100644 src/components/EquipmentLink.svelte delete mode 100644 src/components/ForgotPassword.svelte delete mode 100644 src/components/Frame.svelte delete mode 100644 src/components/GameAudio.svelte delete mode 100644 src/components/Headline.svelte delete mode 100644 src/components/Hr.svelte delete mode 100644 src/components/Loader.svelte delete mode 100644 src/components/Login.svelte delete mode 100644 src/components/Logo.svelte delete mode 100644 src/components/MyLudus.svelte delete mode 100644 src/components/RefillHealthTimer.svelte delete mode 100644 src/components/Register.svelte delete mode 100644 src/components/Topbar.svelte delete mode 100644 src/components/Unauthorized.svelte delete mode 100644 src/components/buttons/Clickable.svelte delete mode 100644 src/components/buttons/Close.svelte delete mode 100644 src/components/buttons/GoBack.svelte delete mode 100644 src/components/buttons/Logout.svelte delete mode 100644 src/components/character/AbilityBar.svelte delete mode 100644 src/components/character/AbilityIcon.svelte delete mode 100644 src/components/character/AbilityInventory.svelte delete mode 100644 src/components/character/AbilitySelection.svelte delete mode 100644 src/components/character/CharacterAvatar.svelte delete mode 100644 src/components/character/CharacterEquipment.svelte delete mode 100644 src/components/character/CoreStats.svelte delete mode 100644 src/components/character/Stats.svelte delete mode 100644 src/components/combat/CharacterSelection.svelte delete mode 100644 src/components/combat/CombatArena.svelte delete mode 100644 src/components/combat/CombatAudioPlayer.svelte delete mode 100644 src/components/combat/CombatantAbilityBar.svelte delete mode 100644 src/components/combat/CombatantAudioPlayer.svelte delete mode 100644 src/components/combat/CombatantCard.svelte delete mode 100644 src/components/combat/CombatantImage.svelte delete mode 100644 src/components/combat/HealthBar.svelte delete mode 100644 src/components/combat/ResultAnnouncement.svelte delete mode 100644 src/components/combat/TeamBadge.svelte delete mode 100644 src/components/combat/VictoryOrLoss.svelte delete mode 100644 src/components/dialog/BasicConfirmation.svelte delete mode 100644 src/components/form/Button.svelte delete mode 100644 src/components/form/Checkbox.svelte delete mode 100644 src/components/form/Dropdown.svelte delete mode 100644 src/components/form/Input.svelte delete mode 100644 src/components/global/AccountProgression.svelte delete mode 100644 src/components/global/ClientClock.svelte delete mode 100644 src/components/global/ConnectSocket.svelte delete mode 100644 src/components/global/Dialog.svelte delete mode 100644 src/components/global/InCombat.svelte delete mode 100644 src/components/global/Keystrokes.svelte delete mode 100644 src/components/global/Notifications.svelte delete mode 100644 src/components/global/Overlay.svelte delete mode 100644 src/components/overlays/CodeOfConduct.svelte delete mode 100644 src/components/overlays/Combat.svelte delete mode 100644 src/components/overlays/GameMenu.svelte delete mode 100644 src/components/overlays/ReleaseNotes.svelte delete mode 100644 src/components/tooltips/TooltipAbility.svelte delete mode 100644 src/components/tooltips/TooltipEquipment.svelte delete mode 100644 src/components/ui/Bar.svelte delete mode 100644 src/components/ui/Icon.svelte delete mode 100644 src/components/ui/Pill.svelte delete mode 100644 src/components/ui/Spinner.svelte delete mode 100644 src/components/ui/Tooltip.svelte delete mode 100644 src/constants/APP.ts delete mode 100644 src/constants/AVAILABLE_KEYS.ts delete mode 100644 src/constants/ELEMENTS.ts delete mode 100644 src/constants/ENV_VARS.ts delete mode 100644 src/constants/VFX.ts delete mode 100644 src/constants/WEAPON_TYPES.ts delete mode 100644 src/crow.css delete mode 100644 src/globals.d.ts delete mode 100644 src/iconice-icons.json delete mode 100644 src/routes/+error.svelte delete mode 100644 src/routes/+layout.svelte delete mode 100644 src/routes/ability-scaling/+page.svelte delete mode 100644 src/routes/brawlers/+page.svelte delete mode 100644 src/routes/brawlers/[characterIndex]/+page.svelte delete mode 100644 src/routes/character-scaling/+page.svelte delete mode 100644 src/routes/components/+page.svelte delete mode 100644 src/routes/crow/+page.svelte delete mode 100644 src/routes/debug/+page.svelte delete mode 100644 src/routes/equipment-scaling/+page.svelte delete mode 100644 src/routes/random-duel/+page.svelte delete mode 100644 src/routes/reset-password/[secret]/+page.svelte delete mode 100644 src/routes/the-arena/+page.svelte delete mode 100644 src/routes/the-arena/[fightId]/+page.svelte delete mode 100644 src/routes/vendor/+page.svelte delete mode 100644 src/ts/abilityEntity.ts delete mode 100644 src/ts/actions.ts delete mode 100644 src/ts/coin.ts delete mode 100644 src/ts/customEvent.ts delete mode 100644 src/ts/dialog.ts delete mode 100644 src/ts/entity.ts delete mode 100644 src/ts/level.ts delete mode 100644 src/ts/loadLocalStorage.ts delete mode 100644 src/ts/mediaQuery.ts delete mode 100644 src/ts/use.ts delete mode 100644 src/types/ability.ts delete mode 100644 src/types/character.ts delete mode 100644 src/types/combat.ts delete mode 100644 src/types/combatStats.ts delete mode 100644 src/types/combatant.ts delete mode 100644 src/types/common.ts delete mode 100644 src/types/equipment.ts delete mode 100644 src/types/sfx.ts delete mode 100644 src/types/team.ts delete mode 100644 src/types/vfx.ts delete mode 100644 src/types/weaponType.ts delete mode 100644 svelte.config.js delete mode 100644 tsconfig.json create mode 100644 vercel.json delete mode 100644 vite.config.js diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000..c10ec685 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,83 @@ +# Battle Brawlers — Vibe + Stylecheat Rewrite + +## Overview + +Strategy auto-battler rewritten from SvelteKit + Tailwind to Vibe + Stylecheat. Branch: `chore/major-rewrite-to-stylecheat-and-vibe`. + +## Reference + +The **`main` branch** contains the original SvelteKit implementation. Use `git stash && git checkout main` to inspect original components, then `git checkout chore/major-rewrite-to-stylecheat-and-vibe && git stash pop` to return. The original is the source of truth for all functionality and design. + +## Stack + +- **Runtime**: Vibe (`@ape-egg/vibe`) — runtime-first reactive framework +- **CSS**: Stylecheat (hybrid build) — attribute-based CSS framework +- **Server**: `bunx live-server` — no build step +- **Packages**: async-await-websockets, howler, seedrandom +- **Vibe source**: Symlinked from `/Users/kortes/Projects/webdev/vibe/nodemodules/@ape-egg/vibe` + +## Conventions + +- **No `class=""` or `style=""` attributes** (except dynamic Vibe `@[...]` values for computed widths/positions) +- **Attribute-based styling**: use Stylecheat attributes (`g-4`, `text-sm`, `vertical`, `primary`, etc.) or custom attributes +- **Scoped styles**: each page/component has a ` + diff --git a/components/Layout.html b/components/Layout.html new file mode 100644 index 00000000..78667dd1 --- /dev/null +++ b/components/Layout.html @@ -0,0 +1,215 @@ + + +
+

Connecting to server...

+
+ + + +
+
+ + +
+ +
+ @[authError] +
+ + + +
+ + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + +

+ Already have an account? Log in +

+
+ +
+ + +
+
+ + + + + + +
+ + +
+ +

Loading...

+
+
+
+ + + + +
+ + + +
diff --git a/components/Sidebar.html b/components/Sidebar.html new file mode 100644 index 00000000..261e7861 --- /dev/null +++ b/components/Sidebar.html @@ -0,0 +1,285 @@ +
+ +
+ + + + Fighting.. + + + + + View outcome + + +
+ + + +
MY LUDUS
+ level @[sidebarLevel] +
+
+ + + + + Coins + + - + + + + + @[Math.floor(coins / 100)]× + + + @[coins % 100]× + + + + + + + + + Experience + + + +
+
+ @[sidebarXpCurrent] / @[sidebarXpNeeded] +
+ +
+ + + + + Medical area + + +
+
+ @[String(Math.floor(healTimer / 60)).padStart(2, '0')]:@[String(healTimer % 60).padStart(2, '0')] +
+
+
+ +
+ + + +
MY BRAWLERS
+ @[sidebarChars.length] +
+
+ +
+ + + + You have no brawlers yet. + + + + + + + +
+
+ +
+
+ +
+
+
+ + @[schar.name] + @[schar.healthPct]% + +
+
+
+
+
+ +
+
+ + +
diff --git a/components/Topbar.html b/components/Topbar.html new file mode 100644 index 00000000..f3cc64f6 --- /dev/null +++ b/components/Topbar.html @@ -0,0 +1,31 @@ + + + + + diff --git a/css/icons.css b/css/icons.css new file mode 100644 index 00000000..ee2c525a --- /dev/null +++ b/css/icons.css @@ -0,0 +1,339 @@ +/* Battle Brawlers game icons */ +:root { + --icon-2h: url("data:image/svg+xml;utf8,"); + --icon-checkmark: url("data:image/svg+xml;utf8,"); + --icon-cross: url("data:image/svg+xml;utf8,"); + --icon-dark: url("data:image/svg+xml;utf8,"); + --icon-down: url("data:image/svg+xml;utf8,"); + --icon-info: url("data:image/svg+xml;utf8,"); + --icon-error: url("data:image/svg+xml;utf8,"); + --icon-left: url("data:image/svg+xml;utf8,"); + --icon-light: url("data:image/svg+xml;utf8,"); + --icon-logo-apeegg-simple: url("data:image/svg+xml;utf8,"); + --icon-logo-apeegg: url("data:image/svg+xml;utf8,"); + --icon-menu: url("data:image/svg+xml;utf8,"); + --icon-right: url("data:image/svg+xml;utf8,"); + --icon-spinner-circle: url("data:image/svg+xml;utf8,"); + --icon-success: url("data:image/svg+xml;utf8,"); + --icon-spinner-inner: url("data:image/svg+xml;utf8,"); + --icon-warning: url("data:image/svg+xml;utf8,"); + --icon-up: url("data:image/svg+xml;utf8,"); + --icon-stun: url("data:image/svg+xml;utf8,"); + --icon-1h: url("data:image/svg+xml;utf8,"); + --icon-1h1h: url("data:image/svg+xml;utf8,"); + --icon-spin: url("data:image/svg+xml;utf8,"); + --icon-isBlocking: url("data:image/svg+xml;utf8,"); + --icon-punch: url("data:image/svg+xml;utf8,"); + --icon-slash: url("data:image/svg+xml;utf8,"); + --icon-stab: url("data:image/svg+xml;utf8,"); + --icon-block: url("data:image/svg+xml;utf8,"); + --icon-cheese: url("data:image/svg+xml;utf8,"); + --icon-bite: url("data:image/svg+xml;utf8,"); + --icon-slam: url("data:image/svg+xml;utf8,"); + --icon-shieldBash: url("data:image/svg+xml;utf8,"); + --icon-bowshot: url("data:image/svg+xml;utf8,"); + --icon-lacerate: url("data:image/svg+xml;utf8,"); + --icon-kick: url("data:image/svg+xml;utf8,"); + --icon-whirlwind: url("data:image/svg+xml;utf8,"); + --icon-fillArmor: url("data:image/svg+xml;utf8,"); + --icon-claw: url("data:image/svg+xml;utf8,"); + --icon-demoShout: url("data:image/svg+xml;utf8,"); + --icon-damage: url("data:image/svg+xml;utf8,"); + --icon-victory: url("data:image/svg+xml;utf8,"); + --icon-defeat: url("data:image/svg+xml;utf8,"); + --icon-maxArmor: url("data:image/svg+xml;utf8,"); + --icon-maxHealth: url("data:image/svg+xml;utf8,"); + --icon-coin: url("data:image/svg+xml;utf8,"); + --icon-magicChance: url("data:image/svg+xml;utf8,"); + --icon-dice: url("data:image/svg+xml;utf8,"); + --icon-dodgeChance: url("data:image/svg+xml;utf8,"); + --icon-criticalChance: url("data:image/svg+xml;utf8,"); + --icon-criticalDamage: url("data:image/svg+xml;utf8,"); + --icon-bleeding: url("data:image/svg+xml;utf8,"); + --icon-concussed: url("data:image/svg+xml;utf8,"); + --icon-exposed: url("data:image/svg+xml;utf8,"); + --icon-stunned: url("data:image/svg+xml;utf8,"); + --icon-vulnerable: url("data:image/svg+xml;utf8,"); + --icon-wounded: url("data:image/svg+xml;utf8,"); + --icon-blockChance: url("data:image/svg+xml;utf8,"); +} + +icon[2h], +icon.2h { + --icon: var(--icon-2h); +} + +icon[checkmark], +icon.checkmark { + --icon: var(--icon-checkmark); +} + +icon[cross], +icon.cross { + --icon: var(--icon-cross); +} + +icon[dark], +icon.dark { + --icon: var(--icon-dark); +} + +icon[down], +icon.down { + --icon: var(--icon-down); +} + +icon[info], +icon.info { + --icon: var(--icon-info); +} + +icon[error], +icon.error { + --icon: var(--icon-error); +} + +icon[left], +icon.left { + --icon: var(--icon-left); +} + +icon[light], +icon.light { + --icon: var(--icon-light); +} + +icon[logo-apeegg-simple], +icon.logo-apeegg-simple { + --icon: var(--icon-logo-apeegg-simple); +} + +icon[logo-apeegg], +icon.logo-apeegg { + --icon: var(--icon-logo-apeegg); +} + +icon[menu], +icon.menu { + --icon: var(--icon-menu); +} + +icon[right], +icon.right { + --icon: var(--icon-right); +} + +icon[spinner-circle], +icon.spinner-circle { + --icon: var(--icon-spinner-circle); +} + +icon[success], +icon.success { + --icon: var(--icon-success); +} + +icon[spinner-inner], +icon.spinner-inner { + --icon: var(--icon-spinner-inner); +} + +icon[warning], +icon.warning { + --icon: var(--icon-warning); +} + +icon[up], +icon.up { + --icon: var(--icon-up); +} + +icon[stun], +icon.stun { + --icon: var(--icon-stun); +} + +icon[1h], +icon.1h { + --icon: var(--icon-1h); +} + +icon[1h1h], +icon.1h1h { + --icon: var(--icon-1h1h); +} + +icon[spin], +icon.spin { + --icon: var(--icon-spin); +} + +icon[isBlocking], +icon.isBlocking { + --icon: var(--icon-isBlocking); +} + +icon[punch], +icon.punch { + --icon: var(--icon-punch); +} + +icon[slash], +icon.slash { + --icon: var(--icon-slash); +} + +icon[stab], +icon.stab { + --icon: var(--icon-stab); +} + +icon[block], +icon.block { + --icon: var(--icon-block); +} + +icon[cheese], +icon.cheese { + --icon: var(--icon-cheese); +} + +icon[bite], +icon.bite { + --icon: var(--icon-bite); +} + +icon[slam], +icon.slam { + --icon: var(--icon-slam); +} + +icon[shieldBash], +icon.shieldBash { + --icon: var(--icon-shieldBash); +} + +icon[bowshot], +icon.bowshot { + --icon: var(--icon-bowshot); +} + +icon[lacerate], +icon.lacerate { + --icon: var(--icon-lacerate); +} + +icon[kick], +icon.kick { + --icon: var(--icon-kick); +} + +icon[whirlwind], +icon.whirlwind { + --icon: var(--icon-whirlwind); +} + +icon[fillArmor], +icon.fillArmor { + --icon: var(--icon-fillArmor); +} + +icon[claw], +icon.claw { + --icon: var(--icon-claw); +} + +icon[demoShout], +icon.demoShout { + --icon: var(--icon-demoShout); +} + +icon[damage], +icon.damage { + --icon: var(--icon-damage); +} + +icon[victory], +icon.victory { + --icon: var(--icon-victory); +} + +icon[defeat], +icon.defeat { + --icon: var(--icon-defeat); +} + +icon[maxArmor], +icon.maxArmor { + --icon: var(--icon-maxArmor); +} + +icon[maxHealth], +icon.maxHealth { + --icon: var(--icon-maxHealth); +} + +icon[coin], +icon.coin { + --icon: var(--icon-coin); +} + +icon[magicChance], +icon.magicChance { + --icon: var(--icon-magicChance); +} + +icon[dice], +icon.dice { + --icon: var(--icon-dice); +} + +icon[dodgeChance], +icon.dodgeChance { + --icon: var(--icon-dodgeChance); +} + +icon[criticalChance], +icon.criticalChance { + --icon: var(--icon-criticalChance); +} + +icon[criticalDamage], +icon.criticalDamage { + --icon: var(--icon-criticalDamage); +} + +icon[bleeding], +icon.bleeding { + --icon: var(--icon-bleeding); +} + +icon[concussed], +icon.concussed { + --icon: var(--icon-concussed); +} + +icon[exposed], +icon.exposed { + --icon: var(--icon-exposed); +} + +icon[stunned], +icon.stunned { + --icon: var(--icon-stunned); +} + +icon[vulnerable], +icon.vulnerable { + --icon: var(--icon-vulnerable); +} + +icon[wounded], +icon.wounded { + --icon: var(--icon-wounded); +} + +icon[blockChance], +icon.blockChance { + --icon: var(--icon-blockChance); +} diff --git a/css/index.css b/css/index.css new file mode 100644 index 00000000..67e710fc --- /dev/null +++ b/css/index.css @@ -0,0 +1,218 @@ +@import url('https://fonts.googleapis.com/css2?family=Fira+Sans+Condensed:wght@300;400;500;600;700&family=Cinzel:wght@400;500;600;700&family=Alfa+Slab+One&family=Bree+Serif&display=swap'); +@import url('/css/stylecheat.css'); +@import url('/css/icons.css'); + +/* Game theme — overrides stylecheat's default theme tokens */ +:root { + --unit: 4px; + --font-sans: 'Fira Sans Condensed', 'Trebuchet MS', sans-serif; + --radius: 6px; + + --background: oklch(0.94 0.02 60); + --foreground: oklch(0.2 0 0); + --muted: oklch(0.7 0.03 50); + --muted-foreground: oklch(0.5 0.03 50); + --border: oklch(0.85 0.02 60); + --card: oklch(0.97 0.01 60); + --card-foreground: oklch(0.2 0 0); + + --primary: oklch(0.6 0.15 70); + --primary-foreground: #fff; + --secondary: oklch(0.55 0.05 50); + --destructive: oklch(0.55 0.2 25); + + --sidebar: oklch(0.2 0.03 50); + --sidebar-foreground: oklch(0.85 0.02 60); + --sidebar-primary: oklch(0.6 0.15 70); + + /* Game-specific tokens */ + --font-heading: 'Cinzel', serif; + --font-display: 'Alfa Slab One', cursive; + --font-accent: 'Bree Serif', serif; +} + +[dark] { + --background: oklch(0.15 0.01 60); + --foreground: oklch(0.9 0 0); + --muted-foreground: oklch(0.6 0.02 50); + --border: oklch(0.3 0.02 60); + --card: oklch(0.2 0.01 60); + --card-foreground: oklch(0.9 0 0); +} + +/* Base overrides — game uses Cinzel for headings, not stylecheat default */ +html { + font-family: var(--font-sans); + overflow-x: hidden; + overflow-y: scroll; +} + +body { + background-image: url('/static/images/parchment-bg-2250x1500.jpg'); + background-size: cover; + background-position: center; + background-attachment: fixed; +} + +h1, h2, h3, h4, h5, h6 { + font-family: var(--font-heading); + font-weight: 500; + color: var(--foreground); +} + +a { + color: var(--primary); + text-decoration: none; +} + +a:hover { + text-decoration: underline; +} + +/* Game-specific font attributes */ +[cinzel] { font-family: var(--font-heading); } +[alfa-slab] { font-family: var(--font-display); } +[bree-serif] { font-family: var(--font-accent); } + +/* Game-specific modifiers */ +[glass] { + border-radius: var(--radius); + border: 1px solid rgba(255, 255, 255, 0.5); + background: rgba(255, 255, 255, 0.5); + box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); + backdrop-filter: blur(4px); +} + +[fat-number] { + filter: drop-shadow(0 1.2px 1.2px rgba(0, 0, 0, 0.8)); + -webkit-text-stroke: 0.5px black; +} + +/* Text utilities (not in Stylecheat) */ +[text-sm] { font-size: 0.875rem; } +[text-xs] { font-size: 0.75rem; } +[text-lg] { font-size: 1.125rem; } +[text-muted] { color: var(--muted-foreground); } +[text-center] { text-align: center; } + +/* Font utilities */ +[font-bold] { font-weight: 700; } +[font-medium] { font-weight: 500; } + +/* Flex utilities */ +[flex-none] { flex: none; } + +/* Scrollbar */ +::-webkit-scrollbar { width: 10px; } +::-webkit-scrollbar-track { background: oklch(0.85 0.01 50); } +::-webkit-scrollbar-thumb { background: oklch(0.55 0.01 50); } +::-webkit-scrollbar-thumb:hover { opacity: 0.9; } + +/* Custom game elements */ +page-content { + display: block; + width: 100%; +} + +spinner { + display: inline-block; + width: 20px; + height: 20px; + border: 2px solid var(--border); + border-top-color: var(--primary); + border-radius: 50%; + animation: spin 600ms linear infinite; +} + +@keyframes spin { + to { transform: rotate(360deg); } +} + +/* Health bar — game custom */ +health-bar { + display: block; + height: 6px; + background: rgba(0, 0, 0, 0.2); + border-radius: 3px; + overflow: hidden; +} + +health-bar > div { + height: 100%; + background: oklch(0.65 0.2 145); + transition: width 200ms ease; +} + +/* Progress bar */ +progress-bar { + display: block; + height: 8px; + background: var(--border); + border-radius: 4px; + overflow: hidden; +} + +progress-bar > div { + height: 100%; + background: var(--primary); + transition: width 200ms ease; +} + +/* Combat ring */ +[combat-ring] { + position: relative; + width: 500px; + height: 500px; + margin: 0 auto; +} + +/* Notification/overlay animations */ +@keyframes slide-in { + from { transform: translateX(100%); opacity: 0; } + to { transform: translateX(0); opacity: 1; } +} + +@keyframes fade-in { + from { opacity: 0; } + to { opacity: 1; } +} + +/* Tab styling — active tab matches nav[data-active] to a[data-tab] */ +nav[data-active=""] a[data-tab=""], +nav[data-active="the-arena"] a[data-tab="the-arena"], +nav[data-active="random-duel"] a[data-tab="random-duel"], +nav[data-active="vendor"] a[data-tab="vendor"], +nav[data-active="brawlers"] a[data-tab="brawlers"] { + background: white; + color: var(--foreground); + border-color: var(--border); +} + +/* DnD-specific styles */ +[dnd-zone] { + min-height: 40px; +} + +[dnd-item] { + cursor: grab; + user-select: none; + touch-action: none; +} + +[dnd-item]:active { + cursor: grabbing; +} + +[dnd-ghost] { + cursor: grabbing; +} + +[dnd-placeholder] { + pointer-events: none; +} + +/* Responsive */ +@media (max-width: 768px) { + [sm-hide] { display: none; } +} + diff --git a/css/stylecheat.css b/css/stylecheat.css new file mode 100644 index 00000000..948338f3 --- /dev/null +++ b/css/stylecheat.css @@ -0,0 +1,2297 @@ +@layer base, components, utilities; + +@layer components { +@property --n { + syntax: ''; + inherits: true; + initial-value: 1; +} +@keyframes z-fade-scale { + 0% { + opacity: 0; + scale: 0.5; + } + + 50% { + opacity: 1; + scale: 1; + } + + 100% { + opacity: 0; + scale: 2; + } +} +@keyframes z-pointer { + 0%, + 100% { + pointer-events: none; + } + + 50% { + pointer-events: auto; + } +} +@keyframes grid-pan-y { + to { + background-position-y: + calc(var(--gy) - (var(--n) - 1) * round(nearest, 80cqh, var(--grid-size))), + calc(var(--gy) - (var(--n) - 1) * round(nearest, 80cqh, var(--grid-size))), + calc(var(--gy) - (var(--n) - 1) * round(nearest, 50cqh, calc(var(--grid-size) * 0.5))), + calc(var(--gy) - (var(--n) - 1) * round(nearest, 50cqh, calc(var(--grid-size) * 0.5))); + } +} +@keyframes grid-pan-x { + to { + background-position-x: + calc(var(--gx) - (var(--n) - 1) * round(nearest, 80cqw, var(--grid-size))), + calc(var(--gx) - (var(--n) - 1) * round(nearest, 80cqw, var(--grid-size))), + calc(var(--gx) - (var(--n) - 1) * round(nearest, 50cqw, calc(var(--grid-size) * 0.5))), + calc(var(--gx) - (var(--n) - 1) * round(nearest, 50cqw, calc(var(--grid-size) * 0.5))); + } +} +@keyframes grid-zoom-out { + 0% { + background-size: + var(--grid-size) var(--grid-size), + var(--grid-size) var(--grid-size), + calc(var(--grid-size) * 0.5) calc(var(--grid-size) * 0.5), + calc(var(--grid-size) * 0.5) calc(var(--grid-size) * 0.5); + opacity: 1; + background-position: + var(--gx) var(--gy), + var(--gx) var(--gy), + var(--gx) var(--gy), + var(--gx) var(--gy); + } + + 97% { + opacity: 0; + } + + 100% { + background-size: + calc(var(--grid-size) * 2) calc(var(--grid-size) * 2), + calc(var(--grid-size) * 2) calc(var(--grid-size) * 2), + var(--grid-size) var(--grid-size), + var(--grid-size) var(--grid-size); + opacity: 0; + background-position: + var(--zx) var(--zy), + var(--zx) var(--zy), + var(--zx-m) var(--zy-m), + var(--zx-m) var(--zy-m); + } +} +@keyframes grid-zoom-in { + 0% { + background-size: + calc(var(--grid-size) * 0.5) calc(var(--grid-size) * 0.5), + calc(var(--grid-size) * 0.5) calc(var(--grid-size) * 0.5), + calc(var(--grid-size) * 0.25) calc(var(--grid-size) * 0.25), + calc(var(--grid-size) * 0.25) calc(var(--grid-size) * 0.25); + opacity: 0; + background-position: + var(--gx) var(--gy), + var(--gx) var(--gy), + var(--gx) var(--gy), + var(--gx) var(--gy); + } + + 97% { + opacity: 1; + } + + 100% { + background-size: + var(--grid-size) var(--grid-size), + var(--grid-size) var(--grid-size), + calc(var(--grid-size) * 0.5) calc(var(--grid-size) * 0.5), + calc(var(--grid-size) * 0.5) calc(var(--grid-size) * 0.5); + opacity: 1; + background-position: + var(--zx) var(--zy), + var(--zx) var(--zy), + var(--zx-m) var(--zy-m), + var(--zx-m) var(--zy-m); + } +} + +.stylecheat, +[stylecheat] { +/* ---- A.css ---- */ +.a:is(.primary, .secondary, .tertiary, [primary], [secondary], [tertiary]), +a:is(.primary, .secondary, .tertiary, [primary], [secondary], [tertiary]) { + transition: all 100ms ease-in-out; + text-decoration: none; + + &.primary, + &[primary] { + color: oklch(from var(--primary) calc(l - 0.2 * var(--theme-sign)) c h); + + &:hover, + &:focus { + color: oklch(from var(--primary) calc(l - 0.1 * var(--theme-sign)) c h); + } + } + + &.secondary, + &[secondary] { + color: var(--secondary-foreground); + + &:hover, + &:focus { + color: oklch(from var(--secondary-foreground) calc(l * (1 - 0.1 * var(--theme-sign))) c h); + } + } + + &.tertiary, + &[tertiary] { + /* Start - Button tertiary styles */ + display: flex; + border-radius: min(calc(var(--radius) - calc(var(--unit) * 1)), calc(var(--unit) * 1)); + padding: calc(var(--unit) * 2) calc(var(--unit) * 3); + line-height: calc(var(--unit) * 4); + outline-offset: 0; + > * { + margin-inline: calc(-1 * var(--unit)); + } + /* End - Button tertiary styles */ + + background-color: transparent; + color: var(--secondary-foreground); + outline-color: transparent; + + &:hover { + background-color: oklch(from var(--secondary) calc(l * (1 - 0.1 * var(--theme-sign))) c h); + color: oklch(from var(--secondary-foreground) calc(l * (1 - 0.1 * var(--theme-sign))) c h); + outline-color: oklch(from var(--secondary) calc(l * (1 - 0.075 * var(--theme-sign))) c h); + } + + &:active { + outline-color: transparent; + } + + &:focus-visible { + outline-width: 3px; + outline-color: oklch(from var(--primary) calc(l * (1 - 0.075 * var(--theme-sign))) c h); + } + } + + &:active { + transform: translateY(1px); + } + + &:focus-visible { + outline-width: 3px; + outline-color: oklch(from var(--primary) calc(l/2 + 0.4) c h); + } +} +/* ---- Accordion.css ---- */ +.accordion, +accordion { + display: grid; + width: 100%; + grid-template-rows: 0fr; + transition: grid-template-rows 200ms ease-out; +} + +.accordion:is(.open), +accordion[open] { + grid-template-rows: 1fr; +} + +.accordion > .accordion-content, +accordion > accordion-content { + overflow: hidden; +} +/* ---- Badge.css ---- */ +.badge, +badge { + display: inline-flex; + align-items: center; + justify-content: center; + gap: calc(var(--unit) * 1); + padding: calc(var(--unit) * 0.5) calc(var(--unit) * 2); + border-radius: 9999px; + font-size: calc(var(--unit) * 3); + font-weight: 500; + white-space: nowrap; + width: fit-content; + overflow: hidden; + + background-color: var(--primary); + color: var(--primary-foreground); +} + +.badge.secondary, +.badge[secondary], +badge.secondary, +badge[secondary] { + background-color: var(--secondary); + color: var(--secondary-foreground); +} + +.badge.destructive, +.badge[destructive], +badge.destructive, +badge[destructive] { + background-color: var(--destructive); + color: white; +} + +.badge.outline, +.badge[outline], +badge.outline, +badge[outline] { + background-color: transparent; + color: var(--foreground); + box-shadow: inset 0 0 0 1px var(--border); +} + +.badge.ghost, +.badge[ghost], +badge.ghost, +badge[ghost] { + background-color: transparent; + color: var(--foreground); +} +/* ---- Breadcrumb.css ---- */ +.breadcrumb, +breadcrumb { + display: flex; + align-items: center; + gap: calc(var(--unit) * 1); + font-size: var(--unit-size, inherit); + color: var(--muted-foreground); +} + +.breadcrumb .current, +.breadcrumb [current], +breadcrumb .current, +breadcrumb [current] { + color: var(--foreground); +} + +.breadcrumb > * + *::before, +breadcrumb > * + *::before { + content: '/'; + margin-right: calc(var(--unit) * 1); + color: var(--muted-foreground); +} +/* ---- Button.css ---- */ +.button:is(.primary, .secondary, .tertiary, [primary], [secondary], [tertiary]), +button:is(.primary, .secondary, .tertiary, [primary], [secondary], [tertiary]) { + display: flex; + align-items: center; + justify-content: center; + white-space: nowrap; + gap: calc(var(--unit) * 1); + font-size: var(--unit-size, inherit); + + background-color: transparent; + + border-radius: min(calc(var(--radius) - calc(var(--unit) * 1)), calc(var(--unit) * 1)); + padding: calc(var(--unit) * 2) calc(var(--unit) * 3); + line-height: 1em; + outline: 1px solid transparent; + + cursor: pointer; + + > * { + margin-inline: calc(-1 * var(--unit)); + } + + &.primary, + &[primary] { + background-color: oklch(from var(--primary) l c h / 0.12); + color: oklch(from var(--primary) calc(l - 0.2 * var(--theme-sign)) c h); + outline-color: oklch(from var(--primary) l c h / 0.2); + + &:not(:disabled):hover { + background-color: oklch(from var(--primary) l c h / 0.2); + outline-color: oklch(from var(--primary) l c h / 0.3); + } + + &:focus-visible:not(:disabled) { + outline-width: 3px; + outline-color: oklch(from var(--primary) l c h / 0.4); + } + } + + &.secondary, + &[secondary] { + background-color: var(--secondary); + color: var(--secondary-foreground); + outline-color: var(--border); + + &:not(:disabled):hover { + background-color: oklch(from var(--secondary) calc(l * (1 - 0.1 * var(--theme-sign))) c h); + color: oklch(from var(--secondary-foreground) calc(l * (1 - 0.1 * var(--theme-sign))) c h); + } + + &:focus-visible:not(:disabled) { + outline-width: 3px; + outline-color: oklch(from var(--secondary) calc(l * (1 - 0.075 * var(--theme-sign))) c h); + } + } + + &.tertiary, + &[tertiary] { + background-color: transparent; + color: var(--secondary-foreground); + outline-color: transparent; + + &:not(:disabled):hover { + background-color: oklch(from var(--secondary) calc(l * (1 - 0.1 * var(--theme-sign))) c h); + color: oklch(from var(--secondary-foreground) calc(l * (1 - 0.1 * var(--theme-sign))) c h); + } + + &:focus-visible:not(:disabled) { + outline: 3px solid oklch(from var(--secondary) calc(l * (1 - 0.075 * var(--theme-sign))) c h); + } + } + + &:not(:disabled):active { + transform: translateY(1px); + } + + &:disabled { + color: oklch(from var(--popover) calc(l * (1 - 0.5 * var(--theme-sign))) c h); + } +} +/* ---- Card.css ---- */ +.card, +card { + display: flex; + flex-direction: column; + padding: calc(var(--unit) * 4); + box-shadow: inset 0 0 0 1px var(--border); + + background-color: var(--card); + color: var(--foreground); + font-size: var(--unit-size, inherit); + border-radius: calc(var(--radius) - (8px - var(--unit))); +} +/* ---- Carousel.css ---- */ + +slides > * { + --n: sibling-count(); +} + +carousel { + display: flex; + flex: 1; + container-type: size; + contain: paint; + width: 100%; + height: 100%; +} + +slides { + width: 100%; + height: 100%; + position: relative; + overflow-y: auto; + scrollbar-width: none; +} + +slides.scrollbar, +slides[scrollbar] { + scrollbar-width: auto; +} + +slides.x, +slides[x] { + display: flex; + flex-direction: row; + flex-wrap: nowrap; + overflow-y: hidden; + overflow-x: scroll; + scroll-snap-type: x mandatory; + overscroll-behavior-x: none; +} + +slides.y, +slides[y], +slides.z, +slides[z] { + overflow-y: scroll; + overflow-x: hidden; + scroll-snap-type: y mandatory; + overscroll-behavior-y: none; +} + +slides.y, +slides[y], +slides.z, +slides[z] { + scroll-timeline: --slides-scroll block; +} + +slides.x, +slides[x] { + scroll-timeline: --slides-scroll-x inline; +} + +slides.x > *, +slides[x] > * { + height: 100%; + min-width: 100%; + scroll-snap-align: start; + scroll-snap-stop: always; + display: flex; +} + +slides.y > *, +slides[y] > * { + min-height: 100%; + width: 100%; + scroll-snap-align: start; + scroll-snap-stop: always; + display: flex; +} + +slides.z > *, +slides[z] > * { + min-height: 100%; + width: 100%; + scroll-snap-align: start; +} + +slide { + display: block; +} + +slides.x > * > *, +slides[x] > * > *, +slides.y > * > *, +slides[y] > * > * { + position: relative; +} + +slides.z > * > *, +slides[z] > * > * { + position: fixed; + inset: 0; + display: flex; + align-items: center; + justify-content: center; + pointer-events: none; +} + +slides.z > slide, +slides[z] > slide { + view-timeline: --slide-self block; +} + +slides.z > slide > *, +slides[z] > slide > * { + animation: z-fade-scale linear both; + animation-timeline: --slide-self; +} + +slides.z > slide > * > *, +slides[z] > slide > * > * { + pointer-events: none; + animation: z-pointer linear both; + animation-timeline: --slide-self; +} + + + +/* --- slides + square-grid: animated grid for y/x/z modes --- */ + +slides.square-grid.center, +slides.square-grid[center], +slides[square-grid].center, +slides[square-grid][center] { + --gx: 50cqw; + --gy: 50cqh; +} + +slides.square-grid:not(.locked):not([locked]).y:has(> :nth-child(2)), +slides.square-grid:not(.locked):not([locked])[y]:has(> :nth-child(2)), +slides[square-grid]:not(.locked):not([locked]).y:has(> :nth-child(2)), +slides[square-grid]:not(.locked):not([locked])[y]:has(> :nth-child(2)), +slides.square-grid:not(.locked):not([locked]).z:has(> :nth-child(2)), +slides.square-grid:not(.locked):not([locked])[z]:has(> :nth-child(2)), +slides[square-grid]:not(.locked):not([locked]).z:has(> :nth-child(2)), +slides[square-grid]:not(.locked):not([locked])[z]:has(> :nth-child(2)) { + background-image: none; +} + +slides.square-grid:not(.locked):not([locked]).y:has(> :nth-child(2)) > :first-child::before, +slides.square-grid:not(.locked):not([locked])[y]:has(> :nth-child(2)) > :first-child::before, +slides[square-grid]:not(.locked):not([locked]).y:has(> :nth-child(2)) > :first-child::before, +slides[square-grid]:not(.locked):not([locked])[y]:has(> :nth-child(2)) > :first-child::before { + content: ''; + position: fixed; + inset: 0; + pointer-events: none; + background: + linear-gradient(to right, oklch(0.55 0.02 260 / 0.22) 0 1px, transparent 1px) var(--gx) + var(--gy) / var(--grid-size) var(--grid-size), + linear-gradient(to bottom, oklch(0.55 0.02 260 / 0.22) 0 1px, transparent 1px) var(--gx) + var(--gy) / var(--grid-size) var(--grid-size), + linear-gradient(to right, oklch(0.55 0.02 260 / 0.08) 0 1px, transparent 1px) var(--gx) + var(--gy) / calc(var(--grid-size) * 0.5) calc(var(--grid-size) * 0.5), + linear-gradient(to bottom, oklch(0.55 0.02 260 / 0.08) 0 1px, transparent 1px) var(--gx) + var(--gy) / calc(var(--grid-size) * 0.5) calc(var(--grid-size) * 0.5); + animation: grid-pan-y linear both; + animation-timeline: --slides-scroll; +} + +slides.square-grid:not(.locked):not([locked]).x:has(> :nth-child(2)), +slides.square-grid:not(.locked):not([locked])[x]:has(> :nth-child(2)), +slides[square-grid]:not(.locked):not([locked]).x:has(> :nth-child(2)), +slides[square-grid]:not(.locked):not([locked])[x]:has(> :nth-child(2)) { + background-image: none; +} + +slides.square-grid:not(.locked):not([locked]).x:has(> :nth-child(2)) > :first-child::before, +slides.square-grid:not(.locked):not([locked])[x]:has(> :nth-child(2)) > :first-child::before, +slides[square-grid]:not(.locked):not([locked]).x:has(> :nth-child(2)) > :first-child::before, +slides[square-grid]:not(.locked):not([locked])[x]:has(> :nth-child(2)) > :first-child::before { + content: ''; + position: fixed; + inset: 0; + pointer-events: none; + background: + linear-gradient(to right, oklch(0.55 0.02 260 / 0.22) 0 1px, transparent 1px) var(--gx) + var(--gy) / var(--grid-size) var(--grid-size), + linear-gradient(to bottom, oklch(0.55 0.02 260 / 0.22) 0 1px, transparent 1px) var(--gx) + var(--gy) / var(--grid-size) var(--grid-size), + linear-gradient(to right, oklch(0.55 0.02 260 / 0.08) 0 1px, transparent 1px) var(--gx) + var(--gy) / calc(var(--grid-size) * 0.5) calc(var(--grid-size) * 0.5), + linear-gradient(to bottom, oklch(0.55 0.02 260 / 0.08) 0 1px, transparent 1px) var(--gx) + var(--gy) / calc(var(--grid-size) * 0.5) calc(var(--grid-size) * 0.5); + animation: grid-pan-x linear both; + animation-timeline: --slides-scroll-x; +} + + + +slides.square-grid:not(.locked):not([locked]).z:has(> :nth-child(2)) > :first-child::before, +slides.square-grid:not(.locked):not([locked]).z:has(> :nth-child(2)) > :first-child::after, +slides.square-grid:not(.locked):not([locked])[z]:has(> :nth-child(2)) > :first-child::before, +slides.square-grid:not(.locked):not([locked])[z]:has(> :nth-child(2)) > :first-child::after, +slides[square-grid]:not(.locked):not([locked]).z:has(> :nth-child(2)) > :first-child::before, +slides[square-grid]:not(.locked):not([locked]).z:has(> :nth-child(2)) > :first-child::after, +slides[square-grid]:not(.locked):not([locked])[z]:has(> :nth-child(2)) > :first-child::before, +slides[square-grid]:not(.locked):not([locked])[z]:has(> :nth-child(2)) > :first-child::after { + content: ''; + position: fixed; + inset: 0; + pointer-events: none; + --cx: calc(var(--gx) + round(nearest, 50cqw - var(--gx), var(--grid-size))); + --cy: calc(var(--gy) + round(nearest, 50cqh - var(--gy), var(--grid-size))); + --zx: calc(2 * var(--gx) - var(--cx)); + --zy: calc(2 * var(--gy) - var(--cy)); + --zx-m: calc(2 * var(--gx) - var(--cx)); + --zy-m: calc(2 * var(--gy) - var(--cy)); + background-position: var(--gx) var(--gy); + background-image: + linear-gradient(to right, oklch(0.55 0.02 260 / 0.22) 0 1px, transparent 1px), + linear-gradient(to bottom, oklch(0.55 0.02 260 / 0.22) 0 1px, transparent 1px), + linear-gradient(to right, oklch(0.55 0.02 260 / 0.08) 0 1px, transparent 1px), + linear-gradient(to bottom, oklch(0.55 0.02 260 / 0.08) 0 1px, transparent 1px); + animation-timeline: --slides-scroll; + animation-timing-function: linear; + animation-fill-mode: both; + animation-iteration-count: calc(var(--n) - 1); +} + +slides.square-grid:not(.locked):not([locked]).z:has(> :nth-child(2)) > :first-child::before, +slides.square-grid:not(.locked):not([locked])[z]:has(> :nth-child(2)) > :first-child::before, +slides[square-grid]:not(.locked):not([locked]).z:has(> :nth-child(2)) > :first-child::before, +slides[square-grid]:not(.locked):not([locked])[z]:has(> :nth-child(2)) > :first-child::before { + animation-name: grid-zoom-out; +} + +slides.square-grid:not(.locked):not([locked]).z:has(> :nth-child(2)) > :first-child::after, +slides.square-grid:not(.locked):not([locked])[z]:has(> :nth-child(2)) > :first-child::after, +slides[square-grid]:not(.locked):not([locked]).z:has(> :nth-child(2)) > :first-child::after, +slides[square-grid]:not(.locked):not([locked])[z]:has(> :nth-child(2)) > :first-child::after { + animation-name: grid-zoom-in; +} + + +/* ---- Checkbox.css ---- */ +.checkbox, +.switch, +input[type='checkbox'] { + appearance: none; + + background-color: var(--input); + border-radius: min(calc(var(--radius) - calc(var(--unit) * 1)), calc(var(--unit) * 1)); + + width: calc(var(--unit) * 6); + height: calc(var(--unit) * 6); + padding: calc(var(--unit) * 1); + + vertical-align: top; + + &:before { + content: ''; + display: block; + aspect-ratio: 1; + height: 100%; + border-radius: min(calc(var(--radius) - calc(var(--unit) * 1)), calc(var(--unit) * 1)); + background-color: oklch( + from var(--primary) calc(l + (1 - (var(--theme-sign) + 1) / 2)) + calc(c * (1 * (var(--theme-sign) + 1) / 2)) h + ); + transform: scale(0); + } + + &:checked:before { + transition: transform 100ms cubic-bezier(0.25, 0.1, 0.25, 1); + transform: scale(1); + } +} +/* ---- Circle.css ---- */ +circle, +.circle, +[circle] { + display: flex; + width: 100%; + aspect-ratio: 1; + border-radius: 100%; +} +/* ---- Code.css ---- */ +code { + font-family: var(--font-mono, monospace); + font-size: calc(var(--unit-size, 1em) * 0.85); + background: oklch(from var(--slate) l c h / 0.1); + color: var(--slate); + border: 1px solid oklch(from var(--slate) l c h / 0.2); + border-radius: calc(var(--unit) * 1); + padding: calc(var(--unit) * 0.5) calc(var(--unit) * 1.5); +} + +:where(.dark, [dark]) code { + background: oklch(0.3 0.03 260); + color: oklch(0.7 0.05 260); + border-color: oklch(0.35 0.03 260); +} +/* ---- Collective.css ---- */ +.input, +.checkbox, +.radio, +.select, +.textarea, +.button:is(.primary, .secondary, .tertiary, [primary], [secondary], [tertiary]), +input:not([type='checkbox'], [type='radio'], [type='range'], [type='file']), +input[type='file'], +input[type='checkbox'], +input[type='radio'], +select, +textarea, +button:is(.primary, .secondary, .tertiary, [primary], [secondary], [tertiary]) { + /* margin: 1px; */ + outline-width: 1px; + outline-style: solid; + outline-color: var(--border); + outline-offset: 0; + + transition-property: outline-color, background-color, color; + transition-duration: 100ms; + transition-timing-function: ease-in-out; + + &::placeholder { + font-weight: 400; + } + + &:disabled { + background-color: oklch(from var(--background) calc(l * (1 - 0.1 * var(--theme-sign))) c h); + color: oklch(from var(--popover) calc(l * (1 - 0.5 * var(--theme-sign))) c h); + outline-color: transparent; + cursor: not-allowed; + } +} + +.input, +.textarea, +input:not([type='checkbox'], [type='radio'], [type='range'], [type='file']), +textarea { + &:disabled { + /* This is needed for Input & Textarea, to actually respect the color in chrome */ + -webkit-text-fill-color: oklch(from var(--popover) calc(l * (1 - 0.5 * var(--theme-sign))) c h); + } +} + +.input, +.select, +textarea, +input:not([type='checkbox'], [type='radio'], [type='range'], [type='file']), +input[type='range'], +input[type='checkbox'], +input[type='radio'], +select, +textarea { + /* &:focus:enabled:not(:checked), */ + &:active:enabled:not(:checked), + &:focus-visible:enabled { + outline-width: 3px; + outline-color: oklch(from var(--input) calc(l/2 + 0.4) c h); + } +} +/* ---- Container.css ---- */ +container, +.container, +[container] { + width: 100%; + max-width: 1280px; + margin-inline: auto; +} +/* ---- Crow.css ---- */ +crow:not(:has(> crow)) > crow, +.crow:not(:has(> .crow)) > .crow { + flex: 1; +} + +crow, +.crow { + display: flex; + align-items: center; + justify-content: center; + font-size: var(--unit-size, inherit); +} +.up, +[up], +.down, +[down] { + height: 100%; +} +.up, +[up] { + align-items: flex-start; +} +.down, +[down] { + align-items: flex-end; +} +.left, +[left] { + justify-content: flex-start; +} +.right, +[right] { + justify-content: flex-end; +} +.vertical, +[vertical] { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} +.vertical > .up, +[vertical] > [up], +.vertical > .down, +[vertical] > [down] { + height: auto; +} +.vertical.up, +[vertical][up] { + justify-content: flex-start; +} +.vertical.down, +[vertical][down] { + justify-content: flex-end; +} +.crow.vertical.left, +crow[vertical][left], +.crow.vertical > .left, +crow[vertical] > [left], +.crow.vertical.right, +crow[vertical][right], +.crow.vertical > .right, +crow[vertical] > [right] { + width: 100%; +} +.vertical.left, +[vertical][left] { + align-items: flex-start; +} +.vertical.right, +[vertical][right] { + align-items: flex-end; +} +.vertical > .left, +[vertical] > [left], +.vertical > .right, +[vertical] > [right] { + display: flex; +} +/* ---- Hr.css ---- */ +hr, +.hr { + border: none; + width: 100%; + height: 1px; + background: linear-gradient(to right, transparent, var(--border), transparent); +} +/* ---- Label.css ---- */ +.label:has(input[type='checkbox'], input[type='radio']), +label:has(input[type='checkbox'], input[type='radio']) { + display: flex; + gap: calc(var(--unit) * 2); + font-size: var(--unit-size, inherit); + align-items: center; +} + +.label:has(select, input:not([type='checkbox'], [type='radio'], [type='range'], [type='file'])), +label:has(select, input:not([type='checkbox'], [type='radio'], [type='range'], [type='file'])) { + display: grid; +} + +.label > .select, +.label > .input, +label > select, +label > input:not([type='checkbox'], [type='radio'], [type='range'], [type='file']) { + grid-area: 1/1; + padding-inline-end: calc(var(--unit) * 8); /* 32px */ +} + +.label:has(:disabled), +label:has(:disabled) { + color: oklch(from var(--popover) calc(l * (1 - 0.5 * var(--theme-sign))) c h); + cursor: not-allowed; +} + +.label:has( + .select, + .input:not([type='checkbox'], [type='radio'], [type='range'], [type='file']) + ):after, +label:has( + select, + input:not([type='checkbox'], [type='radio'], [type='range'], [type='file']) + ):after { + content: ''; + pointer-events: none; + grid-area: 1/1; + mask-position: calc(100% - calc(var(--unit) * 2)) center; + mask-size: calc(var(--unit) * 3.5) auto; + mask-repeat: no-repeat; + background-color: var(--popover-foreground); +} + +.label:has( + .input:not([type='checkbox'], [type='radio'], [type='range'], [type='file']):placeholder-shown + ):after, +label:has( + input:not([type='checkbox'], [type='radio'], [type='range'], [type='file']):placeholder-shown + ):after { + background-color: oklch(from var(--background) calc(l/2 + 0.3 - 0.1 * var(--theme-sign)) c h); +} + +.label:has( + .select:disabled, + .input:not([type='checkbox'], [type='radio'], [type='range'], [type='file']):disabled + ):after, +label:has( + select:disabled, + input:not([type='checkbox'], [type='radio'], [type='range'], [type='file']):disabled + ):after { + background-color: oklch(from var(--popover) calc(l * (1 - 0.5 * var(--theme-sign))) c h); +} + +.label:has(.select):after, +label:has(select):after { + mask-image: var(--icon, var(--icon-chevron-down)); +} + +.label:has(.input:not([type='checkbox'], [type='radio'], [type='range'], [type='file'])):after, +label:has(input:not([type='checkbox'], [type='radio'], [type='range'], [type='file'])):after { + transition: background-color 150ms ease-in-out; + mask-image: var(--icon, var(--icon-search)); +} +/* ---- Modifiers.css ---- */ +.sm, [sm] { --unit: 3px; --unit-size: 12px; font-size: var(--unit-size, inherit); } +.md, [md] { --unit: 4px; --unit-size: 16px; font-size: var(--unit-size, inherit); } +.lg, [lg] { --unit: 5px; --unit-size: 18px; font-size: var(--unit-size, inherit); } + +.hide, [hide] { display: none; } +.flex, [flex] { display: flex; } +.block, [block] { display: block; } + +.f-0, [f-0] { flex: none; } +.f-1, [f-1] { flex: 1; } +.f-2, [f-2] { flex: 2; } +.f-3, [f-3] { flex: 3; } +.f-4, [f-4] { flex: 4; } +.f-5, [f-5] { flex: 5; } +.f-6, [f-6] { flex: 6; } +.f-7, [f-7] { flex: 7; } +.f-8, [f-8] { flex: 8; } + +.justify-between, [justify-between] { justify-content: space-between; } + +.w-full, [w-full] { width: 100%; } +.h-full, [h-full] { height: 100%; } +.mw-full, [mw-full] { min-width: 100%; } +.mh-full, [mh-full] { min-height: 100%; } + +/* EXPERIMENTAL: typed attr() is Chrome 133+ only (not Safari/Firefox yet). + Once supported in all major browsers, w="12" / h="12" syntax should replace + all fixed-value w-* / h-* modifiers as the standard Stylecheat pattern. */ +[w] { width: calc(attr(w type()) * var(--unit)); } +[h] { height: calc(attr(h type()) * var(--unit)); } + +/* Gap utilities */ +.g-0, [g-0] { gap: 0; } +.g-1, [g-1] { gap: calc(var(--unit) * 1); } +.g-2, [g-2] { gap: calc(var(--unit) * 2); } +.g-3, [g-3] { gap: calc(var(--unit) * 3); } +.g-4, [g-4] { gap: calc(var(--unit) * 4); } +.g-5, [g-5] { gap: calc(var(--unit) * 5); } +.g-6, [g-6] { gap: calc(var(--unit) * 6); } +.g-7, [g-7] { gap: calc(var(--unit) * 7); } +.g-8, [g-8] { gap: calc(var(--unit) * 8); } + +/* Margin utilities */ +.mt-0, [mt-0] { margin-top: 0; } +.mt-1, [mt-1] { margin-top: calc(var(--unit) * 1); } +.mt-2, [mt-2] { margin-top: calc(var(--unit) * 2); } +.mt-3, [mt-3] { margin-top: calc(var(--unit) * 3); } +.mt-4, [mt-4] { margin-top: calc(var(--unit) * 4); } +.mt-5, [mt-5] { margin-top: calc(var(--unit) * 5); } +.mt-6, [mt-6] { margin-top: calc(var(--unit) * 6); } +.mt-7, [mt-7] { margin-top: calc(var(--unit) * 7); } +.mt-8, [mt-8] { margin-top: calc(var(--unit) * 8); } + +.mb-0, [mb-0] { margin-bottom: 0; } +.mb-1, [mb-1] { margin-bottom: calc(var(--unit) * 1); } +.mb-2, [mb-2] { margin-bottom: calc(var(--unit) * 2); } +.mb-3, [mb-3] { margin-bottom: calc(var(--unit) * 3); } +.mb-4, [mb-4] { margin-bottom: calc(var(--unit) * 4); } +.mb-5, [mb-5] { margin-bottom: calc(var(--unit) * 5); } +.mb-6, [mb-6] { margin-bottom: calc(var(--unit) * 6); } +.mb-7, [mb-7] { margin-bottom: calc(var(--unit) * 7); } +.mb-8, [mb-8] { margin-bottom: calc(var(--unit) * 8); } + +.ml-0, [ml-0] { margin-left: 0; } +.ml-1, [ml-1] { margin-left: calc(var(--unit) * 1); } +.ml-2, [ml-2] { margin-left: calc(var(--unit) * 2); } +.ml-3, [ml-3] { margin-left: calc(var(--unit) * 3); } +.ml-4, [ml-4] { margin-left: calc(var(--unit) * 4); } +.ml-5, [ml-5] { margin-left: calc(var(--unit) * 5); } +.ml-6, [ml-6] { margin-left: calc(var(--unit) * 6); } +.ml-7, [ml-7] { margin-left: calc(var(--unit) * 7); } +.ml-8, [ml-8] { margin-left: calc(var(--unit) * 8); } + +.mr-0, [mr-0] { margin-right: 0; } +.mr-1, [mr-1] { margin-right: calc(var(--unit) * 1); } +.mr-2, [mr-2] { margin-right: calc(var(--unit) * 2); } +.mr-3, [mr-3] { margin-right: calc(var(--unit) * 3); } +.mr-4, [mr-4] { margin-right: calc(var(--unit) * 4); } +.mr-5, [mr-5] { margin-right: calc(var(--unit) * 5); } +.mr-6, [mr-6] { margin-right: calc(var(--unit) * 6); } +.mr-7, [mr-7] { margin-right: calc(var(--unit) * 7); } +.mr-8, [mr-8] { margin-right: calc(var(--unit) * 8); } + +.mx-0, [mx-0] { margin-inline: 0; } +.mx-1, [mx-1] { margin-inline: calc(var(--unit) * 1); } +.mx-2, [mx-2] { margin-inline: calc(var(--unit) * 2); } +.mx-3, [mx-3] { margin-inline: calc(var(--unit) * 3); } +.mx-4, [mx-4] { margin-inline: calc(var(--unit) * 4); } +.mx-5, [mx-5] { margin-inline: calc(var(--unit) * 5); } +.mx-6, [mx-6] { margin-inline: calc(var(--unit) * 6); } +.mx-7, [mx-7] { margin-inline: calc(var(--unit) * 7); } +.mx-8, [mx-8] { margin-inline: calc(var(--unit) * 8); } + +.my-0, [my-0] { margin-block: 0; } +.my-1, [my-1] { margin-block: calc(var(--unit) * 1); } +.my-2, [my-2] { margin-block: calc(var(--unit) * 2); } +.my-3, [my-3] { margin-block: calc(var(--unit) * 3); } +.my-4, [my-4] { margin-block: calc(var(--unit) * 4); } +.my-5, [my-5] { margin-block: calc(var(--unit) * 5); } +.my-6, [my-6] { margin-block: calc(var(--unit) * 6); } +.my-7, [my-7] { margin-block: calc(var(--unit) * 7); } +.my-8, [my-8] { margin-block: calc(var(--unit) * 8); } + +.m-0, [m-0] { margin: 0; } +.m-1, [m-1] { margin: calc(var(--unit) * 1); } +.m-2, [m-2] { margin: calc(var(--unit) * 2); } +.m-3, [m-3] { margin: calc(var(--unit) * 3); } +.m-4, [m-4] { margin: calc(var(--unit) * 4); } +.m-5, [m-5] { margin: calc(var(--unit) * 5); } +.m-6, [m-6] { margin: calc(var(--unit) * 6); } +.m-7, [m-7] { margin: calc(var(--unit) * 7); } +.m-8, [m-8] { margin: calc(var(--unit) * 8); } + +/* Padding utilities */ +.pt-0, [pt-0] { padding-top: 0; } +.pt-1, [pt-1] { padding-top: calc(var(--unit) * 1); } +.pt-2, [pt-2] { padding-top: calc(var(--unit) * 2); } +.pt-3, [pt-3] { padding-top: calc(var(--unit) * 3); } +.pt-4, [pt-4] { padding-top: calc(var(--unit) * 4); } +.pt-5, [pt-5] { padding-top: calc(var(--unit) * 5); } +.pt-6, [pt-6] { padding-top: calc(var(--unit) * 6); } +.pt-7, [pt-7] { padding-top: calc(var(--unit) * 7); } +.pt-8, [pt-8] { padding-top: calc(var(--unit) * 8); } + +.pb-0, [pb-0] { padding-bottom: 0; } +.pb-1, [pb-1] { padding-bottom: calc(var(--unit) * 1); } +.pb-2, [pb-2] { padding-bottom: calc(var(--unit) * 2); } +.pb-3, [pb-3] { padding-bottom: calc(var(--unit) * 3); } +.pb-4, [pb-4] { padding-bottom: calc(var(--unit) * 4); } +.pb-5, [pb-5] { padding-bottom: calc(var(--unit) * 5); } +.pb-6, [pb-6] { padding-bottom: calc(var(--unit) * 6); } +.pb-7, [pb-7] { padding-bottom: calc(var(--unit) * 7); } +.pb-8, [pb-8] { padding-bottom: calc(var(--unit) * 8); } + +.pl-0, [pl-0] { padding-left: 0; } +.pl-1, [pl-1] { padding-left: calc(var(--unit) * 1); } +.pl-2, [pl-2] { padding-left: calc(var(--unit) * 2); } +.pl-3, [pl-3] { padding-left: calc(var(--unit) * 3); } +.pl-4, [pl-4] { padding-left: calc(var(--unit) * 4); } +.pl-5, [pl-5] { padding-left: calc(var(--unit) * 5); } +.pl-6, [pl-6] { padding-left: calc(var(--unit) * 6); } +.pl-7, [pl-7] { padding-left: calc(var(--unit) * 7); } +.pl-8, [pl-8] { padding-left: calc(var(--unit) * 8); } + +.pr-0, [pr-0] { padding-right: 0; } +.pr-1, [pr-1] { padding-right: calc(var(--unit) * 1); } +.pr-2, [pr-2] { padding-right: calc(var(--unit) * 2); } +.pr-3, [pr-3] { padding-right: calc(var(--unit) * 3); } +.pr-4, [pr-4] { padding-right: calc(var(--unit) * 4); } +.pr-5, [pr-5] { padding-right: calc(var(--unit) * 5); } +.pr-6, [pr-6] { padding-right: calc(var(--unit) * 6); } +.pr-7, [pr-7] { padding-right: calc(var(--unit) * 7); } +.pr-8, [pr-8] { padding-right: calc(var(--unit) * 8); } + +.px-0, [px-0] { padding-inline: 0; } +.px-1, [px-1] { padding-inline: calc(var(--unit) * 1); } +.px-2, [px-2] { padding-inline: calc(var(--unit) * 2); } +.px-3, [px-3] { padding-inline: calc(var(--unit) * 3); } +.px-4, [px-4] { padding-inline: calc(var(--unit) * 4); } +.px-5, [px-5] { padding-inline: calc(var(--unit) * 5); } +.px-6, [px-6] { padding-inline: calc(var(--unit) * 6); } +.px-7, [px-7] { padding-inline: calc(var(--unit) * 7); } +.px-8, [px-8] { padding-inline: calc(var(--unit) * 8); } + +.py-0, [py-0] { padding-block: 0; } +.py-1, [py-1] { padding-block: calc(var(--unit) * 1); } +.py-2, [py-2] { padding-block: calc(var(--unit) * 2); } +.py-3, [py-3] { padding-block: calc(var(--unit) * 3); } +.py-4, [py-4] { padding-block: calc(var(--unit) * 4); } +.py-5, [py-5] { padding-block: calc(var(--unit) * 5); } +.py-6, [py-6] { padding-block: calc(var(--unit) * 6); } +.py-7, [py-7] { padding-block: calc(var(--unit) * 7); } +.py-8, [py-8] { padding-block: calc(var(--unit) * 8); } + +.p-0, [p-0] { padding: 0; } +.p-1, [p-1] { padding: calc(var(--unit) * 1); } +.p-2, [p-2] { padding: calc(var(--unit) * 2); } +.p-3, [p-3] { padding: calc(var(--unit) * 3); } +.p-4, [p-4] { padding: calc(var(--unit) * 4); } +.p-5, [p-5] { padding: calc(var(--unit) * 5); } +.p-6, [p-6] { padding: calc(var(--unit) * 6); } +.p-7, [p-7] { padding: calc(var(--unit) * 7); } +.p-8, [p-8] { padding: calc(var(--unit) * 8); } + +.text-blue, [text-blue] { color: var(--blue); } +.text-purple, [text-purple] { color: var(--purple); } +.text-green, [text-green] { color: var(--green); } +.text-orange, [text-orange] { color: var(--orange); } +.text-cyan, [text-cyan] { color: var(--cyan); } +.text-red, [text-red] { color: var(--red); } +.text-yellow, [text-yellow] { color: var(--yellow); } +.text-slate, [text-slate] { color: var(--slate); } + +.bg-blue, [bg-blue] { background-color: oklch(from var(--blue) l c h / 0.2); } +.bg-purple, [bg-purple] { background-color: oklch(from var(--purple) l c h / 0.2); } +.bg-green, [bg-green] { background-color: oklch(from var(--green) l c h / 0.2); } +.bg-orange, [bg-orange] { background-color: oklch(from var(--orange) l c h / 0.2); } +.bg-cyan, [bg-cyan] { background-color: oklch(from var(--cyan) l c h / 0.2); } +.bg-red, [bg-red] { background-color: oklch(from var(--red) l c h / 0.2); } +.bg-yellow, [bg-yellow] { background-color: oklch(from var(--yellow) l c h / 0.2); } +.bg-slate, [bg-slate] { background-color: oklch(from var(--slate) l c h / 0.2); } + +@media (width <= 768px) { + .sm\:sm, [sm\:sm] { --unit: 2px; --unit-size: 12px; font-size: var(--unit-size, inherit); } + .sm\:md, [sm\:md] { --unit: 4px; --unit-size: 16px; font-size: var(--unit-size, inherit); } + .sm\:lg, [sm\:lg] { --unit: 8px; --unit-size: 24px; font-size: var(--unit-size, inherit); } + .sm\:hide, [sm\:hide] { display: none; } + .sm\:flex, [sm\:flex] { display: flex; } + .sm\:block, [sm\:block] { display: block; } + .sm\:vertical, [sm\:vertical] { flex-direction: column; } +} + +@media (768px < width <= 1024px) { + .md\:sm, [md\:sm] { --unit: 2px; --unit-size: 12px; font-size: var(--unit-size, inherit); } + .md\:md, [md\:md] { --unit: 4px; --unit-size: 16px; font-size: var(--unit-size, inherit); } + .md\:lg, [md\:lg] { --unit: 8px; --unit-size: 24px; font-size: var(--unit-size, inherit); } + .md\:hide, [md\:hide] { display: none; } + .md\:flex, [md\:flex] { display: flex; } + .md\:block, [md\:block] { display: block; } + .md\:vertical, [md\:vertical] { flex-direction: column; } +} + +@media (width > 1024px) { + .lg\:sm, [lg\:sm] { --unit: 2px; --unit-size: 12px; font-size: var(--unit-size, inherit); } + .lg\:md, [lg\:md] { --unit: 4px; --unit-size: 16px; font-size: var(--unit-size, inherit); } + .lg\:lg, [lg\:lg] { --unit: 8px; --unit-size: 24px; font-size: var(--unit-size, inherit); } + .lg\:hide, [lg\:hide] { display: none; } + .lg\:flex, [lg\:flex] { display: flex; } + .lg\:block, [lg\:block] { display: block; } + .lg\:vertical, [lg\:vertical] { flex-direction: column; } +} +/* ---- Radio.css ---- */ +.radio, +input[type='radio'] { + appearance: none; + + background-color: var(--input); + border-radius: 50%; + + width: calc(var(--unit) * 6); + height: calc(var(--unit) * 6); /* aspect-ratio: 1; doesn't seem to work for safari iphone */ + padding: calc(var(--unit) * 1); + + vertical-align: top; + + &:before { + content: ''; + display: block; + aspect-ratio: 1; + height: 100%; + border-radius: 50%; + background-color: oklch( + from var(--primary) calc(l + (1 - (var(--theme-sign) + 1) / 2)) + calc(c * (1 * (var(--theme-sign) + 1) / 2)) h + ); + transform: scale(0); + } + + &:checked:before { + transition: transform 100ms cubic-bezier(0.25, 0.1, 0.25, 1); + transform: scale(1); + } +} +/* ---- Range.css ---- */ +.range, +input[type='range'] { + display: flex; + appearance: none; + min-width: calc(var(--unit) * 59); + outline: 0; + + background-color: var(--input); + border-radius: calc(var(--unit) * 3); + height: calc(var(--unit) * 1.5); + border: 1px solid var(--border); + + &::-webkit-slider-thumb { + appearance: none; + outline: 0px inset transparent; + outline-offset: 0; + transition: outline 50ms ease-in-out; + transition: outline 50ms ease-in-out; + + width: calc(var(--unit) * 6); + aspect-ratio: 1; + background-color: oklch( + from var(--primary) calc(l + (1 - (var(--theme-sign) + 1) / 2)) + calc(c * (1 * (var(--theme-sign) + 1) / 2)) h + ); + border-radius: 50%; + + border: calc(var(--unit) * 1) solid var(--input); + outline: 1px solid var(--border); + } + + &:disabled { + cursor: not-allowed; + + &::-webkit-slider-thumb { + border-color: oklch(from var(--background) calc(l * (1 - 0.1 * var(--theme-sign))) c h); + background-color: oklch(from var(--popover) calc(l * (1 - 0.5 * var(--theme-sign))) c h); + outline-color: transparent; + } + } + + &:focus:enabled:not(:active) { + outline: 0; + + &::-webkit-slider-thumb { + outline: 3px solid oklch(from var(--input) calc(l/2 + 0.4) c h); + } + } +} +/* ---- Select.css ---- */ +.select, +select { + min-width: calc(var(--unit) * 59); + appearance: none; + + background-color: var(--input); + color: var(--popover-foreground); + border-radius: min(calc(var(--radius) - calc(var(--unit) * 1)), calc(var(--unit) * 1)); + font-size: var(--unit-size, inherit); + + padding: calc(var(--unit) * 1.5) calc(var(--unit) * 2); + + &:invalid { + color: oklch(from var(--background) calc(l/2 + 0.3 - 0.1 * var(--theme-sign)) c h); + } +} +/* ---- Sidebar.css ---- */ +sidebar { + position: sticky; + top: 0; + flex-shrink: 0; + display: flex; + flex-direction: column; + order: -1; +} + +sidebar-content { + width: calc(var(--unit) * 56); + display: flex; + flex-direction: column; + flex: 1; + padding: calc(var(--unit) * 4); + justify-content: space-between; +} + +input#sidebar-toggle { + position: absolute; + opacity: 0; +} + +@media (width <= 768px) { + sidebar { + position: fixed; + inset: 0; + height: 100%; + width: 100%; + pointer-events: none; + } + + sidebar:has(input#sidebar-toggle:checked) input#sidebar-toggle { + inset: 0; + width: 100%; + height: 100%; + } + + sidebar:has(input#sidebar-toggle:checked) { + pointer-events: auto; + transform: translateX(0); + background: oklch(from black l c h / 0.5); + transition: background-color 300ms ease; + } + + sidebar-content { + background-color: var(--sidebar); + border-right: 1px solid var(--sidebar-border); + color: var(--sidebar-foreground); + transform: translateX(-100%); + transition: transform 300ms ease; + pointer-events: auto; + } + + sidebar:has(input#sidebar-toggle:checked) sidebar-content { + transform: translateX(0); + } +} +/* ---- SquareGrid.css ---- */ +.square-grid, +square-grid { + --grid-size: calc(var(--grid-unit, var(--unit)) * 12); + --gx: 0px; + --gy: 0px; + /* background-attachment: fixed; */ +} + +.square-grid.center, +.square-grid[center], +square-grid.center, +square-grid[center] { + --gx: calc(50% - var(--grid-size) / 2); + --gy: calc(50% - var(--grid-size) / 2); +} + +.square-grid.locked, +.square-grid[locked], +square-grid.locked, +square-grid[locked] { + background: + linear-gradient(to right, oklch(0.55 0.02 260 / 0.22) 0 1px, transparent 1px) var(--gx) + var(--gy) / var(--grid-size) var(--grid-size), + linear-gradient(to bottom, oklch(0.55 0.02 260 / 0.22) 0 1px, transparent 1px) var(--gx) + var(--gy) / var(--grid-size) var(--grid-size), + linear-gradient(to right, oklch(0.55 0.02 260 / 0.08) 0 1px, transparent 1px) var(--gx) + var(--gy) / calc(var(--grid-size) * 0.5) calc(var(--grid-size) * 0.5), + linear-gradient(to bottom, oklch(0.55 0.02 260 / 0.08) 0 1px, transparent 1px) var(--gx) + var(--gy) / calc(var(--grid-size) * 0.5) calc(var(--grid-size) * 0.5); + background-attachment: fixed; +} + +.square-grid:not(.locked):not([locked]), +square-grid:not(.locked):not([locked]) { + background: + linear-gradient(to right, oklch(0.55 0.02 260 / 0.22) 0 1px, transparent 1px) var(--gx) + var(--gy) / var(--grid-size) var(--grid-size), + linear-gradient(to bottom, oklch(0.55 0.02 260 / 0.22) 0 1px, transparent 1px) var(--gx) + var(--gy) / var(--grid-size) var(--grid-size), + linear-gradient(to right, oklch(0.55 0.02 260 / 0.08) 0 1px, transparent 1px) var(--gx) + var(--gy) / calc(var(--grid-size) * 0.5) calc(var(--grid-size) * 0.5), + linear-gradient(to bottom, oklch(0.55 0.02 260 / 0.08) 0 1px, transparent 1px) var(--gx) + var(--gy) / calc(var(--grid-size) * 0.5) calc(var(--grid-size) * 0.5); +} +/* ---- Stack.css ---- */ +stack { + display: grid; + place-items: center; +} + +stack > * { + grid-area: 1/1; +} +/* ---- Switch.css ---- */ +.switch, +input[type='checkbox'][role='switch'] { + width: calc(var(--unit) * 12); + + background-color: var(--input); + border-radius: calc(var(--unit) * 3); + + &:before { + transition: all 150ms ease-in-out; + content: ''; + color: var(--primary-foreground); + border-radius: calc(var(--unit) * 2.5); + transform: scale(1); + } + + &:disabled:before { + background-color: oklch(from var(--popover) calc(l * (1 - 0.5 * var(--theme-sign))) c h); + } + + &:checked { + background-color: var(--primary); + outline-color: oklch(from var(--primary) calc(l * (1 + 0.075 * var(--theme-sign))) c h); + + &:focus-visible:enabled { + outline-color: oklch(from var(--primary) calc(l * (1 + 0.075 * var(--theme-sign))) c h); + } + + &:before { + background-color: var(--primary-foreground); + } + } + + &:checked:before { + margin-inline-start: calc(var(--unit) * 6); + } + + &:active:enabled, + &:focus-visible:enabled { + outline-width: 3px; + } +} +/* ---- TextArea.css ---- */ +.textarea, +textarea { + min-width: calc(var(--unit) * 59); + + background-color: var(--input); + border-radius: min(calc(var(--radius) - calc(var(--unit) * 1)), calc(var(--unit) * 1)); + color: oklch(calc((1 - var(--theme-sign)) * 0.5) 0 0); + padding: calc(var(--unit) * 1.5) calc(var(--unit) * 2); + font-size: var(--unit-size, inherit); + + &:not(:disabled)::placeholder { + color: oklch(from var(--background) calc(l/2 + 0.3 - 0.1 * var(--theme-sign)) c h); + } +} +/* ---- TextInput.css ---- */ +.input, +input:not([type='checkbox'], [type='radio'], [type='range'], [type='file']) { + min-width: calc(var(--unit) * 59); + + background-color: var(--input); + border-radius: min(calc(var(--radius) - calc(var(--unit) * 1)), calc(var(--unit) * 1)); + color: var(--popover-foreground); + padding: calc(var(--unit) * 1.5) calc(var(--unit) * 2); + font-size: var(--unit-size, inherit); + + &:not(:disabled)::placeholder { + color: oklch(from var(--background) calc(l/2 + 0.3 - 0.1 * var(--theme-sign)) c h); + } +} + + } +} +/* ---- Iconice.css ---- */ +:root { + --icon-bell: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2016%2016%22%3E%0A%3Cpath%20fill%3D%22%23000%22%20d%3D%22M16.023%2012.5c0-4.5-4-3.5-4-7%200-0.29-0.028-0.538-0.079-0.749-0.263-1.766-1.44-3.183-2.965-3.615%200.014-0.062%200.021-0.125%200.021-0.191%200-0.52-0.45-0.945-1-0.945s-1%200.425-1%200.945c0%200.065%200.007%200.129%200.021%200.191-1.71%200.484-2.983%202.208-3.020%204.273-0.001%200.030-0.001%200.060-0.001%200.091%200%203.5-4%202.5-4%207%200%201.191%202.665%202.187%206.234%202.439%200.336%200.631%201.001%201.061%201.766%201.061s1.43-0.43%201.766-1.061c3.568-0.251%206.234-1.248%206.234-2.439%200-0.004-0-0.007-0-0.011l0.024%200.011zM12.91%2013.345c-0.847%200.226-1.846%200.389-2.918%200.479-0.089-1.022-0.947-1.824-1.992-1.824s-1.903%200.802-1.992%201.824c-1.072-0.090-2.071-0.253-2.918-0.479-1.166-0.311-1.724-0.659-1.928-0.845%200.204-0.186%200.762-0.534%201.928-0.845%201.356-0.362%203.1-0.561%204.91-0.561s3.554%200.199%204.91%200.561c1.166%200.311%201.724%200.659%201.928%200.845-0.204%200.186-0.762%200.534-1.928%200.845z%22%3E%3C%2Fpath%3E%0A%3C%2Fsvg%3E%0A'); + --icon-book: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2016%2016%22%3E%0A%3Cpath%20fill%3D%22%23000%22%20d%3D%22M14%202v13h-10.5c-0.829%200-1.5-0.672-1.5-1.5s0.671-1.5%201.5-1.5h9.5v-12h-10c-1.1%200-2%200.9-2%202v12c0%201.1%200.9%202%202%202h12v-14h-1z%22%3E%3C%2Fpath%3E%0A%3Cpath%20fill%3D%22%23000%22%20d%3D%22M3.501%2013v0c-0%200-0.001%200-0.001%200-0.276%200-0.5%200.224-0.5%200.5s0.224%200.5%200.5%200.5c0%200%200.001-0%200.001-0v0h9.498v-1h-9.498z%22%3E%3C%2Fpath%3E%0A%3C%2Fsvg%3E%0A'); + --icon-bullhorn: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2016%2016%22%3E%0A%3Cpath%20fill%3D%22%23000%22%20d%3D%22M16%206.707c0-3.139-0.919-5.687-2.054-5.707%200.005-0%200.009-0%200.014-0h-1.296c0%200-3.044%202.287-7.425%203.184-0.134%200.708-0.219%201.551-0.219%202.523s0.085%201.816%200.219%202.523c4.382%200.897%207.425%203.184%207.425%203.184h1.296c-0.005%200-0.009-0-0.014-0.001%201.136-0.020%202.054-2.567%202.054-5.707zM13.513%2011.551c-0.147%200-0.305-0.152-0.387-0.243-0.197-0.22-0.387-0.562-0.55-0.989-0.363-0.957-0.564-2.239-0.564-3.611s0.2-2.655%200.564-3.611c0.162-0.428%200.353-0.77%200.55-0.99%200.081-0.091%200.24-0.243%200.387-0.243s0.305%200.152%200.387%200.243c0.197%200.22%200.387%200.562%200.55%200.99%200.363%200.957%200.564%202.239%200.564%203.611s-0.2%202.655-0.564%203.611c-0.162%200.428-0.353%200.77-0.55%200.989-0.081%200.091-0.24%200.243-0.387%200.243zM3.935%206.707c0-0.812%200.060-1.6%200.173-2.33-0.74%200.102-1.39%200.161-2.193%200.161-1.048%200-1.048%200-1.048%200l-0.867%201.479v1.378l0.867%201.479c0%200%200%200%201.048%200%200.803%200%201.453%200.059%202.193%200.161-0.113-0.729-0.173-1.518-0.173-2.33zM5.752%2010.034l-2-0.383%201.279%205.024c0.066%200.26%200.324%200.391%200.573%200.291l1.852-0.741c0.249-0.1%200.349-0.374%200.222-0.611l-1.926-3.581zM13.513%208.574c-0.057%200-0.118-0.059-0.149-0.094-0.076-0.085-0.149-0.217-0.212-0.381-0.14-0.369-0.217-0.863-0.217-1.392s0.077-1.023%200.217-1.392c0.063-0.165%200.136-0.297%200.212-0.381%200.031-0.035%200.092-0.094%200.149-0.094s0.118%200.059%200.149%200.094c0.076%200.085%200.149%200.217%200.212%200.381%200.14%200.369%200.217%200.863%200.217%201.392s-0.077%201.023-0.217%201.392c-0.063%200.165-0.136%200.297-0.212%200.381-0.031%200.035-0.092%200.094-0.149%200.094z%22%3E%3C%2Fpath%3E%0A%3C%2Fsvg%3E%0A'); + --icon-checkmark: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20512%20512%22%3E%0A%3Cpath%20fill%3D%22%23000%22%20d%3D%22M432%2064l-240%20240-112-112-80%2080%20192%20192%20320-320z%22%3E%3C%2Fpath%3E%0A%3C%2Fsvg%3E'); + --icon-chevron-left: url('data:image/svg+xml;utf8,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22UTF-8%22%3F%3E%0A%3Csvg%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20version%3D%221.1%22%20viewBox%3D%220%200%2016%2016%22%3E%0A%20%20%3C!--%20Generator%3A%20Adobe%20Illustrator%2029.8.4%2C%20SVG%20Export%20Plug-In%20.%20SVG%20Version%3A%202.1.1%20Build%206)%20%20--%3E%0A%20%20%3Cpath%20d%3D%22M10.25%2C1.75l1.5%2C1.5-4.5%2C4.5%2C4.5%2C4.5-1.5%2C1.5-6-6L10.25%2C1.75Z%22%2F%3E%0A%3C%2Fsvg%3E'); + --icon-chevron-down: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2016%2016%22%3E%0A%3Cpath%20fill%3D%22%23000%22%20d%3D%22M2%205.5l1.5-1.5%204.5%204.5%204.5-4.5%201.5%201.5-6%206-6-6z%22%3E%3C%2Fpath%3E%0A%3C%2Fsvg%3E%0A'); + --icon-chevron-right: url('data:image/svg+xml;utf8,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22UTF-8%22%3F%3E%0A%3Csvg%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20version%3D%221.1%22%20viewBox%3D%220%200%2016%2016%22%3E%0A%20%20%3C!--%20Generator%3A%20Adobe%20Illustrator%2029.8.4%2C%20SVG%20Export%20Plug-In%20.%20SVG%20Version%3A%202.1.1%20Build%206)%20%20--%3E%0A%20%20%3Cpath%20d%3D%22M5.75%2C13.75l-1.5-1.5%2C4.5-4.5L4.25%2C3.25l1.5-1.5%2C6%2C6-6%2C6Z%22%2F%3E%0A%3C%2Fsvg%3E'); + --icon-clock: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2016%2016%22%3E%0A%3Cpath%20fill%3D%22%23000%22%20d%3D%22M10.293%2011.707l-3.293-3.293v-4.414h2v3.586l2.707%202.707zM8%200c-4.418%200-8%203.582-8%208s3.582%208%208%208%208-3.582%208-8-3.582-8-8-8zM8%2014c-3.314%200-6-2.686-6-6s2.686-6%206-6c3.314%200%206%202.686%206%206s-2.686%206-6%206z%22%3E%3C%2Fpath%3E%0A%3C%2Fsvg%3E%0A'); + --icon-chevron-up: url('data:image/svg+xml;utf8,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22UTF-8%22%3F%3E%0A%3Csvg%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20version%3D%221.1%22%20viewBox%3D%220%200%2016%2016%22%3E%0A%20%20%3C!--%20Generator%3A%20Adobe%20Illustrator%2029.8.4%2C%20SVG%20Export%20Plug-In%20.%20SVG%20Version%3A%202.1.1%20Build%206)%20%20--%3E%0A%20%20%3Cpath%20d%3D%22M14%2C10l-1.5%2C1.5-4.5-4.5-4.5%2C4.5-1.5-1.5%2C6-6%2C6%2C6Z%22%2F%3E%0A%3C%2Fsvg%3E'); + --icon-cog: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2016%2016%22%3E%0A%3Cpath%20fill%3D%22%23000%22%20d%3D%22M14.59%209.535c-0.839-1.454-0.335-3.317%201.127-4.164l-1.572-2.723c-0.449%200.263-0.972%200.414-1.529%200.414-1.68%200-3.042-1.371-3.042-3.062h-3.145c0.004%200.522-0.126%201.051-0.406%201.535-0.839%201.454-2.706%201.948-4.17%201.106l-1.572%202.723c0.453%200.257%200.845%200.634%201.123%201.117%200.838%201.452%200.336%203.311-1.12%204.16l1.572%202.723c0.448-0.261%200.967-0.41%201.522-0.41%201.675%200%203.033%201.362%203.042%203.046h3.145c-0.001-0.517%200.129-1.040%200.406-1.519%200.838-1.452%202.7-1.947%204.163-1.11l1.572-2.723c-0.45-0.257-0.839-0.633-1.116-1.113zM8%2011.24c-1.789%200-3.24-1.45-3.24-3.24s1.45-3.24%203.24-3.24c1.789%200%203.24%201.45%203.24%203.24s-1.45%203.24-3.24%203.24z%22%3E%3C%2Fpath%3E%0A%3C%2Fsvg%3E%0A'); + --icon-credit-card: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2016%2016%22%3E%0A%3Cpath%20fill%3D%22%23000%22%20d%3D%22M14.5%202h-13c-0.825%200-1.5%200.675-1.5%201.5v9c0%200.825%200.675%201.5%201.5%201.5h13c0.825%200%201.5-0.675%201.5-1.5v-9c0-0.825-0.675-1.5-1.5-1.5zM1.5%203h13c0.271%200%200.5%200.229%200.5%200.5v1.5h-14v-1.5c0-0.271%200.229-0.5%200.5-0.5zM14.5%2013h-13c-0.271%200-0.5-0.229-0.5-0.5v-4.5h14v4.5c0%200.271-0.229%200.5-0.5%200.5zM2%2010h1v2h-1zM4%2010h1v2h-1zM6%2010h1v2h-1z%22%3E%3C%2Fpath%3E%0A%3C%2Fsvg%3E%0A'); + --icon-cross: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20512%20512%22%3E%0A%3Cpath%20fill%3D%22%23000%22%20d%3D%22M507.331%20411.33c-0.002-0.002-0.004-0.004-0.006-0.005l-155.322-155.325%20155.322-155.325c0.002-0.002%200.004-0.003%200.006-0.005%201.672-1.673%202.881-3.627%203.656-5.708%202.123-5.688%200.912-12.341-3.662-16.915l-73.373-73.373c-4.574-4.573-11.225-5.783-16.914-3.66-2.080%200.775-4.035%201.984-5.709%203.655%200%200.002-0.002%200.003-0.004%200.005l-155.324%20155.326-155.324-155.325c-0.002-0.002-0.003-0.003-0.005-0.005-1.673-1.671-3.627-2.88-5.707-3.655-5.69-2.124-12.341-0.913-16.915%203.66l-73.374%2073.374c-4.574%204.574-5.784%2011.226-3.661%2016.914%200.776%202.080%201.985%204.036%203.656%205.708%200.002%200.001%200.003%200.003%200.005%200.005l155.325%20155.324-155.325%20155.326c-0.001%200.002-0.003%200.003-0.004%200.005-1.671%201.673-2.88%203.627-3.657%205.707-2.124%205.688-0.913%2012.341%203.661%2016.915l73.374%2073.373c4.575%204.574%2011.226%205.784%2016.915%203.661%202.080-0.776%204.035-1.985%205.708-3.656%200.001-0.002%200.003-0.003%200.005-0.005l155.324-155.325%20155.324%20155.325c0.002%200.001%200.004%200.003%200.006%200.004%201.674%201.672%203.627%202.881%205.707%203.657%205.689%202.123%2012.342%200.913%2016.914-3.661l73.373-73.374c4.574-4.574%205.785-11.227%203.662-16.915-0.776-2.080-1.985-4.034-3.657-5.707z%22%3E%3C%2Fpath%3E%0A%3C%2Fsvg%3E'); + --icon-crow: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20345.5%20341.9%22%3E%3Cpath%20d%3D%22M157.4%20249.7c-.3-.7-12-6-13-6l-46%2016.9c-13.8%2013.2-52.6%2068.1-71.8%2066.6-2.9-.2-4.3-2.8-7.1-2.9s-5.2%203.7-10.9%202.9c-13.3-1.9-10.8-4%205.4-22.4s35.9-34.2%2053.4-51.3c7.6-7.4%2015.1-14.9%2022-23l.4-1.4c-2.9-2.7-4.7%204-2.7-7.9s18.3-37.5%2025.3-48.5c15-23.8%2034.5-53.6%2055.2-72.4%206.2-5.6%2023.1-16%2026.8-21.1%2010.9-14.8%208.8-41.6%2022.1-58.7%2017.9-22.9%2056.4-27.6%2078.8-9%202.4%202%206.7%209.3%2010%2011s15.2%202.8%2020.5%204.5c3.4%201.1%2022.6%209.2%2019.4%2013.9l-36.7%2013.6c-4.7%208-11.4%2014.8-12.2%2025.2s2%204.9%209.2%2032.6-.3%2048.8-13.7%2072.6-32.6%2041.2-57.1%2053.1l-3.2%2013.3%2030.9%2032.9c5.8-1.5%2013-1.6%2018.2-2.7s6.8-5%2013.9-2.5c4.9%201.8%205.3%205.3%206.8%206.2s3.8.5%205.7%201.3c4.8%202%207.2%208.2%206.2%2013.2-1.6-.2-1.3-2.3-3-3.5-8.1-6.1-8.2%203.3-16.9%201.5%201.4%205.7%207%209.4%202.5%2016-.6%200-1.9-7.7-6.4-8.6-11.5%208.2-24.2-9-34.6-9.4-5.3-.2-12%204-19.9%203-5.2-.7-7.4-6.8-12.4-2.5-1.7%201.4-1.6%204.6-4%205.4-1.9-16.6%2015.3-14.2%2026.4-15%201.5-.1%202.9.8%202.5-1.5L220%20254.6c-4-1.5-6.8-3.2-9.5-6.5l-21.2%201.7c-3%202.5-12%2011.4-11.5%2015l27%2045.3c12%206.9%2031.1-4%2042.1%205.2%205%204.1%203.4%207.5%204.7%2012.2s4.9%207.6%201.8%2013.7c-3%20.6%201.3%202.6-2.9-4.5s-6.4-2.4-10.4-2.5c-2.1-.1-8-3.6-8.6-3l2%2010c-3.2%201.1-4.3-3.3-6.6-3.8s-5.5%201.4-10.2%200c-7.8-2.3-16.2-16-20.8-17.1s-11%203.2-18.6-1.3c-4-2.3-5.3-8.2-11.4-5.1-2%201.1-1.5%203.1-3.2%203.3-.5-9.3%206.1-12%2014.4-10.9%203.4.4%206.5%202.6%2010.1%202.9%201.5.1%203%20.6%202.5-1.5-7.3-12.7-14.6-25.7-22.9-37.9-2.8-4.3-8.1-7.9-8.9-13-.2-2.1.5-6.2-.2-7.3z%22%2F%3E%3C%2Fsvg%3E'); + --icon-css-3: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%202048%202048%22%3E%0A%20%20%3Cpath%20fill%3D%22%23000%22%20fill-rule%3D%22evenodd%22%20d%3D%22M1582.48%2C1627.13l120.816-1353.38%2C1.585-17.752H343.116l1.582%2C17.752%2C120.689%2C1353.6%2C1.006%2C11.285%2C10.872%2C3.018%2C541.588%2C150.346h8.685l543.064-150.568%2C10.87-3.014%2C1.009-11.289v.002ZM1025.111%2C555.45h395.75l-11.913%2C138.491-390.414%2C173.558%2C6.576%2C31.226h366.391l-42.175%2C483.261-325.538%2C90.259-325.432-90.106-20.667-231.443h134l10.737%2C117.345%2C1.042%2C11.374%2C11.027%2C2.929%2C186.463%2C49.539%2C8.493-.064%2C184.316-51.231%2C10.978-3.053.964-11.315%2C18.428-216.732%2C1.504-17.688h-578.959l-12.198-139.091%2C377.014-161.362%2C26.761-11.469-6.386-31.354h-414.572l-11.81-133.074h399.62Z%22%2F%3E%0A%3C%2Fsvg%3E%0A'); + --icon-dark: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20448%20448%22%3E%0A%20%20%3Cpath%20fill%3D%22%23000%22%0A%20%20%20%20d%3D%22M355.656%20325.63c-9%201.5-18.25%202.25-27.5%202.25-92.75%200-168-75.25-168-168%200-31.75%209.25-62.75%2026-89.25-66.5%2019.75-114%2080.75-114%20153.25%200%2088.25%2071.75%20160%20160%20160%2048.25%200%2093.5-22%20123.5-58.25m50.75-21.249c-31.25%2067.75-99.75%20111.5-174.25%20111.5-105.75%200-192-86.25-192-192%200-103.75%2081.25-188%20184.75-191.75%207-.25%2012.75%203.75%2015.25%209.75%202.75%206.25%201%2013.5-3.75%2018-28.5%2026-44.25%2061.5-44.25%20100%200%2075%2061%20136%20136%20136q29.625%200%2057-12.75c6.25-2.75%2013.25-1.5%2018%203.25s6%2012%203.25%2018%22%20%2F%3E%0A%3C%2Fsvg%3E'); + --icon-desktop: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2016%2016%22%3E%0A%3Cpath%20fill%3D%22%23000%22%20d%3D%22M0%201v10h16v-10h-16zM15%2010h-14v-8h14v8zM10.5%2012h-5l-0.5%202-1%201h8l-1-1z%22%3E%3C%2Fpath%3E%0A%3C%2Fsvg%3E%0A'); + --icon-download: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2016%2016%22%3E%0A%3Cpath%20fill%3D%22%23000%22%20d%3D%22M8%209l4-4h-3v-4h-2v4h-3zM11.636%207.364l-1.121%201.121%204.064%201.515-6.579%202.453-6.579-2.453%204.064-1.515-1.121-1.121-4.364%201.636v4l8%203%208-3v-4z%22%3E%3C%2Fpath%3E%0A%3C%2Fsvg%3E%0A'); + --icon-earth: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2016%2016%22%3E%0A%3Cpath%20fill%3D%22%23000%22%20d%3D%22M8%200c-4.418%200-8%203.582-8%208s3.582%208%208%208%208-3.582%208-8-3.582-8-8-8zM8%2015c-0.984%200-1.92-0.203-2.769-0.57l3.643-4.098c0.081-0.092%200.126-0.21%200.126-0.332v-1.5c0-0.276-0.224-0.5-0.5-0.5-1.765%200-3.628-1.835-3.646-1.854-0.094-0.094-0.221-0.146-0.354-0.146h-2c-0.276%200-0.5%200.224-0.5%200.5v3c0%200.189%200.107%200.363%200.276%200.447l1.724%200.862v2.936c-1.813-1.265-3-3.366-3-5.745%200-1.074%200.242-2.091%200.674-3h1.826c0.133%200%200.26-0.053%200.354-0.146l2-2c0.094-0.094%200.146-0.221%200.146-0.354v-1.21c0.634-0.189%201.305-0.29%202-0.29%201.1%200%202.141%200.254%203.067%200.706-0.065%200.055-0.128%200.112-0.188%200.172-0.567%200.567-0.879%201.32-0.879%202.121s0.312%201.555%200.879%202.121c0.569%200.569%201.332%200.879%202.119%200.879%200.049%200%200.099-0.001%200.149-0.004%200.216%200.809%200.605%202.917-0.131%205.818-0.007%200.027-0.011%200.055-0.013%200.082-1.271%201.298-3.042%202.104-5.002%202.104z%22%3E%3C%2Fpath%3E%0A%3C%2Fsvg%3E%0A'); + --icon-embed: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2020%2016%22%3E%0A%3Cpath%20fill%3D%22%23000%22%20d%3D%22M13%2011.5l1.5%201.5%205-5-5-5-1.5%201.5%203.5%203.5z%22%3E%3C%2Fpath%3E%0A%3Cpath%20fill%3D%22%23000%22%20d%3D%22M7%204.5l-1.5-1.5-5%205%205%205%201.5-1.5-3.5-3.5z%22%3E%3C%2Fpath%3E%0A%3Cpath%20fill%3D%22%23000%22%20d%3D%22M10.958%202.352l1.085%200.296-3%2011-1.085-0.296%203-11z%22%3E%3C%2Fpath%3E%0A%3C%2Fsvg%3E%0A'); + --icon-eye: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%201536%201536%22%3E%0A%3Cpath%20fill%3D%22%23000%22%20d%3D%22M575.841%20768c0%20105.951%2086.049%20192%20192%20192s192-86.049%20192-192v-1.599c-20.511%2020.799-48.639%2033.6-80.001%2033.6-61.791%200-111.999-50.241-111.999-111.999%200-44.481%2026.208-83.199%2063.681-101.121-19.839-7.041-41.28-10.881-63.681-10.881-105.921%200-192%2086.079-192%20192zM1492.959%20658.881c-119.040-151.362-408.96-402.882-724.8-402.882-316.161%200-606.369%20251.52-725.439%20402.879-26.88%2034.56-41.601%2072-42.561%20109.119%200.96%2037.119%2015.681%2074.559%2042.561%20109.119%20119.073%20151.395%20408.96%20402.885%20725.121%20402.885s606.048-251.487%20725.121-402.879c27.201-34.56%2041.919-72%2042.879-109.119-0.96-37.122-15.681-74.562-42.882-109.122zM767.841%201088.001c-176.64%200-320.001-143.361-320.001-320.001s143.361-320.001%20320.001-320.001%20320.001%20143.361%20320.001%20320.001c-0.003%20176.64-143.361%20320.001-320.001%20320.001z%22%2F%3E%0A%3C%2Fsvg%3E%0A'); + --icon-folder: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2016%2016%22%3E%0A%3Cpath%20fill%3D%22%23000%22%20d%3D%22M13%2015l3-8h-13l-3%208zM2%206l-2%209v-13h4.5l2%202h6.5v2z%22%3E%3C%2Fpath%3E%0A%3C%2Fsvg%3E%0A'); + --icon-heart-broken: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2016%2016%22%3E%0A%3Cpath%20fill%3D%22%23000%22%20d%3D%22M11.8%201c2.318%200%204.2%201.882%204.2%204.2%200%204.566-4.935%205.982-8%2010.616-3.243-4.663-8-5.9-8-10.616%200-2.319%201.882-4.2%204.2-4.2%200.943%200%201.812%200.43%202.512%201.060l-1.213%201.94%203.5%202-2%205%205.5-6-3.5-2%200.967-1.451c0.553-0.34%201.175-0.549%201.833-0.549z%22%3E%3C%2Fpath%3E%0A%3C%2Fsvg%3E%0A'); + --icon-heart: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2016%2016%22%3E%0A%3Cpath%20fill%3D%22%23000%22%20d%3D%22M11.8%201c-1.682%200-3.129%201.368-3.799%202.797-0.671-1.429-2.118-2.797-3.8-2.797-2.318%200-4.2%201.882-4.2%204.2%200%204.716%204.758%205.953%208%2010.616%203.065-4.634%208-6.050%208-10.616%200-2.319-1.882-4.2-4.2-4.2z%22%3E%3C%2Fpath%3E%0A%3C%2Fsvg%3E%0A'); + --icon-home: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2016%2016%22%3E%0A%3Cpath%20fill%3D%22%23000%22%20d%3D%22M16%209.226l-8-6.21-8%206.21v-2.532l8-6.21%208%206.21zM14%209v6h-4v-4h-4v4h-4v-6l6-4.5z%22%3E%3C%2Fpath%3E%0A%3C%2Fsvg%3E%0A'); + --icon-html-5: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2016%2016%22%3E%0A%3Cpath%20fill%3D%22%23000%22%20d%3D%22M0.946%200l1.284%2014.4%205.762%201.6%205.777-1.602%201.286-14.398h-14.108zM12.668%2013.482l-4.644%201.287v0.007l-0.012-0.004-0.012%200.004v-0.007l-4.644-1.287-1.098-12.304h11.508l-1.098%2012.304zM10.168%208.284l-0.204%202.29-1.972%200.532-1.967-0.53-0.126-1.41h-1.773l0.247%202.774%203.626%201.003%203.615-1.003%200.485-5.422h-6.437l-0.161-1.809h6.758l0.158-1.766h-8.847l0.477%205.341z%22%3E%3C%2Fpath%3E%0A%3C%2Fsvg%3E%0A'); + --icon-image: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2016%2016%22%3E%0A%3Cpath%20fill%3D%22%23000%22%20d%3D%22M13%2014h-10v-2l3-5%204.109%205%202.891-2v4z%22%3E%3C%2Fpath%3E%0A%3Cpath%20fill%3D%22%23000%22%20d%3D%22M13%207.5c0%200.828-0.672%201.5-1.5%201.5s-1.5-0.672-1.5-1.5%200.672-1.5%201.5-1.5c0.828%200%201.5%200.672%201.5%201.5z%22%3E%3C%2Fpath%3E%0A%3Cpath%20fill%3D%22%23000%22%20d%3D%22M14.341%203.579c-0.347-0.473-0.831-1.027-1.362-1.558s-1.085-1.015-1.558-1.362c-0.806-0.591-1.197-0.659-1.421-0.659h-7.75c-0.689%200-1.25%200.561-1.25%201.25v13.5c0%200.689%200.561%201.25%201.25%201.25h11.5c0.689%200%201.25-0.561%201.25-1.25v-9.75c0-0.224-0.068-0.615-0.659-1.421zM12.271%202.729c0.48%200.48%200.856%200.912%201.134%201.271h-2.406v-2.405c0.359%200.278%200.792%200.654%201.271%201.134zM14%2014.75c0%200.136-0.114%200.25-0.25%200.25h-11.5c-0.135%200-0.25-0.114-0.25-0.25v-13.5c0-0.135%200.115-0.25%200.25-0.25%200%200%207.749-0%207.75%200v3.5c0%200.276%200.224%200.5%200.5%200.5h3.5v9.75z%22%3E%3C%2Fpath%3E%0A%3C%2Fsvg%3E%0A'); + --icon-layout: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20768%20768%22%3E%0A%3Cpath%20fill%3D%22%23000%22%20d%3D%22M160%2064c-26.496%200-50.56%2010.784-67.872%2028.128s-28.128%2041.376-28.128%2067.872v448c0%2026.496%2010.784%2050.56%2028.128%2067.872s41.376%2028.128%2067.872%2028.128h448c26.496%200%2050.56-10.784%2067.872-28.128s28.128-41.376%2028.128-67.872v-448c0-26.496-10.784-50.56-28.128-67.872s-41.376-28.128-67.872-28.128zM640%20256h-512v-96c0-8.832%203.552-16.8%209.376-22.624s13.792-9.376%2022.624-9.376h448c8.832%200%2016.8%203.552%2022.624%209.376s9.376%2013.792%209.376%2022.624zM256%20320v320h-96c-8.832%200-16.8-3.552-22.624-9.376s-9.376-13.792-9.376-22.624v-288zM320%20640v-320h320v288c0%208.832-3.552%2016.8-9.376%2022.624s-13.792%209.376-22.624%209.376z%22%3E%3C%2Fpath%3E%0A%3C%2Fsvg%3E%0A'); + --icon-images: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2018%2016%22%3E%0A%3Cpath%20fill%3D%22%23000%22%20d%3D%22M17%202h-1v-1c0-0.55-0.45-1-1-1h-14c-0.55%200-1%200.45-1%201v12c0%200.55%200.45%201%201%201h1v1c0%200.55%200.45%201%201%201h14c0.55%200%201-0.45%201-1v-12c0-0.55-0.45-1-1-1zM2%203v10h-0.998c-0.001-0.001-0.001-0.001-0.002-0.002v-11.996c0.001-0.001%200.001-0.001%200.002-0.002h13.996c0.001%200.001%200.001%200.001%200.002%200.002v0.998h-12c-0.55%200-1%200.45-1%201v0zM17%2014.998c-0.001%200.001-0.001%200.001-0.002%200.002h-13.996c-0.001-0.001-0.001-0.001-0.002-0.002v-11.996c0.001-0.001%200.001-0.001%200.002-0.002h13.996c0.001%200.001%200.001%200.001%200.002%200.002v11.996z%22%3E%3C%2Fpath%3E%0A%3Cpath%20fill%3D%22%23000%22%20d%3D%22M15%205.5c0%200.828-0.672%201.5-1.5%201.5s-1.5-0.672-1.5-1.5%200.672-1.5%201.5-1.5%201.5%200.672%201.5%201.5z%22%3E%3C%2Fpath%3E%0A%3Cpath%20fill%3D%22%23000%22%20d%3D%22M16%2014h-12v-2l3.5-6%204%205h1l3.5-3z%22%3E%3C%2Fpath%3E%0A%3C%2Fsvg%3E%0A'); + --icon-light: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20768%20768%22%3E%0A%20%20%3Cpath%20fill%3D%22%23000%22%0A%20%20%20%20d%3D%22M576%20384c0-53.024-21.536-101.056-56.224-135.776S437.024%20192%20384%20192s-101.056%2021.536-135.776%2056.224S192%20330.976%20192%20384s21.536%20101.056%2056.224%20135.776S330.976%20576%20384%20576s101.056-21.536%20135.776-56.224S576%20437.024%20576%20384m-64%200c0%2035.36-14.304%2067.296-37.504%2090.496S419.36%20512%20384%20512s-67.296-14.304-90.496-37.504S256%20419.36%20256%20384s14.304-67.296%2037.504-90.496S348.64%20256%20384%20256s67.296%2014.304%2090.496%2037.504S512%20348.64%20512%20384M352%2032v64c0%2017.664%2014.336%2032%2032%2032s32-14.336%2032-32V32c0-17.664-14.336-32-32-32s-32%2014.336-32%2032m0%20640v64c0%2017.664%2014.336%2032%2032%2032s32-14.336%2032-32v-64c0-17.664-14.336-32-32-32s-32%2014.336-32%2032M112.416%20157.664l45.44%2045.44c12.512%2012.512%2032.768%2012.512%2045.248%200s12.512-32.768%200-45.248l-45.44-45.44c-12.512-12.512-32.768-12.512-45.248%200s-12.512%2032.768%200%2045.248m452.48%20452.48%2045.44%2045.44c12.512%2012.512%2032.768%2012.512%2045.248%200s12.512-32.768%200-45.248l-45.44-45.44c-12.512-12.512-32.768-12.512-45.248%200s-12.512%2032.768%200%2045.248M32%20416h64c17.664%200%2032-14.336%2032-32s-14.336-32-32-32H32c-17.664%200-32%2014.336-32%2032s14.336%2032%2032%2032m640%200h64c17.664%200%2032-14.336%2032-32s-14.336-32-32-32h-64c-17.664%200-32%2014.336-32%2032s14.336%2032%2032%2032M157.664%20655.584l45.44-45.44c12.512-12.512%2012.512-32.768%200-45.248s-32.768-12.512-45.248%200l-45.44%2045.44c-12.512%2012.512-12.512%2032.768%200%2045.248s32.768%2012.512%2045.248%200m452.48-452.48%2045.44-45.44c12.512-12.512%2012.512-32.768%200-45.248s-32.768-12.512-45.248%200l-45.44%2045.44c-12.512%2012.512-12.512%2032.768%200%2045.248s32.768%2012.512%2045.248%200%22%20%2F%3E%0A%3C%2Fsvg%3E'); + --icon-lock-open: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20640%20640%22%3E%0A%3Cpath%20fill%3D%22%23000%22%20d%3D%22M128%20256v-64c0-106.039%2085.961-192%20192-192s192%2085.961%20192%20192v0h-96v64h128c35.346%200%2064%2028.654%2064%2064v0%20256c0%2035.346-28.654%2064-64%2064v0h-448c-35.346%200-64-28.654-64-64v0-256c0-35.2%2028.8-64%2064-64h32zM288%20471.36v72.64h64v-72.64c19.265-11.272%2032-31.861%2032-55.426%200-35.346-28.654-64-64-64s-64%2028.654-64%2064c0%2023.564%2012.735%2044.154%2031.698%2055.262l0.302%200.164zM224%20192v64h192v-64c0-53.019-42.981-96-96-96s-96%2042.981-96%2096v0z%22%2F%3E%0A%3C%2Fsvg%3E%0A'); + --icon-lock-closed: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20640%20640%22%3E%0A%3Cpath%20fill%3D%22%23000%22%20d%3D%22M128%20256v-64c0-106.039%2085.961-192%20192-192s192%2085.961%20192%20192v0%2064h32c35.346%200%2064%2028.654%2064%2064v0%20256c0%2035.346-28.654%2064-64%2064v0h-448c-35.346%200-64-28.654-64-64v0-256c0-35.2%2028.8-64%2064-64h32zM288%20471.36v72.64h64v-72.64c19.265-11.272%2032-31.861%2032-55.426%200-35.346-28.654-64-64-64s-64%2028.654-64%2064c0%2023.564%2012.735%2044.154%2031.698%2055.262l0.302%200.164zM224%20192v64h192v-64c0-53.019-42.981-96-96-96s-96%2042.981-96%2096v0z%22%2F%3E%0A%3C%2Fsvg%3E%0A'); + --icon-logo-apeegg-simple: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20340%20448%22%3E%0A%20%20%3Cpath%20fill%3D%22%23000%22%0A%20%20%20%20d%3D%22M305.249%20120.181C273.934%2053.884%20221.742%200%20169.834%200%20117.079%200%2064.323%2058.398%2033.008%20126.106%209.028%20178.861%200%20237.259%200%20278.167c0%2093.944%2076.171%20169.834%20169.834%20169.834s169.834-76.171%20169.834-169.834c0-41.753-9.31-104.383-34.418-157.985zM132.877%20390.166c-37.521.847-37.521-27.647-37.521-27.647%202.257-24.544%2020.03-19.184%2043.164-20.03s42.035%2016.927%2043.164%2026.801c.847%209.874-11.284%2020.03-48.806%2020.877zm115.949-180.272c0%2018.337%2014.952%2027.93%2018.056%2036.957%203.103%209.31%206.489%2027.083-2.821%2030.751-13.824%205.078-26.237-4.514-44.574-16.081s-7.617%2010.721-7.617%2010.721%2017.773%2028.493%2017.773%2055.295-21.441%2018.337-21.441%2018.337-30.468-25.108-69.683-25.108c-39.214%200-40.343%209.028-54.73%209.874-14.67.847-15.234-3.95-15.234-17.773s7.617-22.851%2018.056-34.7c7.335-8.181%206.489-23.134-5.925-16.927s-37.521%2015.234-38.368%200%2022.287-35.265%2022.287-49.934-32.161-29.904-33.854-41.471C29.34%20158.268%2047.678%2094.51%2081.532%2093.664c33.008-.847%2010.438%2028.493%2063.194%2029.904h6.771c7.899-.282%2021.723-.564%2043.728-20.594%2027.647-25.39%2052.192-20.03%2062.912-11.567s32.161%2020.03%2032.161%2057.552c0%2037.804-41.471%2042.317-41.471%2060.937z%22%20%2F%3E%0A%20%20%3Cpath%20fill%3D%22%23000%22%0A%20%20%20%20d%3D%22M116.232%20208.201c-12.695%201.975-29.058-9.028-34.418-12.977-.847-.564-1.975-.282-2.257.847-6.489%2024.544%206.206%2023.98%2012.413%2025.955%206.489%201.975%202.539%206.489-.282%208.181-3.385%201.693-15.799-1.128-15.799-1.128.564%207.335%2014.388%205.078%2020.03%204.796%201.41%200%202.821-.564%203.95-1.693%206.489-5.642%2029.622-25.955%2016.363-23.98zm69.4%205.643c-11.849-.282%205.078%2014.67%2013.542%2021.723%203.103%202.539%206.771%204.232%2010.721%204.796%206.489.847%2015.799.847%2016.081-5.642%200%200-13.259%203.103-15.799%201.128s-7.053-6.206-.282-8.181c6.489-1.975%2030.186-1.411%2022.569-27.93-.282%200-30.186%2014.388-46.831%2014.106zm-10.72%2020.312c-6.206%200-34.418%2028.212-34.983%2033.008-.282%204.514-7.053-31.033-26.237-30.186-19.184.564-12.977%2013.259-11.003%2019.184s16.645%2017.491%2021.159%2017.773c0%200-13.824-13.541-13.824-18.902s1.41-7.053%205.36-7.053%209.31%203.103%2012.695%2010.72c2.539%205.925%206.206%2013.824%207.617%2017.491.564%201.128%201.411%201.693%202.821%201.693.847%200%201.975-.564%202.257-1.128%203.95-5.642%2021.441-28.776%2031.879-28.776%2011.849%200%207.053%2012.413%203.95%2014.952-3.103%202.821-9.874%207.617-20.03%209.592%200%200%2013.824%203.95%2029.622-11.567%2015.799-14.952-3.385-26.801-11.284-26.801z%22%20%2F%3E%0A%3C%2Fsvg%3E'); + --icon-logo-apeegg: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20777%20448%22%3E%0A%20%20%3Cpath%20fill%3D%22%23000%22%0A%20%20%20%20d%3D%22M305.249%20120.181C273.934%2053.884%20221.742%200%20169.834%200%20117.079%200%2064.323%2058.398%2033.008%20126.106%209.028%20178.861%200%20237.259%200%20278.167c0%2093.944%2076.171%20169.834%20169.834%20169.834s169.834-76.171%20169.834-169.834c0-41.753-9.31-104.383-34.418-157.985zM132.877%20390.166c-37.521.847-37.521-27.647-37.521-27.647%202.257-24.544%2020.03-19.184%2043.164-20.03s42.035%2016.927%2043.164%2026.801c.847%209.874-11.284%2020.03-48.806%2020.877zm115.949-180.272c0%2018.337%2014.952%2027.93%2018.056%2036.957%203.103%209.31%206.489%2027.083-2.821%2030.751-13.824%205.078-26.237-4.514-44.574-16.081s-7.617%2010.721-7.617%2010.721%2017.773%2028.493%2017.773%2055.295-21.441%2018.337-21.441%2018.337-30.468-25.108-69.683-25.108c-39.214%200-40.343%209.028-54.73%209.874-14.67.847-15.234-3.95-15.234-17.773s7.617-22.851%2018.056-34.7c7.335-8.181%206.489-23.134-5.925-16.927s-37.521%2015.234-38.368%200%2022.287-35.265%2022.287-49.934-32.161-29.904-33.854-41.471C29.34%20158.268%2047.678%2094.51%2081.532%2093.664c33.008-.847%2010.438%2028.493%2063.194%2029.904h6.771c7.899-.282%2021.723-.564%2043.728-20.594%2027.647-25.39%2052.192-20.03%2062.912-11.567s32.161%2020.03%2032.161%2057.552c0%2037.804-41.471%2042.317-41.471%2060.937z%22%20%2F%3E%0A%20%20%3Cpath%20fill%3D%22%23000%22%0A%20%20%20%20d%3D%22M116.232%20208.201c-12.695%201.975-29.058-9.028-34.418-12.977-.847-.564-1.975-.282-2.257.847-6.489%2024.544%206.206%2023.98%2012.413%2025.955%206.489%201.975%202.539%206.489-.282%208.181-3.385%201.693-15.799-1.128-15.799-1.128.564%207.335%2014.388%205.078%2020.03%204.796%201.41%200%202.821-.564%203.95-1.693%206.489-5.642%2029.622-25.955%2016.363-23.98zm69.4%205.643c-11.849-.282%205.078%2014.67%2013.542%2021.723%203.103%202.539%206.771%204.232%2010.721%204.796%206.489.847%2015.799.847%2016.081-5.642%200%200-13.259%203.103-15.799%201.128s-7.053-6.206-.282-8.181c6.489-1.975%2030.186-1.411%2022.569-27.93-.282%200-30.186%2014.388-46.831%2014.106zm-10.72%2020.312c-6.206%200-34.418%2028.212-34.983%2033.008-.282%204.514-7.053-31.033-26.237-30.186-19.184.564-12.977%2013.259-11.003%2019.184s16.645%2017.491%2021.159%2017.773c0%200-13.824-13.541-13.824-18.902s1.41-7.053%205.36-7.053%209.31%203.103%2012.695%2010.72c2.539%205.925%206.206%2013.824%207.617%2017.491.564%201.128%201.411%201.693%202.821%201.693.847%200%201.975-.564%202.257-1.128%203.95-5.642%2021.441-28.776%2031.879-28.776%2011.849%200%207.053%2012.413%203.95%2014.952-3.103%202.821-9.874%207.617-20.03%209.592%200%200%2013.824%203.95%2029.622-11.567%2015.799-14.952-3.385-26.801-11.284-26.801zm309.199-77.3H451.95l-14.388%2043.446h-42.599l48.524-165.038h49.652l48.242%20165.038h-42.317zM601.189%2035.265c7.053%200%2013.542%201.128%2019.184%203.668s10.72%206.206%2014.67%2010.721c3.95%204.796%207.335%2010.156%209.31%2016.927q3.385%209.734%203.385%2022.005c0%207.899-1.128%2015.234-3.385%2021.723s-5.36%2012.131-9.31%2016.927-9.028%208.463-14.67%2011.003-12.131%203.95-19.184%203.95h-12.413v58.116h-37.803V35.267h50.217zm160.806%2037.239h-63.758v26.801h46.831v37.239h-46.831v26.519h63.758v37.239H660.998V35.264h100.997zM502.73%20279.013h-64.04v26.519h47.114v37.239h-47.113v26.519h64.04v37.239H401.452V241.773h101.279zm134.005%2097.048c-2.821%203.95-6.206%207.899-10.156%2011.567s-8.181%207.053-12.977%209.874-10.156%205.078-15.799%206.771-11.849%202.539-18.056%202.539h-.282c-9.874%200-18.62-1.693-26.237-4.796-7.617-3.385-13.824-7.899-18.902-13.541s-9.028-12.695-11.849-20.594-4.232-16.645-4.796-26.237v-34.136c0-8.746%201.41-17.209%203.95-25.39%202.539-7.899%206.489-14.952%2011.849-20.877%205.078-5.925%2011.567-10.721%2019.184-14.388s16.645-5.36%2026.519-5.36h.282q15.234.846%2024.544%205.078c6.206%203.103%2011.284%206.489%2014.952%2010.438s6.489%207.899%208.463%2011.849q2.963%205.925%205.078%209.31l-30.468%2020.312c-.847-1.693-1.693-3.385-2.539-5.642s-2.257-3.95-3.95-5.642-3.95-3.385-6.206-4.514c-2.539-1.128-5.642-1.975-9.592-1.975-7.335%200-13.259%202.539-17.491%207.335-4.232%205.078-6.489%2011.567-6.489%2019.748v32.161c0%203.95.564%207.617%201.975%2011.284%201.411%203.385%203.103%206.489%205.36%208.746%202.257%202.539%205.078%204.514%208.181%205.925s6.489%202.257%2010.156%202.257c5.078.282%209.31-.847%2012.413-2.539%203.103-1.975%205.642-4.232%207.335-7.335v-8.181h-17.491v-31.879h53.32v57.834zm140.212%200c-2.821%203.95-6.206%207.899-10.156%2011.567s-8.181%207.053-12.977%209.874-10.156%205.078-15.799%206.771-11.849%202.539-18.056%202.539h-.564c-9.874%200-18.62-1.693-26.237-4.796-7.617-3.385-13.824-7.899-18.902-13.541s-9.028-12.695-11.849-20.594-4.232-16.645-4.514-26.237v-34.136c0-8.746%201.41-17.209%203.95-25.39%202.539-7.899%206.489-14.952%2011.849-20.877%205.078-5.925%2011.567-10.721%2019.184-14.388s16.645-5.36%2026.519-5.36h.282q15.234.846%2024.544%205.078c6.206%203.103%2011.284%206.489%2014.952%2010.438s6.489%207.899%208.463%2011.849q2.963%205.925%205.078%209.31l-30.468%2020.312c-.847-1.693-1.693-3.385-2.539-5.642s-2.257-3.95-3.95-5.642-3.95-3.385-6.206-4.514c-2.539-1.128-5.642-1.975-9.592-1.975-7.335%200-13.259%202.539-17.491%207.335-4.232%205.078-6.489%2011.567-6.489%2019.748v32.161c0%203.95.564%207.617%201.975%2011.284%201.41%203.385%203.103%206.489%205.36%208.746%202.257%202.539%205.078%204.514%208.181%205.925s6.489%202.257%2010.156%202.257c5.078.282%209.31-.847%2012.413-2.539%203.103-1.975%205.642-4.232%207.335-7.335v-8.181h-17.491v-31.879h53.32v57.834z%22%20%2F%3E%0A%3C%2Fsvg%3E'); + --icon-logo-react: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2023%2020.5%22%3E%0A%20%20%3Ccircle%20fill%3D%22%2361dafb%22%20cx%3D%2211.5%22%20cy%3D%2210.2%22%20r%3D%222%22%2F%3E%0A%20%20%3Cpath%20fill%3D%22%2361dafb%22%20d%3D%22M23%2C10.2c0-1.6-1.9-3-4.8-3.9.7-3%2C.5-5.3-.9-6.1-1.4-.8-3.5.1-5.8%2C2.3C9.3.4%2C7.2-.5%2C5.8.3c-1.2.7-1.6%2C2.4-1.2%2C4.8%2C0%2C.4.2.9.3%2C1.3-2.9.8-4.8%2C2.2-4.8%2C3.9s1.9%2C3%2C4.8%2C3.8c-.1.5-.2.9-.3%2C1.3-.4%2C2.4%2C0%2C4.1%2C1.2%2C4.8.3.2.7.3%2C1.2.3%2C1%2C0%2C2.2-.6%2C3.6-1.6.3-.3.7-.6%2C1-.9.3.3.7.6%2C1%2C.9%2C1.3%2C1.1%2C2.6%2C1.6%2C3.6%2C1.6s.8%2C0%2C1.2-.3c1.2-.7%2C1.6-2.4%2C1.2-4.8%2C0-.4-.2-.9-.3-1.3%2C2.9-.8%2C4.8-2.2%2C4.8-3.8ZM16.1%2C1c.2%2C0%2C.5%2C0%2C.7.2.9.5%2C1.1%2C2.4.5%2C5-.9-.2-2-.4-3.1-.5-.6-.9-1.3-1.7-1.9-2.4%2C1.5-1.5%2C2.9-2.3%2C3.9-2.3ZM7.4%2C12.6c.2.4.5.8.7%2C1.2-.7-.1-1.4-.2-2.1-.4.2-.6.4-1.3.7-2%2C.2.4.4.8.6%2C1.2ZM6.1%2C7.1c.6-.2%2C1.3-.3%2C2.1-.4-.2.4-.5.8-.7%2C1.1-.2.4-.4.8-.6%2C1.2-.3-.7-.5-1.3-.7-2ZM7.3%2C10.2c.3-.6.6-1.2%2C1-1.9.4-.6.7-1.2%2C1.1-1.8.7%2C0%2C1.4%2C0%2C2.1%2C0s1.4%2C0%2C2.1%2C0c.4.5.8%2C1.1%2C1.1%2C1.8.3.6.7%2C1.2%2C1%2C1.9-.3.6-.6%2C1.2-1%2C1.8-.4.6-.7%2C1.2-1.1%2C1.8-.7%2C0-1.4%2C0-2.1%2C0s-1.4%2C0-2.1%2C0c-.4-.6-.8-1.2-1.1-1.8-.4-.6-.7-1.2-1-1.8ZM15.6%2C12.6c.2-.4.4-.8.6-1.2.3.7.5%2C1.3.7%2C2-.6.1-1.3.3-2.1.4.2-.4.5-.8.7-1.2ZM15.6%2C7.9c-.2-.4-.5-.8-.7-1.1.7%2C0%2C1.4.2%2C2.1.4-.2.6-.4%2C1.3-.7%2C2-.2-.4-.4-.8-.6-1.2ZM11.5%2C3.9c.4.5.9%2C1%2C1.3%2C1.6-.4%2C0-.9%2C0-1.3%2C0s-.9%2C0-1.3%2C0c.5-.6.9-1.1%2C1.3-1.6ZM6.2%2C1.1c.2-.1.4-.2.7-.2.9%2C0%2C2.2.7%2C3.6%2C1.9.1%2C0%2C.2.2.3.3-.6.7-1.3%2C1.5-1.9%2C2.4-1.1%2C0-2.1.3-3.1.5-.1-.4-.2-.8-.3-1.2-.3-2%2C0-3.3.7-3.8ZM1%2C10.2c0-1.1%2C1.5-2.2%2C4.1-2.9.3.9.7%2C1.9%2C1.1%2C2.9-.5%2C1-.8%2C1.9-1.1%2C2.9-2.6-.7-4.1-1.8-4.1-2.9ZM9.9%2C18.1c-1.5%2C1.2-2.8%2C1.7-3.6%2C1.3-.8-.5-1-1.8-.7-3.8%2C0-.4.1-.8.3-1.2.9.2%2C2%2C.4%2C3.1.5.6.9%2C1.3%2C1.7%2C1.9%2C2.4-.3.3-.6.6-.9.8ZM10.2%2C14.9c.4%2C0%2C.9%2C0%2C1.3%2C0s.9%2C0%2C1.3%2C0c-.4.6-.9%2C1.1-1.3%2C1.6-.5-.5-.9-1-1.3-1.6ZM16.8%2C19.3c-.8.4-2.1%2C0-3.6-1.3-.3-.2-.6-.5-.9-.8.7-.7%2C1.3-1.5%2C1.9-2.4%2C1.1%2C0%2C2.1-.3%2C3.1-.5.1.4.2.8.3%2C1.2.3%2C2%2C0%2C3.3-.7%2C3.8ZM17.9%2C13.1c-.3-.9-.7-1.9-1.1-2.9.5-1%2C.8-2%2C1.1-2.9%2C2.6.7%2C4.1%2C1.8%2C4.1%2C2.9s-1.5%2C2.2-4.1%2C2.9Z%22%2F%3E%0A%3C%2Fsvg%3E%0A'); + --icon-logo-svelte: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2098.1%20118%22%3E%0A%20%20%3Cpath%20fill%3D%22%23ff3e00%22%20d%3D%22M91.8%2C15.6C80.9%2C0%2C59.2-4.7%2C43.6%2C5.2l-27.5%2C17.6c-7.5%2C4.7-12.7%2C12.4-14.2%2C21.1-1.3%2C7.3-.2%2C14.8%2C3.3%2C21.3-2.4%2C3.6-4%2C7.6-4.7%2C11.8-1.6%2C8.9.5%2C18.1%2C5.7%2C25.4%2C11%2C15.7%2C32.6%2C20.3%2C48.2%2C10.4l27.5-17.5c7.5-4.7%2C12.7-12.4%2C14.2-21.1%2C1.3-7.3.2-14.8-3.3-21.3%2C2.4-3.6%2C4-7.6%2C4.7-11.8%2C1.7-9-.4-18.2-5.7-25.5ZM84%2C38.1c-.2.9-.4%2C1.7-.7%2C2.6l-.5%2C1.6-1.4-1c-3.3-2.4-6.9-4.2-10.8-5.4l-1-.3v-1c.2-1.4-.2-2.9-1-4.1-1.6-2.3-4.4-3.3-7.1-2.6-.6.2-1.2.4-1.7.7l-27.4%2C17.5c-1.4.9-2.3%2C2.2-2.6%2C3.8-.3%2C1.6.1%2C3.3%2C1%2C4.6%2C1.6%2C2.3%2C4.4%2C3.3%2C7.1%2C2.6.6-.2%2C1.2-.4%2C1.7-.7l10.5-6.7c1.7-1.1%2C3.6-1.9%2C5.6-2.5%2C8.9-2.3%2C18.2%2C1.2%2C23.4%2C8.7%2C3.2%2C4.4%2C4.4%2C9.9%2C3.5%2C15.3-.9%2C5.2-4.1%2C9.9-8.6%2C12.7l-27.5%2C17.5c-1.7%2C1.1-3.6%2C1.9-5.6%2C2.5-8.9%2C2.3-18.2-1.2-23.4-8.7-3.2-4.4-4.4-9.9-3.5-15.3.2-.9.4-1.7.6-2.6l.5-1.6%2C1.4%2C1c3.3%2C2.4%2C6.9%2C4.2%2C10.8%2C5.4l1%2C.3v1c-.2%2C1.4.2%2C2.9%2C1%2C4.1%2C1.6%2C2.3%2C4.4%2C3.4%2C7.1%2C2.7.6-.2%2C1.2-.4%2C1.7-.7l27.4-17.5c1.4-.9%2C2.3-2.2%2C2.6-3.8.3-1.6%2C0-3.3-1-4.6-1.6-2.3-4.4-3.3-7.1-2.6-.6.2-1.2.4-1.7.7l-10.5%2C6.7c-1.7%2C1.1-3.6%2C1.9-5.6%2C2.4-8.9%2C2.3-18.2-1.2-23.4-8.7-3.1-4.4-4.4-9.9-3.4-15.3.9-5.2%2C4.1-9.9%2C8.6-12.7l27.5-17.5c1.7-1.1%2C3.6-1.9%2C5.6-2.5%2C8.9-2.3%2C18.2%2C1.2%2C23.4%2C8.7%2C3.2%2C4.4%2C4.4%2C9.9%2C3.5%2C15.3Z%22%2F%3E%0A%3C%2Fsvg%3E%0A'); + --icon-logo-vibe: url('data:image/svg+xml;utf8,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22UTF-8%22%3F%3E%0A%3Csvg%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20version%3D%221.1%22%20viewBox%3D%220%200%20260%20257%22%3E%0A%20%20%3Cpath%20fill%3D%22%23000%22%20d%3D%22M104.16%2C73.48c12.36-1.2%2C26.82%2C2.68%2C38.06%2C10.15%2C3.88%2C2.57%2C10.55%2C9.13%2C12.87%2C12.72%2C6.16%2C9.46%2C8.55%2C20.33%2C7.07%2C31.89-.8%2C5.98-.4%2C7.97%2C1.63%2C7.97%2C3.66%2C0%2C10.55-11.45%2C13.52-22.62%2C4.1-13.21.94-28.89.14-32.47-2.07-9.35-6.89-21.46-11.89-29.97-9.02-15.37-24.57-30.99-42.37-42.59-5.29-3.48-12.14-7.32-14.39-8.12-2.1-.72-6.05-.58-10.4.43-4.78%2C1.09-14.24%2C4.24-19.43%2C6.49-13.34%2C5.69-24.46%2C12.79-35.52%2C22.69-14.93%2C13.34-24.65%2C26.57-32.47%2C44.22C5.14%2C87.44%2C1.95%2C99.14.43%2C113.03c-.65%2C6.2-.54%2C21.49.22%2C27.91%2C1.49%2C12.29%2C4.82%2C24.46%2C9.39%2C34.14%2C3.77%2C7.97%2C5.55%2C9.89%2C7.36%2C7.79.83-.94.8-.91%2C2.14-11.82%2C3.41-27.47%2C16.2-54.51%2C34.47-72.7%2C14.97-14.93%2C31.42-23.09%2C50.16-24.86l-.01-.01Z%22%2F%3E%0A%20%20%3Cpath%20fill%3D%22%23000%22%20d%3D%22M257.31%2C105.29c-3.47-19.72-9.59-34.95-20.47-50.9-8.11-11.89-16.38-20.77-27.37-29.38-4.92-3.82-17.98-11.79-23.83-14.52-11.23-5.21-23.33-8.79-33.96-10.01-8.76-1-11.33-.5-10.5%2C2.15.36%2C1.21.35%2C1.16%2C8.92%2C8.04%2C21.55%2C17.37%2C37.79%2C42.49%2C43.63%2C67.61%2C4.8%2C20.59%2C3.06%2C38.87-5.3%2C55.73-5.5%2C11.13-16.41%2C21.38-28.68%2C27-4.23%2C1.94-13.32%2C4.15-17.59%2C4.23-11.29.25-21.79-3.44-30.83-10.8-4.66-3.83-6.57-4.54-7.64-2.82-1.93%2C3.11%2C4.18%2C15%2C12.1%2C23.41%2C9.06%2C10.45%2C24.05%2C16.02%2C27.52%2C17.24%2C9.04%2C3.17%2C21.86%2C5.45%2C31.74%2C5.69%2C17.82.43%2C39.29-4.56%2C58.52-13.57%2C5.75-2.66%2C12.62-6.46%2C14.48-7.95%2C1.72-1.4%2C3.68-4.84%2C5.11-9.07%2C1.6-4.64%2C3.9-14.34%2C4.72-19.93%2C2.19-14.33%2C2.02-27.53-.57-42.14v-.01Z%22%2F%3E%0A%20%20%3Cpath%20fill%3D%22%23000%22%20d%3D%22M231.57%2C197.33c-1.23-.27-1.18-.25-11.35%2C3.92-25.63%2C10.47-55.47%2C12.54-80.27%2C5.51-20.35-5.75-35.51-16.11-46.23-31.58-7.09-10.19-10.78-24.7-9.77-38.16.35-4.64%2C2.81-13.67%2C4.8-17.45%2C5.24-10%2C13.55-17.41%2C24.36-21.77%2C5.61-2.23%2C7.15-3.55%2C6.16-5.32-1.79-3.19-15.15-3.59-26.34-.73-13.53%2C2.88-25.65%2C13.31-28.39%2C15.76-7.14%2C6.38-15.34%2C16.5-20.32%2C25.03-8.99%2C15.39-15%2C36.59-16.41%2C57.79-.45%2C6.32-.44%2C14.17-.04%2C16.52.4%2C2.19%2C2.46%2C5.56%2C5.47%2C8.86%2C3.29%2C3.64%2C10.67%2C10.35%2C15.16%2C13.77%2C11.49%2C8.85%2C23.13%2C15.08%2C37.17%2C19.88%2C18.94%2C6.5%2C35.23%2C8.5%2C54.45%2C6.69%2C14.33-1.35%2C26.1-4.29%2C38.95-9.76%2C5.72-2.46%2C19.01-10.04%2C24.23-13.84%2C9.99-7.31%2C18.98-16.17%2C25.18-24.89%2C5.11-7.19%2C5.92-9.68%2C3.2-10.23h-.01Z%22%2F%3E%0A%3C%2Fsvg%3E'); + --icon-logo-x: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%3E%0A%3Cpath%20fill%3D%22%23000%22%20d%3D%22M14.234%2010.162%2022.977%200h-2.072l-7.591%208.824L7.251%200H.258l9.168%2013.343L.258%2024H2.33l8.016-9.318L16.749%2024h6.993zm-2.837%203.299-.929-1.329L3.076%201.56h3.182l5.965%208.532.929%201.329%207.754%2011.09h-3.182z%22%2F%3E%0A%3C%2Fsvg%3E%0A'); + --icon-logo-github: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2016%2016%22%3E%0A%3Cpath%20fill%3D%22%23000%22%20d%3D%22M8%200.198c-4.418%200-8%203.582-8%208%200%203.535%202.292%206.533%205.471%207.591%200.4%200.074%200.547-0.174%200.547-0.385%200-0.191-0.008-0.821-0.011-1.489-2.226%200.484-2.695-0.944-2.695-0.944-0.364-0.925-0.888-1.171-0.888-1.171-0.726-0.497%200.055-0.486%200.055-0.486%200.803%200.056%201.226%200.824%201.226%200.824%200.714%201.223%201.872%200.869%202.328%200.665%200.072-0.517%200.279-0.87%200.508-1.070-1.777-0.202-3.645-0.888-3.645-3.954%200-0.873%200.313-1.587%200.824-2.147-0.083-0.202-0.357-1.015%200.077-2.117%200%200%200.672-0.215%202.201%200.82%200.638-0.177%201.322-0.266%202.002-0.269%200.68%200.003%201.365%200.092%202.004%200.269%201.527-1.035%202.198-0.82%202.198-0.82%200.435%201.102%200.162%201.916%200.079%202.117%200.513%200.56%200.823%201.274%200.823%202.147%200%203.073-1.872%203.749-3.653%203.947%200.287%200.248%200.543%200.735%200.543%201.481%200%201.070-0.009%201.932-0.009%202.195%200%200.213%200.144%200.462%200.55%200.384%203.177-1.059%205.466-4.057%205.466-7.59%200-4.418-3.582-8-8-8z%22%3E%3C%2Fpath%3E%0A%3C%2Fsvg%3E%0A'); + --icon-quill: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2016%2016%22%3E%0A%3Cpath%20fill%3D%22%23000%22%20d%3D%22M0%2016c2-6%207.234-16%2016-16-4.109%203.297-6%2011-9%2011s-3%200-3%200l-3%205h-1z%22%3E%3C%2Fpath%3E%0A%3C%2Fsvg%3E%0A'); + --icon-pencil: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2016%2016%22%3E%0A%3Cpath%20fill%3D%22%23000%22%20d%3D%22M6%2010l2-1%207-7-1-1-7%207-1%202zM4.52%2013.548c-0.494-1.043-1.026-1.574-2.069-2.069l1.548-4.262%202-1.217%206-6h-3l-6%206-3%2010%2010-3%206-6v-3l-6%206-1.217%202z%22%3E%3C%2Fpath%3E%0A%3C%2Fsvg%3E%0A'); + --icon-play-button: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2016%2016%22%3E%0A%3Cpath%20fill%3D%22%23000%22%20d%3D%22M8%200c-4.418%200-8%203.582-8%208s3.582%208%208%208%208-3.582%208-8-3.582-8-8-8zM8%2014.5c-3.59%200-6.5-2.91-6.5-6.5s2.91-6.5%206.5-6.5%206.5%202.91%206.5%206.5-2.91%206.5-6.5%206.5zM6%204.5l6%203.5-6%203.5z%22%3E%3C%2Fpath%3E%0A%3C%2Fsvg%3E%0A'); + --icon-pin: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2016%2016%22%3E%0A%3Cpath%20fill%3D%22%23000%22%20d%3D%22M8%200c-2.761%200-5%202.239-5%205%200%205%205%2011%205%2011s5-6%205-11c0-2.761-2.239-5-5-5zM8%208c-1.657%200-3-1.343-3-3s1.343-3%203-3%203%201.343%203%203-1.343%203-3%203z%22%3E%3C%2Fpath%3E%0A%3C%2Fsvg%3E%0A'); + --icon-search: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20512%20512%22%3E%0A%3Cpath%20fill%3D%22%23000%22%20d%3D%22M496.131%20435.698l-121.276-103.147c-12.537-11.283-25.945-16.463-36.776-15.963%2028.628-33.534%2045.921-77.039%2045.921-124.588%200-106.039-85.961-192-192-192s-192%2085.961-192%20192%2085.961%20192%20192%20192c47.549%200%2091.054-17.293%20124.588-45.922-0.5%2010.831%204.68%2024.239%2015.963%2036.776l103.147%20121.276c17.661%2019.623%2046.511%2021.277%2064.11%203.678s15.946-46.449-3.677-64.11zM192%20320c-70.692%200-128-57.308-128-128s57.308-128%20128-128%20128%2057.308%20128%20128-57.307%20128-128%20128z%22%3E%3C%2Fpath%3E%0A%3C%2Fsvg%3E%0A'); + --icon-spinner: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20512%20512%22%3E%0A%3Cpath%20fill%3D%22%23000%22%20d%3D%22M304%2C480c-114.7%2C0-207.6-99.9-208-223.3%2C0-26.5-21.8-48.9-48.4-48.7-26.3.2-47.6%2C21.6-47.6%2C48%2C0%2C141.4%2C114.6%2C256%2C256%2C256s249.6-108.4%2C255.7-244.3c-5.6%2C118.3-96.5%2C212.3-207.7%2C212.3ZM256%2C0C118.5%2C0%2C6.4%2C108.4.3%2C244.3%2C5.9%2C126%2C96.8%2C32%2C208%2C32s207.6%2C99.9%2C208%2C223.3c0%2C26.5%2C21.8%2C48.9%2C48.4%2C48.7%2C26.3-.2%2C47.6-21.6%2C47.6-48C512%2C114.6%2C397.4%2C0%2C256%2C0Z%22%2F%3E%0A%3C%2Fsvg%3E%0A'); + --icon-smartphone: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2016%2016%22%3E%0A%3Cpath%20fill%3D%22%23000%22%20d%3D%22M12%200h-9c-0.55%200-1%200.45-1%201v14c0%200.55%200.45%201%201%201h9c0.55%200%201-0.45%201-1v-14c0-0.55-0.45-1-1-1zM7.5%2015.278c-0.43%200-0.778-0.348-0.778-0.778s0.348-0.778%200.778-0.778%200.778%200.348%200.778%200.778-0.348%200.778-0.778%200.778zM12%2013h-9v-11h9v11z%22%3E%3C%2Fpath%3E%0A%3C%2Fsvg%3E%0A'); + --icon-stack: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2016%2016%22%3E%0A%3Cpath%20fill%3D%22%23000%22%20d%3D%22M16%205l-8-4-8%204%208%204%208-4zM8%202.328l5.345%202.672-5.345%202.672-5.345-2.672%205.345-2.672zM14.398%207.199l1.602%200.801-8%204-8-4%201.602-0.801%206.398%203.199zM14.398%2010.199l1.602%200.801-8%204-8-4%201.602-0.801%206.398%203.199z%22%3E%3C%2Fpath%3E%0A%3C%2Fsvg%3E%0A'); + --icon-tablet: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2016%2016%22%3E%0A%3Cpath%20fill%3D%22%23000%22%20d%3D%22M12.5%200h-10c-0.825%200-1.5%200.675-1.5%201.5v13c0%200.825%200.675%201.5%201.5%201.5h10c0.825%200%201.5-0.675%201.5-1.5v-13c0-0.825-0.675-1.5-1.5-1.5zM7.5%2015.5c-0.276%200-0.5-0.224-0.5-0.5s0.224-0.5%200.5-0.5%200.5%200.224%200.5%200.5-0.224%200.5-0.5%200.5zM12%2014h-9v-12h9v12z%22%3E%3C%2Fpath%3E%0A%3C%2Fsvg%3E%0A'); + --icon-user: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2016%2016%22%3E%0A%3Cpath%20fill%3D%22%23000%22%20d%3D%22M9%2011.041v-0.825c1.102-0.621%202-2.168%202-3.716%200-2.485%200-4.5-3-4.5s-3%202.015-3%204.5c0%201.548%200.898%203.095%202%203.716v0.825c-3.392%200.277-6%201.944-6%203.959h14c0-2.015-2.608-3.682-6-3.959z%22%3E%3C%2Fpath%3E%0A%3C%2Fsvg%3E%0A'); + --icon-void: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20viewBox%3D%220%200%20120%20120%22%3E%0A%20%20%3Cpath%0A%20%20%20%20d%3D%22M111.82%2C60.419c-26.177%2C4.938-46.869%2C25.364-52.226%2C51.388-4.951-26.164-25.377-46.844-51.414-52.201%2C26.177-4.951%2C46.869-25.377%2C52.214-51.414%2C4.938%2C26.177%2C25.39%2C46.882%2C51.427%2C52.226Z%22%20%2F%3E%0A%3C%2Fsvg%3E'); + --icon-upload: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2016%2016%22%3E%0A%3Cpath%20fill%3D%22%23000%22%20d%3D%22M7%209h2v-4h3l-4-4-4%204h3zM10%206.75v1.542l4.579%201.708-6.579%202.453-6.579-2.453%204.579-1.708v-1.542l-6%202.25v4l8%203%208-3v-4z%22%3E%3C%2Fpath%3E%0A%3C%2Fsvg%3E%0A'); + --icon-components: url('data:image/svg+xml;utf8,%3C!--%20Generated%20by%20IcoMoon.io%20--%3E%0A%3Csvg%20version%3D%221.1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22512%22%20height%3D%22512%22%20viewBox%3D%220%200%20512%20512%22%3E%0A%3Cg%20id%3D%22icomoon-ignore%22%3E%0A%3C%2Fg%3E%0A%3Cpath%20fill%3D%22%23000%22%20d%3D%22M192%20288h-160c-17.688%200-32%2014.312-32%2032v160c0%2017.688%2014.312%2032%2032%2032h160c17.688%200%2032-14.312%2032-32v-160c0-17.688-14.312-32-32-32zM160%20448h-96v-96h96v96zM192%200h-160c-17.688%200-32%2014.312-32%2032v160c0%2017.688%2014.312%2032%2032%2032h160c17.688%200%2032-14.312%2032-32v-160c0-17.688-14.312-32-32-32zM160%20160h-96v-96h96v96zM480%20288h-160c-17.688%200-32%2014.312-32%2032v160c0%2017.688%2014.312%2032%2032%2032h160c17.688%200%2032-14.312%2032-32v-160c0-17.688-14.312-32-32-32zM448%20448h-96v-96h96v96zM480%200h-160c-17.688%200-32%2014.312-32%2032v160c0%2017.688%2014.312%2032%2032%2032h160c17.688%200%2032-14.312%2032-32v-160c0-17.688-14.312-32-32-32zM448%20160h-96v-96h96v96z%22%3E%3C%2Fpath%3E%0A%3C%2Fsvg%3E%0A'); + --icon-users: url('data:image/svg+xml;utf8,%3C!--%20Generated%20by%20IcoMoon.io%20--%3E%0A%3Csvg%20version%3D%221.1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22576%22%20height%3D%22512%22%20viewBox%3D%220%200%20576%20512%22%3E%0A%3Cg%20id%3D%22icomoon-ignore%22%3E%0A%3C%2Fg%3E%0A%3Cpath%20d%3D%22M384%20385.306v-26.39c35.249-19.864%2064-69.386%2064-118.916%200-79.529%200-144-96-144s-96%2064.471-96%20144c0%2049.53%2028.751%2099.052%2064%20118.916v26.39c-108.551%208.874-192%2062.21-192%20126.694h448c0-64.484-83.449-117.82-192-126.694z%22%3E%3C%2Fpath%3E%0A%3Cpath%20d%3D%22M163.598%20397.664c27.655-18.075%2062.040-31.818%2099.894-40.207-7.527-8.892-14.354-18.811-20.246-29.51-15.207-27.617-23.246-58.029-23.246-87.947%200-43.021%200-83.655%2015.3-116.881%2014.853-32.252%2041.564-52.248%2079.611-59.744-8.457-38.24-30.97-63.375-90.911-63.375-96%200-96%2064.471-96%20144%200%2049.53%2028.751%2099.052%2064%20118.916v26.39c-108.551%208.874-192%2062.21-192%20126.694h139.503c7.259-6.455%2015.298-12.586%2024.095-18.336z%22%3E%3C%2Fpath%3E%0A%3C%2Fsvg%3E%0A'); + --icon-menu: url('data:image/svg+xml;utf8,%3Csvg%20version%3D%221.1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22512%22%20height%3D%22512%22%20viewBox%3D%220%200%20512%20512%22%3E%0A%3Cpath%20fill%3D%22%23000%22%20d%3D%22M85%2091h341v42h-341z%22%3E%3C%2Fpath%3E%0A%3Cpath%20fill%3D%22%23000%22%20d%3D%22M85%20235h341v42h-341z%22%3E%3C%2Fpath%3E%0A%3Cpath%20fill%3D%22%23000%22%20d%3D%22M85%20379h341v42h-341z%22%3E%3C%2Fpath%3E%0A%3C%2Fsvg%3E%0A'); + --icon-stylecheat: url('data:image/svg+xml;utf8,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22UTF-8%22%3F%3E%0A%3Csvg%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20version%3D%221.1%22%20viewBox%3D%220%200%20128%20140%22%3E%0A%20%20%3Cpolygon%20fill%3D%22%23918dc6%22%20points%3D%2264%200%2084%2011%2064%2022%2044%2011%2064%200%22%2F%3E%0A%20%20%3Cpolygon%20fill%3D%22%23918dc6%22%20points%3D%2286%2012%20106%2023%2086%2034%2066%2023%2086%2012%22%2F%3E%0A%20%20%3Cpolygon%20fill%3D%22%23918dc6%22%20points%3D%22108%2024%20128%2035%20108%2046%2088%2035%20108%2024%22%2F%3E%0A%20%20%3Cpolygon%20fill%3D%22%23918dc6%22%20points%3D%2242%2012%2062%2023%2042%2034%2022%2023%2042%2012%22%2F%3E%0A%20%20%3Cpolygon%20fill%3D%22%23918dc6%22%20points%3D%2264%2024%2084%2035%2064%2046%2044%2035%2064%2024%22%2F%3E%0A%20%20%3Cpolygon%20fill%3D%22%23918dc6%22%20points%3D%2286%2036%20106%2047%2086%2058%2066%2047%2086%2036%22%2F%3E%0A%20%20%3Cpolygon%20fill%3D%22%23918dc6%22%20points%3D%2220%2024%2040%2035%2020%2046%200%2035%2020%2024%22%2F%3E%0A%20%20%3Cpolygon%20fill%3D%22%23918dc6%22%20points%3D%2242%2036%2062%2047%2042%2058%2022%2047%2042%2036%22%2F%3E%0A%20%20%3Cpolygon%20fill%3D%22%23918dc6%22%20points%3D%2264%2048%2084%2059%2064%2070%2044%2059%2064%2048%22%2F%3E%0A%20%20%3Cpolygon%20fill%3D%22%238888bc%22%20points%3D%220%2035%2020%2046%2020%2068%200%2057%200%2035%22%2F%3E%0A%20%20%3Cpolygon%20fill%3D%22%238888bc%22%20points%3D%2222%2047%2042%2058%2042%2080%2022%2069%2022%2047%22%2F%3E%0A%20%20%3Cpolygon%20fill%3D%22%238888bc%22%20points%3D%2244%2059%2064%2070%2064%2092%2044%2081%2044%2059%22%2F%3E%0A%20%20%3Cpolygon%20fill%3D%22%238888bc%22%20points%3D%220%2059%2020%2070%2020%2092%200%2081%200%2059%22%2F%3E%0A%20%20%3Cpolygon%20fill%3D%22%238888bc%22%20points%3D%2222%2071%2042%2082%2042%20104%2022%2093%2022%2071%22%2F%3E%0A%20%20%3Cpolygon%20fill%3D%22%238888bc%22%20points%3D%2244%2083%2064%2094%2064%20116%2044%20105%2044%2083%22%2F%3E%0A%20%20%3Cpolygon%20fill%3D%22%238888bc%22%20points%3D%220%2083%2020%2094%2020%20116%200%20105%200%2083%22%2F%3E%0A%20%20%3Cpolygon%20fill%3D%22%238888bc%22%20points%3D%2222%2095%2042%20106%2042%20128%2022%20117%2022%2095%22%2F%3E%0A%20%20%3Cpolygon%20fill%3D%22%238888bc%22%20points%3D%2244%20107%2064%20118%2064%20140%2044%20129%2044%20107%22%2F%3E%0A%20%20%3Cpolygon%20fill%3D%22%237676a3%22%20points%3D%2264%2070%2084%2059%2084%2081%2064%2092%2064%2070%22%2F%3E%0A%20%20%3Cpolygon%20fill%3D%22%237676a3%22%20points%3D%2286%2058%20106%2047%20106%2069%2086%2080%2086%2058%22%2F%3E%0A%20%20%3Cpolygon%20fill%3D%22%237676a3%22%20points%3D%22108%2046%20128%2035%20128%2057%20108%2068%20108%2046%22%2F%3E%0A%20%20%3Cpolygon%20fill%3D%22%237676a3%22%20points%3D%2264%2094%2084%2083%2084%20105%2064%20116%2064%2094%22%2F%3E%0A%20%20%3Cpolygon%20fill%3D%22%237676a3%22%20points%3D%2286%2082%20106%2071%20106%2093%2086%20104%2086%2082%22%2F%3E%0A%20%20%3Cpolygon%20fill%3D%22%237676a3%22%20points%3D%22108%2070%20128%2059%20128%2081%20108%2092%20108%2070%22%2F%3E%0A%20%20%3Cpolygon%20fill%3D%22%237676a3%22%20points%3D%2264%20118%2084%20107%2084%20129%2064%20140%2064%20118%22%2F%3E%0A%20%20%3Cpolygon%20fill%3D%22%237676a3%22%20points%3D%2286%20106%20106%2095%20106%20117%2086%20128%2086%20106%22%2F%3E%0A%20%20%3Cpolygon%20fill%3D%22%237676a3%22%20points%3D%22108%2094%20128%2083%20128%20105%20108%20116%20108%2094%22%2F%3E%0A%3C%2Fsvg%3E%0A'); +} + +icon, +.icon { + --icon: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20viewBox%3D%220%200%20120%20120%22%3E%0A%20%20%3Cpath%0A%20%20%20%20d%3D%22M111.82%2C60.419c-26.177%2C4.938-46.869%2C25.364-52.226%2C51.388-4.951-26.164-25.377-46.844-51.414-52.201%2C26.177-4.951%2C46.869-25.377%2C52.214-51.414%2C4.938%2C26.177%2C25.39%2C46.882%2C51.427%2C52.226Z%22%20%2F%3E%0A%3C%2Fsvg%3E'); + display: inline-block; + width: 1em; + height: 1em; + background-color: var(--color, currentColor); + mask-image: var(--icon); + mask-repeat: no-repeat; + mask-size: cover; +} + +icon[bell], +icon.bell { + --icon: var(--icon-bell); + width: 1em; + height: 1em; +} + +icon[book], +icon.book { + --icon: var(--icon-book); + width: 1em; + height: 1em; +} + +icon[bullhorn], +icon.bullhorn { + --icon: var(--icon-bullhorn); + width: 1em; + height: 1em; +} + +icon[checkmark], +icon.checkmark { + --icon: var(--icon-checkmark); + width: 1em; + height: 1em; +} + +icon[chevron-left], +icon.chevron-left { + --icon: var(--icon-chevron-left); + width: 1em; + height: 1em; +} + +icon[chevron-down], +icon.chevron-down { + --icon: var(--icon-chevron-down); + width: 1em; + height: 1em; +} + +icon[chevron-right], +icon.chevron-right { + --icon: var(--icon-chevron-right); + width: 1em; + height: 1em; +} + +icon[clock], +icon.clock { + --icon: var(--icon-clock); + width: 1em; + height: 1em; +} + +icon[chevron-up], +icon.chevron-up { + --icon: var(--icon-chevron-up); + width: 1em; + height: 1em; +} + +icon[cog], +icon.cog { + --icon: var(--icon-cog); + width: 1em; + height: 1em; +} + +icon[credit-card], +icon.credit-card { + --icon: var(--icon-credit-card); + width: 1em; + height: 1em; +} + +icon[cross], +icon.cross { + --icon: var(--icon-cross); + width: 1em; + height: 1em; +} + +icon[crow], +icon.crow { + --icon: var(--icon-crow); + width: 1.0105293945598128em; + height: 1em; +} + +icon[css-3], +icon.css-3 { + --icon: var(--icon-css-3); + width: 1em; + height: 1em; +} + +icon[dark], +icon.dark { + --icon: var(--icon-dark); + width: 1em; + height: 1em; +} + +icon[desktop], +icon.desktop { + --icon: var(--icon-desktop); + width: 1em; + height: 1em; +} + +icon[download], +icon.download { + --icon: var(--icon-download); + width: 1em; + height: 1em; +} + +icon[earth], +icon.earth { + --icon: var(--icon-earth); + width: 1em; + height: 1em; +} + +icon[embed], +icon.embed { + --icon: var(--icon-embed); + width: 1.25em; + height: 1em; +} + +icon[eye], +icon.eye { + --icon: var(--icon-eye); + width: 1em; + height: 1em; +} + +icon[folder], +icon.folder { + --icon: var(--icon-folder); + width: 1em; + height: 1em; +} + +icon[heart-broken], +icon.heart-broken { + --icon: var(--icon-heart-broken); + width: 1em; + height: 1em; +} + +icon[heart], +icon.heart { + --icon: var(--icon-heart); + width: 1em; + height: 1em; +} + +icon[home], +icon.home { + --icon: var(--icon-home); + width: 1em; + height: 1em; +} + +icon[html-5], +icon.html-5 { + --icon: var(--icon-html-5); + width: 1em; + height: 1em; +} + +icon[image], +icon.image { + --icon: var(--icon-image); + width: 1em; + height: 1em; +} + +icon[layout], +icon.layout { + --icon: var(--icon-layout); + width: 1em; + height: 1em; +} + +icon[images], +icon.images { + --icon: var(--icon-images); + width: 1.125em; + height: 1em; +} + +icon[light], +icon.light { + --icon: var(--icon-light); + width: 1em; + height: 1em; +} + +icon[lock-open], +icon.lock-open { + --icon: var(--icon-lock-open); + width: 1em; + height: 1em; +} + +icon[lock-closed], +icon.lock-closed { + --icon: var(--icon-lock-closed); + width: 1em; + height: 1em; +} + +icon[logo-apeegg-simple], +icon.logo-apeegg-simple { + --icon: var(--icon-logo-apeegg-simple); + width: 1em; + height: 1.3176470588235294em; +} + +icon[logo-apeegg], +icon.logo-apeegg { + --icon: var(--icon-logo-apeegg); + width: 1.734375em; + height: 1em; +} + +icon[logo-react], +icon.logo-react { + --icon: var(--icon-logo-react); + width: 1.1219512195121952em; + height: 1em; +} + +icon[logo-svelte], +icon.logo-svelte { + --icon: var(--icon-logo-svelte); + width: 0.8313559322033898em; + height: 1em; +} + +icon[logo-vibe], +icon.logo-vibe { + --icon: var(--icon-logo-vibe); + width: 1.0116731517509727em; + height: 1em; +} + +icon[logo-x], +icon.logo-x { + --icon: var(--icon-logo-x); + width: 1em; + height: 1em; +} + +icon[logo-github], +icon.logo-github { + --icon: var(--icon-logo-github); + width: 1em; + height: 1em; +} + +icon[quill], +icon.quill { + --icon: var(--icon-quill); + width: 1em; + height: 1em; +} + +icon[pencil], +icon.pencil { + --icon: var(--icon-pencil); + width: 1em; + height: 1em; +} + +icon[play-button], +icon.play-button { + --icon: var(--icon-play-button); + width: 1em; + height: 1em; +} + +icon[pin], +icon.pin { + --icon: var(--icon-pin); + width: 1em; + height: 1em; +} + +icon[search], +icon.search { + --icon: var(--icon-search); + width: 1em; + height: 1em; +} + +icon[spinner], +icon.spinner { + --icon: var(--icon-spinner); + width: 1em; + height: 1em; +} + +icon[smartphone], +icon.smartphone { + --icon: var(--icon-smartphone); + width: 1em; + height: 1em; +} + +icon[stack], +icon.stack { + --icon: var(--icon-stack); + width: 1em; + height: 1em; +} + +icon[tablet], +icon.tablet { + --icon: var(--icon-tablet); + width: 1em; + height: 1em; +} + +icon[user], +icon.user { + --icon: var(--icon-user); + width: 1em; + height: 1em; +} + +icon[void], +icon.void { + --icon: var(--icon-void); + width: 1em; + height: 1em; +} + +icon[upload], +icon.upload { + --icon: var(--icon-upload); + width: 1em; + height: 1em; +} + +icon[components], +icon.components { + --icon: var(--icon-components); + width: 1em; + height: 1em; +} + +icon[users], +icon.users { + --icon: var(--icon-users); + width: 1.125em; + height: 1em; +} + +icon[menu], +icon.menu { + --icon: var(--icon-menu); + width: 1em; + height: 1em; +} + +icon[stylecheat], +icon.stylecheat { + --icon: var(--icon-stylecheat); + width: 1em; + height: 1.09375em; +} +/* ---- Reset.css ---- */ +@layer base { + *, + ::after, + ::before, + ::backdrop, + ::file-selector-button { + box-sizing: border-box; + margin: 0; + padding: 0; + border: 0 solid; + } + + button, + input, + select, + optgroup, + textarea, + ::file-selector-button { + font: inherit; + font-feature-settings: inherit; + font-variation-settings: inherit; + letter-spacing: inherit; + } + + html, + :host { + line-height: 1.5; + } +} +/* ---- Stylecheat.css ---- */ +.stylecheat, [stylecheat] { + --unit: 4px; + --icon-down: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 448 448' fill='currentColor'%3E%3Cpath fill='black' d='m420.75 202-185.5 185.25a15.844 15.844 0 0 1-22.5 0L27.25 202c-6.25-6.25-6.25-16.5 0-22.75L68.75 138a15.844 15.844 0 0 1 22.5 0L224 270.75 356.75 138a15.844 15.844 0 0 1 22.5 0l41.5 41.25c6.25 6.25 6.25 16.5 0 22.75'/%3E%3C/svg%3E"); + --theme-sign: 1; + + --primary: oklch(0.62 0.14 260); + --blue: oklch(0.62 0.21 260); + --purple: oklch(0.63 0.26 304); + --green: oklch(0.72 0.22 142); + --orange: oklch(0.70 0.19 37); + --cyan: oklch(0.71 0.14 215); + --red: oklch(0.64 0.21 25); + --yellow: oklch(0.80 0.18 87); + --slate: oklch(0.55 0.05 264); +} + +.dark, [dark] { + --icon-down: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 448 448' fill='currentColor'%3E%3Cpath fill='white' d='m420.75 202-185.5 185.25a15.844 15.844 0 0 1-22.5 0L27.25 202c-6.25-6.25-6.25-16.5 0-22.75L68.75 138a15.844 15.844 0 0 1 22.5 0L224 270.75 356.75 138a15.844 15.844 0 0 1 22.5 0l41.5 41.25c6.25 6.25 6.25 16.5 0 22.75'/%3E%3C/svg%3E"); + --theme-sign: -1; + + --primary: oklch(0.75 0.12 260); + --blue: oklch(0.71 0.17 255); + --purple: oklch(0.71 0.20 306); + --green: oklch(0.79 0.21 152); + --orange: oklch(0.75 0.18 48); + --cyan: oklch(0.79 0.15 211); + --red: oklch(0.70 0.19 22); + --yellow: oklch(0.85 0.20 92); + --slate: oklch(0.70 0.04 257); +} +/* ---- Theme.css ---- */ +.stylecheat, +[stylecheat] { + --background: oklch(0.9383 0.0042 236.4993); + --foreground: oklch(0.3211 0 0); + --card: oklch(1 0 0); + --card-foreground: oklch(0.3211 0 0); + --popover: oklch(1 0 0); + --popover-foreground: oklch(0.3211 0 0); + --primary: oklch(0.6397 0.172 36.4421); + --primary-foreground: oklch(1 0 0); + --secondary: oklch(0.967 0.0029 264.5419); + --secondary-foreground: oklch(0.4461 0.0263 256.8018); + --muted: oklch(0.9846 0.0017 247.8389); + --muted-foreground: oklch(0.551 0.0234 264.3637); + --accent: oklch(0.9119 0.0222 243.8174); + --accent-foreground: oklch(0.3791 0.1378 265.5222); + --destructive: oklch(0.6368 0.2078 25.3313); + --destructive-foreground: oklch(1 0 0); + --border: oklch(0.9022 0.0052 247.8822); + --input: oklch(0.97 0.0029 264.542); + --ring: oklch(0.6397 0.172 36.4421); + --chart-1: oklch(0.7156 0.0605 248.6845); + --chart-2: oklch(0.7875 0.0917 35.9616); + --chart-3: oklch(0.5778 0.0759 254.1573); + --chart-4: oklch(0.5016 0.0849 259.4902); + --chart-5: oklch(0.4241 0.0952 264.0306); + --sidebar: oklch(0.903 0.0046 258.3257); + --sidebar-foreground: oklch(0.3211 0 0); + --sidebar-primary: oklch(0.6397 0.172 36.4421); + --sidebar-primary-foreground: oklch(1 0 0); + --sidebar-accent: oklch(0.9119 0.0222 243.8174); + --sidebar-accent-foreground: oklch(0.3791 0.1378 265.5222); + --sidebar-border: oklch(0.9276 0.0058 264.5313); + --sidebar-ring: oklch(0.6397 0.172 36.4421); + --font-sans: Inter, sans-serif; + --font-serif: Source Serif 4, serif; + --font-mono: JetBrains Mono, monospace; + --radius: 0.75rem; + --shadow-x: 0px; + --shadow-y: 1px; + --shadow-blur: 3px; + --shadow-spread: 0px; + --shadow-opacity: 0.1; + --shadow-color: hsl(0 0% 0%); + --shadow-2xs: 0px 1px 3px 0px hsl(0 0% 0% / 0.05); + --shadow-xs: 0px 1px 3px 0px hsl(0 0% 0% / 0.05); + --shadow-sm: 0px 1px 3px 0px hsl(0 0% 0% / 0.1), 0px 1px 2px -1px hsl(0 0% 0% / 0.1); + --shadow: 0px 1px 3px 0px hsl(0 0% 0% / 0.1), 0px 1px 2px -1px hsl(0 0% 0% / 0.1); + --shadow-md: 0px 1px 3px 0px hsl(0 0% 0% / 0.1), 0px 2px 4px -1px hsl(0 0% 0% / 0.1); + --shadow-lg: 0px 1px 3px 0px hsl(0 0% 0% / 0.1), 0px 4px 6px -1px hsl(0 0% 0% / 0.1); + --shadow-xl: 0px 1px 3px 0px hsl(0 0% 0% / 0.1), 0px 8px 10px -1px hsl(0 0% 0% / 0.1); + --shadow-2xl: 0px 1px 3px 0px hsl(0 0% 0% / 0.25); + --tracking-normal: 0em; + --spacing: 0.25rem; +} + +.dark, +[dark] { + --background: oklch(0.2598 0.0306 262.6666); + --foreground: oklch(0.9219 0 0); + --card: oklch(0.3106 0.0301 268.6365); + --card-foreground: oklch(0.9219 0 0); + --popover: oklch(0.29 0.0249 268.3986); + --popover-foreground: oklch(0.9219 0 0); + --primary: oklch(0.6397 0.172 36.4421); + --primary-foreground: oklch(1 0 0); + --secondary: oklch(0.3095 0.0266 266.7132); + --secondary-foreground: oklch(0.9219 0 0); + --muted: oklch(0.3095 0.0266 266.7132); + --muted-foreground: oklch(0.7155 0 0); + --accent: oklch(0.338 0.0589 267.5867); + --accent-foreground: oklch(0.8823 0.0571 254.1284); + --destructive: oklch(0.6368 0.2078 25.3313); + --destructive-foreground: oklch(1 0 0); + --border: oklch(0.3843 0.0301 269.7337); + --input: oklch(0.3843 0.0301 269.7337); + --ring: oklch(0.6397 0.172 36.4421); + --chart-1: oklch(0.7156 0.0605 248.6845); + --chart-2: oklch(0.7693 0.0876 34.1875); + --chart-3: oklch(0.5778 0.0759 254.1573); + --chart-4: oklch(0.5016 0.0849 259.4902); + --chart-5: oklch(0.4241 0.0952 264.0306); + --sidebar: oklch(0.31 0.0283 267.7408); + --sidebar-foreground: oklch(0.9219 0 0); + --sidebar-primary: oklch(0.6397 0.172 36.4421); + --sidebar-primary-foreground: oklch(1 0 0); + --sidebar-accent: oklch(0.338 0.0589 267.5867); + --sidebar-accent-foreground: oklch(0.8823 0.0571 254.1284); + --sidebar-border: oklch(0.3843 0.0301 269.7337); + --sidebar-ring: oklch(0.6397 0.172 36.4421); + --font-sans: Inter, sans-serif; + --font-serif: Source Serif 4, serif; + --font-mono: JetBrains Mono, monospace; + --radius: 0.75rem; + --shadow-x: 0px; + --shadow-y: 1px; + --shadow-blur: 3px; + --shadow-spread: 0px; + --shadow-opacity: 0.1; + --shadow-color: hsl(0 0% 0%); + --shadow-2xs: 0px 1px 3px 0px hsl(0 0% 0% / 0.05); + --shadow-xs: 0px 1px 3px 0px hsl(0 0% 0% / 0.05); + --shadow-sm: 0px 1px 3px 0px hsl(0 0% 0% / 0.1), 0px 1px 2px -1px hsl(0 0% 0% / 0.1); + --shadow: 0px 1px 3px 0px hsl(0 0% 0% / 0.1), 0px 1px 2px -1px hsl(0 0% 0% / 0.1); + --shadow-md: 0px 1px 3px 0px hsl(0 0% 0% / 0.1), 0px 2px 4px -1px hsl(0 0% 0% / 0.1); + --shadow-lg: 0px 1px 3px 0px hsl(0 0% 0% / 0.1), 0px 4px 6px -1px hsl(0 0% 0% / 0.1); + --shadow-xl: 0px 1px 3px 0px hsl(0 0% 0% / 0.1), 0px 8px 10px -1px hsl(0 0% 0% / 0.1); + --shadow-2xl: 0px 1px 3px 0px hsl(0 0% 0% / 0.25); +} + +@theme inline { + --color-background: var(--background); + --color-foreground: var(--foreground); + --color-card: var(--card); + --color-card-foreground: var(--card-foreground); + --color-popover: var(--popover); + --color-popover-foreground: var(--popover-foreground); + --color-primary: var(--primary); + --color-primary-foreground: var(--primary-foreground); + --color-secondary: var(--secondary); + --color-secondary-foreground: var(--secondary-foreground); + --color-muted: var(--muted); + --color-muted-foreground: var(--muted-foreground); + --color-accent: var(--accent); + --color-accent-foreground: var(--accent-foreground); + --color-destructive: var(--destructive); + --color-destructive-foreground: var(--destructive-foreground); + --color-border: var(--border); + --color-input: var(--input); + --color-ring: var(--ring); + --color-chart-1: var(--chart-1); + --color-chart-2: var(--chart-2); + --color-chart-3: var(--chart-3); + --color-chart-4: var(--chart-4); + --color-chart-5: var(--chart-5); + --color-sidebar: var(--sidebar); + --color-sidebar-foreground: var(--sidebar-foreground); + --color-sidebar-primary: var(--sidebar-primary); + --color-sidebar-primary-foreground: var(--sidebar-primary-foreground); + --color-sidebar-accent: var(--sidebar-accent); + --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); + --color-sidebar-border: var(--sidebar-border); + --color-sidebar-ring: var(--sidebar-ring); + + --font-sans: var(--font-sans); + --font-mono: var(--font-mono); + --font-serif: var(--font-serif); + + --radius-sm: calc(var(--radius) - 4px); + --radius-md: calc(var(--radius) - 2px); + --radius-lg: var(--radius); + --radius-xl: calc(var(--radius) + 4px); + + --shadow-2xs: var(--shadow-2xs); + --shadow-xs: var(--shadow-xs); + --shadow-sm: var(--shadow-sm); + --shadow: var(--shadow); + --shadow-md: var(--shadow-md); + --shadow-lg: var(--shadow-lg); + --shadow-xl: var(--shadow-xl); + --shadow-2xl: var(--shadow-2xl); +} + +/* @layer base { + * { + @apply border-border outline-ring/50; + } + body { + @apply bg-background text-foreground; + } +} */ +/* ---- Typography.css ---- */ +/* Typography - content flow with sibling-aware spacing */ +/* Visual styles apply globally, layout spacing requires [typography] or .typography wrapper */ + +/* Base text color */ +body { + color: var(--foreground); +} + +/* Headings - visual styles (sizes, colors, line-height, font-weight) */ +@layer base { + h1, + .h1, + h2, + .h2, + h3, + .h3, + h4, + .h4, + h5, + .h5, + h6, + .h6 { + margin: 0; + font-weight: 600; + color: var(--primary); + } +} + +h1, +.h1 { + font-size: calc(var(--unit) * 10); + line-height: calc(var(--unit) * 12); +} +h2, +.h2 { + font-size: calc(var(--unit) * 7); + line-height: calc(var(--unit) * 8); +} +h3, +.h3 { + font-size: calc(var(--unit) * 6); + line-height: calc(var(--unit) * 8); +} +h4, +.h4 { + font-size: calc(var(--unit) * 5); + line-height: calc(var(--unit) * 8); +} +h5, +.h5 { + font-size: calc(var(--unit) * 4); + line-height: calc(var(--unit) * 4); +} +h6, +.h6 { + font-size: calc(var(--unit) * 3.5); + line-height: calc(var(--unit) * 4); +} + +/* Paragraphs - visual styles */ +p { + margin: 0; + line-height: 1.6; +} + +/* Lists - visual styles */ +ul, +ol { + margin: 0; + padding-inline-start: calc(var(--unit) * 6); /* 24px */ +} + +li { + margin: 0; + line-height: 1.5; +} +li::marker { + color: oklch(from var(--foreground) calc(l + 0.05) c h); +} + +blockquote, +quote { + display: block; + margin: 0; + padding: calc(var(--unit) * 3) calc(var(--unit) * 4); + border-inline-start: calc(var(--unit) * 1) solid var(--primary); + border-radius: 0 calc(var(--radius) / 2) calc(var(--radius) / 2) 0; + background-color: oklch(from var(--primary) l c h / 0.06); + font-style: italic; + color: var(--muted-foreground); +} + +/* Inline elements - visual styles */ +strong, +b { + font-weight: 600; + color: oklch(from var(--foreground) calc(l + 0.05) c h); +} +em, +i { + font-style: italic; +} +small { + font-size: calc(var(--unit) * 3.5); +} /* 14px */ +mark { + background: oklch(from var(--primary) l c h / 0.1); + color: oklch(from var(--primary) calc(l + 0.2 * var(--theme-sign, 1)) c h); + padding: 0 calc(var(--unit) * 1.5); + border-radius: calc(var(--unit) * 0.5); +} + +/* --- Layout spacing (requires [typography] or .typography wrapper) --- */ +/* Uses wildcards for extensibility with custom elements */ +/* Order matters: later rules win when specificity is equal */ + +typography, +[typography], +.typography { + /* Fallback: direct siblings not explicitly covered */ + > * + * { + margin-top: calc(var(--unit) * 6); + } + + /* Nested lists - tighter */ + li > ul, + li > ol { + margin-top: calc(var(--unit) * 1); + } + + /* After headings - moderate space */ + h1 + *, + h2 + * { + margin-top: calc(var(--unit) * 3); + } + h3 + *, + h4 + *, + h5 + *, + h6 + * { + margin-top: calc(var(--unit) * 2); + } + + /* After paragraphs */ + p + * { + margin-top: calc(var(--unit) * 3); + } + + /* After lists */ + ul + *, + ol + * { + margin-top: calc(var(--unit) * 3); + } + + /* After blockquotes and pre */ + blockquote + *, + quote + *, + pre + *, + template + * { + margin-top: calc(var(--unit) * 4); + } + + /* Before headings - section break (last so they override above) */ + * + h1 { + margin-top: calc(var(--unit) * 8); + } + * + h2 { + margin-top: calc(var(--unit) * 7); + } + * + h3 { + margin-top: calc(var(--unit) * 6); + } + * + h4 { + margin-top: calc(var(--unit) * 5); + } + * + h5 { + margin-top: calc(var(--unit) * 4); + } + * + h6 { + margin-top: calc(var(--unit) * 4); + } + + /* HR spacing */ + hr + *, + * + hr { + margin-top: calc(var(--unit) * 6); + } +} diff --git a/eslint.config.js b/eslint.config.js deleted file mode 100644 index d670a334..00000000 --- a/eslint.config.js +++ /dev/null @@ -1,38 +0,0 @@ -import js from '@eslint/js'; -import ts from 'typescript-eslint'; -import svelte from 'eslint-plugin-svelte'; -import prettier from 'eslint-config-prettier'; -import globals from 'globals'; - -/** @type {import('eslint').Linter.Config[]} */ -export default [ - js.configs.recommended, - ...ts.configs.recommended, - ...svelte.configs['flat/recommended'], - prettier, - ...svelte.configs['flat/prettier'], - { - languageOptions: { - globals: { - ...globals.browser, - ...globals.node - } - } - }, - { - files: ['**/*.svelte'], - languageOptions: { - parserOptions: { - parser: ts.parser - } - } - }, - { - ignores: ['build/', '.svelte-kit/', 'dist/'], - rules: { - '@typescript-eslint/no-explicit-any': 'warn', - '@typescript-eslint/no-require-imports': 'off', - '@typescript-eslint/no-unused-vars': 'warn' - } - } -]; diff --git a/index.html b/index.html new file mode 100644 index 00000000..c70fb5b4 --- /dev/null +++ b/index.html @@ -0,0 +1,992 @@ + + + + + + + Battle Brawlers + + + + + + + + + + + + + + Elapsed ms: @[elapsedMilliseconds.toFixed(0)] + Duration: @[combat.duration] + + + Server: @[new Date(serverTimestampSnapshot).toLocaleString()] + + Debug + Ability Scaling + Equipment Scaling + Character Scaling + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +

Combat

+ @[Math.floor(elapsedMilliseconds / 1000)]s / @[Math.floor(combat.duration / 1000)]s +
+
+
+
+ + + + Your Team + vs + Enemy + + + + +
+ +
+ + @[c.name] + @[Math.max(0, c.combatStats.currentHealth)] / @[c.combatStats.maxHealth] + +
+
+
+ +
+
+
+ + + KO + + + Stunned @[c.statuses.isStunned.ticks] + + + Bleed @[c.statuses.isBleeding.ticks] + + + Vuln @[c.statuses.isVulnerable.ticks] + +
+ +
+ + +
+ +
+ + @[c.name] + @[Math.max(0, c.combatStats.currentHealth)] / @[c.combatStats.maxHealth] + +
+
+
+ +
+
+
+ + + KO + + + Stunned @[c.statuses.isStunned.ticks] + + + Bleed @[c.statuses.isBleeding.ticks] + + + Vuln @[c.statuses.isVulnerable.ticks] + +
+ +
+
+ + + + + +
+

Victory!

+

Your brawlers won the fight.

+
+ XP earned +
+ +
+ + + + +
+

Defeat

+

Your brawlers were knocked out.

+ +
+ + +
+
+ + + +
+
+ +

Game Menu

+ +
+ +
VOLUME
+ + + + + + + + + + + + + + + + + + + +
SETTINGS
+ + + + + + + + + +
+
+ + + +
+
+ +

Code of Conduct

+ +
+

Be respectful to other players.

+

No harassment, hate speech, or toxic behavior.

+

Play fair — no exploits or cheating.

+

Have fun!

+
+
+ + + +
+
+ +

Release Notes

+ +
+
v0.1.0
+

Initial Vibe + Stylecheat rewrite.

+
+
+ + + + +
+ +

@[dialog.props?.text || 'Are you sure?']

+ + + + +
+
+ + + + +
+
+ +

Account Progression

+ +
+
+ + +

Lv @[lvl]

+
+ +100 coins + + +1 brawler slot + + +4 maxHealth + +2 damage +
+ + Claimed + + + + + + + +
+ +
+
+
+ + + + +
+ + + @[tooltip.props.name] + + +

@[tooltip.props.description]

+ + + @[tooltip.props.ticks] ticks + + + Damage: @[tooltip.props.damagePercent]% + + + Healing: @[tooltip.props.healingPercent]% + + + Level @[tooltip.props.level] + + +
+ + HP: +@[tooltip.props.combatStats.maxHealth] + + + DMG: +@[tooltip.props.combatStats.damage] + + + Armor: +@[tooltip.props.combatStats.maxArmor] + + + Speed: +@[tooltip.props.combatStats.speed] + +
+ + + Tooltip + +
+
+ + + + +
+ + + @[notification.message] + + +
+ + + + + + + + + \ No newline at end of file diff --git a/js/actions.js b/js/actions.js new file mode 100644 index 00000000..e8d710b7 --- /dev/null +++ b/js/actions.js @@ -0,0 +1,32 @@ +export const disableGameKeyboard = () => { + $.gameKeyboardDisabled = true; +}; + +export const enableGameKeyboard = () => { + $.gameKeyboardDisabled = false; +}; + +export const notify = (payload) => { + const { error, warning, success, info } = payload; + const message = error || warning || success || info; + + if ($.notifications.find(n => n.message === message)) { + return true; + } + + const type = error ? 'error' : warning ? 'warning' : success ? 'success' : 'info'; + $.notifications.push({ type, message }); + + // Auto-dismiss after 3s + setTimeout(() => { + removeFirstNotification(); + }, 3000); + + return true; +}; + +export const removeFirstNotification = () => { + const [_first, ...rest] = $.notifications; + $.notifications = [...rest]; + return true; +}; diff --git a/js/app.js b/js/app.js new file mode 100644 index 00000000..35c6db8b --- /dev/null +++ b/js/app.js @@ -0,0 +1,65 @@ +const loadLocalStorage = (props) => + Object.entries(props).reduce( + (a, [key, value]) => ({ + ...a, + [key]: window.localStorage.getItem(key) + ? JSON.parse(window.localStorage.getItem(key)) + : value + }), + {} + ); + +const SETTINGS_DEFAULT_VOLUME = { + master: 0.5, + ambient: 0.25, + sfx: 0.5, + combat: 1 +}; + +const INITIAL_COMBAT = { + teamsStartState: [], + teamsEndState: [], + events: [], + duration: 0, + winningTeam: undefined, + fightId: undefined, + audio: [] +}; + +export default { + combat: INITIAL_COMBAT, + liveTeams: [], + elapsedMilliseconds: 0, + serverTimestampSnapshot: 0, + syncPerformanceNow: 0, + serverTimestamp: 0, + experience: 0, + coins: 400, + accountRewards: 1, + bossHighscore: 0, + characters: [], + inventory: [], + socket: undefined, + token: undefined, + selectedBrawlers: [], + maxBrawlers: 0, + tooltip: undefined, + dialog: undefined, + showAccountProgression: false, + notifications: [], + gameKeyboardDisabled: false, + keys: ['keyw','keya','keys','keyd','keyi','keyq','keye','keyz','keyx','keyc','arrowright','arrowleft','arrowdown','arrowup','escape','enter','shiftleft','shiftright','digit1','digit2','digit3','digit4','digit5','numpad1','numpad2','numpad3','numpad4','numpad5','numpad6','numpad7','numpad8','numpad9'].reduce((a, key) => ({ ...a, [key]: false }), {}), + overlay: '', + settings: loadLocalStorage({ + volume: SETTINGS_DEFAULT_VOLUME, + loginPageMode: 0, + openProperties: {}, + debugOpen: false, + showDetailedCharacterView: false + }), + route: '/', + routeParams: {}, + mqs: {} +}; + +export { SETTINGS_DEFAULT_VOLUME, INITIAL_COMBAT }; diff --git a/js/audio.js b/js/audio.js new file mode 100644 index 00000000..5eeef740 --- /dev/null +++ b/js/audio.js @@ -0,0 +1,58 @@ +const audioFiles = [ + '/static/audio/Fire & Shimmer.wav', + '/static/audio/Special Lootbox 13.wav', + '/static/audio/Stinger - Ominous Timpani.wav', + '/static/audio/Victory Stinger 1.wav', + '/static/audio/abilities/cheesyTactics/Food Use - Buff Style 1.wav', + '/static/audio/abilities/cheesyTactics/Food Use - Buff Style 2.wav', + '/static/audio/abilities/demoralizingShout/Fantasy Game Buff (1).wav', + '/static/audio/abilities/lacerate/SOW1 - Combo - Double Slash 1.wav', + '/static/audio/ambient/Pure Desert Wind.wav', + '/static/audio/ambient/Summer Day In Nature.wav', + '/static/audio/armor/Armor Layer (1).wav', + '/static/audio/armor/Armor Layer (2).wav', + '/static/audio/armor/Armor Layer (3).wav', + '/static/audio/armor/Armor Layer (4).wav', + '/static/audio/armor/Armor Layer (5).wav', + '/static/audio/armor/Armor Layer (6).wav', + '/static/audio/block/Axe Chop 1.wav', + '/static/audio/block/Axe Chop 2.wav', + '/static/audio/block/Axe Chop 3.wav', + '/static/audio/block/Axe Chop 4.wav', + '/static/audio/block/Axe Chop 5.wav', + '/static/audio/block/Axe Chop 6.wav', + '/static/audio/bow/Target Impact - Default (1).wav', + '/static/audio/dodge/Swing Whoosh (1).wav', + '/static/audio/dodge/Swing Whoosh (10).wav', + '/static/audio/dodge/Swing Whoosh (2).wav', + '/static/audio/dodge/Swing Whoosh (3).wav', + '/static/audio/dodge/Swing Whoosh (4).wav', + '/static/audio/dodge/Swing Whoosh (5).wav', + '/static/audio/dodge/Swing Whoosh (6).wav', + '/static/audio/dodge/Swing Whoosh (7).wav', + '/static/audio/dodge/Swing Whoosh (8).wav', + '/static/audio/dodge/Swing Whoosh (9).wav', + '/static/audio/slam/Hammer Maul 1.wav', + '/static/audio/slam/Hammer Maul 2.wav', + '/static/audio/slam/Hammer Maul 3.wav', + '/static/audio/slam/Hammer Maul 4.wav', + '/static/audio/slam/Slap Style Layer (1).wav', + '/static/audio/slam/Slap Style Layer (2).wav', + '/static/audio/slam/Slap Style Layer (3).wav', + '/static/audio/slash/Simple Cut 1.wav', + '/static/audio/slash/Simple Cut 2.wav', + '/static/audio/slash/Simple Cut 3.wav', + '/static/audio/stab/Spear Stab 1.wav', + '/static/audio/stab/Spear Stab 2.wav', + '/static/audio/stab/Spear Stab 3.wav', + '/static/audio/sword/swoosh.mp3', +]; + +export const AUDIO = {}; + +audioFiles.forEach(path => { + const filename = path.split('/').pop().replace(/\.(wav|mp3)$/, ''); + AUDIO[filename] = path; +}); + +export default AUDIO; diff --git a/js/coin.js b/js/coin.js new file mode 100644 index 00000000..af928437 --- /dev/null +++ b/js/coin.js @@ -0,0 +1,6 @@ +export const formatCoins = (amount) => { + const gold = 0; + const silver = Math.floor(amount / 100); + const copper = amount % 100; + return { gold, silver, copper }; +}; diff --git a/js/combat-loop.js b/js/combat-loop.js new file mode 100644 index 00000000..21ae4650 --- /dev/null +++ b/js/combat-loop.js @@ -0,0 +1,33 @@ +export const init = () => { + let animationId = null; + let lastTimestamp = 0; + + const tick = (timestamp) => { + if (!$.combat.duration) { + animationId = null; + return; + } + + if (!lastTimestamp) lastTimestamp = timestamp; + const delta = timestamp - lastTimestamp; + lastTimestamp = timestamp; + + if ($.elapsedMilliseconds < $.combat.duration) { + $.elapsedMilliseconds += delta; + + const events = $.combat.events; + const currentEvent = events.findLast(e => e.eventTimestamp <= $.elapsedMilliseconds); + if (currentEvent) $.liveTeams = currentEvent.teams; + } + + animationId = requestAnimationFrame(tick); + }; + + $.on('afterUpdate', (current, prev) => { + if (current.combat?.duration > 0 && !animationId) { + lastTimestamp = 0; + $.elapsedMilliseconds = 0; + animationId = requestAnimationFrame(tick); + } + }); +}; diff --git a/src/ts/combat.ts b/js/combat.js similarity index 75% rename from src/ts/combat.ts rename to js/combat.js index f582442a..c27d6b34 100644 --- a/src/ts/combat.ts +++ b/js/combat.js @@ -1,31 +1,21 @@ -import { ABILITY_PRIORITY, COMBAT_TICK_TIME } from '@/constants/APP'; -import type { CombatEvent } from '@/types/combat'; -import type { Team } from '@/types/team'; -import { calculateCombatStatsByCharacter, prepareCombatant, seededRandom } from '@/ts/utils'; -import type { Combatant } from '@/types/combatant'; -import type { VFX } from '@/types/vfx'; -import type { SFX } from '@/types/sfx'; -import _VFX from '@/constants/VFX'; -import _SFX from '@/constants/SFX'; -import { AbilityType, type Ability } from '@/types/ability'; -import type { CharacterRef } from '@/types/character'; -import CHARACTERS from '@/constants/CHARACTERS'; -import { ALL_FIGHTS } from '@/constants/FIGHTS'; - -const isLucky = (chance: number, seed: string, luckDisabled: boolean) => { +import { ABILITY_PRIORITY, COMBAT_TICK_TIME } from '/js/constants/APP.js'; +import { calculateCombatStatsByCharacter, prepareCombatant, seededRandom } from '/js/utils.js'; +import _VFX from '/js/constants/VFX.js'; +import _SFX from '/js/constants/SFX.js'; +import CHARACTERS from '/js/constants/CHARACTERS.js'; +import { ALL_FIGHTS } from '/js/constants/FIGHTS.js'; + +const isLucky = (chance, seed, luckDisabled) => { if (luckDisabled) return false; const random = seededRandom(0, 1, seed, 0.01); return random < chance; }; -export const healFull = (characters: CharacterRef[]) => { - return characters.map((character) => { +export const healFull = (characters) => + characters.map((character) => { const combatStats = calculateCombatStatsByCharacter(CHARACTERS(character, true)); - // character.overrides.combatStats.currentHealth = combatStats.maxHealth; - - // return character; return { ...character, overrides: { @@ -37,22 +27,18 @@ export const healFull = (characters: CharacterRef[]) => { } }; }); -}; -export const prepareTeams = (...teams: [CharacterRef[], CharacterRef[], ...CharacterRef[][]]) => { +export const prepareTeams = (...teams) => { const preparedCombatants = teams.map((team, teamIndex) => { - // Heal full for non-player teams - const preparedTeam = (teamIndex !== 0 ? healFull(team) : team).map( - (combatant, combatatantIndex) => { - return prepareCombatant( + (combatant, combatatantIndex) => + prepareCombatant( CHARACTERS(combatant, true), teams.length, team.length, teamIndex, combatatantIndex - ); - } + ) ); return preparedTeam; @@ -65,14 +51,14 @@ export const prepareTeams = (...teams: [CharacterRef[], CharacterRef[], ...Chara })); }; -const moreThanOneTeamStanding = (teams: Team[]) => { +const moreThanOneTeamStanding = (teams) => { const teamsStanding = teams.filter((team) => - team.combatants.some((combatant) => combatant.combatStats.currentHealth! > 0) + team.combatants.some((combatant) => combatant.combatStats.currentHealth > 0) ); return teamsStanding.length > 1; }; -const bufferAnimation = (combatant: Combatant, vfx: VFX, timestamp: number) => { +const bufferAnimation = (combatant, vfx, timestamp) => { const newVFX = structuredClone(vfx); newVFX.start = timestamp; @@ -83,7 +69,7 @@ const bufferAnimation = (combatant: Combatant, vfx: VFX, timestamp: number) => { combatant.animations.push(newVFX); }; -const bufferAudio = (audio: SFX[], sfx: SFX, timestamp: number) => { +const bufferAudio = (audio, sfx, timestamp) => { const newSFX = structuredClone(sfx); newSFX.id = crypto.randomUUID(); @@ -92,15 +78,7 @@ const bufferAudio = (audio: SFX[], sfx: SFX, timestamp: number) => { audio.push(newSFX); }; -// TODO: refine this, right now it adds more animations than needed -const injectAnimation = ( - combatant: Combatant, - target: Combatant, - currentAbility: Ability, - now: number, - teams: Team[], - events: CombatEvent[] = [] -) => { +const injectAnimation = (combatant, target, currentAbility, now, teams, events = []) => { const vfx = structuredClone(currentAbility.vfx); if ( !['basicAttackFast', 'basicAttackRegular', 'basicAttackSlow', 'whirlwind', 'block'].includes( @@ -117,7 +95,6 @@ const injectAnimation = ( vfx.id = crypto.randomUUID(); vfx.targetX = target.position.x; vfx.targetY = target.position.y; - // vfx.injected = true; combatant.injectedAnimations.push(vfx); @@ -130,27 +107,17 @@ const injectAnimation = ( }); }; -const pickAbility = ( - abilities: Ability[], - ability: Ability, - now: number, - startOrEnd: 'start' | 'end' -) => - now % abilities[abilities.length - 1].end! === - ability[startOrEnd]! % abilities[abilities.length - 1].end!; - -const sortByAbilityPriority = ( - a: Combatant, - b: Combatant, - now: number, - startOrEnd: 'start' | 'end' -) => { +const pickAbility = (abilities, ability, now, startOrEnd) => + now % abilities[abilities.length - 1].end === + ability[startOrEnd] % abilities[abilities.length - 1].end; + +const sortByAbilityPriority = (a, b, now, startOrEnd) => { const aAbility = a.abilities.find((ability) => - pickAbility(a.abilities, ability as Ability, now, startOrEnd) - ) as Ability; + pickAbility(a.abilities, ability, now, startOrEnd) + ); const bAbility = b.abilities.find((ability) => - pickAbility(b.abilities, ability as Ability, now, startOrEnd) - ) as Ability; + pickAbility(b.abilities, ability, now, startOrEnd) + ); const aPriority = ABILITY_PRIORITY.indexOf(aAbility.id); const bPriority = ABILITY_PRIORITY.indexOf(bAbility.id); @@ -163,20 +130,19 @@ const sortByAbilityPriority = ( return 0; }; -const timedAbility = (combatants: Combatant[], now: number, startOrEnd: 'start' | 'end') => +const timedAbility = (combatants, now, startOrEnd) => combatants.filter((combatant) => combatant.abilities.some((ability) => - pickAbility(combatant.abilities, ability as Ability, now, startOrEnd) + pickAbility(combatant.abilities, ability, now, startOrEnd) ) ); -const prioritySorting = (combatants: Combatant[], now: number, startOrEnd: 'start' | 'end') => - combatants.sort((a: Combatant, b: Combatant) => sortByAbilityPriority(a, b, now, startOrEnd)); +const prioritySorting = (combatants, now, startOrEnd) => + combatants.sort((a, b) => sortByAbilityPriority(a, b, now, startOrEnd)); -const tickStatusEffects = (combatants: Combatant[], now: number) => { +const tickStatusEffects = (combatants, now) => { combatants.forEach((combatant) => { if (combatant.statuses.isStunned.ticks > 0) { - // combatant.combatStats.currentHealth -= combatant.statuses.isStunned.value; combatant.statuses.isStunned.ticks -= 1; } @@ -199,13 +165,9 @@ const tickStatusEffects = (combatants: Combatant[], now: number) => { }); }; -export const generateCombat = ( - teams: Team[], - seed: string = `${Math.random()}`, - fightId?: string -) => { +export const generateCombat = (teams, seed = `${Math.random()}`, fightId) => { const luckDisabled = !!ALL_FIGHTS.find(({ id }) => id === fightId)?.boss; - const events: CombatEvent[] = []; + const events = []; let now = 0; let tickCount = 0; @@ -213,7 +175,7 @@ export const generateCombat = ( const teamsStartState = structuredClone(teams); teams = structuredClone(teams); - const audio: SFX[] = []; + const audio = []; let healingEfficiency = 1; @@ -225,7 +187,6 @@ export const generateCombat = ( ); while (moreThanOneTeamStanding(teams)) { - // console.info(`--- Tick ${tickCount} at ${now}ms ---`); const stillStandingCombatants = teams .flatMap((team) => team.combatants.map((combatant) => combatant)) .filter((combatant) => combatant.combatStats.currentHealth > 0); @@ -237,11 +198,9 @@ export const generateCombat = ( const orderedCombatantsStarting = prioritySorting(combatantsStarting, now, 'start'); const orderedCombatantsEnding = prioritySorting(combatantsEnding, now, 'end'); - // START OF ABILITY - // Put this there so that block actually blocks at start of ability orderedCombatantsStarting.forEach((combatant) => { const currentAbilityIndex = combatant.abilities.findIndex((ability) => - pickAbility(combatant.abilities, ability as Ability, now, 'start') + pickAbility(combatant.abilities, ability, now, 'start') ); const currentAbility = combatant.abilities[currentAbilityIndex]; @@ -252,13 +211,12 @@ export const generateCombat = ( combatant.damage = Math.floor(combatant.combatStats.damage * currentAbility.damage); }); - // END OF ABILITY orderedCombatantsEnding.forEach((combatant, i) => { const targetableCombatants = stillStandingCombatants.filter( ({ teamIndex }) => teamIndex !== combatant.teamIndex ); const currentAbilityIndex = combatant.abilities.findIndex((ability) => - pickAbility(combatant.abilities, ability as Ability, now, 'end') + pickAbility(combatant.abilities, ability, now, 'end') ); const currentAbility = combatant.abilities[currentAbilityIndex]; @@ -279,7 +237,7 @@ export const generateCombat = ( amount: 0 }; - const isWindUp = currentAbility.type === AbilityType.WindUp; + const isWindUp = currentAbility.type === 'WindUp'; const isStunned = combatant.statuses.isStunned; const isBlocking = target.statuses.isBlocking; @@ -336,13 +294,6 @@ export const generateCombat = ( luckDisabled ); - // console.info( - // isCritical, - // currentAbility.damage, - // combatant.combatStats.criticalDamage, - // currentAbility.damage + combatant.combatStats.criticalDamage - // ); - const abilityDamage = combatant.combatStats.damage * currentAbility.damage; const weakenedDamage = abilityDamage * (1 + target.statuses.isVulnerable.value); const criticalDamage = @@ -468,35 +419,27 @@ export const generateCombat = ( } if (currentAbility.id === 'kick') { - // My implementation attempt - // const targetCurrentAbility = target.abilitiesCopied.find( - // (ability) => - // ability.start % target.abilitiesCopied[target.abilitiesCopied.length - 1].end! <= - // now % target.abilitiesCopied[target.abilitiesCopied.length - 1].end! && - // ability.end % target.abilitiesCopied[target.abilitiesCopied.length - 1].end! > - // now % target.abilitiesCopied[target.abilitiesCopied.length - 1].end! - // ); - const total = target.abilitiesCopied[target.abilitiesCopied.length - 1].end!; - const t = ((now % total) + total) % total; // normalize now into [0,total) + const total = target.abilitiesCopied[target.abilitiesCopied.length - 1].end; + const t = ((now % total) + total) % total; const targetCurrentAbility = target.abilitiesCopied.find((ability) => { const start = ability.start % total; const end = ability.end % total; if (end > start) { - return t >= start && t < end; // normal segment + return t >= start && t < end; } else if (end < start) { - return t >= start || t < end; // wraps around + return t >= start || t < end; } else { - return true; // start === end → treat as full cycle, not zero-length + return true; } - }) as Required; + }); const endTime = targetCurrentAbility.end % total; const remainingTime = endTime > t - ? endTime - t // normal case - : total - t + endTime; // wrapped segment case + ? endTime - t + : total - t + endTime; const ticks = remainingTime / COMBAT_TICK_TIME; target.statuses.isStunned = { @@ -509,16 +452,6 @@ export const generateCombat = ( } } - // console.table({ - // Tick: tickCount, - // attacker: combatant.race, - // ability: currentAbility.id, - // damage: damage.amount, - // target: target.race, - // isBlocking, - // targetStatuses: target.statuses - // }); - teams.forEach((team) => team.combatants.forEach((c) => { if (c.combatStats.currentHealth <= 0 && !c.statuses.knockedOut) { @@ -547,17 +480,12 @@ export const generateCombat = ( now += COMBAT_TICK_TIME; tickCount += 1; - - // if (tickCount > 100) { - // console.warn('Breaking out of combat loop after 24 ticks.'); - // break; - // } } const duration = events[events.length - 1]?.eventTimestamp; const teamsEndState = events[events.length - 1].teams; const winningTeam = teamsEndState.find((team) => - team.combatants.some((combatant) => combatant.combatStats.currentHealth! > 0) + team.combatants.some((combatant) => combatant.combatStats.currentHealth > 0) ); const combined = Object.values( @@ -578,7 +506,7 @@ export const generateCombat = ( } return acc; }, - {} as Record + {} ) ); diff --git a/js/config.js b/js/config.js new file mode 100644 index 00000000..377a4e18 --- /dev/null +++ b/js/config.js @@ -0,0 +1,10 @@ +const IS_DEV = ['localhost', '127.0.0.1'].includes(location.hostname); + +export default { + WEBSOCKET_CONNECT: IS_DEV ? 'ws://localhost:1337' : 'wss://battle-brawlers-server.vercel.app', + ENVIRONMENT: IS_DEV ? 'development' : 'production', + IS_DEV, + IS_PROD: !IS_DEV, + AUTO_EMAIL: IS_DEV ? 'me@korte.kim' : '', + AUTO_PASSWORD: IS_DEV ? 'qwe123' : '' +}; diff --git a/js/connectSocket.js b/js/connectSocket.js new file mode 100644 index 00000000..c9b70b3e --- /dev/null +++ b/js/connectSocket.js @@ -0,0 +1,56 @@ +import config from '/js/config.js'; +import AsyncAwaitWebsocket from '/nodemodules/async-await-websockets/client.js'; + +let saveTimeout = null; + +const saveGameState = () => { + clearTimeout(saveTimeout); + saveTimeout = setTimeout(async () => { + if (!$.socket || !$.token) return; + + const res = await $.socket.sendAsync('store-game-state', { + token: $.token, + inventory: JSON.parse(JSON.stringify($.inventory)), + characters: JSON.parse(JSON.stringify($.characters)), + experience: $.experience, + coins: $.coins, + accountRewards: $.accountRewards + }); + + $.serverTimestampSnapshot = res; + $.syncPerformanceNow = performance.now(); + console.info('Game state saved'); + }, 1000); +}; + +const connectWebSocket = () => { + const ws = AsyncAwaitWebsocket(config.WEBSOCKET_CONNECT); + + ws.on('open', () => { + $.socket = ws; + console.info('Connected to game server'); + }); + + ws.on('close', () => { + console.info('Disconnected from game server, reconnecting...'); + }); + + ws.on('broadcast', (data) => { + console.info('Broadcast:', data); + }); +}; + +export const init = () => { + connectWebSocket(); + + $.on('afterUpdate', (current, prev) => { + const settings = current.settings; + if (settings) { + Object.entries(settings).forEach(([key, value]) => { + window.localStorage.setItem(key, JSON.stringify(value)); + }); + } + + saveGameState(); + }); +}; diff --git a/src/constants/ABILITIES.ts b/js/constants/ABILITIES.js similarity index 76% rename from src/constants/ABILITIES.ts rename to js/constants/ABILITIES.js index ff77a561..8d4e53a0 100644 --- a/src/constants/ABILITIES.ts +++ b/js/constants/ABILITIES.js @@ -1,15 +1,13 @@ -import { AbilityType, type Ability, type AbilityRef } from '@/types/ability'; -import VFX from '@/constants/VFX'; -import SFX from '@/constants/SFX'; -import entity from '@/ts/entity'; -import type { DynamicObject } from '@/types/common'; -import { deepMerge } from '@/helpers'; +import VFX from '/js/constants/VFX.js'; +import SFX from '/js/constants/SFX.js'; +import entity from '/js/entity.js'; +import { deepMerge } from '/js/helpers.js'; export const ALL_ABILITIES = { stab: { name: 'Stab', - type: AbilityType.WindUp, - description: '', //"Thrust with your weapon's sharp end.", + type: 'WindUp', + description: '', ticks: 2, icon: 'stab', basic: true, @@ -22,8 +20,8 @@ export const ALL_ABILITIES = { }, pierce: { name: 'Pierce', - type: AbilityType.WindUp, - description: '', //"Thrust with your weapon's sharp end.", + type: 'WindUp', + description: '', ticks: 3, icon: 'bowshot', basic: true, @@ -36,8 +34,8 @@ export const ALL_ABILITIES = { }, swing: { name: 'Swing', - type: AbilityType.WindUp, - description: '', // 'Swing your weapon in a wide arc.', + type: 'WindUp', + description: '', ticks: 3, icon: 'slash', basic: true, @@ -50,8 +48,8 @@ export const ALL_ABILITIES = { }, slam: { name: 'Slam', - type: AbilityType.WindUp, - description: '', //'Attack your opponent with a devestating slam.', + type: 'WindUp', + description: '', ticks: 4, icon: 'slam', basic: true, @@ -64,8 +62,8 @@ export const ALL_ABILITIES = { }, punch: { name: 'Punch', - type: AbilityType.WindUp, - description: '', //'Throw a punch at your opponent.', + type: 'WindUp', + description: '', ticks: 2, icon: 'punch', basic: true, @@ -78,7 +76,7 @@ export const ALL_ABILITIES = { }, block: { name: 'Block', - type: AbilityType.Channeling, + type: 'Channeling', description: 'Raise your shield to block. Prevent all damage for the duration.', ticks: 3, icon: 'block', @@ -92,7 +90,7 @@ export const ALL_ABILITIES = { }, shieldBash: { name: 'Shield Bash', - type: AbilityType.WindUp, + type: 'WindUp', description: 'Lunge forward and bash your opponent with your shield.', ticks: 3, icon: 'shieldBash', @@ -106,7 +104,7 @@ export const ALL_ABILITIES = { }, kick: { name: 'Kick', - type: AbilityType.WindUp, + type: 'WindUp', description: 'Kick your opponent, stunning them for the duration of their current ability.', ticks: 1, icon: 'kick', @@ -120,7 +118,7 @@ export const ALL_ABILITIES = { }, whirlwind: { name: 'Whirlwind', - type: AbilityType.Channeling, + type: 'Channeling', description: 'Quickly spin and deal damage each tick.', ticks: 10, chainLink: 10, @@ -135,7 +133,7 @@ export const ALL_ABILITIES = { }, lacerate: { name: 'Lacerate', - type: AbilityType.WindUp, + type: 'WindUp', description: 'Bleeds your opponent for 20% of your total damage.', ticks: 2, icon: 'lacerate', @@ -149,7 +147,7 @@ export const ALL_ABILITIES = { }, demoralizingShout: { name: 'Demoralizing Shout', - type: AbilityType.WindUp, + type: 'WindUp', description: 'Weakens your opponent. They take 50% more damage for the duration of the effect.', ticks: 2, icon: 'demoShout', @@ -163,7 +161,7 @@ export const ALL_ABILITIES = { }, cheesyTactics: { name: 'Cheesy Tactics', - type: AbilityType.Channeling, + type: 'Channeling', description: 'Restores some health.', ticks: 9, chainLink: 3, @@ -178,7 +176,7 @@ export const ALL_ABILITIES = { }, bite: { name: 'Bite', - type: AbilityType.WindUp, + type: 'WindUp', description: 'A vicious bite.
', ticks: 2, icon: 'bite', @@ -192,7 +190,7 @@ export const ALL_ABILITIES = { }, harden: { name: 'Harden', - type: AbilityType.WindUp, + type: 'WindUp', description: "If armor isn't depleted regain all armor.", ticks: 1, icon: 'fillArmor', @@ -206,7 +204,7 @@ export const ALL_ABILITIES = { }, playingTheVictim: { name: 'Playing the Victim', - type: AbilityType.WindUp, + type: 'WindUp', description: '', ticks: 12, icon: 'cross', @@ -220,11 +218,11 @@ export const ALL_ABILITIES = { } }; -const scalingCalc = (ticks: number, modifier: number = 0) => { +const scalingCalc = (ticks, modifier = 0) => { if (modifier === null) return; const maxTicks = 4; - const mod = 1.25; //;- modifier; + const mod = 1.25; const base = ticks / maxTicks; const pow = +Math.pow(base, mod).toFixed(2); const result = +(pow * (1 + modifier)).toFixed(2); @@ -232,20 +230,20 @@ const scalingCalc = (ticks: number, modifier: number = 0) => { return { base, pow, modifier, result }; }; -function damageCalc(this: Ability) { +function damageCalc() { const ticks = this.chainLink ? this.ticks / this.chainLink : this.ticks; const scaled = scalingCalc(ticks, this.damageModifier); return scaled; } -function healingCalc(this: Ability) { +function healingCalc() { const ticks = this.chainLink ? this.ticks / this.chainLink : this.ticks; const scaled = scalingCalc(ticks, this.healingModifier); return scaled; } -function durationCalc(this: Ability) { +function durationCalc() { if (this.durationModifier === null) return; const ticks = this.ticks; @@ -254,19 +252,7 @@ function durationCalc(this: Ability) { }; } -// for (const ability of Object.values(ALL_ABILITIES)) { -// Object.defineProperty(ability, 'calc', { -// enumerable: true, -// get() { -// return { -// damage: damageCalc.bind(this), -// healing: healingCalc.bind(this) -// }; -// } -// }); -// } - -function attachCalcs(a: Ability) { +function attachCalcs(a) { Object.defineProperty(a, 'calc', { enumerable: true, get() { @@ -280,7 +266,7 @@ function attachCalcs(a: Ability) { return a; } -export default (id: string | AbilityRef, fullBody: boolean = false, meta?: DynamicObject) => { +export default (id, fullBody = false, meta) => { const ent = entity( ALL_ABILITIES, typeof id === 'string' ? id : id.id, @@ -291,7 +277,7 @@ export default (id: string | AbilityRef, fullBody: boolean = false, meta?: Dynam : meta?.overrides ? deepMerge(id.overrides || {}, meta.overrides || {}) : id.overrides - ) as Ability; + ); - return (fullBody ? attachCalcs(ent) : ent) as Required; + return fullBody ? attachCalcs(ent) : ent; }; diff --git a/js/constants/APP.js b/js/constants/APP.js new file mode 100644 index 00000000..136186d0 --- /dev/null +++ b/js/constants/APP.js @@ -0,0 +1,3 @@ +export const COMBAT_TICK_TIME = 400; +export const COMBAT_RING_BASE_RADIUS = 250; +export const ABILITY_PRIORITY = ['stab', 'pierce', 'punch', 'slam', 'swing', 'whirlwind', 'block']; diff --git a/js/constants/AVAILABLE_KEYS.js b/js/constants/AVAILABLE_KEYS.js new file mode 100644 index 00000000..952a9bda --- /dev/null +++ b/js/constants/AVAILABLE_KEYS.js @@ -0,0 +1 @@ +export default ['keyw','keya','keys','keyd','keyi','keyq','keye','keyz','keyx','keyc','arrowright','arrowleft','arrowdown','arrowup','escape','enter','shiftleft','shiftright','digit1','digit2','digit3','digit4','digit5','numpad1','numpad2','numpad3','numpad4','numpad5','numpad6','numpad7','numpad8','numpad9'].reduce((a, key) => ({ ...a, [key]: false }), {}); diff --git a/src/constants/CHARACTERS.ts b/js/constants/CHARACTERS.js similarity index 94% rename from src/constants/CHARACTERS.ts rename to js/constants/CHARACTERS.js index fc189bc7..903a9dd1 100644 --- a/src/constants/CHARACTERS.ts +++ b/js/constants/CHARACTERS.js @@ -1,11 +1,7 @@ -import type { Character, CharacterRef, Race } from '@/types/character'; -import ABILITIES from '@/constants/ABILITIES'; -import type { DynamicObject } from '@/types/common'; -import entity from '@/ts/entity'; -import { deepMerge } from '@/helpers'; -import EQUIPMENT from '@/constants/EQUIPMENT'; - -export type CharacterKey = Race | 'creature'; +import ABILITIES from '/js/constants/ABILITIES.js'; +import entity from '/js/entity.js'; +import { deepMerge } from '/js/helpers.js'; +import EQUIPMENT from '/js/constants/EQUIPMENT.js'; const DEFAULT_MAX_HP = 24; const DEFAULT_DAMAGE = 8; @@ -25,6 +21,7 @@ export const DEFAULT_LUCKY_STATS = { blockChance: 0, magicChance: 0 }; + const DEFAULT_LIMITS = { wounded: 8, concussed: 8, @@ -47,10 +44,6 @@ const DEFAULT_NPC_EQUIPMENT = { trinket: null }; -// !!!IMPORTANT!!! -// Don't use { overrides } here, it breaks playable characters -// inheriting. If "creatures" need overrides, add them to the specific creature. - export const ALL_CHARACTERS = { elfMale: { name: 'Brawler', @@ -101,7 +94,7 @@ export const ALL_CHARACTERS = { }, maxTicks: DEFAULT_MAX_TICKS, abilities: [ - ABILITIES('pierce'), //, false, { overrides: { ticks: 12 } }), + ABILITIES('pierce'), ABILITIES('pierce'), ABILITIES('pierce'), ABILITIES('pierce') @@ -123,7 +116,6 @@ export const ALL_CHARACTERS = { damage: DEFAULT_DAMAGE, ...{ ...DEFAULT_LUCKY_STATS - // dodgeChance: 1 }, limits: DEFAULT_LIMITS, modifiers: { @@ -339,16 +331,12 @@ export const ALL_CHARACTERS = { }, maxTicks: DEFAULT_MAX_TICKS, abilities: [ - // ABILITIES('stab', false, { overrides: { ticks: 4 } }), - // ABILITIES('stab', false, { overrides: { ticks: 4 } }), - // ABILITIES('stab', false, { overrides: { ticks: 4 } }) ABILITIES('swing'), ABILITIES('swing'), ABILITIES('swing'), ABILITIES('swing') ] }, - // Creatures succubus: { name: 'Succubus', race: 'creature', @@ -449,7 +437,6 @@ export const ALL_CHARACTERS = { size: 1, equipment: { ...DEFAULT_NPC_EQUIPMENT - // offHand: EQUIPMENT('shield') }, description: '', element: '', @@ -461,7 +448,6 @@ export const ALL_CHARACTERS = { damage: 0, ...{ ...DEFAULT_LUCKY_STATS - // dodgeChance: 0.5 }, limits: DEFAULT_LIMITS, modifiers: DEFAULT_MODIFIERS @@ -487,7 +473,6 @@ export const ALL_CHARACTERS = { damage: DEFAULT_DAMAGE, ...{ ...DEFAULT_LUCKY_STATS - // dodgeChance: 0.6 }, limits: DEFAULT_LIMITS, modifiers: DEFAULT_MODIFIERS @@ -539,8 +524,6 @@ export const ALL_CHARACTERS = { size: 1, equipment: { ...DEFAULT_NPC_EQUIPMENT - // mainHand: EQUIPMENT('dagger', false, { overrides: { level: 5 } }) - // offHand: EQUIPMENT('shield') }, description: 'This civilian took the wrong turn when going shopping and ended up in the arena.', element: '', @@ -688,7 +671,7 @@ export const ALL_CHARACTERS = { } }; -export default (id: string | CharacterRef, fullBody: boolean = false, meta?: DynamicObject) => +export default (id, fullBody = false, meta) => entity( ALL_CHARACTERS, typeof id === 'string' ? id : id.id, @@ -699,4 +682,4 @@ export default (id: string | CharacterRef, fullBody: boolean = false, meta?: Dyn : meta?.overrides ? deepMerge(id.overrides || {}, meta.overrides || {}) : id.overrides - ) as Required; + ); diff --git a/src/constants/COMBAT_STATS.ts b/js/constants/COMBAT_STATS.js similarity index 100% rename from src/constants/COMBAT_STATS.ts rename to js/constants/COMBAT_STATS.js diff --git a/js/constants/ELEMENTS.js b/js/constants/ELEMENTS.js new file mode 100644 index 00000000..7d771d99 --- /dev/null +++ b/js/constants/ELEMENTS.js @@ -0,0 +1,7 @@ +export const ALL_ELEMENTS = { + nature: { name: 'Nature', color: { primary: '#8bb737', secondary: '#a0ce3e' } }, + earth: { name: 'Earth', color: { primary: '#7f5322', secondary: '#c68533' } }, + lightning: { name: 'Lightning', color: { primary: '#eac303', secondary: '#fced60' } }, + frost: { name: 'Frost', color: { primary: '#88c6cb', secondary: '#eaffff' } }, + fire: { name: 'Fire', color: { primary: '#f45f31', secondary: '#f4a031' } } +}; diff --git a/src/constants/EQUIPMENT.ts b/js/constants/EQUIPMENT.js similarity index 87% rename from src/constants/EQUIPMENT.ts rename to js/constants/EQUIPMENT.js index eac0ec43..cf7b53af 100644 --- a/src/constants/EQUIPMENT.ts +++ b/js/constants/EQUIPMENT.js @@ -1,8 +1,6 @@ -import ABILITIES from '@/constants/ABILITIES'; -import type { Equipment, EquipmentRef } from '@/types/equipment'; -import entity from '@/ts/entity'; -import type { DynamicObject } from '@/types/common'; -import { deepAdd, deepMerge } from '@/helpers'; +import ABILITIES from '/js/constants/ABILITIES.js'; +import entity from '/js/entity.js'; +import { deepAdd, deepMerge } from '/js/helpers.js'; const DEFAULT_COST = 100; const DEFAULT_LEVEL = 1; @@ -136,8 +134,6 @@ export const ALL_EQUIPMENT = { }, abilities: [ ABILITIES('swing', false, { overrides: { ticks: 12 } }) - // ABILITIES('swing', false, { overrides: { ticks: 4 } }), - // ABILITIES('swing', false, { overrides: { ticks: 4 } }) ] }, bow: { @@ -200,15 +196,7 @@ export const ALL_EQUIPMENT = { } }; -// export default (id: string | EquipmentRef, fullBody: boolean = false) => -// entity( -// ALL_EQUIPMENT, -// typeof id === 'string' ? id : id.id, -// typeof id === 'string' ? undefined : id.uuid, -// fullBody -// ) as Equipment; - -const applyScaling = (equipment: Equipment) => { +const applyScaling = (equipment) => { const isWeapon = ['oneHand', 'twoHand'].includes(equipment?.slotsIn); if (isWeapon) { const multiplier = equipment?.slotsIn === 'twoHand' ? 2 : 1; @@ -231,7 +219,7 @@ const applyScaling = (equipment: Equipment) => { return equipment; }; -export default (id: string | EquipmentRef, fullBody: boolean = false, meta?: DynamicObject) => +export default (id, fullBody = false, meta) => applyScaling( entity( ALL_EQUIPMENT, @@ -243,5 +231,5 @@ export default (id: string | EquipmentRef, fullBody: boolean = false, meta?: Dyn : meta?.overrides ? deepMerge(id.overrides || {}, meta.overrides || {}) : id.overrides - ) as Equipment - ) as Required; + ) + ); diff --git a/src/constants/FIGHTS.ts b/js/constants/FIGHTS.js similarity index 99% rename from src/constants/FIGHTS.ts rename to js/constants/FIGHTS.js index 761955e9..ddb1f580 100644 --- a/src/constants/FIGHTS.ts +++ b/js/constants/FIGHTS.js @@ -1,4 +1,4 @@ -import CHARACTERS from '@/constants/CHARACTERS'; +import CHARACTERS from '/js/constants/CHARACTERS.js'; export const ALL_FIGHTS = [ { diff --git a/src/constants/RECRUITABLE_CHARACTERS.ts b/js/constants/RECRUITABLE_CHARACTERS.js similarity index 98% rename from src/constants/RECRUITABLE_CHARACTERS.ts rename to js/constants/RECRUITABLE_CHARACTERS.js index 07c15d36..efb47687 100644 --- a/src/constants/RECRUITABLE_CHARACTERS.ts +++ b/js/constants/RECRUITABLE_CHARACTERS.js @@ -1,6 +1,6 @@ -import ABILITIES from '@/constants/ABILITIES'; -import EQUIPMENT from '@/constants/EQUIPMENT'; -import CHARACTERS, { DEFAULT_EQUIPMENT } from '@/constants/CHARACTERS'; +import ABILITIES from '/js/constants/ABILITIES.js'; +import EQUIPMENT from '/js/constants/EQUIPMENT.js'; +import CHARACTERS, { DEFAULT_EQUIPMENT } from '/js/constants/CHARACTERS.js'; const DEFAULT_ABILITIES = [ ABILITIES('punch'), diff --git a/src/constants/SFX.ts b/js/constants/SFX.js similarity index 99% rename from src/constants/SFX.ts rename to js/constants/SFX.js index 630139b7..22e9c55b 100644 --- a/src/constants/SFX.ts +++ b/js/constants/SFX.js @@ -72,4 +72,4 @@ export default { duration: 1500, variants: ['Fantasy Game Buff (1)'] } -} as any; +}; diff --git a/src/constants/STATUS_EFFECTS.ts b/js/constants/STATUS_EFFECTS.js similarity index 97% rename from src/constants/STATUS_EFFECTS.ts rename to js/constants/STATUS_EFFECTS.js index e8435372..5e12f9d6 100644 --- a/src/constants/STATUS_EFFECTS.ts +++ b/js/constants/STATUS_EFFECTS.js @@ -38,4 +38,4 @@ export default { convertsInto: 'isVulnerable', animation: 'animate-pulse' } -} as Record; +}; diff --git a/js/constants/VFX.js b/js/constants/VFX.js new file mode 100644 index 00000000..a0054f11 --- /dev/null +++ b/js/constants/VFX.js @@ -0,0 +1,14 @@ +export default { + basicAttackFast: { vfxName: 'basicAttackFast', duration: 1000 }, + basicAttackRegular: { vfxName: 'basicAttackRegular', duration: 1500 }, + basicAttackSlow: { vfxName: 'basicAttackSlow', duration: 2000 }, + block: { vfxName: 'block', duration: 2250 }, + kick: { vfxName: 'kick', duration: 500 }, + whirlwind: { vfxName: 'whirlwind', duration: 500 }, + hurt: { vfxName: 'hurt', duration: 500 }, + armorHurt: { vfxName: 'armorHurt', duration: 500 }, + attackBlocked: { vfxName: 'attackBlocked', duration: 340 }, + attackDodged: { vfxName: 'attackDodged', duration: 340 }, + heal: { vfxName: 'heal', duration: 500 }, + filler: { vfxName: 'filler', duration: 500 } +}; diff --git a/js/constants/WEAPON_TYPES.js b/js/constants/WEAPON_TYPES.js new file mode 100644 index 00000000..ca030104 --- /dev/null +++ b/js/constants/WEAPON_TYPES.js @@ -0,0 +1 @@ +export default ['1h', '2h', 'offHand']; diff --git a/js/customEvent.js b/js/customEvent.js new file mode 100644 index 00000000..0fa43f1c --- /dev/null +++ b/js/customEvent.js @@ -0,0 +1,2 @@ +export default (event, detail) => + document.dispatchEvent(new CustomEvent(event, { detail })); diff --git a/js/dialog.js b/js/dialog.js new file mode 100644 index 00000000..2f8c190a --- /dev/null +++ b/js/dialog.js @@ -0,0 +1,3 @@ +export const confirmWithDialog = (dialogId, props) => { + $.dialog = { dialogId, props }; +}; diff --git a/js/dnd.js b/js/dnd.js new file mode 100644 index 00000000..eecc84f7 --- /dev/null +++ b/js/dnd.js @@ -0,0 +1,296 @@ +// Custom drag-and-drop engine +// Game-feel: snappy bezier animations, pointer events for desktop+mobile +// FLIP technique for smooth reordering + +const EASE = 'cubic-bezier(0.2, 0.8, 0.2, 1)'; +const SNAP_DURATION = 180; +const SHIFT_DURATION = 150; + +const zones = new Map(); +let activeZone = null; +let dragState = null; + +const getItemRects = (el) => { + const items = [...el.children].filter(c => !c.hasAttribute('dnd-ghost')); + return items.map(child => ({ + el: child, + rect: child.getBoundingClientRect() + })); +}; + +const animateFLIP = (entries, duration = SHIFT_DURATION) => { + entries.forEach(({ el, rect: oldRect }) => { + const newRect = el.getBoundingClientRect(); + const dx = oldRect.left - newRect.left; + const dy = oldRect.top - newRect.top; + if (dx === 0 && dy === 0) return; + + el.animate([ + { transform: `translate(${dx}px, ${dy}px)` }, + { transform: 'translate(0, 0)' } + ], { + duration, + easing: EASE, + fill: 'none' + }); + }); +}; + +const getInsertIndex = (zoneEl, x, y, direction) => { + const children = [...zoneEl.children].filter(c => + !c.hasAttribute('dnd-ghost') && !c.hasAttribute('dnd-placeholder') + ); + if (!children.length) return 0; + + for (let i = 0; i < children.length; i++) { + const rect = children[i].getBoundingClientRect(); + const mid = direction === 'horizontal' + ? rect.left + rect.width / 2 + : rect.top + rect.height / 2; + const pos = direction === 'horizontal' ? x : y; + if (pos < mid) return i; + } + return children.length; +}; + +const createGhost = (sourceEl) => { + const ghost = sourceEl.cloneNode(true); + ghost.setAttribute('dnd-ghost', ''); + ghost.style.cssText = ` + position: fixed; + z-index: 1000; + pointer-events: none; + transition: transform 50ms ${EASE}; + transform: scale(1.05); + box-shadow: 0 8px 24px rgba(0,0,0,0.25); + opacity: 0.95; + `; + const rect = sourceEl.getBoundingClientRect(); + ghost.style.width = `${rect.width}px`; + ghost.style.height = `${rect.height}px`; + document.body.appendChild(ghost); + return ghost; +}; + +const createPlaceholder = (sourceEl) => { + const ph = document.createElement('div'); + ph.setAttribute('dnd-placeholder', ''); + const rect = sourceEl.getBoundingClientRect(); + ph.style.cssText = ` + width: ${rect.width}px; + height: ${rect.height}px; + border: 2px dashed var(--border, #ccc); + border-radius: var(--radius, 4px); + opacity: 0.5; + transition: all ${SHIFT_DURATION}ms ${EASE}; + `; + return ph; +}; + +const positionGhost = (ghost, x, y, sourceRect) => { + ghost.style.left = `${x - sourceRect.width / 2}px`; + ghost.style.top = `${y - sourceRect.height / 2}px`; +}; + +const findZoneUnder = (x, y) => { + for (const [el, config] of zones) { + const rect = el.getBoundingClientRect(); + if (x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom) { + return { el, config }; + } + } + return null; +}; + +const startDrag = (e, itemEl, zoneEl) => { + const config = zones.get(zoneEl); + if (!config) return; + + const items = config.items(); + const children = [...zoneEl.children].filter(c => + !c.hasAttribute('dnd-ghost') && !c.hasAttribute('dnd-placeholder') + ); + const itemIndex = children.indexOf(itemEl); + if (itemIndex === -1) return; + + const touch = e.touches ? e.touches[0] : e; + const rect = itemEl.getBoundingClientRect(); + + const ghost = createGhost(itemEl); + const placeholder = createPlaceholder(itemEl); + + positionGhost(ghost, touch.clientX, touch.clientY, rect); + + // Record positions before DOM change + const rects = getItemRects(zoneEl); + + // Replace item with placeholder + itemEl.style.display = 'none'; + zoneEl.insertBefore(placeholder, itemEl); + + // FLIP animate the shift + animateFLIP(rects); + + dragState = { + ghost, + placeholder, + sourceEl: itemEl, + sourceZone: zoneEl, + sourceConfig: config, + sourceIndex: itemIndex, + item: items[itemIndex], + sourceRect: rect, + currentZone: zoneEl, + currentIndex: itemIndex + }; + + activeZone = { el: zoneEl, config }; +}; + +const moveDrag = (e) => { + if (!dragState) return; + e.preventDefault(); + + const touch = e.touches ? e.touches[0] : e; + const { ghost, placeholder, sourceRect, sourceConfig } = dragState; + + positionGhost(ghost, touch.clientX, touch.clientY, sourceRect); + + // Find which zone we're over + const zoneUnder = findZoneUnder(touch.clientX, touch.clientY); + + if (zoneUnder && zoneUnder.config.group === sourceConfig.group) { + const targetZone = zoneUnder.el; + const direction = zoneUnder.config.direction || 'horizontal'; + const newIndex = getInsertIndex(targetZone, touch.clientX, touch.clientY, direction); + + // Move placeholder to target zone if needed + if (targetZone !== dragState.currentZone || newIndex !== dragState.currentIndex) { + const rects = getItemRects(targetZone); + + // Remove placeholder from current zone + if (dragState.placeholder.parentNode) { + dragState.placeholder.parentNode.removeChild(dragState.placeholder); + } + + // Insert in new position + const visibleChildren = [...targetZone.children].filter(c => + !c.hasAttribute('dnd-ghost') && !c.hasAttribute('dnd-placeholder') && c.style.display !== 'none' + ); + + if (newIndex >= visibleChildren.length) { + targetZone.appendChild(placeholder); + } else { + targetZone.insertBefore(placeholder, visibleChildren[newIndex]); + } + + animateFLIP(rects); + + dragState.currentZone = targetZone; + dragState.currentIndex = newIndex; + } + } +}; + +const endDrag = () => { + if (!dragState) return; + + const { + ghost, placeholder, sourceEl, sourceZone, sourceConfig, + sourceIndex, item, currentZone, currentIndex + } = dragState; + + // Snap ghost to placeholder position + const phRect = placeholder.getBoundingClientRect(); + ghost.style.transition = `all ${SNAP_DURATION}ms ${EASE}`; + ghost.style.left = `${phRect.left}px`; + ghost.style.top = `${phRect.top}px`; + ghost.style.transform = 'scale(1)'; + ghost.style.boxShadow = '0 2px 8px rgba(0,0,0,0.1)'; + + setTimeout(() => { + // Clean up DOM + ghost.remove(); + placeholder.remove(); + sourceEl.style.display = ''; + + const targetConfig = zones.get(currentZone); + + if (currentZone === sourceZone) { + // Reorder within same zone + if (currentIndex !== sourceIndex && targetConfig?.onReorder) { + const items = [...sourceConfig.items()]; + const [moved] = items.splice(sourceIndex, 1); + items.splice(currentIndex > sourceIndex ? currentIndex - 1 : currentIndex, 0, moved); + targetConfig.onReorder(items); + } + } else if (targetConfig) { + // Transfer between zones + if (sourceConfig.onSend) sourceConfig.onSend(item, currentZone); + if (targetConfig.onReceive) targetConfig.onReceive(item, sourceZone, currentIndex); + } + + dragState = null; + activeZone = null; + }, SNAP_DURATION); +}; + +const handlePointerDown = (e) => { + const itemEl = e.target.closest('[dnd-item]'); + if (!itemEl) return; + const zoneEl = itemEl.closest('[dnd-zone]'); + if (!zoneEl || !zones.has(zoneEl)) return; + + // Long press on mobile, immediate on desktop + if (e.pointerType === 'touch') { + const timer = setTimeout(() => { + startDrag(e, itemEl, zoneEl); + bindMoveEnd(); + }, 200); + + const cancel = () => { + clearTimeout(timer); + document.removeEventListener('pointermove', cancel); + document.removeEventListener('pointerup', cancel); + }; + document.addEventListener('pointermove', cancel, { once: true }); + document.addEventListener('pointerup', cancel, { once: true }); + } else { + e.preventDefault(); + startDrag(e, itemEl, zoneEl); + bindMoveEnd(); + } +}; + +const bindMoveEnd = () => { + document.addEventListener('pointermove', moveDrag); + document.addEventListener('pointerup', () => { + endDrag(); + document.removeEventListener('pointermove', moveDrag); + }, { once: true }); +}; + +// Public API +const dnd = { + zone(element, config) { + zones.set(element, config); + element.setAttribute('dnd-zone', ''); + element.style.touchAction = 'none'; + + return () => { + zones.delete(element); + element.removeAttribute('dnd-zone'); + }; + }, + + init() { + document.addEventListener('pointerdown', handlePointerDown); + }, + + destroy() { + document.removeEventListener('pointerdown', handlePointerDown); + zones.clear(); + } +}; + +export default dnd; diff --git a/js/entity.js b/js/entity.js new file mode 100644 index 00000000..34f29e1f --- /dev/null +++ b/js/entity.js @@ -0,0 +1,13 @@ +import { deepMerge } from '/js/helpers.js'; + +export default (entities, id, uuid, fullBody = false, overrides = {}) => ({ + ...deepMerge( + { + uuid: uuid || crypto.randomUUID(), + id, + ...(fullBody ? structuredClone(entities[id]) : undefined) + }, + fullBody ? overrides : {} + ), + ...(JSON.stringify(overrides) !== '{}' ? { overrides } : {}) +}); diff --git a/src/ts/equipment.ts b/js/equipment.js similarity index 51% rename from src/ts/equipment.ts rename to js/equipment.js index 8cb768ee..970ef276 100644 --- a/src/ts/equipment.ts +++ b/js/equipment.js @@ -1,19 +1,11 @@ -import { page } from '$app/stores'; -import type { EquipmentRef, EquipmentType, EquipmentSlot, Equipment } from '@/types/equipment'; -import { get } from 'svelte/store'; -import app from '@/app.svelte'; -import type { Character, CharacterRef } from '@/types/character'; -import EQUIPMENT from '@/constants/EQUIPMENT'; -import CHARACTERS from '@/constants/CHARACTERS'; -import { calculateCombatStatsByCharacter } from '@/ts/utils'; - -export const correctHealth = (characterRef: Required, prevMax?: number) => { +import EQUIPMENT from '/js/constants/EQUIPMENT.js'; +import CHARACTERS from '/js/constants/CHARACTERS.js'; +import { calculateCombatStatsByCharacter } from '/js/utils.js'; + +export const correctHealth = (characterRef, prevMax) => { const character = CHARACTERS(characterRef, true); const combatStats = calculateCombatStatsByCharacter(character); - // Case 1: new current health is more than new max health, damage to 100% - // Case 2: previous max health is equal to current health, keep it full - // Case 3: no previous max health provided, assume full health (eg. level up / recruit) if ( combatStats.maxHealth <= character.combatStats.currentHealth || prevMax === character.combatStats.currentHealth || @@ -25,7 +17,7 @@ export const correctHealth = (characterRef: Required, prevMax?: nu return characterRef; }; -const decideEquipmentSlot = (slotsIn: EquipmentType, character: Character) => { +const decideEquipmentSlot = (slotsIn, character) => { if (slotsIn === 'oneHand') { const mainHand = character.equipment.mainHand; const offHand = character.equipment.offHand; @@ -46,74 +38,69 @@ const decideEquipmentSlot = (slotsIn: EquipmentType, character: Character) => { return slotsIn; }; -export const dismantle = (itemRef: EquipmentRef) => { - const index = app.inventory.findIndex(({ uuid }) => uuid === itemRef.uuid); +export const dismantle = (itemRef) => { + const index = $.inventory.findIndex(({ uuid }) => uuid === itemRef.uuid); if (index === -1) return; - app.inventory.splice(index, 1); + $.inventory.splice(index, 1); }; -export const equip = (equipmentRef: EquipmentRef) => { - const characterIndex = Number(get(page)?.params.characterIndex) || 0; +export const equip = (equipmentRef) => { + const characterIndex = Number($.routeParams.characterIndex) || 0; if (characterIndex === undefined) return; const equipment = EQUIPMENT(equipmentRef, true); - const characterRef = app.characters[characterIndex]; + const characterRef = $.characters[characterIndex]; const character = CHARACTERS(characterRef, true); let slotsIn = decideEquipmentSlot(equipment.slotsIn, character); const slot = character.equipment[slotsIn]; const mainHand = character.equipment.mainHand ? EQUIPMENT(character.equipment.mainHand, true) - : ({} as Equipment); + : {}; - const index = app.inventory.findIndex((item) => item === equipmentRef); - // Remove item from inventory (equipment) - app.inventory.splice(index, 1); + const index = $.inventory.findIndex((item) => item === equipmentRef); + $.inventory.splice(index, 1); - // If something is already in the slot, push it to inventory if (slot !== null) { - app.inventory.push(slot); + $.inventory.push(slot); } - // If two handed weapon is being equipped, check if offHand needs to go to inventory if (equipment.slotsIn === 'twoHand' && character.equipment.offHand) { - app.inventory.push(character.equipment.offHand); + $.inventory.push(character.equipment.offHand); characterRef.overrides.equipment.offHand = null; } - // If oneHanded weapon is being equipped, check if mainHand is two hand (thus need to go to inventory) if ( (slotsIn === 'mainHand' || slotsIn === 'offHand') && equipment.slotsIn !== 'twoHand' && mainHand?.slotsIn === 'twoHand' && character.equipment.mainHand !== null ) { - app.inventory.push(character.equipment.mainHand); + $.inventory.push(character.equipment.mainHand); characterRef.overrides.equipment.mainHand = null; if (equipment.slotsIn !== 'offHand') slotsIn = 'mainHand'; } - // Replace whatever is in the slot characterRef.overrides.equipment[slotsIn] = equipmentRef; correctHealth(characterRef, calculateCombatStatsByCharacter(character).maxHealth); }; -export const unequip = (equipmentRef: EquipmentRef, slot: EquipmentSlot) => { - const characterIndex = Number(get(page)?.params.characterIndex); +export const unequip = (equipmentRef, slot) => { + const characterIndex = Number($.routeParams.characterIndex); if (characterIndex === undefined) return; - const characterRef = app.characters[characterIndex]; + const characterRef = $.characters[characterIndex]; const character = CHARACTERS(characterRef, true); - app.inventory.push(equipmentRef); + $.inventory.push(equipmentRef); characterRef.overrides.equipment[slot] = null; correctHealth(characterRef, calculateCombatStatsByCharacter(character).maxHealth); }; -export const slotsInPrettyName = (slotsIn: EquipmentType | string) => +export const slotsInPrettyName = (slotsIn) => ({ twoHand: 'Two-Handed', oneHand: 'One-Handed', diff --git a/src/helpers.ts b/js/helpers.js similarity index 55% rename from src/helpers.ts rename to js/helpers.js index 9d620b8f..32324d9e 100644 --- a/src/helpers.ts +++ b/js/helpers.js @@ -1,25 +1,7 @@ -import { IS_DEV } from '@/constants/ENV_VARS'; -import type { DynamicObject } from '@/types/common'; - -const wrapBasedOnType = (value: any) => (typeof value !== 'string' ? `{${value}}` : `"${value}"`); - -const generateStyles = (styles: any) => - Object.entries(styles) - .reduce((a: any, [property, value]) => [...a, `${property}: ${value};`], []) - .join(' '); - -const formatProps = (props: any) => - props.reduce( - (a: string, { name, defaultValue }: any) => `${a} ${name}=${wrapBasedOnType(defaultValue)}\n`, - '' - ); - -const deepMerge = (target: DynamicObject, source: DynamicObject) => { +const deepMerge = (target, source) => { for (const key in source) { - if ( - Array.isArray(source[key]) // check if it's an array - ) { - target[key] = [...source[key]]; // replace entirely with a shallow copy + if (Array.isArray(source[key])) { + target[key] = [...source[key]]; } else if (source[key] instanceof Object && key in target && target[key] instanceof Object) { deepMerge(target[key], source[key]); } else { @@ -29,7 +11,7 @@ const deepMerge = (target: DynamicObject, source: DynamicObject) => { return target; }; -const deepSubtract = (target: any, source: any): any => { +const deepSubtract = (target, source) => { for (const key in source) { if ( source[key] instanceof Object && @@ -38,20 +20,17 @@ const deepSubtract = (target: any, source: any): any => { !Array.isArray(source[key]) && !Array.isArray(target[key]) ) { - // If both are objects, recurse deepSubtract(target[key], source[key]); } else if (typeof source[key] === 'number' && typeof target[key] === 'number') { - // If both are numbers, subtract target[key] -= source[key]; } else { - // Otherwise, just set/overwrite target[key] = source[key]; } } return target; }; -const deepAdd = (target: any, source: any): any => { +const deepAdd = (target, source) => { for (const key in source) { if ( source[key] instanceof Object && @@ -60,13 +39,10 @@ const deepAdd = (target: any, source: any): any => { !Array.isArray(source[key]) && !Array.isArray(target[key]) ) { - // If both are objects, recurse deepAdd(target[key], source[key]); } else if (typeof source[key] === 'number' && typeof target[key] === 'number') { - // If both are numbers, add target[key] += source[key]; } else { - // Otherwise, just set/overwrite target[key] = source[key]; } } @@ -78,30 +54,25 @@ const range = (end = 0, start = 0) => .fill(0) .map((_, i) => start + i * (end > 0 ? 1 : -1)); -const emptySlot = (obj: any) => typeof obj !== 'object' || !Object.keys(obj).length; +const emptySlot = (obj) => typeof obj !== 'object' || !Object.keys(obj).length; -const filledSlot = (obj: DynamicObject) => !emptySlot(obj); +const filledSlot = (obj) => !emptySlot(obj); -const compare = (a: any, b: any) => { +const compare = (a, b) => { if (a > b) return +1; if (a < b) return -1; return 0; }; -// const generateID = () => -// `_${ -// Number(String(Math.random()).slice(2)) + Date.now() + Math.round(performance.now()).toString(36) -// }`; - -const sleep = (ms: number) => new Promise((res) => setTimeout(res, ms)); +const sleep = (ms) => new Promise((res) => setTimeout(res, ms)); -const getCookie = (): DynamicObject => +const getCookie = () => document.cookie .replaceAll(' ', '') .split(';') .reduce((a, b) => ({ ...a, [b.split('=')[0]]: b.split('=')[1] }), {}); -const recursiveLookup = (target: HTMLElement, searches: string[] = []) => { +const recursiveLookup = (target, searches = []) => { if (!target) return false; if (searches.find((search) => target.classList.contains(search))) { @@ -113,10 +84,10 @@ const recursiveLookup = (target: HTMLElement, searches: string[] = []) => { return false; }; -const capitalizeFirstLetter = (string: string) => +const capitalizeFirstLetter = (string) => `${string.charAt(0).toUpperCase()}${string.slice(1)}`; -const colorStrength = (col: any, amt: any) => { +const colorStrength = (col, amt) => { let usePound = false; if (col[0] === '#') { @@ -144,10 +115,10 @@ const colorStrength = (col: any, amt: any) => { return (usePound ? '#' : '') + (g | (b << 8) | (r << 16)).toString(16); }; -const debounce = (fn: any, wait: any, maxWait: any) => { - let timer: any, maxTimer: any; +const debounce = (fn, wait, maxWait) => { + let timer, maxTimer; - return function (...args: any) { + return function (...args) { if (timer) clearTimeout(timer); timer = setTimeout(() => { clearTimeout(maxTimer); @@ -164,40 +135,40 @@ const debounce = (fn: any, wait: any, maxWait: any) => { }; }; -const lerp = (min: number, max: number, value: number) => { +const lerp = (min, max, value) => { const x = Math.max(0, Math.min(1, (value - min) / (max - min))); return x * x * (3 - 2 * x); }; -const smoothLerp = (animStart: number, animEnd: number, animCurrent: number, reverse: boolean) => { +const smoothLerp = (animStart, animEnd, animCurrent, reverse) => { const lerped = lerp(animStart, animEnd, animCurrent); return reverse ? 1 - lerped : lerped; }; -const randomNumber = (min: number, max: number, factor = Math.random()) => +const randomNumber = (min, max, factor = Math.random()) => Math.floor(factor * (max - min + 1) + min); -const stringListToArray = (string: string) => +const stringListToArray = (string) => string .split(',') .map((segment) => segment.trim()) .filter((segment) => segment); -const pad = (v: number) => (v < 10 ? `0${v}` : v); +const pad = (v) => (v < 10 ? `0${v}` : v); -const intToArray = (int: number, fill: any = undefined) => +const intToArray = (int, fill = undefined) => Array(int) .fill(0) .map((_, i) => (fill !== undefined ? fill : i)); -const filterSplit = (array: any[], condition: any) => +const filterSplit = (array, condition) => array.reduce( ([hits, misses], item) => condition(item) ? [[...hits, item], misses] : [hits, [...misses, item]], [[], []] ); -const findNIndex = (array: any, condition: any, n = 1, searched = []) => { +const findNIndex = (array, condition, n = 1, searched = []) => { const index = array.findIndex(condition); if (index === -1) { @@ -214,9 +185,9 @@ const findNIndex = (array: any, condition: any, n = 1, searched = []) => { return searched.length + index; }; -const readableTimestamp = (timestamp: any) => new Date(timestamp).toLocaleString(); +const readableTimestamp = (timestamp) => new Date(timestamp).toLocaleString(); -const msToTime = (ms: number) => ({ +const msToTime = (ms) => ({ milliseconds: ms % 1000, seconds: Math.floor((ms / 1000) % 60), minutes: Math.floor((ms / (1000 * 60)) % 60), @@ -224,8 +195,8 @@ const msToTime = (ms: number) => ({ days: Math.floor((ms / (1000 * 60 * 60 * 24)) % 365) }); -const upsertArray = (players: any[], nearby: any[]) => - nearby.reduce((a: any[], player: any) => { +const upsertArray = (players, nearby) => + nearby.reduce((a, player) => { const index = a.findIndex(({ _id }) => _id === player._id); if (index === -1) { return [...a, player]; @@ -234,28 +205,23 @@ const upsertArray = (players: any[], nearby: any[]) => } }, players); -const fillOut = (array: any[], amount: number, fillWith = false, repeat = false) => +const fillOut = (array, amount, fillWith = false, repeat = false) => amount > array.length || repeat ? [...array, ...intToArray(amount - (repeat ? array.length % amount : array.length), fillWith)] : array; -const prettyNumber = (value: number) => value.toString().replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1 '); +const prettyNumber = (value) => value.toString().replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1 '); -const parseVersion = (versionString: string) => { +const parseVersion = (versionString) => { const [major, minor, patch] = versionString.split('.').map(Number); - - return { - major, - minor, - patch - }; + return { major, minor, patch }; }; -const dayAfter = (time: any) => { +const dayAfter = (time) => { + const IS_DEV = ['localhost', '127.0.0.1'].includes(location.hostname); const tomorrow = new Date(time || null); tomorrow.setHours(IS_DEV ? tomorrow.getHours() : 0); - // tomorrow.setMinutes(tomorrow.getMinutes() + +IS_DEV); tomorrow.setMinutes(IS_DEV ? tomorrow.getMinutes() : 0); tomorrow.setSeconds(IS_DEV ? tomorrow.getSeconds() + 10 : 0); tomorrow.setDate(tomorrow.getDate() + +!IS_DEV); @@ -263,63 +229,48 @@ const dayAfter = (time: any) => { return tomorrow; }; -const clamp = (num: number, min: number, max: number) => Math.min(Math.max(num, min), max); +const clamp = (num, min, max) => Math.min(Math.max(num, min), max); -const isNewerVersion = (oldVer = '0.0.0', newVer: any) => { +const isNewerVersion = (oldVer = '0.0.0', newVer) => { const oldParts = oldVer.split('.'); const newParts = newVer.split('.'); for (let i = 0; i < newParts.length; i++) { - const a = ~~newParts[i]; // parse int - const b = ~~oldParts[i]; // parse int + const a = ~~newParts[i]; + const b = ~~oldParts[i]; if (a > b) return true; if (a < b) return false; } return false; }; -const traverse = ([step, ...steps]: any, tree: any, pointer: any = undefined) => { - if (!pointer) pointer = tree; - if (steps.length - 1) { - if (!pointer[step]) pointer[step] = {}; - return traverse(steps, tree, pointer[step]); - } - - pointer[step] = steps[0] === undefined ? '$unset' : steps[0]; - - return tree; -}; - -const byKeys = (keys: any[]) => (checkAgainst: any[]) => - Object.entries(keys).every(([key, value]: any) => checkAgainst?.[key] === value); +const byKeys = (keys) => (checkAgainst) => + Object.entries(keys).every(([key, value]) => checkAgainst?.[key] === value); -const notByKeys = (keys: any[]) => (checkAgainst: any[]) => !byKeys(keys)(checkAgainst); +const notByKeys = (keys) => (checkAgainst) => !byKeys(keys)(checkAgainst); -const reorder = (array: any[], index: number) => array.slice(index).concat(array.slice(0, index)); +const reorder = (array, index) => array.slice(index).concat(array.slice(0, index)); -const onlyUnique = (value: number, index: number, self: any) => self.indexOf(value) === index; +const onlyUnique = (value, index, self) => self.indexOf(value) === index; -function once(fn: any) { - return function (this: any, event: any) { +function once(fn) { + return function (event) { if (fn) fn.call(this, event); fn = null; }; } -function preventDefault(fn: any) { - return function (this: any, event: any) { +function preventDefault(fn) { + return function (event) { event.preventDefault(); fn.call(this, event); }; } export { - generateStyles, - formatProps, range, emptySlot, filledSlot, compare, - // generateID, sleep, getCookie, recursiveLookup, diff --git a/js/keystrokes.js b/js/keystrokes.js new file mode 100644 index 00000000..1d9ffaf3 --- /dev/null +++ b/js/keystrokes.js @@ -0,0 +1,15 @@ +export const init = () => { + document.addEventListener('keydown', (e) => { + const code = e.code.toLowerCase(); + if (code in $.keys) { + $.keys[code] = true; + } + }); + + document.addEventListener('keyup', (e) => { + const code = e.code.toLowerCase(); + if (code in $.keys) { + $.keys[code] = false; + } + }); +}; diff --git a/js/level.js b/js/level.js new file mode 100644 index 00000000..313f6772 --- /dev/null +++ b/js/level.js @@ -0,0 +1,37 @@ +const SCALING = 1.6; +const MAX_LEVEL = 25; + +export const getLevelByExperience = (experience) => + Math.min(Math.floor(Math.pow(experience / 100, 1 / SCALING)) + 1, MAX_LEVEL); + +export const getExperienceByLevel = (level) => + Math.pow(level - 1, SCALING) * 100; + +export const getExperienceForNextLevel = (currentLevel) => { + const xpCurrentLevel = getExperienceByLevel(currentLevel); + const xpNextLevel = getExperienceByLevel(currentLevel + 1); + return Math.floor(xpNextLevel - xpCurrentLevel); +}; + +export const getCurrentExperienceAtLevel = (experience) => { + const currentLevel = getLevelByExperience(experience); + const xpForCurrentLevel = getExperienceByLevel(currentLevel); + return Math.ceil(experience - xpForCurrentLevel); +}; + +export const allowedNumberOfCharacters = () => + Math.floor($.accountRewards / 5) + 1; + +export const getExperienceRangeForLevel = (level) => { + const minXp = getExperienceByLevel(level) + 1; + let maxXp = getExperienceByLevel(level + 1); + + if (level + 1 > MAX_LEVEL) { + maxXp = Infinity; + } + + return { minXp: Math.floor(minXp), maxXp: Math.floor(maxXp) }; +}; + +export const getExperienceReward = (numberOfEnemies, minLevel, maxLevel, boss) => + 40 * numberOfEnemies * (boss ? 10 : 1) + 40 * Math.ceil((minLevel + maxLevel) / 2) * 0.2; diff --git a/js/lib/howler.js b/js/lib/howler.js new file mode 100644 index 00000000..cb86f156 --- /dev/null +++ b/js/lib/howler.js @@ -0,0 +1,10 @@ +// ESM wrapper for Howler.js (UMD) +const script = document.createElement('script'); +script.src = '/nodemodules/howler/dist/howler.min.js'; +document.head.appendChild(script); + +await new Promise(resolve => { script.onload = resolve; }); + +export const Howl = window.Howl; +export const Howler = window.Howler; +export default { Howl, Howler }; diff --git a/js/lib/seedrandom.js b/js/lib/seedrandom.js new file mode 100644 index 00000000..33961b5b --- /dev/null +++ b/js/lib/seedrandom.js @@ -0,0 +1,9 @@ +// ESM wrapper for seedrandom (which is UMD/CJS) +// Load it as a script and expose the global +const script = document.createElement('script'); +script.src = '/nodemodules/seedrandom/seedrandom.min.js'; +document.head.appendChild(script); + +await new Promise(resolve => { script.onload = resolve; }); + +export default Math.seedrandom; diff --git a/js/router.js b/js/router.js new file mode 100644 index 00000000..afb53657 --- /dev/null +++ b/js/router.js @@ -0,0 +1,122 @@ +const routes = [ + { path: '/', page: '/pages/home.html' }, + { path: '/brawlers', page: '/pages/brawlers.html' }, + { path: '/brawlers/:characterIndex', page: '/pages/brawler-detail.html' }, + { path: '/the-arena', page: '/pages/the-arena.html' }, + { path: '/the-arena/:fightId', page: '/pages/fight-detail.html' }, + { path: '/random-duel', page: '/pages/random-duel.html' }, + { path: '/vendor', page: '/pages/vendor.html' }, + { path: '/ability-scaling', page: '/pages/ability-scaling.html' }, + { path: '/character-scaling', page: '/pages/character-scaling.html' }, + { path: '/equipment-scaling', page: '/pages/equipment-scaling.html' }, + { path: '/debug', page: '/pages/debug.html' }, + { path: '/reset-password/:secret', page: '/pages/reset-password.html' }, +]; + +const cache = {}; + +const match = (path) => { + for (const route of routes) { + const paramNames = []; + const pattern = route.path.replace(/:(\w+)/g, (_, name) => { + paramNames.push(name); + return '([^/]+)'; + }); + const m = path.match(new RegExp(`^${pattern}$`)); + if (m) { + const params = paramNames.reduce((a, name, i) => ({ ...a, [name]: m[i + 1] }), {}); + return { route, params }; + } + } + return null; +}; + +// Inject HTML into page-content and execute any + +
+ +

Ability Scaling

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTicksChainDmg ModDmg PowDmg ResultHeal ModHeal PowHeal Result
@[a.name]@[a.ticks]@[a.chainLink ?? '-']@[a.damageModifier ?? '-']@[a.damagePow]@[a.damageResult]@[a.healingModifier ?? '-']@[a.healingPow]@[a.healingResult]
+
+
+
+ + diff --git a/pages/brawler-detail.html b/pages/brawler-detail.html new file mode 100644 index 00000000..016f5183 --- /dev/null +++ b/pages/brawler-detail.html @@ -0,0 +1,343 @@ + + +
+ + + ← Back to Brawlers + + + +

@[currentCharacter.name]

+ + + + + + @[currentCombatStats.maxHealth] + + / + + 🛡 + @[currentCombatStats.maxArmor] + + / + + + @[currentCombatStats.damage] + + + +
+ + + + + + + + +
+ + + + + @[currentCharacter.name] + + + + + + + +
Base Stats
+ + maxHealth@[currentCombatStats.maxHealth] + maxArmor@[currentCombatStats.maxArmor] + damage@[currentCombatStats.damage] + + +
+ +
Lucky Stats
+ + criticalChance@[currentCombatStats.criticalChance] + criticalDamage@[currentCombatStats.criticalDamage] + blockChance@[currentCombatStats.blockChance] + dodgeChance@[currentCombatStats.dodgeChance] + magicChance@[currentCombatStats.magicChance] + +
+ + +
Modifiers
+ + maxHealth@[currentCombatStats.modifiers.maxHealth] + maxArmor@[currentCombatStats.modifiers.maxArmor] + damage@[currentCombatStats.modifiers.damage] + resistance@[currentCombatStats.modifiers.resistance] + + +
+ +
Resistances
+ + wounded@[currentCombatStats.limits.wounded] + concussed@[currentCombatStats.limits.concussed] + exposed@[currentCombatStats.limits.exposed] + +
+
+ + +
+
Equipment
+ + + + + + + + @[eq.prettyName] + + @[eq.itemName] + + + + @[eq.itemName] + + + @[eq.itemName] + + + + + + + + + + + +
+
+ +
+ + + +
Active Abilities
+ +
+ +
+ + +
+ +
+ +
+ +
+ + Ability sequence + @[currentLastTickEnd] ticks + +
+ + + +
+ @[ability.name] + @[ability.ticks]t +
+ +
+
+ +
+ +
Available Abilities
+ + +
+ @[ability.name] + @[ability.ticks]t +
+ + + No additional abilities available. Equip items to unlock more. + +
+
+
+ + + + +

No brawler found.

+ ← Back to Brawlers +
+ +
+ + diff --git a/pages/brawlers.html b/pages/brawlers.html new file mode 100644 index 00000000..2456093e --- /dev/null +++ b/pages/brawlers.html @@ -0,0 +1,154 @@ + + +
+

Brawlers

+ + + + Select your first brawler + + + Select a brawler + + @[brawlerCharCount] / @[brawlerCharMax] + + + + +
+ + @[char.name] +
+ +
+ @[char.name] +
+ + @[char.race] + @[char.element] + + HP @[char.hp] + + Armor @[char.armor] + + Dmg @[char.damage] + + + + + @[bonus.label] @[bonus.value] + + + + + + + @[bonus.label] @[bonus.value] + + + + +
+
+
+ +
+
+ + diff --git a/pages/character-scaling.html b/pages/character-scaling.html new file mode 100644 index 00000000..c819c4f7 --- /dev/null +++ b/pages/character-scaling.html @@ -0,0 +1,63 @@ + + +
+ +

Character Scaling

+ + + + + +
+ @[char.name] +
+ + @[char.name] + @[char.race] · @[char.element] + +
+
+ + Max HP + @[char.maxHealth] + + + Armor + @[char.maxArmor] + + + Damage + @[char.damage] + + + Resistance + @[char.resistance] + +
+ +
+
+
+ + diff --git a/pages/debug.html b/pages/debug.html new file mode 100644 index 00000000..6df2ae51 --- /dev/null +++ b/pages/debug.html @@ -0,0 +1,44 @@ + + +
+ +

Debug

+ + + + + Characters + @[characters.length] + + + Inventory + @[inventory.length] + + + Coins + @[coins] + + + Experience + @[experience] + + + Combat duration + @[combat?.duration ?? 'N/A'] + + + + + +
+
+ + diff --git a/pages/equipment-scaling.html b/pages/equipment-scaling.html new file mode 100644 index 00000000..3f34964f --- /dev/null +++ b/pages/equipment-scaling.html @@ -0,0 +1,61 @@ + + +
+ +

Equipment Scaling

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameSlotCostLevelDamageHPArmorResist
@[item.name]@[item.slot]@[Math.floor(item.cost / 100)]s @[item.cost % 100]c@[item.level]@[item.damage]@[item.maxHealth]@[item.maxArmor]@[item.resistance]
+
+
+
+ + diff --git a/pages/fight-detail.html b/pages/fight-detail.html new file mode 100644 index 00000000..9798abd9 --- /dev/null +++ b/pages/fight-detail.html @@ -0,0 +1,140 @@ + + +
+ + + ← Back to The Arena + + +

@[currentFight.name]

+ + BOSS + +
+ + + Level @[currentFight.minLevel]–@[currentFight.maxLevel] + Max @[currentFight.allowedNumberOfCharacters] brawler@[currentFight.allowedNumberOfCharacters === 1 ? '' : 's'] + + + +

@[currentFight.description]

+ + +
+ +
Your Brawlers
+

Select brawlers from the sidebar

+ + + + + @[slot.name] + + + Select a brawler + + + + + +
+ +
Enemies
+ + + +
+ @[enemy.name] +
+ @[enemy.name] + @[enemy.race] +
+ +
+ +
+ + +
+ + + + +

Fight not found.

+ ← Back to The Arena +
+ +
+ + diff --git a/src/routes/+page.svelte b/pages/home.html similarity index 60% rename from src/routes/+page.svelte rename to pages/home.html index e6f8a2b4..73cf3be0 100644 --- a/src/routes/+page.svelte +++ b/pages/home.html @@ -1,10 +1,9 @@ - - - + +

Game guide

+
+
Battle Brawlers is a strategy auto-battler playable in your browser.

@@ -15,7 +14,11 @@ Every 5 Ludus levels, you unlock an additional Brawler slot.


- + +
Combat
+
+
+ Combat is automatic once you start a fight.

Battle Brawlers uses a tick system. A tick cycle is normally @@ -25,19 +28,32 @@

Tick cycles repeat until a winner is determined.


- -There’s a vast roster of fights available in the arena. Each fight features its own unique set of enemies + + +
The arena
+
+
+ +There's a vast roster of fights available in the arena. Each fight features its own unique set of enemies and rewards.

Boss fights unlock additional content in Battle Brawlers. But be wary! Luck-based combat stats are disabled during boss fights! - + - +[page-home] [headline-hr-sm] { + height: 1px; + border: 0; + background: linear-gradient(to right, rgba(0,0,0,0.25), transparent); + margin: 4px 0 12px; +} + diff --git a/pages/random-duel.html b/pages/random-duel.html new file mode 100644 index 00000000..b646b8b0 --- /dev/null +++ b/pages/random-duel.html @@ -0,0 +1,68 @@ + + +
+ +

Random Duel

+ + + + ? + + vs + + ? + + + + + @[selectedBrawlers.length] / @[maxBrawlers] brawlers selected + + + + +

+ Select your brawlers from the sidebar before starting a duel. You'll be matched against a random opponent near your level. +

+
+
+ + diff --git a/pages/reset-password.html b/pages/reset-password.html new file mode 100644 index 00000000..2118d178 --- /dev/null +++ b/pages/reset-password.html @@ -0,0 +1,31 @@ +
+ +

Reset Password

+ + Token: @[routeParams.secret] + + + + + + +
+
+ + diff --git a/pages/the-arena.html b/pages/the-arena.html new file mode 100644 index 00000000..e4caebf3 --- /dev/null +++ b/pages/the-arena.html @@ -0,0 +1,262 @@ + + + + + diff --git a/pages/vendor.html b/pages/vendor.html new file mode 100644 index 00000000..851cf908 --- /dev/null +++ b/pages/vendor.html @@ -0,0 +1,150 @@ + + +
+

Vendor

+ + + + + + + + + + + + + +
Name
+
Slots in
+
Cost
+
+
+ + + + + @[item.name] + +
@[item.slotLabel]
+
+ @[Math.floor(item.cost / 100)]s @[item.cost % 100]c +
+ + + +
+ +
+
+ + diff --git a/patches/svelte-dnd-action@0.9.64.patch b/patches/svelte-dnd-action@0.9.64.patch deleted file mode 100644 index 18774976..00000000 --- a/patches/svelte-dnd-action@0.9.64.patch +++ /dev/null @@ -1,3177 +0,0 @@ -diff --git a/dist/index.d.ts b/dist/index.d.ts -index ba6b9f7108677b267552e88b33f1d6639cf2c47a..9ad148e866c827e598f3d93ac70458889c5e0774 100644 ---- a/dist/index.d.ts -+++ b/dist/index.d.ts -@@ -1,39 +1,14 @@ --import type {ActionReturn} from "svelte/action"; -- - /** - * A custom action to turn any container to a dnd zone and all of its direct children to draggables - * Supports mouse, touch and keyboard interactions. - * Dispatches two events that the container is expected to react to by modifying its list of items, - * which will then feed back in to this action via the update function - */ --export declare function dndzone(node: HTMLElement, options: Options): ActionReturn, DndZoneAttributes>; -- --export declare function dndzone( -- node: HTMLElement, -- options: Options --): { -- update: (newOptions: Options) => void; -- destroy: () => void; --}; -- --/** -- * A wrapper action to make it easy to work with drag handles. -- * When using this you must also use the 'dragHandle' action on an element inside each item within the zone. -- */ --export declare function dragHandleZone(node: HTMLElement, options: Options): ActionReturn, DndZoneAttributes>; --export declare function dragHandleZone( -+export declare function dndzone( - node: HTMLElement, -- options: Options -+ options: Options - ): { -- update: (newOptions: Options) => void; -- destroy: () => void; --}; -- --/** -- * This should be used to mark drag handles inside items that belong to a 'dragHandleZone' -- */ --export declare function dragHandle(node: HTMLElement): { -- update: () => void; -+ update: (newOptions: Options) => void; - destroy: () => void; - }; - -@@ -44,33 +19,21 @@ export type TransformDraggedElementFunction = ( - ) => void; - - export declare type Item = Record; --export interface Options { -- items: T[]; // the list of items that was used to generate the children of the given node -+export interface Options { -+ items: Item[]; // the list of items that was used to generate the children of the given node - type?: string; // the type of the dnd zone. children dragged from here can only be dropped in other zones of the same type, defaults to a base type - flipDurationMs?: number; // if the list animated using flip (recommended), specifies the flip duration such that everything syncs with it without conflict -+ constrainAxisX?: boolean; // Constrain dragging by X axis. Drag will be allowed only by Y axis. -+ constrainAxisY?: boolean; // Constrain dragging by Y axis. Drag will be allowed only by X axis. - dragDisabled?: boolean; - morphDisabled?: boolean; - dropFromOthersDisabled?: boolean; - zoneTabIndex?: number; // set the tabindex of the list container when not dragging -- zoneItemTabIndex?: number; // set the tabindex of the list container items when not dragging - dropTargetClasses?: string[]; - dropTargetStyle?: Record; - transformDraggedElement?: TransformDraggedElementFunction; - autoAriaDisabled?: boolean; - centreDraggedOnCursor?: boolean; -- dropAnimationDisabled?: boolean; -- /** -- * Improve touch UX: true => default 80 ms delay, number => custom delay in ms. -- * false/undefined => drag starts instantly (legacy behaviour) -- */ -- delayTouchStart?: boolean | number; --} -- --export interface DndZoneAttributes { -- "on:consider"?: (e: CustomEvent>) => void; -- "on:finalize"?: (e: CustomEvent>) => void; -- onconsider?: (e: CustomEvent>) => void; -- onfinalize?: (e: CustomEvent>) => void; - } - - /** -@@ -117,15 +80,8 @@ export type DndEvent = { - export declare const SHADOW_ITEM_MARKER_PROPERTY_NAME: "isDndShadowItem"; - export declare const SHADOW_PLACEHOLDER_ITEM_ID: "id:dnd-shadow-placeholder-0000"; - export declare const DRAGGED_ELEMENT_ID: "dnd-action-dragged-el"; --export declare const SHADOW_ELEMENT_HINT_ATTRIBUTE_NAME = "data-is-dnd-shadow-item-hint"; - - /** - * Allows the user to show/hide console debug output - */ - export declare function setDebugMode(isDebug: boolean): void; -- --export enum FEATURE_FLAG_NAMES { -- // Default value: false, This flag exists as a workaround for issue 454 (basically a browser bug) - seems like these rect values take time to update when in grid layout. Setting it to true can cause strange behaviour in the REPL for non-grid zones, see issue 470 -- USE_COMPUTED_STYLE_INSTEAD_OF_BOUNDING_RECT = "FEATURE_FLAG_NAMES.USE_COMPUTED_STYLE_INSTEAD_OF_BOUNDING_RECT" --} --export declare function setFeatureFlag(flagName: FEATURE_FLAG_NAMES, flagValue: boolean); -diff --git a/dist/index.js b/dist/index.js -index f13c0243ee6115d7945a27261b6a09e814c60a91..f63b0d21cbd4d1fdd095f025f6aaae0ba6659807 100644 ---- a/dist/index.js -+++ b/dist/index.js -@@ -294,8 +294,7 @@ - KEYBOARD: "keyboard" - }; - var SHADOW_ITEM_MARKER_PROPERTY_NAME = "isDndShadowItem"; -- var SHADOW_ELEMENT_ATTRIBUTE_NAME = "data-is-dnd-shadow-item-internal"; -- var SHADOW_ELEMENT_HINT_ATTRIBUTE_NAME = "data-is-dnd-shadow-item-hint"; -+ var SHADOW_ELEMENT_ATTRIBUTE_NAME = "data-is-dnd-shadow-item"; - var SHADOW_PLACEHOLDER_ITEM_ID = "id:dnd-shadow-placeholder-0000"; - var DRAGGED_ELEMENT_ID = "dnd-action-dragged-el"; - var ITEM_ID_KEY = "id"; -@@ -333,7 +332,7 @@ - - /** - * Allows the user to show/hide console debug output -- * * @param {boolean} isDebug -+ * * @param {Boolean} isDebug - */ - function setDebugMode(isDebug) { - if (isDebug) { -@@ -356,13 +355,11 @@ - /** - * Gets the bounding rect but removes transforms (ex: flip animation) - * @param {HTMLElement} el -- * @param {boolean} [onlyVisible] - use the visible rect defaults to true - * @return {{top: number, left: number, bottom: number, right: number}} - */ - function getBoundingRectNoTransforms(el) { -- var onlyVisible = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; - var ta; -- var rect = onlyVisible ? getVisibleRectRecursive(el) : el.getBoundingClientRect(); -+ var rect = el.getBoundingClientRect(); - var style = getComputedStyle(el); - var tx = style.transform; - if (tx) { -@@ -503,58 +500,30 @@ - - /** - * @param {HTMLElement} el - the element to check -- * @returns {boolean} - true if the element in its entirety is off-screen including the scrollable area (the normal dom events look at the mouse rather than the element) -+ * @returns {boolean} - true if the element in its entirety is off screen including the scrollable area (the normal dom events look at the mouse rather than the element) - */ - function isElementOffDocument(el) { - var rect = getAbsoluteRect(el); - return rect.right < 0 || rect.left > document.documentElement.scrollWidth || rect.bottom < 0 || rect.top > document.documentElement.scrollHeight; - } -- function getVisibleRectRecursive(element) { -- var rect = element.getBoundingClientRect(); -- var visibleRect = { -- top: rect.top, -- bottom: rect.bottom, -- left: rect.left, -- right: rect.right -- }; - -- // Traverse up the DOM hierarchy, checking for scrollable ancestors -- var parent = element.parentElement; -- while (parent && parent !== document.body) { -- var parentRect = parent.getBoundingClientRect(); -- -- // Check if the parent has a scrollable overflow -- var overflowY = window.getComputedStyle(parent).overflowY; -- var overflowX = window.getComputedStyle(parent).overflowX; -- var isScrollableY = overflowY === "scroll" || overflowY === "auto"; -- var isScrollableX = overflowX === "scroll" || overflowX === "auto"; -- -- // Constrain the visible area to the parent's visible area -- if (isScrollableY) { -- visibleRect.top = Math.max(visibleRect.top, parentRect.top); -- visibleRect.bottom = Math.min(visibleRect.bottom, parentRect.bottom); -- } -- if (isScrollableX) { -- visibleRect.left = Math.max(visibleRect.left, parentRect.left); -- visibleRect.right = Math.min(visibleRect.right, parentRect.right); -- } -- parent = parent.parentElement; -+ /** -+ * If the point is inside the element returns its distances from the sides, otherwise returns null -+ * @param {Point} point -+ * @param {HTMLElement} el -+ * @return {null|{top: number, left: number, bottom: number, right: number}} -+ */ -+ function calcInnerDistancesBetweenPointAndSidesOfElement(point, el) { -+ var rect = getAbsoluteRect(el); -+ if (!isPointInsideRect(point, rect)) { -+ return null; - } -- -- // Finally, constrain the visible rect to the viewport -- visibleRect.top = Math.max(visibleRect.top, 0); -- visibleRect.bottom = Math.min(visibleRect.bottom, window.innerHeight); -- visibleRect.left = Math.max(visibleRect.left, 0); -- visibleRect.right = Math.min(visibleRect.right, window.innerWidth); -- -- // Return the visible rectangle, ensuring that all values are valid - return { -- top: visibleRect.top, -- bottom: visibleRect.bottom, -- left: visibleRect.left, -- right: visibleRect.right, -- width: Math.max(0, visibleRect.right - visibleRect.left), -- height: Math.max(0, visibleRect.bottom - visibleRect.top) -+ top: point.y - rect.top, -+ bottom: rect.bottom - point.y, -+ left: point.x - rect.left, -+ // TODO - figure out what is so special about right (why the rect is too big) -+ right: Math.min(rect.right, document.documentElement.clientWidth) - point.x - }; - } - -@@ -571,6 +540,17 @@ - } - resetIndexesCache(); - -+ /** -+ * Resets the cache that allows for smarter "would be index" resolution for a specific dropzone, should be called after the zone was scrolled -+ * @param {HTMLElement} dz -+ */ -+ function resetIndexesCacheForDz(dz) { -+ printDebug(function () { -+ return "resetting indexes cache for dz"; -+ }); -+ dzToShadowIndexToRect["delete"](dz); -+ } -+ - /** - * Caches the coordinates of the shadow element when it's in a certain index in a certain dropzone. - * Helpful in order to determine "would be index" more effectively -@@ -647,35 +627,105 @@ - indexOfMin = _i; - } - } -+ return { -+ index: indexOfMin, -+ isProximityBased: true -+ }; -+ } - -- // -------- Phantom slot check -------- -- // Regardless of layout (simple vertical list, flex-wrap, grid, floats …) the -- // visually closest drop target can be *after* the current last **real** child. -- // In simple layouts the would be index from the existing children would always be the last index -- // but in more complex layouts (flex-wrap, grid, floats …) it can be any index. -- // The problem is we can't predict where an additional element would be rendered in the general case, -- // We therefore create a temporary, invisible clone of that last element, let -- // the browser position it, measure the distance, and remove it immediately -- // (same task → no paint). This leaves `children` back in its original state -- // before we exit the function, so existing index-caching logic and shadow- -- // element bookkeeping continue to work unchanged. -- if (children.length > 0) { -- var originalLen = children.length; // before we append the phantom -- var template = children[originalLen - 1]; -- var phantom = template.cloneNode(false); // shallow clone is enough for size -- phantom.style.visibility = "hidden"; -- phantom.style.pointerEvents = "none"; -- collectionBelowEl.appendChild(phantom); -- var phantomDistance = calcDistanceBetweenCenters(floatingAboveEl, phantom); -- if (phantomDistance < minDistanceSoFar) { -- indexOfMin = originalLen; // index of phantom slot in original list -+ var SCROLL_ZONE_PX = 25; -+ function makeScroller() { -+ var scrollingInfo; -+ function resetScrolling() { -+ scrollingInfo = { -+ directionObj: undefined, -+ stepPx: 0 -+ }; -+ } -+ resetScrolling(); -+ // directionObj {x: 0|1|-1, y:0|1|-1} - 1 means down in y and right in x -+ function scrollContainer(containerEl) { -+ var _scrollingInfo = scrollingInfo, -+ directionObj = _scrollingInfo.directionObj, -+ stepPx = _scrollingInfo.stepPx; -+ if (directionObj) { -+ containerEl.scrollBy(directionObj.x * stepPx, directionObj.y * stepPx); -+ window.requestAnimationFrame(function () { -+ return scrollContainer(containerEl); -+ }); - } -+ } -+ function calcScrollStepPx(distancePx) { -+ return SCROLL_ZONE_PX - distancePx; -+ } - -- collectionBelowEl.removeChild(phantom); -+ /** -+ * If the pointer is next to the sides of the element to scroll, will trigger scrolling -+ * Can be called repeatedly with updated pointer and elementToScroll values without issues -+ * @return {boolean} - true if scrolling was needed -+ */ -+ function scrollIfNeeded(pointer, elementToScroll) { -+ if (!elementToScroll) { -+ return false; -+ } -+ var distances = calcInnerDistancesBetweenPointAndSidesOfElement(pointer, elementToScroll); -+ if (distances === null) { -+ resetScrolling(); -+ return false; -+ } -+ var isAlreadyScrolling = !!scrollingInfo.directionObj; -+ var scrollingVertically = false, -+ scrollingHorizontally = false; -+ // vertical -+ if (elementToScroll.scrollHeight > elementToScroll.clientHeight) { -+ if (distances.bottom < SCROLL_ZONE_PX) { -+ scrollingVertically = true; -+ scrollingInfo.directionObj = { -+ x: 0, -+ y: 1 -+ }; -+ scrollingInfo.stepPx = calcScrollStepPx(distances.bottom); -+ } else if (distances.top < SCROLL_ZONE_PX) { -+ scrollingVertically = true; -+ scrollingInfo.directionObj = { -+ x: 0, -+ y: -1 -+ }; -+ scrollingInfo.stepPx = calcScrollStepPx(distances.top); -+ } -+ if (!isAlreadyScrolling && scrollingVertically) { -+ scrollContainer(elementToScroll); -+ return true; -+ } -+ } -+ // horizontal -+ if (elementToScroll.scrollWidth > elementToScroll.clientWidth) { -+ if (distances.right < SCROLL_ZONE_PX) { -+ scrollingHorizontally = true; -+ scrollingInfo.directionObj = { -+ x: 1, -+ y: 0 -+ }; -+ scrollingInfo.stepPx = calcScrollStepPx(distances.right); -+ } else if (distances.left < SCROLL_ZONE_PX) { -+ scrollingHorizontally = true; -+ scrollingInfo.directionObj = { -+ x: -1, -+ y: 0 -+ }; -+ scrollingInfo.stepPx = calcScrollStepPx(distances.left); -+ } -+ if (!isAlreadyScrolling && scrollingHorizontally) { -+ scrollContainer(elementToScroll); -+ return true; -+ } -+ } -+ resetScrolling(); -+ return false; - } - return { -- index: indexOfMin, -- isProximityBased: true -+ scrollIfNeeded: scrollIfNeeded, -+ resetScrolling: resetScrolling - }; - } - -@@ -742,20 +792,21 @@ - return true; - } - -- var INTERVAL_MS = 200; -+ var INTERVAL_MS$1 = 200; - var TOLERANCE_PX = 10; -- var next; -+ var _makeScroller$1 = makeScroller(), -+ scrollIfNeeded$1 = _makeScroller$1.scrollIfNeeded, -+ resetScrolling$1 = _makeScroller$1.resetScrolling; -+ var next$1; - - /** - * Tracks the dragged elements and performs the side effects when it is dragged over a drop zone (basically dispatching custom-events scrolling) - * @param {Set} dropZones - * @param {HTMLElement} draggedEl - * @param {number} [intervalMs = INTERVAL_MS] -- * @param {MultiScroller} multiScroller - */ - function observe(draggedEl, dropZones) { -- var intervalMs = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : INTERVAL_MS; -- var multiScroller = arguments.length > 3 ? arguments[3] : undefined; -+ var intervalMs = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : INTERVAL_MS$1; - // initialization - var lastDropZoneFound; - var lastIndexFound; -@@ -771,10 +822,10 @@ - */ - function andNow() { - var currentCenterOfDragged = findCenterOfElement(draggedEl); -- var scrolled = multiScroller.multiScrollIfNeeded(); -+ var scrolled = scrollIfNeeded$1(currentCenterOfDragged, lastDropZoneFound); - // we only want to make a new decision after the element was moved a bit to prevent flickering - if (!scrolled && lastCentrePositionOfDragged && Math.abs(lastCentrePositionOfDragged.x - currentCenterOfDragged.x) < TOLERANCE_PX && Math.abs(lastCentrePositionOfDragged.y - currentCenterOfDragged.y) < TOLERANCE_PX) { -- next = window.setTimeout(andNow, intervalMs); -+ next$1 = window.setTimeout(andNow, intervalMs); - return; - } - if (isElementOffDocument(draggedEl)) { -@@ -792,7 +843,7 @@ - try { - for (_iterator.s(); !(_step = _iterator.n()).done;) { - var dz = _step.value; -- if (scrolled) resetIndexesCache(); -+ if (scrolled) resetIndexesCacheForDz(lastDropZoneFound); - var indexObj = findWouldBeIndex(draggedEl, dz); - if (indexObj === null) { - // it is not inside -@@ -826,7 +877,7 @@ - } else { - lastIsDraggedInADropZone = true; - } -- next = window.setTimeout(andNow, intervalMs); -+ next$1 = window.setTimeout(andNow, intervalMs); - } - andNow(); - } -@@ -836,230 +887,62 @@ - printDebug(function () { - return "unobserving"; - }); -- clearTimeout(next); -+ clearTimeout(next$1); -+ resetScrolling$1(); - resetIndexesCache(); - } - -- var SCROLL_ZONE_PX = 30; -+ var INTERVAL_MS = 300; -+ var mousePosition; - - /** -- * Will make a scroller that can scroll any element given to it in any direction -- * @returns {{scrollIfNeeded: function(Point, HTMLElement): boolean, resetScrolling: function(void):void}} -+ * Do not use this! it is visible for testing only until we get over the issue Cypress not triggering the mousemove listeners -+ * // TODO - make private (remove export) -+ * @param {{clientX: number, clientY: number}} e - */ -- function makeScroller() { -- var scrollingInfo; -- function resetScrolling() { -- scrollingInfo = { -- directionObj: undefined, -- stepPx: 0 -- }; -- } -- resetScrolling(); -- // directionObj {x: 0|1|-1, y:0|1|-1} - 1 means down in y and right in x -- function scrollContainer(containerEl) { -- var _scrollingInfo = scrollingInfo, -- directionObj = _scrollingInfo.directionObj, -- stepPx = _scrollingInfo.stepPx; -- if (directionObj) { -- containerEl.scrollBy(directionObj.x * stepPx, directionObj.y * stepPx); -- window.requestAnimationFrame(function () { -- return scrollContainer(containerEl); -- }); -- } -- } -- function calcScrollStepPx(distancePx) { -- return SCROLL_ZONE_PX - distancePx; -- } -- -- /** -- * @param {Point} pointer - the pointer will be used to decide in which direction to scroll -- * @param {HTMLElement} elementToScroll - the scroll container -- * If the pointer is next to the sides of the element to scroll, will trigger scrolling -- * Can be called repeatedly with updated pointer and elementToScroll values without issues -- * @return {boolean} - true if scrolling was needed -- */ -- function scrollIfNeeded(pointer, elementToScroll) { -- if (!elementToScroll) { -- return false; -- } -- var distances = calcInnerDistancesBetweenPointAndSidesOfElement(pointer, elementToScroll); -- var isAlreadyScrolling = !!scrollingInfo.directionObj; -- if (distances === null) { -- if (isAlreadyScrolling) resetScrolling(); -- return false; -- } -- var scrollingVertically = false, -- scrollingHorizontally = false; -- // vertical -- if (elementToScroll.scrollHeight > elementToScroll.clientHeight) { -- if (distances.bottom < SCROLL_ZONE_PX) { -- scrollingVertically = true; -- scrollingInfo.directionObj = { -- x: 0, -- y: 1 -- }; -- scrollingInfo.stepPx = calcScrollStepPx(distances.bottom); -- } else if (distances.top < SCROLL_ZONE_PX) { -- scrollingVertically = true; -- scrollingInfo.directionObj = { -- x: 0, -- y: -1 -- }; -- scrollingInfo.stepPx = calcScrollStepPx(distances.top); -- } -- if (!isAlreadyScrolling && scrollingVertically) { -- scrollContainer(elementToScroll); -- return true; -- } -- } -- // horizontal -- if (elementToScroll.scrollWidth > elementToScroll.clientWidth) { -- if (distances.right < SCROLL_ZONE_PX) { -- scrollingHorizontally = true; -- scrollingInfo.directionObj = { -- x: 1, -- y: 0 -- }; -- scrollingInfo.stepPx = calcScrollStepPx(distances.right); -- } else if (distances.left < SCROLL_ZONE_PX) { -- scrollingHorizontally = true; -- scrollingInfo.directionObj = { -- x: -1, -- y: 0 -- }; -- scrollingInfo.stepPx = calcScrollStepPx(distances.left); -- } -- if (!isAlreadyScrolling && scrollingHorizontally) { -- scrollContainer(elementToScroll); -- return true; -- } -- } -- resetScrolling(); -- return false; -- } -- return { -- scrollIfNeeded: scrollIfNeeded, -- resetScrolling: resetScrolling -+ function updateMousePosition(e) { -+ var c = e.touches ? e.touches[0] : e; -+ mousePosition = { -+ x: c.clientX, -+ y: c.clientY - }; - } -- -- /** -- * If the point is inside the element returns its distances from the sides, otherwise returns null -- * @param {Point} point -- * @param {HTMLElement} el -- * @return {null|{top: number, left: number, bottom: number, right: number}} -- */ -- function calcInnerDistancesBetweenPointAndSidesOfElement(point, el) { -- // Even if the scrolling element is small it acts as a scroller for the viewport -- var rect = el === document.scrollingElement ? { -- top: 0, -- bottom: window.innerHeight, -- left: 0, -- right: window.innerWidth -- } : el.getBoundingClientRect(); -- if (!isPointInsideRect(point, rect)) { -- return null; -+ var _makeScroller = makeScroller(), -+ scrollIfNeeded = _makeScroller.scrollIfNeeded, -+ resetScrolling = _makeScroller.resetScrolling; -+ var next; -+ function loop() { -+ if (mousePosition) { -+ var scrolled = scrollIfNeeded(mousePosition, document.documentElement); -+ if (scrolled) resetIndexesCache(); - } -- return { -- top: point.y - rect.top, -- bottom: rect.bottom - point.y, -- left: point.x - rect.left, -- right: rect.right - point.x -- }; -+ next = window.setTimeout(loop, INTERVAL_MS); - } - - /** -- @typedef {Object} MultiScroller -- @property {function():boolean} multiScrollIfNeeded - call this on every "tick" to scroll containers if needed, returns true if anything was scrolled -- /** -- * Creates a scroller than can scroll any of the provided containers or any of their scrollable parents (including the document's scrolling element) -- * @param {HTMLElement[]} baseElementsForScrolling -- * @param {function():Point} getPointerPosition -- * @return {MultiScroller} -+ * will start watching the mouse pointer and scroll the window if it goes next to the edges - */ -- function createMultiScroller() { -- var baseElementsForScrolling = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; -- var getPointerPosition = arguments.length > 1 ? arguments[1] : undefined; -+ function armWindowScroller() { - printDebug(function () { -- return "creating multi-scroller"; -+ return "arming window scroller"; - }); -- var scrollingContainersSet = findRelevantScrollContainers(baseElementsForScrolling); -- var scrollingContainersDeepToShallow = Array.from(scrollingContainersSet).sort(function (dz1, dz2) { -- return getDepth(dz2) - getDepth(dz1); -- }); -- var _makeScroller = makeScroller(), -- scrollIfNeeded = _makeScroller.scrollIfNeeded, -- resetScrolling = _makeScroller.resetScrolling; -- -- /** -- * @return {boolean} - was any container scrolled -- */ -- function tick() { -- var mousePosition = getPointerPosition(); -- if (!mousePosition || !scrollingContainersDeepToShallow) { -- return false; -- } -- var scrollContainersUnderCursor = scrollingContainersDeepToShallow.filter(function (el) { -- return isPointInsideRect(mousePosition, el.getBoundingClientRect()) || el === document.scrollingElement; -- }); -- for (var i = 0; i < scrollContainersUnderCursor.length; i++) { -- var scrolled = scrollIfNeeded(mousePosition, scrollContainersUnderCursor[i]); -- if (scrolled) { -- return true; -- } -- } -- return false; -- } -- return { -- multiScrollIfNeeded: scrollingContainersSet.size > 0 ? tick : function () { -- return false; -- }, -- destroy: function destroy() { -- return resetScrolling(); -- } -- }; -+ window.addEventListener("mousemove", updateMousePosition); -+ window.addEventListener("touchmove", updateMousePosition); -+ loop(); - } - -- // internal utils -- function findScrollableParents(element) { -- if (!element) { -- return []; -- } -- var scrollableContainers = []; -- var parent = element; -- while (parent) { -- var _window$getComputedSt = window.getComputedStyle(parent), -- overflow = _window$getComputedSt.overflow; -- if (overflow.split(" ").some(function (o) { -- return o.includes("auto") || o.includes("scroll"); -- })) { -- scrollableContainers.push(parent); -- } -- parent = parent.parentElement; -- } -- return scrollableContainers; -- } -- function findRelevantScrollContainers(dropZones) { -- var scrollingContainers = new Set(); -- var _iterator = _createForOfIteratorHelper(dropZones), -- _step; -- try { -- for (_iterator.s(); !(_step = _iterator.n()).done;) { -- var dz = _step.value; -- findScrollableParents(dz).forEach(function (container) { -- return scrollingContainers.add(container); -- }); -- } -- // The scrolling element might have overflow visible and still be scrollable -- } catch (err) { -- _iterator.e(err); -- } finally { -- _iterator.f(); -- } -- if (document.scrollingElement.scrollHeight > document.scrollingElement.clientHeight || document.scrollingElement.scrollWidth > document.scrollingElement.clientHeight) { -- scrollingContainers.add(document.scrollingElement); -- } -- return scrollingContainers; -+ /** -+ * will stop watching the mouse pointer and won't scroll the window anymore -+ */ -+ function disarmWindowScroller() { -+ printDebug(function () { -+ return "disarming window scroller"; -+ }); -+ window.removeEventListener("mousemove", updateMousePosition); -+ window.removeEventListener("touchmove", updateMousePosition); -+ mousePosition = undefined; -+ window.clearTimeout(next); -+ resetScrolling(); - } - - /** -@@ -1067,15 +950,15 @@ - * Since svelte manages select value internally. - * @see https://github.com/sveltejs/svelte/issues/6717 - * @see https://github.com/isaacHagoel/svelte-dnd-action/issues/306 -- * -- * @param {HTMLElement} el -- * @returns -+ * -+ * @param {HTMLElement} el -+ * @returns - */ - function svelteNodeClone(el) { - var cloned = el.cloneNode(true); - var values = []; - var elIsSelect = el.tagName === "SELECT"; -- var selects = elIsSelect ? [el] : _toConsumableArray(el.querySelectorAll("select")); -+ var selects = elIsSelect ? [el] : _toConsumableArray(el.querySelectorAll('select')); - var _iterator = _createForOfIteratorHelper(selects), - _step; - try { -@@ -1088,62 +971,21 @@ - } finally { - _iterator.f(); - } -- if (selects.length > 0) { -- var clonedSelects = elIsSelect ? [cloned] : _toConsumableArray(cloned.querySelectorAll("select")); -- for (var i = 0; i < clonedSelects.length; i++) { -- var select = clonedSelects[i]; -- var value = values[i]; -- var optionEl = select.querySelector("option[value=\"".concat(value, "\"")); -- if (optionEl) { -- optionEl.setAttribute("selected", true); -- } -- } -+ if (selects.length <= 0) { -+ return cloned; - } -- var elIsCanvas = el.tagName === "CANVAS"; -- var canvases = elIsCanvas ? [el] : _toConsumableArray(el.querySelectorAll("canvas")); -- if (canvases.length > 0) { -- var clonedCanvases = elIsCanvas ? [cloned] : _toConsumableArray(cloned.querySelectorAll("canvas")); -- for (var _i = 0; _i < clonedCanvases.length; _i++) { -- var canvas = canvases[_i]; -- var clonedCanvas = clonedCanvases[_i]; -- clonedCanvas.width = canvas.width; -- clonedCanvas.height = canvas.height; -- if (canvas.width > 0 && canvas.height > 0) { -- clonedCanvas.getContext("2d").drawImage(canvas, 0, 0); -- } -+ var clonedSelects = elIsSelect ? [cloned] : _toConsumableArray(cloned.querySelectorAll('select')); -+ for (var i = 0; i < clonedSelects.length; i++) { -+ var select = clonedSelects[i]; -+ var value = values[i]; -+ var optionEl = select.querySelector("option[value=\"".concat(value, "\"")); -+ if (optionEl) { -+ optionEl.setAttribute('selected', true); - } - } - return cloned; - } - -- /** -- * @type {{USE_COMPUTED_STYLE_INSTEAD_OF_BOUNDING_RECT: string}} -- */ -- var FEATURE_FLAG_NAMES = Object.freeze({ -- // This flag exists as a workaround for issue 454 (basically a browser bug) - seems like these rect values take time to update when in grid layout. Setting it to true can cause strange behaviour in the REPL for non-grid zones, see issue 470 -- USE_COMPUTED_STYLE_INSTEAD_OF_BOUNDING_RECT: "USE_COMPUTED_STYLE_INSTEAD_OF_BOUNDING_RECT" -- }); -- var featureFlagsMap = _defineProperty({}, FEATURE_FLAG_NAMES.USE_COMPUTED_STYLE_INSTEAD_OF_BOUNDING_RECT, false); -- -- /** -- * @param {FEATURE_FLAG_NAMES} flagName -- * @param {boolean} flagValue -- */ -- function setFeatureFlag(flagName, flagValue) { -- if (!FEATURE_FLAG_NAMES[flagName]) throw new Error("Can't set non existing feature flag ".concat(flagName, "! Supported flags: ").concat(Object.keys(FEATURE_FLAG_NAMES))); -- featureFlagsMap[flagName] = !!flagValue; -- } -- -- /** -- * -- * @param {FEATURE_FLAG_NAMES} flagName -- * @return {boolean} -- */ -- function getFeatureFlag(flagName) { -- if (!FEATURE_FLAG_NAMES[flagName]) throw new Error("Can't get non existing feature flag ".concat(flagName, "! Supported flags: ").concat(Object.keys(FEATURE_FLAG_NAMES))); -- return featureFlagsMap[flagName]; -- } -- - var TRANSITION_DURATION_SECONDS = 0.2; - - /** -@@ -1220,10 +1062,8 @@ - left: (currentMouseX - draggedElRect.left) / draggedElRect.width, - top: (currentMouseY - draggedElRect.top) / draggedElRect.height - }; -- if (!getFeatureFlag(FEATURE_FLAG_NAMES.USE_COMPUTED_STYLE_INSTEAD_OF_BOUNDING_RECT)) { -- draggedEl.style.height = "".concat(newRect.height, "px"); -- draggedEl.style.width = "".concat(newRect.width, "px"); -- } -+ draggedEl.style.height = "".concat(newRect.height, "px"); -+ draggedEl.style.width = "".concat(newRect.width, "px"); - draggedEl.style.left = "".concat(parseFloat(draggedEl.style.left) - relativeDistanceOfMousePointerFromDraggedSides.left * widthChange, "px"); - draggedEl.style.top = "".concat(parseFloat(draggedEl.style.top) - relativeDistanceOfMousePointerFromDraggedSides.top * heightChange, "px"); - } -@@ -1236,9 +1076,7 @@ - function copyStylesFromTo(copyFromEl, copyToEl) { - var computedStyle = window.getComputedStyle(copyFromEl); - Array.from(computedStyle).filter(function (s) { -- return s.startsWith("background") || s.startsWith("padding") || s.startsWith("font") || s.startsWith("text") || s.startsWith("align") || s.startsWith("justify") || s.startsWith("display") || s.startsWith("flex") || s.startsWith("border") || s === "opacity" || s === "color" || s === "list-style-type" || -- // copying with and height to make up for rect update timing issues in some browsers -- getFeatureFlag(FEATURE_FLAG_NAMES.USE_COMPUTED_STYLE_INSTEAD_OF_BOUNDING_RECT) && (s === "width" || s === "height"); -+ return s.startsWith("background") || s.startsWith("padding") || s.startsWith("font") || s.startsWith("text") || s.startsWith("align") || s.startsWith("justify") || s.startsWith("display") || s.startsWith("flex") || s.startsWith("border") || s === "opacity" || s === "color" || s === "list-style-type"; - }).forEach(function (s) { - return copyToEl.style.setProperty(s, computedStyle.getPropertyValue(s), computedStyle.getPropertyPriority(s)); - }); -@@ -1355,13 +1193,10 @@ - - var DEFAULT_DROP_ZONE_TYPE$1 = "--any--"; - var MIN_OBSERVATION_INTERVAL_MS = 100; -- var DISABLED_OBSERVATION_INTERVAL_MS = 20; - var MIN_MOVEMENT_BEFORE_DRAG_START_PX = 3; -- var DEFAULT_TOUCH_DELAY_MS = 80; - var DEFAULT_DROP_TARGET_STYLE$1 = { - outline: "rgba(255, 255, 102, 0.7) solid 2px" - }; -- var ORIGINAL_DRAGGED_ITEM_MARKER_ATTRIBUTE = "data-is-dnd-original-dragged-item"; - var originalDragTarget; - var draggedEl; - var draggedElData; -@@ -1377,9 +1212,6 @@ - var unlockOriginDzMinDimensions; - var isDraggedOutsideOfAnyDz = false; - var scheduledForRemovalAfterDrop = []; -- var multiScroller; -- var touchDragHoldTimer; -- var touchHoldElapsed = false; - - // a map from type to a set of drop-zones - var typeToDropZones$1 = new Map(); -@@ -1414,6 +1246,7 @@ - printDebug(function () { - return "watching dragged element"; - }); -+ armWindowScroller(); - var dropZones = typeToDropZones$1.get(draggedElType); - var _iterator = _createForOfIteratorHelper(dropZones), - _step; -@@ -1430,21 +1263,17 @@ - _iterator.f(); - } - window.addEventListener(DRAGGED_LEFT_DOCUMENT_EVENT_NAME, handleDrop$1); -- - // it is important that we don't have an interval that is faster than the flip duration because it can cause elements to jump bach and forth -- var setIntervalMs = Math.max.apply(Math, _toConsumableArray(Array.from(dropZones.keys()).map(function (dz) { -+ var observationIntervalMs = Math.max.apply(Math, [MIN_OBSERVATION_INTERVAL_MS].concat(_toConsumableArray(Array.from(dropZones.keys()).map(function (dz) { - return dzToConfig$1.get(dz).dropAnimationDurationMs; -- }))); -- var observationIntervalMs = setIntervalMs === 0 ? DISABLED_OBSERVATION_INTERVAL_MS : Math.max(setIntervalMs, MIN_OBSERVATION_INTERVAL_MS); // if setIntervalMs is 0 it goes to 20, otherwise it is max between it and min observation. -- multiScroller = createMultiScroller(dropZones, function () { -- return currentMousePosition; -- }); -- observe(draggedEl, dropZones, observationIntervalMs * 1.07, multiScroller); -+ })))); -+ observe(draggedEl, dropZones, observationIntervalMs * 1.07); - } - function unWatchDraggedElement() { - printDebug(function () { - return "unwatching dragged element"; - }); -+ disarmWindowScroller(); - var dropZones = typeToDropZones$1.get(draggedElType); - var _iterator2 = _createForOfIteratorHelper(dropZones), - _step2; -@@ -1461,21 +1290,20 @@ - _iterator2.f(); - } - window.removeEventListener(DRAGGED_LEFT_DOCUMENT_EVENT_NAME, handleDrop$1); -- // ensuring multiScroller is not already destroyed before destroying -- if (multiScroller) { -- multiScroller.destroy(); -- multiScroller = undefined; -- } - unobserve(); - } -- function findShadowElementIdx(items) { -+ -+ // finds the initial placeholder that is placed there on drag start -+ function findShadowPlaceHolderIdx(items) { - return items.findIndex(function (item) { -- return !!item[SHADOW_ITEM_MARKER_PROPERTY_NAME]; -+ return item[ITEM_ID_KEY] === SHADOW_PLACEHOLDER_ITEM_ID; - }); - } -- function createShadowElData(draggedElData) { -- var _objectSpread2$1; -- return _objectSpread2(_objectSpread2({}, draggedElData), {}, (_objectSpread2$1 = {}, _defineProperty(_objectSpread2$1, SHADOW_ITEM_MARKER_PROPERTY_NAME, true), _defineProperty(_objectSpread2$1, ITEM_ID_KEY, SHADOW_PLACEHOLDER_ITEM_ID), _objectSpread2$1)); -+ function findShadowElementIdx(items) { -+ // checking that the id is not the placeholder's for Dragula like usecases -+ return items.findIndex(function (item) { -+ return !!item[SHADOW_ITEM_MARKER_PROPERTY_NAME] && item[ITEM_ID_KEY] !== SHADOW_PLACEHOLDER_ITEM_ID; -+ }); - } - - /* custom drag-events handlers */ -@@ -1510,8 +1338,20 @@ - id: draggedElData[ITEM_ID_KEY], - source: SOURCES.POINTER - }); -+ } else { -+ var shadowPlaceHolderIdx = findShadowPlaceHolderIdx(items); -+ if (shadowPlaceHolderIdx !== -1) { -+ // only happens right after drag start, on the first drag entered event -+ printDebug(function () { -+ return "removing placeholder item from origin dz"; -+ }); -+ items.splice(shadowPlaceHolderIdx, 1); -+ } - } -- var shadowElIdx = e.detail.indexObj.index; -+ var _e$detail$indexObj = e.detail.indexObj, -+ index = _e$detail$indexObj.index, -+ isProximityBased = _e$detail$indexObj.isProximityBased; -+ var shadowElIdx = isProximityBased && index === e.currentTarget.children.length - 1 ? index + 1 : index; - shadowElDropZone = e.currentTarget; - items.splice(shadowElIdx, 0, shadowElData); - dispatchConsiderEvent(e.currentTarget, items, { -@@ -1527,7 +1367,7 @@ - return ["dragged left", e.currentTarget, e.detail]; - }); - var _dzToConfig$get2 = dzToConfig$1.get(e.currentTarget), -- originalItems = _dzToConfig$get2.items, -+ items = _dzToConfig$get2.items, - dropFromOthersDisabled = _dzToConfig$get2.dropFromOthersDisabled; - if (dropFromOthersDisabled && e.currentTarget !== originDropZone && e.currentTarget !== shadowElDropZone) { - printDebug(function () { -@@ -1535,12 +1375,8 @@ - }); - return; - } -- var items = _toConsumableArray(originalItems); - var shadowElIdx = findShadowElementIdx(items); -- if (shadowElIdx !== -1) { -- items.splice(shadowElIdx, 1); -- } -- var origShadowDz = shadowElDropZone; -+ var shadowItem = items.splice(shadowElIdx, 1)[0]; - shadowElDropZone = undefined; - var _e$detail = e.detail, - type = _e$detail.type, -@@ -1551,9 +1387,8 @@ - }); - isDraggedOutsideOfAnyDz = true; - shadowElDropZone = originDropZone; -- // if the last zone it left is the origin dz, we will put it back into items (which we just removed it from) -- var originZoneItems = origShadowDz === originDropZone ? items : _toConsumableArray(dzToConfig$1.get(originDropZone).items); -- originZoneItems.splice(originIndex, 0, shadowElData); -+ var originZoneItems = dzToConfig$1.get(originDropZone).items; -+ originZoneItems.splice(originIndex, 0, shadowItem); - dispatchConsiderEvent(originDropZone, originZoneItems, { - trigger: TRIGGERS.DRAGGED_LEFT_ALL, - id: draggedElData[ITEM_ID_KEY], -@@ -1572,7 +1407,7 @@ - return ["dragged is over index", e.currentTarget, e.detail]; - }); - var _dzToConfig$get3 = dzToConfig$1.get(e.currentTarget), -- originalItems = _dzToConfig$get3.items, -+ items = _dzToConfig$get3.items, - dropFromOthersDisabled = _dzToConfig$get3.dropFromOthersDisabled; - if (dropFromOthersDisabled && e.currentTarget !== originDropZone) { - printDebug(function () { -@@ -1580,13 +1415,10 @@ - }); - return; - } -- var items = _toConsumableArray(originalItems); - isDraggedOutsideOfAnyDz = false; - var index = e.detail.indexObj.index; - var shadowElIdx = findShadowElementIdx(items); -- if (shadowElIdx !== -1) { -- items.splice(shadowElIdx, 1); -- } -+ items.splice(shadowElIdx, 1); - items.splice(index, 0, shadowElData); - dispatchConsiderEvent(e.currentTarget, items, { - trigger: TRIGGERS.DRAGGED_OVER_INDEX, -@@ -1599,9 +1431,12 @@ - function handleMouseMove(e) { - e.preventDefault(); - var c = e.touches ? e.touches[0] : e; -+ var _dzToConfig$get4 = dzToConfig$1.get(originDropZone), -+ constrainAxisX = _dzToConfig$get4.constrainAxisX, -+ constrainAxisY = _dzToConfig$get4.constrainAxisY; - currentMousePosition = { -- x: c.clientX, -- y: c.clientY -+ x: constrainAxisX ? dragStartMousePosition.x : c.clientX, -+ y: constrainAxisY ? dragStartMousePosition.y : c.clientY - }; - draggedEl.style.transform = "translate3d(".concat(currentMousePosition.x - dragStartMousePosition.x, "px, ").concat(currentMousePosition.y - dragStartMousePosition.y, "px, 0)"); - } -@@ -1626,9 +1461,9 @@ - printDebug(function () { - return ["dropped in dz", shadowElDropZone]; - }); -- var _dzToConfig$get4 = dzToConfig$1.get(shadowElDropZone), -- items = _dzToConfig$get4.items, -- type = _dzToConfig$get4.type; -+ var _dzToConfig$get5 = dzToConfig$1.get(shadowElDropZone), -+ items = _dzToConfig$get5.items, -+ type = _dzToConfig$get5.type; - styleInactiveDropZones(typeToDropZones$1.get(type), function (dz) { - return dzToConfig$1.get(dz).dropTargetStyle; - }, function (dz) { -@@ -1636,11 +1471,7 @@ - }); - var shadowElIdx = findShadowElementIdx(items); - // the handler might remove the shadow element, ex: dragula like copy on drag -- if (shadowElIdx === -1) { -- if (shadowElDropZone === originDropZone) { -- shadowElIdx = originIndex; -- } -- } -+ if (shadowElIdx === -1) shadowElIdx = originIndex; - items = items.map(function (item) { - return item[SHADOW_ITEM_MARKER_PROPERTY_NAME] ? draggedElData : item; - }); -@@ -1659,29 +1490,21 @@ - source: SOURCES.POINTER - }); - } -- // In edge cases the dom might have not been updated yet so we can't rely on data list index -- var domShadowEl = Array.from(shadowElDropZone.children).find(function (c) { -- return c.getAttribute(SHADOW_ELEMENT_ATTRIBUTE_NAME); -- }); -- if (domShadowEl) unDecorateShadowElement(domShadowEl); -+ unDecorateShadowElement(shadowElDropZone.children[shadowElIdx]); - cleanupPostDrop(); - } -- if (dzToConfig$1.get(shadowElDropZone).dropAnimationDisabled) { -- finalizeWithinZone(); -- } else { -- animateDraggedToFinalPosition(shadowElIdx, finalizeWithinZone); -- } -+ animateDraggedToFinalPosition(shadowElIdx, finalizeWithinZone); - } - - // helper function for handleDrop - function animateDraggedToFinalPosition(shadowElIdx, callback) { -- var shadowElRect = shadowElIdx > -1 ? getBoundingRectNoTransforms(shadowElDropZone.children[shadowElIdx], false) : getBoundingRectNoTransforms(shadowElDropZone, false); -+ var shadowElRect = getBoundingRectNoTransforms(shadowElDropZone.children[shadowElIdx]); - var newTransform = { - x: shadowElRect.left - parseFloat(draggedEl.style.left), - y: shadowElRect.top - parseFloat(draggedEl.style.top) - }; -- var _dzToConfig$get5 = dzToConfig$1.get(shadowElDropZone), -- dropAnimationDurationMs = _dzToConfig$get5.dropAnimationDurationMs; -+ var _dzToConfig$get6 = dzToConfig$1.get(shadowElDropZone), -+ dropAnimationDurationMs = _dzToConfig$get6.dropAnimationDurationMs; - var transition = "transform ".concat(dropAnimationDurationMs, "ms ease"); - draggedEl.style.transition = draggedEl.style.transition ? draggedEl.style.transition + "," + transition : transition; - draggedEl.style.transform = "translate3d(".concat(newTransform.x, "px, ").concat(newTransform.y, "px, 0)"); -@@ -1699,12 +1522,19 @@ - } - /* cleanup */ - function cleanupPostDrop() { -- // Remove the temporary elements that were kept in the DOM during the drag -- if (draggedEl && draggedEl.remove) { -- draggedEl.remove(); -- } -- if (originalDragTarget && originalDragTarget.remove) { -- originalDragTarget.remove(); -+ draggedEl.remove(); -+ originalDragTarget.remove(); -+ if (scheduledForRemovalAfterDrop.length) { -+ printDebug(function () { -+ return ["will destroy zones that were removed during drag", scheduledForRemovalAfterDrop]; -+ }); -+ scheduledForRemovalAfterDrop.forEach(function (_ref) { -+ var dz = _ref.dz, -+ destroy = _ref.destroy; -+ destroy(); -+ dz.remove(); -+ }); -+ scheduledForRemovalAfterDrop = []; - } - draggedEl = undefined; - originalDragTarget = undefined; -@@ -1720,23 +1550,6 @@ - finalizingPreviousDrag = false; - unlockOriginDzMinDimensions = undefined; - isDraggedOutsideOfAnyDz = false; -- if (touchDragHoldTimer) { -- clearTimeout(touchDragHoldTimer); -- } -- touchDragHoldTimer = undefined; -- touchHoldElapsed = false; -- if (scheduledForRemovalAfterDrop.length) { -- printDebug(function () { -- return ["will destroy zones that were removed during drag", scheduledForRemovalAfterDrop]; -- }); -- scheduledForRemovalAfterDrop.forEach(function (_ref) { -- var dz = _ref.dz, -- destroy = _ref.destroy; -- destroy(); -- dz.remove(); -- }); -- scheduledForRemovalAfterDrop = []; -- } - } - function dndzone$2(node, options) { - var initialized = false; -@@ -1744,15 +1557,15 @@ - items: undefined, - type: undefined, - flipDurationMs: 0, -+ constrainAxisX: false, -+ constrainAxisY: false, - dragDisabled: false, - morphDisabled: false, - dropFromOthersDisabled: false, - dropTargetStyle: DEFAULT_DROP_TARGET_STYLE$1, - dropTargetClasses: [], - transformDraggedElement: function transformDraggedElement() {}, -- centreDraggedOnCursor: false, -- dropAnimationDisabled: false, -- delayTouchStartMs: 0 -+ centreDraggedOnCursor: false - }; - printDebug(function () { - return ["dndzone good to go options: ".concat(toString(options), ", config: ").concat(toString(config)), { -@@ -1780,52 +1593,16 @@ - window.removeEventListener("touchmove", handleMouseMoveMaybeDragStart); - window.removeEventListener("mouseup", handleFalseAlarm); - window.removeEventListener("touchend", handleFalseAlarm); -- if (touchDragHoldTimer) { -- clearTimeout(touchDragHoldTimer); -- touchDragHoldTimer = undefined; -- touchHoldElapsed = false; -- } - } -- function handleFalseAlarm(e) { -+ function handleFalseAlarm() { - removeMaybeListeners(); - originalDragTarget = undefined; - dragStartMousePosition = undefined; - currentMousePosition = undefined; -- -- // dragging initiated by touch events prevents onclick from initially firing -- if (e.type === "touchend") { -- var clickEvent = new Event("click", { -- bubbles: true, -- cancelable: true -- }); -- // doing it this way instead of calling .click() because that doesn't work for SVG elements -- e.target.dispatchEvent(clickEvent); -- } - } - function handleMouseMoveMaybeDragStart(e) { -- var isTouch = !!e.touches; -- var c = isTouch ? e.touches[0] : e; -- // If touch drag delay is configured and not elapsed yet, allow scrolling until either -- // the delay elapses (timer will call handleDragStart) or the user moves significantly, -- // in which case we cancel the potential drag and let the interaction be a scroll. -- if (isTouch && config.delayTouchStartMs > 0 && !touchHoldElapsed) { -- currentMousePosition = { -- x: c.clientX, -- y: c.clientY -- }; -- if (Math.abs(currentMousePosition.x - dragStartMousePosition.x) >= MIN_MOVEMENT_BEFORE_DRAG_START_PX || Math.abs(currentMousePosition.y - dragStartMousePosition.y) >= MIN_MOVEMENT_BEFORE_DRAG_START_PX) { -- // User started scrolling, cancel drag attempt. -- if (touchDragHoldTimer) { -- clearTimeout(touchDragHoldTimer); -- touchDragHoldTimer = undefined; -- } -- handleFalseAlarm(e); -- } -- return; // Do not preventDefault so scrolling works. -- } -- -- // legacy / post-delay path – block scrolling and maybe start drag - e.preventDefault(); -+ var c = e.touches ? e.touches[0] : e; - currentMousePosition = { - x: c.clientX, - y: c.clientY -@@ -1856,29 +1633,14 @@ - }); - return; - } -- var isTouchStart = !!e.touches; -- var useDelay = isTouchStart && config.delayTouchStartMs > 0; -- if (!useDelay) { -- e.preventDefault(); -- } - e.stopPropagation(); -- var c = isTouchStart ? e.touches[0] : e; -+ var c = e.touches ? e.touches[0] : e; - dragStartMousePosition = { - x: c.clientX, - y: c.clientY - }; - currentMousePosition = _objectSpread2({}, dragStartMousePosition); - originalDragTarget = e.currentTarget; -- if (useDelay) { -- touchHoldElapsed = false; -- touchDragHoldTimer = window.setTimeout(function () { -- // If the finger is still down and no false-alarm happened -- if (!originalDragTarget) return; -- touchHoldElapsed = true; -- removeMaybeListeners(); -- handleDragStart(); -- }, config.delayTouchStartMs); -- } - addMaybeListeners(); - } - function handleDragStart() { -@@ -1891,32 +1653,29 @@ - var currentIdx = elToIdx.get(originalDragTarget); - originIndex = currentIdx; - originDropZone = originalDragTarget.parentElement; -- /** @type {ShadowRoot | HTMLDocument | Element } */ -- var rootNode = originDropZone.closest("dialog") || originDropZone.closest("[popover]") || originDropZone.getRootNode(); -+ /** @type {ShadowRoot | HTMLDocument} */ -+ var rootNode = originDropZone.getRootNode(); - var originDropZoneRoot = rootNode.body || rootNode; -- var originalItems = config.items, -+ var items = config.items, - type = config.type, - centreDraggedOnCursor = config.centreDraggedOnCursor; -- var items = _toConsumableArray(originalItems); -- draggedElData = items[currentIdx]; -+ draggedElData = _objectSpread2({}, items[currentIdx]); - draggedElType = type; -- shadowElData = createShadowElData(draggedElData); -+ shadowElData = _objectSpread2(_objectSpread2({}, draggedElData), {}, _defineProperty({}, SHADOW_ITEM_MARKER_PROPERTY_NAME, true)); -+ // The initial shadow element. We need a different id at first in order to avoid conflicts and timing issues -+ var placeHolderElData = _objectSpread2(_objectSpread2({}, shadowElData), {}, _defineProperty({}, ITEM_ID_KEY, SHADOW_PLACEHOLDER_ITEM_ID)); - - // creating the draggable element - draggedEl = createDraggedElementFrom(originalDragTarget, centreDraggedOnCursor && currentMousePosition); -- originDropZoneRoot.appendChild(draggedEl); - // We will keep the original dom node in the dom because touch events keep firing on it, we want to re-add it after the framework removes it - function keepOriginalElementInDom() { -- if (!originalDragTarget.parentElement) { -- originalDragTarget.setAttribute(ORIGINAL_DRAGGED_ITEM_MARKER_ATTRIBUTE, true); -- originDropZoneRoot.appendChild(originalDragTarget); -- // have to watch before we hide, otherwise Svelte 5 $state gets confused -- watchDraggedElement(); -- hideElement(originalDragTarget); -- // after the removal of the original element we can give the shadow element the original item id so that the host zone can find it and render it correctly if it does lookups by id -- shadowElData[ITEM_ID_KEY] = draggedElData[ITEM_ID_KEY]; -+ if (!draggedEl.parentElement) { -+ originDropZoneRoot.appendChild(draggedEl); - // to prevent the outline from disappearing - draggedEl.focus(); -+ watchDraggedElement(); -+ hideElement(originalDragTarget); -+ originDropZoneRoot.appendChild(originalDragTarget); - } else { - window.requestAnimationFrame(keepOriginalElementInDom); - } -@@ -1931,7 +1690,7 @@ - }); - - // removing the original element by removing its data entry -- items.splice(currentIdx, 1, shadowElData); -+ items.splice(currentIdx, 1, placeHolderElData); - unlockOriginDzMinDimensions = preventShrinking(originDropZone); - dispatchConsiderEvent(originDropZone, items, { - trigger: TRIGGERS.DRAG_STARTED, -@@ -1961,6 +1720,10 @@ - dropAnimationDurationMs = _ref2$flipDurationMs === void 0 ? 0 : _ref2$flipDurationMs, - _ref2$type = _ref2.type, - newType = _ref2$type === void 0 ? DEFAULT_DROP_ZONE_TYPE$1 : _ref2$type, -+ _ref2$constrainAxisX = _ref2.constrainAxisX, -+ constrainAxisX = _ref2$constrainAxisX === void 0 ? false : _ref2$constrainAxisX, -+ _ref2$constrainAxisY = _ref2.constrainAxisY, -+ constrainAxisY = _ref2$constrainAxisY === void 0 ? false : _ref2$constrainAxisY, - _ref2$dragDisabled = _ref2.dragDisabled, - dragDisabled = _ref2$dragDisabled === void 0 ? false : _ref2$dragDisabled, - _ref2$morphDisabled = _ref2.morphDisabled, -@@ -1974,29 +1737,20 @@ - _ref2$transformDragge = _ref2.transformDraggedElement, - transformDraggedElement = _ref2$transformDragge === void 0 ? function () {} : _ref2$transformDragge, - _ref2$centreDraggedOn = _ref2.centreDraggedOnCursor, -- centreDraggedOnCursor = _ref2$centreDraggedOn === void 0 ? false : _ref2$centreDraggedOn, -- _ref2$dropAnimationDi = _ref2.dropAnimationDisabled, -- dropAnimationDisabled = _ref2$dropAnimationDi === void 0 ? false : _ref2$dropAnimationDi, -- _ref2$delayTouchStart = _ref2.delayTouchStart, -- delayTouchStartOpt = _ref2$delayTouchStart === void 0 ? false : _ref2$delayTouchStart; -+ centreDraggedOnCursor = _ref2$centreDraggedOn === void 0 ? false : _ref2$centreDraggedOn; - config.dropAnimationDurationMs = dropAnimationDurationMs; -- var effectiveDelayMs = 0; -- if (delayTouchStartOpt === true) { -- effectiveDelayMs = DEFAULT_TOUCH_DELAY_MS; -- } else if (typeof delayTouchStartOpt === "number" && isFinite(delayTouchStartOpt) && delayTouchStartOpt >= 0) { -- effectiveDelayMs = delayTouchStartOpt; -- } -- config.delayTouchStartMs = effectiveDelayMs; - if (config.type && newType !== config.type) { - unregisterDropZone$1(node, config.type); - } - config.type = newType; -+ registerDropZone$1(node, newType); - config.items = _toConsumableArray(items); -+ config.constrainAxisX = constrainAxisX; -+ config.constrainAxisY = constrainAxisY; - config.dragDisabled = dragDisabled; - config.morphDisabled = morphDisabled; - config.transformDraggedElement = transformDraggedElement; - config.centreDraggedOnCursor = centreDraggedOnCursor; -- config.dropAnimationDisabled = dropAnimationDisabled; - - // realtime update for dropTargetStyle - if (initialized && isWorkingOnPreviousDrag && !finalizingPreviousDrag && (!areObjectsShallowEqual(dropTargetStyle, config.dropTargetStyle) || !areArraysShallowEqualSameOrder(dropTargetClasses, config.dropTargetClasses))) { -@@ -2035,16 +1789,15 @@ - } - config.dropFromOthersDisabled = dropFromOthersDisabled; - dzToConfig$1.set(node, config); -- registerDropZone$1(node, newType); -- var shadowElIdx = isWorkingOnPreviousDrag ? findShadowElementIdx(config.items) : -1; -+ var shadowElIdx = findShadowElementIdx(config.items); - for (var idx = 0; idx < node.children.length; idx++) { - var draggableEl = node.children[idx]; - styleDraggable(draggableEl, dragDisabled); - if (idx === shadowElIdx) { -+ config.transformDraggedElement(draggedEl, draggedElData, idx); - if (!morphDisabled) { - morphDraggedElementToBeLike(draggedEl, draggableEl, currentMousePosition.x, currentMousePosition.y); - } -- config.transformDraggedElement(draggedEl, draggedElData, idx); - decorateShadowEl(draggableEl); - continue; - } -@@ -2078,7 +1831,7 @@ - unregisterDropZone$1(node, dzToConfig$1.get(node).type); - dzToConfig$1["delete"](node); - } -- if (isWorkingOnPreviousDrag && !node.closest("[".concat(ORIGINAL_DRAGGED_ITEM_MARKER_ATTRIBUTE, "]"))) { -+ if (isWorkingOnPreviousDrag) { - printDebug(function () { - return "pointer dndzone will be scheduled for destruction"; - }); -@@ -2356,7 +2109,6 @@ - type: undefined, - dragDisabled: false, - zoneTabIndex: 0, -- zoneItemTabIndex: 0, - dropFromOthersDisabled: false, - dropTargetStyle: DEFAULT_DROP_TARGET_STYLE, - dropTargetClasses: [], -@@ -2486,7 +2238,7 @@ - var children = Array.from(node.children); - var focusedItemIdx = children.indexOf(draggableEl); - focusedItem = draggableEl; -- focusedItem.tabIndex = config.zoneItemTabIndex; -+ focusedItem.tabIndex = 0; - focusedItemId = items[focusedItemIdx][ITEM_ID_KEY]; - focusedItemLabel = children[focusedItemIdx].getAttribute("aria-label") || ""; - } -@@ -2499,8 +2251,6 @@ - dragDisabled = _ref2$dragDisabled === void 0 ? false : _ref2$dragDisabled, - _ref2$zoneTabIndex = _ref2.zoneTabIndex, - zoneTabIndex = _ref2$zoneTabIndex === void 0 ? 0 : _ref2$zoneTabIndex, -- _ref2$zoneItemTabInde = _ref2.zoneItemTabIndex, -- zoneItemTabIndex = _ref2$zoneItemTabInde === void 0 ? 0 : _ref2$zoneItemTabInde, - _ref2$dropFromOthersD = _ref2.dropFromOthersDisabled, - dropFromOthersDisabled = _ref2$dropFromOthersD === void 0 ? false : _ref2$dropFromOthersD, - _ref2$dropTargetStyle = _ref2.dropTargetStyle, -@@ -2513,7 +2263,6 @@ - config.dragDisabled = dragDisabled; - config.dropFromOthersDisabled = dropFromOthersDisabled; - config.zoneTabIndex = zoneTabIndex; -- config.zoneItemTabIndex = zoneItemTabIndex; - config.dropTargetStyle = dropTargetStyle; - config.dropTargetClasses = dropTargetClasses; - config.autoAriaDisabled = autoAriaDisabled; -@@ -2537,7 +2286,7 @@ - var _loop = function _loop(i) { - var draggableEl = node.children[i]; - allDragTargets.add(draggableEl); -- draggableEl.tabIndex = isDragging ? -1 : config.zoneItemTabIndex; -+ draggableEl.tabIndex = isDragging ? -1 : 0; - if (!autoAriaDisabled) { - draggableEl.setAttribute("role", "listitem"); - } -@@ -2558,7 +2307,7 @@ - }); - // if it is a nested dropzone, it was re-rendered and we need to refresh our pointer - focusedItem = draggableEl; -- focusedItem.tabIndex = config.zoneItemTabIndex; -+ focusedItem.tabIndex = 0; - // without this the element loses focus if it moves backwards in the list - draggableEl.focus(); - } -@@ -2588,7 +2337,7 @@ - return handles; - } - -- var _excluded = ["items", "flipDurationMs", "type", "dragDisabled", "morphDisabled", "dropFromOthersDisabled", "zoneTabIndex", "zoneItemTabIndex", "dropTargetStyle", "dropTargetClasses", "transformDraggedElement", "autoAriaDisabled", "centreDraggedOnCursor", "delayTouchStart", "dropAnimationDisabled"]; -+ var _excluded = ["items", "flipDurationMs", "type", "constrainAxisX", "constrainAxisY", "dragDisabled", "morphDisabled", "dropFromOthersDisabled", "zoneTabIndex", "dropTargetStyle", "dropTargetClasses", "transformDraggedElement", "autoAriaDisabled", "centreDraggedOnCursor"]; - - /** - * A custom action to turn any container to a dnd zone and all of its direct children to draggables -@@ -2600,28 +2349,20 @@ - * @property {array} items - the list of items that was used to generate the children of the given node (the list used in the #each block - * @property {string} [type] - the type of the dnd zone. children dragged from here can only be dropped in other zones of the same type, default to a base type - * @property {number} [flipDurationMs] - if the list animated using flip (recommended), specifies the flip duration such that everything syncs with it without conflict, defaults to zero -+ * @property {boolean} [constrainAxisX] - Constrain dragging by X axis. Drag will be allowed only by Y axis. -+ * @property {boolean} [constrainAxisY] - Constrain dragging by Y axis. Drag will be allowed only by X axis. - * @property {boolean} [dragDisabled] - * @property {boolean} [morphDisabled] - whether dragged element should morph to zone dimensions - * @property {boolean} [dropFromOthersDisabled] - * @property {number} [zoneTabIndex] - set the tabindex of the list container when not dragging -- * @property {number} [zoneItemTabIndex] - set the tabindex of the list container items when not dragging - * @property {object} [dropTargetStyle] - * @property {string[]} [dropTargetClasses] -- * @property {boolean|number} [delayTouchStart] - On touch devices, wait this long before converting the gesture to a drag. -- * `true` uses the built-in default (80 ms). -- * @property {boolean} [dropAnimationDisabled] - cancels the drop animation to place - * @property {function} [transformDraggedElement] - * @param {HTMLElement} node - the element to enhance - * @param {Options} options - * @return {{update: function, destroy: function}} - */ - function dndzone(node, options) { -- if (shouldIgnoreZone(node)) { -- return { -- update: function update() {}, -- destroy: function destroy() {} -- }; -- } - validateOptions(options); - var pointerZone = dndzone$2(node, options); - var keyboardZone = dndzone$1(node, options); -@@ -2637,33 +2378,22 @@ - } - }; - } -- -- /** -- * If the user marked something in the ancestry of our node as shadow element, we can ignore it -- * We need the user to mark it for us because svelte updates the action from deep to shallow (but renders top down) -- * @param {HTMLElement} node -- * @return {boolean} -- */ -- function shouldIgnoreZone(node) { -- return !!node.closest("[".concat(SHADOW_ELEMENT_HINT_ATTRIBUTE_NAME, "=\"true\"]")); -- } - function validateOptions(options) { - /*eslint-disable*/ - var items = options.items; - options.flipDurationMs; - options.type; -+ options.constrainAxisX; -+ options.constrainAxisY; - options.dragDisabled; - options.morphDisabled; - options.dropFromOthersDisabled; -- var zoneTabIndex = options.zoneTabIndex, -- zoneItemTabIndex = options.zoneItemTabIndex; -+ var zoneTabIndex = options.zoneTabIndex; - options.dropTargetStyle; - var dropTargetClasses = options.dropTargetClasses; - options.transformDraggedElement; - options.autoAriaDisabled; - options.centreDraggedOnCursor; -- var delayTouchStart = options.delayTouchStart; -- options.dropAnimationDisabled; - var rest = _objectWithoutProperties(options, _excluded); - /*eslint-enable*/ - if (Object.keys(rest).length > 0) { -@@ -2684,16 +2414,6 @@ - if (zoneTabIndex && !isInt(zoneTabIndex)) { - throw new Error("zoneTabIndex should be a number but instead it is a ".concat(_typeof(zoneTabIndex), ", ").concat(toString(zoneTabIndex))); - } -- if (zoneItemTabIndex && !isInt(zoneItemTabIndex)) { -- throw new Error("zoneItemTabIndex should be a number but instead it is a ".concat(_typeof(zoneItemTabIndex), ", ").concat(toString(zoneItemTabIndex))); -- } -- if (delayTouchStart !== undefined && delayTouchStart !== false) { -- var validBoolean = delayTouchStart === true; -- var validNumber = typeof delayTouchStart === "number" && isFinite(delayTouchStart) && delayTouchStart >= 0; -- if (!validBoolean && !validNumber) { -- throw new Error("delayTouchStart should be a boolean (true/false) or a non-negative number but instead it is a ".concat(_typeof(delayTouchStart), ", ").concat(toString(delayTouchStart))); -- } -- } - } - function isInt(value) { - return !isNaN(value) && function (x) { -@@ -2701,165 +2421,15 @@ - }(parseFloat(value)); - } - -- function createStore(initialValue) { -- var _val = initialValue; -- var subs = new Set(); -- return { -- get: function get() { -- return _val; -- }, -- set: function set(newVal) { -- _val = newVal; -- Array.from(subs).forEach(function (cb) { -- return cb(_val); -- }); -- }, -- subscribe: function subscribe(cb) { -- subs.add(cb); -- cb(_val); -- }, -- unsubscribe: function unsubscribe(cb) { -- subs["delete"](cb); -- } -- }; -- } -- -- var isItemsDragDisabled = createStore(true); -- var userDragDisabled = createStore(false); -- function getAddedOptions() { -- return { -- dragDisabled: userDragDisabled.get() || isItemsDragDisabled.get(), -- zoneItemTabIndex: -1 -- }; -- } -- -- /** -- * This is an action that wraps around the dndzone action to make it easy to work with drag handles -- * When using this you must also use the 'dragHandle' action (see below) on an element inside each item within the zone -- * Credit for the idea and initial implementation goes to @gleuch (Greg Leuch) and @geovie (Georg Vienna) -- * -- * @param {HTMLElement} node -- * @param options - will be passed down to the dndzone -- * @return {{update: (newOptions: Object) => {}, destroy: () => {}}} -- */ -- function dragHandleZone(node, options) { -- var _options$dragDisabled; -- // Initialise stores from initial options -- userDragDisabled.set((_options$dragDisabled = options === null || options === void 0 ? void 0 : options.dragDisabled) !== null && _options$dragDisabled !== void 0 ? _options$dragDisabled : false); -- var currentOptions = options; -- var zone = dndzone(node, _objectSpread2(_objectSpread2({}, currentOptions), getAddedOptions())); -- function updateZone() { -- zone.update(_objectSpread2(_objectSpread2({}, currentOptions), getAddedOptions())); -- } -- -- // Subscribe to internal store so finishing a drag updates the zone -- isItemsDragDisabled.subscribe(updateZone); -- -- // We don't need to subscribe to userDragDisabled here because updates to -- // it always come through the `update` lifecycle and will call `updateZone` -- // anyway. -- -- function consider(e) { -- var _e$detail$info = e.detail.info, -- source = _e$detail$info.source, -- trigger = _e$detail$info.trigger; -- // Ensure dragging is stopped on drag finish via keyboard -- if (source === SOURCES.KEYBOARD && trigger === TRIGGERS.DRAG_STOPPED) { -- isItemsDragDisabled.set(true); -- } -- } -- function finalize(e) { -- var source = e.detail.info.source; -- // Ensure dragging is stopped on drag finish via pointer (mouse, touch) -- if (source === SOURCES.POINTER) { -- isItemsDragDisabled.set(true); -- } -- } -- node.addEventListener("consider", consider); -- node.addEventListener("finalize", finalize); -- return { -- update: function update(newOptions) { -- var _currentOptions$dragD, _currentOptions; -- currentOptions = newOptions; -- // keep store in sync with external prop -- userDragDisabled.set((_currentOptions$dragD = (_currentOptions = currentOptions) === null || _currentOptions === void 0 ? void 0 : _currentOptions.dragDisabled) !== null && _currentOptions$dragD !== void 0 ? _currentOptions$dragD : false); -- updateZone(); -- }, -- destroy: function destroy() { -- node.removeEventListener("consider", consider); -- node.removeEventListener("finalize", finalize); -- isItemsDragDisabled.unsubscribe(updateZone); -- } -- }; -- } -- -- /** -- * This should be used to mark drag handles inside items that belong to a 'dragHandleZone' (see above) -- * @param {HTMLElement} handle -- * @return {{update: *, destroy: *}} -- */ -- function dragHandle(handle) { -- handle.setAttribute("role", "button"); -- function startDrag(e) { -- // preventing default to prevent lag on touch devices (because of the browser checking for screen scrolling) -- e.preventDefault(); -- isItemsDragDisabled.set(false); -- -- // Reset the startDrag/isItemsDragDisabled if the user releases the mouse/touch without initiating a drag -- window.addEventListener("mouseup", resetStartDrag); -- window.addEventListener("touchend", resetStartDrag); -- } -- function handleKeyDown(e) { -- if (e.key === "Enter" || e.key === " ") isItemsDragDisabled.set(false); -- } -- function resetStartDrag() { -- isItemsDragDisabled.set(true); -- window.removeEventListener("mouseup", resetStartDrag); -- window.removeEventListener("touchend", resetStartDrag); -- } -- var recomputeHandleState = function recomputeHandleState() { -- var userDisabled = userDragDisabled.get(); -- var internalDisabled = isItemsDragDisabled.get(); -- if (userDisabled) { -- handle.tabIndex = -1; -- handle.style.cursor = ""; // default cursor -- } else { -- handle.tabIndex = internalDisabled ? 0 : -1; -- handle.style.cursor = internalDisabled ? "grab" : "grabbing"; -- } -- }; -- -- // Subscribe to both stores -- userDragDisabled.subscribe(recomputeHandleState); -- isItemsDragDisabled.subscribe(recomputeHandleState); -- handle.addEventListener("mousedown", startDrag); -- handle.addEventListener("touchstart", startDrag); -- handle.addEventListener("keydown", handleKeyDown); -- return { -- update: function update() {}, -- destroy: function destroy() { -- handle.removeEventListener("mousedown", startDrag); -- handle.removeEventListener("touchstart", startDrag); -- handle.removeEventListener("keydown", handleKeyDown); -- userDragDisabled.unsubscribe(recomputeHandleState); -- isItemsDragDisabled.unsubscribe(recomputeHandleState); -- } -- }; -- } -- - exports.DRAGGED_ELEMENT_ID = DRAGGED_ELEMENT_ID; -- exports.FEATURE_FLAG_NAMES = FEATURE_FLAG_NAMES; - exports.SHADOW_ITEM_MARKER_PROPERTY_NAME = SHADOW_ITEM_MARKER_PROPERTY_NAME; - exports.SHADOW_PLACEHOLDER_ITEM_ID = SHADOW_PLACEHOLDER_ITEM_ID; - exports.SOURCES = SOURCES; - exports.TRIGGERS = TRIGGERS; - exports.alertToScreenReader = alertToScreenReader; - exports.dndzone = dndzone; -- exports.dragHandle = dragHandle; -- exports.dragHandleZone = dragHandleZone; - exports.overrideItemIdKeyNameBeforeInitialisingDndZones = overrideItemIdKeyNameBeforeInitialisingDndZones; - exports.setDebugMode = setDebugMode; -- exports.setFeatureFlag = setFeatureFlag; - - Object.defineProperty(exports, '__esModule', { value: true }); - -diff --git a/dist/index.mjs b/dist/index.mjs -index f48ea5f23aa3547cd68e6541aacabac3504bf0a5..a03b368f5d5bc683331a383e10598cfc8ea99090 100644 ---- a/dist/index.mjs -+++ b/dist/index.mjs -@@ -288,8 +288,7 @@ var SOURCES = { - KEYBOARD: "keyboard" - }; - var SHADOW_ITEM_MARKER_PROPERTY_NAME = "isDndShadowItem"; --var SHADOW_ELEMENT_ATTRIBUTE_NAME = "data-is-dnd-shadow-item-internal"; --var SHADOW_ELEMENT_HINT_ATTRIBUTE_NAME = "data-is-dnd-shadow-item-hint"; -+var SHADOW_ELEMENT_ATTRIBUTE_NAME = "data-is-dnd-shadow-item"; - var SHADOW_PLACEHOLDER_ITEM_ID = "id:dnd-shadow-placeholder-0000"; - var DRAGGED_ELEMENT_ID = "dnd-action-dragged-el"; - var ITEM_ID_KEY = "id"; -@@ -327,7 +326,7 @@ var printDebug = function printDebug() {}; - - /** - * Allows the user to show/hide console debug output -- * * @param {boolean} isDebug -+ * * @param {Boolean} isDebug - */ - function setDebugMode(isDebug) { - if (isDebug) { -@@ -350,13 +349,11 @@ function setDebugMode(isDebug) { - /** - * Gets the bounding rect but removes transforms (ex: flip animation) - * @param {HTMLElement} el -- * @param {boolean} [onlyVisible] - use the visible rect defaults to true - * @return {{top: number, left: number, bottom: number, right: number}} - */ - function getBoundingRectNoTransforms(el) { -- var onlyVisible = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; - var ta; -- var rect = onlyVisible ? getVisibleRectRecursive(el) : el.getBoundingClientRect(); -+ var rect = el.getBoundingClientRect(); - var style = getComputedStyle(el); - var tx = style.transform; - if (tx) { -@@ -497,58 +494,30 @@ function calcDistanceBetweenCenters(elA, elB) { - - /** - * @param {HTMLElement} el - the element to check -- * @returns {boolean} - true if the element in its entirety is off-screen including the scrollable area (the normal dom events look at the mouse rather than the element) -+ * @returns {boolean} - true if the element in its entirety is off screen including the scrollable area (the normal dom events look at the mouse rather than the element) - */ - function isElementOffDocument(el) { - var rect = getAbsoluteRect(el); - return rect.right < 0 || rect.left > document.documentElement.scrollWidth || rect.bottom < 0 || rect.top > document.documentElement.scrollHeight; - } --function getVisibleRectRecursive(element) { -- var rect = element.getBoundingClientRect(); -- var visibleRect = { -- top: rect.top, -- bottom: rect.bottom, -- left: rect.left, -- right: rect.right -- }; - -- // Traverse up the DOM hierarchy, checking for scrollable ancestors -- var parent = element.parentElement; -- while (parent && parent !== document.body) { -- var parentRect = parent.getBoundingClientRect(); -- -- // Check if the parent has a scrollable overflow -- var overflowY = window.getComputedStyle(parent).overflowY; -- var overflowX = window.getComputedStyle(parent).overflowX; -- var isScrollableY = overflowY === "scroll" || overflowY === "auto"; -- var isScrollableX = overflowX === "scroll" || overflowX === "auto"; -- -- // Constrain the visible area to the parent's visible area -- if (isScrollableY) { -- visibleRect.top = Math.max(visibleRect.top, parentRect.top); -- visibleRect.bottom = Math.min(visibleRect.bottom, parentRect.bottom); -- } -- if (isScrollableX) { -- visibleRect.left = Math.max(visibleRect.left, parentRect.left); -- visibleRect.right = Math.min(visibleRect.right, parentRect.right); -- } -- parent = parent.parentElement; -+/** -+ * If the point is inside the element returns its distances from the sides, otherwise returns null -+ * @param {Point} point -+ * @param {HTMLElement} el -+ * @return {null|{top: number, left: number, bottom: number, right: number}} -+ */ -+function calcInnerDistancesBetweenPointAndSidesOfElement(point, el) { -+ var rect = getAbsoluteRect(el); -+ if (!isPointInsideRect(point, rect)) { -+ return null; - } -- -- // Finally, constrain the visible rect to the viewport -- visibleRect.top = Math.max(visibleRect.top, 0); -- visibleRect.bottom = Math.min(visibleRect.bottom, window.innerHeight); -- visibleRect.left = Math.max(visibleRect.left, 0); -- visibleRect.right = Math.min(visibleRect.right, window.innerWidth); -- -- // Return the visible rectangle, ensuring that all values are valid - return { -- top: visibleRect.top, -- bottom: visibleRect.bottom, -- left: visibleRect.left, -- right: visibleRect.right, -- width: Math.max(0, visibleRect.right - visibleRect.left), -- height: Math.max(0, visibleRect.bottom - visibleRect.top) -+ top: point.y - rect.top, -+ bottom: rect.bottom - point.y, -+ left: point.x - rect.left, -+ // TODO - figure out what is so special about right (why the rect is too big) -+ right: Math.min(rect.right, document.documentElement.clientWidth) - point.x - }; - } - -@@ -565,6 +534,17 @@ function resetIndexesCache() { - } - resetIndexesCache(); - -+/** -+ * Resets the cache that allows for smarter "would be index" resolution for a specific dropzone, should be called after the zone was scrolled -+ * @param {HTMLElement} dz -+ */ -+function resetIndexesCacheForDz(dz) { -+ printDebug(function () { -+ return "resetting indexes cache for dz"; -+ }); -+ dzToShadowIndexToRect["delete"](dz); -+} -+ - /** - * Caches the coordinates of the shadow element when it's in a certain index in a certain dropzone. - * Helpful in order to determine "would be index" more effectively -@@ -641,35 +621,105 @@ function findWouldBeIndex(floatingAboveEl, collectionBelowEl) { - indexOfMin = _i; - } - } -+ return { -+ index: indexOfMin, -+ isProximityBased: true -+ }; -+} - -- // -------- Phantom slot check -------- -- // Regardless of layout (simple vertical list, flex-wrap, grid, floats …) the -- // visually closest drop target can be *after* the current last **real** child. -- // In simple layouts the would be index from the existing children would always be the last index -- // but in more complex layouts (flex-wrap, grid, floats …) it can be any index. -- // The problem is we can't predict where an additional element would be rendered in the general case, -- // We therefore create a temporary, invisible clone of that last element, let -- // the browser position it, measure the distance, and remove it immediately -- // (same task → no paint). This leaves `children` back in its original state -- // before we exit the function, so existing index-caching logic and shadow- -- // element bookkeeping continue to work unchanged. -- if (children.length > 0) { -- var originalLen = children.length; // before we append the phantom -- var template = children[originalLen - 1]; -- var phantom = template.cloneNode(false); // shallow clone is enough for size -- phantom.style.visibility = "hidden"; -- phantom.style.pointerEvents = "none"; -- collectionBelowEl.appendChild(phantom); -- var phantomDistance = calcDistanceBetweenCenters(floatingAboveEl, phantom); -- if (phantomDistance < minDistanceSoFar) { -- indexOfMin = originalLen; // index of phantom slot in original list -+var SCROLL_ZONE_PX = 25; -+function makeScroller() { -+ var scrollingInfo; -+ function resetScrolling() { -+ scrollingInfo = { -+ directionObj: undefined, -+ stepPx: 0 -+ }; -+ } -+ resetScrolling(); -+ // directionObj {x: 0|1|-1, y:0|1|-1} - 1 means down in y and right in x -+ function scrollContainer(containerEl) { -+ var _scrollingInfo = scrollingInfo, -+ directionObj = _scrollingInfo.directionObj, -+ stepPx = _scrollingInfo.stepPx; -+ if (directionObj) { -+ containerEl.scrollBy(directionObj.x * stepPx, directionObj.y * stepPx); -+ window.requestAnimationFrame(function () { -+ return scrollContainer(containerEl); -+ }); - } -+ } -+ function calcScrollStepPx(distancePx) { -+ return SCROLL_ZONE_PX - distancePx; -+ } - -- collectionBelowEl.removeChild(phantom); -+ /** -+ * If the pointer is next to the sides of the element to scroll, will trigger scrolling -+ * Can be called repeatedly with updated pointer and elementToScroll values without issues -+ * @return {boolean} - true if scrolling was needed -+ */ -+ function scrollIfNeeded(pointer, elementToScroll) { -+ if (!elementToScroll) { -+ return false; -+ } -+ var distances = calcInnerDistancesBetweenPointAndSidesOfElement(pointer, elementToScroll); -+ if (distances === null) { -+ resetScrolling(); -+ return false; -+ } -+ var isAlreadyScrolling = !!scrollingInfo.directionObj; -+ var scrollingVertically = false, -+ scrollingHorizontally = false; -+ // vertical -+ if (elementToScroll.scrollHeight > elementToScroll.clientHeight) { -+ if (distances.bottom < SCROLL_ZONE_PX) { -+ scrollingVertically = true; -+ scrollingInfo.directionObj = { -+ x: 0, -+ y: 1 -+ }; -+ scrollingInfo.stepPx = calcScrollStepPx(distances.bottom); -+ } else if (distances.top < SCROLL_ZONE_PX) { -+ scrollingVertically = true; -+ scrollingInfo.directionObj = { -+ x: 0, -+ y: -1 -+ }; -+ scrollingInfo.stepPx = calcScrollStepPx(distances.top); -+ } -+ if (!isAlreadyScrolling && scrollingVertically) { -+ scrollContainer(elementToScroll); -+ return true; -+ } -+ } -+ // horizontal -+ if (elementToScroll.scrollWidth > elementToScroll.clientWidth) { -+ if (distances.right < SCROLL_ZONE_PX) { -+ scrollingHorizontally = true; -+ scrollingInfo.directionObj = { -+ x: 1, -+ y: 0 -+ }; -+ scrollingInfo.stepPx = calcScrollStepPx(distances.right); -+ } else if (distances.left < SCROLL_ZONE_PX) { -+ scrollingHorizontally = true; -+ scrollingInfo.directionObj = { -+ x: -1, -+ y: 0 -+ }; -+ scrollingInfo.stepPx = calcScrollStepPx(distances.left); -+ } -+ if (!isAlreadyScrolling && scrollingHorizontally) { -+ scrollContainer(elementToScroll); -+ return true; -+ } -+ } -+ resetScrolling(); -+ return false; - } - return { -- index: indexOfMin, -- isProximityBased: true -+ scrollIfNeeded: scrollIfNeeded, -+ resetScrolling: resetScrolling - }; - } - -@@ -736,20 +786,21 @@ function areArraysShallowEqualSameOrder(arrA, arrB) { - return true; - } - --var INTERVAL_MS = 200; -+var INTERVAL_MS$1 = 200; - var TOLERANCE_PX = 10; --var next; -+var _makeScroller$1 = makeScroller(), -+ scrollIfNeeded$1 = _makeScroller$1.scrollIfNeeded, -+ resetScrolling$1 = _makeScroller$1.resetScrolling; -+var next$1; - - /** - * Tracks the dragged elements and performs the side effects when it is dragged over a drop zone (basically dispatching custom-events scrolling) - * @param {Set} dropZones - * @param {HTMLElement} draggedEl - * @param {number} [intervalMs = INTERVAL_MS] -- * @param {MultiScroller} multiScroller - */ - function observe(draggedEl, dropZones) { -- var intervalMs = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : INTERVAL_MS; -- var multiScroller = arguments.length > 3 ? arguments[3] : undefined; -+ var intervalMs = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : INTERVAL_MS$1; - // initialization - var lastDropZoneFound; - var lastIndexFound; -@@ -765,10 +816,10 @@ function observe(draggedEl, dropZones) { - */ - function andNow() { - var currentCenterOfDragged = findCenterOfElement(draggedEl); -- var scrolled = multiScroller.multiScrollIfNeeded(); -+ var scrolled = scrollIfNeeded$1(currentCenterOfDragged, lastDropZoneFound); - // we only want to make a new decision after the element was moved a bit to prevent flickering - if (!scrolled && lastCentrePositionOfDragged && Math.abs(lastCentrePositionOfDragged.x - currentCenterOfDragged.x) < TOLERANCE_PX && Math.abs(lastCentrePositionOfDragged.y - currentCenterOfDragged.y) < TOLERANCE_PX) { -- next = window.setTimeout(andNow, intervalMs); -+ next$1 = window.setTimeout(andNow, intervalMs); - return; - } - if (isElementOffDocument(draggedEl)) { -@@ -786,7 +837,7 @@ function observe(draggedEl, dropZones) { - try { - for (_iterator.s(); !(_step = _iterator.n()).done;) { - var dz = _step.value; -- if (scrolled) resetIndexesCache(); -+ if (scrolled) resetIndexesCacheForDz(lastDropZoneFound); - var indexObj = findWouldBeIndex(draggedEl, dz); - if (indexObj === null) { - // it is not inside -@@ -820,7 +871,7 @@ function observe(draggedEl, dropZones) { - } else { - lastIsDraggedInADropZone = true; - } -- next = window.setTimeout(andNow, intervalMs); -+ next$1 = window.setTimeout(andNow, intervalMs); - } - andNow(); - } -@@ -830,230 +881,62 @@ function unobserve() { - printDebug(function () { - return "unobserving"; - }); -- clearTimeout(next); -+ clearTimeout(next$1); -+ resetScrolling$1(); - resetIndexesCache(); - } - --var SCROLL_ZONE_PX = 30; -+var INTERVAL_MS = 300; -+var mousePosition; - - /** -- * Will make a scroller that can scroll any element given to it in any direction -- * @returns {{scrollIfNeeded: function(Point, HTMLElement): boolean, resetScrolling: function(void):void}} -+ * Do not use this! it is visible for testing only until we get over the issue Cypress not triggering the mousemove listeners -+ * // TODO - make private (remove export) -+ * @param {{clientX: number, clientY: number}} e - */ --function makeScroller() { -- var scrollingInfo; -- function resetScrolling() { -- scrollingInfo = { -- directionObj: undefined, -- stepPx: 0 -- }; -- } -- resetScrolling(); -- // directionObj {x: 0|1|-1, y:0|1|-1} - 1 means down in y and right in x -- function scrollContainer(containerEl) { -- var _scrollingInfo = scrollingInfo, -- directionObj = _scrollingInfo.directionObj, -- stepPx = _scrollingInfo.stepPx; -- if (directionObj) { -- containerEl.scrollBy(directionObj.x * stepPx, directionObj.y * stepPx); -- window.requestAnimationFrame(function () { -- return scrollContainer(containerEl); -- }); -- } -- } -- function calcScrollStepPx(distancePx) { -- return SCROLL_ZONE_PX - distancePx; -- } -- -- /** -- * @param {Point} pointer - the pointer will be used to decide in which direction to scroll -- * @param {HTMLElement} elementToScroll - the scroll container -- * If the pointer is next to the sides of the element to scroll, will trigger scrolling -- * Can be called repeatedly with updated pointer and elementToScroll values without issues -- * @return {boolean} - true if scrolling was needed -- */ -- function scrollIfNeeded(pointer, elementToScroll) { -- if (!elementToScroll) { -- return false; -- } -- var distances = calcInnerDistancesBetweenPointAndSidesOfElement(pointer, elementToScroll); -- var isAlreadyScrolling = !!scrollingInfo.directionObj; -- if (distances === null) { -- if (isAlreadyScrolling) resetScrolling(); -- return false; -- } -- var scrollingVertically = false, -- scrollingHorizontally = false; -- // vertical -- if (elementToScroll.scrollHeight > elementToScroll.clientHeight) { -- if (distances.bottom < SCROLL_ZONE_PX) { -- scrollingVertically = true; -- scrollingInfo.directionObj = { -- x: 0, -- y: 1 -- }; -- scrollingInfo.stepPx = calcScrollStepPx(distances.bottom); -- } else if (distances.top < SCROLL_ZONE_PX) { -- scrollingVertically = true; -- scrollingInfo.directionObj = { -- x: 0, -- y: -1 -- }; -- scrollingInfo.stepPx = calcScrollStepPx(distances.top); -- } -- if (!isAlreadyScrolling && scrollingVertically) { -- scrollContainer(elementToScroll); -- return true; -- } -- } -- // horizontal -- if (elementToScroll.scrollWidth > elementToScroll.clientWidth) { -- if (distances.right < SCROLL_ZONE_PX) { -- scrollingHorizontally = true; -- scrollingInfo.directionObj = { -- x: 1, -- y: 0 -- }; -- scrollingInfo.stepPx = calcScrollStepPx(distances.right); -- } else if (distances.left < SCROLL_ZONE_PX) { -- scrollingHorizontally = true; -- scrollingInfo.directionObj = { -- x: -1, -- y: 0 -- }; -- scrollingInfo.stepPx = calcScrollStepPx(distances.left); -- } -- if (!isAlreadyScrolling && scrollingHorizontally) { -- scrollContainer(elementToScroll); -- return true; -- } -- } -- resetScrolling(); -- return false; -- } -- return { -- scrollIfNeeded: scrollIfNeeded, -- resetScrolling: resetScrolling -+function updateMousePosition(e) { -+ var c = e.touches ? e.touches[0] : e; -+ mousePosition = { -+ x: c.clientX, -+ y: c.clientY - }; - } -- --/** -- * If the point is inside the element returns its distances from the sides, otherwise returns null -- * @param {Point} point -- * @param {HTMLElement} el -- * @return {null|{top: number, left: number, bottom: number, right: number}} -- */ --function calcInnerDistancesBetweenPointAndSidesOfElement(point, el) { -- // Even if the scrolling element is small it acts as a scroller for the viewport -- var rect = el === document.scrollingElement ? { -- top: 0, -- bottom: window.innerHeight, -- left: 0, -- right: window.innerWidth -- } : el.getBoundingClientRect(); -- if (!isPointInsideRect(point, rect)) { -- return null; -+var _makeScroller = makeScroller(), -+ scrollIfNeeded = _makeScroller.scrollIfNeeded, -+ resetScrolling = _makeScroller.resetScrolling; -+var next; -+function loop() { -+ if (mousePosition) { -+ var scrolled = scrollIfNeeded(mousePosition, document.documentElement); -+ if (scrolled) resetIndexesCache(); - } -- return { -- top: point.y - rect.top, -- bottom: rect.bottom - point.y, -- left: point.x - rect.left, -- right: rect.right - point.x -- }; -+ next = window.setTimeout(loop, INTERVAL_MS); - } - - /** -- @typedef {Object} MultiScroller -- @property {function():boolean} multiScrollIfNeeded - call this on every "tick" to scroll containers if needed, returns true if anything was scrolled --/** -- * Creates a scroller than can scroll any of the provided containers or any of their scrollable parents (including the document's scrolling element) -- * @param {HTMLElement[]} baseElementsForScrolling -- * @param {function():Point} getPointerPosition -- * @return {MultiScroller} -+ * will start watching the mouse pointer and scroll the window if it goes next to the edges - */ --function createMultiScroller() { -- var baseElementsForScrolling = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; -- var getPointerPosition = arguments.length > 1 ? arguments[1] : undefined; -+function armWindowScroller() { - printDebug(function () { -- return "creating multi-scroller"; -+ return "arming window scroller"; - }); -- var scrollingContainersSet = findRelevantScrollContainers(baseElementsForScrolling); -- var scrollingContainersDeepToShallow = Array.from(scrollingContainersSet).sort(function (dz1, dz2) { -- return getDepth(dz2) - getDepth(dz1); -- }); -- var _makeScroller = makeScroller(), -- scrollIfNeeded = _makeScroller.scrollIfNeeded, -- resetScrolling = _makeScroller.resetScrolling; -- -- /** -- * @return {boolean} - was any container scrolled -- */ -- function tick() { -- var mousePosition = getPointerPosition(); -- if (!mousePosition || !scrollingContainersDeepToShallow) { -- return false; -- } -- var scrollContainersUnderCursor = scrollingContainersDeepToShallow.filter(function (el) { -- return isPointInsideRect(mousePosition, el.getBoundingClientRect()) || el === document.scrollingElement; -- }); -- for (var i = 0; i < scrollContainersUnderCursor.length; i++) { -- var scrolled = scrollIfNeeded(mousePosition, scrollContainersUnderCursor[i]); -- if (scrolled) { -- return true; -- } -- } -- return false; -- } -- return { -- multiScrollIfNeeded: scrollingContainersSet.size > 0 ? tick : function () { -- return false; -- }, -- destroy: function destroy() { -- return resetScrolling(); -- } -- }; -+ window.addEventListener("mousemove", updateMousePosition); -+ window.addEventListener("touchmove", updateMousePosition); -+ loop(); - } - --// internal utils --function findScrollableParents(element) { -- if (!element) { -- return []; -- } -- var scrollableContainers = []; -- var parent = element; -- while (parent) { -- var _window$getComputedSt = window.getComputedStyle(parent), -- overflow = _window$getComputedSt.overflow; -- if (overflow.split(" ").some(function (o) { -- return o.includes("auto") || o.includes("scroll"); -- })) { -- scrollableContainers.push(parent); -- } -- parent = parent.parentElement; -- } -- return scrollableContainers; --} --function findRelevantScrollContainers(dropZones) { -- var scrollingContainers = new Set(); -- var _iterator = _createForOfIteratorHelper(dropZones), -- _step; -- try { -- for (_iterator.s(); !(_step = _iterator.n()).done;) { -- var dz = _step.value; -- findScrollableParents(dz).forEach(function (container) { -- return scrollingContainers.add(container); -- }); -- } -- // The scrolling element might have overflow visible and still be scrollable -- } catch (err) { -- _iterator.e(err); -- } finally { -- _iterator.f(); -- } -- if (document.scrollingElement.scrollHeight > document.scrollingElement.clientHeight || document.scrollingElement.scrollWidth > document.scrollingElement.clientHeight) { -- scrollingContainers.add(document.scrollingElement); -- } -- return scrollingContainers; -+/** -+ * will stop watching the mouse pointer and won't scroll the window anymore -+ */ -+function disarmWindowScroller() { -+ printDebug(function () { -+ return "disarming window scroller"; -+ }); -+ window.removeEventListener("mousemove", updateMousePosition); -+ window.removeEventListener("touchmove", updateMousePosition); -+ mousePosition = undefined; -+ window.clearTimeout(next); -+ resetScrolling(); - } - - /** -@@ -1061,15 +944,15 @@ function findRelevantScrollContainers(dropZones) { - * Since svelte manages select value internally. - * @see https://github.com/sveltejs/svelte/issues/6717 - * @see https://github.com/isaacHagoel/svelte-dnd-action/issues/306 -- * -- * @param {HTMLElement} el -- * @returns -+ * -+ * @param {HTMLElement} el -+ * @returns - */ - function svelteNodeClone(el) { - var cloned = el.cloneNode(true); - var values = []; - var elIsSelect = el.tagName === "SELECT"; -- var selects = elIsSelect ? [el] : _toConsumableArray(el.querySelectorAll("select")); -+ var selects = elIsSelect ? [el] : _toConsumableArray(el.querySelectorAll('select')); - var _iterator = _createForOfIteratorHelper(selects), - _step; - try { -@@ -1082,62 +965,21 @@ function svelteNodeClone(el) { - } finally { - _iterator.f(); - } -- if (selects.length > 0) { -- var clonedSelects = elIsSelect ? [cloned] : _toConsumableArray(cloned.querySelectorAll("select")); -- for (var i = 0; i < clonedSelects.length; i++) { -- var select = clonedSelects[i]; -- var value = values[i]; -- var optionEl = select.querySelector("option[value=\"".concat(value, "\"")); -- if (optionEl) { -- optionEl.setAttribute("selected", true); -- } -- } -+ if (selects.length <= 0) { -+ return cloned; - } -- var elIsCanvas = el.tagName === "CANVAS"; -- var canvases = elIsCanvas ? [el] : _toConsumableArray(el.querySelectorAll("canvas")); -- if (canvases.length > 0) { -- var clonedCanvases = elIsCanvas ? [cloned] : _toConsumableArray(cloned.querySelectorAll("canvas")); -- for (var _i = 0; _i < clonedCanvases.length; _i++) { -- var canvas = canvases[_i]; -- var clonedCanvas = clonedCanvases[_i]; -- clonedCanvas.width = canvas.width; -- clonedCanvas.height = canvas.height; -- if (canvas.width > 0 && canvas.height > 0) { -- clonedCanvas.getContext("2d").drawImage(canvas, 0, 0); -- } -+ var clonedSelects = elIsSelect ? [cloned] : _toConsumableArray(cloned.querySelectorAll('select')); -+ for (var i = 0; i < clonedSelects.length; i++) { -+ var select = clonedSelects[i]; -+ var value = values[i]; -+ var optionEl = select.querySelector("option[value=\"".concat(value, "\"")); -+ if (optionEl) { -+ optionEl.setAttribute('selected', true); - } - } - return cloned; - } - --/** -- * @type {{USE_COMPUTED_STYLE_INSTEAD_OF_BOUNDING_RECT: string}} -- */ --var FEATURE_FLAG_NAMES = Object.freeze({ -- // This flag exists as a workaround for issue 454 (basically a browser bug) - seems like these rect values take time to update when in grid layout. Setting it to true can cause strange behaviour in the REPL for non-grid zones, see issue 470 -- USE_COMPUTED_STYLE_INSTEAD_OF_BOUNDING_RECT: "USE_COMPUTED_STYLE_INSTEAD_OF_BOUNDING_RECT" --}); --var featureFlagsMap = _defineProperty({}, FEATURE_FLAG_NAMES.USE_COMPUTED_STYLE_INSTEAD_OF_BOUNDING_RECT, false); -- --/** -- * @param {FEATURE_FLAG_NAMES} flagName -- * @param {boolean} flagValue -- */ --function setFeatureFlag(flagName, flagValue) { -- if (!FEATURE_FLAG_NAMES[flagName]) throw new Error("Can't set non existing feature flag ".concat(flagName, "! Supported flags: ").concat(Object.keys(FEATURE_FLAG_NAMES))); -- featureFlagsMap[flagName] = !!flagValue; --} -- --/** -- * -- * @param {FEATURE_FLAG_NAMES} flagName -- * @return {boolean} -- */ --function getFeatureFlag(flagName) { -- if (!FEATURE_FLAG_NAMES[flagName]) throw new Error("Can't get non existing feature flag ".concat(flagName, "! Supported flags: ").concat(Object.keys(FEATURE_FLAG_NAMES))); -- return featureFlagsMap[flagName]; --} -- - var TRANSITION_DURATION_SECONDS = 0.2; - - /** -@@ -1214,10 +1056,8 @@ function morphDraggedElementToBeLike(draggedEl, copyFromEl, currentMouseX, curre - left: (currentMouseX - draggedElRect.left) / draggedElRect.width, - top: (currentMouseY - draggedElRect.top) / draggedElRect.height - }; -- if (!getFeatureFlag(FEATURE_FLAG_NAMES.USE_COMPUTED_STYLE_INSTEAD_OF_BOUNDING_RECT)) { -- draggedEl.style.height = "".concat(newRect.height, "px"); -- draggedEl.style.width = "".concat(newRect.width, "px"); -- } -+ draggedEl.style.height = "".concat(newRect.height, "px"); -+ draggedEl.style.width = "".concat(newRect.width, "px"); - draggedEl.style.left = "".concat(parseFloat(draggedEl.style.left) - relativeDistanceOfMousePointerFromDraggedSides.left * widthChange, "px"); - draggedEl.style.top = "".concat(parseFloat(draggedEl.style.top) - relativeDistanceOfMousePointerFromDraggedSides.top * heightChange, "px"); - } -@@ -1230,9 +1070,7 @@ function morphDraggedElementToBeLike(draggedEl, copyFromEl, currentMouseX, curre - function copyStylesFromTo(copyFromEl, copyToEl) { - var computedStyle = window.getComputedStyle(copyFromEl); - Array.from(computedStyle).filter(function (s) { -- return s.startsWith("background") || s.startsWith("padding") || s.startsWith("font") || s.startsWith("text") || s.startsWith("align") || s.startsWith("justify") || s.startsWith("display") || s.startsWith("flex") || s.startsWith("border") || s === "opacity" || s === "color" || s === "list-style-type" || -- // copying with and height to make up for rect update timing issues in some browsers -- getFeatureFlag(FEATURE_FLAG_NAMES.USE_COMPUTED_STYLE_INSTEAD_OF_BOUNDING_RECT) && (s === "width" || s === "height"); -+ return s.startsWith("background") || s.startsWith("padding") || s.startsWith("font") || s.startsWith("text") || s.startsWith("align") || s.startsWith("justify") || s.startsWith("display") || s.startsWith("flex") || s.startsWith("border") || s === "opacity" || s === "color" || s === "list-style-type"; - }).forEach(function (s) { - return copyToEl.style.setProperty(s, computedStyle.getPropertyValue(s), computedStyle.getPropertyPriority(s)); - }); -@@ -1349,13 +1187,10 @@ function preventShrinking(el) { - - var DEFAULT_DROP_ZONE_TYPE$1 = "--any--"; - var MIN_OBSERVATION_INTERVAL_MS = 100; --var DISABLED_OBSERVATION_INTERVAL_MS = 20; - var MIN_MOVEMENT_BEFORE_DRAG_START_PX = 3; --var DEFAULT_TOUCH_DELAY_MS = 80; - var DEFAULT_DROP_TARGET_STYLE$1 = { - outline: "rgba(255, 255, 102, 0.7) solid 2px" - }; --var ORIGINAL_DRAGGED_ITEM_MARKER_ATTRIBUTE = "data-is-dnd-original-dragged-item"; - var originalDragTarget; - var draggedEl; - var draggedElData; -@@ -1371,9 +1206,6 @@ var finalizingPreviousDrag = false; - var unlockOriginDzMinDimensions; - var isDraggedOutsideOfAnyDz = false; - var scheduledForRemovalAfterDrop = []; --var multiScroller; --var touchDragHoldTimer; --var touchHoldElapsed = false; - - // a map from type to a set of drop-zones - var typeToDropZones$1 = new Map(); -@@ -1408,6 +1240,7 @@ function watchDraggedElement() { - printDebug(function () { - return "watching dragged element"; - }); -+ armWindowScroller(); - var dropZones = typeToDropZones$1.get(draggedElType); - var _iterator = _createForOfIteratorHelper(dropZones), - _step; -@@ -1424,21 +1257,17 @@ function watchDraggedElement() { - _iterator.f(); - } - window.addEventListener(DRAGGED_LEFT_DOCUMENT_EVENT_NAME, handleDrop$1); -- - // it is important that we don't have an interval that is faster than the flip duration because it can cause elements to jump bach and forth -- var setIntervalMs = Math.max.apply(Math, _toConsumableArray(Array.from(dropZones.keys()).map(function (dz) { -+ var observationIntervalMs = Math.max.apply(Math, [MIN_OBSERVATION_INTERVAL_MS].concat(_toConsumableArray(Array.from(dropZones.keys()).map(function (dz) { - return dzToConfig$1.get(dz).dropAnimationDurationMs; -- }))); -- var observationIntervalMs = setIntervalMs === 0 ? DISABLED_OBSERVATION_INTERVAL_MS : Math.max(setIntervalMs, MIN_OBSERVATION_INTERVAL_MS); // if setIntervalMs is 0 it goes to 20, otherwise it is max between it and min observation. -- multiScroller = createMultiScroller(dropZones, function () { -- return currentMousePosition; -- }); -- observe(draggedEl, dropZones, observationIntervalMs * 1.07, multiScroller); -+ })))); -+ observe(draggedEl, dropZones, observationIntervalMs * 1.07); - } - function unWatchDraggedElement() { - printDebug(function () { - return "unwatching dragged element"; - }); -+ disarmWindowScroller(); - var dropZones = typeToDropZones$1.get(draggedElType); - var _iterator2 = _createForOfIteratorHelper(dropZones), - _step2; -@@ -1455,21 +1284,20 @@ function unWatchDraggedElement() { - _iterator2.f(); - } - window.removeEventListener(DRAGGED_LEFT_DOCUMENT_EVENT_NAME, handleDrop$1); -- // ensuring multiScroller is not already destroyed before destroying -- if (multiScroller) { -- multiScroller.destroy(); -- multiScroller = undefined; -- } - unobserve(); - } --function findShadowElementIdx(items) { -+ -+// finds the initial placeholder that is placed there on drag start -+function findShadowPlaceHolderIdx(items) { - return items.findIndex(function (item) { -- return !!item[SHADOW_ITEM_MARKER_PROPERTY_NAME]; -+ return item[ITEM_ID_KEY] === SHADOW_PLACEHOLDER_ITEM_ID; - }); - } --function createShadowElData(draggedElData) { -- var _objectSpread2$1; -- return _objectSpread2(_objectSpread2({}, draggedElData), {}, (_objectSpread2$1 = {}, _defineProperty(_objectSpread2$1, SHADOW_ITEM_MARKER_PROPERTY_NAME, true), _defineProperty(_objectSpread2$1, ITEM_ID_KEY, SHADOW_PLACEHOLDER_ITEM_ID), _objectSpread2$1)); -+function findShadowElementIdx(items) { -+ // checking that the id is not the placeholder's for Dragula like usecases -+ return items.findIndex(function (item) { -+ return !!item[SHADOW_ITEM_MARKER_PROPERTY_NAME] && item[ITEM_ID_KEY] !== SHADOW_PLACEHOLDER_ITEM_ID; -+ }); - } - - /* custom drag-events handlers */ -@@ -1504,8 +1332,20 @@ function handleDraggedEntered(e) { - id: draggedElData[ITEM_ID_KEY], - source: SOURCES.POINTER - }); -+ } else { -+ var shadowPlaceHolderIdx = findShadowPlaceHolderIdx(items); -+ if (shadowPlaceHolderIdx !== -1) { -+ // only happens right after drag start, on the first drag entered event -+ printDebug(function () { -+ return "removing placeholder item from origin dz"; -+ }); -+ items.splice(shadowPlaceHolderIdx, 1); -+ } - } -- var shadowElIdx = e.detail.indexObj.index; -+ var _e$detail$indexObj = e.detail.indexObj, -+ index = _e$detail$indexObj.index, -+ isProximityBased = _e$detail$indexObj.isProximityBased; -+ var shadowElIdx = isProximityBased && index === e.currentTarget.children.length - 1 ? index + 1 : index; - shadowElDropZone = e.currentTarget; - items.splice(shadowElIdx, 0, shadowElData); - dispatchConsiderEvent(e.currentTarget, items, { -@@ -1521,7 +1361,7 @@ function handleDraggedLeft(e) { - return ["dragged left", e.currentTarget, e.detail]; - }); - var _dzToConfig$get2 = dzToConfig$1.get(e.currentTarget), -- originalItems = _dzToConfig$get2.items, -+ items = _dzToConfig$get2.items, - dropFromOthersDisabled = _dzToConfig$get2.dropFromOthersDisabled; - if (dropFromOthersDisabled && e.currentTarget !== originDropZone && e.currentTarget !== shadowElDropZone) { - printDebug(function () { -@@ -1529,12 +1369,8 @@ function handleDraggedLeft(e) { - }); - return; - } -- var items = _toConsumableArray(originalItems); - var shadowElIdx = findShadowElementIdx(items); -- if (shadowElIdx !== -1) { -- items.splice(shadowElIdx, 1); -- } -- var origShadowDz = shadowElDropZone; -+ var shadowItem = items.splice(shadowElIdx, 1)[0]; - shadowElDropZone = undefined; - var _e$detail = e.detail, - type = _e$detail.type, -@@ -1545,9 +1381,8 @@ function handleDraggedLeft(e) { - }); - isDraggedOutsideOfAnyDz = true; - shadowElDropZone = originDropZone; -- // if the last zone it left is the origin dz, we will put it back into items (which we just removed it from) -- var originZoneItems = origShadowDz === originDropZone ? items : _toConsumableArray(dzToConfig$1.get(originDropZone).items); -- originZoneItems.splice(originIndex, 0, shadowElData); -+ var originZoneItems = dzToConfig$1.get(originDropZone).items; -+ originZoneItems.splice(originIndex, 0, shadowItem); - dispatchConsiderEvent(originDropZone, originZoneItems, { - trigger: TRIGGERS.DRAGGED_LEFT_ALL, - id: draggedElData[ITEM_ID_KEY], -@@ -1566,7 +1401,7 @@ function handleDraggedIsOverIndex(e) { - return ["dragged is over index", e.currentTarget, e.detail]; - }); - var _dzToConfig$get3 = dzToConfig$1.get(e.currentTarget), -- originalItems = _dzToConfig$get3.items, -+ items = _dzToConfig$get3.items, - dropFromOthersDisabled = _dzToConfig$get3.dropFromOthersDisabled; - if (dropFromOthersDisabled && e.currentTarget !== originDropZone) { - printDebug(function () { -@@ -1574,13 +1409,10 @@ function handleDraggedIsOverIndex(e) { - }); - return; - } -- var items = _toConsumableArray(originalItems); - isDraggedOutsideOfAnyDz = false; - var index = e.detail.indexObj.index; - var shadowElIdx = findShadowElementIdx(items); -- if (shadowElIdx !== -1) { -- items.splice(shadowElIdx, 1); -- } -+ items.splice(shadowElIdx, 1); - items.splice(index, 0, shadowElData); - dispatchConsiderEvent(e.currentTarget, items, { - trigger: TRIGGERS.DRAGGED_OVER_INDEX, -@@ -1593,9 +1425,12 @@ function handleDraggedIsOverIndex(e) { - function handleMouseMove(e) { - e.preventDefault(); - var c = e.touches ? e.touches[0] : e; -+ var _dzToConfig$get4 = dzToConfig$1.get(originDropZone), -+ constrainAxisX = _dzToConfig$get4.constrainAxisX, -+ constrainAxisY = _dzToConfig$get4.constrainAxisY; - currentMousePosition = { -- x: c.clientX, -- y: c.clientY -+ x: constrainAxisX ? dragStartMousePosition.x : c.clientX, -+ y: constrainAxisY ? dragStartMousePosition.y : c.clientY - }; - draggedEl.style.transform = "translate3d(".concat(currentMousePosition.x - dragStartMousePosition.x, "px, ").concat(currentMousePosition.y - dragStartMousePosition.y, "px, 0)"); - } -@@ -1620,9 +1455,9 @@ function handleDrop$1() { - printDebug(function () { - return ["dropped in dz", shadowElDropZone]; - }); -- var _dzToConfig$get4 = dzToConfig$1.get(shadowElDropZone), -- items = _dzToConfig$get4.items, -- type = _dzToConfig$get4.type; -+ var _dzToConfig$get5 = dzToConfig$1.get(shadowElDropZone), -+ items = _dzToConfig$get5.items, -+ type = _dzToConfig$get5.type; - styleInactiveDropZones(typeToDropZones$1.get(type), function (dz) { - return dzToConfig$1.get(dz).dropTargetStyle; - }, function (dz) { -@@ -1630,11 +1465,7 @@ function handleDrop$1() { - }); - var shadowElIdx = findShadowElementIdx(items); - // the handler might remove the shadow element, ex: dragula like copy on drag -- if (shadowElIdx === -1) { -- if (shadowElDropZone === originDropZone) { -- shadowElIdx = originIndex; -- } -- } -+ if (shadowElIdx === -1) shadowElIdx = originIndex; - items = items.map(function (item) { - return item[SHADOW_ITEM_MARKER_PROPERTY_NAME] ? draggedElData : item; - }); -@@ -1653,29 +1484,21 @@ function handleDrop$1() { - source: SOURCES.POINTER - }); - } -- // In edge cases the dom might have not been updated yet so we can't rely on data list index -- var domShadowEl = Array.from(shadowElDropZone.children).find(function (c) { -- return c.getAttribute(SHADOW_ELEMENT_ATTRIBUTE_NAME); -- }); -- if (domShadowEl) unDecorateShadowElement(domShadowEl); -+ unDecorateShadowElement(shadowElDropZone.children[shadowElIdx]); - cleanupPostDrop(); - } -- if (dzToConfig$1.get(shadowElDropZone).dropAnimationDisabled) { -- finalizeWithinZone(); -- } else { -- animateDraggedToFinalPosition(shadowElIdx, finalizeWithinZone); -- } -+ animateDraggedToFinalPosition(shadowElIdx, finalizeWithinZone); - } - - // helper function for handleDrop - function animateDraggedToFinalPosition(shadowElIdx, callback) { -- var shadowElRect = shadowElIdx > -1 ? getBoundingRectNoTransforms(shadowElDropZone.children[shadowElIdx], false) : getBoundingRectNoTransforms(shadowElDropZone, false); -+ var shadowElRect = getBoundingRectNoTransforms(shadowElDropZone.children[shadowElIdx]); - var newTransform = { - x: shadowElRect.left - parseFloat(draggedEl.style.left), - y: shadowElRect.top - parseFloat(draggedEl.style.top) - }; -- var _dzToConfig$get5 = dzToConfig$1.get(shadowElDropZone), -- dropAnimationDurationMs = _dzToConfig$get5.dropAnimationDurationMs; -+ var _dzToConfig$get6 = dzToConfig$1.get(shadowElDropZone), -+ dropAnimationDurationMs = _dzToConfig$get6.dropAnimationDurationMs; - var transition = "transform ".concat(dropAnimationDurationMs, "ms ease"); - draggedEl.style.transition = draggedEl.style.transition ? draggedEl.style.transition + "," + transition : transition; - draggedEl.style.transform = "translate3d(".concat(newTransform.x, "px, ").concat(newTransform.y, "px, 0)"); -@@ -1693,12 +1516,19 @@ function scheduleDZForRemovalAfterDrop(dz, destroy) { - } - /* cleanup */ - function cleanupPostDrop() { -- // Remove the temporary elements that were kept in the DOM during the drag -- if (draggedEl && draggedEl.remove) { -- draggedEl.remove(); -- } -- if (originalDragTarget && originalDragTarget.remove) { -- originalDragTarget.remove(); -+ draggedEl.remove(); -+ originalDragTarget.remove(); -+ if (scheduledForRemovalAfterDrop.length) { -+ printDebug(function () { -+ return ["will destroy zones that were removed during drag", scheduledForRemovalAfterDrop]; -+ }); -+ scheduledForRemovalAfterDrop.forEach(function (_ref) { -+ var dz = _ref.dz, -+ destroy = _ref.destroy; -+ destroy(); -+ dz.remove(); -+ }); -+ scheduledForRemovalAfterDrop = []; - } - draggedEl = undefined; - originalDragTarget = undefined; -@@ -1714,23 +1544,6 @@ function cleanupPostDrop() { - finalizingPreviousDrag = false; - unlockOriginDzMinDimensions = undefined; - isDraggedOutsideOfAnyDz = false; -- if (touchDragHoldTimer) { -- clearTimeout(touchDragHoldTimer); -- } -- touchDragHoldTimer = undefined; -- touchHoldElapsed = false; -- if (scheduledForRemovalAfterDrop.length) { -- printDebug(function () { -- return ["will destroy zones that were removed during drag", scheduledForRemovalAfterDrop]; -- }); -- scheduledForRemovalAfterDrop.forEach(function (_ref) { -- var dz = _ref.dz, -- destroy = _ref.destroy; -- destroy(); -- dz.remove(); -- }); -- scheduledForRemovalAfterDrop = []; -- } - } - function dndzone$2(node, options) { - var initialized = false; -@@ -1738,15 +1551,15 @@ function dndzone$2(node, options) { - items: undefined, - type: undefined, - flipDurationMs: 0, -+ constrainAxisX: false, -+ constrainAxisY: false, - dragDisabled: false, - morphDisabled: false, - dropFromOthersDisabled: false, - dropTargetStyle: DEFAULT_DROP_TARGET_STYLE$1, - dropTargetClasses: [], - transformDraggedElement: function transformDraggedElement() {}, -- centreDraggedOnCursor: false, -- dropAnimationDisabled: false, -- delayTouchStartMs: 0 -+ centreDraggedOnCursor: false - }; - printDebug(function () { - return ["dndzone good to go options: ".concat(toString(options), ", config: ").concat(toString(config)), { -@@ -1774,52 +1587,16 @@ function dndzone$2(node, options) { - window.removeEventListener("touchmove", handleMouseMoveMaybeDragStart); - window.removeEventListener("mouseup", handleFalseAlarm); - window.removeEventListener("touchend", handleFalseAlarm); -- if (touchDragHoldTimer) { -- clearTimeout(touchDragHoldTimer); -- touchDragHoldTimer = undefined; -- touchHoldElapsed = false; -- } - } -- function handleFalseAlarm(e) { -+ function handleFalseAlarm() { - removeMaybeListeners(); - originalDragTarget = undefined; - dragStartMousePosition = undefined; - currentMousePosition = undefined; -- -- // dragging initiated by touch events prevents onclick from initially firing -- if (e.type === "touchend") { -- var clickEvent = new Event("click", { -- bubbles: true, -- cancelable: true -- }); -- // doing it this way instead of calling .click() because that doesn't work for SVG elements -- e.target.dispatchEvent(clickEvent); -- } - } - function handleMouseMoveMaybeDragStart(e) { -- var isTouch = !!e.touches; -- var c = isTouch ? e.touches[0] : e; -- // If touch drag delay is configured and not elapsed yet, allow scrolling until either -- // the delay elapses (timer will call handleDragStart) or the user moves significantly, -- // in which case we cancel the potential drag and let the interaction be a scroll. -- if (isTouch && config.delayTouchStartMs > 0 && !touchHoldElapsed) { -- currentMousePosition = { -- x: c.clientX, -- y: c.clientY -- }; -- if (Math.abs(currentMousePosition.x - dragStartMousePosition.x) >= MIN_MOVEMENT_BEFORE_DRAG_START_PX || Math.abs(currentMousePosition.y - dragStartMousePosition.y) >= MIN_MOVEMENT_BEFORE_DRAG_START_PX) { -- // User started scrolling, cancel drag attempt. -- if (touchDragHoldTimer) { -- clearTimeout(touchDragHoldTimer); -- touchDragHoldTimer = undefined; -- } -- handleFalseAlarm(e); -- } -- return; // Do not preventDefault so scrolling works. -- } -- -- // legacy / post-delay path – block scrolling and maybe start drag - e.preventDefault(); -+ var c = e.touches ? e.touches[0] : e; - currentMousePosition = { - x: c.clientX, - y: c.clientY -@@ -1850,29 +1627,14 @@ function dndzone$2(node, options) { - }); - return; - } -- var isTouchStart = !!e.touches; -- var useDelay = isTouchStart && config.delayTouchStartMs > 0; -- if (!useDelay) { -- e.preventDefault(); -- } - e.stopPropagation(); -- var c = isTouchStart ? e.touches[0] : e; -+ var c = e.touches ? e.touches[0] : e; - dragStartMousePosition = { - x: c.clientX, - y: c.clientY - }; - currentMousePosition = _objectSpread2({}, dragStartMousePosition); - originalDragTarget = e.currentTarget; -- if (useDelay) { -- touchHoldElapsed = false; -- touchDragHoldTimer = window.setTimeout(function () { -- // If the finger is still down and no false-alarm happened -- if (!originalDragTarget) return; -- touchHoldElapsed = true; -- removeMaybeListeners(); -- handleDragStart(); -- }, config.delayTouchStartMs); -- } - addMaybeListeners(); - } - function handleDragStart() { -@@ -1885,32 +1647,29 @@ function dndzone$2(node, options) { - var currentIdx = elToIdx.get(originalDragTarget); - originIndex = currentIdx; - originDropZone = originalDragTarget.parentElement; -- /** @type {ShadowRoot | HTMLDocument | Element } */ -- var rootNode = originDropZone.closest("dialog") || originDropZone.closest("[popover]") || originDropZone.getRootNode(); -+ /** @type {ShadowRoot | HTMLDocument} */ -+ var rootNode = originDropZone.getRootNode(); - var originDropZoneRoot = rootNode.body || rootNode; -- var originalItems = config.items, -+ var items = config.items, - type = config.type, - centreDraggedOnCursor = config.centreDraggedOnCursor; -- var items = _toConsumableArray(originalItems); -- draggedElData = items[currentIdx]; -+ draggedElData = _objectSpread2({}, items[currentIdx]); - draggedElType = type; -- shadowElData = createShadowElData(draggedElData); -+ shadowElData = _objectSpread2(_objectSpread2({}, draggedElData), {}, _defineProperty({}, SHADOW_ITEM_MARKER_PROPERTY_NAME, true)); -+ // The initial shadow element. We need a different id at first in order to avoid conflicts and timing issues -+ var placeHolderElData = _objectSpread2(_objectSpread2({}, shadowElData), {}, _defineProperty({}, ITEM_ID_KEY, SHADOW_PLACEHOLDER_ITEM_ID)); - - // creating the draggable element - draggedEl = createDraggedElementFrom(originalDragTarget, centreDraggedOnCursor && currentMousePosition); -- originDropZoneRoot.appendChild(draggedEl); - // We will keep the original dom node in the dom because touch events keep firing on it, we want to re-add it after the framework removes it - function keepOriginalElementInDom() { -- if (!originalDragTarget.parentElement) { -- originalDragTarget.setAttribute(ORIGINAL_DRAGGED_ITEM_MARKER_ATTRIBUTE, true); -- originDropZoneRoot.appendChild(originalDragTarget); -- // have to watch before we hide, otherwise Svelte 5 $state gets confused -- watchDraggedElement(); -- hideElement(originalDragTarget); -- // after the removal of the original element we can give the shadow element the original item id so that the host zone can find it and render it correctly if it does lookups by id -- shadowElData[ITEM_ID_KEY] = draggedElData[ITEM_ID_KEY]; -+ if (!draggedEl.parentElement) { -+ originDropZoneRoot.appendChild(draggedEl); - // to prevent the outline from disappearing - draggedEl.focus(); -+ watchDraggedElement(); -+ hideElement(originalDragTarget); -+ originDropZoneRoot.appendChild(originalDragTarget); - } else { - window.requestAnimationFrame(keepOriginalElementInDom); - } -@@ -1925,7 +1684,7 @@ function dndzone$2(node, options) { - }); - - // removing the original element by removing its data entry -- items.splice(currentIdx, 1, shadowElData); -+ items.splice(currentIdx, 1, placeHolderElData); - unlockOriginDzMinDimensions = preventShrinking(originDropZone); - dispatchConsiderEvent(originDropZone, items, { - trigger: TRIGGERS.DRAG_STARTED, -@@ -1955,6 +1714,10 @@ function dndzone$2(node, options) { - dropAnimationDurationMs = _ref2$flipDurationMs === void 0 ? 0 : _ref2$flipDurationMs, - _ref2$type = _ref2.type, - newType = _ref2$type === void 0 ? DEFAULT_DROP_ZONE_TYPE$1 : _ref2$type, -+ _ref2$constrainAxisX = _ref2.constrainAxisX, -+ constrainAxisX = _ref2$constrainAxisX === void 0 ? false : _ref2$constrainAxisX, -+ _ref2$constrainAxisY = _ref2.constrainAxisY, -+ constrainAxisY = _ref2$constrainAxisY === void 0 ? false : _ref2$constrainAxisY, - _ref2$dragDisabled = _ref2.dragDisabled, - dragDisabled = _ref2$dragDisabled === void 0 ? false : _ref2$dragDisabled, - _ref2$morphDisabled = _ref2.morphDisabled, -@@ -1968,29 +1731,20 @@ function dndzone$2(node, options) { - _ref2$transformDragge = _ref2.transformDraggedElement, - transformDraggedElement = _ref2$transformDragge === void 0 ? function () {} : _ref2$transformDragge, - _ref2$centreDraggedOn = _ref2.centreDraggedOnCursor, -- centreDraggedOnCursor = _ref2$centreDraggedOn === void 0 ? false : _ref2$centreDraggedOn, -- _ref2$dropAnimationDi = _ref2.dropAnimationDisabled, -- dropAnimationDisabled = _ref2$dropAnimationDi === void 0 ? false : _ref2$dropAnimationDi, -- _ref2$delayTouchStart = _ref2.delayTouchStart, -- delayTouchStartOpt = _ref2$delayTouchStart === void 0 ? false : _ref2$delayTouchStart; -+ centreDraggedOnCursor = _ref2$centreDraggedOn === void 0 ? false : _ref2$centreDraggedOn; - config.dropAnimationDurationMs = dropAnimationDurationMs; -- var effectiveDelayMs = 0; -- if (delayTouchStartOpt === true) { -- effectiveDelayMs = DEFAULT_TOUCH_DELAY_MS; -- } else if (typeof delayTouchStartOpt === "number" && isFinite(delayTouchStartOpt) && delayTouchStartOpt >= 0) { -- effectiveDelayMs = delayTouchStartOpt; -- } -- config.delayTouchStartMs = effectiveDelayMs; - if (config.type && newType !== config.type) { - unregisterDropZone$1(node, config.type); - } - config.type = newType; -+ registerDropZone$1(node, newType); - config.items = _toConsumableArray(items); -+ config.constrainAxisX = constrainAxisX; -+ config.constrainAxisY = constrainAxisY; - config.dragDisabled = dragDisabled; - config.morphDisabled = morphDisabled; - config.transformDraggedElement = transformDraggedElement; - config.centreDraggedOnCursor = centreDraggedOnCursor; -- config.dropAnimationDisabled = dropAnimationDisabled; - - // realtime update for dropTargetStyle - if (initialized && isWorkingOnPreviousDrag && !finalizingPreviousDrag && (!areObjectsShallowEqual(dropTargetStyle, config.dropTargetStyle) || !areArraysShallowEqualSameOrder(dropTargetClasses, config.dropTargetClasses))) { -@@ -2029,16 +1783,15 @@ function dndzone$2(node, options) { - } - config.dropFromOthersDisabled = dropFromOthersDisabled; - dzToConfig$1.set(node, config); -- registerDropZone$1(node, newType); -- var shadowElIdx = isWorkingOnPreviousDrag ? findShadowElementIdx(config.items) : -1; -+ var shadowElIdx = findShadowElementIdx(config.items); - for (var idx = 0; idx < node.children.length; idx++) { - var draggableEl = node.children[idx]; - styleDraggable(draggableEl, dragDisabled); - if (idx === shadowElIdx) { -+ config.transformDraggedElement(draggedEl, draggedElData, idx); - if (!morphDisabled) { - morphDraggedElementToBeLike(draggedEl, draggableEl, currentMousePosition.x, currentMousePosition.y); - } -- config.transformDraggedElement(draggedEl, draggedElData, idx); - decorateShadowEl(draggableEl); - continue; - } -@@ -2072,7 +1825,7 @@ function dndzone$2(node, options) { - unregisterDropZone$1(node, dzToConfig$1.get(node).type); - dzToConfig$1["delete"](node); - } -- if (isWorkingOnPreviousDrag && !node.closest("[".concat(ORIGINAL_DRAGGED_ITEM_MARKER_ATTRIBUTE, "]"))) { -+ if (isWorkingOnPreviousDrag) { - printDebug(function () { - return "pointer dndzone will be scheduled for destruction"; - }); -@@ -2350,7 +2103,6 @@ function dndzone$1(node, options) { - type: undefined, - dragDisabled: false, - zoneTabIndex: 0, -- zoneItemTabIndex: 0, - dropFromOthersDisabled: false, - dropTargetStyle: DEFAULT_DROP_TARGET_STYLE, - dropTargetClasses: [], -@@ -2480,7 +2232,7 @@ function dndzone$1(node, options) { - var children = Array.from(node.children); - var focusedItemIdx = children.indexOf(draggableEl); - focusedItem = draggableEl; -- focusedItem.tabIndex = config.zoneItemTabIndex; -+ focusedItem.tabIndex = 0; - focusedItemId = items[focusedItemIdx][ITEM_ID_KEY]; - focusedItemLabel = children[focusedItemIdx].getAttribute("aria-label") || ""; - } -@@ -2493,8 +2245,6 @@ function dndzone$1(node, options) { - dragDisabled = _ref2$dragDisabled === void 0 ? false : _ref2$dragDisabled, - _ref2$zoneTabIndex = _ref2.zoneTabIndex, - zoneTabIndex = _ref2$zoneTabIndex === void 0 ? 0 : _ref2$zoneTabIndex, -- _ref2$zoneItemTabInde = _ref2.zoneItemTabIndex, -- zoneItemTabIndex = _ref2$zoneItemTabInde === void 0 ? 0 : _ref2$zoneItemTabInde, - _ref2$dropFromOthersD = _ref2.dropFromOthersDisabled, - dropFromOthersDisabled = _ref2$dropFromOthersD === void 0 ? false : _ref2$dropFromOthersD, - _ref2$dropTargetStyle = _ref2.dropTargetStyle, -@@ -2507,7 +2257,6 @@ function dndzone$1(node, options) { - config.dragDisabled = dragDisabled; - config.dropFromOthersDisabled = dropFromOthersDisabled; - config.zoneTabIndex = zoneTabIndex; -- config.zoneItemTabIndex = zoneItemTabIndex; - config.dropTargetStyle = dropTargetStyle; - config.dropTargetClasses = dropTargetClasses; - config.autoAriaDisabled = autoAriaDisabled; -@@ -2531,7 +2280,7 @@ function dndzone$1(node, options) { - var _loop = function _loop(i) { - var draggableEl = node.children[i]; - allDragTargets.add(draggableEl); -- draggableEl.tabIndex = isDragging ? -1 : config.zoneItemTabIndex; -+ draggableEl.tabIndex = isDragging ? -1 : 0; - if (!autoAriaDisabled) { - draggableEl.setAttribute("role", "listitem"); - } -@@ -2552,7 +2301,7 @@ function dndzone$1(node, options) { - }); - // if it is a nested dropzone, it was re-rendered and we need to refresh our pointer - focusedItem = draggableEl; -- focusedItem.tabIndex = config.zoneItemTabIndex; -+ focusedItem.tabIndex = 0; - // without this the element loses focus if it moves backwards in the list - draggableEl.focus(); - } -@@ -2582,7 +2331,7 @@ function dndzone$1(node, options) { - return handles; - } - --var _excluded = ["items", "flipDurationMs", "type", "dragDisabled", "morphDisabled", "dropFromOthersDisabled", "zoneTabIndex", "zoneItemTabIndex", "dropTargetStyle", "dropTargetClasses", "transformDraggedElement", "autoAriaDisabled", "centreDraggedOnCursor", "delayTouchStart", "dropAnimationDisabled"]; -+var _excluded = ["items", "flipDurationMs", "type", "constrainAxisX", "constrainAxisY", "dragDisabled", "morphDisabled", "dropFromOthersDisabled", "zoneTabIndex", "dropTargetStyle", "dropTargetClasses", "transformDraggedElement", "autoAriaDisabled", "centreDraggedOnCursor"]; - - /** - * A custom action to turn any container to a dnd zone and all of its direct children to draggables -@@ -2594,28 +2343,20 @@ var _excluded = ["items", "flipDurationMs", "type", "dragDisabled", "morphDisabl - * @property {array} items - the list of items that was used to generate the children of the given node (the list used in the #each block - * @property {string} [type] - the type of the dnd zone. children dragged from here can only be dropped in other zones of the same type, default to a base type - * @property {number} [flipDurationMs] - if the list animated using flip (recommended), specifies the flip duration such that everything syncs with it without conflict, defaults to zero -+ * @property {boolean} [constrainAxisX] - Constrain dragging by X axis. Drag will be allowed only by Y axis. -+ * @property {boolean} [constrainAxisY] - Constrain dragging by Y axis. Drag will be allowed only by X axis. - * @property {boolean} [dragDisabled] - * @property {boolean} [morphDisabled] - whether dragged element should morph to zone dimensions - * @property {boolean} [dropFromOthersDisabled] - * @property {number} [zoneTabIndex] - set the tabindex of the list container when not dragging -- * @property {number} [zoneItemTabIndex] - set the tabindex of the list container items when not dragging - * @property {object} [dropTargetStyle] - * @property {string[]} [dropTargetClasses] -- * @property {boolean|number} [delayTouchStart] - On touch devices, wait this long before converting the gesture to a drag. -- * `true` uses the built-in default (80 ms). -- * @property {boolean} [dropAnimationDisabled] - cancels the drop animation to place - * @property {function} [transformDraggedElement] - * @param {HTMLElement} node - the element to enhance - * @param {Options} options - * @return {{update: function, destroy: function}} - */ - function dndzone(node, options) { -- if (shouldIgnoreZone(node)) { -- return { -- update: function update() {}, -- destroy: function destroy() {} -- }; -- } - validateOptions(options); - var pointerZone = dndzone$2(node, options); - var keyboardZone = dndzone$1(node, options); -@@ -2631,33 +2372,22 @@ function dndzone(node, options) { - } - }; - } -- --/** -- * If the user marked something in the ancestry of our node as shadow element, we can ignore it -- * We need the user to mark it for us because svelte updates the action from deep to shallow (but renders top down) -- * @param {HTMLElement} node -- * @return {boolean} -- */ --function shouldIgnoreZone(node) { -- return !!node.closest("[".concat(SHADOW_ELEMENT_HINT_ATTRIBUTE_NAME, "=\"true\"]")); --} - function validateOptions(options) { - /*eslint-disable*/ - var items = options.items; - options.flipDurationMs; - options.type; -+ options.constrainAxisX; -+ options.constrainAxisY; - options.dragDisabled; - options.morphDisabled; - options.dropFromOthersDisabled; -- var zoneTabIndex = options.zoneTabIndex, -- zoneItemTabIndex = options.zoneItemTabIndex; -+ var zoneTabIndex = options.zoneTabIndex; - options.dropTargetStyle; - var dropTargetClasses = options.dropTargetClasses; - options.transformDraggedElement; - options.autoAriaDisabled; - options.centreDraggedOnCursor; -- var delayTouchStart = options.delayTouchStart; -- options.dropAnimationDisabled; - var rest = _objectWithoutProperties(options, _excluded); - /*eslint-enable*/ - if (Object.keys(rest).length > 0) { -@@ -2678,16 +2408,6 @@ function validateOptions(options) { - if (zoneTabIndex && !isInt(zoneTabIndex)) { - throw new Error("zoneTabIndex should be a number but instead it is a ".concat(_typeof(zoneTabIndex), ", ").concat(toString(zoneTabIndex))); - } -- if (zoneItemTabIndex && !isInt(zoneItemTabIndex)) { -- throw new Error("zoneItemTabIndex should be a number but instead it is a ".concat(_typeof(zoneItemTabIndex), ", ").concat(toString(zoneItemTabIndex))); -- } -- if (delayTouchStart !== undefined && delayTouchStart !== false) { -- var validBoolean = delayTouchStart === true; -- var validNumber = typeof delayTouchStart === "number" && isFinite(delayTouchStart) && delayTouchStart >= 0; -- if (!validBoolean && !validNumber) { -- throw new Error("delayTouchStart should be a boolean (true/false) or a non-negative number but instead it is a ".concat(_typeof(delayTouchStart), ", ").concat(toString(delayTouchStart))); -- } -- } - } - function isInt(value) { - return !isNaN(value) && function (x) { -@@ -2695,150 +2415,4 @@ function isInt(value) { - }(parseFloat(value)); - } - --function createStore(initialValue) { -- var _val = initialValue; -- var subs = new Set(); -- return { -- get: function get() { -- return _val; -- }, -- set: function set(newVal) { -- _val = newVal; -- Array.from(subs).forEach(function (cb) { -- return cb(_val); -- }); -- }, -- subscribe: function subscribe(cb) { -- subs.add(cb); -- cb(_val); -- }, -- unsubscribe: function unsubscribe(cb) { -- subs["delete"](cb); -- } -- }; --} -- --var isItemsDragDisabled = createStore(true); --var userDragDisabled = createStore(false); --function getAddedOptions() { -- return { -- dragDisabled: userDragDisabled.get() || isItemsDragDisabled.get(), -- zoneItemTabIndex: -1 -- }; --} -- --/** -- * This is an action that wraps around the dndzone action to make it easy to work with drag handles -- * When using this you must also use the 'dragHandle' action (see below) on an element inside each item within the zone -- * Credit for the idea and initial implementation goes to @gleuch (Greg Leuch) and @geovie (Georg Vienna) -- * -- * @param {HTMLElement} node -- * @param options - will be passed down to the dndzone -- * @return {{update: (newOptions: Object) => {}, destroy: () => {}}} -- */ --function dragHandleZone(node, options) { -- var _options$dragDisabled; -- // Initialise stores from initial options -- userDragDisabled.set((_options$dragDisabled = options === null || options === void 0 ? void 0 : options.dragDisabled) !== null && _options$dragDisabled !== void 0 ? _options$dragDisabled : false); -- var currentOptions = options; -- var zone = dndzone(node, _objectSpread2(_objectSpread2({}, currentOptions), getAddedOptions())); -- function updateZone() { -- zone.update(_objectSpread2(_objectSpread2({}, currentOptions), getAddedOptions())); -- } -- -- // Subscribe to internal store so finishing a drag updates the zone -- isItemsDragDisabled.subscribe(updateZone); -- -- // We don't need to subscribe to userDragDisabled here because updates to -- // it always come through the `update` lifecycle and will call `updateZone` -- // anyway. -- -- function consider(e) { -- var _e$detail$info = e.detail.info, -- source = _e$detail$info.source, -- trigger = _e$detail$info.trigger; -- // Ensure dragging is stopped on drag finish via keyboard -- if (source === SOURCES.KEYBOARD && trigger === TRIGGERS.DRAG_STOPPED) { -- isItemsDragDisabled.set(true); -- } -- } -- function finalize(e) { -- var source = e.detail.info.source; -- // Ensure dragging is stopped on drag finish via pointer (mouse, touch) -- if (source === SOURCES.POINTER) { -- isItemsDragDisabled.set(true); -- } -- } -- node.addEventListener("consider", consider); -- node.addEventListener("finalize", finalize); -- return { -- update: function update(newOptions) { -- var _currentOptions$dragD, _currentOptions; -- currentOptions = newOptions; -- // keep store in sync with external prop -- userDragDisabled.set((_currentOptions$dragD = (_currentOptions = currentOptions) === null || _currentOptions === void 0 ? void 0 : _currentOptions.dragDisabled) !== null && _currentOptions$dragD !== void 0 ? _currentOptions$dragD : false); -- updateZone(); -- }, -- destroy: function destroy() { -- node.removeEventListener("consider", consider); -- node.removeEventListener("finalize", finalize); -- isItemsDragDisabled.unsubscribe(updateZone); -- } -- }; --} -- --/** -- * This should be used to mark drag handles inside items that belong to a 'dragHandleZone' (see above) -- * @param {HTMLElement} handle -- * @return {{update: *, destroy: *}} -- */ --function dragHandle(handle) { -- handle.setAttribute("role", "button"); -- function startDrag(e) { -- // preventing default to prevent lag on touch devices (because of the browser checking for screen scrolling) -- e.preventDefault(); -- isItemsDragDisabled.set(false); -- -- // Reset the startDrag/isItemsDragDisabled if the user releases the mouse/touch without initiating a drag -- window.addEventListener("mouseup", resetStartDrag); -- window.addEventListener("touchend", resetStartDrag); -- } -- function handleKeyDown(e) { -- if (e.key === "Enter" || e.key === " ") isItemsDragDisabled.set(false); -- } -- function resetStartDrag() { -- isItemsDragDisabled.set(true); -- window.removeEventListener("mouseup", resetStartDrag); -- window.removeEventListener("touchend", resetStartDrag); -- } -- var recomputeHandleState = function recomputeHandleState() { -- var userDisabled = userDragDisabled.get(); -- var internalDisabled = isItemsDragDisabled.get(); -- if (userDisabled) { -- handle.tabIndex = -1; -- handle.style.cursor = ""; // default cursor -- } else { -- handle.tabIndex = internalDisabled ? 0 : -1; -- handle.style.cursor = internalDisabled ? "grab" : "grabbing"; -- } -- }; -- -- // Subscribe to both stores -- userDragDisabled.subscribe(recomputeHandleState); -- isItemsDragDisabled.subscribe(recomputeHandleState); -- handle.addEventListener("mousedown", startDrag); -- handle.addEventListener("touchstart", startDrag); -- handle.addEventListener("keydown", handleKeyDown); -- return { -- update: function update() {}, -- destroy: function destroy() { -- handle.removeEventListener("mousedown", startDrag); -- handle.removeEventListener("touchstart", startDrag); -- handle.removeEventListener("keydown", handleKeyDown); -- userDragDisabled.unsubscribe(recomputeHandleState); -- isItemsDragDisabled.unsubscribe(recomputeHandleState); -- } -- }; --} -- --export { DRAGGED_ELEMENT_ID, FEATURE_FLAG_NAMES, SHADOW_ITEM_MARKER_PROPERTY_NAME, SHADOW_PLACEHOLDER_ITEM_ID, SOURCES, TRIGGERS, alertToScreenReader, dndzone, dragHandle, dragHandleZone, overrideItemIdKeyNameBeforeInitialisingDndZones, setDebugMode, setFeatureFlag }; -+export { DRAGGED_ELEMENT_ID, SHADOW_ITEM_MARKER_PROPERTY_NAME, SHADOW_PLACEHOLDER_ITEM_ID, SOURCES, TRIGGERS, alertToScreenReader, dndzone, overrideItemIdKeyNameBeforeInitialisingDndZones, setDebugMode }; diff --git a/scripts/generate-component-typedefs.ts b/scripts/generate-component-typedefs.ts deleted file mode 100644 index 999c7309..00000000 --- a/scripts/generate-component-typedefs.ts +++ /dev/null @@ -1,180 +0,0 @@ -// scripts/generate-components-typedefs.ts -// Generates src/components.d.ts with global component typings. -// Run with: bun run ./scripts/generate-components-typedefs.ts -// (If you use --watch, Bun will rerun this whenever files change.) - -import { promises as fs } from 'node:fs'; -import * as path from 'node:path'; - -const SRC_DIR = path.resolve(process.cwd(), 'src'); -const COMPONENTS_DIR = path.join(SRC_DIR, 'components'); -const OUTPUT_PATH = path.join(SRC_DIR, 'components.d.ts'); - -type FileEntry = { - name: string; // PascalCase name (global const) - importPath: string; // "./components/....svelte" (posix) - absPath: string; // absolute path -}; - -export async function generateComponentsTypedefs() { - // Ensure components dir exists - const exists = await dirExists(COMPONENTS_DIR); - if (!exists) { - console.error(`[generate-components-typedefs] Directory not found: ${COMPONENTS_DIR}`); - process.exit(1); - } - - const svelteFiles = await collectSvelteFiles(COMPONENTS_DIR); - - const entries: FileEntry[] = svelteFiles.map((abs) => { - // Create a posix-style relative path from src to the file, prefixed with "./" - const relFromSrc = path.posix.join( - ...path.relative(SRC_DIR, abs).split(path.sep) // convert to posix segments - ); - const importPath = `./${relFromSrc}`; - - const fileBase = path.basename(abs, '.svelte'); - const name = toPascalCase(fileBase); - - return { name, importPath, absPath: abs }; - }); - - // Deduplicate by name (warn if duplicates) - const deduped = new Map(); - for (const e of entries) { - if (deduped.has(e.name)) { - const other = deduped.get(e.name)!; - console.warn( - `[generate-components-typedefs] Duplicate component name "${e.name}"\n` + - ` keeping: ${other.absPath}\n ignoring: ${e.absPath}` - ); - continue; - } - deduped.set(e.name, e); - } - - const sorted = [...deduped.values()].sort((a, b) => a.name.localeCompare(b.name)); - - const header = `// generated by scripts/generate-component-typedefs.ts -// It's suggested you commit this file into source control -`; - - const body = - 'declare global {\n' + - sorted - .map((e) => ` const ${e.name}: typeof import("${e.importPath}")["default"]`) - .join('\n') + - '\n}\n\nexport {}\n'; - - const content = header + body; - - await writeIfChanged(OUTPUT_PATH, content); - - console.info( - `[generate-components-typedefs] Wrote ${sorted.length} component${sorted.length === 1 ? '' : 's'} to ${path.relative( - process.cwd(), - OUTPUT_PATH - )}` - ); -} - -async function collectSvelteFiles(dir: string): Promise { - const out: string[] = []; - await walk(dir, out); - // Filter *.svelte (ignore hidden files/dirs by default) - return ( - out - .filter((p) => p.toLowerCase().endsWith('.svelte')) - // Optional: ignore story/test files if you use them - .filter((p) => !/[.-](test|spec|stories)\.svelte$/i.test(p)) - ); -} - -async function walk(dir: string, out: string[]) { - const entries = await fs.readdir(dir, { withFileTypes: true }); - for (const entry of entries) { - if (entry.name.startsWith('.')) continue; // skip hidden - const full = path.join(dir, entry.name); - if (entry.isDirectory()) { - await walk(full, out); - } else if (entry.isFile()) { - out.push(full); - } - } -} - -function toPascalCase(input: string): string { - // Handle common separators and camelCase already - // Examples: - // "ability-bar" -> "AbilityBar" - // "AbilityBar" -> "AbilityBar" - // "ability_bar" -> "AbilityBar" - // "ability.bar" -> "AbilityBar" - // "ability bar" -> "AbilityBar" - const parts = input - .replace(/([a-z0-9])([A-Z])/g, '$1 $2') - .split(/[\s._-]+/g) - .filter(Boolean); - - return parts.map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join(''); -} - -async function dirExists(dir: string): Promise { - try { - const st = await fs.stat(dir); - return st.isDirectory(); - } catch { - return false; - } -} - -async function writeIfChanged(filePath: string, next: string) { - try { - const prev = await fs.readFile(filePath, 'utf8'); - if (prev === next) return; // no change - } catch { - // file doesn't exist—proceed to write - } - await fs.mkdir(path.dirname(filePath), { recursive: true }); - await fs.writeFile(filePath, next, 'utf8'); -} - -export function componentsTypedefsPlugin() { - const globs = ['src/components/**/*.svelte']; - const debounce = (fn: () => void, ms: number) => { - let t: any; - return () => { - clearTimeout(t); - t = setTimeout(fn, ms); - }; - }; - const run = debounce(() => { - generateComponentsTypedefs().catch((e) => console.error('[typedefs] generation failed:', e)); - }, 120); - - return { - name: 'components-typedefs', - apply: 'serve', - configureServer(server: any) { - // initial run - run(); - // watch for add/change/remove of svelte components - server.watcher.add(globs); - server.watcher.on('add', run); - server.watcher.on('change', run); - server.watcher.on('unlink', run); - }, - // Optional: also generate before prod builds - async buildStart() { - await generateComponentsTypedefs(); - } - }; -} - -// Keep the CLI entry for manual runs: -if (import.meta.main) { - generateComponentsTypedefs().catch((err) => { - console.error('[generate-components-typedefs] Error:', err); - process.exit(1); - }); -} diff --git a/scripts/icon.js b/scripts/icon.js deleted file mode 100644 index b1e64b7d..00000000 --- a/scripts/icon.js +++ /dev/null @@ -1,139 +0,0 @@ -import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from 'node:fs'; - -const [, , path, generateTypescript] = process.argv; - -if (!path) { - console.info("Please provide a path to the folder containing .svg's"); - process.exit(1); -} - -const projectRoot = process.cwd(); -const folder = `${projectRoot}/${path}`; - -if (!existsSync(folder)) { - mkdirSync(folder); - console.info('Folder created:', folder); -} - -const svgs = readdirSync(folder) - .filter((file) => file.endsWith('.svg')) - .map((file) => `${folder}/${file}`); - -console.info( - `Scanned and found ${svgs.length} icons: (${svgs - .map((name) => name.split('/').pop()) - .join(', ')})` -); - -if (svgs.length === 0) { - console.info('Exiting...'); - process.exit(1); -} - -const svgObject = {}; - -for (const svg of svgs) { - const name = svg.split('/').pop().replace('.svg', ''); - const contents = readFileSync(svg).toString(); - const viewBoxRegex = /viewBox="([^"]*)"/; - const viewBoxMatch = contents.match(viewBoxRegex); - const viewBox = viewBoxMatch ? viewBoxMatch[1] : ''; - let [, , viewBoxWidth, viewBoxHeight] = viewBox.split(' '); - - const widthRegex = /width="([^"]*)"/; - const widthMatch = contents.match(widthRegex); - if (!viewBoxWidth && widthMatch) viewBoxWidth = widthMatch[1]; - - const heightRegex = /height="([^"]*)"/; - const heightMatch = contents.match(heightRegex); - if (!viewBoxHeight && heightMatch) viewBoxHeight = heightMatch[1]; - - const stripSvgTag = contents.replace(//, '').replace('', ''); - - const svgElementRegex = /<([^>]*)>/g; - - const svgElements = []; - - // Iterate through each match of SVG elements - let match, - i = 0, - fills = [], - strokes = []; - while ((match = svgElementRegex.exec(stripSvgTag)) !== null) { - let elementMatch = match[0]; - const fillRegex = /fill="([^"]*)"/; - const strokeRegex = /stroke="([^"]*)"/; - const styleRegex = /style="([^"]*)"/; - - const fillMatch = match[0].match(fillRegex); - const strokeMatch = match[0].match(strokeRegex); - const styleMatch = elementMatch.match(styleRegex); - - const styles = []; - - styles.push(`fill: var(--${name}-fill-color-${i}, currentColor);`); - if (fillMatch) { - fills.push(fillMatch[1]); - elementMatch = elementMatch.replace(fillRegex, ''); - } else { - fills.push('#000'); - } - - if (strokeMatch) { - strokes.push(strokeMatch[1]); - styles.push(`stroke: var(--${name}-stroke-color-${i}, transparent);`); - elementMatch = elementMatch.replace(strokeRegex, ''); - } else { - strokes.push(null); - } - - if (styleMatch) { - // If there's already a style attribute, append the new styles to it - // const existingStyles = styleMatch[1]; - elementMatch = elementMatch.replace(styleRegex, `style="${styles.join(' ')}"`); - } else { - // If there's no style attribute, add one - elementMatch = elementMatch.replace(/<([^>]+)>/, `<$1 style="${styles.join(' ')}"/>`); - } - - svgElements.push(elementMatch); - i++; - } - - let widthRatio = 1; - let heightRatio = 1; - - if (viewBoxWidth && viewBoxHeight) { - const wide = viewBoxWidth > viewBoxHeight; - if (wide) { - widthRatio = viewBoxWidth / viewBoxHeight; - } else { - heightRatio = viewBoxHeight / viewBoxWidth; - } - } - - svgObject[name] = { - elements: svgElements, - fills, - strokes, - viewBoxWidth: viewBoxWidth ? parseFloat(viewBoxWidth, 10) : null, - viewBoxHeight: viewBoxHeight ? parseFloat(viewBoxHeight, 10) : null, - widthRatio, - heightRatio - }; -} - -const jsonFile = `${folder}/icons.json`; -writeFileSync(jsonFile, `${JSON.stringify(svgObject, null, 2)}\n`); -console.info('File generated:', jsonFile); - -if (generateTypescript && generateTypescript === '--generate-types') { - const tsFile = `${folder}/types.ts`; - writeFileSync( - tsFile, - `export type IconName = ${Object.keys(svgObject) - .map((name) => `'${name}'`) - .join(' | ')};\n` - ); - console.info('File generated:', tsFile); -} diff --git a/src/Iconice.d.ts b/src/Iconice.d.ts deleted file mode 100644 index 5c1f3625..00000000 --- a/src/Iconice.d.ts +++ /dev/null @@ -1 +0,0 @@ -export type IconName = '2h' | 'checkmark' | 'cross' | 'dark' | 'down' | 'info' | 'error' | 'left' | 'light' | 'logo-apeegg-simple' | 'logo-apeegg' | 'menu' | 'right' | 'spinner-circle' | 'success' | 'spinner-inner' | 'warning' | 'up' | 'stun' | '1h' | '1h1h' | 'spin' | 'isBlocking' | 'punch' | 'slash' | 'stab' | 'block' | 'cheese' | 'bite' | 'slam' | 'shieldBash' | 'bowshot' | 'lacerate' | 'kick' | 'whirlwind' | 'fillArmor' | 'claw' | 'demoShout' | 'damage' | 'victory' | 'defeat' | 'maxArmor' | 'maxHealth' | 'coin' | 'magicChance' | 'dice' | 'dodgeChance' | 'criticalChance' | 'criticalDamage' | 'bleeding' | 'concussed' | 'exposed' | 'stunned' | 'vulnerable' | 'wounded' | 'blockChance'; \ No newline at end of file diff --git a/src/app.css b/src/app.css deleted file mode 100644 index 9663b18b..00000000 --- a/src/app.css +++ /dev/null @@ -1,79 +0,0 @@ -@import 'tailwindcss'; -@custom-variant dark (&:where(.dark, .dark *)); - -@layer base { - #sticky-bottom > div { - display: contents; - } - html { - font-family: 'Fira Sans Condensed', 'Trebuchet MS', 'sans-serif'; - @apply overflow-x-hidden overflow-y-scroll; - } - body { - @apply text-gray-800 dark:text-white; - } - .alfa-slab-one { - @apply font-['Alfa_Slab_One']; - } - .cinzel { - @apply font-['Cinzel']; - } - .bree-serif { - @apply font-['Bree_Serif']; - } - .fat-number { - @apply drop-shadow-[0_1.2px_1.2px_rgba(0,0,0,0.8)] [-webkit-text-stroke:_0.5px_black]; - } - h1, - h2, - h3, - h4, - h5, - h6 { - @apply font-['Cinzel']; - } - - /* width */ - ::-webkit-scrollbar { - @apply w-2.5; - } - - /* Track */ - ::-webkit-scrollbar-track { - @apply bg-stone-300; - } - - /* Handle */ - ::-webkit-scrollbar-thumb { - @apply bg-stone-500; - } - - /* Handle on hover */ - ::-webkit-scrollbar-thumb:hover { - @apply bg-stone-500/90; - } -} - -@layer utilities { - .glass { - @apply rounded-md border border-white/50 bg-white/50 shadow-lg backdrop-blur-sm; - } - h1 { - @apply text-2xl font-medium sm:text-4xl sm:tracking-[-0.0625rem] md:text-5xl md:tracking-[-0.0625rem]; - } - h2 { - @apply text-xl font-medium sm:text-3xl sm:tracking-normal md:text-4xl md:tracking-[-0.0625rem]; - } - h3 { - @apply text-lg font-medium sm:text-2xl md:text-3xl; - } - h4 { - @apply text-base font-medium sm:text-lg md:text-2xl; - } - h5 { - @apply text-sm font-medium sm:text-base md:text-lg; - } - h6 { - @apply text-xs font-medium sm:text-sm md:text-base; - } -} diff --git a/src/app.html b/src/app.html deleted file mode 100644 index 3ec40512..00000000 --- a/src/app.html +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - Battle Brawlers - %sveltekit.head% - - - -
%sveltekit.body%
- - - \ No newline at end of file diff --git a/src/app.svelte.ts b/src/app.svelte.ts deleted file mode 100644 index d7ec3b54..00000000 --- a/src/app.svelte.ts +++ /dev/null @@ -1,166 +0,0 @@ -import type { Combat } from '@/types/combat'; -import type { CharacterRef } from '@/types/character'; -import type { AsyncAwaitWebsocket } from 'async-await-websockets'; -import app from '@/app.svelte'; -import type { EquipmentRef } from '@/types/equipment'; -import type { Tooltip } from '@/ts/use'; -import type { Team } from '@/types/team'; -import type { Dialog } from '@/ts/dialog'; -import type { DynamicObject } from '@/types/common'; -import loadLocalStorage from '@/ts/loadLocalStorage'; -import { browser } from '$app/environment'; -import mediaQuery from '@/ts/mediaQuery'; - -const audioModules = import.meta.glob('/static/audio/**/*.*', { eager: true }); -export const AUDIO: Record = {}; - -for (const [path, module] of Object.entries(audioModules)) { - const filename = - path - .split('/') - .pop() - ?.replace(/\.(wav|mp3)$/, '') || ''; - AUDIO[filename] = (module as { default: string }).default; // correct production URL -} - -export const SETTINGS_DEFAULT_VOLUME = { - master: 0.5, - ambient: 0.25, - sfx: 0.5, - combat: 1 -}; - -export const INITIAL_COMBAT = { - teamsStartState: [], - teamsEndState: [], - events: [], - duration: 0, - winningTeam: undefined, - fightId: undefined, - audio: [] -}; - -const INITIAL_CHARACTERS: Required[] = []; -// const INITIAL_INVENTORY = [ -// EQUIPMENT('sword'), -// EQUIPMENT('sword'), -// EQUIPMENT('dagger'), -// EQUIPMENT('dagger'), -// EQUIPMENT('shield'), -// EQUIPMENT('greatSword'), -// EQUIPMENT('leatherBoots') -// ]; -const INITIAL_INVENTORY: EquipmentRef[] = []; -const INITIAL_COINS = 400; // One silver - -export default new (class { - combat: Combat = $state(INITIAL_COMBAT); - liveTeams: Team[] = $state([]); - elapsedMilliseconds: number = $state(0); - - serverTimestampSnapshot: number = $state(0); - syncPerformanceNow: number = $state(0); - serverTimestamp: number = $state(0); - - experience: number = $state(0); - coins: number = $state(INITIAL_COINS); - accountRewards: number = $state(1); - bossHighscore: number = $state(0); - - characters: Required[] = $state(INITIAL_CHARACTERS); - inventory: EquipmentRef[] = $state(INITIAL_INVENTORY); - socket = $state() as AsyncAwaitWebsocket; - token: string | undefined = $state(); - selectedBrawlers: string[] = $state([]); - maxBrawlers: number = $state(0); - - tooltip?: Tooltip = $state(); - dialog?: Dialog = $state(); - showAccountProgression: boolean = $state(false); - notifications: string[] = $state([]); - - gameKeyboardDisabled: boolean = $state(false); - keys: DynamicObject = $state({}); - overlay: string = $state(''); - settings: DynamicObject = $state( - loadLocalStorage({ - volume: SETTINGS_DEFAULT_VOLUME, - loginPageMode: 0, - openProperties: {}, - debugOpen: false, - showDetailedCharacterView: false - }) - ); - - audio: any = $state(AUDIO); - - mqs = mediaQuery({ - desktop: '(min-width: 1200px)', - tablet: '(min-width: 768px) and (max-width: 1199px)', - smartphone: '(max-width: 767px)', - landscape: '(orientation: landscape)', - portrait: '(orientation: portrait)', - hoverable: '(hover: hover)' - }); - - constructor() { - $effect.root(() => { - $effect(() => { - const settings = $state.snapshot(this.settings); - - Object.entries(settings).forEach( - ([key, value]) => browser && window.localStorage.setItem(key, JSON.stringify(value)) - ); - }); - $effect(() => { - const inventory = $state.snapshot(this.inventory); // Hack to trigger reruns - const characters = $state.snapshot(this.characters); // Hack to trigger reruns - const experience = $state.snapshot(this.experience); // Hack to trigger reruns - const coins = $state.snapshot(this.coins); // Hack to trigger reruns - const accountRewards = $state.snapshot(this.accountRewards); // Hack to trigger reruns - // console.info(app.syncPerformanceNow); - const saveDebounce = setTimeout(() => { - if (app.socket && app.token) { - (async () => { - const res = await app.socket.sendAsync('store-game-state', { - token: app.token, - inventory, - characters, - experience, - coins, - accountRewards - }); - - app.serverTimestampSnapshot = res; - app.syncPerformanceNow = performance.now(); - console.info('Game state saved'); - })(); - } - }, 1000); - - return () => clearTimeout(saveDebounce); - }); - }); - } - - dump() { - function flatSnapshot(o: any) { - const out: any = {}; - let p = o; - while ((p = Object.getPrototypeOf(p)) && p !== Object.prototype) { - for (const n of Object.getOwnPropertyNames(p)) { - const d = Object.getOwnPropertyDescriptor(p, n); - if (d?.get && !(n in out)) { - try { - out[n] = o[n]; - } catch { - console.error('Failed to get property', n); - } - } - } - } - return out; - } - return flatSnapshot(this); - } -})(); diff --git a/src/components.d.ts b/src/components.d.ts deleted file mode 100644 index 228d8b1b..00000000 --- a/src/components.d.ts +++ /dev/null @@ -1,77 +0,0 @@ -// generated by scripts/generate-component-typedefs.ts -// It's suggested you commit this file into source control -declare global { - const AbilityBar: typeof import("./components/character/AbilityBar.svelte")["default"] - const AbilityIcon: typeof import("./components/character/AbilityIcon.svelte")["default"] - const AbilityInventory: typeof import("./components/character/AbilityInventory.svelte")["default"] - const AbilitySelection: typeof import("./components/character/AbilitySelection.svelte")["default"] - const Accordion: typeof import("./components/Accordion.svelte")["default"] - const AccountProgression: typeof import("./components/global/AccountProgression.svelte")["default"] - const Armory: typeof import("./components/Armory.svelte")["default"] - const Async: typeof import("./components/Async.svelte")["default"] - const Authorization: typeof import("./components/Authorization.svelte")["default"] - const Bar: typeof import("./components/ui/Bar.svelte")["default"] - const BasicConfirmation: typeof import("./components/dialog/BasicConfirmation.svelte")["default"] - const Button: typeof import("./components/form/Button.svelte")["default"] - const CharacterAvatar: typeof import("./components/character/CharacterAvatar.svelte")["default"] - const CharacterEquipment: typeof import("./components/character/CharacterEquipment.svelte")["default"] - const CharacterSelection: typeof import("./components/combat/CharacterSelection.svelte")["default"] - const Checkbox: typeof import("./components/form/Checkbox.svelte")["default"] - const Clickable: typeof import("./components/buttons/Clickable.svelte")["default"] - const ClientClock: typeof import("./components/global/ClientClock.svelte")["default"] - const Close: typeof import("./components/buttons/Close.svelte")["default"] - const CodeOfConduct: typeof import("./components/overlays/CodeOfConduct.svelte")["default"] - const Coin: typeof import("./components/Coin.svelte")["default"] - const Coins: typeof import("./components/Coins.svelte")["default"] - const Combat: typeof import("./components/overlays/Combat.svelte")["default"] - const CombatantAbilityBar: typeof import("./components/combat/CombatantAbilityBar.svelte")["default"] - const CombatantAudioPlayer: typeof import("./components/combat/CombatantAudioPlayer.svelte")["default"] - const CombatantCard: typeof import("./components/combat/CombatantCard.svelte")["default"] - const CombatantImage: typeof import("./components/combat/CombatantImage.svelte")["default"] - const CombatArena: typeof import("./components/combat/CombatArena.svelte")["default"] - const CombatAudioPlayer: typeof import("./components/combat/CombatAudioPlayer.svelte")["default"] - const ConnectSocket: typeof import("./components/global/ConnectSocket.svelte")["default"] - const CoreStats: typeof import("./components/character/CoreStats.svelte")["default"] - const Debug: typeof import("./components/Debug.svelte")["default"] - const DebugAppData: typeof import("./components/DebugAppData.svelte")["default"] - const DevBar: typeof import("./components/DevBar.svelte")["default"] - const Dialog: typeof import("./components/global/Dialog.svelte")["default"] - const Dropdown: typeof import("./components/form/Dropdown.svelte")["default"] - const EquipmentFilter: typeof import("./components/EquipmentFilter.svelte")["default"] - const EquipmentLink: typeof import("./components/EquipmentLink.svelte")["default"] - const ForgotPassword: typeof import("./components/ForgotPassword.svelte")["default"] - const Frame: typeof import("./components/Frame.svelte")["default"] - const GameAudio: typeof import("./components/GameAudio.svelte")["default"] - const GameMenu: typeof import("./components/overlays/GameMenu.svelte")["default"] - const GoBack: typeof import("./components/buttons/GoBack.svelte")["default"] - const Headline: typeof import("./components/Headline.svelte")["default"] - const HealthBar: typeof import("./components/combat/HealthBar.svelte")["default"] - const Hr: typeof import("./components/Hr.svelte")["default"] - const Icon: typeof import("./components/ui/Icon.svelte")["default"] - const InCombat: typeof import("./components/global/InCombat.svelte")["default"] - const Input: typeof import("./components/form/Input.svelte")["default"] - const Keystrokes: typeof import("./components/global/Keystrokes.svelte")["default"] - const Loader: typeof import("./components/Loader.svelte")["default"] - const Login: typeof import("./components/Login.svelte")["default"] - const Logo: typeof import("./components/Logo.svelte")["default"] - const Logout: typeof import("./components/buttons/Logout.svelte")["default"] - const MyLudus: typeof import("./components/MyLudus.svelte")["default"] - const Notifications: typeof import("./components/global/Notifications.svelte")["default"] - const Overlay: typeof import("./components/global/Overlay.svelte")["default"] - const Pill: typeof import("./components/ui/Pill.svelte")["default"] - const RefillHealthTimer: typeof import("./components/RefillHealthTimer.svelte")["default"] - const Register: typeof import("./components/Register.svelte")["default"] - const ReleaseNotes: typeof import("./components/overlays/ReleaseNotes.svelte")["default"] - const ResultAnnouncement: typeof import("./components/combat/ResultAnnouncement.svelte")["default"] - const Spinner: typeof import("./components/ui/Spinner.svelte")["default"] - const Stats: typeof import("./components/character/Stats.svelte")["default"] - const TeamBadge: typeof import("./components/combat/TeamBadge.svelte")["default"] - const Tooltip: typeof import("./components/ui/Tooltip.svelte")["default"] - const TooltipAbility: typeof import("./components/tooltips/TooltipAbility.svelte")["default"] - const TooltipEquipment: typeof import("./components/tooltips/TooltipEquipment.svelte")["default"] - const Topbar: typeof import("./components/Topbar.svelte")["default"] - const Unauthorized: typeof import("./components/Unauthorized.svelte")["default"] - const VictoryOrLoss: typeof import("./components/combat/VictoryOrLoss.svelte")["default"] -} - -export {} diff --git a/src/components/Accordion.svelte b/src/components/Accordion.svelte deleted file mode 100644 index f39fcf7d..00000000 --- a/src/components/Accordion.svelte +++ /dev/null @@ -1,21 +0,0 @@ - - -
-
- {@render children()} -
-
diff --git a/src/components/Armory.svelte b/src/components/Armory.svelte deleted file mode 100644 index 9e9b673d..00000000 --- a/src/components/Armory.svelte +++ /dev/null @@ -1,49 +0,0 @@ - - -
- - {#each items as item, i (item.uuid)} - {@const equipment = EQUIPMENT(item, true)} - - - {#if type === 'equipment'} - - {:else} - - {/if} - - {/each} - -
diff --git a/src/components/Async.svelte b/src/components/Async.svelte deleted file mode 100644 index 115b5410..00000000 --- a/src/components/Async.svelte +++ /dev/null @@ -1,19 +0,0 @@ - - -{#await happy ? import(`./${making}/${vite}/${happy}.svelte`) : import(`./${making}/${vite}.svelte`) then { default: component }} - -{:catch} - - Error loading.
Restart your game to fix it. - - (We're aware of this issue and hope for
a fix in the future. It's framework related.) -
- 🤷‍♂️ -
-{/await} diff --git a/src/components/Authorization.svelte b/src/components/Authorization.svelte deleted file mode 100644 index cc47b15d..00000000 --- a/src/components/Authorization.svelte +++ /dev/null @@ -1,56 +0,0 @@ - - -{#if !connected} - Connecting to server -{:else if authorized} - {@render children?.()} -{:else if !app.token} - -{/if} diff --git a/src/components/Coin.svelte b/src/components/Coin.svelte deleted file mode 100644 index 04500f82..00000000 --- a/src/components/Coin.svelte +++ /dev/null @@ -1,23 +0,0 @@ - - -
- -
diff --git a/src/components/Coins.svelte b/src/components/Coins.svelte deleted file mode 100644 index 8fb196b7..00000000 --- a/src/components/Coins.svelte +++ /dev/null @@ -1,56 +0,0 @@ - - - - {#if copper > 0} - {#if renderAll} - {#each Array(copper).fill(0) as _, i} - - {/each} - {:else} - - {copper}× - - - {/if} - {/if} - {#if silver > 0} - {#if renderAll} - {#each Array(silver).fill(0) as _, i} - - {/each} - {:else} - - {silver}× - - - {/if} - {/if} - {#if gold > 0} - {#if renderAll} - {#each Array(gold).fill(0) as _, i} - - {/each} - {:else} - - {gold}× - - - {/if} - {/if} - diff --git a/src/components/Debug.svelte b/src/components/Debug.svelte deleted file mode 100644 index b92c577e..00000000 --- a/src/components/Debug.svelte +++ /dev/null @@ -1,11 +0,0 @@ - - -{#if IS_DEV} - -
{JSON.stringify(data, null, 2)}
-
-{/if} diff --git a/src/components/DebugAppData.svelte b/src/components/DebugAppData.svelte deleted file mode 100644 index fc1a8c26..00000000 --- a/src/components/DebugAppData.svelte +++ /dev/null @@ -1,106 +0,0 @@ - - - (showSidebar = true)} - onmouseleave={() => !app.settings.debugOpen && (showSidebar = false)} -> - -
- -
- (app.settings.debugOpen = checked)} - > - Keep open - -
-
-
- {#if app.combat.duration <= 0} - {#each Object.entries(app.dump()) as [key, value]} - {@const isOpenable = typeof value === 'object'} - {@const openValue = app.settings.openProperties?.[key]} - {@const isOpen = isOpenable && !!openValue} - { - if (openValue) { - delete app.settings.openProperties[key]; - } else { - app.settings.openProperties[key] = true; - } - }} - > - - -
{key}
- - {#if isOpenable} - - {Math.max( - 0, - Array.isArray(value) ? value.length : Object.keys(value || {}).length - )} - - {:else} - {value || value === 0 ? value : '-'} - {/if} - - {#if isOpenable} - - {/if} - - -
- - - -
-                    {stringifyCircularJSON(value)}
-                    
-
-
-
-
-
- {/each} - - {/if} -
-
diff --git a/src/components/DevBar.svelte b/src/components/DevBar.svelte deleted file mode 100644 index d5bdd3c0..00000000 --- a/src/components/DevBar.svelte +++ /dev/null @@ -1,124 +0,0 @@ - - - - - - -
- Elapsed ms: {app.elapsedMilliseconds.toFixed(0)} -
-
- Duration: {app.combat.duration} -
-
- -
- Time sync (server): {new Date(app.serverTimestampSnapshot).toLocaleString()} -
-
- Time (server): {new Date(app.serverTimestamp).toLocaleString()} -
- - - Debug - - - Ability Scaling - - - Equipment Scaling - - - Character Scaling - - -
- - - - - - - - - - - - - - - - - - - - - - -
diff --git a/src/components/EquipmentFilter.svelte b/src/components/EquipmentFilter.svelte deleted file mode 100644 index 28e721d4..00000000 --- a/src/components/EquipmentFilter.svelte +++ /dev/null @@ -1,57 +0,0 @@ - - - - - {#each EQUIPMENT_TYPES as type} - - {/each} - diff --git a/src/components/EquipmentLink.svelte b/src/components/EquipmentLink.svelte deleted file mode 100644 index 9c0fdf23..00000000 --- a/src/components/EquipmentLink.svelte +++ /dev/null @@ -1,78 +0,0 @@ - - -
-
-
= 2 && 'bg-gray-400', - level >= 5 && 'bg-green-600', - level >= 10 && 'bg-blue-600', - level >= 15 && 'bg-purple-600', - level >= 20 && 'bg-orange-600', - level >= 25 && 'bg-red-600' - )} - style=" - mask: url('/images/brush-center.png') repeat-x 0 0 / auto 20px; - " - >
-
= 2 && 'bg-gray-400', - level >= 5 && 'bg-green-600', - level >= 10 && 'bg-blue-600', - level >= 15 && 'bg-purple-600', - level >= 20 && 'bg-orange-600', - level >= 25 && 'bg-red-600' - )} - style=" - mask: url('/images/brush-left.png') no-repeat 0 0 / auto 20px; - " - >
-
= 2 && 'bg-gray-400', - level >= 5 && 'bg-green-600', - level >= 10 && 'bg-blue-600', - level >= 15 && 'bg-purple-600', - level >= 20 && 'bg-orange-600', - level >= 25 && 'bg-red-600' - )} - style=" - mask: url('/images/brush-right.png') no-repeat 100% 0 / auto 20px; - " - >
- - {props.name} - -
- -
diff --git a/src/components/ForgotPassword.svelte b/src/components/ForgotPassword.svelte deleted file mode 100644 index edddb89d..00000000 --- a/src/components/ForgotPassword.svelte +++ /dev/null @@ -1,35 +0,0 @@ - - -
- - - -
diff --git a/src/components/Frame.svelte b/src/components/Frame.svelte deleted file mode 100644 index 7b5c3c2c..00000000 --- a/src/components/Frame.svelte +++ /dev/null @@ -1,41 +0,0 @@ - - -
- {#if title} - {title} - {/if} - - {@render children?.()} - -
- - diff --git a/src/components/GameAudio.svelte b/src/components/GameAudio.svelte deleted file mode 100644 index 2506f2b3..00000000 --- a/src/components/GameAudio.svelte +++ /dev/null @@ -1,81 +0,0 @@ - diff --git a/src/components/Headline.svelte b/src/components/Headline.svelte deleted file mode 100644 index 218bebaa..00000000 --- a/src/components/Headline.svelte +++ /dev/null @@ -1,30 +0,0 @@ - - - - {#if small} -
{text}
- {:else} -

{text}

- {/if} - {#if children} - {@render children()} - {/if} -
- -
diff --git a/src/components/Hr.svelte b/src/components/Hr.svelte deleted file mode 100644 index 0257ba0f..00000000 --- a/src/components/Hr.svelte +++ /dev/null @@ -1,17 +0,0 @@ - - -
diff --git a/src/components/Loader.svelte b/src/components/Loader.svelte deleted file mode 100644 index 900a6afc..00000000 --- a/src/components/Loader.svelte +++ /dev/null @@ -1,34 +0,0 @@ - - -{#if show} -
- -

- {@render children()} -

- -
-
-{/if} diff --git a/src/components/Login.svelte b/src/components/Login.svelte deleted file mode 100644 index 6a9c76c9..00000000 --- a/src/components/Login.svelte +++ /dev/null @@ -1,90 +0,0 @@ - - -
- - - - - - - -
- (codeOfConduct = checked)} - > - I agree to the (app.overlay = 'CodeOfConduct'))} - > - Code of Conduct - - -
- -
- (rememberMe = checked)} - > - Remember me - -
-
diff --git a/src/components/Logo.svelte b/src/components/Logo.svelte deleted file mode 100644 index 0c521f2b..00000000 --- a/src/components/Logo.svelte +++ /dev/null @@ -1,12 +0,0 @@ - - - - - BATTLE BRAWLERS - - - - - - diff --git a/src/components/MyLudus.svelte b/src/components/MyLudus.svelte deleted file mode 100644 index ceaa6cb2..00000000 --- a/src/components/MyLudus.svelte +++ /dev/null @@ -1,131 +0,0 @@ - - - - {#each LEVELS as level} - {@const accountLevel = getLevelByExperience(app.experience)} - {@const claimed = level <= app.accountRewards} - {@const disabled = level > app.accountRewards + 1 || accountLevel < level} - {@const rewards = rewardForLevel(level)} - -

Level {level}

-
-
- - - {#if rewards.includes('100_coins')} - - Account - - +1 - - - {#if rewards.includes('ludus_upgrade')} - +1 brawler - {/if} - - {/if} - {#if rewards.includes('character_stats')} - - Brawlers - - +4 - - - - +2 - - - - {/if} - - - {#if claimed} - - - - {:else} - - {/if} - -
- - Premium rewards TBA. - -
- {/each} -
diff --git a/src/components/RefillHealthTimer.svelte b/src/components/RefillHealthTimer.svelte deleted file mode 100644 index f662750e..00000000 --- a/src/components/RefillHealthTimer.svelte +++ /dev/null @@ -1,64 +0,0 @@ - - - diff --git a/src/components/Register.svelte b/src/components/Register.svelte deleted file mode 100644 index e59acb04..00000000 --- a/src/components/Register.svelte +++ /dev/null @@ -1,38 +0,0 @@ - - -
- - - - -
diff --git a/src/components/Topbar.svelte b/src/components/Topbar.svelte deleted file mode 100644 index c02fb3ff..00000000 --- a/src/components/Topbar.svelte +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - (app.overlay = 'GameMenu')}> - - - diff --git a/src/components/Unauthorized.svelte b/src/components/Unauthorized.svelte deleted file mode 100644 index e8aba6f0..00000000 --- a/src/components/Unauthorized.svelte +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - {#if loginPageMode === 0} - - {/if} - {#if loginPageMode === 1} - - {/if} - {#if loginPageMode === 2} - - {/if} - - {#if loginPageMode !== 0} - - {/if} - {#if loginPageMode !== 1} - - {/if} - {#if loginPageMode !== 2} - - {/if} - - - - - diff --git a/src/components/buttons/Clickable.svelte b/src/components/buttons/Clickable.svelte deleted file mode 100644 index 5e9a5541..00000000 --- a/src/components/buttons/Clickable.svelte +++ /dev/null @@ -1,27 +0,0 @@ - - -{#if href} - - {@render children()} - -{:else} - -{/if} diff --git a/src/components/buttons/Close.svelte b/src/components/buttons/Close.svelte deleted file mode 100644 index 7ba8ef21..00000000 --- a/src/components/buttons/Close.svelte +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/src/components/buttons/GoBack.svelte b/src/components/buttons/GoBack.svelte deleted file mode 100644 index eabddfbf..00000000 --- a/src/components/buttons/GoBack.svelte +++ /dev/null @@ -1,9 +0,0 @@ - - -
- -
diff --git a/src/components/buttons/Logout.svelte b/src/components/buttons/Logout.svelte deleted file mode 100644 index b5a6a9ec..00000000 --- a/src/components/buttons/Logout.svelte +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/src/components/character/AbilityBar.svelte b/src/components/character/AbilityBar.svelte deleted file mode 100644 index 15d648ca..00000000 --- a/src/components/character/AbilityBar.svelte +++ /dev/null @@ -1,139 +0,0 @@ - - -
-
- - {#each Array(small ? 12 : 15) as _, i} - - {/each} - - {#if !dndDisabled} -
- -
- {/if} - {#if !minimalistic} -
- - Ability sequence{lastTick} ticks - -
- {/if} - -
- - - {#each hydratedAbilities as ability, i (ability.uuid)} - {@const tickStart = calculateTickStart(hydratedAbilities, i)} - 11 && 'border-red-300 bg-red-100', - !dndDisabled && tickStart > 11 && hideOverflow && '!hidden' - )} - style="width: calc(((100%/{small ? 12 : 15})*{ability.ticks}) + 1px);" - > - 11} /> - - - {/each} - -
-
diff --git a/src/components/character/AbilityIcon.svelte b/src/components/character/AbilityIcon.svelte deleted file mode 100644 index 47f0c176..00000000 --- a/src/components/character/AbilityIcon.svelte +++ /dev/null @@ -1,65 +0,0 @@ - - - - -{#if !hideTickCount} - -
- {ticks} -
-
-{/if} - - - - - -{#if statusEffects && statusEffects.length > 0} - - {#each statusEffects as effect} - {@const isStatusStack = ['isConcussed', 'isWounded', 'isExposed'].includes(effect)} - - - {/each} - -{/if} diff --git a/src/components/character/AbilityInventory.svelte b/src/components/character/AbilityInventory.svelte deleted file mode 100644 index c5c7aa8b..00000000 --- a/src/components/character/AbilityInventory.svelte +++ /dev/null @@ -1,78 +0,0 @@ - - - - {#if hydratedAbilities.length} - {#each hydratedAbilities as ability (ability.uuid)} - - - - - - {/each} - {:else if hasNonbasicAbilities} - Abilities from equipped gear will appear here - {:else} - Abilities from equipped gear will appear here - {/if} - diff --git a/src/components/character/AbilitySelection.svelte b/src/components/character/AbilitySelection.svelte deleted file mode 100644 index 50549e28..00000000 --- a/src/components/character/AbilitySelection.svelte +++ /dev/null @@ -1,304 +0,0 @@ - - - - - {#if renderSides} - - - - - {/if} - - - {#if !renderSides} - -
Active abilities
- -
- {/if} - -
- - {#if renderSides} - - - - - {/if} -
- - -
- - -
Available abilities
- - {#if !characterIndex} - - {/if} -
- -
-
-
-
- - diff --git a/src/components/character/CharacterAvatar.svelte b/src/components/character/CharacterAvatar.svelte deleted file mode 100644 index 0434c655..00000000 --- a/src/components/character/CharacterAvatar.svelte +++ /dev/null @@ -1,24 +0,0 @@ - - - - - diff --git a/src/components/character/CharacterEquipment.svelte b/src/components/character/CharacterEquipment.svelte deleted file mode 100644 index 6d40c5a1..00000000 --- a/src/components/character/CharacterEquipment.svelte +++ /dev/null @@ -1,34 +0,0 @@ - - -{#each Object.entries(character.equipment) as [slot, equipment] (`${character.uuid}-${slot}-${equipment?.uuid}`)} - - -
{slotsInPrettyName(slot as EquipmentSlot)}
- - {#if slot === 'offHand' && character.equipment.mainHand && EQUIPMENT(character.equipment.mainHand, true).slotsIn === 'twoHand'} - - {EQUIPMENT(character.equipment.mainHand, true).name} - - {:else if slot === 'mainHand' && !character.equipment.mainHand} - Fist - {:else if slot === 'offHand' && !character.equipment.offHand} - Fist - {:else if equipment} - - {:else} - - - {/if} -
- - {#if equipment} - - {/if} -
-{/each} diff --git a/src/components/character/CoreStats.svelte b/src/components/character/CoreStats.svelte deleted file mode 100644 index 462d8ca6..00000000 --- a/src/components/character/CoreStats.svelte +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - {combatStats?.maxHealth} - - {#if !vertical} - / - {/if} - - - {combatStats?.maxArmor} - - {#if !vertical} - / - {/if} - - - {combatStats?.damage} - - diff --git a/src/components/character/Stats.svelte b/src/components/character/Stats.svelte deleted file mode 100644 index b8698643..00000000 --- a/src/components/character/Stats.svelte +++ /dev/null @@ -1,33 +0,0 @@ - - -{#each stats as [key, value]} - {@const actualKey = - { - wounded: 'bleeding', - concussed: 'stunned', - exposed: 'vulnerable' - }?.[key as string] || key} - {@const actualValue = prettyCombatStatValue(actualKey, value)} - - - - {#if !showAsAddition} - - - - {/if} - {prettyCombatStatKey(actualKey)} - -
- {#if showAsAddition}+ {/if}{actualValue} -
-
-{/each} diff --git a/src/components/combat/CharacterSelection.svelte b/src/components/combat/CharacterSelection.svelte deleted file mode 100644 index b5ace179..00000000 --- a/src/components/combat/CharacterSelection.svelte +++ /dev/null @@ -1,66 +0,0 @@ - - - - {#each Array(count) as _, index} - {@const selectedBrawler = selectedBrawlers[index]} - {#if !selectedBrawler} - - {#if index === 0} -
select a brawler
- {:else} -
slot {index + 1}
- {/if} -
- {:else} - - {/if} - {/each} -
- - - - - - - - diff --git a/src/components/combat/CombatArena.svelte b/src/components/combat/CombatArena.svelte deleted file mode 100644 index f82e08d0..00000000 --- a/src/components/combat/CombatArena.svelte +++ /dev/null @@ -1,9 +0,0 @@ - - -
- {@render children()} -
diff --git a/src/components/combat/CombatAudioPlayer.svelte b/src/components/combat/CombatAudioPlayer.svelte deleted file mode 100644 index fdb6d953..00000000 --- a/src/components/combat/CombatAudioPlayer.svelte +++ /dev/null @@ -1,65 +0,0 @@ - - - diff --git a/src/components/combat/CombatantAbilityBar.svelte b/src/components/combat/CombatantAbilityBar.svelte deleted file mode 100644 index 8a0917a1..00000000 --- a/src/components/combat/CombatantAbilityBar.svelte +++ /dev/null @@ -1,81 +0,0 @@ - - -{#snippet iconBar(topLayer = false, progress = 100)} -
-
- {#each abilitiesCopied as { name, ticks, icon, chainLink }, i (`icon_${i}_${name}`)} - - {#if chainLink} - {#each Array(chainLink).fill(0).slice(0, -1) as _, j} -
- {/each} - {/if} - -
- {/each} -
-
-{/snippet} - -
-
- {@render iconBar()} - {#if progress} - {@render iconBar(true, progress * 100)} - {/if} -
-
diff --git a/src/components/combat/CombatantAudioPlayer.svelte b/src/components/combat/CombatantAudioPlayer.svelte deleted file mode 100644 index aceb68ab..00000000 --- a/src/components/combat/CombatantAudioPlayer.svelte +++ /dev/null @@ -1,49 +0,0 @@ - - - diff --git a/src/components/combat/CombatantCard.svelte b/src/components/combat/CombatantCard.svelte deleted file mode 100644 index dfd5c97a..00000000 --- a/src/components/combat/CombatantCard.svelte +++ /dev/null @@ -1,213 +0,0 @@ - - - - - diff --git a/src/components/combat/CombatantImage.svelte b/src/components/combat/CombatantImage.svelte deleted file mode 100644 index ed16855a..00000000 --- a/src/components/combat/CombatantImage.svelte +++ /dev/null @@ -1,310 +0,0 @@ - - -
- - - -
- -
- - - -
-
-
- - -
- - diff --git a/src/components/combat/HealthBar.svelte b/src/components/combat/HealthBar.svelte deleted file mode 100644 index d1e18134..00000000 --- a/src/components/combat/HealthBar.svelte +++ /dev/null @@ -1,209 +0,0 @@ - - - - {#each armorHurts as armorHurt (armorHurt.id)} -
-
- - {#if armorHurt.isCritical} - - {/if} - {armorHurt.amount} - -
-
- {/each} - - {#each hurts as hurt (hurt.id)} -
-
- - {#if hurt.isCritical} - - {/if} - {hurt.amount} - -
-
- {/each} - - {#each heals as heal (heal.id)} -
-
-
+{heal.amount}
-
-
- {/each} -
- - diff --git a/src/components/combat/ResultAnnouncement.svelte b/src/components/combat/ResultAnnouncement.svelte deleted file mode 100644 index d6507c9e..00000000 --- a/src/components/combat/ResultAnnouncement.svelte +++ /dev/null @@ -1,115 +0,0 @@ - - -{#if progress >= 1} - - - - - {#if outcome === 'victory'} - delayTicks}> - -
rewards
-
- - {#each rewardsToShow as { type, amount }, i} - i + delayTicks + 1}> - +{amount} {type} - - {/each} - {#each rewardsToShow as { type, amount }, i} - - - - - - {/each} - -
- - - -
-
- {:else} - delayTicks}> - - - - - {/if} -
-
-{/if} diff --git a/src/components/combat/TeamBadge.svelte b/src/components/combat/TeamBadge.svelte deleted file mode 100644 index e58dc399..00000000 --- a/src/components/combat/TeamBadge.svelte +++ /dev/null @@ -1,127 +0,0 @@ - - -
-
-
- -
- -
-
-
-
- -
-
-
-
- -
- - -
- -
-
-
- -
-
-
-
-
-
- - diff --git a/src/components/combat/VictoryOrLoss.svelte b/src/components/combat/VictoryOrLoss.svelte deleted file mode 100644 index ff2b1ea4..00000000 --- a/src/components/combat/VictoryOrLoss.svelte +++ /dev/null @@ -1,62 +0,0 @@ - - - - {#if outcome === 'victory'} -
- {/if} -
- -
-
- - -
- -
- {outcome.toUpperCase()} -
-
diff --git a/src/components/dialog/BasicConfirmation.svelte b/src/components/dialog/BasicConfirmation.svelte deleted file mode 100644 index 2a698337..00000000 --- a/src/components/dialog/BasicConfirmation.svelte +++ /dev/null @@ -1,24 +0,0 @@ - - - - -
- {@html text} -
-
- - - - -
- diff --git a/src/components/form/Button.svelte b/src/components/form/Button.svelte deleted file mode 100644 index d3589652..00000000 --- a/src/components/form/Button.svelte +++ /dev/null @@ -1,85 +0,0 @@ - - - diff --git a/src/components/form/Checkbox.svelte b/src/components/form/Checkbox.svelte deleted file mode 100644 index 3e233364..00000000 --- a/src/components/form/Checkbox.svelte +++ /dev/null @@ -1,25 +0,0 @@ - - - diff --git a/src/components/form/Dropdown.svelte b/src/components/form/Dropdown.svelte deleted file mode 100644 index b16d656a..00000000 --- a/src/components/form/Dropdown.svelte +++ /dev/null @@ -1,41 +0,0 @@ - - -
- - - - {#if options.find(emptySlot)} - {value} - {:else} - {value || 'N/A'} - {/if} - - - -
diff --git a/src/components/form/Input.svelte b/src/components/form/Input.svelte deleted file mode 100644 index b782b398..00000000 --- a/src/components/form/Input.svelte +++ /dev/null @@ -1,76 +0,0 @@ - - -
- - - {#if placeholder} -
- {placeholder} -
- {/if} -
- - diff --git a/src/components/global/AccountProgression.svelte b/src/components/global/AccountProgression.svelte deleted file mode 100644 index 3580ee31..00000000 --- a/src/components/global/AccountProgression.svelte +++ /dev/null @@ -1,46 +0,0 @@ - - - diff --git a/src/components/global/ClientClock.svelte b/src/components/global/ClientClock.svelte deleted file mode 100644 index 39c40201..00000000 --- a/src/components/global/ClientClock.svelte +++ /dev/null @@ -1,13 +0,0 @@ - diff --git a/src/components/global/ConnectSocket.svelte b/src/components/global/ConnectSocket.svelte deleted file mode 100644 index 391932d3..00000000 --- a/src/components/global/ConnectSocket.svelte +++ /dev/null @@ -1,22 +0,0 @@ - diff --git a/src/components/global/Dialog.svelte b/src/components/global/Dialog.svelte deleted file mode 100644 index 116b7003..00000000 --- a/src/components/global/Dialog.svelte +++ /dev/null @@ -1,33 +0,0 @@ - - -
-
-
- {#if Component} - - {/if} -
-
-
diff --git a/src/components/global/InCombat.svelte b/src/components/global/InCombat.svelte deleted file mode 100644 index 44b42526..00000000 --- a/src/components/global/InCombat.svelte +++ /dev/null @@ -1,68 +0,0 @@ - diff --git a/src/components/global/Keystrokes.svelte b/src/components/global/Keystrokes.svelte deleted file mode 100644 index a317f52a..00000000 --- a/src/components/global/Keystrokes.svelte +++ /dev/null @@ -1,39 +0,0 @@ - - - - - diff --git a/src/components/global/Notifications.svelte b/src/components/global/Notifications.svelte deleted file mode 100644 index 7267e07c..00000000 --- a/src/components/global/Notifications.svelte +++ /dev/null @@ -1,106 +0,0 @@ - - -
-
- {#each [...app.notifications] as notification, i (notification)} - {@const { type, message } = JSON.parse(notification)} -
-
-
- - - - -
- {titleByType(type)} -
- {message.replace('Error: ', '')} -
-
-
-
-
-
- {/each} -
-
diff --git a/src/components/global/Overlay.svelte b/src/components/global/Overlay.svelte deleted file mode 100644 index 296b837c..00000000 --- a/src/components/global/Overlay.svelte +++ /dev/null @@ -1,42 +0,0 @@ - - - diff --git a/src/components/overlays/CodeOfConduct.svelte b/src/components/overlays/CodeOfConduct.svelte deleted file mode 100644 index 8a31f1c3..00000000 --- a/src/components/overlays/CodeOfConduct.svelte +++ /dev/null @@ -1,50 +0,0 @@ - - - - By playing the game you agree to the following rules and regulations. -
-
-

Rules:

-
    -
  1. Respect everyone and their opinions
  2. -
  3. Don't spam chat
  4. -
  5. No excessive caps
  6. -
  7. No excessive swearing
  8. -
  9. No NSFW Content in chat
  10. -
  11. Always respect a moderators request
  12. -
- We want to promote a sense of community where conversations and discussion is encouraged. Therefore - we expect everyone to to behave in a civil and respectful manner. -
-
-

Verbal abuse, threatening behaviour, harassment

- Remember this is a game where people come to relax and have fun. Although you may disagree with other - players and their opinions, this does not mean you need to be abusive or offensive. Direct verbal abuse - and/or harassment of other players is not acceptable. -
-
-

Racism, discrimination and hate speech

- Any kind of racism, discrimination or hate speech will not be tolerated. -
-
-

Religious, political discussions

- Conversations around these topics can sometimes be considered controversial, however we will allow - it as long as the above rules are adhered to. Moderator discretion also applies. -
-
-

Moderators discretion

- Sometimes prolonged or heated discussion around the above topics can become uncomfortable for other - players. The mods will always put the community first. If the community is showing concern around these - topics, Moderators reserve the right to request the conversation to end. Remember everyone is here - to enjoy the game and have fun. -
-
-

- Moderators reserve the right to mute players who consistently violate - the rules above. -

- - - diff --git a/src/components/overlays/Combat.svelte b/src/components/overlays/Combat.svelte deleted file mode 100644 index 8327510a..00000000 --- a/src/components/overlays/Combat.svelte +++ /dev/null @@ -1,145 +0,0 @@ - - -
- - - - {#if liveTeams.length} - {#each liveTeams as { combatants, name }, _index} - {#each combatants as combatant, _c} - {@const { rot } = combatant.position} - {@const raw = Math.round(Math.abs(Math.abs(rot - 540) - 180))} - {@const z = 10 - Math.floor((raw / 180) * 9)} - - {@const angleDiff = ((rot - 0 + 540) % 360) - 180} - {@const totalTime = - combatant.abilities.reduce((acc, { ticks }) => acc + ticks, 0) * COMBAT_TICK_TIME} - {@const individualProgress = - ((combatant.statuses.knockedOut - ? combatant.statuses.knockedOut - : app.elapsedMilliseconds) / - totalTime) % - 1} - - - - {/each} - - {/each} - {/if} - - - -
- - diff --git a/src/components/overlays/GameMenu.svelte b/src/components/overlays/GameMenu.svelte deleted file mode 100644 index f0aa20ad..00000000 --- a/src/components/overlays/GameMenu.svelte +++ /dev/null @@ -1,111 +0,0 @@ - - -{#snippet displayMasterVolume()} - {prettyVolume(app.settings.volume.master)} -{/snippet} -{#snippet displayAmbientVolume()} - {prettyVolume(app.settings.volume.ambient)} -{/snippet} -{#snippet displaySFXVolume()} - {prettyVolume(app.settings.volume.sfx)} -{/snippet} -{#snippet displayCombatVolume()} - {prettyVolume(app.settings.volume.combat)} -{/snippet} - - - - Audio settings - - - - -
Master
- -
- -
Ambient
- -
- -
Sound effect
- -
- -
Combat
- -
- -
- - - {#if app.token} - - {/if} - - - diff --git a/src/components/overlays/ReleaseNotes.svelte b/src/components/overlays/ReleaseNotes.svelte deleted file mode 100644 index b25bdb2f..00000000 --- a/src/components/overlays/ReleaseNotes.svelte +++ /dev/null @@ -1,10 +0,0 @@ - - - -

0.0.1 - 0.0.2 | 5-8

- Initial version - - - diff --git a/src/components/tooltips/TooltipAbility.svelte b/src/components/tooltips/TooltipAbility.svelte deleted file mode 100644 index 347b5f78..00000000 --- a/src/components/tooltips/TooltipAbility.svelte +++ /dev/null @@ -1,137 +0,0 @@ - - -
- -
{name}
-
-
- - - {#if type === AbilityType.WindUp} -
- Wind up: - {ticks} tick{ticks === 1 ? '' : 's'} -
- {/if} - {#if calculatedDuration?.result} - {@const duration = Math.floor(calculatedDuration.result)} -
- Duration: - {duration === Infinity ? 'variable' : `${duration} tick${duration === 1 ? '' : 's'}`} -
- {/if} - - {#if chainLink} -
- Triggers each: - {ticks / chainLink} tick{ticks / chainLink === 1 ? '' : 's'} -
- {/if} - - {#if calculatedDamage?.result} -
- Damage: - {Math.floor(combatStats?.damage * calculatedDamage.result)} - ({Math.floor(calculatedDamage.result * 100)}% of total damage) -
- {/if} - {#if name === 'Block'} -
- Block chance: - 100% -
- {/if} - {#if calculatedHealing?.result} -
- Heal: - {Math.floor(combatStats?.maxHealth * calculatedHealing.result)} - ({Math.floor(calculatedHealing.result * 100)}% of max health) -
- {/if} -
- - {#if statusEffects.length > 0} -
- {#each statusEffects as statusEffect (statusEffect)} - {@const { singleWord, icon, convertsInto } = STATUS_EFFECTS[statusEffect]} - This ability inflicts {singleWord} ( ){#if convertsInto} -
which in turn inflicts {STATUS_EFFECTS[convertsInto].singleWord} - ( ){/if}. - {/each} -
- {/if} - - {#if description} -
- {@html description} - - -
- {/if} -
diff --git a/src/components/tooltips/TooltipEquipment.svelte b/src/components/tooltips/TooltipEquipment.svelte deleted file mode 100644 index c8deeef3..00000000 --- a/src/components/tooltips/TooltipEquipment.svelte +++ /dev/null @@ -1,83 +0,0 @@ - - - - - -
{name}
-
level {level}
-
- -
-
- {#if regularCombatStats.length > 0} - - {#each regularCombatStats as [key, value]} - - {prettyCombatStatKey(key)} - {value} - - {/each} - - {/if} - {#if luckyCombatStats.length > 0} - - {#each luckyCombatStats as [key, value]} - - {prettyCombatStatKey(key)} - {prettyCombatStatValue(key, value)} - - {/each} - - {/if} - {#if description} - {@html description} - {/if} - - {#if activeAbilities.length > 0} - - - - {/if} - {#if availableAbilities.length > 0} -
- - - - - {/if} -
diff --git a/src/components/ui/Bar.svelte b/src/components/ui/Bar.svelte deleted file mode 100644 index 32ed4312..00000000 --- a/src/components/ui/Bar.svelte +++ /dev/null @@ -1,65 +0,0 @@ - - -
-
- - - {#if isLoading} - - {:else if isTime} - {@const minutes = Math.floor((max - current) / 60000)} - {@const seconds = Math.floor(((max - current) % 60000) / 1000) + 1} - {@const timeToRefillFormatted = `${minutes < 10 ? '0' : ''}${minutes}:${seconds < 10 ? '0' : ''}${seconds}`} - - {timeToRefillFormatted} - - {:else} -
- {#if text} - {text} - {/if} -
- {#if percentage} - {Math.max(0, (current / max) * 100).toFixed(0)}% - {:else} - {current} / {max} - {/if} - {/if} -
- - {#if children} - {@render children()} - {/if} -
diff --git a/src/components/ui/Icon.svelte b/src/components/ui/Icon.svelte deleted file mode 100644 index 381db3f2..00000000 --- a/src/components/ui/Icon.svelte +++ /dev/null @@ -1,52 +0,0 @@ - - - fill && `--${name}-fill-color-${i}: ${fill};`), - ...strokes.map((stroke, i) => stroke && `--${name}-stroke-color-${i}: ${stroke};`) - ] - .filter(Boolean) - .join('') - : undefined} - {...rest} -> - {@html elements.join('')} - diff --git a/src/components/ui/Pill.svelte b/src/components/ui/Pill.svelte deleted file mode 100644 index fa72450a..00000000 --- a/src/components/ui/Pill.svelte +++ /dev/null @@ -1,9 +0,0 @@ - - - - {text} - diff --git a/src/components/ui/Spinner.svelte b/src/components/ui/Spinner.svelte deleted file mode 100644 index 783ef745..00000000 --- a/src/components/ui/Spinner.svelte +++ /dev/null @@ -1,8 +0,0 @@ - - -
- - -
diff --git a/src/components/ui/Tooltip.svelte b/src/components/ui/Tooltip.svelte deleted file mode 100644 index e4dd60ef..00000000 --- a/src/components/ui/Tooltip.svelte +++ /dev/null @@ -1,114 +0,0 @@ - - -
- {@render children()} - -
= 5 && 'bg-green-700', - hasEquipmentLevel >= 10 && 'bg-blue-700', - hasEquipmentLevel >= 15 && 'bg-purple-700', - hasEquipmentLevel >= 20 && 'bg-orange-700', - hasEquipmentLevel >= 25 && 'bg-red-700' - )} - style={['left', 'right'].includes(direction ?? '') - ? `top:${arrowTop}px;` - : `left:${arrowLeft}px;`} - >
-
diff --git a/src/constants/APP.ts b/src/constants/APP.ts deleted file mode 100644 index b41bf986..00000000 --- a/src/constants/APP.ts +++ /dev/null @@ -1,14 +0,0 @@ -export const COMBAT_TICK_TIME = 400; -export const COMBAT_RING_BASE_RADIUS = 250; - -export const ABILITY_PRIORITY = [ - // 'stun', - // 'block', - 'stab', - 'pierce', - 'punch', - 'slam', - 'swing', - 'whirlwind', - 'block' -]; diff --git a/src/constants/AVAILABLE_KEYS.ts b/src/constants/AVAILABLE_KEYS.ts deleted file mode 100644 index de524254..00000000 --- a/src/constants/AVAILABLE_KEYS.ts +++ /dev/null @@ -1,34 +0,0 @@ -export default [ - 'keyw', - 'keya', - 'keys', - 'keyd', - 'keyi', - 'keyq', - 'keye', - 'keyz', - 'keyx', - 'keyc', - 'arrowright', - 'arrowleft', - 'arrowdown', - 'arrowup', - 'escape', - 'enter', - 'shiftleft', - 'shiftright', - 'digit1', - 'digit2', - 'digit3', - 'digit4', - 'digit5', - 'numpad1', - 'numpad2', - 'numpad3', - 'numpad4', - 'numpad5', - 'numpad6', - 'numpad7', - 'numpad8', - 'numpad9' -].reduce((a, key) => ({ ...a, [key]: false }), {}); diff --git a/src/constants/ELEMENTS.ts b/src/constants/ELEMENTS.ts deleted file mode 100644 index 33294c8b..00000000 --- a/src/constants/ELEMENTS.ts +++ /dev/null @@ -1,50 +0,0 @@ -export const ALL_ELEMENTS = { - nature: { - name: 'Nature', - color: { - primary: '#8bb737', - secondary: '#a0ce3e' - } - }, - earth: { - name: 'Earth', - color: { - primary: '#7f5322', - secondary: '#c68533' - } - }, - lightning: { - name: 'Lightning', - color: { - primary: '#eac303', - secondary: '#fced60' - } - }, - frost: { - name: 'Frost', - color: { - primary: '#88c6cb', - secondary: '#eaffff' - } - }, - fire: { - name: 'Fire', - color: { - primary: '#f45f31', - secondary: '#f4a031' - } - } -} as Record; - -// export default (id: string | CharacterRef, fullBody: boolean = false, meta?: DynamicObject) => -// entity( -// ALL_ELEMENTS, -// typeof id === 'string' ? id : id.id, -// typeof id === 'string' ? undefined : id.uuid, -// fullBody, -// typeof id === 'string' -// ? meta?.overrides -// : meta?.overrides -// ? deepMerge(id.overrides || {}, meta.overrides || {}) -// : id.overrides -// ) as Character; diff --git a/src/constants/ENV_VARS.ts b/src/constants/ENV_VARS.ts deleted file mode 100644 index e65680f3..00000000 --- a/src/constants/ENV_VARS.ts +++ /dev/null @@ -1,19 +0,0 @@ -// Named exports are needed to import these individually in .js-files (see `src/helpers`) -export const WEBSOCKET_CONNECT = import.meta.env.VITE_WEBSOCKET_CONNECT; -export const ENVIRONMENT = import.meta.env.VITE_VERCEL_ENV; -export const AUTO_EMAIL = import.meta.env.VITE_AUTO_EMAIL; -export const AUTO_PASSWORD = import.meta.env.VITE_AUTO_PASSWORD; -export const IS_DEV = ENVIRONMENT === 'development'; -export const IS_PROD = ENVIRONMENT === 'production'; -export const IS_PREV = ENVIRONMENT === 'preview'; - -// Default export is needed to expose `ENV` (see `vite.config.js`) -export default { - WEBSOCKET_CONNECT, - ENVIRONMENT, - AUTO_EMAIL, - AUTO_PASSWORD, - IS_DEV, - IS_PROD, - IS_PREV -}; diff --git a/src/constants/VFX.ts b/src/constants/VFX.ts deleted file mode 100644 index 9ef5b5ba..00000000 --- a/src/constants/VFX.ts +++ /dev/null @@ -1,53 +0,0 @@ -import type { VFX } from '@/types/vfx'; - -export default { - basicAttackFast: { - vfxName: 'basicAttackFast', - duration: 1000 - }, - basicAttackRegular: { - vfxName: 'basicAttackRegular', - duration: 1500 - }, - basicAttackSlow: { - vfxName: 'basicAttackSlow', - duration: 2000 - }, - block: { - vfxName: 'block', - duration: 2000 + 250 // lingering time - }, - kick: { - vfxName: 'kick', - duration: 500 - }, - whirlwind: { - vfxName: 'whirlwind', - duration: 500 - }, - // Status effects - hurt: { - vfxName: 'hurt', - duration: 500 - }, - armorHurt: { - vfxName: 'armorHurt', - duration: 500 - }, - attackBlocked: { - vfxName: 'attackBlocked', - duration: 340 - }, - attackDodged: { - vfxName: 'attackDodged', - duration: 340 - }, - heal: { - vfxName: 'heal', - duration: 500 - }, - filler: { - vfxName: 'filler', - duration: 500 - } -} as any; diff --git a/src/constants/WEAPON_TYPES.ts b/src/constants/WEAPON_TYPES.ts deleted file mode 100644 index 6419955d..00000000 --- a/src/constants/WEAPON_TYPES.ts +++ /dev/null @@ -1,3 +0,0 @@ -import type { WeaponType } from '@/types/weaponType'; - -export default ['1h', '2h', 'offHand'] as WeaponType[]; diff --git a/src/crow.css b/src/crow.css deleted file mode 100644 index 6c4e3c59..00000000 --- a/src/crow.css +++ /dev/null @@ -1,74 +0,0 @@ -crow, -.crow { - display: flex; - flex: 1; - align-items: center; - justify-content: center; -} -.up, -[up], -.down, -[down] { - height: 100%; -} -.up, -[up] { - align-items: flex-start; -} -.down, -[down] { - align-items: flex-end; -} -.left, -[left] { - justify-content: flex-start; -} -.right, -[right] { - justify-content: flex-end; -} -.vertical, -[vertical] { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; -} -.vertical > .up, -[vertical] > [up], -.vertical > .down, -[vertical] > [down] { - height: auto; -} -.vertical.up, -[vertical][up] { - justify-content: flex-start; -} -.vertical.down, -[vertical][down] { - justify-content: flex-end; -} -.crow.vertical.left, -crow[vertical][left], -.crow.vertical > .left, -crow[vertical] > [left], -.crow.vertical.right, -crow[vertical][right], -.crow.vertical > .right, -crow[vertical] > [right] { - width: 100%; -} -.vertical.left, -[vertical][left] { - align-items: flex-start; -} -.vertical.right, -[vertical][right] { - align-items: flex-end; -} -.vertical > .left, -[vertical] > [left], -.vertical > .right, -[vertical] > [right] { - display: flex; -} diff --git a/src/globals.d.ts b/src/globals.d.ts deleted file mode 100644 index da82d1bb..00000000 --- a/src/globals.d.ts +++ /dev/null @@ -1,19 +0,0 @@ -// See vite.config.js in root. This project uses sveltekit-autoimport. - -interface Window { - ws: any; -} - -declare module 'async-await-websockets/client'; -declare module 'sveltekit-autoimport'; -declare module 'seedrandom'; -// declare module 'howler'; - -// declare const Howl: (typeof import('@types/howler'))['Howl']; -declare const Howler: (typeof import('howler'))['Howler']; // This solves for { Howler, Howl } for some reason -declare const tw: (typeof import('tailwind-merge'))['default']; -declare const ENV: (typeof import('@/constants/ENV_VARS'))['default']; -declare const app: (typeof import('@/app.svelte.ts'))['default']; -declare const onMount: import('svelte').LifecycleHook; -declare const onDestroy: import('svelte').LifecycleHook; -declare const tooltip: (typeof import('@/ts/use'))['tooltip']; diff --git a/src/iconice-icons.json b/src/iconice-icons.json deleted file mode 100644 index 82290df8..00000000 --- a/src/iconice-icons.json +++ /dev/null @@ -1,964 +0,0 @@ -{ - "2h": { - "elements": [ - "" - ], - "fills": [ - "#000" - ], - "strokes": [], - "viewBoxWidth": 1024, - "viewBoxHeight": 1024, - "viewBoxLeft": 0, - "viewBoxTop": 0, - "widthRatio": 1, - "heightRatio": 1, - "fill": null - }, - "checkmark": { - "elements": [ - "" - ], - "fills": [ - "#000" - ], - "strokes": [], - "viewBoxWidth": 512, - "viewBoxHeight": 512, - "viewBoxLeft": 0, - "viewBoxTop": 0, - "widthRatio": 1, - "heightRatio": 1, - "fill": null - }, - "cross": { - "elements": [ - "" - ], - "fills": [ - "#000" - ], - "strokes": [], - "viewBoxWidth": 512, - "viewBoxHeight": 512, - "viewBoxLeft": 0, - "viewBoxTop": 0, - "widthRatio": 1, - "heightRatio": 1, - "fill": null - }, - "dark": { - "elements": [ - "" - ], - "fills": [ - "#000" - ], - "strokes": [], - "viewBoxWidth": 448, - "viewBoxHeight": 448, - "viewBoxLeft": 0, - "viewBoxTop": 0, - "widthRatio": 1, - "heightRatio": 1, - "fill": null - }, - "down": { - "elements": [ - "" - ], - "fills": [ - "#000" - ], - "strokes": [], - "viewBoxWidth": 448, - "viewBoxHeight": 448, - "viewBoxLeft": 0, - "viewBoxTop": 0, - "widthRatio": 1, - "heightRatio": 1, - "fill": null - }, - "info": { - "elements": [ - "" - ], - "fills": [ - "#000" - ], - "strokes": [], - "viewBoxWidth": 640, - "viewBoxHeight": 640, - "viewBoxLeft": 0, - "viewBoxTop": 0, - "widthRatio": 1, - "heightRatio": 1, - "fill": null - }, - "error": { - "elements": [ - "" - ], - "fills": [ - "#000" - ], - "strokes": [], - "viewBoxWidth": 768, - "viewBoxHeight": 768, - "viewBoxLeft": 0, - "viewBoxTop": 0, - "widthRatio": 1, - "heightRatio": 1, - "fill": null - }, - "left": { - "elements": [ - "" - ], - "fills": [ - "#000" - ], - "strokes": [], - "viewBoxWidth": 336, - "viewBoxHeight": 448, - "viewBoxLeft": 0, - "viewBoxTop": 0, - "widthRatio": 1, - "heightRatio": 1.3333333333333333, - "fill": null - }, - "light": { - "elements": [ - "" - ], - "fills": [ - "#000" - ], - "strokes": [], - "viewBoxWidth": 768, - "viewBoxHeight": 768, - "viewBoxLeft": 0, - "viewBoxTop": 0, - "widthRatio": 1, - "heightRatio": 1, - "fill": null - }, - "logo-apeegg-simple": { - "elements": [ - "", - "" - ], - "fills": [ - "#000", - "#000" - ], - "strokes": [], - "viewBoxWidth": 340, - "viewBoxHeight": 448, - "viewBoxLeft": 0, - "viewBoxTop": 0, - "widthRatio": 1, - "heightRatio": 1.3176470588235294, - "fill": null - }, - "logo-apeegg": { - "elements": [ - "", - "" - ], - "fills": [ - "#000", - "#000" - ], - "strokes": [], - "viewBoxWidth": 777, - "viewBoxHeight": 448, - "viewBoxLeft": 0, - "viewBoxTop": 0, - "widthRatio": 1.734375, - "heightRatio": 1, - "fill": null - }, - "menu": { - "elements": [ - "" - ], - "fills": [ - "#000" - ], - "strokes": [], - "viewBoxWidth": 768, - "viewBoxHeight": 768, - "viewBoxLeft": 0, - "viewBoxTop": 0, - "widthRatio": 1, - "heightRatio": 1, - "fill": null - }, - "right": { - "elements": [ - "" - ], - "fills": [ - "#000" - ], - "strokes": [], - "viewBoxWidth": 304, - "viewBoxHeight": 448, - "viewBoxLeft": 0, - "viewBoxTop": 0, - "widthRatio": 1, - "heightRatio": 1.4736842105263157, - "fill": null - }, - "spinner-circle": { - "elements": [ - "" - ], - "fills": [ - "#000" - ], - "strokes": [], - "viewBoxWidth": 1024, - "viewBoxHeight": 1024, - "viewBoxLeft": 0, - "viewBoxTop": 0, - "widthRatio": 1, - "heightRatio": 1, - "fill": null - }, - "success": { - "elements": [ - "" - ], - "fills": [ - "#000" - ], - "strokes": [], - "viewBoxWidth": 640, - "viewBoxHeight": 640, - "viewBoxLeft": 0, - "viewBoxTop": 0, - "widthRatio": 1, - "heightRatio": 1, - "fill": null - }, - "spinner-inner": { - "elements": [ - "" - ], - "fills": [ - "#000" - ], - "strokes": [], - "viewBoxWidth": 1024, - "viewBoxHeight": 1024, - "viewBoxLeft": 0, - "viewBoxTop": 0, - "widthRatio": 1, - "heightRatio": 1, - "fill": null - }, - "warning": { - "elements": [ - "" - ], - "fills": [ - "#000" - ], - "strokes": [], - "viewBoxWidth": 512, - "viewBoxHeight": 512, - "viewBoxLeft": 0, - "viewBoxTop": 0, - "widthRatio": 1, - "heightRatio": 1, - "fill": null - }, - "up": { - "elements": [ - "" - ], - "fills": [ - "#000" - ], - "strokes": [], - "viewBoxWidth": 448, - "viewBoxHeight": 448, - "viewBoxLeft": 0, - "viewBoxTop": 0, - "widthRatio": 1, - "heightRatio": 1, - "fill": null - }, - "stun": { - "elements": [ - "" - ], - "fills": [ - "#000" - ], - "strokes": [], - "viewBoxWidth": 1018, - "viewBoxHeight": 1024, - "viewBoxLeft": 0, - "viewBoxTop": 0, - "widthRatio": 1, - "heightRatio": 1.005893909626719, - "fill": null - }, - "1h": { - "elements": [ - "" - ], - "fills": [ - "#000" - ], - "strokes": [], - "viewBoxWidth": 1014, - "viewBoxHeight": 1024, - "viewBoxLeft": 0, - "viewBoxTop": 0, - "widthRatio": 1, - "heightRatio": 1.009861932938856, - "fill": null - }, - "1h1h": { - "elements": [ - "", - "" - ], - "fills": [ - "#000", - "#000" - ], - "strokes": [], - "viewBoxWidth": 1014, - "viewBoxHeight": 1024, - "viewBoxLeft": 0, - "viewBoxTop": 0, - "widthRatio": 1, - "heightRatio": 1.009861932938856, - "fill": null - }, - "spin": { - "elements": [ - "" - ], - "fills": [ - "#000" - ], - "strokes": [], - "viewBoxWidth": 512, - "viewBoxHeight": 512, - "viewBoxLeft": 0, - "viewBoxTop": 0, - "widthRatio": 1, - "heightRatio": 1, - "fill": null - }, - "isBlocking": { - "elements": [ - "", - "" - ], - "fills": [ - "#555", - "#aaa" - ], - "strokes": [], - "viewBoxWidth": 272, - "viewBoxHeight": 357.4, - "viewBoxLeft": 0, - "viewBoxTop": 0, - "widthRatio": 1, - "heightRatio": 1.3139705882352941, - "fill": null - }, - "punch": { - "elements": [ - "", - "" - ], - "fills": [ - "#000", - "#000" - ], - "strokes": [], - "viewBoxWidth": 705, - "viewBoxHeight": 1024, - "viewBoxLeft": 0, - "viewBoxTop": 0, - "widthRatio": 0.6884765625, - "heightRatio": 1, - "fill": null - }, - "slash": { - "elements": [ - "", - "", - "" - ], - "fills": [ - "#000", - "#000", - "#000" - ], - "strokes": [], - "viewBoxWidth": 356, - "viewBoxHeight": 364, - "viewBoxLeft": 0, - "viewBoxTop": 0, - "widthRatio": 1, - "heightRatio": 1.0224719101123596, - "fill": null - }, - "stab": { - "elements": [ - "", - "" - ], - "fills": [ - "#000", - "#000" - ], - "strokes": [], - "viewBoxWidth": 346.5, - "viewBoxHeight": 348.7, - "viewBoxLeft": 0, - "viewBoxTop": 0, - "widthRatio": 1, - "heightRatio": 1.0063492063492063, - "fill": null - }, - "block": { - "elements": [ - "" - ], - "fills": [ - "#000" - ], - "strokes": [], - "viewBoxWidth": 782, - "viewBoxHeight": 1024, - "viewBoxLeft": 0, - "viewBoxTop": 0, - "widthRatio": 0.763671875, - "heightRatio": 1, - "fill": null - }, - "cheese": { - "elements": [ - "", - "", - "" - ], - "fills": [ - "#f7d857", - "#ccb34c", - "#ccb34c" - ], - "strokes": [], - "viewBoxWidth": 342, - "viewBoxHeight": 293.1, - "viewBoxLeft": 0, - "viewBoxTop": 0, - "widthRatio": 1.1668372569089047, - "heightRatio": 1, - "fill": null - }, - "bite": { - "elements": [ - "" - ], - "fills": [ - "#000" - ], - "strokes": [], - "viewBoxWidth": 342.4, - "viewBoxHeight": 336, - "viewBoxLeft": 0, - "viewBoxTop": 0, - "widthRatio": 1.019047619047619, - "heightRatio": 1, - "fill": null - }, - "slam": { - "elements": [ - "", - "" - ], - "fills": [ - "#000", - "#000" - ], - "strokes": [], - "viewBoxWidth": 201.2, - "viewBoxHeight": 149.8, - "viewBoxLeft": 0, - "viewBoxTop": 0, - "widthRatio": 1.343124165554072, - "heightRatio": 1, - "fill": null - }, - "shieldBash": { - "elements": [ - "", - "" - ], - "fills": [ - "#000", - "#000" - ], - "strokes": [], - "viewBoxWidth": 671.6, - "viewBoxHeight": 567.1, - "viewBoxLeft": 0, - "viewBoxTop": 0, - "widthRatio": 1.1842708517016398, - "heightRatio": 1, - "fill": null - }, - "bowshot": { - "elements": [ - "", - "" - ], - "fills": [ - "#000", - "#000" - ], - "strokes": [], - "viewBoxWidth": 345.7, - "viewBoxHeight": 347, - "viewBoxLeft": 0, - "viewBoxTop": 0, - "widthRatio": 1, - "heightRatio": 1.0037604859704947, - "fill": null - }, - "lacerate": { - "elements": [ - "", - "" - ], - "fills": [ - "#000", - "#000" - ], - "strokes": [], - "viewBoxWidth": 345.8, - "viewBoxHeight": 350.3, - "viewBoxLeft": 0, - "viewBoxTop": 0, - "widthRatio": 1, - "heightRatio": 1.0130133024869867, - "fill": null - }, - "kick": { - "elements": [ - "", - "" - ], - "fills": [ - "#000", - "#000" - ], - "strokes": [], - "viewBoxWidth": 772.8, - "viewBoxHeight": 803.3, - "viewBoxLeft": 0, - "viewBoxTop": 0, - "widthRatio": 1, - "heightRatio": 1.0394668737060042, - "fill": null - }, - "whirlwind": { - "elements": [ - "", - "", - "", - "" - ], - "fills": [ - "#000", - "#000", - "#000", - "#000" - ], - "strokes": [], - "viewBoxWidth": 345.1, - "viewBoxHeight": 348, - "viewBoxLeft": 0, - "viewBoxTop": 0, - "widthRatio": 1, - "heightRatio": 1.0084033613445378, - "fill": null - }, - "fillArmor": { - "elements": [ - "", - "" - ], - "fills": [ - "#000", - "#000" - ], - "strokes": [], - "viewBoxWidth": 299.2, - "viewBoxHeight": 339.9, - "viewBoxLeft": 0, - "viewBoxTop": 0, - "widthRatio": 1, - "heightRatio": 1.1360294117647058, - "fill": null - }, - "claw": { - "elements": [ - "" - ], - "fills": [ - "#000" - ], - "strokes": [], - "viewBoxWidth": 1182, - "viewBoxHeight": 1024, - "viewBoxLeft": 0, - "viewBoxTop": 0, - "widthRatio": 1.154296875, - "heightRatio": 1, - "fill": null - }, - "demoShout": { - "elements": [ - "", - "" - ], - "fills": [ - "#000", - "#000" - ], - "strokes": [], - "viewBoxWidth": 340.3, - "viewBoxHeight": 332.2, - "viewBoxLeft": 0, - "viewBoxTop": 0, - "widthRatio": 1.0243829018663457, - "heightRatio": 1, - "fill": null - }, - "damage": { - "elements": [ - "", - "" - ], - "fills": [ - "#15970f", - "#15970f" - ], - "strokes": [], - "viewBoxWidth": 1014, - "viewBoxHeight": 1024, - "viewBoxLeft": 0, - "viewBoxTop": 0, - "widthRatio": 1, - "heightRatio": 1.009861932938856, - "fill": null - }, - "victory": { - "elements": [ - "" - ], - "fills": [ - "gold" - ], - "strokes": [], - "viewBoxWidth": 981, - "viewBoxHeight": 1024, - "viewBoxLeft": 0, - "viewBoxTop": 0, - "widthRatio": 0.9580078125, - "heightRatio": 1, - "fill": null - }, - "defeat": { - "elements": [ - "" - ], - "fills": [ - "#000" - ], - "strokes": [], - "viewBoxWidth": 640, - "viewBoxHeight": 640, - "viewBoxLeft": 0, - "viewBoxTop": 0, - "widthRatio": 1, - "heightRatio": 1, - "fill": null - }, - "maxArmor": { - "elements": [ - "", - "" - ], - "fills": [ - "#335cc1", - "#335cc1" - ], - "strokes": [], - "viewBoxWidth": 1034, - "viewBoxHeight": 1024, - "viewBoxLeft": 0, - "viewBoxTop": 0, - "widthRatio": 1.009765625, - "heightRatio": 1, - "fill": null - }, - "maxHealth": { - "elements": [ - "" - ], - "fills": [ - "#9f0712" - ], - "strokes": [], - "viewBoxWidth": 1106, - "viewBoxHeight": 1024, - "viewBoxLeft": 0, - "viewBoxTop": 0, - "widthRatio": 1.080078125, - "heightRatio": 1, - "fill": null - }, - "coin": { - "elements": [ - "", - "", - "" - ], - "fills": [ - "#000", - "#000", - "#000" - ], - "strokes": [], - "viewBoxWidth": 346.8, - "viewBoxHeight": 346.4, - "viewBoxLeft": 0, - "viewBoxTop": 0, - "widthRatio": 1.0011547344110856, - "heightRatio": 1, - "fill": null - }, - "magicChance": { - "elements": [ - "", - "", - "" - ], - "fills": [ - "#000", - "#000", - "#000" - ], - "strokes": [], - "viewBoxWidth": 742, - "viewBoxHeight": 640, - "viewBoxLeft": 0, - "viewBoxTop": 0, - "widthRatio": 1.159375, - "heightRatio": 1, - "fill": null - }, - "dice": { - "elements": [ - "" - ], - "fills": [ - "#000" - ], - "strokes": [], - "viewBoxWidth": 636, - "viewBoxHeight": 640, - "viewBoxLeft": 0, - "viewBoxTop": 0, - "widthRatio": 1, - "heightRatio": 1.0062893081761006, - "fill": null - }, - "dodgeChance": { - "elements": [ - "" - ], - "fills": [ - "#000" - ], - "strokes": [], - "viewBoxWidth": 646, - "viewBoxHeight": 640, - "viewBoxLeft": 0, - "viewBoxTop": 0, - "widthRatio": 1.009375, - "heightRatio": 1, - "fill": null - }, - "criticalChance": { - "elements": [ - "", - "" - ], - "fills": [ - "#000", - "#000" - ], - "strokes": [], - "viewBoxWidth": 586, - "viewBoxHeight": 640, - "viewBoxLeft": 0, - "viewBoxTop": 0, - "widthRatio": 1, - "heightRatio": 1.0921501706484642, - "fill": null - }, - "criticalDamage": { - "elements": [ - "" - ], - "fills": [ - "#000" - ], - "strokes": [], - "viewBoxWidth": 636, - "viewBoxHeight": 640, - "viewBoxLeft": 0, - "viewBoxTop": 0, - "widthRatio": 1, - "heightRatio": 1.0062893081761006, - "fill": null - }, - "bleeding": { - "elements": [ - "", - "" - ], - "fills": [ - "#e20000", - "#f85656" - ], - "strokes": [], - "viewBoxWidth": 713, - "viewBoxHeight": 1024, - "viewBoxLeft": 0, - "viewBoxTop": 0, - "widthRatio": 0.6962890625, - "heightRatio": 1, - "fill": null - }, - "concussed": { - "elements": [ - "", - "" - ], - "fills": [ - "#ab31c9", - "#6d219e" - ], - "strokes": [], - "viewBoxWidth": 1024, - "viewBoxHeight": 1024, - "viewBoxLeft": 0, - "viewBoxTop": 0, - "widthRatio": 1, - "heightRatio": 1, - "fill": null - }, - "exposed": { - "elements": [ - "", - "" - ], - "fills": [ - "#6c7cd8", - "#2b3eaf" - ], - "strokes": [], - "viewBoxWidth": 1024, - "viewBoxHeight": 1024, - "viewBoxLeft": 0, - "viewBoxTop": 0, - "widthRatio": 1, - "heightRatio": 1, - "fill": null - }, - "stunned": { - "elements": [ - "", - "" - ], - "fills": [ - "#ab31c9", - "#6d219e" - ], - "strokes": [], - "viewBoxWidth": 996, - "viewBoxHeight": 1024, - "viewBoxLeft": 0, - "viewBoxTop": 0, - "widthRatio": 0.97265625, - "heightRatio": 1, - "fill": null - }, - "vulnerable": { - "elements": [ - "", - "" - ], - "fills": [ - "#2b3eaf", - "#6c7cd8" - ], - "strokes": [], - "viewBoxWidth": 786, - "viewBoxHeight": 1024, - "viewBoxLeft": 0, - "viewBoxTop": 0, - "widthRatio": 0.767578125, - "heightRatio": 1, - "fill": null - }, - "wounded": { - "elements": [ - "", - "" - ], - "fills": [ - "#f85656", - "#e20000" - ], - "strokes": [], - "viewBoxWidth": 1024, - "viewBoxHeight": 1024, - "viewBoxLeft": 0, - "viewBoxTop": 0, - "widthRatio": 1, - "heightRatio": 1, - "fill": null - }, - "blockChance": { - "elements": [ - "" - ], - "fills": [ - "#000" - ], - "strokes": [], - "viewBoxWidth": 782, - "viewBoxHeight": 1024, - "viewBoxLeft": 0, - "viewBoxTop": 0, - "widthRatio": 0.763671875, - "heightRatio": 1, - "fill": null - } -} \ No newline at end of file diff --git a/src/routes/+error.svelte b/src/routes/+error.svelte deleted file mode 100644 index 144ad83c..00000000 --- a/src/routes/+error.svelte +++ /dev/null @@ -1,10 +0,0 @@ - - -

{status}: {error?.message}

diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte deleted file mode 100644 index 5bd08a9e..00000000 --- a/src/routes/+layout.svelte +++ /dev/null @@ -1,371 +0,0 @@ - - - - - - - - -{#if !IS_PROD} - - -{/if} - -
-
- -
-
-
- {#if app.elapsedMilliseconds < app.combat.duration} - (app.overlay = 'Combat')} class="crow gap-2"> - -
Fightning..
-
- {:else if app.combat.duration !== 0} - (app.overlay = 'Combat')} class="crow gap-2"> -
View outcome
-
- {/if} -
- - - level {getLevelByExperience(app.experience)} - - - - - - Coins - {#if app.coins === 0} - - - {:else} - - {/if} - - - - - Experience - - - - {#if getLevelByExperience(app.experience) < 25} - - {/if} - - - - - Medical area - - - - - - -
- - - - {app.characters.length} / {allowedNumberOfCharacters()} - - - -
(showSequence = true)} - onmouseleave={() => (showSequence = false)} - role="none" - > - - {#if app.characters.length === 0} - - You have no brawlers yet. - - - {:else} - {#each app.characters as char, i (char.uuid)} - {@const character = CHARACTERS(char, true)} - {@const abilitiesHydrated = character.abilities.map((ability) => - ABILITIES(ability, true) - )} - {@const isActive = !!( - parseInt(characterIndex, 10) === i || - (app.maxBrawlers && app.selectedBrawlers.includes(char.uuid)) - )} - selectBrawler(e, char.uuid)} - > - -
-
- -
-
- -
- -
- - -
- - calculateTickStart(abilitiesHydrated, i) < character.maxTicks - )} - /> -
-
-
-
-
- {/each} - {/if} -
-
-
- -
- -
-
- {@render children()} -
-
-
-
-
-
- - - - - Game Guide - - - The Arena - - - Random duel - - - Vendor - - - Brawlers - - - -
-
-
- - - -
-
-
-
-
- - - - - - - - -{#if app.tooltip} - -{/if} - - - diff --git a/src/routes/ability-scaling/+page.svelte b/src/routes/ability-scaling/+page.svelte deleted file mode 100644 index 4e7034a9..00000000 --- a/src/routes/ability-scaling/+page.svelte +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - (chosenAbility = value)} -/> - -{#each abilitiesPool as abilities, i (i)} - -{/each} - - diff --git a/src/routes/brawlers/+page.svelte b/src/routes/brawlers/+page.svelte deleted file mode 100644 index a8062dea..00000000 --- a/src/routes/brawlers/+page.svelte +++ /dev/null @@ -1,113 +0,0 @@ - - - - - - {#if !app.characters.length} - Select your first brawler - {:else if characterCapped} - Level up your ludus in order
to recruit new brawlers - {:else} - Select a brawler - {/if} -
- - - {#each characters as char} - {@const character = CHARACTERS(char, true)} - {@const isRecruited = app.characters.find(({ id }) => id === character.id)} - {@const { primary, secondary } = ALL_ELEMENTS[character.element].color} - - {@const combatStats = character.combatStats} - {@const luckyStats = Object.entries(deepSubtract(combatStats, DEFAULT_LUCKY_STATS)).filter( - ([key, value]) => - ['criticalChance', 'criticalDamage', 'blockChance', 'dodgeChance', 'magicChance'].includes( - key - ) && value !== 0 - )} - {@const modifierStats = Object.entries( - deepSubtract(character.combatStats.modifiers, DEFAULT_MODIFIERS) - ).filter( - ([key, value]) => - ['maxHealth', 'maxArmor', 'damage', 'resistance'].includes(key) && value !== 0 - )} - - isRecruited - ? notify({ warning: `${character.name} is already recruited` }) - : pickCharacter(char)} - style="border: 0.5px solid {primary}; background: linear-gradient(135deg, {primary}20, {secondary}20);" - > -
{character.name}
-
- - -
- - - - - - - - - - -
-
- -
- {/each} -
diff --git a/src/routes/brawlers/[characterIndex]/+page.svelte b/src/routes/brawlers/[characterIndex]/+page.svelte deleted file mode 100644 index ab785612..00000000 --- a/src/routes/brawlers/[characterIndex]/+page.svelte +++ /dev/null @@ -1,148 +0,0 @@ - - -{#snippet RetireInfo()} -
Only possible until level 5
-{/snippet} - - history.back()} /> - - - - - - - - - - {#if getLevelByExperience(app.experience) <= 4} - - - - - {/if} - - - - - - - - - - - - - - - -
- - - - - - ['maxHealth', 'maxArmor', 'damage'].includes(key) - )} - /> - -
- - - - - [ - 'criticalChance', - 'criticalDamage', - 'blockChance', - 'dodgeChance', - 'magicChance' - ].includes(key) - )} - /> - - - - - - ['maxHealth', 'maxArmor', 'damage', 'resistance'].includes(key) - )} - /> - -
- - - - - ['wounded', 'concussed', 'exposed'].includes(key) - )} - /> - - -
- - - -
- - - - - - -
- - diff --git a/src/routes/character-scaling/+page.svelte b/src/routes/character-scaling/+page.svelte deleted file mode 100644 index edf1affa..00000000 --- a/src/routes/character-scaling/+page.svelte +++ /dev/null @@ -1,101 +0,0 @@ - - - - - -
- (characterScaling = checked)} - > - Character scaling - -
-
- (equipmentScaling = checked)} - > - Equipment scaling - -
-
- - (chosenCharacter = value)} -/> - -{#each characterPool as character, i (`${character.id}_${i}`)} - {@const hydratedCharacter = { - ...CHARACTER(character, true), - combatStats: calculateCombatStatsByCharacter(CHARACTER(character, true)) - }} - - - level {i + 1} - - - - - -{/each} - - - - - - - diff --git a/src/routes/components/+page.svelte b/src/routes/components/+page.svelte deleted file mode 100644 index 47ba5156..00000000 --- a/src/routes/components/+page.svelte +++ /dev/null @@ -1,79 +0,0 @@ - - - -
-

Input

- - -
-
-

Checkbox

- Check me -
-
-

Dropdown

- - -
-
-

Button (active)

- - - - - -
-
-

Button (disabled)

- - - - - -
-
-

Toasts

- - - - - - -
-
-

Pills

- - - - - -
-
diff --git a/src/routes/crow/+page.svelte b/src/routes/crow/+page.svelte deleted file mode 100644 index effd714e..00000000 --- a/src/routes/crow/+page.svelte +++ /dev/null @@ -1,82 +0,0 @@ - - - - -
Layout summary
- Side-by-side flex helpers -
- - - -
Horizontal rows
- - Left - Right - - - Aligned - Centers - -
- - -
Stacked columns
- - Top - Start - - - Middle - Center - - - Bottom - End - -
-
- -
- - - - - Card A - portrait - - - - Power - 45 - - - Morale - High - - - - - - - - Column - flex-1 demo - - - - A - 1 - - - B - 2 - - - - - Footer - Aligned - - - -
diff --git a/src/routes/debug/+page.svelte b/src/routes/debug/+page.svelte deleted file mode 100644 index 6a930c09..00000000 --- a/src/routes/debug/+page.svelte +++ /dev/null @@ -1,84 +0,0 @@ - - - - Teams {teamCount}: - - - Combatants {combatantCount}: - - - - - - -{#if app.combat.teamsStartState.length} - -{/if} diff --git a/src/routes/equipment-scaling/+page.svelte b/src/routes/equipment-scaling/+page.svelte deleted file mode 100644 index e59e015b..00000000 --- a/src/routes/equipment-scaling/+page.svelte +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - (chosenEquipment = value)} -/> - -{#each equipmentPool as equipment, i (`${equipment.id}_${i}`)} - {@const hydratedEquipment = EQUIPMENT(equipment, true)} - - {#if i === 0} - base - {:else} - level {i} - {/if} - - -{/each} - - diff --git a/src/routes/random-duel/+page.svelte b/src/routes/random-duel/+page.svelte deleted file mode 100644 index 6def58d7..00000000 --- a/src/routes/random-duel/+page.svelte +++ /dev/null @@ -1,56 +0,0 @@ - - -
- - - - - - - - - - ? - - -
- - diff --git a/src/routes/reset-password/[secret]/+page.svelte b/src/routes/reset-password/[secret]/+page.svelte deleted file mode 100644 index f3e61f8e..00000000 --- a/src/routes/reset-password/[secret]/+page.svelte +++ /dev/null @@ -1,46 +0,0 @@ - - -

Password reset

- -{#if error} - -{:else if error === false} -
- - -
-{/if} diff --git a/src/routes/the-arena/+page.svelte b/src/routes/the-arena/+page.svelte deleted file mode 100644 index de15c3ba..00000000 --- a/src/routes/the-arena/+page.svelte +++ /dev/null @@ -1,154 +0,0 @@ - - - - - - -
name
-
level
-
loot
- - {#if !IS_PROD} -
win
- {/if} - - - - -
- {#each fights as { characters, id, name, minLevel, maxLevel, boss }, i} - {@const wins = runCombatSimulations( - SIMULATION_COUNT, - [$state.snapshot(app.characters[0] as Character)], - characters, - undefined, - id - )} - {@const isLocked = hideTreshold < minLevel} - {@const bossCompleted = boss && app.bossHighscore >= minLevel} - - { - if (isLocked || bossCompleted) { - e.preventDefault(); - return; - } - }} - class={tw( - 'crow left w-full gap-4 px-6 py-2 text-sm transition-all duration-500', - i % 2 === 0 ? 'bg-stone-50' : 'bg-stone-100', - boss && 'boss mt-12 py-8', - !isLocked && - !bossCompleted && - 'hover:bg-gradient-to-r hover:from-transparent hover:via-stone-200 hover:to-transparent active:from-stone-100 active:via-stone-50 active:to-stone-100', - (isLocked || bossCompleted) && 'cursor-default', - isLocked && 'opacity-50' - )} - > - - {isLocked ? '?' : name} - {#if boss} - - {/if} - -
{minLevel} {minLevel === maxLevel ? '' : `- ${maxLevel}`}
-
-
maxLevel && 'text-gray-400 line-through')}> - {getExperienceReward(characters.length, minLevel, maxLevel, boss)} XP -
-
- {#if !IS_PROD} -
{(wins / SIMULATION_COUNT) * 100}%
- {/if} - - - {#each characters as character} - {@const { image } = CHARACTERS(character, true)} - -
- - {#if isLocked} - - ? - - {:else} - - {#if bossCompleted} - - - - {/if} - {/if} - -
-
- {/each} -
-
- {/each} -
- - diff --git a/src/routes/the-arena/[fightId]/+page.svelte b/src/routes/the-arena/[fightId]/+page.svelte deleted file mode 100644 index 33929139..00000000 --- a/src/routes/the-arena/[fightId]/+page.svelte +++ /dev/null @@ -1,161 +0,0 @@ - - - goto('/the-arena')} /> - - - - - - {#if fight.boss} - - - {/if} - - - {#if !IS_PROD} - {(runCombatSimulations( - 10, - [$state.snapshot(app.characters[0] as Character)], - characters, - undefined, - fight.id - ) / - 10) * - 100}% win chance - {/if} - - - - - - -{#each characters as character} - {@const creature = CHARACTERS(character, true)} - {@const combatStats = calculateCombatStatsByCharacter(creature)} - - - - - - - - - - -
- - - - - {#if !IS_PROD && false} - - - - - [ - 'criticalChance', - 'criticalDamage', - 'blockChance', - 'dodgeChance', - 'magicChance' - ].includes(key) - )} - /> - - {/if} - {@html creature.description} -
-
- - - -
Abilities
-
-
- -
-
- - - - -
-{/each} - -
- - - - diff --git a/src/routes/vendor/+page.svelte b/src/routes/vendor/+page.svelte deleted file mode 100644 index fef954b6..00000000 --- a/src/routes/vendor/+page.svelte +++ /dev/null @@ -1,69 +0,0 @@ - - - - - - - - -
Name
-
Slots in
- -
Cost
-
-
- {#each filteredEquipment as item, i (item.uuid)} - - - - - - -
{slotsInPrettyName(item.slotsIn)}
- -
- -
- - - -
- {/each} -
diff --git a/src/ts/abilityEntity.ts b/src/ts/abilityEntity.ts deleted file mode 100644 index 71f792f9..00000000 --- a/src/ts/abilityEntity.ts +++ /dev/null @@ -1,17 +0,0 @@ -import ABILITIES from '@/constants/ABILITIES'; - -const GET = (entities: any, id: string, uuid?: string, fullBody: boolean = false) => ({ - uuid: uuid || crypto.randomUUID(), - id, - ...(fullBody ? entities[id] : fullBody) -}); - -export default { - ability: (id: string | any, fullBody: boolean = false) => - GET( - ABILITIES, - typeof id === 'string' ? id : id.id, - typeof id === 'string' ? undefined : id.uuid, - fullBody - ) -}; diff --git a/src/ts/actions.ts b/src/ts/actions.ts deleted file mode 100644 index 46e239a0..00000000 --- a/src/ts/actions.ts +++ /dev/null @@ -1,30 +0,0 @@ -import app from '@/app.svelte'; - -export const disableGameKeyboard = () => { - app.gameKeyboardDisabled = true; -}; -export const enableGameKeyboard = () => { - app.gameKeyboardDisabled = false; -}; -export const notify = (payload: any) => { - const { error, warning, success, info } = payload; - - const message = error || warning || success || info; - - if (app.notifications.find((string) => string.includes(message))) { - return true; - } - - if (error) app.notifications.push(JSON.stringify({ type: 'error', message })); - if (warning) app.notifications.push(JSON.stringify({ type: 'warning', message })); - if (success) app.notifications.push(JSON.stringify({ type: 'success', message })); - if (info) app.notifications.push(JSON.stringify({ type: 'info', message })); - - return true; -}; -export const removeFirstNotification = () => { - const [_first, ...rest] = app.notifications; - app.notifications = [...rest]; - - return true; -}; diff --git a/src/ts/coin.ts b/src/ts/coin.ts deleted file mode 100644 index f73a8bfd..00000000 --- a/src/ts/coin.ts +++ /dev/null @@ -1,9 +0,0 @@ -export const formatCoins = (amount: number) => { - // const gold = Math.floor(amount / 10000); - // const silver = Math.floor((amount % 10000) / 100); - const gold = 0; - const silver = Math.floor(amount / 100); - const copper = amount % 100; - - return { gold, silver, copper }; -}; diff --git a/src/ts/customEvent.ts b/src/ts/customEvent.ts deleted file mode 100644 index 1b722551..00000000 --- a/src/ts/customEvent.ts +++ /dev/null @@ -1,6 +0,0 @@ -export default (event: string, detail: any) => - document.dispatchEvent( - new CustomEvent(event, { - detail - }) - ); diff --git a/src/ts/dialog.ts b/src/ts/dialog.ts deleted file mode 100644 index 7488f2ce..00000000 --- a/src/ts/dialog.ts +++ /dev/null @@ -1,13 +0,0 @@ -import app from '@/app.svelte'; -import type { Component, ComponentProps } from 'svelte'; - -export type Dialog = { - Component: Component; - props: ComponentProps; -}; - -export const confirmWithDialog = (Component: Component, props: ComponentProps) => - (app.dialog = { - Component, - props - }); diff --git a/src/ts/entity.ts b/src/ts/entity.ts deleted file mode 100644 index 0c090d29..00000000 --- a/src/ts/entity.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { deepMerge } from '@/helpers'; -import type { DynamicObject } from '@/types/common'; - -export default ( - entities: any, - id: string, - uuid?: string, - fullBody: boolean = false, - overrides: DynamicObject = {} -) => { - return { - ...deepMerge( - { - uuid: uuid || crypto.randomUUID(), - id, - ...(fullBody ? structuredClone(entities[id]) : undefined) - }, - fullBody ? overrides : {} - ), - ...(JSON.stringify(overrides) !== '{}' ? { overrides } : {}) - }; -}; diff --git a/src/ts/level.ts b/src/ts/level.ts deleted file mode 100644 index b7d8eb24..00000000 --- a/src/ts/level.ts +++ /dev/null @@ -1,48 +0,0 @@ -import app from '@/app.svelte'; - -const SCALING = 1.6; -const MAX_LEVEL = 25; - -export const getLevelByExperience = (experience: number) => { - return Math.min(Math.floor(Math.pow(experience / 100, 1 / SCALING)) + 1, MAX_LEVEL); -}; - -export const getExperienceByLevel = (level: number) => { - return Math.pow(level - 1, SCALING) * 100; -}; - -export const getExperienceForNextLevel = (currentLevel: number) => { - const xpCurrentLevel = getExperienceByLevel(currentLevel); - const xpNextLevel = getExperienceByLevel(currentLevel + 1); - return Math.floor(xpNextLevel - xpCurrentLevel); -}; - -export const getCurrentExperienceAtLevel = (experience: number) => { - const currentLevel = getLevelByExperience(experience); - const xpForCurrentLevel = getExperienceByLevel(currentLevel); - return Math.ceil(experience - xpForCurrentLevel); -}; - -export const allowedNumberOfCharacters = () => { - return Math.floor(app.accountRewards / 5) + 1; -}; - -export const getExperienceRangeForLevel = (level: number) => { - const minXp = getExperienceByLevel(level) + 1; - let maxXp = getExperienceByLevel(level + 1); - - if (level + 1 > MAX_LEVEL) { - maxXp = Infinity; - } - - return { minXp: Math.floor(minXp), maxXp: Math.floor(maxXp) }; -}; - -export const getExperienceReward = ( - numberOfEnemies: number, - minLevel: number, - maxLevel: number, - boss: boolean -) => { - return 40 * numberOfEnemies * (boss ? 10 : 1) + 40 * Math.ceil((minLevel + maxLevel) / 2) * 0.2; -}; diff --git a/src/ts/loadLocalStorage.ts b/src/ts/loadLocalStorage.ts deleted file mode 100644 index d0672f72..00000000 --- a/src/ts/loadLocalStorage.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { browser } from '$app/environment'; -import type { DynamicObject } from '@/types/common'; - -export default (props: DynamicObject) => - Object.entries(props).reduce( - (a, [key, value]) => ({ - ...a, - [key]: - browser && window.localStorage.getItem(key) - ? JSON.parse(window.localStorage.getItem(key) as string) - : value - }), - {} - ); diff --git a/src/ts/mediaQuery.ts b/src/ts/mediaQuery.ts deleted file mode 100644 index 2d58ceb7..00000000 --- a/src/ts/mediaQuery.ts +++ /dev/null @@ -1,34 +0,0 @@ -import type { DynamicObject } from '@/types/common'; -import { writable } from 'svelte/store'; - -const calculateMedia = (mqls: any) => { - const media: DynamicObject = { classes: '' }; - const mediaClasses: any[] = []; - Object.keys(mqls).forEach((key: any) => { - media[key] = mqls[key].matches; - if (media[key]) { - mediaClasses.push(key); - } - }); - - media.classes = mediaClasses.join(' '); - return media; -}; - -export default (queries: DynamicObject) => - writable({}, (set) => { - if (typeof window === 'undefined') return; - - const mqls: DynamicObject = {}; - const updateMedia = () => set(calculateMedia(mqls)); - - Object.keys(queries).forEach((key: any) => { - mqls[key] = window.matchMedia(queries[key]); - mqls[key].addEventListener('change', updateMedia); - }); - - updateMedia(); - - return () => - Object.keys(mqls).forEach((key) => mqls[key].removeEventListener('change', updateMedia)); - }); diff --git a/src/ts/use.ts b/src/ts/use.ts deleted file mode 100644 index 2f2b72db..00000000 --- a/src/ts/use.ts +++ /dev/null @@ -1,259 +0,0 @@ -import app from '@/app.svelte'; - -export type Tooltip = { - children: () => any; - props: any; - x: number; - y: number; - direction?: 'up' | 'down' | 'left' | 'right'; - lockInPlace: boolean; - targetX: number; - targetY: number; - visible: boolean; -}; - -const tooltip = (node: Node, item: any) => { - const mouseEnter = (event: MouseEvent) => { - const rect = (node as HTMLElement).getBoundingClientRect(); - - let targetX = rect.left + rect.width / 2; - let targetY = rect.top + rect.height / 2; - - if (item?.direction === 'up') targetY = rect.top; - if (item?.direction === 'down') targetY = rect.top + rect.height; - if (item?.direction === 'right') targetX = rect.left + rect.width; - if (item?.direction === 'left') targetX = rect.left; - - const { clientX, clientY } = event; - - app.tooltip = { - x: clientX, - y: clientY, - targetX, - targetY, - visible: item?.props?.visible !== undefined ? item.props.visible : true, - ...item, - props: { ...item.props } // strip reference - }; - - document.addEventListener('mousemove', mouseMove as EventListener); - }; - - const mouseMove = (event: MouseEvent) => { - if (!app.tooltip) return; - app.tooltip.x = event.clientX; - app.tooltip.y = event.clientY; - }; - - const mouseLeave = () => { - if (!app.tooltip) return; - app.tooltip.visible = false; - }; - - node.addEventListener('mouseenter', mouseEnter as EventListener); - node.addEventListener('mouseleave', mouseLeave as EventListener); - - return { - destroy: () => { - node.removeEventListener('mouseenter', mouseEnter as EventListener); - document.removeEventListener('mousemove', mouseMove as EventListener); - node.removeEventListener('mouseleave', mouseLeave as EventListener); - } - }; -}; - -// let itemBeingDragged; -// let clone; -// const drops = []; -// let x, y; - -// const drag = (node, item) => { -// if (!item?.dragFrom) return; - -// node.style.touchAction = 'none'; - -// const end = (event) => { -// const { which, type } = event; -// if (which !== 1 && type !== 'touchend') return; - -// node.style.pointerEvents = 'auto'; - -// if (itemBeingDragged) { -// let failed = true; -// drops.forEach((drop) => { -// const { left, top, width, height } = drop.getBoundingClientRect(); -// if ( -// x > left && -// x < left + width && -// y > top && -// y < top + height && -// !itemBeingDragged?.dropped -// ) { -// drop.dispatchEvent(new CustomEvent('attemptDrop')); -// failed = false; -// itemBeingDragged.dropped = true; -// } else { -// drop.dispatchEvent(new CustomEvent('outside')); -// } -// }); - -// if (failed) { -// // TODO: remove this in the future if no bug seems to be related to it -// // requestAnimationFrame(() => -// // ); -// node.dispatchEvent( -// new CustomEvent('dropOutside', { -// detail: document.elementFromPoint(x, y) -// }) -// ); -// } - -// itemBeingDragged = undefined; -// document.body.removeChild(clone); -// node.style.opacity = 1; -// } - -// node.dispatchEvent( -// new CustomEvent('dragging', { -// detail: { -// dragging: false, -// dragTarget: undefined, -// dragType: type -// } -// }) -// ); - -// window.removeEventListener('mousemove', move); -// window.removeEventListener('mouseup', end); -// window.removeEventListener('touchmove', move); -// window.removeEventListener('touchend', end, true); -// }; - -// const move = (event) => { -// const { type, touches, clientX, clientY, which } = event; -// if (which !== 1 && type !== 'touchmove') return; -// event.preventDefault(); - -// x = type === 'mousemove' ? clientX : touches[0].clientX; -// y = type === 'mousemove' ? clientY : touches[0].clientY; - -// node.style.pointerEvents = 'none'; -// node.dispatchEvent( -// new CustomEvent('dragging', { -// detail: { -// dragging: true, -// dragTarget: document.elementFromPoint(x, y), -// dragType: type -// } -// }) -// ); - -// if (!itemBeingDragged) { -// itemBeingDragged = { ...item }; // Clone it otherwise `itemBeingDragged.dropped` won't reset on `itemBeingDragged = undefined` -// clone = node.cloneNode(true); -// document.body.appendChild(clone); -// node.style.opacity = 0; -// clone.style.position = 'fixed'; -// const { top, left } = node.getBoundingClientRect(); -// clone.style.top = `${top - y}px`; -// clone.style.left = `${left - x}px`; -// clone.style.zIndex = 25; -// } - -// clone.style.transform = `translate(${x}px, ${y}px)`; - -// drops.forEach((drop) => { -// const { left, top, width, height } = drop.getBoundingClientRect(); -// if (x > left && x < left + width && y > top && y < top + height) { -// drop.dispatchEvent(new CustomEvent('inside')); -// } else { -// drop.dispatchEvent(new CustomEvent('outside')); -// } -// }); -// }; - -// const start = (event) => { -// const { which, ctrlKey, type } = event; - -// if ((which !== 1 || ctrlKey) && type !== 'touchstart') return; -// if (type !== 'touchstart') event.preventDefault(); // TODO: figure out if we need this for smartphone aswell? -// node.dispatchEvent( -// new CustomEvent('dragging', { -// detail: { -// dragging: true, -// dragTarget: undefined, -// dragType: type -// } -// }) -// ); -// window.addEventListener('mousemove', move); -// window.addEventListener('mouseup', end); -// window.addEventListener('touchmove', move); -// window.addEventListener('touchend', end, true); // useCapture = true to make sure this events fires before any local `touchend`'s -// }; - -// node.addEventListener('touchstart', start); -// node.addEventListener('mousedown', start); - -// return { -// destroy: () => { -// node.removeEventListener('touchstart', start); -// node.removeEventListener('mousedown', start); -// } -// }; -// }; - -// const drop = (node, droppable) => { -// drops.push(node); - -// const inside = () => -// node.dispatchEvent( -// new CustomEvent('hover', { -// detail: { -// canDrop: droppable(itemBeingDragged), -// }, -// }), -// ); - -// const outside = () => -// node.dispatchEvent( -// new CustomEvent('hover', { -// detail: { -// canDrop: undefined, -// }, -// }), -// ); - -// const attemptDrop = () => { -// if (droppable(itemBeingDragged)) { -// node.dispatchEvent( -// new CustomEvent('dropped', { -// detail: { -// item: itemBeingDragged, -// }, -// }), -// ); -// } else { -// node.dispatchEvent( -// new CustomEvent('hover', { -// detail: { -// canDrop: undefined, -// }, -// }), -// ); -// } -// }; - -// node.addEventListener('inside', inside); -// node.addEventListener('outside', outside); -// node.addEventListener('attemptDrop', attemptDrop); -// return { -// destroy: () => { -// node.removeEventListener('inside', inside); -// node.removeEventListener('outside', outside); -// node.removeEventListener('attemptDrop', attemptDrop); -// }, -// }; -// }; - -export { tooltip }; diff --git a/src/types/ability.ts b/src/types/ability.ts deleted file mode 100644 index ebf4520e..00000000 --- a/src/types/ability.ts +++ /dev/null @@ -1,55 +0,0 @@ -import type { IconName } from '@/Iconice'; -import type { VFX } from '@/types/vfx'; -import type { DynamicObject } from '@/types/common'; -import type { SFX } from '@/types/sfx'; - -export enum AbilityType { - WindUp = 'windUp', - WindDown = 'windDown', - Channeling = 'channeling' -} - -export type StatusEffect = - | 'isBleeding' - | 'isHealed' - | 'isStunned' - | 'isVulnerable' - | 'isWounded' - | 'isConcussed' - | 'isExposed'; - -type AbilityTicks = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12; // 0 is for removal, see `prepareCombatant` in `generateCombat` - -export type AbilityRef = { - uuid?: string; - id: string; - overrides?: DynamicObject; -}; - -export type Ability = AbilityRef & { - name: string; - type: AbilityType; - description: string; - basic: boolean; - ticks: AbilityTicks; - chainLink?: number; - chainTo?: number; - start?: number; - end?: number; - icon: IconName; - vfx: VFX; - sfx: SFX; - statusEffects: StatusEffect[]; - - damage: number; - healing: number; - duration: number; - damageModifier: number; - healingModifier: number; - durationModifier: number; - calc?: { - damage: any; - healing: any; - duration: any; - }; -}; diff --git a/src/types/character.ts b/src/types/character.ts deleted file mode 100644 index e52cbf78..00000000 --- a/src/types/character.ts +++ /dev/null @@ -1,31 +0,0 @@ -import type { CharacterKey } from '@/constants/CHARACTERS'; -import type { AbilityRef } from '@/types/ability'; -import type { CombatStats } from '@/types/combatStats'; -import type { CharacterEquipment } from '@/types/equipment'; -// import type { DynamicObject } from '@/types/common'; - -export type Race = 'elf' | 'human' | 'troll' | 'dwarf' | 'goblin'; - -export type CharacterRef = { - uuid?: string; - id: string; - overrides: { - abilities: AbilityRef[]; - equipment: CharacterEquipment; - combatStats: CombatStats; - }; -}; - -export type Character = CharacterRef & { - name: string; - description: string; - race: CharacterKey; - level: number; - image: string; - size: number; - equipment: CharacterEquipment; - abilities: AbilityRef[]; - maxTicks: number; - element: string; - combatStats?: Required; -}; diff --git a/src/types/combat.ts b/src/types/combat.ts deleted file mode 100644 index 369eed38..00000000 --- a/src/types/combat.ts +++ /dev/null @@ -1,37 +0,0 @@ -import type { Team } from '@/types/team'; -import type { Combatant } from '@/types/combatant'; -import type { SFX } from '@/types/sfx'; - -type RewardType = 'experience' | 'bossHighscore'; - -export type Reward = { - type: RewardType; - amount: number; - showInUI: boolean; -}; - -export type CombatEvent = { - eventTimestamp: number; - // globalEventIndex: number; - // eventIndex: number; - // eventTaker: Combatant; - // target: Combatant; - teams: Team[]; - // log: { - // damage: { - // result: number; - // }; - // }; -}; - -export type Combat = { - // start: number; - // seed: string; - teamsStartState: Team[]; - teamsEndState: Team[]; - events: CombatEvent[]; - duration: number; - winningTeam?: Team; - fightId?: string; - audio: SFX[]; -}; diff --git a/src/types/combatStats.ts b/src/types/combatStats.ts deleted file mode 100644 index c6832b7d..00000000 --- a/src/types/combatStats.ts +++ /dev/null @@ -1,56 +0,0 @@ -export type CombatStats = { - maxHealth?: number; - currentHealth?: number; - damage?: number; - maxArmor?: number; - currentArmor?: number; - criticalChance?: number; - criticalDamage?: number; - dodgeChance?: number; - blockChance?: number; - magicChance?: number; - limits: { - wounded: number; - concussed: number; - exposed: number; - }; - modifiers: { - maxHealth: number; - maxArmor: number; - damage: number; - }; -}; - -export const prettyCombatStatKey = (key: string) => - ({ - maxArmor: 'Armor', - damage: 'Damage', - maxHealth: 'Health', - resistance: 'All resistances', - - bleeding: 'Bleeding', - stunned: 'Stunned', - vulnerable: 'Vulnerable', - - criticalChance: 'Critical chance', - criticalDamage: 'Critical damage', - dodgeChance: 'Dodge chance', - blockChance: 'Block chance', - magicChance: 'Magic chance' - })[key] || key; - -// export const prettyCombatStatValue = (key: string, value: number) => -// ( -// ({ -// criticalChance: `${(value * 100).toFixed(0)}%`, -// criticalDamage: `${(value * 100).toFixed(0)}%`, -// dodgeChance: `${(value * 100).toFixed(0)}%`, -// blockChance: `${(value * 100).toFixed(0)}%`, -// magicChance: `${(value * 100).toFixed(0)}%` -// })[key] || value -// ) -// .toString() -// .replace(/^0%$/, '-'); - -export const prettyCombatStatValue = (key: string, value: number) => - value === 0 ? '-' : value > 1 ? value : `${(value * 100).toFixed(0)}%`; diff --git a/src/types/combatant.ts b/src/types/combatant.ts deleted file mode 100644 index fda47edd..00000000 --- a/src/types/combatant.ts +++ /dev/null @@ -1,47 +0,0 @@ -import type { Character } from '@/types/character'; -import type { CombatStats } from '@/types/combatStats'; -import type { Ability } from '@/types/ability'; -import type { VFX } from '@/types/vfx'; -import type { SFX } from '@/types/sfx'; - -export type StatusStack = { - max: number; - value: number; -}; - -export type StatusEffect = { - ticks: number; - value: number; -}; - -export type Combatant = Omit & { - id: string; - teamIndex: number; - animations: VFX[]; - injectedAnimations: VFX[]; - audio: SFX[]; - combatStats: Required; - eventTimestamp: number; - eventAbility: Ability['id']; - eventIndex: number; - abilities: Required[]; - abilitiesCopied: Required[]; - damage: number; - statuses: { - isBlocking: boolean; - knockedOut: number; - - isStunned: StatusEffect; - isBleeding: StatusEffect; - isVulnerable: StatusEffect; - - isWounded: StatusStack; - isConcussed: StatusStack; - isExposed: StatusStack; - }; - position: { - x: number; - y: number; - rot: number; - }; -}; diff --git a/src/types/common.ts b/src/types/common.ts deleted file mode 100644 index 678e941f..00000000 --- a/src/types/common.ts +++ /dev/null @@ -1,4 +0,0 @@ -export type DynamicObject = { [key: string]: any }; - -export type ClickEvent = any; -export type ChangeEvent = Event & { target: HTMLInputElement }; diff --git a/src/types/equipment.ts b/src/types/equipment.ts deleted file mode 100644 index 405795bf..00000000 --- a/src/types/equipment.ts +++ /dev/null @@ -1,30 +0,0 @@ -import type { AbilityRef } from '@/types/ability'; -import type { CombatStats } from '@/types/combatStats'; -import type { DynamicObject } from '@/types/common'; - -export type EquipmentSlot = 'mainHand' | 'offHand' | 'armor' | 'accessory' | 'trinket'; -export type EquipmentType = EquipmentSlot | 'oneHand' | 'twoHand'; - -export type EquipmentRef = { - uuid?: string; - id: string; - overrides?: DynamicObject; -}; - -export type Equipment = EquipmentRef & { - name: string; - slotsIn: Required; - cost: number; - level: number; - description: string; - combatStats: CombatStats; - abilities: AbilityRef[]; -}; - -export type CharacterEquipment = { - mainHand: EquipmentRef | null; - offHand: EquipmentRef | null; - armor: EquipmentRef | null; - accessory: EquipmentRef | null; - trinket: EquipmentRef | null; -}; diff --git a/src/types/sfx.ts b/src/types/sfx.ts deleted file mode 100644 index ed70bafa..00000000 --- a/src/types/sfx.ts +++ /dev/null @@ -1,10 +0,0 @@ -export type SFX = { - id?: string; - sfxName: string; - // duration: number; - start: number; - // end: number; - // targetX?: number; - // targetY?: number; - // amount?: number; -}; diff --git a/src/types/team.ts b/src/types/team.ts deleted file mode 100644 index f9e4c706..00000000 --- a/src/types/team.ts +++ /dev/null @@ -1,7 +0,0 @@ -import type { Combatant } from '@/types/combatant'; - -export type Team = { - name: string; - index: number; - combatants: Combatant[]; -}; diff --git a/src/types/vfx.ts b/src/types/vfx.ts deleted file mode 100644 index 6c2962c5..00000000 --- a/src/types/vfx.ts +++ /dev/null @@ -1,12 +0,0 @@ -export type VFX = { - id?: string; - vfxName: string; - duration: number; - start: number; - end: number; - targetX?: number; - targetY?: number; - amount?: number; - - isCritical?: boolean; -}; diff --git a/src/types/weaponType.ts b/src/types/weaponType.ts deleted file mode 100644 index 93cba07d..00000000 --- a/src/types/weaponType.ts +++ /dev/null @@ -1 +0,0 @@ -export type WeaponType = '1h' | '2h' | 'offHand'; diff --git a/svelte.config.js b/svelte.config.js deleted file mode 100644 index cdf29302..00000000 --- a/svelte.config.js +++ /dev/null @@ -1,31 +0,0 @@ -import adapter from '@sveltejs/adapter-vercel'; -import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; - -/** @type {import('@sveltejs/kit').Config} */ -export default { - kit: { - adapter: adapter(), - alias: { - '@': 'src/*' - } - }, - compilerOptions: { - runes: true - }, - preprocess: vitePreprocess(), - vitePlugin: { - // set to true for defaults or customize with object - inspector: { - toggleKeyCombo: 'meta-shift', - showToggleButton: 'never', - holdMode: true - }, - // This ignores "compilerOptions.runes = true" for external packages - // Ideally this is removed once third party packages supports Svelte 5 runes - dynamicCompileOptions({ filename }) { - if (filename.includes('node_modules')) { - return { runes: undefined }; // or false, check what works - } - } - } -}; diff --git a/tsconfig.json b/tsconfig.json deleted file mode 100644 index 208ca700..00000000 --- a/tsconfig.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "extends": "./.svelte-kit/tsconfig.json", - "compilerOptions": { - "types": ["bun-types"], - "allowJs": true, - "checkJs": true, - "esModuleInterop": true, - "forceConsistentCasingInFileNames": true, - "resolveJsonModule": true, - "skipLibCheck": true, - "sourceMap": true, - "strict": true, - "moduleResolution": "bundler", - "baseUrl": ".", - "paths": { - "@/*": ["src/*"] - } - }, - "exclude": ["src/**/*.js"] - // Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias - // except $lib which is handled by https://kit.svelte.dev/docs/configuration#files - // - // If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes - // from the referenced tsconfig.json - TypeScript does not merge them in -} diff --git a/vercel.json b/vercel.json new file mode 100644 index 00000000..0d199b2d --- /dev/null +++ b/vercel.json @@ -0,0 +1,5 @@ +{ + "rewrites": [ + { "source": "/(.*)", "destination": "/index.html" } + ] +} diff --git a/vite.config.js b/vite.config.js deleted file mode 100644 index 1a0053a7..00000000 --- a/vite.config.js +++ /dev/null @@ -1,35 +0,0 @@ -import { sveltekit } from '@sveltejs/kit/vite'; -import { defineConfig } from 'vite'; -import autoImport from 'sveltekit-autoimport'; -import tailwindcss from '@tailwindcss/vite'; -import { componentsTypedefsPlugin } from './scripts/generate-component-typedefs'; - -export default defineConfig({ - build: { - sourcemap: true - }, - server: { - fs: { - allow: ['..'] - } - }, - plugins: [ - tailwindcss(), - autoImport({ - components: [{ name: './src/components', flat: true }], - mapping: { - ENV: `import ENV from '@/constants/ENV_VARS'`, - app: `import app from '@/app.svelte'`, - Howl: `import { Howl, Howler } from 'howler'` - }, - module: { - svelte: ['onMount', 'onDestroy'], - '@/ts/use': ['tooltip'], - 'tailwind-merge': ['twMerge as tw'] - }, - include: ['**/*.svelte'] - }), - componentsTypedefsPlugin(), - sveltekit() - ] -}); From 7352faf85ed0481e51e70d93480ea86106e082dd Mon Sep 17 00:00:00 2001 From: kkortes Date: Wed, 8 Apr 2026 19:14:20 +0200 Subject: [PATCH 002/200] cleanslate and move on --- CLAUDE.md | 7 +- components/Armory.html | 58 +++--- components/Layout.html | 42 ++-- components/Notifications.html | 88 ++++++++ components/Sidebar.html | 99 ++++----- css/index.css | 54 +++++ index.html | 365 ++++++++++++++++++---------------- pages/ability-scaling.html | 20 +- pages/brawler-detail.html | 129 ++++++------ pages/brawlers.html | 45 +++-- pages/character-scaling.html | 20 +- pages/debug.html | 6 +- pages/equipment-scaling.html | 20 +- pages/fight-detail.html | 354 +++++++++++++++++++++++++-------- pages/home.html | 8 +- pages/random-duel.html | 157 +++++++++++---- pages/reset-password.html | 10 +- pages/the-arena.html | 73 +++---- pages/vendor.html | 63 +++--- 19 files changed, 1024 insertions(+), 594 deletions(-) create mode 100644 components/Notifications.html diff --git a/CLAUDE.md b/CLAUDE.md index c10ec685..8f2b48ec 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -18,13 +18,16 @@ The **`main` branch** contains the original SvelteKit implementation. Use `git s ## Conventions -- **No `class=""` or `style=""` attributes** (except dynamic Vibe `@[...]` values for computed widths/positions) +- **No `class=""` attributes** — use individual HTML attributes instead +- **No `style=""` attributes** — use custom attributes with CSS custom properties via `` instead of `style="--var: @[value]"`. Only acceptable for truly unavoidable cases like tooltip absolute positioning. +- **No `
` elements** — use semantic or custom element names instead: ``, ``, ``, ``, ``, etc. Divs say nothing about what they are. - **Attribute-based styling**: use Stylecheat attributes (`g-4`, `text-sm`, `vertical`, `primary`, etc.) or custom attributes - **Scoped styles**: each page/component has a ` -
+ diff --git a/components/Layout.html b/components/Layout.html index 78667dd1..4cfb0905 100644 --- a/components/Layout.html +++ b/components/Layout.html @@ -1,20 +1,20 @@ -
+

Connecting to server...

-
+ -
-
+ + -
+ -
+ @[authError] -
+ @@ -51,11 +51,11 @@

-
+ -
-
+ + @@ -64,7 +64,7 @@ -
+ -
+

Loading...

-
-
+ +