Skip to content

meta: renderer roadmap #61

Description

@unhappychoice

Tracks all renderer candidates for splashboard. Part of the widget catalog index in #41.

What a renderer is

See #41 and AGENTS.md for the full architecture. In short: a renderer consumes a Payload + RenderOptions and draws into a ratatui frame. It declares name(), accepts() (which Shape variants it can render), animates(), and render(). One shape can feed multiple renderers.

Before building a renderer from scratch, check awesome-ratatui. Many primitive gaps already have community crates that we can wrap with a thin Renderer impl — a few lines mapping Payload → widget input. Each gap below notes candidate crates where relevant.

Two paradigms of "animation" are worth distinguishing, since different libraries serve each:

  • Post-process effects — render the widget normally, then apply a shader-like pass (fade, color shift, glitch, slide, dissolve, sweep). tachyonfx (50+ effects) is the clear winner; it works on the ratatui buffer after the widget drew into it.
  • Reveal animations — the widget's final form emerges from a particle-like animation (matrix rain decrypting into text, beams scanning, fireworks coalescing, characters burning up from below). TTE is the reference. tachyonfx can't cleanly express most of these because its paradigm is buffer post-processing, not per-character physics. Expect a custom build for this family (clean-room, TTE as algorithmic reference). tui-rain covers matrix/rain specifically.

ansi-to-tui covers ANSI passthrough as a separate concern.

Naming conventions

Renderer names are user-facing (they appear in render = "..." config) so drift is expensive to undo. Locking in the rules here so new renderers don't add fresh inconsistencies:

  • Registered name == module name. One-to-one. Grep-ability across config and source.
  • Every renderer carries a family prefix, never a suffix. chart_bar / chart_line / chart_pie, not bar_chart / line_chart / pie_chart. Prefix form is sortable (all siblings cluster), autocomplete-friendly, and consistent with the clock_* / system_* / git_* / github_* fetchers. No bare names — every renderer belongs to exactly one family.
  • Families:
    • text_* — rich single-glyph-per-cell text. text_plain, text_ascii, text_markdown, future text_diff.
    • list_* — multi-row lists. list_plain, list_timeline, future list_tree, list_checklist, list_ranking.
    • chart_* — data charts. chart_bar, chart_line, chart_pie, chart_scatter, chart_sparkline, future chart_stacked_bar, chart_area, chart_histogram, chart_radar, chart_tree_map, chart_bullet, chart_candlestick, chart_gantt, chart_funnel, chart_word_cloud, chart_waterfall, chart_slope, chart_bumps, chart_diverging_bars, chart_radial_dial, chart_box.
    • gauge_* — ratio indicators. gauge_circle, gauge_line, future gauge_ring, gauge_battery, gauge_thermometer, gauge_segment, gauge_speedometer, gauge_compass.
    • grid_* — 2D cell grids. grid_table, grid_heatmap, grid_calendar, future grid_kanban.
    • status_* — single-value status display. status_badge, future status_row, status_traffic_light, status_dots, status_pulse.
    • media_* — raster / ANSI / pictorial renderers. media_image, future media_ansi, media_pixel, media_gradient, media_qr, media_barcode, media_analog_clock, media_video, media_ascii_map.
    • animated_* — animation effects. animated_typewriter, future animated_reveal, animated_postfx, animated_skeleton, animated_spinner, animated_marquee, animated_flip, animated_glitch.

Catalog

[x] shipped (code in src/render/), [ ] planned. Planned entries are annotated with wrap (existing ecosystem crate) or build (from-scratch). Sorted by family to match the naming convention above.

text_*

  • text_plain — plain-text lines (accepts Text + TextBlock)
  • text_ascii — figlet-style big text via tui-big-text / figlet-rs
  • text_markdown — render Markdown via tui-markdown. Shipped in feat(render): add text_markdown renderer + MarkdownTextBlock shape #151. Introduced a dedicated MarkdownTextBlock shape (rather than reusing Text/TextBlock) so a fetcher emitting it commits to "this string is Markdown source" — Text payloads can't accidentally route here, and text_plain can't be asked to render Markdown verbatim. Theme integration via SplashStyleSheet: headings → panel_title, code/links → accent_event, blockquote → text_secondary, heading_meta → text_dim. Syntect highlight intentionally disabled (tui-markdown default-features = false) so fenced code blocks stay on theme tokens. Currently only basic_static emits the shape; richer fetcher consumers (README excerpts, release notes) deferred to follow-up. Side fix: default_cache_key now includes ctx.shape so multi-shape fetchers don't cache-poison across shape variants.
  • text_diff wrap — highlighted unified diff (last commit, staged changes). Wrap syntect or paint inline with ratatui Text + red/green line styling.

