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

2
.env.example Normal file
View file

@ -0,0 +1,2 @@
VITE_SUPABASE_URL=https://yourproject.supabase.co
VITE_SUPABASE_ANON_KEY=your-service-role-key

42
.gitignore vendored
View file

@ -1,22 +1,26 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.idea
# Output
.output
.vercel
.netlify
.wrangler
/.svelte-kit
/build
# OS
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
Thumbs.db
# Env
.env
.env.*
!.env.example
!.env.test
# Vite
vite.config.js.timestamp-*
vite.config.ts.timestamp-*
package-lock.json

1
.npmrc Normal file
View file

@ -0,0 +1 @@
engine-strict=true

4
.prettierignore Normal file
View file

@ -0,0 +1,4 @@
# Package Managers
package-lock.json
pnpm-lock.yaml
yarn.lock

15
.prettierrc Normal file
View file

@ -0,0 +1,15 @@
{
"useTabs": true,
"singleQuote": false,
"trailingComma": "all",
"printWidth": 100,
"plugins": ["prettier-plugin-svelte", "prettier-plugin-tailwindcss"],
"overrides": [
{
"files": "*.svelte",
"options": {
"parser": "svelte"
}
}
]
}

View file

@ -1,3 +0,0 @@
{
"recommendations": ["svelte.svelte-vscode", "esbenp.prettier-vscode"]
}

10
.vscode/settings.json vendored
View file

@ -1,7 +1,9 @@
{
"editor.defaultFormatter": "esbenp.prettier-vscode",
"[svelte]": {
"editor.defaultFormatter": "svelte.svelte-vscode"
"tailwindCSS.emmetCompletions": true,
"editor.inlineSuggest.enabled": true,
"editor.quickSuggestions": {
"strings": true
},
"cSpell.words": ["Kahoot", "kokoro"]
"editor.defaultFormatter": "esbenp.prettier-vscode",
"css.customData": [".vscode/tailwind.json"]
}

96
.vscode/tailwind.json vendored Normal file
View file

@ -0,0 +1,96 @@
{
"version": 1.2,
"atDirectives": [
{
"name": "@theme",
"description": "Use the `@theme` directive to define your project's custom design tokens, like fonts, colors, and breakpoints.",
"references": [
{
"name": "Tailwind Documentation",
"url": "https://tailwindcss.com/docs/functions-and-directives#theme-directive"
}
]
},
{
"name": "@source",
"description": "Use the `@source` directive to explicitly specify source files that aren't picked up by Tailwind's automatic content detection.",
"references": [
{
"name": "Tailwind Documentation",
"url": "https://tailwindcss.com/docs/functions-and-directives#source-directive"
}
]
},
{
"name": "@utility",
"description": "Use the `@utility` directive to add custom utilities to your project that work with variants like `hover`, `focus` and `lg`.",
"references": [
{
"name": "Tailwind Documentation",
"url": "https://tailwindcss.com/docs/functions-and-directives#utility-directive"
}
]
},
{
"name": "@variant",
"description": "Use the `@variant` directive to apply a Tailwind variant to styles in your CSS.",
"references": [
{
"name": "Tailwind Documentation",
"url": "https://tailwindcss.com/docs/functions-and-directives#variant-directive"
}
]
},
{
"name": "@custom-variant",
"description": "Use the `@custom-variant` directive to add a custom variant in your project.",
"references": [
{
"name": "Tailwind Documentation",
"url": "https://tailwindcss.com/docs/functions-and-directives#custom-variant-directive"
}
]
},
{
"name": "@apply",
"description": "Use the `@apply` directive to inline any existing utility classes into your own custom CSS.",
"references": [
{
"name": "Tailwind Documentation",
"url": "https://tailwindcss.com/docs/functions-and-directives#apply-directive"
}
]
},
{
"name": "@reference",
"description": "If you want to use `@apply` or `@variant` in the `<style>` block of a Vue or Svelte component, or within CSS modules, you will need to import your theme variables, custom utilities, and custom variants to make those values available in that context.\n\nTo do this without duplicating any CSS in your output, use the `@reference` directive to import your main stylesheet for reference without actually including the styles.",
"references": [
{
"name": "Tailwind Documentation",
"url": "https://tailwindcss.com/docs/functions-and-directives#reference-directive"
}
]
},
{
"name": "@config",
"description": "Use the `@config` directive to load a legacy JavaScript-based configuration file.",
"references": [
{
"name": "Tailwind Documentation",
"url": "https://tailwindcss.com/docs/functions-and-directives#config-directive"
}
]
},
{
"name": "@plugin",
"description": "Use the `@plugin` directive to load a legacy JavaScript-based plugin.",
"references": [
{
"name": "Tailwind Documentation",
"url": "https://tailwindcss.com/docs/functions-and-directives#plugin-directive"
}
]
}
]
}

3
eslint.config.js Normal file
View file

@ -0,0 +1,3 @@
import prettier from 'eslint-config-prettier';
import svelte from 'eslint-plugin-svelte';
export default [prettier, ...svelte.configs.prettier];

View file

@ -1,30 +0,0 @@
<!DOCTYPE html>
<html lang="en" style="height: 100%; margin: 0; padding: 0">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@200&family=Sour+Gummy:ital,wght@0,100..900;1,100..900&display=swap"
rel="stylesheet"
/>
<script
src="https://drag-drop-touch-js.github.io/dragdroptouch/dist/drag-drop-touch.esm.min.js"
type="module"
></script>
</head>
<body style="height: 100%; margin: 0; padding: 0">
<div id="app" style="height: 100%"></div>
<script type="module">
import { mount } from "svelte";
import App from "./src/app.svelte";
const app = mount(App, {
target: document.getElementById("app"),
});
export default app;
</script>
</body>
</html>

View file

@ -1,35 +1,14 @@
{
"extends": "./.svelte-kit/tsconfig.json",
"compilerOptions": {
"moduleResolution": "bundler",
"target": "ESNext",
"module": "ESNext",
/**
* svelte-preprocess cannot figure out whether you have
* a value or a type, so tell TypeScript to enforce using
* `import type` instead of `import` for Types.
*/
"verbatimModuleSyntax": true,
"isolatedModules": true,
"resolveJsonModule": true,
/**
* To have warnings / errors of the Svelte compiler at the
* correct position, enable source maps by default.
*/
"sourceMap": true,
"esModuleInterop": true,
"skipLibCheck": true,
/**
* Typecheck JS in `.svelte` and `.js` files by default.
* Disable this if you'd like to use dynamic types.
*/
"allowJs": true,
"checkJs": true,
"types": ["svelte", "estree"],
"exclude": [
"node_modules",
"**/node_modules/*",
".git",
"**/*",
"!src/wordle/**/*"
]
}
"moduleResolution": "bundler"
},
// Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias
// except $lib which is handled by https://svelte.dev/docs/kit/configuration#files
//
// If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes
// from the referenced tsconfig.json - TypeScript does not merge them in
"exclude": ["**/node_modules/**", "**/build/**", "**/.svelte-kit/**", "**/out/**", "!src/**"],
}

