@peaske7/readit 0.1.8 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (221) hide show
  1. package/.claude/CLAUDE.md +118 -76
  2. package/.claude/commands/review.md +1 -1
  3. package/.claude/roadmap.md +32 -9
  4. package/.claude/user-stories.md +100 -15
  5. package/AGENTS.md +30 -26
  6. package/Makefile +32 -0
  7. package/README.md +90 -5
  8. package/biome.json +18 -8
  9. package/bun.lock +426 -710
  10. package/bunfig.toml +2 -0
  11. package/docs/perf-baseline.md +130 -0
  12. package/docs/superpowers/plans/2026-03-26-surgical-pruning.md +1176 -0
  13. package/docs/superpowers/specs/2026-03-27-go-server-rewrite-design.md +284 -0
  14. package/e2e/comments.spec.ts +14 -58
  15. package/e2e/document-load.spec.ts +1 -23
  16. package/e2e/export.spec.ts +4 -4
  17. package/e2e/perf/add-comment.spec.ts +116 -0
  18. package/e2e/perf/fixtures/generate.ts +327 -0
  19. package/e2e/perf/initial-load.spec.ts +49 -0
  20. package/e2e/perf/perf.setup.ts +23 -0
  21. package/e2e/perf/perf.teardown.ts +9 -0
  22. package/e2e/perf/screenshot-final.png +0 -0
  23. package/e2e/perf/scroll.spec.ts +39 -0
  24. package/e2e/perf/tab-switch.spec.ts +69 -0
  25. package/e2e/perf/text-selection.spec.ts +119 -0
  26. package/e2e/perf/utils/metrics.ts +350 -0
  27. package/e2e/perf/utils/perf-cli.ts +86 -0
  28. package/e2e/persistence-file.spec.ts +41 -26
  29. package/e2e/utils/selection.ts +17 -73
  30. package/go/cmd/readit/main.go +416 -0
  31. package/go/go.mod +20 -0
  32. package/go/go.sum +41 -0
  33. package/go/internal/server/anchor.go +302 -0
  34. package/go/internal/server/anchor_test.go +111 -0
  35. package/go/internal/server/comments.go +390 -0
  36. package/go/internal/server/documents.go +113 -0
  37. package/go/internal/server/embed.go +17 -0
  38. package/go/internal/server/headings.go +33 -0
  39. package/go/internal/server/headings_test.go +75 -0
  40. package/go/internal/server/htmltext.go +123 -0
  41. package/go/internal/server/markdown.go +157 -0
  42. package/go/internal/server/markdown_bench_test.go +42 -0
  43. package/go/internal/server/markdown_test.go +79 -0
  44. package/go/internal/server/server.go +453 -0
  45. package/go/internal/server/server_bench_test.go +122 -0
  46. package/go/internal/server/settings.go +110 -0
  47. package/go/internal/server/sse.go +140 -0
  48. package/go/internal/server/storage.go +275 -0
  49. package/go/internal/server/storage_test.go +118 -0
  50. package/go/internal/server/template.go +66 -0
  51. package/go/internal/server/types.go +101 -0
  52. package/go/internal/server/watcher.go +74 -0
  53. package/index.html +4 -14
  54. package/nvim-readit/lua/readit/health.lua +64 -0
  55. package/nvim-readit/lua/readit/init.lua +463 -0
  56. package/nvim-readit/plugin/readit.lua +19 -0
  57. package/package.json +24 -41
  58. package/playwright.config.ts +12 -0
  59. package/shell/_readit +158 -0
  60. package/shell/readit.zsh +87 -0
  61. package/src/App.svelte +881 -0
  62. package/src/{cli/index.ts → cli.ts} +216 -70
  63. package/src/components/ActionsMenu.svelte +95 -0
  64. package/src/components/CommentBadge.svelte +67 -0
  65. package/src/components/CommentErrorBanner.svelte +33 -0
  66. package/src/components/CommentInput.svelte +75 -0
  67. package/src/components/CommentListItem.svelte +95 -0
  68. package/src/components/CommentManager.svelte +129 -0
  69. package/src/components/CommentNav.svelte +109 -0
  70. package/src/components/DocumentViewer.svelte +218 -0
  71. package/src/components/FloatingComment.svelte +107 -0
  72. package/src/components/Header.svelte +76 -0
  73. package/src/components/InlineEditor.svelte +72 -0
  74. package/src/components/MarginNote.svelte +167 -0
  75. package/src/components/MarginNotesContainer.svelte +33 -0
  76. package/src/components/RawModal.svelte +126 -0
  77. package/src/components/ReanchorConfirm.svelte +30 -0
  78. package/src/components/SettingsModal.svelte +220 -0
  79. package/src/components/ShortcutCapture.svelte +82 -0
  80. package/src/components/ShortcutList.svelte +145 -0
  81. package/src/components/TabBar.svelte +52 -0
  82. package/src/components/TableOfContents.svelte +125 -0
  83. package/src/components/ui/ActionLink.svelte +40 -0
  84. package/src/components/ui/Button.svelte +53 -0
  85. package/src/components/ui/Dialog.svelte +97 -0
  86. package/src/components/ui/DropdownMenu.svelte +85 -0
  87. package/src/components/ui/DropdownMenuItem.svelte +38 -0
  88. package/src/components/ui/DropdownMenuSeparator.svelte +11 -0
  89. package/src/components/ui/Text.svelte +42 -0
  90. package/src/env.d.ts +6 -0
  91. package/src/index.css +36 -166
  92. package/src/lib/__fixtures__/bench-data.ts +1 -54
  93. package/src/lib/anchor.bench.ts +47 -68
  94. package/src/lib/anchor.test.ts +5 -9
  95. package/src/lib/anchor.ts +9 -93
  96. package/src/lib/comment-storage.bench.ts +6 -20
  97. package/src/lib/comment-storage.test.ts +45 -37
  98. package/src/lib/comment-storage.ts +23 -64
  99. package/src/lib/export.bench.ts +9 -23
  100. package/src/lib/export.ts +7 -14
  101. package/src/lib/headings.test.ts +103 -0
  102. package/src/lib/headings.ts +44 -0
  103. package/src/lib/highlight/core.test.ts +1 -6
  104. package/src/lib/highlight/dom.ts +53 -280
  105. package/src/lib/highlight/highlight-registry.ts +221 -0
  106. package/src/lib/highlight/highlight.bench.ts +92 -0
  107. package/src/lib/highlight/highlighter.ts +122 -302
  108. package/src/lib/highlight/{core.ts → resolver.ts} +3 -19
  109. package/src/lib/highlight/types.ts +0 -40
  110. package/src/lib/html-text.test.ts +162 -0
  111. package/src/lib/html-text.ts +161 -0
  112. package/src/lib/i18n/en.ts +13 -36
  113. package/src/lib/i18n/ja.ts +14 -37
  114. package/src/lib/i18n/types.ts +13 -36
  115. package/src/lib/margin-layout.bench.ts +48 -15
  116. package/src/lib/margin-layout.ts +2 -31
  117. package/src/lib/markdown-renderer.test.ts +154 -0
  118. package/src/lib/markdown-renderer.ts +177 -0
  119. package/src/lib/mermaid-config.ts +38 -0
  120. package/src/lib/mermaid-renderer.ts +162 -0
  121. package/src/lib/mermaid-worker.ts +60 -0
  122. package/src/lib/positions.ts +157 -0
  123. package/src/lib/shortcut-registry.ts +138 -103
  124. package/src/lib/utils.ts +2 -48
  125. package/src/main.ts +16 -0
  126. package/src/schema.ts +92 -0
  127. package/src/{server/index.ts → server.ts} +427 -163
  128. package/src/stores/app.svelte.ts +231 -0
  129. package/src/stores/locale.svelte.ts +46 -0
  130. package/src/stores/settings.svelte.ts +90 -0
  131. package/src/stores/shortcuts.svelte.ts +104 -0
  132. package/src/stores/ui.svelte.ts +12 -0
  133. package/src/template.ts +104 -0
  134. package/src/test-setup.ts +47 -0
  135. package/svelte.config.js +5 -0
  136. package/tsconfig.json +2 -2
  137. package/vite.config.ts +31 -3
  138. package/vscode-readit/.mcp.json +7 -0
  139. package/vscode-readit/.vscodeignore +7 -0
  140. package/vscode-readit/bun.lock +78 -0
  141. package/vscode-readit/icon.svg +10 -0
  142. package/vscode-readit/package.json +110 -0
  143. package/vscode-readit/src/extension.ts +117 -0
  144. package/vscode-readit/src/server-manager.ts +272 -0
  145. package/vscode-readit/src/webview-provider.ts +204 -0
  146. package/vscode-readit/tsconfig.json +20 -0
  147. package/e2e/fixtures/sample.html +0 -13
  148. package/src/App.tsx +0 -416
  149. package/src/components/ActionsMenu.tsx +0 -112
  150. package/src/components/DocumentViewer/CodeBlock.tsx +0 -160
  151. package/src/components/DocumentViewer/DocumentViewer.tsx +0 -259
  152. package/src/components/DocumentViewer/IframeContainer.tsx +0 -251
  153. package/src/components/DocumentViewer/InlineCode.tsx +0 -60
  154. package/src/components/DocumentViewer/MermaidDiagram.tsx +0 -137
  155. package/src/components/DocumentViewer/index.ts +0 -1
  156. package/src/components/FloatingTOC.tsx +0 -61
  157. package/src/components/Header.tsx +0 -65
  158. package/src/components/InlineEditor.tsx +0 -74
  159. package/src/components/MarginNote.tsx +0 -207
  160. package/src/components/MarginNotes.tsx +0 -50
  161. package/src/components/RawModal.tsx +0 -143
  162. package/src/components/ReanchorConfirm.tsx +0 -36
  163. package/src/components/SettingsModal.tsx +0 -310
  164. package/src/components/ShortcutCapture.tsx +0 -48
  165. package/src/components/ShortcutList.tsx +0 -198
  166. package/src/components/TabBar.tsx +0 -60
  167. package/src/components/TableOfContents.tsx +0 -108
  168. package/src/components/comments/CommentBadge.tsx +0 -49
  169. package/src/components/comments/CommentInput.tsx +0 -114
  170. package/src/components/comments/CommentListItem.tsx +0 -92
  171. package/src/components/comments/CommentManager.tsx +0 -113
  172. package/src/components/comments/CommentMinimap.tsx +0 -62
  173. package/src/components/comments/CommentNav.tsx +0 -109
  174. package/src/components/ui/ActionBar.tsx +0 -16
  175. package/src/components/ui/ActionLink.tsx +0 -32
  176. package/src/components/ui/Button.tsx +0 -55
  177. package/src/components/ui/Dialog.tsx +0 -156
  178. package/src/components/ui/DropdownMenu.tsx +0 -114
  179. package/src/components/ui/SeparatorDot.tsx +0 -9
  180. package/src/components/ui/Text.tsx +0 -54
  181. package/src/contexts/CommentContext.tsx +0 -229
  182. package/src/contexts/LayoutContext.tsx +0 -88
  183. package/src/contexts/LocaleContext.tsx +0 -35
  184. package/src/hooks/useClickOutside.ts +0 -35
  185. package/src/hooks/useClipboard.ts +0 -82
  186. package/src/hooks/useCommentNavigation.ts +0 -130
  187. package/src/hooks/useComments.ts +0 -323
  188. package/src/hooks/useDocument.ts +0 -156
  189. package/src/hooks/useEditorScheme.ts +0 -51
  190. package/src/hooks/useFontPreference.ts +0 -59
  191. package/src/hooks/useHeadings.test.ts +0 -159
  192. package/src/hooks/useHeadings.ts +0 -129
  193. package/src/hooks/useKeybindings.ts +0 -108
  194. package/src/hooks/useKeyboardShortcuts.ts +0 -63
  195. package/src/hooks/useLayoutMode.ts +0 -44
  196. package/src/hooks/useLocalePreference.ts +0 -42
  197. package/src/hooks/useReanchorMode.ts +0 -33
  198. package/src/hooks/useScrollMetrics.ts +0 -56
  199. package/src/hooks/useScrollSpy.ts +0 -81
  200. package/src/hooks/useTextSelection.ts +0 -123
  201. package/src/hooks/useThemePreference.ts +0 -66
  202. package/src/lib/context.bench.ts +0 -41
  203. package/src/lib/context.test.ts +0 -224
  204. package/src/lib/context.ts +0 -193
  205. package/src/lib/editor-links.ts +0 -59
  206. package/src/lib/highlight/colors.ts +0 -37
  207. package/src/lib/highlight/index.ts +0 -23
  208. package/src/lib/highlight/script-builder.ts +0 -485
  209. package/src/lib/html-processor.test.tsx +0 -170
  210. package/src/lib/html-processor.tsx +0 -95
  211. package/src/lib/i18n/completeness.test.ts +0 -51
  212. package/src/lib/i18n/translations.test.ts +0 -39
  213. package/src/lib/layout-constants.ts +0 -12
  214. package/src/lib/scroll.test.ts +0 -118
  215. package/src/lib/scroll.ts +0 -47
  216. package/src/lib/shortcut-registry.test.ts +0 -173
  217. package/src/lib/utils.test.ts +0 -110
  218. package/src/main.tsx +0 -13
  219. package/src/store/index.test.ts +0 -242
  220. package/src/store/index.ts +0 -254
  221. package/src/types/index.ts +0 -127
