Update Next.js configuration and package dependencies
All checks were successful
Deploy / deploy (push) Successful in 9m29s
All checks were successful
Deploy / deploy (push) Successful in 9m29s
- Modified redirects in next.config.ts to change the destination for "/sohbet" to "/listem". - Added new dependencies in package.json: marked, motion, react-markdown, remark-breaks, remark-gfm, shiki, and use-stick-to-bottom. - Updated pnpm-lock.yaml to reflect the new package versions and dependencies. - Removed obsolete stack files for Flutter, Nuxt, Nuxt.js, React Native, and Svelte from the UI/UX Pro Max skill data. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
110
src/components/ui/markdown.tsx
Normal file
110
src/components/ui/markdown.tsx
Normal file
@@ -0,0 +1,110 @@
|
||||
import { cn } from "@/lib/utils"
|
||||
import { marked } from "marked"
|
||||
import { memo, useId, useMemo } from "react"
|
||||
import ReactMarkdown, { Components } from "react-markdown"
|
||||
import remarkBreaks from "remark-breaks"
|
||||
import remarkGfm from "remark-gfm"
|
||||
import { CodeBlock, CodeBlockCode } from "./code-block"
|
||||
|
||||
export type MarkdownProps = {
|
||||
children: string
|
||||
id?: string
|
||||
className?: string
|
||||
components?: Partial<Components>
|
||||
}
|
||||
|
||||
function parseMarkdownIntoBlocks(markdown: string): string[] {
|
||||
const tokens = marked.lexer(markdown)
|
||||
return tokens.map((token) => token.raw)
|
||||
}
|
||||
|
||||
function extractLanguage(className?: string): string {
|
||||
if (!className) return "plaintext"
|
||||
const match = className.match(/language-(\w+)/)
|
||||
return match ? match[1] : "plaintext"
|
||||
}
|
||||
|
||||
const INITIAL_COMPONENTS: Partial<Components> = {
|
||||
code: function CodeComponent({ className, children, ...props }) {
|
||||
const isInline =
|
||||
!props.node?.position?.start.line ||
|
||||
props.node?.position?.start.line === props.node?.position?.end.line
|
||||
|
||||
if (isInline) {
|
||||
return (
|
||||
<span
|
||||
className={cn(
|
||||
"bg-primary-foreground rounded-sm px-1 font-mono text-sm",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
const language = extractLanguage(className)
|
||||
|
||||
return (
|
||||
<CodeBlock className={className}>
|
||||
<CodeBlockCode code={children as string} language={language} />
|
||||
</CodeBlock>
|
||||
)
|
||||
},
|
||||
pre: function PreComponent({ children }) {
|
||||
return <>{children}</>
|
||||
},
|
||||
}
|
||||
|
||||
const MemoizedMarkdownBlock = memo(
|
||||
function MarkdownBlock({
|
||||
content,
|
||||
components = INITIAL_COMPONENTS,
|
||||
}: {
|
||||
content: string
|
||||
components?: Partial<Components>
|
||||
}) {
|
||||
return (
|
||||
<ReactMarkdown
|
||||
remarkPlugins={[remarkGfm, remarkBreaks]}
|
||||
components={components}
|
||||
>
|
||||
{content}
|
||||
</ReactMarkdown>
|
||||
)
|
||||
},
|
||||
function propsAreEqual(prevProps, nextProps) {
|
||||
return prevProps.content === nextProps.content
|
||||
}
|
||||
)
|
||||
|
||||
MemoizedMarkdownBlock.displayName = "MemoizedMarkdownBlock"
|
||||
|
||||
function MarkdownComponent({
|
||||
children,
|
||||
id,
|
||||
className,
|
||||
components = INITIAL_COMPONENTS,
|
||||
}: MarkdownProps) {
|
||||
const generatedId = useId()
|
||||
const blockId = id ?? generatedId
|
||||
const blocks = useMemo(() => parseMarkdownIntoBlocks(children), [children])
|
||||
|
||||
return (
|
||||
<div className={className}>
|
||||
{blocks.map((block, index) => (
|
||||
<MemoizedMarkdownBlock
|
||||
key={`${blockId}-block-${index}`}
|
||||
content={block}
|
||||
components={components}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const Markdown = memo(MarkdownComponent)
|
||||
Markdown.displayName = "Markdown"
|
||||
|
||||
export { Markdown }
|
||||
Reference in New Issue
Block a user