Initial commit
This commit is contained in:
160
skills/markdown-editor-integrator/assets/MarkdownEditor.tsx
Normal file
160
skills/markdown-editor-integrator/assets/MarkdownEditor.tsx
Normal file
@@ -0,0 +1,160 @@
|
||||
'use client'
|
||||
|
||||
import { useTheme } from 'next-themes'
|
||||
import dynamic from 'next/dynamic'
|
||||
import { ComponentProps } from 'react'
|
||||
import rehypeSanitize from 'rehype-sanitize'
|
||||
import '@uiw/react-md-editor/markdown-editor.css'
|
||||
import '@uiw/react-markdown-preview/markdown.css'
|
||||
|
||||
const MDEditor = dynamic(
|
||||
() => import('@uiw/react-md-editor'),
|
||||
{ ssr: false }
|
||||
)
|
||||
|
||||
type MDEditorProps = ComponentProps<typeof MDEditor>
|
||||
|
||||
interface MarkdownEditorProps extends Omit<MDEditorProps, 'data-color-mode'> {
|
||||
value: string
|
||||
onChange?: (value?: string) => void
|
||||
height?: number | string
|
||||
hideToolbar?: boolean
|
||||
enablePreview?: boolean
|
||||
preview?: 'edit' | 'live' | 'preview'
|
||||
}
|
||||
|
||||
/**
|
||||
* Markdown Editor component with theme integration and sanitization
|
||||
*
|
||||
* Features:
|
||||
* - Automatic theme switching (light/dark)
|
||||
* - XSS protection with rehype-sanitize
|
||||
* - Controlled component for forms
|
||||
* - Customizable toolbar and preview
|
||||
* - Next.js SSR compatible
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* const [content, setContent] = useState('')
|
||||
*
|
||||
* <MarkdownEditor
|
||||
* value={content}
|
||||
* onChange={setContent}
|
||||
* height={400}
|
||||
* />
|
||||
* ```
|
||||
*/
|
||||
export function MarkdownEditor({
|
||||
value,
|
||||
onChange,
|
||||
height = 400,
|
||||
hideToolbar = false,
|
||||
enablePreview = true,
|
||||
preview = 'live',
|
||||
...props
|
||||
}: MarkdownEditorProps) {
|
||||
const { theme } = useTheme()
|
||||
|
||||
return (
|
||||
<div
|
||||
data-color-mode={theme === 'dark' ? 'dark' : 'light'}
|
||||
className="markdown-editor-wrapper"
|
||||
>
|
||||
<MDEditor
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
height={height}
|
||||
hideToolbar={hideToolbar}
|
||||
enablePreview={enablePreview}
|
||||
preview={preview}
|
||||
previewOptions={{
|
||||
rehypePlugins: [[rehypeSanitize]],
|
||||
}}
|
||||
{...props}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// CSS for theme integration (add to globals.css)
|
||||
const editorStyles = `
|
||||
/* Markdown Editor Theme Integration */
|
||||
|
||||
.markdown-editor-wrapper {
|
||||
@apply rounded-md border border-input;
|
||||
}
|
||||
|
||||
.w-md-editor {
|
||||
@apply bg-background text-foreground shadow-none;
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
.w-md-editor-toolbar {
|
||||
@apply border-b border-border bg-muted/30;
|
||||
}
|
||||
|
||||
.w-md-editor-toolbar button,
|
||||
.w-md-editor-toolbar li button {
|
||||
@apply text-foreground hover:bg-accent hover:text-accent-foreground;
|
||||
}
|
||||
|
||||
.w-md-editor-toolbar li.active button {
|
||||
@apply bg-accent text-accent-foreground;
|
||||
}
|
||||
|
||||
.w-md-editor-content {
|
||||
@apply text-foreground;
|
||||
}
|
||||
|
||||
.w-md-editor-preview {
|
||||
@apply prose prose-sm dark:prose-invert max-w-none p-4;
|
||||
}
|
||||
|
||||
.w-md-editor-text-pre,
|
||||
.w-md-editor-text-input {
|
||||
@apply text-foreground;
|
||||
}
|
||||
|
||||
.w-md-editor-text-pre > code,
|
||||
.w-md-editor-text-input > code {
|
||||
@apply text-foreground;
|
||||
}
|
||||
|
||||
/* Code blocks in preview */
|
||||
.wmde-markdown {
|
||||
@apply bg-transparent text-foreground;
|
||||
}
|
||||
|
||||
.wmde-markdown pre {
|
||||
@apply bg-muted;
|
||||
}
|
||||
|
||||
.wmde-markdown code {
|
||||
@apply text-primary;
|
||||
}
|
||||
|
||||
.wmde-markdown blockquote {
|
||||
@apply border-l-primary/50;
|
||||
}
|
||||
|
||||
.wmde-markdown a {
|
||||
@apply text-primary hover:underline;
|
||||
}
|
||||
|
||||
/* Tables in preview */
|
||||
.wmde-markdown table {
|
||||
@apply border-border;
|
||||
}
|
||||
|
||||
.wmde-markdown th,
|
||||
.wmde-markdown td {
|
||||
@apply border-border;
|
||||
}
|
||||
|
||||
.wmde-markdown th {
|
||||
@apply bg-muted;
|
||||
}
|
||||
`
|
||||
|
||||
// Export styles constant for documentation
|
||||
export { editorStyles }
|
||||
151
skills/markdown-editor-integrator/assets/MarkdownPreview.tsx
Normal file
151
skills/markdown-editor-integrator/assets/MarkdownPreview.tsx
Normal file
@@ -0,0 +1,151 @@
|
||||
'use client'
|
||||
|
||||
import { useTheme } from 'next-themes'
|
||||
import dynamic from 'next/dynamic'
|
||||
import { ComponentProps } from 'react'
|
||||
import rehypeSanitize from 'rehype-sanitize'
|
||||
import '@uiw/react-markdown-preview/markdown.css'
|
||||
|
||||
const MDPreview = dynamic(
|
||||
() => import('@uiw/react-markdown-preview'),
|
||||
{ ssr: false }
|
||||
)
|
||||
|
||||
type MDPreviewProps = ComponentProps<typeof MDPreview>
|
||||
|
||||
interface MarkdownPreviewProps extends Omit<MDPreviewProps, 'source' | 'data-color-mode'> {
|
||||
content: string
|
||||
className?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Markdown Preview component for displaying formatted markdown
|
||||
*
|
||||
* Features:
|
||||
* - Automatic theme switching
|
||||
* - XSS protection with rehype-sanitize
|
||||
* - GitHub-flavored markdown support
|
||||
* - Syntax highlighting for code blocks
|
||||
* - Next.js SSR compatible
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* <MarkdownPreview
|
||||
* content={character.biography}
|
||||
* className="max-w-3xl"
|
||||
* />
|
||||
* ```
|
||||
*/
|
||||
export function MarkdownPreview({
|
||||
content,
|
||||
className = '',
|
||||
...props
|
||||
}: MarkdownPreviewProps) {
|
||||
const { theme } = useTheme()
|
||||
|
||||
return (
|
||||
<div
|
||||
data-color-mode={theme === 'dark' ? 'dark' : 'light'}
|
||||
className={`markdown-preview-wrapper ${className}`}
|
||||
>
|
||||
<MDPreview
|
||||
source={content}
|
||||
rehypePlugins={[[rehypeSanitize]]}
|
||||
{...props}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// CSS for preview theme integration (add to globals.css)
|
||||
const previewStyles = `
|
||||
/* Markdown Preview Theme Integration */
|
||||
|
||||
.markdown-preview-wrapper {
|
||||
@apply rounded-md;
|
||||
}
|
||||
|
||||
.markdown-preview-wrapper .wmde-markdown {
|
||||
@apply bg-transparent text-foreground;
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
/* Typography */
|
||||
.markdown-preview-wrapper .wmde-markdown h1,
|
||||
.markdown-preview-wrapper .wmde-markdown h2,
|
||||
.markdown-preview-wrapper .wmde-markdown h3,
|
||||
.markdown-preview-wrapper .wmde-markdown h4,
|
||||
.markdown-preview-wrapper .wmde-markdown h5,
|
||||
.markdown-preview-wrapper .wmde-markdown h6 {
|
||||
@apply text-foreground font-semibold;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.markdown-preview-wrapper .wmde-markdown p {
|
||||
@apply text-foreground leading-7;
|
||||
}
|
||||
|
||||
/* Links */
|
||||
.markdown-preview-wrapper .wmde-markdown a {
|
||||
@apply text-primary hover:underline;
|
||||
}
|
||||
|
||||
/* Lists */
|
||||
.markdown-preview-wrapper .wmde-markdown ul,
|
||||
.markdown-preview-wrapper .wmde-markdown ol {
|
||||
@apply text-foreground;
|
||||
}
|
||||
|
||||
/* Blockquotes */
|
||||
.markdown-preview-wrapper .wmde-markdown blockquote {
|
||||
@apply border-l-4 border-primary/50 bg-muted/50 text-muted-foreground italic;
|
||||
}
|
||||
|
||||
/* Code */
|
||||
.markdown-preview-wrapper .wmde-markdown code {
|
||||
@apply bg-muted text-primary px-1.5 py-0.5 rounded text-sm;
|
||||
}
|
||||
|
||||
.markdown-preview-wrapper .wmde-markdown pre {
|
||||
@apply bg-muted rounded-md p-4 overflow-x-auto;
|
||||
}
|
||||
|
||||
.markdown-preview-wrapper .wmde-markdown pre code {
|
||||
@apply bg-transparent p-0;
|
||||
}
|
||||
|
||||
/* Tables */
|
||||
.markdown-preview-wrapper .wmde-markdown table {
|
||||
@apply border-collapse w-full border border-border;
|
||||
}
|
||||
|
||||
.markdown-preview-wrapper .wmde-markdown th {
|
||||
@apply bg-muted font-semibold text-left p-2 border border-border;
|
||||
}
|
||||
|
||||
.markdown-preview-wrapper .wmde-markdown td {
|
||||
@apply p-2 border border-border;
|
||||
}
|
||||
|
||||
.markdown-preview-wrapper .wmde-markdown tr:nth-child(even) {
|
||||
@apply bg-muted/30;
|
||||
}
|
||||
|
||||
/* Horizontal Rule */
|
||||
.markdown-preview-wrapper .wmde-markdown hr {
|
||||
@apply border-border my-4;
|
||||
}
|
||||
|
||||
/* Images */
|
||||
.markdown-preview-wrapper .wmde-markdown img {
|
||||
@apply rounded-md max-w-full h-auto;
|
||||
}
|
||||
|
||||
/* Task Lists */
|
||||
.markdown-preview-wrapper .wmde-markdown input[type="checkbox"] {
|
||||
@apply mr-2;
|
||||
}
|
||||
`
|
||||
|
||||
// Export styles constant for documentation
|
||||
export { previewStyles }
|
||||
Reference in New Issue
Block a user