Please enter the commit message for your changes. Lines starting

with '' will be ignored, and an empty message aborts the commit.

 On branch main

 Initial commit

 Changes to be committed:
	new file:   README.md
	new file:   main.user.js
	new file:   screenshot1.png
	new file:   screenshot2.png
	new file:   screenshot3.webp
This commit is contained in:
commit 1ed638c38b
5 changed files with 260 additions and 0 deletions

22
README.md Normal file
View file

@ -0,0 +1,22 @@
[← Homepage](../README.md)
# Enhancements for Forgejo
| [Install](../../../../raw/branch/master/enhancementsForForgejo/main.user.js) |
|------------------------------------------------------------------------------|
## Features
- ~~Fix alphabetical files ordering~~ ([`forgejo/forgejo@codeberg.org#317`](https://codeberg.org/forgejo/forgejo/issues/317), fixed by [`forgejo/forgejo@codeberg.org#2522`](https://codeberg.org/forgejo/forgejo/pulls/2522))
- Fix comment buttons alignment ([`forgejo/forgejo@codeberg.org#560`](https://codeberg.org/forgejo/forgejo/issues/560))
- Sticky navbar ([`forgejo/forgejo@codeberg.org#1318`](https://codeberg.org/forgejo/forgejo/issues/1318))
- Scrollable code view
- Fix issues tab consistency ([`forgejo/forgejo@codeberg.org#365`](https://codeberg.org/forgejo/forgejo/issues/365))
- Restore feature-rich Markdown editor ([`forgejo/forgejo@codeberg.org#3604`](https://codeberg.org/forgejo/forgejo/issues/3604))
- Sort `/admin/users` by newest by default ([`forgejo/forgejo@codeberg.org#2574`](https://codeberg.org/forgejo/forgejo/issues/2574))
---
| ![](./screenshot1.png) | ![](./screenshot2.png) |
|-------------------------|------------------------|
| ![](./screenshot3.webp) | |

238
main.user.js Normal file
View file

@ -0,0 +1,238 @@
// ==UserScript==
// @match https://*/*
// @name Enhancements for Forgejo
// @description Enhancements for Forgejo
// @grant GM.getValue
// @grant GM.setValue
// @version 0.2.1
// @author KaKi87
// @license MIT
// @namespace https://git.kaki87.net/KaKi87/userscripts/src/branch/master/enhancementsForForgejo
// @require https://cdn.jsdelivr.net/npm/vue@3.3.4/dist/vue.global.prod.js
// ==/UserScript==
const
APP_ID = 'enhancementsForForgejo',
APP_NAME = 'Enhancements for Forgejo';
(async () => {
if(!document.querySelector('[href$="/explore/repos"]')) return;
unsafeWindow.Vue = Vue;
let
tweaksModalAppMountedPromiseResolve,
setTweakModalActive,
getSettings;
const
tweaksModalElement = document.createElement('div'),
tweaksModalAppMountedPromise = new Promise(resolve => tweaksModalAppMountedPromiseResolve = resolve),
tweaksModalApp = Vue.createApp({
data: () => ({
isActive: false,
settings: {
isFixCommentButtonsAlignment: true,
isNavbarSticky: false,
isCodeViewScrollable: true,
isFixIssuesTabConsistency: true,
isRestoreFeatureRichMarkdownEditor: true,
isSortAdminUsersByNewestByDefault: true
}
}),
methods: {
refreshSettings: async function(){
const settings = await GM.getValue('settings');
if(settings)
Object.assign(this.settings, settings);
},
applySettings: function(){
document.body.classList.toggle(`__${APP_ID}--isFixCommentButtonsAlignment`, this.settings.isFixCommentButtonsAlignment);
document.body.classList.toggle(`__${APP_ID}--isNavbarSticky`, this.settings.isNavbarSticky);
document.body.classList.toggle(`__${APP_ID}--isCodeViewScrollable`, this.settings.isCodeViewScrollable);
}
},
watch: {
isActive: async function(isActive, wasActive){
document.body.classList.toggle('dimmable', isActive);
document.body.classList.toggle('dimmed', isActive);
if(!wasActive && isActive)
await this.refreshSettings();
},
settings: {
deep: true,
handler: async function(){
await GM.setValue('settings', this.settings);
this.applySettings();
}
}
},
mounted: async function(){
await this.refreshSettings();
setTweakModalActive = isActive => this.isActive = isActive;
getSettings = () => this.settings;
tweaksModalAppMountedPromiseResolve();
this.applySettings();
},
template: `
<div
class="__${APP_ID}__tweaksModal__container ui dimmer"
:class="{ 'active': isActive }"
@click="event => isActive = event.target !== event.currentTarget"
><div
class="ui small modal active"
>
<div class="__${APP_ID}__tweaksModal__header header">
<span>${APP_NAME}</span>
<button
class="__${APP_ID}__tweaksModal__header__close"
@click.stop="isActive = false"
><i
class="bx bx-x"
></i></button>
</div>
<form class="__${APP_ID}__tweaksModal__form content">
<div
class="ui checkbox"
><input
v-model="settings.isFixCommentButtonsAlignment"
type="checkbox"
><label>Fix comment buttons alignment</label></div>
<div
class="ui checkbox"
><input
v-model="settings.isNavbarSticky"
type="checkbox"
><label>Sticky navbar</label></div>
<div
class="ui checkbox"
><input
v-model="settings.isCodeViewScrollable"
type="checkbox"
><label>Scrollable code view</label></div>
<div
class="ui checkbox"
><input
v-model="settings.isFixIssuesTabConsistency"
type="checkbox"
><label>Fix issues tab consistency</label></div>
<div
class="ui checkbox"
><input
v-model="settings.isRestoreFeatureRichMarkdownEditor"
type="checkbox"
><label>Restore feature-rich Markdown editor</label></div>
<div
class="ui checkbox"
><input
v-model="settings.isSortAdminUsersByNewestByDefault"
type="checkbox"
><label>Sort <code class="inline-code-block">/admin/users</code> by newest by default</label></div>
</form>
</div></div>
`
});
tweaksModalElement.setAttribute('class', `__${APP_ID}__tweaksModal`);
await Promise.all([
tweaksModalApp.mount(tweaksModalElement),
tweaksModalAppMountedPromise
]);
document.body.append(tweaksModalElement);
for(const userMenuNotificationsElement of document.querySelectorAll('[href="/notifications"]')){
const userMenuTweaksElement = document.createElement(userMenuNotificationsElement.tagName);
userMenuTweaksElement.style.setProperty('--width', `${userMenuNotificationsElement.offsetWidth}px`);
userMenuTweaksElement.style.setProperty('--height', `${userMenuNotificationsElement.offsetHeight}px`);
userMenuTweaksElement.setAttribute('class', `__${APP_ID}__tweaksMenu ${userMenuNotificationsElement.className}`);
userMenuTweaksElement.setAttribute('data-tooltip-content', APP_NAME);
userMenuTweaksElement.setAttribute('href', '#');
userMenuTweaksElement.innerHTML = `<i class='bx bxs-extension'></i>`;
userMenuTweaksElement.addEventListener(
'click',
event => {
event.preventDefault();
setTweakModalActive(true);
}
);
userMenuNotificationsElement.parentElement.insertBefore(userMenuTweaksElement, userMenuNotificationsElement);
}
{
const issuesTabElement = document.querySelector(`[href="${window.location.pathname}/issues"]`);
if(issuesTabElement && getSettings().isFixIssuesTabConsistency)
issuesTabElement.setAttribute('href', `${issuesTabElement.getAttribute('href')}?state=open`);
}
if(getSettings().isRestoreFeatureRichMarkdownEditor){
localStorage.setItem('markdown-editor-default', 'easymde');
localStorage.setItem('markdown-editor-wiki', 'easymde');
}
if(window.location.pathname.startsWith('/admin') && getSettings().isSortAdminUsersByNewestByDefault)
document.querySelector('[href="/admin/users"]').setAttribute('href', '/admin/users?sort=newest');
document.documentElement.style.setProperty(
`--${APP_ID}-navbar-height`,
`${document.querySelector('#navbar').offsetHeight}px`
);
document.head.append(Object.assign(
document.createElement('link'),
{ 'rel': 'stylesheet', 'href': 'https://cdn.jsdelivr.net/npm/boxicons@2.1.4/css/boxicons.min.css' }
));
{
const styles = new CSSStyleSheet();
// language=CSS
await styles.replace(`
.__${APP_ID}__tweaksMenu {
padding: 0 !important;
justify-content: center;
width: var(--width);
height: var(--height);
font-size: 1.25rem;
color: inherit;
}
.__${APP_ID}__tweaksModal__container {
position: fixed !important;
}
.__${APP_ID}__tweaksModal__header {
display: flex !important;
justify-content: space-between;
}
.__${APP_ID}__tweaksModal__header__close {
border: none;
background: none;
padding: 0;
font-size: 1.5rem;
cursor: pointer;
}
.__${APP_ID}__tweaksModal__form {
display: flex !important;
flex-direction: column;
row-gap: 0.5rem;
}
.__${APP_ID}__tweaksModal__form .checkbox label {
user-select: initial;
}
.__${APP_ID}--isFixCommentButtonsAlignment div:has(> #status-button) {
display: flex;
justify-content: flex-end;
column-gap: 0.5rem;
}
.__${APP_ID}--isFixCommentButtonsAlignment #status-button {
margin-bottom: 0;
display: flex;
align-items: center;
justify-content: center;
}
.__${APP_ID}--isNavbarSticky [role="main"] {
padding-top: var(--${APP_ID}-navbar-height);
}
.__${APP_ID}--isNavbarSticky #navbar {
position: fixed;
width: 100%;
z-index: 1;
}
.__${APP_ID}--isCodeViewScrollable .code-view,
.__${APP_ID}--isCodeViewScrollable .code-block {
overflow: auto;
}
.__${APP_ID}--isCodeViewScrollable .code-view .code-inner,
.__${APP_ID}--isCodeViewScrollable .code-block .chroma {
white-space: pre;
}
`);
document.adoptedStyleSheets = [...document.adoptedStyleSheets, styles];
}
})();

BIN
screenshot1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

BIN
screenshot2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
screenshot3.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB