Узлы
Graph
Графики, объявленные как данные — столбцы, линия или круг — из ваших цифр или цифр эндпоинта.
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.
- Хелперы
- graph
- Вид узла
- graph
- Категория
- Данные
- Разрешается
- Both
- Дочерние узлы
- None.
| Опция | Тип | Что делает |
|---|---|---|
id обязательно | 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 обязательно | 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 обязательно | (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(). |
Объявляется, а не рисуется
Узел графика содержит числа и форму, но никогда — изображение:
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",
});
Нести цифры, а не картинку, означает, что одно объявление обслуживает любой рендерер, читаемо различается между сборками и может быть проверено в тесте.
Форма полезной нагрузки — ваша
data — это JsonObject, и фреймворк никогда в него не заглядывает.
Использованная здесь форма { labels, series } — соглашение, а не схема:
берите то, чего ждёт ваш рендерер, и держите это единообразным в пределах
проекта.
Одно с полезной нагрузкой всё же происходит: строковые значения пропускаются
через рендерер шаблонов, как и проза. Подпись, содержащая {{derived.total}},
будет разрешена.
Вычисления внутри data
Нарастающие итоги, проценты и пересчёт базы — дело функции data, а не того,
что выше по течению. Тогда график и текст рядом с ним считаются из одного
источника и не могут разойтись.
Варианты
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`,
}); Во что это разрешается
Узел в том виде, в каком он попадает в DocumentModel: JSON, который получает рендерер. Без стилей и без вёрстки.
{
"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",
}); Во что это разрешается
Узел в том виде, в каком он попадает в DocumentModel: JSON, который получает рендерер. Без стилей и без вёрстки.
{
"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",
}); Во что это разрешается
Узел в том виде, в каком он попадает в DocumentModel: JSON, который получает рендерер. Без стилей и без вёрстки.
{
"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.`,
}); Во что это разрешается
Узел в том виде, в каком он попадает в DocumentModel: JSON, который получает рендерер. Без стилей и без вёрстки.
{
"id": "peak-times",
"kind": "graph",
"mode": "dynamic",
"graphType": "bar",
"placeholder": "Your busiest hours, Monday to Sunday"
} О чём просят эндпоинт
Разрешено на тех же тестовых данных. Сборка предпросмотра останавливается на заполнителе; сборка во время запроса отправляет их.
- 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.
Примечания
- У обоих хелперов
graphTypeпо умолчанию равенbar. - У динамического графика форма всё равно фиксируется локально. Эндпоинт даёт
цифры и может переопределить
graphTypeиcaptionв своём ответе.