repo-dive 0.14.0 → 0.15.1
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/cli.js +225 -59
- package/dist/config.d.ts +14 -0
- package/dist/config.js +15 -0
- package/dist/dashboard/assets/index-1G4_D1Zz.css +2 -0
- package/dist/dashboard/assets/index-CRLzUhgz.js +10 -0
- package/dist/dashboard/index.html +2 -2
- package/package.json +4 -4
- package/dist/dashboard/assets/index-BKx1tskf.css +0 -2
- package/dist/dashboard/assets/index-Ds8-JhLY.js +0 -9
package/dist/cli.js
CHANGED
|
@@ -42694,7 +42694,7 @@ var runWith = (command, config) => {
|
|
|
42694
42694
|
};
|
|
42695
42695
|
var package_default = {
|
|
42696
42696
|
name: "repo-dive",
|
|
42697
|
-
version: "0.
|
|
42697
|
+
version: "0.15.1",
|
|
42698
42698
|
description: "Dive into a git repo's history: per-commit snapshots, an indexed metrics catalog and an interactive dashboard",
|
|
42699
42699
|
keywords: [
|
|
42700
42700
|
"git",
|
|
@@ -42767,9 +42767,9 @@ var package_default = {
|
|
|
42767
42767
|
devDependencies: {
|
|
42768
42768
|
"@babel/core": "8.0.1",
|
|
42769
42769
|
"@base-ui/react": "1.7.0",
|
|
42770
|
-
"@changesets/changelog-github": "0.
|
|
42771
|
-
"@changesets/cli": "
|
|
42772
|
-
"@changesets/config": "
|
|
42770
|
+
"@changesets/changelog-github": "1.0.0",
|
|
42771
|
+
"@changesets/cli": "3.0.0",
|
|
42772
|
+
"@changesets/config": "4.0.0",
|
|
42773
42773
|
"@effect/platform-node": "4.0.0-beta.106",
|
|
42774
42774
|
"@effect/vitest": "4.0.0-beta.106",
|
|
42775
42775
|
"@kachkaev/eslint-config-base": "1.2.0",
|
|
@@ -50060,6 +50060,23 @@ var collectorsCommand = make$5("collectors").pipe(withDescription("List availabl
|
|
|
50060
50060
|
].join("\n"));
|
|
50061
50061
|
})));
|
|
50062
50062
|
//#endregion
|
|
50063
|
+
//#region src/config.ts
|
|
50064
|
+
/**
|
|
50065
|
+
* Stable identifiers of the dashboard's chart sections, in page order — the
|
|
50066
|
+
* keys {@link ChartsConfig.annotations} accepts.
|
|
50067
|
+
*/
|
|
50068
|
+
var chartIds = [
|
|
50069
|
+
"lines-of-code",
|
|
50070
|
+
"commit-calendar",
|
|
50071
|
+
"commits-per-month",
|
|
50072
|
+
"churn-per-month",
|
|
50073
|
+
"direct-dependencies",
|
|
50074
|
+
"dependencies",
|
|
50075
|
+
"loose-ends",
|
|
50076
|
+
"most-suppressed-eslint-rules",
|
|
50077
|
+
"contributors"
|
|
50078
|
+
];
|
|
50079
|
+
//#endregion
|
|
50063
50080
|
//#region src/cli/shared/config.ts
|
|
50064
50081
|
/** "12345+alice@users.noreply.github.com" → "alice"; other emails unchanged. */
|
|
50065
50082
|
var prettifyAuthorEmail = (email) => {
|
|
@@ -50104,12 +50121,28 @@ var botPattern = /\brenovate\b|\bdependabot\b|github-actions|\[bot\]/i;
|
|
|
50104
50121
|
* those letters, like `Kate Talbot`, stay human.
|
|
50105
50122
|
*/
|
|
50106
50123
|
var botNameSuffixPattern = /[\s-]bot$/i;
|
|
50107
|
-
/**
|
|
50108
|
-
|
|
50124
|
+
/**
|
|
50125
|
+
* Names AI coding agents sign with, matched against the display name as whole
|
|
50126
|
+
* words so a human name that merely contains one stays human — `Sam Devine`
|
|
50127
|
+
* is not Devin, `Kai Haider` is not aider. `coderabbit` is anchored on
|
|
50128
|
+
* the left only because the agent signs as `coderabbitai`. Claude and Devin
|
|
50129
|
+
* are ordinary given names even as whole words, so those two only count with
|
|
50130
|
+
* the agent/model word agents append (`Claude Opus 4.5`, `Devin AI`).
|
|
50131
|
+
*/
|
|
50132
|
+
var aiNamePattern = /\bcopilot\b|\bcursor\b|\bchatgpt\b|\bcodex\b|\bopenai\b|\bgemini\b|\baider\b|\bcoderabbit|\bcodegen\b|\bsweep\b|\bwindsurf\b|\bclaude[\s.-]?(?:ai|code|opus|sonnet|haiku|fable|mythos|\d)\b|\bdevin[\s.-]?ai\b/i;
|
|
50133
|
+
/**
|
|
50134
|
+
* Mailboxes agents commit under — always agent-specific addresses, never a
|
|
50135
|
+
* bare vendor domain: `alice@openai.com` is an employee, not an agent, which
|
|
50136
|
+
* is why product names are never matched against the email side.
|
|
50137
|
+
*/
|
|
50138
|
+
var aiEmailPattern = /(?:claude|noreply)@anthropic\.com|(?:chatgpt|codex|noreply)@openai\.com/i;
|
|
50109
50139
|
/** `"Name <email>"` → `"Name"`; identities without an email part are used as-is. */
|
|
50110
50140
|
var identityName = (identity) => identity.replace(/\s*<[^<>]*>\s*$/, "").trim();
|
|
50111
50141
|
/** Classifies a `"Name <email>"` identity when the config leaves `kind` unset. */
|
|
50112
|
-
var deriveContributorKind = (identity) =>
|
|
50142
|
+
var deriveContributorKind = (identity) => {
|
|
50143
|
+
const name = identityName(identity);
|
|
50144
|
+
return botPattern.test(identity) || botNameSuffixPattern.test(name) ? "bot" : aiNamePattern.test(name) || aiEmailPattern.test(identity) ? "ai" : "human";
|
|
50145
|
+
};
|
|
50113
50146
|
/**
|
|
50114
50147
|
* Tidies an auto-derived name for display. The kind badge (🤖 / ✨) already
|
|
50115
50148
|
* marks bots and AI agents, so the `[bot]` suffix GitHub bakes into author names
|
|
@@ -50211,6 +50244,18 @@ var parseWeekStartsOn = (value) => {
|
|
|
50211
50244
|
for (const weekStart of weekStarts) if (value === weekStart) return weekStart;
|
|
50212
50245
|
throw configError("`charts.weekStartsOn` must be \"monday\" or \"sunday\".");
|
|
50213
50246
|
};
|
|
50247
|
+
var parseChartAnnotations = (value) => {
|
|
50248
|
+
if (value === void 0) return {};
|
|
50249
|
+
if (!isPlainObject(value)) throw configError("`charts.annotations` must be an object.");
|
|
50250
|
+
const annotations = {};
|
|
50251
|
+
for (const [key, markdown] of Object.entries(value)) {
|
|
50252
|
+
const chartId = chartIds.find((id) => id === key);
|
|
50253
|
+
if (chartId === void 0) throw configError(`\`charts.annotations\` has an unknown chart id ${JSON.stringify(key)}; known ids: ${chartIds.join(", ")}.`);
|
|
50254
|
+
if (typeof markdown !== "string" || markdown.trim() === "") throw configError(`\`charts.annotations.${chartId}\` must be a non-empty string.`);
|
|
50255
|
+
annotations[chartId] = markdown.trim();
|
|
50256
|
+
}
|
|
50257
|
+
return annotations;
|
|
50258
|
+
};
|
|
50214
50259
|
/**
|
|
50215
50260
|
* Resolves `catalog.dir` against the repository root. `gc` deletes whole
|
|
50216
50261
|
* subtrees under the result, so the two placements that would take the
|
|
@@ -50252,6 +50297,7 @@ var resolveConfig = (raw, repoRoot) => {
|
|
|
50252
50297
|
const charts = prop(raw, "charts");
|
|
50253
50298
|
if (charts !== void 0 && !isPlainObject(charts)) throw configError("`charts` must be an object.");
|
|
50254
50299
|
const weekStartsOn = parseWeekStartsOn(charts === void 0 ? void 0 : prop(charts, "weekStartsOn"));
|
|
50300
|
+
const chartAnnotations = parseChartAnnotations(charts === void 0 ? void 0 : prop(charts, "annotations"));
|
|
50255
50301
|
const catalog = prop(raw, "catalog");
|
|
50256
50302
|
if (catalog !== void 0 && !isPlainObject(catalog)) throw configError("`catalog` must be an object.");
|
|
50257
50303
|
const catalogPath = parseCatalogPath(catalog === void 0 ? void 0 : prop(catalog, "dir"), repoRoot);
|
|
@@ -50259,6 +50305,7 @@ var resolveConfig = (raw, repoRoot) => {
|
|
|
50259
50305
|
resolveContributor: (email, name) => bareResolveContributor(aliasMap, email, name),
|
|
50260
50306
|
maxInCharts,
|
|
50261
50307
|
weekStartsOn,
|
|
50308
|
+
chartAnnotations,
|
|
50262
50309
|
catalogPath,
|
|
50263
50310
|
catalogRelativePath: relativeCatalogPath(repoRoot, catalogPath),
|
|
50264
50311
|
checkIgnoreFiles: parseCheckIgnoreFiles(catalog === void 0 ? void 0 : prop(catalog, "checkIgnoreFiles"))
|
|
@@ -50726,7 +50773,7 @@ var warnAboutIgnoreFiles = ({ repoRoot, config }) => gen(function* () {
|
|
|
50726
50773
|
].join("\n"));
|
|
50727
50774
|
}).pipe(ignore$1);
|
|
50728
50775
|
//#endregion
|
|
50729
|
-
//#region src/cli/shared/worktree.ts
|
|
50776
|
+
//#region src/cli/shared/scan/with-temporary-worktree.ts
|
|
50730
50777
|
/**
|
|
50731
50778
|
* Materializes a commit into a detached temporary worktree, runs `use` and
|
|
50732
50779
|
* always cleans up. The user's own working tree is never touched: the checkout
|
|
@@ -50845,8 +50892,8 @@ var haveUnrelatedHistories = (repoRoot, left, right) => runGit([
|
|
|
50845
50892
|
right
|
|
50846
50893
|
], { okExitCodes: [1] }).pipe(map$7((stdout) => stdout.trim() === ""));
|
|
50847
50894
|
/**
|
|
50848
|
-
* The first-parent
|
|
50849
|
-
* founding graft, or
|
|
50895
|
+
* The first-parent chains that continue `chain` backwards in time across a
|
|
50896
|
+
* founding graft, or no chains when there is none.
|
|
50850
50897
|
*
|
|
50851
50898
|
* A repository migration (monorepo assembly, host move, history rewrite)
|
|
50852
50899
|
* leaves a recognizable signature: a fresh root commit followed immediately by
|
|
@@ -50861,53 +50908,107 @@ var haveUnrelatedHistories = (repoRoot, left, right) => runGit([
|
|
|
50861
50908
|
* history that overlaps the chain (e.g. a plugin repository absorbed while
|
|
50862
50909
|
* mainline development continued) does not qualify.
|
|
50863
50910
|
*
|
|
50864
|
-
*
|
|
50865
|
-
* eight)
|
|
50866
|
-
*
|
|
50911
|
+
* Every qualifying absorbed history is returned (effect's monorepo assembly
|
|
50912
|
+
* merged eight): before the migration they were the project's parallel parts,
|
|
50913
|
+
* so composed timelines sum all of them until the assembly replaces them.
|
|
50867
50914
|
*/
|
|
50868
|
-
var
|
|
50915
|
+
var findFoundingGraft = (repoRoot, chain, alreadyClaimed) => gen(function* () {
|
|
50869
50916
|
const root = chain.at(-1);
|
|
50870
|
-
if (root === void 0) return
|
|
50917
|
+
if (root === void 0) return {
|
|
50918
|
+
absorbed: [],
|
|
50919
|
+
assemblyShas: []
|
|
50920
|
+
};
|
|
50871
50921
|
const rootDate = Date.parse(root.committerDate);
|
|
50872
50922
|
const candidateTips = [];
|
|
50923
|
+
const assemblyShas = [root.hash];
|
|
50873
50924
|
for (let index = chain.length - 2; index >= 0; index -= 1) {
|
|
50874
50925
|
const entry = chain[index];
|
|
50875
50926
|
if (entry === void 0 || entry.parentHashes.length < 2) break;
|
|
50876
50927
|
candidateTips.push(...entry.parentHashes.slice(1));
|
|
50928
|
+
assemblyShas.push(entry.hash);
|
|
50877
50929
|
}
|
|
50878
|
-
|
|
50879
|
-
let bestRootDate = Number.POSITIVE_INFINITY;
|
|
50930
|
+
const absorbed = [];
|
|
50880
50931
|
for (const tip of candidateTips) {
|
|
50881
|
-
if (
|
|
50932
|
+
if (alreadyClaimed.has(tip)) continue;
|
|
50882
50933
|
if (!(yield* haveUnrelatedHistories(repoRoot, root.hash, tip))) continue;
|
|
50883
50934
|
const candidate = yield* listFirstParentChain(repoRoot, tip);
|
|
50884
50935
|
const tipDate = Date.parse(candidate.at(0)?.committerDate ?? "");
|
|
50885
50936
|
const candidateRootDate = Date.parse(candidate.at(-1)?.committerDate ?? "");
|
|
50886
50937
|
if (Number.isNaN(tipDate) || Number.isNaN(candidateRootDate) || tipDate >= rootDate) continue;
|
|
50887
|
-
|
|
50888
|
-
best = candidate;
|
|
50889
|
-
bestRootDate = candidateRootDate;
|
|
50890
|
-
}
|
|
50938
|
+
absorbed.push(candidate);
|
|
50891
50939
|
}
|
|
50892
|
-
return
|
|
50940
|
+
return {
|
|
50941
|
+
absorbed,
|
|
50942
|
+
assemblyShas
|
|
50943
|
+
};
|
|
50893
50944
|
});
|
|
50894
50945
|
/**
|
|
50895
|
-
*
|
|
50896
|
-
*
|
|
50897
|
-
*
|
|
50898
|
-
*
|
|
50899
|
-
*
|
|
50900
|
-
*
|
|
50946
|
+
* The lineages whose tree states describe the project over time — the states
|
|
50947
|
+
* the repository (or, before a migration, its absorbed predecessors) actually
|
|
50948
|
+
* passed through. The first lineage is HEAD's own first-parent chain; each
|
|
50949
|
+
* founding graft on any lineage (see {@link findFoundingGraft}) contributes
|
|
50950
|
+
* the absorbed histories as further lineages, recursively, each ending where
|
|
50951
|
+
* the assembly that absorbed it completes. Ordinary side branches and
|
|
50952
|
+
* histories grafted mid-life stay excluded — their trees were never the
|
|
50953
|
+
* repository's state. See {@link describesTreeState} for what consumes this.
|
|
50954
|
+
*/
|
|
50955
|
+
var listLineages = (repoRoot) => gen(function* () {
|
|
50956
|
+
const lineages = [];
|
|
50957
|
+
/** Every sha assigned to a lineage (or queued): overlap and cycle guard. */
|
|
50958
|
+
const claimed = /* @__PURE__ */ new Set();
|
|
50959
|
+
const initial = yield* listFirstParentChain(repoRoot);
|
|
50960
|
+
const queue = initial.length > 0 ? [{
|
|
50961
|
+
chain: initial,
|
|
50962
|
+
endsAtMs: Number.POSITIVE_INFINITY
|
|
50963
|
+
}] : [];
|
|
50964
|
+
while (queue.length > 0) {
|
|
50965
|
+
const pending = queue.shift();
|
|
50966
|
+
if (pending === void 0) break;
|
|
50967
|
+
const { chain, endsAtMs } = pending;
|
|
50968
|
+
for (const entry of chain) claimed.add(entry.hash);
|
|
50969
|
+
const graft = yield* findFoundingGraft(repoRoot, chain, claimed);
|
|
50970
|
+
const shas = new Set(chain.map((entry) => entry.hash));
|
|
50971
|
+
let absorbedEndsAtMs = endsAtMs;
|
|
50972
|
+
if (graft.absorbed.length > 0 && graft.assemblyShas.length < chain.length) {
|
|
50973
|
+
for (const sha of graft.assemblyShas) shas.delete(sha);
|
|
50974
|
+
const firstPostAssembly = chain.at(-1 - graft.assemblyShas.length);
|
|
50975
|
+
if (firstPostAssembly !== void 0) absorbedEndsAtMs = Date.parse(firstPostAssembly.committerDate);
|
|
50976
|
+
}
|
|
50977
|
+
lineages.push({
|
|
50978
|
+
shas,
|
|
50979
|
+
endsAtMs
|
|
50980
|
+
});
|
|
50981
|
+
for (const absorbedChain of graft.absorbed) queue.push({
|
|
50982
|
+
chain: absorbedChain,
|
|
50983
|
+
endsAtMs: absorbedEndsAtMs
|
|
50984
|
+
});
|
|
50985
|
+
}
|
|
50986
|
+
return lineages;
|
|
50987
|
+
});
|
|
50988
|
+
/**
|
|
50989
|
+
* Every sha on any lineage — what `scan` samples tree snapshots from, `gc`
|
|
50990
|
+
* refuses to reclaim and `index` admits into the cube.
|
|
50991
|
+
*/
|
|
50992
|
+
var listMainlineShas = (repoRoot) => listLineages(repoRoot).pipe(map$7((lineages) => {
|
|
50993
|
+
const shas = /* @__PURE__ */ new Set();
|
|
50994
|
+
for (const lineage of lineages) for (const sha of lineage.shas) shas.add(sha);
|
|
50995
|
+
return shas;
|
|
50996
|
+
}));
|
|
50997
|
+
/**
|
|
50998
|
+
* The commits a tree-state collector should cover: `policy` applied to each
|
|
50999
|
+
* lineage separately, so a monthly policy keeps one snapshot per month of
|
|
51000
|
+
* *each* parallel pre-migration history rather than one per month overall.
|
|
51001
|
+
* Shared by `scan` (planning) and `status` (progress targets), which must
|
|
51002
|
+
* count the same way.
|
|
50901
51003
|
*/
|
|
50902
|
-
var
|
|
51004
|
+
var sampleTreeCommits = (lineages, commits, policy) => {
|
|
50903
51005
|
const shas = /* @__PURE__ */ new Set();
|
|
50904
|
-
|
|
50905
|
-
|
|
50906
|
-
for (const
|
|
50907
|
-
chain = yield* findFoundingGraftChain(repoRoot, chain, shas);
|
|
51006
|
+
for (const lineage of lineages) {
|
|
51007
|
+
const candidates = commits.filter((commit) => lineage.shas.has(commit.hash));
|
|
51008
|
+
for (const commit of sampleCommits(candidates, policy)) shas.add(commit.hash);
|
|
50908
51009
|
}
|
|
50909
51010
|
return shas;
|
|
50910
|
-
}
|
|
51011
|
+
};
|
|
50911
51012
|
var summarizeCommits = (commits) => {
|
|
50912
51013
|
const authorEmails = new Set(commits.map((commit) => commit.authorEmail));
|
|
50913
51014
|
const dates = commits.flatMap((commit) => [commit.authorDate, commit.committerDate]).filter((date) => !Number.isNaN(Date.parse(date))).toSorted((left, right) => Date.parse(left) - Date.parse(right));
|
|
@@ -50987,14 +51088,13 @@ var runScan = ({ repoPath, collectorNames, maxCommits, sample, force = false })
|
|
|
50987
51088
|
const summary = summarizeCommits(commits);
|
|
50988
51089
|
const cacheKeys = new Map(collectors.map((collector) => [collector.name, collectorCacheKey(collector, config)]));
|
|
50989
51090
|
const cacheKeyOf = (collector) => cacheKeys.get(collector.name) ?? collectorCacheKey(collector, config);
|
|
50990
|
-
const
|
|
51091
|
+
const lineages = yield* listLineages(repoRoot);
|
|
50991
51092
|
const plans = collectors.map((collector) => {
|
|
50992
51093
|
const policy = sampleOverride ?? collector.defaultSampling;
|
|
50993
|
-
const candidates = describesTreeState(collector) ? selected.filter((commit) => mainlineShas.has(commit.hash)) : selected;
|
|
50994
51094
|
return {
|
|
50995
51095
|
collector,
|
|
50996
51096
|
policy,
|
|
50997
|
-
shas: new Set(sampleCommits(
|
|
51097
|
+
shas: describesTreeState(collector) ? sampleTreeCommits(lineages, selected, policy) : new Set(sampleCommits(selected, policy).map((commit) => commit.hash))
|
|
50998
51098
|
};
|
|
50999
51099
|
});
|
|
51000
51100
|
yield* log(`Plan: ${plans.map((plan) => `${plan.collector.name} → ${plan.shas.size} commits (${samplingLabel(plan.policy)})`).join(", ")}`);
|
|
@@ -51168,7 +51268,7 @@ var dashboardCommand = make$5("dashboard", {
|
|
|
51168
51268
|
open: config.open
|
|
51169
51269
|
})));
|
|
51170
51270
|
//#endregion
|
|
51171
|
-
//#region src/cli/
|
|
51271
|
+
//#region src/cli/gc-command/run-gc.ts
|
|
51172
51272
|
var readdirIfExists = (dirPath) => tryPromise(async () => {
|
|
51173
51273
|
try {
|
|
51174
51274
|
return await readdir(dirPath);
|
|
@@ -51392,7 +51492,7 @@ var gcCommand = make$5("gc", {
|
|
|
51392
51492
|
yes: config.yes
|
|
51393
51493
|
})));
|
|
51394
51494
|
//#endregion
|
|
51395
|
-
//#region src/cli/
|
|
51495
|
+
//#region src/cli/ignore-command/run-ignore.ts
|
|
51396
51496
|
/**
|
|
51397
51497
|
* Lists the catalog in every ignore file at the repository root that needs it,
|
|
51398
51498
|
* so the tools reading them stop walking the cache.
|
|
@@ -51436,7 +51536,49 @@ var ignoreCommand = make$5("ignore", {
|
|
|
51436
51536
|
dryRun: config.dryRun
|
|
51437
51537
|
})));
|
|
51438
51538
|
//#endregion
|
|
51439
|
-
//#region src/cli/shared/
|
|
51539
|
+
//#region src/cli/shared/indexing/compose-lineage-series.ts
|
|
51540
|
+
/** Adds `source`'s numeric leaves into `target`, recursing through records. */
|
|
51541
|
+
var addValuesInto = (target, source) => {
|
|
51542
|
+
for (const [key, value] of Object.entries(source)) {
|
|
51543
|
+
if (key === "sha" || key === "date") continue;
|
|
51544
|
+
if (typeof value === "number") {
|
|
51545
|
+
const current = target[key];
|
|
51546
|
+
target[key] = (typeof current === "number" ? current : 0) + value;
|
|
51547
|
+
} else if (value !== null && typeof value === "object") {
|
|
51548
|
+
const existing = target[key];
|
|
51549
|
+
const nested = existing !== null && typeof existing === "object" ? existing : {};
|
|
51550
|
+
target[key] = nested;
|
|
51551
|
+
addValuesInto(nested, value);
|
|
51552
|
+
}
|
|
51553
|
+
}
|
|
51554
|
+
};
|
|
51555
|
+
/**
|
|
51556
|
+
* One composed row per snapshot, in input order (which must be chronological
|
|
51557
|
+
* by `date`): the snapshot's own values plus the latest values of every other
|
|
51558
|
+
* lineage still alive at that instant. A single-lineage input — every repo
|
|
51559
|
+
* without a founding graft — composes to exactly its own rows.
|
|
51560
|
+
*/
|
|
51561
|
+
var composeLineageSeries = (snapshots) => {
|
|
51562
|
+
const latest = /* @__PURE__ */ new Map();
|
|
51563
|
+
return snapshots.map((snapshot) => {
|
|
51564
|
+
latest.set(snapshot.lineage, snapshot);
|
|
51565
|
+
const timeMs = Date.parse(snapshot.row.date);
|
|
51566
|
+
const composed = {
|
|
51567
|
+
sha: snapshot.row.sha,
|
|
51568
|
+
date: snapshot.row.date
|
|
51569
|
+
};
|
|
51570
|
+
for (const [lineage, candidate] of latest) {
|
|
51571
|
+
if (candidate !== snapshot && timeMs >= candidate.lineageEndsAtMs) {
|
|
51572
|
+
latest.delete(lineage);
|
|
51573
|
+
continue;
|
|
51574
|
+
}
|
|
51575
|
+
addValuesInto(composed, candidate.row);
|
|
51576
|
+
}
|
|
51577
|
+
return composed;
|
|
51578
|
+
});
|
|
51579
|
+
};
|
|
51580
|
+
//#endregion
|
|
51581
|
+
//#region src/cli/shared/indexing/remote.ts
|
|
51440
51582
|
/**
|
|
51441
51583
|
* Turns whatever `remote.origin.url` holds into a URL a browser can open, or
|
|
51442
51584
|
* `undefined` when there is nothing to link to (no remote, a local path, an
|
|
@@ -51560,7 +51702,22 @@ var spanOf = (commits) => {
|
|
|
51560
51702
|
last
|
|
51561
51703
|
};
|
|
51562
51704
|
};
|
|
51563
|
-
var buildDashboardData = (repoRoot, commits, config, remoteUrl) => {
|
|
51705
|
+
var buildDashboardData = (repoRoot, commits, config, remoteUrl, lineageOf) => {
|
|
51706
|
+
/**
|
|
51707
|
+
* Rows of one tree-state series: eligible snapshots tagged with their
|
|
51708
|
+
* lineage, summed across the lineages alive at each instant. For a repo
|
|
51709
|
+
* with a single lineage (no founding graft) this is exactly the plain
|
|
51710
|
+
* filter-and-map it replaces.
|
|
51711
|
+
*/
|
|
51712
|
+
const composeSeries = (eligible, toRow) => composeLineageSeries(commits.flatMap((commit) => {
|
|
51713
|
+
if (!eligible(commit)) return [];
|
|
51714
|
+
const lineage = lineageOf(commit.sha);
|
|
51715
|
+
return lineage === void 0 ? [] : [{
|
|
51716
|
+
row: toRow(commit),
|
|
51717
|
+
lineage: lineage.index,
|
|
51718
|
+
lineageEndsAtMs: lineage.endsAtMs
|
|
51719
|
+
}];
|
|
51720
|
+
}));
|
|
51564
51721
|
/** The raw `"Name <email>"` trailers on a commit, in the order git listed them. */
|
|
51565
51722
|
const coAuthorsOf = (commit) => [...commit.factsByCollector.values()].flat().filter((fact) => fact.metric === "commits.coAuthor").map((fact) => fact.categories?.["coAuthor"] ?? "");
|
|
51566
51723
|
/**
|
|
@@ -51585,18 +51742,18 @@ var buildDashboardData = (repoRoot, commits, config, remoteUrl) => {
|
|
|
51585
51742
|
added: sumMetric(commit, "churn.added"),
|
|
51586
51743
|
deleted: sumMetric(commit, "churn.deleted")
|
|
51587
51744
|
}));
|
|
51588
|
-
const languages =
|
|
51745
|
+
const languages = composeSeries((commit) => hasMetric(commit, "languages.lines"), (commit) => ({
|
|
51589
51746
|
sha: commit.sha.slice(0, 10),
|
|
51590
51747
|
date: commit.committedAt,
|
|
51591
51748
|
byLanguage: groupMetric(commit, "languages.lines", "language")
|
|
51592
51749
|
}));
|
|
51593
|
-
const fileTypes =
|
|
51750
|
+
const fileTypes = composeSeries((commit) => hasMetric(commit, "files.count"), (commit) => ({
|
|
51594
51751
|
sha: commit.sha.slice(0, 10),
|
|
51595
51752
|
date: commit.committedAt,
|
|
51596
51753
|
totalFiles: sumMetric(commit, "files.count"),
|
|
51597
51754
|
totalBytes: sumMetric(commit, "files.bytes")
|
|
51598
51755
|
}));
|
|
51599
|
-
const directives =
|
|
51756
|
+
const directives = composeSeries((commit) => hasMetric(commit, "directives.ts"), (commit) => {
|
|
51600
51757
|
const byType = groupMetric(commit, "directives.eslint", "type");
|
|
51601
51758
|
const ts = groupMetric(commit, "directives.ts", "type");
|
|
51602
51759
|
return {
|
|
@@ -51612,7 +51769,7 @@ var buildDashboardData = (repoRoot, commits, config, remoteUrl) => {
|
|
|
51612
51769
|
todos: sumMetric(commit, "todos.count")
|
|
51613
51770
|
};
|
|
51614
51771
|
});
|
|
51615
|
-
const dependencies =
|
|
51772
|
+
const dependencies = composeSeries((commit) => hasMetric(commit, "dependencies.resolved") || hasMetric(commit, "dependencies.manifest") || hasMetric(commit, "dependencies.scanned"), (commit) => {
|
|
51616
51773
|
const byKind = groupMetric(commit, "dependencies.direct", "kind");
|
|
51617
51774
|
return {
|
|
51618
51775
|
sha: commit.sha.slice(0, 10),
|
|
@@ -51658,7 +51815,7 @@ var buildDashboardData = (repoRoot, commits, config, remoteUrl) => {
|
|
|
51658
51815
|
if (resolved.kind !== "human") return kindGroupLabels[resolved.kind];
|
|
51659
51816
|
return nameByCanonicalEmail.get(resolved.canonicalEmail.toLowerCase()) ?? resolved.label;
|
|
51660
51817
|
};
|
|
51661
|
-
const survival =
|
|
51818
|
+
const survival = composeSeries((commit) => hasMetric(commit, "survival.lines"), (commit) => {
|
|
51662
51819
|
const byContributorYear = {};
|
|
51663
51820
|
const byLanguageYear = {};
|
|
51664
51821
|
for (const facts of commit.factsByCollector.values()) for (const fact of facts) {
|
|
@@ -51755,7 +51912,10 @@ var buildDashboardData = (repoRoot, commits, config, remoteUrl) => {
|
|
|
51755
51912
|
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
51756
51913
|
config: {
|
|
51757
51914
|
contributors: { maxInCharts: config.maxInCharts },
|
|
51758
|
-
charts: {
|
|
51915
|
+
charts: {
|
|
51916
|
+
weekStartsOn: config.weekStartsOn,
|
|
51917
|
+
...Object.keys(config.chartAnnotations).length > 0 ? { annotations: config.chartAnnotations } : {}
|
|
51918
|
+
}
|
|
51759
51919
|
},
|
|
51760
51920
|
repo: {
|
|
51761
51921
|
name: remoteUrl?.split("/").at(-1) ?? path.basename(repoRoot),
|
|
@@ -51824,7 +51984,13 @@ var runIndex = ({ repoPath }) => gen(function* () {
|
|
|
51824
51984
|
const commitsPath = path.join(catalogPath, "commits");
|
|
51825
51985
|
const registry = new Map(builtInCollectors.map((collector) => [collector.name, collector]));
|
|
51826
51986
|
const gitCommits = yield* listCommits(repoRoot);
|
|
51827
|
-
const
|
|
51987
|
+
const lineages = yield* listLineages(repoRoot);
|
|
51988
|
+
/** sha → its lineage's index and contribution window end. */
|
|
51989
|
+
const lineageBySha = /* @__PURE__ */ new Map();
|
|
51990
|
+
for (const [index, lineage] of lineages.entries()) for (const sha of lineage.shas) lineageBySha.set(sha, {
|
|
51991
|
+
index,
|
|
51992
|
+
endsAtMs: lineage.endsAtMs
|
|
51993
|
+
});
|
|
51828
51994
|
const remoteUrl = yield* readRemoteUrl(repoRoot);
|
|
51829
51995
|
const catalogShas = new Set(yield* tryPromise(async () => {
|
|
51830
51996
|
try {
|
|
@@ -51837,7 +52003,7 @@ var runIndex = ({ repoPath }) => gen(function* () {
|
|
|
51837
52003
|
if (orderedCommits.length === 0) return yield* new NoCollectedCommitsError({ commitsPath });
|
|
51838
52004
|
const readOutcomes = yield* forEach$1(orderedCommits, (commit) => tryPromise(async () => {
|
|
51839
52005
|
const commitDir = path.join(commitsPath, commit.hash);
|
|
51840
|
-
const onMainline =
|
|
52006
|
+
const onMainline = lineageBySha.has(commit.hash);
|
|
51841
52007
|
const factsByCollector = /* @__PURE__ */ new Map();
|
|
51842
52008
|
let unknownCollectorDirs = 0;
|
|
51843
52009
|
let offMainlineSnapshots = 0;
|
|
@@ -51875,7 +52041,7 @@ var runIndex = ({ repoPath }) => gen(function* () {
|
|
|
51875
52041
|
const dbPath = path.join(indexDir, "metrics.sqlite");
|
|
51876
52042
|
yield* tryPromise(() => rm(dbPath, { force: true }));
|
|
51877
52043
|
const factCount = yield* try_(() => writeSqlite(dbPath, commitFacts));
|
|
51878
|
-
const dashboardData = buildDashboardData(repoRoot, commitFacts, config, remoteUrl);
|
|
52044
|
+
const dashboardData = buildDashboardData(repoRoot, commitFacts, config, remoteUrl, (sha) => lineageBySha.get(sha));
|
|
51879
52045
|
const dashboardPath = path.join(indexDir, "dashboard.json");
|
|
51880
52046
|
yield* tryPromise(() => writeFile(dashboardPath, JSON.stringify(dashboardData), "utf8"));
|
|
51881
52047
|
yield* log([
|
|
@@ -51883,7 +52049,7 @@ var runIndex = ({ repoPath }) => gen(function* () {
|
|
|
51883
52049
|
`Cube: ${dbPath}`,
|
|
51884
52050
|
`Dashboard data: ${dashboardPath}`,
|
|
51885
52051
|
...unknownCollectorDirs > 0 ? [`Skipped ${unknownCollectorDirs} outputs from unknown collectors (see \`gc --stale\`).`] : [],
|
|
51886
|
-
...offMainlineSnapshots > 0 ? [`Skipped ${offMainlineSnapshots} tree snapshots taken off
|
|
52052
|
+
...offMainlineSnapshots > 0 ? [`Skipped ${offMainlineSnapshots} tree snapshots taken off the lineages.`] : []
|
|
51887
52053
|
].join("\n"));
|
|
51888
52054
|
yield* warnAboutIgnoreFiles({
|
|
51889
52055
|
repoRoot,
|
|
@@ -56703,7 +56869,7 @@ var runQuery = ({ repoPath, sql, json }) => gen(function* () {
|
|
|
56703
56869
|
yield* log(json ? JSON.stringify(result.rows, void 0, 2) : formatTable(result));
|
|
56704
56870
|
});
|
|
56705
56871
|
//#endregion
|
|
56706
|
-
//#region src/cli/
|
|
56872
|
+
//#region src/cli/mcp-command/build-mcp-layer.ts
|
|
56707
56873
|
var queryTool = make$3("query", {
|
|
56708
56874
|
description: "Run a read-only SQL query (SELECT/WITH/EXPLAIN) against the repository's metrics cube. Tables: commits (sha, authored_at, committed_at, author_email, author_name) and facts (commit_sha, collector, metric, value, categories as a JSON object usable via json_extract). Returns { columns, rows, truncated }.",
|
|
56709
56875
|
parameters: Struct({ sql: String$1 }),
|
|
@@ -56789,7 +56955,7 @@ var queryCommand = make$5("query", {
|
|
|
56789
56955
|
json: config.json
|
|
56790
56956
|
})));
|
|
56791
56957
|
//#endregion
|
|
56792
|
-
//#region src/cli/
|
|
56958
|
+
//#region src/cli/report-command/run-report.ts
|
|
56793
56959
|
/**
|
|
56794
56960
|
* Builds a single self-contained report.html: the dashboard bundle with CSS,
|
|
56795
56961
|
* JS and the repository's dashboard.json inlined — shareable without running
|
|
@@ -56849,12 +57015,12 @@ var scanCommand = make$5("scan", {
|
|
|
56849
57015
|
force: config.force
|
|
56850
57016
|
})));
|
|
56851
57017
|
//#endregion
|
|
56852
|
-
//#region src/cli/
|
|
57018
|
+
//#region src/cli/status-command/run-status.ts
|
|
56853
57019
|
var exists = (filePath) => promise(() => access(filePath).then(() => true, () => false));
|
|
56854
57020
|
var runStatus = ({ repoPath }) => gen(function* () {
|
|
56855
57021
|
const repoRoot = yield* resolveRepoRoot(repoPath);
|
|
56856
57022
|
const commits = yield* listCommits(repoRoot);
|
|
56857
|
-
const
|
|
57023
|
+
const lineages = yield* listLineages(repoRoot);
|
|
56858
57024
|
const config = yield* loadConfig(repoRoot);
|
|
56859
57025
|
const catalogPath = config.catalogPath;
|
|
56860
57026
|
if (!(yield* exists(path.join(catalogPath, "catalog.json")))) {
|
|
@@ -56876,10 +57042,10 @@ var runStatus = ({ repoPath }) => gen(function* () {
|
|
|
56876
57042
|
`Catalog: ${catalogPath}`
|
|
56877
57043
|
];
|
|
56878
57044
|
for (const collector of builtInCollectors) {
|
|
56879
|
-
const
|
|
57045
|
+
const targetShas = describesTreeState(collector) ? sampleTreeCommits(lineages, commits, collector.defaultSampling) : new Set(sampleCommits(commits, collector.defaultSampling).map((commit) => commit.hash));
|
|
56880
57046
|
const cacheKey = collectorCacheKey(collector, config);
|
|
56881
|
-
const collected = (yield* forEach$1(
|
|
56882
|
-
lines.push(collector.defaultSampling === "all" ? ` ${collector.name}: ${collected}/${
|
|
57047
|
+
const collected = (yield* forEach$1([...targetShas], (sha) => isCollected(catalog, sha, collector, cacheKey), { concurrency: 16 })).filter(Boolean).length;
|
|
57048
|
+
lines.push(collector.defaultSampling === "all" ? ` ${collector.name}: ${collected}/${targetShas.size} commits collected` : ` ${collector.name}: ${collected}/${targetShas.size} commits collected (${samplingLabel(collector.defaultSampling)} sample of ${commits.length})`);
|
|
56883
57049
|
}
|
|
56884
57050
|
yield* log(lines.join("\n"));
|
|
56885
57051
|
yield* warnAboutIgnoreFiles({
|
package/dist/config.d.ts
CHANGED
|
@@ -71,12 +71,26 @@ export type ContributorsConfig = {
|
|
|
71
71
|
};
|
|
72
72
|
/** First day of the week in calendar-shaped charts. */
|
|
73
73
|
export type WeekStart = "monday" | "sunday";
|
|
74
|
+
/**
|
|
75
|
+
* Stable identifiers of the dashboard's chart sections, in page order — the
|
|
76
|
+
* keys {@link ChartsConfig.annotations} accepts.
|
|
77
|
+
*/
|
|
78
|
+
export declare const chartIds: readonly ["lines-of-code", "commit-calendar", "commits-per-month", "churn-per-month", "direct-dependencies", "dependencies", "loose-ends", "most-suppressed-eslint-rules", "contributors"];
|
|
79
|
+
export type ChartId = (typeof chartIds)[number];
|
|
74
80
|
export type ChartsConfig = {
|
|
75
81
|
/**
|
|
76
82
|
* Which day calendar-shaped dashboard charts (e.g. the commit calendar)
|
|
77
83
|
* start the week on. Defaults to `"monday"`.
|
|
78
84
|
*/
|
|
79
85
|
readonly weekStartsOn?: WeekStart;
|
|
86
|
+
/**
|
|
87
|
+
* Per-chart notes rendered in a callout between the chart's heading and the
|
|
88
|
+
* chart itself — for explaining oddities the data alone cannot, like a
|
|
89
|
+
* history migration that makes a timeline sparse. Keyed by {@link ChartId};
|
|
90
|
+
* values are Markdown limited to paragraphs, `-` lists, `**bold**`,
|
|
91
|
+
* `_italic_`, `` `code` `` and `[links](https://…)`.
|
|
92
|
+
*/
|
|
93
|
+
readonly annotations?: Partial<Record<ChartId, string>>;
|
|
80
94
|
};
|
|
81
95
|
export type CatalogConfig = {
|
|
82
96
|
/**
|
package/dist/config.js
CHANGED
|
@@ -31,6 +31,21 @@
|
|
|
31
31
|
* });
|
|
32
32
|
* ```
|
|
33
33
|
*/
|
|
34
|
+
/**
|
|
35
|
+
* Stable identifiers of the dashboard's chart sections, in page order — the
|
|
36
|
+
* keys {@link ChartsConfig.annotations} accepts.
|
|
37
|
+
*/
|
|
38
|
+
export const chartIds = [
|
|
39
|
+
"lines-of-code",
|
|
40
|
+
"commit-calendar",
|
|
41
|
+
"commits-per-month",
|
|
42
|
+
"churn-per-month",
|
|
43
|
+
"direct-dependencies",
|
|
44
|
+
"dependencies",
|
|
45
|
+
"loose-ends",
|
|
46
|
+
"most-suppressed-eslint-rules",
|
|
47
|
+
"contributors",
|
|
48
|
+
];
|
|
34
49
|
/**
|
|
35
50
|
* Identity helper that gives `repo-dive.config.ts` full type-checking and
|
|
36
51
|
* editor IntelliSense. It returns its argument unchanged.
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */
|
|
2
|
+
@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-space-y-reverse:0;--tw-border-style:solid;--tw-font-weight:initial;--tw-tracking:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-outline-style:solid;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-duration:initial;--tw-scale-x:1;--tw-scale-y:1;--tw-scale-z:1}}}@layer theme{:root,:host{--font-sans:-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--spacing:.25rem;--container-xs:20rem;--container-xl:36rem;--container-5xl:64rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-base:1rem;--text-base--line-height:calc(1.5 / 1);--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--text-2xl:1.5rem;--text-2xl--line-height:calc(2 / 1.5);--font-weight-normal:400;--font-weight-medium:500;--font-weight-semibold:600;--tracking-wide:.025em;--radius-xs:.125rem;--radius-sm:.25rem;--radius-md:.375rem;--animate-spin:spin 1s linear infinite;--animate-pulse:pulse 2s cubic-bezier(.4, 0, .6, 1) infinite;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--animate-late-fade-in:late-fade-in .15s ease-out .5s both}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring:where(:not(iframe)){outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab, currentcolor 50%, transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.pointer-events-none{pointer-events:none}.collapse{visibility:collapse}.invisible{visibility:hidden}.sr-only{clip-path:inset(50%);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.static{position:static}.inset-0{inset:0}.inset-y-0{inset-block:0}.top-0{top:0}.top-2{top:calc(var(--spacing) * 2)}.top-7{top:calc(var(--spacing) * 7)}.right-0{right:0}.right-2{right:calc(var(--spacing) * 2)}.bottom-0{bottom:0}.left-0{left:0}.isolate{isolation:isolate}.z-10{z-index:10}.z-50{z-index:50}.col-start-1{grid-column-start:1}.row-start-1{grid-row-start:1}.mx-1\.5{margin-inline:calc(var(--spacing) * 1.5)}.mx-2{margin-inline:calc(var(--spacing) * 2)}.mx-auto{margin-inline:auto}.my-0\.5{margin-block:calc(var(--spacing) * .5)}.mt-0\.5{margin-top:calc(var(--spacing) * .5)}.mt-1{margin-top:var(--spacing)}.mt-1\.5{margin-top:calc(var(--spacing) * 1.5)}.mt-2{margin-top:calc(var(--spacing) * 2)}.mt-3{margin-top:calc(var(--spacing) * 3)}.mr-1{margin-right:var(--spacing)}.mb-1{margin-bottom:var(--spacing)}.mb-8{margin-bottom:calc(var(--spacing) * 8)}.mb-10{margin-bottom:calc(var(--spacing) * 10)}.ml-auto{margin-left:auto}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline{display:inline}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.inline-grid{display:inline-grid}.table{display:table}.size-2{width:calc(var(--spacing) * 2);height:calc(var(--spacing) * 2)}.size-2\.5{width:calc(var(--spacing) * 2.5);height:calc(var(--spacing) * 2.5)}.size-3\.5{width:calc(var(--spacing) * 3.5);height:calc(var(--spacing) * 3.5)}.size-4{width:calc(var(--spacing) * 4);height:calc(var(--spacing) * 4)}.size-5{width:calc(var(--spacing) * 5);height:calc(var(--spacing) * 5)}.size-full{width:100%;height:100%}.h-\(--scroll-area-thumb-height\){height:var(--scroll-area-thumb-height)}.h-2\.5{height:calc(var(--spacing) * 2.5)}.h-3\.5{height:calc(var(--spacing) * 3.5)}.h-4{height:calc(var(--spacing) * 4)}.h-5{height:calc(var(--spacing) * 5)}.h-7{height:calc(var(--spacing) * 7)}.h-8{height:calc(var(--spacing) * 8)}.h-9{height:calc(var(--spacing) * 9)}.h-10{height:calc(var(--spacing) * 10)}.h-full{height:100%}.max-h-\(--available-height\){max-height:var(--available-height)}.max-h-64{max-height:calc(var(--spacing) * 64)}.w-\(--scroll-area-thumb-width\){width:var(--scroll-area-thumb-width)}.w-2\.5{width:calc(var(--spacing) * 2.5)}.w-12{width:calc(var(--spacing) * 12)}.w-20{width:calc(var(--spacing) * 20)}.w-36{width:calc(var(--spacing) * 36)}.w-56{width:calc(var(--spacing) * 56)}.w-80{width:calc(var(--spacing) * 80)}.w-fit{width:fit-content}.w-full{width:100%}.w-max{width:max-content}.max-w-5xl{max-width:var(--container-5xl)}.max-w-full{max-width:100%}.max-w-prose{max-width:65ch}.max-w-xl{max-width:var(--container-xl)}.max-w-xs{max-width:var(--container-xs)}.min-w-\(--anchor-width\){min-width:var(--anchor-width)}.min-w-8{min-width:calc(var(--spacing) * 8)}.min-w-9{min-width:calc(var(--spacing) * 9)}.min-w-10{min-width:calc(var(--spacing) * 10)}.flex-1{flex:1}.shrink{flex-shrink:1}.shrink-0{flex-shrink:0}.grow{flex-grow:1}.origin-\(--transform-origin\){transform-origin:var(--transform-origin)}.animate-late-fade-in{animation:var(--animate-late-fade-in)}.cursor-default{cursor:default}.cursor-help{cursor:help}.cursor-pointer{cursor:pointer}.touch-none{touch-action:none}.list-disc{list-style-type:disc}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.place-items-center{place-items:center}.items-center{align-items:center}.items-end{align-items:flex-end}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.gap-1{gap:var(--spacing)}.gap-1\.5{gap:calc(var(--spacing) * 1.5)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-2\.5{gap:calc(var(--spacing) * 2.5)}.gap-3{gap:calc(var(--spacing) * 3)}:where(.space-y-1>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(var(--spacing) * var(--tw-space-y-reverse));margin-block-end:calc(var(--spacing) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-1\.5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 1.5) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 1.5) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-4>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 4) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-y-reverse)))}.gap-x-3{column-gap:calc(var(--spacing) * 3)}.gap-y-1{row-gap:var(--spacing)}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-visible{overflow:visible}.overflow-x-hidden{overflow-x:hidden}.overflow-y-auto{overflow-y:auto}.rounded{border-radius:.25rem}.rounded-\[inherit\]{border-radius:inherit}.rounded-full{border-radius:2147483647px}.rounded-md{border-radius:var(--radius-md)}.rounded-sm{border-radius:var(--radius-sm)}.rounded-xs{border-radius:var(--radius-xs)}.border{border-style:var(--tw-border-style);border-width:1px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-l{border-left-style:var(--tw-border-style);border-left-width:1px}.border-\(--grid-line\),.border-border,.border-input{border-color:var(--grid-line)}.border-t-transparent{border-top-color:#0000}.border-l-transparent{border-left-color:#0000}.bg-\(--surface-1\){background-color:var(--surface-1)}.bg-\(--surface-2\){background-color:var(--surface-2)}.bg-background\/85{background-color:var(--surface-1)}@supports (color:color-mix(in lab, red, red)){.bg-background\/85{background-color:color-mix(in oklab, var(--surface-1) 85%, transparent)}}.bg-border{background-color:var(--grid-line)}.bg-muted{background-color:var(--surface-2)}.bg-popover{background-color:var(--surface-1)}.bg-transparent{background-color:#0000}.fill-muted{fill:var(--surface-2)}.stroke-border{stroke:var(--grid-line)}.p-2{padding:calc(var(--spacing) * 2)}.p-4{padding:calc(var(--spacing) * 4)}.p-px{padding:1px}.px-1{padding-inline:var(--spacing)}.px-1\.5{padding-inline:calc(var(--spacing) * 1.5)}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-2\.5{padding-inline:calc(var(--spacing) * 2.5)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-4{padding-inline:calc(var(--spacing) * 4)}.px-6{padding-inline:calc(var(--spacing) * 6)}.py-0\.5{padding-block:calc(var(--spacing) * .5)}.py-1{padding-block:var(--spacing)}.py-1\.5{padding-block:calc(var(--spacing) * 1.5)}.py-2{padding-block:calc(var(--spacing) * 2)}.py-3{padding-block:calc(var(--spacing) * 3)}.py-8{padding-block:calc(var(--spacing) * 8)}.py-16{padding-block:calc(var(--spacing) * 16)}.pr-4{padding-right:calc(var(--spacing) * 4)}.pr-8{padding-right:calc(var(--spacing) * 8)}.pb-1{padding-bottom:var(--spacing)}.pb-2\.5{padding-bottom:calc(var(--spacing) * 2.5)}.pl-1{padding-left:var(--spacing)}.pl-2{padding-left:calc(var(--spacing) * 2)}.pl-3{padding-left:calc(var(--spacing) * 3)}.pl-4{padding-left:calc(var(--spacing) * 4)}.text-center{text-align:center}.text-left{text-align:left}.text-right{text-align:right}.font-mono{font-family:var(--font-mono)}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.text-\[0\.65rem\]{font-size:.65rem}.text-\[0\.85em\]{font-size:.85em}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-normal{--tw-font-weight:var(--font-weight-normal);font-weight:var(--font-weight-normal)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-wide{--tw-tracking:var(--tracking-wide);letter-spacing:var(--tracking-wide)}.text-balance{text-wrap:balance}.whitespace-nowrap{white-space:nowrap}.text-\(--text-muted\){color:var(--text-muted)}.text-\(--text-secondary\){color:var(--text-secondary)}.text-foreground{color:var(--text-primary)}.text-muted-foreground{color:var(--text-muted)}.text-popover-foreground{color:var(--text-primary)}.lowercase{text-transform:lowercase}.uppercase{text-transform:uppercase}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.underline{text-decoration-line:underline}.decoration-\(--text-muted\){-webkit-text-decoration-color:var(--text-muted);-webkit-text-decoration-color:var(--text-muted);-webkit-text-decoration-color:var(--text-muted);text-decoration-color:var(--text-muted)}.decoration-dotted{text-decoration-style:dotted}.underline-offset-2{text-underline-offset:2px}.underline-offset-4{text-underline-offset:4px}.opacity-90{opacity:.9}.shadow-md{--tw-shadow:0 4px 6px -1px var(--tw-shadow-color,#0000001a), 0 2px 4px -2px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-sm{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ring{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.outline-hidden{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.outline-hidden{outline-offset:2px;outline:2px solid #0000}}.outline{outline-style:var(--tw-outline-style);outline-width:1px}.blur{--tw-blur:blur(8px);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.filter{filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-\[color\,box-shadow\]{transition-property:color,box-shadow;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-\[opacity\,transform\]{transition-property:opacity,transform;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-opacity{transition-property:opacity;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.duration-100{--tw-duration:.1s;transition-duration:.1s}.outline-none{--tw-outline-style:none;outline-style:none}.select-none{-webkit-user-select:none;user-select:none}.not-data-pressed\:text-\(--text-muted\):not([data-pressed]){color:var(--text-muted)}@media (hover:hover){.group-hover\:text-\(--series-1\):is(:where(.group):hover *){color:var(--series-1)}}.first\:mt-0:first-child{margin-top:0}@media (hover:hover){.hover\:text-\(--series-1\):hover{color:var(--series-1)}.hover\:text-\(--text-primary\):hover{color:var(--text-primary)}.hover\:underline:hover{text-decoration-line:underline}}.focus\:z-10:focus,.focus-visible\:z-10:focus-visible{z-index:10}.focus-visible\:border-ring:focus-visible{border-color:var(--series-1)}.focus-visible\:ring-\[3px\]:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.focus-visible\:ring-ring\/50:focus-visible{--tw-ring-color:var(--series-1)}@supports (color:color-mix(in lab, red, red)){.focus-visible\:ring-ring\/50:focus-visible{--tw-ring-color:color-mix(in oklab, var(--series-1) 50%, transparent)}}.focus-visible\:outline-1:focus-visible{outline-style:var(--tw-outline-style);outline-width:1px}@media (hover:hover){.enabled\:hover\:bg-accent:enabled:hover,.enabled\:hover\:bg-muted:enabled:hover{background-color:var(--surface-2)}.enabled\:hover\:text-accent-foreground:enabled:hover{color:var(--text-primary)}.enabled\:hover\:text-muted-foreground:enabled:hover{color:var(--text-muted)}.not-data-pressed\:enabled\:hover\:bg-transparent:not([data-pressed]):enabled:hover{background-color:#0000}.not-data-pressed\:enabled\:hover\:text-\(--text-secondary\):not([data-pressed]):enabled:hover{color:var(--text-secondary)}}.disabled\:pointer-events-auto:disabled{pointer-events:auto}.disabled\:pointer-events-none:disabled{pointer-events:none}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-50:disabled{opacity:.5}.data-disabled\:pointer-events-none[data-disabled]{pointer-events:none}.data-disabled\:opacity-50[data-disabled]{opacity:.5}.data-highlighted\:bg-accent[data-highlighted]{background-color:var(--surface-2)}.data-highlighted\:text-accent-foreground[data-highlighted]{color:var(--text-primary)}.data-pressed\:bg-accent[data-pressed]{background-color:var(--surface-2)}.data-pressed\:text-accent-foreground[data-pressed]{color:var(--text-primary)}.data-\[ending-style\]\:scale-95[data-ending-style]{--tw-scale-x:95%;--tw-scale-y:95%;--tw-scale-z:95%;scale:var(--tw-scale-x) var(--tw-scale-y)}.data-\[ending-style\]\:opacity-0[data-ending-style]{opacity:0}.data-\[side\=bottom\]\:-top-\[4px\][data-side=bottom]{top:-4px}.data-\[side\=bottom\]\:rotate-180[data-side=bottom]{rotate:180deg}.data-\[side\=left\]\:-right-\[6\.5px\][data-side=left]{right:-6.5px}.data-\[side\=left\]\:-rotate-90[data-side=left]{rotate:-90deg}.data-\[side\=right\]\:-left-\[6\.5px\][data-side=right]{left:-6.5px}.data-\[side\=right\]\:rotate-90[data-side=right]{rotate:90deg}.data-\[side\=top\]\:-bottom-\[4px\][data-side=top]{bottom:-4px}.data-\[size\=default\]\:h-9[data-size=default]{height:calc(var(--spacing) * 9)}.data-\[size\=sm\]\:h-8[data-size=sm]{height:calc(var(--spacing) * 8)}.data-\[size\=xs\]\:h-7[data-size=xs]{height:calc(var(--spacing) * 7)}.data-\[size\=xs\]\:px-2[data-size=xs]{padding-inline:calc(var(--spacing) * 2)}.data-\[size\=xs\]\:text-xs[data-size=xs]{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}:is(.\*\:data-\[slot\=select-value\]\:line-clamp-1>*)[data-slot=select-value]{-webkit-line-clamp:1;-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}:is(.\*\:data-\[slot\=select-value\]\:flex>*)[data-slot=select-value]{display:flex}:is(.\*\:data-\[slot\=select-value\]\:items-center>*)[data-slot=select-value]{align-items:center}:is(.\*\:data-\[slot\=select-value\]\:gap-2>*)[data-slot=select-value]{gap:calc(var(--spacing) * 2)}.data-\[starting-style\]\:scale-95[data-starting-style]{--tw-scale-x:95%;--tw-scale-y:95%;--tw-scale-z:95%;scale:var(--tw-scale-x) var(--tw-scale-y)}.data-\[starting-style\]\:opacity-0[data-starting-style]{opacity:0}.data-\[variant\=outline\]\:gap-0[data-variant=outline]{gap:0}.data-\[variant\=outline\]\:rounded-md[data-variant=outline]{border-radius:var(--radius-md)}.data-\[variant\=outline\]\:rounded-none[data-variant=outline]{border-radius:0}.data-\[variant\=outline\]\:border-l-0[data-variant=outline]{border-left-style:var(--tw-border-style);border-left-width:0}.data-\[variant\=outline\]\:first\:rounded-l-md[data-variant=outline]:first-child{border-top-left-radius:var(--radius-md);border-bottom-left-radius:var(--radius-md)}.data-\[variant\=outline\]\:first\:border-l[data-variant=outline]:first-child{border-left-style:var(--tw-border-style);border-left-width:1px}.data-\[variant\=outline\]\:last\:rounded-r-md[data-variant=outline]:last-child{border-top-right-radius:var(--radius-md);border-bottom-right-radius:var(--radius-md)}@media (prefers-reduced-motion:no-preference){.motion-safe\:animate-pulse{animation:var(--animate-pulse)}.motion-safe\:animate-spin{animation:var(--animate-spin)}}@media (width>=40rem){.sm\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}}@media (width>=64rem){.lg\:grid-cols-5{grid-template-columns:repeat(5,minmax(0,1fr))}}.\[\&_svg\]\:pointer-events-none svg{pointer-events:none}.\[\&_svg\]\:shrink-0 svg{flex-shrink:0}.\[\&_svg\:not\(\[class\*\=\'size-\'\]\)\]\:size-4 svg:not([class*=size-]){width:calc(var(--spacing) * 4);height:calc(var(--spacing) * 4)}.\[\&_svg\:not\(\[class\*\=\'text-\'\]\)\]\:text-muted-foreground svg:not([class*=text-]){color:var(--text-muted)}}:root{--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light;--surface-1:#fcfcfb;--surface-2:#f4f4f2;--text-primary:#0b0b0b;--text-secondary:#52514e;--text-muted:#86847e;--grid-line:#e7e6e2;--series-1:#2a78d6;--series-2:#1baf7a;--series-3:#eda100;--series-4:#008300;--series-5:#4a3aa7;--series-6:#e34948;--series-7:#e87ba4;--series-8:#eb6834;--series-9:#8569e8;--series-10:#958400;--series-11:#a92172;--series-12:#925001;--series-13:#00b8bf;--series-14:#59bc42;--series-15:#df71d7;--series-16:#8339b0;--series-17:#5b6c00;--series-18:#006a9e;--series-19:#b51b34;--series-20:#3d54cb;--diverge-pos:#2a78d6;--diverge-neg:#e34948;--kind-human:var(--series-1);--kind-bot:var(--series-3);--kind-ai:#93278f;--seq-100:#cde2fb;--seq-200:#9ec5f4;--seq-300:#6da7ec;--seq-400:#3987e5;--seq-500:#256abf;--seq-600:#184f95;--seq-700:#0d366b}@media (prefers-color-scheme:dark){:root{--lightningcss-light: ;--lightningcss-dark:initial;color-scheme:dark;--surface-1:#1a1a19;--surface-2:#242423;--text-primary:#fff;--text-secondary:#c3c2b7;--text-muted:#8b897f;--grid-line:#33332f;--series-1:#3987e5;--series-2:#199e70;--series-3:#c98500;--series-4:#008300;--series-5:#9085e9;--series-6:#e66767;--series-7:#d55181;--series-8:#d95926;--series-9:#8164e3;--series-10:#907f03;--series-11:#b02978;--series-12:#9b5400;--series-13:#03a5ab;--series-14:#47aa2d;--series-15:#cd5fc5;--series-16:#893fb6;--series-17:#607201;--series-18:#0070a6;--series-19:#bc2539;--series-20:#425ad1;--diverge-pos:#3987e5;--diverge-neg:#e66767;--kind-ai:#b02fa8}}html{overflow-y:scroll}body{background:var(--surface-1);color:var(--text-primary)}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-ordinal{syntax:"*";inherits:false}@property --tw-slashed-zero{syntax:"*";inherits:false}@property --tw-numeric-figure{syntax:"*";inherits:false}@property --tw-numeric-spacing{syntax:"*";inherits:false}@property --tw-numeric-fraction{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-outline-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@property --tw-duration{syntax:"*";inherits:false}@property --tw-scale-x{syntax:"*";inherits:false;initial-value:1}@property --tw-scale-y{syntax:"*";inherits:false;initial-value:1}@property --tw-scale-z{syntax:"*";inherits:false;initial-value:1}@keyframes spin{to{transform:rotate(360deg)}}@keyframes pulse{50%{opacity:.5}}@keyframes late-fade-in{0%{opacity:0}to{opacity:1}}
|