From 64be72194e5fe89229a6bbb6395f9f456dcf998c Mon Sep 17 00:00:00 2001 From: Christian Semmler Date: Sat, 31 Jan 2026 15:28:16 -0800 Subject: [PATCH] Add save game editor (#12) * WIP * stuff * WIP: Interactive 3D score cube for save editor * Conditionally render ScoreCube to properly clean up WebGL canvas Only mount the ScoreCube component when on the save-editor page. This ensures onDestroy is called when navigating away, properly disposing of the WebGL renderer and removing the canvas from DOM. * Refactor: Consolidate formats and genericize WDB rendering - Move parsing/serialization code to src/core/formats/: - BinaryReader.js, BinaryWriter.js (shared utilities) - SaveGameParser.js, SaveGameSerializer.js - PlayersParser.js, PlayersSerializer.js - Create formats/index.js as barrel export - Extract generic WdbModelRenderer from ScoreCubeRenderer: - WdbModelRenderer handles D3DRM geometry and paletted textures - ScoreCubeRenderer extends it with score-specific logic - Prepares for rendering other WDB models in the future - Keep savegame/constants.js for domain-specific constants - savegame/index.js remains as high-level API facade * Save editor UI improvements - Make save slot cards fixed width (85px) to prevent resizing with name length - Make save slot cards more compact (smaller icons, padding, font) - Remove Act selection from Character section - Remove box-shadow from selected character to fix collapsed section bleed * Improve score cube lighting to match in-game appearance - Use flat, even lighting (high ambient + soft front light) - Remove harsh directional shadows from edges - Adjust camera position slightly for better framing * Add spacing below score cube canvas * Add spinning loader while loading score cube * Update three.js to 0.182.0 and fix npm audit issues - Update three.js from 0.170.0 to 0.182.0 - Fix npm audit vulnerabilities (devalue, lodash, svelte) - Remaining vulns are in dev dependencies (vite, workbox-cli) * Fix score cube overflow on mobile Add max-width constraints to prevent the score cube from expanding its container on narrow viewports while preserving its natural 200x200 size on desktop. * Add save slot carousel and improve empty states - Add reusable Carousel component with arrow navigation, drag-to-scroll, and click-to-scroll-into-view functionality - Replace static save slot list with horizontal carousel - Add empty state with image when no save files exist - Add prompt state when saves exist but none is selected - Reset selected slot when entering Save Editor page * Add February 2026 changelog entry for Save Editor * Add missing January 2026 changelog entries for Safari and mobile fixes * Remove unused mission images * Refactor opfs.js to reduce code duplication - Consolidate getFileHandle to use getOpfsRoot internally - Add writeTextFile helper that uses writeBinaryFile - Extract showToast helper for toast notifications - Simplify saveConfig to use writeTextFile instead of duplicate worker - Simplify fileExists and readBinaryFile to use getFileHandle * Remove unused ScoreColorButton component * Remove unused UI components --- package-lock.json | 44 +- package.json | 3 + public/laura-selected.webp | Bin 0 -> 918 bytes public/laura.webp | Bin 0 -> 1018 bytes public/mama-selected.webp | Bin 0 -> 964 bytes public/mama.webp | Bin 0 -> 1016 bytes public/nick-selected.webp | Bin 0 -> 876 bytes public/nick.webp | Bin 0 -> 986 bytes public/papa-selected.webp | Bin 0 -> 948 bytes public/papa.webp | Bin 0 -> 980 bytes public/pepper-selected.webp | Bin 0 -> 844 bytes public/pepper.webp | Bin 0 -> 946 bytes public/save.webp | Bin 0 -> 20846 bytes src/App.svelte | 4 + src/app.css | 27 +- src/core/formats/BinaryReader.js | 164 ++++++ src/core/formats/BinaryWriter.js | 172 ++++++ src/core/formats/PlayersParser.js | 54 ++ src/core/formats/PlayersSerializer.js | 62 ++ src/core/formats/SaveGameParser.js | 396 +++++++++++++ src/core/formats/SaveGameSerializer.js | 329 +++++++++++ src/core/formats/WdbParser.js | 439 +++++++++++++++ src/core/formats/index.js | 18 + src/core/opfs.js | 230 ++++++-- src/core/rendering/ScoreCubeRenderer.js | 84 +++ src/core/rendering/WdbModelRenderer.js | 303 ++++++++++ src/core/savegame/constants.js | 191 +++++++ src/core/savegame/index.js | 280 ++++++++++ src/lib/Carousel.svelte | 184 ++++++ src/lib/CollapsibleSection.svelte | 15 - src/lib/ConfigToast.svelte | 4 +- src/lib/ReadMePage.svelte | 7 +- src/lib/SaveEditorPage.svelte | 528 ++++++++++++++++++ src/lib/TabNavigation.svelte | 18 - src/lib/ToggleSwitch.svelte | 49 -- src/lib/config/ExtrasTab.svelte | 11 +- .../save-editor/MissionScoresEditor.svelte | 31 + src/lib/save-editor/ScoreCube.svelte | 226 ++++++++ src/stores.js | 12 +- 39 files changed, 3723 insertions(+), 162 deletions(-) create mode 100644 public/laura-selected.webp create mode 100644 public/laura.webp create mode 100644 public/mama-selected.webp create mode 100644 public/mama.webp create mode 100644 public/nick-selected.webp create mode 100644 public/nick.webp create mode 100644 public/papa-selected.webp create mode 100644 public/papa.webp create mode 100644 public/pepper-selected.webp create mode 100644 public/pepper.webp create mode 100644 public/save.webp create mode 100644 src/core/formats/BinaryReader.js create mode 100644 src/core/formats/BinaryWriter.js create mode 100644 src/core/formats/PlayersParser.js create mode 100644 src/core/formats/PlayersSerializer.js create mode 100644 src/core/formats/SaveGameParser.js create mode 100644 src/core/formats/SaveGameSerializer.js create mode 100644 src/core/formats/WdbParser.js create mode 100644 src/core/formats/index.js create mode 100644 src/core/rendering/ScoreCubeRenderer.js create mode 100644 src/core/rendering/WdbModelRenderer.js create mode 100644 src/core/savegame/constants.js create mode 100644 src/core/savegame/index.js create mode 100644 src/lib/Carousel.svelte delete mode 100644 src/lib/CollapsibleSection.svelte create mode 100644 src/lib/SaveEditorPage.svelte delete mode 100644 src/lib/TabNavigation.svelte delete mode 100644 src/lib/ToggleSwitch.svelte create mode 100644 src/lib/save-editor/MissionScoresEditor.svelte create mode 100644 src/lib/save-editor/ScoreCube.svelte diff --git a/package-lock.json b/package-lock.json index 7934ee1..b0d8f9c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,6 +7,9 @@ "": { "name": "isle.pizza", "version": "1.0.0", + "dependencies": { + "three": "^0.182.0" + }, "devDependencies": { "@sveltejs/vite-plugin-svelte": "^4.0.0", "svelte": "^5.0.0", @@ -3583,10 +3586,11 @@ } }, "node_modules/devalue": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.6.1.tgz", - "integrity": "sha512-jDwizj+IlEZBunHcOuuFVBnIMPAEHvTsJj0BcIp94xYguLRVBcXO853px/MyIJvbVzWdsGvrRweIUWJw8hBP7A==", - "dev": true + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.6.2.tgz", + "integrity": "sha512-nPRkjWzzDQlsejL1WVifk5rvcFi/y1onBRxjaFMjZeR9mFpqu2gmAZ9xUB9/IEanEP/vBtGeGganC/GO1fmufg==", + "dev": true, + "license": "MIT" }, "node_modules/dot-prop": { "version": "9.0.0", @@ -3876,10 +3880,11 @@ "dev": true }, "node_modules/esrap": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/esrap/-/esrap-2.2.1.tgz", - "integrity": "sha512-GiYWG34AN/4CUyaWAgunGt0Rxvr1PTMlGC0vvEov/uOQYWne2bpN03Um+k8jT+q3op33mKouP2zeJ6OlM+qeUg==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/esrap/-/esrap-2.2.2.tgz", + "integrity": "sha512-zA6497ha+qKvoWIK+WM9NAh5ni17sKZKhbS5B3PoYbBvaYHZWoS33zmFybmyqpn07RLUxSmn+RCls2/XF+d0oQ==", "dev": true, + "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" } @@ -5192,10 +5197,11 @@ } }, "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", + "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", + "dev": true, + "license": "MIT" }, "node_modules/lodash.debounce": { "version": "4.0.8", @@ -6715,10 +6721,11 @@ } }, "node_modules/svelte": { - "version": "5.46.1", - "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.46.1.tgz", - "integrity": "sha512-ynjfCHD3nP2el70kN5Pmg37sSi0EjOm9FgHYQdC4giWG/hzO3AatzXXJJgP305uIhGQxSufJLuYWtkY8uK/8RA==", + "version": "5.49.1", + "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.49.1.tgz", + "integrity": "sha512-jj95WnbKbXsXXngYj28a4zx8jeZx50CN/J4r0CEeax2pbfdsETv/J1K8V9Hbu3DCXnpHz5qAikICuxEooi7eNQ==", "dev": true, + "license": "MIT", "dependencies": { "@jridgewell/remapping": "^2.3.4", "@jridgewell/sourcemap-codec": "^1.5.0", @@ -6728,9 +6735,9 @@ "aria-query": "^5.3.1", "axobject-query": "^4.1.0", "clsx": "^2.1.1", - "devalue": "^5.5.0", + "devalue": "^5.6.2", "esm-env": "^1.2.1", - "esrap": "^2.2.1", + "esrap": "^2.2.2", "is-reference": "^3.0.3", "locate-character": "^3.0.0", "magic-string": "^0.30.11", @@ -6879,6 +6886,11 @@ "node": ">=10" } }, + "node_modules/three": { + "version": "0.182.0", + "resolved": "https://registry.npmjs.org/three/-/three-0.182.0.tgz", + "integrity": "sha512-GbHabT+Irv+ihI1/f5kIIsZ+Ef9Sl5A1Y7imvS5RQjWgtTPfPnZ43JmlYI7NtCRDK9zir20lQpfg8/9Yd02OvQ==" + }, "node_modules/through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", diff --git a/package.json b/package.json index dda8867..5e4ea1a 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,9 @@ "preview": "vite preview", "prepare:assets": "node scripts/prepare.js" }, + "dependencies": { + "three": "^0.182.0" + }, "devDependencies": { "@sveltejs/vite-plugin-svelte": "^4.0.0", "svelte": "^5.0.0", diff --git a/public/laura-selected.webp b/public/laura-selected.webp new file mode 100644 index 0000000000000000000000000000000000000000..70b5f8635defc741e5839ffdbc899aec7ea6b5fe GIT binary patch literal 918 zcmV;H18MwHNk&GF0{{S5MM6+kP&goh0{{RJ5CEM4DkuOh06uLnlSZT?A)zX|s;Gbs ziEIF!GUz!kxN)4u2T}~y+aE9=tRAgig}03Vkbi*o9sPjx*Y;}iZmdVm7*3Lw`SQhJ z29PPIc=UWZ0%hI0JeZ6(c!foX zfyoVWBy#Nl0RHML|0;+VK&i0Rm_#9p#KB1alcQ7S`_5TJ)=uoIl9`eiFJj`&Zf5Yd z$^n|BBfPpeM}(U_`d@t-=|nyxwG{brr-V)4qrFU5JlY=oMiXA8`&RhQ1(VrIf3cZl zA2Cob|J|d-uO6^!4+%L*eQ=EWevs1>=;@zeM@?5kJBdEXkGuG70{oW}!Q`~XGb5!y z!qcaSWA?_))0kS^p;19;e2O(ivcrja|*8yc1vQ> zJ!uVZq2IVpJ23)qFI$LZDOEB5ECko~-x(g9Q9LIcJ5m%M0M3qh`f$;TS|Ix;!FWXf zU-6FCeAM-~R#qt?{)`Lgck%epXNZORfXmpK?Eh1ddvo@Mxj-OV%d$d6H#PJqC@~2A z+Wl#z3U^h`O@cntW`of%x$ENt{#Hj(`Kq-U?;Z28;R$(K=RIU7|KXEXmC9YrP^wbt z&0yZ52W2b?t$z#r6Cj?;9tjv?Gz#5~xRxWx9j`<+Ul@(2cmlF;b0ZG*%@s7VYOhNM zJ|f*yimjtF?_J)V^Au1uFwi_oU&@%pI{ztss$ysU>SMhsC#s`I_@C)Xm)(fF$Hmhg zQ-=G}1ouGilGhCy8q|O56LwA#3@_f%u|oGlX*>l=EwIYZY~uQg?dIxR&I6LZ{FPEV z9aTVunu{D+O|&K9$xby{%i|`4MF9nJRssNYcwB!@q{3Xq(S+RVKQ2Z&&`bGxde1WS ztsj63R~G5gZySr6M|YPT{oTF4m`EmxPH`OB`tnp!;Kl*MhIR)w&!SPB4nxX)d7D^TBEQ>)PAlFe?0AW`r{ypSTZx9;$ZE`GEah^=|(`(&^^i?1}p2>~s2)*0K9x`yK5Y zSdWT-PN)RobDtb9A$Rt^0LEa>LZNHAr)$O#5>8C* zw+edcUop>&<|*{D1oW*#yWYu9+%^`3Qu4<#d)*JN+*290EQkYgs3;lmqQVUc32g1%yQI_l}~(yc|mSbDI4JpZ4vNjO*=Hb!sPVy@EH zZCa`ReR_M)5)xgwd4R>NLq`&6r0eFBJnZB(u&_o~+sIlMXm&p&Z;Y>E<}k1+nce9XeVJ?= zs3}aMw{w2Sm;fF90SaW?2bAG_8SFF4SwkNp?xwinJQ{f_`ncZ}>q{Nn1tQe=cGuN7 z$zwGA6wsyuBny?LTtnuK7j;d7Mu2A{T8*-8683WJxT|peXlVIjyh$?%xzkXTtYw)F zlbb8~73<-RhBO#?NJZ=rt_dWl_>b-YWbgN_+jQYZVbogpoje2Q^6t5#!Gd9TZjj8s zUGlF%@cd^sek)TY&dc9vA3pbh+O;aEtWRW)vu!NK_$OKG8-y`_7e29Z`vi}cf!r0m z-}67rp8%!=Pb;cm5^--Yr?uusNDCGjrmA*vHOd`+Hqd?wka@I`MGUbAY!+ZU?8&d- z0s6CL&hUyV7aBp9Dwf#yJd-!# zojkW-%=3%5yv+^zp#G}u6-*U8=CLaF8PTmUsrbm zAd&t<#e$=$5w=(4BdAI3;GW9-if6%k6Se9?U+-aZ`dE85Zug5|A20oDp=dnt71ENe zLcwuzTgPC=W%k#4|FuSTUO#b08>|i1$9vfxM`6ETwb7WBH^^-&3W!&Z;Hb%(@%|yD2V*B{L zU*Cxr+F@#)O$CQ4e+h`KCR%q4575BITAq0a9NQho;+g`?==sjex`8X5FGYl$9yj{= z9(>Y%lXZ9|E&s<@_|dZw(Cz=)c{)fc#{M&Gk5w2|uyvQM_?1^Q#7EP;p_(0$-NWxm z8x4aRlpAZafDNiDxwzME8c$HylV0vqY95#e8*Io_`Fq=Y(+j2kj+N4nCpP0VFO2iy z>NBgJx@QAvpelF_*{Vz*(StCkwti~I;;&bVoLACU!a?KJ)R{En9X)=;DrmC1jtneA$sym{Y4!uJ6*Yc7U*zvW z_m}-K6%6)>C7JwU z|2Ldp1;|ZWpm#oWA97mW>`|V2N1_4ki{9p&8BI87g>hQ4y-0WGJl=1K>&|DM0BnqwuzO(3;eaQLm2wR#^DM6stbX11udVo@Y)DDF6Ufy67$d literal 0 HcmV?d00001 diff --git a/public/mama.webp b/public/mama.webp new file mode 100644 index 0000000000000000000000000000000000000000..55c51bfba5ee7af364b1c2d6dc3e2f06e29b0989 GIT binary patch literal 1016 zcmVJDZ_W|$Q)Nb0}F@AOagM9!$Vm%qXiai8BRed-ASUwql zUB7C*sB0AR+6{FVIq|?!SN}WVTYG=Se$E{vFlA<3qOgfk6!BHJoZdkPRQW7|Nb2c~ zv%}=7;Wlpz8!l_baJ=E90~;#{fhRZHsHc|z0RH)ww)YdqP^eLddYvc^ZVnv^x8xE_ zFoI7?R$GyNr);EOa62~9PuxaSI!?Is@u$#5p1}Is_(?Z>LtTYQ?%oD|6d6~+{Dl52 zfMM;1Djh;<|2$QM)OVPH*$POB5wteCKaS_js#uJKJiekXK3>n#KWe!^fmK`Dw+d@R z%R8fT3s<}=Q|LAR#FgegyK76;YUBPyv0HTgh)5sOpprLI#scaHpM#uzf#;gJ0a3!u z!o%<`Ni+STZxssUrRLu>|Hb+%H(c-!m$QfoOJr_nlOsEUR~wYh!2Mlk}Nzauvsp!=ucM}1gl5#B%_*1P!n#= z^Tx4cSk`ZpJCq2K=WZ~tED#N21{ZiMqG1t^ z-i>2V#`B>~nMw5hgTLT0b&ma+O`|wwA8OP4CG#Wq>%Z-fzA(o#`-8L0XHD+5QyL)_ zVh`uMtET6_)PgO6k&80_UI>bDJ}`5l4Zcq~iucX1GFTED!y`ddF(;KY6b@W82jzCH zsp`Vr$r#S8_tM5L?Jy_CkYR2@ubq0_ih1F>wuC>xUh@k#V&%cu#|t>IoU;YjeuVsR z^}n%*K0JdyJYv@8|MLSpRH}`k>awlVXAl03{v5DW`mx zwZHN<_nZ~+C7#j>D`otIM>f50UW_Jl+|wkdQ&fl!oxTxnfs>cWs(oftO&_F)&MxS; z>(0dJ#Wgi?yNBV+rj-)_%?irgdeW5x5@NiXp1+y-Q9P1@AGUU2>?R_l&gM(eX)J$G z9$>>8m9ZO}H}guNyBuUTw+8T3>}4$;`vzf@mc}wDnyp_7M?f&@G~Zvb1`Oq#0Hn4` mz{zv{ZRa5FH4lU$Rft7&v<03zYy+-cnU1C;$M6Xzh*w literal 0 HcmV?d00001 diff --git a/public/nick-selected.webp b/public/nick-selected.webp new file mode 100644 index 0000000000000000000000000000000000000000..d6e626c307de86107b1d8180fc56b39855ea7040 GIT binary patch literal 876 zcmV-y1C#txNk&Fw0{{S5MM6+kP&go10{{RJ5CEM4DklIg06uLlkVYgTp`k7~3ow8U ziEIF!IYCqYK>fP)J7UJeY~MV0_zzUO&>o4uupXK|%AQacarmO?uXr+n;0GI?0_Jbd zqwasW1kivE`ZieXbk3W{Z@UXxm0+IAI$^`vi8p#tJ^VI7Apkh_QkE z44OEeVj2aQgYIPr?$I!Ud3XwMMr z@-3Qb1GC5pD+F=l8$MgLMNvco5N(C{P;pmvtJT-OLSgxDBc?Q+i@EFhqgc=Kczf`K zv_4YLOBk<(oPyVz{s18!A|Lz4wBJuAwwLZedjGE}t3xZIU-9dA-woFjp(NPczg4&p zBbq9e0Pq^Z%Mr8ry=VX>`PQw$SG%&d>^6$pEBVaQ##f~#TN!o7V$}I%>h)y~&&FgK z6sd`}^);jhmf+Zd$5p4KJ##dIG%&f-8l#87b(hllO`Eoa-)bnAjZyG&vfMlP5R;D}e3S30Ux znu86a3T?Z2doxlz}k&w=U=QoJzgC95nZ!fjL&s;lGGf8_@GFiw}BfGclH>-CWRqU$KpZ#Kz7M3yw-QmY|4KxQ#IcI?*mWF@M)D&I{KRuvjnd#A#{1M``{ zX&~-kufWe(TN2z@Ww}wOai=T>j+I0!u5M_&{ZQ2_V&fW+46=y|B z($nqYEdhU~tl7tc`xmqIxhkYkPZMGnaRBmC)6}1qlLQx;%2B8_dcB!ObhFRw-|Kf$ zt44^q1E^L*U)_i}^*jgJfdhnubt}{h@8sk`vNre)7Lj(}e3;rbCZfK)?VF2Li8_-$ zB`Hfz0pYk0b92%2F1RFomK0l&9SFU0I-R=zfffv_iZYb`T)qoH3f8NC0oFXwbw%d~BYBnXjelhfg(o{(Qi}!fLtA$LZ056s&5WVjhJe$y zB)lNGyGKtWJ~3oR%c5Uq@KYts>6}Sq1Eu+a#Wq)?f5T$^0RsUGozT`irH9n2o2f{3 z6xcSY`}{80Gd+kd3POi=mM6F*GI-3F-LSV^B~K2E`<3C!u*-e@JV1A_xr<|>^090^ zA0Zhq%%C5h=FlH|w$#ukBvv)@z2BSA(qT&EFgKq`-SJrbjsZR4lU@eyb!o)*DvCQZ z(ehY?_c+pIeu)Cb{vJ2pFuOofH|%7DnW~gJk6tL0**a#hl~^GG{AU(O`Le`Lgtt|a z>!Hs$oz_>wLa}9oHoXk~AHcCdd4rjDIR@x+L(|m>L*TB?T>-RN#`X==+c3`B)G(PFjdwEE#U_~5QN-s#);S^AfN`meZL&sS#u z-#F}MdjsvMv4Ilm+P08qckd^OqQ!}v?Z!l*Ud4tf(h0(E)@!6PhK{?jDf%(>#u-=M z5VpjCcgM{Gui0auu>vX*y_XENS*TYM0B#w@S-)CrqIOw_GyviJv1D7<8>uiID zytwR<8V46C%0>kgv|g;v75g8LOi^-u=D+qs|Bd_KR^s!2ocA;{mT?M3<=^_}^uizA IF~wj20AD`xzyJUM literal 0 HcmV?d00001 diff --git a/public/papa-selected.webp b/public/papa-selected.webp new file mode 100644 index 0000000000000000000000000000000000000000..cc6b2213e83ab4bb63ba554049fdd71f55cab51c GIT binary patch literal 948 zcmV;l155l;Nk&Gj0{{S5MM6+kP&go<0{{SU5CEM4DklIg06uLjkwv5;p_;0!06+%B zwg66X z1GAb}WszNPzR16nzO)Nnb7KY!Xv-&v`g(GQo>0HOU9(ff+2$4cEeS%0Sp%AlMWuW$rG!wZq zF$gR_DBzjXYu~pn?-PEKg6k^+PCeacWHQ$Y3;SlT8x2J}U1v*0d9$nE|&vv3W^Lj^~{6jMD^~s9Ma9N-jm$a}5mF@(0=nuOJVfPJu4DnIP zRF_T*5UG|$mDBniaM$NSn2!bDckG|>-2=EkFYD{}k&jLB@1uvAo)hzB7RhTZfUV0K zq(98IAMdcvFU{)T=RUkcVZH#^Nm*w-HisDP?cUt1SNjIJBlKqJP)ufaM@ZjcOc$4R zKry}VMWc*IUx(+i;=$HG&|!i%v6w!ZSGJGmDCW1109lENsdNaiSr`-YC=^m)B zBCE|+2JuIaIb{&Z2QM*^!A2E=9pl=rJSSe&9yshAjk38IY54#5M_WJJ?R0k4DjuIw zI87*UR}>1EQ4lFT)({frUkFi1a)RijN-Z}1RCm14>Vazi W|0!}?kGe^x+}gj_&$m1aNB{u*cGB|z literal 0 HcmV?d00001 diff --git a/public/papa.webp b/public/papa.webp new file mode 100644 index 0000000000000000000000000000000000000000..83935244cb85a6d520aef00a290e76be825c4c14 GIT binary patch literal 980 zcmV;_11tPeNk&G@0{{S5MM6+kP&gpK0{{RJ5&)e6DklIg06uLjkwzpUp`kI>Y5;%@ ziEIF!GQD#T%6XAepS9}|FvGgAFiI3zRn(0m5jN~^hA)# zE_G{*BX zcnE#3Vqg9C}aGj^)+Wy=FlZ7;$&VL-d04%?jIrfB?I9+q4 zM5EI!|EDwPP~9TsIZ@@vVc;QaS8M>lBpjD0oYTlt-RT4lUZs!7{P1nXURyJ^tW z?G6rrVC>ukamnZY5674Am(+rW$h?STeEe)=l+k>)$mnb-uxh2Gca%&ql3M6GNM_Fm zE2dJgAx)>i0VJ@Tepxx%jy7r407)VzA(cuiyAmY_eQd@$WlhwN93sNw zCDC00R5R(I*&qL~_+=5QwV>&y1j@#$U$Kco%83TWzX}c}(|NF~0FW9cROmlz+>FOd>B%4W_zSGn zV-^1eN}quy`#@}K_ua&FHnc(iK89U4%CScbgn7p?%W~Rc9wfWFAvGDDQ~pxy;yXR7 z;hah+2#08rR+2EZzx27huaGN2Vv)qG7t{YF1?++aMc!5265bS>F8?hoe>kxmy>C;T zI!zRk<<;v53szn;FEBHe5EW)Ua(*wwF`VZMIEzC4mg<;Df^J56VmhxCm>RWiAOHYN CH{0g` literal 0 HcmV?d00001 diff --git a/public/pepper-selected.webp b/public/pepper-selected.webp new file mode 100644 index 0000000000000000000000000000000000000000..1cacc1402e2a0f219218462bc9c6e6f90a5e19b9 GIT binary patch literal 844 zcmV-S1GD^6Nk&FQ0{{S5MM6+kP&gns0{{T94*;D3DklIg06uLhk42;+p%)fpKnBFN z08X5ssNWF2@8(5aYPu8dz;Y|hy?W0092<#_i|rV)S+=^Y~m6^yxpRnGqa8yW>gVPEw&8dliGJW1TzhotmtrMlA<& z*=kE4jA(boq7pn5pa3mM96)vcBmgTX(2*as?uyI_NmgMtCUw7kA#KiYobY-7a`u-L z>wi$49|TwbsIK-h{gX6awJ1{!HOQ={)M@KiMit;KiIw3$ium>nZuOE&mBb&VWB_tn z!4wZns(acD7IJ018sYIb=q|I{X=2i}hdHAP`Uqm$ETf|O;Ha;$(suM}61SsoM!ZWxE{49|m9RZV$UxX$_8OvNlkGp5ATVmeaP9+coW zaR)(=a-KZDVJ-=gxl~q$4}Dvt#3gtAY#7{=*iRkTRi6@qa90=duf+!%?lw>)qW2${BQ)DM`u&zlHtc?(5i}V#u z{_9>B968gsgM&Yyg?x*(>vQPfP9~4yrA||DOdMf#7emKWCC;R+i`NkAq8nqjs4%WE W$WSr6nvtmsg?QXDu>)HH@&EwzvzC4U literal 0 HcmV?d00001 diff --git a/public/pepper.webp b/public/pepper.webp new file mode 100644 index 0000000000000000000000000000000000000000..fdf9fa102d05805d9e3bde725a93067a1ee47623 GIT binary patch literal 946 zcmV;j15Nx=Nk&Gh0{{S5MM6+kP&go-0{{T<5CEM4DklIg06uLjk42;+p&0IE06+%B zwg66?psJq${>{ci%1;$>-qCs0^&hsk%$J;xNC!yQ><6$1><6ZQvS;lN?60&0PeTCg ztuQ4(_NV)|s5h1Gv2jAu$uXFA$p1lF0g>TPmSfrwSh59*9_PLAeC5Mj|C}n}pTv~+ z{X)=UfLcB(RzSK+I}`u_{D>WMD{eUS!nQz@I`GC4uG0OhsPhXAV|2C9`PewXD=~CR6Y5Sh};&jMWwEn|704hJ5%n_CN1W9mv zoB2uvzPEm(?hnNo3EVv_xg~e#gIde_E=mWt7IPtnOZ|2%06lf)vBbc*^Nx}VlQUn% z$}CY@a4myyleoePQfWMo5OzNcYQI+*DJ+YuvZ=a8gXkJWYi@U$$d>?*? zx+VXIseW}#{*Wf2*uDU}D-X{@3^$_YlHBonOYb_CZ(=l5b}QO+nMVMm&?NgcD?5S{ zA9bTLkww5j;oyiV_Em~Fup++!YdFGa;$G|Vzg<0E%oi750G%oFbg6XgvW%3?!y&5* zDjub<;?~CY_ZOQ|M^4+`(^@TK+zy2aq}Nm5X|T$Z-Oiwf-MX+7mhGj@+^v?J>fb~k z;7vLgqWkL0dOzLnDk!4o>J-`4tSe?i`NZDG*#fi;_UsexWAr|D++S~aH?KOM(ffVs zkrC%Q|HA|@^%KO7rTT1B<}Pb@t82t{1dg~~yCbx_-YHzIt7tUd0--O9Um0ne4Y*K5 z*J9^Pl=nAxi6cX6Nv25cCDhf+1B~X_0rs}1fF&5IJ+pmf2WT8lWdGRp!UpZ8GWrbb z`90>A$!Sup;v_@1Uu3#MqNcz$oq2szbFb`!{SaLar&mKm{`R)6M%JFG!^m`%`s!pN zpSpf{gp@FqwV2_fqG&>pzjb`oUiw9^>7r|Gl;M&}JZBvqkwt2!2PX=TU)pg=?%@X7 zDP0Lvty-hTG`&EtE0?m!FUgA9$rKt%k+o-KhJNVzt(U0pWgk+|J(i0_t*W;{^#M(!T;%>>A&~?w)c|! zz4x#FOVmgFH~Y@F4?$lqAKY(;pX=ZCe|5d7zy5#R?fTS#=I82sE%Ryh9SCxl+7_gJ z@8hR{zbUl~_XnIzE;^g}K7=0w{zuKObU!?@Ye4TCO+~6SG$OD;{*KMoZV8dwZ}rt1 zdPKs6;J=v9n;kTl`s&wLe3k|m(Y94p1ESAuoCbt+958W!FO<&hj!Xrl{E4!LXM_MaB*fsPv&|+2Lp?XJS z_a<}Z8+)KI6H4}N=nnRNFS2Hq*Zl|Uwj1*`y9nLG73w!dc{V_GHM1x%a!Qp7DkXky z+(z**3JiUPpb{rzI^4_OX#T<Z9$>YkPW1C#c zc12w}e(m^-BcDao9BP#l?C4am6C8^1b zif};%R`2kj=s`Ftq^Du(SOoLulQ11^{ct#&FfXmmm>;&qcL>YG>{HV8w4IzZ=nm2$ zB!v1-ho6&ECc#(oR*dm`n6pR@w#6MqEH%|926eQLw{HoNp#2MvC^;}T^{=39`+)x!Yr!iKfkLe96*FKT1QZd) zOBsv()UC|k!9#advM--!6VPuryq6*ICR21wjT74NjO7w4LO1AfT0FXw;6`-~vK*7W{f0}a8hhKp zDrAq>P~CW-6Cq#4?|y(WKd!mbp_5C2P8Gw^R{El75mw(=xWIKR$v82C*RW#1jF#xMU*!?rXI@aOrqE00v8G_jgzWn1)Nb@V|1CLzj~Au1{f zo0csXM>wPmV&Bo2!CH^w2wv%8{}8d2be+x zzvidP_30=ecl6F5AT!Q_(ZKdV;CKf}U|YnGAVPH_HD+`@IHxB}OqK6#1U_~5ij&Q@ zTJy|i(MoV}DeMaKJQE9{a8NPqYPdTsI~tYwon-eN z9q+(H>ZA#uce7bR_7WvCBo7!fssJ1Sg;q%#tb)eu`-YcLc$*V_toc1RnytC8mamOz z@}oYMD-}MCYR+zACT^zlb~kM^i#>&WT(RAJ-VN zv9`t#JwT5;%~-i)RzY4Y5#$EfZj{`_;@JY$y!sew$2( zbfNW^x;DtVviAHL?c7zdSW~qz1{um{;5JBpy%-6eLl1K7^v*WcE`Y|{i8M}@i75-d z^x1M75(%n5UpF8cbbA;BE(0`u5$ET;;u~anc4?N$g;9qp$A{EL03S!~4F(Yvv~^mpXmUxjv>2P4 z6d;%nwX*<`e$WxbU*^E!G#uRlGm4GFjaq5apC|gs%Ylmt-z&-QvXl36Qwr5W_F)Y* zg~0+4Ewx8eK{eT|gb;sW#hvKA1qR6((G~`gqv%m%@amF&2LeB5cfjZ<$BmmwB_LPI z5zeML*%Z2$wP&COdyHs$i(F+7s}Gj8tf{WG@#h2C1Ae^&5jDt!Mc3`x`cCe0x1WJV z543?%i|EN97u~<_1@tIz3>C2xIA8?fnWpu6DQ_ zt9}wa-g)G*sE(_g!*Ey&<6o$ECIsY1puaMlbDl;3Rf~N0H3`{`Go90qA^kj^UGN)G zA1k|To@e`TyIZ0JlcsB$9QmOEw3Nl3T}O6$!}%Z_*XLw6MJ*O3p>1!>N$hQoetq=9 z^c3h>Tmb2t-DLMd)vmgL0+o95p8C5*2rFCM{^{O~qj);V;-wWjg{jxX^$EpxmC}KK z1Xzy!*kl7@NLfp)8Ir6Mxpn@4%A&HX&`7dHv#PfPOXH2{rRgp0Na(#zk%7hzc*Gno z8Vt!iBuQyKTUNN%rO`-?v}HF#0-OK<{`67NpZF2k5s6yhyuwv>Q&fJ=Uhh+hX+Lpi@B;6(IF@S-r82T-UBFb{ zU!*y1#MM?;(Aqa7$8+ktU~yH$naxqXRma)-Ih+xL)T|O5k19>D)X2_G?8-{W@BnOs zV7Ql6tu@J{*RAke@OAC}bsar5UPx0fAqa~6Xh{IOJ<$>~z1tv|8s)0|bI$$+l1~~b zY5N0JYBe3Sy~srVRr^!?A#9I}q5vAhG83$^(T@k+F&J%V!@N|^#)@UGd1<;~LE8eV zb>uvWv$vyJ*Wol{g_p3Z9SRb9=QE?QWSYwKQ;QuT+`x55%QTC4i4XzEBoOE749~iS z5hYfPI6RH6wO$sZ-NFy4nj&+hyoVl;qd?hw*er2DNFS7~hfJk*pWxNk^KOBeDkwQc}+y3=^+_CghHA793;DY zP+wv6xEI_P7kWncRE_Lh@X4?}YAm532_zGup)+E&mT_ZOxpv;mR(x}0JkS=^SB*XF z#;jQkA0CepzrRgtipw+E*nsk+860Q+zG0I)`|SZ3#l+52D}M2KYbY!$cI@{hi=YV* zV-lqZCNO;A9Ao~J(R;4{OF5EG?zi~;9Xjl_)x4QYqTg>$BE|u1>s?|ben!)mk0@zK z;K^LuIO^ISqwn-lyb_p7oqb{p*}%61H2!Rr~rDeu-)h2QZA?br$Sd2OP-+CzNgae*zS3IG6d3-T~JO}2rgHQrafS!kRc zCRw(?jg=lB2B_c;Yr(TSYcqqmciQvNJ4*=F3&TR5-`Rvz-+ymjx-!qZ7JpK%`s9*+ zdcY`gC=E^jRpkl-@1J%Vd&4g z2s^3}cHLsVD#u0TqSr#ws8Evalhc9@yN!TG$>W%CE;tZGH2=3+W{Sh1g z4_H;b5#kiVB`9NIkW(llA@b>egcvFG|2@z9{By~433G^sk-|7!%UaqO&mbJlymoJ? zXXc~O1Z)~h9)OB>lb4vL89RaW=(7MyvPj19L7r@Q3JDJQ>|e|V0@ZHwd02Lwg{e8K*@Rwm zj4z3V)EgEFm}{y{UuTLI%A`S`2qo5-AcLQLWhlKh)WGg^-S@YVici#%A!>ffb(P(_ z20cRB2dQO2rhTj!rN(y=`l@L-&g=t=)W-WzD+b7u&g$~4Y_ccz_M}xJ@G_E0izM!go+I=ZhtY|j8PSZ>o9gdsrWS@meS`O zO?a4xW+fY&60-Xd+zP+;@2=1R#XJsoIsv7r#~Nc0O-svMO0(nnj5+;2=XZE2a3jJZ z(ZRSL_m&gd9bH$2ILh#Zb2y?K*SyfZy7SKq5#SLpYq`Gj`t2Wa39p)TyNH?XjB1>z$UCj0Q zdwlO~OY(Zk0}&Ye8esajPn!vPA(j&>(%=EAS^|?+m?jjgycE z#M1qwnM(&wYwY9KB@mYW71orV3s+8ov!aFOY0ugjngN_4I$6|sO9hZe3vcTcIooOk zP3uE%;B$r=+TpZ%CxjaTsXzj zR}6o4Ptd(hR`cJlN1{Ni8(*5Bw&j7Pf==?+u7K;g3yL4sq*CQG<(9v7zirT|0sc4P zsYi(&RP4M9G+QVD?}(uR3&$7eAaalhlBdC*La_4XW1_)Vbv0E>9y8%ZpNc(^)2(+R z#VJxV-qH3b&y*CqRW+#GySfz3jk%1tF`S zDC_8vqIH)!MAd8=grF-}sxIt1_uIMJ2M3C&9Y&>UroE>`+oy5}SRB;A8w3b2b;Oqy zbnyVGk5~BnFkey36)xc^4*?jiPOmtb8{Y^QRXU--x+ihJRz{C_pj(IqXO=gEfd@uK~&mVvBa0p;_v9k~pm_s{GJsVxmZk^Cl20_3MsG~NA{aI+6a zHK{<302Ts;i3%(4yQ^C0j>&XAm#z`o0;pP=+j6rCY97PSEb*m}m-hBsmFis=;iyT# zD_|L>h=CBeend9z&5}0o@>k5{%=~MK(aZ4VIzWO2~o%-av4^)Jd^y} zj_g&{Hxy>ypKii=88UcoQXvqJ4z&fv_M4cP^0P8P;Uq|L$EZBzOBg5}9no{G#*aow zn-^!AAx>EUA6Z@4keIi$PkGMuGTQ!^?V*m?a}z@y%24zNRldxgre}$ceoR@L3h#~V zOao&?G^gJK%=-r7hABaEHjFK1K79cv`J9vi{F09EWxI@fClR)jI^AC3(dkopNFl0R zBhRMqDP^7o>}G8OI{OK5*L{DEyGtoO{U#yRu@?6sk|%;Rk=#8y0n>)OTtS}v%^4!3 z_?B%#tYl74bi&y-3osaSmyt~rL`nV$478dHHSo0KiMXqhfXJJ{BV$E3$xN6O1T_p8 z;B*lrv&=a&Q2$`hlU<{8!(7+L7o_5)!uRX6aU3?PV5cQ3uX!e~6YG|pnfi%fT#3fg0+aH`m>J&k=8vY2+xC<9Y*A(LUsOu!4n{IQaN5HHP6G|q5ry=? zLIxIUDNw=8TA#ud%~=x^_Z6u;yw@;=cyGPX(cPNd;h}WAlzG9G^ZA|tj?)G(Ybe>5 zakwzN)4Vhrd~T8t974hwX3aSM(&OKIRFDi1+6bdyKwzn)Qt?s0cwa@O0wTHW>#Eax zuN8>AF`B<$t6mI(ma8bR_a*H5>d2r5J9Wm3Xs5@H=I}sqcbz43u@GSPMs6FxFC~pU zwuPiQy_`Kih?^NIY?R?MI0L~ptjEey1C8d)Uxt)v2%zRLSCMgSdcnM;qf zWOUxUDE53~DxN;eB=NS4Pp;!iu!oC{&66)DjN^FnZ__B*_=!jj>5HYMivfX%s6Ycv zc~{BPdv`OI2uebW!5{L{Yd2vr`CfdvPoT|u5QpwRrRrd5i2e9BMQ=;Y-AE4G61rV6 zb-`d9a_u>F)>(L8nR7`vaS}|oXtXWYY)D$T^FDfU4nbhlG(n_7Hguv03d*s8lYPGB z_L6Lr&>d2E;VxPzbP?2j5CLhFHL6WeR^e~ZrXz=5*FTfZQ1HOk08p8EA8oOy_G^FHzCb?rdm{=td4!>mA4a8455hC1dF#*TjymKg}UzATF$@ zRtlM~V$imA=uPqRunmOcz$8y%i)GCsL-~?(G+A5RCNXDUPp(eJrP=2Co{%88L*Aka~9cl%z3iCNpm#-@$zG^v;e6qi_aa_7}2PcY2P* z-Zz+;yd$`WE=?5>rm9aGfkkW-OIkhD(k!UAvQIRUn*+md-CReqMB$``OWtQ4a4HGO z@I?>uvJUE3MW5#}v@~s+S{U75e*x!U9HNnu$KqD$>$Oi@C$Q;FwJLF3b_kW4cLj8Z zTaPmSwL%}`IoBVd;xc1-xCG;Zf)Mu~KWr_%fW&HLUfWG~_oQv*%l-oblV)L^=Wo6r zjRsvtkP!C*KLF6E^3Wx9s4G8tkQ@>D(H2Nb+qJ(MdbiXO+Qn zWQIua-%f4J8n)1|Edwomaca@YVm1Gj4aeIYwB2ZWp~poMy8iTVoNVe0u{vK~(g+eI zso?SsnS41h*w0SBZ5)pIII=nNTny|FgFl2==}!JEOzJO&QfLACuP&r1(kAC$2haX- zhGMlzCLp z+LLWjxv+MZ(R`Zidk!s23$DoHU5GCf58`O zn606Gu>i(3nVQgPgB1Wy`CTMa|1qv)BuouG{`wDBn_#|Ms@kX!Obsld?#>_;9nh&q zB*=Tz=rfjhZ23{8aUT|`ai%I-NCq@ue&_C+5@+C=phH+5=cw5Pzz`--NYRYES|134 z#+60YVRNkqp)4Qp`G^88W zen^ooun{bAoKxG?E8%62D2jW`I4;Phu+6U&?pkzkm4Dx)F z?$bRa(S^t7ikB~`XK`)R$UagRUUpvD_N!Ty9s=f-3|`@2VN;C+ zJ6_kZq$Xz0W8(*tFvR9|3VGd)kQrhQ8;RmhG^s^0aF?}Hf^pOyVC5FA-UbW>I4w$&JxYG2JStaU5 z>fSVC-DLS`RdZYyD41EAU@1S5KULS%R64J6Vp^(3$RYD7r|;!Ez0@ zE2!CY|6zU526h81SMQP*t0r1hacUk$F6^NC+AmmMu6aYg_Ktm1vA**w8f+~VX$!)d zHeFAEm>L{@S}Spa)ao(>ZI@>5ygx0z55)m2D;T?@GT&*S2+Gx)S9h7^(|M1M12r8|w_hi!v=tWNA%ZL~wl z*%oHxW}%0h)y{{OM_Z3JxAj4P(<2}9CrM4D)johQ2CC%a%vxe$UhqY*HsAK=H#jLz z*;u#Hw!2e^C|f{;>ZNV8+S5b?eK15ilP*qm9P$<%xS>@UyZZiItBbylke`3Ihoql{ zhl@}DL);)4P~1Y9r=RCNl&TY>7ellHUlX`<2F-UqOX;!Y?5P)4{6Jh!aiR|}O1vVc z&Rj>J6-ad3;?zX~#eqqc%2_M0@(#G-#%qI0uot$4LT zSJy?>xz!BH*39h*4859>|G}A*BHUA}{AUkjC{pK#D=>x6n;|j5Xf30a0970PeC(Fv zxg9yL+lAwEPRXR(ao|7&yRl4Q{VZo#8GOt|4Ar_4t%gJYpj_ zpCHCE1l`cmL*DdS*4!Gd?tj*~D`piR@ufVY^ot|$PT$)zwFsrj$hcK6sIef*t5?Vv zC5#0>ufZ&e6?D}^`-9E7bfHPjU|*&US|$q-z`PP2ba98iR(^!A>49|H{&g zk9&yo+(gzP4;Fv)VKrb~-YyIDb}p$)dDowFEn9jNbx_C}zRS|`87I%}#hG zEsX8aAjQTVU@i;l=aHmKKOkw252UwLp=0~MjV6123)C9qKC-qP9xgM^a zNQ^j_WfGQPgzfT#4L>=|F?%xRLH^Lsh+w{=I#(YO3&)OyHaj}?2}6TDtc$qd^tf%= zDW69(xjYcA7yo$}c+Uj^9Z=EUB$QF@Nh2+EmMD;(xjromk+YN=jHV8t7z8zF4r^d8 zE^0ObxFkATqh9~soXUW4ySI2rQhW?g8XhGOZaVh?BEu({7ra8+j6n!o8K5cKioTvb zJnKdCcNXPGM4K*5KwXomXYEAG+Wv)B)(i#h97q-uU309knGNcG-7EJ3;7;uHk;zt-os_wG2b73YPNMtIF{y0Bg}? zi=+%b=DVacNGjTLAjEL$oqJ$z5;9rAFBrpX;PzE^7`zH!Icif`RP!R(7ZfC#!zg+L zFvB(`4r|yJr=K6kQRX7bKk@Vf)r(MWHFF*QQjKMb_%hv;4IbdHAV_F31$#o7e`UEz zw)2k5xm~*?0Aw{VFDX|1gr(wl-GQ_Y527`E!!GXfDZKG)^^TcdCx|oh5?#1(N7sPb z#en^uZ}QthmMTAv<-KnUPR-s5-n_Vz!yUuml7FJ`O->j+?Dum~b`v_xUV8Eqk79aR z1hm83C5mX>KCAm4uBS9bzE59vsz7UmuCkGu10a1Bl^Rmh`M&a}JNTrAgcZd-SP%v8 z<#Z*q7)$&@p(1e63+5p$A3?kEWbQCRJTki8)XG(Htkq35tPP`sx?Cu>3zr!`&b>&~ zZ}aHVzInNj^t{2pf6FkI1x?X!4TgU*|?Z$K!%SCsIl0CIeA_@Kwhsr;*imr}DUu7^aji!S&>dHcjBX z9iw_}6Ypt%LM=}kv&yT6+QFuAd#3p}&pvS&=l;ujB*f;FF|%tY!_+y<^)NcuI>xMp z`0Dpn-*%`zmsM&~<64qIf5NY^0~9@&!j)muPjN}wtHHgEj$c6)EEVavMP6qz-wWO4 z24ckY%hlZHU*Hn2--~>TDliQZ@^#8e)J#}He3yECo{v~r_|&fo3TV1fjuq!VNL*xD z&d+oh5$mK0ob9}8i365|=1_}Xw;Z-b+uI};(^HQl6Xh*H+~n2GS_ZFIBPtgNT!|*r zRYinQ?I`HZ@6N^nJ!_$J;KTq4j^&+hjemIwquSA@E*!FZmvj? z|9o{gcVV%(ON1;_BI^BU0Yb=}P;`bF<>TX;_qbPOY214g+^;8P^c0C&6O{RLAS)yT z$Rz$ME|GAB*c`jlo5Vr+g=$4MJZF0v|AaK(M3nqAE~4CCyn68I2MLamA8JW0b>;3f zL=UCg8h-_K9!?O0VRIrgMOgJnpHJ3yNjHhgSh!TLUJ#gWUv%p7lEQbXKv!1+X-$Q7 zsmwxhdE~Spc4BTdZW+lPEj6JPbsPVLJ}HnbVqir}pG)U)Oj5RlY%J*khM5Phv)l&o?^?4!WQas~byD?005{qN1Nlj-1<49R87r?T^HaOj&4Xusts3 zP{!boVXbcnx+ebe$-9OA0IViCs~C(Vf_0TZ=P3sT&2DyNgy?tpjd0IGG2$PmQ1vww z3?4eng7`&(VR%u$J{D#~3(9h=KqhPtn>NRYkG`W?99KfRk>l>>^dxX$$0-!7Jz6C^ zynM#|^DZ=m`3&FG3OKWklzXvgTB71!Rv~qVxg8J>&+|k_0m@MZV5YIng$ux=gJt~K(5?4d z+D3mt!)_VFMg(wjfS4?qF`bKkFcMX`RwGEWfw%4kMcVbQpgo-x?;m^_8S*8Ns6jc) z`8xS$$cnN z`IoBPfPV%8p6}_5LrD>1t4lL^H>cUR_n|PJBR{_Gj^j}Wb?$Opp`&@zE9cNc1!T#6 z0=>Ya3c5r2tZHb=AfFBH;rH?#v4mZ3@2SAT~+8&M(>%%Wz%LPnUR?qo_1kQWZmmneo3= zDD)6ATtbN>(j6;lz#e4JQJHJEyb3cBlGTqzKXxi}K`*w)o?fl3~n*T%t#@l3s)Y!)UeQ!TIZubW<1F1Z`%LRI{DXj|^!gJ`@8 z&sDcTCsItK9w5a(Nexk9;6S!1uR7BUd{+zoc;`q}3T?kFzFP6|s2am`9H<08N}Bad zsD8MwHvcR%Hd{rbEhZBgM;tYbWZ^9?>#4xTe?<9jHWrAeui<%0aNw?Pw9$%VqT}v} z;ya>v8_~=q*jf{OQ-Q-(`*3G?m$HB#Z3P+6=hy0rh&kq9E9^!$O|3KZdu84wBu-=l z{&ILuNsV$g?=(P)$=%d{K$?7rx8UvSBHN|*Yo6jFNEwS(3xv(*@yiV3T{X_?B|sfw z>2^ttR+Nr3slh>%07~b6Jru%-5ms2OM!Cv*!d;&xG+A#`sJ&Vzd4#e|4!u~BCfska zis=0286ff;x`D>CL07gh#$oaBwCQ)ivy;PbkwT*U!xQ2VLAESmmIKJdQ`fTuP+k(2 zdY+F`Xnv<3AjbQMN-{YhG+T&WdfN|$y+)#eq`HiPh^1EM^aJ85GB_(uL5ZbRBC?}x zqJ~%VUIiE^=JNap@-_zJE-3|vR!g@5obh^{L*95%4qvRl^D|jJJTiM5QuQ1)6lZzEBA+E!DFe?>0VA=+S;URM4BJncz zS+*F?@v)U^p6ngGOVx*xIA8TbnB8q~KKaT|sdMbitBz;i8D%e*pgIRZUFhil&Y<`6 zWGEc&$ocJzw>$nZbs}%Jp_0SH())_{<2rEZJ1z`!8Qv^ld3AL{JaZr(f2;#u$Bnb) z6`Lu@;e^B~>2Q!e$dWgrtE?Z(zoAdwqg@xO%aEK)EZOvdr;8qVDou0l65aS_pidp2 zWx@s;*2r)P2Y~mI(g>*VHj~n7WHT2zi!=XB{y6hVVD`Gv&4+q(N>{uoGr1%Fq`NT7 zMbLv1<4K!G4rH^lhMmQZwWd=)O8uxs`QmkoA1LdA(X+0swtOehr=vdaWDshtb;*c- zAv0g5jh0C)qsk0R{yei7TJr0`B0~ros*H;XL=AI14ov!rh8iG;;Gr9bG>6=kVav5J z->vt)SAF&qD;n3PbV!wOcFB~h!)b_DDngI<%0r%5d5banFrUN8EX>~DrJ}I2UPG?+ zQsLe7Dw{kg_7_&|w4s*)E4)f!UH%(``4A$U@VcRm@OA%+g?n+y;kqp;0@e0ICEwntD%Up67a!9=LFj*qupGu zY}3dK4EQME_G7+JMk8v@uU%~;V2*sV`P=&TDYtf4ZG~k&kTENJjo4JDG%!TS8oIIrgQr zqOn<7<7m=?rEbf*My< z$V*L`NWWMSXZ(@raa6d7q`Z|`N=3ma%5~sSa+m;5_Bqa@Po6yKmat@fx^$ z&&$$agqn($NiU}WWM+NMTeA=e0ugz6n%w=g1j01*BHC&hKhH-%QXDD(QhJ~WsoN${ zBvf#Wz477T2CeLTDNid7LDuWe^rlSbI?m`kh;3224pu|^pOCcnss6d$Zsr10nb0(> zunJiMGy{zO>OUw!V7ohrg?y&wPsHSKR~eyVfZZsqfWIsXbaoOwrvB@|%cWY+p1}jl zM0uW5ktUX?ixv4f(TqQ#M+j-VXI_= zY%Vg*80wkyOL`-{0#DXmOH=SAT_FEfEvYv~&21lWP5D;NLA=wCrPWc( zmD(PIPT!mmPXlQf9)DejnMa}0Cbt8?AhEZ+<_sdC6WCH|+R5x0r&FDWmJAdmP_ud1 zT$Yb)n=dt2)X(3UTB`8xdY(p{DLEL_%~Xt1WK zlwy+fPf33UbZ_b41T5VXkJhiK^PCO#vq_qUGqr%i1(fXX)dr)DDeY1+3C#7UDn*IN&~ z&e_fEoXO)Ql_PH=LL5p*a+QFN|N0*S=y77R50?bng&UIqTM9Motli%bx2)UGL zr+FTg@!Z7J-^Z+*?~~Y3RU?^Q4`gbprZc)5A~F8DY@$9)qdHoLR_W<2A87*h=#AW& zHb+nn_GZj-RG^meQr2AZ=0n@n^D=zjL-tvygPI*zN%WL^^ND!tm0H4Ls>BzyUQmA` z)jPl-_VE39v1gFxTku^D;;iuhx=_sPlRr#G zbITm{vl)7Oi0&yU8oR~ApsIx&;B2K(48|BL-ROq50EPTUMnl5|d6)VtZfdU^dS3}^ z__;F%ZbJoQXSXeN{VA@snWZ%f5j@Ely?85q1Kyx~1f&s_J)Ldzh&$2KLfqosrnfyv z^!y`=3RA%P=)pHF%!U$eo9^Q_kbFQz9{K@h|G^M};w%E$ueOS_l}&D3wX^OZc8w~_ z@evxYtUDw*!`eF1SRG!3Na+>a5#C-7ulY((sh=0U&MNIoR#+^Y8GQKS@n!L8CTV0g zb+bb4jw;KD7(J{I<*x@ig#6@9#jJPd0eDc$MGs^aTfAweB$i4!ZigkZdvJ4p>$k(f}ysyiu z3*mBaY@8G_K|e{6WK_LZl$OO=&=d9=J|&OMODPqTroZ8xaY>3CvSzs)fnNCJS_UX( z012P738K|y?B5EpHXqSu_0r*73TpzSep;y(4yyaPT<||cz3!mseQ^sYU`q$QJ2Z*v z->J31d>1L=_q&3oNE`RL^Fdeur4jdk?YwJ*!tVSDIR_8K@X;Q3#>3wnXAGo7>n|ty zn)a|5rQKY(oTd22agiyUc}O2-LWI0HUKaT%61VyaD!hQS70K2xFHq7Z&e^=(KK@#PBXpJMG>Kxb8ydEl(EJ7qc%}5ZO?_1xYh*&Jhl`=l7h~L!IvX-EX zV~AGow^09x${*~R?`Vs=w9_A+hhX5YXn=&?a;LmV!I^fr{hhQn8EMUSs2l9W>tikI z>x;)AJ~JhR6DH8wD-x79+T4l&+%-sQM?Crn>bv}=x-p+t$}0E zB51RobUvR&`(&%N4DNp^(&E5Lf-M9_H7=OKVNK0p9+`oJ+wHmnHO3K;ufOj!v_ZUW=)dTrd zqvl7_mz`QiPHJOeDnKg+6UA<-z4(Zwuk)XW*P`(`XQv-hM&}+0=QGzZ!bTq(Rrhpx^kiSg@U?B_9l3f}s3>V5VzL?{mCp?HCaZ7vnHYou3WEbniwJQE=)KdAk zMF@9sQ=m7 z+jlb|QjTx{F!!3*@NRQ1Z%phxS}Y8N%rH@T1HXjmV1p*r8$SZdD8~|lm>U%`#{Uln0 zWn9M*$<&q&fzM%=V;*ohV*d!p#x6k+FWi`vM`$(SZx%Nf#K17^mHK`0EvVwSX)m3AYy;B&|MO|#lw4e*SnSi zp&`U$mgA>)M1P+b_=)@29VL5+PSn|M%S%Ll>9xBZ2mfJW`ni~PgDIGQOpR0fyxPhRvS}$=&v2%bi`A*^IQ@?#F0LKV5x^Emgl+0;3a2&|tq; zfU)M_|D)oOm`?$TUn!YV6=y&icdeDR9-ZqskrcO|U-Rlg*)!9)-|_8`1NV&?$D~o^ z%tns;)3d=gq9|sn8XD_dia4PWe_0U-LTPURlS&1(q&zOeVR6ngz9MeJAw%%W!FD&T za3M4;lI|3P00E4%BATk@(~g{lNbBcPu7}u=w>?`Y+OoQb2#v=EJZ`q7z6D9?DHlWz z7bMQijnq<~3Zmb8@0Q^>(R6oL;mu6X9AO=5NDH)VCLvgjtU~u=#0W|H^6{*P>0$OV z&V2m9<%HH@jYroIs`P4ESId>wX8?&ya$%-xS_eS^t_db;9)T5Yyd#v{b2WmS0#B;4 z_t<0rMGe8d)6e@-fYp(3=idHi*?Xmj<~LS{IKy@vfm5$4V9O$^e=W$f_r=ZtQ`@9u5kZ_2#!>#3Zee7Wa<9x?bCf%4nI|2@ouTN4P<0`YJ!z( zj>lc!{7PI)e!0^SH@OtABd2xrO=!i2cnd}xsj0-?QWB8eaLda`__^~;cYXNE`PWV0 zq5fKi_pmPb9+T~3E~srVHN*EL^>KHot?x+u`VZfH6fjbcx^(|u&2qZKYzgzK&Q69}Kd+;N>Y!dFZ2Nx|e*F6=I)0-LRl`7Syovhw3a8uYYZXS7j)8F+ zO7qdMZGR%YF~M~}FZeL`pb6MtH$e3?Fz5ka5uolCnVp5|SG`+FKN2DbhBqk|{boty z65DKTI&IikRLL(kqlEiE>tQ#i>Sm<7vQP!oWF)aAfLvh_t8=T80UHg@lHlI%0Q8m{ z(_OS)4-=H<2SD0rn@)2jC9ei({TY(T?ww0G1gLZU`9^o%t1;12k9|snL8qtii~GqX z;7Wkh_?)@`g?PDca6%i! z+`Oz>W+}`QU)YcX{a#~4`1Z<2aEE9Ck-&_PC}+pzi|!G)B3SHC?%RP~Xk%R(32y3= zc7n7n0iJG`8B%1~XGJ0DP_mYxQb|;MK3wlj2c+O5l96URn5^kn){>P7Ul5R_3c}Nf zr$EVD<^kRxQGlYwdJruHZY#%g%#R|7_SkhAxWq-eV;JI0ZlAwXPTSt%eVCx&)8}}(*zhNsE())lv$DGNsKAw92W`vlAS zPDmoas=h4KJj4V~TT>E}T|efx1|A7U|39ei^Mw?Y)ID=eI#U?z zK|%V=fn7irG76y4l-paciC}Ny(9n(3b7pKd zv2nUs9f^mfjchbM;<)CqL~{(z}_%j>jg{CEV^UBjf%Jhz>@Zfjr60e2xr> z2Zq-|T@#v-+Tk*75aOJvI_c%;3_l`1kGNvsi({CPQ~-1DO%owS)OX;^1vvi7`Qb_sIa)3;swC*H;<>4bvy+{Us4duj@3l(7=9am(n5Y>2%-zUF2OKz17TJCX6@_*-bU@f}V zzw&3AVVA_s+qlv~QENE{%#88b5>I&8#rE3?dqL4Xuk`eeU@{!cW?apPA76MS9`bkx z4H#K{glzX>m??@s7Dt*~>=mLO9!{B!g~v_)d><3%0k?5+%VTnxgldyAwHDECbLAM} zZN!~fPu!K0_dV-EyO6lJW;o#3V}hJ>6Trm5jBHMA`@W(j>=R3@WPeM+6@z4>X?UNu z-g}r{hMvyI>kPHUzFjCvEsYPSR}Pe(QKN(-->by{wy$B2Lp+l1E~3d=NA@`jSOFK| z8ch#_2qO+W8Oxkdx!M}9F~4jzwIhrS(emmI3jkAOYGxH;YoC4l$7Cy;IW*+uuu zkCaIltDx^|celoL$7^jSDt3tu+gg8zNq3>_h&4-4Qmw_AFWwq~C2{gnO-9VYAY@fh zw(Ddz#b66x9paEES{dQYzqLi+%Fo|j`_X#4uc|IqmJXaOB$gkogu`~vnoS@cOwqpl zHK;K`W+5)KbJn7NYT2IKs$?+6L_LgeUtc(08f+ws*848{6iufx3naT|G_?F98F=eJ zdMGMki;i5Vv;hK1G(#TJ(JWdw1eDjTsU)dlzlw6J%!%--+AN>OXiZTgNWHGSh-pP# znf!FyDv3Sw8CJU-Mi$+EwePyUNGh{UI;1ZhJw&wp7-9WZ!K+`vz0lX0{Q%jev|M~IakqDT4^yRqy-CJ9+vEH9s>#`?lADibL z5hN@Udb9;gF;V?aAmq%x&AcZ(D_C3e6TJ7Vm89&!98myXaX3Riz_{w$Z~aWi!Eo<@ z;7XkT%FlFz=yEC`qJV#rl@;%Jd^nVguh^?#g#y@%2@Z|DlwUS=fYa&sb3eN6R(|=( zl?)J6<`*(+_9!!jS}i_RM$N)IkW8bbQzJ!9bn8eLnN|DeX^9=58b%l|?YkbLtl)Aa zJEH2XudDRj-xdK|u2*&FC0OBQK=qN%S&*-bjzQT0Sf5-9+Zc}HUEu#_t41J!pw1kz z^-sxyk;p)RmVEgy{4B#*{(EnMS*fvA*4bx^TLun9!ly?9-xw8Nx(r&P_=Ny0%qdcY zQ7$Ychrt&7!dG|#Zt6^VpyAi6{m(<60)0gf4W26|yNv5iu*K$Fy4$nF>mEa_d`e^+ zN3m!Q{_{MHOyfs4AAQ?1eVU|jz-Tc(d16KdHPP8h_y820sAaI1fsjjH<*%`s?JZ}< zT98wYQ?}xQBtA4{GY>=hh9~gH?b6yr5W97afmS?7g|oktytxSs&06@B-p>(|>cL^f9hz#lM{8a`7o=fW4hDPbq<&98Gk8x+3(8G1ZB^*&wuolU*#711fN zS%SJF79cEhecGW4p&YUq-F?kB*ujFBjZl+pYVJ#<2hu>!Xfz^0PARt%nZg-% zWQ*r!KiJiEaXT_(F%5f``QG?T9zT^vDm1z#ReQPT=@bgei-*jxv(6<-Lu5bgBD~4B zTxuoep6nJe!C}VS(gDmhc5m(X z9G6%?RH!H}g>XZnsXzW{jOA^`4S3^%uMFV?uP)AX4{5pcMx;%F8|;BuZxhA*^^|xL z$%UPPAqt9sK!}zKm5PW@9+X*UOB%9K?{ViS#pTvLM*NpFw-VqHsi^!-4BVO2Iu@|3 z;>xKklGv7^#WQ!L2uyVqLFaU5G_4{^I=+5jJ6R}OwU!T`obs0PoC{4U*yJkY<<6OU zmsFepoJ`Fg4f8Kj99gJ@0Me0adHg4mEVWU5FI$!x>I zC$zg!twE}VELdCc_@vxow`5M5c<1llmMB62rWE9^<)UY$CYEB>VSYQrlhXshK=LwX z%dk!zwv2y7mER z1Lq{k-{PSQ*h_u}U_A>`#%*4ykB^rLyFOzSVX~*GJDiR$OQ$jJN*D!&r~d>3*v~;5 zc-Erf13G0cC9CslcM_LVs0}?pbu(q0M(NGzwQh5bE8I6LMn@0Sg!5fD;*jlGB6Et@ z&kbpl$W+=?_`px)G)aU@VnwpeiMX$Evlj3Zgs1Z1#GT}YE_C26%^k*pD9;>FKqF(K zZvK1keT+SIXN-4p^I7wab`IfdLMK$?O$ZcE=FESH4N)-4s&zxf?llQ^`L31jQ{e1V zk^uXTqZ7NAJz~9tL>0x;BN1H)Xz~|`R5(4Z$NqajV4h{6nC*FN^MH&&uMQmsc4j)AW3qq+Lr)=OSvw zp#<3s>Gd3f(p7t+%72wxdx)G9jiG+p^%}+a9)qZdDtLLSqc;G}wJZ(;z1XPRJwTh> zIy3>M6}8|`;`R}SAOj4M2HCUtLFkL$EYHnb zP177&ZKnVZb^0ktPtBGxtLzfs@Z^^cy@VZKQFI(>onVx^INkIcEwNfnY4v4~vWEl}~ zfNM7!hz>r7WFnENRN9`dZxEHEYh>>CDl5kCT#Gc5=HWc>0H0{*AfYW}*GD zf8!)CD{jU#8#4x9*>>KsyGQWzJMr%9qt#{rIb=5`Vz4lnec3Y4_Ll1mqMG@U0F|Lj>sN(93Sl zq+x$c(;f)|m17#SlZML5I+khaZedf{o-<4%n8}l>+Xk%HTM=q)i!(3M&eDH`_q|}Y zu*%6pl6oYTZn-UmGTx={-t0ra6167>g_7Yh8r-aqY%#55bhz$rx)d;2J2>c?J#1Kr JOc#0J001AUnSlTR literal 0 HcmV?d00001 diff --git a/src/App.svelte b/src/App.svelte index 7f3bd8f..81dfbdd 100644 --- a/src/App.svelte +++ b/src/App.svelte @@ -8,6 +8,7 @@ import ReadMePage from './lib/ReadMePage.svelte'; import ConfigurePage from './lib/ConfigurePage.svelte'; import FreeStuffPage from './lib/FreeStuffPage.svelte'; + import SaveEditorPage from './lib/SaveEditorPage.svelte'; import UpdatePopup from './lib/UpdatePopup.svelte'; import GoodbyePopup from './lib/GoodbyePopup.svelte'; import ConfigToast from './lib/ConfigToast.svelte'; @@ -76,6 +77,9 @@
+
+ +
+ diff --git a/src/lib/save-editor/MissionScoresEditor.svelte b/src/lib/save-editor/MissionScoresEditor.svelte new file mode 100644 index 0000000..f51bbec --- /dev/null +++ b/src/lib/save-editor/MissionScoresEditor.svelte @@ -0,0 +1,31 @@ + + +
+ {#if $currentPage === 'save-editor'} + + {/if} +
+ + diff --git a/src/lib/save-editor/ScoreCube.svelte b/src/lib/save-editor/ScoreCube.svelte new file mode 100644 index 0000000..93db3a5 --- /dev/null +++ b/src/lib/save-editor/ScoreCube.svelte @@ -0,0 +1,226 @@ + + +
+
+ ? + Click on the cube to cycle high scores. Changes are automatically saved. + +
+ + + {#if loading} +
+
+
+ {:else if error} +
Error: {error}
+ {/if} +
+ + diff --git a/src/stores.js b/src/stores.js index 97dd8f4..cf9e708 100644 --- a/src/stores.js +++ b/src/stores.js @@ -7,7 +7,8 @@ function getInitialPage() { const pageMap = { '#read-me': 'read-me', '#configure': 'configure', - '#free-stuff': 'free-stuff' + '#free-stuff': 'free-stuff', + '#save-editor': 'save-editor' }; return pageMap[hash] || 'main'; } @@ -35,6 +36,7 @@ export const installState = writable({ // Config toast export const configToastVisible = writable(false); +export const configToastMessage = writable('Settings saved'); // Debug UI visible (set when game reaches intro animation) export const debugUIVisible = writable(false); @@ -44,3 +46,11 @@ export const gameRunning = writable(false); // Service worker registration export const swRegistration = writable(null); + +// Save editor state +export const saveEditorState = writable({ + slots: [], // Array of SaveSlot objects + selectedSlot: null, // Currently selected slot number + loading: true, + error: null +});