diffstory 0.2.0__tar.gz → 0.2.1__tar.gz

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.
@@ -0,0 +1,242 @@
1
+ Metadata-Version: 2.4
2
+ Name: diffstory
3
+ Version: 0.2.1
4
+ Summary: Transform Git diffs into rich, interactive, self-contained HTML reports
5
+ License: MIT
6
+ Project-URL: Homepage, https://github.com/lakshayjindal/diffstory
7
+ Classifier: Development Status :: 3 - Alpha
8
+ Classifier: Environment :: Console
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.10
13
+ Classifier: Topic :: Software Development :: Version Control :: Git
14
+ Requires-Python: >=3.10
15
+ Description-Content-Type: text/markdown
16
+ Requires-Dist: Pygments>=2.10
17
+
18
+ # DiffStory
19
+
20
+ **Transform Git diffs into rich, interactive, self-contained HTML reports.**
21
+
22
+ DiffStory turns any `git diff` into a beautiful, portable HTML report that answers not just *what* changed, but *who* changed it, *when*, and *why* — all offline, in a single file you can share, archive, or email.
23
+
24
+ ```bash
25
+ pip install diffstory
26
+ cd my-repo
27
+ diffstory --staged -o review.html
28
+ # Open review.html in any browser — zero setup required
29
+ ```
30
+
31
+ ---
32
+
33
+ ## Features
34
+
35
+ ### Three Diff Views
36
+
37
+ | View | Description |
38
+ |---|---|
39
+ | **Unified** | Classic git-style diff with line numbers and syntax highlighting |
40
+ | **Side-by-Side** | Original (left) and modified (right) columns, visually aligned |
41
+ | **Inline Edit** | Word-level diff — shows exact token changes inline, green additions and red strikethrough removals. No more mental diffing. |
42
+
43
+ Switch between views instantly with the toolbar or keyboard shortcuts — no regeneration needed.
44
+
45
+ ### Blame & Provenance
46
+
47
+ Every changed line carries its history. **Hover** any line to see a tooltip with author name, commit hash, subject, date, and relative time (e.g. "2h ago"). **Click** any line to open the commit drawer — a side panel with full metadata: commit body, author, committer, parents, files changed, insertions, and deletions.
48
+
49
+ ### Search & Filtering
50
+
51
+ - **Global search** — find files by name, author, commit subject, or code content with live results
52
+ - **Filter chips** — narrow the view by file extension (`.py`, `.js`, `.html`, etc.) or change type (added, deleted, modified, renamed)
53
+ - **File sidebar** — searchable file list with add/delete indicators and collapse/expand
54
+
55
+ ### Statistics Dashboard
56
+
57
+ A floating panel showing:
58
+ - Files changed, additions, deletions
59
+ - Added / deleted / modified / renamed file counts
60
+ - Author count and commit count (from blame)
61
+ - Contributor breakdown with commit counts
62
+ - Top 10 most-changed files
63
+
64
+ ### Keyboard Navigation
65
+
66
+ | Key | Action |
67
+ |---|---|
68
+ | `J` / `K` | Next / previous file |
69
+ | `F` or `/` | Focus global search |
70
+ | `U` / `S` / `I` | Unified / Side-by-side / Inline view |
71
+ | `D` | Toggle light/dark theme |
72
+ | `Esc` | Close drawer → close search → close stats |
73
+
74
+ ### Deep Linking
75
+
76
+ Link directly to specific files and lines: `#file-0` scrolls to the first file, `#L-0-42` scrolls to line 42 in the first file. Shareable, stable anchors.
77
+
78
+ ### Binary File Support
79
+
80
+ Binary files are detected and displayed with meaningful placeholders — image files get a preview icon, other binaries show metadata — preventing crashes and keeping the report clean.
81
+
82
+ ### Customization
83
+
84
+ - **Light/Dark themes** — toggle instantly, persists across sessions
85
+ - **Config file** — `~/.diffstory.toml` or `.diffstory.toml` in your project sets defaults for verbose mode, debug output, and more
86
+ - **`--verbose` / `--debug` flags** — see what's happening under the hood
87
+
88
+ ### Export Formats
89
+
90
+ Alongside the HTML report, export structured data:
91
+
92
+ ```bash
93
+ diffstory --staged --json --md --csv
94
+ ```
95
+
96
+ ---
97
+
98
+ ## Installation
99
+
100
+ ```bash
101
+ pip install diffstory
102
+ ```
103
+
104
+ **Requires:** Python 3.10+ and Git.
105
+
106
+ To install from source:
107
+
108
+ ```bash
109
+ git clone https://github.com/lakshayjindal/diffstory.git
110
+ cd diffstory
111
+ pip install -e .
112
+ ```
113
+
114
+ ---
115
+
116
+ ## Usage
117
+
118
+ ```bash
119
+ # Working tree diff
120
+ diffstory
121
+
122
+ # Staged changes (what will be committed)
123
+ diffstory --staged
124
+
125
+ # Compare commits
126
+ diffstory HEAD~3 HEAD
127
+
128
+ # Compare branches
129
+ diffstory main feature-branch
130
+
131
+ # Compare revisions with path restriction
132
+ diffstory HEAD~3 HEAD src/
133
+
134
+ # Generate from a diff file directly (no git repo needed)
135
+ diffstory --diff /path/to/patch.diff
136
+
137
+ # Custom output file
138
+ diffstory -o my-review.html
139
+
140
+ # Multiple export formats at once
141
+ diffstory --staged --json --md --csv -o release-v2.0
142
+
143
+ # Verbose mode
144
+ diffstory --staged --verbose
145
+
146
+ # Show version
147
+ diffstory --version
148
+ ```
149
+
150
+ ### Config File Example
151
+
152
+ Create `~/.diffstory.toml` or `.diffstory.toml` in your project:
153
+
154
+ ```toml
155
+ [cli]
156
+ verbose = true
157
+ debug = false
158
+ ```
159
+
160
+ ---
161
+
162
+ ## HTML Report
163
+
164
+ Every generated HTML report is **fully self-contained**:
165
+
166
+ - All CSS inlined — no external stylesheets
167
+ - All JavaScript inlined — no external scripts
168
+ - All data (blame, commits, search index) embedded as JSON
169
+ - Works offline in any modern browser
170
+ - Safe to email, archive, or include in audit evidence
171
+ - Zero external dependencies at runtime
172
+
173
+ Open it, share it, attach it to a PR, or file it for compliance. It just works.
174
+
175
+ ---
176
+
177
+ ## Project Structure
178
+
179
+ ```
180
+ diffstory/
181
+ ├── pyproject.toml # Build config & CLI entry point
182
+ ├── README.md
183
+ ├── requirements.md # Full product requirements & spec
184
+ ├── deploy.sh # Build & publish script
185
+ ├── .github/workflows/publish.yml # CI/CD for PyPI publishing
186
+ ├── src/diffstory/
187
+ │ ├── __init__.py # Package version
188
+ │ ├── __main__.py # python -m diffstory support
189
+ │ ├── cli.py # CLI argument parsing & orchestration
190
+ │ ├── git_utils.py # Git subprocess wrappers (diff, blame, log)
191
+ │ ├── diff_parser.py # Unified diff parser → structured data
192
+ │ ├── syntax.py # Pygments syntax highlighting
193
+ │ └── html_generator.py # Self-contained HTML report generation
194
+ └── tests/
195
+ └── __init__.py
196
+ ```
197
+
198
+ ---
199
+
200
+ ## Development
201
+
202
+ ```bash
203
+ # Install in editable mode
204
+ pip install -e .
205
+
206
+ # Test with a quick repo
207
+ cd /tmp && mkdir test-diffstory && cd test-diffstory
208
+ git init && echo "hello" > test.py && git add -A && git commit -m "init"
209
+ echo "world" >> test.py
210
+ diffstory --staged
211
+
212
+ # Build the package
213
+ python -m build
214
+
215
+ # Deploy (version bump, build, publish)
216
+ ./deploy.sh # patch bump
217
+ ./deploy.sh minor # minor bump
218
+ ```
219
+
220
+ ---
221
+
222
+ ## Design Philosophy
223
+
224
+ DiffStory was built to answer five questions about every changed line:
225
+
226
+ > **What changed? Who changed it? When? Why? How did it evolve?**
227
+
228
+ It consolidates `git diff`, `git blame`, `git log`, and GitHub-style review UX into a single, portable artifact — no server, no accounts, no data leaving your machine.
229
+
230
+ ### Security
231
+
232
+ - Never uploads code or transmits data
233
+ - No telemetry, no analytics, no accounts
234
+ - No external API calls by default
235
+ - Never modifies your repository
236
+ - Generated reports are safe for air-gapped environments, client deliverables, and compliance audits
237
+
238
+ ---
239
+
240
+ ## License
241
+
242
+ MIT
@@ -0,0 +1,225 @@
1
+ # DiffStory
2
+
3
+ **Transform Git diffs into rich, interactive, self-contained HTML reports.**
4
+
5
+ DiffStory turns any `git diff` into a beautiful, portable HTML report that answers not just *what* changed, but *who* changed it, *when*, and *why* — all offline, in a single file you can share, archive, or email.
6
+
7
+ ```bash
8
+ pip install diffstory
9
+ cd my-repo
10
+ diffstory --staged -o review.html
11
+ # Open review.html in any browser — zero setup required
12
+ ```
13
+
14
+ ---
15
+
16
+ ## Features
17
+
18
+ ### Three Diff Views
19
+
20
+ | View | Description |
21
+ |---|---|
22
+ | **Unified** | Classic git-style diff with line numbers and syntax highlighting |
23
+ | **Side-by-Side** | Original (left) and modified (right) columns, visually aligned |
24
+ | **Inline Edit** | Word-level diff — shows exact token changes inline, green additions and red strikethrough removals. No more mental diffing. |
25
+
26
+ Switch between views instantly with the toolbar or keyboard shortcuts — no regeneration needed.
27
+
28
+ ### Blame & Provenance
29
+
30
+ Every changed line carries its history. **Hover** any line to see a tooltip with author name, commit hash, subject, date, and relative time (e.g. "2h ago"). **Click** any line to open the commit drawer — a side panel with full metadata: commit body, author, committer, parents, files changed, insertions, and deletions.
31
+
32
+ ### Search & Filtering
33
+
34
+ - **Global search** — find files by name, author, commit subject, or code content with live results
35
+ - **Filter chips** — narrow the view by file extension (`.py`, `.js`, `.html`, etc.) or change type (added, deleted, modified, renamed)
36
+ - **File sidebar** — searchable file list with add/delete indicators and collapse/expand
37
+
38
+ ### Statistics Dashboard
39
+
40
+ A floating panel showing:
41
+ - Files changed, additions, deletions
42
+ - Added / deleted / modified / renamed file counts
43
+ - Author count and commit count (from blame)
44
+ - Contributor breakdown with commit counts
45
+ - Top 10 most-changed files
46
+
47
+ ### Keyboard Navigation
48
+
49
+ | Key | Action |
50
+ |---|---|
51
+ | `J` / `K` | Next / previous file |
52
+ | `F` or `/` | Focus global search |
53
+ | `U` / `S` / `I` | Unified / Side-by-side / Inline view |
54
+ | `D` | Toggle light/dark theme |
55
+ | `Esc` | Close drawer → close search → close stats |
56
+
57
+ ### Deep Linking
58
+
59
+ Link directly to specific files and lines: `#file-0` scrolls to the first file, `#L-0-42` scrolls to line 42 in the first file. Shareable, stable anchors.
60
+
61
+ ### Binary File Support
62
+
63
+ Binary files are detected and displayed with meaningful placeholders — image files get a preview icon, other binaries show metadata — preventing crashes and keeping the report clean.
64
+
65
+ ### Customization
66
+
67
+ - **Light/Dark themes** — toggle instantly, persists across sessions
68
+ - **Config file** — `~/.diffstory.toml` or `.diffstory.toml` in your project sets defaults for verbose mode, debug output, and more
69
+ - **`--verbose` / `--debug` flags** — see what's happening under the hood
70
+
71
+ ### Export Formats
72
+
73
+ Alongside the HTML report, export structured data:
74
+
75
+ ```bash
76
+ diffstory --staged --json --md --csv
77
+ ```
78
+
79
+ ---
80
+
81
+ ## Installation
82
+
83
+ ```bash
84
+ pip install diffstory
85
+ ```
86
+
87
+ **Requires:** Python 3.10+ and Git.
88
+
89
+ To install from source:
90
+
91
+ ```bash
92
+ git clone https://github.com/lakshayjindal/diffstory.git
93
+ cd diffstory
94
+ pip install -e .
95
+ ```
96
+
97
+ ---
98
+
99
+ ## Usage
100
+
101
+ ```bash
102
+ # Working tree diff
103
+ diffstory
104
+
105
+ # Staged changes (what will be committed)
106
+ diffstory --staged
107
+
108
+ # Compare commits
109
+ diffstory HEAD~3 HEAD
110
+
111
+ # Compare branches
112
+ diffstory main feature-branch
113
+
114
+ # Compare revisions with path restriction
115
+ diffstory HEAD~3 HEAD src/
116
+
117
+ # Generate from a diff file directly (no git repo needed)
118
+ diffstory --diff /path/to/patch.diff
119
+
120
+ # Custom output file
121
+ diffstory -o my-review.html
122
+
123
+ # Multiple export formats at once
124
+ diffstory --staged --json --md --csv -o release-v2.0
125
+
126
+ # Verbose mode
127
+ diffstory --staged --verbose
128
+
129
+ # Show version
130
+ diffstory --version
131
+ ```
132
+
133
+ ### Config File Example
134
+
135
+ Create `~/.diffstory.toml` or `.diffstory.toml` in your project:
136
+
137
+ ```toml
138
+ [cli]
139
+ verbose = true
140
+ debug = false
141
+ ```
142
+
143
+ ---
144
+
145
+ ## HTML Report
146
+
147
+ Every generated HTML report is **fully self-contained**:
148
+
149
+ - All CSS inlined — no external stylesheets
150
+ - All JavaScript inlined — no external scripts
151
+ - All data (blame, commits, search index) embedded as JSON
152
+ - Works offline in any modern browser
153
+ - Safe to email, archive, or include in audit evidence
154
+ - Zero external dependencies at runtime
155
+
156
+ Open it, share it, attach it to a PR, or file it for compliance. It just works.
157
+
158
+ ---
159
+
160
+ ## Project Structure
161
+
162
+ ```
163
+ diffstory/
164
+ ├── pyproject.toml # Build config & CLI entry point
165
+ ├── README.md
166
+ ├── requirements.md # Full product requirements & spec
167
+ ├── deploy.sh # Build & publish script
168
+ ├── .github/workflows/publish.yml # CI/CD for PyPI publishing
169
+ ├── src/diffstory/
170
+ │ ├── __init__.py # Package version
171
+ │ ├── __main__.py # python -m diffstory support
172
+ │ ├── cli.py # CLI argument parsing & orchestration
173
+ │ ├── git_utils.py # Git subprocess wrappers (diff, blame, log)
174
+ │ ├── diff_parser.py # Unified diff parser → structured data
175
+ │ ├── syntax.py # Pygments syntax highlighting
176
+ │ └── html_generator.py # Self-contained HTML report generation
177
+ └── tests/
178
+ └── __init__.py
179
+ ```
180
+
181
+ ---
182
+
183
+ ## Development
184
+
185
+ ```bash
186
+ # Install in editable mode
187
+ pip install -e .
188
+
189
+ # Test with a quick repo
190
+ cd /tmp && mkdir test-diffstory && cd test-diffstory
191
+ git init && echo "hello" > test.py && git add -A && git commit -m "init"
192
+ echo "world" >> test.py
193
+ diffstory --staged
194
+
195
+ # Build the package
196
+ python -m build
197
+
198
+ # Deploy (version bump, build, publish)
199
+ ./deploy.sh # patch bump
200
+ ./deploy.sh minor # minor bump
201
+ ```
202
+
203
+ ---
204
+
205
+ ## Design Philosophy
206
+
207
+ DiffStory was built to answer five questions about every changed line:
208
+
209
+ > **What changed? Who changed it? When? Why? How did it evolve?**
210
+
211
+ It consolidates `git diff`, `git blame`, `git log`, and GitHub-style review UX into a single, portable artifact — no server, no accounts, no data leaving your machine.
212
+
213
+ ### Security
214
+
215
+ - Never uploads code or transmits data
216
+ - No telemetry, no analytics, no accounts
217
+ - No external API calls by default
218
+ - Never modifies your repository
219
+ - Generated reports are safe for air-gapped environments, client deliverables, and compliance audits
220
+
221
+ ---
222
+
223
+ ## License
224
+
225
+ MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "diffstory"
7
- version = "0.2.0"
7
+ version = "0.2.1"
8
8
  description = "Transform Git diffs into rich, interactive, self-contained HTML reports"
