/** * Spaxel First-Time Feature Discovery Toololtips * * Shows brief, non-intrusive tooltips when users access features for the first time. * Tooltips are shown once per feature and remembered in localStorage. */ (function () { 'use strict'; // Feature IDs that trigger tooltips var FEATURES = { TRIGGER_VOLUMES: 'trigger_volumes', COVERAGE_PAINTING: 'coverage_painting', TIME_TRAVEL: 'time_travel', FRESNEL_ZONES: 'fresnel_zones', PERSON_IDENTITY: 'person_identity', AUTOMATION_BUILDER: 'automation_builder', }; // Cooldown duration (24 hours) var COOLDOWN_MS = 24 * 60 * 60 * 1000; /** * Check if a tooltip should be shown for a feature. * Returns a promise that resolves to {show: boolean, tooltip?: object}. */ function shouldShow(featureId) { // Check localStorage cooldown var cooldownKey = 'spaxel_tooltip_shown_' + featureId; var lastShown = localStorage.getItem(cooldownKey); if (lastShown) { var elapsed = Date.now() - parseInt(lastShown, 10); if (elapsed < COOLDOWN_MS) { return Promise.resolve({ show: false }); } } // Check with server return fetch('/api/guided/tooltip/' + featureId) .then(function(res) { if (!res.ok) { if (res.status === 404) { // No tooltip configured for this feature return { show: false }; } throw new Error('Failed to check tooltip: ' + res.status); } return res.json(); }) .then(function(data) { return data.show ? { show: true, tooltip: data } : { show: false }; }) .catch(function(err) { console.error('[Tooltip] Failed to check tooltip:', err); return { show: false }; }); } /** * Mark a tooltip as shown (dismissed). */ function markShown(featureId) { var cooldownKey = 'spaxel_tooltip_shown_' + featureId; localStorage.setItem(cooldownKey, Date.now().toString()); // Also notify server return fetch('/api/guided/tooltip/' + featureId + '/dismiss', { method: 'POST', headers: { 'Content-Type': 'application/json' }, }).catch(function(err) { console.error('[Tooltip] Failed to dismiss tooltip:', err); }); } /** * Show a tooltip for a feature. * @param {string} featureId - The feature ID * @param {HTMLElement} target - The target element to anchor the tooltip to * @param {string} position - Position: 'top', 'bottom', 'left', or 'right' */ function show(featureId, target, position) { if (!target) return; shouldShow(featureId).then(function(result) { if (!result.show) return; var tooltip = result.tooltip; var el = createTooltipElement(tooltip, position); positionTooltip(el, target, position || 'bottom'); // Auto-dismiss after 10 seconds or on click var dismissTimer = setTimeout(function() { dismiss(el, featureId); }, 10000); el.addEventListener('click', function() { clearTimeout(dismissTimer); dismiss(el, featureId); }); }); } /** * Create the tooltip DOM element. */ function createTooltipElement(tooltip, position) { var el = document.createElement('div'); el.className = 'spaxel-tooltip spaxel-tooltip-' + (position || 'bottom'); el.innerHTML = '