lingo-free.openapps.xyz

Source

This app’s code and revision history are public. Download a copy or explore the files below.

Public
The app’s code and revision history are public.
Owned app · rev 8af89d2cc9f8
Run it yourself
Take the whole tree and run it on your own machine. The download needs no account. Cloning needs a sign-in.
Download .zip
bounded clone 7efc97d0dde0b2a3e729ccf9
A copy you download is not bounded. The guarantees this app runs under - declared egress, proven invariants, writes that fail closed - live in the runtime serving this page, not in the files. What you get is auditable source you can read and run, with no runtime guarantees attached.
← All files
src/lib/srs.ts
// Spaced repetition (SM-2): every word and sentence a learner has met carries a small card that says
// when to see it again. Right answers push the next review further out, mistakes bring it back tomorrow.

/** [due day, interval in days, ease x100, reps in a row, lapses, last seen ms] */
export type Card = [number, number, number, number, number, number];

/** Today as a day number in the learner's own time zone. */
export function today(now = Date.now()): number {
  return Math.floor((now - new Date(now).getTimezoneOffset() * 60000) / 86400000);
}

export function newCard(day = today()): Card {
  return [day, 0, 250, 0, 0, Date.now()];
}

/** Applies one answer. q is 5 (easy), 4 (right), 3 (right after a retry) or 2 (wrong). */
export function review(card: Card | undefined, q: number, day = today()): Card {
  let [, interval, ease, reps, lapses] = card || newCard(day);
  if (q >= 3) {
    interval = reps === 0 ? 1 : reps === 1 ? 4 : Math.round(interval * (ease / 100));
    reps += 1;
  } else {
    reps = 0;
    interval = 1;
    lapses += 1;
  }
  ease = Math.max(130, Math.round(ease + 100 * (0.1 - (5 - q) * (0.08 + (5 - q) * 0.02))));
  return [day + interval, interval, ease, reps, lapses, Date.now()];
}

/** A card for something the learner already showed they know (a passed jump test). */
export function knownCard(day = today()): Card {
  return [day + 6, 6, 250, 2, 0, Date.now()];
}

/** 0 to 4 bars: how well a word is known right now. */
export function strength(card: Card | undefined, day = today()): number {
  if (!card) return 0;
  const [due, interval] = card;
  const level = interval >= 21 ? 4 : interval >= 7 ? 3 : interval >= 3 ? 2 : 1;
  return Math.max(1, due < day ? level - 1 : level);
}

/** Lower is weaker: overdue first, then hard ones. */
export function weakness(card: Card, day = today()): number {
  const [due, interval, ease, , lapses] = card;
  return (due - day) / Math.max(1, interval) + ease / 250 - lapses * 0.3;
}

Change history

Point OpenApps links to openapps.free
8af89d2cc9 · 17 hours ago
Bind agent address in policy
0018350275 · 19 hours ago
Use OpenApps service URLs and add contribution strip
805365daf4 · 19 hours ago
Bounded source sync
3f2fb3b265 · 2 days ago
Bounded source sync
7dcb580324 · 2 days ago
Bounded source sync
dcd05c0944 · 2 days ago