├── .github └── FUNDING.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── assets ├── banner.png ├── cws-1.jpg ├── cws-2.jpg ├── demo.gif ├── favicon.png ├── feature-1-example.png ├── feature-2-example.png ├── feature-3-example.png ├── icon-no-bg.png ├── icon128.png ├── icon16.png ├── icon48.png └── popup.png ├── better-github.js ├── package.json ├── popup.html ├── scripts └── build.js ├── styles.css └── yarn.lock /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | custom: "https://www.buymeacoffee.com/ceoshikhar" 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | better-github.zip 2 | better-github-chrome.zip 3 | better-github-firefox.zip 4 | manifest.json 5 | node_modules 6 | .DS_Store 7 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | # Unversioned 4 | 5 | ### Fixes 6 | 7 | - Some font names don't apply, for example 'Terminus (TTF) for Windows'. [PR #14](https://github.com/ceoshikhar/better-github/pull/14). 8 | 9 | 10 | # 1.2.0 (16-01-2022) 11 | 12 | ### Features 13 | 14 | - Apply styles to `code` HTML elements. [PR #13](https://github.com/ceoshikhar/better-github/pull/13). 15 | 16 | # 1.1.0 (16-01-2022) 17 | 18 | ### Features 19 | 20 | - Can provide just one style property and it will apply that style property instead of making the user provide values for all the style properties. [PR #12](https://github.com/ceoshikhar/better-github/pull/12). 21 | 22 | # 1.0.3 (26-12-2021) 23 | 24 | ### Features 25 | 26 | - Can customize line height. 27 | 28 | # 1.0.2 (07-07-2021) 29 | 30 | ### Fixes 31 | 32 | - Mouse cursor not falling in correct place while editing a file. [Issue #6](https://github.com/ceoshikhar/better-github/issues/6) - Fixed by [PR #10](https://github.com/ceoshikhar/better-github/pull/10). 33 | 34 | - Stopped working on github gists. [Issue #8](https://github.com/ceoshikhar/better-github/issues/8) - Fixed by [PR #9](https://github.com/ceoshikhar/better-github/pull/9). Thanks to [Aahnik Daw](https://github.com/aahnik). 35 | 36 | # 1.0.1 (01-04-2021) 37 | 38 | ### Features 39 | 40 | - Support for Firefox. Released it as an official mozilla firefox [addon](https://addons.mozilla.org/en-US/firefox/addon/bettergithub) 41 | - Scripts to build/package the extension( zip ) and generate manifest.json 42 | 43 | ### Fixes and refactors 44 | 45 | - Extra scroll( overflow ) on `` in Popup on Firefox 46 | - Refactor Popup CSS styles to `styles.css` 47 | 48 | # 1.0.0 (29-03-2021) 49 | 50 | First public release 51 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Shikhar Sharma 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![readme banner](./assets/banner.png) 2 | 3 |
4 |

Better GitHub

5 |

🎨 Enhance your code reading experience on GitHub

6 |

7 | Firefox 8 | · 9 | Chrome 10 |

