@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,128 @@
1
+ /**
2
+ * Reusable confirmation dialog component
3
+ */
4
+
5
+ import React, { useEffect } from 'react';
6
+
7
+ export interface ConfirmDialogProps {
8
+ isOpen: boolean;
9
+ onClose: () => void;
10
+ onConfirm?: () => void;
11
+ title: string;
12
+ message: React.ReactNode;
13
+ subMessage?: React.ReactNode;
14
+ confirmText?: string;
15
+ cancelText?: string;
16
+ variant?: 'info' | 'warning';
17
+ showCancel?: boolean;
18
+ wide?: boolean;
19
+ }
20
+
21
+ export const ConfirmDialog: React.FC<ConfirmDialogProps> = ({
22
+ isOpen,
23
+ onClose,
24
+ onConfirm,
25
+ title,
26
+ message,
27
+ subMessage,
28
+ confirmText = 'Got it',
29
+ cancelText = 'Cancel',
30
+ variant = 'info',
31
+ showCancel = false,
32
+ wide = false,
33
+ }) => {
34
+ useEffect(() => {
35
+ if (!isOpen) return;
36
+ const handleKey = (event: KeyboardEvent) => {
37
+ if (event.key === 'Enter' && (event.metaKey || event.ctrlKey)) {
38
+ event.preventDefault();
39
+ event.stopPropagation();
40
+ if (onConfirm) onConfirm();
41
+ else onClose();
42
+ }
43
+ if (event.key === 'Escape') {
44
+ event.preventDefault();
45
+ event.stopPropagation();
46
+ onClose();
47
+ }
48
+ };
49
+ window.addEventListener('keydown', handleKey);
50
+ return () => window.removeEventListener('keydown', handleKey);
51
+ }, [isOpen, onConfirm, onClose]);
52
+
53
+ if (!isOpen) return null;
54
+
55
+ const iconColors = {
56
+ info: 'bg-accent/20 text-accent',
57
+ warning: 'bg-warning/20 text-warning',
58
+ };
59
+
60
+ const buttonColors = {
61
+ info: 'bg-primary text-primary-foreground hover:opacity-90',
62
+ warning: 'bg-warning text-warning-foreground hover:opacity-90',
63
+ };
64
+
65
+ const icons = {
66
+ info: (
67
+ <svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
68
+ <path strokeLinecap="round" strokeLinejoin="round" d="M7 8h10M7 12h4m1 8l-4-4H5a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v8a2 2 0 01-2 2h-3l-4 4z" />
69
+ </svg>
70
+ ),
71
+ warning: (
72
+ <svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
73
+ <path strokeLinecap="round" strokeLinejoin="round" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
74
+ </svg>
75
+ ),
76
+ };
77
+
78
+ return (
79
+ <div
80
+ className="fixed inset-0 z-[100] flex items-center justify-center bg-background/80 backdrop-blur-sm p-4"
81
+ data-plannotator-confirm-dialog="true"
82
+ >
83
+ <div
84
+ role="dialog"
85
+ aria-modal="true"
86
+ className={`bg-card border border-border rounded-xl w-full shadow-2xl p-6 ${wide ? 'max-w-md' : 'max-w-sm'}`}
87
+ >
88
+ <div className="flex items-center gap-3 mb-4">
89
+ <div className={`w-10 h-10 rounded-full flex items-center justify-center ${iconColors[variant]}`}>
90
+ {icons[variant]}
91
+ </div>
92
+ <h3 className="font-semibold">{title}</h3>
93
+ </div>
94
+ <div className="text-sm text-muted-foreground mb-2">
95
+ {message}
96
+ </div>
97
+ {subMessage && (
98
+ <div className="text-xs text-muted-foreground mb-6">
99
+ {subMessage}
100
+ </div>
101
+ )}
102
+ {!subMessage && <div className="mb-4" />}
103
+ <div className="flex justify-end gap-2">
104
+ {showCancel && (
105
+ <button
106
+ onClick={onClose}
107
+ className="px-4 py-2 rounded-md text-sm font-medium bg-muted text-muted-foreground hover:bg-muted/80 transition-opacity"
108
+ >
109
+ {cancelText}
110
+ </button>
111
+ )}
112
+ <button
113
+ onClick={() => {
114
+ if (onConfirm) {
115
+ onConfirm();
116
+ } else {
117
+ onClose();
118
+ }
119
+ }}
120
+ className={`px-4 py-2 rounded-md text-sm font-medium transition-opacity ${buttonColors[variant]}`}
121
+ >
122
+ {confirmText}
123
+ </button>
124
+ </div>
125
+ </div>
126
+ </div>
127
+ );
128
+ };
@@ -0,0 +1,231 @@
1
+ /**
2
+ * DocBadges — repo / branch / plan-diff / demo / archive / linked-doc badge cluster.
3
+ *
4
+ * Extracted from Viewer.tsx so the same markup can render in two places:
5
+ * - layout="column": original location at the top-left of the plan card (absolute)
6
+ * - layout="row": inside the sticky header lane when the user scrolls
7
+ *
8
+ * In row layout, the demo badge and linked-doc breadcrumb are dropped — the
9
+ * sticky lane hides entirely in linked-doc mode, and the demo badge is purely
10
+ * decorative top-of-doc context.
11
+ */
12
+
13
+ import React from 'react';
14
+ import { PlanDiffBadge } from './plan-diff/PlanDiffBadge';
15
+ import type { PlanDiffStats } from '../utils/planDiffEngine';
16
+ import { hostnameOrFallback } from '@plannotator/core/project';
17
+ import { OpenInAppButton } from './OpenInAppButton';
18
+
19
+ export interface LinkedDocBadgeInfo {
20
+ filepath: string;
21
+ onBack: () => void;
22
+ label?: string;
23
+ backLabel?: string;
24
+ variant?: 'breadcrumb' | 'folder-file';
25
+ }
26
+
27
+ export interface DocBadgesProps {
28
+ layout: 'column' | 'row';
29
+ repoInfo?: { display: string; branch?: string } | null;
30
+ planDiffStats?: PlanDiffStats | null;
31
+ isPlanDiffActive?: boolean;
32
+ hasPreviousVersion?: boolean;
33
+ onPlanDiffToggle?: () => void;
34
+ showDemoBadge?: boolean;
35
+ archiveInfo?: { status: 'approved' | 'denied' | 'unknown'; timestamp: string; title: string } | null;
36
+ linkedDocInfo?: LinkedDocBadgeInfo | null;
37
+ /** Source attribution for HTML/URL annotations (e.g. "https://..." or "index.html") */
38
+ sourceInfo?: string;
39
+ /**
40
+ * Absolute on-disk path of the annotated source file, used by the
41
+ * Open-in-app control. Omitted (or an https:// URL) -> no control rendered.
42
+ */
43
+ openInAppPath?: string | null;
44
+ }
45
+
46
+ export const DocBadges: React.FC<DocBadgesProps> = ({
47
+ layout,
48
+ repoInfo,
49
+ planDiffStats,
50
+ isPlanDiffActive,
51
+ hasPreviousVersion,
52
+ onPlanDiffToggle,
53
+ showDemoBadge,
54
+ archiveInfo,
55
+ linkedDocInfo,
56
+ sourceInfo,
57
+ openInAppPath,
58
+ }) => {
59
+ const isRow = layout === 'row';
60
+ const canOpenInApp =
61
+ !!openInAppPath && !/^https?:\/\//i.test(openInAppPath);
62
+ const openInButton = canOpenInApp ? (
63
+ <OpenInAppButton filePath={openInAppPath} base={null} />
64
+ ) : null;
65
+
66
+ // In row layout, only PlanDiffBadge (when it has stats to show) and
67
+ // archiveInfo actually render — everything else is hidden. Check what
68
+ // will truly produce visible output to avoid an empty wrapper div.
69
+ const anything = isRow
70
+ ? (!linkedDocInfo && ((hasPreviousVersion && planDiffStats) || archiveInfo))
71
+ : repoInfo || hasPreviousVersion || showDemoBadge || linkedDocInfo || archiveInfo || sourceInfo || canOpenInApp;
72
+ if (!anything) return null;
73
+
74
+ // Row layout: single horizontal line. Column layout: stacked rows.
75
+ const outerClass = isRow
76
+ ? 'flex flex-row items-center gap-1.5 text-[9px] text-muted-foreground/70 font-mono'
77
+ : 'flex flex-col items-start gap-1 text-[9px] text-muted-foreground/50 font-mono';
78
+
79
+ return (
80
+ <div className={outerClass}>
81
+ {/* Open-in-app normally renders inline (to the right) within the source /
82
+ linked-doc rows below. This standalone fallback only fires when there
83
+ is no file row to attach to. Hidden in the sticky row and for URLs. */}
84
+ {!isRow && canOpenInApp && !sourceInfo && !linkedDocInfo && openInButton}
85
+ {/* Row layout (sticky lane) omits repo/branch to keep the bar compact —
86
+ they'd otherwise push the container wide enough to visually extend
87
+ under the action buttons. Plan-diff badge still renders below. */}
88
+ {repoInfo && !linkedDocInfo && !isRow && (
89
+ <div className="flex items-center gap-1.5">
90
+ <span
91
+ className="px-1.5 py-0.5 bg-muted/50 rounded truncate max-w-[140px]"
92
+ title={repoInfo.display}
93
+ >
94
+ {repoInfo.display}
95
+ </span>
96
+ {repoInfo.branch && (
97
+ <span
98
+ className="px-1.5 py-0.5 bg-muted/30 rounded max-w-[120px] flex items-center gap-1 overflow-hidden"
99
+ title={repoInfo.branch}
100
+ >
101
+ <svg className="w-2.5 h-2.5 flex-shrink-0" viewBox="0 0 16 16" fill="currentColor">
102
+ <path d="M9.5 3.25a2.25 2.25 0 1 1 3 2.122V6A2.5 2.5 0 0 1 10 8.5H6a1 1 0 0 0-1 1v1.128a2.251 2.251 0 1 1-1.5 0V5.372a2.25 2.25 0 1 1 1.5 0v1.836A2.493 2.493 0 0 1 6 7h4a1 1 0 0 0 1-1v-.628A2.25 2.25 0 0 1 9.5 3.25Zm-6 0a.75.75 0 1 0 1.5 0 .75.75 0 0 0-1.5 0Zm8.25-.75a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5ZM4.25 12a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5Z" />
103
+ </svg>
104
+ <span className="truncate">{repoInfo.branch}</span>
105
+ </span>
106
+ )}
107
+ </div>
108
+ )}
109
+
110
+ {sourceInfo && !linkedDocInfo && !isRow && (
111
+ <div className="flex items-center gap-1">
112
+ <span
113
+ className="px-1.5 py-0.5 bg-muted/30 rounded truncate max-w-[200px]"
114
+ title={sourceInfo}
115
+ >
116
+ {/^https?:\/\//i.test(sourceInfo)
117
+ ? hostnameOrFallback(sourceInfo)
118
+ : sourceInfo}
119
+ </span>
120
+ {openInButton}
121
+ </div>
122
+ )}
123
+
124
+ {onPlanDiffToggle && !linkedDocInfo && (
125
+ <PlanDiffBadge
126
+ stats={planDiffStats ?? null}
127
+ isActive={isPlanDiffActive ?? false}
128
+ onToggle={onPlanDiffToggle}
129
+ hasPreviousVersion={hasPreviousVersion ?? false}
130
+ />
131
+ )}
132
+
133
+ {/* Demo badge: only in column (top-of-doc) layout */}
134
+ {!isRow && showDemoBadge && !linkedDocInfo && (
135
+ <span className="px-1.5 py-0.5 rounded text-[9px] font-mono bg-amber-500/15 text-amber-600 dark:text-amber-400">
136
+ Demo
137
+ </span>
138
+ )}
139
+
140
+ {archiveInfo && !linkedDocInfo && (
141
+ <div className="flex items-center gap-1.5">
142
+ <span
143
+ className={`px-1.5 py-0.5 rounded ${
144
+ archiveInfo.status === 'approved'
145
+ ? 'bg-green-500/15 text-green-600 dark:text-green-400'
146
+ : archiveInfo.status === 'denied'
147
+ ? 'bg-red-500/15 text-red-600 dark:text-red-400'
148
+ : 'bg-muted/50 text-muted-foreground'
149
+ }`}
150
+ >
151
+ {archiveInfo.status === 'approved'
152
+ ? 'Approved'
153
+ : archiveInfo.status === 'denied'
154
+ ? 'Denied'
155
+ : 'Unknown'}
156
+ </span>
157
+ {archiveInfo.timestamp && (
158
+ <span
159
+ className="px-1.5 py-0.5 bg-muted/50 rounded"
160
+ title={archiveInfo.timestamp}
161
+ >
162
+ {new Date(archiveInfo.timestamp).toLocaleDateString(undefined, {
163
+ month: 'short',
164
+ day: 'numeric',
165
+ year: 'numeric',
166
+ })}{' '}
167
+ {new Date(archiveInfo.timestamp).toLocaleTimeString(undefined, {
168
+ hour: 'numeric',
169
+ minute: '2-digit',
170
+ })}
171
+ </span>
172
+ )}
173
+ </div>
174
+ )}
175
+
176
+ {/* Linked-doc breadcrumb: only in column layout (sticky lane is hidden in linked-doc mode) */}
177
+ {!isRow && linkedDocInfo && (
178
+ linkedDocInfo.variant === 'folder-file' ? (
179
+ <div className="flex items-center gap-2">
180
+ <button
181
+ type="button"
182
+ onClick={linkedDocInfo.onBack}
183
+ className="rounded-sm text-[9px] font-medium text-primary transition-colors hover:text-primary/80 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background"
184
+ >
185
+ Close
186
+ </button>
187
+ <span
188
+ className="truncate rounded bg-muted/50 px-1.5 py-0.5 text-[9px] text-muted-foreground max-w-[220px]"
189
+ title={linkedDocInfo.filepath}
190
+ >
191
+ {linkedDocInfo.filepath.split('/').pop()}
192
+ </span>
193
+ {openInButton}
194
+ </div>
195
+ ) : (
196
+ <div className="flex items-center gap-1.5">
197
+ <button
198
+ onClick={linkedDocInfo.onBack}
199
+ className="px-1.5 py-0.5 bg-primary/10 text-primary rounded hover:bg-primary/20 transition-colors flex items-center gap-1"
200
+ >
201
+ <svg
202
+ className="w-2.5 h-2.5"
203
+ fill="none"
204
+ viewBox="0 0 24 24"
205
+ stroke="currentColor"
206
+ strokeWidth={2.5}
207
+ >
208
+ <path
209
+ strokeLinecap="round"
210
+ strokeLinejoin="round"
211
+ d="M10.5 19.5L3 12m0 0l7.5-7.5M3 12h18"
212
+ />
213
+ </svg>
214
+ {linkedDocInfo.backLabel || 'plan'}
215
+ </button>
216
+ <span className="px-1.5 py-0.5 bg-primary/10 text-primary/80 rounded">
217
+ {linkedDocInfo.label || 'Linked File'}
218
+ </span>
219
+ <span
220
+ className="px-1.5 py-0.5 bg-muted/50 text-muted-foreground rounded truncate max-w-[200px]"
221
+ title={linkedDocInfo.filepath}
222
+ >
223
+ {linkedDocInfo.filepath.split('/').pop()}
224
+ </span>
225
+ {openInButton}
226
+ </div>
227
+ )
228
+ )}
229
+ </div>
230
+ );
231
+ };
@@ -0,0 +1,65 @@
1
+ import React from 'react';
2
+ import type { EditorAnnotation } from '../types';
3
+ import { cn } from '../lib/utils';
4
+
5
+ // EditorAnnotationCard is SHARED across surfaces:
6
+ // - the plan/annotate AnnotationPanel (flat surface-1 cards), and
7
+ // - the code-review ReviewSidebar (bordered cards, sitting next to
8
+ // renderAnnotationCard's `p-2.5 rounded border border-transparent
9
+ // hover:bg-muted/30` code cards).
10
+ // The `variant` prop keeps each surface visually cohesive:
11
+ // - 'plan' → flat surface-1 hover (matches the plan AnnotationCard restyle)
12
+ // - 'code-review' → bordered + muted hover (matches code-review's code cards)
13
+ type EditorAnnotationVariant = 'plan' | 'code-review';
14
+
15
+ interface EditorAnnotationCardProps {
16
+ annotation: EditorAnnotation;
17
+ onDelete: () => void;
18
+ variant?: EditorAnnotationVariant;
19
+ }
20
+
21
+ export const EditorAnnotationCard: React.FC<EditorAnnotationCardProps> = ({ annotation, onDelete, variant = 'plan' }) => {
22
+ const lineRange = annotation.lineStart === annotation.lineEnd
23
+ ? `L${annotation.lineStart}`
24
+ : `L${annotation.lineStart}-${annotation.lineEnd}`;
25
+
26
+ return (
27
+ <div
28
+ className={cn(
29
+ 'group w-full text-left transition-colors duration-150',
30
+ variant === 'code-review'
31
+ ? 'relative p-2.5 rounded border border-transparent hover:bg-muted/30'
32
+ : 'rounded-lg px-3 py-2.5 hover:bg-surface-1/50',
33
+ )}
34
+ >
35
+ {/* Header: type word + file:line + delete */}
36
+ <div className="mb-1.5 flex items-center gap-1.5">
37
+ <span className="text-[11px] font-medium text-amber-600 dark:text-amber-400">Editor</span>
38
+ <span className="text-[10px] font-mono text-muted-foreground/50 truncate" title={annotation.filePath}>
39
+ {annotation.filePath}:{lineRange}
40
+ </span>
41
+ <button
42
+ onClick={(e) => { e.stopPropagation(); onDelete(); }}
43
+ className="ml-auto relative rounded-md p-1.5 text-muted-foreground transition-colors before:absolute before:-inset-1.5 before:content-[''] opacity-0 group-hover:opacity-100 [@media(hover:none)]:opacity-100 hover:text-destructive"
44
+ title="Delete annotation"
45
+ >
46
+ <svg className="w-3 h-3" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
47
+ <path strokeLinecap="round" strokeLinejoin="round" d="M6 18L18 6M6 6l12 12" />
48
+ </svg>
49
+ </button>
50
+ </div>
51
+
52
+ {/* Selected text */}
53
+ <p className="mb-1.5 line-clamp-2 whitespace-pre-wrap font-mono text-[11px] leading-relaxed text-muted-foreground/80">
54
+ {annotation.selectedText}
55
+ </p>
56
+
57
+ {/* Comment */}
58
+ {annotation.comment && (
59
+ <p className="whitespace-pre-wrap text-[13px] leading-relaxed text-foreground/90">
60
+ {annotation.comment}
61
+ </p>
62
+ )}
63
+ </div>
64
+ );
65
+ };