Galaxy Markdown Architecture
Learning Questions
- What is Galaxy Markdown and how does it differ from regular HTML?
- How do contextual references get resolved to internal IDs?
- What is the transformation pipeline for Galaxy Markdown?
- How do frontend components render Galaxy directives?
Learning Objectives
- Understand the contextual addressing system for Galaxy Markdown
- Learn the backend parsing and transformation pipeline
- Understand the handler pattern for export/rendering
- Know how frontend components process Galaxy directives
- Learn the editor architecture for pages and reports
What is Galaxy Markdown?
- Portable, reproducible documentation with embedded Galaxy objects
- Alternative to HTML (instance-locked)
- Powers: workflow reports, pages, tool outputs
Galaxy Markdown emerged to solve a fundamental portability problem. Traditional HTML-based
documentation embedded instance-specific URLs—when workflows were exported and imported
elsewhere, embedded links broke. Galaxy Markdown uses contextual addressing: references
like output="results" or step="alignment" resolve at runtime to the appropriate
objects in whichever Galaxy instance renders the document.
This enables truly portable documentation that travels with workflows, automatically adapting to new execution contexts while preserving the author’s intent.
The diagram shows four input contexts converging to a single internal representation:
- Workflow Markdown uses step and output labels (
step="bwa_mem",output="aligned_reads") - Tool Output Markdown uses output references from the generating job
- Page API uses encoded IDs for URL safety (
history_dataset_id=a1b2c3d4) - History Markdown (planned) will use history-relative references
All contexts converge to Internal Galaxy Markdown with numeric IDs
(history_dataset_id=12345). This conversion happens at context-appropriate boundaries—
workflow refs resolve when an invocation is created, encoded IDs decode on import.
The internal form then renders to HTML, PDF, or interactive UI.
Three Document Types
| Type | Editable | Scope | Use Case |
|---|---|---|---|
| Reports | No | Invocation | Workflow output docs |
| Pages | Yes | Any | User documentation |
| Tool Output | No | Job | Tool-generated reports |
Reports are auto-generated when a workflow completes. The WorkflowMarkdownGeneratorPlugin
uses a default template containing invocation_inputs(), invocation_outputs(), and
workflow_display() directives. Workflows can override this with custom reports_config.
Pages are user-created documents with full revision history—every save creates a new
PageRevision record, enabling rollback and version comparison. Pages can embed any Galaxy
object the user can access.
Tool Output markdown is generated by tools themselves, providing structured documentation of results. Like reports, these are read-only and scoped to a specific job execution.
lib/galaxy/managers/markdown_parse.py
The parser architecture separates concerns into two complementary files:
lib/galaxy/managers/markdown_parse.py (Syntactic Layer):
- Lightweight, self-contained parser with NO Galaxy dependencies
- Reusable in external tools (e.g., gxformat2)
- Recognizes directives within
```galaxyfenced blocks - Validates directive syntax and arguments
- Reports errors with line numbers for debugging
lib/galaxy/managers/markdown_util.py (Semantic Layer):
- Galaxy-specific integration
- Resolves contextual references to internal IDs
- Implements handler pattern for export/rendering
- Handles ID encoding for URLs vs storage
This separation enables the parser to be packaged independently, while Galaxy-specific resolution logic stays in the util module.
Directive Syntax
Block directive:
```galaxy
history_dataset_as_table(history_dataset_id=12345, title="Results")
```
Inline directive:
Text with ${galaxy history_dataset_name(output="results")} embedded.
lib/galaxy/managers/markdown_parse.py:GALAXY_MARKDOWN_FUNCTION_CALL_LINE
Galaxy Markdown supports two directive syntaxes:
Block directives appear in fenced code blocks with the galaxy language tag. The parser
uses GALAXY_FLAVORED_MARKDOWN_CONTAINER_LINE_PATTERN to detect these blocks and
GALAXY_MARKDOWN_FUNCTION_CALL_LINE to parse the directive call.
Inline directives use template syntax: ${galaxy directive(...)}. These are detected
by EMBED_DIRECTIVE_REGEX and can appear anywhere in regular markdown text.
Arguments support both formats: unquoted values for IDs (history_dataset_id=12345) and
quoted strings for display text (title="My Results"). The ARG_VAL_REGEX pattern
handles both cases.
27 Directives
- Dataset:
display,as_image,as_table,peek,info,link,name,type - Workflow:
display,image,license,invocation_inputs/outputs - Job:
parameters,metrics,tool_stdout/stderr - Meta:
generate_time,generate_galaxy_version,invocation_time - Instance: 6
instance_*_linkvariants
The 30+ directives fall into functional categories based on the Galaxy objects they render:
- Dataset directives display history items in various formats (tables, images, raw text)
- Workflow directives show workflow structure, licensing, and invocation summaries
- Job directives expose execution details like parameters, metrics, and tool output
- Instance directives generate links to Galaxy documentation and resources
- Meta directives insert generation timestamps and version information
Each directive accepts specific arguments validated against ALLOWED_ARGUMENTS in
markdown_parse.py. Invalid arguments trigger validation errors with line numbers.
Validation
def validate_galaxy_markdown(galaxy_markdown, internal=True):
# Line-by-line fence tracking state machine
for line, fenced, open_fence, line_no in _split_markdown_lines(markdown):
if fenced and GALAXY_FUNC_CALL.match(line):
_check_func_call(match, line_no) # Validates args
# Raises ValueError with line number on failure
lib/galaxy/managers/markdown_parse.py:validate_galaxy_markdown()
The validator implements a fail-fast strategy: it raises ValueError on the first
invalid directive, reporting the exact line number for immediate feedback in the editor.
The fence-tracking state machine handles edge cases like nested backticks and
whitespace-only lines, ensuring only content within ```galaxy blocks is validated
as directives.
The diagram shows the five-stage transformation pipeline:
-
Import:
ready_galaxy_markdown_for_import()decodes external IDs (base36) to internal numeric IDs for database storage. -
Validate:
validate_galaxy_markdown()checks syntax without Galaxy dependencies. -
Resolve:
resolve_invocation_markdown()expands workflow-relative references (output="results") to concrete IDs (history_dataset_id=12345). -
Export: Choose between lazy (
ReadyForExportMarkdownDirectiveHandler) which preserves directives for frontend rendering, or eager (ToBasicMarkdownDirectiveHandler) which fully expands for PDF export. -
Render: Final output as HTML, PDF, or interactive Vue components.
lib/galaxy/managers/markdown_util.py
The handler pattern uses an abstract base class with two concrete implementations:
ReadyForExportMarkdownDirectiveHandler (lazy):
- Preserves directives in output for frontend processing
- Collects metadata (dataset names, types, peek data) in
extra_rendering_data - Used when rendering interactive Vue components
ToBasicMarkdownDirectiveHandler (eager):
- Fully expands directives to standard markdown
- Converts datasets to formatted tables, embeds images as base64 data URIs
- Used for PDF export where all content must be self-contained
This pattern enables adding new output formats by implementing the abstract interface without modifying existing handlers.
ID Encoding
# Storage (internal): numeric IDs
history_dataset_display(history_dataset_id=12345)
# Export (external): encoded IDs for URLs
history_dataset_display(history_dataset_id=a1b2c3d4e5f6)
# Regex patterns for conversion
UNENCODED_ID_PATTERN = r"(history_dataset_id)=([\\d]+)"
ENCODED_ID_PATTERN = r"(history_dataset_id)=([a-z0-9]+)"
Invocation Resolution
# Input: workflow-relative references
invocation_outputs(output="alignment_results")
job_metrics(step="bwa_mem")
# Output: instance-specific IDs
history_dataset_display(history_dataset_id=98765)
job_metrics(job_id=54321)
lib/galaxy/managers/markdown_util.py:resolve_invocation_markdown()
Invocation resolution is the key to portable workflow reports. When a workflow runs,
resolve_invocation_markdown() maps abstract references to the specific objects created
by that execution:
output="aligned_reads"becomeshistory_dataset_id=98765step="bwa_mem"becomesjob_id=54321input="reference_genome"becomeshistory_dataset_id=12345
This happens via populate_invocation_markdown() which first attaches invocation_id
to each directive, then resolution looks up the actual IDs from the invocation record.
The same report template works across any invocation of the workflow.
lib/galaxy/managers/markdown_util.py:internal_galaxy_markdown_to_pdf()
PDF export uses the eager handler to create self-contained documents:
ToBasicMarkdownDirectiveHandlerexpands all directives to standard markdownmarkdown.markdown()converts to HTML- HTML is sanitized for security
- WeasyPrint renders HTML to PDF
- Optional branding via
markdown_export_prologue/epilogueand custom CSS
Instance administrators can customize PDF appearance per document type using
markdown_export_css_reports, markdown_export_css_pages, etc. Large documents
use async Celery tasks to avoid request timeouts.
client/src/components/Markdown/Markdown.vue
The frontend rendering hierarchy starts with Markdown.vue, which receives a
MarkdownConfig containing the raw content and any validation errors from the backend.
parseMarkdown() splits content by triple-backtick delimiters into typed sections.
SectionWrapper then dispatches each section to the appropriate renderer:
- MarkdownDefault: Standard markdown via markdown-it, with KaTeX for math
- MarkdownGalaxy: Galaxy directives (the focus of this architecture)
- MarkdownVega: Vega-Lite data visualizations
- MarkdownVisualization: Galaxy’s plugin-based visualizations
- MarkdownVitessce: Spatial and single-cell data viewers
Unknown section types display an error alert rather than failing silently.
Section Parsing
// Triple-backtick splits content into typed sections
parseMarkdown(content) → [
{ type: 'markdown', content: '# Title...' },
{ type: 'galaxy', content: 'history_dataset_as_table(...)' },
{ type: 'vega', content: '{"$schema": "..."}' }
]
client/src/components/Markdown/parse.ts
The parser splits markdown content into discrete rendering units. Each fenced code block becomes a separate section with its language tag as the type:
```galaxy→{ type: 'galaxy', content: '...' }```vega→{ type: 'vega', content: '...' }- Regular markdown between blocks →
{ type: 'markdown', content: '...' }
This allows mixing standard markdown prose with embedded Galaxy directives, data visualizations, and interactive components in a single document.
client/src/components/Markdown/Sections/MarkdownGalaxy.vue
MarkdownGalaxy.vue processes Galaxy directives through a validation pipeline:
- Parse:
getArgs()extracts the directive name and arguments - Validate name:
hasValidName()checks against therequirements.ymlregistry - Validate object:
hasValidObject()ensures required IDs are present (e.g.,history_dataset_idfor dataset directives) - Validate label:
hasValidLabel()checks workflow labels in report mode - Render: Route to the appropriate Element component
Errors at any stage display user-friendly messages via Bootstrap alerts. The component
uses Pinia stores (useInvocationStore, useWorkflowStore, useDatasetStore) to
fetch and cache the data needed for rendering.
Element Components
21+ specialized renderers:
HistoryDatasetAsTable,HistoryDatasetAsImageWorkflowImage,WorkflowDisplayJobMetrics,JobParametersToolStdout,ToolStderr
Each handles data fetching + rendering.
client/src/components/Markdown/Sections/Elements/
Each Element component handles a specific directive type with a consistent pattern:
- Props: Receive IDs and configuration from
MarkdownGalaxy - Stores: Fetch data via Pinia stores with automatic caching
- Rendering: Type-aware display (tables, images, iframes, formatted text)
- Errors: Graceful handling with
BAlertcomponents
Dataset elements detect content type and render appropriately: PDFs and HTML as iframes,
images inline, tabular data as paginated tables. Job elements support both single jobs
and implicit collection jobs via the useMappingJobs() composable.
Element components use Pinia stores as a caching layer between the UI and Galaxy APIs:
- useDatasetStore: Dataset metadata and content
- useInvocationStore: Workflow invocation records
- useWorkflowStore: Workflow definitions
- useJobStore: Job execution details
- useDatatypesMapperStore: Datatype hierarchy for rendering decisions
When multiple elements reference the same object, the store serves cached data instead of making redundant API calls. Stores use Vue’s reactivity system, so components automatically update when data changes.
client/src/components/Markdown/MarkdownEditor.vue
The markdown editor supports two editing modes:
TextEditor (always available):
- Two-panel layout: MarkdownToolBox sidebar + textarea
- Direct markdown editing with syntax highlighting
- MarkdownToolBox provides directive insertion with dialogs
- 300ms debounced updates to prevent excessive re-renders
CellEditor (workflow reports only):
- Jupyter-like cell-based editing
- Each fenced code block becomes a discrete cell
- Visual preview of rendered content
- Cell operations: add, delete, clone, move, configure
- Lazy-loaded Ace editor for syntax highlighting
Mode availability depends on context: pages get text mode only, workflow reports get both modes with a toggle.
Directive Registry
# directives.yml - metadata for editor UI
history_dataset_as_table:
side_panel_name:
page: "Dataset Table"
report: "Output Table"
help: "Embed dataset as formatted table..."
# templates.yml - insertion templates
history_dataset_as_table:
template: 'history_dataset_as_table(history_dataset_id="%ID%")'
client/src/components/Markdown/directives.yml
The editor uses two YAML files to configure directive insertion:
directives.yml provides metadata for the editor UI:
side_panel_name: Display label (can vary by mode)side_panel_description: Brief tooltip texthelp: Detailed usage guidance with%MODE%placeholder
templates.yml provides insertion templates:
- Default argument placeholders
- Pre-filled values for common patterns
Both files support mode-aware variants—the same directive can show as “Current Workflow”
in report mode but “Display a Workflow” in page mode. The directives.ts module
resolves these variants at runtime based on context.
Mode-Aware Design
- Page mode: Reference any workflow/dataset in instance
- Report mode: Reference “this” invocation (step labels)
- Same directives, different presentation and validation
The architecture follows five key principles:
Contextual Addressing: Multiple input formats (workflow labels, encoded IDs, output references) converge to internal numeric IDs at well-defined boundaries.
Lazy Resolution: Defer expensive operations until needed. The lazy handler preserves directives for frontend rendering; resolution happens on-demand.
Handler Extensibility: The abstract handler pattern enables new output formats without modifying existing code.
Mode Awareness: Pages, reports, and tool outputs have different rules for what can be referenced and how it’s displayed.
Separation of Concerns: Syntactic parsing (markdown_parse.py) is isolated from semantic resolution (markdown_util.py), and frontend rendering is independent of backend processing.
Adding a Directive
# 1. markdown_parse.py - add to ALLOWED_ARGUMENTS
ALLOWED_ARGUMENTS["new_directive"] = frozenset(["arg1", "arg2"])
# 2. markdown_util.py - add handler method
def handle_new_directive(self, ...):
...
# 3. Frontend - add element component
# client/src/components/Markdown/Sections/Elements/NewDirective.vue
# 4. directives.yml - add metadata
Adding a new directive requires changes across four layers:
-
Backend parsing (
markdown_parse.py): Add the directive name and allowed arguments toALLOWED_ARGUMENTS. This enables validation without Galaxy dependencies. -
Backend handling (
markdown_util.py): Implement handler methods in bothReadyForExportMarkdownDirectiveHandler(for frontend) andToBasicMarkdownDirectiveHandler(for PDF export). -
Frontend rendering (
Elements/): Create a Vue component that fetches data via Pinia stores and renders the appropriate UI. -
Editor integration (
directives.yml,templates.yml): Add metadata for the editor sidebar and an insertion template.
The validation layer in MarkdownGalaxy.vue (requirements.yml) also needs updating
to recognize the new directive and its required objects.
The architecture overview shows the complete flow:
Input: Contextual markdown from workflows, pages, or tools enters through format-specific APIs, each with its own addressing scheme.
Backend Processing: The four-stage pipeline (import → validate → resolve → export) transforms contextual references to internal IDs, then prepares content for the target format.
Frontend Rendering: Vue components parse sections, validate directives, fetch data through Pinia stores, and render interactive elements.
Output: The same source content produces interactive web UI, static PDF documents, or exportable markdown depending on the rendering path chosen.
Key Takeaways
- Portable: Context-specific refs → internal IDs at boundaries
- Extensible: Handler pattern, directive registry, new contexts
- Multi-format: Same internal form → HTML, PDF, interactive UI
- 27+ directives for embedding Galaxy objects
Key Takeaways
- Galaxy Markdown enables portable documentation via contextual addressing
- Multiple context-specific formats converge to internal IDs at boundaries
- Handler pattern enables lazy (frontend) vs eager (PDF) export
- 27+ directives for embedding Galaxy objects
- Frontend uses store-centric data flow for caching