reviw 0.17.0 → 0.17.2
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 +130 -21
- package/package.json +1 -1
package/cli.cjs
CHANGED
|
@@ -126,6 +126,13 @@ function sanitizeHtml(html) {
|
|
|
126
126
|
}
|
|
127
127
|
|
|
128
128
|
marked.use({
|
|
129
|
+
hooks: {
|
|
130
|
+
// テーブルをスクロールラッパーで囲む(後処理)
|
|
131
|
+
postprocess: function(html) {
|
|
132
|
+
return html.replace(/<table>/g, '<div class="table-scroll-container"><span class="scroll-hint">← scroll →</span><div class="table-scroll-wrapper"><table>')
|
|
133
|
+
.replace(/<\/table>/g, '</table></div></div>');
|
|
134
|
+
}
|
|
135
|
+
},
|
|
129
136
|
renderer: {
|
|
130
137
|
// 生HTMLブロックをサニタイズ
|
|
131
138
|
html: function(token) {
|
|
@@ -159,7 +166,8 @@ marked.use({
|
|
|
159
166
|
if (videoExtensions.test(href)) {
|
|
160
167
|
// For videos, render as video element with controls and thumbnail preview
|
|
161
168
|
var displayText = text || href.split('/').pop();
|
|
162
|
-
|
|
169
|
+
var dataAlt = text ? ' data-alt="' + escapeHtmlForXss(text) + '"' : "";
|
|
170
|
+
return '<video src="' + escapeHtmlForXss(href) + '" controls preload="metadata" class="video-preview"' + titleAttr + dataAlt + '>' +
|
|
163
171
|
'<a href="' + escapeHtmlForXss(href) + '">📹' + escapeHtmlForXss(displayText) + '</a></video>';
|
|
164
172
|
}
|
|
165
173
|
return '<img src="' + escapeHtmlForXss(href) + '"' + altAttr + titleAttr + '>';
|
|
@@ -2729,7 +2737,8 @@ function htmlTemplate(dataRows, cols, projectRoot, relativePath, mode, previewHt
|
|
|
2729
2737
|
flex: 1;
|
|
2730
2738
|
min-width: 0;
|
|
2731
2739
|
overflow-y: auto;
|
|
2732
|
-
overflow-x:
|
|
2740
|
+
overflow-x: auto;
|
|
2741
|
+
overscroll-behavior: contain;
|
|
2733
2742
|
}
|
|
2734
2743
|
.md-left .md-preview {
|
|
2735
2744
|
max-height: none;
|
|
@@ -2739,6 +2748,7 @@ function htmlTemplate(dataRows, cols, projectRoot, relativePath, mode, previewHt
|
|
|
2739
2748
|
min-width: 0;
|
|
2740
2749
|
overflow-y: auto;
|
|
2741
2750
|
overflow-x: auto;
|
|
2751
|
+
overscroll-behavior: contain;
|
|
2742
2752
|
}
|
|
2743
2753
|
.md-right .table-box {
|
|
2744
2754
|
max-width: none;
|
|
@@ -2769,8 +2779,6 @@ function htmlTemplate(dataRows, cols, projectRoot, relativePath, mode, previewHt
|
|
|
2769
2779
|
display: block;
|
|
2770
2780
|
width: 100%;
|
|
2771
2781
|
height: auto;
|
|
2772
|
-
min-width: 120px;
|
|
2773
|
-
max-width: 250px;
|
|
2774
2782
|
}
|
|
2775
2783
|
.md-preview code { background: rgba(255,255,255,0.08); padding: 2px 4px; border-radius: 4px; }
|
|
2776
2784
|
.md-preview pre {
|
|
@@ -2896,12 +2904,38 @@ function htmlTemplate(dataRows, cols, projectRoot, relativePath, mode, previewHt
|
|
|
2896
2904
|
[data-theme="light"] .frontmatter-table tbody th {
|
|
2897
2905
|
color: #7c3aed;
|
|
2898
2906
|
}
|
|
2907
|
+
/* Table scroll container and indicator */
|
|
2908
|
+
.table-scroll-container {
|
|
2909
|
+
position: relative;
|
|
2910
|
+
margin: 16px 0;
|
|
2911
|
+
}
|
|
2912
|
+
.table-scroll-wrapper {
|
|
2913
|
+
overflow-x: auto;
|
|
2914
|
+
border-radius: 8px;
|
|
2915
|
+
}
|
|
2916
|
+
.scroll-hint {
|
|
2917
|
+
text-align: right;
|
|
2918
|
+
font-size: 12px;
|
|
2919
|
+
color: var(--accent);
|
|
2920
|
+
padding: 4px 8px;
|
|
2921
|
+
margin-bottom: 4px;
|
|
2922
|
+
opacity: 0;
|
|
2923
|
+
visibility: hidden;
|
|
2924
|
+
transition: opacity 200ms ease;
|
|
2925
|
+
}
|
|
2926
|
+
.table-scroll-container.can-scroll .scroll-hint {
|
|
2927
|
+
opacity: 0.8;
|
|
2928
|
+
visibility: visible;
|
|
2929
|
+
}
|
|
2930
|
+
.table-scroll-container.scrolled-end .scroll-hint {
|
|
2931
|
+
opacity: 0;
|
|
2932
|
+
visibility: hidden;
|
|
2933
|
+
}
|
|
2899
2934
|
/* Markdown tables in preview */
|
|
2900
2935
|
.md-preview table:not(.frontmatter-table table) {
|
|
2901
|
-
width: 100%;
|
|
2936
|
+
min-width: 100%;
|
|
2937
|
+
width: max-content;
|
|
2902
2938
|
border-collapse: collapse;
|
|
2903
|
-
table-layout: fixed;
|
|
2904
|
-
margin: 16px 0;
|
|
2905
2939
|
border: 1px solid var(--border);
|
|
2906
2940
|
border-radius: 8px;
|
|
2907
2941
|
}
|
|
@@ -2913,17 +2947,12 @@ function htmlTemplate(dataRows, cols, projectRoot, relativePath, mode, previewHt
|
|
|
2913
2947
|
vertical-align: top;
|
|
2914
2948
|
word-break: break-word;
|
|
2915
2949
|
overflow-wrap: anywhere;
|
|
2950
|
+
width: auto;
|
|
2916
2951
|
}
|
|
2917
|
-
|
|
2918
|
-
.md-preview table:not(.frontmatter-table table)
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
}
|
|
2922
|
-
.md-preview table:not(.frontmatter-table table) th:last-child,
|
|
2923
|
-
.md-preview table:not(.frontmatter-table table) td:last-child {
|
|
2924
|
-
width: 180px;
|
|
2925
|
-
min-width: 180px;
|
|
2926
|
-
max-width: 180px;
|
|
2952
|
+
/* Force equal column widths when colgroup is not specified */
|
|
2953
|
+
.md-preview table:not(.frontmatter-table table) colgroup ~ * th,
|
|
2954
|
+
.md-preview table:not(.frontmatter-table table) colgroup ~ * td {
|
|
2955
|
+
width: auto;
|
|
2927
2956
|
}
|
|
2928
2957
|
.md-preview table:not(.frontmatter-table table) td:has(video),
|
|
2929
2958
|
.md-preview table:not(.frontmatter-table table) td:has(img) {
|
|
@@ -4033,6 +4062,48 @@ function htmlTemplate(dataRows, cols, projectRoot, relativePath, mode, previewHt
|
|
|
4033
4062
|
themeToggle.addEventListener('click', toggleTheme);
|
|
4034
4063
|
})();
|
|
4035
4064
|
|
|
4065
|
+
// --- Table Scroll Indicator ---
|
|
4066
|
+
(function initTableScrollIndicators() {
|
|
4067
|
+
function updateScrollIndicator(wrapper) {
|
|
4068
|
+
const container = wrapper.closest('.table-scroll-container');
|
|
4069
|
+
if (!container) return;
|
|
4070
|
+
|
|
4071
|
+
const canScroll = wrapper.scrollWidth > wrapper.clientWidth;
|
|
4072
|
+
const isAtEnd = wrapper.scrollLeft + wrapper.clientWidth >= wrapper.scrollWidth - 5;
|
|
4073
|
+
|
|
4074
|
+
container.classList.toggle('can-scroll', canScroll && !isAtEnd);
|
|
4075
|
+
container.classList.toggle('scrolled-end', isAtEnd);
|
|
4076
|
+
}
|
|
4077
|
+
|
|
4078
|
+
function initWrapper(wrapper) {
|
|
4079
|
+
updateScrollIndicator(wrapper);
|
|
4080
|
+
wrapper.addEventListener('scroll', () => updateScrollIndicator(wrapper));
|
|
4081
|
+
}
|
|
4082
|
+
|
|
4083
|
+
// Initialize existing wrappers
|
|
4084
|
+
document.querySelectorAll('.table-scroll-wrapper').forEach(initWrapper);
|
|
4085
|
+
|
|
4086
|
+
// Watch for dynamically added wrappers
|
|
4087
|
+
const observer = new MutationObserver((mutations) => {
|
|
4088
|
+
mutations.forEach((mutation) => {
|
|
4089
|
+
mutation.addedNodes.forEach((node) => {
|
|
4090
|
+
if (node.nodeType === 1) {
|
|
4091
|
+
if (node.classList?.contains('table-scroll-wrapper')) {
|
|
4092
|
+
initWrapper(node);
|
|
4093
|
+
}
|
|
4094
|
+
node.querySelectorAll?.('.table-scroll-wrapper').forEach(initWrapper);
|
|
4095
|
+
}
|
|
4096
|
+
});
|
|
4097
|
+
});
|
|
4098
|
+
});
|
|
4099
|
+
observer.observe(document.body, { childList: true, subtree: true });
|
|
4100
|
+
|
|
4101
|
+
// Update on resize
|
|
4102
|
+
window.addEventListener('resize', () => {
|
|
4103
|
+
document.querySelectorAll('.table-scroll-wrapper').forEach(updateScrollIndicator);
|
|
4104
|
+
});
|
|
4105
|
+
})();
|
|
4106
|
+
|
|
4036
4107
|
// --- History Management ---
|
|
4037
4108
|
// History is now server-side (file-based), HISTORY_DATA is provided by server
|
|
4038
4109
|
|
|
@@ -6117,8 +6188,46 @@ function htmlTemplate(dataRows, cols, projectRoot, relativePath, mode, previewHt
|
|
|
6117
6188
|
.trim();
|
|
6118
6189
|
}
|
|
6119
6190
|
|
|
6120
|
-
// Helper: find matching source line for text
|
|
6121
|
-
|
|
6191
|
+
// Helper: find matching source line for text or element
|
|
6192
|
+
// If element is provided, also searches by media src attributes
|
|
6193
|
+
function findSourceLine(text, element = null) {
|
|
6194
|
+
// First, try to find by media src (images, videos) in the element
|
|
6195
|
+
if (element) {
|
|
6196
|
+
const mediaElements = element.querySelectorAll('img, video');
|
|
6197
|
+
for (const m of mediaElements) {
|
|
6198
|
+
const src = m.getAttribute('src');
|
|
6199
|
+
if (!src) continue;
|
|
6200
|
+
|
|
6201
|
+
const fileName = src.split('/').pop();
|
|
6202
|
+
const alt = m.getAttribute('alt') || m.getAttribute('data-alt') || m.getAttribute('title') || '';
|
|
6203
|
+
|
|
6204
|
+
// Search for lines containing this media file ( syntax)
|
|
6205
|
+
// Prioritize exact match with alt text
|
|
6206
|
+
let bestMatch = -1;
|
|
6207
|
+
for (let i = 0; i < DATA.length; i++) {
|
|
6208
|
+
const lineText = (DATA[i][0] || '');
|
|
6209
|
+
if (!lineText.includes(fileName)) continue;
|
|
6210
|
+
|
|
6211
|
+
// Check if it's an image/video markdown syntax
|
|
6212
|
+
const match = lineText.match(/!\\[([^\\]]*)\\]\\(([^)]+)\\)/);
|
|
6213
|
+
if (!match) continue;
|
|
6214
|
+
|
|
6215
|
+
const [, mdAlt, mdPath] = match;
|
|
6216
|
+
|
|
6217
|
+
// Exact path match
|
|
6218
|
+
if (mdPath.includes(fileName)) {
|
|
6219
|
+
// If alt text matches exactly, this is definitely the right one
|
|
6220
|
+
if (alt && mdAlt && mdAlt === alt) {
|
|
6221
|
+
return i + 1;
|
|
6222
|
+
}
|
|
6223
|
+
// Otherwise, remember as fallback (prefer first match)
|
|
6224
|
+
if (bestMatch === -1) bestMatch = i + 1;
|
|
6225
|
+
}
|
|
6226
|
+
}
|
|
6227
|
+
if (bestMatch !== -1) return bestMatch;
|
|
6228
|
+
}
|
|
6229
|
+
}
|
|
6230
|
+
|
|
6122
6231
|
if (!text) return -1;
|
|
6123
6232
|
const normalized = text.trim().replace(/\\s+/g, ' ').slice(0, 100);
|
|
6124
6233
|
if (!normalized) return -1;
|
|
@@ -6358,9 +6467,9 @@ function htmlTemplate(dataRows, cols, projectRoot, relativePath, mode, previewHt
|
|
|
6358
6467
|
const target = e.target.closest('p, h1, h2, h3, h4, h5, h6, li, blockquote, td, th');
|
|
6359
6468
|
if (!target) return;
|
|
6360
6469
|
|
|
6361
|
-
// Use table-specific search for table cells
|
|
6470
|
+
// Use table-specific search for table cells, otherwise use element-aware search
|
|
6362
6471
|
const isTableCell = target.tagName === 'TD' || target.tagName === 'TH';
|
|
6363
|
-
const line = isTableCell ? findTableSourceLine(target.textContent) : findSourceLine(target.textContent);
|
|
6472
|
+
const line = isTableCell ? findTableSourceLine(target.textContent) : findSourceLine(target.textContent, target);
|
|
6364
6473
|
if (line <= 0) return;
|
|
6365
6474
|
|
|
6366
6475
|
e.preventDefault();
|