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.
Files changed (3) hide show
  1. package/README.md +11 -6
  2. package/cli.cjs +105 -18
  3. 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
- ![CSV View](./assets/screenshot-csv.png?v=2)
80
+ ![CSV View](https://raw.githubusercontent.com/kazuph/reviw/main/assets/screenshot-csv.png)
76
81
 
77
82
  ### Markdown View
78
- ![Markdown View](./assets/screenshot-md.png?v=2)
83
+ ![Markdown View](https://raw.githubusercontent.com/kazuph/reviw/main/assets/screenshot-md.png)
79
84
 
80
85
  ### Diff View
81
- ![Diff View](./assets/screenshot-diff.png?v=2)
86
+ ![Diff View](https://raw.githubusercontent.com/kazuph/reviw/main/assets/screenshot-diff.png)
82
87
 
83
88
  ### Mermaid Fullscreen
84
- ![Mermaid Fullscreen](./assets/screenshot-mermaid.png?v=2)
89
+ ![Mermaid Fullscreen](https://raw.githubusercontent.com/kazuph/reviw/main/assets/screenshot-mermaid.png)
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
- console.log(`Usage: reviw <file...> [options]
46
- git diff | reviw [options]
47
- reviw [options] (auto runs git diff HEAD)
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 (auto runs git diff HEAD)");
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.1",
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
+ }