list_*

  • list_plain — bulleted/styled entries
  • list_timeline — time-prefixed event list ("3h ago: merged feat(render): heatmap renderer for contribution grids #42") with relative Nm/Nh/Nd/Nw labels, shipped in feat(render): add timeline renderer for timestamped event lists #48.
  • list_links — clickable rows over the new LinkedTextBlock shape, shipped in feat: LinkedTextBlock shape with clickable rows (OSC 8) + hn_top fetcher #148. Each row whose url is Some(_) is wrapped in OSC 8 escape sequences so modern terminals (iTerm2, kitty, WezTerm, recent Windows Terminal, …) surface it as a hyperlink; rows without a URL render as plain text. Implementation note: the OSC 8 sequence is collapsed into the row's first cell with skip = true on the trailing cells — without that, ratatui's Buffer::diff counts printable bytes inside the escape sequence as visible columns and silently drops the cell after the link.
  • list_tree wrap — nested structures (todo nesting, folder view). Wrap tui-tree-widget.
  • list_checklist build — checkbox items with done/pending state. New shape Checklist { items: Vec<{label, done}> }. Todos, release checklists, pre-flight checks.
  • list_cards — card rows over the new ImageLinkedList shape, shipped in feat(render): add list_cards renderer + ImageLinkedList shape #217. Each item is {title, url?, thumbnail_path?, subtitle?}; rows with url get the same OSC 8 wrap as list_links (via a shared pub(crate) wrap_osc8 helper), rows without a thumbnail_path keep a blank cell so the text column stays column-aligned. Options: thumbnail_width (default 6 cells), row_height (default 3), gap (default 1), fit (contain / cover / stretch, mirrors media_image). Reuses media_image::draw_thumbnail (extracted to pub(crate)) so the ratatui-image pipeline isn't duplicated. Remote thumbnail URLs route through a shared fetcher::thumbnails::download_to_cache that caches to $HOME/.splashboard/cache/thumbnails/<sha256>.<ext> with magic-byte sniffing and a 4 MB cap; per-row failures collapse to None so a single broken image doesn't break a feed. Fetcher consumers shipped in the same PR: rss (extracts media:thumbnailmedia:content → first HTTP(S) <img> in <content>/<summary> with HTML-entity decoding), reddit_subreddit_posts / reddit_user_posts (Reddit thumbnail URL, filtering placeholder strings self/default/nsfw/spoiler), wikipedia_featured / wikipedia_random (REST API thumbnail.source), and basic_read_store (JSON/TOML escape hatch). reddit_user_comments deliberately stays out since comments don't carry images.
  • list_ranking — top-N ranking, shipped in feat(render): list_ranking renderer for Bars shape #125. Sorts Bars descending, prints <rank> <label> <value> rows with rank-prefix and value columns aligned across rows. style = "number" | "medal" | "none" selects the prefix glyph (medal decorates 🥇/🥈/🥉 for the top three, falls back to numeric for 4th+). Reuses existing RenderOptions (style / max_items / align) — no new fields, no shape changes. Delta marker (▲ +3) deferred: would need a delta field on BarsData::Bar, follow-up if/when fetchers want period-over-period.
  • list_metrics build — a stat table where each row carries label + current value + an inline mini-sparkline of recent history. The canonical ops-dashboard row. Needs a new MetricRows { items: [{label, value, series: Vec<f64>}] } shape. Consumers: system_monitor_* rollups, git_churn, package-download trends.

