markup-hub is a Markdown-first collaboration layer for long-form research writing. It keeps chapter files as the source of truth, assembles them through a main.md manifest, and supports two stable review surfaces:
- an HTML reviewer for browser-based reading, comments, and edit suggestions
- a full-manuscript Markdown workflow for in-meeting editing in VS Code
The repository is intentionally small and local-first. No server, no database, and no required cloud platform beyond the shared folder you already use.
markup-hub currently supports:
- manifest-driven multi-file manuscript assembly
- numbering for chapters, sections, tables, and figures
- a local HTML reviewer with inline annotations saved to
reviews.json - applying saved HTML review comments back into source Markdown
- building a reversible
paper.full.mdfor direct editing - syncing edits from
paper.full.mdback into chapter files - optional Git snapshot prompting after sync
The source of truth is always:
main.md- chapter files under
src/or other included paths - your numbering and build rules
Reviewer-facing artifacts are generated from those sources.
Example manifest:
<!-- INCLUDE src/intro.md -->
<!-- INCLUDE src/related_work.md -->
<!-- INCLUDE src/results.md -->workspace/
|-- markup-hub/
|-- discussion-a/
| |-- main.md
| |-- src/
| `-- .git/
|-- proposal-b/
| |-- main.md
| |-- src/
| `-- .git/
`-- collab_viewer.html
Inside markup-hub/:
markup-hub/
|-- apply_review.py
|-- build_full.py
|-- build_viewer.py
|-- manuscript.py
|-- meeting.py
|-- sync_full.py
|-- collab_config.toml
|-- collab_viewer_template.html
|-- core/
| |-- config.py
| |-- manifests.py
| |-- merge_md.py
| |-- number_md.py
| |-- numbering.py
| `-- validate_md.py
|-- export/
| |-- build_html.py
| |-- build_word.ps1
| |-- export_docx.py
| `-- export_pdf.py
`-- README.md
All paper-level paths live in collab_config.toml. This is the main way to keep the workflow easy to remember as your manuscript folders grow.
Minimal example:
project = "Research Workspace"
subtitle = "Spring 2026"
reviewers = ["Reviewer One", "Reviewer Two"]
default_group = "Discussion Topic A"
[groups."Discussion Topic A"]
main = "../discussion-a/main.md"
enabled = true
docx_template = "../discussion-a/src/archive/template.docx"
[groups."Proposal B"]
main = "../proposal-b/main.md"
enabled = true
# docx_template = "../proposal-b/src/archive/template.docx"You do not need to set up a separate command for each chapter or manually pass manuscript paths every time.
- If
collab_config.tomlcontains only one enabled paper group,python meeting.py buildandpython meeting.py syncwill automatically use that paper. - If you keep multiple paper groups in the config, set
default_group = "..."to make one of them the default no-argument target. - If you want to work on a different paper for a specific run, use
--group, for examplepython meeting.py build --group "Proposal B".
In other words, --group selects a paper, not a chapter.
If you want to temporarily exclude a paper from all config-driven workflows, you have two options:
- set
enabled = falseinside that group's config block - or comment out that group block in the TOML file
Example:
[groups."Proposal B"]
main = "../proposal-b/main.md"
enabled = falseUse this when you want a browser-based reading and annotation surface.
Build the viewer:
python build_viewer.pyReview flow:
- Open the generated
collab_viewer.html - Add comments or edit suggestions
- Save annotations to
reviews.json - Apply them back into the chapter files
python apply_review.pyIf you prefer the VS Code Run and Debug panel, use:
Hub: Build Viewer
Use this when you want one integrated manuscript file for live editing in VS Code.
Build the meeting file:
python meeting.py buildBuild paper.full.md for all enabled papers in the config:
python meeting.py build-allSync edits back into source files:
python meeting.py syncPreview a sync without writing files:
python meeting.py sync --dry-runPrompt for a Git snapshot after sync:
python meeting.py sync --prompt-commitWhen multiple groups are configured and you want to target a different paper:
python meeting.py build --group "Metrics Paper"
python meeting.py sync --group "Metrics Paper"If you prefer the VS Code Run and Debug panel, use:
Hub: Meeting BuildHub: Meeting Sync Dry RunHub: Meeting Sync
Use this when you want the hub to run the regular manuscript build/export steps for a paper repo.
Examples:
python manuscript.py merge
python manuscript.py number
python manuscript.py validate
python manuscript.py html
python manuscript.py docx
python manuscript.py pdf
python manuscript.py allWith multiple configured papers:
python manuscript.py all --group "Metrics Paper"manuscript.py all runs the full pipeline from the hub against the selected paper repo. The paper repo provides content and assets; the hub owns the tooling.
If you prefer the VS Code Run and Debug panel, use:
Hub: Manuscript All
meeting.py build creates a generated paper.full.md beside the target paper's main.md. That file includes protected source markers and numbered headings/captions so it is convenient to read during a meeting.
Important:
paper.full.mdis generated, not source- it is safe and recommended to keep
paper.full.mdout of Git - the files Git should track are
main.md, chapter files undersrc/, and supporting assets - after the meeting, run
meeting.py syncto write accepted edits back into the real source files - use
{{build_date}}in source Markdown when you want the generated full manuscript to show the current build date automatically
meeting.py sync then:
- reads the edited
paper.full.md - maps each block back to its original chapter file
- strips generated heading numbers back to plain Markdown headings
- restores table and figure label placeholders in source files
- refuses to overwrite source files if they changed since the full file was built unless you explicitly force it
- writes
.bakbackups before changing chapter files
The chapter files remain the real manuscript source.
paper.full.md is a generated collaboration surface.
Numbering remains part of the build layer, not the source layer.
- source files keep semantic Markdown
- numbered headings and captions are generated for review artifacts
- sync restores source-friendly Markdown before writing back
This preserves compatibility with your existing manuscript tooling.
Each paper folder can live in its own Git repository even when stored inside a shared OneDrive workspace. Git is used for safety and revision control, not for day-to-day syncing between collaborators.
Recommended practice:
- keep chapter Markdown files under Git
- treat
paper.full.mdas a generated meeting artifact - add
paper.full.mdto the paper repo's.gitignore - create Git snapshots after meaningful sync events, not after every draft edit
- Copy
collab_config.example.tomltocollab_config.toml - Add your paper folders under
[groups] - Set the
reviewerslist for the HTML viewer - Run either the HTML reviewer flow or the meeting flow
markup-hub includes launch.json so the most common workflows can be started from the Run and Debug panel.
To use them:
- Open
markup-hubas the workspace root in VS Code - Open
Run and Debug - Choose a launch config from the dropdown
- Click the green Run button
Available launch configs:
Hub: Build ViewerRebuilds the HTML reviewer from the current Markdown sourcesHub: Meeting BuildRegeneratespaper.full.mdfor the default or selected paperHub: Meeting Sync Dry RunPreviews sync-back frompaper.full.mdwithout writing filesHub: Meeting SyncWrites meeting edits frompaper.full.mdback into the chapter filesHub: Manuscript AllRuns the full manuscript pipeline (merge,number,validate,html,docx,pdf)
Recommended day-to-day use:
- after editing chapter Markdown and wanting to refresh the browser reviewer:
Hub: Build Viewer - before a meeting:
Hub: Meeting Build - after meeting edits, to safely preview the result:
Hub: Meeting Sync Dry Run - after confirming the preview looks right:
Hub: Meeting Sync
core/: manuscript and configuration logiccore/config.py: config loading and group/path resolutioncore/manifests.py: manifest parsing, file loading, labels, and path normalizationcore/merge_md.py,core/number_md.py,core/validate_md.py: manuscript pipeline stepscore/numbering.py: numbering and reference handlingexport/: HTML, DOCX, PDF, and Word-export helpers used bymanuscript.py
markup-hub does not replace your manuscript repositories. It provides stable review and collaboration tooling around Markdown-first papers.
Its current focus is:
- keeping long-form research writing maintainable
- preserving traceability back to chapter files
- giving collaborators a choice between HTML review and direct manuscript editing