Content manager
Create and edit pages with a live preview, inside the site itself.
The content manager lives at /_editor — the pencil icon in the
navbar. It shows the docs/ tree on the left, the file's source in the middle,
and a live Markdown preview on the right.
What it can do
- Edit any
.md,.htmlor.scssfile underdocs/, with Ctrl+S to save (local) or stage (GitHub) - Create pages — type a path like
guide/deploying.mdand it is scaffolded with front matter, in the right section - New from template — a dedicated button with a submenu of everything in
docs/_templates/;{{title}}and{{date}}tokens are filled in from the new file's name and today's date. Plain + New stays a one-click blank page - Delete files — the ✕ on each file row (deletes immediately in local mode, stages the deletion in GitHub mode)
- Batch commits — in GitHub mode every save, creation and deletion is
staged (
M/A/Dbadges in the file list, ↺ to undo one); the commit bar publishes all staged changes as a single commit, with an optional message - Insert helper — two ways in: the “+ Insert” menu on the toolbar, and an
inline + that appears beside the caret whenever it rests on an empty
line (and stays out of the way while you type). Both drop admonitions, code
blocks, tables, doc components (
<fd-tabs>,<fd-steps>,<fd-api-field>,<fd-counter>) and inline extras at the cursor - Preview while typing — the right pane re-renders on every keystroke
- Jump to the real page — the “View page” link opens the route the file publishes to
Saving writes the file to disk, which means the normal pipeline takes over: the watcher re-renders the content, the dev server hot-reloads, and the change shows up in the real site seconds later.
Two backends, two strategies
The editor picks its backend by where it is running:
| Backend | When | What Save does |
|---|---|---|
| Local | npm start is running (the file API on 127.0.0.1:4271) |
Writes the file to disk; you commit and push from your own editor or terminal, already authenticated with git |
| GitHub | github.repo is set in feastdocs.config.mjs — the mode for the deployed site |
Stages the change; Commit publishes everything staged as one commit, authored by the connected GitHub user |
In development with both available, a Local/GitHub switch appears above the file list. Without either, the page explains what to set up instead of failing.
Connecting GitHub
Two ways in, on the same connect screen:
- Sign in with GitHub — a real OAuth login. It appears once
github.oauthClientIdis set; the code-for-token exchange runs in a Cloudflare Pages Function shipped with the project (functions/api/oauth/token.js), so the OAuth client secret never touches the static bundle. - Personal access token — always available as a fallback: a fine-grained
token with contents read and write on the docs repository. Either way the
token stays in the browser's localStorage and is sent only to
api.github.com.
To enable the sign-in button:
- GitHub → Settings → Developer settings → OAuth Apps → New OAuth App.
Homepage URL: your site. Authorization callback URL:
https://your-site/_editor. - Put the app's Client ID in
feastdocs.config.mjsasgithub.oauthClientId(client ids are public). - On the Cloudflare Pages project (Settings → Variables and secrets), add two
secrets:
GITHUB_CLIENT_ID(same value) andGITHUB_CLIENT_SECRET(the app's secret — it lives only there).
Access is still the repository's
Signing in proves who the visitor is; what they may do is decided by GitHub. Accounts without write access to the repo get a read-only editor, and every write is rejected server-side by GitHub regardless of the UI.
Production never touches local files
The local file API is probed only in development builds. On a deployed site the
editor is GitHub-only — a visitor's own npm start on their machine is
invisible to it, so "saving" can never silently land on someone's local disk.
Concurrent editing and conflicts
Several people can edit at the same time; what happens depends on the path:
- Git pushes conflict the way git always has — the second push is rejected and the author merges locally. Nothing new to learn.
- Web commits are checked before they publish. The editor remembers each file's blob SHA from when you read it. At commit time it compares every staged file against the branch as it is now — if someone changed (or created, or deleted) one of your files in the meantime, the commit is blocked and a panel lists each conflicted file with three choices: Merge… opens a hunk-by-hunk resolver — both versions diffed line by line, each conflicting hunk resolved as theirs, mine or both, with shared lines shown as context; Use theirs drops your staged change and loads their version; Keep mine explicitly overwrites theirs. Nothing is ever overwritten silently.
- The final ref update is atomic. If the branch moves in the instant between the check and the write, GitHub rejects the commit outright (nothing half-written) and the editor asks you to press Commit again.
Author attribution
Every page's footer shows when it last changed and by whom. That comes from
git log at build time — not from the editor — so it is correct for both
strategies: a web commit and a pushed commit look identical in history. After a
web edit, the deployed site shows the new author once CI rebuilds.
The preview is an approximation
The live preview renders admonitions, tables, task lists and attributes exactly
like the real build — including the built-in {.lead} and {.callout} utility
classes. What it does not do: syntax-highlight code blocks, rewrite relative
links, or apply page-scoped SCSS (classes from a sibling .scss file only
style the real page). The saved page — rendered by the actual pipeline — is
always the source of truth, one hot-reload away.
Working with a repository
The content manager writes plain files into docs/ on your machine — nothing
else. Git sees those files like any other change:
Create and edit pages in the content manager (or any editor). Every save lands in
docs/ as an ordinary file change — git status shows it immediately.
git add docs/
git commit -m "docs: add deployment guide"Docs changes diff and review like code, because they are code. Open a pull request if your team reviews content.
git pushCI (or you) runs npm run build; the static output in dist/feastdocs/browser/
is what gets deployed. Generated files (src/app/generated/, public/docs-assets/)
are git-ignored — they are rebuilt from docs/ every time, so they never need
committing.
Nothing writes in production
The deployed site is static and the file API only exists on 127.0.0.1 during
npm start. Publishing a change always goes through the repository — which is
the point.
Why files, still
The content manager is a convenience layer, not a CMS. Files stay the source of truth: they diff, review and version like the rest of the repository, and everything the editor does can also be done in any text editor.
Templates
docs/_templates/ is one flat folder of starter pages. Anything in it shows up
in the "From template" submenu next to the + New button, and — because underscore paths never
publish — templates are versioned with the repo and editable right here in the
editor, without ever appearing on the site.
Two tokens are substituted at creation time:
| Token | Becomes |
|---|---|
{{title}} |
The new file's name, humanised (release-2-1.md → "Release 2 1") |
{{date}} |
Today's date, YYYY-MM-DD |
Every created page — blank or templated — is guaranteed to start with the
title / description / sidebar_position front matter, so nothing shippable
is ever missing it.
The project ships four starters — guide page, tutorial (with <fd-steps>), API
reference (with <fd-api-field>), and release notes. Add your own by dropping a
file in the folder, or by creating one in the editor at _templates/name.md.