v3: comments say what the code can't — the documentation pass wanted JSDoc on every export and every field of an exported interface, which filled the code with restatements ("The dump.", "Whether it is loading.", "The props of X.", "Renders the X.") and module docs that told a reader nothing ("The hot and new feeds, and the props every main feed of the index takes."); some were wrong as well as vague. Every comment in api/, shared/ and src/ is rewritten under one rule, now stated in the README's "Comments" section: every module and export still has a JSDoc, and it gives context (who uses it and where), usage (what a caller must know: a precondition, what it leaves to the caller, a side effect) or its place in the chain (where its input comes from, where its output goes); a field, parameter, private function or local gets one only when it adds to its parent's, and context about a whole type goes on the type; a type's doc is phrased from the value's side ("Returned to Y by X when Z", "Passed to Y by X"); full sentences, as short as they can be, no label-colon-list; // goes wherever the code isn't obvious and says why, and the section banners and narration go. Every claim was checked against the code, which turned up some forty comments that were wrong: DumpCard's claimed it was only used by the hot and new feeds, the WebSocket welcome claimed to be a socket's first message (presence comes first), UpdateUserRequest said null removes a description (it keeps it), deletedDumpIds leaves out that it also holds dumps made private, THEME_COLOR claimed to mirror index.html's, and the like. The routers' JSDoc now says where they are mounted and what becomes of a thrown APIException. doc-lint.ts lets a field without a doc through; a module or export without one still fails. No code changes; 204 backend and 628 frontend tests pass, and doc:lint, deno check, deno lint and tsc are clean.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,35 +1,24 @@
|
||||
/**
|
||||
* Color and lettering for generated thumbnail placeholders.
|
||||
*
|
||||
* The tint is never painted on its own — it's mixed into the theme's surface
|
||||
* color by CSS (`--thumb-tint`), so contrast against `--color-text` holds for
|
||||
* any site color, in every style, in both light and dark.
|
||||
* Picks the colour and the letter of `ThumbnailPlaceholder`. The CSS mixes the
|
||||
* tint into the theme's surface (`--thumb-tint`) rather than painting it alone,
|
||||
* so the text keeps its contrast whatever the site's colour and the theme.
|
||||
*
|
||||
* @module
|
||||
*/
|
||||
|
||||
/**
|
||||
* Color and lettering for generated thumbnail placeholders.
|
||||
*
|
||||
* The tint is never painted on its own — it's mixed into the theme's surface
|
||||
* color by CSS (`--thumb-tint`), so contrast against `--color-text` holds for
|
||||
* any site color, in every style, in both light and dark.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Checks whether `value` is a `#rrggbb` color. `accentColor` comes from a
|
||||
* dumped page's `theme-color`, so it's attacker-controlled. Custom properties
|
||||
* bypass CSS value parsing and get substituted verbatim wherever the var is
|
||||
* used, so the canonical form is re-checked here before it ever reaches a style
|
||||
* object.
|
||||
* Checks whether `value` is a lowercase `#rrggbb` colour. An `accentColor`
|
||||
* comes from a dumped page's `theme-color`, which anyone controls, and a custom
|
||||
* property is substituted verbatim wherever it is used, unparsed. So the form
|
||||
* is checked here before it reaches a style.
|
||||
*/
|
||||
export function isSafeHex(value: string | undefined): value is string {
|
||||
return !!value && /^#[0-9a-f]{6}$/.test(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the FNV-1a hash of `seed` — small, stable, and well spread across the
|
||||
* hue circle.
|
||||
* Returns the FNV-1a hash of `seed`, which is small, stable, and well spread
|
||||
* across the hue circle.
|
||||
*/
|
||||
function hashString(seed: string): number {
|
||||
let hash = 0x811c9dc5;
|
||||
@@ -41,13 +30,13 @@ function hashString(seed: string): number {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a stable hue for `seed`, so the same site always gets the same tint.
|
||||
* Returns a colour of a hue drawn from `seed`, so that the same site always
|
||||
* gets the same tint.
|
||||
*/
|
||||
export function hueFromString(seed: string): string {
|
||||
return `hsl(${hashString(seed) % 360} 55% 55%)`;
|
||||
}
|
||||
|
||||
/** Returns the hostname of `url` without `www.`, undefined when it has none. */
|
||||
function hostnameOf(url: string | undefined): string | undefined {
|
||||
if (!url) return undefined;
|
||||
try {
|
||||
@@ -58,10 +47,9 @@ function hostnameOf(url: string | undefined): string | undefined {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the page's own brand color when it declared a usable one, otherwise a
|
||||
* hue derived from its hostname — so every card gets a tint, including rows
|
||||
* saved before accent extraction existed. File dumps have no hostname to hash
|
||||
* and pass `seed` (their filename) instead.
|
||||
* Returns the page's own colour when it declared a valid one, or else a hue
|
||||
* drawn from `seed` or the hostname of `url`, so that every card gets a tint,
|
||||
* even for a dump saved before colours were extracted.
|
||||
*/
|
||||
export function tintFor(
|
||||
{ accentColor, url, seed }: {
|
||||
@@ -75,8 +63,8 @@ export function tintFor(
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the letter drawn when there's no favicon to show: the first letter or
|
||||
* digit of `siteName`, else of the hostname of `url`.
|
||||
* Returns the letter drawn when there's no favicon to show, the first letter or
|
||||
* digit of `siteName`, or else of the hostname of `url`, or "?".
|
||||
*/
|
||||
export function initialsFor(
|
||||
siteName: string | undefined,
|
||||
|
||||
Reference in New Issue
Block a user