Перейти к содержимому
Docxcelerate

Начните отсюда

Как писать узлы

Узел — это небольшой компонент, который принимает свои данные и возвращает то, что хочет сказать.

Документ — это дерево компонентов. Каждый возвращает узел — абзац, изображение, график — и каждый живёт в собственном файле в nodes/. Документы становятся длинными, и шаблон, вставляющий текст прямо в себя, перестаёт быть читаемым примерно на половине пути.

Сгенерируйте один

dxcl document node documents/welcome next-steps --type paragraph

Это запишет nodes/next-steps.node.tsx и добавит его в nodes/index.ts. --type принимает paragraph, image или graph; запустите команду без аргументов, чтобы вас спросили.

Размещение намеренно оставлено вам. Откройте document.tsx и добавьте компонент в то место документа, которому он принадлежит:

/** @jsxImportSource docxcelerate/template */
import { Document, Section, template } from "docxcelerate/template";
import * as Nodes from "./nodes/index.ts";
import type { DocumentData } from "./types.ts";

export const documentTemplate = template<DocumentData>(
  <Document id="welcome" title="Welcome">
    <Section id="opening" title="Opening">
      <Nodes.Greeting />
    </Section>
    <Section id="closing" title="Closing">
      <Nodes.NextSteps />
    </Section>
  </Document>,
);

Сохраните — и предпросмотр перезагрузится с новым узлом на месте.

Данные приходят через useState

/** @jsxImportSource docxcelerate/template */
import { Paragraph, useState } from "docxcelerate/template";
import type { DocumentData } from "../types.ts";

export const Greeting: Paragraph = () => {
  const [state] = useState((data: DocumentData) => ({
    name: data.recipientName,
  }));

  return <Paragraph id="greeting">Hello {state.name},</Paragraph>;
};

useState — это место, где данные входят в компонент, и единственное такое место, так что от чего зависит узел, записано в одном объявлении, а не размазано по коду, который это читает.

Инициализатор — обычный TypeScript. Языка шаблонов нет, поэтому условия, форматирование и импорты просто доступны, а if, возвращающий другой абзац, — в точности то, чем выглядит.

Paragraph называет и элемент, и тип компонента, поэтому const Greeting: Paragraph говорит, что этот узел даёт, — а вернуть из него <Section> будет ошибкой компиляции.

Текст, который вы хотите получить сгенерированным

Некоторые абзацы нельзя написать заранее, потому что то, что они должны сказать, зависит от человека, который их получит. Задайте вместо текста промпты и заполнитель, чтобы предпросмотр оставался читаемым:

export const TutorNote: Paragraph = () => {
  const [state] = useState((data: DocumentData) => ({
    applicantName: data.applicantName,
    interviewer: data.interviewer,
  }));

  useSetPrompts({
    generalPrompt: `Write two warm sentences about ${state.applicantName}'s interview.`,
  });
  useSetPlaceholders(`A note from ${state.interviewer}.`);

  return <Paragraph id="tutor-note" />;
};

Ничего объявлять не нужно, и режим выбирать тоже. Узел, у которого есть текст, можно получить на вашей машине; узлу, у которого есть только промпты, нужен движок. Компонент решает, чем он является, тем, что он задал; сборка это выводит, а собранный пакет говорит, какие узлы движку предстоит разрешить.

Задать и то и другое на одном элементе — ошибка, а не подбрасывание монетки. Промпты можно передать и пропсами, что читается лучше, когда они короткие, и пропсы выигрывают у хука — вызывающая сторона может переопределить то, что поставил вокруг общий хук.

Доступны четыре слота промптов. Обязателен только generalPrompt:

СлотНазначение
generalPromptЧто узел должен сказать
infoPromptКонтекст, который модель должна иметь, но не пересказывать
negativePromptЧего избегать
systemPromptУказания о роли и тоне

Image и Graph работают так же: дайте src или data — и узел разрешится локально; дайте промпты — и нет.

Что показывает предпросмотр

Сборка для предпросмотра разрешает узлы с промптами в их заполнители и никогда — в сгенерированный текст. Предпросмотр остаётся детерминированным и бесплатным, так что вы можете дорабатывать структуру и оформление без единого запроса, покидающего вашу машину.

Заполнитель — ещё и полезная дисциплина: если документ нечитаем с заполнителями на месте, его структура делает слишком мало работы.

usePlaceholderData даёт вам подставные имена, даты и цифры, засеянные от места, где стоит компонент, — тот же узел каждый раз показывает те же значения. Предпросмотр, который перетасовывает себя при каждой сборке, никто не сможет вычитать.

Идентификаторы

id — это то, как движок адресует узел и как два артефакта сборки совмещаются в диффе, поэтому считайте переименование ломающим изменением.

Его можно не указывать. Узел без id получает его из своего места, и это избавляет ветвления и списки от требования придумывать имена. Чего нельзя — использовать один дважды: два узла, претендующих на один id, — ошибка с указанием обеих позиций, а не гонка, которую выигрывает последний.

Куда дальше


Изменить эту страницу на GitHub ↗