`;
@@ -1145,6 +1146,9 @@ async function UIDesktop(options){
// prepend toolbar to desktop
$(ht).insertBefore(el_desktop);
+ // initialize toolbar auto-hide functionality
+ toolbar_autohide.init();
+
// notification container
$('body').append(`
`);
diff --git a/src/gui/src/css/style.css b/src/gui/src/css/style.css
index 4c7d1637e2..a0d3df08bd 100644
--- a/src/gui/src/css/style.css
+++ b/src/gui/src/css/style.css
@@ -1736,19 +1736,46 @@ label {
}
.toolbar {
- float: right;
- width: 100%;
- background-color: #00000040;
+ position: fixed;
+ top: 10px;
+ left: 50%;
+ transform: translateX(-50%);
+ width: auto;
height: 30px;
- position: relative;
+ background-color: #00000040;
z-index: 999999;
box-sizing: border-box;
display: flex;
flex-direction: row;
- justify-content: flex-end;
+ justify-content: center;
align-content: center;
- flex-wrap: wrap;
- padding-right: 10px
+ align-items: center;
+ flex-wrap: nowrap;
+ padding: 0 15px;
+ gap: 5px;
+ border-radius: 20px;
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
+ transition: transform 0.3s ease-in-out, opacity 0.3s ease-in-out;
+}
+
+/* Toolbar hidden state */
+.toolbar.toolbar-hidden {
+ transform: translateX(-50%) translateY(-100%);
+ opacity: 0;
+ pointer-events: none;
+}
+
+/* Toolbar visible state (explicit for clarity) */
+.toolbar.toolbar-visible {
+ transform: translateX(-50%) translateY(0);
+ opacity: 1;
+ pointer-events: all;
+}
+
+/* Toolbar peek state (when mouse is near top) */
+.toolbar.toolbar-peek {
+ transform: translateX(-50%) translateY(0);
+ opacity: 0.6;
}
.show-desktop-btn {
@@ -4144,6 +4171,63 @@ fieldset[name=number-code] {
color: #c98900;
}
+/* Toggle Switch */
+.switch {
+ position: relative;
+ display: inline-block;
+ width: 44px;
+ height: 24px;
+ flex-shrink: 0;
+}
+
+.switch input {
+ opacity: 0;
+ width: 0;
+ height: 0;
+}
+
+.slider {
+ position: absolute;
+ cursor: pointer;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background-color: #ccc;
+ transition: 0.3s;
+}
+
+.slider:before {
+ position: absolute;
+ content: "";
+ height: 18px;
+ width: 18px;
+ left: 3px;
+ bottom: 3px;
+ background-color: white;
+ transition: 0.3s;
+}
+
+input:checked + .slider {
+ background-color: #2196F3;
+}
+
+input:focus + .slider {
+ box-shadow: 0 0 1px #2196F3;
+}
+
+input:checked + .slider:before {
+ transform: translateX(20px);
+}
+
+.slider.round {
+ border-radius: 24px;
+}
+
+.slider.round:before {
+ border-radius: 50%;
+}
+
.error-message {
display: none;
color: rgb(215 2 2);
diff --git a/src/gui/src/globals.js b/src/gui/src/globals.js
index a6c0ad105b..b6c4e774fb 100644
--- a/src/gui/src/globals.js
+++ b/src/gui/src/globals.js
@@ -99,6 +99,7 @@ if (window.user_preferences === null) {
show_hidden_files: false,
language: navigator.language.split("-")[0] || navigator.userLanguage || 'en',
clock_visible: 'auto',
+ toolbar_autohide: false, // Add this new preference
}
}
diff --git a/src/gui/src/helpers/toolbar_autohide.js b/src/gui/src/helpers/toolbar_autohide.js
new file mode 100644
index 0000000000..2e2cd5cd58
--- /dev/null
+++ b/src/gui/src/helpers/toolbar_autohide.js
@@ -0,0 +1,220 @@
+/**
+ * Copyright (C) 2024 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
.
+ */
+
+class ToolbarAutoHide {
+ constructor() {
+ this.hideTimeout = null;
+ this.hideDelay = 2000; // 2 seconds
+ this.proximityThreshold = 50; // pixels from top
+ this.isHidden = false;
+ this.isEnabled = false;
+ this.$toolbar = null;
+ this.lastMouseY = 0;
+ this.isInitialized = false;
+ }
+
+ init() {
+ if (this.isInitialized) return;
+
+ this.$toolbar = $('.toolbar');
+
+ if (this.$toolbar.length === 0) {
+ console.warn('Toolbar not found, auto-hide initialization skipped');
+ return;
+ }
+
+ this.isInitialized = true;
+
+ // First, check localStorage for initial state
+ const localPref = window.user_preferences?.toolbar_autohide ?? false;
+ this.isEnabled = localPref;
+
+ // Then load from KV store to get the authoritative state
+ puter.kv.get('user_preferences.toolbar_autohide').then((val) => {
+ const kvEnabled = !!val;
+
+ // If KV store has a different value, use that
+ if (kvEnabled !== this.isEnabled) {
+ this.isEnabled = kvEnabled;
+
+ // Update localStorage to match
+ if (window.user_preferences) {
+ window.user_preferences.toolbar_autohide = kvEnabled;
+ localStorage.setItem('user_preferences', JSON.stringify(window.user_preferences));
+ }
+ }
+
+ // Apply the state
+ if (this.isEnabled) {
+ this.enable();
+ } else {
+ // Ensure toolbar is visible by default
+ this.$toolbar.addClass('toolbar-visible');
+ }
+ }).catch((err) => {
+ console.warn('Could not load toolbar autohide preference from KV store:', err);
+ // Fall back to localStorage value
+ if (this.isEnabled) {
+ this.enable();
+ } else {
+ this.$toolbar.addClass('toolbar-visible');
+ }
+ });
+
+ // Add event listeners for toolbar interaction
+ this.$toolbar.on('mouseenter', () => this.onMouseEnterToolbar());
+ this.$toolbar.on('mouseleave', () => this.onMouseLeaveToolbar());
+
+ // Prevent hiding when interacting with toolbar buttons
+ this.$toolbar.on('click', () => {
+ this.clearHideTimer();
+ this.show();
+ });
+ }
+
+ enable() {
+ this.isEnabled = true;
+ this.$toolbar.addClass('toolbar-autohide-enabled');
+ this.startHideTimer();
+ }
+
+ disable() {
+ this.isEnabled = false;
+ this.show();
+ this.clearHideTimer();
+ this.$toolbar.removeClass('toolbar-autohide-enabled');
+ }
+
+ toggle() {
+ if (this.isEnabled) {
+ this.disable();
+ } else {
+ this.enable();
+ }
+
+ // Save preference
+ window.mutate_user_preferences({
+ toolbar_autohide: this.isEnabled
+ });
+ }
+
+ startHideTimer() {
+ if (!this.isEnabled) return;
+
+ this.clearHideTimer();
+ this.hideTimeout = setTimeout(() => {
+ this.hide();
+ }, this.hideDelay);
+ }
+
+ clearHideTimer() {
+ if (this.hideTimeout) {
+ clearTimeout(this.hideTimeout);
+ this.hideTimeout = null;
+ }
+ }
+
+ hide() {
+ if (!this.isEnabled || this.isHidden) return;
+
+ this.isHidden = true;
+ this.$toolbar.removeClass('toolbar-visible toolbar-peek').addClass('toolbar-hidden');
+
+ // Adjust layout
+ this.updateLayout(true);
+ }
+
+ show() {
+ if (!this.isHidden && !this.$toolbar.hasClass('toolbar-peek')) return;
+
+ this.isHidden = false;
+ this.$toolbar.removeClass('toolbar-hidden toolbar-peek').addClass('toolbar-visible');
+
+ // Adjust layout
+ this.updateLayout(false);
+
+ // Restart hide timer
+ if (this.isEnabled) {
+ this.startHideTimer();
+ }
+ }
+
+ peek() {
+ if (!this.isEnabled || !this.isHidden) return;
+
+ this.$toolbar.removeClass('toolbar-hidden').addClass('toolbar-peek');
+ }
+
+ updateLayout(isHidden) {
+ const toolbarHeight = isHidden ? 0 : window.toolbar_height;
+
+ // Update window container top position
+ $('.window-container').css('top', toolbarHeight);
+
+ // Update desktop height
+ const taskbarHeight = window.taskbar_height || 0;
+ $('.desktop').css('height', `calc(100vh - ${taskbarHeight + toolbarHeight}px)`);
+
+ // Update global desktop_height for calculations
+ window.desktop_height = window.innerHeight - taskbarHeight - toolbarHeight;
+ }
+
+ onMouseMove(mouseY) {
+ if (!this.isEnabled || !this.isInitialized) return;
+
+ this.lastMouseY = mouseY;
+
+ // Check if mouse is near top of screen
+ if (mouseY <= this.proximityThreshold) {
+ if (this.isHidden) {
+ this.peek();
+ }
+ this.clearHideTimer();
+ } else {
+ if (this.$toolbar.hasClass('toolbar-peek')) {
+ // Mouse moved away from top, hide the peek
+ this.$toolbar.removeClass('toolbar-peek').addClass('toolbar-hidden');
+ } else if (!this.isHidden) {
+ // Restart hide timer when mouse moves away
+ this.startHideTimer();
+ }
+ }
+ }
+
+ onMouseEnterToolbar() {
+ if (!this.isEnabled) return;
+
+ this.clearHideTimer();
+ this.show();
+ }
+
+ onMouseLeaveToolbar() {
+ if (!this.isEnabled) return;
+
+ // Only start timer if mouse is not near the top
+ if (this.lastMouseY > this.proximityThreshold) {
+ this.startHideTimer();
+ }
+ }
+}
+
+// Create singleton instance
+window.toolbar_autohide = new ToolbarAutoHide();
+
+export default window.toolbar_autohide;
\ No newline at end of file
diff --git a/src/gui/src/helpers/update_mouse_position.js b/src/gui/src/helpers/update_mouse_position.js
index 7b433180ee..4bdd33c17f 100644
--- a/src/gui/src/helpers/update_mouse_position.js
+++ b/src/gui/src/helpers/update_mouse_position.js
@@ -81,6 +81,14 @@ const update_mouse_position = function(x, y){
}
window.mouseover_item_container = active_ic;
+ // ============================================
+ // NEW: Toolbar Auto-Hide Integration
+ // ============================================
+ // Update toolbar auto-hide based on mouse position
+ if (window.toolbar_autohide) {
+ window.toolbar_autohide.onMouseMove(y);
+ }
+
}
export default update_mouse_position;
\ No newline at end of file
diff --git a/src/puter-js/src/modules/KV.js b/src/puter-js/src/modules/KV.js
index d49def1bb4..8512721d2b 100644
--- a/src/puter-js/src/modules/KV.js
+++ b/src/puter-js/src/modules/KV.js
@@ -10,6 +10,7 @@ const gui_cache_keys = [
'user_preferences.show_hidden_files',
'user_preferences.language',
'user_preferences.clock_visible',
+ 'user_preferences.toolbar_autohide',
'has_seen_welcome_window',
];