Added Audio Questions
This commit is contained in:
6
.vscode/settings.json
vendored
6
.vscode/settings.json
vendored
@@ -1,4 +1,8 @@
|
|||||||
{
|
{
|
||||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||||
"editor.formatOnSave": true
|
"editor.formatOnSave": true,
|
||||||
|
"[svg]": {
|
||||||
|
"editor.defaultFormatter": "jock.svg"
|
||||||
|
},
|
||||||
|
"svg.preview.background": "dark-transparent"
|
||||||
}
|
}
|
||||||
|
|||||||
78
src/lib/AudioMultipleChoiceQuestionComponent.svelte
Normal file
78
src/lib/AudioMultipleChoiceQuestionComponent.svelte
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import AudioPlayerComponent from "./AudioPlayerComponent.svelte";
|
||||||
|
import type { AudioMultipleChoiceQuestion } from "./games/games";
|
||||||
|
|
||||||
|
const path = "/sounds/";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
question: AudioMultipleChoiceQuestion;
|
||||||
|
showAnswer: boolean;
|
||||||
|
showQuestion: boolean;
|
||||||
|
showPlayer: boolean;
|
||||||
|
[key: string]: unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
let { question, showAnswer, showQuestion, showPlayer }: Props = $props();
|
||||||
|
|
||||||
|
function shuffle<T>(array: T[]) {
|
||||||
|
let currentIndex = array.length;
|
||||||
|
|
||||||
|
// While there remain elements to shuffle...
|
||||||
|
while (currentIndex != 0) {
|
||||||
|
// Pick a remaining element...
|
||||||
|
let randomIndex = Math.floor(Math.random() * currentIndex);
|
||||||
|
currentIndex--;
|
||||||
|
|
||||||
|
// And swap it with the current element.
|
||||||
|
[array[currentIndex], array[randomIndex]] = [array[randomIndex], array[currentIndex]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const answer = question.data.choices[0];
|
||||||
|
|
||||||
|
let _choices = [...question.data.choices];
|
||||||
|
shuffle(_choices);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="mb-4 flex grow flex-col items-center text-6xl">
|
||||||
|
{#if showQuestion}
|
||||||
|
<div class="flex grow-1 items-center">
|
||||||
|
<div class="text-center">{question.data.question}</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
{#if showPlayer}
|
||||||
|
<div class="flex w-full flex-col justify-center">
|
||||||
|
<AudioPlayerComponent src={path + question.data.audio} />
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
{#if showQuestion}
|
||||||
|
<div class="flex w-full grow-1 flex-wrap items-center justify-around gap-2">
|
||||||
|
{#each _choices as choice}
|
||||||
|
<div class="choiceCard {showAnswer && choice === answer ? 'answer' : ''}">
|
||||||
|
{choice}
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.choiceCard {
|
||||||
|
border: 1px solid black;
|
||||||
|
border-radius: 5px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: fit-content;
|
||||||
|
padding: 32px;
|
||||||
|
/* font-size: larger;
|
||||||
|
font-weight: bold;
|
||||||
|
cursor: pointer; */
|
||||||
|
}
|
||||||
|
|
||||||
|
.answer {
|
||||||
|
background-color: rgb(87, 255, 87);
|
||||||
|
box-shadow: 0 0 20px 5px rgb(87, 255, 87);
|
||||||
|
border: 1px solid rgb(50, 141, 50);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
217
src/lib/AudioPlayerComponent.svelte
Normal file
217
src/lib/AudioPlayerComponent.svelte
Normal file
@@ -0,0 +1,217 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { onMount } from "svelte";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
src: string;
|
||||||
|
[key: string]: unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
const saveKey = "jeopardyAudioVolume";
|
||||||
|
|
||||||
|
let { src }: Props = $props();
|
||||||
|
|
||||||
|
let time = $state(0);
|
||||||
|
let duration = $state(0);
|
||||||
|
let paused = $state(true);
|
||||||
|
let volume = $state(100);
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
let savedVolume = localStorage.getItem(saveKey);
|
||||||
|
if (savedVolume != null) {
|
||||||
|
let parsedVolume = parseFloat(savedVolume);
|
||||||
|
if (!isNaN(parsedVolume)) {
|
||||||
|
setVolume(parsedVolume);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function setVolume(vol: number): void {
|
||||||
|
const audioPlayer = document.getElementById("AudioQuestionAudioPlayer") as HTMLMediaElement;
|
||||||
|
volume = Math.round(vol * 100);
|
||||||
|
if (audioPlayer) {
|
||||||
|
audioPlayer.volume = vol;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function format(time: number) {
|
||||||
|
if (isNaN(time)) return "...";
|
||||||
|
|
||||||
|
const minutes = Math.floor(time / 60);
|
||||||
|
const seconds = Math.floor(time % 60);
|
||||||
|
|
||||||
|
return `${minutes}:${seconds < 10 ? `0${seconds}` : seconds}`;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class={["player", { paused }]}>
|
||||||
|
<audio
|
||||||
|
id="AudioQuestionAudioPlayer"
|
||||||
|
{src}
|
||||||
|
bind:currentTime={time}
|
||||||
|
bind:duration
|
||||||
|
bind:paused
|
||||||
|
onended={() => {
|
||||||
|
time = 0;
|
||||||
|
}}
|
||||||
|
></audio>
|
||||||
|
|
||||||
|
<button class="play" aria-label={paused ? "play" : "pause"} onclick={() => (paused = !paused)}
|
||||||
|
></button>
|
||||||
|
|
||||||
|
<div class="info">
|
||||||
|
<div class="time">
|
||||||
|
<span>{format(time)}</span>
|
||||||
|
<div
|
||||||
|
class="slider"
|
||||||
|
onpointerdown={(e) => {
|
||||||
|
const div = e.currentTarget;
|
||||||
|
|
||||||
|
function seek(e: PointerEvent) {
|
||||||
|
const { left, width } = div.getBoundingClientRect();
|
||||||
|
|
||||||
|
let p = (e.clientX - left) / width;
|
||||||
|
if (p < 0) p = 0;
|
||||||
|
if (p > 1) p = 1;
|
||||||
|
|
||||||
|
time = p * duration;
|
||||||
|
}
|
||||||
|
|
||||||
|
seek(e);
|
||||||
|
|
||||||
|
window.addEventListener("pointermove", seek);
|
||||||
|
|
||||||
|
window.addEventListener(
|
||||||
|
"pointerup",
|
||||||
|
() => {
|
||||||
|
window.removeEventListener("pointermove", seek);
|
||||||
|
},
|
||||||
|
{
|
||||||
|
once: true
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div class="progress" style="--progress: {time / duration}%"></div>
|
||||||
|
</div>
|
||||||
|
<span>{duration ? format(duration) : "--:--"}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div></div>
|
||||||
|
<div class="info">
|
||||||
|
<div class="time">
|
||||||
|
<div
|
||||||
|
class="slider"
|
||||||
|
onpointerdown={(e) => {
|
||||||
|
const div = e.currentTarget;
|
||||||
|
|
||||||
|
function seek(e: PointerEvent) {
|
||||||
|
const { left, width } = div.getBoundingClientRect();
|
||||||
|
|
||||||
|
let p = (e.clientX - left) / width;
|
||||||
|
if (p < 0) p = 0;
|
||||||
|
if (p > 1) p = 1;
|
||||||
|
|
||||||
|
setVolume(p);
|
||||||
|
localStorage.setItem(saveKey, p.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
seek(e);
|
||||||
|
|
||||||
|
window.addEventListener("pointermove", seek);
|
||||||
|
|
||||||
|
window.addEventListener(
|
||||||
|
"pointerup",
|
||||||
|
() => {
|
||||||
|
window.removeEventListener("pointermove", seek);
|
||||||
|
},
|
||||||
|
{
|
||||||
|
once: true
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div class="volume" style="--volume: {volume / 100}%"></div>
|
||||||
|
</div>
|
||||||
|
<span>{volume}%</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.player {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 2.5em 1fr;
|
||||||
|
align-items: center;
|
||||||
|
gap: 1em;
|
||||||
|
padding: 0.5em 1em 0.5em 0.5em;
|
||||||
|
border-radius: 2em;
|
||||||
|
background: gray;
|
||||||
|
transition: filter 0.2s;
|
||||||
|
color: white;
|
||||||
|
user-select: none;
|
||||||
|
font-size: medium;
|
||||||
|
}
|
||||||
|
|
||||||
|
.player:not(.paused) {
|
||||||
|
color: white;
|
||||||
|
filter: drop-shadow(0.5em 0.5em 1em rgba(0, 0, 0, 0.1));
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
width: 100%;
|
||||||
|
aspect-ratio: 1;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: 50% 50%;
|
||||||
|
border-radius: 50%;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.play {
|
||||||
|
width: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
[aria-label="pause"] {
|
||||||
|
background-image: url(/images/pause.svg);
|
||||||
|
}
|
||||||
|
|
||||||
|
[aria-label="play"] {
|
||||||
|
background-image: url(/images/play.svg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.info {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time span {
|
||||||
|
font-size: 0.7em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slider {
|
||||||
|
flex: 1;
|
||||||
|
height: 0.5em;
|
||||||
|
background: lightgray;
|
||||||
|
border-radius: 0.5em;
|
||||||
|
overflow: hidden;
|
||||||
|
cursor: grab;
|
||||||
|
}
|
||||||
|
.slider:active {
|
||||||
|
cursor: grabbing;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress {
|
||||||
|
width: calc(100 * var(--progress));
|
||||||
|
height: 100%;
|
||||||
|
background: lightgreen;
|
||||||
|
}
|
||||||
|
.volume {
|
||||||
|
width: calc(100 * var(--volume));
|
||||||
|
height: 100%;
|
||||||
|
background: lightgreen;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
34
src/lib/AudioQuestionComponent.svelte
Normal file
34
src/lib/AudioQuestionComponent.svelte
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import AudioPlayerComponent from "./AudioPlayerComponent.svelte";
|
||||||
|
import type { AudioQuestion } from "./games/games";
|
||||||
|
|
||||||
|
const path = "/sounds/";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
question: AudioQuestion;
|
||||||
|
showAnswer: boolean;
|
||||||
|
showQuestion: boolean;
|
||||||
|
showPlayer: boolean;
|
||||||
|
[key: string]: unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
let { question, showAnswer, showQuestion, showPlayer }: Props = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="mb-4 flex grow flex-col items-center text-6xl">
|
||||||
|
{#if showQuestion || showAnswer}
|
||||||
|
<div class="flex grow-1 items-center">
|
||||||
|
<div class="text-center">{question.data.question}</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
{#if showPlayer}
|
||||||
|
<div class="flex w-full flex-col justify-center">
|
||||||
|
<AudioPlayerComponent src={path + question.data.audio} />
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
{#if showAnswer}
|
||||||
|
<div class="flex grow-1 items-center text-center">
|
||||||
|
{question.data.answer}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
@@ -46,18 +46,27 @@ const games: Games = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
points: 400,
|
points: 400,
|
||||||
type: "SIMPLE",
|
type: "AUDIO",
|
||||||
data: {
|
data: {
|
||||||
question: "Question 4?",
|
question: "Question 4?",
|
||||||
|
audio: "music.mp3",
|
||||||
answer: "Answer 4"
|
answer: "Answer 4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
points: 500,
|
points: 500,
|
||||||
type: "SIMPLE",
|
type: "AUDIO_MULTIPLE_CHOICE",
|
||||||
data: {
|
data: {
|
||||||
question: "Question 5?",
|
question: "Question 5?",
|
||||||
answer: "Answer 5"
|
audio: "music.mp3",
|
||||||
|
choices: [
|
||||||
|
"Choice 1",
|
||||||
|
"Choice 2",
|
||||||
|
"Choice 3",
|
||||||
|
"Choice 4",
|
||||||
|
"Choice 5",
|
||||||
|
"Choice 6"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -1224,7 +1233,12 @@ const games: Games = [
|
|||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
export type QuestionType = "SIMPLE" | "MULTIPLE_CHOICE" | "IMAGE";
|
export type QuestionType =
|
||||||
|
| "SIMPLE"
|
||||||
|
| "MULTIPLE_CHOICE"
|
||||||
|
| "IMAGE"
|
||||||
|
| "AUDIO"
|
||||||
|
| "AUDIO_MULTIPLE_CHOICE";
|
||||||
|
|
||||||
export type Question = {
|
export type Question = {
|
||||||
points: number;
|
points: number;
|
||||||
@@ -1259,6 +1273,24 @@ export type ImageQuestion = Question & {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type AudioQuestion = Question & {
|
||||||
|
type: "AUDIO";
|
||||||
|
data: {
|
||||||
|
question: string;
|
||||||
|
audio: string;
|
||||||
|
answer: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export type AudioMultipleChoiceQuestion = Question & {
|
||||||
|
type: "AUDIO_MULTIPLE_CHOICE";
|
||||||
|
data: {
|
||||||
|
question: string;
|
||||||
|
audio: string;
|
||||||
|
choices: string[];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
export function isSimpleQuestion(question: Question): question is SimpleQuestion {
|
export function isSimpleQuestion(question: Question): question is SimpleQuestion {
|
||||||
return (question as SimpleQuestion).type === "SIMPLE";
|
return (question as SimpleQuestion).type === "SIMPLE";
|
||||||
}
|
}
|
||||||
@@ -1268,10 +1300,24 @@ export function isMultipleChoiceQuestion(question: Question): question is Multip
|
|||||||
export function isImageQuestion(question: Question): question is ImageQuestion {
|
export function isImageQuestion(question: Question): question is ImageQuestion {
|
||||||
return (question as ImageQuestion).type === "IMAGE";
|
return (question as ImageQuestion).type === "IMAGE";
|
||||||
}
|
}
|
||||||
|
export function isAudioQuestion(question: Question): question is AudioQuestion {
|
||||||
|
return (question as AudioQuestion).type === "AUDIO";
|
||||||
|
}
|
||||||
|
export function isAudioMultipleChoiceQuestion(
|
||||||
|
question: Question
|
||||||
|
): question is AudioMultipleChoiceQuestion {
|
||||||
|
return (question as AudioMultipleChoiceQuestion).type === "AUDIO_MULTIPLE_CHOICE";
|
||||||
|
}
|
||||||
|
|
||||||
export type Category = {
|
export type Category = {
|
||||||
name: string;
|
name: string;
|
||||||
questions: (SimpleQuestion | MultipleChoiceQuestion | ImageQuestion)[];
|
questions: (
|
||||||
|
| SimpleQuestion
|
||||||
|
| MultipleChoiceQuestion
|
||||||
|
| ImageQuestion
|
||||||
|
| AudioQuestion
|
||||||
|
| AudioMultipleChoiceQuestion
|
||||||
|
)[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Wall = {
|
export type Wall = {
|
||||||
|
|||||||
@@ -3,12 +3,20 @@
|
|||||||
import { page } from "$app/state";
|
import { page } from "$app/state";
|
||||||
import { error } from "@sveltejs/kit";
|
import { error } from "@sveltejs/kit";
|
||||||
import SimpleQuestionComponent from "$lib/SimpleQuestionComponent.svelte";
|
import SimpleQuestionComponent from "$lib/SimpleQuestionComponent.svelte";
|
||||||
import { isImageQuestion, isMultipleChoiceQuestion, isSimpleQuestion } from "$lib/games/games";
|
import {
|
||||||
|
isAudioMultipleChoiceQuestion,
|
||||||
|
isAudioQuestion,
|
||||||
|
isImageQuestion,
|
||||||
|
isMultipleChoiceQuestion,
|
||||||
|
isSimpleQuestion
|
||||||
|
} from "$lib/games/games";
|
||||||
import ws from "$lib/websocket.svelte";
|
import ws from "$lib/websocket.svelte";
|
||||||
import { MessageType } from "$lib/MessageType";
|
import { MessageType } from "$lib/MessageType";
|
||||||
import { untrack } from "svelte";
|
import { untrack } from "svelte";
|
||||||
import MultipleChoiceQuestionComponent from "$lib/MultipleChoiceQuestionComponent.svelte";
|
import MultipleChoiceQuestionComponent from "$lib/MultipleChoiceQuestionComponent.svelte";
|
||||||
import ImageQuestionComponent from "$lib/ImageQuestionComponent.svelte";
|
import ImageQuestionComponent from "$lib/ImageQuestionComponent.svelte";
|
||||||
|
import AudioQuestionComponent from "$lib/AudioQuestionComponent.svelte";
|
||||||
|
import AudioMultipleChoiceQuestionComponent from "$lib/AudioMultipleChoiceQuestionComponent.svelte";
|
||||||
|
|
||||||
console.log("wall:", page.params.wall);
|
console.log("wall:", page.params.wall);
|
||||||
|
|
||||||
@@ -126,6 +134,15 @@
|
|||||||
<MultipleChoiceQuestionComponent {question} {showAnswer} {showQuestion} />
|
<MultipleChoiceQuestionComponent {question} {showAnswer} {showQuestion} />
|
||||||
{:else if isImageQuestion(question)}
|
{:else if isImageQuestion(question)}
|
||||||
<ImageQuestionComponent {question} {showAnswer} {showQuestion} {isBuzzed} />
|
<ImageQuestionComponent {question} {showAnswer} {showQuestion} {isBuzzed} />
|
||||||
|
{:else if isAudioQuestion(question)}
|
||||||
|
<AudioQuestionComponent {question} {showAnswer} {showQuestion} showPlayer={false} />
|
||||||
|
{:else if isAudioMultipleChoiceQuestion(question)}
|
||||||
|
<AudioMultipleChoiceQuestionComponent
|
||||||
|
{question}
|
||||||
|
{showAnswer}
|
||||||
|
{showQuestion}
|
||||||
|
showPlayer={false}
|
||||||
|
/>
|
||||||
{:else}
|
{:else}
|
||||||
<p>Type of question unknown</p>
|
<p>Type of question unknown</p>
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
@@ -6,7 +6,9 @@
|
|||||||
isMultipleChoiceQuestion,
|
isMultipleChoiceQuestion,
|
||||||
isSimpleQuestion,
|
isSimpleQuestion,
|
||||||
isImageQuestion,
|
isImageQuestion,
|
||||||
type Game
|
type Game,
|
||||||
|
isAudioQuestion,
|
||||||
|
isAudioMultipleChoiceQuestion
|
||||||
} from "$lib/games/games";
|
} from "$lib/games/games";
|
||||||
import ws from "$lib/websocket.svelte";
|
import ws from "$lib/websocket.svelte";
|
||||||
import { page } from "$app/state";
|
import { page } from "$app/state";
|
||||||
@@ -18,6 +20,8 @@
|
|||||||
import type { VisitedQuestions } from "$lib/Types.js";
|
import type { VisitedQuestions } from "$lib/Types.js";
|
||||||
import MultipleChoiceQuestionComponent from "$lib/MultipleChoiceQuestionComponent.svelte";
|
import MultipleChoiceQuestionComponent from "$lib/MultipleChoiceQuestionComponent.svelte";
|
||||||
import ImageQuestionComponent from "$lib/ImageQuestionComponent.svelte";
|
import ImageQuestionComponent from "$lib/ImageQuestionComponent.svelte";
|
||||||
|
import AudioQuestionComponent from "$lib/AudioQuestionComponent.svelte";
|
||||||
|
import AudioMultipleChoiceQuestionComponent from "$lib/AudioMultipleChoiceQuestionComponent.svelte";
|
||||||
|
|
||||||
let startDisabled = $state(true);
|
let startDisabled = $state(true);
|
||||||
|
|
||||||
@@ -366,6 +370,20 @@
|
|||||||
showQuestion={true}
|
showQuestion={true}
|
||||||
isBuzzed={false}
|
isBuzzed={false}
|
||||||
/>
|
/>
|
||||||
|
{:else if isAudioQuestion(gameManager.question)}
|
||||||
|
<AudioQuestionComponent
|
||||||
|
question={gameManager.question}
|
||||||
|
showAnswer={true}
|
||||||
|
showPlayer={true}
|
||||||
|
showQuestion={true}
|
||||||
|
/>
|
||||||
|
{:else if isAudioMultipleChoiceQuestion(gameManager.question)}
|
||||||
|
<AudioMultipleChoiceQuestionComponent
|
||||||
|
question={gameManager.question}
|
||||||
|
showAnswer={true}
|
||||||
|
showPlayer={true}
|
||||||
|
showQuestion={true}
|
||||||
|
/>
|
||||||
{:else}
|
{:else}
|
||||||
<p>Type of question unknown</p>
|
<p>Type of question unknown</p>
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
10
static/images/pause.svg
Normal file
10
static/images/pause.svg
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<svg width='24' height='24' viewBox='0 0 24 24' fill='none' xmlns='http://www.w3.org/2000/svg'>
|
||||||
|
<style>
|
||||||
|
rect {
|
||||||
|
fill: white;
|
||||||
|
stroke: white;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<rect x='7' y='6' width='2' height='12' stroke-width='1.5' stroke-linejoin='round' />
|
||||||
|
<rect x='15' y='6' width='2' height='12' stroke-width='1.5' stroke-linejoin='round' />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 396 B |
10
static/images/play.svg
Normal file
10
static/images/play.svg
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<svg width='24' height='24' viewBox='0 0 24 24' fill='none' xmlns='http://www.w3.org/2000/svg'>
|
||||||
|
<style>
|
||||||
|
path {
|
||||||
|
fill: white;
|
||||||
|
stroke: white;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<path d='M7.5 11.9999V5.93774L12.75 8.96884L18 11.9999L12.75 15.031L7.5 18.0621V11.9999Z' stroke-width='1.5'
|
||||||
|
stroke-linejoin='round' />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 363 B |
BIN
static/sounds/music.mp3
Normal file
BIN
static/sounds/music.mp3
Normal file
Binary file not shown.
Reference in New Issue
Block a user