exlab_wizard.ui.pages.templates#

Template manager page (Frontend Spec §4 step 2, §5 step 2).

Two operations the wizards depend on:

  • list_templates() – scan config.paths.templates_dir for Copier templates (directories containing a copier.yml) and return a small summary per template. The project / run wizards call this to populate their “pick a template” step.

  • create_template() – scaffold a new minimal Copier template under templates_dir: a copier.yml carrying the _exlab_* manifest keys plus one rendered content file. The result is immediately loadable by TemplateEngine.

list_templates / create_template are pure (no NiceGUI) so they are unit-testable; render_template_manager() is the NiceGUI view.

Functions

create_template(templates_dir, *, name, ...)

Scaffold a new minimal Copier template under templates_dir.

list_templates(templates_dir, *[, template_type])

Return the templates under templates_dir, optionally filtered.

render_question_field(question, answers, *, ...)

Render one Copier question as a bound NiceGUI widget.

render_template_manager(*, templates[, ...])

Render the template manager: existing-template list + create form.

Classes

TemplateSummary(name, path, template_type, ...)

One row in the template list.

class exlab_wizard.ui.pages.templates.TemplateQuestion(key, kind, default=None, choices=(), help='', secret=False)[source]#

Bases: object

One Copier question parsed from a template’s copier.yml.

kind is normalised to the widget family the wizard renders: str / int / float / bool / choice. choices is populated only for choice questions. secret flags a password-style str input.

Parameters:
choices: tuple[Any, ...] = ()#
default: Any = None#
help: str = ''#
key: str#
kind: str#
secret: bool = False#
class exlab_wizard.ui.pages.templates.TemplateSummary(name, path, template_type, run_scope, description)[source]#

Bases: object

One row in the template list.

name is the template directory name (what the wizards store as selected_template); path is its absolute location; template_type / run_scope / description come from the _exlab_* keys in copier.yml.

Parameters:
description: str#
name: str#
path: Path#
run_scope: str | None#
template_type: str#
exlab_wizard.ui.pages.templates.create_template(templates_dir, *, name, template_type, description='', run_scope=None)[source]#

Scaffold a new minimal Copier template under templates_dir.

Writes <templates_dir>/<name>/copier.yml plus one content file. Returns the new template’s root directory. Raises ValueError on an empty / duplicate name, an unknown template_type, or a run template missing its run_scope.

The scaffold logic lives in exlab_wizard.template.authoring.create_template_dir() (the non-UI authoring service); this thin wrapper preserves the historical page-level signature and is imported lazily to avoid an import cycle.

Parameters:
Return type:

Path

exlab_wizard.ui.pages.templates.list_templates(templates_dir, *, template_type=None)[source]#

Return the templates under templates_dir, optionally filtered.

A template is any immediate sub-directory containing a copier.yml. Malformed manifests are skipped with a WARN rather than failing the whole scan. When template_type is given, only templates whose _exlab_type matches are returned.

Parameters:
Return type:

list[TemplateSummary]

exlab_wizard.ui.pages.templates.render_question_field(question, answers, *, testid_prefix)[source]#

Render one Copier question as a bound NiceGUI widget.

The widget two-way binds into answers[question.key]; the entry is seeded with the question’s default so a never-touched field still contributes its default to the render. testid_prefix namespaces the data-testid (f"{prefix}-{key}").

Parameters:
Return type:

None

exlab_wizard.ui.pages.templates.render_template_manager(*, templates, on_create=None, on_back=None, on_edit=None, locations=None, on_location_change=None)[source]#

Render the template manager: existing-template list + create form.

on_create is invoked with (name, template_type, description, run_scope) when the operator submits the create form; run_scope is None for non-run templates.

on_edit (optional) is invoked with a template name when the operator clicks that row’s Edit button – the caller routes it to the template editor page.

locations (optional) is a [(label, value), ...] scope list (at minimum ("Global", ...) plus one entry per configured equipment / project); when given, a selector is rendered at the top and on_location_change(value) is invoked when the operator switches scope (the caller re-renders the manager scoped to that location). All three new parameters are optional and default None so existing callers keep working unchanged.

Parameters:
Return type:

Any

exlab_wizard.ui.pages.templates.template_questions(raw_manifest)[source]#

Parse the operator-answerable questions out of a copier.yml body.

Handles both Copier question forms:

  • long formkey: {type: ..., default: ..., choices: ...}

  • short formkey: <scalar> (the scalar is the default; the type is inferred from it)

_-prefixed keys (Copier / _exlab_* metadata) are skipped. Questions carrying a when clause are still returned – the wizard renders them unconditionally for v1.

Parameters:

raw_manifest (dict[str, Any])

Return type:

list[TemplateQuestion]