@validation-os/dashboard 0.15.4 → 0.15.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +2 -0
- package/dist/index.js +388 -131
- package/dist/index.js.map +1 -1
- package/dist/styles.css +182 -72
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -9,14 +9,14 @@ var REGISTER_LABEL = {
|
|
|
9
9
|
// One container-agnostic lens now Goal is unified into Experiment (OPS-1299 /
|
|
10
10
|
// OPS-1287 story 22): all plans browse in one place under this label.
|
|
11
11
|
experiments: "Evidence plans",
|
|
12
|
-
readings: "
|
|
12
|
+
readings: "Evidence",
|
|
13
13
|
decisions: "Decisions",
|
|
14
14
|
glossary: "Glossary"
|
|
15
15
|
};
|
|
16
16
|
var REGISTER_SINGULAR = {
|
|
17
17
|
assumptions: "Assumption",
|
|
18
18
|
experiments: "Evidence plan",
|
|
19
|
-
readings: "
|
|
19
|
+
readings: "Evidence",
|
|
20
20
|
decisions: "Decision",
|
|
21
21
|
glossary: "Glossary term"
|
|
22
22
|
};
|
|
@@ -37,7 +37,7 @@ var REGISTER_SUBTITLE = {
|
|
|
37
37
|
var REGISTER_ICON = {
|
|
38
38
|
assumptions: "\u25CE",
|
|
39
39
|
experiments: "\u2697",
|
|
40
|
-
readings: "\
|
|
40
|
+
readings: "\u2726",
|
|
41
41
|
decisions: "\xA7",
|
|
42
42
|
glossary: "A"
|
|
43
43
|
};
|
|
@@ -50,7 +50,7 @@ var REGISTER_GROUPS = [
|
|
|
50
50
|
var WORKFLOW_NAV = [
|
|
51
51
|
{ route: "assumptions", label: "Assumptions", icon: "\u25CE", isDefault: true },
|
|
52
52
|
{ route: "experiments", label: "Experiments", icon: "\u2697" },
|
|
53
|
-
{ route: "readings", label: "
|
|
53
|
+
{ route: "readings", label: "Evidence", icon: "\u2726" }
|
|
54
54
|
];
|
|
55
55
|
|
|
56
56
|
// src/assumption-detail.tsx
|
|
@@ -218,6 +218,47 @@ function buildEvidenceComposition(assumption, readings) {
|
|
|
218
218
|
totalContribution: Math.round((total + Number.EPSILON) * 100) / 100
|
|
219
219
|
};
|
|
220
220
|
}
|
|
221
|
+
function readingContributions(assumption, readings) {
|
|
222
|
+
const id = str(assumption.id) ?? "";
|
|
223
|
+
const inputs = [];
|
|
224
|
+
for (const r of readings) {
|
|
225
|
+
const belief2 = readingBeliefFor(r, id);
|
|
226
|
+
if (!belief2) continue;
|
|
227
|
+
const result = str(belief2.Result) ?? "Inconclusive";
|
|
228
|
+
if (result === "Inconclusive") continue;
|
|
229
|
+
const rung = str(r.Rung) ?? "Talk";
|
|
230
|
+
inputs.push({
|
|
231
|
+
id: str(r.id) ?? "",
|
|
232
|
+
source: str(r.Source) ?? null,
|
|
233
|
+
rung,
|
|
234
|
+
result,
|
|
235
|
+
representativeness: Number(r.Representativeness) || 1,
|
|
236
|
+
credibility: Number(r.Credibility) || 1,
|
|
237
|
+
date: str(r.Date),
|
|
238
|
+
magnitudeBand: r.magnitudeBand,
|
|
239
|
+
experimentId: str(r.experimentId)
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
const winners = scoreAndDedupe(inputs);
|
|
243
|
+
const winnerIds = new Set(winners.map((w) => w.input.id));
|
|
244
|
+
const rungsPresent = new Set(winners.map((x) => x.input.rung));
|
|
245
|
+
let den = 0;
|
|
246
|
+
for (const rung of rungsPresent) den += w0ForRung(rung);
|
|
247
|
+
for (const x of winners) den += x.weight;
|
|
248
|
+
return inputs.map((input) => {
|
|
249
|
+
const w = winners.find((x) => x.input.id === input.id);
|
|
250
|
+
const used = winnerIds.has(input.id);
|
|
251
|
+
const strength = w ? w.strength : (input.result === "Validated" ? 1 : input.result === "Invalidated" ? -1 : 0) * (RUNG_CAPS[input.rung] ?? 0);
|
|
252
|
+
const weight = w ? w.weight : 0;
|
|
253
|
+
return {
|
|
254
|
+
id: input.id,
|
|
255
|
+
contribution: w && den > 0 ? Math.round(w.weight * w.strength / den * 100) / 100 : 0,
|
|
256
|
+
weight,
|
|
257
|
+
strength,
|
|
258
|
+
used
|
|
259
|
+
};
|
|
260
|
+
});
|
|
261
|
+
}
|
|
221
262
|
|
|
222
263
|
// src/confidence-explainer.ts
|
|
223
264
|
import { RUNG_ANCHOR, W0_BY_RUNG } from "@validation-os/core/derivation";
|
|
@@ -1004,10 +1045,26 @@ function readingBeliefVerdicts(reading, assumptions = []) {
|
|
|
1004
1045
|
linked: hit != null,
|
|
1005
1046
|
result: b.Result ?? null,
|
|
1006
1047
|
strength,
|
|
1007
|
-
justification: typeof b["Grading justification"] === "string" ? b["Grading justification"] : ""
|
|
1048
|
+
justification: typeof b["Grading justification"] === "string" ? b["Grading justification"] : "",
|
|
1049
|
+
excerpt: typeof b.excerpt === "string" && b.excerpt !== "" ? b.excerpt : snippetFromBody(String(reading.body ?? ""), b.assumptionId)
|
|
1008
1050
|
};
|
|
1009
1051
|
});
|
|
1010
1052
|
}
|
|
1053
|
+
function snippetFromBody(body, cue) {
|
|
1054
|
+
if (!body) return "";
|
|
1055
|
+
const quoteMatch = body.match(/## Quote\n+([\s\S]*?)(?=\n## |\n##$|$)/i);
|
|
1056
|
+
if (quoteMatch) {
|
|
1057
|
+
const q = quoteMatch[1].trim();
|
|
1058
|
+
return q.length > 220 ? q.slice(0, 217).trim() + "\u2026" : q;
|
|
1059
|
+
}
|
|
1060
|
+
const sentences = body.split(/(?<=[.!?])\s+/);
|
|
1061
|
+
const cueLower = cue.toLowerCase();
|
|
1062
|
+
for (const s of sentences) {
|
|
1063
|
+
if (s.toLowerCase().includes(cueLower)) return s.trim();
|
|
1064
|
+
}
|
|
1065
|
+
const first = sentences[0]?.trim() ?? "";
|
|
1066
|
+
return first.length > 220 ? first.slice(0, 217).trim() + "\u2026" : first;
|
|
1067
|
+
}
|
|
1011
1068
|
function readingBeliefSummary(reading, assumptions = []) {
|
|
1012
1069
|
const verdicts = readingBeliefVerdicts(reading, assumptions);
|
|
1013
1070
|
let validated = 0;
|
|
@@ -1554,64 +1611,118 @@ function AssumptionDetail({
|
|
|
1554
1611
|
);
|
|
1555
1612
|
})
|
|
1556
1613
|
] }) : null,
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1614
|
+
/* @__PURE__ */ jsx4(
|
|
1615
|
+
EvidenceList,
|
|
1616
|
+
{
|
|
1617
|
+
assumptionId,
|
|
1618
|
+
readings: readings.records ?? [],
|
|
1619
|
+
onNavigate
|
|
1620
|
+
}
|
|
1621
|
+
)
|
|
1622
|
+
] });
|
|
1623
|
+
}
|
|
1624
|
+
function EvidenceList({
|
|
1625
|
+
assumptionId,
|
|
1626
|
+
readings,
|
|
1627
|
+
onNavigate
|
|
1628
|
+
}) {
|
|
1629
|
+
const linkedReadings = readings.filter((r) => readingBeliefs(r).some((b) => b.assumptionId === assumptionId)).sort((a, b) => String(b.Date ?? "").localeCompare(String(a.Date ?? "")));
|
|
1630
|
+
const contribById = useMemo(() => {
|
|
1631
|
+
const rec = readings.find((a) => String(a.id) === assumptionId);
|
|
1632
|
+
if (!rec) return /* @__PURE__ */ new Map();
|
|
1633
|
+
const rows = readingContributions(rec, readings);
|
|
1634
|
+
return new Map(rows.map((r) => [r.id, r]));
|
|
1635
|
+
}, [readings, assumptionId]);
|
|
1636
|
+
if (linkedReadings.length === 0) return null;
|
|
1637
|
+
return /* @__PURE__ */ jsxs4("div", { className: "vos-card vos-detail-section", children: [
|
|
1638
|
+
/* @__PURE__ */ jsxs4("div", { className: "vos-detail-section-label", children: [
|
|
1639
|
+
"Evidence \xB7 ",
|
|
1640
|
+
linkedReadings.length,
|
|
1641
|
+
" piece",
|
|
1642
|
+
linkedReadings.length === 1 ? "" : "s",
|
|
1643
|
+
" of evidence"
|
|
1644
|
+
] }),
|
|
1645
|
+
linkedReadings.map((r) => {
|
|
1646
|
+
const id = String(r.id ?? "");
|
|
1647
|
+
const belief2 = readingBeliefFor(r, assumptionId);
|
|
1648
|
+
if (!belief2) return null;
|
|
1649
|
+
const result = String(belief2.Result ?? "Inconclusive");
|
|
1650
|
+
const justification = String(belief2["Grading justification"] ?? "");
|
|
1651
|
+
const excerpt = typeof belief2.excerpt === "string" && belief2.excerpt !== "" ? belief2.excerpt : snippetFromBody2(String(r.body ?? ""), assumptionId);
|
|
1652
|
+
const rung = String(r.Rung ?? "");
|
|
1653
|
+
const source = String(r.Source ?? "");
|
|
1654
|
+
const expId = r.experimentId ? String(r.experimentId) : null;
|
|
1655
|
+
const c = contribById.get(id);
|
|
1656
|
+
return /* @__PURE__ */ jsxs4(
|
|
1657
|
+
"button",
|
|
1658
|
+
{
|
|
1659
|
+
type: "button",
|
|
1660
|
+
className: `vos-evidence-row vos-verdict-${verdictTone(result)}`,
|
|
1661
|
+
onClick: () => onNavigate({ name: "reading", id }),
|
|
1662
|
+
children: [
|
|
1663
|
+
/* @__PURE__ */ jsxs4("div", { className: "vos-evidence-row-head", children: [
|
|
1664
|
+
/* @__PURE__ */ jsx4("span", { className: "vos-evidence-date vos-num", children: String(r.Date ?? "") }),
|
|
1665
|
+
/* @__PURE__ */ jsx4("span", { className: "vos-evidence-title", children: String(r.Title ?? id) }),
|
|
1666
|
+
/* @__PURE__ */ jsx4("span", { className: `vos-pill vos-pill-${verdictTone(result)}`, children: result }),
|
|
1667
|
+
/* @__PURE__ */ jsx4("span", { className: "vos-rung-tag", children: rung }),
|
|
1668
|
+
c && c.used ? /* @__PURE__ */ jsxs4("span", { className: `vos-evidence-score vos-num vos-text-${contributionTone(c.contribution)}`, children: [
|
|
1669
|
+
formatSigned(c.contribution),
|
|
1670
|
+
" confidence"
|
|
1671
|
+
] }) : null
|
|
1672
|
+
] }),
|
|
1673
|
+
excerpt ? /* @__PURE__ */ jsxs4("div", { className: "vos-evidence-excerpt", children: [
|
|
1674
|
+
"\u201C",
|
|
1675
|
+
excerpt,
|
|
1676
|
+
"\u201D"
|
|
1677
|
+
] }) : null,
|
|
1678
|
+
justification ? /* @__PURE__ */ jsxs4("div", { className: `vos-belief-rationale vos-verdict-border-${verdictTone(result)}`, children: [
|
|
1679
|
+
/* @__PURE__ */ jsx4("span", { className: "vos-belief-rationale-label", children: "grading rationale:" }),
|
|
1680
|
+
justification
|
|
1681
|
+
] }) : null,
|
|
1682
|
+
/* @__PURE__ */ jsxs4("div", { className: "vos-evidence-source", children: [
|
|
1683
|
+
/* @__PURE__ */ jsx4("span", { className: "vos-evidence-source-link", title: source, children: source }),
|
|
1684
|
+
expId ? /* @__PURE__ */ jsxs4(Fragment3, { children: [
|
|
1685
|
+
" \xB7 from experiment ",
|
|
1686
|
+
/* @__PURE__ */ jsx4(
|
|
1687
|
+
"span",
|
|
1688
|
+
{
|
|
1689
|
+
className: "vos-link",
|
|
1690
|
+
onClick: (ev) => {
|
|
1691
|
+
ev.stopPropagation();
|
|
1692
|
+
onNavigate({ name: "experiment", id: expId });
|
|
1693
|
+
},
|
|
1694
|
+
children: expId
|
|
1695
|
+
}
|
|
1696
|
+
)
|
|
1697
|
+
] }) : null
|
|
1698
|
+
] })
|
|
1699
|
+
]
|
|
1700
|
+
},
|
|
1701
|
+
id
|
|
1702
|
+
);
|
|
1703
|
+
})
|
|
1613
1704
|
] });
|
|
1614
1705
|
}
|
|
1706
|
+
function contributionTone(v) {
|
|
1707
|
+
if (v > 0) return "good";
|
|
1708
|
+
if (v < 0) return "crit";
|
|
1709
|
+
return "neutral";
|
|
1710
|
+
}
|
|
1711
|
+
function snippetFromBody2(body, cue) {
|
|
1712
|
+
if (!body) return "";
|
|
1713
|
+
const quoteMatch = body.match(/## Quote\n+([\s\S]*?)(?=\n## |\n##$|$)/i);
|
|
1714
|
+
if (quoteMatch) {
|
|
1715
|
+
const q = quoteMatch[1].trim();
|
|
1716
|
+
return q.length > 220 ? q.slice(0, 217).trim() + "\u2026" : q;
|
|
1717
|
+
}
|
|
1718
|
+
const sentences = body.split(/(?<=[.!?])\s+/);
|
|
1719
|
+
const cueLower = cue.toLowerCase();
|
|
1720
|
+
for (const s of sentences) {
|
|
1721
|
+
if (s.toLowerCase().includes(cueLower)) return s.trim();
|
|
1722
|
+
}
|
|
1723
|
+
const first = sentences[0]?.trim() ?? "";
|
|
1724
|
+
return first.length > 220 ? first.slice(0, 217).trim() + "\u2026" : first;
|
|
1725
|
+
}
|
|
1615
1726
|
function ScoreCard({
|
|
1616
1727
|
label,
|
|
1617
1728
|
value,
|
|
@@ -1781,16 +1892,24 @@ function EvidenceCompositionView({
|
|
|
1781
1892
|
" (adds to Confidence)"
|
|
1782
1893
|
] }),
|
|
1783
1894
|
comp.rungs.length === 0 ? /* @__PURE__ */ jsx4("div", { className: "vos-muted", children: "No lens set \u2014 evidence composition needs a lens." }) : comp.rungs.map((r) => {
|
|
1784
|
-
const
|
|
1895
|
+
const abs = Math.abs(r.contribution);
|
|
1896
|
+
const pct2 = r.cap > 0 ? Math.min(100, abs / r.cap * 100) : 0;
|
|
1785
1897
|
const isEmpty = r.count === 0;
|
|
1898
|
+
const tone = r.contribution > 0 ? "good" : r.contribution < 0 ? "crit" : "accent";
|
|
1786
1899
|
return /* @__PURE__ */ jsxs4("div", { className: "vos-comp-row", children: [
|
|
1787
1900
|
/* @__PURE__ */ jsx4("span", { className: `vos-comp-rung ${isEmpty ? "is-empty" : ""}`, children: r.rung }),
|
|
1788
|
-
/* @__PURE__ */ jsx4("div", { className: "vos-comp-bar", children: /* @__PURE__ */ jsx4("i", { className:
|
|
1789
|
-
/* @__PURE__ */ jsx4("span", { className: "vos-comp-val vos-num", children: isEmpty ? "\u2014" :
|
|
1901
|
+
/* @__PURE__ */ jsx4("div", { className: "vos-comp-bar", children: /* @__PURE__ */ jsx4("i", { className: `vos-comp-fill vos-fill-${tone}`, style: { width: `${isEmpty ? 0 : Math.max(4, pct2)}%` } }) }),
|
|
1902
|
+
/* @__PURE__ */ jsx4("span", { className: "vos-comp-val vos-num", children: isEmpty ? "\u2014" : /* @__PURE__ */ jsxs4(Fragment3, { children: [
|
|
1903
|
+
/* @__PURE__ */ jsx4("span", { className: `vos-text-${tone}`, children: formatSigned(r.contribution) }),
|
|
1904
|
+
/* @__PURE__ */ jsxs4("span", { style: { color: "var(--vos-faint)" }, children: [
|
|
1905
|
+
" \xB7 cap ",
|
|
1906
|
+
r.cap
|
|
1907
|
+
] })
|
|
1908
|
+
] }) }),
|
|
1790
1909
|
/* @__PURE__ */ jsxs4("span", { className: "vos-comp-count vos-num", children: [
|
|
1791
1910
|
r.count,
|
|
1792
|
-
"
|
|
1793
|
-
r.count === 1 ? "" : "
|
|
1911
|
+
" ",
|
|
1912
|
+
r.count === 1 ? "source" : "sources"
|
|
1794
1913
|
] })
|
|
1795
1914
|
] }, r.rung);
|
|
1796
1915
|
})
|
|
@@ -1798,7 +1917,7 @@ function EvidenceCompositionView({
|
|
|
1798
1917
|
}
|
|
1799
1918
|
|
|
1800
1919
|
// src/assumptions-surface.tsx
|
|
1801
|
-
import { useMemo as useMemo2 } from "react";
|
|
1920
|
+
import { useMemo as useMemo2, useState as useState3 } from "react";
|
|
1802
1921
|
|
|
1803
1922
|
// src/next-move.ts
|
|
1804
1923
|
import {
|
|
@@ -2099,9 +2218,14 @@ function weekOverWeekDelta(assumptions, readings, now) {
|
|
|
2099
2218
|
}
|
|
2100
2219
|
|
|
2101
2220
|
// src/recommended-experiments.ts
|
|
2102
|
-
var MAX_RECOMMENDED =
|
|
2221
|
+
var MAX_RECOMMENDED = 3;
|
|
2103
2222
|
var MAX_CLUSTER_SIZE = 3;
|
|
2104
2223
|
var MAX_NEEDS_FRAMING = 3;
|
|
2224
|
+
var LENS_COLOUR = {
|
|
2225
|
+
Consumer: "#8b83f5",
|
|
2226
|
+
Commercial: "#38c793",
|
|
2227
|
+
Investor: "#e6a23c"
|
|
2228
|
+
};
|
|
2105
2229
|
function buildNeedsFraming(assumptions) {
|
|
2106
2230
|
const live = assumptions.filter((a) => {
|
|
2107
2231
|
const status = str(a.Status);
|
|
@@ -2116,7 +2240,8 @@ function buildNeedsFraming(assumptions) {
|
|
|
2116
2240
|
const stage = str(a.Stage) ?? "\u2014";
|
|
2117
2241
|
const title = str(a.Title) ?? id;
|
|
2118
2242
|
const hint = framingHint(a, completeness);
|
|
2119
|
-
|
|
2243
|
+
const lensColour = LENS_COLOUR[lens] ?? LENS_COLOUR["Investor"] ?? "#6b7484";
|
|
2244
|
+
return { id, title, risk, completeness, lens, stage, hint, lensColour };
|
|
2120
2245
|
}).filter((a) => a.completeness < 100);
|
|
2121
2246
|
const byLens = /* @__PURE__ */ new Map();
|
|
2122
2247
|
for (const item of items) {
|
|
@@ -2166,22 +2291,28 @@ function buildRecommendedExperiments(assumptions, experiments) {
|
|
|
2166
2291
|
if (bucket) bucket.push(a);
|
|
2167
2292
|
else clusters.set(key, [a]);
|
|
2168
2293
|
}
|
|
2169
|
-
const
|
|
2294
|
+
const rankedClusters = [...clusters.entries()].map(([key, cluster]) => {
|
|
2170
2295
|
const ranked = cluster.sort((a, b) => (derivedNum(b, "risk") ?? 0) - (derivedNum(a, "risk") ?? 0)).slice(0, MAX_CLUSTER_SIZE);
|
|
2171
2296
|
const maxRisk = Math.max(...ranked.map((a) => derivedNum(a, "risk") ?? 0), 0);
|
|
2172
2297
|
return { key, cluster: ranked, maxRisk };
|
|
2173
|
-
}).sort((a, b) => b.maxRisk - a.maxRisk)
|
|
2298
|
+
}).sort((a, b) => b.maxRisk - a.maxRisk);
|
|
2299
|
+
const byLens = /* @__PURE__ */ new Map();
|
|
2300
|
+
for (const entry of rankedClusters) {
|
|
2301
|
+
const lens = entry.key.split("\xD7")[0] ?? "\u2014";
|
|
2302
|
+
if (!byLens.has(lens)) byLens.set(lens, entry);
|
|
2303
|
+
}
|
|
2304
|
+
const clusterEntries = [...byLens.values()].sort((a, b) => b.maxRisk - a.maxRisk).slice(0, MAX_RECOMMENDED);
|
|
2174
2305
|
const recs = [];
|
|
2175
2306
|
for (const { key, cluster, maxRisk } of clusterEntries) {
|
|
2176
2307
|
const lens = key.split("\xD7")[0] ?? "\u2014";
|
|
2177
2308
|
const stage = key.split("\xD7")[1] ?? "\u2014";
|
|
2178
2309
|
const assumptionIds = cluster.map((a) => str(a.id) ?? "").filter(Boolean).sort();
|
|
2179
2310
|
const type = LENS_TO_TYPE[lens ?? ""] ?? "Desk research";
|
|
2180
|
-
const titles = cluster.map((a) => str(a.Title) ?? str(a.id) ?? "");
|
|
2181
2311
|
const title = cluster.length === 1 ? `Test ${assumptionIds[0]}` : `Test ${cluster.length} ${lens} \xB7 ${stage} beliefs`;
|
|
2182
2312
|
const rationale = cluster.length === 1 ? `One belief (${assumptionIds[0]}) at ${Math.round(maxRisk)} risk with no live test. Designing an experiment here would buy down the most risk.` : `${cluster.length} beliefs share ${lens} \xB7 ${stage}, the riskiest at ${Math.round(maxRisk)} risk. One experiment can address them all.`;
|
|
2183
2313
|
const barPreview = `The riskiest belief moves out of the kill zone, or stays in it.`;
|
|
2184
2314
|
const body = generateExperimentBody(type, lens, stage, cluster, maxRisk);
|
|
2315
|
+
const lensColour = LENS_COLOUR[lens] ?? LENS_COLOUR["Investor"] ?? "#6b7484";
|
|
2185
2316
|
recs.push({
|
|
2186
2317
|
id: assumptionIds.join("+"),
|
|
2187
2318
|
type,
|
|
@@ -2190,7 +2321,9 @@ function buildRecommendedExperiments(assumptions, experiments) {
|
|
|
2190
2321
|
maxRisk,
|
|
2191
2322
|
rationale,
|
|
2192
2323
|
barPreview,
|
|
2193
|
-
body
|
|
2324
|
+
body,
|
|
2325
|
+
lens,
|
|
2326
|
+
lensColour
|
|
2194
2327
|
});
|
|
2195
2328
|
}
|
|
2196
2329
|
recs.sort(
|
|
@@ -2675,14 +2808,12 @@ function NextMovesSection({
|
|
|
2675
2808
|
recs,
|
|
2676
2809
|
onOpenAssumption
|
|
2677
2810
|
}) {
|
|
2811
|
+
const [openRec, setOpenRec] = useState3(null);
|
|
2678
2812
|
return /* @__PURE__ */ jsxs5("div", { className: "vos-next-moves", children: [
|
|
2679
2813
|
/* @__PURE__ */ jsx5("div", { className: "vos-next-moves-head", children: "Next moves" }),
|
|
2680
2814
|
/* @__PURE__ */ jsxs5("div", { className: "vos-next-moves-cols", children: [
|
|
2681
2815
|
/* @__PURE__ */ jsxs5("div", { className: "vos-card vos-next-moves-col", children: [
|
|
2682
|
-
/* @__PURE__ */
|
|
2683
|
-
"Needs framing \xB7 top ",
|
|
2684
|
-
needsFraming.length
|
|
2685
|
-
] }),
|
|
2816
|
+
/* @__PURE__ */ jsx5("div", { className: "vos-next-moves-col-label", children: "Needs framing \xB7 one per lens" }),
|
|
2686
2817
|
needsFraming.length === 0 ? /* @__PURE__ */ jsx5("div", { className: "vos-muted vos-next-moves-empty", children: "Every belief is fully framed." }) : needsFraming.map((a) => /* @__PURE__ */ jsxs5(
|
|
2687
2818
|
"button",
|
|
2688
2819
|
{
|
|
@@ -2691,13 +2822,24 @@ function NextMovesSection({
|
|
|
2691
2822
|
onClick: () => onOpenAssumption(a.id),
|
|
2692
2823
|
children: [
|
|
2693
2824
|
/* @__PURE__ */ jsxs5("div", { className: "vos-next-moves-item-head", children: [
|
|
2694
|
-
/* @__PURE__ */ jsx5(
|
|
2825
|
+
/* @__PURE__ */ jsx5(
|
|
2826
|
+
"span",
|
|
2827
|
+
{
|
|
2828
|
+
className: "vos-next-moves-lens",
|
|
2829
|
+
style: { background: `${a.lensColour}20`, color: a.lensColour, borderColor: `${a.lensColour}40` },
|
|
2830
|
+
children: a.lens
|
|
2831
|
+
}
|
|
2832
|
+
),
|
|
2695
2833
|
/* @__PURE__ */ jsxs5("span", { className: `vos-next-moves-item-risk vos-num vos-text-${riskToneClass(a.risk)}`, children: [
|
|
2696
2834
|
Math.round(a.risk),
|
|
2697
2835
|
" risk"
|
|
2698
2836
|
] })
|
|
2699
2837
|
] }),
|
|
2700
|
-
/* @__PURE__ */
|
|
2838
|
+
/* @__PURE__ */ jsxs5("div", { className: "vos-next-moves-item-title", children: [
|
|
2839
|
+
/* @__PURE__ */ jsx5("span", { className: "vos-next-moves-item-id vos-num", children: a.id }),
|
|
2840
|
+
" \xB7 ",
|
|
2841
|
+
a.title
|
|
2842
|
+
] }),
|
|
2701
2843
|
/* @__PURE__ */ jsx5("div", { className: "vos-next-moves-item-hint", children: a.hint })
|
|
2702
2844
|
]
|
|
2703
2845
|
},
|
|
@@ -2705,42 +2847,119 @@ function NextMovesSection({
|
|
|
2705
2847
|
))
|
|
2706
2848
|
] }),
|
|
2707
2849
|
/* @__PURE__ */ jsxs5("div", { className: "vos-card vos-next-moves-col", children: [
|
|
2708
|
-
/* @__PURE__ */
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2850
|
+
/* @__PURE__ */ jsx5("div", { className: "vos-next-moves-col-label", children: "Proposed experiments \xB7 one per lens" }),
|
|
2851
|
+
recs.length === 0 ? /* @__PURE__ */ jsx5("div", { className: "vos-muted vos-next-moves-empty", children: "Every risk has a live test." }) : recs.map((rec) => /* @__PURE__ */ jsxs5(
|
|
2852
|
+
"button",
|
|
2853
|
+
{
|
|
2854
|
+
type: "button",
|
|
2855
|
+
className: "vos-next-moves-rec vos-next-moves-rec-compact",
|
|
2856
|
+
onClick: () => setOpenRec(rec),
|
|
2857
|
+
children: [
|
|
2858
|
+
/* @__PURE__ */ jsxs5("div", { className: "vos-next-moves-rec-head", children: [
|
|
2859
|
+
/* @__PURE__ */ jsx5(
|
|
2860
|
+
"span",
|
|
2861
|
+
{
|
|
2862
|
+
className: "vos-next-moves-lens",
|
|
2863
|
+
style: { background: `${rec.lensColour}20`, color: rec.lensColour, borderColor: `${rec.lensColour}40` },
|
|
2864
|
+
children: rec.lens
|
|
2865
|
+
}
|
|
2866
|
+
),
|
|
2867
|
+
/* @__PURE__ */ jsx5("span", { className: "vos-next-moves-rec-type", children: rec.type }),
|
|
2868
|
+
/* @__PURE__ */ jsxs5("span", { className: "vos-next-moves-rec-risk vos-num", children: [
|
|
2869
|
+
Math.round(rec.maxRisk),
|
|
2870
|
+
" risk"
|
|
2871
|
+
] })
|
|
2872
|
+
] }),
|
|
2873
|
+
/* @__PURE__ */ jsx5("div", { className: "vos-next-moves-rec-title", children: rec.title }),
|
|
2874
|
+
/* @__PURE__ */ jsx5("span", { className: "vos-next-moves-rec-open", "aria-hidden": "true", children: "\u2192" })
|
|
2875
|
+
]
|
|
2876
|
+
},
|
|
2877
|
+
rec.id
|
|
2878
|
+
))
|
|
2879
|
+
] })
|
|
2880
|
+
] }),
|
|
2881
|
+
openRec ? /* @__PURE__ */ jsx5(
|
|
2882
|
+
RecommendedExperimentDrawer,
|
|
2883
|
+
{
|
|
2884
|
+
rec: openRec,
|
|
2885
|
+
onOpenAssumption,
|
|
2886
|
+
onClose: () => setOpenRec(null)
|
|
2887
|
+
}
|
|
2888
|
+
) : null
|
|
2889
|
+
] });
|
|
2890
|
+
}
|
|
2891
|
+
function RecommendedExperimentDrawer({
|
|
2892
|
+
rec,
|
|
2893
|
+
onOpenAssumption,
|
|
2894
|
+
onClose
|
|
2895
|
+
}) {
|
|
2896
|
+
return /* @__PURE__ */ jsxs5(Fragment4, { children: [
|
|
2897
|
+
/* @__PURE__ */ jsx5("div", { className: "vos-scrim", onClick: onClose, "aria-hidden": "true" }),
|
|
2898
|
+
/* @__PURE__ */ jsxs5(
|
|
2899
|
+
"aside",
|
|
2900
|
+
{
|
|
2901
|
+
className: "vos-drawer vos-rec-drawer",
|
|
2902
|
+
role: "dialog",
|
|
2903
|
+
"aria-modal": "true",
|
|
2904
|
+
"aria-label": `Proposed experiment: ${rec.title}`,
|
|
2905
|
+
children: [
|
|
2906
|
+
/* @__PURE__ */ jsxs5("div", { className: "vos-drawer-header", children: [
|
|
2907
|
+
/* @__PURE__ */ jsxs5("div", { children: [
|
|
2908
|
+
/* @__PURE__ */ jsx5("div", { className: "vos-drawer-eyebrow", children: "Proposed experiment" }),
|
|
2909
|
+
/* @__PURE__ */ jsx5("h2", { className: "vos-drawer-title", children: rec.title })
|
|
2910
|
+
] }),
|
|
2911
|
+
/* @__PURE__ */ jsxs5("div", { style: { marginLeft: "auto", display: "flex", gap: 8, alignItems: "center" }, children: [
|
|
2912
|
+
/* @__PURE__ */ jsx5(
|
|
2913
|
+
"span",
|
|
2914
|
+
{
|
|
2915
|
+
className: "vos-next-moves-lens",
|
|
2916
|
+
style: { background: `${rec.lensColour}20`, color: rec.lensColour, borderColor: `${rec.lensColour}40` },
|
|
2917
|
+
children: rec.lens
|
|
2918
|
+
}
|
|
2919
|
+
),
|
|
2920
|
+
/* @__PURE__ */ jsx5("span", { className: "vos-next-moves-rec-type", children: rec.type }),
|
|
2921
|
+
/* @__PURE__ */ jsxs5("span", { className: "vos-next-moves-rec-risk vos-num", children: [
|
|
2922
|
+
Math.round(rec.maxRisk),
|
|
2923
|
+
" risk"
|
|
2924
|
+
] }),
|
|
2925
|
+
/* @__PURE__ */ jsx5("button", { type: "button", className: "vos-btn vos-btn-sm", onClick: onClose, "aria-label": "Close", children: "\u2715" })
|
|
2719
2926
|
] })
|
|
2720
2927
|
] }),
|
|
2721
|
-
/* @__PURE__ */ jsxs5("div", { className: "vos-
|
|
2722
|
-
/* @__PURE__ */
|
|
2723
|
-
"
|
|
2724
|
-
{
|
|
2725
|
-
type: "button",
|
|
2726
|
-
className: "vos-pipe-rec-chip",
|
|
2727
|
-
onClick: () => onOpenAssumption(id),
|
|
2728
|
-
children: id
|
|
2729
|
-
},
|
|
2730
|
-
id
|
|
2731
|
-
)) }),
|
|
2732
|
-
/* @__PURE__ */ jsx5("div", { className: "vos-pipe-rec-rationale", children: rec.rationale }),
|
|
2733
|
-
/* @__PURE__ */ jsxs5("div", { className: "vos-pipe-rec-bar", children: [
|
|
2734
|
-
/* @__PURE__ */ jsx5("strong", { children: "Right if:" }),
|
|
2735
|
-
" ",
|
|
2736
|
-
rec.barPreview
|
|
2928
|
+
/* @__PURE__ */ jsxs5("div", { className: "vos-drawer-body", children: [
|
|
2929
|
+
/* @__PURE__ */ jsxs5("div", { className: "vos-rec-drawer-section", children: [
|
|
2930
|
+
/* @__PURE__ */ jsx5("div", { className: "vos-drawer-eyebrow", children: "Why this test" }),
|
|
2931
|
+
/* @__PURE__ */ jsx5("p", { className: "vos-rec-drawer-rationale", children: rec.rationale })
|
|
2737
2932
|
] }),
|
|
2738
|
-
/* @__PURE__ */
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2933
|
+
/* @__PURE__ */ jsxs5("div", { className: "vos-rec-drawer-section", children: [
|
|
2934
|
+
/* @__PURE__ */ jsx5("div", { className: "vos-drawer-eyebrow", children: "Bar" }),
|
|
2935
|
+
/* @__PURE__ */ jsxs5("div", { className: "vos-rec-drawer-bar", children: [
|
|
2936
|
+
/* @__PURE__ */ jsx5("strong", { children: "Right if:" }),
|
|
2937
|
+
" ",
|
|
2938
|
+
rec.barPreview
|
|
2939
|
+
] })
|
|
2940
|
+
] }),
|
|
2941
|
+
/* @__PURE__ */ jsxs5("div", { className: "vos-rec-drawer-section", children: [
|
|
2942
|
+
/* @__PURE__ */ jsx5("div", { className: "vos-drawer-eyebrow", children: "Assumptions" }),
|
|
2943
|
+
/* @__PURE__ */ jsx5("div", { className: "vos-next-moves-rec-chips", children: rec.assumptionIds.map((id) => /* @__PURE__ */ jsx5(
|
|
2944
|
+
"button",
|
|
2945
|
+
{
|
|
2946
|
+
type: "button",
|
|
2947
|
+
className: "vos-next-moves-rec-chip",
|
|
2948
|
+
onClick: () => onOpenAssumption(id),
|
|
2949
|
+
children: id
|
|
2950
|
+
},
|
|
2951
|
+
id
|
|
2952
|
+
)) })
|
|
2953
|
+
] }),
|
|
2954
|
+
/* @__PURE__ */ jsxs5("div", { className: "vos-rec-drawer-section", children: [
|
|
2955
|
+
/* @__PURE__ */ jsx5("div", { className: "vos-drawer-eyebrow", children: "Protocol" }),
|
|
2956
|
+
/* @__PURE__ */ jsx5("div", { className: "vos-rec-drawer-body", children: /* @__PURE__ */ jsx5(EvidenceBody, { text: rec.body }) })
|
|
2957
|
+
] })
|
|
2958
|
+
] }),
|
|
2959
|
+
/* @__PURE__ */ jsx5("div", { className: "vos-drawer-footer", children: /* @__PURE__ */ jsx5("button", { type: "button", className: "vos-btn vos-btn-sm vos-btn-accent", children: "Accept & create experiment" }) })
|
|
2960
|
+
]
|
|
2961
|
+
}
|
|
2962
|
+
)
|
|
2744
2963
|
] });
|
|
2745
2964
|
}
|
|
2746
2965
|
function riskToneClass(risk) {
|
|
@@ -2766,8 +2985,8 @@ function ConfidenceDonut({
|
|
|
2766
2985
|
value,
|
|
2767
2986
|
size = 56
|
|
2768
2987
|
}) {
|
|
2769
|
-
const stroke = size > 60 ?
|
|
2770
|
-
const r = (size - stroke) / 2;
|
|
2988
|
+
const stroke = size > 60 ? 4 : 3;
|
|
2989
|
+
const r = (size - stroke * 3) / 2;
|
|
2771
2990
|
const cx = size / 2;
|
|
2772
2991
|
const cy = size / 2;
|
|
2773
2992
|
const circumference = 2 * Math.PI * r;
|
|
@@ -2783,7 +3002,7 @@ function ConfidenceDonut({
|
|
|
2783
3002
|
cy,
|
|
2784
3003
|
r,
|
|
2785
3004
|
fill: "none",
|
|
2786
|
-
stroke: "var(--vos-
|
|
3005
|
+
stroke: "var(--vos-border)",
|
|
2787
3006
|
strokeWidth: stroke
|
|
2788
3007
|
}
|
|
2789
3008
|
),
|
|
@@ -2807,7 +3026,7 @@ function ConfidenceDonut({
|
|
|
2807
3026
|
"span",
|
|
2808
3027
|
{
|
|
2809
3028
|
className: "vos-donut-num vos-num",
|
|
2810
|
-
style: { fontSize: size > 60 ?
|
|
3029
|
+
style: { fontSize: size > 60 ? 22 : 15 },
|
|
2811
3030
|
children: Math.round(value)
|
|
2812
3031
|
}
|
|
2813
3032
|
)
|
|
@@ -2948,11 +3167,11 @@ function ExperimentDetail({
|
|
|
2948
3167
|
/* @__PURE__ */ jsxs7("div", { className: "vos-detail-section-label", children: [
|
|
2949
3168
|
"Evidence \xB7 ",
|
|
2950
3169
|
expReadings.length,
|
|
2951
|
-
"
|
|
3170
|
+
" piece",
|
|
2952
3171
|
expReadings.length === 1 ? "" : "s",
|
|
2953
3172
|
" from this experiment"
|
|
2954
3173
|
] }),
|
|
2955
|
-
expReadings.length === 0 ? /* @__PURE__ */ jsx7("div", { className: "vos-muted vos-empty", children: "No
|
|
3174
|
+
expReadings.length === 0 ? /* @__PURE__ */ jsx7("div", { className: "vos-muted vos-empty", children: "No evidence yet \u2014 experiment is running." }) : expReadings.map((r) => {
|
|
2956
3175
|
const beliefs = readingBeliefs(r);
|
|
2957
3176
|
const rDate = String(r.Date ?? "");
|
|
2958
3177
|
const rTitle = String(r.Title ?? r.id);
|
|
@@ -2999,10 +3218,20 @@ function ExperimentDetail({
|
|
|
2999
3218
|
/* @__PURE__ */ jsx7("span", { className: `vos-pill vos-pill-${verdictTone2(b.Result)}`, children: b.Result }),
|
|
3000
3219
|
/* @__PURE__ */ jsx7("span", { className: "vos-rung-tag", children: String(r.Rung ?? "") })
|
|
3001
3220
|
] }),
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
|
|
3005
|
-
|
|
3221
|
+
typeof b.excerpt === "string" && b.excerpt !== "" ? /* @__PURE__ */ jsxs7("div", { className: `vos-belief-excerpt vos-verdict-border-${verdictTone2(b.Result)}`, children: [
|
|
3222
|
+
"\u201C",
|
|
3223
|
+
b.excerpt,
|
|
3224
|
+
"\u201D"
|
|
3225
|
+
] }) : justification ? /* @__PURE__ */ jsxs7(
|
|
3226
|
+
"div",
|
|
3227
|
+
{
|
|
3228
|
+
className: `vos-belief-rationale vos-verdict-border-${verdictTone2(b.Result)}`,
|
|
3229
|
+
children: [
|
|
3230
|
+
/* @__PURE__ */ jsx7("span", { className: "vos-belief-rationale-label", children: "grading rationale:" }),
|
|
3231
|
+
justification
|
|
3232
|
+
]
|
|
3233
|
+
}
|
|
3234
|
+
) : null,
|
|
3006
3235
|
bl ? /* @__PURE__ */ jsxs7("div", { className: "vos-belief-bar", children: [
|
|
3007
3236
|
/* @__PURE__ */ jsx7("div", { className: "vos-belief-bar-label", children: "Pre-registered bar" }),
|
|
3008
3237
|
/* @__PURE__ */ jsxs7("div", { children: [
|
|
@@ -3032,7 +3261,7 @@ function ExperimentDetail({
|
|
|
3032
3261
|
unstartedBars.length,
|
|
3033
3262
|
" bar",
|
|
3034
3263
|
unstartedBars.length === 1 ? "" : "s",
|
|
3035
|
-
" with no
|
|
3264
|
+
" with no evidence"
|
|
3036
3265
|
] }),
|
|
3037
3266
|
unstartedBars.map((bl) => {
|
|
3038
3267
|
const a = (assumptions.records ?? []).find((x) => String(x.id) === bl.assumptionId);
|
|
@@ -3049,7 +3278,7 @@ function ExperimentDetail({
|
|
|
3049
3278
|
),
|
|
3050
3279
|
/* @__PURE__ */ jsx7("span", { className: "vos-belief-title", children: String(a?.Title ?? bl.assumptionId) }),
|
|
3051
3280
|
/* @__PURE__ */ jsx7("span", { className: "vos-rung-tag", children: String(bl.plannedRung ?? "") }),
|
|
3052
|
-
/* @__PURE__ */ jsx7("span", { className: "vos-muted", children: "\u25CC no
|
|
3281
|
+
/* @__PURE__ */ jsx7("span", { className: "vos-muted", children: "\u25CC no evidence" })
|
|
3053
3282
|
] }),
|
|
3054
3283
|
/* @__PURE__ */ jsxs7("div", { className: "vos-belief-bar", children: [
|
|
3055
3284
|
/* @__PURE__ */ jsxs7("div", { children: [
|
|
@@ -3168,15 +3397,15 @@ function ReadingDetail({
|
|
|
3168
3397
|
);
|
|
3169
3398
|
if (loading) {
|
|
3170
3399
|
return /* @__PURE__ */ jsxs9("div", { children: [
|
|
3171
|
-
/* @__PURE__ */ jsx9(Breadcrumb, { trail: [{ label: "
|
|
3172
|
-
/* @__PURE__ */ jsx9("p", { className: "vos-muted", children: "Loading
|
|
3400
|
+
/* @__PURE__ */ jsx9(Breadcrumb, { trail: [{ label: "Evidence", route: { name: "readings" } }], onNavigate }),
|
|
3401
|
+
/* @__PURE__ */ jsx9("p", { className: "vos-muted", children: "Loading evidence\u2026" })
|
|
3173
3402
|
] });
|
|
3174
3403
|
}
|
|
3175
3404
|
if (!reading) {
|
|
3176
3405
|
return /* @__PURE__ */ jsxs9("div", { children: [
|
|
3177
|
-
/* @__PURE__ */ jsx9(Breadcrumb, { trail: [{ label: "
|
|
3406
|
+
/* @__PURE__ */ jsx9(Breadcrumb, { trail: [{ label: "Evidence", route: { name: "readings" } }], onNavigate }),
|
|
3178
3407
|
/* @__PURE__ */ jsxs9("p", { className: "vos-error", children: [
|
|
3179
|
-
"
|
|
3408
|
+
"Evidence not found: ",
|
|
3180
3409
|
readingId
|
|
3181
3410
|
] })
|
|
3182
3411
|
] });
|
|
@@ -3193,7 +3422,7 @@ function ReadingDetail({
|
|
|
3193
3422
|
Breadcrumb,
|
|
3194
3423
|
{
|
|
3195
3424
|
trail: [
|
|
3196
|
-
{ label: "
|
|
3425
|
+
{ label: "Evidence", route: { name: "readings" } },
|
|
3197
3426
|
{ label: readingId, route: { name: "reading", id: readingId } }
|
|
3198
3427
|
],
|
|
3199
3428
|
onNavigate
|
|
@@ -3217,7 +3446,7 @@ function ReadingDetail({
|
|
|
3217
3446
|
beliefs.length === 1 ? "" : "s",
|
|
3218
3447
|
" \xB7 each with its own quote"
|
|
3219
3448
|
] }),
|
|
3220
|
-
beliefs.length === 0 ? /* @__PURE__ */ jsx9("div", { className: "vos-muted", children: "No beliefs scored in this
|
|
3449
|
+
beliefs.length === 0 ? /* @__PURE__ */ jsx9("div", { className: "vos-muted", children: "No beliefs scored in this evidence." }) : beliefs.map((b) => {
|
|
3221
3450
|
const a = (assumptions.records ?? []).find((x) => String(x.id) === b.assumptionId);
|
|
3222
3451
|
const bl = experiment && Array.isArray(experiment.barLines) ? experiment.barLines.find((x) => x?.assumptionId === b.assumptionId) : null;
|
|
3223
3452
|
const result = String(b.Result ?? "Inconclusive");
|
|
@@ -3237,11 +3466,19 @@ function ReadingDetail({
|
|
|
3237
3466
|
/* @__PURE__ */ jsx9("span", { className: `vos-pill vos-pill-${verdictTone3(result)}`, children: result }),
|
|
3238
3467
|
/* @__PURE__ */ jsx9("span", { className: "vos-rung-tag", children: rung })
|
|
3239
3468
|
] }),
|
|
3469
|
+
typeof b.excerpt === "string" && b.excerpt !== "" ? /* @__PURE__ */ jsxs9("div", { className: `vos-belief-excerpt vos-verdict-border-${verdictTone3(result)}`, children: [
|
|
3470
|
+
"\u201C",
|
|
3471
|
+
b.excerpt,
|
|
3472
|
+
"\u201D"
|
|
3473
|
+
] }) : body ? /* @__PURE__ */ jsxs9("div", { className: `vos-belief-excerpt vos-verdict-border-${verdictTone3(result)}`, children: [
|
|
3474
|
+
"\u201C",
|
|
3475
|
+
snippetFromReadingBody(body, b.assumptionId),
|
|
3476
|
+
"\u201D"
|
|
3477
|
+
] }) : null,
|
|
3240
3478
|
justification ? /* @__PURE__ */ jsxs9("div", { className: `vos-belief-rationale vos-verdict-border-${verdictTone3(result)}`, children: [
|
|
3241
3479
|
/* @__PURE__ */ jsx9("span", { className: "vos-belief-rationale-label", children: "grading rationale:" }),
|
|
3242
3480
|
justification
|
|
3243
3481
|
] }) : null,
|
|
3244
|
-
body ? /* @__PURE__ */ jsx9("div", { className: "vos-belief-context-link vos-muted", children: "see context above for the full quote" }) : null,
|
|
3245
3482
|
bl ? /* @__PURE__ */ jsxs9("div", { className: "vos-belief-bar", children: [
|
|
3246
3483
|
/* @__PURE__ */ jsx9("div", { className: "vos-belief-bar-label", children: "Pre-registered bar" }),
|
|
3247
3484
|
/* @__PURE__ */ jsxs9("div", { children: [
|
|
@@ -3288,6 +3525,21 @@ function verdictTone3(verdict) {
|
|
|
3288
3525
|
if (verdict === "Invalidated") return "crit";
|
|
3289
3526
|
return "neutral";
|
|
3290
3527
|
}
|
|
3528
|
+
function snippetFromReadingBody(body, cue) {
|
|
3529
|
+
if (!body) return "";
|
|
3530
|
+
const quoteMatch = body.match(/## Quote\n+([\s\S]*?)(?=\n## |\n##$|$)/i);
|
|
3531
|
+
if (quoteMatch) {
|
|
3532
|
+
const q = quoteMatch[1].trim();
|
|
3533
|
+
return q.length > 220 ? q.slice(0, 217).trim() + "\u2026" : q;
|
|
3534
|
+
}
|
|
3535
|
+
const sentences = body.split(/(?<=[.!?])\s+/);
|
|
3536
|
+
const cueLower = cue.toLowerCase();
|
|
3537
|
+
for (const s of sentences) {
|
|
3538
|
+
if (s.toLowerCase().includes(cueLower)) return s.trim();
|
|
3539
|
+
}
|
|
3540
|
+
const first = sentences[0]?.trim() ?? "";
|
|
3541
|
+
return first.length > 220 ? first.slice(0, 217).trim() + "\u2026" : first;
|
|
3542
|
+
}
|
|
3291
3543
|
|
|
3292
3544
|
// src/readings-surface.tsx
|
|
3293
3545
|
import { jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
@@ -5119,6 +5371,11 @@ function BeliefVerdicts({
|
|
|
5119
5371
|
"Strength ",
|
|
5120
5372
|
formatSigned(v.strength)
|
|
5121
5373
|
] }) }) : null,
|
|
5374
|
+
v.excerpt ? /* @__PURE__ */ jsxs17("p", { className: "vos-verdict-quote", children: [
|
|
5375
|
+
"\u201C",
|
|
5376
|
+
v.excerpt,
|
|
5377
|
+
"\u201D"
|
|
5378
|
+
] }) : null,
|
|
5122
5379
|
v.justification ? /* @__PURE__ */ jsx17("p", { className: "vos-verdict-why", children: v.justification }) : null
|
|
5123
5380
|
]
|
|
5124
5381
|
},
|