Implemented mailbox in ConsoleUI

This commit is contained in:
2026-10-22 18:01:22 +02:00
parent 6ff016e7ec
commit cb89817d88
20 changed files with 3208 additions and 19 deletions
@@ -0,0 +1,146 @@
import React, { useState, useMemo } from 'react';
import {
Code,
Eye,
Copy,
Check,
ShieldCheck,
ExternalLink,
} from 'lucide-react';
import { BrandLogo } from '../ui/BrandLogo';
import { parseMarkdownToHtml, isHtmlContent } from '../../lib/markdownUtils';
interface MarkdownViewerProps {
content: string;
subject?: string;
showBrandingHeader?: boolean;
className?: string;
}
export const MarkdownViewer: React.FC<MarkdownViewerProps> = ({
content,
subject,
showBrandingHeader = true,
className = '',
}) => {
const [viewMode, setViewMode] = useState<'rendered' | 'raw'>('rendered');
const [copied, setCopied] = useState(false);
const isHtml = useMemo(() => isHtmlContent(content), [content]);
const renderedHtml = useMemo(() => {
if (!content) return '<p class="text-slate-400 italic">No content</p>';
if (isHtml) return content;
return parseMarkdownToHtml(content);
}, [content, isHtml]);
const handleCopy = () => {
navigator.clipboard.writeText(content);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
};
return (
<div className={`flex flex-col rounded-2xl bg-slate-900/40 border border-slate-800/80 overflow-hidden ${className}`}>
{/* Top Controls Bar */}
<div className="px-4 py-2 border-b border-slate-800 bg-[#0d1322]/80 flex items-center justify-between text-xs text-slate-400">
<div className="flex items-center gap-2">
{showBrandingHeader && (
<div className="flex items-center gap-1.5 text-slate-300 font-semibold pr-2 border-r border-slate-700/60">
<BrandLogo size={18} showGlow={false} animateOnHover={false} />
<span className="text-[11px] tracking-tight">PFM Markdown Engine</span>
</div>
)}
<span className="text-[10px] text-blue-400 font-mono">
{isHtml ? 'Format: HTML Email' : 'Format: GitHub Flavored Markdown'}
</span>
</div>
<div className="flex items-center gap-2">
{/* Toggle View Mode */}
<div className="flex items-center rounded-lg bg-slate-950/70 p-0.5 border border-slate-800">
<button
type="button"
onClick={() => setViewMode('rendered')}
className={`flex items-center gap-1 px-2 py-1 rounded-md text-[11px] font-medium transition cursor-pointer ${
viewMode === 'rendered'
? 'bg-blue-600/30 text-white font-semibold shadow-xs'
: 'text-slate-400 hover:text-white'
}`}
>
<Eye className="w-3 h-3" />
<span>Rendered</span>
</button>
<button
type="button"
onClick={() => setViewMode('raw')}
className={`flex items-center gap-1 px-2 py-1 rounded-md text-[11px] font-medium transition cursor-pointer ${
viewMode === 'raw'
? 'bg-blue-600/30 text-white font-semibold shadow-xs'
: 'text-slate-400 hover:text-white'
}`}
>
<Code className="w-3 h-3" />
<span>Raw Source</span>
</button>
</div>
{/* Copy Button */}
<button
type="button"
onClick={handleCopy}
className="flex items-center gap-1 px-2 py-1 rounded-lg bg-slate-800 hover:bg-slate-700 text-slate-300 hover:text-white text-[11px] font-medium transition cursor-pointer border border-slate-700/60"
title="Copy content to clipboard"
>
{copied ? (
<>
<Check className="w-3 h-3 text-emerald-400" />
<span className="text-emerald-300">Copied</span>
</>
) : (
<>
<Copy className="w-3 h-3" />
<span>Copy</span>
</>
)}
</button>
</div>
</div>
{/* Content Rendering */}
{viewMode === 'rendered' ? (
<div className="p-6 overflow-x-auto text-sm leading-relaxed text-slate-200 font-sans">
{isHtml ? (
<div
className="prose prose-invert max-w-none"
dangerouslySetInnerHTML={{ __html: renderedHtml }}
/>
) : (
<div
className="prose prose-invert max-w-none [&_h1]:text-white [&_h2]:text-white [&_h3]:text-white [&_h4]:text-white [&_a]:text-blue-400 [&_a:hover]:underline [&_table]:border-collapse [&_th]:border [&_th]:border-slate-700 [&_th]:p-2 [&_td]:border [&_td]:border-slate-800 [&_td]:p-2 [&_blockquote]:border-l-4 [&_blockquote]:border-blue-500 [&_blockquote]:pl-4 [&_blockquote]:italic [&_code]:bg-slate-800 [&_code]:px-1.5 [&_code]:py-0.5 [&_code]:rounded [&_code]:text-blue-300"
dangerouslySetInnerHTML={{ __html: renderedHtml }}
/>
)}
</div>
) : (
<pre className="p-5 overflow-x-auto font-mono text-xs text-slate-300 bg-[#070b13] select-all whitespace-pre-wrap leading-relaxed">
{content}
</pre>
)}
{/* Branded Footer Stamp */}
{showBrandingHeader && (
<div className="px-5 py-2.5 border-t border-slate-800/80 bg-[#0a0f1b] flex items-center justify-between text-[11px] text-slate-400">
<div className="flex items-center gap-1.5">
<ShieldCheck className="w-3.5 h-3.5 text-blue-400" />
<span>Verified PFM Secure Platform Formatting</span>
</div>
<span className="font-mono text-[10px] text-slate-400">
{content.length} characters
</span>
</div>
)}
</div>
);
};