Timeline DSL

unlisted

by keroway

Render .tdsl timeline DSL code blocks as SVG previews in Obsidian.

Updated 25d agoMIT
View on GitHub

obsidian-tdsl

CI License: MIT npm: @keroway/tdsl-wasm timeline-dsl

An Obsidian plugin that renders Timeline DSL (tdsl) code blocks as interactive SVG timelines in the live preview.

日本語版: README.ja.md

Preview

Timeline DSL rendered in Obsidian (light mode)

Timeline DSL rendered in Obsidian (dark mode)

Features

  • SVG timeline previewtdsl code blocks are rendered as SVG directly in the Obsidian live preview / reading view
  • Inline syntax errors — Parse and semantic errors are shown with line/column numbers inside the note, without leaving the editor
  • Dark mode support — Automatically follows Obsidian's body.theme-dark class; colour palette adapts to Catppuccin-style dark colours
  • XSS-safe SVG insertion — SVG output is parsed via DOMParser and inserted with document.adoptNode; no innerHTML, no script execution
  • Mobile-enabledisDesktopOnly: false; the plugin is not blocked from running on Obsidian Mobile, but has not yet been extensively verified on iOS/Android. Feedback via GitHub Issues is welcome.
  • Zero network requests — The Timeline DSL WASM renderer is bundled inline; no external calls at render time

Usage

Write a tdsl fenced code block in any note:

```tdsl
timeline "Heian Period" {
  unit year;
  range 794..1185;
}

lane "Emperor" as emperor {}

span emperor 781..806 "Kanmu" {};
span emperor 806..809 "Heizei" {};
span emperor 809..823 "Saga" {};
```

> **Syntax notes:** every property inside the `timeline { … }` block ends with `;`,
> ranges use `start..end` (not `start to end`), and each `span` / `event` /
> `event_range` statement ends with a trailing `;` after its `{ … }` block.
> `lane` and `group` declarations take **no** trailing `;`.

Supported DSL features in Obsidian

The following DSL constructs work fully inside Obsidian:

timeline block

Declares the title, time unit, display range, and colour mappings.

timeline "Chinese Dynasties" {
    title "Chinese Dynasties";
    unit year;
    range -500..2000;
    color_map {
        dynasty: "#3366cc";
        war:     "#cc0000";
    }
}

unit accepts year, month, or day.

lane declaration

Defines a vertical category. Use as to assign an internal ID used by span / event.

lane "Han" as han { kind dynasty; order 20; }

group block

Groups multiple lanes with a shared label and visual boundary.

group "Ancient China" {
    lane "Qin" as qin { kind dynasty; order 10; }
    lane "Han" as han { kind dynasty; order 20; }
}

span / event / event_range

Three types of time elements attached to a lane:

// Duration (start..end) — note the trailing ; after the block
span han -206..220 "Han Dynasty" { tags ["dynasty"]; };

// Point event
event han -209 "Dazexiang Uprising" {};

// Range event (wars, disasters, etc.)
event_range han 184..204 "Yellow Turban Rebellion" { tags ["war"]; };

