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.
Files changed (154) hide show
  1. package/.next/standalone/apps/web/.next/BUILD_ID +1 -1
  2. package/.next/standalone/apps/web/.next/build-manifest.json +3 -3
  3. package/.next/standalone/apps/web/.next/prerender-manifest.json +3 -3
  4. package/.next/standalone/apps/web/.next/server/app/_global-error.html +1 -1
  5. package/.next/standalone/apps/web/.next/server/app/_global-error.rsc +1 -1
  6. package/.next/standalone/apps/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  7. package/.next/standalone/apps/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  8. package/.next/standalone/apps/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  9. package/.next/standalone/apps/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  10. package/.next/standalone/apps/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  11. package/.next/standalone/apps/web/.next/server/app/_not-found/page.js +1 -1
  12. package/.next/standalone/apps/web/.next/server/app/_not-found/page.js.nft.json +1 -1
  13. package/.next/standalone/apps/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  14. package/.next/standalone/apps/web/.next/server/app/_not-found.html +1 -1
  15. package/.next/standalone/apps/web/.next/server/app/_not-found.rsc +16 -15
  16. package/.next/standalone/apps/web/.next/server/app/_not-found.segments/_full.segment.rsc +16 -15
  17. package/.next/standalone/apps/web/.next/server/app/_not-found.segments/_head.segment.rsc +4 -4
  18. package/.next/standalone/apps/web/.next/server/app/_not-found.segments/_index.segment.rsc +5 -4
  19. package/.next/standalone/apps/web/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
  20. package/.next/standalone/apps/web/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
  21. package/.next/standalone/apps/web/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  22. package/.next/standalone/apps/web/.next/server/app/api/comments/route.js +1 -1
  23. package/.next/standalone/apps/web/.next/server/app/api/comments/route.js.nft.json +1 -1
  24. package/.next/standalone/apps/web/.next/server/app/api/diff/route.js +2 -2
  25. package/.next/standalone/apps/web/.next/server/app/api/diff/route.js.nft.json +1 -1
  26. package/.next/standalone/apps/web/.next/server/app/api/discard/route.js +2 -2
  27. package/.next/standalone/apps/web/.next/server/app/api/discard/route.js.nft.json +1 -1
  28. package/.next/standalone/apps/web/.next/server/app/api/file/route.js +2 -2
  29. package/.next/standalone/apps/web/.next/server/app/api/file/route.js.nft.json +1 -1
  30. package/.next/standalone/apps/web/.next/server/app/api/files/route.js +2 -2
  31. package/.next/standalone/apps/web/.next/server/app/api/files/route.js.nft.json +1 -1
  32. package/.next/standalone/apps/web/.next/server/app/index.html +1 -1
  33. package/.next/standalone/apps/web/.next/server/app/index.rsc +15 -14
  34. package/.next/standalone/apps/web/.next/server/app/index.segments/__PAGE__.segment.rsc +3 -3
  35. package/.next/standalone/apps/web/.next/server/app/index.segments/_full.segment.rsc +15 -14
  36. package/.next/standalone/apps/web/.next/server/app/index.segments/_head.segment.rsc +4 -4
  37. package/.next/standalone/apps/web/.next/server/app/index.segments/_index.segment.rsc +5 -4
  38. package/.next/standalone/apps/web/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  39. package/.next/standalone/apps/web/.next/server/app/page/react-loadable-manifest.json +2 -2
  40. package/.next/standalone/apps/web/.next/server/app/page.js +2 -2
  41. package/.next/standalone/apps/web/.next/server/app/page.js.nft.json +1 -1
  42. package/.next/standalone/apps/web/.next/server/app/page_client-reference-manifest.js +1 -1
  43. package/.next/standalone/apps/web/.next/server/chunks/[root-of-the-server]__05ejtyr._.js +3 -0
  44. package/.next/standalone/apps/web/.next/server/chunks/[root-of-the-server]__0e2dp4h._.js +3 -0
  45. package/.next/standalone/apps/web/.next/server/chunks/[root-of-the-server]__0egk6ui._.js +3 -0
  46. package/.next/standalone/apps/web/.next/server/chunks/[root-of-the-server]__0i6i-~n._.js +3 -0
  47. package/.next/standalone/apps/web/.next/server/chunks/[root-of-the-server]__0sv4hr9._.js +3 -0
  48. package/.next/standalone/apps/web/.next/server/chunks/[root-of-the-server]__0t.tl18._.js +3 -0
  49. package/.next/standalone/apps/web/.next/server/chunks/ssr/{[root-of-the-server]__06b81~v._.js → [root-of-the-server]__0giwc4b._.js} +2 -2
  50. package/.next/standalone/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0jit913._.js +3 -0
  51. package/.next/standalone/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0v19a7g._.js +3 -0
  52. package/.next/standalone/apps/web/.next/server/chunks/ssr/_0f40lcw._.js +3 -0
  53. package/.next/standalone/apps/web/.next/server/chunks/ssr/_0oc3qg_._.js +3 -3
  54. package/.next/standalone/apps/web/.next/server/chunks/ssr/{apps_web_0b_ykcu._.js → _0qo42r0._.js} +2 -2
  55. package/.next/standalone/apps/web/.next/server/chunks/ssr/{apps_web_08kf15u._.js → apps_web_0758ax4._.js} +2 -2
  56. package/.next/standalone/apps/web/.next/server/chunks/ssr/node_modules_0v8w2j~._.js +70 -0
  57. package/.next/standalone/apps/web/.next/server/middleware-build-manifest.js +3 -3
  58. package/.next/standalone/apps/web/.next/server/pages/404.html +1 -1
  59. package/.next/standalone/apps/web/.next/server/pages/500.html +1 -1
  60. package/.next/standalone/apps/web/.next/server/server-reference-manifest.js +1 -1
  61. package/.next/standalone/apps/web/.next/server/server-reference-manifest.json +1 -1
  62. package/.next/standalone/apps/web/.next/static/chunks/{0-ci0c9di0qo8.js → 00zp83w4g1v1r.js} +3 -3
  63. package/.next/standalone/apps/web/.next/static/chunks/042ip11u2i2z6.js +138 -0
  64. package/.next/standalone/apps/web/.next/static/chunks/085s9eg867ha-.js +1 -0
  65. package/.next/standalone/apps/web/.next/static/chunks/0_vmme_k_ff_u.js +67 -0
  66. package/.next/standalone/apps/web/.next/static/chunks/0ap~_hc7r17_6.js +1 -0
  67. package/.next/standalone/apps/web/.next/static/chunks/0nt5-rwas5thn.js +67 -0
  68. package/.next/standalone/apps/web/.next/static/chunks/0p~3-4_.v0cft.js +1 -0
  69. package/.next/standalone/apps/web/.next/static/chunks/0wx-2dr44ql-g.js +1 -0
  70. package/.next/standalone/apps/web/.next/static/chunks/{0syypqto3~pe_.js → 0y0o261rjun_2.js} +8 -8
  71. package/.next/standalone/apps/web/.next/static/chunks/0y5z3t-z1c8ks.js.map +5 -0
  72. package/.next/standalone/apps/web/.next/static/chunks/17b.xoi.b6rcl.js +1 -0
  73. package/.next/standalone/apps/web/.next/static/chunks/turbopack-0_n_4n~_4no2a.js +1 -0
  74. package/.next/standalone/apps/web/.next/static/chunks/turbopack-worker-0sjn--fhq~1cg.js +1 -0
  75. package/.next/standalone/apps/web/.next/static/media/diffs.worker.09unk0quktc_5.ts +1 -0
  76. package/.next/standalone/apps/web/package.json +4 -4
  77. package/README.md +10 -3
  78. package/package.json +4 -4
  79. package/.next/standalone/apps/web/.next/server/chunks/[root-of-the-server]__05vwx85._.js +0 -3
  80. package/.next/standalone/apps/web/.next/server/chunks/[root-of-the-server]__09vmjc2._.js +0 -3
  81. package/.next/standalone/apps/web/.next/server/chunks/[root-of-the-server]__0etmu4u._.js +0 -3
  82. package/.next/standalone/apps/web/.next/server/chunks/[root-of-the-server]__0g-_a7n._.js +0 -3
  83. package/.next/standalone/apps/web/.next/server/chunks/[root-of-the-server]__0mshgfw._.js +0 -3
  84. package/.next/standalone/apps/web/.next/server/chunks/[root-of-the-server]__0qixima._.js +0 -3
  85. package/.next/standalone/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0f~hmsk._.js +0 -3
  86. package/.next/standalone/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0khyzju._.js +0 -3
  87. package/.next/standalone/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0l2mjim._.js +0 -70
  88. package/.next/standalone/apps/web/.next/server/chunks/ssr/_0kwaklj._.js +0 -3
  89. package/.next/standalone/apps/web/.next/static/chunks/0.ae0sud8tm7k.js +0 -204
  90. package/.next/standalone/apps/web/.next/static/chunks/0di~ntk7iivm4.js +0 -1
  91. package/.next/standalone/apps/web/.next/static/chunks/0pklg0nmdvay8.js +0 -1
  92. package/.next/standalone/apps/web/.next/static/chunks/0up9_7hiwl_dt.js +0 -1
  93. package/.next/standalone/apps/web/.next/static/chunks/0~984a88e4rp9.js +0 -204
  94. package/.next/standalone/apps/web/.next/static/chunks/13y8355z8m13w.js +0 -1
  95. package/.next/standalone/apps/web/AGENTS.md +0 -60
  96. package/.next/standalone/apps/web/CHANGELOG.md +0 -7
  97. package/.next/standalone/apps/web/CLAUDE.md +0 -1
  98. package/.next/standalone/apps/web/README.md +0 -78
  99. package/.next/standalone/apps/web/app/api/comments/route.ts +0 -20
  100. package/.next/standalone/apps/web/app/api/diff/route.ts +0 -15
  101. package/.next/standalone/apps/web/app/api/discard/route.ts +0 -15
  102. package/.next/standalone/apps/web/app/api/file/route.ts +0 -17
  103. package/.next/standalone/apps/web/app/api/files/route.ts +0 -14
  104. package/.next/standalone/apps/web/app/api/open/route.ts +0 -64
  105. package/.next/standalone/apps/web/app/favicon.ico +0 -0
  106. package/.next/standalone/apps/web/app/globals.css +0 -214
  107. package/.next/standalone/apps/web/app/layout.tsx +0 -52
  108. package/.next/standalone/apps/web/app/page.tsx +0 -6
  109. package/.next/standalone/apps/web/bin/diffhub.mjs +0 -147
  110. package/.next/standalone/apps/web/components/ContextMenu.tsx +0 -161
  111. package/.next/standalone/apps/web/components/DiffApp.tsx +0 -419
  112. package/.next/standalone/apps/web/components/DiffViewer.tsx +0 -565
  113. package/.next/standalone/apps/web/components/FileDiffHeader.tsx +0 -119
  114. package/.next/standalone/apps/web/components/FileList.tsx +0 -455
  115. package/.next/standalone/apps/web/components/KeyboardShortcutsDialog.tsx +0 -79
  116. package/.next/standalone/apps/web/components/SidebarHelpMenu.tsx +0 -86
  117. package/.next/standalone/apps/web/components/StatusBar.tsx +0 -212
  118. package/.next/standalone/apps/web/components/icons/file-status-icons.tsx +0 -48
  119. package/.next/standalone/apps/web/components/theme-provider.tsx +0 -12
  120. package/.next/standalone/apps/web/components/ui/button.tsx +0 -90
  121. package/.next/standalone/apps/web/components/ui/empty.tsx +0 -82
  122. package/.next/standalone/apps/web/components/ui/input.tsx +0 -18
  123. package/.next/standalone/apps/web/components/ui/kbd.tsx +0 -14
  124. package/.next/standalone/apps/web/components/ui/separator.tsx +0 -23
  125. package/.next/standalone/apps/web/components/ui/sheet.tsx +0 -109
  126. package/.next/standalone/apps/web/components/ui/sidebar.tsx +0 -700
  127. package/.next/standalone/apps/web/components/ui/skeleton.tsx +0 -9
  128. package/.next/standalone/apps/web/components/ui/toggle.tsx +0 -35
  129. package/.next/standalone/apps/web/components/ui/tooltip.tsx +0 -52
  130. package/.next/standalone/apps/web/components.json +0 -27
  131. package/.next/standalone/apps/web/lib/comments.ts +0 -52
  132. package/.next/standalone/apps/web/lib/export-comments.ts +0 -13
  133. package/.next/standalone/apps/web/lib/git.ts +0 -201
  134. package/.next/standalone/apps/web/lib/use-mobile.ts +0 -19
  135. package/.next/standalone/apps/web/lib/utils.ts +0 -5
  136. package/.next/standalone/apps/web/next.config.ts +0 -19
  137. package/.next/standalone/apps/web/oxfmt.config.ts +0 -6
  138. package/.next/standalone/apps/web/oxlint.config.ts +0 -13
  139. package/.next/standalone/apps/web/postcss.config.mjs +0 -7
  140. package/.next/standalone/apps/web/public/file.svg +0 -1
  141. package/.next/standalone/apps/web/public/glide-variable-italic.woff2 +0 -0
  142. package/.next/standalone/apps/web/public/glide-variable.woff2 +0 -0
  143. package/.next/standalone/apps/web/public/globe.svg +0 -1
  144. package/.next/standalone/apps/web/public/next.svg +0 -1
  145. package/.next/standalone/apps/web/public/operator-mono-book-italic.woff2 +0 -0
  146. package/.next/standalone/apps/web/public/operator-mono-book.woff2 +0 -0
  147. package/.next/standalone/apps/web/public/operator-mono-medium-italic.woff2 +0 -0
  148. package/.next/standalone/apps/web/public/operator-mono-medium.woff2 +0 -0
  149. package/.next/standalone/apps/web/public/vercel.svg +0 -1
  150. package/.next/standalone/apps/web/public/window.svg +0 -1
  151. package/.next/standalone/apps/web/tsconfig.json +0 -34
  152. /package/.next/standalone/apps/web/.next/static/{ZhI_-YaFho-fQoajjgwSH → mAVCL4HCmtJwT35feBPdK}/_buildManifest.js +0 -0
  153. /package/.next/standalone/apps/web/.next/static/{ZhI_-YaFho-fQoajjgwSH → mAVCL4HCmtJwT35feBPdK}/_clientMiddlewareManifest.js +0 -0
  154. /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,6 +0,0 @@
1
- import { DiffApp } from "@/components/DiffApp";
2
-
3
- export default function Home() {
4
- const repoPath = process.env.CMUX_DIFF_REPO ?? process.cwd();
5
- return <DiffApp repoPath={repoPath} />;
6
- }
@@ -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
- };