@meistrari/minuta-nuxt 1.0.0 → 1.1.1
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/dist/module.json +1 -1
- package/dist/module.mjs +3 -0
- package/dist/runtime/components/MinutaAnnotationsGutter.vue +9 -4
- package/dist/runtime/components/MinutaDocumentViewer.vue +106 -204
- package/dist/runtime/components/MinutaDocxEditor.vue +5 -13
- package/dist/runtime/components/MinutaEmbed.vue +123 -22
- package/dist/runtime/composables/docxExport.js +5 -1
- package/dist/runtime/composables/paginationPlugin.d.ts +13 -0
- package/dist/runtime/composables/paginationPlugin.js +88 -4
- package/package.json +99 -96
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -12,6 +12,9 @@ const module = defineNuxtModule({
|
|
|
12
12
|
},
|
|
13
13
|
setup(options, nuxt) {
|
|
14
14
|
const resolver = createResolver(import.meta.url);
|
|
15
|
+
nuxt.options.build.transpile = nuxt.options.build.transpile || [];
|
|
16
|
+
if (!nuxt.options.build.transpile.includes("@meistrari/tela-build"))
|
|
17
|
+
nuxt.options.build.transpile.push("@meistrari/tela-build");
|
|
15
18
|
nuxt.options.css = nuxt.options.css || [];
|
|
16
19
|
if (!nuxt.options.css.includes("@fontsource-variable/inter"))
|
|
17
20
|
nuxt.options.css.push("@fontsource-variable/inter");
|
|
@@ -15,6 +15,11 @@ const props = defineProps<{
|
|
|
15
15
|
analysis: MinutaAnalysis
|
|
16
16
|
/** container com scroll do documento (para o scroll suave ao clicar) */
|
|
17
17
|
scrollContainer: HTMLElement | null
|
|
18
|
+
/**
|
|
19
|
+
* Modo lista: em tela estreita não há margem onde ancorar os cards, então
|
|
20
|
+
* eles viram uma coluna em fluxo normal (dentro de um painel lateral).
|
|
21
|
+
*/
|
|
22
|
+
compact?: boolean
|
|
18
23
|
}>()
|
|
19
24
|
|
|
20
25
|
const emit = defineEmits<{
|
|
@@ -306,7 +311,7 @@ watch(() => props.editor, (editor, old) => {
|
|
|
306
311
|
}, { immediate: true })
|
|
307
312
|
|
|
308
313
|
onMounted(() => {
|
|
309
|
-
const sheet = rootRef.value?.parentElement
|
|
314
|
+
const sheet = props.compact ? null : rootRef.value?.parentElement
|
|
310
315
|
if (sheet) {
|
|
311
316
|
// como o Google Docs: com os comentários abertos, a folha desloca pra
|
|
312
317
|
// esquerda pra dar lugar à coluna (a classe vive enquanto o gutter vive)
|
|
@@ -331,7 +336,7 @@ onUnmounted(() => {
|
|
|
331
336
|
</script>
|
|
332
337
|
|
|
333
338
|
<template>
|
|
334
|
-
<div ref="rootRef" class="minuta-gutter">
|
|
339
|
+
<div ref="rootRef" class="minuta-gutter" :class="{ 'minuta-gutter--compact': props.compact }">
|
|
335
340
|
<!-- análise sem âncora: sem `trecho` não há onde grudar o card; diz isso
|
|
336
341
|
em vez de empilhar tudo no topo com clique morto -->
|
|
337
342
|
<div v-if="items.length && orphanCount === items.length" ref="noticeRef" class="minuta-gutter-notice">
|
|
@@ -347,7 +352,7 @@ onUnmounted(() => {
|
|
|
347
352
|
'minuta-gutter-card--active': activeId === item.id,
|
|
348
353
|
'minuta-gutter-card--orphan': !item.range,
|
|
349
354
|
}"
|
|
350
|
-
:style="{ transform: `translateY(${item.finalY}px)` }"
|
|
355
|
+
:style="props.compact ? undefined : { transform: `translateY(${item.finalY}px)` }"
|
|
351
356
|
:title="item.range ? undefined : 'Sem trecho de referência: não dá para localizar no texto'"
|
|
352
357
|
@click="onCardClick(item)"
|
|
353
358
|
>
|
|
@@ -384,7 +389,7 @@ onUnmounted(() => {
|
|
|
384
389
|
</template>
|
|
385
390
|
|
|
386
391
|
<style scoped>
|
|
387
|
-
.minuta-gutter{height:100%;left:calc(100% + 16px);pointer-events:none;position:absolute;top:0;width:264px;z-index:20}.minuta-gutter-card{background:#fff;border:.5px solid #dee3e7;border-radius:10px;cursor:pointer;left:0;padding:8px 12px;pointer-events:auto;position:absolute;right:0;transition:border-color .15s ease,background .15s ease,transform .2s ease}.minuta-gutter-card:hover{border-color:#b6bcc6}.minuta-gutter-card--active{background:#fafbfc;border-color:#565d69}.minuta-gutter-card--orphan{border-style:dashed;cursor:default;opacity:.7}.minuta-gutter-notice{background:#fafbfc;border:.5px solid #e5e7eb;border-radius:8px;color:#6b7280;font-size:11.5px;line-height:1.45;margin-bottom:8px;padding:8px 10px;position:relative}.minuta-gutter-card__source{align-items:baseline;border-top:.5px solid #eceef1;display:flex;gap:8px;justify-content:space-between;margin-top:5px;padding-top:5px}.minuta-gutter-card__source-text{color:#9ca3af;font-size:10px;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.minuta-gutter-card__source-btn{background:none;border:none;color:#565d69;cursor:pointer;flex-shrink:0;font-size:10px;font-weight:500;padding:0}.minuta-gutter-card__source-btn:hover{color:#111827;text-decoration:underline}.minuta-gutter-card__badge{align-items:center;border-radius:999px;display:inline-flex;flex-shrink:0;font-size:9.5px;font-weight:650;letter-spacing:.05em;line-height:1.5;padding:1px 7px;text-transform:uppercase}.minuta-gutter-card__kind{align-items:center;display:flex;gap:6px;margin-bottom:4px;min-width:0}.minuta-gutter-card__onde{color:#b6bcc6;font-size:10px;font-weight:400;letter-spacing:normal;min-width:0;overflow:hidden;text-overflow:ellipsis;text-transform:none;white-space:nowrap}.minuta-gutter-card__title{color:#111827;font-size:12px;font-weight:590;line-height:1.35;margin:0}.minuta-gutter-card__body{color:#6b7280;display:-webkit-box;font-size:11px;-webkit-line-clamp:3;line-height:1.45;margin:3px 0 0;-webkit-box-orient:vertical;overflow:hidden}
|
|
392
|
+
.minuta-gutter{height:100%;left:calc(100% + 16px);pointer-events:none;position:absolute;top:0;width:264px;z-index:20}.minuta-gutter--compact{display:flex;flex-direction:column;gap:8px;position:static;width:100%}.minuta-gutter--compact .minuta-gutter-card{position:static;transform:none!important}.minuta-gutter-card{background:#fff;border:.5px solid #dee3e7;border-radius:10px;cursor:pointer;left:0;padding:8px 12px;pointer-events:auto;position:absolute;right:0;transition:border-color .15s ease,background .15s ease,transform .2s ease}.minuta-gutter-card:hover{border-color:#b6bcc6}.minuta-gutter-card--active{background:#fafbfc;border-color:#565d69}.minuta-gutter-card--orphan{border-style:dashed;cursor:default;opacity:.7}.minuta-gutter-notice{background:#fafbfc;border:.5px solid #e5e7eb;border-radius:8px;color:#6b7280;font-size:11.5px;line-height:1.45;margin-bottom:8px;padding:8px 10px;position:relative}.minuta-gutter-card__source{align-items:baseline;border-top:.5px solid #eceef1;display:flex;gap:8px;justify-content:space-between;margin-top:5px;padding-top:5px}.minuta-gutter-card__source-text{color:#9ca3af;font-size:10px;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.minuta-gutter-card__source-btn{background:none;border:none;color:#565d69;cursor:pointer;flex-shrink:0;font-size:10px;font-weight:500;padding:0}.minuta-gutter-card__source-btn:hover{color:#111827;text-decoration:underline}.minuta-gutter-card__badge{align-items:center;border-radius:999px;display:inline-flex;flex-shrink:0;font-size:9.5px;font-weight:650;letter-spacing:.05em;line-height:1.5;padding:1px 7px;text-transform:uppercase}.minuta-gutter-card__kind{align-items:center;display:flex;gap:6px;margin-bottom:4px;min-width:0}.minuta-gutter-card__onde{color:#b6bcc6;font-size:10px;font-weight:400;letter-spacing:normal;min-width:0;overflow:hidden;text-overflow:ellipsis;text-transform:none;white-space:nowrap}.minuta-gutter-card__title{color:#111827;font-size:12px;font-weight:590;line-height:1.35;margin:0}.minuta-gutter-card__body{color:#6b7280;display:-webkit-box;font-size:11px;-webkit-line-clamp:3;line-height:1.45;margin:3px 0 0;-webkit-box-orient:vertical;overflow:hidden}
|
|
388
393
|
</style>
|
|
389
394
|
|
|
390
395
|
<style>
|
|
@@ -1,203 +1,109 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import { computed,
|
|
2
|
+
import { computed, ref, watch } from 'vue'
|
|
3
|
+
// Import explícito, e não auto-import do Nuxt: o auto-import é uma transformação
|
|
4
|
+
// de build que roda nos arquivos DO APP. Este componente é publicado em
|
|
5
|
+
// node_modules e não passa por ela — em produção `usePdfLoader` chegava como
|
|
6
|
+
// ReferenceError. Vale para composable; componente resolve em runtime pelo
|
|
7
|
+
// registro global, mas importar também os dois tira a dúvida.
|
|
8
|
+
import TelaButton from '@meistrari/tela-build/components/tela/button/button.vue'
|
|
9
|
+
import TelaPreview from '@meistrari/tela-build/components/tela/preview/preview.vue'
|
|
10
|
+
import { usePdfLoader } from '@meistrari/tela-build/composables/use-pdf-loader'
|
|
3
11
|
import type { MinutaFile } from '../composables/useMinutaApi'
|
|
4
12
|
import { useMinutaApi } from '../composables/useMinutaApi'
|
|
5
13
|
|
|
6
14
|
/**
|
|
7
|
-
*
|
|
8
|
-
*
|
|
15
|
+
* Visualização dos documentos do caso, aberta pelos cards de fato extraído da
|
|
16
|
+
* margem ou pelo botão Documentos.
|
|
9
17
|
*
|
|
10
|
-
* O
|
|
11
|
-
*
|
|
18
|
+
* O corpo é o `TelaPreview` do Tela Build — o mesmo visualizador que o resto
|
|
19
|
+
* do produto usa. Ele resolve o que a implementação anterior (um `<iframe>`
|
|
20
|
+
* apontando para uma `blob:` URL) não resolvia:
|
|
12
21
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* A `blob:` URL resolve (1): o disposition da origem não a acompanha, e o tipo
|
|
22
|
-
* do blob é o que dissermos que é. A classificação por tipo resolve (2): o que
|
|
23
|
-
* o browser não abre ganha um cartão com botão de baixar, em vez de cair no
|
|
24
|
-
* mesmo `else` do PDF e baixar sozinho sem nunca ter chance de aparecer.
|
|
22
|
+
* - **destaque do trecho.** O card de fato manda `highlight`, e antes esse
|
|
23
|
+
* campo era recebido e descartado: o visualizador nativo do Chrome não tem
|
|
24
|
+
* API de destaque. O TelaPreview aceita `highlightText`/`highlightPage`.
|
|
25
|
+
* - zoom, navegação por página, modo arrasto e tela cheia, que o `<iframe>`
|
|
26
|
+
* delegava à barra cinza do browser.
|
|
27
|
+
* - `vault://` resolvido por `resolveFileUrl`, e o cartão de download para
|
|
28
|
+
* formato que o navegador não abre.
|
|
25
29
|
*/
|
|
26
30
|
|
|
27
31
|
const props = defineProps<{
|
|
28
32
|
files: MinutaFile[]
|
|
29
33
|
initialUrl?: string | null
|
|
30
|
-
/** página
|
|
34
|
+
/** página para onde rolar e destacar */
|
|
31
35
|
initialPage?: number | null
|
|
36
|
+
/** trecho a destacar na página (vem do card de fato extraído) */
|
|
37
|
+
highlight?: string | null
|
|
32
38
|
}>()
|
|
33
39
|
|
|
34
40
|
const emit = defineEmits<{ close: [], error: [message: string] }>()
|
|
35
41
|
|
|
36
42
|
const { getDownloadUrl } = useMinutaApi()
|
|
37
|
-
|
|
38
|
-
type ViewerKind = 'image' | 'text' | 'pdf' | 'binary'
|
|
39
|
-
type ViewerContent = { kind: ViewerKind, src: string, text?: string, sizeLabel?: string }
|
|
43
|
+
const { pdfLoader } = usePdfLoader()
|
|
40
44
|
|
|
41
45
|
const selected = ref(props.initialUrl || props.files[0]?.url || '')
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
const currentName = computed(() => props.files.find(f => f.url === selected.value)?.name ?? '')
|
|
47
|
-
|
|
48
|
-
const IMAGE_RE = /\.(?:png|jpe?g|gif|webp|avif|bmp|svg)$/i
|
|
49
|
-
const TEXT_RE = /\.(?:txt|md|json|csv|tsv|log|xml|ya?ml)$/i
|
|
50
|
-
const PDF_RE = /\.pdf$/i
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* O nome do arquivo manda; o `Content-Type` do blob é a reserva.
|
|
54
|
-
*
|
|
55
|
-
* Referência do Vault costuma trazer o nome original (extensão junto), mas
|
|
56
|
-
* upload sem extensão existe — e aí adivinhar pelo tipo é melhor do que mandar
|
|
57
|
-
* tudo para o cartão de download.
|
|
58
|
-
*/
|
|
59
|
-
function classify(name: string, mimeType: string): ViewerKind {
|
|
60
|
-
if (IMAGE_RE.test(name))
|
|
61
|
-
return 'image'
|
|
62
|
-
if (PDF_RE.test(name))
|
|
63
|
-
return 'pdf'
|
|
64
|
-
if (TEXT_RE.test(name))
|
|
65
|
-
return 'text'
|
|
66
|
-
if (mimeType.startsWith('image/'))
|
|
67
|
-
return 'image'
|
|
68
|
-
if (mimeType === 'application/pdf')
|
|
69
|
-
return 'pdf'
|
|
70
|
-
if (mimeType.startsWith('text/') || mimeType === 'application/json')
|
|
71
|
-
return 'text'
|
|
72
|
-
return 'binary'
|
|
73
|
-
}
|
|
46
|
+
watch(() => props.initialUrl, (url) => {
|
|
47
|
+
if (url)
|
|
48
|
+
selected.value = url
|
|
49
|
+
})
|
|
74
50
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
51
|
+
const EXT_MIME: Record<string, string> = {
|
|
52
|
+
pdf: 'application/pdf',
|
|
53
|
+
png: 'image/png',
|
|
54
|
+
jpg: 'image/jpeg',
|
|
55
|
+
jpeg: 'image/jpeg',
|
|
56
|
+
gif: 'image/gif',
|
|
57
|
+
webp: 'image/webp',
|
|
58
|
+
svg: 'image/svg+xml',
|
|
59
|
+
txt: 'text/plain',
|
|
60
|
+
md: 'text/markdown',
|
|
61
|
+
csv: 'text/csv',
|
|
62
|
+
json: 'application/json',
|
|
81
63
|
}
|
|
82
64
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
if (objectUrl) {
|
|
87
|
-
URL.revokeObjectURL(objectUrl)
|
|
88
|
-
objectUrl = null
|
|
89
|
-
}
|
|
65
|
+
function mimeOf(name: string): string {
|
|
66
|
+
const ext = name.split('.').pop()?.toLowerCase() ?? ''
|
|
67
|
+
return EXT_MIME[ext] ?? 'application/octet-stream'
|
|
90
68
|
}
|
|
91
69
|
|
|
92
|
-
|
|
70
|
+
/** o seletor do TelaPreview agrupa por "variável"; aqui só há uma categoria */
|
|
71
|
+
const fileOptions = computed(() => props.files.map(file => ({
|
|
72
|
+
variable: 'Documentos do caso',
|
|
73
|
+
file: file.name,
|
|
74
|
+
value: file.url,
|
|
75
|
+
mimeType: mimeOf(file.name),
|
|
76
|
+
})))
|
|
93
77
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
*
|
|
97
|
-
* Vale depois de CADA await, não só do primeiro: `blob.text()` de um arquivo
|
|
98
|
-
* grande termina depois de o usuário já ter trocado de documento, e escreveria
|
|
99
|
-
* o conteúdo antigo sob o nome novo.
|
|
100
|
-
*
|
|
101
|
-
* O desmonte também invalida (incrementa o token), e não só revoga a `blob:`
|
|
102
|
-
* atual: sem isso, uma carga em voo criaria uma `blob:` DEPOIS da limpeza —
|
|
103
|
-
* URL que ninguém mais revoga, segurando os bytes do arquivo pelo resto da vida
|
|
104
|
-
* da página.
|
|
105
|
-
*/
|
|
106
|
-
function isStale(token: number): boolean {
|
|
107
|
-
return token !== loadToken
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
async function load() {
|
|
111
|
-
const file = props.files.find(f => f.url === selected.value)
|
|
78
|
+
const currentFile = computed(() => {
|
|
79
|
+
const file = props.files.find(f => f.url === selected.value) ?? props.files[0]
|
|
112
80
|
if (!file)
|
|
113
|
-
return
|
|
81
|
+
return null
|
|
82
|
+
return {
|
|
83
|
+
id: file.url,
|
|
84
|
+
fileName: file.name,
|
|
85
|
+
fileUrl: file.url,
|
|
86
|
+
fileType: mimeOf(file.name),
|
|
87
|
+
}
|
|
88
|
+
})
|
|
114
89
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
90
|
+
/** o destaque só vale para o documento que o card apontou */
|
|
91
|
+
const highlightForCurrent = computed(() => (
|
|
92
|
+
selected.value === props.initialUrl ? props.highlight ?? null : null
|
|
93
|
+
))
|
|
94
|
+
const pageForCurrent = computed(() => (
|
|
95
|
+
selected.value === props.initialUrl ? props.initialPage ?? null : null
|
|
96
|
+
))
|
|
119
97
|
|
|
98
|
+
async function resolveFileUrl(url: string): Promise<string> {
|
|
120
99
|
try {
|
|
121
|
-
|
|
122
|
-
const response = await fetch(downloadUrl)
|
|
123
|
-
if (!response.ok)
|
|
124
|
-
throw new Error(`HTTP ${response.status}`)
|
|
125
|
-
const blob = await response.blob()
|
|
126
|
-
if (isStale(token))
|
|
127
|
-
return
|
|
128
|
-
|
|
129
|
-
const kind = classify(file.name, blob.type || '')
|
|
130
|
-
|
|
131
|
-
if (kind === 'text') {
|
|
132
|
-
const text = await blob.text()
|
|
133
|
-
if (isStale(token))
|
|
134
|
-
return
|
|
135
|
-
releaseObjectUrl()
|
|
136
|
-
content.value = { kind, src: '', text }
|
|
137
|
-
return
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
// daqui até o fim é síncrono: a checagem acima ainda vale, e nada é
|
|
141
|
-
// criado depois de a carga ter sido invalidada
|
|
142
|
-
|
|
143
|
-
// o tipo do blob manda no que o browser faz com ele: forçar
|
|
144
|
-
// application/pdf faz o visualizador nativo abrir mesmo quando o Vault
|
|
145
|
-
// devolveu octet-stream
|
|
146
|
-
const typed = kind === 'pdf' && blob.type !== 'application/pdf'
|
|
147
|
-
? new Blob([blob], { type: 'application/pdf' })
|
|
148
|
-
: blob
|
|
149
|
-
|
|
150
|
-
releaseObjectUrl()
|
|
151
|
-
objectUrl = URL.createObjectURL(typed)
|
|
152
|
-
const page = kind === 'pdf' && props.initialPage && file.url === props.initialUrl
|
|
153
|
-
? `#page=${props.initialPage}`
|
|
154
|
-
: ''
|
|
155
|
-
content.value = {
|
|
156
|
-
kind,
|
|
157
|
-
src: objectUrl + page,
|
|
158
|
-
sizeLabel: formatSize(blob.size),
|
|
159
|
-
}
|
|
100
|
+
return await getDownloadUrl(url)
|
|
160
101
|
}
|
|
161
|
-
catch {
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
loadError.value = 'Não consegui carregar o documento.'
|
|
165
|
-
emit('error', 'Não consegui carregar o documento')
|
|
102
|
+
catch (error: any) {
|
|
103
|
+
emit('error', error?.message || 'Não foi possível abrir o documento')
|
|
104
|
+
return ''
|
|
166
105
|
}
|
|
167
|
-
finally {
|
|
168
|
-
if (!isStale(token))
|
|
169
|
-
loading.value = false
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
/**
|
|
174
|
-
* Baixar de propósito. O `download` de uma `blob:` URL não depende do
|
|
175
|
-
* disposition da origem, então funciona igual para PDF e para docx.
|
|
176
|
-
*/
|
|
177
|
-
function downloadCurrent() {
|
|
178
|
-
const current = content.value
|
|
179
|
-
if (!current)
|
|
180
|
-
return
|
|
181
|
-
const link = document.createElement('a')
|
|
182
|
-
link.download = currentName.value || 'documento'
|
|
183
|
-
if (current.kind === 'text') {
|
|
184
|
-
const href = URL.createObjectURL(new Blob([current.text ?? ''], { type: 'text/plain' }))
|
|
185
|
-
link.href = href
|
|
186
|
-
link.click()
|
|
187
|
-
URL.revokeObjectURL(href)
|
|
188
|
-
return
|
|
189
|
-
}
|
|
190
|
-
// sem o fragmento `#page=`: ele é do visualizador, não do arquivo
|
|
191
|
-
link.href = current.src.split('#')[0]!
|
|
192
|
-
link.click()
|
|
193
106
|
}
|
|
194
|
-
|
|
195
|
-
watch(selected, load, { immediate: true })
|
|
196
|
-
onBeforeUnmount(() => {
|
|
197
|
-
// invalida antes de revogar: ver `isStale`
|
|
198
|
-
loadToken++
|
|
199
|
-
releaseObjectUrl()
|
|
200
|
-
})
|
|
201
107
|
</script>
|
|
202
108
|
|
|
203
109
|
<template>
|
|
@@ -205,44 +111,40 @@ onBeforeUnmount(() => {
|
|
|
205
111
|
<div class="minuta-viewer__overlay" @click.self="emit('close')">
|
|
206
112
|
<div class="minuta-viewer__modal">
|
|
207
113
|
<div class="minuta-viewer__head">
|
|
208
|
-
<
|
|
209
|
-
|
|
210
|
-
{{ file.name }}
|
|
211
|
-
</option>
|
|
212
|
-
</select>
|
|
213
|
-
<span v-else>{{ currentName }}</span>
|
|
214
|
-
<div class="minuta-viewer__actions">
|
|
215
|
-
<button v-if="content" type="button" class="minuta-viewer__download" @click="downloadCurrent">
|
|
216
|
-
Baixar
|
|
217
|
-
</button>
|
|
218
|
-
<button type="button" class="minuta-viewer__close" @click="emit('close')">
|
|
219
|
-
×
|
|
220
|
-
</button>
|
|
221
|
-
</div>
|
|
114
|
+
<span class="minuta-viewer__title">Documentos do caso</span>
|
|
115
|
+
<TelaButton variant="ghost" size="sm" icon="i-ph-x" @click="emit('close')" />
|
|
222
116
|
</div>
|
|
223
117
|
<div class="minuta-viewer__body">
|
|
224
|
-
<
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
118
|
+
<TelaPreview
|
|
119
|
+
class="h-full"
|
|
120
|
+
:model-value="selected"
|
|
121
|
+
:file-options="fileOptions"
|
|
122
|
+
:current-file="currentFile"
|
|
123
|
+
:resolve-file-url="resolveFileUrl"
|
|
124
|
+
:pdf-loader="pdfLoader"
|
|
125
|
+
:highlight-text="highlightForCurrent"
|
|
126
|
+
:highlight-page="pageForCurrent"
|
|
127
|
+
:labels="{ selectVariable: 'Selecionar documento', file: 'Documento' }"
|
|
128
|
+
:select-variable-labels="{
|
|
129
|
+
placeholder: 'Selecionar documento',
|
|
130
|
+
searchPlaceholder: 'Buscar documento...',
|
|
131
|
+
noVariablesFound: 'Nenhum documento encontrado',
|
|
132
|
+
variablesLabel: 'Categoria',
|
|
133
|
+
filesLabel: 'documentos',
|
|
134
|
+
}"
|
|
135
|
+
:content-labels="{
|
|
136
|
+
failedToLoadFile: 'Não foi possível carregar o documento',
|
|
137
|
+
page: 'Página',
|
|
138
|
+
download: 'Baixar',
|
|
139
|
+
unableToPreview: 'Este formato não abre no navegador',
|
|
140
|
+
zoomOut: 'Diminuir',
|
|
141
|
+
zoomIn: 'Aumentar',
|
|
142
|
+
handMode: 'Modo arrasto',
|
|
143
|
+
fullscreen: 'Tela cheia',
|
|
144
|
+
pageOf: 'de',
|
|
145
|
+
}"
|
|
146
|
+
@update:model-value="selected = $event"
|
|
147
|
+
/>
|
|
246
148
|
</div>
|
|
247
149
|
</div>
|
|
248
150
|
</div>
|
|
@@ -250,5 +152,5 @@ onBeforeUnmount(() => {
|
|
|
250
152
|
</template>
|
|
251
153
|
|
|
252
154
|
<style scoped>
|
|
253
|
-
.minuta-viewer__overlay{align-items:center;background:rgba(
|
|
155
|
+
.minuta-viewer__overlay{align-items:center;background:rgba(15,23,42,.45);display:flex;inset:0;justify-content:center;padding:24px;position:fixed;z-index:60}.minuta-viewer__modal{background:#fff;border-radius:12px;box-shadow:0 24px 60px rgba(15,23,42,.28);display:flex;flex-direction:column;height:min(88vh,100%);overflow:hidden;width:min(1100px,100%)}.minuta-viewer__head{align-items:center;border-bottom:1px solid rgb(0 0 0/8%);display:flex;gap:12px;justify-content:space-between;padding:10px 12px 10px 16px}.minuta-viewer__title{color:#171717;font-size:13px;font-weight:540}.minuta-viewer__body{background:#fff;flex:1;min-height:0}@media (max-width:640px){.minuta-viewer__overlay{padding:0}.minuta-viewer__modal{border-radius:0;height:100%;width:100%}}
|
|
254
156
|
</style>
|
|
@@ -202,7 +202,10 @@ watch(containerRef, (el) => {
|
|
|
202
202
|
if (!el)
|
|
203
203
|
return
|
|
204
204
|
resizeObserver = new ResizeObserver(() => {
|
|
205
|
-
|
|
205
|
+
// respiro lateral da folha: 48px dá moldura no desktop, mas em tela de
|
|
206
|
+
// celular custa ~13% do zoom e o texto fica ilegível
|
|
207
|
+
const breathing = el.clientWidth < 640 ? 12 : 48
|
|
208
|
+
zoom.value = Math.min(1, (el.clientWidth - breathing) / A4_WIDTH_PX)
|
|
206
209
|
})
|
|
207
210
|
resizeObserver.observe(el)
|
|
208
211
|
}, { immediate: true })
|
|
@@ -1137,7 +1140,7 @@ defineExpose({ editor, schedulePaginate, refreshChrome, containerRef })
|
|
|
1137
1140
|
</template>
|
|
1138
1141
|
|
|
1139
1142
|
<style scoped>
|
|
1140
|
-
:deep(.poc-ins){background:rgba(34,197,94,.16);text-decoration:underline;text-decoration-color:#16a34a}:deep(.poc-del){background:rgba(239,68,68,.12);color:#b91c1c;text-decoration:line-through}.poc-bubble-btn{border-radius:6px;font-size:13px;padding:4px 8px;transition:all .15s;transition-timing-function:ease}.poc-toolbar{background:#fafbfc;border-bottom:.5px solid #dee3e7;flex-wrap:wrap;gap:2px;padding:4px 12px}.poc-
|
|
1143
|
+
:deep(.poc-ins){background:rgba(34,197,94,.16);text-decoration:underline;text-decoration-color:#16a34a}:deep(.poc-del){background:rgba(239,68,68,.12);color:#b91c1c;text-decoration:line-through}.poc-bubble-btn{border-radius:6px;font-size:13px;padding:4px 8px;transition:all .15s;transition-timing-function:ease}.poc-toolbar{align-items:center;background:#fafbfc;border-bottom:.5px solid #dee3e7;display:flex;flex-wrap:wrap;gap:2px;padding:4px 12px}@media (max-width:640px){.poc-toolbar{flex-wrap:nowrap;overflow-x:auto;padding:4px 8px;scrollbar-width:none}.poc-toolbar::-webkit-scrollbar{display:none}.poc-toolbar>*{flex:none}}.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-tool-sep{background:#dee3e7;height:20px;margin:0 6px;width:1px}.poc-tool-select{background:#fff;border-radius:6px;font-size:12px;height:28px;padding-left:6px;padding-right:6px}.poc-color-underline{background:#dc2626;border-radius:1px;bottom:4px;height:3px;left:7px;position:absolute;right:7px}.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-palette-swatch{border:1px solid rgba(0,0,0,.1);border-radius:3px;cursor:pointer;height:19px;width:19px}.poc-palette-swatch:hover{transform:scale(1.2)}.poc-palette-custom{align-items:center;color:#565d69;cursor:pointer;display:flex;font-size:12px;margin-top:6px;width:100%}.poc-color-swatch{border:1px solid rgba(0,0,0,.12);border-radius:4px;cursor:pointer;height:16px;margin:0 2px;width:16px}.poc-color-swatch:hover{transform:scale(1.15)}.poc-first-header{align-items:center;cursor:pointer;display:flex;gap:12px;position:absolute;top:0}.poc-first-header[style*=center]{justify-content:center}.poc-first-header[style*=right]{justify-content:flex-end}.poc-first-header:hover,.poc-last-footer:hover,:deep(.poc-page-footerarea:hover),:deep(.poc-page-headerarea:hover){background:rgba(99,102,241,.06);outline:1px dashed #c7d2fe}.poc-last-footer{align-items:center;bottom:0;color:#94a3b8;cursor:pointer;display:flex;font-size:11px;justify-content:space-between;position:absolute}.poc-chrome-text,:deep(.poc-chrome-text){color:#64748b;font-size:11px}.poc-chrome-text :deep(img),:deep(.poc-chrome-text img){display:inline-block;max-height:48px;max-width:200px;vertical-align:middle}:deep(.poc-pagebreak-marker){border-top:1px dashed #a5b4fc;font-size:0!important;height:16px;line-height:0!important;margin:6px 0!important;position:relative}:deep(.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%)}:deep(.poc-page-chrome){display:block;pointer-events:none;text-align:initial;text-indent:0;-webkit-user-select:none;-moz-user-select:none;user-select:none;width:100%}:deep(.poc-page-footerarea),:deep(.poc-page-headerarea){cursor:pointer;pointer-events:auto}:deep(.poc-page-footerarea){align-items:center;color:#94a3b8;display:flex;font-size:11px;justify-content:space-between}:deep(.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)}:deep(.poc-page-headerarea){align-items:center;display:flex}:deep(.poc-page-headerarea[style*=center]){justify-content:center}:deep(.poc-page-headerarea[style*=right]){justify-content:flex-end}:deep(.ProseMirror){outline:none;white-space:pre-wrap;word-wrap:break-word}:deep(h1){color:#0f172a;font-size:24px;font-weight:640;letter-spacing:-.3px;line-height:32px;margin:0 0 20px}:deep(h2){color:#0f172a;font-size:19px;font-weight:620;letter-spacing:-.2px;line-height:26px;margin:28px 0 14px}:deep(h3){color:#0f172a;font-size:17px;font-weight:600;letter-spacing:-.1px;line-height:24px;margin:24px 0 12px}:deep(h1):first-child,:deep(h2):first-child,:deep(h3):first-child{margin-top:0}:deep(strong){font-weight:540}:deep(p){color:#000;font-size:18px;font-weight:400;letter-spacing:-.4px;line-height:28px;line-height:1.7em;margin-bottom:.75em;text-align:justify}:deep(blockquote){font-size:18px;font-weight:400;letter-spacing:-.4px;line-height:28px;margin-bottom:2em;margin-left:35%}:deep(ol),:deep(ul){margin-bottom:2em}:deep(li){list-style-position:inside;margin-bottom:16px;margin-left:48px}:deep(ul)>li{list-style-type:disc}:deep(ol)>li{list-style-type:decimal}:deep(table){border:.5px solid #d4d9df;max-width:100%;table-layout:fixed;width:100%}:deep(th){color:inherit;font-size:inherit;font-weight:700;overflow-wrap:anywhere;padding:12px;text-align:left}:deep(td){color:inherit;font-size:inherit;line-height:inherit;overflow-wrap:anywhere;padding:12px;text-align:left}:deep(td p),:deep(th p){color:inherit;font-size:inherit;line-height:inherit;margin-bottom:0}:deep(img){display:block;max-width:100%}:deep(.poc-island--img>img),:deep(.poc-island>img:only-child){margin-left:auto;margin-right:auto}:deep(.poc-chrome-canvas){overflow:hidden}:deep(.poc-chrome-canvas [data-el]){position:absolute}:deep(.poc-chrome-canvas [data-el=text]){color:#64748b;font-size:11px;line-height:1.4;text-align:left;white-space:pre-wrap}:deep(img[data-vault-src]){animation:poc-img-loading 1.4s ease-in-out infinite}@keyframes poc-img-loading{0%,to{opacity:.5}50%{opacity:.9}}:deep(.poc-lone-row){justify-content:center!important}:deep(.poc-lone-row>:not(.poc-lone-cell)){flex:0 0 0!important;min-width:0!important;padding:0!important}:deep(.poc-lone-row>.poc-lone-cell){align-items:center!important;flex:0 0 auto!important;max-width:100%;min-width:0}:deep([data-horizontal-align=left] .poc-island--img>img),:deep([data-horizontal-align=left] .poc-island>img:only-child){margin-left:0}:deep([data-horizontal-align=right] .poc-island--img>img),:deep([data-horizontal-align=right] .poc-island>img:only-child){margin-right:0}.poc-review-cursor :deep(.poc-minuta-block){cursor:pointer}:deep(.poc-minuta-block){border-radius:4px;position:relative}:deep(.poc-minuta-block:hover){outline:1px dashed #c7d2fe;outline-offset:6px}.poc-no-block-origin :deep(.poc-minuta-block:hover){outline:none}.poc-no-block-origin :deep(.poc-minuta-block:hover):before{content:none}:deep(.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}:deep(.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}:deep(.poc-varchip:hover){background:rgba(59,130,246,.16)}:deep(.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}:deep(.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}:deep(.ProseMirror-selectednode.poc-tplvar){box-shadow:inset 0 0 0 2px rgba(180,83,9,.75)}:deep(.poc-missing-image .missing-image-warning:hover){background-color:rgba(254,174,0,.09);border-color:rgba(223,139,13,.85)}:deep(.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}:deep(.poc-missing-image:hover .poc-missing-image__remove){opacity:1}:deep(.poc-missing-image__remove:hover){background:rgba(254,174,0,.35)}:deep(.poc-missing-image.ProseMirror-selectednode .missing-image-warning){outline:2px solid rgba(223,139,13,.9);outline-offset:2px}.poc-page-notes,:deep(.poc-page-notes){color:#1c242d;font-size:10.67px;line-height:1.25;padding-top:6px}.poc-page-notes:before,:deep(.poc-page-notes):before{border-top:.5px solid #9aa4ae;content:"";display:block;margin-bottom:6px;width:30%}.poc-page-note,:deep(.poc-page-note){color:#1c242d;font-size:10.67px;font-weight:400;line-height:1.25;margin:0;text-align:justify}.poc-page-note sup,:deep(.poc-page-note sup){font-size:.8em;line-height:0;vertical-align:super}.poc-page-note a,:deep(.poc-page-note a){color:#0563c1;text-decoration:underline}:deep(.poc-footnote){color:#1d4ed8;cursor:help;font-size:.7em;line-height:0;vertical-align:super}:deep(.poc-island){position:relative}:deep(.poc-island--img){cursor:grab}:deep(.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}:deep(.poc-island--img.ProseMirror-selectednode .poc-img-handle),:deep(.poc-island--img:hover .poc-img-handle){opacity:1}:deep(.poc-island.ProseMirror-selectednode){outline:2px solid #93c5fd;outline-offset:2px}
|
|
1141
1144
|
</style>
|
|
1142
1145
|
|
|
1143
1146
|
<style scoped>
|
|
@@ -1221,17 +1224,6 @@ defineExpose({ editor, schedulePaginate, refreshChrome, containerRef })
|
|
|
1221
1224
|
border: 0.5px solid #d4d9df;
|
|
1222
1225
|
}
|
|
1223
1226
|
|
|
1224
|
-
/* no CDB os tokens de heading vencem os overrides text-24px/text-20px — replica */
|
|
1225
|
-
.poc-editor-root :deep(h2) {
|
|
1226
|
-
font-size: 14px;
|
|
1227
|
-
line-height: 16px;
|
|
1228
|
-
}
|
|
1229
|
-
|
|
1230
|
-
.poc-editor-root :deep(h3) {
|
|
1231
|
-
font-size: 12px;
|
|
1232
|
-
line-height: 14px;
|
|
1233
|
-
}
|
|
1234
|
-
|
|
1235
1227
|
/* popover de nota de rodapé (ancorado na chamada, estilo comentário) */
|
|
1236
1228
|
.poc-footnote-pop {
|
|
1237
1229
|
position: absolute;
|
|
@@ -8,6 +8,7 @@ import { applyChatEditOps, mergeChatAnnotations } from '../composables/chatEditO
|
|
|
8
8
|
import { pocPageKey } from '../composables/paginationPlugin'
|
|
9
9
|
import { pocEnterVersionDiff, pocExitVersionDiff } from '../composables/versionDiff'
|
|
10
10
|
import { refreshVaultImages } from '../composables/vaultImages'
|
|
11
|
+
import TelaButton from '@meistrari/tela-build/components/tela/button/button.vue'
|
|
11
12
|
import MinutaDocxEditor from './MinutaDocxEditor.vue'
|
|
12
13
|
import MinutaAnnotationsGutter from './MinutaAnnotationsGutter.vue'
|
|
13
14
|
import MinutaChatEditCard from './MinutaChatEditCard.vue'
|
|
@@ -251,6 +252,12 @@ async function exportDocx() {
|
|
|
251
252
|
// ==========================================
|
|
252
253
|
|
|
253
254
|
const historyOpen = ref(false)
|
|
255
|
+
/**
|
|
256
|
+
* Painel de análise em tela estreita. Declarado aqui, e não junto do resto do
|
|
257
|
+
* layout responsivo, porque `toggleHistory` precisa fechá-lo: os dois painéis
|
|
258
|
+
* são mutuamente exclusivos.
|
|
259
|
+
*/
|
|
260
|
+
const showAnalysisPanel = ref(false)
|
|
254
261
|
const versions = ref<MinutaVersion[]>([])
|
|
255
262
|
const loadingVersions = ref(false)
|
|
256
263
|
const savingCheckpoint = ref(false)
|
|
@@ -296,6 +303,8 @@ function toggleHistory() {
|
|
|
296
303
|
closeHistory()
|
|
297
304
|
return
|
|
298
305
|
}
|
|
306
|
+
// só cabe um painel: dois de 320px lado a lado esmagam o documento
|
|
307
|
+
showAnalysisPanel.value = false
|
|
299
308
|
historyOpen.value = true
|
|
300
309
|
void loadVersions()
|
|
301
310
|
}
|
|
@@ -446,6 +455,44 @@ async function applyChrome({ html, scope, showPageNumber }: { html: string, scop
|
|
|
446
455
|
}
|
|
447
456
|
}
|
|
448
457
|
|
|
458
|
+
/**
|
|
459
|
+
* Comentários de margem só cabem quando há folha (≈794px) + coluna (264px) +
|
|
460
|
+
* respiro. Abaixo disso a margem fica fora da tela — então os cards viram um
|
|
461
|
+
* painel lateral que o usuário abre e fecha.
|
|
462
|
+
*/
|
|
463
|
+
const GUTTER_MIN_WIDTH = 1120
|
|
464
|
+
const bodyRef = ref<HTMLElement | null>(null)
|
|
465
|
+
const bodyWidth = ref(0)
|
|
466
|
+
let bodyResizeObserver: ResizeObserver | null = null
|
|
467
|
+
|
|
468
|
+
const narrowLayout = computed(() => bodyWidth.value > 0 && bodyWidth.value < GUTTER_MIN_WIDTH)
|
|
469
|
+
|
|
470
|
+
function openAnalysisPanel() {
|
|
471
|
+
// mutuamente exclusivo com o histórico (ver toggleHistory)
|
|
472
|
+
closeHistory()
|
|
473
|
+
showAnalysisPanel.value = true
|
|
474
|
+
}
|
|
475
|
+
const analysisCount = computed(() => {
|
|
476
|
+
const a = minuta.value?.analysis
|
|
477
|
+
if (!a)
|
|
478
|
+
return 0
|
|
479
|
+
return (a.teses?.length ?? 0) + (a.jurisprudencias?.length ?? 0) + (a.citacoes?.length ?? 0) + (a.fatos?.length ?? 0)
|
|
480
|
+
})
|
|
481
|
+
|
|
482
|
+
watch(bodyRef, (el) => {
|
|
483
|
+
bodyResizeObserver?.disconnect()
|
|
484
|
+
bodyResizeObserver = null
|
|
485
|
+
if (!el || typeof ResizeObserver === 'undefined')
|
|
486
|
+
return
|
|
487
|
+
bodyWidth.value = el.clientWidth
|
|
488
|
+
bodyResizeObserver = new ResizeObserver(() => {
|
|
489
|
+
bodyWidth.value = el.clientWidth
|
|
490
|
+
})
|
|
491
|
+
bodyResizeObserver.observe(el)
|
|
492
|
+
}, { immediate: true })
|
|
493
|
+
|
|
494
|
+
onUnmounted(() => bodyResizeObserver?.disconnect())
|
|
495
|
+
|
|
449
496
|
onMounted(() => window.addEventListener('poc:edit-chrome', onEditChrome))
|
|
450
497
|
onUnmounted(() => window.removeEventListener('poc:edit-chrome', onEditChrome))
|
|
451
498
|
|
|
@@ -523,10 +570,22 @@ const editorCompRef = ref<any>(null)
|
|
|
523
570
|
const docPreviewOpen = ref(false)
|
|
524
571
|
const docPreviewUrl = ref<string | null>(null)
|
|
525
572
|
const docPreviewPage = ref<number | null>(null)
|
|
526
|
-
|
|
527
|
-
|
|
573
|
+
/** trecho que o card de fato pediu para destacar no documento-fonte */
|
|
574
|
+
const docPreviewHighlight = ref<string | null>(null)
|
|
575
|
+
|
|
576
|
+
/**
|
|
577
|
+
* `fromCard` separa os dois chamadores: o botão Documentos abre a lista limpa,
|
|
578
|
+
* e o card de fato extraído mantém página e destaque mesmo quando o documento
|
|
579
|
+
* que ele nomeia não casa com nenhum anexo (aí abre o primeiro arquivo, mas a
|
|
580
|
+
* localização que o card pediu continua valendo).
|
|
581
|
+
*/
|
|
582
|
+
function openDocPreview(url?: string | null, fromCard = false) {
|
|
528
583
|
if (!minuta.value?.files.length)
|
|
529
584
|
return
|
|
585
|
+
if (!fromCard) {
|
|
586
|
+
docPreviewPage.value = null
|
|
587
|
+
docPreviewHighlight.value = null
|
|
588
|
+
}
|
|
530
589
|
docPreviewUrl.value = url ?? minuta.value.files[0].url
|
|
531
590
|
docPreviewOpen.value = true
|
|
532
591
|
}
|
|
@@ -555,7 +614,8 @@ function onOpenDocument(payload: { documento: string, pagina?: number, highlight
|
|
|
555
614
|
const file = minuta.value?.files.find(f => f.name === payload.documento)
|
|
556
615
|
?? minuta.value?.files.find(f => f.name.includes(payload.documento) || payload.documento.includes(f.name))
|
|
557
616
|
docPreviewPage.value = payload.pagina ?? null
|
|
558
|
-
|
|
617
|
+
docPreviewHighlight.value = payload.highlight ?? null
|
|
618
|
+
openDocPreview(file?.url, true)
|
|
559
619
|
}
|
|
560
620
|
|
|
561
621
|
const hasAnalysis = computed(() => {
|
|
@@ -592,32 +652,41 @@ defineExpose({ saveNow, exportDocx, editor })
|
|
|
592
652
|
>
|
|
593
653
|
<span v-else class="minuta-embed__name">{{ minuta.name }}</span>
|
|
594
654
|
<span class="minuta-embed__save-state">{{ SAVE_LABEL[saveState] }}</span>
|
|
595
|
-
<
|
|
655
|
+
<TelaButton
|
|
596
656
|
v-if="minuta.status === 'READY' && minuta.files.length"
|
|
597
|
-
|
|
598
|
-
|
|
657
|
+
variant="ghost"
|
|
658
|
+
size="sm"
|
|
659
|
+
icon="i-ph-files"
|
|
660
|
+
leading
|
|
661
|
+
class="minuta-embed__toolbar-btn"
|
|
599
662
|
@click="openDocPreview()"
|
|
600
663
|
>
|
|
601
|
-
Documentos
|
|
602
|
-
</
|
|
603
|
-
<
|
|
664
|
+
<span class="minuta-embed__btn-label">Documentos</span>
|
|
665
|
+
</TelaButton>
|
|
666
|
+
<TelaButton
|
|
604
667
|
v-if="minuta.status === 'READY'"
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
668
|
+
:variant="historyOpen ? 'secondary' : 'ghost'"
|
|
669
|
+
size="sm"
|
|
670
|
+
icon="i-ph-clock-counter-clockwise"
|
|
671
|
+
leading
|
|
672
|
+
class="minuta-embed__toolbar-btn"
|
|
608
673
|
@click="toggleHistory"
|
|
609
674
|
>
|
|
610
|
-
Histórico
|
|
611
|
-
</
|
|
612
|
-
<
|
|
675
|
+
<span class="minuta-embed__btn-label">Histórico</span>
|
|
676
|
+
</TelaButton>
|
|
677
|
+
<TelaButton
|
|
613
678
|
v-if="minuta.status === 'READY'"
|
|
614
|
-
|
|
679
|
+
variant="primary"
|
|
680
|
+
size="sm"
|
|
681
|
+
icon="i-ph-download-simple"
|
|
682
|
+
leading
|
|
683
|
+
:loading="isExporting"
|
|
615
684
|
:disabled="isExporting || !!versionPreview"
|
|
616
|
-
|
|
685
|
+
class="minuta-embed__toolbar-btn"
|
|
617
686
|
@click="exportDocx"
|
|
618
687
|
>
|
|
619
|
-
{{ isExporting ? 'Exportando…' : 'Baixar DOCX' }}
|
|
620
|
-
</
|
|
688
|
+
<span class="minuta-embed__btn-label">{{ isExporting ? 'Exportando…' : 'Baixar DOCX' }}</span>
|
|
689
|
+
</TelaButton>
|
|
621
690
|
</div>
|
|
622
691
|
|
|
623
692
|
<div v-if="isLoading" class="minuta-embed__status">
|
|
@@ -636,7 +705,7 @@ defineExpose({ saveNow, exportDocx, editor })
|
|
|
636
705
|
A geração falhou: {{ minuta.error || 'erro desconhecido' }}
|
|
637
706
|
</div>
|
|
638
707
|
|
|
639
|
-
<div v-else-if="minuta" class="minuta-embed__body">
|
|
708
|
+
<div v-else-if="minuta" ref="bodyRef" class="minuta-embed__body">
|
|
640
709
|
<div class="minuta-embed__document">
|
|
641
710
|
<MinutaDocxEditor
|
|
642
711
|
ref="editorCompRef"
|
|
@@ -652,7 +721,7 @@ defineExpose({ saveNow, exportDocx, editor })
|
|
|
652
721
|
>
|
|
653
722
|
<template #gutter>
|
|
654
723
|
<MinutaAnnotationsGutter
|
|
655
|
-
v-if="hasAnalysis && minuta.analysis"
|
|
724
|
+
v-if="hasAnalysis && minuta.analysis && !narrowLayout"
|
|
656
725
|
:editor="editor"
|
|
657
726
|
:analysis="minuta.analysis"
|
|
658
727
|
:scroll-container="editorCompRef?.containerRef ?? null"
|
|
@@ -662,6 +731,17 @@ defineExpose({ saveNow, exportDocx, editor })
|
|
|
662
731
|
</template>
|
|
663
732
|
</MinutaDocxEditor>
|
|
664
733
|
|
|
734
|
+
<!-- tela estreita: os comentários viram painel lateral -->
|
|
735
|
+
<button
|
|
736
|
+
v-if="hasAnalysis && narrowLayout && !showAnalysisPanel"
|
|
737
|
+
type="button"
|
|
738
|
+
class="minuta-embed__analysis-toggle"
|
|
739
|
+
@click="openAnalysisPanel"
|
|
740
|
+
>
|
|
741
|
+
Análise
|
|
742
|
+
<span class="minuta-embed__analysis-count">{{ analysisCount }}</span>
|
|
743
|
+
</button>
|
|
744
|
+
|
|
665
745
|
<!-- edição de cabeçalho/rodapé (canvas estilo Word) -->
|
|
666
746
|
<div v-if="chromeEditor" class="minuta-embed__chrome-overlay">
|
|
667
747
|
<MinutaChromeEditor
|
|
@@ -703,6 +783,26 @@ defineExpose({ saveNow, exportDocx, editor })
|
|
|
703
783
|
</div>
|
|
704
784
|
</div>
|
|
705
785
|
|
|
786
|
+
<!-- painel: análise (tela estreita — sem margem para ancorar os cards) -->
|
|
787
|
+
<aside v-if="hasAnalysis && narrowLayout && showAnalysisPanel && minuta.analysis" class="minuta-embed__panel minuta-embed__panel--analysis">
|
|
788
|
+
<div class="minuta-embed__panel-head">
|
|
789
|
+
<span>Análise da peça</span>
|
|
790
|
+
<button type="button" @click="showAnalysisPanel = false">
|
|
791
|
+
×
|
|
792
|
+
</button>
|
|
793
|
+
</div>
|
|
794
|
+
<div class="minuta-embed__panel-scroll">
|
|
795
|
+
<MinutaAnnotationsGutter
|
|
796
|
+
:editor="editor"
|
|
797
|
+
:analysis="minuta.analysis"
|
|
798
|
+
:scroll-container="editorCompRef?.containerRef ?? null"
|
|
799
|
+
compact
|
|
800
|
+
@open-document="onOpenDocument"
|
|
801
|
+
@update-analysis="onAnalysisUpdate"
|
|
802
|
+
/>
|
|
803
|
+
</div>
|
|
804
|
+
</aside>
|
|
805
|
+
|
|
706
806
|
<!-- painel: histórico -->
|
|
707
807
|
<aside v-if="historyOpen" class="minuta-embed__panel">
|
|
708
808
|
<div class="minuta-embed__panel-head">
|
|
@@ -771,6 +871,7 @@ defineExpose({ saveNow, exportDocx, editor })
|
|
|
771
871
|
:files="minuta.files"
|
|
772
872
|
:initial-url="docPreviewUrl"
|
|
773
873
|
:initial-page="docPreviewPage"
|
|
874
|
+
:highlight="docPreviewHighlight"
|
|
774
875
|
@close="docPreviewOpen = false"
|
|
775
876
|
@error="msg => emit('error', msg)"
|
|
776
877
|
/>
|
|
@@ -779,5 +880,5 @@ defineExpose({ saveNow, exportDocx, editor })
|
|
|
779
880
|
</template>
|
|
780
881
|
|
|
781
882
|
<style scoped>
|
|
782
|
-
.minuta-embed{background:#f7f7f8;display:flex;flex-direction:column;height:100%}.minuta-embed__toolbar{align-items:center;background:#fff;border-bottom:1px solid #e5e7eb;display:flex;gap:8px;padding:8px 16px}.minuta-embed__name--input{background:transparent;border:0;font-family:inherit;outline:none}.minuta-embed__name--input:focus{text-decoration:underline}.minuta-embed__name{color:#111827;flex:1;font-size:15px;font-weight:500;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.minuta-embed__save-state{color:#9ca3af;font-size:12px;margin-right:4px}.minuta-
|
|
883
|
+
.minuta-embed{background:#f7f7f8;display:flex;flex-direction:column;height:100%}.minuta-embed__toolbar{align-items:center;background:#fff;border-bottom:1px solid #e5e7eb;display:flex;gap:8px;padding:8px 16px}@media (max-width:640px){.minuta-embed__toolbar{gap:4px;padding:6px 10px}.minuta-embed__btn-label{clip-path:inset(50%);height:1px;overflow:hidden;position:absolute;white-space:nowrap;width:1px}.minuta-embed__save-state{display:none}}.minuta-embed__name--input{background:transparent;border:0;font-family:inherit;outline:none}.minuta-embed__name--input:focus{text-decoration:underline}.minuta-embed__name{color:#111827;flex:1;font-size:15px;font-weight:500;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.minuta-embed__save-state{color:#9ca3af;font-size:12px;margin-right:4px}.minuta-embed__chrome-overlay{bottom:16px;position:absolute;right:16px;z-index:30}.minuta-embed__status{color:#6b7280;font-size:14px;padding:48px 24px;text-align:center}.minuta-embed__status--error{color:#b91c1c}.minuta-embed__body,.minuta-embed__document{display:flex;flex:1;min-height:0}.minuta-embed__document{min-width:0;position:relative}.minuta-embed__banner{align-items:center;background:#fffbeb;border:1px solid #fcd34d;border-radius:16px;bottom:16px;box-shadow:0 4px 6px -1px rgba(0,0,0,.1);display:flex;flex-wrap:wrap;gap:8px 12px;justify-content:center;left:50%;max-width:calc(100% - 32px);padding:8px 16px;position:absolute;transform:translateX(-50%);width:-moz-max-content;width:max-content;z-index:30}.minuta-embed__banner-text{color:#92400e;font-size:12px;font-weight:500}.minuta-embed__banner-actions{display:flex;gap:8px}.minuta-embed__banner-actions button{background:#fff;border:1px solid #fcd34d;border-radius:6px;color:#92400e;cursor:pointer;font-size:12px;padding:4px 10px;white-space:nowrap}.minuta-embed__banner-actions button:disabled{cursor:default;opacity:.6}.minuta-embed__analysis-toggle{align-items:center;background:#fff;border:1px solid #d4d9df;border-radius:999px;bottom:16px;box-shadow:0 2px 10px rgba(15,23,42,.12);color:#334155;cursor:pointer;display:inline-flex;font-size:13px;font-weight:540;gap:8px;padding:8px 14px;position:absolute;right:16px;z-index:20}.minuta-embed__analysis-toggle:hover{border-color:#b8bfc9;color:#0f172a}.minuta-embed__analysis-count{background:#eef1f5;border-radius:999px;color:#64748b;font-size:11px;font-variant-numeric:tabular-nums;padding:1px 7px}.minuta-embed__panel--analysis .minuta-embed__panel-scroll{flex:1;min-height:0;overflow-y:auto;padding:12px}.minuta-embed__panel{background:#fff;border-left:1px solid #e5e7eb;display:flex;flex-direction:column;flex-shrink:0;max-width:55%;width:320px}.minuta-embed__panel-head{align-items:center;border-bottom:1px solid #e5e7eb;color:#111827;display:flex;font-size:14px;font-weight:600;justify-content:space-between;padding:10px 16px}.minuta-embed__panel-head button{background:none;border:none;color:#6b7280;cursor:pointer;font-size:18px;line-height:1}.minuta-embed__checkpoint{border-bottom:1px solid #e5e7eb;display:flex;flex-direction:column;gap:8px;padding:12px}.minuta-embed__checkpoint input{border:1px solid #e5e7eb;border-radius:6px;font-size:13px;padding:6px 10px}.minuta-embed__checkpoint button{background:#fff;border:1px solid #e5e7eb;border-radius:6px;cursor:pointer;font-size:13px;padding:6px 12px}.minuta-embed__versions{flex:1;min-height:0;overflow-y:auto;padding:8px}.minuta-embed__versions-empty{color:#6b7280;font-size:12px;margin:0;padding:8px 12px}.minuta-embed__version{background:#fff;border:1px solid #e5e7eb;border-radius:10px;cursor:pointer;display:block;margin-bottom:6px;padding:10px;text-align:left;width:100%}.minuta-embed__version:hover{background:#faf5ff;border-color:#c4b5fd}.minuta-embed__version--active{background:#f5f3ff;border-color:#a78bfa}.minuta-embed__version:disabled{cursor:wait;opacity:.7}.minuta-embed__version-row{align-items:center;color:#111827;display:flex;font-size:12px;gap:6px}.minuta-embed__version-type{background:#f3f4f6;border-radius:999px;color:#4b5563;font-size:11px;font-style:normal;padding:1px 8px}.minuta-embed__version-type[data-type=auto]{background:#f5f3ff;color:#6d28d9}.minuta-embed__version-type[data-type=manual]{background:#eff6ff;color:#1d4ed8}.minuta-embed__version-date{color:#9ca3af;font-size:11px;margin-left:auto}.minuta-embed__version-comment{color:#111827;display:block;font-size:12px;margin-top:4px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.minuta-embed__version-author{color:#9ca3af;display:block;font-size:11px;margin-top:2px}
|
|
783
884
|
</style>
|
|
@@ -1219,7 +1219,11 @@ export async function pocExportDocx(editor, margins, chrome, options = {}) {
|
|
|
1219
1219
|
const doc = editor.state.doc;
|
|
1220
1220
|
const ctx = {
|
|
1221
1221
|
view: editor.view,
|
|
1222
|
-
|
|
1222
|
+
// quebras `soft` (dentro de um parágrafo) não viram pageBreakBefore:
|
|
1223
|
+
// partir o parágrafo em dois no Word entregaria ao advogado um arquivo
|
|
1224
|
+
// que não reflui ao editar. Ali o Word quebra sozinho, no mesmo lugar
|
|
1225
|
+
// ou a uma linha de distância.
|
|
1226
|
+
breakSet: new Set((options.breaks ?? []).filter((b) => !b.soft).map((b) => b.pos)),
|
|
1223
1227
|
pending: { breakBefore: false },
|
|
1224
1228
|
lastAfterTw: 0,
|
|
1225
1229
|
meta: /* @__PURE__ */ new WeakMap(),
|
|
@@ -54,11 +54,24 @@ export interface PocPageNote {
|
|
|
54
54
|
text: string;
|
|
55
55
|
/** corpo declarado no template, em pontos (default: 8pt, como o Word) */
|
|
56
56
|
size?: number | null;
|
|
57
|
+
/**
|
|
58
|
+
* posição do MARCADOR no documento. Só é usada para repartir as notas
|
|
59
|
+
* quando a quebra cai no meio do parágrafo: a nota mora na página da
|
|
60
|
+
* chamada, então as que ficam depois do corte viajam para a página nova.
|
|
61
|
+
*/
|
|
62
|
+
at?: number;
|
|
57
63
|
}
|
|
58
64
|
export interface PocPageBreak {
|
|
59
65
|
pos: number;
|
|
60
66
|
filler: number;
|
|
61
67
|
pageNo: number;
|
|
68
|
+
/**
|
|
69
|
+
* quebra no MEIO de um parágrafo (entre linhas). O DOCX não recebe
|
|
70
|
+
* pageBreakBefore nessas: partir o parágrafo em dois no Word entregaria
|
|
71
|
+
* ao advogado um arquivo que não reflui quando ele edita o texto. Aqui o
|
|
72
|
+
* Word quebra naturalmente, e o preview só antecipa onde.
|
|
73
|
+
*/
|
|
74
|
+
soft?: boolean;
|
|
62
75
|
/** notas cujos marcadores estão nesta página — desenhadas acima do rodapé */
|
|
63
76
|
notes?: PocPageNote[];
|
|
64
77
|
/** altura reservada pelas notas (já descontada do filler) */
|
|
@@ -120,6 +120,54 @@ function absTop(el, sheet) {
|
|
|
120
120
|
return top;
|
|
121
121
|
}
|
|
122
122
|
const HEADING_TAGS = /* @__PURE__ */ new Set(["H1", "H2", "H3", "H4"]);
|
|
123
|
+
const SPLITTABLE_TAGS = /* @__PURE__ */ new Set(["P"]);
|
|
124
|
+
const MIN_LINES_PER_SIDE = 2;
|
|
125
|
+
function findInlineSplit(view, dom, availablePx, fromLine = 0, minHead = MIN_LINES_PER_SIDE, minTail = MIN_LINES_PER_SIDE) {
|
|
126
|
+
if (!SPLITTABLE_TAGS.has(dom.tagName))
|
|
127
|
+
return null;
|
|
128
|
+
let rects;
|
|
129
|
+
try {
|
|
130
|
+
const range = document.createRange();
|
|
131
|
+
range.selectNodeContents(dom);
|
|
132
|
+
rects = Array.from(range.getClientRects()).filter((r) => r.height > 0 && r.width > 0);
|
|
133
|
+
} catch {
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
if (rects.length < minHead + minTail)
|
|
137
|
+
return null;
|
|
138
|
+
const lines = [];
|
|
139
|
+
for (const r of rects) {
|
|
140
|
+
const last2 = lines[lines.length - 1];
|
|
141
|
+
if (last2 && r.top < last2.bottom - 1) {
|
|
142
|
+
last2.bottom = Math.max(last2.bottom, r.bottom);
|
|
143
|
+
last2.left = Math.min(last2.left, r.left);
|
|
144
|
+
} else {
|
|
145
|
+
lines.push({ top: r.top, bottom: r.bottom, left: r.left });
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
if (lines.length < minHead + minTail)
|
|
149
|
+
return null;
|
|
150
|
+
const scale = dom.offsetHeight > 0 ? dom.getBoundingClientRect().height / dom.offsetHeight : 1;
|
|
151
|
+
if (!Number.isFinite(scale) || scale <= 0)
|
|
152
|
+
return null;
|
|
153
|
+
const originTop = lines[0].top;
|
|
154
|
+
const first = fromLine + minHead;
|
|
155
|
+
const last = lines.length - minTail;
|
|
156
|
+
let cut = -1;
|
|
157
|
+
for (let i = first; i <= last; i++) {
|
|
158
|
+
if ((lines[i].bottom - originTop) / scale > availablePx) {
|
|
159
|
+
cut = i;
|
|
160
|
+
break;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
if (cut < first || cut > last)
|
|
164
|
+
return null;
|
|
165
|
+
const line = lines[cut];
|
|
166
|
+
const found = view.posAtCoords({ left: line.left + 1, top: (line.top + line.bottom) / 2 });
|
|
167
|
+
if (!found)
|
|
168
|
+
return null;
|
|
169
|
+
return { pos: found.pos, usedPx: (lines[cut - 1].bottom - originTop) / scale, line: cut };
|
|
170
|
+
}
|
|
123
171
|
function pageSafetyPx(contentHeight) {
|
|
124
172
|
return Math.max(32, Math.round(contentHeight * 0.08));
|
|
125
173
|
}
|
|
@@ -180,10 +228,10 @@ export function computePocBreaks(view, cfg, sheet) {
|
|
|
180
228
|
return;
|
|
181
229
|
const top = absTop(dom, sheet) - baseY - chromeAbove(absTop(dom, sheet));
|
|
182
230
|
const notes = [];
|
|
183
|
-
child.descendants((inner) => {
|
|
231
|
+
child.descendants((inner, innerPos) => {
|
|
184
232
|
if (inner.type.name === "footnoteRef" && inner.attrs.text) {
|
|
185
233
|
noteCounter += 1;
|
|
186
|
-
notes.push({ n: noteCounter, text: String(inner.attrs.text), size: inner.attrs.size ?? null });
|
|
234
|
+
notes.push({ n: noteCounter, text: String(inner.attrs.text), size: inner.attrs.size ?? null, at: pos + 1 + innerPos });
|
|
187
235
|
}
|
|
188
236
|
return true;
|
|
189
237
|
});
|
|
@@ -193,6 +241,7 @@ export function computePocBreaks(view, cfg, sheet) {
|
|
|
193
241
|
top,
|
|
194
242
|
height: dom.offsetHeight,
|
|
195
243
|
notes,
|
|
244
|
+
dom,
|
|
196
245
|
// margem superior conta como parte da página que o item abre —
|
|
197
246
|
// senão a "página" do preview fica mais alta que um A4 real e
|
|
198
247
|
// o DOCX (página física) divergiria
|
|
@@ -217,9 +266,9 @@ export function computePocBreaks(view, cfg, sheet) {
|
|
|
217
266
|
let forcedBreakPending = false;
|
|
218
267
|
let pageNotes = [];
|
|
219
268
|
const notesH = (notes) => measurer && notes.length ? measurer.height(notes) : 0;
|
|
220
|
-
const closePage = (pos, filler) => {
|
|
269
|
+
const closePage = (pos, filler, soft = false) => {
|
|
221
270
|
const height = notesH(pageNotes);
|
|
222
|
-
breaks.push({ pos, filler: Math.max(0, filler - height), pageNo, notes: pageNotes, notesH: height });
|
|
271
|
+
breaks.push({ pos, filler: Math.max(0, filler - height), pageNo, notes: pageNotes, notesH: height, soft });
|
|
223
272
|
pageNo++;
|
|
224
273
|
pageNotes = [];
|
|
225
274
|
};
|
|
@@ -244,6 +293,41 @@ export function computePocBreaks(view, cfg, sheet) {
|
|
|
244
293
|
pageStart = startWithMargin;
|
|
245
294
|
forcedBreakPending = false;
|
|
246
295
|
} else if (item.top > pageStart && itemEnd - pageStart > capacity) {
|
|
296
|
+
let split = findInlineSplit(view, item.dom, capacity - (item.top - pageStart));
|
|
297
|
+
if (split) {
|
|
298
|
+
let pending = [...item.notes];
|
|
299
|
+
let lastLine = split.line;
|
|
300
|
+
while (split) {
|
|
301
|
+
lastLine = split.line;
|
|
302
|
+
const usedEnd = item.top + split.usedPx;
|
|
303
|
+
const cutAt = split.pos;
|
|
304
|
+
const stay = pending.filter((n) => n.at === void 0 || n.at < cutAt);
|
|
305
|
+
pending = pending.filter((n) => !stay.includes(n));
|
|
306
|
+
pageNotes.push(...stay);
|
|
307
|
+
closePage(cutAt, fullH - (usedEnd - pageStart), true);
|
|
308
|
+
pageStart = usedEnd;
|
|
309
|
+
if (itemEnd - pageStart <= contentH - notesH(pending))
|
|
310
|
+
break;
|
|
311
|
+
const fromTop = pageStart - item.top;
|
|
312
|
+
split = findInlineSplit(
|
|
313
|
+
view,
|
|
314
|
+
item.dom,
|
|
315
|
+
fromTop + contentH - notesH(pending),
|
|
316
|
+
split.line,
|
|
317
|
+
1,
|
|
318
|
+
1
|
|
319
|
+
);
|
|
320
|
+
if (!split)
|
|
321
|
+
split = findInlineSplit(view, item.dom, fromTop + contentH, lastLine, 1, 1);
|
|
322
|
+
}
|
|
323
|
+
pageNotes.push(...pending);
|
|
324
|
+
forcedBreakPending = false;
|
|
325
|
+
while (item.top + item.height - pageStart > contentH) {
|
|
326
|
+
pageStart += contentH;
|
|
327
|
+
pageNo++;
|
|
328
|
+
}
|
|
329
|
+
continue;
|
|
330
|
+
}
|
|
247
331
|
let breakIndex = i;
|
|
248
332
|
if (shouldMoveBreakToHeading({ heading: items[i - 1], item, pageStart, capacity }))
|
|
249
333
|
breakIndex = i - 1;
|
package/package.json
CHANGED
|
@@ -1,100 +1,103 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
},
|
|
10
|
-
"license": "UNLICENSED",
|
|
11
|
-
"type": "module",
|
|
12
|
-
"exports": {
|
|
13
|
-
".": {
|
|
14
|
-
"types": "./dist/types.d.mts",
|
|
15
|
-
"import": "./dist/module.mjs"
|
|
2
|
+
"name": "@meistrari/minuta-nuxt",
|
|
3
|
+
"version": "1.1.1",
|
|
4
|
+
"description": "Embed do editor de minuta (docx paginado A4) do defesa.ai para apps Nuxt — componente + proxy Nitro, sem segredos no browser",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "git+https://github.com/meistrari/case-defense-builder.git",
|
|
8
|
+
"directory": "packages/minuta-nuxt"
|
|
16
9
|
},
|
|
17
|
-
"
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
"
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
"
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
"
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
"
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
10
|
+
"license": "UNLICENSED",
|
|
11
|
+
"type": "module",
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"types": "./dist/types.d.mts",
|
|
15
|
+
"import": "./dist/module.mjs"
|
|
16
|
+
},
|
|
17
|
+
"./runtime/*": {
|
|
18
|
+
"types": "./dist/runtime/*.d.ts",
|
|
19
|
+
"import": "./dist/runtime/*.js"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"main": "./dist/module.mjs",
|
|
23
|
+
"typesVersions": {
|
|
24
|
+
"*": {
|
|
25
|
+
".": [
|
|
26
|
+
"./dist/types.d.mts"
|
|
27
|
+
]
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"files": [
|
|
31
|
+
"dist"
|
|
32
|
+
],
|
|
33
|
+
"scripts": {
|
|
34
|
+
"prepack": "nuxt-module-build build",
|
|
35
|
+
"build": "nuxt-module-build build",
|
|
36
|
+
"dev:prepare": "nuxt-module-build build --stub && nuxi prepare playground",
|
|
37
|
+
"dev": "nuxi dev playground",
|
|
38
|
+
"check:consumo": "node scripts/check-package-consumption.mjs"
|
|
39
|
+
},
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"@fontsource-variable/inter": "5.3.0",
|
|
42
|
+
"@nuxt/kit": "^3.17.7",
|
|
43
|
+
"@phosphor-icons/vue": "2.2.1",
|
|
44
|
+
"@tiptap/core": "2.11.9",
|
|
45
|
+
"@tiptap/extension-table": "2.11.9",
|
|
46
|
+
"@tiptap/extension-table-cell": "2.11.9",
|
|
47
|
+
"@tiptap/extension-table-header": "2.11.9",
|
|
48
|
+
"@tiptap/extension-table-row": "2.11.9",
|
|
49
|
+
"@tiptap/pm": "2.11.9",
|
|
50
|
+
"@tiptap/starter-kit": "2.11.9",
|
|
51
|
+
"@tiptap/vue-3": "2.11.9",
|
|
52
|
+
"defu": "^6.1.4",
|
|
53
|
+
"docx": "9.7.1",
|
|
54
|
+
"h3": "^1.15.0",
|
|
55
|
+
"ofetch": "^1.4.1"
|
|
56
|
+
},
|
|
57
|
+
"overrides": {
|
|
58
|
+
"@tiptap/core": "2.11.9",
|
|
59
|
+
"@tiptap/pm": "2.11.9",
|
|
60
|
+
"@tiptap/extension-blockquote": "2.11.9",
|
|
61
|
+
"@tiptap/extension-bold": "2.11.9",
|
|
62
|
+
"@tiptap/extension-bullet-list": "2.11.9",
|
|
63
|
+
"@tiptap/extension-code": "2.11.9",
|
|
64
|
+
"@tiptap/extension-code-block": "2.11.9",
|
|
65
|
+
"@tiptap/extension-document": "2.11.9",
|
|
66
|
+
"@tiptap/extension-dropcursor": "2.11.9",
|
|
67
|
+
"@tiptap/extension-gapcursor": "2.11.9",
|
|
68
|
+
"@tiptap/extension-hard-break": "2.11.9",
|
|
69
|
+
"@tiptap/extension-heading": "2.11.9",
|
|
70
|
+
"@tiptap/extension-history": "2.11.9",
|
|
71
|
+
"@tiptap/extension-horizontal-rule": "2.11.9",
|
|
72
|
+
"@tiptap/extension-italic": "2.11.9",
|
|
73
|
+
"@tiptap/extension-list-item": "2.11.9",
|
|
74
|
+
"@tiptap/extension-ordered-list": "2.11.9",
|
|
75
|
+
"@tiptap/extension-paragraph": "2.11.9",
|
|
76
|
+
"@tiptap/extension-strike": "2.11.9",
|
|
77
|
+
"@tiptap/extension-text": "2.11.9",
|
|
78
|
+
"@tiptap/extension-text-style": "2.11.9",
|
|
79
|
+
"@tiptap/extension-bubble-menu": "2.11.9",
|
|
80
|
+
"@tiptap/extension-floating-menu": "2.11.9"
|
|
81
|
+
},
|
|
82
|
+
"devDependencies": {
|
|
83
|
+
"@nuxt/module-builder": "^0.8.4",
|
|
84
|
+
"nuxt": "~3.17.7",
|
|
85
|
+
"pino-pretty": "^13.1.3",
|
|
86
|
+
"typescript": "^5.8.2",
|
|
87
|
+
"vue": "^3.5.13",
|
|
88
|
+
"@meistrari/chat-nuxt": "3.14.0-rc.2",
|
|
89
|
+
"@meistrari/tela-build": "1.70.1"
|
|
90
|
+
},
|
|
91
|
+
"publishConfig": {
|
|
92
|
+
"access": "public"
|
|
93
|
+
},
|
|
94
|
+
"peerDependencies": {
|
|
95
|
+
"@meistrari/chat-nuxt": ">=3.13.0",
|
|
96
|
+
"@meistrari/tela-build": ">=1.63.0"
|
|
97
|
+
},
|
|
98
|
+
"peerDependenciesMeta": {
|
|
99
|
+
"@meistrari/chat-nuxt": {
|
|
100
|
+
"optional": true
|
|
101
|
+
}
|
|
98
102
|
}
|
|
99
|
-
}
|
|
100
103
|
}
|