presidio ok v.1 button disposition
This commit is contained in:
@@ -18,7 +18,7 @@ export const AnonymizationInterface = ({
|
|||||||
const anonymizedTypes = new Set<string>();
|
const anonymizedTypes = new Set<string>();
|
||||||
|
|
||||||
// ✅ NOUVEAUX PATTERNS PRESIDIO
|
// ✅ NOUVEAUX PATTERNS PRESIDIO
|
||||||
|
|
||||||
// Noms (PERSON)
|
// Noms (PERSON)
|
||||||
if (outputText.includes("<PERSON>")) {
|
if (outputText.includes("<PERSON>")) {
|
||||||
anonymizedTypes.add("Prénoms");
|
anonymizedTypes.add("Prénoms");
|
||||||
@@ -62,7 +62,7 @@ export const AnonymizationInterface = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ✅ ANCIENS PATTERNS (pour compatibilité)
|
// ✅ ANCIENS PATTERNS (pour compatibilité)
|
||||||
|
|
||||||
// Noms (anciens patterns [Nom1], [Nom2]...)
|
// Noms (anciens patterns [Nom1], [Nom2]...)
|
||||||
if (outputText.includes("[Nom1]") || outputText.includes("[Nom")) {
|
if (outputText.includes("[Nom1]") || outputText.includes("[Nom")) {
|
||||||
anonymizedTypes.add("Prénoms");
|
anonymizedTypes.add("Prénoms");
|
||||||
@@ -76,7 +76,10 @@ export const AnonymizationInterface = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Téléphones (anciens patterns)
|
// Téléphones (anciens patterns)
|
||||||
if (outputText.includes("[Téléphone1]") || outputText.includes("[Téléphone")) {
|
if (
|
||||||
|
outputText.includes("[Téléphone1]") ||
|
||||||
|
outputText.includes("[Téléphone")
|
||||||
|
) {
|
||||||
anonymizedTypes.add("Numéros de téléphone");
|
anonymizedTypes.add("Numéros de téléphone");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,15 @@
|
|||||||
import { Upload, FileText, AlertTriangle } from "lucide-react";
|
import {
|
||||||
|
Upload,
|
||||||
|
FileText,
|
||||||
|
AlertTriangle,
|
||||||
|
Shield,
|
||||||
|
Copy,
|
||||||
|
Download,
|
||||||
|
} from "lucide-react";
|
||||||
import { SampleTextComponent } from "./SampleTextComponent";
|
import { SampleTextComponent } from "./SampleTextComponent";
|
||||||
import { SupportedDataTypes } from "./SupportedDataTypes";
|
import { SupportedDataTypes } from "./SupportedDataTypes";
|
||||||
import { AnonymizationInterface } from "./AnonymizationInterface";
|
import { AnonymizationInterface } from "./AnonymizationInterface";
|
||||||
|
import { highlightEntities } from "../utils/highlightEntities";
|
||||||
|
|
||||||
interface FileUploadComponentProps {
|
interface FileUploadComponentProps {
|
||||||
uploadedFile: File | null;
|
uploadedFile: File | null;
|
||||||
@@ -16,6 +24,8 @@ interface FileUploadComponentProps {
|
|||||||
isLoadingFile?: boolean;
|
isLoadingFile?: boolean;
|
||||||
onRestart?: () => void;
|
onRestart?: () => void;
|
||||||
outputText?: string;
|
outputText?: string;
|
||||||
|
copyToClipboard?: () => void;
|
||||||
|
downloadText?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const FileUploadComponent = ({
|
export const FileUploadComponent = ({
|
||||||
@@ -31,66 +41,156 @@ export const FileUploadComponent = ({
|
|||||||
isLoadingFile = false,
|
isLoadingFile = false,
|
||||||
onRestart,
|
onRestart,
|
||||||
outputText,
|
outputText,
|
||||||
|
copyToClipboard,
|
||||||
|
downloadText,
|
||||||
}: FileUploadComponentProps) => {
|
}: FileUploadComponentProps) => {
|
||||||
// Si un fichier est uploadé ou qu'il y a du texte d'exemple, on affiche le preview
|
// Si un fichier est uploadé ou qu'il y a du texte d'exemple, on affiche le preview
|
||||||
if (uploadedFile || (sourceText && sourceText.trim())) {
|
if (uploadedFile || (sourceText && sourceText.trim())) {
|
||||||
return (
|
return (
|
||||||
<div className="w-full flex flex-col space-y-6">
|
<div className="w-full flex flex-col space-y-6">
|
||||||
{/* Preview du document avec en-tête simple */}
|
{/* Si on a un résultat, afficher 2 blocs côte à côte */}
|
||||||
<div className="bg-white rounded-2xl border border-gray-100 overflow-hidden">
|
{outputText ? (
|
||||||
<div className="bg-orange-50 border-b border-orange-200 px-4 sm:px-6 py-4">
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||||||
<div className="flex items-center justify-between">
|
{/* Preview du document original */}
|
||||||
<div className="flex items-center space-x-3">
|
<div className="bg-white rounded-2xl border border-gray-100 overflow-hidden">
|
||||||
<div className="w-8 h-8 sm:w-10 sm:h-10 bg-orange-100 rounded-lg flex items-center justify-center">
|
<div className="bg-orange-50 border-b border-orange-200 px-4 sm:px-6 py-4">
|
||||||
<FileText className="h-4 w-4 sm:h-5 sm:w-5 text-orange-600" />
|
<div className="flex items-center justify-between">
|
||||||
</div>
|
|
||||||
<div className="min-w-0 flex-1">
|
|
||||||
{uploadedFile ? (
|
|
||||||
<p className="text-xs sm:text-sm text-orange-600 truncate">
|
|
||||||
{uploadedFile.name} •{" "}
|
|
||||||
{(uploadedFile.size / 1024).toFixed(1)} KB
|
|
||||||
</p>
|
|
||||||
) : (
|
|
||||||
<p className="text-xs sm:text-sm text-orange-600">
|
|
||||||
Demo - Exemple de texte
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="p-4 sm:p-6">
|
|
||||||
{/* Zone de texte avec limite de hauteur et scroll */}
|
|
||||||
<div className="bg-gray-50 border border-gray-200 rounded-lg p-3 sm:p-4 max-h-48 overflow-y-auto overflow-x-hidden">
|
|
||||||
{isLoadingFile ? (
|
|
||||||
<div className="flex items-center justify-center py-8">
|
|
||||||
<div className="flex items-center space-x-3">
|
<div className="flex items-center space-x-3">
|
||||||
<div className="animate-spin rounded-full h-5 w-5 sm:h-6 sm:w-6 border-b-2 border-[#f7ab6e]"></div>
|
<div className="w-8 h-8 sm:w-10 sm:h-10 bg-orange-100 rounded-lg flex items-center justify-center">
|
||||||
<span className="text-xs sm:text-sm text-gray-600">
|
<FileText className="h-4 w-4 sm:h-5 sm:w-5 text-orange-600" />
|
||||||
Chargement du fichier en cours...
|
</div>
|
||||||
</span>
|
<div className="min-w-0 flex-1">
|
||||||
|
{uploadedFile ? (
|
||||||
|
<p className="text-xs sm:text-sm text-orange-600 truncate">
|
||||||
|
{uploadedFile.name} •{" "}
|
||||||
|
{(uploadedFile.size / 1024).toFixed(1)} KB
|
||||||
|
</p>
|
||||||
|
) : (
|
||||||
|
<p className="text-xs sm:text-sm text-orange-600">
|
||||||
|
Demo - Exemple de texte
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
</div>
|
||||||
<pre className="text-xs sm:text-sm text-gray-700 whitespace-pre-wrap font-mono break-words overflow-wrap-anywhere">
|
<div className="p-1">
|
||||||
{sourceText || "Aucun contenu à afficher"}
|
<div className="bg-gray-50 border border-gray-200 rounded-lg p-3 sm:p-4 max-h-72 overflow-y-auto overflow-x-hidden">
|
||||||
</pre>
|
<pre className="text-xs sm:text-sm text-gray-700 whitespace-pre-wrap font-mono break-words overflow-wrap-anywhere">
|
||||||
)}
|
{sourceText || "Aucun contenu à afficher"}
|
||||||
|
</pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Disclaimer déplacé en dessous du texte */}
|
{/* Bloc résultat anonymisé */}
|
||||||
<div className="mt-4">
|
<div className="bg-white rounded-2xl border border-gray-100 overflow-hidden">
|
||||||
<div className="flex items-start gap-2 p-3 bg-[#f7ab6e] bg-opacity-10 border border-[#f7ab6e] border-opacity-30 rounded-lg">
|
<div className="bg-green-50 border-b border-green-200 px-4 sm:px-6 py-4">
|
||||||
<AlertTriangle className="h-4 w-4 text-[#f7ab6e] mt-0.5 flex-shrink-0" />
|
<div className="flex items-center justify-between">
|
||||||
<p className="text-[10px] sm:text-[11px] text-[#092727] leading-relaxed">
|
<div className="flex items-center space-x-3">
|
||||||
Cet outil IA peut ne pas détecter toutes les informations
|
<div className="w-8 h-8 sm:w-10 sm:h-10 bg-green-100 rounded-lg flex items-center justify-center">
|
||||||
sensibles. Vérifiez le résultat avant de le partager.
|
<Shield className="h-4 w-4 sm:h-5 sm:w-5 text-green-600" />
|
||||||
</p>
|
</div>
|
||||||
|
<div className="min-w-0 flex-1">
|
||||||
|
<p className="text-xs sm:text-sm text-green-600">
|
||||||
|
Document anonymisé
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Boutons d'action */}
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
{copyToClipboard && (
|
||||||
|
<button
|
||||||
|
onClick={copyToClipboard}
|
||||||
|
className="p-2 text-green-600 hover:text-green-700 hover:bg-green-100 rounded-lg transition-colors duration-200"
|
||||||
|
title="Copier le texte"
|
||||||
|
>
|
||||||
|
<Copy className="h-4 w-4" />
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{downloadText && (
|
||||||
|
<button
|
||||||
|
onClick={downloadText}
|
||||||
|
className="bg-green-600 hover:bg-green-700 text-white px-3 py-2 rounded-lg text-xs font-medium transition-colors duration-200 flex items-center space-x-1"
|
||||||
|
title="Télécharger le fichier"
|
||||||
|
>
|
||||||
|
<Download className="h-3 w-3" />
|
||||||
|
<span className="hidden sm:inline">Télécharger</span>
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="p-1">
|
||||||
|
<div className="bg-gray-50 border border-gray-200 rounded-lg p-3 sm:p-4 max-h-72 overflow-y-auto overflow-x-hidden">
|
||||||
|
<div className="text-xs sm:text-sm text-gray-700 whitespace-pre-wrap break-words overflow-wrap-anywhere leading-relaxed">
|
||||||
|
{highlightEntities(
|
||||||
|
outputText || "Aucun contenu à afficher"
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
) : (
|
||||||
|
/* Preview normal quand pas de résultat */
|
||||||
|
<div className="bg-white rounded-2xl border border-gray-100 overflow-hidden">
|
||||||
|
<div className="bg-orange-50 border-b border-orange-200 px-4 sm:px-6 py-4">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div className="flex items-center space-x-3">
|
||||||
|
<div className="w-8 h-8 sm:w-10 sm:h-10 bg-orange-100 rounded-lg flex items-center justify-center">
|
||||||
|
<FileText className="h-4 w-4 sm:h-5 sm:w-5 text-orange-600" />
|
||||||
|
</div>
|
||||||
|
<div className="min-w-0 flex-1">
|
||||||
|
{uploadedFile ? (
|
||||||
|
<p className="text-xs sm:text-sm text-orange-600 truncate">
|
||||||
|
{uploadedFile.name} •{" "}
|
||||||
|
{(uploadedFile.size / 1024).toFixed(1)} KB
|
||||||
|
</p>
|
||||||
|
) : (
|
||||||
|
<p className="text-xs sm:text-sm text-orange-600">
|
||||||
|
Demo - Exemple de texte
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="p-2 ">
|
||||||
|
{/* Zone de texte avec limite de hauteur et scroll */}
|
||||||
|
<div className="bg-gray-50 border border-gray-200 rounded-lg p-3 sm:p-4 max-h-48 overflow-y-auto overflow-x-hidden">
|
||||||
|
{isLoadingFile ? (
|
||||||
|
<div className="flex items-center justify-center py-8">
|
||||||
|
<div className="flex items-center space-x-3">
|
||||||
|
<div className="animate-spin rounded-full h-5 w-5 sm:h-6 sm:w-6 border-b-2 border-[#f7ab6e]"></div>
|
||||||
|
<span className="text-xs sm:text-sm text-gray-600">
|
||||||
|
Chargement du fichier en cours...
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<pre className="text-xs sm:text-sm text-gray-700 whitespace-pre-wrap font-mono break-words overflow-wrap-anywhere">
|
||||||
|
{sourceText || "Aucun contenu à afficher"}
|
||||||
|
</pre>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Disclaimer déplacé en dessous du texte */}
|
||||||
|
<div className="mt-4">
|
||||||
|
<div className="flex items-start gap-2 p-3 bg-[#f7ab6e] bg-opacity-10 border border-[#f7ab6e] border-opacity-30 rounded-lg">
|
||||||
|
<AlertTriangle className="h-4 w-4 text-[#f7ab6e] mt-0.5 flex-shrink-0" />
|
||||||
|
<p className="text-[10px] sm:text-[11px] text-[#092727] leading-relaxed">
|
||||||
|
Cet outil IA peut ne pas détecter toutes les informations
|
||||||
|
sensibles. Vérifiez le résultat avant de le partager.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Boutons d'action - Responsive mobile */}
|
{/* Boutons d'action - Responsive mobile */}
|
||||||
{canAnonymize && !isLoadingFile && (
|
{canAnonymize && !isLoadingFile && (
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { Check, Upload, Eye, Shield } from "lucide-react";
|
import { Check, Upload, Eye, Shield } from "lucide-react";
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
interface ProgressBarProps {
|
interface ProgressBarProps {
|
||||||
currentStep: number;
|
currentStep: number;
|
||||||
@@ -27,15 +28,15 @@ export const ProgressBar = ({ currentStep, steps }: ProgressBarProps) => {
|
|||||||
return (
|
return (
|
||||||
<div className="w-full max-w-2xl mx-auto mb-4 px-2">
|
<div className="w-full max-w-2xl mx-auto mb-4 px-2">
|
||||||
<div className="flex items-center justify-center">
|
<div className="flex items-center justify-center">
|
||||||
<div className="flex items-center justify-between w-full max-w-xs sm:max-w-md">
|
<div className="flex items-center w-full max-w-md">
|
||||||
{steps.map((step, index) => {
|
{steps.map((step, index) => {
|
||||||
const stepNumber = index + 1;
|
const stepNumber = index + 1;
|
||||||
const isCompleted = stepNumber < currentStep;
|
const isCompleted = stepNumber < currentStep;
|
||||||
const isCurrent = stepNumber === currentStep;
|
const isCurrent = stepNumber === currentStep;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={index} className="flex items-center">
|
<React.Fragment key={index}>
|
||||||
{/* Step Circle */}
|
{/* Step Circle and Label */}
|
||||||
<div className="flex flex-col items-center">
|
<div className="flex flex-col items-center">
|
||||||
<div
|
<div
|
||||||
className={`w-5 h-5 sm:w-6 sm:h-6 rounded-full flex items-center justify-center font-medium text-xs transition-all duration-300 ${
|
className={`w-5 h-5 sm:w-6 sm:h-6 rounded-full flex items-center justify-center font-medium text-xs transition-all duration-300 ${
|
||||||
@@ -55,8 +56,8 @@ export const ProgressBar = ({ currentStep, steps }: ProgressBarProps) => {
|
|||||||
: "text-gray-500"
|
: "text-gray-500"
|
||||||
}`}
|
}`}
|
||||||
style={{
|
style={{
|
||||||
wordBreak: 'break-word',
|
wordBreak: "break-word",
|
||||||
hyphens: 'auto'
|
hyphens: "auto",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{step === "Anonymisation" ? (
|
{step === "Anonymisation" ? (
|
||||||
@@ -72,9 +73,9 @@ export const ProgressBar = ({ currentStep, steps }: ProgressBarProps) => {
|
|||||||
|
|
||||||
{/* Connector Line */}
|
{/* Connector Line */}
|
||||||
{index < steps.length - 1 && (
|
{index < steps.length - 1 && (
|
||||||
<div className="flex-1 mx-2 sm:mx-4">
|
<div className="flex-1 flex items-center justify-center px-2 sm:px-4">
|
||||||
<div
|
<div
|
||||||
className={`h-0.5 w-6 sm:w-12 transition-all duration-300 ${
|
className={`h-0.5 w-full max-w-[40px] sm:max-w-[60px] transition-all duration-300 ${
|
||||||
stepNumber < currentStep
|
stepNumber < currentStep
|
||||||
? "bg-[#f7ab6e]"
|
? "bg-[#f7ab6e]"
|
||||||
: "bg-gray-200"
|
: "bg-gray-200"
|
||||||
@@ -82,7 +83,7 @@ export const ProgressBar = ({ currentStep, steps }: ProgressBarProps) => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</React.Fragment>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
33
app/page.tsx
33
app/page.tsx
@@ -2,13 +2,12 @@
|
|||||||
|
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { FileUploadComponent } from "./components/FileUploadComponent";
|
import { FileUploadComponent } from "./components/FileUploadComponent";
|
||||||
import { ResultPreviewComponent } from "./components/ResultPreviewComponent";
|
|
||||||
import { EntityMappingTable } from "./components/EntityMappingTable";
|
import { EntityMappingTable } from "./components/EntityMappingTable";
|
||||||
import { ProgressBar } from "./components/ProgressBar";
|
import { ProgressBar } from "./components/ProgressBar";
|
||||||
import { useFileHandler } from "./components/FileHandler";
|
import { useFileHandler } from "./components/FileHandler";
|
||||||
import { useAnonymization } from "./components/AnonymizationLogic";
|
import { useAnonymization } from "./components/AnonymizationLogic";
|
||||||
import { useDownloadActions } from "./components/DownloadActions";
|
import { useDownloadActions } from "./components/DownloadActions";
|
||||||
import { highlightEntities } from "./utils/highlightEntities";
|
|
||||||
|
|
||||||
interface EntityMapping {
|
interface EntityMapping {
|
||||||
originalValue: string;
|
originalValue: string;
|
||||||
@@ -81,13 +80,13 @@ export default function Home() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Main Content */}
|
{/* Main Content */}
|
||||||
<div className="max-w-5xl mx-auto px-2 sm:px-4 py-4 sm:py-8 space-y-4">
|
<div className="max-w-6xl mx-auto px-2 sm:px-4 py-4 sm:py-8 space-y-4">
|
||||||
{/* Progress Bar */}
|
{/* Progress Bar */}
|
||||||
<ProgressBar currentStep={getCurrentStep()} steps={progressSteps} />
|
<ProgressBar currentStep={getCurrentStep()} steps={progressSteps} />
|
||||||
|
|
||||||
{/* Upload Section */}
|
{/* Upload Section */}
|
||||||
<div className="bg-white rounded-2xl border border-gray-50 overflow-hidden">
|
<div className="bg-white rounded-2xl border border-gray-50 overflow-hidden">
|
||||||
<div className="p-3 sm:p-6">
|
<div className="p-1 sm:p-3">
|
||||||
<FileUploadComponent
|
<FileUploadComponent
|
||||||
uploadedFile={uploadedFile}
|
uploadedFile={uploadedFile}
|
||||||
handleFileChange={handleFileChange}
|
handleFileChange={handleFileChange}
|
||||||
@@ -104,28 +103,16 @@ export default function Home() {
|
|||||||
isLoadingFile={isLoadingFile}
|
isLoadingFile={isLoadingFile}
|
||||||
onRestart={handleRestart}
|
onRestart={handleRestart}
|
||||||
outputText={outputText}
|
outputText={outputText}
|
||||||
|
copyToClipboard={copyToClipboard}
|
||||||
|
downloadText={downloadText}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Result Preview */}
|
{/* Entity Mapping Table - Seulement si outputText existe */}
|
||||||
{outputText && (
|
{outputText && (
|
||||||
<div className="bg-white rounded-2xl border border-gray-100 overflow-hidden">
|
<div className="bg-white rounded-2xl border border-gray-100 overflow-hidden">
|
||||||
<div className="p-3 sm:p-6">
|
<div className="p-1 sm:p-3">
|
||||||
<ResultPreviewComponent
|
|
||||||
outputText={outputText}
|
|
||||||
copyToClipboard={copyToClipboard}
|
|
||||||
downloadText={downloadText}
|
|
||||||
highlightEntities={highlightEntities}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Entity Mapping Table */}
|
|
||||||
{outputText && (
|
|
||||||
<div className="bg-white rounded-2xl border border-gray-100 overflow-hidden">
|
|
||||||
<div className="p-3 sm:p-6">
|
|
||||||
<EntityMappingTable mappings={entityMappings} />
|
<EntityMappingTable mappings={entityMappings} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -166,11 +153,9 @@ export default function Home() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : line.startsWith("-") ? (
|
) : line.startsWith("-") ? (
|
||||||
<div className="ml-4 text-blue-700">
|
<div className="ml-4 text-blue-700">{line}</div>
|
||||||
• {line.substring(1).trim()}
|
|
||||||
</div>
|
|
||||||
) : (
|
) : (
|
||||||
<div>{line}</div>
|
line
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|||||||
Reference in New Issue
Block a user