Commit
·
2225a22
1
Parent(s):
45f6190
update app
Browse files
app.py
CHANGED
|
@@ -7,7 +7,6 @@ import glob
|
|
| 7 |
import os
|
| 8 |
import re
|
| 9 |
import sys
|
| 10 |
-
import threading
|
| 11 |
import time
|
| 12 |
import webbrowser
|
| 13 |
from datetime import datetime
|
|
@@ -906,13 +905,15 @@ def create_timeline_template():
|
|
| 906 |
}
|
| 907 |
|
| 908 |
.header {
|
| 909 |
-
background: rgba(255, 255, 255, 0.
|
| 910 |
backdrop-filter: blur(10px);
|
| 911 |
-
padding: 0.
|
| 912 |
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
|
|
|
|
| 913 |
position: sticky;
|
| 914 |
top: 0;
|
| 915 |
z-index: 100;
|
|
|
|
| 916 |
}
|
| 917 |
|
| 918 |
.header h1 {
|
|
@@ -928,17 +929,67 @@ def create_timeline_template():
|
|
| 928 |
margin: 0;
|
| 929 |
}
|
| 930 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 931 |
.controls {
|
| 932 |
background: rgba(255, 255, 255, 0.9);
|
| 933 |
backdrop-filter: blur(10px);
|
| 934 |
padding: 0.4rem 1rem;
|
| 935 |
-
margin: 0.3rem 2rem;
|
| 936 |
border-radius: 8px;
|
| 937 |
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.06);
|
| 938 |
display: flex;
|
| 939 |
-
gap: 0.
|
| 940 |
align-items: start;
|
| 941 |
-
flex-wrap:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 942 |
}
|
| 943 |
|
| 944 |
.input-group {
|
|
@@ -970,8 +1021,9 @@ def create_timeline_template():
|
|
| 970 |
}
|
| 971 |
|
| 972 |
.modality-group {
|
| 973 |
-
flex:
|
| 974 |
-
min-width:
|
|
|
|
| 975 |
}
|
| 976 |
|
| 977 |
.modality-filters {
|
|
@@ -988,8 +1040,8 @@ def create_timeline_template():
|
|
| 988 |
}
|
| 989 |
|
| 990 |
.task-group {
|
| 991 |
-
flex:
|
| 992 |
-
min-width:
|
| 993 |
}
|
| 994 |
|
| 995 |
.task-filters {
|
|
@@ -1499,10 +1551,10 @@ def create_timeline_template():
|
|
| 1499 |
|
| 1500 |
/* Expanded card styles */
|
| 1501 |
.timeline-label.expanded {
|
| 1502 |
-
max-width:
|
| 1503 |
-
min-width:
|
| 1504 |
-
width:
|
| 1505 |
-
min-height:
|
| 1506 |
text-align: left;
|
| 1507 |
z-index: 9999 !important; /* Much higher than all other elements */
|
| 1508 |
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
|
@@ -1533,6 +1585,9 @@ def create_timeline_template():
|
|
| 1533 |
color: #1f2937;
|
| 1534 |
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
|
| 1535 |
padding-bottom: 0.3rem;
|
|
|
|
|
|
|
|
|
|
| 1536 |
}
|
| 1537 |
|
| 1538 |
.timeline-label .model-description {
|
|
@@ -1786,8 +1841,8 @@ def create_timeline_template():
|
|
| 1786 |
|
| 1787 |
.date-input-group {
|
| 1788 |
display: flex;
|
| 1789 |
-
flex-direction:
|
| 1790 |
-
gap: 0.
|
| 1791 |
align-items: center;
|
| 1792 |
}
|
| 1793 |
|
|
@@ -1795,7 +1850,7 @@ def create_timeline_template():
|
|
| 1795 |
font-size: 0.7rem;
|
| 1796 |
color: #4a5568;
|
| 1797 |
font-weight: 600;
|
| 1798 |
-
|
| 1799 |
}
|
| 1800 |
|
| 1801 |
.date-input-group input {
|
|
@@ -1895,10 +1950,12 @@ def create_timeline_template():
|
|
| 1895 |
<body>
|
| 1896 |
<div class="header">
|
| 1897 |
<h1>🤗 Transformers Models Timeline</h1>
|
| 1898 |
-
<p>Interactive timeline
|
| 1899 |
</div>
|
| 1900 |
|
| 1901 |
-
<div class="controls">
|
|
|
|
|
|
|
| 1902 |
<div class="input-group modality-group">
|
| 1903 |
<label>Modalities</label>
|
| 1904 |
<div class="modality-filters" id="modalityFilters">
|
|
@@ -1927,6 +1984,7 @@ def create_timeline_template():
|
|
| 1927 |
</button>
|
| 1928 |
</div>
|
| 1929 |
</div>
|
|
|
|
| 1930 |
</div>
|
| 1931 |
|
| 1932 |
<div class="timeline-container">
|
|
@@ -1977,6 +2035,7 @@ def create_timeline_template():
|
|
| 1977 |
let timelineOffset = 0;
|
| 1978 |
let timelineWidth = 0;
|
| 1979 |
let containerWidth = 0;
|
|
|
|
| 1980 |
let isDragging = false;
|
| 1981 |
let startX = 0;
|
| 1982 |
let startOffset = 0;
|
|
@@ -2018,6 +2077,31 @@ def create_timeline_template():
|
|
| 2018 |
allModels = data.models;
|
| 2019 |
currentModels = data.models;
|
| 2020 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2021 |
updateStats(data.total_count, data.filtered_count);
|
| 2022 |
renderTimeline(currentModels);
|
| 2023 |
|
|
@@ -2877,10 +2961,72 @@ def create_timeline_template():
|
|
| 2877 |
if (!e.target.closest('.timeline-label')) {
|
| 2878 |
document.querySelectorAll('.timeline-label.expanded').forEach(label => {
|
| 2879 |
label.classList.remove('expanded');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2880 |
});
|
| 2881 |
}
|
| 2882 |
});
|
|
|
|
|
|
|
|
|
|
| 2883 |
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2884 |
</script>
|
| 2885 |
</body>
|
| 2886 |
</html>"""
|
|
@@ -2892,7 +3038,7 @@ def create_timeline_template():
|
|
| 2892 |
def open_browser():
|
| 2893 |
"""Open the browser after a short delay."""
|
| 2894 |
time.sleep(1.5)
|
| 2895 |
-
webbrowser.open("http://localhost:
|
| 2896 |
|
| 2897 |
|
| 2898 |
def main():
|
|
@@ -2916,14 +3062,6 @@ def main():
|
|
| 2916 |
else:
|
| 2917 |
print(f"✅ Found {len(models)} models with release dates")
|
| 2918 |
|
| 2919 |
-
print("\n🚀 Starting timeline server...")
|
| 2920 |
-
print("📱 Opening in browser...")
|
| 2921 |
-
print("🌐 Access at: http://localhost:7860")
|
| 2922 |
-
print("🛑 Press Ctrl+C to stop")
|
| 2923 |
-
|
| 2924 |
-
# Start browser opening in background
|
| 2925 |
-
threading.Thread(target=open_browser, daemon=True).start()
|
| 2926 |
-
|
| 2927 |
# Run Flask app
|
| 2928 |
try:
|
| 2929 |
app.run(host="0.0.0.0", port=7860, debug=False)
|
|
|
|
| 7 |
import os
|
| 8 |
import re
|
| 9 |
import sys
|
|
|
|
| 10 |
import time
|
| 11 |
import webbrowser
|
| 12 |
from datetime import datetime
|
|
|
|
| 905 |
}
|
| 906 |
|
| 907 |
.header {
|
| 908 |
+
background: rgba(255, 255, 255, 0.9);
|
| 909 |
backdrop-filter: blur(10px);
|
| 910 |
+
padding: 0.8rem 1.5rem;
|
| 911 |
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
|
| 912 |
+
border-bottom: 1px solid rgba(255, 255, 255, 0.5);
|
| 913 |
position: sticky;
|
| 914 |
top: 0;
|
| 915 |
z-index: 100;
|
| 916 |
+
margin: 0 0 0.3rem 0;
|
| 917 |
}
|
| 918 |
|
| 919 |
.header h1 {
|
|
|
|
| 929 |
margin: 0;
|
| 930 |
}
|
| 931 |
|
| 932 |
+
.controls-wrapper {
|
| 933 |
+
margin: 0.3rem 2rem;
|
| 934 |
+
}
|
| 935 |
+
|
| 936 |
+
.controls-toggle {
|
| 937 |
+
position: absolute;
|
| 938 |
+
left: 8px;
|
| 939 |
+
right: 8px;
|
| 940 |
+
top: 8px;
|
| 941 |
+
z-index: 2;
|
| 942 |
+
display: inline-flex;
|
| 943 |
+
align-items: center;
|
| 944 |
+
justify-content: center;
|
| 945 |
+
width: 28px;
|
| 946 |
+
height: 28px;
|
| 947 |
+
font-size: 0.9rem;
|
| 948 |
+
border-radius: 999px;
|
| 949 |
+
border: 1px solid #e5e7eb;
|
| 950 |
+
background: #ffffff;
|
| 951 |
+
color: #374151;
|
| 952 |
+
cursor: pointer;
|
| 953 |
+
transition: all 0.2s ease;
|
| 954 |
+
box-shadow: 0 2px 8px rgba(0,0,0,0.06);
|
| 955 |
+
}
|
| 956 |
+
|
| 957 |
+
.controls-toggle:hover {
|
| 958 |
+
transform: translateY(-1px);
|
| 959 |
+
box-shadow: 0 4px 14px rgba(0,0,0,0.1);
|
| 960 |
+
}
|
| 961 |
+
|
| 962 |
.controls {
|
| 963 |
background: rgba(255, 255, 255, 0.9);
|
| 964 |
backdrop-filter: blur(10px);
|
| 965 |
padding: 0.4rem 1rem;
|
|
|
|
| 966 |
border-radius: 8px;
|
| 967 |
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.06);
|
| 968 |
display: flex;
|
| 969 |
+
gap: 0.8rem;
|
| 970 |
align-items: start;
|
| 971 |
+
flex-wrap: nowrap;
|
| 972 |
+
overflow: hidden;
|
| 973 |
+
transition: max-height 0.25s ease, opacity 0.2s ease;
|
| 974 |
+
position: relative;
|
| 975 |
+
padding-left: 2.4rem; /* space for the left toggle button */
|
| 976 |
+
max-height: none; /* will be set dynamically */
|
| 977 |
+
}
|
| 978 |
+
|
| 979 |
+
.controls.collapsed {
|
| 980 |
+
opacity: 1;
|
| 981 |
+
}
|
| 982 |
+
|
| 983 |
+
/* Blur fade to hint there is more content when collapsed */
|
| 984 |
+
.controls.collapsed::after {
|
| 985 |
+
content: '';
|
| 986 |
+
position: absolute;
|
| 987 |
+
left: 0;
|
| 988 |
+
right: 0;
|
| 989 |
+
bottom: 0;
|
| 990 |
+
height: 26px;
|
| 991 |
+
background: linear-gradient(to bottom, rgba(255,255,255,0), rgba(255,255,255,0.94) 60%, rgba(255,255,255,1));
|
| 992 |
+
pointer-events: none;
|
| 993 |
}
|
| 994 |
|
| 995 |
.input-group {
|
|
|
|
| 1021 |
}
|
| 1022 |
|
| 1023 |
.modality-group {
|
| 1024 |
+
flex: 22%;
|
| 1025 |
+
min-width: 220px;
|
| 1026 |
+
padding-left: 8px; /* add small spacing from collapse arrow */
|
| 1027 |
}
|
| 1028 |
|
| 1029 |
.modality-filters {
|
|
|
|
| 1040 |
}
|
| 1041 |
|
| 1042 |
.task-group {
|
| 1043 |
+
flex: 78%;
|
| 1044 |
+
min-width: 520px;
|
| 1045 |
}
|
| 1046 |
|
| 1047 |
.task-filters {
|
|
|
|
| 1551 |
|
| 1552 |
/* Expanded card styles */
|
| 1553 |
.timeline-label.expanded {
|
| 1554 |
+
max-width: 500px !important;
|
| 1555 |
+
min-width: 400px !important;
|
| 1556 |
+
width: 500px !important;
|
| 1557 |
+
min-height: 250px !important;
|
| 1558 |
text-align: left;
|
| 1559 |
z-index: 9999 !important; /* Much higher than all other elements */
|
| 1560 |
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
|
|
|
| 1585 |
color: #1f2937;
|
| 1586 |
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
|
| 1587 |
padding-bottom: 0.3rem;
|
| 1588 |
+
white-space: nowrap;
|
| 1589 |
+
overflow: hidden;
|
| 1590 |
+
text-overflow: ellipsis;
|
| 1591 |
}
|
| 1592 |
|
| 1593 |
.timeline-label .model-description {
|
|
|
|
| 1841 |
|
| 1842 |
.date-input-group {
|
| 1843 |
display: flex;
|
| 1844 |
+
flex-direction: row;
|
| 1845 |
+
gap: 0.5rem;
|
| 1846 |
align-items: center;
|
| 1847 |
}
|
| 1848 |
|
|
|
|
| 1850 |
font-size: 0.7rem;
|
| 1851 |
color: #4a5568;
|
| 1852 |
font-weight: 600;
|
| 1853 |
+
white-space: nowrap;
|
| 1854 |
}
|
| 1855 |
|
| 1856 |
.date-input-group input {
|
|
|
|
| 1950 |
<body>
|
| 1951 |
<div class="header">
|
| 1952 |
<h1>🤗 Transformers Models Timeline</h1>
|
| 1953 |
+
<p>Interactive timeline to explore models supported by the Hugging Face Transformers library!</p>
|
| 1954 |
</div>
|
| 1955 |
|
| 1956 |
+
<div class="controls-wrapper">
|
| 1957 |
+
<div class="controls" id="filtersPanel">
|
| 1958 |
+
<button id="toggleFilters" class="controls-toggle" type="button" aria-label="Toggle filters" onclick="toggleFilters()">▾</button>
|
| 1959 |
<div class="input-group modality-group">
|
| 1960 |
<label>Modalities</label>
|
| 1961 |
<div class="modality-filters" id="modalityFilters">
|
|
|
|
| 1984 |
</button>
|
| 1985 |
</div>
|
| 1986 |
</div>
|
| 1987 |
+
</div>
|
| 1988 |
</div>
|
| 1989 |
|
| 1990 |
<div class="timeline-container">
|
|
|
|
| 2035 |
let timelineOffset = 0;
|
| 2036 |
let timelineWidth = 0;
|
| 2037 |
let containerWidth = 0;
|
| 2038 |
+
let dateInitialized = false; // initialize date inputs once from data
|
| 2039 |
let isDragging = false;
|
| 2040 |
let startX = 0;
|
| 2041 |
let startOffset = 0;
|
|
|
|
| 2077 |
allModels = data.models;
|
| 2078 |
currentModels = data.models;
|
| 2079 |
|
| 2080 |
+
// Initialize date inputs to min/max once (based on current data)
|
| 2081 |
+
if (!dateInitialized && currentModels && currentModels.length > 0) {
|
| 2082 |
+
const validDates = currentModels
|
| 2083 |
+
.map(m => m.transformers_date)
|
| 2084 |
+
.filter(d => !!d)
|
| 2085 |
+
.sort();
|
| 2086 |
+
if (validDates.length > 0) {
|
| 2087 |
+
const minDate = validDates[0];
|
| 2088 |
+
const maxDate = validDates[validDates.length - 1];
|
| 2089 |
+
const startEl = document.getElementById('startDate');
|
| 2090 |
+
const endEl = document.getElementById('endDate');
|
| 2091 |
+
if (startEl) {
|
| 2092 |
+
startEl.min = minDate;
|
| 2093 |
+
startEl.max = maxDate;
|
| 2094 |
+
if (!startEl.value) startEl.value = minDate;
|
| 2095 |
+
}
|
| 2096 |
+
if (endEl) {
|
| 2097 |
+
endEl.min = minDate;
|
| 2098 |
+
endEl.max = maxDate;
|
| 2099 |
+
if (!endEl.value) endEl.value = maxDate;
|
| 2100 |
+
}
|
| 2101 |
+
dateInitialized = true;
|
| 2102 |
+
}
|
| 2103 |
+
}
|
| 2104 |
+
|
| 2105 |
updateStats(data.total_count, data.filtered_count);
|
| 2106 |
renderTimeline(currentModels);
|
| 2107 |
|
|
|
|
| 2961 |
if (!e.target.closest('.timeline-label')) {
|
| 2962 |
document.querySelectorAll('.timeline-label.expanded').forEach(label => {
|
| 2963 |
label.classList.remove('expanded');
|
| 2964 |
+
|
| 2965 |
+
// Restore compact content
|
| 2966 |
+
setTimeout(() => {
|
| 2967 |
+
label.innerHTML = `
|
| 2968 |
+
<div class="model-title">${label.dataset.modelName}</div>
|
| 2969 |
+
<div class="timeline-date">${label.dataset.modelDate}</div>
|
| 2970 |
+
`;
|
| 2971 |
+
|
| 2972 |
+
// Reset positioning after content change
|
| 2973 |
+
setTimeout(() => {
|
| 2974 |
+
label.style.top = '';
|
| 2975 |
+
label.style.bottom = '';
|
| 2976 |
+
label.parentElement.style.zIndex = '';
|
| 2977 |
+
}, 50);
|
| 2978 |
+
}, 50);
|
| 2979 |
});
|
| 2980 |
}
|
| 2981 |
});
|
| 2982 |
+
|
| 2983 |
+
// Initialize filters collapsible panel
|
| 2984 |
+
initFiltersCollapsible();
|
| 2985 |
});
|
| 2986 |
+
|
| 2987 |
+
function initFiltersCollapsible() {
|
| 2988 |
+
const panel = document.getElementById('filtersPanel');
|
| 2989 |
+
const btn = document.getElementById('toggleFilters');
|
| 2990 |
+
if (!panel || !btn) return;
|
| 2991 |
+
|
| 2992 |
+
// Restore persisted state, default to collapsed
|
| 2993 |
+
const saved = localStorage.getItem('filtersCollapsed');
|
| 2994 |
+
if (saved === 'false') {
|
| 2995 |
+
// Set natural max-height to allow animation on first collapse
|
| 2996 |
+
panel.style.maxHeight = panel.scrollHeight + 'px';
|
| 2997 |
+
btn.textContent = '▾';
|
| 2998 |
+
} else {
|
| 2999 |
+
// set partial height (~2 rows) when collapsed (default state)
|
| 3000 |
+
const partial = 74; // pixels to show when collapsed
|
| 3001 |
+
panel.style.maxHeight = partial + 'px';
|
| 3002 |
+
panel.classList.add('collapsed');
|
| 3003 |
+
btn.textContent = '▸';
|
| 3004 |
+
}
|
| 3005 |
+
}
|
| 3006 |
+
|
| 3007 |
+
function toggleFilters() {
|
| 3008 |
+
const panel = document.getElementById('filtersPanel');
|
| 3009 |
+
const btn = document.getElementById('toggleFilters');
|
| 3010 |
+
if (!panel || !btn) return;
|
| 3011 |
+
|
| 3012 |
+
const isCollapsed = panel.classList.contains('collapsed');
|
| 3013 |
+
if (isCollapsed) {
|
| 3014 |
+
// expand to full content height
|
| 3015 |
+
panel.classList.remove('collapsed');
|
| 3016 |
+
panel.style.maxHeight = panel.scrollHeight + 'px';
|
| 3017 |
+
btn.textContent = '▾';
|
| 3018 |
+
localStorage.setItem('filtersCollapsed', 'false');
|
| 3019 |
+
} else {
|
| 3020 |
+
// collapse to partial height (show a hint of content)
|
| 3021 |
+
panel.style.maxHeight = panel.scrollHeight + 'px'; // set current height
|
| 3022 |
+
void panel.offsetHeight; // reflow
|
| 3023 |
+
panel.classList.add('collapsed');
|
| 3024 |
+
const partial = 74; // pixels to show when collapsed
|
| 3025 |
+
panel.style.maxHeight = partial + 'px';
|
| 3026 |
+
btn.textContent = '▸';
|
| 3027 |
+
localStorage.setItem('filtersCollapsed', 'true');
|
| 3028 |
+
}
|
| 3029 |
+
}
|
| 3030 |
</script>
|
| 3031 |
</body>
|
| 3032 |
</html>"""
|
|
|
|
| 3038 |
def open_browser():
|
| 3039 |
"""Open the browser after a short delay."""
|
| 3040 |
time.sleep(1.5)
|
| 3041 |
+
webbrowser.open("http://localhost:5000")
|
| 3042 |
|
| 3043 |
|
| 3044 |
def main():
|
|
|
|
| 3062 |
else:
|
| 3063 |
print(f"✅ Found {len(models)} models with release dates")
|
| 3064 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3065 |
# Run Flask app
|
| 3066 |
try:
|
| 3067 |
app.run(host="0.0.0.0", port=7860, debug=False)
|