Tobias Brugger commited on
Commit
35396c2
·
1 Parent(s): 62c4986

formatting + some styling

Browse files
Files changed (1) hide show
  1. src/lib/DatasetViewer.svelte +207 -75
src/lib/DatasetViewer.svelte CHANGED
@@ -1,34 +1,35 @@
1
  <script lang="ts">
2
- import { fetchAllRows } from './huggingfaceApi';
3
- import type { TermDefinition, EquivalencyScore } from './types';
4
- import { marked } from 'marked';
5
 
6
  // Available jurisdictions
7
  const availableJurisdictions = [
8
- { code: 'en-us', label: 'English (US)' },
9
- { code: 'sv-se', label: 'Swedish (SE)' }
10
  ];
11
 
12
  // Hardcoded configuration
13
- const dataset = import.meta.env.VITE_HUGGINGFACE_ORG + '/structured-answers';
14
- const scoresDataset = import.meta.env.VITE_HUGGINGFACE_ORG + '/equivalency-scores';
15
-
 
16
  // Direct translations dictionary (English -> Swedish)
17
  const directTranslations: Record<string, string> = {
18
  "proximate cause": "adekvat kausalitet",
19
  "articles of association": "bolagsordning",
20
- "bylaws": "bolagsordning",
21
- "condominium": "bostadrätt",
22
  "cooperative apartment": "bostadrätt",
23
  "company name": "firma",
24
- "company": "firma",
25
  "dispose of": "förfoga över",
26
  "rights of first refusal": "hembud",
27
  "implied consent": "konkludent handlande",
28
  "aiding and abetting": "medverkande",
29
  "trading prohibition": "näringsförbud",
30
  "affiliated company": "närstående bolag",
31
- "molestation": "ofredande",
32
  "dismiss on the merits": "ogilla",
33
  "the rule of contra proferentem": "oklarhetsregeln",
34
  "joinder of parties": "processgemenskap",
@@ -42,32 +43,33 @@
42
  translationMap.set(en.toLowerCase(), sv.toLowerCase());
43
  translationMap.set(sv.toLowerCase(), en.toLowerCase());
44
  });
45
-
46
  // Form inputs
47
- let jurisdiction1 = $state('en-us');
48
- let jurisdiction2 = $state('sv-se');
49
-
50
  // Data state
51
  let data1 = $state<TermDefinition[]>([]);
52
  let data2 = $state<TermDefinition[]>([]);
53
  let equivalencyScores = $state<EquivalencyScore[]>([]);
54
  let selectedTerm1 = $state<string | null>(null);
55
  let selectedTerm2 = $state<string | null>(null);
56
-
57
  // UI state
58
  let loading = $state(false);
59
- let error = $state('');
60
  let hasLoaded = $state(false);
 
61
 
62
  // Track previous values to detect changes
63
- let prevJurisdiction1 = $state('en-us');
64
- let prevJurisdiction2 = $state('sv-se');
65
 
66
  // Reload when jurisdictions change (but not on initial mount)
