yonigozlan HF Staff commited on
Commit
f724e0a
·
1 Parent(s): 2a0740b

Update style and behaviors

Browse files
Files changed (1) hide show
  1. app.py +138 -64
app.py CHANGED
@@ -34,7 +34,7 @@ class TransformersTimelineParser:
34
  self.modalities = {
35
  "text": {
36
  "name": "Text Models",
37
- "color": "#8B5CF6", # Soft purple
38
  "models": [
39
  "albert",
40
  "apertus",
@@ -303,7 +303,7 @@ class TransformersTimelineParser:
303
  },
304
  "audio": {
305
  "name": "Audio Models",
306
- "color": "#F59E0B", # Soft amber
307
  "models": [
308
  "audio-spectrogram-transformer",
309
  "bark",
@@ -479,7 +479,7 @@ class TransformersTimelineParser:
479
  if model_name in modality_info["models"]:
480
  return {"key": modality_key, "name": modality_info["name"], "color": modality_info["color"]}
481
  # Default to text if not found (most common)
482
- return {"key": "text", "name": "Text Models", "color": "#8B5CF6"}
483
 
484
  def parse_release_date_from_file(self, file_path: str) -> Optional[Dict[str, str]]:
485
  """Parse the release date line from a model documentation file."""
@@ -909,7 +909,7 @@ def create_timeline_template():
909
 
910
  body {
911
  font-family: 'Inter', system-ui, -apple-system, sans-serif;
912
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
913
  min-height: 100vh;
914
  color: #333;
915
  display: flex;
@@ -1028,8 +1028,8 @@ def create_timeline_template():
1028
 
1029
  .input-group input:focus {
1030
  outline: none;
1031
- border-color: #667eea;
1032
- box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
1033
  }
1034
 
1035
  .modality-group {
@@ -1106,7 +1106,6 @@ def create_timeline_template():
1106
  }
1107
 
1108
  .modality-checkbox:hover {
1109
- transform: translateY(-2px);
1110
  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.12);
1111
  border-color: currentColor;
1112
  }
@@ -1118,7 +1117,6 @@ def create_timeline_template():
1118
  .modality-checkbox.checked {
1119
  background: rgba(255, 255, 255, 0.95);
1120
  border-color: currentColor;
1121
- box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
1122
  }
1123
 
1124
  .modality-checkbox.checked::before {
@@ -1193,7 +1191,6 @@ def create_timeline_template():
1193
  }
1194
 
1195
  .task-checkbox:hover {
1196
- transform: translateY(-2px);
1197
  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.12);
1198
  border-color: currentColor;
1199
  }
@@ -1205,7 +1202,6 @@ def create_timeline_template():
1205
  .task-checkbox.checked {
1206
  background: rgba(255, 255, 255, 0.95);
1207
  border-color: currentColor;
1208
- box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
1209
  }
1210
 
1211
  .task-checkbox.checked::before {
@@ -1267,13 +1263,13 @@ def create_timeline_template():
1267
  }
1268
 
1269
  .btn-primary {
1270
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
1271
  color: white;
1272
  }
1273
 
1274
  .btn-primary:hover {
1275
  transform: translateY(-2px);
1276
- box-shadow: 0 8px 20px rgba(102, 126, 234, 0.3);
1277
  }
1278
 
1279
  .btn-secondary {
@@ -1342,18 +1338,18 @@ def create_timeline_template():
1342
  justify-content: center;
1343
  cursor: pointer;
1344
  font-size: 1.2rem;
1345
- color: #667eea;
1346
- border: 2px solid rgba(102, 126, 234, 0.2);
1347
  backdrop-filter: blur(10px);
1348
  transition: all 0.2s ease;
1349
  z-index: 10;
1350
  }
1351
 
1352
  .nav-arrow:hover {
1353
- background: rgba(102, 126, 234, 0.1);
1354
- border-color: #667eea;
1355
  transform: translateY(-50%) scale(1.1);
1356
- box-shadow: 0 4px 16px rgba(102, 126, 234, 0.3);
1357
  }
1358
 
1359
  .nav-arrow.left {
@@ -1390,18 +1386,18 @@ def create_timeline_template():
1390
  cursor: pointer;
1391
  font-size: 1.1rem;
1392
  font-weight: 600;
1393
- color: #667eea;
1394
- border: 2px solid rgba(102, 126, 234, 0.2);
1395
  backdrop-filter: blur(10px);
1396
  transition: all 0.2s ease;
1397
  user-select: none;
1398
  }
1399
 
1400
  .zoom-btn:hover {
1401
- background: rgba(102, 126, 234, 0.1);
1402
- border-color: #667eea;
1403
  transform: scale(1.1);
1404
- box-shadow: 0 4px 16px rgba(102, 126, 234, 0.3);
1405
  }
1406
 
1407
  .zoom-btn:active {
@@ -1413,9 +1409,9 @@ def create_timeline_template():
1413
  border-radius: 6px;
1414
  padding: 4px 8px;
1415
  font-size: 0.75rem;
1416
- color: #667eea;
1417
  font-weight: 500;
1418
- border: 1px solid rgba(102, 126, 234, 0.2);
1419
  }
1420
 
1421
  .timeline-line {
@@ -1424,7 +1420,7 @@ def create_timeline_template():
1424
  left: 0;
1425
  right: 0;
1426
  height: 4px;
1427
- background: linear-gradient(90deg, #667eea, #764ba2);
1428
  border-radius: 2px;
1429
  transform: translateY(-50%);
1430
  }
@@ -1458,7 +1454,7 @@ def create_timeline_template():
1458
  bottom: 0;
1459
  width: 2px;
1460
  background: #9ca3af;
1461
- opacity: 0.6;
1462
  pointer-events: none;
1463
  z-index: 1; /* In background, below everything */
1464
  }
@@ -1482,7 +1478,7 @@ def create_timeline_template():
1482
  }
1483
 
1484
  .date-marker.year {
1485
- opacity: 0.8;
1486
  background: #6b7280;
1487
  width: 3px;
1488
  }
@@ -1496,11 +1492,11 @@ def create_timeline_template():
1496
  }
1497
 
1498
  .date-marker.quarter {
1499
- opacity: 0.5;
1500
  }
1501
 
1502
  .date-marker.month {
1503
- opacity: 0.4;
1504
  }
1505
 
1506
  .timeline-dot {
@@ -1516,11 +1512,11 @@ def create_timeline_template():
1516
  }
1517
 
1518
  .timeline-item:nth-child(odd) .timeline-dot {
1519
- border-color: #667eea;
1520
  }
1521
 
1522
  .timeline-item:nth-child(even) .timeline-dot {
1523
- border-color: #764ba2;
1524
  }
1525
 
1526
  .timeline-label {
@@ -1544,7 +1540,7 @@ def create_timeline_template():
1544
  cursor: pointer;
1545
  }
1546
 
1547
- .timeline-label:hover {
1548
  background: rgba(255, 255, 255, 0.98);
1549
  box-shadow: 0 6px 25px rgba(0, 0, 0, 0.15),
1550
  0 0 4px var(--modality-color, #8B5CF6);
@@ -1560,8 +1556,8 @@ def create_timeline_template():
1560
  text-align: left;
1561
  z-index: 9999 !important; /* Much higher than all other elements */
1562
  transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
1563
- box-shadow: 0 20px 40px rgba(0, 0, 0, 0.2),
1564
- 0 0 8px var(--modality-color, #8B5CF6);
1565
  }
1566
 
1567
  /* Cards above axis - expand downward with TOP edge fixed */
@@ -1723,6 +1719,26 @@ def create_timeline_template():
1723
  transform: translateY(-1px);
1724
  }
1725
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1726
  .model-tasks {
1727
  margin: 0.5rem 0;
1728
  padding: 0.4rem 0.6rem;
@@ -1763,7 +1779,7 @@ def create_timeline_template():
1763
  /* Above the axis - wave pattern */
1764
  .timeline-item.above-1 .timeline-label {
1765
  bottom: 60px;
1766
- border-left: 3px solid #667eea;
1767
  }
1768
 
1769
  .timeline-item.above-1 .timeline-connector {
@@ -1773,7 +1789,7 @@ def create_timeline_template():
1773
 
1774
  .timeline-item.above-2 .timeline-label {
1775
  bottom: 120px;
1776
- border-left: 3px solid #667eea;
1777
  }
1778
 
1779
  .timeline-item.above-2 .timeline-connector {
@@ -1783,7 +1799,7 @@ def create_timeline_template():
1783
 
1784
  .timeline-item.above-3 .timeline-label {
1785
  bottom: 180px;
1786
- border-left: 3px solid #667eea;
1787
  }
1788
 
1789
  .timeline-item.above-3 .timeline-connector {
@@ -1794,7 +1810,7 @@ def create_timeline_template():
1794
  /* Below the axis - wave pattern */
1795
  .timeline-item.below-1 .timeline-label {
1796
  top: 60px;
1797
- border-left: 3px solid #764ba2;
1798
  }
1799
 
1800
  .timeline-item.below-1 .timeline-connector {
@@ -1804,7 +1820,7 @@ def create_timeline_template():
1804
 
1805
  .timeline-item.below-2 .timeline-label {
1806
  top: 120px;
1807
- border-left: 3px solid #764ba2;
1808
  }
1809
 
1810
  .timeline-item.below-2 .timeline-connector {
@@ -1814,7 +1830,7 @@ def create_timeline_template():
1814
 
1815
  .timeline-item.below-3 .timeline-label {
1816
  top: 180px;
1817
- border-left: 3px solid #764ba2;
1818
  }
1819
 
1820
  .timeline-item.below-3 .timeline-connector {
@@ -1824,9 +1840,9 @@ def create_timeline_template():
1824
 
1825
  .timeline-date {
1826
  font-size: 0.65rem;
1827
- color: #666;
1828
  margin-top: 0.3rem;
1829
- font-weight: 400;
1830
  }
1831
 
1832
  .date-controls {
@@ -1876,8 +1892,8 @@ def create_timeline_template():
1876
 
1877
  .date-input-group input:focus {
1878
  outline: none;
1879
- border-color: #667eea;
1880
- box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.15);
1881
  transform: translateY(-1px);
1882
  }
1883
 
@@ -1904,7 +1920,7 @@ def create_timeline_template():
1904
  .stat-number {
1905
  font-size: 1.2rem;
1906
  font-weight: 600;
1907
- color: #667eea;
1908
  display: block;
1909
  }
1910
 
@@ -1928,7 +1944,7 @@ def create_timeline_template():
1928
  width: 20px;
1929
  height: 20px;
1930
  border: 2px solid #e2e8f0;
1931
- border-top: 2px solid #667eea;
1932
  border-radius: 50%;
1933
  animation: spin 1s linear infinite;
1934
  margin-left: 1rem;
@@ -2125,6 +2141,40 @@ def create_timeline_template():
2125
  }
2126
  }
2127
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2128
  function markdownToHtml(markdown) {
2129
  if (!markdown) return '';
2130
 
@@ -2250,7 +2300,7 @@ def create_timeline_template():
2250
 
2251
  // Ensure the model name is always displayed
2252
  const modelName = model.display_name || model.model_name || 'Unknown Model';
2253
- const modelDate = model.transformers_date || 'Unknown Date';
2254
  const description = model.description || 'No description available.';
2255
 
2256
  // Set initial compact content (no tasks, description, or learn more)
@@ -2272,6 +2322,11 @@ def create_timeline_template():
2272
  label.addEventListener('click', (e) => {
2273
  e.stopPropagation();
2274
 
 
 
 
 
 
2275
  // Close any other expanded cards
2276
  document.querySelectorAll('.timeline-label.expanded').forEach(otherLabel => {
2277
  if (otherLabel !== label) {
@@ -2637,6 +2692,11 @@ def create_timeline_template():
2637
  return;
2638
  }
2639
 
 
 
 
 
 
2640
  isDragging = true;
2641
  startX = e.clientX;
2642
  startOffset = timelineOffset;
@@ -2679,6 +2739,11 @@ def create_timeline_template():
2679
  return;
2680
  }
2681
 
 
 
 
 
 
2682
  isDragging = true;
2683
  startX = e.touches[0].clientX;
2684
  startOffset = timelineOffset;
@@ -2954,26 +3019,35 @@ def create_timeline_template():
2954
 
2955
  // Close expanded cards when clicking outside
2956
  document.addEventListener('click', (e) => {
2957
- if (!e.target.closest('.timeline-label')) {
2958
- document.querySelectorAll('.timeline-label.expanded').forEach(label => {
2959
- label.classList.remove('expanded');
 
2960
 
2961
- // Restore compact content
2962
- setTimeout(() => {
2963
- label.innerHTML = `
2964
- <div class="model-title">${label.dataset.modelName}</div>
2965
- <div class="timeline-date">${label.dataset.modelDate}</div>
2966
- `;
2967
 
2968
- // Reset positioning after content change
2969
- setTimeout(() => {
2970
- label.style.top = '';
2971
- label.style.bottom = '';
2972
- label.parentElement.style.zIndex = '';
2973
- }, 50);
 
 
 
 
 
 
 
 
 
 
2974
  }, 50);
2975
- });
2976
- }
2977
  });
2978
 
2979
  // Initialize filters collapsible panel
 
34
  self.modalities = {
35
  "text": {
36
  "name": "Text Models",
37
+ "color": "#F59E0B", # Soft amber
38
  "models": [
39
  "albert",
40
  "apertus",
 
303
  },
304
  "audio": {
305
  "name": "Audio Models",
306
+ "color": "#8B5CF6", # Soft purple
307
  "models": [
308
  "audio-spectrogram-transformer",
309
  "bark",
 
479
  if model_name in modality_info["models"]:
480
  return {"key": modality_key, "name": modality_info["name"], "color": modality_info["color"]}
481
  # Default to text if not found (most common)
482
+ return {"key": "text", "name": "Text Models", "color": "#F59E0B"}
483
 
484
  def parse_release_date_from_file(self, file_path: str) -> Optional[Dict[str, str]]:
485
  """Parse the release date line from a model documentation file."""
 
909
 
910
  body {
911
  font-family: 'Inter', system-ui, -apple-system, sans-serif;
912
+ background: linear-gradient(135deg, #fef3c7 0%, #fed7aa 100%);
913
  min-height: 100vh;
914
  color: #333;
915
  display: flex;
 
1028
 
1029
  .input-group input:focus {
1030
  outline: none;
1031
+ border-color: #d97706;
1032
+ box-shadow: 0 0 0 3px rgba(217, 119, 6, 0.1);
1033
  }
1034
 
1035
  .modality-group {
 
1106
  }
1107
 
1108
  .modality-checkbox:hover {
 
1109
  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.12);
1110
  border-color: currentColor;
1111
  }
 
1117
  .modality-checkbox.checked {
1118
  background: rgba(255, 255, 255, 0.95);
1119
  border-color: currentColor;
 
1120
  }
1121
 
1122
  .modality-checkbox.checked::before {
 
1191
  }
1192
 
1193
  .task-checkbox:hover {
 
1194
  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.12);
1195
  border-color: currentColor;
1196
  }
 
1202
  .task-checkbox.checked {
1203
  background: rgba(255, 255, 255, 0.95);
1204
  border-color: currentColor;
 
1205
  }
1206
 
1207
  .task-checkbox.checked::before {
 
1263
  }
1264
 
1265
  .btn-primary {
1266
+ background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%);
1267
  color: white;
1268
  }
1269
 
1270
  .btn-primary:hover {
1271
  transform: translateY(-2px);
1272
+ box-shadow: 0 8px 20px rgba(217, 119, 6, 0.3);
1273
  }
1274
 
1275
  .btn-secondary {
 
1338
  justify-content: center;
1339
  cursor: pointer;
1340
  font-size: 1.2rem;
1341
+ color: #d97706;
1342
+ border: 2px solid rgba(217, 119, 6, 0.2);
1343
  backdrop-filter: blur(10px);
1344
  transition: all 0.2s ease;
1345
  z-index: 10;
1346
  }
1347
 
1348
  .nav-arrow:hover {
1349
+ background: rgba(217, 119, 6, 0.1);
1350
+ border-color: #d97706;
1351
  transform: translateY(-50%) scale(1.1);
1352
+ box-shadow: 0 4px 16px rgba(217, 119, 6, 0.3);
1353
  }
1354
 
1355
  .nav-arrow.left {
 
1386
  cursor: pointer;
1387
  font-size: 1.1rem;
1388
  font-weight: 600;
1389
+ color: #d97706;
1390
+ border: 2px solid rgba(217, 119, 6, 0.2);
1391
  backdrop-filter: blur(10px);
1392
  transition: all 0.2s ease;
1393
  user-select: none;
1394
  }
1395
 
1396
  .zoom-btn:hover {
1397
+ background: rgba(217, 119, 6, 0.1);
1398
+ border-color: #d97706;
1399
  transform: scale(1.1);
1400
+ box-shadow: 0 4px 16px rgba(217, 119, 6, 0.3);
1401
  }
1402
 
1403
  .zoom-btn:active {
 
1409
  border-radius: 6px;
1410
  padding: 4px 8px;
1411
  font-size: 0.75rem;
1412
+ color: #d97706;
1413
  font-weight: 500;
1414
+ border: 1px solid rgba(217, 119, 6, 0.2);
1415
  }
1416
 
1417
  .timeline-line {
 
1420
  left: 0;
1421
  right: 0;
1422
  height: 4px;
1423
+ background: linear-gradient(90deg, #fbbf24, #d97706);
1424
  border-radius: 2px;
1425
  transform: translateY(-50%);
1426
  }
 
1454
  bottom: 0;
1455
  width: 2px;
1456
  background: #9ca3af;
1457
+ opacity: 0.8;
1458
  pointer-events: none;
1459
  z-index: 1; /* In background, below everything */
1460
  }
 
1478
  }
1479
 
1480
  .date-marker.year {
1481
+ opacity: 0.9;
1482
  background: #6b7280;
1483
  width: 3px;
1484
  }
 
1492
  }
1493
 
1494
  .date-marker.quarter {
1495
+ opacity: 0.7;
1496
  }
1497
 
1498
  .date-marker.month {
1499
+ opacity: 0.8;
1500
  }
1501
 
1502
  .timeline-dot {
 
1512
  }
1513
 
1514
  .timeline-item:nth-child(odd) .timeline-dot {
1515
+ border-color: #fbbf24;
1516
  }
1517
 
1518
  .timeline-item:nth-child(even) .timeline-dot {
1519
+ border-color: #d97706;
1520
  }
1521
 
1522
  .timeline-label {
 
1540
  cursor: pointer;
1541
  }
1542
 
1543
+ .timeline-label:not(.expanded):hover {
1544
  background: rgba(255, 255, 255, 0.98);
1545
  box-shadow: 0 6px 25px rgba(0, 0, 0, 0.15),
1546
  0 0 4px var(--modality-color, #8B5CF6);
 
1556
  text-align: left;
1557
  z-index: 9999 !important; /* Much higher than all other elements */
1558
  transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
1559
+ box-shadow: 0 20px 40px rgba(0, 0, 0, 0.2);
1560
+ user-select: text;
1561
  }
1562
 
1563
  /* Cards above axis - expand downward with TOP edge fixed */
 
1719
  transform: translateY(-1px);
1720
  }
1721
 
1722
+ .timeline-label.expanded .learn-more {
1723
+ pointer-events: auto;
1724
+ cursor: pointer;
1725
+ }
1726
+
1727
+ .timeline-label.expanded .model-description a {
1728
+ pointer-events: auto;
1729
+ cursor: pointer;
1730
+ }
1731
+
1732
+ .timeline-label.expanded .description-content {
1733
+ pointer-events: auto;
1734
+ cursor: text;
1735
+ }
1736
+
1737
+ .timeline-label.expanded .model-tasks {
1738
+ pointer-events: none;
1739
+ cursor: default;
1740
+ }
1741
+
1742
  .model-tasks {
1743
  margin: 0.5rem 0;
1744
  padding: 0.4rem 0.6rem;
 
1779
  /* Above the axis - wave pattern */
1780
  .timeline-item.above-1 .timeline-label {
1781
  bottom: 60px;
1782
+ border-left: 3px solid #fbbf24;
1783
  }
1784
 
1785
  .timeline-item.above-1 .timeline-connector {
 
1789
 
1790
  .timeline-item.above-2 .timeline-label {
1791
  bottom: 120px;
1792
+ border-left: 3px solid #fbbf24;
1793
  }
1794
 
1795
  .timeline-item.above-2 .timeline-connector {
 
1799
 
1800
  .timeline-item.above-3 .timeline-label {
1801
  bottom: 180px;
1802
+ border-left: 3px solid #fbbf24;
1803
  }
1804
 
1805
  .timeline-item.above-3 .timeline-connector {
 
1810
  /* Below the axis - wave pattern */
1811
  .timeline-item.below-1 .timeline-label {
1812
  top: 60px;
1813
+ border-left: 3px solid #d97706;
1814
  }
1815
 
1816
  .timeline-item.below-1 .timeline-connector {
 
1820
 
1821
  .timeline-item.below-2 .timeline-label {
1822
  top: 120px;
1823
+ border-left: 3px solid #d97706;
1824
  }
1825
 
1826
  .timeline-item.below-2 .timeline-connector {
 
1830
 
1831
  .timeline-item.below-3 .timeline-label {
1832
  top: 180px;
1833
+ border-left: 3px solid #d97706;
1834
  }
1835
 
1836
  .timeline-item.below-3 .timeline-connector {
 
1840
 
1841
  .timeline-date {
1842
  font-size: 0.65rem;
1843
+ color: #9ca3af;
1844
  margin-top: 0.3rem;
1845
+ font-weight: 500;
1846
  }
1847
 
1848
  .date-controls {
 
1892
 
1893
  .date-input-group input:focus {
1894
  outline: none;
1895
+ border-color: #d97706;
1896
+ box-shadow: 0 0 0 3px rgba(217, 119, 6, 0.15);
1897
  transform: translateY(-1px);
1898
  }
1899
 
 
1920
  .stat-number {
1921
  font-size: 1.2rem;
1922
  font-weight: 600;
1923
+ color: #d97706;
1924
  display: block;
1925
  }
1926
 
 
1944
  width: 20px;
1945
  height: 20px;
1946
  border: 2px solid #e2e8f0;
1947
+ border-top: 2px solid #d97706;
1948
  border-radius: 50%;
1949
  animation: spin 1s linear infinite;
1950
  margin-left: 1rem;
 
2141
  }
2142
  }
2143
 
2144
+ function formatDate(dateString) {
2145
+ if (!dateString || dateString === 'Unknown Date') return 'Unknown Date';
2146
+
2147
+ try {
2148
+ const date = new Date(dateString);
2149
+ return date.toLocaleDateString('en-US', {
2150
+ year: 'numeric',
2151
+ month: 'short',
2152
+ day: 'numeric'
2153
+ });
2154
+ } catch (error) {
2155
+ return dateString; // Return original if parsing fails
2156
+ }
2157
+ }
2158
+
2159
+ function createFaintColor(hexColor) {
2160
+ // Remove # if present
2161
+ hexColor = hexColor.replace('#', '');
2162
+
2163
+ // Parse RGB values
2164
+ const r = parseInt(hexColor.substr(0, 2), 16);
2165
+ const g = parseInt(hexColor.substr(2, 2), 16);
2166
+ const b = parseInt(hexColor.substr(4, 2), 16);
2167
+
2168
+ // Reduce saturation by mixing with a lighter gray (keeping brightness)
2169
+ const lightGray = 200; // Light gray to maintain brightness
2170
+ const desaturatedR = Math.floor(r * 0.6 + lightGray * 0.4);
2171
+ const desaturatedG = Math.floor(g * 0.6 + lightGray * 0.4);
2172
+ const desaturatedB = Math.floor(b * 0.6 + lightGray * 0.4);
2173
+
2174
+ // Add some opacity (70%)
2175
+ return `rgba(${desaturatedR}, ${desaturatedG}, ${desaturatedB}, 0.7)`;
2176
+ }
2177
+
2178
  function markdownToHtml(markdown) {
2179
  if (!markdown) return '';
2180
 
 
2300
 
2301
  // Ensure the model name is always displayed
2302
  const modelName = model.display_name || model.model_name || 'Unknown Model';
2303
+ const modelDate = formatDate(model.transformers_date || 'Unknown Date');
2304
  const description = model.description || 'No description available.';
2305
 
2306
  // Set initial compact content (no tasks, description, or learn more)
 
2322
  label.addEventListener('click', (e) => {
2323
  e.stopPropagation();
2324
 
2325
+ // Don't toggle if clicking inside an expanded card
2326
+ if (label.classList.contains('expanded')) {
2327
+ return;
2328
+ }
2329
+
2330
  // Close any other expanded cards
2331
  document.querySelectorAll('.timeline-label.expanded').forEach(otherLabel => {
2332
  if (otherLabel !== label) {
 
2692
  return;
2693
  }
2694
 
2695
+ // Ignore clicks inside expanded cards to allow text selection
2696
+ if (e.target.closest('.timeline-label.expanded')) {
2697
+ return;
2698
+ }
2699
+
2700
  isDragging = true;
2701
  startX = e.clientX;
2702
  startOffset = timelineOffset;
 
2739
  return;
2740
  }
2741
 
2742
+ // Ignore touches inside expanded cards to allow text selection
2743
+ if (e.target.closest('.timeline-label.expanded')) {
2744
+ return;
2745
+ }
2746
+
2747
  isDragging = true;
2748
  startX = e.touches[0].clientX;
2749
  startOffset = timelineOffset;
 
3019
 
3020
  // Close expanded cards when clicking outside
3021
  document.addEventListener('click', (e) => {
3022
+ // Don't close if clicking inside an expanded card
3023
+ if (e.target.closest('.timeline-label.expanded')) {
3024
+ return;
3025
+ }
3026
 
3027
+ // Don't close if clicking on a timeline label (non-expanded)
3028
+ if (e.target.closest('.timeline-label:not(.expanded)')) {
3029
+ return;
3030
+ }
 
 
3031
 
3032
+ // Close all expanded cards
3033
+ document.querySelectorAll('.timeline-label.expanded').forEach(label => {
3034
+ label.classList.remove('expanded');
3035
+
3036
+ // Restore compact content
3037
+ setTimeout(() => {
3038
+ label.innerHTML = `
3039
+ <div class="model-title">${label.dataset.modelName}</div>
3040
+ <div class="timeline-date">${label.dataset.modelDate}</div>
3041
+ `;
3042
+
3043
+ // Reset positioning after content change
3044
+ setTimeout(() => {
3045
+ label.style.top = '';
3046
+ label.style.bottom = '';
3047
+ label.parentElement.style.zIndex = '';
3048
  }, 50);
3049
+ }, 50);
3050
+ });
3051
  });
3052
 
3053
  // Initialize filters collapsible panel