@meistrari/minuta-editor 1.0.2 → 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 +11 -0
- package/dist/index.d.mts +72 -9
- package/dist/index.d.ts +72 -9
- package/dist/index.mjs +191 -17
- package/package.json +1 -1
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
|
-
/**
|
|
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) */
|
|
@@ -232,6 +252,12 @@ interface MinutaEditorOptions {
|
|
|
232
252
|
toolbar?: boolean;
|
|
233
253
|
/** realce de proveniência de bloco no hover (só faz sentido no modo estruturado) */
|
|
234
254
|
showBlockOrigin?: boolean;
|
|
255
|
+
/**
|
|
256
|
+
* Dispara `onBlockClick` também com o editor editável. Desligado por
|
|
257
|
+
* padrão: na tela do defesa.ai o clique em edição é para posicionar o
|
|
258
|
+
* cursor, e abrir o bloco do checklist a cada clique atrapalharia.
|
|
259
|
+
*/
|
|
260
|
+
blockClickWhileEditing?: boolean;
|
|
235
261
|
onReady?: (tiptap: Editor) => void;
|
|
236
262
|
/** o documento mudou por edição (troca de imagem fria não conta) */
|
|
237
263
|
onChange?: () => void;
|
|
@@ -246,8 +272,10 @@ interface MinutaEditorOptions {
|
|
|
246
272
|
/** clique no cabeçalho/rodapé da folha (além do CustomEvent `poc:edit-chrome`) */
|
|
247
273
|
onEditChrome?: (part: 'header' | 'footer') => void;
|
|
248
274
|
/**
|
|
249
|
-
* Clique numa variável renderizada (`<span data-variable-id>`
|
|
250
|
-
*
|
|
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.
|
|
251
279
|
*/
|
|
252
280
|
onVariableClick?: (variable: MinutaVariableRef) => void;
|
|
253
281
|
onError?: (message: string) => void;
|
|
@@ -274,6 +302,8 @@ interface MinutaEditor {
|
|
|
274
302
|
setToolbar: (on: boolean) => void;
|
|
275
303
|
setMargins: (margins: MinutaMargins) => void;
|
|
276
304
|
setShowBlockOrigin: (on: boolean) => void;
|
|
305
|
+
/** liga/desliga `onBlockClick` em edição depois de criado (ver `blockClickWhileEditing`) */
|
|
306
|
+
setBlockClickWhileEditing: (on: boolean) => void;
|
|
277
307
|
/**
|
|
278
308
|
* Ponte formulário → documento: rola até a primeira ocorrência da variável
|
|
279
309
|
* e a destaca por um instante. `false` se ela não está no documento.
|
|
@@ -283,11 +313,26 @@ interface MinutaEditor {
|
|
|
283
313
|
* Troca o texto de TODAS as ocorrências da variável no documento — inclusive
|
|
284
314
|
* numa edição livre, porque a marca é um nó e sobrevive à lapidação. É o que
|
|
285
315
|
* deixa um formulário de variáveis continuar valendo depois que o advogado
|
|
286
|
-
* editou o texto em volta.
|
|
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.
|
|
287
319
|
*/
|
|
288
320
|
setVariableValue: (variableId: string, value: string) => number;
|
|
289
|
-
/**
|
|
321
|
+
/**
|
|
322
|
+
* Troca a imagem de uma variável de imagem: o `<img data-variable-id>` ganha
|
|
323
|
+
* o `src` novo, e um aviso de imagem faltando com esse id vira a imagem.
|
|
324
|
+
* Devolve quantas ocorrências mudaram.
|
|
325
|
+
*/
|
|
326
|
+
setVariableImage: (variableId: string, src: string) => number;
|
|
327
|
+
/** ids marcados no documento, na ordem, sem repetição: chips de texto, imagens e imagens faltando */
|
|
290
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>;
|
|
291
336
|
/** repagina (o host chama após mudar algo que afeta altura fora do editor) */
|
|
292
337
|
schedulePaginate: () => void;
|
|
293
338
|
/** reconstrói cabeçalhos/rodapés com o chrome atual, sem repaginar */
|
|
@@ -387,6 +432,24 @@ declare function shouldMoveBreakToHeading(params: {
|
|
|
387
432
|
capacity: number;
|
|
388
433
|
}): boolean;
|
|
389
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
|
+
|
|
390
453
|
/**
|
|
391
454
|
* Sanitização do HTML rico de cabeçalho/rodapé (chrome) — conteúdo criado em
|
|
392
455
|
* contenteditable e PERSISTIDO (template.headers, case_edition.chrome). Sem
|
|
@@ -409,5 +472,5 @@ declare function escapeVaultAttr(value: string): string;
|
|
|
409
472
|
declare function decodeVaultAttr(value: string): string;
|
|
410
473
|
declare function vaultImageHtml(src: string, vaultUrl: string): string;
|
|
411
474
|
|
|
412
|
-
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 };
|
|
413
|
-
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
|
-
/**
|
|
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) */
|
|
@@ -232,6 +252,12 @@ interface MinutaEditorOptions {
|
|
|
232
252
|
toolbar?: boolean;
|
|
233
253
|
/** realce de proveniência de bloco no hover (só faz sentido no modo estruturado) */
|
|
234
254
|
showBlockOrigin?: boolean;
|
|
255
|
+
/**
|
|
256
|
+
* Dispara `onBlockClick` também com o editor editável. Desligado por
|
|
257
|
+
* padrão: na tela do defesa.ai o clique em edição é para posicionar o
|
|
258
|
+
* cursor, e abrir o bloco do checklist a cada clique atrapalharia.
|
|
259
|
+
*/
|
|
260
|
+
blockClickWhileEditing?: boolean;
|
|
235
261
|
onReady?: (tiptap: Editor) => void;
|
|
236
262
|
/** o documento mudou por edição (troca de imagem fria não conta) */
|
|
237
263
|
onChange?: () => void;
|
|
@@ -246,8 +272,10 @@ interface MinutaEditorOptions {
|
|
|
246
272
|
/** clique no cabeçalho/rodapé da folha (além do CustomEvent `poc:edit-chrome`) */
|
|
247
273
|
onEditChrome?: (part: 'header' | 'footer') => void;
|
|
248
274
|
/**
|
|
249
|
-
* Clique numa variável renderizada (`<span data-variable-id>`
|
|
250
|
-
*
|
|
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.
|
|
251
279
|
*/
|
|
252
280
|
onVariableClick?: (variable: MinutaVariableRef) => void;
|
|
253
281
|
onError?: (message: string) => void;
|
|
@@ -274,6 +302,8 @@ interface MinutaEditor {
|
|
|
274
302
|
setToolbar: (on: boolean) => void;
|
|
275
303
|
setMargins: (margins: MinutaMargins) => void;
|
|
276
304
|
setShowBlockOrigin: (on: boolean) => void;
|
|
305
|
+
/** liga/desliga `onBlockClick` em edição depois de criado (ver `blockClickWhileEditing`) */
|
|
306
|
+
setBlockClickWhileEditing: (on: boolean) => void;
|
|
277
307
|
/**
|
|
278
308
|
* Ponte formulário → documento: rola até a primeira ocorrência da variável
|
|
279
309
|
* e a destaca por um instante. `false` se ela não está no documento.
|
|
@@ -283,11 +313,26 @@ interface MinutaEditor {
|
|
|
283
313
|
* Troca o texto de TODAS as ocorrências da variável no documento — inclusive
|
|
284
314
|
* numa edição livre, porque a marca é um nó e sobrevive à lapidação. É o que
|
|
285
315
|
* deixa um formulário de variáveis continuar valendo depois que o advogado
|
|
286
|
-
* editou o texto em volta.
|
|
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.
|
|
287
319
|
*/
|
|
288
320
|
setVariableValue: (variableId: string, value: string) => number;
|
|
289
|
-
/**
|
|
321
|
+
/**
|
|
322
|
+
* Troca a imagem de uma variável de imagem: o `<img data-variable-id>` ganha
|
|
323
|
+
* o `src` novo, e um aviso de imagem faltando com esse id vira a imagem.
|
|
324
|
+
* Devolve quantas ocorrências mudaram.
|
|
325
|
+
*/
|
|
326
|
+
setVariableImage: (variableId: string, src: string) => number;
|
|
327
|
+
/** ids marcados no documento, na ordem, sem repetição: chips de texto, imagens e imagens faltando */
|
|
290
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>;
|
|
291
336
|
/** repagina (o host chama após mudar algo que afeta altura fora do editor) */
|
|
292
337
|
schedulePaginate: () => void;
|
|
293
338
|
/** reconstrói cabeçalhos/rodapés com o chrome atual, sem repaginar */
|
|
@@ -387,6 +432,24 @@ declare function shouldMoveBreakToHeading(params: {
|
|
|
387
432
|
capacity: number;
|
|
388
433
|
}): boolean;
|
|
389
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
|
+
|
|
390
453
|
/**
|
|
391
454
|
* Sanitização do HTML rico de cabeçalho/rodapé (chrome) — conteúdo criado em
|
|
392
455
|
* contenteditable e PERSISTIDO (template.headers, case_edition.chrome). Sem
|
|
@@ -409,5 +472,5 @@ declare function escapeVaultAttr(value: string): string;
|
|
|
409
472
|
declare function decodeVaultAttr(value: string): string;
|
|
410
473
|
declare function vaultImageHtml(src: string, vaultUrl: string): string;
|
|
411
474
|
|
|
412
|
-
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 };
|
|
413
|
-
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
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
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, "&").replace(/"/g, """);
|
|
641
671
|
}
|
|
642
672
|
function escapeVaultAttr(value) {
|
|
@@ -646,7 +676,81 @@ function decodeVaultAttr(value) {
|
|
|
646
676
|
return value.replace(/"/g, '"').replace(/>/g, ">").replace(/</g, "<").replace(/&/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, "&").replace(/"/g, """);
|
|
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 = []) {
|
|
@@ -1426,8 +1530,9 @@ function createMinutaEditor(element, opts) {
|
|
|
1426
1530
|
opts.onEditChrome?.(part);
|
|
1427
1531
|
window.dispatchEvent(new CustomEvent("poc:edit-chrome", { detail: { part, editor: tiptap } }));
|
|
1428
1532
|
}
|
|
1533
|
+
let blockClickWhileEditing = opts.blockClickWhileEditing === true;
|
|
1429
1534
|
function onSheetClick(event) {
|
|
1430
|
-
if (editable)
|
|
1535
|
+
if (editable && !blockClickWhileEditing)
|
|
1431
1536
|
return;
|
|
1432
1537
|
const target = event.target;
|
|
1433
1538
|
if (target.closest(".poc-varchip") || target.closest(".poc-page-headerarea") || target.closest(".poc-page-footerarea") || target.closest(".poc-first-header") || target.closest(".poc-last-footer")) {
|
|
@@ -1649,12 +1754,22 @@ function createMinutaEditor(element, opts) {
|
|
|
1649
1754
|
opts.onVariableClick?.({
|
|
1650
1755
|
variableId: detail.variableId,
|
|
1651
1756
|
parentBlockId: detail.parentBlockId ?? null,
|
|
1652
|
-
text: String(detail.text ?? "")
|
|
1757
|
+
text: String(detail.text ?? ""),
|
|
1758
|
+
kind: detail.kind === "image" || detail.kind === "missingImage" ? detail.kind : "text"
|
|
1653
1759
|
});
|
|
1654
1760
|
}
|
|
1761
|
+
function variableTargetElement(variableId) {
|
|
1762
|
+
const id = CSS.escape(variableId);
|
|
1763
|
+
const chip = sheet.querySelector(`.poc-varchip [data-variable-id="${id}"], .poc-varchip[data-variable-id="${id}"]`);
|
|
1764
|
+
if (chip)
|
|
1765
|
+
return chip.closest(".poc-varchip") ?? chip;
|
|
1766
|
+
const img = sheet.querySelector(`img[data-variable-id="${id}"]`);
|
|
1767
|
+
if (img)
|
|
1768
|
+
return img;
|
|
1769
|
+
return sheet.querySelector(`.poc-missing-image [data-variable-id="${id}"]`);
|
|
1770
|
+
}
|
|
1655
1771
|
function scrollToVariable(variableId) {
|
|
1656
|
-
const
|
|
1657
|
-
const chip = el?.closest(".poc-varchip") ?? el;
|
|
1772
|
+
const chip = variableTargetElement(variableId);
|
|
1658
1773
|
if (!chip)
|
|
1659
1774
|
return false;
|
|
1660
1775
|
chip.scrollIntoView({ block: "center", behavior: "smooth" });
|
|
@@ -1682,17 +1797,66 @@ function createMinutaEditor(element, opts) {
|
|
|
1682
1797
|
ed.view.dispatch(tr);
|
|
1683
1798
|
return count;
|
|
1684
1799
|
}
|
|
1800
|
+
function setVariableImage(variableId, src) {
|
|
1801
|
+
const ed = tiptap;
|
|
1802
|
+
if (!ed || ed.isDestroyed)
|
|
1803
|
+
return 0;
|
|
1804
|
+
const islandType = ed.state.schema.nodes.htmlIsland;
|
|
1805
|
+
const tr = ed.state.tr;
|
|
1806
|
+
let count = 0;
|
|
1807
|
+
const hits = [];
|
|
1808
|
+
ed.state.doc.descendants((node, pos) => {
|
|
1809
|
+
if (node.type.name === "missingImage" && node.attrs.variableId === variableId)
|
|
1810
|
+
hits.push({ pos, node });
|
|
1811
|
+
else if (node.type.name === "htmlIsland" && String(node.attrs.html ?? "").includes(`data-variable-id="${variableId}"`))
|
|
1812
|
+
hits.push({ pos, node });
|
|
1813
|
+
});
|
|
1814
|
+
for (const { pos, node } of hits.reverse()) {
|
|
1815
|
+
if (node.type.name === "missingImage") {
|
|
1816
|
+
if (!islandType)
|
|
1817
|
+
continue;
|
|
1818
|
+
const html = `<img src="${escapeAttr(src)}" alt="${escapeAttr(variableId)}" data-variable-id="${escapeAttr(variableId)}" data-parent-block-id="${escapeAttr(String(node.attrs.parentBlockId ?? ""))}" style="max-width: 100%; height: auto;" />`;
|
|
1819
|
+
tr.replaceWith(pos, pos + node.nodeSize, islandType.create({ html }));
|
|
1820
|
+
count++;
|
|
1821
|
+
continue;
|
|
1822
|
+
}
|
|
1823
|
+
const tpl = document.createElement("template");
|
|
1824
|
+
tpl.innerHTML = String(node.attrs.html ?? "");
|
|
1825
|
+
const imgs = tpl.content.querySelectorAll(`img[data-variable-id="${CSS.escape(variableId)}"]`);
|
|
1826
|
+
if (!imgs.length)
|
|
1827
|
+
continue;
|
|
1828
|
+
for (const img of imgs)
|
|
1829
|
+
img.setAttribute("src", src);
|
|
1830
|
+
tr.setNodeMarkup(pos, void 0, { ...node.attrs, html: tpl.innerHTML });
|
|
1831
|
+
count += imgs.length;
|
|
1832
|
+
}
|
|
1833
|
+
if (count)
|
|
1834
|
+
ed.view.dispatch(tr);
|
|
1835
|
+
return count;
|
|
1836
|
+
}
|
|
1685
1837
|
function variablesInDocument() {
|
|
1686
1838
|
const ed = tiptap;
|
|
1687
1839
|
const ids = [];
|
|
1688
1840
|
if (!ed || ed.isDestroyed)
|
|
1689
1841
|
return ids;
|
|
1842
|
+
const add = (id) => {
|
|
1843
|
+
if (typeof id === "string" && id && !ids.includes(id))
|
|
1844
|
+
ids.push(id);
|
|
1845
|
+
};
|
|
1690
1846
|
ed.state.doc.descendants((node) => {
|
|
1691
|
-
if (node.type.name === "variableChip"
|
|
1692
|
-
|
|
1847
|
+
if (node.type.name === "variableChip" || node.type.name === "missingImage") {
|
|
1848
|
+
add(node.attrs.variableId);
|
|
1849
|
+
} else if (node.type.name === "htmlIsland") {
|
|
1850
|
+
for (const m of String(node.attrs.html ?? "").matchAll(/data-variable-id="([^"]+)"/g)) {
|
|
1851
|
+
add(m[1]);
|
|
1852
|
+
}
|
|
1853
|
+
}
|
|
1693
1854
|
});
|
|
1694
1855
|
return ids;
|
|
1695
1856
|
}
|
|
1857
|
+
function escapeAttr(value) {
|
|
1858
|
+
return value.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">");
|
|
1859
|
+
}
|
|
1696
1860
|
function buildBubbleMenu() {
|
|
1697
1861
|
const TEXT_COLORS = [
|
|
1698
1862
|
{ label: "Preto", value: "#1C242D" },
|
|
@@ -1862,9 +2026,19 @@ function createMinutaEditor(element, opts) {
|
|
|
1862
2026
|
setShowBlockOrigin(on) {
|
|
1863
2027
|
toggleClass(root, "poc-no-block-origin", !on);
|
|
1864
2028
|
},
|
|
2029
|
+
setBlockClickWhileEditing(on) {
|
|
2030
|
+
blockClickWhileEditing = on;
|
|
2031
|
+
},
|
|
1865
2032
|
scrollToVariable,
|
|
1866
2033
|
setVariableValue,
|
|
2034
|
+
setVariableImage,
|
|
1867
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
|
+
},
|
|
1868
2042
|
schedulePaginate,
|
|
1869
2043
|
refreshChrome,
|
|
1870
2044
|
totalPages: () => totalPages,
|
|
@@ -1884,4 +2058,4 @@ function createMinutaEditor(element, opts) {
|
|
|
1884
2058
|
return api;
|
|
1885
2059
|
}
|
|
1886
2060
|
|
|
1887
|
-
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.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",
|