Skip to content

Recap slides

The year in review is built from what each integration offers. Return recap beside widgets. It gets the period, and answers with slides.

async recap({ year, from, to }) {
const n = await countThings(from, to);
if (!n) return []; // nothing to say? say nothing
return [{
id: 'things',
kicker: 'You made',
value: n.toLocaleString('en'),
unit: 'things',
scope: 'year',
line: 'One sentence on why that matters.',
stat: 'Things made',
score: 60,
trait: { name: 'The Maker', line: 'Who this makes them.', strength: 0.6 },
}];
}

from and to are milliseconds. to stops at now, so a year in progress is covered up to today. A recap that takes longer than 25 seconds or throws is left out, and the rest go on.

No model writes any of this. A paper full of real numbers can’t have one inventing a few.

  • Only real numbers. If your service can’t really know something, leave it out.
  • Say what it covers. scope is 'year', or what the number covers: 'All time', 'Last 30 days'. Left out, it’s 'Right now'. A slide that doesn’t say doesn’t get to claim the year. When more than half the slides are 'year', the review opens on the year. Otherwise it opens “Your homelab” and “Busy, wasn’t it?”.
  • An empty value is dropped.
  • Mark private: true if it names people. It stays off the shareable card unless chosen.

Most services can only say a count. In a built-in service(), tally(count, slide) makes the usual slide from one number, and nothing at all when it’s zero.

import { service, tally } from '../../server/service.ts';
recap: d => tally(d.stats.total, { id: 'code', theme: 'code', kicker: 'Your own forge holds', unit: 'repositories', stat: 'Repositories', line: 'On hardware you run.' }),

It sets value from the number, score: 55 and scope: 'Right now'. You can override the scope.

tally isn’t in the plugin toolkit. In a plugin, write the slide yourself:

recap: d => d.stats.total > 0 ? [{ id: 'code', theme: 'code', value: d.stats.total.toLocaleString('en'), score: 55, scope: 'Right now', kicker: 'Your own forge holds', unit: 'repositories', stat: 'Repositories', line: 'On hardware you run.' }] : [],

A service() gets its app icon on the slide without asking, unless the slide brings its own art.

Valet keeps up to 3 slides per theme from each service and 4 per theme overall, sorted by score. The strongest slides play first, up to eight, the richest at the front. A slide with a chart, artwork or a trait beats a bare count, and neighbours never share a theme. All the rest follow without a break.

FieldWhat it does
idRequired.
kickerRequired. The small line above: “You watched”.
valueRequired. The big one. A plain number counts up.
lineRequired. A sentence on why it’s interesting.
unitBeside the value.
scope'year', or what it covers.
scoreHigher comes earlier. 50 by default.
themeOne of media, music, reading, food, home, network, reach, security, files, ops, code, ai, hardware, games.
statIts label on the shareable card, if it belongs there.
privateIt names people.
trait{ name, line, strength }. Who this makes you, 0 to 1. The strongest across all services wins.
logoThe app’s icon, large, tinting the slide with its colours.
itemsA top five: { name, value, image }.
barsA small chart of what the number is made of: { label, value, show }.
chartProgress: { pct, from, to, mark }.
raceThe top titles overtaking each other.
backdropWide artwork, full bleed.
wallThe drifting wall of posters behind it.
artPosters to add to that wall.
showA wall of this slide’s own pictures: { images, shape, soft }.
iconsA wall of app icons.
visualA live 3D model: { kind, data }, where kind is home or pc-case.
factsRaw numbers, so slides can be written across services.