Nodos
Graph
Gráficos declarados como datos — barras, líneas o sectores — a partir de tus cifras o de las de un 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
- Clase de nodo
- graph
- Categoría
- Datos
- Se resuelve
- Both
- Hijos
- None.
| Opción | Tipo | Qué hace |
|---|---|---|
id obligatorio | 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 obligatorio | 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 obligatorio | (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(). |
Declarado, no dibujado
Un nodo de gráfico contiene números y una forma, nunca una imagen:
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",
});
Llevar cifras en lugar de una imagen significa que una sola declaración sirve a todos los renderizadores, produce diffs legibles entre compilaciones y puede comprobarse en una prueba.
La forma del payload es tuya
data es un JsonObject y el framework nunca mira dentro. La forma
{ labels, series } que se usa aquí es una convención, no un esquema — usa lo
que espere tu renderizador y mantenlo coherente en todo el proyecto.
Una cosa sí le ocurre al payload: los valores de tipo cadena pasan por el
renderizador de plantillas, igual que la prosa. Una etiqueta que contenga
{{derived.total}} se resuelve.
Derivar dentro de data
Los acumulados, los porcentajes y los rebasados van en la función data, no
aguas arriba. Así el gráfico y el texto que lo acompaña se calculan desde una
única fuente y no pueden contradecirse.
Variantes
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`,
}); En qué se resuelve
El nodo tal como aparece en el DocumentModel: el JSON que recibe un renderizador. Sin estilos, sin maquetación.
{
"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",
}); En qué se resuelve
El nodo tal como aparece en el DocumentModel: el JSON que recibe un renderizador. Sin estilos, sin maquetación.
{
"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",
}); En qué se resuelve
El nodo tal como aparece en el DocumentModel: el JSON que recibe un renderizador. Sin estilos, sin maquetación.
{
"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.`,
}); En qué se resuelve
El nodo tal como aparece en el DocumentModel: el JSON que recibe un renderizador. Sin estilos, sin maquetación.
{
"id": "peak-times",
"kind": "graph",
"mode": "dynamic",
"graphType": "bar",
"placeholder": "Your busiest hours, Monday to Sunday"
} Qué se le pide al endpoint
Resuelto con los mismos datos de ejemplo. Una compilación de vista previa se detiene en el marcador de posición; una compilación en el momento de la petición envía estos.
- 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.
Notas
graphTypevalebarpor defecto en ambos helpers.- En un gráfico dinámico la forma se sigue fijando en local. El endpoint aporta
las cifras y puede sobrescribir
graphTypeycaptionen su respuesta.