reviw 0.7.1 → 0.9.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/README.md +4 -4
- package/cli.cjs +383 -98
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -72,16 +72,16 @@ reviw changes.diff
|
|
|
72
72
|
## Screenshots
|
|
73
73
|
|
|
74
74
|
### CSV View
|
|
75
|
-

|
|
75
|
+

|
|
76
76
|
|
|
77
77
|
### Markdown View
|
|
78
|
-

|
|
78
|
+

|
|
79
79
|
|
|
80
80
|
### Diff View
|
|
81
|
-

|
|
81
|
+

|
|
82
82
|
|
|
83
83
|
### Mermaid Fullscreen
|
|
84
|
-

|
|
84
|
+

|
|
85
85
|
|
|
86
86
|
## Output Example
|
|
87
87
|
|
package/cli.cjs
CHANGED
|
@@ -403,7 +403,60 @@ function loadMarkdown(filePath) {
|
|
|
403
403
|
const raw = fs.readFileSync(filePath);
|
|
404
404
|
const text = decodeBuffer(raw);
|
|
405
405
|
const lines = text.split(/\r?\n/);
|
|
406
|
-
|
|
406
|
+
|
|
407
|
+
// Parse YAML frontmatter
|
|
408
|
+
let frontmatterHtml = '';
|
|
409
|
+
let contentStart = 0;
|
|
410
|
+
|
|
411
|
+
if (lines[0] && lines[0].trim() === '---') {
|
|
412
|
+
let frontmatterEnd = -1;
|
|
413
|
+
for (let i = 1; i < lines.length; i++) {
|
|
414
|
+
if (lines[i].trim() === '---') {
|
|
415
|
+
frontmatterEnd = i;
|
|
416
|
+
break;
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
if (frontmatterEnd > 0) {
|
|
421
|
+
const frontmatterLines = lines.slice(1, frontmatterEnd);
|
|
422
|
+
const frontmatterText = frontmatterLines.join('\n');
|
|
423
|
+
|
|
424
|
+
try {
|
|
425
|
+
const frontmatter = yaml.load(frontmatterText);
|
|
426
|
+
if (frontmatter && typeof frontmatter === 'object') {
|
|
427
|
+
// Create HTML table for frontmatter
|
|
428
|
+
frontmatterHtml = '<div class="frontmatter-table"><table>';
|
|
429
|
+
frontmatterHtml += '<colgroup><col style="width:12%"><col style="width:88%"></colgroup>';
|
|
430
|
+
frontmatterHtml += '<thead><tr><th colspan="2">Document Metadata</th></tr></thead>';
|
|
431
|
+
frontmatterHtml += '<tbody>';
|
|
432
|
+
|
|
433
|
+
function renderValue(val) {
|
|
434
|
+
if (Array.isArray(val)) {
|
|
435
|
+
return val.map(v => '<span class="fm-tag">' + escapeHtmlChars(String(v)) + '</span>').join(' ');
|
|
436
|
+
}
|
|
437
|
+
if (typeof val === 'object' && val !== null) {
|
|
438
|
+
return '<pre>' + escapeHtmlChars(JSON.stringify(val, null, 2)) + '</pre>';
|
|
439
|
+
}
|
|
440
|
+
return escapeHtmlChars(String(val));
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
for (const [key, val] of Object.entries(frontmatter)) {
|
|
444
|
+
frontmatterHtml += '<tr><th>' + escapeHtmlChars(key) + '</th><td>' + renderValue(val) + '</td></tr>';
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
frontmatterHtml += '</tbody></table></div>';
|
|
448
|
+
contentStart = frontmatterEnd + 1;
|
|
449
|
+
}
|
|
450
|
+
} catch (e) {
|
|
451
|
+
// Invalid YAML, skip frontmatter rendering
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
// Parse markdown content (without frontmatter)
|
|
457
|
+
const contentText = lines.slice(contentStart).join('\n');
|
|
458
|
+
const preview = frontmatterHtml + marked.parse(contentText, { breaks: true });
|
|
459
|
+
|
|
407
460
|
return {
|
|
408
461
|
rows: lines.map((line) => [line]),
|
|
409
462
|
cols: 1,
|
|
@@ -412,6 +465,14 @@ function loadMarkdown(filePath) {
|
|
|
412
465
|
};
|
|
413
466
|
}
|
|
414
467
|
|
|
468
|
+
function escapeHtmlChars(str) {
|
|
469
|
+
return str
|
|
470
|
+
.replace(/&/g, '&')
|
|
471
|
+
.replace(/</g, '<')
|
|
472
|
+
.replace(/>/g, '>')
|
|
473
|
+
.replace(/"/g, '"');
|
|
474
|
+
}
|
|
475
|
+
|
|
415
476
|
function loadData(filePath) {
|
|
416
477
|
const ext = path.extname(filePath).toLowerCase();
|
|
417
478
|
if (ext === '.csv' || ext === '.tsv') {
|
|
@@ -1751,10 +1812,201 @@ function htmlTemplate(dataRows, cols, title, mode, previewHtml) {
|
|
|
1751
1812
|
.md-preview img { max-width: 100%; height: auto; border-radius: 8px; }
|
|
1752
1813
|
.md-preview code { background: rgba(255,255,255,0.08); padding: 2px 4px; border-radius: 4px; }
|
|
1753
1814
|
.md-preview pre {
|
|
1754
|
-
background:
|
|
1755
|
-
padding:
|
|
1815
|
+
background: var(--code-bg);
|
|
1816
|
+
padding: 12px 16px;
|
|
1756
1817
|
border-radius: 8px;
|
|
1757
1818
|
overflow: auto;
|
|
1819
|
+
border: 1px solid var(--border);
|
|
1820
|
+
}
|
|
1821
|
+
.md-preview pre code {
|
|
1822
|
+
background: none;
|
|
1823
|
+
padding: 0;
|
|
1824
|
+
font-size: 13px;
|
|
1825
|
+
line-height: 1.5;
|
|
1826
|
+
}
|
|
1827
|
+
.md-preview pre code.hljs {
|
|
1828
|
+
background: transparent;
|
|
1829
|
+
padding: 0;
|
|
1830
|
+
}
|
|
1831
|
+
/* YAML Frontmatter table */
|
|
1832
|
+
.frontmatter-table {
|
|
1833
|
+
margin-bottom: 20px;
|
|
1834
|
+
border-radius: 8px;
|
|
1835
|
+
overflow: hidden;
|
|
1836
|
+
border: 1px solid var(--border);
|
|
1837
|
+
background: var(--panel);
|
|
1838
|
+
}
|
|
1839
|
+
.frontmatter-table table {
|
|
1840
|
+
width: 100%;
|
|
1841
|
+
border-collapse: collapse;
|
|
1842
|
+
table-layout: fixed;
|
|
1843
|
+
}
|
|
1844
|
+
.frontmatter-table thead th {
|
|
1845
|
+
background: linear-gradient(135deg, rgba(147, 51, 234, 0.15), rgba(96, 165, 250, 0.15));
|
|
1846
|
+
color: var(--text);
|
|
1847
|
+
font-size: 12px;
|
|
1848
|
+
font-weight: 600;
|
|
1849
|
+
padding: 10px 16px;
|
|
1850
|
+
text-align: left;
|
|
1851
|
+
border-bottom: 1px solid var(--border);
|
|
1852
|
+
}
|
|
1853
|
+
.frontmatter-table tbody th {
|
|
1854
|
+
background: rgba(147, 51, 234, 0.08);
|
|
1855
|
+
color: #c084fc;
|
|
1856
|
+
font-weight: 500;
|
|
1857
|
+
font-size: 12px;
|
|
1858
|
+
padding: 8px 10px;
|
|
1859
|
+
text-align: left;
|
|
1860
|
+
border-bottom: 1px solid var(--border);
|
|
1861
|
+
vertical-align: top;
|
|
1862
|
+
}
|
|
1863
|
+
.frontmatter-table tbody td {
|
|
1864
|
+
padding: 8px 14px;
|
|
1865
|
+
font-size: 13px;
|
|
1866
|
+
border-bottom: 1px solid var(--border);
|
|
1867
|
+
word-break: break-word;
|
|
1868
|
+
}
|
|
1869
|
+
.frontmatter-table tbody tr:last-child th,
|
|
1870
|
+
.frontmatter-table tbody tr:last-child td {
|
|
1871
|
+
border-bottom: none;
|
|
1872
|
+
}
|
|
1873
|
+
.frontmatter-table .fm-tag {
|
|
1874
|
+
display: inline-block;
|
|
1875
|
+
background: rgba(96, 165, 250, 0.15);
|
|
1876
|
+
color: var(--accent);
|
|
1877
|
+
padding: 2px 8px;
|
|
1878
|
+
border-radius: 12px;
|
|
1879
|
+
font-size: 11px;
|
|
1880
|
+
margin-right: 4px;
|
|
1881
|
+
margin-bottom: 4px;
|
|
1882
|
+
}
|
|
1883
|
+
.frontmatter-table pre {
|
|
1884
|
+
margin: 0;
|
|
1885
|
+
background: var(--code-bg);
|
|
1886
|
+
padding: 8px;
|
|
1887
|
+
border-radius: 4px;
|
|
1888
|
+
font-size: 11px;
|
|
1889
|
+
}
|
|
1890
|
+
[data-theme="light"] .frontmatter-table tbody th {
|
|
1891
|
+
color: #7c3aed;
|
|
1892
|
+
}
|
|
1893
|
+
/* Markdown tables in preview */
|
|
1894
|
+
.md-preview table:not(.frontmatter-table table) {
|
|
1895
|
+
width: 100%;
|
|
1896
|
+
border-collapse: collapse;
|
|
1897
|
+
margin: 16px 0;
|
|
1898
|
+
border: 1px solid var(--border);
|
|
1899
|
+
border-radius: 8px;
|
|
1900
|
+
overflow: hidden;
|
|
1901
|
+
}
|
|
1902
|
+
.md-preview table:not(.frontmatter-table table) th,
|
|
1903
|
+
.md-preview table:not(.frontmatter-table table) td {
|
|
1904
|
+
width: 50%;
|
|
1905
|
+
padding: 10px 16px;
|
|
1906
|
+
text-align: left;
|
|
1907
|
+
border-bottom: 1px solid var(--border);
|
|
1908
|
+
}
|
|
1909
|
+
.md-preview table:not(.frontmatter-table table) th {
|
|
1910
|
+
background: rgba(255,255,255,0.05);
|
|
1911
|
+
}
|
|
1912
|
+
.md-preview table:not(.frontmatter-table table) th {
|
|
1913
|
+
background: var(--panel);
|
|
1914
|
+
font-weight: 600;
|
|
1915
|
+
font-size: 13px;
|
|
1916
|
+
}
|
|
1917
|
+
.md-preview table:not(.frontmatter-table table) td {
|
|
1918
|
+
font-size: 13px;
|
|
1919
|
+
}
|
|
1920
|
+
.md-preview table:not(.frontmatter-table table) tr:last-child td {
|
|
1921
|
+
border-bottom: none;
|
|
1922
|
+
}
|
|
1923
|
+
.md-preview table:not(.frontmatter-table table) tr:hover td {
|
|
1924
|
+
background: var(--hover-bg);
|
|
1925
|
+
}
|
|
1926
|
+
/* Source table (右ペイン) */
|
|
1927
|
+
.table-box table {
|
|
1928
|
+
table-layout: fixed;
|
|
1929
|
+
width: 100%;
|
|
1930
|
+
}
|
|
1931
|
+
.table-box th,
|
|
1932
|
+
.table-box td {
|
|
1933
|
+
word-break: break-word;
|
|
1934
|
+
min-width: 140px;
|
|
1935
|
+
}
|
|
1936
|
+
.table-box th:first-child,
|
|
1937
|
+
.table-box td:first-child {
|
|
1938
|
+
min-width: 320px;
|
|
1939
|
+
max-width: 480px;
|
|
1940
|
+
}
|
|
1941
|
+
/* Image fullscreen overlay */
|
|
1942
|
+
.image-fullscreen-overlay {
|
|
1943
|
+
position: fixed;
|
|
1944
|
+
inset: 0;
|
|
1945
|
+
background: rgba(0, 0, 0, 0.9);
|
|
1946
|
+
z-index: 1001;
|
|
1947
|
+
display: none;
|
|
1948
|
+
justify-content: center;
|
|
1949
|
+
align-items: center;
|
|
1950
|
+
}
|
|
1951
|
+
.image-fullscreen-overlay.visible {
|
|
1952
|
+
display: flex;
|
|
1953
|
+
}
|
|
1954
|
+
.image-close-btn {
|
|
1955
|
+
position: absolute;
|
|
1956
|
+
top: 14px;
|
|
1957
|
+
right: 14px;
|
|
1958
|
+
width: 40px;
|
|
1959
|
+
height: 40px;
|
|
1960
|
+
display: flex;
|
|
1961
|
+
align-items: center;
|
|
1962
|
+
justify-content: center;
|
|
1963
|
+
background: rgba(0, 0, 0, 0.55);
|
|
1964
|
+
border: 1px solid rgba(255, 255, 255, 0.25);
|
|
1965
|
+
border-radius: 50%;
|
|
1966
|
+
cursor: pointer;
|
|
1967
|
+
color: #fff;
|
|
1968
|
+
font-size: 18px;
|
|
1969
|
+
z-index: 10;
|
|
1970
|
+
backdrop-filter: blur(4px);
|
|
1971
|
+
transition: background 120ms ease, transform 120ms ease;
|
|
1972
|
+
}
|
|
1973
|
+
.image-close-btn:hover {
|
|
1974
|
+
background: rgba(0, 0, 0, 0.75);
|
|
1975
|
+
transform: scale(1.04);
|
|
1976
|
+
}
|
|
1977
|
+
.image-container {
|
|
1978
|
+
max-width: 90vw;
|
|
1979
|
+
max-height: 90vh;
|
|
1980
|
+
display: flex;
|
|
1981
|
+
justify-content: center;
|
|
1982
|
+
align-items: center;
|
|
1983
|
+
}
|
|
1984
|
+
.image-container img {
|
|
1985
|
+
max-width: 100%;
|
|
1986
|
+
max-height: 90vh;
|
|
1987
|
+
object-fit: contain;
|
|
1988
|
+
border-radius: 8px;
|
|
1989
|
+
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
|
|
1990
|
+
}
|
|
1991
|
+
/* Copy notification toast */
|
|
1992
|
+
.copy-toast {
|
|
1993
|
+
position: fixed;
|
|
1994
|
+
bottom: 60px;
|
|
1995
|
+
left: 50%;
|
|
1996
|
+
transform: translateX(-50%) translateY(20px);
|
|
1997
|
+
background: var(--accent);
|
|
1998
|
+
color: var(--text-inverse);
|
|
1999
|
+
padding: 8px 16px;
|
|
2000
|
+
border-radius: 8px;
|
|
2001
|
+
font-size: 13px;
|
|
2002
|
+
opacity: 0;
|
|
2003
|
+
pointer-events: none;
|
|
2004
|
+
transition: opacity 200ms ease, transform 200ms ease;
|
|
2005
|
+
z-index: 1000;
|
|
2006
|
+
}
|
|
2007
|
+
.copy-toast.visible {
|
|
2008
|
+
opacity: 1;
|
|
2009
|
+
transform: translateX(-50%) translateY(0);
|
|
1758
2010
|
}
|
|
1759
2011
|
@media (max-width: 960px) {
|
|
1760
2012
|
.md-layout { flex-direction: column; }
|
|
@@ -1859,6 +2111,8 @@ function htmlTemplate(dataRows, cols, title, mode, previewHtml) {
|
|
|
1859
2111
|
.mermaid-container .mermaid svg {
|
|
1860
2112
|
max-width: 100%;
|
|
1861
2113
|
height: auto;
|
|
2114
|
+
cursor: pointer;
|
|
2115
|
+
pointer-events: auto;
|
|
1862
2116
|
}
|
|
1863
2117
|
.mermaid-fullscreen-btn {
|
|
1864
2118
|
position: absolute;
|
|
@@ -1923,39 +2177,6 @@ function htmlTemplate(dataRows, cols, title, mode, previewHtml) {
|
|
|
1923
2177
|
.fullscreen-content .mermaid svg {
|
|
1924
2178
|
display: block;
|
|
1925
2179
|
}
|
|
1926
|
-
/* Minimap */
|
|
1927
|
-
.minimap {
|
|
1928
|
-
position: absolute;
|
|
1929
|
-
top: 70px;
|
|
1930
|
-
right: 20px;
|
|
1931
|
-
width: 200px;
|
|
1932
|
-
height: 150px;
|
|
1933
|
-
background: var(--panel-alpha);
|
|
1934
|
-
border: 1px solid var(--border);
|
|
1935
|
-
border-radius: 8px;
|
|
1936
|
-
overflow: hidden;
|
|
1937
|
-
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
|
|
1938
|
-
}
|
|
1939
|
-
.minimap-content {
|
|
1940
|
-
width: 100%;
|
|
1941
|
-
height: 100%;
|
|
1942
|
-
display: flex;
|
|
1943
|
-
align-items: center;
|
|
1944
|
-
justify-content: center;
|
|
1945
|
-
padding: 8px;
|
|
1946
|
-
}
|
|
1947
|
-
.minimap-content svg {
|
|
1948
|
-
max-width: 100%;
|
|
1949
|
-
max-height: 100%;
|
|
1950
|
-
opacity: 0.6;
|
|
1951
|
-
}
|
|
1952
|
-
.minimap-viewport {
|
|
1953
|
-
position: absolute;
|
|
1954
|
-
border: 2px solid var(--accent);
|
|
1955
|
-
background: rgba(102, 126, 234, 0.2);
|
|
1956
|
-
pointer-events: none;
|
|
1957
|
-
border-radius: 2px;
|
|
1958
|
-
}
|
|
1959
2180
|
/* Error toast */
|
|
1960
2181
|
.mermaid-error-toast {
|
|
1961
2182
|
position: fixed;
|
|
@@ -1977,6 +2198,9 @@ function htmlTemplate(dataRows, cols, title, mode, previewHtml) {
|
|
|
1977
2198
|
.mermaid-error-toast.visible { display: block; }
|
|
1978
2199
|
</style>
|
|
1979
2200
|
<script src="https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js"></script>
|
|
2201
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11/build/styles/github-dark.min.css" id="hljs-theme-dark">
|
|
2202
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11/build/styles/github.min.css" id="hljs-theme-light" disabled>
|
|
2203
|
+
<script src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11/build/highlight.min.js"></script>
|
|
1980
2204
|
</head>
|
|
1981
2205
|
<body>
|
|
1982
2206
|
<header>
|
|
@@ -2106,12 +2330,13 @@ function htmlTemplate(dataRows, cols, title, mode, previewHtml) {
|
|
|
2106
2330
|
<div class="fullscreen-content" id="fs-content">
|
|
2107
2331
|
<div class="mermaid-wrapper" id="fs-wrapper"></div>
|
|
2108
2332
|
</div>
|
|
2109
|
-
<div class="minimap" id="fs-minimap">
|
|
2110
|
-
<div class="minimap-content" id="fs-minimap-content"></div>
|
|
2111
|
-
<div class="minimap-viewport" id="fs-minimap-viewport"></div>
|
|
2112
|
-
</div>
|
|
2113
2333
|
</div>
|
|
2114
2334
|
<div class="mermaid-error-toast" id="mermaid-error-toast"></div>
|
|
2335
|
+
<div class="copy-toast" id="copy-toast">Copied to clipboard!</div>
|
|
2336
|
+
<div class="image-fullscreen-overlay" id="image-fullscreen">
|
|
2337
|
+
<button class="image-close-btn" id="image-close" aria-label="Close image" title="Close (ESC)">✕</button>
|
|
2338
|
+
<div class="image-container" id="image-container"></div>
|
|
2339
|
+
</div>
|
|
2115
2340
|
|
|
2116
2341
|
<script>
|
|
2117
2342
|
const DATA = ${serialized};
|
|
@@ -2133,12 +2358,18 @@ function htmlTemplate(dataRows, cols, title, mode, previewHtml) {
|
|
|
2133
2358
|
}
|
|
2134
2359
|
|
|
2135
2360
|
function setTheme(theme) {
|
|
2361
|
+
const hljsDark = document.getElementById('hljs-theme-dark');
|
|
2362
|
+
const hljsLight = document.getElementById('hljs-theme-light');
|
|
2136
2363
|
if (theme === 'light') {
|
|
2137
2364
|
document.documentElement.setAttribute('data-theme', 'light');
|
|
2138
2365
|
themeIcon.textContent = '☀️';
|
|
2366
|
+
if (hljsDark) hljsDark.disabled = true;
|
|
2367
|
+
if (hljsLight) hljsLight.disabled = false;
|
|
2139
2368
|
} else {
|
|
2140
2369
|
document.documentElement.removeAttribute('data-theme');
|
|
2141
2370
|
themeIcon.textContent = '🌙';
|
|
2371
|
+
if (hljsDark) hljsDark.disabled = false;
|
|
2372
|
+
if (hljsLight) hljsLight.disabled = true;
|
|
2142
2373
|
}
|
|
2143
2374
|
localStorage.setItem('reviw-theme', theme);
|
|
2144
2375
|
}
|
|
@@ -2182,11 +2413,14 @@ function htmlTemplate(dataRows, cols, title, mode, previewHtml) {
|
|
|
2182
2413
|
const freezeRowCheck = document.getElementById('freeze-row-check');
|
|
2183
2414
|
|
|
2184
2415
|
const ROW_HEADER_WIDTH = 28;
|
|
2185
|
-
const MIN_COL_WIDTH =
|
|
2186
|
-
const MAX_COL_WIDTH =
|
|
2187
|
-
const DEFAULT_COL_WIDTH =
|
|
2416
|
+
const MIN_COL_WIDTH = 140;
|
|
2417
|
+
const MAX_COL_WIDTH = 520;
|
|
2418
|
+
const DEFAULT_COL_WIDTH = 240;
|
|
2188
2419
|
|
|
2189
2420
|
let colWidths = Array.from({ length: MAX_COLS }, () => DEFAULT_COL_WIDTH);
|
|
2421
|
+
if (MODE !== 'csv' && MAX_COLS === 1) {
|
|
2422
|
+
colWidths[0] = 480;
|
|
2423
|
+
}
|
|
2190
2424
|
let panelOpen = false;
|
|
2191
2425
|
let filters = {}; // colIndex -> predicate
|
|
2192
2426
|
let filterTargetCol = null;
|
|
@@ -2343,6 +2577,7 @@ function htmlTemplate(dataRows, cols, title, mode, previewHtml) {
|
|
|
2343
2577
|
const th = document.createElement('th');
|
|
2344
2578
|
th.textContent = rIdx + 1;
|
|
2345
2579
|
tr.appendChild(th);
|
|
2580
|
+
|
|
2346
2581
|
for (let c = 0; c < MAX_COLS; c += 1) {
|
|
2347
2582
|
const td = document.createElement('td');
|
|
2348
2583
|
const val = row[c] || '';
|
|
@@ -2422,11 +2657,48 @@ function htmlTemplate(dataRows, cols, title, mode, previewHtml) {
|
|
|
2422
2657
|
isDragging = false;
|
|
2423
2658
|
document.body.classList.remove('dragging');
|
|
2424
2659
|
if (selection) {
|
|
2660
|
+
// Copy selected text to clipboard
|
|
2661
|
+
copySelectionToClipboard(selection);
|
|
2425
2662
|
openCardForSelection();
|
|
2426
2663
|
}
|
|
2427
2664
|
});
|
|
2428
2665
|
}
|
|
2429
2666
|
|
|
2667
|
+
// Copy selected range to clipboard
|
|
2668
|
+
function copySelectionToClipboard(sel) {
|
|
2669
|
+
const { startRow, endRow, startCol, endCol } = sel;
|
|
2670
|
+
const lines = [];
|
|
2671
|
+
for (let r = startRow; r <= endRow; r++) {
|
|
2672
|
+
const rowData = [];
|
|
2673
|
+
for (let c = startCol; c <= endCol; c++) {
|
|
2674
|
+
const td = tbody.querySelector('td[data-row="' + r + '"][data-col="' + c + '"]');
|
|
2675
|
+
if (td) {
|
|
2676
|
+
// Get text content (strip HTML tags from inline code highlighting)
|
|
2677
|
+
rowData.push(td.textContent || '');
|
|
2678
|
+
}
|
|
2679
|
+
}
|
|
2680
|
+
lines.push(rowData.join('\\t'));
|
|
2681
|
+
}
|
|
2682
|
+
const text = lines.join('\\n');
|
|
2683
|
+
if (text && navigator.clipboard) {
|
|
2684
|
+
navigator.clipboard.writeText(text).then(() => {
|
|
2685
|
+
showCopyToast();
|
|
2686
|
+
}).catch(() => {
|
|
2687
|
+
// Fallback: silent fail
|
|
2688
|
+
});
|
|
2689
|
+
}
|
|
2690
|
+
}
|
|
2691
|
+
|
|
2692
|
+
// Show copy toast notification
|
|
2693
|
+
function showCopyToast() {
|
|
2694
|
+
const toast = document.getElementById('copy-toast');
|
|
2695
|
+
if (!toast) return;
|
|
2696
|
+
toast.classList.add('visible');
|
|
2697
|
+
setTimeout(() => {
|
|
2698
|
+
toast.classList.remove('visible');
|
|
2699
|
+
}, 1500);
|
|
2700
|
+
}
|
|
2701
|
+
|
|
2430
2702
|
function openCardForSelection() {
|
|
2431
2703
|
if (!selection) return;
|
|
2432
2704
|
const { startRow, endRow, startCol, endCol } = selection;
|
|
@@ -3162,15 +3434,12 @@ function htmlTemplate(dataRows, cols, title, mode, previewHtml) {
|
|
|
3162
3434
|
const fsWrapper = document.getElementById('fs-wrapper');
|
|
3163
3435
|
const fsContent = document.getElementById('fs-content');
|
|
3164
3436
|
const fsZoomInfo = document.getElementById('fs-zoom-info');
|
|
3165
|
-
const minimapContent = document.getElementById('fs-minimap-content');
|
|
3166
|
-
const minimapViewport = document.getElementById('fs-minimap-viewport');
|
|
3167
3437
|
let currentZoom = 1;
|
|
3168
3438
|
let initialZoom = 1;
|
|
3169
3439
|
let panX = 0, panY = 0;
|
|
3170
3440
|
let isPanning = false;
|
|
3171
3441
|
let startX, startY;
|
|
3172
3442
|
let svgNaturalWidth = 0, svgNaturalHeight = 0;
|
|
3173
|
-
let minimapScale = 1;
|
|
3174
3443
|
|
|
3175
3444
|
function openFullscreen(mermaidEl) {
|
|
3176
3445
|
const svg = mermaidEl.querySelector('svg');
|
|
@@ -3179,11 +3448,6 @@ function htmlTemplate(dataRows, cols, title, mode, previewHtml) {
|
|
|
3179
3448
|
const clonedSvg = svg.cloneNode(true);
|
|
3180
3449
|
fsWrapper.appendChild(clonedSvg);
|
|
3181
3450
|
|
|
3182
|
-
// Setup minimap
|
|
3183
|
-
minimapContent.innerHTML = '';
|
|
3184
|
-
const minimapSvg = svg.cloneNode(true);
|
|
3185
|
-
minimapContent.appendChild(minimapSvg);
|
|
3186
|
-
|
|
3187
3451
|
// Get SVG's intrinsic/natural size from viewBox or attributes
|
|
3188
3452
|
const viewBox = svg.getAttribute('viewBox');
|
|
3189
3453
|
let naturalWidth, naturalHeight;
|
|
@@ -3200,11 +3464,6 @@ function htmlTemplate(dataRows, cols, title, mode, previewHtml) {
|
|
|
3200
3464
|
svgNaturalWidth = naturalWidth;
|
|
3201
3465
|
svgNaturalHeight = naturalHeight;
|
|
3202
3466
|
|
|
3203
|
-
// Calculate minimap scale
|
|
3204
|
-
const minimapMaxWidth = 184; // 200 - 16 padding
|
|
3205
|
-
const minimapMaxHeight = 134; // 150 - 16 padding
|
|
3206
|
-
minimapScale = Math.min(minimapMaxWidth / naturalWidth, minimapMaxHeight / naturalHeight);
|
|
3207
|
-
|
|
3208
3467
|
clonedSvg.style.width = naturalWidth + 'px';
|
|
3209
3468
|
clonedSvg.style.height = naturalHeight + 'px';
|
|
3210
3469
|
|
|
@@ -3236,48 +3495,6 @@ function htmlTemplate(dataRows, cols, title, mode, previewHtml) {
|
|
|
3236
3495
|
function updateTransform() {
|
|
3237
3496
|
fsWrapper.style.transform = 'translate(' + panX + 'px, ' + panY + 'px) scale(' + currentZoom + ')';
|
|
3238
3497
|
fsZoomInfo.textContent = Math.round(currentZoom * 100) + '%';
|
|
3239
|
-
updateMinimap();
|
|
3240
|
-
}
|
|
3241
|
-
|
|
3242
|
-
function updateMinimap() {
|
|
3243
|
-
if (!svgNaturalWidth || !svgNaturalHeight) return;
|
|
3244
|
-
|
|
3245
|
-
const viewportWidth = window.innerWidth - 40;
|
|
3246
|
-
const viewportHeight = window.innerHeight - 80;
|
|
3247
|
-
|
|
3248
|
-
// Minimap dimensions
|
|
3249
|
-
const mmWidth = 184;
|
|
3250
|
-
const mmHeight = 134;
|
|
3251
|
-
const mmPadding = 8;
|
|
3252
|
-
|
|
3253
|
-
// SVG size in minimap (centered)
|
|
3254
|
-
const mmSvgWidth = svgNaturalWidth * minimapScale;
|
|
3255
|
-
const mmSvgHeight = svgNaturalHeight * minimapScale;
|
|
3256
|
-
const mmSvgLeft = (mmWidth - mmSvgWidth) / 2 + mmPadding;
|
|
3257
|
-
const mmSvgTop = (mmHeight - mmSvgHeight) / 2 + mmPadding;
|
|
3258
|
-
|
|
3259
|
-
// Calculate visible area in SVG coordinates (accounting for transform origin at 0,0)
|
|
3260
|
-
// panX/panY are the translation values, currentZoom is the scale
|
|
3261
|
-
// The visible area starts at -panX/currentZoom in SVG coordinates
|
|
3262
|
-
const visibleLeft = Math.max(0, -panX / currentZoom);
|
|
3263
|
-
const visibleTop = Math.max(0, (-panY + 60) / currentZoom);
|
|
3264
|
-
const visibleWidth = viewportWidth / currentZoom;
|
|
3265
|
-
const visibleHeight = viewportHeight / currentZoom;
|
|
3266
|
-
|
|
3267
|
-
// Clamp to SVG bounds
|
|
3268
|
-
const clampedLeft = Math.min(visibleLeft, svgNaturalWidth);
|
|
3269
|
-
const clampedTop = Math.min(visibleTop, svgNaturalHeight);
|
|
3270
|
-
|
|
3271
|
-
// Position viewport indicator in minimap coordinates
|
|
3272
|
-
const vpLeft = mmSvgLeft + clampedLeft * minimapScale;
|
|
3273
|
-
const vpTop = mmSvgTop + clampedTop * minimapScale;
|
|
3274
|
-
const vpWidth = Math.min(mmWidth - vpLeft + mmPadding, visibleWidth * minimapScale);
|
|
3275
|
-
const vpHeight = Math.min(mmHeight - vpTop + mmPadding, visibleHeight * minimapScale);
|
|
3276
|
-
|
|
3277
|
-
minimapViewport.style.left = vpLeft + 'px';
|
|
3278
|
-
minimapViewport.style.top = vpTop + 'px';
|
|
3279
|
-
minimapViewport.style.width = Math.max(20, vpWidth) + 'px';
|
|
3280
|
-
minimapViewport.style.height = Math.max(15, vpHeight) + 'px';
|
|
3281
3498
|
}
|
|
3282
3499
|
|
|
3283
3500
|
// Use multiplicative zoom for consistent behavior
|
|
@@ -3350,6 +3567,74 @@ function htmlTemplate(dataRows, cols, title, mode, previewHtml) {
|
|
|
3350
3567
|
}
|
|
3351
3568
|
});
|
|
3352
3569
|
})();
|
|
3570
|
+
|
|
3571
|
+
// --- Highlight.js Initialization ---
|
|
3572
|
+
(function initHighlightJS() {
|
|
3573
|
+
if (typeof hljs === 'undefined') return;
|
|
3574
|
+
|
|
3575
|
+
// Highlight all code blocks in preview (skip mermaid blocks)
|
|
3576
|
+
const preview = document.querySelector('.md-preview');
|
|
3577
|
+
if (preview) {
|
|
3578
|
+
preview.querySelectorAll('pre code').forEach(block => {
|
|
3579
|
+
// Skip if inside mermaid container or already highlighted
|
|
3580
|
+
if (block.closest('.mermaid-container') || block.classList.contains('hljs')) {
|
|
3581
|
+
return;
|
|
3582
|
+
}
|
|
3583
|
+
hljs.highlightElement(block);
|
|
3584
|
+
});
|
|
3585
|
+
}
|
|
3586
|
+
})();
|
|
3587
|
+
|
|
3588
|
+
// --- Image Fullscreen ---
|
|
3589
|
+
(function initImageFullscreen() {
|
|
3590
|
+
const preview = document.querySelector('.md-preview');
|
|
3591
|
+
if (!preview) return;
|
|
3592
|
+
|
|
3593
|
+
const imageOverlay = document.getElementById('image-fullscreen');
|
|
3594
|
+
const imageContainer = document.getElementById('image-container');
|
|
3595
|
+
const imageClose = document.getElementById('image-close');
|
|
3596
|
+
if (!imageOverlay || !imageContainer) return;
|
|
3597
|
+
|
|
3598
|
+
function closeImageOverlay() {
|
|
3599
|
+
imageOverlay.classList.remove('visible');
|
|
3600
|
+
imageContainer.innerHTML = '';
|
|
3601
|
+
}
|
|
3602
|
+
|
|
3603
|
+
if (imageClose) {
|
|
3604
|
+
imageClose.addEventListener('click', closeImageOverlay);
|
|
3605
|
+
}
|
|
3606
|
+
|
|
3607
|
+
if (imageOverlay) {
|
|
3608
|
+
imageOverlay.addEventListener('click', (e) => {
|
|
3609
|
+
if (e.target === imageOverlay) closeImageOverlay();
|
|
3610
|
+
});
|
|
3611
|
+
}
|
|
3612
|
+
|
|
3613
|
+
document.addEventListener('keydown', (e) => {
|
|
3614
|
+
if (e.key === 'Escape' && imageOverlay.classList.contains('visible')) {
|
|
3615
|
+
closeImageOverlay();
|
|
3616
|
+
}
|
|
3617
|
+
});
|
|
3618
|
+
|
|
3619
|
+
preview.querySelectorAll('img').forEach(img => {
|
|
3620
|
+
img.style.cursor = 'pointer';
|
|
3621
|
+
img.title = 'Click to view fullscreen';
|
|
3622
|
+
|
|
3623
|
+
img.addEventListener('click', (e) => {
|
|
3624
|
+
e.stopPropagation();
|
|
3625
|
+
|
|
3626
|
+
imageContainer.innerHTML = '';
|
|
3627
|
+
const clonedImg = img.cloneNode(true);
|
|
3628
|
+
clonedImg.style.maxWidth = '90vw';
|
|
3629
|
+
clonedImg.style.maxHeight = '90vh';
|
|
3630
|
+
clonedImg.style.objectFit = 'contain';
|
|
3631
|
+
clonedImg.style.cursor = 'default';
|
|
3632
|
+
imageContainer.appendChild(clonedImg);
|
|
3633
|
+
|
|
3634
|
+
imageOverlay.classList.add('visible');
|
|
3635
|
+
});
|
|
3636
|
+
});
|
|
3637
|
+
})();
|
|
3353
3638
|
</script>
|
|
3354
3639
|
</body>
|
|
3355
3640
|
</html>`;
|