/** * Library - David Steinberg's Original Essays and Book Chapters * * A curated collection of investment writings organized by category, * with reading view, download capabilities, and progress tracking. */ import { useState, useEffect, useRef, useMemo } from "react"; import { Button } from "@/components/ui/button"; import { ScrollArea } from "@/components/ui/scroll-area"; import { Input } from "@/components/ui/input"; import { Progress } from "@/components/ui/progress"; import { cn } from "@/lib/utils"; import { ArrowLeft, BookOpen, Download, Search, ChevronRight, Clock, FileText, Bookmark, X, ExternalLink, Volume2, Loader2, CheckCircle2, Circle, Trophy } from "lucide-react"; import { Link } from "wouter"; import { motion, AnimatePresence } from "framer-motion"; import { LazyStreamdown as Streamdown } from "@/components/LazyStreamdown"; import { trpc } from "@/lib/trpc"; import { useAuth } from "@/_core/hooks/useAuth"; import { AudioPlayer, VoiceSelector } from "@/components/AudioPlayer"; import { ElevenLabsListenButton } from "@/components/ElevenLabsListenButton"; import { BookmarkPopup } from "@/components/BookmarkPopup"; import { BookmarksPanel } from "@/components/BookmarksPanel"; import { errorTranslators } from "@/lib/errorMessages"; import { toast } from "sonner"; import { LibraryGridSkeleton } from "@/components/SkeletonLoaders"; import { SEOHead, pageSEO } from "@/components/SEOHead"; import { BreadcrumbJsonLd, breadcrumbs } from "@/components/BreadcrumbJsonLd"; import { PageTransition } from "@/components/PageTransition"; import { Breadcrumb } from "@/components/Breadcrumb"; import { formatReadingTime, getReadingStats } from "@/lib/readingTime"; import { QUERY_CACHE } from "@/lib/queryConfig"; import { EmptyState, ErrorState } from "@/components/EmptyState"; import { ScrollReveal } from "@/components/ScrollReveal"; // Types interface Essay { id: string; title: string; category: string; description: string; readTime: string; content: string; source?: string; } // Categories const categories = [ { id: "all", name: "All Essays", icon: BookOpen }, { id: "philosophy", name: "Investment Philosophy", icon: BookOpen }, { id: "risk", name: "Risk Management", icon: BookOpen }, { id: "psychology", name: "Market Psychology", icon: BookOpen }, { id: "history", name: "Market History", icon: BookOpen }, { id: "process", name: "Investment Process", icon: BookOpen }, { id: "culture", name: "Marlowe Culture", icon: BookOpen }, { id: "operations", name: "Business Operations", icon: BookOpen }, ]; // ─── Scroll Tracker Component ──────────────────────────────────────────────── function LibraryScrollTracker({ scrollRef, essayId, isAuthenticated, onScrollChange, lastSavedRef, updateMutation, }: { scrollRef: React.RefObject; essayId: string; isAuthenticated: boolean; onScrollChange: (percent: number) => void; lastSavedRef: React.MutableRefObject; updateMutation: { mutate: (data: { essayId: string; scrollProgress: number }) => void }; }) { useEffect(() => { const viewport = scrollRef.current?.querySelector('[data-slot="scroll-area-viewport"]') as HTMLElement | null; if (!viewport) return; let saveTimer: ReturnType | null = null; const handleScroll = () => { const { scrollTop, scrollHeight, clientHeight } = viewport; const maxScroll = scrollHeight - clientHeight; if (maxScroll <= 0) return; const percent = Math.round((scrollTop / maxScroll) * 100); onScrollChange(percent); // Debounced save: only save if authenticated and progress increased by 5%+ if (isAuthenticated && percent - lastSavedRef.current >= 5) { if (saveTimer) clearTimeout(saveTimer); saveTimer = setTimeout(() => { lastSavedRef.current = percent; updateMutation.mutate({ essayId, scrollProgress: percent, }); }, 1000); } }; viewport.addEventListener('scroll', handleScroll, { passive: true }); return () => { viewport.removeEventListener('scroll', handleScroll); if (saveTimer) clearTimeout(saveTimer); }; }, [essayId, isAuthenticated]); return null; } // Essays from David Steinberg's books const essays: Essay[] = [ // Investment Philosophy { id: "why-stock-market", title: "Why Do We Have a Stock Market?", category: "philosophy", description: "Understanding the origins and purpose of equity markets, from mercantilism to modern investing.", readTime: "8 min", source: "The Ocean Doesn't Care About Your Swimming Lessons", content: `# Why Do We Have a Stock Market and Where Did It Come From? What is the stock market? This might sound simplistic, but without intuition about the markets and where they came from, it is difficult to have intuition about Marlowe's perspective on investing. My grandfather was a limitless source of common sense. One proverb of my childhood: two young fish are swimming in the ocean when an older fish passes by and says, "enjoy the warm water today." The two younger fish look at each other in confusion and ask, "what the heck is water?" If we are going to swim together, it is worthwhile to know something about the environment that I've spent my adult life studying. The stock market as we imagine it today is young, perhaps 400 to 500 years old. Prior to that, the world operated in a mercantilist system. Commercial opportunities didn't need a stock market, as persistent capital needs weren't around. This changed with the building of the so-called New World, namely the Americas, starting about 500 years ago. ## The Birth of Joint Stock Companies With the discovery of the so-called New World, came new projects on a scale never seen before. With a newfound need for capital, came the invention of companies. At first it was the sovereign who would finance a particular project. When the sovereigns were stretched financially, they turned to the nobility and occasionally to the merchant classes to pay for a new project. This process needed a structure to organize the money, resulting in "joint stock companies," in which the ownership was dispersed among several noble families or those who had wealth. ## From Illiquidity to Speculation There was no financial speculation at this point in time, because investments were illiquid. Participating in a venture in the New World meant risk and lack of knowledge. Once the investment was made—whether a goldmine or a new canal—the way to make money on that investment was by reaping the rewards of profit, by way of dividends. Introducing the ability to dispose of investments opened a door to liquidity, and short-term decision making. And with that, human psychology entered the business of investment. Lowering the barrier to sell made way for human instincts, such as greed, the desire to get rich quickly—to influence the price. ## The Modern Reality The end result was a growing market that mobilized capital from all elements of society. Speculation made the stock market a powerful aggregator of capital, beyond anything seen before. Today, about 80% of the volume is S&P 500. Meanwhile 60 plus percent of the market is passive. The reference benchmark is S&P 500. Thirty-nine percent or so of the market is 10 stocks. Four stocks have determined basically the profitability of the market for the last four years. The structure of our market has changed. It doesn't mean bad, it just means indexed and correlated and different.`, }, { id: "compounding-capital", title: "Compounding Capital: Exponential Growth", category: "philosophy", description: "Why exponential growth is counterintuitive and how to harness it for long-term wealth creation.", readTime: "6 min", source: "The Ocean Doesn't Care About Your Swimming Lessons", content: `# Compounding Capital Means Exponential Growth, and It's Not Intuitive Growing our and our partners' money exponentially is contrary to human psychology. It is contrary to the incentives and structure of financial institutions. It is contrary to the investing norms. The culture of using time to grow capital is so simple that it is a topic of never-ending discussions but seldom observed in terms of the investors' behavior. ## The Power of Time Throughout the office, we have one table framed on most walls. The table has numbers, but it is a qualitative message about our mission over the next 32 years. I am 41 years old and savor the journey to this first goal post. It may be peculiar to think in these time horizons. That said, it's the appropriate lens. Time is our steadfast and dependable ally that supports our goals. Conversely, time tends to work against our investing competition. We savor the passage of time, because it thins out competitors, compounds our capital, and gives us a compounding of knowledge in specific areas. Thereby deepening our edge in those areas. ## Why Most Fail to Compound If there was one thing to understand and repeat about Marlowe it would be time. A long time. The layers of complexity around the casino of the market are perhaps more pernicious, in that outcomes that are in truth random, are misunderstood as being the result of skill. The introduction of equity investing pandered to such base instincts. Behind gambling behavior is a soup of cognitive biases that could be short-hand for believing in luck. These biases are monetized in the Gaming Industry, but perhaps the largest source of profit from similar biases stems from the trading commissions, taxes, and other frictional costs that come from frequent buying and selling. ## The Marlowe Approach My preference is for results to be measured over a five-year time frame. However, whenever there is an opportunity to learn, we will learn. So we may be disappointed with a stock price in the short term, but you can't learn from that. However, suppose management makes an unforced error in a capital allocation decision, or we realize we have fundamentally misunderstood an aspect of the business model. In that case, this is worth much more than a stock price going up or down.`, }, { id: "investing-vs-speculating", title: "Investing vs. Speculating", category: "philosophy", description: "The critical distinction between owning businesses and trading stocks.", readTime: "5 min", source: "The Ocean Doesn't Care About Your Swimming Lessons", content: `# Investing vs. Speculating Do you feel you own a business if you own its stock? I do. This simple question reveals the fundamental divide in how people approach the stock market. The speculator sees stocks as tickets to be traded. The investor sees stocks as ownership stakes in real businesses. ## Self-Awareness is Critical Many ways to make money exist in markets, but be mindful of what you're actually doing. The speculator and the investor can both make money, but they are playing entirely different games with different rules and different odds. Marlowe is an Investor. We are authentically investing for the long term in modestly valued companies run by management teams that, at times, will make decisions whose fruits may not be reflected in the stock price for years. ## The Ownership Mentality When I look around, I think, well, where are the Walmart billionaires aside from the families? Where are the non-family Walmart billionaires? As you dig, you will soon find that Walmart is one of many such publicly traded companies from an early phase. Given this, the public, whether a sophisticated hedge fund or a personal investor investing their retirement savings, has had and continues to have access to some of the most exceptional businesses ever created. And yet, where are the people who benefited? Where are the people who will take those shares and pass them on to the next generation? ## Why This Matters This is why my fund exists. To capture the wealth creation that the investment industry systematically fails to capture, despite having access to the same great businesses as founders and early investors. The near-term results are likely to be as bad as they may be very good. But we are confident, not so much in the prediction of good or bad, but that in the long run they will prove satisfactory.`, }, // Risk Management { id: "how-marlowe-sees-risk", title: "How Marlowe Sees Risk", category: "risk", description: "A different perspective on risk that focuses on permanent loss of capital, not volatility.", readTime: "7 min", source: "The Ocean Doesn't Care About Your Swimming Lessons", content: `# How Marlowe Sees Risk Risk is not volatility. Risk is the permanent loss of capital. This distinction is fundamental to how we operate. The financial industry has convinced most investors that volatility equals risk. This conflation serves the industry well—it justifies constant activity, frequent rebalancing, and the sale of "risk management" products. ## Marlowe's View on Risk is Common Sense A good risk has a narrow asymmetrical outcome. When we take risk, we want the potential upside to far exceed the potential downside. This is not about avoiding risk—it's about taking intelligent risk. Concentration in high-conviction ideas reduces risk when you truly understand the business. The stapler analogy: if you had to bet your life on a stapler working, would you rather have one high-quality stapler you've tested thoroughly, or ten random staplers you've never used? ## The Manager's Motivation, Tenure, and Commitment We look at the manager's motivation, tenure, and commitment to the business. Are they invested alongside us? Have they been through difficult periods? Do they have skin in the game? ## Useful Worrying We engage in what we call "useful worrying." This means constantly asking: what could go wrong? What are we missing? What would make us sell? ## Learning from Risk Every investment teaches us something about risk. We document our mistakes and update our process. The goal is not to avoid all mistakes—that's impossible—but to learn from each one and avoid repeating it.`, }, { id: "concentration-vs-diversification", title: "Concentration vs. Diversification", category: "risk", description: "Why concentrated portfolios can be less risky than diversified ones when you truly understand your investments.", readTime: "6 min", source: "The Ocean Doesn't Care About Your Swimming Lessons", content: `# Concentration vs. Diversification Diversification is protection against ignorance. It makes little sense if you know what you are doing. This is a controversial statement in modern finance, but we believe it deeply. The conventional wisdom that diversification always reduces risk is simply wrong when applied to investors who truly understand their holdings. ## The Case for Concentration If you have a genuine edge—deep knowledge of a business, its industry, and its management—why would you dilute that edge by spreading your capital across dozens of positions you understand less well? Our best ideas deserve our most capital. Our tenth-best idea should not receive the same allocation as our best idea. ## When Diversification Makes Sense Diversification makes sense when you're uncertain. If you're indexing or don't have the time or skill to analyze individual businesses, broad diversification is prudent. But for professional investors who dedicate their lives to understanding businesses, excessive diversification is a crutch that masks a lack of conviction. ## The Marlowe Approach We typically hold 10-15 positions. Each position represents a business we understand deeply. We know the management, the competitive dynamics, the financial structure, and the key risks. This concentration allows us to monitor our investments closely and react quickly when circumstances change.`, }, // Market Psychology { id: "market-psychology", title: "Understanding Market Psychology", category: "psychology", description: "How human emotions drive market behavior and create opportunities for rational investors.", readTime: "8 min", source: "The Ocean Doesn't Care About Your Swimming Lessons", content: `# Understanding Market Psychology The market is not a weighing machine in the short term. It's a voting machine, driven by emotion, narrative, and momentum. Understanding this distinction is crucial for long-term investors. In the short term, prices are determined by psychology. In the long term, they're determined by fundamentals. ## Fear and Greed The two dominant emotions in markets are fear and greed. They alternate in cycles, creating booms and busts. The wise investor learns to recognize these cycles and act contrary to the crowd. When others are greedy, be fearful. When others are fearful, be greedy. This simple advice is extraordinarily difficult to follow in practice. ## The Narrative Machine Markets love narratives. A compelling story can drive prices far beyond fundamental value. The dot-com bubble was driven by the narrative of the "new economy." The housing bubble was driven by the narrative that "housing prices never fall." We try to separate narrative from reality. What are the actual cash flows? What is the business actually worth? ## Patience as an Edge Most market participants have short time horizons. Quarterly earnings, annual performance reviews, and career risk all push investors toward short-term thinking. Our edge is patience. We can wait for the market to recognize value that others are too impatient to see.`, }, { id: "behavioral-biases", title: "Behavioral Biases in Investing", category: "psychology", description: "The cognitive traps that cause investors to make systematic errors.", readTime: "7 min", source: "The Ocean Doesn't Care About Your Swimming Lessons", content: `# Behavioral Biases in Investing We are not rational beings. We are rationalizing beings. Our brains are wired with shortcuts that served us well on the savanna but lead us astray in financial markets. ## Confirmation Bias We seek information that confirms our existing beliefs and ignore information that contradicts them. This is perhaps the most dangerous bias for investors. The antidote is to actively seek disconfirming evidence. When we make an investment, we assign someone to argue the bear case. ## Loss Aversion We feel losses about twice as strongly as equivalent gains. This leads us to hold losers too long (hoping to break even) and sell winners too soon (locking in gains). The solution is to focus on expected value, not on whether a position is currently profitable. ## Recency Bias We overweight recent events and underweight historical base rates. After a market crash, we expect more crashes. After a boom, we expect more boom. The solution is to study history and remember that cycles repeat. ## Overconfidence We systematically overestimate our abilities and the precision of our forecasts. This leads to excessive trading and inadequate diversification. The solution is humility. Acknowledge uncertainty. Use ranges, not point estimates.`, }, // Market History { id: "lessons-from-history", title: "Lessons from Market History", category: "history", description: "What past market cycles teach us about human nature and investing.", readTime: "9 min", source: "The Ocean Doesn't Care About Your Swimming Lessons", content: `# Lessons from Market History Those who cannot remember the past are condemned to repeat it. This is especially true in financial markets, where the same patterns recur generation after generation. ## The Persistence of Bubbles Bubbles are not anomalies. They are a regular feature of financial markets. From the Dutch tulip mania to the dot-com bubble to the housing crisis, the pattern is remarkably consistent. Prices rise far above fundamental value, driven by speculation and easy credit. Eventually, reality intrudes, and prices collapse. The cycle then repeats. ## The Importance of Survival The first rule of compounding is to never interrupt it unnecessarily. Many investors who were "right" about bubbles still lost money because they were early or used leverage. Survival is more important than maximizing returns. A 50% loss requires a 100% gain to recover. ## Mean Reversion Extreme valuations tend to revert to historical averages. High P/E ratios predict low future returns. Low P/E ratios predict high future returns. This doesn't help with timing, but it helps with expectations. When valuations are extreme, adjust your return expectations accordingly. ## The Power of Compounding The most important lesson from history is the power of compounding. Patient investors who stayed the course through multiple cycles accumulated enormous wealth. The key is to survive the downturns and remain invested for the long term.`, }, // Investment Process { id: "research-process", title: "The Marlowe Keynes Process", category: "process", description: "How we analyze businesses and make investment decisions.", readTime: "10 min", source: "The Ocean Doesn't Care About Your Swimming Lessons", content: `# The Marlowe Keynes Process Our research process is designed to understand businesses deeply before investing. We are not looking for quick trades or momentum plays. We are looking for businesses we can own for years. ## Step 1: Idea Generation Ideas come from many sources: industry contacts, reading, screens, and serendipity. We cast a wide net but are highly selective about what we pursue. ## Step 2: Initial Assessment Before diving deep, we ask: Is this business within our circle of competence? Is it the type of business that can compound value over time? Is the valuation potentially attractive? If the answer to any of these is no, we move on. ## Step 3: Deep Research For ideas that pass initial screening, we conduct extensive research. We read every public filing. We talk to customers, competitors, and industry experts. We build detailed financial models. This process typically takes months, not days. ## Step 4: Investment Decision After research is complete, we make a decision. We require high conviction to invest. If we're uncertain, we pass. ## Step 5: Position Sizing Position size reflects conviction and risk. Our highest-conviction ideas receive the most capital. We never bet the farm on any single idea. ## Step 6: Ongoing Monitoring Research doesn't stop after we invest. We continuously monitor our holdings and update our views as new information emerges.`, }, { id: "valuation-framework", title: "Our Valuation Framework", category: "process", description: "How we think about what a business is worth.", readTime: "8 min", source: "The Ocean Doesn't Care About Your Swimming Lessons", content: `# Our Valuation Framework Valuation is both art and science. The science is the math—discounted cash flows, comparable analysis, and sum-of-the-parts. The art is the judgment—what assumptions to use, what risks to weight, what the future might hold. ## Intrinsic Value We define intrinsic value as the present value of all future cash flows. This is conceptually simple but practically difficult. It requires forecasting cash flows far into the future and choosing an appropriate discount rate. ## Margin of Safety We never pay intrinsic value. We require a margin of safety—a discount to our estimate of intrinsic value. This margin protects us from errors in our analysis and unforeseen events. ## Multiple Scenarios We don't rely on a single forecast. We model multiple scenarios: base case, bull case, and bear case. We weight these scenarios by probability and look at the expected value. ## Qualitative Factors Numbers don't tell the whole story. We also consider qualitative factors: management quality, competitive position, industry dynamics, and regulatory environment. ## Valuation is Not Timing Knowing that something is undervalued doesn't tell you when it will be recognized. We may be early. We may be wrong. But over time, value tends to be recognized.`, }, // Marlowe Culture { id: "marlowe-culture", title: "The Marlowe Culture", category: "culture", description: "The values and principles that guide how we work.", readTime: "7 min", source: "The Ocean Doesn't Care About Your Swimming Lessons", content: `# The Marlowe Culture Culture is not what you say. It's what you do. At Marlowe, we try to live our values every day. ## Intellectual Honesty We prize intellectual honesty above all else. We want to know the truth, even when it's uncomfortable. We encourage dissent and debate. We change our minds when the evidence warrants. ## Long-Term Thinking We think in years and decades, not quarters. This affects everything from how we invest to how we compensate our team. ## Continuous Learning We are students of business and markets. We read voraciously. We seek out experts. We never stop learning. ## Humility We know we will be wrong. We don't know when or how, but we know it will happen. This humility keeps us cautious and open-minded. ## Partnership We treat our investors as partners, not customers. Their interests are our interests. We eat our own cooking. ## Simplicity We favor simple over complex. Simple strategies are easier to understand, easier to execute, and easier to stick with during difficult times.`, }, { id: "finding-joy", title: "Finding Joy in the Challenge", category: "culture", description: "Why we love what we do and how that passion drives results.", readTime: "6 min", source: "The Ocean Doesn't Care About Your Swimming Lessons", content: `# Finding Joy in the Challenge The most rewarding investments are often the most difficult. If it were easy, everyone would do it, and there would be no excess returns. ## Don't Confuse Obedience with Intelligence Following the crowd is easy. Thinking independently is hard. We value independent thinking over credentials or conformity. ## Mandate Requires Obsessive Curiosity Our mandate requires obsessive curiosity and relentlessness. We are not satisfied with surface-level understanding. We dig until we truly understand. ## Work Culture Hard work never killed anyone. We embrace the difficulty because we know it's the source of our edge. The mediocre are always at their best—we strive to continuously improve. ## Managing Fear as a Business Owner Fear is natural. The question is how we channel it. Useful fear drives preparation and caution. Destructive fear drives paralysis and panic selling. ## Day-to-Day Routine Consistency matters. We show up every day, do the work, and trust the process. The compound effect of daily effort is enormous over time. ## Unified Vision and the Power of Belief Everyone at Marlowe shares the same vision. This alignment creates power that fragmented organizations cannot match. ## Patience Patience is perhaps our greatest competitive advantage. In a world of instant gratification, the ability to wait is rare and valuable.`, }, { id: "capital-allocation", title: "Capital Allocation Decisions", category: "culture", description: "How management teams should think about deploying capital.", readTime: "6 min", source: "The Ocean Doesn't Care About Your Swimming Lessons", content: `# Capital Allocation Decisions Capital allocation is the most important job of a CEO. How a company deploys its capital determines its long-term value creation. ## The CEO's Role The CEO must be a capital allocator first and an operator second. Many excellent operators are poor capital allocators, destroying value through ill-conceived acquisitions or poorly timed buybacks. ## The CFO's Role The CFO must be the guardian of capital discipline. They must push back against empire-building and ensure every dollar is deployed for maximum return. ## Compensation Schemes and Incentives Incentives drive behavior. We look for management teams whose compensation is aligned with long-term shareholder value, not short-term stock price or accounting metrics. ## Buybacks and Dividends Buybacks are good when stock is undervalued and bad when stock is overvalued. Most companies get this backwards, buying back stock when times are good (and prices high) and stopping when times are bad (and prices low). Dividends signal discipline. A company that commits to a dividend must maintain capital discipline to fund it. ## Capital Allocation and Human Progress Good capital allocation drives human progress. Capital flows to its highest and best use, funding innovation and growth. Poor capital allocation wastes resources and slows progress. ## Business Incentives We study incentives carefully. What gets measured gets managed. What gets rewarded gets done. Understanding incentives helps us predict behavior.`, }, ]; export default function Library() { const { user, isAuthenticated } = useAuth(); const [selectedCategory, setSelectedCategory] = useState("all"); const [searchQuery, setSearchQuery] = useState(""); const [selectedEssay, setSelectedEssay] = useState(null); const [allEssays, setAllEssays] = useState(essays); const readingStartTime = useRef(null); // Load new essays from JSON file useEffect(() => { fetch('/essays/new-essays.json') .then(res => res.json()) .then((data: Essay[]) => { setAllEssays([...essays, ...data]); }) .catch(err => console.error('Failed to load new essays:', err)); }, []); // Reading progress queries const { data: progressData, refetch: refetchProgress } = trpc.essayProgress.getAll.useQuery( undefined, { ...QUERY_CACHE.STATIC, enabled: isAuthenticated }); const { data: statsData } = trpc.essayProgress.getStats.useQuery( undefined, { ...QUERY_CACHE.STATIC, enabled: isAuthenticated }); // Reading progress mutations const markReadMutation = trpc.essayProgress.markRead.useMutation({ onSuccess: () => { refetchProgress(); toast.success("Essay marked as read!"); }, }); const updateReadTimeMutation = trpc.essayProgress.updateReadTime.useMutation(); // Get set of read essay IDs const readEssayIds = new Set( progressData?.filter(p => p.isCompleted).map(p => p.essayId) || [] ); // Build scroll progress map for essay cards const essayProgressMap = useMemo(() => { const map = new Map(); if (progressData) { for (const p of progressData) { map.set(p.essayId, { scrollProgress: (p as any).scrollProgress || 0, isCompleted: p.isCompleted || false, }); } } return map; }, [progressData]); // Calculate reading progress percentage const totalEssays = allEssays.length; const readCount = statsData?.totalRead || 0; const progressPercent = totalEssays > 0 ? Math.round((readCount / totalEssays) * 100) : 0; // Scroll tracking for essay reading view const scrollRef = useRef(null); const [scrollPercent, setScrollPercent] = useState(0); const lastSavedScrollProgress = useRef(0); const updateScrollProgressMutation = trpc.essayProgress.updateScrollProgress.useMutation({ onSuccess: () => refetchProgress(), }); // TTS state const [audioData, setAudioData] = useState(null); const [audioUrl, setAudioUrl] = useState(null); const [audioFormat, setAudioFormat] = useState('mp3'); const [isGeneratingAudio, setIsGeneratingAudio] = useState(false); const [selectedVoiceId, setSelectedVoiceId] = useState(() => { if (typeof window !== 'undefined') { return localStorage.getItem('speechify-preferred-voice') || 'george'; } return 'george'; }); // Fetch available voices const { data: voices = [] } = trpc.speechify.getVoices.useQuery(undefined, QUERY_CACHE.SLOW_MOVING); // TTS mutation const generateSpeech = trpc.speechify.generateLongSpeech.useMutation({ onSuccess: (data) => { // Prefer URL (S3 cached) over base64 data setAudioUrl(data.audioUrl || null); setAudioData(data.audioData || null); setAudioFormat(data.audioFormat); setIsGeneratingAudio(false); }, onError: (error) => { const translated = errorTranslators.audio(error.message); toast.error(translated.message, { description: translated.description, }); setIsGeneratingAudio(false); }, }); // Bookmarks state const [showBookmarks, setShowBookmarks] = useState(false); const contentRef = useRef(null); const bookmarkUtils = trpc.useUtils(); const { data: bookmarks = [], isLoading: bookmarksLoading } = trpc.essayBookmarks.getAll.useQuery( { essayId: selectedEssay?.id || '' }, { ...QUERY_CACHE.STATIC, enabled: isAuthenticated && !!selectedEssay }); const addBookmarkMutation = trpc.essayBookmarks.add.useMutation({ onSuccess: () => { bookmarkUtils.essayBookmarks.getAll.invalidate({ essayId: selectedEssay?.id || '' }); toast.success('Bookmark saved!'); }, onError: () => toast.error('Failed to save bookmark'), }); const removeBookmarkMutation = trpc.essayBookmarks.remove.useMutation({ onSuccess: () => { bookmarkUtils.essayBookmarks.getAll.invalidate({ essayId: selectedEssay?.id || '' }); toast.success('Bookmark removed'); }, onError: () => toast.error('Failed to remove bookmark'), }); const updateBookmarkMutation = trpc.essayBookmarks.update.useMutation({ onSuccess: () => { bookmarkUtils.essayBookmarks.getAll.invalidate({ essayId: selectedEssay?.id || '' }); toast.success('Bookmark updated'); }, onError: () => toast.error('Failed to update bookmark'), }); // Handle listen button click const handleListen = () => { if (!selectedEssay) return; if (audioData || audioUrl) { // Already have audio, just toggle player visibility return; } setIsGeneratingAudio(true); // Strip markdown formatting for cleaner audio const cleanText = selectedEssay.content .replace(/^#+\s*/gm, '') // Remove headers .replace(/\*\*/g, '') // Remove bold .replace(/\*/g, '') // Remove italic .replace(/\[([^\]]+)\]\([^)]+\)/g, '$1') // Remove links, keep text .trim(); generateSpeech.mutate({ text: cleanText, voiceId: selectedVoiceId }); }; // Handle voice change const handleVoiceChange = (voiceId: string) => { setSelectedVoiceId(voiceId); if (typeof window !== 'undefined') { localStorage.setItem('speechify-preferred-voice', voiceId); } // Clear cached audio when voice changes setAudioData(null); setAudioUrl(null); }; // Reset audio and track reading time when essay changes const handleSelectEssay = (essay: Essay | null) => { // Save reading time for previous essay if (selectedEssay && readingStartTime.current && isAuthenticated) { const readingTime = Math.floor((Date.now() - readingStartTime.current) / 1000); if (readingTime > 5) { // Only track if read for more than 5 seconds updateReadTimeMutation.mutate({ essayId: selectedEssay.id, additionalSeconds: readingTime, }); } } // Set new essay and start timer setSelectedEssay(essay); readingStartTime.current = essay ? Date.now() : null; // Reset audio state setAudioData(null); setAudioUrl(null); setAudioFormat('mp3'); setIsGeneratingAudio(false); }; // Mark essay as read const handleMarkAsRead = () => { if (!selectedEssay || !isAuthenticated) return; const readingTime = readingStartTime.current ? Math.floor((Date.now() - readingStartTime.current) / 1000) : 0; markReadMutation.mutate({ essayId: selectedEssay.id, readTimeSeconds: readingTime, }); }; // Filter essays const filteredEssays = allEssays.filter(essay => { const matchesCategory = selectedCategory === "all" || essay.category === selectedCategory; const matchesSearch = essay.title.toLowerCase().includes(searchQuery.toLowerCase()) || essay.description.toLowerCase().includes(searchQuery.toLowerCase()); return matchesCategory && matchesSearch; }); // Download essay as text const handleDownload = (essay: Essay) => { const content = `${essay.title}\n\nBy David Steinberg\nFrom: ${essay.source || "Marlowe Keynes"}\n\n${essay.content.replace(/^#+ /gm, '').replace(/\*\*/g, '')}`; const blob = new Blob([content], { type: "text/plain" }); const url = URL.createObjectURL(blob); const a = document.createElement("a"); a.href = url; a.download = `${essay.id}.txt`; a.click(); URL.revokeObjectURL(url); }; // Dynamic SEO: update OG tags when viewing a specific essay SEOHead( selectedEssay ? { title: `${selectedEssay.title} | Marlowe Keynes Learn`, description: selectedEssay.content ? selectedEssay.content.replace(/[#*_\[\]()>]/g, '').slice(0, 155).trim() + '...' : pageSEO.library.description, image: pageSEO.library.image, type: 'article' as const, } : pageSEO.library ); const currentBreadcrumbs = selectedEssay ? breadcrumbs.libraryEssay(selectedEssay.title, selectedEssay.id) : breadcrumbs.library; return (
{/* Header */}

