git-diff-view 0.0.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.
package/CLAUDE.md ADDED
@@ -0,0 +1,106 @@
1
+
2
+ Default to using Bun instead of Node.js.
3
+
4
+ - Use `bun <file>` instead of `node <file>` or `ts-node <file>`
5
+ - Use `bun test` instead of `jest` or `vitest`
6
+ - Use `bun build <file.html|file.ts|file.css>` instead of `webpack` or `esbuild`
7
+ - Use `bun install` instead of `npm install` or `yarn install` or `pnpm install`
8
+ - Use `bun run <script>` instead of `npm run <script>` or `yarn run <script>` or `pnpm run <script>`
9
+ - Use `bunx <package> <command>` instead of `npx <package> <command>`
10
+ - Bun automatically loads .env, so don't use dotenv.
11
+
12
+ ## APIs
13
+
14
+ - `Bun.serve()` supports WebSockets, HTTPS, and routes. Don't use `express`.
15
+ - `bun:sqlite` for SQLite. Don't use `better-sqlite3`.
16
+ - `Bun.redis` for Redis. Don't use `ioredis`.
17
+ - `Bun.sql` for Postgres. Don't use `pg` or `postgres.js`.
18
+ - `WebSocket` is built-in. Don't use `ws`.
19
+ - Prefer `Bun.file` over `node:fs`'s readFile/writeFile
20
+ - Bun.$`ls` instead of execa.
21
+
22
+ ## Testing
23
+
24
+ Use `bun test` to run tests.
25
+
26
+ ```ts#index.test.ts
27
+ import { test, expect } from "bun:test";
28
+
29
+ test("hello world", () => {
30
+ expect(1).toBe(1);
31
+ });
32
+ ```
33
+
34
+ ## Frontend
35
+
36
+ Use HTML imports with `Bun.serve()`. Don't use `vite`. HTML imports fully support React, CSS, Tailwind.
37
+
38
+ Server:
39
+
40
+ ```ts#index.ts
41
+ import index from "./index.html"
42
+
43
+ Bun.serve({
44
+ routes: {
45
+ "/": index,
46
+ "/api/users/:id": {
47
+ GET: (req) => {
48
+ return new Response(JSON.stringify({ id: req.params.id }));
49
+ },
50
+ },
51
+ },
52
+ // optional websocket support
53
+ websocket: {
54
+ open: (ws) => {
55
+ ws.send("Hello, world!");
56
+ },
57
+ message: (ws, message) => {
58
+ ws.send(message);
59
+ },
60
+ close: (ws) => {
61
+ // handle close
62
+ }
63
+ },
64
+ development: {
65
+ hmr: true,
66
+ console: true,
67
+ }
68
+ })
69
+ ```
70
+
71
+ HTML files can import .tsx, .jsx or .js files directly and Bun's bundler will transpile & bundle automatically. `<link>` tags can point to stylesheets and Bun's CSS bundler will bundle.
72
+
73
+ ```html#index.html
74
+ <html>
75
+ <body>
76
+ <h1>Hello, world!</h1>
77
+ <script type="module" src="./frontend.tsx"></script>
78
+ </body>
79
+ </html>
80
+ ```
81
+
82
+ With the following `frontend.tsx`:
83
+
84
+ ```tsx#frontend.tsx
85
+ import React from "react";
86
+ import { createRoot } from "react-dom/client";
87
+
88
+ // import .css files directly and it works
89
+ import './index.css';
90
+
91
+ const root = createRoot(document.body);
92
+
93
+ export default function Frontend() {
94
+ return <h1>Hello, world!</h1>;
95
+ }
96
+
97
+ root.render(<Frontend />);
98
+ ```
99
+
100
+ Then, run index.ts
101
+
102
+ ```sh
103
+ bun --hot ./index.ts
104
+ ```
105
+
106
+ For more information, read the Bun API docs in `node_modules/bun-types/docs/**.mdx`.
package/README.md ADDED
@@ -0,0 +1,49 @@
1
+ # git-diff-view
2
+
3
+ A beautiful git diff viewer that opens in your browser, powered by [@pierre/diffs](https://diffs.com/).
4
+
5
+ ## Features
6
+
7
+ - 🎨 **Auto theme detection** - matches your system light/dark mode
8
+ - 📊 **Stacked (unified) layout** - easy to read diffs
9
+ - 📏 **Styled bars** - visual indicators for additions/deletions
10
+ - 🔢 **Line numbers** - always visible
11
+ - ✨ **Word-level highlighting** - see exactly what changed
12
+ - 📁 **Multi-file support** - all changes in one view
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ bun install
18
+ ./index.ts install
19
+ ```
20
+
21
+ This installs a global git alias `dv`.
22
+
23
+ ## Usage
24
+
25
+ ```bash
26
+ git dv # unstaged changes
27
+ git dv --staged # staged changes
28
+ git dv HEAD~3 # last 3 commits
29
+ git dv main..HEAD # compare branches
30
+ git dv -- file.ts # specific file
31
+ ```
32
+
33
+ ## Commands
34
+
35
+ ```bash
36
+ ./index.ts install # install git alias
37
+ ./index.ts uninstall # remove git alias
38
+ ./index.ts run # view diff (used by alias)
39
+ ./index.ts --help # show help
40
+ ```
41
+
42
+ ## Requirements
43
+
44
+ - [Bun](https://bun.sh) runtime
45
+ - macOS (for `open` command and theme detection)
46
+
47
+ ## License
48
+
49
+ MIT
package/frontend.tsx ADDED
@@ -0,0 +1,58 @@
1
+ import { FileDiff } from "@pierre/diffs/react";
2
+ import { parsePatchFiles, type FileDiffMetadata } from "@pierre/diffs";
3
+ import React, { useEffect, useState } from "react";
4
+ import { createRoot } from "react-dom/client";
5
+
6
+ interface DiffData {
7
+ patch: string;
8
+ theme: "pierre-dark" | "pierre-light";
9
+ }
10
+
11
+ const App = () => {
12
+ const [data, setData] = useState<DiffData | null>(null);
13
+ const [files, setFiles] = useState<FileDiffMetadata[]>([]);
14
+ const [error, setError] = useState<string | null>(null);
15
+
16
+ useEffect(() => {
17
+ fetch("/api/diff")
18
+ .then((res) => res.json())
19
+ .then((data: DiffData) => {
20
+ setData(data);
21
+ const parsed = parsePatchFiles(data.patch);
22
+ const allFiles = parsed.flatMap((p) => p.files);
23
+ setFiles(allFiles);
24
+ })
25
+ .catch((err) => setError(err.message));
26
+ }, []);
27
+
28
+ if (error) {
29
+ return <div style={{ color: "red", padding: 20 }}>Error: {error}</div>;
30
+ }
31
+
32
+ if (!data) {
33
+ return <div style={{ color: "#888", padding: 20 }}>Loading...</div>;
34
+ }
35
+
36
+ if (files.length === 0) {
37
+ return <div style={{ color: "#888", padding: 20 }}>No changes</div>;
38
+ }
39
+
40
+ return (
41
+ <div style={{ display: "flex", flexDirection: "column", gap: 16 }}>
42
+ {files.map((file, i) => (
43
+ <FileDiff
44
+ key={i}
45
+ fileDiff={file}
46
+ options={{
47
+ theme: data.theme,
48
+ diffStyle: "unified",
49
+ diffIndicators: "bars",
50
+ lineDiffType: "word",
51
+ }}
52
+ />
53
+ ))}
54
+ </div>
55
+ );
56
+ };
57
+
58
+ createRoot(document.getElementById("diff")!).render(<App />);
package/index.html ADDED
@@ -0,0 +1,12 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <title>Git Diff</title>
6
+ <link rel="stylesheet" href="./styles.css">
7
+ </head>
8
+ <body>
9
+ <div id="diff"></div>
10
+ <script type="module" src="./frontend.tsx"></script>
11
+ </body>
12
+ </html>
package/index.ts ADDED
@@ -0,0 +1,150 @@
1
+ #!/usr/bin/env bun
2
+
3
+ import { $ } from "bun";
4
+ import index from "./index.html";
5
+
6
+ const SCRIPT_PATH = import.meta.path;
7
+
8
+ const isDarkMode = async (): Promise<boolean> => {
9
+ if (process.platform !== "darwin") return true;
10
+ try {
11
+ const result = await $`defaults read -g AppleInterfaceStyle`.quiet();
12
+ return result.text().trim() === "Dark";
13
+ } catch {
14
+ return false;
15
+ }
16
+ };
17
+
18
+ const getPatch = async (args: string[]): Promise<string> => {
19
+ try {
20
+ const proc = Bun.spawn(["git", "diff", ...args], {
21
+ stdout: "pipe",
22
+ stderr: "pipe",
23
+ });
24
+ const stdout = await new Response(proc.stdout).text();
25
+ const stderr = await new Response(proc.stderr).text();
26
+ const exitCode = await proc.exited;
27
+
28
+ if (exitCode !== 0) {
29
+ if (stderr.includes("Not a git repository")) {
30
+ console.error("Error: Not a git repository");
31
+ process.exit(1);
32
+ }
33
+ console.error(stderr);
34
+ process.exit(exitCode);
35
+ }
36
+ return stdout;
37
+ } catch (err) {
38
+ throw err;
39
+ }
40
+ };
41
+
42
+ const install = async () => {
43
+ const aliasValue = `!${SCRIPT_PATH} run`;
44
+ await $`git config --global alias.dv ${aliasValue}`;
45
+ console.log("✓ Installed git alias 'dv'");
46
+ console.log(" Usage: git dv [options]");
47
+ };
48
+
49
+ const uninstall = async () => {
50
+ try {
51
+ await $`git config --global --unset alias.dv`.quiet();
52
+ console.log("✓ Uninstalled git alias 'dv'");
53
+ } catch {
54
+ console.log("Alias 'dv' was not installed");
55
+ }
56
+ };
57
+
58
+ const run = async (args: string[]) => {
59
+ const patch = await getPatch(args);
60
+ if (!patch.trim()) {
61
+ console.log("No diff to display");
62
+ process.exit(0);
63
+ }
64
+
65
+ const dark = await isDarkMode();
66
+ const theme = dark ? "pierre-dark" : "pierre-light";
67
+
68
+ const diffData = JSON.stringify({ patch, theme });
69
+
70
+ const server = Bun.serve({
71
+ port: 0,
72
+ routes: {
73
+ "/": index,
74
+ "/api/diff": new Response(diffData, {
75
+ headers: { "Content-Type": "application/json" },
76
+ }),
77
+ },
78
+ development: {
79
+ hmr: true,
80
+ },
81
+ });
82
+
83
+ const url = `http://localhost:${server.port}`;
84
+ console.log(`Opening diff at ${url}`);
85
+
86
+ await $`open ${url}`;
87
+
88
+ process.on("SIGINT", () => {
89
+ server.stop();
90
+ process.exit(0);
91
+ });
92
+
93
+ console.log("Press Ctrl+C to exit");
94
+ await Bun.sleep(Number.MAX_SAFE_INTEGER);
95
+ };
96
+
97
+ const showHelp = () => {
98
+ console.log(`git-diff-view - Beautiful git diffs in your browser
99
+
100
+ Usage:
101
+ git-diff-view <command> [options]
102
+
103
+ Commands:
104
+ install Install the 'git dv' alias globally
105
+ uninstall Remove the 'git dv' alias
106
+ run View diff in browser (used by the alias)
107
+
108
+ Examples:
109
+ git-diff-view install
110
+ git-diff-view run
111
+ git-diff-view run --staged
112
+ git-diff-view run HEAD~3
113
+
114
+ After installing, use:
115
+ git dv # unstaged changes
116
+ git dv --staged # staged changes
117
+ git dv HEAD~3 # last 3 commits
118
+ `);
119
+ };
120
+
121
+ const main = async () => {
122
+ const args = process.argv.slice(2);
123
+ const command = args[0];
124
+
125
+ switch (command) {
126
+ case "install":
127
+ await install();
128
+ break;
129
+ case "uninstall":
130
+ await uninstall();
131
+ break;
132
+ case "run":
133
+ await run(args.slice(1));
134
+ break;
135
+ case "--help":
136
+ case "-h":
137
+ showHelp();
138
+ break;
139
+ default:
140
+ if (!command) {
141
+ showHelp();
142
+ } else {
143
+ console.error(`Unknown command: ${command}`);
144
+ console.error("Run 'git-diff-view --help' for usage");
145
+ process.exit(1);
146
+ }
147
+ }
148
+ };
149
+
150
+ main();
package/package.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "git-diff-view",
3
+ "version": "0.0.1",
4
+ "module": "index.ts",
5
+ "type": "module",
6
+ "bin": {
7
+ "git-diff-view": "./index.ts"
8
+ },
9
+ "scripts": {
10
+ "start": "bun run index.ts"
11
+ },
12
+ "devDependencies": {
13
+ "@types/bun": "latest"
14
+ },
15
+ "peerDependencies": {
16
+ "typescript": "^5"
17
+ },
18
+ "dependencies": {
19
+ "@pierre/diffs": "^1.0.6",
20
+ "@types/react": "^19.2.8",
21
+ "@types/react-dom": "^19.2.3",
22
+ "react": "^19.2.3",
23
+ "react-dom": "^19.2.3"
24
+ }
25
+ }
package/styles.css ADDED
@@ -0,0 +1,16 @@
1
+ :root {
2
+ color-scheme: light dark;
3
+ }
4
+
5
+ body {
6
+ margin: 0;
7
+ padding: 20px;
8
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
9
+ background: var(--bg-color, #ffffff);
10
+ }
11
+
12
+ @media (prefers-color-scheme: dark) {
13
+ body {
14
+ background: #1a1a1a;
15
+ }
16
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "compilerOptions": {
3
+ // Environment setup & latest features
4
+ "lib": ["ESNext"],
5
+ "target": "ESNext",
6
+ "module": "Preserve",
7
+ "moduleDetection": "force",
8
+ "jsx": "react-jsx",
9
+ "allowJs": true,
10
+
11
+ // Bundler mode
12
+ "moduleResolution": "bundler",
13
+ "allowImportingTsExtensions": true,
14
+ "verbatimModuleSyntax": true,
15
+ "noEmit": true,
16
+
17
+ // Best practices
18
+ "strict": true,
19
+ "skipLibCheck": true,
20
+ "noFallthroughCasesInSwitch": true,
21
+ "noUncheckedIndexedAccess": true,
22
+ "noImplicitOverride": true,
23
+
24
+ // Some stricter flags (disabled by default)
25
+ "noUnusedLocals": false,
26
+ "noUnusedParameters": false,
27
+ "noPropertyAccessFromIndexSignature": false
28
+ }
29
+ }