Initial commit: TypoGenie - Markdown to Word document converter

This commit is contained in:
TypoGenie
2026-01-29 18:10:07 +02:00
commit f179e79f35
29 changed files with 6708 additions and 0 deletions

View File

@@ -0,0 +1,128 @@
import React, { useEffect, useRef } from 'react';
import { X } from 'lucide-react';
import { StyleOption } from '../types';
interface StylePreviewModalProps {
style: StyleOption;
onClose: () => void;
}
const SAMPLE_CONTENT = `
<h1>The Art of Typography</h1>
<p>Typography is the art and technique of arranging type to make written language legible, readable, and appealing when displayed. The arrangement of type involves selecting typefaces, point sizes, line lengths, line-spacing, and letter-spacing.</p>
<h2>1. Visual Hierarchy</h2>
<p>Visual hierarchy enables the reader to understand the importance of different sections. By using size, weight, and color, we guide the eye through the document in a logical flow.</p>
<blockquote>
"Design is not just what it looks like and feels like. Design is how it works."
</blockquote>
<h2>2. Key Elements</h2>
<ul>
<li><strong>Typeface:</strong> The design of the letters.</li>
<li><strong>Contrast:</strong> Distinguishing elements effectively.</li>
<li><strong>Consistency:</strong> Maintaining a coherent structure.</li>
</ul>
<p>Good typography is invisible. It should not distract the reader but rather enhance the reading experience, ensuring the message is delivered clearly and effectively.</p>
`;
export const StylePreviewModal: React.FC<StylePreviewModalProps> = ({ style, onClose }) => {
const iframeRef = useRef<HTMLIFrameElement>(null);
useEffect(() => {
const handleEscape = (e: KeyboardEvent) => {
if (e.key === 'Escape') onClose();
};
window.addEventListener('keydown', handleEscape);
return () => window.removeEventListener('keydown', handleEscape);
}, [onClose]);
useEffect(() => {
if (!iframeRef.current) return;
const doc = iframeRef.current.contentDocument;
if (doc) {
const html = `
<!DOCTYPE html>
<html>
<head>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="${style.googleFontsImport}" rel="stylesheet">
<style>
body {
background-color: #fff;
margin: 0;
padding: 40px;
/* Inject default body color from config */
color: #${style.wordConfig.body.color};
/* Apply Sample CSS */
${style.previewCss}
}
/* Override body margins from preview css to fit modal better */
body { width: 100%; height: 100%; box-sizing: border-box; overflow-y: auto; }
h1 { margin-top: 0 !important; }
</style>
</head>
<body>
${SAMPLE_CONTENT}
</body>
</html>
`;
doc.open();
doc.write(html);
doc.close();
}
}, [style]);
return (
<div
className="fixed inset-0 z-[100] flex items-center justify-center p-4 bg-zinc-950/80 backdrop-blur-sm animate-in fade-in duration-200"
onClick={(e) => {
// Close if clicking the background overlay directly
if (e.target === e.currentTarget) {
onClose();
}
}}
>
<div className="relative w-full max-w-3xl bg-zinc-900 rounded-2xl border border-zinc-700 shadow-2xl overflow-hidden flex flex-col h-[85vh]">
{/* Header */}
<div className="flex items-center justify-between p-4 border-b border-zinc-800 bg-zinc-900/50">
<div>
<h3 className="text-xl font-bold text-white">{style.name}</h3>
<p className="text-sm text-zinc-400">{style.description}</p>
</div>
<button
onClick={onClose}
className="p-2 text-zinc-400 hover:text-white hover:bg-zinc-800 rounded-full transition-colors"
>
<X size={24} />
</button>
</div>
{/* Content */}
<div className="flex-grow bg-zinc-800 p-1 overflow-hidden relative">
<div className="w-full h-full bg-white rounded-lg overflow-hidden shadow-inner">
<iframe
ref={iframeRef}
title="Style Preview"
className="w-full h-full border-0"
/>
</div>
</div>
{/* Footer */}
<div className="p-4 border-t border-zinc-800 bg-zinc-900 flex justify-end">
<button
onClick={onClose}
className="px-4 py-2 bg-indigo-600 hover:bg-indigo-500 text-white font-medium rounded-lg transition-colors"
>
Close Preview
</button>
</div>
</div>
</div>
);
};