Table of Contents

List Command Panel

A ListCommand is the container command: an optional title plus a list of enable-wrapped commands. Running one walks CommandEntryList top-down and skips a disabled entry together with everything nested under it — the wrapper yields nothing while its flag is clear. The mission's own command is always a list, so the Mission branch root is a list, and every nested one is the same shape one level down.

List is an ordinary catalog kind — category Flow — so a list is added the way any command is, and adding one grows a sub-tree: the node's children are its own entries, edited on their own Control Tree rows, and the list's own panel holds only its optional title, beneath the Disabled Caption while the command is unticked.

This page covers what a list is, how its title reads, and what moving commands in and out of one costs. The Control Tree row affordances every list shares — Add Command, the row buttons, the three drag landings — are documented once, on Mission Root Panel.

Key Models

The Panel of a List Entry

Selecting a list command in the Control Tree opens, top to bottom:

  • List Entry Panel
    • Disabled Caption
      • Present only while the command is disabled: skipped during play, still editable.
    • Title (optional) Input

Nothing else is on the panel. The list's entries are the node's children in the tree, and the operations that rewrite the parent list — Move up, Move down, Duplicate, Delete — are the buttons on the list's own tree row, revealed on hover and on the selected row, and always shown on a touch or pen device, where there is no hover; Delete asks for confirmation in a dialog naming the command. The + on that row adds a command into this list.

The Title input holds the raw title — empty when unset — while the label the tree row shows is composed by the engine: List when the title is blank, List [title] when it is not. A titled list still says what it is. A whitespace-only title counts as unset and is never written into the project file: it reloads as the empty title, and the label reads List both before and after, so a save cannot come back as a different label on the next load.

Keystrokes debounce into one title save. The same dirty gate flushes the pending save — and awaits a save already on the wire — on two occasions: before every Control Tree row op, whose rebuild remounts the panel and can shift the entry paths, and before a selection change, which switches the panel away; unmount cancels it, so a late write cannot land on whichever command slides into the old path. A flush whose save fails refuses the op or the switch, and a refused op says so in a notice; a save that was already on the wire when the gate ran is only waited for — its failure raises a notice of its own, and the op then proceeds regardless, leaving the title unsaved. Once it is saved the tree label refreshes in place rather than by rebuilding the branch, which would remount the open editor mid-edit.

The root list is the one list without a title input: the branch root reads Mission.

Enabling, and What a Disabled List Skips

The enable switch is the checkbox on the command's own Control Tree row, not a control on its panel and not one of the row's buttons. It decides only whether the command runs: while it is clear the tree row dims, and the editor stays open and fully editable. The checkboxes are read-only while a mission row op, or a tick's own writes, are in flight. Unticking a list dims its whole sub-tree — the dim rides ancestor propagation in the tree while each nested entry keeps its own flag, which is exactly what the run does: a disabled entry is skipped with everything under it.

Paths

A command node's key is its Mission API path — "0", "1", nested "0.2" for entry 2 inside the list at index 0. Every structural change re-mints them: deleting entry 2 slides the next command into path "2". Each branch build therefore stamps its nodes, so the editor panel remounts onto the new path instead of going on showing a command the path no longer names. The host carries its own index-based ids — the expansion set, the Add focus and the selection — through each row op, so they follow the rows rather than the slots: a selection riding on the acted-on row follows it — the stepped or dropped row at its new index, or the clone a Duplicate makes — except on Delete, where no row remains to follow: it lands on the nearest remaining entry of that list, the index clamped to the new count, or on the owning list node once the list is empty, where Add Command waits. Any other selection keeps its own re-indexed row.

Moving Commands In and Out

  • Into a list — drag a command's tree row — the whole row drags; the handle icon at its left only marks it as draggable — onto the middle band of a list row; that row's outer quarters still reorder around it. The Mission root row is all middle band: a drop anywhere on it lands in the root list. Either way the entry lands at that list's tail.
  • Out of a list — drop the row on the edge of a command row in another list, before or after it; the lower quarter of the list command's own row lands the entry right after the list in the owning list.

