@meistrari/minuta-editor 1.5.0 → 1.7.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.
package/README.md CHANGED
@@ -61,6 +61,13 @@ editor.setImageFallback('dataUri' | 'none')
61
61
  editor.variablesInDocument() // → string[]
62
62
  editor.refreshVaultImages(vaultUrl => Promise<string>) // renova o src das imagens do vault (data-vault-src ou src="vault://"); sem undo/onChange
63
63
 
64
+ // checklist junto da edição (o documento gerado vem do GET /data/case/:id/html?source=blocks)
65
+ editor.stampGenerated() // ao CRIAR a edição a partir do HTML do checklist: marca os blocos como "gerados"
66
+ editor.applyChecklist(htmlGerado, { toggled? }) // → { inserted, removed, regenerated, variablesUpdated, conflicts, … }
67
+ editor.checklistConflicts() // → [{ blockId, blockTitle, kind: 'replace' | 'remove' }]
68
+ editor.resolveChecklistConflict(blockId, 'keep' | 'apply')
69
+ editor.documentBlockIds() // → blocos no texto, na ordem
70
+
64
71
  editor.schedulePaginate() // repagina (após mudar algo que afeta altura por fora)
65
72
  editor.totalPages()
66
73
  editor.destroy()
@@ -79,6 +86,12 @@ sozinho toma uma decisão que não é dele.
79
86
  (antd, Material…) monta a barra dele e fala com o Tiptap direto —
80
87
  `editor.tiptap.chain().focus().toggleBold().run()`, `isActive('bold')`. É a API
81
88
  do Tiptap, documentada, sem um wrapper de comandos que sempre falta um.
89
+ Os comandos próprios do editor seguem o mesmo caminho:
90
+ `toggleTopicNumber()` / `setTopicNumber()` / `unsetTopicNumber()` numeram o
91
+ parágrafo da seleção com o número de tópico da peça (o nó `topicNumber`, que
92
+ continua a sequência do template e sai como numeração nativa no Word — não é a
93
+ lista `<ol>`); `hasLeadingTopicNumber(editor.tiptap)` dá o estado do botão.
94
+ Enter num parágrafo numerado já continua a numeração sozinho.
82
95
 
83
96
  **CSS em arquivo, com namespace.** Tudo vive sob `.poc-editor-root`, inclusive
84
97
  as utilities atômicas que o defesa.ai tira do UnoCSS. Importe
package/dist/index.d.mts CHANGED
@@ -18,11 +18,11 @@
18
18
  /// <reference types="@tiptap/extension-table" />
19
19
  /// <reference types="@tiptap/extension-bubble-menu" />
20
20
  import { Node, Editor, Extension, Mark } from '@tiptap/core';
21
+ import { Node as Node$1 } from '@tiptap/pm/model';
21
22
  import { Transaction } from '@tiptap/pm/state';
22
23
  import { EditorView } from '@tiptap/pm/view';
23
24
  import { P as PocPageConfig } from './shared/minuta-editor.9e65d163.mjs';
24
25
  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';
25
- import '@tiptap/pm/model';
26
26
 
27
27
  /**
28
28
  * Variáveis de template da minuta — o formato do placeholder e a resolução dele.
@@ -170,6 +170,18 @@ declare function renumberPocFootnotes(editor: Editor): void;
170
170
  * nativa do Word, que também se refaz quando o advogado apaga um parágrafo lá.
171
171
  * Contrato do marcador em `utils/topicNumber.ts`.
172
172
  */
173
+ declare module '@tiptap/core' {
174
+ interface Commands<ReturnType> {
175
+ topicNumber: {
176
+ /** numera os parágrafos da seleção (continua a sequência do parágrafo numerado mais próximo) */
177
+ setTopicNumber: () => ReturnType;
178
+ /** tira o número (e o separador colado nele) dos parágrafos da seleção */
179
+ unsetTopicNumber: () => ReturnType;
180
+ /** todos numerados → tira; senão numera os que faltam */
181
+ toggleTopicNumber: () => ReturnType;
182
+ };
183
+ }
184
+ }
173
185
  declare const PocTopicNumber: Node<any, any>;
174
186
  /**
175
187
  * Re-sequencia os números de tópico na ordem do documento, por grupo, dentro
@@ -185,6 +197,18 @@ declare function renumberPocTopics(editor: Editor): void;
185
197
  * parágrafo não custa nada além da varredura.
186
198
  */
187
199
  declare const PocTopicRenumber: Extension<any, any>;
200
+ /** índice do nó de número que abre o parágrafo (só espaço antes dele; `ref` não conta), ou -1 */
201
+ declare function leadingTopicIndex(block: Node$1): number;
202
+ /** o parágrafo onde está o cursor começa com número de tópico (estado do botão da toolbar) */
203
+ declare function hasLeadingTopicNumber(editor: Editor): boolean;
204
+ /** numera / desnumera os parágrafos da seleção (atalho tipado para `commands.toggleTopicNumber`) */
205
+ declare function toggleTopicNumber(editor: Editor): boolean;
206
+ /**
207
+ * Enter num parágrafo numerado: o seguinte nasce numerado (continua a
208
+ * sequência). Enter num parágrafo que só tem o número tira o número — a
209
+ * mesma saída da lista que o Word dá.
210
+ */
211
+ declare const PocTopicNumberKeymap: Extension<any, any>;
188
212
  declare const PocStyledDiv: Node<any, any>;
189
213
  declare const PocInlineStyle: Mark<any, any>;
190
214
  declare const PocUnderline: Mark<any, any>;
@@ -206,6 +230,84 @@ declare function buildPocExtensions(pageConfig?: PocPageConfig, options?: {
206
230
  handleKeyDown?: (view: EditorView, event: KeyboardEvent) => boolean;
207
231
  }): (Node<any, any> | Extension<any, any> | Mark<any, any>)[];
208
232
 
233
+ /**
234
+ * Impressão digital de um bloco da minuta (nó `minutaBlock` do editor) — base
235
+ * de "o advogado mexeu neste trecho?" quando o checklist muda com uma edição
236
+ * viva. O bloco nasce com `genHash` = impressão do texto gerado; enquanto a
237
+ * impressão atual bater com ela, o trecho é do checklist e pode ser
238
+ * re-gerado sem perder nada.
239
+ *
240
+ * Fica de fora o que muda SEM o advogado editar:
241
+ * - URL descartável de imagem (blob:/placeholder do load progressivo/vault:// do
242
+ * save): a imagem entra pela identidade DURÁVEL (`imageIdentity`), então
243
+ * trocar a imagem de verdade muda a impressão;
244
+ * - número de tópico e de nota (re-sequenciam quando outro trecho muda);
245
+ * - valor das variáveis (o chip é atualizado no lugar, em qualquer bloco —
246
+ * inclusive dentro de lista/tabela guardada como HTML cru).
247
+ *
248
+ * Mesma regra em `client/utils/minutaBlockFingerprint.ts` (o app ainda mantém a
249
+ * cópia dele) — a impressão gravada por um tem que bater com a do outro.
250
+ *
251
+ * Recebe o JSON do nó (`node.toJSON()`), então é pura e não depende de DOM.
252
+ */
253
+ interface NodeJson {
254
+ type: string;
255
+ attrs?: Record<string, any>;
256
+ content?: NodeJson[];
257
+ marks?: {
258
+ type: string;
259
+ attrs?: Record<string, any>;
260
+ }[];
261
+ text?: string;
262
+ }
263
+ /**
264
+ * Identidade durável de uma imagem a partir do `src` (e do `data-vault-src` do
265
+ * placeholder do load progressivo) — no app, blob: do cache → vault://.
266
+ */
267
+ type ImageIdentity = (src: string) => string;
268
+ declare function minutaBlockFingerprint(blockJson: NodeJson, identity?: ImageIdentity): string;
269
+
270
+ /**
271
+ * Checklist × edição viva, para quem usa o editor fora do defesa.ai.
272
+ *
273
+ * O documento editado continua dividido em `minutaBlock` (um por bloco do
274
+ * template, `isolating`), e cada bloco guarda em `genHash` a impressão digital
275
+ * do texto que o checklist gerou. Com isso, quando o checklist muda (tese que
276
+ * passou a valer, variável alterada), dá para aplicar a mudança SÓ onde ela
277
+ * cai, sem passar por cima do que o advogado escreveu:
278
+ *
279
+ * - tese que passou a gerar texto → o bloco entra na posição do template;
280
+ * - tese que deixou de gerar → o bloco sai (se foi editado, vira conflito);
281
+ * - variável alterada → o chip atualiza no lugar, em qualquer bloco;
282
+ * - bloco cuja ESTRUTURA mudou → re-gerado se ninguém o editou, conflito se sim.
283
+ *
284
+ * Mesma lógica de `client/composables/poc/pocEditionSync.ts` + o
285
+ * `syncEditionWithChecklist` do app; aqui o documento gerado chega pronto (o
286
+ * `GET /data/case/:id/html?source=blocks`), em vez de renderizado no browser.
287
+ */
288
+ interface MinutaChecklistConflict {
289
+ blockId: string;
290
+ blockTitle: string | null;
291
+ /** `replace`: o checklist mudou o texto de um trecho editado; `remove`: desligou uma tese editada */
292
+ kind: 'replace' | 'remove';
293
+ }
294
+ interface MinutaChecklistResult {
295
+ inserted: string[];
296
+ removed: string[];
297
+ regenerated: string[];
298
+ /** quantos chips de variável mudaram de valor */
299
+ variablesUpdated: number;
300
+ /** conflitos abertos depois desta aplicação (inclui os que já estavam) */
301
+ conflicts: MinutaChecklistConflict[];
302
+ /** a tese pedida em `toggled.show` não gera texto (condições internas) */
303
+ toggledWithoutContent: boolean;
304
+ /** a remoção não aconteceu porque o documento ficaria sem nenhum bloco */
305
+ lastBlockKept: boolean;
306
+ }
307
+ declare function blockHash(node: Node$1, identity?: ImageIdentity): string;
308
+ /** o conteúdo ainda é o que o checklist gerou (sem `genHash` = editado, por segurança) */
309
+ declare function isBlockUntouched(node: Node$1, identity?: ImageIdentity): boolean;
310
+
209
311
  /**
210
312
  * O editor de minuta como coisa montável em qualquer lugar: `createMinutaEditor(el, opts)`.
211
313
  *
@@ -374,6 +476,35 @@ interface MinutaEditor {
374
476
  * guarda uma cópia do documento.
375
477
  */
376
478
  refreshVaultImages: (resolveUrl: (vaultUrl: string) => Promise<string>) => Promise<number>;
479
+ /**
480
+ * Checklist junto da edição: aplica o documento GERADO pelo checklist agora
481
+ * (o `GET /data/case/:id/html?source=blocks`) sobre o documento editado,
482
+ * bloco a bloco — tese que passou a valer entra na posição do template, a
483
+ * que deixou de valer sai, variáveis atualizam no lugar e trecho cuja
484
+ * estrutura mudou é re-gerado só se ninguém o editou (senão vira conflito).
485
+ * `toggled` força a decisão de UMA tese (o host ligou/desligou na mão).
486
+ * Uma transação só: o desfazer volta tudo. Blocos novos (e os aceitos em
487
+ * `resolveChecklistConflict`) podem trazer imagem `vault://` — chame
488
+ * `refreshVaultImages` depois de aplicar E de resolver os conflitos.
489
+ */
490
+ applyChecklist: (generatedHtml: string, options?: {
491
+ toggled?: {
492
+ blockId: string;
493
+ show: boolean;
494
+ };
495
+ }) => MinutaChecklistResult;
496
+ /** trechos editados que o checklist quer mudar e aguardam decisão do advogado */
497
+ checklistConflicts: () => MinutaChecklistConflict[];
498
+ /** `apply` usa o texto novo (ou remove); `keep` mantém o editado e não pergunta de novo pela mesma mudança */
499
+ resolveChecklistConflict: (blockId: string, resolution: 'keep' | 'apply') => boolean;
500
+ /**
501
+ * Marca todos os blocos como "texto gerado". Chame UMA vez ao criar a edição
502
+ * a partir do HTML do checklist — sem a marca, todo bloco conta como editado
503
+ * e o `applyChecklist` pergunta em vez de re-gerar. Devolve quantos marcou.
504
+ */
505
+ stampGenerated: () => number;
506
+ /** blocos (`data-block-id`) no documento, na ordem — para o checklist mostrar o que está no texto */
507
+ documentBlockIds: () => string[];
377
508
  /** repagina (o host chama após mudar algo que afeta altura fora do editor) */
378
509
  schedulePaginate: () => void;
379
510
  /** reconstrói cabeçalhos/rodapés com o chrome atual, sem repaginar */
@@ -561,5 +692,5 @@ declare function collectVaultRefs(html: string): string[];
561
692
  /** aplica as URLs resolvidas às `<img>` do trecho; tags sem resolução ficam como estão */
562
693
  declare function renewVaultImageTags(html: string, resolved: Map<string, string>): string;
