lvwerra HF Staff commited on
Commit
8e172ba
·
verified ·
1 Parent(s): 0572118

Update README.md

Browse files
Files changed (1) hide show
  1. README.md +301 -176
README.md CHANGED
@@ -45,174 +45,272 @@ model-index:
45
  name: Code Generation
46
  type: code-generation
47
  ---
48
-
49
- # HuggingFace Research Timeline
50
 
51
  <style>
 
 
52
  body {
53
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
54
- line-height: 1.6;
55
- margin: 0;
56
- padding: 20px;
57
  background: #fafafa;
 
 
58
  }
59
 
60
- .timeline-container {
61
- max-width: 1000px;
 
62
  margin: 0 auto;
63
- background: white;
64
- border-radius: 12px;
65
- padding: 30px;
66
- box-shadow: 0 2px 10px rgba(0,0,0,0.1);
67
  }
68
 
69
- .timeline-year {
70
- margin: 30px 0;
 
 
 
 
71
  }
72
 
73
- .year-header {
74
- font-size: 28px;
75
- font-weight: bold;
76
- color: #1a1a1a;
77
- margin-bottom: 20px;
78
- text-align: center;
79
- padding: 10px;
80
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
81
- color: white;
82
- border-radius: 8px;
83
  }
84
 
85
- .timeline-grid {
86
- display: grid;
87
- grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
88
- gap: 20px;
89
- margin-bottom: 30px;
 
90
  }
91
 
92
- .month-card {
93
- background: #f8f9fa;
94
- border: 1px solid #e9ecef;
95
- border-radius: 8px;
96
- padding: 15px;
97
- transition: transform 0.2s ease, box-shadow 0.2s ease;
98
  }
99
 
100
- .month-card:hover {
101
- transform: translateY(-2px);
102
- box-shadow: 0 4px 15px rgba(0,0,0,0.1);
103
  }
104
 
105
- .month-title {
106
- font-weight: 600;
107
- font-size: 16px;
108
- color: #495057;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
109
  margin-bottom: 12px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
110
  text-transform: uppercase;
111
  letter-spacing: 0.5px;
112
- border-bottom: 2px solid #dee2e6;
113
- padding-bottom: 5px;
114
  }
115
 
116
- .event-item {
117
- margin: 8px 0;
118
- padding: 6px 0;
119
  font-size: 14px;
120
- color: #212529;
121
- display: block;
122
- transition: color 0.2s ease;
 
123
  }
124
 
125
- .event-item:hover {
126
- color: #007bff;
127
  }
128
 
129
- .event-link {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
130
  text-decoration: none;
131
  color: inherit;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
132
  }
133
 
134
- .event-link:hover {
135
- color: #0056b3;
 
 
 
 
 
 
136
  }
137
 
138
  @media (max-width: 768px) {
139
- .timeline-grid {
140
- grid-template-columns: 1fr;
141
  }
142
 
143
- .timeline-container {
144
- padding: 20px;
145
- margin: 10px;
 
 
 
 
 
 
 
 
 
 
 
146
  }
147
  }
148
  </style>
149
 
150
- <div id="timeline-container" class="timeline-container">
151
- <h2 style="text-align: center; margin-bottom: 30px; color: #1a1a1a;">Loading Timeline...</h2>
 
 
 
 
 
 
 
 
152
  </div>
153
 
154
  <script>
155
- // Release data
 
 
 
 
 
 
 
 
 
 
 
