docstodev 1.1.1 → 2.0.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/docs/docs-to-dev.md +225 -0
- package/docs/report.html +633 -0
- package/docs/report.pdf +0 -0
- package/docs/summary.md +16 -0
- package/package.json +2 -2
- package/src/ai/analyzer.ts +258 -19
- package/src/cli/index.ts +8 -7
- package/src/commands/run.ts +149 -83
- package/src/exporters/html.ts +302 -48
package/src/exporters/html.ts
CHANGED
|
@@ -38,13 +38,12 @@ export function exportToHTML(
|
|
|
38
38
|
const cells = processed.split("|").filter(c => c.trim());
|
|
39
39
|
const isHeader = processed.includes("Module") || processed.includes("Type");
|
|
40
40
|
|
|
41
|
-
htmlResult += `<tr>${
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
}</tr>`;
|
|
41
|
+
htmlResult += `<tr>${cells.map(c =>
|
|
42
|
+
isHeader
|
|
43
|
+
? `<th>${formatText(c.trim())}</th>`
|
|
44
|
+
: `<td>${formatText(c.trim())}</td>`
|
|
45
|
+
).join("")
|
|
46
|
+
}</tr>`;
|
|
48
47
|
continue;
|
|
49
48
|
} else if (inTable) {
|
|
50
49
|
htmlResult += "</table></div>";
|
|
@@ -59,7 +58,7 @@ export function exportToHTML(
|
|
|
59
58
|
) {
|
|
60
59
|
const hasFile = processed.match(/\.(ts|js|py|java|cs|go|rs|tsx|jsx|html|css|php|rb|sql)$/);
|
|
61
60
|
const fileMatch = hasFile ? processed.match(/([\w-]+\.(ts|js|py|java|cs|go|rs|tsx|jsx|html|css|php|rb|sql))/)?.[0] : null;
|
|
62
|
-
|
|
61
|
+
|
|
63
62
|
let treeLine = line
|
|
64
63
|
.replace(/├─/g, '<span class="branch">├─</span>')
|
|
65
64
|
.replace(/└─/g, '<span class="branch">└─</span>')
|
|
@@ -109,6 +108,10 @@ export function exportToHTML(
|
|
|
109
108
|
const hierarchyGraph = fileTree ? generateHierarchyGraph(fileTree) : getDefaultHierarchyGraph();
|
|
110
109
|
const dataFlowGraph = generateDataFlowGraph();
|
|
111
110
|
|
|
111
|
+
// Extraire les sections spéciales de markdownContent
|
|
112
|
+
const projectGoalMatch = markdownContent.match(/(?:But du Projet|Project Goal)[:\s]*\n+([\s\S]*?)(?=\n#{2,}|$)/i);
|
|
113
|
+
const risksMatch = markdownContent.match(/(?:Risques Identifiés|Identified Risks)[:\s]*\n+([\s\S]*?)(?=\n#{2,}|$)/i);
|
|
114
|
+
|
|
112
115
|
const html = `<!DOCTYPE html>
|
|
113
116
|
<html lang="${lang}" data-theme="dark">
|
|
114
117
|
<head>
|
|
@@ -303,10 +306,18 @@ h3 {
|
|
|
303
306
|
margin-bottom: 1.5rem;
|
|
304
307
|
}
|
|
305
308
|
|
|
309
|
+
.component p,
|
|
310
|
+
.component .list-item {
|
|
311
|
+
word-wrap: break-word;
|
|
312
|
+
overflow-wrap: break-word;
|
|
313
|
+
word-break: break-word;
|
|
314
|
+
}
|
|
315
|
+
|
|
306
316
|
.tree-line {
|
|
307
317
|
font-family: "Fira Code", monospace;
|
|
308
318
|
font-size: 0.85rem;
|
|
309
|
-
white-space: pre;
|
|
319
|
+
white-space: pre-wrap;
|
|
320
|
+
word-wrap: break-word;
|
|
310
321
|
color: var(--muted);
|
|
311
322
|
line-height: 1.8;
|
|
312
323
|
}
|
|
@@ -340,12 +351,15 @@ table {
|
|
|
340
351
|
width: 100%;
|
|
341
352
|
border-collapse: collapse;
|
|
342
353
|
font-size: 0.9rem;
|
|
354
|
+
table-layout: fixed;
|
|
343
355
|
}
|
|
344
356
|
|
|
345
357
|
th, td {
|
|
346
358
|
border-bottom: 1px solid var(--border);
|
|
347
359
|
padding: 0.75rem 1rem;
|
|
348
360
|
text-align: left;
|
|
361
|
+
word-wrap: break-word;
|
|
362
|
+
overflow-wrap: break-word;
|
|
349
363
|
}
|
|
350
364
|
|
|
351
365
|
th {
|
|
@@ -367,6 +381,8 @@ code {
|
|
|
367
381
|
padding: 0.2rem 0.4rem;
|
|
368
382
|
font-size: 0.9em;
|
|
369
383
|
color: var(--accent);
|
|
384
|
+
word-wrap: break-word;
|
|
385
|
+
overflow-wrap: break-word;
|
|
370
386
|
}
|
|
371
387
|
|
|
372
388
|
.list-item {
|
|
@@ -428,6 +444,133 @@ mark {
|
|
|
428
444
|
font-size: 0.9rem;
|
|
429
445
|
}
|
|
430
446
|
|
|
447
|
+
.project-goal-section {
|
|
448
|
+
background: linear-gradient(135deg, var(--accent) 0%, #7c3aed 100%);
|
|
449
|
+
padding: 2rem;
|
|
450
|
+
margin: 2rem 0;
|
|
451
|
+
border-radius: 8px;
|
|
452
|
+
border-left: 4px solid var(--accent);
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
.project-goal-section h2 {
|
|
456
|
+
color: #ffffff;
|
|
457
|
+
border-bottom: none;
|
|
458
|
+
margin: 0 0 1rem 0;
|
|
459
|
+
font-size: 1.8rem;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
.project-goal-section p {
|
|
463
|
+
color: rgba(255, 255, 255, 0.95);
|
|
464
|
+
font-size: 1.1rem;
|
|
465
|
+
line-height: 1.8;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
.risks-section {
|
|
469
|
+
background: var(--card);
|
|
470
|
+
border: 1px solid var(--border);
|
|
471
|
+
border-left: 4px solid #f85149;
|
|
472
|
+
padding: 1.5rem;
|
|
473
|
+
margin: 2rem 0;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
.risks-section h3 {
|
|
477
|
+
color: #f85149;
|
|
478
|
+
margin-bottom: 1rem;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
.risk-item {
|
|
482
|
+
padding: 0.75rem;
|
|
483
|
+
margin: 0.5rem 0;
|
|
484
|
+
border-radius: 4px;
|
|
485
|
+
display: flex;
|
|
486
|
+
align-items: flex-start;
|
|
487
|
+
gap: 0.75rem;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
.risk-item.critical {
|
|
491
|
+
background: rgba(248, 81, 73, 0.1);
|
|
492
|
+
border-left: 3px solid #f85149;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
.risk-item.medium {
|
|
496
|
+
background: rgba(251, 191, 36, 0.1);
|
|
497
|
+
border-left: 3px solid #fbbf24;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
.risk-item.low {
|
|
501
|
+
background: rgba(34, 197, 94, 0.1);
|
|
502
|
+
border-left: 3px solid #22c55e;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
.risk-icon {
|
|
506
|
+
font-size: 1.2rem;
|
|
507
|
+
flex-shrink: 0;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
.recommendations-section {
|
|
511
|
+
background: var(--card);
|
|
512
|
+
border: 1px solid var(--border);
|
|
513
|
+
border-left: 4px solid #22c55e;
|
|
514
|
+
padding: 1.5rem;
|
|
515
|
+
margin: 2rem 0;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
.recommendations-section h3 {
|
|
519
|
+
color: #22c55e;
|
|
520
|
+
margin-bottom: 1rem;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
.recommendation-item {
|
|
524
|
+
padding: 0.75rem;
|
|
525
|
+
margin: 0.5rem 0;
|
|
526
|
+
background: rgba(34, 197, 94, 0.05);
|
|
527
|
+
border-radius: 4px;
|
|
528
|
+
padding-left: 2rem;
|
|
529
|
+
position: relative;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
.recommendation-item::before {
|
|
533
|
+
content: "💡";
|
|
534
|
+
position: absolute;
|
|
535
|
+
left: 0.5rem;
|
|
536
|
+
top: 0.75rem;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
.design-system-section {
|
|
540
|
+
background: var(--card);
|
|
541
|
+
border: 1px solid var(--border);
|
|
542
|
+
padding: 1.5rem;
|
|
543
|
+
margin: 2rem 0;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
.color-palette {
|
|
547
|
+
display: flex;
|
|
548
|
+
gap: 1rem;
|
|
549
|
+
flex-wrap: wrap;
|
|
550
|
+
margin: 1rem 0;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
.color-swatch {
|
|
554
|
+
display: flex;
|
|
555
|
+
flex-direction: column;
|
|
556
|
+
align-items: center;
|
|
557
|
+
gap: 0.5rem;
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
.color-box {
|
|
561
|
+
width: 60px;
|
|
562
|
+
height: 60px;
|
|
563
|
+
border-radius: 8px;
|
|
564
|
+
border: 2px solid var(--border);
|
|
565
|
+
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
.color-label {
|
|
569
|
+
font-size: 0.75rem;
|
|
570
|
+
font-family: "Fira Code", monospace;
|
|
571
|
+
color: var(--muted);
|
|
572
|
+
}
|
|
573
|
+
|
|
431
574
|
@media print {
|
|
432
575
|
.controls {
|
|
433
576
|
display: none;
|
|
@@ -458,6 +601,15 @@ mark {
|
|
|
458
601
|
<body>
|
|
459
602
|
<div class="container">
|
|
460
603
|
|
|
604
|
+
${projectGoalMatch ? `
|
|
605
|
+
<div class="project-goal-section">
|
|
606
|
+
<h2>🎯 ${lang === 'fr' ? 'But du Projet' : 'Project Goal'}</h2>
|
|
607
|
+
<p>${formatText(projectGoalMatch[1]?.trim() || "")}</p>
|
|
608
|
+
</div>
|
|
609
|
+
` : ''}
|
|
610
|
+
|
|
611
|
+
${risksMatch ? formatRisksSection(risksMatch[1] || "", lang) : ''}
|
|
612
|
+
|
|
461
613
|
<div class="controls">
|
|
462
614
|
<input id="search" placeholder="${lang === 'fr' ? 'Rechercher dans la documentation...' : 'Search in documentation...'}" type="text">
|
|
463
615
|
<button onclick="toggleTheme()">
|
|
@@ -530,14 +682,13 @@ document.getElementById("search").addEventListener("input", e => {
|
|
|
530
682
|
return;
|
|
531
683
|
}
|
|
532
684
|
|
|
533
|
-
const regex = new RegExp("(" + term.replace(/[
|
|
685
|
+
const regex = new RegExp("(" + term.replace(/[.*+?\\\${}()|[\\]\\\\]/g, "\\\\$&") + ")", "gi");
|
|
534
686
|
container.innerHTML = originalHTML.replace(regex, "<mark>$1</mark>");
|
|
535
687
|
|
|
536
688
|
const first = container.querySelector("mark");
|
|
537
689
|
if (first) first.scrollIntoView({ behavior: "smooth", block: "center" });
|
|
538
690
|
});
|
|
539
691
|
|
|
540
|
-
// Gestion d'erreur Mermaid
|
|
541
692
|
window.addEventListener('load', () => {
|
|
542
693
|
setTimeout(() => {
|
|
543
694
|
document.querySelectorAll('.mermaid').forEach(el => {
|
|
@@ -559,61 +710,124 @@ window.addEventListener('load', () => {
|
|
|
559
710
|
}
|
|
560
711
|
|
|
561
712
|
function sanitizeMermaidGraph(graph: string): string {
|
|
562
|
-
// Nettoyer les caractères problématiques
|
|
563
713
|
let cleaned = graph
|
|
564
714
|
.replace(/[""]/g, '"')
|
|
565
715
|
.replace(/['']/g, "'")
|
|
566
|
-
.replace(/\u00A0/g, ' ')
|
|
716
|
+
.replace(/\u00A0/g, ' ')
|
|
567
717
|
.trim();
|
|
568
|
-
|
|
569
|
-
// Vérifier que le graphe a une déclaration valide
|
|
718
|
+
|
|
570
719
|
if (!cleaned.match(/^(graph|flowchart|sequenceDiagram|classDiagram|stateDiagram|gantt|pie|erDiagram)/)) {
|
|
571
720
|
cleaned = "graph TD\n " + cleaned;
|
|
572
721
|
}
|
|
573
|
-
|
|
574
|
-
// Limiter la complexité si trop de nœuds
|
|
722
|
+
|
|
575
723
|
const lines = cleaned.split('\n');
|
|
576
|
-
|
|
577
|
-
|
|
724
|
+
let enrichedGraph = lines[0] + '\n';
|
|
725
|
+
|
|
726
|
+
for (let i = 1; i < Math.min(lines.length, 40); i++) {
|
|
727
|
+
const line = lines[i]?.trim();
|
|
728
|
+
if (!line || line.startsWith('classDef')) continue;
|
|
729
|
+
|
|
730
|
+
let styledLine = line;
|
|
731
|
+
|
|
732
|
+
if (line.includes('_js') || line.includes('.js"')) {
|
|
733
|
+
styledLine = line + ':::jsFileStyle';
|
|
734
|
+
} else if (line.includes('_ts') || line.includes('.ts"')) {
|
|
735
|
+
styledLine = line + ':::tsFileStyle';
|
|
736
|
+
} else if (line.includes('Analyzer') || line.includes('analyzer')) {
|
|
737
|
+
styledLine = line + ':::analyzerStyle';
|
|
738
|
+
} else if (line.includes('Component') || line.includes('component')) {
|
|
739
|
+
styledLine = line + ':::componentStyle';
|
|
740
|
+
} else if (line.match(/\(\)/)) {
|
|
741
|
+
styledLine = line + ':::functionStyle';
|
|
742
|
+
} else if (line.includes('Service') || line.includes('service')) {
|
|
743
|
+
styledLine = line + ':::serviceStyle';
|
|
744
|
+
} else if (line.includes('Config') || line.includes('config')) {
|
|
745
|
+
styledLine = line + ':::configStyle';
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
enrichedGraph += ' ' + styledLine + '\n';
|
|
578
749
|
}
|
|
579
|
-
|
|
580
|
-
|
|
750
|
+
|
|
751
|
+
enrichedGraph += `
|
|
752
|
+
classDef jsFileStyle fill:#f7df1e,stroke:#c9b003,stroke-width:2px,color:#000
|
|
753
|
+
classDef tsFileStyle fill:#3178c6,stroke:#235a97,stroke-width:2px,color:#fff
|
|
754
|
+
classDef analyzerStyle fill:#8b5cf6,stroke:#6d28d9,stroke-width:2px,color:#fff
|
|
755
|
+
classDef componentStyle fill:#06b6d4,stroke:#0891b2,stroke-width:2px,color:#fff
|
|
756
|
+
classDef functionStyle fill:#ec4899,stroke:#be185d,stroke-width:2px,color:#fff
|
|
757
|
+
classDef serviceStyle fill:#10b981,stroke:#059669,stroke-width:2px,color:#fff
|
|
758
|
+
classDef configStyle fill:#6e7681,stroke:#484f58,stroke-width:2px,color:#fff`;
|
|
759
|
+
|
|
760
|
+
return enrichedGraph;
|
|
581
761
|
}
|
|
582
762
|
|
|
583
|
-
function generateHierarchyGraph(tree: TreeStructure, maxDepth =
|
|
763
|
+
function generateHierarchyGraph(tree: TreeStructure, maxDepth: number = 4): string {
|
|
584
764
|
let graph = "graph TD\n";
|
|
585
765
|
let nodeId = 0;
|
|
586
|
-
|
|
587
|
-
|
|
766
|
+
|
|
588
767
|
function sanitizeLabel(label: string): string {
|
|
589
|
-
return label.replace(/["\[\]]/g, '');
|
|
768
|
+
return label.replace(/["\[\]]/g, '').replace(/\//g, '');
|
|
590
769
|
}
|
|
591
|
-
|
|
592
|
-
function
|
|
770
|
+
|
|
771
|
+
function getFileExtension(filename: string): string {
|
|
772
|
+
const parts = filename.split('.');
|
|
773
|
+
return parts.length > 1 ? parts[parts.length - 1] ?? "" : '';
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
function getNodeStyle(key: string, isFolder: boolean): string {
|
|
777
|
+
if (isFolder) {
|
|
778
|
+
return ':::folderStyle';
|
|
779
|
+
}
|
|
780
|
+
const ext = getFileExtension(key);
|
|
781
|
+
const styleMap: { [key: string]: string } = {
|
|
782
|
+
'ts': ':::tsStyle',
|
|
783
|
+
'tsx': ':::tsStyle',
|
|
784
|
+
'js': ':::jsStyle',
|
|
785
|
+
'jsx': ':::jsStyle',
|
|
786
|
+
'json': ':::configStyle',
|
|
787
|
+
'html': ':::htmlStyle',
|
|
788
|
+
'css': ':::cssStyle',
|
|
789
|
+
'md': ':::docStyle'
|
|
790
|
+
};
|
|
791
|
+
return styleMap[ext] || ':::fileStyle';
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
function traverse(obj: TreeStructure, parentId: string | null, depth: number): void {
|
|
593
795
|
if (depth > maxDepth) return;
|
|
594
|
-
|
|
595
|
-
const entries = Object.entries(obj)
|
|
596
|
-
|
|
796
|
+
|
|
797
|
+
const entries = Object.entries(obj);
|
|
798
|
+
|
|
597
799
|
entries.forEach(([key, value]) => {
|
|
598
800
|
const currentId = `node${nodeId++}`;
|
|
599
801
|
const isFolder = value && typeof value === 'object' && Object.keys(value).length > 0;
|
|
600
|
-
const label = sanitizeLabel(
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
802
|
+
const label = sanitizeLabel(key);
|
|
803
|
+
const displayLabel = isFolder ? `📁 ${label}` : `📄 ${label}`;
|
|
804
|
+
const style = getNodeStyle(key, isFolder || false);
|
|
805
|
+
|
|
806
|
+
graph += ` ${currentId}["${displayLabel}"]${style}\n`;
|
|
807
|
+
|
|
605
808
|
if (parentId) {
|
|
606
809
|
graph += ` ${parentId} --> ${currentId}\n`;
|
|
607
810
|
}
|
|
608
|
-
|
|
811
|
+
|
|
609
812
|
if (isFolder && value && depth < maxDepth) {
|
|
610
|
-
traverse(value, currentId, depth + 1
|
|
813
|
+
traverse(value, currentId, depth + 1);
|
|
611
814
|
}
|
|
612
815
|
});
|
|
613
816
|
}
|
|
614
|
-
|
|
817
|
+
|
|
615
818
|
try {
|
|
616
819
|
traverse(tree, null, 0);
|
|
820
|
+
|
|
821
|
+
graph += `
|
|
822
|
+
classDef folderStyle fill:#58a6ff,stroke:#1f6feb,stroke-width:2px,color:#fff
|
|
823
|
+
classDef tsStyle fill:#3178c6,stroke:#235a97,stroke-width:2px,color:#fff
|
|
824
|
+
classDef jsStyle fill:#f7df1e,stroke:#c9b003,stroke-width:2px,color:#000
|
|
825
|
+
classDef configStyle fill:#6e7681,stroke:#484f58,stroke-width:2px,color:#fff
|
|
826
|
+
classDef htmlStyle fill:#e34c26,stroke:#b83c1f,stroke-width:2px,color:#fff
|
|
827
|
+
classDef cssStyle fill:#264de4,stroke:#1b3ba3,stroke-width:2px,color:#fff
|
|
828
|
+
classDef docStyle fill:#22c55e,stroke:#16a34a,stroke-width:2px,color:#fff
|
|
829
|
+
classDef fileStyle fill:#8b949e,stroke:#6e7681,stroke-width:2px,color:#fff`;
|
|
830
|
+
|
|
617
831
|
return graph || getDefaultHierarchyGraph();
|
|
618
832
|
} catch (e) {
|
|
619
833
|
console.error("Error generating hierarchy graph:", e);
|
|
@@ -623,18 +837,58 @@ function generateHierarchyGraph(tree: TreeStructure, maxDepth = 3): string {
|
|
|
623
837
|
|
|
624
838
|
function getDefaultHierarchyGraph(): string {
|
|
625
839
|
return `graph TD
|
|
626
|
-
A["Root"] --> B["src"]
|
|
627
|
-
A --> C["config"]
|
|
628
|
-
B --> D["components"]
|
|
629
|
-
B --> E["utils"]
|
|
630
|
-
B --> F["services"]
|
|
840
|
+
A["📁 Root"]:::folderStyle --> B["📁 src"]:::folderStyle
|
|
841
|
+
A --> C["📁 config"]:::folderStyle
|
|
842
|
+
B --> D["📁 components"]:::folderStyle
|
|
843
|
+
B --> E["📁 utils"]:::folderStyle
|
|
844
|
+
B --> F["📁 services"]:::folderStyle
|
|
845
|
+
|
|
846
|
+
classDef folderStyle fill:#58a6ff,stroke:#1f6feb,stroke-width:2px,color:#fff`;
|
|
631
847
|
}
|
|
632
848
|
|
|
633
849
|
function generateDataFlowGraph(): string {
|
|
634
850
|
return `graph LR
|
|
635
|
-
A["Input"] --> B["Analyzer"]
|
|
636
|
-
B --> C["Parser"]
|
|
637
|
-
C --> D["Generator"]
|
|
638
|
-
D --> E["Exporter"]
|
|
639
|
-
E --> F["Output"]
|
|
851
|
+
A["📥 Input<br/>(fichiers source)"]:::inputStyle --> B["🔍 Analyzer<br/>(analyse code)"]:::analyzerStyle
|
|
852
|
+
B --> C["📊 Parser<br/>(extraction données)"]:::parserStyle
|
|
853
|
+
C --> D["⚙️ Generator<br/>(génération docs)"]:::generatorStyle
|
|
854
|
+
D --> E["📤 Exporter<br/>(export HTML/PDF)"]:::exporterStyle
|
|
855
|
+
E --> F["✅ Output<br/>(documentation)"]:::outputStyle
|
|
856
|
+
|
|
857
|
+
classDef inputStyle fill:#3b82f6,stroke:#1d4ed8,stroke-width:2px,color:#fff
|
|
858
|
+
classDef analyzerStyle fill:#8b5cf6,stroke:#6d28d9,stroke-width:2px,color:#fff
|
|
859
|
+
classDef parserStyle fill:#ec4899,stroke:#be185d,stroke-width:2px,color:#fff
|
|
860
|
+
classDef generatorStyle fill:#f59e0b,stroke:#d97706,stroke-width:2px,color:#fff
|
|
861
|
+
classDef exporterStyle fill:#10b981,stroke:#059669,stroke-width:2px,color:#fff
|
|
862
|
+
classDef outputStyle fill:#22c55e,stroke:#16a34a,stroke-width:2px,color:#fff`;
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
function formatRisksSection(risksContent: string, lang: "fr" | "en"): string {
|
|
866
|
+
const criticalMatches = risksContent.match(/🔴[^\n]+/g) || [];
|
|
867
|
+
const mediumMatches = risksContent.match(/🟡[^\n]+/g) || [];
|
|
868
|
+
const lowMatches = risksContent.match(/🟢[^\n]+/g) || [];
|
|
869
|
+
|
|
870
|
+
if (criticalMatches.length === 0 && mediumMatches.length === 0 && lowMatches.length === 0) {
|
|
871
|
+
return '';
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
let html = `
|
|
875
|
+
<div class="risks-section">
|
|
876
|
+
<h3>⚠️ ${lang === 'fr' ? 'Risques Identifiés' : 'Identified Risks'}</h3>
|
|
877
|
+
`;
|
|
878
|
+
|
|
879
|
+
criticalMatches.forEach(risk => {
|
|
880
|
+
html += `<div class="risk-item critical"><span class="risk-icon">🔴</span><span>${risk.replace(/🔴\s*/, '')}</span></div>`;
|
|
881
|
+
});
|
|
882
|
+
|
|
883
|
+
mediumMatches.forEach(risk => {
|
|
884
|
+
html += `<div class="risk-item medium"><span class="risk-icon">🟡</span><span>${risk.replace(/🟡\s*/, '')}</span></div>`;
|
|
885
|
+
});
|
|
886
|
+
|
|
887
|
+
lowMatches.forEach(risk => {
|
|
888
|
+
html += `<div class="risk-item low"><span class="risk-icon">🟢</span><span>${risk.replace(/🟢\s*/, '')}</span></div>`;
|
|
889
|
+
});
|
|
890
|
+
|
|
891
|
+
html += `</div>`;
|
|
892
|
+
return html;
|
|
893
|
+
|
|
640
894
|
}
|