Registry Features (Phases 1-6): - Tool manifest format and validation - Registry client for API communication - Resolver integration for tool installation - CLI commands: install, search, browse, publish, auth - TUI browse interface with urwid - Smart features: auto-categorization, similarity suggestions Web UI (Phase 7): - Flask blueprint with server-side sessions - Authentication routes (login, register, logout) - Public pages: landing, tools browse, tool detail, search - Dashboard: overview, tools management, API tokens, settings - Template components: cards, forms, callouts, code blocks - Tailwind CSS build pipeline - SEO infrastructure (sitemap, robots.txt) - Cookie consent and privacy compliance Infrastructure: - SQLite database with FTS5 search - Rate limiting for API endpoints - Git-based tool submission workflow - Session management with auto-cleanup 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]>
149 lines
6.7 KiB
HTML
149 lines
6.7 KiB
HTML
{# Cookie consent banner #}
|
|
<div id="consent-banner"
|
|
class="fixed bottom-0 inset-x-0 z-50 bg-white border-t border-gray-200 shadow-lg transform transition-transform duration-300"
|
|
role="dialog"
|
|
aria-labelledby="consent-title"
|
|
aria-describedby="consent-description">
|
|
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-4">
|
|
<div class="flex flex-col sm:flex-row items-start sm:items-center justify-between gap-4">
|
|
<div class="flex-1">
|
|
<h3 id="consent-title" class="text-sm font-semibold text-gray-900">
|
|
We value your privacy
|
|
</h3>
|
|
<p id="consent-description" class="mt-1 text-sm text-gray-600">
|
|
We use cookies to improve your experience and for analytics.
|
|
You can choose to accept all cookies or manage your preferences.
|
|
<a href="{{ url_for('web.privacy') }}" class="text-indigo-600 hover:text-indigo-800 underline">Learn more</a>
|
|
</p>
|
|
</div>
|
|
<div class="flex items-center gap-3 flex-shrink-0">
|
|
<button type="button"
|
|
onclick="openConsentPreferences()"
|
|
class="px-4 py-2 text-sm font-medium text-gray-700 hover:text-gray-900 transition-colors">
|
|
Manage preferences
|
|
</button>
|
|
<button type="button"
|
|
onclick="rejectConsent()"
|
|
class="px-4 py-2 text-sm font-medium text-gray-700 border border-gray-300 rounded-md hover:bg-gray-50 transition-colors">
|
|
Reject all
|
|
</button>
|
|
<button type="button"
|
|
onclick="acceptAllConsent()"
|
|
class="px-4 py-2 text-sm font-medium text-white bg-indigo-600 rounded-md hover:bg-indigo-700 transition-colors">
|
|
Accept all
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Consent preferences modal -->
|
|
<div id="consent-modal"
|
|
class="hidden fixed inset-0 z-50 overflow-y-auto"
|
|
aria-modal="true"
|
|
role="dialog"
|
|
aria-labelledby="consent-modal-title">
|
|
<div class="min-h-screen px-4 text-center">
|
|
<!-- Backdrop -->
|
|
<div class="fixed inset-0 bg-black bg-opacity-50 transition-opacity" onclick="closeConsentPreferences()"></div>
|
|
|
|
<!-- Modal -->
|
|
<div class="inline-block w-full max-w-lg my-8 text-left align-middle transition-all transform bg-white shadow-xl rounded-lg">
|
|
<div class="p-6">
|
|
<h3 id="consent-modal-title" class="text-lg font-semibold text-gray-900">
|
|
Cookie Preferences
|
|
</h3>
|
|
<p class="mt-2 text-sm text-gray-600">
|
|
Choose which cookies you want to accept. Essential cookies cannot be disabled as they are required for the site to function.
|
|
</p>
|
|
|
|
<div class="mt-6 space-y-4">
|
|
<!-- Essential cookies (always on) -->
|
|
<div class="flex items-start justify-between p-4 bg-gray-50 rounded-lg">
|
|
<div>
|
|
<h4 class="text-sm font-medium text-gray-900">Essential cookies</h4>
|
|
<p class="mt-1 text-sm text-gray-500">Required for the website to function properly.</p>
|
|
</div>
|
|
<input type="checkbox" checked disabled class="mt-1 h-4 w-4 text-indigo-600 border-gray-300 rounded">
|
|
</div>
|
|
|
|
<!-- Analytics cookies -->
|
|
<div class="flex items-start justify-between p-4 bg-gray-50 rounded-lg">
|
|
<div>
|
|
<label for="consent-analytics" class="text-sm font-medium text-gray-900 cursor-pointer">Analytics cookies</label>
|
|
<p class="mt-1 text-sm text-gray-500">Help us understand how visitors use our site.</p>
|
|
</div>
|
|
<input type="checkbox"
|
|
id="consent-analytics"
|
|
name="analytics"
|
|
class="mt-1 h-4 w-4 text-indigo-600 focus:ring-indigo-500 border-gray-300 rounded">
|
|
</div>
|
|
|
|
<!-- Advertising cookies -->
|
|
<div class="flex items-start justify-between p-4 bg-gray-50 rounded-lg">
|
|
<div>
|
|
<label for="consent-ads" class="text-sm font-medium text-gray-900 cursor-pointer">Advertising cookies</label>
|
|
<p class="mt-1 text-sm text-gray-500">Used to show relevant ads and support the project.</p>
|
|
</div>
|
|
<input type="checkbox"
|
|
id="consent-ads"
|
|
name="ads"
|
|
class="mt-1 h-4 w-4 text-indigo-600 focus:ring-indigo-500 border-gray-300 rounded">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="bg-gray-50 px-6 py-4 rounded-b-lg flex justify-end gap-3">
|
|
<button type="button"
|
|
onclick="closeConsentPreferences()"
|
|
class="px-4 py-2 text-sm font-medium text-gray-700 hover:text-gray-900 transition-colors">
|
|
Cancel
|
|
</button>
|
|
<button type="button"
|
|
onclick="saveConsentPreferences()"
|
|
class="px-4 py-2 text-sm font-medium text-white bg-indigo-600 rounded-md hover:bg-indigo-700 transition-colors">
|
|
Save preferences
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
function acceptAllConsent() {
|
|
saveConsent(true, true);
|
|
}
|
|
|
|
function rejectConsent() {
|
|
saveConsent(false, false);
|
|
}
|
|
|
|
function openConsentPreferences() {
|
|
document.getElementById('consent-modal').classList.remove('hidden');
|
|
}
|
|
|
|
function closeConsentPreferences() {
|
|
document.getElementById('consent-modal').classList.add('hidden');
|
|
}
|
|
|
|
function saveConsentPreferences() {
|
|
const analytics = document.getElementById('consent-analytics').checked;
|
|
const ads = document.getElementById('consent-ads').checked;
|
|
saveConsent(analytics, ads);
|
|
closeConsentPreferences();
|
|
}
|
|
|
|
function saveConsent(analytics, ads) {
|
|
fetch('/api/v1/consent', {
|
|
method: 'POST',
|
|
headers: {'Content-Type': 'application/json'},
|
|
body: JSON.stringify({analytics: analytics, ads: ads})
|
|
}).then(() => {
|
|
document.getElementById('consent-banner').style.transform = 'translateY(100%)';
|
|
setTimeout(() => {
|
|
document.getElementById('consent-banner').remove();
|
|
}, 300);
|
|
});
|
|
}
|
|
</script>
|