diffhub 0.1.1 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (163) 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 +17 -16
  16. package/.next/standalone/apps/web/.next/server/app/_not-found.segments/_full.segment.rsc +17 -16
  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 +6 -5
  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 +2 -2
  22. package/.next/standalone/apps/web/.next/server/app/api/comments/route.js +2 -2
  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 +4 -3
  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 +3 -3
  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 +3 -3
  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 +3 -3
  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/api/open/route.js +1 -1
  33. package/.next/standalone/apps/web/.next/server/app/api/open/route.js.nft.json +1 -1
  34. package/.next/standalone/apps/web/.next/server/app/favicon.ico/route.js +1 -1
  35. package/.next/standalone/apps/web/.next/server/app/favicon.ico/route.js.nft.json +1 -1
  36. package/.next/standalone/apps/web/.next/server/app/index.html +1 -1
  37. package/.next/standalone/apps/web/.next/server/app/index.rsc +16 -15
  38. package/.next/standalone/apps/web/.next/server/app/index.segments/__PAGE__.segment.rsc +3 -3
  39. package/.next/standalone/apps/web/.next/server/app/index.segments/_full.segment.rsc +16 -15
  40. package/.next/standalone/apps/web/.next/server/app/index.segments/_head.segment.rsc +4 -4
  41. package/.next/standalone/apps/web/.next/server/app/index.segments/_index.segment.rsc +6 -5
  42. package/.next/standalone/apps/web/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  43. package/.next/standalone/apps/web/.next/server/app/page/react-loadable-manifest.json +1 -2
  44. package/.next/standalone/apps/web/.next/server/app/page.js +2 -2
  45. package/.next/standalone/apps/web/.next/server/app/page.js.nft.json +1 -1
  46. package/.next/standalone/apps/web/.next/server/app/page_client-reference-manifest.js +1 -1
  47. package/.next/standalone/apps/web/.next/server/chunks/[externals]_shiki_wasm_0~fgmgp._.js +3 -0
  48. package/.next/standalone/apps/web/.next/server/chunks/[root-of-the-server]__01.zj5h._.js +3 -0
  49. package/.next/standalone/apps/web/.next/server/chunks/[root-of-the-server]__05ejtyr._.js +3 -0
  50. package/.next/standalone/apps/web/.next/server/chunks/[root-of-the-server]__0e2dp4h._.js +3 -0
  51. package/.next/standalone/apps/web/.next/server/chunks/[root-of-the-server]__0egk6ui._.js +3 -0
  52. package/.next/standalone/apps/web/.next/server/chunks/[root-of-the-server]__0i6i-~n._.js +3 -0
  53. package/.next/standalone/apps/web/.next/server/chunks/[root-of-the-server]__0sv4hr9._.js +3 -0
  54. package/.next/standalone/apps/web/.next/server/chunks/[root-of-the-server]__0tbrp5x._.js +13 -0
  55. package/.next/standalone/apps/web/.next/server/chunks/_0r24f4c._.js +69 -0
  56. package/.next/standalone/apps/web/.next/server/chunks/node_modules_@pierre_theme_dist_pierre-dark_mjs_0ojo3_n._.js +3 -0
  57. package/.next/standalone/apps/web/.next/server/chunks/node_modules_@pierre_theme_dist_pierre-light_mjs_0pw9wwg._.js +3 -0
  58. package/.next/standalone/apps/web/.next/server/chunks/ssr/0fuv_@swc_helpers_cjs__interop_require_default_cjs_0ghzfn9._.js +3 -0
  59. package/.next/standalone/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0bit3~x._.js +3 -0
  60. package/.next/standalone/apps/web/.next/server/chunks/ssr/{[root-of-the-server]__06b81~v._.js → [root-of-the-server]__0giwc4b._.js} +2 -2
  61. package/.next/standalone/apps/web/.next/server/chunks/ssr/{apps_web_0b_ykcu._.js → _0m1v4-9._.js} +2 -2
  62. package/.next/standalone/apps/web/.next/server/chunks/ssr/_0oc3qg_._.js +3 -3
  63. package/.next/standalone/apps/web/.next/server/chunks/ssr/{apps_web_08kf15u._.js → apps_web_0758ax4._.js} +2 -2
  64. package/.next/standalone/apps/web/.next/server/middleware-build-manifest.js +3 -3
  65. package/.next/standalone/apps/web/.next/server/pages/404.html +1 -1
  66. package/.next/standalone/apps/web/.next/server/pages/500.html +1 -1
  67. package/.next/standalone/apps/web/.next/server/server-reference-manifest.js +1 -1
  68. package/.next/standalone/apps/web/.next/server/server-reference-manifest.json +1 -1
  69. package/.next/standalone/apps/web/.next/static/chunks/01f1ms~jsc5vh.js +138 -0
  70. package/.next/standalone/apps/web/.next/static/chunks/0c7yx0ttmhb4s.js +63 -0
  71. package/.next/standalone/apps/web/.next/static/chunks/0ogqv_xj2r0c6.js +1 -0
  72. package/.next/standalone/apps/web/.next/static/chunks/0qp8t.3t~v6um.js +1 -0
  73. package/.next/standalone/apps/web/.next/static/chunks/{0syypqto3~pe_.js → 0y0o261rjun_2.js} +8 -8
  74. package/.next/standalone/apps/web/.next/static/chunks/0y5z3t-z1c8ks.js.map +5 -0
  75. package/.next/standalone/apps/web/.next/static/chunks/130i667qy-j80.css +3 -0
  76. package/.next/standalone/apps/web/.next/static/chunks/15uwrard~z-l5.js +16 -0
  77. package/.next/standalone/apps/web/.next/static/chunks/17b.xoi.b6rcl.js +1 -0
  78. package/.next/standalone/apps/web/.next/static/chunks/turbopack-0_n_4n~_4no2a.js +1 -0
  79. package/.next/standalone/apps/web/.next/static/chunks/turbopack-worker-0sjn--fhq~1cg.js +1 -0
  80. package/.next/standalone/apps/web/.next/static/media/diffs.worker.09unk0quktc_5.ts +1 -0
  81. package/.next/standalone/apps/web/package.json +4 -4
  82. package/README.md +10 -3
  83. package/bin/diffhub.mjs +25 -1
  84. package/package.json +4 -4
  85. package/.next/standalone/apps/web/.next/server/chunks/[root-of-the-server]__05vwx85._.js +0 -3
  86. package/.next/standalone/apps/web/.next/server/chunks/[root-of-the-server]__09vmjc2._.js +0 -3
  87. package/.next/standalone/apps/web/.next/server/chunks/[root-of-the-server]__0etmu4u._.js +0 -3
  88. package/.next/standalone/apps/web/.next/server/chunks/[root-of-the-server]__0g-_a7n._.js +0 -3
  89. package/.next/standalone/apps/web/.next/server/chunks/[root-of-the-server]__0gf_xk6._.js +0 -13
  90. package/.next/standalone/apps/web/.next/server/chunks/[root-of-the-server]__0mshgfw._.js +0 -3
  91. package/.next/standalone/apps/web/.next/server/chunks/[root-of-the-server]__0qixima._.js +0 -3
  92. package/.next/standalone/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0f~hmsk._.js +0 -3
  93. package/.next/standalone/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0khyzju._.js +0 -3
  94. package/.next/standalone/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0l2mjim._.js +0 -70
  95. package/.next/standalone/apps/web/.next/server/chunks/ssr/_0kwaklj._.js +0 -3
  96. package/.next/standalone/apps/web/.next/static/chunks/0-ci0c9di0qo8.js +0 -16
  97. package/.next/standalone/apps/web/.next/static/chunks/0.ae0sud8tm7k.js +0 -204
  98. package/.next/standalone/apps/web/.next/static/chunks/07ndp5x2cvqo..css +0 -3
  99. package/.next/standalone/apps/web/.next/static/chunks/0di~ntk7iivm4.js +0 -1
  100. package/.next/standalone/apps/web/.next/static/chunks/0pklg0nmdvay8.js +0 -1
  101. package/.next/standalone/apps/web/.next/static/chunks/0up9_7hiwl_dt.js +0 -1
  102. package/.next/standalone/apps/web/.next/static/chunks/0~984a88e4rp9.js +0 -204
  103. package/.next/standalone/apps/web/.next/static/chunks/13y8355z8m13w.js +0 -1
  104. package/.next/standalone/apps/web/AGENTS.md +0 -60
  105. package/.next/standalone/apps/web/CHANGELOG.md +0 -7
  106. package/.next/standalone/apps/web/CLAUDE.md +0 -1
  107. package/.next/standalone/apps/web/README.md +0 -78
  108. package/.next/standalone/apps/web/app/api/comments/route.ts +0 -20
  109. package/.next/standalone/apps/web/app/api/diff/route.ts +0 -15
  110. package/.next/standalone/apps/web/app/api/discard/route.ts +0 -15
  111. package/.next/standalone/apps/web/app/api/file/route.ts +0 -17
  112. package/.next/standalone/apps/web/app/api/files/route.ts +0 -14
  113. package/.next/standalone/apps/web/app/api/open/route.ts +0 -64
  114. package/.next/standalone/apps/web/app/favicon.ico +0 -0
  115. package/.next/standalone/apps/web/app/globals.css +0 -214
  116. package/.next/standalone/apps/web/app/layout.tsx +0 -52
  117. package/.next/standalone/apps/web/app/page.tsx +0 -6
  118. package/.next/standalone/apps/web/bin/diffhub.mjs +0 -147
  119. package/.next/standalone/apps/web/components/ContextMenu.tsx +0 -161
  120. package/.next/standalone/apps/web/components/DiffApp.tsx +0 -419
  121. package/.next/standalone/apps/web/components/DiffViewer.tsx +0 -565
  122. package/.next/standalone/apps/web/components/FileDiffHeader.tsx +0 -119
  123. package/.next/standalone/apps/web/components/FileList.tsx +0 -455
  124. package/.next/standalone/apps/web/components/KeyboardShortcutsDialog.tsx +0 -79
  125. package/.next/standalone/apps/web/components/SidebarHelpMenu.tsx +0 -86
  126. package/.next/standalone/apps/web/components/StatusBar.tsx +0 -212
  127. package/.next/standalone/apps/web/components/icons/file-status-icons.tsx +0 -48
  128. package/.next/standalone/apps/web/components/theme-provider.tsx +0 -12
  129. package/.next/standalone/apps/web/components/ui/button.tsx +0 -90
  130. package/.next/standalone/apps/web/components/ui/empty.tsx +0 -82
  131. package/.next/standalone/apps/web/components/ui/input.tsx +0 -18
  132. package/.next/standalone/apps/web/components/ui/kbd.tsx +0 -14
  133. package/.next/standalone/apps/web/components/ui/separator.tsx +0 -23
  134. package/.next/standalone/apps/web/components/ui/sheet.tsx +0 -109
  135. package/.next/standalone/apps/web/components/ui/sidebar.tsx +0 -700
  136. package/.next/standalone/apps/web/components/ui/skeleton.tsx +0 -9
  137. package/.next/standalone/apps/web/components/ui/toggle.tsx +0 -35
  138. package/.next/standalone/apps/web/components/ui/tooltip.tsx +0 -52
  139. package/.next/standalone/apps/web/components.json +0 -27
  140. package/.next/standalone/apps/web/lib/comments.ts +0 -52
  141. package/.next/standalone/apps/web/lib/export-comments.ts +0 -13
  142. package/.next/standalone/apps/web/lib/git.ts +0 -201
  143. package/.next/standalone/apps/web/lib/use-mobile.ts +0 -19
  144. package/.next/standalone/apps/web/lib/utils.ts +0 -5
  145. package/.next/standalone/apps/web/next.config.ts +0 -19
  146. package/.next/standalone/apps/web/oxfmt.config.ts +0 -6
  147. package/.next/standalone/apps/web/oxlint.config.ts +0 -13
  148. package/.next/standalone/apps/web/postcss.config.mjs +0 -7
  149. package/.next/standalone/apps/web/public/file.svg +0 -1
  150. package/.next/standalone/apps/web/public/glide-variable-italic.woff2 +0 -0
  151. package/.next/standalone/apps/web/public/glide-variable.woff2 +0 -0
  152. package/.next/standalone/apps/web/public/globe.svg +0 -1
  153. package/.next/standalone/apps/web/public/next.svg +0 -1
  154. package/.next/standalone/apps/web/public/operator-mono-book-italic.woff2 +0 -0
  155. package/.next/standalone/apps/web/public/operator-mono-book.woff2 +0 -0
  156. package/.next/standalone/apps/web/public/operator-mono-medium-italic.woff2 +0 -0
  157. package/.next/standalone/apps/web/public/operator-mono-medium.woff2 +0 -0
  158. package/.next/standalone/apps/web/public/vercel.svg +0 -1
  159. package/.next/standalone/apps/web/public/window.svg +0 -1
  160. package/.next/standalone/apps/web/tsconfig.json +0 -34
  161. /package/.next/standalone/apps/web/.next/static/{ZhI_-YaFho-fQoajjgwSH → C1Ggd_ITpnb7yBHrIOODV}/_buildManifest.js +0 -0
  162. /package/.next/standalone/apps/web/.next/static/{ZhI_-YaFho-fQoajjgwSH → C1Ggd_ITpnb7yBHrIOODV}/_clientMiddlewareManifest.js +0 -0
  163. /package/.next/standalone/apps/web/.next/static/{ZhI_-YaFho-fQoajjgwSH → C1Ggd_ITpnb7yBHrIOODV}/_ssgManifest.js +0 -0
