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
238
main.user.js
Normal file
238
main.user.js
Normal 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];
|
||||
}
|
||||
})();
|
||||
Loading…
Add table
Add a link
Reference in a new issue