Jx Documentation
The user-facing documentation corpus, published at jxsuite.com/docs. Where
specs/
defines the contract, these pages track what actually ships. Every page is a Markdown file with YAML frontmatter, and the folder path is the URL:
docs/studio/editing.md
→
/docs/studio/editing/
. A section's landing page is the sibling
.md
one level up (
docs/start.md
→ slug
start
).
The style guide for writing a page — voice, page shapes, callouts, the canonical names for every Studio surface — is itself a page: extending/contributing/docs.md . This file covers the directory and its machinery.
| Member | Contents |
|---|---|
start/
+
start.md |
Install, first project, Studio tour, tutorials, "coming from X" migration guides |
studio/
+
studio.md |
The visual editor, surface by surface (
ai/ data/ design/ editing/ interface/ logic/ projects/ publish/
) |
framework/
+
framework.md |
The JSON document format Studio writes, how a site compiles, and the generated catalogs |
extending/
+
extending.md |
Extension authoring, embedding Studio, the backend protocol, generated reference tables |
nav.json |
The sidebar manifest —
{ id, sections[{ path, label, pages[], groups[{ label, pages[] }] }] } |
images/ |
Screenshots, every one produced by
bun run screenshots |
Anatomy of a page
---
title: "Style inspector"
description: "The Style tab in Jx Studio: a Target Line stating what an edit changes, chips naming where each value came from, and visual CSS controls."
spec:
- studio.md#6.2
code:
- packages/studio/src/panels/style-panel.ts
- packages/studio/src/panels/target-line.ts
---
# Style inspector
| Key | Rule |
|---|---|
title |
Required. Sentence case, no site suffix; becomes the page
<title> |
description |
Required,
≤ 155 characters
(
MAX_DESCRIPTION
). Becomes the meta description and the page's one line in
llms.txt |
spec: |
Optional string or array.
<spec-file>
or
<spec-file>#<anchor>
; the anchor must match a numbered heading in that spec |
code: |
Optional string or array of repo-relative paths that must exist. This is the association
docs:sync
reads |
generated: |
true
only on the generated pages, which must also carry the
<!-- GENERATED
banner |
The reverse association is a
@docs <slug>
tag in a source comment, scanned across
packages/*/src/**/*.ts
and
extensions/*/src/**/*.ts
. A tag pointing at a page that does not exist fails CI; a missing tag fails nothing, it just drops that file out of future sync reports.
Every page appears exactly once in
nav.json
, and every nav path has a page.
The bijection is enforced in both directions, and a duplicate nav path is a third failure mode — so a new page and its one nav line land in the same change.
A section holds its own rows in
pages
, leading with
Overview
, which is the section's own index page, and its disclosures in
groups
. The sidebar draws each as a
<details>
and ships every page already expanded to its own location. A group has no path and opens when the current page is one of its
pages
, so it needs no shared path segment, which is how "Start here" is grouped at all.
Neither array may be empty
: a repeater over an empty array survives the build and makes its node dynamic, so one empty
groups
would ship JavaScript to every page in that section.
scripts/docs/nav.ts
owns the walk and asserts all of this.
Markup beyond CommonMark:
:::doc-note
,
:::doc-tip
and
:::doc-warning
container directives (real components registered as the collection's
$elements
), and
:kbd[Ctrl+K]
for keys. Bold for clickable UI labels; backticks only for literal code, filenames, and JSON keys.
Generated pages
These pages are build outputs: gitignored, written by
bun run docs:generate
(which
postinstall
, the docs gates and the site build all run), and never hand-edited because there is nothing to edit under version control.
scripts/docs/generators/pages.ts
is the list; this table tracks it. They were committed once, and every spec release rewrote the same nine files, so any two open pull requests that released a spec conflicted on them by construction.
| Page | Generated from |
|---|---|
extending/reference/studio-routes.md |
@jxsuite/protocol
STUDIO_ROUTES
+
PROBLEM_TYPES |
extending/reference/implementation-status.md |
The specs'
**Status:**
markers |
extending/reference/spec-changelog.md |
The specs'
## Changelog
sections |
extending/reference/standards.md |
The specs'
## N. Standards Alignment
tables +
scripts/docs/standards.json |
framework/reference/formulas.md |
@jxsuite/formulas/catalog |
framework/reference/operators.md |
packages/schema/schema.json |
studio/projects/starters.md |
packages/starters/registry.json |
studio/interface/commands.md |
packages/studio/src/commands/app-commands.ts |
studio/interface/shortcuts.md |
The same command set |
The generator runs oxfmt over what it writes, so what the site builds and the docs gates read is formatted the same way as every hand-written page beside it.
Releasing a spec therefore changes these pages at the next build:
bun run spec:bump
bumps the version and prepends a changelog entry, which feeds the spec-changelog page, and the
**Status:**
markers a release moves by hand feed the other two. There is nothing to regenerate in the PR.
Images
docs/images/
is written only by
bun run screenshots
, which commits the PNGs alongside
scripts/screenshots/capture.lock.json
— the record of the bytes and the shot definition each image came from.
You cannot add an image by hand:
a PNG whose
sha256
has no lock entry fails
docs:images:check
, and a page referencing an image the lock does not name fails
docs:check
, even with the file sitting right there on disk.
Reference images page-relative into
docs/images/
—

