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
238 lines
No EOL
10 KiB
JavaScript
238 lines
No EOL
10 KiB
JavaScript
// ==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];
|
|
}
|
|
})(); |