Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
82 commits
Select commit Hold shift + click to select a range
5ea108b
DEV: Add Starlight documentation site
gschlager Apr 24, 2026
1669b50
DEV: Polish docs site header and Ruby compatibility notes
gschlager May 5, 2026
bbccfcf
DEV: Ignore pnpm content-addressable store
gschlager May 5, 2026
84a0595
DEV: Polish docs landing page
gschlager May 5, 2026
5cae721
DEV: Slim CLAUDE.md to project-specific guardrails
gschlager May 5, 2026
d4a2485
DEV: Correct stale Reordering closing-strategy claims
gschlager May 5, 2026
d2a6e09
DEV: Use per-format requires in docs guides
gschlager May 5, 2026
d40b3cd
DEV: Spell out the Nokogiri requirement in the TextFormatter guide
gschlager May 5, 2026
5654133
DEV: Tighten accuracy of landing-page and guide claims
gschlager May 5, 2026
4c9bc30
DEV: Use minimal requires in docs examples
gschlager May 5, 2026
979b935
DEV: Generate the docs changelog from GitHub Releases
gschlager May 5, 2026
b726009
DEV: Guard AST docs coverage with a spec
gschlager May 5, 2026
1349718
DEV: Add docs code-example runner spec (prototype)
gschlager May 5, 2026
0066060
DEV: Teach the docs code-example runner about directives
gschlager May 5, 2026
82e98d3
DEV: Wire docs snippets up to the code-example runner
gschlager May 5, 2026
5b239ee
DEV: Park a migration-API plan on the docs branch
gschlager May 5, 2026
53635de
DEV: Restructure docs around the migration use case
gschlager May 6, 2026
5a2f2df
DEV: Track docs site npm dependencies via dependabot
gschlager May 6, 2026
b0ee579
DEV: Use generic names in migration doc examples
gschlager May 6, 2026
e087bc8
DEV: Add Excalidraw diagrams to docs
gschlager May 7, 2026
4732ac6
DEV: Click-to-zoom for embedded diagrams
gschlager May 7, 2026
00870da
DEV: Force fresh public/ assets in dev
gschlager May 7, 2026
795d1ab
DEV: Use phpBB3 attachment BBCode in placeholder example
gschlager May 8, 2026
11610e4
DEV: Resolve attachment placeholder to [upload|HASH] at render time
gschlager May 8, 2026
a19a316
DEV: Rename attachment placeholder hash to upload_id
gschlager May 8, 2026
5d857f1
DEV: Store filename on AttachmentPlaceholder; emit on :uploads
gschlager May 8, 2026
55a542f
DEV: Reword the phpBB3 attachment intro
gschlager May 8, 2026
47f42ec
DEV: Document handler-side resolution architecture
gschlager May 8, 2026
1320bf6
DEV: Adopt silo's built-in Ruby/Node toolchain management
gschlager Jun 10, 2026
6158617
DEV: Regenerate placeholder diagrams
gschlager Jun 10, 2026
068bdcc
DEPS: Update docs site deps (astro 7, starlight 0.41, sharp 0.35)
gschlager Jun 29, 2026
e225431
DEV: Install node + pnpm via .silo.yml for the docs site
gschlager Jun 29, 2026
2d7b46b
DEV: Realign migration docs with the emit-free Conversion API
gschlager Jun 29, 2026
a19fa0b
DEV: Linkify #NNN PR references in the generated changelog
gschlager Jun 29, 2026
4a77ed3
DEV: Simplify the docs landing page cards
gschlager Jun 29, 2026
29099ce
DEV: Rework docs structure, copy, and stray files
gschlager Jun 30, 2026
b39e31a
DEV: Drop the full-example page and the examples/ dir
gschlager Jun 30, 2026
4b99e45
DEV: Note the pipeline is renderer-agnostic on the architecture page
gschlager Jun 30, 2026
0abd693
DEV: Make the docs code-example suite green
gschlager Jun 30, 2026
6a21567
DEV: Calmer card hover and a faint surface tint on the landing page
gschlager Jun 30, 2026
e34371e
DEV: Update diagram sources to the emit-free flow
gschlager Jun 30, 2026
854cf5d
DEV: Shorten the format-guide nav labels
gschlager Jun 30, 2026
40daa0e
DEV: Tighten landing-page cards instead of tinting them
gschlager Jun 30, 2026
37bdf3a
DEV: Consolidate duplicate blocks in custom.css
gschlager Jun 30, 2026
3d39bf7
DEV: Re-export diagram SVGs from the emit-free sources
gschlager Jun 30, 2026
b6ec7e5
DEV: Drop '→ Markdown' from the format-guide links on Getting Started
gschlager Jun 30, 2026
bf8e760
DEV: Pin pnpm via packageManager in docs
gschlager Jun 30, 2026
9992d05
DEV: Add robots.txt pointing crawlers at the sitemap
gschlager Jun 30, 2026
932c55d
DEV: Build docs on PRs, deploy only on main
gschlager Jun 30, 2026
c74a25b
FIX: Drop pnpm version from docs workflow to avoid pin conflict
gschlager Jun 30, 2026
76b3129
FIX: Bump docs CI to Node 22 for pnpm 11
gschlager Jun 30, 2026
eb8ddfd
DEV: Update docs workflow actions to latest
gschlager Jun 30, 2026
b9d4ade
DEV: Run the docs code-example specs on CRuby only
gschlager Jun 30, 2026
f565ed4
DEV: Fix misleading 'quotes' example in the migration overview
gschlager Jun 30, 2026
720d0ee
DEV: Make the docs dev server watch Markdown in containers
gschlager Jun 30, 2026
650315e
DEV: Revert dev-server polling — it wasn't an inotify issue
gschlager Jun 30, 2026
defe94e
DEV: Drop Vite usePolling from the docs dev server
gschlager Jun 30, 2026
55ab5c6
DEV: Simplify the overview diagram's read-back label
gschlager Jun 30, 2026
ac246db
DEV: Account for the diagram's read-back step in the overview text
gschlager Jun 30, 2026
19d1e93
DEV: Drop the read-back box from the Four stages diagram
gschlager Jun 30, 2026
be66e8f
DEV: Use parser-neutral wording in the overview Parse stage
gschlager Jun 30, 2026
00f1c74
DEV: Reorder nav so the site doesn't read as Discourse-migration-only
gschlager Jun 30, 2026
b634c29
DEV: Tone down 'importer' framing on the migration overview
gschlager Jun 30, 2026
b4bc62c
DEV: Add an Introduction page as the docs front door
gschlager Jun 30, 2026
48dab32
DEV: Stop re-teaching the pipeline on the migration overview
gschlager Jun 30, 2026
9243c34
DEV: Put Getting Started first in the nav, Introduction second
gschlager Jun 30, 2026
3e13fae
DEV: Revert nav order — Introduction back above Getting Started
gschlager Jun 30, 2026
0f697b2
DEV: Drop Bundler from Getting Started requirements
gschlager Jun 30, 2026
20c2e61
DEV: Add section landing pages so sections are linkable
gschlager Jun 30, 2026
fe0f925
DEV: Make the single requirement a sentence, not a bullet
gschlager Jun 30, 2026
c3177a9
DEV: Drop '→ Markdown' from format-guide page titles
gschlager Jun 30, 2026
4b89192
DEV: Give Conversion/Parse a general home, off the migration page
gschlager Jun 30, 2026
96db840
DEV: Unify BBCode tag tables to one schema with aligned columns
gschlager Jul 1, 2026
bc2d00d
DEV: Unify all four format-guide tables to Tags | Renders as | AST node
gschlager Jul 1, 2026
78d0b26
DEV: Restore useful format variant notes as prose under the tables
gschlager Jul 1, 2026
49f7216
Merge remote-tracking branch 'origin/main' into docs
gschlager Jul 9, 2026
23b4e0f
DEV: Document the 0.3.0 breaking changes and refresh perf claims
gschlager Jul 9, 2026
dd20b43
Merge remote-tracking branch 'origin/main' into docs
gschlager Jul 20, 2026
80723f5
DEV: Document the 0.3.1 AST normalization on the docs site
gschlager Jul 20, 2026
e6d0018
DEV: Normalize emoji and GitHub alerts in the generated changelog
gschlager Jul 20, 2026
e97a544
DEV: Sync docs with main and refresh the documentation site
gschlager Sep 18, 2026
908097c
DEV: Mark documentation as work in progress and disable indexing
gschlager Sep 18, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,17 @@ updates:
gems:
patterns:
- "*"

- package-ecosystem: npm
directory: "/docs"
schedule:
interval: "weekly"
commit-message:
prefix: "DEPS"
versioning-strategy: increase
allow:
- dependency-type: "all"
groups:
docs:
patterns:
- "*"
52 changes: 52 additions & 0 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
name: Deploy docs to Pages

on:
push:
branches: [main]
paths:
- 'docs/**'
- '.github/workflows/docs.yml'
pull_request:
paths:
- 'docs/**'
- '.github/workflows/docs.yml'
release:
types: [published, edited, deleted]
workflow_dispatch:

permissions:
contents: read
pages: write
id-token: write

# Per-ref so a PR build never queues behind (or blocks) a main deploy.
# Cancel superseded PR builds; never cancel an in-flight deploy.
concurrency:
group: pages-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: withastro/action@v6
with:
path: ./docs
# Astro requires Node 22.12 or newer.
node-version: 22
# pnpm version comes from the "packageManager" field in
# docs/package.json — passing it here too makes pnpm/action-setup
# error with "Multiple versions of pnpm specified".

deploy:
needs: build
# PRs validate the build only; deploy happens on push to main / release.
if: github.event_name != 'pull_request'
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- id: deployment
uses: actions/deploy-pages@v5
1 change: 1 addition & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,6 @@ Lint/UnusedMethodArgument:

