Antigravity Bot commited on
Commit
ee1fafd
·
1 Parent(s): 9bd50b9

Blur Badges

Browse files
Files changed (2) hide show
  1. app.py +18 -12
  2. public/elements/UserMessage.jsx +157 -33
app.py CHANGED
@@ -110,42 +110,36 @@ async def set_starters():
110
  return [
111
  cl.Starter(
112
  label="Example en Français",
113
- message="""Jean-Marc Durand, né le 14 avril 1987 à Lyon (69003), de nationalité française, est un ingénieur informatique marié à Sophie Martin depuis le 22 juin 2013. Il réside actuellement au 27 avenue des Lilas, appartement 42, 75019 Paris, France. Son numéro de téléphone personnel est le +33 6 48 72 19 05, son numéro de téléphone fixe est le 01 44 62 98 31, et son adresse e-mail principale est jeanmarc.durand1987@gmail.com
114
- . Il utilise également l’adresse professionnelle j.durand@techsoluce.fr
115
- .
116
  Reformule ce texte""",
117
  ),
118
 
119
  cl.Starter(
120
  label="Example en Anglais",
121
  message="""Michael Andrew Thompson, born on September 18, 1985, in Boston, Massachusetts (02118), USA, is a U.S. citizen currently living at 742 Evergreen Terrace, Apartment 5B, Brooklyn, New York, NY 11221. His mobile phone number is +1 (917) 555-3849, his home phone is +1 (718) 555-9021, and his primary email address is michael.thompson1985@gmail.com
122
- , while his work email is m.thompson@fintechcore.com
123
- . His Social Security Number is 123-45-6789, and his passport number is XK4598721, issued on March 12, 2018, expiring on March 11, 2028.
124
 
125
- etabli une mise en demeure pour la personne concerné dans ce texte
126
  """,
127
  ),
128
  cl.Starter(
129
  label="Example en Italien",
130
  message="""Marco Alessandro Bianchi, nato il 12 febbraio 1989 a Milano (MI), Italia, è un cittadino italiano residente in Via Giuseppe Verdi 18, interno 7, 00198 Roma. Il suo numero di telefono cellulare è +39 347 582 9146, il telefono fisso è 06 8392 4751, e il suo indirizzo email personale è marco.bianchi1989@gmail.com
131
- , mentre quello professionale è m.bianchi@italtechsolutions.it
132
- . Il suo codice fiscale è BNCMRC89B12F205X e il numero di passaporto è YA3487291, rilasciato il 5 maggio 2019 con scadenza il 4 maggio 2029.
133
 
134
  Extrait les information sous forme de tableau""",
135
  ),
136
  cl.Starter(
137
  label="Example en Espagnol",
138
  message="""Carlos Javier Hernández López, nacido el 7 de julio de 1986 en Madrid (28015), España, es ciudadano español y reside en Calle Alcalá 233, piso 4ºB, 28028 Madrid. Su número de teléfono móvil es +34 612 845 739, el teléfono fijo es 91 458 2391, y su correo electrónico personal es carlos.hernandez1986@gmail.com
139
- , mientras que el correo corporativo es c.hernandez@iberdata.es
140
- . Su DNI es 48739215-M, y su número de pasaporte es PA7349821, expedido el 15 de abril de 2019 y válido hasta el 14 de abril de 2029.
141
 
142
  Quelle est la personne concerné dans ce texte""",
143
  ),
144
  cl.Starter(
145
  label="Example en Allemand",
146
  message="""Thomas Michael Schneider, geboren am 3. November 1984 in Hamburg (22085), Deutschland, ist deutscher Staatsbürger und wohnhaft in der Musterstraße 47, Wohnung 12, 80336 München. Seine Mobiltelefonnummer lautet +49 176 8345 2197, die Festnetznummer ist 089 4578 9321, und seine private E-Mail-Adresse ist thomas.schneider1984@gmail.com
147
- , während er beruflich unter t.schneider@datafusion.de
148
- erreichbar ist. Seine Steuer-Identifikationsnummer lautet 12 345 678 901, und seine Reisepassnummer ist C4X982731, ausgestellt am 22. August 2018, gültig bis 21. August 2028.
149
 
150
  Resume moi le texte""",
151
  )
@@ -238,6 +232,17 @@ async def format_document(pathfile, anonymized_text):
238
 
239
  @cl.on_chat_start
240
  async def start_chat():
 
 
 
 
 
 
 
 
 
 
 
241
  return
242
 
243
  client = Groq()
@@ -422,6 +427,7 @@ async def start_message(message: cl.Message):
422
  await cl.Message("", elements=[element]).send()
423
  # await message.remove()
424
 
 
425
  cl.user_session.set("pii_map", updated_map)
426
  cl.user_session.set("reverse_pii_map", reverse_map)
427
 
 
110
  return [
111
  cl.Starter(
112
  label="Example en Français",
113
+ message="""Jean-Marc Durand, né le 14 avril 1987 à Lyon (69003), de nationalité française, est un ingénieur informatique marié à Sophie Martin depuis le 22 juin 2013. Il réside actuellement au 27 avenue des Lilas, appartement 42, 75019 Paris, France. Son numéro de téléphone personnel est le +33 6 48 72 19 05, son numéro de téléphone fixe est le 01 44 62 98 31, et son adresse e-mail principale est jeanmarc.durand1987@gmail.com. Il utilise également l’adresse professionnelle j.durand@techsoluce.fr.
 
 
114
  Reformule ce texte""",
115
  ),
116
 
117
  cl.Starter(
118
  label="Example en Anglais",
119
  message="""Michael Andrew Thompson, born on September 18, 1985, in Boston, Massachusetts (02118), USA, is a U.S. citizen currently living at 742 Evergreen Terrace, Apartment 5B, Brooklyn, New York, NY 11221. His mobile phone number is +1 (917) 555-3849, his home phone is +1 (718) 555-9021, and his primary email address is michael.thompson1985@gmail.com
120
+ , while his work email is m.thompson@fintechcore.com. His Social Security Number is 123-45-6789, and his passport number is XK4598721, issued on March 12, 2018, expiring on March 11, 2028.
 
121
 
122
+ etabli une mise en demeure pour la personne concerné dans ce texte.
123
  """,
124
  ),
125
  cl.Starter(
126
  label="Example en Italien",
127
  message="""Marco Alessandro Bianchi, nato il 12 febbraio 1989 a Milano (MI), Italia, è un cittadino italiano residente in Via Giuseppe Verdi 18, interno 7, 00198 Roma. Il suo numero di telefono cellulare è +39 347 582 9146, il telefono fisso è 06 8392 4751, e il suo indirizzo email personale è marco.bianchi1989@gmail.com
128
+ , mentre quello professionale è m.bianchi@italtechsolutions.it . Il suo codice fiscale è BNCMRC89B12F205X e il numero di passaporto è YA3487291, rilasciato il 5 maggio 2019 con scadenza il 4 maggio 2029.
 
129
 
130
  Extrait les information sous forme de tableau""",
131
  ),
132
  cl.Starter(
133
  label="Example en Espagnol",
134
  message="""Carlos Javier Hernández López, nacido el 7 de julio de 1986 en Madrid (28015), España, es ciudadano español y reside en Calle Alcalá 233, piso 4ºB, 28028 Madrid. Su número de teléfono móvil es +34 612 845 739, el teléfono fijo es 91 458 2391, y su correo electrónico personal es carlos.hernandez1986@gmail.com
135
+ , mientras que el correo corporativo es c.hernandez@iberdata.es . Su DNI es 48739215-M, y su número de pasaporte es PA7349821, expedido el 15 de abril de 2019 y válido hasta el 14 de abril de 2029.
 
136
 
137
  Quelle est la personne concerné dans ce texte""",
138
  ),
139
  cl.Starter(
140
  label="Example en Allemand",
141
  message="""Thomas Michael Schneider, geboren am 3. November 1984 in Hamburg (22085), Deutschland, ist deutscher Staatsbürger und wohnhaft in der Musterstraße 47, Wohnung 12, 80336 München. Seine Mobiltelefonnummer lautet +49 176 8345 2197, die Festnetznummer ist 089 4578 9321, und seine private E-Mail-Adresse ist thomas.schneider1984@gmail.com
142
+ , während er beruflich unter t.schneider@datafusion.de erreichbar ist. Seine Steuer-Identifikationsnummer lautet 12 345 678 901, und seine Reisepassnummer ist C4X982731, ausgestellt am 22. August 2018, gültig bis 21. August 2028.
 
143
 
144
  Resume moi le texte""",
145
  )
 
232
 
233
  @cl.on_chat_start
234
  async def start_chat():
235
+ # pii_map = cl.user_session.get("pii_map", {})
236
+ # reverse_map = cl.user_session.get("reverse_pii_map", {})
237
+ # text = """Thomas Michael Schneider, geboren am 3. November 1984 in Hamburg (22085), Deutschland, ist deutscher Staatsbürger und wohnhaft in der Musterstraße 47, Wohnung 12, 80336 München. Seine Mobiltelefonnummer lautet +49 176 8345 2197, die Festnetznummer ist 089 4578 9321, und seine private E-Mail-Adresse ist thomas.schneider1984@gmail.com
238
+ # , während er beruflich unter t.schneider@datafusion.de erreichbar ist. Seine Steuer-Identifikationsnummer lautet 12 345 678 901, und seine Reisepassnummer ist C4X982731, ausgestellt am 22. August 2018, gültig bis 21. August 2028.
239
+
240
+ # Resume moi le texte"""
241
+ # text_processed = await pii_gliner(text)
242
+ # text_sanitized, updated_map, reverse_map = pii_format(text_processed, pii_map, reverse_map)
243
+
244
+ # element = cl.CustomElement(name="UserMessage", props={"message": text_sanitized, "dic": reverse_map, "original": text, 'author':'user'})
245
+ # await cl.Message("", elements=[element]).send()
246
  return
247
 
248
  client = Groq()
 
427
  await cl.Message("", elements=[element]).send()
428
  # await message.remove()
429
 
430
+
431
  cl.user_session.set("pii_map", updated_map)
432
  cl.user_session.set("reverse_pii_map", reverse_map)
433
 
public/elements/UserMessage.jsx CHANGED
@@ -62,10 +62,122 @@ function parseTable(lines, startIndex) {
62
  }
63
 
64
  export function Markdown({ children, dic, show }) {
 
 
 
 
65
  const lines = children.split("\n")
66
  const elements = []
67
  let i = 0
68
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
69
  while (i < lines.length) {
70
  const line = lines[i]
71
 
@@ -133,7 +245,36 @@ export function Markdown({ children, dic, show }) {
133
  i++
134
  }
135
 
136
- return <div>{elements}</div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
137
  }
138
 
139
  function parseInlineText(text) {
@@ -206,14 +347,21 @@ function renderInline(text, dic, show) {
206
  ) : (
207
  <Tooltip key={index}>
208
  <TooltipTrigger asChild>
209
- <span className="bg-pii px-1 rounded cursor-pointer">
210
- {part.toUpperCase()}
211
- </span>
 
 
 
 
 
 
 
212
  </TooltipTrigger>
213
  <TooltipContent>
214
  <p>{dic[match[2]].value}</p>
215
  </TooltipContent>
216
- </Tooltip>
217
  )
218
  }
219
 
@@ -375,31 +523,7 @@ function MessageWithHighlight({ message, dic, show }) {
375
  return (
376
  <div className='flex flex-row-reverse w-full'>
377
  <div onMouseUp={handleMouseUp} className="relative bg-accent rounded-3xl px-5 py-2.5 user-message flex-grow-0">
378
- <p className="leading-[1.75] whitespace-pre-wrap">
379
- {parts.map((part, index) => {
380
- // Check if part is [something]
381
- const match = part.match(/^\[(\w+)_(.*?)\]$/);
382
-
383
- if (match && dic[match[2]]) {
384
- return (
385
- show ? <Badge key={index} className="bg-pii text-blue-800 rounded-full px-2 py-1 mx-1 cursor-pointer">
386
- {dic[match[2]].value}
387
- </Badge> :
388
- <Tooltip>
389
- <TooltipTrigger asChild>
390
- <button className=""><Badge key={index} className="bg-pii text-blue-800 rounded-sm px-1 py-1 mx-1 cursor-pointer">
391
- {part.toUpperCase()}
392
- </Badge></button>
393
- </TooltipTrigger>
394
- <TooltipContent>
395
- <p>{dic[match[2]].value}</p>
396
- </TooltipContent>
397
- </Tooltip>
398
- );
399
- }
400
- return <span key={index} data-index={index}>{part}</span>;
401
- })}
402
- </p>
403
 
404
  <DropdownMenu open={open} onOpenChange={setOpen}>
405
  <DropdownMenuTrigger asChild>
@@ -434,9 +558,9 @@ export default function UserMessage() {
434
  <div className="flex flex-col gap-2">
435
  {props.author == 'user' ? (
436
  <div className='flex flex-row-reverse w-full'>
437
- <div className="relative bg-accent rounded-3xl px-5 py-2.5 user-message flex-grow-0">
438
- <Markdown dic={props.dic} show={show}>{props.message}</Markdown>
439
- </div>
440
  </div>
441
  ) : (
442
  <Markdown dic={props.dic} show={show}>{props.message}</Markdown>
 
62
  }
63
 
64
  export function Markdown({ children, dic, show }) {
65
+ const [localMessage, setLocalMessage] = useState(children);
66
+ const [open, setOpen] = useState(false);
67
+ const [position, setPosition] = useState({ x: 0, y: 0 });
68
+ const [selectionData, setSelectionData] = useState(null);
69
  const lines = children.split("\n")
70
  const elements = []
71
  let i = 0
72
 
73
+ const handleMouseUp = () => {
74
+
75
+ const selection = window.getSelection();
76
+
77
+ // 1. Check basic validity
78
+ if (!selection || selection.rangeCount === 0 || selection.isCollapsed) {
79
+ // If just a click (collapsed), we assume menu shouldn't open or should close
80
+ // But we don't want to close immediately if clicking inside the menu, handled by DropdownMenu logic
81
+ return;
82
+ }
83
+
84
+ // 2. Expand Selection to Word Boundaries
85
+ const anchorNode = selection.anchorNode;
86
+ const focusNode = selection.focusNode;
87
+
88
+ // Only support single text node selection for simplicity to map back to data-index
89
+ if (anchorNode !== focusNode || anchorNode.nodeType !== 3) {
90
+ setOpen(false);
91
+ return;
92
+ }
93
+
94
+ const textContent = anchorNode.textContent;
95
+ let start = Math.min(selection.anchorOffset, selection.focusOffset);
96
+ let end = Math.max(selection.anchorOffset, selection.focusOffset);
97
+
98
+ // Expand left
99
+ while (start > 0 && /\S/.test(textContent[start - 1])) {
100
+ start--;
101
+ }
102
+ // Expand right
103
+ while (end < textContent.length && /\S/.test(textContent[end])) {
104
+ end++;
105
+ }
106
+
107
+ // Update visual selection
108
+ const newRange = document.createRange();
109
+ newRange.setStart(anchorNode, start);
110
+ newRange.setEnd(anchorNode, end);
111
+ selection.removeAllRanges();
112
+ selection.addRange(newRange);
113
+
114
+
115
+ const newText = textContent.slice(start, end);
116
+ if (!newText.trim()) return;
117
+
118
+ // 3. Validation Logic
119
+ // Check 1: Less than 4 words
120
+ const words = newText.split(/\s+/);
121
+ if (words.length >= 4) {
122
+ setOpen(false);
123
+ return;
124
+ }
125
+
126
+ // Check 2: Does not contain highlighted word (brackets)
127
+ if (newText.includes('[') || newText.includes(']')) {
128
+ setOpen(false);
129
+ return;
130
+ }
131
+
132
+ // Check 3: Check if selection is inside a Badge
133
+ const parentElement = anchorNode.parentElement;
134
+ if (parentElement.closest('.bg-pii')) {
135
+ setOpen(false);
136
+ return;
137
+ }
138
+
139
+ // Identify Part Index
140
+ const partIndex = parseInt(parentElement.getAttribute('data-index'), 10);
141
+ // if (isNaN(partIndex)) {
142
+ // setOpen(false);
143
+ // return;
144
+ // }
145
+
146
+ const rect = newRange.getBoundingClientRect();
147
+ setPosition({
148
+ x: rect.left,
149
+ y: rect.bottom
150
+ });
151
+ setSelectionData({
152
+ partIndex,
153
+ start,
154
+ end,
155
+ text: newText
156
+ });
157
+ setOpen(true);
158
+ };
159
+
160
+ const handleAnonymize = () => {
161
+ if (!selectionData) return;
162
+ const { partIndex, start, end, text } = selectionData;
163
+
164
+ // Split by brackets to reconstruct parts array as seen in render
165
+ const parts = localMessage.split(/(\[[^\]]+\])/g);
166
+
167
+ if (parts[partIndex] === undefined) return;
168
+
169
+ const originalPart = parts[partIndex];
170
+ const newPart = originalPart.substring(0, start) + `[ANONYME_${text}]` + originalPart.substring(end);
171
+
172
+ parts[partIndex] = newPart;
173
+ const newMessage = parts.join('');
174
+
175
+ setLocalMessage(newMessage);
176
+ setOpen(false);
177
+ // Clear selection
178
+ window.getSelection().removeAllRanges();
179
+ };
180
+
181
  while (i < lines.length) {
182
  const line = lines[i]
183
 
 
245
  i++
246
  }
247
 
248
+ return (
249
+ <div onMouseUp={handleMouseUp}>
250
+ <div className="relative bg-accent rounded-3xl px-5 py-2.5 user-message flex-grow-0">
251
+ <p className="leading-[1.75] whitespace-pre-wrap">{elements}</p>
252
+
253
+ <DropdownMenu open={open} onOpenChange={setOpen}>
254
+ <DropdownMenuTrigger asChild>
255
+ <div
256
+ style={{
257
+ position: 'fixed',
258
+ top: position.y,
259
+ left: position.x,
260
+ width: '1px',
261
+ height: '1px',
262
+ visibility: 'hidden',
263
+ pointerEvents: 'none'
264
+ }}
265
+ />
266
+ </DropdownMenuTrigger>
267
+ <DropdownMenuContent align="start">
268
+ <DropdownMenuGroup>
269
+ <DropdownMenuItem onSelect={handleAnonymize}>
270
+ Anonymiser
271
+ </DropdownMenuItem>
272
+ </DropdownMenuGroup>
273
+ </DropdownMenuContent>
274
+ </DropdownMenu>
275
+ </div>
276
+ </div>
277
+ )
278
  }
279
 
280
  function parseInlineText(text) {
 
347
  ) : (
348
  <Tooltip key={index}>
349
  <TooltipTrigger asChild>
350
+ <Badge
351
+ key={index}
352
+ className="bg-pii text-blue-800 rounded-full px-2 py-1 mx-1 blur-sm"
353
+ >
354
+ {dic[match[2]].value}
355
+ </Badge>
356
+ {/* <span className="bg-pii px-1 rounded cursor-pointer blur-sm">
357
+ {/* {part.toUpperCase()} */}
358
+ {/* {dic[match[2]].value} */}
359
+ {/* </span> */}
360
  </TooltipTrigger>
361
  <TooltipContent>
362
  <p>{dic[match[2]].value}</p>
363
  </TooltipContent>
364
+ </Tooltip >
365
  )
366
  }
367
 
 
523
  return (
524
  <div className='flex flex-row-reverse w-full'>
525
  <div onMouseUp={handleMouseUp} className="relative bg-accent rounded-3xl px-5 py-2.5 user-message flex-grow-0">
526
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
527
 
528
  <DropdownMenu open={open} onOpenChange={setOpen}>
529
  <DropdownMenuTrigger asChild>
 
558
  <div className="flex flex-col gap-2">
559
  {props.author == 'user' ? (
560
  <div className='flex flex-row-reverse w-full'>
561
+ {/* <div className="relative bg-accent rounded-3xl px-5 py-2.5 user-message flex-grow-0"> */}
562
+ <Markdown dic={props.dic} show={show}>{props.message}</Markdown>
563
+ {/* </div> */}
564
  </div>
565
  ) : (
566
  <Markdown dic={props.dic} show={show}>{props.message}</Markdown>