Improved code block

This commit is contained in:
2026-05-11 07:32:16 +02:00
parent ee0b8100ad
commit b8c7b6e3ea
2 changed files with 59 additions and 52 deletions
+5 -8
View File
@@ -1,8 +1,7 @@
"use client";
import MorphText from "@/components/Morph";
import { Card } from "fumadocs-ui/components/card";
import { BookOpenIcon, NewspaperIcon, RocketIcon } from "lucide-react";
import MorphCode from "@/components/Morph";
import { NewspaperIcon, RocketIcon } from "lucide-react";
import Link from "next/link";
import { useEffect, useRef, useState } from "react";
@@ -152,11 +151,9 @@ export default function HomePage() {
return (
<div className="flex w-full flex-col md:flex-row">
<div className="order-1 md:order-2 w-full md:w-[40vw] md:h-svh sticky top-20 md:top-0 flex items-center justify-center z-10 bg-background pb-4 md:pb-0">
<Card className="w-full max-w-md h-auto md:h-100" title>
<code className="whitespace-pre-wrap">
<MorphText text={currentText} />
</code>
</Card>
<div className="w-max h-auto border border-fd-border p-5 rounded-xl min-h-24">
<MorphCode code={currentText} />
</div>
</div>
<div className="flex-1 order-2 md:order-1">
+47 -37
View File
@@ -1,60 +1,70 @@
"use client";
import { BundledLanguage, codeToTokens } from "shiki";
import { motion, AnimatePresence } from "framer-motion";
import { useEffect, useState } from "react";
interface MorphTextProps {
text: string;
interface Props {
code: string;
lang?: BundledLanguage;
}
const tokenize = (str: string) => {
const counts: Record<string, number> = {};
return str.split("").map((char) => {
const displayChar = char === " " ? "\u00A0" : char;
counts[char] = (counts[char] || 0) + 1;
return { char: displayChar, id: `${char}-${counts[char]}` };
export default function MorphCode({ code, lang = "rust" }: Props) {
const [lines, setLines] = useState<any[][]>([]);
useEffect(() => {
async function run() {
const result = await codeToTokens(code, {
lang,
theme: "github-dark",
});
};
export default function MorphText({ text }: MorphTextProps) {
const lines = text.split("\n"); // 👈 split by newline
const rawLines = code.split("\n");
const fixed = rawLines.map((_, i) => {
return (
<div className="flex flex-col">
<AnimatePresence mode="popLayout">
{lines.map((line, lineIndex) => {
const tokens = tokenize(line);
// 👇 empty line = real vertical space
if (line === "") {
return (
<div
key={lineIndex}
className="h-4" // adjust spacing as needed
/>
result.tokens[i] ?? [
{
content: "\n",
color: undefined,
},
]
);
});
setLines(fixed);
}
run();
}, [code, lang]);
return (
<div key={lineIndex} className="flex flex-wrap">
{tokens.map(({ char, id }) => (
<pre className="text-sm">
<code>
<AnimatePresence mode="popLayout">
{lines.map((line, lineIndex) => (
<div key={lineIndex} className="flex min-h-[1em]">
{line.map((token, tokenIndex) => (
<motion.span
key={`${lineIndex}-${id}`}
layoutId={`${lineIndex}-${id}`}
key={`${lineIndex}-${tokenIndex}`}
layoutId={`${lineIndex}-${tokenIndex}`}
layout
initial={{ opacity: 0, y: 10 }}
initial={{ opacity: 0, y: 6 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -10 }}
transition={{
type: "keyframes",
duration: 0.6,
exit={{ opacity: 0, y: -6 }}
transition={{ duration: 0.35 }}
style={{
color: token.color,
whiteSpace: "pre",
}}
className="inline-block"
>
{char}
{token.content}
</motion.span>
))}
</div>
);
})}
))}
</AnimatePresence>
</div>
</code>
</pre>
);
}