diff --git a/src/gui/src/UI/UIDesktop.js b/src/gui/src/UI/UIDesktop.js
index c9014c0e..2d41dc6e 100644
--- a/src/gui/src/UI/UIDesktop.js
+++ b/src/gui/src/UI/UIDesktop.js
@@ -563,6 +563,11 @@ async function UIDesktop(options){
}
})
+ // load window sidebar items from KV
+ puter.kv.get("sidebar_items").then(async (val) => {
+ window.sidebar_items = val;
+ })
+
// Get menubar style
puter.kv.get('menubar_style').then(async (val) => {
let value = val;
diff --git a/src/gui/src/UI/UIWindow.js b/src/gui/src/UI/UIWindow.js
index 14e013d8..26437f7b 100644
--- a/src/gui/src/UI/UIWindow.js
+++ b/src/gui/src/UI/UIWindow.js
@@ -265,12 +265,35 @@ async function UIWindow(options) {
>`;
// favorites
h += `
`;
- h += ``;
- h += ``;
- h += ``;
- h += ``;
- h += ``;
- h += ``;
+ // default items if sidebar_items is not set
+ if(!window.sidebar_items){
+ h += ``;
+ h += ``;
+ h += ``;
+ h += ``;
+ h += ``;
+ h += ``;
+ }else{
+ let items = JSON.parse(window.sidebar_items);
+ for(let item of items){
+ let icon;
+ if(item.path === window.home_path)
+ icon = window.icons['sidebar-folder-home.svg'];
+ else if(item.path === window.docs_path)
+ icon = window.icons['sidebar-folder-documents.svg'];
+ else if(item.path === window.public_path)
+ icon = window.icons['sidebar-folder-public.svg'];
+ else if(item.path === window.pictures_path)
+ icon = window.icons['sidebar-folder-pictures.svg'];
+ else if(item.path === window.desktop_path)
+ icon = window.icons['sidebar-folder-desktop.svg'];
+ else if(item.path === window.videos_path)
+ icon = window.icons['sidebar-folder-videos.svg'];
+ else
+ icon = window.icons['sidebar-folder.svg'];
+ h += ``;
+ }
+ }
h += ``;
}
@@ -434,6 +457,7 @@ async function UIWindow(options) {
// Append
$(el_body).append(h);
+
// disable_parent_window
if(options.disable_parent_window && options.parent_uuid !== null){
const $el_parent_window = $(`.window[data-element_uuid="${options.parent_uuid}"]`);
@@ -2369,6 +2393,70 @@ async function UIWindow(options) {
});
})
+ //--------------------------------------------------
+ // Sidebar sortable
+ //--------------------------------------------------
+ if(options.is_dir && !isMobile.phone){
+ loadSavedSidebarOrder(el_window);
+ const $sidebar = $(el_window).find('.window-sidebar');
+
+ $sidebar.sortable({
+ items: '.window-sidebar-item:not(.window-sidebar-title)', // More specific selector
+ connectWith: '.window-sidebar',
+ cursor: 'move',
+ axis: 'y',
+ distance: 5,
+ containment: 'parent',
+ placeholder: 'window-sidebar-item-placeholder',
+ tolerance: 'pointer',
+ helper: 'clone',
+ opacity: 0.8,
+
+ start: function(event, ui) {
+ // Add dragging class
+ ui.item.addClass('window-sidebar-item-dragging');
+
+ // Create placeholder styling
+ ui.placeholder.css({
+ 'height': ui.item.height(),
+ 'visibility': 'visible',
+ });
+ },
+
+ sort: function(event, ui) {
+ // Ensure the helper follows the cursor properly
+ ui.helper.css('pointer-events', 'none');
+ },
+
+ stop: function(event, ui) {
+ // Remove dragging class
+ ui.item.removeClass('window-sidebar-item-dragging');
+
+ // Get the new order
+ const newOrder = $sidebar.find('.window-sidebar-item').map(function() {
+ return {
+ path: $(this).attr('data-path'),
+ name: $(this).text().trim()
+ };
+ }).get();
+
+ // Save the new order
+ saveSidebarOrder(newOrder);
+ }
+ }).disableSelection(); // Prevent text selection while dragging
+
+ // Make the sortable operation more responsive
+ $sidebar.on('mousedown', '.window-sidebar-item', function(e) {
+ if (!$(this).hasClass('window-sidebar-title')) {
+ $(this).addClass('grabbing');
+ }
+ });
+
+ $sidebar.on('mouseup mouseleave', '.window-sidebar-item', function() {
+ $(this).removeClass('grabbing');
+ });
+ }
+
//set styles
$(el_window_body).css(options.body_css);
@@ -3575,4 +3663,50 @@ document.addEventListener('scroll', function (event) {
}
}, true);
+// Function to save sidebar order to user preferences
+async function saveSidebarOrder(order) {
+ try {
+ await puter.kv.set({
+ key: "sidebar_items",
+ value: JSON.stringify(order)
+ });
+
+ // Save to window object for quick access
+ window.sidebar_items = JSON.stringify(order);
+ } catch(err) {
+ console.error('Error saving sidebar order:', err);
+ }
+}
+
+// Function to load and apply saved sidebar order
+async function loadSavedSidebarOrder(el_window) {
+ setTimeout(async () => {
+ try {
+ // Load saved sidebar order
+ let savedOrder = window.sidebar_items
+
+ // If not found in window object, try to get from KV
+ if(!savedOrder){
+ savedOrder = await puter.kv.get("sidebar_items");
+ }
+
+ // If found, apply the order
+ if (savedOrder && savedOrder !== 'null' && savedOrder !== 'undefined') {
+ const order = JSON.parse(savedOrder);
+ const $sidebar = $(el_window).find('.window-sidebar');
+
+ // Reorder items according to saved order
+ order.forEach(item => {
+ const $item = $sidebar.find(`.window-sidebar-item[data-path="${item.path}"]`);
+ if ($item.length) {
+ $item.appendTo($sidebar);
+ }
+ });
+ }
+ } catch(err) {
+ console.error('Error loading sidebar order:', err);
+ }
+}, 1000);
+}
+
export default UIWindow;
diff --git a/src/gui/src/css/style.css b/src/gui/src/css/style.css
index d27c9856..4025a9a3 100644
--- a/src/gui/src/css/style.css
+++ b/src/gui/src/css/style.css
@@ -1242,7 +1242,7 @@ span.header-sort-icon img {
cursor: pointer;
}
-.window-sidebar-item {
+.window-sidebar-item, .window-sidebar-item.grabbing {
margin-bottom: 6px;
margin-top: 2px;
padding: 4px;
@@ -1262,6 +1262,34 @@ span.header-sort-icon img {
background-color: #fefeff;
}
+.window-sidebar-item.grabbing, .window-sidebar-item.ui-sortable-helper{
+ background-color: none !important;
+ height: 23px !important;
+ /* width: 100% !important; */
+}
+.window-sidebar-item-placeholder{
+ height: 27px !important;
+}
+.window-sidebar-item {
+ cursor: pointer;
+ user-select: none;
+}
+.window-sidebar-item:not(.window-sidebar-title):hover {
+ cursor: grab;
+}
+.window-sidebar-item.grabbing {
+ cursor: grabbing !important;
+}
+.window-sidebar-item-dragging {
+ background-color: #f5f5f5 !important;
+ opacity: 0.8;
+ cursor: grabbing;
+}
+.ui-sortable-helper {
+ background: white !important;
+ box-shadow: 0 2px 8px rgba(0,0,0,0.1) !important;
+}
+
.window-sidebar-item-icon {
width: 14px;
height: 14px;
diff --git a/src/gui/src/icons/sidebar-folder.svg b/src/gui/src/icons/sidebar-folder.svg
new file mode 100644
index 00000000..54b66cc7
--- /dev/null
+++ b/src/gui/src/icons/sidebar-folder.svg
@@ -0,0 +1,8 @@
+