Dashboard

Component Markdown Visualizations

Five fenced block types for galaxy directives, Vega charts, visualizations, Vitessce dashboards

Raw
Revised:
2026-04-22
Revision:
2
Related Notes:
Component - Invocation Report to Pages

Galaxy Markdown Visualizations: Complete Reference

How visualizations are embedded, rendered, and extended in Galaxy’s markdown system.


1. Overview

Galaxy Markdown supports five fenced block types, dispatched by the SectionWrapper.vue router:

Block TagHandler ComponentPurpose
```galaxyMarkdownGalaxy.vueDataset, job, workflow directives with function-call syntax
```vegaMarkdownVega.vueInline Vega/Vega-Lite chart specs (JSON)
```visualizationMarkdownVisualization.vueGalaxy visualization plugin embeds (JSON config)
```vitessceMarkdownVitessce.vueSpatial/single-cell data dashboards (JSON config)
(default)MarkdownDefault.vueStandard markdown via markdown-it + KaTeX math

The first (galaxy) uses directive function-call syntax. The other three use JSON payloads parsed client-side. All visualization types support an expand/maximize button for fullscreen viewing.


2. Galaxy Directives (```galaxy blocks)

These are the structured directives — parsed by both backend (markdown_parse.py) and frontend (parse.ts). They use function-call syntax with named arguments.

Dataset Visualization Directives

DirectivePurposeKey Arguments
history_dataset_displayFull dataset contenthistory_dataset_id, hid, input, output, invocation_id
history_dataset_as_imageRender as image (PNG/SVG)Same + path
history_dataset_as_tableRender as HTML tableSame + path, title, compact, footer, show_column_headers
history_dataset_peekPre-computed content previewSame core args
history_dataset_infoDataset metadataSame core args
history_dataset_nameJust the dataset nameSame core args
history_dataset_typeJust the file formatSame core args
history_dataset_linkClickable download linkSame + path, label
history_dataset_indexFile listing (composite)Same + path
history_dataset_embeddedInline embedSame core args
history_dataset_collection_displayCollection contentshistory_dataset_collection_id, hid, input, output, invocation_id

Syntax Examples

Block directive (inside ```galaxy fence):

history_dataset_as_table(history_dataset_id=12345, title="Mapping Stats", compact=true)

Inline directive (in regular markdown text):

The alignment produced ${galaxy history_dataset_name(output="results")} with format ${galaxy history_dataset_type(output="results")}.

Workflow-relative references (resolved at render time via invocation context):

history_dataset_display(output="alignment_results", invocation_id=98765)

ID Formats

FormatExampleContext
Internal (storage)history_dataset_id=12345Raw DB IDs, stored in page revisions
Encoded (API/export)history_dataset_id=a1b2c3d4e5f6Base36-encoded for API responses
HID (notebook-relative)hid=3Accepted in validation, resolved by agent tools
Workflow-relativeoutput="bam_output"Resolved via invocation store

Backend Processing Pipeline

                    ready_galaxy_markdown_for_import()
Encoded IDs ──────────────────────────────────────────→ Internal IDs (storage)

                    validate_galaxy_markdown()
Markdown text ────────────────────────────────────────→ Syntax errors (line numbers)

                    resolve_invocation_markdown()
Workflow-relative ────────────────────────────────────→ Absolute dataset IDs

                    ready_galaxy_markdown_for_export()
Internal IDs ─────────────────────────────────────────→ Encoded IDs + expanded embeds

Two export handlers:

  • Lazy (ReadyForExportMarkdownDirectiveHandler): Preserves directives, collects metadata for frontend rendering. Used for interactive display.
  • Eager (ToBasicMarkdownDirectiveHandler): Expands directives to standard markdown/HTML. Used for PDF export via WeasyPrint.

Frontend Rendering

MarkdownGalaxy.vue dispatches to 22 specialized element components in Markdown/Sections/Elements/:

ComponentDirective
HistoryDatasetDisplay.vuehistory_dataset_display
HistoryDatasetAsImage.vuehistory_dataset_as_image
HistoryDatasetAsTable.vuehistory_dataset_as_table
HistoryDatasetDetails.vuehistory_dataset_peek, history_dataset_info
HistoryDatasetLink.vuehistory_dataset_link
HistoryDatasetIndex.vuehistory_dataset_index
HistoryDatasetCollection.vuehistory_dataset_collection_display
WorkflowDisplay.vueworkflow_display
WorkflowImage.vueworkflow_image
JobMetrics.vuejob_metrics
JobParameters.vuejob_parameters
ToolStd.vuetool_stdout, tool_stderr
InvocationTime.vueinvocation_time, generate_time

