Skip to content

Instantly share code, notes, and snippets.

@malys
Created December 17, 2025 10:45
Show Gist options
  • Select an option

  • Save malys/91ce814a02787a474ca5dbe2b3701493 to your computer and use it in GitHub Desktop.

Select an option

Save malys/91ce814a02787a474ca5dbe2b3701493 to your computer and use it in GitHub Desktop.
[Vikunja] minimal view #userscript #violentmonkey
// ==UserScript==
// @name Vikunja Auto Minimal View
// @namespace http://tampermonkey.net/
// @version 0.7
// @description Shows only task detail in minimal view, removing all other page elements including top bar
// @author You
// @match https://*/tasks/*
// @grant none
// ==/UserScript==
(function() {
'use strict';
// Check if URL has minimal=true parameter
function isMinimalParam() {
const urlParams = new URLSearchParams(window.location.search);
return urlParams.get('minimal') === 'true';
}
// Apply minimal view - show only task detail
function applyMinimalView() {
const container = document.querySelector('.task-view-container');
if (!container) return;
// Add minimal view class
container.classList.add('minimal-view');
// Hide everything except the task container
const elementsToHide = [
'aside.menu-container',
'nav.menu-container',
'header',
'header.navbar',
'.navbar',
'.menu-hide-button',
'.menu-show-button',
'.quick-actions',
'.mobile-overlay',
'.keyboard-shortcuts-button',
'.app-container-background',
'.content-auth > .menu-hide-button',
'.notification',
'.add-to-home-screen',
'.update-notification',
'.demo-mode',
'.navbar-end',
'.username-dropdown-trigger',
'.avatar',
'.navbar-brand',
'.navbar-start',
'.navbar-menu',
'.navbar-item',
'.navbar-link',
'.navbar-dropdown',
];
elementsToHide.forEach(selector => {
const elements = document.querySelectorAll(selector);
elements.forEach(el => {
el.style.display = 'none !important';
el.style.visibility = 'hidden !important';
el.style.width = '0 !important';
el.style.height = '0 !important';
el.style.overflow = 'hidden !important';
el.style.position = 'absolute !important';
el.style.left = '-9999px !important';
el.style.top = '-9999px !important';
});
});
// Hide non-essential task elements
const taskSelectors = [
'.subtitle',
'.checklist-summary',
'.action-buttons',
'.attachments',
'.comments',
'.related-tasks',
'.detail-content .columns:not(:first-child)'
];
taskSelectors.forEach(selector => {
const elements = document.querySelectorAll(selector);
elements.forEach(el => el.style.display = 'none');
});
// Make task container take full viewport
const taskView = document.querySelector('.task-view');
if (taskView) {
taskView.style.margin = '0';
taskView.style.padding = '20px';
taskView.style.maxWidth = 'none';
taskView.style.width = '100%';
taskView.style.minHeight = '100vh';
// Remove background color to preserve theme
}
// Style the container to be the only visible element
container.style.position = 'fixed';
container.style.top = '0';
container.style.left = '0';
container.style.width = '100vw';
container.style.height = '100vh';
container.style.margin = '0';
container.style.padding = '0';
// Remove background color to preserve theme
container.style.zIndex = '999999';
container.style.overflow = 'auto';
// Hide body scroll but keep background
document.body.style.margin = '0';
document.body.style.padding = '0';
// Remove background color to preserve theme
document.body.style.overflow = 'hidden';
// Hide html scroll
document.documentElement.style.overflow = 'hidden';
// Add toggle button
addToggleButton();
const header = document.querySelector('header[aria-label="main navigation"]');
if (header) {
header.style.display = 'none';
}
document.querySelector(
'button.back-button.base-button--type-button'
)?.style.setProperty('display', 'none', 'important');
}
// Add toggle button to switch views
function addToggleButton() {
const heading = document.querySelector('.task-view .heading');
if (!heading || document.querySelector('#minimal-toggle')) return;
const toggle = document.createElement('button');
toggle.id = 'minimal-toggle';
toggle.className = 'button';
toggle.textContent = 'Full View';
toggle.style.marginLeft = '10px';
toggle.style.fontSize = '0.8rem';
toggle.style.position = 'fixed';
toggle.style.top = '10px';
toggle.style.right = '10px';
toggle.style.zIndex = '1000000';
toggle.addEventListener('click', toggleView);
document.body.appendChild(toggle);
}
// Toggle between minimal and full view
function toggleView() {
const container = document.querySelector('.task-view-container');
const toggle = document.querySelector('#minimal-toggle');
const isMinimal = container.classList.contains('minimal-view');
if (isMinimal) {
// Switch to full view - restore everything
container.classList.remove('minimal-view');
toggle.textContent = 'Minimal View';
// Restore all hidden elements
const elementsToShow = [
'aside.menu-container',
'nav.menu-container',
'header.navbar',
'.navbar',
'.menu-hide-button',
'.menu-show-button',
'.quick-actions',
'.mobile-overlay',
'.keyboard-shortcuts-button',
'.app-container-background',
'.notification',
'.add-to-home-screen',
'.update-notification',
'.demo-mode',
'.navbar-end',
'.username-dropdown-trigger',
'.avatar',
'.navbar-brand',
'.navbar-start',
'.navbar-menu',
'.navbar-item',
'.navbar-link',
'.navbar-dropdown'
];
elementsToShow.forEach(selector => {
const elements = document.querySelectorAll(selector);
elements.forEach(el => {
el.style.display = '';
el.style.visibility = '';
el.style.width = '';
el.style.height = '';
el.style.overflow = '';
el.style.position = '';
el.style.left = '';
el.style.top = '';
});
});
// Restore task view styles
const taskView = document.querySelector('.task-view');
if (taskView) {
taskView.style.margin = '';
taskView.style.padding = '';
taskView.style.maxWidth = '';
taskView.style.width = '';
taskView.style.minHeight = '';
// Background will revert to default
}
// Restore container styles
container.style.position = '';
container.style.top = '';
container.style.left = '';
container.style.width = '';
container.style.height = '';
container.style.margin = '';
container.style.padding = '';
// Background will revert to default
container.style.zIndex = '';
container.style.overflow = '';
// Restore body styles
document.body.style.margin = '';
document.body.style.padding = '';
// Background will revert to default
document.body.style.overflow = '';
// Restore html scroll
document.documentElement.style.overflow = '';
// Show all hidden task elements
const hidden = document.querySelectorAll('.task-view [style*="display: none"]');
hidden.forEach(el => {
if (!el.id || !el.id.includes('minimal-toggle')) {
el.style.display = '';
}
});
// Move toggle button back to heading
const heading = document.querySelector('.task-view .heading');
if (heading) {
toggle.style.position = '';
toggle.style.top = '';
toggle.style.right = '';
toggle.style.zIndex = '';
heading.appendChild(toggle);
}
} else {
// Switch back to minimal view
applyMinimalView();
}
}
// Add CSS for minimal view
const style = document.createElement('style');
style.textContent = `
.minimal-view {
/* Remove background to preserve theme */
}
.minimal-view .task-view {
padding: 20px !important;
margin: 0 !important;
max-width: none !important;
width: 100% !important;
min-height: 100vh !important;
/* Remove background to preserve theme */
}
/* Ensure description is visible in minimal view */
.minimal-view .details.content.description {
display: block !important;
}
/* Hide all other elements aggressively */
body.minimal-view > *:not(.task-view-container),
.minimal-view .content-auth > *:not(.task-view-container),
.minimal-view .app-container > *:not(.task-view-container) {
display: none !important;
visibility: hidden !important;
}
/* Aggressively hide header and navbar elements */
.minimal-view header,
.minimal-view .navbar,
.minimal-view .navbar-brand,
.minimal-view .navbar-start,
.minimal-view .navbar-end,
.minimal-view .navbar-menu,
.minimal-view .navbar-item,
.minimal-view .navbar-link,
.minimal-view .navbar-dropdown,
.minimal-view .username-dropdown-trigger,
.minimal-view .avatar {
display: none !important;
visibility: hidden !important;
position: absolute !important;
left: -9999px !important;
top: -9999px !important;
}
`;
document.head.appendChild(style);
// Initialize when page loads
function init() {
if (document.querySelector('.task-view')) {
if (isMinimalParam()) {
// Apply minimal view automatically
setTimeout(applyMinimalView, 100);
} else {
// Add toggle button even without minimal parameter
setTimeout(addToggleButton, 100);
}
}
}
// Use MutationObserver to handle dynamic content
const observer = new MutationObserver((mutations) => {
if (document.querySelector('.task-view') && !document.querySelector('#minimal-toggle')) {
init();
}
});
observer.observe(document.body, {
childList: true,
subtree: true
});
// Also try immediately
init();
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment