Rendering (internals)#

The only place Rich lives. Documented for contributors; signatures are not a stable API.

The single Rich console the whole tool prints through.

Every human-facing message goes through this module (CLAUDE.md rule), so output styling, quiet modes, and testability stay in one place. Core logic never prints – the core/ report objects arrive here fully computed and this module only renders them.

matmelt.ui.console.note_console = <console width=80 None>#

Advisory notes that must not land on stdout, because a --json run’s stdout has to stay parseable (the M10 lesson).

matmelt.ui.console.render_config_error(message)[source]#

One config problem report (the message already carries YAML paths).

Rendered verbatim: config errors quote user input and name lists in square brackets (missing [structure]), which Rich would otherwise swallow as markup tags.

Parameters:

message (str) – The full ConfigError text, printed with Rich markup off so bracketed content such as missing [structure] reaches the user verbatim instead of being eaten as a style tag.

Return type:

None

matmelt.ui.console.render_legacy_config_note(legacy, preferred)[source]#

Say once that the pre-rename config filename is being used.

Parameters:
  • legacy (str) – The pre-rename filename actually being read.

  • preferred (str) – The current filename it should be renamed to.

Return type:

None

matmelt.ui.console.render_error(message)[source]#

A command-level problem that is not a config error (already actionable).

Parameters:

message (str) – The message, which the caller has already made actionable.

Return type:

None

matmelt.ui.console.render_init_created(path)[source]#

Announce the scaffolded config and hand off to the doctor checks.

Parameters:

path (str) – Where the config was scaffolded.

Return type:

None

matmelt.ui.console.print_json_payload(payload)[source]#

Machine-readable output for --json flags: plain JSON on stdout.

Parameters:

payload (dict[str, Any]) – The document to print. It goes to stdout alone, with every human-facing line routed to stderr, so the output stays machine-parseable.

Return type:

None

matmelt.ui.console.render_doctor(report, enumeration, model, aiida=None, *, ok)[source]#

The full doctor diagnosis: backends, stage readiness, seeds, model, verdict.

Parameters:
  • report (DoctorReport) – Backend health and per-stage readiness.

  • enumeration (EnumerationReport) – The seed-count report.

  • model (ModelStatus | None) – The screen model status, or None when no screen stage is configured.

  • aiida (AiidaStatus | None, default: None) – The AiiDA status, or None when no calculate stage is configured.

  • ok (bool) – The overall verdict.

Return type:

None

matmelt.ui.console.render_note(message)[source]#

A one-line informational message.

Parameters:

message (str) – The text to print.

Return type:

None

matmelt.ui.console.render_warning(message)[source]#

A one-line yellow warning: worth seeing, never fatal.

markup=False for the same reason as render_config_error: messages carry user data (ids, paths) that must never be eaten as Rich markup.

Return type:

None

matmelt.ui.console.render_enumeration(enumeration)[source]#

The seed-enumeration report: the n-ary blast radius, before any work.

Parameters:

enumeration (EnumerationReport) – The report, whose oversized sizes are named explicitly so a silently-empty size is never a mystery.

Return type:

None

matmelt.ui.console.render_run_header(run_name, active_stage_names)[source]#

Which run this is and which stages it will actually execute.

Parameters:
  • run_name (str) – The configured run name.

  • active_stage_names (Sequence[str]) – The stages this run will execute, in funnel order.

Return type:

None

matmelt.ui.console.render_unready(failures)[source]#

The preflight refusal: every unmet requirement and its exact fix.

Parameters:

failures (Sequence[RequirementStatus]) – The unmet requirements. Each is printed with the exact install command and the location it was probed at, so a remote gap is not mistaken for a local one.

Return type:

None

matmelt.ui.console.confirm_model_download(detail)[source]#

The disclosed foundation-model consent prompt. EOF/no-TTY = declined.

Parameters:

detail (str) – The disclosure: model size, cache location, and that nothing downloads without consent.

Returns:

Whether the user agreed. EOF or a non-interactive terminal reads as declined – an unattended run must never start a large download by accident.

Return type:

bool

matmelt.ui.console.render_pass_summary(index, summary)[source]#

One progress line per pass with activity; silent when nothing happened.

Parameters:
  • index (int) – The pass number.

  • summary (Mapping[str, int]) – What the pass did. A pass with no activity prints nothing, so a long drain does not scroll a wall of zeroes.

Return type:

None

matmelt.ui.console.render_interrupted()[source]#

Clean Ctrl-C: everything durable, the same command resumes.

Return type:

None

matmelt.ui.console.render_run_result(counts, *, drained, only=None, selective=False, stopped_early=None)[source]#

The final per-state tally and whether the funnel fully drained.

