schoolbox web extension :)
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: snippets options page svelte

Co-authored-by: BC-548 <BC-548@users.noreply.github.com>

Willow 60f4423d a076e9d8

+112 -224
+1 -1
src/background.js
··· 22 22 snippets: { 23 23 toggle: true, 24 24 enabled: ["hide-pfp"], 25 - user: [], 25 + user: {}, 26 26 }, 27 27 plugins: { 28 28 toggle: true,
+4 -4
src/options/components/Title.svelte
··· 1 1 <script> 2 - import browser from 'webextension-polyfill'; 2 + import browser from "webextension-polyfill"; 3 3 4 4 export let data = {}; 5 - export let title = ''; 6 - export let key = ''; 5 + export let title = ""; 6 + export let key = ""; 7 7 8 8 function setStorage() { 9 9 browser.storage.local.set({ [key]: data }); ··· 15 15 <input 16 16 id="theme-toggle" 17 17 type="checkbox" 18 - class="absolute left-1/2 -translate-x-1/2 w-full h-full peer appearance-none rounded-md" 18 + class="peer slider-input" 19 19 bind:checked={data.toggle} 20 20 on:change={setStorage} /> 21 21 <span class="slider big"></span>
+1 -1
src/options/pages/Home.svelte
··· 50 50 bind:checked={settings.updates[update]} 51 51 on:change={() => toggleUpdate()} 52 52 type="checkbox" 53 - class="peer absolute left-1/2 h-full w-full -translate-x-1/2 appearance-none rounded-md" /> 53 + class="peer slider-input" /> 54 54 <span class="slider small"></span> 55 55 </label> 56 56 {/each}
+1 -1
src/options/pages/Plugins.svelte
··· 48 48 plugin-id={plugin.id} 49 49 bind:checked={plugin.toggled} 50 50 type="checkbox" 51 - class="plugin-toggle absolute left-1/2 -translate-x-1/2 w-full h-full peer appearance-none rounded-md" 51 + class="peer slider-input" 52 52 on:change={() => togglePlugin(plugin.id, plugin.toggled)} /> 53 53 <span class="slider small"></span> 54 54 </label>
+101 -215
src/options/pages/Snippets.svelte
··· 1 1 <script> 2 2 import { onMount } from "svelte"; 3 3 import browser from "webextension-polyfill"; 4 - import Title from "../components/Title.svelte" 4 + import Title from "../components/Title.svelte"; 5 5 6 6 let snippets = { 7 7 toggle: false, 8 + user: [], 8 9 }; 9 - 10 - let populatedSnippets = []; 10 + let snippetURL = ""; 11 + let defaultSnippets = []; 11 12 12 13 onMount(async () => { 14 + const response = await fetch("/snippets.json"); 15 + const data = await response.json(); 13 16 const storage = await browser.storage.local.get(); 14 17 snippets = storage.snippets; 15 18 console.log("snippets", snippets); 16 - }); 17 - // // add snippet listener 18 - // document.getElementById("snippet-form").addEventListener("submit", function (event) { 19 - // event.preventDefault(); 20 - // let url = document.getElementById("snippet-input").value; 21 - // if (url) { 22 - // console.log(url); 23 19 24 - // // if is link, send request 25 - // if (url.startsWith("http") && url.includes("gist.github.com")) { 26 - // console.log("Downloading snippet..."); 27 - // // send request to get snippet 28 - // fetch(url + "/raw") 29 - // .then((response) => response.text()) 30 - // .then((cssSnippet) => { 31 - // console.log(cssSnippet); 32 - // let nameMatch = cssSnippet.match(/\/\*\s*name:\s*(.*?)\s*\*\//); 33 - // let descriptionMatch = cssSnippet.match(/\/\*\s*description:\s*(.*?)\s*\*\//); 34 - // let snippetName = nameMatch ? nameMatch[1] : null; 35 - // let snippetDescription = descriptionMatch ? descriptionMatch[1] : null; 36 - // let sections = url.split("/"); 37 - // let snippetAuthor = sections[3]; 38 - // let snippetID = sections[sections.length - 1].split(".")[0]; 39 - 40 - // // example URL: https://gist.githubusercontent.com/42Willow/98435ecb3d871ecf14659936cdf36105 41 - // console.log(`author: ${snippetAuthor}`); // Outputs: 42Willow 42 - // console.log(`id: ${snippetID}`); // Outputs: hide-iframes 43 - // console.log(`name: ${snippetName}`); // Outputs: Hide homepage iframe 44 - // console.log(`description: ${snippetDescription}`); // Outputs: Useful for use with dark mode themes 45 - 46 - // let snip = { 47 - // [snippetID]: { 48 - // name: snippetName, 49 - // author: snippetAuthor, 50 - // description: snippetDescription, 51 - // }, 52 - // }; 53 - 54 - // if (Object.values(snip[snippetID]).includes(null)) { 55 - // alert("This snippet is missing a name or description"); 56 - // return; 57 - // } 58 - // console.log(snip); 59 - 60 - // // modify the settings to add to list of userSnippets 61 - // chrome.storage.local.get(["settings"], function (settingsData) { 62 - // let settings = settingsData.settings; 63 - // // if not already installed 64 - // if (settings.userSnippets.some((e) => e[snippetID])) { 65 - // alert("This snippet is already installed."); 66 - // return; 67 - // } 68 - // settings.userSnippets.push(snip); 69 - // chrome.storage.local.set({ settings: settings }, function () { 70 - // console.log(`Installed snippet ${snip}`); 71 - // }); 72 - // addUserSnippets(); 73 - // }); 74 - // }); 75 - // } else if (url.startsWith("http") && url.includes("gist") && url.includes("raw")) { 76 - // alert("Please use the main URL of the Gist, not the raw URL."); 77 - // } else { 78 - // alert("Please enter a valid URL."); 79 - // } 80 - // } 81 - // }); 82 - 83 - // fetch("/snippets/snippets.json") 84 - // .then((response) => response.json()) 85 - // .then((data) => { 86 - // // console.log(data); 87 - 88 - // // add snippets from the json file 89 - // addSnippets(data); 90 - 91 - // // add user installed snippets 92 - // addUserSnippets(); 93 - // }); 94 - 95 - // // Add snippets to the page 96 - // function addSnippets(data) { 97 - // chrome.storage.local.get(["settings"], function (settingsData) { 98 - // let options = document.querySelector(".snippets-container"); 99 - // let snippets = Object.entries(data); 100 - // // console.log(snippets); 101 - // // console.log(settingsData); 102 - // snippets.forEach((snippet) => { 103 - // // console.log(snippet); 104 - // let snippetId = snippet[0]; 105 - // let snippetName = snippet[1].name; 106 - // let snippetDescription = snippet[1].description; 107 - // let snippetToggled = settingsData.settings.enabledSnippets.includes(snippetId); 108 - // let option = document.createElement("div"); 109 - // option.classList.add("my-4"); 110 - // option.classList.add("group"); 111 - // option.innerHTML = ` 112 - // <label class="relative flex justify-between items-center group py-2 text-xl text-ctp-text"> 113 - // <h4 class="text-ctp-text">${snippetName}</h4> 114 - // <input snippet-id="${snippetId}" ${snippetToggled ? "checked" : ""} type="checkbox" class="snippet-toggle absolute left-1/2 -translate-x-1/2 w-full h-full peer appearance-none rounded-md" /> 115 - // <span class="w-11 h-5 flex items-center flex-shrink-0 ml-4 p-1 bg-ctp-red rounded-lg duration-500 ease-in-out peer-checked:bg-ctp-green after:w-3 after:h-3 after:bg-ctp-base after:rounded-lg after:shadow-md after:duration-300 peer-checked:after:translate-x-6 group-hover:after:translate-x-1"></span> 116 - // </label> 117 - // <div class="text-subtext0 group-hover:text-subtext1">${snippetDescription}</div> 118 - // `; 119 - // options.appendChild(option); 120 - // }); 121 - // }); 122 - // } 123 - 124 - // function addUserSnippets() { 125 - // chrome.storage.local.get(["settings"], function (settingsData) { 126 - // let settings = settingsData.settings; 127 - // let snippets = settings.userSnippets; 128 - // let options = document.querySelector(".user-snippets-container"); 129 - // // clear the container 130 - // options.innerHTML = ""; 131 - // // console.log(snippets); 132 - // if (snippets.length > 0) { 133 - // snippets.forEach((snippet) => { 134 - // let snippetId = Object.keys(snippet)[0]; 135 - // let snippetName = snippet[snippetId].name; 136 - // let snippetDescription = snippet[snippetId].description; 137 - // let snippetURL = `https://gist.github.com/${snippet[snippetId].author}/${snippetId}`; 138 - // let option = document.createElement("div"); 139 - // let snippetToggled = settingsData.settings.enabledSnippets.includes(snippetId); 140 - // option.classList.add("my-4"); 141 - // option.classList.add("group"); 142 - // option.innerHTML = ` 143 - // <label class="relative flex justify-between items-center group py-2 text-xl text-ctp-text"> 144 - // <h4 class="text-ctp-text">${snippetName}</h4> 145 - // <input snippet-id="${snippetId}" ${snippetToggled ? "checked" : ""} type="checkbox" class="snippet-toggle absolute left-1/2 -translate-x-1/2 w-full h-full peer appearance-none rounded-md" /> 146 - // <span class="w-11 h-5 flex items-center flex-shrink-0 ml-4 p-1 bg-ctp-red rounded-lg duration-500 ease-in-out peer-checked:bg-ctp-green after:w-3 after:h-3 after:bg-ctp-base after:rounded-lg after:shadow-md after:duration-300 peer-checked:after:translate-x-6 group-hover:after:translate-x-1"></span> 147 - // </label> 148 - // <div class="text-subtext0 group-hover:text-subtext1 mb-2">${snippetDescription}</div> 149 - // <a href="${snippetURL}" target="_blank"><button class="text-xs rounded-md px-1 py-0.5 bg-ctp-base text-ctp-text hover:text-crust hover:bg-ctp-pink">Gist</button></a> 150 - // <button snippet-id="${snippetId}" class="remove-snippet text-xs rounded-md px-1 py-0.5 bg-ctp-base text-ctp-text hover:text-crust hover:bg-ctp-red">Remove</button> 151 - // `; 152 - // options.appendChild(option); 153 - // }); 154 - // } 155 - 156 - // // setup toggle listeners 157 - // let snippetToggles = document.querySelectorAll(".snippet-toggle"); 158 - 159 - // snippetToggles.forEach((toggle) => { 160 - // // console.log(toggle); 161 - // toggle.addEventListener("click", toggleClicked); 162 - // }); 163 - 164 - // // setup remove listeners 165 - // let removeButtons = document.querySelectorAll(".remove-snippet"); 20 + // populate default snippets 21 + defaultSnippets = Object.entries(data).map(([snippetId, snippetData]) => { 22 + return { 23 + id: snippetId, 24 + name: snippetData.name, 25 + description: snippetData.description, 26 + path: snippetData.path, 27 + toggled: storage.snippets.enabled.includes(snippetId), 28 + }; 29 + }); 30 + // console.log(defaultSnippets); 31 + }); 166 32 167 - // removeButtons.forEach((button) => { 168 - // button.addEventListener("click", removeClicked); 169 - // }); 170 - // }); 171 - // } 33 + async function addUserSnippet() { 34 + if (!snippetURL.startsWith("http") && !snippetURL.includes("gist.github.com")) { 35 + alert("Invalid URL. Please enter a valid Gist URL."); 36 + return; 37 + } 38 + // create new user snippet from URL (snippetURL) 39 + // eg. https://gist.github.com/42Willow/e89e76ef3853e83a6439ffba42f7d273 40 + const response = await fetch(snippetURL + "/raw"); 41 + const data = await response.text(); 42 + // console.log(data); 43 + const getMatch = (snippet, regex) => { 44 + const match = snippet.match(regex); 45 + return match ? match[1] : null; 46 + }; 47 + let sections = snippetURL.split("/"); 48 + let key = sections[sections.length - 1].split(".")[0]; 172 49 173 - // function toggleClicked(event) { 174 - // // console.log(event.target); 175 - // let snippetId = event.target.getAttribute("snippet-id"); 176 - // if (event.target.checked) { 177 - // installSnippet(snippetId); 178 - // } else { 179 - // uninstallSnippet(snippetId); 180 - // } 181 - // } 50 + snippets.user[key] = { 51 + author: sections[3], 52 + name: getMatch(data, /\/\*\s*name:\s*(.*?)\s*\*\//), 53 + description: getMatch(data, /\/\*\s*description:\s*(.*?)\s*\*\//), 54 + toggled: true, 55 + }; 56 + await browser.storage.local.set({ snippets: snippets }); 57 + // console.log(snippets); 58 + } 182 59 183 - // function removeClicked(event) { 184 - // let snippetId = event.target.getAttribute("snippet-id"); 185 - // chrome.storage.local.get(["settings"], function (settingsData) { 186 - // let settings = settingsData.settings; 187 - // let index = settings.enabledSnippets.indexOf(snippetId); 188 - // if (index !== -1) { 189 - // settings.enabledSnippets.splice(index, 1); 190 - // } 191 - // settings.userSnippets.splice( 192 - // settings.userSnippets.findIndex((e) => e[snippetId]), 193 - // 1, 194 - // ); 195 - // chrome.storage.local.set({ settings: settings }, function () { 196 - // console.log(`Removed snippet ${snippetId}`); 197 - // }); 198 - // addUserSnippets(); 199 - // }); 200 - // } 201 - 202 - // function installSnippet(snippetId) { 203 - // chrome.storage.local.get(["settings"], function (settingsData) { 204 - // let settings = settingsData.settings; 205 - // settings.enabledSnippets.push(snippetId); 206 - // chrome.storage.local.set({ settings: settings }, function () { 207 - // console.log(`Installed snippet ${snippetId}`); 208 - // }); 209 - // }); 210 - // } 211 - // function uninstallSnippet(snippetId) { 212 - // chrome.storage.local.get(["settings"], function (settingsData) { 213 - // let settings = settingsData.settings; 214 - // settings.enabledSnippets.splice(settings.enabledSnippets.indexOf(snippetId), 1); 215 - // chrome.storage.local.set({ settings: settings }, function () { 216 - // console.log(`Uninstalled snippet ${snippetId}`); 217 - // }); 218 - // }); 219 - // } 60 + async function toggleSnippet(snippetId, toggled, isUser = false) { 61 + if (isUser) { 62 + snippets.user[snippetId].toggled = toggled; 63 + } else { 64 + if (toggled) { 65 + snippets.enabled.push(snippetId); 66 + } else { 67 + snippets.enabled = snippets.enabled.filter((id) => id !== snippetId); 68 + } 69 + } 70 + await browser.storage.local.set({ snippets: snippets }); 71 + } 220 72 </script> 221 73 222 74 <div id="card"> 223 75 <Title title="Snippets" data={snippets} key="snippets" /> 224 76 225 - <div class="snippets-container w-full"></div> 77 + <div class="snippets-container w-full"> 78 + {#each defaultSnippets as snippet (snippet.id)} 79 + <div class="my-4 group w-full"> 80 + <label class="relative flex justify-between items-center group py-2 text-xl text-ctp-text"> 81 + <h4 class="text-ctp-text">{snippet.name}</h4> 82 + <input 83 + snippet-id={snippet.id} 84 + bind:checked={snippet.toggled} 85 + type="checkbox" 86 + class="peer slider-input" 87 + on:change={() => toggleSnippet(snippet.id, snippet.toggled)} /> 88 + <span class="slider small"></span> 89 + </label> 90 + <div class="text-ctp-overlay1 group-hover:text-ctp-subtext0 transition-colors duration-500 ease-in-out"> 91 + {snippet.description} 92 + </div> 93 + </div> 94 + {/each} 95 + </div> 226 96 <div class="w-full"> 227 97 <h3 class="text-ctp-text my-4">User Snippets</h3> 228 98 <p class="text-ctp-overlay2 mb-4"> ··· 234 104 >. 235 105 </p> 236 106 <!-- input box to make new snippet --> 237 - <form id="snippet-form" class="w-full"> 238 - <div class="flex justify-center items-center w-full"> 239 - <input 240 - id="snippet-input" 241 - class="w-full p-2 rounded-l-xl bg-ctp-surface0 text-ctp-text" 242 - type="text" 243 - placeholder="Gist URL" /> 244 - <button id="add-snippet" class="p-2 rounded-r-xl bg-ctp-pink text-ctp-crust" type="submit">Add</button> 107 + <div class="flex justify-center items-center w-full"> 108 + <input 109 + id="snippet-input" 110 + class="w-full p-2 rounded-l-xl bg-ctp-surface0 text-ctp-text" 111 + type="text" 112 + placeholder="Gist URL" 113 + bind:value={snippetURL} /> 114 + <button class="p-2 rounded-r-xl bg-ctp-pink text-ctp-crust" type="submit" on:click={addUserSnippet}>Add</button> 115 + </div> 116 + </div> 117 + 118 + <div class="user-snippets-container w-full"> 119 + {#each Object.entries(snippets.user) as [key, snippet] (key)} 120 + <div class="my-4 group w-full"> 121 + <label class="relative flex justify-between items-center group py-2 text-xl text-ctp-text"> 122 + <h4 class="text-ctp-text">{snippet.name}</h4> 123 + <input 124 + bind:checked={snippet.toggled} 125 + type="checkbox" 126 + class="peer slider-input" 127 + on:change={() => toggleSnippet(key, snippet.toggled, true)} /> 128 + <span class="slider small"></span> 129 + </label> 130 + <div class="text-ctp-overlay1 group-hover:text-ctp-subtext0 transition-colors duration-500 ease-in-out"> 131 + {snippet.description} 132 + </div> 245 133 </div> 246 - </form> 134 + {/each} 247 135 </div> 248 - 249 - <div class="user-snippets-container w-full"></div> 250 136 </div>
+4
src/options/popup.css
··· 20 20 @apply after:h-3 after:w-3; 21 21 } 22 22 23 + .slider-input { 24 + @apply absolute left-1/2 h-full w-full -translate-x-1/2 appearance-none rounded-md; 25 + } 26 + 23 27 #flavours button { 24 28 @apply bg-ctp-surface0; 25 29 }
-2
src/snippets/snippets.json public/snippets.json
··· 1 1 { 2 2 "hide-pfp": { 3 3 "name": "Hide PFP", 4 - "author": "Schooltape", 5 4 "description": "Hide your profile picture across Schoolbox.", 6 5 "path": "hide-pfp.css" 7 6 }, 8 7 "censor": { 9 8 "name": "Censor", 10 - "author": "Schooltape", 11 9 "description": "Censors all text and images. This is intended for development purposes.", 12 10 "path": "censor.css" 13 11 }