reviw 0.14.5 → 0.15.0
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/cli.cjs +33 -6
- package/package.json +1 -1
package/cli.cjs
CHANGED
|
@@ -29,21 +29,25 @@ const allowedTags = new Set([
|
|
|
29
29
|
'ul', 'ol', 'li',
|
|
30
30
|
'blockquote', 'pre', 'code',
|
|
31
31
|
'em', 'strong', 'del', 's',
|
|
32
|
-
'a', 'img',
|
|
32
|
+
'a', 'img', 'video',
|
|
33
33
|
'table', 'thead', 'tbody', 'tr', 'th', 'td',
|
|
34
34
|
'div', 'span', // Markdown拡張用
|
|
35
|
+
'details', 'summary', // 折りたたみ用
|
|
35
36
|
]);
|
|
36
37
|
|
|
37
38
|
// 許可属性リスト(タグごとに定義)
|
|
38
39
|
const allowedAttributes = {
|
|
39
40
|
'a': ['href', 'title', 'target', 'rel'],
|
|
40
41
|
'img': ['src', 'alt', 'title', 'width', 'height'],
|
|
42
|
+
'video': ['src', 'controls', 'width', 'height', 'poster', 'preload', 'muted', 'loop'],
|
|
41
43
|
'code': ['class'], // 言語ハイライト用
|
|
42
44
|
'pre': ['class'],
|
|
43
45
|
'div': ['class'],
|
|
44
46
|
'span': ['class'],
|
|
45
47
|
'th': ['align'],
|
|
46
48
|
'td': ['align'],
|
|
49
|
+
'details': ['open'],
|
|
50
|
+
'summary': [],
|
|
47
51
|
};
|
|
48
52
|
|
|
49
53
|
// HTMLエスケープ関数(XSS対策用)
|
|
@@ -149,12 +153,13 @@ marked.use({
|
|
|
149
153
|
}
|
|
150
154
|
var titleAttr = title ? ' title="' + escapeHtmlForXss(title) + '"' : "";
|
|
151
155
|
var altAttr = text ? ' alt="' + escapeHtmlForXss(text) + '"' : "";
|
|
152
|
-
// Check if this is a video file -
|
|
156
|
+
// Check if this is a video file - render as video element with thumbnail
|
|
153
157
|
var videoExtensions = /\.(mp4|mov|webm|avi|mkv|m4v|ogv)$/i;
|
|
154
158
|
if (videoExtensions.test(href)) {
|
|
155
|
-
// For videos,
|
|
159
|
+
// For videos, render as video element with controls and thumbnail preview
|
|
156
160
|
var displayText = text || href.split('/').pop();
|
|
157
|
-
return '<
|
|
161
|
+
return '<video src="' + escapeHtmlForXss(href) + '" controls preload="metadata" class="video-preview"' + titleAttr + '>' +
|
|
162
|
+
'<a href="' + escapeHtmlForXss(href) + '">📹' + escapeHtmlForXss(displayText) + '</a></video>';
|
|
158
163
|
}
|
|
159
164
|
return '<img src="' + escapeHtmlForXss(href) + '"' + altAttr + titleAttr + '>';
|
|
160
165
|
}
|
|
@@ -719,8 +724,28 @@ function loadMarkdown(filePath) {
|
|
|
719
724
|
}
|
|
720
725
|
|
|
721
726
|
// Parse markdown content (without frontmatter)
|
|
722
|
-
|
|
723
|
-
|
|
727
|
+
let contentText = lines.slice(contentStart).join("\n");
|
|
728
|
+
|
|
729
|
+
// Preprocess <details> blocks to preserve their content
|
|
730
|
+
// marked splits HTML blocks on blank lines, so we need to handle details specially
|
|
731
|
+
const detailsBlocks = [];
|
|
732
|
+
contentText = contentText.replace(/<details>([\s\S]*?)<\/details>/gi, function(match, inner) {
|
|
733
|
+
const placeholder = `<!--DETAILS_PLACEHOLDER_${detailsBlocks.length}-->`;
|
|
734
|
+
// Parse the inner content as markdown, preserving the details structure
|
|
735
|
+
const summaryMatch = inner.match(/<summary>([\s\S]*?)<\/summary>/i);
|
|
736
|
+
const summary = summaryMatch ? summaryMatch[1] : '';
|
|
737
|
+
const content = summaryMatch ? inner.replace(/<summary>[\s\S]*?<\/summary>/i, '').trim() : inner.trim();
|
|
738
|
+
const parsedContent = marked.parse(content, { breaks: true });
|
|
739
|
+
detailsBlocks.push(`<details><summary>${summary}</summary>${parsedContent}</details>`);
|
|
740
|
+
return placeholder;
|
|
741
|
+
});
|
|
742
|
+
|
|
743
|
+
let preview = frontmatterHtml + marked.parse(contentText, { breaks: true });
|
|
744
|
+
|
|
745
|
+
// Restore details blocks
|
|
746
|
+
detailsBlocks.forEach((block, i) => {
|
|
747
|
+
preview = preview.replace(`<!--DETAILS_PLACEHOLDER_${i}-->`, block);
|
|
748
|
+
});
|
|
724
749
|
|
|
725
750
|
return {
|
|
726
751
|
rows: lines.map((line) => [line]),
|
|
@@ -2224,6 +2249,8 @@ function htmlTemplate(dataRows, cols, projectRoot, relativePath, mode, previewHt
|
|
|
2224
2249
|
}
|
|
2225
2250
|
.md-preview p { margin: 0.3em 0; line-height: 1.5; }
|
|
2226
2251
|
.md-preview img { max-width: 100%; height: auto; border-radius: 8px; }
|
|
2252
|
+
.md-preview video.video-preview { max-width: 100%; height: auto; border-radius: 8px; background: #000; }
|
|
2253
|
+
.md-preview table video.video-preview { max-width: 300px; }
|
|
2227
2254
|
.md-preview code { background: rgba(255,255,255,0.08); padding: 2px 4px; border-radius: 4px; }
|
|
2228
2255
|
.md-preview pre {
|
|
2229
2256
|
background: var(--code-bg);
|