Files
decision/frontend/app/components/common/MarkdownRenderer.vue
Yvv 2bdc731639 Sprint 2 : moteur de documents + sanctuaire
Backend:
- CRUD complet documents/items/versions (update, delete, accept, reject, reorder)
- Service IPFS (upload/retrieve/pin via kubo HTTP API)
- Service sanctuaire : pipeline SHA-256 + IPFS + on-chain (system.remark)
- Verification integrite des entrees sanctuaire
- Recherche par reference (document -> entrees sanctuaire)
- Serialisation deterministe des documents pour archivage
- 14 tests unitaires supplementaires (document service)

Frontend:
- 9 composants : StatusBadge, MarkdownRenderer, DiffView, ItemCard,
  ItemVersionDiff, DocumentList, SanctuaryEntry, IPFSLink, ChainAnchor
- Page detail item avec historique des versions et diff
- Page detail sanctuaire avec verification integrite
- Modal de creation de document + proposition de version
- Archivage document vers sanctuaire depuis la page detail

Documentation:
- API reference mise a jour (9 nouveaux endpoints)
- Guides utilisateur documents et sanctuaire enrichis

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 13:08:48 +01:00

68 lines
1.5 KiB
Vue

<script setup lang="ts">
const props = defineProps<{
content: string
}>()
/**
* Basic markdown rendering: bold, line breaks, unordered lists.
* Returns sanitized HTML string for v-html usage.
*/
const renderedHtml = computed(() => {
if (!props.content) return ''
let html = props.content
// Escape HTML entities to prevent XSS
html = html
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
// Convert **bold** to <strong>
html = html.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>')
// Convert *italic* to <em>
html = html.replace(/\*(.+?)\*/g, '<em>$1</em>')
// Process lines for list items
const lines = html.split('\n')
const result: string[] = []
let inList = false
for (const line of lines) {
const trimmed = line.trim()
if (trimmed.startsWith('- ')) {
if (!inList) {
result.push('<ul class="list-disc list-inside space-y-1 my-2">')
inList = true
}
result.push(`<li>${trimmed.slice(2)}</li>`)
} else {
if (inList) {
result.push('</ul>')
inList = false
}
if (trimmed === '') {
result.push('<br>')
} else {
result.push(`<p class="my-1">${line}</p>`)
}
}
}
if (inList) {
result.push('</ul>')
}
return result.join('\n')
})
</script>
<template>
<div
class="prose prose-sm dark:prose-invert max-w-none text-gray-700 dark:text-gray-300 leading-relaxed"
v-html="renderedHtml"
/>
</template>