Brief architecture overview for understanding the codebase structure.
DarkDraw is a VisiData plugin that treats art as data. The drawing you see is a visual representation of structured data you can manipulate directly.
File: drawing.py (~1200 lines)
JSON-based data layer storing drawing elements as rows.
Row structure:
{
'id': str, # unique identifier
'type': str, # '', 'frame', 'group', 'ref'
'x': int, 'y': int, # position
'text': str, # character to display
'color': str, # 'fg on bg' format
'tags': list, # arbitrary tags
'group': str, # group membership
'frame': str, # frame(s) element appears in
'rows': list # child elements (for groups)
}Key methods:
addRow(row)- add element with undo supportiterdeep(rows)- recursively traverse groupsgroup_selected(name)- create group from selection
Extends VisiData's TextCanvas. Renders DrawingSheet data to terminal.
Responsibilities:
- Cursor management and movement
- Element rendering with colors
- Selection handling
- Animation frame display
- Viewport scrolling
Key properties:
cursorBox- current cursor boundsselectedRows- selected elementscurrentFrame- active animation frame
Manages animation frames as a sheet.
Columns: type, id, duration_ms, x, y
- Element: Single string rendered at (x,y) position with (one row)
- Group: Container element (type='group') with child rows
- Frame: Animation frame object (type='frame'; other elements frame='frame')
- Origin (0,0) at top-left
- X increases rightward
- Y increases downward
- Terminal-based coordinates
Format: '[attr] [fg] on [bg]'
- Colors: named (red, blue) or numeric (0-255)
- Attributes: bold, underline
- Example:
'bold red on black'
- Elements can be grouped
- Groups can contain groups (recursive)
- Groups have relative coordinates
iterdeep()flattens hierarchy with absolute positions
- Frames are special rows (type='frame')
- Elements tagged with frame ID(s)
- No frame id on element means always display (background/base frame)
- Element can appear in multiple frames
- Frame has duration_ms for playback timing
- Type='ref' creates instance of a group
- References have position offset
- Enables reuse without duplication
DarkDraw extends VisiData with:
- Sheet types: DrawingSheet, FramesSheet
- Canvas type: Drawing
- Loaders: .ddw, .dur, .scr
- Savers: .ddw, .png, .gif, .ansihtml
- Commands: ~100 keybindings for drawing operations
- Menus: Hierarchical menu structure
User Input → Drawing (canvas)
↓
Modifies DrawingSheet (data)
↓
Triggers re-render
↓
Drawing displays updated state
Native .ddw format is JSON Lines:
{"x": 5, "y": 3, "text": "A", "color": "red", "id": "e1", "type": ""}
{"x": 6, "y": 3, "text": "B", "color": "blue", "id": "e2", "type": ""}
{"type": "frame", "id": "0", "duration_ms": 100}Each line is a complete JSON object. Easily parsed, human-readable, git-friendly.
Add new capabilities by:
- New commands via
Drawing.addCommand() - New methods via
@Drawing.apidecorator - New file formats via
vd.open_xyz()andvd.save_xyz() - New menu items via
vd.addMenuItems()
@drawcache_propertyfor expensive computationsvd.clearCaches()invalidates the drawcache, which may be necessary after data changes asynchronously