Nodes
Graph
Grafieken als data gedeclareerd — staaf, lijn of taart — uit jouw cijfers of die van een endpoint.
Charts are declared, never drawn: `graphType` fixes the form, `data` returns the payload. Holding numbers rather than an image means one declaration serves every renderer and stays diffable in the artifact.
- Helpers
- graph
- Nodesoort
- graph
- Categorie
- Data
- Wordt opgelost
- Both
- Children
- None.
| Optie | Type | Wat het doet |
|---|---|---|
id verplicht | string | Stable address for the node. Generation endpoints target it and build artifacts diff on it, so treat a rename as a breaking change. |
graphType | "bar" | "line" | "pie" | The form of the chart. Defaults to bar. |
data verplicht | JsonObject | (data) => JsonObject | Static only. The plot payload, as plain JSON. Any shape you like — string values in it are run through the template renderer, so {{derived.total}} resolves inside the payload as it would in prose. |
caption | string | (data) => string | Printed beneath the chart, and the words the placeholder frame shows today. Optional on both modes. |
placeholder | (data, availableTokens) => string | Dynamic only. What previews show in place of generated content. Optional, but a letter that reads badly without one cannot be reviewed. |
generalPrompt verplicht | (data, availableTokens) => string | Dynamic only. What this node should say. |
infoPrompt | (data, availableTokens) => string | Dynamic only. Context the model should have but should not restate. |
negativePrompt | (data, availableTokens) => string | Dynamic only. What to avoid — claims, tones, or facts it must not invent. |
systemPrompt | (data, availableTokens) => string | Dynamic only. Role and voice, applied ahead of the other prompts. |
derivers | DeriverInvocation[] | Values computed before the node resolves, written to derived.* and readable from a template token. Built with derive(). |
Gedeclareerd, niet getekend
Een graph-node bevat getallen en een vorm, nooit een afbeelding:
graph<MemberData>({
id: "visits-by-month",
graphType: "bar",
data: (data) => ({
labels: data.visitsByMonth.map((entry) => entry.month),
series: [{ name: "Visits", values: data.visitsByMonth.map((entry) => entry.visits) }],
}),
caption: () => "Your visits, last six months",
});
Cijfers meedragen in plaats van een plaatje betekent dat één declaratie elke renderer bedient, leesbaar diff’t tussen builds, en in een test kan worden vastgelegd.
De vorm van de payload is van jou
data is een JsonObject en het framework kijkt er nooit in. De hier gebruikte
vorm { labels, series } is een conventie, geen schema — gebruik wat jouw
renderer verwacht, en houd het consistent binnen een project.
Eén ding gebeurt er wel met de payload: stringwaarden gaan door de
templaterenderer heen, net als proza. Een label met {{derived.total}} erin
wordt opgelost.
Afleiden in data
Lopende totalen, percentages en herbasering horen in de data-functie thuis en
niet stroomopwaarts. De grafiek en de tekst ernaast worden dan uit één bron
berekend, zodat ze het niet oneens kunnen zijn.
Varianten
Bar
src/nodes/graph/bar.node.ts Discrete values across a handful of buckets.
import { graph } from "docxcelerate";
import type { SampleData } from "../sample-data.ts";
/**
* A chart is declared, not drawn: `graphType` fixes the form, `data` returns
* the payload. One node then serves the browser preview and the packed DOCX.
*/
export const VisitsByMonth = graph<SampleData>({
id: "visits-by-month",
graphType: "bar",
data: (data) => ({
labels: data.visitsByMonth.map((entry) => entry.month),
series: [{ name: "Visits", values: data.visitsByMonth.map((entry) => entry.visits) }],
}),
caption: (data) => `Your visits to ${data.centreName}, last six months`,
}); Waartoe het wordt opgelost
De node zoals hij in het DocumentModel verschijnt: de JSON die een renderer aangereikt krijgt. Geen opmaak, geen lay-out.
{
"id": "visits-by-month",
"kind": "graph",
"mode": "static",
"graphType": "bar",
"data": {
"labels": [
"Apr",
"May",
"Jun",
"Jul",
"Aug",
"Sep"
],
"series": [
{
"name": "Visits",
"values": [
11,
14,
9,
16,
18,
12
]
}
]
},
"caption": "Your visits to Riverside Leisure Centre, last six months"
} Line
src/nodes/graph/line.node.ts The same data as a running total — derived in `data`, not upstream.
import { graph } from "docxcelerate";
import type { SampleData } from "../sample-data.ts";
/**
* Only `graphType` changes between the three static forms. Deriving the
* running total here keeps the chart and the prose from disagreeing.
*/
export const CumulativeVisits = graph<SampleData>({
id: "cumulative-visits",
graphType: "line",
data: (data) => {
let running = 0;
return {
labels: data.visitsByMonth.map((entry) => entry.month),
series: [
{
name: "Visits to date",
values: data.visitsByMonth.map((entry) => (running += entry.visits)),
},
],
};
},
caption: () => "Visits accumulated across the membership year",
}); Waartoe het wordt opgelost
De node zoals hij in het DocumentModel verschijnt: de JSON die een renderer aangereikt krijgt. Geen opmaak, geen lay-out.
{
"id": "cumulative-visits",
"kind": "graph",
"mode": "static",
"graphType": "line",
"data": {
"labels": [
"Apr",
"May",
"Jun",
"Jul",
"Aug",
"Sep"
],
"series": [
{
"name": "Visits to date",
"values": [
11,
25,
34,
50,
68,
80
]
}
]
},
"caption": "Visits accumulated across the membership year"
} Pie
src/nodes/graph/pie.node.ts Shares of a whole.
import { graph } from "docxcelerate";
import type { SampleData } from "../sample-data.ts";
/**
* Shares of a whole. `caption` is optional, but a chart in a letter is read
* once and not returned to.
*/
export const ClassMix = graph<SampleData>({
id: "class-mix",
graphType: "pie",
data: (data) => ({
labels: data.classMix.map((entry) => entry.label),
series: [{ name: "Share of visits", values: data.classMix.map((entry) => entry.share) }],
}),
caption: () => "How you used the centre, by activity",
}); Waartoe het wordt opgelost
De node zoals hij in het DocumentModel verschijnt: de JSON die een renderer aangereikt krijgt. Geen opmaak, geen lay-out.
{
"id": "class-mix",
"kind": "graph",
"mode": "static",
"graphType": "pie",
"data": {
"labels": [
"Swim",
"Strength",
"Classes"
],
"series": [
{
"name": "Share of visits",
"values": [
42,
33,
25
]
}
]
},
"caption": "How you used the centre, by activity"
} Dynamic
src/nodes/graph/dynamic.node.ts Figures the endpoint derives; the form still fixed locally.
import { graph } from "docxcelerate";
import type { SampleData } from "../sample-data.ts";
/**
* For figures that need deriving rather than reading. `graphType` still fixes
* the form locally, so the layout is known before the numbers are.
*/
export const PeakTimes = graph<SampleData>({
id: "peak-times",
graphType: "bar",
placeholder: () => "Your busiest hours, Monday to Sunday",
generalPrompt: (data) =>
`Plot when ${data.memberName} (${data.membershipRef}) visits, bucketed by ` +
`hour of the day across the week.`,
infoPrompt: (data) => `Their plan is ${data.plan}, which allows entry at any hour.`,
}); Waartoe het wordt opgelost
De node zoals hij in het DocumentModel verschijnt: de JSON die een renderer aangereikt krijgt. Geen opmaak, geen lay-out.
{
"id": "peak-times",
"kind": "graph",
"mode": "dynamic",
"graphType": "bar",
"placeholder": "Your busiest hours, Monday to Sunday"
} Wat er aan het endpoint wordt gevraagd
Opgelost tegen dezelfde voorbeelddata. Een previewbuild stopt bij de placeholder; een build op het moment van de aanvraag stuurt deze mee.
- general
- Plot when Adaeze Nkemelu (RIV-88214) visits, bucketed by hour of the day across the week.
- info
- Their plan is Peak Anytime, which allows entry at any hour.
Aantekeningen
graphTypestaat op beide helpers standaard opbar.- Bij een dynamische graph ligt de vorm nog steeds lokaal vast. Het endpoint
levert de cijfers en mag
graphTypeencaptionin zijn antwoord overschrijven.