diff --git a/apps/editor/src/__tests__/examples.test.ts b/apps/editor/src/__tests__/examples.test.ts new file mode 100644 index 0000000..1cde38e --- /dev/null +++ b/apps/editor/src/__tests__/examples.test.ts @@ -0,0 +1,107 @@ +import { describe, it, expect } from 'vitest' +import { renderMarkdown, validate } from 'mindmaply-core' +import { EXAMPLES, exampleVideoUrl, previewSource } from '../examples/manifest' + +// These four maps ship on the landing page, so a bad entry is a visibly broken +// card in production. The sources are committed, so all of this is checkable +// offline: nothing here touches the network. + +describe('EXAMPLES', () => { + it('is non-empty', () => { + expect(EXAMPLES.length).toBeGreaterThan(0) + }) + + it('has unique slugs and share ids', () => { + expect(new Set(EXAMPLES.map((e) => e.slug)).size).toBe(EXAMPLES.length) + expect(new Set(EXAMPLES.map((e) => e.shareId)).size).toBe(EXAMPLES.length) + }) + + it('has url-safe slugs', () => { + for (const e of EXAMPLES) expect(e.slug).toMatch(/^[a-z0-9]+(-[a-z0-9]+)*$/) + }) + + it('has share ids the api worker will treat as short ids', () => { + // Mirrors SHORT_ID_RE in api/src/shortlinks.ts. A share id outside this + // shape is read as a full payload and the link 302s to the homepage. + for (const e of EXAMPLES) expect(e.shareId).toMatch(/^[A-Za-z0-9_-]{8,16}$/) + }) + + it('has 11-character youtube video ids', () => { + for (const e of EXAMPLES) expect(e.videoId).toMatch(/^[A-Za-z0-9_-]{11}$/) + }) + + it('has a title and a blurb', () => { + for (const e of EXAMPLES) { + expect(e.title.length).toBeGreaterThan(0) + expect(e.blurb.length).toBeGreaterThan(0) + } + }) + + // The model emits curly quotes, en dashes and non-breaking hyphens. They + // render, but they are miserable to hand-edit, so sources are normalised on + // the way in and stay that way. + it('has ascii-only sources', () => { + for (const e of EXAMPLES) { + const offenders = [...new Set([...e.source].filter((c) => c.charCodeAt(0) > 127))] + expect(offenders, `${e.slug} has non-ascii: ${offenders.join(' ')}`).toEqual([]) + } + }) +}) + +describe('example sources', () => { + it('every source is valid markdown for the engine', () => { + for (const e of EXAMPLES) { + const result = validate(e.source, 'markdown') + expect(result.valid, `${e.slug}: ${JSON.stringify(result.errors)}`).toBe(true) + } + }) + + it('every source renders', () => { + for (const e of EXAMPLES) { + expect(renderMarkdown(e.source, { direction: 'LR' })).toContain(' { + it('keeps the heading and drops everything below depth 1', () => { + const trimmed = previewSource(['# Root', '- One', ' - Deep', '- Two', ' - Deeper'].join('\n')) + expect(trimmed).toBe(['# Root', '- One', '- Two'].join('\n')) + }) + + it('leaves every example a renderable map', () => { + for (const e of EXAMPLES) { + const preview = previewSource(e.source) + expect(validate(preview, 'markdown').valid, e.slug).toBe(true) + expect(renderMarkdown(preview, { direction: 'LR' })).toContain(' { + for (const e of EXAMPLES) { + const svg = renderMarkdown(previewSource(e.source), { direction: 'LR' }) + const vb = svg.match(/viewBox="[^"]*?([\d.]+) ([\d.]+)"/) + expect(vb, e.slug).not.toBeNull() + const aspect = Number(vb![1]) / Number(vb![2]) + expect(aspect, `${e.slug} aspect ${aspect.toFixed(2)}`).toBeGreaterThan(0.7) + } + }) + + it('trims each example to a handful of branches', () => { + for (const e of EXAMPLES) { + const lines = previewSource(e.source).split('\n').length + expect(lines, e.slug).toBeGreaterThan(2) + expect(lines, e.slug).toBeLessThanOrEqual(12) + } + }) +}) + +describe('exampleVideoUrl', () => { + it('points at the source talk', () => { + expect(exampleVideoUrl(EXAMPLES[0])).toBe( + `https://www.youtube.com/watch?v=${EXAMPLES[0].videoId}`, + ) + }) +}) diff --git a/apps/editor/src/editor.css b/apps/editor/src/editor.css index 2754ade..3dac157 100644 --- a/apps/editor/src/editor.css +++ b/apps/editor/src/editor.css @@ -638,6 +638,63 @@ body { } .landing-yt .landing-cta { margin-top:28px; } +/* Real generated maps, as proof the YouTube flow above actually works. Sits + directly after it on purpose. */ +.landing-examples { + max-width:1000px; margin:0 auto 80px; padding:0 24px; text-align:center; +} +.landing-examples h2 { font-size:24px; font-weight:800; letter-spacing:-.4px; margin-bottom:12px; } +.landing-examples-intro { + color:var(--tx2); font-size:16px; line-height:1.7; + max-width:640px; margin:0 auto 28px; +} +.landing-examples-grid { + list-style:none; padding:0; margin:0; + display:grid; grid-template-columns:1fr 1fr; gap:22px; +} +.landing-examples-grid > li { display:flex; flex-direction:column; min-width:0; } +/* flex:1 so cards in a row match height despite blurbs wrapping to two or three + lines, which otherwise leaves the "Watch the talk" links on ragged baselines. */ +.landing-examples-card { + flex:1; text-align:left; text-decoration:none; color:inherit; + background:var(--white); border:1px solid var(--bd2); border-radius:16px; + padding:16px 18px 18px; box-shadow:0 12px 40px rgba(0,0,0,.08); + transition:transform .15s ease, box-shadow .15s ease; +} +a.landing-examples-card:hover { + transform:translateY(-2px); box-shadow:0 18px 50px rgba(0,0,0,.12); +} +/* Fixed height so cards line up despite previews ranging from square to 1.7:1; + the SVG scales inside rather than stretching. */ +.landing-examples-canvas { + position:relative; height:190px; margin-bottom:14px; + display:flex; align-items:center; justify-content:center; + border-radius:10px; overflow:hidden; +} +.landing-examples-canvas::before { + content:''; position:absolute; inset:0; pointer-events:none; + background-image:radial-gradient(circle, rgba(0,0,0,.048) 1px, transparent 1px); + background-size:20px 20px; +} +.landing-examples-canvas .landing-demo-svg { width:auto; max-width:100%; } +.landing-examples-canvas .landing-demo-svg svg { + width:auto; max-width:100%; height:auto; max-height:190px; +} +.landing-examples-card h3 { + font-size:15px; font-weight:700; margin:0 0 6px; line-height:1.4; +} +.landing-examples-card p { + color:var(--tx2); font-size:13.5px; line-height:1.6; margin:0; +} +.landing-examples-src { + align-self:flex-start; margin-top:10px; + color:var(--tx3); font-size:13px; text-decoration:none; +} +.landing-examples-src:hover { color:var(--brand); } +.landing-examples-caption { + color:var(--tx3); font-size:13px; margin-top:22px; +} + .landing-agents { max-width:780px; margin:0 auto 80px; padding:0 24px; text-align:center; } @@ -736,9 +793,15 @@ body { .landing-trio-grid { grid-template-columns:1fr; } .landing-anywhere-grid { grid-template-columns:1fr; } .landing-anywhere-shots { grid-template-columns:1fr; } + /* One card per row. The previews are trimmed to depth 1 (see previewSource), + so they stay legible at phone width and this section does NOT need the + sideways scroll .landing-roadmap-canvas falls back to. */ + .landing-examples-grid { grid-template-columns:1fr; } + .landing-examples-canvas { height:170px; } + .landing-examples-canvas .landing-demo-svg svg { max-height:170px; } /* 24px gutters cost the copy a word per line on a 360px phone. */ .landing-trio, .landing-anywhere, .landing-yt, .landing-agents, - .landing-oss, .landing-roadmap { padding:0 16px; } + .landing-oss, .landing-roadmap, .landing-examples { padding:0 16px; } /* The install command is one unbreakable token pair: size it to the gutter it has left rather than leaving it to scroll a few characters. */ .landing-agents-cmd { padding:12px; } diff --git a/apps/editor/src/examples/hans-rosling-best-stats.mmd b/apps/editor/src/examples/hans-rosling-best-stats.mmd new file mode 100644 index 0000000..00c6d84 --- /dev/null +++ b/apps/editor/src/examples/hans-rosling-best-stats.mmd @@ -0,0 +1,66 @@ +# The best stats you've ever seen | Hans Rosling +- Teaching global health at Karolinska Institute + - Pre-test of Swedish undergraduates + - Question on child mortality by country + - Result: students scored poorly, showing knowledge gap + - Realization of students' misconceptions + - "Western world" vs "third world" view + - Preconceived ideas versus actual data +- Development of bubble visualization tool + - Bubbles represent countries + - Size = population + - X-axis = fertility rate (children per woman) + - Y-axis = life expectancy at birth + - Data source: UN statistics since 1960 + - Early pattern (1962) + - Industrialized: low fertility, high life expectancy + - Developing: high fertility, low life expectancy +- Observing change over decades + - 1960s-1980s shifts + - China moving toward lower fertility, higher life expectancy + - Latin America and Arabic countries showing similar trends + - Africa remaining with high fertility, low life expectancy + - Bangladesh's rapid transition after family-planning promotion + - 1990s HIV epidemic lowering African life expectancy + - Comparison United States vs Vietnam + - 1964: US low fertility, high life expectancy; Vietnam opposite + - Post-war Vietnam catches up to US levels by 2003 +- Income distribution visualization + - World income per day ($1, $10, $100) + - No clear gap between rich and poor + - Rich 20% own ~74% of global income + - Poor 20% own ~2% + - Middle 60% own ~24% + - Regional breakdowns + - Africa: 10% of world population, most impoverished + - OECD, Latin America, East Europe, East Asia, South Asia + - Historical change + - 1970: Asian poverty dominant + - Today: large middle-income population, fewer in absolute poverty +- Linking income, health, and child survival + - Adding child-survival axis to income bubbles + - Strong linear relationship between money and child survival + - Regional gaps (OECD, Latin America, East Europe, etc.) + - Sub-Saharan Africa split: low health, low income + - Country-level examples + - Sierra Leone, Uganda, Ghana - wide internal variation + - South Asia: India vs Afghanistan vs Sri Lanka + - Arab states: UAE vs Yemen - similar wealth, different health outcomes +- Gapminder and data accessibility + - Creation of non-profit "Linking Data to Design" (Gapminder) + - Software that turns raw UN data into interactive graphics + - Challenges + - Data hidden in databases, passwords, formats + - Need for searchable, open, visual tools + - Cooperation with UN statistical head +- Future projections and patterns + - Income distribution of China vs United States (1970) + - China moving upward, overlapping US + - Internet usage per 1,000 people + - Rapid increase, linked to GDP per capita + - Implications for "$100 computer" and global flattening + - Emphasis on hypothesis-generating visualisation versus traditional statistical analysis +- Conclusions + - World moving toward a large middle-income, middle-life-expectancy, medium-fertility zone + - Health improvements can precede wealth + - Open data plus design tools empower students, policymakers, entrepreneurs diff --git a/apps/editor/src/examples/manifest.ts b/apps/editor/src/examples/manifest.ts new file mode 100644 index 0000000..00135e3 --- /dev/null +++ b/apps/editor/src/examples/manifest.ts @@ -0,0 +1,116 @@ +// Curated, real mind maps to show on the landing page. +// +// Every one of these was generated by the live YouTube flow +// (POST /yt/transform on the api worker), then hand-picked. The full source is +// committed next to this file rather than fetched from the API at render time, +// for three reasons: the landing page must not depend on the API being up to +// draw itself, `robots.txt` disallows /svg so a crawler would never see an +// API-served thumbnail, and a source in the repo is reviewable in a PR before +// it goes public. +// +// `shareId` is the 11-char content hash under which the api worker stored the +// same payload, so the card can link to the canonical /s/ page. It is NOT +// how the preview is drawn. +// +// Adding one: generate a map, save its source as `.mmd` here (ASCII only +// -- the model emits curly quotes and non-breaking hyphens, normalise them), +// then add an entry below. `pnpm --filter @mindmaply/editor test` checks that +// every source parses and that ids and slugs are well formed. + +import jobsSource from './steve-jobs-stanford-2005.mmd?raw' +import roslingSource from './hans-rosling-best-stats.mmd?raw' +import mcravenSource from './mcraven-make-your-bed.mmd?raw' +import pauschSource from './randy-pausch-last-lecture.mmd?raw' +import { SHARE_BASE } from '../share' + +export interface Example { + /** URL-safe id. Reserved for the prerendered /examples// pages. */ + slug: string + /** Human title. Shorter than the YouTube title, which is often unwieldy. */ + title: string + /** One sentence. Doubles as the meta description on the per-example page. */ + blurb: string + /** 11-char content-hash id stored by the api worker (see /shorten). */ + shareId: string + /** Source video, credited on the card. */ + videoId: string + /** The full generated map. */ + source: string +} + +export const EXAMPLES: Example[] = [ + { + slug: 'steve-jobs-stanford-2005', + title: "Steve Jobs' 2005 Stanford Commencement Address", + blurb: + 'The three-stories speech, mapped: connecting the dots, love and loss, and death, each with the lesson Jobs drew from it.', + shareId: 'XrSa97dgHck', + videoId: 'UF8uR6Z6KLc', + source: jobsSource, + }, + { + slug: 'mcraven-make-your-bed', + title: 'Admiral McRaven: Make Your Bed', + blurb: + 'Ten lessons from Navy SEAL training, from making your bed to never ringing the bell, as ten readable branches.', + shareId: 'h2CfqBUw6P4', + videoId: 'pxBQLFLei70', + source: mcravenSource, + }, + { + slug: 'hans-rosling-best-stats', + title: "Hans Rosling: The best stats you've ever seen", + blurb: + 'Global development in bubbles: what the fertility and life-expectancy axes actually show, and how the world moved across them.', + shareId: '5-v5XwE6o50', + videoId: 'hVimVzgtD6w', + source: roslingSource, + }, + { + slug: 'randy-pausch-last-lecture', + title: 'Randy Pausch: The Last Lecture', + blurb: + 'Seventy-six minutes on achieving your childhood dreams, including the brick walls, condensed to one page.', + shareId: 'khh1OA8U9xg', + videoId: 'ji5_MqicxSo', + source: pauschSource, + }, +] + +/** + * Root plus first-level branches only. + * + * These maps are 66 to 74 lines and render about 1500x4300, an aspect around + * 0.35. Fitted whole into a card that is wider than it is tall, the labels come + * out a few pixels high and the card reads as texture rather than as a diagram. + * Trimming to depth 1 leaves 6 to 9 nodes at an aspect of 0.96 to 1.73, which + * is card-shaped and legible. Same trick, and the same reason, as + * MOBILE_DEMO_SOURCE in Landing.tsx. + * + * The card links to the full map; this only governs the thumbnail. + */ +export function previewSource(source: string): string { + return source + .split('\n') + .filter((line) => line.startsWith('# ') || line.startsWith('- ')) + .join('\n') +} + +/** + * Canonical share page for an example, or null when no share base is + * configured (a local dev build without VITE_SHARE_BASE, or a fork). Built from + * SHARE_BASE rather than a literal domain so forks and zone config win, exactly + * as shortenShareUrl does. + * + * `view=map` matches the links the Chrome extension hands out: the editor opens + * with the source panel collapsed, map first. + */ +export function exampleShareUrl(example: Example): string | null { + if (!SHARE_BASE) return null + return `${SHARE_BASE}s/${example.shareId}?view=map` +} + +/** The talk this map was generated from. */ +export function exampleVideoUrl(example: Example): string { + return `https://www.youtube.com/watch?v=${example.videoId}` +} diff --git a/apps/editor/src/examples/mcraven-make-your-bed.mmd b/apps/editor/src/examples/mcraven-make-your-bed.mmd new file mode 100644 index 0000000..6567b67 --- /dev/null +++ b/apps/editor/src/examples/mcraven-make-your-bed.mmd @@ -0,0 +1,70 @@ +# University of Texas at Austin 2014 Commencement Address - Admiral William H. McRaven +- opening remarks and personal memories + - thank you to university officials and families + - remembers graduation day 37 years ago + - mentions throbbing headache, serious girlfriend, Navy commission +- the power of small impact + - average American meets 10,000 people + - change lives of 10 people, each changes 10 more + - five generations could affect 800 million people + - one generation could affect world population of 8 billion +- military examples of impact + - officer choosing left saved 10 soldiers in Baghdad + - non-commissioned officer avoided 500 lb IED in Kandahar + - decisions saved soldiers, children, grandchildren +- "what starts here changes the world" + - confidence the world will be better after graduates act + - lessons apply regardless of military service, gender, ethnicity, orientation, social status +- basic SEAL training overview + - six months of long runs, cold midnight swims, obstacle courses, calisthenics, sleep deprivation + - constant harassment by trained warriors to find weak minds and bodies + - training seeks leaders who thrive in stress, chaos, failure, hardships +- ten lessons learned from SEAL training + - make your bed + - perfect bed gives small sense of pride + - leads to completing many tasks throughout the day + - reinforces that little things matter + - find someone to help you paddle + - boat crews of seven must synchronize paddling + - everyone must exert equal effort or be dumped back on beach + - need friends, colleagues, strangers, and a strong coxin to guide + - measure a person by the size of their heart, not by the size of their flippers + - Munchkin crew: diverse backgrounds, all under 5'5" + - out-paddled, outran, out-swam larger crews + - SEAL training equalizes; will to succeed matters most + - get over being a sugar cookie and keep moving forward + - uniform inspection failures lead to "sugar cookie" punishment + - sometimes effort is unappreciated; life can be like that + - keep moving despite setbacks + - embrace the circuses + - failing standards puts name on circus list + - two extra hours of calisthenics build inner strength and resiliency + - life is filled with painful "circuses"; do not fear them + - slide down the obstacles head first + - slide-for-life obstacle: rope swing versus daring head-first slide + - risky move broke long-standing record + - sometimes you must take bold risks to succeed + - don't back down from the sharks + - night swim with great-white sharks off San Clemens + - if sharks approach, stand ground and punch the snout + - face fear directly + - be your very best in the darkest moments + - underwater ship attack: swim below keel in total darkness + - must stay calm, composed, and use all skills + - darkest moments demand highest performance + - sing in the mud + - hell week mud flats: 15-hour cold, pressure to quit + - class begins singing, morale rises, mud feels warmer + - hope can lift people out of the toughest situations + - never ever ring the bell + - bell signals quitting SEAL training + - message: never quit, keep enduring hardships +- closing exhortation to the class of 2014 + - moments away from graduation and beginning life's journey + - potential to affect 800 million people in the next century + - start each day with a task completed + - find someone to help you through life + - respect everyone, know life is unfair and failure is common + - take risks, step up in toughest times, face bullies, lift downtrodden, never give up + - what started here will indeed change the world for the better + - thank you. diff --git a/apps/editor/src/examples/randy-pausch-last-lecture.mmd b/apps/editor/src/examples/randy-pausch-last-lecture.mmd new file mode 100644 index 0000000..6ac5ec8 --- /dev/null +++ b/apps/editor/src/examples/randy-pausch-last-lecture.mmd @@ -0,0 +1,74 @@ +# Randy Pausch's Last Lecture: Achieving Your Childhood Dreams +- Opening remarks + - Health diagnosis and outlook + - Purpose of the lecture +- Childhood dreams + - Zero-gravity experience + - NASA "vomit comet" program + - Student proposal competition + - Press-pass workaround + - Flight and reaction + - Playing in the NFL + - Early football coach Jim Graham + - Fundamentals lesson + - Coach Setliff's enthusiasm trick + - Lessons on criticism and perseverance + - Authoring a World Book encyclopedia article + - Contribution on virtual reality + - Reflection on quality control + - Being Captain Kirk / Star Trek fandom + - Inspiration from the character + - Collaboration with William Shatner and Chip Walter + - Virtual reality tribute + - Winning stuffed animals at amusement parks + - Personal stories with family pictures + - Message about authenticity + - Becoming a Disney Imagineer + - Early Disney trip inspiration + - Rejection letters and "brick wall" metaphor + - Virtual reality project for Aladdin attraction + - Negotiations with Disney (Jon Snoddy, legal issues) + - Work on head-mounted display and magic carpet ride + - Long-term involvement and consulting +- Enabling others' dreams + - Role as professor + - Building Virtual Worlds (BVW) course + - Structure: 50 students, rotating teams, 2-week projects + - Rules: no violence, no pornography + - Student creativity and surprise outcomes + - Mentor Andy Van Dam's advice + - Campus-wide exhibitions and audience impact + - Dream Fulfillment Factory concept + - Collaboration with Don Marinelli + - Entertainment Technology Center (ETC) model + - Project-based master's program + - Industry partnerships (EA, Activision, Disney Quest, etc.) + - Global expansion (Singapore, Australia, Korea) + - Alice project + - Teaching programming through storytelling + - Wide adoption and future versions + - Key contributors (Dennis Cosgrove, Caitlin Kelleher, Wanda Dann) +- Lessons learned + - Influence of family + - Mother's support and father's adventurous spirit + - Parents' charitable actions + - Mentors and teachers + - Andy Van Dam's guidance and career advice + - Jim Foley's encouragement + - Jon Snoddy's patience and perseverance lesson + - Denny Proffitt's leadership example + - Peers and colleagues + - Sharon Burks and Syl's personal advice + - Dennis Cosgrove's loyalty advocacy + - Jessica Hodgins and Jesse Schell's faculty recruitment + - Personal philosophies + - Embrace brick walls as tests of dedication + - Head-fake learning (teach through fun) + - Earnestness over hipness + - Gratitude for feedback and karma + - Balance of Tigger vs. Eeyore attitude +- Closing thoughts + - Celebration of childhood wonder + - Commitment to have fun despite illness + - Invitation to continue enabling dreams + - Thank you and applause diff --git a/apps/editor/src/examples/steve-jobs-stanford-2005.mmd b/apps/editor/src/examples/steve-jobs-stanford-2005.mmd new file mode 100644 index 0000000..d6937a4 --- /dev/null +++ b/apps/editor/src/examples/steve-jobs-stanford-2005.mmd @@ -0,0 +1,66 @@ +# Steve Jobs' 2005 Stanford Commencement Address +- Introduction + - Honor to speak at Stanford + - Never graduated from college +- Story 1: Connecting the Dots + - Adoption background + - Mother wanted college-graduate adoptive parents + - Parents accepted a baby boy on a night call + - Promise to send him to college + - Reed College experience + - Dropped out after six months, stayed as a drop-in + - Financial strain on working-class parents + - Decision to quit and trust the future + - Life on a tight budget (coke bottles, floor sleeping, Hare Krishna meals) + - Calligraphy class + - Took class because he could + - Learned serif vs. sans-serif, spacing, typography + - No practical use at the time + - Impact on Macintosh + - Ten years later, calligraphy inspired beautiful typography on the Mac + - Mac's multiple typefaces and proportional fonts + - Influence on personal computers overall + - Lesson + - Cannot connect dots forward, only backward + - Trust that the dots will connect + - Follow curiosity, intuition, and gut +- Story 2: Love and Loss + - Apple beginnings + - Started Apple with Woz in parents' garage at age 20 + - Grew to $2 billion company, 4,000 employees + - Released Macintosh, turned 30 + - Being fired + - Hired a new executive, vision clash + - Board sided with executive, Jobs ousted publicly + - Felt like a failure, met Packard and Noyce + - Restarting + - Started NeXT and Pixar + - Met Laurene, fell in love, married + - Pixar created "Toy Story," became top animation studio + - Return to Apple + - Apple bought NeXT, Jobs returned + - NeXT technology powered Apple's renaissance + - Lesson + - Love what you do, keep looking for it + - Do not settle; when you find it you'll know + - Great work comes from loving your work +- Story 3: Death + - Daily reminder + - Quote about living each day as if it were the last + - Each morning asked if today would be his last desired day + - Changed course when answer was "No" too often + - Cancer diagnosis + - Pancreatic tumor, told only 3-6 months left + - Doctor advised preparing for death + - Biopsy revealed rare curable form + - Surgery saved his life + - Perspective on death + - Death as life's change agent, clearing the old for the new + - No one wants to die, but it is inevitable + - Encourages living without others' expectations + - Follow inner voice, have courage + - Whole Earth Catalog reference + - Stewart Brand's "Stay Hungry. Stay Foolish." message + - Jobs' wish for graduates +- Closing + - Thank you and farewell. diff --git a/apps/editor/src/pages/Landing.tsx b/apps/editor/src/pages/Landing.tsx index 75567a0..7d8306a 100644 --- a/apps/editor/src/pages/Landing.tsx +++ b/apps/editor/src/pages/Landing.tsx @@ -1,10 +1,16 @@ import { useMemo } from 'react' import { Link } from 'react-router-dom' -import { render } from 'mindmaply-core' +import { render, renderMarkdown } from 'mindmaply-core' import { highlight } from '../highlight' import { buildEmbedUrl } from '../share' import { CHROME_EXTENSION_URL } from '../extension' import { NARROW_QUERY, useMediaQuery } from '../mobile' +import { + EXAMPLES, + exampleShareUrl, + exampleVideoUrl, + previewSource, +} from '../examples/manifest' // The hero demo's content IS the value story — rendered live by mindmaply-core. const DEMO_SOURCE = `flowchart LR @@ -103,6 +109,21 @@ export default function Landing() { return '' } }, []) + // Rendered here by the same engine the editor uses, so this section draws + // even if the API is down, and so crawlers get real markup (/svg on the API + // is disallowed in robots.txt). Depth-1 previews only: see previewSource. + const examplePreviews = useMemo( + () => + EXAMPLES.map((example) => { + try { + return { example, svg: renderMarkdown(previewSource(example.source), { direction: 'LR' }) } + } catch (err) { + console.error('mindmaply-core render error:', err) + return { example, svg: '' } + } + }), + [], + ) return (
@@ -282,6 +303,56 @@ export default function Landing() { +
+

Maps from real talks

+

+ Every one of these was generated from a YouTube talk by the extension, then + left exactly as it came out. Open one to pan, zoom and edit the real map. +

+ +

+ Each card shows the top level of its map. The full maps run 60 to 75 branches. +

+
+

Your AI already knows how to use this