@@ -1,32 +0,0 @@
1
- import { cva, type VariantProps } from "class-variance-authority";
2
- import { cn } from "../../lib/utils";
3
-
4
- const actionLinkVariants = cva(
5
- "cursor-pointer transition-colors duration-150",
6
- {
7
- variants: {
8
- variant: {
9
- default: "hover:text-zinc-600",
10
- destructive: "hover:text-red-500",
11
- },
12
- },
13
- defaultVariants: { variant: "default" },
14
- },
15
- );
16
-
17
- function ActionLink({
18
- className,
19
- variant,
20
- ...props
21
- }: React.ComponentProps<"button"> & VariantProps<typeof actionLinkVariants>) {
22
- return (
23
- <button
24
- type="button"
25
- data-slot="action-link"
26
- className={cn(actionLinkVariants({ variant, className }))}
27
- {...props}
28
- />
29
- );
30
- }
31
-
32
- export { ActionLink, actionLinkVariants };
@@ -1,55 +0,0 @@
1
- import { Slot } from "@radix-ui/react-slot";
2
- import { cva, type VariantProps } from "class-variance-authority";
3
- import { cn } from "../../lib/utils";
4
-
5
- const buttonVariants = cva(
6
- "inline-flex items-center justify-center gap-2 rounded-lg text-sm font-medium transition-colors duration-150 active:scale-[0.98] disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0",
7
- {
8
- variants: {
9
- variant: {
10
- default: "bg-blue-600 text-white hover:bg-blue-700",
11
- secondary:
12
- "bg-zinc-100 dark:bg-zinc-800 text-zinc-900 dark:text-zinc-100 hover:bg-zinc-200 dark:hover:bg-zinc-700",
13
- outline:
14
- "border border-zinc-200 dark:border-zinc-700 bg-white dark:bg-zinc-900 text-zinc-700 dark:text-zinc-300 hover:bg-zinc-50 dark:hover:bg-zinc-800",
15
- ghost:
16
- "text-zinc-600 dark:text-zinc-400 hover:bg-zinc-100 dark:hover:bg-zinc-800 hover:text-zinc-900 dark:hover:text-zinc-100",
17
- destructive: "bg-red-600 text-white hover:bg-red-700",
18
- link: "text-zinc-600 dark:text-zinc-400 underline-offset-4 hover:underline",
19
- },
20
- size: {
21
- default: "h-9 px-4",
22
- sm: "h-8 px-3 text-xs",
23
- lg: "h-10 px-6",
24
- icon: "size-9",
25
- },
26
- },
27
- defaultVariants: {
28
- variant: "default",
29
- size: "default",
30
- },
31
- },
32
- );
33
-
34
- function Button({
35
- className,
36
- variant,
37
- size,
38
- asChild = false,
39
- ...props
40
- }: React.ComponentProps<"button"> &
41
- VariantProps<typeof buttonVariants> & {
42
- asChild?: boolean;
43
- }) {
44
- const Comp = asChild ? Slot : "button";
45
-
46
- return (
47
- <Comp
48
- data-slot="button"
49
- className={cn(buttonVariants({ variant, size, className }))}
50
- {...props}
51
- />
52
- );
53
- }
54
-
55
- export { Button, buttonVariants };
@@ -1,156 +0,0 @@
1
- import * as DialogPrimitive from "@radix-ui/react-dialog";
2
- import { X } from "lucide-react";
3
- import { use } from "react";
4
- import { LayoutContext } from "../../contexts/LayoutContext";
5
- import { cn } from "../../lib/utils";
6
- import { FontFamilies } from "../../types";
7
- import { buttonVariants } from "./Button";
8
- import { textVariants } from "./Text";
9
-
10
- function Dialog({
11
- ...props
12
- }: React.ComponentProps<typeof DialogPrimitive.Root>) {
13
- return <DialogPrimitive.Root data-slot="dialog" {...props} />;
14
- }
15
-
16
- function DialogTrigger({
17
- ...props
18
- }: React.ComponentProps<typeof DialogPrimitive.Trigger>) {
19
- return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />;
20
- }
21
-
22
- function DialogPortal({
23
- ...props
24
- }: React.ComponentProps<typeof DialogPrimitive.Portal>) {
25
- return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />;
26
- }
27
-
28
- function DialogClose({
29
- ...props
30
- }: React.ComponentProps<typeof DialogPrimitive.Close>) {
31
- return <DialogPrimitive.Close data-slot="dialog-close" {...props} />;
32
- }
33
-
34
- function DialogOverlay({
35
- className,
36
- ...props
37
- }: React.ComponentProps<typeof DialogPrimitive.Overlay>) {
38
- return (
39
- <DialogPrimitive.Overlay
40
- data-slot="dialog-overlay"
41
- className={cn(
42
- "fixed inset-0 z-50 bg-black/20 dark:bg-black/40 backdrop-blur-sm",
43
- className,
44
- )}
45
- {...props}
46
- />
47
- );
48
- }
49
-
50
- function DialogContent({
51
- className,
52
- children,
53
- showCloseButton = true,
54
- ...props
55
- }: React.ComponentProps<typeof DialogPrimitive.Content> & {
56
- showCloseButton?: boolean;
57
- }) {
58
- return (
59
- <DialogPortal>
60
- <DialogOverlay />
61
- <DialogPrimitive.Content
62
- data-slot="dialog-content"
63
- className={cn(
64
- "fixed top-1/2 left-1/2 z-50 w-full -translate-x-1/2 -translate-y-1/2",
65
- "bg-white/95 dark:bg-zinc-900/95 backdrop-blur-sm shadow-lg border border-zinc-200/40 dark:border-zinc-700/40 rounded-xl",
66
- "flex flex-col animate-in",
67
- className,
68
- )}
69
- {...props}
70
- >
71
- {children}
72
- {showCloseButton && (
73
- <DialogPrimitive.Close
74
- data-slot="dialog-close"
75
- className={cn(
76
- buttonVariants({ variant: "ghost", size: "icon" }),
77
- "absolute top-3 right-3 size-7",
78
- )}
79
- >
80
- <X className="w-4 h-4" />
81
- <span className="sr-only">Close</span>
82
- </DialogPrimitive.Close>
83
- )}
84
- </DialogPrimitive.Content>
85
- </DialogPortal>
86
- );
87
- }
88
-
89
- function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
90
- return (
91
- <div
92
- data-slot="dialog-header"
93
- className={cn(
94
- "flex items-center justify-between pl-4 pr-12 py-3 border-b border-zinc-100 dark:border-zinc-800",
95
- className,
96
- )}
97
- {...props}
98
- />
99
- );
100
- }
101
-
102
- function DialogTitle({
103
- className,
104
- ...props
105
- }: React.ComponentProps<typeof DialogPrimitive.Title>) {
106
- const layout = use(LayoutContext);
107
- const fontClass = layout
108
- ? layout.fontFamily === FontFamilies.SANS_SERIF
109
- ? "font-sans"
110
- : "font-serif"
111
- : undefined;
112
-
113
- return (
114
- <DialogPrimitive.Title
115
- data-slot="dialog-title"
116
- className={cn(textVariants({ variant: "section" }), fontClass, className)}
117
- {...props}
118
- />
119
- );
120
- }
121
-
122
- function DialogDescription({
123
- className,
124
- ...props
125
- }: React.ComponentProps<typeof DialogPrimitive.Description>) {
126
- return (
127
- <DialogPrimitive.Description
128
- data-slot="dialog-description"
129
- className={cn(textVariants({ variant: "caption" }), "text-sm", className)}
130
- {...props}
131
- />
132
- );
133
- }
134
-
135
- function DialogBody({ className, ...props }: React.ComponentProps<"div">) {
136
- return (
137
- <div
138
- data-slot="dialog-body"
139
- className={cn("flex-1 overflow-auto p-4", className)}
140
- {...props}
141
- />
142
- );
143
- }
144
-
145
- export {
146
- Dialog,
147
- DialogBody,
148
- DialogClose,
149
- DialogContent,
150
- DialogDescription,
151
- DialogHeader,
152
- DialogOverlay,
153
- DialogPortal,
154
- DialogTitle,
155
- DialogTrigger,
156
- };
@@ -1,114 +0,0 @@
1
- import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
2
- import { cn } from "../../lib/utils";
3
- import { textVariants } from "./Text";
4
-
5
- function DropdownMenu({
6
- ...props
7
- }: React.ComponentProps<typeof DropdownMenuPrimitive.Root>) {
8
- return <DropdownMenuPrimitive.Root data-slot="dropdown-menu" {...props} />;
9
- }
10
-
11
- function DropdownMenuTrigger({
12
- ...props
13
- }: React.ComponentProps<typeof DropdownMenuPrimitive.Trigger>) {
14
- return (
15
- <DropdownMenuPrimitive.Trigger
16
- data-slot="dropdown-menu-trigger"
17
- {...props}
18
- />
19
- );
20
- }
21
-
22
- function DropdownMenuContent({
23
- className,
24
- sideOffset = 4,
25
- ...props
26
- }: React.ComponentProps<typeof DropdownMenuPrimitive.Content>) {
27
- return (
28
- <DropdownMenuPrimitive.Portal>
29
- <DropdownMenuPrimitive.Content
30
- data-slot="dropdown-menu-content"
31
- sideOffset={sideOffset}
32
- className={cn(
33
- "z-50 min-w-[8rem] overflow-hidden rounded-xl py-1",
34
- "bg-white/95 dark:bg-zinc-900/95 backdrop-blur-sm shadow-lg border border-zinc-200/40 dark:border-zinc-700/40",
35
- "animate-in",
36
- className,
37
- )}
38
- {...props}
39
- />
40
- </DropdownMenuPrimitive.Portal>
41
- );
42
- }
43
-
44
- function DropdownMenuGroup({
45
- ...props
46
- }: React.ComponentProps<typeof DropdownMenuPrimitive.Group>) {
47
- return (
48
- <DropdownMenuPrimitive.Group data-slot="dropdown-menu-group" {...props} />
49
- );
50
- }
51
-
52
- function DropdownMenuItem({
53
- className,
54
- variant = "default",
55
- ...props
56
- }: React.ComponentProps<typeof DropdownMenuPrimitive.Item> & {
57
- variant?: "default" | "destructive";
58
- }) {
59
- return (
60
- <DropdownMenuPrimitive.Item
61
- data-slot="dropdown-menu-item"
62
- data-variant={variant}
63
- className={cn(
64
- "w-full px-3 py-1.5 text-left text-sm outline-none select-none transition-colors duration-150 flex items-center gap-2 cursor-default",
65
- "data-[variant=default]:text-zinc-600 dark:data-[variant=default]:text-zinc-400 data-[variant=default]:focus:bg-zinc-50 dark:data-[variant=default]:focus:bg-zinc-800 data-[variant=default]:focus:text-zinc-900 dark:data-[variant=default]:focus:text-zinc-100",
66
- "data-[variant=destructive]:text-red-600 dark:data-[variant=destructive]:text-red-400 data-[variant=destructive]:focus:bg-red-50 dark:data-[variant=destructive]:focus:bg-red-950 data-[variant=destructive]:focus:text-red-700 dark:data-[variant=destructive]:focus:text-red-300",
67
- "data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
68
- "[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-3.5",
69
- className,
70
- )}
71
- {...props}
72
- />
73
- );
74
- }
75
-
76
- function DropdownMenuSeparator({
77
- className,
78
- ...props
79
- }: React.ComponentProps<typeof DropdownMenuPrimitive.Separator>) {
80
- return (
81
- <DropdownMenuPrimitive.Separator
82
- data-slot="dropdown-menu-separator"
83
- className={cn("my-1 h-px bg-zinc-100 dark:bg-zinc-800", className)}
84
- {...props}
85
- />
86
- );
87
- }
88
-
89
- function DropdownMenuLabel({
90
- className,
91
- ...props
92
- }: React.ComponentProps<typeof DropdownMenuPrimitive.Label>) {
93
- return (
94
- <DropdownMenuPrimitive.Label
95
- data-slot="dropdown-menu-label"
96
- className={cn(
97
- "px-3 py-1.5 font-medium",
98
- textVariants({ variant: "caption" }),
99
- className,
100
- )}
101
- {...props}
102
- />
103
- );
104
- }
105
-
106
- export {
107
- DropdownMenu,
108
- DropdownMenuContent,
109
- DropdownMenuGroup,
110
- DropdownMenuItem,
111
- DropdownMenuLabel,
112
- DropdownMenuSeparator,
113
- DropdownMenuTrigger,
114
- };
@@ -1,9 +0,0 @@
1
- function SeparatorDot() {
2
- return (
3
- <span data-slot="separator-dot" aria-hidden="true">
4
- ·
5
- </span>
6
- );
7
- }
8
-
9
- export { SeparatorDot };
@@ -1,54 +0,0 @@
1
- import { Slot } from "@radix-ui/react-slot";
2
- import { cva, type VariantProps } from "class-variance-authority";
3
- import { use } from "react";
4
- import { LayoutContext } from "../../contexts/LayoutContext";
5
- import { cn } from "../../lib/utils";
6
- import { FontFamilies } from "../../types";
7
-
8
- const textVariants = cva("", {
9
- variants: {
10
- variant: {
11
- title:
12
- "text-lg font-semibold tracking-tight text-zinc-900 dark:text-zinc-100",
13
- section: "text-sm font-medium text-zinc-900 dark:text-zinc-100",
14
- subsection: "text-xs font-medium text-zinc-700 dark:text-zinc-300",
15
- overline:
16
- "text-xs font-medium text-zinc-500 dark:text-zinc-400 uppercase tracking-wider",
17
- body: "text-sm text-zinc-600 dark:text-zinc-400",
18
- caption: "text-xs text-zinc-500 dark:text-zinc-400",
19
- micro: "text-[10px] text-zinc-400 dark:text-zinc-500",
20
- },
21
- },
22
- defaultVariants: {
23
- variant: "body",
24
- },
25
- });
26
-
27
- function Text({
28
- className,
29
- variant,
30
- asChild = false,
31
- ...props
32
- }: React.ComponentProps<"p"> &
33
- VariantProps<typeof textVariants> & {
34
- asChild?: boolean;
35
- }) {
36
- const layout = use(LayoutContext);
37
- const fontClass = layout
38
- ? layout.fontFamily === FontFamilies.SANS_SERIF
39
- ? "font-sans"
40
- : "font-serif"
41
- : undefined;
42
-
43
- const Comp = asChild ? Slot : "p";
44
-
45
- return (
46
- <Comp
47
- data-slot="text"
48
- className={cn(fontClass, textVariants({ variant }), className)}
49
- {...props}
50
- />
51
- );
52
- }
53
-
54
- export { Text, textVariants };
@@ -1,229 +0,0 @@
1
- import {
2
- createContext,
3
- type ReactNode,
4
- use,
5
- useCallback,
6
- useEffect,
7
- useMemo,
8
- } from "react";
9
- import { toast } from "sonner";
10
- import { useCommentNavigation } from "../hooks/useCommentNavigation";
11
- import { useComments } from "../hooks/useComments";
12
- import { useReanchorMode } from "../hooks/useReanchorMode";
13
- import { extractContext, formatForLLM } from "../lib/context";
14
- import { generatePrompt } from "../lib/export";
15
- import { truncate } from "../lib/utils";
16
- import { useAppStore } from "../store";
17
- import type { Comment, DocumentType } from "../types";
18
- import { useLocale } from "./LocaleContext";
19
-
20
- interface CommentContextValue {
21
- // From useComments
22
- comments: Comment[];
23
- commentCount: number;
24
- addComment: (
25
- selectedText: string,
26
- comment: string,
27
- startOffset: number,
28
- endOffset: number,
29
- ) => void;
30
- editComment: (id: string, newText: string) => void;
31
- deleteComment: (id: string) => void;
32
- deleteAll: () => void;
33
- reanchorComment: (
34
- id: string,
35
- selectedText: string,
36
- startOffset: number,
37
- endOffset: number,
38
- ) => void;
39
- // Derived
40
- sortedComments: Comment[];
41
- // From useCommentNavigation
42
- currentIndex: number;
43
- hoveredCommentId: string | undefined;
44
- setHoveredCommentId: (id: string | undefined) => void;
45
- navigateToComment: (commentId: string) => void;
46
- navigatePrevious: () => void;
47
- navigateNext: () => void;
48
- // From useReanchorMode
49
- reanchorTarget: { commentId: string } | null;
50
- startReanchor: (commentId: string) => void;
51
- cancelReanchor: () => void;
52
- // Copy operations
53
- copyCommentRaw: (comment: Comment) => void;
54
- copyCommentForLLM: (comment: Comment) => void;
55
- copyAllForLLM: () => void;
56
- // Scroll to highlight
57
- scrollToHighlight: (commentId: string) => void;
58
- }
59
-
60
- export const CommentContext = createContext<CommentContextValue | null>(null);
61
-
62
- export function useCommentContext(): CommentContextValue {
63
- const value = use(CommentContext);
64
- if (!value) {
65
- throw new Error("useCommentContext must be used within a CommentProvider");
66
- }
67
- return value;
68
- }
69
-
70
- interface CommentProviderProps {
71
- filePath: string;
72
- clean: boolean;
73
- documentContent: string;
74
- fileName: string;
75
- documentType: DocumentType;
76
- children: ReactNode;
77
- }
78
-
79
- export function CommentProvider({
80
- filePath,
81
- clean,
82
- documentContent,
83
- fileName,
84
- documentType,
85
- children,
86
- }: CommentProviderProps) {
87
- const {
88
- comments,
89
- error: commentsError,
90
- addComment,
91
- deleteComment,
92
- deleteAll,
93
- editComment,
94
- reanchorComment,
95
- } = useComments(filePath, { clean });
96
-
97
- // sortedComments from store (already sorted by setComments)
98
- const sortedComments = useAppStore(
99
- (s) => s.documents.get(filePath)?.sortedComments ?? [],
100
- );
101
-
102
- const {
103
- currentIndex,
104
- hoveredCommentId,
105
- setHoveredCommentId,
106
- navigateToComment,
107
- navigatePrevious,
108
- navigateNext,
109
- } = useCommentNavigation(sortedComments);
110
-
111
- const { reanchorTarget, startReanchor, cancelReanchor } = useReanchorMode();
112
- const { t } = useLocale();
113
-
114
- // Show comments errors as toast
115
- useEffect(() => {
116
- if (commentsError) {
117
- toast.error(commentsError);
118
- }
119
- }, [commentsError]);
120
-
121
- const copyCommentRaw = useCallback(
122
- (comment: Comment) => {
123
- const raw = `${comment.selectedText}\n\n${comment.comment}`;
124
- navigator.clipboard.writeText(raw);
125
- toast.success(t("toast.copied", { text: truncate(comment.comment) }));
126
- },
127
- [t],
128
- );
129
-
130
- const copyCommentForLLM = useCallback(
131
- (comment: Comment) => {
132
- const context = extractContext({
133
- content: documentContent,
134
- startOffset: comment.startOffset,
135
- endOffset: comment.endOffset,
136
- });
137
- const formatted = formatForLLM({
138
- context,
139
- fileName,
140
- comment: comment.comment,
141
- });
142
-
143
- navigator.clipboard.writeText(formatted);
144
- toast.success(
145
- t("toast.copiedForLLM", { text: truncate(comment.comment) }),
146
- );
147
- },
148
- [documentContent, fileName, t],
149
- );
150
-
151
- const copyAllForLLM = useCallback(() => {
152
- const prompt = generatePrompt(comments, fileName);
153
- navigator.clipboard.writeText(prompt);
154
- toast.success(t("toast.copiedAllComments"));
155
- }, [comments, fileName, t]);
156
-
157
- const scrollToHighlight = useCallback(
158
- (commentId: string) => {
159
- if (documentType === "html") {
160
- const iframe = window.document.querySelector("iframe");
161
- iframe?.contentWindow?.postMessage(
162
- { type: "scrollToHighlight", commentId },
163
- "*",
164
- );
165
- } else {
166
- const mark = window.document.querySelector(
167
- `mark[data-comment-id="${commentId}"]`,
168
- );
169
- if (mark) {
170
- mark.scrollIntoView({ behavior: "smooth", block: "center" });
171
- }
172
- }
173
- },
174
- [documentType],
175
- );
176
-
177
- const commentCount = comments.length;
178
-
179
- const value = useMemo<CommentContextValue>(
180
- () => ({
181
- comments,
182
- commentCount,
183
- addComment,
184
- editComment,
185
- deleteComment,
186
- deleteAll,
187
- reanchorComment,
188
- sortedComments,
189
- currentIndex,
190
- hoveredCommentId,
191
- setHoveredCommentId,
192
- navigateToComment,
193
- navigatePrevious,
194
- navigateNext,
195
- reanchorTarget,
196
- startReanchor,
197
- cancelReanchor,
198
- copyCommentRaw,
199
- copyCommentForLLM,
200
- copyAllForLLM,
201
- scrollToHighlight,
202
- }),
203
- [
204
- comments,
205
- commentCount,
206
- addComment,
207
- editComment,
208
- deleteComment,
209
- deleteAll,
210
- reanchorComment,
211
- sortedComments,
212
- currentIndex,
213
- hoveredCommentId,
214
- setHoveredCommentId,
215
- navigateToComment,
216
- navigatePrevious,
217
- navigateNext,
218
- reanchorTarget,
219
- startReanchor,
220
- cancelReanchor,
221
- copyCommentRaw,
222
- copyCommentForLLM,
223
- copyAllForLLM,
224
- scrollToHighlight,
225
- ],
226
- );
227
-
228
- return <CommentContext value={value}>{children}</CommentContext>;
229
- }