11 |
12 | 13 | # Demo 14 | 15 | 16 | 17 | # Get the extension 18 | 19 | ### [Firefox](https://addons.mozilla.org/en-US/firefox/addon/bettergithub/) 20 | 21 | ### [Chrome](https://chrome.google.com/webstore/detail/better-github/ammeaejgjdeifjekkofhnliedhccbmgp) 22 | 23 | # Features 24 | 25 | ### Simple interface to use the extension 26 | 27 | ![popup](./assets/popup.png) 28 | 29 | `Better Github` allows you to apply custom fonts and modify font size of code text. These styles are applied to : 30 | 31 | > Example below have the following Better GitHub's config: `Font Name` is [Hack](https://github.com/source-foundry/Hack) and `Font Size` is **14**. 32 | 33 | - All the text inside a file while viewing( reading ) it. 34 | 35 | 36 | 37 | - Code in README files that are inside "``" blocks also known as `
` tags.
38 | 
39 |   
40 | 
41 | - Code in pull request diffs.
42 | 
43 |   
44 | 
45 | **Note:** If the custom styles are not applied( it can happen sometimes ), refresh the page.
46 | 
47 | # Motivation
48 | 
49 | Default font size of code text was very small which gave me had a hard time reading code in any repository and in PR diffs. I also wanted my code editor font and GitHub font to be same.
50 | 
51 | Couldn't find anything existing to help me solve my problem, so I created this simple yet powerful extension for Chrome browser. If something does exist though, let me know, alright?
52 | 
53 | I called it `Better GitHub` inspired by `Better Twitch TV` and `Better Discord`.
54 | 
55 | # Todos
56 | 
57 | > Context: I created this list of tasks after I decided to make this extension public. Initially the code was very small and the font styles were hard coded. If I needed to modify the styles, I had to change it in the source code, reload the extension and refresh GitHub pages to reflect the updates. Consider this list as the roadmap for the project.
58 | 
59 | - [x] Instead of hard coding the `fontFamily` and `fontSize`, we should be able to allow the user to choose a font size and font family of their choice (which they have installed on their machine).
60 | - [x] Create a browser action popup to show an interface to allow the user to customise their extension's settings for `fontFamily` and `fontSize`.
61 | - [x] Integrate browser action popup with chrome API to persist and read user's settings for `fontFamily` and `fontSize` from and to the storage.
62 | - [x] Apply styles without reloading whenever the settings are changed from the browser action popup interface.
63 | - [x] If no custom font styles are set, load GitHub's default font styles.
64 | - [x] User can reset font styles to GitHub's default font styles.
65 | - [x] Add GIF to show the usage( demo ) of the extension.
66 | - [x] Installation instructions on how to clone/download this repository, install the extension and use it.
67 | - [x] Logo for the extension that will be used as favicon, icon, in documentation etc.
68 | - [x] Firefox support as it was requested by [others](https://dev.to/ceoshikhar/enhance-your-code-reading-experience-on-github-with-this-chrome-extension-24ei).
69 | - [x] Publish it as an official Mozilla Firefox addon.
70 | - [x] Publish it as an official Chrome extension on Chrome Web Store.
71 | - [x] Allow user to change only one property instead of all properties being mandatory.
72 | - [ ] Add `CONTRIBUTING.md` to help others so that they can contribute to the project.
73 | 


--------------------------------------------------------------------------------
/assets/banner.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ceoshikhar/better-github/ce7a2929aa762bee9e8dd9e93837704724b4d40d/assets/banner.png


--------------------------------------------------------------------------------
/assets/cws-1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ceoshikhar/better-github/ce7a2929aa762bee9e8dd9e93837704724b4d40d/assets/cws-1.jpg


--------------------------------------------------------------------------------
/assets/cws-2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ceoshikhar/better-github/ce7a2929aa762bee9e8dd9e93837704724b4d40d/assets/cws-2.jpg


--------------------------------------------------------------------------------
/assets/demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ceoshikhar/better-github/ce7a2929aa762bee9e8dd9e93837704724b4d40d/assets/demo.gif


--------------------------------------------------------------------------------
/assets/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ceoshikhar/better-github/ce7a2929aa762bee9e8dd9e93837704724b4d40d/assets/favicon.png


--------------------------------------------------------------------------------
/assets/feature-1-example.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ceoshikhar/better-github/ce7a2929aa762bee9e8dd9e93837704724b4d40d/assets/feature-1-example.png


--------------------------------------------------------------------------------
/assets/feature-2-example.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ceoshikhar/better-github/ce7a2929aa762bee9e8dd9e93837704724b4d40d/assets/feature-2-example.png


--------------------------------------------------------------------------------
/assets/feature-3-example.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ceoshikhar/better-github/ce7a2929aa762bee9e8dd9e93837704724b4d40d/assets/feature-3-example.png


--------------------------------------------------------------------------------
/assets/icon-no-bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ceoshikhar/better-github/ce7a2929aa762bee9e8dd9e93837704724b4d40d/assets/icon-no-bg.png


--------------------------------------------------------------------------------
/assets/icon128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ceoshikhar/better-github/ce7a2929aa762bee9e8dd9e93837704724b4d40d/assets/icon128.png


--------------------------------------------------------------------------------
/assets/icon16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ceoshikhar/better-github/ce7a2929aa762bee9e8dd9e93837704724b4d40d/assets/icon16.png


--------------------------------------------------------------------------------
/assets/icon48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ceoshikhar/better-github/ce7a2929aa762bee9e8dd9e93837704724b4d40d/assets/icon48.png


--------------------------------------------------------------------------------
/assets/popup.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ceoshikhar/better-github/ce7a2929aa762bee9e8dd9e93837704724b4d40d/assets/popup.png


--------------------------------------------------------------------------------
/better-github.js:
--------------------------------------------------------------------------------
  1 | // Store the current set font styles to this object so that we don't have to
  2 | // fetch them from chrome storage API again and again.
  3 | // This improves the performance as we don't do the async chrome storage API
  4 | // read calls every time `applyCurrentSetStyles` is called.
  5 | const cache = {
  6 |     dirty: false,
  7 |     fontName: null,
  8 |     fontSize: null,
  9 |     lineHeight: null,
 10 | };
 11 | 
 12 | // These styles were taken from Google Chrome's(Windows) inspect element tool
 13 | // on 24/3/2021. These default styles might change but it's not really that
 14 | // important otherwise it would defeat the whole purpose of `Better Github`.
 15 | const DEFAULT_FONT_FAMILY =
 16 |     "SFMono-Regular,Consolas,Liberation Mono,Menlo,monospace";
 17 | const DEFAULT_FONT_SIZE = "12px";
 18 | const DEFAULT_LINE_HEIGHT = 1.4;
 19 | 
 20 | // Do the magic once the DOM is completely loaded.
 21 | window.onload = init();
 22 | 
 23 | // Entry point of the extension to interact with the DOM on GitHub.
 24 | function init() {
 25 |     // Apply font styles when DOM tree is completely loaded.
 26 |     applyCurrentSetStyles();
 27 | 
 28 |     // Apply font styles when DOM tree is mutated.
 29 |     reApplyStylesOnDOMChange();
 30 | }
 31 | 
 32 | // Apply the currently set font styles if they exist
 33 | async function applyCurrentSetStyles() {
 34 |     if (isUserEditingFile()) return;
 35 | 
 36 |     const currentSetFontStyles = await getCurrentSetFontStyles();
 37 |     if (!currentSetFontStyles) return;
 38 | 
 39 |     const { fontFamily, fontSize, lineHeight } = currentSetFontStyles;
 40 |     applyStyles(fontFamily, fontSize, lineHeight);
 41 | }
 42 | 
 43 | // Apply the custom styles whenever something on the DOM changes. Following are
 44 | // some scenarios why this is important.
 45 | //
 46 | // 1. With larger PRs, sometimes diffs are loaded lazily. Which means, a DOM
 47 | // mutation is happening. If we don't re-apply the styles, the newly loaded
 48 | // code text in the diff will have the defaul styles.
 49 | //
 50 | // 2. GitHub is an SPA. Which means, DOM is loaded only once. When you navigate
 51 | // to other "pages" on GitHub, the DOM is changing( mutation ) and if we don't
 52 | // re-apply the styles, the code text on new page will have default styles.
 53 | //
 54 | // We don't care to check what the change is happening on the DOM, we just
 55 | // re-apply the styles no matter what's happening. This shouldn't be affecting
 56 | // the performance as all we are doing is changing the CSS styles, also there
 57 | // are not so many DOM changes happening once the entire page content is loaded.
 58 | function reApplyStylesOnDOMChange() {
 59 |     // We pass `applyCurrentSetStyles` as the callback to `Mutation Observer`.
 60 |     // This means, whenever a DOM mutation is observed it fires the callback.
 61 |     const observer = new MutationObserver(applyCurrentSetStyles);
 62 | 
 63 |     observer.observe(document, { childList: true, subtree: true });
 64 | }
 65 | 
 66 | // Reset the font styles to Github's default.
 67 | function resetStyles() {
 68 |     // Clear the storage so that when we reload/refresh or visit GitHub in another
 69 |     // tab, we don't apply any custom styles including the above two mentioned.
 70 |     // The real(coming from GitHub) GitHub's default styles are used.
 71 |     clearStorage();
 72 | 
 73 |     // We apply the GitHub's "default styles" so that we don't have to manually
 74 |     // refresh the current open tabs and this makes it feel "reactive".
 75 |     applyStyles(DEFAULT_FONT_FAMILY, DEFAULT_FONT_SIZE, DEFAULT_LINE_HEIGHT);
 76 | }
 77 | 
 78 | // This is where the magic happens. As the name suggests, the function is
 79 | // responsible for applying the font styles to the code text elements on a page.
 80 | //
 81 | // Apply font styles (font-name & font-size).
 82 | // These styles are applied to :
 83 | // - All the text inside a file while viewing( reading ) it.
 84 | // - Code in README files that are inside "``" blocks also known as `
