reviw 0.10.1 → 0.10.3
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 +11 -6
- package/cli.cjs +105 -18
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@ A lightweight browser-based tool for reviewing and annotating tabular data, text
|
|
|
6
6
|
|
|
7
7
|
### File Format Support
|
|
8
8
|
- **CSV/TSV**: View tabular data with sticky headers, column freezing, filtering, and column resizing
|
|
9
|
-
- **Markdown**: Side-by-side preview with synchronized scrolling
|
|
9
|
+
- **Markdown**: Side-by-side preview with synchronized scrolling, click-to-comment from preview
|
|
10
10
|
- **Diff/Patch**: GitHub-style diff view with syntax highlighting, collapsible large files (500+ lines), and binary files sorted to end
|
|
11
11
|
- **Text**: Line-by-line commenting for plain text files
|
|
12
12
|
|
|
@@ -15,9 +15,12 @@ A lightweight browser-based tool for reviewing and annotating tabular data, text
|
|
|
15
15
|
- Click any diagram to open fullscreen viewer
|
|
16
16
|
- Zoom with mouse wheel (centered on cursor position, up to 10x)
|
|
17
17
|
- Pan with mouse drag
|
|
18
|
-
- Minimap showing current viewport position
|
|
19
18
|
- Syntax error display in toast notifications
|
|
20
19
|
|
|
20
|
+
### Media Fullscreen
|
|
21
|
+
- Click images in Markdown preview to open fullscreen viewer
|
|
22
|
+
- Click videos to open fullscreen playback with native controls
|
|
23
|
+
|
|
21
24
|
### UI Features
|
|
22
25
|
- **Theme toggle**: Switch between light and dark modes
|
|
23
26
|
- **Multi-file support**: Open multiple files simultaneously on separate ports
|
|
@@ -62,6 +65,8 @@ reviw changes.diff
|
|
|
62
65
|
- `--port <number>`: Specify starting port (default: 4989)
|
|
63
66
|
- `--encoding <encoding>`: Force specific encoding (auto-detected by default)
|
|
64
67
|
- `--no-open`: Prevent automatic browser opening
|
|
68
|
+
- `--help, -h`: Show help message
|
|
69
|
+
- `--version, -v`: Show version number
|
|
65
70
|
|
|
66
71
|
### Workflow
|
|
67
72
|
1. Browser opens automatically (macOS: `open` / Linux: `xdg-open` / Windows: `start`)
|
|
@@ -72,16 +77,16 @@ reviw changes.diff
|
|
|
72
77
|
## Screenshots
|
|
73
78
|
|
|
74
79
|
### CSV View
|
|
75
|
-

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

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

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

