#ideasYour 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)]
}| Argument | Type | Description |
|---|---|---|
tagged | str | array | dictionary | none | The tags to narrow the corpus to. The same argument #window and #outline take, with the same meanings. |
match | str | Whether a matching idea has to carry "any" of those tags — the default — or "all" of them. |
filter | function | none | A 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. |
sort | str | auto | The order the rows come back in. auto and "lexicographic" both mean by name; "date" puts the newest created first and undated ideas last. |
index | dictionary | none | A #tag-index projection, whose declared fields are merged onto every row. The supported way to filter or sort on a tag value. |
values | bool | Whether 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:
| Field | Type | What it holds |
|---|---|---|
id | str | The full name, prefix included — "idea:etal". |
name | str | The same name with the prefix stripped — "etal". What every accessor on this page takes. |
title | content | none | The authored title, as content, refs and all. none where the idea has none. |
text | str | That same title as plain text, "" where there is none. A reference inside it reads as its target’s name rather than as a number. |
label | str | What 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. |
tags | array | The 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. |
body | str | The idea’s body as plain text, "" where it is empty — matchable and excerptable, but not renderable. #idea-body is how you render one. |
href | str | none | Where the idea’s minted page sits from the page this was called on. See #idea-href. |
page | str | none | The same page from the site root. See #idea-path. |
created | datetime | none | The 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.
#tag-indexA 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:
| Form | What 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.
#tag-dataEvery 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.