@@ -1,35 +0,0 @@
1
- import { Toggle as BaseToggle } from "@base-ui/react/toggle";
2
- import { cva } from "class-variance-authority";
3
- import type { VariantProps } from "class-variance-authority";
4
- import type * as React from "react";
5
- import { cn } from "@/lib/utils";
6
-
7
- const toggleVariants = cva(
8
- "inline-flex shrink-0 select-none items-center justify-center rounded-lg border border-transparent outline-none transition-[color,background-color,border-color,box-shadow,opacity] duration-150 ease-out focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring/50 focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:pointer-events-none disabled:opacity-45 [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0",
9
- {
10
- defaultVariants: {
11
- size: "icon-xs",
12
- variant: "ghost",
13
- },
14
- variants: {
15
- size: {
16
- "icon-sm": "size-9 rounded-[min(var(--radius-md),12px)]",
17
- "icon-xs":
18
- "size-8 rounded-[min(var(--radius-md),10px)] [&_svg:not([class*='size-'])]:size-3",
19
- },
20
- variant: {
21
- ghost:
22
- "hover:bg-muted hover:text-foreground data-[pressed]:bg-muted data-[pressed]:text-foreground",
23
- },
24
- },
25
- },
26
- );
27
-
28
- export const Toggle = ({
29
- className,
30
- variant,
31
- size,
32
- ...props
33
- }: React.ComponentProps<typeof BaseToggle> & VariantProps<typeof toggleVariants>) => (
34
- <BaseToggle className={cn(toggleVariants({ className, size, variant }))} {...props} />
35
- );
@@ -1,52 +0,0 @@
1
- "use client";
2
-
3
- import { Tooltip as TooltipPrimitive } from "@base-ui/react/tooltip";
4
-
5
- import { cn } from "@/lib/utils";
6
-
7
- const TooltipProvider = ({ delay = 0, ...props }: TooltipPrimitive.Provider.Props) => (
8
- <TooltipPrimitive.Provider data-slot="tooltip-provider" delay={delay} {...props} />
9
- );
10
-
11
- const Tooltip = ({ ...props }: TooltipPrimitive.Root.Props) => (
12
- <TooltipPrimitive.Root data-slot="tooltip" {...props} />
13
- );
14
-
15
- const TooltipTrigger = ({ ...props }: TooltipPrimitive.Trigger.Props) => (
16
- <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />
17
- );
18
-
19
- const TooltipContent = ({
20
- className,
21
- side = "top",
22
- sideOffset = 4,
23
- align = "center",
24
- alignOffset = 0,
25
- children,
26
- ...props
27
- }: TooltipPrimitive.Popup.Props &
28
- Pick<TooltipPrimitive.Positioner.Props, "align" | "alignOffset" | "side" | "sideOffset">) => (
29
- <TooltipPrimitive.Portal>
30
- <TooltipPrimitive.Positioner
31
- align={align}
32
- alignOffset={alignOffset}
33
- side={side}
34
- sideOffset={sideOffset}
35
- className="isolate z-50"
36
- >
37
- <TooltipPrimitive.Popup
38
- data-slot="tooltip-content"
39
- className={cn(
40
- "z-50 inline-flex w-fit max-w-xs origin-(--transform-origin) items-center gap-1.5 rounded-md bg-foreground px-3 py-1.5 text-xs text-background has-data-[slot=kbd]:pr-1.5 data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 **:data-[slot=kbd]:relative **:data-[slot=kbd]:isolate **:data-[slot=kbd]:z-50 **:data-[slot=kbd]:rounded-sm data-[state=delayed-open]:animate-in data-[state=delayed-open]:fade-in-0 data-[state=delayed-open]:zoom-in-95 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
41
- className,
42
- )}
43
- {...props}
44
- >
45
- {children}
46
- <TooltipPrimitive.Arrow className="z-50 size-2.5 translate-y-[calc(-50%-2px)] rotate-45 rounded-[2px] bg-foreground fill-foreground data-[side=bottom]:top-1 data-[side=inline-end]:top-1/2! data-[side=inline-end]:-left-1 data-[side=inline-end]:-translate-y-1/2 data-[side=inline-start]:top-1/2! data-[side=inline-start]:-right-1 data-[side=inline-start]:-translate-y-1/2 data-[side=left]:top-1/2! data-[side=left]:-right-1 data-[side=left]:-translate-y-1/2 data-[side=right]:top-1/2! data-[side=right]:-left-1 data-[side=right]:-translate-y-1/2 data-[side=top]:-bottom-2.5" />
47
- </TooltipPrimitive.Popup>
48
- </TooltipPrimitive.Positioner>
49
- </TooltipPrimitive.Portal>
50
- );
51
-
52
- export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };
@@ -1,27 +0,0 @@
1
- {
2
- "$schema": "https://ui.shadcn.com/schema.json",
3
- "style": "base-nova",
4
- "rsc": true,
5
- "tsx": true,
6
- "tailwind": {
7
- "config": "",
8
- "css": "app/globals.css",
9
- "baseColor": "neutral",
10
- "cssVariables": true,
11
- "prefix": ""
12
- },
13
- "iconLibrary": "lucide",
14
- "rtl": false,
15
- "aliases": {
16
- "components": "@/components",
17
- "utils": "@/lib/utils",
18
- "ui": "@/components/ui",
19
- "lib": "@/lib",
20
- "hooks": "@/hooks"
21
- },
22
- "menuColor": "default",
23
- "menuAccent": "subtle",
24
- "registries": {
25
- "@blode": "https://ui.blode.co/r/{name}.json"
26
- }
27
- }
@@ -1,52 +0,0 @@
1
- import { existsSync, readFileSync, writeFileSync } from "node:fs";
2
- import { join } from "node:path";
3
-
4
- const getCommentsPath = (): string => {
5
- const repoPath = process.env.DIFFHUB_REPO ?? process.cwd();
6
- return join(repoPath, ".git", "diffhub-comments.json");
7
- };
8
-
9
- export type CommentTag = "[must-fix]" | "[suggestion]" | "[nit]" | "[question]" | "";
10
-
11
- export interface Comment {
12
- id: string;
13
- file: string;
14
- lineNumber: number;
15
- side: "left" | "right";
16
- body: string;
17
- tag: CommentTag;
18
- createdAt: string;
19
- }
20
-
21
- export const readComments = (): Comment[] => {
22
- const path = getCommentsPath();
23
- if (!existsSync(path)) {
24
- return [];
25
- }
26
- try {
27
- return JSON.parse(readFileSync(path, "utf-8")) as Comment[];
28
- } catch {
29
- // empty
30
- return [];
31
- }
32
- };
33
-
34
- const saveComments = (comments: Comment[]): void => {
35
- writeFileSync(getCommentsPath(), JSON.stringify(comments, null, 2));
36
- };
37
-
38
- export const addComment = (data: Omit<Comment, "id" | "createdAt">): Comment => {
39
- const comments = readComments();
40
- const comment: Comment = {
41
- ...data,
42
- createdAt: new Date().toISOString(),
43
- id: crypto.randomUUID(),
44
- };
45
- comments.push(comment);
46
- saveComments(comments);
47
- return comment;
48
- };
49
-
50
- export const deleteComment = (id: string): void => {
51
- saveComments(readComments().filter((c) => c.id !== id));
52
- };
@@ -1,13 +0,0 @@
1
- import type { Comment } from "./comments";
2
-
3
- export const exportCommentsAsPrompt = (comments: Comment[]): string => {
4
- if (comments.length === 0) {
5
- return "No comments.";
6
- }
7
- const lines = comments.map((c) => {
8
- const tag = c.tag ? `${c.tag} ` : "";
9
- const loc = c.lineNumber > 0 ? `:${c.lineNumber}` : "";
10
- return `- ${tag}**${c.file}${loc}**: ${c.body}`;
11
- });
12
- return `## Code Review Comments\n\nPlease address the following:\n\n${lines.join("\n")}`;
13
- };
@@ -1,201 +0,0 @@
1
- import { simpleGit } from "simple-git";
2
- import type { SimpleGit } from "simple-git";
3
- import { readFileSync, statSync } from "node:fs";
4
- import { join } from "node:path";
5
-
6
- const REPO_POINTER = "/tmp/diffhub-active-repo";
7
-
8
- // TTL cache — avoids spawning git subprocesses on every poll
9
- const cache = new Map<string, { value: unknown; expires: number }>();
10
- let lastPointerMtime = 0;
11
-
12
- const bustCacheIfRepoChanged = () => {
13
- try {
14
- const mtime = statSync(REPO_POINTER).mtimeMs;
15
- if (mtime !== lastPointerMtime) {
16
- lastPointerMtime = mtime;
17
- cache.clear();
18
- }
19
- } catch {
20
- // empty
21
- }
22
- };
23
-
24
- const cached = async <T>(key: string, ttlMs: number, fn: () => Promise<T>): Promise<T> => {
25
- bustCacheIfRepoChanged();
26
- const hit = cache.get(key);
27
- if (hit && hit.expires > Date.now()) {
28
- return hit.value as T;
29
- }
30
- const value = await fn();
31
- cache.set(key, { expires: Date.now() + ttlMs, value });
32
- return value;
33
- };
34
-
35
- const getRepoPath = (): string => {
36
- // Temp file from diffhub-point takes priority (dev workflow)
37
- try {
38
- const p = readFileSync(REPO_POINTER, "utf-8").trim();
39
- if (p) {
40
- return p;
41
- }
42
- } catch {
43
- // empty
44
- }
45
- // Fallback: env var (set by CLI in production, or .env.local)
46
- return process.env.DIFFHUB_REPO ?? process.cwd();
47
- };
48
-
49
- const git = (): SimpleGit => simpleGit(getRepoPath());
50
-
51
- export const getBaseBranch = (): Promise<string> => {
52
- const repoPath = getRepoPath();
53
- return cached(`baseBranch:${repoPath}`, 30_000, async () => {
54
- const g = git();
55
- const remotes = await g.branch(["-r"]);
56
- for (const name of ["main", "master", "develop", "dev"]) {
57
- if (remotes.all.includes(`origin/${name}`)) {
58
- return `origin/${name}`;
59
- }
60
- }
61
- const local = await g.branchLocal();
62
- for (const name of ["main", "master", "develop", "dev"]) {
63
- if (local.all.includes(name)) {
64
- return name;
65
- }
66
- }
67
- return "origin/main";
68
- });
69
- };
70
-
71
- export const getMergeBase = (baseBranch: string): Promise<string> => {
72
- const repoPath = getRepoPath();
73
- return cached(`mergeBase:${repoPath}:${baseBranch}`, 30_000, async () => {
74
- const g = git();
75
- const mb = await g.raw(["merge-base", "HEAD", baseBranch]);
76
- return mb.trim();
77
- });
78
- };
79
-
80
- export interface DiffResult {
81
- patch: string;
82
- baseBranch: string;
83
- mergeBase: string;
84
- branch: string;
85
- }
86
-
87
- export const getDiff = (base?: string, mode?: "uncommitted"): Promise<DiffResult> => {
88
- const repoPath = getRepoPath();
89
- return cached(`diff:${repoPath}:${base ?? ""}:${mode ?? ""}`, 2000, async () => {
90
- const g = git();
91
- const raw = await g.revparse(["--abbrev-ref", "HEAD"]);
92
- const branch = raw.trim();
93
- if (mode === "uncommitted") {
94
- const patch = await g.diff(["HEAD"]);
95
- return { baseBranch: "HEAD", branch, mergeBase: "HEAD", patch };
96
- }
97
- const baseBranch = base ?? (await getBaseBranch());
98
- const mergeBase = await getMergeBase(baseBranch);
99
- const patch = await g.diff([mergeBase]);
100
- return { baseBranch, branch, mergeBase, patch };
101
- });
102
- };
103
-
104
- export const getDiffForFile = (
105
- file: string,
106
- base?: string,
107
- mode?: "uncommitted",
108
- ): Promise<DiffResult> => {
109
- const repoPath = getRepoPath();
110
- return cached(`diff:${repoPath}:${base ?? ""}:${mode ?? ""}:${file}`, 2000, async () => {
111
- const g = git();
112
- const raw = await g.revparse(["--abbrev-ref", "HEAD"]);
113
- const branch = raw.trim();
114
- if (mode === "uncommitted") {
115
- const patch = await g.diff(["HEAD", "--", file]);
116
- return { baseBranch: "HEAD", branch, mergeBase: "HEAD", patch };
117
- }
118
- const baseBranch = base ?? (await getBaseBranch());
119
- const mergeBase = await getMergeBase(baseBranch);
120
- const patch = await g.diff([mergeBase, "--", file]);
121
- return { baseBranch, branch, mergeBase, patch };
122
- });
123
- };
124
-
125
- export interface DiffFileStat {
126
- file: string;
127
- changes: number;
128
- insertions: number;
129
- deletions: number;
130
- binary: boolean;
131
- }
132
-
133
- export interface DiffStatsResult {
134
- files: DiffFileStat[];
135
- insertions: number;
136
- deletions: number;
137
- branch: string;
138
- baseBranch: string;
139
- }
140
-
141
- export const getDiffStats = (base?: string, mode?: "uncommitted"): Promise<DiffStatsResult> => {
142
- const repoPath = getRepoPath();
143
- return cached(`stats:${repoPath}:${base ?? ""}:${mode ?? ""}`, 2000, async () => {
144
- const g = git();
145
- const raw = await g.revparse(["--abbrev-ref", "HEAD"]);
146
- const branch = raw.trim();
147
- if (mode === "uncommitted") {
148
- const baseBranch = "HEAD";
149
- const summary = await g.diffSummary(["HEAD"]);
150
- return {
151
- baseBranch,
152
- branch,
153
- deletions: summary.deletions,
154
- files: summary.files as DiffFileStat[],
155
- insertions: summary.insertions,
156
- };
157
- }
158
- const baseBranch = base ?? (await getBaseBranch());
159
- const mergeBase = await getMergeBase(baseBranch);
160
- const summary = await g.diffSummary([mergeBase]);
161
- return {
162
- baseBranch,
163
- branch,
164
- deletions: summary.deletions,
165
- files: summary.files as DiffFileStat[],
166
- insertions: summary.insertions,
167
- };
168
- });
169
- };
170
-
171
- /**
172
- * Return the content of a file at a specific git ref.
173
- * Pass "WORKING_TREE" to read the current working-tree copy.
174
- * Returns an empty string if the file doesn't exist at that ref (new/deleted files).
175
- */
176
- export const getFileAtRef = (filePath: string, ref: string): Promise<string> => {
177
- const repoPath = getRepoPath();
178
- if (ref === "WORKING_TREE") {
179
- try {
180
- return Promise.resolve(readFileSync(join(repoPath, filePath), "utf-8"));
181
- } catch {
182
- // empty
183
- return Promise.resolve("");
184
- }
185
- }
186
- return cached(`file:${repoPath}:${ref}:${filePath}`, 30_000, async () => {
187
- try {
188
- return await git().show([`${ref}:${filePath}`]);
189
- } catch {
190
- // empty
191
- return "";
192
- }
193
- });
194
- };
195
-
196
- /** Discard all uncommitted changes to a file (staged + working tree). */
197
- export const discardFile = async (file: string): Promise<void> => {
198
- const g = git();
199
- // restore --staged --worktree handles both staged and unstaged changes
200
- await g.raw(["restore", "--staged", "--worktree", "--", file]);
201
- };
@@ -1,19 +0,0 @@
1
- import { useEffect, useState } from "react";
2
-
3
- const MOBILE_BREAKPOINT = 768;
4
-
5
- export const useIsMobile = () => {
6
- const [isMobile, setIsMobile] = useState<boolean | undefined>();
7
-
8
- useEffect(() => {
9
- const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`);
10
- const onChange = () => {
11
- setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
12
- };
13
- mql.addEventListener("change", onChange);
14
- setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
15
- return () => mql.removeEventListener("change", onChange);
16
- }, []);
17
-
18
- return !!isMobile;
19
- };
@@ -1,5 +0,0 @@
1
- import { clsx } from "clsx";
2
- import type { ClassValue } from "clsx";
3
- import { twMerge } from "tailwind-merge";
4
-
5
- export const cn = (...inputs: ClassValue[]) => twMerge(clsx(inputs));
@@ -1,19 +0,0 @@
1
- import type { NextConfig } from "next";
2
- import { join } from "node:path";
3
-
4
- const nextConfig: NextConfig = {
5
- allowedDevOrigins: ["diffhub.localhost", "*.diffhub.localhost"],
6
- logging: {
7
- browserToTerminal: true,
8
- },
9
- // Standalone output: produces .next/standalone/server.js — a self-contained server
10
- // that works without a full Next.js install. Shipped in the npm package.
11
- output: "standalone",
12
- // Monorepo: point tracing root at the repo root so Next.js can resolve packages
13
- // hoisted to the root node_modules. The standalone output lands at
14
- // .next/standalone/apps/web/server.js (mirroring the workspace path).
15
- outputFileTracingRoot: join(import.meta.dirname, "../.."),
16
- reactCompiler: true,
17
- };
18
-
19
- export default nextConfig;
@@ -1,6 +0,0 @@
1
- import { defineConfig } from "oxfmt";
2
- import ultracite from "ultracite/oxfmt";
3
-
4
- export default defineConfig({
5
- extends: [ultracite],
6
- });
@@ -1,13 +0,0 @@
1
- import { defineConfig } from "oxlint";
2
- import core from "ultracite/oxlint/core";
3
- import next from "ultracite/oxlint/next";
4
- import react from "ultracite/oxlint/react";
5
- import vitest from "ultracite/oxlint/vitest";
6
-
7
- export default defineConfig({
8
- extends: [core, react, next, vitest],
9
- rules: {
10
- // React components use PascalCase filenames by convention
11
- "unicorn/filename-case": ["error", { cases: { kebabCase: true, pascalCase: true } }],
12
- },
13
- });
@@ -1,7 +0,0 @@
1
- const config = {
2
- plugins: {
3
- "@tailwindcss/postcss": {},
4
- },
5
- };
6
-
7
- export default config;
@@ -1 +0,0 @@
1
- <svg fill="none" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M14.5 13.5V5.41a1 1 0 0 0-.3-.7L9.8.29A1 1 0 0 0 9.08 0H1.5v13.5A2.5 2.5 0 0 0 4 16h8a2.5 2.5 0 0 0 2.5-2.5m-1.5 0v-7H8v-5H3v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1M9.5 5V2.12L12.38 5zM5.13 5h-.62v1.25h2.12V5zm-.62 3h7.12v1.25H4.5zm.62 3h-.62v1.25h7.12V11z" clip-rule="evenodd" fill="#666" fill-rule="evenodd"/></svg>
@@ -1 +0,0 @@
1
- <svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><g clip-path="url(#a)"><path fill-rule="evenodd" clip-rule="evenodd" d="M10.27 14.1a6.5 6.5 0 0 0 3.67-3.45q-1.24.21-2.7.34-.31 1.83-.97 3.1M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16m.48-1.52a7 7 0 0 1-.96 0H7.5a4 4 0 0 1-.84-1.32q-.38-.89-.63-2.08a40 40 0 0 0 3.92 0q-.25 1.2-.63 2.08a4 4 0 0 1-.84 1.31zm2.94-4.76q1.66-.15 2.95-.43a7 7 0 0 0 0-2.58q-1.3-.27-2.95-.43a18 18 0 0 1 0 3.44m-1.27-3.54a17 17 0 0 1 0 3.64 39 39 0 0 1-4.3 0 17 17 0 0 1 0-3.64 39 39 0 0 1 4.3 0m1.1-1.17q1.45.13 2.69.34a6.5 6.5 0 0 0-3.67-3.44q.65 1.26.98 3.1M8.48 1.5l.01.02q.41.37.84 1.31.38.89.63 2.08a40 40 0 0 0-3.92 0q.25-1.2.63-2.08a4 4 0 0 1 .85-1.32 7 7 0 0 1 .96 0m-2.75.4a6.5 6.5 0 0 0-3.67 3.44 29 29 0 0 1 2.7-.34q.31-1.83.97-3.1M4.58 6.28q-1.66.16-2.95.43a7 7 0 0 0 0 2.58q1.3.27 2.95.43a18 18 0 0 1 0-3.44m.17 4.71q-1.45-.12-2.69-.34a6.5 6.5 0 0 0 3.67 3.44q-.65-1.27-.98-3.1" fill="#666"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h16v16H0z"/></clipPath></defs></svg>
@@ -1 +0,0 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>
@@ -1 +0,0 @@
1
- <svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1155 1000"><path d="m577.3 0 577.4 1000H0z" fill="#fff"/></svg>
@@ -1 +0,0 @@
1
- <svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill-rule="evenodd" clip-rule="evenodd" d="M1.5 2.5h13v10a1 1 0 0 1-1 1h-11a1 1 0 0 1-1-1zM0 1h16v11.5a2.5 2.5 0 0 1-2.5 2.5h-11A2.5 2.5 0 0 1 0 12.5zm3.75 4.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5M7 4.75a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0m1.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5" fill="#666"/></svg>
@@ -1,34 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2017",
4
- "lib": ["dom", "dom.iterable", "esnext"],
5
- "allowJs": true,
6
- "skipLibCheck": true,
7
- "strict": true,
8
- "noEmit": true,
9
- "esModuleInterop": true,
10
- "module": "esnext",
11
- "moduleResolution": "bundler",
12
- "resolveJsonModule": true,
13
- "isolatedModules": true,
14
- "jsx": "react-jsx",
15
- "incremental": true,
16
- "plugins": [
17
- {
18
- "name": "next"
19
- }
20
- ],
21
- "paths": {
22
- "@/*": ["./*"]
23
- }
24
- },
25
- "include": [
26
- "next-env.d.ts",
27
- "**/*.ts",
28
- "**/*.tsx",
29
- ".next/types/**/*.ts",
30
- ".next/dev/types/**/*.ts",
31
- "**/*.mts"
32
- ],
33
- "exclude": ["node_modules"]
34
- }