563
694
 
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 };
695
+ 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, PocTopicNumberKeymap, PocTopicRenumber, PocUnderline, PocVariableChip, TOPIC_DEFAULT_GROUP, TOPIC_GROUP_ATTR, TOPIC_NUMBER_ATTR, TOPIC_REF_ATTR, TOPIC_ROMAN_ATTR, blockHash, buildPocExtensions, collectVaultRefs, createMinutaEditor, decodeVaultAttr, dispatchVariableClick, escapeHtml, escapeVaultAttr, extractTemplateVariables, footnoteHtmlToRuns, footnoteSizePt, formatTopicNumber, fromRoman, hasLeadingTopicNumber, iconSvg, isBlockUntouched, leadingTopicIndex, minutaBlockFingerprint, readTopicNumber, refreshVaultImages, renewVaultImageTags, renumberPocFootnotes, renumberPocTopics, renumberPocTopicsIn, resolveTemplateVariables, sanitizeFootnoteHtml, sanitizeRichHtml, shouldMoveBreakToHeading, swapVaultSrc, toRoman, toggleTopicNumber, topicNumberHtml, variableChipHtml, vaultImageHtml, vaultRefOf };
696
+ export type { FootnoteRun, ImageIdentity, MinutaChecklistConflict, MinutaChecklistResult, MinutaChromeText, MinutaEditor, MinutaEditorOptions, MinutaHeader, MinutaMargins, MinutaVariableCatalog, MinutaVariableDef, MinutaVariableRef, PocKeepWithNextItem, RefreshVaultImagesOptions, TopicNumberInfo, VariableClickKind };
package/dist/index.d.ts CHANGED
@@ -18,11 +18,11 @@
18
18
  /// <reference types="@tiptap/extension-table" />
19
19
  /// <reference types="@tiptap/extension-bubble-menu" />
20
20
  import { Node, Editor, Extension, Mark } from '@tiptap/core';
21
+ import { Node as Node$1 } from '@tiptap/pm/model';
21
22
  import { Transaction } from '@tiptap/pm/state';
22
23
  import { EditorView } from '@tiptap/pm/view';
23
24
  import { P as PocPageConfig } from './shared/minuta-editor.9e65d163.js';
24
25
  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';
25
- import '@tiptap/pm/model';
26
26
 
27
27
  /**
28
28
  * Variáveis de template da minuta — o formato do placeholder e a resolução dele.
@@ -170,6 +170,18 @@ declare function renumberPocFootnotes(editor: Editor): void;
170
170
  * nativa do Word, que também se refaz quando o advogado apaga um parágrafo lá.
171
171
  * Contrato do marcador em `utils/topicNumber.ts`.
172
172
  */
173
+ declare module '@tiptap/core' {
174
+ interface Commands<ReturnType> {
175
+ topicNumber: {
176
+ /** numera os parágrafos da seleção (continua a sequência do parágrafo numerado mais próximo) */
177
+ setTopicNumber: () => ReturnType;
178
+ /** tira o número (e o separador colado nele) dos parágrafos da seleção */
179
+ unsetTopicNumber: () => ReturnType;
180
+ /** todos numerados → tira; senão numera os que faltam */
181
+ toggleTopicNumber: () => ReturnType;
182
+ };
183
+ }
184
+ }
173
185
  declare const PocTopicNumber: Node<any, any>;
174
186
  /**
175
187
  * Re-sequencia os números de tópico na ordem do documento, por grupo, dentro
@@ -185,6 +197,18 @@ declare function renumberPocTopics(editor: Editor): void;
185
197
  * parágrafo não custa nada além da varredura.
186
198
  */
187
199
  declare const PocTopicRenumber: Extension<any, any>;
200
+ /** índice do nó de número que abre o parágrafo (só espaço antes dele; `ref` não conta), ou -1 */
201
+ declare function leadingTopicIndex(block: Node$1): number;
202
+ /** o parágrafo onde está o cursor começa com número de tópico (estado do botão da toolbar) */
203
+ declare function hasLeadingTopicNumber(editor: Editor): boolean;
204
+ /** numera / desnumera os parágrafos da seleção (atalho tipado para `commands.toggleTopicNumber`) */
205
+ declare function toggleTopicNumber(editor: Editor): boolean;
206
+ /**
207
+ * Enter num parágrafo numerado: o seguinte nasce numerado (continua a
208
+ * sequência). Enter num parágrafo que só tem o número tira o número — a
209
+ * mesma saída da lista que o Word dá.
210
+ */
211
+ declare const PocTopicNumberKeymap: Extension<any, any>;
188
212
  declare const PocStyledDiv: Node<any, any>;
189
213
  declare const PocInlineStyle: Mark<any, any>;
190
214
  declare const PocUnderline: Mark<any, any>;
@@ -206,6 +230,84 @@ declare function buildPocExtensions(pageConfig?: PocPageConfig, options?: {
206
230
  handleKeyDown?: (view: EditorView, event: KeyboardEvent) => boolean;
207
231
  }): (Node<any, any> | Extension<any, any> | Mark<any, any>)[];
208
232
 
233
+ /**
234
+ * Impressão digital de um bloco da minuta (nó `minutaBlock` do editor) — base
235
+ * de "o advogado mexeu neste trecho?" quando o checklist muda com uma edição
236
+ * viva. O bloco nasce com `genHash` = impressão do texto gerado; enquanto a
237
+ * impressão atual bater com ela, o trecho é do checklist e pode ser
238
+ * re-gerado sem perder nada.
239
+ *
240
+ * Fica de fora o que muda SEM o advogado editar:
241
+ * - URL descartável de imagem (blob:/placeholder do load progressivo/vault:// do
242
+ * save): a imagem entra pela identidade DURÁVEL (`imageIdentity`), então
243
+ * trocar a imagem de verdade muda a impressão;
244
+ * - número de tópico e de nota (re-sequenciam quando outro trecho muda);
245
+ * - valor das variáveis (o chip é atualizado no lugar, em qualquer bloco —
246
+ * inclusive dentro de lista/tabela guardada como HTML cru).
247
+ *
248
+ * Mesma regra em `client/utils/minutaBlockFingerprint.ts` (o app ainda mantém a
249
+ * cópia dele) — a impressão gravada por um tem que bater com a do outro.
250
+ *
251
+ * Recebe o JSON do nó (`node.toJSON()`), então é pura e não depende de DOM.
252
+ */
253
+ interface NodeJson {
254
+ type: string;
255
+ attrs?: Record<string, any>;
256
+ content?: NodeJson[];
257
+ marks?: {
258
+ type: string;
259
+ attrs?: Record<string, any>;
260
+ }[];
261
+ text?: string;
262
+ }
263
+ /**
264
+ * Identidade durável de uma imagem a partir do `src` (e do `data-vault-src` do
265
+ * placeholder do load progressivo) — no app, blob: do cache → vault://.
266
+ */
267
+ type ImageIdentity = (src: string) => string;
268
+ declare function minutaBlockFingerprint(blockJson: NodeJson, identity?: ImageIdentity): string;
269
+
270
+ /**
271
+ * Checklist × edição viva, para quem usa o editor fora do defesa.ai.
272
+ *
273
+ * O documento editado continua dividido em `minutaBlock` (um por bloco do
274
+ * template, `isolating`), e cada bloco guarda em `genHash` a impressão digital
275
+ * do texto que o checklist gerou. Com isso, quando o checklist muda (tese que
276
+ * passou a valer, variável alterada), dá para aplicar a mudança SÓ onde ela
277
+ * cai, sem passar por cima do que o advogado escreveu:
278
+ *
279
+ * - tese que passou a gerar texto → o bloco entra na posição do template;
280
+ * - tese que deixou de gerar → o bloco sai (se foi editado, vira conflito);
281
+ * - variável alterada → o chip atualiza no lugar, em qualquer bloco;
282
+ * - bloco cuja ESTRUTURA mudou → re-gerado se ninguém o editou, conflito se sim.
283
+ *
284
+ * Mesma lógica de `client/composables/poc/pocEditionSync.ts` + o
285
+ * `syncEditionWithChecklist` do app; aqui o documento gerado chega pronto (o
286
+ * `GET /data/case/:id/html?source=blocks`), em vez de renderizado no browser.
287
+ */
288
+ interface MinutaChecklistConflict {
289
+ blockId: string;
290
+ blockTitle: string | null;
291
+ /** `replace`: o checklist mudou o texto de um trecho editado; `remove`: desligou uma tese editada */
292
+ kind: 'replace' | 'remove';
293
+ }
294
+ interface MinutaChecklistResult {
295
+ inserted: string[];
296
+ removed: string[];
297
+ regenerated: string[];
298
+ /** quantos chips de variável mudaram de valor */
299
+ variablesUpdated: number;
300
+ /** conflitos abertos depois desta aplicação (inclui os que já estavam) */
301
+ conflicts: MinutaChecklistConflict[];
302
+ /** a tese pedida em `toggled.show` não gera texto (condições internas) */
303
+ toggledWithoutContent: boolean;
304
+ /** a remoção não aconteceu porque o documento ficaria sem nenhum bloco */
305
+ lastBlockKept: boolean;
306
+ }
307
+ declare function blockHash(node: Node$1, identity?: ImageIdentity): string;
308
+ /** o conteúdo ainda é o que o checklist gerou (sem `genHash` = editado, por segurança) */
309
+ declare function isBlockUntouched(node: Node$1, identity?: ImageIdentity): boolean;
310
+
209
311
  /**
210
312
  * O editor de minuta como coisa montável em qualquer lugar: `createMinutaEditor(el, opts)`.
211
313
  *
@@ -374,6 +476,35 @@ interface MinutaEditor {
374
476
  * guarda uma cópia do documento.
375
477
  */
376
478
  refreshVaultImages: (resolveUrl: (vaultUrl: string) => Promise<string>) => Promise<number>;
479
+ /**
480
+ * Checklist junto da edição: aplica o documento GERADO pelo checklist agora
481
+ * (o `GET /data/case/:id/html?source=blocks`) sobre o documento editado,
482
+ * bloco a bloco — tese que passou a valer entra na posição do template, a
483
+ * que deixou de valer sai, variáveis atualizam no lugar e trecho cuja
484
+ * estrutura mudou é re-gerado só se ninguém o editou (senão vira conflito).
485
+ * `toggled` força a decisão de UMA tese (o host ligou/desligou na mão).
486
+ * Uma transação só: o desfazer volta tudo. Blocos novos (e os aceitos em
487
+ * `resolveChecklistConflict`) podem trazer imagem `vault://` — chame
488
+ * `refreshVaultImages` depois de aplicar E de resolver os conflitos.
489
+ */
490
+ applyChecklist: (generatedHtml: string, options?: {
491
+ toggled?: {
492
+ blockId: string;
493
+ show: boolean;
494
+ };
495
+ }) => MinutaChecklistResult;
496
+ /** trechos editados que o checklist quer mudar e aguardam decisão do advogado */
497
+ checklistConflicts: () => MinutaChecklistConflict[];
498
+ /** `apply` usa o texto novo (ou remove); `keep` mantém o editado e não pergunta de novo pela mesma mudança */
499
+ resolveChecklistConflict: (blockId: string, resolution: 'keep' | 'apply') => boolean;
500
+ /**
501
+ * Marca todos os blocos como "texto gerado". Chame UMA vez ao criar a edição
502
+ * a partir do HTML do checklist — sem a marca, todo bloco conta como editado
503
+ * e o `applyChecklist` pergunta em vez de re-gerar. Devolve quantos marcou.
504
+ */
505
+ stampGenerated: () => number;
506
+ /** blocos (`data-block-id`) no documento, na ordem — para o checklist mostrar o que está no texto */
507
+ documentBlockIds: () => string[];
377
508
  /** repagina (o host chama após mudar algo que afeta altura fora do editor) */
378
509
  schedulePaginate: () => void;
379
510
  /** reconstrói cabeçalhos/rodapés com o chrome atual, sem repaginar */
@@ -561,5 +692,5 @@ declare function collectVaultRefs(html: string): string[];
561
692
  /** aplica as URLs resolvidas às `<img>` do trecho; tags sem resolução ficam como estão */
562
693
  declare function renewVaultImageTags(html: string, resolved: Map<string, string>): string;
563
694
 
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 };
695
+ 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, PocTopicNumberKeymap, PocTopicRenumber, PocUnderline, PocVariableChip, TOPIC_DEFAULT_GROUP, TOPIC_GROUP_ATTR, TOPIC_NUMBER_ATTR, TOPIC_REF_ATTR, TOPIC_ROMAN_ATTR, blockHash, buildPocExtensions, collectVaultRefs, createMinutaEditor, decodeVaultAttr, dispatchVariableClick, escapeHtml, escapeVaultAttr, extractTemplateVariables, footnoteHtmlToRuns, footnoteSizePt, formatTopicNumber, fromRoman, hasLeadingTopicNumber, iconSvg, isBlockUntouched, leadingTopicIndex, minutaBlockFingerprint, readTopicNumber, refreshVaultImages, renewVaultImageTags, renumberPocFootnotes, renumberPocTopics, renumberPocTopicsIn, resolveTemplateVariables, sanitizeFootnoteHtml, sanitizeRichHtml, shouldMoveBreakToHeading, swapVaultSrc, toRoman, toggleTopicNumber, topicNumberHtml, variableChipHtml, vaultImageHtml, vaultRefOf };
696
+ export type { FootnoteRun, ImageIdentity, MinutaChecklistConflict, MinutaChecklistResult, MinutaChromeText, MinutaEditor, MinutaEditorOptions, MinutaHeader, MinutaMargins, MinutaVariableCatalog, MinutaVariableDef, MinutaVariableRef, PocKeepWithNextItem, RefreshVaultImagesOptions, TopicNumberInfo, VariableClickKind };
package/dist/index.mjs CHANGED
@@ -24,6 +24,8 @@ import TableRow from '@tiptap/extension-table-row';
24
24
  import { Plugin } from '@tiptap/pm/state';