RSpec/DescribeClass:
Exclude:
- spec/docs/**/*
- spec/integration/**/*
- spec/system/**/*
15 changes: 11 additions & 4 deletions .silo.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,27 @@ use:
default: "4.0"

setup:
- sudo dnf update -y
# Node + pnpm for the docs site (Astro/Starlight); both ship as Fedora packages.
- sudo dnf install -y nodejs pnpm
- bundle install
- bundle exec lefthook install
- bundle exec lefthook install -f
- (cd docs && CI=true pnpm install)

sync:
- bundle install
- (cd docs && CI=true pnpm install)

update:
- sudo dnf update -y
- rv self update

ports:
- 4567:4567

daemons:
playground:
cmd: bin/playground
ports: ["4567"]
autostart: true
docs:
cmd: bin/docs
ports: ["4321"]
autostart: true
20 changes: 10 additions & 10 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Quick reference guide for AI assistants working on the Markbridge codebase.

## Project Overview

**Markbridge** converts BBCode to Discourse-flavored Markdown using a **Parse → AST → Render** pipeline.
**Markbridge** converts BBCode, HTML, MediaWiki, and s9e TextFormatter XML to Markdown using a **Parse → AST → Render** pipeline. The shipped renderer produces Discourse-flavored Markdown. The parsers and AST are renderer-agnostic.

**Design Philosophy:**
- Graceful degradation (unknown tags preserved, no exceptions)
Expand Down Expand Up @@ -155,7 +155,7 @@ check ships as a shared RSpec example (`require "markbridge/rspec"`,
then `it_behaves_like "an html_mode safe tag"`), so consumer projects
can run it against their own tags.

**See `examples/` for complete examples.**
**See `docs/src/content/docs/customization/extending.md` for complete examples.**

## Development Workflow

