@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.
- package/README.md +60 -0
- package/assets/diff-options.png +0 -0
- package/assets/icon-codex.png +0 -0
- package/assets/look-flat.png +0 -0
- package/assets/look-grid.png +0 -0
- package/assets/review-sections.png +0 -0
- package/assets/review-tree.png +0 -0
- package/assets/workspaces.webp +0 -0
- package/components/AISettingsTab.tsx +145 -0
- package/components/ActionMenu.tsx +101 -0
- package/components/AgentControls.tsx +216 -0
- package/components/AgentsTab.tsx +1294 -0
- package/components/AnnotationPanel.tsx +731 -0
- package/components/AnnotationSidebar.tsx +86 -0
- package/components/AnnotationToolbar.tsx +323 -0
- package/components/AnnotationToolstrip.tsx +355 -0
- package/components/ApproveDropdown.tsx +170 -0
- package/components/AttachmentsButton.tsx +410 -0
- package/components/BlockRenderer.tsx +163 -0
- package/components/BorderTrail.tsx +38 -0
- package/components/CodeFilePicker.tsx +64 -0
- package/components/CodeFilePopout.tsx +602 -0
- package/components/CodePathValidationContext.tsx +14 -0
- package/components/CommentPopover.tsx +512 -0
- package/components/CompletionOverlay.tsx +99 -0
- package/components/ConfirmDialog.tsx +128 -0
- package/components/DocBadges.tsx +231 -0
- package/components/EditorAnnotationCard.tsx +65 -0
- package/components/ExportModal.tsx +565 -0
- package/components/FloatingQuickLabelPicker.tsx +144 -0
- package/components/GitHubIcon.tsx +16 -0
- package/components/GitLabIcon.tsx +30 -0
- package/components/GraphvizBlock.tsx +511 -0
- package/components/ImageAnnotator/Canvas.tsx +118 -0
- package/components/ImageAnnotator/Toolbar.tsx +218 -0
- package/components/ImageAnnotator/index.tsx +272 -0
- package/components/ImageAnnotator/types.ts +39 -0
- package/components/ImageAnnotator/utils.ts +147 -0
- package/components/ImageThumbnail.tsx +126 -0
- package/components/ImportModal.tsx +146 -0
- package/components/InlineMarkdown.tsx +1094 -0
- package/components/KeyboardShortcuts.tsx +208 -0
- package/components/Landing.tsx +496 -0
- package/components/ListItemBody.tsx +62 -0
- package/components/ListMarker.tsx +73 -0
- package/components/LookAndFeelAnnouncementDialog.tsx +230 -0
- package/components/MarkdownEditor.tsx +44 -0
- package/components/MenuVersionSection.tsx +91 -0
- package/components/MermaidBlock.tsx +579 -0
- package/components/ModeToggle.tsx +68 -0
- package/components/OpenInAppButton.tsx +299 -0
- package/components/OverlayScrollArea.tsx +82 -0
- package/components/PermissionModeSetup.tsx +92 -0
- package/components/PinpointOverlay.tsx +99 -0
- package/components/PlanAIAnnouncementDialog.tsx +187 -0
- package/components/PlanHeaderMenu.tsx +299 -0
- package/components/PopoutDialog.tsx +91 -0
- package/components/Popover.tsx +27 -0
- package/components/ProviderIcons.tsx +51 -0
- package/components/PullRequestIcon.tsx +12 -0
- package/components/QuickLabelDropdown.tsx +63 -0
- package/components/RenderedMarkdown.tsx +54 -0
- package/components/RepoIcon.tsx +12 -0
- package/components/ResizeHandle.tsx +86 -0
- package/components/ReviewAgentsIcon.tsx +18 -0
- package/components/SearchableSelect.tsx +157 -0
- package/components/Settings.tsx +2220 -0
- package/components/SparklesIcon.tsx +45 -0
- package/components/StickyHeaderLane.tsx +260 -0
- package/components/TableOfContents.tsx +157 -0
- package/components/TaterSpritePullup.tsx +34 -0
- package/components/TaterSpriteRunning.tsx +54 -0
- package/components/TaterSpriteSitting.tsx +37 -0
- package/components/TextShimmer.tsx +57 -0
- package/components/ThemeProvider.tsx +162 -0
- package/components/ThemeTab.tsx +139 -0
- package/components/ToolbarButtons.tsx +116 -0
- package/components/Tooltip.tsx +45 -0
- package/components/Viewer.tsx +977 -0
- package/components/ai/AIProviderBar.tsx +95 -0
- package/components/ai/DocumentAIChatPanel.tsx +313 -0
- package/components/blocks/AlertBlock.tsx +58 -0
- package/components/blocks/Callout.tsx +60 -0
- package/components/blocks/CodeBlock.tsx +75 -0
- package/components/blocks/HtmlBlock.tsx +122 -0
- package/components/blocks/MathBlock.tsx +36 -0
- package/components/blocks/TableBlock.tsx +146 -0
- package/components/blocks/TablePopout.tsx +279 -0
- package/components/blocks/TableToolbar.tsx +153 -0
- package/components/blocks/proseBody.tsx +102 -0
- package/components/core/button.tsx +44 -0
- package/components/core/textarea.tsx +26 -0
- package/components/diagramLanguages.ts +14 -0
- package/components/goal-setup/GoalSetupSurface.tsx +1354 -0
- package/components/html-viewer/HtmlViewer.tsx +386 -0
- package/components/html-viewer/bridge-script.ts +505 -0
- package/components/html-viewer/index.ts +1 -0
- package/components/html-viewer/useHtmlAnnotation.ts +400 -0
- package/components/icons/AgentIcons.tsx +66 -0
- package/components/icons/AppIcon.tsx +56 -0
- package/components/icons/MessagesIcon.tsx +11 -0
- package/components/icons/ObsidianIcons.tsx +208 -0
- package/components/icons/app/android-studio.svg +369 -0
- package/components/icons/app/antigravity.svg +97 -0
- package/components/icons/app/cursor.svg +16 -0
- package/components/icons/app/file-explorer.svg +20 -0
- package/components/icons/app/finder.png +0 -0
- package/components/icons/app/ghostty.svg +13 -0
- package/components/icons/app/iterm2.svg +13 -0
- package/components/icons/app/powershell.svg +14 -0
- package/components/icons/app/sublime-text.svg +17 -0
- package/components/icons/app/terminal.png +0 -0
- package/components/icons/app/textmate.png +0 -0
- package/components/icons/app/vscode.svg +39 -0
- package/components/icons/app/warp.png +0 -0
- package/components/icons/app/xcode.png +0 -0
- package/components/icons/app/zed-dark.svg +15 -0
- package/components/icons/app/zed.svg +15 -0
- package/components/icons/themeIcons.tsx +47 -0
- package/components/mermaidSvg.ts +33 -0
- package/components/plan-diff/PlanCleanDiffView.tsx +884 -0
- package/components/plan-diff/PlanDiffBadge.tsx +48 -0
- package/components/plan-diff/PlanDiffModeSwitcher.tsx +103 -0
- package/components/plan-diff/PlanDiffViewer.tsx +214 -0
- package/components/plan-diff/PlanRawDiffView.tsx +102 -0
- package/components/plan-diff/VSCodeIcon.tsx +132 -0
- package/components/settings/HooksTab.tsx +208 -0
- package/components/sidebar/ArchiveBrowser.tsx +98 -0
- package/components/sidebar/CountBadge.tsx +12 -0
- package/components/sidebar/FileBrowser.tsx +498 -0
- package/components/sidebar/MessagesBrowser.tsx +109 -0
- package/components/sidebar/SidebarContainer.tsx +353 -0
- package/components/sidebar/SidebarTabs.tsx +155 -0
- package/components/sidebar/VersionBrowser.tsx +141 -0
- package/components/types.d.ts +7 -0
- package/components/ui/badge.tsx +44 -0
- package/components/ui/button.tsx +83 -0
- package/components/ui/card.tsx +58 -0
- package/components/ui/dialog.tsx +106 -0
- package/components/ui/dropdown-menu.tsx +240 -0
- package/components/ui/state-pill.tsx +45 -0
- package/components/ui/tabs.tsx +46 -0
- package/components/ui/textarea.tsx +25 -0
- package/config/configStore.ts +214 -0
- package/config/index.ts +4 -0
- package/config/reviewView.ts +42 -0
- package/config/settings.ts +310 -0
- package/config/useConfig.ts +20 -0
- package/configure.ts +69 -0
- package/globals.d.ts +14 -0
- package/hooks/pfm/useCodeFilePopout.ts +111 -0
- package/hooks/useAIChat.ts +532 -0
- package/hooks/useAIProviderConfig.ts +115 -0
- package/hooks/useActiveSection.ts +78 -0
- package/hooks/useAgentJobs.ts +306 -0
- package/hooks/useAgentSettings.ts +579 -0
- package/hooks/useAgents.ts +90 -0
- package/hooks/useAnnotationDraft.ts +523 -0
- package/hooks/useAnnotationHighlighter.ts +1170 -0
- package/hooks/useArchive.ts +171 -0
- package/hooks/useAutoClose.ts +104 -0
- package/hooks/useCodeAnnotationDraft.ts +194 -0
- package/hooks/useCodeFilePopout.ts +1 -0
- package/hooks/useDismissOnOutsideAndEscape.ts +50 -0
- package/hooks/useDraggable.ts +108 -0
- package/hooks/useEditorAnnotations.ts +64 -0
- package/hooks/useExternalAnnotationHighlights.ts +105 -0
- package/hooks/useExternalAnnotations.ts +268 -0
- package/hooks/useFileBrowser.ts +402 -0
- package/hooks/useInputMethodSwitch.ts +89 -0
- package/hooks/useIsMobile.ts +17 -0
- package/hooks/useLinkedDoc.ts +494 -0
- package/hooks/useOverlayViewport.ts +36 -0
- package/hooks/usePinpoint.ts +182 -0
- package/hooks/usePlanDiff.ts +176 -0
- package/hooks/usePrintMode.ts +27 -0
- package/hooks/useResizablePanel.ts +173 -0
- package/hooks/useScrollViewport.ts +38 -0
- package/hooks/useSharing.ts +441 -0
- package/hooks/useSidebar.ts +55 -0
- package/hooks/useUpdateCheck.ts +132 -0
- package/hooks/useValidatedCodePaths.ts +94 -0
- package/icons/GitUser.tsx +17 -0
- package/lib/utils.ts +11 -0
- package/package.json +117 -0
- package/plannotator.webp +0 -0
- package/print.css +427 -0
- package/shortcuts/code-review/ai.shortcuts.ts +25 -0
- package/shortcuts/code-review/allFilesDiff.shortcuts.ts +46 -0
- package/shortcuts/code-review/annotationToolbar.shortcuts.ts +30 -0
- package/shortcuts/code-review/fileTree.shortcuts.ts +35 -0
- package/shortcuts/code-review/prComments.shortcuts.ts +18 -0
- package/shortcuts/code-review/suggestionModal.shortcuts.ts +25 -0
- package/shortcuts/code-review/tourDialog.shortcuts.ts +18 -0
- package/shortcuts/core.ts +399 -0
- package/shortcuts/index.ts +21 -0
- package/shortcuts/plan-review/annotationPanel.shortcuts.ts +25 -0
- package/shortcuts/plan-review/annotationToolbar.shortcuts.ts +39 -0
- package/shortcuts/plan-review/commentPopover.shortcuts.ts +21 -0
- package/shortcuts/plan-review/goalSetup.shortcuts.ts +10 -0
- package/shortcuts/plan-review/imageAnnotator.shortcuts.ts +42 -0
- package/shortcuts/plan-review/inputMethod.shortcuts.ts +30 -0
- package/shortcuts/plan-review/sidebar.shortcuts.ts +34 -0
- package/shortcuts/plan-review/viewer.shortcuts.ts +24 -0
- package/shortcuts/runtime.ts +259 -0
- package/sprite_package_additional/index.html +34 -0
- package/sprite_package_additional/sprite.png +0 -0
- package/sprite_package_new/index.html +34 -0
- package/sprite_package_new/sprite.png +0 -0
- package/sprite_package_pulluphang/index.html +34 -0
- package/sprite_package_pulluphang/sprite.png +0 -0
- package/styles.css +1 -0
- package/theme.css +893 -0
- package/themes/adwaita.css +62 -0
- package/themes/andromeeda.css +66 -0
- package/themes/aurora-x.css +66 -0
- package/themes/ayu-dark.css +66 -0
- package/themes/caffeine.css +113 -0
- package/themes/catppuccin.css +62 -0
- package/themes/claude-plus.css +60 -0
- package/themes/cursor-hc.css +34 -0
- package/themes/cursor-midnight.css +34 -0
- package/themes/cursor.css +62 -0
- package/themes/dark-plus.css +66 -0
- package/themes/doom-64.css +109 -0
- package/themes/dracula.css +33 -0
- package/themes/everforest-hard.css +62 -0
- package/themes/everforest-soft.css +62 -0
- package/themes/everforest.css +62 -0
- package/themes/github.css +66 -0
- package/themes/gruvbox.css +62 -0
- package/themes/houston.css +66 -0
- package/themes/kanagawa-dragon.css +34 -0
- package/themes/kanagawa-lotus.css +34 -0
- package/themes/kanagawa-wave.css +34 -0
- package/themes/laserwave.css +66 -0
- package/themes/material.css +62 -0
- package/themes/min.css +66 -0
- package/themes/monokai-pro.css +34 -0
- package/themes/neutral.css +59 -0
- package/themes/night-owl.css +66 -0
- package/themes/nord.css +66 -0
- package/themes/one-dark-pro.css +66 -0
- package/themes/one-light.css +66 -0
- package/themes/paulmillr.css +34 -0
- package/themes/plannotator.css +60 -0
- package/themes/plastic.css +66 -0
- package/themes/poimandres.css +66 -0
- package/themes/quantum-rose.css +109 -0
- package/themes/red.css +66 -0
- package/themes/rose-pine.css +62 -0
- package/themes/simple.css +124 -0
- package/themes/slack.css +66 -0
- package/themes/snazzy-light.css +66 -0
- package/themes/soft-pop.css +60 -0
- package/themes/solar-dusk.css +109 -0
- package/themes/solarized.css +66 -0
- package/themes/synthwave-84.css +34 -0
- package/themes/terminal.css +62 -0
- package/themes/tinacious.css +57 -0
- package/themes/tokyo-night.css +62 -0
- package/themes/vesper.css +62 -0
- package/themes/vitesse-black.css +66 -0
- package/themes/vitesse.css +62 -0
- package/types.ts +264 -0
- package/utils/agentSwitch.ts +71 -0
- package/utils/aiChatFormat.ts +33 -0
- package/utils/aiPrompt.ts +29 -0
- package/utils/aiProvider.ts +217 -0
- package/utils/anchors.ts +11 -0
- package/utils/annotateAgentTerminal.ts +29 -0
- package/utils/annotationHelpers.ts +101 -0
- package/utils/bear.ts +59 -0
- package/utils/blockTargeting.ts +240 -0
- package/utils/callback.ts +99 -0
- package/utils/commentContent.ts +8 -0
- package/utils/defaultNotesApp.ts +20 -0
- package/utils/diffFonts.ts +33 -0
- package/utils/editorMode.ts +31 -0
- package/utils/fileBrowser.ts +40 -0
- package/utils/generateId.ts +7 -0
- package/utils/generateIdentity.ts +29 -0
- package/utils/identity.ts +112 -0
- package/utils/inlineTransforms.ts +41 -0
- package/utils/inputMethod.ts +17 -0
- package/utils/lookAndFeelAnnouncement.ts +18 -0
- package/utils/obsidian.ts +201 -0
- package/utils/octarine.ts +53 -0
- package/utils/parser.ts +991 -0
- package/utils/permissionMode.ts +77 -0
- package/utils/planAIAnnouncement.ts +17 -0
- package/utils/planAgentInstructions.ts +137 -0
- package/utils/planDiffEngine.ts +590 -0
- package/utils/planSave.ts +49 -0
- package/utils/platform.ts +11 -0
- package/utils/quickLabels.ts +77 -0
- package/utils/reviewAgentInstructions.ts +182 -0
- package/utils/sanitizeHtml.ts +45 -0
- package/utils/sharing.ts +350 -0
- package/utils/slugify.ts +37 -0
- package/utils/storage.ts +141 -0
- package/utils/themeRegistry.ts +567 -0
- package/utils/uiPreferences.ts +34 -0
- package/utils/upload.ts +56 -0
- 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
|
+
};
|