reviw 0.6.0 → 0.7.1

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.
Files changed (3) hide show
  1. package/README.md +47 -13
  2. package/cli.cjs +418 -0
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -1,16 +1,34 @@
1
1
  # reviw
2
2
 
3
- A lightweight browser-based tool for reviewing and annotating tabular data, text, and Markdown files. Supports CSV, TSV, plain text, and Markdown. Comments are output as YAML to stdout.
3
+ A lightweight browser-based tool for reviewing and annotating tabular data, text, Markdown, and diff files. Supports CSV, TSV, plain text, Markdown, and unified diff formats. Comments are output as YAML to stdout.
4
4
 
5
5
  ## Features
6
6
 
7
- - **CSV/TSV support**: View tabular data with column/row freezing and filtering
8
- - **Markdown support**: Side-by-side preview with synchronized scrolling
9
- - **Text support**: Line-by-line commenting for plain text files
7
+ ### File Format Support
8
+ - **CSV/TSV**: View tabular data with sticky headers, column freezing, filtering, and column resizing
9
+ - **Markdown**: Side-by-side preview with synchronized scrolling
10
+ - **Diff/Patch**: GitHub-style diff view with syntax highlighting, collapsible large files (500+ lines), and binary files sorted to end
11
+ - **Text**: Line-by-line commenting for plain text files
12
+
13
+ ### Mermaid.js Diagrams
14
+ - Auto-detect and render Mermaid diagrams in Markdown files
15
+ - Click any diagram to open fullscreen viewer
16
+ - Zoom with mouse wheel (centered on cursor position, up to 10x)
17
+ - Pan with mouse drag
18
+ - Minimap showing current viewport position
19
+ - Syntax error display in toast notifications
20
+
21
+ ### UI Features
22
+ - **Theme toggle**: Switch between light and dark modes
10
23
  - **Multi-file support**: Open multiple files simultaneously on separate ports
11
24
  - **Drag selection**: Select rectangular regions or multiple rows for batch comments
12
- - **Real-time**: Hot reload on file changes via SSE
13
- - **YAML output**: Comments exported with file, mode, row, col, value, and text
25
+ - **Real-time updates**: Hot reload on file changes via SSE
26
+ - **Comment persistence**: Auto-save comments to localStorage with recovery modal
27
+ - **Keyboard shortcuts**: Cmd/Ctrl+Enter to open submit modal
28
+
29
+ ### Output
30
+ - YAML format with file, mode, row, col, value, and comment text
31
+ - Overall summary field for review notes
14
32
 
15
33
  ## Installation
16
34
 
@@ -32,24 +50,39 @@ reviw <file> [--port 3000] [--encoding utf8|shift_jis|...]
32
50
 
33
51
  # Multiple files (each opens on consecutive ports)
34
52
  reviw file1.csv file2.md file3.tsv --port 3000
53
+
54
+ # Diff from stdin
55
+ git diff HEAD | reviw
56
+
57
+ # Diff file
58
+ reviw changes.diff
35
59
  ```
36
60
 
37
- - Browser opens automatically (macOS: `open` / Linux: `xdg-open` / Windows: `start`)
38
- - Click cells to add comments, or drag to select multiple cells/rows
39
- - Close the tab or click "Submit & Exit" to output comments as YAML and terminate the server
40
- - Encoding defaults to UTF-8; use `--encoding` to specify or let `chardet` auto-detect (Shift_JIS, CP932, etc.)
41
- - Use `--no-open` to prevent automatic browser opening
61
+ ### Options
62
+ - `--port <number>`: Specify starting port (default: 3000)
63
+ - `--encoding <encoding>`: Force specific encoding (auto-detected by default)
64
+ - `--no-open`: Prevent automatic browser opening
65
+
66
+ ### Workflow
67
+ 1. Browser opens automatically (macOS: `open` / Linux: `xdg-open` / Windows: `start`)
68
+ 2. Click cells/lines to add comments, or drag to select multiple
69
+ 3. Use Cmd/Ctrl+Enter or click "Submit & Exit" to output comments
70
+ 4. Comments are printed as YAML to stdout
42
71
 
43
72
  ## Screenshots
44
73
 
45
74
  ### CSV View
46
-
47
75
  ![CSV View](./assets/screenshot-csv.png)
48
76
 
49
77
  ### Markdown View
50
-
51
78
  ![Markdown View](./assets/screenshot-md.png)
52
79
 
80
+ ### Diff View
81
+ ![Diff View](./assets/screenshot-diff.png)
82
+
83
+ ### Mermaid Fullscreen
84
+ ![Mermaid Fullscreen](./assets/screenshot-mermaid.png)
85
+
53
86
  ## Output Example
54
87
 
55
88
  ```yaml
