merge both repos! atempt 1 by making the file system the same!

This commit is contained in:
RezHackXYZ 2025-05-29 13:11:49 +05:30
parent badb303ea6
commit 2fe58ee6be
No known key found for this signature in database
128 changed files with 2320 additions and 4285 deletions

View file

@ -0,0 +1,28 @@
<script>
import tools from "./tools.json"
</script>
<div class="flex items-center justify-center p-3">
<div
class="flex flex-wrap justify-center gap-5"
>
{#each tools as tool}
<a href={tool.link} class="flex justify-center">
<div class="card flex w-full max-w-xs flex-col items-center text-center">
<img
class="mb-2 w-full rounded border-2 border-white"
src={tool.image}
alt="how the tool {tool.name} looks like"
/>
<div
class="flex items-center justify-center text-2xl whitespace-nowrap md:text-3xl lg:text-4xl"
>
<i class="nf {tool.icon}"></i>
<span class="ml-2">{tool.name}</span>
</div>
<p>{@html tool.description}</p>
</div>
</a>
{/each}
</div>
</div>

View file

@ -0,0 +1,87 @@
<pre class="w-full text-center font-mono text-[4.5px] leading-[0.6] text-gray-500">
##########%#% ############%#
#%#################% %###################%#%%
%%###################### ###########################%
########################## %###########################%#
%###########################% %#############################%#
##############################% ######%##########################
%%###############%##%############ %#################################%
###############*+=-----=+*%#######% #######+--=+**#####################%
%%###########+=-------------*######% %######=--------=-----=*############%
############+----------------.######% #######------------------=*###########
##########+------------------.######% %#######% ######*----------------::::=##########%
%#########=-----------------::.######%############%% %######+--------------::::::-=##########
########*-----------------:::.:######################% %######=------------::::::-====*#########
%#######*:---------------:::::.-######################## %######=----------::::::-=======*########
#######*:::------------::::::-:=########################% #######---------::::::-======+++=########%
########=::::----------:::::-==.+#########################% ######*-------::::::-======++++++=#######%
#######+==:::::------::...:--==.*#########################% %######+-----:::::.:-=====++++++++=+######%
%#######+===-:::::-::...:===--:.:################%########## %######=---::::::=.-==-=+++++++++++-#######%
#######*++====::::::. =#######################+++++*######## %%%% %% #%%% #######=-::::::===.####++++++++++++-*####### #%#%###%% #%%####%# %%#%####%%#
#######++++====::::..*#######################*++++++==############## #%#####%% %###############::::::====-.#####*++++++++++==##################% %#%############%% %%%%%###############
#######+++++=====-..*#######################%**+++++-:##################% %############ #################*::::======--######+++++++++++-###################%% #################### %%%######################%%
######*+++++++====.=##############%##########****+++--#####################%%################################+::=======+-=######+++++++++++:######################%#################################################
######*++++++++==-.##########################*****++:=#######################################################========+++:+######++++++++**+:#########################################################################
######*++++++++++--##########################******+:+#######################################################+=====+++++.######*+++++*****+:##########################################################################
######*++++++++++=-##########################*******.*#######################################################====+++++++.######++++*******=-##########################################################################%
######*++++++++++==########################%#*******.########################################################==+++++++++=*###*+++*********-=##########***#############################################**+**#############
######***++++++++=-########################%#******+.#####*++++++*###############*++++*#########*+===#######*++++++++++++++++++***********.*########*********############********##################*++++++++++##########
######****++++++++:#################*+######*******+:####+++++++++++**########%*++++++-#######*+++==-+######*++++++++++++++++************=.#######************#########***********+#######%#+++++=======+++++++*########%
#######****+++++++:################***+#####*******=-###++++++++++++++++++####+++++++++=#####++++++++-######*++++++++++++++*************+.+#####**************+*######***********+++*##%**+++=============+++++++########
#######*****++++++=+##############****+*####++++***-=##**+++++++++++++++++:##++++++++++-####++++++++++-#####+++++++++++++***************::#####****************=#####*********++++++=*##++==================++++=*#######%
#######*******+++++=#############******+*###+++++**:+##***++++++++++++++++.#********+=:.=##+++++++++-: #####+++++++++++****************::######*******+=+*******=###******+++++++++++-##+=========-----=======+++-########
#######********+++++=###########******++=###++++++*.*##****+++=-=********=-#********+.=####++++++++::*######+++++++++*****************=.######*+*****-.:==******-###***++++-.:-=+++====#*=====-----------:======+=+#######
%######**********+++*+*#######*******++++=##==+++++.##*******-.=*+*******:+#*********-#####++++++++-*######*+++++++***=+************+++-######++++++=.*###******==##++++++:.*###======:#*==---------------+=======-#######%
########***************************++++++-=*====++=.##******+.*###*******.###**********####+++++++++=######*+++++*****.==-=*******+++++:######++++++:=####*++++++-#*++++++.*####======:*#------::--------:#=--====:########%
#######*************************+++++++=.=+=====+=:##******=-####******+:###**********+####**++++++++*####++++******* ###*=****+++++++-+#####++++++:*####*+++++=:#*+++++=.#####+===--:+#--------#-------.#+----==-*#######%
%#######**********************+++++++==::#+======--##******==####******=-####+*********+###+***++++++++###++********+:#####**+++++++===-#####=====+-*####*+++++=:#*++====:#####=-----:+#=------:#+------.**-------=########
%#######********************+++++++===- *#=----==:=##+******=####******:+#####+*********=###+*****++++=*##**********=-#####*++++++=====:#####========####++++++:-#*======:####+------.+#=-----:.#*:::---:+#--------########
########+****************+++++++====:.+##------=:+##++*****+*##*******:*######+********-####********++-##**********:=######++++=======:#####+========*+=======.*##======--**+-------.*#+--::::.*#:::::::=#=--------#######
%########+**************++++++=====:.*###-------.*###++***************:*#######********-+####*********.#***********.*######++=======--:+####*--==============::###===--------------::##*::::::.+#:::::::-##--------.######
########%=***********++++++======:.*###*-------.####+++**************=*######++++*****.*####********=:##********** ########======------#####=----------====-.*####---------------:.+###:::::::=#-::::::.##+------.-######
##########=+*******+++++++=====-.:#####+-------.#####=++++++++++++++++-####+++++++++*-.###*********+.+##*********=:########====-------:######--------------.=#####+------------::.-####::::::::#=::::::.##%+:---.:#######
%#########*=+***+++++++=====-:.=######+::----::######=+++++++++++++++:+###+++++++++-.*###********+.-###-=+****++--#########=---------.######*-----------:.=#######+-------::::..-#####-::::::.#+::::::.*%##*-:..########
%##########=-+++++++=====-:.:*#######=::::--:-#######=-=+++++=+++++-.####*+++++++:.*####*******=.=####*+-::--=+:=#########=---------.#######*::-------..+#########*::-:::::...=######=::::::.#*:::::..=#####*+#########
############=--======-:..-*##########=:..::.=########*-::::.:=====.+#####====-:.:#######***+=..+###########*=--##########*------::..#########=::::...-#############+:.....:+########+::::::.*#=...--+################
%#############+=--::-=*#################*+=##################===.-#######-:..=#########*-:.-+############################----:..:=############*==+*################################*::....:*##*#####################
#############################################################*-:#########+*############*+################################:..:=*####################################################*:=+*##########################
########################################################################################################################**####################################################################################%
#######################%@%##############################################################+++*#######################################*#######################################################################%
###################%@@@@@%##############################################**+++########*++++++++###############################+=-::=###############*+-:::*########################################%%###%%
################%@@@@@@@@%##################++++*############**+==---------:#######++++++==-:#########################*+=:::::::.=#########*+=-:::::::.######%%%%%#################%%%%#######%
##############%@@@@%#%@@@@@%############*+---------###*+=------------------:+######========-:#########*##########*+=-::::::::::::-#####+=-::::::::::::.######%@@@@@@%###########%@@@@@@#######%
############%@@@@%%####%@@@@%##########------------.##=------:::::---------:=######========:=########+::-+*#####:::::::::::::::::.###=::::::::::::::::.+#####%@@@@@@@@%#######%@@@@@@@@########
##########%@@@@@%########%@@@@%#######::::---::::::.##=:::::::::::::-------:=#####*-======-.+########-::::::-*##:::::::::::::::::.###=:::::::::::::::::=#####%@@@@@@@@@@%###%@@@@@@@@@@#######%
#########@@@@@%############%@@@@%####-:::::::::::::.*#=::::::::::::::::----:-#####+-------- #########:::::::::*#:::::::::::::::::.+##+::::::::::::::::::#####%@@%**%@@@@@%#%@@@@@#**@@@########
#########%@@@@@%##########%@@@@@@###*::::::::::::::.*#+::::::::::::::::::--::#####=--------.########*::::::::.=#=:::::::::::::::::=##*:::::::::::::::::.#####%@@%****%@@@@@@@@@#****@@@#######%
%##########%@@@@@%######%@@@@@@@@###=::::::::::::::.*#*:::::::::::::::::::::.#####--------:-########+::::::::.*#+::::.........:::.:###::::::::::::::::..*####%@@%******%@@@@@%******@@@########
%###########@@@@@%###%@@@@@%#@@@###-::::::::::::::.*#*:::::::::::::::......-#####--------.=########-:::::::: ##*::...............+###::::::::::::.....:#####%@@%*******#@@@%*******@@@#######%
############@@@@@@@%@@@@%@@%#@@@###:::::::::::..:::*##:::::::::::::::.=+**######+-------:.*########::::::::.:###...........:-=*######=...........:=+*#######%@@%********%@@#*******@@@#######%
%##########@@#*%@@@@@%#*@@%#@@@###-:::::::::.=#######+....::::::::::.##########=:----:::.########*::::::::.=###:........=###########+........:#############%@@%********#@@#*******@@@#######%
%#########@@#***%@@#***@@%#@@@###-::::::::.:#########**###+::::::::.*#########-:::::::.:########=::::::::.*###=........=#*+=-+#####*.........##*+==#######%@@%********#@@#*******@@@########
%########@@#***#@#****@@%#@@@###+:::::::::-##############+::::::::.*#########::::::::.=########-:::::::. ####+........:......+#####:........:.....:######%@@%********%@@#*******@@@#######%
########@@@#**#@#***%@@%#@@@####.::::::::-##############*::::::::.+########*::::::::.+########::::::...:####*...::::::::....-#####-.............:.*#####%@@%********%@@#*******@@@%#######
########@@@@%*#@#*#@@@@%#%%%####-.:::::::.###############::::::::.=########+:::::::: ########*::::.....=#####::::::::::::::::#####=..........::::.*#####%@@@@%#*****%@@#****%%@@@@#######
#########%@@@@%@#@@@@%##########+..::::::.*##############.....::::-########=:::::::::########=::.......*#####=::::::::::::::.#####*:::::..::::::::+#####%@@@@@@@%#**%@@#*#%@@@@@@@#######
%##########%@@@@@@@@%############.........-##############:......:::########-:::::::.-########:........ ######+:::--:::::::::.######:::::::::::::::=#####%@@@@@@@@@%*%@@#%@@@@@@@@@#######
#########%#%%@@@@%##############-.........##############-........:########::::::::.=#######*.........:######*:------------:.*#####-:::::::::::----#########%%@@@@@@@@@@@@@@@%%##########
%############%@%################=.........=#############-.........########::::::::.=#######=.........=#######--------------.+#####=::::::::-----::#############%@@@@@@@@@%##############
%##############################=.........-#############=.........########:::::::::=#######:......::.*#######---------...:-=######+--------:...:-*##############%%@@@@@%###############
#######################%######:..........#############+.........*#######-:::::::::######-.....::::.########+-------:-###########*--------.*#####################%@@@%################
###########################*-::.........#############*:::......+#######=:::::::..:###+-....:::::.=########+---------############--------.########################%#################
#%###################%=----:::::::::::.####%########*::::::...+#######*.................::::::: *########*--------:############=-------:+######################################%%
%%##################=-----:::::::::::##############::::::::.=########:..............:::::::-:-##########--------.############+-------:=###########%%####################%#%
#%########%%######=------::::::::.=##############-::::::::-########*............:::::::--- *##########=-------:+###########*--------:########% #%#################%
#%% #######=-------:::::::.###############=---::::::#########-.........:::::::----.-###########+-------:=############--------.#######% ###############%
%######=------------:.+###############=-----::::##########:......:::::::-----:.*###########*--------:############--------.*######% %##########%
#######+-----------:.+################=--------.###########-...:::::::------:.*#############--------.############=-------:=###### #%####%#
%######+---------:.-##################+--------.############=:::::::------:.:*##############--------:*###########+---------######
%#######+::::...:=*########## ######*-----::: ##############-:::------:..+################=-------.+###########*-------::######
##########*+*#############% #######+..:--=+*################=::::...:=##################+--::..:-#############--::..:-*#####%
########################## %###################%#############***#######################=:-+*#################:-=*#########
%######################% ###################% %###########################% #########################################%
###################### #################%% %#########################%% ####################%##################%
#%################# ###############% %######################## %##################% %################%
############### %############# ####################%% ################% %###############
#%##%%%#% %#######%## %%%###########%%% #############% ###########%%
</pre>

View file

@ -0,0 +1,11 @@
<div class="mt-5 bg-gray-700 p-3 text-center text-2xl">
<div>
Made By <a href="https://rezhack.xyz" class="text-blue-400 underline">RezHackXYZ</a> for
<a href="https://neighborhood.hackclub.com/" class="text-blue-400 underline">Neighborhood</a>. •
Source code available
<a href="https://neighborhood.hackclub.com/" class="text-blue-400 underline">here</a>.
</div>
<div class="text-sm">
and the "<a href="?ascii" class="text-blue-400 underline">?ascii</a>" flag for a surprise!
</div>
</div>

View file

@ -0,0 +1,42 @@
<script>
import { onMount } from "svelte";
import Ascii from "./ascii.svelte";
import RightCards from "./rightCards.svelte";
let FlagAscii = false;
onMount(() => {
FlagAscii = new URLSearchParams(window.location.search).has("ascii");
});
</script>
<div class="flex flex-col h-full justify-between items-center">
<div class="flex h-full items-center justify-around w-full">
<div class="flex w-[600px] flex-col items-baseline gap-3">
{#if FlagAscii == true}
<Ascii />
{:else}
<img
src="https://hc-cdn.hel1.your-objectstorage.com/s/v3/77e7a04f27807b4e0c16bcda09ea222f9e091616_group_18.svg"
alt="ClassRoomStuff Logo"
class="w-[600px]"
/>
{/if}
<h1 class="text-center text-5xl">
The
<span class="rounded-full bg-blue-800 px-3 py-1 text-3xl">
<i class="nf nf-cod-sparkle"></i> ultimate <i class="nf nf-cod-sparkle"></i>
</span>
classroom
<span class="rounded-full bg-green-800 px-3 py-1 text-3xl">
<i class="nf nf-cod-tools"></i> toolkit <i class="nf nf-cod-tools"></i>
</span> for all the teachers and students needs!
</h1>
</div>
<RightCards />
</div>
<div class="rounded-full bg-gray-900 px-3 py-1 text-xl text-gray-500 w-fit m-3">
<i class="nf-fa-angles_down nf"></i>
Scroll to see more tools!
<i class="nf-fa-angles_down nf"></i>
</div>
</div>

View file

@ -0,0 +1,24 @@
<script>
import tools from "./tools.json";
</script>
<div class="hidden flex-col gap-3 lg:flex">
{#each tools as tool, i}
{#if i < 3}
<div class="card max-w-[320px]">
<div class="flex gap-2 text-4xl">
<i class="nf {tool.icon}"></i>
<h1>{tool.name}</h1>
</div>
<p>{@html tool.description}</p>
</div>
{/if}
{/each}
<div class="card max-w-[320px]">
<div class="flex gap-2 text-4xl">
<i class="nf nf-fa-angles_down"></i>
<h1>and more!</h1>
</div>
</div>
</div>

View file

@ -0,0 +1,37 @@
[
{
"name": "Kahoot Clone",
"description": "A Kahoot clone built from scratch with a Postgres DB, AI integration, and more.",
"link": "/kahootclone",
"icon": "nf-md-chat_question",
"image": "https://placehold.co/1800x1000?text=screenshot+TBA"
},
{
"name": "Wordle",
"description": "with unlimited tries, customizable world lengths and more.",
"link": "/wordle",
"icon": "nf-md-file_word_box",
"image": "https://placehold.co/1800x1000?text=screenshot+TBA"
},
{
"name": "Announcer",
"description": "To let the have computer <strike>talk</strike> shout on them, if they don't listen to you",
"link": "/announcer",
"icon": "nf-md-speaker_wireless",
"image": "https://placehold.co/1800x1000?text=screenshot+TBA"
},
{
"name": "Name Selecter",
"description": "to chose any student randomly, with memory of previous names and more.",
"link": "/randomname",
"icon": "nf-oct-people",
"image": "https://placehold.co/1800x1000?text=screenshot+TBA"
},
{
"name": "Time&Table",
"description": "The perfect idle screen when theres no teacher with the timetable, time and date!",
"link": "/randomname",
"icon": "nf-cod-table",
"image": "https://placehold.co/1800x1000?text=screenshot+TBA"
}
]

1
src/routes/+layout.js Normal file
View file

@ -0,0 +1 @@
export const prerender = true;

View file

@ -0,0 +1,9 @@
<script>
import "./tailwind.css";
import { Toaster } from "svelte-5-french-toast";
let { children } = $props();
</script>
<Toaster />
<div class="h-full font-[Sour_Gummy]">{@render children()}</div>

9
src/routes/+page.svelte Normal file
View file

@ -0,0 +1,9 @@
<script>
import LandingPage from "./+HomePage/landingPage.svelte";
import Galery from "./+HomePage/Galery.svelte";
import Footer from "./+HomePage/footer.svelte";
</script>
<LandingPage />
<Galery />
<Footer />

View file

@ -0,0 +1,36 @@
<script module>
import { Modal, Content, Trigger } from "sv-popup";
import Time from "./components/time/DisplayCollsOfTime.svelte";
import TimeTable from "./components/timetable/DisplayRowsOfTimetable.svelte";
import EditTimetableDiv from "./components/timetable/EditTimetable.svelte";
import { colseModal } from "./logic/TimeAndTableData.svelte.js";
export let ShowSeconds = $state({ v: true });
</script>
<div class="flex h-full flex-col">
<div>
<button
class="btn"
onclick={() => {
ShowSeconds.v = !ShowSeconds.v;
localStorage.setItem("ShowSeconds", String(ShowSeconds.v));
}}
>
{#if ShowSeconds.v}Disable Seconds{:else}Enable Seconds{/if}</button
>
<Modal big={true} close={colseModal.v}>
<Content>
<EditTimetableDiv />
</Content>
<Trigger>
<button class="btn">Edit timetable </button>
</Trigger>
</Modal>
</div>
<div class="flex flex-1 flex-col items-center justify-center">
<Time /><TimeTable />
</div>
</div>

View file

@ -0,0 +1,40 @@
<script>
import { onMount } from "svelte";
import Row from "./row.svelte";
let ShowSeconds;
let ampm;
onMount(() => {
ShowSeconds = localStorage.getItem("ShowSeconds") || "true" == "true" ? true : false;
setInterval(() => {
ampm = new Date().getHours() >= 12 ? "PM" : "AM";
}, 1000);
});
</script>
<div class="m-5 w-fit rounded-lg bg-gray-700 p-3">
<div class="flex items-baseline justify-center">
<Row type={"hour"} digit={0} />
<Row type={"hour"} digit={1} />
<h1 class="m-0 text-[200px] leading-[200px]">:</h1>
<Row type={"min"} digit={0} />
<Row type={"min"} digit={1} />
{#if ShowSeconds}
<h1 class="text-[75px] leading-none text-gray-500">.</h1>
<Row type={"sec"} digit={0} />
<Row type={"sec"} digit={1} />
{/if}
<h1 class="text-[75px] leading-none text-gray-500 ml-3">{ampm}</h1>
</div>
<div>
<h1 class="text-center text-5xl text-gray-300">
{new Date().toLocaleString("en-US", { weekday: "short" })}
{new Date().getDate()},
{new Date().toLocaleString("en-US", { month: "short" })}
{new Date().getFullYear()}
</h1>
</div>
</div>

View file

@ -0,0 +1,11 @@
<script>
let props = $props();
</script>
{#if props.size == "small"}
<h1 class="flex items-center justify-center text-[75px] leading-none m-0 text-gray-500">
{props.digit}
</h1>
{:else if props.size == "large"}
<h1 class="flex items-center justify-center text-[200px] leading-none m-0">{props.digit}</h1>
{/if}

View file

@ -0,0 +1,29 @@
<script>
import { onMount } from "svelte";
import Digit from "./digit.svelte";
import { updateTime } from "../../logic/updateTime.js";
let props = $props();
let size = props.type == "sec" ? "small" : "large";
let digit = props.digit;
let digits = digit == 1 ? 10 : digit == 0 && props.type == "hour" ? 2 : 6;
let thisRow;
onMount(() => {
setInterval(() => {
updateTime(thisRow, digit, props.type);
}, 1000);
});
</script>
<div
style="--height: {size == 'small' ? '75px' : '200px'};"
class="flex h-(--height) flex-col overflow-y-hidden scroll-smooth"
bind:this={thisRow}
>
{#each Array(digits) as _, i}
<Digit {size} digit={i} />
{/each}
<Digit {size} digit={0} />
</div>

View file

@ -0,0 +1,37 @@
<script>
import { onMount } from "svelte";
import { timetableData } from "../../logic/TimeAndTableData.svelte";
let legend = ["", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"];
onMount(() => {
timetableData.v = JSON.parse(localStorage.getItem("timetableData")) || timetableData.v;
});
</script>
<div
class="m-5 grid grid-cols-[auto_repeat(var(--NoOfPeriods),_auto)] gap-1 gap-x-1 gap-y-2 rounded-lg bg-gray-800 p-3"
style="--NoOfPeriods: {timetableData.v[0].length - 1};"
>
{#each timetableData.v as row, RowIndex}
{#each row as time, timeIndex}
{#if RowIndex == 0 && timeIndex == 0}
<span class="rounded-xl bg-transparent p-1.5 text-center text-xl"></span>
{:else if RowIndex == 0}
<span class="rounded-xl bg-blue-800 p-1.5 text-center text-xl">{time}</span>
{:else if RowIndex == new Date().getDay() && timeIndex == 0}
<span class="rounded-xl bg-green-600 p-1.5 text-center text-xl"
>{legend[RowIndex]}</span
>
{:else if RowIndex == new Date().getDay()}
<span class="rounded-xl bg-green-600 p-1.5 text-center text-xl">{time}</span>
{:else if timeIndex == 0}
<span class="rounded-xl bg-green-900 p-1.5 text-center text-xl"
>{legend[RowIndex]}</span
>
{:else}
<span class="rounded-xl bg-gray-700 p-1.5 text-center text-xl">{time}</span>
{/if}
{/each}
{/each}
</div>

View file

@ -0,0 +1,58 @@
<script>
import { onMount } from "svelte";
import { timetableData } from "../../logic/TimeAndTableData.svelte";
let data = $state.snapshot(timetableData).v;
let legend = ["", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"];
import { colseModal } from "../../logic/TimeAndTableData.svelte.js";
onMount(() => {
colseModal.v = false;
});
</script>
<div class="m-5 flex flex-col items-center justify-center rounded-lg bg-gray-800 p-3">
<div
class=" flex flex-col gap-1 gap-x-1 gap-y-2 overflow-x-auto"
style="--NoOfPeriods: {timetableData.v[0].length - 1};"
>
{#each data as row, RowIndex}
<div class="flex">
{#each row as _, timeIndex}
{#if RowIndex == 0 && timeIndex == 0}
<input class="rounded-xl bg-transparent p-0.5 text-center" disabled />
{:else if timeIndex == 0}
<input
class="rounded-xl bg-green-900 p-0.5 text-center"
bind:value={legend[RowIndex]}
disabled
/>
{:else if RowIndex == 0}
<input
class="rounded-xl border-2 border-white bg-blue-800 p-0.5 text-center"
bind:value={data[RowIndex][timeIndex]}
/>
{:else}
<input
class="rounded-xl border-2 border-white bg-gray-700 p-0.5 text-center"
bind:value={data[RowIndex][timeIndex]}
/>
{/if}
{/each}
</div>
{/each}
</div>
<div>
<button
onclick={() => {
timetableData.v = data;
localStorage.setItem("timetableData", JSON.stringify(data));
colseModal.v = true;
}}
class="btn green mt-3">SAVE</button
>
</div>
</div>

View file

@ -0,0 +1,20 @@
export let timetableData = $state({
v: [
[
"07:50 - 08:50",
"08:50 - 09:40",
"09:40 - 10:30 ",
"10:30 - 11:00",
"11:00 - 12:00",
"12:00 - 01:00",
"01:00 - 02:00",
],
["English", "Sanskrit", "Math", "Lunch", "Hindi", "Social Science", "Science"],
["English", "Art & Craft", "Math", "Lunch", "Hindi", "Social Science", "Science"],
["English", "GK", "Math", "Lunch", "Hindi", "Social Science", "Science"],
["English", "Sanskrit", "Math", "Lunch", "Hindi", "Social Science", "Science"],
["English", "Computers", "Math", "Lunch", "Hindi", "Social Science", "Science"],
],
});
export let colseModal = $state({ v: false });

View file

@ -0,0 +1,31 @@
export function updateTime(RowsObject, Digit, type) {
let DigitsHeight = parseInt(type == "sec" ? "75" : "200");
let LastZeroPos = Digit == 1 ? 10 : Digit == 0 && type == "hour" ? 2 : 6;
let currentTime;
if (type == "hour") {
if (new Date().getHours() > 12) {
currentTime = parseInt((new Date().getHours() - 12).toString().padStart(2, "0")[Digit]);
} else {
currentTime = parseInt(new Date().getHours().toString().padStart(2, "0")[Digit]);
}
} else if (type == "min") {
currentTime = parseInt(new Date().getMinutes().toString().padStart(2, "0")[Digit]);
} else if (type == "sec") {
currentTime = parseInt(new Date().getSeconds().toString().padStart(2, "0")[Digit]);
}
if (currentTime == 0) {
if (RowsObject.scrollTop != 0) {
RowsObject.scrollTop = LastZeroPos * DigitsHeight;
setTimeout(() => {
RowsObject.scroll({
top: 0,
behavior: "instant",
});
}, 500);
}
} else {
RowsObject.scrollTop = currentTime * DigitsHeight;
}
}

View file

@ -0,0 +1,20 @@
<script>
import List from "./components/CommonAnounceedTexts/list.svelte";
import Add from "./components/CustomText/add.svelte";
import CustomText from "./components/CustomText/CustomText.svelte";
import { onMount } from "svelte";
import { LoadMostUsedAnnouncement } from "./logic/LoadMostUsedAnnouncement.js";
onMount(() => LoadMostUsedAnnouncement());
</script>
<div class="flex h-full flex-col items-center justify-center gap-5 p-5">
<div class="w-fit rounded-2xl bg-gray-900 p-3">
<h1 class="text-center text-4xl">Most Announced announcements</h1>
<List />
<hr class="my-5 w-full border-gray-600" />
<h1 class="text-center text-4xl">Or announce something else</h1>
<CustomText />
<Add />
</div>
</div>

View file

@ -0,0 +1,14 @@
<script>
import { DeleteMostUsedAnnouncement } from "../../logic/AddAndDeleteMostUsedAnnouncements.js";
let props = $props();
let announcementID = props.announcementID;
</script>
<button
class="cursor-pointer rounded-2xl bg-gray-800 p-2.5 text-2xl transition-all hover:scale-120 hover:-rotate-15 hover:bg-gray-600"
aria-label="Delete announcement"
onclick={() => DeleteMostUsedAnnouncement(announcementID)}
>
<i class="nf nf-md-trash_can_outline"></i></button
>

View file

@ -0,0 +1,12 @@
<script>
import { MostUsedAnnouncements } from "../../logic/announcerData.svelte.js";
import Delete from "./delete.svelte";
import Text from "./text.svelte";
</script>
{#each MostUsedAnnouncements.v as announcementText, announcementID}
<div class="flex w-full gap-2 mt-2">
<Text {announcementText} />
<Delete {announcementID} />
</div>
{/each}

View file

@ -0,0 +1,13 @@
<script>
import { AnnounceUsingTTS } from "../../logic/AnnounceUsingTTS.js";
let props = $props();
let announcementText = props.announcementText;
</script>
<button
class="w-full cursor-pointer rounded-2xl bg-gray-800 p-2.5 text-2xl transition-all hover:scale-105 hover:bg-gray-600"
onclick={() => AnnounceUsingTTS(announcementText)}
>
{announcementText}
</button>

View file

@ -0,0 +1,9 @@
<script>
import Input from "./input.svelte";
import Play from "./play.svelte";
</script>
<div class="mt-2 flex gap-2">
<Input />
<Play />
</div>

View file

@ -0,0 +1,16 @@
<script>
import { AddMostUsedAnnouncement } from "../../logic/AddAndDeleteMostUsedAnnouncements.js";
import { CurrentText } from "../../logic/announcerData.svelte.js";
</script>
{#if CurrentText.v}
<div class="flex w-full justify-center mt-2">
<button
class="text-1xl w-fit cursor-pointer self-center rounded-2xl bg-gray-800 p-2.5 transition-all hover:scale-110 hover:bg-gray-600"
onclick={() => {
AddMostUsedAnnouncement(CurrentText.v);
CurrentText.v = "";
}}>Add "{CurrentText.v}" to "Most Announced announcements"</button
>
</div>
{/if}

View file

@ -0,0 +1,9 @@
<script>
import { CurrentText } from "../../logic/announcerData.svelte.js";
</script>
<input
bind:value={CurrentText.v}
placeholder="Type in here what you want to announce"
class="flex-1 rounded-2xl bg-gray-800 p-2.5 text-2xl"
/>

View file

@ -0,0 +1,11 @@
<script>
import { AnnounceUsingTTS } from "../../logic/AnnounceUsingTTS.js";
import { CurrentText } from "../../logic/announcerData.svelte.js";
</script>
<button
class="cursor-pointer rounded-2xl bg-gray-800 p-2.5 text-2xl transition-all hover:scale-120 hover:-rotate-15 hover:bg-gray-600"
onclick={() => AnnounceUsingTTS(CurrentText.v)}
>
📢</button
>

View file

@ -0,0 +1,13 @@
import { MostUsedAnnouncements } from "./announcerData.svelte.js";
export function AddMostUsedAnnouncement(announcementText) {
MostUsedAnnouncements.v.push(announcementText);
localStorage.setItem("MostUsedAnnouncements", JSON.stringify(MostUsedAnnouncements.v));
}
export function DeleteMostUsedAnnouncement(announcementID) {
if (confirm("Are you sure you want to delete this announcement?")) {
MostUsedAnnouncements.v.splice(announcementID, 1);
localStorage.setItem("MostUsedAnnouncements", JSON.stringify(MostUsedAnnouncements.v));
}
}

View file

@ -0,0 +1,7 @@
export function AnnounceUsingTTS(text) {
window.speechSynthesis.speak(
Object.assign(new SpeechSynthesisUtterance(text), {
rate: 0.5,
}),
);
}

View file

@ -0,0 +1,12 @@
import { MostUsedAnnouncements } from "./announcerData.svelte.js";
export function LoadMostUsedAnnouncement() {
let TempMostUsedAnnouncements = JSON.parse(localStorage.getItem("MostUsedAnnouncements")) || "";
if (TempMostUsedAnnouncements == "") {
MostUsedAnnouncements.v = ["Please be quiet"];
localStorage.setItem("MostUsedAnnouncements", JSON.stringify(MostUsedAnnouncements.v));
} else {
MostUsedAnnouncements.v = TempMostUsedAnnouncements;
}
}

View file

@ -0,0 +1,2 @@
export let CurrentText = $state({ v: "" });
export let MostUsedAnnouncements = $state({ v: [] });

View file

@ -0,0 +1,18 @@
<div class="bg-grey-900 flex h-full items-center justify-center">
<div class="flex flex-col items-center justify-center gap-1 rounded-lg bg-gray-900 p-8 shadow-lg">
<h1 class="m-[0] text-6xl">DaKahootClone</h1>
<p class="m-[0] mb-2 text-lg text-gray-400">The best ever kahoot clone.</p>
<a href="./kahootclone/join">
<button
class="cursor-pointer rounded-full bg-green-700 p-2 transition-all hover:scale-110 hover:-rotate-10"
>Join a game</button
></a
>
<a href="./kahootclone/create">
<button
class="cursor-pointer rounded-full bg-blue-700 p-2 transition-all hover:scale-110 hover:-rotate-10"
>Create and Host a game</button
>
</a>
</div>
</div>

View file

@ -0,0 +1,28 @@
<script>
import UseDemoQuestions from "./components/buttons/UseDemoQuestions.svelte";
import NewQuestion from "./components/buttons/NewQuestion.svelte";
import StartGame from "./components/buttons/StartGame.svelte";
import Question from "./components/Questions/question.svelte";
import { questions, Wait } from "./logic/GameCreateData.svelte.js";
import WaitStartGame from "./components/buttons/WaitStartGame.svelte";
import GenerateQuetionsUsingAi from "./components/buttons/GenerateQuetionsUsingAI.svelte";
</script>
<div class="bg-grey-900 flex justify-center p-5">
<div
class="flex flex-col items-center justify-center gap-1 rounded-lg bg-gray-900 p-8 shadow-lg"
>
<div class="flex gap-3"><UseDemoQuestions /> <GenerateQuetionsUsingAi /></div>
{#each questions.v as question, index}
<Question {index} />
{/each}
<div class="flex gap-3">
<NewQuestion />
{#if Wait.v == false}
<StartGame />
{:else}
<WaitStartGame />
{/if}
</div>
</div>
</div>

View file

@ -0,0 +1,23 @@
<script>
import { questions } from '../../logic/GameCreateData.svelte.js';
let props = $props();
let questionsIndex = props.questionsIndex;
let index = props.answersIndex;
</script>
<div class="flex items-center gap-2">
<input
type="radio"
value={index}
name={index.toString()}
bind:group={questions.v[questionsIndex].correctAnswer}
class="input"
/>
<input
placeholder="Option {index + 1}"
bind:value={questions.v[questionsIndex].answers[index]}
class="w-[500px] rounded-lg bg-gray-800 p-1 text-center text-white"
/>
</div>

View file

@ -0,0 +1,54 @@
<script>
import DeleteQuestion from "../buttons/DeleteQuestion.svelte";
import GenerateOptionsUsingAI from "../buttons/GenerateOptionsUsingAI.svelte";
import Answers from "./answers.svelte";
import { questions } from "../../logic/GameCreateData.svelte.js";
let props = $props();
let index = props.index;
</script>
<div class="flex items-center gap-3">
<div class="mb-3 flex flex-col items-center justify-center gap-1 rounded-2xl bg-gray-600 p-2">
<div class="flex h-fit items-center gap-3">
<h1 class="mt-2 mb-3 text-2xl">Q{index + 1}.</h1>
<input
type="text"
bind:value={questions.v[index].name}
placeholder="Question {index + 1}"
class="h-fit w-[500px] rounded-xl bg-gray-800 p-1 text-center text-2xl text-white"
/>
<select
bind:value={questions.v[index].answers.length}
onchange={(e) => {
const newLength = questions.v[index].answers.length;
const currentAnswers = questions.v[index].answers;
if (newLength > currentAnswers.length) {
// Add more answers
while (questions.v[index].answers.length < newLength) {
questions.v[index].answers.push("");
}
} else if (newLength < currentAnswers.length) {
// Remove excess answers
questions.v[index].answers = currentAnswers.slice(0, newLength);
}
}}
class="h-fit rounded-xl bg-gray-800 p-1 text-center text-white"
>
<option disabled selected>Options</option>
{#each Array(7) as _, i}
<option value={i + 2}>{i + 2}</option>
{/each}
</select>
<DeleteQuestion {index} />
</div>
<div class="flex flex-col gap-2">
{#each questions.v[index].answers as _, answersIndex}
<Answers questionsIndex={index} {answersIndex} />
{/each}
</div>
<GenerateOptionsUsingAI {index} />
</div>
</div>

View file

@ -0,0 +1,20 @@
<script>
import { DeleteQuestion } from '../../logic/GameCreateData.svelte.js';
let props = $props();
</script>
<button
onclick={() => DeleteQuestion(props.index)}
class="flex h-fit cursor-pointer items-center justify-center rounded-xl bg-red-700 p-2 transition-all hover:scale-110 hover:-rotate-10"
><svg
xmlns="http://www.w3.org/2000/svg"
height="24px"
viewBox="0 -960 960 960"
width="24px"
fill="#FFFFFF"
><path
d="M280-120q-33 0-56.5-23.5T200-200v-520h-40v-80h200v-40h240v40h200v80h-40v520q0 33-23.5 56.5T680-120H280Zm400-600H280v520h400v-520ZM360-280h80v-360h-80v360Zm160 0h80v-360h-80v360ZM280-720v520-520Z"
/></svg
>Delete question</button
>

View file

@ -0,0 +1,19 @@
<script>
import { GenerateOptionsUsingAI } from "../../logic/GenerateOptionsUsingAI.js";
import { questions } from "../../logic/GameCreateData.svelte.js";
let props = $props();
let index = props.index;
</script>
{#if questions.v[index].answers.every((answer) => answer === "")}
<button
onclick={() => {
GenerateOptionsUsingAI(index);
}}
class="mt-1 mb-1 flex h-fit cursor-pointer items-center justify-center gap-2 rounded-xl bg-blue-700 p-2 transition-all hover:scale-110 hover:-rotate-5"
>
<i class="nf nf-cod-sparkle"></i>
Generate Options using AI
</button>
{/if}

View file

@ -0,0 +1,14 @@
<script>
import { GenerateQuestionsUsingAI } from "../../logic/GenerateQuestionsUsingAI.js";
import { questions } from "../../logic/GameCreateData.svelte.js";
</script>
{#if questions.v.length === 0 || (questions.v.length === 1 && questions.v[0].name === "" && questions.v[0].answers.every((answer) => answer === "") && questions.v[0].correctAnswer === undefined)}
<button
onclick={GenerateQuestionsUsingAI}
class="-mt-5 mb-3 flex h-fit cursor-pointer items-center justify-center gap-2 rounded-xl bg-blue-700 p-2 transition-all hover:scale-110 hover:-rotate-5"
>
<i class="nf nf-cod-sparkle"></i>
Generate questions using AI
</button>
{/if}

View file

@ -0,0 +1,15 @@
<script>
import { AddQuestion } from '../../logic/GameCreateData.svelte.js';
</script>
<button
onclick={() => AddQuestion()}
class="flex h-fit cursor-pointer items-center justify-center rounded-xl bg-green-700 p-2 transition-all hover:scale-110 hover:-rotate-10"
><svg
xmlns="http://www.w3.org/2000/svg"
height="24px"
viewBox="0 -960 960 960"
width="24px"
fill="#FFFFFF"><path d="M440-440H200v-80h240v-240h80v240h240v80H520v240h-80v-240Z" /></svg
>New question</button
>

View file

@ -0,0 +1,18 @@
<script>
import { startGame } from '../../logic/StartGame.js';
</script>
<button
onclick={startGame}
class="flex h-fit cursor-pointer items-center justify-center gap-1 rounded-xl bg-green-700 p-2 transition-all hover:scale-110 hover:-rotate-10"
><svg
xmlns="http://www.w3.org/2000/svg"
height="24px"
viewBox="0 -960 960 960"
width="24px"
fill="#FFFFFF"
><path
d="M560-360q17 0 29.5-12.5T602-402q0-17-12.5-29.5T560-444q-17 0-29.5 12.5T518-402q0 17 12.5 29.5T560-360Zm-30-128h60q0-29 6-42.5t28-35.5q30-30 40-48.5t10-43.5q0-45-31.5-73.5T560-760q-41 0-71.5 23T446-676l54 22q9-25 24.5-37.5T560-704q24 0 39 13.5t15 36.5q0 14-8 26.5T578-596q-33 29-40.5 45.5T530-488ZM320-240q-33 0-56.5-23.5T240-320v-480q0-33 23.5-56.5T320-880h480q33 0 56.5 23.5T880-800v480q0 33-23.5 56.5T800-240H320Zm0-80h480v-480H320v480ZM160-80q-33 0-56.5-23.5T80-160v-560h80v560h560v80H160Zm160-720v480-480Z"
/></svg
>Start Quiz</button
>

View file

@ -0,0 +1,15 @@
<script>
import { SetQuestionsToDemoQuestions } from '../../logic/GameCreateData.svelte.js';
</script>
<button
onclick={() => SetQuestionsToDemoQuestions()}
class="-mt-5 mb-3 flex h-fit cursor-pointer items-center justify-center rounded-xl bg-green-700 p-2 transition-all hover:scale-110 hover:-rotate-10"
><svg
xmlns="http://www.w3.org/2000/svg"
height="24px"
viewBox="0 -960 960 960"
width="24px"
fill="#FFFFFF"><path d="M440-440H200v-80h240v-240h80v240h240v80H520v240h-80v-240Z" /></svg
>Use demo questions</button
>

View file

@ -0,0 +1,18 @@
<script>
import { startGame } from "../../logic/StartGame.js";
</script>
<button
onclick={startGame}
class="flex h-fit cursor-pointer items-center justify-center gap-1 rounded-xl bg-gray-700 p-2 transition-all hover:scale-110"
><svg
xmlns="http://www.w3.org/2000/svg"
height="24px"
viewBox="0 -960 960 960"
width="24px"
fill="#FFFFFF"
><path
d="M560-360q17 0 29.5-12.5T602-402q0-17-12.5-29.5T560-444q-17 0-29.5 12.5T518-402q0 17 12.5 29.5T560-360Zm-30-128h60q0-29 6-42.5t28-35.5q30-30 40-48.5t10-43.5q0-45-31.5-73.5T560-760q-41 0-71.5 23T446-676l54 22q9-25 24.5-37.5T560-704q24 0 39 13.5t15 36.5q0 14-8 26.5T578-596q-33 29-40.5 45.5T530-488ZM320-240q-33 0-56.5-23.5T240-320v-480q0-33 23.5-56.5T320-880h480q33 0 56.5 23.5T880-800v480q0 33-23.5 56.5T800-240H320Zm0-80h480v-480H320v480ZM160-80q-33 0-56.5-23.5T80-160v-560h80v560h560v80H160Zm160-720v480-480Z"
/></svg
>Wait for game to be created</button
>

View file

@ -0,0 +1,36 @@
import { DefaultQuestions } from "$lib/config.js";
export let Wait = $state({
v: false,
});
export let questions = $state({
v: [
{
name: "",
answers: ["", "", "", ""],
correctAnswer: undefined,
},
],
});
export function SetQuestionsToDemoQuestions() {
questions.v = DefaultQuestions;
}
export function AddQuestion() {
questions.v.push({
name: "",
answers: ["", "", "", ""],
correctAnswer: undefined,
});
}
export function DeleteQuestion(index) {
if (questions.v.length > 1) {
if (confirm("Are you sure you want to delete this question? You cant undo this.")) {
questions.v.splice(index, 1);
}
} else {
alert("You need at least one question.");
}
}

View file

@ -0,0 +1,34 @@
import { questions } from "./GameCreateData.svelte.js";
import { AiPrompts } from "$lib/config.js";
export function GenerateOptionsUsingAI(index) {
fetch("https://ai.hackclub.com/chat/completions", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
messages: [
{
role: "user",
content: AiPrompts.GenerateOptionsUsingAI.replace(
"[question]",
questions.v[index].name,
),
},
],
}),
})
.then((response) => response.json())
.then((data) => {
let question = questions.v[index].name;
questions.v[index] = JSON.parse(data.choices[0].message.content);
questions.v[index].name = question;
})
.catch((error) => {
alert("Error:" + error);
return;
});
alert("added!");
}

View file

@ -0,0 +1,38 @@
import { questions } from "./GameCreateData.svelte.js";
import { AiPrompts } from "$lib/config.js";
export function GenerateQuestionsUsingAI() {
let topic = window.prompt(
"What is the topic of the questions?\nand the number of questions in the topic?",
);
if (!topic) {
return;
}
fetch("https://ai.hackclub.com/chat/completions", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
messages: [
{
role: "user",
content: AiPrompts.GenerateQuestionsUsingAI.replace("[topic]", topic),
},
],
}),
})
.then((response) => response.json())
.then((data) => {
console.log(data);
questions.v = JSON.parse(data.choices[0].message.content);
})
.catch((error) => {
alert("Error:" + error);
return;
});
alert("added!");
}

View file

@ -0,0 +1,51 @@
import { supabase } from "$lib/supabase";
export async function createGame(questions, gamePin) {
const { data: gameData, error: gameError } = await supabase.from("games").insert({
creator: "anonymous",
creationdate: new Date().toISOString(),
status: "lobby",
gamepin: gamePin,
});
if (gameError) {
alert("Failed to create game: " + gameError.message + "\n\nPlease try again.");
return;
}
// Prepare questions and answers for batch insertion
const questionsData = questions.map((q, index) => ({
gameid: gamePin,
questionstext: q.name,
correctanswer: q.correctAnswer,
}));
const { data: questionsResult, error: questionsError } = await supabase
.from("questions")
.insert(questionsData)
.select("id");
if (questionsError) {
alert("Failed to insert questions: " + questionsError.message + "\n\nPlease try again.");
return;
}
const answersData = [];
questionsResult.forEach((question, index) => {
questions[index].answers.forEach((answer, answerIndex) => {
answersData.push({
questionid: question.id,
content: answer,
});
});
});
const { error: answersError } = await supabase.from("answers").insert(answersData);
if (answersError) {
alert("Failed to insert answers: " + answersError.message + "\n\nPlease try again.");
return;
}
window.location.href = `/host?gamepin=${gamePin}` ;
}

View file

@ -0,0 +1,16 @@
import { createGame } from "./InsertGameInDB.js";
import { questions,Wait } from "./GameCreateData.svelte.js";
export async function startGame() {
if (questions.v.some((q) => q.name === "")) return alert("Please fill all questions");
if (questions.v.some((q) => q.answers.some((a) => a === ""))) return alert("Fill all options");
if (questions.v.some((q) => q.correctAnswer === undefined))
return alert("Select correct answers");
const gamePin = Math.floor(Math.random() * 1000000)
.toString()
.padStart(6, "0");
Wait.v = true;
await createGame(questions.v, gamePin);}

View file

@ -0,0 +1,30 @@
<script>
import PlayingDisplay from "./components/DuringGame/display.svelte";
import LobbyDisplay from "./components/lobby/display.svelte";
import { Status } from "./logic/HostsData.svelte.js";
import { AutoUpdatePlayersList } from "./logic/UpdatePlayersList.js";
import { GetCurrentPlayers } from "./logic/GetCurrentPlayers.js";
import { onMount } from "svelte";
let gamePin;
onMount(() => {
gamePin = new URLSearchParams(new URL(window.location.href).search).get("gamepin");
GetCurrentPlayers(gamePin);
AutoUpdatePlayersList(gamePin);
});
</script>
<div class="bg-grey-900 flex h-full items-center justify-center">
<div
class="flex max-w-[700px] flex-col items-center justify-center gap-1 rounded-lg bg-gray-900 p-8 shadow-lg"
>
{#if Status.v == "lobby"}
<LobbyDisplay {gamePin} />
{:else if Status.v == "started"}
<PlayingDisplay />
{/if}
</div>
</div>

View file

@ -0,0 +1,13 @@
<script>
import { PeopleAwnseredQ, Totalplayers } from "./../../logic/HostsData.svelte.js";
</script>
<div class="mt-2 mb-3 flex w-full flex-col rounded-2xl border-2 border-green-400 p-2">
<h3>{PeopleAwnseredQ.v} out of {Totalplayers.v} have answered the question</h3>
<div class="flex-1 rounded-full border-2 border-gray-600">
<div
class="h-4 rounded-full bg-green-600 transition-all duration-500"
style="width: {(PeopleAwnseredQ.v / Totalplayers.v) * 100}%;"
></div>
</div>
</div>

View file

@ -0,0 +1,24 @@
<script>
import { CurrentQuestionDetails } from "../../../logic/HostsData.svelte.js";
import { AnswersSymbolAndColorScheme } from "$lib/config.js";
</script>
<div class="mt-5 grid grid-cols-2 gap-5 gap-x-3">
{#each CurrentQuestionDetails.v.answers as answer, index}
<div class="flex">
<input type="radio" name="question" class="sr-only" />
<label
for="O{index}"
style="
--border-color: {AnswersSymbolAndColorScheme[index].Color};
--bg-color: {AnswersSymbolAndColorScheme[index].Color};
"
class="w-full rounded-lg border-[5px] border-[var(--border-color)] bg-[var(--bg-color)] pt-1 pr-2 pb-1 pl-2 text-center text-3xl transition-all"
>
<i class="nf {AnswersSymbolAndColorScheme[index].Symbol}"></i>
{answer}
</label>
</div>
{/each}
</div>

View file

@ -0,0 +1,13 @@
<script>
import { currentQuestion, totalQuetions } from "./../../../logic/HostsData.svelte.js";
</script>
<div class="mt-5 mb-2 flex w-full items-center justify-center gap-3">
<h3>Question {currentQuestion.v + 1} of {totalQuetions.v}</h3>
<div class="flex-1 rounded-full border-2 border-gray-600">
<div
class="h-4 rounded-full bg-green-600 transition-all duration-700"
style="width: {(currentQuestion.v / totalQuetions.v) * 100}%;"
></div>
</div>
</div>

View file

@ -0,0 +1,9 @@
<script>
import Question from "./text/Quetion.svelte";
import Awnsers from "./Awnsers.svelte";
import ProgressBar from "./ProgressBar.svelte";
</script>
<ProgressBar />
<Question />
<Awnsers />

View file

@ -0,0 +1,7 @@
<script>
import { currentQuestion, CurrentQuestionDetails } from "../../../../logic/HostsData.svelte.js";
</script>
<h1 class="m-[0] text-center text-5xl">
Q{currentQuestion.v + 1}. {CurrentQuestionDetails.v.question}
</h1>

View file

@ -0,0 +1,8 @@
<script>
import PeopleAwnsered from "./PeopleAwnsered.svelte";
import Display from "./awnseringQuetions/display.svelte";
</script>
<h1 class="m-[0] text-7xl">HOSTING</h1>
<Display />
<PeopleAwnsered />

View file

@ -0,0 +1,6 @@
<script>
let props = $props();
let playerName = props.playerName;
</script>
<span class="m-[0] rounded-xl bg-gray-700 pt-1 pr-2 pb-0 pl-2 font-mono text-3xl">{playerName}</span>

View file

@ -0,0 +1,12 @@
<script>
import { players } from "../../../logic/HostsData.svelte.js";
import PlayerBadge from "./playerBadge.svelte";
</script>
<h1 class="m-[0] mt-10 text-6xl">Players Joined:</h1>
<h1 class="m-[0] text-4xl text-gray-400">(Total Players: {players.v.length})</h1>
<div class="mt-2 flex flex-wrap justify-center gap-2">
{#each players.v as playerName}
<PlayerBadge {playerName} />
{/each}
</div>

View file

@ -0,0 +1,12 @@
<script>
import { startGame } from "../../../logic/startGame.js";
let props = $props();
</script>
<button
class="mt-5 cursor-pointer rounded-2xl bg-green-700 p-2 text-4xl transition-all hover:scale-110 hover:-rotate-10"
onclick={() => {
startGame(props.gamePin);
}}>Start the game</button
>

View file

@ -0,0 +1,15 @@
<script>
import StartGame from "./buttons/startGame.svelte";
import Players from "./PlayersGUI/players.svelte";
let props = $props();
let gamePin = props.gamePin;
</script>
<h1 class="m-[0] text-9xl">HOSTING</h1>
<h1 class="m-[0] text-7xl">Game Pin:</h1>
<h1 class="m-[0] rounded-2xl bg-gray-700 pt-1.5 pr-2 pb-0 pl-2 font-mono text-5xl">
{gamePin}
</h1>
<Players />
<StartGame {gamePin} />

View file

@ -0,0 +1,7 @@
import { supabase } from "$lib/supabase.js";
export async function GameOver(GamePin) {
await supabase.from("games").update({ status: `completed` }).eq("gamepin", GamePin);
window.location.replace("/results?gamepin=" + GamePin + "&playerID=host-null");
}

View file

@ -0,0 +1,18 @@
import { supabase } from "$lib/supabase.js";
import { players } from "./HostsData.svelte.js";
export async function GetCurrentPlayers(gamePin) {
const { data, error } = await supabase
.from("players")
.select("playername")
.eq("gameid", Number(gamePin));
console.log("Current players data:", JSON.stringify(data));
if (error) {
console.error("Error fetching players:", error);
return;
}
players.v = data ? data.map(player => player.playername) : [];
}

View file

@ -0,0 +1,10 @@
export let players = $state({ v: [] });
export let Status = $state({ v: "lobby" });
export let questions = { v: {} };
export let currentQuestion = $state({ v: 0 });
export let totalQuetions = $state({ v: 3 });
export let PeopleAwnseredQ = $state({ v: 0 });
export let Totalplayers = $state({ v: 3 });
export let CurrentQuestionDetails = $state({ v: {} });

View file

@ -0,0 +1,26 @@
import { supabase } from "$lib/supabase.js";
import { players } from "./HostsData.svelte.js";
export let LobbyConnection;
function onNewPlayer(Newplayers) {
players.v.push(Newplayers.playername);
}
export async function AutoUpdatePlayersList(gamePin) {
LobbyConnection = supabase
.channel("players-realtime")
.on(
"postgres_changes",
{
event: "INSERT",
schema: "public",
table: "players",
filter: `gameid=eq.${gamePin}`,
},
(payload) => {
onNewPlayer(payload.new);
},
)
.subscribe();
}

View file

@ -0,0 +1,51 @@
import { supabase } from "$lib/supabase.js";
import { onNewPlayerAwnsered } from "./onNewPlayerAwnsered.js";
import { currentQuestion, questions, CurrentQuestionDetails } from "./HostsData.svelte.js";
let WaitingForAwnserConection;
export async function WaitForAwnser(questionid, gamePin) {
if (questionid != 0) {
await supabase.removeChannel(WaitingForAwnserConection);
}
await supabase
.from("games")
.update({ status: `question-${currentQuestion.v}` })
.eq("gamepin", gamePin);
WaitingForAwnserConection = supabase
.channel("answeredby-realtime")
.on(
"postgres_changes",
{
event: "INSERT",
schema: "public",
table: "answeredby",
filter: `questionid=eq.${questions.v[questionid].id}`,
},
(payload) => {
onNewPlayerAwnsered(gamePin);
},
)
.subscribe();
const { data: questionsData } = await supabase
.from("questions")
.select("id,questionstext,correctanswer")
.eq("gameid", Number(gamePin))
.order("id", { ascending: true });
const { data: answers } = await supabase
.from("answers")
.select("content")
.eq("questionid", Number(questionsData[currentQuestion.v].id))
.order("id", { ascending: true });
CurrentQuestionDetails.v = {
question: questionsData[currentQuestion.v].questionstext,
correctAnswer: questionsData[currentQuestion.v].correctanswer,
answers: answers.map((answer) => answer.content),
questionid: questionsData[currentQuestion.v].id,
};
}

View file

@ -0,0 +1,23 @@
import {
Totalplayers,
PeopleAwnseredQ,
currentQuestion,
totalQuetions,
} from "./HostsData.svelte.js";
import { GameOver } from "./GameOver.js";
import { WaitForAwnser } from "./WaitForAwnser.js";
export async function onNewPlayerAwnsered(GamePin) {
PeopleAwnseredQ.v++;
if (PeopleAwnseredQ.v == Totalplayers.v) {
currentQuestion.v++;
if (currentQuestion.v == totalQuetions.v) {
GameOver(GamePin);
return;
}
PeopleAwnseredQ.v = 0;
WaitForAwnser(currentQuestion.v, GamePin);
}
}

View file

@ -0,0 +1,41 @@
import { supabase } from "$lib/supabase.js";
import { LobbyConnection } from "./UpdatePlayersList.js";
import {
questions,
Status,
Totalplayers,
totalQuetions,
players,
} from "./HostsData.svelte.js";
import { WaitForAwnser } from "./WaitForAwnser.js";
export async function startGame(gamePin) {
if (players.v.length == 0) {
alert("you need at least 1 person to start the game!");
return;
}
await supabase.removeChannel(LobbyConnection);
Status.v = "started";
const { data } = await supabase
.from("questions")
.select("*")
.eq("gameid", Number(gamePin))
.order("id", { ascending: true });
questions.v = data;
totalQuetions.v = data.length;
const { data: playersData } = await supabase
.from("players")
.select("id")
.eq("gameid", Number(gamePin))
.order("id", { ascending: true });
Totalplayers.v = playersData.length;
WaitForAwnser(0, gamePin);
}

View file

@ -0,0 +1,47 @@
<script>
import { joinGame } from "./logic/joinGame.js";
import { Checking } from "./logic/JoinGameData.svelte.js";
let pin;
let name;
</script>
<div class="bg-grey-900 flex h-full items-center justify-center">
<div
class="flex flex-col items-center justify-center gap-1 rounded-lg bg-gray-900 p-7 shadow-lg"
>
<h1 class="m-[0] mb-3 text-5xl">Join a game here</h1>
<input
placeholder="Enter game pin"
class="rounded-lg bg-gray-800 p-2 text-center text-white"
bind:value={pin}
/>
<input
placeholder="Enter your name"
bind:value={name}
class="rounded-lg bg-gray-800 p-2 text-center text-white"
/>
{#if Checking.v}
<button
class="mt-2 cursor-pointer rounded-full bg-gray-700 p-2 transition-all hover:scale-110 hover:-rotate-10"
>
Checking if pin is valid...
</button>
{:else}
<button
class="mt-2 cursor-pointer rounded-full bg-green-700 p-2 transition-all hover:scale-110 hover:-rotate-10"
on:click={() => {
if (!pin || !name) {
alert("Please fill in the game pin and your name.");
} else {
joinGame(pin, name);
}
}}
>
Join game
</button>{/if}
</div>
</div>

View file

@ -0,0 +1,19 @@
import { supabase } from "$lib/supabase";
export async function addPlayer(name, gamePin) {
const { data, error } = await supabase
.from("players")
.insert({
gameid: gamePin,
score: 0,
playername: name,
})
.select("id");
if (error) {
alert("Failed to join game: " + error.message + "\n\nPlease try again.");
return;
}
return data[0].id;
}

View file

@ -0,0 +1,3 @@
export let Checking = $state({
v: false,
});

View file

@ -0,0 +1,19 @@
import { addPlayer } from "./InsertPlayerInDB.js";
import { validateGamePin } from "./validateGamePin.js";
import { Checking } from "./JoinGameData.svelte.js";
export async function joinGame(pin, name) {
Checking.v = true;
if (!(await validateGamePin(pin))) {
alert("Invalid game pin. Please try again.");
Checking.v = false;
return;
}
let id = await addPlayer(name, pin);
Checking.v = false;
window.location.href = `./play?gamepin=${pin}&name=${name}&playerid=${id}`;
}

View file

@ -0,0 +1,11 @@
import { supabase } from '$lib/supabase';
export async function validateGamePin(pin) {
const { data, error } = await supabase
.from('games')
.select('gamepin')
.eq('gamepin', Number(pin))
.maybeSingle();
return data !== null && !error;
}

View file

@ -0,0 +1,34 @@
<script>
import AwnserQuetion from "./components/awnseringQuetions/display.svelte";
import LobbyDisplay from "./components/lobby/display.svelte";
import { Status } from "./logic/HostsData.svelte.js";
import { AutoUpdatePlayersList } from "./logic/UpdatePlayersList.js";
import { GetCurrentPlayers } from "./logic/GetCurrentPlayers.js";
import { IntializeGameStart } from "./logic/IntializeGameStart.js";
import { onMount } from "svelte";
import { name,playerid } from "./logic/HostsData.svelte.js";
let gamePin;
onMount(() => {
name.v = new URLSearchParams(new URL(window.location.href).search).get("name");
playerid.v = new URLSearchParams(new URL(window.location.href).search).get("playerid");
gamePin = new URLSearchParams(new URL(window.location.href).search).get("gamepin");
GetCurrentPlayers(gamePin);
AutoUpdatePlayersList(gamePin);
IntializeGameStart(gamePin);
});
</script>
<div class="bg-grey-900 flex h-full items-center justify-center">
<div
class="flex max-w-[700px] flex-col items-center justify-center gap-1 rounded-lg bg-gray-900 p-8 shadow-lg"
>
{#if Status.v == "lobby"}
<LobbyDisplay {gamePin} />
{:else if Status.v == "started"}
<AwnserQuetion />
{/if}
</div>
</div>

View file

@ -0,0 +1,36 @@
<script>
import { questions, Selected } from "../../logic/HostsData.svelte.js";
import { AnswersSymbolAndColorScheme } from "$lib/config.js";
</script>
<div class="mt-5 grid grid-cols-2 gap-5 gap-x-3">
{#each questions.v.answers as answer, index}
<div class="flex">
<input
type="radio"
id="O{index}"
name="question"
class="peer sr-only"
value={index}
bind:group={Selected.v}
/>
<label
for="O{index}"
style="
--border-color: {AnswersSymbolAndColorScheme[index].Color};
--bg-color: {AnswersSymbolAndColorScheme[index].Color};
--border-color-checked: {AnswersSymbolAndColorScheme[index].SelectedColor};
--bg-color-checked: {AnswersSymbolAndColorScheme[index].SelectedColor};
--border-color-hover: {AnswersSymbolAndColorScheme[index].HoverBorderColor};
--border-color-checked: {AnswersSymbolAndColorScheme[index].SelectedBorderColor};
--border-color-hover: {AnswersSymbolAndColorScheme[index].HoverBorderColor};
"
class="w-full cursor-pointer rounded-lg border-[5px] border-[var(--border-color)] bg-[var(--bg-color)] pt-1 pr-2 pb-1 pl-2 text-center text-3xl transition-all peer-checked:border-[var(--border-color-checked)] peer-checked:border-[var(--border-color-checked)] peer-checked:bg-[var(--bg-color-checked)] hover:border-[var(--border-color-hover)]"
>
<i class="nf {AnswersSymbolAndColorScheme[index].Symbol}"></i>
{answer}
</label>
</div>
{/each}
</div>

View file

@ -0,0 +1,13 @@
<script>
import { CurrentQuestion, TotalQuestions } from "../../logic/HostsData.svelte.js";
</script>
<div class="mb-5 flex w-full items-center justify-center gap-3">
<h3>Question {CurrentQuestion.v + 1} of {TotalQuestions.v}</h3>
<div class="flex-1 rounded-full border-2 border-gray-600">
<div
class="h-4 rounded-full bg-green-600 transition-all duration-700"
style="width: {(CurrentQuestion.v / TotalQuestions.v) * 100}%;"
></div>
</div>
</div>

View file

@ -0,0 +1,13 @@
<div class="group relative">
<button
class="mt-4 cursor-not-allowed gap-0 rounded-lg bg-gray-500 p-2 text-3xl text-gray-300 transition-all"
disabled
>
Submit
</button>
<div
class="invisible absolute bottom-full left-1/2 mb-2 w-40 -translate-x-1/2 rounded-md bg-black px-3 py-1 text-center text-sm text-white opacity-0 transition-all group-hover:visible group-hover:opacity-100"
>
Select an option to submit
</div>
</div>

View file

@ -0,0 +1,9 @@
<script>
import { SubmitAnswer } from "../../../logic/SubmitAnswer.js";
</script>
<button
class="mt-4 cursor-pointer gap-0 rounded-lg bg-green-700 p-2 text-3xl transition-all hover:scale-110 hover:-rotate-10"
onclick={SubmitAnswer}
>Submit
</button>

View file

@ -0,0 +1,28 @@
<script>
import Question from "./text/Quetion.svelte";
import Awnsers from "./Awnsers.svelte";
import ProgressBar from "./ProgressBar.svelte";
import SelectFirst from "./buttons/SelectFirst.svelte";
import Wait from "./text/wait.svelte";
import SubmitAwnser from "./buttons/submitAwnser.svelte";
import { CurrentQuestion, Selected } from "../../logic/HostsData.svelte.js";
</script>
<div class="bg-grey-900 flex h-full items-center justify-center">
<div
class="flex max-w-[700px] flex-col items-center justify-center gap-1 rounded-lg bg-gray-900 p-8 shadow-lg"
>
<ProgressBar />
{#if CurrentQuestion.v != null}
<Question />
<Awnsers />
{#if Selected.v != null}
<SubmitAwnser />
{:else}
<SelectFirst />
{/if}
{:else}
<Wait />
{/if}
</div>
</div>

View file

@ -0,0 +1,7 @@
<script>
import { CurrentQuestion, questions } from "../../../logic/HostsData.svelte.js";
</script>
<h1 class="m-[0] text-center text-5xl">
Q{CurrentQuestion.v + 1}. {questions.v.question}
</h1>

View file

@ -0,0 +1 @@
<h1 class="m-[0] text-center text-5xl">Please wait for everyone else to answer the question.</h1>

View file

@ -0,0 +1,6 @@
<script>
let props = $props();
let playerName = props.playerName;
</script>
<span class="m-[0] rounded-xl bg-gray-700 pt-1 pr-2 pb-0 pl-2 font-mono text-3xl">{playerName}</span>

View file

@ -0,0 +1,12 @@
<script>
import { players } from "../../../logic/HostsData.svelte.js";
import PlayerBadge from "./playerBadge.svelte";
</script>
<h1 class="m-[0] mt-5 text-6xl">Players Joined:</h1>
<h1 class="m-[0] text-4xl text-gray-400">(Total Players: {players.v.length})</h1>
<div class="mt-2 flex flex-wrap justify-center gap-2">
{#each players.v as playerName}
<PlayerBadge {playerName} />
{/each}
</div>

View file

@ -0,0 +1,18 @@
<script>
import Players from "./PlayersGUI/players.svelte";
let props = $props();
let gamePin = props.gamePin;
</script>
<h1 class="m-[0] text-9xl">PLAYING</h1>
<h1 class="m-[0] text-7xl text-center">Waiting for host to start the game</h1>
<!--
<h1 class="m-[0] text-7xl">Game Pin:</h1>
<h1 class="m-[0] rounded-2xl bg-gray-700 pt-1.5 pr-2 pb-0 pl-2 font-mono text-5xl">
{gamePin}
</h1>
-->
<Players />

View file

@ -0,0 +1,18 @@
import { supabase } from "$lib/supabase.js";
import { players } from "./HostsData.svelte.js";
export async function GetCurrentPlayers(gamePin) {
const { data, error } = await supabase
.from("players")
.select("playername")
.eq("gameid", Number(gamePin));
console.log("Current players data:", JSON.stringify(data));
if (error) {
console.error("Error fetching players:", error);
return;
}
players.v = data ? data.map(player => player.playername) : [];
}

View file

@ -0,0 +1,9 @@
export let players = $state({ v: {} });
export let Status = $state({ v: "lobby" });
export let questions = $state({ v: {} });
export let CurrentQuestion = $state({ v: null });
export let TotalQuestions = $state({ v: 0 });
export let Selected = $state({ v: null });
export let isWait = $state({ v: true });
export let name = $state({ v: "" });
export let playerid = $state({ v: null });

View file

@ -0,0 +1,22 @@
import { supabase } from "$lib/supabase.js";
import { NewStatus } from "./NewStatus.js";
export async function IntializeGameStart(gamepin) {
supabase
.channel(`game_status_${gamepin}`)
.on(
"postgres_changes",
{
event: "UPDATE",
schema: "public",
table: "games",
filter: `gamepin=eq.${gamepin}`,
},
(payload) => {
if (payload.new.status) {
NewStatus(payload.new.status, gamepin);
}
},
)
.subscribe();
}

View file

@ -0,0 +1,44 @@
import {
CurrentQuestion,
Status,
questions,
isWait,
Selected,
playerid,
TotalQuestions,
} from "./HostsData.svelte.js";
import { supabase } from "$lib/supabase.js";
export async function NewStatus(NewStatus, gamePin) {
if (NewStatus == "completed") {
window.location.replace("/results?gamepin" + gamePin + "&playerID=" + playerid.v);
return;
}
Status.v = "started";
CurrentQuestion.v = Number(NewStatus.replace("question-", ""));
const { data: questionsData } = await supabase
.from("questions")
.select("id,questionstext,correctanswer")
.eq("gameid", Number(gamePin))
.order("id", { ascending: true });
TotalQuestions.v = questionsData.length;
const { data: answers } = await supabase
.from("answers")
.select("content")
.eq("questionid", Number(questionsData[CurrentQuestion.v].id))
.order("id", { ascending: true });
questions.v = {
question: questionsData[CurrentQuestion.v].questionstext,
correctAnswer: questionsData[CurrentQuestion.v].correctanswer,
answers: answers.map((answer) => answer.content),
questionid: questionsData[CurrentQuestion.v].id,
};
isWait.v = false;
Selected.v = null;
}

View file

@ -0,0 +1,31 @@
import { CurrentQuestion, Selected, questions, playerid } from "./HostsData.svelte.js";
import { supabase } from "$lib/supabase.js";
export async function SubmitAnswer() {
CurrentQuestion.v = null;
if (Selected.v == questions.v.correctAnswer) {
await supabase
.from("answeredby")
.insert([
{ questionid: questions.v.questionid, nameofanswerer: playerid.v, correct: true },
])
.select();
let { data: score } = await supabase.from("players").select("score").eq("id", playerid.v);
await supabase
.from("players")
.update({ score: score[0].score + 1 })
.eq("id", playerid.v)
.select();
} else {
await supabase
.from("answeredby")
.insert([
{ questionid: questions.v.questionid, nameofanswerer: playerid.v, correct: false },
])
.select();
}
}

View file

@ -0,0 +1,26 @@
import { supabase } from "$lib/supabase.js";
import { players } from "./HostsData.svelte.js";
export let LobbyConnection;
function onNewPlayer(Newplayers) {
players.v.push(Newplayers.playername);
}
export async function AutoUpdatePlayersList(gamePin) {
LobbyConnection = supabase
.channel("players-realtime")
.on(
"postgres_changes",
{
event: "INSERT",
schema: "public",
table: "players",
filter: `gameid=eq.${gamePin}`,
},
(payload) => {
onNewPlayer(payload.new);
},
)
.subscribe();
}

View file

@ -0,0 +1,23 @@
import { supabase } from "$lib/supabase.js";
import { LobbyConnection } from "./UpdatePlayersList.js";
import { questions, Status, CurrentQuestion, TotalQuestions } from "./HostsData.svelte.js";
export async function startGame(gamePin) {
await supabase.removeChannel(LobbyConnection);
Status.v = "started";
const { data } = await supabase
.from("questions")
.select("*")
.eq("gameid", Number(gamePin))
.order("id", { ascending: true });
TotalQuestions.v = data.length;
CurrentQuestion.v = 0;
await supabase
.from("games")
.update({ status: `question-${CurrentQuestion.v}` })
.eq("gamepin", gamePin);
}

View file

@ -0,0 +1,82 @@
<script>
import { supabase } from "$lib/supabase";
import { onMount } from "svelte";
let gamePin;
let playerID;
let players = [];
onMount(async () => {
playerID = new URLSearchParams(new URL(window.location.href).search).get("playerID");
gamePin = new URLSearchParams(new URL(window.location.href).search).get("gamepin");
let { data: fetchedPlayers } = await supabase
.from("players")
.select("*")
.eq("gameid", gamePin);
players = fetchedPlayers.sort((a, b) => b.score - a.score);
console.log(players);
});
</script>
<div class="bg-grey-900 flex h-full items-center justify-center">
<div
class="flex max-w-[700px] flex-col items-center justify-center gap-1 rounded-lg bg-gray-900 p-8 shadow-lg"
>
<h1 class="mb-3 text-7xl font-bold text-white">Leaderboard</h1>
{#each players as player, i}
{#if player.id == playerID}
<div class="flex w-full items-center justify-between rounded-lg bg-green-950 p-2">
<div
class="mr-2 flex h-6 w-6 items-center justify-center rounded-full bg-gray-500"
>
{i + 1}
</div>
<div class="w-20 text-white">{player.playername}</div>
<div class="flex-1 rounded-full border-2 border-gray-600">
<div
class="flex h-6 items-center justify-center rounded-full bg-green-600 transition-all duration-700"
style="width: {(player.score / players[0].score) * 100}%;"
></div>
</div>
<div class="flex w-17 justify-end">{player.score} points</div>
</div>
{:else}
<div class="flex w-full items-center justify-between rounded-lg bg-gray-800 p-2">
<div
class="mr-2 flex h-6 w-6 items-center justify-center rounded-full bg-gray-500"
>
{i + 1}
</div>
<div class="w-20 text-white">{player.playername}</div>
<div class="flex-1 rounded-full border-2 border-gray-600">
<div
class="flex h-6 items-center justify-center rounded-full bg-green-600 transition-all duration-700"
style="width: {(player.score / players[0].score) * 100}%;"
></div>
</div>
<div class="flex w-17 justify-end">{player.score} points</div>
</div>
{/if}
{/each}
<a href="/"
><button
class="mt-4 cursor-pointer rounded-full bg-green-700 p-2 transition-all hover:scale-110 hover:-rotate-10"
>
Go back to the home page!
</button></a
>
</div>
</div>

View file

@ -0,0 +1,220 @@
<script module>
import Selector from "./selector.svelte";
import TopDisplay from "./TopDisplay.svelte";
import EditNameOfStudents from "./EditNameOfStudents.svelte";
//import {ShowAlert} from "../+page.svelte";
export let RandomNamesState = $state({
NotSelectedYet: [],
Selected: [],
Absent: [],
selectedStudent: "None yet!",
});
export function SelectStudent() {
if (RandomNamesState.NotSelectedYet.length != 0) {
let randomIndex = Math.floor(
Math.random() * RandomNamesState.NotSelectedYet.length
);
RandomNamesState.selectedStudent =
RandomNamesState.NotSelectedYet[randomIndex];
RandomNamesState.Selected = [
RandomNamesState.NotSelectedYet[randomIndex],
...RandomNamesState.Selected,
];
RandomNamesState.NotSelectedYet.splice(randomIndex, 1);
} else {
//ShowAlert("All students have been selected.", "warning");
}
}
let TabOpen = $state({
v: false,
});
export function newNames(Names) {
localStorage.setItem("Names", JSON.stringify(Names));
RandomNamesState.NotSelectedYet = Names;
RandomNamesState.Selected = [];
RandomNamesState.Absent = [];
RandomNamesState.selectedStudent = "None yet!";
}
let TempelateNames = ["John", "Jane", "Alice", "Bob"];
let TempNames = localStorage.getItem("Names") || "";
if (TempNames != "") {
RandomNamesState.NotSelectedYet = JSON.parse(TempNames);
RandomNamesState.Selected = [];
RandomNamesState.Absent = [];
RandomNamesState.selectedStudent = "None yet!";
} else {
newNames($state.snapshot(TempelateNames));
}
</script>
<div id="wrap">
<div id="nav">
<a href="/" aria-label="Back to main menu"
><button aria-label="Back to main menu"
><span class="front"
><svg
xmlns="http://www.w3.org/2000/svg"
height="24px"
viewBox="0 -960 960 960"
width="24px"
fill="#FFFFFF"
><path
d="M240-200h120v-240h240v240h120v-360L480-740 240-560v360Zm-80 80v-480l320-240 320 240v480H520v-240h-80v240H160Zm320-350Z"
/></svg
> GO BACK
</span></button
></a
>
<h1>Random student selector</h1>
<div>
<button
aria-label="Back to main menu"
onclick={() => (TabOpen.v = true)}
><span class="front"
><svg
xmlns="http://www.w3.org/2000/svg"
height="24px"
viewBox="0 -960 960 960"
width="24px"
fill="#FFFFFF"
><path
d="M400-480q-66 0-113-47t-47-113q0-66 47-113t113-47q66 0 113 47t47 113q0 66-47 113t-113 47ZM80-160v-112q0-33 17-62t47-44q51-26 115-44t141-18h14q6 0 12 2-8 18-13.5 37.5T404-360h-4q-71 0-127.5 18T180-306q-9 5-14.5 14t-5.5 20v32h252q6 21 16 41.5t22 38.5H80Zm560 40-12-60q-12-5-22.5-10.5T584-204l-58 18-40-68 46-40q-2-14-2-26t2-26l-46-40 40-68 58 18q11-8 21.5-13.5T628-460l12-60h80l12 60q12 5 22.5 11t21.5 15l58-20 40 70-46 40q2 12 2 25t-2 25l46 40-40 68-58-18q-11 8-21.5 13.5T732-180l-12 60h-80Zm40-120q33 0 56.5-23.5T760-320q0-33-23.5-56.5T680-400q-33 0-56.5 23.5T600-320q0 33 23.5 56.5T680-240ZM400-560q33 0 56.5-23.5T480-640q0-33-23.5-56.5T400-720q-33 0-56.5 23.5T320-640q0 33 23.5 56.5T400-560Zm0-80Zm12 400Z"
/></svg
> Edit list of names
</span></button
>
</div>
</div>
<div id="root">
<Selector />
<div id="listWrap"><TopDisplay /></div>
</div>
</div>
{#if TabOpen.v !== false}
<div id="UperLayer">
<div id="wrapClose">
<EditNameOfStudents />
<button
class="close"
onclick={() => (TabOpen.v = false)}
aria-label="close">CLOSE</button
>
</div>
</div>
{/if}
<style>
#wrap {
height: 100%;
display: flex;
flex-direction: column;
}
#nav {
display: flex;
justify-content: space-between;
align-items: center;
background-color: #121212;
color: white;
}
button {
background: #292929;
border-radius: 12px;
border: none;
margin: 10px;
padding: 2px 0px;
cursor: pointer;
outline-offset: 4px;
max-width: 400px;
}
button:hover .front {
transform: translateY(-7px);
}
button:active .front {
transform: translateY(-2px);
}
.front {
display: flex;
text-align: center;
align-items: center;
padding: 5px;
border-radius: 12px;
background: #4d4d4d;
color: white;
transform: translateY(-4px);
transition: all 0.1s ease-in-out;
font-family: "JetBrains Mono", monospace;
font-size: 25px;
}
#root {
flex: 1;
display: flex;
flex-direction: column;
justify-content: space-between;
}
h1 {
text-align: center;
margin: 5px 0px;
text-decoration: underline #444;
position: fixed;
top: 0;
left: 50%;
transform: translate(-50%, 0);
}
span {
font-size: 0.5em;
text-decoration: none;
color: #444;
}
#listWrap {
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
}
#UperLayer {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
backdrop-filter: blur(5px);
background-color: rgba(0, 0, 0, 0.5);
z-index: 1;
display: flex;
justify-content: center;
align-items: center;
}
#wrapClose {
display: flex;
flex-direction: column;
}
.close {
background-color: #2b2b2b;
color: #888;
border: none;
padding: 10px 20px;
border-radius: 5px;
cursor: pointer;
align-self: center;
}
</style>

View file

@ -0,0 +1,51 @@
<script>
import { newNames, RandomNamesState } from "./+page.svelte";
let names =
RandomNamesState.NotSelectedYet.join("\n") +
"\n" +
RandomNamesState.Selected.join("\n") +
"\n" +
RandomNamesState.Absent.join("\n");
let namesArray = [];
</script>
<div id="root">
<div id="root">
<h1>Edit Names</h1>
<p></p>
<textarea
bind:value={names}
id="box"
onchange={() => {
let namesArray = names.split("\n");
newNames(namesArray);
}}
></textarea>
</div>
</div>
<style>
#root {
display: flex;
flex-direction: column;
gap: 5px;
margin: 10px;
justify-content: center;
background-color: #303030;
padding: 10px;
margin: 20px;
border-radius: 10px;
}
#box {
width: 100%;
height: 500px;
background-color: #121212;
color: white;
border-radius: 10px;
resize: vertical;
font-size: 20px;
}
</style>

View file

@ -0,0 +1,161 @@
<script context="module">
import { RandomNamesState } from "./+page.svelte";
let DragTempName = "na";
</script>
<div id="root">
<div
id="Selected"
on:dragover|preventDefault
role="list"
aria-label="drag name to this list!"
on:drop={(event) => {
event.preventDefault();
if (DragTempName !== "na") {
RandomNamesState.Selected.push(DragTempName);
DragTempName = "na";
}
}}
>
<h1>Selected</h1>
<div class="wrap">
{#each RandomNamesState.Selected as name, i}
<span
class="Selected"
draggable="true"
role="listitem"
aria-label="drag name to different list!"
on:dragstart={() => {
setTimeout(() => {
RandomNamesState.Selected.splice(i, 1);
DragTempName = name;
}, 100);
}}
>
{name}
</span>
{/each}
</div>
</div>
<hr />
<div
id="NotSelectedYet"
on:dragover|preventDefault
role="list"
aria-label="drag name to this list!"
on:drop={(event) => {
event.preventDefault();
if (DragTempName !== "na") {
RandomNamesState.NotSelectedYet.push(DragTempName);
DragTempName = "na";
}
}}
>
<h1>Not Selected Yet</h1>
<div class="wrap">
{#each RandomNamesState.NotSelectedYet as name, i}
<span
class="NotSelectedYet"
draggable="true"
role="listitem"
aria-label="drag name to different list!"
on:dragstart={() => {
setTimeout(() => {
RandomNamesState.NotSelectedYet.splice(i, 1);
DragTempName = name;
}, 100);
}}
>
{name}
</span>
{/each}
</div>
</div>
<hr />
<div
id="Absent"
on:dragover|preventDefault
role="list"
aria-label="drag name to this list!"
on:drop={(event) => {
event.preventDefault();
if (DragTempName !== "na") {
RandomNamesState.Absent.push(DragTempName);
DragTempName = "na";
}
}}
>
<h1>Absent</h1>
<div class="wrap">
{#each RandomNamesState.Absent as name, i}
<span
draggable="true"
class="Absent"
role="listitem"
aria-label="drag name to different list!"
on:dragstart={() => {
setTimeout(() => {
RandomNamesState.Absent.splice(i, 1);
DragTempName = name;
}, 100);
}}
>
{name}
</span>
{/each}
</div>
</div>
</div>
<style>
#root {
display: flex;
overflow-x: hidden;
}
span {
margin: 5px;
padding: 5px;
border-radius: 5px;
background-color: #4d4d4d;
color: white;
font-size: 30px;
}
h1 {
text-align: center;
font-size: 40px;
margin: 0;
}
#NotSelectedYet,
#Selected {
min-width: 40%;
max-width: 40%;
}
#Absent {
min-width: 20%;
max-width: 20%;
}
.wrap {
display: flex;
flex-wrap: wrap;
height: fit-content;
justify-content: center;
}
.NotSelectedYet {
color: #ffe677;
}
.Selected {
color: #a3ffa3;
}
.Absent {
color: #242424;
}
</style>

View file

@ -0,0 +1,135 @@
<script>
import { RandomNamesState, SelectStudent } from "./+page.svelte";
</script>
<div id="root">
<p>The latest selected student is:</p>
<h1>{RandomNamesState.selectedStudent}</h1>
<div id="options">
<button
aria-label="Back to main menu"
id="selectStudent"
onclick={() => {
SelectStudent();
}}
><span class="front">
<span>Select a random student</span>
</span></button
>
<div>
{#if RandomNamesState.Selected.length > 0}
<button
aria-label="Back to main menu"
onclick={() => {
if (
confirm(
"Are you sure you want to reset all the Selected names? This action cannot be undone."
)
) {
RandomNamesState.NotSelectedYet = [
...RandomNamesState.NotSelectedYet,
...RandomNamesState.Selected,
];
RandomNamesState.Selected = [];
RandomNamesState.selectedStudent = "None yet!";
}
}}
><span class="front">
<span>Reset selected</span>
</span></button
>{/if}
{#if RandomNamesState.Absent.length > 0}
<button
aria-label="Back to main menu"
onclick={() => {
if (
confirm(
"Are you sure you want to reset all the names? this will also move all the students to the 'Not Selected Yet' list. This action cannot be undone."
)
) {
RandomNamesState.NotSelectedYet = [
...RandomNamesState.NotSelectedYet,
...RandomNamesState.Absent,
];
RandomNamesState.Absent = [];
RandomNamesState.selectedStudent = "None yet!";
}
}}
><span class="front">
<span>Reset absentees</span>
</span></button
>{/if}
</div>
</div>
</div>
<style>
#root {
background-color: #222;
border-radius: 30px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding-top: 20px;
padding-left: 20px;
padding-right: 20px;
padding-bottom: 0px;
margin: 20px;
}
#options {
display: flex;
justify-content: space-between;
width: 100%;
}
button {
background: #292929;
border-radius: 12px;
border: none;
margin: 10px;
padding: 2px 0px;
cursor: pointer;
outline-offset: 4px;
width: fit-content;
margin-bottom: 20px;
}
button:hover .front {
transform: translateY(-7px);
}
button:active .front {
transform: translateY(-2px);
}
.front {
display: flex;
text-align: center;
align-items: center;
padding: 5px;
border-radius: 12px;
background: #4d4d4d;
color: white;
transform: translateY(-4px);
transition: all 0.1s ease-in-out;
font-family: "JetBrains Mono", monospace;
font-size: 25px;
gap: 5px;
}
h1 {
font-size: 120px;
margin: 10px;
}
p {
color: #aaa;
margin: 0;
}
#selectStudent > .front {
background-color: #0e71e2;
}
</style>

19
src/routes/tailwind.css Normal file
View file

@ -0,0 +1,19 @@
@import "https://www.nerdfonts.com/assets/css/webfont.css";
@import url("https://fonts.googleapis.com/css2?family=Comfortaa:wght@300..700&family=JetBrains+Mono:wght@200&family=Sour+Gummy:wght@300&display=swap");
@import "tailwindcss";
:root {
@apply bg-gray-950 text-white;
}
.btn {
@apply cursor-pointer rounded border-2 border-white bg-gray-600 px-4 py-2 font-bold text-white transition-all hover:scale-105 hover:-rotate-5 hover:bg-gray-500 hover:shadow-lg;
&.green {
@apply bg-green-600 hover:bg-green-500;
}
}
.card {
@apply shadow-[4px_4px_0px_0px_white] hover:-translate-0.5 hover:shadow-[6px_6px_0px_0px_white] active:translate-0.5 active:shadow-[2px_2px_0px_0px_white] cursor-pointer border-2 border-gray-700 bg-gray-800 p-4 rounded transition-all;
}

View file

@ -0,0 +1,132 @@
<script>
import Keyboard from "./game/keyboard.svelte";
import Display from "./game/display.svelte";
import { onMount } from "svelte";
import { handleKey } from "./logic.svelte.js";
import Right from "./InfoAndSetings/main.svelte";
import { OpenTab } from "./InfoAndSetings/main.svelte";
onMount(() => {
window.addEventListener("keydown", handleKey);
});
</script>
<div id="nav">
<div></div>
<div>
<button
aria-label="Back to main menu"
onclick={() => OpenTab("WordLength")}
><span class="front"
><svg
xmlns="http://www.w3.org/2000/svg"
height="24px"
viewBox="0 -960 960 960"
width="24px"
fill="#FFFFFF"
><path
d="M320-80 160-240l160-160 57 56-64 64h334l-63-64 56-56 160 160L640-80l-57-56 64-64H313l63 64-56 56ZM200-480v-400h80v400h-80Zm240 0v-400h80v400h-80Zm240 0v-400h80v400h-80Z"
/></svg
> Change word legnth
</span></button
>
<button aria-label="Back to main menu" onclick={() => OpenTab("Stats")}
><span class="front"
><svg
xmlns="http://www.w3.org/2000/svg"
height="24px"
viewBox="0 -960 960 960"
width="24px"
fill="#FFFFFF"
><path
d="m105-399-65-47 200-320 120 140 160-260 120 180 135-214 65 47-198 314-119-179-152 247-121-141-145 233Zm475 159q42 0 71-29t29-71q0-42-29-71t-71-29q-42 0-71 29t-29 71q0 42 29 71t71 29ZM784-80 676-188q-21 14-45.5 21t-50.5 7q-75 0-127.5-52.5T400-340q0-75 52.5-127.5T580-520q75 0 127.5 52.5T760-340q0 26-7 50.5T732-244l108 108-56 56Z"
/></svg
> Stats
</span></button
>
</div>
</div>
<div id="root">
<div id="left">
<Display />
<Keyboard />
</div>
<Right />
</div>
<style>
#nav {
display: flex;
justify-content: space-between;
align-items: center;
background-color: #121212;
color: white;
}
button {
background: #292929;
border-radius: 12px;
border: none;
margin: 10px;
padding: 2px 0px;
cursor: pointer;
outline-offset: 4px;
max-width: 400px;
}
button:hover .front {
transform: translateY(-7px);
}
button:active .front {
transform: translateY(-2px);
}
.front {
display: flex;
text-align: center;
align-items: center;
padding: 5px;
border-radius: 12px;
background: #4d4d4d;
color: white;
transform: translateY(-4px);
transition: all 0.1s ease-in-out;
font-family: "JetBrains Mono", monospace;
font-size: 25px;
}
#root {
height: 90%;
display: flex;
margin: 0;
font-family: "Sour Gummy", sans-serif;
background-color: #121212;
color: white;
}
#left {
width: 100%;
height: 100%;
border-radius: 20px;
margin: 20px;
margin-top: 5px;
border: 2px solid #444;
font-family: "JetBrains Mono", monospace;
}
h1 {
text-align: center;
margin: 5px 0px;
text-decoration: underline #444;
position: fixed;
top: 0;
left: 50%;
transform: translate(-50%, 0);
}
span {
font-size: 0.5em;
text-decoration: none;
color: #444;
}
</style>

View file

@ -0,0 +1,39 @@
<script>
import { newGame, WordLegnth } from "../logic.svelte.js";
import { TabOpen } from "./main.svelte";
let LetersSelected = WordLegnth.v;
</script>
<div id="root">
<h4>Select The Word legnth (between 3 and 10 letters)</h4>
<input type="range" id="vol" name="vol" min="3" max="10" bind:value={LetersSelected} />
<button
onclick={() => {
WordLegnth.v = LetersSelected;
newGame();
TabOpen.v = "none";
}}
class="btn"
>Start New Game with Word legnth of {LetersSelected} letters
</button>
</div>
<style>
#root {
display: flex;
flex-direction: column;
gap: 5px;
margin: 10px;
justify-content: center;
background-color: #303030;
padding: 10px;
margin: 20px;
border-radius: 10px;
width: 400px;
}
h4 {
text-align: center;
margin: 0px;
}
</style>

View file

@ -0,0 +1,58 @@
<script module>
import { newGame, WordLegnth } from "../logic.svelte.js";
import WordLegnthSetings from "./WordLegnthSetings.svelte";
import Stats from "./stats.svelte";
export let TabOpen = $state({ v: "none" });
export function OpenTab(type) {
TabOpen.v = type;
}
</script>
{#if TabOpen.v !== "none"}
<div id="UperLayer">
<div id="wrap">
{#if TabOpen.v == "WordLength"}
<WordLegnthSetings />
{:else if TabOpen.v == "Stats"}
<Stats />
{/if}
<button
class="close"
onclick={() => (TabOpen.v = "none")}
aria-label="close">CLOSE</button
>
</div>
</div>
{/if}
<style>
#UperLayer {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
backdrop-filter: blur(5px);
background-color: rgba(0, 0, 0, 0.5);
z-index: 1;
display: grid;
place-items: center;
}
#wrap {
display: flex;
flex-direction: column;
}
.close {
background-color: #2b2b2b;
color: #888;
border: none;
padding: 10px 20px;
border-radius: 5px;
cursor: pointer;
align-self: center;
}
</style>

View file

@ -0,0 +1,155 @@
<script>
import { data, WordLegnth } from "../logic.svelte.js";
// svelte-ignore non_reactive_update
let LetersSelected = JSON.stringify(WordLegnth.v);
let dataPoints = data.value[LetersSelected];
import { onMount } from "svelte";
import Chart from "chart.js/auto";
let canvas;
let chart;
onMount(() => {
const ctx = canvas.getContext("2d");
chart = new Chart(ctx, {
type: "line",
data: {
labels: dataPoints.map((_, i) => `Game ${i + 1}`),
datasets: [
{
label: "Number Of Guesses Taken to Win",
data: $state.snapshot(dataPoints),
borderColor: "rgba(75, 192, 192, 1)",
backgroundColor: "rgba(75, 192, 192, 0.2)",
fill: true,
tension: 0.3,
},
],
},
options: {
responsive: true,
scales: {
y: {
beginAtZero: true,
},
},
},
});
});
let Avgguesses = $state(
(
dataPoints.reduce((acc, val) => acc + val, 0) / dataPoints.length
).toFixed(2)
);
let TotalWins = $state(dataPoints.length);
$effect(() => {
const snapshot = JSON.stringify(data.value);
UpdateChart(data.value);
});
function UpdateChart(val) {
dataPoints = data.value[LetersSelected];
if (chart) {
chart.data.labels = dataPoints.map((_, i) => `Game ${i + 1}`);
chart.data.datasets[0].data = $state.snapshot(dataPoints);
chart.update();
}
Avgguesses = (
dataPoints.reduce((acc, val) => acc + val, 0) / dataPoints.length
).toFixed(2);
TotalWins = dataPoints.length;
}
</script>
<div id="root">
<p>
Stats For:
<select
bind:value={LetersSelected}
onchange={() => {
dataPoints = data.value[LetersSelected];
if (chart) {
chart.data.labels = dataPoints.map(
(_, i) => `Point ${i + 1}`
);
chart.data.datasets[0].data = $state.snapshot(dataPoints);
chart.update();
}
Avgguesses = (
dataPoints.reduce((acc, val) => acc + val, 0) /
dataPoints.length
).toFixed(2);
TotalWins = dataPoints.length;
}}
>
<option value="3">3 Letters</option>
<option value="4">4 Letters</option>
<option value="5" selected>5 Letters</option>
<option value="6">6 Letters</option>
<option value="7">7 Letters</option>
<option value="8">8 Letters</option>
<option value="9">9 Letters</option>
<option value="10">10 Letters</option>
</select>
</p>
<div id="toprow">
<div class="toprow">
<h2>Total WINS</h2>
<h1>{TotalWins}</h1>
</div>
<div class="toprow">
<h2>Avg No. of guesses</h2>
<h1>{Avgguesses}</h1>
</div>
</div>
<canvas bind:this={canvas}></canvas>
</div>
<style>
#root {
display: flex;
flex-direction: column;
gap: 5px;
margin: 10px;
justify-content: center;
background-color: #303030;
padding: 10px;
margin: 20px;
border-radius: 10px;
width: 700px;
}
h2,
h1,
p {
text-align: center;
margin: 0px;
}
h2 {
font-size: 20px;
color: #888;
}
#toprow {
display: inline-flex;
gap: 20px;
}
.toprow {
background-color: #3f3f3f;
border-radius: 10px;
}
.toprow:nth-child(1n) {
min-width: 35%;
}
.toprow:nth-child(2n) {
width: 100%;
}
</style>

View file

@ -0,0 +1,73 @@
<script>
import { CurrentWord, words, WordLegnth } from "../logic.svelte.js";
</script>
<div id="DisplayOfWords">
{#each words.v as word}
<div class="word">
{#each word as letter}
<span class={letter[1]}>{letter[0]}</span>
{/each}
</div>
{/each}
<div class="word">
{#each Array(WordLegnth.v) as _, i}
<span>{CurrentWord.v[i] || ""}</span>
{/each}
</div>
</div>
<style>
#DisplayOfWords {
height: calc(100% - 320px);
border: 2px solid #202020;
margin: 20px;
border-radius: 10px;
overflow-y: auto;
}
.word {
display: flex;
gap: 5px;
margin: 10px;
justify-content: center;
}
span {
width: 70px;
height: 70px;
display: flex;
justify-content: center;
align-items: center;
border-radius: 10px;
font-size: 50px;
border: 2px solid #444;
}
.c {
background-color: #2b5f2d;
}
.d {
background-color: #804d00;
}
.w {
background-color: #2b2b2b;
}
/* width */
::-webkit-scrollbar {
width: 10px;
}
/* Track */
::-webkit-scrollbar-track {
border: 1px solid #5c5c5c;
border-radius: 10px;
}
/* Handle */
::-webkit-scrollbar-thumb {
background: #3f3f3f;
border-radius: 10px;
}
</style>

View file

@ -0,0 +1,71 @@
<script>
import { keys, ButtonPressed } from "../logic.svelte.js";
</script>
<div id="root">
<div class="word">
{#each keys.v.slice(0, 10) as key}
<button on:click={() => ButtonPressed(key[0])} class={key[1]}
>{key[0]}</button
>
{/each}
</div>
<div class="word">
{#each keys.v.slice(10, 19) as key}
<button on:click={() => ButtonPressed(key[0])} class={key[1]}
>{key[0]}</button
>
{/each}
</div>
<div class="word">
{#each keys.v.slice(19) as key}
<button on:click={() => ButtonPressed(key[0])} class={key[1]}
>{key[0]}</button
>
{/each}
</div>
</div>
<style>
#root {
height: 255px;
margin: 20px;
border-radius: 10px;
border: 2px solid #202020;
}
.word {
display: flex;
gap: 5px;
margin: 10px;
justify-content: center;
}
button {
background: none;
font-family: "JetBrains Mono", monospace;
color: #888;
width: 80px;
height: 70px;
display: flex;
justify-content: center;
align-items: center;
border-radius: 10px;
font-size: 50px;
border: 2px solid #444;
}
.c {
background-color: #2b5f2d;
}
.d {
background-color: #804d00;
}
.w {
background-color: #2b2b2b;
}
.o {
background-color: #00202c;
}
</style>

View file

@ -0,0 +1,222 @@
import wordExists from "word-exists";
import { generate } from "random-words";
import toast from "svelte-5-french-toast";
export let WordLegnth = $state({ v: 5 });
let CorrectWord = generate({
minLength: WordLegnth.v,
maxLength: WordLegnth.v,
});
console.log("CorrectWord: ", CorrectWord);
export let words = $state({ v: [] });
export let CurrentWord = $state({ v: [] });
export let keys = $state({
v: [
["Q", "n"],
["W", "n"],
["E", "n"],
["R", "n"],
["T", "n"],
["Y", "n"],
["U", "n"],
["I", "n"],
["O", "n"],
["P", "n"],
["A", "n"],
["S", "n"],
["D", "n"],
["F", "n"],
["G", "n"],
["H", "n"],
["J", "n"],
["K", "n"],
["L", "n"],
["⌫", "o"],
["Z", "n"],
["X", "n"],
["C", "n"],
["V", "n"],
["B", "n"],
["N", "n"],
["M", "n"],
["⏎", "o"],
],
});
export function newGame() {
CorrectWord = generate({
minLength: WordLegnth.v,
maxLength: WordLegnth.v,
});
console.log("CorrectWord: ", CorrectWord);
words.v = [];
CurrentWord.v = [];
keys.v = [
["Q", "n"],
["W", "n"],
["E", "n"],
["R", "n"],
["T", "n"],
["Y", "n"],
["U", "n"],
["I", "n"],
["O", "n"],
["P", "n"],
["A", "n"],
["S", "n"],
["D", "n"],
["F", "n"],
["G", "n"],
["H", "n"],
["J", "n"],
["K", "n"],
["L", "n"],
["⌫", "o"],
["Z", "n"],
["X", "n"],
["C", "n"],
["V", "n"],
["B", "n"],
["N", "n"],
["M", "n"],
["⏎", "o"],
];
}
function GameWin() {
ShowAlert("You win!", "success");
toast.success("You win!");
data.value[WordLegnth.v].push(words.v.length);
localStorage.setItem("WordleGamesData", JSON.stringify(data.value));
newGame();
}
function SendWord(word) {
let result = Array(word.length).fill(null);
let used = Array(CorrectWord.length).fill(false);
// First pass: exact matches
for (let i = 0; i < word.length; i++) {
if (word[i].toLowerCase() === CorrectWord[i].toLowerCase()) {
result[i] = [word[i].toUpperCase(), "c"];
used[i] = true;
}
}
// Second pass: wrong place but correct letter
for (let i = 0; i < word.length; i++) {
if (result[i]) continue;
let found = false;
for (let j = 0; j < CorrectWord.length; j++) {
if (!used[j] && word[i].toLowerCase() === CorrectWord[j].toLowerCase()) {
found = true;
used[j] = true;
break;
}
}
result[i] = found ? [word[i].toUpperCase(), "d"] : [word[i].toUpperCase(), "w"];
}
words.v.push(result);
setTimeout(() => {
document.getElementById("DisplayOfWords").scrollTo({
top: document.getElementById("DisplayOfWords").scrollHeight,
behavior: "smooth",
});
}, 100);
// Update keyboard status
for (let [letter, status] of result) {
let keyIndex = keys.v.findIndex((k) => k[0].toLowerCase() === letter.toLowerCase());
if (keyIndex !== -1) {
let current = keys.v[keyIndex][1];
if (status === "c") {
keys.v[keyIndex][1] = "c";
} else if (status === "d" && current === "n") {
keys.v[keyIndex][1] = "d";
} else if (status === "w" && current === "n") {
keys.v[keyIndex][1] = "w";
}
}
}
// Check for win
if (result.every(([_, status]) => status === "c")) {
GameWin();
}
}
export function ButtonPressed(key) {
document.getElementById("DisplayOfWords").scrollTo({
top: document.getElementById("DisplayOfWords").scrollHeight,
behavior: "smooth",
});
if (key === "⏎") {
if (CurrentWord.v.length === WordLegnth.v) {
let word = CurrentWord.v.join("").toUpperCase();
if (wordExists(word)) {
SendWord(CurrentWord.v);
CurrentWord.v = [];
} else {
ShowAlert("Not a valid word", "error");
}
}
return;
} else if (key === "⌫") {
CurrentWord.v.pop();
return;
}
if (CurrentWord.v.length === WordLegnth.v) {
return;
}
CurrentWord.v.push(key);
}
export function handleKey(event) {
const key = event.key.toLowerCase();
if (key === "enter") {
ButtonPressed("⏎");
} else if (key === "backspace") {
ButtonPressed("⌫");
} else if (/^[a-z]$/.test(key)) {
ButtonPressed(key.toUpperCase());
}
}
export let data = $state({
value: {
3: [],
4: [],
5: [],
6: [],
7: [],
8: [],
9: [],
10: [],
},
});
let WordleGamesData = localStorage.getItem("WordleGamesData") || "";
if (WordleGamesData != "") {
data.value = JSON.parse(WordleGamesData);
} else {
data.value = {
3: [],
4: [],
5: [],
6: [],
7: [],
8: [],
9: [],
10: [],
};
}