Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions docs/data-ownership.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
---
id: data-ownership
title: Data ownership and exit story
section: concepts
order: 30
summary: Your flat files stay the source of truth; exports and types keep them portable.
related:
- json-export
- positioning
---

# Data ownership and exit story

Flatbread's portability story starts with a simple constraint: **your flat files
Expand Down
10 changes: 10 additions & 0 deletions docs/edit-file-see-query-update-demo.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
---
id: edit-file-see-query-update-demo
title: Edit file, see query update demo
section: guides
order: 20
summary: A single-process demo of editing a file and watching the query update.
related:
- local-dev-loop
---

# Edit file → see query update demo

This is a single-process demo harness, not the long-running `flatbread start`
Expand Down
11 changes: 11 additions & 0 deletions docs/glossary.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
---
id: glossary
title: Flatbread glossary
section: concepts
order: 20
summary: Definitions for collections, records, refs, and relations.
related:
- positioning
- pmf-decision-rubric
---

# Flatbread glossary — relational content primitives

This page defines words used by Flatbread. Flatbread turns files in your
Expand Down
10 changes: 10 additions & 0 deletions docs/json-export.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
---
id: json-export
title: Snapshot export
section: concepts
order: 40
summary: Turn the content graph into portable JSON and CSV review artifacts.
related:
- data-ownership
---

# Snapshot export

Snapshot exports are part of Flatbread's data ownership story: they turn the
Expand Down
10 changes: 10 additions & 0 deletions docs/local-dev-loop.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
---
id: local-dev-loop
title: Local dev loop and watch boundaries
section: guides
order: 10
summary: The four moving parts of Flatbread's watch loop and what reloads when.
related:
- edit-file-see-query-update-demo
---

# Local dev loop and watch boundaries

Flatbread's local loop has four moving parts:
Expand Down
11 changes: 11 additions & 0 deletions docs/pmf-decision-rubric.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
---
id: pmf-decision-rubric
title: Comparing Flatbread with other tools
section: reference
order: 10
summary: Where Flatbread fits next to databases, CMSs, and file-based tools.
related:
- positioning
- glossary
---

# Comparing Flatbread with other tools

This page helps explain where Flatbread fits. It compares Flatbread with tools
Expand Down
12 changes: 12 additions & 0 deletions docs/positioning.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
---
id: positioning
title: Flatbread positioning
section: concepts
order: 10
summary: What Flatbread is, who it is for, and what it does not do.
related:
- glossary
- pmf-decision-rubric
- data-ownership
---

# Flatbread positioning

For installation and usage, see the [main README](../README.md). For

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HIGH (disputed → not BLOCK) — Relative GitHub links (../README.md, ./glossary.md, later ../packages/…) 404 or miss anchors when the same file is rendered on the dogfood site at /docs/{id}.

Minimal fix: Keep GitHub-valid paths in source or rewrite/resolve them in the Doc renderer for /docs/{id} (and heading ids). Same pattern on the other six indexed guides.

Expand Down
19 changes: 19 additions & 0 deletions examples/docs/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# dependencies
/node_modules

# next.js
/.next/
/out/

# flatbread
.flatbread-codegen-cache.json

# misc
.DS_Store

# env files
.env*

# typescript
*.tsbuildinfo
next-env.d.ts
37 changes: 37 additions & 0 deletions examples/docs/app/components/AsciiRule.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import type { ReactNode } from 'react';

type AsciiRuleProps = {
label?: string;
align?: 'left' | 'right' | 'center';
className?: string;
children?: ReactNode;
};

/**
* A horizontal rule built from box-drawing characters with an inline label,
* e.g. `──[ CONCEPTS ]──`. Rendered as flexbox + borders so it stays crisp at
* any width and copies as plain text.
*/
export function AsciiRule({
label,
align = 'left',
className,
children,
}: AsciiRuleProps) {
return (
<div
className={`ascii-rule ${className ?? ''}`}
data-align={align}
aria-hidden={Boolean(label) ? undefined : true}
>
{label ? (
<span className="ascii-rule__label">
<span className="ascii-rule__bracket">[</span>
{` ${label} `}
<span className="ascii-rule__bracket">]</span>
</span>
) : null}
{children}
</div>
);
}
110 changes: 110 additions & 0 deletions examples/docs/app/components/Doc.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import Link from 'next/link';
import { AsciiRule } from './AsciiRule';
import { MotionText } from './MotionText';
import { MotionReveal } from './MotionReveal';

export type DocPageView = {
id: string;
slug: string;
title: string;
section: string;
order: number;
summary: string | null;
html: string;
timeToRead: number | null;
};

export type RelatedLink = {
slug: string;
title: string;
};

type DocProps = {
doc: DocPageView;
related: RelatedLink[];
prev: RelatedLink | null;
next: RelatedLink | null;
};

const SECTION_LABELS: Record<string, string> = {
concepts: 'Concepts',
guides: 'Guides',
reference: 'Reference',
};

/**
* Renders a single doc page: a section rule, an animated title, the summary
* line, the rendered prose, related links, and prev/next navigation.
*/
export function Doc({ doc, related, prev, next }: DocProps) {
const sectionLabel = SECTION_LABELS[doc.section] ?? doc.section;
return (
<article className="flex flex-col gap-6">
<MotionReveal>
<AsciiRule label={sectionLabel} />
</MotionReveal>

<MotionText as="h1" text={doc.title} className="text-3xl font-semibold tracking-tight" />

{doc.summary ? (
<p className="text-[var(--muted-foreground)] -mt-2">{doc.summary}</p>
) : null}

<div className="flex items-center gap-4 text-xs text-[var(--muted-foreground)] -mt-2">
{doc.timeToRead ? <span>{doc.timeToRead} min read</span> : null}
<span>·</span>
<span>docs/{doc.slug}.md</span>
</div>

<MotionReveal delay={0.05}>
<div
className="prose"
dangerouslySetInnerHTML={{ __html: doc.html }}
/>
</MotionReveal>

{related.length > 0 ? (
<section className="mt-8 flex flex-col gap-3">
<AsciiRule label="Related" />
<ul className="flex flex-col gap-1">
{related.map((r) => (
<li key={r.slug}>
<Link
href={`/docs/${r.slug}`}
className="text-sm no-underline hover:text-[var(--accent)] transition-colors"
>
→ {r.title}
</Link>
</li>
))}
</ul>
</section>
) : null}

<nav className="mt-10 grid grid-cols-1 sm:grid-cols-2 gap-4">
{prev ? (
<Link
href={`/docs/${prev.slug}`}
className="no-underline border p-4 hover:border-[var(--accent)] transition-colors"
>
<div className="text-xs text-[var(--muted-foreground)]">← prev</div>
<div className="mt-1">{prev.title}</div>
</Link>
) : (
<span />
)}
{next ? (
<Link
href={`/docs/${next.slug}`}
className="no-underline border p-4 hover:border-[var(--accent)] transition-colors sm:text-right"
>
<div className="text-xs text-[var(--muted-foreground)]">next →</div>
<div className="mt-1">{next.title}</div>
</Link>
) : (
<span />
)}
</nav>
</article>
);
}
41 changes: 41 additions & 0 deletions examples/docs/app/components/MotionReveal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
'use client';

import { motion, type Variants } from 'motion/react';
import type { ReactNode } from 'react';

const EASE = [0.22, 1, 0.36, 1] as const;

const variants: Variants = {
hidden: { opacity: 0, y: 12 },
visible: {
opacity: 1,
y: 0,
transition: { duration: 0.5, ease: EASE },
},
};

type MotionRevealProps = {
children: ReactNode;
className?: string;
delay?: number;
};

/**
* Fades and lifts its children into view once, when scrolled to. Used for
* section blocks so the page stays calm: motion is a finish on the typography,
* not a feature.
*/
export function MotionReveal({ children, className, delay = 0 }: MotionRevealProps) {
return (
<motion.div
className={className}
variants={variants}
initial="hidden"
whileInView="visible"
viewport={{ once: true, amount: 0.2 }}
transition={{ delay }}
>
{children}
</motion.div>
);
}
61 changes: 61 additions & 0 deletions examples/docs/app/components/MotionText.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
'use client';

import { motion, type Variants } from 'motion/react';
import type { ReactNode } from 'react';

const EASE = [0.22, 1, 0.36, 1] as const;

const container: Variants = {
hidden: {},
visible: {
transition: { staggerChildren: 0.04, delayChildren: 0.05 },
},
};

const word: Variants = {
hidden: { opacity: 0, y: 8 },
visible: {
opacity: 1,
y: 0,
transition: { duration: 0.5, ease: EASE },
},
};

type MotionTextProps = {
text: string;
as?: 'h1' | 'h2' | 'h3' | 'p' | 'span';
className?: string;
children?: ReactNode;
};

/**
* Reveals text one word at a time. Used on the hero headline so it reads like a
* terminal settling into place, without a fake typewriter. Renders the words
* inline so the heading still wraps naturally.
*/
export function MotionText({
text,
as = 'h1',
className,
}: MotionTextProps) {
const Tag = motion[as];
const words = text.split(' ');
return (
<Tag
className={className}
variants={container}
initial="hidden"
whileInView="visible"
viewport={{ once: true, amount: 0.4 }}
>
{words.map((w, i) => (
<span key={i} className="inline-block overflow-hidden align-bottom">
<motion.span className="inline-block" variants={word}>
{w}
{i < words.length - 1 ? '\u00A0' : ''}
</motion.span>
</span>
))}
</Tag>
);
}
Loading
Loading