Library

Original Essays by David Steinberg

{/* Reading Progress Stats */} {isAuthenticated && statsData && (
{readCount} / {totalEssays} read
{progressPercent}%
)}
{/* Breadcrumb Navigation */}
{/* Reading View */} {selectedEssay && ( {/* Reading progress bar at the very top */}
{/* Reading Header */}
{/* Scroll progress indicator */} {scrollPercent}% {/* Mark as Read Button */} {isAuthenticated && ( )} {/* Bookmarks Button */} {isAuthenticated && ( )} {/* Voice Selector */} {voices.length > 0 && ( )} {isAuthenticated && selectedEssay && ( )}
{/* Reading Content */}

{categories.find(c => c.id === selectedEssay.category)?.name}

{readEssayIds.has(selectedEssay.id) && ( Read )}

{selectedEssay.title}

{formatReadingTime(selectedEssay.content)} {getReadingStats(selectedEssay.content).wordCount.toLocaleString()} words {selectedEssay.source && ( {selectedEssay.source} )}
{/* Audio Player - appears when audio is generated */} {(audioData || audioUrl) && (
)}
{selectedEssay.content}

From the writings of David Steinberg, Founder & Chief Investment Officer of Marlowe Partners.

{isAuthenticated && !readEssayIds.has(selectedEssay.id) && ( )}
{/* Bookmark text selection popup */} {isAuthenticated && contentRef.current && ( { addBookmarkMutation.mutate({ essayId: selectedEssay.id, ...data, }); }} /> )} {/* Bookmarks panel */} setShowBookmarks(false)} bookmarks={bookmarks} isLoading={bookmarksLoading} essayTitle={selectedEssay.title} onDelete={(bookmarkId) => removeBookmarkMutation.mutate({ bookmarkId })} onUpdate={(bookmarkId, data) => updateBookmarkMutation.mutate({ bookmarkId, ...data })} isDeleting={removeBookmarkMutation.isPending} isUpdating={updateBookmarkMutation.isPending} />
)}
{/* Main Content */} {!selectedEssay && ( <> {/* Reading Progress Card (Mobile) */} {isAuthenticated && statsData && (
Reading Progress
{progressPercent}%

{readCount} of {totalEssays} essays completed

)} {/* Continue Reading Section */} {isAuthenticated && (() => { const inProgressEssays = allEssays.filter(essay => { const progress = essayProgressMap.get(essay.id); return progress && progress.scrollProgress > 0 && !progress.isCompleted; }).sort((a, b) => { const pA = essayProgressMap.get(a.id)?.scrollProgress || 0; const pB = essayProgressMap.get(b.id)?.scrollProgress || 0; return pB - pA; }).slice(0, 4); if (inProgressEssays.length === 0) return null; return (

Continue Reading

{inProgressEssays.length} in progress
{inProgressEssays?.length === 0 && } {inProgressEssays.map((essay) => { const scrollPct = essayProgressMap.get(essay.id)?.scrollProgress || 0; return ( handleSelectEssay(essay)} >

{categories.find(c => c.id === essay.category)?.name || essay.category}

{scrollPct}% read

{essay.title}

{formatReadingTime(essay.content)}
); })}
); })()} {/* Search and Filters */}
setSearchQuery(e.target.value)} className="pl-10" />
{/* Category Tabs */}
{categories.map((category) => ( ))}
{/* Essays Grid */}
{filteredEssays.map((essay) => { const progress = essayProgressMap.get(essay.id); const scrollPct = progress?.scrollProgress || 0; const isRead = readEssayIds.has(essay.id); return ( handleSelectEssay(essay)} > {/* Read Badge */} {isRead && (
Done
)}
{categories.find(c => c.id === essay.category)?.name} {formatReadingTime(essay.content)} · {getReadingStats(essay.content).wordCount.toLocaleString()} words

{essay.title}

{essay.description}

{essay.source}
{scrollPct > 0 && !isRead && ( {scrollPct}% )}
{/* Progress bar at bottom of card */} {scrollPct > 0 && (
)} ); })}
{filteredEssays.length === 0 && (

No essays match your filters

Try a different search term, or click a category above to browse by topic.

)} )}
); }