Basically wrote the whole thing.

This commit is contained in:
Aodhan
2025-06-25 04:21:13 +01:00
parent 1ff4a6f6d7
commit c5391a957d
216 changed files with 168676 additions and 1303 deletions

View File

@@ -3,7 +3,11 @@ import { showToast, updateImageInfo } from './utils.js';
document.addEventListener('DOMContentLoaded', () => {
const state = {
currentImageInfo: null,
currentOrientation: 'all',
currentOrientation: ['all'],
currentActions: ['Unactioned'],
previousOrientation: ['all'],
allowNsfw: false,
searchKeywords: [],
isLoading: false,
isDragging: false,
startX: 0,
@@ -17,9 +21,14 @@ document.addEventListener('DOMContentLoaded', () => {
const card = document.getElementById('current-card');
const lastActionText = document.getElementById('last-action');
const orientationFilters = document.querySelector('.orientation-filters');
const actionFilters = document.querySelector('.action-filters');
const modal = document.getElementById('fullscreen-modal');
const fullscreenImage = document.getElementById('fullscreen-image');
const closeModal = document.querySelector('.close-modal');
const searchInput = document.getElementById('search-input');
const nsfwToggleBtn = document.getElementById('toggle-nsfw');
const searchButton = document.getElementById('search-button');
const keywordPillsContainer = document.getElementById('keyword-pills-container');
const SWIPE_THRESHOLD = 100;
@@ -27,10 +36,10 @@ document.addEventListener('DOMContentLoaded', () => {
if (!state.currentImageInfo) return;
card.classList.add(`swipe-${direction}`);
const actionNameMap = { left: 'Discard', right: 'Keep', up: 'Favorite', down: 'Review' };
const actionNameMap = { left: 'Discarded', right: 'Kept', up: 'Favourited', down: 'Reviewing' };
const actionName = actionNameMap[direction] || direction;
lastActionText.textContent = `Last action: ${actionName}`;
const toastMap = { left: 'Discarded', right: 'Kept', up: 'Favorited', down: 'Marked for review' };
const toastMap = { left: 'Discarded', right: 'Kept', up: 'Favourited', down: 'Reviewing' };
showToast(toastMap[direction] || 'Action');
recordSelection(state.currentImageInfo, actionName);
@@ -67,7 +76,23 @@ document.addEventListener('DOMContentLoaded', () => {
state.isLoading = true;
card.classList.add('loading');
fetch(`/random-image?orientation=${state.currentOrientation}&t=${new Date().getTime()}`)
const params = new URLSearchParams({
orientation: state.currentOrientation.join(','),
t: new Date().getTime(),
});
// NSFW param
params.append('allow_nsfw', state.allowNsfw ? '1' : '0');
if (state.searchKeywords.length > 0) {
params.append('search', state.searchKeywords.join(','));
}
if (state.currentActions.length > 0) {
params.append('actions', state.currentActions.join(','));
}
fetch(`/random-image?${params.toString()}`)
.then(response => response.json())
.then(data => {
state.isLoading = false;
@@ -170,11 +195,136 @@ document.addEventListener('DOMContentLoaded', () => {
document.getElementById('btn-up').addEventListener('click', () => performSwipe('up'));
document.getElementById('btn-down').addEventListener('click', () => performSwipe('down'));
document.addEventListener('keydown', (e) => {
if (state.isLoading || document.activeElement === searchInput) return;
const keyMap = {
ArrowLeft: 'left',
ArrowRight: 'right',
ArrowUp: 'up',
ArrowDown: 'down',
};
if (keyMap[e.key]) {
e.preventDefault(); // Prevent scrolling
performSwipe(keyMap[e.key]);
}
});
orientationFilters.addEventListener('click', (e) => {
if (e.target.tagName === 'BUTTON' && !e.target.classList.contains('active')) {
orientationFilters.querySelector('.active').classList.remove('active');
e.target.classList.add('active');
state.currentOrientation = e.target.dataset.orientation;
const button = e.target.closest('button');
if (!button) return;
const clickedOrientation = button.dataset.orientation;
if (clickedOrientation === 'all') {
state.currentOrientation = ['all'];
} else {
// If 'all' was the only active filter, start a new selection
if (state.currentOrientation.length === 1 && state.currentOrientation[0] === 'all') {
state.currentOrientation = [];
}
const index = state.currentOrientation.indexOf(clickedOrientation);
if (index > -1) {
// Already selected, so deselect
state.currentOrientation.splice(index, 1);
} else {
// Not selected, so select
state.currentOrientation.push(clickedOrientation);
}
}
// If no filters are selected after interaction, default to 'all'
if (state.currentOrientation.length === 0) {
state.currentOrientation = ['all'];
}
// Update UI based on the state
orientationFilters.querySelectorAll('button').forEach(btn => {
if (state.currentOrientation.includes(btn.dataset.orientation)) {
btn.classList.add('active');
} else {
btn.classList.remove('active');
}
});
loadNewImage();
});
actionFilters.addEventListener('click', (e) => {
const button = e.target.closest('button');
if (!button) return;
const clickedAction = button.dataset.action;
if (state.currentActions.length === 1 && state.currentActions[0] === 'Unactioned' && clickedAction !== 'Unactioned') {
state.currentActions = [];
}
const index = state.currentActions.indexOf(clickedAction);
if (index > -1) {
state.currentActions.splice(index, 1);
} else {
state.currentActions.push(clickedAction);
}
if (state.currentActions.length === 0) {
state.currentActions = ['Unactioned'];
}
actionFilters.querySelectorAll('button').forEach(btn => {
if (state.currentActions.includes(btn.dataset.action)) {
btn.classList.add('active');
} else {
btn.classList.remove('active');
}
});
loadNewImage();
});
const renderKeywordPills = () => {
keywordPillsContainer.innerHTML = '';
state.searchKeywords.forEach(keyword => {
const pill = document.createElement('div');
pill.className = 'keyword-pill';
pill.textContent = keyword;
const removeBtn = document.createElement('button');
removeBtn.className = 'remove-keyword';
removeBtn.innerHTML = '×';
removeBtn.dataset.keyword = keyword;
pill.appendChild(removeBtn);
keywordPillsContainer.appendChild(pill);
});
};
const addSearchKeyword = () => {
const newKeyword = searchInput.value.trim();
if (newKeyword && !state.searchKeywords.includes(newKeyword)) {
state.searchKeywords.push(newKeyword);
renderKeywordPills();
loadNewImage();
}
searchInput.value = '';
searchInput.focus();
};
searchButton.addEventListener('click', addSearchKeyword);
searchInput.addEventListener('keypress', (e) => {
if (e.key === 'Enter') {
addSearchKeyword();
}
});
keywordPillsContainer.addEventListener('click', (e) => {
if (e.target.classList.contains('remove-keyword')) {
const keywordToRemove = e.target.dataset.keyword;
state.searchKeywords = state.searchKeywords.filter(k => k !== keywordToRemove);
renderKeywordPills();
loadNewImage();
}
});
@@ -185,6 +335,7 @@ document.addEventListener('DOMContentLoaded', () => {
document.getElementById('modal-resolution').textContent = `Resolution: ${state.currentImageInfo.resolution}`;
document.getElementById('modal-filename').textContent = `Filename: ${state.currentImageInfo.filename || 'N/A'}`;
document.getElementById('modal-creation-date').textContent = `Creation Date: ${state.currentImageInfo.creation_date || 'N/A'}`;
document.getElementById('modal-prompt-data').textContent = `Prompt: ${state.currentImageInfo.prompt_data || 'N/A'}`;
modal.style.display = 'flex';
}
});
@@ -211,5 +362,43 @@ document.addEventListener('DOMContentLoaded', () => {
}
});
loadNewImage();
// --- Ultra-wide mode ---
const fullscreenToggle = document.getElementById('fullscreen-toggle');
fullscreenToggle.setAttribute('title', 'Toggle fullscreen Mode');
const setfullscreenMode = (isActive) => {
if (isActive) {
// Entering ultra-wide mode: just disable filter controls
orientationFilters.style.pointerEvents = 'none';
orientationFilters.style.opacity = '0.5';
} else {
// Exiting ultra-wide mode: re-enable filter controls
orientationFilters.style.pointerEvents = 'auto';
orientationFilters.style.opacity = '1';
}
};
fullscreenToggle.addEventListener('click', () => {
const isActive = document.body.classList.toggle('fullscreen-mode');
localStorage.setItem('fullscreenMode', isActive);
showToast(isActive ? 'fullscreen mode enabled' : 'fullscreen mode disabled');
setfullscreenMode(isActive);
});
// Check for saved preference on load
const isfullscreenModeOnLoad = localStorage.getItem('fullscreenMode') === 'true';
if (isfullscreenModeOnLoad) {
document.body.classList.add('fullscreen-mode');
setfullscreenMode(true);
}
// --- NSFW toggle ---
nsfwToggleBtn.addEventListener('click', () => {
state.allowNsfw = !state.allowNsfw;
nsfwToggleBtn.dataset.allow = state.allowNsfw ? '1' : '0';
nsfwToggleBtn.classList.toggle('active', state.allowNsfw);
loadNewImage();
});
loadNewImage(); // Always load an image on startup
});