[TikiWiki-commits] [Git][tikiwiki/tiki][master] [FIX] Markdown: Implement consistent styling for code blocks

"Victor Emanouilov \(@kroky\) via TikiWiki-cvs" <[email protected]> Mon, 29 Jun 2026 13:18:43 +0000
Newsgroups gmane.comp.cms.tiki.cvs
Message-ID <6a4270b3e1dd9_383f4b46c6725@gitlab-sidekiq-low-urgency-cpu-bound-v2-656d6f88d7-mqh5z.mail>

Victor Emanouilov pushed to branch master at Tiki Wiki CMS Groupware / Tiki


Commits:
98400570 by Moïse Nturubika at 2026-06-29T13:04:50+00:00
[FIX] Markdown: Implement consistent styling for code blocks
---
* [FIX] Markdown: Implement consistent styling for code blocks

See merge request tikiwiki/tiki!10137

- - - - -


4 changed files:

- lib/core/WikiParser/Markdown/Extension.php
- + lib/core/WikiParser/Markdown/Renderer/FencedCodeRenderer.php
- lib/core/WikiParser/ParsableMarkdown.php
- lib/test/TikiLib/MarkdownParserTest.php


Changes:

=====================================
lib/core/WikiParser/Markdown/Extension.php
=====================================
@@ -9,6 +9,7 @@ namespace Tiki\WikiParser\Markdown;
 use League\CommonMark\Environment\EnvironmentBuilderInterface;
 use League\CommonMark\Event\DocumentParsedEvent;
 use League\CommonMark\Extension\ExtensionInterface;
+use League\CommonMark\Extension\CommonMark\Node\Block\FencedCode;
 use League\CommonMark\Extension\TaskList\TaskListItemMarker;
 use League\CommonMark\Extension\TaskList\TaskListItemMarkerParser;
 use Tiki\WikiParser\Markdown\Renderer\TaskListItemMarkerRenderer;
@@ -22,6 +23,7 @@ class Extension implements ExtensionInterface
             ->addRenderer(Node\CollapsibleHeading::class, new Renderer\CollapsibleHeadingRenderer(), 0)
             ->addRenderer(Node\CollapsibleLink::class, new Renderer\CollapsibleLinkRenderer(), 0)
             ->addRenderer(Node\CollapsibleContainer::class, new Renderer\CollapsibleContainerRenderer(), 0)
+            ->addRenderer(FencedCode::class, new Renderer\FencedCodeRenderer(), 10)
         ;
         $environment->addEventListener(DocumentParsedEvent::class, new CollapsibleHeadingProcessor(), -100);
 


=====================================
lib/core/WikiParser/Markdown/Renderer/FencedCodeRenderer.php
=====================================
@@ -0,0 +1,79 @@
+<?php
+
+// (c) Copyright by authors of the Tiki Wiki CMS Groupware Project
+//
+// All Rights Reserved. See copyright.txt for details and a complete list of authors.
+// Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See license.txt for details.
+namespace Tiki\WikiParser\Markdown\Renderer;
+
+use League\CommonMark\Extension\CommonMark\Node\Block\FencedCode;
+use League\CommonMark\Node\Node;
+use League\CommonMark\Renderer\ChildNodeRendererInterface;
+use League\CommonMark\Renderer\NodeRendererInterface;
+use League\CommonMark\Util\HtmlElement;
+
+/**
+ * Custom renderer for Markdown fenced code blocks (```) that produces
+ * the same HTML structure as the native Tiki {CODE()} wiki plugin.
+ *
+ * This ensures consistent styling, copy-to-clipboard functionality,
+ * and optional CodeMirror integration for Markdown-authored content.
+ */
+class FencedCodeRenderer implements NodeRendererInterface
+{
+    /**
+     * @var int Static counter for generating unique IDs across all code blocks on a page.
+     */
+    private static int $codeCount = 0;
+
+    public static function resetCount(): void
+    {
+        self::$codeCount = 0;
+    }
+
+    public function render(Node $node, ChildNodeRendererInterface $childRenderer): \Stringable|string
+    {
+        FencedCode::assertInstanceOf($node);
+
+        $code = $node->getLiteral();
+
+        // Extract the language hint (e.g., "javascript" from ```javascript)
+        $infoWords = $node->getInfoWords();
+        $language = ! empty($infoWords) ? strtolower($infoWords[0]) : null;
+
+        // Generate a unique ID for clipboard targeting
+        $id = 'md-codebox' . ++self::$codeCount;
+
+        // HTML-escape the code content
+        $escapedCode = htmlspecialchars($code, ENT_QUOTES, 'UTF-8');
+
+        // Build the inner <div class="code"> wrapper (same as {CODE()})
+        $codeDiv = new HtmlElement('div', ['class' => 'code'], $escapedCode);
+
+        // Build the <pre> element with the same attributes as {CODE()}
+        $preAttrs = [
+            'class' => 'codelisting',
+            'id'    => $id,
+            'dir'   => 'ltr',
+            'style' => 'white-space:pre-wrap; overflow-wrap: break-word; word-wrap: break-word;',
+        ];
+        if ($language) {
+            $preAttrs['data-syntax'] = $language;
+        }
+
+        $pre = new HtmlElement('pre', $preAttrs, $codeDiv);
+
+        // Build the copy-to-clipboard icon
+        $copyTooltip = new HtmlElement('span', ['class' => 'copy_code_tooltiptext'], 'Copy to clipboard');
+        $copyIcon = new HtmlElement('div', [
+            'class'                 => 'icon_copy_code far fa-clipboard',
+            'tabindex'              => '0',
+            'data-clipboard-target' => '#' . $id,
+        ], $copyTooltip);
+
+        // Wrap everything in the codelisting_container (same as {CODE()})
+        $container = new HtmlElement('div', ['class' => 'codelisting_container'], $copyIcon . $pre);
+
+        return $container;
+    }
+}


