first commit

This commit is contained in:
pmb
2025-06-26 12:44:28 -07:00
commit b672b249a6
15 changed files with 1476 additions and 0 deletions
+205
View File
@@ -0,0 +1,205 @@
/* Custom styles for Finger Web Flask App */
/* Global styles */
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
line-height: 1.6;
}
/* Navigation enhancements */
.navbar-brand {
font-weight: bold;
font-size: 1.5rem;
}
.navbar-nav .nav-link {
font-weight: 500;
transition: color 0.3s ease;
}
.navbar-nav .nav-link:hover {
color: #fff !important;
text-decoration: underline;
}
/* Card enhancements */
.card {
border: none;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
}
/* Jumbotron styling */
.jumbotron {
background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
border: 1px solid #dee2e6;
}
/* Button enhancements */
.btn {
border-radius: 6px;
font-weight: 500;
transition: all 0.3s ease;
}
.btn-primary {
background: linear-gradient(45deg, #007bff, #0056b3);
border: none;
}
.btn-primary:hover {
background: linear-gradient(45deg, #0056b3, #004085);
transform: translateY(-1px);
}
.btn-outline-primary:hover {
transform: translateY(-1px);
}
/* Form enhancements */
.form-control {
border-radius: 6px;
border: 2px solid #e9ecef;
transition: border-color 0.3s ease, box-shadow 0.3s ease;
}
.form-control:focus {
border-color: #007bff;
box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);
}
/* Alert enhancements */
.alert {
border-radius: 8px;
border: none;
}
.alert-success {
background: linear-gradient(45deg, #d4edda, #c3e6cb);
color: #155724;
}
.alert-danger {
background: linear-gradient(45deg, #f8d7da, #f5c6cb);
color: #721c24;
}
/* Footer styling */
footer {
margin-top: auto;
border-top: 1px solid #e9ecef;
}
/* Code block styling */
pre {
font-size: 0.9rem;
line-height: 1.4;
}
pre code {
color: #495057;
}
/* Responsive adjustments */
@media (max-width: 768px) {
.jumbotron {
padding: 2rem 1rem;
}
.display-4 {
font-size: 2rem;
}
.card-body {
padding: 1rem;
}
}
/* Animation for page load */
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
main {
animation: fadeIn 0.6s ease-out;
}
/* Custom utility classes */
.text-gradient {
background: linear-gradient(45deg, #007bff, #6610f2);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.shadow-custom {
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
}
/* Loading animation for forms */
.btn.loading {
position: relative;
color: transparent;
}
.btn.loading::after {
content: "";
position: absolute;
width: 16px;
height: 16px;
top: 50%;
left: 50%;
margin-left: -8px;
margin-top: -8px;
border: 2px solid #ffffff;
border-radius: 50%;
border-top-color: transparent;
animation: spin 1s linear infinite;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
/* Finger command specific styles */
.finger-output pre {
font-family: 'Courier New', Consolas, monospace;
font-size: 0.85rem;
line-height: 1.4;
white-space: pre-wrap;
word-wrap: break-word;
max-height: 500px;
overflow-y: auto;
color: white;
}
.finger-output pre::-webkit-scrollbar {
width: 8px;
}
.finger-output pre::-webkit-scrollbar-track {
background: #2d3748;
}
.finger-output pre::-webkit-scrollbar-thumb {
background: #4a5568;
border-radius: 4px;
}
.finger-output pre::-webkit-scrollbar-thumb:hover {
background: #718096;
}
+205
View File
@@ -0,0 +1,205 @@
// Main JavaScript file for Finger Web Flask App
document.addEventListener('DOMContentLoaded', function() {
// Initialize all functionality when DOM is loaded
initializeFormValidation();
initializeNavigation();
initializeAnimations();
initializeTooltips();
});
// Form validation and enhancement
function initializeFormValidation() {
const forms = document.querySelectorAll('form');
forms.forEach(form => {
form.addEventListener('submit', function(event) {
if (!form.checkValidity()) {
event.preventDefault();
event.stopPropagation();
} else {
// Add loading state to submit button
const submitBtn = form.querySelector('button[type="submit"]');
if (submitBtn) {
submitBtn.classList.add('loading');
submitBtn.disabled = true;
}
}
form.classList.add('was-validated');
});
// Real-time validation feedback
const inputs = form.querySelectorAll('input, textarea');
inputs.forEach(input => {
input.addEventListener('blur', function() {
if (this.checkValidity()) {
this.classList.remove('is-invalid');
this.classList.add('is-valid');
} else {
this.classList.remove('is-valid');
this.classList.add('is-invalid');
}
});
});
});
}
// Navigation enhancements
function initializeNavigation() {
// Highlight current page in navigation
const currentPath = window.location.pathname;
const navLinks = document.querySelectorAll('.navbar-nav .nav-link');
navLinks.forEach(link => {
if (link.getAttribute('href') === currentPath) {
link.classList.add('active');
}
});
// Smooth scrolling for anchor links
const anchorLinks = document.querySelectorAll('a[href^="#"]');
anchorLinks.forEach(link => {
link.addEventListener('click', function(e) {
e.preventDefault();
const target = document.querySelector(this.getAttribute('href'));
if (target) {
target.scrollIntoView({
behavior: 'smooth',
block: 'start'
});
}
});
});
}
// Animation and visual enhancements
function initializeAnimations() {
// Fade in cards on scroll
const observerOptions = {
threshold: 0.1,
rootMargin: '0px 0px -50px 0px'
};
const observer = new IntersectionObserver(function(entries) {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.style.opacity = '1';
entry.target.style.transform = 'translateY(0)';
}
});
}, observerOptions);
// Observe all cards
const cards = document.querySelectorAll('.card');
cards.forEach(card => {
card.style.opacity = '0';
card.style.transform = 'translateY(20px)';
card.style.transition = 'opacity 0.6s ease, transform 0.6s ease';
observer.observe(card);
});
}
// Initialize Bootstrap tooltips
function initializeTooltips() {
// Enable tooltips if Bootstrap is available
if (typeof bootstrap !== 'undefined') {
const tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'));
tooltipTriggerList.map(function(tooltipTriggerEl) {
return new bootstrap.Tooltip(tooltipTriggerEl);
});
}
}
// Utility functions
function showNotification(message, type = 'info') {
// Create a notification element
const notification = document.createElement('div');
notification.className = `alert alert-${type} alert-dismissible fade show position-fixed`;
notification.style.cssText = 'top: 20px; right: 20px; z-index: 9999; min-width: 300px;';
notification.innerHTML = `
${message}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
`;
document.body.appendChild(notification);
// Auto-remove after 5 seconds
setTimeout(() => {
if (notification.parentNode) {
notification.remove();
}
}, 5000);
}
// API interaction helpers
async function fetchAPI(endpoint) {
try {
const response = await fetch(endpoint);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.json();
} catch (error) {
console.error('API fetch error:', error);
showNotification('Failed to fetch data from API', 'danger');
return null;
}
}
// Form submission with AJAX (optional enhancement)
function submitFormAjax(form, successCallback) {
const formData = new FormData(form);
fetch(form.action, {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success) {
showNotification(data.message, 'success');
if (successCallback) successCallback(data);
} else {
showNotification(data.message || 'An error occurred', 'danger');
}
})
.catch(error => {
console.error('Form submission error:', error);
showNotification('Failed to submit form', 'danger');
})
.finally(() => {
// Remove loading state
const submitBtn = form.querySelector('button[type="submit"]');
if (submitBtn) {
submitBtn.classList.remove('loading');
submitBtn.disabled = false;
}
});
}
// Keyboard shortcuts
document.addEventListener('keydown', function(e) {
// Alt + H for Home
if (e.altKey && e.key === 'h') {
e.preventDefault();
window.location.href = '/';
}
// Alt + A for About
if (e.altKey && e.key === 'a') {
e.preventDefault();
window.location.href = '/about';
}
// Alt + F for Finger
if (e.altKey && e.key === 'f') {
e.preventDefault();
window.location.href = '/finger';
}
});
// Console welcome message
console.log('%c🚀 Finger Web Flask App', 'color: #007bff; font-size: 16px; font-weight: bold;');
console.log('%cWelcome to the developer console!', 'color: #6c757d;');
console.log('%cKeyboard shortcuts: Alt+H (Home), Alt+A (About), Alt+F (Finger)', 'color: #6c757d;');