156
  const releaseData = [
157
- // 2024
158
  { month: "2024-01", name: "🔥 Warming up", link: null },
159
  { month: "2024-02", name: "⚙️ Nanotron Release", link: "#" },
160
  { month: "2024-02", name: "⛅ Lighteval Release", link: "#" },
161
- { month: "2024-02", name: "⚙️ Datatrove Release", link: "#" },
162
- { month: "2024-02", name: "⭐ The Stack v2", link: "#" },
163
- { month: "2024-02", name: "⭐ StarCoder2", link: "#" },
164
  { month: "2024-03", name: "🪁 Zephyr Gemma", link: "#" },
165
  { month: "2024-03", name: "🪐 Cosmopedia", link: "#" },
166
  { month: "2024-04", name: "🍷 FineWeb", link: "#" },
167
  { month: "2024-04", name: "🕵️ JAT Agent", link: "#" },
168
- { month: "2024-04", name: "🪁 Zephyr Mixtral", link: "#" },
169
- { month: "2024-04", name: "🐶 Idefics 2", link: "#" },
170
- { month: "2024-04", name: "🏆 Community Leaderboards", link: "#" },
171
  { month: "2024-05", name: "🦾 LeRobot Release", link: "#" },
172
- { month: "2024-05", name: "📈 WSD Analysis", link: "#" },
173
- { month: "2024-06", name: "🍷 FineWeb Report", link: "#" },
174
- { month: "2024-06", name: "🍷 FineWeb-Edu", link: "#" },
175
- { month: "2024-06", name: "🌺 Florence 2 Blog", link: "#" },
176
- { month: "2024-06", name: "🏆 Open LLM Leaderboard v2", link: "#" },
177
- { month: "2024-06", name: "👩‍🏫 Stanford CS25", link: "#" },
178
- { month: "2024-07", name: "🛟 Ring attention", link: "#" },
179
- { month: "2024-07", name: "🦾 LeRobot TeleOps", link: "#" },
180
- { month: "2024-07", name: "🥇 Win AIMO", link: "#" },
181
- { month: "2024-07", name: "🐶 Docmatix", link: "#" },
182
- { month: "2024-07", name: "🤏 SmolLM", link: "#" },
183
- { month: "2024-08", name: "🦾 LeRobot Tutorial", link: "#" },
184
- { month: "2024-08", name: "📣 Speech-to-Speech", link: "#" },
185
- { month: "2024-08", name: "🐶 Idefics 3", link: "#" },
186
- { month: "2024-08", name: "🤏 Instant SmolLM", link: "#" },
187
- { month: "2024-09", name: "🦾 LeRobot Video", link: "#" },
188
- { month: "2024-09", name: "🎥 FineVideo", link: "#" },
189
- { month: "2024-09", name: "📣 Speech-to-Speech Multilingual", link: "#" },
190
- { month: "2024-10", name: "🔎 LLM Evaluation Guidebook", link: "#" },
191
- { month: "2024-10", name: "🦾 LeRobot Hackathon", link: "#" },
192
- { month: "2024-10", name: "🗺️ FineTasks", link: "#" },
193
- { month: "2024-10", name: "⛅ Lighteval now Multilingual", link: "#" },
194
- { month: "2024-11", name: "🤏 SmolLM2", link: "#" },
195
- { month: "2024-11", name: "🤓 SmolVLM", link: "#" },
196
- { month: "2024-12", name: "🔢 Number Tokenization Blog", link: "#" },
197
- { month: "2024-12", name: "🥂 FineWeb 2", link: "#" },
198
- { month: "2024-12", name: "📈 Scaling Test Time Compute", link: "#" },
199
- { month: "2024-12", name: "🤖 Picotron", link: "#" },
200
- { month: "2024-12", name: "📐 FineMath", link: "#" },
201
- // 2025
202
- { month: "2025-01", name: "🎊 New year kickoff", link: "#" },
203
- { month: "2025-02", name: "🔮 Planning", link: "#" },
204
- { month: "2025-03", name: "🎯 In progress", link: "#" },
205
- { month: "2025-04", name: "🚀 Coming soon", link: "#" },
206
- { month: "2025-05", name: "🌸 Spring updates", link: "#" },
207
- { month: "2025-06", name: "📅 Mid-year goals", link: "#" }
208
  ];
209
 
 
 
210
  function parseMonth(monthStr) {
211
  const [year, month] = monthStr.split('-');
212
  return {
213
  year: parseInt(year),
214
  month: parseInt(month),
215
- monthName: new Date(year, month - 1).toLocaleString('en', { month: 'long' })
216
  };
217
  }
218
 
@@ -227,102 +325,129 @@ function groupByMonth(data) {
227
  return grouped;
228
  }
229
 
