Spaces:
Running
Running
| // get url of current tab | |
| chrome.tabs.query({ active: true, currentWindow: true }, async function (tabs) { | |
| const tab = tabs[0]; | |
| const url = tab.url; | |
| const prefix = 'https://huggingface.co/'; | |
| if (!url.startsWith(prefix)) { | |
| // show error message in .message | |
| const message = document.querySelector('.container'); | |
| message.innerHTML = '<h2>Not a Hugging Face page.</h2>'; | |
| return; | |
| } | |
| // get project type and path | |
| const [type, path] = getProjectTypeAndPath(url); | |
| if (type === null) { | |
| // show error message in .message | |
| const message = document.querySelector('.container'); | |
| message.innerHTML = '<h2>No project found.</h2>'; | |
| return; | |
| } | |
| // get project info | |
| const res = await fetch(`https://huggingface.co/api/${type}/${path}/likers?expand[]=likeAt`) | |
| const likers = await res.json() | |
| let likeHistory = transformLikesData(likers) | |
| if (likeHistory.length > 40) { | |
| // sample 20 points | |
| const sampledLikeHistory = [] | |
| const step = Math.floor(likeHistory.length / 20) | |
| for (let i = 0; i < likeHistory.length; i += step) { | |
| sampledLikeHistory.push(likeHistory[i]) | |
| } | |
| // Add the last point if it's not included | |
| if (sampledLikeHistory[sampledLikeHistory.length - 1].x !== likeHistory[likeHistory.length - 1].x) { | |
| sampledLikeHistory.push(likeHistory[likeHistory.length - 1]) | |
| } | |
| likeHistory = sampledLikeHistory | |
| } | |
| // if likeHistory is empty, show error message | |
| if (likeHistory.length === 0) { | |
| const message = document.querySelector('.container'); | |
| message.innerHTML = '<h2>No likes found.</h2>'; | |
| return | |
| } | |
| const svg = document.querySelector('.line-chart'); | |
| new chartXkcd.XY(svg, { | |
| title: 'Like History', | |
| xLabel: 'Time', | |
| yLabel: 'Likes', | |
| data: { | |
| datasets: [{ | |
| label: path, | |
| data: likeHistory, | |
| }], | |
| }, | |
| options: { | |
| // unxkcdify: true, | |
| showLegend: false, | |
| xTickCount: 3, | |
| yTickCount: 4, | |
| legendPosition: chartXkcd.config.positionType.upLeft, | |
| showLine: true, | |
| timeFormat: 'MM/DD/YYYY', | |
| dotSize: 0.5, | |
| dataColors: [ | |
| "#FBBF24", // Warm Yellow | |
| "#60A5FA", // Light Blue | |
| "#14B8A6", // Teal | |
| "#A78BFA", // Soft Purple | |
| "#FF8C00", // Orange | |
| "#64748B", // Slate Gray | |
| "#FB7185", // Coral Pink | |
| "#6EE7B7", // Mint Green | |
| "#2563EB", // Deep Blue | |
| "#374151" // Charcoal | |
| ] | |
| }, | |
| }); | |
| }); | |
| function getProjectTypeAndPath(url) { | |
| // Define the possible project types | |
| // Create a URL object to parse the given url | |
| const parsedUrl = new URL(url); | |
| // Extract the pathname from the URL and split it into parts | |
| const pathParts = parsedUrl.pathname.split('/').filter(part => part.length > 0); | |
| if (pathParts.length < 2) { | |
| return [null, null]; | |
| } | |
| // The project type should be the first part of the path | |
| const type = pathParts[0]; | |
| console.log(type); | |
| if (type !== 'spaces' && type !== 'datasets') { | |
| // If the type is not spaces or datasets, it should be models | |
| const actualType = 'models'; | |
| const path = pathParts.slice(0, 2).join('/'); | |
| return [actualType, path]; | |
| } else { | |
| const path = pathParts.slice(1, 3).join('/'); | |
| return [type, path]; | |
| } | |
| } | |
| function transformLikesData(likesData) { | |
| // Step 1 | |
| likesData.sort((a, b) => new Date(a.likedAt) - new Date(b.likedAt)); | |
| // Step 2 | |
| const cumulativeLikes = {}; | |
| let cumulativeCount = 0; | |
| // Step 3 | |
| likesData.forEach(like => { | |
| const date = like.likedAt | |
| cumulativeCount++; | |
| cumulativeLikes[date] = cumulativeCount; | |
| }); | |
| // Step 4 | |
| const transformedData = Object.keys(cumulativeLikes).map(date => ({ | |
| x: date, | |
| y: cumulativeLikes[date].toString() | |
| })); | |
| return transformedData; | |
| } |