markup-carve / laravel-carve
Laravel integration for markup-carve/carve-php — Blade directives, services, validation, and caching
Requires
- php: ^8.2
- illuminate/contracts: ^11.0 || ^12.0 || ^13.0
- illuminate/support: ^11.0 || ^12.0 || ^13.0
- illuminate/view: ^11.0 || ^12.0 || ^13.0
- markup-carve/carve-php: ^0.1.9
Requires (Dev)
- orchestra/testbench: ^9.0 || ^10.0 || ^11.0
- php-collective/code-sniffer: ^0.6.0
- phpstan/phpstan: ^2.2
- phpunit/phpunit: ^11.5 || ^12.5 || ^13.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-09-23 11:33:35 UTC
README
Carve markup language integration for Laravel: Blade directives, services, validation, and caching.
Installation
composer require markup-carve/laravel-carve
The service provider and Carve facade alias are auto-discovered via Laravel's package discovery.
Optionally publish the config:
php artisan vendor:publish --tag=carve-config
Usage
Blade Directives
{{-- Safe by default - XSS protection enabled --}} @carve($article->body) {{-- For trusted content only - no XSS protection --}} @carveRaw($trustedContent) {{-- Plain text output (escaped) --}} @carveText($article->body)
Facade
use MarkupCarve\LaravelCarve\Facades\Carve; $html = Carve::toHtml($source); $text = Carve::toText($source); $md = Carve::toMarkdown($source); $ansi = Carve::toAnsi($source); // terminal output $raw = Carve::toHtmlRaw($trustedSource);
Dependency Injection
use MarkupCarve\LaravelCarve\Service\CarveConverterInterface; use MarkupCarve\LaravelCarve\Service\CarveManager; class ArticleController { public function __construct( private CarveConverterInterface $carve, private CarveManager $manager, ) {} public function show(Article $article): View { return view('article.show', [ 'html' => $this->carve->toHtml($article->body), 'text' => $this->carve->toText($article->body), 'docs' => $this->manager->toHtml($article->body, 'docs'), ]); } }
Configuration
// config/carve.php return [ 'include_root' => null, 'converters' => [ // Default has safe_mode: true (XSS protection enabled) 'default' => [ 'safe_mode' => true, 'symbols' => [], 'source_lines' => false, ], // For trusted content (admin, CMS) 'trusted' => [ 'safe_mode' => false, ], ], 'cache' => [ 'enabled' => false, 'store' => null, ], ];
symbols maps :name: shortcodes to trusted raw HTML. Symbol values are
inserted without escaping, so never populate this option from user input.
Set source_lines to true to add 1-based data-source-line attributes to
rendered blocks for editor preview scroll-sync. Both options are configured per
converter profile.
File includes
Blade directives and string methods never read files. To enable includes for trusted file-backed content, configure an absolute containment root and call the explicit file API:
// config/carve.php 'include_root' => '/srv/app/content', $report = Carve::toHtmlFileWithReport('/srv/app/content/book/main.crv'); $html = $report['value']; $warnings = $report['warnings']; $dependencies = $report['dependencies'];
Carve::toHtmlFile() returns only the HTML. Nested paths resolve relative to
the file containing each directive. Traversal and symlink escapes are refused.
Warnings sent to Laravel's logger omit resolver details and replace outside
paths.
include_root has to be an absolute path. A relative one is refused rather
than resolved against the working directory, which is arbitrary with respect to
the document, so a misconfigured root raises at boot instead of widening
silently.
When package caching is enabled, a cached file render is served only while every recorded dependency still hashes the same, so editing an included file cannot serve the parent page's stale HTML. Missing targets are recorded too: creating one is what makes its directive start working.
Multiple Converter Profiles
Use different configurations for different contexts:
{{-- Default is safe --}} @carve($comment->body) {{-- Use named converter for trusted content --}} {!! Carve::toHtml($article->body, 'trusted') !!} {{-- Or use @carveRaw for quick trusted rendering --}} @carveRaw($article->body)
Safe Mode
Safe mode is enabled by default for XSS protection. Disable only for trusted content:
'converters' => [ 'trusted' => [ 'safe_mode' => false, ], ],
Extensions
Enable carve-php extensions per converter:
'converters' => [ 'default' => [ 'extensions' => [ ['type' => 'autolink'], ['type' => 'smart_quotes'], [ 'type' => 'heading_permalinks', 'symbol' => '#', 'position' => 'after', ], ], ], 'with_mentions' => [ 'extensions' => [ [ 'type' => 'mentions', 'user_url_template' => 'https://github.com/{username}', ], 'table_of_contents', ], ], ],
Available extensions:
admonition- Admonition blocks (note, tip, warning, danger, etc.)autolink- Auto-convert URLs to clickable linkscitations- Bracketed citations with an in-document bibliography (numbered or author-date)code_callouts- Numbered callout markers on fenced-code lines with a bound explanation listcode_group- Transform code-group divs into tabbed interfacescolor_swatch- Inline color swatches for CSS color tokens via thecolorroledefault_attributes- Add default attributes to elements by typedetails- Render::: detailsblocks as native<details>/<summary>widgetsexternal_links- Configure external link behavior (target, rel)fenced_render- Emit fenced blocks of a chosen language as client-rendered hydration elementsfrontmatter- Parse YAML/TOML/JSON frontmatter blocksglossary- Glossary definition lists with linked term referencesheading_level_shift- Shift heading levels up/downheading_numbers- Auto-number sections and rewrite heading cross-referencesheading_permalinks- Add anchor links to headingsheading_reference- Link to headings with[text](#heading)syntaxindex- Collect:index[term]markers into a sorted index blockinline_footnotes- Convert spans with class to inline footnoteslist_table- Author tables as nested lists (::: list-table) with block content in cellsmath_block- Rendermathfenced code blocks as display mathmentions- Convert @username to profile linksmermaid- Render Mermaid diagram code blocksplantuml- Render PlantUML/pumldiagram code blocks (needs a client renderer; see below)semantic_span- Convert spans to<kbd>,<dfn>,<abbr>elementssmart_quotes- Convert straight quotes to typographic quotesspoiler- Hidden spoiler content revealed on interactiontab_normalize- Expand tabs in code content to spaces at render timetable_of_contents- Generate TOC from headingstabs- Tabbed content blocks (CSS or ARIA mode)toc_placement- Render the TOC exactly where a::: tocblock appearswikilinks- Support[[Page Name]]wiki-style links
See Extensions documentation for detailed configuration options.
Client-side diagram rendering
The diagram extensions emit a <pre class="LANG"> hydration element; the browser
turns it into a picture. Mermaid, WaveDrom, Vega-Lite and Chart each render once
you load their library. Graphviz and D2 render fully offline with the
WebAssembly helpers from
@markup-carve/carve-grammars
(no server, no external call):
import { renderDiagrams } from '@markup-carve/carve-grammars/diagrams' await renderDiagrams(document.querySelector('.carve-content')) // graphviz + d2, offline
PlantUML is the exception - it has no practical in-browser renderer and needs a Kroki server:
import { renderKrokiDiagrams } from '@markup-carve/carve-grammars/diagrams/kroki' await renderKrokiDiagrams(document.querySelector('.carve-content'), { server: 'https://kroki.internal', // your own instance })
⚠️ Privacy / GDPR: the default Kroki server is the public
https://kroki.io, so the PlantUML source is sent to a third party outside your domain. For sensitive content, or to stay offline, pointserverat a self-hosted or localhost Kroki so no data leaves your control, and disclose the external call to end users where required. For a build-time / SSR pipeline, render diagrams server-side instead so no client JS ships.
Validation Rule
Validate that a field contains valid Carve markup:
use MarkupCarve\LaravelCarve\Rules\ValidCarve; $request->validate([ 'body' => ['required', 'string', new ValidCarve()], ]);
Documentation
Full documentation: markup-carve.github.io/laravel-carve
- Installation
- Configuration
- Blade Usage
- Service Usage
- Validation
- Safe Mode
- Extensions
- Caching
- Carve Syntax
Demo Application
A full runnable demo app lives at laravel-carve-demo: the Blade directives, facade and service usage, form validation, safe-mode comparison, static render mode, plain text extraction and the extension set.
What is Carve?
Carve is a lightweight markup language for structured documents, with clear, consistent syntax. It supports rich document features such as footnotes, definition lists, task lists, and math.
Learn more about Carve syntax at github.com/markup-carve/carve.
Ecosystem
This package is part of the Carve organization - the spec with its conformance corpus, three byte-identical reference implementations (JS, PHP, Rust), editor plugins, and framework integrations. See awesome-carve for a curated list of everything Carve.
