Vokturz commited on
Commit
d6b92b1
·
1 Parent(s): f7c4172

Prevent example duplicates and improve responsive UI in

Browse files
src/components/pipelines/FeatureExtraction.tsx CHANGED
@@ -142,9 +142,21 @@ function FeatureExtraction() {
142
  const handleAddExample = useCallback(() => {
143
  if (!newExampleText.trim()) return
144
 
145
- addExample(newExampleText)
 
 
 
 
 
 
 
 
 
 
 
 
146
  setNewExampleText('')
147
- }, [newExampleText, addExample])
148
 
149
  const handleExtractAll = useCallback(() => {
150
  const textsToExtract = examples
@@ -167,8 +179,13 @@ function FeatureExtraction() {
167
  )
168
 
169
  const handleLoadSampleData = useCallback(() => {
170
- SAMPLE_TEXTS.forEach((text) => addExample(text))
171
- }, [addExample])
 
 
 
 
 
172
 
173
  useEffect(() => {
174
  if (!activeWorker) return
@@ -224,14 +241,16 @@ function FeatureExtraction() {
224
  const busy = status !== 'ready' || isExtracting
225
 
226
  return (
227
- <div className="flex flex-col h-full max-h-[92vh] w-full p-4">
228
- <div className="flex items-center justify-between mb-4">
229
- <h1 className="text-2xl font-bold">Feature Extraction (Embeddings)</h1>
230
- <div className="flex gap-2">
 
 
231
  <button
232
  onClick={handleLoadSampleData}
233
  disabled={!hasBeenLoaded || isExtracting}
234
- className="px-3 py-2 bg-purple-100 hover:bg-purple-200 disabled:bg-gray-100 disabled:cursor-not-allowed rounded-lg transition-colors text-sm"
235
  title="Load Sample Data"
236
  >
237
  Load Samples
@@ -259,28 +278,28 @@ function FeatureExtraction() {
259
  </div>
260
  </div>
261
 
262
- <div className="flex flex-col lg:flex-row gap-4 flex-1">
263
  {/* Left Panel - Examples */}
264
- <div className="lg:w-1/2 flex flex-col">
265
  {/* Add Example */}
266
  <div className="mb-4">
267
  <label className="block text-sm font-medium text-gray-700 mb-2">
268
  Add Text Examples:
269
  </label>
270
- <div className="flex gap-2">
271
  <textarea
272
  value={newExampleText}
273
  onChange={(e) => setNewExampleText(e.target.value)}
274
  onKeyPress={handleKeyPress}
275
  placeholder="Enter text to get embeddings... (Press Enter to add)"
276
- className="flex-1 p-3 border border-gray-300 rounded-lg resize-none focus:outline-hidden focus:ring-2 focus:ring-blue-500 focus:border-blue-500 disabled:bg-gray-100 disabled:cursor-not-allowed"
277
  rows={2}
278
  disabled={!hasBeenLoaded || isExtracting}
279
  />
280
  <button
281
  onClick={handleAddExample}
282
  disabled={!newExampleText.trim() || !hasBeenLoaded}
283
- className="px-4 py-2 bg-blue-500 hover:bg-blue-600 disabled:bg-gray-300 disabled:cursor-not-allowed text-white rounded-lg transition-colors"
284
  >
285
  <Plus className="w-4 h-4" />
286
  </button>
@@ -309,9 +328,9 @@ function FeatureExtraction() {
309
  )}
310
 
311
  {/* Examples List */}
312
- <div className="flex-1 overflow-y-auto border border-gray-300 rounded-lg bg-white">
313
- <div className="p-4">
314
- <h3 className="text-sm font-medium text-gray-700 mb-3">
315
  Examples ({examples.length})
316
  </h3>
317
  {examples.length === 0 ? (
@@ -319,11 +338,11 @@ function FeatureExtraction() {
319
  No examples added yet. Add some text above to get started.
320
  </div>
321
  ) : (
322
- <div className="space-y-2">
323
  {examples.map((example) => (
324
  <div
325
  key={example.id}
326
- className={`p-3 border rounded-lg cursor-pointer transition-colors ${
327
  selectedExample?.id === example.id
328
  ? 'border-blue-500 bg-blue-50'
329
  : 'border-gray-200 hover:border-gray-300'
@@ -377,19 +396,19 @@ function FeatureExtraction() {
377
  </div>
378
 
379
  {/* Right Panel - Visualization and Similarities */}
380
- <div className="lg:w-1/2 flex flex-col">
381
  {showVisualization && (
382
  <div className="mb-4">
383
  <h3 className="text-sm font-medium text-gray-700 mb-2">
384
  2D Visualization
385
  </h3>
386
- <div className="border border-gray-300 rounded-lg bg-white p-4">
387
  <svg
388
  ref={chartRef}
389
  width="100%"
390
- height="300"
391
  viewBox="0 0 400 300"
392
- className="border border-gray-100"
393
  >
394
  {points2D.map((point) => {
395
  const isSelected = selectedExample?.id === point.id
@@ -463,30 +482,30 @@ function FeatureExtraction() {
463
  </div>
464
  )}
465
  {points2D.length > 0 && (
466
- <div className="mt-3 p-3 bg-gray-50 rounded-lg">
467
  <h4 className="text-xs font-medium text-gray-700 mb-2">
468
  Legend:
469
  </h4>
470
- <div className="flex flex-wrap gap-3 text-xs">
471
  <div className="flex items-center gap-1">
472
- <div className="w-3 h-3 rounded-full bg-blue-500"></div>
473
- <span>Selected</span>
474
  </div>
475
  <div className="flex items-center gap-1">
476
- <div className="w-3 h-3 rounded-full bg-green-500"></div>
477
- <span>High similarity (&gt;80%)</span>
478
  </div>
479
  <div className="flex items-center gap-1">
480
- <div className="w-3 h-3 rounded-full bg-yellow-500"></div>
481
- <span>Medium similarity (50-80%)</span>
482
  </div>
483
  <div className="flex items-center gap-1">
484
- <div className="w-3 h-3 rounded-full bg-red-500"></div>
485
- <span>Low similarity (&lt;50%)</span>
486
  </div>
487
  <div className="flex items-center gap-1">
488
- <div className="w-3 h-3 rounded-full bg-gray-500"></div>
489
- <span>Not compared</span>
490
  </div>
491
  </div>
492
  </div>
@@ -496,9 +515,9 @@ function FeatureExtraction() {
496
  )}
497
 
498
  {/* Similarity Results */}
499
- <div className="flex-1 overflow-y-auto border border-gray-300 rounded-lg bg-white">
500
- <div className="p-4">
501
- <h3 className="text-sm font-medium text-gray-700 mb-3">
502
  Cosine Similarities
503
  {selectedExample &&
504
  ` (vs "${selectedExample.text.substring(0, 30)}...")`}
@@ -512,7 +531,7 @@ function FeatureExtraction() {
512
  No other examples with embeddings to compare
513
  </div>
514
  ) : (
515
- <div className="space-y-2">
516
  {similarities.map((sim) => {
517
  const example = examples.find(
518
  (ex) => ex.id === sim.exampleId
@@ -530,7 +549,7 @@ function FeatureExtraction() {
530
  return (
531
  <div
532
  key={sim.exampleId}
533
- className="p-3 border border-gray-200 rounded-lg hover:bg-gray-50 transition-colors"
534
  >
535
  <div className="flex justify-between items-start">
536
  <div className="flex-1 min-w-0">
 
142
  const handleAddExample = useCallback(() => {
143
  if (!newExampleText.trim()) return
144
 
145
+ // Check for duplicates
146
+ const trimmedText = newExampleText.trim()
147
+ const isDuplicate = examples.some(
148
+ (example) => example.text.toLowerCase() === trimmedText.toLowerCase()
149
+ )
150
+
151
+ if (isDuplicate) {
152
+ // Optionally show a toast or alert here
153
+ setNewExampleText('')
154
+ return
155
+ }
156
+
157
+ addExample(trimmedText)
158
  setNewExampleText('')
159
+ }, [newExampleText, addExample, examples])
160
 
161
  const handleExtractAll = useCallback(() => {
162
  const textsToExtract = examples
 
179
  )
180
 
181
  const handleLoadSampleData = useCallback(() => {
182
+ const existingTexts = new Set(examples.map((ex) => ex.text.toLowerCase()))
183
+ SAMPLE_TEXTS.forEach((text) => {
184
+ if (!existingTexts.has(text.toLowerCase())) {
185
+ addExample(text)
186
+ }
187
+ })
188
+ }, [addExample, examples])
189
 
190
  useEffect(() => {
191
  if (!activeWorker) return
 
241
  const busy = status !== 'ready' || isExtracting
242
 
243
  return (
244
+ <div className="flex flex-col h-full max-h-[88vh] w-full p-4">
245
+ <div className="flex flex-col sm:flex-row sm:items-center sm:justify-between mb-4 gap-2">
246
+ <h1 className="text-xl sm:text-2xl font-bold">
247
+ Feature Extraction (Embeddings)
248
+ </h1>
249
+ <div className="flex flex-nowrap gap-2">
250
  <button
251
  onClick={handleLoadSampleData}
252
  disabled={!hasBeenLoaded || isExtracting}
253
+ className="px-3 py-2 bg-purple-100 hover:bg-purple-200 disabled:bg-gray-100 disabled:cursor-not-allowed rounded-lg transition-colors text-xs sm:text-sm"
254
  title="Load Sample Data"
255
  >
256
  Load Samples
 
278
  </div>
279
  </div>
280
 
281
+ <div className="flex flex-col lg:flex-row gap-4 flex-1 min-h-0">
282
  {/* Left Panel - Examples */}
283
+ <div className="lg:w-1/2 flex flex-col min-h-0">
284
  {/* Add Example */}
285
  <div className="mb-4">
286
  <label className="block text-sm font-medium text-gray-700 mb-2">
287
  Add Text Examples:
288
  </label>
289
+ <div className="flex flex-col sm:flex-row gap-2">
290
  <textarea
291
  value={newExampleText}
292
  onChange={(e) => setNewExampleText(e.target.value)}
293
  onKeyPress={handleKeyPress}
294
  placeholder="Enter text to get embeddings... (Press Enter to add)"
295
+ className="flex-1 p-3 border border-gray-300 rounded-lg resize-none focus:outline-hidden focus:ring-2 focus:ring-blue-500 focus:border-blue-500 disabled:bg-gray-100 disabled:cursor-not-allowed text-sm"
296
  rows={2}
297
  disabled={!hasBeenLoaded || isExtracting}
298
  />
299
  <button
300
  onClick={handleAddExample}
301
  disabled={!newExampleText.trim() || !hasBeenLoaded}
302
+ className="px-4 py-2 bg-blue-500 hover:bg-blue-600 disabled:bg-gray-300 disabled:cursor-not-allowed text-white rounded-lg transition-colors self-start sm:self-stretch"
303
  >
304
  <Plus className="w-4 h-4" />
305
  </button>
 
328
  )}
329
 
330
  {/* Examples List */}
331
+ <div className="flex-1 overflow-y-auto border border-gray-300 rounded-lg bg-white min-h-0 max-h-[35vh] sm:max-h-[40vh] lg:max-h-none">
332
+ <div className="p-4 h-full">
333
+ <h3 className="text-sm font-medium text-gray-700 mb-3 sticky top-0 bg-white z-10">
334
  Examples ({examples.length})
335
  </h3>
336
  {examples.length === 0 ? (
 
338
  No examples added yet. Add some text above to get started.
339
  </div>
340
  ) : (
341
+ <div className="space-y-2 overflow-y-auto max-h-[calc(100%-3rem)]">
342
  {examples.map((example) => (
343
  <div
344
  key={example.id}
345
+ className={`p-2 sm:p-3 border rounded-lg cursor-pointer transition-colors ${
346
  selectedExample?.id === example.id
347
  ? 'border-blue-500 bg-blue-50'
348
  : 'border-gray-200 hover:border-gray-300'
 
396
  </div>
397
 
398
  {/* Right Panel - Visualization and Similarities */}
399
+ <div className="lg:w-1/2 flex flex-col min-h-0">
400
  {showVisualization && (
401
  <div className="mb-4">
402
  <h3 className="text-sm font-medium text-gray-700 mb-2">
403
  2D Visualization
404
  </h3>
405
+ <div className="border border-gray-300 rounded-lg bg-white p-2 sm:p-4">
406
  <svg
407
  ref={chartRef}
408
  width="100%"
409
+ height="250"
410
  viewBox="0 0 400 300"
411
+ className="border border-gray-100 sm:h-[300px]"
412
  >
413
  {points2D.map((point) => {
414
  const isSelected = selectedExample?.id === point.id
 
482
  </div>
483
  )}
484
  {points2D.length > 0 && (
485
+ <div className="mt-3 p-2 sm:p-3 bg-gray-50 rounded-lg">
486
  <h4 className="text-xs font-medium text-gray-700 mb-2">
487
  Legend:
488
  </h4>
489
+ <div className="flex flex-wrap gap-2 sm:gap-3 text-xs">
490
  <div className="flex items-center gap-1">
491
+ <div className="w-2 h-2 sm:w-3 sm:h-3 rounded-full bg-blue-500"></div>
492
+ <span className="text-xs">Selected</span>
493
  </div>
494
  <div className="flex items-center gap-1">
495
+ <div className="w-2 h-2 sm:w-3 sm:h-3 rounded-full bg-green-500"></div>
496
+ <span className="text-xs">High (&gt;80%)</span>
497
  </div>
498
  <div className="flex items-center gap-1">
499
+ <div className="w-2 h-2 sm:w-3 sm:h-3 rounded-full bg-yellow-500"></div>
500
+ <span className="text-xs">Med (50-80%)</span>
501
  </div>
502
  <div className="flex items-center gap-1">
503
+ <div className="w-2 h-2 sm:w-3 sm:h-3 rounded-full bg-red-500"></div>
504
+ <span className="text-xs">Low (&lt;50%)</span>
505
  </div>
506
  <div className="flex items-center gap-1">
507
+ <div className="w-2 h-2 sm:w-3 sm:h-3 rounded-full bg-gray-500"></div>
508
+ <span className="text-xs">Not compared</span>
509
  </div>
510
  </div>
511
  </div>
 
515
  )}
516
 
517
  {/* Similarity Results */}
518
+ <div className="flex-1 overflow-y-auto border border-gray-300 rounded-lg bg-white min-h-0 max-h-[35vh] sm:max-h-[40vh] lg:max-h-none">
519
+ <div className="p-4 h-full">
520
+ <h3 className="text-sm font-medium text-gray-700 mb-3 sticky top-0 bg-white z-10">
521
  Cosine Similarities
522
  {selectedExample &&
523
  ` (vs "${selectedExample.text.substring(0, 30)}...")`}
 
531
  No other examples with embeddings to compare
532
  </div>
533
  ) : (
534
+ <div className="space-y-2 overflow-y-auto max-h-[calc(100%-3rem)]">
535
  {similarities.map((sim) => {
536
  const example = examples.find(
537
  (ex) => ex.id === sim.exampleId
 
549
  return (
550
  <div
551
  key={sim.exampleId}
552
+ className="p-2 sm:p-3 border border-gray-200 rounded-lg hover:bg-gray-50 transition-colors"
553
  >
554
  <div className="flex justify-between items-start">
555
  <div className="flex-1 min-w-0">