openvibers/openvibe.bot Commits

Model pages: the panel lists its actions once, and a robot's own buttons (#409)

OP OpenVibers committed 1 days ago · fafad1c9a0 · 2 files, +24 −2

A model's "Its panel" listed only the profile's widgets, so LOOI's page left out
its headlights (a mapped action since #402), and the capability chips under the
list said the same things again. The list now names the widgets, then the
mapped actions with who may use them ("Headlights (the owner and Operators)"),
and the chips are gone from this page.

A new "Your own buttons" section points to OpenVibe Elements on Design: the free
LOOI pack on LOOI's page, "Make one with AI" (Design's composer opened on the
Element kind) and the gallery on every model.

Test: test/directory.test.js.

Changes

ddiff --git a/server/web/directory.js b/server/web/directory.js
iindex acfb3e8..2c0d103 100644
---- a/server/web/directory.js
++++ b/server/web/directory.js
@@@ -186,8 +186,22 @@ ${community.length ? `<section class="dir-sec" aria-labelledby="g-community"><h2
}
// ── GET /models/:id ─────────────────────────────────────────────────────────────────────────────────
++// A profile's mapped actions, by what the panel calls them and who may use them (server/domain ROLE_KINDS).
++const ACTION_WORDS = { headlight: ['Headlights', 'the owner and Operators'] };
++
function renderModel({ config, model, kit = null }) {
-- const controls = model.widgets.filter((w) => w.type !== 'latency').map((w) => `<li>${esc(w.label || w.type)}</li>`).join('');
++ // The panel's controls: its widgets, then the actions its profile maps (LOOI's headlights were missing from this
++ // list, and the capability chips under it said the list again: the 11-12 review).
++ const mapped = (model.profile && model.profile.mapping && model.profile.mapping.actions) || {};
++ const names = Array.isArray(mapped) ? mapped.filter((a) => typeof a === 'string') : Object.keys(typeof mapped === 'object' ? mapped : {});
++ const actions = names.map((a) => {
++ const [name, who] = ACTION_WORDS[a] || [a.replace(/[_-]+/g, ' ').replace(/^./, (c) => c.toUpperCase()), ''];
++ return `<li>${esc(name)}${who ? ` <span class="muted">(${esc(who)})</span>` : ''}</li>`;
++ }).join('');
++ const controls = model.widgets.filter((w) => w.type !== 'latency').map((w) => `<li>${esc(w.label || w.type)}</li>`).join('') + actions;
++ // Buttons viewers press under the stream: made with AI or installed from OpenVibe.Design's gallery (Elements).
++ const own = `<section class="dir-sec card"><h2>Your own buttons</h2><p class="muted">Add buttons viewers press under your stream: install free ones, or describe yours and the AI makes them. Every press still passes Bot's gate.</p>
++<p class="dir-actions">${model.id === 'looi' ? `${link('https://openvibe.design/gallery/looi-pack', 'The free LOOI pack', 'dir-btn dir-primary')} ` : ''}${link('https://openvibe.design/create?kind=element', 'Make one with AI', model.id === 'looi' ? 'dir-btn' : 'dir-btn dir-primary')} ${link('https://openvibe.design/gallery', 'Browse the gallery', 'dir-btn')}</p></section>`;
const parts = kit ? `<section class="dir-sec card"><h2>What you need</h2><ul>${kit.parts.map((p) => `<li>${esc(p.qty)}× ${esc(p.name)}${p.note ? ` <span class="muted">(${esc(p.note)})</span>` : ''}</li>`).join('')}</ul>
${kit.build_guide_url ? `<p>${link(kit.build_guide_url, 'The kit\'s build guide')}</p>` : ''}</section>` : '';
const add = model.connect === 'browser'
@@@ -198,9 +212,10 @@ ${kit.build_guide_url ? `<p>${link(kit.build_guide_url, 'The kit\'s build guide'
<div class="dir-cols">
<section class="dir-sec card"><h2>How it connects</h2><p><b>${esc(CONNECT[model.connect].label)}.</b> ${esc(CONNECT[model.connect].text)}</p>
${model.setup.length ? `<ol>${model.setup.map((s) => `<li>${esc(s)}</li>`).join('')}</ol>` : ''}</section>
--<section class="dir-sec card"><h2>Its panel</h2><p class="muted">Laid out from its profile (<code>${esc(model.id)}</code>):</p><ul>${controls}</ul>${chips(model.capabilities)}</section>
++<section class="dir-sec card"><h2>Its panel</h2><p class="muted">Laid out from its profile (<code>${esc(model.id)}</code>):</p><ul>${controls}</ul></section>
</div>
${parts}
++${own}
${model.community ? `<section class="dir-sec card"><h2>From the community</h2><p>Submitted by ${esc(ownerName(model.community.by))} and reviewed by an OpenVibe admin.${model.community.license ? ` Licence: ${esc(model.community.license)}.` : ''}</p>
<ul>${model.community.driver_url ? `<li>Driver: ${link(model.community.driver_url, model.community.driver_url)}</li>` : ''}${model.community.guide_url ? `<li>Setup guide: ${link(model.community.guide_url, model.community.guide_url)}</li>` : ''}
<li>Signed by OpenVibe.Bot (Ed25519, key <code>${esc(model.community.key_id || '')}</code>): ${link(`/api/v1/profiles/${model.id}`, 'the profile and its signature')} · ${link('/api/v1/profiles/signing-key', 'the public key')}</li>
ddiff --git a/test/directory.test.js b/test/directory.test.js
iindex 219b92d..a9074ea 100644
---- a/test/directory.test.js
++++ b/test/directory.test.js
@@@ -88,6 +88,13 @@ const { loadProfiles } = require('../server/profiles');
assert.ok(cozmo.includes('<h1>Cozmo') && cozmo.includes('How it connects') && cozmo.includes('hardware-cozmo.md') && cozmo.includes('href="/robots/new?profile=cozmo"'));
const sim = await (await get('/models/sim.rover')).text();
assert.ok(sim.includes('href="/try"'), 'the simulated rover points at /try');
++ // LOOI's panel lists its mapped headlights (owner and Operators), once; and the page points to its free pack
++ // and to making your own buttons with AI (OpenVibe Elements on Design).
++ const looi = await (await get('/models/looi')).text();
++ assert.ok(looi.includes('<li>Headlights <span class="muted">(the owner and Operators)</span></li>'), 'the headlights are on the panel list');
++ assert.ok(!looi.includes('class="dir-chips"'), 'the panel is said once, not again as chips');
++ assert.ok(looi.includes('https://openvibe.design/gallery/looi-pack') && looi.includes('https://openvibe.design/create?kind=element'), 'its buttons: the pack, or made with AI');
++ assert.ok(sim.includes('https://openvibe.design/create?kind=element') && !sim.includes('looi-pack'), 'every model can make its own; only LOOI has a pack');
assert.strictEqual((await get('/models/nope')).status, 404);
const sitemap = await (await get('/sitemap.xml')).text();
for (const p of profiles) assert.ok(sitemap.includes(`/models/${p.id}</loc>`), p.id);