diff --git a/src/gui/src/UI/Dashboard/TabApps.js b/src/gui/src/UI/Dashboard/TabApps.js
index b4e8831f6..0ce36f231 100644
--- a/src/gui/src/UI/Dashboard/TabApps.js
+++ b/src/gui/src/UI/Dashboard/TabApps.js
@@ -32,47 +32,77 @@ const APP_NAMES_NO_UNINSTALL = new Set([
* along with this program. If not, see .
*/
-function buildAppsGrid (apps) {
- if ( !apps || apps.length === 0 ) {
- let h = '
';
- h += '';
- h += '
No apps installed yet
';
- h += '
';
- return h;
+function buildTileHtml (app) {
+ let title = (app.title || app.name || '').trim();
+ let targetLink = '';
+
+ // External apps (not owned by a Puter user) can report an opaque app-… id
+ // as their title (uid === name === title); in that case show the hostname
+ // of index_url instead, and open the app's website (index_url) on click —
+ // matching the Home tab.
+ const appUid = app.uid || app.uuid;
+ if (
+ app.external &&
+ app.name === app.title &&
+ app.name === appUid &&
+ app.index_url
+ ) {
+ title = new URL(app.index_url).hostname;
+ targetLink = app.index_url;
}
- let h = '
';
- for ( const app of apps ) {
- let title = (app.title || app.name || '').trim();
- let targetLink = '';
+ const iconUrl = app.iconUrl || window.icons['app.svg'];
- // External apps (not owned by a Puter user) can report an opaque app-… id
- // as their title (uid === name === title); in that case show the hostname
- // of index_url instead, and open the app's website (index_url) on click —
- // matching the Home tab.
- const appUid = app.uid || app.uuid;
- if (
- app.external &&
- app.name === app.title &&
- app.name === appUid &&
- app.index_url
- ) {
- title = new URL(app.index_url).hostname;
- targetLink = app.index_url;
+ let h = `
`;
+ h += '
';
+ h += ``;
+ h += '
';
+ h += `${html_encode(title)}`;
+ h += '
';
+ return h;
+}
+
+function buildNoAppsHtml () {
+ let h = '
';
+ h += '';
+ h += '
No apps installed yet
';
+ h += '
';
+ return h;
+}
+
+// iOS-home-screen-style pager: fixed cols × rows pages in a horizontal
+// scroll-snap scroller, with page dots below and hover arrows for mouse users.
+function buildPagerHtml (apps, layout) {
+ const pageCount = Math.ceil(apps.length / layout.perPage);
+
+ let h = `
`;
+
+ h += '
';
+ for ( let p = 0; p < pageCount; p++ ) {
+ h += '
';
+ for ( const app of apps.slice(p * layout.perPage, (p + 1) * layout.perPage) ) {
+ h += buildTileHtml(app);
}
-
- const iconUrl = app.iconUrl || window.icons['app.svg'];
-
- h += `
`;
- h += '
';
- h += ``;
- h += '
';
- h += `${html_encode(title)}`;
h += '
';
}
+ h += '
';
+
+ h += '';
+ h += '';
+
+ h += `
`;
+ for ( let p = 0; p < pageCount; p++ ) {
+ h += ``;
+ }
+ h += '
';
+
h += '
';
return h;
}
@@ -113,15 +143,8 @@ function showUninstallModal ({ appName, appTitle, appUid, self, $el_window }) {
try {
await puter.perms.revokeApp(appUid, '*');
- // Remove from internal state
self._apps = self._apps.filter(a => a.name !== appName);
- // Remove tile from DOM
- $el_window.find(`.myapps-tile[data-app-name="${appName}"]`).remove();
- // Show empty state if no apps left
- if ( self._apps.length === 0 ) {
- const $container = $el_window.find('.myapps-container');
- $container.html(buildAppsGrid(null));
- }
+ self.renderApps($el_window, { preservePage: true });
} catch ( err ) {
console.error('Failed to uninstall app:', err);
}
@@ -131,12 +154,14 @@ function showUninstallModal ({ appName, appTitle, appUid, self, $el_window }) {
}
function revealWhenLoaded ($container) {
- const $grid = $container.find('.myapps-grid-loading');
- if ( $grid.length === 0 ) return;
+ const $pager = $container.find('.myapps-pager-loading');
+ if ( $pager.length === 0 ) return;
- const imgs = $grid.find('img').toArray();
+ // Only the first page's icons gate the fade-in; the other pages are
+ // offscreen and their icons can finish loading behind it.
+ const imgs = $pager.find('.myapps-page').first().find('img').toArray();
if ( imgs.length === 0 ) {
- $grid.removeClass('myapps-grid-loading');
+ $pager.removeClass('myapps-pager-loading');
return;
}
@@ -146,7 +171,7 @@ function revealWhenLoaded ($container) {
function onDone () {
loaded++;
if ( loaded >= total ) {
- $grid.removeClass('myapps-grid-loading');
+ $pager.removeClass('myapps-pager-loading');
}
}
@@ -166,6 +191,9 @@ const TabApps = {
icon: '',
_apps: null,
+ _layout: null,
+ _page: 0,
+ _pageCount: 0,
html () {
let h = '