Which call a landing makes depends on whether the entry changes list, not on where the pointer sat. A landing that changes the list — the middle band of a list the entry is not in, or an edge in another list — is the reparent call; a landing that keeps the list — an edge in the entry's own list, or the middle band of the list row (the Mission root row included) that already holds it, which sends it to the tail — is the reorder call, sent as the list's whole new order. The server resolves the source list and the target list to object references before it mutates either, so the index shift the removal causes cannot misroute the insert, and it refuses to move a list into itself or into one of its own descendants. That guard compares identity rather than path-string prefixes, because "02", "+2" and " 2" all parse to index 2.

Two more properties of a list follow from the same API:

  • A new command lands right after the focused command when that command is a direct entry of the list the + belongs to, root or nested, and at that list's tail otherwise. The focus is one for the whole Mission tree — the last selected or last added command, whichever list it sits in — so each list row's +, scoped to its own list, ignores a focus that lies in another list. Nothing is pinned; the order is entirely the user's, changed with the row's Move up / Move down buttons or by dragging.
  • Duplicate is a deep copy. The entry is cloned through the same XML round-trip the project file uses, so a nested list copies with its whole sub-tree, and the clone lands right after the source.

Dragging serves rearrangement only: a row accepts a drop only while a mission row is being dragged, so a drag carrying text or files keeps the browser's no-drop cursor. To bring several program files in at once, open a Program File command's picker and pick them together — the first pick lands on that command and each further pick becomes another Program File command right after it, in pick order, inside the same list at any depth.

Which Commands a List Can Hold

The addable set is the server's command catalog: every engine command carrying [CommandCatalog], reflected into the picker Add Command opens, which searches by display label, kind key and alias and takes one pick per opening. List is one of those commands, which is what makes nesting an ordinary act rather than a special case. The frontend contributes an icon per kind, and its own label where it has one; the server supplies the localized label otherwise, so a newly attributed engine command becomes addable with no frontend change.

A command without that attribute stays loadable from a project file but is never offered for creation. General Config is the one such command with an editor of its own: it is the legacy settings bundle, so a project file storing one loads as the individual setting commands it stands for and saving never writes the bundle back — editable when a project still constructs one through the API, and absent from the catalog.

Selecting a command's tree row opens that command's editor on its node — the kind's own panel when one exists, otherwise a generic editor built from the scalar fields the command declares. Selection is one row at a time: there is no multi-selection here, and every row button acts on the row that carries it, whether or not that row is the selected one. Because the server composes every entry label, a command carrying no title of its own still reads as its localized display name.

A list's entries never appear in the editor pane: they are the node's children in the Control Tree, and the pane below the tree shows only the selected command's panel. The left-most of the Execution page's three draggable vertical dividers sits between the left dock — the Control Tree over the editor panel — and the central area; the divider inside that dock drags the height between the tree and the editor.

Source Code Path

See HiNC App Anatomy for git repository links.

