improved ui a bit

This commit is contained in:
didier amyot committed 2023-12-12 10:27:54 -05:00
1 parent 002ca66666
commit cbb4d0a674
5 files changed
+112 -35

No files matched your search

Generated
+7
View File
@@ -6,6 +6,7 @@
"https://unpkg.com/pure-rand/lib/esm/pure-rand.js": "https://unpkg.com/pure-rand@6.0.4/lib/esm/pure-rand.js" "https://unpkg.com/pure-rand/lib/esm/pure-rand.js": "https://unpkg.com/pure-rand@6.0.4/lib/esm/pure-rand.js"
}, },
"remote": { "remote": {
"https://cdn.jsdelivr.net/npm/highlight.js@11.9.0": "91a9625e96503cd01729ec392b4dfb0a33d6062395c1907041344bd7706ac099",
"https://deno.land/std@0.200.0/_util/os.ts": "d932f56d41e4f6a6093d56044e29ce637f8dcc43c5a90af43504a889cf1775e3", "https://deno.land/std@0.200.0/_util/os.ts": "d932f56d41e4f6a6093d56044e29ce637f8dcc43c5a90af43504a889cf1775e3",
"https://deno.land/std@0.200.0/assert/assert.ts": "9a97dad6d98c238938e7540736b826440ad8c1c1e54430ca4c4e623e585607ee", "https://deno.land/std@0.200.0/assert/assert.ts": "9a97dad6d98c238938e7540736b826440ad8c1c1e54430ca4c4e623e585607ee",
"https://deno.land/std@0.200.0/assert/assertion_error.ts": "4d0bde9b374dfbcbe8ac23f54f567b77024fb67dbb1906a852d67fe050d42f56", "https://deno.land/std@0.200.0/assert/assertion_error.ts": "4d0bde9b374dfbcbe8ac23f54f567b77024fb67dbb1906a852d67fe050d42f56",
@@ -185,13 +186,19 @@
"https://denopkg.com/chiefbiiko/sha1@master/deps.ts": "434b70f37ed145ca5259353c96fdee1e7d1cd8c6a32baad9dce2aedc79d0a3bc", "https://denopkg.com/chiefbiiko/sha1@master/deps.ts": "434b70f37ed145ca5259353c96fdee1e7d1cd8c6a32baad9dce2aedc79d0a3bc",
"https://denopkg.com/chiefbiiko/sha1@master/mod.ts": "146a101c9776cc9c807053c93f23e4b321ade5251f65745df418f4a75d5fd27b", "https://denopkg.com/chiefbiiko/sha1@master/mod.ts": "146a101c9776cc9c807053c93f23e4b321ade5251f65745df418f4a75d5fd27b",
"https://denopkg.com/chiefbiiko/std-encoding@v1.1.1/mod.ts": "e61017da4cef146863d614a961b4e61f1df14b72c906092bd02161f913e76522", "https://denopkg.com/chiefbiiko/std-encoding@v1.1.1/mod.ts": "e61017da4cef146863d614a961b4e61f1df14b72c906092bd02161f913e76522",
"https://esm.sh/highlight.js@11.9.0": "fedce3b9e0d80a1302f7880a5db7527b00fb565777abe6350769883790465c65",
"https://esm.sh/highlight.js@11.9.0/es/languages/javascript.js": "28afc23607edc404bc69093b2f1eb82dbae810e3ef6bf1357c3e90b5bb5c6fea",
"https://esm.sh/preact-render-to-string@5.1.19?deps=preact@10.5.15": "fba6961b9688151eeccc5425e05740009c3e7abee863615e626fb13e1d17e87a", "https://esm.sh/preact-render-to-string@5.1.19?deps=preact@10.5.15": "fba6961b9688151eeccc5425e05740009c3e7abee863615e626fb13e1d17e87a",
"https://esm.sh/preact@10.19.2": "41f31ba8eecf666f08289f2205f57c45f74baba5be815a55c15c4cc61086143c", "https://esm.sh/preact@10.19.2": "41f31ba8eecf666f08289f2205f57c45f74baba5be815a55c15c4cc61086143c",
"https://esm.sh/preact@10.19.2/jsx-runtime": "2a4111d9aa667a3e3776ffd9082d00d63f6960c038d01620d79f80645ecc049d", "https://esm.sh/preact@10.19.2/jsx-runtime": "2a4111d9aa667a3e3776ffd9082d00d63f6960c038d01620d79f80645ecc049d",
"https://esm.sh/stable/preact@10.19.2/denonext/jsx-runtime.js": "f196c14042fc6019f3fed31eec7cfedea4669d0926858939380a5f54bd503798", "https://esm.sh/stable/preact@10.19.2/denonext/jsx-runtime.js": "f196c14042fc6019f3fed31eec7cfedea4669d0926858939380a5f54bd503798",
"https://esm.sh/stable/preact@10.19.2/denonext/preact.mjs": "dc999b6432dc04d74ad7b692a8801f346294d815266dff915d0f222c120fd0b0", "https://esm.sh/stable/preact@10.19.2/denonext/preact.mjs": "dc999b6432dc04d74ad7b692a8801f346294d815266dff915d0f222c120fd0b0",
"https://esm.sh/stable/preact@10.5.15/denonext/preact.mjs": "4e6645aa945a53e44eaa4d882f9f9b4e76c992875fad9ef5791784474a71a116", "https://esm.sh/stable/preact@10.5.15/denonext/preact.mjs": "4e6645aa945a53e44eaa4d882f9f9b4e76c992875fad9ef5791784474a71a116",
"https://esm.sh/v135/highlight.js@11.9.0/denonext/es/languages/javascript.js": "e6dbfe6f4e73f1ea4b346b20336e48a710be4aa516c0baad2b68721ce5fd3a7c",
"https://esm.sh/v135/highlight.js@11.9.0/denonext/highlight.mjs": "a6c1ad8d57cb7bff25c3e0084d9312631fa6e24cd7da5a9565314410897af271",
"https://esm.sh/v135/highlight.js@11.9.0/denonext/lib/core.js": "8653667a14004953b2fcbf10be691096e9f699741b23e5a858cb1e4b09f9e3b3",
"https://esm.sh/v135/preact-render-to-string@5.1.19/X-ZC9wcmVhY3RAMTAuNS4xNQ/denonext/preact-render-to-string.mjs": "3ce1bda513633d15cd313e97ab7eacba9bd50eb4dc95e41861056c6bae1bfac8", "https://esm.sh/v135/preact-render-to-string@5.1.19/X-ZC9wcmVhY3RAMTAuNS4xNQ/denonext/preact-render-to-string.mjs": "3ce1bda513633d15cd313e97ab7eacba9bd50eb4dc95e41861056c6bae1bfac8",
"https://unpkg.com/@highlightjs/cdn-assets@11.9.0/highlight.min.js": "837a6fa5b0c736b52bbde2b2b6190f305da3fc9ed41681db5321507057b5c846",
"https://unpkg.com/pure-rand@6.0.4/lib/esm/distribution/UniformArrayIntDistribution.js": "121a5017f80ac3c36359877407f6f9d16cc8d28fb88e21990d1ecba6a6ad6a90", "https://unpkg.com/pure-rand@6.0.4/lib/esm/distribution/UniformArrayIntDistribution.js": "121a5017f80ac3c36359877407f6f9d16cc8d28fb88e21990d1ecba6a6ad6a90",
"https://unpkg.com/pure-rand@6.0.4/lib/esm/distribution/UniformBigIntDistribution.js": "1fafebba70f83e619d852964506bbb259e26618238b087bf7cab203b9848bf71", "https://unpkg.com/pure-rand@6.0.4/lib/esm/distribution/UniformBigIntDistribution.js": "1fafebba70f83e619d852964506bbb259e26618238b087bf7cab203b9848bf71",
"https://unpkg.com/pure-rand@6.0.4/lib/esm/distribution/UniformIntDistribution.js": "fb21df31e3395ed0f38194ec5e99bcba6c89be02acd2d7cc07145b665d997e78", "https://unpkg.com/pure-rand@6.0.4/lib/esm/distribution/UniformIntDistribution.js": "fb21df31e3395ed0f38194ec5e99bcba6c89be02acd2d7cc07145b665d997e78",
+18 -5
View File
@@ -6,6 +6,9 @@
<meta http-equiv="X-UA-Compatible" content="ie=edge"> <meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>JS Reading App</title> <title>JS Reading App</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.4/css/bulma.min.css"> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.4/css/bulma.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.9.0/build/styles/default.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/atom-one-light.min.css">
<script src="https://unpkg.com/htmx.org@1.9.9"></script> <script src="https://unpkg.com/htmx.org@1.9.9"></script>
</head> </head>
<body> <body>
@@ -25,12 +28,22 @@
</div> </div>
<script> <script>
function getDefaultIndex(){
if(document.location.hash.length > 1){ function getIndexFromHash(){
return document.location.hash.substr(1) if(document.location.hash.length < 2){
return null;
} }
return localStorage.getItem("defaultIndex") || '0'; const ret = document.location.hash.substr(1)
if(isNaN(ret)){
return ret;
}
return parseInt(ret) - 1;
} }
function getDefaultIndex(){
return getIndexFromHash() || localStorage.getItem("defaultIndex") || '0';
}
document.body.addEventListener('htmx:configRequest', function(evt) { document.body.addEventListener('htmx:configRequest', function(evt) {
evt.detail.headers.session = localStorage.getItem("seed"); evt.detail.headers.session = localStorage.getItem("seed");
evt.detail.path = evt.detail.path.replace(":seed", localStorage.getItem("seed")); evt.detail.path = evt.detail.path.replace(":seed", localStorage.getItem("seed"));
@@ -46,7 +59,7 @@
if(isNaN(evt.detail.value)){ if(isNaN(evt.detail.value)){
return; return;
} }
document.location.hash = evt.detail.value document.location.hash = parseInt(evt.detail.value) + 1;
localStorage.setItem("defaultIndex", evt.detail.value); localStorage.setItem("defaultIndex", evt.detail.value);
}); });
+30 -1
View File
@@ -6,7 +6,21 @@ function basicFrenchQuestion(argumentValue){
} }
function basicQuestionValidator(expected){ function basicQuestionValidator(expected){
return (answer) => answer.toString() === expected.toString(); const cannonicalString = (v) => {
if(v === null){
return 'null';
}
if(v === undefined){
return 'undefined'
}
return v.toString()
}
return (answer) => {
return cannonicalString(answer) === cannonicalString(expected);
}
} }
function basicQuestionMetaData(question){ function basicQuestionMetaData(question){
@@ -211,3 +225,18 @@ export function question015(x){
question015.questions = [-4, 2, 7].map(basicQuestionMetaData(question015)) question015.questions = [-4, 2, 7].map(basicQuestionMetaData(question015))
export function question016(id){
const people = [{id: 12, name: "bob"},
{id: 34, name: "alice"},
{id: 56, name: "cedric"},
{id: 6, name: "david"}, ]
for(const person of people){
if (person.id === id){
return person;
}
}
}
question016.questions = [12, 56, 7].map(basicQuestionMetaData(question016))
+56 -28
View File
@@ -5,6 +5,9 @@ import { renderToString } from "https://esm.sh/preact-render-to-string@5.1.19?de
import * as prand from 'https://unpkg.com/pure-rand/lib/esm/pure-rand.js'; import * as prand from 'https://unpkg.com/pure-rand/lib/esm/pure-rand.js';
import {QuestionSource, QuestionMeta} from './shared.ts'; import {QuestionSource, QuestionMeta} from './shared.ts';
import highlightJs from "https://esm.sh/highlight.js@11.9.0"
import highlightJsJavascript from "https://esm.sh/highlight.js@11.9.0/es/languages/javascript.js"
highlightJs.registerLanguage('javascript', highlightJsJavascript);
type Question = [QuestionSource, QuestionMeta]; type Question = [QuestionSource, QuestionMeta];
@@ -14,7 +17,7 @@ type QuestionURLS = {
answerQuestionUrl: string answerQuestionUrl: string
}; };
const questions: Question = Object.values(rawQuestions).flatMap(s => s.questions.map( q => [s, q])); const questions: Question[] = Object.values(rawQuestions).flatMap(s => s.questions.map( (q, idx) => [s as QuestionSource, q as QuestionMeta]));
const apiRouter = new Router(); const apiRouter = new Router();
const loginRouter = new Router(); const loginRouter = new Router();
@@ -28,33 +31,43 @@ function hashString(str: string): number {
hash |= 0; // Convert to 32bit integer hash |= 0; // Convert to 32bit integer
} }
return hash; return hash;
} }
function randomPermutation(seed: number, n: number): number[] { function randomPermutation(seed: number, n: number): number[] {
const rand = prand.mersenne(seed) const rand = prand.mersenne(seed)
const base = [...Array(n).keys()]; const base = [...Array(n).keys()];
for(let i = 0; i < (n -1); i++){ for(let i = 0; i < (n -1); i++){
const j = prand.unsafeUniformIntDistribution(i, n-1, rand); const j = prand.unsafeUniformIntDistribution(i, n-1, rand);
const tmp = base[i]; const tmp = base[i];
base[i] = base[j]; base[i] = base[j];
base[j] = tmp; base[j] = tmp;
} }
return base; return base;
}
function getRandomQuestion<T>(seed: string, index: number, questions: Question[]): Question {
const numberSeed = hashString(seed);
const permutations = randomPermutation(numberSeed, questions.length)
return questions[permutations[index]];
} }
function getQuestionPage(question: Question, urls: QuestionURLS){ function isSecretSeed(seed: string): boolean{
return seed === 'cannonical';
}
function getRandomQuestion<T>(seed: string, index: number, questions: Question[]): Question {
if(isSecretSeed(seed)){
return questions[index] ;
}
const numberSeed = hashString(seed);
const permutations = randomPermutation(numberSeed, questions.length)
return [... questions[permutations[index]]];
}
function getQuestionPage(question: Question, urls: QuestionURLS, questionIndex: number){
const source = highlightJs.highlight(question[0].toString(), {language: 'javascript'}).value;
return (<div> return (<div>
<pre> { question[0].toString() }</pre> <div style="text-align:center">
<div class="content mt-2 mb-2"> {questionIndex + 1}/{ questions.length }
</div>
<pre> <code dangerouslySetInnerHTML={{__html: source}}></code></pre>
<div class="content mt-2 mb-2" >
{ question[1].question_fr } { question[1].question_fr }
</div> </div>
@@ -129,17 +142,26 @@ function processValidAnswer(ctx, answer: string): void {
ctx.response.body = renderToString(getValidAnswerMessage()); ctx.response.body = renderToString(getValidAnswerMessage());
} }
function getQuestionIndex(ctx){
const index = Number.parseInt(ctx.params.idx??"0");
return index % questions.length;
}
function getNextQuestionIndex(ctx){
return (getQuestionIndex(ctx) + 1) % questions.length
}
function getQuestion(ctx): Question{ function getQuestion(ctx): Question{
const seed = ctx.params.seed; const seed = ctx.params.seed;
const index = Number.parseInt(ctx.params.idx??"0"); const index = getQuestionIndex(ctx);
return getRandomQuestion(seed, index, questions); return getRandomQuestion(seed, index, questions);
} }
function getUrls(ctx): QuestionURLS{ function getUrls(ctx): QuestionURLS{
const seed = ctx.params.seed; const seed = ctx.params.seed;
const index = Number.parseInt(ctx.params.idx??"0"); const index = getQuestionIndex(ctx);
const nextQuestionUrl = `/api/v1/random-questions/${seed}/${index + 1}`; const nextQuestionIndex = getNextQuestionIndex(ctx);
const nextQuestionUrl = `/api/v1/random-questions/${seed}/${nextQuestionIndex}`;
const validationUrl = `/api/v1/random-questions/${seed}/${index}/validate`; const validationUrl = `/api/v1/random-questions/${seed}/${index}/validate`;
const answerQuestionUrl = `/api/v1/random-questions/${seed}/${index}/answer` const answerQuestionUrl = `/api/v1/random-questions/${seed}/${index}/answer`
return {nextQuestionUrl, validationUrl, answerQuestionUrl}; return {nextQuestionUrl, validationUrl, answerQuestionUrl};
@@ -201,12 +223,18 @@ apiRouter.get("/api/v1/random-questions/:seed/:idx/answer", (ctx)=> {
apiRouter.get("/api/v1/random-questions/:seed/:idx", (ctx)=> { apiRouter.get("/api/v1/random-questions/:seed/:idx", (ctx)=> {
const q = getQuestion(ctx); const q = getQuestion(ctx);
const urls = getUrls(ctx); const urls = getUrls(ctx);
const questionIndex = getQuestionIndex(ctx);
ctx.response.body = renderToString(getQuestionPage(q, urls));
ctx.response.body = renderToString(getQuestionPage(q, urls, questionIndex));
ctx.response.headers.append("HX-Trigger", JSON.stringify({'set-question-id': ctx.params.idx})); ctx.response.headers.append("HX-Trigger", JSON.stringify({'set-question-id': ctx.params.idx}));
}); });
loginRouter.get("/toto", (ctx)=> {
ctx.response.body = highlightJs.highlight('function(){console.log("hello world")}', {language: 'javascript'}).value;
});
loginRouter.post("/api/v1/login", async (ctx)=> { loginRouter.post("/api/v1/login", async (ctx)=> {
const username = (await ctx.request.body().value).get("username"); const username = (await ctx.request.body().value).get("username");
ctx.response.headers.append("HX-Trigger", JSON.stringify({'set-seed': username})); ctx.response.headers.append("HX-Trigger", JSON.stringify({'set-seed': username}));
+1 -1
View File
@@ -1,4 +1,4 @@
interface QuestionSource { export interface QuestionSource {
toString(): string; toString(): string;
} }