@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,353 @@
1
+ /**
2
+ * SidebarContainer — Shared sidebar shell
3
+ *
4
+ * Houses the Table of Contents, Version Browser, File Browser, and Archive Browser views.
5
+ * Tab bar at top switches between them.
6
+ */
7
+
8
+ import React from "react";
9
+ import type { SidebarTab } from "../../hooks/useSidebar";
10
+ import type { Block, Annotation } from "../../types";
11
+ import type { VersionInfo, VersionEntry } from "../../hooks/usePlanDiff";
12
+ import type { UseFileBrowserReturn } from "../../hooks/useFileBrowser";
13
+ import { TableOfContents } from "../TableOfContents";
14
+ import { VersionBrowser } from "./VersionBrowser";
15
+ import { FileBrowser, type FileEditStatus } from "./FileBrowser";
16
+ import { ArchiveBrowser, type ArchivedPlan } from "./ArchiveBrowser";
17
+ import { MessagesBrowser, type PickerMessage } from "./MessagesBrowser";
18
+ import { MessagesIcon } from "../icons/MessagesIcon";
19
+ import { OverlayScrollArea } from "../OverlayScrollArea";
20
+ import { ReviewAgentsIcon } from "../ReviewAgentsIcon";
21
+
22
+ interface SidebarContainerProps {
23
+ activeTab: SidebarTab;
24
+ onTabChange: (tab: SidebarTab) => void;
25
+ onClose: () => void;
26
+ width: number | string;
27
+ showAgentTerminalButton?: boolean;
28
+ isAgentTerminalOpen?: boolean;
29
+ isAgentTerminalRunning?: boolean;
30
+ onToggleAgentTerminal?: () => void;
31
+ // TOC props
32
+ blocks: Block[];
33
+ annotations: Annotation[];
34
+ activeSection: string | null;
35
+ onTocNavigate: (blockId: string) => void;
36
+ linkedDocFilepath?: string | null;
37
+ onLinkedDocBack?: () => void;
38
+ backLabel?: string;
39
+ // File Browser props
40
+ showFilesTab?: boolean;
41
+ fileAnnotationCounts?: Map<string, number>;
42
+ highlightedFiles?: Set<string>;
43
+ fileEditStatuses?: Map<string, FileEditStatus>;
44
+ fileBrowser?: UseFileBrowserReturn;
45
+ onFilesSelectFile?: (absolutePath: string, dirPath: string) => void;
46
+ onFilesFetchAll?: () => void;
47
+ onFilesRetryVaultDir?: (vaultPath: string) => void;
48
+ // Version Browser props
49
+ showVersionsTab?: boolean;
50
+ versionInfo: VersionInfo | null;
51
+ versions: VersionEntry[];
52
+ selectedBaseVersion: number | null;
53
+ onSelectBaseVersion: (version: number) => void;
54
+ isPlanDiffActive: boolean;
55
+ hasPreviousVersion: boolean;
56
+ onActivatePlanDiff: () => void;
57
+ isLoadingVersions: boolean;
58
+ isSelectingVersion: boolean;
59
+ fetchingVersion: number | null;
60
+ onFetchVersions: () => void;
61
+ // Annotation indicators
62
+ hasFileAnnotations?: boolean;
63
+ // Archive Browser props
64
+ showArchiveTab?: boolean;
65
+ archivePlans: ArchivedPlan[];
66
+ selectedArchiveFile: string | null;
67
+ onArchiveSelect: (filename: string) => void;
68
+ isLoadingArchive: boolean;
69
+ showMessagesTab?: boolean;
70
+ messages?: PickerMessage[];
71
+ selectedMessageId?: string | null;
72
+ onSelectMessage?: (messageId: string) => void;
73
+ messageAnnotationCounts?: Map<string, number>;
74
+ }
75
+
76
+ export const SidebarContainer: React.FC<SidebarContainerProps> = ({
77
+ activeTab,
78
+ onTabChange,
79
+ onClose,
80
+ width,
81
+ showAgentTerminalButton,
82
+ isAgentTerminalOpen,
83
+ isAgentTerminalRunning,
84
+ onToggleAgentTerminal,
85
+ blocks,
86
+ annotations,
87
+ activeSection,
88
+ onTocNavigate,
89
+ linkedDocFilepath,
90
+ onLinkedDocBack,
91
+ backLabel,
92
+ showFilesTab,
93
+ fileAnnotationCounts,
94
+ highlightedFiles,
95
+ fileEditStatuses,
96
+ fileBrowser,
97
+ onFilesSelectFile,
98
+ onFilesFetchAll,
99
+ onFilesRetryVaultDir,
100
+ showVersionsTab,
101
+ versionInfo,
102
+ versions,
103
+ selectedBaseVersion,
104
+ onSelectBaseVersion,
105
+ isPlanDiffActive,
106
+ hasPreviousVersion,
107
+ onActivatePlanDiff,
108
+ isLoadingVersions,
109
+ isSelectingVersion,
110
+ fetchingVersion,
111
+ onFetchVersions,
112
+ hasFileAnnotations,
113
+ showArchiveTab,
114
+ archivePlans,
115
+ selectedArchiveFile,
116
+ onArchiveSelect,
117
+ isLoadingArchive,
118
+ showMessagesTab,
119
+ messages,
120
+ selectedMessageId,
121
+ onSelectMessage,
122
+ messageAnnotationCounts,
123
+ }) => {
124
+ return (
125
+ <aside
126
+ className="hidden lg:flex flex-col sticky top-12 h-[calc(100vh-3rem)] flex-shrink-0 bg-card border-r border-border"
127
+ style={{ width }}
128
+ >
129
+ {/* Tab bar */}
130
+ <div className="flex h-10 items-center border-b border-border/50 px-2 gap-0.5 flex-shrink-0 overflow-hidden min-w-0">
131
+ {showAgentTerminalButton && onToggleAgentTerminal && (
132
+ <ActionButton
133
+ active={!!isAgentTerminalOpen}
134
+ running={!!isAgentTerminalRunning}
135
+ onClick={onToggleAgentTerminal}
136
+ icon={<ReviewAgentsIcon className="w-3 h-3" />}
137
+ label="Agent"
138
+ />
139
+ )}
140
+ <TabButton
141
+ active={activeTab === "toc"}
142
+ onClick={() => onTabChange("toc")}
143
+ icon={
144
+ <svg
145
+ className="w-3 h-3"
146
+ fill="none"
147
+ viewBox="0 0 24 24"
148
+ stroke="currentColor"
149
+ strokeWidth={2}
150
+ >
151
+ <path
152
+ strokeLinecap="round"
153
+ strokeLinejoin="round"
154
+ d="M4 6h16M4 10h16M4 14h10M4 18h10"
155
+ />
156
+ </svg>
157
+ }
158
+ label="Contents"
159
+ />
160
+ {showVersionsTab && (
161
+ <TabButton
162
+ active={activeTab === "versions"}
163
+ onClick={() => onTabChange("versions")}
164
+ icon={
165
+ <svg
166
+ className="w-3 h-3"
167
+ fill="none"
168
+ viewBox="0 0 24 24"
169
+ stroke="currentColor"
170
+ strokeWidth={2}
171
+ >
172
+ <path
173
+ strokeLinecap="round"
174
+ strokeLinejoin="round"
175
+ d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"
176
+ />
177
+ </svg>
178
+ }
179
+ label="Versions"
180
+ />
181
+ )}
182
+ {showMessagesTab && (
183
+ <TabButton
184
+ active={activeTab === "messages"}
185
+ onClick={() => onTabChange("messages")}
186
+ icon={<MessagesIcon className="w-3 h-3" />}
187
+ label="Messages"
188
+ badge={messageAnnotationCounts !== undefined && messageAnnotationCounts.size > 0}
189
+ />
190
+ )}
191
+ {showFilesTab && (
192
+ <TabButton
193
+ active={activeTab === "files"}
194
+ onClick={() => onTabChange("files")}
195
+ icon={
196
+ <svg
197
+ className="w-3 h-3"
198
+ fill="none"
199
+ viewBox="0 0 24 24"
200
+ stroke="currentColor"
201
+ strokeWidth={2}
202
+ >
203
+ <path
204
+ strokeLinecap="round"
205
+ strokeLinejoin="round"
206
+ d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"
207
+ />
208
+ </svg>
209
+ }
210
+ label="Files"
211
+ badge={hasFileAnnotations}
212
+ />
213
+ )}
214
+ {showArchiveTab && (
215
+ <TabButton
216
+ active={activeTab === "archive"}
217
+ onClick={() => onTabChange("archive")}
218
+ icon={
219
+ <svg
220
+ className="w-3 h-3"
221
+ fill="none"
222
+ viewBox="0 0 24 24"
223
+ stroke="currentColor"
224
+ strokeWidth={2}
225
+ >
226
+ <path
227
+ strokeLinecap="round"
228
+ strokeLinejoin="round"
229
+ d="M5 8h14M5 8a2 2 0 110-4h14a2 2 0 110 4M5 8v10a2 2 0 002 2h10a2 2 0 002-2V8m-9 4h4"
230
+ />
231
+ </svg>
232
+ }
233
+ label="Archive"
234
+ />
235
+ )}
236
+ {/* No header close button — the sidebar collapses via the resize-handle
237
+ hover button (see ResizeHandle onCollapse). */}
238
+ </div>
239
+
240
+ {/* Content area */}
241
+ <OverlayScrollArea className="flex-1 min-h-0">
242
+ {activeTab === "toc" && (
243
+ <TableOfContents
244
+ blocks={blocks}
245
+ annotations={annotations}
246
+ activeId={activeSection}
247
+ onNavigate={onTocNavigate}
248
+ className=""
249
+ linkedDocFilepath={linkedDocFilepath}
250
+ onLinkedDocBack={onLinkedDocBack}
251
+ backLabel={backLabel}
252
+ />
253
+ )}
254
+ {activeTab === "versions" && (
255
+ <VersionBrowser
256
+ versionInfo={versionInfo}
257
+ versions={versions}
258
+ selectedBaseVersion={selectedBaseVersion}
259
+ onSelectBaseVersion={onSelectBaseVersion}
260
+ isPlanDiffActive={isPlanDiffActive}
261
+ hasPreviousVersion={hasPreviousVersion}
262
+ onActivatePlanDiff={onActivatePlanDiff}
263
+ isLoading={isLoadingVersions}
264
+ isSelectingVersion={isSelectingVersion}
265
+ fetchingVersion={fetchingVersion}
266
+ onFetchVersions={onFetchVersions}
267
+ />
268
+ )}
269
+ {activeTab === "files" && showFilesTab && fileBrowser && (
270
+ <FileBrowser
271
+ dirs={fileBrowser.dirs}
272
+ expandedFolders={fileBrowser.expandedFolders}
273
+ onToggleFolder={fileBrowser.toggleFolder}
274
+ collapsedDirs={fileBrowser.collapsedDirs}
275
+ onToggleCollapse={fileBrowser.toggleCollapse}
276
+ onSelectFile={onFilesSelectFile ?? (() => {})}
277
+ activeFile={fileBrowser.activeFile}
278
+ onFetchAll={onFilesFetchAll ?? (() => {})}
279
+ onRetryVaultDir={onFilesRetryVaultDir}
280
+ annotationCounts={fileAnnotationCounts}
281
+ highlightedFiles={highlightedFiles}
282
+ editStatuses={fileEditStatuses}
283
+ />
284
+ )}
285
+ {activeTab === "archive" && showArchiveTab && (
286
+ <ArchiveBrowser
287
+ plans={archivePlans}
288
+ selectedFile={selectedArchiveFile}
289
+ onSelect={onArchiveSelect}
290
+ isLoading={isLoadingArchive}
291
+ />
292
+ )}
293
+ {activeTab === "messages" && showMessagesTab && messages && onSelectMessage && (
294
+ <MessagesBrowser
295
+ messages={messages}
296
+ selectedMessageId={selectedMessageId ?? null}
297
+ onSelect={onSelectMessage}
298
+ annotationCounts={messageAnnotationCounts}
299
+ />
300
+ )}
301
+ </OverlayScrollArea>
302
+ </aside>
303
+ );
304
+ };
305
+
306
+ const TabButton: React.FC<{
307
+ active: boolean;
308
+ onClick: () => void;
309
+ icon: React.ReactNode;
310
+ label: string;
311
+ badge?: boolean;
312
+ }> = ({ active, onClick, icon, label, badge }) => (
313
+ <button
314
+ onClick={onClick}
315
+ className={`relative flex items-center gap-1 px-2 py-1 rounded text-xs font-medium transition-colors min-w-0 shrink-0 ${
316
+ active
317
+ ? "bg-primary/10 text-primary"
318
+ : "text-muted-foreground hover:text-foreground hover:bg-muted/50"
319
+ }`}
320
+ >
321
+ {icon}
322
+ {label}
323
+ {badge && (
324
+ <span className="absolute -top-0.5 -right-0.5 w-1.5 h-1.5 rounded-full bg-primary" />
325
+ )}
326
+ </button>
327
+ );
328
+
329
+ const ActionButton: React.FC<{
330
+ active: boolean;
331
+ running?: boolean;
332
+ onClick: () => void;
333
+ icon: React.ReactNode;
334
+ label: string;
335
+ }> = ({ active, running, onClick, icon, label }) => (
336
+ <button
337
+ type="button"
338
+ onClick={onClick}
339
+ aria-pressed={active}
340
+ title={running ? "Agent running" : label}
341
+ className={`relative flex items-center gap-1 px-2 py-1 rounded text-xs font-medium transition-colors min-w-0 shrink-0 ${
342
+ active || running
343
+ ? "bg-primary/10 text-primary"
344
+ : "text-muted-foreground hover:text-foreground hover:bg-muted/50"
345
+ }`}
346
+ >
347
+ {icon}
348
+ {label}
349
+ {running && (
350
+ <span className="absolute -top-0.5 -right-0.5 h-1.5 w-1.5 rounded-full bg-primary" />
351
+ )}
352
+ </button>
353
+ );
@@ -0,0 +1,155 @@
1
+ /**
2
+ * SidebarTabs — Collapsed tab flags
3
+ *
4
+ * When the sidebar is closed, small vertical tabs protrude from the left edge.
5
+ * Clicking a tab opens the sidebar in that mode.
6
+ */
7
+
8
+ import React from "react";
9
+ import type { SidebarTab } from "../../hooks/useSidebar";
10
+ import { MessagesIcon } from "../icons/MessagesIcon";
11
+ import { ReviewAgentsIcon } from "../ReviewAgentsIcon";
12
+
13
+ interface SidebarTabsProps {
14
+ activeTab: SidebarTab;
15
+ onToggleTab: (tab: SidebarTab) => void;
16
+ hasDiff: boolean;
17
+ showVersionsTab?: boolean;
18
+ showFilesTab?: boolean;
19
+ showMessagesTab?: boolean;
20
+ showAgentTerminalTab?: boolean;
21
+ isAgentTerminalOpen?: boolean;
22
+ isAgentTerminalRunning?: boolean;
23
+ onToggleAgentTerminal?: () => void;
24
+ hasFileAnnotations?: boolean;
25
+ hasMessageAnnotations?: boolean;
26
+ className?: string;
27
+ }
28
+
29
+ export const SidebarTabs: React.FC<SidebarTabsProps> = ({
30
+ activeTab,
31
+ onToggleTab,
32
+ hasDiff,
33
+ showVersionsTab,
34
+ showFilesTab,
35
+ showMessagesTab,
36
+ showAgentTerminalTab,
37
+ isAgentTerminalOpen,
38
+ isAgentTerminalRunning,
39
+ onToggleAgentTerminal,
40
+ hasFileAnnotations,
41
+ hasMessageAnnotations,
42
+ className,
43
+ }) => {
44
+ return (
45
+ <div
46
+ data-sidebar-tabs="true"
47
+ className={`flex flex-col gap-1 pt-3 pl-0.5 flex-shrink-0 ${className ?? ""}`}
48
+ >
49
+ {showAgentTerminalTab && onToggleAgentTerminal && (
50
+ <button
51
+ onClick={onToggleAgentTerminal}
52
+ className={`sidebar-tab-flag group relative flex items-center justify-center w-7 h-9 rounded-r-md border border-l-0 border-border/50 bg-card/80 backdrop-blur-sm transition-colors ${
53
+ isAgentTerminalOpen || isAgentTerminalRunning
54
+ ? "text-primary"
55
+ : "text-muted-foreground hover:text-foreground hover:bg-card"
56
+ }`}
57
+ title={isAgentTerminalRunning ? "Agent running" : "Agent"}
58
+ aria-pressed={isAgentTerminalOpen}
59
+ >
60
+ <ReviewAgentsIcon className="w-3.5 h-3.5" />
61
+ {isAgentTerminalRunning && (
62
+ <span className="absolute top-1 right-1 h-1.5 w-1.5 rounded-full bg-primary" />
63
+ )}
64
+ </button>
65
+ )}
66
+
67
+ {/* TOC tab */}
68
+ <button
69
+ onClick={() => onToggleTab("toc")}
70
+ className="sidebar-tab-flag group flex items-center justify-center w-7 h-9 rounded-r-md border border-l-0 border-border/50 bg-card/80 backdrop-blur-sm text-muted-foreground hover:text-foreground hover:bg-card transition-colors"
71
+ title="Table of Contents"
72
+ >
73
+ <svg
74
+ className="w-3.5 h-3.5"
75
+ fill="none"
76
+ viewBox="0 0 24 24"
77
+ stroke="currentColor"
78
+ strokeWidth={2}
79
+ >
80
+ <path
81
+ strokeLinecap="round"
82
+ strokeLinejoin="round"
83
+ d="M4 6h16M4 10h16M4 14h10M4 18h10"
84
+ />
85
+ </svg>
86
+ </button>
87
+
88
+ {/* Versions tab — only shown when multiple versions exist */}
89
+ {showVersionsTab && (
90
+ <button
91
+ onClick={() => onToggleTab("versions")}
92
+ className="sidebar-tab-flag group relative flex items-center justify-center w-7 h-9 rounded-r-md border border-l-0 border-border/50 bg-card/80 backdrop-blur-sm text-muted-foreground hover:text-foreground hover:bg-card transition-colors"
93
+ title="Plan Versions"
94
+ >
95
+ <svg
96
+ className="w-3.5 h-3.5"
97
+ fill="none"
98
+ viewBox="0 0 24 24"
99
+ stroke="currentColor"
100
+ strokeWidth={2}
101
+ >
102
+ <path
103
+ strokeLinecap="round"
104
+ strokeLinejoin="round"
105
+ d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"
106
+ />
107
+ </svg>
108
+ {/* Availability indicator dot */}
109
+ {hasDiff && (
110
+ <span className="absolute top-1 right-1 w-1.5 h-1.5 rounded-full bg-primary" />
111
+ )}
112
+ </button>
113
+ )}
114
+
115
+ {showMessagesTab && (
116
+ <button
117
+ onClick={() => onToggleTab("messages")}
118
+ className="sidebar-tab-flag group relative flex items-center justify-center w-7 h-9 rounded-r-md border border-l-0 border-border/50 bg-card/80 backdrop-blur-sm text-muted-foreground hover:text-foreground hover:bg-card transition-colors"
119
+ title="Pick a different message"
120
+ >
121
+ <MessagesIcon />
122
+ {hasMessageAnnotations && (
123
+ <span className="absolute top-1 right-1 w-1.5 h-1.5 rounded-full bg-primary" />
124
+ )}
125
+ </button>
126
+ )}
127
+
128
+ {/* Files tab */}
129
+ {showFilesTab && (
130
+ <button
131
+ onClick={() => onToggleTab("files")}
132
+ className="sidebar-tab-flag group relative flex items-center justify-center w-7 h-9 rounded-r-md border border-l-0 border-border/50 bg-card/80 backdrop-blur-sm text-muted-foreground hover:text-foreground hover:bg-card transition-colors"
133
+ title="File Browser"
134
+ >
135
+ <svg
136
+ className="w-3.5 h-3.5"
137
+ fill="none"
138
+ viewBox="0 0 24 24"
139
+ stroke="currentColor"
140
+ strokeWidth={2}
141
+ >
142
+ <path
143
+ strokeLinecap="round"
144
+ strokeLinejoin="round"
145
+ d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"
146
+ />
147
+ </svg>
148
+ {hasFileAnnotations && (
149
+ <span className="absolute top-1 right-1 w-1.5 h-1.5 rounded-full bg-primary" />
150
+ )}
151
+ </button>
152
+ )}
153
+ </div>
154
+ );
155
+ };
@@ -0,0 +1,141 @@
1
+ /**
2
+ * VersionBrowser — Version list for plan diff
3
+ *
4
+ * Shows all versions of the current plan and allows selecting
5
+ * which version to diff against.
6
+ */
7
+
8
+ import React, { useEffect } from "react";
9
+ import type { VersionInfo, VersionEntry } from "../../hooks/usePlanDiff";
10
+
11
+ interface VersionBrowserProps {
12
+ versionInfo: VersionInfo | null;
13
+ versions: VersionEntry[];
14
+ selectedBaseVersion: number | null;
15
+ onSelectBaseVersion: (version: number) => void;
16
+ isPlanDiffActive: boolean;
17
+ hasPreviousVersion: boolean;
18
+ onActivatePlanDiff: () => void;
19
+ isLoading: boolean;
20
+ isSelectingVersion: boolean;
21
+ fetchingVersion: number | null;
22
+ onFetchVersions: () => void;
23
+ }
24
+
25
+ function relativeTime(timestamp: string): string {
26
+ if (!timestamp) return "";
27
+ const now = Date.now();
28
+ const then = new Date(timestamp).getTime();
29
+ const diff = now - then;
30
+
31
+ if (diff < 60_000) return "just now";
32
+ if (diff < 3_600_000) return `${Math.floor(diff / 60_000)}m ago`;
33
+ if (diff < 86_400_000) return `${Math.floor(diff / 3_600_000)}h ago`;
34
+ if (diff < 604_800_000) return `${Math.floor(diff / 86_400_000)}d ago`;
35
+ return new Date(timestamp).toLocaleDateString();
36
+ }
37
+
38
+ export const VersionBrowser: React.FC<VersionBrowserProps> = ({
39
+ versionInfo,
40
+ versions,
41
+ selectedBaseVersion,
42
+ onSelectBaseVersion,
43
+ isPlanDiffActive,
44
+ hasPreviousVersion,
45
+ onActivatePlanDiff,
46
+ isLoading,
47
+ isSelectingVersion,
48
+ fetchingVersion,
49
+ onFetchVersions,
50
+ }) => {
51
+ // Fetch version list once versionInfo is available
52
+ useEffect(() => {
53
+ if (versionInfo && versions.length === 0) {
54
+ onFetchVersions();
55
+ }
56
+ }, [versionInfo, versions.length, onFetchVersions]);
57
+
58
+ if (!versionInfo) {
59
+ return (
60
+ <div className="p-4 text-xs text-muted-foreground text-center">
61
+ No version history available.
62
+ </div>
63
+ );
64
+ }
65
+
66
+ const currentVersion = versionInfo.version;
67
+ const totalVersions = versionInfo.totalVersions;
68
+
69
+ return (
70
+ <div className="p-3">
71
+ {/* Current version info */}
72
+ <div className="mb-3">
73
+ <div className="text-[10px] font-medium text-muted-foreground uppercase tracking-wider mb-1">
74
+ Current Plan
75
+ </div>
76
+ <div className="text-xs text-foreground">
77
+ Version {currentVersion} of {totalVersions}
78
+ </div>
79
+ </div>
80
+
81
+ {/* Show Diff button if not active */}
82
+ {hasPreviousVersion && !isPlanDiffActive && (
83
+ <button
84
+ onClick={onActivatePlanDiff}
85
+ className="w-full mb-3 px-3 py-1.5 text-xs font-medium rounded-md bg-primary/10 text-primary hover:bg-primary/20 transition-colors border border-primary/20"
86
+ >
87
+ Show Changes
88
+ </button>
89
+ )}
90
+
91
+ {/* Version list */}
92
+ {totalVersions > 1 && (
93
+ <div className="mb-3">
94
+ <div className="text-[10px] font-medium text-muted-foreground uppercase tracking-wider mb-2">
95
+ Compare Against
96
+ </div>
97
+
98
+ {isLoading ? (
99
+ <div className="text-xs text-muted-foreground py-2 text-center">
100
+ Loading...
101
+ </div>
102
+ ) : (
103
+ <div className="space-y-0.5">
104
+ {versions
105
+ .filter((v) => v.version !== currentVersion)
106
+ .reverse()
107
+ .map((v) => {
108
+ const isSelected = selectedBaseVersion === v.version;
109
+ return (
110
+ <button
111
+ key={v.version}
112
+ onClick={() => onSelectBaseVersion(v.version)}
113
+ className={`w-full text-left px-2 py-1.5 rounded text-xs transition-colors ${
114
+ isSelected
115
+ ? "bg-primary/10 text-primary border border-primary/30"
116
+ : "text-foreground hover:bg-muted/50 border border-transparent"
117
+ }`}
118
+ >
119
+ <div className="flex items-center justify-between">
120
+ <span className="font-medium">
121
+ v{v.version}
122
+ {v.version === currentVersion - 1 && (
123
+ <span className="ml-1 text-muted-foreground font-normal">
124
+ (previous)
125
+ </span>
126
+ )}
127
+ </span>
128
+ <span className="text-[10px] text-muted-foreground">
129
+ {fetchingVersion === v.version ? "Loading..." : relativeTime(v.timestamp)}
130
+ </span>
131
+ </div>
132
+ </button>
133
+ );
134
+ })}
135
+ </div>
136
+ )}
137
+ </div>
138
+ )}
139
+ </div>
140
+ );
141
+ };
@@ -0,0 +1,7 @@
1
+ // Vite globals injected at build time
2
+ declare const __APP_VERSION__: string;
3
+
4
+ // Image asset module declarations live in ../globals.d.ts (the package's
5
+ // ambient-declaration home, /// <reference>'d by each asset-importing
6
+ // component so consumer compilers load them too). Declaring them here as
7
+ // well would be a duplicate-identifier error under skipLibCheck: false.
@@ -0,0 +1,44 @@
1
+ import { Slot } from "@radix-ui/react-slot";
2
+ import { cva, type VariantProps } from "class-variance-authority";
3
+ import type * as React from "react";
4
+
5
+ import { cn } from "../../lib/utils";
6
+
7
+ /**
8
+ * Badge — small labelled token (status, category, count). Token-driven variants.
9
+ * Pass `asChild` to render as a link/button. Override padding/size via className
10
+ * for tighter inline badges (e.g. `px-1.5 py-px text-[10px]`).
11
+ */
12
+ const badgeVariants = cva(
13
+ "inline-flex w-fit shrink-0 items-center justify-center gap-1 overflow-auto whitespace-nowrap rounded-md border px-3 py-1 text-xs font-medium transition-[color,box-shadow] [&>svg]:size-3 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40",
14
+ {
15
+ variants: {
16
+ variant: {
17
+ default: "border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90",
18
+ secondary:
19
+ "border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90",
20
+ destructive:
21
+ "border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40",
22
+ outline: "text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
23
+ },
24
+ },
25
+ defaultVariants: {
26
+ variant: "default",
27
+ },
28
+ },
29
+ );
30
+
31
+ function Badge({
32
+ className,
33
+ variant,
34
+ asChild = false,
35
+ ...props
36
+ }: React.ComponentProps<"span"> & VariantProps<typeof badgeVariants> & { asChild?: boolean }) {
37
+ const Comp = asChild ? Slot : "span";
38
+
39
+ return (
40
+ <Comp data-slot="badge" className={cn(badgeVariants({ variant }), className)} {...props} />
41
+ );
42
+ }
43
+
44
+ export { Badge, badgeVariants };