@meistrari/minuta-react 1.2.0 → 1.3.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
@@ -35,6 +35,8 @@ um `Blob` de `.docx`. Buscar o documento, persistir a edição e baixar o arquiv
35
35
  | `chrome` | — | `{ header, footer, headerScope, footerScope, showPageNumber }` |
36
36
  | `variables` | — | catálogo `{ key, label, description?, group? }[]` → liga o modo template |
37
37
  | `uploadImage` | — | `(file) => Promise<{ src, vaultSrc } \| null>` |
38
+ | `imageFallback` | `'dataUri'` | `'none'` = upload falhou → não insere nada, só `onImageUploadError` |
39
+ | `onImageUploadError` | — | `(error, file) => void` |
38
40
  | `toolbar` | `true` | `false` = você monta a sua sobre `ref.current.tiptap` |
39
41
  | `blockClickWhileEditing` | `false` | dispara `onBlockClick` também em edição (padrão: só em leitura) |
40
42
  | `onChange` | — | `(html) => void`, a cada edição |
@@ -81,13 +83,21 @@ de conta no defesa.ai:
81
83
 
82
84
  ```
83
85
  GET /data/case/:id/html → { html, chrome, margins, source, editionId, variablesMarked, markedVariables }
84
- POST /data/case/:id/editions { name, html } → cria a edição → { id, updatedAt, … }
85
- PATCH /data/case/:id/editions/:id → salva → { updatedAt, updatedBy, … }
86
+ POST /data/case/:id/editions { name, html } → cria a edição → { id, updatedAt, contentHash, … }
87
+ PATCH /data/case/:id/editions/:id → salva → { updatedAt, updatedBy, contentHash, … }
86
88
  { html, baseUpdatedAt } 400 sem o token; 409 se outro salvou antes
89
+ GET /data/case/:id/editions → [{ id, updatedAt, updatedBy, contentHash, … }] sem html
87
90
  POST /data/case/:id/editions/:id/versions → marco no histórico (o PATCH NÃO cria versão)
91
+ { comment?, baseUpdatedAt? } com baseUpdatedAt: 409 se a revisão mudou; já versionada → 200 reused
88
92
  POST /data/case/:id/docx → .docx do estado atual (a edição, se existir)
89
93
  ```
90
94
 
95
+ `contentHash` é o sha256 do `html` exatamente como foi gravado (o servidor não
96
+ normaliza): num save que deu timeout, compare com o hash do que você enviou
97
+ pela listagem, sem baixar o documento. `baseUpdatedAt` no `POST …/versions`
98
+ amarra o checkpoint à revisão que você viu: a versão nunca sai com o seu nome
99
+ e o texto que outra pessoa salvou depois, e repetir a chamada não duplica.
100
+
91
101
  ```tsx
92
102
  // abrir
93
103
  const doc = await api.get(`/case/${caseId}/html`)
@@ -178,17 +188,28 @@ salva a edição. Decisão de UX do seu app; as duas funcionam.
178
188
  ### Imagens inseridas pelo advogado
179
189
 
180
190
  `uploadImage` devolve `{ src, vaultSrc }`: o editor grava a tag com o `src`
181
- (presignado, expira) e `data-vault-src` (a `vault://`, durável). Ao reabrir uma
182
- edição salva, renove os links antes de mostrar:
191
+ (presignado, expira) e `data-vault-src` (a `vault://`, durável). Persista o
192
+ `getHTML()` como está: o `POST /data/case/:id/docx` usa o `data-vault-src` e
193
+ resolve `vault://` sozinho. Ao reabrir uma edição salva, renove os links antes
194
+ de mostrar:
183
195
 
184
196
  ```tsx
185
197
  // resolver: POST /data/vault/download-url { vaultUrl } → { downloadUrl } (pelo seu backend)
186
198
  await editor.current!.refreshVaultImages(vaultUrl => api.post('/vault/download-url', { vaultUrl }).then(r => r.downloadUrl))
187
199
  ```
188
200
 