Rendering options (//! directives)

Because Obsidian only passes the code-block body to the renderer, per-diagram options are written as //! comment lines (ordinary DSL comments the compiler ignores). Place them anywhere in the block:

//! scale: 3
//! grid: decade
//! events: on
timeline "Demo" { unit year; range 0..100; }
lane "Main" as main {}
span main 10..50 "An era" {};
DirectiveValuesEffect
scalepositive number, or fitpixels per year. Higher = wider / more readable. fit shrinks the timeline to the note width (no horizontal scroll). Omit for auto.
gridnone, decade, year, monthgridline density
themedefault, dark, print, pastelbuilt-in colour theme
eventson / offshow labels next to event / event_range items
tableon / offrender the accompanying item-listing table (drawn natively in the SVG)
legendon / offrender a static legend panel showing lane and tag colors
orientationhorizontal, verticallayout direction

The timeline renders at its natural size; if it is wider than the note column it scrolls horizontally rather than shrinking (which would make labels unreadable). Use a numeric scale to make a sparse timeline span a wider area, or //! scale: fit to shrink it to the note width for an at-a-glance view (labels scale down with the graphic).

Default options (settings tab)

Settings → Community plugins → Timeline DSL lets you set vault-wide defaults so you don't repeat the same directive in every block:

SettingValuesDefault
Default themeauto, default, dark, print, pastelauto (follow Obsidian light/dark via plugin CSS)
Default gridnone, decade, year, monthnone
Default scaleauto, fit, or a positive numberauto
Show event labels by defaulton / offoff

Resolution order is block //! directive > settings default > built-in. Changes apply when you reopen the affected note.

Full example

timeline "Japanese History" {
    title "Nara to Edo";
    unit year;
    range 710..1868;
    color_map {
        imperial: "#8b5cf6";
        military: "#ef4444";
    }
}

group "Imperial Court" {
    lane "Emperor" as emperor { kind imperial; order 1; }
}

group "Military Government" {
    lane "Kamakura Shogunate" as kamakura { kind military; order 2; }
    lane "Muromachi Shogunate" as muromachi { kind military; order 3; }
    lane "Edo Shogunate"       as edo      { kind military; order 4; }
}

span emperor 710..794 "Nara Period" { tags ["imperial"]; };
span emperor 794..1185 "Heian Period" { tags ["imperial"]; };

span kamakura  1185..1336 "Kamakura Shogunate" { tags ["military"]; };
span muromachi 1336..1573 "Muromachi Shogunate" { tags ["military"]; };
span edo       1603..1868 "Edo Shogunate" { tags ["military"]; };

event kamakura 1185 "Minamoto no Yoritomo appointed Shogun" {};
event edo      1868 "Meiji Restoration" {};

Limitations

The following Timeline DSL features require network access or server-side processing and are not supported inside Obsidian:

FeatureReason
import wikidataWikidata HTTP requests cannot be made from the browser renderer
map blocksDepend on resolved import wikidata data
template / apply syntaxDepend on resolved import wikidata data

If a tdsl block contains import wikidata, the plugin displays a notice and renders only the static items (span / event / event_range) defined in the source.

For full Wikidata integration, use the tdsl CLI or the WebUI to pre-render to SVG/HTML.

Installation

Community Plugin (coming soon)

The plugin is not yet listed in the Obsidian Community Plugin directory. Once published, it will be installable from Settings → Community plugins → Browse by searching for Timeline DSL.

Manual installation (GitHub Release)

  1. Go to the Releases page and download the latest release assets:

    • main.js
    • manifest.json
    • styles.css
  2. Create the plugin directory in your vault (if it does not exist):

    mkdir -p <vault>/.obsidian/plugins/timeline-dsl/
    
  3. Copy the three downloaded files into that directory.

  4. In Obsidian: Settings → Community plugins → Installed plugins → enable Timeline DSL

Requires Obsidian ≥ 1.4.0.

Manual installation (development build)

  1. Clone this repository

  2. Install dependencies and build:

    pnpm install
    pnpm run build
    
  3. Copy the three output files into your vault:

    # replace <vault> with your actual vault path
    cp main.js manifest.json styles.css <vault>/.obsidian/plugins/timeline-dsl/
    
  4. In Obsidian: Settings → Community plugins → Installed plugins → enable Timeline DSL

Requires Obsidian ≥ 1.4.0.

Development

pnpm install          # install dependencies
pnpm run dev          # watch mode (rebuilds on save)
pnpm run build        # production build → main.js
pnpm run lint         # Biome lint
pnpm run typecheck    # tsc --noEmit

CI runs lint → typecheck → build and verifies that main.js is produced. See CONTRIBUTING.md for a full guide.

Related projects

ProjectDescription
keroway/timeline-dslThe Timeline DSL compiler (Rust + WASM). CLI, WebUI, GitHub Actions integration
WebUIReal-time browser editor — full Wikidata support
Landing pageOverview and feature tour
VS Code extensionSyntax highlighting for .tdsl files
@keroway/tdsl-wasmWASM package used by this plugin

License

MIT © keroway

Third-party licenses for bundled dependencies are listed in THIRD-PARTY-NOTICES.md.

For plugin developers

Search results and similarity scores are powered by semantic analysis of your plugin's README. If your plugin isn't appearing for searches you'd expect, try updating your README to clearly describe your plugin's purpose, features, and use cases.