@elaraai/e3-ui-components 1.0.30 → 1.0.31
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/experiment/charts.d.ts.map +1 -1
- package/dist/experiment/derive.d.ts +33 -1
- package/dist/experiment/derive.d.ts.map +1 -1
- package/dist/experiment/help-ui.d.ts +11 -0
- package/dist/experiment/help-ui.d.ts.map +1 -1
- package/dist/experiment/help.d.ts +2 -2
- package/dist/experiment/index.d.ts.map +1 -1
- package/dist/index.cjs +315 -81
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +315 -81
- package/dist/index.js.map +1 -1
- package/package.json +13 -13
package/dist/index.cjs
CHANGED
|
@@ -59938,7 +59938,7 @@ const HELP = {
|
|
|
59938
59938
|
},
|
|
59939
59939
|
// ── Tabs ────────────────────────────────────────────────────────────
|
|
59940
59940
|
tab_answer: {
|
|
59941
|
-
term: "
|
|
59941
|
+
term: "The answer",
|
|
59942
59942
|
gist: "The headline: did {treatment} move {outcome}, and by how much?",
|
|
59943
59943
|
detail: "Shows the raw gap, the fair like-for-like estimate, and an honest verdict."
|
|
59944
59944
|
},
|
|
@@ -60126,7 +60126,7 @@ const HELP = {
|
|
|
60126
60126
|
},
|
|
60127
60127
|
// ── Validate tab ────────────────────────────────────────────────────
|
|
60128
60128
|
tab_validate: {
|
|
60129
|
-
term: "
|
|
60129
|
+
term: "Prove it",
|
|
60130
60130
|
gist: "The real experiment you’d run to prove this — for sure.",
|
|
60131
60131
|
detail: "This analysis adjusted for what it could measure. A controlled trial — assigning {treatment} at random — removes any leftover doubt. This tab sizes that trial."
|
|
60132
60132
|
},
|
|
@@ -60215,6 +60215,12 @@ function Help({ id: id3, extraVars, children: children2, display = "inline", gap
|
|
|
60215
60215
|
)
|
|
60216
60216
|
) });
|
|
60217
60217
|
}
|
|
60218
|
+
function GuidanceGist({ id: id3, extraVars }) {
|
|
60219
|
+
const { on: on3, vars } = React.useContext(GuidanceContext);
|
|
60220
|
+
if (!on3) return null;
|
|
60221
|
+
const e3 = fillHelp(HELP[id3], { ...vars, ...extraVars });
|
|
60222
|
+
return /* @__PURE__ */ jsxRuntime.jsx(react.Text, { textStyle: "caption", color: "fg.muted", lineHeight: "1.45", mt: "-1.5", mb: "2.5", children: e3.gist });
|
|
60223
|
+
}
|
|
60218
60224
|
function GuidanceToggle({ on: on3, onToggle }) {
|
|
60219
60225
|
const iconButton = react.useRecipe({ key: "iconButton" });
|
|
60220
60226
|
return /* @__PURE__ */ jsxRuntime.jsx(Hover, { id: "guidance", trigger: /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -60369,8 +60375,8 @@ function AreaRange({ lo: lo2, mid, hi, xTicks = [], yTicks = [], zero: zero2, to
|
|
|
60369
60375
|
const atEnd = m2.at >= mid.length - 2;
|
|
60370
60376
|
const atStart = m2.at <= 1;
|
|
60371
60377
|
const align = atEnd ? "right" : atStart ? "left" : "center";
|
|
60372
|
-
const labW = m2.label.length * 6 +
|
|
60373
|
-
const labX = atEnd ? -labW : atStart ?
|
|
60378
|
+
const labW = m2.label.length * 6.5 + 8;
|
|
60379
|
+
const labX = atEnd ? -labW - 5 : atStart ? 5 : -labW / 2;
|
|
60374
60380
|
return /* @__PURE__ */ jsxRuntime.jsxs(Group, { left: xScale(m2.at), children: [
|
|
60375
60381
|
/* @__PURE__ */ jsxRuntime.jsx(Line, { from: { x: 0, y: -2 }, to: { x: 0, y: innerH }, stroke: mc, strokeWidth: 1, strokeDasharray: "2 3" }),
|
|
60376
60382
|
/* @__PURE__ */ jsxRuntime.jsx(Circle, { cx: 0, cy: dotY, r: t4.dotRadius, fill: mc, stroke: t4.surface, strokeWidth: 1.5 }),
|
|
@@ -60441,6 +60447,10 @@ const unitOf = (meta3, col) => {
|
|
|
60441
60447
|
var _a2;
|
|
60442
60448
|
return eastUiComponents.getSomeorUndefined((_a2 = colMeta(meta3, col)) == null ? void 0 : _a2.unit) ?? "";
|
|
60443
60449
|
};
|
|
60450
|
+
const labelOf = (meta3, col) => {
|
|
60451
|
+
var _a2;
|
|
60452
|
+
return eastUiComponents.getSomeorUndefined((_a2 = colMeta(meta3, col)) == null ? void 0 : _a2.label) ?? col;
|
|
60453
|
+
};
|
|
60444
60454
|
const kindOf = (cols, name) => {
|
|
60445
60455
|
var _a2;
|
|
60446
60456
|
return (_a2 = cols.find((c2) => c2.name === name)) == null ? void 0 : _a2.kind;
|
|
@@ -60476,8 +60486,7 @@ function treatmentKind(cols, treatment) {
|
|
|
60476
60486
|
return "value";
|
|
60477
60487
|
}
|
|
60478
60488
|
}
|
|
60479
|
-
function deriveSpec(config2, cols, result, meta3, dataLen) {
|
|
60480
|
-
const tKind = kindOf(cols, config2.treatment);
|
|
60489
|
+
function deriveSpec(config2, cols, result, meta3, dataLen, subject) {
|
|
60481
60490
|
const outUnit = unitOf(meta3, config2.outcome);
|
|
60482
60491
|
const balByCol = /* @__PURE__ */ new Map();
|
|
60483
60492
|
for (const b2 of (result == null ? void 0 : result.balance) ?? []) {
|
|
@@ -60489,7 +60498,7 @@ function deriveSpec(config2, cols, result, meta3, dataLen) {
|
|
|
60489
60498
|
const absStd = b2 ? Math.abs(b2.std_diff) : 0;
|
|
60490
60499
|
const { level, tone } = band(absStd);
|
|
60491
60500
|
const dir = b2 ? b2.treated_mean < b2.control_mean ? "lower" : "higher" : "";
|
|
60492
|
-
const reason = b2 ? `the treated group ran ${dir} on ${col}` : "measured once you Run";
|
|
60501
|
+
const reason = b2 ? `the treated group ran ${dir} on ${labelOf(meta3, col)}` : "measured once you Run";
|
|
60493
60502
|
return { col, reason, imbalance: clamp01(absStd), level, tone };
|
|
60494
60503
|
});
|
|
60495
60504
|
const inPlay = /* @__PURE__ */ new Set([config2.treatment, config2.outcome, ...config2.common_causes]);
|
|
@@ -60501,15 +60510,21 @@ function deriveSpec(config2, cols, result, meta3, dataLen) {
|
|
|
60501
60510
|
const target = targetV && (targetV.type === "att" || targetV.type === "atc") ? "treated" : "all";
|
|
60502
60511
|
return {
|
|
60503
60512
|
treatment: config2.treatment,
|
|
60513
|
+
treatmentLabel: labelOf(meta3, config2.treatment),
|
|
60504
60514
|
treatmentKind: treatmentKind(cols, config2.treatment),
|
|
60505
60515
|
outcome: config2.outcome,
|
|
60516
|
+
outcomeLabel: labelOf(meta3, config2.outcome),
|
|
60506
60517
|
outcomeKind: outUnit ? `number · ${outUnit}` : "number",
|
|
60507
|
-
comparison:
|
|
60518
|
+
comparison: kindOf(cols, config2.treatment) === "boolean" ? "yes · vs no" : "high · vs low",
|
|
60508
60519
|
confounders,
|
|
60509
60520
|
suggestion,
|
|
60510
60521
|
method,
|
|
60511
60522
|
target,
|
|
60512
|
-
|
|
60523
|
+
// Always the CURRENT (filtered) dataset's row count — the result's own
|
|
60524
|
+
// n_total belongs to the Answer-tab counts strip; labelling the header
|
|
60525
|
+
// with it desyncs from the data after a population edit (and reads
|
|
60526
|
+
// wrong whenever the two legitimately differ).
|
|
60527
|
+
dataLabel: `${dataLen} ${subject.many}`
|
|
60513
60528
|
};
|
|
60514
60529
|
}
|
|
60515
60530
|
function balanceDisplay(b2, categorical) {
|
|
@@ -60521,7 +60536,9 @@ function deriveAnswer(config2, result, adj, meta3) {
|
|
|
60521
60536
|
const nci = eastUiComponents.getSomeorUndefined(result.naive_ci);
|
|
60522
60537
|
const categorical = new Set(eastUiComponents.getSomeorUndefined(config2.categorical) ?? []);
|
|
60523
60538
|
const balance = [...result.balance].sort((a2, b2) => Math.abs(b2.std_diff) - Math.abs(a2.std_diff)).map((b2) => ({
|
|
60539
|
+
// One-hot rows are `col=level` — label the BASE confounder, keep the level.
|
|
60524
60540
|
col: b2.column,
|
|
60541
|
+
label: b2.column === b2.base_column ? labelOf(meta3, b2.base_column) : `${labelOf(meta3, b2.base_column)}${b2.column.startsWith(b2.base_column) ? b2.column.slice(b2.base_column.length) : ` (${b2.column})`}`,
|
|
60525
60542
|
treated: b2.treated_mean,
|
|
60526
60543
|
control: b2.control_mean,
|
|
60527
60544
|
display: balanceDisplay(b2, categorical),
|
|
@@ -60535,8 +60552,8 @@ function deriveAnswer(config2, result, adj, meta3) {
|
|
|
60535
60552
|
const nDropped = Number(result.n_dropped);
|
|
60536
60553
|
const nTotal = Number(result.n_total);
|
|
60537
60554
|
return {
|
|
60538
|
-
treatment: config2.treatment,
|
|
60539
|
-
outcome: config2.outcome,
|
|
60555
|
+
treatment: labelOf(meta3, config2.treatment),
|
|
60556
|
+
outcome: labelOf(meta3, config2.outcome),
|
|
60540
60557
|
unit: unitOf(meta3, config2.outcome),
|
|
60541
60558
|
naive: result.naive,
|
|
60542
60559
|
naiveLo: (nci == null ? void 0 : nci.lower) ?? result.naive,
|
|
@@ -60555,18 +60572,63 @@ function deriveAnswer(config2, result, adj, meta3) {
|
|
|
60555
60572
|
balance
|
|
60556
60573
|
};
|
|
60557
60574
|
}
|
|
60558
|
-
function
|
|
60575
|
+
function deriveNarrative(result, a2, refute, higherBetter, subject) {
|
|
60576
|
+
if (a2 === null) return null;
|
|
60577
|
+
const unit2 = a2.unit ? ` ${a2.unit}` : "";
|
|
60578
|
+
const dirUp = a2.effect > 0;
|
|
60579
|
+
const verb = higherBetter === void 0 ? dirUp ? "raises" : "lowers" : dirUp === higherBetter ? "improves" : "worsens";
|
|
60580
|
+
const range = ` (likely between ${signed(a2.lo)} and ${signed(a2.hi)})`;
|
|
60581
|
+
const passed = (refute == null ? void 0 : refute.checks.filter((c2) => c2.passed).length) ?? 0;
|
|
60582
|
+
const total = (refute == null ? void 0 : refute.checks.length) ?? 0;
|
|
60583
|
+
const trustClause = total === 0 ? "" : passed === total ? ` It held up against all ${total} ways we tried to break it.` : ` It passed ${passed} of ${total} stress tests — see “Can we trust it?”.`;
|
|
60584
|
+
switch (result.verdict.type) {
|
|
60585
|
+
case "causal":
|
|
60586
|
+
return {
|
|
60587
|
+
tone: "pos",
|
|
60588
|
+
segments: [
|
|
60589
|
+
{ text: "Yes — ", strong: true },
|
|
60590
|
+
{ text: `${a2.treatment} genuinely ${verb} ${a2.outcome}: about ` },
|
|
60591
|
+
{ text: `${signed(a2.effect)}${unit2}`, strong: true },
|
|
60592
|
+
{ text: ` per ${subject.one}${range}.${trustClause}` }
|
|
60593
|
+
]
|
|
60594
|
+
};
|
|
60595
|
+
case "modest": {
|
|
60596
|
+
const tags = new Set(result.verdict_reasons.map((v3) => v3.type));
|
|
60597
|
+
const why = tags.has("not_material") ? ` — the change (${signed(a2.effect)}${unit2}) is smaller than what would matter here (±${fmt$1(result.materiality_threshold)}${unit2}).` : tags.has("ci_spans_zero") ? ` — the likely range (${signed(a2.lo)} to ${signed(a2.hi)}) includes zero.` : tags.has("thin_support") ? " — too few treated and untreated look-alikes overlap to be confident." : tags.has("low_robustness") ? " — a fairly weak hidden factor could overturn it." : a2.lo <= 0 && a2.hi >= 0 ? ` — the likely range (${signed(a2.lo)} to ${signed(a2.hi)}) includes zero.` : ".";
|
|
60598
|
+
return {
|
|
60599
|
+
tone: "warn",
|
|
60600
|
+
segments: [
|
|
60601
|
+
{ text: "Maybe — ", strong: true },
|
|
60602
|
+
{ text: `there may be an effect of ${a2.treatment} on ${a2.outcome}, but it’s too small or unclear to act on` },
|
|
60603
|
+
{ text: why }
|
|
60604
|
+
]
|
|
60605
|
+
};
|
|
60606
|
+
}
|
|
60607
|
+
case "adjustment_insufficient":
|
|
60608
|
+
return {
|
|
60609
|
+
tone: "warn",
|
|
60610
|
+
segments: [
|
|
60611
|
+
{ text: "Don’t act on this yet — ", strong: true },
|
|
60612
|
+
{ text: `we got a number (${signed(a2.effect)}${unit2}), but a stress test failed: something we didn’t adjust for may still be driving it.` }
|
|
60613
|
+
]
|
|
60614
|
+
};
|
|
60615
|
+
default:
|
|
60616
|
+
return null;
|
|
60617
|
+
}
|
|
60618
|
+
}
|
|
60619
|
+
function deriveRefusal(config2, result, meta3, subject) {
|
|
60559
60620
|
const nT = Number(result.n_treated), nC = Number(result.n_control), nTot = Number(result.n_total);
|
|
60621
|
+
const treatment = labelOf(meta3, config2.treatment);
|
|
60560
60622
|
if (result.verdict.type === "not_estimable") {
|
|
60561
60623
|
const reason = result.verdict.value || "The treatment barely varies, so no comparison can be formed.";
|
|
60562
60624
|
return {
|
|
60563
60625
|
kind: "not_estimable",
|
|
60564
|
-
title: `Can’t estimate the effect of ${
|
|
60626
|
+
title: `Can’t estimate the effect of ${treatment}`,
|
|
60565
60627
|
body: reason,
|
|
60566
60628
|
evidence: [
|
|
60567
60629
|
{ label: "treated", value: String(nT) },
|
|
60568
60630
|
{ label: "untreated", value: String(nC) },
|
|
60569
|
-
{ label:
|
|
60631
|
+
{ label: subject.many, value: String(nTot) }
|
|
60570
60632
|
]
|
|
60571
60633
|
};
|
|
60572
60634
|
}
|
|
@@ -60585,12 +60647,12 @@ function deriveRefusal(config2, result) {
|
|
|
60585
60647
|
}
|
|
60586
60648
|
return {
|
|
60587
60649
|
kind: "not_estimable",
|
|
60588
|
-
title: `Can’t estimate the effect of ${
|
|
60650
|
+
title: `Can’t estimate the effect of ${treatment}`,
|
|
60589
60651
|
body: "The engine could not produce a like-for-like estimate for this configuration.",
|
|
60590
60652
|
evidence: [
|
|
60591
60653
|
{ label: "treated", value: String(nT) },
|
|
60592
60654
|
{ label: "untreated", value: String(nC) },
|
|
60593
|
-
{ label:
|
|
60655
|
+
{ label: subject.many, value: String(nTot) }
|
|
60594
60656
|
]
|
|
60595
60657
|
};
|
|
60596
60658
|
}
|
|
@@ -60614,17 +60676,21 @@ function zeroCrossing(xs, ys) {
|
|
|
60614
60676
|
}
|
|
60615
60677
|
return null;
|
|
60616
60678
|
}
|
|
60617
|
-
function deriveRefute(r2, adj) {
|
|
60679
|
+
function deriveRefute(r2, adj, meta3) {
|
|
60618
60680
|
const checks = [];
|
|
60619
60681
|
const est = (adj == null ? void 0 : adj.effect) ?? 0;
|
|
60620
60682
|
const placeboEffect = eastUiComponents.getSomeorUndefined(r2.placebo_effect);
|
|
60621
60683
|
const placeboPasses = eastUiComponents.getSomeorUndefined(r2.placebo_passes);
|
|
60622
60684
|
if (placeboEffect !== void 0 || placeboPasses !== void 0) {
|
|
60685
|
+
const passed = placeboPasses ?? (placeboEffect !== void 0 && Math.abs(placeboEffect) < Math.max(0.5, Math.abs(est) * 0.15));
|
|
60623
60686
|
checks.push({
|
|
60624
60687
|
name: "Shuffle test",
|
|
60688
|
+
short: "shuffle",
|
|
60625
60689
|
desc: "Shuffle which rows were treated; a real effect should collapse to zero.",
|
|
60626
|
-
|
|
60627
|
-
|
|
60690
|
+
// Speak the outcome, not the statistic: on fake labels the effect
|
|
60691
|
+
// should VANISH — say whether it did.
|
|
60692
|
+
value: placeboEffect !== void 0 ? passed ? `vanished (${signed(placeboEffect)})` : `didn’t vanish (${signed(placeboEffect)})` : passed ? "vanished" : "didn’t vanish",
|
|
60693
|
+
passed,
|
|
60628
60694
|
tip: east.some("Randomly re-label which rows were treated. A genuine effect should vanish."),
|
|
60629
60695
|
help: "check_shuffle"
|
|
60630
60696
|
});
|
|
@@ -60632,11 +60698,13 @@ function deriveRefute(r2, adj) {
|
|
|
60632
60698
|
const ds = eastUiComponents.getSomeorUndefined(r2.data_subset_effect);
|
|
60633
60699
|
if (ds !== void 0) {
|
|
60634
60700
|
const dstd = eastUiComponents.getSomeorUndefined(r2.data_subset_std) ?? 0;
|
|
60701
|
+
const passed = Math.abs(ds - est) < Math.max(0.5, Math.abs(est) * 0.2);
|
|
60635
60702
|
checks.push({
|
|
60636
60703
|
name: "Drop-some test",
|
|
60704
|
+
short: "drop-some",
|
|
60637
60705
|
desc: "Re-estimate on random subsamples; a trustworthy effect stays put.",
|
|
60638
|
-
value:
|
|
60639
|
-
passed
|
|
60706
|
+
value: passed ? `stayed at ${signed(ds)} ± ${fmt$1(dstd)}` : `moved to ${signed(ds)} ± ${fmt$1(dstd)}`,
|
|
60707
|
+
passed,
|
|
60640
60708
|
tip: east.none,
|
|
60641
60709
|
help: "check_dropsome"
|
|
60642
60710
|
});
|
|
@@ -60645,8 +60713,9 @@ function deriveRefute(r2, adj) {
|
|
|
60645
60713
|
if (rcc !== void 0) {
|
|
60646
60714
|
checks.push({
|
|
60647
60715
|
name: "Decoy cause",
|
|
60716
|
+
short: "decoy",
|
|
60648
60717
|
desc: "Add an irrelevant random factor; the answer should not move.",
|
|
60649
|
-
value: rcc ? "
|
|
60718
|
+
value: rcc ? "didn’t move" : "moved",
|
|
60650
60719
|
passed: rcc,
|
|
60651
60720
|
tip: east.none,
|
|
60652
60721
|
help: "check_decoy"
|
|
@@ -60654,14 +60723,18 @@ function deriveRefute(r2, adj) {
|
|
|
60654
60723
|
}
|
|
60655
60724
|
const sens = eastUiComponents.getSomeorUndefined(r2.sensitivity);
|
|
60656
60725
|
const evalue = eastUiComponents.getSomeorUndefined(r2.robustness_value);
|
|
60726
|
+
const benchmarks = sens ? [...sens.benchmarks].sort((a2, b2) => b2.strength - a2.strength) : [];
|
|
60657
60727
|
if (sens !== void 0 || evalue !== void 0) {
|
|
60658
60728
|
const strengths = sens ? arr(sens.strengths) : [];
|
|
60659
60729
|
const effects = sens ? arr(sens.effects) : [];
|
|
60660
60730
|
const tip = sens ? zeroCrossing(strengths, effects) : null;
|
|
60661
|
-
const
|
|
60731
|
+
const strongest = benchmarks[0];
|
|
60732
|
+
const value = tip !== null ? strongest !== void 0 ? tip > strongest.strength ? `flips only beyond ${labelOf(meta3, strongest.column)}` : `a cause weaker than ${labelOf(meta3, strongest.column)} could flip it` : `would flip at ${fmt$1(tip)}` : evalue !== void 0 ? `holds (E-value ${fmt$1(evalue)})` : "holds throughout";
|
|
60733
|
+
const scaleNote = strongest !== void 0 ? ` For scale, your strongest known factor (${labelOf(meta3, strongest.column)}) sits at ${fmt$1(strongest.strength)} on this axis.` : "";
|
|
60662
60734
|
checks.push({
|
|
60663
60735
|
name: "Hidden cause",
|
|
60664
|
-
|
|
60736
|
+
short: "hidden cause",
|
|
60737
|
+
desc: `How strong an unrecorded common cause would need to be to overturn the result.${scaleNote}`,
|
|
60665
60738
|
value,
|
|
60666
60739
|
passed: tip === null,
|
|
60667
60740
|
tip: east.some("Something unrecorded could drive both the treatment choice and the outcome."),
|
|
@@ -60677,7 +60750,23 @@ function deriveRefute(r2, adj) {
|
|
|
60677
60750
|
const lo2 = mid.map((m2) => m2 - half);
|
|
60678
60751
|
const hi = mid.map((m2) => m2 + half);
|
|
60679
60752
|
const yLo = Math.min(0, ...lo2), yHi = Math.max(...hi, 0);
|
|
60680
|
-
|
|
60753
|
+
const strengths = arr(sens.strengths);
|
|
60754
|
+
const atOf = (s2) => {
|
|
60755
|
+
if (strengths.length < 2) return null;
|
|
60756
|
+
if (s2 <= strengths[0]) return 0;
|
|
60757
|
+
for (let i = 1; i < strengths.length; i++) {
|
|
60758
|
+
if (s2 <= strengths[i]) {
|
|
60759
|
+
const span = strengths[i] - strengths[i - 1];
|
|
60760
|
+
return span > 0 ? i - 1 + (s2 - strengths[i - 1]) / span : i;
|
|
60761
|
+
}
|
|
60762
|
+
}
|
|
60763
|
+
return strengths.length - 1;
|
|
60764
|
+
};
|
|
60765
|
+
const marks = benchmarks.slice(0, 2).flatMap((b2) => {
|
|
60766
|
+
const at = atOf(b2.strength);
|
|
60767
|
+
return at === null ? [] : [{ at, label: `as strong as ${labelOf(meta3, b2.column)}`, tone: "muted", help: "sensitivity" }];
|
|
60768
|
+
});
|
|
60769
|
+
sensVM = { lo: lo2, mid, hi, xTicks: ["none", "", "stronger"], yTicks: [fmt$1(yLo), fmt$1((yLo + yHi) / 2), fmt$1(yHi)], marks };
|
|
60681
60770
|
}
|
|
60682
60771
|
}
|
|
60683
60772
|
return { checks, sens: sensVM };
|
|
@@ -60714,21 +60803,26 @@ function deriveDose(dose, config2, meta3) {
|
|
|
60714
60803
|
const marks = [];
|
|
60715
60804
|
if (sizes.length) marks.push({ at: here, label: "you are here", tone: "muted", help: "dose_here" });
|
|
60716
60805
|
marks.push({ at: sweet, label: "sweet spot", tone: "pos", help: "dose_sweet" });
|
|
60806
|
+
const featureLabel = labelOf(meta3, dose.feature);
|
|
60807
|
+
const outcomeLabel = labelOf(meta3, config2.outcome);
|
|
60717
60808
|
return {
|
|
60718
|
-
feature:
|
|
60719
|
-
outcome:
|
|
60809
|
+
feature: featureLabel,
|
|
60810
|
+
outcome: outcomeLabel,
|
|
60720
60811
|
lo: lo2,
|
|
60721
60812
|
mid,
|
|
60722
60813
|
hi,
|
|
60723
60814
|
xTicks,
|
|
60724
60815
|
yTicks,
|
|
60725
60816
|
marks,
|
|
60726
|
-
|
|
60817
|
+
// Name the feature in the recommendation — a bare "≈ 5.5" doesn't say
|
|
60818
|
+
// 5.5 of WHAT.
|
|
60819
|
+
recoLabel: `aim for ${featureLabel} ≈ ${fmt$1(grid[sweet] ?? 0)}${featureUnit ? " " + featureUnit : ""}`,
|
|
60727
60820
|
recoEffect: mid[sweet] ?? 0,
|
|
60728
60821
|
recoLo: lo2[sweet] ?? 0,
|
|
60729
60822
|
recoHi: hi[sweet] ?? 0,
|
|
60730
60823
|
tradeoff,
|
|
60731
|
-
marginal
|
|
60824
|
+
marginal,
|
|
60825
|
+
framing: `A different question from the headline: how ${outcomeLabel} responds as ${featureLabel} itself moves.`
|
|
60732
60826
|
};
|
|
60733
60827
|
}
|
|
60734
60828
|
function relTime(d2, now2) {
|
|
@@ -60745,12 +60839,12 @@ const VERDICT_WORD = {
|
|
|
60745
60839
|
non_identifiable_positivity: "no overlap",
|
|
60746
60840
|
not_estimable: "n/a"
|
|
60747
60841
|
};
|
|
60748
|
-
function deriveJournalRow(row, now2) {
|
|
60842
|
+
function deriveJournalRow(row, now2, meta3) {
|
|
60749
60843
|
const adj = eastUiComponents.getSomeorUndefined(row.adjusted);
|
|
60750
60844
|
return {
|
|
60751
|
-
treatment: row.config.treatment,
|
|
60752
|
-
outcome: row.config.outcome,
|
|
60753
|
-
confounders: row.config.common_causes.join(", "),
|
|
60845
|
+
treatment: labelOf(meta3, row.config.treatment),
|
|
60846
|
+
outcome: labelOf(meta3, row.config.outcome),
|
|
60847
|
+
confounders: row.config.common_causes.map((c2) => labelOf(meta3, c2)).join(", "),
|
|
60754
60848
|
effect: adj !== void 0 ? signed(adj) : signed(row.naive),
|
|
60755
60849
|
verdict: VERDICT_WORD[row.verdict.type],
|
|
60756
60850
|
verdictTone: VERDICT_TONE[row.verdict.type],
|
|
@@ -60761,22 +60855,27 @@ function deriveJournalRow(row, now2) {
|
|
|
60761
60855
|
preset: row.preset
|
|
60762
60856
|
};
|
|
60763
60857
|
}
|
|
60764
|
-
function deriveView(config2, ranConfig, cols, result, journal, meta3, dataLen, now2) {
|
|
60858
|
+
function deriveView(config2, ranConfig, cols, result, journal, meta3, dataLen, now2, subject = { one: "record", many: "records" }) {
|
|
60859
|
+
var _a2;
|
|
60765
60860
|
const adj = result ? eastUiComponents.getSomeorUndefined(result.adjusted) : void 0;
|
|
60766
60861
|
const refutation = result ? eastUiComponents.getSomeorUndefined(result.refutation) : void 0;
|
|
60767
60862
|
const doseR = result ? eastUiComponents.getSomeorUndefined(result.dose_response) : void 0;
|
|
60863
|
+
const answer = result && adj ? deriveAnswer(ranConfig, result, adj, meta3) : null;
|
|
60864
|
+
const refute = refutation ? deriveRefute(refutation, adj, meta3) : null;
|
|
60865
|
+
const higherBetter = eastUiComponents.getSomeorUndefined((_a2 = colMeta(meta3, ranConfig.outcome)) == null ? void 0 : _a2.higherIsBetter);
|
|
60768
60866
|
return {
|
|
60769
60867
|
// The set-up rail reflects the LIVE config (the editor); the result deck
|
|
60770
60868
|
// reflects RANCONFIG — the config that produced `result` — so the result
|
|
60771
60869
|
// strings never drift ahead of the numbers on a live picker edit.
|
|
60772
|
-
spec: deriveSpec(config2, cols, result, meta3, dataLen),
|
|
60870
|
+
spec: deriveSpec(config2, cols, result, meta3, dataLen, subject),
|
|
60773
60871
|
verdict: result ? deriveVerdict(result.verdict) : null,
|
|
60774
|
-
|
|
60775
|
-
|
|
60872
|
+
narrative: result ? deriveNarrative(result, answer, refute, higherBetter, subject) : null,
|
|
60873
|
+
answer,
|
|
60874
|
+
refusal: result && !adj ? deriveRefusal(ranConfig, result, meta3, subject) : null,
|
|
60776
60875
|
overlap: result ? deriveOverlap(result.overlap) : null,
|
|
60777
|
-
refute
|
|
60876
|
+
refute,
|
|
60778
60877
|
dose: doseR && doseR.effect.length ? deriveDose(doseR, ranConfig, meta3) : null,
|
|
60779
|
-
journal: journal ? journal.map((r2) => deriveJournalRow(r2, now2)) : null
|
|
60878
|
+
journal: journal ? journal.map((r2) => deriveJournalRow(r2, now2, meta3)) : null
|
|
60780
60879
|
};
|
|
60781
60880
|
}
|
|
60782
60881
|
const DESIGN_HEADLINE = {
|
|
@@ -60858,8 +60957,7 @@ function deriveDesign(d2, result, ranConfig, meta3) {
|
|
|
60858
60957
|
faint
|
|
60859
60958
|
};
|
|
60860
60959
|
}
|
|
60861
|
-
const
|
|
60862
|
-
const SUBJECT_MANY = "records";
|
|
60960
|
+
const SUBJECT_DEFAULT = { one: "record", many: "records" };
|
|
60863
60961
|
const AUTORUN_MAX_ROWS = 5e4;
|
|
60864
60962
|
const TONE_TOKEN = {
|
|
60865
60963
|
neg: "fg.danger",
|
|
@@ -60928,7 +61026,10 @@ function rowMatchesAll(row, preds) {
|
|
|
60928
61026
|
}
|
|
60929
61027
|
const num = (x2) => typeof x2 === "number" ? x2 : typeof x2 === "bigint" ? Number(x2) : null;
|
|
60930
61028
|
function Cap({ children: children2, help }) {
|
|
60931
|
-
return /* @__PURE__ */ jsxRuntime.
|
|
61029
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
61030
|
+
/* @__PURE__ */ jsxRuntime.jsx(react.Text, { textStyle: "caption.eyebrow", fontSize: "9px", mb: "2.5", children: help ? /* @__PURE__ */ jsxRuntime.jsx(Help, { id: help, children: children2 }) : children2 }),
|
|
61031
|
+
help && /* @__PURE__ */ jsxRuntime.jsx(GuidanceGist, { id: help })
|
|
61032
|
+
] });
|
|
60932
61033
|
}
|
|
60933
61034
|
function Card({ children: children2, mt = "3" }) {
|
|
60934
61035
|
return /* @__PURE__ */ jsxRuntime.jsx(react.Box, { layerStyle: "card", p: "3.5", borderRadius: "lg", mt, children: children2 });
|
|
@@ -61050,6 +61151,7 @@ const EastChakraExperiment = React.memo(function EastChakraExperiment2({ value }
|
|
|
61050
61151
|
const data4 = useBindingValue(v3.data);
|
|
61051
61152
|
const journalBind = useBindingValue(v3.journal.type === "some" ? v3.journal.value : null);
|
|
61052
61153
|
const meta3 = eastUiComponents.getSomeorUndefined(v3.columnMeta);
|
|
61154
|
+
const subject = eastUiComponents.getSomeorUndefined(v3.subject) ?? SUBJECT_DEFAULT;
|
|
61053
61155
|
const readonly = eastUiComponents.getSomeorUndefined(v3.readonly) ?? false;
|
|
61054
61156
|
const configsBind = useBindingValue(v3.configs);
|
|
61055
61157
|
const configs = React.useMemo(() => configsBind.value ?? [], [configsBind.value]);
|
|
@@ -61186,16 +61288,16 @@ const EastChakraExperiment = React.memo(function EastChakraExperiment2({ value }
|
|
|
61186
61288
|
const nRows = (filteredRows == null ? void 0 : filteredRows.length) ?? 0;
|
|
61187
61289
|
const helpVars = React.useMemo(() => {
|
|
61188
61290
|
const rc = ranConfig ?? config2;
|
|
61189
|
-
const
|
|
61291
|
+
const labelOf2 = (col) => {
|
|
61190
61292
|
var _a3;
|
|
61191
61293
|
return col ? eastUiComponents.getSomeorUndefined((_a3 = meta3 == null ? void 0 : meta3.get(col)) == null ? void 0 : _a3.label) ?? col : "";
|
|
61192
61294
|
};
|
|
61193
|
-
return { treatment:
|
|
61194
|
-
}, [ranConfig, config2, meta3]);
|
|
61295
|
+
return { treatment: labelOf2(rc == null ? void 0 : rc.treatment), outcome: labelOf2(rc == null ? void 0 : rc.outcome), subject: subject.one, subjects: subject.many };
|
|
61296
|
+
}, [ranConfig, config2, meta3, subject]);
|
|
61195
61297
|
const view = React.useMemo(() => {
|
|
61196
61298
|
if (!config2) return null;
|
|
61197
|
-
return deriveView(config2, ranConfig ?? config2, columns, shownResult, journalBind.value, meta3, nRows, now2);
|
|
61198
|
-
}, [config2, ranConfig, columns, shownResult, journalBind.value, meta3, nRows, now2]);
|
|
61299
|
+
return deriveView(config2, ranConfig ?? config2, columns, shownResult, journalBind.value, meta3, nRows, now2, subject);
|
|
61300
|
+
}, [config2, ranConfig, columns, shownResult, journalBind.value, meta3, nRows, now2, subject]);
|
|
61199
61301
|
const designConfigValue = React.useMemo(() => ({
|
|
61200
61302
|
alpha: east.none,
|
|
61201
61303
|
target_power: east.none,
|
|
@@ -61250,16 +61352,18 @@ const EastChakraExperiment = React.memo(function EastChakraExperiment2({ value }
|
|
|
61250
61352
|
const showResult = shownResult !== null && !experiment.pending && !failed;
|
|
61251
61353
|
const anyPrecomputedDesign = configs.some((c2) => c2.design.type === "some");
|
|
61252
61354
|
const showValidate = hasDesign || anyPrecomputedDesign;
|
|
61253
|
-
const
|
|
61355
|
+
const showDose = eastUiComponents.getSomeorUndefined(config2.dose_feature) !== void 0 || (shownResult == null ? void 0 : shownResult.dose_response.type) === "some";
|
|
61356
|
+
const tabKeys = [...["answer", "trust"], ...showDose ? ["dose"] : [], ...showValidate ? ["validate"] : []];
|
|
61357
|
+
const activeTab = tabKeys.includes(tab) ? tab : "answer";
|
|
61254
61358
|
return /* @__PURE__ */ jsxRuntime.jsx(GuidanceProvider, { on: guidance, vars: helpVars, children: /* @__PURE__ */ jsxRuntime.jsxs(react.Box, { layerStyle: "surface.frameless", overflow: "visible", children: [
|
|
61255
61359
|
/* @__PURE__ */ jsxRuntime.jsxs(react.Box, { layerStyle: "header.bar", display: "flex", alignItems: "center", gap: "3.5", children: [
|
|
61256
61360
|
configs.length > 1 ? /* @__PURE__ */ jsxRuntime.jsxs(react.Menu.Root, { children: [
|
|
61257
61361
|
/* @__PURE__ */ jsxRuntime.jsx(react.Menu.Trigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(react.Box, { as: "button", bg: "transparent", border: "0", p: "0", cursor: "pointer", display: "inline-flex", alignItems: "center", gap: "2", textAlign: "start", children: [
|
|
61258
61362
|
/* @__PURE__ */ jsxRuntime.jsxs(react.Text, { textStyle: "title.card", color: "fg.muted", children: [
|
|
61259
61363
|
"Does ",
|
|
61260
|
-
/* @__PURE__ */ jsxRuntime.jsx(react.Text, { as: "span", color: "brand.solid", fontWeight: "bold", children: vs.
|
|
61364
|
+
/* @__PURE__ */ jsxRuntime.jsx(react.Text, { as: "span", color: "brand.solid", fontWeight: "bold", children: vs.treatmentLabel }),
|
|
61261
61365
|
" change ",
|
|
61262
|
-
/* @__PURE__ */ jsxRuntime.jsx(react.Text, { as: "span", color: "brand.solid", fontWeight: "bold", children: vs.
|
|
61366
|
+
/* @__PURE__ */ jsxRuntime.jsx(react.Text, { as: "span", color: "brand.solid", fontWeight: "bold", children: vs.outcomeLabel }),
|
|
61263
61367
|
"?"
|
|
61264
61368
|
] }),
|
|
61265
61369
|
/* @__PURE__ */ jsxRuntime.jsx(react.Box, { as: "span", color: "fg.subtle", fontSize: "11px", lineHeight: "1", children: /* @__PURE__ */ jsxRuntime.jsx(FontAwesomeIcon, { icon: faChevronDown }) })
|
|
@@ -61273,9 +61377,9 @@ const EastChakraExperiment = React.memo(function EastChakraExperiment2({ value }
|
|
|
61273
61377
|
] }, g2.label)) }) }) })
|
|
61274
61378
|
] }) : /* @__PURE__ */ jsxRuntime.jsx(react.Text, { textStyle: "title.card", color: "fg.muted", children: /* @__PURE__ */ jsxRuntime.jsxs(Help, { id: "header", children: [
|
|
61275
61379
|
"Does ",
|
|
61276
|
-
/* @__PURE__ */ jsxRuntime.jsx(react.Text, { as: "span", color: "brand.solid", fontWeight: "bold", children: vs.
|
|
61380
|
+
/* @__PURE__ */ jsxRuntime.jsx(react.Text, { as: "span", color: "brand.solid", fontWeight: "bold", children: vs.treatmentLabel }),
|
|
61277
61381
|
" change ",
|
|
61278
|
-
/* @__PURE__ */ jsxRuntime.jsx(react.Text, { as: "span", color: "brand.solid", fontWeight: "bold", children: vs.
|
|
61382
|
+
/* @__PURE__ */ jsxRuntime.jsx(react.Text, { as: "span", color: "brand.solid", fontWeight: "bold", children: vs.outcomeLabel }),
|
|
61279
61383
|
"?"
|
|
61280
61384
|
] }) }),
|
|
61281
61385
|
/* @__PURE__ */ jsxRuntime.jsx(react.Box, { flex: "1" }),
|
|
@@ -61315,7 +61419,7 @@ const EastChakraExperiment = React.memo(function EastChakraExperiment2({ value }
|
|
|
61315
61419
|
"add another"
|
|
61316
61420
|
] }) })
|
|
61317
61421
|
] }) }),
|
|
61318
|
-
/* @__PURE__ */ jsxRuntime.jsx(Step, { n: 4, title: `Which ${
|
|
61422
|
+
/* @__PURE__ */ jsxRuntime.jsx(Step, { n: 4, title: `Which ${subject.many}?`, help: "step_population", children: /* @__PURE__ */ jsxRuntime.jsx(FilterRail, { fields, population, onChange: editPopulation, chip, button, readonly, subjectMany: subject.many }) }),
|
|
61319
61423
|
/* @__PURE__ */ jsxRuntime.jsxs(react.Box, { as: "details", borderTopWidth: "1px", borderColor: "border.subtle", children: [
|
|
61320
61424
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
61321
61425
|
react.Box,
|
|
@@ -61366,7 +61470,7 @@ const EastChakraExperiment = React.memo(function EastChakraExperiment2({ value }
|
|
|
61366
61470
|
"aria-label": "Result views",
|
|
61367
61471
|
onKeyDown: (e3) => {
|
|
61368
61472
|
var _a3;
|
|
61369
|
-
const i = tabKeys.indexOf(
|
|
61473
|
+
const i = tabKeys.indexOf(activeTab);
|
|
61370
61474
|
let j2 = i;
|
|
61371
61475
|
if (e3.key === "ArrowRight") j2 = (i + 1) % tabKeys.length;
|
|
61372
61476
|
else if (e3.key === "ArrowLeft") j2 = (i - 1 + tabKeys.length) % tabKeys.length;
|
|
@@ -61380,9 +61484,9 @@ const EastChakraExperiment = React.memo(function EastChakraExperiment2({ value }
|
|
|
61380
61484
|
},
|
|
61381
61485
|
children: [
|
|
61382
61486
|
tabKeys.map((tk) => {
|
|
61383
|
-
const on3 =
|
|
61487
|
+
const on3 = activeTab === tk;
|
|
61384
61488
|
const tabHelp = tk === "answer" ? "tab_answer" : tk === "trust" ? "tab_trust" : tk === "dose" ? "tab_dose" : "tab_validate";
|
|
61385
|
-
const tabLabel = tk === "answer" ? "
|
|
61489
|
+
const tabLabel = tk === "answer" ? "The answer" : tk === "trust" ? "Can we trust it?" : tk === "dose" ? "How much?" : "Prove it";
|
|
61386
61490
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
61387
61491
|
react.Box,
|
|
61388
61492
|
{
|
|
@@ -61414,7 +61518,7 @@ const EastChakraExperiment = React.memo(function EastChakraExperiment2({ value }
|
|
|
61414
61518
|
]
|
|
61415
61519
|
}
|
|
61416
61520
|
),
|
|
61417
|
-
failed ? /* @__PURE__ */ jsxRuntime.jsx(react.Box, { px: "4.5", pt: "4.5", children: /* @__PURE__ */ jsxRuntime.jsx(RunError, { error: experiment.error }) }) : !showResult ? /* @__PURE__ */ jsxRuntime.jsx(DeckSkeleton, { tab }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
61521
|
+
failed ? /* @__PURE__ */ jsxRuntime.jsx(react.Box, { px: "4.5", pt: "4.5", children: /* @__PURE__ */ jsxRuntime.jsx(RunError, { error: experiment.error }) }) : !showResult ? /* @__PURE__ */ jsxRuntime.jsx(DeckSkeleton, { tab: activeTab }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
61418
61522
|
stale && /* @__PURE__ */ jsxRuntime.jsxs(react.Box, { layerStyle: "banner.stale", display: "flex", alignItems: "center", gap: "2", mx: "4.5", mt: "4.5", children: [
|
|
61419
61523
|
/* @__PURE__ */ jsxRuntime.jsx(react.Box, { as: "span", color: "fg.warning", flexShrink: "0", fontSize: "12px", children: /* @__PURE__ */ jsxRuntime.jsx(FontAwesomeIcon, { icon: faTriangleExclamation }) }),
|
|
61420
61524
|
/* @__PURE__ */ jsxRuntime.jsxs(react.Text, { textStyle: "body.sm", color: "fg.default", children: [
|
|
@@ -61422,9 +61526,22 @@ const EastChakraExperiment = React.memo(function EastChakraExperiment2({ value }
|
|
|
61422
61526
|
" Hit Run to update these results for your edits."
|
|
61423
61527
|
] })
|
|
61424
61528
|
] }),
|
|
61425
|
-
|
|
61426
|
-
|
|
61427
|
-
|
|
61529
|
+
activeTab === "answer" && a2 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
61530
|
+
AnswerNumeric,
|
|
61531
|
+
{
|
|
61532
|
+
a: a2,
|
|
61533
|
+
verdict,
|
|
61534
|
+
narrative: view.narrative,
|
|
61535
|
+
checks: (vr == null ? void 0 : vr.checks) ?? null,
|
|
61536
|
+
onGoTrust: () => setTab("trust"),
|
|
61537
|
+
subjectMany: subject.many,
|
|
61538
|
+
higherBetter,
|
|
61539
|
+
badge,
|
|
61540
|
+
barList
|
|
61541
|
+
}
|
|
61542
|
+
),
|
|
61543
|
+
activeTab === "answer" && !a2 && ref && /* @__PURE__ */ jsxRuntime.jsx(RefusalZone, { refusal: ref, overlap: ov, naiveValue: (shownResult == null ? void 0 : shownResult.naive) ?? 0, outcome: vs.outcomeLabel }),
|
|
61544
|
+
activeTab === "trust" && vr && /* @__PURE__ */ jsxRuntime.jsxs(react.Box, { p: "4.5", children: [
|
|
61428
61545
|
/* @__PURE__ */ jsxRuntime.jsx(react.Text, { textStyle: "body.sm", color: "fg.muted", mb: "3.5", children: /* @__PURE__ */ jsxRuntime.jsx(Help, { id: "trust_intro", children: "Before trusting the answer we tried to break it — colour shows pass / caution." }) }),
|
|
61429
61546
|
/* @__PURE__ */ jsxRuntime.jsx(react.Box, { layerStyle: "frame.flat", children: vr.checks.map((c2, i) => {
|
|
61430
61547
|
const cs = statusR({ status: c2.passed ? "success" : "warning", size: "sm" });
|
|
@@ -61442,10 +61559,34 @@ const EastChakraExperiment = React.memo(function EastChakraExperiment2({ value }
|
|
|
61442
61559
|
}) }),
|
|
61443
61560
|
vr.sens && /* @__PURE__ */ jsxRuntime.jsxs(Card, { children: [
|
|
61444
61561
|
/* @__PURE__ */ jsxRuntime.jsx(Cap, { help: "sensitivity", children: "Effect as a hidden cause is made stronger" }),
|
|
61445
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
61562
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
61563
|
+
AreaRange,
|
|
61564
|
+
{
|
|
61565
|
+
lo: vr.sens.lo,
|
|
61566
|
+
mid: vr.sens.mid,
|
|
61567
|
+
hi: vr.sens.hi,
|
|
61568
|
+
zero: 0,
|
|
61569
|
+
tone: "brand",
|
|
61570
|
+
xTicks: vr.sens.xTicks,
|
|
61571
|
+
yTicks: vr.sens.yTicks,
|
|
61572
|
+
marks: vr.sens.marks,
|
|
61573
|
+
height: vr.sens.marks.length ? 148 : 132
|
|
61574
|
+
}
|
|
61575
|
+
)
|
|
61446
61576
|
] })
|
|
61447
61577
|
] }),
|
|
61448
|
-
|
|
61578
|
+
activeTab === "trust" && !vr && // Two distinct reasons the checklist is absent: the engine REFUSED
|
|
61579
|
+
// (no adjusted estimate to break) vs. the answer landed WITHOUT a
|
|
61580
|
+
// robustness summary (e.g. a precomputed result whose stress tests
|
|
61581
|
+
// were never run). Conflating them asserted a refusal that didn't
|
|
61582
|
+
// happen.
|
|
61583
|
+
((shownResult == null ? void 0 : shownResult.adjusted.type) === "some" ? /* @__PURE__ */ jsxRuntime.jsxs(react.Box, { p: "4.5", children: [
|
|
61584
|
+
/* @__PURE__ */ jsxRuntime.jsxs(react.Box, { display: "inline-flex", alignItems: "center", gap: "2", mb: "2", color: "fg.muted", children: [
|
|
61585
|
+
/* @__PURE__ */ jsxRuntime.jsx(FontAwesomeIcon, { icon: faTriangleExclamation, style: { fontSize: "13px" } }),
|
|
61586
|
+
/* @__PURE__ */ jsxRuntime.jsx(react.Text, { textStyle: "body.sm", fontWeight: "semibold", color: "fg.default", children: "Stress tests not run" })
|
|
61587
|
+
] }),
|
|
61588
|
+
/* @__PURE__ */ jsxRuntime.jsx(react.Text, { textStyle: "body.sm", color: "fg.muted", lineHeight: "1.5", children: "This answer came without a robustness summary — the stress tests weren’t run for this question, so there’s nothing to report here." })
|
|
61589
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsxs(react.Box, { p: "4.5", children: [
|
|
61449
61590
|
/* @__PURE__ */ jsxRuntime.jsxs(react.Box, { display: "inline-flex", alignItems: "center", gap: "2", mb: "2", color: "fg.muted", children: [
|
|
61450
61591
|
/* @__PURE__ */ jsxRuntime.jsx(FontAwesomeIcon, { icon: faTriangleExclamation, style: { fontSize: "13px" } }),
|
|
61451
61592
|
/* @__PURE__ */ jsxRuntime.jsx(react.Text, { textStyle: "body.sm", fontWeight: "semibold", color: "fg.default", children: "Nothing to stress-test" })
|
|
@@ -61455,8 +61596,9 @@ const EastChakraExperiment = React.memo(function EastChakraExperiment2({ value }
|
|
|
61455
61596
|
/* @__PURE__ */ jsxRuntime.jsx(react.Text, { as: "span", fontWeight: "semibold", color: "fg.default", children: "Answer" }),
|
|
61456
61597
|
" tab for why)."
|
|
61457
61598
|
] })
|
|
61458
|
-
] }),
|
|
61459
|
-
|
|
61599
|
+
] })),
|
|
61600
|
+
activeTab === "dose" && vd && /* @__PURE__ */ jsxRuntime.jsxs(react.Box, { p: "4.5", children: [
|
|
61601
|
+
/* @__PURE__ */ jsxRuntime.jsx(react.Text, { textStyle: "body.sm", color: "fg.muted", mb: "3.5", children: /* @__PURE__ */ jsxRuntime.jsx(Help, { id: "tab_dose", children: vd.framing }) }),
|
|
61460
61602
|
/* @__PURE__ */ jsxRuntime.jsxs(Card, { mt: "0", children: [
|
|
61461
61603
|
/* @__PURE__ */ jsxRuntime.jsxs(Cap, { help: "dose_curve", children: [
|
|
61462
61604
|
vd.outcome,
|
|
@@ -61493,7 +61635,27 @@ const EastChakraExperiment = React.memo(function EastChakraExperiment2({ value }
|
|
|
61493
61635
|
] })
|
|
61494
61636
|
] })
|
|
61495
61637
|
] }),
|
|
61496
|
-
|
|
61638
|
+
activeTab === "dose" && !vd && // The tab is reachable (a dose feature is configured) but the shown
|
|
61639
|
+
// result has no curve — e.g. a refusal verdict, or the engine
|
|
61640
|
+
// returned no dose-response for this setup. Say so; never a blank pane.
|
|
61641
|
+
/* @__PURE__ */ jsxRuntime.jsxs(react.Box, { p: "4.5", children: [
|
|
61642
|
+
/* @__PURE__ */ jsxRuntime.jsxs(react.Box, { display: "inline-flex", alignItems: "center", gap: "2", mb: "2", color: "fg.muted", children: [
|
|
61643
|
+
/* @__PURE__ */ jsxRuntime.jsx(FontAwesomeIcon, { icon: faTriangleExclamation, style: { fontSize: "13px" } }),
|
|
61644
|
+
/* @__PURE__ */ jsxRuntime.jsx(react.Text, { textStyle: "body.sm", fontWeight: "semibold", color: "fg.default", children: "No dose-response to show" })
|
|
61645
|
+
] }),
|
|
61646
|
+
(shownResult == null ? void 0 : shownResult.adjusted.type) === "some" ? /* @__PURE__ */ jsxRuntime.jsx(react.Text, { textStyle: "body.sm", color: "fg.muted", lineHeight: "1.5", children: "The experiment didn’t return a how-much curve for this setup." }) : /* @__PURE__ */ jsxRuntime.jsxs(react.Text, { textStyle: "body.sm", color: "fg.muted", lineHeight: "1.5", children: [
|
|
61647
|
+
"A how-much curve needs a like-for-like estimate, and the experiment couldn’t produce one (see the ",
|
|
61648
|
+
/* @__PURE__ */ jsxRuntime.jsx(react.Text, { as: "span", fontWeight: "semibold", color: "fg.default", children: "Answer" }),
|
|
61649
|
+
" tab for why)."
|
|
61650
|
+
] })
|
|
61651
|
+
] }),
|
|
61652
|
+
activeTab === "validate" && (design.status === "failed" && design.error ? /* @__PURE__ */ jsxRuntime.jsx(react.Box, { px: "4.5", pt: "4.5", children: /* @__PURE__ */ jsxRuntime.jsx(RunError, { error: design.error }) }) : designFresh && vmDesign ? /* @__PURE__ */ jsxRuntime.jsx(ValidatePanel, { vm: vmDesign, barList }) : !hasDesign && precomputedDesign === null ? /* @__PURE__ */ jsxRuntime.jsxs(react.Box, { p: "4.5", children: [
|
|
61653
|
+
/* @__PURE__ */ jsxRuntime.jsxs(react.Box, { display: "inline-flex", alignItems: "center", gap: "2", mb: "2", color: "fg.muted", children: [
|
|
61654
|
+
/* @__PURE__ */ jsxRuntime.jsx(FontAwesomeIcon, { icon: faTriangleExclamation, style: { fontSize: "13px" } }),
|
|
61655
|
+
/* @__PURE__ */ jsxRuntime.jsx(react.Text, { textStyle: "body.sm", fontWeight: "semibold", color: "fg.default", children: "No trial recipe for this question" })
|
|
61656
|
+
] }),
|
|
61657
|
+
/* @__PURE__ */ jsxRuntime.jsx(react.Text, { textStyle: "body.sm", color: "fg.muted", lineHeight: "1.5", children: "This question doesn’t include a validation-trial recipe, and no design function is bound to size one." })
|
|
61658
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsxs(react.Box, { p: "4.5", display: "inline-flex", alignItems: "center", gap: "2", color: "fg.muted", children: [
|
|
61497
61659
|
/* @__PURE__ */ jsxRuntime.jsx(react.Spinner, { size: "xs", borderWidth: "1.5px", color: "brand.solid" }),
|
|
61498
61660
|
/* @__PURE__ */ jsxRuntime.jsx(react.Text, { textStyle: "body.sm", children: "Sizing the trial that would confirm this…" })
|
|
61499
61661
|
] }))
|
|
@@ -61503,11 +61665,11 @@ const EastChakraExperiment = React.memo(function EastChakraExperiment2({ value }
|
|
|
61503
61665
|
journal && journal.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
61504
61666
|
/* @__PURE__ */ jsxRuntime.jsxs(react.Box, { css: es.root, bg: "bg.canvas", borderTopWidth: "1px", borderColor: "border.subtle", children: [
|
|
61505
61667
|
/* @__PURE__ */ jsxRuntime.jsx(react.Box, { css: es.lbl, children: /* @__PURE__ */ jsxRuntime.jsx(Help, { id: "journal", children: "Committed experiments" }) }),
|
|
61506
|
-
/* @__PURE__ */ jsxRuntime.
|
|
61668
|
+
/* @__PURE__ */ jsxRuntime.jsx(react.Box, { css: es.meta, children: /* @__PURE__ */ jsxRuntime.jsxs(react.Text, { as: "span", children: [
|
|
61507
61669
|
/* @__PURE__ */ jsxRuntime.jsx(react.Text, { as: "span", color: "fg.default", fontWeight: "semibold", children: journal.length }),
|
|
61508
61670
|
" on record",
|
|
61509
61671
|
journal.length > 50 ? " · showing newest 50" : ""
|
|
61510
|
-
] })
|
|
61672
|
+
] }) })
|
|
61511
61673
|
] }),
|
|
61512
61674
|
journal.slice(0, 50).map((r2, i) => /* @__PURE__ */ jsxRuntime.jsxs(react.Box, { display: "grid", gridTemplateColumns: "2fr 1fr 1fr 1fr", gap: "3", alignItems: "center", px: "4.5", py: "2.5", borderTopWidth: "1px", borderColor: "border.subtle", children: [
|
|
61513
61675
|
/* @__PURE__ */ jsxRuntime.jsxs(react.Text, { textStyle: "body.sm", children: [
|
|
@@ -61546,24 +61708,31 @@ const EastChakraExperiment = React.memo(function EastChakraExperiment2({ value }
|
|
|
61546
61708
|
] })
|
|
61547
61709
|
] }) });
|
|
61548
61710
|
}, (prev2, next2) => experimentValueEqual(prev2.value, next2.value) && prev2.storageKey === next2.storageKey);
|
|
61549
|
-
function AnswerNumeric({ a: a2, verdict, higherBetter, badge, barList }) {
|
|
61711
|
+
function AnswerNumeric({ a: a2, verdict, narrative, checks, onGoTrust, subjectMany, higherBetter, badge, barList }) {
|
|
61550
61712
|
const dirUp = a2.effect > 0;
|
|
61551
61713
|
const statusWord = higherBetter === void 0 ? dirUp ? "Higher" : "Lower" : dirUp === higherBetter ? "Better" : "Worse";
|
|
61552
61714
|
const lowerWord = a2.naive < 0 ? "lower" : "higher";
|
|
61553
|
-
const top = a2.balance[0] ?? {
|
|
61715
|
+
const top = a2.balance[0] ?? { label: "", display: "" };
|
|
61554
61716
|
const kpiColor = a2.clear && !a2.cautious ? "fg.success" : "fg.warning";
|
|
61555
61717
|
const badgeOk = a2.clear && !a2.cautious;
|
|
61556
61718
|
const badgeText = a2.cautious ? (verdict == null ? void 0 : verdict.label) ?? "Not trustworthy yet" : a2.clear ? `${statusWord} with ${a2.treatment}` : "No clear effect";
|
|
61557
61719
|
return /* @__PURE__ */ jsxRuntime.jsxs(react.Box, { p: "4.5", children: [
|
|
61720
|
+
narrative && /* @__PURE__ */ jsxRuntime.jsx(react.Text, { textStyle: "body.md", color: "fg.default", lineHeight: "1.6", mb: "3.5", children: narrative.segments.map((s2, i) => s2.strong ? /* @__PURE__ */ jsxRuntime.jsx(react.Text, { as: "span", fontWeight: "bold", color: toneToken(narrative.tone), children: s2.text }, i) : /* @__PURE__ */ jsxRuntime.jsx(react.Text, { as: "span", children: s2.text }, i)) }),
|
|
61558
61721
|
/* @__PURE__ */ jsxRuntime.jsxs(react.Box, { display: "flex", alignItems: "center", gap: "4.5", flexWrap: "wrap", children: [
|
|
61559
61722
|
/* @__PURE__ */ jsxRuntime.jsxs(react.Box, { display: "flex", flexDirection: "column", gap: "0.5", children: [
|
|
61560
61723
|
/* @__PURE__ */ jsxRuntime.jsx(react.Text, { textStyle: "mono.sm", color: "fg.muted", children: a2.outcome }),
|
|
61561
61724
|
/* @__PURE__ */ jsxRuntime.jsxs(react.Box, { display: "flex", alignItems: "baseline", gap: "2.5", children: [
|
|
61562
|
-
/* @__PURE__ */ jsxRuntime.jsx(react.Text, { textStyle: "mono-kpi", fontFamily: "heading", fontSize: "32px", color: kpiColor, children: /* @__PURE__ */ jsxRuntime.
|
|
61725
|
+
/* @__PURE__ */ jsxRuntime.jsx(react.Text, { textStyle: "mono-kpi", fontFamily: "heading", fontSize: "32px", color: kpiColor, children: /* @__PURE__ */ jsxRuntime.jsxs(Help, { id: "answer_effect", children: [
|
|
61726
|
+
signed(a2.effect),
|
|
61727
|
+
a2.unit ? /* @__PURE__ */ jsxRuntime.jsxs(react.Text, { as: "span", fontSize: "16px", color: "fg.muted", children: [
|
|
61728
|
+
" ",
|
|
61729
|
+
a2.unit
|
|
61730
|
+
] }) : null
|
|
61731
|
+
] }) }),
|
|
61563
61732
|
/* @__PURE__ */ jsxRuntime.jsx(react.Text, { textStyle: "mono.sm", color: "fg.muted", children: /* @__PURE__ */ jsxRuntime.jsxs(Help, { id: "answer_ci", children: [
|
|
61564
|
-
"
|
|
61733
|
+
"likely between ",
|
|
61565
61734
|
signed(a2.lo),
|
|
61566
|
-
"
|
|
61735
|
+
" and ",
|
|
61567
61736
|
signed(a2.hi)
|
|
61568
61737
|
] }) })
|
|
61569
61738
|
] })
|
|
@@ -61573,6 +61742,42 @@ function AnswerNumeric({ a: a2, verdict, higherBetter, badge, barList }) {
|
|
|
61573
61742
|
/* @__PURE__ */ jsxRuntime.jsx(Help, { id: `verdict_${(verdict == null ? void 0 : verdict.tag) ?? "modest"}`, children: badgeText })
|
|
61574
61743
|
] })
|
|
61575
61744
|
] }),
|
|
61745
|
+
checks && checks.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
61746
|
+
react.Box,
|
|
61747
|
+
{
|
|
61748
|
+
as: "button",
|
|
61749
|
+
onClick: onGoTrust,
|
|
61750
|
+
display: "inline-flex",
|
|
61751
|
+
alignItems: "center",
|
|
61752
|
+
gap: "2",
|
|
61753
|
+
flexWrap: "wrap",
|
|
61754
|
+
mt: "2.5",
|
|
61755
|
+
bg: "transparent",
|
|
61756
|
+
border: "0",
|
|
61757
|
+
p: "0",
|
|
61758
|
+
cursor: "pointer",
|
|
61759
|
+
title: "See the full stress-test checklist",
|
|
61760
|
+
children: [
|
|
61761
|
+
/* @__PURE__ */ jsxRuntime.jsx(react.Text, { textStyle: "caption", color: "fg.muted", children: "Stress tests:" }),
|
|
61762
|
+
checks.map((c2, i) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
61763
|
+
react.Box,
|
|
61764
|
+
{
|
|
61765
|
+
as: "span",
|
|
61766
|
+
display: "inline-flex",
|
|
61767
|
+
alignItems: "center",
|
|
61768
|
+
gap: "1.5",
|
|
61769
|
+
color: c2.passed ? "fg.success" : "fg.warning",
|
|
61770
|
+
children: [
|
|
61771
|
+
/* @__PURE__ */ jsxRuntime.jsx(FontAwesomeIcon, { icon: c2.passed ? faCheck : faTriangleExclamation, style: { fontSize: "10px" } }),
|
|
61772
|
+
/* @__PURE__ */ jsxRuntime.jsx(react.Text, { as: "span", textStyle: "caption", color: "fg.default", children: c2.short }),
|
|
61773
|
+
i < checks.length - 1 && /* @__PURE__ */ jsxRuntime.jsx(react.Text, { as: "span", textStyle: "caption", color: "fg.subtle", children: "·" })
|
|
61774
|
+
]
|
|
61775
|
+
},
|
|
61776
|
+
i
|
|
61777
|
+
))
|
|
61778
|
+
]
|
|
61779
|
+
}
|
|
61780
|
+
),
|
|
61576
61781
|
a2.cautious && /* @__PURE__ */ jsxRuntime.jsxs(react.Box, { layerStyle: "banner.stale", display: "flex", alignItems: "flex-start", gap: "2", mt: "3", children: [
|
|
61577
61782
|
/* @__PURE__ */ jsxRuntime.jsx(react.Box, { as: "span", color: "fg.warning", flexShrink: "0", mt: "0.5", fontSize: "12px", children: /* @__PURE__ */ jsxRuntime.jsx(FontAwesomeIcon, { icon: faTriangleExclamation }) }),
|
|
61578
61783
|
/* @__PURE__ */ jsxRuntime.jsxs(react.Text, { textStyle: "body.sm", color: "fg.default", children: [
|
|
@@ -61586,15 +61791,15 @@ function AnswerNumeric({ a: a2, verdict, higherBetter, badge, barList }) {
|
|
|
61586
61791
|
/* @__PURE__ */ jsxRuntime.jsxs(react.Text, { textStyle: "body.sm", color: "fg.default", children: [
|
|
61587
61792
|
/* @__PURE__ */ jsxRuntime.jsx(Help, { id: "answer_flip", children: /* @__PURE__ */ jsxRuntime.jsx(react.Text, { as: "span", fontWeight: "bold", children: "Raw and like-for-like disagree." }) }),
|
|
61588
61793
|
" In the plain average, the ",
|
|
61589
|
-
/* @__PURE__ */ jsxRuntime.jsx(react.Text, { as: "span",
|
|
61794
|
+
/* @__PURE__ */ jsxRuntime.jsx(react.Text, { as: "span", fontWeight: "semibold", children: a2.treatment }),
|
|
61590
61795
|
" group sits ",
|
|
61591
61796
|
/* @__PURE__ */ jsxRuntime.jsx(react.Text, { as: "span", fontStyle: "italic", children: lowerWord }),
|
|
61592
61797
|
" on ",
|
|
61593
|
-
/* @__PURE__ */ jsxRuntime.jsx(react.Text, { as: "span",
|
|
61798
|
+
/* @__PURE__ */ jsxRuntime.jsx(react.Text, { as: "span", fontWeight: "semibold", children: a2.outcome }),
|
|
61594
61799
|
" (",
|
|
61595
61800
|
signed(a2.naive),
|
|
61596
61801
|
") — but they also differ most on ",
|
|
61597
|
-
/* @__PURE__ */ jsxRuntime.jsx(react.Text, { as: "span",
|
|
61802
|
+
/* @__PURE__ */ jsxRuntime.jsx(react.Text, { as: "span", fontWeight: "semibold", children: top.label }),
|
|
61598
61803
|
" (",
|
|
61599
61804
|
top.display,
|
|
61600
61805
|
"). Adjusting for it reverses the result."
|
|
@@ -61617,11 +61822,40 @@ function AnswerNumeric({ a: a2, verdict, higherBetter, badge, barList }) {
|
|
|
61617
61822
|
}
|
|
61618
61823
|
)
|
|
61619
61824
|
] }),
|
|
61620
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
61621
|
-
/* @__PURE__ */ jsxRuntime.
|
|
61622
|
-
|
|
61825
|
+
/* @__PURE__ */ jsxRuntime.jsxs(react.Box, { as: "details", layerStyle: "card", borderRadius: "lg", mt: "3", children: [
|
|
61826
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
61827
|
+
react.Box,
|
|
61828
|
+
{
|
|
61829
|
+
as: "summary",
|
|
61830
|
+
textStyle: "caption.eyebrow",
|
|
61831
|
+
fontSize: "9px",
|
|
61832
|
+
cursor: "pointer",
|
|
61833
|
+
p: "3.5",
|
|
61834
|
+
display: "flex",
|
|
61835
|
+
alignItems: "center",
|
|
61836
|
+
gap: "1.5",
|
|
61837
|
+
css: { listStyle: "none", "&::-webkit-details-marker": { display: "none" } },
|
|
61838
|
+
children: [
|
|
61839
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
61840
|
+
react.Box,
|
|
61841
|
+
{
|
|
61842
|
+
as: "span",
|
|
61843
|
+
display: "inline-flex",
|
|
61844
|
+
color: "fg.subtle",
|
|
61845
|
+
fontSize: "10px",
|
|
61846
|
+
transition: "transform 180ms ease",
|
|
61847
|
+
transform: "rotate(-90deg)",
|
|
61848
|
+
css: { "details[open] > summary > &": { transform: "rotate(0deg)" } },
|
|
61849
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(FontAwesomeIcon, { icon: faChevronDown })
|
|
61850
|
+
}
|
|
61851
|
+
),
|
|
61852
|
+
/* @__PURE__ */ jsxRuntime.jsx(Help, { id: "balance", children: "How we levelled the playing field" })
|
|
61853
|
+
]
|
|
61854
|
+
}
|
|
61855
|
+
),
|
|
61856
|
+
/* @__PURE__ */ jsxRuntime.jsx(react.Box, { px: "3.5", pb: "3.5", py: "0.5", maxH: "208px", overflowY: "auto", children: barList(a2.balance.map((b2) => ({ label: b2.label, frac: b2.frac, tone: b2.tone, value: b2.display }))) })
|
|
61623
61857
|
] }),
|
|
61624
|
-
/* @__PURE__ */ jsxRuntime.jsx(react.Box, { mt: "3.5", children: /* @__PURE__ */ jsxRuntime.jsx(Help, { id: "counts", display: "inline-flex", gap: "4", children: [[a2.nTotal,
|
|
61858
|
+
/* @__PURE__ */ jsxRuntime.jsx(react.Box, { mt: "3.5", children: /* @__PURE__ */ jsxRuntime.jsx(Help, { id: "counts", display: "inline-flex", gap: "4", children: [[a2.nTotal, subjectMany], [a2.nCompared, "compared like-for-like"], [a2.nDropped, "had no fair match"]].map(([n2, label], i) => /* @__PURE__ */ jsxRuntime.jsxs(react.Text, { textStyle: "mono.sm", color: "fg.muted", children: [
|
|
61625
61859
|
/* @__PURE__ */ jsxRuntime.jsx(react.Text, { as: "span", color: "fg.default", fontWeight: "semibold", children: Number(n2) }),
|
|
61626
61860
|
" ",
|
|
61627
61861
|
label
|
|
@@ -61725,7 +61959,7 @@ function ValidatePanel({ vm, barList }) {
|
|
|
61725
61959
|
] }, i)) })
|
|
61726
61960
|
] });
|
|
61727
61961
|
}
|
|
61728
|
-
function FilterRail({ fields, population, onChange: onChange2, chip, button, readonly }) {
|
|
61962
|
+
function FilterRail({ fields, population, onChange: onChange2, chip, button, readonly, subjectMany }) {
|
|
61729
61963
|
const [open, setOpen] = React.useState(null);
|
|
61730
61964
|
const replaceAt = (i, p2) => onChange2(population.map((f2, j2) => j2 === i ? p2 : f2));
|
|
61731
61965
|
const removeAt = (i) => onChange2(population.filter((_2, j2) => j2 !== i));
|
|
@@ -61733,7 +61967,7 @@ function FilterRail({ fields, population, onChange: onChange2, chip, button, rea
|
|
|
61733
61967
|
if (readonly) {
|
|
61734
61968
|
return /* @__PURE__ */ jsxRuntime.jsx(react.Box, { display: "flex", flexWrap: "wrap", gap: "1.5", alignItems: "center", children: population.length === 0 ? /* @__PURE__ */ jsxRuntime.jsxs(react.Box, { css: chip({ tone: "plain", numeric: true, shape: "rounded" }), children: [
|
|
61735
61969
|
"all ",
|
|
61736
|
-
|
|
61970
|
+
subjectMany
|
|
61737
61971
|
] }) : population.map((pred, i) => /* @__PURE__ */ jsxRuntime.jsx(react.Box, { css: chip({ tone: "brand", numeric: true, shape: "rounded" }), flexShrink: 0, children: /* @__PURE__ */ jsxRuntime.jsx(react.Box, { as: "span", whiteSpace: "nowrap", children: eastUiComponents.formatPredicate(pred) }) }, i)) });
|
|
61738
61972
|
}
|
|
61739
61973
|
return /* @__PURE__ */ jsxRuntime.jsxs(react.Box, { display: "flex", flexWrap: "wrap", gap: "1.5", alignItems: "center", children: [
|