@meistrari/minuta-editor 1.1.0 → 1.2.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
@@ -37,6 +37,8 @@ const editor = createMinutaEditor(el, {
37
37
  onReady: tiptap => {},
38
38
  onChange: () => {},
39
39
  onEditChrome: part => {}, // clique no cabeçalho/rodapé
40
+ onVariableClick: ({ variableId, parentBlockId, text, kind }) => {}, // kind: 'text' | 'image' | 'missingImage'
41
+ onBlockClick: blockId => {}, // read-only (ou com blockClickWhileEditing)
40
42
  })
41
43
 
42
44
  editor.getHTML() // → string
@@ -46,6 +48,15 @@ editor.setEditable(bool)
46
48
  editor.setChrome({ header, footer, headerScope, footerScope, showPageNumber })
47
49
  editor.setHeader({ logoUrl, position })
48
50
  editor.setVariables(catalogo)
51
+ editor.setBlockClickWhileEditing(bool)
52
+
53
+ // variáveis do case marcadas no documento (<span|img data-variable-id>, aviso de imagem faltando)
54
+ editor.scrollToVariable(id) // → boolean (rola e dá um flash)
55
+ editor.setVariableValue(id, texto) // → nº de ocorrências (o aviso de "faltando" vira chip)
56
+ editor.setVariableImage(id, src) // → nº de ocorrências (o aviso de imagem vira <img>)
57
+ editor.variablesInDocument() // → string[]
58
+ editor.refreshVaultImages(vaultUrl => Promise<string>) // renova o src das imagens inseridas (data-vault-src)
59
+
49
60
  editor.schedulePaginate() // repagina (após mudar algo que afeta altura por fora)
50
61
  editor.totalPages()
51
62
  editor.destroy()
