openvibers/openvibe.trade Commits
A market brief's sources and citations are separated (#231)
Changes
@@@ -135,10 +135,11 @@ const marketBriefLabel = (b) => (b.reviewed ? 'AI-written, reviewed by a person'/** A brief's text, every point linked to the Trade page it rests on, its sources and its gaps. */function marketBriefBody(b) {-- const cites = (list) => list.map((c) => html`<a class="cite" href="${c.url}">${c.label}</a>`);++ // Separated, so the links never run together into one unbreakable word (11-07: '10-Q filings8-K filings…').++ const cites = (list) => list.map((c, i) => html`${i ? ' · ' : ''}<a class="cite" href="${c.url}">${c.label}</a>`);return html`${b.summary ? html`<p>${b.summary}</p>` : ''}${b.points.length ? html`<ul>${b.points.map((p) => html`<li>${p.text} ${cites(p.cites)}</li>`)}</ul>` : ''}--${b.cites.length ? html`<p class="muted tr-small">Sources: ${b.cites.map((c) => html`<a href="${c.url}">${c.label}</a>`)}</p>` : ''}++${b.cites.length ? html`<p class="muted tr-small">Sources: ${b.cites.map((c, i) => html`${i ? ', ' : ''}<a href="${c.url}">${c.label}</a>`)}</p>` : ''}<p class="muted">${b.disclaimer}${b.model ? html` (${b.model})` : ''}</p>${b.gaps.length ? html`<details><summary>What the sources did not support</summary><ul>${b.gaps.map((g) => html`<li>${g}</li>`)}</ul></details>` : ''}`;}@@@ -116,6 +116,9 @@ const quiet = { log() {}, warn() {}, error() {} };assert.ok(page.text.includes('AI-written, not reviewed by a person'));const b = await t.ctx.briefs.latest(apple);assert.ok(page.text.includes(`href="#${b.points[0].cites[0].id}"`), 'the citation links to the observation row');++ const briefHtml = (page.text.match(/<section[^>]*class="[^"]*ai-brief[\s\S]*?<\/section>/) || page.text.match(/class="ai-brief[\s\S]*?<\/(article|section|div)>/) || [''])[0];++ assert.ok(briefHtml.includes('class="cite"'), 'the brief was found on the page');++ assert.ok(!/<\/a><a (class="cite" )?href/.test(briefHtml), 'citations and sources are separated, never one run-on word');for (const c of b.points.flatMap((p) => p.cites)) assert.ok(page.text.includes(`id="${c.id}"`), `cited row ${c.id} is on the page`);assert.strictEqual(b.points[0].cites.length, 1, 'a series is cited once, by its newest row');assert.ok(page.text.includes('<data value="109417000000">109,417,000,000</data> <small>USD</small>'), 'the stated value, digit for digit, grouped');nnew file mode 100644@@@ -0,0 +1,20 @@++'use strict';++/**++ * A market brief's sources and citations are separated (11-07): its "Sources:" links were placed side by side with++ * nothing between them, so the page read "10-Q filings8-K filings424B2 filings…" and, being one unbreakable word,++ * pushed a 390 px phone 27 px sideways.++ * node test/market-brief-cites.test.js++ */++const assert = require('assert');++const { marketBriefPage } = require('../server/render/home');++++const out = String(marketBriefPage({++ date: '2026-11-07', reviewed: false, summary: 'A quiet day.', disclaimer: 'AI-written.', model: null, gaps: [],++ points: [{ text: 'Two filings moved.', cites: [{ url: '#obs_1', label: 'A' }, { url: '#obs_2', label: 'B' }] }],++ cites: [{ url: '/filings?form=10-Q', label: '10-Q filings' }, { url: '/filings?form=8-K', label: '8-K filings' }, { url: '/i/TPTS', label: 'TPTS' }],++}));++++assert.ok(!/<\/a><a /.test(out), 'no two links touch');++assert.match(out, /Sources: <a href="\/filings\?form=10-Q">10-Q filings<\/a>, <a href="\/filings\?form=8-K">8-K filings<\/a>, <a href="\/i\/TPTS">TPTS<\/a>/);++assert.match(out, /<a class="cite" href="#obs_1">A<\/a> · <a class="cite" href="#obs_2">B<\/a>/);++console.log('market brief cites: sources and citations are separated');
@@ -135,10 +135,11 @@ const marketBriefLabel = (b) => (b.reviewed ? 'AI-written, reviewed by a person'
@@ -135,10 +135,11 @@ const marketBriefLabel = (b) => (b.reviewed ? 'AI-written, reviewed by a person'
/** A brief's text, every point linked to the Trade page it rests on, its sources and its gaps. */
/** A brief's text, every point linked to the Trade page it rests on, its sources and its gaps. */
function marketBriefBody(b) {
function marketBriefBody(b) {
- const cites = (list) => list.map((c) => html`<a class="cite" href="${c.url}">${c.label}</a>`);
+ // Separated, so the links never run together into one unbreakable word (11-07: '10-Q filings8-K filings…').
+ const cites = (list) => list.map((c, i) => html`${i ? ' · ' : ''}<a class="cite" href="${c.url}">${c.label}</a>`);
return html`${b.summary ? html`<p>${b.summary}</p>` : ''}
return html`${b.summary ? html`<p>${b.summary}</p>` : ''}
${b.points.length ? html`<ul>${b.points.map((p) => html`<li>${p.text} ${cites(p.cites)}</li>`)}</ul>` : ''}
${b.points.length ? html`<ul>${b.points.map((p) => html`<li>${p.text} ${cites(p.cites)}</li>`)}</ul>` : ''}
-${b.cites.length ? html`<p class="muted tr-small">Sources: ${b.cites.map((c) => html`<a href="${c.url}">${c.label}</a>`)}</p>` : ''}
+${b.cites.length ? html`<p class="muted tr-small">Sources: ${b.cites.map((c, i) => html`${i ? ', ' : ''}<a href="${c.url}">${c.label}</a>`)}</p>` : ''}
<p class="muted">${b.disclaimer}${b.model ? html` (${b.model})` : ''}</p>
<p class="muted">${b.disclaimer}${b.model ? html` (${b.model})` : ''}</p>
${b.gaps.length ? html`<details><summary>What the sources did not support</summary><ul>${b.gaps.map((g) => html`<li>${g}</li>`)}</ul></details>` : ''}`;
${b.gaps.length ? html`<details><summary>What the sources did not support</summary><ul>${b.gaps.map((g) => html`<li>${g}</li>`)}</ul></details>` : ''}`;
}
}
@@ -116,6 +116,9 @@ const quiet = { log() {}, warn() {}, error() {} };
@@ -116,6 +116,9 @@ const quiet = { log() {}, warn() {}, error() {} };
assert.ok(page.text.includes('AI-written, not reviewed by a person'));
assert.ok(page.text.includes('AI-written, not reviewed by a person'));
const b = await t.ctx.briefs.latest(apple);
const b = await t.ctx.briefs.latest(apple);
assert.ok(page.text.includes(`href="#${b.points[0].cites[0].id}"`), 'the citation links to the observation row');
assert.ok(page.text.includes(`href="#${b.points[0].cites[0].id}"`), 'the citation links to the observation row');
+ const briefHtml = (page.text.match(/<section[^>]*class="[^"]*ai-brief[\s\S]*?<\/section>/) || page.text.match(/class="ai-brief[\s\S]*?<\/(article|section|div)>/) || [''])[0];
+ assert.ok(briefHtml.includes('class="cite"'), 'the brief was found on the page');
+ assert.ok(!/<\/a><a (class="cite" )?href/.test(briefHtml), 'citations and sources are separated, never one run-on word');
for (const c of b.points.flatMap((p) => p.cites)) assert.ok(page.text.includes(`id="${c.id}"`), `cited row ${c.id} is on the page`);
for (const c of b.points.flatMap((p) => p.cites)) assert.ok(page.text.includes(`id="${c.id}"`), `cited row ${c.id} is on the page`);
assert.strictEqual(b.points[0].cites.length, 1, 'a series is cited once, by its newest row');
assert.strictEqual(b.points[0].cites.length, 1, 'a series is cited once, by its newest row');
assert.ok(page.text.includes('<data value="109417000000">109,417,000,000</data> <small>USD</small>'), 'the stated value, digit for digit, grouped');
assert.ok(page.text.includes('<data value="109417000000">109,417,000,000</data> <small>USD</small>'), 'the stated value, digit for digit, grouped');
new file mode 100644
new file mode 100644
@@ -0,0 +1,20 @@
@@ -0,0 +1,20 @@
+'use strict';
+/**
+ * A market brief's sources and citations are separated (11-07): its "Sources:" links were placed side by side with
+ * nothing between them, so the page read "10-Q filings8-K filings424B2 filings…" and, being one unbreakable word,
+ * pushed a 390 px phone 27 px sideways.
+ * node test/market-brief-cites.test.js
+ */
+const assert = require('assert');
+const { marketBriefPage } = require('../server/render/home');
+
+const out = String(marketBriefPage({
+ date: '2026-11-07', reviewed: false, summary: 'A quiet day.', disclaimer: 'AI-written.', model: null, gaps: [],
+ points: [{ text: 'Two filings moved.', cites: [{ url: '#obs_1', label: 'A' }, { url: '#obs_2', label: 'B' }] }],
+ cites: [{ url: '/filings?form=10-Q', label: '10-Q filings' }, { url: '/filings?form=8-K', label: '8-K filings' }, { url: '/i/TPTS', label: 'TPTS' }],
+}));
+
+assert.ok(!/<\/a><a /.test(out), 'no two links touch');
+assert.match(out, /Sources: <a href="\/filings\?form=10-Q">10-Q filings<\/a>, <a href="\/filings\?form=8-K">8-K filings<\/a>, <a href="\/i\/TPTS">TPTS<\/a>/);
+assert.match(out, /<a class="cite" href="#obs_1">A<\/a> · <a class="cite" href="#obs_2">B<\/a>/);
+console.log('market brief cites: sources and citations are separated');