In only-mode (only set) “drained” means the one stage has no work left – other states legitimately hold non-terminal materials awaiting the rest of the funnel, so the full-run wording would mislead. The same holds for a selective run (selective): only the chosen lineage was driven, so unselected materials legitimately remain non-terminal. And for a mid-funnel stop (stopped_early = the stop_after stage name): materials rest at that stage’s output, resumable, never DONE.

Parameters:
  • counts (Mapping[MaterialState, int]) – The final per-state tally.

  • drained (bool) – Whether the funnel ran out of work.

  • only (str | None, default: None) – The single stage this run executed, if any.

  • selective (bool, default: False) – Whether a material selection was active.

  • stopped_early (str | None, default: None) – Whether a pass limit cut the run short.

Return type:

None

matmelt.ui.console.render_status(run_name, counts, failed, jobs)[source]#

The status view: state tally, failed materials, in-flight jobs.

Parameters:
Return type:

None

matmelt.ui.console.render_state_materials(run_name, state, records)[source]#

The status --state view: the material ids sitting at one state.

This is the picking list for selective runs: the ids feed run --materials (parent ids shown so lineage selections are easy to trace back to their seeds).

Parameters:
  • run_name (str) – The configured run name.

  • state (MaterialState) – The state being listed.

  • records (Sequence[MaterialRecord]) – The materials at that state, with their parents where they have one.

Return type:

None

matmelt.ui.console.render_retry_preview(run_name, records)[source]#

Summarise the FAILED materials to be retried, grouped by stage.

The “ingenious UX” the user asked for: instead of a flat wall of ids, one row per stage with its count – the headline decision info (“128 failed at structure”) – plus how many distinct reasons that stage saw and one example. (Reasons often embed the material’s own path, so clustering on the exact reason string is useless; the distinct-count still flags whether a stage failed one way or many.) Narrow with --stage if a group is not what you meant. Stage/reason are escape-d – they carry user data (paths, <&>) that is never Rich markup.

Parameters:
Return type:

None

matmelt.ui.console.confirm_retry(count)[source]#

Confirm resetting count FAILED materials. EOF/no-TTY = declined.

Parameters:

count (int) – How many materials would be reset.

Returns:

Whether the user agreed. EOF or a non-interactive terminal reads as declined.

Return type:

bool

matmelt.ui.console.render_retry_result(count)[source]#

Report how many materials were reset back to a claimable state.

Parameters:

count (int) – How many materials were actually reset.

Return type:

None

matmelt.ui.console.render_results(run_name, done, with_record)[source]#

The results view: every DONE material and whether it has a record.

Parameters:
  • run_name (str) – The configured run name.

  • done (Sequence[MaterialRecord]) – The finished materials.

  • with_record (set[str]) – Which of them have a record.json. A finished material without one is first-class, not an error.

Return type:

None

matmelt.ui.console.render_cif_export(run_name, out_dir, written, skipped, failed)[source]#

The cif view: what was exported, what was skipped, what failed.

Skips are grouped by reason (hundreds of “did not converge” rows help nobody); failures are listed per material, because each one is something the user may want to chase. Ids and reasons are escape-d – they are user data, never Rich markup.

Parameters:
Return type:

None

Rich rendering for the monitor command: snapshot in, panels out.

The UI half of the monitor seam: core/monitor.py computes a MonitorSnapshot; this module turns it into the five stacked panels (run overview with the done-donut and state bar, stage pipeline, running now with per-material CPU/GPU meters, recent completions, recent failures) and nothing else – no queries, no business logic, no mutation. It prints through the shared console from ui/console.py, so the one-console rule holds; it lives in its own module only because the monitor’s rendering is a cohesive block that would double console.py’s size.

Everything user-data-bearing (ids, stages, failure reasons) is escape-d before rendering – reasons carry paths and bracketed text that Rich would otherwise swallow as markup (the render_config_error lesson).

matmelt.ui.monitor.fmt_duration(seconds)[source]#

5085.0 -> "1h 24m" – coarse, human-first wall-clock spans.

Sub-minute spans show seconds ("42s"), sub-hour spans minutes and seconds ("4m 12s"), longer spans hours and minutes. Negative or zero input renders as "0s" (clock skew must never crash a dashboard).

Parameters:

seconds (float) – A wall-clock span.

Returns:

A coarse, human-first rendering – one unit of precision is what a glancing reader can use.

Return type:

str

matmelt.ui.monitor.fmt_rate(per_min)[source]#

2.34 -> "2.3/min"; a zero rate is a dash, never "0.0/min".

Parameters:

per_min (float) – A rate per minute.

Returns:

The formatted rate, or a dash when it is zero: an idle stage should read as idle, not as a precise zero.