3. Vega-Lite Visualizations (```vega blocks)

Self-contained chart specifications using the Vega-Lite grammar. No plugin system needed — the spec IS the chart.

Syntax

```vega
{
    "$schema": "https://vega.github.io/schema/vega-lite/v5.json",
    "description": "Gene expression bar chart",
    "data": {
        "values": [
            {"gene": "TP53", "expression": 42.5},
            {"gene": "BRCA1", "expression": 38.2},
            {"gene": "EGFR", "expression": 55.1}
        ]
    },
    "mark": "bar",
    "encoding": {
        "x": {"field": "gene", "type": "nominal"},
        "y": {"field": "expression", "type": "quantitative"}
    }
}
```

Rendering Pipeline

MarkdownVega.vue
  │  JSON.parse(content)
  │  Inject width: "container" for responsive sizing

VegaWrapper.vue (client/src/components/Common/VegaWrapper.vue)
  │  vega-embed library with SVG renderer
  │  ResizeObserver for responsive width
  │  Cleanup via vegaView.finalize() on unmount

Rendered SVG chart

Supported Chart Types (via Vega-Lite)

Vega-Lite’s mark property supports all standard chart types:

Mark TypeDescription
barBar charts (vertical/horizontal)
lineLine charts, time series
pointScatter plots
areaArea charts, stacked areas
circleCircle plots
squareSquare plots
rectHeatmaps, 2D histograms
tickStrip plots
boxplotBox-and-whisker plots
ruleReference lines, error bars
textText labels
arcPie/donut charts
geoshapeGeographic maps
trailVariable-width lines
imageImage marks

Vega-Lite also supports layered, faceted, concatenated, and repeated views for complex dashboards.

Key Properties

// VegaWrapper.vue props
interface VisSpec {
    spec: VisualizationSpec;  // Full Vega or Vega-Lite spec
    fillWidth?: boolean;       // Auto-resize to container (default: true)
}
  • Renderer: SVG (not Canvas) for crisp rendering in markdown
  • Responsive: useResizeObserver on container updates vegaView.width()
  • Lifecycle: Chart finalized on component unmount (no memory leaks)

Example: Scatter Plot with Color Encoding

```vega
{
    "$schema": "https://vega.github.io/schema/vega-lite/v5.json",
    "data": {"url": "https://vega.github.io/vega-datasets/data/cars.json"},
    "mark": "point",
    "encoding": {
        "x": {"field": "Horsepower", "type": "quantitative"},
        "y": {"field": "Miles_per_Gallon", "type": "quantitative"},
        "color": {"field": "Origin", "type": "nominal"},
        "tooltip": [
            {"field": "Name", "type": "nominal"},
            {"field": "Horsepower", "type": "quantitative"},
            {"field": "Miles_per_Gallon", "type": "quantitative"}
        ]
    }
}
```

Example: Time Series

```vega
{
    "$schema": "https://vega.github.io/schema/vega-lite/v5.json",
    "data": {"url": "https://vega.github.io/vega-datasets/data/stocks.csv"},
    "mark": "line",
    "encoding": {
        "x": {"field": "date", "type": "temporal"},
        "y": {"field": "price", "type": "quantitative"},
        "color": {"field": "symbol", "type": "nominal"}
    }
}
```

4. Galaxy Plugin Visualizations (```visualization blocks)

The full plugin-based visualization framework. Plugins are external packages that run in sandboxed iframes with their own JS/CSS.

Syntax

```visualization
{
    "visualization_name": "charts",
    "visualization_title": "Quality Score Distribution",
    "dataset_id": "a1b2c3d4e5f6",
    "height": 400,
    "settings": {
        "chart_type": "bar",
        "x_axis_label": "Quality Score",
        "y_axis_label": "Count"
    },
    "tracks": [...]
}
```

Configuration Schema

// Parsed from JSON content by MarkdownVisualization.vue
interface VisualizationConfig {
    visualization_name: string;        // Plugin identifier (required)
    visualization_title?: string;      // Display title
    dataset_id?: string;               // Direct dataset reference
    dataset_url?: string;              // Alternative: URL to data
    dataset_label?: {                  // Workflow-relative reference
        invocation_id?: string;
        input?: string;                // Workflow input label
        output?: string;               // Workflow output label
    };
    dataset_name?: string;             // Human-readable dataset name
    height?: number;                   // Pixel height (default: 400)
    settings?: object;                 // Plugin-specific settings
    tracks?: object[];                 // Plugin-specific data tracks
}

Rendering Pipeline

MarkdownVisualization.vue
  │  JSON.parse(content)
  │  Resolve dataset_label via invocationStore (if workflow context)

VisualizationWrapper.vue
  │  Expand/collapse UI (fixed height ↔ fullscreen)
  │  Default height: 400px

VisualizationFrame.vue
  │  GET /api/plugins/{name} → plugin metadata
  │  Create <iframe>
  │  Inject <div id="app" data-incoming="{...}">
  │  Load plugin script (entry_point.attr.src)
  │  Load plugin CSS (entry_point.attr.css)
  │  Listen for postMessage from iframe

