C.W.K.
Stream
Lesson 01 of 04 · published

Source Is the Model

~11 min · source, model, decorations, markdown

Level 0Cold Draft
0 XP0/33 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete
"The characters on disk are the characters in the editor. Everything else is a layer painted on top."

One Text, Not Two Documents

There are two ways an editor can hold a document. A WYSIWYG editor holds a rich-text tree: bold is a node, a heading is a node, and the ** or # characters don't exist as data — they're inferred, and re-emitted as Markdown only when you export. Rekindle does the opposite. Its model is the Markdown source string itself. The # in your heading is a real character in the document, at a real offset. There is one text, and it's the source.

Rendered Look Is a Layer, Not a Model Swap

So how does bold look bold? A decoration layer paints appearance over the source without changing it. The word **fire** stays five-plus-four characters in the model; a decoration renders it bold and dims the asterisks on inactive lines. Nothing is converted; nothing is lost; the source is never abandoned for a second representation.

Model (what's on disk and in the editor):   The **fire** catches.
Decoration layer (painted over, not stored): The 𝗳𝗶𝗿𝗲 catches.

# The model never changes. The look is a layer you can toggle off
# and still have byte-identical Markdown underneath.

Why This Matters for Everything Downstream

Choosing source-as-model isn't cosmetic — it decides the architecture. Because the model is plain Markdown text, the file on disk is the truth (Track 7), a diff is a real text diff, a grep finds real characters, and every soul feature (voice underline, CMD+K) operates on offsets into source rather than nodes in a proprietary tree. The whole editor gets simpler because there's exactly one representation to reason about. WYSIWYG's rich-text tree would force a translation layer between what you see, what you store, and what Pippa reads — three representations to keep in sync instead of one.

Code

One model, a look painted on top·text
Model (on disk AND in the editor):            The **fire** catches.
Decoration layer (painted over, not stored):  The [fire in bold] catches.

# Toggle every decoration off and you still have byte-identical Markdown.
# There is ONE representation. WYSIWYG would give you three:
#   what you see  !=  what you store  !=  what Pippa reads.
Soul features operate on source offsets, not tree nodes·typescript
// Because the model IS the source string, a voice-rule match is just
// a range of character offsets — no proprietary node walk required.
type VoiceIssue = { from: number; to: number; note: string };

function detectVoiceIssues(doc: string): VoiceIssue[] {
  // scan the literal source text; return character ranges to underline
  // (Track 4 turns these ranges into CM6 decorations)
  return scan(doc);
}

// A WYSIWYG tree would force every feature to translate between the tree,
// the rendered view, and the Markdown export. Source-as-model deletes that tax.

External links

Exercise

Open a note in an editor you use and ask: if I saved this and opened the raw file in a plain text viewer, would it be identical to what I see, or a translation of it? If identical, you're in a source model. If translated (or binary), you're in a rich-text/WYSIWYG model. Notice which one makes diff, grep, and version control feel natural.
Hint
The tell: can you meaningfully git diff two versions and read the change as text? Source models diff cleanly; rich-text/binary models produce diffs you can't read, because the stored form isn't the form you edited.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.