package/dist/index.d.mts CHANGED
@@ -92,6 +92,19 @@ declare function resolveTemplateVariables(html: string | null | undefined, value
92
92
  declare const POC_ASSET_SWAP = "pocAssetSwap";
93
93
  declare const PocDocument: Node<any, any>;
94
94
  declare const PocMinutaBlock: Node<any, any>;
95
+ /**
96
+ * Clique numa variável do case — a ponte documento → formulário. Disparado no
97
+ * DOM do editor (com bubbling), não em `window`: dois editores na página não
98
+ * se cruzam, e o host escuta no próprio elemento. `kind` diz o que foi
99
+ * clicado: chip de texto, `<img data-variable-id>` ou aviso de imagem faltando.
100
+ */
101
+ type VariableClickKind = 'text' | 'image' | 'missingImage';
102
+ declare function dispatchVariableClick(editor: Editor, detail: {
103
+ variableId: string | null;
104
+ parentBlockId: string | null;
105
+ text: string;
106
+ kind: VariableClickKind;
107
+ }): void;
95
108
  declare const PocHtmlIsland: Node<any, any>;
96
109
  /**
97
110
  * Aviso de imagem faltando — a caixa tracejada que o render emite quando a
@@ -113,7 +126,12 @@ declare const PocHtmlIsland: Node<any, any>;
113
126
  */
114
127
  declare const PocMissingImage: Node<any, any>;
115
128
  declare const PocVariableChip: Node<any, any>;
116
- /** `<span data-variable-id>` com o texto trocado — o que `setVariableValue` grava no chip */
129
+ /**
130
+ * `<span data-variable-id>` com o texto trocado — o que `setVariableValue` grava
131
+ * no chip. O aviso de variável sem valor (`data-invalid-variable`, vermelho)
132
+ * é o mesmo nó: ao receber um valor ele vira chip comum — perde a marca de
133
+ * inválido e o estilo do aviso, mantém id, bloco e índice.
134
+ */
117
135
  declare function variableChipHtml(currentHtml: string, text: string): string;
118
136
  /**
119
137
  * Placeholder de variável do TEMPLATE — o `#NOME_VARIAVEL` de antigamente, só
@@ -198,12 +216,14 @@ interface MinutaChromeText {
198
216
  footerScope?: 'all' | 'first';
199
217
  showPageNumber?: boolean;
200
218
  }
201
- /** uma variável do case dentro do documento (`<span data-variable-id>`) */
219
+ /** uma variável do case dentro do documento (`<span data-variable-id>`, `<img data-variable-id>` ou aviso de imagem faltando) */
202
220
  interface MinutaVariableRef {
203
221
  variableId: string;
204
222
  parentBlockId: string | null;
205
- /** texto atual do trecho */
223
+ /** texto atual do trecho (vazio para imagens) */
206
224
  text: string;
225
+ /** o que foi clicado: chip de texto, imagem marcada, ou o aviso de imagem faltando */
226
+ kind: 'text' | 'image' | 'missingImage';
207
227
  }
208
228
  interface MinutaEditorOptions {
209
229
  /** documento inicial (HTML) */
@@ -252,8 +272,10 @@ interface MinutaEditorOptions {
252
272
  /** clique no cabeçalho/rodapé da folha (além do CustomEvent `poc:edit-chrome`) */
253
273
  onEditChrome?: (part: 'header' | 'footer') => void;
254
274
  /**
255
- * Clique numa variável renderizada (`<span data-variable-id>` do documento).
256
- * É a ponte documento → formulário: o host recebe o id e foca o campo.
275
+ * Clique numa variável renderizada: chip de texto (`<span data-variable-id>`),
276
+ * imagem marcada (`<img data-variable-id>`) ou aviso de imagem faltando —
277
+ * `kind` diz qual. É a ponte documento → formulário: o host recebe o id e
278
+ * foca o campo.
257
279
  */
258
280
  onVariableClick?: (variable: MinutaVariableRef) => void;
259
281
  onError?: (message: string) => void;
@@ -291,7 +313,9 @@ interface MinutaEditor {
291
313
  * Troca o texto de TODAS as ocorrências da variável no documento — inclusive
292
314
  * numa edição livre, porque a marca é um nó e sobrevive à lapidação. É o que
293
315
  * deixa um formulário de variáveis continuar valendo depois que o advogado
294
- * editou o texto em volta. Devolve quantas ocorrências mudaram.
316
+ * editou o texto em volta. O aviso de variável sem valor
317
+ * (`data-invalid-variable`) também é uma ocorrência: ao receber valor vira
318
+ * chip comum. Devolve quantas ocorrências mudaram.
295
319
  */
296
320
  setVariableValue: (variableId: string, value: string) => number;
297
321
  /**
@@ -302,6 +326,13 @@ interface MinutaEditor {
302
326
  setVariableImage: (variableId: string, src: string) => number;
303
327
  /** ids marcados no documento, na ordem, sem repetição: chips de texto, imagens e imagens faltando */
304
328
  variablesInDocument: () => string[];
329
+ /**
330
+ * Renova o `src` das imagens inseridas no editor (as que têm `data-vault-src`):
331
+ * o presignado do upload expira, então quem reabre uma edição salva passa
332
+ * um resolvedor `vault://` → URL nova (no defesa.ai, `POST /data/vault/download-url`).
333
+ * Devolve quantos nós mudaram. Não entra no undo nem dispara `onChange`.
334
+ */
335
+ refreshVaultImages: (resolveUrl: (vaultUrl: string) => Promise<string>) => Promise<number>;
305
336
  /** repagina (o host chama após mudar algo que afeta altura fora do editor) */
306
337
  schedulePaginate: () => void;
307
338
  /** reconstrói cabeçalhos/rodapés com o chrome atual, sem repaginar */
@@ -401,6 +432,24 @@ declare function shouldMoveBreakToHeading(params: {
401
432
  capacity: number;
402
433
  }): boolean;
403
434
 
435
+ interface RefreshVaultImagesOptions {
436
+ /**
437
+ * Metadados extras da transação (ex.: a marca de "programática" do embed
438
+ * Nuxt, para o rastro de anotações não contar a troca como texto novo).
439
+ */
440
+ meta?: Record<string, unknown>;
441
+ }
442
+ /**
443
+ * Re-resolve todas as imagens `vault://` do documento: as inseridas pela
444
+ * toolbar entram com `src` presignado (funciona agora) e `data-vault-src`
445
+ * durável; como o presignado expira, quem reabre uma edição salva chama isto
446
+ * no load. `resolveUrl` recebe a `vault://` e devolve a URL nova (no defesa.ai,
447
+ * `POST /data/vault/download-url`). Devolve quantos nós mudaram.
448
+ *
449
+ * Fora do histórico de undo: renovar um link não é uma edição do advogado.
450
+ */
451
+ declare function refreshVaultImages(editor: Editor, resolveUrl: (vaultUrl: string) => Promise<string>, options?: RefreshVaultImagesOptions): Promise<number>;
452
+
404
453
  /**
405
454
  * Sanitização do HTML rico de cabeçalho/rodapé (chrome) — conteúdo criado em
406
455
  * contenteditable e PERSISTIDO (template.headers, case_edition.chrome). Sem
@@ -423,5 +472,5 @@ declare function escapeVaultAttr(value: string): string;
423
472
  declare function decodeVaultAttr(value: string): string;
424
473
  declare function vaultImageHtml(src: string, vaultUrl: string): string;
425
474
 
426
- export { A4_HEIGHT_PX, A4_WIDTH_PX, DEFAULT_MARGINS, DOC_FONTS, ICON_PATHS, POC_ASSET_SWAP, PocDocument, PocFootnoteRef, PocHtmlIsland, PocInlineStyle, PocMinutaBlock, PocMissingImage, PocPageConfig, PocStylePassthrough, PocStyledDiv, PocTemplateVariable, PocUnderline, PocVariableChip, buildPocExtensions, createMinutaEditor, decodeVaultAttr, escapeHtml, escapeVaultAttr, extractTemplateVariables, footnoteHtmlToRuns, footnoteSizePt, iconSvg, renumberPocFootnotes, resolveTemplateVariables, sanitizeFootnoteHtml, sanitizeRichHtml, shouldMoveBreakToHeading, variableChipHtml, vaultImageHtml };
427
- export type { FootnoteRun, MinutaChromeText, MinutaEditor, MinutaEditorOptions, MinutaHeader, MinutaMargins, MinutaVariableCatalog, MinutaVariableDef, MinutaVariableRef, PocKeepWithNextItem };
475
+ export { A4_HEIGHT_PX, A4_WIDTH_PX, DEFAULT_MARGINS, DOC_FONTS, ICON_PATHS, POC_ASSET_SWAP, PocDocument, PocFootnoteRef, PocHtmlIsland, PocInlineStyle, PocMinutaBlock, PocMissingImage, PocPageConfig, PocStylePassthrough, PocStyledDiv, PocTemplateVariable, PocUnderline, PocVariableChip, buildPocExtensions, createMinutaEditor, decodeVaultAttr, dispatchVariableClick, escapeHtml, escapeVaultAttr, extractTemplateVariables, footnoteHtmlToRuns, footnoteSizePt, iconSvg, refreshVaultImages, renumberPocFootnotes, resolveTemplateVariables, sanitizeFootnoteHtml, sanitizeRichHtml, shouldMoveBreakToHeading, variableChipHtml, vaultImageHtml };
476
+ export type { FootnoteRun, MinutaChromeText, MinutaEditor, MinutaEditorOptions, MinutaHeader, MinutaMargins, MinutaVariableCatalog, MinutaVariableDef, MinutaVariableRef, PocKeepWithNextItem, RefreshVaultImagesOptions, VariableClickKind };
package/dist/index.d.ts CHANGED
@@ -92,6 +92,19 @@ declare function resolveTemplateVariables(html: string | null | undefined, value
92
92
  declare const POC_ASSET_SWAP = "pocAssetSwap";
93
93
  declare const PocDocument: Node<any, any>;
94
94
  declare const PocMinutaBlock: Node<any, any>;
95
+ /**
96
+ * Clique numa variável do case — a ponte documento → formulário. Disparado no
97
+ * DOM do editor (com bubbling), não em `window`: dois editores na página não
98
+ * se cruzam, e o host escuta no próprio elemento. `kind` diz o que foi
99
+ * clicado: chip de texto, `<img data-variable-id>` ou aviso de imagem faltando.
100
+ */
101
+ type VariableClickKind = 'text' | 'image' | 'missingImage';
102
+ declare function dispatchVariableClick(editor: Editor, detail: {
103
+ variableId: string | null;
104
+ parentBlockId: string | null;
105
+ text: string;
106
+ kind: VariableClickKind;
107
+ }): void;
95
108
  declare const PocHtmlIsland: Node<any, any>;
96
109
  /**
97
110
  * Aviso de imagem faltando — a caixa tracejada que o render emite quando a
@@ -113,7 +126,12 @@ declare const PocHtmlIsland: Node<any, any>;
113
126
  */
114
127
  declare const PocMissingImage: Node<any, any>;
115
128
  declare const PocVariableChip: Node<any, any>;
116
- /** `<span data-variable-id>` com o texto trocado — o que `setVariableValue` grava no chip */
129
+ /**
130
+ * `<span data-variable-id>` com o texto trocado — o que `setVariableValue` grava
131
+ * no chip. O aviso de variável sem valor (`data-invalid-variable`, vermelho)
132
+ * é o mesmo nó: ao receber um valor ele vira chip comum — perde a marca de
133
+ * inválido e o estilo do aviso, mantém id, bloco e índice.
134
+ */
117
135
  declare function variableChipHtml(currentHtml: string, text: string): string;
118
136
  /**
119
137
  * Placeholder de variável do TEMPLATE — o `#NOME_VARIAVEL` de antigamente, só
@@ -198,12 +216,14 @@ interface MinutaChromeText {
198
216
  footerScope?: 'all' | 'first';
199
217
  showPageNumber?: boolean;
200
218
  }
201
- /** uma variável do case dentro do documento (`<span data-variable-id>`) */
219
+ /** uma variável do case dentro do documento (`<span data-variable-id>`, `<img data-variable-id>` ou aviso de imagem faltando) */
202
220
  interface MinutaVariableRef {
203
221
  variableId: string;
204
222
  parentBlockId: string | null;
205
- /** texto atual do trecho */
223
+ /** texto atual do trecho (vazio para imagens) */
206
224
  text: string;
225
+ /** o que foi clicado: chip de texto, imagem marcada, ou o aviso de imagem faltando */
226
+ kind: 'text' | 'image' | 'missingImage';
207
227
  }
208
228
  interface MinutaEditorOptions {
209
229
  /** documento inicial (HTML) */
@@ -252,8 +272,10 @@ interface MinutaEditorOptions {
252
272
  /** clique no cabeçalho/rodapé da folha (além do CustomEvent `poc:edit-chrome`) */
253
273
  onEditChrome?: (part: 'header' | 'footer') => void;
254
274
  /**
255
- * Clique numa variável renderizada (`<span data-variable-id>` do documento).
256
- * É a ponte documento → formulário: o host recebe o id e foca o campo.
275
+ * Clique numa variável renderizada: chip de texto (`<span data-variable-id>`),
276
+ * imagem marcada (`<img data-variable-id>`) ou aviso de imagem faltando —
277
+ * `kind` diz qual. É a ponte documento → formulário: o host recebe o id e
278
+ * foca o campo.
257
279
  */
258
280
  onVariableClick?: (variable: MinutaVariableRef) => void;
259
281
  onError?: (message: string) => void;
@@ -291,7 +313,9 @@ interface MinutaEditor {
291
313
  * Troca o texto de TODAS as ocorrências da variável no documento — inclusive
292
314
  * numa edição livre, porque a marca é um nó e sobrevive à lapidação. É o que
293
315
  * deixa um formulário de variáveis continuar valendo depois que o advogado
294
- * editou o texto em volta. Devolve quantas ocorrências mudaram.
316
+ * editou o texto em volta. O aviso de variável sem valor
317
+ * (`data-invalid-variable`) também é uma ocorrência: ao receber valor vira
318
+ * chip comum. Devolve quantas ocorrências mudaram.
295
319
  */
296
320
  setVariableValue: (variableId: string, value: string) => number;
297
321
  /**
@@ -302,6 +326,13 @@ interface MinutaEditor {
302
326
  setVariableImage: (variableId: string, src: string) => number;
303
327
  /** ids marcados no documento, na ordem, sem repetição: chips de texto, imagens e imagens faltando */
304
328
  variablesInDocument: () => string[];
329
+ /**
330
+ * Renova o `src` das imagens inseridas no editor (as que têm `data-vault-src`):
331
+ * o presignado do upload expira, então quem reabre uma edição salva passa
332
+ * um resolvedor `vault://` → URL nova (no defesa.ai, `POST /data/vault/download-url`).
333
+ * Devolve quantos nós mudaram. Não entra no undo nem dispara `onChange`.
334
+ */
335
+ refreshVaultImages: (resolveUrl: (vaultUrl: string) => Promise<string>) => Promise<number>;
305
336
  /** repagina (o host chama após mudar algo que afeta altura fora do editor) */
306
337
  schedulePaginate: () => void;
307
338
  /** reconstrói cabeçalhos/rodapés com o chrome atual, sem repaginar */
@@ -401,6 +432,24 @@ declare function shouldMoveBreakToHeading(params: {
401
432
  capacity: number;
402
433
  }): boolean;
403
434
 
435
+ interface RefreshVaultImagesOptions {
436
+ /**
437
+ * Metadados extras da transação (ex.: a marca de "programática" do embed
438
+ * Nuxt, para o rastro de anotações não contar a troca como texto novo).
439
+ */
440
+ meta?: Record<string, unknown>;
441
+ }
442
+ /**
443
+ * Re-resolve todas as imagens `vault://` do documento: as inseridas pela
444
+ * toolbar entram com `src` presignado (funciona agora) e `data-vault-src`
445
+ * durável; como o presignado expira, quem reabre uma edição salva chama isto
446
+ * no load. `resolveUrl` recebe a `vault://` e devolve a URL nova (no defesa.ai,
447
+ * `POST /data/vault/download-url`). Devolve quantos nós mudaram.
448
+ *
449
+ * Fora do histórico de undo: renovar um link não é uma edição do advogado.
450
+ */
451
+ declare function refreshVaultImages(editor: Editor, resolveUrl: (vaultUrl: string) => Promise<string>, options?: RefreshVaultImagesOptions): Promise<number>;
452
+
404
453
  /**
405
454
  * Sanitização do HTML rico de cabeçalho/rodapé (chrome) — conteúdo criado em
406
455
  * contenteditable e PERSISTIDO (template.headers, case_edition.chrome). Sem
@@ -423,5 +472,5 @@ declare function escapeVaultAttr(value: string): string;
423
472
  declare function decodeVaultAttr(value: string): string;
424
473
  declare function vaultImageHtml(src: string, vaultUrl: string): string;
425
474
 
426
- export { A4_HEIGHT_PX, A4_WIDTH_PX, DEFAULT_MARGINS, DOC_FONTS, ICON_PATHS, POC_ASSET_SWAP, PocDocument, PocFootnoteRef, PocHtmlIsland, PocInlineStyle, PocMinutaBlock, PocMissingImage, PocPageConfig, PocStylePassthrough, PocStyledDiv, PocTemplateVariable, PocUnderline, PocVariableChip, buildPocExtensions, createMinutaEditor, decodeVaultAttr, escapeHtml, escapeVaultAttr, extractTemplateVariables, footnoteHtmlToRuns, footnoteSizePt, iconSvg, renumberPocFootnotes, resolveTemplateVariables, sanitizeFootnoteHtml, sanitizeRichHtml, shouldMoveBreakToHeading, variableChipHtml, vaultImageHtml };
427
- export type { FootnoteRun, MinutaChromeText, MinutaEditor, MinutaEditorOptions, MinutaHeader, MinutaMargins, MinutaVariableCatalog, MinutaVariableDef, MinutaVariableRef, PocKeepWithNextItem };
475
+ export { A4_HEIGHT_PX, A4_WIDTH_PX, DEFAULT_MARGINS, DOC_FONTS, ICON_PATHS, POC_ASSET_SWAP, PocDocument, PocFootnoteRef, PocHtmlIsland, PocInlineStyle, PocMinutaBlock, PocMissingImage, PocPageConfig, PocStylePassthrough, PocStyledDiv, PocTemplateVariable, PocUnderline, PocVariableChip, buildPocExtensions, createMinutaEditor, decodeVaultAttr, dispatchVariableClick, escapeHtml, escapeVaultAttr, extractTemplateVariables, footnoteHtmlToRuns, footnoteSizePt, iconSvg, refreshVaultImages, renumberPocFootnotes, resolveTemplateVariables, sanitizeFootnoteHtml, sanitizeRichHtml, shouldMoveBreakToHeading, variableChipHtml, vaultImageHtml };
476
+ export type { FootnoteRun, MinutaChromeText, MinutaEditor, MinutaEditorOptions, MinutaHeader, MinutaMargins, MinutaVariableCatalog, MinutaVariableDef, MinutaVariableRef, PocKeepWithNextItem, RefreshVaultImagesOptions, VariableClickKind };
package/dist/index.mjs CHANGED
@@ -68,6 +68,11 @@ const PocMinutaBlock = Node$1.create({
68
68
  }), 0];
69
69
  }
70
70
  });
71
+ function dispatchVariableClick(editor, detail) {
72
+ if (!detail.variableId)
73
+ return;
74
+ editor.view.dom.dispatchEvent(new CustomEvent("poc:variable-click", { bubbles: true, detail }));
75
+ }
71
76
  const PocHtmlIsland = Node$1.create({
72
77
  name: "htmlIsland",
73
78
  group: "block",
@@ -95,6 +100,17 @@ const PocHtmlIsland = Node$1.create({
95
100
  dom.className = "poc-island";
96
101
  dom.contentEditable = "false";
97
102
  dom.innerHTML = sanitizeRichHtml(node.attrs.html);
103
+ dom.addEventListener("click", (event) => {
104
+ const target = event.target?.closest?.("img[data-variable-id]");
105
+ if (!target)
106
+ return;
107
+ dispatchVariableClick(editor, {
108
+ variableId: target.getAttribute("data-variable-id"),
109
+ parentBlockId: target.getAttribute("data-parent-block-id"),
110
+ text: "",
111
+ kind: "image"
112
+ });
113
+ });
98
114
  const img = dom.children.length === 1 && dom.firstElementChild instanceof HTMLImageElement ? dom.firstElementChild : null;
99
115
  if (img)
100
116
  dom.classList.add("poc-island--img");
@@ -206,11 +222,23 @@ const PocMissingImage = Node$1.create({
206
222
  const caixa = document.createElement("div");
207
223
  caixa.className = "missing-image-warning";
208
224
  caixa.setAttribute("style", MISSING_IMAGE_BOX_STYLE);
225
+ caixa.setAttribute("data-variable-id", node.attrs.variableId ?? "");
226
+ caixa.setAttribute("data-parent-block-id", node.attrs.parentBlockId ?? "");
209
227
  const linha = document.createElement("span");
210
228
  linha.setAttribute("style", MISSING_IMAGE_ROW_STYLE);
211
229
  linha.textContent = `\u26A0 A imagem ${node.attrs.variableId ?? ""} est\xE1 faltando`;
212
230
  caixa.appendChild(linha);
213
231
  dom.appendChild(caixa);
232
+ dom.addEventListener("click", (event) => {
233
+ if (event.target.closest(".poc-missing-image__remove"))
234
+ return;
235
+ dispatchVariableClick(editor, {
236
+ variableId: node.attrs.variableId,
237
+ parentBlockId: node.attrs.parentBlockId,
238
+ text: "",
239
+ kind: "missingImage"
240
+ });
241
+ });
214
242
  if (!editor.isEditable)
215
243
  return { dom };
216
244
  caixa.style.position = "relative";
@@ -280,14 +308,12 @@ const PocVariableChip = Node$1.create({
280
308
  dom.innerHTML = sanitizeRichHtml(node.attrs.html);
281
309
  dom.addEventListener("click", (event) => {
282
310
  event.preventDefault();
283
- editor.view.dom.dispatchEvent(new CustomEvent("poc:variable-click", {
284
- bubbles: true,
285
- detail: {
286
- variableId: node.attrs.variableId,
287
- parentBlockId: node.attrs.parentBlockId,
288
- text: node.attrs.text
289
- }
290
- }));
311
+ dispatchVariableClick(editor, {
312
+ variableId: node.attrs.variableId,
313
+ parentBlockId: node.attrs.parentBlockId,
314
+ text: node.attrs.text,
315
+ kind: "text"
316
+ });
291
317
  });
292
318
  return { dom };
293
319
  };
@@ -300,6 +326,10 @@ function variableChipHtml(currentHtml, text) {
300
326
  if (!el)
301
327
  return currentHtml;
302
328
  el.textContent = text;
329
+ if (el.hasAttribute("data-invalid-variable")) {
330
+ el.removeAttribute("data-invalid-variable");
331
+ el.removeAttribute("style");
332
+ }
303
333
  return el.outerHTML;
304
334
  }
305
335
  function readCatalog(catalog) {
@@ -636,7 +666,7 @@ function resolveTemplateVariables(html, values) {
636
666
  return { html: resolved, missing };
637
667
  }
638
668
 
639
- function escapeAttr(value) {
669
+ function escapeAttr$1(value) {
640
670
  return value.replace(/&/g, "&amp;").replace(/"/g, "&quot;");
641
671
  }
642
672
  function escapeVaultAttr(value) {
@@ -646,7 +676,81 @@ function decodeVaultAttr(value) {
646
676
  return value.replace(/&quot;/g, '"').replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&amp;/g, "&");
647
677
  }
648
678
  function vaultImageHtml(src, vaultUrl) {
649
- return `<img src="${escapeAttr(src)}" data-vault-src="${escapeVaultAttr(vaultUrl)}" style="width: 420px; height: auto; max-width: 100%;">`;
679
+ return `<img src="${escapeAttr$1(src)}" data-vault-src="${escapeVaultAttr(vaultUrl)}" style="width: 420px; height: auto; max-width: 100%;">`;
680
+ }
681
+
682
+ function escapeAttr(value) {
683
+ return value.replace(/&/g, "&amp;").replace(/"/g, "&quot;");
684
+ }
685
+ function escapeRegExp(value) {
686
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
687
+ }
688
+ function swapSrcKeepRef(html, attrValue, freshUrl) {
689
+ const needle = `data-vault-src="${attrValue}"`;
690
+ if (!html.includes(needle))
691
+ return html;
692
+ const tagRe = new RegExp(`<img\\b[^>]*?${escapeRegExp(needle)}[^>]*?>`, "g");
693
+ return html.replace(tagRe, (tag) => tag.replace(/\ssrc="[^"]*"/, ` src="${escapeAttr(freshUrl)}"`));
694
+ }
695
+ async function refreshVaultImages(editor, resolveUrl, options = {}) {
696
+ if (!editor || editor.isDestroyed)
697
+ return 0;
698
+ const refs = /* @__PURE__ */ new Set();
699
+ editor.state.doc.descendants((node) => {
700
+ if (node.type.name !== "htmlIsland")
701
+ return;
702
+ for (const m of String(node.attrs.html ?? "").matchAll(/data-vault-src="([^"]+)"/g))
703
+ refs.add(m[1]);
704
+ });
705
+ if (!refs.size)
706
+ return 0;
707
+ const resolved = /* @__PURE__ */ new Map();
708
+ await Promise.all([...refs].map(async (attrValue) => {
709
+ try {
710
+ const fresh = await resolveUrl(decodeVaultAttr(attrValue));
711
+ if (!fresh)
712
+ return;
713
+ await new Promise((done) => {
714
+ const img = new Image();
715
+ const finish = () => done();
716
+ img.onload = finish;
717
+ img.onerror = finish;
718
+ setTimeout(finish, 4e3);
719
+ img.src = fresh;
720
+ });
721
+ resolved.set(attrValue, fresh);
722
+ } catch (error) {
723
+ console.error("minuta: falha ao resolver imagem do vault", attrValue, error);
724
+ }
725
+ }));
726
+ if (!resolved.size || editor.isDestroyed)
727
+ return 0;
728
+ const tr = editor.state.tr;
729
+ let touched = 0;
730
+ editor.state.doc.descendants((node, pos) => {
731
+ if (node.type.name !== "htmlIsland")
732
+ return;
733
+ let html = String(node.attrs.html ?? "");
734
+ let changed = false;
735
+ for (const [attrValue, freshUrl] of resolved) {
736
+ const next = swapSrcKeepRef(html, attrValue, freshUrl);
737
+ if (next !== html) {
738
+ html = next;
739
+ changed = true;
740
+ }
741
+ }
742
+ if (changed) {
743
+ tr.setNodeMarkup(pos, void 0, { ...node.attrs, html });
744
+ touched++;
745
+ }
746
+ });
747
+ if (!touched)
748
+ return 0;
749
+ tr.setMeta("addToHistory", false);
750
+ for (const [key, value] of Object.entries(options.meta ?? {}))
751
+ tr.setMeta(key, value);
752
+ editor.view.dispatch(tr);
753
+ return touched;
650
754
  }
651
755
 
652
756
  function h(tag, attrs = {}, children = []) {
@@ -1650,7 +1754,8 @@ function createMinutaEditor(element, opts) {
1650
1754
  opts.onVariableClick?.({
1651
1755
  variableId: detail.variableId,
1652
1756
  parentBlockId: detail.parentBlockId ?? null,
1653
- text: String(detail.text ?? "")
1757
+ text: String(detail.text ?? ""),
1758
+ kind: detail.kind === "image" || detail.kind === "missingImage" ? detail.kind : "text"
1654
1759
  });
1655
1760
  }
1656
1761
  function variableTargetElement(variableId) {
@@ -1661,18 +1766,7 @@ function createMinutaEditor(element, opts) {
1661
1766
  const img = sheet.querySelector(`img[data-variable-id="${id}"]`);
1662
1767
  if (img)
1663
1768
  return img;
1664
- const ed = tiptap;
1665
- if (!ed || ed.isDestroyed)
1666
- return null;
1667
- let found = null;
1668
- ed.state.doc.descendants((node, pos) => {
1669
- if (found || node.type.name !== "missingImage" || node.attrs.variableId !== variableId)
1670
- return;
1671
- const dom = ed.view.nodeDOM(pos);
1672
- if (dom instanceof HTMLElement)
1673
- found = dom;
1674
- });
1675
- return found;
1769
+ return sheet.querySelector(`.poc-missing-image [data-variable-id="${id}"]`);
1676
1770
  }
1677
1771
  function scrollToVariable(variableId) {
1678
1772
  const chip = variableTargetElement(variableId);
@@ -1939,6 +2033,12 @@ function createMinutaEditor(element, opts) {
1939
2033
  setVariableValue,
1940
2034
  setVariableImage,
1941
2035
  variablesInDocument,
2036
+ refreshVaultImages(resolveUrl) {
2037
+ const ed = tiptap;
2038
+ if (!ed || ed.isDestroyed)
2039
+ return Promise.resolve(0);
2040
+ return refreshVaultImages(ed, resolveUrl, { meta: { [POC_ASSET_SWAP]: true } });
2041
+ },
1942
2042
  schedulePaginate,
1943
2043
  refreshChrome,
1944
2044
  totalPages: () => totalPages,
@@ -1958,4 +2058,4 @@ function createMinutaEditor(element, opts) {
1958
2058
  return api;
1959
2059
  }
1960
2060
 
1961
- export { A4_HEIGHT_PX, A4_WIDTH_PX, DEFAULT_MARGINS, DOC_FONTS, ICON_PATHS, PAGINATE_META, POC_ASSET_SWAP, PocDocument, PocFootnoteRef, PocHtmlIsland, PocInlineStyle, PocMinutaBlock, PocMissingImage, PocPaginationPlugin, PocStylePassthrough, PocStyledDiv, PocTemplateVariable, PocUnderline, PocVariableChip, breaksEqual, buildPocExtensions, computePocBreaks, createMinutaEditor, decodeVaultAttr, escapeHtml, escapeVaultAttr, extractTemplateVariables, iconSvg, pocPageKey, renumberPocFootnotes, resolveTemplateVariables, sanitizeFootnoteHtml, sanitizeRichHtml, variableChipHtml, vaultImageHtml };
2061
+ export { A4_HEIGHT_PX, A4_WIDTH_PX, DEFAULT_MARGINS, DOC_FONTS, ICON_PATHS, PAGINATE_META, POC_ASSET_SWAP, PocDocument, PocFootnoteRef, PocHtmlIsland, PocInlineStyle, PocMinutaBlock, PocMissingImage, PocPaginationPlugin, PocStylePassthrough, PocStyledDiv, PocTemplateVariable, PocUnderline, PocVariableChip, breaksEqual, buildPocExtensions, computePocBreaks, createMinutaEditor, decodeVaultAttr, dispatchVariableClick, escapeHtml, escapeVaultAttr, extractTemplateVariables, iconSvg, pocPageKey, refreshVaultImages, renumberPocFootnotes, resolveTemplateVariables, sanitizeFootnoteHtml, sanitizeRichHtml, variableChipHtml, vaultImageHtml };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meistrari/minuta-editor",
3
- "version": "1.1.0",
3
+ "version": "1.2.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",