67
  $effect(() => {
68
  const j1 = jurisdiction1;
69
  const j2 = jurisdiction2;
70
-
71
  if (hasLoaded && (j1 !== prevJurisdiction1 || j2 !== prevJurisdiction2)) {
72
  prevJurisdiction1 = j1;
73
  prevJurisdiction2 = j2;
@@ -77,12 +79,12 @@
77
 
78
  async function loadComparison() {
79
  if (jurisdiction1 === jurisdiction2) {
80
- error = 'Please select two different jurisdictions';
81
  return;
82
  }
83
 
84
  loading = true;
85
- error = '';
86
  data1 = [];
87
  data2 = [];
88
  equivalencyScores = [];
@@ -97,50 +99,56 @@
97
 
98
  // Fetch both jurisdictions and equivalency scores in parallel
99
  const [rows1, rows2, scoresRows] = await Promise.all([
100
- fetchAllRows(dataset, jurisdiction1, 'train', undefined),
101
- fetchAllRows(dataset, jurisdiction2, 'train', undefined),
102
- fetchAllRows(scoresDataset, scoresConfig, 'train', undefined)
103
  ]);
104
 
105
- data1 = (rows1 as TermDefinition[]).sort((a, b) => a.term.localeCompare(b.term));
106
- data2 = (rows2 as TermDefinition[]).sort((a, b) => a.term.localeCompare(b.term));
 
 
 
 
107
  equivalencyScores = scoresRows as EquivalencyScore[];
108
-
109
- console.log('Jurisdiction 1 data:', data1);
110
- console.log('Jurisdiction 2 data:', data2);
111
- console.log('Equivalency scores:', equivalencyScores);
112
-
113
  hasLoaded = true;
114
 
115
  // Auto-select first term if available
116
  if (data1.length > 0) selectedTerm1 = data1[0].term;
117
  if (data2.length > 0) selectedTerm2 = data2[0].term;
118
-
119
  } catch (e) {
120
- error = e instanceof Error ? e.message : 'Failed to load jurisdictions';
121
- console.error('Jurisdiction loading error:', e);
122
  } finally {
123
  loading = false;
124
  }
125
  }
126
 
127
  function getSelectedDefinition1(): string {
128
- if (!selectedTerm1) return '';
129
- const found = data1.find(item => item.term === selectedTerm1);
130
- return found?.definition || '';
131
  }
132
 
133
  function getSelectedDefinition2(): string {
134
- if (!selectedTerm2) return '';
135
- const found = data2.find(item => item.term === selectedTerm2);
136
- return found?.definition || '';
137
  }
138
 
139
  function normalizeTermForComparison(term: string): string {
140
  // Replace underscores with spaces and normalize whitespace for comparison
141
- return term.replace(/_/g, ' ').replace(/\s+/g, ' ').trim().toLowerCase();
 
 
 
 
142
  }
143
-
144
  // Get direct translation equivalent for a term
145
  function getDirectTranslation(term: string): string | null {
146
  const normalized = normalizeTermForComparison(term);
@@ -148,12 +156,17 @@
148
  }
149
 
150
  // Check if a term should be highlighted based on the selected term in the other jurisdiction
151
- function shouldHighlightTerm(term: string, selectedInOtherJurisdiction: string | null): boolean {
 
 
 
152
  if (!selectedInOtherJurisdiction) return false;
153
-
154
- const equivalentOfSelected = getDirectTranslation(selectedInOtherJurisdiction);
 
 
155
  if (!equivalentOfSelected) return false;
156
-
157
  const normalizedTerm = normalizeTermForComparison(term);
158
  return normalizedTerm === equivalentOfSelected;
159
  }
@@ -168,28 +181,47 @@
168
  const normalizedTerm2 = normalizeTermForComparison(selectedTerm2);
169
 
170
  // Look for the score in both directions, comparing normalized terms
171
- const score = equivalencyScores.find(s => {
172
  const normalizedJ1 = normalizeTermForComparison(s.term_j1);
173
  const normalizedJ2 = normalizeTermForComparison(s.term_j2);
174
-
175
  return (
176
- (normalizedJ1 === normalizedTerm1 && normalizedJ2 === normalizedTerm2) ||
 
177
  (normalizedJ1 === normalizedTerm2 && normalizedJ2 === normalizedTerm1)
178
  );
179
  });
180
 
181
  return score || null;
182
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
183
  </script>
184
 
185
  <div class="jurisdiction-comparison">
186
  <h2>Jurisdiction Term Comparison</h2>
187
-
188
  <div class="form">
189
  <div class="form-row">
190
  <div class="form-group">
191
  <label for="jurisdiction1">Jurisdiction 1:</label>
192
- <select id="jurisdiction1" bind:value={jurisdiction1} disabled={loading}>
 
 
 
 
193
  {#each availableJurisdictions as jur}
194
  <option value={jur.code}>{jur.label}</option>
195
  {/each}
@@ -198,7 +230,11 @@
198
 
199
  <div class="form-group">
200
  <label for="jurisdiction2">Jurisdiction 2:</label>
201
- <select id="jurisdiction2" bind:value={jurisdiction2} disabled={loading}>
 
 
 
 
202
  {#each availableJurisdictions as jur}
203
  <option value={jur.code}>{jur.label}</option>
204
  {/each}
@@ -208,14 +244,15 @@
208
 
209
  {#if !hasLoaded}
210
  <button onclick={loadComparison} disabled={loading}>
211
- {loading ? 'Loading...' : 'Load Comparison'}
212
  </button>
213
  {/if}
214
  </div>
215
 
216
  {#if error}
217
  <div class="error">
218
- <strong>Error:</strong> {error}
 
219
  </div>
220
  {/if}
221
 
@@ -223,17 +260,24 @@
223
  <div class="comparison-container">
224
  <!-- Jurisdiction 1 -->
225
  <div class="jurisdiction-column">
226
- <h3>{availableJurisdictions.find(j => j.code === jurisdiction1)?.label}</h3>
 
 
227
  <div class="term-selector">
228
  <label for="term1">Select Term:</label>
229
  <select id="term1" bind:value={selectedTerm1}>
230
  {#each data1 as item}
231
  <option value={item.term}>
232
- {shouldHighlightTerm(item.term, selectedTerm2) ? '★ ' : ''}{item.term}
 
 
233
  </option>
234
  {/each}
235
  </select>
236
- <small class="translation-hint">★ = Direct translation of the term selected in the opposite jurisdiction</small>
 
 
 
237
  </div>
238
  {#if selectedTerm1}
239
  <div class="definition-box">
@@ -250,17 +294,24 @@
250
 
251
  <!-- Jurisdiction 2 -->
252
  <div class="jurisdiction-column">
253
- <h3>{availableJurisdictions.find(j => j.code === jurisdiction2)?.label}</h3>
 
 
254
  <div class="term-selector">
255
  <label for="term2">Select Term:</label>
256
  <select id="term2" bind:value={selectedTerm2}>
257
  {#each data2 as item}
258
  <option value={item.term}>
259
- {shouldHighlightTerm(item.term, selectedTerm1) ? '★ ' : ''}{item.term}
 
 
260
  </option>
261
  {/each}
262
  </select>
263
- <small class="translation-hint">★ = Direct translation of the term selected in the opposite jurisdiction</small>
 
 
 
264
  </div>
265
  {#if selectedTerm2}
266
  <div class="definition-box">
@@ -287,33 +338,72 @@
287
  </div>
288
  {#if score.comparative_law_note}
289
  <div class="comparative-note">
290
- <h4>Comparative Law Note</h4>
291
  <div class="markdown-content">
292
  {@html marked(score.comparative_law_note)}
293
  </div>
294
  </div>
295
  {/if}
296
-
297
  {#if score.comparisons && score.comparisons.length > 0}
 
 
298
  <div class="comparisons-section">
299
- <h4>Detailed Comparisons</h4>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
300
  <div class="comparisons-list">
301
- {#each score.comparisons as comparison}
302
  <div class="comparison-item">
303
  <div class="comparison-header">
304
- <span class="comparison-category">{comparison.category}</span>
 
 
305
  {#if comparison.subcategory}
306
- <span class="comparison-subcategory"> › {comparison.subcategory}</span>
 
 
307
  {/if}
308
- <span class="comparison-score">{comparison.similarity_score.toFixed(2)}</span>
 
 
309
  </div>
310
  <div class="comparison-meta">
311
- <span class="comparison-weight">Weight: {comparison.weight.toFixed(2)}</span>
312
- <span class="comparison-weighted-score">Weighted Score: {comparison.weighted_similarity_score.toFixed(2)}</span>
 
 
 
 
 
 
313
  </div>
314
  {#if comparison.reasoning}
315
  <div class="comparison-reasoning">
316
- <strong>Reasoning:</strong> {comparison.reasoning}
317
  </div>
318
  {/if}
319
  </div>
@@ -383,7 +473,8 @@
383
  color: #555;
384
  }
385
 
386
- input, select {
 
387
  width: 100%;
388
  padding: 0.5rem;
389
  border: 1px solid #ddd;
@@ -394,7 +485,8 @@
394
  color: #333;
395
  }
396
 
397
- input:disabled, select:disabled {
 
398
  background: #e9e9e9;
399
  cursor: not-allowed;
400
  }
@@ -658,7 +750,7 @@
658
  }
659
 
660
  .comparison-subcategory {
661
- color: #666;
662
  font-size: 0.9rem;
663
  }
664
 
@@ -696,9 +788,49 @@
696
  font-size: 0.9rem;
697
  line-height: 1.5;
698
  color: #444;
 
699
  }
700
 
701
  .comparison-reasoning strong {
702
  color: #333;
703
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
704
  </style>
 
1
  <script lang="ts">
2
+ import { fetchAllRows } from "./huggingfaceApi";
3
+ import type { TermDefinition, EquivalencyScore } from "./types";
4
+ import { marked } from "marked";
5
 
6
  // Available jurisdictions
7
  const availableJurisdictions = [
8
+ { code: "en-us", label: "English (US)" },
9
+ { code: "sv-se", label: "Swedish (SE)" },
10
  ];
11
 
12
  // Hardcoded configuration
13
+ const dataset = import.meta.env.VITE_HUGGINGFACE_ORG + "/structured-answers";
14
+ const scoresDataset =
15
+ import.meta.env.VITE_HUGGINGFACE_ORG + "/equivalency-scores";
16
+
17
  // Direct translations dictionary (English -> Swedish)
18
  const directTranslations: Record<string, string> = {
19
  "proximate cause": "adekvat kausalitet",
20
  "articles of association": "bolagsordning",
21
+ bylaws: "bolagsordning",
22
+ condominium: "bostadrätt",
23
  "cooperative apartment": "bostadrätt",
24
  "company name": "firma",
25
+ company: "firma",
26
  "dispose of": "förfoga över",
27
  "rights of first refusal": "hembud",
28
  "implied consent": "konkludent handlande",
29
  "aiding and abetting": "medverkande",
30
  "trading prohibition": "näringsförbud",
31
  "affiliated company": "närstående bolag",
32
+ molestation: "ofredande",
33
  "dismiss on the merits": "ogilla",
34
  "the rule of contra proferentem": "oklarhetsregeln",
35
  "joinder of parties": "processgemenskap",
 
43
  translationMap.set(en.toLowerCase(), sv.toLowerCase());
44
  translationMap.set(sv.toLowerCase(), en.toLowerCase());
45
  });
46
+
47
  // Form inputs
48
+ let jurisdiction1 = $state("en-us");
49
+ let jurisdiction2 = $state("sv-se");
50
+
51
  // Data state
52
  let data1 = $state<TermDefinition[]>([]);
53
  let data2 = $state<TermDefinition[]>([]);
54
  let equivalencyScores = $state<EquivalencyScore[]>([]);
55
  let selectedTerm1 = $state<string | null>(null);
56
  let selectedTerm2 = $state<string | null>(null);
57
+
58
  // UI state
59
  let loading = $state(false);
60
+ let error = $state("");
61
  let hasLoaded = $state(false);
62
+ let selectedCategory = $state<string>("All");
63
 
64
  // Track previous values to detect changes
65
+ let prevJurisdiction1 = $state("en-us");
66
+ let prevJurisdiction2 = $state("sv-se");
67
 
68
  // Reload when jurisdictions change (but not on initial mount)
69
  $effect(() => {
70
  const j1 = jurisdiction1;
71
  const j2 = jurisdiction2;
72
+
73
  if (hasLoaded && (j1 !== prevJurisdiction1 || j2 !== prevJurisdiction2)) {
74
  prevJurisdiction1 = j1;
75
  prevJurisdiction2 = j2;
 
79
 
80
  async function loadComparison() {
81
  if (jurisdiction1 === jurisdiction2) {
82
+ error = "Please select two different jurisdictions";
83
  return;
84
  }
85
 
86
  loading = true;
87
+ error = "";
88
  data1 = [];
89
  data2 = [];
90
  equivalencyScores = [];
 
99
 
100
  // Fetch both jurisdictions and equivalency scores in parallel
101
  const [rows1, rows2, scoresRows] = await Promise.all([
102
+ fetchAllRows(dataset, jurisdiction1, "train", undefined),
103
+ fetchAllRows(dataset, jurisdiction2, "train", undefined),
104
+ fetchAllRows(scoresDataset, scoresConfig, "train", undefined),
105
  ]);
106
 
107
+ data1 = (rows1 as TermDefinition[]).sort((a, b) =>
108
+ a.term.localeCompare(b.term)
109
+ );
110
+ data2 = (rows2 as TermDefinition[]).sort((a, b) =>
111
+ a.term.localeCompare(b.term)
112
+ );
113
  equivalencyScores = scoresRows as EquivalencyScore[];
114
+
115
+ console.log("Jurisdiction 1 data:", data1);
116
+ console.log("Jurisdiction 2 data:", data2);
117
+ console.log("Equivalency scores:", equivalencyScores);
118
+
119
  hasLoaded = true;
120
 
121
  // Auto-select first term if available
122
  if (data1.length > 0) selectedTerm1 = data1[0].term;
123
  if (data2.length > 0) selectedTerm2 = data2[0].term;
 
124
  } catch (e) {
125
+ error = e instanceof Error ? e.message : "Failed to load jurisdictions";
126
+ console.error("Jurisdiction loading error:", e);
127
  } finally {
128
  loading = false;
129
  }
130
  }
131
 
132
  function getSelectedDefinition1(): string {
133
+ if (!selectedTerm1) return "";
134
+ const found = data1.find((item) => item.term === selectedTerm1);
135
+ return found?.definition || "";
136
  }
137
 
138
  function getSelectedDefinition2(): string {
139
+ if (!selectedTerm2) return "";
140
+ const found = data2.find((item) => item.term === selectedTerm2);
141
+ return found?.definition || "";
142
  }
143
 
144
  function normalizeTermForComparison(term: string): string {
145
  // Replace underscores with spaces and normalize whitespace for comparison
146
+ return term.replace(/_/g, " ").replace(/\s+/g, " ").trim().toLowerCase();
147
+ }
148
+ function formatCategoryName(name: string): string {
149
+ // Replace underscores with spaces for display
150
+ return name.replace(/_/g, " ");
151
  }
 
152
  // Get direct translation equivalent for a term
153
  function getDirectTranslation(term: string): string | null {
154
  const normalized = normalizeTermForComparison(term);
 
156
  }
157
 
158
  // Check if a term should be highlighted based on the selected term in the other jurisdiction
159
+ function shouldHighlightTerm(
160
+ term: string,
161
+ selectedInOtherJurisdiction: string | null
162
+ ): boolean {
163
  if (!selectedInOtherJurisdiction) return false;
164
+
165
+ const equivalentOfSelected = getDirectTranslation(
166
+ selectedInOtherJurisdiction
167
+ );
168
  if (!equivalentOfSelected) return false;
169
+
170
  const normalizedTerm = normalizeTermForComparison(term);
171
  return normalizedTerm === equivalentOfSelected;
172
  }
 
181
  const normalizedTerm2 = normalizeTermForComparison(selectedTerm2);
182
 
183
  // Look for the score in both directions, comparing normalized terms
184
+ const score = equivalencyScores.find((s) => {
185
  const normalizedJ1 = normalizeTermForComparison(s.term_j1);
186
  const normalizedJ2 = normalizeTermForComparison(s.term_j2);
187
+
188
  return (
189
+ (normalizedJ1 === normalizedTerm1 &&
190
+ normalizedJ2 === normalizedTerm2) ||
191
  (normalizedJ1 === normalizedTerm2 && normalizedJ2 === normalizedTerm1)
192
  );
193
  });
194
 
195
  return score || null;
196
  }
197
+
198
+ // Get unique categories from comparisons
199
+ function getUniqueCategories(score: EquivalencyScore | null): string[] {
200
+ if (!score || !score.comparisons) return [];
201
+ const categories = new Set(score.comparisons.map((c) => c.category));
202
+ return Array.from(categories).sort();
203
+ }
204
+
205
+ // Filter comparisons by selected category
206
+ function getFilteredComparisons(score: EquivalencyScore | null) {
207
+ if (!score || !score.comparisons) return [];
208
+ if (selectedCategory === "All") return score.comparisons;
209
+ return score.comparisons.filter((c) => c.category === selectedCategory);
210
+ }
211
  </script>
212
 
213
  <div class="jurisdiction-comparison">
214
  <h2>Jurisdiction Term Comparison</h2>
215
+
216
  <div class="form">
217
  <div class="form-row">
218
  <div class="form-group">
219
  <label for="jurisdiction1">Jurisdiction 1:</label>
220
+ <select
221
+ id="jurisdiction1"
222
+ bind:value={jurisdiction1}
223
+ disabled={loading}
224
+ >
225
  {#each availableJurisdictions as jur}
226
  <option value={jur.code}>{jur.label}</option>
227
  {/each}
 
230
 
231
  <div class="form-group">
232
  <label for="jurisdiction2">Jurisdiction 2:</label>
233
+ <select
234
+ id="jurisdiction2"
235
+ bind:value={jurisdiction2}
236
+ disabled={loading}
237
+ >
238
  {#each availableJurisdictions as jur}
239
  <option value={jur.code}>{jur.label}</option>
240
  {/each}
 
244
 
245
  {#if !hasLoaded}
246
  <button onclick={loadComparison} disabled={loading}>
247
+ {loading ? "Loading..." : "Load Comparison"}
248
  </button>
249
  {/if}
250
  </div>
251
 
252
  {#if error}
253
  <div class="error">
254
+ <strong>Error:</strong>
255
+ {error}
256
  </div>
257
  {/if}
258
 
 
260
  <div class="comparison-container">
261
  <!-- Jurisdiction 1 -->
262
  <div class="jurisdiction-column">
263
+ <h3>
264
+ {availableJurisdictions.find((j) => j.code === jurisdiction1)?.label}
265
+ </h3>
266
  <div class="term-selector">
267
  <label for="term1">Select Term:</label>
268
  <select id="term1" bind:value={selectedTerm1}>
269
  {#each data1 as item}
270
  <option value={item.term}>
271
+ {shouldHighlightTerm(item.term, selectedTerm2)
272
+ ? "★ "
273
+ : ""}{item.term}
274
  </option>
275
  {/each}
276
  </select>
277
+ <small class="translation-hint"
278
+ >★ = Direct translation of the term selected in the opposite
279
+ jurisdiction</small
280
+ >
281
  </div>
282
  {#if selectedTerm1}
283
  <div class="definition-box">
 
294
 
295
  <!-- Jurisdiction 2 -->
296
  <div class="jurisdiction-column">
297
+ <h3>
298
+ {availableJurisdictions.find((j) => j.code === jurisdiction2)?.label}
299
+ </h3>
300
  <div class="term-selector">
301
  <label for="term2">Select Term:</label>
302
  <select id="term2" bind:value={selectedTerm2}>
303
  {#each data2 as item}
304
  <option value={item.term}>
305
+ {shouldHighlightTerm(item.term, selectedTerm1)
306
+ ? "★ "
307
+ : ""}{item.term}
308
  </option>
309
  {/each}
310
  </select>
311
+ <small class="translation-hint"
312
+ >★ = Direct translation of the term selected in the opposite
313
+ jurisdiction</small
314
+ >
315
  </div>
316
  {#if selectedTerm2}
317
  <div class="definition-box">
 
338
  </div>
339
  {#if score.comparative_law_note}
340
  <div class="comparative-note">
 
341
  <div class="markdown-content">
342
  {@html marked(score.comparative_law_note)}
343
  </div>
344
  </div>
345
  {/if}
346
+
347
  {#if score.comparisons && score.comparisons.length > 0}
348
+ {@const categories = getUniqueCategories(score)}
349
+ {@const filteredComparisons = getFilteredComparisons(score)}
350
  <div class="comparisons-section">
351
+ <h3>Detailed Comparisons</h3>
352
+
353
+ <!-- Category Filter Pills -->
354
+ {#if categories.length > 0}
355
+ <div class="category-filters">
356
+ <button
357
+ class="filter-pill"
358
+ class:active={selectedCategory === "All"}
359
+ onclick={() => (selectedCategory = "All")}
360
+ >
361
+ All ({score.comparisons.length})
362
+ </button>
363
+ {#each categories as category}
364
+ {@const count = score.comparisons.filter(
365
+ (c) => c.category === category
366
+ ).length}
367
+ <button
368
+ class="filter-pill"
369
+ class:active={selectedCategory === category}
370
+ onclick={() => (selectedCategory = category)}
371
+ >
372
+ {formatCategoryName(category)} ({count})
373
+ </button>
374
+ {/each}
375
+ </div>
376
+ {/if}
377
+
378
  <div class="comparisons-list">
379
+ {#each filteredComparisons as comparison}
380
  <div class="comparison-item">
381
  <div class="comparison-header">
382
+ <span class="comparison-category">
383
+ {formatCategoryName(comparison.category)}
384
+ </span>
385
  {#if comparison.subcategory}
386
+ <span class="comparison-subcategory">
387
+ › {formatCategoryName(comparison.subcategory)}
388
+ </span>
389
  {/if}
390
+ <span class="comparison-score"
391
+ >{comparison.similarity_score.toFixed(2)}</span
392
+ >
393
  </div>
394
  <div class="comparison-meta">
395
+ <span class="comparison-weight"
396
+ >Weight: {comparison.weight.toFixed(2)}</span
397
+ >
398
+ <span class="comparison-weighted-score"
399
+ >Weighted Score: {comparison.weighted_similarity_score.toFixed(
400
+ 2
401
+ )}</span
402
+ >
403
  </div>
404
  {#if comparison.reasoning}
405
  <div class="comparison-reasoning">
406
+ {comparison.reasoning}
407
  </div>
408
  {/if}
409
  </div>
 
473
  color: #555;
474
  }
475
 
476
+ input,
477
+ select {
478
  width: 100%;
479
  padding: 0.5rem;
480
  border: 1px solid #ddd;
 
485
  color: #333;
486
  }
487
 
488
+ input:disabled,
489
+ select:disabled {
490
  background: #e9e9e9;
491
  cursor: not-allowed;
492
  }
 
750
  }
751
 
752
  .comparison-subcategory {
753
+ color: #464444;
754
  font-size: 0.9rem;
755
  }
756
 
 
788
  font-size: 0.9rem;
789
  line-height: 1.5;
790
  color: #444;
791
+ text-align: left;
792
  }
793
 
794
  .comparison-reasoning strong {
795
  color: #333;
796
  }
797
+
798
+ /* Category filter pills */
799
+ .category-filters {
800
+ display: flex;
801
+ flex-wrap: wrap;
802
+ gap: 0.5rem;
803
+ margin-bottom: 1.5rem;
804
+ padding-bottom: 1rem;
805
+ border-bottom: 1px solid #e0e0e0;
806
+ }
807
+
808
+ .filter-pill {
809
+ background: #f0f0f0;
810
+ color: #555;
811
+ border: 1px solid #ddd;
812
+ border-radius: 20px;
813
+ padding: 0.5rem 1rem;
814
+ font-size: 0.9rem;
815
+ cursor: pointer;
816
+ transition: all 0.2s;
817
+ width: auto;
818
+ }
819
+
820
+ .filter-pill:hover {
821
+ background: #e0e0e0;
822
+ border-color: #999;
823
+ }
824
+
825
+ .filter-pill.active {
826
+ background: #646cff;
827
+ color: white;
828
+ border-color: #646cff;
829
+ font-weight: 600;
830
+ }
831
+
832
+ .filter-pill.active:hover {
833
+ background: #535bf2;
834
+ border-color: #535bf2;
835
+ }
836
  </style>