9
9
  readme = "README.md"
10
10
  license = {text = "MIT"}
@@ -1,3 +1,3 @@
1
1
  """DiffStory — Transform Git diffs into rich, interactive HTML reports."""
2
2
 
3
- __version__ = "0.2.0"
3
+ __version__ = "0.2.1"
@@ -3,7 +3,6 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import argparse
6
- import os
7
6
  import sys
8
7
  from pathlib import Path
9
8
  from typing import Optional
@@ -134,6 +134,7 @@ def parse_diff(diff_text: str) -> list[DiffFile]:
134
134
 
135
135
  # Binary files
136
136
  if line.startswith("Binary files ") or line == "Binary files differ":
137
+ current_file.is_binary_file = True
137
138
  continue
138
139
 
139
140
  # New file mode
@@ -9,7 +9,7 @@ from typing import Any, Optional
9
9
 
10
10
  import json
11
11
 
12
- from diffstory.diff_parser import DiffFile, DiffLine, Hunk, compute_word_diffs, IMAGE_EXTENSIONS
12
+ from diffstory.diff_parser import DiffFile, DiffLine, Hunk, compute_word_diffs
13
13
  from diffstory.syntax import get_highlighted_line, get_syntax_styles
14
14
  from diffstory.git_utils import get_blame_for_revision, get_commit_info, get_repo_name
