buildCandidate method
ThemeSpec
buildCandidate(
- int seed,
- ThemeFamily family,
- int engineVersion, {
- int attempt = 0,
- bool accessible = false,
- int? seedHueTenths,
- ThemeIntent? intent,
- bool intentCentred = false,
One unvalidated attempt (tests and diagnostics; generate adds repair, retries and the hero fallback).
Implementation
ThemeSpec buildCandidate(
int seed,
ThemeFamily family,
int engineVersion, {
int attempt = 0,
bool accessible = false,
int? seedHueTenths,
ThemeIntent? intent,
bool intentCentred = false,
}) {
final shipped =
familyDefinitionAt(family, engineVersion) ??
(throw ArgumentError.value(
family,
'family',
'not shipped at engine version $engineVersion',
));
final steering = _steering(
intent,
intentCentred,
engineVersion,
seedHueTenths,
);
if (steering != null && !steerableFamilies.contains(family)) {
// Not steerable yet: the family's own theme, with the intent's name.
final spec = buildCandidate(
seed,
family,
engineVersion,
attempt: attempt,
accessible: accessible,
);
return spec.copyWith(
variant: ThemeVariant.steered,
intent: steering.intent.forSpec,
intentCentred: steering.centred,
name: steering.nameOr(() => spec.name),
);
}
final def = steering == null ? shipped : steering.revise(shipped);
// The six families of the 2026-10 round each build their own palette,
// background scene, shape, style, type and motion from the same named
// streams (their own files).
if (family == ThemeFamily.claymorphism) {
return buildClaymorphismCandidate(
seed,
def,
engineVersion,
attempt: attempt,
accessible: accessible,
steering: steering,
);
}
if (family == ThemeFamily.y2kChrome) {
return buildY2kAeroCandidate(
seed,
def,
engineVersion,
attempt: attempt,
accessible: accessible,
);
}
if (family == ThemeFamily.paperInk) {
return buildPaperInkCandidate(
seed,
def,
engineVersion,
attempt: attempt,
accessible: accessible,
);
}
if (family == ThemeFamily.cyberpunk) {
return buildCyberpunkCandidate(
seed,
def,
engineVersion,
attempt: attempt,
accessible: accessible,
);
}
if (family == ThemeFamily.vaporwave) {
return buildVaporwaveCandidate(
seed,
def,
engineVersion,
attempt: attempt,
accessible: accessible,
);
}
if (family == ThemeFamily.bauhaus) {
return buildBauhausCandidate(
seed,
def,
engineVersion,
attempt: attempt,
accessible: accessible,
);
}
if (family == ThemeFamily.neoBrutalism) {
// Neo-brutalism builds its own palette (ink outlines and hard
// shadow, saturated flat fills, outlined display card;
// neo_brutalism.dart) from the same named streams.
return buildNeoBrutalCandidate(
seed,
def,
engineVersion,
attempt: attempt,
accessible: accessible,
);
}
if (family == ThemeFamily.liquidGlass) {
// Liquid Glass builds its own palette (vivid mesh, translucent glass
// tints solved on their composites, display backing; liquid_glass.dart)
// from the same named streams.
return buildGlassCandidate(
seed,
def,
engineVersion,
attempt: attempt,
accessible: accessible,
);
}
if (family == ThemeFamily.retro) {
// Retro builds its own palette (plastic body, keycaps, LCD / VFD
// panel and segments; retro.dart) from the same named streams.
return buildRetroCandidate(
seed,
def,
engineVersion,
attempt: attempt,
accessible: accessible,
);
}
final paletteTag = attempt == 0
? RngTag.palette
: RngTag.sub(RngTag.palette, 'retry', attempt);
if (def.keyStyle.kind == KeyStyleKind.soft) {
// Neumorphism builds its own palette (surface-coloured keys, soft
// shadow pair, solved rims); other families never reach this.
return buildNeumorphicCandidate(
seed,
def,
engineVersion,
paletteTag: paletteTag,
accessible: accessible,
);
}
final p = Rng.stream(seed, paletteTag);
final drawnBrightness = p.pickWeighted(def.brightnessWeights) == 0
? ThemeBrightness.dark
: ThemeBrightness.light;
final brightness = steering?.brightnessFor(def) ?? drawnBrightness;
final dark = brightness == ThemeBrightness.dark;
final ranges = def.ranges(brightness);
final drawnScheme = pickWeightedValue(p, def.schemes);
final scheme = steering?.schemeFor(def.schemes) ?? drawnScheme;
final ir = steering == null
? null
: Rng.stream(seed, intentTag(paletteTag));
final steeredH0 = ir == null ? null : steering!.baseHue(ir);
var hues = _Hues.draw(
scheme,
p,
h0Override: seedHueTenths == null
? steeredH0
: _wrapTenths(seedHueTenths),
);
if (ir != null) {
hues = hues.withAccents(
steering!.accentHue(ir, 0),
steering.accentHue(ir, 1),
);
}
final lim = accessible
? GuardrailLimits.accessible
: GuardrailLimits.standard;
final bile = def.bile;
final faces = _sampleFaces(
p,
ranges,
scheme,
hues,
bile,
dark: dark,
accessible: accessible,
);
// Labels.
final policy = lim.labelPolicy;
final labelChroma = p.nextIntIn(10, ranges.labelMaxChromaMilli) / 1000;
KeyColors key(Lch face, double edgeShade) {
final start = face.l < 0.58 ? 0.97 : 0.17;
var pair = repairPair(face, (
l: start,
c: labelChroma,
h: face.h,
), policy: policy);
if (bile.recheckFinal) {
// Label repair may have moved the face into the disliked band:
// check the final colour and re-solve with the hue rotated out.
final f = argbToOklch(pair.face);
final hue = (f.h * 10).round() % 3600;
final rotated = bile.apply(hue, f.l, f.c);
if (rotated != hue) {
final moved = (l: f.l, c: f.c, h: rotated / 10);
pair = repairPair(moved, (
l: start,
c: labelChroma,
h: moved.h,
), policy: policy);
}
}
final f = pair.faceLch;
final edgeL = f.l + edgeShade;
return KeyColors(
face: pair.face,
label: pair.label,
edge: oklchToArgb(
edgeL < 0 ? 0 : (edgeL > 1 ? 1 : edgeL),
f.c * 0.85,
f.h,
),
);
}
final extruded = def.keyStyle.kind == KeyStyleKind.extruded;
final edgeShade = extruded
? -def.keyStyle.edgeShadePermille / 1000
: (dark ? 0.08 : -0.08);
final digit = key(faces.digit, edgeShade);
final function = key(faces.function, edgeShade);
final operator = key(faces.operator, edgeShade);
final equals = key(faces.equals, edgeShade);
final s = faces.surface;
final surfaceArgb = oklchToArgb(s.l, s.c, s.h);
final background = _background(
Rng.stream(seed, RngTag.background),
def,
faces.background,
hues,
dark: dark,
);
final bgArgb = background.base;
// Display texts against every backdrop.
final draft = Palette(
background: bgArgb,
surface: surfaceArgb,
displayText: 0,
previewText: 0,
error: 0,
caret: 0,
focusRing: 0,
accent: 0,
digit: digit,
operator: operator,
equals: equals,
function: function,
shadow: 0,
highlight: 0,
seedColor: operator.face,
);
final backdrops = [surfaceArgb, ...backgroundSamples(background)];
int text(double l, double c, double hDeg, double target) =>
solveOnAll(oklchToArgb(l, c, hDeg), backdrops, target) ??
blackOrWhite(surfaceArgb);
final h0 = hues.h0 / 10;
final displayText = text(dark ? 0.97 : 0.17, 0.012, h0, lim.displayRatio);
final previewTarget = lim.previewRatio < ratioText
? ratioText
: lim.previewRatio;
final previewText = text(dark ? 0.80 : 0.42, 0.03, h0, previewTarget);
final error = text(dark ? 0.72 : 0.50, 0.16, 25, lim.displayRatio);
final opH = hues.operator / 10;
final accentL = dark ? 0.80 : 0.50;
final accentC = _chroma(p, ranges.accentChroma, accentL, opH);
final accent = text(accentL, accentC, opH, lim.caretRatio);
final palette = draft.copyWith(
displayText: displayText,
previewText: previewText,
error: error,
caret: accent,
focusRing: accent,
accent: accent,
shadow: extruded ? 0xB3000000 : (dark ? 0x52000000 : 0x29000000),
highlight: dark ? 0x14FFFFFF : 0x66FFFFFF,
);
// Motion (shared with the extruded style's depth).
final motion = def.motionRanges.vary(
def.motion,
Rng.stream(seed, RngTag.motion),
);
final (keyShape, cue) = _shape(Rng.stream(seed, RngTag.shape), def);
final keyStyle = _style(Rng.stream(seed, RngTag.style), def, motion);
final type = _type(Rng.stream(seed, RngTag.type), def);
String generatedName() => generateThemeName(
rng: Rng.stream(seed, RngTag.name),
accentArgb: operator.face,
family: family,
dark: dark,
);
final name = steering?.nameOr(generatedName) ?? generatedName();
return ThemeSpec(
seed: seed,
family: family,
engineVersion: engineVersion,
variant: seedHueTenths != null
? ThemeVariant.wallpaper
: (steering != null ? ThemeVariant.steered : ThemeVariant.generated),
seedHueTenths: seedHueTenths == null ? null : _wrapTenths(seedHueTenths),
intent: steering?.intent.forSpec,
intentCentred: steering?.centred ?? false,
brightness: brightness,
accessible: accessible,
palette: palette,
keyShape: keyShape,
keyStyle: keyStyle,
type: type,
background: background,
motion: motion,
sound: def.sound,
name: name,
nonColourOperatorCue: cue,
);
}