@nvq/flowtoken
Version:
Animated React components for streaming text and markdown with GitHub theme syntax highlighting (forked from flowtoken)
134 lines (133 loc) • 6.18 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const react_1 = __importDefault(require("react"));
// Custom hook for dark mode detection
const useDarkMode = () => {
const [isDark, setIsDark] = react_1.default.useState(false);
react_1.default.useEffect(() => {
const checkDarkMode = () => {
// Detect Storybook dark mode toggle
const htmlElement = document.documentElement;
const isDarkMode = htmlElement.classList.contains("dark");
setIsDark(isDarkMode);
};
// Initial check
checkDarkMode();
// Monitor class changes with MutationObserver
const observer = new MutationObserver(checkDarkMode);
observer.observe(document.documentElement, {
attributes: true,
attributeFilter: ["class"],
});
return () => observer.disconnect();
}, []);
return isDark;
};
// Custom renderer for SHIKI (based on original customCodeRenderer animation logic)
const ShikiCodeRenderer = ({ codeContent, language, animation, animationDuration, animationTimingFunction, }) => {
const [renderedTokens, setRenderedTokens] = react_1.default.useState(null);
const isDark = useDarkMode();
react_1.default.useEffect(() => {
const renderCode = async () => {
try {
// Dynamic import to avoid serverExternalPackages issues
const { codeToTokens, bundledLanguages } = await Promise.resolve().then(() => __importStar(require("shiki")));
// Check if language is valid, fallback to 'text' if invalid
const validLanguage = language in bundledLanguages
? language
: "text";
// text言語(フォールバック)の場合のみ末尾改行を除去
const isUnsupportedLanguage = !(language in bundledLanguages);
const processedCode = isUnsupportedLanguage
? codeContent.trimEnd()
: codeContent;
// Select theme based on dark mode
const selectedTheme = isDark ? "github-dark" : "github-light";
const { tokens } = await codeToTokens(processedCode, {
lang: validLanguage,
theme: selectedTheme, // Specify single theme
});
// Apply animation logic similar to original customCodeRenderer
const rendered = tokens.map((line, lineIndex) => (react_1.default.createElement("div", { key: lineIndex, style: { display: "block" } }, line.map((token, tokenIndex) => {
// SHIKI's ThemedToken actually only has color property
const tokenStyles = {
color: token.color || (isDark ? "#e6edf3" : "#24292f"), // Theme default color when undefined
};
// Apply animation to each word like original customCodeRenderer
return (react_1.default.createElement("span", { key: tokenIndex, style: tokenStyles }, token.content
.split(" ")
.map((word, wordIndex) => (react_1.default.createElement("span", { key: wordIndex, style: {
animationName: animation || "",
animationDuration,
animationTimingFunction,
animationIterationCount: 1,
whiteSpace: "pre-wrap",
display: "inline-block",
} }, word +
(wordIndex < token.content.split(" ").length - 1
? " "
: ""))))));
}))));
setRenderedTokens(rendered);
}
catch (error) {
console.warn(`Failed to render code for language: ${language}`, error);
setRenderedTokens(`Error rendering code: ${error}`);
}
};
renderCode();
}, [
codeContent,
language,
animation,
animationDuration,
animationTimingFunction,
isDark, // Re-render when dark mode changes
]);
return (react_1.default.createElement("pre", { style: {
margin: 0,
padding: "1rem",
background: "transparent",
fontSize: "0.775rem",
overflow: "auto",
borderRadius: "0 0 0.75rem 0.75rem", // rounded-b-xl
} },
react_1.default.createElement("code", null, renderedTokens)));
};
exports.default = ShikiCodeRenderer;