repo-dive 0.15.2 → 0.16.0
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/README.md +2 -1
- package/dist/cli.js +191 -27
- package/dist/config.d.ts +1 -1
- package/dist/config.js +1 -0
- package/dist/dashboard/assets/index-DzAtbZv2.css +2 -0
- package/dist/dashboard/assets/index-VZ0gkKmm.js +10 -0
- package/dist/dashboard/index.html +2 -2
- package/package.json +7 -7
- package/dist/dashboard/assets/index-1G4_D1Zz.css +0 -2
- package/dist/dashboard/assets/index-CyovVos3.js +0 -10
package/README.md
CHANGED
|
@@ -73,12 +73,13 @@ Collectors so far:
|
|
|
73
73
|
- **todo-comments** — TODO/FIXME/HACK/XXX counts
|
|
74
74
|
- **languages** — lines and file count per language across a commit's source files (lockfiles, minified bundles and generated data excluded)
|
|
75
75
|
- **survival** — `git blame` line survival by extension, author and age cohort (sampled monthly)
|
|
76
|
+
- **file-survival** — file survival by extension, creator and creation cohort, renames followed (sampled monthly)
|
|
76
77
|
|
|
77
78
|
The catalog hides itself from git, but other tools that walk the repository (prettier, markdownlint, cspell, docker builds) each read one ignore file at its root.
|
|
78
79
|
`scan` warns when the catalog is missing from those; `repo-dive ignore` adds it to every one that needs it, writing the entry in the shape the file is already written in and skipping the files whose tool learns about the catalog elsewhere.
|
|
79
80
|
|
|
80
81
|
`index` normalizes raw snapshots into `.repo-dive/index/metrics.sqlite` — a facts-by-categories cube, rebuildable at any time — plus `dashboard.json`.
|
|
81
|
-
`dashboard` then serves a local React app with interactive charts: languages over time, a GitHub-style commit calendar, monthly commits with AI-assisted share, churn, lint-suppression trends, dependency counts over time, code survival by cohort and author, and more.
|
|
82
|
+
`dashboard` then serves a local React app with interactive charts: languages over time, file counts over time, a GitHub-style commit calendar, monthly commits with AI-assisted share, churn, lint-suppression trends, dependency counts over time, code survival by cohort and author, and more.
|
|
82
83
|
|
|
83
84
|
## Configuration
|
|
84
85
|
|
package/dist/cli.js
CHANGED
|
@@ -43882,7 +43882,7 @@ var runWith = (command, config) => {
|
|
|
43882
43882
|
};
|
|
43883
43883
|
var package_default = {
|
|
43884
43884
|
name: "repo-dive",
|
|
43885
|
-
version: "0.
|
|
43885
|
+
version: "0.16.0",
|
|
43886
43886
|
description: "Dive into a git repo's history: per-commit snapshots, an indexed metrics catalog and an interactive dashboard",
|
|
43887
43887
|
keywords: [
|
|
43888
43888
|
"git",
|
|
@@ -43968,9 +43968,9 @@ var package_default = {
|
|
|
43968
43968
|
"@tsconfig/node-ts": "23.6.4",
|
|
43969
43969
|
"@tsconfig/strictest": "2.0.8",
|
|
43970
43970
|
"@types/babel__core": "7.20.5",
|
|
43971
|
-
"@types/node": "26.4.
|
|
43971
|
+
"@types/node": "26.4.1",
|
|
43972
43972
|
"@types/react": "19.2.18",
|
|
43973
|
-
"@types/react-dom": "19.2.
|
|
43973
|
+
"@types/react-dom": "19.2.7",
|
|
43974
43974
|
"@typescript/native": "npm:typescript@7.0.2",
|
|
43975
43975
|
"@visx/axis": "4.0.0",
|
|
43976
43976
|
"@visx/curve": "4.0.0",
|
|
@@ -43981,14 +43981,14 @@ var package_default = {
|
|
|
43981
43981
|
"@vitejs/plugin-react": "6.1.1",
|
|
43982
43982
|
"babel-plugin-react-compiler": "1.0.0",
|
|
43983
43983
|
"class-variance-authority": "0.7.1",
|
|
43984
|
-
"cspell": "10.
|
|
43984
|
+
"cspell": "10.2.1",
|
|
43985
43985
|
"effect": "4.0.0-rc.112",
|
|
43986
43986
|
"eslint": "9.39.5",
|
|
43987
43987
|
"eslint-plugin-react-hooks": "7.1.1",
|
|
43988
43988
|
"husky": "9.1.7",
|
|
43989
|
-
"knip": "6.
|
|
43989
|
+
"knip": "6.34.0",
|
|
43990
43990
|
"lint-staged": "17.4.1",
|
|
43991
|
-
"lucide-react": "1.
|
|
43991
|
+
"lucide-react": "1.40.0",
|
|
43992
43992
|
"markdownlint-cli": "0.49.1",
|
|
43993
43993
|
"npm-run-all2": "9.0.3",
|
|
43994
43994
|
"prettier": "3.9.6",
|
|
@@ -43998,7 +43998,7 @@ var package_default = {
|
|
|
43998
43998
|
"tailwindcss": "4.3.3",
|
|
43999
43999
|
"typescript": "npm:@typescript/typescript6@6.0.2",
|
|
44000
44000
|
"vite": "8.2.2",
|
|
44001
|
-
"vitest": "
|
|
44001
|
+
"vitest": "5.0.0",
|
|
44002
44002
|
"yaml": "2.9.0"
|
|
44003
44003
|
},
|
|
44004
44004
|
packageManager: "pnpm@12.3.3",
|
|
@@ -44156,6 +44156,14 @@ var languageOfExtension = (extension) => languageByExtension[extension] ?? exten
|
|
|
44156
44156
|
* state, so charting it puts a cliff into every timeline.
|
|
44157
44157
|
*/
|
|
44158
44158
|
var describesTreeState = (collector) => collector.strategy !== "log";
|
|
44159
|
+
/**
|
|
44160
|
+
* YYYY-MM of a unix timestamp in UTC — the age-cohort key the survival
|
|
44161
|
+
* collectors bin by, shared so lines and files land in identical cohorts.
|
|
44162
|
+
*/
|
|
44163
|
+
var cohortMonthOf = (unixSeconds) => {
|
|
44164
|
+
const date = /* @__PURE__ */ new Date(unixSeconds * 1e3);
|
|
44165
|
+
return `${date.getUTCFullYear()}-${String(date.getUTCMonth() + 1).padStart(2, "0")}`;
|
|
44166
|
+
};
|
|
44159
44167
|
/** File extension used as a category key, e.g. ".ts"; files without one map to "(none)". */
|
|
44160
44168
|
var extensionOf = (filePath) => {
|
|
44161
44169
|
const basename = filePath.split("/").at(-1) ?? "";
|
|
@@ -44189,7 +44197,7 @@ var isScannableSourceFile = (filePath) => {
|
|
|
44189
44197
|
};
|
|
44190
44198
|
//#endregion
|
|
44191
44199
|
//#region src/cli/shared/collectors/churn.ts
|
|
44192
|
-
var recordSeparator$
|
|
44200
|
+
var recordSeparator$2 = "";
|
|
44193
44201
|
/** Parses `git show --numstat --format=` output: "<added>\t<deleted>\t<path>" per file. */
|
|
44194
44202
|
var parseNumstat = (stdout) => {
|
|
44195
44203
|
let filesChanged = 0;
|
|
@@ -44249,11 +44257,11 @@ var churnCollector = {
|
|
|
44249
44257
|
"-C",
|
|
44250
44258
|
repoRoot,
|
|
44251
44259
|
"log",
|
|
44252
|
-
`--format=${recordSeparator$
|
|
44260
|
+
`--format=${recordSeparator$2}%H`,
|
|
44253
44261
|
"--numstat"
|
|
44254
44262
|
]).pipe(map$7((stdout) => {
|
|
44255
44263
|
const outputs = /* @__PURE__ */ new Map();
|
|
44256
|
-
for (const record of stdout.split(recordSeparator$
|
|
44264
|
+
for (const record of stdout.split(recordSeparator$2)) {
|
|
44257
44265
|
const newlineIndex = record.indexOf("\n");
|
|
44258
44266
|
if (newlineIndex === -1) continue;
|
|
44259
44267
|
const sha = record.slice(0, newlineIndex).trim();
|
|
@@ -44277,8 +44285,8 @@ var churnCollector = {
|
|
|
44277
44285
|
};
|
|
44278
44286
|
//#endregion
|
|
44279
44287
|
//#region src/cli/shared/collectors/commit-meta.ts
|
|
44280
|
-
var fieldSeparator$
|
|
44281
|
-
var recordSeparator = "";
|
|
44288
|
+
var fieldSeparator$2 = "";
|
|
44289
|
+
var recordSeparator$1 = "";
|
|
44282
44290
|
var format = [
|
|
44283
44291
|
"%H",
|
|
44284
44292
|
"%an",
|
|
@@ -44306,7 +44314,7 @@ var parseTrailers = (raw) => {
|
|
|
44306
44314
|
return trailers;
|
|
44307
44315
|
};
|
|
44308
44316
|
var parseCommitMeta = (stdout) => {
|
|
44309
|
-
const [sha = "", authorName = "", authorEmail = "", authoredAt = "", committerName = "", committerEmail = "", committedAt = "", parentsRaw = "", subject = "", trailersRaw = ""] = stdout.trim().split(fieldSeparator$
|
|
44317
|
+
const [sha = "", authorName = "", authorEmail = "", authoredAt = "", committerName = "", committerEmail = "", committedAt = "", parentsRaw = "", subject = "", trailersRaw = ""] = stdout.trim().split(fieldSeparator$2);
|
|
44310
44318
|
const trailers = parseTrailers(trailersRaw);
|
|
44311
44319
|
return {
|
|
44312
44320
|
sha,
|
|
@@ -44340,10 +44348,10 @@ var commitMetaCollector = {
|
|
|
44340
44348
|
"-C",
|
|
44341
44349
|
repoRoot,
|
|
44342
44350
|
"log",
|
|
44343
|
-
`--format=${recordSeparator}${format}`
|
|
44351
|
+
`--format=${recordSeparator$1}${format}`
|
|
44344
44352
|
]).pipe(map$7((stdout) => {
|
|
44345
44353
|
const outputs = /* @__PURE__ */ new Map();
|
|
44346
|
-
for (const record of stdout.split(recordSeparator)) {
|
|
44354
|
+
for (const record of stdout.split(recordSeparator$1)) {
|
|
44347
44355
|
if (!record.trim()) continue;
|
|
44348
44356
|
const meta = parseCommitMeta(record);
|
|
44349
44357
|
if (shas.has(meta.sha)) outputs.set(meta.sha, meta);
|
|
@@ -50893,6 +50901,154 @@ var directivesCollector = {
|
|
|
50893
50901
|
}
|
|
50894
50902
|
};
|
|
50895
50903
|
//#endregion
|
|
50904
|
+
//#region src/cli/shared/collectors/file-survival.ts
|
|
50905
|
+
/** Between the fields of one commit header in the log stream. */
|
|
50906
|
+
var fieldSeparator$1 = "";
|
|
50907
|
+
/** Before each commit record in the log stream. */
|
|
50908
|
+
var recordSeparator = "";
|
|
50909
|
+
/**
|
|
50910
|
+
* Resolves each present file to the commit that created it, from one
|
|
50911
|
+
* `git log --name-status` stream over the history below the snapshot
|
|
50912
|
+
* (newest first, `--format=%x1E%ae%x1F%at`).
|
|
50913
|
+
*
|
|
50914
|
+
* The walk keeps a map from a path *as history spelled it at that point* to
|
|
50915
|
+
* the present-day paths it became. An `A` entry is the creation event for
|
|
50916
|
+
* whatever that path currently traces; an `R old new` entry re-keys the trace
|
|
50917
|
+
* from `new` to `old`, so a rename chain resolves to the original creation
|
|
50918
|
+
* rather than restarting the file's life — later edits never re-bin a file
|
|
50919
|
+
* either, because only `A`/`R` entries are read at all. Renames of one commit
|
|
50920
|
+
* are applied as a batch: `a→b` and `c→a` in the same commit must both re-key
|
|
50921
|
+
* against the pre-commit names.
|
|
50922
|
+
*
|
|
50923
|
+
* A file whose creation the walk never meets (a history boundary, or a rename
|
|
50924
|
+
* past git's detection limit inside a merge) stays unresolved and is simply
|
|
50925
|
+
* absent from the returned map.
|
|
50926
|
+
*/
|
|
50927
|
+
var resolveFileOrigins = (stdout, presentFiles) => {
|
|
50928
|
+
const origins = /* @__PURE__ */ new Map();
|
|
50929
|
+
/** Path as spelled at this point of the walk → present-day paths it became. */
|
|
50930
|
+
const pending = /* @__PURE__ */ new Map();
|
|
50931
|
+
for (const filePath of presentFiles) pending.set(filePath, [filePath]);
|
|
50932
|
+
for (const record of stdout.split(recordSeparator)) {
|
|
50933
|
+
if (pending.size === 0) break;
|
|
50934
|
+
const lines = record.split("\n");
|
|
50935
|
+
const [authorEmail = "", authorTimeRaw = ""] = (lines[0] ?? "").split(fieldSeparator$1);
|
|
50936
|
+
const authorTime = Number(authorTimeRaw);
|
|
50937
|
+
if (!Number.isFinite(authorTime)) continue;
|
|
50938
|
+
const origin = {
|
|
50939
|
+
authorEmail,
|
|
50940
|
+
cohortMonth: cohortMonthOf(authorTime)
|
|
50941
|
+
};
|
|
50942
|
+
/** This commit's renames, applied after its entries so they re-key atomically. */
|
|
50943
|
+
const renamedTo = /* @__PURE__ */ new Map();
|
|
50944
|
+
for (const line of lines.slice(1)) {
|
|
50945
|
+
const [status = "", ...paths] = line.split(" ");
|
|
50946
|
+
const createdPath = status === "A" ? paths[0] : void 0;
|
|
50947
|
+
const copiedPath = status.startsWith("C") ? paths[1] : void 0;
|
|
50948
|
+
const resolvedPath = createdPath ?? copiedPath;
|
|
50949
|
+
if (resolvedPath !== void 0) {
|
|
50950
|
+
const targets = pending.get(resolvedPath);
|
|
50951
|
+
if (targets) {
|
|
50952
|
+
for (const target of targets) origins.set(target, origin);
|
|
50953
|
+
pending.delete(resolvedPath);
|
|
50954
|
+
}
|
|
50955
|
+
} else if (status.startsWith("R")) {
|
|
50956
|
+
const [oldPath, newPath] = paths;
|
|
50957
|
+
if (oldPath === void 0 || newPath === void 0) continue;
|
|
50958
|
+
const targets = pending.get(newPath);
|
|
50959
|
+
if (targets) {
|
|
50960
|
+
pending.delete(newPath);
|
|
50961
|
+
renamedTo.set(oldPath, [...renamedTo.get(oldPath) ?? [], ...targets]);
|
|
50962
|
+
}
|
|
50963
|
+
}
|
|
50964
|
+
}
|
|
50965
|
+
for (const [oldPath, targets] of renamedTo) pending.set(oldPath, [...pending.get(oldPath) ?? [], ...targets]);
|
|
50966
|
+
}
|
|
50967
|
+
return origins;
|
|
50968
|
+
};
|
|
50969
|
+
/**
|
|
50970
|
+
* When a file was created and by whom, for every source file alive at a
|
|
50971
|
+
* commit. The granular sibling of `survival`: where that collector asks which
|
|
50972
|
+
* *lines* still live, this one only asks whether the *file* still exists —
|
|
50973
|
+
* edits after creation neither re-attribute a file nor move it to a newer
|
|
50974
|
+
* cohort, and only a deletion ends it.
|
|
50975
|
+
*
|
|
50976
|
+
* Emits into the `survival.*` metric namespace on purpose: `survival.lines`
|
|
50977
|
+
* and `survival.files` are two grains of the same question, and the dashboard
|
|
50978
|
+
* draws them with the same machinery. Collected separately so bumping either
|
|
50979
|
+
* collector's version does not invalidate the other's outputs (blame is far
|
|
50980
|
+
* more expensive than this log walk).
|
|
50981
|
+
*/
|
|
50982
|
+
var fileSurvivalCollector = {
|
|
50983
|
+
name: "file-survival",
|
|
50984
|
+
description: "File survival via git log: living files by extension, creator and creation cohort, renames followed (sampled monthly by default)",
|
|
50985
|
+
version: "1",
|
|
50986
|
+
strategy: "tree",
|
|
50987
|
+
defaultSampling: "monthly",
|
|
50988
|
+
collect: ({ repoRoot, sha }) => gen(function* () {
|
|
50989
|
+
const fileList = yield* runGit([
|
|
50990
|
+
"-C",
|
|
50991
|
+
repoRoot,
|
|
50992
|
+
"ls-tree",
|
|
50993
|
+
"-r",
|
|
50994
|
+
sha
|
|
50995
|
+
]);
|
|
50996
|
+
const files = [];
|
|
50997
|
+
for (const line of fileList.split("\n")) {
|
|
50998
|
+
const tabIndex = line.indexOf(" ");
|
|
50999
|
+
if (tabIndex === -1) continue;
|
|
51000
|
+
const [, type] = line.slice(0, tabIndex).split(/\s+/);
|
|
51001
|
+
const filePath = line.slice(tabIndex + 1);
|
|
51002
|
+
if (type === "blob" && isScannableSourceFile(filePath)) files.push(filePath);
|
|
51003
|
+
}
|
|
51004
|
+
const origins = resolveFileOrigins(yield* runGit([
|
|
51005
|
+
"-C",
|
|
51006
|
+
repoRoot,
|
|
51007
|
+
"log",
|
|
51008
|
+
"--topo-order",
|
|
51009
|
+
"-M",
|
|
51010
|
+
"--name-status",
|
|
51011
|
+
`--format=${recordSeparator}%ae${fieldSeparator$1}%at`,
|
|
51012
|
+
sha
|
|
51013
|
+
]), files);
|
|
51014
|
+
const counts = /* @__PURE__ */ new Map();
|
|
51015
|
+
for (const filePath of files) {
|
|
51016
|
+
const origin = origins.get(filePath);
|
|
51017
|
+
const key = [
|
|
51018
|
+
extensionOf(filePath),
|
|
51019
|
+
origin?.authorEmail ?? "",
|
|
51020
|
+
origin?.cohortMonth ?? ""
|
|
51021
|
+
].join(fieldSeparator$1);
|
|
51022
|
+
counts.set(key, (counts.get(key) ?? 0) + 1);
|
|
51023
|
+
}
|
|
51024
|
+
return {
|
|
51025
|
+
rows: [...counts.entries()].map(([key, fileCount]) => {
|
|
51026
|
+
const [extension = "", authorEmail = "", cohortMonth = ""] = key.split(fieldSeparator$1);
|
|
51027
|
+
return {
|
|
51028
|
+
extension,
|
|
51029
|
+
authorEmail,
|
|
51030
|
+
cohortMonth,
|
|
51031
|
+
files: fileCount
|
|
51032
|
+
};
|
|
51033
|
+
}),
|
|
51034
|
+
totalFiles: files.length
|
|
51035
|
+
};
|
|
51036
|
+
}),
|
|
51037
|
+
normalize: (raw) => {
|
|
51038
|
+
const facts = [];
|
|
51039
|
+
for (const row of arrayAt(raw, "rows")) facts.push({
|
|
51040
|
+
metric: "survival.files",
|
|
51041
|
+
value: numberAt(row, "files"),
|
|
51042
|
+
categories: {
|
|
51043
|
+
extension: stringAt(row, "extension"),
|
|
51044
|
+
author: stringAt(row, "authorEmail"),
|
|
51045
|
+
cohort: stringAt(row, "cohortMonth")
|
|
51046
|
+
}
|
|
51047
|
+
});
|
|
51048
|
+
return facts;
|
|
51049
|
+
}
|
|
51050
|
+
};
|
|
51051
|
+
//#endregion
|
|
50896
51052
|
//#region src/cli/shared/collectors/file-types.ts
|
|
50897
51053
|
/** Parses `git ls-tree -r -l <sha>` output: "<mode> <type> <sha> <size>\t<path>" per entry. */
|
|
50898
51054
|
var parseLsTree = (stdout) => {
|
|
@@ -51028,10 +51184,6 @@ var languagesCollector = {
|
|
|
51028
51184
|
};
|
|
51029
51185
|
//#endregion
|
|
51030
51186
|
//#region src/cli/shared/collectors/survival.ts
|
|
51031
|
-
var monthOf = (unixSeconds) => {
|
|
51032
|
-
const date = /* @__PURE__ */ new Date(unixSeconds * 1e3);
|
|
51033
|
-
return `${date.getUTCFullYear()}-${String(date.getUTCMonth() + 1).padStart(2, "0")}`;
|
|
51034
|
-
};
|
|
51035
51187
|
/**
|
|
51036
51188
|
* Parses `git blame --line-porcelain` output into one attribution per line.
|
|
51037
51189
|
* Porcelain repeats full headers per line, so author-mail/author-time always
|
|
@@ -51045,7 +51197,7 @@ var parseBlamePorcelain = (stdout) => {
|
|
|
51045
51197
|
else if (line.startsWith("author-time ")) authorTime = Number(line.slice(12));
|
|
51046
51198
|
else if (line.startsWith(" ")) attributions.push({
|
|
51047
51199
|
authorEmail,
|
|
51048
|
-
cohortMonth:
|
|
51200
|
+
cohortMonth: cohortMonthOf(authorTime)
|
|
51049
51201
|
});
|
|
51050
51202
|
return attributions;
|
|
51051
51203
|
};
|
|
@@ -51178,7 +51330,8 @@ var builtInCollectors = [
|
|
|
51178
51330
|
}
|
|
51179
51331
|
},
|
|
51180
51332
|
languagesCollector,
|
|
51181
|
-
survivalCollector
|
|
51333
|
+
survivalCollector,
|
|
51334
|
+
fileSurvivalCollector
|
|
51182
51335
|
];
|
|
51183
51336
|
var UnknownCollectorError = class extends TaggedError$1("UnknownCollectorError") {
|
|
51184
51337
|
get message() {
|
|
@@ -51302,6 +51455,7 @@ var collectorsCommand = make$6("collectors").pipe(withDescription("List availabl
|
|
|
51302
51455
|
*/
|
|
51303
51456
|
var chartIds = [
|
|
51304
51457
|
"lines-of-code",
|
|
51458
|
+
"number-of-files",
|
|
51305
51459
|
"commit-calendar",
|
|
51306
51460
|
"commits-per-month",
|
|
51307
51461
|
"churn-per-month",
|
|
@@ -53020,7 +53174,8 @@ var buildDashboardData = (repoRoot, commits, config, remoteUrl, lineageOf) => {
|
|
|
53020
53174
|
const languages = composeSeries((commit) => hasMetric(commit, "languages.lines"), (commit) => ({
|
|
53021
53175
|
sha: commit.sha.slice(0, 10),
|
|
53022
53176
|
date: commit.committedAt,
|
|
53023
|
-
byLanguage: groupMetric(commit, "languages.lines", "language")
|
|
53177
|
+
byLanguage: groupMetric(commit, "languages.lines", "language"),
|
|
53178
|
+
byLanguageFiles: groupMetric(commit, "languages.files", "language")
|
|
53024
53179
|
}));
|
|
53025
53180
|
const fileTypes = composeSeries((commit) => hasMetric(commit, "files.count"), (commit) => ({
|
|
53026
53181
|
sha: commit.sha.slice(0, 10),
|
|
@@ -53090,11 +53245,17 @@ var buildDashboardData = (repoRoot, commits, config, remoteUrl, lineageOf) => {
|
|
|
53090
53245
|
if (resolved.kind !== "human") return kindGroupLabels[resolved.kind];
|
|
53091
53246
|
return nameByCanonicalEmail.get(resolved.canonicalEmail.toLowerCase()) ?? resolved.label;
|
|
53092
53247
|
};
|
|
53093
|
-
|
|
53248
|
+
/**
|
|
53249
|
+
* One survival series — living lines or living files, the same shape at two
|
|
53250
|
+
* grains: the units cross-tabulated by contributor (and by extension) and
|
|
53251
|
+
* the year each was authored, so the dashboard can split a contributor's or
|
|
53252
|
+
* language's area into year bands.
|
|
53253
|
+
*/
|
|
53254
|
+
const survivalSeriesOf = (metric) => composeSeries((commit) => hasMetric(commit, metric), (commit) => {
|
|
53094
53255
|
const byContributorYear = {};
|
|
53095
53256
|
const byLanguageYear = {};
|
|
53096
53257
|
for (const facts of commit.factsByCollector.values()) for (const fact of facts) {
|
|
53097
|
-
if (fact.metric !==
|
|
53258
|
+
if (fact.metric !== metric) continue;
|
|
53098
53259
|
const label = survivalLabelOf(fact.categories?.["author"] ?? "");
|
|
53099
53260
|
const year = (fact.categories?.["cohort"] ?? "").slice(0, 4) || "?";
|
|
53100
53261
|
const byYear = byContributorYear[label] ??= {};
|
|
@@ -53106,13 +53267,15 @@ var buildDashboardData = (repoRoot, commits, config, remoteUrl, lineageOf) => {
|
|
|
53106
53267
|
return {
|
|
53107
53268
|
sha: commit.sha.slice(0, 10),
|
|
53108
53269
|
date: commit.committedAt,
|
|
53109
|
-
byCohort: groupMetric(commit,
|
|
53110
|
-
byContributor: sumByKey(groupMetric(commit,
|
|
53270
|
+
byCohort: groupMetric(commit, metric, "cohort"),
|
|
53271
|
+
byContributor: sumByKey(groupMetric(commit, metric, "author"), survivalLabelOf),
|
|
53111
53272
|
byContributorYear,
|
|
53112
|
-
byLanguage: sumByKey(groupMetric(commit,
|
|
53273
|
+
byLanguage: sumByKey(groupMetric(commit, metric, "extension"), languageOfExtension),
|
|
53113
53274
|
byLanguageYear
|
|
53114
53275
|
};
|
|
53115
53276
|
});
|
|
53277
|
+
const survival = survivalSeriesOf("survival.lines");
|
|
53278
|
+
const fileSurvival = survivalSeriesOf("survival.files");
|
|
53116
53279
|
const contributorMap = /* @__PURE__ */ new Map();
|
|
53117
53280
|
/**
|
|
53118
53281
|
* Fetches or creates a contributor's bucket.
|
|
@@ -53209,6 +53372,7 @@ var buildDashboardData = (repoRoot, commits, config, remoteUrl, lineageOf) => {
|
|
|
53209
53372
|
dependencies,
|
|
53210
53373
|
topRules,
|
|
53211
53374
|
survival,
|
|
53375
|
+
fileSurvival,
|
|
53212
53376
|
contributors
|
|
53213
53377
|
};
|
|
53214
53378
|
};
|
package/dist/config.d.ts
CHANGED
|
@@ -75,7 +75,7 @@ export type WeekStart = "monday" | "sunday";
|
|
|
75
75
|
* Stable identifiers of the dashboard's chart sections, in page order — the
|
|
76
76
|
* keys {@link ChartsConfig.annotations} accepts.
|
|
77
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"];
|
|
78
|
+
export declare const chartIds: readonly ["lines-of-code", "number-of-files", "commit-calendar", "commits-per-month", "churn-per-month", "direct-dependencies", "dependencies", "loose-ends", "most-suppressed-eslint-rules", "contributors"];
|
|
79
79
|
export type ChartId = (typeof chartIds)[number];
|
|
80
80
|
export type ChartsConfig = {
|
|
81
81
|
/**
|
package/dist/config.js
CHANGED
|
@@ -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}.visible{visibility:visible}.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)}.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,)}.line-through{text-decoration-line:line-through}.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-35{opacity:.35}.opacity-60{opacity:.6}.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,)}.invert{--tw-invert:invert(100%);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)}.group-hover\:line-through:is(:where(.group):hover *){text-decoration-line:line-through}.group-hover\:decoration-muted-foreground\/40:is(:where(.group):hover *){-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)}@supports (color:color-mix(in lab, red, red)){.group-hover\:decoration-muted-foreground\/40:is(:where(.group):hover *){-webkit-text-decoration-color:color-mix(in oklab, var(--text-muted) 40%, transparent);-webkit-text-decoration-color:color-mix(in oklab, var(--text-muted) 40%, transparent);-webkit-text-decoration-color:color-mix(in oklab, var(--text-muted) 40%, transparent);text-decoration-color:color-mix(in oklab, var(--text-muted) 40%, transparent)}}}.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,.hover\:text-foreground: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}}
|