FossilRepo

Add Pikchr diagram rendering support - <verbatim type="pikchr">...</verbatim> in Fossil wiki → rendered SVG - ```pikchr fenced code blocks in markdown → rendered SVG - Uses `fossil pikchr` CLI for server-side rendering - Falls back to <pre><code> display if fossil binary unavailable - Wraps SVG in <div class="pikchr-diagram"> for styling

lmata 2026-04-07 00:04 trunk
Commit fefca89c2be51c7f5f4d83e8240c24aa8108b93e9089472bc67d6f84b1cba7cd
--- fossil/cli.py
+++ fossil/cli.py
@@ -32,10 +32,26 @@
3232
try:
3333
self._run("version")
3434
return True
3535
except (FileNotFoundError, subprocess.CalledProcessError):
3636
return False
37
+
38
+ def render_pikchr(self, source: str) -> str:
39
+ """Render Pikchr markup to SVG. Returns SVG string or empty on failure."""
40
+ try:
41
+ result = subprocess.run(
42
+ [self.binary, "pikchr", "-"],
43
+ input=source,
44
+ capture_output=True,
45
+ text=True,
46
+ timeout=10,
47
+ )
48
+ if result.returncode == 0:
49
+ return result.stdout
50
+ except (FileNotFoundError, subprocess.TimeoutExpired):
51
+ pass
52
+ return ""
3753
3854
def wiki_commit(self, repo_path: Path, page_name: str, content: str, user: str = "") -> bool:
3955
"""Create or update a wiki page. Pipes content to fossil wiki commit."""
4056
cmd = [self.binary, "wiki", "commit", page_name, "-R", str(repo_path)]
4157
if user:
4258
--- fossil/cli.py
+++ fossil/cli.py
@@ -32,10 +32,26 @@
32 try:
33 self._run("version")
34 return True
35 except (FileNotFoundError, subprocess.CalledProcessError):
36 return False
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
38 def wiki_commit(self, repo_path: Path, page_name: str, content: str, user: str = "") -> bool:
39 """Create or update a wiki page. Pipes content to fossil wiki commit."""
40 cmd = [self.binary, "wiki", "commit", page_name, "-R", str(repo_path)]
41 if user:
42
--- fossil/cli.py
+++ fossil/cli.py
@@ -32,10 +32,26 @@
32 try:
33 self._run("version")
34 return True
35 except (FileNotFoundError, subprocess.CalledProcessError):
36 return False
37
38 def render_pikchr(self, source: str) -> str:
39 """Render Pikchr markup to SVG. Returns SVG string or empty on failure."""
40 try:
41 result = subprocess.run(
42 [self.binary, "pikchr", "-"],
43 input=source,
44 capture_output=True,
45 text=True,
46 timeout=10,
47 )
48 if result.returncode == 0:
49 return result.stdout
50 except (FileNotFoundError, subprocess.TimeoutExpired):
51 pass
52 return ""
53
54 def wiki_commit(self, repo_path: Path, page_name: str, content: str, user: str = "") -> bool:
55 """Create or update a wiki page. Pipes content to fossil wiki commit."""
56 cmd = [self.binary, "wiki", "commit", page_name, "-R", str(repo_path)]
57 if user:
58
--- fossil/views.py
+++ fossil/views.py
@@ -41,10 +41,25 @@
4141
return f"[{text}]({path})"
4242
4343
content = re.sub(r"\[([^\]\|]+?)\s*\|\s*([^\]]+?)\]", _fossil_to_md_link, content)
4444
content = re.sub(r"<verbatim>(.*?)</verbatim>", r"```\n\1\n```", content, flags=re.DOTALL)
4545
html = md.markdown(content, extensions=["fenced_code", "tables", "toc"])
46
+
47
+ # Post-process: render pikchr fenced code blocks to SVG
48
+ def _render_pikchr_md(m):
49
+ try:
50
+ from fossil.cli import FossilCLI
51
+
52
+ cli = FossilCLI()
53
+ svg = cli.render_pikchr(m.group(1))
54
+ if svg:
55
+ return f'<div class="pikchr-diagram">{svg}</div>'
56
+ except Exception:
57
+ pass
58
+ return m.group(0)
59
+
60
+ html = re.sub(r'<code class="language-pikchr">(.*?)</code>', _render_pikchr_md, html, flags=re.DOTALL)
4661
return _rewrite_fossil_links(html, project_slug) if project_slug else html
4762
4863
# Fossil wiki / HTML: convert Fossil-specific syntax to HTML
4964
# Fossil links: [path | text] or [path|text] — spaces around pipe are optional
5065
def _fossil_link_replace(match):
@@ -63,11 +78,27 @@
6378
content = re.sub(r"\[wikipedia:([^\]]+)\]", r'<a href="https://en.wikipedia.org/wiki/\1">\1</a>', content)
6479
# Anchor links: [#anchor-name] -> local anchor
6580
content = re.sub(r"\[#([^\]]+)\]", r'<a href="#\1">\1</a>', content)
6681
# Bare wiki links: [PageName] (no pipe, not a URL)
6782
content = re.sub(r"\[([A-Z][a-zA-Z0-9_]+)\]", r'<a href="\1">\1</a>', content)
83
+
6884
# Verbatim blocks
85
+ # Pikchr diagrams: <verbatim type="pikchr">...</verbatim> → SVG
86
+ def _render_pikchr_block(m):
87
+ try:
88
+ from fossil.cli import FossilCLI
89
+
90
+ cli = FossilCLI()
91
+ svg = cli.render_pikchr(m.group(1))
92
+ if svg:
93
+ return f'<div class="pikchr-diagram">{svg}</div>'
94
+ except Exception:
95
+ pass
96
+ return f'<pre><code class="language-pikchr">{m.group(1)}</code></pre>'
97
+
98
+ content = re.sub(r'<verbatim\s+type="pikchr">(.*?)</verbatim>', _render_pikchr_block, content, flags=re.DOTALL)
99
+ # Regular verbatim blocks
69100
content = re.sub(r"<verbatim>(.*?)</verbatim>", r"<pre><code>\1</code></pre>", content, flags=re.DOTALL)
70101
# <nowiki> blocks — strip the tags, content passes through as-is
71102
content = re.sub(r"<nowiki>(.*?)</nowiki>", r"\1", content, flags=re.DOTALL)
72103
73104
# Convert Fossil wiki list syntax: * bullets and # enumeration
74105
--- fossil/views.py
+++ fossil/views.py
@@ -41,10 +41,25 @@
41 return f"[{text}]({path})"
42
43 content = re.sub(r"\[([^\]\|]+?)\s*\|\s*([^\]]+?)\]", _fossil_to_md_link, content)
44 content = re.sub(r"<verbatim>(.*?)</verbatim>", r"```\n\1\n```", content, flags=re.DOTALL)
45 html = md.markdown(content, extensions=["fenced_code", "tables", "toc"])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46 return _rewrite_fossil_links(html, project_slug) if project_slug else html
47
48 # Fossil wiki / HTML: convert Fossil-specific syntax to HTML
49 # Fossil links: [path | text] or [path|text] — spaces around pipe are optional
50 def _fossil_link_replace(match):
@@ -63,11 +78,27 @@
63 content = re.sub(r"\[wikipedia:([^\]]+)\]", r'<a href="https://en.wikipedia.org/wiki/\1">\1</a>', content)
64 # Anchor links: [#anchor-name] -> local anchor
65 content = re.sub(r"\[#([^\]]+)\]", r'<a href="#\1">\1</a>', content)
66 # Bare wiki links: [PageName] (no pipe, not a URL)
67 content = re.sub(r"\[([A-Z][a-zA-Z0-9_]+)\]", r'<a href="\1">\1</a>', content)
 
68 # Verbatim blocks
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
69 content = re.sub(r"<verbatim>(.*?)</verbatim>", r"<pre><code>\1</code></pre>", content, flags=re.DOTALL)
70 # <nowiki> blocks — strip the tags, content passes through as-is
71 content = re.sub(r"<nowiki>(.*?)</nowiki>", r"\1", content, flags=re.DOTALL)
72
73 # Convert Fossil wiki list syntax: * bullets and # enumeration
74
--- fossil/views.py
+++ fossil/views.py
@@ -41,10 +41,25 @@
41 return f"[{text}]({path})"
42
43 content = re.sub(r"\[([^\]\|]+?)\s*\|\s*([^\]]+?)\]", _fossil_to_md_link, content)
44 content = re.sub(r"<verbatim>(.*?)</verbatim>", r"```\n\1\n```", content, flags=re.DOTALL)
45 html = md.markdown(content, extensions=["fenced_code", "tables", "toc"])
46
47 # Post-process: render pikchr fenced code blocks to SVG
48 def _render_pikchr_md(m):
49 try:
50 from fossil.cli import FossilCLI
51
52 cli = FossilCLI()
53 svg = cli.render_pikchr(m.group(1))
54 if svg:
55 return f'<div class="pikchr-diagram">{svg}</div>'
56 except Exception:
57 pass
58 return m.group(0)
59
60 html = re.sub(r'<code class="language-pikchr">(.*?)</code>', _render_pikchr_md, html, flags=re.DOTALL)
61 return _rewrite_fossil_links(html, project_slug) if project_slug else html
62
63 # Fossil wiki / HTML: convert Fossil-specific syntax to HTML
64 # Fossil links: [path | text] or [path|text] — spaces around pipe are optional
65 def _fossil_link_replace(match):
@@ -63,11 +78,27 @@
78 content = re.sub(r"\[wikipedia:([^\]]+)\]", r'<a href="https://en.wikipedia.org/wiki/\1">\1</a>', content)
79 # Anchor links: [#anchor-name] -> local anchor
80 content = re.sub(r"\[#([^\]]+)\]", r'<a href="#\1">\1</a>', content)
81 # Bare wiki links: [PageName] (no pipe, not a URL)
82 content = re.sub(r"\[([A-Z][a-zA-Z0-9_]+)\]", r'<a href="\1">\1</a>', content)
83
84 # Verbatim blocks
85 # Pikchr diagrams: <verbatim type="pikchr">...</verbatim> → SVG
86 def _render_pikchr_block(m):
87 try:
88 from fossil.cli import FossilCLI
89
90 cli = FossilCLI()
91 svg = cli.render_pikchr(m.group(1))
92 if svg:
93 return f'<div class="pikchr-diagram">{svg}</div>'
94 except Exception:
95 pass
96 return f'<pre><code class="language-pikchr">{m.group(1)}</code></pre>'
97
98 content = re.sub(r'<verbatim\s+type="pikchr">(.*?)</verbatim>', _render_pikchr_block, content, flags=re.DOTALL)
99 # Regular verbatim blocks
100 content = re.sub(r"<verbatim>(.*?)</verbatim>", r"<pre><code>\1</code></pre>", content, flags=re.DOTALL)
101 # <nowiki> blocks — strip the tags, content passes through as-is
102 content = re.sub(r"<nowiki>(.*?)</nowiki>", r"\1", content, flags=re.DOTALL)
103
104 # Convert Fossil wiki list syntax: * bullets and # enumeration
105

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button