15
15
 
@@ -278,10 +278,33 @@ def _render_file_section(file: DiffFile, file_index: int, lexer_cache: dict) ->
278
278
  sbs_html = ""
279
279
  inline_html = ""
280
280
 
281
- for hunk in file.hunks:
282
- unified_html += _render_unified_hunk(hunk, file.display_path, lexer_cache)
283
- sbs_html += _render_sidebyside_hunk(hunk, file.display_path, lexer_cache)
284
- inline_html += _render_inline_hunk(hunk, file.display_path, lexer_cache)
281
+ if file.is_binary_file:
282
+ # Binary file — show placeholder instead of diff hunks
283
+ binary_content = escape("Binary file")
284
+ if file.is_image:
285
+ binary_content = (
286
+ '<div class="binary-preview">'
287
+ '<div class="binary-icon">&#128247;</div>'
288
+ '<div class="binary-label">Image file &mdash; ' + escape(file_label) + '</div>'
289
+ '<div class="binary-note">Preview requires the file to be checked out locally</div>'
290
+ '</div>'
291
+ )
292
+ else:
293
+ binary_content = (
294
+ '<div class="binary-preview">'
295
+ '<div class="binary-icon">&#128196;</div>'
296
+ '<div class="binary-label">Binary file &mdash; ' + escape(file_label) + '</div>'
297
+ '<div class="binary-note">Diff content not available for binary files</div>'
298
+ '</div>'
299
+ )
300
+ unified_html = '<div class="binary-container">' + binary_content + '</div>'
301
+ sbs_html = '<div class="binary-container">' + binary_content + '</div>'
302
+ inline_html = '<div class="binary-container">' + binary_content + '</div>'
303
+ else:
304
+ for hunk in file.hunks:
305
+ unified_html += _render_unified_hunk(hunk, file.display_path, lexer_cache)
306
+ sbs_html += _render_sidebyside_hunk(hunk, file.display_path, lexer_cache)
307
+ inline_html += _render_inline_hunk(hunk, file.display_path, lexer_cache)
285
308
 
