@meistrari/minuta-editor 1.3.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,11 +1,33 @@
1
- import { f as footnoteHtmlToRuns, p as pocLoneCellIndex, a as pocLooksLikeTitle } from '../shared/minuta-editor.d9d0c0ce.mjs';
2
- import { BorderStyle, TextRun, ExternalHyperlink, Paragraph, Document, Header, Packer, AlignmentType, LineRuleType, Table, TableRow, TableCell, WidthType, TableLayoutType, Footer, FootnoteReferenceRun, VerticalAlign, ImageRun, HorizontalPositionRelativeFrom, VerticalPositionRelativeFrom, TextWrappingType, FrameAnchorType, PageNumber, HeadingLevel } from 'docx';
1
+ import { f as footnoteHtmlToRuns, p as pocLoneCellIndex, a as formatTopicNumber, b as pocLooksLikeTitle, T as TOPIC_DEFAULT_GROUP } from '../shared/minuta-editor.b3351035.mjs';
2
+ import { BorderStyle, TextRun, ExternalHyperlink, Paragraph, Document, LevelFormat, AlignmentType, LevelSuffix, Header, Packer, LineRuleType, Table, TableRow, TableCell, WidthType, TableLayoutType, Footer, FootnoteReferenceRun, VerticalAlign, ImageRun, HorizontalPositionRelativeFrom, VerticalPositionRelativeFrom, TextWrappingType, FrameAnchorType, PageNumber, HeadingLevel } from 'docx';
3
3
  import '@tiptap/pm/state';
4
4
  import '@tiptap/pm/view';
5
5
 
6
6
  const A4 = { width: 11906, height: 16838 };
7
7
  const PX_TO_TWIP = 15;
8
8
  const PX_PER_PT = 96 / 72;
9
+ function topicSequenceFor(ctx, group, roman, number) {
10
+ const key = group || TOPIC_DEFAULT_GROUP;
11
+ let seq = ctx.topicSequences.get(key);
12
+ if (!seq || seq.roman !== roman || seq.next !== number) {
13
+ const reference = `cdb-topic-${ctx.topicNumbering.size + 1}`;
14
+ ctx.topicNumbering.set(reference, { roman, start: number });
15
+ seq = { reference, roman, next: number };
16
+ ctx.topicSequences.set(key, seq);
17
+ }
18
+ seq.next = number + 1;
19
+ return seq.reference;
20
+ }
21
+ function leadingTopicNumber(node) {
22
+ for (let i = 0; i < node.childCount; i++) {
23
+ const child = node.child(i);
24
+ if (child.isText && !child.text?.trim())
25
+ continue;
26
+ const number = Number(child.attrs.number);
27
+ return child.type.name === "topicNumber" && !child.attrs.ref && Number.isInteger(number) && number >= 1 ? child : null;
28
+ }
29
+ return null;
30
+ }
9
31
  function getProp(style, prop) {
10
32
  if (!style)
11
33
  return void 0;
@@ -185,9 +207,15 @@ function withMarks(base, marks) {
185
207
  }
186
208
  return info;
187
209
  }
