revealStyleFor function

TextStyle? revealStyleFor(
  1. GptMarkdownAnimation effect,
  2. double progress,
  3. Color color
)

The style an in-flight character is drawn with.

progress is 0 the moment the character is revealed and 1 when its entrance is over. color is the colour it will settle into.

The result is a delta, applied over whatever the span already carries, so an effect only names what it changes and inherits the rest. That is also what makes GptMarkdownAnimation.blurIn safe: foreground and color cannot both be set on one style, and TextStyle.merge drops the inherited colour when the delta brings a paint.

Returns null when the character should be drawn exactly as it will settle, which the renderer takes as licence to skip styling it at all.

Implementation

TextStyle? revealStyleFor(
  GptMarkdownAnimation effect,
  double progress,
  Color color,
) {
  if (progress >= 1) {
    return null;
  }
  final t = progress.clamp(0.0, 1.0);
  switch (effect) {
    case GptMarkdownAnimation.none:
    case GptMarkdownAnimation.typewriter:
      return null;

    case GptMarkdownAnimation.fade:
      return TextStyle(color: color.withValues(alpha: color.a * t));

    case GptMarkdownAnimation.blurIn:
      // The blur has to resolve a little ahead of the opacity, or the last
      // of it lands on a character already at full strength and reads as a
      // smudge rather than as focus arriving.
      final sigma = _blurSigma * (1 - Curves.easeOut.transform(t));
      final paint = Paint()..color = color.withValues(alpha: color.a * t);
      if (sigma > _minSigma) {
        paint.maskFilter = ui.MaskFilter.blur(ui.BlurStyle.normal, sigma);
      }
      // `foreground` and `color` are mutually exclusive in a TextStyle, and
      // the base may carry a colour, so it is cleared here.
      return TextStyle(color: null, foreground: paint);

    case GptMarkdownAnimation.wave:
      // Full opacity almost immediately — the motion readers see is the
      // colour crest travelling, not letters materialising.
      final opacity = (t * 3).clamp(0.0, 1.0);
      final crest = Color.lerp(_waveCrest(color), color, t) ?? color;
      return TextStyle(color: crest.withValues(alpha: color.a * opacity));
  }
}