got sql working
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed

This commit is contained in:
Michael Zhang 2024-05-15 20:04:15 -05:00
parent a45002f693
commit 10719b6d83
21 changed files with 533 additions and 35 deletions

5
.gitignore vendored
View file

@ -4,4 +4,7 @@ certs
.env.local
.env.production
.env.staging
stepData.ndjson
stepData.ndjson
public/bannerImages/*
!public/bannerImages/.gitkeep

30
lib/db/client.ts Normal file
View file

@ -0,0 +1,30 @@
import type { BindParams, QueryExecResult } from "sql.js";
import { RpcProvider } from "worker-rpc";
import DbWorker from "./db.worker?worker";
const worker = new DbWorker();
const rpcProvider = new RpcProvider((message, transfer) =>
worker.postMessage(message, transfer),
);
worker.onmessage = (e) => rpcProvider.dispatch(e.data);
export async function getDbClient() {
await new Promise<void>((resolve) => {
rpcProvider.registerSignalHandler("ready", () => {
resolve();
});
});
return {
async run(s: string, p?: BindParams[]) {
return await rpcProvider.rpc("run", { s, p });
},
async exec(s: string, p?: BindParams[]): Promise<QueryExecResult[]> {
return await rpcProvider.rpc("exec", { s, p });
},
};
}
export const dbClient = await getDbClient();

59
lib/db/db.worker.ts Normal file
View file

@ -0,0 +1,59 @@
import sqlWasmUrl from "@jlongster/sql.js/dist/sql-wasm.wasm?url";
import initSqlJs from "@jlongster/sql.js";
import { SQLiteFS } from "absurd-sql";
import type { Database } from "sql.js";
import IndexedDBBackend from "absurd-sql/dist/indexeddb-backend";
import { RpcProvider } from "worker-rpc";
let SQL;
let db: Database;
async function init() {
const SQL = await initSqlJs({
locateFile: (file) => {
console.log("FILE IS", file);
console.log("url is", sqlWasmUrl);
switch (file) {
case "sql-wasm.wasm":
return sqlWasmUrl;
}
return file;
},
});
const sqlFS = new SQLiteFS(SQL.FS, new IndexedDBBackend());
SQL.register_for_idb(sqlFS);
SQL.FS.mkdir("/sql");
SQL.FS.mount(sqlFS, {}, "/sql");
const path = "/sql/db.sqlite";
if (typeof SharedArrayBuffer === "undefined") {
const stream = SQL.FS.open(path, "a+");
await stream.node.contents.readIfFallback();
SQL.FS.close(stream);
}
db = new SQL.Database(path, { filename: true });
db.exec("PRAGMA journal_mode=MEMORY;");
}
await init();
const rpcProvider = new RpcProvider((message, transfer) =>
self.postMessage(message, undefined, transfer),
);
self.addEventListener("message", (evt) => rpcProvider.dispatch(evt.data));
rpcProvider.registerRpcHandler("run", ({ s, p }) => {
return db.run(s, p);
});
rpcProvider.registerRpcHandler("exec", ({ s, p }) => {
return db.exec(s, p);
});
console.log("SHIET");
rpcProvider.signal("ready");

7
lib/idb.ts Normal file
View file

@ -0,0 +1,7 @@
// TODO: Actually do a partial download
export function downloadDatafile() {
// Get download status from local storage
const stepDataDownloadStatus = localStorage.getItem(
"stepDataDownloadStatus",
) ?? { hash: null };
}

View file

@ -25,10 +25,13 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
export default function ndjsonStream(stream: ReadableStream) {
export default function ndjsonStream<T>(
stream: ReadableStream,
): ReadableStream<T> {
// For cancellation
let is_reader: ReadableStreamDefaultReader | undefined = undefined;
let cancellationRequest = false;
return new ReadableStream({
start: (controller) => {
const reader = stream.getReader();
@ -46,7 +49,7 @@ export default function ndjsonStream(stream: ReadableStream) {
data_buf = data_buf.trim();
if (data_buf.length !== 0) {
try {
const data_l = JSON.parse(data_buf);
const data_l = JSON.parse(data_buf) as T;
controller.enqueue(data_l);
} catch (e) {
controller.error(e);

View file

@ -111,7 +111,7 @@ function parseArrowStream(
const arrows: Arrow[] = [];
const freezes: FreezeBody[] = [];
let currentFreezeDirections: string[] = [];
const currentFreezeDirections: string[] = [];
const openFreezes: Record<
FreezeBody["direction"],
Partial<FreezeBody> | null

View file

@ -1,13 +1,19 @@
import { stat, readFile, readdir, copyFile } from "node:fs/promises";
import { join, extname, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import { join, extname, dirname, resolve } from "node:path";
import { createHash } from "node:crypto";
import { parseDwi } from "./parseDwi";
import { parseSm } from "./parseSm";
const FILENAME = fileURLToPath(import.meta.url);
const REPO_ROOT = dirname(dirname(dirname(FILENAME)));
type RawSimfile = Omit<Simfile, "mix" | "title"> & {
title: string;
titletranslit: string | null;
banner: string | null;
bannerEncoded: string | null;
bannerHash: string | null;
bannerFilename: string | null;
displayBpm: string | undefined;
};
type Parser = (simfileSource: string, titleDir: string) => Promise<RawSimfile>;
@ -71,7 +77,13 @@ async function parseSimfile(
const bannerPath = join(stepchartSongDirPath, rawStepchart.banner);
try {
const bannerData = await readFile(bannerPath);
rawStepchart.bannerEncoded = bannerData.toString("base64");
const hash = createHash("sha256").update(bannerData).digest("hex");
const ext = extname(rawStepchart.banner);
const filename = `${hash}${ext}`;
const outPath = join(REPO_ROOT, "public", "bannerImages", filename);
copyFile(bannerPath, outPath);
rawStepchart.bannerHash = hash;
rawStepchart.bannerFilename = filename;
} catch (e) {}
// if (bannerMeta !== undefined) {

View file

@ -16,7 +16,8 @@ function concludesANoteTag(line: string | undefined): boolean {
return line[0] === ";" || (line[0] === "," && line[1] === ";");
}
function getMeasureLength(lines: string[], i: number): number {
function getMeasureLength(lines: string[], i0: number): number {
let i = i0;
let measureLength = 0;
for (
@ -44,8 +45,9 @@ function isRest(line: string): boolean {
function findFirstNonEmptyMeasure(
mode: "single" | "double",
lines: string[],
i: number,
i0: number,
): { firstNonEmptyMeasureIndex: number; numMeasuresSkipped: number } {
let i = i0;
let numMeasuresSkipped = 0;
let measureIndex = i;
@ -202,7 +204,8 @@ async function parseSm(sm: string, _titlePath: string): Promise<RawSimfile> {
return freezes;
}
function parseNotes(lines: string[], i: number, bpmString: string): number {
function parseNotes(lines: string[], i0: number, bpmString: string): number {
let i = i0;
// move past #NOTES into the note metadata
i++;
const mode = lines[i++].replace("dance-", "").replace(":", "");

View file

@ -52,6 +52,7 @@ type Stop = {
};
type Stepchart = {
src: string;
arrows: Arrow[];
freezes: FreezeBody[];
bpm: Bpm[];

View file

@ -19,6 +19,7 @@
"@types/ndjson": "^2.0.4",
"@types/react": "^18.3.2",
"@types/react-dom": "^18.3.0",
"@types/sql.js": "^1.4.9",
"@vite-pwa/assets-generator": "^0.2.4",
"@vitejs/plugin-basic-ssl": "^1.1.0",
"@vitejs/plugin-react-swc": "^3.6.0",
@ -33,9 +34,16 @@
"dependencies": {
"@emotion/react": "^11.11.4",
"@emotion/styled": "^11.11.5",
"@fontsource/inter": "^5.0.18",
"@jlongster/sql.js": "^1.6.7",
"@mui/icons-material": "^5.15.17",
"@mui/joy": "5.0.0-beta.36",
"@mui/material": "^5.15.17",
"@tanstack/react-query": "^5.36.1",
"@tanstack/react-table": "^8.17.3",
"@zlepper/rpc": "^0.0.10",
"@zlepper/web-worker-rpc": "^0.0.10",
"absurd-sql": "^0.0.54",
"classnames": "^2.5.1",
"jotai": "^2.8.0",
"ndjson": "^2.0.0",
@ -43,6 +51,7 @@
"react-dom": "^18.3.1",
"react-router": "^6.23.1",
"react-router-dom": "^6.23.1",
"stream-json": "^1.8.0"
"stream-json": "^1.8.0",
"worker-rpc": "^0.2.0"
}
}

View file

@ -14,15 +14,36 @@ importers:
'@emotion/styled':
specifier: ^11.11.5
version: 11.11.5(@emotion/react@11.11.4(@types/react@18.3.2)(react@18.3.1))(@types/react@18.3.2)(react@18.3.1)
'@fontsource/inter':
specifier: ^5.0.18
version: 5.0.18
'@jlongster/sql.js':
specifier: ^1.6.7
version: 1.6.7
'@mui/icons-material':
specifier: ^5.15.17
version: 5.15.17(@mui/material@5.15.17(@emotion/react@11.11.4(@types/react@18.3.2)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.2)(react@18.3.1))(@types/react@18.3.2)(react@18.3.1))(@types/react@18.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.2)(react@18.3.1)
'@mui/joy':
specifier: 5.0.0-beta.36
version: 5.0.0-beta.36(@emotion/react@11.11.4(@types/react@18.3.2)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.2)(react@18.3.1))(@types/react@18.3.2)(react@18.3.1))(@types/react@18.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@mui/material':
specifier: ^5.15.17
version: 5.15.17(@emotion/react@11.11.4(@types/react@18.3.2)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.2)(react@18.3.1))(@types/react@18.3.2)(react@18.3.1))(@types/react@18.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@tanstack/react-query':
specifier: ^5.36.1
version: 5.36.1(react@18.3.1)
'@tanstack/react-table':
specifier: ^8.17.3
version: 8.17.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@zlepper/rpc':
specifier: ^0.0.10
version: 0.0.10
'@zlepper/web-worker-rpc':
specifier: ^0.0.10
version: 0.0.10(@zlepper/rpc@0.0.10)
absurd-sql:
specifier: ^0.0.54
version: 0.0.54
classnames:
specifier: ^2.5.1
version: 2.5.1
@ -47,6 +68,9 @@ importers:
stream-json:
specifier: ^1.8.0
version: 1.8.0
worker-rpc:
specifier: ^0.2.0
version: 0.2.0
devDependencies:
'@biomejs/biome':
specifier: ^1.7.3
@ -60,6 +84,9 @@ importers:
'@types/react-dom':
specifier: ^18.3.0
version: 18.3.0
'@types/sql.js':
specifier: ^1.4.9
version: 1.4.9
'@vite-pwa/assets-generator':
specifier: ^0.2.4
version: 0.2.4
@ -945,6 +972,12 @@ packages:
'@floating-ui/utils@0.2.2':
resolution: {integrity: sha512-J4yDIIthosAsRZ5CPYP/jQvUAQtlZTTD/4suA08/FEnlxqW3sKS9iAhgsa9VYLZ6vDHn/ixJgIqRQPotoBjxIw==}
'@fontsource/inter@5.0.18':
resolution: {integrity: sha512-YCsoYPTcs713sI7tLtxaPrIhXAXvEetGg5Ry02ivA8qUOb3fQHojbK/X9HLD5OOKvFUNR2Ynkwb1kR1hVKQHpw==}
'@jlongster/sql.js@1.6.7':
resolution: {integrity: sha512-4hf0kZr5WPoirdR5hUSfQ9O0JpH/qlW1CaR2wZ6zGrDz1xjSdTPuR8AW/oXzIHnJvZSEvlcIE+dfXJZwh/Lxfw==}
'@jridgewell/gen-mapping@0.3.5':
resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==}
engines: {node: '>=6.0.0'}
@ -977,9 +1010,23 @@ packages:
'@types/react':
optional: true
'@mui/base@5.0.0-beta.42':
resolution: {integrity: sha512-fWRiUJVCHCPF+mxd5drn08bY2qRw3jj5f1SSQdUXmaJ/yKpk23ys8MgLO2KGVTRtbks/+ctRfgffGPbXifj0Ug==}
engines: {node: '>=12.0.0'}
peerDependencies:
'@types/react': ^17.0.0 || ^18.0.0
react: ^17.0.0 || ^18.0.0
react-dom: ^17.0.0 || ^18.0.0
peerDependenciesMeta:
'@types/react':
optional: true
'@mui/core-downloads-tracker@5.15.17':
resolution: {integrity: sha512-DVAejDQkjNnIac7MfP8sLzuo7fyrBPxNdXe+6bYqOqg1z2OPTlfFAejSNzWe7UenRMuFu9/AyFXj/X2vN2w6dA==}
'@mui/core-downloads-tracker@6.0.0-dev.240424162023-9968b4889d':
resolution: {integrity: sha512-doh3M3U7HUGSBIWGe1yvesSbfDguMRjP0N09ogWSBM2hovXAlgULhMgcRTepAZLLwfRxFII0bCohq6B9NqoKuw==}
'@mui/icons-material@5.15.17':
resolution: {integrity: sha512-xVzl2De7IY36s/keHX45YMiCpsIx3mNv2xwDgtBkRSnZQtVk+Gqufwj1ktUxEyjzEhBl0+PiNJqYC31C+n1n6A==}
engines: {node: '>=12.0.0'}
@ -991,6 +1038,23 @@ packages:
'@types/react':
optional: true
'@mui/joy@5.0.0-beta.36':
resolution: {integrity: sha512-fFW8jqA/6JcnjjSUgiFJ17bbvMxwFU+daT5Ohpi1qYrDub0XYj1+8UYDUgGCLsv8XNe50AkbnqidtAlMpq7Glg==}
engines: {node: '>=12.0.0'}
peerDependencies:
'@emotion/react': ^11.5.0
'@emotion/styled': ^11.3.0
'@types/react': ^17.0.0 || ^18.0.0
react: ^17.0.0 || ^18.0.0
react-dom: ^17.0.0 || ^18.0.0
peerDependenciesMeta:
'@emotion/react':
optional: true
'@emotion/styled':
optional: true
'@types/react':
optional: true
'@mui/material@5.15.17':
resolution: {integrity: sha512-ru/MLvTkCh0AZXmqwIpqGTOoVBS/sX48zArXq/DvktxXZx4fskiRA2PEc7Rk5ZlFiZhKh4moL4an+l8zZwq49Q==}
engines: {node: '>=12.0.0'}
@ -1018,6 +1082,16 @@ packages:
'@types/react':
optional: true
'@mui/private-theming@6.0.0-alpha.5':
resolution: {integrity: sha512-jMo45tVNOA0t2GEIgOiroPHWkranirwNGdzElxjbNmNXhxniFHDISKzCvEljCEfU88syAwm9fi22CwmqwIbfag==}
engines: {node: '>=12.0.0'}
peerDependencies:
'@types/react': ^17.0.0 || ^18.0.0
react: ^17.0.0 || ^18.0.0
peerDependenciesMeta:
'@types/react':
optional: true
'@mui/styled-engine@5.15.14':
resolution: {integrity: sha512-RILkuVD8gY6PvjZjqnWhz8fu68dVkqhM5+jYWfB5yhlSQKg+2rHkmEwm75XIeAqI3qwOndK6zELK5H6Zxn4NHw==}
engines: {node: '>=12.0.0'}
@ -1031,6 +1105,19 @@ packages:
'@emotion/styled':
optional: true
'@mui/styled-engine@6.0.0-alpha.5':
resolution: {integrity: sha512-M+nvgL6V9JAnJTy9WJLXXWKfe5YnkIpzheXszsvwi0Yoig/er1DcUwhd7fiKhrgSzEhB9eKXA93fdzFdf1+mcQ==}
engines: {node: '>=12.0.0'}
peerDependencies:
'@emotion/react': ^11.4.1
'@emotion/styled': ^11.3.0
react: ^17.0.0 || ^18.0.0
peerDependenciesMeta:
'@emotion/react':
optional: true
'@emotion/styled':
optional: true
'@mui/system@5.15.15':
resolution: {integrity: sha512-aulox6N1dnu5PABsfxVGOZffDVmlxPOVgj56HrUnJE8MCSh8lOvvkd47cebIVQQYAjpwieXQXiDPj5pwM40jTQ==}
engines: {node: '>=12.0.0'}
@ -1047,6 +1134,22 @@ packages:
'@types/react':
optional: true
'@mui/system@6.0.0-dev.240424162023-9968b4889d':
resolution: {integrity: sha512-Y3yCFUHN1xMK62hJJBqzZb1YQvHNaHc7JUX01eU6QTPojtIbGMF2jCOP/EQw77/byahNbxeLoAIQx10F0IR3Rw==}
engines: {node: '>=12.0.0'}
peerDependencies:
'@emotion/react': ^11.5.0
'@emotion/styled': ^11.3.0
'@types/react': ^17.0.0 || ^18.0.0
react: ^17.0.0 || ^18.0.0
peerDependenciesMeta:
'@emotion/react':
optional: true
'@emotion/styled':
optional: true
'@types/react':
optional: true
'@mui/types@7.2.14':
resolution: {integrity: sha512-MZsBZ4q4HfzBsywtXgM1Ksj6HDThtiwmOKUXH1pKYISI9gAVXCNHNpo7TlGoGrBaYWZTdNoirIN7JsQcQUjmQQ==}
peerDependencies:
@ -1065,6 +1168,16 @@ packages:
'@types/react':
optional: true
'@mui/utils@6.0.0-alpha.5':
resolution: {integrity: sha512-E0MNO8pfIkxsF/Ql+W0J0t4uTgXsTjGHrKr3UhnyFRLqP+JFg5tCvGcSqL0bCDck60Kuc77JCYMr4wbJrhk3LA==}
engines: {node: '>=12.0.0'}
peerDependencies:
'@types/react': ^17.0.0 || ^18.0.0
react: ^17.0.0 || ^18.0.0
peerDependenciesMeta:
'@types/react':
optional: true
'@nodelib/fs.scandir@2.1.5':
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
engines: {node: '>= 8'}
@ -1359,9 +1472,23 @@ packages:
peerDependencies:
react: ^18.0.0
'@tanstack/react-table@8.17.3':
resolution: {integrity: sha512-5gwg5SvPD3lNAXPuJJz1fOCEZYk9/GeBFH3w/hCgnfyszOIzwkwgp5I7Q4MJtn0WECp84b5STQUDdmvGi8m3nA==}
engines: {node: '>=12'}
peerDependencies:
react: '>=16.8'
react-dom: '>=16.8'
'@tanstack/table-core@8.17.3':
resolution: {integrity: sha512-mPBodDGVL+fl6d90wUREepHa/7lhsghg2A3vFpakEhrhtbIlgNAZiMr7ccTgak5qbHqF14Fwy+W1yFWQt+WmYQ==}
engines: {node: '>=12'}
'@types/cacheable-request@6.0.3':
resolution: {integrity: sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==}
'@types/emscripten@1.39.12':
resolution: {integrity: sha512-AQImDBgudQfMqUBfrjZYilRxoHDzTBp+ejh+g1fY67eSMalwIKtBXofjpyI0JBgNpHGzxeGAR2QDya0wxW9zbA==}
'@types/estree@0.0.39':
resolution: {integrity: sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==}
@ -1407,6 +1534,9 @@ packages:
'@types/responselike@1.0.3':
resolution: {integrity: sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==}
'@types/sql.js@1.4.9':
resolution: {integrity: sha512-ep8b36RKHlgWPqjNG9ToUrPiwkhwh0AEzy883mO5Xnd+cL6VBH1EvSjBAAuxLUFF2Vn/moE3Me6v9E1Lo+48GQ==}
'@types/through@0.0.33':
resolution: {integrity: sha512-HsJ+z3QuETzP3cswwtzt2vEIiHBk/dCcHGhbmG5X3ecnwFD/lPrMpliGXxSCg03L9AhrdwA4Oz/qfspkDW+xGQ==}
@ -1429,6 +1559,17 @@ packages:
peerDependencies:
vite: ^4 || ^5
'@zlepper/rpc@0.0.10':
resolution: {integrity: sha512-NAcpr2KGrRIGFkkTB5Eniz3eYhYExY52IJu5V0dOsgBCSg5qcaqjpF71ue4lCMT10fJ/jzhexuFhvFlbZ8dQ5g==}
'@zlepper/web-worker-rpc@0.0.10':
resolution: {integrity: sha512-aqTgsHl9KwWGxE18MhKV8fWgsPorrPS/wapXjwijglPO+WOnN938az0bmxMRw1cgyXqj1dyD65jB8hm2A0RCVg==}
peerDependencies:
'@zlepper/rpc': ^0.0.10
absurd-sql@0.0.54:
resolution: {integrity: sha512-p+SWTtpRs2t3sXMLxkTyLRZkEzxTv/zG/Bl93wibegLZTGAHGk68SJMWslRWHBGh63ka/ePGTXGHh1117++45Q==}
acorn@8.11.3:
resolution: {integrity: sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==}
engines: {node: '>=0.4.0'}
@ -2278,6 +2419,9 @@ packages:
resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
engines: {node: '>= 8'}
microevent.ts@0.2.1:
resolution: {integrity: sha512-YaOQr4V70QzTy3sTRkBUa7+clmN4rMdKs9L5wCCxYjo8gknO/FXhcEX5Pot4IWtAdiZqhxN7vskoywQbAOAkDQ==}
micromatch@4.0.5:
resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==}
engines: {node: '>=8.6'}
@ -2618,6 +2762,9 @@ packages:
run-parallel@1.2.0:
resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
safari-14-idb-fix@1.0.6:
resolution: {integrity: sha512-oTEQOdMwRX+uCtWCKT1nx2gAeSdpr8elg/2gcaKUH00SJU2xWESfkx11nmXwTRHy7xfQoj1o4TTQvdmuBosTnA==}
safe-array-concat@1.1.2:
resolution: {integrity: sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==}
engines: {node: '>=0.4'}
@ -3055,6 +3202,9 @@ packages:
workbox-window@7.1.0:
resolution: {integrity: sha512-ZHeROyqR+AS5UPzholQRDttLFqGMwP0Np8MKWAdyxsDETxq3qOAyXvqessc3GniohG6e0mAqSQyKOHmT8zPF7g==}
worker-rpc@0.2.0:
resolution: {integrity: sha512-S74HnfAdmMlUYmr6+Lx6TmxvffM2vRZSk4RfI/Bxco4xZGw+FREzLRZhFxf8QIzI2/5NKNMn5+Pj69Bp+rweIg==}
wrappy@1.0.2:
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
@ -3997,6 +4147,10 @@ snapshots:
'@floating-ui/utils@0.2.2': {}
'@fontsource/inter@5.0.18': {}
'@jlongster/sql.js@1.6.7': {}
'@jridgewell/gen-mapping@0.3.5':
dependencies:
'@jridgewell/set-array': 1.2.1
@ -4033,8 +4187,24 @@ snapshots:
optionalDependencies:
'@types/react': 18.3.2
'@mui/base@5.0.0-beta.42(@types/react@18.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
'@babel/runtime': 7.24.5
'@floating-ui/react-dom': 2.0.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@mui/types': 7.2.14(@types/react@18.3.2)
'@mui/utils': 6.0.0-alpha.5(@types/react@18.3.2)(react@18.3.1)
'@popperjs/core': 2.11.8
clsx: 2.1.1
prop-types: 15.8.1
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
optionalDependencies:
'@types/react': 18.3.2
'@mui/core-downloads-tracker@5.15.17': {}
'@mui/core-downloads-tracker@6.0.0-dev.240424162023-9968b4889d': {}
'@mui/icons-material@5.15.17(@mui/material@5.15.17(@emotion/react@11.11.4(@types/react@18.3.2)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.2)(react@18.3.1))(@types/react@18.3.2)(react@18.3.1))(@types/react@18.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.2)(react@18.3.1)':
dependencies:
'@babel/runtime': 7.24.5
@ -4043,6 +4213,23 @@ snapshots:
optionalDependencies:
'@types/react': 18.3.2
'@mui/joy@5.0.0-beta.36(@emotion/react@11.11.4(@types/react@18.3.2)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.2)(react@18.3.1))(@types/react@18.3.2)(react@18.3.1))(@types/react@18.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
'@babel/runtime': 7.24.5
'@mui/base': 5.0.0-beta.42(@types/react@18.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@mui/core-downloads-tracker': 6.0.0-dev.240424162023-9968b4889d
'@mui/system': 6.0.0-dev.240424162023-9968b4889d(@emotion/react@11.11.4(@types/react@18.3.2)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.2)(react@18.3.1))(@types/react@18.3.2)(react@18.3.1))(@types/react@18.3.2)(react@18.3.1)
'@mui/types': 7.2.14(@types/react@18.3.2)
'@mui/utils': 6.0.0-alpha.5(@types/react@18.3.2)(react@18.3.1)
clsx: 2.1.1
prop-types: 15.8.1
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
optionalDependencies:
'@emotion/react': 11.11.4(@types/react@18.3.2)(react@18.3.1)
'@emotion/styled': 11.11.5(@emotion/react@11.11.4(@types/react@18.3.2)(react@18.3.1))(@types/react@18.3.2)(react@18.3.1)
'@types/react': 18.3.2
'@mui/material@5.15.17(@emotion/react@11.11.4(@types/react@18.3.2)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.2)(react@18.3.1))(@types/react@18.3.2)(react@18.3.1))(@types/react@18.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
'@babel/runtime': 7.24.5
@ -4073,6 +4260,15 @@ snapshots:
optionalDependencies:
'@types/react': 18.3.2
'@mui/private-theming@6.0.0-alpha.5(@types/react@18.3.2)(react@18.3.1)':
dependencies:
'@babel/runtime': 7.24.5
'@mui/utils': 6.0.0-alpha.5(@types/react@18.3.2)(react@18.3.1)
prop-types: 15.8.1
react: 18.3.1
optionalDependencies:
'@types/react': 18.3.2
'@mui/styled-engine@5.15.14(@emotion/react@11.11.4(@types/react@18.3.2)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.2)(react@18.3.1))(@types/react@18.3.2)(react@18.3.1))(react@18.3.1)':
dependencies:
'@babel/runtime': 7.24.5
@ -4084,6 +4280,17 @@ snapshots:
'@emotion/react': 11.11.4(@types/react@18.3.2)(react@18.3.1)
'@emotion/styled': 11.11.5(@emotion/react@11.11.4(@types/react@18.3.2)(react@18.3.1))(@types/react@18.3.2)(react@18.3.1)
'@mui/styled-engine@6.0.0-alpha.5(@emotion/react@11.11.4(@types/react@18.3.2)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.2)(react@18.3.1))(@types/react@18.3.2)(react@18.3.1))(react@18.3.1)':
dependencies:
'@babel/runtime': 7.24.5
'@emotion/cache': 11.11.0
csstype: 3.1.3
prop-types: 15.8.1
react: 18.3.1
optionalDependencies:
'@emotion/react': 11.11.4(@types/react@18.3.2)(react@18.3.1)
'@emotion/styled': 11.11.5(@emotion/react@11.11.4(@types/react@18.3.2)(react@18.3.1))(@types/react@18.3.2)(react@18.3.1)
'@mui/system@5.15.15(@emotion/react@11.11.4(@types/react@18.3.2)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.2)(react@18.3.1))(@types/react@18.3.2)(react@18.3.1))(@types/react@18.3.2)(react@18.3.1)':
dependencies:
'@babel/runtime': 7.24.5
@ -4100,6 +4307,22 @@ snapshots:
'@emotion/styled': 11.11.5(@emotion/react@11.11.4(@types/react@18.3.2)(react@18.3.1))(@types/react@18.3.2)(react@18.3.1)
'@types/react': 18.3.2
'@mui/system@6.0.0-dev.240424162023-9968b4889d(@emotion/react@11.11.4(@types/react@18.3.2)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.2)(react@18.3.1))(@types/react@18.3.2)(react@18.3.1))(@types/react@18.3.2)(react@18.3.1)':
dependencies:
'@babel/runtime': 7.24.5
'@mui/private-theming': 6.0.0-alpha.5(@types/react@18.3.2)(react@18.3.1)
'@mui/styled-engine': 6.0.0-alpha.5(@emotion/react@11.11.4(@types/react@18.3.2)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.2)(react@18.3.1))(@types/react@18.3.2)(react@18.3.1))(react@18.3.1)
'@mui/types': 7.2.14(@types/react@18.3.2)
'@mui/utils': 6.0.0-alpha.5(@types/react@18.3.2)(react@18.3.1)
clsx: 2.1.1
csstype: 3.1.3
prop-types: 15.8.1
react: 18.3.1
optionalDependencies:
'@emotion/react': 11.11.4(@types/react@18.3.2)(react@18.3.1)
'@emotion/styled': 11.11.5(@emotion/react@11.11.4(@types/react@18.3.2)(react@18.3.1))(@types/react@18.3.2)(react@18.3.1)
'@types/react': 18.3.2
'@mui/types@7.2.14(@types/react@18.3.2)':
optionalDependencies:
'@types/react': 18.3.2
@ -4114,6 +4337,16 @@ snapshots:
optionalDependencies:
'@types/react': 18.3.2
'@mui/utils@6.0.0-alpha.5(@types/react@18.3.2)(react@18.3.1)':
dependencies:
'@babel/runtime': 7.24.5
'@types/prop-types': 15.7.12
prop-types: 15.8.1
react: 18.3.1
react-is: 18.3.1
optionalDependencies:
'@types/react': 18.3.2
'@nodelib/fs.scandir@2.1.5':
dependencies:
'@nodelib/fs.stat': 2.0.5
@ -4360,6 +4593,14 @@ snapshots:
'@tanstack/query-core': 5.36.1
react: 18.3.1
'@tanstack/react-table@8.17.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
'@tanstack/table-core': 8.17.3
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
'@tanstack/table-core@8.17.3': {}
'@types/cacheable-request@6.0.3':
dependencies:
'@types/http-cache-semantics': 4.0.4
@ -4367,6 +4608,8 @@ snapshots:
'@types/node': 20.12.12
'@types/responselike': 1.0.3
'@types/emscripten@1.39.12': {}
'@types/estree@0.0.39': {}
'@types/estree@1.0.5': {}
@ -4413,6 +4656,11 @@ snapshots:
dependencies:
'@types/node': 20.12.12
'@types/sql.js@1.4.9':
dependencies:
'@types/emscripten': 1.39.12
'@types/node': 20.12.12
'@types/through@0.0.33':
dependencies:
'@types/node': 20.12.12
@ -4439,6 +4687,16 @@ snapshots:
transitivePeerDependencies:
- '@swc/helpers'
'@zlepper/rpc@0.0.10': {}
'@zlepper/web-worker-rpc@0.0.10(@zlepper/rpc@0.0.10)':
dependencies:
'@zlepper/rpc': 0.0.10
absurd-sql@0.0.54:
dependencies:
safari-14-idb-fix: 1.0.6
acorn@8.11.3: {}
agent-base@6.0.2:
@ -5360,6 +5618,8 @@ snapshots:
merge2@1.4.1: {}
microevent.ts@0.2.1: {}
micromatch@4.0.5:
dependencies:
braces: 3.0.2
@ -5716,6 +5976,8 @@ snapshots:
dependencies:
queue-microtask: 1.2.3
safari-14-idb-fix@1.0.6: {}
safe-array-concat@1.1.2:
dependencies:
call-bind: 1.0.7
@ -6279,6 +6541,10 @@ snapshots:
'@types/trusted-types': 2.0.7
workbox-core: 7.1.0
worker-rpc@0.2.0:
dependencies:
microevent.ts: 0.2.1
wrappy@1.0.2: {}
yallist@3.1.1: {}

View file

View file

@ -27,9 +27,22 @@ async function* mixToSongs(
async function* processSong(gen: AsyncGenerator<[string, string]>) {
for await (const [mix, song] of gen) {
const totalPath = join(stepchartsDir, mix, song);
const meta = await stat(totalPath);
if (!meta.isDirectory()) continue;
try {
const parsed = await parseSimfile(stepchartsDir, mix, song);
yield parsed;
for (const [name, chart] of Object.entries(parsed.charts)) {
yield {
difficulty: name,
artist: parsed.artist,
title: parsed.title.titleName,
titleRomanized: parsed.title.translitTitleName,
bannerFilename: parsed.bannerFilename,
};
}
// yield parsed;
} catch (e) {
console.error("failed on", mix, song, ":", e.message);
}
@ -45,3 +58,5 @@ for await (const songObj of outIter) {
writer.write(JSON.stringify(songObj));
writer.write("\n");
}
console.log("Done.");

View file

@ -19,7 +19,8 @@ import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { CHART_STORE_CREATION_EVENT, dbStatusAtom } from "./globals";
import NavBar from "./components/NavBar";
import WarningBars from "./components/WarningBars";
import { useAtom, useSetAtom } from "jotai";
import { useAtom, useAtomValue, useSetAtom } from "jotai";
import { dbClient, getDbClient } from "../lib/db/client";
const queryClient = new QueryClient();
@ -76,23 +77,23 @@ export function AppWrapper() {
}
export default function App() {
const setDbStatus = useSetAtom(dbStatusAtom);
useEffect(() => {
const worker = new ImportChartWorker();
worker.postMessage("start");
worker.onmessage = (evt) => {
console.log("got event from web worker", evt);
switch (evt.kind) {
case "dbIsBlocked": {
setDbStatus((db) => ({ ...db, status: "upgrading" }));
}
}
if (evt.kind === "chartStoreCreate") {
document.dispatchEvent(CHART_STORE_CREATION_EVENT);
}
};
});
(async () => {
// await getDbClient();
console.log("WTF?", dbClient);
console.log("a");
await dbClient.run(
"CREATE TABLE IF NOT EXISTS rows (lol INTEGER PRIMARY KEY AUTOINCREMENT , what STRING);",
);
console.log("b");
await dbClient.run("INSERT INTO rows (what) VALUES ('helloge')");
console.log("c");
const result = await dbClient.exec("SELECT * FROM rows");
console.log("d");
console.log("RESULT", result);
})();
}, [dbClient]);
return (
<>

View file

@ -0,0 +1,81 @@
import {
flexRender,
getCoreRowModel,
useReactTable,
} from "@tanstack/react-table";
export default function ChartsTable({ data }) {
console.log(data);
const table = useReactTable({
data,
columns: [
{
accessorKey: "bannerFilename",
cell: ({ cell, row }) => {
const value = cell.getValue();
return (
<div>
<img
style={{ width: "100px" }}
src={`${import.meta.env.BASE_URL}bannerImages/${value}`}
alt={`Banner image`}
/>
</div>
);
},
},
{ accessorKey: "difficulty" },
{ accessorKey: "artist" },
{ accessorKey: "title" },
],
getCoreRowModel: getCoreRowModel(),
});
return (
<table>
<thead>
{table.getHeaderGroups().map((headerGroup) => (
<tr key={headerGroup.id}>
{headerGroup.headers.map((header) => (
<th key={header.id}>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.header,
header.getContext(),
)}
</th>
))}
</tr>
))}
</thead>
<tbody>
{table.getRowModel().rows.map((row) => (
<tr key={row.id}>
{row.getVisibleCells().map((cell) => (
<td key={cell.id}>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</td>
))}
</tr>
))}
</tbody>
<tfoot>
{table.getFooterGroups().map((footerGroup) => (
<tr key={footerGroup.id}>
{footerGroup.headers.map((header) => (
<th key={header.id}>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.footer,
header.getContext(),
)}
</th>
))}
</tr>
))}
</tfoot>
</table>
);
}

View file

@ -1,10 +1,12 @@
.bar {
display: flex;
min-width: 0;
border-top: 1px solid lightgray;
}
.searchBar {
flex-grow: 1;
min-width: 0;
display: flex;
@ -13,4 +15,8 @@
border: none;
outline: none;
}
}
.searchEntry {
font-size: 20pt;
}

View file

@ -7,6 +7,7 @@ export default function NavBar() {
<nav className={styles.bar}>
<div className={styles.searchBar}>
<input
className={styles.searchEntry}
placeholder="Search charts..."
// biome-ignore lint/a11y/noAutofocus: this is the only one in the page
autoFocus

View file

@ -4,7 +4,7 @@ import WarningIcon from "@mui/icons-material/Warning";
import Chip from "./Chip";
import { dbStatusAtom } from "../globals";
import { useAtom, useAtomValue } from "jotai";
import { ReactNode } from "react";
import type { ReactNode } from "react";
export default function WarningBars() {
return (
@ -42,7 +42,7 @@ function StatusBar() {
const statuses = [];
statuses.push(
<Chip>
<Chip key="lol">
db: <DbStatusChip />
</Chip>,
);

View file

@ -1,6 +1,7 @@
import App from "./App";
import { createRoot } from "react-dom/client";
import { StrictMode } from "react";
import "@fontsource/inter";
import "./global.scss";
// biome-ignore lint/style/noNonNullAssertion: don't care

View file

@ -1,5 +1,7 @@
import { useQuery, useQueryClient } from "@tanstack/react-query";
import { APP_DATA_VERSION, CHART_STORE_CREATION_EVENT } from "../globals";
import { useReactTable } from "@tanstack/react-table";
import ChartsTable from "../components/ChartsTable";
function openDb(): Promise<IDBOpenDBRequest> {
return new Promise((resolve) => {
@ -52,10 +54,7 @@ export default function Charts() {
if (fetchChartsQuery.isSuccess) {
inner = (
<div>
{fetchChartsQuery.data.map((chart) => (
// <div>{JSON.stringify(Object.keys(chart))}</div>
<div>{JSON.stringify(chart.title)}</div>
))}
<ChartsTable data={fetchChartsQuery.data} />
</div>
);
}

View file

@ -20,6 +20,7 @@ export default defineConfig(({ mode }) => ({
manifest: {
icons: [{ src: "pwa-192x192.png", purpose: "any", sizes: "192x192" }],
},
includeAssets: ["public/bannerImages/*"],
}),
basicSsl({