chart_*

  • chart_bar — horizontal bars
  • chart_line — time series
  • chart_pie — proportion slices
  • chart_scatter — points
  • chart_sparkline — compact trend
  • chart_stacked_bar / chart_area wrap — trend primitive. Wrap ratatui-stacked-bar.
  • chart_histogram — value distribution. Consumes NumberSeries. Complement to chart_sparkline (sequence → distribution). Shipped in feat(render): add chart_histogram #122. Stretches bar width to fill the slot, lifts long-tail bins above ratatui's sub-cell render threshold, and supports axis_labels = true for min / max / peak overlays.
  • chart_radar build — multi-axis comparison. New shape Radar { axes: Vec<String>, series: Vec<Vec<f64>> }. Language breakdown, per-core CPU, skill charts.
  • chart_tree_map build — area-weighted rectangles. Consumes Bars. Disk usage, repo language composition, dependency sizes.
  • chart_bullet build — target vs actual. New shape Bullet { value: f64, target: f64, ranges: Vec<f64> }. SLO / OKR progress.
  • chart_candlestick build — OHLC. New shape Candles { items: Vec<{time, open, high, low, close}> }. Stock prices, CI timing min/max/median.
  • chart_gantt build — milestone timeline. New shape Gantt { items: Vec<{label, start, end}> }. Release schedule, roadmap view.
  • chart_funnel build — ordered conversion stages. Consumes Bars. PR funnel (opened → reviewed → merged), onboarding funnel.
  • chart_word_cloud build — tag / keyword cloud sized by frequency. New shape WordCloud { items: Vec<{word, weight, style?}> }. Moved from text_* — output is size-weighted layout, not line text.
  • chart_waterfall build — running-total bars showing per-step contribution to a final value (up bars green, down bars red, total bar grey). Performance regression breakdown (PR-by-PR ms delta), monthly budget contribution, PR-by-PR LOC delta. Consumes the same SignedBars shape as chart_diverging_bars (running-total semantics is the differentiator — bars are sequenced and accumulate).
  • chart_diverging_bars build — diverging centered bars on a single signed-value axis. Two visual orientations:
    • orientation = "horizontal" — horizontal tornado: positive right, negative left. Sentiment, A/B lift.
    • orientation = "vertical" (default for paired data) — population-pyramid: same-category mirrored bars left vs right. Languages added vs deleted, PRs opened vs closed.
      Absorbs the previously drafted chart_tornado and chart_population_pyramid — the visual difference is one option, not two renderers. Needs new SignedBars { items: Vec<{label, value: f64}> } shape (shared with chart_waterfall).
  • chart_slope build — 2-column slope chart (left col → right col with connecting lines), good for before/after value comparison ("this month vs last month", language rank diff after a refactor). Evaluate overlap with chart_bumps first — bumps generalises slope to N rounds, slope might collapse into chart_bumps with rounds.len() == 2. Needs new Slope { left: Vec<{label, value}>, right: Vec<{label, value}> } shape if it stays standalone.
  • chart_bumps build — multi-round rank-over-time spaghetti (chained slope chart). F1 standings progression, repo star ranking over weeks, top-N project ranking. Distinct from chart_line because of explicit rank semantics (Y axis is rank, lines cross when ranks swap) and per-line label decoration at each round. Needs new Bumps { rounds: Vec<String>, series: Vec<{label, ranks: Vec<u32>}> } shape.
  • chart_radial_dial build — 24h circular timeline (clock-face) with event dots placed by time-of-day. Daily event distribution, PR-merged-at distribution. Consumes Timeline. Shares clock-face geometry with media_analog_clock; factor a common helper rather than duplicating.
  • chart_box build — box plot (min / Q1 / median / Q3 / max + outliers). Consumes NumberSeries. CI duration distribution (p50/p95), benchmark variance, request latency distribution. Distinct primitive from chart_histogram (binned distribution) and chart_violin (KDE shape, planned as a chart_histogram smoothing mode rather than a separate renderer).
  • chart_sunburst build — multi-level radial hierarchy (concentric rings, inner = parent). Disk usage by nested directory, dependency tree by size. Consumes the same hierarchical Tree shape as chart_tree_map — evaluate whether it's a chart_tree_map { layout = "radial" } mode vs a sibling renderer first.
  • chart_dumbbell build — one row per category with two dots joined by a line (before / after, min / max, you vs team). Evaluate overlap with chart_slope first — slope connects two columns, dumbbell connects two points per row; might collapse into chart_slope { layout = "rows" } over the same Slope shape.

