diff --git a/site/doc/pages/markdown_to_html.txt b/site/doc/pages/markdown_to_html.txt index d60297e..b5c8ef4 100644 --- a/site/doc/pages/markdown_to_html.txt +++ b/site/doc/pages/markdown_to_html.txt @@ -45,6 +45,7 @@ Options: - `options["gfm"]` defaults to `true` and turns on GitHub-style extras such as tables, task lists, autolinks, and strikethrough. - `options["allow_html"]` defaults to `false`. When true, raw HTML blocks and inline tags may pass through as `raw_html` nodes instead of being escaped as plain text. +- `options["heading_offset"]` shifts rendered heading levels while leaving AST `level` values unchanged. The default is `0`; final levels clamp to `h1` through `h6`. Use `1` when Markdown is nested below a component-owned page title. - `options["components"]` declares renderer extension points using normal UCE components. Directive hook example: diff --git a/site/tests/cli_runner.uce b/site/tests/cli_runner.uce index 67ac1ef..b2321b5 100644 --- a/site/tests/cli_runner.uce +++ b/site/tests/cli_runner.uce @@ -217,7 +217,9 @@ void cli_run_doc_pages_gate() CliHttpResponse res; String summary; bool ok = false; - for(u64 attempt = 0; attempt < 3; attempt++) + // Frontend GETs may serve the last complete nested component while its + // prioritized replacement compiles; cover a normal cold WASI compile. + for(u64 attempt = 0; attempt < 12; attempt++) { if(attempt > 0) usleep(500000); diff --git a/site/tests/components/markdown/heading.uce b/site/tests/components/markdown/heading.uce new file mode 100644 index 0000000..c5769f7 --- /dev/null +++ b/site/tests/components/markdown/heading.uce @@ -0,0 +1,5 @@ +COMPONENT(Request& context) +{ + DValue& node = context.props["node"]; + <>
">
+} diff --git a/site/tests/markdown.uce b/site/tests/markdown.uce index fedd9cc..d4a7b13 100644 --- a/site/tests/markdown.uce +++ b/site/tests/markdown.uce @@ -23,12 +23,21 @@ RENDER(Request& context) DValue ast = markdown_to_ast(markdown_src, options); String ast_json = json_encode(ast); String html = markdown_to_html(markdown_src, options); + DValue heading_options; + heading_options["heading_offset"] = "1"; + heading_options["components"]["node.heading"] = "components/markdown/heading"; + String shifted_headings = markdown_to_html("# Page child\n\n###### Deep child", heading_options); + DValue negative_heading_options; + negative_heading_options["heading_offset"] = "-2"; + String clamped_headings = markdown_to_html("# Top\n\n### Third", negative_heading_options); String ast_excerpt = ast_json.substr(0, ast_json.length() > 220 ? 220 : ast_json.length()); site_tests_page_start("Markdown", "Markdown parser coverage with component-backed directive and node hooks."); check("markdown_to_ast()", ast_json.find("heading") != String::npos || ast_json.find("code_block") != String::npos, ast_excerpt); check("markdown_to_html()", html.find("Page child") != String::npos && shifted_headings.find("
Deep child
") != String::npos && shifted_headings.find("

") == String::npos, shifted_headings); + check("negative heading offset clamps at h1", clamped_headings.find("

Top

") != String::npos && clamped_headings.find("

Third

") != String::npos, clamped_headings); check("component hook output", html.find("tests-code-block") != String::npos, html); ?>
@@ -38,4 +47,4 @@ RENDER(Request& context) site_tests_summary(passed, failed, skipped, "The warning directive and code-block renderer both run through local component fixtures under site/tests/components/markdown."); site_tests_page_end(); -} \ No newline at end of file +} diff --git a/src/lib/markdown.cpp b/src/lib/markdown.cpp index 30830d1..6bf925d 100644 --- a/src/lib/markdown.cpp +++ b/src/lib/markdown.cpp @@ -1218,6 +1218,10 @@ String markdown_render_node(DValue node, DValue& options) else if(type == "heading") { s64 level = int_val(markdown_get_string(node, "level", "1")); + s64 heading_offset = int_val(markdown_get_string(options, "heading_offset", "0")); + if(heading_offset < -5) heading_offset = -5; + if(heading_offset > 5) heading_offset = 5; + level += heading_offset; if(level < 1) level = 1; if(level > 6) level = 6; html = String("" + children_html + String("";