Web Application

  • wwwroot-src/src/components/controlTree/MissionCommandSlavePanel.vue — a list entry's panel: the disabled caption, the optional Title input with its debounce / flush / cancel discipline and the dirty gate it answers to the host, and the bespoke-or-generic editor choice for every other kind.
  • wwwroot-src/src/components/controlTree/ControlTreePanel.vue — the tree rows: the draggable row with its handle icon, the Move up / Move down / Duplicate / Delete buttons on every command row (revealed on hover and on the selected row, always shown on a touch or pen device), the + on every list row (the Mission root included) and the Add Command dialog it opens, and the drop-indicator styles.
  • wwwroot-src/src/components/controlTree/useMissionTreeDrag.ts — the drag bookkeeping between mission rows: which rows drag, which receive, and the before / after / into landing read from the pointer's position on the row.
  • wwwroot-src/src/components/controlTree/MissionRootPanel.vue — the branch root's panel, the one without a title input: the command count and the run-order hint.
  • wwwroot-src/src/components/controlTree/AddCommandDialog.vue — the search-first catalog picker; one pick per opening.
  • wwwroot-src/src/components/controlTree/missionItemTypes.ts — the child builders that recurse into a list's own entries, the per-kind editor override map, and the per-build stamp that forces the panel remount.
  • wwwroot-src/src/components/controlTree/missionKindIcons.ts — the kind and category icons the picker shows.
  • wwwroot-src/src/components/controlTree/useControlTreeHost.ts — the five row ops — add, drag move, step, duplicate, delete — through one missionRowOp helper (the busy gate, the open panel's dirty flush, the API call, the scoped rebuild, the post-op selection), the insert-after-focus rule scoped to the +'s own list, and the index translation that carries the expansion set, the Add focus and the selection through each op; which nodes show a checkbox, the dim that flows down a disabled command's sub-tree, the enable write, and the in-place label refresh after a title edit or a language switch.
  • wwwroot-src/src/api/mission.ts — the typed wrapper over the Mission API and the path convention (root or empty = the root list, 0.2 = a nested entry); addEntry takes the optional insert index and resolves to the new entry's path.
  • wwwroot-src/src/components/mission/GenericCommandPanel.vue — the editor a catalog kind gets when it ships no bespoke panel, built from the command's declared scalar fields.
  • wwwroot-src/src/components/mission/NcFileCommandPanel.vue — the multi-pick file dialog: the first pick lands on the command, each further pick becomes another Program File command right after it.
  • wwwroot-src/src/pages/ExecutionPage.vue — the page-level splitters; the left-most parts the left dock from the central area.
  • wwwroot-src/src/components/controlTree/ControlTreeDock.vue — the left dock's two rows and the height divider between the tree and the editor.
  • wwwroot-src/src/i18n/en/tree.ts — the tree.mission.* strings: the kind names, the run-order hint that names the row affordances, the disabled hint, the cancelled-op notice and the operation labels.
  • wwwroot-src/src/i18n/en/common.ts — the row buttons' titles and the Title (optional) label.
  • wwwroot-src/src/i18n/en/dialog.ts — the Delete Command confirmation.
  • Missions/MissionController.cs — the entry lifecycle: GET list-command/entries (recursive), POST list-command/entries and POST list-command/entries/{path} (into the root or a nested list, at an optional insertIndex clamped to the list and otherwise at its tail, answering the new entry's index and path), DELETE list-command/entries/{path}, POST list-command/entries/{path}/move (the row's Move up / Move down), PUT list-command/reorder (a drop that keeps the owning list), POST list-command/entries/{path}/duplicate (the XML-round-trip deep clone), POST list-command/entries/{path}/reparent (a drop that changes the owning list, with the self/descendant guard), PUT commands/{path}/listcommand for the title, and PUT commands/{path}/enabled for an entry's enable flag. GetCommandTitle composes every entry label the tree shows.
  • Missions/MissionCommandCatalog.cs — reflects the [CommandCatalog] commands into the addable set and constructs the picked kind.

HiAPI Engine

  • HiNc/SessionCommands/ListCommand.cs — the container: the optional title, the entry list a run walks top-down, the whitespace-only-title-is-unset XML rule, the Flow catalog attribute, and the List / List [title] composition.
  • HiNc/SessionCommands/EnablingWrapper.cs — the enable flag plus the wrapped command that together make one entry.
  • HiNc/SessionCommands/ITitleCommand.cs — the interface a command implements to compose its own title from the caller's vocabulary.
  • HiNc/SessionCommands/CommandCatalogAttribute.cs — CommandCategory and the attribute (category, order, kind key, aliases) that makes the addable set backend-owned.
  • HiNc/SessionCommands/PreSettingCommand.cs — the General Config bundle: no catalog attribute, and a loaded project expands a stored one into the split setting commands.
  • HiNc/MachiningProcs/MachiningProject.cs — declares the mission's command and keeps it a list.

See Also

  • Mission Root Panel — the branch root, and the Control Tree row affordances every list shares, documented once
  • Building a Mission — the task this command serves: grouping a mission and moving entries between groups
  • Mission — the rest of the command panels