docuflow / src /components /document /ReadingModeView.tsx
Joedroid's picture
fix: add remark-gfm for table parsing and table CSS styles
49852ff
Raw
History Blame Contribute Delete
9.13 kB
import React from "react";
import ReactMarkdown from "react-markdown";
import remarkGfm from "remark-gfm";
import { Minimize2, Type, BookOpen } from "lucide-react";
import { Document } from "../../types";
interface ReadingModeViewProps {
activeDoc: Document;
charCount: number;
wordCount: number;
readingTheme: "dark" | "sepia" | "light";
setReadingTheme: (theme: "dark" | "sepia" | "light") => void;
readingFontFamily: "sans" | "serif" | "mono";
setReadingFontFamily: (ff: "sans" | "serif" | "mono") => void;
readingFontSize: "sm" | "base" | "lg" | "xl";
setReadingFontSize: (sz: "sm" | "base" | "lg" | "xl") => void;
readingLineHeight: "normal" | "relaxed" | "loose";
setReadingLineHeight: (lh: "normal" | "relaxed" | "loose") => void;
setIsReadingMode: (isReading: boolean) => void;
}
export default function ReadingModeView({
activeDoc,
charCount,
wordCount,
readingTheme,
setReadingTheme,
readingFontFamily,
setReadingFontFamily,
readingFontSize,
setReadingFontSize,
readingLineHeight,
setReadingLineHeight,
setIsReadingMode
}: ReadingModeViewProps) {
return (
<div
id="reading-mode-canvas"
className={`min-h-screen flex flex-col transition-colors duration-250 ${
readingTheme === "dark" ? "bg-[#090B0E] text-slate-200" :
readingTheme === "sepia" ? "bg-[#FAF4EB] text-amber-955" :
"bg-[#FCFBFA] text-slate-900"
}`}
>
<header className={`sticky top-0 z-30 shadow-sm border-b transition-colors duration-200 ${
readingTheme === "dark" ? "bg-[#0F1216] border-slate-800" :
readingTheme === "sepia" ? "bg-[#F3EADF] border-amber-200" :
"bg-white border-slate-200"
}`}>
<div className="max-w-4xl mx-auto px-6 h-14 flex items-center justify-between">
<div className="flex items-center gap-2 animate-pulse">
<span className="text-[10px] font-mono tracking-wider uppercase text-slate-500">
Reading: <span className="font-semibold text-blue-505 font-mono text-xs">{activeDoc.title}</span>
</span>
</div>
<div className="flex items-center gap-3">
<div className="flex items-center gap-1 p-0.5 rounded-lg border border-slate-700/20 bg-black/5 text-[10px]">
<button
type="button"
onClick={() => setReadingTheme("dark")}
className={`px-2 py-1 font-mono rounded transition cursor-pointer ${
readingTheme === "dark" ? "bg-slate-800 text-white font-medium" : "text-slate-500 hover:text-slate-800"
}`}
>
Dark
</button>
<button
type="button"
onClick={() => setReadingTheme("sepia")}
className={`px-2 py-1 font-mono rounded transition cursor-pointer ${
readingTheme === "sepia" ? "bg-amber-800/10 text-amber-800 font-bold" : "text-slate-500 hover:text-slate-800"
}`}
>
Sepia
</button>
<button
type="button"
onClick={() => setReadingTheme("light")}
className={`px-2 py-1 font-mono rounded transition cursor-pointer ${
readingTheme === "light" ? "bg-slate-200 text-slate-900 font-medium" : "text-slate-505 hover:text-slate-800"
}`}
>
Light
</button>
</div>
<div className={`h-6 w-px ${readingTheme === "dark" ? "bg-slate-800" : "bg-slate-200"}`} />
<div className="flex items-center gap-1.5">
<Type className="h-3.5 w-3.5 text-slate-500" />
<div className="flex items-center gap-1">
{(["sm", "base", "lg", "xl"] as const).map((sz) => (
<button
key={sz}
type="button"
onClick={() => setReadingFontSize(sz)}
className={`h-6 w-6 rounded text-[10px] font-mono font-bold transition cursor-pointer flex items-center justify-center uppercase ${
readingFontSize === sz
? "bg-blue-600 text-white shadow-sm"
: readingTheme === "dark" ? "text-slate-500 hover:bg-slate-800" : "text-slate-600 hover:bg-slate-202"
}`}
>
{sz}
</button>
))}
</div>
</div>
<div className={`h-6 w-px ${readingTheme === "dark" ? "bg-slate-800" : "bg-slate-200"}`} />
<div className="flex items-center gap-1 bg-black/5 p-0.5 rounded-lg border border-slate-700/30 text-[10px]">
{(["sans", "serif", "mono"] as const).map((ff) => (
<button
key={ff}
type="button"
onClick={() => setReadingFontFamily(ff)}
className={`px-1.5 py-0.5 rounded text-[9px] font-mono transition cursor-pointer font-bold uppercase ${
readingFontFamily === ff
? "bg-slate-800 text-white shadow-sm font-semibold"
: "text-slate-500 hover:text-slate-800"
}`}
>
{ff}
</button>
))}
</div>
<div className={`h-6 w-px ${readingTheme === "dark" ? "bg-slate-800" : "bg-slate-200"}`} />
<button
type="button"
onClick={() => {
const nextHeight: Record<typeof readingLineHeight, typeof readingLineHeight> = {
normal: "relaxed",
relaxed: "loose",
loose: "normal"
};
setReadingLineHeight(nextHeight[readingLineHeight]);
}}
className={`px-2 py-0.5 rounded text-[10px] font-mono border transition cursor-pointer ${
readingTheme === "dark" ? "border-slate-800 bg-[#15181E] text-slate-400 hover:text-white" : "border-slate-200 bg-slate-100 text-slate-700"
}`}
title="Line Spacing"
>
Spacing: <span className="capitalize">{readingLineHeight}</span>
</button>
<div className={`h-6 w-px ${readingTheme === "dark" ? "bg-slate-800" : "bg-slate-200"}`} />
<button
type="button"
onClick={() => setIsReadingMode(false)}
className="px-2.5 py-1 text-xs bg-red-600 hover:bg-red-500 text-white font-medium rounded transition flex items-center gap-1 cursor-pointer font-sans"
>
<Minimize2 className="h-3.5 w-3.5" /> Close Reader
</button>
</div>
</div>
</header>
<main className="flex-1 w-full max-w-2xl mx-auto px-6 py-12 overflow-y-auto">
<div className="text-left select-text">
<h1 className={`text-4xl font-sans tracking-tight font-extrabold mb-3 ${
readingTheme === "dark" ? "text-white" :
readingTheme === "sepia" ? "text-amber-950" : "text-slate-900"
}`}>
{activeDoc.title.replace(/\.[^/.]+$/, "")}
</h1>
<div className={`mb-8 pb-3 border-b text-[10px] font-mono ${
readingTheme === "dark" ? "border-slate-800 text-slate-500" :
readingTheme === "sepia" ? "border-amber-200 text-amber-800/60" : "border-slate-200 text-slate-400"
}`}>
Last saved {new Date(activeDoc.lastSaved).toLocaleString()} • {charCount} characters • {wordCount} words
</div>
<div className={`prose ${
readingTheme === "dark" ? "prose-invert text-slate-300" :
readingTheme === "sepia" ? "text-amber-900" : "text-slate-800"
} ${
readingFontFamily === "sans" ? "font-sans font-normal" :
readingFontFamily === "serif" ? "font-serif tracking-wide" : "font-mono text-xs tracking-tight"
} ${
readingFontSize === "sm" ? "text-sm" :
readingFontSize === "base" ? "text-base" :
readingFontSize === "lg" ? "text-lg" : "text-xl"
} ${
readingLineHeight === "normal" ? "leading-normal" :
readingLineHeight === "relaxed" ? "leading-relaxed" : "leading-loose"
} markdown-body transition-all duration-150`}>
<ReactMarkdown remarkPlugins={[remarkGfm]}>{activeDoc.content}</ReactMarkdown>
</div>
</div>
</main>
<footer className={`h-12 border-t text-center flex items-center justify-center text-[11px] font-mono ${
readingTheme === "dark" ? "border-slate-900 bg-[#0A0C10] text-slate-600" :
readingTheme === "sepia" ? "border-amber-100 bg-[#F3EADF] text-amber-800/40" : "bg-white border-slate-150 text-slate-500"
}`}>
Press <kbd className="px-1.5 py-0.5 rounded bg-black/5 border border-slate-400/20 text-[10px] mx-1">ESC</kbd> to exit readers view
</footer>
</div>
);
}