Skip to content

Commit bae5894

Browse files
authored
Merge branch 'master' into feature/table-header-equals-syntax
2 parents 09c86ed + ec86f65 commit bae5894

File tree

3 files changed

+407
-8
lines changed

3 files changed

+407
-8
lines changed

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ https://sandbox.dereuromark.de/sandbox/djot
103103
- [Converters](docs/converters.md) - Markdown/BBCode to Djot conversion
104104
- [Cookbook](docs/cookbook.md) - Common customizations and recipes
105105
- [Architecture](docs/architecture.md) - Internal design
106+
- [Enhancements](docs/enhancements.md) - Fixes beyond the current spec
106107

107108
## Security
108109

@@ -117,6 +118,11 @@ Safe mode automatically blocks dangerous URL schemes (`javascript:`, etc.), stri
117118

118119
See [Security Considerations](docs/README.md#security-considerations) for details and advanced configuration.
119120

121+
122+
## Implementations
123+
124+
- [php-collective/wp-djot](https://github.com/php-collective/wp-djot) - WordPress plugin for Djot support
125+
120126
## See Also
121127

122128
- [Djot](https://djot.net/) - Official Djot website with syntax reference and playground

docs/cookbook.md

Lines changed: 149 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -644,22 +644,110 @@ Output:
644644

645645
### Wiki Links
646646

647-
Support `[[Page Name]]` wiki-style links:
647+
Support wiki-style links using a `wiki:` URL scheme. This approach uses standard
648+
djot link syntax and avoids ambiguity with nested spans (since `[[x]{.a}]{.b}` is valid djot).
648649

649650
```php
650-
$parser->addInlinePattern('/\[\[([^\]]+)\]\]/', function ($match, $groups, $p) {
651-
$page = $groups[1];
652-
$link = new Link('/wiki/' . rawurlencode($page));
653-
$link->appendChild(new Text($page));
654-
return $link;
651+
use Djot\DjotConverter;
652+
use Djot\Event\RenderEvent;
653+
654+
$converter = new DjotConverter();
655+
656+
// Handle wiki: scheme in links
657+
$converter->on('render.link', function (RenderEvent $event): void {
658+
$link = $event->getNode();
659+
$url = $link->getDestination() ?? '';
660+
661+
if (str_starts_with($url, 'wiki:')) {
662+
$target = substr($url, 5); // Remove 'wiki:' prefix
663+
664+
// If empty, use the link text as target
665+
if ($target === '') {
666+
$text = '';
667+
foreach ($link->getChildren() as $child) {
668+
if ($child instanceof \Djot\Node\Inline\Text) {
669+
$text .= $child->getContent();
670+
}
671+
}
672+
$target = $text;
673+
}
674+
675+
// Convert to URL slug
676+
$slug = strtolower(str_replace(' ', '-', $target));
677+
$link->setDestination('/wiki/' . rawurlencode($slug));
678+
$link->setAttribute('class', 'wikilink');
679+
}
655680
});
656681

657-
echo $converter->convert('See [[Home Page]] and [[Getting Started]].');
682+
echo $converter->convert('See [Home Page](wiki:) and [the API docs](wiki:API Reference).');
658683
```
659684

660685
Output:
661686
```html
662-
<p>See <a href="/wiki/Home%20Page">Home Page</a> and <a href="/wiki/Getting%20Started">Getting Started</a>.</p>
687+
<p>See <a href="/wiki/home-page" class="wikilink">Home Page</a> and <a href="/wiki/api-reference" class="wikilink">the API docs</a>.</p>
688+
```
689+
690+
The syntax:
691+
- `[Page Name](wiki:)` - link text becomes the target
692+
- `[display text](wiki:Page Name)` - explicit target with custom display text
693+
694+
#### Configurable Base URL
695+
696+
```php
697+
$wikiBaseUrl = '/docs/'; // or 'https://wiki.example.com/'
698+
699+
$converter->on('render.link', function (RenderEvent $event) use ($wikiBaseUrl): void {
700+
$link = $event->getNode();
701+
$url = $link->getDestination() ?? '';
702+
703+
if (str_starts_with($url, 'wiki:')) {
704+
$target = substr($url, 5);
705+
706+
if ($target === '') {
707+
$text = '';
708+
foreach ($link->getChildren() as $child) {
709+
if ($child instanceof \Djot\Node\Inline\Text) {
710+
$text .= $child->getContent();
711+
}
712+
}
713+
$target = $text;
714+
}
715+
716+
$slug = strtolower(str_replace(' ', '-', $target));
717+
$link->setDestination($wikiBaseUrl . rawurlencode($slug));
718+
}
719+
});
720+
```
721+
722+
#### With File Extension
723+
724+
```php
725+
use Djot\Event\RenderEvent;
726+
727+
// Add .html extension for static sites
728+
$converter->on('render.link', function (RenderEvent $event): void {
729+
$link = $event->getNode();
730+
$url = $link->getDestination() ?? '';
731+
732+
if (str_starts_with($url, 'wiki:')) {
733+
$target = substr($url, 5);
734+
735+
if ($target === '') {
736+
$text = '';
737+
foreach ($link->getChildren() as $child) {
738+
if ($child instanceof \Djot\Node\Inline\Text) {
739+
$text .= $child->getContent();
740+
}
741+
}
742+
$target = $text;
743+
}
744+
745+
$slug = strtolower(str_replace(' ', '-', $target));
746+
$link->setDestination('/pages/' . $slug . '.html');
747+
}
748+
});
749+
750+
// [Installation Guide](wiki:) → <a href="/pages/installation-guide.html">Installation Guide</a>
663751
```
664752

665753
### Hashtags
@@ -678,6 +766,59 @@ $parser->addInlinePattern('/#([a-zA-Z][a-zA-Z0-9_]*)/', function ($match, $group
678766
echo $converter->convert('Check out #PHP and #WebDev!');
679767
```
680768

769+
### Root-Relative Links
770+
771+
Support `<~/path>` and `<~/path|display text>` for site-root-relative links:
772+
773+
```php
774+
use Djot\DjotConverter;
775+
use Djot\Node\Inline\Link;
776+
use Djot\Node\Inline\Text;
777+
778+
$converter = new DjotConverter();
779+
$parser = $converter->getParser()->getInlineParser();
780+
781+
// Pattern matches <~/path> or <~/path|display text>
782+
$parser->addInlinePattern('/<~([^>|]+)(?:\|([^>]+))?>/​', function ($match, $groups, $p) {
783+
$path = trim($groups[1]);
784+
$display = isset($groups[2]) ? trim($groups[2]) : basename($path);
785+
786+
// Build root-relative URL
787+
$url = '/' . ltrim($path, '/');
788+
789+
$link = new Link($url);
790+
$link->appendChild(new Text($display));
791+
$link->setAttribute('class', 'internal-link');
792+
return $link;
793+
});
794+
795+
echo $converter->convert('See <~/docs/installation> and <~/api/users|the API>.');
796+
```
797+
798+
Output:
799+
```html
800+
<p>See <a href="/docs/installation" class="internal-link">installation</a> and <a href="/api/users" class="internal-link">the API</a>.</p>
801+
```
802+
803+
#### Configurable Base Path
804+
805+
```php
806+
$basePath = '/docs/v2'; // Prepend to all paths
807+
808+
$parser->addInlinePattern('/<~([^>|]+)(?:\|([^>]+))?>/​', function ($match, $groups, $p) use ($basePath) {
809+
$path = trim($groups[1]);
810+
$display = isset($groups[2]) ? trim($groups[2]) : basename($path);
811+
812+
$url = $basePath . '/' . ltrim($path, '/');
813+
814+
$link = new Link($url);
815+
$link->appendChild(new Text($display));
816+
return $link;
817+
});
818+
819+
// <~/guide> → <a href="/docs/v2/guide">guide</a>
820+
```
821+
681822
### Conditional Patterns
682823

683824
Return `null` to fall back to default parsing:

0 commit comments

Comments
 (0)