- name
- manage-annotations
- description
- MUST READ before creating, modifying, or querying annotations. Contains parameter names, coordinate model, and API quirks.
<!-- Generated by Embody/Envoy - Do not remove this comment - sha:ba9799e3ea1e5758 -->
# Manage Annotations Workflow
## Creating Annotations
```
create_annotation(
parent_path='/path/to/network',
mode='annotate', # 'annotate' (title bar), 'comment', or 'networkbox'
title='My Group',
text='Description of this group',
x=..., y=..., width=..., height=...,
color=[r, g, b], # Optional, floats 0-1
opacity=0.5 # Optional
)
```
**Always favor annotations over OP comments** for documenting operators or groups.
## Enclosing Operators
To create an annotation that encloses a group of operators:
1. **Get positions**: `get_network_layout` on the parent COMP (includes all operator positions and bounding box)
2. **Calculate bounding box**: Find `min_x`, `max_x`, `min_y`, `max_y` (max includes operator width/height: `max_x = max(op_x + op_w)`, `max_y = max(op_y + op_h)`)
3. **Add padding**: 70 units on left/right/bottom, **170 units on top** (title bar + body text)
4. **Set coordinates**:
- `nodeX = min_x - 70`
- `nodeY = min_y - 70` (BELOW operators, not above!)
- `nodeWidth = max_x - min_x + 140`
- `nodeHeight = max_y - min_y + 240` (70 bottom + 170 top)
## Coordinate Model
- `nodeX`/`nodeY` = **bottom-left corner**, width/height extend rightward and upward
- Title bar renders at the **top** of the rectangle
- **Common mistake**: Setting `nodeY` above the operators. `nodeY` must be BELOW (less than) the lowest operator's Y.
## Querying
- `get_annotations` - list all annotations in a COMP with properties and enclosed operators
- `get_enclosed_ops` - get operators enclosed by an annotation, or annotations enclosing an operator
- `set_annotation` - modify text, title, color, opacity, position, or size
## Deleting Annotations
**Delete via `delete_op`, never via raw `.destroy()` in `execute_python`.**
`delete_op` resolves utility annotations, purges any tracking, and arms an
auto-save checkpoint that re-exports the parent TDXN COMP's `.tdxn` without
the annotation -- the deletion is durable. A raw `.destroy()` leaves the
stale `annotations:` entry in the parent's `.tdxn` on disk, and the next
reimport of that COMP (import_network, manager Reload, or cold open)
resurrects the annotation with its pre-delete text.
## `annotateCOMP` Quirks
- **`utility` is `True` for every annotation** -- TD UI-drawn ones are born
that way, `create_annotation` sets it, and TDXN import applies it on every
annotation it recreates. (A bare Python `parent.create('annotateCOMP')`
is `utility=False` -- set `ann.utility = True` immediately to match; a
non-utility annotation is an ordinary COMP subtree that enumeration
sweeps will walk into.)
- **`utility=True` hides the op from `op()`, `parent.op()`, AND
`.children`** -- only `findChildren(includeUtility=True)` sees it, and a
deep `findChildren` does not even DESCEND into a utility annotate's
subtree unless `includeUtility=True` is passed (verified live, TD
2025.33070). Paths THROUGH a utility annotate to its interior ops still
resolve via `op()`.
- **Every Envoy op-path tool resolves utility annotations** (`delete_op`,
`set_parameter`, `set_op_position`, `get_op`, ... -- they share one
utility-aware resolver). For DISCOVERY, use `get_annotations` (always
sees them) or pass `include_utility=True` to
`query_network`/`find_children` -- with the default `False`, annotations
are invisible in those listings.
- **Annotations are never externalized per-op** -- they round-trip through
the parent TDXN COMP's semantic `annotations:` section. `externalize_op`
refuses them, and tagging sweeps skip them and their internals (the
widget internals are TD-managed stock content cloned from TDAnnotate).
- **`envoy_bot_*` is reserved for Embot**, the mascot Envoy stands on the
operator it is working on (the `Embot` parameter). The read tools hide
those parts and report how many as `embot_hidden`; Embody strips them
from every saved file and deletes loose ones on save. Never create,
move, edit or delete an annotation with that prefix -- and never name
one of yours that way, or it will be deleted as an artifact.
- `.type` returns `'annotate'` (not `'annotateCOMP'`)
- `findChildren(type=annotateCOMP)` requires the class object, not the string
- Cannot be reliably renamed after creation (TD also ignores a name passed
at create time -- rename right after creating instead)
Ver en GitHub