@meistrari/minuta-nuxt 0.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/README.md +106 -0
- package/dist/module.cjs +5 -0
- package/dist/module.d.mts +18 -0
- package/dist/module.d.ts +18 -0
- package/dist/module.json +9 -0
- package/dist/module.mjs +45 -0
- package/dist/runtime/components/MinutaAnnotationsGutter.vue +425 -0
- package/dist/runtime/components/MinutaChatEditCard.vue +124 -0
- package/dist/runtime/components/MinutaChromeCanvas.vue +549 -0
- package/dist/runtime/components/MinutaChromeEditor.vue +100 -0
- package/dist/runtime/components/MinutaDocumentViewer.vue +90 -0
- package/dist/runtime/components/MinutaDocxEditor.vue +1158 -0
- package/dist/runtime/components/MinutaEmbed.vue +783 -0
- package/dist/runtime/components/MinutaIcon.vue +53 -0
- package/dist/runtime/composables/annotationAnchors.d.ts +73 -0
- package/dist/runtime/composables/annotationAnchors.js +243 -0
- package/dist/runtime/composables/chatEditOps.d.ts +37 -0
- package/dist/runtime/composables/chatEditOps.js +61 -0
- package/dist/runtime/composables/docxExport.d.ts +24 -0
- package/dist/runtime/composables/docxExport.js +1292 -0
- package/dist/runtime/composables/editorExtensions.d.ts +56 -0
- package/dist/runtime/composables/editorExtensions.js +466 -0
- package/dist/runtime/composables/paginationPlugin.d.ts +126 -0
- package/dist/runtime/composables/paginationPlugin.js +421 -0
- package/dist/runtime/composables/useMinutaApi.d.ts +112 -0
- package/dist/runtime/composables/useMinutaApi.js +68 -0
- package/dist/runtime/composables/vaultImages.d.ts +12 -0
- package/dist/runtime/composables/vaultImages.js +85 -0
- package/dist/runtime/composables/versionDiff.d.ts +34 -0
- package/dist/runtime/composables/versionDiff.js +62 -0
- package/dist/runtime/server/api/minuta/[...path].d.ts +2 -0
- package/dist/runtime/server/api/minuta/[...path].js +6 -0
- package/dist/runtime/server/api/minuta/index.d.ts +2 -0
- package/dist/runtime/server/api/minuta/index.js +3 -0
- package/dist/runtime/server/utils/minuta-proxy.d.ts +10 -0
- package/dist/runtime/server/utils/minuta-proxy.js +63 -0
- package/dist/runtime/utils/footnoteHtml.d.ts +26 -0
- package/dist/runtime/utils/footnoteHtml.js +117 -0
- package/dist/runtime/utils/pocKeepWithNext.d.ts +44 -0
- package/dist/runtime/utils/pocKeepWithNext.js +9 -0
- package/dist/runtime/utils/sanitizeRichHtml.d.ts +10 -0
- package/dist/runtime/utils/sanitizeRichHtml.js +73 -0
- package/dist/types.d.mts +7 -0
- package/dist/types.d.ts +7 -0
- package/package.json +100 -0
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed, ref } from 'vue'
|
|
3
|
+
import { sanitizeRichHtml } from '../utils/sanitizeRichHtml'
|
|
4
|
+
import MinutaChromeCanvas from './MinutaChromeCanvas.vue'
|
|
5
|
+
import MinutaIcon from './MinutaIcon.vue'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Editor de cabeçalho/rodapé da folha — canvas estilo Word em tamanho real da
|
|
9
|
+
* faixa (texto/imagem/forma com arrasto, resize e fonte). Port do
|
|
10
|
+
* poc-chrome-editor do defesa.ai com CSS puro.
|
|
11
|
+
*/
|
|
12
|
+
const props = withDefaults(defineProps<{
|
|
13
|
+
part: 'header' | 'footer'
|
|
14
|
+
html: string
|
|
15
|
+
scope: 'all' | 'first'
|
|
16
|
+
/** altura real da faixa (margem da página) — vem do consumidor */
|
|
17
|
+
bandHeightPx?: number
|
|
18
|
+
docFont?: string
|
|
19
|
+
showPageNumber?: boolean
|
|
20
|
+
/** mostra o toggle de numeração no rodapé */
|
|
21
|
+
allowPageNumberToggle?: boolean
|
|
22
|
+
}>(), {
|
|
23
|
+
bandHeightPx: 94,
|
|
24
|
+
docFont: 'Arial',
|
|
25
|
+
showPageNumber: true,
|
|
26
|
+
allowPageNumberToggle: false,
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
const emit = defineEmits<{
|
|
30
|
+
apply: [payload: { html: string, scope: 'all' | 'first', showPageNumber?: boolean }]
|
|
31
|
+
close: []
|
|
32
|
+
}>()
|
|
33
|
+
|
|
34
|
+
const localScope = ref<'all' | 'first'>(props.scope)
|
|
35
|
+
const localHtml = ref(sanitizeRichHtml(props.html))
|
|
36
|
+
const localShowPageNumber = ref(props.showPageNumber)
|
|
37
|
+
|
|
38
|
+
const effectiveShowPageNumber = computed(() =>
|
|
39
|
+
props.allowPageNumberToggle ? localShowPageNumber.value : props.showPageNumber)
|
|
40
|
+
|
|
41
|
+
function apply() {
|
|
42
|
+
emit('apply', {
|
|
43
|
+
html: sanitizeRichHtml(localHtml.value),
|
|
44
|
+
scope: localScope.value,
|
|
45
|
+
...(props.allowPageNumberToggle ? { showPageNumber: localShowPageNumber.value } : {}),
|
|
46
|
+
})
|
|
47
|
+
}
|
|
48
|
+
</script>
|
|
49
|
+
|
|
50
|
+
<template>
|
|
51
|
+
<div class="minuta-chrome-card">
|
|
52
|
+
<div class="minuta-chrome-card__head">
|
|
53
|
+
<span class="minuta-chrome-card__badge">
|
|
54
|
+
{{ part === 'header' ? 'Cabeçalho das páginas' : 'Rodapé das páginas' }}
|
|
55
|
+
</span>
|
|
56
|
+
<button class="minuta-chrome-card__close" type="button" @click="emit('close')">
|
|
57
|
+
<MinutaIcon name="i-ph-x" />
|
|
58
|
+
</button>
|
|
59
|
+
</div>
|
|
60
|
+
|
|
61
|
+
<MinutaChromeCanvas
|
|
62
|
+
:html="localHtml"
|
|
63
|
+
:width-px="794"
|
|
64
|
+
:height-px="bandHeightPx"
|
|
65
|
+
:doc-font="docFont"
|
|
66
|
+
:ghost="part === 'footer' && effectiveShowPageNumber ? 'Página 1 de N' : ''"
|
|
67
|
+
:placeholder="part === 'header'
|
|
68
|
+
? 'Monte o cabeçalho: texto, imagem e formas — arraste como no Word'
|
|
69
|
+
: (effectiveShowPageNumber ? 'Monte o rodapé — o nº de página é automático' : 'Monte o rodapé — sem numeração de páginas')"
|
|
70
|
+
class="minuta-chrome-card__canvas"
|
|
71
|
+
@change="(html: string) => localHtml = html"
|
|
72
|
+
/>
|
|
73
|
+
|
|
74
|
+
<label v-if="allowPageNumberToggle && part === 'footer'" class="minuta-chrome-card__pagenum">
|
|
75
|
+
<input v-model="localShowPageNumber" type="checkbox">
|
|
76
|
+
Numerar as páginas automaticamente ("Página 1 de N")
|
|
77
|
+
</label>
|
|
78
|
+
|
|
79
|
+
<div class="minuta-chrome-card__scope">
|
|
80
|
+
<span>Aparece em</span>
|
|
81
|
+
<select v-model="localScope">
|
|
82
|
+
<option value="all">
|
|
83
|
+
Todas as páginas
|
|
84
|
+
</option>
|
|
85
|
+
<option value="first">
|
|
86
|
+
Só na primeira página
|
|
87
|
+
</option>
|
|
88
|
+
</select>
|
|
89
|
+
</div>
|
|
90
|
+
<div class="minuta-chrome-card__actions">
|
|
91
|
+
<button class="minuta-chrome-card__apply" type="button" @click="apply">
|
|
92
|
+
Aplicar
|
|
93
|
+
</button>
|
|
94
|
+
</div>
|
|
95
|
+
</div>
|
|
96
|
+
</template>
|
|
97
|
+
|
|
98
|
+
<style scoped>
|
|
99
|
+
.minuta-chrome-card{background:#fff;border:1px solid #ddd6fe;border-radius:12px;box-shadow:0 10px 25px -5px rgba(0,0,0,.15);max-height:calc(100vh - 120px);overflow-y:auto;padding:16px;width:min(846px,calc(100vw - 48px))}.minuta-chrome-card__head{align-items:center;display:flex;justify-content:space-between;margin-bottom:8px}.minuta-chrome-card__badge{background:#ede9fe;border-radius:999px;color:#6d28d9;font-size:12px;font-weight:600;padding:2px 10px}.minuta-chrome-card__close{background:none;border:none;color:#9ca3af;cursor:pointer;display:inline-flex}.minuta-chrome-card__close:hover{color:#1f2937}.minuta-chrome-card__canvas{margin-bottom:10px}.minuta-chrome-card__pagenum{align-items:center;color:#374151;display:flex;font-size:12px;gap:8px;margin-bottom:8px}.minuta-chrome-card__scope{align-items:center;color:#6b7280;display:flex;font-size:12px;gap:8px;margin-bottom:10px}.minuta-chrome-card__scope select{background:#fff;border:1px solid #e5e7eb;border-radius:8px;color:#1f2937;font-size:12px;outline:none;padding:4px 8px;width:220px}.minuta-chrome-card__actions{display:flex;justify-content:flex-end}.minuta-chrome-card__apply{background:rgba(0,0,0,.88);border:none;border-radius:8px;color:#fff;cursor:pointer;font-size:12.5px;font-weight:500;padding:5px 14px}
|
|
100
|
+
</style>
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed, ref, watch } from 'vue'
|
|
3
|
+
import type { MinutaFile } from '../composables/useMinutaApi'
|
|
4
|
+
import { useMinutaApi } from '../composables/useMinutaApi'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Overlay de visualização dos documentos do caso (imagem/texto/iframe),
|
|
8
|
+
* aberto pelos cards de fato extraído da margem ou pelo botão Documentos.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const props = defineProps<{
|
|
12
|
+
files: MinutaFile[]
|
|
13
|
+
initialUrl?: string | null
|
|
14
|
+
/** página inicial (PDF em iframe via #page=N) */
|
|
15
|
+
initialPage?: number | null
|
|
16
|
+
}>()
|
|
17
|
+
|
|
18
|
+
const emit = defineEmits<{ close: [], error: [message: string] }>()
|
|
19
|
+
|
|
20
|
+
const { getDownloadUrl } = useMinutaApi()
|
|
21
|
+
|
|
22
|
+
const selected = ref(props.initialUrl || props.files[0]?.url || '')
|
|
23
|
+
const content = ref<{ src: string, kind: 'image' | 'text' | 'frame', text?: string } | null>(null)
|
|
24
|
+
const loading = ref(false)
|
|
25
|
+
|
|
26
|
+
const currentName = computed(() => props.files.find(f => f.url === selected.value)?.name ?? '')
|
|
27
|
+
|
|
28
|
+
async function load() {
|
|
29
|
+
const file = props.files.find(f => f.url === selected.value)
|
|
30
|
+
if (!file)
|
|
31
|
+
return
|
|
32
|
+
loading.value = true
|
|
33
|
+
content.value = null
|
|
34
|
+
try {
|
|
35
|
+
const src = await getDownloadUrl(file.url)
|
|
36
|
+
if (/\.(png|jpe?g|gif|webp|svg)$/i.test(file.name)) {
|
|
37
|
+
content.value = { src, kind: 'image' }
|
|
38
|
+
}
|
|
39
|
+
else if (/\.(txt|md|json|csv|log)$/i.test(file.name)) {
|
|
40
|
+
const text = await (await fetch(src)).text()
|
|
41
|
+
content.value = { src, kind: 'text', text }
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
const isPdf = /\.pdf$/i.test(file.name)
|
|
45
|
+
const page = props.initialPage && file.url === props.initialUrl ? `#page=${props.initialPage}` : ''
|
|
46
|
+
content.value = { src: isPdf ? src + page : src, kind: 'frame' }
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
emit('error', 'Não consegui carregar o documento')
|
|
51
|
+
}
|
|
52
|
+
finally {
|
|
53
|
+
loading.value = false
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
watch(selected, load, { immediate: true })
|
|
58
|
+
</script>
|
|
59
|
+
|
|
60
|
+
<template>
|
|
61
|
+
<Teleport to="body">
|
|
62
|
+
<div class="minuta-viewer__overlay" @click.self="emit('close')">
|
|
63
|
+
<div class="minuta-viewer__modal">
|
|
64
|
+
<div class="minuta-viewer__head">
|
|
65
|
+
<select v-if="files.length > 1" v-model="selected">
|
|
66
|
+
<option v-for="file in files" :key="file.url" :value="file.url">
|
|
67
|
+
{{ file.name }}
|
|
68
|
+
</option>
|
|
69
|
+
</select>
|
|
70
|
+
<span v-else>{{ currentName }}</span>
|
|
71
|
+
<button type="button" @click="emit('close')">
|
|
72
|
+
×
|
|
73
|
+
</button>
|
|
74
|
+
</div>
|
|
75
|
+
<div class="minuta-viewer__body">
|
|
76
|
+
<p v-if="loading" class="minuta-viewer__loading">
|
|
77
|
+
Carregando…
|
|
78
|
+
</p>
|
|
79
|
+
<img v-else-if="content?.kind === 'image'" :src="content.src">
|
|
80
|
+
<pre v-else-if="content?.kind === 'text'">{{ content.text }}</pre>
|
|
81
|
+
<iframe v-else-if="content" :src="content.src" />
|
|
82
|
+
</div>
|
|
83
|
+
</div>
|
|
84
|
+
</div>
|
|
85
|
+
</Teleport>
|
|
86
|
+
</template>
|
|
87
|
+
|
|
88
|
+
<style scoped>
|
|
89
|
+
.minuta-viewer__overlay{align-items:center;background:rgba(0,0,0,.5);display:flex;inset:0;justify-content:center;padding:24px;position:fixed;z-index:100}.minuta-viewer__modal{background:#fff;border-radius:12px;box-shadow:0 20px 25px -5px rgba(0,0,0,.2);display:flex;flex-direction:column;height:100%;max-height:90vh;max-width:960px;overflow:hidden;width:100%}.minuta-viewer__head{align-items:center;border-bottom:1px solid #e5e7eb;color:#111827;display:flex;font-size:14px;font-weight:600;gap:12px;justify-content:space-between;padding:10px 16px}.minuta-viewer__head select{background:#fff;border:.5px solid #d4d9df;border-radius:6px;font-size:13px;max-width:70%;padding:4px 8px}.minuta-viewer__head button{background:none;border:none;color:#6b7280;cursor:pointer;font-size:18px;line-height:1}.minuta-viewer__body{background:#f9fafb;flex:1;min-height:0;overflow-y:auto}.minuta-viewer__loading{color:#6b7280;font-size:13px;padding:24px}.minuta-viewer__body img{height:100%;-o-object-fit:contain;object-fit:contain;width:100%}.minuta-viewer__body pre{color:#1f2937;font-size:13px;line-height:1.6;margin:0;padding:20px;white-space:pre-wrap}.minuta-viewer__body iframe{border:0;height:100%;width:100%}
|
|
90
|
+
</style>
|