Skip to content

Plugin Documentation

Displaymotron Core

Displaymotron intercepts markdown image syntax ![alt](path){attrs} and renders different content based on path type:

Path Type Handler
Directory Gallery module
.mp4, .webm, etc. Video module
.stl, .dxf Model3D module
Image + figure attrs Figure module
Text file Textfile module
Binary file Textfile (hex preview)

Centralized Extensions (extensions.py)

IMAGE_EXTENSIONS = {".png", ".jpg", ".jpeg", ".gif", ".svg", ".webp", ".bmp"}
VIDEO_EXTENSIONS = {".mp4", ".webm", ".ogg", ".mov", ".avi", ".mkv"}
MODEL_EXTENSIONS = {".stl", ".dxf"}

Plugin Lifecycle

on_config        → Register JS files
on_page_markdown → Process all ![](path) patterns
on_post_page     → Inject CSS for used features
on_post_build    → Copy JS files to site

Page Meta Flags

Each feature sets a flag in page.meta to enable conditional CSS injection:

  • displaymotron_video
  • displaymotron_3d
  • displaymotron_text
  • displaymotron_gallery
  • displaymotron_figure
  • displaymotron_columnizer

Renders directory contents as masonry image gallery using CSS columns.

Syntax

![alt text](path/to/directory/){cols=3 orderby=name}

Attributes

Attribute Description
cols=N Number of columns (default: 3)
width=N% Column width percentage (alternative to cols)
orderby=date\|name Sort order (default: name with natural sort)

Flow Diagram

@startuml
start
:Parse attributes (cols, orderby, width);
:Scan directory for images/videos;
:Sort files (natural or date);
:Get image dimensions for aspect ratios;
:Determine container width;
note right
  From columnizer data-width-em
  or default 70em
end note
:Compute column reductions;
:Generate CSS with container queries;
:Build HTML gallery div;
stop
@enduml

Responsive Column Reduction

Galleries reduce columns as container narrows. Breakpoints use cascading ratios relative to container width:

starting_width = container_width_em (or 70em default)
reduction_ratio = 0.6

For 3-col gallery at 70em:
  3→2 at 70 × 0.6 = 42em
  2→1 at 42 × 0.6 = 25.2em

Each breakpoint is relative to the previous, creating proportional spacing.

Container Query Integration

Galleries detect their container via data-width-em attribute on columnizer columns:

@container displaymotron-col (max-width: 42.0em) {
  #gallery-abc123 { column-count: 2; }
}

For galleries outside columns, uses @container content instead.


Columnizer Module

Creates multi-column layouts using curly brace syntax.

Syntax

<div class="displaymotron-columns" markdown="1">
<div class="displaymotron-column" data-width-em="46.2" style="flex: 0 0 calc(66.0% - 0.5em);" markdown="1">

First column content

</div>
<div class="displaymotron-column" data-width-em="11.9" style="flex: 0 0 calc(17.0% - 0.5em);" markdown="1">

Second column content

</div>
<div class="displaymotron-column" data-width-em="11.9" style="flex: 0 0 calc(17.0% - 0.5em);" markdown="1">

Third column content

</div>
</div>

Width Calculation

  1. Explicit widths sum up
  2. Remaining percentage divided among auto columns
  3. Each column gets data-width-em attribute: 70 × width_pct / 100

Flow Diagram

