Merge claude/2

This commit is contained in:
librelad 2026-07-04 22:23:28 +01:00
commit ace415d654
3 changed files with 47 additions and 28 deletions

View File

@ -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"
#

View File

@ -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('');
}

View File

@ -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>/"
}