aboutsummaryrefslogtreecommitdiff
path: root/src/lib
diff options
context:
space:
mode:
authorMartin Kagamino Lehoux <martin@lehoux.net>2026-09-25 09:10:31 +0200
committerMartin Kagamino Lehoux <martin@lehoux.net>2026-09-25 09:10:31 +0200
commit760a61001edafb462ca9c7f5bc8013f31fb15a5c (patch)
tree50f906de9ddaefb7362d444937f7ff4177ee2a7a /src/lib
parente5e1482b1f5aea4a50de44801c81c81750042201 (diff)
Compress comments to terse single lines within line width
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/components/HealthChart.svelte4
-rw-r--r--src/lib/server/db/schema.ts3
-rw-r--r--src/lib/server/services/grocery-actions.ts6
-rw-r--r--src/lib/server/services/routine-actions.ts11
-rw-r--r--src/lib/server/services/routines.ts19
-rw-r--r--src/lib/server/services/validation.ts2
-rw-r--r--src/lib/shared/routine-metrics.test.ts2
-rw-r--r--src/lib/shared/routine-metrics.ts30
-rw-r--r--src/lib/shared/temporal.ts3
9 files changed, 19 insertions, 61 deletions
diff --git a/src/lib/components/HealthChart.svelte b/src/lib/components/HealthChart.svelte
index c6121e3..6de0891 100644
--- a/src/lib/components/HealthChart.svelte
+++ b/src/lib/components/HealthChart.svelte
@@ -20,9 +20,7 @@
markers.map((t) => ({ date: new Date(t), health: 100 })),
);
- // Up to 5 x ticks spread across the series, skipping repeats (two samples
- // the same day would print the same label twice). Dates stay Date objects
- // because LayerChart's time scale consumes them; labels go through Temporal.
+ // Up to 5 x ticks across the series, skipping same-day repeats.
const xTicks = $derived.by(() => {
if (data.length < 2) return undefined;
const step = Math.max(1, Math.floor((data.length - 1) / 4));
diff --git a/src/lib/server/db/schema.ts b/src/lib/server/db/schema.ts
index c5daac5..a7fcd8f 100644
--- a/src/lib/server/db/schema.ts
+++ b/src/lib/server/db/schema.ts
@@ -40,8 +40,7 @@ export const projects = sqliteTable('projects', {
archived: integer('archived', { mode: 'boolean' }).notNull().default(false),
});
-// Append-only snapshot table: an edit inserts a new row with version+1, the
-// current routine is the row with the max version for its id.
+// Append-only snapshot table: current routine = max version per id.
export const routines = sqliteTable(
'routines',
{
diff --git a/src/lib/server/services/grocery-actions.ts b/src/lib/server/services/grocery-actions.ts
index c8974e9..2138f2d 100644
--- a/src/lib/server/services/grocery-actions.ts
+++ b/src/lib/server/services/grocery-actions.ts
@@ -17,11 +17,7 @@ export async function toggleGroceryAction(request: Request) {
export type DeleteRowResult =
ActionFailure<{ error: string }> | { success: boolean; type?: 'delete' };
-/**
- * Shared 'delete a row by its id form field' action, used by the category
- * list and the reference list (both submit one form whose buttons carry the
- * row id).
- */
+/** Shared 'delete by id form field' action for category and reference lists. */
export async function deleteByIdAction(
request: Request,
deleteFn: (id: number) => Promise<unknown>,
diff --git a/src/lib/server/services/routine-actions.ts b/src/lib/server/services/routine-actions.ts
index 64d0a1b..28a25cc 100644
--- a/src/lib/server/services/routine-actions.ts
+++ b/src/lib/server/services/routine-actions.ts
@@ -2,13 +2,7 @@ import { fail, type ActionFailure } from '@sveltejs/kit';
import { archive, complete, edit, loadCurrent } from './routines';
import { parseFormString, parseFrequencyDays } from './validation';
-/**
- * Shared implementations of the routine edit/complete form actions, used by
- * both the project dashboard (/projects/[id]) and the routine page
- * (/projects/[projectId]/routine/[routineId]). Guarding and failure messages
- * are the strictest common denominator: a missing or archived routine is
- * rejected before any write.
- */
+/** Shared routine edit/complete actions; rejects missing/archived routines. */
export type EditRoutineResult =
ActionFailure<{ error: string }> | { success: boolean; type?: 'edit' };
@@ -38,8 +32,7 @@ export async function editRoutineAction(
return fail(400, { error: 'Fréquence invalide' });
}
- // The dashboard row form has no description field; only touch it when the
- // form actually sends one, so list edits never wipe a description.
+ // Only touch description when the form sends one; list edits never wipe it.
const fields: { name: string; frequencyDays: number; description?: string } =
{ name, frequencyDays };
const description = data.get('description');
diff --git a/src/lib/server/services/routines.ts b/src/lib/server/services/routines.ts
index a29872a..57611d6 100644
--- a/src/lib/server/services/routines.ts
+++ b/src/lib/server/services/routines.ts
@@ -11,8 +11,7 @@ import {
type HealthPoint,
} from '$lib/shared/routine-metrics';
-// ponytail: next routine id via MAX(id)+1, safe enough for a single-user app;
-// switch to a dedicated id table or transaction if concurrent writes appear.
+// ponytail: next routine id via MAX(id)+1; fine while single-user.
export async function nextRoutineId(db: AppDatabase): Promise<number> {
const [{ maxId }] = await db
@@ -44,12 +43,7 @@ export async function add(
return inserted;
}
-/**
- * Edit = insert a new snapshot row with version+1; history stays intact.
- * Only the given fields change; the others are copied from the current
- * snapshot so a name/frequency edit never touches the description and
- * vice-versa.
- */
+/** Edit = insert a version+1 snapshot, copying unchanged fields forward. */
export async function edit(
id: number,
fields: Partial<Pick<Routine, 'name' | 'frequencyDays' | 'description'>>,
@@ -117,8 +111,7 @@ export async function loadArchived(
.from(routines)
.where(eq(routines.projectId, projectId));
- // Keep only the max-version row per routine id (same rule as the
- // dashboard): archived = its current snapshot is archived.
+ // Max-version row per routine id; archived = max snapshot archived.
const current = new Map<number, Routine>();
for (const r of versions) {
const existing = current.get(r.id);
@@ -127,8 +120,7 @@ export async function loadArchived(
return (
[...current.values()]
.filter((r) => r.archived)
- // Legacy rows archived before archived_at existed fall back to their
- // creation date.
+ // Rows archived before archived_at existed fall back to creation date.
.sort((a, b) => ((a.archivedAt ?? '') < (b.archivedAt ?? '') ? 1 : -1))
.map((r) => ({
id: r.id,
@@ -218,8 +210,7 @@ export async function loadProjectDashboard(
.from(routines)
.where(eq(routines.projectId, projectId));
- // Keep only the max-version row per routine id, then drop archived ones:
- // an archived routine's max version is archived, so it disappears.
+ // Max-version row per routine id, then drop archived ones.
const current = new Map<number, Routine>();
for (const r of versions) {
const existing = current.get(r.id);
diff --git a/src/lib/server/services/validation.ts b/src/lib/server/services/validation.ts
index 69820ea..b0a515c 100644
--- a/src/lib/server/services/validation.ts
+++ b/src/lib/server/services/validation.ts
@@ -45,7 +45,7 @@ export function parseOptionalNumber(
return n;
}
-/** Parses an interval like '2w' into days; throws when the format is invalid. */
+/** Parses an interval like '2w' into days; throws on invalid format. */
export function parseFrequencyDays(value: FormDataEntryValue | null): number {
const days = parseIntervalToDays(value?.toString() ?? '');
if (days === null) {
diff --git a/src/lib/shared/routine-metrics.test.ts b/src/lib/shared/routine-metrics.test.ts
index 64b5da9..5bc0a78 100644
--- a/src/lib/shared/routine-metrics.test.ts
+++ b/src/lib/shared/routine-metrics.test.ts
@@ -49,7 +49,7 @@ describe('computeRoutineState', () => {
it('is due the calendar day after completion for a daily routine', () => {
const daily = { ...routine, frequencyDays: 1 };
const doneAt = '2026-09-07T20:00:00.000Z'; // yesterday evening
- const now = Temporal.Instant.from('2026-09-08T08:00:00.000Z'); // this morning, <24h later
+ const now = Temporal.Instant.from('2026-09-08T08:00:00.000Z'); // <24h later
const state = computeRoutineState(daily, [{ routineId: 1, doneAt }], now);
expect(state.status).toBe('due');
});
diff --git a/src/lib/shared/routine-metrics.ts b/src/lib/shared/routine-metrics.ts
index 7758f27..4cff240 100644
--- a/src/lib/shared/routine-metrics.ts
+++ b/src/lib/shared/routine-metrics.ts
@@ -1,7 +1,4 @@
-// Pure routine metrics, shared by server services and UI. No db access.
-// All timestamps are ISO strings; calendar math uses Temporal in UTC: due
-// dates anchor on UTC calendar days, not exact 24h intervals: a daily
-// routine done yesterday is due today, whatever the time of day.
+// Pure routine metrics shared by server and UI; ISO timestamps, UTC calendar.
const UTC = 'UTC';
@@ -78,7 +75,7 @@ export interface RoutineState {
/** Time past due, in units of the routine's own frequency (0 if not due). */
overdueRatio: number;
status: RoutineStatus;
- /** 100 on schedule, 50 when a full period overdue, 0 at two periods, floor at 0. */
+ /** 100 on schedule, 50 one period overdue, 0 at two periods. */
health: number;
}
@@ -90,10 +87,7 @@ export function computeRoutineState(
const lastDone = maxInstant(parseCompletions(routine.id, completions));
const reference = lastDone ?? parseInstant(routine.createdAt, now);
const period = periodMs(routine.frequencyDays);
- // Calendar-day anchor: the period starts at midnight of the reference day,
- // so any new calendar day past the frequency flips the routine to due.
- // A never-done routine has no completed period: its creation day is its
- // due date, so it ages exactly like a done one whose anchor is creation.
+ // Calendar-day anchor: the period starts at the reference day's midnight.
const due = reference
.toZonedDateTimeISO(UTC)
.startOfDay()
@@ -138,7 +132,7 @@ export interface HealthPoint {
health: number;
}
-/** Health at instant t given the reference anchor (creation or last completion). */
+/** Health at t given the reference anchor (creation or last completion). */
function healthAtPoint(
reference: Temporal.Instant,
frequencyDays: number,
@@ -155,14 +149,7 @@ function healthAtPoint(
return Math.max(0, Math.round(100 - Math.max(0, rawRatio) * 50));
}
-/**
- * Daily health samples from the routine's creation to now: 100 right after a
- * completion, then a linear decay to 0 once two full periods are overdue
- * (sawtooth).
- * Each sample anchors on the latest completion at or before it (or creation,
- * which is then itself the due date for a never-done routine).
- * Uses the routine's current frequency across the whole history.
- */
+/** Daily health samples: 100 at completion, decaying while overdue. */
export function computeHealthSeries(
routine: RoutineInput,
completions: CompletionInput[],
@@ -210,12 +197,7 @@ export function computeHealthSeries(
return points;
}
-/**
- * Project health series: daily mean of per-routine health across the routines
- * alive that day (createdAt <= t). Same sawtooth as the per-routine series,
- * so the project curve ends at the current health score. Empty when no
- * routines.
- */
+/** Project health: daily mean of alive routines' health, same sawtooth. */
export function computeProjectHealthSeries(
routines: RoutineInput[],
completions: CompletionInput[],
diff --git a/src/lib/shared/temporal.ts b/src/lib/shared/temporal.ts
index 7097ba3..103f209 100644
--- a/src/lib/shared/temporal.ts
+++ b/src/lib/shared/temporal.ts
@@ -1,5 +1,4 @@
-// Shared Temporal display helpers. Storage and logic stay in ISO instants;
-// these format one for the viewer in their own timezone.
+// Temporal display helpers: format ISO instants for the viewer's timezone.
/** Format an ISO instant in the viewer's timezone. */
export function formatInstant(