carve-php's FencedRenderExtension ships eight fenced-diagram presets.
Each claims one or more fence languages and emits a client-hydration element -
a <pre class="TYPE"> for text-mode presets, or a
<div class="TYPE"><script type="application/json"> for the
JSON-mode ones (Vega-Lite, Chart.js). A browser renderer per type draws the
result below. This is progressive enhancement: if a library or network call
fails, the emitted source stays visible - nothing breaks.
All eight are registered at once through the with_diagrams converter
profile:
'with_diagrams' => [
'safe_mode' => false,
'extensions' => [
['type' => 'fenced_render', 'language' => 'mermaid'],
['type' => 'fenced_render', 'language' => 'd2'],
['type' => 'fenced_render', 'language' => ['dot', 'graphviz'], 'css_class' => 'graphviz'],
['type' => 'fenced_render', 'language' => ['plantuml', 'puml'], 'css_class' => 'plantuml'],
['type' => 'fenced_render', 'language' => 'vega-lite', 'content_mode' => 'json'],
['type' => 'fenced_render', 'language' => 'wavedrom'],
['type' => 'fenced_render', 'language' => 'chart', 'content_mode' => 'json'],
['type' => 'fenced_render', 'language' => 'abc'],
],
],
Flowcharts, sequence and state diagrams. Emits <pre class="mermaid">; mermaid.run() draws it.
Browser renderer: mermaid.js (ESM, jsDelivr)
``` mermaid
graph LR
A[Write Carve] --> B{Render}
B --> C[Interactive HTML]
B --> D[Static / PDF]
```
<pre class="mermaid" role="img" aria-label="mermaid">graph LR
A[Write Carve] --> B{Render}
B --> C[Interactive HTML]
B --> D[Static / PDF]</pre>
graph LR
A[Write Carve] --> B{Render}
B --> C[Interactive HTML]
B --> D[Static / PDF]
UML shapes Mermaid does not cover (sequence, use case, component). Claims plantuml and puml fences.
Browser renderer: public PlantUML server (~h hex encoding)
``` plantuml
@startuml
actor User
participant "Laravel App" as App
participant "laravel-carve" as Carve
User -> App: submit Carve markup
App -> Carve: toHtml(source, 'with_diagrams')
Carve --> App: <pre class="plantuml">...</pre>
App --> User: rendered page
@enduml
```
<pre class="plantuml" role="img" aria-label="plantuml">@startuml
actor User
participant "Laravel App" as App
participant "laravel-carve" as Carve
User -> App: submit Carve markup
App -> Carve: toHtml(source, 'with_diagrams')
Carve --> App: <pre class="plantuml">...</pre>
App --> User: rendered page
@enduml</pre>
@startuml actor User participant "Laravel App" as App participant "laravel-carve" as Carve User -> App: submit Carve markup App -> Carve: toHtml(source, 'with_diagrams') Carve --> App: <pre class="plantuml">...</pre> App --> User: rendered page @enduml
DOT graphs. Claims both dot and graphviz fences; rendered to SVG entirely in the browser.
Browser renderer: @hpcc-js/wasm Graphviz (WASM, jsDelivr)
``` graphviz
digraph Pipeline {
rankdir=LR;
node [shape=box, style=rounded];
Carve -> Parser -> AST -> Renderer -> HTML;
AST -> Markdown;
AST -> ANSI;
}
```
<pre class="graphviz" role="img" aria-label="graphviz">digraph Pipeline {
rankdir=LR;
node [shape=box, style=rounded];
Carve -> Parser -> AST -> Renderer -> HTML;
AST -> Markdown;
AST -> ANSI;
}</pre>
digraph Pipeline {
rankdir=LR;
node [shape=box, style=rounded];
Carve -> Parser -> AST -> Renderer -> HTML;
AST -> Markdown;
AST -> ANSI;
}
Terrastruct D2 diagrams. No practical in-browser build, so the demo posts the source to a public Kroki server; without network the source stays visible.
Browser renderer: Kroki server (POST /d2/svg)
``` d2
Author -> Carve: markup
Carve -> Browser: <pre class="d2">
Browser -> Kroki: render
Kroki -> Browser: SVG
```
<pre class="d2" role="img" aria-label="d2">Author -> Carve: markup
Carve -> Browser: <pre class="d2">
Browser -> Kroki: render
Kroki -> Browser: SVG</pre>
Author -> Carve: markup Carve -> Browser: <pre class="d2"> Browser -> Kroki: render Kroki -> Browser: SVG
Declarative charts from a JSON spec. JSON mode: emits <div class="vega-lite"><script type="application/json">.
Browser renderer: vega + vega-lite + vega-embed (jsDelivr)
``` vega-lite
{
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
"description": "A simple bar chart.",
"data": {"values": [
{"lang": "carve-rs", "speed": 100},
{"lang": "carve-js", "speed": 15},
{"lang": "carve-php", "speed": 5}
]},
"mark": "bar",
"encoding": {
"x": {"field": "lang", "type": "nominal", "axis": {"labelAngle": 0}},
"y": {"field": "speed", "type": "quantitative"}
}
}
```
<div class="vega-lite" role="img" aria-label="vega-lite"><script type="application/json">{
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
"description": "A simple bar chart.",
"data": {"values": [
{"lang": "carve-rs", "speed": 100},
{"lang": "carve-js", "speed": 15},
{"lang": "carve-php", "speed": 5}
]},
"mark": "bar",
"encoding": {
"x": {"field": "lang", "type": "nominal", "axis": {"labelAngle": 0}},
"y": {"field": "speed", "type": "quantitative"}
}
}</script></div>
Digital timing diagrams from a WaveJSON description.
Browser renderer: wavedrom + default skin (jsDelivr)
``` wavedrom
{"signal": [
{"name": "clk", "wave": "p......"},
{"name": "req", "wave": "0.1..0."},
{"name": "ack", "wave": "0...1.0"}
]}
```
<pre class="wavedrom" role="img" aria-label="wavedrom">{"signal": [
{"name": "clk", "wave": "p......"},
{"name": "req", "wave": "0.1..0."},
{"name": "ack", "wave": "0...1.0"}
]}</pre>
{"signal": [
{"name": "clk", "wave": "p......"},
{"name": "req", "wave": "0.1..0."},
{"name": "ack", "wave": "0...1.0"}
]}
Chart.js config as JSON. JSON mode: emits <div class="chart"><script type="application/json">.
Browser renderer: chart.js (UMD, jsDelivr)
``` chart
{
"type": "doughnut",
"data": {
"labels": ["HTML", "Markdown", "ANSI", "Plain text"],
"datasets": [{
"data": [40, 25, 20, 15],
"backgroundColor": ["#ff2d20", "#f6ad55", "#4299e1", "#48bb78"]
}]
},
"options": {"plugins": {"legend": {"position": "right"}}}
}
```
<div class="chart" role="img" aria-label="chart"><script type="application/json">{
"type": "doughnut",
"data": {
"labels": ["HTML", "Markdown", "ANSI", "Plain text"],
"datasets": [{
"data": [40, 25, 20, 15],
"backgroundColor": ["#ff2d20", "#f6ad55", "#4299e1", "#48bb78"]
}]
},
"options": {"plugins": {"legend": {"position": "right"}}}
}</script></div>
Sheet music from ABC music notation, rendered to SVG.
Browser renderer: abcjs (jsDelivr)
``` abc
X:1
T:Carve Fanfare
M:4/4
L:1/4
K:C
C D E F | G A B c | c B A G | F E D C |]
```
<pre class="abc" role="img" aria-label="abc">X:1
T:Carve Fanfare
M:4/4
L:1/4
K:C
C D E F | G A B c | c B A G | F E D C |]</pre>
X:1 T:Carve Fanfare M:4/4 L:1/4 K:C C D E F | G A B c | c B A G | F E D C |]
The img_fence shorthand renders an img (alias
image) fence: the SVG body is sanitized and emitted as a sandboxed
data:image/svg+xml <img>. The <script>
and inline onclick handler in the source below are stripped by the
sanitizer - inspect the rendered <img> to confirm. No client
library or external service is needed; this one renders fully server-side.
{alt="Carve logo mark"}
``` img
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 120 120" width="120" height="120">
<title>Carve logo mark</title>
<rect width="120" height="120" rx="16" fill="#ff2d20"/>
<path d="M30 78 L60 30 L90 78 Z" fill="#ffffff"/>
<circle cx="60" cy="70" r="10" fill="#ff2d20"/>
<script>alert('this script is stripped by the sanitizer')</script>
<rect x="0" y="0" width="10" height="10" onclick="alert('handler stripped too')"/>
</svg>
```
'with_img_fence' => [
'safe_mode' => true,
'extensions' => [
'img_fence',
],
],
Every shorthand the ExtensionFactory understands is discoverable via
ExtensionFactory::types(). Pass any of these as a bare string (or an
['type' => '...'] array) in a converter profile's
extensions list.
admonition
ascii_heading_ids
autolink
citations
code_callouts
code_group
color_swatch
default_attributes
details
external_links
fenced_render
frontmatter
glossary
heading_level_shift
heading_numbers
heading_permalinks
heading_reference
img_fence
index
inline_footnotes
list_table
lowercase_heading_ids
math_block
mentions
mermaid
plantuml
plus_bullet
semantic_span
smart_quotes
spoiler
tab_normalize
table_of_contents
tabs
toc_placement
wikilinks
use MarkupCarve\LaravelCarve\Service\ExtensionFactory;
$types = ExtensionFactory::types(); // 35 shorthands