2223
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,25 +1,42 @@
{
"name": "package",
"name": "kahootclone",
"private": true,
"version": "0.0.0",
"version": "0.0.1",
"type": "module",
"scripts": {
"dev": "vite",
"dev": "vite dev",
"build": "vite build",
"preview": "vite preview"
"preview": "vite preview",
"prepare": "svelte-kit sync || echo ''",
"format": "prettier --write .",
"lint": "prettier --check . && eslint ."
},
"devDependencies": {
"@sveltejs/vite-plugin-svelte": "^5.0.3",
"@types/estree": "^1.0.6",
"svelte": "^5.20.2",
"vite": "^6.3.4"
"@eslint/compat": "^1.2.5",
"@eslint/js": "^9.18.0",
"@sveltejs/adapter-static": "^3.0.8",
"@sveltejs/kit": "^2.16.0",
"@sveltejs/vite-plugin-svelte": "^5.0.0",
"@tailwindcss/vite": "^4.0.0",
"eslint": "^9.18.0",
"eslint-config-prettier": "^10.0.1",
"eslint-plugin-svelte": "^3.0.0",
"globals": "^16.0.0",
"prettier": "^3.4.2",
"prettier-plugin-svelte": "^3.3.3",
"prettier-plugin-tailwindcss": "^0.6.11",
"svelte": "^5.0.0",
"tailwindcss": "^4.0.0",
"vite": "^6.0.0"
},
"dependencies": {
"chart.js": "^4.4.9",
"drag-drop-touch": "^1.3.1",
"kokoro-js": "^1.2.1",
"random-words": "^2.0.1",
"svelte-spa-router": "^4.0.1",
"word-exists": "^1.0.0"
"dependencies": {
"@supabase/supabase-js": "^2.49.4",
"@sveltejs/adapter-static": "^3.0.8",
"chart.js": "^4.4.9",
"js-confetti": "^0.12.0",
"random-words": "^2.0.1",
"svelte-5-french-toast": "^2.0.4",
"word-exists": "^1.0.0"
}
}

View file

@ -1,19 +0,0 @@
# ClassRoomStuff
![ClassRoomStuff's logo](https://hc-cdn.hel1.your-objectstorage.com/s/v3/4a82e0c815624c7786ca2a5addbcc74487da8940_group_8__2_.svg)
A collection of awesome tools, games, and more — made to be used in any classroom!
A project by [RezHackXYZ](https://rezhack.xyz) for [Neighborhood](https://neighborhood.hackclub.com/)
[Try Now](https://edu.rezhack.xyz/) • [Repo for Kahhot Clone in the project](https://github.com/RezHackXYZ/KahootClone)
---
## The "Stuff" in it
1. **DaKahootClone** — The best ever kahoot clone (code in [different repo](https://github.com/RezHackXYZ/KahootClone))
2. **Timetable** — Clock included!
3. **Name selector** — For any class activity!
4. **Wordle** — Challenge your vocabulary!
5. **Announcer** — To shout from the speakers!

View file

@ -1,316 +0,0 @@
export function confettiAnimation() {
(() => {
"use strict";
// Utility functions grouped into a single object
const Utils = {
// Parse pixel values to numeric values
parsePx: (value) => parseFloat(value.replace(/px/, "")),
// Generate a random number between two values, optionally with a fixed precision
getRandomInRange: (min, max, precision = 0) => {
const multiplier = Math.pow(10, precision);
const randomValue = Math.random() * (max - min) + min;
return Math.floor(randomValue * multiplier) / multiplier;
},
// Pick a random item from an array
getRandomItem: (array) =>
array[Math.floor(Math.random() * array.length)],
// Scaling factor based on screen width
getScaleFactor: () => Math.log(window.innerWidth) / Math.log(1920),
// Debounce function to limit event firing frequency
debounce: (func, delay) => {
let timeout;
return (...args) => {
clearTimeout(timeout);
timeout = setTimeout(() => func(...args), delay);
};
},
};
// Precomputed constants
const DEG_TO_RAD = Math.PI / 180;
// Centralized configuration for default values
const defaultConfettiConfig = {
confettiesNumber: 250,
confettiRadius: 6,
confettiColors: [
"#fcf403",
"#62fc03",
"#f4fc03",
"#03e7fc",
"#03fca5",
"#a503fc",
"#fc03ad",
"#fc03c2",
],
emojies: [],
svgIcon: null, // Example SVG link
};
// Confetti class representing individual confetti pieces
class Confetti {
constructor({
initialPosition,
direction,
radius,
colors,
emojis,
svgIcon,
}) {
const speedFactor =
Utils.getRandomInRange(0.9, 1.7, 3) *
Utils.getScaleFactor();
this.speed = { x: speedFactor, y: speedFactor };
this.finalSpeedX = Utils.getRandomInRange(0.2, 0.6, 3);
this.rotationSpeed =
emojis.length || svgIcon
? 0.01
: Utils.getRandomInRange(0.03, 0.07, 3) *
Utils.getScaleFactor();
this.dragCoefficient = Utils.getRandomInRange(
0.0005,
0.0009,
6
);
this.radius = { x: radius, y: radius };
this.initialRadius = radius;
this.rotationAngle =
direction === "left"
? Utils.getRandomInRange(0, 0.2, 3)
: Utils.getRandomInRange(-0.2, 0, 3);
this.emojiRotationAngle = Utils.getRandomInRange(
0,
2 * Math.PI
);
this.radiusYDirection = "down";
const angle =
direction === "left"
? Utils.getRandomInRange(82, 15) * DEG_TO_RAD
: Utils.getRandomInRange(-15, -82) * DEG_TO_RAD;
this.absCos = Math.abs(Math.cos(angle));
this.absSin = Math.abs(Math.sin(angle));
const offset = Utils.getRandomInRange(-150, 0);
const position = {
x:
initialPosition.x +
(direction === "left" ? -offset : offset) * this.absCos,
y: initialPosition.y - offset * this.absSin,
};
this.position = { ...position };
this.initialPosition = { ...position };
this.color =
emojis.length || svgIcon
? null
: Utils.getRandomItem(colors);
this.emoji = emojis.length ? Utils.getRandomItem(emojis) : null;
this.svgIcon = null;
// Preload SVG if provided
if (svgIcon) {
this.svgImage = new Image();
this.svgImage.src = svgIcon;
this.svgImage.onload = () => {
this.svgIcon = this.svgImage; // Mark as ready once loaded
};
}
this.createdAt = Date.now();
this.direction = direction;
}
draw(context) {
const { x, y } = this.position;
const { x: radiusX, y: radiusY } = this.radius;
const scale = window.devicePixelRatio;
if (this.svgIcon) {
context.save();
context.translate(scale * x, scale * y);
context.rotate(this.emojiRotationAngle);
context.drawImage(
this.svgIcon,
-radiusX,
-radiusY,
radiusX * 2,
radiusY * 2
);
context.restore();
} else if (this.color) {
context.fillStyle = this.color;
context.beginPath();
context.ellipse(
x * scale,
y * scale,
radiusX * scale,
radiusY * scale,
this.rotationAngle,
0,
2 * Math.PI
);
context.fill();
} else if (this.emoji) {
context.font = `${radiusX * scale}px serif`;
context.save();
context.translate(scale * x, scale * y);
context.rotate(this.emojiRotationAngle);
context.textAlign = "center";
context.fillText(this.emoji, 0, radiusY / 2); // Adjust vertical alignment
context.restore();
}
}
updatePosition(deltaTime, currentTime) {
const elapsed = currentTime - this.createdAt;
if (this.speed.x > this.finalSpeedX) {
this.speed.x -= this.dragCoefficient * deltaTime;
}
this.position.x +=
this.speed.x *
(this.direction === "left" ? -this.absCos : this.absCos) *
deltaTime;
this.position.y =
this.initialPosition.y -
this.speed.y * this.absSin * elapsed +
(0.00125 * Math.pow(elapsed, 2)) / 2;
if (!this.emoji && !this.svgIcon) {
this.rotationSpeed -= 1e-5 * deltaTime;
this.rotationSpeed = Math.max(this.rotationSpeed, 0);
if (this.radiusYDirection === "down") {
this.radius.y -= deltaTime * this.rotationSpeed;
if (this.radius.y <= 0) {
this.radius.y = 0;
this.radiusYDirection = "up";
}
} else {
this.radius.y += deltaTime * this.rotationSpeed;
if (this.radius.y >= this.initialRadius) {
this.radius.y = this.initialRadius;
this.radiusYDirection = "down";
}
}
}
}
isVisible(canvasHeight) {
return this.position.y < canvasHeight + 100;
}
}
class ConfettiManager {
constructor() {
this.canvas = document.createElement("canvas");
// @ts-ignore
this.canvas.style =
"position: fixed; top: 0; left: 0; width: 100%; height: 100%; z-index: 1000; pointer-events: none;";
document.body.appendChild(this.canvas);
this.context = this.canvas.getContext("2d");
this.confetti = [];
this.lastUpdated = Date.now();
window.addEventListener(
"resize",
Utils.debounce(() => this.resizeCanvas(), 200)
);
this.resizeCanvas();
requestAnimationFrame(() => this.loop());
}
resizeCanvas() {
this.canvas.width = window.innerWidth * window.devicePixelRatio;
this.canvas.height =
window.innerHeight * window.devicePixelRatio;
}
addConfetti(config = {}) {
const {
confettiesNumber,
confettiRadius,
confettiColors,
emojies,
svgIcon,
} = {
...defaultConfettiConfig,
...config,
};
const baseY = (5 * window.innerHeight) / 7;
for (let i = 0; i < confettiesNumber / 2; i++) {
this.confetti.push(
new Confetti({
initialPosition: { x: 0, y: baseY },
direction: "right",
radius: confettiRadius,
colors: confettiColors,
emojis: emojies,
svgIcon,
})
);
this.confetti.push(
new Confetti({
initialPosition: { x: window.innerWidth, y: baseY },
direction: "left",
radius: confettiRadius,
colors: confettiColors,
emojis: emojies,
svgIcon,
})
);
}
}
resetAndStart(config = {}) {
// Clear existing confetti
this.confetti = [];
// Add new confetti
this.addConfetti(config);
}
loop() {
const currentTime = Date.now();
const deltaTime = currentTime - this.lastUpdated;
this.lastUpdated = currentTime;
this.context.clearRect(
0,
0,
this.canvas.width,
this.canvas.height
);
this.confetti = this.confetti.filter((item) => {
item.updatePosition(deltaTime, currentTime);
item.draw(this.context);
return item.isVisible(this.canvas.height);
});
requestAnimationFrame(() => this.loop());
}
}
const manager = new ConfettiManager();
manager.addConfetti();
const triggerButton = document.getElementById("show-again");
if (triggerButton) {
triggerButton.addEventListener("click", () =>
manager.addConfetti()
);
}
const resetInput = document.getElementById("reset");
if (resetInput) {
resetInput.addEventListener("input", () => manager.resetAndStart());
}
})();
}

View file

@ -1,348 +0,0 @@
<script>
import { newTable } from "./timeTable.svelte";
let table = JSON.parse(localStorage.getItem("TimeTable"));
let TabOpen = false;
let csv = "";
</script>
<div class="root">
<div id="wrap">
<div id="left">
<div class="Header Row">
<span class="DayOfWeek" style="Opacity: 0;"></span>
{#each table.Times as time}
<input
type="text"
onchange={() => {
newTable(table);
}}
bind:value={time}
/>
{/each}
</div>
<div class="Row">
<span class="DayOfWeek">Monday</span>
{#each table.Monday as time}
<input
type="text"
onchange={() => {
newTable(table);
}}
bind:value={time}
/>
{/each}
</div>
<div class="Row">
<span class="DayOfWeek">Tuesday</span>
{#each table.Tuesday as time}
<input
type="text"
onchange={() => {
newTable(table);
}}
bind:value={time}
/>
{/each}
</div>
<div class="Row">
<span class="DayOfWeek">Wednesday</span>
{#each table.Wednesday as time}
<input
type="text"
onchange={() => {
newTable(table);
}}
bind:value={time}
/>
{/each}
</div>
<div class="Row">
<span class="DayOfWeek">Thursday</span>
{#each table.Thursday as time}
<input
type="text"
onchange={() => {
newTable(table);
}}
bind:value={time}
/>
{/each}
</div>
<div class="Row">
<span class="DayOfWeek">Friday</span>
{#each table.Friday as time}
<input
type="text"
onchange={() => {
newTable(table);
}}
bind:value={time}
/>
{/each}
</div>
</div>
<div id="right">
<button
onclick={() => {
table.Times.push("");
table.Monday.push("");
table.Tuesday.push("");
table.Wednesday.push("");
table.Thursday.push("");
table.Friday.push("");
newTable(table);
table = JSON.parse(localStorage.getItem("TimeTable"));
}}
><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
>Add new Period
</button>
<button
onclick={() => {
if (
confirm(
"This will remove the last period, it is not reversible! are you sure?"
)
) {
table.Times.pop();
table.Monday.pop();
table.Tuesday.pop();
table.Wednesday.pop();
table.Thursday.pop();
table.Friday.pop();
newTable(table);
table = JSON.parse(localStorage.getItem("TimeTable"));
}
}}
><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 last Period
</button>
<button
onclick={() => {
TabOpen = true;
}}
><svg
xmlns="http://www.w3.org/2000/svg"
height="24px"
viewBox="0 -960 960 960"
width="24px"
fill="#FFFFFF"
><path
d="M230-360h120v-60H250v-120h100v-60H230q-17 0-28.5 11.5T190-560v160q0 17 11.5 28.5T230-360Zm156 0h120q17 0 28.5-11.5T546-400v-60q0-17-11.5-31.5T506-506h-60v-34h100v-60H426q-17 0-28.5 11.5T386-560v60q0 17 11.5 30.5T426-456h60v36H386v60Zm264 0h60l70-240h-60l-40 138-40-138h-60l70 240ZM160-160q-33 0-56.5-23.5T80-240v-480q0-33 23.5-56.5T160-800h640q33 0 56.5 23.5T880-720v480q0 33-23.5 56.5T800-160H160Zm0-80h640v-480H160v480Zm0 0v-480 480Z"
/></svg
>
Import From CSV
</button>
</div>
</div>
</div>
{#if TabOpen !== false}
<div id="UpperLayer">
<div id="wrapClose">
<div class="root">
<h1>Paste CSV here:</h1>
<p></p>
<textarea
bind:value={csv}
id="box"
placeholder="Duration 1, Duration 2, Duration 3, Duration 4
Subject 01, Subject 02, Subject 03, Subject 04
Subject 08, Subject 09, Subject 10, Subject 12
Subject 13, Subject 13, Subject 14, Subject 15"
></textarea>
</div>
<div id="options">
<button
class="close"
id="cancel"
onclick={() => (TabOpen = false)}
aria-label="close">CANCEL</button
>
<button
class="close"
id="save"
onclick={() => {
TabOpen = false;
let lines = csv.split("\n");
table.Times = lines[0].split(",");
table.Monday = lines[1].split(",");
table.Tuesday = lines[2].split(",");
table.Wednesday = lines[3].split(",");
table.Thursday = lines[4].split(",");
table.Friday = lines[5].split(",");
newTable(table);
table = JSON.parse(localStorage.getItem("TimeTable"));
}}
aria-label="close">SAVE</button
>
</div>
</div>
</div>
{/if}
<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: fit-content;
}
#wrap {
display: flex;
background-color: #252525;
padding: 20px;
border-radius: 20px;
gap: 20px;
}
.Row {
display: flex;
gap: 2px;
}
span {
font-size: 15px;
color: white;
background-color: #3f3f3f;
padding: 5px;
border-radius: 10px;
width: 100px;
text-align: center;
}
.DayOfWeek {
width: 100px;
background-color: #30492e;
}
input {
font-size: 15px;
color: white;
background-color: #3f3f3f;
padding: 5px;
border-radius: 10px;
width: 100px;
text-align: center;
}
.Header > input {
background-color: #30492e;
}
#left {
gap: 5px;
display: flex;
flex-direction: column;
}
#right {
gap: 10px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
#right > button {
background-color: rgb(63, 63, 63);
color: white;
border: 2px solid white;
border-radius: 10px;
cursor: pointer;
transition: all 0.1s ease-in-out;
display: flex;
flex-direction: column;
align-items: center;
max-width: 100px;
}
#right > button:hover {
transform: scale(1.2);
background-color: rgb(50, 50, 50);
}
#UpperLayer {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
backdrop-filter: blur(10px);
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;
}
#box {
width: 700px;
height: 300px;
background-color: #121212;
color: white;
border-radius: 10px;
resize: vertical;
font-size: 20px;
}
h1,
p {
margin: 0;
}
#options {
display: flex;
gap: 10px;
justify-content: center;
}
#cancel {
background-color: #580000;
color: #ffffff;
}
#save {
background-color: #004611;
color: #ffffff;
}
</style>

View file

@ -1,180 +0,0 @@
<script module>
import Time from "./time.svelte";
import TimeTable from "./timeTable.svelte";
import EditTimetableDiv from "./EditTimetable.svelte";
function EditTimetable() {
console.log("Edit timetable");
TabOpen.v = true;
}
let TabOpen = $state({ v: false });
export let ShowSeconds = $state({ v: true });
</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>Idle Screen</h1>
<div>
<button
aria-label="Back to main menu"
onclick={() => {
ShowSeconds.v = !ShowSeconds.v;
localStorage.setItem("ShowSeconds", String(ShowSeconds.v));
}}
><span class="front"
>{#if ShowSeconds.v}Disable Seconds{:else}Enable Seconds{/if}</span
></button
><button
aria-label="Back to main menu"
onclick={() => EditTimetable()}
><span class="front"
><svg
xmlns="http://www.w3.org/2000/svg"
height="24px"
viewBox="0 -960 960 960"
width="24px"
fill="#FFFFFF"
><path
d="M200-80q-33 0-56.5-23.5T120-160v-560q0-33 23.5-56.5T200-800h40v-80h80v80h320v-80h80v80h40q33 0 56.5 23.5T840-720v200h-80v-40H200v400h280v80H200Zm0-560h560v-80H200v80Zm0 0v-80 80ZM560-80v-123l221-220q9-9 20-13t22-4q12 0 23 4.5t20 13.5l37 37q8 9 12.5 20t4.5 22q0 11-4 22.5T903-300L683-80H560Zm300-263-37-37 37 37ZM620-140h38l121-122-18-19-19-18-122 121v38Zm141-141-19-18 37 37-18-19Z"
/></svg
> Edit timetable
</span></button
>
</div>
</div>
<div id="root">
<TimeTable /><Time />
</div>
</div>
{#if TabOpen.v !== false}
<div id="UpperLayer">
<div id="wrapClose">
<EditTimetableDiv />
<button
class="close"
onclick={() => (TabOpen.v = false)}
aria-label="close">CLOSE</button
>
</div>
</div>
{/if}
<style>
#wrap {
display: flex;
flex-direction: column;
height: 100%;
}
#root {
display: flex;
flex-direction: column;
height: 100%;
justify-content: space-evenly;
}
#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;
}
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;
}
#UpperLayer {
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

@ -1,231 +0,0 @@
<script>
import { ShowSeconds } from "./main.svelte";
import { onMount } from "svelte";
let hour1;
let hour2;
let min1;
let min2;
let sec1;
let sec2;
let AmOrPm;
function DecideScrool(Object, currentTime, legnth, LastZeroPos) {
if (currentTime == 0) {
if (Object.scrollTop != 0) {
Object.scrollTop = LastZeroPos * legnth;
setTimeout(() => {
Object.scroll({
top: 0,
behavior: "instant",
});
}, 500);
}
} else {
Object.scrollTop = currentTime * legnth;
}
}
function updateTime() {
const now = new Date();
let hours = now.getHours().toString().padStart(2, "0");
if (now.getHours() >= 12) {
hours = (now.getHours() - 12).toString().padStart(2, "0");
AmOrPm = "PM";
} else {
AmOrPm = "AM";
}
const minutes = now.getMinutes().toString().padStart(2, "0");
const seconds = now.getSeconds().toString().padStart(2, "0");
DecideScrool(hour1, parseInt(hours[0]), 200, 2);
DecideScrool(hour2, parseInt(hours[1]), 200, 10);
DecideScrool(min1, parseInt(minutes[0]), 200, 6);
DecideScrool(min2, parseInt(minutes[1]), 200, 10);
if (ShowSeconds) {
DecideScrool(sec1, parseInt(seconds[0]), 75, 6);
DecideScrool(sec2, parseInt(seconds[1]), 75, 10);
}
}
onMount(() => {
updateTime();
setInterval(updateTime, 1000);
});
let TempLocalStorage = localStorage.getItem("ShowSeconds") || "";
if (TempLocalStorage == "false") {
ShowSeconds.v = false;
}
</script>
<div id="root">
<div id="wrap">
<div id="time">
<div class="rowOfNumbers" bind:this={hour1}>
<h1>0</h1>
<h1>1</h1>
<h1>0</h1>
</div>
<div class="rowOfNumbers" bind:this={hour2}>
<h1>0</h1>
<h1>1</h1>
<h1>2</h1>
<h1>3</h1>
<h1>4</h1>
<h1>5</h1>
<h1>6</h1>
<h1>7</h1>
<h1>8</h1>
<h1>9</h1>
<h1>0</h1>
</div>
<h1 id="HourMinDivider">:</h1>
<div class="rowOfNumbers" bind:this={min1}>
<h1>0</h1>
<h1>1</h1>
<h1>2</h1>
<h1>3</h1>
<h1>4</h1>
<h1>5</h1>
<h1>0</h1>
</div>
<div class="rowOfNumbers" bind:this={min2}>
<h1>0</h1>
<h1>1</h1>
<h1>2</h1>
<h1>3</h1>
<h1>4</h1>
<h1>5</h1>
<h1>6</h1>
<h1>7</h1>
<h1>8</h1>
<h1>9</h1>
<h1>0</h1>
</div>
{#if ShowSeconds.v}
<h1 id="MinSecDivider">.</h1>
<div class="rowOfNumbersSec" bind:this={sec1}>
<h1>0</h1>
<h1>1</h1>
<h1>2</h1>
<h1>3</h1>
<h1>4</h1>
<h1>5</h1>
<h1>0</h1>
</div>
<div class="rowOfNumbersSec" bind:this={sec2}>
<h1>0</h1>
<h1>1</h1>
<h1>2</h1>
<h1>3</h1>
<h1>4</h1>
<h1>5</h1>
<h1>6</h1>
<h1>7</h1>
<h1>8</h1>
<h1>9</h1>
<h1>0</h1>
</div>{/if}
<h1 id="AmOrPm">{AmOrPm}</h1>
</div>
<div id="date">
<h2>
{new Date().toLocaleString("en-US", { weekday: "short" })}
{new Date().getDate()},
{new Date().toLocaleString("en-US", { month: "short" })}
{new Date().getFullYear()}
</h2>
</div>
</div>
</div>
<style>
#root {
display: grid;
place-items: center;
}
#wrap {
background-color: #252525;
padding: 20px 70px;
border-radius: 20px;
}
#time {
display: flex;
justify-content: center;
align-items: baseline;
margin: -30px;
}
#HourMinDivider {
font-size: 200px;
margin: 0;
}
#MinSecDivider {
font-size: 75px;
margin: 0;
color: #585858;
}
.rowOfNumbers {
display: flex;
flex-direction: column;
height: 200px;
overflow-y: hidden;
scroll-behavior: smooth;
}
.rowOfNumbers h1 {
height: 200px;
display: flex;
align-items: center;
justify-content: center;
}
.rowOfNumbers > h1 {
font-size: 200px;
margin: 0;
}
.rowOfNumbersSec {
display: flex;
flex-direction: column;
height: 75px;
overflow-y: hidden;
scroll-behavior: smooth;
}
.rowOfNumbersSec > h1 {
height: 75px;
display: flex;
align-items: center;
justify-content: center;
}
.rowOfNumbersSec > h1 {
font-size: 75px;
margin: 0;
color: #585858;
}
#AmOrPm {
font-size: 75px;
margin: 0;
margin-left: 30px;
color: #585858;
}
h2 {
margin: 0;
font-size: 40px;
color: #888;
text-align: center;
}
</style>