gauge_*

  • gauge_circle — circular gauge
  • gauge_line — single-metric bar
  • gauge_ring build — Apple activity-ring style circle. Evaluate overlap with existing gauge_circle first.
  • gauge_battery — battery-icon fill, shipped in feat(render): add gauge_battery renderer for Ratio shape #113. Consumes Ratio. Playful framing for laptop battery, quota, progress.
  • gauge_thermometer — vertical mercury-tube gauge for Ratio, shipped in feat(render): add gauge_thermometer vertical mercury-tube renderer #126. Tone follows the gauge_battery / gauge_segment neutral / fill / drain semantics. Falls back to a plain fill column when the slot is too narrow for tube + side labels; degrades to top-cap + bulb only at h=2. Unit ticks deferred — can land as a follow-up if a fetcher needs them.
  • gauge_segment — 5-LED segmented bar, shipped in feat(render): add gauge_segment renderer for Ratio shape #117. Consumes Ratio. Retro-hardware feel.
  • gauge_speedometer build — half-circle dial with a needle pointing to the value position. Showy variant of gauge_circle for percentage / throttle / RPM-like readings. Consumes Ratio. Evaluate overlap with gauge_circle / gauge_ring first — three Ratio gauges in the circular family is a lot; consider whether this can be a gauge_circle style = "speedometer" mode (half-circle + needle) instead of a sibling renderer.
  • gauge_compass build — N/E/S/W compass dial with a directional indicator. Wind direction, magnetic bearing, optional magnitude as inner ring fill. Needs new Bearing { degrees: f64, magnitude: Option<f64> } shape.
  • gauge_rating build — discrete star / pip rating (★★★☆☆). Consumes Ratio mapped onto N pips (default 5). Package score, code-health grade, review rating. Evaluate whether it's just gauge_segment { glyph = "star" } before adding a sibling.
  • gauge_steps build — linear labeled progress stepper (① ─ ② ─ ③ ─ ○ ─ ○ with stage names). Deploy pipeline stages, onboarding progress, a release checklist as a flow. Distinct from status_traffic_light (stacked independent badges) and grid_kanban (columns). Needs a Steps { stages: Vec<{label, state}>, current: usize } shape.

grid_*

  • grid_table — key/value rows
  • grid_calendar — month grid (today highlight)
  • grid_heatmap — 2D intensity grid, shipped in feat(render): heatmap renderer for contribution grids #42. GitHub light-mode palette (pending theme migration in feat: Theme / color scheme system #17). Body::Heatmap { cells, thresholds, row_labels, col_labels }; 5-level intensity, auto-quartile or explicit thresholds, right-aligns the window when the slot is narrower than the data, optional top-row month labels, honours align for horizontal placement.
  • grid_kanban build — column board layout (To Do / Doing / Done, or arbitrary column titles) with item cards under each column header. Consumes Linear / Todoist / Jira ticket lists by status. Needs new KanbanBoard { columns: Vec<{title: String, items: Vec<String>}> } shape.

status_*

  • status_badge — single-status traffic light (green/red/yellow), shipped in feat(render): add badge renderer #46. Used by CI, deploy, SLO, oncall.
  • status_row build — row of status_badges. Probably nested layout rather than a dedicated renderer.
  • status_traffic_light build — multiple stacked badges at a glance (CI + deploy + SLO in one widget). New shape BadgeGroup { items: Vec<BadgeData> }.
  • status_dots build — recent N-state history dots (e.g., last 10 CI runs as coloured dots). New shape StatusHistory { states: Vec<StatusLevel> }.
  • status_pulse build — animated ECG-style heartbeat trace. Service-alive indicator for uptime / oncall widgets — visual "still beating" cue distinct from a static badge. Animates within ANIMATION_WINDOW. Consumes Text (label) or Ratio (frequency).

