@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,153 @@
|
|
|
1
|
+
import React, { useState, useEffect, useRef } from 'react';
|
|
2
|
+
import { createPortal } from 'react-dom';
|
|
3
|
+
import { buildCsv } from './TableBlock';
|
|
4
|
+
|
|
5
|
+
interface TableToolbarProps {
|
|
6
|
+
element: HTMLElement;
|
|
7
|
+
markdown: string;
|
|
8
|
+
isExiting?: boolean;
|
|
9
|
+
onMouseEnter?: () => void;
|
|
10
|
+
onMouseLeave?: () => void;
|
|
11
|
+
onExpand?: () => void;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Floats above a table on hover. Mirrors AnnotationToolbar's positioning +
|
|
15
|
+
// portal + entry/exit animation pattern so hover-triggered block toolbars
|
|
16
|
+
// all look and move the same way. Content is table-specific (copy markdown
|
|
17
|
+
// for now; CSV text button and expand icon planned). When a third use case
|
|
18
|
+
// appears we should factor the positioning shell into a shared hook.
|
|
19
|
+
export const TableToolbar: React.FC<TableToolbarProps> = ({
|
|
20
|
+
element,
|
|
21
|
+
markdown,
|
|
22
|
+
isExiting = false,
|
|
23
|
+
onMouseEnter,
|
|
24
|
+
onMouseLeave,
|
|
25
|
+
onExpand,
|
|
26
|
+
}) => {
|
|
27
|
+
const [position, setPosition] = useState<{ top: number; right: number } | null>(null);
|
|
28
|
+
const [copiedMd, setCopiedMd] = useState(false);
|
|
29
|
+
const [copiedCsv, setCopiedCsv] = useState(false);
|
|
30
|
+
const toolbarRef = useRef<HTMLDivElement>(null);
|
|
31
|
+
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
const updatePosition = () => {
|
|
34
|
+
const rect = element.getBoundingClientRect();
|
|
35
|
+
setPosition({
|
|
36
|
+
top: rect.top - 40,
|
|
37
|
+
right: window.innerWidth - rect.right,
|
|
38
|
+
});
|
|
39
|
+
};
|
|
40
|
+
updatePosition();
|
|
41
|
+
window.addEventListener('scroll', updatePosition, true);
|
|
42
|
+
window.addEventListener('resize', updatePosition);
|
|
43
|
+
return () => {
|
|
44
|
+
window.removeEventListener('scroll', updatePosition, true);
|
|
45
|
+
window.removeEventListener('resize', updatePosition);
|
|
46
|
+
};
|
|
47
|
+
}, [element]);
|
|
48
|
+
|
|
49
|
+
useEffect(() => {
|
|
50
|
+
setCopiedMd(false);
|
|
51
|
+
setCopiedCsv(false);
|
|
52
|
+
}, [element]);
|
|
53
|
+
|
|
54
|
+
const handleCopyMarkdown = async () => {
|
|
55
|
+
try {
|
|
56
|
+
await navigator.clipboard.writeText(markdown);
|
|
57
|
+
setCopiedMd(true);
|
|
58
|
+
setTimeout(() => setCopiedMd(false), 1500);
|
|
59
|
+
} catch (err) {
|
|
60
|
+
console.error('Failed to copy:', err);
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const handleCopyCsv = async () => {
|
|
65
|
+
try {
|
|
66
|
+
await navigator.clipboard.writeText(buildCsv(markdown));
|
|
67
|
+
setCopiedCsv(true);
|
|
68
|
+
setTimeout(() => setCopiedCsv(false), 1500);
|
|
69
|
+
} catch (err) {
|
|
70
|
+
console.error('Failed to copy:', err);
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
if (!position) return null;
|
|
75
|
+
|
|
76
|
+
const style: React.CSSProperties = {
|
|
77
|
+
top: position.top,
|
|
78
|
+
right: position.right,
|
|
79
|
+
animation: isExiting
|
|
80
|
+
? 'table-toolbar-out 0.15s ease-in forwards'
|
|
81
|
+
: 'table-toolbar-in 0.15s ease-out',
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
return createPortal(
|
|
85
|
+
<div
|
|
86
|
+
ref={toolbarRef}
|
|
87
|
+
className="fixed z-[100] bg-popover border border-border rounded-md shadow-md"
|
|
88
|
+
style={style}
|
|
89
|
+
onMouseDown={(e) => e.stopPropagation()}
|
|
90
|
+
onMouseEnter={onMouseEnter}
|
|
91
|
+
onMouseLeave={onMouseLeave}
|
|
92
|
+
>
|
|
93
|
+
<style>{`
|
|
94
|
+
@keyframes table-toolbar-in {
|
|
95
|
+
from { opacity: 0; transform: translateY(12px); }
|
|
96
|
+
to { opacity: 1; transform: translateY(0); }
|
|
97
|
+
}
|
|
98
|
+
@keyframes table-toolbar-out {
|
|
99
|
+
from { opacity: 1; transform: translateY(0); }
|
|
100
|
+
to { opacity: 0; transform: translateY(8px); }
|
|
101
|
+
}
|
|
102
|
+
`}</style>
|
|
103
|
+
<div className="flex items-center p-0.5 gap-0.5">
|
|
104
|
+
<button
|
|
105
|
+
onClick={handleCopyMarkdown}
|
|
106
|
+
title={copiedMd ? 'Copied!' : 'Copy as markdown'}
|
|
107
|
+
className={`p-1 rounded transition-colors ${
|
|
108
|
+
copiedMd ? 'text-success' : 'text-muted-foreground hover:bg-muted hover:text-foreground'
|
|
109
|
+
}`}
|
|
110
|
+
>
|
|
111
|
+
{copiedMd ? <CheckIcon /> : <CopyIcon />}
|
|
112
|
+
</button>
|
|
113
|
+
<button
|
|
114
|
+
onClick={handleCopyCsv}
|
|
115
|
+
title={copiedCsv ? 'Copied as CSV!' : 'Copy as CSV'}
|
|
116
|
+
className={`px-1.5 py-1 rounded text-[10px] font-bold tracking-tight uppercase leading-none transition-colors ${
|
|
117
|
+
copiedCsv ? 'text-success' : 'text-primary hover:bg-primary/10'
|
|
118
|
+
}`}
|
|
119
|
+
>
|
|
120
|
+
{copiedCsv ? '✓' : 'CSV'}
|
|
121
|
+
</button>
|
|
122
|
+
{onExpand && (
|
|
123
|
+
<button
|
|
124
|
+
onClick={onExpand}
|
|
125
|
+
title="Expand table"
|
|
126
|
+
className="p-1 rounded text-muted-foreground hover:bg-muted hover:text-foreground transition-colors"
|
|
127
|
+
>
|
|
128
|
+
<ExpandIcon />
|
|
129
|
+
</button>
|
|
130
|
+
)}
|
|
131
|
+
</div>
|
|
132
|
+
</div>,
|
|
133
|
+
document.body,
|
|
134
|
+
);
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
const CopyIcon = () => (
|
|
138
|
+
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
|
|
139
|
+
<path strokeLinecap="round" strokeLinejoin="round" d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z" />
|
|
140
|
+
</svg>
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
const CheckIcon = () => (
|
|
144
|
+
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2.5}>
|
|
145
|
+
<path strokeLinecap="round" strokeLinejoin="round" d="M5 13l4 4L19 7" />
|
|
146
|
+
</svg>
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
const ExpandIcon = () => (
|
|
150
|
+
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
|
|
151
|
+
<path strokeLinecap="round" strokeLinejoin="round" d="M4 8V4h4M20 8V4h-4M4 16v4h4M20 16v4h-4" />
|
|
152
|
+
</svg>
|
|
153
|
+
);
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { InlineMarkdown } from '../InlineMarkdown';
|
|
3
|
+
|
|
4
|
+
// Shared block-level renderer for alert and directive bodies. Handles
|
|
5
|
+
// paragraphs, unordered lists, and ordered lists — the shapes GitHub alerts
|
|
6
|
+
// and Docusaurus / VitePress directives commonly carry. More exotic content
|
|
7
|
+
// (headings, tables, code fences, nested alerts) falls back to inline text;
|
|
8
|
+
// add cases here when a real use case turns up.
|
|
9
|
+
//
|
|
10
|
+
// Paragraph lines are joined with '\n' (not ' ') so InlineMarkdown's hard
|
|
11
|
+
// line-break handling still fires for ` \n` inside a callout body.
|
|
12
|
+
export function renderProseBody(args: {
|
|
13
|
+
body: string;
|
|
14
|
+
paragraphClassName?: string;
|
|
15
|
+
listClassName?: string;
|
|
16
|
+
imageBaseDir?: string;
|
|
17
|
+
onImageClick?: (src: string, alt: string) => void;
|
|
18
|
+
onOpenLinkedDoc?: (path: string) => void;
|
|
19
|
+
onOpenCodeFile?: (path: string) => void;
|
|
20
|
+
onNavigateAnchor?: (hash: string) => void;
|
|
21
|
+
githubRepo?: string;
|
|
22
|
+
}): React.ReactNode {
|
|
23
|
+
const {
|
|
24
|
+
body,
|
|
25
|
+
paragraphClassName = 'text-[15px] leading-relaxed text-foreground/90',
|
|
26
|
+
listClassName = 'text-[15px] leading-relaxed text-foreground/90',
|
|
27
|
+
imageBaseDir,
|
|
28
|
+
onImageClick,
|
|
29
|
+
onOpenLinkedDoc,
|
|
30
|
+
onOpenCodeFile,
|
|
31
|
+
onNavigateAnchor,
|
|
32
|
+
githubRepo,
|
|
33
|
+
} = args;
|
|
34
|
+
|
|
35
|
+
const inline = (text: string) => (
|
|
36
|
+
<InlineMarkdown
|
|
37
|
+
imageBaseDir={imageBaseDir}
|
|
38
|
+
onImageClick={onImageClick}
|
|
39
|
+
text={text}
|
|
40
|
+
onOpenLinkedDoc={onOpenLinkedDoc}
|
|
41
|
+
onOpenCodeFile={onOpenCodeFile}
|
|
42
|
+
onNavigateAnchor={onNavigateAnchor}
|
|
43
|
+
githubRepo={githubRepo}
|
|
44
|
+
/>
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
const lines = body.split('\n');
|
|
48
|
+
const out: React.ReactNode[] = [];
|
|
49
|
+
let paraLines: string[] = [];
|
|
50
|
+
let list: { ordered: boolean; items: string[] } | null = null;
|
|
51
|
+
let key = 0;
|
|
52
|
+
|
|
53
|
+
const flushPara = () => {
|
|
54
|
+
if (paraLines.length === 0) return;
|
|
55
|
+
const text = paraLines.join('\n');
|
|
56
|
+
if (text.trim()) {
|
|
57
|
+
out.push(
|
|
58
|
+
<p key={`p-${key++}`} className={`${paragraphClassName} ${out.length > 0 ? 'mt-2' : ''}`}>
|
|
59
|
+
{inline(text)}
|
|
60
|
+
</p>,
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
paraLines = [];
|
|
64
|
+
};
|
|
65
|
+
const flushList = () => {
|
|
66
|
+
if (!list) return;
|
|
67
|
+
const Tag = list.ordered ? 'ol' : 'ul';
|
|
68
|
+
const className = `${list.ordered ? 'list-decimal' : 'list-disc'} pl-5 ${listClassName} ${out.length > 0 ? 'mt-2' : ''}`;
|
|
69
|
+
out.push(
|
|
70
|
+
<Tag key={`l-${key++}`} className={className}>
|
|
71
|
+
{list.items.map((item, i) => (
|
|
72
|
+
<li key={i} className="my-0.5">{inline(item)}</li>
|
|
73
|
+
))}
|
|
74
|
+
</Tag>,
|
|
75
|
+
);
|
|
76
|
+
list = null;
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
for (const line of lines) {
|
|
80
|
+
if (line.trim() === '') {
|
|
81
|
+
flushPara();
|
|
82
|
+
flushList();
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
const listMatch = line.match(/^\s*(\*|-|\d+\.)\s+(.*)$/);
|
|
86
|
+
if (listMatch) {
|
|
87
|
+
flushPara();
|
|
88
|
+
const ordered = /\d/.test(listMatch[1]);
|
|
89
|
+
if (!list || list.ordered !== ordered) {
|
|
90
|
+
flushList();
|
|
91
|
+
list = { ordered, items: [] };
|
|
92
|
+
}
|
|
93
|
+
list.items.push(listMatch[2]);
|
|
94
|
+
} else {
|
|
95
|
+
flushList();
|
|
96
|
+
paraLines.push(line);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
flushPara();
|
|
100
|
+
flushList();
|
|
101
|
+
return out;
|
|
102
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
type ButtonVariant = 'primary' | 'outline' | 'ghost' | 'icon' | 'danger';
|
|
4
|
+
type ButtonSize = 'sm' | 'md' | 'icon';
|
|
5
|
+
|
|
6
|
+
export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
7
|
+
variant?: ButtonVariant;
|
|
8
|
+
size?: ButtonSize;
|
|
9
|
+
active?: boolean;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function cx(...classes: Array<string | false | null | undefined>): string {
|
|
13
|
+
return classes.filter(Boolean).join(' ');
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(({
|
|
17
|
+
className,
|
|
18
|
+
variant = 'outline',
|
|
19
|
+
size = 'md',
|
|
20
|
+
active = false,
|
|
21
|
+
type = 'button',
|
|
22
|
+
...props
|
|
23
|
+
}, ref) => (
|
|
24
|
+
<button
|
|
25
|
+
ref={ref}
|
|
26
|
+
type={type}
|
|
27
|
+
className={cx(
|
|
28
|
+
'inline-flex items-center justify-center gap-2 rounded-md text-xs font-medium transition-colors outline-none focus-visible:ring-2 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50',
|
|
29
|
+
size === 'sm' && 'h-8 px-2.5',
|
|
30
|
+
size === 'md' && 'h-9 px-3',
|
|
31
|
+
size === 'icon' && 'h-8 w-8',
|
|
32
|
+
variant === 'primary' && 'bg-primary text-primary-foreground hover:opacity-90',
|
|
33
|
+
variant === 'outline' && 'border border-border bg-card text-foreground hover:bg-muted',
|
|
34
|
+
variant === 'ghost' && 'text-muted-foreground hover:bg-muted hover:text-foreground',
|
|
35
|
+
variant === 'icon' && (active
|
|
36
|
+
? 'border border-primary/50 bg-primary/10 text-primary'
|
|
37
|
+
: 'border border-border bg-background text-muted-foreground hover:bg-muted hover:text-foreground'),
|
|
38
|
+
variant === 'danger' && 'border border-border bg-background text-muted-foreground hover:border-destructive/50 hover:bg-destructive/10 hover:text-destructive',
|
|
39
|
+
className
|
|
40
|
+
)}
|
|
41
|
+
{...props}
|
|
42
|
+
/>
|
|
43
|
+
));
|
|
44
|
+
Button.displayName = 'Button';
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
export type TextareaProps = React.TextareaHTMLAttributes<HTMLTextAreaElement>;
|
|
4
|
+
|
|
5
|
+
const fieldSizing = { fieldSizing: 'content' } as React.CSSProperties;
|
|
6
|
+
|
|
7
|
+
function cx(...classes: Array<string | false | null | undefined>): string {
|
|
8
|
+
return classes.filter(Boolean).join(' ');
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(({
|
|
12
|
+
className,
|
|
13
|
+
style,
|
|
14
|
+
...props
|
|
15
|
+
}, ref) => (
|
|
16
|
+
<textarea
|
|
17
|
+
ref={ref}
|
|
18
|
+
className={cx(
|
|
19
|
+
'min-h-24 w-full resize-none rounded-md border border-border bg-background px-3 py-2 text-sm leading-6 text-foreground outline-none transition-colors placeholder:text-muted-foreground/70 focus:border-primary/60 focus:ring-2 focus:ring-primary/15',
|
|
20
|
+
className
|
|
21
|
+
)}
|
|
22
|
+
style={{ ...fieldSizing, ...style }}
|
|
23
|
+
{...props}
|
|
24
|
+
/>
|
|
25
|
+
));
|
|
26
|
+
Textarea.displayName = 'Textarea';
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
const GRAPHVIZ_LANGUAGES = new Set(['dot', 'graphviz', 'gv']);
|
|
2
|
+
|
|
3
|
+
function getFenceLanguage(language?: string): string | undefined {
|
|
4
|
+
return language?.trim().split(/\s+/, 1)[0]?.toLowerCase();
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function isMermaidLanguage(language?: string): boolean {
|
|
8
|
+
return getFenceLanguage(language) === 'mermaid';
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function isGraphvizLanguage(language?: string): boolean {
|
|
12
|
+
const normalizedLanguage = getFenceLanguage(language);
|
|
13
|
+
return normalizedLanguage ? GRAPHVIZ_LANGUAGES.has(normalizedLanguage) : false;
|
|
14
|
+
}
|