189
- Não entra no undo nem dispara `onChange`. O `exportDocx()` do browser baixa
190
- pelo `src`, então renove antes de exportar também; o `POST /data/case/:id/docx`
191
- resolve `vault://` sozinho.
201
+ Renova tanto `data-vault-src` quanto imagens salvas só com `src="vault://…"`
202
+ (que ganham o `data-vault-src`), mantendo a referência durável. Não entra no
203
+ undo nem dispara `onChange`: chame `getHTML()` depois se guarda uma cópia. O
204
+ `exportDocx()` do browser baixa pelo `src`, então renove antes de exportar.
205
+
206
+ Se o upload falhar (ou `uploadImage` devolver `null`), o padrão é embutir a
207
+ imagem em base64. Um app cujo documento canônico depende de `vault://` não
208
+ quer isso: `imageFallback="none"` não insere nada e `onImageUploadError` recebe
209
+ o erro e o arquivo para você mostrar e deixar tentar de novo.
210
+
211
+ `setVariableImage(id, src, vaultSrc)` grava o `data-vault-src` junto — use a
212
+ forma de três argumentos para a imagem sobreviver à expiração do `src`.
192
213
 
193
214
  ## Next.js (App Router)
194
215
 
package/dist/index.d.mts CHANGED
@@ -32,6 +32,9 @@ interface MinutaEditorProps {
32
32
  chrome?: Partial<MinutaChromeText>;
33
33
  variables?: MinutaVariableCatalog;
34
34
  uploadImage?: MinutaEditorOptions['uploadImage'];
35
+ /** `'dataUri'` (default) embute em base64 quando o upload falha; `'none'` não insere e avisa por `onImageUploadError` */
36
+ imageFallback?: MinutaEditorOptions['imageFallback'];
37
+ onImageUploadError?: MinutaEditorOptions['onImageUploadError'];
35
38
  /** barra de formatação do pacote (default: true). `false` = monte a sua sobre `ref.tiptap` */
36
39
  toolbar?: boolean;
37
40
  showBlockOrigin?: boolean;
package/dist/index.d.ts CHANGED
@@ -32,6 +32,9 @@ interface MinutaEditorProps {
32
32
  chrome?: Partial<MinutaChromeText>;
33
33
  variables?: MinutaVariableCatalog;
34
34
  uploadImage?: MinutaEditorOptions['uploadImage'];
35
+ /** `'dataUri'` (default) embute em base64 quando o upload falha; `'none'` não insere e avisa por `onImageUploadError` */
36
+ imageFallback?: MinutaEditorOptions['imageFallback'];
37
+ onImageUploadError?: MinutaEditorOptions['onImageUploadError'];
35
38
  /** barra de formatação do pacote (default: true). `false` = monte a sua sobre `ref.tiptap` */
36
39
  toolbar?: boolean;
37
40
  showBlockOrigin?: boolean;
package/dist/index.mjs CHANGED
@@ -21,6 +21,8 @@ const MinutaEditor = forwardRef((props, ref) => {
21
21
  variables: props.variables,
22
22
  // lido na hora do upload, não no mount: o host pode trocar a função
23
23
  uploadImage: (file) => callbacks.current.uploadImage ? callbacks.current.uploadImage(file) : Promise.resolve(null),
24
+ imageFallback: props.imageFallback,
25
+ onImageUploadError: (error, file) => callbacks.current.onImageUploadError?.(error, file),
24
26
  toolbar: props.toolbar,
25
27
  showBlockOrigin: props.showBlockOrigin,
26
28
  blockClickWhileEditing: props.blockClickWhileEditing,
@@ -64,6 +66,9 @@ const MinutaEditor = forwardRef((props, ref) => {
64
66
  useEffect(() => {
65
67
  instance.current?.setBlockClickWhileEditing(props.blockClickWhileEditing === true);
66
68
  }, [props.blockClickWhileEditing]);
69
+ useEffect(() => {
70
+ instance.current?.setImageFallback(props.imageFallback ?? "dataUri");
71
+ }, [props.imageFallback]);
67
72
  useImperativeHandle(ref, () => instance.current);
68
73
  return createElement("div", {
69
74
  ref: host,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meistrari/minuta-react",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "description": "Componente React do editor de minuta do defesa.ai — casca fina sobre @meistrari/minuta-editor",
5
5
  "repository": {
6
6
  "type": "git",