|
|
85
90
|
|
|
86
91
|
## Output Example
|
|
87
92
|
|
package/cli.cjs
CHANGED
|
@@ -21,6 +21,7 @@ const marked = require("marked");
|
|
|
21
21
|
const yaml = require("js-yaml");
|
|
22
22
|
|
|
23
23
|
// --- CLI arguments ---------------------------------------------------------
|
|
24
|
+
const VERSION = "0.10.3";
|
|
24
25
|
const args = process.argv.slice(2);
|
|
25
26
|
|
|
26
27
|
const filePaths = [];
|
|
@@ -31,6 +32,49 @@ let stdinMode = false;
|
|
|
31
32
|
let diffMode = false;
|
|
32
33
|
let stdinContent = null;
|
|
33
34
|
|
|
35
|
+
function showHelp() {
|
|
36
|
+
console.log(`reviw v${VERSION} - Lightweight file reviewer with in-browser comments
|
|
37
|
+
|
|
38
|
+
Usage:
|
|
39
|
+
reviw <file...> [options] Review files (CSV, TSV, Markdown, Text)
|
|
40
|
+
reviw <file.diff> Review diff/patch file
|
|
41
|
+
git diff | reviw [options] Review diff from stdin
|
|
42
|
+
reviw Auto run git diff HEAD
|
|
43
|
+
|
|
44
|
+
Supported Formats:
|
|
45
|
+
CSV/TSV Tabular data with sticky headers, filtering, column resizing
|
|
46
|
+
Markdown Side-by-side preview with synchronized scrolling, Mermaid diagrams
|
|
47
|
+
Diff/Patch GitHub-style view with syntax highlighting
|
|
48
|
+
Text Line-by-line commenting
|
|
49
|
+
|
|
50
|
+
Options:
|
|
51
|
+
--port <number> Server port (default: 4989)
|
|
52
|
+
--encoding <enc>, -e Force encoding (utf8, shift_jis, euc-jp, etc.)
|
|
53
|
+
--no-open Don't open browser automatically
|
|
54
|
+
--help, -h Show this help message
|
|
55
|
+
--version, -v Show version number
|
|
56
|
+
|
|
57
|
+
Examples:
|
|
58
|
+
reviw data.csv # Review CSV file
|
|
59
|
+
reviw README.md # Review Markdown with preview
|
|
60
|
+
reviw file1.csv file2.md # Multiple files on consecutive ports
|
|
61
|
+
git diff | reviw # Review uncommitted changes
|
|
62
|
+
git diff HEAD~3 | reviw # Review last 3 commits
|
|
63
|
+
reviw changes.patch # Review patch file
|
|
64
|
+
|
|
65
|
+
Workflow:
|
|
66
|
+
1. Browser opens automatically (use --no-open to disable)
|
|
67
|
+
2. Click cells/lines to add comments, drag to select multiple
|
|
68
|
+
3. Press Cmd/Ctrl+Enter or click "Submit & Exit"
|
|
69
|
+
4. Comments are output as YAML to stdout
|
|
70
|
+
|
|
71
|
+
More info: https://github.com/kazuph/reviw`);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function showVersion() {
|
|
75
|
+
console.log(VERSION);
|
|
76
|
+
}
|
|
77
|
+
|
|
34
78
|
for (let i = 0; i < args.length; i += 1) {
|
|
35
79
|
const arg = args[i];
|
|
36
80
|
if (arg === "--port" && args[i + 1]) {
|
|
@@ -42,22 +86,10 @@ for (let i = 0; i < args.length; i += 1) {
|
|
|
42
86
|
} else if (arg === "--no-open") {
|
|
43
87
|
noOpen = true;
|
|
44
88
|
} else if (arg === "--help" || arg === "-h") {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
Options:
|
|
50
|
-
--port <number> Server port (default: 4989)
|
|
51
|
-
--encoding <enc> Force encoding (utf8, shift_jis, etc.)
|
|
52
|
-
--no-open Don't open browser automatically
|
|
53
|
-
--help, -h Show this help message
|
|
54
|
-
|
|
55
|
-
Examples:
|
|
56
|
-
reviw data.csv # View CSV file
|
|
57
|
-
reviw README.md # View Markdown file
|
|
58
|
-
git diff | reviw # Review diff from stdin
|
|
59
|
-
git diff HEAD~3 | reviw # Review diff from last 3 commits
|
|
60
|
-
reviw # Auto run git diff HEAD`);
|
|
89
|
+
showHelp();
|
|
90
|
+
process.exit(0);
|
|
91
|
+
} else if (arg === "--version" || arg === "-v") {
|
|
92
|
+
showVersion();
|
|
61
93
|
process.exit(0);
|
|
62
94
|
} else if (!arg.startsWith("-")) {
|
|
63
95
|
filePaths.push(arg);
|
|
@@ -116,7 +148,7 @@ function runGitDiff() {
|
|
|
116
148
|
});
|
|
117
149
|
}
|
|
118
150
|
|
|
119
|
-
// Validate all files exist (if files specified)
|
|
151
|
+
// Validate all files exist and are not directories (if files specified)
|
|
120
152
|
const resolvedPaths = [];
|
|
121
153
|
for (const fp of filePaths) {
|
|
122
154
|
const resolved = path.resolve(fp);
|
|
@@ -124,6 +156,13 @@ for (const fp of filePaths) {
|
|
|
124
156
|
console.error(`File not found: ${resolved}`);
|
|
125
157
|
process.exit(1);
|
|
126
158
|
}
|
|
159
|
+
const stat = fs.statSync(resolved);
|
|
160
|
+
if (stat.isDirectory()) {
|
|
161
|
+
console.error(`Cannot open directory: ${resolved}`);
|
|
162
|
+
console.error(`Usage: reviw <file> [file2...]`);
|
|
163
|
+
console.error(`Please specify a file, not a directory.`);
|
|
164
|
+
process.exit(1);
|
|
165
|
+
}
|
|
127
166
|
resolvedPaths.push(resolved);
|
|
128
167
|
}
|
|
129
168
|
|
|
@@ -477,6 +516,19 @@ function escapeHtmlChars(str) {
|
|
|
477
516
|
}
|
|
478
517
|
|
|
479
518
|
function loadData(filePath) {
|
|
519
|
+
// Check if path exists
|
|
520
|
+
if (!fs.existsSync(filePath)) {
|
|
521
|
+
throw new Error(`File not found: ${filePath}`);
|
|
522
|
+
}
|
|
523
|
+
// Check if path is a directory
|
|
524
|
+
const stat = fs.statSync(filePath);
|
|
525
|
+
if (stat.isDirectory()) {
|
|
526
|
+
throw new Error(
|
|
527
|
+
`Cannot open directory: ${filePath}\n` +
|
|
528
|
+
`Usage: reviw <file> [file2...]\n` +
|
|
529
|
+
`Please specify a file, not a directory.`
|
|
530
|
+
);
|
|
531
|
+
}
|
|
480
532
|
const ext = path.extname(filePath).toLowerCase();
|
|
481
533
|
if (ext === ".csv" || ext === ".tsv") {
|
|
482
534
|
const data = loadCsv(filePath);
|
|
@@ -3827,6 +3879,20 @@ function htmlTemplate(dataRows, cols, title, mode, previewHtml) {
|
|
|
3827
3879
|
\`;
|
|
3828
3880
|
document.head.appendChild(style);
|
|
3829
3881
|
|
|
3882
|
+
// Helper: strip markdown formatting from text
|
|
3883
|
+
function stripMarkdown(text) {
|
|
3884
|
+
return text
|
|
3885
|
+
.replace(/^[-*+]\\s+/, '') // List markers: - * +
|
|
3886
|
+
.replace(/^\\d+\\.\\s+/, '') // Numbered list: 1. 2.
|
|
3887
|
+
.replace(/\\*\\*([^*]+)\\*\\*/g, '$1') // Bold: **text**
|
|
3888
|
+
.replace(/\\*([^*]+)\\*/g, '$1') // Italic: *text*
|
|
3889
|
+
.replace(/__([^_]+)__/g, '$1') // Bold: __text__
|
|
3890
|
+
.replace(/_([^_]+)_/g, '$1') // Italic: _text_
|
|
3891
|
+
.replace(/\`([^\`]+)\`/g, '$1') // Inline code: \`code\`
|
|
3892
|
+
.replace(/\\[([^\\]]+)\\]\\([^)]+\\)/g, '$1') // Links: [text](url)
|
|
3893
|
+
.trim();
|
|
3894
|
+
}
|
|
3895
|
+
|
|
3830
3896
|
// Helper: find matching source line for text
|
|
3831
3897
|
function findSourceLine(text) {
|
|
3832
3898
|
if (!text) return -1;
|
|
@@ -3849,6 +3915,14 @@ function htmlTemplate(dataRows, cols, title, mode, previewHtml) {
|
|
|
3849
3915
|
return i + 1;
|
|
3850
3916
|
}
|
|
3851
3917
|
}
|
|
3918
|
+
|
|
3919
|
+
// Check for markdown list items: strip list markers and formatting
|
|
3920
|
+
if (lineText.match(/^[-*+]\\s|^\\d+\\.\\s/)) {
|
|
3921
|
+
const strippedLine = stripMarkdown(lineText).replace(/\\s+/g, ' ').slice(0, 100);
|
|
3922
|
+
if (strippedLine === normalized) return i + 1;
|
|
3923
|
+
if (strippedLine.includes(normalized.slice(0, 30)) && normalized.length > 5) return i + 1;
|
|
3924
|
+
if (normalized.includes(strippedLine.slice(0, 30)) && strippedLine.length > 5) return i + 1;
|
|
3925
|
+
}
|
|
3852
3926
|
}
|
|
3853
3927
|
return -1;
|
|
3854
3928
|
}
|
|
@@ -4286,6 +4360,10 @@ function createFileServer(filePath) {
|
|
|
4286
4360
|
|
|
4287
4361
|
function tryListen(attemptPort, attempts = 0) {
|
|
4288
4362
|
if (serverStarted) return; // Prevent double-start race condition
|
|
4363
|
+
// Clear previous listeners from failed attempts to avoid duplicate
|
|
4364
|
+
// "listening" callbacks firing after a later successful bind.
|
|
4365
|
+
ctx.server.removeAllListeners("error");
|
|
4366
|
+
ctx.server.removeAllListeners("listening");
|
|
4289
4367
|
if (attempts >= MAX_PORT_ATTEMPTS) {
|
|
4290
4368
|
console.error(
|
|
4291
4369
|
`Could not find an available port for ${baseName} after ${MAX_PORT_ATTEMPTS} attempts.`,
|
|
@@ -4450,6 +4528,10 @@ function createDiffServer(diffContent) {
|
|
|
4450
4528
|
|
|
4451
4529
|
function tryListen(attemptPort, attempts = 0) {
|
|
4452
4530
|
if (serverStarted) return; // Prevent double-start race condition
|
|
4531
|
+
// Clear listeners from previous failed attempts to prevent stale
|
|
4532
|
+
// "listening" handlers from firing on the successful bind.
|
|
4533
|
+
ctx.server.removeAllListeners("error");
|
|
4534
|
+
ctx.server.removeAllListeners("listening");
|
|
4453
4535
|
if (attempts >= MAX_PORT_ATTEMPTS) {
|
|
4454
4536
|
console.error(
|
|
4455
4537
|
`Could not find an available port for diff viewer after ${MAX_PORT_ATTEMPTS} attempts.`,
|
|
@@ -4543,6 +4625,7 @@ function createDiffServer(diffContent) {
|
|
|
4543
4625
|
console.log("Close all browser tabs or Submit & Exit to finish.");
|
|
4544
4626
|
} else {
|
|
4545
4627
|
// No files and no stdin: try auto git diff
|
|
4628
|
+
console.log(`reviw v${VERSION}`);
|
|
4546
4629
|
console.log("No files specified. Running git diff HEAD...");
|
|
4547
4630
|
try {
|
|
4548
4631
|
const gitDiff = await runGitDiff();
|
|
@@ -4551,7 +4634,9 @@ function createDiffServer(diffContent) {
|
|
|
4551
4634
|
console.log("");
|
|
4552
4635
|
console.log("Usage: reviw <file...> [options]");
|
|
4553
4636
|
console.log(" git diff | reviw [options]");
|
|
4554
|
-
console.log(" reviw
|
|
4637
|
+
console.log(" reviw (auto runs git diff HEAD)");
|
|
4638
|
+
console.log("");
|
|
4639
|
+
console.log("Run 'reviw --help' for more information.");
|
|
4555
4640
|
process.exit(0);
|
|
4556
4641
|
}
|
|
4557
4642
|
diffMode = true;
|
|
@@ -4565,6 +4650,8 @@ function createDiffServer(diffContent) {
|
|
|
4565
4650
|
console.log("");
|
|
4566
4651
|
console.log("Usage: reviw <file...> [options]");
|
|
4567
4652
|
console.log(" git diff | reviw [options]");
|
|
4653
|
+
console.log("");
|
|
4654
|
+
console.log("Run 'reviw --help' for more information.");
|
|
4568
4655
|
process.exit(1);
|
|
4569
4656
|
}
|
|
4570
4657
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "reviw",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.3",
|
|
4
4
|
"description": "Lightweight file reviewer with in-browser comments for CSV, TSV, Markdown, and Git diffs.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -9,6 +9,10 @@
|
|
|
9
9
|
"files": [
|
|
10
10
|
"cli.cjs"
|
|
11
11
|
],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"test": "vitest run",
|
|
14
|
+
"test:watch": "vitest"
|
|
15
|
+
},
|
|
12
16
|
"license": "MIT",
|
|
13
17
|
"author": "kazuph",
|
|
14
18
|
"publishConfig": {
|
|
@@ -27,9 +31,5 @@
|
|
|
27
31
|
"@playwright/test": "^1.57.0",
|
|
28
32
|
"playwright": "^1.57.0",
|
|
29
33
|
"vitest": "^4.0.14"
|
|
30
|
-
},
|
|
31
|
-
"scripts": {
|
|
32
|
-
"test": "vitest run",
|
|
33
|
-
"test:watch": "vitest"
|
|
34
34
|
}
|
|
35
|
-
}
|
|
35
|
+
}
|