Files
BetterDesk/web-nodejs/views/layouts/main.ejs
T
UNITRONIX e556b181df Gate agent admin UI by OS privileges
Add OS-level admin detection and use it to gate sensitive agent UI and tray actions. Introduce privileges.rs (Windows TokenElevation / Unix geteuid) and expose is_os_admin as a Tauri command; wire it into tray setup to hide admin-only menu items (Settings, Quit) and re-check privileges before executing those actions. Add show_window helper and emit navigate events from the tray; frontend listens for navigate and conditionally renders /settings (shows AdminRequired component for non-admins). Update App.tsx to query is_os_admin on startup and include navigation listener; add AdminRequired component, styles, and i18n keys. Update Cargo.toml with platform deps (windows features + libc for unix). Also add UI/locale assets and CSS for agent lazy-loaded device tabs and a notifications dropdown, plus several web-nodejs route/view/style updates and new task docs describing phase work.
2026-04-18 01:18:57 +02:00

161 lines
7.9 KiB
HTML

<!DOCTYPE html>
<html lang="<%= lang || 'en' %>">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="<%= appName %> - <%= appDescription %>">
<meta name="robots" content="noindex, nofollow">
<title><%= typeof title !== 'undefined' ? title + ' - ' : '' %><%= appName %></title>
<!-- Favicon -->
<link rel="icon" type="image/svg+xml" href="/branding/favicon.svg">
<!-- Stylesheets -->
<link rel="stylesheet" href="/css/main.css?v=<%= cacheVersion %>">
<link rel="stylesheet" href="/css/theme.css?v=<%= cacheVersion %>">
<link rel="stylesheet" href="/css/transitions.css?v=<%= cacheVersion %>">
<link rel="stylesheet" href="/css/notif-center.css?v=<%= cacheVersion %>">
<% if (!embed) { %>
<link rel="stylesheet" href="/css/desktop-mode.css?v=<%= cacheVersion %>">
<link rel="stylesheet" href="/css/desktop-widgets.css?v=<%= cacheVersion %>">
<link rel="stylesheet" href="/css/tutorial.css?v=<%= cacheVersion %>">
<% } else { %>
<link rel="stylesheet" href="/css/desktop-widget-overrides.css?v=<%= cacheVersion %>">
<% } %>
<% if (typeof pageStyles !== 'undefined' && pageStyles.length) { %>
<% pageStyles.forEach(style => { %>
<link rel="stylesheet" href="/css/<%= style %>.css?v=<%= cacheVersion %>">
<% }); %>
<% } %>
</head>
<body<%= embed ? ' class="embed-mode"' : '' %>>
<div class="app-layout" id="app">
<% if (!embed) { %>
<!-- Sidebar -->
<%- include('../partials/sidebar') %>
<!-- Sidebar overlay for mobile -->
<div class="sidebar-overlay" id="sidebar-overlay"></div>
<% } %>
<!-- Main wrapper -->
<div class="main-wrapper">
<% if (!embed) { %>
<!-- Navbar -->
<%- include('../partials/navbar') %>
<% } %>
<!-- Main content -->
<main class="main-content">
<%- body %>
</main>
</div>
</div>
<% if (!embed) { %>
<!-- Desktop Mode Shell (hidden by default, activated via toggle) -->
<div id="desktop-shell" class="desktop-shell">
<!-- Top bar with logo + app drawer + clock -->
<div class="desktop-topbar" id="desktop-topbar">
<div class="topbar-left">
<button class="topbar-btn topbar-app-drawer-btn" id="topbar-app-drawer-btn" title="<%= _('desktop.app_drawer') || 'Applications' %>">
<span class="material-icons">apps</span>
</button>
<span class="topbar-brand"><%= appName %></span>
<div class="topbar-shortcuts" id="topbar-shortcuts"></div>
<button class="topbar-btn topbar-edit-btn" id="topbar-edit-btn" title="<%= _('desktop.edit_shortcuts') || 'Edit shortcuts' %>">
<span class="material-icons">edit</span>
</button>
</div>
<div class="topbar-center" id="topbar-clock"></div>
<div class="topbar-right">
<button class="topbar-btn" id="topbar-add-widget-btn" title="<%= _('desktop.add_widget') || 'Add Widget' %>">
<span class="material-icons">add_circle_outline</span>
</button>
<button class="topbar-btn" id="topbar-snap-layout-btn" title="<%= _('desktop.label_snap_layout') || 'Window Layout' %>">
<span class="material-icons">grid_view</span>
</button>
<button class="topbar-btn" id="topbar-wallpaper-btn" title="<%= _('desktop.wallpaper') || 'Wallpaper' %>">
<span class="material-icons">wallpaper</span>
</button>
<button class="topbar-btn" id="topbar-fullscreen-btn" title="<%= _('desktop.fullscreen') || 'Fullscreen' %>">
<span class="material-icons">fullscreen</span>
</button>
<button class="topbar-btn" id="topbar-widgets-btn" title="<%= _('desktop.widgets') || 'Widgets' %>">
<span class="material-icons">widgets</span>
</button>
<button class="topbar-btn" id="topbar-help-btn" title="<%= _('desktop.help') || 'Help' %>">
<span class="material-icons">help_outline</span>
</button>
<button class="topbar-btn" id="topbar-exit-btn" title="<%= _('desktop.exit_desktop') %>">
<span class="material-icons">logout</span>
</button>
</div>
</div>
<!-- App drawer overlay -->
<div class="app-drawer-overlay" id="app-drawer-overlay" style="display:none">
<div class="app-drawer" id="app-drawer">
<div class="app-drawer-header">
<input type="text" class="app-drawer-search" id="app-drawer-search" placeholder="<%= _('desktop.search_apps') || 'Search apps...' %>">
</div>
<div class="app-drawer-grid" id="app-drawer-grid"></div>
</div>
</div>
<div class="desktop-wallpaper"></div>
<div class="widget-canvas" id="widget-canvas"></div>
<div class="desktop-icons" id="desktop-icons"></div>
<div class="desktop-windows" id="desktop-windows"></div>
<div class="desktop-taskbar" id="desktop-taskbar">
<div class="taskbar-apps" id="taskbar-apps"></div>
</div>
<!-- Desktop right-click context menu -->
<div class="desktop-context-menu" id="desktop-context-menu" style="display:none">
<button class="ctx-item" data-action="wallpaper"><span class="material-icons">wallpaper</span><%= _('desktop.wallpaper') %></button>
<button class="ctx-item" data-action="layout"><span class="material-icons">grid_view</span><%= _('desktop.label_snap_layout') || 'Window Layout' %></button>
<button class="ctx-item" data-action="refresh"><span class="material-icons">refresh</span><%= _('desktop.refresh') || 'Refresh' %></button>
<div class="ctx-divider"></div>
<button class="ctx-item" data-action="exit"><span class="material-icons">view_sidebar</span><%= _('desktop.exit_desktop') %></button>
</div>
</div>
<% } %>
<!-- Modal container -->
<div id="modal-container"></div>
<!-- Toast container -->
<div id="toast-container" class="toast-container"></div>
<!-- Scripts -->
<script nonce="<%= cspNonce %>">
// Global config
window.BetterDesk = {
lang: '<%= lang || "en" %>',
translations: <%- JSON.stringify(translations || {}) %>,
csrfToken: '<%= typeof csrfToken !== 'undefined' ? csrfToken : '' %>',
user: <%- JSON.stringify(user || null) %>,
branding: <%- JSON.stringify(branding || {}) %>,
embed: <%= embed ? 'true' : 'false' %>
};
</script>
<script src="/js/utils.js?v=<%= cacheVersion %>"></script>
<script src="/js/i18n-client.js?v=<%= cacheVersion %>"></script>
<script src="/js/notifications.js?v=<%= cacheVersion %>"></script>
<script src="/js/notif-center.js?v=<%= cacheVersion %>"></script>
<script src="/js/modal.js?v=<%= cacheVersion %>"></script>
<script src="/js/app.js?v=<%= cacheVersion %>"></script>
<% if (!embed) { %>
<script src="/js/widget-plugins.js?v=<%= cacheVersion %>"></script>
<script src="/js/desktop-widgets.js?v=<%= cacheVersion %>"></script>
<script src="/js/desktop-mode.js?v=<%= cacheVersion %>"></script>
<script src="/js/tutorial.js?v=<%= cacheVersion %>"></script>
<script nonce="<%= cspNonce %>">if (typeof Tutorial !== 'undefined') Tutorial.autoStart('console');</script>
<% } %>
<% if (typeof pageScripts !== 'undefined' && pageScripts.length) { %>
<% pageScripts.forEach(script => { %>
<script src="/js/<%= script %>.js?v=<%= cacheVersion %>"></script>
<% }); %>
<% } %>
</body>
</html>