Expand Down Expand Up @@ -217,7 +217,7 @@ automatically when mutation work comes up.
bundle exec ruby --yjit bench/bench.rb --isolated`). It pins the process
to the fastest cores and reports power and governor state. Results from
a laptop on battery, or from an efficiency core, don't compare with
the numbers in `docs/benchmarks.md`.
the numbers in `docs/src/content/docs/concepts/benchmarks.md`.

**MarkdownEscaper** is a hot path. Benchmark before/after any change to
`lib/markbridge/renderers/discourse/markdown_escaper.rb` with the
Expand Down Expand Up @@ -299,17 +299,17 @@ refactors when behavior is equivalent.

- **This file**: Quick reference and architecture
- **README.md**: User-facing quick start
- **docs/architecture.md**: System architecture and design patterns
- **docs/parsers/**: BBCode, HTML, and TextFormatter parser guides
- **docs/renderers/**: Discourse renderer guide
- **docs/extending.md**: How to add custom tags and handlers
- **docs/performance.md**: Performance optimization guide
- **examples/**: Runnable code examples
- **docs/src/content/docs/concepts/architecture.md**: System architecture and design patterns
- **docs/src/content/docs/format-guides/**: BBCode, HTML, and TextFormatter parser guides
- **docs/src/content/docs/concepts/renderers.md**: Discourse renderer guide
- **docs/src/content/docs/customization/extending.md**: How to add custom tags and handlers
- **docs/src/content/docs/concepts/performance.md**: Performance optimization guide
- **spec/docs/**: Checks for runnable documentation examples and AST coverage
- **spec/**: Executable documentation (tests show expected behavior)

---

**Maintenance**: This file should be updated when core architecture changes. Details that change frequently (file counts, specific line numbers, step-by-step tutorials) are intentionally excluded. Point to examples/ and spec/ for those.
**Maintenance**: This file should be updated when core architecture changes. Details that change frequently (file counts, specific line numbers, step-by-step tutorials) are intentionally excluded. Point to the docs site and spec/ for those.

**Last Updated**: 2025-11-26
**Version**: 0.1.0
2 changes: 2 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ source "https://rubygems.org"

gemspec

gem "benchmark"
gem "benchmark-ips"
gem "csv"
gem "commonmarker", install_if: -> { RUBY_ENGINE == "ruby" }
gem "lefthook"
gem "nokogiri"
Expand Down
6 changes: 6 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ GEM
ice_nine (~> 0.11.0)
thread_safe (~> 0.3, >= 0.3.1)
base64 (0.3.0)
benchmark (0.5.0)
benchmark-ips (2.15.1)
bigdecimal (4.1.3)
bigdecimal (4.1.3-java)
Expand All @@ -31,6 +32,7 @@ GEM
commonmarker (2.10.0-x86_64-linux)
commonmarker (2.10.0-x86_64-linux-musl)
concurrent-ruby (1.3.8)
csv (3.3.5)
descendants_tracker (0.0.4)
thread_safe (~> 0.3, >= 0.3.1)
diff-lcs (1.6.2)
Expand Down Expand Up @@ -277,8 +279,10 @@ PLATFORMS
x86_64-linux-musl

DEPENDENCIES
benchmark
benchmark-ips
commonmarker
csv
lefthook
markbridge!
mutant
Expand All @@ -300,6 +304,7 @@ CHECKSUMS
ast (2.4.3) sha256=954615157c1d6a382bc27d690d973195e79db7f55e9765ac7c481c60bdb4d383
axiom-types (0.1.1) sha256=c1ff113f3de516fa195b2db7e0a9a95fd1b08475a502ff660d04507a09980383
base64 (0.3.0) sha256=27337aeabad6ffae05c265c450490628ef3ebd4b67be58257393227588f5a97b
benchmark (0.5.0) sha256=465df122341aedcb81a2a24b4d3bd19b6c67c1530713fd533f3ff034e419236c
benchmark-ips (2.15.1) sha256=07a1a9f3c6105ecaf68c174fc3fbcddd71a0e9ada6236ae03093a0dcfd812d59
bigdecimal (4.1.3) sha256=61ebe1e5e559bdc3cc6f2c0ee7f427321fc838f59611c294356eb04d6e21cf66
bigdecimal (4.1.3-java) sha256=9a6a1fa67723a27ab1b3a6d5526322d8c82170af03199da7c915b8d9a1770532
Expand All @@ -315,6 +320,7 @@ CHECKSUMS
commonmarker (2.10.0-x86_64-linux) sha256=945c510c0bfca9022245928e2248468fe3ddad58c939be0c37b20082cc392880
commonmarker (2.10.0-x86_64-linux-musl) sha256=288cd4fb9f17eed2ffa73551ef241dcf594e0f484b2aee2a0999540333c0618e
concurrent-ruby (1.3.8) sha256=b2f1be836e968ccc78ccfce277ea79c72a88633f22306782c16ff23fb415d1e1
csv (3.3.5) sha256=6e5134ac3383ef728b7f02725d9872934f523cb40b961479f69cf3afa6c8e73f
descendants_tracker (0.0.4) sha256=e9c41dd4cfbb85829a9301ea7e7c48c2a03b26f09319db230e6479ccdc780897
diff-lcs (1.6.2) sha256=9ae0d2cba7d4df3075fe8cd8602a8604993efc0dfa934cff568969efb1909962
dry-configurable (1.4.0) sha256=e35d1b5f3c081753ef361f564919db79000f32cfa6f20ee3a3ba5921b41b73ce
Expand Down
41 changes: 18 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
# Markbridge

Markbridge converts BBCode into Discourse-flavored Markdown through a clean parse → AST → render pipeline. It is intended for forum migrations and any workflow that needs predictable BBCode handling.
Markbridge converts BBCode, HTML, MediaWiki wikitext, and s9e/TextFormatter XML into Discourse-flavored Markdown through a clean parse → AST → render pipeline. It's built for forum migrations into Discourse, but works for any job that needs predictable, repeatable conversion.

## How it works
Full documentation lives at **[markbridge.dev](https://markbridge.dev)**.

1. **Parse BBCode** – `Markbridge::Parsers::BBCode::Parser` tokenizes input and builds an `AST::Document`, reconciling nesting and collecting raw content where needed.
2. **Transform AST** – The AST captures semantic nodes such as text, formatting elements, lists, URLs, and code blocks that are renderer-agnostic.
3. **Render to Markdown** – `Markbridge::Renderers::Discourse::Renderer` walks the tree with a tag library to emit Discourse-compatible Markdown, then normalizes spacing for final output.
## How it works

Refer to the component guides for more detail:
Every conversion runs the same three steps:

* [BBCode parser](docs/parsers/bbcode.md)
* [Discourse renderer](docs/renderers/discourse.md)
1. **Parse** – a format-specific parser turns the input into an `AST::Document`. Unknown tags are counted, not raised — the parser keeps going.
2. **AST** – a renderer-agnostic tree of text, formatting, lists, links, and so on. The same tree comes out no matter which format went in.
3. **Render** – `Markbridge::Renderers::Discourse::Renderer` walks the tree and emits Discourse-compatible Markdown.

## Installation

Expand All @@ -33,33 +32,29 @@ gem install markbridge
require "markbridge/bbcode"

bbcode = "[b]Hello[/b] [url=https://example.com]world[/url]!"
markdown = Markbridge.bbcode_to_markdown(bbcode)
result = Markbridge.bbcode_to_markdown(bbcode)

puts markdown
puts result.markdown
# => "**Hello** [world](https://example.com)!"
```

