@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,496 @@
|
|
|
1
|
+
/// <reference path="../globals.d.ts" />
|
|
2
|
+
import React from "react";
|
|
3
|
+
import logoImage from "../plannotator.webp";
|
|
4
|
+
import { ModeToggle } from "./ModeToggle";
|
|
5
|
+
|
|
6
|
+
interface LandingProps {
|
|
7
|
+
onEnter?: () => void;
|
|
8
|
+
shareBaseUrl?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const Landing: React.FC<LandingProps> = ({ onEnter, shareBaseUrl }) => {
|
|
12
|
+
const demoUrl = shareBaseUrl || "https://share.plannotator.ai";
|
|
13
|
+
return (
|
|
14
|
+
<div className="min-h-screen bg-background text-foreground">
|
|
15
|
+
{/* Nav */}
|
|
16
|
+
<nav className="fixed top-0 left-0 right-0 h-12 flex items-center justify-between px-6 bg-background/80 backdrop-blur-sm border-b border-border/30 z-50">
|
|
17
|
+
<div className="flex items-center gap-2">
|
|
18
|
+
<span className="text-sm font-semibold tracking-tight">
|
|
19
|
+
Plannotator
|
|
20
|
+
</span>
|
|
21
|
+
</div>
|
|
22
|
+
<div className="flex items-center gap-3">
|
|
23
|
+
<div className="flex items-center gap-2 text-xs">
|
|
24
|
+
<a
|
|
25
|
+
href="https://github.com/backnotprop/plannotator"
|
|
26
|
+
target="_blank"
|
|
27
|
+
rel="noopener noreferrer"
|
|
28
|
+
className="text-muted-foreground hover:text-foreground transition-colors"
|
|
29
|
+
>
|
|
30
|
+
GitHub
|
|
31
|
+
</a>
|
|
32
|
+
<span className="text-muted-foreground/50">|</span>
|
|
33
|
+
<a
|
|
34
|
+
href="https://github.com/backnotprop/plannotator/blob/main/apps/hook/README.md"
|
|
35
|
+
target="_blank"
|
|
36
|
+
rel="noopener noreferrer"
|
|
37
|
+
className="text-muted-foreground hover:text-foreground transition-colors"
|
|
38
|
+
>
|
|
39
|
+
Install
|
|
40
|
+
</a>
|
|
41
|
+
</div>
|
|
42
|
+
<ModeToggle />
|
|
43
|
+
</div>
|
|
44
|
+
</nav>
|
|
45
|
+
|
|
46
|
+
{/* Content wrapper with vertical borders */}
|
|
47
|
+
<div className="max-w-4xl mx-auto border-x border-border/30">
|
|
48
|
+
{/* Hero */}
|
|
49
|
+
<section className="pt-32 pb-20 px-8">
|
|
50
|
+
<div className="max-w-2xl">
|
|
51
|
+
<div className="inline-flex items-center gap-2 px-3 py-1 rounded-full bg-primary/10 text-primary text-xs font-medium mb-6">
|
|
52
|
+
<span className="w-1.5 h-1.5 rounded-full bg-primary animate-pulse" />
|
|
53
|
+
For Claude Code & OpenCode
|
|
54
|
+
</div>
|
|
55
|
+
|
|
56
|
+
<div className="flex items-center gap-6 mb-4">
|
|
57
|
+
<h1 className="text-4xl md:text-5xl font-bold tracking-tight leading-[1.1]">
|
|
58
|
+
Annotate plans.
|
|
59
|
+
<br />
|
|
60
|
+
<span className="text-muted-foreground">
|
|
61
|
+
Not in the terminal.
|
|
62
|
+
</span>
|
|
63
|
+
</h1>
|
|
64
|
+
<img
|
|
65
|
+
src={logoImage}
|
|
66
|
+
alt="Plannotator logo"
|
|
67
|
+
className="w-20 h-20 md:w-24 md:h-24 object-contain shrink-0 -scale-x-100"
|
|
68
|
+
/>
|
|
69
|
+
</div>
|
|
70
|
+
|
|
71
|
+
<p className="text-lg text-muted-foreground mb-8 max-w-lg">
|
|
72
|
+
Interactive Plan Review for coding agents. Mark up and refine plans visually,
|
|
73
|
+
share for team collaboration. Works with Claude Code and OpenCode.
|
|
74
|
+
</p>
|
|
75
|
+
|
|
76
|
+
<div className="flex flex-col gap-3">
|
|
77
|
+
<div className="flex items-center gap-3">
|
|
78
|
+
{/* YouTube button - Claude Code */}
|
|
79
|
+
<a
|
|
80
|
+
href="https://www.youtube.com/watch?v=a_AT7cEN_9I"
|
|
81
|
+
target="_blank"
|
|
82
|
+
rel="noopener noreferrer"
|
|
83
|
+
className="inline-flex items-center gap-2 px-5 py-2.5 rounded-lg bg-secondary text-white font-medium hover:opacity-90 transition-opacity"
|
|
84
|
+
>
|
|
85
|
+
<svg
|
|
86
|
+
className="w-5 h-5"
|
|
87
|
+
viewBox="0 0 24 24"
|
|
88
|
+
fill="currentColor"
|
|
89
|
+
>
|
|
90
|
+
<path d="M23.498 6.186a3.016 3.016 0 0 0-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 0 0 .502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 0 0 2.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 0 0 2.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z" />
|
|
91
|
+
</svg>
|
|
92
|
+
Watch for Claude Code
|
|
93
|
+
</a>
|
|
94
|
+
|
|
95
|
+
{/* Open Demo button */}
|
|
96
|
+
{onEnter ? (
|
|
97
|
+
<button
|
|
98
|
+
onClick={onEnter}
|
|
99
|
+
className="inline-flex items-center gap-2 px-5 py-2.5 rounded-lg bg-primary text-primary-foreground font-medium hover:opacity-90 transition-opacity"
|
|
100
|
+
>
|
|
101
|
+
Open Demo
|
|
102
|
+
<svg
|
|
103
|
+
className="w-4 h-4"
|
|
104
|
+
fill="none"
|
|
105
|
+
viewBox="0 0 24 24"
|
|
106
|
+
stroke="currentColor"
|
|
107
|
+
strokeWidth={2}
|
|
108
|
+
>
|
|
109
|
+
<path
|
|
110
|
+
strokeLinecap="round"
|
|
111
|
+
strokeLinejoin="round"
|
|
112
|
+
d="M13 7l5 5m0 0l-5 5m5-5H6"
|
|
113
|
+
/>
|
|
114
|
+
</svg>
|
|
115
|
+
</button>
|
|
116
|
+
) : (
|
|
117
|
+
<a
|
|
118
|
+
href={demoUrl}
|
|
119
|
+
className="inline-flex items-center gap-2 px-5 py-2.5 rounded-lg bg-primary text-primary-foreground font-medium hover:opacity-90 transition-opacity"
|
|
120
|
+
>
|
|
121
|
+
Open Demo
|
|
122
|
+
<svg
|
|
123
|
+
className="w-4 h-4"
|
|
124
|
+
fill="none"
|
|
125
|
+
viewBox="0 0 24 24"
|
|
126
|
+
stroke="currentColor"
|
|
127
|
+
strokeWidth={2}
|
|
128
|
+
>
|
|
129
|
+
<path
|
|
130
|
+
strokeLinecap="round"
|
|
131
|
+
strokeLinejoin="round"
|
|
132
|
+
d="M13 7l5 5m0 0l-5 5m5-5H6"
|
|
133
|
+
/>
|
|
134
|
+
</svg>
|
|
135
|
+
</a>
|
|
136
|
+
)}
|
|
137
|
+
</div>
|
|
138
|
+
|
|
139
|
+
{/* OpenCode video link */}
|
|
140
|
+
<a
|
|
141
|
+
href="https://youtu.be/_N7uo0EFI-U"
|
|
142
|
+
target="_blank"
|
|
143
|
+
rel="noopener noreferrer"
|
|
144
|
+
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
|
145
|
+
>
|
|
146
|
+
Also watch for OpenCode →
|
|
147
|
+
</a>
|
|
148
|
+
</div>
|
|
149
|
+
</div>
|
|
150
|
+
</section>
|
|
151
|
+
|
|
152
|
+
{/* Problem → Solution */}
|
|
153
|
+
<section className="py-16 px-8 border-t border-border/30">
|
|
154
|
+
<div className="grid md:grid-cols-2 gap-8 max-w-2xl">
|
|
155
|
+
<div>
|
|
156
|
+
<h3 className="text-xs font-semibold uppercase tracking-wider text-muted-foreground mb-3">
|
|
157
|
+
The Problem
|
|
158
|
+
</h3>
|
|
159
|
+
<p className="text-foreground/90">
|
|
160
|
+
Coding agents show plans in the terminal. You approve or deny, but
|
|
161
|
+
giving specific feedback means typing everything out. Hard to
|
|
162
|
+
reference exact sections. Zero collaboration features.
|
|
163
|
+
</p>
|
|
164
|
+
</div>
|
|
165
|
+
<div>
|
|
166
|
+
<h3 className="text-xs font-semibold uppercase tracking-wider text-muted-foreground mb-3">
|
|
167
|
+
The Solution
|
|
168
|
+
</h3>
|
|
169
|
+
<p className="text-foreground/90">
|
|
170
|
+
Select the exact parts of the plan you want to change. Mark for
|
|
171
|
+
deletion, add a comment, or suggest a replacement. Share plans
|
|
172
|
+
with your team. Feedback flows back to your agent automatically.
|
|
173
|
+
</p>
|
|
174
|
+
</div>
|
|
175
|
+
</div>
|
|
176
|
+
</section>
|
|
177
|
+
|
|
178
|
+
{/* Privacy & Sharing */}
|
|
179
|
+
<section className="py-16 px-8 border-t border-border/30">
|
|
180
|
+
<div className="grid md:grid-cols-2 gap-8 max-w-2xl">
|
|
181
|
+
<div className="flex items-start gap-4">
|
|
182
|
+
<div className="w-10 h-10 rounded-lg bg-secondary/10 flex items-center justify-center shrink-0">
|
|
183
|
+
<svg
|
|
184
|
+
className="w-5 h-5 text-secondary"
|
|
185
|
+
fill="none"
|
|
186
|
+
viewBox="0 0 24 24"
|
|
187
|
+
stroke="currentColor"
|
|
188
|
+
strokeWidth={2}
|
|
189
|
+
>
|
|
190
|
+
<path
|
|
191
|
+
strokeLinecap="round"
|
|
192
|
+
strokeLinejoin="round"
|
|
193
|
+
d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z"
|
|
194
|
+
/>
|
|
195
|
+
</svg>
|
|
196
|
+
</div>
|
|
197
|
+
<div>
|
|
198
|
+
<h3 className="font-semibold mb-1">Runs locally.</h3>
|
|
199
|
+
<p className="text-muted-foreground text-sm">
|
|
200
|
+
Local plugin. No network requests. Plannotator runs entirely
|
|
201
|
+
in your browser. Plans never leave your machine.
|
|
202
|
+
</p>
|
|
203
|
+
</div>
|
|
204
|
+
</div>
|
|
205
|
+
<div className="flex items-start gap-4">
|
|
206
|
+
<div className="w-10 h-10 rounded-lg bg-primary/10 flex items-center justify-center shrink-0">
|
|
207
|
+
<svg
|
|
208
|
+
className="w-5 h-5 text-primary"
|
|
209
|
+
fill="none"
|
|
210
|
+
viewBox="0 0 24 24"
|
|
211
|
+
stroke="currentColor"
|
|
212
|
+
strokeWidth={2}
|
|
213
|
+
>
|
|
214
|
+
<path
|
|
215
|
+
strokeLinecap="round"
|
|
216
|
+
strokeLinejoin="round"
|
|
217
|
+
d="M8.684 13.342C8.886 12.938 9 12.482 9 12c0-.482-.114-.938-.316-1.342m0 2.684a3 3 0 110-2.684m0 2.684l6.632 3.316m-6.632-6l6.632-3.316m0 0a3 3 0 105.367-2.684 3 3 0 00-5.367 2.684zm0 9.316a3 3 0 105.368 2.684 3 3 0 00-5.368-2.684z"
|
|
218
|
+
/>
|
|
219
|
+
</svg>
|
|
220
|
+
</div>
|
|
221
|
+
<div>
|
|
222
|
+
<h3 className="font-semibold mb-1">Share privately.</h3>
|
|
223
|
+
<p className="text-muted-foreground text-sm">
|
|
224
|
+
Collaborate without a backend. Plans and annotations compress
|
|
225
|
+
into the URL itself — share a link. No accounts, no database,
|
|
226
|
+
no third parties. Inspired by{" "}
|
|
227
|
+
<a
|
|
228
|
+
href="https://github.com/antonmedv/textarea"
|
|
229
|
+
target="_blank"
|
|
230
|
+
rel="noopener noreferrer"
|
|
231
|
+
className="text-primary hover:underline"
|
|
232
|
+
>
|
|
233
|
+
textarea.my
|
|
234
|
+
</a>
|
|
235
|
+
.
|
|
236
|
+
</p>
|
|
237
|
+
</div>
|
|
238
|
+
</div>
|
|
239
|
+
<div className="flex items-start gap-4">
|
|
240
|
+
<div className="w-10 h-10 rounded-lg bg-[#7c3aed]/10 flex items-center justify-center shrink-0">
|
|
241
|
+
<svg
|
|
242
|
+
className="w-5 h-5 text-[#7c3aed]"
|
|
243
|
+
fill="none"
|
|
244
|
+
viewBox="0 0 24 24"
|
|
245
|
+
stroke="currentColor"
|
|
246
|
+
strokeWidth={2}
|
|
247
|
+
>
|
|
248
|
+
<path
|
|
249
|
+
strokeLinecap="round"
|
|
250
|
+
strokeLinejoin="round"
|
|
251
|
+
d="M19.5 14.25v-2.625a3.375 3.375 0 00-3.375-3.375h-1.5A1.125 1.125 0 0113.5 7.125v-1.5a3.375 3.375 0 00-3.375-3.375H8.25m0 12.75h7.5m-7.5 3H12M10.5 2.25H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 00-9-9z"
|
|
252
|
+
/>
|
|
253
|
+
</svg>
|
|
254
|
+
</div>
|
|
255
|
+
<div>
|
|
256
|
+
<h3 className="font-semibold mb-1">Save to Obsidian.</h3>
|
|
257
|
+
<p className="text-muted-foreground text-sm">
|
|
258
|
+
Approved plans auto-save to your vault with frontmatter and
|
|
259
|
+
auto-extracted tags. Build a searchable archive of every plan
|
|
260
|
+
your agents create.
|
|
261
|
+
</p>
|
|
262
|
+
</div>
|
|
263
|
+
</div>
|
|
264
|
+
</div>
|
|
265
|
+
</section>
|
|
266
|
+
|
|
267
|
+
{/* Preview */}
|
|
268
|
+
<section className="py-16 px-8 border-t border-border/30">
|
|
269
|
+
<h2 className="text-xl font-semibold mb-8 text-center">
|
|
270
|
+
What it looks like
|
|
271
|
+
</h2>
|
|
272
|
+
|
|
273
|
+
{/* App Preview */}
|
|
274
|
+
<div className="rounded-xl border border-border/50 bg-card/50 overflow-hidden shadow-2xl">
|
|
275
|
+
{/* Mock Header */}
|
|
276
|
+
<div className="h-10 bg-card/80 border-b border-border/30 flex items-center px-4 gap-2">
|
|
277
|
+
<div className="flex gap-1.5">
|
|
278
|
+
<div className="w-2.5 h-2.5 rounded-full bg-destructive/60" />
|
|
279
|
+
<div className="w-2.5 h-2.5 rounded-full bg-accent/60" />
|
|
280
|
+
<div className="w-2.5 h-2.5 rounded-full bg-secondary/60" />
|
|
281
|
+
</div>
|
|
282
|
+
<div className="flex-1 flex justify-center">
|
|
283
|
+
<div className="flex items-center gap-1.5 text-xs text-muted-foreground">
|
|
284
|
+
<div className="w-4 h-4 rounded bg-primary/20" />
|
|
285
|
+
<span>Plannotator</span>
|
|
286
|
+
</div>
|
|
287
|
+
</div>
|
|
288
|
+
<div className="w-16" />
|
|
289
|
+
</div>
|
|
290
|
+
|
|
291
|
+
{/* Mock Content */}
|
|
292
|
+
<div className="flex h-80">
|
|
293
|
+
{/* Document Area */}
|
|
294
|
+
<div className="flex-1 p-6 bg-background/50 flex items-start justify-center">
|
|
295
|
+
<div className="w-full max-w-sm bg-card rounded-lg border border-border/30 p-5 shadow-lg">
|
|
296
|
+
{/* Mock heading */}
|
|
297
|
+
<div className="h-3 w-3/4 bg-foreground/80 rounded mb-4" />
|
|
298
|
+
|
|
299
|
+
{/* Mock paragraph */}
|
|
300
|
+
<div className="space-y-2 mb-4">
|
|
301
|
+
<div className="h-2 w-full bg-muted-foreground/30 rounded" />
|
|
302
|
+
<div className="h-2 w-5/6 bg-muted-foreground/30 rounded" />
|
|
303
|
+
<div className="flex items-center gap-1">
|
|
304
|
+
<div className="h-2 w-1/4 bg-muted-foreground/30 rounded" />
|
|
305
|
+
<div className="h-2 w-1/3 bg-destructive/40 rounded line-through" />
|
|
306
|
+
<div className="h-2 w-1/6 bg-muted-foreground/30 rounded" />
|
|
307
|
+
</div>
|
|
308
|
+
</div>
|
|
309
|
+
|
|
310
|
+
{/* Mock subheading */}
|
|
311
|
+
<div className="h-2.5 w-1/2 bg-foreground/60 rounded mb-3 mt-5" />
|
|
312
|
+
|
|
313
|
+
{/* Mock list */}
|
|
314
|
+
<div className="space-y-2 mb-4">
|
|
315
|
+
<div className="flex items-center gap-2">
|
|
316
|
+
<div className="w-1 h-1 rounded-full bg-primary/60" />
|
|
317
|
+
<div className="h-2 w-4/5 bg-muted-foreground/30 rounded" />
|
|
318
|
+
</div>
|
|
319
|
+
<div className="flex items-center gap-2">
|
|
320
|
+
<div className="w-1 h-1 rounded-full bg-primary/60" />
|
|
321
|
+
<div className="h-2 w-2/3 bg-accent/40 rounded border-b border-accent" />
|
|
322
|
+
</div>
|
|
323
|
+
<div className="flex items-center gap-2">
|
|
324
|
+
<div className="w-1 h-1 rounded-full bg-primary/60" />
|
|
325
|
+
<div className="h-2 w-3/4 bg-muted-foreground/30 rounded" />
|
|
326
|
+
</div>
|
|
327
|
+
</div>
|
|
328
|
+
|
|
329
|
+
{/* Mock code block */}
|
|
330
|
+
<div className="bg-muted/50 rounded p-2 space-y-1.5">
|
|
331
|
+
<div className="h-1.5 w-1/2 bg-primary/40 rounded" />
|
|
332
|
+
<div className="h-1.5 w-2/3 bg-muted-foreground/30 rounded" />
|
|
333
|
+
<div className="h-1.5 w-1/3 bg-secondary/40 rounded" />
|
|
334
|
+
</div>
|
|
335
|
+
</div>
|
|
336
|
+
</div>
|
|
337
|
+
|
|
338
|
+
{/* Mock Sidebar */}
|
|
339
|
+
<div className="w-48 border-l border-border/30 bg-card/30 p-3">
|
|
340
|
+
<div className="text-[10px] uppercase tracking-wider text-muted-foreground mb-3 flex items-center justify-between">
|
|
341
|
+
<span>Annotations</span>
|
|
342
|
+
<span className="bg-muted px-1.5 py-0.5 rounded text-[9px]">
|
|
343
|
+
2
|
|
344
|
+
</span>
|
|
345
|
+
</div>
|
|
346
|
+
|
|
347
|
+
{/* Mock annotation cards */}
|
|
348
|
+
<div className="space-y-2">
|
|
349
|
+
<div className="p-2 rounded-md border border-destructive/30 bg-destructive/5">
|
|
350
|
+
<div className="flex items-center gap-1 mb-1.5">
|
|
351
|
+
<div className="w-3 h-3 rounded bg-destructive/20" />
|
|
352
|
+
<span className="text-[9px] font-semibold text-destructive uppercase">
|
|
353
|
+
Delete
|
|
354
|
+
</span>
|
|
355
|
+
</div>
|
|
356
|
+
<div className="h-1.5 w-full bg-muted rounded" />
|
|
357
|
+
</div>
|
|
358
|
+
|
|
359
|
+
<div className="p-2 rounded-md border border-accent/30 bg-accent/5">
|
|
360
|
+
<div className="flex items-center gap-1 mb-1.5">
|
|
361
|
+
<div className="w-3 h-3 rounded bg-accent/20" />
|
|
362
|
+
<span className="text-[9px] font-semibold text-accent uppercase">
|
|
363
|
+
Comment
|
|
364
|
+
</span>
|
|
365
|
+
</div>
|
|
366
|
+
<div className="h-1.5 w-3/4 bg-muted rounded mb-1" />
|
|
367
|
+
<div className="h-1.5 w-full bg-accent/20 rounded" />
|
|
368
|
+
</div>
|
|
369
|
+
</div>
|
|
370
|
+
</div>
|
|
371
|
+
</div>
|
|
372
|
+
</div>
|
|
373
|
+
|
|
374
|
+
<p className="text-center text-sm text-muted-foreground mt-6">
|
|
375
|
+
Select text → annotate → export feedback
|
|
376
|
+
</p>
|
|
377
|
+
</section>
|
|
378
|
+
|
|
379
|
+
{/* How it works */}
|
|
380
|
+
<section className="py-16 px-8 border-t border-border/30 bg-card/30">
|
|
381
|
+
<h2 className="text-xl font-semibold mb-8">How it works</h2>
|
|
382
|
+
|
|
383
|
+
<div className="space-y-6 max-w-2xl">
|
|
384
|
+
<Step num={1} title="Agent triggers Plannotator">
|
|
385
|
+
<span className="text-xs">
|
|
386
|
+
<strong>Claude Code:</strong> ExitPlanMode hook opens UI<br />
|
|
387
|
+
<strong>OpenCode:</strong> Agent calls submit_plan tool
|
|
388
|
+
</span>
|
|
389
|
+
</Step>
|
|
390
|
+
|
|
391
|
+
<Step num={2} title="Annotate visually">
|
|
392
|
+
Select text → choose action (delete, comment, replace) → annotations appear
|
|
393
|
+
in the sidebar
|
|
394
|
+
</Step>
|
|
395
|
+
|
|
396
|
+
<Step num={3} title="Approve or request changes">
|
|
397
|
+
Click approve to proceed, or provide feedback with annotations.
|
|
398
|
+
Feedback flows back to your agent automatically.
|
|
399
|
+
</Step>
|
|
400
|
+
</div>
|
|
401
|
+
</section>
|
|
402
|
+
|
|
403
|
+
{/* Technical */}
|
|
404
|
+
<section className="py-16 px-8 border-t border-border/30 bg-card/30">
|
|
405
|
+
<h2 className="text-xl font-semibold mb-6">Technical details</h2>
|
|
406
|
+
<ul className="space-y-2 text-sm text-muted-foreground max-w-2xl mb-6">
|
|
407
|
+
<li className="flex items-center gap-2">
|
|
408
|
+
<span className="text-primary">•</span>
|
|
409
|
+
Single HTML file build — runs from Bun server on random port
|
|
410
|
+
</li>
|
|
411
|
+
<li className="flex items-center gap-2">
|
|
412
|
+
<span className="text-primary">•</span>
|
|
413
|
+
<strong>Claude Code:</strong> Binary + plugin with PermissionRequest hook
|
|
414
|
+
</li>
|
|
415
|
+
<li className="flex items-center gap-2">
|
|
416
|
+
<span className="text-primary">•</span>
|
|
417
|
+
<strong>OpenCode:</strong> npm package with submit_plan tool
|
|
418
|
+
</li>
|
|
419
|
+
<li className="flex items-center gap-2">
|
|
420
|
+
<span className="text-primary">•</span>
|
|
421
|
+
URL sharing via deflate compression — no backend required
|
|
422
|
+
</li>
|
|
423
|
+
</ul>
|
|
424
|
+
|
|
425
|
+
<a
|
|
426
|
+
href="https://github.com/backnotprop/plannotator"
|
|
427
|
+
target="_blank"
|
|
428
|
+
rel="noopener noreferrer"
|
|
429
|
+
className="inline-flex items-center gap-2 text-sm text-muted-foreground hover:text-foreground transition-colors"
|
|
430
|
+
>
|
|
431
|
+
<span className="inline-flex items-center rounded overflow-hidden">
|
|
432
|
+
<span className="bg-[#121011] text-white px-2 py-1 flex items-center gap-1.5">
|
|
433
|
+
<svg
|
|
434
|
+
className="w-3.5 h-3.5"
|
|
435
|
+
viewBox="0 0 24 24"
|
|
436
|
+
fill="currentColor"
|
|
437
|
+
>
|
|
438
|
+
<path d="M12 0C5.37 0 0 5.37 0 12c0 5.31 3.435 9.795 8.205 11.385.6.105.825-.255.825-.57 0-.285-.015-1.23-.015-2.235-3.015.555-3.795-.735-4.035-1.41-.135-.345-.72-1.41-1.23-1.695-.42-.225-1.02-.78-.015-.795.945-.015 1.62.87 1.845 1.23 1.08 1.815 2.805 1.305 3.495.99.105-.78.42-1.305.765-1.605-2.67-.3-5.46-1.335-5.46-5.925 0-1.305.465-2.385 1.23-3.225-.12-.3-.54-1.53.12-3.18 0 0 1.005-.315 3.3 1.23.96-.27 1.98-.405 3-.405s2.04.135 3 .405c2.295-1.56 3.3-1.23 3.3-1.23.66 1.65.24 2.88.12 3.18.765.84 1.23 1.905 1.23 3.225 0 4.605-2.805 5.625-5.475 5.925.435.375.81 1.095.81 2.22 0 1.605-.015 2.895-.015 3.3 0 .315.225.69.825.57A12.02 12.02 0 0024 12c0-6.63-5.37-12-12-12z" />
|
|
439
|
+
</svg>
|
|
440
|
+
<span className="text-xs font-medium">GitHub</span>
|
|
441
|
+
</span>
|
|
442
|
+
</span>
|
|
443
|
+
<span className="underline underline-offset-2">
|
|
444
|
+
View GitHub repository
|
|
445
|
+
</span>
|
|
446
|
+
</a>
|
|
447
|
+
</section>
|
|
448
|
+
|
|
449
|
+
{/* Footer */}
|
|
450
|
+
<footer className="py-8 px-8 border-t border-border/30 text-center">
|
|
451
|
+
<p className="text-xs text-muted-foreground inline-flex items-center justify-center gap-1">
|
|
452
|
+
<a
|
|
453
|
+
href="https://github.com/backnotprop"
|
|
454
|
+
target="_blank"
|
|
455
|
+
rel="noopener noreferrer"
|
|
456
|
+
className="inline-flex items-center gap-1 text-foreground hover:text-primary transition-colors"
|
|
457
|
+
>
|
|
458
|
+
<svg className="w-3 h-3" viewBox="0 0 24 24" fill="currentColor">
|
|
459
|
+
<path d="M12 0C5.37 0 0 5.37 0 12c0 5.31 3.435 9.795 8.205 11.385.6.105.825-.255.825-.57 0-.285-.015-1.23-.015-2.235-3.015.555-3.795-.735-4.035-1.41-.135-.345-.72-1.41-1.23-1.695-.42-.225-1.02-.78-.015-.795.945-.015 1.62.87 1.845 1.23 1.08 1.815 2.805 1.305 3.495.99.105-.78.42-1.305.765-1.605-2.67-.3-5.46-1.335-5.46-5.925 0-1.305.465-2.385 1.23-3.225-.12-.3-.54-1.53.12-3.18 0 0 1.005-.315 3.3 1.23.96-.27 1.98-.405 3-.405s2.04.135 3 .405c2.295-1.56 3.3-1.23 3.3-1.23.66 1.65.24 2.88.12 3.18.765.84 1.23 1.905 1.23 3.225 0 4.605-2.805 5.625-5.475 5.925.435.375.81 1.095.81 2.22 0 1.605-.015 2.895-.015 3.3 0 .315.225.69.825.57A12.02 12.02 0 0024 12c0-6.63-5.37-12-12-12z" />
|
|
460
|
+
</svg>
|
|
461
|
+
backnotprop
|
|
462
|
+
</a>{" "}
|
|
463
|
+
·{" "}
|
|
464
|
+
<a
|
|
465
|
+
href="https://x.com/backnotprop"
|
|
466
|
+
target="_blank"
|
|
467
|
+
rel="noopener noreferrer"
|
|
468
|
+
className="text-foreground hover:text-primary transition-colors"
|
|
469
|
+
>
|
|
470
|
+
@backnotprop
|
|
471
|
+
</a>
|
|
472
|
+
</p>
|
|
473
|
+
<p className="text-xs text-muted-foreground/60 mt-2">
|
|
474
|
+
© 2025 backnotprop. All rights reserved.
|
|
475
|
+
</p>
|
|
476
|
+
</footer>
|
|
477
|
+
</div>
|
|
478
|
+
</div>
|
|
479
|
+
);
|
|
480
|
+
};
|
|
481
|
+
|
|
482
|
+
const Step: React.FC<{
|
|
483
|
+
num: number;
|
|
484
|
+
title: string;
|
|
485
|
+
children: React.ReactNode;
|
|
486
|
+
}> = ({ num, title, children }) => (
|
|
487
|
+
<div className="flex gap-4">
|
|
488
|
+
<div className="w-7 h-7 rounded-full bg-primary/10 text-primary text-sm font-semibold flex items-center justify-center shrink-0">
|
|
489
|
+
{num}
|
|
490
|
+
</div>
|
|
491
|
+
<div>
|
|
492
|
+
<h4 className="font-medium mb-1">{title}</h4>
|
|
493
|
+
<p className="text-sm text-muted-foreground">{children}</p>
|
|
494
|
+
</div>
|
|
495
|
+
</div>
|
|
496
|
+
);
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { ListMarker } from './ListMarker';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Shared list-item body: the marker plus the item's text content. Used by the
|
|
6
|
+
* main Viewer (BlockRenderer) and the plan-diff clean view (both the inline
|
|
7
|
+
* modified-block render and SimpleBlockRenderer).
|
|
8
|
+
*
|
|
9
|
+
* Callers own the surrounding row element so each surface keeps its own
|
|
10
|
+
* concerns (indent, data attributes, hover-prop spread, diff wrapper classes).
|
|
11
|
+
* This component owns the parts that must stay identical: marker selection and
|
|
12
|
+
* the single-paragraph `<span>` vs multi-paragraph `<div><p>...</p></div>`
|
|
13
|
+
* split, so a fix to either never has to be repeated per surface.
|
|
14
|
+
*/
|
|
15
|
+
interface ListItemBodyProps {
|
|
16
|
+
level: number;
|
|
17
|
+
ordered?: boolean;
|
|
18
|
+
orderedIndex?: number | null;
|
|
19
|
+
checked?: boolean; // undefined = not a checkbox
|
|
20
|
+
interactive?: boolean;
|
|
21
|
+
onToggle?: () => void;
|
|
22
|
+
textClassName: string;
|
|
23
|
+
content: string;
|
|
24
|
+
renderInline: (text: string) => React.ReactNode;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const ListItemBody: React.FC<ListItemBodyProps> = ({
|
|
28
|
+
level,
|
|
29
|
+
ordered,
|
|
30
|
+
orderedIndex,
|
|
31
|
+
checked,
|
|
32
|
+
interactive,
|
|
33
|
+
onToggle,
|
|
34
|
+
textClassName,
|
|
35
|
+
content,
|
|
36
|
+
renderInline,
|
|
37
|
+
}) => {
|
|
38
|
+
const paragraphs = content.split(/\n\n+/);
|
|
39
|
+
return (
|
|
40
|
+
<>
|
|
41
|
+
<ListMarker
|
|
42
|
+
level={level}
|
|
43
|
+
ordered={ordered}
|
|
44
|
+
orderedIndex={orderedIndex}
|
|
45
|
+
checked={checked}
|
|
46
|
+
interactive={interactive}
|
|
47
|
+
onToggle={onToggle}
|
|
48
|
+
/>
|
|
49
|
+
{paragraphs.length === 1 ? (
|
|
50
|
+
<span className={textClassName}>{renderInline(content)}</span>
|
|
51
|
+
) : (
|
|
52
|
+
<div className={textClassName}>
|
|
53
|
+
{paragraphs.map((para, i) => (
|
|
54
|
+
<p key={i} className={i > 0 ? 'mt-3' : ''}>
|
|
55
|
+
{renderInline(para)}
|
|
56
|
+
</p>
|
|
57
|
+
))}
|
|
58
|
+
</div>
|
|
59
|
+
)}
|
|
60
|
+
</>
|
|
61
|
+
);
|
|
62
|
+
};
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Shared list-item marker used by both the main Viewer and the plan-diff
|
|
5
|
+
* clean view. Renders the appropriate glyph(s) for a list item given its
|
|
6
|
+
* level, ordered flag, display index, and checkbox state.
|
|
7
|
+
*
|
|
8
|
+
* Rendering rules:
|
|
9
|
+
* - Ordered items render `${orderedIndex}.` with tabular-nums so digit
|
|
10
|
+
* widths stay stable across e.g. `9.` → `10.`.
|
|
11
|
+
* - Checkbox items render the circle / checkmark SVG.
|
|
12
|
+
* - Ordered + checkbox renders BOTH: numeral first, checkbox second
|
|
13
|
+
* (matches GitHub's `1. [ ] task` rendering).
|
|
14
|
+
* - Plain bullets fall back to `•` / `◦` / `▪` by level.
|
|
15
|
+
*
|
|
16
|
+
* Interactivity is opt-in: the Viewer passes `interactive` + `onToggle`
|
|
17
|
+
* for click-to-toggle checkboxes; the diff view leaves both undefined.
|
|
18
|
+
*/
|
|
19
|
+
interface ListMarkerProps {
|
|
20
|
+
level: number;
|
|
21
|
+
ordered?: boolean;
|
|
22
|
+
orderedIndex?: number | null;
|
|
23
|
+
checked?: boolean; // undefined = not a checkbox
|
|
24
|
+
interactive?: boolean;
|
|
25
|
+
onToggle?: () => void;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const BULLET_BY_LEVEL = ['\u2022', '\u2022', '\u2022'];
|
|
29
|
+
|
|
30
|
+
export const ListMarker: React.FC<ListMarkerProps> = ({
|
|
31
|
+
level,
|
|
32
|
+
ordered,
|
|
33
|
+
orderedIndex,
|
|
34
|
+
checked,
|
|
35
|
+
interactive,
|
|
36
|
+
onToggle,
|
|
37
|
+
}) => {
|
|
38
|
+
const isCheckbox = checked !== undefined;
|
|
39
|
+
const showNumeral = !!ordered && orderedIndex != null;
|
|
40
|
+
const bullet = BULLET_BY_LEVEL[Math.min(level, BULLET_BY_LEVEL.length - 1)];
|
|
41
|
+
|
|
42
|
+
const handleClick = interactive && onToggle
|
|
43
|
+
? (e: React.MouseEvent) => { e.stopPropagation(); onToggle(); }
|
|
44
|
+
: undefined;
|
|
45
|
+
|
|
46
|
+
return (
|
|
47
|
+
<span
|
|
48
|
+
className={`select-none shrink-0 self-start flex items-center gap-1${interactive ? ' cursor-pointer' : ''}`}
|
|
49
|
+
onClick={handleClick}
|
|
50
|
+
role={interactive ? 'checkbox' : undefined}
|
|
51
|
+
aria-checked={interactive ? checked : undefined}
|
|
52
|
+
>
|
|
53
|
+
{showNumeral && (
|
|
54
|
+
<span className="text-primary/60 tabular-nums text-right" style={{ minWidth: '1.5rem' }}>
|
|
55
|
+
{orderedIndex}.
|
|
56
|
+
</span>
|
|
57
|
+
)}
|
|
58
|
+
{isCheckbox ? (
|
|
59
|
+
checked ? (
|
|
60
|
+
<svg className="w-4 h-4 text-success mt-[3px]" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2.5}>
|
|
61
|
+
<path strokeLinecap="round" strokeLinejoin="round" d="M9 12.75L11.25 15 15 9.75M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
|
62
|
+
</svg>
|
|
63
|
+
) : (
|
|
64
|
+
<svg className={`w-4 h-4 text-muted-foreground/50 mt-[3px]${interactive ? ' hover:text-muted-foreground transition-colors' : ''}`} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2}>
|
|
65
|
+
<circle cx="12" cy="12" r="9" />
|
|
66
|
+
</svg>
|
|
67
|
+
)
|
|
68
|
+
) : !showNumeral ? (
|
|
69
|
+
<span className="text-primary/60">{bullet}</span>
|
|
70
|
+
) : null}
|
|
71
|
+
</span>
|
|
72
|
+
);
|
|
73
|
+
};
|