@startuml
start
:Parse markdown line by line;
:Detect opening brace {;
:Track brace depth;
:Accumulate block content;
:On depth=0, extract content;
:Parse [width=X%] arguments;
:Continue until chain breaks;
note right
  Chain breaks on:
  - Blank line
  - Non-brace content
  - Less than 2 blocks
end note
:Calculate column widths;
:Generate HTML with flex layout;
:Add container-type: inline-size;
stop
@enduml

Generated HTML

<div class="displaymotron-columns" markdown="1">
  <div class="displaymotron-column" data-width-em="46.2" 
       style="flex: 0 0 calc(66% - 0.5em);" markdown="1">
    Content...
  </div>
  <div class="displaymotron-column" data-width-em="23.8" 
       style="flex: 0 0 calc(34% - 0.5em);" markdown="1">
    Content...
  </div>
</div>

CSS Container Setup

.displaymotron-column {
  min-width: 0;
  container-type: inline-size;
  container-name: displaymotron-col;
}

Figure Module

Renders images with captions, supporting rows, columns, and inline floating.

Syntax

![alt](image.jpg){caption="My caption" align=left width=300}

Attributes

Attribute Values Description
caption string Caption text
caption-width CSS value Max caption width
caption-align left/center/right Caption text alignment
align left/center/right Figure alignment/float
gridType row/col Layout direction for chains
normalizeHeights true/false Equal heights in row
normalizeWidths true/false Equal widths in column
aspectRatio fixed/stretch/zoom Image fit mode
rowHeight CSS value Fixed row height
colHeight CSS value Fixed column height

Figure Chains

Consecutive figures on same line form a chain:

![](a.jpg){gridType=row} ![](b.jpg) ![](c.jpg)

Flow Diagram

@startuml
start
:Parse figure attributes;
:Extract FigureConfig;
if (Multiple figures on line?) then (yes)
  :Build figure chain;
  :Apply hierarchical inheritance;
  :Group by gridType boundaries;
  if (gridType=row?) then (yes)
    :Calculate aspect ratios;
    :Set flex-grow per image;
    :Render horizontal row;
  else (col)
    :Calculate inverse ratios;
    :Set flex-grow per image;
    :Render vertical column;
  endif
else (single)
  if (Inline float?) then (yes)
    :Render with float: left/right;
  else (no)
    :Render centered figure;
  endif
endif
stop
@enduml

Inline Floating

Figures without blank line before them float alongside text:

Some text here.
![](image.jpg){caption="Float right" align=right width=200}
More text wraps around the image.

Height/Width Normalization

Row normalization uses aspect ratios for flex-grow:

# flex-grow = width / height (aspect ratio)
aspect_ratios = [img_width / img_height for img in images]

Column normalization uses inverse:

# flex-grow = height / width (inverse aspect ratio)
inverse_ratios = [img_height / img_width for img in images]

NarrowNav Plugin

Auto-calculates minimum navigation sidebar width based on nav item titles.

Configuration

plugins:
  - narrownav:
      char_width_em: 0.6      # Character width estimate
      indent_per_level_em: 1.2 # Indent per nesting level
      base_padding_em: 4.5    # Base padding (scrollbar, arrows)
      min_width_em: 8.0       # Minimum width
      max_width_em: 20.0      # Maximum width

Flow Diagram

@startuml
start
:on_nav hook receives Navigation object;
:Walk nav.items recursively;
:For each item:;
:  width = len(title) × char_width_em;
:  width += depth × indent_per_level_em;
:  width += base_padding_em;
:  track max_width;
:Clamp to min/max bounds;
:on_post_build writes CSS file;
:on_post_page injects CSS link;
stop
@enduml

Width Calculation

total_width = (len(title) * char_width_em) + 
              (depth * indent_per_level_em) + 
              base_padding_em

Generated CSS

/* Auto-generated by NarrowNav plugin */
.md-sidebar--primary {
    width: 14.2em;
}
.md-sidebar--primary .md-sidebar__scrollwrap {
    width: 14.2em;
}

When gallery is inside a columnizer column:

@startuml
participant Columnizer
participant Plugin
participant Gallery

Columnizer -> Plugin: Process {blocks}
Plugin -> Plugin: Calculate column widths
Plugin -> Plugin: Add data-width-em to HTML
Plugin -> Gallery: render(..., container_width_em=35.0)
Gallery -> Gallery: Compute breakpoints from 35em
Gallery -> Gallery: Generate @container displaymotron-col queries
@enduml

The data-width-em attribute bridges columnizer width to gallery breakpoint calculation, enabling proper responsive behavior when galleries are nested inside columns.