230
- function groupByYear(data) {
231
- const years = {};
232
- data.forEach(item => {
233
- const year = item.month.split('-')[0];
234
- if (!years[year]) {
235
- years[year] = {};
236
- }
237
- if (!years[year][item.month]) {
238
- years[year][item.month] = [];
239
- }
240
- years[year][item.month].push(item);
241
- });
242
- return years;
243
- }
244
-
245
  function generateTimeline() {
246
- console.log('Generating timeline...');
247
 
248
  const container = document.getElementById('timeline-container');
249
  if (!container) {
250
- console.error('Timeline container not found!');
251
  return;
252
  }
253
-
254
- const yearlyData = groupByYear(releaseData);
255
- const years = Object.keys(yearlyData).sort();
256
 
257
- let html = '<h2 style="text-align: center; margin-bottom: 30px; color: #1a1a1a;">HuggingFace Research Timeline</h2>';
 
 
 
258
 
259
- years.forEach(year => {
260
- html += `<div class="timeline-year">`;
261
- html += `<div class="year-header">${year}</div>`;
262
- html += `<div class="timeline-grid">`;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
263
 
264
- const yearData = yearlyData[year];
265
- const months = Object.keys(yearData).sort();
266
 
267
- months.forEach(monthKey => {
268
  const monthData = parseMonth(monthKey);
269
- const events = yearData[monthKey];
270
 
271
- html += `<div class="month-card">`;
272
- html += `<div class="month-title">${monthData.monthName}</div>`;
273
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
274
  events.forEach(event => {
 
 
 
275
  if (event.link && event.link !== '#') {
276
- html += `<div class="event-item">
277
- <a href="${event.link}" class="event-link" target="_blank">
278
- ${event.name}
279
- </a>
280
- </div>`;
281
  } else {
282
- html += `<div class="event-item">${event.name}</div>`;
283
  }
 
 
284
  });
285
-
286
- html += `</div>`;
 
 
 
287
  });
288
-
289
- html += `</div></div>`;
 
 
 
 
 
 
 
 
290
  });
291
 
292
- container.innerHTML = html;
293
- console.log('Timeline generated successfully!');
294
  }
295
 
296
- // Multiple approaches to ensure it runs
297
- function initTimeline() {
298
- // Try immediately
299
  generateTimeline();
300
-
301
- // Try after a short delay
302
- setTimeout(generateTimeline, 100);
303
-
304
- // Try after a longer delay as backup
305
- setTimeout(generateTimeline, 500);
306
  }
307
 
308
- // Run when DOM is ready
 
 
 
 
 
 
 
 
 
 
 
 
 
309
  if (document.readyState === 'loading') {
310
- document.addEventListener('DOMContentLoaded', initTimeline);
 
 
 
 
311
  } else {
312
- initTimeline();
 
313
  }
314
  </script>
