let sortBy = 'created_at'; let sortDirection = 'desc'; window.websites = []; let search_query; window.create_website = async (name, directoryPath = null) => { let website; // Use provided directory path or default to the default website file const websiteDir = directoryPath || window.default_website_file; try { website = await puter.hosting.create(name, websiteDir); } catch ( error ) { puter.ui.alert(`Error creating website: ${error.error.message}`); } return website; }; window.refresh_websites_list = async (show_loading = false) => { if ( show_loading ) { puter.ui.showSpinner(); } // puter.hosting.list() returns an array of website objects window.websites = await puter.hosting.list(); // Get websites if ( window.activeTab === 'websites' && window.websites.length > 0 ) { $('.website-card').remove(); $('#no-websites-notice').hide(); $('#website-list').show(); for ( let i = 0; i < window.websites.length; i++ ) { const website = window.websites[i]; // append row to website-list-table $('#website-list-table > tbody').append(generate_website_card(website)); } } else { $('#no-websites-notice').show(); $('#website-list').hide(); } count_websites(); puter.ui.hideSpinner(); }; async function init_websites () { puter.hosting.list().then((websites) => { window.websites = websites; count_websites(); }); } $(document).on('click', '.create-a-website-btn', async function (e) { // Step 1: Show directory picker let selectedDirectory; try { selectedDirectory = await puter.ui.showDirectoryPicker(); } catch ( err ) { // User cancelled directory picker or there was an error console.log('Directory picker cancelled or error:', err); return; } // Step 2: Ask for website name if ( selectedDirectory && selectedDirectory.path ) { let name = await puter.ui.prompt('Please enter a name for your website:', 'my-awesome-website'); // Step 3: Create website with selected directory if ( name ) { await create_website(name, selectedDirectory.path); refresh_websites_list(); } } }); $(document).on('click', '.website-checkbox', function (e) { // was shift key pressed? if ( e.originalEvent && e.originalEvent.shiftKey ) { // select all checkboxes in range const currentIndex = $('.website-checkbox').index(this); const startIndex = Math.min(window.last_clicked_website_checkbox_index, currentIndex); const endIndex = Math.max(window.last_clicked_website_checkbox_index, currentIndex); // set all checkboxes in range to the same state as current checkbox for ( let i = startIndex; i <= endIndex; i++ ) { const checkbox = $('.website-checkbox').eq(i); checkbox.prop('checked', $(this).is(':checked')); // activate row if ( $(checkbox).is(':checked') ) { $(checkbox).closest('tr').addClass('active'); } else { $(checkbox).closest('tr').removeClass('active'); } } } // determine if select-all checkbox should be checked, indeterminate, or unchecked if ( $('.website-checkbox:checked').length === $('.website-checkbox').length ) { $('.select-all-websites').prop('indeterminate', false); $('.select-all-websites').prop('checked', true); } else if ( $('.website-checkbox:checked').length > 0 ) { $('.select-all-websites').prop('indeterminate', true); $('.select-all-websites').prop('checked', false); } else { $('.select-all-websites').prop('indeterminate', false); $('.select-all-websites').prop('checked', false); } // activate row if ( $(this).is(':checked') ) { $(this).closest('tr').addClass('active'); } else { $(this).closest('tr').removeClass('active'); } // enable delete button if at least one checkbox is checked if ( $('.website-checkbox:checked').length > 0 ) { $('.delete-websites-btn').removeClass('disabled'); } else { $('.delete-websites-btn').addClass('disabled'); } // store the index of the last clicked checkbox window.last_clicked_website_checkbox_index = $('.website-checkbox').index(this); }); $(document).on('change', '.select-all-websites', function (e) { if ( $(this).is(':checked') ) { $('.website-checkbox').prop('checked', true); $('.website-card').addClass('active'); $('.delete-websites-btn').removeClass('disabled'); } else { $('.website-checkbox').prop('checked', false); $('.website-card').removeClass('active'); $('.delete-websites-btn').addClass('disabled'); } }); $('.refresh-website-list').on('click', function (e) { puter.ui.showSpinner(); refresh_websites_list(); puter.ui.hideSpinner(); }); $('th.sort').on('click', function (e) { // determine what column to sort by const sortByColumn = $(this).attr('data-column'); // toggle sort direction if ( sortByColumn === sortBy ) { if ( sortDirection === 'asc' ) { sortDirection = 'desc'; } else { sortDirection = 'asc'; } } else { sortBy = sortByColumn; sortDirection = 'desc'; } // update arrow $('.sort-arrow').css('display', 'none'); $('#website-list-table').find('th').removeClass('sorted'); $(this).find(`.sort-arrow-${ sortDirection}`).css('display', 'inline'); $(this).addClass('sorted'); sort_websites(); }); function sort_websites () { let sorted_websites; // sort if ( sortDirection === 'asc' ) { sorted_websites = websites.sort((a, b) => { if ( sortBy === 'name' ) { return a.subdomain.localeCompare(b.subdomain); } else if ( sortBy === 'created_at' ) { return new Date(a[sortBy]) - new Date(b[sortBy]); } else if ( sortBy === 'user_count' || sortBy === 'open_count' ) { return a.stats[sortBy] - b.stats[sortBy]; } else if ( sortBy === 'root_dir' ) { const aRootDir = a.root_dir?.name || ''; const bRootDir = b.root_dir?.name || ''; return aRootDir.localeCompare(bRootDir); } else { return a[sortBy] > b[sortBy] ? 1 : -1; } }); } else { sorted_websites = websites.sort((a, b) => { if ( sortBy === 'name' ) { return b.subdomain.localeCompare(a.subdomain); } else if ( sortBy === 'created_at' ) { return new Date(b[sortBy]) - new Date(a[sortBy]); } else if ( sortBy === 'user_count' || sortBy === 'open_count' ) { return b.stats[sortBy] - a.stats[sortBy]; } else if ( sortBy === 'root_dir' ) { const aRootDir = a.root_dir?.name || ''; const bRootDir = b.root_dir?.name || ''; return bRootDir.localeCompare(aRootDir); } else { return b[sortBy] > a[sortBy] ? 1 : -1; } }); } // refresh website list $('.website-card').remove(); sorted_websites.forEach(website => { $('#website-list-table > tbody').append(generate_website_card(website)); }); count_websites(); // show websites that match search_query and hide websites that don't if ( search_query ) { // show websites that match search_query and hide websites that don't websites.forEach((website) => { if ( website.subdomain.toLowerCase().includes(search_query.toLowerCase()) ) { $(`.website-card[data-name="${html_encode(website.subdomain)}"]`).show(); } else { $(`.website-card[data-name="${html_encode(website.subdomain)}"]`).hide(); } }); } } function count_websites () { let count = window.websites.length; $('.website-count').html(count ? count : ''); return count; } function generate_website_card (website) { return `