diff --git a/src/gui/src/UI/Dashboard/TabApps.js b/src/gui/src/UI/Dashboard/TabApps.js index 6a3a0418b..5ba7e9981 100644 --- a/src/gui/src/UI/Dashboard/TabApps.js +++ b/src/gui/src/UI/Dashboard/TabApps.js @@ -58,14 +58,6 @@ const APP_CENTER_APP_NAME = 'app-center'; /** Puter's AI app builder, which generates an app from a description. */ const BUILDER_APP_NAME = 'builder'; -// -- App Center discovery under search -- -// When a search matches none of the user's own apps, the same query is put to -// the App Center catalogue, so "I don't have it" continues into "here it is" -// without leaving the tab. Shown only on the no-local-matches state — a query -// that found the user's apps already answered them. -const DISCOVER_PAGE_SIZE = 8; -const DISCOVER_DEBOUNCE_MS = 250; - // -- Drag-to-reorder tuning -- const DRAG_START_DISTANCE = 5; // px a pointer must travel before a drag begins const MODE_LONGPRESS_MS = 500; // hold on empty grid space before reorder mode engages @@ -844,11 +836,6 @@ const TabApps = { // their tiles (the mirror of _removedLocal). _arriving: null, _pendingInstalls: null, - // Latest App Center answer for the search box ({ query, items }), plus - // the debounce timer and a sequence guard against out-of-order responses. - _discover: null, - _discoverTimer: null, - _discoverSeq: 0, html () { let h = '
'; @@ -942,26 +929,6 @@ const TabApps = { $el_window.on('input', '.myapps-search', function () { self.updateSearchIcons($el_window); self.renderApps($el_window); - self._queueDiscover($el_window); - }); - - // Launch an App Center discovery straight from the search results. - // Distinct from '.myapps-tile': discovery tiles take no part in - // drag-reorder, folders, or the running-dot machinery. - const launchDiscovery = function () { - const name = this.dataset.appName; - if ( ! name || TabApps._launchingApps.has(name) ) return; - TabApps._launchingApps.add(name); - launch_app({ name, maximized: true }) - .catch(err => console.error(`Failed to launch ${name}:`, err)) - .finally(() => TabApps._launchingApps.delete(name)); - }; - $el_window.on('click', '.myapps-discover-tile', launchDiscovery); - $el_window.on('keydown', '.myapps-discover-tile', function (e) { - if ( e.key === 'Enter' || e.key === ' ' ) { - e.preventDefault(); - launchDiscovery.call(this); - } }); // Clear search on cross click @@ -1436,7 +1403,7 @@ const TabApps = { this._pageCount = Math.ceil(items.length / layout.perPage); this._page = Math.min(Math.floor(anchorIndex / layout.perPage), this._pageCount - 1); - $container.html((no_matches ? buildNoMatchesHtml() + this._discoverHtml(query) : '') + $container.html((no_matches ? buildNoMatchesHtml() : '') + buildPagerHtml(items, layout, instant)); // A tile mid-arrival (a deep-link landing installing its app — see // _spliceDeepLinkApp) stays parked invisible across re-renders; the @@ -1478,89 +1445,6 @@ const TabApps = { }, { passive: true }); }, - // -- App Center discovery under search -- - - /** - * The "From the App Center" strip for the no-local-matches state, from - * the cached catalogue answer. Empty until the fetch for exactly this - * query has landed — a stale strip would answer the wrong question. - */ - _discoverHtml (query) { - const discover = this._discover; - if ( ! discover || discover.query !== query || discover.items.length === 0 ) { - return ''; - } - let h = '
'; - h += '
From the App Center
'; - h += '
'; - for ( const item of discover.items ) { - h += `
`; - h += item.icon - ? `` - : '
'; - h += `${html_encode(item.title)}`; - h += '
'; - } - h += '
'; - return h; - }, - - /** Debounced catalogue lookup for the current search box contents. */ - _queueDiscover ($el_window) { - clearTimeout(this._discoverTimer); - const query = String($el_window.find('.myapps-search').val() || '').trim(); - if ( ! query ) { - this._discover = null; - return; - } - // Already answered — nothing to fetch, nothing to re-render. - if ( this._discover?.query === query ) return; - const self = this; - this._discoverTimer = setTimeout(async () => { - const seq = ++self._discoverSeq; - let items = []; - try { - const resp = await fetch( - `${window.api_origin}/marketplace/search?q=${encodeURIComponent(query)}&page=1&pageSize=${DISCOVER_PAGE_SIZE}`, - ); - if ( resp.ok ) { - const body = await resp.json(); - items = Array.isArray(body?.items) ? body.items : []; - } - } catch { - // Offline or the catalogue is down — the empty state simply - // stays as it was. - } - // A newer keystroke owns the strip now. - if ( seq !== self._discoverSeq ) return; - - // What the user already has isn't a discovery. - const owned = new Set(); - for ( const app of self._apps || [] ) { - if ( app.name ) owned.add(String(app.name).toLowerCase()); - if ( app.uid || app.uuid ) owned.add(String(app.uid || app.uuid).toLowerCase()); - } - self._discover = { - query, - items: items - .filter(item => item?.item_name - && ! owned.has(String(item.item_name).toLowerCase()) - && ! owned.has(String(item.item_id || '').toLowerCase())) - .map(item => ({ - name: String(item.item_name), - title: String(item.item_title || item.item_name), - icon: typeof item.icon_url === 'string' ? item.icon_url : null, - })), - }; - // Only the empty state renders the strip, so only re-render if - // the query in the box is still the one this answer belongs to. - const current = String($el_window.find('.myapps-search').val() || '').trim(); - if ( current === query ) { - self.renderApps($el_window, { preservePage: true, instant: true }); - } - }, DISCOVER_DEBOUNCE_MS); - }, - // Mark tiles whose app has a live window (visible OR minimized — both // are running) with the macOS-dock-style dot. A folder wears the dot when // anything inside it is running — the apps it hides are exactly the ones diff --git a/src/gui/src/css/dashboard.css b/src/gui/src/css/dashboard.css index 0bff7d247..74510112b 100644 --- a/src/gui/src/css/dashboard.css +++ b/src/gui/src/css/dashboard.css @@ -921,64 +921,6 @@ input.myapps-search:disabled { padding: 20px 0 18px; } -/* "From the App Center" strip under a search with no local matches. */ -.myapps-discover { - padding: 0 8px 16px; -} - -.myapps-discover-title { - font-size: 13px; - font-weight: 500; - color: var(--dashboard-text-tertiary); - margin-bottom: 10px; - text-align: center; -} - -.myapps-discover-row { - display: flex; - flex-wrap: wrap; - justify-content: center; - gap: 14px; -} - -.myapps-discover-tile { - display: flex; - flex-direction: column; - align-items: center; - gap: 6px; - width: 96px; - padding: 10px 4px; - border-radius: 10px; - cursor: pointer; - text-align: center; -} - -.myapps-discover-tile:hover, -.myapps-discover-tile:focus-visible { - background: var(--dashboard-hover-bg, rgba(127, 127, 127, 0.12)); -} - -.myapps-discover-tile img, -.myapps-discover-tile-blank { - width: 48px; - height: 48px; - border-radius: 10px; - object-fit: cover; -} - -.myapps-discover-tile-blank { - background: rgba(127, 127, 127, 0.15); -} - -.myapps-discover-tile span { - font-size: 12px; - color: var(--dashboard-text-secondary); - max-width: 100%; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - /* iOS-style pager: pages snap horizontally, dots below, hover arrows */ .myapps-pager {