V0.38.3 extension config sections (#236)

Co-authored-by: gobha <jasafpro@gmail.com>
Co-committed-by: gobha <jasafpro@gmail.com>
This commit is contained in:
2026-03-25 16:38:38 +00:00
committed by xcaliber
parent 2136535176
commit fe5894b6e2
11 changed files with 338 additions and 12 deletions

View File

@@ -2,11 +2,12 @@
* AdminSurface — root admin surface
*
* Reads globals:
* __SECTION__ — active section name (string)
* __BASE__ — base path
* __SECTION__ — active section name (string)
* __BASE__ — base path
* __CONFIG_SECTIONS__ — v0.38.3: extension config sections (array|null)
*
* Layout: topbar (back + category tabs) + body (sidebar nav + content area).
* All 24 sections are native Preact components loaded lazily.
* All 24+ sections are native Preact components loaded lazily.
*/
const { html } = window;
const { useState, useEffect, useCallback, useMemo } = hooks;
@@ -37,6 +38,20 @@ const ADMIN_LABELS = {
dashboard: 'Dashboard', usage: 'Usage', audit: 'Audit', stats: 'Stats',
};
// ── v0.38.3: Extension config sections ──────
// Packages declare config_section in their manifest targeting "admin".
// We merge them into the appropriate category and section module map.
const _configSections = window.__CONFIG_SECTIONS__ || [];
const _base = window.__BASE__ || '';
for (const cs of _configSections) {
const pkgId = cs.package_id;
const cat = cs.category || 'system';
// Add to category (create if new — unlikely but safe)
if (!ADMIN_SECTIONS[cat]) ADMIN_SECTIONS[cat] = [];
if (!ADMIN_SECTIONS[cat].includes(pkgId)) ADMIN_SECTIONS[cat].push(pkgId);
ADMIN_LABELS[pkgId] = cs.label;
}
const CATEGORY_META = {
people: { label: 'People', first: 'users', icon: 'M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2|C9 7 4' },
ai: { label: 'AI', first: 'providers', icon: 'R4 4 16 16 2|L9 9 9.01 9|L15 9 15.01 9|M8 14s1.5 2 4 2 4-2 4-2' },
@@ -76,6 +91,13 @@ const sectionModules = {
stats: () => import(`./stats.js${_v}`),
};
// v0.38.3: Register dynamic section loaders for extension config sections
for (const cs of _configSections) {
const pkgId = cs.package_id;
const component = cs.component || 'js/config.js';
sectionModules[pkgId] = () => import(`${_base}/surfaces/${pkgId}/${component}`);
}
// ── Derive category from section ────────────
function catForSection(section) {
for (const [cat, sections] of Object.entries(ADMIN_SECTIONS)) {

View File

@@ -2,8 +2,9 @@
* SettingsSurface — root settings surface
*
* Reads globals:
* __SECTION__ — active section name (string)
* __BASE__ — base path
* __SECTION__ — active section name (string)
* __BASE__ — base path
* __CONFIG_SECTIONS__ — v0.38.3: extension config sections (array|null)
*
* Policy flags (BYOK, personas) come from sw.auth.policies,
* populated at SDK boot via GET /api/v1/profile/permissions.
@@ -75,6 +76,20 @@ const SECTION_TITLES = {
notifications: 'Notifications',
};
// ── v0.38.3: Extension config sections ──────
// Packages declare config_section in their manifest. The backend passes
// matching entries via __CONFIG_SECTIONS__. We merge them into the nav
// and section module map for lazy loading.
const _configSections = window.__CONFIG_SECTIONS__ || [];
const EXT_NAV_ITEMS = _configSections.map(cs => ({ key: cs.package_id, label: cs.label }));
const _base = window.__BASE__ || '';
for (const cs of _configSections) {
const pkgId = cs.package_id;
const component = cs.component || 'js/config.js';
sectionModules[pkgId] = () => import(`${_base}/surfaces/${pkgId}/${component}`);
SECTION_TITLES[pkgId] = cs.label;
}
function SettingsSurface() {
const BASE = window.__BASE__ || '';
const section = window.__SECTION__ || 'general';
@@ -195,6 +210,20 @@ function SettingsSurface() {
</p>
</div>
`}
${EXT_NAV_ITEMS.length > 0 && html`
<div>
<div class="settings-nav-sep"></div>
<div class="settings-nav-label" style="font-size:10px;font-weight:600;color:var(--text-3);padding:8px 12px 4px;text-transform:uppercase;letter-spacing:0.5px;">Extensions</div>
${EXT_NAV_ITEMS.map(item => html`
<a href="${BASE}/settings/${item.key}"
class="settings-nav-link ${section === item.key ? 'active' : ''}"
onClick=${onNavClick}>
${item.label}
</a>
`)}
</div>
`}
</div>
${/* Content */''}

View File

@@ -2,11 +2,12 @@
* TeamAdminSurface — root team admin surface
*
* Reads globals:
* __SECTION__ — active section name (string)
* __BASE__ — base path
* __SECTION__ — active section name (string)
* __BASE__ — base path
* __CONFIG_SECTIONS__ — v0.38.3: extension config sections (array|null)
*
* Layout: topbar (back + team name) + sidebar nav + content area.
* All 10 sections are native Preact components loaded lazily.
* All 10+ sections are native Preact components loaded lazily.
*/
const { html } = window;
const { useState, useEffect, useCallback, useMemo } = hooks;
@@ -45,6 +46,16 @@ const sectionModules = {
activity: () => import('./activity.js'),
};
// ── v0.38.3: Extension config sections ──────
const _configSections = window.__CONFIG_SECTIONS__ || [];
const _base = window.__BASE__ || '';
for (const cs of _configSections) {
const pkgId = cs.package_id;
const component = cs.component || 'js/config.js';
SECTIONS.push({ key: pkgId, label: cs.label });
sectionModules[pkgId] = () => import(`${_base}/surfaces/${pkgId}/${component}`);
}
function TeamAdminSurface() {
const BASE = window.__BASE__ || '';
const section = window.__SECTION__ || 'members';