diffhub 0.1.1 → 0.1.2
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/.next/standalone/apps/web/.next/BUILD_ID +1 -1
- package/.next/standalone/apps/web/.next/build-manifest.json +3 -3
- package/.next/standalone/apps/web/.next/prerender-manifest.json +3 -3
- package/.next/standalone/apps/web/.next/server/app/_global-error.html +1 -1
- package/.next/standalone/apps/web/.next/server/app/_global-error.rsc +1 -1
- package/.next/standalone/apps/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/.next/standalone/apps/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/.next/standalone/apps/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/.next/standalone/apps/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/.next/standalone/apps/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/apps/web/.next/server/app/_not-found/page.js +1 -1
- package/.next/standalone/apps/web/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/.next/standalone/apps/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/standalone/apps/web/.next/server/app/_not-found.html +1 -1
- package/.next/standalone/apps/web/.next/server/app/_not-found.rsc +16 -15
- package/.next/standalone/apps/web/.next/server/app/_not-found.segments/_full.segment.rsc +16 -15
- package/.next/standalone/apps/web/.next/server/app/_not-found.segments/_head.segment.rsc +4 -4
- package/.next/standalone/apps/web/.next/server/app/_not-found.segments/_index.segment.rsc +5 -4
- package/.next/standalone/apps/web/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/apps/web/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
- package/.next/standalone/apps/web/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/apps/web/.next/server/app/api/comments/route.js +1 -1
- package/.next/standalone/apps/web/.next/server/app/api/comments/route.js.nft.json +1 -1
- package/.next/standalone/apps/web/.next/server/app/api/diff/route.js +2 -2
- package/.next/standalone/apps/web/.next/server/app/api/diff/route.js.nft.json +1 -1
- package/.next/standalone/apps/web/.next/server/app/api/discard/route.js +2 -2
- package/.next/standalone/apps/web/.next/server/app/api/discard/route.js.nft.json +1 -1
- package/.next/standalone/apps/web/.next/server/app/api/file/route.js +2 -2
- package/.next/standalone/apps/web/.next/server/app/api/file/route.js.nft.json +1 -1
- package/.next/standalone/apps/web/.next/server/app/api/files/route.js +2 -2
- package/.next/standalone/apps/web/.next/server/app/api/files/route.js.nft.json +1 -1
- package/.next/standalone/apps/web/.next/server/app/index.html +1 -1
- package/.next/standalone/apps/web/.next/server/app/index.rsc +15 -14
- package/.next/standalone/apps/web/.next/server/app/index.segments/__PAGE__.segment.rsc +3 -3
- package/.next/standalone/apps/web/.next/server/app/index.segments/_full.segment.rsc +15 -14
- package/.next/standalone/apps/web/.next/server/app/index.segments/_head.segment.rsc +4 -4
- package/.next/standalone/apps/web/.next/server/app/index.segments/_index.segment.rsc +5 -4
- package/.next/standalone/apps/web/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/apps/web/.next/server/app/page/react-loadable-manifest.json +2 -2
- package/.next/standalone/apps/web/.next/server/app/page.js +2 -2
- package/.next/standalone/apps/web/.next/server/app/page.js.nft.json +1 -1
- package/.next/standalone/apps/web/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/standalone/apps/web/.next/server/chunks/[root-of-the-server]__05ejtyr._.js +3 -0
- package/.next/standalone/apps/web/.next/server/chunks/[root-of-the-server]__0e2dp4h._.js +3 -0
- package/.next/standalone/apps/web/.next/server/chunks/[root-of-the-server]__0egk6ui._.js +3 -0
- package/.next/standalone/apps/web/.next/server/chunks/[root-of-the-server]__0i6i-~n._.js +3 -0
- package/.next/standalone/apps/web/.next/server/chunks/[root-of-the-server]__0sv4hr9._.js +3 -0
- package/.next/standalone/apps/web/.next/server/chunks/[root-of-the-server]__0t.tl18._.js +3 -0
- package/.next/standalone/apps/web/.next/server/chunks/ssr/{[root-of-the-server]__06b81~v._.js → [root-of-the-server]__0giwc4b._.js} +2 -2
- package/.next/standalone/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0jit913._.js +3 -0
- package/.next/standalone/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0v19a7g._.js +3 -0
- package/.next/standalone/apps/web/.next/server/chunks/ssr/_0f40lcw._.js +3 -0
- package/.next/standalone/apps/web/.next/server/chunks/ssr/_0oc3qg_._.js +3 -3
- package/.next/standalone/apps/web/.next/server/chunks/ssr/{apps_web_0b_ykcu._.js → _0qo42r0._.js} +2 -2
- package/.next/standalone/apps/web/.next/server/chunks/ssr/{apps_web_08kf15u._.js → apps_web_0758ax4._.js} +2 -2
- package/.next/standalone/apps/web/.next/server/chunks/ssr/node_modules_0v8w2j~._.js +70 -0
- package/.next/standalone/apps/web/.next/server/middleware-build-manifest.js +3 -3
- package/.next/standalone/apps/web/.next/server/pages/404.html +1 -1
- package/.next/standalone/apps/web/.next/server/pages/500.html +1 -1
- package/.next/standalone/apps/web/.next/server/server-reference-manifest.js +1 -1
- package/.next/standalone/apps/web/.next/server/server-reference-manifest.json +1 -1
- package/.next/standalone/apps/web/.next/static/chunks/{0-ci0c9di0qo8.js → 00zp83w4g1v1r.js} +3 -3
- package/.next/standalone/apps/web/.next/static/chunks/042ip11u2i2z6.js +138 -0
- package/.next/standalone/apps/web/.next/static/chunks/085s9eg867ha-.js +1 -0
- package/.next/standalone/apps/web/.next/static/chunks/0_vmme_k_ff_u.js +67 -0
- package/.next/standalone/apps/web/.next/static/chunks/0ap~_hc7r17_6.js +1 -0
- package/.next/standalone/apps/web/.next/static/chunks/0nt5-rwas5thn.js +67 -0
- package/.next/standalone/apps/web/.next/static/chunks/0p~3-4_.v0cft.js +1 -0
- package/.next/standalone/apps/web/.next/static/chunks/0wx-2dr44ql-g.js +1 -0
- package/.next/standalone/apps/web/.next/static/chunks/{0syypqto3~pe_.js → 0y0o261rjun_2.js} +8 -8
- package/.next/standalone/apps/web/.next/static/chunks/0y5z3t-z1c8ks.js.map +5 -0
- package/.next/standalone/apps/web/.next/static/chunks/17b.xoi.b6rcl.js +1 -0
- package/.next/standalone/apps/web/.next/static/chunks/turbopack-0_n_4n~_4no2a.js +1 -0
- package/.next/standalone/apps/web/.next/static/chunks/turbopack-worker-0sjn--fhq~1cg.js +1 -0
- package/.next/standalone/apps/web/.next/static/media/diffs.worker.09unk0quktc_5.ts +1 -0
- package/.next/standalone/apps/web/package.json +4 -4
- package/README.md +10 -3
- package/package.json +4 -4
- package/.next/standalone/apps/web/.next/server/chunks/[root-of-the-server]__05vwx85._.js +0 -3
- package/.next/standalone/apps/web/.next/server/chunks/[root-of-the-server]__09vmjc2._.js +0 -3
- package/.next/standalone/apps/web/.next/server/chunks/[root-of-the-server]__0etmu4u._.js +0 -3
- package/.next/standalone/apps/web/.next/server/chunks/[root-of-the-server]__0g-_a7n._.js +0 -3
- package/.next/standalone/apps/web/.next/server/chunks/[root-of-the-server]__0mshgfw._.js +0 -3
- package/.next/standalone/apps/web/.next/server/chunks/[root-of-the-server]__0qixima._.js +0 -3
- package/.next/standalone/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0f~hmsk._.js +0 -3
- package/.next/standalone/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0khyzju._.js +0 -3
- package/.next/standalone/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0l2mjim._.js +0 -70
- package/.next/standalone/apps/web/.next/server/chunks/ssr/_0kwaklj._.js +0 -3
- package/.next/standalone/apps/web/.next/static/chunks/0.ae0sud8tm7k.js +0 -204
- package/.next/standalone/apps/web/.next/static/chunks/0di~ntk7iivm4.js +0 -1
- package/.next/standalone/apps/web/.next/static/chunks/0pklg0nmdvay8.js +0 -1
- package/.next/standalone/apps/web/.next/static/chunks/0up9_7hiwl_dt.js +0 -1
- package/.next/standalone/apps/web/.next/static/chunks/0~984a88e4rp9.js +0 -204
- package/.next/standalone/apps/web/.next/static/chunks/13y8355z8m13w.js +0 -1
- package/.next/standalone/apps/web/AGENTS.md +0 -60
- package/.next/standalone/apps/web/CHANGELOG.md +0 -7
- package/.next/standalone/apps/web/CLAUDE.md +0 -1
- package/.next/standalone/apps/web/README.md +0 -78
- package/.next/standalone/apps/web/app/api/comments/route.ts +0 -20
- package/.next/standalone/apps/web/app/api/diff/route.ts +0 -15
- package/.next/standalone/apps/web/app/api/discard/route.ts +0 -15
- package/.next/standalone/apps/web/app/api/file/route.ts +0 -17
- package/.next/standalone/apps/web/app/api/files/route.ts +0 -14
- package/.next/standalone/apps/web/app/api/open/route.ts +0 -64
- package/.next/standalone/apps/web/app/favicon.ico +0 -0
- package/.next/standalone/apps/web/app/globals.css +0 -214
- package/.next/standalone/apps/web/app/layout.tsx +0 -52
- package/.next/standalone/apps/web/app/page.tsx +0 -6
- package/.next/standalone/apps/web/bin/diffhub.mjs +0 -147
- package/.next/standalone/apps/web/components/ContextMenu.tsx +0 -161
- package/.next/standalone/apps/web/components/DiffApp.tsx +0 -419
- package/.next/standalone/apps/web/components/DiffViewer.tsx +0 -565
- package/.next/standalone/apps/web/components/FileDiffHeader.tsx +0 -119
- package/.next/standalone/apps/web/components/FileList.tsx +0 -455
- package/.next/standalone/apps/web/components/KeyboardShortcutsDialog.tsx +0 -79
- package/.next/standalone/apps/web/components/SidebarHelpMenu.tsx +0 -86
- package/.next/standalone/apps/web/components/StatusBar.tsx +0 -212
- package/.next/standalone/apps/web/components/icons/file-status-icons.tsx +0 -48
- package/.next/standalone/apps/web/components/theme-provider.tsx +0 -12
- package/.next/standalone/apps/web/components/ui/button.tsx +0 -90
- package/.next/standalone/apps/web/components/ui/empty.tsx +0 -82
- package/.next/standalone/apps/web/components/ui/input.tsx +0 -18
- package/.next/standalone/apps/web/components/ui/kbd.tsx +0 -14
- package/.next/standalone/apps/web/components/ui/separator.tsx +0 -23
- package/.next/standalone/apps/web/components/ui/sheet.tsx +0 -109
- package/.next/standalone/apps/web/components/ui/sidebar.tsx +0 -700
- package/.next/standalone/apps/web/components/ui/skeleton.tsx +0 -9
- package/.next/standalone/apps/web/components/ui/toggle.tsx +0 -35
- package/.next/standalone/apps/web/components/ui/tooltip.tsx +0 -52
- package/.next/standalone/apps/web/components.json +0 -27
- package/.next/standalone/apps/web/lib/comments.ts +0 -52
- package/.next/standalone/apps/web/lib/export-comments.ts +0 -13
- package/.next/standalone/apps/web/lib/git.ts +0 -201
- package/.next/standalone/apps/web/lib/use-mobile.ts +0 -19
- package/.next/standalone/apps/web/lib/utils.ts +0 -5
- package/.next/standalone/apps/web/next.config.ts +0 -19
- package/.next/standalone/apps/web/oxfmt.config.ts +0 -6
- package/.next/standalone/apps/web/oxlint.config.ts +0 -13
- package/.next/standalone/apps/web/postcss.config.mjs +0 -7
- package/.next/standalone/apps/web/public/file.svg +0 -1
- package/.next/standalone/apps/web/public/glide-variable-italic.woff2 +0 -0
- package/.next/standalone/apps/web/public/glide-variable.woff2 +0 -0
- package/.next/standalone/apps/web/public/globe.svg +0 -1
- package/.next/standalone/apps/web/public/next.svg +0 -1
- package/.next/standalone/apps/web/public/operator-mono-book-italic.woff2 +0 -0
- package/.next/standalone/apps/web/public/operator-mono-book.woff2 +0 -0
- package/.next/standalone/apps/web/public/operator-mono-medium-italic.woff2 +0 -0
- package/.next/standalone/apps/web/public/operator-mono-medium.woff2 +0 -0
- package/.next/standalone/apps/web/public/vercel.svg +0 -1
- package/.next/standalone/apps/web/public/window.svg +0 -1
- package/.next/standalone/apps/web/tsconfig.json +0 -34
- /package/.next/standalone/apps/web/.next/static/{ZhI_-YaFho-fQoajjgwSH → mAVCL4HCmtJwT35feBPdK}/_buildManifest.js +0 -0
- /package/.next/standalone/apps/web/.next/static/{ZhI_-YaFho-fQoajjgwSH → mAVCL4HCmtJwT35feBPdK}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/apps/web/.next/static/{ZhI_-YaFho-fQoajjgwSH → mAVCL4HCmtJwT35feBPdK}/_ssgManifest.js +0 -0
|
@@ -1,214 +0,0 @@
|
|
|
1
|
-
@import "tailwindcss";
|
|
2
|
-
@import "tw-animate-css";
|
|
3
|
-
@import "shadcn/tailwind.css";
|
|
4
|
-
|
|
5
|
-
@custom-variant dark (&:is(.dark *));
|
|
6
|
-
|
|
7
|
-
@theme inline {
|
|
8
|
-
--color-background: var(--background);
|
|
9
|
-
--color-foreground: var(--foreground);
|
|
10
|
-
--font-sans: var(--font-glide), ui-sans-serif, system-ui, sans-serif;
|
|
11
|
-
--font-mono: var(--font-operator-mono), ui-monospace, monospace;
|
|
12
|
-
--font-heading: var(--font-glide), ui-sans-serif, system-ui, sans-serif;
|
|
13
|
-
--color-sidebar-ring: var(--sidebar-ring);
|
|
14
|
-
--color-sidebar-border: var(--sidebar-border);
|
|
15
|
-
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
|
16
|
-
--color-sidebar-accent: var(--sidebar-accent);
|
|
17
|
-
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
|
18
|
-
--color-sidebar-primary: var(--sidebar-primary);
|
|
19
|
-
--color-sidebar-foreground: var(--sidebar-foreground);
|
|
20
|
-
--color-sidebar: var(--sidebar);
|
|
21
|
-
--color-chart-5: var(--chart-5);
|
|
22
|
-
--color-chart-4: var(--chart-4);
|
|
23
|
-
--color-chart-3: var(--chart-3);
|
|
24
|
-
--color-chart-2: var(--chart-2);
|
|
25
|
-
--color-chart-1: var(--chart-1);
|
|
26
|
-
--color-ring: var(--ring);
|
|
27
|
-
--color-input: var(--input);
|
|
28
|
-
--color-border: var(--border);
|
|
29
|
-
--color-destructive: var(--destructive);
|
|
30
|
-
--color-accent-foreground: var(--accent-foreground);
|
|
31
|
-
--color-accent: var(--accent);
|
|
32
|
-
--color-muted-foreground: var(--muted-foreground);
|
|
33
|
-
--color-muted: var(--muted);
|
|
34
|
-
--color-secondary-foreground: var(--secondary-foreground);
|
|
35
|
-
--color-secondary: var(--secondary);
|
|
36
|
-
--color-primary-foreground: var(--primary-foreground);
|
|
37
|
-
--color-primary: var(--primary);
|
|
38
|
-
--color-popover-foreground: var(--popover-foreground);
|
|
39
|
-
--color-popover: var(--popover);
|
|
40
|
-
--color-card-foreground: var(--card-foreground);
|
|
41
|
-
--color-card: var(--card);
|
|
42
|
-
--radius-sm: calc(var(--radius) * 0.6);
|
|
43
|
-
--radius-md: calc(var(--radius) * 0.8);
|
|
44
|
-
--radius-lg: var(--radius);
|
|
45
|
-
--radius-xl: calc(var(--radius) * 1.4);
|
|
46
|
-
--radius-2xl: calc(var(--radius) * 1.8);
|
|
47
|
-
--radius-3xl: calc(var(--radius) * 2.2);
|
|
48
|
-
--radius-4xl: calc(var(--radius) * 2.6);
|
|
49
|
-
/* Diff-specific semantic tokens */
|
|
50
|
-
--color-diff-green: var(--diff-green);
|
|
51
|
-
--color-diff-purple: var(--diff-purple);
|
|
52
|
-
--color-diff-selected: var(--diff-selected);
|
|
53
|
-
--color-diff-success: var(--diff-success);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
:root {
|
|
57
|
-
--background: oklch(1 0 0);
|
|
58
|
-
--foreground: oklch(0.145 0 0);
|
|
59
|
-
--card: oklch(1 0 0);
|
|
60
|
-
--card-foreground: oklch(0.145 0 0);
|
|
61
|
-
--popover: oklch(1 0 0);
|
|
62
|
-
--popover-foreground: oklch(0.145 0 0);
|
|
63
|
-
--primary: oklch(0.205 0 0);
|
|
64
|
-
--primary-foreground: oklch(0.985 0 0);
|
|
65
|
-
--secondary: oklch(0.97 0 0);
|
|
66
|
-
--secondary-foreground: oklch(0.205 0 0);
|
|
67
|
-
--muted: oklch(0.97 0 0);
|
|
68
|
-
--muted-foreground: oklch(0.556 0 0);
|
|
69
|
-
--accent: oklch(0.97 0 0);
|
|
70
|
-
--accent-foreground: oklch(0.205 0 0);
|
|
71
|
-
--destructive: oklch(0.577 0.245 27.325);
|
|
72
|
-
--border: oklch(0.922 0 0);
|
|
73
|
-
--input: oklch(0.922 0 0);
|
|
74
|
-
--ring: oklch(0.708 0 0);
|
|
75
|
-
--chart-1: oklch(0.87 0 0);
|
|
76
|
-
--chart-2: oklch(0.556 0 0);
|
|
77
|
-
--chart-3: oklch(0.439 0 0);
|
|
78
|
-
--chart-4: oklch(0.371 0 0);
|
|
79
|
-
--chart-5: oklch(0.269 0 0);
|
|
80
|
-
--radius: 0.625rem;
|
|
81
|
-
--sidebar: oklch(0.985 0 0);
|
|
82
|
-
--sidebar-foreground: oklch(0.145 0 0);
|
|
83
|
-
--sidebar-primary: oklch(0.205 0 0);
|
|
84
|
-
--sidebar-primary-foreground: oklch(0.985 0 0);
|
|
85
|
-
--sidebar-accent: oklch(0.97 0 0);
|
|
86
|
-
--sidebar-accent-foreground: oklch(0.205 0 0);
|
|
87
|
-
--sidebar-border: oklch(0.922 0 0);
|
|
88
|
-
--sidebar-ring: oklch(0.708 0 0);
|
|
89
|
-
/* Diff-specific tokens */
|
|
90
|
-
--diff-green: oklch(0.696 0.17 145);
|
|
91
|
-
--diff-purple: oklch(0.78 0.12 300);
|
|
92
|
-
--diff-selected: oklch(0.93 0 0);
|
|
93
|
-
--diff-success: oklch(0.573 0.15 145);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
.dark {
|
|
97
|
-
--background: oklch(0.145 0 0);
|
|
98
|
-
--foreground: oklch(0.985 0 0);
|
|
99
|
-
--card: oklch(0.205 0 0);
|
|
100
|
-
--card-foreground: oklch(0.985 0 0);
|
|
101
|
-
--popover: oklch(0.205 0 0);
|
|
102
|
-
--popover-foreground: oklch(0.985 0 0);
|
|
103
|
-
--primary: oklch(0.922 0 0);
|
|
104
|
-
--primary-foreground: oklch(0.205 0 0);
|
|
105
|
-
--secondary: oklch(0.269 0 0);
|
|
106
|
-
--secondary-foreground: oklch(0.985 0 0);
|
|
107
|
-
--muted: oklch(0.269 0 0);
|
|
108
|
-
--muted-foreground: oklch(0.708 0 0);
|
|
109
|
-
--accent: oklch(0.269 0 0);
|
|
110
|
-
--accent-foreground: oklch(0.985 0 0);
|
|
111
|
-
--destructive: oklch(0.704 0.191 22.216);
|
|
112
|
-
--border: oklch(1 0 0 / 10%);
|
|
113
|
-
--input: oklch(1 0 0 / 15%);
|
|
114
|
-
--ring: oklch(0.556 0 0);
|
|
115
|
-
--chart-1: oklch(0.87 0 0);
|
|
116
|
-
--chart-2: oklch(0.556 0 0);
|
|
117
|
-
--chart-3: oklch(0.439 0 0);
|
|
118
|
-
--chart-4: oklch(0.371 0 0);
|
|
119
|
-
--chart-5: oklch(0.269 0 0);
|
|
120
|
-
--sidebar: oklch(0.205 0 0);
|
|
121
|
-
--sidebar-foreground: oklch(0.985 0 0);
|
|
122
|
-
--sidebar-primary: oklch(0.922 0 0);
|
|
123
|
-
--sidebar-primary-foreground: oklch(0.985 0 0);
|
|
124
|
-
--sidebar-accent: oklch(0.269 0 0);
|
|
125
|
-
--sidebar-accent-foreground: oklch(0.985 0 0);
|
|
126
|
-
--sidebar-border: oklch(1 0 0 / 10%);
|
|
127
|
-
--sidebar-ring: oklch(0.556 0 0);
|
|
128
|
-
/* Diff-specific tokens */
|
|
129
|
-
--diff-green: oklch(0.696 0.15 145);
|
|
130
|
-
--diff-purple: oklch(0.78 0.12 300);
|
|
131
|
-
--diff-selected: oklch(0.269 0 0);
|
|
132
|
-
--diff-success: oklch(0.573 0.15 145);
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
@layer base {
|
|
136
|
-
* {
|
|
137
|
-
@apply border-border outline-ring/50;
|
|
138
|
-
}
|
|
139
|
-
body {
|
|
140
|
-
@apply bg-background text-foreground font-sans;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
kbd {
|
|
144
|
-
display: inline-flex;
|
|
145
|
-
align-items: center;
|
|
146
|
-
justify-content: center;
|
|
147
|
-
min-width: 16px;
|
|
148
|
-
padding: 1px 4px;
|
|
149
|
-
border-radius: 4px;
|
|
150
|
-
border: 1px solid var(--border);
|
|
151
|
-
background: var(--muted);
|
|
152
|
-
font-size: 10px;
|
|
153
|
-
font-family: var(--font-mono);
|
|
154
|
-
line-height: 1.4;
|
|
155
|
-
color: var(--muted-foreground);
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
/*
|
|
159
|
-
* Gutter "+" button: the library renders a light-DOM slot wrapper with
|
|
160
|
-
* inline styles `position:absolute; top:0; bottom:0` but NO width. We use
|
|
161
|
-
* a fixed pixel size so the button is always visible regardless of parent.
|
|
162
|
-
*/
|
|
163
|
-
.diffhub-gutter-btn {
|
|
164
|
-
display: flex;
|
|
165
|
-
align-items: center;
|
|
166
|
-
justify-content: center;
|
|
167
|
-
width: 20px;
|
|
168
|
-
height: 20px;
|
|
169
|
-
border: none;
|
|
170
|
-
border-radius: 3px;
|
|
171
|
-
background: var(--primary);
|
|
172
|
-
color: var(--primary-foreground);
|
|
173
|
-
font-size: 14px;
|
|
174
|
-
font-weight: bold;
|
|
175
|
-
line-height: 1;
|
|
176
|
-
cursor: pointer;
|
|
177
|
-
animation: diffhub-gutter-in 0.08s ease-out both;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
.diffhub-gutter-btn:hover {
|
|
181
|
-
background: color-mix(in srgb, var(--primary) 85%, black);
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
@keyframes diffhub-gutter-in {
|
|
185
|
-
from {
|
|
186
|
-
opacity: 0;
|
|
187
|
-
}
|
|
188
|
-
to {
|
|
189
|
-
opacity: 1;
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
@keyframes diffhub-menu-in {
|
|
195
|
-
from {
|
|
196
|
-
opacity: 0;
|
|
197
|
-
transform: translateY(-4px) scale(0.97);
|
|
198
|
-
}
|
|
199
|
-
to {
|
|
200
|
-
opacity: 1;
|
|
201
|
-
transform: translateY(0) scale(1);
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
.diffhub-menu-animate {
|
|
206
|
-
animation: diffhub-menu-in 120ms ease-out forwards;
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
/* ── Hunk expand separator ──────────────────────────────────────────────── */
|
|
210
|
-
/*
|
|
211
|
-
@pierre/diffs renders expand buttons inside Shadow DOM — global CSS cannot
|
|
212
|
-
reach them. The library provides its own expand-button styling. No overrides
|
|
213
|
-
are needed here.
|
|
214
|
-
*/
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import { Agentation } from "agentation";
|
|
2
|
-
import type { Metadata } from "next";
|
|
3
|
-
import localFont from "next/font/local";
|
|
4
|
-
import { ThemeProvider } from "@/components/theme-provider";
|
|
5
|
-
import "./globals.css";
|
|
6
|
-
|
|
7
|
-
const glide = localFont({
|
|
8
|
-
display: "swap",
|
|
9
|
-
src: [
|
|
10
|
-
{ path: "../public/glide-variable.woff2", style: "normal" },
|
|
11
|
-
{ path: "../public/glide-variable-italic.woff2", style: "italic" },
|
|
12
|
-
],
|
|
13
|
-
variable: "--font-glide",
|
|
14
|
-
weight: "400 900",
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
const operatorMono = localFont({
|
|
18
|
-
display: "swap",
|
|
19
|
-
src: [
|
|
20
|
-
{ path: "../public/operator-mono-book.woff2", style: "normal", weight: "400" },
|
|
21
|
-
{ path: "../public/operator-mono-book-italic.woff2", style: "italic", weight: "400" },
|
|
22
|
-
{ path: "../public/operator-mono-medium.woff2", style: "normal", weight: "500" },
|
|
23
|
-
{ path: "../public/operator-mono-medium-italic.woff2", style: "italic", weight: "500" },
|
|
24
|
-
],
|
|
25
|
-
variable: "--font-operator-mono",
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
export const metadata: Metadata = {
|
|
29
|
-
description: "GitHub PR-style local diff viewer",
|
|
30
|
-
title: "diffhub",
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
export default function RootLayout({
|
|
34
|
-
children,
|
|
35
|
-
}: Readonly<{
|
|
36
|
-
children: React.ReactNode;
|
|
37
|
-
}>) {
|
|
38
|
-
return (
|
|
39
|
-
<html
|
|
40
|
-
lang="en"
|
|
41
|
-
className={`${glide.variable} ${operatorMono.variable} h-full`}
|
|
42
|
-
suppressHydrationWarning
|
|
43
|
-
>
|
|
44
|
-
<body className="h-full overflow-hidden bg-background antialiased">
|
|
45
|
-
<ThemeProvider attribute="class" defaultTheme="dark" enableSystem disableTransitionOnChange>
|
|
46
|
-
{children}
|
|
47
|
-
{process.env.NODE_ENV === "development" && <Agentation />}
|
|
48
|
-
</ThemeProvider>
|
|
49
|
-
</body>
|
|
50
|
-
</html>
|
|
51
|
-
);
|
|
52
|
-
}
|
|
@@ -1,147 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { program } from "commander";
|
|
3
|
-
import { spawn } from "node:child_process";
|
|
4
|
-
import { existsSync } from "node:fs";
|
|
5
|
-
import { createServer } from "node:net";
|
|
6
|
-
import { join, resolve } from "node:path";
|
|
7
|
-
|
|
8
|
-
const __dirname = import.meta.dirname;
|
|
9
|
-
|
|
10
|
-
// Fast-fail on unsupported Node.js versions
|
|
11
|
-
const nodeMajor = Number.parseInt(process.version.slice(1).split(".")[0], 10);
|
|
12
|
-
if (nodeMajor < 18) {
|
|
13
|
-
process.stderr.write(`❌ diffhub requires Node.js 18+. You have ${process.version}.\n`);
|
|
14
|
-
process.stderr.write(` Download: https://nodejs.org\n`);
|
|
15
|
-
process.exit(1);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
// -- Port utilities ----------------------------------------------------------
|
|
19
|
-
|
|
20
|
-
// oxlint-disable-next-line promise/avoid-new
|
|
21
|
-
const findFreePort = async (start) => {
|
|
22
|
-
for (let p = start; p < start + 10; p += 1) {
|
|
23
|
-
// oxlint-disable-next-line promise/avoid-new
|
|
24
|
-
const free = await new Promise((_resolve) => {
|
|
25
|
-
const s = createServer();
|
|
26
|
-
s.listen(p, "127.0.0.1", () => {
|
|
27
|
-
s.close();
|
|
28
|
-
_resolve(true);
|
|
29
|
-
});
|
|
30
|
-
s.on("error", () => _resolve(false));
|
|
31
|
-
});
|
|
32
|
-
if (free) {
|
|
33
|
-
return p;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
return start;
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
const waitForServer = async (port, maxMs = 15_000) => {
|
|
40
|
-
const deadline = Date.now() + maxMs;
|
|
41
|
-
while (Date.now() < deadline) {
|
|
42
|
-
try {
|
|
43
|
-
const res = await fetch(`http://127.0.0.1:${port}`);
|
|
44
|
-
if (res.ok || res.status === 404) {
|
|
45
|
-
return true;
|
|
46
|
-
}
|
|
47
|
-
} catch {
|
|
48
|
-
// empty
|
|
49
|
-
}
|
|
50
|
-
// oxlint-disable-next-line promise/avoid-new
|
|
51
|
-
await new Promise((_resolve) => {
|
|
52
|
-
setTimeout(_resolve, 300);
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
|
-
return false;
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
// -- CLI setup ---------------------------------------------------------------
|
|
59
|
-
|
|
60
|
-
program
|
|
61
|
-
.name("diffhub")
|
|
62
|
-
.description("GitHub PR-style local diff viewer")
|
|
63
|
-
.version("0.1.0")
|
|
64
|
-
.option("-p, --port <port>", "Port to serve on", "2047")
|
|
65
|
-
.option("-r, --repo <path>", "Git repository path (defaults to cwd)")
|
|
66
|
-
.option("-b, --base <branch>", "Base branch to diff against (defaults to main/master)")
|
|
67
|
-
.option("--no-open", "Don't open browser automatically")
|
|
68
|
-
.parse(process.argv);
|
|
69
|
-
|
|
70
|
-
const opts = program.opts();
|
|
71
|
-
const repoPath = resolve(opts.repo ?? process.cwd());
|
|
72
|
-
const baseBranch = opts.base ?? "";
|
|
73
|
-
|
|
74
|
-
// Verify it's a git repo
|
|
75
|
-
if (!existsSync(join(repoPath, ".git"))) {
|
|
76
|
-
console.error(`❌ Not a git repository: ${repoPath}`);
|
|
77
|
-
console.error(` Run from inside a git repo, or pass --repo:`);
|
|
78
|
-
console.error(` diffhub --repo /path/to/your-repo`);
|
|
79
|
-
process.exit(1);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// outputFileTracingRoot is set to the monorepo root, so Next.js places the server at
|
|
83
|
-
// .next/standalone/apps/web/server.js (mirroring the workspace path).
|
|
84
|
-
const appDir = resolve(__dirname, "..");
|
|
85
|
-
const serverPath = join(appDir, ".next", "standalone", "apps", "web", "server.js");
|
|
86
|
-
|
|
87
|
-
if (!existsSync(serverPath)) {
|
|
88
|
-
console.error("❌ No production build found.");
|
|
89
|
-
console.error(" Run: npm run build");
|
|
90
|
-
process.exit(1);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
const standaloneDir = resolve(serverPath, "..");
|
|
94
|
-
const port = await findFreePort(Number.parseInt(opts.port, 10));
|
|
95
|
-
|
|
96
|
-
// -- Startup banner ----------------------------------------------------------
|
|
97
|
-
|
|
98
|
-
console.log(` diffhub\n`);
|
|
99
|
-
console.log(` Repo ${repoPath}`);
|
|
100
|
-
if (baseBranch) {
|
|
101
|
-
console.log(` Base ${baseBranch}`);
|
|
102
|
-
}
|
|
103
|
-
console.log(` URL http://localhost:${port}`);
|
|
104
|
-
console.log(`\n Press Ctrl+C to stop\n`);
|
|
105
|
-
|
|
106
|
-
// -- Start server ------------------------------------------------------------
|
|
107
|
-
|
|
108
|
-
const server = spawn("node", ["server.js"], {
|
|
109
|
-
cwd: standaloneDir,
|
|
110
|
-
env: {
|
|
111
|
-
...process.env,
|
|
112
|
-
DIFFHUB_REPO: repoPath,
|
|
113
|
-
...(baseBranch ? { DIFFHUB_BASE: baseBranch } : {}),
|
|
114
|
-
HOSTNAME: "127.0.0.1",
|
|
115
|
-
NODE_ENV: "production",
|
|
116
|
-
PORT: String(port),
|
|
117
|
-
},
|
|
118
|
-
stdio: "inherit",
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
server.on("error", (err) => {
|
|
122
|
-
console.error("Failed to start server:", err.message);
|
|
123
|
-
process.exit(1);
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
// -- Open browser when ready -------------------------------------------------
|
|
127
|
-
|
|
128
|
-
if (opts.open !== false) {
|
|
129
|
-
const url = `http://localhost:${port}`;
|
|
130
|
-
const ready = await waitForServer(port);
|
|
131
|
-
if (ready) {
|
|
132
|
-
const opener =
|
|
133
|
-
{ darwin: "open", linux: "xdg-open", win32: "start" }[process.platform] ?? "xdg-open";
|
|
134
|
-
spawn(opener, [url], {
|
|
135
|
-
detached: true,
|
|
136
|
-
shell: process.platform === "win32",
|
|
137
|
-
stdio: "ignore",
|
|
138
|
-
}).unref();
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
// -- Graceful shutdown -------------------------------------------------------
|
|
143
|
-
|
|
144
|
-
process.on("SIGINT", () => {
|
|
145
|
-
server.kill();
|
|
146
|
-
process.exit(0);
|
|
147
|
-
});
|
|
@@ -1,161 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import { useEffect, useRef, useState } from "react";
|
|
4
|
-
import {
|
|
5
|
-
FolderOpenIcon,
|
|
6
|
-
CodeLinesIcon,
|
|
7
|
-
CodeBracketsIcon,
|
|
8
|
-
CopySimpleIcon,
|
|
9
|
-
CheckIcon,
|
|
10
|
-
ArrowUndoDownIcon,
|
|
11
|
-
} from "blode-icons-react";
|
|
12
|
-
|
|
13
|
-
interface ContextMenuProps {
|
|
14
|
-
x: number;
|
|
15
|
-
y: number;
|
|
16
|
-
filePath: string;
|
|
17
|
-
repoPath: string;
|
|
18
|
-
onClose: () => void;
|
|
19
|
-
onDiscard?: () => Promise<void>;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const APPS = [
|
|
23
|
-
{ icon: FolderOpenIcon, key: "finder", label: "Finder" },
|
|
24
|
-
{ icon: CodeLinesIcon, key: "zed", label: "Zed" },
|
|
25
|
-
{ icon: CodeLinesIcon, key: "vscode", label: "VS Code" },
|
|
26
|
-
{ icon: CodeLinesIcon, key: "xcode", label: "Xcode" },
|
|
27
|
-
{ icon: CodeBracketsIcon, key: "ghostty", label: "Ghostty" },
|
|
28
|
-
{ icon: CodeBracketsIcon, key: "terminal", label: "Terminal" },
|
|
29
|
-
] as const;
|
|
30
|
-
|
|
31
|
-
type AppKey = (typeof APPS)[number]["key"];
|
|
32
|
-
|
|
33
|
-
export const ContextMenu = ({ x, y, filePath, repoPath, onClose, onDiscard }: ContextMenuProps) => {
|
|
34
|
-
const ref = useRef<HTMLDivElement>(null);
|
|
35
|
-
const [copied, setCopied] = useState(false);
|
|
36
|
-
const [discarding, setDiscarding] = useState(false);
|
|
37
|
-
|
|
38
|
-
useEffect(() => {
|
|
39
|
-
const handleClick = (e: MouseEvent) => {
|
|
40
|
-
if (ref.current && !ref.current.contains(e.target as Node)) {
|
|
41
|
-
onClose();
|
|
42
|
-
}
|
|
43
|
-
};
|
|
44
|
-
const handleKey = (e: KeyboardEvent) => {
|
|
45
|
-
if (e.key === "Escape") {
|
|
46
|
-
onClose();
|
|
47
|
-
}
|
|
48
|
-
};
|
|
49
|
-
document.addEventListener("mousedown", handleClick);
|
|
50
|
-
document.addEventListener("keydown", handleKey);
|
|
51
|
-
return () => {
|
|
52
|
-
document.removeEventListener("mousedown", handleClick);
|
|
53
|
-
document.removeEventListener("keydown", handleKey);
|
|
54
|
-
};
|
|
55
|
-
}, [onClose]);
|
|
56
|
-
|
|
57
|
-
const openIn = async (app: AppKey) => {
|
|
58
|
-
const fullPath = `${repoPath}/${filePath}`;
|
|
59
|
-
try {
|
|
60
|
-
await fetch("/api/open", {
|
|
61
|
-
body: JSON.stringify({ app, path: fullPath }),
|
|
62
|
-
headers: { "Content-Type": "application/json" },
|
|
63
|
-
method: "POST",
|
|
64
|
-
});
|
|
65
|
-
} catch {
|
|
66
|
-
// best-effort — still close the menu
|
|
67
|
-
}
|
|
68
|
-
onClose();
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
// oxlint-disable-next-line react-perf/jsx-no-new-function-as-prop
|
|
72
|
-
const handleDiscard = async () => {
|
|
73
|
-
if (!onDiscard || discarding) {
|
|
74
|
-
return;
|
|
75
|
-
}
|
|
76
|
-
setDiscarding(true);
|
|
77
|
-
try {
|
|
78
|
-
await onDiscard();
|
|
79
|
-
onClose();
|
|
80
|
-
} finally {
|
|
81
|
-
setDiscarding(false);
|
|
82
|
-
}
|
|
83
|
-
};
|
|
84
|
-
|
|
85
|
-
// oxlint-disable-next-line react-perf/jsx-no-new-function-as-prop
|
|
86
|
-
const copyPath = async () => {
|
|
87
|
-
const fullPath = `${repoPath}/${filePath}`;
|
|
88
|
-
try {
|
|
89
|
-
await navigator.clipboard.writeText(fullPath);
|
|
90
|
-
setCopied(true);
|
|
91
|
-
setTimeout(() => {
|
|
92
|
-
setCopied(false);
|
|
93
|
-
onClose();
|
|
94
|
-
}, 1200);
|
|
95
|
-
} catch {
|
|
96
|
-
onClose();
|
|
97
|
-
}
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
// Adjust position to stay in viewport
|
|
101
|
-
const style: React.CSSProperties = {
|
|
102
|
-
left: Math.min(x, window.innerWidth - 200),
|
|
103
|
-
position: "fixed",
|
|
104
|
-
top: Math.min(y, window.innerHeight - 300),
|
|
105
|
-
zIndex: 1000,
|
|
106
|
-
};
|
|
107
|
-
|
|
108
|
-
return (
|
|
109
|
-
<div
|
|
110
|
-
ref={ref}
|
|
111
|
-
style={style}
|
|
112
|
-
className="diffhub-menu-animate min-w-[160px] rounded-lg border border-border bg-popover shadow-xl dark:shadow-none py-1 text-sm"
|
|
113
|
-
>
|
|
114
|
-
{/* File path header */}
|
|
115
|
-
<div className="px-3 py-1.5 text-xs text-muted-foreground border-b border-border mb-1 font-mono truncate max-w-[200px]">
|
|
116
|
-
{filePath.split("/").pop()}
|
|
117
|
-
</div>
|
|
118
|
-
|
|
119
|
-
{APPS.map(({ key, label, icon: Icon }) => (
|
|
120
|
-
<button
|
|
121
|
-
type="button"
|
|
122
|
-
key={key}
|
|
123
|
-
// oxlint-disable-next-line react-perf/jsx-no-new-function-as-prop
|
|
124
|
-
onClick={() => openIn(key)}
|
|
125
|
-
className="flex w-full items-center gap-2.5 px-3 py-1.5 text-foreground hover:bg-secondary transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring/50"
|
|
126
|
-
>
|
|
127
|
-
<Icon size={14} className="text-muted-foreground" />
|
|
128
|
-
{label}
|
|
129
|
-
</button>
|
|
130
|
-
))}
|
|
131
|
-
|
|
132
|
-
<div className="border-t border-border mt-1 pt-1">
|
|
133
|
-
<button
|
|
134
|
-
type="button"
|
|
135
|
-
// oxlint-disable-next-line react-perf/jsx-no-new-function-as-prop
|
|
136
|
-
onClick={copyPath}
|
|
137
|
-
className="flex w-full items-center gap-2.5 px-3 py-1.5 text-foreground hover:bg-secondary transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring/50"
|
|
138
|
-
>
|
|
139
|
-
{copied ? (
|
|
140
|
-
<CheckIcon size={14} className="text-diff-green" />
|
|
141
|
-
) : (
|
|
142
|
-
<CopySimpleIcon size={14} className="text-muted-foreground" />
|
|
143
|
-
)}
|
|
144
|
-
{copied ? "Copied!" : "Copy path"}
|
|
145
|
-
</button>
|
|
146
|
-
{onDiscard && (
|
|
147
|
-
<button
|
|
148
|
-
type="button"
|
|
149
|
-
// oxlint-disable-next-line react-perf/jsx-no-new-function-as-prop
|
|
150
|
-
onClick={handleDiscard}
|
|
151
|
-
disabled={discarding}
|
|
152
|
-
className="flex w-full items-center gap-2.5 px-3 py-1.5 text-destructive hover:bg-destructive/10 transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring/50 disabled:opacity-50"
|
|
153
|
-
>
|
|
154
|
-
<ArrowUndoDownIcon size={14} className={discarding ? "animate-spin" : ""} />
|
|
155
|
-
Discard changes
|
|
156
|
-
</button>
|
|
157
|
-
)}
|
|
158
|
-
</div>
|
|
159
|
-
</div>
|
|
160
|
-
);
|
|
161
|
-
};
|