@@ -62,6 +95,7 @@ comments:
62
95
  col: 3
63
96
  text: This value needs review
64
97
  value: '150'
98
+ summary: Overall the data looks good, minor issues noted above.
65
99
  ```
66
100
 
67
101
  ## Development
package/cli.cjs CHANGED
@@ -1842,7 +1842,141 @@ function htmlTemplate(dataRows, cols, title, mode, previewHtml) {
1842
1842
  header { flex-direction: column; align-items: flex-start; }
1843
1843
  .comment-list { width: calc(100% - 24px); right: 12px; }
1844
1844
  }
1845
+ /* Mermaid diagram styles */
1846
+ .mermaid-container {
1847
+ position: relative;
1848
+ margin: 16px 0;
1849
+ background: var(--panel);
1850
+ border: 1px solid var(--border);
1851
+ border-radius: 8px;
1852
+ padding: 16px;
1853
+ overflow: hidden;
1854
+ }
1855
+ .mermaid-container .mermaid {
1856
+ display: flex;
1857
+ justify-content: center;
1858
+ }
1859
+ .mermaid-container .mermaid svg {
1860
+ max-width: 100%;
1861
+ height: auto;
1862
+ }
1863
+ .mermaid-fullscreen-btn {
1864
+ position: absolute;
1865
+ top: 8px;
1866
+ right: 8px;
1867
+ background: var(--selected-bg);
1868
+ border: 1px solid var(--border);
1869
+ border-radius: 6px;
1870
+ padding: 6px 10px;
1871
+ cursor: pointer;
1872
+ color: var(--text);
1873
+ font-size: 12px;
1874
+ z-index: 2;
1875
+ display: flex;
1876
+ align-items: center;
1877
+ gap: 4px;
1878
+ }
1879
+ .mermaid-fullscreen-btn:hover { background: var(--hover-bg); }
1880
+ /* Fullscreen overlay */
1881
+ .fullscreen-overlay {
1882
+ position: fixed;
1883
+ inset: 0;
1884
+ background: var(--bg);
1885
+ z-index: 1000;
1886
+ display: none;
1887
+ flex-direction: column;
1888
+ }
1889
+ .fullscreen-overlay.visible { display: flex; }
1890
+ .fullscreen-header {
1891
+ display: flex;
1892
+ justify-content: space-between;
1893
+ align-items: center;
1894
+ padding: 12px 20px;
1895
+ background: var(--panel-alpha);
1896
+ border-bottom: 1px solid var(--border);
1897
+ }
1898
+ .fullscreen-header h3 { margin: 0; font-size: 14px; }
1899
+ .fullscreen-controls { display: flex; gap: 8px; align-items: center; }
1900
+ .fullscreen-controls button {
1901
+ background: var(--selected-bg);
1902
+ border: 1px solid var(--border);
1903
+ border-radius: 6px;
1904
+ padding: 6px 12px;
1905
+ cursor: pointer;
1906
+ color: var(--text);
1907
+ font-size: 13px;
1908
+ }
1909
+ .fullscreen-controls button:hover { background: var(--hover-bg); }
1910
+ .fullscreen-controls .zoom-info { font-size: 12px; color: var(--muted); min-width: 50px; text-align: center; }
1911
+ .fullscreen-content {
1912
+ flex: 1;
1913
+ overflow: hidden;
1914
+ position: relative;
1915
+ cursor: grab;
1916
+ }
1917
+ .fullscreen-content:active { cursor: grabbing; }
1918
+ .fullscreen-content .mermaid-wrapper {
1919
+ position: absolute;
1920
+ transform-origin: 0 0;
1921
+ padding: 40px;
1922
+ }
1923
+ .fullscreen-content .mermaid svg {
1924
+ display: block;
1925
+ }
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
+ /* Error toast */
1960
+ .mermaid-error-toast {
1961
+ position: fixed;
1962
+ bottom: 20px;
1963
+ left: 50%;
1964
+ transform: translateX(-50%);
1965
+ background: #dc3545;
1966
+ color: white;
1967
+ padding: 12px 24px;
1968
+ border-radius: 8px;
1969
+ font-size: 13px;
1970
+ max-width: 80%;
1971
+ z-index: 2000;
1972
+ display: none;
1973
+ box-shadow: 0 4px 12px rgba(0,0,0,0.3);
1974
+ white-space: pre-wrap;
1975
+ font-family: monospace;
1976
+ }
1977
+ .mermaid-error-toast.visible { display: block; }
1845
1978
  </style>
1979
+ <script src="https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js"></script>
1846
1980
  </head>
1847
1981
  <body>
1848
1982
  <header>
@@ -1958,6 +2092,27 @@ function htmlTemplate(dataRows, cols, title, mode, previewHtml) {
1958
2092
  </div>
1959
2093
  </div>
1960
2094
 
2095
+ <div class="fullscreen-overlay" id="mermaid-fullscreen">
2096
+ <div class="fullscreen-header">
2097
+ <h3>Mermaid Diagram</h3>
2098
+ <div class="fullscreen-controls">
2099
+ <button id="fs-zoom-out">−</button>
2100
+ <span class="zoom-info" id="fs-zoom-info">100%</span>
2101
+ <button id="fs-zoom-in">+</button>
2102
+ <button id="fs-reset">Reset</button>
2103
+ <button id="fs-close">Close (ESC)</button>
2104
+ </div>
2105
+ </div>
2106
+ <div class="fullscreen-content" id="fs-content">
2107
+ <div class="mermaid-wrapper" id="fs-wrapper"></div>
2108
+ </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
+ </div>
2114
+ <div class="mermaid-error-toast" id="mermaid-error-toast"></div>
2115
+
1961
2116
  <script>
1962
2117
  const DATA = ${serialized};
1963
2118
  const MAX_COLS = ${cols};
@@ -2932,6 +3087,269 @@ function htmlTemplate(dataRows, cols, title, mode, previewHtml) {
2932
3087
  mdRight.addEventListener('scroll', () => syncScroll(mdRight, mdLeft, 'right'), { passive: true });
2933
3088
  }
2934
3089
  }
3090
+
3091
+ // --- Mermaid Initialization ---
3092
+ (function initMermaid() {
3093
+ if (typeof mermaid === 'undefined') return;
3094
+
3095
+ const errorToast = document.getElementById('mermaid-error-toast');
3096
+ let errorTimeout;
3097
+
3098
+ function showError(msg) {
3099
+ errorToast.textContent = msg;
3100
+ errorToast.classList.add('visible');
3101
+ console.error('[Mermaid Error]', msg);
3102
+ clearTimeout(errorTimeout);
3103
+ errorTimeout = setTimeout(() => errorToast.classList.remove('visible'), 8000);
3104
+ }
3105
+
3106
+ mermaid.initialize({
3107
+ startOnLoad: false,
3108
+ theme: document.documentElement.getAttribute('data-theme') === 'light' ? 'default' : 'dark',
3109
+ securityLevel: 'loose',
3110
+ logLevel: 'error'
3111
+ });
3112
+
3113
+ // Find all mermaid code blocks in preview
3114
+ const preview = document.querySelector('.md-preview');
3115
+ if (!preview) return;
3116
+
3117
+ const codeBlocks = preview.querySelectorAll('pre code.language-mermaid, pre code');
3118
+ codeBlocks.forEach((code, idx) => {
3119
+ const pre = code.parentElement;
3120
+ const text = code.textContent.trim();
3121
+
3122
+ // Check if it's mermaid content
3123
+ if (!code.classList.contains('language-mermaid') && !text.match(/^(graph|flowchart|sequenceDiagram|classDiagram|stateDiagram|erDiagram|journey|gantt|pie|gitGraph|mindmap|timeline)/)) {
3124
+ return;
3125
+ }
3126
+
3127
+ // Create container
3128
+ const container = document.createElement('div');
3129
+ container.className = 'mermaid-container';
3130
+ container.style.cursor = 'pointer';
3131
+ container.title = 'Click to view fullscreen';
3132
+
3133
+ const mermaidDiv = document.createElement('div');
3134
+ mermaidDiv.className = 'mermaid';
3135
+ mermaidDiv.id = 'mermaid-' + idx;
3136
+ mermaidDiv.textContent = text;
3137
+
3138
+ // Click anywhere on container to open fullscreen
3139
+ container.addEventListener('click', () => openFullscreen(mermaidDiv));
3140
+
3141
+ container.appendChild(mermaidDiv);
3142
+ pre.replaceWith(container);
3143
+ });
3144
+
3145
+ // Render all mermaid diagrams with error handling
3146
+ mermaid.run().catch(err => {
3147
+ showError('Mermaid Syntax Error: ' + (err.message || err));
3148
+ });
3149
+
3150
+ // Watch for render errors in DOM
3151
+ setTimeout(() => {
3152
+ document.querySelectorAll('.mermaid').forEach(el => {
3153
+ if (el.querySelector('.error-text, .error-icon')) {
3154
+ const errText = el.textContent;
3155
+ showError('Mermaid Parse Error: ' + errText.slice(0, 200));
3156
+ }
3157
+ });
3158
+ }, 500);
3159
+
3160
+ // Fullscreen functionality
3161
+ const fsOverlay = document.getElementById('mermaid-fullscreen');
3162
+ const fsWrapper = document.getElementById('fs-wrapper');
3163
+ const fsContent = document.getElementById('fs-content');
3164
+ const fsZoomInfo = document.getElementById('fs-zoom-info');
3165
+ const minimapContent = document.getElementById('fs-minimap-content');
3166
+ const minimapViewport = document.getElementById('fs-minimap-viewport');
3167
+ let currentZoom = 1;
3168
+ let initialZoom = 1;
3169
+ let panX = 0, panY = 0;
3170
+ let isPanning = false;
3171
+ let startX, startY;
3172
+ let svgNaturalWidth = 0, svgNaturalHeight = 0;
3173
+ let minimapScale = 1;
3174
+
3175
+ function openFullscreen(mermaidEl) {
3176
+ const svg = mermaidEl.querySelector('svg');
3177
+ if (!svg) return;
3178
+ fsWrapper.innerHTML = '';
3179
+ const clonedSvg = svg.cloneNode(true);
3180
+ fsWrapper.appendChild(clonedSvg);
3181
+
3182
+ // Setup minimap
3183
+ minimapContent.innerHTML = '';
3184
+ const minimapSvg = svg.cloneNode(true);
3185
+ minimapContent.appendChild(minimapSvg);
3186
+
3187
+ // Get SVG's intrinsic/natural size from viewBox or attributes
3188
+ const viewBox = svg.getAttribute('viewBox');
3189
+ let naturalWidth, naturalHeight;
3190
+
3191
+ if (viewBox) {
3192
+ const parts = viewBox.split(/[\\s,]+/);
3193
+ naturalWidth = parseFloat(parts[2]) || 800;
3194
+ naturalHeight = parseFloat(parts[3]) || 600;
3195
+ } else {
3196
+ naturalWidth = parseFloat(svg.getAttribute('width')) || svg.getBoundingClientRect().width || 800;
3197
+ naturalHeight = parseFloat(svg.getAttribute('height')) || svg.getBoundingClientRect().height || 600;
3198
+ }
3199
+
3200
+ svgNaturalWidth = naturalWidth;
3201
+ svgNaturalHeight = naturalHeight;
3202
+
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
+ clonedSvg.style.width = naturalWidth + 'px';
3209
+ clonedSvg.style.height = naturalHeight + 'px';
3210
+
3211
+ // Calculate fit-to-viewport zoom
3212
+ const viewportHeight = window.innerHeight - 80;
3213
+ const viewportWidth = window.innerWidth - 40;
3214
+
3215
+ const zoomForHeight = viewportHeight / naturalHeight;
3216
+ const zoomForWidth = viewportWidth / naturalWidth;
3217
+ const fitZoom = Math.min(zoomForHeight, zoomForWidth);
3218
+
3219
+ currentZoom = fitZoom;
3220
+ initialZoom = fitZoom;
3221
+
3222
+ // Center the SVG in viewport
3223
+ const scaledWidth = naturalWidth * currentZoom;
3224
+ const scaledHeight = naturalHeight * currentZoom;
3225
+ panX = (viewportWidth - scaledWidth) / 2 + 20;
3226
+ panY = (viewportHeight - scaledHeight) / 2 + 60;
3227
+
3228
+ updateTransform();
3229
+ fsOverlay.classList.add('visible');
3230
+ }
3231
+
3232
+ function closeFullscreen() {
3233
+ fsOverlay.classList.remove('visible');
3234
+ }
3235
+
3236
+ function updateTransform() {
3237
+ fsWrapper.style.transform = 'translate(' + panX + 'px, ' + panY + 'px) scale(' + currentZoom + ')';
3238
+ 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
+ }
3282
+
3283
+ // Use multiplicative zoom for consistent behavior
3284
+ function zoomAt(factor, clientX, clientY) {
3285
+ const oldZoom = currentZoom;
3286
+ currentZoom = Math.max(0.1, Math.min(10, currentZoom * factor));
3287
+
3288
+ // Zoom around mouse position
3289
+ const fsRect = fsContent.getBoundingClientRect();
3290
+ const mouseX = clientX - fsRect.left;
3291
+ const mouseY = clientY - fsRect.top;
3292
+
3293
+ const zoomRatio = currentZoom / oldZoom;
3294
+ panX = mouseX - (mouseX - panX) * zoomRatio;
3295
+ panY = mouseY - (mouseY - panY) * zoomRatio;
3296
+
3297
+ updateTransform();
3298
+ }
3299
+
3300
+ function zoom(factor) {
3301
+ const fsRect = fsContent.getBoundingClientRect();
3302
+ zoomAt(factor, fsRect.left + fsRect.width / 2, fsRect.top + fsRect.height / 2);
3303
+ }
3304
+
3305
+ document.getElementById('fs-zoom-in').addEventListener('click', () => zoom(1.25));
3306
+ document.getElementById('fs-zoom-out').addEventListener('click', () => zoom(0.8));
3307
+ document.getElementById('fs-reset').addEventListener('click', () => {
3308
+ currentZoom = initialZoom;
3309
+ const viewportHeight = window.innerHeight - 80;
3310
+ const viewportWidth = window.innerWidth - 40;
3311
+ const scaledWidth = svgNaturalWidth * currentZoom;
3312
+ const scaledHeight = svgNaturalHeight * currentZoom;
3313
+ panX = (viewportWidth - scaledWidth) / 2 + 20;
3314
+ panY = (viewportHeight - scaledHeight) / 2 + 60;
3315
+ updateTransform();
3316
+ });
3317
+ document.getElementById('fs-close').addEventListener('click', closeFullscreen);
3318
+
3319
+ // Pan with mouse drag
3320
+ fsContent.addEventListener('mousedown', (e) => {
3321
+ isPanning = true;
3322
+ startX = e.clientX - panX;
3323
+ startY = e.clientY - panY;
3324
+ fsContent.style.cursor = 'grabbing';
3325
+ });
3326
+
3327
+ document.addEventListener('mousemove', (e) => {
3328
+ if (!isPanning) return;
3329
+ panX = e.clientX - startX;
3330
+ panY = e.clientY - startY;
3331
+ updateTransform();
3332
+ });
3333
+
3334
+ document.addEventListener('mouseup', () => {
3335
+ isPanning = false;
3336
+ fsContent.style.cursor = 'grab';
3337
+ });
3338
+
3339
+ // Zoom with mouse wheel - use multiplicative factor
3340
+ fsContent.addEventListener('wheel', (e) => {
3341
+ e.preventDefault();
3342
+ const factor = e.deltaY > 0 ? 0.9 : 1.1;
3343
+ zoomAt(factor, e.clientX, e.clientY);
3344
+ }, { passive: false });
3345
+
3346
+ // ESC to close
3347
+ document.addEventListener('keydown', (e) => {
3348
+ if (e.key === 'Escape' && fsOverlay.classList.contains('visible')) {
3349
+ closeFullscreen();
3350
+ }
3351
+ });
3352
+ })();
2935
3353
  </script>
2936
3354
  </body>
2937
3355
  </html>`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "reviw",
3
- "version": "0.6.0",
3
+ "version": "0.7.1",
4
4
  "description": "Lightweight file reviewer with in-browser comments for CSV, TSV, Markdown, and Git diffs.",
5
5
  "type": "module",
6
6
  "bin": {