Ir al contenido
Docxcelerate

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`,
});
graph · bar Open ↗
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",
});
graph · line Open ↗
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",
});
graph · pie Open ↗
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.`,
});
graph · dynamic Open ↗
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

  • graphType vale bar por 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 graphType y caption en su respuesta.

Editar esta página en GitHub ↗