## Configuration
Swap `bbcode` for `html`, `mediawiki`, or `textformatter` for the other formats, or `require "markbridge/all"` to load everything.

```ruby
Markbridge.configure do |config|
# Strip trailing spaces before newlines to prevent hard line breaks (<br/>).
# Defaults to false (Discourse has this disabled by default).
config.escape_hard_line_breaks = true
end
```
`*_to_markdown` returns a `Markbridge::Conversion`, not a plain string. The rendered Markdown is on `.markdown` (and `.to_s` delegates to it, so `puts result` works). The same object also carries `.unknown_tags`, `.diagnostics`, and `.errors` — handy when you're migrating a forum and want to know what showed up.

## Customizing output

Configuration applies to all `*_to_markdown` convenience methods (`bbcode_to_markdown`, `html_to_markdown`, etc.).
Build a renderer once with `Markbridge.discourse_renderer(...)` and pass it via `renderer:` — custom tags, a custom escaper, dropping tags, and more. See [Customizing the renderer](https://markbridge.dev/customization/customizing-renderer/), and [Migrating to Discourse](https://markbridge.dev/migrating/overview/) for the full forum-migration workflow.

## Learn more

* See `examples/` for runnable scripts such as `examples/basic_usage.rb`.
* Browse integration and unit coverage under `spec/` to understand supported tags and edge cases.
* Use `bin/console` during development for interactive exploration.
* [markbridge.dev](https://markbridge.dev) – guides, format references, and the architecture deep-dive.
* `spec/` – executable documentation of every supported tag and edge case.
* `bin/console` – an interactive prompt for poking at things during development.

## Development

This repository is set up to run inside [silo](https://github.com/gschlager/silo), a lightweight dev-environment tool. The `.silo.yml` file provisions a Fedora container with JRuby and multiple CRuby versions (via [rv](https://rv.dev)), installs dependencies, and starts the playground daemon. If you prefer your own setup, `bin/setup` and `bundle install` are all you need.
This repository is set up to run inside [silo](https://github.com/gschlager/silo), a development environment tool. The `.silo.yml` file provisions a Fedora container with CRuby, JRuby, TruffleRuby, Node.js, and pnpm. It installs dependencies and starts the playground on port 4567 and the docs site on port 4321. For a Ruby setup outside Silo, use `bin/setup` and `bundle install`. See [docs/README.md](docs/README.md) to run the documentation site.

## Playground

Expand Down
Loading
Loading