25
25
  import { s as sanitizeRichHtml, e as sanitizeFootnoteHtml, g as footnoteSizePt, T as TOPIC_DEFAULT_GROUP, h as TOPIC_NUMBER_ATTR, r as readTopicNumber, i as TOPIC_GROUP_ATTR, j as TOPIC_ROMAN_ATTR, k as TOPIC_REF_ATTR, a as formatTopicNumber, P as PocPaginationPlugin, l as computePocBreaks, m as pocPageKey, n as breaksEqual, o as PAGINATE_META } from './shared/minuta-editor.f1239e0b.mjs';
26
26
  export { v as buildPocFooterEl, u as buildPocHeaderEl, w as buildPocNotesEl, z as computePocLoneRows, f as footnoteHtmlToRuns, C as fromRoman, t as isPocChromeCanvas, y as isPocFlexRow, q as pocChromeFlowHeight, x as pocDescendsInto, p as pocLoneCellIndex, b as pocLooksLikeTitle, c as pocNodeHasVisibleContent, d as pocNodePaintsBox, A as shouldMoveBreakToHeading, B as toRoman, D as topicNumberHtml } from './shared/minuta-editor.f1239e0b.mjs';
27
+ import { closeHistory } from '@tiptap/pm/history';
28
+ import { DOMParser } from '@tiptap/pm/model';
27
29
  import '@tiptap/pm/view';
28
30
 
29
31
  const POC_ASSET_SWAP = "pocAssetSwap";