, one
../
per level below
docs/
. Root-absolute and URL forms both fail the check. The relative form is what keeps
/docs
readable in a plain Markdown editor; the site republishes the directory at
/content/docs/images/
through the collection's asset mount. Alt text is mandatory and describes the state shown.
The shot grammar — and the rule that a step names a command id and a capture a region id, never a CSS selector, a sleep, or a toggle — is
scripts/screenshots/README.md
.
Gates
These commands guard this directory, but none of them live in it: each is a script in
scripts/
— mostly under
scripts/docs/
, with
docs:images:check
and
docs:markdown
at the top level.
scripts/README.md
is where their placement in CI, their shared readers, and the conventions for changing one are written down.
| Command | Enforces |
|---|---|
| bun run docs
|
Frontmatter,
spec:
anchors,
code:
paths, image refs, the nav bijection, and the reverse
@docs
tags |
| bun run docs:images
|
The bytes: every PNG is one the lock names, and each shot's definition hash still matches the working tree |
| bun run docs
|
Writes the generated pages (gitignored build outputs) so the gates and the site build can read them |
| bun run docs
|
The CI chain —
docs:check
(which generates first) and
docs:images:check |
| bun run docs
|
Every internal link: the slug against
nav.json
, and every
#anchor
against the target's headings |
| bun run docs
|
No em dash, curly quote, decorative emoji or stock AI vocabulary; the em-dash debt only falls |
| bun run docs
|
Visual-editor escapes — an escaped heading number, an escaped inner underscore — every tracked
*.md |
| bun run docs
|
Advisory only: maps a diff to the pages and spec sections declared for the files it touched |
docs:check
,
docs:links
and
docs:sync
write the generated pages before they read the page set, so a fresh checkout needs nothing run by hand (
postinstall
writes them too).
docs:markdown
is repo-wide, so a spec or a package README can turn it red;
bun run format:md
fixes it.
docs:sync
runs on its own in two places — the Claude Code Stop hook and a non-blocking pre-commit advisory — and it also joins in the screenshot manifest, so a report can name the page whose picture, and therefore whose surrounding prose, your change just aged. It never blocks and it only knows about declared associations: silence is not proof the docs are current.
Surprises
A relative
../foo.mdlink publishes broken , because the site serves the target verbatim rather than rewriting it to a URL. It is the failure that looks right while you write it: the relative form is exactly what resolves in a Markdown preview.docs:linksis the gate.A
spec:anchor breaks when someone renumbers a spec heading , which is why spec sections are edited in place and never renumbered or removed. The failure surfaces here, not in the spec.The screenshots lane's normal outcome is a bot commit, not a red X. It re-captures, pushes the images and the lock to your branch, and comments with the pages each changed image appears on. Go re-read those pages: whether the paragraph beside a moved surface is now wrong is a judgement no check makes.
Behavior changes land with their docs. Every plan for behavior-changing work carries a "Specs & docs" step, and the code, the spec edit, and the page update go in one change set.
Publishing
sites/jxsuite.com
consumes this directory as a Markdown content collection (
content.docs.source = "../../docs"
), with
nav.json
loaded as a second, JSON collection that renders the sidebar. One catch-all route serves every page, building its
<title>
from the frontmatter
title
plus the site suffix and its meta description from
description
; the same collection feeds site search. After
jx build
, the site build emits
dist/llms.txt
(nav-ordered) and
dist/docs/full-docs.json
(the whole corpus) for machine readers — neither is committed. Pushes touching
docs/**
or
scripts/docs/**
trigger the site deploy, which runs
bun run docs:claims
first.