315
-
316
- ---
317
-
318
- ## 🛠️ Tooling & Infrastructure
319
-
320
- The foundation of ML research is tooling and infrastructure and we are working on a range of tools such as [datatrove](www.github.com/huggingface/datatrove), [nanotron](www.github.com/huggingface/nanotron), [TRL](www.github.com/huggingface/trl), [LeRobot](www.github.com/huggingface/lerobot), and [lighteval](www.github.com/huggingface/lighteval).
321
-
322
- ## 📑 Datasets
323
-
324
- High quality datasets are the powerhouse of LLMs and require special care and skills to build. We focus on building high-quality datasets such as [no-robots](https://huggingface.co/datasets/HuggingFaceH4/no_robots), [FineWeb](https://huggingface.co/datasets/HuggingFaceFW/fineweb), [The Stack](https://huggingface.co/datasets/bigcode/the-stack-v2), and [FineVideo](https://huggingface.co/datasets/HuggingFaceFV/finevideo).
325
-
326
- ## 🤖 Open Models
327
-
328
- The datatsets and training recipes of most state-of-the-art models are not released. We build cutting-edge models and release the full training pipeline as well fostering more innovation and reproducibility, such as [Zephyr](https://huggingface.co/HuggingFaceH4/zephyr-7b-beta), [StarCoder2](https://huggingface.co/bigcode/starcoder2-15b), or [SmolLM2](https://huggingface.co/HuggingFaceTB/SmolLM2-1.7B-Instruct).
 
45
  name: Code Generation
46
  type: code-generation
47
  ---
48
+ # Snake Timeline Debug
 
49
 
50
  <style>
51
+ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap');
52
+
53
  body {
 
 
 
 
54
  background: #fafafa;
55
+ margin: 0;
56
+ padding: 0;
57
  }
58
 
59
+ .page-container {
60
+ width: 100%;
61
+ max-width: 1200px;
62
  margin: 0 auto;
63
+ padding: 60px 20px;
64
+ font-family: 'Inter', sans-serif;
 
 
65
  }
66
 
67
+ h2 {
68
+ font-size: 24px;
69
+ font-weight: 600;
70
+ margin-bottom: 40px;
71
+ color: #1a1a1a;
72
+ letter-spacing: -0.5px;
73
  }
74
 
75
+ .snake-timeline {
76
+ position: relative;
77
+ padding: 20px 0;
78
+ background: white;
79
+ border-radius: 16px;
80
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
81
+ padding: 40px;
 
 
 
82
  }
83
 
84
+ .timeline-row {
85
+ display: flex;
86
+ position: relative;
87
+ margin-bottom: 32px;
88
+ min-height: 140px;
89
+ border: 1px dashed #ccc; /* DEBUG: Show row boundaries */
90
  }
91
 
92
+ .timeline-row:last-child {
93
+ margin-bottom: 0;
 
 
 
 
94
  }
95
 
96
+ .timeline-row.reverse {
97
+ flex-direction: row-reverse;
98
+ background: rgba(255, 0, 0, 0.05); /* DEBUG: Highlight reverse rows */
99
  }
100
 
101
+ .month-item {
102
+ flex: 1;
103
+ display: flex;
104
+ flex-direction: column;
105
+ align-items: center;
106
+ position: relative;
107
+ padding: 0 15px;
108
+ border: 1px dotted #999; /* DEBUG: Show month boundaries */
109
+ background: rgba(0, 255, 0, 0.02); /* DEBUG: Subtle green background */
110
+ }
111
+
112
+ .month-arrow {
113
+ width: 0;
114
+ height: 0;
115
+ border-top: 6px solid transparent;
116
+ border-bottom: 6px solid transparent;
117
+ border-left: 10px solid #ff6b6b; /* DEBUG: Make arrows red and visible */
118
+ position: relative;
119
+ z-index: 2;
120
  margin-bottom: 12px;
121
+ transition: all 0.2s ease;
122
+ }
123
+
124
+ .timeline-row.reverse .month-arrow {
125
+ border-left: none;
126
+ border-right: 10px solid #ff6b6b; /* DEBUG: Red arrows for reverse too */
127
+ }
128
+
129
+ .month-item:hover .month-arrow {
130
+ border-left-color: #333;
131
+ transform: scale(1.2); /* DEBUG: Make hover more obvious */
132
+ }
133
+
134
+ .timeline-row.reverse .month-item:hover .month-arrow {
135
+ border-right-color: #333;
136
+ border-left: none;
137
+ transform: scale(1.2);
138
+ }
139
+
140
+ .month-label {
141
+ font-weight: 500;
142
+ font-size: 13px;
143
+ margin-bottom: 16px;
144
+ color: #666;
145
  text-transform: uppercase;
146
  letter-spacing: 0.5px;
147
+ background: yellow; /* DEBUG: Make labels highly visible */
148
+ padding: 2px 4px;
149
  }
150
 
151
+ .month-item.year-marker .month-label {
152
+ font-weight: 600;
 
153
  font-size: 14px;
154
+ color: #1a1a1a;
155
+ background: #f0f0f0;
156
+ padding: 4px 12px;
157
+ border-radius: 12px;
158
  }
159
 
160
+ .month-item.year-marker .month-arrow {
161
+ border-left-color: #1a1a1a;
162
  }
163
 
164
+ .timeline-row.reverse .month-item.year-marker .month-arrow {
165
+ border-right-color: #1a1a1a;
166
+ border-left: none;
167
+ }
168
+
169
+ .events-list {
170
+ display: flex;
171
+ flex-direction: column;
172
+ gap: 6px;
173
+ max-width: 180px;
174
+ background: rgba(0, 0, 255, 0.1); /* DEBUG: Blue background for events */
175
+ padding: 4px;
176
+ border-radius: 4px;
177
+ }
178
+
179
+ .event {
180
+ font-size: 12px;
181
+ display: flex;
182
+ align-items: flex-start;
183
+ gap: 6px;
184
+ padding: 3px 0;
185
+ color: #4a4a4a;
186
+ transition: all 0.2s ease;
187
+ background: white; /* DEBUG: White background for individual events */
188
+ margin: 1px;
189
+ padding: 2px 4px;
190
+ border-radius: 2px;
191
+ }
192
+
193
+ .event:hover {
194
+ transform: translateX(2px);
195
+ background: #ffeb3b; /* DEBUG: Yellow on hover */
196
+ }
197
+
198
+ .event a {
199
  text-decoration: none;
200
  color: inherit;
201
+ line-height: 1.4;
202
+ }
203
+
204
+ .event a:hover {
205
+ color: #1a1a1a;
206
+ }
207
+
208
+ .timeline-row::before {
209
+ content: '';
210
+ position: absolute;
211
+ top: 8px;
212
+ left: 0;
213
+ right: 0;
214
+ height: 1px;
215
+ background: #e0e0e0;
216
+ z-index: 1;
217
+ }
218
+
219
+ .vertical-connector {
220
+ position: absolute;
221
+ width: 1px;
222
+ height: calc(100% + 32px);
223
+ background: #e0e0e0;
224
+ top: 8px;
225
+ z-index: 1;
226
+ }
227
+
228
+ .connector-right {
229
+ right: 0;
230
+ }
231
+
232
+ .connector-left {
233
+ left: 0;
234
  }
235
 
236
+ /* DEBUG: Show what's happening */
237
+ .debug-info {
238
+ background: #fff3cd;
239
+ padding: 10px;
240
+ margin: 20px 0;
241
+ border-radius: 5px;
242
+ font-family: monospace;
243
+ font-size: 12px;
244
  }
245
 
246
  @media (max-width: 768px) {
247
+ .snake-timeline {
248
+ padding: 20px;
249
  }
250
 
251
+ .month-item {
252
+ padding: 0 8px;
253
+ }
254
+
255
+ .events-list {
256
+ max-width: 120px;
257
+ }
258
+
259
+ .event {
260
+ font-size: 11px;
261
+ }
262
+
263
+ .month-label {
264
+ font-size: 11px;
265
  }
266
  }
267
  </style>
268
 
269
+ <div class="page-container">
270
+ <h2>Snake Timeline Debug Version</h2>
271
+
272
+ <div class="debug-info" id="debug-output">
273
+ Debug info will appear here...
274
+ </div>
275
+
276
+ <div class="snake-timeline" id="timeline-container">
277
+ <p>Loading snake timeline...</p>
278
+ </div>
279
  </div>
280
 
281
  <script>
282
+ // Debug function
283
+ function debugLog(message) {
284
+ console.log(message);
285
+ const debugDiv = document.getElementById('debug-output');
286
+ if (debugDiv) {
287
+ debugDiv.innerHTML += message + '<br>';
288
+ }
289
+ }
290
+
291
+ debugLog('🐍 Starting Snake Timeline Debug...');
292
+
293
+ // Your original release data (simplified for testing)
294
  const releaseData = [
 
295
  { month: "2024-01", name: "🔥 Warming up", link: null },
296
  { month: "2024-02", name: "⚙️ Nanotron Release", link: "#" },
297
  { month: "2024-02", name: "⛅ Lighteval Release", link: "#" },
 
 
 
298
  { month: "2024-03", name: "🪁 Zephyr Gemma", link: "#" },
299
  { month: "2024-03", name: "🪐 Cosmopedia", link: "#" },
300
  { month: "2024-04", name: "🍷 FineWeb", link: "#" },
301
  { month: "2024-04", name: "🕵️ JAT Agent", link: "#" },
 
 
 
302
  { month: "2024-05", name: "🦾 LeRobot Release", link: "#" },
303
+ { month: "2024-06", name: "🍷 FineWeb Report", link: "#" }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
304
  ];
305
 
306
+ debugLog(`📊 Loaded ${releaseData.length} data items`);
307
+
308
  function parseMonth(monthStr) {
309
  const [year, month] = monthStr.split('-');
310
  return {
311
  year: parseInt(year),
312
  month: parseInt(month),
313
+ monthName: new Date(year, month - 1).toLocaleString('en', { month: 'short' }).toUpperCase()
314
  };
315
  }
316
 
 
325
  return grouped;
326
  }
327
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
328
  function generateTimeline() {
329
+ debugLog('🔨 generateTimeline() called');
330
 
331
  const container = document.getElementById('timeline-container');
332
  if (!container) {
333
+ debugLog(' ERROR: timeline-container not found!');
334
  return;
335
  }
 
 
 
336
 
337
+ debugLog(' Container found');
338
+
339
+ const groupedData = groupByMonth(releaseData);
340
+ const months = Object.keys(groupedData).sort();
341
 
342
+ debugLog(`📅 Processing months: ${months.join(', ')}`);
343
+
344
+ // Group months into rows of 4
345
+ const rows = [];
346
+ for (let i = 0; i < months.length; i += 4) {
347
+ rows.push(months.slice(i, i + 4));
348
+ }
349
+
350
+ debugLog(`📏 Created ${rows.length} rows`);
351
+
352
+ container.innerHTML = '';
353
+
354
+ rows.forEach((row, rowIndex) => {
355
+ debugLog(`🏗️ Building row ${rowIndex + 1}: [${row.join(', ')}]`);
356
+
357
+ const isReverse = rowIndex % 2 === 1;
358
+ const rowDiv = document.createElement('div');
359
+ rowDiv.className = `timeline-row${isReverse ? ' reverse' : ''}`;
360
 
361
+ const displayRow = isReverse ? [...row].reverse() : row;
 
362
 
363
+ displayRow.forEach((monthKey, monthIndex) => {
364
  const monthData = parseMonth(monthKey);
365
+ const events = groupedData[monthKey] || [];
366
 
367
+ debugLog(` 📦 Adding month ${monthKey} with ${events.length} events`);
 
368
 
369
+ const monthDiv = document.createElement('div');
370
+ const isYearMarker = monthData.month === 1;
371
+ monthDiv.className = `month-item${isYearMarker ? ' year-marker' : ''}`;
372
+
373
+ const arrowDiv = document.createElement('div');
374
+ arrowDiv.className = 'month-arrow';
375
+
376
+ const labelDiv = document.createElement('div');
377
+ labelDiv.className = 'month-label';
378
+ labelDiv.textContent = isYearMarker ?
379
+ `${monthData.monthName} ${monthData.year}` :
380
+ monthData.monthName;
381
+
382
+ const eventsDiv = document.createElement('div');
383
+ eventsDiv.className = 'events-list';
384
+
385
  events.forEach(event => {
386
+ const eventDiv = document.createElement('div');
387
+ eventDiv.className = 'event';
388
+
389
  if (event.link && event.link !== '#') {
390
+ const link = document.createElement('a');
391
+ link.href = event.link;
392
+ link.target = '_blank';
393
+ link.textContent = event.name;
394
+ eventDiv.appendChild(link);
395
  } else {
396
+ eventDiv.textContent = event.name;
397
  }
398
+
399
+ eventsDiv.appendChild(eventDiv);
400
  });
401
+
402
+ monthDiv.appendChild(arrowDiv);
403
+ monthDiv.appendChild(labelDiv);
404
+ monthDiv.appendChild(eventsDiv);
405
+ rowDiv.appendChild(monthDiv);
406
  });
407
+
408
+ // Add connector if not the last row
409
+ if (rowIndex < rows.length - 1) {
410
+ const connectorDiv = document.createElement('div');
411
+ connectorDiv.className = `vertical-connector ${isReverse ? 'connector-left' : 'connector-right'}`;
412
+ rowDiv.appendChild(connectorDiv);
413
+ debugLog(` 🔗 Added connector (${isReverse ? 'left' : 'right'})`);
414
+ }
415
+
416
+ container.appendChild(rowDiv);
417
  });
418
 
419
+ debugLog('🎉 Snake timeline generated successfully!');
420
+ debugLog(`📊 Final stats: ${rows.length} rows, ${months.length} months total`);
421
  }
422
 
423
+ // Multiple initialization approaches
424
+ function initSnakeTimeline() {
425
+ debugLog('🚀 Initializing snake timeline...');
426
  generateTimeline();
 
 
 
 
 
 
427
  }
428
 
429
+ // Try different timing approaches
430
+ setTimeout(() => {
431
+ debugLog('⏰ Timeout 100ms trigger');
432
+ initSnakeTimeline();
433
+ }, 100);
434
+
435
+ setTimeout(() => {
436
+ debugLog('⏰ Timeout 500ms trigger');
437
+ if (document.getElementById('timeline-container').innerHTML.includes('Loading')) {
438
+ debugLog('🔄 Timeline still loading, trying again...');
439
+ initSnakeTimeline();
440
+ }
441
+ }, 500);
442
+
443
  if (document.readyState === 'loading') {
444
+ debugLog('⏳ DOM still loading, waiting for DOMContentLoaded...');
445
+ document.addEventListener('DOMContentLoaded', () => {
446
+ debugLog('✅ DOMContentLoaded fired');
447
+ initSnakeTimeline();
448
+ });
449
  } else {
450
+ debugLog('⚡ DOM already loaded');
451
+ initSnakeTimeline();
452
  }
453
  </script>