Technical previewShare feedback

Contributing

Updated Nov 12, 2026

Livemark is an open-source project developed by the Datist team. Everyone is welcome to help shape it with bug reports, ideas and questions; code changes come from team members.

Contributors

Report a bug

Open an issue on GitHub Issues. A good report includes:

  • the Livemark version and how you use it
  • the smallest input that reproduces the problem
  • the output you got and the output you expected

Share an idea or ask a question

Use GitHub Discussions for anything that isn't a bug:

  • Ideas: features and improvements you'd like to see
  • Q&A: questions about using Livemark
  • Show and tell: projects built with Livemark

Ideas the team picks up turn into issues.

Members

The rest of this guide is for Datist team members working on Livemark.

Quick start

You'll need Node.js 24+ and pnpm 11+. Then:

git clone https://github.com/datisthq/livemark.git
cd livemark
pnpm install
pnpm start

pnpm start boots the dev server against this repo's own livemark/docs/ so you can iterate on Livemark while seeing the result in your browser. Edits to livemark/source/, markdown/source/ or livemark/docs/ reload automatically.

Repository layout

├── livemark/               # the `livemark` package: CLI, site and escapable UI
│   ├── source/             # its code (compiles into build/)
│   │   ├── actions/        # build-time orchestration (config, articles, changelog)
│   │   ├── commands/       # `livemark <cmd>` CLI entrypoints
│   │   ├── components/     # site React components (overridable via .livemark/)
│   │   ├── elements/       # shadcn-style primitives (overridable)
│   │   ├── helpers/        # internal utilities (private)
│   │   ├── hooks/          # public React hooks
│   │   ├── plugins/        # the site's Vite plugins
│   │   ├── routes/         # TanStack Start routes
│   │   ├── models/         # Zod schemas
│   │   ├── content/        # derivations over the loaded articles
│   │   └── vite.config.ts  # the Vite config consumers run against
│   ├── shims/              # `livemark/components/*` etc. for files that live in markdown/
│   ├── skills/             # agent skills shipped to people who install Livemark
│   ├── docs/               # livemark.dev's content (not published)
│   └── blog/               # livemark.dev's blog posts (not published)
├── markdown/               # the internal `@livemark/markdown` package
│   └── source/             # the Markdown pipeline, and the components, elements and
│                           # styles articles render with (overridable too)
├── vite/                   # the public `@livemark/vite` package
│   └── source/             # the Vite plugin and what apps import beside it
├── .scripts/               # repo tooling and repo-wide guards
└── livemark.config.ts      # livemark.dev's livemark config

Each package ships source/ and build/: Node loads build/*.js, while Vite bundles raw TS from source/* so the literal .ts import extensions resolve. A site is assembled from markdown/source, then livemark/source over it, then the project's .livemark/ escapes; no path exists in both packages.

Local commands

CommandWhat it does
pnpm startRun the dev server against this repo's docs
pnpm build:docsProduction build of the docs site
pnpm buildCompile each package's source/ → build/
pnpm check:typesType-check (no emit)
pnpm check:lintFormat check + lint via vite-plus
pnpm checkLint + type
pnpm formatAuto-fix formatting
pnpm test:unitRun unit tests (Vitest)
pnpm testLint + type + unit

A change is ready to push when pnpm test is green and pnpm build:docs succeeds.

Code conventions

  • TypeScript: strict mode is on. Don't use any, the as cast, or non-null ! without a clear reason — flag it in the PR.
  • Imports: use full ESM paths with the .ts(x) extension (from "./foo.ts"). Tsgo rewrites them to .js on emit via rewriteRelativeImportExtensions.
  • Comments: docstrings on exports. A // comment must name the mistake it prevents, stays under about three lines, and comments never outnumber code — rationale and history belong in git.
  • File layout: high-level public items at the top, private helpers at the bottom.
  • Tests: place unit tests in <module>.unit.ts next to the code. No "Arrange/Act/Assert" comments — the structure should be obvious.
  • Formatting: vite-plus via pnpm format. 2-space indent, no semicolons, double quotes, 90 columns.

Proposing a change

  1. Open an issue first for anything bigger than a typo or a small bug. It's much easier to align on direction before code.
  2. Branch from main with a short descriptive name (e.g. fix-mobile-toc, feat-yaml-frontmatter).
  3. Write a focused commit history. Livemark uses Conventional Commits — the prefix drives release-please:
    • fix: — bug fix in a published package (livemark/ or markdown/) → patch release
    • feat: — new feature in a published package → minor release
    • chore:, docs:, refactor:, test: — no release; use these for CI, tooling, dependencies, the docs site and anything else an installer never sees
    • never mark a commit breaking (! or a BREAKING CHANGE: footer) without a maintainer agreeing to a major release
  4. Run pnpm test and pnpm build:docs before pushing.
  5. Open a PR against main. Describe the why (link the issue), include screenshots for any UI change, and call out anything reviewers should pay extra attention to.

Releases

Releases are driven by release-please. A feat or fix landing on main opens a Release PR with the computed version and changelog; merging that PR tags, publishes and creates the GitHub release. Maintainers never bump versions by hand — .release/manifest.json is maintained by release-please.

Created with ❤ and Livemark