@kirigami/php-prepros 3.2.1 → 3.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -249,7 +249,7 @@ seo: seo:
249
249
 
250
250
  Positional args: source path (relative to `image.source`), width, height,
251
251
  and the literal `cover` keyword. Registered in `md.plugins.php` alongside
252
- `codepen`/`youtube`/`checklist`/`callout` — available out of the box, drop
252
+ `codepen`/`youtube`/`callout` — available out of the box, drop
253
253
  it with `MD::unregisterPlugin('img-asset')` if you don't want it.
254
254
 
255
255
  ---
@@ -2009,18 +2009,6 @@ Embeds a CodePen result via `<iframe>`. `user` defaults to `anonymous`, `height`
2009
2009
  {% codepen abcXYZ jsmith 500 %}
2010
2010
  ```
2011
2011
 
2012
- #### `{% checklist ["Title"] items %}`
2013
-
2014
- Renders a block-syntax list of checkbox items, one per line, with an optional title.
2015
-
2016
- ```
2017
- {% checklist "Today"
2018
- Do the dishes
2019
- Walk the dog
2020
- Read a book
2021
- %}
2022
- ```
2023
-
2024
2012
  ---
2025
2013
 
2026
2014
  ## Extending the `<markdown>` tag
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kirigami/php-prepros",
3
- "version": "3.2.1",
3
+ "version": "3.2.2",
4
4
  "description": "PHP preprocessor for the Kirigami static site generator. Compile PHP page templates to clean, deployable HTML — with zero server dependency.",
5
5
  "keywords": [
6
6
  "kirigami",
@@ -746,8 +746,9 @@ final class LD
746
746
  if ($url === null || $url === '') return null;
747
747
  if (preg_match('#^(https?:)?//#', $url) || str_starts_with($url, 'data:')) return $url;
748
748
 
749
- $origin = preg_replace('#^(https?://[^/]+).*#', '$1', (string) (self::data()->baseurl ?? ''));
750
- return $origin . '/' . ltrim($url, '/');
749
+ // Against the whole baseurl, path included: a site on `user.github.io/repo` keeps `/repo`.
750
+ $base = rtrim((string) (self::data()->baseurl ?? ''), '/');
751
+ return $base . '/' . ltrim($url, '/');
751
752
  }
752
753
 
753
754
  /** @return array<int,string> */
@@ -1,57 +1,57 @@
1
- <?php
2
-
3
- /**
4
- * MD — Markdown renderer backed by the native `mdhtml` extension (CommonMark
5
- * + GFM via cmark-gfm 0.29.0.gfm.13, statically built into @kirigami/php-wasm
6
- * — see php-kirigami/php-mdhtml).
7
- *
8
- * Switched from the hand-written recursive-descent/regex renderer to this
9
- * native extension — see docs/DECISIONS.md and docs/STATUS.md in the repo
10
- * root for the full reasoning. Every MD::toHtml() extension (plugins, emoji,
11
- * GFM alerts, definition lists, heading anchors, ==highlight==/^sup^/~sub~)
12
- * is reimplemented in C, not just the CommonMark/GFM core — see php-mdhtml's
13
- * README for exactly what's covered.
14
- *
15
- * Kept the old implementation as MD_LEGACY (md-legacy.class.php) — untouched,
16
- * still autoloadable — as a rollback path.
17
- *
18
- * Usage:
19
- * $html = MD::toHtml($markdown);
20
- * MD::registerPlugin('name', function (array $args, string $body): string { ... });
21
- * MD::registerEmoji('kirigami', '📐');
22
- */
23
- class MD
24
- {
25
- public static function registerPlugin(string $name, callable $callback): void
26
- {
27
- \MDHtml\RegisterPlugin($name, $callback);
28
- }
29
-
30
- public static function unregisterPlugin(string $name): void
31
- {
32
- \MDHtml\UnregisterPlugin($name);
33
- }
34
-
35
- public static function getRegisteredPlugins(): array
36
- {
37
- return \MDHtml\GetRegisteredPlugins();
38
- }
39
-
40
- public static function registerEmoji(string $shortcode, string $char): void
41
- {
42
- \MDHtml\RegisterEmoji($shortcode, $char);
43
- }
44
-
45
- public static function toHtml(string $markdown): string
46
- {
47
- return \MDHtml\Render($markdown);
48
- }
49
- }
50
-
51
-
52
- // Default Markdown plugins ({% codepen %}, {% checklist %}, {% callout %},
53
- // {% img-asset %}) — "registered out of the box" per the README. Loaded here
54
- // so every entrypoint (prepros.php, runenv.php, imagebatch.php) gets them
55
- // without an explicit include. A project can still MD::unregisterPlugin() any
56
- // of them, or MD::registerPlugin() its own with the same name to override.
57
- include_once(__DIR__ . '/md.plugins.php');
1
+ <?php
2
+
3
+ /**
4
+ * MD — Markdown renderer backed by the native `mdhtml` extension (CommonMark
5
+ * + GFM via cmark-gfm 0.29.0.gfm.13, statically built into @kirigami/php-wasm
6
+ * — see php-kirigami/php-mdhtml).
7
+ *
8
+ * Switched from the hand-written recursive-descent/regex renderer to this
9
+ * native extension — see docs/DECISIONS.md and docs/STATUS.md in the repo
10
+ * root for the full reasoning. Every MD::toHtml() extension (plugins, emoji,
11
+ * GFM alerts, definition lists, heading anchors, ==highlight==/^sup^/~sub~)
12
+ * is reimplemented in C, not just the CommonMark/GFM core — see php-mdhtml's
13
+ * README for exactly what's covered.
14
+ *
15
+ * Kept the old implementation as MD_LEGACY (md-legacy.class.php) — untouched,
16
+ * still autoloadable — as a rollback path.
17
+ *
18
+ * Usage:
19
+ * $html = MD::toHtml($markdown);
20
+ * MD::registerPlugin('name', function (array $args, string $body): string { ... });
21
+ * MD::registerEmoji('kirigami', '📐');
22
+ */
23
+ class MD
24
+ {
25
+ public static function registerPlugin(string $name, callable $callback): void
26
+ {
27
+ \MDHtml\RegisterPlugin($name, $callback);
28
+ }
29
+
30
+ public static function unregisterPlugin(string $name): void
31
+ {
32
+ \MDHtml\UnregisterPlugin($name);
33
+ }
34
+
35
+ public static function getRegisteredPlugins(): array
36
+ {
37
+ return \MDHtml\GetRegisteredPlugins();
38
+ }
39
+
40
+ public static function registerEmoji(string $shortcode, string $char): void
41
+ {
42
+ \MDHtml\RegisterEmoji($shortcode, $char);
43
+ }
44
+
45
+ public static function toHtml(string $markdown): string
46
+ {
47
+ return \MDHtml\Render($markdown);
48
+ }
49
+ }
50
+
51
+
52
+ // Default Markdown plugins ({% codepen %}, {% callout %},
53
+ // {% img-asset %}) — "registered out of the box" per the README. Loaded here
54
+ // so every entrypoint (prepros.php, runenv.php, imagebatch.php) gets them
55
+ // without an explicit include. A project can still MD::unregisterPlugin() any
56
+ // of them, or MD::registerPlugin() its own with the same name to override.
57
+ include_once(__DIR__ . '/md.plugins.php');
@@ -1,103 +1,80 @@
1
- <?php
2
-
3
-
4
-
5
- // =============================================================================
6
- // DEFAULT MARKDOWN PLUGINS
7
- //
8
- // Auto-loaded from md.class.php, so these are available in every render without
9
- // any setup ("registered out of the box"). To drop one, call
10
- // MD::unregisterPlugin('name') (or md_unregister_plugin('name')) from an
11
- // `includes` file; to change one, MD::registerPlugin() your own under the same
12
- // name.
13
- // =============================================================================
14
-
15
- // --- CodePen (inline) ---
16
- // {% codepen PENid %}
17
- // {% codepen PENid user height %}
18
- MD::registerPlugin('codepen', function (array $args, string $body): string {
19
- $id = htmlspecialchars($args[0] ?? '', ENT_QUOTES, 'UTF-8');
20
- $user = htmlspecialchars($args[1] ?? 'anonymous', ENT_QUOTES, 'UTF-8');
21
- $height = intval($args[2] ?? 400);
22
- if ($id === '') return '<!-- codepen: missing id -->';
23
- return "<iframe height=\"{$height}\" style=\"width:100%\" "
24
- . "scrolling=\"no\" "
25
- . "src=\"https://codepen.io/{$user}/embed/{$id}?default-tab=result\" "
26
- . "frameborder=\"no\" loading=\"lazy\" allowfullscreen>"
27
- . "</iframe>";
28
- });
29
-
30
- // --- Checklist (multi-line block) ---
31
- // {% checklist
32
- // Do the dishes
33
- // Walk the dog
34
- // Read a book
35
- // %}
36
- //
37
- // Optionally with a title:
38
- // {% checklist "My tasks for today"
39
- // Task 1
40
- // Task 2
41
- // %}
42
- MD::registerPlugin('checklist', function (array $args, string $body): string {
43
- $title = !empty($args[0]) ? '<p class="checklist-title"><strong>' . htmlspecialchars($args[0], ENT_QUOTES, 'UTF-8') . '</strong></p>' : '';
44
- $items = array_filter(array_map('trim', explode("\n", $body)));
45
- if (empty($items)) return '<!-- checklist: no items -->';
46
- $html = "<div class=\"checklist\">{$title}<ul>\n";
47
- foreach ($items as $item) {
48
- $html .= ' <li><label><input type="checkbox" /> ' . htmlspecialchars($item, ENT_QUOTES, 'UTF-8') . '</label></li>' . "\n";
49
- }
50
- return $html . "</ul></div>";
51
- });
52
-
53
- // --- Custom callout (inline or block) ---
54
- // Inline: {% callout warning "Warning!" Content on one line %}
55
- // Block : {% callout danger "Title"
56
- // Paragraph 1
57
- //
58
- // Paragraph 2
59
- // %}
60
- MD::registerPlugin('callout', function (array $args, string $body): string {
61
- $validTypes = ['info', 'success', 'warning', 'danger'];
62
- $type = in_array($args[0] ?? '', $validTypes, true) ? $args[0] : 'info';
63
- $title = !empty($args[1]) ? htmlspecialchars($args[1], ENT_QUOTES, 'UTF-8') : '';
64
-
65
- // Content: $body for a block tag, otherwise the remaining args on the opening line
66
- if ($body !== '') {
67
- $content = htmlspecialchars($body, ENT_QUOTES, 'UTF-8');
68
- // Replace double line breaks with <br><br> to preserve paragraphs
69
- $content = str_replace("\n\n", '<br><br>', $content);
70
- $content = str_replace("\n", '<br>', $content);
71
- } else {
72
- $content = htmlspecialchars(implode(' ', array_slice($args, $title ? 2 : 1)), ENT_QUOTES, 'UTF-8');
73
- }
74
-
75
- $titleHtml = $title ? "<strong>{$title}</strong><br>" : '';
76
- return "<div class=\"callout callout-{$type}\">{$titleHtml}{$content}</div>";
77
- });
78
-
79
- // --- Image asset (inline) — same pipeline as <img asset>, via IMG::asset() ---
80
- // {% img-asset photo.jpg %}
81
- // {% img-asset photo.jpg 800 %}
82
- // {% img-asset photo.jpg 800 600 %}
83
- // {% img-asset photo.jpg 800 600 cover %}
84
- MD::registerPlugin('img-asset', function (array $args, string $body): string {
85
- $path = trim($args[0] ?? '');
86
- if ($path === '') return '<!-- img-asset: missing path -->';
87
-
88
- $width = isset($args[1]) ? (int) $args[1] : 0;
89
- $height = isset($args[2]) ? (int) $args[2] : 0;
90
- $cover = isset($args[3]) && strtolower($args[3]) === 'cover';
91
-
92
- // A resolvable path is required to actually call IMG::asset() — this
93
- // also protects a literal `{% img-asset path … %}` written as Markdown
94
- // *documentation* (inside a code span): the plugin still runs even when
95
- // its output ends up discarded for the literal text, so a placeholder
96
- // path must not throw.
97
- try {
98
- $src = IMG::asset($path, $width, $height, $cover, PREPROS::$file);
99
- } catch (Throwable $e) {
100
- return '<!-- img-asset: ' . htmlspecialchars($e->getMessage(), ENT_QUOTES, 'UTF-8') . ' -->';
101
- }
102
- return '<img src="' . htmlspecialchars($src, ENT_QUOTES, 'UTF-8') . '" alt="">';
1
+ <?php
2
+
3
+
4
+
5
+ // =============================================================================
6
+ // DEFAULT MARKDOWN PLUGINS
7
+ //
8
+ // Auto-loaded from md.class.php, so these are available in every render without
9
+ // any setup ("registered out of the box"). To drop one, call
10
+ // MD::unregisterPlugin('name') (or md_unregister_plugin('name')) from an
11
+ // `includes` file; to change one, MD::registerPlugin() your own under the same
12
+ // name.
13
+ // =============================================================================
14
+
15
+ // --- CodePen (inline) ---
16
+ // {% codepen PENid %}
17
+ // {% codepen PENid user height %}
18
+ MD::registerPlugin('codepen', function (array $args, string $body): string {
19
+ $id = htmlspecialchars($args[0] ?? '', ENT_QUOTES, 'UTF-8');
20
+ $user = htmlspecialchars($args[1] ?? 'anonymous', ENT_QUOTES, 'UTF-8');
21
+ $height = intval($args[2] ?? 400);
22
+ if ($id === '') return '<!-- codepen: missing id -->';
23
+ return "<iframe height=\"{$height}\" style=\"width:100%\" "
24
+ . "scrolling=\"no\" "
25
+ . "src=\"https://codepen.io/{$user}/embed/{$id}?default-tab=result\" "
26
+ . "frameborder=\"no\" loading=\"lazy\" allowfullscreen>"
27
+ . "</iframe>";
28
+ });
29
+
30
+ // --- Custom callout (inline or block) ---
31
+ // Inline: {% callout warning "Warning!" Content on one line %}
32
+ // Block : {% callout danger "Title"
33
+ // Paragraph 1
34
+ //
35
+ // Paragraph 2
36
+ // %}
37
+ MD::registerPlugin('callout', function (array $args, string $body): string {
38
+ $validTypes = ['info', 'success', 'warning', 'danger'];
39
+ $type = in_array($args[0] ?? '', $validTypes, true) ? $args[0] : 'info';
40
+ $title = !empty($args[1]) ? htmlspecialchars($args[1], ENT_QUOTES, 'UTF-8') : '';
41
+
42
+ // Content: $body for a block tag, otherwise the remaining args on the opening line
43
+ if ($body !== '') {
44
+ $content = htmlspecialchars($body, ENT_QUOTES, 'UTF-8');
45
+ // Replace double line breaks with <br><br> to preserve paragraphs
46
+ $content = str_replace("\n\n", '<br><br>', $content);
47
+ $content = str_replace("\n", '<br>', $content);
48
+ } else {
49
+ $content = htmlspecialchars(implode(' ', array_slice($args, $title ? 2 : 1)), ENT_QUOTES, 'UTF-8');
50
+ }
51
+
52
+ $titleHtml = $title ? "<strong>{$title}</strong><br>" : '';
53
+ return "<div class=\"callout callout-{$type}\">{$titleHtml}{$content}</div>";
54
+ });
55
+
56
+ // --- Image asset (inline) — same pipeline as <img asset>, via IMG::asset() ---
57
+ // {% img-asset photo.jpg %}
58
+ // {% img-asset photo.jpg 800 %}
59
+ // {% img-asset photo.jpg 800 600 %}
60
+ // {% img-asset photo.jpg 800 600 cover %}
61
+ MD::registerPlugin('img-asset', function (array $args, string $body): string {
62
+ $path = trim($args[0] ?? '');
63
+ if ($path === '') return '<!-- img-asset: missing path -->';
64
+
65
+ $width = isset($args[1]) ? (int) $args[1] : 0;
66
+ $height = isset($args[2]) ? (int) $args[2] : 0;
67
+ $cover = isset($args[3]) && strtolower($args[3]) === 'cover';
68
+
69
+ // A resolvable path is required to actually call IMG::asset() — this
70
+ // also protects a literal `{% img-asset path … %}` written as Markdown
71
+ // *documentation* (inside a code span): the plugin still runs even when
72
+ // its output ends up discarded for the literal text, so a placeholder
73
+ // path must not throw.
74
+ try {
75
+ $src = IMG::asset($path, $width, $height, $cover, PREPROS::$file);
76
+ } catch (Throwable $e) {
77
+ return '<!-- img-asset: ' . htmlspecialchars($e->getMessage(), ENT_QUOTES, 'UTF-8') . ' -->';
78
+ }
79
+ return '<img src="' . htmlspecialchars($src, ENT_QUOTES, 'UTF-8') . '" alt="">';
103
80
  });
@@ -458,8 +458,9 @@ final class META
458
458
  $url = trim($url);
459
459
  if (preg_match('#^(https?:)?//#', $url) || str_starts_with($url, 'data:')) return $url;
460
460
 
461
- $origin = preg_replace('#^(https?://[^/]+).*#', '$1', (string) (self::data()->baseurl ?? ''));
462
- return $origin === '' ? $url : $origin . '/' . ltrim($url, '/');
461
+ // Against the whole baseurl, path included: a site on `user.github.io/repo` keeps `/repo`.
462
+ $base = rtrim((string) (self::data()->baseurl ?? ''), '/');
463
+ return $base === '' ? $url : $base . '/' . ltrim($url, '/');
463
464
  }
464
465
 
465
466
  /** `@user` / `user` / `https://twitter.com/user` → `@user`. */