Viz

unlisted

by Roman

Grafana-lite charts in notes: one code block — one chart. Inline JSON/YAML samples or PostgreSQL/MySQL connections; hour/day/week/month periods; line, area, bars, stat, cohort heatmap.

Updated 13d agoMIT
View on GitHub

Viz (Obsidian plugin)

Status: 0.4.0 — feature-complete v1: inline charts + PostgreSQL/MySQL connections + categorical bars + cohort heatmap + public API. Design: spec/001-architecture.md.

Grafana-lite for Obsidian notes: one code block — one chart. Samples come from JSON/YAML pasted straight into the block, or from a named PostgreSQL/MySQL connection configured in settings. Buckets (hour/day/week/month), a period selector when several are given, and five chart types: line, area, bars, stat, and a cohort heatmap.

Embed in a note

```viz
title: New users by platform
type: bars
periods: [day, week]
last: 12
series: platform
value: count
data:
  - { date: 2026-08-25, platform: mac, count: 12 }
  - { date: 2026-08-25, platform: win, count: 5 }
  - { date: 2026-08-26, platform: mac, count: 9 }
  - { date: 2026-08-26, platform: win, count: 7 }
```

Database-backed (the plugin buckets client-side, so one query serves every period; macro table in the spec):

```viz
title: Signups
type: area
periods: [day, week, month]
connection: pg-main
query: |
  SELECT created_at AS t, platform AS series, 1 AS value
  FROM users
  WHERE created_at >= '${from}'
```

Cohort retention from a single event stream:

```viz
title: Weekly cohorts
type: heatmap
transform: cohort
periods: week
connection: pg-main
query: |
  SELECT ts AS t, user_id AS series FROM events WHERE ts >= '${from}'
```

The body can be simple key: value lines (with - item lists, - {json} rows, minimal YAML mapping rows, and key: | block scalars) or one JSON object. Full option reference: spec §4.2.

Highlights

  • One block, one chart — compose dashboards by stacking blocks in any note.
  • Two sources — inline data (works offline, zero config) or named PostgreSQL/MySQL connections (multiple of each, editable, health-checked).
  • Periodshour|day|week|month; give a list and the widget renders a Day|Week|Month selector; last: N sets the trailing window.
  • Client-side bucketing — SQL only filters the time window (${from}/${to} macros); dialect-specific aggregation stays optional.
  • Types — line, area (stacked), bars (time or categorical, topN), stat (big number + Δ% + sparkline), cohort heatmap (percent/count).
  • Hand-rolled SVG renderer — themed via Obsidian CSS variables, tooltip
    • legend, responsive; no bundled chart library.
  • Read-only by default — queries are guarded to a single SELECT/WITH statement; passwords passed via env vars, never argv.

Settings

  • General — refresh interval (DB widgets; 0 = manual only), result cache TTL.
  • Connections — one card per connection: name, kind (PostgreSQL / MySQL), host/port/database/user/password, binary path override (psql/mysql), SSL mode, Test button with latency, duplicate/delete.

Note: passwords are stored plaintext in the plugin's data.json (same stance as the sibling plugin's server password) and passed to the client binary via env vars, never on the command line. Use a SELECT-only database user; the read-only guard (single SELECT/WITH statement, no data-modifying keywords) is on by default per connection.

Install (manual)

Copy main.js, manifest.json, styles.css into <vault>/.obsidian/plugins/viz/, then enable Viz under Settings → Community plugins. Desktop only (spawns psql/mysql for DB-backed widgets).

Client binaries are autodetected when the path setting is empty: viz probes your PATH plus common macOS locations (Homebrew libpq / mysql-client kegs, Postgres.app). Note that GUI apps like Obsidian inherit launchd's minimal PATH, not your shell's — if autodetection fails, set an absolute binary path in the connection's settings (e.g. /opt/homebrew/opt/libpq/bin/psql).

API

globalThis.viz (version 1):

const api = globalThis.viz;
api.connections();                          // [{ id, name, kind, enabled }]
const rows = await api.run("pg-main", sql); // read-only guarded, cached
const handle = api.render(el, blockSource); // render a chart into any element
handle.refresh();                           // re-resolve (inline or db)
handle.unload();                            // stop timers, detach

A seam for Datacore JSX or a future grid view.

Development

Plain single-file plugin, no build step (main.js is hand-written ES2022). node --check main.js to syntax-check; design docs in spec/; sample widgets in examples/charts.md. Excluded from the repo: data.json (local settings) and .hotreload (dev marker).

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.