media_*

  • media_image — PNG/JPEG via ratatui-image
  • media_ansi wrap — render hand-authored colored art verbatim. Wrap ansi-to-tui (converts ANSI strings to ratatui::text::Text).
  • media_pixel — shipped in feat(render,weather): PixelArt shape + media_pixel + weather sprites #260. Half-block + truecolor renderer for the new PixelArt shape; works in any 24-bit terminal (no kitty/sixel/iTerm2 dependency). Accepts align / max_width / max_height. Jagged rows surface an in-band error; zero-row grids fall through the centralized empty-state placeholder.
  • media_gradient build or wrap — color-gradient background for banner text. Potentially via tachyonfx or custom shader.
  • media_qr wrap — takes string, paints QR pattern. Wrap the qrcode crate + paint as block cells. Consumes Text. Moved from text_qr — output is block-cell drawing, not line text.
  • media_barcode wrap — barcode counterpart to QR. Consumes Text.
  • media_analog_clock build — takes a time, paints analog hands. Consumes Text (time string); a future dedicated Instant shape is possible but not required for a single-reader renderer. Previously drafted under a clock_* family, but splashboard's one-shot render model doesn't really support a "live ticking clock", and a single pictorial clock renderer doesn't justify its own family — media_* is the right bucket alongside media_image / media_qr.
  • media_video build — frame-based small video loop within the 2s ANIMATION_WINDOW. New shape Frames { paths: Vec<PathBuf>, fps: u32 }.
  • media_ascii_map build — ASCII world / region map painted as block cells with markers placed by latitude/longitude. Remote-team locations, error-origin map, server-region map, earthquake / news geographic feeds. Needs new GeoPoints { points: Vec<{lat: f64, lon: f64, label: Option<String>, intensity: Option<f64>}>, region: Option<String> } shape.
  • media_braille build — render an image as 2×4 braille dots: monochrome but high-resolution, and works on terminals with no sixel / kitty / iTerm graphics protocol. The universal fallback to media_image. Consumes Image; evaluate whether it's a media_image { mode = "braille" } option vs a sibling renderer.