` tags.
 85 | // - Code in pull request diffs.
 86 | function applyStyles(fontFamily, fontSize, lineHeight) {
 87 |     const codeTextElements = document.getElementsByClassName("blob-code-inner");
 88 |     const codeLineNumElements = document.getElementsByClassName("blob-num");
 89 |     const preElements = document.querySelectorAll("pre");
 90 |     const codeElements = document.querySelectorAll("code");
 91 | 
 92 |     for (let i = 0; i < codeTextElements.length; i++) {
 93 |         applyStyle(codeTextElements[i], "fontFamily", fontFamily);
 94 |         applyStyle(codeTextElements[i], "fontSize", fontSize);
 95 |         applyStyle(codeTextElements[i], "lineHeight", lineHeight);
 96 |     }
 97 | 
 98 |     for (let i = 0; i < codeLineNumElements.length; i++) {
 99 |         applyStyle(codeLineNumElements[i], "fontFamily", fontFamily);
100 |         applyStyle(codeLineNumElements[i], "fontSize", fontSize);
101 |         applyStyle(codeLineNumElements[i], "fontHeight", lineHeight);
102 |     }
103 | 
104 |     for (let i = 0; i < preElements.length; i++) {
105 |         applyStyle(preElements[i], "fontFamily", fontFamily);
106 |         applyStyle(preElements[i], "fontSize", fontSize);
107 |         applyStyle(preElements[i], "fontHeight", lineHeight);
108 |     }
109 | 
110 |     for (let i = 0; i < codeElements.length; i++) {
111 |         applyStyle(codeElements[i], "fontFamily", fontFamily);
112 |         applyStyle(codeElements[i], "fontSize", fontSize);
113 |         applyStyle(codeElements[i], "fontHeight", lineHeight);
114 |     }
115 | }
116 | 
117 | /**
118 |  * @param {HTMLElement} el The element to apply the style to.
119 |  * @param {string} prop The style property to change.
120 |  * @param {string} value The value to apply for the given `prop`.
121 |  *
122 |  * If a falsy(inc. empty string) value is passed as `value` then that value
123 |  * will not be applied and will be skipped.
124 |  */
125 | function applyStyle(el, prop, value) {
126 |     if (!value) {
127 |         return;
128 |     }
129 | 
130 |     el.style[prop] = value;
131 | }
132 | 
133 | // Extension's browser action popup UI handling to allow user to customize the
134 | // settings of the styles. Handles the `APPLY` and `RESET` button logic.
135 | document.addEventListener("DOMContentLoaded", async function () {
136 |     const setFontName = (await getCurrentSetFontName()) || "";
137 |     const setFontSize = (await getCurrentSetFontSize()) || "";
138 |     const setLineHeight = (await getCurrentSetLineHeight()) || "";
139 |     const applyButton = document.getElementById("apply-button");
140 |     const resetButton = document.getElementById("reset-button");
141 |     const fontNameInput = document.getElementById("font-name-input");
142 |     const fontSizeInput = document.getElementById("font-size-input");
143 |     const lineHeightInput = document.getElementById("line-height-input");
144 | 
145 |     // Set the initial value of the inputs to be the current set styles.
146 |     fontNameInput.value = setFontName;
147 |     fontSizeInput.value = setFontSize;
148 |     lineHeightInput.value = setLineHeight;
149 | 
150 |     applyButton.addEventListener("click", function () {
151 |         const font = fontNameInput.value;
152 |         const size = fontSizeInput.value;
153 |         const height = lineHeightInput.value;
154 |         const fontStyles = {
155 |             font,
156 |             size,
157 |             height,
158 |         };
159 | 
160 |         // We get the details of all the tabs open and send message to all the
161 |         // tabs with the new font styles data. All the tabs with GitHub open
162 |         // will read this message and apply the new styles sent in the message.
163 |         chrome.tabs.query({}, function (tabs) {
164 |             tabs.map(function (tab) {
165 |                 chrome.tabs.sendMessage(tab.id, { data: fontStyles });
166 |             });
167 |         });
168 |     });
169 | 
170 |     resetButton.addEventListener("click", function () {
171 |         // Send message to all the tabs with data saying that we should reset the
172 |         // styles. All the tabs with GitHub open will read this message and reset
173 |         // styles to GitHub's default styles.
174 |         chrome.tabs.query({}, function (tabs) {
175 |             tabs.map(function (tab) {
176 |                 chrome.tabs.sendMessage(tab.id, { data: { reset: true } });
177 |             });
178 |         });
179 |     });
180 | });
181 | 
182 | // We listen for messages that we earlier sent when a user clicked on `APPLY` or
183 | // `RESET` button. Based on the `request.data` of the message, we either apply
184 | // the new styles or reset the styles.
185 | chrome.runtime.onMessage.addListener(function (request, _sender, sendResponse) {
186 |     const data = request.data || {};
187 |     const shouldReset = data.reset === true;
188 | 
189 |     if (shouldReset) {
190 |         resetStyles();
191 |         return;
192 |     }
193 | 
194 |     const name = data.font;
195 |     const size = data.size;
196 |     const height = data.height;
197 | 
198 |     setCurrentSetFontName(name);
199 |     setCurrentSetFontSize(size);
200 |     setCurrentSetLineHeight(height);
201 | 
202 |     const { fontFamily, fontSize, lineHeight } = genFontStyles(
203 |         name,
204 |         size,
205 |         height
206 |     );
207 |     applyStyles(fontFamily, fontSize, lineHeight);
208 |     sendResponse({ data, success: true });
209 | });
210 | 
211 | // Chrome's storage API allows us to store & fetch user's recent applied styles.
212 | // Save to chrome's storage. `data` should be an object like `{ key: value }`.
213 | function saveToStorage(data) {
214 |     chrome.storage.sync.set(data);
215 | }
216 | 
217 | // Read a `value` from chromes' storage by providing the `key`. This returns a
218 | // Promise so that we can await for the `value`. If we don't use Promise, the
219 | // return value will always be `undefined` as chrome's storage read is async.
220 | function getFromStorage(key) {
221 |     return new Promise(function (resolve, _reject) {
222 |         chrome.storage.sync.get([`${key}`], function (result) {
223 |             const value = result[`${key}`];
224 |             resolve(value);
225 |         });
226 |     });
227 | }
228 | 
229 | // Destroy everything from the chrome's storage.
230 | function clearStorage() {
231 |     chrome.storage.sync.clear();
232 | }
233 | 
234 | async function getCurrentSetFontName() {
235 |     const currentFontName = await getFromStorage("fontName");
236 |     return currentFontName;
237 | }
238 | 
239 | async function getCurrentSetFontSize() {
240 |     const currentFontSize = await getFromStorage("fontSize");
241 |     return currentFontSize;
242 | }
243 | 
244 | async function getCurrentSetLineHeight() {
245 |     const currentLineHeight = await getFromStorage("lineHeight");
246 |     return currentLineHeight;
247 | }
248 | 
249 | function setCurrentSetFontName(name) {
250 |     saveToStorage({ fontName: name });
251 |     cache.fontName = name;
252 | }
253 | 
254 | function setCurrentSetFontSize(size) {
255 |     saveToStorage({ fontSize: size });
256 |     cache.fontSize = size;
257 | }
258 | 
259 | function setCurrentSetLineHeight(height) {
260 |     saveToStorage({ lineHeight: height });
261 |     cache.lineHeight = height;
262 | }
263 | 
264 | // Generates correct styles by adding `px` for `size` and  adding
265 | // `'monospace'` for `name`.
266 | function genFontStyles(name, size, height) {
267 |     const fontFamily = !name ? DEFAULT_FONT_FAMILY : `'${name}', 'monospace'`;
268 |     const fontSize = !size ? DEFAULT_FONT_SIZE : `${size}px`;
269 |     const lineHeight = !height ? DEFAULT_LINE_HEIGHT : height;
270 | 
271 |     return { fontFamily, fontSize, lineHeight };
272 | }
273 | 
274 | async function getCurrentSetFontStyles() {
275 |     if (cache.dirty) {
276 |         return genFontStyles(cache.fontName, cache.fontSize, cache.lineHeight);
277 |     }
278 | 
279 |     // Everything below here will be executed only during the first time the
280 |     // document is loaded. After that, `cache` will always have latest font styles.
281 | 
282 |     const currentSetFontName = await getCurrentSetFontName();
283 |     const currentSetFontSize = await getCurrentSetFontSize();
284 |     const currentSetLineHeight = await getCurrentSetLineHeight();
285 | 
286 |     // Update the cache so that we don't make the chrome storage read calls again.
287 |     cache.dirty = true;
288 |     if (currentSetFontName) cache.fontName = currentSetFontName;
289 |     if (currentSetFontSize) cache.fontSize = currentSetFontSize;
290 |     if (currentSetLineHeight) cache.lineHeight = currentSetLineHeight;
291 | 
292 |     return getCurrentSetFontStyles();
293 | }
294 | 
295 | // This is a cheeky solution to the issue where the code editor goes whack
296 | // mode when we change the font styles on the code lines inside the editor.
297 | //
298 | // So for the time being we will not change the font styles when a user is
299 | // editing a file on GitHub.
300 | //
301 | // Maybe in the future, if I get lucky( idk wtf is GitHub doing ) or someone
302 | // in the world contributes and solves this issue where we can apply custom
303 | // font styles to editor font without making it go insane, that would be nice.
304 | //
305 | // Untill then, Better GitHub won't do it's magic when user edits a file on
306 | // GitHub. Also, who does that? xD
307 | // Check - https://github.com/ceoshikhar/better-github/issues/6 for more info.
308 | function isUserEditingFile() {
309 |     function fileNameFromPath() {
310 |         const words = window.location.pathname.split("/");
311 |         const len = words.length;
312 |         // The file being viewed/edited on GitHub is the last word in the URL path.
313 |         const fileName = words[len - 1];
314 | 
315 |         return fileName;
316 |     }
317 | 
318 |     // The word "edit" exists in the URL path if the user is editing the file.
319 |     const wordEditInPathExists = window.location.pathname.includes("edit");
320 |     const fileNameFromInputEl = document.querySelector(
321 |         "input[name=filename]"
322 |     )?.value;
323 | 
324 |     // The name of the file being edited will be same in URL path and input element on page load.
325 |     if (fileNameFromPath() === fileNameFromInputEl && wordEditInPathExists) {
326 |         return true;
327 |     }
328 | 
329 |     return false;
330 | }
331 | 


--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
 1 | {
 2 |     "name": "better-github",
 3 |     "version": "1.2.0",
 4 |     "description": "Enhance your code reading experience on GitHub",
 5 |     "author": {
 6 |         "name": "Shikhar Sharma",
 7 |         "email": "ceoshikhar@gmail.com"
 8 |     },
 9 |     "license": "MIT",
10 |     "main": "better-github.js",
11 |     "scripts": {
12 |         "package:chrome": "node scripts/build chrome",
13 |         "package:firefox": "node scripts/build firefox",
14 |         "manifest:chrome": "node scripts/build chrome -m",
15 |         "manifest:firefox": "node scripts/build firefox -m",
16 |         "clean": "rm -f manifest.json better-github-chrome.zip better-github-firefox.zip"
17 |     },
18 |     "repository": {
19 |         "type": "git",
20 |         "url": "git+https://github.com/ceoshikhar/better-github.git"
21 |     },
22 |     "bugs": {
23 |         "url": "https://github.com/ceoshikhar/better-github/issues"
24 |     },
25 |     "homepage": "https://github.com/ceoshikhar/better-github#readme",
26 |     "dependencies": {},
27 |     "devDependencies": {
28 |         "chalk": "^4.1.1"
29 |     }
30 | }
31 | 


--------------------------------------------------------------------------------
/popup.html:
--------------------------------------------------------------------------------
 1 | 
 2 | 
 3 |     
 4 |         
 5 |         
 9 |         
10 |         Better Github
11 |     
12 | 
13 |     
14 |         
15 | logo 16 |

BETTER GITHUB

17 |
18 | 19 |
20 |
21 | 22 | 29 |
30 |
31 |
32 | 33 | 40 |
41 |
42 |
43 | 44 | 52 |
53 |
54 | 55 | 56 |
57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /scripts/build.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This is the build script that you can use to : 3 | * 1. Package the extension to better-github.zip for the specified browser. 4 | * Generally for publication or distribution of the extension. 5 | * 2. Generate manifest.json file for the specified browser. 6 | * 7 | * Prerequisites for this script to work : 8 | * 1. `zip` util binary installed, run `zip --version` to check if you have it 9 | * or not. If you get something like `command not found: zip` then you don't 10 | * have it, so install it. `zip` is not needed for generating manifest.json. 11 | * 2. `rm -rf` should work. This is available on `linux` and `unix` machines. 12 | * Don't know what you need to do for it to work on `windows`. I use WSL. 13 | * 14 | * USAGE : 15 | * - node build [options] OR you can run scripts from `package.json` 16 | * 17 | * Valid values for `` are : "chrome" and "firefox" 18 | * Valid options : 19 | * 1. `-m` : To generate only the manifest.json and not generate package.zip 20 | * 21 | * EXAMPLE : 22 | * 1. Generate better-github.zip for chrome : `node build chrome` 23 | * 2. Generate manifest.json for firefox : `node build firefox -m` 24 | */ 25 | 26 | // NOTE: Update the `version` here and in `package.json` whenever a new release 27 | // of the extension is published. 28 | const VERSION = "1.2.0"; 29 | 30 | const fs = require("fs"); 31 | const childProcess = require("child_process"); 32 | const chalk = require("chalk"); 33 | 34 | function green(text) { 35 | return chalk.green(text); 36 | } 37 | 38 | function blue(text) { 39 | return chalk.blue.underline(text); 40 | } 41 | 42 | function result(text) { 43 | return chalk.black.bgYellowBright(text); 44 | } 45 | 46 | const chromeManifestContent = { 47 | manifest_version: 2, 48 | name: "Better Github", 49 | version: VERSION, 50 | description: "Enhance your code reading experience on GitHub", 51 | content_scripts: [ 52 | { 53 | js: ["better-github.js"], 54 | matches: ["https://github.com/*", "https://gist.github.com/*"], 55 | }, 56 | ], 57 | permissions: ["storage"], 58 | browser_action: { 59 | default_title: "Better Github", 60 | default_icon: "./assets/favicon.png", 61 | default_popup: "popup.html", 62 | }, 63 | icons: { 64 | 16: "./assets/icon16.png", 65 | 48: "./assets/icon48.png", 66 | 128: "./assets/icon128.png", 67 | }, 68 | }; 69 | 70 | // For Firefox's manifest, we just have to add one extra property: "applications" 71 | const firefoxManifestContent = { 72 | ...chromeManifestContent, 73 | applications: { 74 | gecko: { 75 | id: "better-github@ceoshikhar.com", 76 | strict_min_version: "80.0", 77 | }, 78 | }, 79 | }; 80 | 81 | // The first element will be 'node', the second element will be the name of the 82 | // JS file. The next elements will be any additional command line arguments. 83 | const args = process.argv.slice(2); 84 | const maxArgs = 2; 85 | const browser = args[0]; 86 | const validBrowsers = ["chrome", "firefox"]; 87 | const shouldBuildPackage = browser && args[1] === "-m" ? false : true; 88 | const manifestName = "manifest.json"; 89 | const packageChromeName = "better-github-chrome.zip"; 90 | const packageFirefoxName = "better-github-firefox.zip"; 91 | const packageName = browserType().isChrome 92 | ? packageChromeName 93 | : packageFirefoxName; 94 | 95 | const thingsToZip = [ 96 | "assets/icon16.png", 97 | "assets/icon48.png", 98 | "assets/icon128.png", 99 | "assets/favicon.png", 100 | "assets/icon-no-bg.png", 101 | "better-github.js", 102 | manifestName, 103 | "popup.html", 104 | "styles.css", 105 | ]; 106 | 107 | function browserType() { 108 | const isChrome = browser === validBrowsers[0] ? true : false; 109 | const isFirefox = browser === validBrowsers[1] ? true : false; 110 | return { isChrome, isFirefox }; 111 | } 112 | 113 | function makeSureArgsAreValid() { 114 | if (args.length > maxArgs) { 115 | throw new Error(`Maximum 2 arguments are allowed`); 116 | } 117 | 118 | if (!validBrowsers.includes(browser)) { 119 | throw new Error( 120 | `Invalid browser, "chrome" and "firefox" are only valid` 121 | ); 122 | } 123 | 124 | if (shouldBuildPackage && args.length === maxArgs) { 125 | throw new Error(`Unexpected arguments`); 126 | } 127 | } 128 | 129 | function cleanUpOldFiles() { 130 | console.log(green("> Cleaning up old files if they exist")); 131 | 132 | const command = `rm -f manifest.json better-github-chrome.zip better-github-firefox.zip`; 133 | childProcess.execSync(command); 134 | } 135 | 136 | // Create new manifest.json and put `chromeManifestContent` if the build is 137 | // for Chrome browser otherwise put `firefoxManifestContent` if the build is 138 | // for Firefox browser. 139 | function buildManifest() { 140 | console.log( 141 | green(`> Building manifest for ${browser}: `) + blue(`${manifestName}`) 142 | ); 143 | if (browserType().isChrome) { 144 | fs.writeFileSync( 145 | manifestName, 146 | JSON.stringify(chromeManifestContent, null, 2) 147 | ); 148 | } else if (browserType().isFirefox) { 149 | fs.writeFileSync( 150 | manifestName, 151 | JSON.stringify(firefoxManifestContent, null, 2) 152 | ); 153 | } 154 | } 155 | 156 | function buildPackage() { 157 | const command = `zip ${packageName} ${thingsToZip.join(" ")}`; 158 | console.log( 159 | green(`> Building package for ${browser}: `) + blue(`${packageName}`) 160 | ); 161 | childProcess.execSync(command); 162 | } 163 | 164 | function main() { 165 | const t0 = Date.now(); 166 | 167 | makeSureArgsAreValid(); 168 | cleanUpOldFiles(); 169 | buildManifest(); 170 | 171 | if (shouldBuildPackage) { 172 | buildPackage(); 173 | } 174 | 175 | const t1 = Date.now(); 176 | console.log(result(`> 🚀 Finished in ${t1 - t0}ms`)); 177 | } 178 | 179 | main(); 180 | -------------------------------------------------------------------------------- /styles.css: -------------------------------------------------------------------------------- 1 | body { 2 | width: 250px; 3 | padding: 0; 4 | margin: 0; 5 | border: 2px solid RGB(42, 46, 47); 6 | overflow: -moz-hidden-unscrollable; 7 | } 8 | 9 | h2, 10 | p, 11 | label { 12 | margin: 0; 13 | padding: 0; 14 | font-size: 1rem; 15 | color: RGB(42, 46, 47); 16 | font-family: "Poppins", sans-serif; 17 | } 18 | 19 | h2 { 20 | font-size: 1.25rem; 21 | margin: 0.25em 0; 22 | text-align: center; 23 | } 24 | 25 | p { 26 | text-align: center; 27 | font-family: "Source Code Pro", monospace; 28 | } 29 | 30 | label { 31 | display: block; 32 | text-align: left; 33 | font-weight: bold; 34 | } 35 | 36 | form { 37 | width: 100%; 38 | padding: 1em; 39 | padding-top: 0; 40 | } 41 | 42 | form input { 43 | font-family: "Source Code Pro", monospace; 44 | color: RGB(42, 46, 47); 45 | font-weight: normal; 46 | margin-top: 0.25em; 47 | border: none; 48 | border-bottom: 1px solid RGB(42, 46, 47); 49 | box-sizing: border-box; 50 | padding: 0.25em 0; 51 | outline: none; 52 | font-size: 0.85rem; 53 | width: 90%; 54 | } 55 | 56 | input::placeholder { 57 | color: RGB(42, 46, 47); 58 | opacity: 0.69; 59 | } 60 | 61 | button { 62 | width: 90%; 63 | padding: 0.5em 0.35em; 64 | font-size: 1.15rem; 65 | border: none; 66 | cursor: pointer; 67 | font-family: "Poppins", sans-serif; 68 | } 69 | 70 | #apply-button { 71 | background: RGB(42, 46, 47); 72 | color: RGB(249, 246, 239); 73 | } 74 | 75 | #reset-button { 76 | color: RGB(42, 46, 47); 77 | background: RGB(249, 246, 239); 78 | margin-top: 0.5em; 79 | } 80 | 81 | .flex { 82 | display: flex; 83 | justify-content: center; 84 | align-items: center; 85 | } 86 | 87 | .flex img { 88 | margin-right: 0.5em; 89 | } 90 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | ansi-styles@^4.1.0: 6 | version "4.3.0" 7 | resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" 8 | integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== 9 | dependencies: 10 | color-convert "^2.0.1" 11 | 12 | chalk@^4.1.1: 13 | version "4.1.1" 14 | resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.1.tgz#c80b3fab28bf6371e6863325eee67e618b77e6ad" 15 | integrity sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg== 16 | dependencies: 17 | ansi-styles "^4.1.0" 18 | supports-color "^7.1.0" 19 | 20 | color-convert@^2.0.1: 21 | version "2.0.1" 22 | resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" 23 | integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== 24 | dependencies: 25 | color-name "~1.1.4" 26 | 27 | color-name@~1.1.4: 28 | version "1.1.4" 29 | resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" 30 | integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== 31 | 32 | has-flag@^4.0.0: 33 | version "4.0.0" 34 | resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" 35 | integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== 36 | 37 | supports-color@^7.1.0: 38 | version "7.2.0" 39 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" 40 | integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== 41 | dependencies: 42 | has-flag "^4.0.0" 43 | --------------------------------------------------------------------------------