286
309
  additions = sum(1 for h in file.hunks for l in h.lines if l.line_type == "addition")
287
310
  deletions = sum(1 for h in file.hunks for l in h.lines if l.line_type == "deletion")
@@ -1713,6 +1736,39 @@ body {
1713
1736
  background: #ffd70033;
1714
1737
  }
1715
1738
 
1739
+ /* Binary file preview */
1740
+ .binary-container {
1741
+ padding: 20px;
1742
+ text-align: center;
1743
+ }
1744
+
1745
+ .binary-preview {
1746
+ padding: 24px;
1747
+ border: 2px dashed var(--border);
1748
+ border-radius: 8px;
1749
+ background: var(--bg-secondary);
1750
+ max-width: 400px;
1751
+ margin: 0 auto;
1752
+ }
1753
+
1754
+ .binary-icon {
1755
+ font-size: 40px;
1756
+ margin-bottom: 8px;
1757
+ }
1758
+
1759
+ .binary-label {
1760
+ font-size: 14px;
1761
+ font-weight: 600;
1762
+ color: var(--text);
1763
+ font-family: 'SFMono-Regular', 'Menlo', 'Monaco', 'Consolas', monospace;
1764
+ }
1765
+
1766
+ .binary-note {
1767
+ font-size: 12px;
1768
+ color: var(--text-secondary);
1769
+ margin-top: 4px;
1770
+ }
1771
+
1716
1772
  /* Diff line hover cursor for blame */
1717
1773
  .diff-line {
1718
1774
  cursor: pointer;