{% import "partials/icons.html" as ic %} {# Metadata-driven form controls. Every control is chosen from a FieldSpec's `kind`, never from the primitive's type, so registering a new action / condition / constraint in Python gives it a working inspector with no change to this file. #} {% macro control(spec, value, document, prefix='', draft=none, params={}) %} {%- set name = prefix ~ spec.name -%} {%- set current = spec.default if value is none else value -%} {% if spec.kind == 'bool' %} {% elif spec.kind == 'select' %} {% elif spec.kind == 'entity' %} {% elif spec.kind in ('lanelet', 'lanelet_list') %} {# The readout only. Its map lives in a modal that `partials/lanelet_search.html` renders outside this form -- the choice between a pinned lanelet and a constraint search is made of forms, and a form cannot nest. Anything rendering `fields()` renders `modals()` too. #} {{ lanelet_field(name, current, draft, many=(spec.kind == 'lanelet_list')) }} {% elif spec.kind == 'action' %} {# Actions are listed by track, because "which one is Cut in" is a question about whose track it sits on as much as about its title. #} {% elif spec.kind == 'signal_controller' %} {# The document already says which junctions it runs. Offering them beats a text box whose only feedback is a validation error after the fact. #} {%- set declared = document.map.traffic_signal_controllers -%} {%- set names = [] -%} {%- for controller in declared %}{% set _ = names.append(controller.name) %}{% endfor -%} {% elif spec.kind == 'signal_phase' %} {# Scoped to the controller the card names, because a phase only means anything inside one. The form re-renders on change, so choosing a controller refills this list. #} {%- set chosen = params.get('controller') -%} {%- set declared = document.map.traffic_signal_controllers -%} {%- set scoped = declared|selectattr('name', 'equalto', chosen)|list if chosen else declared -%} {%- set names = [] -%} {%- for controller in scoped %}{% for phase in controller.phases %}{% set _ = names.append(phase.name) %}{% endfor %}{% endfor -%} {% elif spec.kind in ('int_list', 'int_list_or_ref') %} {% elif spec.kind == 'trajectory' %} {# Rows of data rather than one value: a recording transcribes to hundreds of vertices, so they are edited as text, one per line, and the line above says what the text adds up to. #}
{{ trajectory_summary(current) }}
{% elif spec.kind == 'int' %} {% elif spec.kind == 'number' %} {% else %} {% endif %} {% endmacro %} {# The readout and the button that opens the map. Split from the modal so a caller can put the two in different places: the modal holds forms -- the spawn's constraint tree -- and a form cannot sit inside the inspector's own form. The halves find each other by id, not by being adjacent. #} {% macro lanelet_field(name, current, draft, many=false) %} {%- set shown = (current if current is string else (current or [])|join(', ')) if many else ('' if current is none else current) -%} {# The map is the only way to set a lanelet: the number is a readout, not an input. Nobody knows lanelet ids by heart, so typing one is guesswork that the map answers exactly -- and making the map the single editor means there is one path a value can arrive by, not two to keep in step. There is no "type it instead" fallback for a viewer that will not load, because the page fetches htmx from a CDN too: an editor that cannot reach the network has no working controls at all, so the fallback would only ever run when nothing else does. #}The Lanelet2 map viewer could not be loaded, so there is nothing to pick from. Check that this scenario's Lanelet2 file path is right.
{{ spec.help }}
{% endif %}