From 62580c93a5a1d3c9be8b9e3b533586a6701654ed Mon Sep 17 00:00:00 2001
From: Claas
Date: Sat, 11 Jul 2026 16:36:00 +0200
Subject: [PATCH] Format using oxfmt
---
CLAUDE.md | 1 +
package.json | 2 +-
src/App.tsx | 8 +++-----
src/components/Markdown.tsx | 6 ++----
src/db/cards.ts | 4 ++--
src/db/client.ts | 4 ++--
src/db/connection.ts | 4 ++--
src/db/decks.ts | 3 ++-
src/db/reviews.ts | 9 +++++----
src/lib/db-file.ts | 9 ++++-----
src/lib/deck-json.ts | 23 ++++++++++++++++++-----
src/lib/time.ts | 5 ++---
src/pages/DeckPage.tsx | 13 +++++++++----
src/pages/SettingsPage.tsx | 4 +---
src/pages/StatsPage.tsx | 5 ++++-
src/pages/StudyPage.tsx | 3 +--
src/srs/scheduler.ts | 7 +------
tsconfig.json | 5 +----
18 files changed, 61 insertions(+), 54 deletions(-)
diff --git a/CLAUDE.md b/CLAUDE.md
index 4c47bbc..cce2cf2 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -22,6 +22,7 @@ Always use **pnpm** (never npm/npx; use `pnpm dlx` instead of npx).
**Storage:** `@tursodatabase/database-wasm` — SQLite in the browser, persisted in OPFS. It needs SharedArrayBuffer, so COOP/COEP headers are mandatory in dev AND production (`vite.config.ts` sets them for dev/preview; `public/_headers` for Netlify/Cloudflare; plain GitHub Pages cannot host this app). Only one tab can hold the database; `App.tsx`'s ErrorBoundary shows the multi-tab error screen. Import from `@tursodatabase/database-wasm/vite` (dev-server workaround baked into the export map).
**Layering** (UI → db, with srs as pure functions in between):
+
- `src/db/client.ts` — lazy connection singleton (`getDb`) + `closeDb` (used around OPFS file import/export). Runs migrations on open.
- `src/db/migrations.ts` — append-only SQL migrations, tracked via `PRAGMA user_version`. Cascading deletes happen in repository code, not via FK enforcement.
- `src/db/{decks,cards,reviews}.ts` — repository functions. All take a `DbConnection` parameter so tests can inject `@tursodatabase/database` (the Node build with an identical async API — this is why tests run in plain Node, no browser needed).
diff --git a/package.json b/package.json
index 0103f1a..5d67438 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "flashcut",
- "private": true,
"version": "0.0.0",
+ "private": true,
"type": "module",
"scripts": {
"dev": "vite",
diff --git a/src/App.tsx b/src/App.tsx
index 0178fa5..7b4a477 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -1,8 +1,8 @@
import { A } from "@solidjs/router";
import { ErrorBoundary, type ParentProps, Show, Suspense } from "solid-js";
-import { colorScheme, toggleColorScheme } from "./stores/theme";
import { btnGhost, btnPrimary } from "./lib/ui";
+import { colorScheme, toggleColorScheme } from "./stores/theme";
function navLink(active?: boolean) {
return `rounded-lg px-3 py-1.5 text-sm font-medium transition-colors hover:bg-stone-200 dark:hover:bg-stone-800 ${
@@ -19,7 +19,7 @@ function ErrorScreen(props: { error: unknown; reset: () => void }) {
The local database could not be opened. This usually means Flashcut is already open in
another tab — the database can only be used by one tab at a time.
-
{message()}
+
{message()}
@@ -55,9 +55,7 @@ export default function App(props: ParentProps) {
}>
- Loading…}
- >
+ Loading…}>
{props.children}
diff --git a/src/components/Markdown.tsx b/src/components/Markdown.tsx
index dbfbe66..544d04f 100644
--- a/src/components/Markdown.tsx
+++ b/src/components/Markdown.tsx
@@ -3,12 +3,10 @@ import { marked } from "marked";
import { createMemo } from "solid-js";
export function Markdown(props: { source: string; class?: string }) {
- const html = createMemo(() =>
- DOMPurify.sanitize(marked.parse(props.source, { async: false })),
- );
+ const html = createMemo(() => DOMPurify.sanitize(marked.parse(props.source, { async: false })));
return (
);
diff --git a/src/db/cards.ts b/src/db/cards.ts
index 5c672c8..8b4085a 100644
--- a/src/db/cards.ts
+++ b/src/db/cards.ts
@@ -127,8 +127,8 @@ export async function listCards(db: DbConnection, deckId: number): Promise | undefined;
/**
- * Lazily opens the OPFS-backed database. Rejects (and stays rejected for
- * retry) when another tab already holds the file lock.
+ * Lazily opens the OPFS-backed database. Rejects (and stays rejected for retry) when another tab
+ * already holds the file lock.
*/
export function getDb(): Promise {
dbPromise ??= open().catch((error: unknown) => {
diff --git a/src/db/connection.ts b/src/db/connection.ts
index 79b30b4..a5b17d6 100644
--- a/src/db/connection.ts
+++ b/src/db/connection.ts
@@ -3,8 +3,8 @@ export type Row = Record;
/**
* The subset of the Turso database API used by the repositories. Both
- * `@tursodatabase/database-wasm` (browser) and `@tursodatabase/database`
- * (Node, used in tests) satisfy this interface.
+ * `@tursodatabase/database-wasm` (browser) and `@tursodatabase/database` (Node, used in tests)
+ * satisfy this interface.
*/
export interface DbConnection {
exec(sql: string): Promise;
diff --git a/src/db/decks.ts b/src/db/decks.ts
index 74a292f..412e9aa 100644
--- a/src/db/decks.ts
+++ b/src/db/decks.ts
@@ -1,6 +1,7 @@
-import { type DbConnection, type Row, withTransaction } from "./connection";
import { State } from "ts-fsrs";
+import { type DbConnection, type Row, withTransaction } from "./connection";
+
export interface Deck {
id: number;
name: string;
diff --git a/src/db/reviews.ts b/src/db/reviews.ts
index f5131bb..5016f3a 100644
--- a/src/db/reviews.ts
+++ b/src/db/reviews.ts
@@ -46,7 +46,10 @@ export async function recordReview(
});
}
-/** Instants of all reviews since `sinceIso`, ascending. Day-bucketing happens in the UI (user timezone). */
+/**
+ * Instants of all reviews since `sinceIso`, ascending. Day-bucketing happens in the UI (user
+ * timezone).
+ */
export async function reviewTimesSince(db: DbConnection, sinceIso: string): Promise {
const rows = await db.all(
"SELECT review FROM review_logs WHERE review >= ? ORDER BY review",
@@ -62,8 +65,6 @@ export async function totalReviewCount(db: DbConnection): Promise {
/** Due instants of all scheduled (non-new) cards, for the forecast chart. */
export async function scheduledDueTimes(db: DbConnection): Promise {
- const rows = await db.all(
- `SELECT due FROM cards WHERE state != ${State.New} ORDER BY due`,
- );
+ const rows = await db.all(`SELECT due FROM cards WHERE state != ${State.New} ORDER BY due`);
return rows.map((row) => String(row["due"]));
}
diff --git a/src/lib/db-file.ts b/src/lib/db-file.ts
index 059411a..2ddd8cf 100644
--- a/src/lib/db-file.ts
+++ b/src/lib/db-file.ts
@@ -2,9 +2,8 @@ import { closeDb, DB_FILE, getDb } from "../db/client";
import { downloadBlob } from "./download";
/**
- * Downloads the raw SQLite file from OPFS. The WAL is checkpointed and the
- * connection closed first so the copied file is complete and consistent; the
- * next query lazily reopens the database.
+ * Downloads the raw SQLite file from OPFS. The WAL is checkpointed and the connection closed first
+ * so the copied file is complete and consistent; the next query lazily reopens the database.
*/
export async function exportDatabaseFile(): Promise {
const db = await getDb();
@@ -19,8 +18,8 @@ export async function exportDatabaseFile(): Promise {
}
/**
- * Replaces the OPFS database with the given SQLite file and reloads the app.
- * Destructive — callers must confirm with the user first.
+ * Replaces the OPFS database with the given SQLite file and reloads the app. Destructive — callers
+ * must confirm with the user first.
*/
export async function importDatabaseFile(file: File): Promise {
const bytes = await file.arrayBuffer();
diff --git a/src/lib/deck-json.ts b/src/lib/deck-json.ts
index 7a789bc..ec2ff86 100644
--- a/src/lib/deck-json.ts
+++ b/src/lib/deck-json.ts
@@ -1,9 +1,10 @@
+import { State } from "ts-fsrs";
+
import { type FsrsColumns, createCard, listCards } from "../db/cards";
import { type DbConnection, withTransaction } from "../db/connection";
import { createDeck, getDeck } from "../db/decks";
import { newCardFsrs } from "../srs/scheduler";
import { isoNow } from "./time";
-import { State } from "ts-fsrs";
export interface DeckExport {
version: 1;
@@ -39,7 +40,10 @@ export async function exportDeckJson(db: DbConnection, deckId: number): Promise<
};
}
-/** Creates a new deck from parsed JSON; cards without valid FSRS state start as new. Returns the deck id. */
+/**
+ * Creates a new deck from parsed JSON; cards without valid FSRS state start as new. Returns the
+ * deck id.
+ */
export async function importDeckJson(db: DbConnection, data: unknown): Promise {
const parsed = parseDeckExport(data);
const now = isoNow();
@@ -68,7 +72,11 @@ export function parseDeckExport(data: unknown): DeckExport {
throw new Error("Not a valid Flashcut deck file: unsupported version");
}
const deck = record["deck"];
- if (typeof deck !== "object" || deck === null || typeof (deck as Record)["name"] !== "string") {
+ if (
+ typeof deck !== "object" ||
+ deck === null ||
+ typeof (deck as Record)["name"] !== "string"
+ ) {
throw new Error("Not a valid Flashcut deck file: missing deck name");
}
const deckRecord = deck as Record;
@@ -77,11 +85,16 @@ export function parseDeckExport(data: unknown): DeckExport {
throw new Error("Not a valid Flashcut deck file: missing cards array");
}
const cards = cardsRaw.map((item, index) => {
- const cardRecord = (typeof item === "object" && item !== null ? item : {}) as Record;
+ const cardRecord = (typeof item === "object" && item !== null ? item : {}) as Record<
+ string,
+ unknown
+ >;
const front = cardRecord["front"];
const back = cardRecord["back"];
if (typeof front !== "string" || typeof back !== "string") {
- throw new Error(`Not a valid Flashcut deck file: card ${index + 1} needs front and back strings`);
+ throw new Error(
+ `Not a valid Flashcut deck file: card ${index + 1} needs front and back strings`,
+ );
}
const fsrs = parseFsrs(cardRecord["fsrs"]);
return fsrs ? { front, back, fsrs } : { front, back };
diff --git a/src/lib/time.ts b/src/lib/time.ts
index a1580fe..77addcb 100644
--- a/src/lib/time.ts
+++ b/src/lib/time.ts
@@ -1,7 +1,6 @@
/**
- * Stored timestamps are ISO-8601 UTC with exactly 3 fractional digits
- * (matching Date#toISOString), so lexicographic order == chronological order
- * in SQL string comparisons.
+ * Stored timestamps are ISO-8601 UTC with exactly 3 fractional digits (matching Date#toISOString),
+ * so lexicographic order == chronological order in SQL string comparisons.
*/
export function toIso(instant: Temporal.Instant): string {
return instant.toString({ fractionalSecondDigits: 3 });
diff --git a/src/pages/DeckPage.tsx b/src/pages/DeckPage.tsx
index 4efc65e..67e38f8 100644
--- a/src/pages/DeckPage.tsx
+++ b/src/pages/DeckPage.tsx
@@ -42,7 +42,14 @@ export default function DeckPage() {
const db = await getDb();
const id = editingId();
if (id == null) {
- await createCard(db, deckId(), front(), back(), isoNow(), newCardFsrs(Temporal.Now.instant()));
+ await createCard(
+ db,
+ deckId(),
+ front(),
+ back(),
+ isoNow(),
+ newCardFsrs(Temporal.Now.instant()),
+ );
} else {
await updateCardContent(db, id, front(), back());
}
@@ -77,9 +84,7 @@ export default function DeckPage() {