new stuff

This commit is contained in:
2024-10-08 22:00:12 -04:00
parent eeecd72331
commit 6f9de5aa9f
10 changed files with 3447 additions and 68 deletions
Generated
+3144 -1
View File
File diff suppressed because it is too large Load Diff
+153 -5
View File
@@ -11,6 +11,7 @@
"dependencies": {
"@astrojs/svelte": "^5.7.1",
"@astrojs/tailwind": "^5.1.1",
"@honeybadger-io/js": "^6.10.0",
"astro": "^4.15.11",
"gsap": "^3.12.5",
"svelte": "^4.2.19",
@@ -864,6 +865,36 @@
"node": ">=12"
}
},
"node_modules/@honeybadger-io/core": {
"version": "6.6.0",
"resolved": "https://registry.npmjs.org/@honeybadger-io/core/-/core-6.6.0.tgz",
"integrity": "sha512-B5X05huAsDs7NJOYm4bwHf2v0tMuTjBWLfumHH9DCblq8E1XrujlbbNkIdEHlzc01K9oAXuvsaBwVkE7G5+aLQ==",
"license": "MIT",
"dependencies": {
"json-nd": "^1.0.0",
"stacktrace-parser": "^0.1.10"
},
"engines": {
"node": ">=14"
}
},
"node_modules/@honeybadger-io/js": {
"version": "6.10.0",
"resolved": "https://registry.npmjs.org/@honeybadger-io/js/-/js-6.10.0.tgz",
"integrity": "sha512-XHIWLW27IWP+HmWTiiCXVQMOeIGliJdodmWVciENGGrNa0OMLG9UrwVMiM6/lPjtdzLC0PcUVvfvEkMqJbbkug==",
"license": "MIT",
"dependencies": {
"@honeybadger-io/core": "^6.6.0",
"@types/aws-lambda": "^8.10.89",
"@types/express": "^4.17.13"
},
"bin": {
"honeybadger-checkins-sync": "scripts/check-ins-sync-bin.js"
},
"engines": {
"node": ">=14"
}
},
"node_modules/@img/sharp-darwin-arm64": {
"version": "0.33.5",
"resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz",
@@ -1720,6 +1751,12 @@
}
}
},
"node_modules/@types/aws-lambda": {
"version": "8.10.145",
"resolved": "https://registry.npmjs.org/@types/aws-lambda/-/aws-lambda-8.10.145.tgz",
"integrity": "sha512-dtByW6WiFk5W5Jfgz1VM+YPA21xMXTuSFoLYIDY0L44jDLLflVPtZkYuu3/YxpGcvjzKFBZLU+GyKjR0HOYtyw==",
"license": "MIT"
},
"node_modules/@types/babel__core": {
"version": "7.20.5",
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
@@ -1761,6 +1798,25 @@
"@babel/types": "^7.20.7"
}
},
"node_modules/@types/body-parser": {
"version": "1.19.5",
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz",
"integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==",
"license": "MIT",
"dependencies": {
"@types/connect": "*",
"@types/node": "*"
}
},
"node_modules/@types/connect": {
"version": "3.4.38",
"resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz",
"integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==",
"license": "MIT",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/cookie": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz",
@@ -1782,6 +1838,30 @@
"integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==",
"license": "MIT"
},
"node_modules/@types/express": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz",
"integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==",
"license": "MIT",
"dependencies": {
"@types/body-parser": "*",
"@types/express-serve-static-core": "^4.17.33",
"@types/qs": "*",
"@types/serve-static": "*"
}
},
"node_modules/@types/express-serve-static-core": {
"version": "4.19.6",
"resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz",
"integrity": "sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==",
"license": "MIT",
"dependencies": {
"@types/node": "*",
"@types/qs": "*",
"@types/range-parser": "*",
"@types/send": "*"
}
},
"node_modules/@types/hast": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz",
@@ -1791,6 +1871,12 @@
"@types/unist": "*"
}
},
"node_modules/@types/http-errors": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz",
"integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==",
"license": "MIT"
},
"node_modules/@types/mdast": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz",
@@ -1800,6 +1886,12 @@
"@types/unist": "*"
}
},
"node_modules/@types/mime": {
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz",
"integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==",
"license": "MIT"
},
"node_modules/@types/ms": {
"version": "0.7.34",
"resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz",
@@ -1820,12 +1912,43 @@
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.10.tgz",
"integrity": "sha512-vQUKgWTjEIRFCvK6CyriPH3MZYiYlNy0fKiEYHWbcoWLEgs4opurGGKlebrTLqdSMIbXImH6XExNiIyNUv3WpA==",
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"undici-types": "~6.19.2"
}
},
"node_modules/@types/qs": {
"version": "6.9.16",
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.16.tgz",
"integrity": "sha512-7i+zxXdPD0T4cKDuxCUXJ4wHcsJLwENa6Z3dCu8cfCK743OGy5Nu1RmAGqDPsoTDINVEcdXKRvR/zre+P2Ku1A==",
"license": "MIT"
},
"node_modules/@types/range-parser": {
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz",
"integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==",
"license": "MIT"
},
"node_modules/@types/send": {
"version": "0.17.4",
"resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz",
"integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==",
"license": "MIT",
"dependencies": {
"@types/mime": "^1",
"@types/node": "*"
}
},
"node_modules/@types/serve-static": {
"version": "1.15.7",
"resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz",
"integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==",
"license": "MIT",
"dependencies": {
"@types/http-errors": "*",
"@types/node": "*",
"@types/send": "*"
}
},
"node_modules/@types/unist": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz",
@@ -3537,6 +3660,12 @@
"node": ">=6"
}
},
"node_modules/json-nd": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/json-nd/-/json-nd-1.0.0.tgz",
"integrity": "sha512-8TIp0HZAY0VVrwRQJJPb4+nOTSPoOWZeEKBTLizUfQO4oym5Fc/MKqN8vEbLCxcyxDf2vwNxOQ1q84O49GWPyQ==",
"license": "BSD-3-Clause"
},
"node_modules/json5": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
@@ -5735,6 +5864,27 @@
"integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==",
"license": "BSD-3-Clause"
},
"node_modules/stacktrace-parser": {
"version": "0.1.10",
"resolved": "https://registry.npmjs.org/stacktrace-parser/-/stacktrace-parser-0.1.10.tgz",
"integrity": "sha512-KJP1OCML99+8fhOHxwwzyWrlUuVX5GQ0ZpJTd1DFXhdkrvg1szxfHhawXUZ3g9TkXORQd4/WG68jMlQZ2p8wlg==",
"license": "MIT",
"dependencies": {
"type-fest": "^0.7.1"
},
"engines": {
"node": ">=6"
}
},
"node_modules/stacktrace-parser/node_modules/type-fest": {
"version": "0.7.1",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.7.1.tgz",
"integrity": "sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==",
"license": "(MIT OR CC0-1.0)",
"engines": {
"node": ">=8"
}
},
"node_modules/stdin-discarder": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz",
@@ -6159,9 +6309,7 @@
"version": "6.19.8",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz",
"integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==",
"license": "MIT",
"optional": true,
"peer": true
"license": "MIT"
},
"node_modules/unified": {
"version": "11.0.5",
+1
View File
@@ -13,6 +13,7 @@
"dependencies": {
"@astrojs/svelte": "^5.7.1",
"@astrojs/tailwind": "^5.1.1",
"@honeybadger-io/js": "^6.10.0",
"astro": "^4.15.11",
"gsap": "^3.12.5",
"svelte": "^4.2.19",
+8 -2
View File
@@ -84,7 +84,7 @@ export function stepsValidForStationPair(steps, stationA, stationB) {
if (steps[steps.length - 1].station != stationB)
return [
false,
`Route doesn't end at ${agency.stations[stationB].stop_name}.`,
[`Route doesn't end at ${agency.stations[stationB].stop_name}.`],
];
let stops = 0;
@@ -99,7 +99,13 @@ export function stepsValidForStationPair(steps, stationA, stationB) {
if (!possible) {
return [
false,
`${steps[i].line} doesn't serve ${agency.stations[err].stop_name}`,
new Array(steps.length).fill(
`${agency.routes[steps[i].line].route_long_name} doesn't serve ${
agency.stations[err].stop_name
}`,
i,
i + 1
),
];
}
stops += _stops;
+38 -12
View File
@@ -1,5 +1,5 @@
<script>
import { onMount, createEventDispatcher } from "svelte";
import { onMount, createEventDispatcher, tick } from "svelte";
import agency, { minStops, randomStationPair, stepsValidForStationPair } from "../agency"
import MbtaLine from "./MbtaLine.svelte";
import RouteBuilder from "./RouteBuilder.svelte";
@@ -17,52 +17,74 @@
let to
let points = 0
let errors = []
function setStationPair() {
let pointFlourish
let pointCounter
let stationsElement
async function setStationPair() {
const stations = randomStationPair()
from = stations[0]
to = stations[1]
await tick()
gsap.from(stationsElement, { opacity: 0, scale: 0.9 })
}
onMount(() => {
setStationPair()
})
let pointFlourish
let pointCounter
let disabled = false
async function submit() {
if(disabled) return
disabled = true
if(route[route.length - 1].station != to) {
return alert(`Oops, your route doesn't end at ${agency.stations[to].stop_name}!`)
}
const [valid, err, stops] = (stepsValidForStationPair(route, from, to))
if(!valid) {
gsap.to(pointCounter, {
await gsap.to(pointCounter, {
keyframes: {
rotate: [0, -10, 10, -10, 10, -10, 10, -10, 10, 0]
}
})
}).then()
errors = err
} else if(stops > minStops(from, to)) {
await addPoints(1)
next()
} else {
await addPoints(2)
next()
}
}
if(question >= 9) {
function next() {
if(question >= questionCount - 1) {
window.umami?.track("Completed quiz")
dispatch("done", points)
return
}
question++
route = []
errors = []
setStationPair()
disabled = false
}
async function addPoints(p) {
points += p
pointFlourish.innerText = `+${p}`
gsap.to(pointFlourish, {x: -40, y: -50, rotate: -30, scale: 2, opacity: 0, duration: 1, startAt: {opacity: 1, y: 0, x: 0, scale: 1, rotate: 0}})
await gsap.to(pointFlourish, {x: -40, y: -50, rotate: -30, scale: 2, opacity: 0, duration: 1, startAt: {opacity: 1, y: 0, x: 0, scale: 1, rotate: 0}}).then()
}
</script>
@@ -73,7 +95,7 @@
</div>
{#if from && to}
<h3 class="text-3xl">
<h3 class="text-3xl inline-block" bind:this={stationsElement}>
<span class="inline-flex items-center justify-center gap-2">
<span class="font-bold">{agency.stations[from].stop_name}</span>
{#each agency.stations[from].routes as line}
@@ -94,8 +116,12 @@
{/if}
</div>
<RouteBuilder lines={agency.routes} stations={agency.stations} origin={agency.stations[from]?.stop_name} bind:route />
<RouteBuilder {errors} lines={agency.routes} stations={agency.stations} origin={agency.stations[from]?.stop_name} showRemovalButton={!disabled} bind:route />
<button class="text-md border border-blue-500 text-blue-500 font-bold rounded-lg px-5 py-1 uppercase cursor-pointer transition-colors hover:bg-blue-50" on:click={submit} class:opacity-50={route.length == 0} disabled={route.length == 0}>
Submit
<button class="text-md border border-blue-500 text-blue-500 font-bold rounded-lg px-5 py-1 uppercase cursor-pointer transition-colors hover:bg-blue-50" on:click={() => errors.length == 0 ? submit() : next()} class:opacity-50={route.length == 0} disabled={route.length == 0}>
{#if errors.length == 0}
Submit
{:else}
Next
{/if}
</button>
+9
View File
@@ -1,6 +1,7 @@
<script>
import Game from "./Game.svelte";
import screenshot from "../screenshot.png"
import Route from "./Route.svelte";
let page = "welcome"
let score = 0
@@ -28,6 +29,14 @@
{/if}
<div class="flex flex-col items-center justify-center">
<div class="mx-auto">
<Route origin="Sullivan Square" steps={[
{ line: "Orange", station: "place-north" },
{ line: "Green-D", station: "place-pktrm" },
{ line: "Red", station: "place-knncl" },
]} />
</div>
<div class="inline-block relative text-left mb-5">
<h1 class="font-black text-5xl">The Train Quiz</h1>
<div class="max-w-72">
+69
View File
@@ -0,0 +1,69 @@
<script>
import MbtaLine from "./MbtaLine.svelte";
import agency from "../agency"
import { createEventDispatcher } from "svelte";
const dispatch = createEventDispatcher()
export let steps = [];
export let origin;
export let showRemovalButton = false;
export let errors = []
</script>
<ul class="w-full mb-4 empty:mb-0">
<li class="relative font-bold">{origin}</li>
{#each steps as step, index}
<li class="relative flex items-center">
<MbtaLine name={step.line} />
<span>
to
<span class="font-bold relative">
{#if errors[index]}
<div class="absolute bottom-full left-full text-nowrap border border-red-500 text-red-500 bg-white/90 p-3 rounded-lg rounded-bl-none z-40 animate-error origin-bottom-left">
{errors[index]}
</div>
{/if}
{agency.stations[step.station].stop_name}
</span>
</span>
{#if showRemovalButton}
<button class="ml-auto text-gray-500" on:click={() => dispatch("remove", index)}>x</button>
{/if}
</li>
{/each}
</ul>
<style>
li {
@apply pl-5;
}
li:before {
content: "";
width: 10px;
height: 10px;
@apply bg-white rounded-full ring ring-gray-800;
display: block;
position: absolute;
top: calc(50% - 5px);
left: 0;
z-index: 5;
}
li:after {
content: "";
height: 100%;
width: 4px;
@apply bg-gray-800;
position: absolute;
left: 3px;
}
li:first-of-type::after {
@apply rounded-t-full;
}
li:last-of-type::after {
@apply rounded-b-full;
}
</style>
+6 -46
View File
@@ -1,12 +1,14 @@
<script>
import { onMount } from "svelte";
import MbtaLine from "./MbtaLine.svelte";
import Route from "./Route.svelte";
export let route = []
export let errors = []
export let lines
export let stations
export let origin
export let showRemovalButton = true
let selectedLine = ""
let selectedStation = ""
@@ -34,20 +36,9 @@
</script>
<div class="p-5 rounded-lg mb-5">
<ul class="w-full mb-4 empty:mb-0">
<li class="relative font-bold">{origin}</li>
{#each route as step, index }
<li class="relative flex items-center">
<MbtaLine name={step.line} />
<span>
to
<span class="font-bold">{stations[step.station].stop_name}</span>
</span>
<button class="ml-auto text-gray-500" on:click={() => {route = route.filter((v,idx) => idx != index)}}>x</button>
</li>
{/each}
</ul>
<Route steps={route} {errors} {origin} {showRemovalButton} on:remove={({detail}) => {
route = route.filter((v, index) => index != detail)
}} />
<div class="flex gap-2 items-center">
<form on:submit|preventDefault={() => addStep()}>
@@ -75,37 +66,6 @@
</div>
<style>
li {
@apply pl-5;
}
li:before {
content: "";
width: 10px;
height: 10px;
@apply bg-white rounded-full ring ring-gray-800;
display: block;
position: absolute;
top: calc(50% - 5px);
left: 0;
z-index: 5;
}
li:after {
content: "";
height: 100%;
width: 4px;
@apply bg-gray-800;
position: absolute;
left: 3px;
}
li:first-of-type::after {
@apply rounded-t-full;
}
li:last-of-type::after {
@apply rounded-b-full;
}
select {
@apply border border-blue-500 rounded-md;
}
+14 -2
View File
@@ -25,8 +25,12 @@ import screenshot from "../screenshot.png";
<p
class="fixed bottom-0 left-0 text-gray-400 transition-transform translate-y-3 hover:translate-y-0 hover:translate-x-2 cursor-default"
>
<a href="https://calebden.io" class="underline" target="_blank">caleb</a> made
this
<a href="https://calebden.io" class="underline" target="_blank">Caleb</a> made
this &middot; and it's on <a
href="https://github.com/cjdenio/train-quiz"
class="underline"
target="_blank">GitHub</a
>
</p>
<p class="fixed bottom-1 right-2 text-gray-400">
@@ -36,5 +40,13 @@ import screenshot from "../screenshot.png";
target="_blank">MBTA</a
>
</p>
<script>
import Honeybadger from "@honeybadger-io/js";
Honeybadger.configure({
apiKey: "hbp_kMocExYws1yKlIKXs0TkQzbAGmDpqJ2kz24F",
environment: import.meta.env.MODE,
});
</script>
</body>
</html>
+5
View File
@@ -9,9 +9,14 @@ export default {
"50%": { transform: "rotate(-30deg) scale(1.05)" },
"100%": { transform: "rotate(-30deg) scale(1)" },
},
error: {
"0%": { transform: "scale(0.9)", opacity: 0 },
"100%": { transform: "scale(1)", opacity: 100 },
},
},
animation: {
bump: "bump 0.5s ease infinite",
error: "error 0.5s forwards ease",
},
},
},