Backend: - Sessions de vote : list, close, tally, threshold details, auto-expiration - Protocoles : update, simulate, meta-gouvernance, formulas CRUD - Service vote enrichi : close_session, get_threshold_details, nuanced breakdown - Schemas : ThresholdDetailOut, VoteResultOut, FormulaSimulationRequest/Result - WebSocket broadcast sur chaque vote + fermeture session - 25 nouveaux tests (threshold details, close, nuanced, simulation) Frontend: - 5 composants vote : VoteBinary, VoteNuanced, ThresholdGauge, FormulaDisplay, VoteHistory - 3 composants protocoles : ProtocolPicker, FormulaEditor, ModeParamsDisplay - Simulateur de formules interactif (page /protocols/formulas) - Page detail protocole (/protocols/[id]) - Composable useWebSocket (live updates) - Composable useVoteFormula (calcul client-side reactif) - Integration KaTeX pour rendu LaTeX des formules Documentation: - API reference : 8 nouveaux endpoints documentes - Formules : tables d'inertie, parametres detailles, simulation API - Guide vote : vote binaire/nuance, jauge, historique, simulateur, meta-gouvernance 55 tests passes (+ 1 skipped), 126 fichiers total. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
111 lines
4.7 KiB
Vue
111 lines
4.7 KiB
Vue
<script setup lang="ts">
|
|
/**
|
|
* Display vote formula with KaTeX rendering.
|
|
*
|
|
* Renders the WoT threshold formula using KaTeX when available,
|
|
* falling back to a code display. Shows parameter values and
|
|
* optional Smith/TechComm criteria formulas.
|
|
*/
|
|
import type { FormulaConfig } from '~/stores/protocols'
|
|
|
|
const props = defineProps<{
|
|
formulaConfig: FormulaConfig
|
|
showExplanation?: boolean
|
|
}>()
|
|
|
|
const showExplain = ref(props.showExplanation ?? false)
|
|
|
|
/**
|
|
* Render a LaTeX string to HTML using KaTeX, with code fallback.
|
|
*/
|
|
function renderFormula(tex: string): string {
|
|
if (typeof window !== 'undefined' && (window as any).katex) {
|
|
return (window as any).katex.renderToString(tex, { throwOnError: false, displayMode: true })
|
|
}
|
|
return `<code class="text-sm font-mono">${tex}</code>`
|
|
}
|
|
|
|
/** Main threshold formula in LaTeX. */
|
|
const mainFormulaTeX = 'Seuil = C + B^W + \\left(M + (1-M) \\cdot \\left(1 - \\left(\\frac{T}{W}\\right)^G\\right)\\right) \\cdot \\max(0,\\, T - C)'
|
|
|
|
/** Smith criterion formula. */
|
|
const smithFormulaTeX = computed(() => {
|
|
if (props.formulaConfig.smith_exponent === null) return null
|
|
return `Seuil_{Smith} = \\lceil W_{Smith}^{${props.formulaConfig.smith_exponent}} \\rceil`
|
|
})
|
|
|
|
/** TechComm criterion formula. */
|
|
const techcommFormulaTeX = computed(() => {
|
|
if (props.formulaConfig.techcomm_exponent === null) return null
|
|
return `Seuil_{TechComm} = \\lceil W_{TechComm}^{${props.formulaConfig.techcomm_exponent}} \\rceil`
|
|
})
|
|
|
|
const mainFormulaHtml = computed(() => renderFormula(mainFormulaTeX))
|
|
const smithFormulaHtml = computed(() => smithFormulaTeX.value ? renderFormula(smithFormulaTeX.value) : null)
|
|
const techcommFormulaHtml = computed(() => techcommFormulaTeX.value ? renderFormula(techcommFormulaTeX.value) : null)
|
|
|
|
const parameters = computed(() => [
|
|
{ label: 'Duree', code: 'D', value: `${props.formulaConfig.duration_days} jours`, description: 'Duree du vote en jours' },
|
|
{ label: 'Majorite', code: 'M', value: `${props.formulaConfig.majority_pct}%`, description: 'Ratio de majorite cible a haute participation' },
|
|
{ label: 'Base', code: 'B', value: String(props.formulaConfig.base_exponent), description: 'Exposant de base (B^W tend vers 0 si B < 1)' },
|
|
{ label: 'Gradient', code: 'G', value: String(props.formulaConfig.gradient_exponent), description: 'Exposant du gradient d\'inertie' },
|
|
{ label: 'Constante', code: 'C', value: String(props.formulaConfig.constant_base), description: 'Plancher fixe de votes requis' },
|
|
...(props.formulaConfig.smith_exponent !== null ? [{
|
|
label: 'Smith', code: 'S', value: String(props.formulaConfig.smith_exponent), description: 'Exposant du critere Smith',
|
|
}] : []),
|
|
...(props.formulaConfig.techcomm_exponent !== null ? [{
|
|
label: 'TechComm', code: 'T', value: String(props.formulaConfig.techcomm_exponent), description: 'Exposant du critere TechComm',
|
|
}] : []),
|
|
])
|
|
</script>
|
|
|
|
<template>
|
|
<div class="space-y-4">
|
|
<!-- Main formula -->
|
|
<div class="p-4 bg-gray-50 dark:bg-gray-800 rounded-lg overflow-x-auto">
|
|
<div v-html="mainFormulaHtml" class="text-center" />
|
|
</div>
|
|
|
|
<!-- Smith criterion -->
|
|
<div v-if="smithFormulaHtml" class="p-3 bg-blue-50 dark:bg-blue-900/20 rounded-lg overflow-x-auto">
|
|
<p class="text-xs font-semibold text-blue-600 dark:text-blue-400 mb-2">Critere Smith</p>
|
|
<div v-html="smithFormulaHtml" class="text-center" />
|
|
</div>
|
|
|
|
<!-- TechComm criterion -->
|
|
<div v-if="techcommFormulaHtml" class="p-3 bg-purple-50 dark:bg-purple-900/20 rounded-lg overflow-x-auto">
|
|
<p class="text-xs font-semibold text-purple-600 dark:text-purple-400 mb-2">Critere TechComm</p>
|
|
<div v-html="techcommFormulaHtml" class="text-center" />
|
|
</div>
|
|
|
|
<!-- Parameters grid -->
|
|
<div class="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-3">
|
|
<div
|
|
v-for="param in parameters"
|
|
:key="param.code"
|
|
class="p-3 bg-gray-50 dark:bg-gray-800 rounded-lg"
|
|
>
|
|
<div class="flex items-center gap-2 mb-1">
|
|
<span class="font-mono font-bold text-primary text-sm">{{ param.code }}</span>
|
|
<span class="text-xs font-medium text-gray-700 dark:text-gray-300">{{ param.label }}</span>
|
|
</div>
|
|
<div class="text-lg font-semibold text-gray-900 dark:text-white">{{ param.value }}</div>
|
|
<p v-if="showExplain" class="text-xs text-gray-500 mt-1">{{ param.description }}</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Explanation toggle -->
|
|
<div class="flex justify-end">
|
|
<UButton
|
|
variant="ghost"
|
|
color="neutral"
|
|
size="xs"
|
|
:icon="showExplain ? 'i-lucide-eye-off' : 'i-lucide-eye'"
|
|
@click="showExplain = !showExplain"
|
|
>
|
|
{{ showExplain ? 'Masquer les explications' : 'Afficher les explications' }}
|
|
</UButton>
|
|
</div>
|
|
</div>
|
|
</template>
|