@@ -35,7 +37,18 @@ function htmlToDom(html) {
35
37
  const PocDocument = Node$1.create({
36
38
  name: "doc",
37
39
  topNode: true,
38
- content: "minutaBlock+"
40
+ content: "minutaBlock+",
41
+ addAttributes() {
42
+ return {
43
+ /**
44
+ * Estado do checklist × edição (engine/checklistSync): teses que o
45
+ * checklist queria no texto na última aplicação + conflitos abertos.
46
+ * Vive NO documento para o desfazer do editor voltar junto com o
47
+ * texto; não vai para o HTML (sem documento salvo, nasce do texto).
48
+ */
49
+ checklist: { default: null, rendered: false, parseHTML: () => null }
50
+ };
51
+ }
39
52
  });
40
53
  const PocMinutaBlock = Node$1.create({
41
54
  name: "minutaBlock",
@@ -291,6 +304,14 @@ const PocVariableChip = Node$1.create({
291
304
  return {
292
305
  variableId: { default: null },
293
306
  parentBlockId: { default: null },
307
+ /**
308
+ * linha do `<for>` — distingue a mesma variável em linhas diferentes
309
+ * (`data-loop-index` do app; `data-variable-index` da Data API)
310
+ */
311
+ loopIndex: {
312
+ default: null,
313
+ parseHTML: (el) => el.getAttribute("data-loop-index") ?? el.getAttribute("data-variable-index")
314
+ },
294
315
  html: { default: "" },
295
316
  text: { default: "" }
296
317
  };
@@ -302,6 +323,7 @@ const PocVariableChip = Node$1.create({
302
323
  getAttrs: (el) => ({
303
324
  variableId: el.getAttribute("data-variable-id"),
304
325
  parentBlockId: el.getAttribute("data-parent-block-id"),
326
+ loopIndex: el.getAttribute("data-loop-index") ?? el.getAttribute("data-variable-index"),
305
327
  html: el.outerHTML,
306
328
  text: el.textContent ?? ""
307
329
  })
@@ -528,6 +550,13 @@ const PocTopicNumber = Node$1.create({
528
550
  if (node.attrs.ref)
529
551
  attrs[TOPIC_REF_ATTR] = "true";
530
552
  return ["span", attrs, formatTopicNumber(node.attrs.number, node.attrs.roman)];
553
+ },
554
+ addCommands() {
555
+ return {
556
+ setTopicNumber: () => ({ tr }) => applyTopicNumbers(tr, "set"),
557
+ unsetTopicNumber: () => ({ tr }) => applyTopicNumbers(tr, "unset"),
558
+ toggleTopicNumber: () => ({ tr }) => applyTopicNumbers(tr, "toggle")
559
+ };
531
560
  }
532
561
  });
533
562
  function renumberPocTopicsIn(tr) {
@@ -565,6 +594,169 @@ const PocTopicRenumber = Extension.create({
565
594
  })];
566
595
  }
567
596
  });
597
+ const TOPIC_SEPARATOR_DEFAULT = ". ";
598
+ const TOPIC_SEPARATOR_RE = /^[ \u00A0]*[.)\-–—:]?[ \u00A0]*/u;
599
+ const TOPIC_SEPARATOR_MAX = 4;
600
+ function leadingTopicIndex(block) {
601
+ for (let i = 0; i < block.childCount; i++) {
602
+ const child = block.child(i);
603
+ if (child.isText && !child.text?.trim())
604
+ continue;
605
+ return child.type.name === "topicNumber" && !child.attrs.ref ? i : -1;
606
+ }
607
+ return -1;
608
+ }
609
+ function topicSeparatorAfter(block, topicIndex) {
610
+ const next = block.maybeChild(topicIndex + 1);
611
+ if (!next?.isText || !next.text)
612
+ return "";
613
+ return (next.text.match(TOPIC_SEPARATOR_RE)?.[0] ?? "").slice(0, TOPIC_SEPARATOR_MAX);
614
+ }
615
+ function firstTextMarks(block) {
616
+ for (let i = 0; i < block.childCount; i++) {
617
+ const child = block.child(i);
618
+ if (child.isText && child.text?.trim())
619
+ return child.marks;
620
+ }
621
+ return void 0;
622
+ }
623
+ function findTopicReference(doc, blockPos) {
624
+ let before = null;
625
+ let after = null;
626
+ doc.descendants((node, pos) => {
627
+ if (after)
628
+ return false;
629
+ if (!node.isTextblock)
630
+ return;
631
+ if (pos === blockPos)
632
+ return false;
633
+ const index = leadingTopicIndex(node);
634
+ if (index < 0)
635
+ return false;
636
+ const topic = node.child(index);
637
+ const ref = {
638
+ group: topic.attrs.group || TOPIC_DEFAULT_GROUP,
639
+ roman: Boolean(topic.attrs.roman),
640
+ number: Number(topic.attrs.number) || 0,
641
+ separator: topicSeparatorAfter(node, index) || TOPIC_SEPARATOR_DEFAULT,
642
+ marks: topic.marks
643
+ };
644
+ if (pos < blockPos)
645
+ before = ref;
646
+ else
647
+ after = ref;
648
+ return false;
649
+ });
650
+ return before ?? after;
651
+ }
652
+ function textBlockAt(doc, pos) {
653
+ const $pos = doc.resolve(pos);
654
+ for (let depth = $pos.depth; depth > 0; depth--) {
655
+ const node = $pos.node(depth);
656
+ if (node.type.name === "paragraph" || node.type.name === "heading")
657
+ return { node, pos: $pos.before(depth) };
658
+ if (node.isTextblock)
659
+ return null;
660
+ }
661
+ return null;
662
+ }
663
+ function insertTopicNumberAt(tr, blockPos) {
664
+ const block = tr.doc.nodeAt(blockPos);
665
+ if (!block || !block.isTextblock || leadingTopicIndex(block) >= 0)
666
+ return false;
667
+ const type = tr.doc.type.schema.nodes.topicNumber;
668
+ if (!type)
669
+ return false;
670
+ const ref = findTopicReference(tr.doc, blockPos);
671
+ const marks = firstTextMarks(block) ?? ref?.marks ?? [];
672
+ const topic = type.create({
673
+ number: ref && ref.number > 0 ? ref.number + 1 : 1,
674
+ group: ref?.group ?? TOPIC_DEFAULT_GROUP,
675
+ roman: ref?.roman ?? false,
676
+ ref: false
677
+ }, void 0, marks);
678
+ const separator = ref?.separator ?? TOPIC_SEPARATOR_DEFAULT;
679
+ const content = separator ? [topic, tr.doc.type.schema.text(separator, marks)] : [topic];
680
+ tr.insert(blockPos + 1, content);
681
+ return true;
682
+ }
683
+ function removeTopicNumberAt(tr, blockPos) {
684
+ const block = tr.doc.nodeAt(blockPos);
685
+ if (!block || !block.isTextblock)
686
+ return false;
687
+ const index = leadingTopicIndex(block);
688
+ if (index < 0)
689
+ return false;
690
+ let from = blockPos + 1;
691
+ for (let i = 0; i < index; i++)
692
+ from += block.child(i).nodeSize;
693
+ const to = from + block.child(index).nodeSize + topicSeparatorAfter(block, index).length;
694
+ tr.delete(from, to);
695
+ return true;
696
+ }
697
+ function selectedTextBlocks(tr) {
698
+ const { from, to } = tr.selection;
699
+ const blocks = [];
700
+ tr.doc.nodesBetween(from, to, (node, pos) => {
701
+ if (node.type.name === "paragraph" || node.type.name === "heading")
702
+ blocks.push({ node, pos });
703
+ });
704
+ return blocks;
705
+ }
706
+ function applyTopicNumbers(tr, mode) {
707
+ const blocks = selectedTextBlocks(tr);
708
+ if (!blocks.length)
709
+ return false;
710
+ const allNumbered = blocks.every((b) => leadingTopicIndex(b.node) >= 0);
711
+ const remove = mode === "unset" || mode === "toggle" && allNumbered;
712
+ const stepsBefore = tr.mapping.maps.length;
713
+ let changed = false;
714
+ for (const { pos } of blocks) {
715
+ const mapped = tr.mapping.slice(stepsBefore).map(pos);
716
+ changed = (remove ? removeTopicNumberAt(tr, mapped) : insertTopicNumberAt(tr, mapped)) || changed;
717
+ }
718
+ return changed;
719
+ }
720
+ function hasLeadingTopicNumber(editor) {
721
+ if (!editor || editor.isDestroyed)
722
+ return false;
723
+ const block = textBlockAt(editor.state.doc, editor.state.selection.from);
724
+ return Boolean(block && leadingTopicIndex(block.node) >= 0);
725
+ }
726
+ function toggleTopicNumber(editor) {
727
+ return editor.chain().focus().toggleTopicNumber().run();
728
+ }
729
+ const PocTopicNumberKeymap = Extension.create({
730
+ name: "pocTopicNumberKeymap",
731
+ // acima do keymap base e das listas (100); abaixo do `pocKeyDownFirst` (1000)
732
+ priority: 200,
733
+ addKeyboardShortcuts() {
734
+ return {
735
+ Enter: ({ editor }) => {
736
+ const { selection } = editor.state;
737
+ const { $from } = selection;
738
+ const block = $from.parent;
739
+ if (block.type.name !== "paragraph" && block.type.name !== "heading")
740
+ return false;
741
+ const index = leadingTopicIndex(block);
742
+ if (index < 0)
743
+ return false;
744
+ const body = block.textBetween(0, block.content.size, void 0, "\uFFFC");
745
+ const onlyNumber = !/[\p{L}\p{N}]/u.test(body.replace(/^/, ""));
746
+ if (selection.empty && onlyNumber)
747
+ return editor.chain().command(({ tr }) => removeTopicNumberAt(tr, $from.before())).run();
748
+ const inListItem = $from.depth >= 2 && $from.node(-1).type.name === "listItem";
749
+ return editor.chain().command(({ commands }) => inListItem ? commands.splitListItem("listItem") : commands.splitBlock()).command(({ tr }) => {
750
+ const target = textBlockAt(tr.doc, tr.selection.from);
751
+ if (!target || leadingTopicIndex(target.node) >= 0)
752
+ return true;
753
+ insertTopicNumberAt(tr, target.pos);
754
+ return true;
755
+ }).run();
756
+ }
757
+ };
758
+ }
759
+ });
568
760
  const PocStyledDiv = Node$1.create({
569
761
  name: "styledDiv",
570
762
  group: "block",
@@ -696,6 +888,7 @@ function buildPocExtensions(pageConfig, options) {
696
888
  PocFootnoteRef,
697
889
  PocTopicNumber,
698
890
  PocTopicRenumber,
891
+ PocTopicNumberKeymap,
699
892
  PocStyledDiv,
700
893
  PocInlineStyle,
701
894
  PocUnderline,
@@ -728,7 +921,7 @@ const VARIABLE_TAG_RE = /<span\b[^>]*\bdata-minuta-var="([^"]*)"[^>]*>[^<]*<\/sp
728
921
  function escapeHtml(value) {
729
922
  return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
730
923
  }
731
- function decodeAttr(value) {
924
+ function decodeAttr$1(value) {
732
925
  return value.replace(/&quot;/g, '"').replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&amp;/g, "&");
733
926
  }
734
927
  function extractTemplateVariables(html) {
@@ -736,7 +929,7 @@ function extractTemplateVariables(html) {
736
929
  return [];
737
930
  const keys = [];
738
931
  for (const match of html.matchAll(VARIABLE_TAG_RE)) {
739
- const key = decodeAttr(match[1] ?? "").trim();
932
+ const key = decodeAttr$1(match[1] ?? "").trim();
740
933
  if (key && !keys.includes(key))
741
934
  keys.push(key);
742
935
  }
@@ -747,7 +940,7 @@ function resolveTemplateVariables(html, values) {
747
940
  return { html: "", missing: [] };
748
941
  const missing = [];
749
942
  const resolved = html.replace(VARIABLE_TAG_RE, (tag, rawKey) => {
750
- const key = decodeAttr(rawKey ?? "").trim();
943
+ const key = decodeAttr$1(rawKey ?? "").trim();
751
944
  if (!key)
752
945
  return tag;
753
946
  const value = values?.[key];
@@ -864,6 +1057,362 @@ async function refreshVaultImages(editor, resolveUrl, options = {}) {
864
1057
  return touched;
865
1058
  }
866
1059
 
1060
+ const IMG_TAG = /<img\b[^>]*>/g;
1061
+ const ATTR = (name) => new RegExp(`\\s${name}="([^"]*)"`);
1062
+ function decodeAttr(value) {
1063
+ return value.replace(/&quot;/g, '"').replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&amp;/g, "&");
1064
+ }
1065
+ function normalizeImgTags(html, identity) {
1066
+ if (!html.includes("<img"))
1067
+ return html;
1068
+ return html.replace(IMG_TAG, (tag) => {
1069
+ const vault = tag.match(ATTR("data-vault-src"))?.[1];
1070
+ const src = tag.match(ATTR("src"))?.[1] ?? "";
1071
+ const id = vault ? decodeAttr(vault) : identity(decodeAttr(src));
1072
+ return tag.replace(/\s(?:src|data-vault-src|data-download-url)="[^"]*"/g, "").replace(/^<img\b/, `<img data-img="${id}"`);
1073
+ });
1074
+ }
1075
+ const CHIP_SPAN = /<span\b([^>]*\sdata-variable-id="[^"]*"[^>]*)>[\s\S]*?<\/span>/g;
1076
+ function normalizeChipSpans(html) {
1077
+ if (!html.includes("data-variable-id"))
1078
+ return html;
1079
+ return html.replace(CHIP_SPAN, (_all, attrs) => {
1080
+ const id = attrs.match(/\sdata-variable-id="([^"]*)"/)?.[1] ?? "";
1081
+ const index = attrs.match(/\sdata-(?:loop|variable)-index="([^"]*)"/)?.[1] ?? "";
1082
+ return `<var:${id}:${index}>`;
1083
+ });
1084
+ }
1085
+ function normalizeAttrs(attrs, identity, chipSpans = true) {
1086
+ if (!attrs)
1087
+ return void 0;
1088
+ const out = {};
1089
+ for (const key of Object.keys(attrs).sort()) {
1090
+ if (key === "genHash" || key === "uid")
1091
+ continue;
1092
+ const value = attrs[key];
1093
+ if (key === "src" && typeof value === "string") {
1094
+ out[key] = identity(value);
1095
+ } else {
1096
+ out[key] = typeof value === "string" ? chipSpans ? normalizeChipSpans(normalizeImgTags(value, identity)) : normalizeImgTags(value, identity) : value;
1097
+ }
1098
+ }
1099
+ return out;
1100
+ }
1101
+ function normalize(node, identity, chipSpans = true) {
1102
+ switch (node.type) {
1103
+ case "variableChip":
1104
+ return ["var", node.attrs?.variableId ?? null];
1105
+ case "topicNumber":
1106
+ return ["topic", node.attrs?.group ?? null, !!node.attrs?.ref];
1107
+ case "footnoteRef":
1108
+ return ["note", node.attrs?.text ?? "", node.attrs?.size ?? null];
1109
+ case "text":
1110
+ return ["text", node.text ?? "", (node.marks ?? []).map((mark) => [mark.type, normalizeAttrs(mark.attrs, identity, chipSpans)])];
1111
+ }
1112
+ return [node.type, normalizeAttrs(node.attrs, identity, chipSpans), (node.content ?? []).map((child) => normalize(child, identity, chipSpans))];
1113
+ }
1114
+ function fnv1a(input) {
1115
+ let hash = 2166136261;
1116
+ for (let i = 0; i < input.length; i++) {
1117
+ hash ^= input.charCodeAt(i);
1118
+ hash = Math.imul(hash, 16777619);
1119
+ }
1120
+ return (hash >>> 0).toString(16).padStart(8, "0");
1121
+ }
1122
+ function minutaBlockFingerprint(blockJson, identity = (src) => src) {
1123
+ const body = JSON.stringify((blockJson.content ?? []).map((child) => normalize(child, identity)));
1124
+ return `${fnv1a(body)}${body.length.toString(16)}`;
1125
+ }
1126
+ function minutaBlockFingerprintV1(blockJson, identity = (src) => src) {
1127
+ const body = JSON.stringify((blockJson.content ?? []).map((child) => normalize(child, identity, false)));
1128
+ return `${fnv1a(body)}${body.length.toString(16)}`;
1129
+ }
1130
+
1131
+ function readChecklist(doc) {
1132
+ const stored = doc.attrs.checklist;
1133
+ return stored ?? { wanted: [...docBlocks(doc).keys()], pending: [] };
1134
+ }
1135
+ const defaultImageIdentity = (src) => src;
1136
+ function blockHash(node, identity = defaultImageIdentity) {
1137
+ return minutaBlockFingerprint(node.toJSON(), identity);
1138
+ }
1139
+ function isBlockUntouched(node, identity = defaultImageIdentity) {
1140
+ const genHash = node.attrs.genHash;
1141
+ if (!genHash)
1142
+ return false;
1143
+ return genHash === blockHash(node, identity) || genHash === minutaBlockFingerprintV1(node.toJSON(), identity);
1144
+ }
1145
+ function docBlocks(doc) {
1146
+ const map = /* @__PURE__ */ new Map();
1147
+ doc.forEach((node, offset) => {
1148
+ const id = node.attrs.blockId;
1149
+ if (node.type.name === "minutaBlock" && id && !map.has(id))
1150
+ map.set(id, { pos: offset, node });
1151
+ });
1152
+ return map;
1153
+ }
1154
+ function parseGeneratedBlocks(schema, html, identity = defaultImageIdentity) {
1155
+ const wrapper = document.createElement("div");
1156
+ wrapper.innerHTML = html;
1157
+ const doc = DOMParser.fromSchema(schema).parse(wrapper);
1158
+ const blocks = [];
1159
+ doc.forEach((node) => {
1160
+ if (node.type.name === "minutaBlock" && node.attrs.blockId)
1161
+ blocks.push(node.type.create({ ...node.attrs, genHash: blockHash(node, identity) }, node.content, node.marks));
1162
+ });
1163
+ return blocks;
1164
+ }
1165
+ function stampGeneratedBlocks(editor, identity = defaultImageIdentity) {
1166
+ const tr = editor.state.tr;
1167
+ let stamped = 0;
1168
+ editor.state.doc.forEach((node, offset) => {
1169
+ if (node.type.name !== "minutaBlock")
1170
+ return;
1171
+ const genHash = blockHash(node, identity);
1172
+ if (node.attrs.genHash !== genHash) {
1173
+ tr.setNodeMarkup(offset, void 0, { ...node.attrs, genHash });
1174
+ stamped++;
1175
+ }
1176
+ });
1177
+ if (tr.docChanged)
1178
+ editor.view.dispatch(tr);
1179
+ return stamped;
1180
+ }
1181
+ function baseChipKey(variableId, parentBlockId) {
1182
+ return `${parentBlockId ?? ""}|${variableId ?? ""}`;
1183
+ }
1184
+ function chipKey(variableId, parentBlockId, loopIndex) {
1185
+ return `${baseChipKey(variableId, parentBlockId)}|${loopIndex ?? ""}`;
1186
+ }
1187
+ function collectChips(html) {
1188
+ const wrapper = document.createElement("div");
1189
+ wrapper.innerHTML = html;
1190
+ const byKey = /* @__PURE__ */ new Map();
1191
+ const byBase = /* @__PURE__ */ new Map();
1192
+ wrapper.querySelectorAll("span[data-variable-id]").forEach((el) => {
1193
+ const variableId = el.getAttribute("data-variable-id");
1194
+ const parentBlockId = el.getAttribute("data-parent-block-id");
1195
+ const loopIndex = el.getAttribute("data-loop-index") ?? el.getAttribute("data-variable-index");
1196
+ const value = { html: el.outerHTML, text: el.textContent ?? "", loopIndex };
1197
+ for (const [map, key] of [[byKey, chipKey(variableId, parentBlockId, loopIndex)], [byBase, baseChipKey(variableId, parentBlockId)]]) {
1198
+ const list = map.get(key) ?? [];
1199
+ list.push(value);
1200
+ map.set(key, list);
1201
+ }
1202
+ });
1203
+ return { byKey, byBase };
1204
+ }
1205
+ function matchByValue(docChips, generated) {
1206
+ const pairs = [];
1207
+ const count = (list, text) => list.filter((t) => t === text).length;
1208
+ const docTexts = docChips.map((chip) => chip.node.attrs.text);
1209
+ const genTexts = generated.map((value) => value.text);
1210
+ const freeDoc = new Set(docChips.keys());
1211
+ const freeGen = new Set(generated.keys());
1212
+ docChips.forEach((chip, i) => {
1213
+ const text = docTexts[i];
1214
+ if (count(docTexts, text) !== 1 || count(genTexts, text) !== 1)
1215
+ return;
1216
+ const j = genTexts.indexOf(text);
1217
+ pairs.push({ ...chip, next: generated[j] });
1218
+ freeDoc.delete(i);
1219
+ freeGen.delete(j);
1220
+ });
1221
+ if (freeDoc.size === 1 && freeGen.size === 1) {
1222
+ const [i] = freeDoc;
1223
+ const [j] = freeGen;
1224
+ pairs.push({ ...docChips[i], next: generated[j] });
1225
+ }
1226
+ return pairs;
1227
+ }
1228
+ function refreshChips(tr, chips) {
1229
+ const inDoc = /* @__PURE__ */ new Map();
1230
+ tr.doc.descendants((node, pos) => {
1231
+ if (node.type.name !== "variableChip")
1232
+ return;
1233
+ const key = chipKey(node.attrs.variableId, node.attrs.parentBlockId, node.attrs.loopIndex);
1234
+ const list = inDoc.get(key) ?? [];
1235
+ list.push({ pos, node });
1236
+ inDoc.set(key, list);
1237
+ });
1238
+ let changed = 0;
1239
+ for (const [key, docChips] of inDoc) {
1240
+ let generated = chips.byKey.get(key);
1241
+ if (!generated?.length && docChips[0].node.attrs.loopIndex == null) {
1242
+ const { variableId, parentBlockId } = docChips[0].node.attrs;
1243
+ generated = chips.byBase.get(baseChipKey(variableId, parentBlockId));
1244
+ }
1245
+ if (!generated?.length)
1246
+ continue;
1247
+ const values = generated;
1248
+ const uniform = values.every((value) => value.html === values[0].html);
1249
+ const pairs = uniform ? docChips.map((chip) => ({ ...chip, next: values[0] })) : matchByValue(docChips, values);
1250
+ for (const { pos, node, next } of pairs) {
1251
+ const loopIndex = uniform ? node.attrs.loopIndex : next.loopIndex ?? node.attrs.loopIndex;
1252
+ if (next.html === node.attrs.html && loopIndex === node.attrs.loopIndex)
1253
+ continue;
1254
+ tr.setNodeMarkup(pos, void 0, { ...node.attrs, html: next.html, text: next.text, loopIndex });
1255
+ if (next.text !== node.attrs.text)
1256
+ changed++;
1257
+ }
1258
+ }
1259
+ return changed;
1260
+ }
1261
+ function refreshIslandChips(tr, chips) {
1262
+ let changed = 0;
1263
+ tr.doc.descendants((node, pos) => {
1264
+ if (node.type.name !== "htmlIsland" || !String(node.attrs.html ?? "").includes("data-variable-id"))
1265
+ return;
1266
+ const tpl = document.createElement("template");
1267
+ tpl.innerHTML = node.attrs.html;
1268
+ let touched = false;
1269
+ tpl.content.querySelectorAll("span[data-variable-id]").forEach((el) => {
1270
+ const key = chipKey(
1271
+ el.getAttribute("data-variable-id"),
1272
+ el.getAttribute("data-parent-block-id"),
1273
+ el.getAttribute("data-loop-index") ?? el.getAttribute("data-variable-index")
1274
+ );
1275
+ const values = chips.byKey.get(key);
1276
+ if (!values?.length || values.some((value) => value.html !== values[0].html))
1277
+ return;
1278
+ if (el.outerHTML !== values[0].html) {
1279
+ if (el.textContent !== values[0].text)
1280
+ changed++;
1281
+ el.outerHTML = values[0].html;
1282
+ touched = true;
1283
+ }
1284
+ });
1285
+ if (touched)
1286
+ tr.setNodeMarkup(pos, void 0, { ...node.attrs, html: tpl.innerHTML });
1287
+ return false;
1288
+ });
1289
+ return changed;
1290
+ }
1291
+ function insertPos(doc, blockId, order) {
1292
+ const present = docBlocks(doc);
1293
+ const index = order.indexOf(blockId);
1294
+ for (let i = index - 1; i >= 0; i--) {
1295
+ const prev = present.get(order[i]);
1296
+ if (prev)
1297
+ return prev.pos + prev.node.nodeSize;
1298
+ }
1299
+ return 0;
1300
+ }
1301
+ function listConflicts(doc) {
1302
+ return readChecklist(doc).pending.map(({ blockId, kind, title }) => ({ blockId, blockTitle: title, kind }));
1303
+ }
1304
+ function applyChecklistToDocument(editor, generatedHtml, options = {}) {
1305
+ const identity = options.imageIdentity ?? defaultImageIdentity;
1306
+ const generated = parseGeneratedBlocks(editor.schema, generatedHtml, identity);
1307
+ const genById = new Map(generated.map((node) => [node.attrs.blockId, node]));
1308
+ const order = generated.map((node) => node.attrs.blockId);
1309
+ const wanted = new Set(order);
1310
+ const stored = readChecklist(editor.state.doc);
1311
+ const before = new Set(stored.wanted);
1312
+ const previousPending = new Map(stored.pending.map((entry) => [entry.blockId, entry]));
1313
+ const pending = /* @__PURE__ */ new Map();
1314
+ const toggled = options.toggled;
1315
+ const tr = editor.state.tr;
1316
+ const result = {
1317
+ inserted: [],
1318
+ removed: [],
1319
+ regenerated: [],
1320
+ variablesUpdated: 0,
1321
+ conflicts: [],
1322
+ toggledWithoutContent: false,
1323
+ lastBlockKept: false
1324
+ };
1325
+ const setConflict = (id, kind, node, title) => {
1326
+ pending.set(id, { blockId: id, kind, title, node: node ? node.toJSON() : null });
1327
+ };
1328
+ const ids = [...order, ...[...docBlocks(tr.doc).keys()].filter((id) => !wanted.has(id))];
1329
+ if (toggled && !ids.includes(toggled.blockId))
1330
+ ids.push(toggled.blockId);
1331
+ for (const id of ids) {
1332
+ const fresh = genById.get(id) ?? null;
1333
+ const explicit = toggled?.blockId === id;
1334
+ const turnedOn = explicit ? toggled.show : !before.has(id) && !!fresh;
1335
+ const turnedOff = explicit ? !toggled.show : before.has(id) && !fresh;
1336
+ const present = docBlocks(tr.doc).get(id);
1337
+ if (!present) {
1338
+ if (turnedOn && fresh) {
1339
+ tr.insert(insertPos(tr.doc, id, order), fresh);
1340
+ result.inserted.push(id);
1341
+ } else if (explicit && toggled.show && !fresh) {
1342
+ result.toggledWithoutContent = true;
1343
+ }
1344
+ continue;
1345
+ }
1346
+ const untouched = isBlockUntouched(present.node, identity);
1347
+ const title = present.node.attrs.blockTitle ?? null;
1348
+ if (!fresh || turnedOff) {
1349
+ if (!turnedOff) {
1350
+ const open = previousPending.get(id);
1351
+ if (!untouched && open?.kind === "remove")
1352
+ pending.set(id, open);
1353
+ continue;
1354
+ }
1355
+ if (tr.doc.childCount <= 1) {
1356
+ result.lastBlockKept = true;
1357
+ continue;
1358
+ }
1359
+ if (untouched) {
1360
+ tr.delete(present.pos, present.pos + present.node.nodeSize);
1361
+ result.removed.push(id);
1362
+ } else {
1363
+ setConflict(id, "remove", null, title);
1364
+ }
1365
+ continue;
1366
+ }
1367
+ if (fresh.attrs.genHash === present.node.attrs.genHash)
1368
+ continue;
1369
+ if (untouched) {
1370
+ tr.replaceWith(present.pos, present.pos + present.node.nodeSize, fresh);
1371
+ result.regenerated.push(id);
1372
+ } else {
1373
+ setConflict(id, "replace", fresh, title);
1374
+ }
1375
+ }
1376
+ const chips = collectChips(generatedHtml);
1377
+ result.variablesUpdated = refreshChips(tr, chips) + refreshIslandChips(tr, chips);
1378
+ const next = { wanted: [...wanted], pending: [...pending.values()] };
1379
+ if (JSON.stringify(next) !== JSON.stringify(stored) || tr.docChanged)
1380
+ tr.setDocAttribute("checklist", next);
1381
+ if (tr.docChanged) {
1382
+ editor.view.dispatch(closeHistory(tr));
1383
+ }
1384
+ result.conflicts = next.pending.map(({ blockId, kind, title }) => ({ blockId, blockTitle: title, kind }));
1385
+ return result;
1386
+ }
1387
+ function resolveChecklistConflict(editor, blockId, resolution) {
1388
+ const stored = readChecklist(editor.state.doc);
1389
+ const entry = stored.pending.find((item) => item.blockId === blockId);
1390
+ const present = docBlocks(editor.state.doc).get(blockId);
1391
+ const tr = editor.state.tr;
1392
+ const rest = { ...stored, pending: stored.pending.filter((item) => item.blockId !== blockId) };
1393
+ if (!entry || !present) {
1394
+ if (entry) {
1395
+ tr.setDocAttribute("checklist", rest);
1396
+ editor.view.dispatch(tr);
1397
+ }
1398
+ return false;
1399
+ }
1400
+ if (resolution === "apply" && entry.kind === "remove" && editor.state.doc.childCount <= 1)
1401
+ return false;
1402
+ const fresh = entry.node ? editor.schema.nodeFromJSON(entry.node) : null;
1403
+ if (resolution === "apply") {
1404
+ if (entry.kind === "remove")
1405
+ tr.delete(present.pos, present.pos + present.node.nodeSize);
1406
+ else if (fresh)
1407
+ tr.replaceWith(present.pos, present.pos + present.node.nodeSize, fresh);
1408
+ } else if (entry.kind === "replace" && fresh) {
1409
+ tr.setNodeMarkup(present.pos, void 0, { ...present.node.attrs, genHash: fresh.attrs.genHash });
1410
+ }
1411
+ tr.setDocAttribute("checklist", rest);
1412
+ editor.view.dispatch(closeHistory(tr));
1413
+ return true;
1414
+ }
1415
+
867
1416
  function h(tag, attrs = {}, children = []) {
868
1417
  const el = document.createElement(tag);
869
1418
  if (attrs.class)
@@ -1252,6 +1801,11 @@ function createToolbar(actions) {
1252
1801
  const strikeBtn = tool("Tachado", "S", () => ed()?.chain().focus().toggleStrike().run(), "line-through");
1253
1802
  const bulletBtn = tool("Lista com marcadores", icon("list-bullets"), () => ed()?.chain().focus().toggleBulletList().run());
1254
1803
  const orderedBtn = tool("Lista numerada", icon("list-numbers"), () => ed()?.chain().focus().toggleOrderedList().run());
1804
+ const topicBtn = tool("Numerar par\xE1grafo (continua a numera\xE7\xE3o da pe\xE7a)", h("span", { class: "poc-tool-glyph", text: "1." }), () => {
1805
+ const e = ed();
1806
+ if (e)
1807
+ toggleTopicNumber(e);
1808
+ });
1255
1809
  const variableBtn = tool("Inserir vari\xE1vel (ou digite / no texto)", icon("brackets-curly"), actions.openVariablePicker);
1256
1810
  const el = h("div", { class: "poc-toolbar", on: { click: (e) => e.stopPropagation() } }, [
1257
1811
  tool("Desfazer", icon("arrow-u-up-left"), () => ed()?.chain().focus().undo().run()),
@@ -1273,6 +1827,7 @@ function createToolbar(actions) {
1273
1827
  tool("Alinhar \xE0 direita", icon("text-align-right"), () => actions.setAlignment("right")),
1274
1828
  tool("Justificar", icon("text-align-justify"), () => actions.setAlignment("justify")),
1275
1829
  sep(),
1830
+ topicBtn,
1276
1831
  bulletBtn,
1277
1832
  orderedBtn,
1278
1833
  sep(),
@@ -1287,7 +1842,7 @@ function createToolbar(actions) {
1287
1842
  variableBtn.hidden = !actions.hasVariables();
1288
1843
  toggleClass(variableBtn, "poc-tool--on", actions.isVariablePickerOpen());
1289
1844
  if (!e || e.isDestroyed) {
1290
- for (const b of [boldBtn, italicBtn, underlineBtn, strikeBtn, bulletBtn, orderedBtn])
1845
+ for (const b of [boldBtn, italicBtn, underlineBtn, strikeBtn, topicBtn, bulletBtn, orderedBtn])
1291
1846
  toggleClass(b, "poc-tool--on", false);
1292
1847
  return;
1293
1848
  }
@@ -1295,6 +1850,7 @@ function createToolbar(actions) {
1295
1850
  toggleClass(italicBtn, "poc-tool--on", e.isActive("italic"));
1296
1851
  toggleClass(underlineBtn, "poc-tool--on", e.isActive("underline"));
1297
1852
  toggleClass(strikeBtn, "poc-tool--on", e.isActive("strike"));
1853
+ toggleClass(topicBtn, "poc-tool--on", hasLeadingTopicNumber(e));
1298
1854
  toggleClass(bulletBtn, "poc-tool--on", e.isActive("bulletList"));
1299
1855
  toggleClass(orderedBtn, "poc-tool--on", e.isActive("orderedList"));
1300
1856
  const family = currentFontFamily(e);
@@ -2167,6 +2723,32 @@ function createMinutaEditor(element, opts) {
2167
2723
  return Promise.resolve(0);
2168
2724
  return refreshVaultImages(ed, resolveUrl, { meta: { [POC_ASSET_SWAP]: true } });
2169
2725
  },
2726
+ applyChecklist(generatedHtml, options) {
2727
+ const ed = tiptap;
2728
+ if (!ed || ed.isDestroyed)
2729
+ throw new Error("editor n\xE3o montado");
2730
+ return applyChecklistToDocument(ed, generatedHtml, options ?? {});
2731
+ },
2732
+ checklistConflicts() {
2733
+ const ed = tiptap;
2734
+ return ed && !ed.isDestroyed ? listConflicts(ed.state.doc) : [];
2735
+ },
2736
+ resolveChecklistConflict(blockId, resolution) {
2737
+ const ed = tiptap;
2738
+ if (!ed || ed.isDestroyed)
2739
+ return false;
2740
+ return resolveChecklistConflict(ed, blockId, resolution);
2741
+ },
2742
+ stampGenerated() {
2743
+ const ed = tiptap;
2744
+ if (!ed || ed.isDestroyed)
2745
+ return 0;
2746
+ return stampGeneratedBlocks(ed);
2747
+ },
2748
+ documentBlockIds() {
2749
+ const ed = tiptap;
2750
+ return ed && !ed.isDestroyed ? [...docBlocks(ed.state.doc).keys()] : [];
2751
+ },
2170
2752
  schedulePaginate,
2171
2753
  refreshChrome,
2172
2754
  totalPages: () => totalPages,
@@ -2186,4 +2768,4 @@ function createMinutaEditor(element, opts) {
2186
2768
  return api;
2187
2769
  }
2188
2770
 
2189
- 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, footnoteSizePt, formatTopicNumber, iconSvg, pocPageKey, readTopicNumber, refreshVaultImages, renewVaultImageTags, renumberPocFootnotes, renumberPocTopics, renumberPocTopicsIn, resolveTemplateVariables, sanitizeFootnoteHtml, sanitizeRichHtml, swapVaultSrc, variableChipHtml, vaultImageHtml, vaultRefOf };
2771
+ 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, PocTopicNumberKeymap, PocTopicRenumber, PocUnderline, PocVariableChip, TOPIC_DEFAULT_GROUP, TOPIC_GROUP_ATTR, TOPIC_NUMBER_ATTR, TOPIC_REF_ATTR, TOPIC_ROMAN_ATTR, blockHash, breaksEqual, buildPocExtensions, collectVaultRefs, computePocBreaks, createMinutaEditor, decodeVaultAttr, dispatchVariableClick, escapeHtml, escapeVaultAttr, extractTemplateVariables, footnoteSizePt, formatTopicNumber, hasLeadingTopicNumber, iconSvg, isBlockUntouched, leadingTopicIndex, minutaBlockFingerprint, pocPageKey, readTopicNumber, refreshVaultImages, renewVaultImageTags, renumberPocFootnotes, renumberPocTopics, renumberPocTopicsIn, resolveTemplateVariables, sanitizeFootnoteHtml, sanitizeRichHtml, swapVaultSrc, toggleTopicNumber, variableChipHtml, vaultImageHtml, vaultRefOf };
package/dist/style.css CHANGED
@@ -1 +1 @@
1
- .poc-editor-root .poc-ins{background:rgba(34,197,94,.16);text-decoration:underline;text-decoration-color:#16a34a}.poc-editor-root .poc-del{background:rgba(239,68,68,.12);color:#b91c1c;text-decoration:line-through}.poc-editor-root .poc-bubble-btn{border-radius:6px;font-size:13px;padding:4px 8px;transition:all .15s;transition-timing-function:ease}.poc-editor-root .poc-toolbar{align-items:center;display:flex;flex-wrap:wrap;gap:2px;padding:4px 12px}@media (max-width:640px){.poc-editor-root .poc-toolbar{flex-wrap:nowrap;overflow-x:auto;padding:4px 8px;scrollbar-width:none}.poc-editor-root .poc-toolbar::-webkit-scrollbar{display:none}.poc-editor-root .poc-toolbar>*{flex:none}}.poc-editor-root .poc-tool{align-items:center;border-radius:6px;display:flex;font-size:13px;height:28px;justify-content:center;min-width:28px;padding-left:6px;padding-right:6px;position:relative;transition:all .15s;transition-timing-function:ease}.poc-editor-root .poc-tool-sep{background:#dee3e7;height:20px;margin:0 6px;width:1px}.poc-editor-root .poc-tool-select{border-radius:6px;font-size:12px;height:28px;padding-left:6px;padding-right:6px}.poc-editor-root .poc-color-underline{background:#dc2626;border-radius:1px;bottom:4px;height:3px;left:7px;position:absolute;right:7px}.poc-editor-root .poc-palette{background:#fff;border:.5px solid #dee3e7;border-radius:8px;box-shadow:0 8px 24px rgba(0,0,0,.12);display:flex;flex-wrap:wrap;gap:2px;left:0;padding:8px;position:absolute;top:32px;width:230px;z-index:50}.poc-editor-root .poc-palette-swatch{border:1px solid rgba(0,0,0,.1);border-radius:3px;cursor:pointer;height:19px;width:19px}.poc-editor-root .poc-palette-swatch:hover{transform:scale(1.2)}.poc-editor-root .poc-palette-custom{align-items:center;color:#565d69;cursor:pointer;display:flex;font-size:12px;margin-top:6px;width:100%}.poc-editor-root .poc-color-swatch{border:1px solid rgba(0,0,0,.12);border-radius:4px;cursor:pointer;height:16px;margin:0 2px;width:16px}.poc-editor-root .poc-color-swatch:hover{transform:scale(1.15)}.poc-editor-root .poc-first-header{align-items:center;cursor:pointer;display:flex;gap:12px;position:absolute;top:0}.poc-editor-root .poc-first-header[style*=center]{justify-content:center}.poc-editor-root .poc-first-header[style*=right]{justify-content:flex-end}.poc-editor-root .poc-first-header:hover,.poc-editor-root .poc-last-footer:hover,.poc-editor-root .poc-page-footerarea:hover,.poc-editor-root .poc-page-headerarea:hover{background:rgba(99,102,241,.06);outline:1px dashed #c7d2fe}.poc-editor-root .poc-last-footer{align-items:center;bottom:0;color:#94a3b8;cursor:pointer;display:flex;font-size:11px;justify-content:space-between;position:absolute}.poc-editor-root .poc-chrome-text{color:#64748b;font-size:11px}.poc-editor-root .poc-chrome-text img{display:inline-block;max-height:48px;max-width:200px;vertical-align:middle}.poc-editor-root .poc-pagebreak-marker{border-top:1px dashed #a5b4fc;font-size:0!important;height:16px;line-height:0!important;margin:6px 0!important;position:relative}.poc-editor-root .poc-pagebreak-marker:after{background:#fff;color:#818cf8;content:"quebra de página do template";font-size:10px;left:50%;line-height:16px;padding:0 8px;position:absolute;top:-8px;transform:translateX(-50%)}.poc-editor-root .poc-page-chrome{color:initial;display:block;font-style:normal;font-weight:400;letter-spacing:normal;line-height:normal;pointer-events:none;text-align:initial;text-indent:0;text-transform:none;-webkit-user-select:none;-moz-user-select:none;user-select:none;white-space:normal}.poc-editor-root .poc-page-chrome>.poc-page-footerarea:not(:hover),.poc-editor-root .poc-page-chrome>.poc-page-headerarea:not(:hover),.poc-editor-root .poc-page-chrome>.poc-page-notes{background:#fff}.poc-editor-root tr.poc-page-chrome-row>td.poc-page-chrome-cell{background:transparent!important;border:0!important;padding:0!important;vertical-align:top}.poc-editor-root .poc-page-footerarea,.poc-editor-root .poc-page-headerarea{cursor:pointer;pointer-events:auto}.poc-editor-root .poc-page-footerarea{align-items:center;color:#94a3b8;display:flex;font-size:11px;justify-content:space-between}.poc-editor-root .poc-page-gapband{background:#eceef1;box-shadow:inset 0 6px 6px -6px rgba(0,0,0,.25),inset 0 -6px 6px -6px rgba(0,0,0,.25)}.poc-editor-root .poc-page-headerarea{align-items:center;display:flex}.poc-editor-root .poc-page-headerarea[style*=center]{justify-content:center}.poc-editor-root .poc-page-headerarea[style*=right]{justify-content:flex-end}.poc-editor-root .ProseMirror{white-space:pre-wrap;word-wrap:break-word}.poc-editor-root h1{color:#0f172a;font-size:24px;font-weight:640;letter-spacing:-.3px;line-height:32px;margin:0 0 20px}.poc-editor-root h2{color:#0f172a;font-size:19px;font-weight:620;letter-spacing:-.2px;line-height:26px;margin:28px 0 14px}.poc-editor-root h3{color:#0f172a;font-size:17px;font-weight:600;letter-spacing:-.1px;line-height:24px;margin:24px 0 12px}.poc-editor-root h1:first-child,.poc-editor-root h2:first-child,.poc-editor-root h3:first-child{margin-top:0}.poc-editor-root strong{font-weight:540}.poc-editor-root p{color:#000;line-height:28px;line-height:1.7em;margin-bottom:.75em;text-align:justify}.poc-editor-root blockquote,.poc-editor-root p{font-size:18px;font-weight:400;letter-spacing:-.4px}.poc-editor-root blockquote{line-height:28px;margin-bottom:2em;margin-left:35%}.poc-editor-root ol,.poc-editor-root ul{margin-bottom:2em}.poc-editor-root li{list-style-position:inside;margin-bottom:16px;margin-left:48px}.poc-editor-root ul>li{list-style-type:disc}.poc-editor-root ol>li{list-style-type:decimal}.poc-editor-root table{border:.5px solid #d4d9df;max-width:100%;table-layout:fixed;width:100%}.poc-editor-root th{font-weight:700}.poc-editor-root td,.poc-editor-root th{color:inherit;font-size:inherit;overflow-wrap:anywhere;padding:12px;text-align:left}.poc-editor-root td{line-height:inherit}.poc-editor-root td p,.poc-editor-root th p{color:inherit;font-size:inherit;line-height:inherit;margin-bottom:0}.poc-editor-root img{display:block;max-width:100%}.poc-editor-root .poc-island--img>img,.poc-editor-root .poc-island>img:only-child{margin-left:auto;margin-right:auto}.poc-editor-root .poc-chrome-canvas{overflow:hidden}.poc-editor-root .poc-chrome-canvas [data-el]{position:absolute}.poc-editor-root .poc-chrome-canvas [data-el=text]{color:#64748b;font-size:11px;line-height:1.4;text-align:left;white-space:pre-wrap}.poc-editor-root img[src^="vault://"]{animation:poc-img-loading 1.4s ease-in-out infinite}@keyframes poc-img-loading{0%,to{opacity:.5}50%{opacity:.9}}.poc-editor-root .poc-lone-row{justify-content:center!important}.poc-editor-root .poc-lone-row>:not(.poc-lone-cell){flex:0 0 0!important;min-width:0!important;padding:0!important}.poc-editor-root .poc-lone-row>.poc-lone-cell{align-items:center!important;flex:0 0 auto!important;max-width:100%;min-width:0}.poc-editor-root [data-horizontal-align=left] .poc-island--img>img,.poc-editor-root [data-horizontal-align=left] .poc-island>img:only-child{margin-left:0}.poc-editor-root [data-horizontal-align=right] .poc-island--img>img,.poc-editor-root [data-horizontal-align=right] .poc-island>img:only-child{margin-right:0}.poc-editor-root .poc-review-cursor .poc-minuta-block{cursor:pointer}.poc-editor-root .poc-minuta-block{border-radius:4px;position:relative}.poc-editor-root .poc-minuta-block:hover{outline:1px dashed #c7d2fe;outline-offset:6px}.poc-editor-root.poc-no-block-origin .poc-minuta-block:hover{outline:none}.poc-editor-root.poc-no-block-origin .poc-minuta-block:hover:before{content:none}.poc-editor-root .poc-minuta-block:hover:before{background:#eef2ff;border:1px solid #c7d2fe;border-radius:4px;color:#6366f1;content:attr(data-block-title);font-size:10px;letter-spacing:.04em;padding:1px 8px;pointer-events:none;position:absolute;right:0;text-transform:uppercase;top:-22px;z-index:3}.poc-editor-root .poc-varchip{background:rgba(59,130,246,.06);border-radius:3px;box-shadow:inset 0 0 0 1px rgba(59,130,246,.25);cursor:pointer;transition:background .15s ease}.poc-editor-root .poc-varchip:hover{background:rgba(59,130,246,.16)}.poc-editor-root .poc-tplvar{background:rgba(251,191,36,.16);border-radius:3px;box-shadow:inset 0 0 0 1px rgba(180,83,9,.35);color:#78350f;display:inline;padding:0 2px;white-space:pre-wrap}.poc-editor-root .poc-tplvar--unknown{background:rgba(244,63,94,.12);box-shadow:inset 0 0 0 1px rgba(190,18,60,.45);color:#881337;-webkit-text-decoration:underline dotted;text-decoration:underline dotted}.poc-editor-root .ProseMirror-selectednode.poc-tplvar{box-shadow:inset 0 0 0 2px rgba(180,83,9,.75)}.poc-editor-root .poc-missing-image .missing-image-warning:hover{background-color:rgba(254,174,0,.09);border-color:rgba(223,139,13,.85)}.poc-editor-root .poc-missing-image__remove{background:hsla(0,0%,100%,.75);border:none;border-radius:999px;color:#773613;cursor:pointer;font-size:16px;height:22px;line-height:1;opacity:0;position:absolute;right:4px;top:4px;transition:opacity .15s ease,background .15s ease;width:22px}.poc-editor-root .poc-missing-image:hover .poc-missing-image__remove{opacity:1}.poc-editor-root .poc-missing-image__remove:hover{background:rgba(254,174,0,.35)}.poc-editor-root .poc-missing-image.ProseMirror-selectednode .missing-image-warning{outline:2px solid rgba(223,139,13,.9);outline-offset:2px}.poc-editor-root .poc-page-notes{color:#1c242d;font-size:10.67px;line-height:1.25;padding-top:6px}.poc-editor-root .poc-page-notes:before{border-top:.5px solid #9aa4ae;content:"";display:block;margin-bottom:6px;width:30%}.poc-editor-root .poc-page-note{color:#1c242d;font-size:10.67px;font-weight:400;line-height:1.25;margin:0;text-align:justify}.poc-editor-root .poc-page-note sup{font-size:.8em;line-height:0;vertical-align:super}.poc-editor-root .poc-page-note a{color:#0563c1;text-decoration:underline}.poc-editor-root .poc-footnote{color:#1d4ed8;cursor:help;font-size:.7em;line-height:0;vertical-align:super}.poc-editor-root .poc-island{position:relative}.poc-editor-root .poc-island--img{cursor:grab}.poc-editor-root .poc-img-handle{background:#3b82f6;border:2px solid #fff;border-radius:3px;bottom:-6px;box-shadow:0 1px 3px rgba(0,0,0,.3);cursor:nwse-resize;height:12px;opacity:0;position:absolute;right:-6px;transition:opacity .12s ease;width:12px}.poc-editor-root .poc-island--img.ProseMirror-selectednode .poc-img-handle,.poc-editor-root .poc-island--img:hover .poc-img-handle{opacity:1}.poc-editor-root .poc-island.ProseMirror-selectednode{outline:2px solid #93c5fd;outline-offset:2px}.poc-editor-root .h-full{height:100%}.poc-editor-root .w-full{width:100%}.poc-editor-root .w-1px{width:1px}.poc-editor-root .w-64px{width:64px}.poc-editor-root .h-16px{height:16px}.poc-editor-root .flex{display:flex}.poc-editor-root .flex-col{flex-direction:column}.poc-editor-root .flex-1{flex:1 1 0%}.poc-editor-root .items-center{align-items:center}.poc-editor-root .gap-2px{gap:2px}.poc-editor-root .min-h-0{min-height:0}.poc-editor-root .overflow-y-auto{overflow-y:auto}.poc-editor-root .relative{position:relative}.poc-editor-root .hidden{display:none}.poc-editor-root .inline-block{display:inline-block}.poc-editor-root .align-middle{vertical-align:middle}.poc-editor-root .ml-auto{margin-left:auto}.poc-editor-root .mx-auto{margin-left:auto;margin-right:auto}.poc-editor-root .mx-2px{margin-left:2px;margin-right:2px}.poc-editor-root .p-2px{padding:2px}.poc-editor-root .py-24px{padding-bottom:24px;padding-top:24px}.poc-editor-root .bg-white{background:#fff}.poc-editor-root .bg-gray-120{background:#ebedf0}.poc-editor-root .bg-gray-200{background:#e5e7eb}.poc-editor-root .border{border-style:solid;border-width:1px}.poc-editor-root .border-gray-200{border-color:#e5e7eb}.poc-editor-root .rounded-8px{border-radius:8px}.poc-editor-root .shadow-md{box-shadow:0 4px 6px -1px rgba(0,0,0,.1),0 2px 4px -2px rgba(0,0,0,.1)}.poc-editor-root .shadow-lg{box-shadow:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -4px rgba(0,0,0,.1)}.poc-editor-root .font-bold{font-weight:700}.poc-editor-root .font-semibold{font-weight:600}.poc-editor-root .italic{font-style:italic}.poc-editor-root .underline{text-decoration:underline}.poc-editor-root .line-through{text-decoration:line-through}.poc-editor-root .text-red-600{color:#dc2626}.poc-editor-root .body-13-regular{font-size:13px;font-weight:400}.poc-editor-root{font-family:Inter Variable,Inter,-apple-system,Segoe UI,Roboto,sans-serif}.poc-editor-root .poc-toolbar{background:#fafbfc;border-bottom:.5px solid #dee3e7}.poc-editor-root .poc-tool{background:transparent;border:none;cursor:pointer}.poc-editor-root .poc-tool:hover{background:#ebedf0}.poc-editor-root .poc-tool--on{background:#e2e8f0}.poc-editor-root .poc-tool-select{background:#fff;border:.5px solid #d4d9df;cursor:pointer}.poc-editor-root .ProseMirror{outline:none}.poc-editor-root td,.poc-editor-root th{border:.5px solid #d4d9df}.poc-editor-root .poc-footnote-pop{background:#fff;border:.5px solid #d4d9df;border-radius:10px;box-shadow:0 10px 25px -5px rgba(0,0,0,.18);padding:10px 12px;position:absolute;width:364px;z-index:40}.poc-editor-root .poc-footnote-pop__head{color:#6b7280;font-size:11px;font-weight:650;letter-spacing:.04em;margin-bottom:6px;text-transform:uppercase}.poc-editor-root .poc-footnote-pop__toolbar{align-items:center;display:flex;gap:4px;margin-bottom:6px}.poc-editor-root .poc-footnote-pop__tool{background:transparent;border:none;border-radius:6px;color:#374151;cursor:pointer;font-size:12px;height:24px;width:24px}.poc-editor-root .poc-footnote-pop__tool:hover{background:rgba(28,36,45,.06)}.poc-editor-root .poc-footnote-pop__sep{background:#e5e7eb;height:16px;margin:0 2px;width:1px}.poc-editor-root .poc-footnote-pop__select{background:#fff;border:1px solid #e5e7eb;border-radius:6px;color:#374151;font-size:11px;height:24px;max-width:108px;outline:none}.poc-editor-root .poc-footnote-pop__select--narrow{max-width:58px}.poc-editor-root .poc-footnote-pop__swatch{border:1px solid rgba(0,0,0,.15);border-radius:4px;cursor:pointer;height:14px;padding:0;width:14px}.poc-editor-root .poc-footnote-pop__input{border:1px solid #e5e7eb;border-radius:8px;color:#111827;font-size:12.5px;line-height:1.5;max-height:160px;min-height:58px;outline:none;overflow-y:auto;padding:6px 8px;width:100%}.poc-editor-root .poc-footnote-pop__input:focus{border-color:#a5b4fc}.poc-editor-root .poc-footnote-pop__input:empty:before{color:#9ca3af;content:attr(data-placeholder)}.poc-editor-root .poc-footnote-pop__actions{align-items:center;display:flex;gap:8px;margin-top:8px}.poc-editor-root .poc-footnote-pop__spacer{flex:1}.poc-editor-root .poc-footnote-pop__actions button{background:transparent;border:none;border-radius:8px;color:#565d69;cursor:pointer;font-size:12px;font-weight:500;padding:4px 12px}.poc-editor-root .poc-footnote-pop__remove{color:#b91c1c!important}.poc-editor-root .poc-footnote-pop__save{background:rgba(0,0,0,.88)!important;color:#fff!important}.poc-editor-root{display:flex;flex-direction:column;height:100%;width:100%}.poc-editor-root .w-118px{width:118px}.poc-editor-root .poc-varpicker{background:#fff;border:1px solid #e5e7eb;border-radius:10px;box-shadow:0 12px 24px -8px rgba(0,0,0,.25);display:flex;flex-direction:column;font-size:13px;max-height:300px;overflow:hidden;position:absolute;width:320px;z-index:60}.poc-editor-root .poc-varpicker__search{border:0;border-bottom:1px solid #f3f4f6;font-family:inherit;font-size:13px;outline:none;padding:10px 12px}.poc-editor-root .poc-varpicker__hint{border-bottom:1px solid #f3f4f6;color:#6b7280;font-size:12px;padding:8px 12px}.poc-editor-root .poc-varpicker__list{flex:1;min-height:0;overflow-y:auto;padding:4px}.poc-editor-root .poc-varpicker__empty{color:#6b7280;font-size:12px;margin:0;padding:12px}.poc-editor-root .poc-varpicker__group{color:#9ca3af;font-size:11px;font-weight:600;letter-spacing:.04em;padding:8px 8px 4px;text-transform:uppercase}.poc-editor-root .poc-varpicker__item{background:none;border:0;border-radius:6px;cursor:pointer;display:grid;font-family:inherit;gap:2px 8px;grid-template-columns:1fr auto;padding:6px 8px;text-align:left;width:100%}.poc-editor-root .poc-varpicker__item--on{background:#f3f4f6}.poc-editor-root .poc-varpicker__label{color:#111827;font-weight:500}.poc-editor-root .poc-varpicker__key{color:#9ca3af;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px}.poc-editor-root .poc-varpicker__desc{color:#6b7280;display:-webkit-box;font-size:11px;grid-column:1/-1;-webkit-line-clamp:2;line-height:1.4;overflow:hidden;-webkit-box-orient:vertical}.poc-editor-root [hidden]{display:none!important}.poc-editor-root .poc-varchip--flash{background:rgba(59,130,246,.35);transition:background .9s ease}
1
+ .poc-editor-root .poc-ins{background:rgba(34,197,94,.16);text-decoration:underline;text-decoration-color:#16a34a}.poc-editor-root .poc-del{background:rgba(239,68,68,.12);color:#b91c1c;text-decoration:line-through}.poc-editor-root .poc-bubble-btn{border-radius:6px;font-size:13px;padding:4px 8px;transition:all .15s;transition-timing-function:ease}.poc-editor-root .poc-toolbar{align-items:center;display:flex;flex-wrap:wrap;gap:2px;padding:4px 12px}@media (max-width:640px){.poc-editor-root .poc-toolbar{flex-wrap:nowrap;overflow-x:auto;padding:4px 8px;scrollbar-width:none}.poc-editor-root .poc-toolbar::-webkit-scrollbar{display:none}.poc-editor-root .poc-toolbar>*{flex:none}}.poc-editor-root .poc-tool{align-items:center;border-radius:6px;display:flex;font-size:13px;height:28px;justify-content:center;min-width:28px;padding-left:6px;padding-right:6px;position:relative;transition:all .15s;transition-timing-function:ease}.poc-editor-root .poc-tool-glyph{font-size:11px;font-weight:600;line-height:1}.poc-editor-root .poc-tool-sep{background:#dee3e7;height:20px;margin:0 6px;width:1px}.poc-editor-root .poc-tool-select{border-radius:6px;font-size:12px;height:28px;padding-left:6px;padding-right:6px}.poc-editor-root .poc-color-underline{background:#dc2626;border-radius:1px;bottom:4px;height:3px;left:7px;position:absolute;right:7px}.poc-editor-root .poc-palette{background:#fff;border:.5px solid #dee3e7;border-radius:8px;box-shadow:0 8px 24px rgba(0,0,0,.12);display:flex;flex-wrap:wrap;gap:2px;left:0;padding:8px;position:absolute;top:32px;width:230px;z-index:50}.poc-editor-root .poc-palette-swatch{border:1px solid rgba(0,0,0,.1);border-radius:3px;cursor:pointer;height:19px;width:19px}.poc-editor-root .poc-palette-swatch:hover{transform:scale(1.2)}.poc-editor-root .poc-palette-custom{align-items:center;color:#565d69;cursor:pointer;display:flex;font-size:12px;margin-top:6px;width:100%}.poc-editor-root .poc-color-swatch{border:1px solid rgba(0,0,0,.12);border-radius:4px;cursor:pointer;height:16px;margin:0 2px;width:16px}.poc-editor-root .poc-color-swatch:hover{transform:scale(1.15)}.poc-editor-root .poc-first-header{align-items:center;cursor:pointer;display:flex;gap:12px;position:absolute;top:0}.poc-editor-root .poc-first-header[style*=center]{justify-content:center}.poc-editor-root .poc-first-header[style*=right]{justify-content:flex-end}.poc-editor-root .poc-first-header:hover,.poc-editor-root .poc-last-footer:hover,.poc-editor-root .poc-page-footerarea:hover,.poc-editor-root .poc-page-headerarea:hover{background:rgba(99,102,241,.06);outline:1px dashed #c7d2fe}.poc-editor-root .poc-last-footer{align-items:center;bottom:0;color:#94a3b8;cursor:pointer;display:flex;font-size:11px;justify-content:space-between;position:absolute}.poc-editor-root .poc-chrome-text{color:#64748b;font-size:11px}.poc-editor-root .poc-chrome-text img{display:inline-block;max-height:48px;max-width:200px;vertical-align:middle}.poc-editor-root .poc-pagebreak-marker{border-top:1px dashed #a5b4fc;font-size:0!important;height:16px;line-height:0!important;margin:6px 0!important;position:relative}.poc-editor-root .poc-pagebreak-marker:after{background:#fff;color:#818cf8;content:"quebra de página do template";font-size:10px;left:50%;line-height:16px;padding:0 8px;position:absolute;top:-8px;transform:translateX(-50%)}.poc-editor-root .poc-page-chrome{color:initial;display:block;font-style:normal;font-weight:400;letter-spacing:normal;line-height:normal;pointer-events:none;text-align:initial;text-indent:0;text-transform:none;-webkit-user-select:none;-moz-user-select:none;user-select:none;white-space:normal}.poc-editor-root .poc-page-chrome>.poc-page-footerarea:not(:hover),.poc-editor-root .poc-page-chrome>.poc-page-headerarea:not(:hover),.poc-editor-root .poc-page-chrome>.poc-page-notes{background:#fff}.poc-editor-root tr.poc-page-chrome-row>td.poc-page-chrome-cell{background:transparent!important;border:0!important;padding:0!important;vertical-align:top}.poc-editor-root .poc-page-footerarea,.poc-editor-root .poc-page-headerarea{cursor:pointer;pointer-events:auto}.poc-editor-root .poc-page-footerarea{align-items:center;color:#94a3b8;display:flex;font-size:11px;justify-content:space-between}.poc-editor-root .poc-page-gapband{background:#eceef1;box-shadow:inset 0 6px 6px -6px rgba(0,0,0,.25),inset 0 -6px 6px -6px rgba(0,0,0,.25)}.poc-editor-root .poc-page-headerarea{align-items:center;display:flex}.poc-editor-root .poc-page-headerarea[style*=center]{justify-content:center}.poc-editor-root .poc-page-headerarea[style*=right]{justify-content:flex-end}.poc-editor-root .ProseMirror{white-space:pre-wrap;word-wrap:break-word}.poc-editor-root h1{color:#0f172a;font-size:24px;font-weight:640;letter-spacing:-.3px;line-height:32px;margin:0 0 20px}.poc-editor-root h2{color:#0f172a;font-size:19px;font-weight:620;letter-spacing:-.2px;line-height:26px;margin:28px 0 14px}.poc-editor-root h3{color:#0f172a;font-size:17px;font-weight:600;letter-spacing:-.1px;line-height:24px;margin:24px 0 12px}.poc-editor-root h1:first-child,.poc-editor-root h2:first-child,.poc-editor-root h3:first-child{margin-top:0}.poc-editor-root strong{font-weight:540}.poc-editor-root p{color:#000;line-height:28px;line-height:1.7em;margin-bottom:.75em;text-align:justify}.poc-editor-root blockquote,.poc-editor-root p{font-size:18px;font-weight:400;letter-spacing:-.4px}.poc-editor-root blockquote{line-height:28px;margin-bottom:2em;margin-left:35%}.poc-editor-root ol,.poc-editor-root ul{margin-bottom:2em}.poc-editor-root li{list-style-position:inside;margin-bottom:16px;margin-left:48px}.poc-editor-root ul>li{list-style-type:disc}.poc-editor-root ol>li{list-style-type:decimal}.poc-editor-root table{border:.5px solid #d4d9df;max-width:100%;table-layout:fixed;width:100%}.poc-editor-root th{font-weight:700}.poc-editor-root td,.poc-editor-root th{color:inherit;font-size:inherit;overflow-wrap:anywhere;padding:12px;text-align:left}.poc-editor-root td{line-height:inherit}.poc-editor-root td p,.poc-editor-root th p{color:inherit;font-size:inherit;line-height:inherit;margin-bottom:0}.poc-editor-root img{display:block;max-width:100%}.poc-editor-root .poc-island--img>img,.poc-editor-root .poc-island>img:only-child{margin-left:auto;margin-right:auto}.poc-editor-root .poc-chrome-canvas{overflow:hidden}.poc-editor-root .poc-chrome-canvas [data-el]{position:absolute}.poc-editor-root .poc-chrome-canvas [data-el=text]{color:#64748b;font-size:11px;line-height:1.4;text-align:left;white-space:pre-wrap}.poc-editor-root img[src^="vault://"]{animation:poc-img-loading 1.4s ease-in-out infinite}@keyframes poc-img-loading{0%,to{opacity:.5}50%{opacity:.9}}.poc-editor-root .poc-lone-row{justify-content:center!important}.poc-editor-root .poc-lone-row>:not(.poc-lone-cell){flex:0 0 0!important;min-width:0!important;padding:0!important}.poc-editor-root .poc-lone-row>.poc-lone-cell{align-items:center!important;flex:0 0 auto!important;max-width:100%;min-width:0}.poc-editor-root [data-horizontal-align=left] .poc-island--img>img,.poc-editor-root [data-horizontal-align=left] .poc-island>img:only-child{margin-left:0}.poc-editor-root [data-horizontal-align=right] .poc-island--img>img,.poc-editor-root [data-horizontal-align=right] .poc-island>img:only-child{margin-right:0}.poc-editor-root .poc-review-cursor .poc-minuta-block{cursor:pointer}.poc-editor-root .poc-minuta-block{border-radius:4px;position:relative}.poc-editor-root .poc-minuta-block:hover{outline:1px dashed #c7d2fe;outline-offset:6px}.poc-editor-root.poc-no-block-origin .poc-minuta-block:hover{outline:none}.poc-editor-root.poc-no-block-origin .poc-minuta-block:hover:before{content:none}.poc-editor-root .poc-minuta-block:hover:before{background:#eef2ff;border:1px solid #c7d2fe;border-radius:4px;color:#6366f1;content:attr(data-block-title);font-size:10px;letter-spacing:.04em;padding:1px 8px;pointer-events:none;position:absolute;right:0;text-transform:uppercase;top:-22px;z-index:3}.poc-editor-root .poc-varchip{background:rgba(59,130,246,.06);border-radius:3px;box-shadow:inset 0 0 0 1px rgba(59,130,246,.25);cursor:pointer;transition:background .15s ease}.poc-editor-root .poc-varchip:hover{background:rgba(59,130,246,.16)}.poc-editor-root .poc-tplvar{background:rgba(251,191,36,.16);border-radius:3px;box-shadow:inset 0 0 0 1px rgba(180,83,9,.35);color:#78350f;display:inline;padding:0 2px;white-space:pre-wrap}.poc-editor-root .poc-tplvar--unknown{background:rgba(244,63,94,.12);box-shadow:inset 0 0 0 1px rgba(190,18,60,.45);color:#881337;-webkit-text-decoration:underline dotted;text-decoration:underline dotted}.poc-editor-root .ProseMirror-selectednode.poc-tplvar{box-shadow:inset 0 0 0 2px rgba(180,83,9,.75)}.poc-editor-root .poc-missing-image .missing-image-warning:hover{background-color:rgba(254,174,0,.09);border-color:rgba(223,139,13,.85)}.poc-editor-root .poc-missing-image__remove{background:hsla(0,0%,100%,.75);border:none;border-radius:999px;color:#773613;cursor:pointer;font-size:16px;height:22px;line-height:1;opacity:0;position:absolute;right:4px;top:4px;transition:opacity .15s ease,background .15s ease;width:22px}.poc-editor-root .poc-missing-image:hover .poc-missing-image__remove{opacity:1}.poc-editor-root .poc-missing-image__remove:hover{background:rgba(254,174,0,.35)}.poc-editor-root .poc-missing-image.ProseMirror-selectednode .missing-image-warning{outline:2px solid rgba(223,139,13,.9);outline-offset:2px}.poc-editor-root .poc-page-notes{color:#1c242d;font-size:10.67px;line-height:1.25;padding-top:6px}.poc-editor-root .poc-page-notes:before{border-top:.5px solid #9aa4ae;content:"";display:block;margin-bottom:6px;width:30%}.poc-editor-root .poc-page-note{color:#1c242d;font-size:10.67px;font-weight:400;line-height:1.25;margin:0;text-align:justify}.poc-editor-root .poc-page-note sup{font-size:.8em;line-height:0;vertical-align:super}.poc-editor-root .poc-page-note a{color:#0563c1;text-decoration:underline}.poc-editor-root .poc-footnote{color:#1d4ed8;cursor:help;font-size:.7em;line-height:0;vertical-align:super}.poc-editor-root .poc-island{position:relative}.poc-editor-root .poc-island--img{cursor:grab}.poc-editor-root .poc-img-handle{background:#3b82f6;border:2px solid #fff;border-radius:3px;bottom:-6px;box-shadow:0 1px 3px rgba(0,0,0,.3);cursor:nwse-resize;height:12px;opacity:0;position:absolute;right:-6px;transition:opacity .12s ease;width:12px}.poc-editor-root .poc-island--img.ProseMirror-selectednode .poc-img-handle,.poc-editor-root .poc-island--img:hover .poc-img-handle{opacity:1}.poc-editor-root .poc-island.ProseMirror-selectednode{outline:2px solid #93c5fd;outline-offset:2px}.poc-editor-root .h-full{height:100%}.poc-editor-root .w-full{width:100%}.poc-editor-root .w-1px{width:1px}.poc-editor-root .w-64px{width:64px}.poc-editor-root .h-16px{height:16px}.poc-editor-root .flex{display:flex}.poc-editor-root .flex-col{flex-direction:column}.poc-editor-root .flex-1{flex:1 1 0%}.poc-editor-root .items-center{align-items:center}.poc-editor-root .gap-2px{gap:2px}.poc-editor-root .min-h-0{min-height:0}.poc-editor-root .overflow-y-auto{overflow-y:auto}.poc-editor-root .relative{position:relative}.poc-editor-root .hidden{display:none}.poc-editor-root .inline-block{display:inline-block}.poc-editor-root .align-middle{vertical-align:middle}.poc-editor-root .ml-auto{margin-left:auto}.poc-editor-root .mx-auto{margin-left:auto;margin-right:auto}.poc-editor-root .mx-2px{margin-left:2px;margin-right:2px}.poc-editor-root .p-2px{padding:2px}.poc-editor-root .py-24px{padding-bottom:24px;padding-top:24px}.poc-editor-root .bg-white{background:#fff}.poc-editor-root .bg-gray-120{background:#ebedf0}.poc-editor-root .bg-gray-200{background:#e5e7eb}.poc-editor-root .border{border-style:solid;border-width:1px}.poc-editor-root .border-gray-200{border-color:#e5e7eb}.poc-editor-root .rounded-8px{border-radius:8px}.poc-editor-root .shadow-md{box-shadow:0 4px 6px -1px rgba(0,0,0,.1),0 2px 4px -2px rgba(0,0,0,.1)}.poc-editor-root .shadow-lg{box-shadow:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -4px rgba(0,0,0,.1)}.poc-editor-root .font-bold{font-weight:700}.poc-editor-root .font-semibold{font-weight:600}.poc-editor-root .italic{font-style:italic}.poc-editor-root .underline{text-decoration:underline}.poc-editor-root .line-through{text-decoration:line-through}.poc-editor-root .text-red-600{color:#dc2626}.poc-editor-root .body-13-regular{font-size:13px;font-weight:400}.poc-editor-root{font-family:Inter Variable,Inter,-apple-system,Segoe UI,Roboto,sans-serif}.poc-editor-root .poc-toolbar{background:#fafbfc;border-bottom:.5px solid #dee3e7}.poc-editor-root .poc-tool{background:transparent;border:none;cursor:pointer}.poc-editor-root .poc-tool:hover{background:#ebedf0}.poc-editor-root .poc-tool--on{background:#e2e8f0}.poc-editor-root .poc-tool-select{background:#fff;border:.5px solid #d4d9df;cursor:pointer}.poc-editor-root .ProseMirror{outline:none}.poc-editor-root td,.poc-editor-root th{border:.5px solid #d4d9df}.poc-editor-root .poc-footnote-pop{background:#fff;border:.5px solid #d4d9df;border-radius:10px;box-shadow:0 10px 25px -5px rgba(0,0,0,.18);padding:10px 12px;position:absolute;width:364px;z-index:40}.poc-editor-root .poc-footnote-pop__head{color:#6b7280;font-size:11px;font-weight:650;letter-spacing:.04em;margin-bottom:6px;text-transform:uppercase}.poc-editor-root .poc-footnote-pop__toolbar{align-items:center;display:flex;gap:4px;margin-bottom:6px}.poc-editor-root .poc-footnote-pop__tool{background:transparent;border:none;border-radius:6px;color:#374151;cursor:pointer;font-size:12px;height:24px;width:24px}.poc-editor-root .poc-footnote-pop__tool:hover{background:rgba(28,36,45,.06)}.poc-editor-root .poc-footnote-pop__sep{background:#e5e7eb;height:16px;margin:0 2px;width:1px}.poc-editor-root .poc-footnote-pop__select{background:#fff;border:1px solid #e5e7eb;border-radius:6px;color:#374151;font-size:11px;height:24px;max-width:108px;outline:none}.poc-editor-root .poc-footnote-pop__select--narrow{max-width:58px}.poc-editor-root .poc-footnote-pop__swatch{border:1px solid rgba(0,0,0,.15);border-radius:4px;cursor:pointer;height:14px;padding:0;width:14px}.poc-editor-root .poc-footnote-pop__input{border:1px solid #e5e7eb;border-radius:8px;color:#111827;font-size:12.5px;line-height:1.5;max-height:160px;min-height:58px;outline:none;overflow-y:auto;padding:6px 8px;width:100%}.poc-editor-root .poc-footnote-pop__input:focus{border-color:#a5b4fc}.poc-editor-root .poc-footnote-pop__input:empty:before{color:#9ca3af;content:attr(data-placeholder)}.poc-editor-root .poc-footnote-pop__actions{align-items:center;display:flex;gap:8px;margin-top:8px}.poc-editor-root .poc-footnote-pop__spacer{flex:1}.poc-editor-root .poc-footnote-pop__actions button{background:transparent;border:none;border-radius:8px;color:#565d69;cursor:pointer;font-size:12px;font-weight:500;padding:4px 12px}.poc-editor-root .poc-footnote-pop__remove{color:#b91c1c!important}.poc-editor-root .poc-footnote-pop__save{background:rgba(0,0,0,.88)!important;color:#fff!important}.poc-editor-root{display:flex;flex-direction:column;height:100%;width:100%}.poc-editor-root .w-118px{width:118px}.poc-editor-root .poc-varpicker{background:#fff;border:1px solid #e5e7eb;border-radius:10px;box-shadow:0 12px 24px -8px rgba(0,0,0,.25);display:flex;flex-direction:column;font-size:13px;max-height:300px;overflow:hidden;position:absolute;width:320px;z-index:60}.poc-editor-root .poc-varpicker__search{border:0;border-bottom:1px solid #f3f4f6;font-family:inherit;font-size:13px;outline:none;padding:10px 12px}.poc-editor-root .poc-varpicker__hint{border-bottom:1px solid #f3f4f6;color:#6b7280;font-size:12px;padding:8px 12px}.poc-editor-root .poc-varpicker__list{flex:1;min-height:0;overflow-y:auto;padding:4px}.poc-editor-root .poc-varpicker__empty{color:#6b7280;font-size:12px;margin:0;padding:12px}.poc-editor-root .poc-varpicker__group{color:#9ca3af;font-size:11px;font-weight:600;letter-spacing:.04em;padding:8px 8px 4px;text-transform:uppercase}.poc-editor-root .poc-varpicker__item{background:none;border:0;border-radius:6px;cursor:pointer;display:grid;font-family:inherit;gap:2px 8px;grid-template-columns:1fr auto;padding:6px 8px;text-align:left;width:100%}.poc-editor-root .poc-varpicker__item--on{background:#f3f4f6}.poc-editor-root .poc-varpicker__label{color:#111827;font-weight:500}.poc-editor-root .poc-varpicker__key{color:#9ca3af;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px}.poc-editor-root .poc-varpicker__desc{color:#6b7280;display:-webkit-box;font-size:11px;grid-column:1/-1;-webkit-line-clamp:2;line-height:1.4;overflow:hidden;-webkit-box-orient:vertical}.poc-editor-root [hidden]{display:none!important}.poc-editor-root .poc-varchip--flash{background:rgba(59,130,246,.35);transition:background .9s ease}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meistrari/minuta-editor",
3
- "version": "1.5.0",
3
+ "version": "1.7.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",