Spaces:
Running
Running
Mark Duppenthaler
commited on
Commit
Β·
4d38e50
1
Parent(s):
2ebc2c3
message size and alias updates
Browse files- frontend/dist/assets/{index-DNwK_9E1.js β index-BcDPtpHh.js} +0 -0
- frontend/dist/index.html +1 -1
- frontend/src/Descriptions.ts +8 -0
- frontend/src/components/DatasetSelector.tsx +1 -1
- frontend/src/components/Examples.tsx +2 -1
- frontend/src/components/LeaderboardChart.tsx +10 -3
- frontend/src/components/LeaderboardTable.tsx +11 -15
- frontend/src/components/ModelFilter.tsx +2 -1
- frontend/src/components/ModelInfoIcon.tsx +2 -0
- frontend/src/components/QualityMetricsTable.tsx +17 -4
frontend/dist/assets/{index-DNwK_9E1.js β index-BcDPtpHh.js}
RENAMED
The diff for this file is too large to render.
See raw diff
|
|
frontend/dist/index.html
CHANGED
@@ -5,7 +5,7 @@
|
|
5 |
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
6 |
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
7 |
<title>π₯ Omni Seal Bench Watermarking Leaderboard</title>
|
8 |
-
<script type="module" crossorigin src="/assets/index-
|
9 |
<link rel="stylesheet" crossorigin href="/assets/index-stFRue7K.css">
|
10 |
</head>
|
11 |
<body>
|
|
|
5 |
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
6 |
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
7 |
<title>π₯ Omni Seal Bench Watermarking Leaderboard</title>
|
8 |
+
<script type="module" crossorigin src="/assets/index-BcDPtpHh.js"></script>
|
9 |
<link rel="stylesheet" crossorigin href="/assets/index-stFRue7K.css">
|
10 |
</head>
|
11 |
<body>
|
frontend/src/Descriptions.ts
CHANGED
@@ -43,6 +43,10 @@ class Descriptions {
|
|
43 |
return this.descriptions[name]?.link
|
44 |
}
|
45 |
|
|
|
|
|
|
|
|
|
46 |
getModelDescription(name: string): string | undefined {
|
47 |
return this.modelDescriptions[name]?.description
|
48 |
}
|
@@ -59,6 +63,10 @@ class Descriptions {
|
|
59 |
return this.modelDescriptions[name]?.github_link
|
60 |
}
|
61 |
|
|
|
|
|
|
|
|
|
62 |
// Metric description methods
|
63 |
getMetricDescription(name: string): string | undefined {
|
64 |
return this.metricDescriptions[name]?.description
|
|
|
43 |
return this.descriptions[name]?.link
|
44 |
}
|
45 |
|
46 |
+
getModelAlias(name: string): string | undefined {
|
47 |
+
return this.modelDescriptions[name]?.alias
|
48 |
+
}
|
49 |
+
|
50 |
getModelDescription(name: string): string | undefined {
|
51 |
return this.modelDescriptions[name]?.description
|
52 |
}
|
|
|
63 |
return this.modelDescriptions[name]?.github_link
|
64 |
}
|
65 |
|
66 |
+
getModelMessageSize(name: string): number | undefined {
|
67 |
+
return this.modelDescriptions[name]?.message_size
|
68 |
+
}
|
69 |
+
|
70 |
// Metric description methods
|
71 |
getMetricDescription(name: string): string | undefined {
|
72 |
return this.metricDescriptions[name]?.description
|
frontend/src/components/DatasetSelector.tsx
CHANGED
@@ -26,7 +26,7 @@ const DatasetSelector: React.FC<DatasetSelectorProps> = ({
|
|
26 |
checked={selectedDatasetName === datasetName}
|
27 |
onChange={() => onDatasetNameChange(datasetName)}
|
28 |
/>
|
29 |
-
<span className="text-sm">{datasetName}</span>
|
30 |
<DatasetInfoIcon datasetName={datasetName} />
|
31 |
</label>
|
32 |
))}
|
|
|
26 |
checked={selectedDatasetName === datasetName}
|
27 |
onChange={() => onDatasetNameChange(datasetName)}
|
28 |
/>
|
29 |
+
<span className="text-sm ml-2">{datasetName}</span>
|
30 |
<DatasetInfoIcon datasetName={datasetName} />
|
31 |
</label>
|
32 |
))}
|
frontend/src/components/Examples.tsx
CHANGED
@@ -111,6 +111,7 @@ const Examples = ({ fileType }: ExamplesProps) => {
|
|
111 |
<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-1 max-h-48 overflow-y-auto pr-2">
|
112 |
{Object.keys(examples).map((model) => {
|
113 |
const fullName = descriptions.current.getModelFullName(model) || model
|
|
|
114 |
return (
|
115 |
<div key={model} className="flex items-center gap-2 text-sm relative group">
|
116 |
<label className="flex items-center gap-2 flex-grow">
|
@@ -123,7 +124,7 @@ const Examples = ({ fileType }: ExamplesProps) => {
|
|
123 |
/>
|
124 |
<div className="flex items-center">
|
125 |
<span className="truncate" title={fullName}>
|
126 |
-
{
|
127 |
</span>
|
128 |
<ModelInfoIcon modelName={model} />
|
129 |
</div>
|
|
|
111 |
<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-1 max-h-48 overflow-y-auto pr-2">
|
112 |
{Object.keys(examples).map((model) => {
|
113 |
const fullName = descriptions.current.getModelFullName(model) || model
|
114 |
+
const modelAlias = descriptions.current.getModelAlias(model) || model
|
115 |
return (
|
116 |
<div key={model} className="flex items-center gap-2 text-sm relative group">
|
117 |
<label className="flex items-center gap-2 flex-grow">
|
|
|
124 |
/>
|
125 |
<div className="flex items-center">
|
126 |
<span className="truncate" title={fullName}>
|
127 |
+
{modelAlias}
|
128 |
</span>
|
129 |
<ModelInfoIcon modelName={model} />
|
130 |
</div>
|
frontend/src/components/LeaderboardChart.tsx
CHANGED
@@ -14,6 +14,7 @@ import API from '../API'
|
|
14 |
import LoadingSpinner from './LoadingSpinner'
|
15 |
import { ContentType, TooltipProps } from 'recharts/types/component/Tooltip'
|
16 |
import { NameType, ValueType } from 'recharts/types/component/DefaultTooltipContent'
|
|
|
17 |
|
18 |
interface LeaderboardChartProps {
|
19 |
dataset: string
|
@@ -82,6 +83,10 @@ const AttackSelector = ({
|
|
82 |
)
|
83 |
}
|
84 |
|
|
|
|
|
|
|
|
|
85 |
const CustomTooltip = (
|
86 |
props: TooltipProps<ValueType, NameType> & {
|
87 |
dataset: string
|
@@ -115,9 +120,11 @@ const CustomTooltip = (
|
|
115 |
</div>
|
116 |
{payload.map((entry: any, i: number) => {
|
117 |
const model = entry.name
|
|
|
|
|
118 |
const handleClick = () => {
|
119 |
navigate(
|
120 |
-
`${route}?model=${encodeURIComponent(
|
121 |
)
|
122 |
}
|
123 |
|
@@ -285,11 +292,11 @@ const LeaderboardChart = ({ dataset, selectedModels, datasetType }: LeaderboardC
|
|
285 |
return [...models].map((model, index) => {
|
286 |
return (
|
287 |
<Line
|
288 |
-
key={model as string}
|
289 |
type="monotone"
|
290 |
dataKey={selectedMetric as string} // Ensure selectedMetric is a string
|
291 |
data={sortedChartData.filter((row) => row.model === model)}
|
292 |
-
name={model as string}
|
293 |
stroke={colors[index % colors.length]}
|
294 |
dot={false}
|
295 |
/>
|
|
|
14 |
import LoadingSpinner from './LoadingSpinner'
|
15 |
import { ContentType, TooltipProps } from 'recharts/types/component/Tooltip'
|
16 |
import { NameType, ValueType } from 'recharts/types/component/DefaultTooltipContent'
|
17 |
+
import Descriptions from '../Descriptions'
|
18 |
|
19 |
interface LeaderboardChartProps {
|
20 |
dataset: string
|
|
|
83 |
)
|
84 |
}
|
85 |
|
86 |
+
function getModelAlias(model: string): string {
|
87 |
+
return Descriptions.getInstance().getModelAlias(model) || model
|
88 |
+
}
|
89 |
+
|
90 |
const CustomTooltip = (
|
91 |
props: TooltipProps<ValueType, NameType> & {
|
92 |
dataset: string
|
|
|
120 |
</div>
|
121 |
{payload.map((entry: any, i: number) => {
|
122 |
const model = entry.name
|
123 |
+
const originalModel = entry.payload.model
|
124 |
+
|
125 |
const handleClick = () => {
|
126 |
navigate(
|
127 |
+
`${route}?model=${encodeURIComponent(originalModel)}&attack=${encodeURIComponent(selectedAttack || '')}&strength=${strength}`
|
128 |
)
|
129 |
}
|
130 |
|
|
|
292 |
return [...models].map((model, index) => {
|
293 |
return (
|
294 |
<Line
|
295 |
+
key={getModelAlias(model as string)}
|
296 |
type="monotone"
|
297 |
dataKey={selectedMetric as string} // Ensure selectedMetric is a string
|
298 |
data={sortedChartData.filter((row) => row.model === model)}
|
299 |
+
name={getModelAlias(model as string)}
|
300 |
stroke={colors[index % colors.length]}
|
301 |
dot={false}
|
302 |
/>
|
frontend/src/components/LeaderboardTable.tsx
CHANGED
@@ -17,10 +17,11 @@
|
|
17 |
*
|
18 |
* Both sorting mechanisms operate independently and can be used simultaneously.
|
19 |
*/
|
20 |
-
import React, { useEffect, useState, useMemo, useCallback } from 'react'
|
21 |
import { ArrowDownTrayIcon } from '@heroicons/react/24/solid'
|
22 |
import QualityMetricsTable from './QualityMetricsTable'
|
23 |
import MetricInfoIcon from './MetricInfoIcon'
|
|
|
24 |
import {
|
25 |
createColumnHelper,
|
26 |
flexRender,
|
@@ -122,6 +123,8 @@ const LeaderboardTable: React.FC<LeaderboardTableProps> = ({ benchmarkData, sele
|
|
122 |
const [error, setError] = useState<string | null>(null)
|
123 |
const [groupRows, setGroupRows] = useState<Groups>({})
|
124 |
const [openGroupRows, setOpenGroupRows] = useState<{ [key: string]: boolean }>({})
|
|
|
|
|
125 |
|
126 |
const [selectedMetrics, setSelectedMetrics] = useState<Set<string>>(new Set())
|
127 |
const [overallMetrics, setOverallMetrics] = useState<string[]>([])
|
@@ -138,6 +141,11 @@ const LeaderboardTable: React.FC<LeaderboardTableProps> = ({ benchmarkData, sele
|
|
138 |
return tableHeader.filter((model) => selectedModels.has(model))
|
139 |
}, [tableHeader, selectedModels])
|
140 |
|
|
|
|
|
|
|
|
|
|
|
141 |
// Parse benchmark data when it changes
|
142 |
useEffect(() => {
|
143 |
if (!benchmarkData) {
|
@@ -249,7 +257,6 @@ const LeaderboardTable: React.FC<LeaderboardTableProps> = ({ benchmarkData, sele
|
|
249 |
metric: string | null
|
250 |
) => {
|
251 |
const rowKey = getColumnSortRowKey(group, subGroup, metric)
|
252 |
-
console.log('Column sort clicked:', { group, subGroup, metric, rowKey })
|
253 |
|
254 |
// First determine the new sort direction
|
255 |
let newDirection: 'asc' | 'desc' | null = null
|
@@ -463,12 +470,10 @@ const LeaderboardTable: React.FC<LeaderboardTableProps> = ({ benchmarkData, sele
|
|
463 |
|
464 |
// Effect: update model order when columnSortState or dependencies change
|
465 |
useEffect(() => {
|
466 |
-
console.log(columnSortState)
|
467 |
if (!columnSortState) return
|
468 |
// Parse out group, subGroup, metric from rowKey
|
469 |
const [group, subGroup, metric] = columnSortState.rowKey.split('||').map((v) => v || null)
|
470 |
const newDirection = columnSortState.direction
|
471 |
-
console.log(newDirection, group, subGroup, metric)
|
472 |
if (!newDirection) return // Only run if a sort direction is present
|
473 |
|
474 |
// Update model order for all visible metrics
|
@@ -491,13 +496,6 @@ const LeaderboardTable: React.FC<LeaderboardTableProps> = ({ benchmarkData, sele
|
|
491 |
}
|
492 |
|
493 |
if (!rowToSort && !metric) {
|
494 |
-
console.log('Row to sort not found', {
|
495 |
-
group,
|
496 |
-
subGroup,
|
497 |
-
metric,
|
498 |
-
rowKey: columnSortState.rowKey,
|
499 |
-
})
|
500 |
-
|
501 |
// Try to proceed anyway with group/subgroup sorting
|
502 |
if (group) {
|
503 |
metricsToUpdate.forEach((metricName) => {
|
@@ -601,8 +599,6 @@ const LeaderboardTable: React.FC<LeaderboardTableProps> = ({ benchmarkData, sele
|
|
601 |
selectedOverallMetrics,
|
602 |
])
|
603 |
|
604 |
-
console.log(modelOrderByOverallMetric)
|
605 |
-
|
606 |
// CSV export function
|
607 |
const exportToCsv = () => {
|
608 |
// Build header row
|
@@ -612,7 +608,7 @@ const LeaderboardTable: React.FC<LeaderboardTableProps> = ({ benchmarkData, sele
|
|
612 |
.filter((metric) => selectedOverallMetrics.has(metric))
|
613 |
.flatMap((metric) => {
|
614 |
const metricModels = modelOrderByOverallMetric[metric] || models
|
615 |
-
return metricModels.map((model) => `${metric} - ${model}`)
|
616 |
}),
|
617 |
]
|
618 |
|
@@ -804,7 +800,7 @@ const LeaderboardTable: React.FC<LeaderboardTableProps> = ({ benchmarkData, sele
|
|
804 |
className="cursor-pointer select-none"
|
805 |
onClick={() => handleRowSort(metric, model)}
|
806 |
>
|
807 |
-
{model}
|
808 |
<span
|
809 |
className="ml-1 font-bold"
|
810 |
title={
|
|
|
17 |
*
|
18 |
* Both sorting mechanisms operate independently and can be used simultaneously.
|
19 |
*/
|
20 |
+
import React, { useEffect, useState, useMemo, useCallback, useRef } from 'react'
|
21 |
import { ArrowDownTrayIcon } from '@heroicons/react/24/solid'
|
22 |
import QualityMetricsTable from './QualityMetricsTable'
|
23 |
import MetricInfoIcon from './MetricInfoIcon'
|
24 |
+
import Descriptions from '../Descriptions'
|
25 |
import {
|
26 |
createColumnHelper,
|
27 |
flexRender,
|
|
|
123 |
const [error, setError] = useState<string | null>(null)
|
124 |
const [groupRows, setGroupRows] = useState<Groups>({})
|
125 |
const [openGroupRows, setOpenGroupRows] = useState<{ [key: string]: boolean }>({})
|
126 |
+
const [descriptionsLoaded, setDescriptionsLoaded] = useState(false)
|
127 |
+
const descriptions = useRef(Descriptions.getInstance())
|
128 |
|
129 |
const [selectedMetrics, setSelectedMetrics] = useState<Set<string>>(new Set())
|
130 |
const [overallMetrics, setOverallMetrics] = useState<string[]>([])
|
|
|
141 |
return tableHeader.filter((model) => selectedModels.has(model))
|
142 |
}, [tableHeader, selectedModels])
|
143 |
|
144 |
+
// Load descriptions
|
145 |
+
useEffect(() => {
|
146 |
+
descriptions.current.load().then(() => setDescriptionsLoaded(true))
|
147 |
+
}, [])
|
148 |
+
|
149 |
// Parse benchmark data when it changes
|
150 |
useEffect(() => {
|
151 |
if (!benchmarkData) {
|
|
|
257 |
metric: string | null
|
258 |
) => {
|
259 |
const rowKey = getColumnSortRowKey(group, subGroup, metric)
|
|
|
260 |
|
261 |
// First determine the new sort direction
|
262 |
let newDirection: 'asc' | 'desc' | null = null
|
|
|
470 |
|
471 |
// Effect: update model order when columnSortState or dependencies change
|
472 |
useEffect(() => {
|
|
|
473 |
if (!columnSortState) return
|
474 |
// Parse out group, subGroup, metric from rowKey
|
475 |
const [group, subGroup, metric] = columnSortState.rowKey.split('||').map((v) => v || null)
|
476 |
const newDirection = columnSortState.direction
|
|
|
477 |
if (!newDirection) return // Only run if a sort direction is present
|
478 |
|
479 |
// Update model order for all visible metrics
|
|
|
496 |
}
|
497 |
|
498 |
if (!rowToSort && !metric) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
499 |
// Try to proceed anyway with group/subgroup sorting
|
500 |
if (group) {
|
501 |
metricsToUpdate.forEach((metricName) => {
|
|
|
599 |
selectedOverallMetrics,
|
600 |
])
|
601 |
|
|
|
|
|
602 |
// CSV export function
|
603 |
const exportToCsv = () => {
|
604 |
// Build header row
|
|
|
608 |
.filter((metric) => selectedOverallMetrics.has(metric))
|
609 |
.flatMap((metric) => {
|
610 |
const metricModels = modelOrderByOverallMetric[metric] || models
|
611 |
+
return metricModels.map((model) => `${metric} - ${descriptions.current.getModelAlias(model) || model}`)
|
612 |
}),
|
613 |
]
|
614 |
|
|
|
800 |
className="cursor-pointer select-none"
|
801 |
onClick={() => handleRowSort(metric, model)}
|
802 |
>
|
803 |
+
{descriptionsLoaded && descriptions.current.getModelAlias(model) || model}
|
804 |
<span
|
805 |
className="ml-1 font-bold"
|
806 |
title={
|
frontend/src/components/ModelFilter.tsx
CHANGED
@@ -38,6 +38,7 @@ const ModelFilter: React.FC<ModelFilterProps> = ({ models, selectedModels, setSe
|
|
38 |
const description = descriptions.current.getModelDescription(model)
|
39 |
const paperUrl = descriptions.current.getModelPaperUrl(model)
|
40 |
const githubUrl = descriptions.current.getModelGithubUrl(model)
|
|
|
41 |
return (
|
42 |
<div key={model} className="flex items-center gap-2 text-sm relative group">
|
43 |
<label className="flex items-center gap-2 flex-grow">
|
@@ -49,7 +50,7 @@ const ModelFilter: React.FC<ModelFilterProps> = ({ models, selectedModels, setSe
|
|
49 |
/>
|
50 |
<div className="flex items-center">
|
51 |
<span className="truncate" title={fullName}>
|
52 |
-
{model}
|
53 |
</span>
|
54 |
<ModelInfoIcon modelName={model} />
|
55 |
</div>
|
|
|
38 |
const description = descriptions.current.getModelDescription(model)
|
39 |
const paperUrl = descriptions.current.getModelPaperUrl(model)
|
40 |
const githubUrl = descriptions.current.getModelGithubUrl(model)
|
41 |
+
const modelAlias = descriptions.current.getModelAlias(model)
|
42 |
return (
|
43 |
<div key={model} className="flex items-center gap-2 text-sm relative group">
|
44 |
<label className="flex items-center gap-2 flex-grow">
|
|
|
50 |
/>
|
51 |
<div className="flex items-center">
|
52 |
<span className="truncate" title={fullName}>
|
53 |
+
{modelAlias || model}
|
54 |
</span>
|
55 |
<ModelInfoIcon modelName={model} />
|
56 |
</div>
|
frontend/src/components/ModelInfoIcon.tsx
CHANGED
@@ -21,6 +21,7 @@ const ModelInfoIcon: React.FC<ModelInfoIconProps> = ({ modelName }) => {
|
|
21 |
const description = descriptions.current.getModelDescription(modelName)
|
22 |
const paperUrl = descriptions.current.getModelPaperUrl(modelName)
|
23 |
const githubUrl = descriptions.current.getModelGithubUrl(modelName)
|
|
|
24 |
|
25 |
return (
|
26 |
<>
|
@@ -51,6 +52,7 @@ const ModelInfoIcon: React.FC<ModelInfoIconProps> = ({ modelName }) => {
|
|
51 |
<div className="mb-2 whitespace-pre-line">
|
52 |
{description || 'No description available.'}
|
53 |
</div>
|
|
|
54 |
<div className="flex space-x-4">
|
55 |
{paperUrl && (
|
56 |
<div>
|
|
|
21 |
const description = descriptions.current.getModelDescription(modelName)
|
22 |
const paperUrl = descriptions.current.getModelPaperUrl(modelName)
|
23 |
const githubUrl = descriptions.current.getModelGithubUrl(modelName)
|
24 |
+
const messageSize = descriptions.current.getModelMessageSize(modelName)
|
25 |
|
26 |
return (
|
27 |
<>
|
|
|
52 |
<div className="mb-2 whitespace-pre-line">
|
53 |
{description || 'No description available.'}
|
54 |
</div>
|
55 |
+
{messageSize !== undefined && <div className="mb-2">Message Size: {messageSize}</div>}
|
56 |
<div className="flex space-x-4">
|
57 |
{paperUrl && (
|
58 |
<div>
|
frontend/src/components/QualityMetricsTable.tsx
CHANGED
@@ -1,6 +1,7 @@
|
|
1 |
-
import React, { useState } from 'react'
|
2 |
import { ArrowDownTrayIcon } from '@heroicons/react/24/outline'
|
3 |
import MetricInfoIcon from './MetricInfoIcon'
|
|
|
4 |
|
5 |
interface Row {
|
6 |
metric: string
|
@@ -25,6 +26,13 @@ const QualityMetricsTable: React.FC<QualityMetricsTableProps> = ({
|
|
25 |
const [columnSort, setColumnSort] = useState<{ model: string; direction: 'asc' | 'desc' } | null>(
|
26 |
null
|
27 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
28 |
|
29 |
// Handle row sort (sort columns by this metric)
|
30 |
const handleRowSort = (metric: string) => {
|
@@ -79,8 +87,13 @@ const QualityMetricsTable: React.FC<QualityMetricsTableProps> = ({
|
|
79 |
|
80 |
// CSV export logic
|
81 |
function exportToCSV() {
|
82 |
-
// Build header row
|
83 |
-
const header = [
|
|
|
|
|
|
|
|
|
|
|
84 |
// Build data rows
|
85 |
const rows = sortedMetrics
|
86 |
.map((metric) => {
|
@@ -142,7 +155,7 @@ const QualityMetricsTable: React.FC<QualityMetricsTableProps> = ({
|
|
142 |
: 'Sort by this column'
|
143 |
}
|
144 |
>
|
145 |
-
{model}
|
146 |
<span className="ml-1">{isSorted ? (direction === 'asc' ? 'β' : 'β') : 'β
'}</span>
|
147 |
</th>
|
148 |
)
|
|
|
1 |
+
import React, { useState, useEffect, useRef } from 'react'
|
2 |
import { ArrowDownTrayIcon } from '@heroicons/react/24/outline'
|
3 |
import MetricInfoIcon from './MetricInfoIcon'
|
4 |
+
import Descriptions from '../Descriptions'
|
5 |
|
6 |
interface Row {
|
7 |
metric: string
|
|
|
26 |
const [columnSort, setColumnSort] = useState<{ model: string; direction: 'asc' | 'desc' } | null>(
|
27 |
null
|
28 |
)
|
29 |
+
const [descriptionsLoaded, setDescriptionsLoaded] = useState(false)
|
30 |
+
const descriptions = useRef(Descriptions.getInstance())
|
31 |
+
|
32 |
+
// Load descriptions
|
33 |
+
useEffect(() => {
|
34 |
+
descriptions.current.load().then(() => setDescriptionsLoaded(true))
|
35 |
+
}, [])
|
36 |
|
37 |
// Handle row sort (sort columns by this metric)
|
38 |
const handleRowSort = (metric: string) => {
|
|
|
87 |
|
88 |
// CSV export logic
|
89 |
function exportToCSV() {
|
90 |
+
// Build header row with model full names when available
|
91 |
+
const header = [
|
92 |
+
'Quality Metric',
|
93 |
+
...sortedModels.map(model =>
|
94 |
+
descriptionsLoaded && descriptions.current.getModelAlias(model) || model
|
95 |
+
)
|
96 |
+
]
|
97 |
// Build data rows
|
98 |
const rows = sortedMetrics
|
99 |
.map((metric) => {
|
|
|
155 |
: 'Sort by this column'
|
156 |
}
|
157 |
>
|
158 |
+
{descriptionsLoaded && descriptions.current.getModelAlias(model) || model}
|
159 |
<span className="ml-1">{isSorted ? (direction === 'asc' ? 'β' : 'β') : 'β
'}</span>
|
160 |
</th>
|
161 |
)
|