Compare commits

...

10 commits

6 changed files with 163 additions and 45 deletions

View file

@ -3,25 +3,38 @@
import Card from "./card.svelte";
import StatsAndButtons from "./StatsAndButtons.svelte";
import { stats, resetDeck,SetNewDeck } from "./logic.svelte.js";
import { stats, SelectNewDeck, SetNewDeck, StorageDeck } from "./logic.svelte.js";
import { onMount } from "svelte";
import tempelateDeck from "./tempelateDeck.json";
onMount(() => {
SetNewDeck(JSON.parse(localStorage.getItem("deck")) || tempelateDeck);
SetNewDeck(JSON.parse(localStorage.getItem("deck")) || tempelateDeck, true);
});
</script>
<div class="flex h-full flex-col overflow-hidden">
{#if stats.isDeckEmpty}
<div class="flex flex-1 flex-col items-center justify-center gap-3">
<h1 class="text-4xl">Hey, You learned everything!</h1>
<button onclick={() => resetDeck()} class="big btn green">Reset Deck</button>
<h1 class="text-4xl">Choose a Deck To learn from!</h1>
<div>
<select
id="ChooseTheDeck"
onchange={(event) => {
SelectNewDeck(event.target.value);
}}
class="input"
>
<option value="" disabled selected> goo ahead choose one! </option>
{#each StorageDeck.v as deckOption, i}
<option value={i}>{deckOption.deckName}</option>
{/each}
</select>
</div>
</div>
{:else}
<div class="flex flex-1 flex-col items-center justify-center gap-2">
<div class="flex flex-1 flex-col items-center justify-center">
<div class="flex flex-col gap-1 text-center">
<div class="flex items-center justify-center p-2">
<div class="flex flex-col items-center justify-center gap-5 p-2">
<StatsAndButtons />
</div>
<Card />

View file

@ -1,24 +1,72 @@
<script>
import { SetNewDeck, deck, stats } from "./logic.svelte";
import { SetNewDeck, StorageDeck } from "./logic.svelte";
import toast from "svelte-5-french-toast";
let LocalDeck = $state($state.snapshot(deck));
function saveDeck() {
localStorage.setItem("deck", JSON.stringify(LocalDeck));
SetNewDeck(LocalDeck);
localStorage.setItem("deck", JSON.stringify(deck));
SetNewDeck(deck);
toast.success("Deck saved successfully! You can now close this window.");
}
let CurrentlyEditingDeckId = $state(0);
let deck = $state($state.snapshot(StorageDeck).v);
let DeckOptions;
</script>
<div class="flex flex-col items-center gap-7 rounded border-2 bg-zinc-800 p-3">
<div class="flex w-full flex-col gap-2">
<div class="mb-2">
<label for="SelectOneDeckToEdit" class="text-lg leading-0.5 text-gray-500">
What deck do you want to edit?
</label>
<select
bind:this={DeckOptions}
onchange={() => {
if (DeckOptions.value == "new") {
deck.push({
deckName: "Some New Deck",
cards: [{ Q: "Some Quetion", a: "Some Awnser" }],
});
CurrentlyEditingDeckId = deck.length - 1;
setTimeout(() => {
DeckOptions.value = (deck.length - 1).toString();
});
} else {
CurrentlyEditingDeckId = DeckOptions.value;
}
}}
class="input"
id="SelectOneDeckToEdit"
>
{#each deck as d, i}
<option value={i}>{d.deckName}</option>
{/each}
<option value="new">Create a new deck</option>
</select>
</div>
<div class="mb-2">
<label for="NameOfthisDeck" class="text-lg leading-0.5 text-gray-500">
Name Of this Deck?
</label>
<input
type="text"
class="input mt-1"
bind:value={deck[CurrentlyEditingDeckId].deckName}
id="NameOfthisDeck"
/>
</div>
<div class="flex w-full gap-2 text-3xl">
<span class="flex-1">Question</span>
<span class="flex-1">Answer</span>
<button aria-label="delete" class="btn invisible"><i class="nf nf-md-delete"></i></button>
<button aria-label="delete" class="btn invisible h-0"><i class="nf nf-md-delete"></i></button>
</div>
{#each LocalDeck as card, i}
{#each deck[CurrentlyEditingDeckId].cards as card, i}
<div class="flex w-full gap-2">
<input type="text" class="input flex-1" bind:value={card.Q} />
<input type="text" class="input flex-1" bind:value={card.a} />
@ -26,20 +74,36 @@
aria-label="delete"
onclick={() => {
if (confirm("Are you sure you want to delete this card?")) {
LocalDeck.splice(i, 1);
toast.success("Card deleted successfully! To make it apply, please save the deck.");
deck[CurrentlyEditingDeckId].cards.splice(i, 1);
}
}}
class="btn dull"><i class="nf nf-md-delete"></i></button
class="btn dull"
>
<i class="nf nf-md-delete"></i>
</button>
</div>
{/each}
</div>
<div class="flex gap-3">
<button
onclick={() => {
LocalDeck.push({ Q: "", a: "" });
toast.success("New card added! To make it apply, please save the deck.");
if (deck.length == 2) {
toast.error("You need to have at least 1 deck");
} else if (confirm("Are you sure you want to delete this deck?")) {
deck.splice(CurrentlyEditingDeckId, 1);
CurrentlyEditingDeckId = Math.max(0, CurrentlyEditingDeckId - 1);
DeckOptions.value = CurrentlyEditingDeckId.toString();
toast.success("Deck deleted successfully");
}
}}
class="btn"
>
<i class="nf nf-md-layers_remove"></i>
Delete this Deck
</button>
<button
onclick={() => {
deck[CurrentlyEditingDeckId].cards.push({ Q: "", a: "" });
}}
class="btn"
>
@ -47,8 +111,8 @@
Add New Card
</button>
<button onclick={saveDeck} class="btn">
<i class="nf nf-fa-save"></i>
Save
<i class="nf nf-md-content_save"></i>
Save All Changes You Made
</button>
</div>
</div>

View file

@ -1,4 +1,3 @@
export let card = $state({ Q: "", a: "" });
export let statusOfCard = $state({
@ -9,37 +8,41 @@ export let statusOfCard = $state({
entering: false,
});
export let deck = [
{ Q: "Best programer in the world?", a: "RezHackXYZ" },
{ Q: "Best coding community?", a: "HackClub" },
{ Q: "Will @Shub go totally bankrupt?", a: "yes!" },
];
export let StorageDeck = $state({ v: [] });
export function SetNewDeck (newDeck) {
deck = newDeck;
export let deck = $state({ v: [] });
export function SetNewDeck(newDeck, ThisIsOfPageLoad = true) {
StorageDeck.v = newDeck;
resetDeck();
stats.isDeckEmpty = ThisIsOfPageLoad;
}
export function SelectNewDeck(deckNumber) {
deck.v = StorageDeck.v[deckNumber].cards;
resetDeck();
}
export let stats = $state({
isDeckEmpty: false,
isDeckEmpty: true,
AnswerKnown: 0,
AnswerNotKnown: 0,
AnswerNotChecked: deck.length,
AnswerNotChecked: deck.v.length,
});
let CurrentDeck = {
AnswersKnown: [],
AnswersNotKnown: [],
AnswersNotChecked: $state.snapshot(deck),
AnswersNotChecked: $state.snapshot(deck).v,
};
export function resetDeck() {
CurrentDeck.AnswersKnown = [];
CurrentDeck.AnswersNotKnown = [];
CurrentDeck.AnswersNotChecked = $state.snapshot(deck);
CurrentDeck.AnswersNotChecked = $state.snapshot(deck).v;
stats.AnswerKnown = 0;
stats.AnswerNotKnown = 0;
stats.AnswerNotChecked = deck.length;
stats.AnswerNotChecked = deck.v.length;
stats.isDeckEmpty = false;
SetNewCard();
}

View file

@ -1,5 +1,26 @@
[
{ "Q": "Best programer in the world?", "a": "RezHackXYZ" },
{ "Q": "Best coding community?", "a": "HackClub" },
{ "Q": "Will @Shub go totally bankrupt?", "a": "yes!" }
{
"deckName": "Tech",
"cards": [
{ "Q": "What is Svelte?", "a": "Best web dev Framework ever." },
{ "Q": "What is a component?", "a": "A reusable piece of UI in Svelte." },
{ "Q": "How do you create a reactive variable?", "a": "$: variableName = value;" }
]
},
{
"deckName": "Math Basics",
"cards": [
{ "Q": "What is 2 + 2?", "a": "4" },
{ "Q": "What is the square root of 16?", "a": "4" },
{ "Q": "What is 5 * 6?", "a": "30" }
]
},
{
"deckName": "Geography",
"cards": [
{ "Q": "What is the capital of France?", "a": "Paris" },
{ "Q": "Which continent is Egypt in?", "a": "Africa" },
{ "Q": "What is the largest ocean?", "a": "Pacific Ocean" }
]
}
]

View file

@ -6,6 +6,7 @@
import { createGame } from "./createGame.js";
let userInput = "";
let numberOfQuestions;
let AIPrompt = `
You are the AI of a quiz game.
@ -13,19 +14,18 @@ Generate a list of quiz questions with possible answers and the correct answer i
Each question must have:
- A "questionText" (what the question is about)
- A "timeLimit" (in seconds, any of these: null (no time limit), 5, 10, 15, 30, 60, 120, 300)
- An "type" (only set to "SingleAnswer" for now)
- An "options" (an array of options with at least 2 and at most 8 options)
- A "CorrectOption" (an object with a key "SingleAnswer" and a value that is the index of the correct answer)
- A "hasMedia" (boolean indicating if the question has media) only set to true if you are sure the URL is valid and related to the question.
- A "mediaURL" (a URL to the media, can be null if no media is present) only add this if you are sure the URL is valid and related to the question.
Ensure the questions are diverse.
Example format:
[{"questionText":"What should you do when you're free?","timeLimit":15,"type":"SingleAnswer","options":["Do something in real life!","Play video games","Code!","Touch grass!"],"CorrectOption":{"SingleAnswer":2},"hasMedia":false,"mediaURL":null},{"questionText":"Is RezHackXYZ the best programmer in the world?","timeLimit":5,"type":"SingleAnswer","options":["Yes :)","No :("],"CorrectOption":{"SingleAnswer":0},"hasMedia":true,"mediaURL":"https://github.com/RezHackXYZ.png"},{"questionText":"Best place in the world?","timeLimit":5,"type":"SingleAnswer","options":["Google","Microsoft","Apple","Samsung","Hack Club!! :D","Amazon","Facebook","Twitter"],"CorrectOption":{"SingleAnswer":4},"hasMedia":false,"mediaURL":null}]
[{"questionText":"What should you do when you're free?","timeLimit":15,"options":["Do something in real life!","Play video games","Code!","Touch grass!"],"CorrectOption":2},{"questionText":"Is RezHackXYZ the best programmer in the world?","timeLimit":5,"options":["Yes :)","No :("],"CorrectOption":0},{"questionText":"Best place in the world?","timeLimit":5,"options":["Google","Microsoft","Apple","Samsung","Hack Club!! :D","Amazon","Facebook","Twitter"],"CorrectOption":4}]
JUST PROVIDE THE JSON AND NOTHING ELSE.
The user's topic of interest is:
[topic]
The User wants [number of questions] questions.
`;
async function ApiCall() {
@ -38,7 +38,7 @@ The user's topic of interest is:
messages: [
{
role: "user",
content: AIPrompt.replace("[topic]", userInput),
content: AIPrompt.replace("[topic]", userInput).replace("[number of questions]", numberOfQuestions),
},
],
}),
@ -46,7 +46,17 @@ The user's topic of interest is:
const data = await response.json();
try {
QuestionsData.v = JSON.parse(data.choices[0].message.content);
QuestionsData.v = JSON.parse(data.choices[0].message.content).map((q) => ({
questionText: q.questionText,
timeLimit: q.timeLimit,
type: "SingleAnswer",
options: q.options,
CorrectOption: {SingleAnswer: q.CorrectOption},
hasMedia: false,
mediaURL: null,
}));
} catch (error) {
if (ParsingTry <= 5) {
ParsingTry++;
@ -60,7 +70,7 @@ The user's topic of interest is:
async function GenerateQuestionsUsingAI() {
ParsingTry = 0;
userInput = prompt(
"Enter the topic and number of questions you want with any instructions for the ai, note: doing this will delete all you previous questions and its not undo able",
"Enter the topic you want with any instructions for the ai, note: doing this will delete all you previous questions and its not undo able",
);
if (!userInput) {
@ -68,6 +78,13 @@ The user's topic of interest is:
return;
}
numberOfQuestions = prompt("How many questions? (e.g. 5, not 'five')", "5");
if (isNaN(parseInt(numberOfQuestions)) || numberOfQuestions <= 0) {
toast.error("Please enter a valid number of questions.");
return;
}
await toast.promise(
(async () => {
try {

View file

@ -60,7 +60,7 @@
import { colseModal, ShowSeconds } from "./IdleScreen/logic/TimeAndTableData.svelte.js";
import EditTimetableDiv from "./IdleScreen/components/timetable/EditTimetable.svelte";
import { TabOpen } from "./randomname/+page.svelte";
import { resetDeck } from "./flashcards/logic.svelte";
import { resetDeck, stats } from "./flashcards/logic.svelte";
import EditCards from "./flashcards/editCards.svelte";
</script>
@ -123,7 +123,7 @@
<button class="btn dull mini">Edit Decks</button>
</Trigger>
</Modal>
<button class="btn dull mini" onclick={() => resetDeck}> Reset current Deck </button>
<button class="btn dull mini" onclick={() => (stats.isDeckEmpty = true)}> Change Deck</button>
{/if}
</div>
</div>