Plugin renders inside iframe

iframe Communication Protocol

The plugin receives data via data-incoming attribute on div#app:

{
    "root": "https://galaxy.example.org/",
    "visualization_config": {
        "dataset_id": "...",
        "settings": {...},
        "tracks": [...]
    },
    "visualization_plugin": { /* full plugin metadata */ },
    "visualization_title": "My Chart"
}

The plugin sends changes back via window.postMessage:

window.parent.postMessage({
    from: "galaxy-visualization",
    visualization_config: { /* updated config */ },
    visualization_title: "Updated Title"
}, "*");

Changes are debounced (300ms) and emitted as change events up the component tree.

Backend: Markdown Processing of Visualization Blocks

Visualization blocks are handled differently from galaxy blocks:

  1. Parsing: visualization is registered in VALID_ARGUMENTS with DYNAMIC_ARGUMENTS — validation is completely bypassed. Any JSON content is accepted.

  2. Export: handle_visualization() is a no-op in the lazy export handler — the block passes through unchanged for frontend rendering.

  3. PDF Export: Returns placeholder text "*Visualization inputs not implemented*" — interactive visualizations cannot be rendered to static PDF.

  4. Invocation IDs: A dedicated regex (VISUALIZATION_FENCED_BLOCK + INVOCATION_ID_JSON_PATTERN) finds and transforms "invocation_id": "..." values inside visualization JSON blocks during import/export. This is separate from the standard _remap_galaxy_markdown_calls system.

# markdown_util.py
VISUALIZATION_FENCED_BLOCK = re.compile(
    r"^```\s*visualization+\n\s*(.*?)^```", re.MULTILINE | re.DOTALL
)
INVOCATION_ID_JSON_PATTERN = re.compile(r'("invocation_id"\s*:\s*)"([^"]*)"')

def process_invocation_ids(f, workflow_markdown: str) -> str:
    """Find invocation_id values in visualization JSON blocks and apply f() to them."""

Used during:

  • ready_galaxy_markdown_for_import() — decode encoded IDs
  • ready_galaxy_markdown_for_export() — encode internal IDs
  • populate_invocation_markdown() — inject invocation.id

5. Plugin System Architecture

Plugin Discovery

Plugins are loaded from config/plugins/visualizations/ (and custom directories). The VisualizationsRegistry class discovers plugins via filesystem walk:

config/plugins/visualizations/
  └── example/
       └── static/
            ├── example.xml     ← Config (name must match directory)
            ├── script.js       ← Entry point
            └── logo.svg        ← Optional logo

A directory is a valid plugin if it contains static/{directory_name}.xml. Two-level nesting is supported.

Plugin XML Configuration

<?xml version="1.0" encoding="UTF-8"?>
<visualization name="Minimal Example" embeddable="true" hidden="false">
    <description>A visualization plugin description</description>

    <data_sources>
        <data_source>
            <model_class>HistoryDatasetAssociation</model_class>
            <test test_attr="ext">tabular</test>
        </data_source>
    </data_sources>

    <params>
        <param required="true">dataset_id</param>
    </params>

    <entry_point entry_point_type="script" src="script.js" css="styles.css" />

    <settings>
        <input>
            <name>chart_type</name>
            <help>Select chart type</help>
            <type>select</type>
        </input>
    </settings>

    <tracks>
        <input>
            <name>data_column</name>
            <help>Column to plot</help>
            <type>data_column</type>
        </input>
    </tracks>

    <specs>
        <ai_prompt>You are a charting assistant...</ai_prompt>
    </specs>

    <help format="markdown"><![CDATA[
    Plugin documentation in markdown format.
    ]]></help>
</visualization>

Key XML Attributes

ElementRequiredPurpose
name (attr)YesDisplay name
embeddable (attr)NoIf true, appears in markdown editor toolbar
hidden (attr)NoIf true, hidden from all UIs
disabled (attr)NoIf present, plugin is skipped entirely
data_sourcesYesWhich Galaxy object types are compatible
model_classYesHistoryDatasetAssociation, LibraryDatasetDatasetAssociation, or Visualization
testNoFilter by file attributes (e.g., ext=tabular)
paramsNoRequired/optional parameters
entry_pointYesJavaScript module or Mako template
settingsNoConfiguration inputs (axes, colors, etc.)
tracksNoData series/track definitions
specsNoCustom config (e.g., ai_prompt for LLM-assisted charting)

Plugins API

MethodPathPurpose
GET/api/plugins?embeddable=TrueList embeddable plugins for editor toolbar
GET/api/plugins?dataset_id=XList plugins compatible with a dataset
GET/api/plugins/{name}Get plugin metadata + entry point
GET/api/plugins/{name}?history_id=XList compatible datasets in a history
POST/api/plugins/{name}/chat/completionsLLM adapter using plugin’s ai_prompt spec