188
- function inlineToRuns(node, base, ctx) {
210
+ function inlineToRuns(node, base, ctx, skip) {
189
211
  const runs = [];
190
212
  node.forEach((child) => {
213
+ if (skip && child === skip)
214
+ return;
215
+ if (child.type.name === "topicNumber") {
216
+ runs.push(new TextRun({ text: formatTopicNumber(child.attrs.number, child.attrs.roman), ...runOptions(withMarks(base, child.marks)) }));
217
+ return;
218
+ }
191
219
  if (child.type.name === "footnoteRef") {
192
220
  const text2 = String(child.attrs.text ?? "").trim();
193
221
  if (ctx && text2) {
@@ -327,10 +355,18 @@ function textBlockToParagraph(node, pos, ctx, opts = {}) {
327
355
  if (spacing.before && !breakBefore)
328
356
  spacing.before = Math.max(0, spacing.before - ctx.lastAfterTw);
329
357
  ctx.lastAfterTw = spacing.after ?? 0;
330
- const runs = inlineToRuns(node, base, ctx);
358
+ const topic = leadingTopicNumber(node);
359
+ let numbering;
360
+ if (topic) {
361
+ const reference = topicSequenceFor(ctx, topic.attrs.group, Boolean(topic.attrs.roman), Number(topic.attrs.number));
362
+ numbering = { reference, level: 0, custom: true };
363
+ }
364
+ const runs = inlineToRuns(node, base, ctx, topic);
331
365
  const keepNext = Boolean(opts.heading || m && pocLooksLikeTitle(m.el)) || void 0;
332
366
  const paragraph = new Paragraph({
333
367
  children: runs,
368
+ numbering,
369
+ run: numbering ? runOptions(topic ? withMarks(base, topic.marks) : base) : void 0,
334
370
  // sem alinhamento explícito, corpo de peça vai justificado (o Word
335
371
  // usaria esquerda e o .docx sairia diferente da praxe e do preview)
336
372
  alignment: m?.align ?? parseAlignment(style) ?? (opts.heading ? void 0 : AlignmentType.JUSTIFIED),
@@ -340,7 +376,7 @@ function textBlockToParagraph(node, pos, ctx, opts = {}) {
340
376
  pageBreakBefore: breakBefore,
341
377
  keepNext
342
378
  });
343
- ctx.meta.set(paragraph, { spacer: runs.length === 0 && !opts.heading, breakBefore });
379
+ ctx.meta.set(paragraph, { spacer: runs.length === 0 && !numbering && !opts.heading, breakBefore });
344
380
  return paragraph;
345
381
  }
346
382
  async function srcToDataUrl(src) {
@@ -963,22 +999,13 @@ async function nodeToDocx(node, pos, contentWidthPx, ctx) {
963
999
  case "table":
964
1000
  return tableToDocx(node, pos, contentWidthPx, ctx);
965
1001
  case "missingImage": {
966
- const aviso = new Paragraph({
967
- children: [new TextRun({
968
- text: `A imagem ${node.attrs.variableId ?? ""} est\xE1 faltando`,
969
- italics: true,
970
- color: "773613"
971
- })],
1002
+ const lugar = new Paragraph({
1003
+ children: [],
972
1004
  alignment: AlignmentType.CENTER,
973
- border: {
974
- top: { style: BorderStyle.DASHED, size: 4, color: "DF8B0D" },
975
- bottom: { style: BorderStyle.DASHED, size: 4, color: "DF8B0D" },
976
- left: { style: BorderStyle.DASHED, size: 4, color: "DF8B0D" },
977
- right: { style: BorderStyle.DASHED, size: 4, color: "DF8B0D" }
978
- },
1005
+ spacing: { after: 0, before: 0 },
979
1006
  pageBreakBefore: consumeBreak(ctx)
980
1007
  });
981
- return [aviso];
1008
+ return [lugar];
982
1009
  }
983
1010
  case "horizontalRule":
984
1011
  return [new Paragraph({ children: [], border: { bottom: { style: BorderStyle.SINGLE, size: 4, color: "AAAAAA" } } })];
@@ -1326,7 +1353,9 @@ async function pocExportDocx(editor, margins, chrome, options = {}) {
1326
1353
  pending: { breakBefore: false },
1327
1354
  lastAfterTw: 0,
1328
1355
  meta: /* @__PURE__ */ new WeakMap(),
1329
- footnotes: []
1356
+ footnotes: [],
1357
+ topicNumbering: /* @__PURE__ */ new Map(),
1358
+ topicSequences: /* @__PURE__ */ new Map()
1330
1359
  };
1331
1360
  const children = [];
1332
1361
  let pos = 0;
@@ -1375,6 +1404,22 @@ async function pocExportDocx(editor, margins, chrome, options = {}) {
1375
1404
  }
1376
1405
  },
1377
1406
  footnotes: Object.keys(footnotes).length ? footnotes : void 0,
1407
+ numbering: ctx.topicNumbering.size ? {
1408
+ config: [...ctx.topicNumbering].map(([reference, { roman, start }]) => ({
1409
+ reference,
1410
+ levels: [{
1411
+ level: 0,
1412
+ format: roman ? LevelFormat.UPPER_ROMAN : LevelFormat.DECIMAL,
1413
+ text: "%1",
1414
+ start,
1415
+ alignment: AlignmentType.START,
1416
+ // o ". " depois do número é texto do template; e sem
1417
+ // recuo/tab o parágrafo fica igual ao preview
1418
+ suffix: LevelSuffix.NOTHING,
1419
+ style: { paragraph: { indent: { left: 0, hanging: 0 } } }
1420
+ }]
1421
+ }))
1422
+ } : void 0,
1378
1423
  sections: [{
1379
1424
  properties: {
1380
1425
  page: {
package/dist/docx.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  export { pocExportDocx } from './chunks/docxExport.mjs';
2
- import './shared/minuta-editor.d9d0c0ce.mjs';
2
+ import './shared/minuta-editor.b3351035.mjs';
3
3
  import '@tiptap/pm/state';
4
4
  import '@tiptap/pm/view';
5
5
  import 'docx';
package/dist/index.d.mts CHANGED
@@ -17,12 +17,12 @@
17
17
  /// <reference types="@tiptap/extension-text" />
18
18
  /// <reference types="@tiptap/extension-table" />
19
19
  /// <reference types="@tiptap/extension-bubble-menu" />
20
- import { Node, Editor, Mark, Extension } from '@tiptap/core';
20
+ import { Node, Editor, Extension, Mark } from '@tiptap/core';
21
+ import { Transaction } from '@tiptap/pm/state';
21
22
  import { EditorView } from '@tiptap/pm/view';
22
23
  import { P as PocPageConfig } from './shared/minuta-editor.9e65d163.mjs';
23
24
  export { e as PAGINATE_META, a as PocDocxChrome, b as PocDocxExportOptions, s as PocLoneRow, d as PocPageBreak, c as PocPageNote, v as PocPaginationPlugin, u as breaksEqual, h as buildPocFooterEl, g as buildPocHeaderEl, j as buildPocNotesEl, l as computePocBreaks, t as computePocLoneRows, i as isPocChromeCanvas, n as isPocFlexRow, f as pocChromeFlowHeight, m as pocDescendsInto, r as pocLoneCellIndex, k as pocLooksLikeTitle, o as pocNodeHasVisibleContent, q as pocNodePaintsBox, p as pocPageKey } from './shared/minuta-editor.9e65d163.mjs';
24
25
  import '@tiptap/pm/model';
25
- import '@tiptap/pm/state';
26
26
 
27
27
  /**
28
28
  * Variáveis de template da minuta — o formato do placeholder e a resolução dele.
@@ -162,6 +162,29 @@ declare const PocTemplateVariable: Node<{
162
162
  declare const PocFootnoteRef: Node<any, any>;
163
163
  /** re-sequencia a numeração das chamadas de nota na ordem do documento */
164
164
  declare function renumberPocFootnotes(editor: Editor): void;
165
+ /**
166
+ * Número de tópico/parágrafo — o `<topic group="paragrafo"/>` do template já
167
+ * resolvido pelo render. É NÓ, e não texto, por dois motivos: quem edita a
168
+ * minuta apaga, move ou insere um parágrafo numerado e a sequência do grupo se
169
+ * refaz sozinha (`PocTopicRenumber`), e o export leva o número como numeração
170
+ * nativa do Word, que também se refaz quando o advogado apaga um parágrafo lá.
171
+ * Contrato do marcador em `utils/topicNumber.ts`.
172
+ */
173
+ declare const PocTopicNumber: Node<any, any>;
174
+ /**
175
+ * Re-sequencia os números de tópico na ordem do documento, por grupo, dentro
176
+ * da transação dada. Devolve se mudou algo. Um nó `ref` (latest) recebe o
177
+ * último número já contado do grupo — a mesma regra do render.
178
+ */
179
+ declare function renumberPocTopicsIn(tr: Transaction): boolean;
180
+ /** re-sequencia a numeração dos tópicos no editor dado (uso pontual; o plugin faz isso a cada mudança) */
181
+ declare function renumberPocTopics(editor: Editor): void;
182
+ /**
183
+ * A cada transação que muda o documento, refaz a sequência dos tópicos. Só
184
+ * despacha quando algum número de fato mudou, então digitar dentro de um
185
+ * parágrafo não custa nada além da varredura.
186
+ */
187
+ declare const PocTopicRenumber: Extension<any, any>;
165
188
  declare const PocStyledDiv: Node<any, any>;
166
189
  declare const PocInlineStyle: Mark<any, any>;
167
190
  declare const PocUnderline: Mark<any, any>;
@@ -181,7 +204,7 @@ declare function buildPocExtensions(pageConfig?: PocPageConfig, options?: {
181
204
  * ainda no editor — o seletor de variável — depende disso.
182
205
  */
183
206
  handleKeyDown?: (view: EditorView, event: KeyboardEvent) => boolean;
184
- }): (Node<any, any> | Mark<any, any> | Extension<any, any>)[];
207
+ }): (Node<any, any> | Extension<any, any> | Mark<any, any>)[];
185
208
 
186
209
  /**
187
210
  * O editor de minuta como coisa montável em qualquer lugar: `createMinutaEditor(el, opts)`.
@@ -480,6 +503,40 @@ declare function refreshVaultImages(editor: Editor, resolveUrl: (vaultUrl: strin
480
503
  */
481
504
  declare function sanitizeRichHtml(html: string | null | undefined): string;
482
505
 
506
+ /**
507
+ * Número de tópico/parágrafo — o `<topic group="paragrafo"/>` dos templates.
508
+ *
509
+ * O render resolve o número, mas em vez de achatá-lo em texto ("12") o guarda
510
+ * num `<span data-topic-number>`: no editor ele vira o nó `topicNumber`, que se
511
+ * re-sequencia sozinho quando um parágrafo é apagado, movido ou inserido, e no
512
+ * DOCX vira numeração nativa do Word (que também se refaz quando o advogado
513
+ * apaga um parágrafo lá). Este é o contrato do marcador — server, client e
514
+ * pacote falam por ele.
515
+ */
516
+ declare const TOPIC_NUMBER_ATTR = "data-topic-number";
517
+ declare const TOPIC_GROUP_ATTR = "data-topic-group";
518
+ declare const TOPIC_ROMAN_ATTR = "data-topic-roman";
519
+ declare const TOPIC_REF_ATTR = "data-topic-ref";
520
+ declare const TOPIC_DEFAULT_GROUP = "default";
521
+ interface TopicNumberInfo {
522
+ /** valor numérico (o que se conta) */
523
+ number: number;
524
+ /** grupo do contador (`group` da tag); sem grupo = `default` */
525
+ group: string;
526
+ /** exibido em algarismos romanos maiúsculos */
527
+ roman: boolean;
528
+ /** `<topic latest="true">`: repete o último número do grupo em vez de avançar */
529
+ ref: boolean;
530
+ }
531
+ declare function toRoman(num: number): string;
532
+ declare function fromRoman(text: string): number | null;
533
+ /** texto exibido para o número (decimal ou romano) */
534
+ declare function formatTopicNumber(number: number, roman: boolean): string;
535
+ /** o marcador em HTML — é o que o render emite e o que o editor lê e grava */
536
+ declare function topicNumberHtml(info: TopicNumberInfo): string;
537
+ /** lê o marcador de um elemento (`span[data-topic-number]`) */
538
+ declare function readTopicNumber(el: Element): TopicNumberInfo;
539
+
483
540
  /**
484
541
  * A tag de imagem que o editor insere após o upload: guarda o `src` presignado
485
542
  * (funciona agora) E o `data-vault-src` durável (`vault://…`). Quem re-resolve
@@ -504,5 +561,5 @@ declare function collectVaultRefs(html: string): string[];
504
561
  /** aplica as URLs resolvidas às `<img>` do trecho; tags sem resolução ficam como estão */
505
562
  declare function renewVaultImageTags(html: string, resolved: Map<string, string>): string;
506
563
 
507
- export { A4_HEIGHT_PX, A4_WIDTH_PX, DEFAULT_MARGINS, DOC_FONTS, ICON_PATHS, POC_ASSET_SWAP, PocDocument, PocFootnoteRef, PocHtmlIsland, PocInlineStyle, PocMinutaBlock, PocMissingImage, PocPageConfig, PocStylePassthrough, PocStyledDiv, PocTemplateVariable, PocUnderline, PocVariableChip, buildPocExtensions, collectVaultRefs, createMinutaEditor, decodeVaultAttr, dispatchVariableClick, escapeHtml, escapeVaultAttr, extractTemplateVariables, footnoteHtmlToRuns, footnoteSizePt, iconSvg, refreshVaultImages, renewVaultImageTags, renumberPocFootnotes, resolveTemplateVariables, sanitizeFootnoteHtml, sanitizeRichHtml, shouldMoveBreakToHeading, swapVaultSrc, variableChipHtml, vaultImageHtml, vaultRefOf };
508
- export type { FootnoteRun, MinutaChromeText, MinutaEditor, MinutaEditorOptions, MinutaHeader, MinutaMargins, MinutaVariableCatalog, MinutaVariableDef, MinutaVariableRef, PocKeepWithNextItem, RefreshVaultImagesOptions, VariableClickKind };
564
+ export { A4_HEIGHT_PX, A4_WIDTH_PX, DEFAULT_MARGINS, DOC_FONTS, ICON_PATHS, POC_ASSET_SWAP, PocDocument, PocFootnoteRef, PocHtmlIsland, PocInlineStyle, PocMinutaBlock, PocMissingImage, PocPageConfig, PocStylePassthrough, PocStyledDiv, PocTemplateVariable, PocTopicNumber, PocTopicRenumber, PocUnderline, PocVariableChip, TOPIC_DEFAULT_GROUP, TOPIC_GROUP_ATTR, TOPIC_NUMBER_ATTR, TOPIC_REF_ATTR, TOPIC_ROMAN_ATTR, buildPocExtensions, collectVaultRefs, createMinutaEditor, decodeVaultAttr, dispatchVariableClick, escapeHtml, escapeVaultAttr, extractTemplateVariables, footnoteHtmlToRuns, footnoteSizePt, formatTopicNumber, fromRoman, iconSvg, readTopicNumber, refreshVaultImages, renewVaultImageTags, renumberPocFootnotes, renumberPocTopics, renumberPocTopicsIn, resolveTemplateVariables, sanitizeFootnoteHtml, sanitizeRichHtml, shouldMoveBreakToHeading, swapVaultSrc, toRoman, topicNumberHtml, variableChipHtml, vaultImageHtml, vaultRefOf };
565
+ export type { FootnoteRun, MinutaChromeText, MinutaEditor, MinutaEditorOptions, MinutaHeader, MinutaMargins, MinutaVariableCatalog, MinutaVariableDef, MinutaVariableRef, PocKeepWithNextItem, RefreshVaultImagesOptions, TopicNumberInfo, VariableClickKind };
package/dist/index.d.ts CHANGED
@@ -17,12 +17,12 @@
17
17
  /// <reference types="@tiptap/extension-text" />
18
18
  /// <reference types="@tiptap/extension-table" />
19
19
  /// <reference types="@tiptap/extension-bubble-menu" />
20
- import { Node, Editor, Mark, Extension } from '@tiptap/core';
20
+ import { Node, Editor, Extension, Mark } from '@tiptap/core';
21
+ import { Transaction } from '@tiptap/pm/state';
21
22
  import { EditorView } from '@tiptap/pm/view';
22
23
  import { P as PocPageConfig } from './shared/minuta-editor.9e65d163.js';
23
24
  export { e as PAGINATE_META, a as PocDocxChrome, b as PocDocxExportOptions, s as PocLoneRow, d as PocPageBreak, c as PocPageNote, v as PocPaginationPlugin, u as breaksEqual, h as buildPocFooterEl, g as buildPocHeaderEl, j as buildPocNotesEl, l as computePocBreaks, t as computePocLoneRows, i as isPocChromeCanvas, n as isPocFlexRow, f as pocChromeFlowHeight, m as pocDescendsInto, r as pocLoneCellIndex, k as pocLooksLikeTitle, o as pocNodeHasVisibleContent, q as pocNodePaintsBox, p as pocPageKey } from './shared/minuta-editor.9e65d163.js';
24
25
  import '@tiptap/pm/model';
25
- import '@tiptap/pm/state';
26
26
 
27
27
  /**
28
28
  * Variáveis de template da minuta — o formato do placeholder e a resolução dele.
@@ -162,6 +162,29 @@ declare const PocTemplateVariable: Node<{
162
162
  declare const PocFootnoteRef: Node<any, any>;
163
163
  /** re-sequencia a numeração das chamadas de nota na ordem do documento */
164
164
  declare function renumberPocFootnotes(editor: Editor): void;
165
+ /**
166
+ * Número de tópico/parágrafo — o `<topic group="paragrafo"/>` do template já
167
+ * resolvido pelo render. É NÓ, e não texto, por dois motivos: quem edita a
168
+ * minuta apaga, move ou insere um parágrafo numerado e a sequência do grupo se
169
+ * refaz sozinha (`PocTopicRenumber`), e o export leva o número como numeração
170
+ * nativa do Word, que também se refaz quando o advogado apaga um parágrafo lá.
171
+ * Contrato do marcador em `utils/topicNumber.ts`.
172
+ */
173
+ declare const PocTopicNumber: Node<any, any>;
174
+ /**
175
+ * Re-sequencia os números de tópico na ordem do documento, por grupo, dentro
176
+ * da transação dada. Devolve se mudou algo. Um nó `ref` (latest) recebe o
177
+ * último número já contado do grupo — a mesma regra do render.
178
+ */
179
+ declare function renumberPocTopicsIn(tr: Transaction): boolean;
180
+ /** re-sequencia a numeração dos tópicos no editor dado (uso pontual; o plugin faz isso a cada mudança) */
181
+ declare function renumberPocTopics(editor: Editor): void;
182
+ /**
183
+ * A cada transação que muda o documento, refaz a sequência dos tópicos. Só
184
+ * despacha quando algum número de fato mudou, então digitar dentro de um
185
+ * parágrafo não custa nada além da varredura.
186
+ */
187
+ declare const PocTopicRenumber: Extension<any, any>;
165
188
  declare const PocStyledDiv: Node<any, any>;
166
189
  declare const PocInlineStyle: Mark<any, any>;
167
190
  declare const PocUnderline: Mark<any, any>;
@@ -181,7 +204,7 @@ declare function buildPocExtensions(pageConfig?: PocPageConfig, options?: {
181
204
  * ainda no editor — o seletor de variável — depende disso.
182
205
  */
183
206
  handleKeyDown?: (view: EditorView, event: KeyboardEvent) => boolean;
184
- }): (Node<any, any> | Mark<any, any> | Extension<any, any>)[];
207
+ }): (Node<any, any> | Extension<any, any> | Mark<any, any>)[];
185
208
 
186
209
  /**
187
210
  * O editor de minuta como coisa montável em qualquer lugar: `createMinutaEditor(el, opts)`.
@@ -480,6 +503,40 @@ declare function refreshVaultImages(editor: Editor, resolveUrl: (vaultUrl: strin
480
503
  */
481
504
  declare function sanitizeRichHtml(html: string | null | undefined): string;
482
505
 
506
+ /**
507
+ * Número de tópico/parágrafo — o `<topic group="paragrafo"/>` dos templates.
508
+ *
509
+ * O render resolve o número, mas em vez de achatá-lo em texto ("12") o guarda
510
+ * num `<span data-topic-number>`: no editor ele vira o nó `topicNumber`, que se
511
+ * re-sequencia sozinho quando um parágrafo é apagado, movido ou inserido, e no
512
+ * DOCX vira numeração nativa do Word (que também se refaz quando o advogado
513
+ * apaga um parágrafo lá). Este é o contrato do marcador — server, client e
514
+ * pacote falam por ele.
515
+ */
516
+ declare const TOPIC_NUMBER_ATTR = "data-topic-number";
517
+ declare const TOPIC_GROUP_ATTR = "data-topic-group";
518
+ declare const TOPIC_ROMAN_ATTR = "data-topic-roman";
519
+ declare const TOPIC_REF_ATTR = "data-topic-ref";
520
+ declare const TOPIC_DEFAULT_GROUP = "default";
521
+ interface TopicNumberInfo {
522
+ /** valor numérico (o que se conta) */
523
+ number: number;
524
+ /** grupo do contador (`group` da tag); sem grupo = `default` */
525
+ group: string;
526
+ /** exibido em algarismos romanos maiúsculos */
527
+ roman: boolean;
528
+ /** `<topic latest="true">`: repete o último número do grupo em vez de avançar */
529
+ ref: boolean;
530
+ }
531
+ declare function toRoman(num: number): string;
532
+ declare function fromRoman(text: string): number | null;
533
+ /** texto exibido para o número (decimal ou romano) */
534
+ declare function formatTopicNumber(number: number, roman: boolean): string;
535
+ /** o marcador em HTML — é o que o render emite e o que o editor lê e grava */
536
+ declare function topicNumberHtml(info: TopicNumberInfo): string;
537
+ /** lê o marcador de um elemento (`span[data-topic-number]`) */
538
+ declare function readTopicNumber(el: Element): TopicNumberInfo;
539
+
483
540
  /**
484
541
  * A tag de imagem que o editor insere após o upload: guarda o `src` presignado
485
542
  * (funciona agora) E o `data-vault-src` durável (`vault://…`). Quem re-resolve
@@ -504,5 +561,5 @@ declare function collectVaultRefs(html: string): string[];
504
561
  /** aplica as URLs resolvidas às `<img>` do trecho; tags sem resolução ficam como estão */
505
562
  declare function renewVaultImageTags(html: string, resolved: Map<string, string>): string;
506
563
 
507
- export { A4_HEIGHT_PX, A4_WIDTH_PX, DEFAULT_MARGINS, DOC_FONTS, ICON_PATHS, POC_ASSET_SWAP, PocDocument, PocFootnoteRef, PocHtmlIsland, PocInlineStyle, PocMinutaBlock, PocMissingImage, PocPageConfig, PocStylePassthrough, PocStyledDiv, PocTemplateVariable, PocUnderline, PocVariableChip, buildPocExtensions, collectVaultRefs, createMinutaEditor, decodeVaultAttr, dispatchVariableClick, escapeHtml, escapeVaultAttr, extractTemplateVariables, footnoteHtmlToRuns, footnoteSizePt, iconSvg, refreshVaultImages, renewVaultImageTags, renumberPocFootnotes, resolveTemplateVariables, sanitizeFootnoteHtml, sanitizeRichHtml, shouldMoveBreakToHeading, swapVaultSrc, variableChipHtml, vaultImageHtml, vaultRefOf };
508
- export type { FootnoteRun, MinutaChromeText, MinutaEditor, MinutaEditorOptions, MinutaHeader, MinutaMargins, MinutaVariableCatalog, MinutaVariableDef, MinutaVariableRef, PocKeepWithNextItem, RefreshVaultImagesOptions, VariableClickKind };
564
+ export { A4_HEIGHT_PX, A4_WIDTH_PX, DEFAULT_MARGINS, DOC_FONTS, ICON_PATHS, POC_ASSET_SWAP, PocDocument, PocFootnoteRef, PocHtmlIsland, PocInlineStyle, PocMinutaBlock, PocMissingImage, PocPageConfig, PocStylePassthrough, PocStyledDiv, PocTemplateVariable, PocTopicNumber, PocTopicRenumber, PocUnderline, PocVariableChip, TOPIC_DEFAULT_GROUP, TOPIC_GROUP_ATTR, TOPIC_NUMBER_ATTR, TOPIC_REF_ATTR, TOPIC_ROMAN_ATTR, buildPocExtensions, collectVaultRefs, createMinutaEditor, decodeVaultAttr, dispatchVariableClick, escapeHtml, escapeVaultAttr, extractTemplateVariables, footnoteHtmlToRuns, footnoteSizePt, formatTopicNumber, fromRoman, iconSvg, readTopicNumber, refreshVaultImages, renewVaultImageTags, renumberPocFootnotes, renumberPocTopics, renumberPocTopicsIn, resolveTemplateVariables, sanitizeFootnoteHtml, sanitizeRichHtml, shouldMoveBreakToHeading, swapVaultSrc, toRoman, topicNumberHtml, variableChipHtml, vaultImageHtml, vaultRefOf };
565
+ export type { FootnoteRun, MinutaChromeText, MinutaEditor, MinutaEditorOptions, MinutaHeader, MinutaMargins, MinutaVariableCatalog, MinutaVariableDef, MinutaVariableRef, PocKeepWithNextItem, RefreshVaultImagesOptions, TopicNumberInfo, VariableClickKind };
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { Node as Node$1, mergeAttributes, Mark, Extension, Editor } from '@tiptap/core';
1
+ import { Node as Node$1, mergeAttributes, Extension, Mark, Editor } from '@tiptap/core';
2
2
  import BubbleMenu from '@tiptap/extension-bubble-menu';
3
3
  import Bold from '@tiptap/extension-bold';
4
4
  import Blockquote from '@tiptap/extension-blockquote';
@@ -22,8 +22,8 @@ import TableCell from '@tiptap/extension-table-cell';
22
22
  import TableHeader from '@tiptap/extension-table-header';
23
23
  import TableRow from '@tiptap/extension-table-row';
24
24
  import { Plugin } from '@tiptap/pm/state';
25
- import { s as sanitizeRichHtml, P as PocPaginationPlugin, b as sanitizeFootnoteHtml, c as computePocBreaks, d as pocPageKey, e as breaksEqual, g as PAGINATE_META } from './shared/minuta-editor.d9d0c0ce.mjs';
26
- export { k as buildPocFooterEl, j as buildPocHeaderEl, l as buildPocNotesEl, r as computePocLoneRows, f as footnoteHtmlToRuns, t as footnoteSizePt, i as isPocChromeCanvas, n as isPocFlexRow, h as pocChromeFlowHeight, m as pocDescendsInto, p as pocLoneCellIndex, a as pocLooksLikeTitle, o as pocNodeHasVisibleContent, q as pocNodePaintsBox, u as shouldMoveBreakToHeading } from './shared/minuta-editor.d9d0c0ce.mjs';
25
+ import { s as sanitizeRichHtml, T as TOPIC_DEFAULT_GROUP, c as TOPIC_NUMBER_ATTR, r as readTopicNumber, d as TOPIC_GROUP_ATTR, e as TOPIC_ROMAN_ATTR, g as TOPIC_REF_ATTR, a as formatTopicNumber, P as PocPaginationPlugin, h as sanitizeFootnoteHtml, i as computePocBreaks, j as pocPageKey, k as breaksEqual, l as PAGINATE_META } from './shared/minuta-editor.b3351035.mjs';
26
+ export { q as buildPocFooterEl, o as buildPocHeaderEl, t as buildPocNotesEl, y as computePocLoneRows, f as footnoteHtmlToRuns, z as footnoteSizePt, C as fromRoman, n as isPocChromeCanvas, v as isPocFlexRow, m as pocChromeFlowHeight, u as pocDescendsInto, p as pocLoneCellIndex, b as pocLooksLikeTitle, w as pocNodeHasVisibleContent, x as pocNodePaintsBox, A as shouldMoveBreakToHeading, B as toRoman, D as topicNumberHtml } from './shared/minuta-editor.b3351035.mjs';
27
27
  import '@tiptap/pm/view';
28
28
 
29
29
  const POC_ASSET_SWAP = "pocAssetSwap";
@@ -472,6 +472,76 @@ function renumberPocFootnotes(editor) {
472
472
  if (touched)
473
473
  editor.view.dispatch(tr);
474
474
  }
475
+ const PocTopicNumber = Node$1.create({
476
+ name: "topicNumber",
477
+ inline: true,
478
+ group: "inline",
479
+ atom: true,
480
+ selectable: false,
481
+ addAttributes() {
482
+ return {
483
+ number: { default: 1 },
484
+ group: { default: TOPIC_DEFAULT_GROUP },
485
+ roman: { default: false },
486
+ /** `<topic latest="true">`: repete o último número do grupo em vez de avançar */
487
+ ref: { default: false }
488
+ };
489
+ },
490
+ parseHTML() {
491
+ return [{
492
+ tag: `span[${TOPIC_NUMBER_ATTR}]`,
493
+ priority: 110,
494
+ getAttrs: (el) => readTopicNumber(el)
495
+ }];
496
+ },
497
+ renderHTML({ node }) {
498
+ const attrs = {
499
+ [TOPIC_NUMBER_ATTR]: String(node.attrs.number),
500
+ [TOPIC_GROUP_ATTR]: node.attrs.group || TOPIC_DEFAULT_GROUP,
501
+ class: "poc-topic-number"
502
+ };
503
+ if (node.attrs.roman)
504
+ attrs[TOPIC_ROMAN_ATTR] = "true";
505
+ if (node.attrs.ref)
506
+ attrs[TOPIC_REF_ATTR] = "true";
507
+ return ["span", attrs, formatTopicNumber(node.attrs.number, node.attrs.roman)];
508
+ }
509
+ });
510
+ function renumberPocTopicsIn(tr) {
511
+ const counters = {};
512
+ let touched = false;
513
+ tr.doc.descendants((node, pos) => {
514
+ if (node.type.name !== "topicNumber")
515
+ return;
516
+ const group = node.attrs.group || TOPIC_DEFAULT_GROUP;
517
+ const number = node.attrs.ref ? counters[group] ?? 0 : counters[group] = (counters[group] ?? 0) + 1;
518
+ if (node.attrs.number !== number) {
519
+ tr.setNodeMarkup(pos, void 0, { ...node.attrs, number });
520
+ touched = true;
521
+ }
522
+ });
523
+ return touched;
524
+ }
525
+ function renumberPocTopics(editor) {
526
+ if (!editor || editor.isDestroyed)
527
+ return;
528
+ const tr = editor.state.tr;
529
+ if (renumberPocTopicsIn(tr))
530
+ editor.view.dispatch(tr);
531
+ }
532
+ const PocTopicRenumber = Extension.create({
533
+ name: "pocTopicRenumber",
534
+ addProseMirrorPlugins() {
535
+ return [new Plugin({
536
+ appendTransaction(transactions, _oldState, newState) {
537
+ if (!transactions.some((t) => t.docChanged))
538
+ return null;
539
+ const tr = newState.tr;
540
+ return renumberPocTopicsIn(tr) ? tr : null;
541
+ }
542
+ })];
543
+ }
544
+ });
475
545
  const PocStyledDiv = Node$1.create({
476
546
  name: "styledDiv",
477
547
  group: "block",
@@ -601,6 +671,8 @@ function buildPocExtensions(pageConfig, options) {
601
671
  PocVariableChip,
602
672
  PocTemplateVariable.configure({ catalog: options?.variables ?? [] }),
603
673
  PocFootnoteRef,
674
+ PocTopicNumber,
675
+ PocTopicRenumber,
604
676
  PocStyledDiv,
605
677
  PocInlineStyle,
606
678
  PocUnderline,
@@ -2091,4 +2163,4 @@ function createMinutaEditor(element, opts) {
2091
2163
  return api;
2092
2164
  }
2093
2165
 
2094
- export { A4_HEIGHT_PX, A4_WIDTH_PX, DEFAULT_MARGINS, DOC_FONTS, ICON_PATHS, PAGINATE_META, POC_ASSET_SWAP, PocDocument, PocFootnoteRef, PocHtmlIsland, PocInlineStyle, PocMinutaBlock, PocMissingImage, PocPaginationPlugin, PocStylePassthrough, PocStyledDiv, PocTemplateVariable, PocUnderline, PocVariableChip, breaksEqual, buildPocExtensions, collectVaultRefs, computePocBreaks, createMinutaEditor, decodeVaultAttr, dispatchVariableClick, escapeHtml, escapeVaultAttr, extractTemplateVariables, iconSvg, pocPageKey, refreshVaultImages, renewVaultImageTags, renumberPocFootnotes, resolveTemplateVariables, sanitizeFootnoteHtml, sanitizeRichHtml, swapVaultSrc, variableChipHtml, vaultImageHtml, vaultRefOf };
2166
+ export { A4_HEIGHT_PX, A4_WIDTH_PX, DEFAULT_MARGINS, DOC_FONTS, ICON_PATHS, PAGINATE_META, POC_ASSET_SWAP, PocDocument, PocFootnoteRef, PocHtmlIsland, PocInlineStyle, PocMinutaBlock, PocMissingImage, PocPaginationPlugin, PocStylePassthrough, PocStyledDiv, PocTemplateVariable, PocTopicNumber, PocTopicRenumber, PocUnderline, PocVariableChip, TOPIC_DEFAULT_GROUP, TOPIC_GROUP_ATTR, TOPIC_NUMBER_ATTR, TOPIC_REF_ATTR, TOPIC_ROMAN_ATTR, breaksEqual, buildPocExtensions, collectVaultRefs, computePocBreaks, createMinutaEditor, decodeVaultAttr, dispatchVariableClick, escapeHtml, escapeVaultAttr, extractTemplateVariables, formatTopicNumber, iconSvg, pocPageKey, readTopicNumber, refreshVaultImages, renewVaultImageTags, renumberPocFootnotes, renumberPocTopics, renumberPocTopicsIn, resolveTemplateVariables, sanitizeFootnoteHtml, sanitizeRichHtml, swapVaultSrc, variableChipHtml, vaultImageHtml, vaultRefOf };
@@ -75,6 +75,84 @@ function sanitizeRichHtml(html) {
75
75
  return tpl.innerHTML;
76
76
  }
77
77
 
78
+ const TOPIC_NUMBER_ATTR = "data-topic-number";
79
+ const TOPIC_GROUP_ATTR = "data-topic-group";
80
+ const TOPIC_ROMAN_ATTR = "data-topic-roman";
81
+ const TOPIC_REF_ATTR = "data-topic-ref";
82
+ const TOPIC_DEFAULT_GROUP = "default";
83
+ function toRoman(num) {
84
+ if (!Number.isFinite(num) || num <= 0)
85
+ return "";
86
+ const numerals = [
87
+ [1e3, "M"],
88
+ [900, "CM"],
89
+ [500, "D"],
90
+ [400, "CD"],
91
+ [100, "C"],
92
+ [90, "XC"],
93
+ [50, "L"],
94
+ [40, "XL"],
95
+ [10, "X"],
96
+ [9, "IX"],
97
+ [5, "V"],
98
+ [4, "IV"],
99
+ [1, "I"]
100
+ ];
101
+ let result = "";
102
+ let rest = Math.floor(num);
103
+ for (const [value, numeral] of numerals) {
104
+ while (rest >= value) {
105
+ result += numeral;
106
+ rest -= value;
107
+ }
108
+ }
109
+ return result;
110
+ }
111
+ function fromRoman(text) {
112
+ const upper = text.trim().toUpperCase();
113
+ if (!upper || !/^M{0,3}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})$/.test(upper))
114
+ return null;
115
+ const values = { I: 1, V: 5, X: 10, L: 50, C: 100, D: 500, M: 1e3 };
116
+ let total = 0;
117
+ for (let i = 0; i < upper.length; i++) {
118
+ const current = values[upper[i]];
119
+ const next = values[upper[i + 1] ?? ""] ?? 0;
120
+ total += current < next ? -current : current;
121
+ }
122
+ return total;
123
+ }
124
+ function formatTopicNumber(number, roman) {
125
+ return roman ? toRoman(number) : String(number);
126
+ }
127
+ function escapeAttr(value) {
128
+ return value.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
129
+ }
130
+ function topicNumberHtml(info) {
131
+ let attrs = `${TOPIC_NUMBER_ATTR}="${info.number}" ${TOPIC_GROUP_ATTR}="${escapeAttr(info.group || TOPIC_DEFAULT_GROUP)}"`;
132
+ if (info.roman)
133
+ attrs += ` ${TOPIC_ROMAN_ATTR}="true"`;
134
+ if (info.ref)
135
+ attrs += ` ${TOPIC_REF_ATTR}="true"`;
136
+ return `<span ${attrs}>${formatTopicNumber(info.number, info.roman)}</span>`;
137
+ }
138
+ function readTopicNumber(el) {
139
+ const roman = el.getAttribute(TOPIC_ROMAN_ATTR) === "true";
140
+ const raw = el.getAttribute(TOPIC_NUMBER_ATTR) ?? "";
141
+ let number = Number.parseInt(raw, 10);
142
+ if (!Number.isFinite(number)) {
143
+ const text = el.textContent?.trim() ?? "";
144
+ number = Number.parseInt(text, 10);
145
+ if (!Number.isFinite(number))
146
+ number = fromRoman(text) ?? 1;
147
+ }
148
+ return {
149
+ number,
150
+ group: el.getAttribute(TOPIC_GROUP_ATTR) || TOPIC_DEFAULT_GROUP,
151
+ roman,
152
+ ref: el.getAttribute(TOPIC_REF_ATTR) === "true"
153
+ };
154
+ }
155
+
78
156
  const INLINE_TAGS = /* @__PURE__ */ new Set(["B", "STRONG", "I", "EM", "U", "SUP", "SUB", "BR"]);
79
157
  function safeHref(value) {
80
158
  const href = (value ?? "").trim();
@@ -819,4 +897,4 @@ function PocPaginationPlugin(cfg) {
819
897
  });
820
898
  }
821
899
 
822
- export { PocPaginationPlugin as P, pocLooksLikeTitle as a, sanitizeFootnoteHtml as b, computePocBreaks as c, pocPageKey as d, breaksEqual as e, footnoteHtmlToRuns as f, PAGINATE_META as g, pocChromeFlowHeight as h, isPocChromeCanvas as i, buildPocHeaderEl as j, buildPocFooterEl as k, buildPocNotesEl as l, pocDescendsInto as m, isPocFlexRow as n, pocNodeHasVisibleContent as o, pocLoneCellIndex as p, pocNodePaintsBox as q, computePocLoneRows as r, sanitizeRichHtml as s, footnoteSizePt as t, shouldMoveBreakToHeading as u };
900
+ export { shouldMoveBreakToHeading as A, toRoman as B, fromRoman as C, topicNumberHtml as D, PocPaginationPlugin as P, TOPIC_DEFAULT_GROUP as T, formatTopicNumber as a, pocLooksLikeTitle as b, TOPIC_NUMBER_ATTR as c, TOPIC_GROUP_ATTR as d, TOPIC_ROMAN_ATTR as e, footnoteHtmlToRuns as f, TOPIC_REF_ATTR as g, sanitizeFootnoteHtml as h, computePocBreaks as i, pocPageKey as j, breaksEqual as k, PAGINATE_META as l, pocChromeFlowHeight as m, isPocChromeCanvas as n, buildPocHeaderEl as o, pocLoneCellIndex as p, buildPocFooterEl as q, readTopicNumber as r, sanitizeRichHtml as s, buildPocNotesEl as t, pocDescendsInto as u, isPocFlexRow as v, pocNodeHasVisibleContent as w, pocNodePaintsBox as x, computePocLoneRows as y, footnoteSizePt as z };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meistrari/minuta-editor",
3
- "version": "1.3.0",
3
+ "version": "1.4.0",
4
4
  "description": "Editor de minuta do defesa.ai sem framework: folha A4 paginada, edição rica, placeholders de variável e export DOCX fiel ao preview — monta em qualquer app (React, Vue, vanilla)",
5
5
  "repository": {
6
6
  "type": "git",