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 startpnpm 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 configEach 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
| Command | What it does |
|---|---|
pnpm start | Run the dev server against this repo's docs |
pnpm build:docs | Production build of the docs site |
pnpm build | Compile each package's source/ → build/ |
pnpm check:types | Type-check (no emit) |
pnpm check:lint | Format check + lint via vite-plus |
pnpm check | Lint + type |
pnpm format | Auto-fix formatting |
pnpm test:unit | Run unit tests (Vitest) |
pnpm test | Lint + 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, theascast, 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.json emit viarewriteRelativeImportExtensions. - 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.tsnext 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
- Open an issue first for anything bigger than a typo or a small bug. It's much easier to align on direction before code.
- Branch from
mainwith a short descriptive name (e.g.fix-mobile-toc,feat-yaml-frontmatter). - Write a focused commit history. Livemark uses Conventional Commits — the prefix drives release-please:
fix:— bug fix in a published package (livemark/ormarkdown/) → patch releasefeat:— new feature in a published package → minor releasechore:,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 aBREAKING CHANGE:footer) without a maintainer agreeing to a major release
- Run
pnpm testandpnpm build:docsbefore pushing. - 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