mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-01-30 06:12:03 +00:00
fix(ui): improve review dialog rendering for tool calls and tables (#657)
* fix(ui): improve review dialog rendering for tool calls and tables - Replace Markdown component with LogViewer in plan-approval-dialog to properly format tool calls with collapsible sections and JSON highlighting - Add remark-gfm plugin to Markdown component for GitHub Flavored Markdown support including tables, task lists, and strikethrough - Add table styling classes to Markdown component for proper table rendering - Install remark-gfm and rehype-sanitize dependencies Fixes mixed/broken rendering in review dialog where tool calls showed as raw text and markdown tables showed as pipe-separated text. * chore: fix git+ssh URL and prettier formatting - Convert git+ssh:// to git+https:// in package-lock.json for @electron/node-gyp - Apply prettier formatting to plan-approval-dialog.tsx * fix(ui): create PlanContentViewer for better plan display The previous LogViewer approach showed tool calls prominently but hid the actual plan/specification markdown content. The new PlanContentViewer: - Separates tool calls (exploration) from plan markdown - Shows the plan/specification markdown prominently using Markdown component - Collapses tool calls by default in an "Exploration" section - Properly renders GFM tables in the plan content This provides a better UX where users see the important plan content first, with tool calls available but not distracting. * fix(ui): add show more/less toggle for feature description The feature description in the plan approval dialog header was truncated at 150 characters with no way to see the full text. Now users can click "show more" to expand and "show less" to collapse. * fix(ui): increase description limit and add feature title to dialog - Increase description character limit from 150 to 250 characters - Add feature title to dialog header (e.g., "Review Plan - Feature Title") only if title exists and is <= 50 characters * feat(ui): render tasks code blocks as proper checkbox lists When markdown contains a ```tasks code block, it now renders as: - Phase headers (## Phase 1: ...) as styled section headings - Task items (- [ ] or - [x]) with proper checkbox icons - Checked items show green checkmark and strikethrough text - Unchecked items show empty square icon This makes implementation task lists in plans much more readable compared to rendering them as raw code blocks. * fix(ui): improve plan content parsing robustness Address CodeRabbit review feedback: 1. Relax heading detection regex to match emoji and non-word chars - Change \w to \S so headings like "## ✅ Plan" are detected - Change \*\*[A-Z] to \*\*\S for bold section detection 2. Flush active tool call when heading is detected - Prevents plan content being dropped when heading follows tool call without a blank line separator 3. Support tool names with dots/hyphens - Change \w+ to [^\s]+ so names like "web.run" or "file-read" work --------- Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
committed by
GitHub
parent
655f254538
commit
7773db559d
@@ -102,6 +102,8 @@
|
||||
"react-markdown": "10.1.0",
|
||||
"react-resizable-panels": "3.0.6",
|
||||
"rehype-raw": "7.0.0",
|
||||
"rehype-sanitize": "^6.0.0",
|
||||
"remark-gfm": "^4.0.1",
|
||||
"sonner": "2.0.7",
|
||||
"tailwind-merge": "3.4.0",
|
||||
"usehooks-ts": "3.1.1",
|
||||
|
||||
@@ -1,13 +1,97 @@
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
import ReactMarkdown, { Components } from 'react-markdown';
|
||||
import rehypeRaw from 'rehype-raw';
|
||||
import rehypeSanitize from 'rehype-sanitize';
|
||||
import remarkGfm from 'remark-gfm';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { Square, CheckSquare } from 'lucide-react';
|
||||
|
||||
interface MarkdownProps {
|
||||
children: string;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a tasks code block as a proper task list with checkboxes
|
||||
*/
|
||||
function TasksBlock({ content }: { content: string }) {
|
||||
const lines = content.split('\n');
|
||||
|
||||
return (
|
||||
<div className="my-4 space-y-1">
|
||||
{lines.map((line, idx) => {
|
||||
const trimmed = line.trim();
|
||||
|
||||
// Check for phase/section headers (## Phase 1: ...)
|
||||
const headerMatch = trimmed.match(/^##\s+(.+)$/);
|
||||
if (headerMatch) {
|
||||
return (
|
||||
<div key={idx} className="text-foreground font-semibold mt-4 mb-2 text-sm">
|
||||
{headerMatch[1]}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Check for task items (- [ ] or - [x])
|
||||
const taskMatch = trimmed.match(/^-\s*\[([ xX])\]\s*(.+)$/);
|
||||
if (taskMatch) {
|
||||
const isChecked = taskMatch[1].toLowerCase() === 'x';
|
||||
const taskText = taskMatch[2];
|
||||
|
||||
return (
|
||||
<div key={idx} className="flex items-start gap-2 py-1">
|
||||
{isChecked ? (
|
||||
<CheckSquare className="w-4 h-4 text-emerald-500 mt-0.5 flex-shrink-0" />
|
||||
) : (
|
||||
<Square className="w-4 h-4 text-muted-foreground mt-0.5 flex-shrink-0" />
|
||||
)}
|
||||
<span
|
||||
className={cn(
|
||||
'text-sm',
|
||||
isChecked ? 'text-muted-foreground line-through' : 'text-foreground-secondary'
|
||||
)}
|
||||
>
|
||||
{taskText}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Empty lines
|
||||
if (!trimmed) {
|
||||
return <div key={idx} className="h-2" />;
|
||||
}
|
||||
|
||||
// Other content (render as-is)
|
||||
return (
|
||||
<div key={idx} className="text-sm text-foreground-secondary">
|
||||
{trimmed}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom components for ReactMarkdown
|
||||
*/
|
||||
const markdownComponents: Components = {
|
||||
// Handle code blocks - special case for 'tasks' language
|
||||
code({ className, children }) {
|
||||
const match = /language-(\w+)/.exec(className || '');
|
||||
const language = match ? match[1] : '';
|
||||
const content = String(children).replace(/\n$/, '');
|
||||
|
||||
// Special handling for tasks code blocks
|
||||
if (language === 'tasks') {
|
||||
return <TasksBlock content={content} />;
|
||||
}
|
||||
|
||||
// Regular code (inline or block)
|
||||
return <code className={className}>{children}</code>;
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Reusable Markdown component for rendering markdown content
|
||||
* Theme-aware styling that adapts to all predefined themes
|
||||
@@ -42,10 +126,20 @@ export function Markdown({ children, className }: MarkdownProps) {
|
||||
'[&_hr]:border-border [&_hr]:my-4',
|
||||
// Images
|
||||
'[&_img]:max-w-full [&_img]:h-auto [&_img]:rounded-lg [&_img]:my-2 [&_img]:border [&_img]:border-border',
|
||||
// Tables
|
||||
'[&_table]:w-full [&_table]:border-collapse [&_table]:my-4',
|
||||
'[&_th]:border [&_th]:border-border [&_th]:bg-muted [&_th]:px-3 [&_th]:py-2 [&_th]:text-left [&_th]:text-foreground [&_th]:font-semibold',
|
||||
'[&_td]:border [&_td]:border-border [&_td]:px-3 [&_td]:py-2 [&_td]:text-foreground-secondary',
|
||||
className
|
||||
)}
|
||||
>
|
||||
<ReactMarkdown rehypePlugins={[rehypeRaw, rehypeSanitize]}>{children}</ReactMarkdown>
|
||||
<ReactMarkdown
|
||||
remarkPlugins={[remarkGfm]}
|
||||
rehypePlugins={[rehypeRaw, rehypeSanitize]}
|
||||
components={markdownComponents}
|
||||
>
|
||||
{children}
|
||||
</ReactMarkdown>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
} from '@/components/ui/dialog';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Textarea } from '@/components/ui/textarea';
|
||||
import { Markdown } from '@/components/ui/markdown';
|
||||
import { PlanContentViewer } from './plan-content-viewer';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { Feature } from '@/store/app-store';
|
||||
import { Check, RefreshCw, Edit2, Eye } from 'lucide-react';
|
||||
@@ -42,6 +42,10 @@ export function PlanApprovalDialog({
|
||||
const [editedPlan, setEditedPlan] = useState(planContent);
|
||||
const [showRejectFeedback, setShowRejectFeedback] = useState(false);
|
||||
const [rejectFeedback, setRejectFeedback] = useState('');
|
||||
const [showFullDescription, setShowFullDescription] = useState(false);
|
||||
|
||||
const DESCRIPTION_LIMIT = 250;
|
||||
const TITLE_LIMIT = 50;
|
||||
|
||||
// Reset state when dialog opens or plan content changes
|
||||
useEffect(() => {
|
||||
@@ -50,6 +54,7 @@ export function PlanApprovalDialog({
|
||||
setIsEditMode(false);
|
||||
setShowRejectFeedback(false);
|
||||
setRejectFeedback('');
|
||||
setShowFullDescription(false);
|
||||
}
|
||||
}, [open, planContent]);
|
||||
|
||||
@@ -82,15 +87,31 @@ export function PlanApprovalDialog({
|
||||
<Dialog open={open} onOpenChange={handleClose}>
|
||||
<DialogContent className="max-w-4xl" data-testid="plan-approval-dialog">
|
||||
<DialogHeader>
|
||||
<DialogTitle>{viewOnly ? 'View Plan' : 'Review Plan'}</DialogTitle>
|
||||
<DialogTitle>
|
||||
{viewOnly ? 'View Plan' : 'Review Plan'}
|
||||
{feature?.title && feature.title.length <= TITLE_LIMIT && (
|
||||
<span className="font-normal text-muted-foreground"> - {feature.title}</span>
|
||||
)}
|
||||
</DialogTitle>
|
||||
<DialogDescription>
|
||||
{viewOnly
|
||||
? 'View the generated plan for this feature.'
|
||||
: 'Review the generated plan before implementation begins.'}
|
||||
{feature && (
|
||||
<span className="block mt-2 text-primary">
|
||||
Feature: {feature.description.slice(0, 150)}
|
||||
{feature.description.length > 150 ? '...' : ''}
|
||||
Feature:{' '}
|
||||
{showFullDescription || feature.description.length <= DESCRIPTION_LIMIT
|
||||
? feature.description
|
||||
: `${feature.description.slice(0, DESCRIPTION_LIMIT)}...`}
|
||||
{feature.description.length > DESCRIPTION_LIMIT && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowFullDescription(!showFullDescription)}
|
||||
className="ml-1 text-muted-foreground hover:text-foreground underline text-sm"
|
||||
>
|
||||
{showFullDescription ? 'show less' : 'show more'}
|
||||
</button>
|
||||
)}
|
||||
</span>
|
||||
)}
|
||||
</DialogDescription>
|
||||
@@ -135,9 +156,7 @@ export function PlanApprovalDialog({
|
||||
disabled={isLoading}
|
||||
/>
|
||||
) : (
|
||||
<div className="p-4 overflow-auto">
|
||||
<Markdown>{editedPlan || 'No plan content available.'}</Markdown>
|
||||
</div>
|
||||
<PlanContentViewer content={editedPlan || ''} className="p-4" />
|
||||
)}
|
||||
</div>
|
||||
|
||||
|
||||
@@ -0,0 +1,216 @@
|
||||
'use client';
|
||||
|
||||
import { useMemo, useState } from 'react';
|
||||
import { ChevronDown, ChevronRight, Wrench } from 'lucide-react';
|
||||
import { Markdown } from '@/components/ui/markdown';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
interface ToolCall {
|
||||
tool: string;
|
||||
input: string;
|
||||
}
|
||||
|
||||
interface ParsedPlanContent {
|
||||
toolCalls: ToolCall[];
|
||||
planMarkdown: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses plan content to separate tool calls from the actual plan/specification markdown.
|
||||
* Tool calls appear at the beginning (exploration phase), followed by the plan markdown.
|
||||
*/
|
||||
function parsePlanContent(content: string): ParsedPlanContent {
|
||||
const lines = content.split('\n');
|
||||
const toolCalls: ToolCall[] = [];
|
||||
let planStartIndex = -1;
|
||||
|
||||
let currentTool: string | null = null;
|
||||
let currentInput: string[] = [];
|
||||
let inJsonBlock = false;
|
||||
let braceDepth = 0;
|
||||
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
const line = lines[i];
|
||||
const trimmed = line.trim();
|
||||
|
||||
// Check if this line starts the actual plan/spec (markdown heading)
|
||||
// Plans typically start with # or ## headings
|
||||
if (
|
||||
!inJsonBlock &&
|
||||
(trimmed.match(/^#{1,3}\s+\S/) || // Markdown headings (including emoji like ## ✅ Plan)
|
||||
trimmed.startsWith('---') || // Horizontal rule often used as separator
|
||||
trimmed.match(/^\*\*\S/)) // Bold text starting a section
|
||||
) {
|
||||
// Flush any active tool call before starting the plan
|
||||
if (currentTool && currentInput.length > 0) {
|
||||
toolCalls.push({
|
||||
tool: currentTool,
|
||||
input: currentInput.join('\n').trim(),
|
||||
});
|
||||
currentTool = null;
|
||||
currentInput = [];
|
||||
}
|
||||
planStartIndex = i;
|
||||
break;
|
||||
}
|
||||
|
||||
// Detect tool call start (supports tool names with dots/hyphens like web.run, file-read)
|
||||
const toolMatch = trimmed.match(/^(?:🔧\s*)?Tool:\s*([^\s]+)/i);
|
||||
if (toolMatch && !inJsonBlock) {
|
||||
// Save previous tool call if exists
|
||||
if (currentTool && currentInput.length > 0) {
|
||||
toolCalls.push({
|
||||
tool: currentTool,
|
||||
input: currentInput.join('\n').trim(),
|
||||
});
|
||||
}
|
||||
currentTool = toolMatch[1];
|
||||
currentInput = [];
|
||||
continue;
|
||||
}
|
||||
|
||||
// Detect Input: line
|
||||
if (trimmed.startsWith('Input:') && currentTool) {
|
||||
const inputContent = trimmed.replace(/^Input:\s*/, '');
|
||||
if (inputContent) {
|
||||
currentInput.push(inputContent);
|
||||
// Check if JSON starts
|
||||
if (inputContent.includes('{')) {
|
||||
braceDepth =
|
||||
(inputContent.match(/\{/g) || []).length - (inputContent.match(/\}/g) || []).length;
|
||||
inJsonBlock = braceDepth > 0;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// If we're collecting input for a tool
|
||||
if (currentTool) {
|
||||
if (inJsonBlock) {
|
||||
currentInput.push(line);
|
||||
braceDepth += (trimmed.match(/\{/g) || []).length - (trimmed.match(/\}/g) || []).length;
|
||||
if (braceDepth <= 0) {
|
||||
inJsonBlock = false;
|
||||
// Save tool call
|
||||
toolCalls.push({
|
||||
tool: currentTool,
|
||||
input: currentInput.join('\n').trim(),
|
||||
});
|
||||
currentTool = null;
|
||||
currentInput = [];
|
||||
}
|
||||
} else if (trimmed.startsWith('{')) {
|
||||
// JSON block starting
|
||||
currentInput.push(line);
|
||||
braceDepth = (trimmed.match(/\{/g) || []).length - (trimmed.match(/\}/g) || []).length;
|
||||
inJsonBlock = braceDepth > 0;
|
||||
if (!inJsonBlock) {
|
||||
// Single-line JSON
|
||||
toolCalls.push({
|
||||
tool: currentTool,
|
||||
input: currentInput.join('\n').trim(),
|
||||
});
|
||||
currentTool = null;
|
||||
currentInput = [];
|
||||
}
|
||||
} else if (trimmed === '') {
|
||||
// Empty line might end the tool call section
|
||||
if (currentInput.length > 0) {
|
||||
toolCalls.push({
|
||||
tool: currentTool,
|
||||
input: currentInput.join('\n').trim(),
|
||||
});
|
||||
currentTool = null;
|
||||
currentInput = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Save any remaining tool call
|
||||
if (currentTool && currentInput.length > 0) {
|
||||
toolCalls.push({
|
||||
tool: currentTool,
|
||||
input: currentInput.join('\n').trim(),
|
||||
});
|
||||
}
|
||||
|
||||
// Extract plan markdown
|
||||
let planMarkdown = '';
|
||||
if (planStartIndex >= 0) {
|
||||
planMarkdown = lines.slice(planStartIndex).join('\n').trim();
|
||||
} else if (toolCalls.length === 0) {
|
||||
// No tool calls found, treat entire content as markdown
|
||||
planMarkdown = content.trim();
|
||||
}
|
||||
|
||||
return { toolCalls, planMarkdown };
|
||||
}
|
||||
|
||||
interface PlanContentViewerProps {
|
||||
content: string;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export function PlanContentViewer({ content, className }: PlanContentViewerProps) {
|
||||
const [showToolCalls, setShowToolCalls] = useState(false);
|
||||
|
||||
const { toolCalls, planMarkdown } = useMemo(() => parsePlanContent(content), [content]);
|
||||
|
||||
if (!content || !content.trim()) {
|
||||
return (
|
||||
<div className={cn('text-muted-foreground text-center py-8', className)}>
|
||||
No plan content available.
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={cn('space-y-4', className)}>
|
||||
{/* Tool Calls Section - Collapsed by default */}
|
||||
{toolCalls.length > 0 && (
|
||||
<div className="border border-border rounded-lg overflow-hidden">
|
||||
<button
|
||||
onClick={() => setShowToolCalls(!showToolCalls)}
|
||||
className="w-full px-4 py-2 flex items-center gap-2 bg-muted/30 hover:bg-muted/50 transition-colors text-left"
|
||||
>
|
||||
{showToolCalls ? (
|
||||
<ChevronDown className="w-4 h-4 text-muted-foreground" />
|
||||
) : (
|
||||
<ChevronRight className="w-4 h-4 text-muted-foreground" />
|
||||
)}
|
||||
<Wrench className="w-4 h-4 text-muted-foreground" />
|
||||
<span className="text-sm text-muted-foreground">
|
||||
Exploration ({toolCalls.length} tool call{toolCalls.length !== 1 ? 's' : ''})
|
||||
</span>
|
||||
</button>
|
||||
|
||||
{showToolCalls && (
|
||||
<div className="p-3 space-y-2 bg-muted/10 max-h-[300px] overflow-y-auto">
|
||||
{toolCalls.map((tc, idx) => (
|
||||
<div key={idx} className="text-xs font-mono">
|
||||
<div className="text-cyan-400 mb-1">Tool: {tc.tool}</div>
|
||||
<pre className="bg-muted/50 rounded p-2 overflow-x-auto text-foreground-secondary whitespace-pre-wrap">
|
||||
{tc.input}
|
||||
</pre>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Plan/Specification Content - Main focus */}
|
||||
{planMarkdown ? (
|
||||
<div className="min-h-[200px]">
|
||||
<Markdown>{planMarkdown}</Markdown>
|
||||
</div>
|
||||
) : toolCalls.length > 0 ? (
|
||||
<div className="text-muted-foreground text-center py-8 border border-dashed border-border rounded-lg">
|
||||
<p className="text-sm">No specification content found.</p>
|
||||
<p className="text-xs mt-1">The plan appears to only contain exploration tool calls.</p>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
295
package-lock.json
generated
295
package-lock.json
generated
@@ -163,6 +163,8 @@
|
||||
"react-markdown": "10.1.0",
|
||||
"react-resizable-panels": "3.0.6",
|
||||
"rehype-raw": "7.0.0",
|
||||
"rehype-sanitize": "^6.0.0",
|
||||
"remark-gfm": "^4.0.1",
|
||||
"sonner": "2.0.7",
|
||||
"tailwind-merge": "3.4.0",
|
||||
"usehooks-ts": "3.1.1",
|
||||
@@ -12130,6 +12132,16 @@
|
||||
"node": "^18.17.0 || >=20.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/markdown-table": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz",
|
||||
"integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/matcher": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz",
|
||||
@@ -12153,6 +12165,34 @@
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/mdast-util-find-and-replace": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz",
|
||||
"integrity": "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/mdast": "^4.0.0",
|
||||
"escape-string-regexp": "^5.0.0",
|
||||
"unist-util-is": "^6.0.0",
|
||||
"unist-util-visit-parents": "^6.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/mdast-util-find-and-replace/node_modules/escape-string-regexp": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz",
|
||||
"integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/mdast-util-from-markdown": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz",
|
||||
@@ -12177,6 +12217,107 @@
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/mdast-util-gfm": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz",
|
||||
"integrity": "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"mdast-util-from-markdown": "^2.0.0",
|
||||
"mdast-util-gfm-autolink-literal": "^2.0.0",
|
||||
"mdast-util-gfm-footnote": "^2.0.0",
|
||||
"mdast-util-gfm-strikethrough": "^2.0.0",
|
||||
"mdast-util-gfm-table": "^2.0.0",
|
||||
"mdast-util-gfm-task-list-item": "^2.0.0",
|
||||
"mdast-util-to-markdown": "^2.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/mdast-util-gfm-autolink-literal": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz",
|
||||
"integrity": "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/mdast": "^4.0.0",
|
||||
"ccount": "^2.0.0",
|
||||
"devlop": "^1.0.0",
|
||||
"mdast-util-find-and-replace": "^3.0.0",
|
||||
"micromark-util-character": "^2.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/mdast-util-gfm-footnote": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.1.0.tgz",
|
||||
"integrity": "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/mdast": "^4.0.0",
|
||||
"devlop": "^1.1.0",
|
||||
"mdast-util-from-markdown": "^2.0.0",
|
||||
"mdast-util-to-markdown": "^2.0.0",
|
||||
"micromark-util-normalize-identifier": "^2.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/mdast-util-gfm-strikethrough": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz",
|
||||
"integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/mdast": "^4.0.0",
|
||||
"mdast-util-from-markdown": "^2.0.0",
|
||||
"mdast-util-to-markdown": "^2.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/mdast-util-gfm-table": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz",
|
||||
"integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/mdast": "^4.0.0",
|
||||
"devlop": "^1.0.0",
|
||||
"markdown-table": "^3.0.0",
|
||||
"mdast-util-from-markdown": "^2.0.0",
|
||||
"mdast-util-to-markdown": "^2.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/mdast-util-gfm-task-list-item": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz",
|
||||
"integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/mdast": "^4.0.0",
|
||||
"devlop": "^1.0.0",
|
||||
"mdast-util-from-markdown": "^2.0.0",
|
||||
"mdast-util-to-markdown": "^2.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/mdast-util-mdx-expression": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz",
|
||||
@@ -12396,6 +12537,127 @@
|
||||
"micromark-util-types": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/micromark-extension-gfm": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz",
|
||||
"integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"micromark-extension-gfm-autolink-literal": "^2.0.0",
|
||||
"micromark-extension-gfm-footnote": "^2.0.0",
|
||||
"micromark-extension-gfm-strikethrough": "^2.0.0",
|
||||
"micromark-extension-gfm-table": "^2.0.0",
|
||||
"micromark-extension-gfm-tagfilter": "^2.0.0",
|
||||
"micromark-extension-gfm-task-list-item": "^2.0.0",
|
||||
"micromark-util-combine-extensions": "^2.0.0",
|
||||
"micromark-util-types": "^2.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/micromark-extension-gfm-autolink-literal": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz",
|
||||
"integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"micromark-util-character": "^2.0.0",
|
||||
"micromark-util-sanitize-uri": "^2.0.0",
|
||||
"micromark-util-symbol": "^2.0.0",
|
||||
"micromark-util-types": "^2.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/micromark-extension-gfm-footnote": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz",
|
||||
"integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"devlop": "^1.0.0",
|
||||
"micromark-core-commonmark": "^2.0.0",
|
||||
"micromark-factory-space": "^2.0.0",
|
||||
"micromark-util-character": "^2.0.0",
|
||||
"micromark-util-normalize-identifier": "^2.0.0",
|
||||
"micromark-util-sanitize-uri": "^2.0.0",
|
||||
"micromark-util-symbol": "^2.0.0",
|
||||
"micromark-util-types": "^2.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/micromark-extension-gfm-strikethrough": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz",
|
||||
"integrity": "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"devlop": "^1.0.0",
|
||||
"micromark-util-chunked": "^2.0.0",
|
||||
"micromark-util-classify-character": "^2.0.0",
|
||||
"micromark-util-resolve-all": "^2.0.0",
|
||||
"micromark-util-symbol": "^2.0.0",
|
||||
"micromark-util-types": "^2.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/micromark-extension-gfm-table": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz",
|
||||
"integrity": "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"devlop": "^1.0.0",
|
||||
"micromark-factory-space": "^2.0.0",
|
||||
"micromark-util-character": "^2.0.0",
|
||||
"micromark-util-symbol": "^2.0.0",
|
||||
"micromark-util-types": "^2.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/micromark-extension-gfm-tagfilter": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz",
|
||||
"integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"micromark-util-types": "^2.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/micromark-extension-gfm-task-list-item": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz",
|
||||
"integrity": "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"devlop": "^1.0.0",
|
||||
"micromark-factory-space": "^2.0.0",
|
||||
"micromark-util-character": "^2.0.0",
|
||||
"micromark-util-symbol": "^2.0.0",
|
||||
"micromark-util-types": "^2.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/micromark-factory-destination": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz",
|
||||
@@ -14184,6 +14446,24 @@
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/remark-gfm": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.1.tgz",
|
||||
"integrity": "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/mdast": "^4.0.0",
|
||||
"mdast-util-gfm": "^3.0.0",
|
||||
"micromark-extension-gfm": "^3.0.0",
|
||||
"remark-parse": "^11.0.0",
|
||||
"remark-stringify": "^11.0.0",
|
||||
"unified": "^11.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/remark-parse": {
|
||||
"version": "11.0.0",
|
||||
"resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz",
|
||||
@@ -14217,6 +14497,21 @@
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/remark-stringify": {
|
||||
"version": "11.0.0",
|
||||
"resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz",
|
||||
"integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/mdast": "^4.0.0",
|
||||
"mdast-util-to-markdown": "^2.0.0",
|
||||
"unified": "^11.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/require-directory": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
|
||||
|
||||
Reference in New Issue
Block a user