';
-
+
// Section header
h += '';
// Security settings cards
h += '
';
h += `
`;
@@ -200,7 +200,7 @@ const TabSecurity = {
is_resizable: false,
body_css: {
width: 'initial',
- 'background-color': 'rgb(245 247 249)',
+ 'background-color': 'var(--dashboard-input-background)',
'backdrop-filter': 'blur(3px)',
padding: '20px',
},
diff --git a/src/gui/src/UI/Dashboard/UIDashboard.js b/src/gui/src/UI/Dashboard/UIDashboard.js
index 3ea38cc0e..ddee60349 100644
--- a/src/gui/src/UI/Dashboard/UIDashboard.js
+++ b/src/gui/src/UI/Dashboard/UIDashboard.js
@@ -1,3 +1,5 @@
+/* eslint-disable no-invalid-this */
+/* eslint-disable @stylistic/indent */
/**
* Copyright (C) 2024-present Puter Technologies Inc.
*
@@ -53,13 +55,22 @@ import TabSecurity from './TabSecurity.js';
const builtinTabs = [
TabHome,
// TabApps,
- // TabFiles,
+ TabFiles,
TabUsage,
TabAccount,
TabSecurity,
];
+// Dynamically load dashboard CSS if not already loaded
+if ( ! document.querySelector('link[href*="dashboard.css"]') ) {
+ const link = document.createElement('link');
+ link.rel = 'stylesheet';
+ link.href = '/css/dashboard.css';
+ document.head.appendChild(link);
+}
+
async function UIDashboard (options) {
+ // eslint-disable-next-line no-unused-vars
options = options ?? {};
// Create mutable tabs array from built-in tabs
@@ -71,12 +82,12 @@ async function UIDashboard (options) {
let h = '';
h += '
';
-
+
// Mobile sidebar toggle
h += '';
-
+
// Sidebar
h += '';
@@ -133,6 +145,11 @@ async function UIDashboard (options) {
const $el_window = $(el_window);
+ // Set initial file path BEFORE tabs are initialized (so TabFiles.init() can use it)
+ if ( window.dashboard_initial_route?.tab === 'files' && window.dashboard_initial_route?.path ) {
+ window.dashboard_initial_file_path = window.dashboard_initial_route.path;
+ }
+
// Initialize all tabs
for ( const tab of tabs ) {
if ( tab.init ) {
@@ -143,15 +160,225 @@ async function UIDashboard (options) {
// Dispatch 'dashboard-ready' event for extensions
window.dispatchEvent(new CustomEvent('dashboard-ready', { detail: { window: $el_window } }));
+ // =========================================================================
+ // Socket initialization
+ // In dashboard mode, UIDesktop is never loaded, so we create the socket here.
+ // This runs inside the function (not at module level) to ensure window.gui_origin
+ // and window.auth_token are already set.
+ // =========================================================================
+ window.socket = io(`${window.gui_origin}/`, {
+ auth: {
+ auth_token: window.auth_token,
+ },
+ });
+
+ window.socket.on('error', (error) => {
+ console.error('Dashboard Socket Error:', error);
+ });
+
+ window.socket.on('connect', function () {
+ window.socket.emit('puter_is_actually_open');
+ });
+
+ window.socket.on('reconnect', function () {
+ console.log('Dashboard Socket: Reconnected', window.socket.id);
+ });
+
+ window.socket.on('disconnect', () => {
+ console.log('Dashboard Socket: Disconnected');
+ });
+
+ window.socket.on('reconnect_attempt', (attempt) => {
+ console.log('Dashboard Socket: Reconnection Attempt', attempt);
+ });
+
+ window.socket.on('reconnect_error', (error) => {
+ console.log('Dashboard Socket: Reconnection Error', error);
+ });
+
+ window.socket.on('reconnect_failed', () => {
+ console.log('Dashboard Socket: Reconnection Failed');
+ });
+
+ // Upload/download progress tracking
+ window.socket.on('upload.progress', (msg) => {
+ if ( window.progress_tracker[msg.operation_id] ) {
+ window.progress_tracker[msg.operation_id].cloud_uploaded += msg.loaded_diff;
+ if ( window.progress_tracker[msg.operation_id][msg.item_upload_id] ) {
+ window.progress_tracker[msg.operation_id][msg.item_upload_id].cloud_uploaded = msg.loaded;
+ }
+ }
+ });
+
+ window.socket.on('download.progress', (msg) => {
+ if ( window.progress_tracker[msg.operation_id] ) {
+ if ( window.progress_tracker[msg.operation_id][msg.item_upload_id] ) {
+ window.progress_tracker[msg.operation_id][msg.item_upload_id].downloaded = msg.loaded;
+ window.progress_tracker[msg.operation_id][msg.item_upload_id].total = msg.total;
+ }
+ }
+ });
+
+ // Trash status updates
+ window.socket.on('trash.is_empty', async (msg) => {
+ // Update sidebar Trash icon
+ const trashIcon = msg.is_empty ? window.icons['trash.svg'] : window.icons['trash-full.svg'];
+ $('.directories [data-folder=\'Trash\'] img').attr('src', trashIcon);
+
+ // If currently viewing trash and it's empty, clear the file list
+ const dashboard = window.dashboard_object;
+ if ( msg.is_empty && dashboard && dashboard.currentPath === window.trash_path ) {
+ $('.files-tab .files').empty();
+ }
+ });
+
+ // =========================================================================
+ // Item event handlers
+ // Incremental DOM updates using UIDashboardFileItem for item creation and
+ // direct jQuery manipulation for removals/updates. Mirrors UIDesktop's
+ // approach but adapted for Dashboard's list-view structure.
+ // =========================================================================
+
+ window.socket.on('item.moved', async (resp) => {
+ if ( resp.original_client_socket_id === window.socket.id ) return;
+
+ // Fade out old item from view
+ $(`.item[data-uid='${resp.uid}']`).fadeOut(150, function () {
+ $(this).remove();
+ });
+
+ // Create new item at destination if user is viewing that directory
+ if ( window.UIDashboardFileItem ) {
+ window.UIDashboardFileItem(resp);
+ }
+ });
+
+ window.socket.on('item.removed', async (item) => {
+ if ( item.original_client_socket_id === window.socket.id ) return;
+ if ( item.descendants_only ) return;
+
+ $(`.item[data-path='${html_encode(item.path)}']`).fadeOut(150, function () {
+ $(this).remove();
+ });
+ });
+
+ window.socket.on('item.renamed', async (item) => {
+ if ( item.original_client_socket_id === window.socket.id ) return;
+
+ const $el = $(`.item[data-uid='${item.uid}']`);
+ if ( $el.length === 0 ) return;
+
+ // Update data attributes
+ $el.attr('data-name', html_encode(item.name));
+ $el.attr('data-path', html_encode(item.path));
+
+ // Update displayed name
+ $el.find('.item-name').text(item.name);
+ $el.find('.item-name-editor').val(item.name);
+ });
+
+ window.socket.on('item.updated', async (item) => {
+ if ( item.original_client_socket_id === window.socket.id ) return;
+
+ const $el = $(`.item[data-uid='${item.uid}']`);
+ if ( $el.length === 0 ) return;
+
+ // Update data attributes
+ $el.attr('data-name', html_encode(item.name));
+ $el.attr('data-path', html_encode(item.path));
+ $el.attr('data-size', item.size);
+ $el.attr('data-modified', item.modified);
+ $el.attr('data-type', html_encode(item.type));
+
+ // Update displayed name
+ $el.find('.item-name').text(item.name);
+ $el.find('.item-name-editor').val(item.name);
+ });
+
+ window.socket.on('item.added', async (item) => {
+ if ( _.isEmpty(item) ) return;
+ if ( item.original_client_socket_id === window.socket.id ) return;
+
+ if ( window.UIDashboardFileItem ) {
+ window.UIDashboardFileItem(item);
+ }
+ });
+
+ // Apply initial route from URL - activate the correct tab
+ if ( window.dashboard_initial_route ) {
+ const route = window.dashboard_initial_route;
+
+ // Activate the correct tab if not home
+ if ( route.tab && route.tab !== 'home' ) {
+ const tabId = route.tab;
+ const $targetTab = $el_window.find(`.dashboard-sidebar-item[data-section="${tabId}"]`);
+
+ // Only switch if the tab exists
+ if ( $targetTab.length > 0 ) {
+ $el_window.find('.dashboard-sidebar-item').removeClass('active');
+ $targetTab.addClass('active');
+ $el_window.find('.dashboard-section').removeClass('active');
+ $el_window.find(`.dashboard-section[data-section="${tabId}"]`).addClass('active');
+
+ document.querySelector('.dashboard-content').setAttribute('class', 'dashboard-content');
+ document.querySelector('.dashboard-content').classList.add(tabId);
+
+ // Call onActivate if exists
+ const tab = tabs.find(t => t.id === tabId);
+ if ( tab?.onActivate ) {
+ tab.onActivate($el_window);
+ }
+ }
+ }
+ }
+
+ // Handle browser back/forward navigation
+ // This handler is called for both hashchange (manual hash changes) and popstate (back/forward)
+ const handleRouteChange = () => {
+ const route = window.parseDashboardRoute();
+ const tab = route.tab;
+ const filePath = route.path;
+
+ // Switch to correct tab
+ const $targetTab = $el_window.find(`.dashboard-sidebar-item[data-section="${tab}"]`);
+ if ( tab === 'home' ) {
+ // Home tab
+ $el_window.find('.dashboard-sidebar-item').removeClass('active');
+ $el_window.find('.dashboard-sidebar-item').first().addClass('active');
+ $el_window.find('.dashboard-section').removeClass('active');
+ $el_window.find('.dashboard-section').first().addClass('active');
+ document.querySelector('.dashboard-content').setAttribute('class', 'dashboard-content');
+ } else if ( $targetTab.length > 0 ) {
+ $el_window.find('.dashboard-sidebar-item').removeClass('active');
+ $targetTab.addClass('active');
+ $el_window.find('.dashboard-section').removeClass('active');
+ $el_window.find(`.dashboard-section[data-section="${tab}"]`).addClass('active');
+ document.querySelector('.dashboard-content').setAttribute('class', 'dashboard-content');
+ document.querySelector('.dashboard-content').classList.add(tab);
+ }
+
+ // If files tab with path, navigate without adding to history
+ if ( tab === 'files' && filePath ) {
+ const filesTab = tabs.find(t => t.id === 'files');
+ if ( filesTab?.renderDirectory ) {
+ filesTab.renderDirectory(filePath, { skipUrlUpdate: true, skipNavHistory: true });
+ }
+ }
+ };
+
+ // Listen for both hashchange and popstate to handle all navigation scenarios
+ window.addEventListener('hashchange', handleRouteChange);
+ window.addEventListener('popstate', handleRouteChange);
+
// Sidebar item click handler
$el_window.on('click', '.dashboard-sidebar-item', function () {
const $this = $(this);
const section = $this.attr('data-section');
-
+
// Update active sidebar item
$el_window.find('.dashboard-sidebar-item').removeClass('active');
$this.addClass('active');
-
+
// Update active content section
$el_window.find('.dashboard-section').removeClass('active');
$el_window.find(`.dashboard-section[data-section="${section}"]`).addClass('active');
@@ -162,6 +389,16 @@ async function UIDashboard (options) {
tab.onActivate($el_window);
}
+ document.querySelector('.dashboard-content').setAttribute('class', 'dashboard-content');
+ document.querySelector('.dashboard-content').classList.add(section);
+
+ // Update hash to reflect current tab
+ // Note: Files tab updates its own hash with full path via onActivate, so skip it here
+ if ( section !== 'files' ) {
+ const newHash = section === 'home' ? '' : section;
+ history.replaceState(null, '', newHash ? `#${newHash}` : window.location.pathname);
+ }
+
// Close sidebar on mobile after selection
$el_window.find('.dashboard-sidebar').removeClass('open');
$el_window.find('.dashboard-sidebar-toggle').removeClass('open');
@@ -173,14 +410,24 @@ async function UIDashboard (options) {
$el_window.find('.dashboard-sidebar').toggleClass('open');
});
+ // Close sidebar when clicking outside
+ $el_window.on('mousedown touchstart', function (e) {
+ if ( !$(e.target).closest('.dashboard-sidebar').length
+ && !$(e.target).closest('.dashboard-sidebar-toggle').length
+ && $el_window.find('.dashboard-sidebar').hasClass('open') ) {
+ $el_window.find('.dashboard-sidebar').removeClass('open');
+ $el_window.find('.dashboard-sidebar-toggle').removeClass('open');
+ }
+ });
+
// User options button click handler
- $el_window.on('click', '.dashboard-user-btn', function (e) {
+ $el_window.on('click', '.dashboard-user-btn', function () {
const $btn = $(this);
const $chevron = $btn.find('.dashboard-user-chevron');
const pos = this.getBoundingClientRect();
-
+
// Don't open if already open
- if ($('.context-menu[data-id="dashboard-user-menu"]').length > 0) {
+ if ( $('.context-menu[data-id="dashboard-user-menu"]').length > 0 ) {
return;
}
@@ -190,10 +437,10 @@ async function UIDashboard (options) {
let items = [];
// Save Session (if temp user)
- if (window.user.is_temp) {
+ if ( window.user.is_temp ) {
items.push({
html: i18n('save_session'),
- icon: '
',
+ icon: '
',
onClick: async function () {
UIWindowSaveAccount({
send_confirmation_code: false,
@@ -212,7 +459,7 @@ async function UIDashboard (options) {
}
// Logged in users
- if (window.logged_in_users.length > 0) {
+ if ( window.logged_in_users.length > 0 ) {
let users_arr = window.logged_in_users;
// bring logged in user's item to top
@@ -226,7 +473,7 @@ async function UIDashboard (options) {
html: l_user.username,
icon: l_user.username === window.user.username ? '✓' : '',
onClick: async function () {
- if (l_user.username === window.user.username) {
+ if ( l_user.username === window.user.username ) {
return;
}
window.update_auth_data(l_user.auth_token, l_user);
@@ -288,7 +535,7 @@ async function UIDashboard (options) {
html: i18n('log_out'),
onClick: async function () {
// Check for open windows
- if ($('.window-app').length > 0) {
+ if ( $('.window-app').length > 0 ) {
const alert_resp = await UIAlert({
message: `
${i18n('confirm_open_apps_log_out')}
`,
buttons: [
@@ -302,7 +549,7 @@ async function UIDashboard (options) {
},
],
});
- if (alert_resp === 'close_and_log_out') {
+ if ( alert_resp === 'close_and_log_out' ) {
window.logout();
}
} else {
@@ -315,15 +562,15 @@ async function UIDashboard (options) {
UIContextMenu({
id: 'dashboard-user-menu',
parent_element: $btn[0],
- position: {
+ position: {
top: pos.top - 8,
- left: pos.left
+ left: pos.left,
},
items: menuItems,
onClose: () => {
// Rotate chevron back to point downwards
$chevron.removeClass('open');
- }
+ },
});
});
diff --git a/src/gui/src/UI/UIItem.js b/src/gui/src/UI/UIItem.js
index aa08e10c4..3184becce 100644
--- a/src/gui/src/UI/UIItem.js
+++ b/src/gui/src/UI/UIItem.js
@@ -107,7 +107,7 @@ const sendSelectionToAIApp = async ($elements) => {
}, '*');
};
-function UIItem (options) {
+async function UIItem (options) {
const matching_appendto_count = $(options.appendTo).length;
if ( matching_appendto_count > 1 ) {
$(options.appendTo).each(function () {
@@ -138,7 +138,16 @@ function UIItem (options) {
options.shortcut_to_path = options.shortcut_to_path ?? '';
options.immutable = (options.immutable === false || options.immutable === 0 || options.immutable === undefined ? 0 : 1);
options.sort_container_after_append = (options.sort_container_after_append !== undefined ? options.sort_container_after_append : false);
- const is_shared_with_me = (options.path && options.path !== `/${window.user.username}` && !options.path.startsWith(`/${window.user.username}/`));
+ const is_shared_with_me = (options.path !== `/${window.user.username}` && !options.path.startsWith(`/${window.user.username}/`));
+ let worker_url;
+ let is_worker;
+ if ( ! options.is_dir ) {
+ const stats = await puter.fs.stat({ path: options.path, returnWorkers: true });
+ is_worker = stats.workers !== undefined && stats.workers.length > 0;
+ if ( is_worker ) {
+ worker_url = stats.workers[0].address;
+ }
+ }
let website_url = window.determine_website_url(options.path);
@@ -165,6 +174,8 @@ function UIItem (options) {
data-website_url = "${website_url ? html_encode(website_url) : ''}"
data-immutable="${options.immutable}"
data-is_shortcut = "${options.is_shortcut}"
+ data-is_worker = "${is_worker !== undefined ? 1 : 0}"
+ data-worker_url = "${is_worker !== undefined ? worker_url : 0}"
data-shortcut_to = "${html_encode(options.shortcut_to)}"
data-shortcut_to_path = "${html_encode(options.shortcut_to_path)}"
data-sortable = "${options.sortable ?? 'true'}"
@@ -240,7 +251,12 @@ function UIItem (options) {
data-item-id="${item_id}"
title="${i18n('item_shortcut')}"
>`;
-
+ // worker badge
+ h += `
})
`;
h += '
';
// divider
@@ -1880,6 +1896,50 @@ $(document).on('click', '.website-badge-popover-link', function (e) {
$(e.target).closest('.popover').remove();
});
+$(document).on('long-hover', '.item-is-worker', function (e) {
+ const worker_url = e.target.parentNode.parentNode.getAttribute('data-worker_url');
+ var box = e.target.getBoundingClientRect();
+
+ var body = document.body;
+ var docEl = document.documentElement;
+
+ var scrollTop = window.pageYOffset || docEl.scrollTop || body.scrollTop;
+ var scrollLeft = window.pageXOffset || docEl.scrollLeft || body.scrollLeft;
+
+ var clientTop = docEl.clientTop || body.clientTop || 0;
+ var clientLeft = docEl.clientLeft || body.clientLeft || 0;
+
+ var top = box.top + scrollTop - clientTop;
+ var left = box.left + scrollLeft - clientLeft;
+
+ if ( worker_url ) {
+ let h = '
';
+ h += `
${i18n('worker')}
`;
+ h += `
+
${worker_url.replace('https://', '')}
+
`;
+ h += '
';
+
+ // close other worker popovers
+ $('.worker-badge-popover-content').closest('.popover').remove();
+
+ // show a UIPopover with the worker URL
+ UIPopover({
+ target: e.target,
+ content: h,
+ snapToElement: e.target,
+ parent_element: e.target,
+ top: top - 30,
+ left: left + 20,
+ });
+ }
+});
+
+$(document).on('click', '.worker-badge-popover-link', function (e) {
+ // remove the parent popover
+ $(e.target).closest('.popover').remove();
+});
+
// removes item(s)
$.fn.removeItems = async function (options) {
options = options || {};
diff --git a/src/gui/src/UI/UIWindowItemProperties.js b/src/gui/src/UI/UIWindowItemProperties.js
index fc92218f3..e468f8bdc 100644
--- a/src/gui/src/UI/UIWindowItemProperties.js
+++ b/src/gui/src/UI/UIWindowItemProperties.js
@@ -42,6 +42,7 @@ async function UIWindowItemProperties (item_name, item_path, item_uid, left, top
h += `
| ${i18n('modified')} | |
`;
h += `
| ${i18n('created')} | |
`;
h += `
| ${i18n('versions')} | |
`;
+ h += `
| ${i18n('worker')} | `;
h += ` |
| ${i18n('associated_websites')} | `;
h += ' |
';
h += `
| ${i18n('access_granted_to')} | |
`;
@@ -109,7 +110,7 @@ async function UIWindowItemProperties (item_name, item_path, item_uid, left, top
returnVersions: true,
returnSize: true,
consistency: 'eventual',
- success: function (fsentry) {
+ success: async function (fsentry) {
// hide versions tab if item is a directory
if ( fsentry.is_dir ) {
$(el_window).find('[data-tab="versions"]').hide();
@@ -134,7 +135,6 @@ async function UIWindowItemProperties (item_name, item_path, item_uid, left, top
// Ignored
}
}
-
// shortcut to
if ( fsentry.shortcut_to && fsentry.shortcut_to_path ) {
$(el_window).find('.item-prop-val-shortcut-to').text(fsentry.shortcut_to_path);
@@ -168,7 +168,14 @@ async function UIWindowItemProperties (item_name, item_path, item_uid, left, top
else {
$(el_window).find('.item-props-version-list').append('-');
}
-
+ // worker
+ if ( fsentry.path.endsWith('.js') ) {
+ const has_worker = fsentry.workers.length > 0;
+ if ( has_worker ) {
+ const worker_url = fsentry.workers[0].address;
+ $(el_window).find('.item-prop-val-worker').html(`
${html_encode(worker_url)}`);
+ }
+ }
$(el_window).find('.disassociate-website-link').on('click', function (e) {
puter.hosting.update($(e.target).attr('data-subdomain'),
null).then(() => {
@@ -178,7 +185,7 @@ async function UIWindowItemProperties (item_name, item_path, item_uid, left, top
// remove the website badge from all instances of the dir
$(`.item[data-uid="${item_uid}"]`).find('.item-has-website-badge').fadeOut(200);
}
- });
+ });
});
},
});
diff --git a/src/gui/src/UI/UIWindowPublishWebsite.js b/src/gui/src/UI/UIWindowPublishWebsite.js
index 6a3a6e0a8..28a7ac0be 100644
--- a/src/gui/src/UI/UIWindowPublishWebsite.js
+++ b/src/gui/src/UI/UIWindowPublishWebsite.js
@@ -234,7 +234,6 @@ async function UIWindowPublishWebsite (target_dir_uid, target_dir_name, target_d
});
window.update_sites_cache();
-
} else if ( publishingType === 'custom' ) {
// Handle custom domain publishing with Entri
let customDomain = $(el_window).find('.publish-website-custom-domain').val();
@@ -279,7 +278,6 @@ async function UIWindowPublishWebsite (target_dir_uid, target_dir_name, target_d
});
window.update_sites_cache();
-
$(el_window).close();
}
diff --git a/src/gui/src/css/dashboard.css b/src/gui/src/css/dashboard.css
new file mode 100644
index 000000000..3046db08f
--- /dev/null
+++ b/src/gui/src/css/dashboard.css
@@ -0,0 +1,2891 @@
+/* ======================================
+ Dashboard
+ ====================================== */
+
+.dashboard * {
+ box-sizing: border-box;
+}
+
+:root {
+ --select-hue: 213.05;
+ --select-saturation: 74.22%;
+ --select-lightness: 55.88%;
+ --select-color: hsl(var(--select-hue), var(--select-saturation), var(--select-lightness));
+
+ --dashboard-text: #444;
+ --dashboard-border: #e0e0e0;
+ --dashboard-background: #ffffff;
+ --dashboard-hover: #e8e8e8;
+ --dashboard-icon: #999;
+ --dashboard-sidebar-background: #f5f5f5;
+
+ --dashboard-text-primary: #1e293b;
+ --dashboard-text-secondary: #666;
+ --dashboard-text-tertiary: #888;
+ --dashboard-text-heading: #333;
+ --dashboard-text-card-title: #1a1a1a;
+ --dashboard-text-username: #414b62;
+ --dashboard-text-hint: #64748b;
+ --dashboard-text-muted: #94a3b8;
+
+ --dashboard-card-background: #ffffff;
+ --dashboard-card-gradient-start: #f8fafc;
+ --dashboard-card-gradient-end: #e2e8f0;
+ --dashboard-avatar-background: #ddd;
+ --dashboard-input-background: rgb(245, 247, 249);
+
+ --dashboard-link: #5271ff;
+ --dashboard-link-hover: #3d5bd9;
+
+ --dashboard-warning-icon: #ffbb00;
+ --dashboard-warning-background: #fef3c7;
+ --dashboard-warning-border: #f59e0b;
+ --dashboard-warning-text: #92400e;
+ --dashboard-warning-hover-bg: #fde68a;
+ --dashboard-warning-hover-border: #d97706;
+ --dashboard-warning-hover-text: #78350f;
+
+ --dashboard-success-background: #e6ffed;
+ --dashboard-success-border: #08bf4e;
+ --dashboard-success-text: #03933a;
+
+ --dashboard-danger-text: #dc2626;
+ --dashboard-danger-background: #fef2f2;
+ --dashboard-danger-border: #fecaca;
+ --dashboard-error-text: #dc2626;
+
+ --dashboard-icon-blue-start: #3b82f6;
+ --dashboard-icon-blue-end: #2563eb;
+ --dashboard-icon-blue-shadow: rgba(59, 130, 246, 0.3);
+ --dashboard-icon-green-start: #10b981;
+ --dashboard-icon-green-end: #059669;
+ --dashboard-icon-green-shadow: rgba(16, 185, 129, 0.3);
+
+ --dashboard-fancy-header-start: rgba(200, 220, 255, 0.5);
+ --dashboard-fancy-header-end: rgba(180, 210, 255, 0.3);
+
+ --dashboard-shadow-subtle: rgba(0, 0, 0, 0.04);
+ --dashboard-shadow-light: rgba(0, 0, 0, 0.06);
+ --dashboard-shadow-medium: rgba(0, 0, 0, 0.1);
+ --dashboard-shadow-overlay: rgba(0, 0, 0, 0.5);
+
+ --dashboard-gradient-indigo: rgba(99, 102, 241, 0.08);
+ --dashboard-gradient-purple: rgba(168, 85, 247, 0.06);
+ --dashboard-gradient-pink: rgba(236, 72, 153, 0.04);
+ --dashboard-gradient-green: rgba(34, 197, 94, 0.05);
+ --dashboard-gradient-blue: rgba(59, 130, 246, 0.05);
+
+ --dashboard-usage-bar-background: #e5e7eb;
+ --dashboard-usage-bar-start: #f59e0b;
+ --dashboard-usage-bar-end: #f97316;
+
+ --dashboard-legacy-bar-start: #dbe3ef;
+ --dashboard-legacy-bar-mid: #c2ccdc;
+}
+
+body {
+ min-height: 100vh;
+}
+
+@media (prefers-color-scheme: dark) {
+ :root {
+ --primary-color: var(--dashboard-border);
+ --primary-color-icon: invert(1);
+ --primary-color-sidebar-item: #e8e8e8;
+
+ --dashboard-text: #d4d4d4;
+ --dashboard-border: #3d3d3d;
+ --dashboard-background: #1e1e1e;
+ --dashboard-hover: #2a2a2a;
+ --dashboard-icon: #888;
+ --dashboard-sidebar-background: #252525;
+
+ --dashboard-text-primary: #e2e8f0;
+ --dashboard-text-secondary: #a1a1aa;
+ --dashboard-text-tertiary: #71717a;
+ --dashboard-text-heading: #f4f4f5;
+ --dashboard-text-card-title: #fafafa;
+ --dashboard-text-username: #c4cad6;
+ --dashboard-text-hint: #94a3b8;
+ --dashboard-text-muted: #64748b;
+
+ --dashboard-card-background: #262626;
+ --dashboard-card-gradient-start: #2a2a2a;
+ --dashboard-card-gradient-end: #1f1f1f;
+ --dashboard-avatar-background: #3f3f3f;
+ --dashboard-input-background: #2d2d2d;
+
+ --dashboard-link: #6b8cff;
+ --dashboard-link-hover: #8aa4ff;
+
+ --dashboard-warning-icon: #fbbf24;
+ --dashboard-warning-background: #422006;
+ --dashboard-warning-border: #b45309;
+ --dashboard-warning-text: #fcd34d;
+ --dashboard-warning-hover-bg: #4a2608;
+ --dashboard-warning-hover-border: #d97706;
+ --dashboard-warning-hover-text: #fde68a;
+
+ --dashboard-success-background: #052e16;
+ --dashboard-success-border: #16a34a;
+ --dashboard-success-text: #4ade80;
+
+ --dashboard-danger-text: #f87171;
+ --dashboard-danger-background: #2a1515;
+ --dashboard-danger-border: #7f1d1d;
+ --dashboard-error-text: #f87171;
+
+ --dashboard-icon-blue-start: #60a5fa;
+ --dashboard-icon-blue-end: #3b82f6;
+ --dashboard-icon-blue-shadow: rgba(96, 165, 250, 0.25);
+ --dashboard-icon-green-start: #34d399;
+ --dashboard-icon-green-end: #10b981;
+ --dashboard-icon-green-shadow: rgba(52, 211, 153, 0.25);
+
+ --dashboard-fancy-header-start: rgba(60, 80, 120, 0.4);
+ --dashboard-fancy-header-end: rgba(50, 70, 100, 0.3);
+
+ --dashboard-shadow-subtle: rgba(0, 0, 0, 0.2);
+ --dashboard-shadow-light: rgba(0, 0, 0, 0.3);
+ --dashboard-shadow-medium: rgba(0, 0, 0, 0.4);
+ --dashboard-shadow-overlay: rgba(0, 0, 0, 0.7);
+
+ --dashboard-gradient-indigo: rgba(99, 102, 241, 0.15);
+ --dashboard-gradient-purple: rgba(168, 85, 247, 0.12);
+ --dashboard-gradient-pink: rgba(236, 72, 153, 0.08);
+ --dashboard-gradient-green: rgba(34, 197, 94, 0.1);
+ --dashboard-gradient-blue: rgba(59, 130, 246, 0.1);
+
+ --dashboard-usage-bar-background: #3f3f46;
+ --dashboard-usage-bar-start: #fbbf24;
+ --dashboard-usage-bar-end: #fb923c;
+
+ --dashboard-legacy-bar-start: #3f3f46;
+ --dashboard-legacy-bar-mid: #52525b;
+ }
+}
+
+.dashboard {
+ display: flex;
+ height: 100%;
+ background: var(--dashboard-background);
+}
+
+.dashboard-sidebar {
+ width: 200px;
+ min-width: 200px;
+ background: var(--dashboard-sidebar-background);
+ border-right: 1px solid var(--dashboard-border);
+ padding: 16px 12px;
+ display: flex;
+ flex-direction: column;
+ box-sizing: border-box;
+}
+
+.dashboard-sidebar-nav {
+ flex: 1;
+}
+
+.dashboard-sidebar-item {
+ display: flex;
+ position: relative;
+ align-items: center;
+ gap: 10px;
+ padding: 10px 12px;
+ margin-bottom: 4px;
+ border-radius: 6px;
+ cursor: pointer;
+ font-size: 14px;
+ color: var(--dashboard-text);
+ transition: background-color 0.15s;
+}
+
+.dashboard-sidebar-item svg {
+ width: 18px;
+ height: 18px;
+ flex-shrink: 0;
+}
+
+.dashboard-sidebar-item:hover {
+ background: var(--dashboard-hover);
+}
+
+.dashboard-sidebar-item.active {
+ background: var(--dashboard-border);
+ font-weight: 500;
+}
+
+.dashboard-sidebar-item.beta:after {
+ content: "Beta";
+ font-size: 12px;
+ color: var(--dashboard-background);
+ background: var(--dashboard-text-muted);
+ padding: 1px 4px;
+ border-radius: 4px;
+ position: absolute;
+ right: 4px;
+}
+
+/* User options button at bottom of sidebar */
+.dashboard-user-options {
+ border-top: 1px solid var(--dashboard-border);
+ padding-top: 12px;
+ margin-top: 8px;
+}
+
+.dashboard-user-btn {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ padding: 10px 12px;
+ border-radius: 6px;
+ cursor: pointer;
+ transition: background-color 0.15s;
+}
+
+.dashboard-user-btn:hover {
+ background: var(--dashboard-hover);
+}
+
+.dashboard-user-btn.has-open-contextmenu {
+ background: var(--dashboard-hover);
+}
+
+.dashboard-user-avatar {
+ width: 28px;
+ height: 28px;
+ border-radius: 50%;
+ background-size: cover;
+ background-position: center;
+ background-color: var(--dashboard-avatar-background);
+ flex-shrink: 0;
+}
+
+.dashboard-user-name {
+ flex: 1;
+ font-size: 14px;
+ color: var(--dashboard-text-heading);
+ font-weight: 500;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.dashboard-user-chevron {
+ width: 16px;
+ height: 16px;
+ color: var(--dashboard-text-tertiary);
+ flex-shrink: 0;
+ transition: transform 0.1s ease;
+}
+
+.dashboard-user-chevron.open {
+ transform: rotate(180deg);
+}
+
+.dashboard-content {
+ flex: 1;
+ padding: 24px 32px;
+ overflow-y: auto;
+}
+
+.dashboard-section {
+ display: none;
+}
+
+.dashboard-section.active {
+ display: block;
+}
+
+.dashboard-section h2 {
+ margin: 0 0 16px 0;
+ font-size: 20px;
+ font-weight: 600;
+ color: var(--dashboard-text-heading);
+}
+
+.dashboard-section p {
+ font-size: 14px;
+}
+
+.dashboard-section-apps {
+ max-width: 600px;
+ margin: 0 auto;
+}
+
+.dashboard-section-usage {
+ max-width: 600px;
+ margin: 0 auto;
+}
+
+.dashboard #storage-used-percent {
+ color: var(--dashboard-text-tertiary);
+}
+
+/* Dashboard Apps */
+.dashboard-apps-container {
+ margin-top: 8px;
+}
+
+.dashboard-apps-heading {
+ font-size: 14px;
+ font-weight: 600;
+ color: var(--dashboard-text-secondary);
+ text-transform: uppercase;
+ letter-spacing: 0.5px;
+ margin: 0 0 16px 0;
+}
+
+.dashboard-apps-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(90px, 1fr));
+ gap: 20px;
+ margin-bottom: 8px;
+}
+
+.dashboard-app-card {
+ width: 100%;
+ transition: background-color 0.15s, transform 0.1s;
+ transition: transform 0.15s ease;
+}
+
+.dashboard-app-card .start-app {
+ cursor: pointer;
+ width: 90px;
+}
+.dashboard-app-card:hover {
+ background: none !important;
+}
+
+.dashboard-app-card:hover .start-app, .dashboard-app-card .start-app:hover {
+ background: none !important;
+}
+
+.dashboard-app-card:active {
+ transform: scale(0.97);
+}
+
+.dashboard-app-card .start-app {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ text-align: center;
+}
+
+.dashboard-app-icon {
+ width: 48px;
+ height: 48px;
+ margin-bottom: 8px;
+ filter: drop-shadow(0px 1px 2px var(--dashboard-shadow-medium));
+}
+
+.dashboard-app-card .start-app{
+ transition: transform 0.15s ease;
+}
+.dashboard-app-card .start-app:hover {
+ transform: scale(1.05);
+}
+
+.dashboard-app-title {
+ font-size: 13px;
+ color: var(--dashboard-text-heading);
+ text-align: center;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ max-width: 100%;
+}
+
+.dashboard-no-apps {
+ color: var(--dashboard-text-tertiary);
+ font-size: 14px;
+ padding: 24px 0;
+}
+
+/* Dashboard files */
+
+.dashboard-content.files {
+ padding: 0 0 0 10px;
+ overflow: hidden;
+}
+
+.dashboard-tab-content.files-tab {
+ display: flex;
+ justify-content: flex-start;
+ align-items: flex-start;
+ max-width: unset;
+ padding: 0;
+ margin: 0;
+}
+
+.dashboard-tab-content.files-tab h2 {
+ margin: 0 0 6px 0;
+ font-size: 26px;
+ font-weight: 700;
+ color: var(--dashboard-text-primary);
+ letter-spacing: -0.02em;
+}
+
+.dashboard-section-files .directories {
+ position: sticky;
+ top: 0;
+ width: 160px;
+ padding: 16px 0;
+}
+
+.dashboard-section-files .directories ul {
+ list-style: none;
+ padding: 0;
+ margin: 0;
+}
+
+.dashboard-section-files .directories li {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ margin-top: 3px;
+ margin-right: 10px;
+ padding: 3px 0px 3px 5px;
+ border-radius: 6px;
+ cursor: pointer;
+ font-size: 13px;
+ color: var(--dashboard-text);
+ border: 2px dashed transparent;
+ transition: background-color 0.15s;
+}
+
+.dashboard-section-files .directories li:hover {
+ background: var(--dashboard-hover);
+}
+
+.dashboard-section-files .directories li.context-menu-active {
+ background: var(--dashboard-hover);
+}
+
+.dashboard-section-files .directories li.active {
+ color: var(--select-color);
+ font-weight: 500;
+}
+
+.dashboard-section-files .directories li img {
+ width: 28px;
+ height: auto;
+}
+
+.dashboard-section-files .directories li[data-folder="Trash"] {
+ position: fixed;
+ bottom: 0;
+ margin-bottom: 20px;
+ width: 150px;
+ height: 38px;
+}
+
+.dashboard-section-files .directory-contents {
+ position: relative;
+ width: calc(100% - 160px);
+ min-height: 100vh;
+ border-left: 1px solid var(--dashboard-border);
+}
+
+.dashboard-section-files .directory-contents .header {
+ position: sticky;
+ top: 0;
+ height: 94px;
+ padding-top: 12px;
+ background: var(--dashboard-background);
+}
+
+.dashboard-section-files .header .path {
+ font-size: 14px;
+ height: 47px;
+ display: flex;
+ align-items: center;
+ justify-content: flex-start;
+ padding: 10px 12px;
+ background: linear-gradient(0deg, var(--dashboard-sidebar-background), var(--dashboard-background));
+ border-bottom: 1px solid var(--dashboard-border);
+}
+
+.dashboard .path-nav-buttons {
+ padding: 4px 10px 4px 0px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ gap: 10px;
+ margin-right: 5px;
+ border-right: 1px dotted var(--dashboard-border);
+}
+
+.dashboard .path-btn {
+ opacity: 0.4;
+ width: 28px;
+ cursor: pointer;
+ border-radius: 5px;
+ padding: 4px;
+ background-color: transparent;
+ transition: background-color 0.3s ease-in-out;
+}
+
+.dashboard .path-btn:hover {
+ filter: invert(1) hue-rotate(180deg);
+ background-color: var(--select-color);
+ opacity: 1;
+}
+
+@media (prefers-color-scheme: dark) {
+ .path-btn {
+ filter: invert(1) hue-rotate(180deg);
+ }
+ .path-btn:hover {
+ filter: invert(0) hue-rotate(0deg);
+ background-color: var(--select-color);
+ opacity: 1;
+ }
+}
+
+.dashboard-section-files .header .path-breadcrumbs {
+ display: flex;
+ align-items: center;
+ margin-left: 10px;
+}
+
+.dashboard-section-files .header .path-breadcrumbs:empty + .path-actions {
+ display: none;
+}
+
+.dashboard-section-files .header .path-actions {
+ display: flex;
+ gap: 10px;
+ margin-left: auto;
+}
+
+.dashboard-section-files .header .path-action-btn {
+ background-color: transparent;
+ border: none;
+ cursor: pointer;
+ padding: 4px;
+ border-radius: 4px;
+ color: var(--dashboard-icon);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.dashboard-section-files .header .path-action-btn:hover {
+ color: var(--dashboard-background);
+ background-color: var(--select-color);
+}
+
+.dashboard-section-files .header .path-btn-disabled {
+ opacity: 0.1;
+ pointer-events: none;
+}
+
+.dashboard-section-files .header .path-action-btn svg {
+ width: 24px;
+ height: 24px;
+}
+
+.dashboard-section-files .header .path .dirname {
+ height: auto;
+ font-weight: 400;
+ -webkit-font-smoothing: subpixel-antialiased;
+ color: var(--dashboard-text-secondary);
+ cursor: pointer;
+ background-color: transparent;
+ padding: 3px 6px;
+ font-size: 13px;
+ border: 1px solid transparent;
+ border-radius: 6px;
+}
+
+.dashboard-section-files .header .path .dirname:hover {
+ color: var(--dashboard-background);
+ background-color: var(--select-color);
+ border: 1px solid var(--select-color);
+}
+
+.dashboard-section-files .header .path .dirname.drop-target {
+ color: var(--dashboard-text);
+ background-color: rgba(59, 130, 246, 0.15);
+ border: 1px dashed var(--select-color);
+}
+
+.dashboard-section-files .header .path .dirname.context-menu-active {
+ color: var(--dashboard-background);
+ background-color: var(--select-color);
+ border: 1px solid var(--select-color);
+}
+
+.dashboard-section-files .header .columns {
+ display: grid;
+ height: 32px;
+ padding: 0 10px;
+ grid-template-columns: 24px auto 4px 100px 4px 120px 4px 20px;
+ align-items: center;
+ margin: 1px 2px;
+ color: var(--dashboard-text-secondary);
+ border-bottom: 1px solid var(--dashboard-border);
+ font-size: 12px;
+}
+
+.dashboard-section-files .files {
+ width: 100%;
+ height: calc(100vh - 124px);
+ display: flex;
+ flex-direction: column;
+ padding-bottom: 30px;
+ overflow-y: auto;
+}
+
+.dashboard-section-files .row {
+ display: grid;
+ width: unset;
+ height: 32px;
+ padding: 0 10px;
+ grid-template-columns: 24px auto 4px 100px 4px 120px 4px 20px;
+ align-items: center;
+ font-size: 13px;
+ color: var(--dashboard-text);
+ margin: 1px 2px;
+ pointer-events: auto;
+ float: unset;
+}
+
+.dashboard-section-files .row.folder {
+ border: 1px solid transparent;
+}
+
+.dashboard-section-files .row:hover {
+ background: var(--dashboard-hover);
+ border-radius: 3px;
+}
+
+.dashboard-section-files .row.selected {
+ color: var(--primary-color-sidebar-item);
+ background-color: var(--select-color);
+ border-radius: 3px;
+}
+
+@keyframes item-added-highlight {
+ from { background-color: var(--select-color); }
+ to { background-color: transparent; }
+}
+
+.dashboard-section-files .row.item-newly-added {
+ animation: item-added-highlight 2s ease-out;
+}
+
+.dashboard-section-files .row img {
+ width: 18px;
+ height: 18px;
+}
+
+.dashboard-section-files .row .item-icon,
+.dashboard-section-files .header .columns .item-icon {
+ padding: inherit;
+ height: 100%;
+ width: 100%;
+ filter: none;
+ margin: 0;
+}
+
+.dashboard-section-files .row .item-name-wrapper {
+ display: flex;
+ align-items: center;
+ overflow: hidden;
+ min-width: 0;
+}
+
+.dashboard-section-files .row .item-name,
+.dashboard-section-files .header .columns .item-name {
+ /* text-overflow: ellipsis; */
+ white-space: nowrap;
+ overflow: hidden;
+ max-width: unset;
+ color: currentColor;
+ text-shadow: none;
+ padding: 0 8px;
+ margin: 0;
+ font-size: inherit;
+ font-weight: 500;
+ word-break: inherit;
+ line-height: 32px;
+}
+
+.dashboard-section-files .row textarea {
+ align-items: center;
+ width: 100%;
+ height: 20px;
+ margin: 0;
+ padding: 3px 8px 0 8px;
+ text-align: left;
+ font-weight: inherit;
+ display: none;
+ white-space: nowrap;
+}
+
+.dashboard-section-files .row .item-size {
+ white-space: nowrap;
+ overflow: hidden;
+ line-height: 32px;
+ text-align: left;
+}
+
+.dashboard-section-files .row .item-modified {
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ overflow: hidden;
+ line-height: 32px;
+ text-align: left;
+}
+
+.dashboard-section-files .row .item-more {
+ color: var(--dashboard-border);
+ cursor: pointer;
+}
+
+.dashboard-section-files .row .item-more svg {
+ pointer-events: none;
+}
+
+.dashboard-section-files .row:hover .item-more {
+ color: var(--dashboard-text);
+}
+
+.dashboard-section-files.ui-draggable-dragging {
+ background-color: transparent !important;
+ opacity: 1 !important;
+}
+
+/* --- List view drag ghost --- */
+
+.dashboard-section-files.ui-draggable-dragging .files-list-view .row,
+.dashboard-section-files.item-selected-clone .files-list-view .row {
+ background-color: var(--select-color) !important;
+ color: var(--dashboard-background) !important;
+ border-radius: 3px;
+ cursor: move;
+ width: auto !important;
+ display: grid !important;
+ grid-template-columns: 24px auto !important;
+ align-items: center;
+ height: 32px;
+}
+
+.dashboard-section-files.item-selected-clone .files-list-view .row {
+ opacity: 0.6;
+ pointer-events: none;
+}
+
+.dashboard-section-files.ui-draggable-dragging .files-list-view .row .item-name-wrapper,
+.dashboard-section-files.item-selected-clone .files-list-view .row .item-name-wrapper {
+ display: flex;
+ align-items: center;
+ overflow: hidden;
+ min-width: 0;
+}
+
+.dashboard-section-files.ui-draggable-dragging .files-list-view .row .item-icon,
+.dashboard-section-files.item-selected-clone .files-list-view .row .item-icon {
+ width: 24px;
+ height: 24px;
+ padding: 0;
+ background: white;
+ border-radius: 2px;
+}
+
+.dashboard-section-files.ui-draggable-dragging .files-list-view .row .item-icon img,
+.dashboard-section-files.item-selected-clone .files-list-view .row .item-icon img {
+ width: 18px;
+ height: 18px;
+ object-fit: cover;
+}
+
+.dashboard-section-files.ui-draggable-dragging .files-list-view .row .item-name,
+.dashboard-section-files.item-selected-clone .files-list-view .row .item-name {
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ overflow: hidden;
+ max-width: unset;
+ color: currentColor;
+ text-shadow: none;
+ padding: 0 8px;
+ margin: 0;
+ font-size: 12px;
+ font-weight: 500;
+ word-break: inherit;
+ line-height: 32px;
+}
+
+.dashboard-section-files.ui-draggable-dragging .files-list-view .row .item-metadata,
+.dashboard-section-files.ui-draggable-dragging .files-list-view .row .col-spacer,
+.dashboard-section-files.ui-draggable-dragging .files-list-view .row .item-more,
+.dashboard-section-files.ui-draggable-dragging .files-list-view .row .item-name-editor,
+.dashboard-section-files.item-selected-clone .files-list-view .row .item-metadata,
+.dashboard-section-files.item-selected-clone .files-list-view .row .col-spacer,
+.dashboard-section-files.item-selected-clone .files-list-view .row .item-more,
+.dashboard-section-files.item-selected-clone .files-list-view .row .item-name-editor {
+ display: none !important;
+}
+
+/* --- Grid view drag ghost --- */
+
+.dashboard-section-files.ui-draggable-dragging .files-grid-view .row,
+.dashboard-section-files.item-selected-clone .files-grid-view .row {
+ background-color: var(--select-color) !important;
+ color: var(--dashboard-background) !important;
+ cursor: move;
+}
+
+.dashboard-section-files.item-selected-clone .files-grid-view .row {
+ opacity: 0.6;
+ pointer-events: none;
+}
+
+.dashboard-section-files.ui-draggable-dragging .files-grid-view .row .item-name-wrapper,
+.dashboard-section-files.item-selected-clone .files-grid-view .row .item-name-wrapper {
+ /* display: none !important; */
+}
+
+.dashboard-section-files.ui-draggable-dragging .files-grid-view .row .item-metadata,
+.dashboard-section-files.ui-draggable-dragging .files-grid-view .row .col-spacer,
+.dashboard-section-files.ui-draggable-dragging .files-grid-view .row .item-more,
+.dashboard-section-files.ui-draggable-dragging .files-grid-view .row .item-name-editor,
+.dashboard-section-files.item-selected-clone .files-grid-view .row .item-metadata,
+.dashboard-section-files.item-selected-clone .files-grid-view .row .col-spacer,
+.dashboard-section-files.item-selected-clone .files-grid-view .row .item-more,
+.dashboard-section-files.item-selected-clone .files-grid-view .row .item-name-editor {
+ display: none !important;
+}
+
+.dashboard-section-files .row.folder.ui-droppable-hover,
+.dashboard-section-files .row.folder.selected.ui-droppable-over {
+ color: var(--dashboard-text);
+ background-color: rgba(59, 130, 246, 0.1);
+ border: 2px dashed var(--select-color);
+ border-radius: 3px;
+}
+
+/* Spring-loaded folder dwell animation */
+.dashboard-section-files .row.folder.dwell-opening,
+.dashboard-section-files .directories li.dwell-opening {
+ background: linear-gradient(90deg, rgba(59, 130, 246, 0.15) 100%, transparent 100%);
+ background-size: 0% 100%;
+ background-repeat: no-repeat;
+ border: 2px dashed var(--select-color);
+ border-radius: 3px;
+ animation: dwell-fill 700ms linear forwards;
+}
+
+@keyframes dwell-fill {
+ from { background-size: 0% 100%; }
+ to { background-size: 100% 100%; }
+}
+
+.dashboard-section-files .draggable-count-badge {
+ position: fixed;
+ background: var(--select-color);
+ color: var(--dashboard-background);
+ border-radius: 50%;
+ width: 24px;
+ height: 24px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 12px;
+ font-weight: bold;
+ pointer-events: none;
+ z-index: 10001;
+}
+
+/* Drag cancel zone — shown after spring-loaded folder navigation */
+.drag-cancel-zone {
+ position: absolute;
+ bottom: 32px;
+ right: 32px;
+ background: #ef4444;
+ color: white;
+ padding: 16px 32px;
+ border-radius: 6px;
+ font-size: 13px;
+ font-weight: 600;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
+ z-index: 10001;
+ user-select: none;
+ cursor: default;
+ transition: background 0.15s, transform 0.15s;
+}
+
+.drag-cancel-zone.drag-cancel-hover {
+ background: #dc2626;
+ transform: scale(1.05);
+}
+
+.dashboard-section-files .directories li.ui-droppable-hover.active {
+ background-color: rgba(59, 130, 246, 0.1);
+ border: 2px dashed var(--select-color);
+ border-radius: 4px;
+}
+
+/* Native file drop visual feedback for Dashboard */
+.dashboard-section-files .files.native-drop-active {
+ background-color: rgba(0, 120, 212, 0.08);
+ outline: 2px dashed #0078d4;
+ outline-offset: -2px;
+ border-radius: 4px;
+}
+
+.dashboard-section-files .directories li.native-drop-target {
+ background-color: rgba(0, 120, 212, 0.15);
+ border-radius: 4px;
+}
+
+.dashboard-section-files .files .row.folder.native-drop-target {
+ background-color: rgba(0, 120, 212, 0.15);
+}
+
+/* Dark mode support for native file drop */
+.window[data-color-scheme="dark"] .dashboard-section-files .files.native-drop-active {
+ background-color: rgba(100, 180, 255, 0.12);
+ outline-color: #4da3ff;
+}
+
+.window[data-color-scheme="dark"] .dashboard-section-files .directories li.native-drop-target,
+.window[data-color-scheme="dark"] .dashboard-section-files .files .row.folder.native-drop-target {
+ background-color: rgba(100, 180, 255, 0.2);
+}
+
+.dashboard-section-files .files-footer {
+ position: fixed;
+ bottom: 0;
+ right: 0;
+ left: 371px;
+ background: linear-gradient(180deg, var(--dashboard-sidebar-background), var(--dashboard-background));
+ border-top: 1px solid var(--dashboard-border);
+ height: 30px;
+ font-size: 13px;
+ line-height: 28px;
+ padding: 0 12px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 8px;
+ color: #666;
+ z-index: 10;
+}
+
+.dashboard-section-files .files-footer-separator,
+.dashboard-section-files .files-footer-selected-items {
+ display: none;
+}
+
+.dashboard-section-files .files-footer-separator {
+ color: #CCC;
+}
+
+/* Floating Selection Actions Bar */
+.dashboard-section-files .files-selection-actions {
+ position: absolute;
+ bottom: 40px;
+ left: 50%;
+ transform: translateX(-50%) translateY(100px);
+ background: var(--dashboard-card-background);
+ border: 1px solid var(--dashboard-border);
+ border-radius: 12px;
+ padding: 8px 12px;
+ display: flex;
+ align-items: center;
+ gap: 4px;
+ box-shadow: 0 4px 20px var(--dashboard-shadow-medium),
+ 0 2px 8px var(--dashboard-shadow-light);
+ z-index: 15;
+ opacity: 0;
+ visibility: hidden;
+ transition: transform 0.25s cubic-bezier(0.4, 0, 0.2, 1),
+ opacity 0.25s ease,
+ visibility 0.25s ease;
+}
+
+.dashboard-section-files .files-selection-actions.visible {
+ transform: translateX(-50%) translateY(0);
+ opacity: 1;
+ visibility: visible;
+ z-index: 99999999999999999;
+}
+
+.dashboard-section-files .files-selection-actions.rubberband-active {
+ pointer-events: none;
+}
+
+.dashboard-section-files .selection-action-btn {
+ display: flex;
+ align-items: center;
+ gap: 6px;
+ padding: 8px 14px;
+ border: none;
+ border-radius: 8px;
+ background: transparent;
+ color: var(--dashboard-text);
+ font-size: 13px;
+ font-weight: 500;
+ cursor: pointer;
+ transition: background-color 0.15s ease, color 0.15s ease;
+}
+
+.dashboard-section-files .selection-action-btn:hover {
+ background: var(--dashboard-hover);
+}
+
+.dashboard-section-files .selection-action-btn:active {
+ transform: scale(0.97);
+}
+
+.dashboard-section-files .selection-action-btn svg {
+ width: 24px;
+ height: 24px;
+ flex-shrink: 0;
+}
+
+.dashboard-section-files .selection-action-btn.restore-btn {
+ color: #43a047;
+}
+
+.dashboard-section-files .selection-action-btn.restore-btn:hover {
+ background: rgba(67, 160, 71, 0.1);
+}
+
+.dashboard-section-files .selection-action-btn.delete-btn {
+ color: #e53935;
+}
+
+.dashboard-section-files .selection-action-btn.delete-btn:hover {
+ background: rgba(229, 57, 53, 0.1);
+}
+
+/* Select mode button - hidden on desktop by default */
+.dashboard-section-files .header .path-action-btn.select-mode-btn {
+ display: none;
+}
+
+/* Done button in floating action bar - hidden by default, shown in select mode on mobile */
+.dashboard-section-files .files-selection-actions .done-btn {
+ display: none;
+}
+
+/* Checkbox in item rows - hidden by default */
+.dashboard-section-files .files-tab .files .row .item-checkbox {
+ display: none;
+ width: 24px;
+ height: 24px;
+ align-items: center;
+ justify-content: center;
+ flex-shrink: 0;
+}
+
+.dashboard-section-files .files-tab .files .row .item-checkbox .checkbox-icon {
+ width: 20px;
+ height: 20px;
+ border: 2px solid var(--dashboard-border);
+ border-radius: 4px;
+ background: var(--dashboard-card-background);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ transition: all 0.15s ease;
+}
+
+.dashboard-section-files .files-tab .files .row.selected .item-checkbox .checkbox-icon {
+ background: var(--primary-color, #3b82f6);
+ border-color: var(--primary-color, #3b82f6);
+}
+
+.dashboard-section-files .files-tab .files .row.selected .item-checkbox .checkbox-icon::after {
+ content: '';
+ width: 6px;
+ height: 10px;
+ border: solid white;
+ border-width: 0 2px 2px 0;
+ transform: rotate(45deg);
+ margin-bottom: 2px;
+}
+
+.dashboard-section-files .files-tab .files.files-list-view .row {
+ display: grid;
+ grid-template-columns: 24px auto 4px 100px 4px 120px 4px 20px;
+ height: 32px;
+ padding: 0 10px;
+ align-items: center;
+}
+
+.dashboard-section-files .files-tab .files.files-list-view .row .item-icon {
+ position: relative;
+ width: 24px;
+ height: 24px;
+ padding: 0;
+ background: var(--dashboard-background);
+ border-radius: 2px;
+}
+
+.dashboard-section-files .files-tab .files.files-list-view .row .item-icon img {
+ width: 18px;
+ height: 18px;
+ object-fit: cover;
+}
+
+.dashboard-section-files .files-tab .files.files-list-view .row .item-size,
+.dashboard-section-files .files-tab .files.files-list-view .row .item-modified,
+.dashboard-section-files .files-tab .files.files-list-view .row .item-more {
+ display: block;
+ padding: 0 10px;
+}
+
+.dashboard-section-files .files-tab .files.files-grid-view {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(170px, 1fr));
+ gap: 10px;
+ padding: 16px;
+ align-content: start;
+ margin-bottom: 30px;
+}
+
+.dashboard-section-files .files-tab .files.files-list-view .row .item-badges {
+ width: 36px;
+ height: 36px;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ justify-content: flex-end;
+ align-items: flex-start;
+}
+
+.dashboard-section-files .files-tab .files.files-list-view .row img.item-badge {
+ width: 12px !important;
+ height: 12px !important;
+ margin: 0 -2px;
+}
+
+.dashboard-section-files .files-tab .files.files-grid-view .row .item-badges {
+ width: 100%;
+ height: 100%;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ justify-content: flex-end;
+ align-items: flex-start;
+}
+
+.dashboard-section-files .files-tab .files.files-grid-view .row img.item-badge {
+ width: 20px !important;
+ height: 20px !important;
+ margin: 5px;
+ border-radius: 50%;
+}
+
+.dashboard-section-files .files-tab .files.files-grid-view .row {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ position: relative;
+ padding: 12px;
+ height: auto;
+ gap: 8px;
+ border: 1px solid var(--dashboard-border);
+ border-radius: 8px;
+ cursor: pointer;
+ transition: all 0.15s ease;
+ box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.1);
+}
+
+.dashboard-section-files .files-tab .files.files-grid-view .row:hover {
+ background: var(--dashboard-sidebar-background);
+ border-color: var(--dashboard-border);
+}
+
+.dashboard-section-files .files-tab .files.files-grid-view .row.selected {
+ background-color: var(--select-color);
+ color: var(--dashboard-background);
+ border-color: var(--select-color);
+}
+
+.dashboard-section-files .files-tab .files.files-grid-view .row .item-icon {
+ width: 150px;
+ height: 150px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ /* background: #fafafa; */
+ border-radius: 8px;
+ overflow: hidden;
+ background: white;
+ border-radius: 2px;
+}
+
+.dashboard-section-files .files-tab .files.files-grid-view .row .item-icon img {
+ width: 100%;
+ height: 100%;
+ object-fit: contain;
+ max-width: fit-content;
+ max-height: fit-content;
+}
+
+.dashboard-section-files .files-tab .files.files-grid-view .row .item-icon svg {
+ width: 64px;
+ height: 64px;
+ opacity: 0.5;
+}
+
+.dashboard-section-files .files-tab .files.files-grid-view .row .item-name {
+ text-align: center;
+ width: 100%;
+ padding: 0;
+ font-size: 13px;
+ line-height: 1.4;
+ max-height: 2.8em;
+ overflow: hidden;
+ word-break: break-word;
+}
+
+.dashboard-section-files .files-tab .files.files-grid-view .row .item-size,
+.dashboard-section-files .files-tab .files.files-grid-view .row .item-modified,
+.dashboard-section-files .files-tab .files.files-grid-view .row .item-more,
+.dashboard-section-files .files-tab .files.files-grid-view .row .col-spacer {
+ display: none;
+}
+
+.dashboard-section-files .files-tab .files.files-grid-view .row .item-name-wrapper {
+ width: 100%;
+ display: block;
+}
+
+.dashboard-section-files .files-tab .files.files-grid-view .row:hover .item-more {
+ position: absolute;
+ top: 1px;
+ right: 1px;
+ color: #666;
+ background: var(--dashboard-sidebar-background);
+ width: 24px;
+ height: 24px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ border-radius: 7px;
+}
+
+/* Hide .item-more on desktop (non-touch devices) - use right-click context menu instead */
+.dashboard-section-files .files-tab:not(.touch-device) .files.files-list-view .row .item-more,
+.dashboard-section-files .files-tab:not(.touch-device) .files.files-grid-view .row .item-more,
+.dashboard-section-files .files-tab:not(.touch-device) .files.files-grid-view .row:hover .item-more,
+.dashboard-section-files .files-tab:not(.touch-device) .header .columns .item-more {
+ display: none !important;
+}
+
+.dashboard-section-files .files-tab .files.files-grid-view .row .item-name-editor {
+ text-align: center;
+}
+
+.dashboard-section-files .files-tab.files-grid-mode .header .columns {
+ display: none;
+}
+
+/* Sortable column headers */
+.dashboard-section-files .header .columns .sortable {
+ cursor: pointer;
+ user-select: none;
+ position: relative;
+ display: flex;
+ align-items: center;
+ gap: 4px;
+ padding: 0 10px;
+ justify-content: space-between;
+}
+
+.dashboard-section-files .header .columns .sortable:hover {
+ color: var(--dashboard-text-heading);
+}
+
+.dashboard-section-files .header .columns .sortable::after {
+ content: '';
+ display: inline-block;
+ width: 0;
+ height: 0;
+ margin-left: 4px;
+ opacity: 0.3;
+ border-left: 4px solid transparent;
+ border-right: 4px solid transparent;
+ border-bottom: 5px solid currentColor;
+}
+
+.dashboard-section-files .header .columns .sortable.sort-asc::after {
+ opacity: 1;
+ border-bottom: 5px solid currentColor;
+ border-top: none;
+}
+
+.dashboard-section-files .header .columns .sortable.sort-desc::after {
+ opacity: 1;
+ border-top: 5px solid currentColor;
+ border-bottom: none;
+}
+
+/* Column resize handles */
+.dashboard-section-files .header .columns .col-resize-handle {
+ width: 4px;
+ height: 100%;
+ cursor: col-resize;
+ background: transparent;
+ position: relative;
+ background: var(--dashboard-sidebar-background);
+}
+
+.dashboard-section-files .header .columns .col-resize-handle:hover {
+ background: var(--dashboard-border);
+}
+
+.dashboard-section-files .header .columns .col-resize-handle:active {
+ background: var(--select-color);
+}
+
+.dashboard-section-files .more-btn {
+ background: none;
+ border: none;
+ padding: 6px;
+ cursor: pointer;
+ color: var(--text-muted);
+ border-radius: 50%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ opacity: 0;
+ transition: all 0.15s ease;
+ position: relative;
+}
+
+.dashboard-section-files .more-menu {
+ position: absolute;
+ min-width: 180px;
+ background: var(--dashboard-background);
+ border: 1px solid var(--dashboard-border);
+ border-radius: 8px;
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
+ z-index: 1000;
+ padding: 4px;
+}
+
+.dashboard-section-files .more-menu .menu-item {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ width: 100%;
+ padding: 10px 12px;
+ background: none;
+ border: none;
+ border-radius: 6px;
+ font-size: 0.85rem;
+ color: var(--dashboard-text);
+ cursor: pointer;
+ transition: background 0.15s ease;
+ text-align: left;
+}
+
+.dashboard-section-files .more-menu .menu-item:hover {
+ background: var(--dashboard-hover);
+}
+
+.dashboard-section-files .more-menu .menu-item svg {
+ width: 16px;
+ height: 16px;
+ color: var(--dashboard-border);
+ flex-shrink: 0;
+}
+
+.dashboard-section-files .more-menu .menu-item.has-submenu {
+ position: relative;
+}
+
+.dashboard-section-files .more-menu .menu-item.has-submenu svg:last-child {
+ margin-left: auto;
+ width: 0.85rem;
+ height: 0.85rem;
+}
+
+.dashboard-section-files .more-menu .menu-item.danger {
+ color: #ea4335;
+}
+
+.dashboard-section-files .more-menu .menu-item.danger svg {
+ color: #ea4335;
+}
+
+.dashboard-section-files .more-menu .menu-item.danger:hover {
+ background: rgba(234, 67, 53, 0.1);
+}
+
+.dashboard-section-files .more-menu .menu-divider {
+ height: 1px;
+ background: var(--dashboard-border);
+ margin: 4px 0;
+}
+
+/* Mobile sidebar toggle */
+.dashboard-sidebar-toggle {
+ display: none;
+ position: fixed;
+ top: 12px;
+ left: 12px;
+ z-index: 100;
+ width: 40px;
+ height: 40px;
+ background: var(--dashboard-background);
+ border: 1px solid var(--dashboard-border);
+ border-radius: 6px;
+ cursor: pointer;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ gap: 4px;
+}
+
+.dashboard-sidebar-toggle span {
+ display: block;
+ width: 18px;
+ height: 2px;
+ background: var(--dashboard-text);
+ border-radius: 1px;
+ transition: transform 0.2s, opacity 0.2s;
+}
+
+.dashboard-sidebar-toggle.open span:nth-child(1) {
+ transform: rotate(45deg) translate(4px, 4px);
+}
+
+.dashboard-sidebar-toggle.open span:nth-child(2) {
+ opacity: 0;
+}
+
+.dashboard-sidebar-toggle.open span:nth-child(3) {
+ transform: rotate(-45deg) translate(4px, -4px);
+}
+
+.dashboard-sidebar-separator {
+ height: 1px;
+ background: var(--dashboard-border);
+ margin: 8px 0;
+}
+
+/* Responsive: tablet and below */
+@media (max-width: 768px) {
+ .dashboard-sidebar-nav {
+ padding-top: 45px;
+ }
+ .dashboard-sidebar-toggle {
+ display: flex;
+ }
+
+ .dashboard-sidebar {
+ position: fixed;
+ left: 0;
+ top: 0;
+ height: 100%;
+ z-index: 99;
+ transform: translateX(-110%);
+ transition: transform 0.2s ease;
+ box-shadow: 2px 0 8px rgba(0, 0, 0, 0.1);
+ }
+
+ .dashboard-sidebar.open {
+ transform: translateX(0);
+ }
+
+ .dashboard-content {
+ padding: 64px 16px 24px;
+ }
+
+ .dashboard-apps-grid {
+ grid-template-columns: repeat(auto-fill, minmax(80px, 1fr));
+ gap: 4px;
+ }
+
+ .dashboard-app-card {
+ padding: 10px 6px;
+ }
+
+ .dashboard-app-icon {
+ width: 40px;
+ height: 40px;
+ }
+}
+
+/* Desktop: Make metadata wrapper transparent */
+.dashboard-section-files .files-tab .files.files-list-view .row .item-metadata {
+ display: contents;
+}
+
+/* Image preview popover */
+.image-preview-popover {
+ position: fixed;
+ z-index: 9999;
+ background: var(--dashboard-background);
+ border: 1px solid var(--dashboard-border);
+ border-radius: 8px;
+ padding: 16px;
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 12px;
+}
+
+.image-preview-popover img {
+ max-width: 100%;
+ max-height: 70vh;
+ object-fit: contain;
+ border-radius: 4px;
+}
+
+.image-preview-name {
+ font-size: 14px;
+ color: var(--dashboard-text);
+ text-align: center;
+ max-width: 100%;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+/* Mobile phone optimizations */
+@media (max-width: 480px) {
+ .dashboard-content.files {
+ padding: 0;
+ }
+
+ /* Hide directories sidebar */
+ .dashboard-section-files .directories {
+ display: none;
+ }
+
+ /* Full width for directory contents */
+ .dashboard-section-files .directory-contents {
+ width: 100%;
+ border-left: none;
+ }
+
+ .dashboard-section-files .directory-contents .header {
+ margin-bottom: 12px;
+ }
+
+ /* Hide column headers */
+ .dashboard-section-files .header .columns {
+ display: none;
+ }
+
+ /* Two-row header layout */
+ .dashboard-section-files .header .path {
+ flex-wrap: wrap;
+ height: auto;
+ padding: 8px 12px;
+ }
+
+ .dashboard-section-files .header .path-breadcrumbs {
+ order: 1;
+ width: 100%;
+ margin: 0 0 8px 0;
+ padding-bottom: 8px;
+ margin-left: 50px;
+ flex-wrap: nowrap;
+ white-space: nowrap;
+ overflow-x: auto;
+ border-bottom: 1px solid var(--dashboard-border);
+ }
+
+ .dashboard-section-files .header .path-nav-buttons {
+ order: 2;
+ border-right: none;
+ margin-right: 0;
+ }
+
+ .dashboard-section-files .header .path-actions {
+ order: 3;
+ margin-left: auto;
+ }
+
+ /* Two-row item layout */
+ .dashboard-section-files .files-tab .files.files-list-view .row {
+ display: grid;
+ grid-template-columns: 48px 1fr !important;
+ grid-template-rows: auto auto;
+ height: auto;
+ padding: 6px 10px;
+ gap: 2px 8px;
+ }
+
+ /* Thumbnail spans both rows */
+ .dashboard-section-files .files-tab .files.files-list-view .row .item-icon {
+ grid-row: 1 / 3;
+ width: 48px;
+ height: 48px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ }
+
+ .dashboard-section-files .files-tab .files.files-list-view .row .item-icon img {
+ width: 40px;
+ height: 40px;
+ }
+
+ /* File name on first row */
+ .dashboard-section-files .files-tab .files.files-list-view .row .item-name-wrapper {
+ grid-column: 2;
+ grid-row: 1;
+ padding-right: 40px;
+ }
+
+ .dashboard-section-files .files-tab .files.files-list-view .row .item-name {
+ padding: 0;
+ line-height: 24px;
+ }
+
+ /* Metadata wrapper for second row */
+ .dashboard-section-files .files-tab .files.files-list-view .row .item-metadata {
+ grid-column: 2;
+ grid-row: 2;
+ display: flex;
+ align-items: center;
+ font-size: 11px;
+ }
+
+ .dashboard-section-files .files-tab .files.files-list-view .row .item-metadata .col-spacer {
+ display: none;
+ }
+
+ .dashboard-section-files .files-tab .files.files-list-view .row .item-modified,
+ .dashboard-section-files .files-tab .files.files-list-view .row .item-size {
+ font-size: 11px;
+ padding: 0;
+ line-height: 24px;
+ color: var(--dashboard-text-muted);
+ }
+
+ .dashboard-section-files .files-tab .files.files-list-view .row:hover .item-modified,
+ .dashboard-section-files .files-tab .files.files-list-view .row:hover .item-size,
+ .dashboard-section-files .files-tab .files.files-list-view .row.selected .item-modified,
+ .dashboard-section-files .files-tab .files.files-list-view .row.selected .item-size {
+ /* color: var(--primary-color-sidebar-item); */
+ }
+
+ /* Bullet separator between size and modified */
+ .dashboard-section-files .files-tab .files.files-list-view .row .item-size:not(:empty)::after {
+ content: '•';
+ margin: 0 6px;
+ }
+
+ /* Hide outer spacers */
+ .dashboard-section-files .files-tab .files.files-list-view .row > .col-spacer {
+ display: none;
+ }
+
+ /* Hide more button (use long-press for context menu on touch) */
+ .dashboard-section-files .files-tab .files.files-list-view .row .item-more {
+ position: absolute;
+ right: 10px;
+ }
+
+ /* Adjust footer position - full width since sidebar is hidden */
+ .dashboard-section-files .files-footer {
+ left: 0;
+ padding: 0;
+ }
+
+ /* Mobile: Floating selection actions - icon-only, full width */
+ .dashboard-section-files .files-selection-actions {
+ left: 0;
+ right: 0;
+ bottom: 38px;
+ transform: translateX(0) translateY(100px);
+ border-radius: 0;
+ justify-content: center;
+ padding: 10px 8px;
+ }
+
+ .dashboard-section-files .files-selection-actions.visible {
+ transform: translateX(0) translateY(0);
+ }
+
+ .dashboard-section-files .selection-action-btn span {
+ display: none;
+ }
+
+ .dashboard-section-files .selection-action-btn {
+ padding: 9px 8px;
+ border-radius: 50%;
+ }
+
+ /* Mobile: Show select mode button */
+ .dashboard-section-files .header .path-action-btn.select-mode-btn {
+ display: flex;
+ }
+
+ .dashboard-section-files .header .path-action-btn.select-mode-btn.active {
+ background: var(--primary-color, #3b82f6);
+ color: white;
+ border-radius: 6px;
+ }
+
+ /* Mobile: Show checkboxes in select mode */
+ .dashboard-section-files .files-tab.select-mode-active .files .row .item-checkbox {
+ display: flex;
+ grid-row: 1 / 3;
+ }
+
+ /* Mobile: Adjust grid for checkbox in list view */
+ .dashboard-section-files .files-tab.select-mode-active .files.files-list-view .row {
+ grid-template-columns: 32px 48px 1fr !important;
+ }
+
+ /* Adjust grid-column for content when checkbox is visible */
+ .dashboard-section-files .files-tab.select-mode-active .files.files-list-view .row .item-icon {
+ grid-column: 2;
+ }
+
+ .dashboard-section-files .files-tab.select-mode-active .files.files-list-view .row .item-name-wrapper {
+ grid-column: 3;
+ }
+
+ .dashboard-section-files .files-tab.select-mode-active .files.files-list-view .row .item-metadata {
+ grid-column: 3;
+ }
+
+ /* Mobile: Show Done button in floating action bar during select mode */
+ .dashboard-section-files .files-tab.select-mode-active .files-selection-actions .done-btn {
+ display: flex;
+ background: var(--primary-color, #3b82f6);
+ color: white;
+ padding: 5px;
+ margin-left: 8px;
+ }
+
+ /* Mobile: Grid view checkbox positioning */
+ .dashboard-section-files .files-tab.select-mode-active .files.files-grid-view .row {
+ position: relative;
+ }
+
+ .dashboard-section-files .files-tab.select-mode-active .files.files-grid-view .row .item-checkbox {
+ position: absolute;
+ top: 8px;
+ left: 8px;
+ z-index: 2;
+ }
+}
+
+/* Full HD */
+@media (min-width: 1920px) {
+ .dashboard-section-home .bento-container {
+ max-width: 1000px;
+ }
+ .dashboard-section-usage{
+ max-width: 800px;
+ }
+}
+/* 4K UHD */
+@media (min-width: 2560px) {
+ .dashboard-section-home .bento-container {
+ max-width: 1200px;
+ }
+ .dashboard-section-usage{
+ max-width: 900px;
+ }
+}
+
+/* ============================================== */
+/* Bento Box Home Dashboard */
+/* ============================================== */
+
+.dashboard .bento-container {
+ display: grid;
+ grid-template-columns: 280px 1fr;
+ gap: 20px;
+ max-width: 800px;
+ margin: 0 auto;
+ padding: 8px 0;
+ align-items: stretch;
+}
+
+.dashboard .bento-card {
+ background: var(--dashboard-background);
+ border-radius: 20px;
+ overflow: hidden;
+ box-shadow:
+ 0 1px 3px var(--dashboard-shadow-subtle),
+ 0 4px 12px var(--dashboard-shadow-subtle);
+ border: 1px solid var(--dashboard-shadow-light);
+}
+
+/* Welcome Card */
+.dashboard .bento-welcome {
+ position: relative;
+ background: linear-gradient(135deg, var(--dashboard-card-gradient-start) 0%, var(--dashboard-card-gradient-end) 100%);
+ color: var(--dashboard-text-primary);
+ min-height: 280px;
+}
+
+.dashboard .bento-welcome-inner {
+ position: relative;
+ width: 100%;
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+ justify-content: flex-end;
+ padding: 24px;
+ box-sizing: border-box;
+}
+
+.dashboard .bento-welcome-pattern {
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background-image:
+ radial-gradient(circle at 20% 30%, var(--dashboard-gradient-indigo) 0%, transparent 50%),
+ radial-gradient(circle at 80% 70%, var(--dashboard-gradient-purple) 0%, transparent 40%);
+ pointer-events: none;
+}
+
+.dashboard .bento-welcome-content {
+ position: relative;
+ z-index: 1;
+}
+
+.dashboard .bento-welcome-avatar {
+ width: 72px;
+ height: 72px;
+ border-radius: 50%;
+ background-size: cover;
+ background-position: center;
+ background-color: var(--dashboard-avatar-background);
+ border: 3px solid var(--dashboard-background);
+ margin-bottom: 16px;
+ box-shadow: 0 4px 16px var(--dashboard-shadow-medium);
+}
+
+.dashboard .bento-greeting {
+ font-size: 14px;
+ color: var(--dashboard-text-hint);
+ font-weight: 400;
+ letter-spacing: 0.02em;
+ display: block;
+ margin-bottom: 4px;
+}
+
+.dashboard .bento-username {
+ font-size: 25px;
+ font-weight: 700;
+ margin: 0 0 8px 0;
+ line-height: 1.1;
+ letter-spacing: -0.02em;
+ color: var(--dashboard-text-username);
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.dashboard .bento-tagline {
+ font-size: 13px;
+ margin: 0;
+ font-weight: 400;
+}
+
+.dashboard .bento-save-account-warning {
+ display: inline-flex;
+ align-items: center;
+ gap: 6px;
+ margin-top: 12px;
+ padding: 8px 14px;
+ background-color: var(--dashboard-warning-background);
+ border: 1px solid var(--dashboard-warning-border);
+ border-radius: 8px;
+ color: var(--dashboard-warning-text);
+ font-size: 13px;
+ font-weight: 500;
+ cursor: pointer;
+ text-decoration: none;
+ transition: all 0.15s ease;
+}
+
+.dashboard .bento-save-account-warning:hover {
+ background-color: var(--dashboard-warning-hover-bg);
+ border-color: var(--dashboard-warning-hover-border);
+ color: var(--dashboard-warning-hover-text);
+}
+
+.dashboard .bento-save-account-warning svg {
+ flex-shrink: 0;
+ color: var(--dashboard-warning-icon);
+}
+
+.dashboard .bento-save-account-warning:hover svg {
+ color: var(--dashboard-warning-hover-border);
+}
+
+/* Recent Apps Card - Rectangle */
+.dashboard .bento-recent {
+ min-height: 310px;
+ display: flex;
+ flex-direction: column;
+ background:
+ radial-gradient(circle at 90% 10%, var(--dashboard-gradient-indigo) 0%, transparent 40%),
+ radial-gradient(circle at 10% 90%, var(--dashboard-gradient-pink) 0%, transparent 35%),
+ linear-gradient(135deg, var(--dashboard-card-gradient-start) 0%, var(--dashboard-card-gradient-end) 100%);
+}
+
+.dashboard .bento-card-header {
+ padding: 20px 24px 0;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+}
+
+.dashboard .bento-card-header h2 {
+ margin: 0;
+ font-size: 15px;
+ font-weight: 600;
+ color: var(--dashboard-text-card-title);
+ letter-spacing: -0.01em;
+}
+
+/* Fancy header with icon */
+.dashboard .bento-card-fancy-header {
+ display: flex;
+ align-items: center;
+ gap: 14px;
+ padding: 20px 24px;
+ background: linear-gradient(135deg, var(--dashboard-fancy-header-start) 0%, var(--dashboard-fancy-header-end) 100%);
+ border-bottom: 1px solid var(--dashboard-shadow-light);
+}
+
+.dashboard .bento-card-fancy-icon {
+ width: 48px;
+ height: 48px;
+ border-radius: 12px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ flex-shrink: 0;
+}
+
+.dashboard .bento-card-fancy-icon svg {
+ width: 28px;
+ height: 28px;
+}
+
+.dashboard .bento-card-fancy-icon-apps {
+ background: linear-gradient(135deg, var(--dashboard-icon-blue-start) 0%, var(--dashboard-icon-blue-end) 100%);
+ color: white;
+ box-shadow: 0 4px 12px var(--dashboard-icon-blue-shadow);
+}
+
+.dashboard .bento-card-fancy-icon-usage {
+ background: linear-gradient(135deg, var(--dashboard-icon-green-start) 0%, var(--dashboard-icon-green-end) 100%);
+ color: white;
+ box-shadow: 0 4px 12px var(--dashboard-icon-green-shadow);
+}
+
+.dashboard .bento-card-fancy-text {
+ display: flex;
+ flex-direction: column;
+ gap: 2px;
+}
+
+.dashboard .bento-card-fancy-text h2 {
+ margin: 0;
+ font-size: 20px;
+ font-weight: 600;
+ color: var(--dashboard-text-primary);
+ letter-spacing: -0.02em;
+}
+
+.dashboard .bento-card-fancy-subtitle {
+ display: flex;
+ align-items: center;
+ gap: 5px;
+ font-size: 13px;
+ color: var(--dashboard-text-hint);
+}
+
+.dashboard .bento-card-fancy-subtitle svg {
+ width: 14px;
+ height: 14px;
+}
+
+.dashboard .bento-view-more {
+ font-size: 13px;
+ color: var(--dashboard-link);
+ text-decoration: none;
+ font-weight: 500;
+ transition: color 0.15s ease;
+}
+
+.dashboard .bento-view-more:hover {
+ color: var(--dashboard-link-hover);
+ text-decoration: underline;
+}
+
+.dashboard .bento-recent-apps-container {
+ flex: 1;
+ padding: 16px 24px 24px;
+}
+
+.dashboard .bento-recent-apps-grid {
+ display: grid;
+ grid-template-columns: repeat(2, 1fr);
+ gap: 12px 20px;
+}
+
+/* Hide apps beyond 6 on smaller screens */
+.dashboard .bento-recent-app:nth-child(n+7) {
+ display: none;
+}
+
+/* Show all 8 apps on 4K UHD screens */
+@media (min-width: 2560px) {
+ .dashboard .bento-recent-apps-grid {
+ grid-template-columns: repeat(2, 1fr);
+ }
+
+ .dashboard .bento-recent-app:nth-child(n+7) {
+ display: flex;
+ }
+}
+
+.dashboard .bento-recent-app {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ padding: 8px 0;
+ cursor: pointer;
+ transition: transform 0.15s ease;
+ gap: 12px;
+ width: 200px;
+}
+
+.dashboard .bento-recent-app:hover {
+ transform: scale(1.02);
+}
+
+.dashboard .bento-recent-app:active {
+ transform: scale(0.98);
+}
+
+.dashboard .bento-recent-app-icon {
+ width: 36px;
+ height: 36px;
+ border-radius: 8px;
+ flex-shrink: 0;
+}
+
+.dashboard .bento-recent-app-title {
+ font-size: 13px;
+ font-weight: 500;
+ color: var(--dashboard-text);
+ text-align: left;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ flex: 1;
+ min-width: 0;
+}
+
+/* Empty state for recent apps */
+.dashboard .bento-recent-apps-empty {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ height: 100%;
+ height: 180px;
+ text-align: center;
+ color: var(--dashboard-icon);
+}
+
+.dashboard .bento-recent-apps-empty svg {
+ width: 48px;
+ height: 48px;
+ stroke: var(--dashboard-border);
+ margin-bottom: 16px;
+}
+
+.dashboard .bento-recent-apps-empty p {
+ margin: 0 0 4px 0;
+ font-size: 14px;
+ font-weight: 500;
+ color: var(--dashboard-text-secondary);
+}
+
+.dashboard .bento-recent-apps-empty span {
+ font-size: 13px;
+ color: var(--dashboard-text-tertiary);
+}
+
+/* Usage bento card */
+.dashboard .bento-usage {
+ grid-column: 1 / -1;
+ min-height: auto;
+ background:
+ radial-gradient(circle at 5% 50%, var(--dashboard-gradient-green) 0%, transparent 40%),
+ radial-gradient(circle at 95% 50%, var(--dashboard-gradient-blue) 0%, transparent 40%),
+ linear-gradient(135deg, var(--dashboard-card-gradient-start) 0%, var(--dashboard-card-gradient-end) 100%);
+}
+
+.dashboard .bento-usage-container {
+ padding: 16px 24px 24px;
+}
+
+.dashboard .bento-usage-grid {
+ display: grid;
+ grid-template-columns: 1fr 1fr 1fr;
+ gap: 24px;
+}
+
+.dashboard .bento-usage-section {
+ display: flex;
+ flex-direction: column;
+}
+
+/* Your Plan section styles */
+.dashboard .bento-plan-section {
+ justify-content: flex-start;
+}
+
+.dashboard .bento-plan-info {
+ flex-grow: 1;
+}
+
+.dashboard .bento-plan-badge {
+ font-weight: 400;
+}
+
+.dashboard .bento-plan-badge.active {
+ color: var(--dashboard-success-text);
+}
+
+.dashboard .bento-plan-upgrade {
+ font-size: 14px;
+ font-weight: 500;
+ color: var(--dashboard-link);
+ text-decoration: none;
+ transition: color 0.2s;
+}
+
+.dashboard .bento-plan-upgrade:hover {
+ color: var(--dashboard-link-hover);
+ text-decoration: underline;
+}
+
+.dashboard .bento-usage-section-header {
+ display: flex;
+ flex-direction: row;
+ align-items: baseline;
+ margin-bottom: 8px;
+}
+
+.dashboard .bento-usage-section-header h3 {
+ margin: 0;
+ font-size: 14px;
+ font-weight: 500;
+ color: var(--dashboard-text-primary);
+ flex-grow: 1;
+}
+
+.dashboard .bento-usage-section-values {
+ font-size: 13px;
+ color: var(--dashboard-text-primary);
+ opacity: 0.85;
+}
+
+/* New card-based usage styles */
+.dashboard .bento-usage-card {
+ display: flex;
+ flex-direction: column;
+ gap: 16px;
+}
+
+.dashboard .bento-usage-card-header {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ text-decoration: none;
+ cursor: pointer;
+ transition: opacity 0.2s;
+}
+
+.dashboard .bento-usage-card-header:hover {
+ opacity: 0.7;
+}
+
+.dashboard .bento-usage-card-header h3 {
+ margin: 0;
+ font-size: 16px;
+ font-weight: 500;
+ color: var(--dashboard-text-primary);
+}
+
+.dashboard .bento-usage-card-arrow {
+ font-size: 18px;
+ font-weight: 300;
+ color: var(--dashboard-text-hint);
+ line-height: 1;
+}
+
+.dashboard .bento-usage-card-bar-wrapper {
+ width: 100%;
+ height: 14px;
+ background-color: var(--dashboard-usage-bar-background);
+ border-radius: 7px;
+ overflow: hidden;
+}
+
+.dashboard .bento-usage-card-bar {
+ height: 100%;
+ background: linear-gradient(90deg, var(--dashboard-usage-bar-start), var(--dashboard-usage-bar-end));
+ border-radius: 7px;
+ width: 0;
+ transition: width 0.4s ease;
+}
+
+.dashboard .bento-usage-card-info {
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+}
+
+.dashboard .bento-usage-card-used {
+ font-size: 20px;
+ font-weight: 600;
+ color: var(--dashboard-text-primary);
+}
+
+.dashboard .bento-usage-card-details {
+ font-size: 14px;
+ color: var(--dashboard-text-hint);
+}
+
+/* Legacy bar styles (kept for compatibility) */
+.dashboard .bento-usage-bar-wrapper {
+ width: 100%;
+ height: 20px;
+ border: 1px solid var(--dashboard-border);
+ border-radius: 3px;
+ background-color: var(--dashboard-card-background);
+ position: relative;
+ display: flex;
+ align-items: center;
+}
+
+.dashboard .bento-usage-bar {
+ height: 20px;
+ background: linear-gradient(var(--dashboard-legacy-bar-start), var(--dashboard-legacy-bar-mid), var(--dashboard-legacy-bar-start));
+ border-top-left-radius: 3px;
+ border-bottom-left-radius: 3px;
+ width: 0;
+ transition: width 0.3s ease;
+}
+
+/* Responsive bento layout */
+@media (max-width: 768px) {
+ .dashboard .bento-container {
+ grid-template-columns: 1fr;
+ gap: 16px;
+ padding: 0;
+ }
+
+ .dashboard .bento-welcome {
+ aspect-ratio: auto;
+ min-height: 180px;
+ }
+
+ .dashboard .bento-welcome-inner {
+ padding: 20px;
+ }
+
+ .dashboard .bento-username {
+ font-size: 24px;
+ }
+
+ .dashboard .bento-welcome-avatar {
+ width: 56px;
+ height: 56px;
+ margin-bottom: 12px;
+ border-width: 2px;
+ }
+
+ .dashboard .bento-recent-apps-grid {
+ grid-template-columns: 1fr;
+ gap: 8px;
+ }
+
+ .dashboard .bento-recent-app {
+ padding: 6px 0;
+ }
+
+ .dashboard .bento-recent-app-icon {
+ width: 32px;
+ height: 32px;
+ }
+
+ .dashboard .bento-recent-app-title {
+ font-size: 12px;
+ }
+
+ .dashboard .bento-usage-grid {
+ grid-template-columns: 1fr;
+ gap: 16px;
+ }
+
+ .dashboard .bento-usage-container {
+ padding: 16px 20px 20px;
+ }
+
+ .dashboard .bento-usage-card-header h3 {
+ font-size: 15px;
+ }
+
+ .dashboard .bento-usage-card-arrow {
+ font-size: 17px;
+ }
+
+ .dashboard .bento-usage-card-used {
+ font-size: 18px;
+ }
+
+ .dashboard .bento-usage-card-details {
+ font-size: 13px;
+ }
+
+ .dashboard .bento-card-fancy-header {
+ padding: 16px 20px;
+ gap: 12px;
+ }
+
+ .dashboard .bento-card-fancy-icon {
+ width: 40px;
+ height: 40px;
+ border-radius: 10px;
+ }
+
+ .dashboard .bento-card-fancy-icon svg {
+ width: 22px;
+ height: 22px;
+ }
+
+ .dashboard .bento-card-fancy-text h2 {
+ font-size: 17px;
+ }
+
+ .dashboard .bento-card-fancy-subtitle {
+ font-size: 12px;
+ }
+}
+
+/* ============================================== */
+/* Dashboard Account Tab */
+/* ============================================== */
+
+.dashboard-tab-content {
+ max-width: 700px;
+ margin: 0 auto;
+ padding: 8px 0;
+}
+
+.dashboard-section-header {
+ margin-bottom: 28px;
+}
+
+.dashboard-section-header h2 {
+ margin: 0 0 6px 0;
+ font-size: 26px;
+ font-weight: 700;
+ color: var(--dashboard-text-primary);
+ letter-spacing: -0.02em;
+}
+
+.dashboard-section-header p {
+ margin: 0;
+ font-size: 15px;
+ color: var(--dashboard-text-hint);
+}
+
+.dashboard-card {
+ background: var(--dashboard-card-background);
+ border-radius: 16px;
+ border: 1px solid var(--dashboard-shadow-light);
+ box-shadow:
+ 0 1px 3px var(--dashboard-shadow-subtle),
+ 0 4px 12px rgba(0, 0, 0, 0.03);
+}
+
+/* Profile card */
+.dashboard-profile-card {
+ padding: 32px;
+ margin-bottom: 24px;
+ background:
+ radial-gradient(circle at 100% 0%, var(--dashboard-gradient-purple) 0%, transparent 50%),
+ radial-gradient(circle at 0% 100%, rgba(168, 85, 247, 0.04) 0%, transparent 40%),
+ var(--dashboard-card-background);
+}
+
+.dashboard-profile-picture-section {
+ display: flex;
+ align-items: center;
+ gap: 24px;
+}
+
+.dashboard-profile-avatar {
+ width: 96px;
+ height: 96px;
+ border-radius: 50%;
+ background-size: cover;
+ background-position: center;
+ background-color: var(--dashboard-card-gradient-end);
+ flex-shrink: 0;
+ position: relative;
+ cursor: pointer;
+ transition: transform 0.2s ease;
+ box-shadow: 0 4px 16px var(--dashboard-shadow-medium);
+}
+
+.dashboard-profile-avatar:hover {
+ transform: scale(1.05);
+}
+
+.dashboard-profile-avatar-overlay {
+ position: absolute;
+ inset: 0;
+ background: var(--dashboard-shadow-overlay);
+ border-radius: 50%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ opacity: 0;
+ transition: opacity 0.2s ease;
+}
+
+.dashboard-profile-avatar:hover .dashboard-profile-avatar-overlay {
+ opacity: 1;
+}
+
+.dashboard-profile-avatar-overlay svg {
+ width: 28px;
+ height: 28px;
+ color: white;
+}
+
+.dashboard-profile-info {
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+}
+
+.dashboard-profile-info h3 {
+ margin: 0;
+ font-size: 22px;
+ font-weight: 700;
+ color: var(--dashboard-text-primary);
+}
+
+.dashboard-profile-info p {
+ margin: 0;
+ font-size: 14px;
+ color: var(--dashboard-text-hint);
+}
+
+.dashboard-profile-hint {
+ font-size: 12px;
+ color: var(--dashboard-text-muted);
+ margin-top: 4px;
+}
+
+/* Settings grid */
+.dashboard-settings-grid {
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+ margin-bottom: 32px;
+}
+
+.dashboard-settings-card {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 20px 24px;
+ gap: 16px;
+}
+
+.dashboard-settings-card-content {
+ display: flex;
+ align-items: center;
+ gap: 16px;
+ flex: 1;
+ min-width: 0;
+}
+
+.dashboard-settings-card-icon {
+ width: 44px;
+ height: 44px;
+ border-radius: 12px;
+ background: linear-gradient(135deg, var(--dashboard-card-gradient-start) 0%, var(--dashboard-card-gradient-end) 100%);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ flex-shrink: 0;
+}
+
+.dashboard-settings-card-icon svg {
+ width: 22px;
+ height: 22px;
+ color: var(--dashboard-text-secondary);
+}
+
+.dashboard-settings-card-info {
+ display: flex;
+ flex-direction: column;
+ gap: 2px;
+ min-width: 0;
+}
+
+.dashboard-settings-card-info strong {
+ font-size: 14px;
+ font-weight: 600;
+ color: var(--dashboard-text-primary);
+}
+
+.dashboard-settings-card-info span {
+ font-size: 14px;
+ color: var(--dashboard-text-hint);
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.dashboard-settings-card .button {
+ flex-shrink: 0;
+ color: var(--dashboard-text);
+ background: linear-gradient(var(--dashboard-sidebar-background), var(--dashboard-border));
+}
+
+.dashboard-settings-card-success {
+ border-color: var(--dashboard-success-border);
+ background: var(--dashboard-success-background);
+}
+
+.dashboard-settings-card-success .dashboard-settings-card-info span {
+ color: var(--dashboard-success-text);
+}
+
+.dashboard-settings-card-warning {
+ border-color: var(--dashboard-warning-border);
+ background: var(--dashboard-warning-background);
+}
+
+.dashboard-settings-card-warning .dashboard-settings-card-info span {
+ color: var(--dashboard-warning-text);
+}
+
+/* Danger zone */
+.dashboard-danger-zone {
+ padding-top: 24px;
+}
+
+.dashboard-danger-zone h3 {
+ margin: 0 0 16px 0;
+ font-size: 14px;
+ font-weight: 600;
+ color: var(--dashboard-danger-text);
+ text-transform: uppercase;
+ letter-spacing: 0.05em;
+}
+
+.dashboard-danger-card {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 20px 24px;
+ gap: 24px;
+ border-color: var(--dashboard-danger-border);
+ background: linear-gradient(135deg, var(--dashboard-danger-background) 0%, var(--dashboard-card-background) 100%);
+}
+
+.dashboard-danger-card-content {
+ flex: 1;
+ min-width: 0;
+}
+
+.dashboard-danger-card-info {
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+}
+
+.dashboard-danger-card-info strong {
+ font-size: 15px;
+ font-weight: 600;
+ color: var(--dashboard-danger-text);
+}
+
+.dashboard-danger-card-info span {
+ font-size: 13px;
+ color: var(--dashboard-text-hint);
+ line-height: 1.5;
+}
+
+/* Responsive styles for Account tab */
+@media (max-width: 768px) {
+ .dashboard-tab-content {
+ padding: 0 16px;
+ }
+
+ .dashboard-section-header h2 {
+ font-size: 22px;
+ }
+
+ .dashboard-profile-card {
+ padding: 24px;
+ }
+
+ .dashboard-profile-picture-section {
+ flex-direction: column;
+ text-align: center;
+ }
+
+ .dashboard-profile-info {
+ align-items: center;
+ }
+
+ .dashboard-settings-card {
+ flex-direction: column;
+ align-items: stretch;
+ gap: 16px;
+ padding: 16px 20px;
+ }
+
+ .dashboard-settings-card .button {
+ width: 100%;
+ }
+
+ .dashboard-danger-card {
+ flex-direction: column;
+ align-items: stretch;
+ gap: 16px;
+ }
+
+ .dashboard-danger-card .button {
+ width: 100%;
+ }
+}
+
+/* ======================================
+ Mobile Context Menu Modal
+ ====================================== */
+
+/* Backdrop - full screen overlay */
+.context-menu-modal-backdrop {
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background-color: var(--dashboard-shadow-overlay, rgba(0, 0, 0, 0.5));
+ z-index: 1000;
+ opacity: 0;
+ z-index: 9999999;
+ transition: opacity 200ms ease-in-out;
+}
+
+.context-menu-modal-backdrop.show {
+ opacity: 1;
+}
+
+/* Modal dialog - positioned over item */
+.context-menu-modal-dialog {
+ position: fixed;
+ background: var(--dashboard-card-background, #ffffff);
+ border-radius: 0.75rem;
+ box-shadow: 0 0.5rem 2rem var(--dashboard-shadow-medium, rgba(0, 0, 0, 0.1));
+ overflow: hidden;
+ transform: scale(0.9);
+ opacity: 0;
+ transition: transform 200ms ease-in-out, opacity 200ms ease-in-out;
+ max-height: calc(100vh - 40px);
+ display: flex;
+ flex-direction: column;
+}
+
+.context-menu-modal-backdrop.show .context-menu-modal-dialog {
+ transform: scale(1);
+ opacity: 1;
+}
+
+/* Menu items container */
+.context-menu-modal-dialog .context-menu-items {
+ display: flex;
+ flex-direction: column;
+ overflow-y: auto;
+}
+
+/* Individual menu item */
+.context-menu-modal-dialog .context-menu-item {
+ display: flex;
+ align-items: center;
+ padding: 0.5rem;
+ background: transparent;
+ border: none;
+ cursor: pointer;
+ text-align: left;
+ transition: background-color 150ms ease-in-out;
+ font-family: inherit;
+}
+
+.context-menu-modal-dialog .context-menu-item:last-child {
+ border-bottom: none;
+}
+
+.context-menu-modal-dialog .context-menu-item:hover {
+ background-color: var(--dashboard-hover, #e8e8e8);
+}
+
+.context-menu-modal-dialog .context-menu-item:active {
+ background-color: var(--dashboard-hover, #e8e8e8);
+}
+
+/* Menu item icon */
+.context-menu-modal-dialog .context-menu-item-icon {
+ width: 1.25rem;
+ height: 1.25rem;
+ margin-right: 0.75rem;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ flex-shrink: 0;
+}
+
+.context-menu-modal-dialog .context-menu-item-icon img {
+ width: 100%;
+ height: 100%;
+ opacity: 0.7;
+}
+
+.context-menu-modal-dialog .context-menu-item-icon svg {
+ width: 100%;
+ height: 100%;
+ opacity: 0.7;
+}
+
+@media (prefers-color-scheme: dark) {
+ .context-menu-modal-dialog .context-menu-item-icon img {
+ filter: invert(1);
+ }
+}
+
+.context-menu-modal-dialog .context-menu-item:hover .context-menu-item-icon img,
+.context-menu-modal-dialog .context-menu-item:hover .context-menu-item-icon svg {
+ opacity: 1;
+}
+
+/* Menu item label */
+.context-menu-modal-dialog .context-menu-item-label {
+ font-size: 0.9rem;
+ font-weight: 500;
+ color: var(--dashboard-text, #444);
+}
+
+/* Separator */
+.context-menu-modal-dialog .context-menu-separator {
+ height: 1px;
+ background-color: var(--dashboard-border, #e0e0e0);
+ margin: 0.25rem 0;
+}
+
+/* Delete item - special styling */
+.context-menu-modal-dialog .context-menu-item--delete .context-menu-item-label {
+ color: var(--dashboard-danger-text, #dc2626);
+}
+
+.context-menu-modal-dialog .context-menu-item--delete .context-menu-item-icon img,
+.context-menu-modal-dialog .context-menu-item--delete .context-menu-item-icon svg {
+ opacity: 0.8;
+}
+
+/* Desktop - transparent backdrop */
+@media (min-width: 768px) {
+ .context-menu-modal-backdrop {
+ background-color: transparent;
+ }
+}
+
+/* ======================================
+ Files Loading Spinner
+ ====================================== */
+
+.files-loading-overlay {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ /* background-color: var(--dashboard-background); */
+ z-index: 2147483647;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ pointer-events: all;
+ opacity: 0;
+ transition: opacity 2s ease-in;
+}
+
+.files-loading-container {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ min-height: 120px;
+ background: var(--dashboard-background);
+ border-radius: 10px;
+ padding: 20px;
+ min-width: 120px;
+}
+
+.files-loading-spinner {
+ width: 50px;
+ height: 50px;
+ border: 5px solid var(--dashboard-border);
+ border-top: 5px solid var(--select-color);
+ border-radius: 50%;
+ animation: files-loading-spin 1s linear infinite;
+ margin-bottom: 10px;
+}
+
+.files-loading-text {
+ font-family: Arial, sans-serif;
+ font-size: 16px;
+ margin-top: 10px;
+ text-align: center;
+ width: 100%;
+ color: var(--dashboard-text);
+}
+
+@keyframes files-loading-spin {
+ 0% { transform: rotate(0deg); }
+ 100% { transform: rotate(360deg); }
+}
+
+
+.context-menu .context-menu-item:not(.context-menu-divider) {
+ display: flex;
+ align-items: center;
+}
+
+.submenu-arrow {
+ margin-left: auto;
+}
diff --git a/src/gui/src/css/style.css b/src/gui/src/css/style.css
index 37288da0b..fe4aa4d12 100644
--- a/src/gui/src/css/style.css
+++ b/src/gui/src/css/style.css
@@ -256,7 +256,7 @@ input[type=text]:focus, input[type=password]:focus, input[type=email]:focus, sel
background: #8fc2e7 !important;
border: 1px solid #98adbd !important;
text-shadow: none !important;
- color: #f5f5f5 !important;
+ color: white !important;
}
.button-block {
@@ -665,6 +665,12 @@ span.header-sort-icon img {
filter: drop-shadow(0px 0px 1px rgba(0, 0, 0, 1));
}
+.item-badge.item-is-worker {
+ border-radius: 50%;
+ background: white;
+ cursor: pointer;
+}
+
.item-name, .item-name-editor, .item-name-shadow {
font-size: 12px;
color: white;
@@ -682,7 +688,6 @@ span.header-sort-icon img {
.item-name {
transition: opacity 0.2s ease-in-out;
- cursor: default;
max-width: 110px;
pointer-events: all;
}
@@ -1343,7 +1348,7 @@ span.header-sort-icon img {
}
.window-sidebar-item-dragging {
- background-color: #f5f5f5 !important;
+ background-color: white !important;
opacity: 0.8;
cursor: grabbing !important;
}
@@ -1808,6 +1813,19 @@ span.header-sort-icon img {
border: none;
}
+/* TabFiles rubber band selection area */
+.tabfiles-selection-area {
+ background-color: rgba(59, 130, 246, 0.15);
+ border: 1px solid var(--select-color);
+ position: absolute;
+ pointer-events: none;
+ z-index: 1000;
+}
+
+.dashboard-section-files .files {
+ position: relative;
+}
+
.container {
user-select: none;
}
@@ -2247,7 +2265,7 @@ label {
padding: 11px;
background-color: white;
border-radius: 3px;
- border: 1px solid #e0e0e0;
+ border: 1px solid var(--dashboard-border);
color: #65707b;
font-size: 13px;
}
@@ -2801,16 +2819,16 @@ label {
top: 0;
background-color: hsla(0, 0%, 100%, 0.8);
text-align: left;
- border-bottom: 1px solid #e0e0e0;
+ border-bottom: 1px solid var(--dashboard-border);
padding: 5px;
}
.task-manager-container thead th:not(:last-of-type) {
- border-right: 1px solid #e0e0e0;
+ border-right: 1px solid var(--dashboard-border);
}
.task-manager-container tbody > tr > td {
- border-bottom: 1px solid #e0e0e0;
+ border-bottom: 1px solid var(--dashboard-border);
padding: 0 calc(2.5 * var(--scale));
vertical-align: middle;
padding-left: 0;
@@ -3908,6 +3926,33 @@ fieldset[name=number-code] {
text-decoration: underline;
}
+.worker-badge-popover-title {
+ font-size: 14px;
+ margin: -10px;
+ margin-bottom: 5px;
+ padding: 8px 10px;
+ background: #e5e5e5;
+ color: #4b5f6f;
+}
+
+.worker-badge-popover-content {
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ overflow: hidden;
+ width: 270px;
+ padding: 10px;
+}
+
+.worker-badge-popover-link, .worker-badge-popover-link:visited {
+ color: #0073ed;
+ text-decoration: none;
+ width: 179px;
+}
+
+.worker-badge-popover-link:hover {
+ text-decoration: underline;
+}
+
/*!
* animate.css - https://animate.style/
* Version - 4.1.1
@@ -4077,7 +4122,7 @@ fieldset[name=number-code] {
}
.puter-auth-dialog-content {
- border: 1px solid #e8e8e8;
+ border: 1px solid var(--dashboard-hover);
border-radius: 8px;
padding: 20px;
background: white;
@@ -4233,7 +4278,7 @@ fieldset[name=number-code] {
background: #8fc2e7 !important;
border: 1px solid #98adbd !important;
text-shadow: none !important;
- color: #f5f5f5 !important;
+ color: var(--dashboard-sidebar-background) !important;
}
.puter-auth-dialog .button-block {
@@ -4401,7 +4446,7 @@ fieldset[name=number-code] {
.settings-sidebar {
width: 200px;
background-color: #f9f9f9;
- border-right: 1px solid #e0e0e0;
+ border-right: 1px solid #3d3d3d;
padding: 20px;
position: fixed;
margin-top: 1px;
@@ -4463,7 +4508,7 @@ fieldset[name=number-code] {
.settings-content h1 {
font-size: 24px;
margin-bottom: 20px;
- border-bottom: 1px solid #e0e0e0;
+ border-bottom: 1px solid #3d3d3d;
padding-bottom: 10px;
padding-left: 5px;
font-weight: 500;
@@ -4592,6 +4637,15 @@ fieldset[name=number-code] {
flex-direction: column;
}
+.dashboard-section-usage {
+ color: var(--dashboard-text);
+}
+
+.dashboard-section-usage .driver-usage {
+ color: var(--dashboard-text);
+ background-color: transparent;
+}
+
.driver-usage-container .driver-usage{
flex-grow: 1;
}
@@ -4832,6 +4886,11 @@ html.dark-mode .usage-table-show-less:hover {
font-weight: 500;
}
+.dashboard-section-usage .driver-usage-details-content-table thead th {
+ border-color: var(--dashboard-border);
+ background: var(--dashboard-input-background);
+}
+
.driver-usage-details-content-table thead th.sortable-th {
cursor: pointer;
user-select: none;
@@ -4842,6 +4901,14 @@ html.dark-mode .usage-table-show-less:hover {
background-color: #e5e5e5;
}
+.dashboard-section-usage .driver-usage-details-content-table thead th.sortable-th {
+ background-color: var(--dashboard-input-background);
+}
+
+.dashboard-section-usage .driver-usage-details-content-table thead th.sortable-th:hover {
+ background-color: var(--dashboard-shadow-light);
+}
+
.driver-usage-details-content-table thead th .sort-icon {
margin-left: 4px;
display: inline-flex;
@@ -4859,7 +4926,7 @@ html.dark-mode .usage-table-show-less:hover {
.driver-usage-details-content-table td {
padding: 7px 5px;
- border: 1px solid #e0e0e0;
+ border: 1px solid var(--dashboard-border);
max-width: 0;
overflow: hidden;
white-space: nowrap;
@@ -4953,9 +5020,9 @@ html.dark-mode .usage-table-show-less:hover {
}
.settings-card-danger {
- border-color: #f0080866;
- background: #ffecec;
- color: rgb(215 2 2);
+ border-color: #fecaca;;
+ background: #fef2f2;
+ color: #dc2626;
}
.settings-card-success {
@@ -4965,9 +5032,9 @@ html.dark-mode .usage-table-show-less:hover {
}
.settings-card-warning {
- border-color: #f0a500;
- background: #fff7e6;
- color: #c98900;
+ border-color: #f59e0b;
+ background: #fef3c7;
+ color: #92400e;
}
.error-message {
@@ -5026,7 +5093,7 @@ html.dark-mode .usage-table-show-less:hover {
display: flex;
flex-direction: column;
padding: 10px;
- border: 1px solid #e0e0e0;
+ border: 1px solid var(--dashboard-border);
border-radius: 4px;
gap: 4px;
}
@@ -5594,7 +5661,7 @@ html.dark-mode .usage-table-show-less:hover {
top: 5px;
right: 5px;
border-radius: 5px;
- background: linear-gradient(to bottom, #f8f8f8, #e0e0e0);
+ background: linear-gradient(to bottom, #f8f8f8, var(--dashboard-border));
cursor: pointer;
}
.btn-show-ai svg{
@@ -5644,1220 +5711,3 @@ html.dark-mode .usage-table-show-less:hover {
width: 20px;
height: 20px;
}
-
-/* ======================================
- Dashboard
- ====================================== */
-
-.dashboard {
- display: flex;
- height: 100%;
- background: #fff;
-}
-
-.dashboard-sidebar {
- width: 200px;
- min-width: 200px;
- background: #f5f5f5;
- border-right: 1px solid #e0e0e0;
- padding: 16px 12px;
- display: flex;
- flex-direction: column;
- box-sizing: border-box;
-}
-
-.dashboard-sidebar-nav {
- flex: 1;
-}
-
-.dashboard-sidebar-item {
- display: flex;
- align-items: center;
- gap: 10px;
- padding: 10px 12px;
- margin-bottom: 4px;
- border-radius: 6px;
- cursor: pointer;
- font-size: 14px;
- color: #444;
- transition: background-color 0.15s;
-}
-
-.dashboard-sidebar-item svg {
- width: 18px;
- height: 18px;
- flex-shrink: 0;
-}
-
-.dashboard-sidebar-item:hover {
- background: #e8e8e8;
-}
-
-.dashboard-sidebar-item.active {
- background: #e0e0e0;
- font-weight: 500;
-}
-
-/* User options button at bottom of sidebar */
-.dashboard-user-options {
- border-top: 1px solid #e0e0e0;
- padding-top: 12px;
- margin-top: 8px;
-}
-
-.dashboard-user-btn {
- display: flex;
- align-items: center;
- gap: 10px;
- padding: 10px 12px;
- border-radius: 6px;
- cursor: pointer;
- transition: background-color 0.15s;
-}
-
-.dashboard-user-btn:hover {
- background: #e8e8e8;
-}
-
-.dashboard-user-btn.has-open-contextmenu {
- background: #e8e8e8;
-}
-
-.dashboard-user-avatar {
- width: 28px;
- height: 28px;
- border-radius: 50%;
- background-size: cover;
- background-position: center;
- background-color: #ddd;
- flex-shrink: 0;
-}
-
-.dashboard-user-name {
- flex: 1;
- font-size: 14px;
- color: #333;
- font-weight: 500;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
-}
-
-.dashboard-user-chevron {
- width: 16px;
- height: 16px;
- color: #888;
- flex-shrink: 0;
- transition: transform 0.1s ease;
-}
-
-.dashboard-user-chevron.open {
- transform: rotate(180deg);
-}
-
-.dashboard-content {
- flex: 1;
- padding: 24px 32px;
- overflow-y: auto;
-}
-
-.dashboard-section {
- display: none;
-}
-
-.dashboard-section.active {
- display: block;
-}
-
-.dashboard-section h2 {
- margin: 0 0 16px 0;
- font-size: 20px;
- font-weight: 600;
- color: #333;
-}
-
-.dashboard-section p {
- font-size: 14px;
-}
-
-.dashboard-section-apps {
- max-width: 600px;
- margin: 0 auto;
-}
-
-.dashboard-section-usage {
- max-width: 600px;
- margin: 0 auto;
-}
-
-/* Dashboard Apps */
-.dashboard-apps-container {
- margin-top: 8px;
-}
-
-.dashboard-apps-heading {
- font-size: 14px;
- font-weight: 600;
- color: #666;
- text-transform: uppercase;
- letter-spacing: 0.5px;
- margin: 0 0 16px 0;
-}
-
-.dashboard-apps-grid {
- display: grid;
- grid-template-columns: repeat(auto-fill, minmax(90px, 1fr));
- gap: 20px;
- margin-bottom: 8px;
-}
-
-.dashboard-app-card {
- width: 100%;
- transition: background-color 0.15s, transform 0.1s;
- transition: transform 0.15s ease;
-}
-
-.dashboard-app-card .start-app {
- cursor: pointer;
- width: 90px;
-}
-.dashboard-app-card:hover {
- background: none !important;
-}
-
-.dashboard-app-card:hover .start-app, .dashboard-app-card .start-app:hover {
- background: none !important;
-}
-
-.dashboard-app-card:active {
- transform: scale(0.97);
-}
-
-.dashboard-app-card .start-app {
- display: flex;
- flex-direction: column;
- align-items: center;
- text-align: center;
-}
-
-.dashboard-app-icon {
- width: 48px;
- height: 48px;
- margin-bottom: 8px;
- filter: drop-shadow(0px 1px 2px rgba(0, 0, 0, 0.1));
-}
-
-.dashboard-app-card .start-app{
- transition: transform 0.15s ease;
-}
-.dashboard-app-card .start-app:hover {
- transform: scale(1.05);
-}
-
-.dashboard-app-title {
- font-size: 13px;
- color: #333;
- text-align: center;
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
- max-width: 100%;
-}
-
-.dashboard-no-apps {
- color: #888;
- font-size: 14px;
- padding: 24px 0;
-}
-
-/* Mobile sidebar toggle */
-.dashboard-sidebar-toggle {
- display: none;
- position: fixed;
- top: 12px;
- left: 12px;
- z-index: 100;
- width: 40px;
- height: 40px;
- background: #fff;
- border: 1px solid #e0e0e0;
- border-radius: 6px;
- cursor: pointer;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- gap: 4px;
-}
-
-.dashboard-sidebar-toggle span {
- display: block;
- width: 18px;
- height: 2px;
- background: #444;
- border-radius: 1px;
- transition: transform 0.2s, opacity 0.2s;
-}
-
-.dashboard-sidebar-toggle.open span:nth-child(1) {
- transform: rotate(45deg) translate(4px, 4px);
-}
-
-.dashboard-sidebar-toggle.open span:nth-child(2) {
- opacity: 0;
-}
-
-.dashboard-sidebar-toggle.open span:nth-child(3) {
- transform: rotate(-45deg) translate(4px, -4px);
-}
-
-.dashboard-sidebar-separator {
- height: 1px;
- background: #e0e0e0;
- margin: 8px 0;
-}
-
-/* Responsive: tablet and below */
-@media (max-width: 768px) {
- .dashboard-sidebar-nav {
- padding-top: 45px;
- }
- .dashboard-sidebar-toggle {
- display: flex;
- }
-
- .dashboard-sidebar {
- position: fixed;
- left: 0;
- top: 0;
- height: 100%;
- z-index: 99;
- transform: translateX(-100%);
- transition: transform 0.2s ease;
- box-shadow: 2px 0 8px rgba(0, 0, 0, 0.1);
- }
-
- .dashboard-sidebar.open {
- transform: translateX(0);
- }
-
- .dashboard-content {
- padding: 64px 16px 24px;
- }
-
- .dashboard-apps-grid {
- grid-template-columns: repeat(auto-fill, minmax(80px, 1fr));
- gap: 4px;
- }
-
- .dashboard-app-card {
- padding: 10px 6px;
- }
-
- .dashboard-app-icon {
- width: 40px;
- height: 40px;
- }
-}
-
-/* Full HD */
-@media (min-width: 1920px) {
- .dashboard-section-home .bento-container {
- max-width: 1000px;
- }
- .dashboard-section-usage{
- max-width: 800px;
- }
-}
-/* 4K UHD */
-@media (min-width: 2560px) {
- .dashboard-section-home .bento-container {
- max-width: 1200px;
- }
- .dashboard-section-usage{
- max-width: 900px;
- }
-}
-
-/* ============================================== */
-/* Bento Box Home Dashboard */
-/* ============================================== */
-
-.bento-container {
- display: grid;
- grid-template-columns: 280px 1fr;
- gap: 20px;
- max-width: 800px;
- margin: 0 auto;
- padding: 8px 0;
- align-items: stretch;
-}
-
-.bento-card {
- background: #fff;
- border-radius: 20px;
- overflow: hidden;
- box-shadow:
- 0 1px 3px rgba(0, 0, 0, 0.04),
- 0 4px 12px rgba(0, 0, 0, 0.03);
- border: 1px solid rgba(0, 0, 0, 0.06);
-}
-
-/* Welcome Card */
-.bento-welcome {
- position: relative;
- background: linear-gradient(135deg, #f8fafc 0%, #e2e8f0 100%);
- color: #1e293b;
- min-height: 280px;
-}
-
-.bento-welcome-inner {
- position: relative;
- width: 100%;
- height: 100%;
- display: flex;
- flex-direction: column;
- justify-content: flex-end;
- padding: 24px;
- box-sizing: border-box;
-}
-
-.bento-welcome-pattern {
- position: absolute;
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
- background-image:
- radial-gradient(circle at 20% 30%, rgba(99, 102, 241, 0.08) 0%, transparent 50%),
- radial-gradient(circle at 80% 70%, rgba(168, 85, 247, 0.06) 0%, transparent 40%);
- pointer-events: none;
-}
-
-.bento-welcome-content {
- position: relative;
- z-index: 1;
-}
-
-.bento-welcome-avatar {
- width: 72px;
- height: 72px;
- border-radius: 50%;
- background-size: cover;
- background-position: center;
- background-color: #e2e8f0;
- border: 3px solid #fff;
- margin-bottom: 16px;
- box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1);
-}
-
-.bento-greeting {
- font-size: 14px;
- color: #64748b;
- font-weight: 400;
- letter-spacing: 0.02em;
- display: block;
- margin-bottom: 4px;
-}
-
-.bento-username {
- font-size: 25px;
- font-weight: 700;
- margin: 0 0 8px 0;
- line-height: 1.1;
- letter-spacing: -0.02em;
- color: #414b62;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
-}
-
-.bento-tagline {
- font-size: 13px;
- margin: 0;
- font-weight: 400;
-}
-
-.bento-save-account-warning {
- display: inline-flex;
- align-items: center;
- gap: 6px;
- margin-top: 12px;
- padding: 8px 14px;
- background-color: #fef3c7;
- border: 1px solid #f59e0b;
- border-radius: 8px;
- color: #92400e;
- font-size: 13px;
- font-weight: 500;
- cursor: pointer;
- text-decoration: none;
- transition: all 0.15s ease;
-}
-
-.bento-save-account-warning:hover {
- background-color: #fde68a;
- border-color: #d97706;
- color: #78350f;
-}
-
-.bento-save-account-warning svg {
- flex-shrink: 0;
- color: #f59e0b;
-}
-
-.bento-save-account-warning:hover svg {
- color: #d97706;
-}
-
-/* Recent Apps Card - Rectangle */
-.bento-recent {
- min-height: 310px;
- display: flex;
- flex-direction: column;
- background:
- radial-gradient(circle at 90% 10%, rgba(99, 102, 241, 0.06) 0%, transparent 40%),
- radial-gradient(circle at 10% 90%, rgba(236, 72, 153, 0.04) 0%, transparent 35%),
- linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);
-}
-
-.bento-card-header {
- padding: 20px 24px 0;
- display: flex;
- align-items: center;
- justify-content: space-between;
-}
-
-.bento-card-header h2 {
- margin: 0;
- font-size: 15px;
- font-weight: 600;
- color: #1a1a1a;
- letter-spacing: -0.01em;
-}
-
-/* Fancy header with icon */
-.bento-card-fancy-header {
- display: flex;
- align-items: center;
- gap: 14px;
- padding: 20px 24px;
- background: linear-gradient(135deg, rgba(200, 220, 255, 0.5) 0%, rgba(180, 210, 255, 0.3) 100%);
- border-bottom: 1px solid rgb(0 0 0 / 1%);
-}
-
-.bento-card-fancy-icon {
- width: 48px;
- height: 48px;
- border-radius: 12px;
- display: flex;
- align-items: center;
- justify-content: center;
- flex-shrink: 0;
-}
-
-.bento-card-fancy-icon svg {
- width: 28px;
- height: 28px;
-}
-
-.bento-card-fancy-icon-apps {
- background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);
- color: white;
- box-shadow: 0 4px 12px rgba(59, 130, 246, 0.3);
-}
-
-.bento-card-fancy-icon-usage {
- background: linear-gradient(135deg, #10b981 0%, #059669 100%);
- color: white;
- box-shadow: 0 4px 12px rgba(16, 185, 129, 0.3);
-}
-
-.bento-card-fancy-text {
- display: flex;
- flex-direction: column;
- gap: 2px;
-}
-
-.bento-card-fancy-text h2 {
- margin: 0;
- font-size: 20px;
- font-weight: 600;
- color: #1e293b;
- letter-spacing: -0.02em;
-}
-
-.bento-card-fancy-subtitle {
- display: flex;
- align-items: center;
- gap: 5px;
- font-size: 13px;
- color: #64748b;
-}
-
-.bento-card-fancy-subtitle svg {
- width: 14px;
- height: 14px;
-}
-
-.bento-view-more {
- font-size: 13px;
- color: #5271ff;
- text-decoration: none;
- font-weight: 500;
- transition: color 0.15s ease;
-}
-
-.bento-view-more:hover {
- color: #3d5bd9;
- text-decoration: underline;
-}
-
-.bento-recent-apps-container {
- flex: 1;
- padding: 16px 24px 24px;
-}
-
-.bento-recent-apps-grid {
- display: grid;
- grid-template-columns: repeat(2, 1fr);
- gap: 12px 20px;
-}
-
-/* Hide apps beyond 6 on smaller screens */
-.bento-recent-app:nth-child(n+7) {
- display: none;
-}
-
-/* Show all 8 apps on 4K UHD screens */
-@media (min-width: 2560px) {
- .bento-recent-apps-grid {
- grid-template-columns: repeat(2, 1fr);
- }
-
- .bento-recent-app:nth-child(n+7) {
- display: flex;
- }
-}
-
-.bento-recent-app {
- display: flex;
- flex-direction: row;
- align-items: center;
- padding: 8px 0;
- cursor: pointer;
- transition: transform 0.15s ease;
- gap: 12px;
- width: 200px;
-}
-
-.bento-recent-app:hover {
- transform: scale(1.02);
-}
-
-.bento-recent-app:active {
- transform: scale(0.98);
-}
-
-.bento-recent-app-icon {
- width: 36px;
- height: 36px;
- border-radius: 8px;
- flex-shrink: 0;
-}
-
-.bento-recent-app-title {
- font-size: 13px;
- font-weight: 500;
- color: #333;
- text-align: left;
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
- flex: 1;
- min-width: 0;
-}
-
-/* Empty state for recent apps */
-.bento-recent-apps-empty {
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- height: 100%;
- height: 180px;
- text-align: center;
- color: #888;
-}
-
-.bento-recent-apps-empty svg {
- width: 48px;
- height: 48px;
- stroke: #ccc;
- margin-bottom: 16px;
-}
-
-.bento-recent-apps-empty p {
- margin: 0 0 4px 0;
- font-size: 14px;
- font-weight: 500;
- color: #666;
-}
-
-.bento-recent-apps-empty span {
- font-size: 13px;
- color: #999;
-}
-
-/* Usage bento card */
-.bento-usage {
- grid-column: 1 / -1;
- min-height: auto;
- background:
- radial-gradient(circle at 5% 50%, rgba(34, 197, 94, 0.05) 0%, transparent 40%),
- radial-gradient(circle at 95% 50%, rgba(59, 130, 246, 0.05) 0%, transparent 40%),
- linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);
-}
-
-.bento-usage-container {
- padding: 16px 24px 24px;
-}
-
-.bento-usage-grid {
- display: grid;
- grid-template-columns: 1fr 1fr 1fr;
- gap: 24px;
-}
-
-.bento-usage-section {
- display: flex;
- flex-direction: column;
-}
-
-/* Your Plan section styles */
-.bento-plan-section {
- justify-content: flex-start;
-}
-
-.bento-plan-info {
- flex-grow: 1;
-}
-
-.bento-plan-badge {
- font-weight: 400;
-}
-
-.bento-plan-badge.active {
- color: #16a34a;
-}
-
-.bento-plan-upgrade {
- font-size: 14px;
- font-weight: 500;
- color: #3b82f6;
- text-decoration: none;
- transition: color 0.2s;
-}
-
-.bento-plan-upgrade:hover {
- color: #2563eb;
- text-decoration: underline;
-}
-
-.bento-usage-section-header {
- display: flex;
- flex-direction: row;
- align-items: baseline;
- margin-bottom: 8px;
-}
-
-.bento-usage-section-header h3 {
- margin: 0;
- font-size: 14px;
- font-weight: 500;
- color: #3c4963;
- flex-grow: 1;
-}
-
-.bento-usage-section-values {
- font-size: 13px;
- color: #3c4963;
- opacity: 0.85;
-}
-
-/* New card-based usage styles */
-.bento-usage-card {
- display: flex;
- flex-direction: column;
- gap: 16px;
-}
-
-.bento-usage-card-header {
- display: flex;
- align-items: center;
- gap: 8px;
- text-decoration: none;
- cursor: pointer;
- transition: opacity 0.2s;
-}
-
-.bento-usage-card-header:hover {
- opacity: 0.7;
-}
-
-.bento-usage-card-header h3 {
- margin: 0;
- font-size: 16px;
- font-weight: 500;
- color: #1e293b;
-}
-
-.bento-usage-card-arrow {
- font-size: 18px;
- font-weight: 300;
- color: #64748b;
- line-height: 1;
-}
-
-.bento-usage-card-bar-wrapper {
- width: 100%;
- height: 14px;
- background-color: #e5e7eb;
- border-radius: 7px;
- overflow: hidden;
-}
-
-.bento-usage-card-bar {
- height: 100%;
- background: linear-gradient(90deg, #f59e0b, #f97316);
- border-radius: 7px;
- width: 0;
- transition: width 0.4s ease;
-}
-
-.bento-usage-card-info {
- display: flex;
- flex-direction: column;
- gap: 4px;
-}
-
-.bento-usage-card-used {
- font-size: 20px;
- font-weight: 600;
- color: #1e293b;
-}
-
-.bento-usage-card-details {
- font-size: 14px;
- color: #64748b;
-}
-
-/* Legacy bar styles (kept for compatibility) */
-.bento-usage-bar-wrapper {
- width: 100%;
- height: 20px;
- border: 1px solid #dddddd;
- border-radius: 3px;
- background-color: #fbfbfb;
- position: relative;
- display: flex;
- align-items: center;
-}
-
-.bento-usage-bar {
- height: 20px;
- background: linear-gradient(#dbe3ef, #c2ccdc, #dbe3ef);
- border-top-left-radius: 3px;
- border-bottom-left-radius: 3px;
- width: 0;
- transition: width 0.3s ease;
-}
-
-/* Responsive bento layout */
-@media (max-width: 768px) {
- .bento-container {
- grid-template-columns: 1fr;
- gap: 16px;
- padding: 0;
- }
-
- .bento-welcome {
- aspect-ratio: auto;
- min-height: 180px;
- }
-
- .bento-welcome-inner {
- padding: 20px;
- }
-
- .bento-username {
- font-size: 24px;
- }
-
- .bento-welcome-avatar {
- width: 56px;
- height: 56px;
- margin-bottom: 12px;
- border-width: 2px;
- }
-
- .bento-recent-apps-grid {
- grid-template-columns: 1fr;
- gap: 8px;
- }
-
- .bento-recent-app {
- padding: 6px 0;
- }
-
- .bento-recent-app-icon {
- width: 32px;
- height: 32px;
- }
-
- .bento-recent-app-title {
- font-size: 12px;
- }
-
- .bento-usage-grid {
- grid-template-columns: 1fr;
- gap: 16px;
- }
-
- .bento-usage-container {
- padding: 16px 20px 20px;
- }
-
- .bento-usage-card-header h3 {
- font-size: 15px;
- }
-
- .bento-usage-card-arrow {
- font-size: 17px;
- }
-
- .bento-usage-card-used {
- font-size: 18px;
- }
-
- .bento-usage-card-details {
- font-size: 13px;
- }
-
- .bento-card-fancy-header {
- padding: 16px 20px;
- gap: 12px;
- }
-
- .bento-card-fancy-icon {
- width: 40px;
- height: 40px;
- border-radius: 10px;
- }
-
- .bento-card-fancy-icon svg {
- width: 22px;
- height: 22px;
- }
-
- .bento-card-fancy-text h2 {
- font-size: 17px;
- }
-
- .bento-card-fancy-subtitle {
- font-size: 12px;
- }
-}
-
-/* ============================================== */
-/* Dashboard Account Tab */
-/* ============================================== */
-
-.dashboard-tab-content {
- max-width: 700px;
- margin: 0 auto;
- padding: 8px 0;
-}
-
-.dashboard-section-header {
- margin-bottom: 28px;
-}
-
-.dashboard-section-header h2 {
- margin: 0 0 6px 0;
- font-size: 26px;
- font-weight: 700;
- color: #1e293b;
- letter-spacing: -0.02em;
-}
-
-.dashboard-section-header p {
- margin: 0;
- font-size: 15px;
- color: #64748b;
-}
-
-.dashboard-card {
- background: #fff;
- border-radius: 16px;
- border: 1px solid rgba(0, 0, 0, 0.06);
- box-shadow:
- 0 1px 3px rgba(0, 0, 0, 0.04),
- 0 4px 12px rgba(0, 0, 0, 0.03);
-}
-
-/* Profile card */
-.dashboard-profile-card {
- padding: 32px;
- margin-bottom: 24px;
- background:
- radial-gradient(circle at 100% 0%, rgba(99, 102, 241, 0.06) 0%, transparent 50%),
- radial-gradient(circle at 0% 100%, rgba(168, 85, 247, 0.04) 0%, transparent 40%),
- #fff;
-}
-
-.dashboard-profile-picture-section {
- display: flex;
- align-items: center;
- gap: 24px;
-}
-
-.dashboard-profile-avatar {
- width: 96px;
- height: 96px;
- border-radius: 50%;
- background-size: cover;
- background-position: center;
- background-color: #e2e8f0;
- flex-shrink: 0;
- position: relative;
- cursor: pointer;
- transition: transform 0.2s ease;
- box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1);
-}
-
-.dashboard-profile-avatar:hover {
- transform: scale(1.05);
-}
-
-.dashboard-profile-avatar-overlay {
- position: absolute;
- inset: 0;
- background: rgba(0, 0, 0, 0.5);
- border-radius: 50%;
- display: flex;
- align-items: center;
- justify-content: center;
- opacity: 0;
- transition: opacity 0.2s ease;
-}
-
-.dashboard-profile-avatar:hover .dashboard-profile-avatar-overlay {
- opacity: 1;
-}
-
-.dashboard-profile-avatar-overlay svg {
- width: 28px;
- height: 28px;
- color: white;
-}
-
-.dashboard-profile-info {
- display: flex;
- flex-direction: column;
- gap: 4px;
-}
-
-.dashboard-profile-info h3 {
- margin: 0;
- font-size: 22px;
- font-weight: 700;
- color: #1e293b;
-}
-
-.dashboard-profile-info p {
- margin: 0;
- font-size: 14px;
- color: #64748b;
-}
-
-.dashboard-profile-hint {
- font-size: 12px;
- color: #94a3b8;
- margin-top: 4px;
-}
-
-/* Settings grid */
-.dashboard-settings-grid {
- display: flex;
- flex-direction: column;
- gap: 12px;
- margin-bottom: 32px;
-}
-
-.dashboard-settings-card {
- display: flex;
- align-items: center;
- justify-content: space-between;
- padding: 20px 24px;
- gap: 16px;
-}
-
-.dashboard-settings-card-content {
- display: flex;
- align-items: center;
- gap: 16px;
- flex: 1;
- min-width: 0;
-}
-
-.dashboard-settings-card-icon {
- width: 44px;
- height: 44px;
- border-radius: 12px;
- background: linear-gradient(135deg, #f1f5f9 0%, #e2e8f0 100%);
- display: flex;
- align-items: center;
- justify-content: center;
- flex-shrink: 0;
-}
-
-.dashboard-settings-card-icon svg {
- width: 22px;
- height: 22px;
- color: #475569;
-}
-
-.dashboard-settings-card-info {
- display: flex;
- flex-direction: column;
- gap: 2px;
- min-width: 0;
-}
-
-.dashboard-settings-card-info strong {
- font-size: 14px;
- font-weight: 600;
- color: #1e293b;
-}
-
-.dashboard-settings-card-info span {
- font-size: 14px;
- color: #64748b;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
-}
-
-.dashboard-settings-card .button {
- flex-shrink: 0;
-}
-
-.dashboard-settings-card-success {
- border-color: #08bf4e;
- background: #e6ffed;
-}
-
-.dashboard-settings-card-success .dashboard-settings-card-info span {
- color: #03933a;
-}
-
-.dashboard-settings-card-warning {
- border-color: #f0a500;
- background: #fff7e6;
-}
-
-.dashboard-settings-card-warning .dashboard-settings-card-info span {
- color: #c98900;
-}
-
-/* Danger zone */
-.dashboard-danger-zone {
- padding-top: 24px;
-}
-
-.dashboard-danger-zone h3 {
- margin: 0 0 16px 0;
- font-size: 14px;
- font-weight: 600;
- color: #dc2626;
- text-transform: uppercase;
- letter-spacing: 0.05em;
-}
-
-.dashboard-danger-card {
- display: flex;
- align-items: center;
- justify-content: space-between;
- padding: 20px 24px;
- gap: 24px;
- border-color: #fecaca;
- background: linear-gradient(135deg, #fef2f2 0%, #fff 100%);
-}
-
-.dashboard-danger-card-content {
- flex: 1;
- min-width: 0;
-}
-
-.dashboard-danger-card-info {
- display: flex;
- flex-direction: column;
- gap: 4px;
-}
-
-.dashboard-danger-card-info strong {
- font-size: 15px;
- font-weight: 600;
- color: #dc2626;
-}
-
-.dashboard-danger-card-info span {
- font-size: 13px;
- color: #64748b;
- line-height: 1.5;
-}
-
-/* Responsive styles for Account tab */
-@media (max-width: 768px) {
- .dashboard-tab-content {
- padding: 0 16px;
- }
-
- .dashboard-section-header h2 {
- font-size: 22px;
- }
-
- .dashboard-profile-card {
- padding: 24px;
- }
-
- .dashboard-profile-picture-section {
- flex-direction: column;
- text-align: center;
- }
-
- .dashboard-profile-info {
- align-items: center;
- }
-
- .dashboard-settings-card {
- flex-direction: column;
- align-items: stretch;
- gap: 16px;
- padding: 16px 20px;
- }
-
- .dashboard-settings-card .button {
- width: 100%;
- }
-
- .dashboard-danger-card {
- flex-direction: column;
- align-items: stretch;
- gap: 16px;
- }
-
- .dashboard-danger-card .button {
- width: 100%;
- }
-}
\ No newline at end of file
diff --git a/src/gui/src/globals.js b/src/gui/src/globals.js
index 4669d88fa..4701deb34 100644
--- a/src/gui/src/globals.js
+++ b/src/gui/src/globals.js
@@ -22,6 +22,8 @@ window.clipboard = [];
window.actions_history = [];
window.window_nav_history = {};
window.window_nav_history_current_position = {};
+window.dashboard_nav_history = [];
+window.dashboard_nav_history_current_position = 0;
window.progress_tracker = [];
window.upload_item_global_id = 0;
window.app_instance_ids = new Set();
diff --git a/src/gui/src/helpers.js b/src/gui/src/helpers.js
index 22a030b84..62fcdeac6 100644
--- a/src/gui/src/helpers.js
+++ b/src/gui/src/helpers.js
@@ -1694,6 +1694,10 @@ window.move_items = async function (el_items, dest_path, is_undo = false) {
suggested_apps: fsentry.suggested_apps,
};
UIItem(options);
+ // In dashboard mode, also create item via dashboard's renderer
+ if ( window.is_dashboard_mode && window.UIDashboardFileItem ) {
+ window.UIDashboardFileItem(fsentry);
+ }
moved_items.push({ 'options': options, 'original_path': $(el_item).attr('data-path') });
// this operation may have created some missing directories,
@@ -1719,6 +1723,10 @@ window.move_items = async function (el_items, dest_path, is_undo = false) {
suggested_apps: dir.suggested_apps,
});
}
+ // In dashboard mode, also create parent dirs via dashboard's renderer
+ if ( window.is_dashboard_mode && window.UIDashboardFileItem ) {
+ window.UIDashboardFileItem(dir);
+ }
window.sort_items(item_container);
});
@@ -2846,7 +2854,7 @@ window.rename_file = async (options, new_name, old_name, old_path, el_item, el_i
puter.fs.rename({
uid: options.uid === 'null' ? null : options.uid,
new_name: new_name,
- excludeSocketID: window.socket.id,
+ excludeSocketID: window.socket?.id,
success: async (fsentry) => {
// Add action to actions_history for undo ability
if ( ! is_undo )
@@ -2975,30 +2983,27 @@ window.delete_item_with_path = async function (path) {
};
window.undo_last_action = async () => {
- if ( window.actions_history.length > 0 ) {
- const last_action = window.actions_history.pop();
+ if ( window.actions_history.length === 0 ) return;
- // Undo the create file action
- if ( last_action.operation === 'create_file' || last_action.operation === 'create_folder' ) {
- const lastCreatedItem = last_action.data;
- window.undo_create_file_or_folder(lastCreatedItem);
- } else if ( last_action.operation === 'rename' ) {
- const { options, new_name, old_name, old_path, el_item, el_item_name, el_item_icon, el_item_name_editor, website_url } = last_action.data;
+ const last_action = window.actions_history.pop();
+ const { operation, data } = last_action;
+
+ // Map operations to their undo handlers
+ const undoHandlers = {
+ create_file: () => window.undo_create_file_or_folder(data),
+ create_folder: () => window.undo_create_file_or_folder(data),
+ upload: () => window.undo_upload(data),
+ copy: () => window.undo_copy(data),
+ move: () => window.undo_move(data),
+ delete: () => window.undo_delete(data),
+ rename: () => {
+ const { options, new_name, old_name, old_path, el_item, el_item_name, el_item_icon, el_item_name_editor, website_url } = data;
window.rename_file(options, old_name, new_name, old_path, el_item, el_item_name, el_item_icon, el_item_name_editor, website_url, true);
- } else if ( last_action.operation === 'upload' ) {
- const files = last_action.data;
- window.undo_upload(files);
- } else if ( last_action.operation === 'copy' ) {
- const files = last_action.data;
- window.undo_copy(files);
- } else if ( last_action.operation === 'move' ) {
- const items = last_action.data;
- window.undo_move(items);
- } else if ( last_action.operation === 'delete' ) {
- const items = last_action.data;
- window.undo_delete(items);
- }
- }
+ },
+ };
+
+ const handler = undoHandlers[operation];
+ if ( handler ) handler();
};
window.undo_create_file_or_folder = async (item) => {
diff --git a/src/gui/src/helpers/generate_file_context_menu.js b/src/gui/src/helpers/generate_file_context_menu.js
new file mode 100644
index 000000000..314f9dc50
--- /dev/null
+++ b/src/gui/src/helpers/generate_file_context_menu.js
@@ -0,0 +1,723 @@
+/**
+ * Copyright (C) 2024-present Puter Technologies Inc.
+ *
+ * This file is part of Puter.
+ *
+ * Puter is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see
.
+ */
+
+import UIAlert from '../UI/UIAlert.js';
+import UIWindowShare from '../UI/UIWindowShare.js';
+import UIWindowPublishWebsite from '../UI/UIWindowPublishWebsite.js';
+import UIWindowItemProperties from '../UI/UIWindowItemProperties.js';
+import UIWindowSaveAccount from '../UI/UIWindowSaveAccount.js';
+import UIWindowEmailConfirmationRequired from '../UI/UIWindowEmailConfirmationRequired.js';
+import UIWindowPublishWorker from '../UI/UIWindowPublishWorker.js';
+import open_item from './open_item.js';
+import launch_app from './launch_app.js';
+import path from '../lib/path.js';
+import mime from '../lib/mime.js';
+
+const AI_APP_NAME = 'ai';
+
+/**
+ * Parses item metadata for AI payload
+ * @param {string} metadata - JSON string of metadata
+ * @returns {Object|undefined} Parsed metadata or undefined
+ */
+const parseItemMetadataForAI = (metadata) => {
+ if ( ! metadata ) {
+ return undefined;
+ }
+ try {
+ return JSON.parse(metadata);
+ } catch ( error ) {
+ console.warn('Failed to parse item metadata for AI payload.', error);
+ return undefined;
+ }
+};
+
+/**
+ * Builds AI payload from item elements
+ * @param {jQuery} $elements - jQuery collection of elements
+ * @returns {Array} Array of item data for AI
+ */
+const buildAIPayloadFromItems = ($elements) => {
+ return $elements.get().map((element) => {
+ const $element = $(element);
+ return {
+ uid: $element.attr('data-uid'),
+ path: $element.attr('data-path'),
+ name: $element.attr('data-name'),
+ is_dir: $element.attr('data-is_dir') === '1',
+ is_shortcut: $element.attr('data-is_shortcut') === '1',
+ shortcut_to: $element.attr('data-shortcut_to') || undefined,
+ shortcut_to_path: $element.attr('data-shortcut_to_path') || undefined,
+ size: $element.attr('data-size') || undefined,
+ type: $element.attr('data-type') || undefined,
+ modified: $element.attr('data-modified') || undefined,
+ metadata: parseItemMetadataForAI($element.attr('data-metadata')),
+ };
+ });
+};
+
+/**
+ * Ensures AI app iframe is available
+ * @returns {Promise
} AI app iframe or null
+ */
+const ensureAIAppIframe = async () => {
+ let $aiWindow = $(`.window[data-app="${AI_APP_NAME}"]`);
+ if ( $aiWindow.length === 0 ) {
+ try {
+ await launch_app({ name: AI_APP_NAME });
+ } catch ( error ) {
+ console.error('Failed to launch AI app.', error);
+ return null;
+ }
+ $aiWindow = $(`.window[data-app="${AI_APP_NAME}"]`);
+ }
+
+ if ( $aiWindow.length === 0 ) {
+ return null;
+ }
+
+ $aiWindow.makeWindowVisible();
+ const iframe = $aiWindow.find('.window-app-iframe').get(0);
+ return iframe ?? null;
+};
+
+/**
+ * Sends selection to AI app
+ * @param {jQuery} $elements - jQuery collection of elements
+ */
+const sendSelectionToAIApp = async ($elements) => {
+ const items = buildAIPayloadFromItems($elements);
+ if ( items.length === 0 ) {
+ return;
+ }
+
+ const aiIframe = await ensureAIAppIframe();
+ if ( !aiIframe || !aiIframe.contentWindow ) {
+ await UIAlert({
+ message: i18n('ai_app_unavailable'),
+ });
+ return;
+ }
+
+ aiIframe.contentWindow.postMessage({
+ msg: 'ai:openFsEntries',
+ items,
+ source: 'desktop-context-menu',
+ }, '*');
+};
+
+/**
+ * Generates context menu items for file/folder operations
+ *
+ * @param {Object} options - Configuration options
+ * @param {HTMLElement} options.element - The DOM element representing the file/folder
+ * @param {Object} options.fsentry - File system entry data (uid, path, name, is_dir, etc.)
+ * @param {boolean} options.is_trash - Whether this is the trash folder
+ * @param {boolean} options.is_trashed - Whether item is in trash
+ * @param {Array} options.suggested_apps - Optional pre-loaded suggested apps
+ * @param {string} options.associated_app_name - Optional associated app
+ * @param {Function} options.onOpen - Optional custom open handler (used by Dashboard)
+ * @returns {Promise} Array of context menu items
+ */
+const generate_file_context_menu = async function (options) {
+ options = options || {};
+
+ const el_item = options.element;
+ const fsentry = options.fsentry || {};
+ const is_trash = options.is_trash ?? false;
+ const is_trashed = options.is_trashed ?? false;
+ const is_worker = options.is_worker ?? false;
+ const onOpen = options.onOpen;
+
+ const is_shared_with_me = (fsentry.path !== `/${window.user.username}` && !fsentry.path.startsWith(`/${window.user.username}/`));
+
+ let menu_items = [];
+
+ // -------------------------------------------
+ // Open
+ // -------------------------------------------
+ if ( ! is_trashed ) {
+ menu_items.push({
+ html: i18n('open'),
+ onClick: () => {
+ if ( onOpen ) {
+ onOpen(el_item, fsentry);
+ } else {
+ open_item({ item: el_item });
+ }
+ },
+ });
+
+ // -------------------------------------------
+ // Separator
+ // -------------------------------------------
+ if ( options.associated_app_name || is_trash ) {
+ menu_items.push('-');
+ }
+ }
+
+ // -------------------------------------------
+ // Open With
+ // -------------------------------------------
+ if ( !is_trashed && !is_trash && (options.associated_app_name === null || options.associated_app_name === undefined) ) {
+ const openWithItems = await generateOpenWithItems(el_item, fsentry, options.suggested_apps);
+ menu_items.push({
+ html: i18n('open_with'),
+ items: openWithItems,
+ });
+
+ menu_items.push('-');
+ }
+
+ // -------------------------------------------
+ // Open in New Window
+ // (only if the item is on a window)
+ // -------------------------------------------
+ if ( $(el_item).closest('.window-body').length > 0 && fsentry.is_dir ) {
+ menu_items.push({
+ html: i18n('open_in_new_window'),
+ onClick: function () {
+ if ( fsentry.is_dir ) {
+ open_item({ item: el_item, new_window: true });
+ }
+ },
+ });
+
+ // -------------------------------------------
+ // Separator
+ // -------------------------------------------
+ if ( !is_trash && !is_trashed && fsentry.is_dir ) {
+ menu_items.push('-');
+ }
+ }
+
+ // -------------------------------------------
+ // Share With…
+ // -------------------------------------------
+ if ( !is_trashed && !is_trash ) {
+ menu_items.push({
+ html: i18n('Share With…'),
+ onClick: async function () {
+ if ( window.user.is_temp &&
+ !await UIWindowSaveAccount({
+ send_confirmation_code: true,
+ message: 'Please create an account to proceed.',
+ window_options: {
+ backdrop: true,
+ close_on_backdrop_click: false,
+ },
+ }) ) {
+ return;
+ }
+ else if ( !window.user.email_confirmed && !await UIWindowEmailConfirmationRequired() ) {
+ return;
+ }
+
+ const icon = $(el_item).find('.icon img').attr('src') || $(el_item).find('img').attr('src');
+ UIWindowShare([{
+ uid: $(el_item).attr('data-uid'),
+ path: $(el_item).attr('data-path'),
+ name: $(el_item).attr('data-name'),
+ icon: icon,
+ }]);
+ },
+ });
+
+ // -------------------------------------------
+ // Open in AI
+ // -------------------------------------------
+ menu_items.push({
+ html: i18n('open_in_ai'),
+ onClick: async function () {
+ await sendSelectionToAIApp($(el_item));
+ },
+ });
+ }
+
+ // -------------------------------------------
+ // Publish As Website
+ // -------------------------------------------
+ if ( !is_trashed && !is_trash && fsentry.is_dir ) {
+ menu_items.push({
+ html: i18n('publish_as_website'),
+ disabled: !fsentry.is_dir || fsentry.has_website,
+ onClick: async function () {
+ if ( window.require_email_verification_to_publish_website ) {
+ if ( window.user.is_temp &&
+ !await UIWindowSaveAccount({
+ send_confirmation_code: true,
+ message: 'Please create an account to proceed.',
+ window_options: {
+ backdrop: true,
+ close_on_backdrop_click: false,
+ },
+ }) ) {
+ return;
+ }
+ else if ( !window.user.email_confirmed && !await UIWindowEmailConfirmationRequired() ) {
+ return;
+ }
+ }
+ UIWindowPublishWebsite(fsentry.uid, $(el_item).attr('data-name'), $(el_item).attr('data-path'));
+ },
+ });
+ }
+
+ // -------------------------------------------
+ // Publish as Worker
+ // -------------------------------------------
+ if ( !is_trashed && !is_trash && !fsentry.is_dir && $(el_item).attr('data-name').toLowerCase().endsWith('.js') ) {
+ menu_items.push({
+ html: i18n('publish_as_serverless_worker'),
+ disabled: is_worker,
+ onClick: async function () {
+ if ( window.user.is_temp &&
+ !await UIWindowSaveAccount({
+ send_confirmation_code: true,
+ message: 'Please create an account to proceed.',
+ window_options: {
+ backdrop: true,
+ close_on_backdrop_click: false,
+ },
+ }) ) {
+ return;
+ }
+ else if ( !window.user.email_confirmed && !await UIWindowEmailConfirmationRequired() ) {
+ return;
+ }
+
+ UIWindowPublishWorker(fsentry.uid, $(el_item).attr('data-name'), $(el_item).attr('data-path'));
+ },
+ });
+ }
+
+ // -------------------------------------------
+ // Deploy As App
+ // -------------------------------------------
+ if ( !is_trashed && !is_trash && fsentry.is_dir ) {
+ menu_items.push({
+ html: i18n('deploy_as_app'),
+ disabled: !fsentry.is_dir,
+ onClick: async function () {
+ launch_app({
+ name: 'dev-center',
+ file_path: $(el_item).attr('data-path'),
+ file_uid: $(el_item).attr('data-uid'),
+ params: {
+ source_path: fsentry.path,
+ },
+ });
+ },
+ });
+
+ menu_items.push('-');
+ }
+
+ // -------------------------------------------
+ // Empty Trash
+ // -------------------------------------------
+ if ( is_trash ) {
+ menu_items.push({
+ html: i18n('empty_trash'),
+ onClick: async function () {
+ window.empty_trash();
+ },
+ });
+ }
+
+ // -------------------------------------------
+ // Download
+ // -------------------------------------------
+ if ( !is_trash && !is_trashed && (options.associated_app_name === null || options.associated_app_name === undefined) ) {
+ menu_items.push({
+ html: i18n('download'),
+ disabled: fsentry.is_dir && !window.feature_flags.download_directory,
+ onClick: async function () {
+ if ( fsentry.is_dir ) {
+ window.zipItems(el_item, path.dirname($(el_item).attr('data-path')), true);
+ }
+ else {
+ window.trigger_download([fsentry.path]);
+ }
+ },
+ });
+ }
+
+ // -------------------------------------------
+ // Set as Wallpaper
+ // -------------------------------------------
+ const mime_type = mime.getType($(el_item).attr('data-name')) ?? 'application/octet-stream';
+ if ( !window.dashboard_object && !is_trashed && !is_trash && !fsentry.is_dir && mime_type.startsWith('image/') ) {
+ menu_items.push({
+ html: i18n('set_as_background'),
+ onClick: async function () {
+ const read_url = await puter.fs.sign(undefined, { uid: $(el_item).attr('data-uid'), action: 'read' });
+ window.set_desktop_background({
+ url: read_url.items.read_url,
+ fit: window.desktop_bg_fit,
+ });
+ try {
+ $.ajax({
+ url: `${window.api_origin}/set-desktop-bg`,
+ type: 'POST',
+ data: JSON.stringify({
+ url: window.desktop_bg_url,
+ color: window.desktop_bg_color,
+ fit: window.desktop_bg_fit,
+ }),
+ async: true,
+ contentType: 'application/json',
+ headers: {
+ 'Authorization': `Bearer ${window.auth_token}`,
+ },
+ statusCode: {
+ 401: function () {
+ window.logout();
+ },
+ },
+ });
+ } catch ( err ) {
+ // Ignore
+ }
+ },
+ });
+ }
+
+ // -------------------------------------------
+ // Zip
+ // -------------------------------------------
+ if ( !is_trash && !is_trashed && !$(el_item).attr('data-path').endsWith('.zip') ) {
+ menu_items.push({
+ html: i18n('zip'),
+ onClick: function () {
+ window.zipItems(el_item, path.dirname($(el_item).attr('data-path')), false);
+ },
+ });
+ }
+
+ // -------------------------------------------
+ // Unzip
+ // -------------------------------------------
+ if ( !is_trash && !is_trashed && $(el_item).attr('data-path').endsWith('.zip') ) {
+ menu_items.push({
+ html: i18n('unzip'),
+ onClick: async function () {
+ let filePath = $(el_item).attr('data-path');
+ window.unzipItem(filePath);
+ },
+ });
+ }
+
+ // -------------------------------------------
+ // Tar
+ // -------------------------------------------
+ if ( !is_trash && !is_trashed && !$(el_item).attr('data-path').endsWith('.tar') ) {
+ menu_items.push({
+ html: i18n('tar'),
+ onClick: function () {
+ window.tarItems(el_item, path.dirname($(el_item).attr('data-path')), false);
+ },
+ });
+ }
+
+ // -------------------------------------------
+ // Untar
+ // -------------------------------------------
+ if ( !is_trash && !is_trashed && $(el_item).attr('data-path').endsWith('.tar') ) {
+ menu_items.push({
+ html: i18n('untar'),
+ onClick: async function () {
+ let filePath = $(el_item).attr('data-path');
+ window.untarItem(filePath);
+ },
+ });
+ }
+
+ // -------------------------------------------
+ // Restore
+ // -------------------------------------------
+ if ( is_trashed ) {
+ menu_items.push({
+ html: i18n('restore'),
+ onClick: async function () {
+ await options.onRestore(el_item);
+ },
+ });
+ }
+
+ // -------------------------------------------
+ // Separator
+ // -------------------------------------------
+ if ( !is_trash && (options.associated_app_name === null || options.associated_app_name === undefined) ) {
+ menu_items.push('-');
+ }
+
+ // -------------------------------------------
+ // Cut
+ // -------------------------------------------
+ if ( $(el_item).attr('data-immutable') === '0' && !is_shared_with_me ) {
+ menu_items.push({
+ html: i18n('cut'),
+ onClick: function () {
+ window.clipboard_op = 'move';
+ window.clipboard = [fsentry.path];
+ },
+ });
+ }
+
+ // -------------------------------------------
+ // Copy
+ // -------------------------------------------
+ if ( !is_trashed && !is_trash ) {
+ menu_items.push({
+ html: i18n('copy'),
+ onClick: function () {
+ window.clipboard_op = 'copy';
+ window.clipboard = [{ path: fsentry.path }];
+ },
+ });
+ }
+
+ // -------------------------------------------
+ // Paste Into Folder
+ // -------------------------------------------
+ if ( $(el_item).attr('data-is_dir') === '1' && !is_trashed && !is_trash ) {
+ menu_items.push({
+ html: i18n('paste_into_folder'),
+ disabled: window.clipboard.length > 0 ? false : true,
+ onClick: function () {
+ if ( window.clipboard_op === 'copy' ) {
+ window.copy_clipboard_items($(el_item).attr('data-path'), null);
+ }
+ else if ( window.clipboard_op === 'move' ) {
+ window.move_clipboard_items(null, $(el_item).attr('data-path'));
+ }
+ },
+ });
+ }
+
+ // -------------------------------------------
+ // Separator
+ // -------------------------------------------
+ if ( $(el_item).attr('data-immutable') === '0' && !is_trash ) {
+ menu_items.push('-');
+ }
+
+ // -------------------------------------------
+ // Create Shortcut
+ // -------------------------------------------
+ if ( !is_trashed && window.feature_flags.create_shortcut ) {
+ menu_items.push({
+ html: is_shared_with_me ? i18n('create_desktop_shortcut') : i18n('create_shortcut'),
+ onClick: async function () {
+ let base_dir = path.dirname($(el_item).attr('data-path'));
+ // Trash on Desktop is a special case
+ if ( $(el_item).attr('data-path') && $(el_item).closest('.item-container').attr('data-path') === window.desktop_path ) {
+ base_dir = window.desktop_path;
+ }
+
+ if ( is_shared_with_me ) base_dir = window.desktop_path;
+
+ window.create_shortcut(path.basename($(el_item).attr('data-path')),
+ fsentry.is_dir,
+ base_dir,
+ null, // appendTo - will be determined by create_shortcut
+ fsentry.shortcut_to === '' ? fsentry.uid : fsentry.shortcut_to,
+ fsentry.shortcut_to_path === '' ? fsentry.path : fsentry.shortcut_to_path);
+ },
+ });
+ }
+
+ // -------------------------------------------
+ // Delete
+ // -------------------------------------------
+ if ( $(el_item).attr('data-immutable') === '0' && !is_trashed && !is_shared_with_me ) {
+ menu_items.push({
+ html: i18n('delete'),
+ onClick: async function () {
+ await window.move_items([el_item], window.trash_path);
+ },
+ });
+ }
+
+ // -------------------------------------------
+ // Delete Permanently
+ // -------------------------------------------
+ if ( is_trashed ) {
+ menu_items.push({
+ html: i18n('delete_permanently'),
+ onClick: async function () {
+ const alert_resp = await UIAlert({
+ message: i18n('confirm_delete_single_item'),
+ buttons: [
+ {
+ label: i18n('delete'),
+ type: 'primary',
+ },
+ {
+ label: i18n('cancel'),
+ },
+ ],
+ });
+
+ if ( (alert_resp) === 'Delete' ) {
+ await window.delete_item(el_item);
+ // check if trash is empty
+ const trash = await puter.fs.stat({ path: window.trash_path, consistency: 'eventual' });
+ // update other clients
+ if ( window.socket ) {
+ window.socket.emit('trash.is_empty', { is_empty: trash.is_empty });
+ }
+ // update this client
+ if ( trash.is_empty ) {
+ $(`.item[data-path="${window.trash_path}" i], .item[data-shortcut_to_path="${window.trash_path}" i]`).find('.item-icon > img').attr('src', window.icons['trash.svg']);
+ $(`.window[data-path="${window.trash_path}"]`).find('.window-head-icon').attr('src', window.icons['trash.svg']);
+ }
+ }
+ },
+ });
+ }
+
+ // -------------------------------------------
+ // Rename
+ // -------------------------------------------
+ if ( $(el_item).attr('data-immutable') === '0' && !is_trashed && !is_trash ) {
+ menu_items.push({
+ html: i18n('rename'),
+ onClick: function () {
+ window.activate_item_name_editor(el_item);
+ },
+ });
+ }
+
+ // -------------------------------------------
+ // Separator
+ // -------------------------------------------
+ menu_items.push('-');
+
+ // -------------------------------------------
+ // Properties
+ // -------------------------------------------
+ menu_items.push({
+ html: i18n('properties'),
+ onClick: function () {
+ let window_height = 500;
+ let window_width = 450;
+
+ let left = $(el_item).position().left + $(el_item).width();
+ left = left > (window.innerWidth - window_width) ? (window.innerWidth - window_width) : left;
+
+ let top = $(el_item).position().top + $(el_item).height();
+ top = top > (window.innerHeight - (window_height + window.taskbar_height + window.toolbar_height)) ? (window.innerHeight - (window_height + window.taskbar_height + window.toolbar_height)) : top;
+
+ UIWindowItemProperties($(el_item).attr('data-name'),
+ $(el_item).attr('data-path'),
+ $(el_item).attr('data-uid'),
+ left,
+ top,
+ window_width,
+ window_height);
+ },
+ });
+
+ return menu_items;
+};
+
+/**
+ * Generates "Open With" menu items for a file
+ *
+ * @param {HTMLElement} el_item - The DOM element representing the file
+ * @param {Object} fsentry - File system entry data
+ * @param {Array} suggested_apps - Optional pre-loaded suggested apps
+ * @returns {Promise} Array of menu items for "Open With" submenu
+ */
+async function generateOpenWithItems (el_item, fsentry, suggested_apps) {
+ let items = [];
+
+ // Try to find suitable apps if not provided
+ if ( !suggested_apps || suggested_apps.length === 0 ) {
+ const suitable_apps = await window.suggest_apps_for_fsentry({
+ uid: fsentry.uid,
+ path: fsentry.path,
+ });
+ if ( suitable_apps && suitable_apps.length > 0 ) {
+ suggested_apps = suitable_apps;
+ }
+ }
+
+ if ( suggested_apps && suggested_apps.length > 0 ) {
+ for ( let index = 0; index < suggested_apps.length; index++ ) {
+ const suggested_app = suggested_apps[index];
+ if ( ! suggested_app ) {
+ console.warn('suggested_app is null', suggested_apps, index);
+ continue;
+ }
+ items.push({
+ html: suggested_app.title,
+ icon: `
`,
+ onClick: async function () {
+ var extension = path.extname($(el_item).attr('data-path')).toLowerCase();
+ if (
+ window.user_preferences[`default_apps${extension}`] !== suggested_app.name
+ &&
+ (
+ (!window.user_preferences[`default_apps${extension}`] && index > 0)
+ ||
+ (window.user_preferences[`default_apps${extension}`])
+ )
+ ) {
+ const alert_resp = await UIAlert({
+ message: `${i18n('change_always_open_with')} ${html_encode(suggested_app.title)}?`,
+ body_icon: suggested_app.icon,
+ buttons: [
+ {
+ label: i18n('yes'),
+ type: 'primary',
+ value: 'yes',
+ },
+ {
+ label: i18n('no'),
+ },
+ ],
+ });
+ if ( (alert_resp) === 'yes' ) {
+ window.user_preferences[`default_apps${extension}`] = suggested_app.name;
+ window.mutate_user_preferences(window.user_preferences);
+ }
+ }
+ launch_app({
+ name: suggested_app.name,
+ file_path: $(el_item).attr('data-path'),
+ window_title: $(el_item).attr('data-name'),
+ file_uid: $(el_item).attr('data-uid'),
+ });
+ },
+ });
+ }
+ } else {
+ items.push({
+ html: i18n('no_suitable_apps_found'),
+ disabled: true,
+ });
+ }
+
+ return items;
+}
+
+export default generate_file_context_menu;
diff --git a/src/gui/src/helpers/get_html_element_from_options.js b/src/gui/src/helpers/get_html_element_from_options.js
index be72a3b91..542d6d035 100644
--- a/src/gui/src/helpers/get_html_element_from_options.js
+++ b/src/gui/src/helpers/get_html_element_from_options.js
@@ -36,6 +36,15 @@ const get_html_element_from_options = async function (options) {
options.immutable = (options.immutable === false || options.immutable === 0 || options.immutable === undefined ? 0 : 1);
options.sort_container_after_append = (options.sort_container_after_append !== undefined ? options.sort_container_after_append : false);
const is_shared_with_me = (options.path !== `/${window.user.username}` && !options.path.startsWith(`/${window.user.username}/`));
+ let worker_url;
+ let is_worker;
+ if ( ! options.is_dir ) {
+ const stats = await puter.fs.stat({ path: options.path, returnWorkers: true });
+ is_worker = stats.workers !== undefined && stats.workers.length > 0;;
+ if ( is_worker ) {
+ worker_url = stats.workers[0].address;
+ }
+ }
let website_url = window.determine_website_url(options.path);
@@ -62,6 +71,8 @@ const get_html_element_from_options = async function (options) {
data-website_url = "${website_url ? html_encode(website_url) : ''}"
data-immutable="${options.immutable}"
data-is_shortcut = "${options.is_shortcut}"
+ data-is_worker = "${is_worker !== undefined ? 1 : 0}"
+ data-worker_url = "${is_worker !== undefined ? worker_url : 0}"
data-shortcut_to = "${html_encode(options.shortcut_to)}"
data-shortcut_to_path = "${html_encode(options.shortcut_to_path)}"
data-sortable = "${options.sortable ?? 'true'}"
@@ -137,7 +148,12 @@ const get_html_element_from_options = async function (options) {
data-item-id="${item_id}"
title="Shortcut"
>`;
-
+ // worker badge
+ h += `
`;
h += '';
// name
diff --git a/src/gui/src/helpers/new_context_menu_item.js b/src/gui/src/helpers/new_context_menu_item.js
index 9ad71dd8c..bb5cf1389 100644
--- a/src/gui/src/helpers/new_context_menu_item.js
+++ b/src/gui/src/helpers/new_context_menu_item.js
@@ -170,7 +170,7 @@ router.get('/', ({request}) => {
return 'Hello World'; // returns a string
});
router.get('/api/hello', ({request}) => {
- return {'msg': 'hello'}; // returns a JSON object
+ return {'msg': 'hello'}; // returns a JSON object
});
router.get('/*page', ({request, params}) => {
return new Response(\`Page \${params.page} not found\`, {status: 404});
diff --git a/src/gui/src/i18n/translations/en.js b/src/gui/src/i18n/translations/en.js
index 016acc3c3..71c8c384b 100644
--- a/src/gui/src/i18n/translations/en.js
+++ b/src/gui/src/i18n/translations/en.js
@@ -321,6 +321,7 @@ const en = {
taskmgr_header_type: 'Type',
terms: 'Terms',
text_document: 'Text document',
+ toggle_view: 'Toggle view',
'toolbar.enter_fullscreen': 'Enter Full Screen',
'toolbar.github': 'GitHub',
'toolbar.refer': 'Refer',
diff --git a/src/gui/src/icons/worker.svg b/src/gui/src/icons/worker.svg
new file mode 100644
index 000000000..9dfe587ca
--- /dev/null
+++ b/src/gui/src/icons/worker.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/gui/src/initgui.js b/src/gui/src/initgui.js
index e2d62a768..aba48a669 100644
--- a/src/gui/src/initgui.js
+++ b/src/gui/src/initgui.js
@@ -156,8 +156,31 @@ if ( jQuery ) {
// are we in dashboard mode?
if ( window.location.pathname === '/dashboard' || window.location.pathname === '/dashboard/' ) {
window.is_dashboard_mode = true;
+ window.dashboard_initial_route = parseDashboardRoute();
}
+/**
+ * Parses the dashboard URL hash into a route object.
+ * Hash format: #files/username/Documents or #usage or #account etc.
+ * @returns {{ tab: string, path: string|null }} Route object with tab name and optional file path
+ */
+function parseDashboardRoute () {
+ const hash = decodeURIComponent(window.location.hash.slice(1)); // Remove '#' and decode URL encoding
+ if ( ! hash ) return { tab: 'home', path: null };
+
+ const parts = hash.split('/').filter(Boolean); // ['files', 'username', 'Documents']
+ const tab = parts[0]; // 'files', 'usage', 'account', 'security'
+
+ if ( tab === 'files' && parts.length > 1 ) {
+ const filePath = `/${parts.slice(1).join('/')}`; // /username/Documents
+ return { tab: 'files', path: filePath };
+ }
+ return { tab: tab || 'home', path: null };
+}
+
+// Make parseDashboardRoute available globally for hashchange handler
+window.parseDashboardRoute = parseDashboardRoute;
+
/**
* Shows a Turnstile challenge modal for first-time temp user creation
* @param {Object} options - Configuration options