feat(marketplace): match the App Center sidebar + simplify the app name/blurb
Website sidebar now mirrors the App Center (sidebar.css): a 220px full-height column, full-width category rows with bottom-border separators and the real per-category icons (bundled into the site by the install hook from the live frontend), and the same search box — instead of the rounded pills + generic circles + count badges it had. Marketplace app copy shortened to peer style: description 'App Catalog & Registry', one-sentence long description. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: librelad <librelad@digitalangels.vip>
This commit is contained in:
parent
8e08e85db3
commit
c0133cd437
@ -31,8 +31,8 @@ CFG_MARKETPLACE_DEV_ONLY=true
|
||||
#
|
||||
CFG_MARKETPLACE_CATEGORY="system"
|
||||
CFG_MARKETPLACE_TITLE="LibrePortal Marketplace"
|
||||
CFG_MARKETPLACE_DESCRIPTION="Host your own app catalog and registry"
|
||||
CFG_MARKETPLACE_LONG_DESCRIPTION="Serves a signed LibrePortal artifact channel (apps, hotfixes, releases) plus a browsable marketplace website over it. The official marketplace runs this exact app; point any box's CFG_RELEASE_BASE_URL at your instance to use it"
|
||||
CFG_MARKETPLACE_DESCRIPTION="App Catalog & Registry"
|
||||
CFG_MARKETPLACE_LONG_DESCRIPTION="Host your own signed LibrePortal app catalog — browse and add apps from your own instance."
|
||||
CFG_MARKETPLACE_URL="https://github.com/Webstar/LibrePortal"
|
||||
CFG_MARKETPLACE_ACTIONS="configure|install|restart|shutdown|uninstall"
|
||||
#
|
||||
|
||||
@ -70,26 +70,33 @@
|
||||
.topbar .spacer { flex: 1; }
|
||||
.topbar .chip { font-size: 0.8rem; color: var(--text-muted); border: 1px solid var(--border-color); padding: 5px 11px; border-radius: 999px; }
|
||||
|
||||
/* Layout: sidebar + main. */
|
||||
/* Layout: sidebar + main — matches the App Center (apps-layout.css /
|
||||
sidebar.css): a 220px full-height column, full-width category rows with
|
||||
bottom-border separators, and the same search box. */
|
||||
.apps-layout { display: flex; width: 100%; min-height: calc(100vh - 60px); }
|
||||
.sidebar-container { flex-shrink: 0; width: 240px; background: var(--sidebar-bg); border-right: 1px solid var(--border-color); }
|
||||
.sidebar { position: sticky; top: 60px; padding: 18px 12px; }
|
||||
.apps-search { position: relative; margin-bottom: 14px; }
|
||||
.sidebar-container {
|
||||
flex-shrink: 0; width: 220px; background: var(--sidebar-bg);
|
||||
border-right: 1px solid var(--sidebar-border);
|
||||
backdrop-filter: blur(12px) saturate(140%); -webkit-backdrop-filter: blur(12px) saturate(140%);
|
||||
}
|
||||
.sidebar { position: sticky; top: 60px; display: flex; flex-direction: column; }
|
||||
.apps-search { position: relative; padding: 14px 20px; border-bottom: 1px solid var(--sidebar-border); }
|
||||
.apps-search-icon { position: absolute; left: 32px; top: 50%; transform: translateY(-50%); color: rgba(var(--text-rgb),0.55); pointer-events: none; }
|
||||
.apps-search input {
|
||||
width: 100%; padding: 9px 12px; border-radius: 10px; border: 1px solid var(--input-border);
|
||||
background: var(--input-bg); color: var(--text-primary); font-size: 0.9rem; outline: none;
|
||||
width: 100%; padding: 9px 12px 9px 36px; border-radius: 8px; border: 1px solid rgba(var(--text-rgb),0.12);
|
||||
background: rgba(var(--text-rgb),0.06); color: var(--text-primary); font-size: 13px; outline: none;
|
||||
transition: background 0.18s ease, border-color 0.18s ease, box-shadow 0.18s ease;
|
||||
}
|
||||
.apps-search input:focus { border-color: var(--accent); }
|
||||
.cat-list { display: flex; flex-direction: column; gap: 2px; }
|
||||
.apps-search input::placeholder { color: rgba(var(--text-rgb),0.5); }
|
||||
.apps-search input:focus { background: rgba(var(--text-rgb),0.10); border-color: rgba(var(--accent-rgb),0.55); box-shadow: 0 0 0 3px rgba(var(--accent-rgb),0.16); }
|
||||
.cat-list { display: flex; flex-direction: column; }
|
||||
.category {
|
||||
display: flex; align-items: center; gap: 10px; padding: 9px 12px; border-radius: 10px;
|
||||
cursor: pointer; color: var(--text-secondary); font-size: 0.92rem; text-transform: capitalize;
|
||||
border: 1px solid transparent;
|
||||
display: flex; align-items: center; gap: 10px; padding: 15px 20px;
|
||||
cursor: pointer; color: var(--text-secondary); font-size: 14px; text-transform: capitalize;
|
||||
border-bottom: 1px solid var(--sidebar-border); transition: background 0.2s, color 0.2s;
|
||||
}
|
||||
.category:hover { background: rgba(var(--text-rgb),0.06); color: var(--text-primary); }
|
||||
.category.active { background: rgba(var(--accent-rgb),0.14); border-color: rgba(var(--accent-rgb),0.35); color: #fff; }
|
||||
.category .cat-ic { width: 16px; height: 16px; opacity: 0.9; flex-shrink: 0; }
|
||||
.category .cat-n { margin-left: auto; font-size: 0.75rem; opacity: 0.6; }
|
||||
.category:hover, .category.active { background: var(--surface-hover); color: var(--text-primary); }
|
||||
.category img { width: 20px; height: 20px; flex-shrink: 0; }
|
||||
|
||||
.main-content { flex: 1; min-width: 0; }
|
||||
.status-strip {
|
||||
@ -171,7 +178,10 @@
|
||||
<div class="apps-layout">
|
||||
<div class="sidebar-container">
|
||||
<div class="sidebar">
|
||||
<div class="apps-search"><input id="q" type="search" placeholder="Search apps…" autocomplete="off" spellcheck="false"></div>
|
||||
<div class="apps-search">
|
||||
<svg class="apps-search-icon" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>
|
||||
<input id="q" type="search" placeholder="Search apps…" autocomplete="off" spellcheck="false">
|
||||
</div>
|
||||
<div class="cat-list" id="cats"></div>
|
||||
</div>
|
||||
</div>
|
||||
@ -218,17 +228,17 @@
|
||||
return by;
|
||||
}
|
||||
|
||||
function catRow(id, label) {
|
||||
// Real App Center category glyphs (bundled into the site by the install
|
||||
// hook); misc.svg is the fallback for an unknown category.
|
||||
return '<div class="category' + (state.cat === id ? ' active' : '') + '" data-cat="' + esc(id) + '">' +
|
||||
'<img src="categories/' + esc(id) + '.svg" alt="" onerror="this.src=\'categories/misc.svg\'">' +
|
||||
'<span>' + esc(label) + '</span></div>';
|
||||
}
|
||||
function renderCats() {
|
||||
var by = counts();
|
||||
var list = Object.keys(by).sort();
|
||||
var rows = ['<div class="category' + (state.cat === 'all' ? ' active' : '') + '" data-cat="all">' +
|
||||
'<svg class="cat-ic" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/></svg>' +
|
||||
'<span>All</span><span class="cat-n">' + state.apps.length + '</span></div>'];
|
||||
list.forEach(function (c) {
|
||||
rows.push('<div class="category' + (state.cat === c ? ' active' : '') + '" data-cat="' + esc(c) + '">' +
|
||||
'<svg class="cat-ic" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="9"/></svg>' +
|
||||
'<span>' + esc(c) + '</span><span class="cat-n">' + by[c] + '</span></div>');
|
||||
});
|
||||
var list = Object.keys(counts()).sort();
|
||||
var rows = [catRow('all', 'All')];
|
||||
list.forEach(function (c) { rows.push(catRow(c, c)); });
|
||||
cats.innerHTML = rows.join('');
|
||||
}
|
||||
|
||||
|
||||
@ -21,5 +21,14 @@ marketplace_install_post_setup()
|
||||
runFileOp mkdir -p "$dest" || return 1
|
||||
runFileOp cp -f "$def/resources/site/index.html" "$dest/index.html" || return 1
|
||||
runFileOp cp -f "$def/$app_name.svg" "$dest/marketplace.svg" || return 1
|
||||
|
||||
# Bundle the App Center's category icons so the site's sidebar matches the
|
||||
# WebUI (same per-category glyphs). Copied from the live frontend so they
|
||||
# stay in step; best-effort (the site falls back to misc.svg if absent).
|
||||
local cat_src="$containers_dir/libreportal/frontend/core/icons/categories"
|
||||
if [[ -d "$cat_src" ]]; then
|
||||
runFileOp mkdir -p "$dest/categories" || true
|
||||
runFileOp cp -f "$cat_src"/*.svg "$dest/categories/" 2>/dev/null || true
|
||||
fi
|
||||
isSuccessful "Marketplace site seeded. Publish a catalog into it: rsync dist/<channel>/ $dest/<channel>/"
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user