openvibers/openvibe.news Commits

Story pages: Ask your agent chip beside Share (#232)

OP OpenVibers committed 6 days ago · 836531dcf6 · 3 files, +22 −1

The chip's text is the story's headline and public address; kit.askAgent escapes the headline. Story pages now load kit.css, so both the Share button and the chip wear the kit's look.

Changes

ddiff --git a/server/http/public.js b/server/http/public.js
iindex 7fbe240..abf87bb 100644
---- a/server/http/public.js
++++ b/server/http/public.js
@@@ -308,6 +308,7 @@ function createPublicRoutes(ctx) {
decision, canonical: m.url, type: 'article',
author: m.authors.length ? m.authors.map((a) => a.name).join(', ') : undefined,
published: m.first_published_at, modified: m.updated_at,
++ styles: ['kit.css'],
feeds: feedsFor('/'),
jsonLd: preview ? [] : [reading.storyJsonLd(m), seo.structuredData.breadcrumbs(crumbs.map((c) => ({ name: c.name, url: c.url ? publication.abs(c.url) : m.url })))],
body: pages.storyPage(m, {
ddiff --git a/server/render/pages.js b/server/render/pages.js
iindex 55f0ff0..9cfc83b 100644
---- a/server/render/pages.js
++++ b/server/render/pages.js
@@@ -196,7 +196,7 @@ ${raw(notices.join(''))}
<h1>${m.headline}</h1>
<p class="byline">${m.authors.length ? h`By ${m.authors.map((a) => a.name).join(', ')} · ` : ''}OpenVibe.News${m.first_published_at ? h` · ${time(Date.parse(m.first_published_at))}` : ''}${m.rev.number > 1 && m.first_published_at && Date.parse(m.updated_at) > Date.parse(m.first_published_at) + 60000 ? h` · updated ${time(Date.parse(m.updated_at))}` : ''}${m.topic ? h` · <a href="/topics/${m.topic.slug}">${m.topic.name}</a>` : ''}${m.story.state !== 'published' && m.story.state !== 'retracted' ? h` <span class="badge">${m.story.state}</span>` : ''}</p>
${m.disclosure ? h`<p class="disclosure" role="note"><strong>${m.disclosure.short}.</strong> ${m.disclosure.long}</p>` : ''}
--<p class="story-share">${raw(kit.share({ title: m.headline, url: m.url, text: m.paragraphs[0] ? m.paragraphs[0].text.slice(0, 200) : '', kind: 'story', id: m.story.id }))}</p>
++<p class="story-share">${raw(kit.share({ title: m.headline, url: m.url, text: m.paragraphs[0] ? m.paragraphs[0].text.slice(0, 200) : '', kind: 'story', id: m.story.id }))} ${raw(kit.askAgent({ text: `About the story “${m.headline}” (${m.url}): ` }))}</p>
</header>
<div class="story-body">${m.paragraphs.map((p) => h`<p${p.supported ? '' : raw(' class="unsupported"')}>${p.text} ${cites(p.sources)}${p.supported ? '' : h` <span class="badge">source removed</span>`}</p>`)}</div>
<p class="meta">Numbers in brackets point to the sources below. Headlines, outlets, authors and dates in the table are as each source published them; the text above is written by OpenVibe.News.</p>
ddiff --git a/test/stories.test.js b/test/stories.test.js
iindex dd7c293..d124cce 100644
---- a/test/stories.test.js
++++ b/test/stories.test.js
@@@ -120,6 +120,12 @@ const BODY = [
assert.match(page.text, /"@type":"NewsArticle"/);
assert.match(page.text, /Mission announcements/);
assert.match(page.text, /2026-09-22<\/time> — Europa Clipper lifts off/);
++ // The Ask your agent chip (openvibe-shared 4.135.0): beside Share, the story's headline and public address in its text.
++ const asks = page.text.match(/<button[^>]*data-ov-ask[^>]*>/g) || [];
++ assert.strictEqual(asks.length, 1, 'one Ask your agent chip: ' + asks.join(' | '));
++ assert.ok(asks[0].includes('data-ov-ask="About the story “Europa Clipper launches for Jupiter” (https://openvibe.news/stories/europa-clipper-launches-for-jupiter): "'), asks[0]);
++ assert.ok(asks[0].includes('class="ovk-ask ovk-btn'), 'the shared kit chip');
++ assert.match(page.text, /<link rel="stylesheet" href="\/shared\/kit\.css\?v=[0-9a-f]+">/, 'the kit’s look is on the page');
const json = (await t.get('/stories/europa-clipper-launches-for-jupiter.json')).json();
assert.strictEqual(json.claims.length, 3);
@@@ -229,6 +235,20 @@ const BODY = [
assert.doesNotMatch((await t.get('/sitemaps/stories.xml')).text, /europa-clipper-launches/);
});
++ await check('the ask chip’s text is the headline and address, and kit.askAgent escapes it', async () => {
++ const html = String(require('../server/render/pages').storyPage({
++ headline: 'Quotes "and" <tags> & more', url: 'https://openvibe.news/stories/quotes',
++ story: { id: 'st_1', state: 'published' }, rev: { number: 1 },
++ authors: [], first_published_at: null, updated_at: null, topic: null, disclosure: null,
++ paragraphs: [], timeline: [], perspectives: [], corrections: [], retraction: null, upstream: [],
++ sources: [], outletGroups: [], path: '/stories/quotes',
++ }, { breadcrumbs: [], jsonUrl: '/stories/quotes.json' }));
++ const asks = html.match(/<button[^>]*data-ov-ask[^>]*>/g) || [];
++ assert.strictEqual(asks.length, 1, asks.join(' | '));
++ assert.ok(asks[0].includes('data-ov-ask="About the story “Quotes &quot;and&quot; &lt;tags&gt; &amp; more” (https://openvibe.news/stories/quotes): "'), asks[0]);
++ assert.ok(!asks[0].includes('<tags>'), 'the headline is escaped in the chip');
++ });
++
await t.close();
done();
})();