[idea:ideate-reference]reference2026-08-20

#ideate

The one place in rookery where an idea is inferred rather than written. #ideate takes a block of content and mints ideas from it, either as a plain function on one block or as a document show rule — #show: ideate at the top level hands it the rest of the document. It is opt-in either way, and on a paged target it is a passthrough: a PDF of a block of prose is that block of prose.

ArgumentTypeDescription
bodycontentThe content to mint ideas from. The sole positional argument, which is what lets the function double as a show rule.
separatorfunction | noneWhat starts a new idea: par (or parbreak) for one idea per paragraph, heading.where(level: n) for one per section, or none — the default — to mint the whole body as a single idea. A heading standing alone is passed through as structure rather than wrapped as an idea of its own.
titlecontent | function | noneA title given to every idea minted, or a function (content, labels) => content called on each section’s separating heading to compute its own. The function form is heading mode only, and the heading leaves the body when it is used, since the title is already rendered as the idea’s heading.
namefunction | autoA function (content, labels) => str computing each idea’s name from its separating heading — slug is exported for exactly this. Defaults to auto, which slugs the heading the same way an unnamed #idea’s title is slugged, falling back to #idea’s own untitled-naming rule when the heading yields no usable slug. A fixed value is refused, as it would mint every idea in the body under one name.
tagsstr | array | dictionary | functionTags put on every idea minted, in the same forms #idea accepts, or a function (content, labels) => tags computing each section’s own. An #ideate-tag(..) beacon placed in a section’s content adds to these, and wins on a conflicting key.
displaydictionaryAs on #idea, and read by every idea minted — see below.

Two of the nine keys invert #idea’s own defaults, and are given here as they are read when nothing is said:

#show: ideate.with(
  display: (
    // an inferred idea is not one anybody named, and a frame around
    // every paragraph is chrome nobody asked for
    frame: false,
    // with more force: an inferred idea's name is a sequence number,
    // which tells a reader nothing
    name: false,
  ),
)

The remaining seven keys — title, date, tags, context, backlinks, label and background — carry their #idea meanings and defaults. As there, each key is also an argument in its own right, with the prefix restored, and an argument on the same call wins over the dictionary’s value for that key.

Every other #idea argument is forwarded to every idea minted. Note that those names are generated rather than authored, so an idea that has to be linkable is written by hand.

[idea:ideate-tag-reference]reference

#ideate-tag

A beacon that tags the idea minted around it, written inside the prose rather than at the #ideate call site. It takes tags in the same four forms #idea’s own tags argument accepts — nothing, one name, a list of names, or a dictionary carrying metadata — and adds to whatever #ideate(tags: ..) already put on every idea, winning on a key the two disagree about.

#show: ideate.with(separator: heading.where(level: 2), tags: "note")

== A section
#ideate-tag(("draft", "phd"))
Its prose, minted as an idea tagged note, draft and phd.

Under separator: heading the beacon may sit anywhere in the section’s content; inside a paragraph it tags the group it is written in. It renders nothing of itself.

[idea:ideate-name-reference]reference

#ideate-name

The same device for a name: a beacon naming the one idea minted around it, overriding whatever name would otherwise have derived.

== A section
#ideate-name("the-name-i-want")
Its prose, minted under `idea:the-name-i-want`.

Unlike a name function, this works under every separator — it carries its own value rather than reading one off a heading — which makes it the way to pin the one inferred idea that has to be linkable without naming the rest.

[idea:slug-reference]reference

#slug

A URL-safe slug from content or a string: lowercased, every run of characters outside a–z and 0–9 collapsed to a single hyphen, with none left at either end.

#import "@rookery/core:0.1.0": ideate, slug
#show: ideate.with(
  separator: heading.where(level: 2),
  name: (h, labels) => "sec-" + slug(h),
)

Exported for exactly that: the name function above, naming each section’s idea after its own heading, so inserting or reordering sections does not renumber every name after it.

Text that is nothing but punctuation slugs to the empty string, and the build fails rather than minting an idea under an empty name.