@plannotator/ui 0.22.0

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 (305) hide show
  1. package/README.md +60 -0
  2. package/assets/diff-options.png +0 -0
  3. package/assets/icon-codex.png +0 -0
  4. package/assets/look-flat.png +0 -0
  5. package/assets/look-grid.png +0 -0
  6. package/assets/review-sections.png +0 -0
  7. package/assets/review-tree.png +0 -0
  8. package/assets/workspaces.webp +0 -0
  9. package/components/AISettingsTab.tsx +145 -0
  10. package/components/ActionMenu.tsx +101 -0
  11. package/components/AgentControls.tsx +216 -0
  12. package/components/AgentsTab.tsx +1294 -0
  13. package/components/AnnotationPanel.tsx +731 -0
  14. package/components/AnnotationSidebar.tsx +86 -0
  15. package/components/AnnotationToolbar.tsx +323 -0
  16. package/components/AnnotationToolstrip.tsx +355 -0
  17. package/components/ApproveDropdown.tsx +170 -0
  18. package/components/AttachmentsButton.tsx +410 -0
  19. package/components/BlockRenderer.tsx +163 -0
  20. package/components/BorderTrail.tsx +38 -0
  21. package/components/CodeFilePicker.tsx +64 -0
  22. package/components/CodeFilePopout.tsx +602 -0
  23. package/components/CodePathValidationContext.tsx +14 -0
  24. package/components/CommentPopover.tsx +512 -0
  25. package/components/CompletionOverlay.tsx +99 -0
  26. package/components/ConfirmDialog.tsx +128 -0
  27. package/components/DocBadges.tsx +231 -0
  28. package/components/EditorAnnotationCard.tsx +65 -0
  29. package/components/ExportModal.tsx +565 -0
  30. package/components/FloatingQuickLabelPicker.tsx +144 -0
  31. package/components/GitHubIcon.tsx +16 -0
  32. package/components/GitLabIcon.tsx +30 -0
  33. package/components/GraphvizBlock.tsx +511 -0
  34. package/components/ImageAnnotator/Canvas.tsx +118 -0
  35. package/components/ImageAnnotator/Toolbar.tsx +218 -0
  36. package/components/ImageAnnotator/index.tsx +272 -0
  37. package/components/ImageAnnotator/types.ts +39 -0
  38. package/components/ImageAnnotator/utils.ts +147 -0
  39. package/components/ImageThumbnail.tsx +126 -0
  40. package/components/ImportModal.tsx +146 -0
  41. package/components/InlineMarkdown.tsx +1094 -0
  42. package/components/KeyboardShortcuts.tsx +208 -0
  43. package/components/Landing.tsx +496 -0
  44. package/components/ListItemBody.tsx +62 -0
  45. package/components/ListMarker.tsx +73 -0
  46. package/components/LookAndFeelAnnouncementDialog.tsx +230 -0
  47. package/components/MarkdownEditor.tsx +44 -0
  48. package/components/MenuVersionSection.tsx +91 -0
  49. package/components/MermaidBlock.tsx +579 -0
  50. package/components/ModeToggle.tsx +68 -0
  51. package/components/OpenInAppButton.tsx +299 -0
  52. package/components/OverlayScrollArea.tsx +82 -0
  53. package/components/PermissionModeSetup.tsx +92 -0
  54. package/components/PinpointOverlay.tsx +99 -0
  55. package/components/PlanAIAnnouncementDialog.tsx +187 -0
  56. package/components/PlanHeaderMenu.tsx +299 -0
  57. package/components/PopoutDialog.tsx +91 -0
  58. package/components/Popover.tsx +27 -0
  59. package/components/ProviderIcons.tsx +51 -0
  60. package/components/PullRequestIcon.tsx +12 -0
  61. package/components/QuickLabelDropdown.tsx +63 -0
  62. package/components/RenderedMarkdown.tsx +54 -0
  63. package/components/RepoIcon.tsx +12 -0
  64. package/components/ResizeHandle.tsx +86 -0
  65. package/components/ReviewAgentsIcon.tsx +18 -0
  66. package/components/SearchableSelect.tsx +157 -0
  67. package/components/Settings.tsx +2220 -0
  68. package/components/SparklesIcon.tsx +45 -0
  69. package/components/StickyHeaderLane.tsx +260 -0
  70. package/components/TableOfContents.tsx +157 -0
  71. package/components/TaterSpritePullup.tsx +34 -0
  72. package/components/TaterSpriteRunning.tsx +54 -0
  73. package/components/TaterSpriteSitting.tsx +37 -0
  74. package/components/TextShimmer.tsx +57 -0
  75. package/components/ThemeProvider.tsx +162 -0
  76. package/components/ThemeTab.tsx +139 -0
  77. package/components/ToolbarButtons.tsx +116 -0
  78. package/components/Tooltip.tsx +45 -0
  79. package/components/Viewer.tsx +977 -0
  80. package/components/ai/AIProviderBar.tsx +95 -0
  81. package/components/ai/DocumentAIChatPanel.tsx +313 -0
  82. package/components/blocks/AlertBlock.tsx +58 -0
  83. package/components/blocks/Callout.tsx +60 -0
  84. package/components/blocks/CodeBlock.tsx +75 -0
  85. package/components/blocks/HtmlBlock.tsx +122 -0
  86. package/components/blocks/MathBlock.tsx +36 -0
  87. package/components/blocks/TableBlock.tsx +146 -0
  88. package/components/blocks/TablePopout.tsx +279 -0
  89. package/components/blocks/TableToolbar.tsx +153 -0
  90. package/components/blocks/proseBody.tsx +102 -0
  91. package/components/core/button.tsx +44 -0
  92. package/components/core/textarea.tsx +26 -0
  93. package/components/diagramLanguages.ts +14 -0
  94. package/components/goal-setup/GoalSetupSurface.tsx +1354 -0
  95. package/components/html-viewer/HtmlViewer.tsx +386 -0
  96. package/components/html-viewer/bridge-script.ts +505 -0
  97. package/components/html-viewer/index.ts +1 -0
  98. package/components/html-viewer/useHtmlAnnotation.ts +400 -0
  99. package/components/icons/AgentIcons.tsx +66 -0
  100. package/components/icons/AppIcon.tsx +56 -0
  101. package/components/icons/MessagesIcon.tsx +11 -0
  102. package/components/icons/ObsidianIcons.tsx +208 -0
  103. package/components/icons/app/android-studio.svg +369 -0
  104. package/components/icons/app/antigravity.svg +97 -0
  105. package/components/icons/app/cursor.svg +16 -0
  106. package/components/icons/app/file-explorer.svg +20 -0
  107. package/components/icons/app/finder.png +0 -0
  108. package/components/icons/app/ghostty.svg +13 -0
  109. package/components/icons/app/iterm2.svg +13 -0
  110. package/components/icons/app/powershell.svg +14 -0
  111. package/components/icons/app/sublime-text.svg +17 -0
  112. package/components/icons/app/terminal.png +0 -0
  113. package/components/icons/app/textmate.png +0 -0
  114. package/components/icons/app/vscode.svg +39 -0
  115. package/components/icons/app/warp.png +0 -0
  116. package/components/icons/app/xcode.png +0 -0
  117. package/components/icons/app/zed-dark.svg +15 -0
  118. package/components/icons/app/zed.svg +15 -0
  119. package/components/icons/themeIcons.tsx +47 -0
  120. package/components/mermaidSvg.ts +33 -0
  121. package/components/plan-diff/PlanCleanDiffView.tsx +884 -0
  122. package/components/plan-diff/PlanDiffBadge.tsx +48 -0
  123. package/components/plan-diff/PlanDiffModeSwitcher.tsx +103 -0
  124. package/components/plan-diff/PlanDiffViewer.tsx +214 -0
  125. package/components/plan-diff/PlanRawDiffView.tsx +102 -0
  126. package/components/plan-diff/VSCodeIcon.tsx +132 -0
  127. package/components/settings/HooksTab.tsx +208 -0
  128. package/components/sidebar/ArchiveBrowser.tsx +98 -0
  129. package/components/sidebar/CountBadge.tsx +12 -0
  130. package/components/sidebar/FileBrowser.tsx +498 -0
  131. package/components/sidebar/MessagesBrowser.tsx +109 -0
  132. package/components/sidebar/SidebarContainer.tsx +353 -0
  133. package/components/sidebar/SidebarTabs.tsx +155 -0
  134. package/components/sidebar/VersionBrowser.tsx +141 -0
  135. package/components/types.d.ts +7 -0
  136. package/components/ui/badge.tsx +44 -0
  137. package/components/ui/button.tsx +83 -0
  138. package/components/ui/card.tsx +58 -0
  139. package/components/ui/dialog.tsx +106 -0
  140. package/components/ui/dropdown-menu.tsx +240 -0
  141. package/components/ui/state-pill.tsx +45 -0
  142. package/components/ui/tabs.tsx +46 -0
  143. package/components/ui/textarea.tsx +25 -0
  144. package/config/configStore.ts +214 -0
  145. package/config/index.ts +4 -0
  146. package/config/reviewView.ts +42 -0
  147. package/config/settings.ts +310 -0
  148. package/config/useConfig.ts +20 -0
  149. package/configure.ts +69 -0
  150. package/globals.d.ts +14 -0
  151. package/hooks/pfm/useCodeFilePopout.ts +111 -0
  152. package/hooks/useAIChat.ts +532 -0
  153. package/hooks/useAIProviderConfig.ts +115 -0
  154. package/hooks/useActiveSection.ts +78 -0
  155. package/hooks/useAgentJobs.ts +306 -0
  156. package/hooks/useAgentSettings.ts +579 -0
  157. package/hooks/useAgents.ts +90 -0
  158. package/hooks/useAnnotationDraft.ts +523 -0
  159. package/hooks/useAnnotationHighlighter.ts +1170 -0
  160. package/hooks/useArchive.ts +171 -0
  161. package/hooks/useAutoClose.ts +104 -0
  162. package/hooks/useCodeAnnotationDraft.ts +194 -0
  163. package/hooks/useCodeFilePopout.ts +1 -0
  164. package/hooks/useDismissOnOutsideAndEscape.ts +50 -0
  165. package/hooks/useDraggable.ts +108 -0
  166. package/hooks/useEditorAnnotations.ts +64 -0
  167. package/hooks/useExternalAnnotationHighlights.ts +105 -0
  168. package/hooks/useExternalAnnotations.ts +268 -0
  169. package/hooks/useFileBrowser.ts +402 -0
  170. package/hooks/useInputMethodSwitch.ts +89 -0
  171. package/hooks/useIsMobile.ts +17 -0
  172. package/hooks/useLinkedDoc.ts +494 -0
  173. package/hooks/useOverlayViewport.ts +36 -0
  174. package/hooks/usePinpoint.ts +182 -0
  175. package/hooks/usePlanDiff.ts +176 -0
  176. package/hooks/usePrintMode.ts +27 -0
  177. package/hooks/useResizablePanel.ts +173 -0
  178. package/hooks/useScrollViewport.ts +38 -0
  179. package/hooks/useSharing.ts +441 -0
  180. package/hooks/useSidebar.ts +55 -0
  181. package/hooks/useUpdateCheck.ts +132 -0
  182. package/hooks/useValidatedCodePaths.ts +94 -0
  183. package/icons/GitUser.tsx +17 -0
  184. package/lib/utils.ts +11 -0
  185. package/package.json +117 -0
  186. package/plannotator.webp +0 -0
  187. package/print.css +427 -0
  188. package/shortcuts/code-review/ai.shortcuts.ts +25 -0
  189. package/shortcuts/code-review/allFilesDiff.shortcuts.ts +46 -0
  190. package/shortcuts/code-review/annotationToolbar.shortcuts.ts +30 -0
  191. package/shortcuts/code-review/fileTree.shortcuts.ts +35 -0
  192. package/shortcuts/code-review/prComments.shortcuts.ts +18 -0
  193. package/shortcuts/code-review/suggestionModal.shortcuts.ts +25 -0
  194. package/shortcuts/code-review/tourDialog.shortcuts.ts +18 -0
  195. package/shortcuts/core.ts +399 -0
  196. package/shortcuts/index.ts +21 -0
  197. package/shortcuts/plan-review/annotationPanel.shortcuts.ts +25 -0
  198. package/shortcuts/plan-review/annotationToolbar.shortcuts.ts +39 -0
  199. package/shortcuts/plan-review/commentPopover.shortcuts.ts +21 -0
  200. package/shortcuts/plan-review/goalSetup.shortcuts.ts +10 -0
  201. package/shortcuts/plan-review/imageAnnotator.shortcuts.ts +42 -0
  202. package/shortcuts/plan-review/inputMethod.shortcuts.ts +30 -0
  203. package/shortcuts/plan-review/sidebar.shortcuts.ts +34 -0
  204. package/shortcuts/plan-review/viewer.shortcuts.ts +24 -0
  205. package/shortcuts/runtime.ts +259 -0
  206. package/sprite_package_additional/index.html +34 -0
  207. package/sprite_package_additional/sprite.png +0 -0
  208. package/sprite_package_new/index.html +34 -0
  209. package/sprite_package_new/sprite.png +0 -0
  210. package/sprite_package_pulluphang/index.html +34 -0
  211. package/sprite_package_pulluphang/sprite.png +0 -0
  212. package/styles.css +1 -0
  213. package/theme.css +893 -0
  214. package/themes/adwaita.css +62 -0
  215. package/themes/andromeeda.css +66 -0
  216. package/themes/aurora-x.css +66 -0
  217. package/themes/ayu-dark.css +66 -0
  218. package/themes/caffeine.css +113 -0
  219. package/themes/catppuccin.css +62 -0
  220. package/themes/claude-plus.css +60 -0
  221. package/themes/cursor-hc.css +34 -0
  222. package/themes/cursor-midnight.css +34 -0
  223. package/themes/cursor.css +62 -0
  224. package/themes/dark-plus.css +66 -0
  225. package/themes/doom-64.css +109 -0
  226. package/themes/dracula.css +33 -0
  227. package/themes/everforest-hard.css +62 -0
  228. package/themes/everforest-soft.css +62 -0
  229. package/themes/everforest.css +62 -0
  230. package/themes/github.css +66 -0
  231. package/themes/gruvbox.css +62 -0
  232. package/themes/houston.css +66 -0
  233. package/themes/kanagawa-dragon.css +34 -0
  234. package/themes/kanagawa-lotus.css +34 -0
  235. package/themes/kanagawa-wave.css +34 -0
  236. package/themes/laserwave.css +66 -0
  237. package/themes/material.css +62 -0
  238. package/themes/min.css +66 -0
  239. package/themes/monokai-pro.css +34 -0
  240. package/themes/neutral.css +59 -0
  241. package/themes/night-owl.css +66 -0
  242. package/themes/nord.css +66 -0
  243. package/themes/one-dark-pro.css +66 -0
  244. package/themes/one-light.css +66 -0
  245. package/themes/paulmillr.css +34 -0
  246. package/themes/plannotator.css +60 -0
  247. package/themes/plastic.css +66 -0
  248. package/themes/poimandres.css +66 -0
  249. package/themes/quantum-rose.css +109 -0
  250. package/themes/red.css +66 -0
  251. package/themes/rose-pine.css +62 -0
  252. package/themes/simple.css +124 -0
  253. package/themes/slack.css +66 -0
  254. package/themes/snazzy-light.css +66 -0
  255. package/themes/soft-pop.css +60 -0
  256. package/themes/solar-dusk.css +109 -0
  257. package/themes/solarized.css +66 -0
  258. package/themes/synthwave-84.css +34 -0
  259. package/themes/terminal.css +62 -0
  260. package/themes/tinacious.css +57 -0
  261. package/themes/tokyo-night.css +62 -0
  262. package/themes/vesper.css +62 -0
  263. package/themes/vitesse-black.css +66 -0
  264. package/themes/vitesse.css +62 -0
  265. package/types.ts +264 -0
  266. package/utils/agentSwitch.ts +71 -0
  267. package/utils/aiChatFormat.ts +33 -0
  268. package/utils/aiPrompt.ts +29 -0
  269. package/utils/aiProvider.ts +217 -0
  270. package/utils/anchors.ts +11 -0
  271. package/utils/annotateAgentTerminal.ts +29 -0
  272. package/utils/annotationHelpers.ts +101 -0
  273. package/utils/bear.ts +59 -0
  274. package/utils/blockTargeting.ts +240 -0
  275. package/utils/callback.ts +99 -0
  276. package/utils/commentContent.ts +8 -0
  277. package/utils/defaultNotesApp.ts +20 -0
  278. package/utils/diffFonts.ts +33 -0
  279. package/utils/editorMode.ts +31 -0
  280. package/utils/fileBrowser.ts +40 -0
  281. package/utils/generateId.ts +7 -0
  282. package/utils/generateIdentity.ts +29 -0
  283. package/utils/identity.ts +112 -0
  284. package/utils/inlineTransforms.ts +41 -0
  285. package/utils/inputMethod.ts +17 -0
  286. package/utils/lookAndFeelAnnouncement.ts +18 -0
  287. package/utils/obsidian.ts +201 -0
  288. package/utils/octarine.ts +53 -0
  289. package/utils/parser.ts +991 -0
  290. package/utils/permissionMode.ts +77 -0
  291. package/utils/planAIAnnouncement.ts +17 -0
  292. package/utils/planAgentInstructions.ts +137 -0
  293. package/utils/planDiffEngine.ts +590 -0
  294. package/utils/planSave.ts +49 -0
  295. package/utils/platform.ts +11 -0
  296. package/utils/quickLabels.ts +77 -0
  297. package/utils/reviewAgentInstructions.ts +182 -0
  298. package/utils/sanitizeHtml.ts +45 -0
  299. package/utils/sharing.ts +350 -0
  300. package/utils/slugify.ts +37 -0
  301. package/utils/storage.ts +141 -0
  302. package/utils/themeRegistry.ts +567 -0
  303. package/utils/uiPreferences.ts +34 -0
  304. package/utils/upload.ts +56 -0
  305. package/utils/wideMode.ts +48 -0
@@ -0,0 +1,126 @@
1
+ import React, { useState } from 'react';
2
+
3
+ export type ImageSrcResolver = (path: string, base?: string) => string;
4
+
5
+ /**
6
+ * Default image URL resolver — Plannotator's local server behavior, verbatim.
7
+ * Remote URLs pass through; local paths resolve through `/api/image`.
8
+ */
9
+ const defaultImageSrcResolver: ImageSrcResolver = (path, base) => {
10
+ if (path.startsWith('http://') || path.startsWith('https://')) {
11
+ return path; // Remote URL, use directly
12
+ }
13
+ let url = `/api/image?path=${encodeURIComponent(path)}`;
14
+ if (base && !path.startsWith('/')) {
15
+ url += `&base=${encodeURIComponent(base)}`;
16
+ }
17
+ return url;
18
+ };
19
+
20
+ // Module-level resolver, stable identity. Defaults to Plannotator's behavior so
21
+ // callers and consumers are unchanged. A host (e.g. Workspaces) calls
22
+ // `setImageSrcResolver` once at startup to resolve images via its own backend.
23
+ let imageSrcResolver: ImageSrcResolver = defaultImageSrcResolver;
24
+
25
+ /** Override how image paths resolve to URLs. Call once at app startup. */
26
+ export const setImageSrcResolver = (resolver: ImageSrcResolver): void => {
27
+ imageSrcResolver = resolver;
28
+ };
29
+
30
+ /** Reset to the default (Plannotator local) resolver. Mainly for tests. */
31
+ export const resetImageSrcResolver = (): void => {
32
+ imageSrcResolver = defaultImageSrcResolver;
33
+ };
34
+
35
+ /**
36
+ * Get the display URL for an image path or URL.
37
+ * Delegates to the active resolver (default = Plannotator `/api/image`).
38
+ */
39
+ export const getImageSrc = (path: string, base?: string): string =>
40
+ imageSrcResolver(path, base);
41
+
42
+ interface ImageThumbnailProps {
43
+ path: string;
44
+ size?: 'sm' | 'md' | 'lg';
45
+ onClick?: () => void;
46
+ onRemove?: () => void;
47
+ showRemove?: boolean;
48
+ className?: string;
49
+ }
50
+
51
+ const SIZES = {
52
+ sm: 'w-6 h-6',
53
+ md: 'w-10 h-10',
54
+ lg: 'w-16 h-16',
55
+ };
56
+
57
+ export const ImageThumbnail: React.FC<ImageThumbnailProps> = ({
58
+ path,
59
+ size = 'md',
60
+ onClick,
61
+ onRemove,
62
+ showRemove = true,
63
+ className = '',
64
+ }) => {
65
+ const [error, setError] = useState(false);
66
+ const [loading, setLoading] = useState(true);
67
+
68
+ const sizeClass = SIZES[size];
69
+
70
+ return (
71
+ <div className={`group relative ${sizeClass} ${className}`}>
72
+ {loading && !error && (
73
+ <div className={`absolute inset-0 bg-muted rounded animate-pulse`} />
74
+ )}
75
+
76
+ {error ? (
77
+ <div
78
+ className={`${sizeClass} rounded bg-muted flex items-center justify-center text-muted-foreground`}
79
+ >
80
+ <svg
81
+ className="w-4 h-4"
82
+ fill="none"
83
+ viewBox="0 0 24 24"
84
+ stroke="currentColor"
85
+ strokeWidth={1.5}
86
+ >
87
+ <path
88
+ strokeLinecap="round"
89
+ strokeLinejoin="round"
90
+ d="M2.25 15.75l5.159-5.159a2.25 2.25 0 013.182 0l5.159 5.159m-1.5-1.5l1.409-1.409a2.25 2.25 0 013.182 0l2.909 2.909m-18 3.75h16.5a1.5 1.5 0 001.5-1.5V6a1.5 1.5 0 00-1.5-1.5H3.75A1.5 1.5 0 002.25 6v12a1.5 1.5 0 001.5 1.5zm10.5-11.25h.008v.008h-.008V8.25zm.375 0a.375.375 0 11-.75 0 .375.375 0 01.75 0z"
91
+ />
92
+ </svg>
93
+ </div>
94
+ ) : (
95
+ <img
96
+ src={getImageSrc(path)}
97
+ alt="Attachment"
98
+ loading="lazy"
99
+ onClick={onClick}
100
+ onLoad={() => setLoading(false)}
101
+ onError={() => {
102
+ setError(true);
103
+ setLoading(false);
104
+ }}
105
+ className={`${sizeClass} rounded object-cover border border-border ${onClick ? 'cursor-pointer hover:opacity-80' : ''}`}
106
+ />
107
+ )}
108
+
109
+ {/* Remove button */}
110
+ {showRemove && onRemove && (
111
+ <button
112
+ type="button"
113
+ onClick={(e) => {
114
+ e.stopPropagation();
115
+ onRemove();
116
+ }}
117
+ className="absolute -top-1 -right-1 w-4 h-4 bg-destructive text-destructive-foreground rounded-full opacity-0 group-hover:opacity-100 transition-opacity flex items-center justify-center"
118
+ >
119
+ <svg className="w-2.5 h-2.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={3}>
120
+ <path strokeLinecap="round" strokeLinejoin="round" d="M6 18L18 6M6 6l12 12" />
121
+ </svg>
122
+ </button>
123
+ )}
124
+ </div>
125
+ );
126
+ };
@@ -0,0 +1,146 @@
1
+ /**
2
+ * Import Modal for loading teammate annotations from a share URL
3
+ */
4
+
5
+ import React, { useState, useRef } from 'react';
6
+ import type { ImportResult } from '../hooks/useSharing';
7
+
8
+ interface ImportModalProps {
9
+ isOpen: boolean;
10
+ onClose: () => void;
11
+ onImport: (url: string) => Promise<ImportResult>;
12
+ shareBaseUrl?: string;
13
+ }
14
+
15
+ export const ImportModal: React.FC<ImportModalProps> = ({
16
+ isOpen,
17
+ onClose,
18
+ onImport,
19
+ shareBaseUrl,
20
+ }) => {
21
+ const [url, setUrl] = useState('');
22
+ const [loading, setLoading] = useState(false);
23
+ const [result, setResult] = useState<ImportResult | null>(null);
24
+ const autoCloseTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
25
+
26
+ if (!isOpen) return null;
27
+
28
+ const handleImport = async () => {
29
+ if (!url.trim()) return;
30
+ setLoading(true);
31
+ setResult(null);
32
+ const res = await onImport(url.trim());
33
+ setResult(res);
34
+ setLoading(false);
35
+ if (res.success && res.count > 0) {
36
+ // Auto-close after successful import
37
+ autoCloseTimer.current = setTimeout(() => {
38
+ autoCloseTimer.current = null;
39
+ setUrl('');
40
+ setResult(null);
41
+ onClose();
42
+ }, 1500);
43
+ }
44
+ };
45
+
46
+ const handleClose = () => {
47
+ if (autoCloseTimer.current) {
48
+ clearTimeout(autoCloseTimer.current);
49
+ autoCloseTimer.current = null;
50
+ }
51
+ setUrl('');
52
+ setResult(null);
53
+ onClose();
54
+ };
55
+
56
+ const handleKeyDown = (e: React.KeyboardEvent) => {
57
+ if (e.key === 'Enter' && !loading) {
58
+ handleImport();
59
+ }
60
+ };
61
+
62
+ return (
63
+ <div className="fixed inset-0 z-50 flex items-center justify-center bg-background/80 backdrop-blur-sm p-4">
64
+ <div
65
+ className="bg-card border border-border rounded-xl w-full max-w-lg flex flex-col shadow-2xl"
66
+ onClick={e => e.stopPropagation()}
67
+ >
68
+ {/* Header */}
69
+ <div className="p-4 border-b border-border">
70
+ <div className="flex justify-between items-center">
71
+ <h3 className="font-semibold text-sm">Import Teammate Review</h3>
72
+ <button
73
+ onClick={handleClose}
74
+ className="p-1.5 rounded-md hover:bg-muted text-muted-foreground hover:text-foreground transition-colors"
75
+ >
76
+ <svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
77
+ <path strokeLinecap="round" strokeLinejoin="round" d="M6 18L18 6M6 6l12 12" />
78
+ </svg>
79
+ </button>
80
+ </div>
81
+ </div>
82
+
83
+ {/* Body */}
84
+ <div className="p-4 space-y-4">
85
+ <div>
86
+ <label className="block text-xs font-medium text-muted-foreground mb-2">
87
+ Plannotator Share Link
88
+ </label>
89
+ <input
90
+ type="text"
91
+ value={url}
92
+ onChange={e => setUrl(e.target.value)}
93
+ onKeyDown={handleKeyDown}
94
+ placeholder={`${shareBaseUrl || 'https://share.plannotator.ai'}/#...`}
95
+ className="w-full bg-muted rounded-lg px-3 py-2 text-xs font-mono focus:outline-none focus:ring-2 focus:ring-accent/50"
96
+ disabled={loading}
97
+ autoFocus
98
+ />
99
+ </div>
100
+
101
+ <p className="text-xs text-muted-foreground">
102
+ Paste a share link from a teammate to import their annotations into the current plan review.
103
+ </p>
104
+
105
+ {/* Result feedback */}
106
+ {result && (
107
+ <div className={`rounded-lg px-3 py-2 text-xs ${
108
+ result.success && result.count > 0
109
+ ? 'bg-green-500/10 text-green-600 dark:text-green-400'
110
+ : result.success && result.count === 0
111
+ ? 'bg-yellow-500/10 text-yellow-600 dark:text-yellow-400'
112
+ : 'bg-destructive/10 text-destructive'
113
+ }`}>
114
+ {result.success && result.count > 0 && (
115
+ <span>Imported {result.count} annotation{result.count !== 1 ? 's' : ''} from "{result.planTitle}"</span>
116
+ )}
117
+ {result.success && result.count === 0 && (
118
+ <span>{result.error || 'No new annotations to import (all already exist)'}</span>
119
+ )}
120
+ {!result.success && (
121
+ <span>{result.error || 'Failed to import'}</span>
122
+ )}
123
+ </div>
124
+ )}
125
+ </div>
126
+
127
+ {/* Footer */}
128
+ <div className="p-4 border-t border-border flex justify-end gap-2">
129
+ <button
130
+ onClick={handleClose}
131
+ className="px-3 py-1.5 rounded-md text-xs font-medium bg-muted hover:bg-muted/80 transition-colors"
132
+ >
133
+ Cancel
134
+ </button>
135
+ <button
136
+ onClick={handleImport}
137
+ disabled={!url.trim() || loading}
138
+ className="px-3 py-1.5 rounded-md text-xs font-medium bg-primary text-primary-foreground hover:opacity-90 transition-opacity disabled:opacity-50 disabled:cursor-not-allowed"
139
+ >
140
+ {loading ? 'Importing...' : 'Import'}
141
+ </button>
142
+ </div>
143
+ </div>
144
+ </div>
145
+ );
146
+ };