View file

@ -1,198 +0,0 @@
<script module>
export function newTable(timetable) {
localStorage.setItem("TimeTable", JSON.stringify(timetable));
table = timetable;
}
let TemplateTable = {
Times: [
"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",
],
Monday: [
"English",
"Sanskrit",
"Math",
"Lunch",
"Hindi",
"Social Science",
"Science",
],
Tuesday: [
"English",
"Art & Craft",
"Math",
"Lunch",
"Hindi",
"Social Science",
"Science",
],
Wednesday: [
"English",
"GK",
"Math",
"Lunch",
"Hindi",
"Social Science",
"Science",
],
Thursday: [
"English",
"Sanskrit",
"Math",
"Lunch",
"Hindi",
"Social Science",
"Science",
],
Friday: [
"English",
"Computers",
"Math",
"Lunch",
"Hindi",
"Social Science",
"Science",
],
};
let table = $state();
let TempTimeTable = localStorage.getItem("TimeTable") || "";
if (TempTimeTable != "") {
table = JSON.parse(TempTimeTable);
} else {
newTable($state.snapshot(TemplateTable));
}
</script>
<div id="root">
<div id="wrap">
<div class="Header Row">
<span class="DayOfWeek" style="Opacity: 0;"></span>
{#each table.Times as time}
<span>{time}</span>
{/each}
</div>
<div class="Row">
{#if new Date().getDay() == 1}
<span class="DayOfWeek NavHighlight">Monday</span>
{:else}
<span class="DayOfWeek">Monday</span>
{/if}
{#each table.Monday as time}
{#if new Date().getDay() == 1}
<span class="highlight">{time}</span>
{:else}
<span>{time}</span>
{/if}
{/each}
</div>
<div class="Row">
{#if new Date().getDay() == 2}
<span class="DayOfWeek NavHighlight">Tuesday</span>
{:else}
<span class="DayOfWeek">Tuesday</span>
{/if}
{#each table.Tuesday as time}
{#if new Date().getDay() == 2}
<span class="highlight">{time}</span>
{:else}
<span>{time}</span>
{/if}
{/each}
</div>
<div class="Row">
{#if new Date().getDay() == 3}
<span class="DayOfWeek NavHighlight">Wednesday</span>
{:else}
<span class="DayOfWeek">Wednesday</span>
{/if}
{#each table.Wednesday as time}
{#if new Date().getDay() == 3}
<span class="highlight">{time}</span>
{:else}
<span>{time}</span>
{/if}
{/each}
</div>
<div class="Row">
{#if new Date().getDay() == 4}
<span class="DayOfWeek NavHighlight">Thursday</span>
{:else}
<span class="DayOfWeek">Thursday</span>
{/if}
{#each table.Thursday as time}
{#if new Date().getDay() == 4}
<span class="highlight">{time}</span>
{:else}
<span>{time}</span>
{/if}
{/each}
</div>
<div class="Row">
{#if new Date().getDay() == 5}
<span class="DayOfWeek NavHighlight">Friday</span>
{:else}
<span class="DayOfWeek">Friday</span>
{/if}
{#each table.Friday as time}
{#if new Date().getDay() == 5}
<span class="highlight">{time}</span>
{:else}
<span>{time}</span>
{/if}
{/each}
</div>
</div>
</div>
<style>
#root {
display: grid;
place-items: center;
}
#wrap {
display: flex;
background-color: #252525;
padding: 20px;
border-radius: 20px;
flex-direction: column;
gap: 10px;
}
.Row {
display: flex;
gap: 5px;
}
span {
font-size: 20px;
color: white;
background-color: #3f3f3f;
padding: 5px;
border-radius: 10px;
width: 140px;
text-align: center;
}
.Header > span {
background-color: #30492e;
}
.DayOfWeek {
width: 120px;
background-color: #30492e;
}
.highlight {
background-color: #707070;
display: block;
}
.NavHighlight {
background-color: #3b8235;
display: block;
}
</style>

View file

@ -1,249 +0,0 @@
<div id="root">
<img
src="https://hc-cdn.hel1.your-objectstorage.com/s/v3/77e7a04f27807b4e0c16bcda09ea222f9e091616_group_18.svg" id="logo"
/>
<h2>
A collection of awesome tools, games, and more — made to be used in any
classroom!
</h2>
<h3>MAIN</h3>
<div id="items2">
<a href="https://kahoot-clone-rezhackxyz.vercel.app/">
<button class="button2">
<span class="front2">
<svg
xmlns="http://www.w3.org/2000/svg"
height="70px"
viewBox="0 -960 960 960"
width="70px"
fill="#e3e3e3"
><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
>
DaKahootClone
<p>The best ever kahoot clone.</p></span
>
</button></a
>
</div>
<h3>OTHERS</h3>
<div id="items">
<a href="#/IdleScreen">
<button>
<span class="front">
<svg
xmlns="http://www.w3.org/2000/svg"
height="30px"
viewBox="0 -960 960 960"
width="30px"
fill="#FFFFFF"
><path
d="m612-292 56-56-148-148v-184h-80v216l172 172ZM480-80q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-400Zm0 320q133 0 226.5-93.5T800-480q0-133-93.5-226.5T480-800q-133 0-226.5 93.5T160-480q0 133 93.5 226.5T480-160Z"
/></svg
>
Timetable
<p>Clock included!</p></span
>
</button></a
>
<a href="#/RandomName">
<button>
<span class="front">
<svg
xmlns="http://www.w3.org/2000/svg"
height="30px"
viewBox="0 -960 960 960"
width="30px"
fill="#FFFFFF"
><path
d="M0-240v-63q0-43 44-70t116-27q13 0 25 .5t23 2.5q-14 21-21 44t-7 48v65H0Zm240 0v-65q0-32 17.5-58.5T307-410q32-20 76.5-30t96.5-10q53 0 97.5 10t76.5 30q32 20 49 46.5t17 58.5v65H240Zm540 0v-65q0-26-6.5-49T754-397q11-2 22.5-2.5t23.5-.5q72 0 116 26.5t44 70.5v63H780Zm-455-80h311q-10-20-55.5-35T480-370q-55 0-100.5 15T325-320ZM160-440q-33 0-56.5-23.5T80-520q0-34 23.5-57t56.5-23q34 0 57 23t23 57q0 33-23 56.5T160-440Zm640 0q-33 0-56.5-23.5T720-520q0-34 23.5-57t56.5-23q34 0 57 23t23 57q0 33-23 56.5T800-440Zm-320-40q-50 0-85-35t-35-85q0-51 35-85.5t85-34.5q51 0 85.5 34.5T600-600q0 50-34.5 85T480-480Zm0-80q17 0 28.5-11.5T520-600q0-17-11.5-28.5T480-640q-17 0-28.5 11.5T440-600q0 17 11.5 28.5T480-560Zm1 240Zm-1-280Z"
/></svg
>
Name selector
<p>for any class activity!</p></span
>
</button></a
>
<a href="#/Wordle">
<button>
<span class="front">
<svg
xmlns="http://www.w3.org/2000/svg"
height="30px"
viewBox="0 -960 960 960"
width="30px"
fill="#FFFFFF"
><path
d="M182-200q-51 0-79-35.5T82-322l42-300q9-60 53.5-99T282-760h396q60 0 104.5 39t53.5 99l42 300q7 51-21 86.5T778-200q-21 0-39-7.5T706-230l-90-90H344l-90 90q-15 15-33 22.5t-39 7.5Zm16-86 114-114h336l114 114q2 2 16 6 11 0 17.5-6.5T800-304l-44-308q-4-29-26-48.5T678-680H282q-30 0-52 19.5T204-612l-44 308q-2 11 4.5 17.5T182-280q2 0 16-6Zm482-154q17 0 28.5-11.5T720-480q0-17-11.5-28.5T680-520q-17 0-28.5 11.5T640-480q0 17 11.5 28.5T680-440Zm-80-120q17 0 28.5-11.5T640-600q0-17-11.5-28.5T600-640q-17 0-28.5 11.5T560-600q0 17 11.5 28.5T600-560ZM310-440h60v-70h70v-60h-70v-70h-60v70h-70v60h70v70Zm170-40Z"
/></svg
>
Wordle
<p>Challenge your vocabulary!</p></span
>
</button></a
>
<a href="#/announcer">
<button>
<span class="front">
<svg
xmlns="http://www.w3.org/2000/svg"
height="30px"
viewBox="0 -960 960 960"
width="30px"
fill="#FFFFFF"
><path
d="M720-440v-80h160v80H720Zm48 280-128-96 48-64 128 96-48 64Zm-80-480-48-64 128-96 48 64-128 96ZM200-200v-160h-40q-33 0-56.5-23.5T80-440v-80q0-33 23.5-56.5T160-600h160l200-120v480L320-360h-40v160h-80Zm240-182v-196l-98 58H160v80h182l98 58Zm120 36v-268q27 24 43.5 58.5T620-480q0 41-16.5 75.5T560-346ZM300-480Z"
/></svg
>
Announcer
<p>to shout from the speakers!</p></span
>
</button></a
>
</div>
</div>
<div id="BottomBadge">
Made by <a href="https://rezhack.xyz" target="_blank">RezHackXYZ</a> for
<a href="https://neighborhood.hackclub.com/" target="_blank">Neighborhood</a
>!
<a href="https://github.com/RezHackXYZ/ClassRoomStuff" target="_blank"
>Contribute here</a
> if you want.
</div>
<style>
#root {
height: 100%;
width: 100%;
display: flex;
flex-direction: column;
justify-content: center;
}
h1 {
text-align: center;
margin: 0;
font-size: 60px;
}
h2 {
text-align: center;
margin: 0;
color: #797979;
font-size: 30px;
margin-bottom: 40px;
}
#items2 {
display: flex;
justify-content: center;
align-items: center;
gap: 20px;
flex-wrap: wrap;
margin-top: 10px;
margin-bottom: 20px;
}
#items {
margin-top: 10px;
display: flex;
justify-content: center;
align-items: center;
gap: 20px;
flex-wrap: wrap;
}
button {
background: #292929;
border-radius: 12px;
border: none;
padding: 0;
cursor: pointer;
outline-offset: 4px;
}
.front {
display: flex;
flex-direction: column;
text-align: center;
align-items: center;
padding: 5px 15px;
border-radius: 12px;
font-size: 1.25rem;
background: #4d4d4d;
color: white;
transform: translateY(-7px);
transition: all 0.1s ease-in-out;
font-family: "JetBrains Mono", monospace;
font-size: 30px;
}
.front2 {
display: flex;
flex-direction: column;
text-align: center;
align-items: center;
padding: 3px 30px;
border-radius: 12px;
background: #4d4d4d;
color: white;
transform: translateY(-7px);
transition: all 0.1s ease-in-out;
font-family: "JetBrains Mono", monospace;
font-size: 50px;
}
p {
font-size: 12px;
color: #aaaaaa;
margin: 0;
text-align: center;
}
.front2 > p {
font-size: 20px;
}
button:hover .front {
transform: translateY(-10px);
}
button:active .front {
transform: translateY(-3px);
}
#BottomBadge {
position: fixed;
bottom: 0;
left: 50%;
transform: translate(-50%, 0);
background-color: #242424;
color: #aaaaaa;
margin-bottom: 10px;
width: fit-content;
align-self: center;
padding: 5px;
font-size: 20px;
border-radius: 10px;
}
a {
color: #aaaaaa;
}
h3 {
text-align: center;
margin: 0;
color: #aaaaaa;
font-size: 30px;
}
#logo {
width: 500px;
margin-bottom: 20px;
align-self: center;
}
</style>

View file

@ -1,182 +0,0 @@
<script>
let text = $state("");
function speak() {
window.speechSynthesis.speak(
Object.assign(new SpeechSynthesisUtterance(text), {
rate: 0.5,
})
);
}
let TempelateCommonAnounce = $state(["Please be quiet"]);
let CommonAnounce = $state([]);
export function newNames(Names) {
localStorage.setItem("CommonAnounce", JSON.stringify(Names));
}
let TempCommonAnounce = localStorage.getItem("CommonAnounce") || "";
if (TempCommonAnounce != "") {
CommonAnounce = JSON.parse(TempCommonAnounce);
} else {
CommonAnounce = $state.snapshot(TempelateCommonAnounce);
localStorage.setItem("CommonAnounce", JSON.stringify(CommonAnounce));
}
</script>
<div id="root">
<div id="wrap">
<h1>Most Announced announcements</h1>
<div id="mostanouncedanounements">
{#each CommonAnounce as anouncement, i}
<div>
<button
class="anuncement"
onclick={() => {
text = anouncement;
speak();
}}
>
{anouncement}
</button>
<button
aria-label="Delete anouncement"
onclick={() => {
if (
confirm(
"Are you sure you want to delete this anouncement?"
)
) {
CommonAnounce.splice(i, 1);
localStorage.setItem(
"CommonAnounce",
JSON.stringify(CommonAnounce)
);
}
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
height="15px"
viewBox="0 -960 960 960"
width="15px"
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
></button
>
</div>
{/each}
</div>
<hr />
<h1>Or announce something else</h1>
<div>
<input
bind:value={text}
placeholder="Type in here what you want to announce"
/>
<button onclick={speak}>Play</button>
</div>
{#if text}
<button
id="new"
onclick={() => {
CommonAnounce.push(text);
text = "";
localStorage.setItem(
"CommonAnounce",
JSON.stringify(CommonAnounce)
);
}}>Add "{text}" to "Most Announced announcements"</button
>
{/if}
</div>
</div>
<style>
#root {
height: 100%;
display: grid;
place-items: center;
}
#wrap {
display: flex;
flex-direction: column;
gap: 5px;
padding: 20px;
border-radius: 20px;
width: fit-content;
}
input {
font-size: 20px;
padding: 10px;
border-radius: 12px;
border: none;
background-color: #292929;
color: white;
width: 400px;
}
button {
font-size: 20px;
padding: 10px;
border-radius: 12px;
border: none;
background-color: #292929;
color: white;
cursor: pointer;
transition: all 0.1s ease-in-out;
}
button:hover {
background-color: #414141;
transform: rotateZ(-15deg) scale(1.2);
}
hr {
width: 100%;
height: 2px;
background-color: #292929;
border: none;
margin: 0;
}
h1 {
text-align: center;
margin: 0;
font-size: 30px;
}
#mostanouncedanounements {
display: flex;
flex-direction: column;
justify-content: center;
gap: 10px;
}
.anuncement:hover {
transform: scale(1.1);
}
.anuncement {
width: calc(100% - 40px);
}
#new {
width: fit-content;
align-self: center;
font-size: 15px;
}
#new:hover {
transform: scale(1.1);
}
</style>

View file

@ -1,100 +0,0 @@
<script>
import ActualAnnouncer from "./ActualAnnouncer.svelte";
</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>Announcer</h1>
</div>
<div id="root">
<ActualAnnouncer />
</div>
</div>
<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;
}
</style>

15
src/app.html Normal file
View file

@ -0,0 +1,15 @@
<!doctype html>
<html lang="en" style="height: 100%; margin: 0">
<head>
<meta charset="utf-8" />
<link
rel="icon"
href="https://hc-cdn.hel1.your-objectstorage.com/s/v3/4a82e0c815624c7786ca2a5addbcc74487da8940_group_8__2_.svg"
/>
<meta name="viewport" content="width=device-width, initial-scale=1" />
%sveltekit.head%
</head>
<body data-sveltekit-preload-data="hover" style="height: 100%; margin: 0">
<div style="display: contents; height: 100%">%sveltekit.body%</div>
</body>
</html>

View file

@ -1,79 +0,0 @@
<script module>
import Router from "svelte-spa-router";
import Wordle from "./wordle/game.svelte";
import TypeSelector from "./SelectionMenue/TypeSelector.svelte";
import IdleScreen from "./IdleScreen/main.svelte";
import RandomName from "./RandomName/main.svelte";
import Announcer from "./announcer/main.svelte";
import { confettiAnimation } from "./Confetti.js";
let routes = {
"/": TypeSelector,
"/Wordle": Wordle,
"/IdleScreen": IdleScreen,
"/RandomName": RandomName,
"/announcer": Announcer,
};
let ShowAlertDiv = $state(false);
let ShowAlertText = $state("this Code is bad");
let ShowAlertType = $state("Error");
export function ShowAlert(text, type) {
ShowAlertDiv = true;
ShowAlertType = type;
ShowAlertText = text;
setTimeout(() => {
ShowAlertDiv = false;
}, 1500);
if (type == "success") {
confettiAnimation();
}
}
</script>
<div id="root">
<Router {routes} />
</div>
{#if ShowAlertDiv == true}
<div id="alert">
<h1 class={ShowAlertType}>{ShowAlertText}</h1>
</div>
{/if}
<style>
:root {
background-color: #121212;
color: white;
}
#root {
height: 100%;
margin: 0;
font-family: "Sour Gummy", sans-serif;
}
#alert {
position: fixed;
top: 10px;
color: white;
left: 50%;
transform: translate(-50%, 0);
h1 {
margin: 0;
padding: 10px 20px;
border-radius: 20px;
font-family: "Sour Gummy", sans-serif;
}
.error {
background-color: #830000;
}
.warning {
background-color: #975b00;
}
.success {
background-color: #006b00;
}
}
</style>

133
src/lib/config.js Normal file
View file

@ -0,0 +1,133 @@
export let AnswersSymbolAndColorScheme = [
{
color: "#6E0000",
selectedColor: "#AA2222",
hoverBorderColor: "#FF5D5D",
selectedBorderColor: "#FF0000",
symbol: "nf-md-triangle",
},
{
color: "#00316E",
selectedColor: "#2255AA",
hoverBorderColor: "#5D9CFF",
selectedBorderColor: "#0000FF",
symbol: "nf-fa-square",
},
{
color: "#6E6E00",
selectedColor: "#AAAA22",
hoverBorderColor: "#FFFF5D",
selectedBorderColor: "#DDFF00",
symbol: "nf-fa-circle",
},
{
color: "#006E00",
selectedColor: "#22AA22",
hoverBorderColor: "#5DFF5D",
selectedBorderColor: "#00FF00",
symbol: "nf-fa-diamond",
},
{
color: "#4B0082",
selectedColor: "#7F33B5",
hoverBorderColor: "#B066FF",
selectedBorderColor: "#9932CC",
symbol: "nf-md-star",
},
{
color: "#FF8C00",
selectedColor: "#FFB347",
hoverBorderColor: "#FFD580",
selectedBorderColor: "#FFA500",
symbol: "nf-md-hexagon",
},
{
color: "#008B8B",
selectedColor: "#33CCCC",
hoverBorderColor: "#66FFFF",
selectedBorderColor: "#00CED1",
symbol: "nf-md-octagon",
},
{
color: "#8B4513",
selectedColor: "#CD853F",
hoverBorderColor: "#DEB887",
selectedBorderColor: "#A0522D",
symbol: "nf-md-heart",
},
];
export let DefaultQuestions = [
{
name: "What should you do when you're free?",
answers: ["Do something in real life!", "Play video games", "Code!", "Touch grass!"],
correctAnswer: 2,
},
{
name: "Is RezHackXYZ the best programmer in the world?",
answers: ["Yes :)", "No :("],
correctAnswer: 0,
},
{
name: "Best place in the world?",
answers: [
"Google",
"Microsoft",
"Apple",
"Samsung",
"Hack Club!! :D",
"Amazon",
"Facebook",
"Twitter",
],
correctAnswer: 4,
},
];
export let AiPrompts = {
GenerateQuestionsUsingAI: `
You are the AI of a quiz game.
Generate a list of quiz questions with possible answers and the correct answer index.
Each question must have:
- A "name" (question text)
- An "answers" array (minimum 2, maximum 8 options)
- A "correctAnswer" (index starting from 0)
Ensure the questions are diverse.
Example format:
{
"name": "What is the capital of France?",
"answers": [
"Paris",
"London",
"Berlin",
"Madrid"
],
"correctAnswer": 0
}
JUST PROVIDE THE JSON AND NOTHING ELSE.
The user's topic of interest is:
[topic]`,
GenerateOptionsUsingAI: `
You are the AI of a quiz game.
Generate a list of answers relevant to the Question the correct answer index.
generate 2 things for the question:
- An "answers" array (minimum 2, maximum 8 options)
- A "correctAnswer" (index starting from 0)
Ensure the questions are diverse.
Example format if the question is "What is the capital of France?":
{
"answers": [
"Paris",
"London",
"Berlin",
"Madrid"
],
"correctAnswer": 0
}
JUST PROVIDE THE JSON AND NOTHING ELSE.
The user's Question that they want to generate options for is:
[question]
`
};

2
src/lib/showAlert.js Normal file
View file

@ -0,0 +1,2 @@
import JSConfetti from "js-confetti";
const jsConfetti = new JSConfetti();

6
src/lib/supabase.js Normal file
View file

@ -0,0 +1,6 @@
import { createClient } from '@supabase/supabase-js';
export const supabase = createClient(
import.meta.env.VITE_SUPABASE_URL,
import.meta.env.VITE_SUPABASE_ANON_KEY
);

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>

Some files were not shown because too many files have changed in this diff Show more