@elaraai/e3-ui-components 1.0.12 → 1.0.13
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 +31 -2
- package/dist/experiment/charts.d.ts.map +1 -1
- package/dist/experiment/derive.d.ts +176 -74
- package/dist/experiment/derive.d.ts.map +1 -1
- package/dist/experiment/help-ui.d.ts +29 -0
- package/dist/experiment/help-ui.d.ts.map +1 -0
- package/dist/experiment/help.d.ts +319 -0
- package/dist/experiment/help.d.ts.map +1 -0
- package/dist/experiment/index.d.ts.map +1 -1
- package/dist/experiment/run-runtime.d.ts +9 -0
- package/dist/experiment/run-runtime.d.ts.map +1 -1
- package/dist/index.cjs +1334 -393
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1335 -394
- package/dist/index.js.map +1 -1
- package/package.json +14 -14
package/dist/index.js
CHANGED
|
@@ -26,7 +26,7 @@ import { workspaceStatus, datasetListAt, datasetList, dataflowExecuteLaunch, dat
|
|
|
26
26
|
import { jsx as jsx$1, Fragment, jsxs } from "react/jsx-runtime";
|
|
27
27
|
import * as React from "react";
|
|
28
28
|
import React__default, { useId, useMemo, useContext, createContext, useRef, useEffect, useState, useCallback, useSyncExternalStore, memo, forwardRef, useLayoutEffect, Fragment as Fragment$1, Component } from "react";
|
|
29
|
-
import { Box, Flex, Text, HStack, Input, useSlotRecipe, useRecipe, VStack, useToken, Drawer, Portal, Field, NativeSelect, Textarea, chakra, Tooltip, SegmentGroup, Menu, useChakraContext, Spinner, Code, Clipboard, IconButton, Badge, Button, Tabs, useTabs } from "@chakra-ui/react";
|
|
29
|
+
import { Box, Flex, Text, HStack, Input, useSlotRecipe, useRecipe, VStack, useToken, Drawer, Portal, Field, NativeSelect, Textarea, chakra, Tooltip, SegmentGroup, Menu, HoverCard, useChakraContext, Spinner, Code, Clipboard, IconButton, Badge, Button, Tabs, useTabs } from "@chakra-ui/react";
|
|
30
30
|
import { implementUIComponent, SliceEditPopover, SlicePredicateBuilder, formatPredicate, getSomeorUndefined, StateRuntime, buildSliceHandle, DEFAULT_SLICE_STATE, registerPlatformImplementation as registerPlatformImplementation$1, EastChakraSelect, EastChakraStringInput, EastChakraDateTimeInput, EastChakraFloatInput, EastChakraIntegerInput, formatTick, useSliceReactivity, SliceRailCluster, EastChakraComponent, ClauseChip, StateImpl, OverlayImpl, createUIStore, UIStoreProvider } from "@elaraai/east-ui-components";
|
|
31
31
|
import { createPortal } from "react-dom";
|
|
32
32
|
import { sliceMatches } from "@elaraai/east-ui/internal";
|
|
@@ -7446,6 +7446,11 @@ var faArrowDown = {
|
|
|
7446
7446
|
iconName: "arrow-down",
|
|
7447
7447
|
icon: [384, 512, [8595], "f063", "M169.4 502.6c12.5 12.5 32.8 12.5 45.3 0l160-160c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L224 402.7 224 32c0-17.7-14.3-32-32-32s-32 14.3-32 32l0 370.7-105.4-105.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3l160 160z"]
|
|
7448
7448
|
};
|
|
7449
|
+
var faCircleInfo = {
|
|
7450
|
+
prefix: "fas",
|
|
7451
|
+
iconName: "circle-info",
|
|
7452
|
+
icon: [512, 512, ["info-circle"], "f05a", "M256 512a256 256 0 1 0 0-512 256 256 0 1 0 0 512zM224 160a32 32 0 1 1 64 0 32 32 0 1 1 -64 0zm-8 64l48 0c13.3 0 24 10.7 24 24l0 88 8 0c13.3 0 24 10.7 24 24s-10.7 24-24 24l-80 0c-13.3 0-24-10.7-24-24s10.7-24 24-24l24 0 0-64-24 0c-13.3 0-24-10.7-24-24s10.7-24 24-24z"]
|
|
7453
|
+
};
|
|
7449
7454
|
const E3ConfigContext = createContext(null);
|
|
7450
7455
|
function E3Provider({ children: children2, config: config2, queryClient: externalClient }) {
|
|
7451
7456
|
const client = useMemo(
|
|
@@ -21861,13 +21866,13 @@ var hashIterableInts = function hashIterableInts2(iterator) {
|
|
|
21861
21866
|
}
|
|
21862
21867
|
return hash2;
|
|
21863
21868
|
};
|
|
21864
|
-
var hashInt = function hashInt2(
|
|
21869
|
+
var hashInt = function hashInt2(num2) {
|
|
21865
21870
|
var seed = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : DEFAULT_HASH_SEED;
|
|
21866
|
-
return seed * K +
|
|
21871
|
+
return seed * K + num2 | 0;
|
|
21867
21872
|
};
|
|
21868
|
-
var hashIntAlt = function hashIntAlt2(
|
|
21873
|
+
var hashIntAlt = function hashIntAlt2(num2) {
|
|
21869
21874
|
var seed = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : DEFAULT_HASH_SEED_ALT;
|
|
21870
|
-
return (seed << 5) + seed +
|
|
21875
|
+
return (seed << 5) + seed + num2 | 0;
|
|
21871
21876
|
};
|
|
21872
21877
|
var combineHashes = function combineHashes2(hash1, hash2) {
|
|
21873
21878
|
return hash1 * 2097152 + hash2;
|
|
@@ -23454,7 +23459,7 @@ var max = function max2(arr2) {
|
|
|
23454
23459
|
}
|
|
23455
23460
|
return max5;
|
|
23456
23461
|
};
|
|
23457
|
-
var mean
|
|
23462
|
+
var mean = function mean2(arr2) {
|
|
23458
23463
|
var begin3 = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : 0;
|
|
23459
23464
|
var end3 = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : arr2.length;
|
|
23460
23465
|
var total = 0;
|
|
@@ -25767,7 +25772,7 @@ var getPreference = function getPreference2(S2, preference) {
|
|
|
25767
25772
|
if (preference === "median") {
|
|
25768
25773
|
p2 = median(S2);
|
|
25769
25774
|
} else if (preference === "mean") {
|
|
25770
|
-
p2 = mean
|
|
25775
|
+
p2 = mean(S2);
|
|
25771
25776
|
} else if (preference === "min") {
|
|
25772
25777
|
p2 = min(S2);
|
|
25773
25778
|
} else if (preference === "max") {
|
|
@@ -56740,7 +56745,7 @@ function useBindingValue(binding) {
|
|
|
56740
56745
|
return { value, mode, pending, mutate, commit, discard };
|
|
56741
56746
|
}
|
|
56742
56747
|
const IDLE = { call: () => {
|
|
56743
|
-
}, result: null, status: "idle", pending: false };
|
|
56748
|
+
}, result: null, status: "idle", pending: false, error: null };
|
|
56744
56749
|
function useFuncCall(name, inputs, output2) {
|
|
56745
56750
|
const workspace = getReactiveDatasetCache().getConfig().workspace ?? "";
|
|
56746
56751
|
const ready4 = !!name && !!inputs && workspace !== "";
|
|
@@ -56763,15 +56768,22 @@ function useFuncCall(name, inputs, output2) {
|
|
|
56763
56768
|
useCallback(() => channelKey ? defaultFuncRuntime.getKeyVersion(channelKey) : 0, [channelKey])
|
|
56764
56769
|
);
|
|
56765
56770
|
return useMemo(() => {
|
|
56771
|
+
var _a2;
|
|
56766
56772
|
if (!handle) return IDLE;
|
|
56767
56773
|
let result = null;
|
|
56768
56774
|
let status = "idle";
|
|
56769
56775
|
let pending = false;
|
|
56776
|
+
let error3 = null;
|
|
56770
56777
|
try {
|
|
56771
56778
|
const read = handle.read();
|
|
56772
56779
|
result = read.type === "some" ? read.value : null;
|
|
56773
56780
|
status = handle.status().type;
|
|
56774
56781
|
pending = handle.pending();
|
|
56782
|
+
const err = handle.error();
|
|
56783
|
+
if (err.type === "some") {
|
|
56784
|
+
const v3 = err.value;
|
|
56785
|
+
error3 = { kind: ((_a2 = v3.kind) == null ? void 0 : _a2.type) ?? "failed", message: v3.message ?? "", stdout: v3.stdout ?? "", stderr: v3.stderr ?? "" };
|
|
56786
|
+
}
|
|
56775
56787
|
} catch {
|
|
56776
56788
|
}
|
|
56777
56789
|
return { call: (...args) => {
|
|
@@ -56779,7 +56791,7 @@ function useFuncCall(name, inputs, output2) {
|
|
|
56779
56791
|
handle.call(...args);
|
|
56780
56792
|
} catch {
|
|
56781
56793
|
}
|
|
56782
|
-
}, result, status, pending };
|
|
56794
|
+
}, result, status, pending, error: error3 };
|
|
56783
56795
|
}, [handle, version2]);
|
|
56784
56796
|
}
|
|
56785
56797
|
var propTypes = { exports: {} };
|
|
@@ -59023,6 +59035,370 @@ function Circle(_ref2) {
|
|
|
59023
59035
|
className: cx("visx-circle", className)
|
|
59024
59036
|
}, restProps));
|
|
59025
59037
|
}
|
|
59038
|
+
function fillHelp(entry, vars) {
|
|
59039
|
+
const sub = (s2) => s2.replace(/\{(\w+)\}/g, (m2, k2) => k2 in vars ? vars[k2] : m2);
|
|
59040
|
+
return {
|
|
59041
|
+
term: sub(entry.term),
|
|
59042
|
+
gist: sub(entry.gist),
|
|
59043
|
+
detail: sub(entry.detail),
|
|
59044
|
+
...entry.jargon !== void 0 ? { jargon: sub(entry.jargon) } : {}
|
|
59045
|
+
};
|
|
59046
|
+
}
|
|
59047
|
+
const HELP = {
|
|
59048
|
+
// ── Guidance mode (the toggle itself) ───────────────────────────────
|
|
59049
|
+
guidance: {
|
|
59050
|
+
term: "Guidance",
|
|
59051
|
+
gist: "Plain-language explainers for everything on this surface.",
|
|
59052
|
+
detail: "While this is on, hover any heading, value or chart label to see what it means — in business terms, with the statistical name underneath. Click to turn it off."
|
|
59053
|
+
},
|
|
59054
|
+
// ── Header ──────────────────────────────────────────────────────────
|
|
59055
|
+
header: {
|
|
59056
|
+
term: "The question",
|
|
59057
|
+
gist: "You’re asking whether {treatment} actually moved {outcome}.",
|
|
59058
|
+
detail: "Pick the lever you changed and the result you cared about, then hit Run — the surface answers whether the change is real and trustworthy.",
|
|
59059
|
+
jargon: "a binary-treatment causal effect"
|
|
59060
|
+
},
|
|
59061
|
+
// ── Set-up rail ─────────────────────────────────────────────────────
|
|
59062
|
+
step_treatment: {
|
|
59063
|
+
term: "What did you change?",
|
|
59064
|
+
gist: "The lever you’re testing — a yes/no flag on each {subject}.",
|
|
59065
|
+
detail: "It splits {subjects} into a “did” group and a “didn’t” group; the whole experiment compares those two.",
|
|
59066
|
+
jargon: "the treatment (binary)"
|
|
59067
|
+
},
|
|
59068
|
+
step_outcome: {
|
|
59069
|
+
term: "What did you want it to improve?",
|
|
59070
|
+
gist: "The result you’re hoping {treatment} moved.",
|
|
59071
|
+
detail: "A number measured on each {subject} (revenue, a duration, a score, …). The effect is the change in this we can credit to the lever.",
|
|
59072
|
+
jargon: "the outcome"
|
|
59073
|
+
},
|
|
59074
|
+
step_confounders: {
|
|
59075
|
+
term: "What else was different?",
|
|
59076
|
+
gist: "Things about the “did” group that could also explain the change.",
|
|
59077
|
+
detail: "If the {subjects} you treated already differed on something that matters — bigger, older, higher-value — that, not the lever, could be moving the result. We level the playing field on each of these so the comparison is fair. Add the ones you suspect.",
|
|
59078
|
+
jargon: "confounders / the backdoor adjustment set"
|
|
59079
|
+
},
|
|
59080
|
+
step_population: {
|
|
59081
|
+
term: "Which {subjects}?",
|
|
59082
|
+
gist: "Narrow the experiment to a slice of the {subjects}.",
|
|
59083
|
+
detail: "Filter to a subset you care about — a region, a period, a category — before measuring, so the answer is about the {subjects} that matter.",
|
|
59084
|
+
jargon: "population / cohort filter"
|
|
59085
|
+
},
|
|
59086
|
+
adv_method: {
|
|
59087
|
+
term: "How to compare",
|
|
59088
|
+
gist: "The maths used to make the like-for-like comparison.",
|
|
59089
|
+
detail: "“Regression” fits a line through the confounders; “re-weighting” up-weights rare look-alikes. They usually agree — if they don’t, treat the result with care.",
|
|
59090
|
+
jargon: "estimator: linear regression vs propensity-score weighting"
|
|
59091
|
+
},
|
|
59092
|
+
adv_estimand: {
|
|
59093
|
+
term: "Answer for",
|
|
59094
|
+
gist: "Who the effect is reported for.",
|
|
59095
|
+
detail: "“All” averages over everyone; “only treated” asks what the lever did for the {subjects} you actually treated — useful when you’d only ever apply it to them.",
|
|
59096
|
+
jargon: "estimand: ATE vs ATT"
|
|
59097
|
+
},
|
|
59098
|
+
confounder_imbalance: {
|
|
59099
|
+
term: "How lopsided this one was",
|
|
59100
|
+
gist: "How different the two groups were on this factor, before adjusting.",
|
|
59101
|
+
detail: "A long bar means the treated and untreated {subjects} started far apart here — exactly the kind of gap the adjustment has to close.",
|
|
59102
|
+
jargon: "standardised mean difference (SMD)"
|
|
59103
|
+
},
|
|
59104
|
+
// ── Tabs ────────────────────────────────────────────────────────────
|
|
59105
|
+
tab_answer: {
|
|
59106
|
+
term: "Answer",
|
|
59107
|
+
gist: "The headline: did {treatment} move {outcome}, and by how much?",
|
|
59108
|
+
detail: "Shows the raw gap, the fair like-for-like estimate, and an honest verdict."
|
|
59109
|
+
},
|
|
59110
|
+
tab_trust: {
|
|
59111
|
+
term: "Can we trust it?",
|
|
59112
|
+
gist: "The stress tests we ran to try to break the answer.",
|
|
59113
|
+
detail: "Each check probes a way the result could be a fluke; green means it held up."
|
|
59114
|
+
},
|
|
59115
|
+
tab_dose: {
|
|
59116
|
+
term: "How much?",
|
|
59117
|
+
gist: "How the result changes as a factor goes up or down.",
|
|
59118
|
+
detail: "Not just yes/no — the shape of the response, so you can find the sweet spot."
|
|
59119
|
+
},
|
|
59120
|
+
// ── Answer tab ──────────────────────────────────────────────────────
|
|
59121
|
+
answer_effect: {
|
|
59122
|
+
term: "Like-for-like effect",
|
|
59123
|
+
gist: "The change in {outcome} we can credit to {treatment}.",
|
|
59124
|
+
detail: "It compares treated vs untreated {subjects} who were otherwise similar — so it strips out “who you targeted” and leaves the lever’s real impact.",
|
|
59125
|
+
jargon: "the backdoor-adjusted average treatment effect"
|
|
59126
|
+
},
|
|
59127
|
+
answer_ci: {
|
|
59128
|
+
term: "95% range",
|
|
59129
|
+
gist: "Where the true effect most likely sits.",
|
|
59130
|
+
detail: "If this range doesn’t cross 0, the effect is real, not noise. The wider it is, the less certain — usually because of fewer {subjects}.",
|
|
59131
|
+
jargon: "95% confidence interval"
|
|
59132
|
+
},
|
|
59133
|
+
answer_flip: {
|
|
59134
|
+
term: "Raw and like-for-like disagree",
|
|
59135
|
+
gist: "The plain average points one way; the fair comparison points the other.",
|
|
59136
|
+
detail: "This happens when the treated group differed sharply on a confounder. Trust the like-for-like number — the raw gap is misleading here.",
|
|
59137
|
+
jargon: "confounding / Simpson’s-paradox sign flip"
|
|
59138
|
+
},
|
|
59139
|
+
answer_cautious: {
|
|
59140
|
+
term: "Treat this as provisional",
|
|
59141
|
+
gist: "We got a number, but a trust check failed.",
|
|
59142
|
+
detail: "The estimate may still be driven by something we didn’t adjust for. See “Can we trust it?” before acting on it.",
|
|
59143
|
+
jargon: "adjustment_insufficient verdict"
|
|
59144
|
+
},
|
|
59145
|
+
forest_plot: {
|
|
59146
|
+
term: "Raw average vs. like-for-like",
|
|
59147
|
+
gist: "The misleading number next to the fair one.",
|
|
59148
|
+
detail: "Each bar is an estimate with its uncertainty range; the dashed line is “no effect”. A bar clear of that line is a real change.",
|
|
59149
|
+
jargon: "a forest plot of naive vs adjusted estimates"
|
|
59150
|
+
},
|
|
59151
|
+
forest_naive: {
|
|
59152
|
+
term: "Raw average",
|
|
59153
|
+
gist: "The plain difference, with no adjusting.",
|
|
59154
|
+
detail: "Just treated-minus-untreated. It mixes in who you targeted, so it can mislead — it’s here only as the “before” picture.",
|
|
59155
|
+
jargon: "the unadjusted mean difference"
|
|
59156
|
+
},
|
|
59157
|
+
forest_adjusted: {
|
|
59158
|
+
term: "Like-for-like",
|
|
59159
|
+
gist: "The fair comparison after levelling the confounders.",
|
|
59160
|
+
detail: "This is the number to act on — what {treatment} did among otherwise-similar {subjects}.",
|
|
59161
|
+
jargon: "the adjusted effect"
|
|
59162
|
+
},
|
|
59163
|
+
balance: {
|
|
59164
|
+
term: "How unbalanced each one was",
|
|
59165
|
+
gist: "How far apart the two groups started on each factor.",
|
|
59166
|
+
detail: "Big bars are the factors the adjustment had to work hardest to correct — and the ones most worth double-checking.",
|
|
59167
|
+
jargon: "pre-adjustment covariate balance (SMD)"
|
|
59168
|
+
},
|
|
59169
|
+
counts: {
|
|
59170
|
+
term: "The {subject} counts",
|
|
59171
|
+
gist: "How many {subjects} went into the comparison.",
|
|
59172
|
+
detail: "“Compared like-for-like” are the ones with a fair match on the other side; “no fair match” were set aside because nobody comparable existed.",
|
|
59173
|
+
jargon: "n total / on-support / off-support"
|
|
59174
|
+
},
|
|
59175
|
+
// ── Refusal zones + overlap ─────────────────────────────────────────
|
|
59176
|
+
overlap_histogram: {
|
|
59177
|
+
term: "Do the two groups overlap?",
|
|
59178
|
+
gist: "Whether treated and untreated {subjects} actually look alike anywhere.",
|
|
59179
|
+
detail: "Each bar is how many {subjects} sit at a given “likelihood of being treated”. Where the two arms both have bars, a fair comparison exists; if they barely meet, there’s nothing to compare.",
|
|
59180
|
+
jargon: "propensity-score overlap / common support"
|
|
59181
|
+
},
|
|
59182
|
+
refusal_positivity: {
|
|
59183
|
+
term: "No like-for-like comparison exists",
|
|
59184
|
+
gist: "The treated and untreated {subjects} are too different to compare.",
|
|
59185
|
+
detail: "For almost every treated {subject} there’s no similar untreated one (or vice-versa), so there’s no fair “other side”. We refuse rather than extrapolate into thin air. Try fewer or different confounders.",
|
|
59186
|
+
jargon: "positivity / overlap violation"
|
|
59187
|
+
},
|
|
59188
|
+
refusal_not_estimable: {
|
|
59189
|
+
term: "Can’t be estimated",
|
|
59190
|
+
gist: "Almost every {subject} is on one side of {treatment}, so there’s no comparison group.",
|
|
59191
|
+
detail: "Measuring an effect needs a decent number of {subjects} who got the lever AND who didn’t. When one side is just a handful, we refuse to guess rather than read too much into a few people.",
|
|
59192
|
+
jargon: "no treatment variation"
|
|
59193
|
+
},
|
|
59194
|
+
// ── Trust tab ───────────────────────────────────────────────────────
|
|
59195
|
+
trust_intro: {
|
|
59196
|
+
term: "Can we trust it?",
|
|
59197
|
+
gist: "Before believing the answer, we tried to break it.",
|
|
59198
|
+
detail: "Each row is a different way the result could be a fluke; colour shows whether it held up (pass) or wobbled (caution)."
|
|
59199
|
+
},
|
|
59200
|
+
check_shuffle: {
|
|
59201
|
+
term: "Shuffle test",
|
|
59202
|
+
gist: "We randomly re-label who got {treatment} and re-measure.",
|
|
59203
|
+
detail: "On fake labels a genuine effect should collapse to nothing. If a “fake” effect still appears, the real one isn’t trustworthy.",
|
|
59204
|
+
jargon: "placebo / permutation refuter"
|
|
59205
|
+
},
|
|
59206
|
+
check_dropsome: {
|
|
59207
|
+
term: "Drop-some test",
|
|
59208
|
+
gist: "We re-run on random slices of the {subjects}.",
|
|
59209
|
+
detail: "A trustworthy effect barely moves when you drop some data; a jumpy one is resting on a few {subjects}.",
|
|
59210
|
+
jargon: "data-subset refuter"
|
|
59211
|
+
},
|
|
59212
|
+
check_decoy: {
|
|
59213
|
+
term: "Decoy factor",
|
|
59214
|
+
gist: "We add a totally random extra factor and re-measure.",
|
|
59215
|
+
detail: "A made-up factor shouldn’t change the answer. If it does, the model is over-reacting to noise.",
|
|
59216
|
+
jargon: "random-common-cause refuter"
|
|
59217
|
+
},
|
|
59218
|
+
check_hidden: {
|
|
59219
|
+
term: "Hidden cause",
|
|
59220
|
+
gist: "How strong an unrecorded cause would have to be to overturn the result.",
|
|
59221
|
+
detail: "Something you didn’t measure could drive both who got {treatment} and {outcome}. “Holds throughout” means only an implausibly strong hidden cause could flip it.",
|
|
59222
|
+
jargon: "unobserved-confounding sensitivity / E-value"
|
|
59223
|
+
},
|
|
59224
|
+
sensitivity: {
|
|
59225
|
+
term: "Effect as a hidden cause gets stronger",
|
|
59226
|
+
gist: "What happens to the effect if an unrecorded cause is dialled up.",
|
|
59227
|
+
detail: "The line is the estimate as a simulated hidden cause grows. The point where it crosses 0 is how much hidden bias it would take to erase the result.",
|
|
59228
|
+
jargon: "sensitivity (tipping-point) curve"
|
|
59229
|
+
},
|
|
59230
|
+
// ── Dose tab ────────────────────────────────────────────────────────
|
|
59231
|
+
dose_curve: {
|
|
59232
|
+
term: "{outcome} gained vs. {feature}",
|
|
59233
|
+
gist: "How {outcome} responds as {feature} goes up.",
|
|
59234
|
+
detail: "The line is the average change in {outcome} at each level of {feature}, with its uncertainty band — the shape tells you where more pays off and where it flattens.",
|
|
59235
|
+
jargon: "accumulated local effects (ALE) dose-response"
|
|
59236
|
+
},
|
|
59237
|
+
dose_here: {
|
|
59238
|
+
term: "You are here",
|
|
59239
|
+
gist: "Where most of your {subjects} currently sit on {feature}.",
|
|
59240
|
+
detail: "The busiest level today — your starting point for deciding whether to push higher or lower.",
|
|
59241
|
+
jargon: "the modal bin"
|
|
59242
|
+
},
|
|
59243
|
+
dose_sweet: {
|
|
59244
|
+
term: "Sweet spot",
|
|
59245
|
+
gist: "Where the gains are solid and start to flatten.",
|
|
59246
|
+
detail: "Past here, pushing {feature} higher buys little extra {outcome} — a sensible target.",
|
|
59247
|
+
jargon: "the diminishing-returns knee"
|
|
59248
|
+
},
|
|
59249
|
+
dose_reco: {
|
|
59250
|
+
term: "Recommended",
|
|
59251
|
+
gist: "The level of {feature} we’d aim for.",
|
|
59252
|
+
detail: "The sweet-spot level and the {outcome} you’d expect there, with its range — and the trade-off of going one step further.",
|
|
59253
|
+
jargon: "the recommended operating point"
|
|
59254
|
+
},
|
|
59255
|
+
dose_marginal: {
|
|
59256
|
+
term: "Extra {outcome} per step",
|
|
59257
|
+
gist: "What each additional step of {feature} buys you.",
|
|
59258
|
+
detail: "Tall early bars then short ones is the classic diminishing-returns shape — spend effort where the bars are tall.",
|
|
59259
|
+
jargon: "marginal / incremental effect per bin"
|
|
59260
|
+
},
|
|
59261
|
+
// ── Verdicts ────────────────────────────────────────────────────────
|
|
59262
|
+
verdict_causal: {
|
|
59263
|
+
term: "Causal",
|
|
59264
|
+
gist: "A real change you can credit to {treatment} — and it held up.",
|
|
59265
|
+
detail: "The like-for-like effect is clear, sizeable, and survived the trust checks. Safe to act on.",
|
|
59266
|
+
jargon: "a robust, identified effect"
|
|
59267
|
+
},
|
|
59268
|
+
verdict_modest: {
|
|
59269
|
+
term: "Modest / unclear",
|
|
59270
|
+
gist: "There may be an effect, but it’s small or fuzzy.",
|
|
59271
|
+
detail: "The fair comparison can’t cleanly separate it from no-effect (the range straddles 0, or it’s tiny). Don’t bet much on it.",
|
|
59272
|
+
jargon: "effect CI spans zero / below materiality"
|
|
59273
|
+
},
|
|
59274
|
+
verdict_adjustment_insufficient: {
|
|
59275
|
+
term: "Not trustworthy yet",
|
|
59276
|
+
gist: "We got a number, but a trust check failed.",
|
|
59277
|
+
detail: "Likely something we didn’t adjust for is still in play. Treat as provisional and look at “Can we trust it?”.",
|
|
59278
|
+
jargon: "placebo / robustness failure"
|
|
59279
|
+
},
|
|
59280
|
+
verdict_non_identifiable_positivity: {
|
|
59281
|
+
term: "No fair comparison",
|
|
59282
|
+
gist: "Treated and untreated {subjects} don’t overlap enough to compare.",
|
|
59283
|
+
detail: "There’s no like-for-like “other side”, so any number would be extrapolation. See the overlap chart.",
|
|
59284
|
+
jargon: "positivity / overlap violation"
|
|
59285
|
+
},
|
|
59286
|
+
verdict_not_estimable: {
|
|
59287
|
+
term: "Can’t be estimated",
|
|
59288
|
+
gist: "Almost everyone is on one side of {treatment} — no comparison group.",
|
|
59289
|
+
detail: "One arm is just a handful of {subjects}, so we won’t guess an effect from it.",
|
|
59290
|
+
jargon: "no treatment variation"
|
|
59291
|
+
},
|
|
59292
|
+
// ── Validate tab ────────────────────────────────────────────────────
|
|
59293
|
+
tab_validate: {
|
|
59294
|
+
term: "Validate",
|
|
59295
|
+
gist: "The real experiment you’d run to prove this — for sure.",
|
|
59296
|
+
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."
|
|
59297
|
+
},
|
|
59298
|
+
validate_size: {
|
|
59299
|
+
term: "How many to run",
|
|
59300
|
+
gist: "The number of {subjects} the trial needs.",
|
|
59301
|
+
detail: "Enough to spot a change this size if it’s real. A bigger or cleaner effect needs fewer; a small or noisy one needs many more.",
|
|
59302
|
+
jargon: "required sample size at the target power"
|
|
59303
|
+
},
|
|
59304
|
+
validate_split: {
|
|
59305
|
+
term: "How to split them",
|
|
59306
|
+
gist: "How many get {treatment} versus are left alone.",
|
|
59307
|
+
detail: "Assign at random — that’s what makes the two groups comparable with no adjusting. An even split is the most efficient; treating fewer needs a larger total.",
|
|
59308
|
+
jargon: "randomised allocation between arms"
|
|
59309
|
+
},
|
|
59310
|
+
validate_match: {
|
|
59311
|
+
term: "Match the groups on",
|
|
59312
|
+
gist: "Keep both groups balanced on these — they were lopsided last time.",
|
|
59313
|
+
detail: "Give each group the same mix of these factors when you split, so neither one starts ahead. These are the factors that muddied the original comparison most.",
|
|
59314
|
+
jargon: "stratified / blocked randomisation on the high-imbalance covariates"
|
|
59315
|
+
},
|
|
59316
|
+
validate_power: {
|
|
59317
|
+
term: "Chance of detecting it",
|
|
59318
|
+
gist: "How likely the trial is to catch the effect, by size.",
|
|
59319
|
+
detail: "More {subjects} means a better chance of a clear result. The marker shows where today’s data sits — usually well short of a confident answer.",
|
|
59320
|
+
jargon: "statistical power vs sample size"
|
|
59321
|
+
},
|
|
59322
|
+
validate_holdback: {
|
|
59323
|
+
term: "Hold back a control",
|
|
59324
|
+
gist: "Keep a random group untreated to compare against.",
|
|
59325
|
+
detail: "Right now almost everything got {treatment}, so there’s nothing to compare to. Next time, leave a random sample untreated — that group becomes the fair baseline.",
|
|
59326
|
+
jargon: "a randomised control arm"
|
|
59327
|
+
},
|
|
59328
|
+
// ── Journal ─────────────────────────────────────────────────────────
|
|
59329
|
+
journal: {
|
|
59330
|
+
term: "Committed experiments",
|
|
59331
|
+
gist: "A log of the questions you’ve saved.",
|
|
59332
|
+
detail: "Each row is a framing you committed — its verdict and headline effect — so the team can see what’s been tried and what held up.",
|
|
59333
|
+
jargon: "the experiment journal"
|
|
59334
|
+
}
|
|
59335
|
+
};
|
|
59336
|
+
const GuidanceContext = createContext({ on: true, vars: {} });
|
|
59337
|
+
function GuidanceProvider({ on: on3, vars, children: children2 }) {
|
|
59338
|
+
return /* @__PURE__ */ jsx$1(GuidanceContext.Provider, { value: { on: on3, vars }, children: children2 });
|
|
59339
|
+
}
|
|
59340
|
+
function HelpCard({ id: id3, extraVars }) {
|
|
59341
|
+
const { vars } = useContext(GuidanceContext);
|
|
59342
|
+
const e3 = fillHelp(HELP[id3], { ...vars, ...extraVars });
|
|
59343
|
+
const slot = useSlotRecipe({ key: "hoverCard" })();
|
|
59344
|
+
return /* @__PURE__ */ jsxs(Box, { display: "flex", flexDirection: "column", gap: "1.5", children: [
|
|
59345
|
+
/* @__PURE__ */ jsx$1(Box, { css: slot.title, mb: "0", children: e3.term }),
|
|
59346
|
+
/* @__PURE__ */ jsx$1(Text, { textStyle: "body.sm", color: "fg.default", lineHeight: "1.5", children: e3.gist }),
|
|
59347
|
+
/* @__PURE__ */ jsx$1(Box, { css: slot.description, lineHeight: "1.5", children: e3.detail }),
|
|
59348
|
+
e3.jargon && /* @__PURE__ */ jsxs(Text, { textStyle: "caption", color: "fg.subtle", mt: "0.5", pt: "1.5", borderTopWidth: "1px", borderColor: "border.subtle", children: [
|
|
59349
|
+
"Technically: ",
|
|
59350
|
+
e3.jargon
|
|
59351
|
+
] })
|
|
59352
|
+
] });
|
|
59353
|
+
}
|
|
59354
|
+
function Hover({ id: id3, extraVars, trigger }) {
|
|
59355
|
+
return /* @__PURE__ */ jsxs(HoverCard.Root, { openDelay: 150, closeDelay: 80, size: "sm", positioning: { placement: "top" }, children: [
|
|
59356
|
+
/* @__PURE__ */ jsx$1(HoverCard.Trigger, { asChild: true, children: trigger }),
|
|
59357
|
+
/* @__PURE__ */ jsx$1(Portal, { children: /* @__PURE__ */ jsx$1(HoverCard.Positioner, { children: /* @__PURE__ */ jsxs(HoverCard.Content, { children: [
|
|
59358
|
+
/* @__PURE__ */ jsx$1(HoverCard.Arrow, { children: /* @__PURE__ */ jsx$1(HoverCard.ArrowTip, {}) }),
|
|
59359
|
+
/* @__PURE__ */ jsx$1(HelpCard, { id: id3, extraVars })
|
|
59360
|
+
] }) }) })
|
|
59361
|
+
] });
|
|
59362
|
+
}
|
|
59363
|
+
function Help({ id: id3, extraVars, children: children2, display = "inline", gap }) {
|
|
59364
|
+
const { on: on3 } = useContext(GuidanceContext);
|
|
59365
|
+
if (!on3) return /* @__PURE__ */ jsx$1(Fragment, { children: children2 });
|
|
59366
|
+
return /* @__PURE__ */ jsx$1(Hover, { id: id3, extraVars, trigger: (
|
|
59367
|
+
// `tabIndex={0}` makes the term keyboard-focusable — the HoverCard's zag
|
|
59368
|
+
// machine opens on focus, so the glossary is reachable without a pointer.
|
|
59369
|
+
/* @__PURE__ */ jsx$1(
|
|
59370
|
+
Box,
|
|
59371
|
+
{
|
|
59372
|
+
as: "span",
|
|
59373
|
+
cursor: "help",
|
|
59374
|
+
tabIndex: 0,
|
|
59375
|
+
display,
|
|
59376
|
+
...gap ? { alignItems: "center", gap } : {},
|
|
59377
|
+
_focusVisible: { outline: "1px dotted", outlineColor: "brand.fg", outlineOffset: "2px", borderRadius: "2px" },
|
|
59378
|
+
children: children2
|
|
59379
|
+
}
|
|
59380
|
+
)
|
|
59381
|
+
) });
|
|
59382
|
+
}
|
|
59383
|
+
function GuidanceToggle({ on: on3, onToggle }) {
|
|
59384
|
+
const iconButton = useRecipe({ key: "iconButton" });
|
|
59385
|
+
return /* @__PURE__ */ jsx$1(Hover, { id: "guidance", trigger: /* @__PURE__ */ jsx$1(
|
|
59386
|
+
Box,
|
|
59387
|
+
{
|
|
59388
|
+
as: "button",
|
|
59389
|
+
css: iconButton({ variant: on3 ? "subtle" : "ghost", size: "sm" }),
|
|
59390
|
+
onClick: onToggle,
|
|
59391
|
+
"aria-pressed": on3,
|
|
59392
|
+
"aria-label": "Toggle guidance",
|
|
59393
|
+
color: on3 ? "brand.fg" : "fg.muted",
|
|
59394
|
+
children: /* @__PURE__ */ jsx$1(FontAwesomeIcon, { icon: faCircleInfo })
|
|
59395
|
+
}
|
|
59396
|
+
) });
|
|
59397
|
+
}
|
|
59398
|
+
function ChartLabel({ x: x2, y: y2, w: w2, align, size: size3, weight: weight8, color: color2, family, help, children: children2 }) {
|
|
59399
|
+
const span = /* @__PURE__ */ jsx$1("span", { style: { fontSize: `${size3}px`, fontFamily: family, fontWeight: weight8, color: color2, lineHeight: 1, whiteSpace: "nowrap" }, children: children2 });
|
|
59400
|
+
return /* @__PURE__ */ jsx$1("foreignObject", { x: x2, y: y2, width: w2, height: size3 + 6, style: { overflow: "visible" }, children: /* @__PURE__ */ jsx$1("div", { style: { textAlign: align, fontSize: `${size3}px`, lineHeight: 1 }, children: help ? /* @__PURE__ */ jsx$1(Help, { id: help, children: span }) : span }) });
|
|
59401
|
+
}
|
|
59026
59402
|
function svgText(size3, fill, family, weight8) {
|
|
59027
59403
|
return { fontSize: `${size3}px`, fill, fontFamily: family, ...weight8 !== void 0 ? { fontWeight: weight8 } : {} };
|
|
59028
59404
|
}
|
|
@@ -59078,7 +59454,7 @@ const niceCeil = (v3) => {
|
|
|
59078
59454
|
const step3 = n2 <= 1 ? 1 : n2 <= 1.5 ? 1.5 : n2 <= 2 ? 2 : n2 <= 3 ? 3 : n2 <= 4 ? 4 : n2 <= 5 ? 5 : n2 <= 6 ? 6 : n2 <= 8 ? 8 : 10;
|
|
59079
59455
|
return step3 * mag;
|
|
59080
59456
|
};
|
|
59081
|
-
function ForestPlot({ rows, min: min4, max: max5, unit: unit2, height: height2 }) {
|
|
59457
|
+
function ForestPlot({ rows, min: min4, max: max5, unit: unit2, height: height2, rowHelp }) {
|
|
59082
59458
|
const t4 = useChartTheme();
|
|
59083
59459
|
const [ref, w2] = useMeasuredWidth();
|
|
59084
59460
|
const n2 = rows.length;
|
|
@@ -59088,11 +59464,14 @@ function ForestPlot({ rows, min: min4, max: max5, unit: unit2, height: height2 }
|
|
|
59088
59464
|
const innerH = hPx - padT - padB;
|
|
59089
59465
|
const x2 = createLinearScale({ domain: [min4, max5], range: [0, innerW] });
|
|
59090
59466
|
const rowH = innerH / Math.max(1, n2);
|
|
59091
|
-
const zeroX = x2(0);
|
|
59092
|
-
const
|
|
59467
|
+
const zeroX = Math.max(0, Math.min(innerW, x2(0)));
|
|
59468
|
+
const frac = (v3) => max5 > min4 ? (v3 - min4) / (max5 - min4) : 0.5;
|
|
59469
|
+
const anchorAt = (v3) => frac(v3) < 0.06 ? "start" : frac(v3) > 0.94 ? "end" : "middle";
|
|
59470
|
+
const zf = frac(0);
|
|
59471
|
+
const ticks2 = zf > 0.12 && zf < 0.88 ? [min4, 0, max5] : [min4, max5];
|
|
59093
59472
|
return /* @__PURE__ */ jsx$1(Box, { ref, width: "100%", height: `${hPx}px`, children: w2 > 0 && /* @__PURE__ */ jsx$1("svg", { width: w2, height: hPx, style: { display: "block" }, children: /* @__PURE__ */ jsxs(Group, { left: padL, top: padT, children: [
|
|
59094
59473
|
/* @__PURE__ */ jsx$1(Line, { from: { x: zeroX, y: -4 }, to: { x: zeroX, y: innerH }, stroke: t4.muted, strokeWidth: 1, strokeDasharray: "3 2" }),
|
|
59095
|
-
/* @__PURE__ */ jsx$1("text", { x: zeroX, y: -6, textAnchor:
|
|
59474
|
+
/* @__PURE__ */ jsx$1("text", { x: zeroX, y: -6, textAnchor: anchorAt(0), style: svgText(t4.labelSize, t4.muted, t4.mono), children: "no effect" }),
|
|
59096
59475
|
rows.map((r2, i) => {
|
|
59097
59476
|
const cy = rowH * i + rowH / 2;
|
|
59098
59477
|
const col = t4.tone(r2.tone);
|
|
@@ -59101,20 +59480,20 @@ function ForestPlot({ rows, min: min4, max: max5, unit: unit2, height: height2 }
|
|
|
59101
59480
|
/* @__PURE__ */ jsx$1(Line, { from: { x: x2(r2.lo), y: -4 }, to: { x: x2(r2.lo), y: 4 }, stroke: col, strokeWidth: 1.5 }),
|
|
59102
59481
|
/* @__PURE__ */ jsx$1(Line, { from: { x: x2(r2.hi), y: -4 }, to: { x: x2(r2.hi), y: 4 }, stroke: col, strokeWidth: 1.5 }),
|
|
59103
59482
|
/* @__PURE__ */ jsx$1(Circle, { cx: x2(r2.est), cy: 0, r: t4.scatterRadius, fill: col, stroke: t4.surface, strokeWidth: 1.8 }),
|
|
59104
|
-
/* @__PURE__ */ jsx$1(
|
|
59483
|
+
/* @__PURE__ */ jsx$1(ChartLabel, { x: -padL + 2, y: -13, w: padL - 6, align: "left", size: t4.labelSize, weight: 600, color: t4.ink, family: t4.body, ...(rowHelp == null ? void 0 : rowHelp[i]) ? { help: rowHelp[i] } : {}, children: r2.label }),
|
|
59105
59484
|
r2.note && /* @__PURE__ */ jsx$1("text", { x: -padL + 2, y: 9, style: svgText(9, t4.faint, t4.body), children: r2.note }),
|
|
59106
59485
|
/* @__PURE__ */ jsx$1("text", { x: innerW + padR - 2, y: 4, textAnchor: "end", style: svgText(t4.titleSize, col, t4.mono, 700), children: signed$1(r2.est) })
|
|
59107
59486
|
] }, i);
|
|
59108
59487
|
}),
|
|
59109
59488
|
/* @__PURE__ */ jsx$1(Line, { from: { x: 0, y: innerH }, to: { x: innerW, y: innerH }, stroke: t4.rule, strokeWidth: 1 }),
|
|
59110
|
-
ticks2.map((tk, i) => /* @__PURE__ */ jsxs(Group, { left: x2(tk), children: [
|
|
59489
|
+
ticks2.map((tk, i) => /* @__PURE__ */ jsxs(Group, { left: Math.max(0, Math.min(innerW, x2(tk))), children: [
|
|
59111
59490
|
/* @__PURE__ */ jsx$1(Line, { from: { x: 0, y: innerH }, to: { x: 0, y: innerH + 4 }, stroke: t4.ruleStrong, strokeWidth: 1 }),
|
|
59112
|
-
/* @__PURE__ */ jsx$1("text", { x: 0, y: innerH + 14, textAnchor:
|
|
59491
|
+
/* @__PURE__ */ jsx$1("text", { x: 0, y: innerH + 14, textAnchor: anchorAt(tk), style: svgText(t4.labelSize, t4.muted, t4.mono), children: signed$1(tk) })
|
|
59113
59492
|
] }, `t${i}`)),
|
|
59114
59493
|
unit2 && /* @__PURE__ */ jsx$1("text", { x: innerW / 2, y: innerH + 26, textAnchor: "middle", style: svgText(t4.labelSize, t4.muted, t4.mono), children: unit2 })
|
|
59115
59494
|
] }) }) });
|
|
59116
59495
|
}
|
|
59117
|
-
function AreaRange({ lo: lo2, mid, hi, xTicks = [], yTicks = [], zero: zero2, tone: toneName, marks = [], height: height2 }) {
|
|
59496
|
+
function AreaRange({ lo: lo2, mid, hi, xTicks = [], yTicks = [], zero: zero2, tone: toneName, marks = [], height: height2, yFormat = "signed" }) {
|
|
59118
59497
|
const t4 = useChartTheme();
|
|
59119
59498
|
const [ref, w2] = useMeasuredWidth();
|
|
59120
59499
|
const hPx = height2 ?? 100;
|
|
@@ -59126,13 +59505,18 @@ function AreaRange({ lo: lo2, mid, hi, xTicks = [], yTicks = [], zero: zero2, to
|
|
|
59126
59505
|
const innerW = Math.max(0, w2 - padL - padR);
|
|
59127
59506
|
const innerH = hPx - padT - padB;
|
|
59128
59507
|
const xScale = createLinearScale({ domain: [0, Math.max(1, mid.length - 1)], range: [0, innerW] });
|
|
59129
|
-
const
|
|
59130
|
-
const
|
|
59508
|
+
const fin = (x2, fallback) => typeof x2 === "number" && Number.isFinite(x2) ? x2 : fallback;
|
|
59509
|
+
const data4 = mid.map((m2, i) => {
|
|
59510
|
+
const mm = fin(m2, 0);
|
|
59511
|
+
return { i, lo: fin(lo2[i], mm), hi: fin(hi[i], mm), mid: mm };
|
|
59512
|
+
});
|
|
59513
|
+
const dataMin = Math.min(...data4.map((d2) => d2.lo), zero2 ?? Infinity);
|
|
59514
|
+
const dataMax = Math.max(...data4.map((d2) => d2.hi), zero2 ?? -Infinity);
|
|
59131
59515
|
const yHiN = niceCeil(Math.max(dataMax, 0));
|
|
59132
59516
|
const yLoN = dataMin < -0.08 * yHiN ? -niceCeil(-dataMin) : 0;
|
|
59133
59517
|
const yScale = createLinearScale({ domain: [yLoN, yHiN], range: [innerH, 0] });
|
|
59134
|
-
const
|
|
59135
|
-
const
|
|
59518
|
+
const yfmt = (v3) => yFormat === "percent" ? `${Math.round(v3)}%` : signed$1(v3);
|
|
59519
|
+
const yLabels = [yfmt(yHiN), yfmt((yHiN + yLoN) / 2), yfmt(yLoN)];
|
|
59136
59520
|
return /* @__PURE__ */ jsx$1(Box, { ref, width: "100%", height: `${hPx}px`, children: w2 > 0 && /* @__PURE__ */ jsx$1("svg", { width: w2, height: hPx, style: { display: "block" }, children: /* @__PURE__ */ jsxs(Group, { left: padL, top: padT, children: [
|
|
59137
59521
|
/* @__PURE__ */ jsx$1(Line, { from: { x: 0, y: 0 }, to: { x: 0, y: innerH }, stroke: t4.rule, strokeWidth: 1 }),
|
|
59138
59522
|
yTicks.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
@@ -59145,11 +59529,17 @@ function AreaRange({ lo: lo2, mid, hi, xTicks = [], yTicks = [], zero: zero2, to
|
|
|
59145
59529
|
/* @__PURE__ */ jsx$1(LinePath, { data: data4, x: (d2) => xScale(d2.i), y: (d2) => yScale(d2.mid), curve: monotoneX, stroke: col, strokeWidth: t4.lineWidth }),
|
|
59146
59530
|
marks.map((m2, i) => {
|
|
59147
59531
|
const mc = t4.tone(m2.tone);
|
|
59148
|
-
const dotY = yScale(mid[m2.at]
|
|
59532
|
+
const dotY = yScale(fin(mid[m2.at], 0));
|
|
59533
|
+
const labY = Math.max(9, dotY - 9);
|
|
59534
|
+
const atEnd = m2.at >= mid.length - 2;
|
|
59535
|
+
const atStart = m2.at <= 1;
|
|
59536
|
+
const align = atEnd ? "right" : atStart ? "left" : "center";
|
|
59537
|
+
const labW = m2.label.length * 6 + 6;
|
|
59538
|
+
const labX = atEnd ? -labW : atStart ? 0 : -labW / 2;
|
|
59149
59539
|
return /* @__PURE__ */ jsxs(Group, { left: xScale(m2.at), children: [
|
|
59150
59540
|
/* @__PURE__ */ jsx$1(Line, { from: { x: 0, y: -2 }, to: { x: 0, y: innerH }, stroke: mc, strokeWidth: 1, strokeDasharray: "2 3" }),
|
|
59151
59541
|
/* @__PURE__ */ jsx$1(Circle, { cx: 0, cy: dotY, r: t4.dotRadius, fill: mc, stroke: t4.surface, strokeWidth: 1.5 }),
|
|
59152
|
-
/* @__PURE__ */ jsx$1(
|
|
59542
|
+
/* @__PURE__ */ jsx$1(ChartLabel, { x: labX, y: labY - 11, w: labW, align, size: t4.labelSize, weight: 500, color: mc, family: t4.body, ...m2.help ? { help: m2.help } : {}, children: m2.label })
|
|
59153
59543
|
] }, `mk${i}`);
|
|
59154
59544
|
}),
|
|
59155
59545
|
/* @__PURE__ */ jsx$1(Line, { from: { x: 0, y: innerH }, to: { x: innerW, y: innerH }, stroke: t4.rule, strokeWidth: 1 }),
|
|
@@ -59159,17 +59549,54 @@ function AreaRange({ lo: lo2, mid, hi, xTicks = [], yTicks = [], zero: zero2, to
|
|
|
59159
59549
|
})
|
|
59160
59550
|
] }) }) });
|
|
59161
59551
|
}
|
|
59552
|
+
function OverlapHistogram({ treated, control, domain = [0, 1], supportLabel, positivityOk, height: height2 }) {
|
|
59553
|
+
const t4 = useChartTheme();
|
|
59554
|
+
const [ref, w2] = useMeasuredWidth();
|
|
59555
|
+
const hPx = height2 ?? 150;
|
|
59556
|
+
const padL = 8, padR = 8, padT = 16, padB = 26;
|
|
59557
|
+
const innerW = Math.max(0, w2 - padL - padR);
|
|
59558
|
+
const innerH = hPx - padT - padB;
|
|
59559
|
+
const centerY = innerH / 2;
|
|
59560
|
+
const halfH = centerY - 4;
|
|
59561
|
+
const bins = Math.max(treated.length, control.length);
|
|
59562
|
+
const x2 = createLinearScale({ domain, range: [0, innerW] });
|
|
59563
|
+
const span = domain[1] - domain[0];
|
|
59564
|
+
const binW = bins > 0 ? innerW / bins : innerW;
|
|
59565
|
+
const barW = Math.max(1, binW * 0.78);
|
|
59566
|
+
const maxVal = Math.max(1e-9, ...treated, ...control);
|
|
59567
|
+
const tCol = t4.tone("pos");
|
|
59568
|
+
const cCol = t4.tone("muted");
|
|
59569
|
+
const ticks2 = [domain[0], (domain[0] + domain[1]) / 2, domain[1]];
|
|
59570
|
+
const cx2 = (i) => x2(domain[0] + (i + 0.5) / bins * span);
|
|
59571
|
+
return /* @__PURE__ */ jsx$1(Box, { ref, width: "100%", height: `${hPx}px`, children: w2 > 0 && /* @__PURE__ */ jsx$1("svg", { width: w2, height: hPx, style: { display: "block" }, children: /* @__PURE__ */ jsxs(Group, { left: padL, top: padT, children: [
|
|
59572
|
+
/* @__PURE__ */ jsx$1("text", { x: 0, y: -4, style: svgText(9, t4.faint, t4.body), children: "treated ↑" }),
|
|
59573
|
+
supportLabel && /* @__PURE__ */ jsx$1("text", { x: innerW, y: -4, textAnchor: "end", style: svgText(t4.labelSize, positivityOk ? t4.tone("pos") : t4.tone("warn"), t4.mono, 600), children: supportLabel }),
|
|
59574
|
+
Array.from({ length: bins }, (_2, i) => {
|
|
59575
|
+
const ht = Number(treated[i] ?? 0) / maxVal * halfH;
|
|
59576
|
+
const hc = Number(control[i] ?? 0) / maxVal * halfH;
|
|
59577
|
+
const left = cx2(i) - barW / 2;
|
|
59578
|
+
return /* @__PURE__ */ jsxs(Group, { children: [
|
|
59579
|
+
ht > 0 && /* @__PURE__ */ jsx$1("rect", { x: left, y: centerY - ht, width: barW, height: ht, fill: tCol, fillOpacity: 0.85, rx: 0.5 }),
|
|
59580
|
+
hc > 0 && /* @__PURE__ */ jsx$1("rect", { x: left, y: centerY, width: barW, height: hc, fill: cCol, fillOpacity: 0.6, rx: 0.5 })
|
|
59581
|
+
] }, i);
|
|
59582
|
+
}),
|
|
59583
|
+
/* @__PURE__ */ jsx$1(Line, { from: { x: 0, y: centerY }, to: { x: innerW, y: centerY }, stroke: t4.ruleStrong, strokeWidth: 1 }),
|
|
59584
|
+
/* @__PURE__ */ jsx$1("text", { x: 0, y: innerH + 4, style: svgText(9, t4.faint, t4.body), children: "untreated ↓" }),
|
|
59585
|
+
ticks2.map((tk, i) => /* @__PURE__ */ jsx$1("text", { x: x2(tk), y: innerH + 16, textAnchor: i === 0 ? "start" : i === ticks2.length - 1 ? "end" : "middle", style: svgText(t4.labelSize, t4.muted, t4.mono), children: fmt$2(tk) }, `x${i}`))
|
|
59586
|
+
] }) }) });
|
|
59587
|
+
}
|
|
59162
59588
|
const arr = (v3) => v3 ? Array.from(v3, Number) : [];
|
|
59589
|
+
const arrI = (v3) => v3 ? Array.from(v3, Number) : [];
|
|
59163
59590
|
const getOpt = (o2) => o2 && o2.type === "some" ? o2.value : void 0;
|
|
59164
|
-
const fmt$1 = (x2) =>
|
|
59165
|
-
const
|
|
59166
|
-
|
|
59167
|
-
const mean2 = (xs) => xs.length ? xs.reduce((a2, b2) => a2 + b2, 0) / xs.length : 0;
|
|
59168
|
-
const std = (xs) => {
|
|
59169
|
-
if (xs.length < 2) return 0;
|
|
59170
|
-
const m2 = mean2(xs);
|
|
59171
|
-
return Math.sqrt(xs.reduce((a2, b2) => a2 + (b2 - m2) * (b2 - m2), 0) / xs.length);
|
|
59591
|
+
const fmt$1 = (x2) => {
|
|
59592
|
+
const r2 = Number.isInteger(x2) ? String(x2) : x2.toFixed(1);
|
|
59593
|
+
return r2 === "-0.0" ? "0.0" : r2;
|
|
59172
59594
|
};
|
|
59595
|
+
const signed = (x2) => {
|
|
59596
|
+
const f2 = fmt$1(x2);
|
|
59597
|
+
return f2 === "0" || f2 === "0.0" || f2.startsWith("-") ? f2 : `+${f2}`;
|
|
59598
|
+
};
|
|
59599
|
+
const clamp01 = (x2) => x2 < 0 ? 0 : x2 > 1 ? 1 : x2;
|
|
59173
59600
|
function band(absStd) {
|
|
59174
59601
|
if (absStd >= 0.66) return { level: "large gap", tone: "neg" };
|
|
59175
59602
|
if (absStd >= 0.33) return { level: "some", tone: "warn" };
|
|
@@ -59184,11 +59611,22 @@ const kindOf = (cols, name) => {
|
|
|
59184
59611
|
var _a2;
|
|
59185
59612
|
return (_a2 = cols.find((c2) => c2.name === name)) == null ? void 0 : _a2.kind;
|
|
59186
59613
|
};
|
|
59187
|
-
|
|
59188
|
-
|
|
59189
|
-
|
|
59190
|
-
|
|
59191
|
-
|
|
59614
|
+
const VERDICT_TONE = {
|
|
59615
|
+
causal: "pos",
|
|
59616
|
+
modest: "warn",
|
|
59617
|
+
adjustment_insufficient: "warn",
|
|
59618
|
+
non_identifiable_positivity: "neg",
|
|
59619
|
+
not_estimable: "muted"
|
|
59620
|
+
};
|
|
59621
|
+
const VERDICT_LABEL = {
|
|
59622
|
+
causal: "Causal effect",
|
|
59623
|
+
modest: "Modest / unclear",
|
|
59624
|
+
adjustment_insufficient: "Not trustworthy yet",
|
|
59625
|
+
non_identifiable_positivity: "No fair comparison",
|
|
59626
|
+
not_estimable: "Can’t be estimated"
|
|
59627
|
+
};
|
|
59628
|
+
function deriveVerdict(v3) {
|
|
59629
|
+
return { tag: v3.type, tone: VERDICT_TONE[v3.type], label: VERDICT_LABEL[v3.type] };
|
|
59192
59630
|
}
|
|
59193
59631
|
function treatmentKind(cols, treatment) {
|
|
59194
59632
|
switch (kindOf(cols, treatment)) {
|
|
@@ -59204,124 +59642,133 @@ function treatmentKind(cols, treatment) {
|
|
|
59204
59642
|
return "value";
|
|
59205
59643
|
}
|
|
59206
59644
|
}
|
|
59207
|
-
function deriveSpec(
|
|
59208
|
-
const tKind = kindOf(cols,
|
|
59209
|
-
const outUnit = unitOf(meta3,
|
|
59210
|
-
const balByCol =
|
|
59211
|
-
const
|
|
59645
|
+
function deriveSpec(config2, cols, result, meta3, dataLen) {
|
|
59646
|
+
const tKind = kindOf(cols, config2.treatment);
|
|
59647
|
+
const outUnit = unitOf(meta3, config2.outcome);
|
|
59648
|
+
const balByCol = /* @__PURE__ */ new Map();
|
|
59649
|
+
for (const b2 of (result == null ? void 0 : result.balance) ?? []) {
|
|
59650
|
+
const prev2 = balByCol.get(b2.base_column);
|
|
59651
|
+
if (!prev2 || Math.abs(b2.std_diff) > Math.abs(prev2.std_diff)) balByCol.set(b2.base_column, b2);
|
|
59652
|
+
}
|
|
59653
|
+
const confounders = config2.common_causes.map((col) => {
|
|
59212
59654
|
const b2 = balByCol.get(col);
|
|
59213
|
-
const absStd = b2 ? Math.abs(b2.
|
|
59655
|
+
const absStd = b2 ? Math.abs(b2.std_diff) : 0;
|
|
59214
59656
|
const { level, tone } = band(absStd);
|
|
59215
|
-
const dir = b2 ? b2.
|
|
59216
|
-
const reason = b2 ? `treated
|
|
59657
|
+
const dir = b2 ? b2.treated_mean < b2.control_mean ? "lower" : "higher" : "";
|
|
59658
|
+
const reason = b2 ? `the treated group ran ${dir} on ${col}` : "measured once you Run";
|
|
59217
59659
|
return { col, reason, imbalance: clamp01(absStd), level, tone };
|
|
59218
59660
|
});
|
|
59219
|
-
const inPlay = /* @__PURE__ */ new Set([
|
|
59661
|
+
const inPlay = /* @__PURE__ */ new Set([config2.treatment, config2.outcome, ...config2.common_causes]);
|
|
59220
59662
|
const suggest = cols.find((c2) => !inPlay.has(c2.name));
|
|
59221
59663
|
const suggestion = suggest ? `add ${suggest.name}` : "";
|
|
59222
|
-
const methodV = getOpt(
|
|
59664
|
+
const methodV = getOpt(config2.method);
|
|
59223
59665
|
const method = (methodV == null ? void 0 : methodV.type) === "propensity_score_weighting" ? "reweighting" : "regression";
|
|
59224
|
-
const targetV = getOpt(
|
|
59666
|
+
const targetV = getOpt(config2.estimand);
|
|
59225
59667
|
const target = targetV && (targetV.type === "att" || targetV.type === "atc") ? "treated" : "all";
|
|
59226
|
-
const trim2 = getOpt(spec.trim) !== void 0;
|
|
59227
59668
|
return {
|
|
59228
|
-
treatment:
|
|
59229
|
-
treatmentKind: treatmentKind(cols,
|
|
59230
|
-
outcome:
|
|
59669
|
+
treatment: config2.treatment,
|
|
59670
|
+
treatmentKind: treatmentKind(cols, config2.treatment),
|
|
59671
|
+
outcome: config2.outcome,
|
|
59231
59672
|
outcomeKind: outUnit ? `number · ${outUnit}` : "number",
|
|
59232
59673
|
comparison: tKind === "boolean" ? "yes · vs no" : "high · vs low",
|
|
59233
59674
|
confounders,
|
|
59234
59675
|
suggestion,
|
|
59235
59676
|
method,
|
|
59236
59677
|
target,
|
|
59237
|
-
|
|
59238
|
-
dataLabel: `${result ? Number(result.nTotal) : dataLen} rows`
|
|
59678
|
+
dataLabel: `${result ? Number(result.n_total) : dataLen} rows`
|
|
59239
59679
|
};
|
|
59240
59680
|
}
|
|
59241
59681
|
function balanceDisplay(b2, categorical) {
|
|
59242
|
-
const isProp = categorical.has(b2.
|
|
59243
|
-
return isProp ? `${Math.round(b2.
|
|
59244
|
-
}
|
|
59245
|
-
function deriveAnswer(
|
|
59246
|
-
const ci = getOpt(
|
|
59247
|
-
const nci = getOpt(result.
|
|
59248
|
-
const categorical = new Set(
|
|
59249
|
-
const balance = [...result.balance].sort((a2, b2) => Math.abs(b2.
|
|
59682
|
+
const isProp = categorical.has(b2.base_column) && b2.treated_mean >= 0 && b2.treated_mean <= 1 && b2.control_mean >= 0 && b2.control_mean <= 1;
|
|
59683
|
+
return isProp ? `${Math.round(b2.treated_mean * 100)}% vs ${Math.round(b2.control_mean * 100)}%` : `${b2.treated_mean.toFixed(1)} vs ${b2.control_mean.toFixed(1)}`;
|
|
59684
|
+
}
|
|
59685
|
+
function deriveAnswer(config2, result, adj, meta3) {
|
|
59686
|
+
const ci = getOpt(adj.ci);
|
|
59687
|
+
const nci = getOpt(result.naive_ci);
|
|
59688
|
+
const categorical = new Set(getOpt(config2.categorical) ?? []);
|
|
59689
|
+
const balance = [...result.balance].sort((a2, b2) => Math.abs(b2.std_diff) - Math.abs(a2.std_diff)).map((b2) => ({
|
|
59250
59690
|
col: b2.column,
|
|
59251
|
-
treated: b2.
|
|
59252
|
-
control: b2.
|
|
59691
|
+
treated: b2.treated_mean,
|
|
59692
|
+
control: b2.control_mean,
|
|
59253
59693
|
display: balanceDisplay(b2, categorical),
|
|
59254
|
-
frac: clamp01(Math.abs(b2.
|
|
59255
|
-
tone: band(Math.abs(b2.
|
|
59694
|
+
frac: clamp01(Math.abs(b2.std_diff)),
|
|
59695
|
+
tone: band(Math.abs(b2.std_diff)).tone
|
|
59256
59696
|
}));
|
|
59257
|
-
const
|
|
59258
|
-
const
|
|
59697
|
+
const lo2 = (ci == null ? void 0 : ci.lower) ?? adj.effect;
|
|
59698
|
+
const hi = (ci == null ? void 0 : ci.upper) ?? adj.effect;
|
|
59699
|
+
const clear = result.verdict.type === "causal" || (ci ? lo2 > 0 || hi < 0 : false);
|
|
59700
|
+
const flip = Math.sign(result.naive) !== Math.sign(adj.effect) && result.naive !== 0;
|
|
59701
|
+
const nDropped = Number(result.n_dropped);
|
|
59702
|
+
const nTotal = Number(result.n_total);
|
|
59259
59703
|
return {
|
|
59260
|
-
treatment:
|
|
59261
|
-
outcome:
|
|
59262
|
-
unit: unitOf(meta3,
|
|
59704
|
+
treatment: config2.treatment,
|
|
59705
|
+
outcome: config2.outcome,
|
|
59706
|
+
unit: unitOf(meta3, config2.outcome),
|
|
59263
59707
|
naive: result.naive,
|
|
59264
59708
|
naiveLo: (nci == null ? void 0 : nci.lower) ?? result.naive,
|
|
59265
59709
|
naiveHi: (nci == null ? void 0 : nci.upper) ?? result.naive,
|
|
59266
|
-
effect:
|
|
59267
|
-
lo:
|
|
59268
|
-
hi
|
|
59269
|
-
|
|
59270
|
-
|
|
59271
|
-
|
|
59710
|
+
effect: adj.effect,
|
|
59711
|
+
lo: lo2,
|
|
59712
|
+
hi,
|
|
59713
|
+
clear,
|
|
59714
|
+
flip,
|
|
59715
|
+
cautious: result.verdict.type === "adjustment_insufficient",
|
|
59716
|
+
nTotal: result.n_total,
|
|
59717
|
+
nTreated: result.n_treated,
|
|
59718
|
+
nControl: result.n_control,
|
|
59272
59719
|
nCompared: BigInt(Math.max(0, nTotal - nDropped)),
|
|
59273
|
-
nDropped: result.
|
|
59720
|
+
nDropped: result.n_dropped,
|
|
59274
59721
|
balance
|
|
59275
59722
|
};
|
|
59276
59723
|
}
|
|
59277
|
-
function
|
|
59278
|
-
const
|
|
59279
|
-
|
|
59280
|
-
|
|
59281
|
-
|
|
59282
|
-
|
|
59283
|
-
|
|
59284
|
-
|
|
59285
|
-
|
|
59286
|
-
|
|
59287
|
-
value:
|
|
59288
|
-
|
|
59289
|
-
|
|
59290
|
-
|
|
59291
|
-
}
|
|
59292
|
-
case "data_subset": {
|
|
59293
|
-
const passed = Math.abs(m2 - est) < Math.max(0.5, Math.abs(est) * 0.2);
|
|
59294
|
-
return {
|
|
59295
|
-
name: "Drop-some test",
|
|
59296
|
-
desc: "Re-estimate on random subsamples; a trustworthy effect stays put.",
|
|
59297
|
-
value: `${fmt$1(m2)} ± ${fmt$1(std(effects))}`,
|
|
59298
|
-
passed,
|
|
59299
|
-
tip: { type: "none", value: null }
|
|
59300
|
-
};
|
|
59301
|
-
}
|
|
59302
|
-
case "random_common_cause": {
|
|
59303
|
-
const passed = Math.abs(m2 - est) < Math.max(0.5, Math.abs(est) * 0.2);
|
|
59304
|
-
return {
|
|
59305
|
-
name: "Decoy cause",
|
|
59306
|
-
desc: "Add an irrelevant random factor; the answer should not move.",
|
|
59307
|
-
value: `${fmt$1(est)} → ${fmt$1(m2)}`,
|
|
59308
|
-
passed,
|
|
59309
|
-
tip: { type: "none", value: null }
|
|
59310
|
-
};
|
|
59311
|
-
}
|
|
59312
|
-
default: {
|
|
59313
|
-
const strengths = arr(getOpt(c2.strengths));
|
|
59314
|
-
const tip = zeroCrossing(strengths, effects);
|
|
59315
|
-
const passed = tip === null;
|
|
59316
|
-
return {
|
|
59317
|
-
name: "Hidden cause",
|
|
59318
|
-
desc: "How strong an unrecorded common cause would need to be to overturn the result.",
|
|
59319
|
-
value: tip === null ? "holds throughout" : `tips at ${fmt$1(tip)}`,
|
|
59320
|
-
passed,
|
|
59321
|
-
tip: { type: "some", value: "Something unrecorded could drive both the treatment choice and the outcome." }
|
|
59322
|
-
};
|
|
59323
|
-
}
|
|
59724
|
+
function deriveRefusal(config2, result) {
|
|
59725
|
+
const nT = Number(result.n_treated), nC = Number(result.n_control), nTot = Number(result.n_total);
|
|
59726
|
+
if (result.verdict.type === "not_estimable") {
|
|
59727
|
+
const reason = result.verdict.value || "The treatment barely varies, so no comparison can be formed.";
|
|
59728
|
+
return {
|
|
59729
|
+
kind: "not_estimable",
|
|
59730
|
+
title: `Can’t estimate the effect of ${config2.treatment}`,
|
|
59731
|
+
body: reason,
|
|
59732
|
+
evidence: [
|
|
59733
|
+
{ label: "treated", value: String(nT) },
|
|
59734
|
+
{ label: "untreated", value: String(nC) },
|
|
59735
|
+
{ label: "rows", value: String(nTot) }
|
|
59736
|
+
]
|
|
59737
|
+
};
|
|
59324
59738
|
}
|
|
59739
|
+
if (result.verdict.type === "non_identifiable_positivity") {
|
|
59740
|
+
const pct = Math.round(result.overlap.common_support_frac * 100);
|
|
59741
|
+
return {
|
|
59742
|
+
kind: "positivity",
|
|
59743
|
+
title: "No like-for-like comparison exists",
|
|
59744
|
+
body: `The treated and untreated groups barely overlap on the confounders — only ${pct}% sit in a range where both occur — so there is no fair comparison to adjust toward.`,
|
|
59745
|
+
evidence: [
|
|
59746
|
+
{ label: "common support", value: `${pct}%` },
|
|
59747
|
+
{ label: "treated", value: String(nT) },
|
|
59748
|
+
{ label: "untreated", value: String(nC) }
|
|
59749
|
+
]
|
|
59750
|
+
};
|
|
59751
|
+
}
|
|
59752
|
+
return {
|
|
59753
|
+
kind: "not_estimable",
|
|
59754
|
+
title: `Can’t estimate the effect of ${config2.treatment}`,
|
|
59755
|
+
body: "The engine could not produce a like-for-like estimate for this configuration.",
|
|
59756
|
+
evidence: [
|
|
59757
|
+
{ label: "treated", value: String(nT) },
|
|
59758
|
+
{ label: "untreated", value: String(nC) },
|
|
59759
|
+
{ label: "rows", value: String(nTot) }
|
|
59760
|
+
]
|
|
59761
|
+
};
|
|
59762
|
+
}
|
|
59763
|
+
function deriveOverlap(o2) {
|
|
59764
|
+
const pct = Math.round(o2.common_support_frac * 100);
|
|
59765
|
+
return {
|
|
59766
|
+
treated: arr(o2.treated_propensity),
|
|
59767
|
+
control: arr(o2.control_propensity),
|
|
59768
|
+
commonSupportFrac: o2.common_support_frac,
|
|
59769
|
+
positivityOk: o2.positivity_ok,
|
|
59770
|
+
supportLabel: `${pct}% common support`
|
|
59771
|
+
};
|
|
59325
59772
|
}
|
|
59326
59773
|
function zeroCrossing(xs, ys) {
|
|
59327
59774
|
for (let i = 1; i < ys.length && i < xs.length; i++) {
|
|
@@ -59333,27 +59780,80 @@ function zeroCrossing(xs, ys) {
|
|
|
59333
59780
|
}
|
|
59334
59781
|
return null;
|
|
59335
59782
|
}
|
|
59336
|
-
function deriveRefute(
|
|
59337
|
-
const checks =
|
|
59338
|
-
const
|
|
59339
|
-
const
|
|
59340
|
-
const
|
|
59341
|
-
|
|
59342
|
-
|
|
59343
|
-
|
|
59344
|
-
|
|
59345
|
-
|
|
59346
|
-
|
|
59347
|
-
|
|
59348
|
-
|
|
59349
|
-
}
|
|
59350
|
-
|
|
59351
|
-
|
|
59783
|
+
function deriveRefute(r2, adj) {
|
|
59784
|
+
const checks = [];
|
|
59785
|
+
const est = (adj == null ? void 0 : adj.effect) ?? 0;
|
|
59786
|
+
const placeboEffect = getOpt(r2.placebo_effect);
|
|
59787
|
+
const placeboPasses = getOpt(r2.placebo_passes);
|
|
59788
|
+
if (placeboEffect !== void 0 || placeboPasses !== void 0) {
|
|
59789
|
+
checks.push({
|
|
59790
|
+
name: "Shuffle test",
|
|
59791
|
+
desc: "Shuffle which rows were treated; a real effect should collapse to zero.",
|
|
59792
|
+
value: placeboEffect !== void 0 ? `→ ${signed(placeboEffect)}` : placeboPasses ? "passed" : "failed",
|
|
59793
|
+
passed: placeboPasses ?? (placeboEffect !== void 0 && Math.abs(placeboEffect) < Math.max(0.5, Math.abs(est) * 0.15)),
|
|
59794
|
+
tip: { type: "some", value: "Randomly re-label which rows were treated. A genuine effect should vanish." },
|
|
59795
|
+
help: "check_shuffle"
|
|
59796
|
+
});
|
|
59797
|
+
}
|
|
59798
|
+
const ds = getOpt(r2.data_subset_effect);
|
|
59799
|
+
if (ds !== void 0) {
|
|
59800
|
+
const dstd = getOpt(r2.data_subset_std) ?? 0;
|
|
59801
|
+
checks.push({
|
|
59802
|
+
name: "Drop-some test",
|
|
59803
|
+
desc: "Re-estimate on random subsamples; a trustworthy effect stays put.",
|
|
59804
|
+
value: `${fmt$1(ds)} ± ${fmt$1(dstd)}`,
|
|
59805
|
+
passed: Math.abs(ds - est) < Math.max(0.5, Math.abs(est) * 0.2),
|
|
59806
|
+
tip: { type: "none", value: null },
|
|
59807
|
+
help: "check_dropsome"
|
|
59808
|
+
});
|
|
59809
|
+
}
|
|
59810
|
+
const rcc = getOpt(r2.random_cc_within_ci);
|
|
59811
|
+
if (rcc !== void 0) {
|
|
59812
|
+
checks.push({
|
|
59813
|
+
name: "Decoy cause",
|
|
59814
|
+
desc: "Add an irrelevant random factor; the answer should not move.",
|
|
59815
|
+
value: rcc ? "within range" : "moved",
|
|
59816
|
+
passed: rcc,
|
|
59817
|
+
tip: { type: "none", value: null },
|
|
59818
|
+
help: "check_decoy"
|
|
59819
|
+
});
|
|
59820
|
+
}
|
|
59821
|
+
const sens = getOpt(r2.sensitivity);
|
|
59822
|
+
const evalue = getOpt(r2.robustness_value);
|
|
59823
|
+
if (sens !== void 0 || evalue !== void 0) {
|
|
59824
|
+
const strengths = sens ? arr(sens.strengths) : [];
|
|
59825
|
+
const effects = sens ? arr(sens.effects) : [];
|
|
59826
|
+
const tip = sens ? zeroCrossing(strengths, effects) : null;
|
|
59827
|
+
const value = tip !== null ? `tips at ${fmt$1(tip)}` : evalue !== void 0 ? `E-value ${fmt$1(evalue)}` : "holds throughout";
|
|
59828
|
+
checks.push({
|
|
59829
|
+
name: "Hidden cause",
|
|
59830
|
+
desc: "How strong an unrecorded common cause would need to be to overturn the result.",
|
|
59831
|
+
value,
|
|
59832
|
+
passed: tip === null,
|
|
59833
|
+
tip: { type: "some", value: "Something unrecorded could drive both the treatment choice and the outcome." },
|
|
59834
|
+
help: "check_hidden"
|
|
59835
|
+
});
|
|
59836
|
+
}
|
|
59837
|
+
let sensVM = null;
|
|
59838
|
+
if (sens !== void 0) {
|
|
59839
|
+
const mid = arr(sens.effects);
|
|
59840
|
+
if (mid.length) {
|
|
59841
|
+
const ci = getOpt(adj == null ? void 0 : adj.ci);
|
|
59842
|
+
const half = ci ? Math.abs(ci.upper - ci.lower) / 2 : 0;
|
|
59843
|
+
const lo2 = mid.map((m2) => m2 - half);
|
|
59844
|
+
const hi = mid.map((m2) => m2 + half);
|
|
59845
|
+
const yLo = Math.min(0, ...lo2), yHi = Math.max(...hi, 0);
|
|
59846
|
+
sensVM = { lo: lo2, mid, hi, xTicks: ["none", "weaker", "stronger"], yTicks: [fmt$1(yLo), fmt$1((yLo + yHi) / 2), fmt$1(yHi)] };
|
|
59847
|
+
}
|
|
59848
|
+
}
|
|
59849
|
+
return { checks, sens: sensVM };
|
|
59850
|
+
}
|
|
59851
|
+
function deriveDose(dose, config2, meta3) {
|
|
59352
59852
|
const grid = arr(dose.grid);
|
|
59353
59853
|
const mid = arr(dose.effect);
|
|
59354
59854
|
const lo2 = getOpt(dose.lower) ? arr(getOpt(dose.lower)) : mid.slice();
|
|
59355
59855
|
const hi = getOpt(dose.upper) ? arr(getOpt(dose.upper)) : mid.slice();
|
|
59356
|
-
const sizes =
|
|
59856
|
+
const sizes = arrI(dose.size);
|
|
59357
59857
|
const featureUnit = unitOf(meta3, dose.feature);
|
|
59358
59858
|
const here = sizes.length ? sizes.indexOf(Math.max(...sizes)) : 0;
|
|
59359
59859
|
const marg = mid.map((m2, i) => i === 0 ? 0 : m2 - mid[i - 1]);
|
|
@@ -59373,31 +59873,28 @@ function deriveDose(dose, spec, meta3) {
|
|
|
59373
59873
|
let lastStep = margSteps.length - 1;
|
|
59374
59874
|
while (lastStep > 0 && Math.abs(margSteps[lastStep]) < 0.12 * maxMarg) lastStep--;
|
|
59375
59875
|
const marginal = margSteps.slice(0, lastStep + 1).map((v3, i) => ({ label: `${fmt$1(grid[i + 1])}`, value: v3, frac: clamp01(Math.abs(v3) / maxMarg) }));
|
|
59376
|
-
const segLabels = ((_a2 = getOpt(dose.segments)) == null ? void 0 : _a2.map((s2) => s2.label)) ?? [];
|
|
59377
|
-
const segments = segLabels.length ? ["all", ...segLabels] : ["all"];
|
|
59378
59876
|
const stepFrom = here < sweet ? here : Math.max(0, sweet - 1);
|
|
59379
59877
|
const gainToSweet = (mid[sweet] ?? 0) - (mid[stepFrom] ?? 0);
|
|
59380
59878
|
const nextGain = sweet + 1 < mid.length ? mid[sweet + 1] - mid[sweet] : 0;
|
|
59381
59879
|
const tradeoff = `Going from ${fmt$1(grid[stepFrom] ?? 0)} to ${fmt$1(grid[sweet] ?? 0)} adds ${signed(gainToSweet)}; the next step adds only ${signed(nextGain)}.`;
|
|
59880
|
+
const marks = [];
|
|
59881
|
+
if (sizes.length) marks.push({ at: here, label: "you are here", tone: "muted", help: "dose_here" });
|
|
59882
|
+
marks.push({ at: sweet, label: "sweet spot", tone: "pos", help: "dose_sweet" });
|
|
59382
59883
|
return {
|
|
59383
59884
|
feature: dose.feature,
|
|
59384
|
-
outcome:
|
|
59885
|
+
outcome: config2.outcome,
|
|
59385
59886
|
lo: lo2,
|
|
59386
59887
|
mid,
|
|
59387
59888
|
hi,
|
|
59388
59889
|
xTicks,
|
|
59389
59890
|
yTicks,
|
|
59390
|
-
marks
|
|
59391
|
-
{ at: BigInt(here), label: "you are here", tone: "muted" },
|
|
59392
|
-
{ at: BigInt(sweet), label: "sweet spot", tone: "pos" }
|
|
59393
|
-
],
|
|
59891
|
+
marks,
|
|
59394
59892
|
recoLabel: `≈ ${fmt$1(grid[sweet] ?? 0)}${featureUnit ? " " + featureUnit : ""}`,
|
|
59395
59893
|
recoEffect: mid[sweet] ?? 0,
|
|
59396
59894
|
recoLo: lo2[sweet] ?? 0,
|
|
59397
59895
|
recoHi: hi[sweet] ?? 0,
|
|
59398
59896
|
tradeoff,
|
|
59399
|
-
marginal
|
|
59400
|
-
segments
|
|
59897
|
+
marginal
|
|
59401
59898
|
};
|
|
59402
59899
|
}
|
|
59403
59900
|
function relTime(d2, now2) {
|
|
@@ -59407,30 +59904,126 @@ function relTime(d2, now2) {
|
|
|
59407
59904
|
if (days < 7) return d2.toLocaleDateString(void 0, { weekday: "short" });
|
|
59408
59905
|
return "last wk";
|
|
59409
59906
|
}
|
|
59907
|
+
const VERDICT_WORD = {
|
|
59908
|
+
causal: "causal",
|
|
59909
|
+
modest: "modest",
|
|
59910
|
+
adjustment_insufficient: "not robust",
|
|
59911
|
+
non_identifiable_positivity: "no overlap",
|
|
59912
|
+
not_estimable: "n/a"
|
|
59913
|
+
};
|
|
59410
59914
|
function deriveJournalRow(row, now2) {
|
|
59411
|
-
const
|
|
59412
|
-
const clear = ci ? ci.lower > 0 || ci.upper < 0 : true;
|
|
59413
|
-
const dirUp = row.effect > 0;
|
|
59915
|
+
const adj = getOpt(row.adjusted);
|
|
59414
59916
|
return {
|
|
59415
|
-
treatment: row.
|
|
59416
|
-
outcome: row.
|
|
59417
|
-
confounders: row.
|
|
59418
|
-
effect: signed(row.
|
|
59419
|
-
verdict:
|
|
59420
|
-
verdictTone:
|
|
59421
|
-
who: row.
|
|
59422
|
-
when: relTime(row.
|
|
59423
|
-
};
|
|
59424
|
-
}
|
|
59425
|
-
function deriveView(
|
|
59917
|
+
treatment: row.config.treatment,
|
|
59918
|
+
outcome: row.config.outcome,
|
|
59919
|
+
confounders: row.config.common_causes.join(", "),
|
|
59920
|
+
effect: adj !== void 0 ? signed(adj) : signed(row.naive),
|
|
59921
|
+
verdict: VERDICT_WORD[row.verdict.type],
|
|
59922
|
+
verdictTone: VERDICT_TONE[row.verdict.type],
|
|
59923
|
+
who: row.committed_by,
|
|
59924
|
+
when: relTime(row.committed_at, now2)
|
|
59925
|
+
};
|
|
59926
|
+
}
|
|
59927
|
+
function deriveView(config2, ranConfig, cols, result, journal, meta3, dataLen, now2) {
|
|
59928
|
+
const adj = result ? getOpt(result.adjusted) : void 0;
|
|
59929
|
+
const refutation = result ? getOpt(result.refutation) : void 0;
|
|
59930
|
+
const doseR = result ? getOpt(result.dose_response) : void 0;
|
|
59426
59931
|
return {
|
|
59427
|
-
|
|
59428
|
-
|
|
59429
|
-
|
|
59430
|
-
|
|
59932
|
+
// The set-up rail reflects the LIVE config (the editor); the result deck
|
|
59933
|
+
// reflects RANCONFIG — the config that produced `result` — so the result
|
|
59934
|
+
// strings never drift ahead of the numbers on a live picker edit.
|
|
59935
|
+
spec: deriveSpec(config2, cols, result, meta3, dataLen),
|
|
59936
|
+
verdict: result ? deriveVerdict(result.verdict) : null,
|
|
59937
|
+
answer: result && adj ? deriveAnswer(ranConfig, result, adj, meta3) : null,
|
|
59938
|
+
refusal: result && !adj ? deriveRefusal(ranConfig, result) : null,
|
|
59939
|
+
overlap: result ? deriveOverlap(result.overlap) : null,
|
|
59940
|
+
refute: refutation ? deriveRefute(refutation, adj) : null,
|
|
59941
|
+
dose: doseR && doseR.effect.length ? deriveDose(doseR, ranConfig, meta3) : null,
|
|
59431
59942
|
journal: journal ? journal.map((r2) => deriveJournalRow(r2, now2)) : null
|
|
59432
59943
|
};
|
|
59433
59944
|
}
|
|
59945
|
+
const DESIGN_HEADLINE = {
|
|
59946
|
+
detect_observed: "Confirm it with a controlled trial",
|
|
59947
|
+
de_bias: "Make it trustworthy with a controlled trial",
|
|
59948
|
+
resolve_vs_null: "Settle it with a bigger trial",
|
|
59949
|
+
restrict_to_overlap: "You can’t compare yet",
|
|
59950
|
+
create_control: "Hold some back next time"
|
|
59951
|
+
};
|
|
59952
|
+
function deriveDesignOption(o2) {
|
|
59953
|
+
return {
|
|
59954
|
+
label: o2.label,
|
|
59955
|
+
nTotal: Number(o2.n_total),
|
|
59956
|
+
nTreated: Number(o2.n_treated),
|
|
59957
|
+
nControl: Number(o2.n_control),
|
|
59958
|
+
treatedShare: o2.treated_share
|
|
59959
|
+
};
|
|
59960
|
+
}
|
|
59961
|
+
function deriveDesign(d2, result, ranConfig, meta3) {
|
|
59962
|
+
const basis = d2.basis.type;
|
|
59963
|
+
const outcome = ranConfig.outcome;
|
|
59964
|
+
const unit2 = unitOf(meta3, outcome);
|
|
59965
|
+
const options2 = d2.options.map(deriveDesignOption);
|
|
59966
|
+
const primary = options2[0] ?? { label: "Even split", nTotal: 0, nTreated: 0, nControl: 0, treatedShare: 0.5 };
|
|
59967
|
+
const balByCol = /* @__PURE__ */ new Map();
|
|
59968
|
+
for (const b2 of (result == null ? void 0 : result.balance) ?? []) {
|
|
59969
|
+
const prev2 = balByCol.get(b2.base_column);
|
|
59970
|
+
if (!prev2 || Math.abs(b2.std_diff) > Math.abs(prev2.std_diff)) balByCol.set(b2.base_column, b2);
|
|
59971
|
+
}
|
|
59972
|
+
const matchOn = d2.match_on.map((col) => {
|
|
59973
|
+
var _a2;
|
|
59974
|
+
const b2 = balByCol.get(col);
|
|
59975
|
+
const absStd = b2 ? Math.abs(b2.std_diff) : 0;
|
|
59976
|
+
const { tone } = band(absStd);
|
|
59977
|
+
return { col, frac: clamp01(absStd), tone, display: getOpt((_a2 = colMeta(meta3, col)) == null ? void 0 : _a2.label) ?? col };
|
|
59978
|
+
});
|
|
59979
|
+
const ns = arrI(d2.power_curve.n);
|
|
59980
|
+
const powers = arr(d2.power_curve.power);
|
|
59981
|
+
const len = Math.min(ns.length, powers.length);
|
|
59982
|
+
const nearest = (target) => {
|
|
59983
|
+
let bi = 0, bd = Infinity;
|
|
59984
|
+
for (let i = 0; i < len; i++) {
|
|
59985
|
+
const dd = Math.abs(ns[i] - target);
|
|
59986
|
+
if (dd < bd) {
|
|
59987
|
+
bd = dd;
|
|
59988
|
+
bi = i;
|
|
59989
|
+
}
|
|
59990
|
+
}
|
|
59991
|
+
return bi;
|
|
59992
|
+
};
|
|
59993
|
+
const xTicks = len ? [String(ns[0]), String(ns[Math.floor((len - 1) / 2)]), String(ns[len - 1])] : [];
|
|
59994
|
+
const marks = [];
|
|
59995
|
+
if (len) marks.push({ at: nearest(primary.nTotal), label: `target ${(d2.target_power * 100).toFixed(0)}%`, tone: "pos", help: "validate_power" });
|
|
59996
|
+
const cp = getOpt(d2.current_power);
|
|
59997
|
+
if (cp !== void 0 && len) {
|
|
59998
|
+
const curN = result ? Number(result.n_total) : 0;
|
|
59999
|
+
if (curN > 0) {
|
|
60000
|
+
const offScale = curN >= (ns[len - 1] ?? 0);
|
|
60001
|
+
marks.push({
|
|
60002
|
+
at: offScale ? len - 1 : nearest(curN),
|
|
60003
|
+
label: offScale ? `you’re here · ${(cp * 100).toFixed(0)}% (off-scale)` : `you’re here ${(cp * 100).toFixed(0)}%`,
|
|
60004
|
+
tone: "muted",
|
|
60005
|
+
help: "validate_power"
|
|
60006
|
+
});
|
|
60007
|
+
}
|
|
60008
|
+
}
|
|
60009
|
+
const faint = d2.outcome_sd > 0 && Math.abs(d2.target_effect) / d2.outcome_sd <= 0.011;
|
|
60010
|
+
return {
|
|
60011
|
+
headline: DESIGN_HEADLINE[basis] ?? "Validate this",
|
|
60012
|
+
rationale: d2.rationale,
|
|
60013
|
+
primary,
|
|
60014
|
+
alternates: options2.slice(1),
|
|
60015
|
+
matchOn,
|
|
60016
|
+
curve: { mid: powers.slice(0, len).map((p2) => p2 * 100), xTicks, marks },
|
|
60017
|
+
targetLabel: `${signed(d2.target_effect)}${unit2 ? ` ${unit2}` : ""}`,
|
|
60018
|
+
targetPctLabel: `${(d2.target_power * 100).toFixed(0)}%`,
|
|
60019
|
+
holdback: basis === "create_control",
|
|
60020
|
+
showOverlap: basis === "restrict_to_overlap",
|
|
60021
|
+
faint
|
|
60022
|
+
};
|
|
60023
|
+
}
|
|
60024
|
+
const SUBJECT_ONE = "record";
|
|
60025
|
+
const SUBJECT_MANY = "records";
|
|
60026
|
+
const AUTORUN_MAX_ROWS = 5e4;
|
|
59434
60027
|
const TONE_TOKEN = {
|
|
59435
60028
|
neg: "fg.danger",
|
|
59436
60029
|
pos: "fg.success",
|
|
@@ -59468,13 +60061,73 @@ function useColumns(workspace, source) {
|
|
|
59468
60061
|
};
|
|
59469
60062
|
}, [types == null ? void 0 : types.sourceType]);
|
|
59470
60063
|
}
|
|
59471
|
-
|
|
59472
|
-
|
|
59473
|
-
|
|
60064
|
+
function rowMatchesAll(row, preds) {
|
|
60065
|
+
return preds.every((p2) => {
|
|
60066
|
+
const { fieldId, op } = p2.value;
|
|
60067
|
+
const v3 = row[fieldId];
|
|
60068
|
+
const ov = op.value;
|
|
60069
|
+
switch (op.type) {
|
|
60070
|
+
case "eq":
|
|
60071
|
+
case "is":
|
|
60072
|
+
return v3 === ov;
|
|
60073
|
+
case "neq":
|
|
60074
|
+
return v3 !== ov;
|
|
60075
|
+
case "lt":
|
|
60076
|
+
return num(v3) != null && num(ov) != null ? num(v3) < num(ov) : true;
|
|
60077
|
+
case "lte":
|
|
60078
|
+
return num(v3) != null && num(ov) != null ? num(v3) <= num(ov) : true;
|
|
60079
|
+
case "gt":
|
|
60080
|
+
return num(v3) != null && num(ov) != null ? num(v3) > num(ov) : true;
|
|
60081
|
+
case "gte":
|
|
60082
|
+
return num(v3) != null && num(ov) != null ? num(v3) >= num(ov) : true;
|
|
60083
|
+
case "in":
|
|
60084
|
+
return ov instanceof Set ? ov.has(v3) : true;
|
|
60085
|
+
case "notIn":
|
|
60086
|
+
return ov instanceof Set ? !ov.has(v3) : true;
|
|
60087
|
+
default:
|
|
60088
|
+
return true;
|
|
60089
|
+
}
|
|
60090
|
+
});
|
|
60091
|
+
}
|
|
60092
|
+
const num = (x2) => typeof x2 === "number" ? x2 : typeof x2 === "bigint" ? Number(x2) : null;
|
|
60093
|
+
function Cap({ children: children2, help }) {
|
|
60094
|
+
return /* @__PURE__ */ jsx$1(Text, { textStyle: "caption.eyebrow", fontSize: "9px", mb: "2.5", children: help ? /* @__PURE__ */ jsx$1(Help, { id: help, children: children2 }) : children2 });
|
|
59474
60095
|
}
|
|
59475
60096
|
function Card({ children: children2, mt = "3" }) {
|
|
59476
60097
|
return /* @__PURE__ */ jsx$1(Box, { layerStyle: "card", p: "3.5", borderRadius: "lg", mt, children: children2 });
|
|
59477
60098
|
}
|
|
60099
|
+
function RunError({ error: error3 }) {
|
|
60100
|
+
const tail = (error3.stderr || error3.stdout || "").trim().split("\n").slice(-12).join("\n");
|
|
60101
|
+
return /* @__PURE__ */ jsxs(Box, { layerStyle: "banner.stale", display: "flex", flexDirection: "column", gap: "2", mt: "3", children: [
|
|
60102
|
+
/* @__PURE__ */ jsxs(Box, { display: "inline-flex", alignItems: "flex-start", gap: "2", color: "fg.danger", children: [
|
|
60103
|
+
/* @__PURE__ */ jsx$1(Box, { as: "span", mt: "0.5", fontSize: "12px", flexShrink: "0", children: /* @__PURE__ */ jsx$1(FontAwesomeIcon, { icon: faTriangleExclamation }) }),
|
|
60104
|
+
/* @__PURE__ */ jsxs(Text, { textStyle: "body.sm", color: "fg.default", children: [
|
|
60105
|
+
/* @__PURE__ */ jsx$1(Text, { as: "span", fontWeight: "bold", children: "Could not run the experiment." }),
|
|
60106
|
+
" ",
|
|
60107
|
+
error3.message
|
|
60108
|
+
] })
|
|
60109
|
+
] }),
|
|
60110
|
+
tail && /* @__PURE__ */ jsx$1(
|
|
60111
|
+
Box,
|
|
60112
|
+
{
|
|
60113
|
+
as: "pre",
|
|
60114
|
+
m: "0",
|
|
60115
|
+
p: "2",
|
|
60116
|
+
maxH: "160px",
|
|
60117
|
+
overflow: "auto",
|
|
60118
|
+
bg: "bg.canvas",
|
|
60119
|
+
borderRadius: "md",
|
|
60120
|
+
borderWidth: "1px",
|
|
60121
|
+
borderColor: "border.subtle",
|
|
60122
|
+
fontFamily: "mono",
|
|
60123
|
+
fontSize: "11px",
|
|
60124
|
+
color: "fg.muted",
|
|
60125
|
+
whiteSpace: "pre-wrap",
|
|
60126
|
+
children: tail
|
|
60127
|
+
}
|
|
60128
|
+
)
|
|
60129
|
+
] });
|
|
60130
|
+
}
|
|
59478
60131
|
function ActionButton({ button, variant: variant2, label, onClick, disabled: disabled2, pulse = false }) {
|
|
59479
60132
|
return /* @__PURE__ */ jsx$1(
|
|
59480
60133
|
Box,
|
|
@@ -59490,8 +60143,46 @@ function ActionButton({ button, variant: variant2, label, onClick, disabled: dis
|
|
|
59490
60143
|
}
|
|
59491
60144
|
);
|
|
59492
60145
|
}
|
|
60146
|
+
function DeckSkeleton({ tab }) {
|
|
60147
|
+
const sk = useRecipe({ key: "skeleton" });
|
|
60148
|
+
const bar = (w2, h2) => /* @__PURE__ */ jsx$1(Box, { css: sk({ variant: "line" }), width: w2, height: h2 });
|
|
60149
|
+
const block = (h2) => /* @__PURE__ */ jsx$1(Box, { css: sk({ variant: "block" }), width: "100%", minHeight: h2 });
|
|
60150
|
+
return /* @__PURE__ */ jsxs(Box, { p: "4.5", display: "flex", flexDirection: "column", gap: "4", children: [
|
|
60151
|
+
tab === "answer" && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
60152
|
+
/* @__PURE__ */ jsxs(Box, { display: "flex", gap: "4.5", alignItems: "flex-end", children: [
|
|
60153
|
+
/* @__PURE__ */ jsxs(Box, { display: "flex", flexDirection: "column", gap: "2", children: [
|
|
60154
|
+
bar("64px", "10px"),
|
|
60155
|
+
bar("120px", "32px")
|
|
60156
|
+
] }),
|
|
60157
|
+
bar("170px", "24px")
|
|
60158
|
+
] }),
|
|
60159
|
+
block("120px"),
|
|
60160
|
+
block("120px")
|
|
60161
|
+
] }),
|
|
60162
|
+
tab === "trust" && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
60163
|
+
bar("70%", "14px"),
|
|
60164
|
+
block("160px")
|
|
60165
|
+
] }),
|
|
60166
|
+
tab === "dose" && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
60167
|
+
block("240px"),
|
|
60168
|
+
/* @__PURE__ */ jsxs(Box, { display: "grid", gridTemplateColumns: "1fr 1fr", gap: "3", children: [
|
|
60169
|
+
block("90px"),
|
|
60170
|
+
block("90px")
|
|
60171
|
+
] })
|
|
60172
|
+
] }),
|
|
60173
|
+
tab === "validate" && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
60174
|
+
/* @__PURE__ */ jsxs(Box, { display: "flex", gap: "5", alignItems: "flex-end", children: [
|
|
60175
|
+
bar("120px", "32px"),
|
|
60176
|
+
bar("200px", "10px")
|
|
60177
|
+
] }),
|
|
60178
|
+
block("110px"),
|
|
60179
|
+
block("150px")
|
|
60180
|
+
] })
|
|
60181
|
+
] });
|
|
60182
|
+
}
|
|
60183
|
+
const experimentValueEqual = equalFor(Experiment.Component.schema);
|
|
59493
60184
|
const EastChakraExperiment = memo(function EastChakraExperiment2({ value }) {
|
|
59494
|
-
var _a2, _b2, _c2;
|
|
60185
|
+
var _a2, _b2, _c2, _d2;
|
|
59495
60186
|
const v3 = value;
|
|
59496
60187
|
const button = useRecipe({ key: "button" });
|
|
59497
60188
|
const chip = useRecipe({ key: "chip" });
|
|
@@ -59501,12 +60192,19 @@ const EastChakraExperiment = memo(function EastChakraExperiment2({ value }) {
|
|
|
59501
60192
|
const es = useSlotRecipe({ key: "eyebrowRow" })({});
|
|
59502
60193
|
const workspace = getReactiveDatasetCache().getConfig().workspace ?? "";
|
|
59503
60194
|
const data4 = useBindingValue(v3.data);
|
|
59504
|
-
const
|
|
60195
|
+
const configBind = useBindingValue(v3.config);
|
|
59505
60196
|
const journalBind = useBindingValue(v3.journal.type === "some" ? v3.journal.value : null);
|
|
60197
|
+
const populationBind = useBindingValue(v3.population.type === "some" ? v3.population.value : null);
|
|
59506
60198
|
const meta3 = getOpt(v3.columnMeta);
|
|
59507
60199
|
const readonly = getOpt(v3.readonly) ?? false;
|
|
59508
60200
|
const { columns, rowArrayType } = useColumns(workspace, v3.data.source);
|
|
59509
|
-
const
|
|
60201
|
+
const config2 = configBind.value;
|
|
60202
|
+
const [localPop, setLocalPop] = useState([]);
|
|
60203
|
+
const population = populationBind.value ?? localPop;
|
|
60204
|
+
const filteredRows = useMemo(() => {
|
|
60205
|
+
if (!data4.value) return null;
|
|
60206
|
+
return population.length ? data4.value.filter((r2) => rowMatchesAll(r2, population)) : data4.value;
|
|
60207
|
+
}, [data4.value, population]);
|
|
59510
60208
|
const fields = useMemo(
|
|
59511
60209
|
() => columns.filter((c2) => c2.kind !== "other").map((c2) => {
|
|
59512
60210
|
var _a3;
|
|
@@ -59518,132 +60216,176 @@ const EastChakraExperiment = memo(function EastChakraExperiment2({ value }) {
|
|
|
59518
60216
|
}),
|
|
59519
60217
|
[columns, meta3]
|
|
59520
60218
|
);
|
|
59521
|
-
const estInputs = useMemo(() => rowArrayType ? [rowArrayType, Experiment.Types.
|
|
59522
|
-
const
|
|
59523
|
-
const
|
|
59524
|
-
const
|
|
59525
|
-
|
|
59526
|
-
|
|
59527
|
-
|
|
59528
|
-
|
|
59529
|
-
|
|
59530
|
-
|
|
60219
|
+
const estInputs = useMemo(() => rowArrayType ? [rowArrayType, Experiment.Types.Config] : null, [rowArrayType]);
|
|
60220
|
+
const experiment = useFuncCall(v3.experiment.name, estInputs, Experiment.Types.Result);
|
|
60221
|
+
const hasDesign = v3.design.type === "some";
|
|
60222
|
+
const designInputs = useMemo(
|
|
60223
|
+
() => rowArrayType ? [rowArrayType, Experiment.Types.Config, Experiment.Types.Result, Experiment.Types.DesignConfig] : null,
|
|
60224
|
+
[rowArrayType]
|
|
60225
|
+
);
|
|
60226
|
+
const design = useFuncCall(v3.design.type === "some" ? v3.design.value.name : null, designInputs, Experiment.Types.Design);
|
|
60227
|
+
const pendingConfigRef = useRef(null);
|
|
60228
|
+
const [ranConfig, setRanConfig] = useState(null);
|
|
59531
60229
|
const runAll = useCallback(() => {
|
|
59532
|
-
if (!
|
|
59533
|
-
|
|
59534
|
-
|
|
59535
|
-
|
|
59536
|
-
}, [data4.value, spec, estimate, refute, dose, doseFeature, v3.refute.type, v3.dose.type]);
|
|
60230
|
+
if (!filteredRows || !config2) return;
|
|
60231
|
+
pendingConfigRef.current = config2;
|
|
60232
|
+
experiment.call(filteredRows, config2);
|
|
60233
|
+
}, [filteredRows, config2, experiment]);
|
|
59537
60234
|
const autoRan = useRef(false);
|
|
59538
60235
|
useEffect(() => {
|
|
59539
60236
|
if (autoRan.current) return;
|
|
59540
|
-
if (!
|
|
59541
|
-
if (
|
|
60237
|
+
if (!filteredRows || !config2 || !rowArrayType) return;
|
|
60238
|
+
if (experiment.result !== null || experiment.status === "running") {
|
|
59542
60239
|
autoRan.current = true;
|
|
59543
60240
|
return;
|
|
59544
60241
|
}
|
|
59545
|
-
if (
|
|
60242
|
+
if (experiment.status !== "idle") return;
|
|
59546
60243
|
autoRan.current = true;
|
|
60244
|
+
if (filteredRows.length > AUTORUN_MAX_ROWS) return;
|
|
59547
60245
|
runAll();
|
|
59548
|
-
}, [
|
|
60246
|
+
}, [filteredRows, config2, rowArrayType, experiment.result, experiment.status, runAll]);
|
|
60247
|
+
useEffect(() => {
|
|
60248
|
+
if (experiment.result !== null) setRanConfig(pendingConfigRef.current);
|
|
60249
|
+
}, [experiment.result]);
|
|
59549
60250
|
const [stale, setStale] = useState(false);
|
|
59550
|
-
const
|
|
60251
|
+
const editConfig = useCallback((next2) => {
|
|
60252
|
+
if (readonly) return;
|
|
60253
|
+
setStale(true);
|
|
60254
|
+
queueMicrotask(() => configBind.mutate(next2));
|
|
60255
|
+
}, [readonly, configBind]);
|
|
60256
|
+
const editPopulation = useCallback((next2) => {
|
|
60257
|
+
if (readonly) return;
|
|
59551
60258
|
setStale(true);
|
|
59552
|
-
queueMicrotask(() =>
|
|
59553
|
-
|
|
60259
|
+
if (v3.population.type === "some") queueMicrotask(() => populationBind.mutate(next2));
|
|
60260
|
+
else setLocalPop(next2);
|
|
60261
|
+
}, [readonly, v3.population.type, populationBind]);
|
|
59554
60262
|
const onRun = useCallback(() => {
|
|
59555
60263
|
runAll();
|
|
59556
60264
|
setStale(false);
|
|
59557
60265
|
}, [runAll]);
|
|
59558
60266
|
const onCommit = useCallback(async () => {
|
|
59559
|
-
if (!
|
|
59560
|
-
const
|
|
60267
|
+
if (!config2 || !experiment.result) return;
|
|
60268
|
+
const r2 = experiment.result;
|
|
60269
|
+
const row = {
|
|
60270
|
+
config: config2,
|
|
60271
|
+
verdict: r2.verdict,
|
|
60272
|
+
naive: r2.naive,
|
|
60273
|
+
adjusted: r2.adjusted.type === "some" ? some(r2.adjusted.value.effect) : none$1,
|
|
60274
|
+
committed_at: /* @__PURE__ */ new Date(),
|
|
60275
|
+
committed_by: "you"
|
|
60276
|
+
};
|
|
59561
60277
|
journalBind.mutate([row, ...journalBind.value ?? []]);
|
|
59562
60278
|
try {
|
|
59563
60279
|
await journalBind.commit();
|
|
59564
|
-
await
|
|
60280
|
+
await configBind.commit();
|
|
60281
|
+
if (v3.population.type === "some") await populationBind.commit();
|
|
59565
60282
|
setStale(false);
|
|
59566
60283
|
} catch {
|
|
59567
60284
|
}
|
|
59568
|
-
}, [
|
|
60285
|
+
}, [config2, experiment.result, journalBind, configBind, populationBind, v3.population.type]);
|
|
59569
60286
|
const [tab, setTab] = useState(((_a2 = getOpt(v3.defaultTab)) == null ? void 0 : _a2.type) ?? "answer");
|
|
60287
|
+
const [guidance, setGuidance] = useState(true);
|
|
59570
60288
|
const now2 = useMemo(() => /* @__PURE__ */ new Date(), []);
|
|
59571
|
-
const nRows = (
|
|
60289
|
+
const nRows = (filteredRows == null ? void 0 : filteredRows.length) ?? 0;
|
|
60290
|
+
const helpVars = useMemo(() => {
|
|
60291
|
+
const rc = ranConfig ?? config2;
|
|
60292
|
+
const labelOf = (col) => {
|
|
60293
|
+
var _a3;
|
|
60294
|
+
return col ? getOpt((_a3 = meta3 == null ? void 0 : meta3.get(col)) == null ? void 0 : _a3.label) ?? col : "";
|
|
60295
|
+
};
|
|
60296
|
+
return { treatment: labelOf(rc == null ? void 0 : rc.treatment), outcome: labelOf(rc == null ? void 0 : rc.outcome), subject: SUBJECT_ONE, subjects: SUBJECT_MANY };
|
|
60297
|
+
}, [ranConfig, config2, meta3]);
|
|
59572
60298
|
const view = useMemo(() => {
|
|
59573
|
-
if (!
|
|
59574
|
-
return deriveView(
|
|
59575
|
-
}, [
|
|
59576
|
-
|
|
59577
|
-
|
|
59578
|
-
|
|
59579
|
-
|
|
59580
|
-
|
|
59581
|
-
|
|
59582
|
-
const
|
|
60299
|
+
if (!config2) return null;
|
|
60300
|
+
return deriveView(config2, ranConfig ?? config2, columns, experiment.result, journalBind.value, meta3, nRows, now2);
|
|
60301
|
+
}, [config2, ranConfig, columns, experiment.result, journalBind.value, meta3, nRows, now2]);
|
|
60302
|
+
const designConfigValue = useMemo(() => ({
|
|
60303
|
+
alpha: none$1,
|
|
60304
|
+
target_power: none$1,
|
|
60305
|
+
materiality: none$1,
|
|
60306
|
+
treated_shares: some([0.5, 0.3])
|
|
60307
|
+
}), []);
|
|
60308
|
+
const designSnapRef = useRef(null);
|
|
60309
|
+
useEffect(() => {
|
|
60310
|
+
var _a3;
|
|
60311
|
+
if (tab !== "validate" || !hasDesign || design.pending) return;
|
|
60312
|
+
if (!filteredRows || !config2 || !experiment.result) return;
|
|
60313
|
+
if (((_a3 = designSnapRef.current) == null ? void 0 : _a3.result) === experiment.result) return;
|
|
60314
|
+
designSnapRef.current = { result: experiment.result, config: ranConfig ?? config2 };
|
|
60315
|
+
design.call(filteredRows, ranConfig ?? config2, experiment.result, designConfigValue);
|
|
60316
|
+
}, [tab, hasDesign, filteredRows, config2, ranConfig, experiment.result, design, designConfigValue]);
|
|
60317
|
+
const vmDesign = useMemo(
|
|
60318
|
+
() => design.result && designSnapRef.current ? deriveDesign(design.result, designSnapRef.current.result, designSnapRef.current.config, meta3) : null,
|
|
60319
|
+
[design.result, meta3]
|
|
60320
|
+
);
|
|
60321
|
+
const designFresh = design.result !== null && ((_b2 = designSnapRef.current) == null ? void 0 : _b2.result) === experiment.result && !design.pending;
|
|
60322
|
+
if (!config2 || !view) {
|
|
60323
|
+
const failed2 = experiment.status === "failed";
|
|
60324
|
+
return /* @__PURE__ */ jsx$1(Box, { layerStyle: "frame", p: "6", children: failed2 && experiment.error ? /* @__PURE__ */ jsx$1(RunError, { error: experiment.error }) : /* @__PURE__ */ jsx$1(Text, { className: failed2 ? void 0 : "elara-skeleton", textStyle: "body.sm", color: "fg.muted", children: failed2 ? "Could not run the experiment." : "Loading experiment…" }) });
|
|
60325
|
+
}
|
|
60326
|
+
const { spec: vs, answer: a2, refusal: ref, overlap: ov, refute: vr, dose: vd, journal, verdict } = view;
|
|
59583
60327
|
const higherBetter = getOpt((_c2 = meta3 == null ? void 0 : meta3.get(vs.outcome)) == null ? void 0 : _c2.higherIsBetter);
|
|
59584
|
-
const dirUp = ans.effect > 0;
|
|
59585
|
-
const statusWord = higherBetter === void 0 ? dirUp ? "Higher" : "Lower" : dirUp === higherBetter ? "Better" : "Worse";
|
|
59586
|
-
const flip = Math.sign(ans.naive) !== Math.sign(ans.effect) && ans.naive !== 0;
|
|
59587
|
-
const top = ans.balance[0] ?? { col: "", display: "" };
|
|
59588
|
-
const lowerWord = ans.naive < 0 ? "lower" : "higher";
|
|
59589
|
-
const population = getOpt(spec.population) ?? [];
|
|
59590
60328
|
const dataStatus = statusR({ status: "success", size: "sm" });
|
|
59591
60329
|
const barList = (rows) => /* @__PURE__ */ jsx$1(Box, { css: bs.root, children: rows.map((r2, i) => /* @__PURE__ */ jsxs(Box, { css: bs.row, children: [
|
|
59592
60330
|
/* @__PURE__ */ jsx$1(Text, { css: bs.label, textStyle: "mono.sm", color: "fg.default", truncate: true, children: r2.label }),
|
|
59593
60331
|
/* @__PURE__ */ jsx$1(Box, { css: bs.track, children: /* @__PURE__ */ jsx$1(Box, { css: bs.fill, width: `${Math.round(r2.frac * 100)}%`, bg: toneToken(r2.tone) }) }),
|
|
59594
60332
|
/* @__PURE__ */ jsx$1(Text, { css: bs.value, children: r2.value })
|
|
59595
60333
|
] }, i)) });
|
|
59596
|
-
const specStaged =
|
|
60334
|
+
const specStaged = configBind.mode === "staged";
|
|
59597
60335
|
const hasJournal = v3.journal.type === "some";
|
|
59598
60336
|
const canRun = !readonly;
|
|
59599
60337
|
const canCommit = !readonly && specStaged && hasJournal;
|
|
59600
|
-
const runDisabled = !data4.value ||
|
|
59601
|
-
const commitDisabled =
|
|
59602
|
-
|
|
60338
|
+
const runDisabled = !data4.value || experiment.pending;
|
|
60339
|
+
const commitDisabled = experiment.status !== "succeeded" || experiment.pending || stale;
|
|
60340
|
+
const failed = experiment.status === "failed" && experiment.error;
|
|
60341
|
+
const showResult = experiment.result !== null && !experiment.pending && !failed;
|
|
60342
|
+
const tabKeys = [...["answer", "trust", "dose"], ...hasDesign ? ["validate"] : []];
|
|
60343
|
+
return /* @__PURE__ */ jsx$1(GuidanceProvider, { on: guidance, vars: helpVars, children: /* @__PURE__ */ jsxs(Box, { layerStyle: "frame", overflow: "visible", children: [
|
|
59603
60344
|
/* @__PURE__ */ jsxs(Box, { layerStyle: "header.bar", display: "flex", alignItems: "center", gap: "3.5", children: [
|
|
59604
|
-
/* @__PURE__ */
|
|
59605
|
-
"Does
|
|
59606
|
-
/* @__PURE__ */ jsx$1(Text, { as: "span", color: "brand.
|
|
59607
|
-
"
|
|
59608
|
-
/* @__PURE__ */ jsx$1(Text, { as: "span", color: "brand.
|
|
60345
|
+
/* @__PURE__ */ jsx$1(Text, { textStyle: "title.card", color: "fg.muted", children: /* @__PURE__ */ jsxs(Help, { id: "header", children: [
|
|
60346
|
+
"Does ",
|
|
60347
|
+
/* @__PURE__ */ jsx$1(Text, { as: "span", color: "brand.solid", fontWeight: "bold", children: vs.treatment }),
|
|
60348
|
+
" change ",
|
|
60349
|
+
/* @__PURE__ */ jsx$1(Text, { as: "span", color: "brand.solid", fontWeight: "bold", children: vs.outcome }),
|
|
59609
60350
|
"?"
|
|
59610
|
-
] }),
|
|
60351
|
+
] }) }),
|
|
59611
60352
|
/* @__PURE__ */ jsx$1(Box, { flex: "1" }),
|
|
59612
60353
|
/* @__PURE__ */ jsxs(Box, { as: "span", css: dataStatus.root, children: [
|
|
59613
60354
|
/* @__PURE__ */ jsx$1(Box, { as: "span", css: dataStatus.indicator }),
|
|
59614
|
-
/* @__PURE__ */ jsx$1(Box, { as: "span", css: dataStatus.label, children: vs.dataLabel })
|
|
60355
|
+
/* @__PURE__ */ jsx$1(Box, { as: "span", css: dataStatus.label, children: stale ? `${nRows} rows` : vs.dataLabel })
|
|
59615
60356
|
] }),
|
|
60357
|
+
/* @__PURE__ */ jsx$1(GuidanceToggle, { on: guidance, onToggle: () => setGuidance((g2) => !g2) }),
|
|
59616
60358
|
canRun && /* @__PURE__ */ jsx$1(ActionButton, { button, variant: "solid", label: "Run", onClick: onRun, disabled: runDisabled, pulse: stale }),
|
|
59617
60359
|
canCommit && /* @__PURE__ */ jsx$1(ActionButton, { button, variant: "ghost", label: "Commit", onClick: onCommit, disabled: commitDisabled })
|
|
59618
60360
|
] }),
|
|
59619
60361
|
/* @__PURE__ */ jsxs(Box, { display: "grid", gridTemplateColumns: "304px minmax(0,1fr)", alignItems: "start", children: [
|
|
59620
60362
|
/* @__PURE__ */ jsxs(Box, { borderRightWidth: "1px", borderColor: "border.subtle", children: [
|
|
59621
|
-
/* @__PURE__ */ jsxs(Step, { n: 1, title: "What did you change?", children: [
|
|
59622
|
-
/* @__PURE__ */ jsx$1(ColumnPick, { column: vs.treatment, kind: vs.treatmentKind, badge, button, choices: columns.filter((c2) => c2.kind === "boolean" || c2.kind === "integer").map((c2) => c2.name), onPick: (c2) =>
|
|
60363
|
+
/* @__PURE__ */ jsxs(Step, { n: 1, title: "What did you change?", help: "step_treatment", children: [
|
|
60364
|
+
/* @__PURE__ */ jsx$1(ColumnPick, { column: vs.treatment, kind: vs.treatmentKind, badge, button, choices: columns.filter((c2) => c2.kind === "boolean" || c2.kind === "integer").map((c2) => c2.name), onPick: (c2) => editConfig({ ...config2, treatment: c2 }) }),
|
|
59623
60365
|
/* @__PURE__ */ jsxs(Text, { textStyle: "caption", mt: "1.5", children: [
|
|
59624
60366
|
"Treated = ",
|
|
59625
60367
|
/* @__PURE__ */ jsx$1(Text, { as: "span", color: "fg.default", fontWeight: "semibold", children: vs.comparison })
|
|
59626
60368
|
] })
|
|
59627
60369
|
] }),
|
|
59628
|
-
/* @__PURE__ */ jsx$1(Step, { n: 2, title: "What did you want it to improve?", children: /* @__PURE__ */ jsx$1(ColumnPick, { column: vs.outcome, kind: vs.outcomeKind, badge, button, choices: columns.filter((c2) => c2.kind === "float" || c2.kind === "integer").map((c2) => c2.name), onPick: (c2) =>
|
|
59629
|
-
/* @__PURE__ */ jsx$1(Step, { n: 3, title: "What else was different
|
|
59630
|
-
vs.confounders.map((c2, i) => /* @__PURE__ */ jsxs(Box, { display: "grid", gridTemplateColumns: "1fr 78px 16px", gap: "2.5", alignItems: "center", px: "2.5", py: "2.5", borderTopWidth: i ? "1px" : "0", borderColor: "border.subtle", children: [
|
|
60370
|
+
/* @__PURE__ */ jsx$1(Step, { n: 2, title: "What did you want it to improve?", help: "step_outcome", children: /* @__PURE__ */ jsx$1(ColumnPick, { column: vs.outcome, kind: vs.outcomeKind, badge, button, choices: columns.filter((c2) => c2.kind === "float" || c2.kind === "integer").map((c2) => c2.name), onPick: (c2) => editConfig({ ...config2, outcome: c2 }) }) }),
|
|
60371
|
+
/* @__PURE__ */ jsx$1(Step, { n: 3, title: "What else was different?", help: "step_confounders", children: /* @__PURE__ */ jsxs(Box, { layerStyle: "frame.flat", children: [
|
|
60372
|
+
/* @__PURE__ */ jsx$1(Box, { maxH: "216px", overflowY: "auto", children: vs.confounders.map((c2, i) => /* @__PURE__ */ jsxs(Box, { display: "grid", gridTemplateColumns: "1fr 78px 16px", gap: "2.5", alignItems: "center", px: "2.5", py: "2.5", borderTopWidth: i ? "1px" : "0", borderColor: "border.subtle", children: [
|
|
59631
60373
|
/* @__PURE__ */ jsxs(Box, { children: [
|
|
59632
60374
|
/* @__PURE__ */ jsx$1(Text, { textStyle: "mono.sm", fontWeight: "semibold", color: "fg.default", children: c2.col }),
|
|
59633
60375
|
/* @__PURE__ */ jsx$1(Text, { textStyle: "caption", lineHeight: "1.35", mt: "px", children: c2.reason })
|
|
59634
60376
|
] }),
|
|
59635
60377
|
/* @__PURE__ */ jsxs(Box, { children: [
|
|
59636
60378
|
/* @__PURE__ */ jsx$1(Box, { css: bs.track, children: /* @__PURE__ */ jsx$1(Box, { css: bs.fill, width: `${Math.round(c2.imbalance * 100)}%`, bg: toneToken(c2.tone) }) }),
|
|
59637
|
-
/* @__PURE__ */ jsx$1(Text, { textStyle: "caption.eyebrow", fontSize: "9px", textAlign: "center", mt: "1.5", children: c2.level })
|
|
60379
|
+
/* @__PURE__ */ jsx$1(Text, { textStyle: "caption.eyebrow", fontSize: "9px", textAlign: "center", mt: "1.5", children: /* @__PURE__ */ jsx$1(Help, { id: "confounder_imbalance", children: c2.level }) })
|
|
59638
60380
|
] }),
|
|
59639
|
-
/* @__PURE__ */ jsx$1(Box, { as: "button", css: button({ variant: "ghost", size: "xs" }), px: "0", minW: "16px", display: "inline-flex", alignItems: "center", justifyContent: "center", "aria-label": `Remove ${c2.col}`, onClick: () =>
|
|
59640
|
-
] }, i)),
|
|
59641
|
-
vs.suggestion && /* @__PURE__ */ jsx$1(ColumnMenu, { choices: columns.filter((c2) => !(/* @__PURE__ */ new Set([
|
|
60381
|
+
/* @__PURE__ */ jsx$1(Box, { as: "button", css: button({ variant: "ghost", size: "xs" }), px: "0", minW: "16px", display: "inline-flex", alignItems: "center", justifyContent: "center", "aria-label": `Remove ${c2.col}`, onClick: () => editConfig({ ...config2, common_causes: config2.common_causes.filter((x2) => x2 !== c2.col), categorical: config2.categorical.type === "some" ? some(config2.categorical.value.filter((x2) => x2 !== c2.col)) : none$1 }), children: /* @__PURE__ */ jsx$1(FontAwesomeIcon, { icon: faXmark, style: { fontSize: "11px" } }) })
|
|
60382
|
+
] }, i)) }),
|
|
60383
|
+
vs.suggestion && /* @__PURE__ */ jsx$1(ColumnMenu, { choices: columns.filter((c2) => !(/* @__PURE__ */ new Set([config2.treatment, config2.outcome, ...config2.common_causes])).has(c2.name)).map((c2) => c2.name), onPick: (c2) => editConfig({ ...config2, common_causes: [...config2.common_causes, c2] }), children: /* @__PURE__ */ jsxs(Box, { as: "button", css: button({ variant: "ghost", size: "sm" }), justifyContent: "flex-start", width: "100%", color: "brand.fg", fontFamily: "mono", display: "inline-flex", alignItems: "center", gap: "2", children: [
|
|
59642
60384
|
/* @__PURE__ */ jsx$1(FontAwesomeIcon, { icon: faPlus, style: { fontSize: "9px" } }),
|
|
59643
60385
|
"add another"
|
|
59644
60386
|
] }) })
|
|
59645
60387
|
] }) }),
|
|
59646
|
-
/* @__PURE__ */ jsx$1(Step, { n: 4, title:
|
|
60388
|
+
/* @__PURE__ */ jsx$1(Step, { n: 4, title: `Which ${SUBJECT_MANY}?`, help: "step_population", children: /* @__PURE__ */ jsx$1(FilterRail, { fields, population, onChange: editPopulation, chip, button }) }),
|
|
59647
60389
|
/* @__PURE__ */ jsxs(Box, { as: "details", borderTopWidth: "1px", borderColor: "border.subtle", children: [
|
|
59648
60390
|
/* @__PURE__ */ jsxs(
|
|
59649
60391
|
Box,
|
|
@@ -59676,173 +60418,168 @@ const EastChakraExperiment = memo(function EastChakraExperiment2({ value }) {
|
|
|
59676
60418
|
}
|
|
59677
60419
|
),
|
|
59678
60420
|
/* @__PURE__ */ jsxs(Box, { px: "4.5", pb: "3.5", pt: "0.5", children: [
|
|
59679
|
-
/* @__PURE__ */ jsx$1(Segmented, { label: "How to compare", left: "regression", right: "reweighting", active: vs.method === "reweighting" ? "right" : "left", onPick: (s2) =>
|
|
59680
|
-
/* @__PURE__ */ jsx$1(Segmented, { label: "Answer for", left: "all
|
|
59681
|
-
/* @__PURE__ */ jsx$1(Segmented, { label: "Drop un-matchable", left: "on", right: "off", active: vs.trim ? "left" : "right", onPick: (s2) => editSpec({ ...spec, trim: s2 === "left" ? some(variant("overlap", null)) : none$1 }), last: true })
|
|
60421
|
+
/* @__PURE__ */ jsx$1(Segmented, { label: "How to compare", help: "adv_method", left: "regression", right: "reweighting", active: vs.method === "reweighting" ? "right" : "left", onPick: (s2) => editConfig({ ...config2, method: some(s2 === "right" ? variant("propensity_score_weighting", { weighting_scheme: none$1 }) : variant("linear_regression", null)) }) }),
|
|
60422
|
+
/* @__PURE__ */ jsx$1(Segmented, { label: "Answer for", help: "adv_estimand", left: "all", right: "only treated", active: vs.target === "treated" ? "right" : "left", onPick: (s2) => editConfig({ ...config2, estimand: some(variant(s2 === "right" ? "att" : "ate", null)) }), last: true })
|
|
59682
60423
|
] })
|
|
59683
60424
|
] })
|
|
59684
60425
|
] }),
|
|
59685
60426
|
/* @__PURE__ */ jsxs(Box, { minW: "0", children: [
|
|
59686
|
-
/* @__PURE__ */
|
|
59687
|
-
|
|
59688
|
-
|
|
59689
|
-
|
|
59690
|
-
|
|
59691
|
-
|
|
59692
|
-
|
|
59693
|
-
|
|
59694
|
-
|
|
59695
|
-
|
|
59696
|
-
|
|
59697
|
-
|
|
59698
|
-
|
|
59699
|
-
|
|
59700
|
-
|
|
59701
|
-
|
|
59702
|
-
|
|
59703
|
-
|
|
60427
|
+
/* @__PURE__ */ jsxs(
|
|
60428
|
+
Box,
|
|
60429
|
+
{
|
|
60430
|
+
display: "flex",
|
|
60431
|
+
alignItems: "center",
|
|
60432
|
+
px: "4",
|
|
60433
|
+
borderBottomWidth: "1px",
|
|
60434
|
+
borderColor: "border.subtle",
|
|
60435
|
+
role: "tablist",
|
|
60436
|
+
"aria-label": "Result views",
|
|
60437
|
+
onKeyDown: (e3) => {
|
|
60438
|
+
var _a3;
|
|
60439
|
+
const i = tabKeys.indexOf(tab);
|
|
60440
|
+
let j2 = i;
|
|
60441
|
+
if (e3.key === "ArrowRight") j2 = (i + 1) % tabKeys.length;
|
|
60442
|
+
else if (e3.key === "ArrowLeft") j2 = (i - 1 + tabKeys.length) % tabKeys.length;
|
|
60443
|
+
else if (e3.key === "Home") j2 = 0;
|
|
60444
|
+
else if (e3.key === "End") j2 = tabKeys.length - 1;
|
|
60445
|
+
else return;
|
|
60446
|
+
e3.preventDefault();
|
|
60447
|
+
setTab(tabKeys[j2]);
|
|
60448
|
+
const btns = e3.currentTarget.querySelectorAll('[role="tab"]');
|
|
60449
|
+
(_a3 = btns[j2]) == null ? void 0 : _a3.focus();
|
|
59704
60450
|
},
|
|
59705
|
-
|
|
59706
|
-
|
|
59707
|
-
|
|
59708
|
-
|
|
59709
|
-
|
|
59710
|
-
|
|
59711
|
-
|
|
59712
|
-
|
|
59713
|
-
|
|
59714
|
-
|
|
59715
|
-
|
|
59716
|
-
|
|
59717
|
-
|
|
59718
|
-
|
|
59719
|
-
|
|
60451
|
+
children: [
|
|
60452
|
+
tabKeys.map((tk) => {
|
|
60453
|
+
const on3 = tab === tk;
|
|
60454
|
+
const tabHelp = tk === "answer" ? "tab_answer" : tk === "trust" ? "tab_trust" : tk === "dose" ? "tab_dose" : "tab_validate";
|
|
60455
|
+
const tabLabel = tk === "answer" ? "Answer" : tk === "trust" ? "Trust" : tk === "dose" ? "Dose" : "Validate";
|
|
60456
|
+
return /* @__PURE__ */ jsx$1(
|
|
60457
|
+
Box,
|
|
60458
|
+
{
|
|
60459
|
+
as: "button",
|
|
60460
|
+
role: "tab",
|
|
60461
|
+
"aria-selected": on3,
|
|
60462
|
+
tabIndex: on3 ? 0 : -1,
|
|
60463
|
+
onClick: () => setTab(tk),
|
|
60464
|
+
cursor: "pointer",
|
|
60465
|
+
fontSize: "xs",
|
|
60466
|
+
fontWeight: "semibold",
|
|
60467
|
+
px: "3.5",
|
|
60468
|
+
py: "3",
|
|
60469
|
+
mb: "-1px",
|
|
60470
|
+
color: on3 ? "brand.fg" : "fg.muted",
|
|
60471
|
+
borderBottomWidth: "2px",
|
|
60472
|
+
borderColor: on3 ? "brand.solid" : "transparent",
|
|
60473
|
+
_hover: { color: on3 ? "brand.fg" : "fg.default" },
|
|
60474
|
+
_focusVisible: { outline: "2px solid", outlineColor: "brand.solid", outlineOffset: "-2px" },
|
|
60475
|
+
children: /* @__PURE__ */ jsx$1(Help, { id: tabHelp, children: tabLabel })
|
|
60476
|
+
},
|
|
60477
|
+
tk
|
|
60478
|
+
);
|
|
60479
|
+
}),
|
|
60480
|
+
experiment.pending && /* @__PURE__ */ jsxs(Box, { ml: "auto", display: "inline-flex", alignItems: "center", gap: "2", color: "fg.muted", pr: "1", children: [
|
|
60481
|
+
/* @__PURE__ */ jsx$1(Spinner, { size: "xs", borderWidth: "1.5px", color: "brand.solid" }),
|
|
60482
|
+
/* @__PURE__ */ jsx$1(Text, { textStyle: "caption.eyebrow", children: "Running…" })
|
|
59720
60483
|
] })
|
|
59721
|
-
]
|
|
59722
|
-
|
|
59723
|
-
|
|
59724
|
-
|
|
59725
|
-
|
|
59726
|
-
|
|
59727
|
-
flip && /* @__PURE__ */ jsxs(Box, { layerStyle: "banner.stale", display: "flex", alignItems: "flex-start", gap: "2", mt: "3", children: [
|
|
59728
|
-
/* @__PURE__ */ jsx$1(Box, { as: "span", color: "fg.warning", flexShrink: "0", mt: "0.5", fontSize: "12px", children: /* @__PURE__ */ jsx$1(FontAwesomeIcon, { icon: faTriangleExclamation }) }),
|
|
60484
|
+
]
|
|
60485
|
+
}
|
|
60486
|
+
),
|
|
60487
|
+
failed ? /* @__PURE__ */ jsx$1(Box, { px: "4.5", pt: "4.5", children: /* @__PURE__ */ jsx$1(RunError, { error: experiment.error }) }) : !showResult ? /* @__PURE__ */ jsx$1(DeckSkeleton, { tab }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
60488
|
+
stale && /* @__PURE__ */ jsxs(Box, { layerStyle: "banner.stale", display: "flex", alignItems: "center", gap: "2", mx: "4.5", mt: "4.5", children: [
|
|
60489
|
+
/* @__PURE__ */ jsx$1(Box, { as: "span", color: "fg.warning", flexShrink: "0", fontSize: "12px", children: /* @__PURE__ */ jsx$1(FontAwesomeIcon, { icon: faTriangleExclamation }) }),
|
|
59729
60490
|
/* @__PURE__ */ jsxs(Text, { textStyle: "body.sm", color: "fg.default", children: [
|
|
59730
|
-
/* @__PURE__ */ jsx$1(Text, { as: "span", fontWeight: "bold", children: "
|
|
59731
|
-
"
|
|
59732
|
-
/* @__PURE__ */ jsx$1(Text, { as: "span", fontFamily: "mono", children: ans.treatment }),
|
|
59733
|
-
" batches sit ",
|
|
59734
|
-
/* @__PURE__ */ jsx$1(Text, { as: "span", fontStyle: "italic", children: lowerWord }),
|
|
59735
|
-
" on ",
|
|
59736
|
-
/* @__PURE__ */ jsx$1(Text, { as: "span", fontFamily: "mono", children: ans.outcome }),
|
|
59737
|
-
" (",
|
|
59738
|
-
signed(ans.naive),
|
|
59739
|
-
") — but they also differ most on ",
|
|
59740
|
-
/* @__PURE__ */ jsx$1(Text, { as: "span", fontFamily: "mono", children: top.col }),
|
|
59741
|
-
" (",
|
|
59742
|
-
top.display,
|
|
59743
|
-
"). Adjusting for it reverses the result."
|
|
60491
|
+
/* @__PURE__ */ jsx$1(Text, { as: "span", fontWeight: "bold", children: "Showing the previous setup." }),
|
|
60492
|
+
" Hit Run to update these results for your edits."
|
|
59744
60493
|
] })
|
|
59745
60494
|
] }),
|
|
59746
|
-
/* @__PURE__ */
|
|
59747
|
-
|
|
59748
|
-
|
|
59749
|
-
|
|
59750
|
-
|
|
59751
|
-
|
|
59752
|
-
|
|
59753
|
-
|
|
59754
|
-
|
|
59755
|
-
|
|
59756
|
-
|
|
59757
|
-
|
|
59758
|
-
|
|
59759
|
-
|
|
59760
|
-
|
|
59761
|
-
|
|
59762
|
-
|
|
59763
|
-
|
|
59764
|
-
/* @__PURE__ */
|
|
59765
|
-
|
|
59766
|
-
|
|
59767
|
-
|
|
59768
|
-
" ",
|
|
59769
|
-
label
|
|
59770
|
-
] }, i)) })
|
|
59771
|
-
] }),
|
|
59772
|
-
tab === "trust" && vr && /* @__PURE__ */ jsxs(Box, { p: "4.5", children: [
|
|
59773
|
-
/* @__PURE__ */ jsx$1(Text, { textStyle: "body.sm", color: "fg.muted", mb: "3.5", children: "Before trusting the answer we tried to break it — colour shows pass / caution." }),
|
|
59774
|
-
/* @__PURE__ */ jsx$1(Box, { layerStyle: "frame.flat", children: vr.checks.map((c2, i) => {
|
|
59775
|
-
const cs = statusR({ status: c2.passed ? "success" : "warning", size: "sm" });
|
|
59776
|
-
return /* @__PURE__ */ jsxs(Box, { display: "grid", gridTemplateColumns: "auto 1fr minmax(96px,auto)", gap: "2.5", alignItems: "start", px: "3.5", py: "2.5", borderTopWidth: i ? "1px" : "0", borderColor: "border.subtle", children: [
|
|
59777
|
-
/* @__PURE__ */ jsx$1(Box, { as: "span", css: cs.root, mt: "1", children: /* @__PURE__ */ jsx$1(Box, { as: "span", css: cs.indicator }) }),
|
|
59778
|
-
/* @__PURE__ */ jsxs(Box, { children: [
|
|
59779
|
-
/* @__PURE__ */ jsx$1(Text, { textStyle: "body.sm", fontWeight: "semibold", color: "fg.default", children: c2.name }),
|
|
59780
|
-
/* @__PURE__ */ jsx$1(Text, { textStyle: "caption", lineHeight: "1.45", mt: "0.5", children: c2.desc })
|
|
59781
|
-
] }),
|
|
59782
|
-
/* @__PURE__ */ jsxs(Box, { display: "inline-flex", alignItems: "center", justifyContent: "flex-end", gap: "1.5", whiteSpace: "nowrap", color: c2.passed ? "fg.success" : "fg.warning", children: [
|
|
59783
|
-
/* @__PURE__ */ jsx$1(Text, { textStyle: "mono.sm", fontWeight: "bold", fontVariantNumeric: "tabular-nums", children: c2.value }),
|
|
59784
|
-
/* @__PURE__ */ jsx$1(FontAwesomeIcon, { icon: c2.passed ? faCheck : faTriangleExclamation, style: { fontSize: "11px" } })
|
|
59785
|
-
] })
|
|
59786
|
-
] }, i);
|
|
59787
|
-
}) }),
|
|
59788
|
-
/* @__PURE__ */ jsxs(Card, { children: [
|
|
59789
|
-
/* @__PURE__ */ jsx$1(Cap, { children: "Effect as a hidden cause is made stronger" }),
|
|
59790
|
-
/* @__PURE__ */ jsx$1(AreaRange, { lo: vr.sensLo, mid: vr.sensMid, hi: vr.sensHi, zero: 0, tone: "brand", xTicks: vr.sensXTicks, yTicks: vr.sensYTicks, height: 132 })
|
|
59791
|
-
] })
|
|
59792
|
-
] }),
|
|
59793
|
-
tab === "dose" && vd && /* @__PURE__ */ jsxs(Box, { p: "4.5", children: [
|
|
59794
|
-
vd.segments.length > 1 && /* @__PURE__ */ jsxs(Box, { display: "flex", alignItems: "center", gap: "2.5", mb: "3.5", children: [
|
|
59795
|
-
/* @__PURE__ */ jsx$1(Text, { textStyle: "caption.eyebrow", children: "Response for" }),
|
|
59796
|
-
/* @__PURE__ */ jsx$1(SegmentSelect, { options: vd.segments, active: 0, onPick: () => {
|
|
59797
|
-
} })
|
|
60495
|
+
tab === "answer" && a2 && /* @__PURE__ */ jsx$1(AnswerNumeric, { a: a2, verdict, higherBetter, badge, barList }),
|
|
60496
|
+
tab === "answer" && !a2 && ref && /* @__PURE__ */ jsx$1(RefusalZone, { refusal: ref, overlap: ov, naiveValue: ((_d2 = experiment.result) == null ? void 0 : _d2.naive) ?? 0, outcome: vs.outcome }),
|
|
60497
|
+
tab === "trust" && vr && /* @__PURE__ */ jsxs(Box, { p: "4.5", children: [
|
|
60498
|
+
/* @__PURE__ */ jsx$1(Text, { textStyle: "body.sm", color: "fg.muted", mb: "3.5", children: /* @__PURE__ */ jsx$1(Help, { id: "trust_intro", children: "Before trusting the answer we tried to break it — colour shows pass / caution." }) }),
|
|
60499
|
+
/* @__PURE__ */ jsx$1(Box, { layerStyle: "frame.flat", children: vr.checks.map((c2, i) => {
|
|
60500
|
+
const cs = statusR({ status: c2.passed ? "success" : "warning", size: "sm" });
|
|
60501
|
+
return /* @__PURE__ */ jsxs(Box, { display: "grid", gridTemplateColumns: "auto 1fr minmax(96px,auto)", gap: "2.5", alignItems: "start", px: "3.5", py: "2.5", borderTopWidth: i ? "1px" : "0", borderColor: "border.subtle", children: [
|
|
60502
|
+
/* @__PURE__ */ jsx$1(Box, { as: "span", css: cs.root, mt: "1", children: /* @__PURE__ */ jsx$1(Box, { as: "span", css: cs.indicator }) }),
|
|
60503
|
+
/* @__PURE__ */ jsxs(Box, { children: [
|
|
60504
|
+
/* @__PURE__ */ jsx$1(Text, { textStyle: "body.sm", fontWeight: "semibold", color: "fg.default", children: /* @__PURE__ */ jsx$1(Help, { id: c2.help, children: c2.name }) }),
|
|
60505
|
+
/* @__PURE__ */ jsx$1(Text, { textStyle: "caption", lineHeight: "1.45", mt: "0.5", children: c2.desc })
|
|
60506
|
+
] }),
|
|
60507
|
+
/* @__PURE__ */ jsxs(Box, { display: "inline-flex", alignItems: "center", justifyContent: "flex-end", gap: "1.5", whiteSpace: "nowrap", color: c2.passed ? "fg.success" : "fg.warning", children: [
|
|
60508
|
+
/* @__PURE__ */ jsx$1(Text, { textStyle: "mono.sm", fontWeight: "bold", fontVariantNumeric: "tabular-nums", children: c2.value }),
|
|
60509
|
+
/* @__PURE__ */ jsx$1(FontAwesomeIcon, { icon: c2.passed ? faCheck : faTriangleExclamation, style: { fontSize: "11px" } })
|
|
60510
|
+
] })
|
|
60511
|
+
] }, i);
|
|
60512
|
+
}) }),
|
|
60513
|
+
vr.sens && /* @__PURE__ */ jsxs(Card, { children: [
|
|
60514
|
+
/* @__PURE__ */ jsx$1(Cap, { help: "sensitivity", children: "Effect as a hidden cause is made stronger" }),
|
|
60515
|
+
/* @__PURE__ */ jsx$1(AreaRange, { lo: vr.sens.lo, mid: vr.sens.mid, hi: vr.sens.hi, zero: 0, tone: "brand", xTicks: vr.sens.xTicks, yTicks: vr.sens.yTicks, height: 132 })
|
|
60516
|
+
] })
|
|
59798
60517
|
] }),
|
|
59799
|
-
/* @__PURE__ */ jsxs(
|
|
59800
|
-
/* @__PURE__ */ jsxs(
|
|
59801
|
-
|
|
59802
|
-
"
|
|
59803
|
-
vd.feature
|
|
60518
|
+
tab === "trust" && !vr && /* @__PURE__ */ jsxs(Box, { p: "4.5", children: [
|
|
60519
|
+
/* @__PURE__ */ jsxs(Box, { display: "inline-flex", alignItems: "center", gap: "2", mb: "2", color: "fg.muted", children: [
|
|
60520
|
+
/* @__PURE__ */ jsx$1(FontAwesomeIcon, { icon: faTriangleExclamation, style: { fontSize: "13px" } }),
|
|
60521
|
+
/* @__PURE__ */ jsx$1(Text, { textStyle: "body.sm", fontWeight: "semibold", color: "fg.default", children: "Nothing to stress-test" })
|
|
59804
60522
|
] }),
|
|
59805
|
-
/* @__PURE__ */
|
|
60523
|
+
/* @__PURE__ */ jsxs(Text, { textStyle: "body.sm", color: "fg.muted", lineHeight: "1.5", children: [
|
|
60524
|
+
"There’s no adjusted estimate to try to break — the experiment couldn’t produce one (see the ",
|
|
60525
|
+
/* @__PURE__ */ jsx$1(Text, { as: "span", fontWeight: "semibold", color: "fg.default", children: "Answer" }),
|
|
60526
|
+
" tab for why)."
|
|
60527
|
+
] })
|
|
59806
60528
|
] }),
|
|
59807
|
-
/* @__PURE__ */ jsxs(Box, {
|
|
59808
|
-
/* @__PURE__ */ jsxs(
|
|
59809
|
-
/* @__PURE__ */
|
|
59810
|
-
|
|
59811
|
-
|
|
59812
|
-
|
|
59813
|
-
/* @__PURE__ */ jsx$1(Text, { textStyle: "mono-kpi", fontFamily: "heading", color: "brand.solid", children: signed(vd.recoEffect) }),
|
|
59814
|
-
/* @__PURE__ */ jsxs(Text, { textStyle: "mono.sm", color: "fg.muted", children: [
|
|
59815
|
-
vd.outcome,
|
|
59816
|
-
" · ",
|
|
59817
|
-
signed(vd.recoLo),
|
|
59818
|
-
" … ",
|
|
59819
|
-
signed(vd.recoHi)
|
|
59820
|
-
] })
|
|
59821
|
-
] })
|
|
60529
|
+
tab === "dose" && vd && /* @__PURE__ */ jsxs(Box, { p: "4.5", children: [
|
|
60530
|
+
/* @__PURE__ */ jsxs(Card, { mt: "0", children: [
|
|
60531
|
+
/* @__PURE__ */ jsxs(Cap, { help: "dose_curve", children: [
|
|
60532
|
+
vd.outcome,
|
|
60533
|
+
" gained vs. ",
|
|
60534
|
+
vd.feature
|
|
59822
60535
|
] }),
|
|
59823
|
-
/* @__PURE__ */ jsx$1(
|
|
60536
|
+
/* @__PURE__ */ jsx$1(AreaRange, { lo: vd.lo, mid: vd.mid, hi: vd.hi, tone: "pos", xTicks: vd.xTicks, yTicks: vd.yTicks, marks: vd.marks.map((m2) => ({ at: m2.at, label: m2.label, tone: m2.tone, help: m2.help })), height: 256 })
|
|
59824
60537
|
] }),
|
|
59825
|
-
/* @__PURE__ */ jsxs(Box, {
|
|
59826
|
-
/* @__PURE__ */ jsxs(
|
|
59827
|
-
"
|
|
59828
|
-
|
|
59829
|
-
|
|
60538
|
+
/* @__PURE__ */ jsxs(Box, { display: "grid", gridTemplateColumns: "1fr 1fr", gap: "3", mt: "3", children: [
|
|
60539
|
+
/* @__PURE__ */ jsxs(Box, { layerStyle: "card", p: "3.5", borderRadius: "lg", children: [
|
|
60540
|
+
/* @__PURE__ */ jsx$1(Cap, { help: "dose_reco", children: "Recommended" }),
|
|
60541
|
+
/* @__PURE__ */ jsxs(Box, { display: "flex", flexDirection: "column", gap: "0.5", children: [
|
|
60542
|
+
/* @__PURE__ */ jsx$1(Text, { textStyle: "caption.eyebrow", children: vd.recoLabel }),
|
|
60543
|
+
/* @__PURE__ */ jsxs(Box, { display: "flex", alignItems: "baseline", gap: "2", children: [
|
|
60544
|
+
/* @__PURE__ */ jsx$1(Text, { textStyle: "mono-kpi", fontFamily: "heading", color: "brand.solid", children: signed(vd.recoEffect) }),
|
|
60545
|
+
/* @__PURE__ */ jsxs(Text, { textStyle: "mono.sm", color: "fg.muted", children: [
|
|
60546
|
+
vd.outcome,
|
|
60547
|
+
" · ",
|
|
60548
|
+
signed(vd.recoLo),
|
|
60549
|
+
" … ",
|
|
60550
|
+
signed(vd.recoHi)
|
|
60551
|
+
] })
|
|
60552
|
+
] })
|
|
60553
|
+
] }),
|
|
60554
|
+
/* @__PURE__ */ jsx$1(Text, { textStyle: "caption", mt: "2.5", pt: "2.5", borderTopWidth: "1px", borderColor: "border.subtle", children: vd.tradeoff })
|
|
59830
60555
|
] }),
|
|
59831
|
-
/* @__PURE__ */
|
|
60556
|
+
/* @__PURE__ */ jsxs(Box, { layerStyle: "card", p: "3.5", borderRadius: "lg", children: [
|
|
60557
|
+
/* @__PURE__ */ jsxs(Cap, { help: "dose_marginal", children: [
|
|
60558
|
+
"Extra ",
|
|
60559
|
+
vd.outcome,
|
|
60560
|
+
" per step"
|
|
60561
|
+
] }),
|
|
60562
|
+
/* @__PURE__ */ jsx$1(Box, { py: "0.5", maxH: "200px", overflowY: "auto", children: barList(vd.marginal.map((m2, i) => ({ label: m2.label, frac: m2.frac, tone: i < 2 ? "brand" : "muted", value: signed(m2.value) }))) })
|
|
60563
|
+
] })
|
|
59832
60564
|
] })
|
|
59833
|
-
] })
|
|
60565
|
+
] }),
|
|
60566
|
+
tab === "validate" && (design.status === "failed" && design.error ? /* @__PURE__ */ jsx$1(Box, { px: "4.5", pt: "4.5", children: /* @__PURE__ */ jsx$1(RunError, { error: design.error }) }) : designFresh && vmDesign ? /* @__PURE__ */ jsx$1(ValidatePanel, { vm: vmDesign, barList }) : /* @__PURE__ */ jsxs(Box, { p: "4.5", display: "inline-flex", alignItems: "center", gap: "2", color: "fg.muted", children: [
|
|
60567
|
+
/* @__PURE__ */ jsx$1(Spinner, { size: "xs", borderWidth: "1.5px", color: "brand.solid" }),
|
|
60568
|
+
/* @__PURE__ */ jsx$1(Text, { textStyle: "body.sm", children: "Sizing the trial that would confirm this…" })
|
|
60569
|
+
] }))
|
|
59834
60570
|
] })
|
|
59835
60571
|
] })
|
|
59836
60572
|
] }),
|
|
59837
60573
|
journal && journal.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
59838
60574
|
/* @__PURE__ */ jsxs(Box, { css: es.root, bg: "bg.canvas", borderTopWidth: "1px", borderColor: "border.subtle", children: [
|
|
59839
|
-
/* @__PURE__ */ jsx$1(Box, { css: es.lbl, children: "Committed experiments" }),
|
|
60575
|
+
/* @__PURE__ */ jsx$1(Box, { css: es.lbl, children: /* @__PURE__ */ jsx$1(Help, { id: "journal", children: "Committed experiments" }) }),
|
|
59840
60576
|
/* @__PURE__ */ jsxs(Box, { css: es.meta, children: [
|
|
59841
60577
|
/* @__PURE__ */ jsx$1(Text, { as: "span", color: "fg.default", fontWeight: "semibold", children: journal.length }),
|
|
59842
|
-
" on record"
|
|
60578
|
+
" on record",
|
|
60579
|
+
journal.length > 50 ? " · showing newest 50" : ""
|
|
59843
60580
|
] })
|
|
59844
60581
|
] }),
|
|
59845
|
-
journal.map((r2, i) => /* @__PURE__ */ jsxs(Box, { display: "grid", gridTemplateColumns: "2fr 1fr 1fr 1fr", gap: "3", alignItems: "center", px: "4.5", py: "2.5", borderTopWidth: "1px", borderColor: "border.subtle", children: [
|
|
60582
|
+
journal.slice(0, 50).map((r2, i) => /* @__PURE__ */ jsxs(Box, { display: "grid", gridTemplateColumns: "2fr 1fr 1fr 1fr", gap: "3", alignItems: "center", px: "4.5", py: "2.5", borderTopWidth: "1px", borderColor: "border.subtle", children: [
|
|
59846
60583
|
/* @__PURE__ */ jsxs(Text, { textStyle: "body.sm", children: [
|
|
59847
60584
|
/* @__PURE__ */ jsxs(Text, { as: "span", fontWeight: "bold", children: [
|
|
59848
60585
|
r2.treatment,
|
|
@@ -59856,7 +60593,7 @@ const EastChakraExperiment = memo(function EastChakraExperiment2({ value }) {
|
|
|
59856
60593
|
] })
|
|
59857
60594
|
] }),
|
|
59858
60595
|
/* @__PURE__ */ jsx$1(Text, { textStyle: "mono.sm", fontWeight: "semibold", textAlign: "right", fontVariantNumeric: "tabular-nums", color: r2.verdictTone === "pos" ? "fg.success" : "fg.default", children: r2.effect }),
|
|
59859
|
-
/* @__PURE__ */ jsx$1(Text, { textStyle: "caption.eyebrow", textAlign: "right", color: r2.verdictTone
|
|
60596
|
+
/* @__PURE__ */ jsx$1(Text, { textStyle: "caption.eyebrow", textAlign: "right", color: toneToken(r2.verdictTone), children: r2.verdict }),
|
|
59860
60597
|
/* @__PURE__ */ jsxs(Text, { textStyle: "mono.sm", textAlign: "right", color: "fg.muted", children: [
|
|
59861
60598
|
r2.who,
|
|
59862
60599
|
" · ",
|
|
@@ -59864,8 +60601,180 @@ const EastChakraExperiment = memo(function EastChakraExperiment2({ value }) {
|
|
|
59864
60601
|
] })
|
|
59865
60602
|
] }, i))
|
|
59866
60603
|
] })
|
|
60604
|
+
] }) });
|
|
60605
|
+
}, (prev2, next2) => experimentValueEqual(prev2.value, next2.value) && prev2.storageKey === next2.storageKey);
|
|
60606
|
+
function AnswerNumeric({ a: a2, verdict, higherBetter, badge, barList }) {
|
|
60607
|
+
const dirUp = a2.effect > 0;
|
|
60608
|
+
const statusWord = higherBetter === void 0 ? dirUp ? "Higher" : "Lower" : dirUp === higherBetter ? "Better" : "Worse";
|
|
60609
|
+
const lowerWord = a2.naive < 0 ? "lower" : "higher";
|
|
60610
|
+
const top = a2.balance[0] ?? { col: "", display: "" };
|
|
60611
|
+
const kpiColor = a2.clear && !a2.cautious ? "fg.success" : "fg.warning";
|
|
60612
|
+
const badgeOk = a2.clear && !a2.cautious;
|
|
60613
|
+
const badgeText = a2.cautious ? (verdict == null ? void 0 : verdict.label) ?? "Not trustworthy yet" : a2.clear ? `${statusWord} with ${a2.treatment}` : "No clear effect";
|
|
60614
|
+
return /* @__PURE__ */ jsxs(Box, { p: "4.5", children: [
|
|
60615
|
+
/* @__PURE__ */ jsxs(Box, { display: "flex", alignItems: "center", gap: "4.5", flexWrap: "wrap", children: [
|
|
60616
|
+
/* @__PURE__ */ jsxs(Box, { display: "flex", flexDirection: "column", gap: "0.5", children: [
|
|
60617
|
+
/* @__PURE__ */ jsx$1(Text, { textStyle: "mono.sm", color: "fg.muted", children: a2.outcome }),
|
|
60618
|
+
/* @__PURE__ */ jsxs(Box, { display: "flex", alignItems: "baseline", gap: "2.5", children: [
|
|
60619
|
+
/* @__PURE__ */ jsx$1(Text, { textStyle: "mono-kpi", fontFamily: "heading", fontSize: "32px", color: kpiColor, children: /* @__PURE__ */ jsx$1(Help, { id: "answer_effect", children: signed(a2.effect) }) }),
|
|
60620
|
+
/* @__PURE__ */ jsx$1(Text, { textStyle: "mono.sm", color: "fg.muted", children: /* @__PURE__ */ jsxs(Help, { id: "answer_ci", children: [
|
|
60621
|
+
"95% CI ",
|
|
60622
|
+
signed(a2.lo),
|
|
60623
|
+
" … ",
|
|
60624
|
+
signed(a2.hi)
|
|
60625
|
+
] }) })
|
|
60626
|
+
] })
|
|
60627
|
+
] }),
|
|
60628
|
+
/* @__PURE__ */ jsxs(Box, { as: "span", css: badge({ variant: badgeOk ? "ok" : "warn", size: "md" }), alignSelf: "flex-end", mb: "1", display: "inline-flex", alignItems: "center", gap: "1", children: [
|
|
60629
|
+
a2.clear && !a2.cautious && /* @__PURE__ */ jsx$1(FontAwesomeIcon, { icon: dirUp ? faArrowUp : faArrowDown, style: { fontSize: "10px" } }),
|
|
60630
|
+
/* @__PURE__ */ jsx$1(Help, { id: `verdict_${(verdict == null ? void 0 : verdict.tag) ?? "modest"}`, children: badgeText })
|
|
60631
|
+
] })
|
|
60632
|
+
] }),
|
|
60633
|
+
a2.cautious && /* @__PURE__ */ jsxs(Box, { layerStyle: "banner.stale", display: "flex", alignItems: "flex-start", gap: "2", mt: "3", children: [
|
|
60634
|
+
/* @__PURE__ */ jsx$1(Box, { as: "span", color: "fg.warning", flexShrink: "0", mt: "0.5", fontSize: "12px", children: /* @__PURE__ */ jsx$1(FontAwesomeIcon, { icon: faTriangleExclamation }) }),
|
|
60635
|
+
/* @__PURE__ */ jsxs(Text, { textStyle: "body.sm", color: "fg.default", children: [
|
|
60636
|
+
/* @__PURE__ */ jsx$1(Help, { id: "answer_cautious", children: /* @__PURE__ */ jsx$1(Text, { as: "span", fontWeight: "bold", children: "Treat this as provisional." }) }),
|
|
60637
|
+
" We adjusted and got a number, but a robustness check failed — the estimate may still be driven by something we didn’t adjust for. See ",
|
|
60638
|
+
/* @__PURE__ */ jsx$1(Text, { as: "span", fontWeight: "semibold", children: "Can we trust it?" })
|
|
60639
|
+
] })
|
|
60640
|
+
] }),
|
|
60641
|
+
a2.flip && /* @__PURE__ */ jsxs(Box, { layerStyle: "banner.stale", display: "flex", alignItems: "flex-start", gap: "2", mt: "3", children: [
|
|
60642
|
+
/* @__PURE__ */ jsx$1(Box, { as: "span", color: "fg.warning", flexShrink: "0", mt: "0.5", fontSize: "12px", children: /* @__PURE__ */ jsx$1(FontAwesomeIcon, { icon: faTriangleExclamation }) }),
|
|
60643
|
+
/* @__PURE__ */ jsxs(Text, { textStyle: "body.sm", color: "fg.default", children: [
|
|
60644
|
+
/* @__PURE__ */ jsx$1(Help, { id: "answer_flip", children: /* @__PURE__ */ jsx$1(Text, { as: "span", fontWeight: "bold", children: "Raw and like-for-like disagree." }) }),
|
|
60645
|
+
" In the plain average, the ",
|
|
60646
|
+
/* @__PURE__ */ jsx$1(Text, { as: "span", fontFamily: "mono", children: a2.treatment }),
|
|
60647
|
+
" group sits ",
|
|
60648
|
+
/* @__PURE__ */ jsx$1(Text, { as: "span", fontStyle: "italic", children: lowerWord }),
|
|
60649
|
+
" on ",
|
|
60650
|
+
/* @__PURE__ */ jsx$1(Text, { as: "span", fontFamily: "mono", children: a2.outcome }),
|
|
60651
|
+
" (",
|
|
60652
|
+
signed(a2.naive),
|
|
60653
|
+
") — but they also differ most on ",
|
|
60654
|
+
/* @__PURE__ */ jsx$1(Text, { as: "span", fontFamily: "mono", children: top.col }),
|
|
60655
|
+
" (",
|
|
60656
|
+
top.display,
|
|
60657
|
+
"). Adjusting for it reverses the result."
|
|
60658
|
+
] })
|
|
60659
|
+
] }),
|
|
60660
|
+
/* @__PURE__ */ jsxs(Card, { children: [
|
|
60661
|
+
/* @__PURE__ */ jsx$1(Cap, { help: "forest_plot", children: "Raw average vs. like-for-like" }),
|
|
60662
|
+
/* @__PURE__ */ jsx$1(
|
|
60663
|
+
ForestPlot,
|
|
60664
|
+
{
|
|
60665
|
+
rows: [
|
|
60666
|
+
{ label: "Raw average", note: "unadjusted", est: a2.naive, lo: a2.naiveLo, hi: a2.naiveHi, tone: a2.naive < 0 ? "neg" : "pos" },
|
|
60667
|
+
{ label: "Like-for-like", note: "adjusted", est: a2.effect, lo: a2.lo, hi: a2.hi, tone: a2.clear && !a2.cautious ? "pos" : "warn" }
|
|
60668
|
+
],
|
|
60669
|
+
min: Math.floor(Math.min(0, a2.naiveLo, a2.lo) - 2),
|
|
60670
|
+
max: Math.ceil(Math.max(0, a2.naiveHi, a2.hi) + 2),
|
|
60671
|
+
unit: `change in ${a2.outcome}${a2.unit ? ` (${a2.unit})` : ""}`,
|
|
60672
|
+
height: 150,
|
|
60673
|
+
rowHelp: ["forest_naive", "forest_adjusted"]
|
|
60674
|
+
}
|
|
60675
|
+
)
|
|
60676
|
+
] }),
|
|
60677
|
+
/* @__PURE__ */ jsxs(Card, { children: [
|
|
60678
|
+
/* @__PURE__ */ jsx$1(Cap, { help: "balance", children: "How unbalanced each one was — before adjusting" }),
|
|
60679
|
+
/* @__PURE__ */ jsx$1(Box, { py: "0.5", maxH: "208px", overflowY: "auto", children: barList(a2.balance.map((b2) => ({ label: b2.col, frac: b2.frac, tone: b2.tone, value: b2.display }))) })
|
|
60680
|
+
] }),
|
|
60681
|
+
/* @__PURE__ */ jsx$1(Box, { mt: "3.5", children: /* @__PURE__ */ jsx$1(Help, { id: "counts", display: "inline-flex", gap: "4", children: [[a2.nTotal, SUBJECT_MANY], [a2.nCompared, "compared like-for-like"], [a2.nDropped, "had no fair match"]].map(([n2, label], i) => /* @__PURE__ */ jsxs(Text, { textStyle: "mono.sm", color: "fg.muted", children: [
|
|
60682
|
+
/* @__PURE__ */ jsx$1(Text, { as: "span", color: "fg.default", fontWeight: "semibold", children: Number(n2) }),
|
|
60683
|
+
" ",
|
|
60684
|
+
label
|
|
60685
|
+
] }, i)) }) })
|
|
59867
60686
|
] });
|
|
59868
|
-
}
|
|
60687
|
+
}
|
|
60688
|
+
function RefusalZone({ refusal, overlap, naiveValue, outcome }) {
|
|
60689
|
+
return /* @__PURE__ */ jsxs(Box, { p: "4.5", children: [
|
|
60690
|
+
/* @__PURE__ */ jsxs(Box, { display: "inline-flex", alignItems: "center", gap: "2", mb: "2", color: "fg.warning", children: [
|
|
60691
|
+
/* @__PURE__ */ jsx$1(FontAwesomeIcon, { icon: faTriangleExclamation, style: { fontSize: "14px" } }),
|
|
60692
|
+
/* @__PURE__ */ jsx$1(Text, { textStyle: "title.card", color: "fg.default", children: /* @__PURE__ */ jsx$1(Help, { id: refusal.kind === "positivity" ? "refusal_positivity" : "refusal_not_estimable", children: refusal.title }) })
|
|
60693
|
+
] }),
|
|
60694
|
+
/* @__PURE__ */ jsx$1(Text, { textStyle: "body.sm", color: "fg.muted", mb: "3", lineHeight: "1.5", children: refusal.body }),
|
|
60695
|
+
refusal.kind === "positivity" && overlap && /* @__PURE__ */ jsxs(Card, { mt: "1", children: [
|
|
60696
|
+
/* @__PURE__ */ jsxs(Cap, { help: "overlap_histogram", children: [
|
|
60697
|
+
"Propensity overlap — ",
|
|
60698
|
+
overlap.supportLabel
|
|
60699
|
+
] }),
|
|
60700
|
+
/* @__PURE__ */ jsx$1(OverlapHistogram, { treated: overlap.treated, control: overlap.control, supportLabel: overlap.supportLabel, positivityOk: overlap.positivityOk, height: 170 })
|
|
60701
|
+
] }),
|
|
60702
|
+
/* @__PURE__ */ jsx$1(Box, { display: "flex", gap: "4", mt: "3.5", children: refusal.evidence.map((e3, i) => /* @__PURE__ */ jsxs(Text, { textStyle: "mono.sm", color: "fg.muted", children: [
|
|
60703
|
+
/* @__PURE__ */ jsx$1(Text, { as: "span", color: "fg.default", fontWeight: "semibold", children: e3.value }),
|
|
60704
|
+
" ",
|
|
60705
|
+
e3.label
|
|
60706
|
+
] }, i)) }),
|
|
60707
|
+
/* @__PURE__ */ jsxs(Text, { textStyle: "caption", color: "fg.subtle", mt: "3.5", pt: "3", borderTopWidth: "1px", borderColor: "border.subtle", children: [
|
|
60708
|
+
"Raw average difference in ",
|
|
60709
|
+
outcome,
|
|
60710
|
+
" (context only, not an answer): ",
|
|
60711
|
+
/* @__PURE__ */ jsx$1(Text, { as: "span", fontFamily: "mono", color: "fg.muted", children: signed(naiveValue) })
|
|
60712
|
+
] })
|
|
60713
|
+
] });
|
|
60714
|
+
}
|
|
60715
|
+
function ValidatePanel({ vm, barList }) {
|
|
60716
|
+
const treatedPct = Math.round(vm.primary.treatedShare * 100);
|
|
60717
|
+
return /* @__PURE__ */ jsxs(Box, { p: "4.5", children: [
|
|
60718
|
+
/* @__PURE__ */ jsx$1(Cap, { help: "tab_validate", children: vm.headline }),
|
|
60719
|
+
/* @__PURE__ */ jsxs(Box, { display: "flex", alignItems: "flex-end", gap: "5", flexWrap: "wrap", children: [
|
|
60720
|
+
/* @__PURE__ */ jsxs(Box, { display: "flex", flexDirection: "column", gap: "0.5", children: [
|
|
60721
|
+
/* @__PURE__ */ jsx$1(Text, { textStyle: "mono.sm", color: "fg.muted", children: /* @__PURE__ */ jsx$1(Help, { id: "validate_size", children: vm.holdback ? "to hold back from" : "to run" }) }),
|
|
60722
|
+
vm.faint ? /* @__PURE__ */ jsx$1(Text, { textStyle: "body.sm", fontWeight: "semibold", color: "fg.warning", maxW: "220px", children: "Effect too faint to size — set a materiality threshold to size a trial." }) : /* @__PURE__ */ jsx$1(Text, { textStyle: "mono-kpi", fontFamily: "heading", fontSize: "32px", color: "brand.solid", children: vm.primary.nTotal.toLocaleString() })
|
|
60723
|
+
] }),
|
|
60724
|
+
/* @__PURE__ */ jsxs(Box, { flex: "1", minW: "220px", children: [
|
|
60725
|
+
/* @__PURE__ */ jsx$1(Cap, { help: "validate_split", children: vm.holdback ? "Hold-back split" : "Split" }),
|
|
60726
|
+
/* @__PURE__ */ jsxs(Box, { display: "flex", h: "10px", borderRadius: "full", overflow: "hidden", borderWidth: "1px", borderColor: "border.subtle", children: [
|
|
60727
|
+
/* @__PURE__ */ jsx$1(Box, { bg: "brand.solid", width: `${treatedPct}%` }),
|
|
60728
|
+
/* @__PURE__ */ jsx$1(Box, { bg: "bg.emphasized", flex: "1" })
|
|
60729
|
+
] }),
|
|
60730
|
+
/* @__PURE__ */ jsxs(Box, { display: "flex", justifyContent: "space-between", mt: "1.5", children: [
|
|
60731
|
+
/* @__PURE__ */ jsxs(Text, { textStyle: "caption", children: [
|
|
60732
|
+
/* @__PURE__ */ jsx$1(Text, { as: "span", color: "brand.fg", fontWeight: "semibold", children: vm.primary.nTreated.toLocaleString() }),
|
|
60733
|
+
" ",
|
|
60734
|
+
vm.holdback ? "treated" : "get it"
|
|
60735
|
+
] }),
|
|
60736
|
+
/* @__PURE__ */ jsxs(Text, { textStyle: "caption", children: [
|
|
60737
|
+
/* @__PURE__ */ jsx$1(Text, { as: "span", color: "fg.default", fontWeight: "semibold", children: vm.primary.nControl.toLocaleString() }),
|
|
60738
|
+
" ",
|
|
60739
|
+
vm.holdback ? "held back" : "left alone"
|
|
60740
|
+
] })
|
|
60741
|
+
] })
|
|
60742
|
+
] })
|
|
60743
|
+
] }),
|
|
60744
|
+
vm.matchOn.length > 0 && /* @__PURE__ */ jsxs(Card, { children: [
|
|
60745
|
+
/* @__PURE__ */ jsx$1(Cap, { help: "validate_match", children: "Match both groups on" }),
|
|
60746
|
+
/* @__PURE__ */ jsx$1(Box, { py: "0.5", children: barList(vm.matchOn.map((m2) => ({ label: m2.display, frac: m2.frac, tone: m2.tone, value: "" }))) })
|
|
60747
|
+
] }),
|
|
60748
|
+
vm.curve.mid.length > 0 && /* @__PURE__ */ jsxs(Card, { children: [
|
|
60749
|
+
/* @__PURE__ */ jsx$1(Cap, { help: "validate_power", children: "Chance of detecting it" }),
|
|
60750
|
+
/* @__PURE__ */ jsx$1(
|
|
60751
|
+
AreaRange,
|
|
60752
|
+
{
|
|
60753
|
+
lo: vm.curve.mid,
|
|
60754
|
+
mid: vm.curve.mid,
|
|
60755
|
+
hi: vm.curve.mid,
|
|
60756
|
+
tone: "brand",
|
|
60757
|
+
xTicks: vm.curve.xTicks,
|
|
60758
|
+
yTicks: ["100", "50", "0"],
|
|
60759
|
+
yFormat: "percent",
|
|
60760
|
+
marks: vm.curve.marks.map((m2) => ({ at: m2.at, label: m2.label, tone: m2.tone, help: m2.help })),
|
|
60761
|
+
height: 170
|
|
60762
|
+
}
|
|
60763
|
+
)
|
|
60764
|
+
] }),
|
|
60765
|
+
/* @__PURE__ */ jsx$1(Text, { textStyle: "body.sm", color: "fg.default", lineHeight: "1.5", mt: "3.5", children: vm.rationale }),
|
|
60766
|
+
vm.alternates.length > 0 && /* @__PURE__ */ jsx$1(Box, { display: "flex", flexDirection: "column", gap: "1.5", mt: "3", pt: "3", borderTopWidth: "1px", borderColor: "border.subtle", children: vm.alternates.map((o2, i) => /* @__PURE__ */ jsxs(Text, { textStyle: "caption", color: "fg.muted", children: [
|
|
60767
|
+
/* @__PURE__ */ jsx$1(Text, { as: "span", fontWeight: "semibold", color: "fg.default", children: o2.label }),
|
|
60768
|
+
" · ",
|
|
60769
|
+
o2.nTotal.toLocaleString(),
|
|
60770
|
+
" total (",
|
|
60771
|
+
o2.nTreated.toLocaleString(),
|
|
60772
|
+
" / ",
|
|
60773
|
+
o2.nControl.toLocaleString(),
|
|
60774
|
+
")"
|
|
60775
|
+
] }, i)) })
|
|
60776
|
+
] });
|
|
60777
|
+
}
|
|
59869
60778
|
function FilterRail({ fields, population, onChange: onChange2, chip, button }) {
|
|
59870
60779
|
const [open, setOpen] = useState(null);
|
|
59871
60780
|
const replaceAt = (i, p2) => onChange2(population.map((f2, j2) => j2 === i ? p2 : f2));
|
|
@@ -59916,11 +60825,11 @@ function FilterRail({ fields, population, onChange: onChange2, chip, button }) {
|
|
|
59916
60825
|
)
|
|
59917
60826
|
] });
|
|
59918
60827
|
}
|
|
59919
|
-
function Step({ n: n2, title, children: children2 }) {
|
|
60828
|
+
function Step({ n: n2, title, help, children: children2 }) {
|
|
59920
60829
|
return /* @__PURE__ */ jsxs(Box, { px: "4.5", py: "3", borderBottomWidth: "1px", borderColor: "border.subtle", children: [
|
|
59921
|
-
/* @__PURE__ */ jsxs(Box, { display: "flex", alignItems: "baseline", gap: "2", mb: "2", children: [
|
|
59922
|
-
/* @__PURE__ */ jsx$1(Box, { as: "span", fontFamily: "mono", fontSize: "
|
|
59923
|
-
/* @__PURE__ */ jsx$1(Text, { textStyle: "title.row", children: title })
|
|
60830
|
+
/* @__PURE__ */ jsxs(Box, { display: "flex", alignItems: "baseline", gap: "2.5", mb: "2", children: [
|
|
60831
|
+
/* @__PURE__ */ jsx$1(Box, { as: "span", fontFamily: "mono", fontSize: "11px", fontWeight: "bold", color: "brand.fg", flex: "0 0 auto", lineHeight: "1", children: n2 }),
|
|
60832
|
+
/* @__PURE__ */ jsx$1(Text, { textStyle: "title.row", children: help ? /* @__PURE__ */ jsx$1(Help, { id: help, children: title }) : title })
|
|
59924
60833
|
] }),
|
|
59925
60834
|
children2
|
|
59926
60835
|
] });
|
|
@@ -59938,13 +60847,45 @@ function ColumnPick({ column: column2, kind, choices, onPick, badge, button }) {
|
|
|
59938
60847
|
/* @__PURE__ */ jsx$1(Box, { as: "span", display: "inline-flex", color: "fg.muted", fontSize: "10px", children: /* @__PURE__ */ jsx$1(FontAwesomeIcon, { icon: faChevronDown }) })
|
|
59939
60848
|
] }) });
|
|
59940
60849
|
}
|
|
59941
|
-
function SegmentSelect({ options: options2, active, onPick, fill, size: size3 = "
|
|
60850
|
+
function SegmentSelect({ options: options2, active, onPick, fill, size: size3 = "xs" }) {
|
|
59942
60851
|
const s2 = useSlotRecipe({ key: "segmentGroup" })({ size: size3 });
|
|
59943
|
-
return /* @__PURE__ */ jsx$1(
|
|
60852
|
+
return /* @__PURE__ */ jsx$1(
|
|
60853
|
+
Box,
|
|
60854
|
+
{
|
|
60855
|
+
css: s2.root,
|
|
60856
|
+
...fill ? { width: "100%" } : {},
|
|
60857
|
+
role: "radiogroup",
|
|
60858
|
+
onKeyDown: (e3) => {
|
|
60859
|
+
var _a2;
|
|
60860
|
+
const d2 = e3.key === "ArrowRight" || e3.key === "ArrowDown" ? 1 : e3.key === "ArrowLeft" || e3.key === "ArrowUp" ? -1 : 0;
|
|
60861
|
+
if (!d2) return;
|
|
60862
|
+
e3.preventDefault();
|
|
60863
|
+
const next2 = (active + d2 + options2.length) % options2.length;
|
|
60864
|
+
onPick(next2);
|
|
60865
|
+
const btns = e3.currentTarget.querySelectorAll('[role="radio"]');
|
|
60866
|
+
(_a2 = btns[next2]) == null ? void 0 : _a2.focus();
|
|
60867
|
+
},
|
|
60868
|
+
children: options2.map((o2, i) => /* @__PURE__ */ jsx$1(
|
|
60869
|
+
Box,
|
|
60870
|
+
{
|
|
60871
|
+
as: "button",
|
|
60872
|
+
role: "radio",
|
|
60873
|
+
"aria-checked": active === i,
|
|
60874
|
+
tabIndex: active === i ? 0 : -1,
|
|
60875
|
+
css: s2.item,
|
|
60876
|
+
...fill ? { flex: "1" } : {},
|
|
60877
|
+
...active === i ? { "data-state": "checked" } : {},
|
|
60878
|
+
onClick: () => onPick(i),
|
|
60879
|
+
children: /* @__PURE__ */ jsx$1(Box, { as: "span", css: s2.itemText, children: o2 })
|
|
60880
|
+
},
|
|
60881
|
+
i
|
|
60882
|
+
))
|
|
60883
|
+
}
|
|
60884
|
+
);
|
|
59944
60885
|
}
|
|
59945
|
-
function Segmented({ label, left, right, active, onPick, last: last2 }) {
|
|
60886
|
+
function Segmented({ label, left, right, active, onPick, last: last2, help }) {
|
|
59946
60887
|
return /* @__PURE__ */ jsxs(Box, { display: "flex", flexDirection: "column", alignItems: "stretch", gap: "1.5", py: "2", borderBottomWidth: last2 ? "0" : "1px", borderColor: "border.subtle", children: [
|
|
59947
|
-
/* @__PURE__ */ jsx$1(Text, { textStyle: "caption.eyebrow", textTransform: "none", letterSpacing: "normal", color: "fg.default", children: label }),
|
|
60888
|
+
/* @__PURE__ */ jsx$1(Text, { textStyle: "caption.eyebrow", textTransform: "none", letterSpacing: "normal", color: "fg.default", children: help ? /* @__PURE__ */ jsx$1(Help, { id: help, children: label }) : label }),
|
|
59948
60889
|
/* @__PURE__ */ jsx$1(SegmentSelect, { options: [left, right], active: active === "left" ? 0 : 1, onPick: (i) => onPick(i === 0 ? "left" : "right"), fill: true, size: "xs" })
|
|
59949
60890
|
] });
|
|
59950
60891
|
}
|
|
@@ -61646,10 +62587,10 @@ function formatPrimitive(type, value) {
|
|
|
61646
62587
|
case "Integer":
|
|
61647
62588
|
return String(value);
|
|
61648
62589
|
case "Float": {
|
|
61649
|
-
const
|
|
61650
|
-
if (Number.isNaN(
|
|
61651
|
-
if (!Number.isFinite(
|
|
61652
|
-
return String(
|
|
62590
|
+
const num2 = value;
|
|
62591
|
+
if (Number.isNaN(num2)) return "NaN";
|
|
62592
|
+
if (!Number.isFinite(num2)) return num2 > 0 ? "Infinity" : "-Infinity";
|
|
62593
|
+
return String(num2);
|
|
61653
62594
|
}
|
|
61654
62595
|
case "String":
|
|
61655
62596
|
return `"${value}"`;
|