Nodes
Graph
Diagramme als Daten deklariert — Balken, Linie oder Kreis — aus Ihren Zahlen oder denen eines Endpoints.
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.
- Helper
- graph
- Node-Art
- graph
- Kategorie
- Daten
- Wird aufgelöst
- Both
- Kinder
- None.
| Option | Typ | Was sie bewirkt |
|---|---|---|
id erforderlich | 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 erforderlich | 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 erforderlich | (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(). |
Deklariert, nicht gezeichnet
Ein Graph-Node enthält Zahlen und eine Form, nie ein Bild:
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",
});
Zahlen statt eines Bildes mitzuführen heißt: eine Deklaration bedient jeden Renderer, sie diffed zwischen Builds lesbar, und man kann sie in einem Test prüfen.
Die Form der Payload gehört Ihnen
data ist ein JsonObject, und das Framework schaut nie hinein. Die hier
verwendete Form { labels, series } ist eine Konvention, kein Schema — nehmen
Sie, was Ihr Renderer erwartet, und halten Sie es im Projekt einheitlich.
Eines geschieht mit der Payload doch: String-Werte laufen durch den
Template-Renderer, genau wie Prosa. Ein Label mit {{derived.total}} wird
aufgelöst.
Ableiten in data
Laufende Summen, Prozentwerte und Umbasierungen gehören in die data-Funktion,
nicht weiter stromaufwärts. Diagramm und der Text daneben werden dann aus einer
Quelle berechnet und können sich nicht widersprechen.
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`,
}); Wozu es aufgelöst wird
Der Node, wie er im DocumentModel erscheint: das JSON, das ein Renderer bekommt. Kein Styling, kein Layout.
{
"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",
}); Wozu es aufgelöst wird
Der Node, wie er im DocumentModel erscheint: das JSON, das ein Renderer bekommt. Kein Styling, kein Layout.
{
"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",
}); Wozu es aufgelöst wird
Der Node, wie er im DocumentModel erscheint: das JSON, das ein Renderer bekommt. Kein Styling, kein Layout.
{
"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.`,
}); Wozu es aufgelöst wird
Der Node, wie er im DocumentModel erscheint: das JSON, das ein Renderer bekommt. Kein Styling, kein Layout.
{
"id": "peak-times",
"kind": "graph",
"mode": "dynamic",
"graphType": "bar",
"placeholder": "Your busiest hours, Monday to Sunday"
} Was der Endpoint gefragt wird
Gegen dieselben Beispieldaten aufgelöst. Ein Vorschau-Build hält beim Platzhalter an; ein Build zur Anfragezeit schickt diese mit.
- 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.
Anmerkungen
graphTypesteht bei beiden Helpern standardmäßig aufbar.- Bei einem dynamischen Graph liegt die Form weiterhin lokal fest. Der Endpoint
liefert die Zahlen und darf
graphTypeundcaptionin seiner Antwort überschreiben.