Skip to content

Commit b4e2bed

Browse files
authored
Transform GitHub markdown task list's rendered <input> into icons. (#8044)
1 parent b36a318 commit b4e2bed

File tree

2 files changed

+45
-0
lines changed

2 files changed

+45
-0
lines changed

app/lib/shared/markdown.dart

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,8 @@ String _renderSafeHtml(
9797
}) {
9898
// Filter unsafe urls on some of the elements.
9999
nodes.forEach((node) => node.accept(_UnsafeUrlFilter()));
100+
// Transform GitHub task lists.
101+
nodes.forEach((node) => node.accept(_TaskListRewriteNodeVisitor()));
100102

101103
if (!disableHashIds) {
102104
// add hash link HTML to header blocks
@@ -277,6 +279,38 @@ class _RelativeUrlRewriter implements m.NodeVisitor {
277279
}
278280
}
279281

282+
/// HTML sanitization will remove the rendered `<input type="checkbox">` elements,
283+
/// we are replacing them with icons.
284+
class _TaskListRewriteNodeVisitor implements m.NodeVisitor {
285+
@override
286+
void visitElementAfter(m.Element element) {
287+
if (element.tag != 'li') {
288+
return;
289+
}
290+
if (!(element.attributes['class']?.contains('task-list-item') ?? false)) {
291+
return;
292+
}
293+
final children = element.children;
294+
if (children == null || children.isEmpty) {
295+
return;
296+
}
297+
final first = children.first;
298+
if (first is m.Element &&
299+
first.tag == 'input' &&
300+
first.attributes['type'] == 'checkbox') {
301+
final checked = first.attributes['checked'] == 'true';
302+
children.removeAt(0);
303+
children.insert(0, m.Text(checked ? '✅ ' : '❌ '));
304+
}
305+
}
306+
307+
@override
308+
bool visitElementBefore(m.Element element) => true;
309+
310+
@override
311+
void visitText(m.Text text) {}
312+
}
313+
280314
bool _isAbsolute(String url) => url.contains(':');
281315

282316
String _rewriteAbsoluteUrl(String url) {

app/test/shared/markdown_test.dart

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,17 @@ void main() {
1515
expect(markdownToHtml('# ABC def'),
1616
'<h1 class="hash-header" id="abc-def">ABC def <a href="#abc-def" class="hash-link">#</a></h1>\n');
1717
});
18+
19+
test('task list', () {
20+
expect(
21+
markdownToHtml('- [ ] a\n- [X] b\n- [ ] c\n'),
22+
'<ul>\n'
23+
'<li>❌ a</li>\n'
24+
'<li>✅ b</li>\n'
25+
'<li>❌ c</li>\n'
26+
'</ul>\n',
27+
);
28+
});
1829
});
1930

2031
group('Valid custom base URL', () {

0 commit comments

Comments
 (0)