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

#ideas

Your rookery’s contents are always also available in Typst through the #ideas() function. This function returns all of your ideas as a data structure that you may then use to customize your rookery or power downstream applications. ideas has to be called inside #context, since it reads the rookery’s registry:

#import "@rookery/core:0.1.0": ideas
#context {
  for e in ideas(tagged: "concept") [- #link(e.href, e.label)]
}
ArgumentTypeDescription
taggedstr | array | dictionary | noneThe tags to narrow the corpus to. The same argument #window and #outline take, with the same meanings.
matchstrWhether a matching idea has to carry "any" of those tags — the default — or "all" of them.
filterfunction | noneA predicate over the idea’s tag dictionary, ANDed with tagged and match. Applied before the row is built, so an idea you drop never pays for its own conversions — which is the whole reason it is an argument here rather than a .filter() on the result.
sortstr | autoThe order the rows come back in. auto and "lexicographic" both mean by name; "date" puts the newest created first and undated ideas last.
indexdictionary | noneA #tag-index projection, whose declared fields are merged onto every row. The supported way to filter or sort on a tag value.
valuesboolWhether each row also carries a tags-dict field holding the idea’s whole tag dictionary, values included. Off by default, and absent rather than empty when off, so a consumer cannot read (:) off a row and conclude the idea is untagged.

Each row is a dictionary of ten fields, in every call:

FieldTypeWhat it holds
idstrThe full name, prefix included — "idea:etal".
namestrThe same name with the prefix stripped — "etal". What every accessor on this page takes.
titlecontent | noneThe authored title, as content, refs and all. none where the idea has none.
textstrThat same title as plain text, "" where there is none. A reference inside it reads as its target’s name rather than as a number.
labelstrWhat to call this idea, and never empty: the title as text, else the body’s first sixty characters, else the idea’s own name. Reach for this wherever an idea is referred to rather than rendered — a row in an index, a node in a graph, a sort key — and the if r.text == "" { r.name } dance stops being yours to write.
tagsarrayThe tag names, every key including the valued ones, () where there are none. A flat array of strings, so it drops into a JSON index unchanged. Tags are unordered and nothing may depend on the sequence; the values live in tags-dict and #tag-data.
bodystrThe idea’s body as plain text, "" where it is empty — matchable and excerptable, but not renderable. #idea-body is how you render one.
hrefstr | noneWhere the idea’s minted page sits from the page this was called on. See #idea-href.
pagestr | noneThe same page from the site root. See #idea-path.
createddatetime | noneThe idea’s date.

Three things are deliberately not here in bulk: the body as content, the backlink graph, and the tag values. The first would make every consumer a second transclusion engine; the second is the minted page’s own business; the third can be a datetime or content, which a JSON index encodes as a silent blob. This list is a contract other packages are written against, so a field is added rather than changed.

[idea:tag-index-reference]reference

#tag-index

A declared projection of tag values onto an #ideas row. It exists because a tag’s value is arbitrary and a row’s fields must be encodable; a projection makes the value narrow and checked, which is what lets it ride a row at all.

#import "@rookery/core:0.1.0": ideas, tag-index

#let INDEX = tag-index((
  cycle: (family: "cycle-"),                      // -> "26-27"
  kind: (family: "venue-", one-of: KINDS),        // -> "postdoc"
  deadline: (key: "date-deadline", stamp: true),  // -> "20261101"
  stage: (from: stage-of),                        // derived
))

#context {
  for e in ideas(index: INDEX) [- #e.kind, due #e.deadline]
}

Each field names exactly one extractor:

FormWhat it projects
key: "<tag>"That tag’s value, or none where the idea does not carry it.
family: "<prefix>"The first flat tag whose key starts with the prefix, prefix stripped. one-of: restricts and orders the candidates, so an idea carrying two of a family resolves to the earliest you listed rather than to whichever key order happens to yield first.
from: <function>Called with the idea’s whole tag dictionary. A derived value — the current stage of a dated log, how far something got — is a computation rather than a tag value, and this is the only form that makes one filterable or sortable.

Any of the three may carry stamp: true, which renders a datetime as a zero-padded [year][month][day] string. That is not only a display choice: a fixed-width numeric string sorts lexically in date order, so a stamped field is a free sort key.

A projected value must be a scalar — a string, an integer, a float, a boolean or none — and the build fails where one is not. A field may not take the name of a row field above, for the same reason: naming one href and silently replacing every link on the page is the failure this refusal prevents.

[idea:tag-data-reference]reference

#tag-data

Every registered idea’s tag dictionary, keyed by full name. The bulk accessor a package builds on when it needs tag values across the whole rookery, where #idea-tag-names and #idea-tag-value answer for one idea at a time.

#import "@rookery/core:0.1.0": ideas, tag-data
#context {
  let tags = tag-data()
  // -> ("idea:etal": (phd: none, priority: 1), ..)
  for e in ideas() [- #e.label: #repr(tags.at(e.id))]
}

Takes no arguments, and must be called inside #context. One #ideas plus one #tag-data covers the corpus, and the two join on id — which is the point, since the per-idea accessors each resolve the registry again and walking a rookery through them pays that cost once per idea.

Values are arbitrary Typst values: datetimes, arrays, content, whatever a package put there. Do not serialize this wholesale into a page.

It is not called tags(), though that is the obvious parallel with #ideas: tags is a parameter name on #idea, #ideate and most of this package’s constructors, so a bare tags() would be shadowed by that parameter inside every one of their bodies.