/** * BetterDesk Console - Visual Tutorial System * Spotlight-based guided tour with i18n support. * * Usage: * Tutorial.start('console'); // Console panel tutorial * Tutorial.start('desktop'); // Desktop mode tutorial * Tutorial.start('devices'); // Devices page tutorial * Tutorial.start('settings'); // Settings page tutorial * Tutorial.skip(); // Close tutorial * Tutorial.autoStart('console'); // Auto-start on first visit */ (function() { 'use strict'; // ============ Constants ============ var STORAGE_SEEN = 'betterdesk_tutorial_seen'; var STORAGE_DISABLED = 'betterdesk_tutorial_disabled'; // ============ State ============ var _overlay = null; var _spotlight = null; var _tooltip = null; var _steps = []; var _currentStep = 0; var _isActive = false; var _onComplete = null; var _tutorialType = 'console'; var _resizeHandler = null; // ============ i18n Helper ============ function t(key, fallback) { if (typeof window._ === 'function') { var result = window._(key); return result !== key ? result : fallback; } if (window.BetterDesk && window.BetterDesk.translations) { var keys = key.split('.'); var val = window.BetterDesk.translations; for (var i = 0; i < keys.length; i++) { if (val && typeof val === 'object' && keys[i] in val) { val = val[keys[i]]; } else { return fallback; } } return val || fallback; } return fallback; } // ============ Tutorial Definitions ============ function getConsoleTutorialSteps() { return [ { selector: '.sidebar-rail', title: t('tutorial.console_sidebar_title', 'Navigation Sidebar'), text: t('tutorial.console_sidebar_text', 'Access all modules from here. Click any icon to navigate to that section. The sidebar adapts to your screen size.'), position: 'right', highlight: true }, { selector: '.sidebar-rail-bottom .sidebar-rail-btn[title]', title: t('tutorial.console_expand_title', 'Expand Sidebar'), text: t('tutorial.console_expand_text', 'Click here to expand the sidebar and see full labels for all navigation items. Click again to collapse.'), position: 'right', highlight: true }, { selector: '.navbar .search-box', title: t('tutorial.console_search_title', 'Quick Search'), text: t('tutorial.console_search_text', 'Search for devices, settings, or actions quickly. Press Ctrl+K for keyboard shortcut.'), position: 'bottom', highlight: true }, { selector: '.content-wrapper', title: t('tutorial.console_dashboard_title', 'Dashboard'), text: t('tutorial.console_dashboard_text', 'Your central hub showing server status, online devices, and quick actions. Keep an eye on important metrics at a glance.'), position: 'center', highlight: false }, { selector: null, title: t('tutorial.console_complete_title', 'You\'re Ready!'), text: t('tutorial.console_complete_text', 'Explore the sidebar categories to discover all features. Click the Help icon anytime to restart this guide.'), position: 'center', highlight: false, final: true } ]; } function getDesktopTutorialSteps() { return [ { selector: '.desktop-topbar', title: t('tutorial.desktop_topbar_title', 'Top Bar'), text: t('tutorial.desktop_topbar_text', 'The top bar shows the clock, quick controls, and access to the app drawer. Click the grid icon to browse all apps.'), position: 'bottom', highlight: true }, { selector: '.app-drawer-overlay .app-drawer', title: t('tutorial.desktop_drawer_title', 'App Drawer'), text: t('tutorial.desktop_drawer_text', 'Open the app drawer to search and launch any console function as a floating window. Apps are organized by category.'), position: 'bottom', highlight: true, beforeShow: function() { var btn = document.querySelector('.topbar-app-drawer-btn'); if (btn) btn.click(); }, afterHide: function() { var overlay = document.querySelector('.app-drawer-overlay'); if (overlay && overlay.classList.contains('open')) { overlay.classList.remove('open'); } } }, { selector: '.widget-canvas', title: t('tutorial.desktop_widgets_title', 'Widget Dashboard'), text: t('tutorial.desktop_widgets_text', 'Your customizable workspace. Widgets display live information and provide quick access to features. Drag to rearrange.'), position: 'center', highlight: false }, { selector: '.desktop-taskbar', title: t('tutorial.desktop_taskbar_title', 'Taskbar'), text: t('tutorial.desktop_taskbar_text', 'Open apps appear here. Click to focus or minimize windows. The clock shows current time and wallpaper button changes the background.'), position: 'top', highlight: true }, { selector: null, title: t('tutorial.desktop_snap_title', 'Snap Layouts'), text: t('tutorial.desktop_snap_text', 'Drag windows to screen edges to snap them into position, or hover the maximize button for layout presets. Try Aero Shake!'), position: 'center', highlight: false }, { selector: null, title: t('tutorial.desktop_complete_title', 'Desktop Mode Ready!'), text: t('tutorial.desktop_complete_text', 'Try opening apps from the drawer and arranging windows side-by-side. Use widget presets for quick dashboard layouts.'), position: 'center', highlight: false, final: true } ]; } function getDevicesTutorialSteps() { return [ { selector: '.folder-chips', title: t('tutorial.devices_folders_title', 'Device Folders'), text: t('tutorial.devices_folders_text', 'Organize devices into folders. Click a folder to filter the list. Right-click to rename or delete.'), position: 'bottom', highlight: true }, { selector: '.search-devices', title: t('tutorial.devices_search_title', 'Search & Filter'), text: t('tutorial.devices_search_text', 'Search by ID, hostname, or platform. Use the status pills to show only online, offline, or all devices.'), position: 'bottom', highlight: true }, { selector: '.devices-table', title: t('tutorial.devices_table_title', 'Device List'), text: t('tutorial.devices_table_text', 'Click any device row for details. Use the kebab menu on the right for quick actions like connect, edit tags, or delete.'), position: 'center', highlight: false }, { selector: null, title: t('tutorial.devices_complete_title', 'Device Management Ready!'), text: t('tutorial.devices_complete_text', 'Drag devices between folders to organize them. Double-click a device to open its detail panel.'), position: 'center', highlight: false, final: true } ]; } function getSettingsTutorialSteps() { return [ { selector: '.settings-tabs, .settings-nav', title: t('tutorial.settings_tabs_title', 'Settings Categories'), text: t('tutorial.settings_tabs_text', 'Navigate between General, Security, Appearance, and other configuration sections.'), position: 'right', highlight: true }, { selector: '.settings-content, .settings-panel', title: t('tutorial.settings_content_title', 'Configuration'), text: t('tutorial.settings_content_text', 'Each section contains specific server and console settings. Changes are saved when you click Save.'), position: 'center', highlight: false }, { selector: null, title: t('tutorial.settings_complete_title', 'Settings Overview'), text: t('tutorial.settings_complete_text', 'Check Security settings for 2FA, API keys and user management. Visit Appearance to customize the console look.'), position: 'center', highlight: false, final: true } ]; } function getStepsForType(type) { switch (type) { case 'desktop': return getDesktopTutorialSteps(); case 'devices': return getDevicesTutorialSteps(); case 'settings': return getSettingsTutorialSteps(); case 'remote': return getRemoteTutorialSteps(); case 'organization': return getOrganizationTutorialSteps(); case 'cdap': return getCDAPTutorialSteps(); case 'chat': return getChatTutorialSteps(); default: return getConsoleTutorialSteps(); } } function getRemoteTutorialSteps() { return [ { selector: '.remote-toolbar, .toolbar', title: t('tutorial.remote_toolbar_title', 'Remote Toolbar'), text: t('tutorial.remote_toolbar_text', 'Access display controls, clipboard sync, special keys, and quality settings from the toolbar. Toggle fullscreen with F11.'), position: 'bottom', highlight: true }, { selector: '#remote-canvas, .remote-canvas', title: t('tutorial.remote_canvas_title', 'Remote Display'), text: t('tutorial.remote_canvas_text', 'This is the remote screen. Click inside to start controlling the remote device. Mouse and keyboard input are forwarded automatically.'), position: 'center', highlight: false }, { selector: '.clipboard-btn, .toolbar-clipboard', title: t('tutorial.remote_clipboard_title', 'Clipboard Sync'), text: t('tutorial.remote_clipboard_text', 'Copy and paste text between your computer and the remote device. Use Ctrl+C / Ctrl+V as normal while focused on the remote display.'), position: 'bottom', highlight: true }, { selector: null, title: t('tutorial.remote_complete_title', 'Remote Session Ready!'), text: t('tutorial.remote_complete_text', 'Use the toolbar buttons for special keys (Ctrl+Alt+Del, PrintScreen) and monitor switching. Press Escape or click Disconnect to end the session.'), position: 'center', highlight: false, final: true } ]; } function getOrganizationTutorialSteps() { return [ { selector: '.org-header, .organizations-page h1', title: t('tutorial.org_overview_title', 'Organizations'), text: t('tutorial.org_overview_text', 'Organizations let you group users, devices, and policies. Each organization has its own admin, operators, and settings.'), position: 'bottom', highlight: true }, { selector: '.org-create-btn, .btn-create-org', title: t('tutorial.org_create_title', 'Create Organization'), text: t('tutorial.org_create_text', 'Click here to create a new organization. Provide a name, slug, and optional description. You will be the owner of the new organization.'), position: 'bottom', highlight: true }, { selector: '.org-members, .org-users-tab', title: t('tutorial.org_members_title', 'Manage Members'), text: t('tutorial.org_members_text', 'Invite users by email, assign roles (admin, operator, user), and manage access. Members can see devices assigned to their organization.'), position: 'right', highlight: true }, { selector: '.org-devices, .org-devices-tab', title: t('tutorial.org_devices_title', 'Assign Devices'), text: t('tutorial.org_devices_text', 'Add devices to the organization. Devices assigned here will inherit the organization\'s policies and be visible to its members.'), position: 'right', highlight: true }, { selector: '.org-policies, .org-settings-tab', title: t('tutorial.org_policies_title', 'Organization Policies'), text: t('tutorial.org_policies_text', 'Set security policies like password requirements, session timeouts, 2FA enforcement, and device enrollment rules for the organization.'), position: 'right', highlight: true }, { selector: null, title: t('tutorial.org_complete_title', 'Organization Setup Complete!'), text: t('tutorial.org_complete_text', 'Invite your team and assign devices to start using organization-scoped management. Policies apply automatically to all members.'), position: 'center', highlight: false, final: true } ]; } function getCDAPTutorialSteps() { return [ { selector: '.cdap-device-list, .cdap-devices', title: t('tutorial.cdap_devices_title', 'CDAP Devices'), text: t('tutorial.cdap_devices_text', 'View all connected CDAP devices — IoT sensors, PLCs, bridges, and custom agents. Green indicators show live connections.'), position: 'bottom', highlight: true }, { selector: '.cdap-widget-grid, .cdap-widgets', title: t('tutorial.cdap_widgets_title', 'Widget Dashboard'), text: t('tutorial.cdap_widgets_text', 'Widgets display real-time data from devices. Gauges, toggles, LEDs, charts — each widget type visualizes a different kind of data.'), position: 'center', highlight: false }, { selector: '.cdap-command-panel, .cdap-commands', title: t('tutorial.cdap_commands_title', 'Send Commands'), text: t('tutorial.cdap_commands_text', 'Interact with devices by sending commands. Some commands need confirmation. The command log keeps a history of all sent commands.'), position: 'left', highlight: true }, { selector: '.cdap-terminal-btn, .cdap-terminal', title: t('tutorial.cdap_terminal_title', 'Device Terminal'), text: t('tutorial.cdap_terminal_text', 'Open a terminal session to CDAP agents for advanced diagnostics or manual configuration. Type commands directly.'), position: 'bottom', highlight: true }, { selector: null, title: t('tutorial.cdap_complete_title', 'CDAP Overview Complete!'), text: t('tutorial.cdap_complete_text', 'Explore device widgets, send commands, and use the terminal or file browser for deeper management. CDAP agents report metrics automatically.'), position: 'center', highlight: false, final: true } ]; } function getChatTutorialSteps() { return [ { selector: '.chat-contacts, .chat-sidebar', title: t('tutorial.chat_contacts_title', 'Contacts & Groups'), text: t('tutorial.chat_contacts_text', 'See your contacts and chat groups. Click any contact to open a conversation. Green dots indicate online users.'), position: 'right', highlight: true }, { selector: '.chat-messages, .chat-area', title: t('tutorial.chat_send_title', 'Send Messages'), text: t('tutorial.chat_send_text', 'Type your message and press Enter to send. Messages are end-to-end encrypted when both parties support E2E.'), position: 'center', highlight: false }, { selector: '.chat-file-btn, .chat-attach', title: t('tutorial.chat_file_title', 'Share Files'), text: t('tutorial.chat_file_text', 'Click the attachment icon to share files. Files are encrypted and can be up to 50 MB.'), position: 'top', highlight: true }, { selector: null, title: t('tutorial.chat_complete_title', 'Chat Ready!'), text: t('tutorial.chat_complete_text', 'Chat with operators and end users in real time. Create groups for team communication. All messages support read receipts.'), position: 'center', highlight: false, final: true } ]; } // ============ DOM Creation ============ function createOverlay() { if (_overlay) return; _overlay = document.createElement('div'); _overlay.className = 'tutorial-overlay'; _overlay.id = 'tutorial-overlay'; _spotlight = document.createElement('div'); _spotlight.className = 'tutorial-spotlight'; _overlay.appendChild(_spotlight); _tooltip = document.createElement('div'); _tooltip.className = 'tutorial-tooltip'; _tooltip.innerHTML = '