=====================================
lib/core/WikiParser/ParsableMarkdown.php
=====================================
@@ -18,8 +18,6 @@ use League\CommonMark\MarkdownConverter;
 use League\CommonMark\Node\Node;
 use League\CommonMark\Renderer\ChildNodeRendererInterface;
 use League\CommonMark\Renderer\NodeRendererInterface;
-use League\CommonMark\Extension\CommonMark\Node\Block\FencedCode;
-use League\CommonMark\Extension\CommonMark\Renderer\Block\FencedCodeRenderer;
 use Tiki\WikiParser\Markdown\Extension as TikiExtension;
 
 class WikiParser_ParsableMarkdown extends ParserLib
@@ -60,21 +58,6 @@ class WikiParser_ParsableMarkdown extends ParserLib
             $environment->addExtension(new GithubFlavoredMarkdownExtension());
         }
 
-        // add default class to code blocks -> <pre class="codelisting">
-        $environment->addRenderer(
-            FencedCode::class,
-            new class implements NodeRendererInterface {
-                public function render(Node $node, ChildNodeRendererInterface $childRenderer)
-                {
-                    $htmlEl = (new FencedCodeRenderer())->render($node, $childRenderer);
-                    $class = $htmlEl->getAttribute('class') ?: 'codelisting';
-                    $htmlEl->setAttribute('class', $class);
-                    return $htmlEl;
-                }
-            },
-            10
-        );
-
         // add default class to table -> <table class="wikitable table table-striped table-hover">
         $environment->addRenderer(
             Table::class,


=====================================
lib/test/TikiLib/MarkdownParserTest.php
=====================================
@@ -26,6 +26,8 @@ class TikiLib_MarkdownParserTest extends TikiTestCase
 
         $this->oldprefs = $prefs;
         $this->olduser = $user;
+
+        \Tiki\WikiParser\Markdown\Renderer\FencedCodeRenderer::resetCount();
     }
 
 
@@ -253,8 +255,8 @@ line 1 of code
 line 2 of code
 line 3 of code
 </code></pre>
-<pre class="codelisting"><code>Sample text here...
-</code></pre>
+<div class="codelisting_container"><div class="icon_copy_code far fa-clipboard" tabindex="0" data-clipboard-target="#md-codebox1"><span class="copy_code_tooltiptext">Copy to clipboard</span></div><pre class="codelisting" id="md-codebox1" dir="ltr" style="white-space:pre-wrap; overflow-wrap: break-word; word-wrap: break-word;"><div class="code">Sample text here...
+</div></pre></div>
 <table class="wikitable table table-striped table-hover">
 <thead>
 <tr>



View it on GitLab: https://gitlab.com/tikiwiki/tiki/-/commit/9840057029edf2d4bec670c0a2bbe55a71980e5c

-- 
View it on GitLab: https://gitlab.com/tikiwiki/tiki/-/commit/9840057029edf2d4bec670c0a2bbe55a71980e5c
You're receiving this email because of your account on gitlab.com. Manage all notifications: https://gitlab.com/-/profile/notifications | Help: https://gitlab.com/help

_______________________________________________
TikiWiki-cvs mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/tikiwiki-cvs