diff --git a/frameworks/vue/notes.json b/frameworks/vue/notes.json new file mode 100644 index 0000000..4182a7c --- /dev/null +++ b/frameworks/vue/notes.json @@ -0,0 +1,3 @@ +{ + "variant": "Vapor" +} diff --git a/results/app/components/variant.gts b/results/app/components/variant.gts new file mode 100644 index 0000000..7825342 --- /dev/null +++ b/results/app/components/variant.gts @@ -0,0 +1,14 @@ +import type { TOC } from "@ember/component/template-only"; + +/** + * A framework's variant label (e.g. "Vapor"), shown under the framework + * name and above its version. Renders nothing when the run recorded no + * variant for the framework. + */ +export const Variant = satisfies TOC<{ + variant: string | undefined; +}>; diff --git a/results/app/routes/compare.ts b/results/app/routes/compare.ts index 8610b88..ecf01df 100644 --- a/results/app/routes/compare.ts +++ b/results/app/routes/compare.ts @@ -1,7 +1,7 @@ import Route from "@ember/routing/route"; import { service } from "@ember/service"; -import { results } from "virtual:result-sets"; +import { experiments, runs } from "virtual:result-sets"; import { warnOnVersionDivergence } from "#utils"; @@ -75,20 +75,20 @@ export default class Compare extends Route { } /** - * The run next to `name`, preferring `direction` -- `results` is + * The run next to `name`, preferring `direction` -- `runs` is * newest-first, so older runs are later in the list. Falls back to the * other side for the first and last run, and to `name` itself when it's * the only run there is. */ function neighborOf(name: string, direction: "older" | "newer") { - const index = results.indexOf(name); + const index = runs.indexOf(name); - if (index === -1) return results[0]; + if (index === -1) return runs[0]; const [preferred, fallback] = direction === "older" ? [index + 1, index - 1] : [index - 1, index + 1]; - return results[preferred] ?? results[fallback] ?? name; + return runs[preferred] ?? runs[fallback] ?? name; } /** @@ -103,11 +103,13 @@ function runsFor(a: string | undefined, b: string | undefined) { if (a) return { a, b: neighborOf(a, "newer") }; // no runs named at all -- the two most recent - return { a: results[1] ?? results[0], b: results[0] }; + return { a: runs[1] ?? runs[0], b: runs[0] }; } async function fetchResultSet(name: string): Promise { - const response = await fetch(`/results/${name}.json`); + // experiments live in a separate directory from the official runs + const dir = experiments.includes(name) ? "experiments" : "results"; + const response = await fetch(`/${dir}/${name}.json`); // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment const json = await response.json(); diff --git a/results/app/routes/index.ts b/results/app/routes/index.ts index d0bb42d..c068b47 100644 --- a/results/app/routes/index.ts +++ b/results/app/routes/index.ts @@ -1,7 +1,7 @@ import Route from "@ember/routing/route"; import { service } from "@ember/service"; -import { results } from "virtual:result-sets"; +import { runs } from "virtual:result-sets"; import type RouterService from "@ember/routing/router-service"; @@ -11,7 +11,7 @@ export default class Index extends Route { beforeModel() { this.router.transitionTo("results", { queryParams: { - q: results[0], + q: runs[0], }, }); } diff --git a/results/app/routes/results.ts b/results/app/routes/results.ts index 59ebd58..6b453cc 100644 --- a/results/app/routes/results.ts +++ b/results/app/routes/results.ts @@ -1,6 +1,8 @@ import Route from "@ember/routing/route"; import { service } from "@ember/service"; +import { experiments } from "virtual:result-sets"; + import { warnOnVersionDivergence } from "#utils"; import type RouterService from "@ember/routing/router-service"; @@ -49,7 +51,9 @@ export default class Results extends Route { const { q } = params as unknown as Params; try { - const response = await fetch(`/results/${q}.json`); + // experiments live in a separate directory from the official runs + const dir = experiments.includes(q) ? "experiments" : "results"; + const response = await fetch(`/${dir}/${q}.json`); // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment const json = await response.json(); diff --git a/results/app/styles/app.css b/results/app/styles/app.css index ed7ae83..9d7d4cb 100644 --- a/results/app/styles/app.css +++ b/results/app/styles/app.css @@ -65,6 +65,16 @@ h1 { font-size: 0.8rem; } +/* a framework's build flavor (e.g. "Vapor"), sitting between the name and + the version -- a block so it lands on its own line under the name */ +.variant { + display: block; + text-align: center; + font-size: 0.8rem; + font-weight: 600; + opacity: 0.75; +} + body { display: grid; gap: 0.5rem; @@ -114,6 +124,7 @@ body { } main { + min-width: 500px; display: grid; justify-items: center; } diff --git a/results/app/templates/compare.gts b/results/app/templates/compare.gts index 6d1616b..1ad065a 100644 --- a/results/app/templates/compare.gts +++ b/results/app/templates/compare.gts @@ -4,10 +4,11 @@ import { LinkTo } from "@ember/routing"; import { service } from "@ember/service"; import { pageTitle } from "ember-page-title"; -import { results } from "virtual:result-sets"; +import { experiments, runs } from "virtual:result-sets"; import { BenchmarkName } from "#components/benchmark-name.gts"; import { FrameworkInfo } from "#components/framework-info.gts"; +import { Variant } from "#components/variant.gts"; import { Version } from "#components/version.gts"; import { nameOf } from "#frameworks"; import { @@ -21,9 +22,11 @@ import { round, throttleLabel, timeFor, + variantOf, versionOf, } from "#utils"; +import type { TOC } from "@ember/component/template-only"; import type RouterService from "@ember/routing/router-service"; import type { Model, NamedRun } from "#routes/compare.ts"; import type { BenchmarkInfo, ResultSet } from "#types"; @@ -43,6 +46,29 @@ function qp(runName: string) { return { q: runName }; } +/** + * The options for one of the A/B run selectors: the official runs, plus + * the experiments in their own group when there are any. Either side can + * point at either category, so a run can be compared against an experiment. + */ +const RunOptions = satisfies TOC<{ + which: "a" | "b"; + isRun: (which: "a" | "b", name: string) => boolean; +}>; + /** * Both runs state their throttle outright rather than leaving it to be * inferred from the warning that only shows when they disagree. @@ -303,6 +329,16 @@ export default class Compare extends Component<{ model: Model }> { return lowerIsBetterBenches(this.benchmarkInfo); } + /** + * The variant either run recorded for the compared framework, preferring + * the candidate (B). Both runs are usually the same build, so this reads + * "what flavor of the framework am I looking at" rather than a per-run + * difference. + */ + get variant() { + return variantOf(this.b.data, this.framework) ?? variantOf(this.a.data, this.framework); + } + isFramework = (name: string) => this.framework === name; isRun = (which: "a" | "b", name: string) => this[which].name === name; @@ -337,17 +373,13 @@ export default class Compare extends Component<{ model: Model }> {