@meistrari/minuta-nuxt 1.0.0 → 1.1.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/dist/module.json +1 -1
- package/dist/runtime/components/MinutaAnnotationsGutter.vue +9 -4
- package/dist/runtime/components/MinutaDocumentViewer.vue +98 -204
- package/dist/runtime/components/MinutaDocxEditor.vue +5 -13
- package/dist/runtime/components/MinutaEmbed.vue +122 -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 +97 -96
package/dist/module.json
CHANGED
|
@@ -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,101 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import { computed,
|
|
2
|
+
import { computed, ref, watch } from 'vue'
|
|
3
3
|
import type { MinutaFile } from '../composables/useMinutaApi'
|
|
4
4
|
import { useMinutaApi } from '../composables/useMinutaApi'
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
8
|
-
*
|
|
7
|
+
* Visualização dos documentos do caso, aberta pelos cards de fato extraído da
|
|
8
|
+
* margem ou pelo botão Documentos.
|
|
9
9
|
*
|
|
10
|
-
* O
|
|
11
|
-
*
|
|
10
|
+
* O corpo é o `TelaPreview` do Tela Build — o mesmo visualizador que o resto
|
|
11
|
+
* do produto usa. Ele resolve o que a implementação anterior (um `<iframe>`
|
|
12
|
+
* apontando para uma `blob:` URL) não resolvia:
|
|
12
13
|
*
|
|
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.
|
|
14
|
+
* - **destaque do trecho.** O card de fato manda `highlight`, e antes esse
|
|
15
|
+
* campo era recebido e descartado: o visualizador nativo do Chrome não tem
|
|
16
|
+
* API de destaque. O TelaPreview aceita `highlightText`/`highlightPage`.
|
|
17
|
+
* - zoom, navegação por página, modo arrasto e tela cheia, que o `<iframe>`
|
|
18
|
+
* delegava à barra cinza do browser.
|
|
19
|
+
* - `vault://` resolvido por `resolveFileUrl`, e o cartão de download para
|
|
20
|
+
* formato que o navegador não abre.
|
|
25
21
|
*/
|
|
26
22
|
|
|
27
23
|
const props = defineProps<{
|
|
28
24
|
files: MinutaFile[]
|
|
29
25
|
initialUrl?: string | null
|
|
30
|
-
/** página
|
|
26
|
+
/** página para onde rolar e destacar */
|
|
31
27
|
initialPage?: number | null
|
|
28
|
+
/** trecho a destacar na página (vem do card de fato extraído) */
|
|
29
|
+
highlight?: string | null
|
|
32
30
|
}>()
|
|
33
31
|
|
|
34
32
|
const emit = defineEmits<{ close: [], error: [message: string] }>()
|
|
35
33
|
|
|
36
34
|
const { getDownloadUrl } = useMinutaApi()
|
|
37
|
-
|
|
38
|
-
type ViewerKind = 'image' | 'text' | 'pdf' | 'binary'
|
|
39
|
-
type ViewerContent = { kind: ViewerKind, src: string, text?: string, sizeLabel?: string }
|
|
35
|
+
const { pdfLoader } = usePdfLoader()
|
|
40
36
|
|
|
41
37
|
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
|
-
}
|
|
38
|
+
watch(() => props.initialUrl, (url) => {
|
|
39
|
+
if (url)
|
|
40
|
+
selected.value = url
|
|
41
|
+
})
|
|
74
42
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
43
|
+
const EXT_MIME: Record<string, string> = {
|
|
44
|
+
pdf: 'application/pdf',
|
|
45
|
+
png: 'image/png',
|
|
46
|
+
jpg: 'image/jpeg',
|
|
47
|
+
jpeg: 'image/jpeg',
|
|
48
|
+
gif: 'image/gif',
|
|
49
|
+
webp: 'image/webp',
|
|
50
|
+
svg: 'image/svg+xml',
|
|
51
|
+
txt: 'text/plain',
|
|
52
|
+
md: 'text/markdown',
|
|
53
|
+
csv: 'text/csv',
|
|
54
|
+
json: 'application/json',
|
|
81
55
|
}
|
|
82
56
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
if (objectUrl) {
|
|
87
|
-
URL.revokeObjectURL(objectUrl)
|
|
88
|
-
objectUrl = null
|
|
89
|
-
}
|
|
57
|
+
function mimeOf(name: string): string {
|
|
58
|
+
const ext = name.split('.').pop()?.toLowerCase() ?? ''
|
|
59
|
+
return EXT_MIME[ext] ?? 'application/octet-stream'
|
|
90
60
|
}
|
|
91
61
|
|
|
92
|
-
|
|
62
|
+
/** o seletor do TelaPreview agrupa por "variável"; aqui só há uma categoria */
|
|
63
|
+
const fileOptions = computed(() => props.files.map(file => ({
|
|
64
|
+
variable: 'Documentos do caso',
|
|
65
|
+
file: file.name,
|
|
66
|
+
value: file.url,
|
|
67
|
+
mimeType: mimeOf(file.name),
|
|
68
|
+
})))
|
|
93
69
|
|
|
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)
|
|
70
|
+
const currentFile = computed(() => {
|
|
71
|
+
const file = props.files.find(f => f.url === selected.value) ?? props.files[0]
|
|
112
72
|
if (!file)
|
|
113
|
-
return
|
|
73
|
+
return null
|
|
74
|
+
return {
|
|
75
|
+
id: file.url,
|
|
76
|
+
fileName: file.name,
|
|
77
|
+
fileUrl: file.url,
|
|
78
|
+
fileType: mimeOf(file.name),
|
|
79
|
+
}
|
|
80
|
+
})
|
|
114
81
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
82
|
+
/** o destaque só vale para o documento que o card apontou */
|
|
83
|
+
const highlightForCurrent = computed(() => (
|
|
84
|
+
selected.value === props.initialUrl ? props.highlight ?? null : null
|
|
85
|
+
))
|
|
86
|
+
const pageForCurrent = computed(() => (
|
|
87
|
+
selected.value === props.initialUrl ? props.initialPage ?? null : null
|
|
88
|
+
))
|
|
119
89
|
|
|
90
|
+
async function resolveFileUrl(url: string): Promise<string> {
|
|
120
91
|
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
|
-
}
|
|
92
|
+
return await getDownloadUrl(url)
|
|
160
93
|
}
|
|
161
|
-
catch {
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
loadError.value = 'Não consegui carregar o documento.'
|
|
165
|
-
emit('error', 'Não consegui carregar o documento')
|
|
94
|
+
catch (error: any) {
|
|
95
|
+
emit('error', error?.message || 'Não foi possível abrir o documento')
|
|
96
|
+
return ''
|
|
166
97
|
}
|
|
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
98
|
}
|
|
194
|
-
|
|
195
|
-
watch(selected, load, { immediate: true })
|
|
196
|
-
onBeforeUnmount(() => {
|
|
197
|
-
// invalida antes de revogar: ver `isStale`
|
|
198
|
-
loadToken++
|
|
199
|
-
releaseObjectUrl()
|
|
200
|
-
})
|
|
201
99
|
</script>
|
|
202
100
|
|
|
203
101
|
<template>
|
|
@@ -205,44 +103,40 @@ onBeforeUnmount(() => {
|
|
|
205
103
|
<div class="minuta-viewer__overlay" @click.self="emit('close')">
|
|
206
104
|
<div class="minuta-viewer__modal">
|
|
207
105
|
<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>
|
|
106
|
+
<span class="minuta-viewer__title">Documentos do caso</span>
|
|
107
|
+
<TelaButton variant="ghost" size="sm" icon="i-ph-x" @click="emit('close')" />
|
|
222
108
|
</div>
|
|
223
109
|
<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
|
-
|
|
110
|
+
<TelaPreview
|
|
111
|
+
class="h-full"
|
|
112
|
+
:model-value="selected"
|
|
113
|
+
:file-options="fileOptions"
|
|
114
|
+
:current-file="currentFile"
|
|
115
|
+
:resolve-file-url="resolveFileUrl"
|
|
116
|
+
:pdf-loader="pdfLoader"
|
|
117
|
+
:highlight-text="highlightForCurrent"
|
|
118
|
+
:highlight-page="pageForCurrent"
|
|
119
|
+
:labels="{ selectVariable: 'Selecionar documento', file: 'Documento' }"
|
|
120
|
+
:select-variable-labels="{
|
|
121
|
+
placeholder: 'Selecionar documento',
|
|
122
|
+
searchPlaceholder: 'Buscar documento...',
|
|
123
|
+
noVariablesFound: 'Nenhum documento encontrado',
|
|
124
|
+
variablesLabel: 'Categoria',
|
|
125
|
+
filesLabel: 'documentos',
|
|
126
|
+
}"
|
|
127
|
+
:content-labels="{
|
|
128
|
+
failedToLoadFile: 'Não foi possível carregar o documento',
|
|
129
|
+
page: 'Página',
|
|
130
|
+
download: 'Baixar',
|
|
131
|
+
unableToPreview: 'Este formato não abre no navegador',
|
|
132
|
+
zoomOut: 'Diminuir',
|
|
133
|
+
zoomIn: 'Aumentar',
|
|
134
|
+
handMode: 'Modo arrasto',
|
|
135
|
+
fullscreen: 'Tela cheia',
|
|
136
|
+
pageOf: 'de',
|
|
137
|
+
}"
|
|
138
|
+
@update:model-value="selected = $event"
|
|
139
|
+
/>
|
|
246
140
|
</div>
|
|
247
141
|
</div>
|
|
248
142
|
</div>
|
|
@@ -250,5 +144,5 @@ onBeforeUnmount(() => {
|
|
|
250
144
|
</template>
|
|
251
145
|
|
|
252
146
|
<style scoped>
|
|
253
|
-
.minuta-viewer__overlay{align-items:center;background:rgba(
|
|
147
|
+
.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
148
|
</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;
|
|
@@ -251,6 +251,12 @@ async function exportDocx() {
|
|
|
251
251
|
// ==========================================
|
|
252
252
|
|
|
253
253
|
const historyOpen = ref(false)
|
|
254
|
+
/**
|
|
255
|
+
* Painel de análise em tela estreita. Declarado aqui, e não junto do resto do
|
|
256
|
+
* layout responsivo, porque `toggleHistory` precisa fechá-lo: os dois painéis
|
|
257
|
+
* são mutuamente exclusivos.
|
|
258
|
+
*/
|
|
259
|
+
const showAnalysisPanel = ref(false)
|
|
254
260
|
const versions = ref<MinutaVersion[]>([])
|
|
255
261
|
const loadingVersions = ref(false)
|
|
256
262
|
const savingCheckpoint = ref(false)
|
|
@@ -296,6 +302,8 @@ function toggleHistory() {
|
|
|
296
302
|
closeHistory()
|
|
297
303
|
return
|
|
298
304
|
}
|
|
305
|
+
// só cabe um painel: dois de 320px lado a lado esmagam o documento
|
|
306
|
+
showAnalysisPanel.value = false
|
|
299
307
|
historyOpen.value = true
|
|
300
308
|
void loadVersions()
|
|
301
309
|
}
|
|
@@ -446,6 +454,44 @@ async function applyChrome({ html, scope, showPageNumber }: { html: string, scop
|
|
|
446
454
|
}
|
|
447
455
|
}
|
|
448
456
|
|
|
457
|
+
/**
|
|
458
|
+
* Comentários de margem só cabem quando há folha (≈794px) + coluna (264px) +
|
|
459
|
+
* respiro. Abaixo disso a margem fica fora da tela — então os cards viram um
|
|
460
|
+
* painel lateral que o usuário abre e fecha.
|
|
461
|
+
*/
|
|
462
|
+
const GUTTER_MIN_WIDTH = 1120
|
|
463
|
+
const bodyRef = ref<HTMLElement | null>(null)
|
|
464
|
+
const bodyWidth = ref(0)
|
|
465
|
+
let bodyResizeObserver: ResizeObserver | null = null
|
|
466
|
+
|
|
467
|
+
const narrowLayout = computed(() => bodyWidth.value > 0 && bodyWidth.value < GUTTER_MIN_WIDTH)
|
|
468
|
+
|
|
469
|
+
function openAnalysisPanel() {
|
|
470
|
+
// mutuamente exclusivo com o histórico (ver toggleHistory)
|
|
471
|
+
closeHistory()
|
|
472
|
+
showAnalysisPanel.value = true
|
|
473
|
+
}
|
|
474
|
+
const analysisCount = computed(() => {
|
|
475
|
+
const a = minuta.value?.analysis
|
|
476
|
+
if (!a)
|
|
477
|
+
return 0
|
|
478
|
+
return (a.teses?.length ?? 0) + (a.jurisprudencias?.length ?? 0) + (a.citacoes?.length ?? 0) + (a.fatos?.length ?? 0)
|
|
479
|
+
})
|
|
480
|
+
|
|
481
|
+
watch(bodyRef, (el) => {
|
|
482
|
+
bodyResizeObserver?.disconnect()
|
|
483
|
+
bodyResizeObserver = null
|
|
484
|
+
if (!el || typeof ResizeObserver === 'undefined')
|
|
485
|
+
return
|
|
486
|
+
bodyWidth.value = el.clientWidth
|
|
487
|
+
bodyResizeObserver = new ResizeObserver(() => {
|
|
488
|
+
bodyWidth.value = el.clientWidth
|
|
489
|
+
})
|
|
490
|
+
bodyResizeObserver.observe(el)
|
|
491
|
+
}, { immediate: true })
|
|
492
|
+
|
|
493
|
+
onUnmounted(() => bodyResizeObserver?.disconnect())
|
|
494
|
+
|
|
449
495
|
onMounted(() => window.addEventListener('poc:edit-chrome', onEditChrome))
|
|
450
496
|
onUnmounted(() => window.removeEventListener('poc:edit-chrome', onEditChrome))
|
|
451
497
|
|
|
@@ -523,10 +569,22 @@ const editorCompRef = ref<any>(null)
|
|
|
523
569
|
const docPreviewOpen = ref(false)
|
|
524
570
|
const docPreviewUrl = ref<string | null>(null)
|
|
525
571
|
const docPreviewPage = ref<number | null>(null)
|
|
526
|
-
|
|
527
|
-
|
|
572
|
+
/** trecho que o card de fato pediu para destacar no documento-fonte */
|
|
573
|
+
const docPreviewHighlight = ref<string | null>(null)
|
|
574
|
+
|
|
575
|
+
/**
|
|
576
|
+
* `fromCard` separa os dois chamadores: o botão Documentos abre a lista limpa,
|
|
577
|
+
* e o card de fato extraído mantém página e destaque mesmo quando o documento
|
|
578
|
+
* que ele nomeia não casa com nenhum anexo (aí abre o primeiro arquivo, mas a
|
|
579
|
+
* localização que o card pediu continua valendo).
|
|
580
|
+
*/
|
|
581
|
+
function openDocPreview(url?: string | null, fromCard = false) {
|
|
528
582
|
if (!minuta.value?.files.length)
|
|
529
583
|
return
|
|
584
|
+
if (!fromCard) {
|
|
585
|
+
docPreviewPage.value = null
|
|
586
|
+
docPreviewHighlight.value = null
|
|
587
|
+
}
|
|
530
588
|
docPreviewUrl.value = url ?? minuta.value.files[0].url
|
|
531
589
|
docPreviewOpen.value = true
|
|
532
590
|
}
|
|
@@ -555,7 +613,8 @@ function onOpenDocument(payload: { documento: string, pagina?: number, highlight
|
|
|
555
613
|
const file = minuta.value?.files.find(f => f.name === payload.documento)
|
|
556
614
|
?? minuta.value?.files.find(f => f.name.includes(payload.documento) || payload.documento.includes(f.name))
|
|
557
615
|
docPreviewPage.value = payload.pagina ?? null
|
|
558
|
-
|
|
616
|
+
docPreviewHighlight.value = payload.highlight ?? null
|
|
617
|
+
openDocPreview(file?.url, true)
|
|
559
618
|
}
|
|
560
619
|
|
|
561
620
|
const hasAnalysis = computed(() => {
|
|
@@ -592,32 +651,41 @@ defineExpose({ saveNow, exportDocx, editor })
|
|
|
592
651
|
>
|
|
593
652
|
<span v-else class="minuta-embed__name">{{ minuta.name }}</span>
|
|
594
653
|
<span class="minuta-embed__save-state">{{ SAVE_LABEL[saveState] }}</span>
|
|
595
|
-
<
|
|
654
|
+
<TelaButton
|
|
596
655
|
v-if="minuta.status === 'READY' && minuta.files.length"
|
|
597
|
-
|
|
598
|
-
|
|
656
|
+
variant="ghost"
|
|
657
|
+
size="sm"
|
|
658
|
+
icon="i-ph-files"
|
|
659
|
+
leading
|
|
660
|
+
class="minuta-embed__toolbar-btn"
|
|
599
661
|
@click="openDocPreview()"
|
|
600
662
|
>
|
|
601
|
-
Documentos
|
|
602
|
-
</
|
|
603
|
-
<
|
|
663
|
+
<span class="minuta-embed__btn-label">Documentos</span>
|
|
664
|
+
</TelaButton>
|
|
665
|
+
<TelaButton
|
|
604
666
|
v-if="minuta.status === 'READY'"
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
667
|
+
:variant="historyOpen ? 'secondary' : 'ghost'"
|
|
668
|
+
size="sm"
|
|
669
|
+
icon="i-ph-clock-counter-clockwise"
|
|
670
|
+
leading
|
|
671
|
+
class="minuta-embed__toolbar-btn"
|
|
608
672
|
@click="toggleHistory"
|
|
609
673
|
>
|
|
610
|
-
Histórico
|
|
611
|
-
</
|
|
612
|
-
<
|
|
674
|
+
<span class="minuta-embed__btn-label">Histórico</span>
|
|
675
|
+
</TelaButton>
|
|
676
|
+
<TelaButton
|
|
613
677
|
v-if="minuta.status === 'READY'"
|
|
614
|
-
|
|
678
|
+
variant="primary"
|
|
679
|
+
size="sm"
|
|
680
|
+
icon="i-ph-download-simple"
|
|
681
|
+
leading
|
|
682
|
+
:loading="isExporting"
|
|
615
683
|
:disabled="isExporting || !!versionPreview"
|
|
616
|
-
|
|
684
|
+
class="minuta-embed__toolbar-btn"
|
|
617
685
|
@click="exportDocx"
|
|
618
686
|
>
|
|
619
|
-
{{ isExporting ? 'Exportando…' : 'Baixar DOCX' }}
|
|
620
|
-
</
|
|
687
|
+
<span class="minuta-embed__btn-label">{{ isExporting ? 'Exportando…' : 'Baixar DOCX' }}</span>
|
|
688
|
+
</TelaButton>
|
|
621
689
|
</div>
|
|
622
690
|
|
|
623
691
|
<div v-if="isLoading" class="minuta-embed__status">
|
|
@@ -636,7 +704,7 @@ defineExpose({ saveNow, exportDocx, editor })
|
|
|
636
704
|
A geração falhou: {{ minuta.error || 'erro desconhecido' }}
|
|
637
705
|
</div>
|
|
638
706
|
|
|
639
|
-
<div v-else-if="minuta" class="minuta-embed__body">
|
|
707
|
+
<div v-else-if="minuta" ref="bodyRef" class="minuta-embed__body">
|
|
640
708
|
<div class="minuta-embed__document">
|
|
641
709
|
<MinutaDocxEditor
|
|
642
710
|
ref="editorCompRef"
|
|
@@ -652,7 +720,7 @@ defineExpose({ saveNow, exportDocx, editor })
|
|
|
652
720
|
>
|
|
653
721
|
<template #gutter>
|
|
654
722
|
<MinutaAnnotationsGutter
|
|
655
|
-
v-if="hasAnalysis && minuta.analysis"
|
|
723
|
+
v-if="hasAnalysis && minuta.analysis && !narrowLayout"
|
|
656
724
|
:editor="editor"
|
|
657
725
|
:analysis="minuta.analysis"
|
|
658
726
|
:scroll-container="editorCompRef?.containerRef ?? null"
|
|
@@ -662,6 +730,17 @@ defineExpose({ saveNow, exportDocx, editor })
|
|
|
662
730
|
</template>
|
|
663
731
|
</MinutaDocxEditor>
|
|
664
732
|
|
|
733
|
+
<!-- tela estreita: os comentários viram painel lateral -->
|
|
734
|
+
<button
|
|
735
|
+
v-if="hasAnalysis && narrowLayout && !showAnalysisPanel"
|
|
736
|
+
type="button"
|
|
737
|
+
class="minuta-embed__analysis-toggle"
|
|
738
|
+
@click="openAnalysisPanel"
|
|
739
|
+
>
|
|
740
|
+
Análise
|
|
741
|
+
<span class="minuta-embed__analysis-count">{{ analysisCount }}</span>
|
|
742
|
+
</button>
|
|
743
|
+
|
|
665
744
|
<!-- edição de cabeçalho/rodapé (canvas estilo Word) -->
|
|
666
745
|
<div v-if="chromeEditor" class="minuta-embed__chrome-overlay">
|
|
667
746
|
<MinutaChromeEditor
|
|
@@ -703,6 +782,26 @@ defineExpose({ saveNow, exportDocx, editor })
|
|
|
703
782
|
</div>
|
|
704
783
|
</div>
|
|
705
784
|
|
|
785
|
+
<!-- painel: análise (tela estreita — sem margem para ancorar os cards) -->
|
|
786
|
+
<aside v-if="hasAnalysis && narrowLayout && showAnalysisPanel && minuta.analysis" class="minuta-embed__panel minuta-embed__panel--analysis">
|
|
787
|
+
<div class="minuta-embed__panel-head">
|
|
788
|
+
<span>Análise da peça</span>
|
|
789
|
+
<button type="button" @click="showAnalysisPanel = false">
|
|
790
|
+
×
|
|
791
|
+
</button>
|
|
792
|
+
</div>
|
|
793
|
+
<div class="minuta-embed__panel-scroll">
|
|
794
|
+
<MinutaAnnotationsGutter
|
|
795
|
+
:editor="editor"
|
|
796
|
+
:analysis="minuta.analysis"
|
|
797
|
+
:scroll-container="editorCompRef?.containerRef ?? null"
|
|
798
|
+
compact
|
|
799
|
+
@open-document="onOpenDocument"
|
|
800
|
+
@update-analysis="onAnalysisUpdate"
|
|
801
|
+
/>
|
|
802
|
+
</div>
|
|
803
|
+
</aside>
|
|
804
|
+
|
|
706
805
|
<!-- painel: histórico -->
|
|
707
806
|
<aside v-if="historyOpen" class="minuta-embed__panel">
|
|
708
807
|
<div class="minuta-embed__panel-head">
|
|
@@ -771,6 +870,7 @@ defineExpose({ saveNow, exportDocx, editor })
|
|
|
771
870
|
:files="minuta.files"
|
|
772
871
|
:initial-url="docPreviewUrl"
|
|
773
872
|
:initial-page="docPreviewPage"
|
|
873
|
+
:highlight="docPreviewHighlight"
|
|
774
874
|
@close="docPreviewOpen = false"
|
|
775
875
|
@error="msg => emit('error', msg)"
|
|
776
876
|
/>
|
|
@@ -779,5 +879,5 @@ defineExpose({ saveNow, exportDocx, editor })
|
|
|
779
879
|
</template>
|
|
780
880
|
|
|
781
881
|
<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-
|
|
882
|
+
.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
883
|
</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,101 @@
|
|
|
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.0",
|
|
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
|
+
},
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"@fontsource-variable/inter": "5.3.0",
|
|
41
|
+
"@nuxt/kit": "^3.17.7",
|
|
42
|
+
"@phosphor-icons/vue": "2.2.1",
|
|
43
|
+
"@tiptap/core": "2.11.9",
|
|
44
|
+
"@tiptap/extension-table": "2.11.9",
|
|
45
|
+
"@tiptap/extension-table-cell": "2.11.9",
|
|
46
|
+
"@tiptap/extension-table-header": "2.11.9",
|
|
47
|
+
"@tiptap/extension-table-row": "2.11.9",
|
|
48
|
+
"@tiptap/pm": "2.11.9",
|
|
49
|
+
"@tiptap/starter-kit": "2.11.9",
|
|
50
|
+
"@tiptap/vue-3": "2.11.9",
|
|
51
|
+
"defu": "^6.1.4",
|
|
52
|
+
"docx": "9.7.1",
|
|
53
|
+
"h3": "^1.15.0",
|
|
54
|
+
"ofetch": "^1.4.1"
|
|
55
|
+
},
|
|
56
|
+
"overrides": {
|
|
57
|
+
"@tiptap/core": "2.11.9",
|
|
58
|
+
"@tiptap/pm": "2.11.9",
|
|
59
|
+
"@tiptap/extension-blockquote": "2.11.9",
|
|
60
|
+
"@tiptap/extension-bold": "2.11.9",
|
|
61
|
+
"@tiptap/extension-bullet-list": "2.11.9",
|
|
62
|
+
"@tiptap/extension-code": "2.11.9",
|
|
63
|
+
"@tiptap/extension-code-block": "2.11.9",
|
|
64
|
+
"@tiptap/extension-document": "2.11.9",
|
|
65
|
+
"@tiptap/extension-dropcursor": "2.11.9",
|
|
66
|
+
"@tiptap/extension-gapcursor": "2.11.9",
|
|
67
|
+
"@tiptap/extension-hard-break": "2.11.9",
|
|
68
|
+
"@tiptap/extension-heading": "2.11.9",
|
|
69
|
+
"@tiptap/extension-history": "2.11.9",
|
|
70
|
+
"@tiptap/extension-horizontal-rule": "2.11.9",
|
|
71
|
+
"@tiptap/extension-italic": "2.11.9",
|
|
72
|
+
"@tiptap/extension-list-item": "2.11.9",
|
|
73
|
+
"@tiptap/extension-ordered-list": "2.11.9",
|
|
74
|
+
"@tiptap/extension-paragraph": "2.11.9",
|
|
75
|
+
"@tiptap/extension-strike": "2.11.9",
|
|
76
|
+
"@tiptap/extension-text": "2.11.9",
|
|
77
|
+
"@tiptap/extension-text-style": "2.11.9",
|
|
78
|
+
"@tiptap/extension-bubble-menu": "2.11.9",
|
|
79
|
+
"@tiptap/extension-floating-menu": "2.11.9"
|
|
80
|
+
},
|
|
81
|
+
"devDependencies": {
|
|
82
|
+
"@nuxt/module-builder": "^0.8.4",
|
|
83
|
+
"nuxt": "~3.17.7",
|
|
84
|
+
"pino-pretty": "^13.1.3",
|
|
85
|
+
"typescript": "^5.8.2",
|
|
86
|
+
"vue": "^3.5.13",
|
|
87
|
+
"@meistrari/chat-nuxt": "3.14.0-rc.2"
|
|
88
|
+
},
|
|
89
|
+
"publishConfig": {
|
|
90
|
+
"access": "public"
|
|
91
|
+
},
|
|
92
|
+
"peerDependencies": {
|
|
93
|
+
"@meistrari/chat-nuxt": ">=3.13.0",
|
|
94
|
+
"@meistrari/tela-build": ">=1.63.0"
|
|
95
|
+
},
|
|
96
|
+
"peerDependenciesMeta": {
|
|
97
|
+
"@meistrari/chat-nuxt": {
|
|
98
|
+
"optional": true
|
|
99
|
+
}
|
|
98
100
|
}
|
|
99
|
-
}
|
|
100
101
|
}
|