8 | Experimental tl;dr summaries for datasets on the Hugging Face Hub!
9 |
10 |
11 | The Hugging Face tl;dr Chrome extension enhances your browsing experience on the Hugging Face Hub by providing concise tl;dr summaries for datasets. With this extension, you can quickly grasp the key points of a dataset when navigating through the Hugging Face Hub, making it easier to identify relevant datasets for your machine learning projects.
12 |
13 |
14 |
15 |
16 |
17 | **Disclaimer**: *This extension is an experimental project aimed at gauging the usefulness of tl;dr summaries for datasets on the Hugging Face Hub. The summaries are generated using the [mistralai/Mixtral-8x7B-Instruct-v0.1](https://huggingface.co/mistralai/Mixtral-8x7B-Instruct-v0.1) model, and the quality of the summaries may vary. If you encounter any issues or have suggestions for improvements, please open an issue before submitting a pull request.*
18 |
19 | ## ✨ Features
20 |
21 | - 🤖 Automatically fetches and displays tl;dr summaries for datasets on the Hugging Face Hub
22 | - 📜 Summaries are added as a compact section below each dataset card, making them easily noticeable
23 | - 👍 Provide feedback on the usefulness of the summaries through a simple rating system
24 |
25 |
26 |
27 | https://github.com/davanstrien/huggingface-tldr/assets/8995957/9f30886e-0917-4c42-b7c7-b940172eae4e
28 |
29 |
30 |
31 | ## 🚀 Getting Started
32 |
33 |
34 | Installation
35 |
36 | 1. Clone this repository or download the source code as a ZIP file using this [link](https://github.com/davanstrien/huggingface-tldr/archive/refs/heads/main.zip).
37 | 2. If you downloaded the source code as a ZIP file, extract the contents to a directory on your computer.
38 | 3. Open Google Chrome and navigate to `chrome://extensions`.
39 | 4. Enable "Developer mode" using the toggle switch in the top right corner.
40 | 5. Click on "Load unpacked" and select the `plugin` directory from the source code you downloaded.
41 | 6. The Hugging Face tl;dr extension should now be installed and active in your Chrome browser.
42 |
43 |
44 |
45 |
46 |
47 | Usage
48 |
49 | 1. Navigate to the Hugging Face Hub and browse through the datasets.
50 | 2. The extension will automatically fetch and display the tl;dr summaries below each dataset card.
51 | 3. If you find a summary helpful, give it an upvote. If not, you can downvote it to provide feedback.
52 | 4. To enable voting functionality and contribute to the improvement of the summaries, you need to provide a valid token in the extension settings. You can obtain a token from your Hugging Face account settings.
53 |
54 |
55 |
56 | ## 🔧 Extension Settings
57 |
58 | To enable voting functionality and provide feedback on the tl;dr summaries, you need to provide a valid Hugging Face token in the extension settings. Follow the steps below to configure the extension settings:
59 |
60 | 1. Right-click on the extension icon in the Chrome toolbar.
61 | 2. Select "Options" from the context menu.
62 | 3. In the settings page, enter your Hugging Face token in the provided input field.
63 | 4. Click on the "Save" button to store the token.
64 |
65 |
66 | 
67 |
68 | Add a read-only token from your Hugging Face account settings to enable voting functionality.
69 | 
70 |
71 | Note: The token is securely stored in the extension's local storage and is only used for authenticating requests to the Hugging Face API when submitting votes. You only need to provide a read-only token, as the extension does not require write access to your Hugging Face account.
72 |
73 | ## Disclaimer
74 |
75 | This extension is an experimental project. The tl;dr summaries are currently generated using [mistralai/Mixtral-8x7B-Instruct-v0.1](https://huggingface.co/mistralai/Mixtral-8x7B-Instruct-v0.1)
76 |
77 | ## Contributing
78 |
79 | Contributions to the Hugging Face tl;dr extension are welcome! If you encounter any issues or have suggestions for improvements, please open an issue before submitting a pull request.
80 |
--------------------------------------------------------------------------------
/assets/banner.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/davanstrien/huggingface-tldr/de9ca42f5b0649a1587e69966f77264247ceb181/assets/banner.png
--------------------------------------------------------------------------------
/assets/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/davanstrien/huggingface-tldr/de9ca42f5b0649a1587e69966f77264247ceb181/assets/screenshot.png
--------------------------------------------------------------------------------
/assets/settings-page.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/davanstrien/huggingface-tldr/de9ca42f5b0649a1587e69966f77264247ceb181/assets/settings-page.png
--------------------------------------------------------------------------------
/assets/settings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/davanstrien/huggingface-tldr/de9ca42f5b0649a1587e69966f77264247ceb181/assets/settings.png
--------------------------------------------------------------------------------
/plugin/background.js:
--------------------------------------------------------------------------------
1 | chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
2 | if (request.action === "getToken") {
3 | chrome.storage.local.get("token", (data) => {
4 | sendResponse({ token: data.token });
5 | });
6 | return true; // Required to use sendResponse asynchronously
7 | }
8 | });
9 |
--------------------------------------------------------------------------------
/plugin/content.css:
--------------------------------------------------------------------------------
1 | .dataset-tooltip {
2 | display: none;
3 | position: absolute;
4 | background-color: #000;
5 | color: #fff;
6 | padding: 5px;
7 | border-radius: 4px;
8 | font-size: 12px;
9 | z-index: 1000;
10 | max-width: 200px;
11 | word-wrap: break-word;
12 | top: 100%;
13 | left: 0;
14 | }
15 |
16 | .overview-card-wrapper header {
17 | position: relative;
18 | }
19 |
20 | .hover-icon {
21 | position: absolute;
22 | top: 0;
23 | right: 0;
24 | font-size: 16px;
25 | color: #888;
26 | cursor: pointer;
27 | padding: 5px;
28 | }
29 |
30 | .hover-icon:hover {
31 | color: #000;
32 | }
33 |
34 | .vote-button {
35 | background-color: transparent;
36 | border: none;
37 | cursor: pointer;
38 | font-size: 16px;
39 | margin-right: 5px;
40 | }
41 |
42 | .vote-button:hover {
43 | opacity: 0.8;
44 | }
45 |
46 | .vote-button.voted {
47 | opacity: 1;
48 | }
49 |
50 | .vote-button:not(.voted) {
51 | opacity: 0.5;
52 | }
--------------------------------------------------------------------------------
/plugin/content.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Generates a unique user ID and stores it in the localStorage.
3 | * If a user ID already exists, retrieves and returns it.
4 | * @returns {string} The generated or existing user ID.
5 | */
6 | function generateUserId() {
7 | const existingUserId = localStorage.getItem("userId");
8 | if (existingUserId) {
9 | return existingUserId;
10 | } else {
11 | const newUserId = Math.random().toString(36).substring(2, 15);
12 | localStorage.setItem("userId", newUserId);
13 | return newUserId;
14 | }
15 | }
16 |
17 | function isTokenAvailable(callback) {
18 | try {
19 | chrome.runtime.sendMessage({ action: "getToken" }, (response) => {
20 | if (chrome.runtime.lastError) {
21 | console.error("Error in sendMessage:", chrome.runtime.lastError);
22 | callback(false);
23 | } else {
24 | callback(!!response.token);
25 | }
26 | });
27 | } catch (error) {
28 | console.error("Error in isTokenAvailable:", error);
29 | callback(false);
30 | }
31 | }
32 |
33 | function addDatasetDescription(descriptions, box) {
34 | const datasetName = box.querySelector("h4").textContent.trim();
35 | const description = descriptions[datasetName];
36 |
37 | if (!box.querySelector(".tl-dr-description")) {
38 | let displayText = "No tl;dr description currently available";
39 | if (description) {
40 | displayText = "tl;dr " + description;
41 | }
42 |
43 | const descriptionElement = document.createElement("div");
44 | descriptionElement.innerHTML = displayText;
45 | descriptionElement.style.marginTop = "5px";
46 | descriptionElement.style.marginLeft = "5px";
47 | descriptionElement.style.fontSize = "12px";
48 | descriptionElement.style.color = "#888";
49 | descriptionElement.classList.add("tl-dr-description");
50 |
51 | const voteText = document.createElement("div");
52 | voteText.textContent = "Is this tl;dr summary useful?";
53 | voteText.style.marginTop = "5px";
54 | voteText.style.marginLeft = "5px";
55 | voteText.style.fontSize = "12px";
56 | voteText.style.color = "#888";
57 | voteText.style.fontWeight = "bold";
58 |
59 | const voteButtons = document.createElement("div");
60 | voteButtons.style.marginTop = "5px";
61 | voteButtons.style.marginLeft = "5px";
62 |
63 | const upvoteButton = document.createElement("button");
64 | upvoteButton.textContent = "👍";
65 | upvoteButton.classList.add("vote-button");
66 | upvoteButton.dataset.vote = "1";
67 | upvoteButton.dataset.dataset = datasetName;
68 |
69 | const downvoteButton = document.createElement("button");
70 | downvoteButton.textContent = "👎";
71 | downvoteButton.classList.add("vote-button");
72 | downvoteButton.dataset.vote = "0";
73 | downvoteButton.dataset.dataset = datasetName;
74 |
75 | voteButtons.appendChild(upvoteButton);
76 | voteButtons.appendChild(downvoteButton);
77 |
78 | box.appendChild(descriptionElement);
79 | box.appendChild(voteText);
80 | box.appendChild(voteButtons);
81 | }
82 | }
83 |
84 | function isMainDatasetsPage() {
85 | const url = window.location.href;
86 | const mainPagePattern = /^https:\/\/huggingface\.co\/datasets(?:\?.*)?$/;
87 | return mainPagePattern.test(url);
88 | }
89 |
90 | function fetchAndAddDescriptions() {
91 | if (isMainDatasetsPage()) {
92 | fetch(
93 | "https://huggingface.co/datasets/davanstrien/descriptions/resolve/main/data.json?download=true"
94 | )
95 | .then((response) => response.json())
96 | .then((data) => {
97 | const datasetBoxes = document.querySelectorAll(
98 | ".overview-card-wrapper"
99 | );
100 | datasetBoxes.forEach((box) => {
101 | addDatasetDescription(data, box);
102 | });
103 | })
104 | .catch((error) => {
105 | console.error("Error fetching dataset descriptions:", error);
106 | });
107 | }
108 | }
109 | let previousVotes = {};
110 |
111 | document.addEventListener("click", (event) => {
112 | if (event.target.classList.contains("vote-button") && isMainDatasetsPage()) {
113 | const { vote } = event.target.dataset;
114 | const datasetName = event.target.dataset.dataset;
115 |
116 | // Check if the user has already voted for the same option
117 | if (previousVotes[datasetName] === parseInt(vote)) {
118 | // Display an "already voted" message
119 | const alreadyVotedMessage = document.createElement("div");
120 | alreadyVotedMessage.textContent = "Already voted!";
121 | alreadyVotedMessage.style.marginTop = "5px";
122 | alreadyVotedMessage.style.color = "#ff0000";
123 | alreadyVotedMessage.style.fontSize = "12px";
124 | event.target.parentNode.appendChild(alreadyVotedMessage);
125 |
126 | // Remove the "already voted" message after 2 seconds
127 | setTimeout(() => {
128 | alreadyVotedMessage.remove();
129 | }, 2000);
130 |
131 | return; // Exit the event listener
132 | }
133 |
134 | const overviewCardWrapper = event.target.closest(".overview-card-wrapper");
135 | if (overviewCardWrapper) {
136 | const descriptionElement =
137 | overviewCardWrapper.querySelector(".tl-dr-description");
138 | const description = descriptionElement
139 | ? descriptionElement.textContent.trim()
140 | : "No tl;dr description available";
141 |
142 | const userID = generateUserId(); // Generate or retrieve the user ID
143 |
144 | const payload = {
145 | dataset: datasetName,
146 | description: description,
147 | vote: parseInt(vote),
148 | userID: userID, // Include the user ID in the payload
149 | };
150 |
151 | // Check if the token is available
152 | isTokenAvailable((tokenAvailable) => {
153 | if (tokenAvailable) {
154 | // Retrieve the token from the background script
155 | chrome.runtime.sendMessage({ action: "getToken" }, (response) => {
156 | const { token } = response;
157 | // Token is available, proceed with vote submission
158 | fetch("https://davanstrien-dataset-tldr.hf.space/vote", {
159 | method: "POST",
160 | headers: {
161 | "Content-Type": "application/json",
162 | Authorization: `${token}`,
163 | },
164 | body: JSON.stringify(payload),
165 | })
166 | .then((response) => response.json())
167 | .then((data) => {
168 | console.log("Vote submitted successfully:", data);
169 |
170 | // Update the previous vote for the dataset
171 | previousVotes[datasetName] = parseInt(vote);
172 |
173 | // Set the opacity of all vote buttons to 0.5
174 | const voteButtons =
175 | overviewCardWrapper.querySelectorAll(".vote-button");
176 | voteButtons.forEach((button) => {
177 | button.style.opacity = "0.5";
178 | button.classList.remove("voted");
179 | });
180 |
181 | // Set the opacity of the current voted button to 1 and add the "voted" class
182 | event.target.style.opacity = "1";
183 | event.target.classList.add("voted");
184 |
185 | // Display a success message
186 | const successMessage = document.createElement("div");
187 | successMessage.textContent = "Vote registered!";
188 | successMessage.style.marginTop = "5px";
189 | successMessage.style.color = "#4caf50";
190 | successMessage.style.fontSize = "12px";
191 | overviewCardWrapper.appendChild(successMessage);
192 |
193 | // Remove the success message after 2 seconds
194 | setTimeout(() => {
195 | successMessage.remove();
196 | }, 2000);
197 | })
198 | .catch((error) => {
199 | console.error("Error submitting vote:", error);
200 | });
201 | });
202 | } else {
203 | // Token is missing, display an alert or message to the user
204 | alert(
205 | "Please provide the token in the extension settings to submit your vote."
206 | );
207 | }
208 | });
209 | }
210 | }
211 | });
212 |
213 | const observer = new MutationObserver((mutations) => {
214 | if (isMainDatasetsPage()) {
215 | mutations.forEach((mutation) => {
216 | if (mutation.type === "childList") {
217 | const { addedNodes } = mutation;
218 | addedNodes.forEach((node) => {
219 | if (
220 | node.nodeType === Node.ELEMENT_NODE &&
221 | node.classList.contains("overview-card-wrapper")
222 | ) {
223 | fetchAndAddDescriptions();
224 | }
225 | });
226 | }
227 | });
228 | }
229 | });
230 |
231 | const observerOptions = {
232 | childList: true,
233 | subtree: true,
234 | };
235 |
236 | const targetNode = document.body;
237 | observer.observe(targetNode, observerOptions);
238 |
239 | // Add descriptions to the initial set of dataset boxes
240 | fetchAndAddDescriptions();
241 |
--------------------------------------------------------------------------------
/plugin/icons/icon128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/davanstrien/huggingface-tldr/de9ca42f5b0649a1587e69966f77264247ceb181/plugin/icons/icon128.png
--------------------------------------------------------------------------------
/plugin/icons/icon16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/davanstrien/huggingface-tldr/de9ca42f5b0649a1587e69966f77264247ceb181/plugin/icons/icon16.png
--------------------------------------------------------------------------------
/plugin/icons/icon32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/davanstrien/huggingface-tldr/de9ca42f5b0649a1587e69966f77264247ceb181/plugin/icons/icon32.png
--------------------------------------------------------------------------------
/plugin/icons/icon48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/davanstrien/huggingface-tldr/de9ca42f5b0649a1587e69966f77264247ceb181/plugin/icons/icon48.png
--------------------------------------------------------------------------------
/plugin/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "manifest_version": 3,
3 | "name": "Hugging Face tl;dr",
4 | "version": "1.0",
5 | "description": "Adds tl;dr descriptions to datasets on the Hugging Face Hub.",
6 | "permissions": [
7 | "activeTab",
8 | "storage"
9 | ],
10 | "options_page": "settings.html",
11 | "host_permissions": [
12 | "https://huggingface.co/datasets/davanstrien/*"
13 | ],
14 | "content_scripts": [
15 | {
16 | "matches": [
17 | "https://huggingface.co/datasets*"
18 | ],
19 | "js": [
20 | "content.js"
21 | ],
22 | "css": [
23 | "content.css"
24 | ]
25 | }
26 | ],
27 | "icons": {
28 | "16": "icons/icon16.png",
29 | "32": "icons/icon32.png",
30 | "48": "icons/icon48.png",
31 | "128": "icons/icon128.png"
32 | },
33 | "background": {
34 | "service_worker": "background.js"
35 | }
36 | }
--------------------------------------------------------------------------------
/plugin/settings.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Hugging Face tl;dr Extension Settings
5 |
6 |
7 |
Extension Settings
8 |
9 | In order to submit feedback on the ratings you need to provide a read only
10 | Hugging Face Hub token. You can get a read only token from
11 | this page.
12 |