Editor Integration

The markdown editor queries GET /api/plugins?embeddable=True and generates toolbar entries:

// services.ts
export async function getVisualizations(): Promise<Array<TemplateEntry>> {
    const { data } = await axios.get(`${getAppRoot()}api/plugins?embeddable=True`);
    return data.map((v: VisualizationType) => ({
        title: v.html,
        description: v.description || "",
        logo: v.logo ? `${getAppRoot()}${v.logo}` : undefined,
        cell: {
            name: "visualization",
            configure: true,
            content: `{ "visualization_name": "${v.name}", "visualization_title": "${v.html}" }`,
        },
    }));
}

Clicking a toolbar entry inserts a ```visualization block with initial JSON config. The configure: true flag opens a configuration dialog.

Shipped Plugins

The Galaxy core repository ships only the example plugin (hidden by default). Real visualization plugins are distributed separately and installed into config/plugins/visualizations/. Known plugins from the Galaxy ecosystem include:

PluginDescriptionData Types
chartsBar, line, scatter, pie chartstabular, csv
tracksterGenome browserBAM, BED, BigWig, VCF
graphvizNetwork graphs (cytoscape.js)graphml, json
chiravizChimeric RNA visualizationtabular
csgChemical structure viewerSDF, MOL2, PDB
jupyterliteJupyterLite notebooksvarious

The Charts plugin (at charts.galaxyproject.org) provides the primary charting capability and is the most widely used visualization plugin.


6. Vitessce Visualizations (```vitessce blocks)

Spatial and single-cell data dashboards using the Vitessce framework.

Syntax

```vitessce
{
    "version": "1.0.16",
    "name": "Single-Cell Dashboard",
    "datasets": [
        {
            "uid": "A",
            "name": "My scRNA-seq",
            "files": [
                {
                    "__gx_dataset_id": "a1b2c3d4",
                    "fileType": "anndata.zarr"
                }
            ]
        }
    ],
    "coordinationSpace": {...},
    "layout": [...],
    "__gx_height": 600
}
```

Special Galaxy Properties

Vitessce configs use __gx_ prefixed properties that are resolved before rendering:

PropertyPurpose
__gx_dataset_idResolved to /api/datasets/{id}/display URL
__gx_dataset_labelResolved via invocation store (workflow context)
__gx_dataset_nameStripped (informational only)
__gx_heightExtracted as visualization height, removed from config

After resolution, the cleaned config is passed to the vitessce visualization plugin via VisualizationWrapperVisualizationFrame.


7. Saved Visualization Model

Galaxy also has a Saved Visualization model (Visualization + VisualizationRevision) for persisting visualization configurations outside of markdown:

class Visualization(Base):
    __tablename__ = "visualization"
    id, user_id, title, type, dbkey, slug
    deleted, importable, published
    latest_revision_id → VisualizationRevision
    revisions, tags, annotations, ratings, users_shared_with

class VisualizationRevision(Base):
    __tablename__ = "visualization_revision"
    id, visualization_id, title, dbkey
    config  # JSON blob with full visualization state

Saved visualizations can be shared, published, and annotated. They’re distinct from the inline markdown visualization blocks but use the same plugin infrastructure. A saved visualization’s type field corresponds to a plugin name.


8. Summary: When to Use What

NeedApproachSyntax
Quick inline chart with literal dataVega-Lite```vega + JSON spec
Chart from a Galaxy datasetPlugin visualization```visualization + JSON config
Dataset table/previewGalaxy directive```galaxy + history_dataset_as_table(...)
Dataset as rendered imageGalaxy directive```galaxy + history_dataset_as_image(...)
Genome browser viewPlugin (trackster)```visualization + "visualization_name": "trackster"
Single-cell spatial dashboardVitessce```vitessce + JSON config
Inline dataset name/type in proseInline directive${galaxy history_dataset_name(...)}

Key Design Principles

  1. Vega-Lite is self-contained — the JSON spec includes everything needed to render. No Galaxy objects required. Best for ad-hoc charts with embedded or external data.

  2. Plugin visualizations are data-bound — they reference Galaxy datasets by ID and render interactively in iframes. The plugin handles all rendering; Galaxy just provides the sandbox and data access.

  3. Galaxy directives are server-resolved — the backend fetches actual dataset content, job metadata, etc. and either preserves the directive for frontend rendering (lazy) or expands to static HTML/markdown (eager/PDF).

  4. Visualization blocks are frontend-only — the backend passes them through unchanged (except for invocation ID encoding). All resolution and rendering happens in Vue components.

  5. Plugins are external — chart types, genome browsers, etc. are installable packages, not hardcoded. The embeddable flag controls which ones appear in the markdown editor toolbar.

Incoming References (1)