Return type:

str

matmelt.ui.monitor.overview_panel(snap)[source]#

Panel 1 – headline numbers, a done-donut, and the state-distribution bar.

Parameters:

snap (MonitorSnapshot) – The snapshot to render.

Returns:

Headline numbers, a completion donut, and the state-distribution bar.

Return type:

rich.panel.Panel

matmelt.ui.monitor.pipeline_panel(snap)[source]#

Panel 2 – one row per configured stage, funnel order.

Parameters:

snap (MonitorSnapshot) – The snapshot to render.

Returns:

One row per configured stage, in funnel order.

Return type:

rich.panel.Panel

matmelt.ui.monitor.running_panel(snap)[source]#

Panel 3 – every material riding a live job right now.

Longest-running first (the snapshot’s order); capped at _RUNNING_LIMIT rows with an honest “+N more” line. An idle pipeline shows a dim note, not an empty table.

Parameters:

snap (MonitorSnapshot) – The snapshot to render.

Returns:

Every material riding a live job right now, longest-running first, with its stage, backend, elapsed time, try count and usage meters.

Return type:

rich.panel.Panel

matmelt.ui.monitor.successes_panel(snap)[source]#

Panel 4 – the newest finished (DONE) materials, the failures’ mirror.

Took is the material’s whole ride (created -> finished), the number a screening campaign actually cares about per material.

Parameters:

snap (MonitorSnapshot) – The snapshot to render.

Returns:

The newest finished materials, with how long each ride took.

Return type:

rich.panel.Panel

matmelt.ui.monitor.failures_panel(snap)[source]#

Panel 3 – the newest terminal failures, or a green all-clear.

Parameters:

snap (MonitorSnapshot) – The snapshot to render.

Returns:

The newest terminal failures, or a green all-clear when there are none.

Return type:

rich.panel.Panel

matmelt.ui.monitor.tracked_panel(snap)[source]#

Panel 3 (tracked mode) – one row per --track-ed material.

Rendered instead of the failures panel whenever snap.tracked is not None (failures stay reachable via status). A found=False row keeps its id visible with a dim-red “not found” – a typo must be diagnosable from the dashboard itself. All user-data cells go through Text/escape so ids and reasons never execute as Rich markup (the module-docstring lesson).

Parameters:

snap (MonitorSnapshot) – The snapshot to render.

Returns:

One row per tracked material, in the order the user named them, including honest rows for ids the store does not know.

Return type:

rich.panel.Panel

matmelt.ui.monitor.render_snapshot(snap)[source]#

The full dashboard, stacked: overview, pipeline, running now, recent completions, then the mode-dependent last panel – tracked materials when the snapshot carries tracked rows (the --track flow, even an empty tuple), recent failures otherwise.

Parameters:

snap (MonitorSnapshot) – The snapshot to render.

Returns:

The stacked dashboard. The third panel is the tracked table when tracking was requested, and recent failures otherwise.

Return type:

RenderableType

matmelt.ui.monitor.print_snapshot(snap)[source]#

The --once path: render the dashboard to stdout a single time.

Parameters:

snap (MonitorSnapshot) – The snapshot to print.

Return type:

None

The monitor’s live loop: a Rich Live context around a build callback.

The tiny UI-layer seam between the monitor command and Rich’s Live display – it exists so that all Rich usage stays inside ui/ (the one-console rule) while the command layer keeps the control flow: what to render each tick (the build callback), how often, and when to stop. This module never queries state, never sleeps forever on its own terms, and never decides what the dashboard contains.

Terminal safety: the loop runs inside with Live(...) so that Live.__exit__ always restores the terminal – a Ctrl-C mid-refresh must never leave the screen in Live’s grip. KeyboardInterrupt therefore propagates out of this function (after the context manager has cleaned up); the calling command catches it and turns it into a clean exit, so the user sees a short note, never a traceback.

matmelt.ui.monitor_live.run_live(build, *, interval_s, max_ticks=None)[source]#

Render a renderable under Rich Live, refreshing on an interval.

screen=False is deliberate: the stacked panels redraw in place and scroll away naturally when the loop ends, leaving the last frame visible in the terminal history instead of wiping it.

Parameters:
  • build (Callable[[], ConsoleRenderable | RichCast | str]) – Called for every frame. The first call happens before the display starts, so the user sees a dashboard immediately rather than a blank frame.

  • interval_s (float) – Seconds to sleep between updates.

  • max_ticks (int | None, default: None) – Bound the number of updates after the initial render. The testing knob; None runs until interrupted.

Raises:

KeyboardInterrupt – Propagated to the caller, as is any exception from build – but only after Live.__exit__ has restored the terminal, which is the whole point of the with block.

Return type:

None