animated_*

  • animated_typewriter — char-by-char reveal (equivalent to TTE's print effect; will be absorbed into the animated_reveal family)
  • animated_revealbuild — TTE-style reveal animations where the final text emerges from a particle-like animation. Fits the 2s ANIMATION_WINDOW. Not expressible via tachyonfx's buffer-post-process paradigm.
    • Algorithmic reference: TTE (Python, MIT, ~4k⭐), what omarchy's screensaver loops over.
    • Effects to build (priority order by visual impact): matrix, beams, decrypt, burn, fireworks, blackhole, scattered, spray, rings, waves, wipe, pour, swarm, synthgrid, unstable.
    • Existing animated_typewriter is TTE's print effect; absorb into this family.
    • matrix specifically can start from wrapping tui-rain (partial coverage).
    • Shape: one renderer animated_reveal + effect = "..." option.
    • Splashboard's differentiator over TTE: multi-widget layout — logo reveals while other widgets stay static.
  • animated_postfx wrap — apply shader-like effects over already-rendered widgets. Fits the 2s ANIMATION_WINDOW (no separate screensaver mode needed). Shipped in feat(render): add animated_postfx renderer #74 with fade_in / fade_out / dissolve / coalesce / sweep_in{,_right,_down,_up} / slide_in{,_right,_down,_up} / hsl_shift — foreground-only variants (pattern-based for directional effects) so the terminal background shows through untouched.
    • Wraps tachyonfx (50+ effects).
    • Covers: fade, slide, sweep_in, dissolve, glitch, hsl_shift, translate, resize, shader_fn, and their compositions (repeat, ping_pong, chain).
    • Shape: one renderer animated_postfx + effect = "..." option.
    • Effect candidates to add (no new renderer needed — these are effect values on this renderer): aurora (gradient sweep via hsl_shift + sweep), plasma (sine-sum shader_fn over cell coords), breath (slow fade ping_pong). Same logic as animated_glitch — only split into a sibling renderer if the option surface gets unwieldy for a specific effect family.
  • animated_skeleton wrap — pulse/sweep/shimmer placeholder for widgets that haven't returned yet. Wrap tui-skeleton.
  • animated_spinner wrap — throbber for pending network widgets. Wrap throbber-widgets-tui.
  • animated_marquee build — scrolling text for long titles. Consumes Text / TextBlock.
  • animated_flip build — digit / character flip reveal animation. Consumes Text / TextBlock. Absorbs the previously drafted clock_flip use-case — the value is in the flip effect itself, not the clock-ness.
  • animated_glitch wrap — glitch-focused variant of animated_postfx. Split out if the postfx effect = "..." option surface gets too generic.
  • animated_count build — a number rolls up from 0 (or from a cached previous value) to its final value within ANIMATION_WINDOW, then freezes. Reveal-to-final-state, so it fits the one-shot model. Star counts, LOC, downloads, revenue. Likely folds into animated_flip { mode = "roll" } rather than a sibling — note the overlap.

Cross-cutting

New shapes introduced by planned renderers

Adding a shape is a 5-touch-point change (Body variant, Shape enum, shape_of(), default_renderer_for(), renderer accepts()). Grouped here so PRs can batch them cleanly rather than overloading Entries:

shape consumer renderer(s)
Radar { axes, series } chart_radar
Bullet { value, target, ranges } chart_bullet
Candles { items: [{time, open, high, low, close}] } chart_candlestick
Gantt { items: [{label, start, end}] } chart_gantt
WordCloud { items: [{word, weight, style?}] } chart_word_cloud
Checklist { items: [{label, done}] } list_checklist
BadgeGroup { items: Vec<BadgeData> } status_traffic_light
StatusHistory { states: Vec<StatusLevel> } status_dots
Frames { paths, fps } media_video
SignedBars { items: [{label, value: f64}] } chart_waterfall / chart_diverging_bars
Slope { left: [{label, value}], right: [{label, value}] } chart_slope (might collapse into Bumps with rounds.len() == 2)
Bumps { rounds: Vec<String>, series: [{label, ranks: Vec<u32>}] } chart_bumps
Bearing { degrees, magnitude? } gauge_compass
KanbanBoard { columns: [{title, items: Vec<String>}] } grid_kanban
GeoPoints { points: [{lat, lon, label?, intensity?}], region? } media_ascii_map
ImageLinkedList { items: [{title, url?, thumbnail_path?, subtitle?}] } list_cards

Reused existing shapes (no new shape needed): Barschart_tree_map / chart_funnel / list_ranking; Ratiogauge_battery / gauge_thermometer / gauge_segment / gauge_speedometer (under overlap review with gauge_circle) / status_pulse (frequency); NumberSerieschart_histogram / chart_box; Timelinechart_radial_dial; Text / TextBlockmedia_qr / media_barcode / media_analog_clock / animated_marquee / animated_flip / status_pulse (label).

Rejected / dropped renderer ideas

  • clock_* renderer family — dropped. Splashboard renders one-shot (ANIMATION_WINDOW = 2s, then the buffer freezes), so a "live ticking clock" renderer fundamentally doesn't fit the model. clock_analog is kept as media_analog_clock (one-shot pictorial render), and the previously drafted clock_flip is superseded by the generic animated_flip effect.
  • text_big_number — dropped. The "huge number with a delta" use case is already covered by the existing text_ascii renderer (figlet-style big text via tui-big-text): a fetcher can emit "⭐ 142 (+3)" as Text and text_ascii renders it big. A dedicated renderer would only have justified its name with a separate visual treatment for the delta line, which doesn't carry its weight as a stand-alone primitive.
  • chart_horizon / chart_step / chart_violin / chart_polar_area / chart_punch_card / status_eq_bars — collapsed into options on existing renderers rather than sibling renderers, because they share the consumed shape with a sibling and only differ visually:
    • chart_horizonchart_sparkline { mode = "horizon" } (folded color-band time-series compression on NumberSeries).
    • chart_stepchart_line { interpolation = "step" } (staircase interpolation on PointSeries).
    • chart_violinchart_histogram { smoothing = "kde" } (KDE-smoothed distribution on NumberSeries).
    • chart_polar_areachart_pie { mode = "rose" } (Nightingale rose: equal slice angles, value as radius).
    • chart_punch_cardgrid_heatmap { cell = "dot" } (size-encoded intensity dots already planned as a heatmap option).
    • status_eq_barschart_sparkline { mode = "bars", animated = true } (audio-EQ idiom on existing bars sparkline).
      General principle: when a candidate consumes the same Shape as a sibling and the difference is purely visual, it's an option on the sibling renderer, not a new renderer name in the catalog.
  • chart_tornado / chart_population_pyramid — merged into chart_diverging_bars with orientation = "horizontal" | "vertical". The two were the same renderer with different orientation conventions on the same SignedBars shape; an option absorbs the difference without paying the catalog tax of two names.
  • animated_aurora / animated_plasma / animated_breath — folded into animated_postfx as effect values (aurora, plasma, breath). animated_postfx already wraps tachyonfx's full effect set (fade / hsl_shift / shader_fn / ping_pong / chain), so these are parameter values, not sibling renderers — same logic as the already-noted animated_glitch.

Renderer option coverage

Most renderers today read only align / style / pixel_size from RenderOptions (see src/render/mod.rs::RenderOptions). option_schemas() is wired into the docs generator (src/catalog.rs) and already used heavily by fetchers, but no renderer has populated it yet. That's a lot of low-risk expressive power sitting unclaimed.

Per-renderer option ideas (not a rename, just new optional fields):

  • list_plainmarker = "•" | "-" | "1." | "none", highlight_first = true, separator = "…", max_items = N (truncate with +3 more footer).
  • list_timelinerelative_labels = true, max_events = N, show_time_column = true.
  • grid_tableborder = "none" | "ascii" | "rounded", show_header = true, key_style = "bold", align_values = "left" | "right".
  • grid_heatmappalette = "github" | "viridis" | "warm" (also blocks on feat: Theme / color scheme system #17 theme work), cell = "block" | "dot" (the dot mode with size-encoded intensity = the GitHub punch-card visualisation; absorbs the previously drafted chart_punch_card — punch-card is a heatmap rendering mode, not a sibling renderer), show_labels = true.
  • grid_calendarfirst_day = "sun" | "mon", range = "month" | "week", marker_style = "dot" | "badge".
  • chart_barorientation = "horizontal" | "vertical", value_label = "inside" | "outside" | "hidden", max_bars = N.
  • chart_line / chart_scattergrid = true, axis_labels = true, series_colors = [...], smoothing = "none" | "spline", interpolation = "linear" | "step" (absorbs the previously drafted chart_step — staircase line for state transitions and CDFs is a chart_line mode, not a sibling renderer).
  • chart_sparklinemode = "bars" | "dots" | "braille" | "horizon", baseline = "min" | "zero", animated = true (peak-hold VU style). mode = "horizon" absorbs the previously drafted chart_horizon (folded color-band time-series compression) — the horizon idiom is a sparkline rendering mode for the same NumberSeries shape, not a sibling renderer. animated = true covers the previously drafted status_eq_bars (audio-EQ idiom) on mode = "bars".
  • chart_piedonut = true (inner-radius hole), legend = "right" | "below" | "none", show_percentages = true, mode = "slice" | "rose" (Nightingale rose: equal slice angles, value encoded as radius — absorbs the previously drafted chart_polar_area).
  • chart_histogramsmoothing = "none" | "kde" (KDE-smoothed = violin shape; absorbs the previously drafted chart_violin — both consume NumberSeries and visualize distribution shape, the only difference is the smoothing kernel).
  • gauge_circle / gauge_linelabel_position = "inside" | "outside", color_thresholds = [0.5, 0.8], unit = "%" | "MB" | "…".
  • status_badgeshape = "dot" | "pill" | "square", show_label = true.
  • media_imagefit = "contain" | "cover" | "stretch", dither = "floyd-steinberg" | "none".
  • text_ascii — already reads style / pixel_size; expose them through option_schemas() so docs pick them up.
  • animated_typewriterspeed_ms = N, cursor = "_" | "|" | "none".

Scoping: these land per-renderer as small PRs. The schema lives next to the renderer (OPTION_SCHEMAS: &[OptionSchema]), the render() body reads from RenderOptions, and xtask docs regenerate automatically.

Metadata

Metadata

Assignees

No one assigned

    Labels

    renderRender typewidgetBuilt-in widget

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions