Skip to content

feat(library): write essays in the app — editor, full Markdown, and a writing guide - #121

Merged
thethinkmachine merged 2 commits into
mainfrom
claude/inspiring-lovelace-ak678d
Sep 30, 2026
Merged

thethinkmachine merged 2 commits into
mainfrom
claude/inspiring-lovelace-ak678d

Conversation

@thethinkmachine

@thethinkmachine thethinkmachine commented Sep 30, 2026 •

Copy link
Copy Markdown
Owner

What

Essays (added in thethinkmachine/AutomataStudio#119) could so far only arrive by pull request, in a small hand-written Markdown dialect. This lets authors write them in the app, supports all of Markdown, and documents it all.

Two commits:

  1. 7065ec9 Essay editor, full Markdown, and sending essays from the app
  2. 9598fd7 A guide to writing an essay, in the app and on the website

The editor (Submit a machine)

  • Write / Split / Preview. Split is Obsidian-style: the Markdown beside the page it makes, scrolled together. Ctrl/⌘ E toggles between writing and reading.
  • Open .md file…, or drop a file on the editor. It takes Markdown or plain text up to 1 MB and drops a BOM and CRLFs. Save as .md writes it back out. Undo restores a draft that opening a file replaced.
  • Live preview with the real answers: {{facts}} from this machine's own analysis, figures drawn in the worker, [[links]] checked against the index, and the warnings the PR check would give listed underneath. A step count past the dialog's run budget shows as a pending …, filled in by CI.
  • Drafts are kept per machine in localStorage. An author updating their own entry starts from its published essay.
  • Guide opens the writing guide in the preview's place, beside the Markdown.

Full Markdown (js/library/article.js, now on markdown-it)

  • CommonMark and GFM: tables with alignment, task lists, strikethrough, autolinks. Also footnotes, definition lists, ==mark==, ~sub~, ^sup^ and smart quotes.
  • From Obsidian: > [!note] Title callouts (foldable with -/+), [[id|text]] links, YAML front matter dropped, and the shallowest heading becomes an h2.
  • Math, {{facts}} and ::: figures are plugins. A price like $5 and $10 is not math: the parser uses Pandoc's rule, and each non-math $ gets its own element so KaTeX's auto-render can't pair it either. \$ is a literal dollar.
  • Safety stays the renderer's job. Links must be http(s), mailto, # or lib:, and images must be https. Raw HTML is allowed only as bare, allow-listed tags with no attributes; anything else is escaped and reported. balanceHtml keeps an author's unclosed or stray tags inside the essay, because the website writes the essay into the page's own markup.
  • #heading links carry the id prefix, so they work in the app too.
  • markdown-it is loaded lazily in the app, as its own ~120 KB chunk. The light essay.js no longer imports the renderer; the build's check moved to essay-check.js.

Sending an essay

  • The issue form's pre-filled URL is capped at 7,800 characters, so the essay can't go there. It rides inside the submitted document (meta.library.essay), sharing the machine's compressed share link, or the clipboard when that is too long, as for large machines.
  • issue-to-entry.mjs writes it beside the machine as .md (the submission workflow already commits machines/**) and strips it from the machine file.
  • A new Essay form field, used for hand-filled forms, wins when filled in. It sits last in the form so a ### Licence heading inside an essay can't end it early.
  • An update with no essay keeps the published one. An essay over 60,000 characters is refused.

The writing guide (js/library/essay-guide.md)

  • One text, written in the essay format itself. Each of its 14 ```example blocks is shown as source beside what the real renderer makes of it, using BB(2)'s real facts and figures.
  • Shown by the editor's Guide button (lazily, via ?raw), and built by the website into /writing/, linked from Submit and the footer.
  • Tests hold it to the code: it must render with no warnings, and must name every fact, figure, callout kind and allowed HTML tag, plus the real limits and defaults. A feature added without documentation fails the tests.

Testing

  • npm test: 2611 pass, 1 skipped (the test that needs a locally built library).
  • tests/essay.test.js (21 tests): every Markdown feature, the security rules (<script>, event handlers, javascript:/data: links, non-https images, disallowed HTML), tag balancing, prices vs math, footnotes, id and anchor prefixing, figure bounds, and the guide checks above.
  • tests/library.test.js: the CI writing the essay out of a submission (from the document, from the form field, update without essay, too long), the draft store, the file reader, the editor (file picker, front matter, facts, links, callout, warnings, undo, draft restore), update prefill, the Guide pane, and the /writing/ page.
  • Mutation-checked. Each of these breaks at least one test: allowing any HTML tag, removing tag balancing, allowing any image source, keeping front matter, the CI dropping the essay, and the app not sending it.
  • vite build succeeds. markdown-it (article-*.js) and the guide (essay-guide-*.js) are their own lazy chunks.
  • Checked by hand in the running app and on the built website against a local build of the library: opened an Obsidian-style .md through the picker, used split, preview, Ctrl+E and the Guide pane, and viewed /writing/ in light, dark and at phone width.

Merge order

Merge this before thethinkmachine/automata-library#5. The library's CI runs this repo's main, so the library's new Essay form field is only read once this has landed.

🤖 Generated with Claude Code

https://claude.ai/code/session_01L8hKpV2hiVoJMZ9f6X4Zwu

…Markdown

The Submit page gets an essay editor, and essays get all of Markdown.

Editor (library-ui.js, essayEditor):
- Write / Split / Preview, Obsidian's split: the Markdown beside the page
  it makes, scrolled together; Ctrl/Cmd+E toggles reading.
- "Open .md file…" or a file dropped on the editor loads an essay already
  written (Markdown or plain text, up to 1 MB); "Save as .md" writes it
  back out; replacing a draft can be undone.
- The preview is the real renderer with this machine's own facts, its
  figures drawn in the worker, [[links]] checked against the index, and
  the warnings the PR check would give listed underneath.
- Drafts are kept per machine in localStorage; an author updating their
  entry starts from its published essay.

Markdown (article.js, now on markdown-it):
- CommonMark and GFM (tables, task lists, strikethrough, autolinks),
  footnotes, definition lists, ==mark==, ~sub~, ^sup^, smart quotes;
  Obsidian's > [!note] callouts (foldable) and [[id|text]] links;
  YAML front matter dropped; the shallowest heading becomes an h2.
- Math, {{facts}} and ::: figures are plugins; $5 and $10 is not math.
- Safety stays the renderer's: links must be http(s), mailto, # or lib:,
  images https; raw HTML only as bare allow-listed tags; balanceHtml
  keeps an author's unclosed or stray tags inside the essay.
- Loaded lazily in the app (its own ~120 KB chunk).

Transport:
- The essay rides inside the submitted document (meta.library.essay), so
  it shares the machine's compressed link instead of the issue URL.
- issue-to-entry.mjs writes it beside the machine as .md and strips it
  from the machine file; the form's own Essay field (last, so headings in
  an essay cannot end it early) wins for hand-filled forms; an update with
  no essay keeps the published one; one past 60,000 characters is refused.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L8hKpV2hiVoJMZ9f6X4Zwu
…site

js/library/essay-guide.md is the one text: everything an essay can hold,
written in the essay format itself. Every ```example block is shown as its
source beside what the real renderer makes of it, with a real machine's
facts and figures (BB(2)), so the guide cannot describe what the renderer
does not do.

- The editor's Guide button shows it in the preview's place, beside the
  Markdown being written (loaded lazily with ?raw).
- The website builds it into /writing/, linked from Submit and the footer.
- tests/essay.test.js holds it to the code: it renders with no warnings,
  and it names every fact, figure, callout kind and allowed HTML tag, and
  the real limits. A feature added without its documentation fails there.

Two renderer fixes the guide turned up:
- A dollar sign that is not math ("$5 and $10", or a written \$) gets an
  element of its own, so KaTeX's auto-render cannot pair it into math
  after the page is drawn. The parser's price rule was not enough alone.
- A #heading link now carries the id prefix, so it reaches the heading in
  the app too; and a footnote shown as `[^x]` in code is no longer
  reported as a missing note.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L8hKpV2hiVoJMZ9f6X4Zwu
Copilot AI balanced review requested due to automatic review settings September 30, 2026 16:00
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@thethinkmachine
thethinkmachine merged commit 078d5d9 into main Sep 30, 2026
17 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants