git-truck 3.0.2 → 3.1.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.
Files changed (50) hide show
  1. package/build/client/assets/GitTruckInfo-CiYjIacT.js +1 -0
  2. package/build/client/assets/RepoTabs-CWt-EVgp.js +1 -0
  3. package/build/client/assets/browse-Ce7uXDK8.js +1 -0
  4. package/build/client/assets/browse-wwHpvs7Z.js +9 -0
  5. package/build/client/assets/chunk-JPUPSTYD-YBbsfvLw.js +1 -0
  6. package/build/client/assets/clear-cache-C5k9U711.js +1 -0
  7. package/build/client/assets/clear-cache-Xrw5kNtu.js +1 -0
  8. package/build/client/assets/{compare-Br3z3FUS-OODPzXmX.js → compare-Br3z3FUS-CQWcjqXQ.js} +1 -1
  9. package/build/client/assets/{entry.client-NklqTk8N.js → entry.client-lU2GATMj.js} +2 -2
  10. package/build/client/assets/jsx-runtime-s_YwNCZb.js +26 -0
  11. package/build/client/assets/manifest-3c232792.js +1 -0
  12. package/build/client/assets/{react-dom-_FKEaPb7.js → react-dom-BkDQo3BN.js} +1 -1
  13. package/build/client/assets/{root-qwQAVU-u.css → root-CaiUVml-.css} +1 -1
  14. package/build/client/assets/{root-Djh_4EO9.js → root-CpxyjKLd.js} +2 -2
  15. package/build/client/assets/ui-EnssW_g_.js +1 -0
  16. package/build/client/assets/view-DhREUKDU.js +1 -0
  17. package/build/client/assets/view._index-jmCCN9mA.js +1 -0
  18. package/build/client/assets/view.commits-D8Z4b-x3.js +1 -0
  19. package/build/client/assets/view.details-Bsu_yyiE.js +1 -0
  20. package/build/server/assets/{server-build-D47uxE8F.js → server-build-DEt84pjT.js} +1200 -1275
  21. package/build/server/index.js +1 -1
  22. package/cli.mjs +1 -1
  23. package/package.json +2 -2
  24. package/build/client/assets/FullscreenButton-CFqdBqf-.js +0 -1
  25. package/build/client/assets/GitTruckInfo-C1VOiqSW.js +0 -1
  26. package/build/client/assets/RepoTabs-xQ7LCUn1.js +0 -1
  27. package/build/client/assets/UnionAuthorsModal-D-H775E5.js +0 -1
  28. package/build/client/assets/browse-CPxPbHtx.js +0 -1
  29. package/build/client/assets/browse-CuAoODR1.js +0 -9
  30. package/build/client/assets/chunk-JPUPSTYD-XO4UPun8.js +0 -1
  31. package/build/client/assets/chunk-LFPYN7LY-CPTP6242.js +0 -26
  32. package/build/client/assets/clear-cache-82hexQD7.js +0 -1
  33. package/build/client/assets/clear-cache-CrL0weu5.js +0 -1
  34. package/build/client/assets/jsx-runtime-BVRj4wYA.js +0 -1
  35. package/build/client/assets/manifest-b5d81016.js +0 -1
  36. package/build/client/assets/styling-B8OEge8x.js +0 -1
  37. package/build/client/assets/ui-nIuMvo3S.js +0 -1
  38. package/build/client/assets/util-1G1Ez1rt.js +0 -1
  39. package/build/client/assets/view-ESMBoxIY.js +0 -1
  40. package/build/client/assets/view._index-CjsEuBV6.js +0 -1
  41. package/build/client/assets/view.commits-DP_ltd6_.js +0 -1
  42. package/build/client/assets/view.details-C6kuGyhb.js +0 -1
  43. package/build/client/assets/view.merge-authors-VwPHWL-d.js +0 -1
  44. /package/build/client/assets/{_-RagYKtT_.js → _-CT17qCl3.js} +0 -0
  45. /package/build/client/assets/{_._well-known._-DIwl00oI.js → _._well-known._-8QUD84tm.js} +0 -0
  46. /package/build/client/assets/{_index-J0jvF8An.js → _index-VLoUE117.js} +0 -0
  47. /package/build/client/assets/{_path-bRAhzWvL.js → _path-DzSNgPJP.js} +0 -0
  48. /package/build/client/assets/{commitcount-eFguKtSp.js → commitcount-B_3CeIvr.js} +0 -0
  49. /package/build/client/assets/{commits-DPfZp-l3.js → commits-DDIAHyHU.js} +0 -0
  50. /package/build/client/assets/{view.progress-B7Os3EHt.js → view.progress-CEmcNaLe.js} +0 -0
@@ -1,12 +1,12 @@
1
1
  import { PassThrough } from "stream";
2
2
  import { createReadableStreamFromReadable } from "@react-router/node";
3
- import { Await, Form, Link, Links, Meta, Outlet, Scripts, ScrollRestoration, ServerRouter, UNSAFE_withComponentProps, UNSAFE_withErrorBoundaryProps, UNSAFE_withHydrateFallbackProps, createContext, href, redirect, useFetcher, useLoaderData, useLocation, useMatch, useNavigate, useNavigation, useOutletContext, useRouteError, useRouteLoaderData, useSearchParams, useSubmit } from "react-router";
3
+ import { Await, Form, Link, Links, Meta, Outlet, Scripts, ScrollRestoration, ServerRouter, UNSAFE_withComponentProps, UNSAFE_withErrorBoundaryProps, UNSAFE_withHydrateFallbackProps, createContext, href, redirect, useFetcher, useLoaderData, useLocation, useMatch, useNavigate, useNavigation, useRouteError, useRouteLoaderData, useSearchParams, useSubmit } from "react-router";
4
4
  import { renderToPipeableStream } from "react-dom/server";
5
5
  import c from "ansi-colors";
6
6
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
7
7
  import * as React$1 from "react";
8
8
  import { Activity, Fragment as Fragment$1, Suspense, createContext as createContext$1, createElement, createRef, memo, startTransition, use, useCallback, useContext, useDeferredValue, useEffect, useId, useMemo, useReducer, useRef, useState, useSyncExternalStore, useTransition } from "react";
9
- import { mdiAccount, mdiAccountGroup, mdiAccountMultiple, mdiArrowLeft, mdiArrowRight, mdiArrowUp, mdiArrowUpBoldCircleOutline, mdiChartBubble, mdiChartTree, mdiCheckboxBlank, mdiCheckboxBlankOutline, mdiCheckboxMarked, mdiCheckboxOutline, mdiChevronDown, mdiChevronRight, mdiCircle, mdiCircleSmall, mdiClockEdit, mdiClose, mdiCog, mdiContentCut, mdiDeleteForever, mdiDiceMultipleOutline, mdiEye, mdiEyeOff, mdiEyeOffOutline, mdiFile, mdiFileCodeOutline, mdiFileOutline, mdiFileTree, mdiFilter, mdiFlaskOutline, mdiFolder, mdiFullscreen, mdiFullscreenExit, mdiHelpCircle, mdiInformationOutline, mdiKnife, mdiLabel, mdiLink, mdiMagnify, mdiMagnifyMinus, mdiMenu, mdiOpenInNew, mdiPlus, mdiPlusMinusVariant, mdiPodiumGold, mdiPulse, mdiRefresh, mdiResize, mdiRestore, mdiScaleBalance, mdiSearchWeb, mdiSort, mdiSortAscending, mdiSortDescending, mdiSourceBranch, mdiSourceCommit, mdiSourceRepository, mdiTransition, mdiUndo } from "@mdi/js";
9
+ import { mdiAccount, mdiAccountGroup, mdiAccountMultiple, mdiAccountMultipleCheck, mdiAccountMultipleMinus, mdiAccountMultiplePlus, mdiArrowLeft, mdiArrowRight, mdiArrowUp, mdiArrowUpBoldCircleOutline, mdiChartBubble, mdiChartTree, mdiCheckboxBlank, mdiCheckboxBlankOutline, mdiCheckboxMarked, mdiCheckboxOutline, mdiChevronDown, mdiChevronRight, mdiCircle, mdiCircleSmall, mdiClockEdit, mdiClose, mdiCog, mdiContentCut, mdiDeleteForever, mdiDiceMultipleOutline, mdiEye, mdiEyeOff, mdiEyeOffOutline, mdiFile, mdiFileCodeOutline, mdiFileOutline, mdiFileTree, mdiFilter, mdiFlaskOutline, mdiFolder, mdiFullscreen, mdiFullscreenExit, mdiHelpCircle, mdiInformationOutline, mdiKnife, mdiLabel, mdiLink, mdiMagnify, mdiMagnifyMinus, mdiMenu, mdiOpenInNew, mdiPlus, mdiPlusMinusVariant, mdiPodiumGold, mdiPulse, mdiRefresh, mdiResize, mdiRestore, mdiScaleBalance, mdiSearchWeb, mdiSort, mdiSortAscending, mdiSortDescending, mdiSourceBranch, mdiSourceCommit, mdiSourceRepository, mdiTransition, mdiUndo } from "@mdi/js";
10
10
  import clsx$1, { clsx } from "clsx";
11
11
  import { ArrowContainer, Popover } from "react-tiny-popover";
12
12
  import { HexColorPicker } from "react-colorful";
@@ -33,12 +33,12 @@ import { readFile, readdir } from "node:fs/promises";
33
33
  import { NuqsAdapter } from "nuqs/adapters/react-router/v7";
34
34
  import randomstring from "randomstring";
35
35
  import { createPortal } from "react-dom";
36
- import { createSerializer, parseAsBoolean, parseAsInteger, parseAsNumberLiteral, useQueryState, useQueryStates } from "nuqs";
37
- import { createLoader, createSerializer as createSerializer$1, parseAsInteger as parseAsInteger$1, parseAsString, parseAsStringLiteral } from "nuqs/server";
36
+ import { createSerializer, parseAsBoolean, parseAsInteger, parseAsNumberLiteral, parseAsStringLiteral, useQueryState, useQueryStates } from "nuqs";
37
+ import { createLoader, createSerializer as createSerializer$1, parseAsInteger as parseAsInteger$1, parseAsString, parseAsStringLiteral as parseAsStringLiteral$1 } from "nuqs/server";
38
38
  import { hierarchy, pack, partition, treemap, treemapResquarify } from "d3-hierarchy";
39
39
  import ignore from "ignore";
40
- import { Handles, Rail, Slider, Ticks, Tracks } from "react-compound-slider";
41
40
  import { Field, Label, Radio, RadioGroup } from "@headlessui/react";
41
+ import { Handles, Rail, Slider, Ticks, Tracks } from "react-compound-slider";
42
42
  import DatePicker from "react-datepicker";
43
43
  import byteSize from "byte-size";
44
44
  var __defProp = Object.defineProperty;
@@ -180,7 +180,7 @@ function handleBrowserRequest(request, responseStatusCode, responseHeaders, reac
180
180
  });
181
181
  }
182
182
  const name = "git-truck";
183
- const version = "3.0.2";
183
+ const version = "3.1.0";
184
184
  const description = "Visualizing a Git repository";
185
185
  const main = "./cli.mjs";
186
186
  const bin = "./cli.mjs";
@@ -281,7 +281,7 @@ const devDependencies = {
281
281
  "eslint-plugin-react-hooks": "^7.0.1",
282
282
  "express": "^5.2.1",
283
283
  "get-port": "^7.1.0",
284
- "globals": "^16.5.0",
284
+ "globals": "^17.3.0",
285
285
  "husky": "^9.1.7",
286
286
  "isbot": "^5.1.35",
287
287
  "knip": "^5.83.1",
@@ -375,7 +375,6 @@ function Icon({ path: path$1, size = "1rem", color = "currentColor", style = {},
375
375
  children: /* @__PURE__ */ jsx("path", { d: path$1 })
376
376
  });
377
377
  }
378
- var truck_default = "/assets/truck-BgAoc4Gr.gif";
379
378
  function Popover$1({ popoverTitle, positions = [
380
379
  "top",
381
380
  "bottom",
@@ -8746,12 +8745,12 @@ var defaultOptions = {
8746
8745
  };
8747
8746
  const getDefaultOptionsContextValue = () => defaultOptions;
8748
8747
  const CloseButton = ({ className = "", absolute = true, ...props }) => /* @__PURE__ */ jsx("button", {
8749
- className: clsx$1(className, "btn btn--text inline-grid bg-transparent text-lg leading-none hover:opacity-80", { "absolute top-2 right-2 z-10": absolute }),
8748
+ className: clsx$1(className, "btn btn--text inline-grid bg-transparent text-lg leading-none hover:text-blue-500", { "absolute top-2 right-2 z-10": absolute }),
8750
8749
  title: "Close",
8751
8750
  ...props,
8752
8751
  children: /* @__PURE__ */ jsx(Icon, {
8753
8752
  path: mdiClose,
8754
- size: 1
8753
+ size: 1.25
8755
8754
  })
8756
8755
  });
8757
8756
  const LegendDot = ({ className = "", dotColor, authorColorToChange = void 0 }) => {
@@ -8821,34 +8820,6 @@ const Code = ({ inline = false, className = "", ...props }) => /* @__PURE__ */ j
8821
8820
  className: cn("primary rounded border px-2 py-1 font-mono text-sm", inline ? "inline-block" : "block", { "select-all": inline }, className),
8822
8821
  ...props
8823
8822
  });
8824
- function CheckboxWithLabel({ children, checked, onChange, className = "", checkedIcon = mdiCheckboxOutline, uncheckedIcon = mdiCheckboxBlankOutline, ...props }) {
8825
- const [isTransitioning, startTransition$1] = useTransition();
8826
- return /* @__PURE__ */ jsxs("label", {
8827
- className: `label flex w-full items-center justify-start gap-2 ${className}`,
8828
- ...props,
8829
- children: [
8830
- /* @__PURE__ */ jsxs("span", {
8831
- className: "flex grow items-center gap-2",
8832
- children: [children, isTransitioning ? /* @__PURE__ */ jsx("img", {
8833
- src: truck_default,
8834
- alt: "...",
8835
- className: "h-5"
8836
- }) : ""]
8837
- }),
8838
- /* @__PURE__ */ jsx(Icon, {
8839
- className: "place-self-end",
8840
- path: checked ? checkedIcon : uncheckedIcon,
8841
- size: 1
8842
- }),
8843
- /* @__PURE__ */ jsx("input", {
8844
- type: "checkbox",
8845
- defaultChecked: checked,
8846
- className: "hidden",
8847
- onChange: (e) => startTransition$1(() => onChange(e))
8848
- })
8849
- ]
8850
- });
8851
- }
8852
8823
  const LegendBarIndicator = ({ visible, offset }) => {
8853
8824
  return /* @__PURE__ */ jsx("div", {
8854
8825
  className: clsx$1("absolute top-1/2 w-min -translate-x-1/2 -translate-y-1/2 transition-all", { "opacity-0": !visible }),
@@ -8864,18 +8835,6 @@ function ClientOnly({ children, fallback = null }) {
8864
8835
  if (!useIsClient()) return fallback;
8865
8836
  return children();
8866
8837
  }
8867
- function FullscreenButton() {
8868
- const { isFullscreen, toggleFullscreen } = useFullscreen(() => document.documentElement);
8869
- return /* @__PURE__ */ jsx("button", {
8870
- className: cn("btn aspect-square p-1", { "btn--primary": isFullscreen }),
8871
- title: isFullscreen ? "Exit fullscreen" : "Enter fullscreen",
8872
- onClick: toggleFullscreen,
8873
- children: /* @__PURE__ */ jsx(Icon, {
8874
- path: isFullscreen ? mdiFullscreenExit : mdiFullscreen,
8875
- size: 1
8876
- })
8877
- });
8878
- }
8879
8838
  var DB = class DB {
8880
8839
  connectionPromise;
8881
8840
  repoSanitized;
@@ -10332,7 +10291,8 @@ var InstanceManager = class {
10332
10291
  this.instances = /* @__PURE__ */ new Map();
10333
10292
  }
10334
10293
  };
10335
- var truck_default$1 = "data:image/png;base64,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";
10294
+ var truck_default = "data:image/png;base64,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";
10295
+ var truck_default$1 = "/assets/truck-BgAoc4Gr.gif";
10336
10296
  function UpdateNotifier({ installedVersion, latestVersion: latestVersion$1 }) {
10337
10297
  const isExperimental = installedVersion.includes("0.0.0");
10338
10298
  return /* @__PURE__ */ jsxs(Popover$1, {
@@ -10390,7 +10350,7 @@ function GitTruckInfo({ className = "", installedVersion, latestVersion: latestV
10390
10350
  installedVersion,
10391
10351
  latestVersion: latestVersion$1
10392
10352
  }) : /* @__PURE__ */ jsx("img", {
10393
- src: loading ? truck_default : truck_default$1,
10353
+ src: loading ? truck_default$1 : truck_default,
10394
10354
  alt: "Git Truck Logo",
10395
10355
  className: "tertiary size-10 rounded-full border p-1"
10396
10356
  }), /* @__PURE__ */ jsxs("div", {
@@ -10498,7 +10458,7 @@ function ErrorPage({ className, message, noTruck = false, children }) {
10498
10458
  className: "flex max-w-2xl flex-col items-center gap-3 text-center",
10499
10459
  children: [
10500
10460
  !noTruck ? /* @__PURE__ */ jsx("img", {
10501
- src: truck_default$1,
10461
+ src: truck_default,
10502
10462
  alt: "Git Truck",
10503
10463
  className: "w-full max-w-sm min-w-0"
10504
10464
  }) : null,
@@ -10582,19 +10542,6 @@ const ErrorBoundary = UNSAFE_withErrorBoundaryProps(() => {
10582
10542
  })
10583
10543
  }) : null] });
10584
10544
  });
10585
- var FullscreenButton_exports = /* @__PURE__ */ __export({ FullscreenButton: () => FullscreenButton$1 }, 1);
10586
- function FullscreenButton$1() {
10587
- const { isFullscreen, toggleFullscreen } = useFullscreen(() => document.documentElement);
10588
- return /* @__PURE__ */ jsx("button", {
10589
- className: cn("btn aspect-square p-1", { "btn--primary": isFullscreen }),
10590
- title: isFullscreen ? "Exit fullscreen" : "Enter fullscreen",
10591
- onClick: toggleFullscreen,
10592
- children: /* @__PURE__ */ jsx(Icon, {
10593
- path: isFullscreen ? mdiFullscreenExit : mdiFullscreen,
10594
- size: 1
10595
- })
10596
- });
10597
- }
10598
10545
  var ____well_known_$_exports = /* @__PURE__ */ __export({ loader: () => loader$10 }, 1);
10599
10546
  function loader$10() {
10600
10547
  return "";
@@ -10778,7 +10725,7 @@ const browseSearchParamsConfig = {
10778
10725
  path: parseAsString.withOptions({ shallow: false }),
10779
10726
  count: parseAsNumberLiteral(COUNT_OPTIONS).withOptions({ shallow: false }).withDefault(DEFAULT_COUNT),
10780
10727
  offset: parseAsInteger$1.withOptions({ shallow: false }).withDefault(DEFAULT_OFFSET),
10781
- sort: parseAsStringLiteral([
10728
+ sort: parseAsStringLiteral$1([
10782
10729
  "most-recent",
10783
10730
  "least-recent",
10784
10731
  "asc",
@@ -11911,415 +11858,283 @@ const HiddenFiles = memo(function HiddenFiles$1() {
11911
11858
  })
11912
11859
  });
11913
11860
  });
11914
- function AuthorOptions({ showUnionAuthorsModal }) {
11915
- const transitionState = useNavigation();
11916
- const action$3 = href("/view");
11917
- return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs("div", {
11918
- className: "mt-2 grid w-full grid-cols-[1fr_1fr] gap-2",
11919
- children: [/* @__PURE__ */ jsxs("button", {
11920
- className: "btn",
11921
- onClick: showUnionAuthorsModal,
11922
- children: [/* @__PURE__ */ jsx(Icon, { path: mdiAccountMultiple }), "Group authors"]
11923
- }), /* @__PURE__ */ jsxs(Form, {
11924
- method: "post",
11925
- action: action$3,
11926
- children: [/* @__PURE__ */ jsx("input", {
11927
- type: "hidden",
11928
- name: "rerollColors",
11929
- value: ""
11930
- }), /* @__PURE__ */ jsxs("button", {
11931
- className: "btn w-full",
11932
- type: "submit",
11933
- disabled: transitionState.state !== "idle",
11934
- children: [/* @__PURE__ */ jsx(Icon, { path: mdiDiceMultipleOutline }), "Shuffle colors"]
11935
- })]
11936
- })]
11937
- }) });
11938
- }
11939
- function GradientLegend({ hoveredObject }) {
11940
- const { metricType } = useOptions();
11941
- const [metricsData] = useMetrics();
11942
- const metricCache = metricsData.get(metricType);
11943
- if (metricCache === void 0) throw new Error("Metric cache is undefined");
11944
- const { minValue, maxValue, minColor, maxColor } = metricCache.legend;
11945
- const { clickedObject } = useClickedObject();
11946
- const path$1 = clickedObject?.path ?? hoveredObject?.path ?? null;
11947
- const color = path$1 ? metricCache.colormap.get(path$1) : null;
11948
- let blobLightness = color ? getLightness(color) : -1;
11949
- if (color === "#c0c0c0") blobLightness = -1;
11950
- const offset = useMemo(() => {
11951
- const min = getLightness(minColor);
11952
- const diff = getLightness(maxColor) - min;
11953
- if (diff === 0) return 1;
11954
- return (blobLightness - min) / diff;
11955
- }, [
11956
- blobLightness,
11957
- maxColor,
11958
- minColor
11959
- ]);
11960
- const visible = path$1 !== null;
11961
- const midValue = Math.round((maxValue - minValue) / 2);
11962
- return /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("div", {
11963
- className: "relative h-4 rounded-sm",
11964
- style: { backgroundImage: `linear-gradient(to right, ${minColor}, ${maxColor})` },
11965
- children: /* @__PURE__ */ jsx(LegendBarIndicator, {
11966
- offset: offset * 100,
11967
- visible
11968
- })
11969
- }), /* @__PURE__ */ jsxs("div", {
11970
- className: "flex justify-between",
11861
+ function CheckboxWithLabel({ children, checked, onChange, className = "", checkedIcon = mdiCheckboxOutline, uncheckedIcon = mdiCheckboxBlankOutline, ...props }) {
11862
+ const [isTransitioning, startTransition$1] = useTransition();
11863
+ return /* @__PURE__ */ jsxs("label", {
11864
+ className: `label group flex w-full items-center justify-start gap-2 hover:text-blue-500 ${className}`,
11865
+ ...props,
11971
11866
  children: [
11972
- /* @__PURE__ */ jsx("span", {
11973
- className: "font-bold",
11974
- title: minValue.toLocaleString(),
11975
- children: numToFriendlyString(minValue)
11867
+ /* @__PURE__ */ jsxs("span", {
11868
+ className: "flex grow items-center gap-2",
11869
+ children: [children, isTransitioning ? /* @__PURE__ */ jsx("img", {
11870
+ src: truck_default$1,
11871
+ alt: "...",
11872
+ className: "h-5"
11873
+ }) : ""]
11976
11874
  }),
11977
- /* @__PURE__ */ jsx("span", {
11978
- className: "absolute left-1/2 -translate-x-1/2 font-bold",
11979
- title: midValue.toLocaleString(),
11980
- children: numToFriendlyString(midValue)
11875
+ /* @__PURE__ */ jsx(Icon, {
11876
+ className: "place-self-end group-hover:text-blue-500",
11877
+ path: checked ? checkedIcon : uncheckedIcon,
11878
+ size: 1
11981
11879
  }),
11982
- /* @__PURE__ */ jsx("span", {
11983
- className: "font-bold",
11984
- title: maxValue.toLocaleString(),
11985
- children: numToFriendlyString(maxValue)
11880
+ /* @__PURE__ */ jsx("input", {
11881
+ type: "checkbox",
11882
+ defaultChecked: checked,
11883
+ className: "hidden",
11884
+ onChange: (e) => startTransition$1(() => onChange(e))
11986
11885
  })
11987
11886
  ]
11988
- })] });
11989
- }
11990
- function Handle({ domain: [min, max], children, disabled, handle, handleType = "round", title, getHandleProps, className, onClick }) {
11991
- return /* @__PURE__ */ jsx("button", {
11992
- className: cn("absolute z-10 flex size-5 -translate-x-1/2 -translate-y-1.5 place-content-center disabled:grayscale", { "size-5 rounded-full": handleType === "round" }, disabled ? "cursor-progress" : "cursor-col-resize", className),
11993
- role: "slider",
11994
- "aria-disabled": disabled,
11995
- "aria-valuemin": min,
11996
- "aria-valuemax": max,
11997
- "aria-valuenow": handle.value,
11998
- title,
11999
- style: { left: `${handle.percent}%` },
12000
- onClick,
12001
- ...getHandleProps(handle.id),
12002
- children: /* @__PURE__ */ jsx("div", {
12003
- className: cn("btn--primary", {
12004
- "size-5 rounded-full": handleType === "round",
12005
- "h-5 w-0.5": handleType === "square"
12006
- }),
12007
- children
12008
- })
12009
- });
12010
- }
12011
- function SliderRail({ getRailProps, className = "", children }) {
12012
- return /* @__PURE__ */ jsx("div", {
12013
- className: cn("bg-blue-secondary/20 absolute h-2 w-full cursor-pointer", {}, className),
12014
- ...getRailProps(),
12015
- children
12016
11887
  });
12017
11888
  }
12018
- function Track({ source, target, trackType = "round", getTrackProps, disabled }) {
12019
- return /* @__PURE__ */ jsx("div", {
12020
- className: cn("btn btn--primary absolute h-2 cursor-pointer p-0", disabled ? "bg-primary-text-dark dark:bg-primary-text" : "bg-blue-primary", {
12021
- "rounded-full": trackType === "round",
12022
- "rounded-none": trackType === "square"
12023
- }),
12024
- style: {
12025
- left: `${source.percent}%`,
12026
- width: `${target.percent - source.percent}%`
11889
+ function UnionAuthorsModal() {
11890
+ const { databaseInfo } = useData();
11891
+ const submit = useSubmit();
11892
+ const { authors } = databaseInfo;
11893
+ const authorUnions = databaseInfo.authorUnions;
11894
+ const [selectedAuthors, setSelectedAuthors] = useState([]);
11895
+ const [filter, setFilter] = useState("");
11896
+ const navigationData = useNavigation();
11897
+ const [, authorColors] = useMetrics();
11898
+ const [, startTransition$1] = useTransition();
11899
+ const location$1 = useLocation();
11900
+ const flattedUnionedAuthors = authorUnions.reduce((acc, union) => {
11901
+ return [...acc, ...union];
11902
+ }, []).sort(stringSorter);
11903
+ function ungroup(groupToUnGroup) {
11904
+ const newAuthorUnions = authorUnions.filter((_, i) => i !== groupToUnGroup);
11905
+ const form = new FormData();
11906
+ form.append("unionedAuthors", JSON.stringify(newAuthorUnions));
11907
+ submit(form, {
11908
+ action: href("/view") + location$1.search,
11909
+ method: "post"
11910
+ });
11911
+ }
11912
+ function ungroupAll() {
11913
+ const form = new FormData();
11914
+ form.append("unionedAuthors", JSON.stringify([]));
11915
+ submit(form, {
11916
+ action: href("/view") + location$1.search,
11917
+ method: "post"
11918
+ });
11919
+ }
11920
+ function groupSelectedAuthors() {
11921
+ if (selectedAuthors.length < 2) return;
11922
+ const form = new FormData();
11923
+ form.append("unionedAuthors", JSON.stringify([...authorUnions, selectedAuthors]));
11924
+ submit(form, {
11925
+ action: href("/view") + location$1.search,
11926
+ method: "post"
11927
+ });
11928
+ setSelectedAuthors([]);
11929
+ }
11930
+ function makePrimaryAlias(alias, groupIndex) {
11931
+ const newAuthorUnions = authorUnions.map((group, i) => {
11932
+ if (i === groupIndex) return [alias, ...group.filter((a) => a !== alias)];
11933
+ return group;
11934
+ });
11935
+ const form = new FormData();
11936
+ form.append("unionedAuthors", JSON.stringify(newAuthorUnions));
11937
+ submit(form, {
11938
+ action: href("/view") + location$1.search,
11939
+ method: "post"
11940
+ });
11941
+ }
11942
+ const disabled = navigationData.state !== "idle";
11943
+ const ungroupedAuthorsSorted = authors.filter((a) => !flattedUnionedAuthors.includes(a)).slice(0).sort(stringSorter);
11944
+ const getColorFromDisplayName = (displayName) => authorColors.get(displayName) ?? "#333";
11945
+ const ungroupedAuthorsFiltered = ungroupedAuthorsSorted.filter((author) => author.toLowerCase().includes(filter.toLowerCase()));
11946
+ const ungroupedAuthorsEntries = ungroupedAuthorsFiltered.map((author) => /* @__PURE__ */ jsx(CheckboxWithLabel, {
11947
+ className: "hover:opacity-70",
11948
+ checked: selectedAuthors.includes(author),
11949
+ onChange: (e) => {
11950
+ setSelectedAuthors(e.target?.checked ? [...selectedAuthors, author] : selectedAuthors.filter((a) => a !== author));
12027
11951
  },
12028
- ...getTrackProps()
12029
- });
12030
- }
12031
- var alignToJustify = {
12032
- left: "start",
12033
- center: "center",
12034
- right: "end"
12035
- };
12036
- function LabeledTicks({ valueMap, titleMap = valueMap, onTop = false }) {
12037
- const count = Object.entries(valueMap).length;
12038
- return /* @__PURE__ */ jsx(Ticks, {
12039
- count,
12040
- children: ({ ticks }) => /* @__PURE__ */ jsx("div", {
12041
- className: "grid grid-flow-col grid-cols-3 grid-rows-[1fr_auto] pt-4",
12042
- children: ticks.map((tick) => {
12043
- const tickLabelInfo = valueMap[tick.value * 100];
12044
- const align = tick.value === 0 ? "left" : tick.value === 1 ? "right" : "center";
12045
- const Tick$1 = /* @__PURE__ */ jsx("div", {
12046
- className: `flex justify-${tickLabelInfo ? alignToJustify[align] : "center"}`,
12047
- children: /* @__PURE__ */ jsx("div", { className: "h-2 w-px bg-gray-950 dark:bg-gray-50" })
12048
- });
12049
- const Label$1 = /* @__PURE__ */ jsx("div", {
12050
- title: titleMap[tick.value * 100],
12051
- className: "truncate text-xs",
12052
- style: { textAlign: align },
12053
- children: tickLabelInfo
12054
- });
12055
- return /* @__PURE__ */ jsxs(Fragment, { children: [onTop ? Label$1 : Tick$1, onTop ? Tick$1 : Label$1] }, tick.id);
12056
- })
12057
- })
12058
- });
12059
- }
12060
- function TicksByCount({ className = "", count, tickToLabel, onTop = true, align, below = false }) {
12061
- return /* @__PURE__ */ jsx("div", {
12062
- className: cn("grid grid-flow-col grid-cols-(--cols) gap-1 text-xs", {
12063
- "grid-rows-[auto]": !below && !onTop,
12064
- "grid-rows-[auto_auto]": below !== onTop,
12065
- "grid-rows-[auto_auto_auto]": below && onTop
12066
- }, className),
12067
- style: { "--cols": `repeat(${count}, minmax(0, 1fr))` },
12068
- children: Array.from({ length: count }).map((_, i) => {
12069
- const tick = i / (count - 1);
12070
- const tickLabelInfo = tickToLabel(tick, i);
12071
- const alignment = align ?? (tick === 0 ? "left" : tick === 1 ? "right" : "center");
12072
- const justification = tickLabelInfo ? alignToJustify[alignment] : "center";
12073
- return /* @__PURE__ */ jsxs(Fragment, { children: [
12074
- onTop ? /* @__PURE__ */ jsx(Tick, { justification }) : null,
12075
- /* @__PURE__ */ jsx("div", {
12076
- className: cn(`text-${alignment}`),
12077
- children: tickLabelInfo === 0 ? "" : tickLabelInfo
12078
- }),
12079
- below ? /* @__PURE__ */ jsx(Tick, { justification }) : null
12080
- ] }, i);
12081
- })
12082
- });
12083
- }
12084
- const Tick = ({ className = "", justification = "start" }) => /* @__PURE__ */ jsx("div", {
12085
- className: cn("flex", `justify-${justification}`, className),
12086
- children: /* @__PURE__ */ jsx("div", { className: "h-2 w-px bg-gray-950 dark:bg-gray-50" })
12087
- });
12088
- function SegmentLegend({ hoveredObject }) {
12089
- const { metricType } = useOptions();
12090
- const [metricsData] = useMetrics();
12091
- const { steps, textGenerator, colorGenerator, offsetStepCalc } = metricsData.get(metricType).legend;
12092
- const width = 100 / steps;
12093
- let arrowVisible = false;
12094
- let arrowOffset = 0;
12095
- const clickedObject = useClickedObject().clickedObject ?? hoveredObject ?? null;
12096
- if (isBlob(clickedObject)) {
12097
- arrowVisible = true;
12098
- arrowOffset = width / 2 + width * offsetStepCalc(clickedObject);
12099
- }
12100
- return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx("div", {
12101
- className: "relative",
12102
11952
  children: /* @__PURE__ */ jsxs("div", {
12103
- className: "relative flex text-xs whitespace-nowrap",
12104
- children: [[...Array(steps)].map((_, i) => {
12105
- return steps >= 4 ? /* @__PURE__ */ jsx(MetricSegment, {
12106
- className: i === 0 ? "rounded-l-sm" : i === steps - 1 ? "rounded-r-sm" : "",
12107
- width,
12108
- color: colorGenerator(i),
12109
- text: textGenerator(i),
12110
- top: i % 2 === 0
12111
- }, i) : /* @__PURE__ */ jsx(TopMetricSegment, {
12112
- width,
12113
- color: colorGenerator(i),
12114
- text: textGenerator(i)
12115
- }, i);
12116
- }), /* @__PURE__ */ jsx(LegendBarIndicator, {
12117
- offset: arrowOffset,
12118
- visible: arrowVisible
12119
- })]
11953
+ className: "inline-flex flex-row place-items-center gap-2",
11954
+ children: [/* @__PURE__ */ jsx(LegendDot, { dotColor: getColorFromDisplayName(author) }), author]
12120
11955
  })
12121
- }) });
12122
- }
12123
- function MetricSegment({ className = "", width, color, text, top }) {
12124
- if (top) return /* @__PURE__ */ jsxs("div", {
12125
- className: "flex flex-col",
12126
- style: { width: `${width}%` },
12127
- children: [
12128
- /* @__PURE__ */ jsx("div", {
12129
- className: "h-5 truncate text-left",
12130
- title: text,
12131
- children: text
12132
- }),
12133
- /* @__PURE__ */ jsx(Tick, { className: "ml-1" }),
12134
- /* @__PURE__ */ jsx("div", {
12135
- className: cn("h-4", className),
12136
- style: { backgroundColor: color }
12137
- }),
12138
- /* @__PURE__ */ jsx(Tick, { className: "invisible" })
12139
- ]
11956
+ }, author));
11957
+ const groupedAuthorsEntries = authorUnions.map((aliasGroup, aliasGroupIndex) => {
11958
+ const displayName = aliasGroup[0];
11959
+ const disabled$1 = navigationData.state !== "idle";
11960
+ return /* @__PURE__ */ jsxs("div", {
11961
+ className: "card group m-0 flex h-full flex-col p-2",
11962
+ children: [
11963
+ /* @__PURE__ */ jsxs("div", {
11964
+ className: "inline-flex flex-row place-items-center gap-2",
11965
+ children: [/* @__PURE__ */ jsx(LegendDot, { dotColor: getColorFromDisplayName(displayName) }), /* @__PURE__ */ jsx("b", {
11966
+ className: "truncate",
11967
+ title: displayName,
11968
+ children: displayName
11969
+ })]
11970
+ }),
11971
+ aliasGroup.slice(1).sort(stringSorter).map((alias) => /* @__PURE__ */ jsx(AliasEntry, {
11972
+ alias,
11973
+ disabled: disabled$1,
11974
+ onClick: () => makePrimaryAlias(alias, aliasGroupIndex)
11975
+ }, alias)),
11976
+ /* @__PURE__ */ jsx("div", { className: "grow" }),
11977
+ /* @__PURE__ */ jsxs("div", {
11978
+ className: "flex items-end justify-end gap-2 opacity-0 transition-opacity duration-200 group-hover:opacity-100",
11979
+ children: [/* @__PURE__ */ jsxs(Form, {
11980
+ action: href("/view") + location$1.search,
11981
+ method: "post",
11982
+ children: [/* @__PURE__ */ jsx("input", {
11983
+ type: "hidden",
11984
+ name: "unionedAuthors",
11985
+ value: JSON.stringify(authorUnions)
11986
+ }), /* @__PURE__ */ jsx("button", {
11987
+ className: "btn",
11988
+ title: "Add selected authors to this group",
11989
+ disabled: disabled$1 || selectedAuthors.length === 0,
11990
+ onClick: () => {
11991
+ const newAuthorUnions = authorUnions.map(([displayName$1, ...group], i) => {
11992
+ if (i === aliasGroupIndex) return [
11993
+ displayName$1,
11994
+ ...group,
11995
+ ...selectedAuthors
11996
+ ];
11997
+ return [displayName$1, ...group];
11998
+ });
11999
+ const form = new FormData();
12000
+ form.append("unionedAuthors", JSON.stringify(newAuthorUnions));
12001
+ submit(form, {
12002
+ action: href("/view") + location$1.search,
12003
+ method: "post"
12004
+ });
12005
+ setSelectedAuthors([]);
12006
+ },
12007
+ children: "Add selected"
12008
+ })]
12009
+ }), /* @__PURE__ */ jsx("button", {
12010
+ className: "btn",
12011
+ title: "Ungroup",
12012
+ disabled: disabled$1,
12013
+ onClick: () => ungroup(aliasGroupIndex),
12014
+ children: "Ungroup"
12015
+ })]
12016
+ })
12017
+ ]
12018
+ }, aliasGroupIndex);
12140
12019
  });
12141
- else return /* @__PURE__ */ jsxs("div", {
12142
- className: "flex flex-col",
12143
- style: { width: `${width}%` },
12020
+ return /* @__PURE__ */ jsxs("div", {
12021
+ className: "m-auto grid h-full max-h-200 w-full max-w-(--breakpoint-2xl) grid-cols-[1fr_1fr] grid-rows-[max-content_1fr] gap-2 overflow-hidden bg-gray-100 p-4",
12144
12022
  children: [
12145
- /* @__PURE__ */ jsx("div", {
12146
- className: "invisible h-5 text-left",
12147
- children: text
12023
+ /* @__PURE__ */ jsxs("div", {
12024
+ className: "flex justify-between",
12025
+ children: [/* @__PURE__ */ jsxs("h3", {
12026
+ className: "grow text-center text-lg font-bold",
12027
+ children: [
12028
+ "Ungrouped Authors (",
12029
+ ungroupedAuthorsSorted.length,
12030
+ ")"
12031
+ ]
12032
+ }), /* @__PURE__ */ jsx("div", {
12033
+ className: "flex justify-end gap-2",
12034
+ children: /* @__PURE__ */ jsxs("button", {
12035
+ className: "btn btn--primary justify-self-end",
12036
+ title: "Group the selected authors",
12037
+ disabled: disabled || selectedAuthors.length < 2,
12038
+ onClick: groupSelectedAuthors,
12039
+ children: [/* @__PURE__ */ jsx(Icon, {
12040
+ path: mdiAccountMultiplePlus,
12041
+ size: 1
12042
+ }), "Create group"]
12043
+ })
12044
+ })]
12148
12045
  }),
12149
- /* @__PURE__ */ jsx(Tick, { className: "invisible" }),
12150
- /* @__PURE__ */ jsx("div", {
12151
- className: cn("h-4", className),
12152
- style: { backgroundColor: color }
12046
+ /* @__PURE__ */ jsxs("div", {
12047
+ className: "flex justify-between",
12048
+ children: [/* @__PURE__ */ jsxs("h3", {
12049
+ className: "grow text-center text-lg font-bold",
12050
+ children: [
12051
+ "Author Groups (",
12052
+ groupedAuthorsEntries.length,
12053
+ ")"
12054
+ ]
12055
+ }), /* @__PURE__ */ jsx("div", {
12056
+ className: "flex justify-end gap-4",
12057
+ children: /* @__PURE__ */ jsxs("button", {
12058
+ className: "btn btn--danger",
12059
+ disabled: disabled || authorUnions.length === 0,
12060
+ onClick: () => {
12061
+ if (confirm("Are you sure you want to ungroup all grouped authors?")) ungroupAll();
12062
+ },
12063
+ children: [/* @__PURE__ */ jsx(Icon, {
12064
+ path: mdiAccountMultipleMinus,
12065
+ size: 1
12066
+ }), "Ungroup all"]
12067
+ })
12068
+ })]
12153
12069
  }),
12154
- /* @__PURE__ */ jsx(Tick, {}),
12155
- /* @__PURE__ */ jsx("div", {
12156
- className: "h-5 truncate text-left",
12157
- children: text
12158
- })
12159
- ]
12160
- });
12161
- }
12162
- function TopMetricSegment({ width, color, text }) {
12163
- return /* @__PURE__ */ jsxs("div", {
12164
- className: "flex flex-col",
12165
- style: { width: `${width}%` },
12166
- children: [
12167
12070
  /* @__PURE__ */ jsx("div", {
12168
- className: "h-5 truncate text-left",
12169
- children: text
12071
+ className: "max-h-full overflow-y-scroll",
12072
+ children: /* @__PURE__ */ jsxs("div", {
12073
+ className: "h-fill flex min-h-0 flex-col rounded-md dark:bg-gray-700",
12074
+ children: [/* @__PURE__ */ jsxs("div", {
12075
+ className: "sticky top-0 z-10 flex gap-2 bg-gray-100 p-2 dark:bg-gray-700",
12076
+ children: [
12077
+ /* @__PURE__ */ jsx("input", {
12078
+ className: "input min-w-0",
12079
+ type: "search",
12080
+ placeholder: "Filter...",
12081
+ disabled: ungroupedAuthorsSorted.length === 0,
12082
+ onChange: (e) => startTransition$1(() => setFilter(e.target.value))
12083
+ }),
12084
+ /* @__PURE__ */ jsx("button", {
12085
+ disabled: disabled || selectedAuthors.length === 0,
12086
+ className: "btn btn--outlined w-max grow",
12087
+ title: "Clear selection",
12088
+ onClick: () => setSelectedAuthors([]),
12089
+ children: "Clear"
12090
+ }),
12091
+ /* @__PURE__ */ jsx("button", {
12092
+ disabled: ungroupedAuthorsSorted.length === 0,
12093
+ className: "btn btn--outlined w-max grow",
12094
+ title: "Clear selection",
12095
+ onClick: () => selectedAuthors.length === ungroupedAuthorsFiltered.length ? setSelectedAuthors([]) : setSelectedAuthors((selected) => Array.from(new Set([...selected, ...ungroupedAuthorsFiltered]))),
12096
+ children: selectedAuthors.length === ungroupedAuthorsFiltered.length ? "Deselect all" : "Select all"
12097
+ })
12098
+ ]
12099
+ }), /* @__PURE__ */ jsx("div", {
12100
+ className: "min-h-fill max-h-full overflow-y-auto p-2",
12101
+ children: ungroupedAuthorsEntries.length > 0 ? ungroupedAuthorsEntries : /* @__PURE__ */ jsx("p", {
12102
+ className: "place-self-center",
12103
+ children: filter.length > 0 ? "No authors found" : "All authors have been grouped"
12104
+ })
12105
+ })]
12106
+ })
12170
12107
  }),
12171
- /* @__PURE__ */ jsx(Tick, {}),
12172
12108
  /* @__PURE__ */ jsx("div", {
12173
- className: "h-5",
12174
- style: { backgroundColor: color }
12109
+ className: "min-h-0 overflow-y-auto",
12110
+ children: /* @__PURE__ */ jsx("div", {
12111
+ className: "grid h-min min-h-0 grid-cols-1 gap-4 rounded-md bg-white p-4 shadow-sm lg:grid-cols-2 xl:grid-cols-3 dark:bg-gray-700",
12112
+ children: authorUnions.length > 0 ? groupedAuthorsEntries : /* @__PURE__ */ jsx("p", {
12113
+ className: "place-self-center",
12114
+ children: "No authors have been grouped yet"
12115
+ })
12116
+ })
12175
12117
  })
12176
12118
  ]
12177
12119
  });
12120
+ function AliasEntry({ alias, onClick, disabled: disabled$1 }) {
12121
+ return /* @__PURE__ */ jsxs("button", {
12122
+ className: "btn flex grid-flow-col gap-2 text-sm [&:hover>svg]:opacity-50 [&>svg]:opacity-0",
12123
+ disabled: disabled$1,
12124
+ title: "Make display name for this grouping",
12125
+ onClick,
12126
+ children: [/* @__PURE__ */ jsx(Icon, {
12127
+ path: mdiArrowUp,
12128
+ size: .75
12129
+ }), /* @__PURE__ */ jsx("label", {
12130
+ title: alias,
12131
+ className: "label truncate",
12132
+ children: alias
12133
+ })]
12134
+ }, alias);
12135
+ }
12178
12136
  }
12179
- function PercentageSlider({ className = "" }) {
12180
- const { dominantAuthorCutoff, setDominantAuthorCutoff } = useOptions();
12181
- const [displayPercentage, setDisplayPercentage] = useState(dominantAuthorCutoff);
12182
- const domain = [0, 100];
12183
- return /* @__PURE__ */ jsxs("div", {
12184
- className: cn("grid grid-cols-[calc(11*var(--spacing))_1fr] px-3", className),
12185
- children: [/* @__PURE__ */ jsxs("p", {
12186
- title: "Min percentage of changes a file's top author must account for to be colored.",
12187
- children: [displayPercentage, "%"]
12188
- }), /* @__PURE__ */ jsx("div", {
12189
- className: "relative",
12190
- title: "Adjust the dominant author percentage cutoff",
12191
- children: /* @__PURE__ */ jsxs(Slider, {
12192
- mode: 1,
12193
- step: 1,
12194
- domain,
12195
- values: [displayPercentage],
12196
- onChange: (e) => {
12197
- setDominantAuthorCutoff(e[0]);
12198
- startTransition(() => {
12199
- setDisplayPercentage(e[0]);
12200
- });
12201
- },
12202
- onUpdate: (e) => {
12203
- startTransition(() => {
12204
- setDisplayPercentage(e[0]);
12205
- });
12206
- },
12207
- children: [
12208
- /* @__PURE__ */ jsx(Rail, { children: SliderRail }),
12209
- /* @__PURE__ */ jsx(Handles, { children: ({ handles, getHandleProps }) => /* @__PURE__ */ jsx(Fragment$1, { children: handles.map((handle) => /* @__PURE__ */ jsx(Handle, {
12210
- handle,
12211
- domain,
12212
- getHandleProps
12213
- }, handle.id)) }) }),
12214
- /* @__PURE__ */ jsx(Tracks, {
12215
- right: false,
12216
- children: ({ tracks, getTrackProps }) => /* @__PURE__ */ jsx(Fragment$1, { children: tracks.map(({ id, ...props }) => /* @__PURE__ */ createElement(Track, {
12217
- ...props,
12218
- key: id,
12219
- backgroundColor: noEntryColor,
12220
- getTrackProps
12221
- })) })
12222
- }),
12223
- /* @__PURE__ */ jsx(LabeledTicks, { valueMap: {
12224
- 0: "Top author",
12225
- 50: "Majority author",
12226
- 100: "Single author"
12227
- } })
12228
- ]
12229
- })
12230
- })]
12231
- });
12232
- }
12233
- function Legend({ hoveredObject, showUnionAuthorsModal }) {
12234
- const { sizeMetric, metricType } = useOptions();
12235
- const [metricsData] = useMetrics();
12236
- const deferredHoveredObject = useDeferredValue(hoveredObject);
12237
- const metricCache = metricsData.get(metricType) ?? void 0;
12238
- const legend = useMemo(() => {
12239
- if (metricCache === void 0) return null;
12240
- switch (getMetricLegendType(metricType)) {
12241
- case "POINT": return /* @__PURE__ */ jsx(PointLegend, {});
12242
- case "GRADIENT": return /* @__PURE__ */ jsx(GradientLegend, { hoveredObject: deferredHoveredObject });
12243
- case "SEGMENTS": return /* @__PURE__ */ jsx(SegmentLegend, { hoveredObject: deferredHoveredObject });
12244
- }
12245
- }, [
12246
- metricCache,
12247
- deferredHoveredObject,
12248
- metricType
12249
- ]);
12250
- if (legend === null) return null;
12251
- return /* @__PURE__ */ jsxs(Fragment, { children: [
12252
- /* @__PURE__ */ jsx("h3", {
12253
- className: "card__subtitle",
12254
- children: "Size legend"
12255
- }),
12256
- /* @__PURE__ */ jsx("p", {
12257
- className: "mb-2 text-sm",
12258
- children: sizeMetricLegendDescriptions[sizeMetric]
12259
- }),
12260
- /* @__PURE__ */ jsx("h3", {
12261
- className: "card__subtitle",
12262
- children: "Color legend"
12263
- }),
12264
- /* @__PURE__ */ jsx("p", {
12265
- className: "mb-4 text-sm",
12266
- children: colorMetricDescriptions[metricType]
12267
- }),
12268
- metricType === "TOP_CONTRIBUTOR" ? /* @__PURE__ */ jsx(PercentageSlider, { className: "my-4" }) : null,
12269
- legend,
12270
- metricType === "TOP_CONTRIBUTOR" ? /* @__PURE__ */ jsx(AuthorOptions, { showUnionAuthorsModal }) : null
12271
- ] });
12272
- }
12273
- function LoadingIndicator({ className = "", showProgress = false, fetchProgress = true, loadingText }) {
12274
- const [path$1] = useQueryState("path");
12275
- const fetcher = useFetcher();
12276
- useEffect(() => {
12277
- if (fetcher.state === "idle" && showProgress && fetchProgress) fetcher.load(href("/view/progress") + viewSerializer({ path: path$1 }));
12278
- }, [
12279
- fetchProgress,
12280
- fetcher,
12281
- fetcher.state,
12282
- path$1,
12283
- showProgress
12284
- ]);
12285
- const [progressText, progress] = useMemo(() => {
12286
- if (!fetcher.data) return ["Loading truck...", 0];
12287
- const { progress: progress$1, analyzationStatus } = fetcher.data;
12288
- switch (analyzationStatus) {
12289
- case "Starting": return ["Loading truck...", 0];
12290
- case "GeneratingChart": return ["Unloading truck...", 100];
12291
- default: return [progress$1 < 100 ? "Driving to destination..." : "Parking truck...", progress$1];
12292
- }
12293
- }, [fetcher.data]);
12294
- return /* @__PURE__ */ jsx("div", {
12295
- className: clsx$1("grid h-full w-full place-items-center px-4", className),
12296
- children: /* @__PURE__ */ jsxs("div", {
12297
- className: "flex w-full max-w-[clamp(16rem,80vw,36rem)] flex-col gap-4 px-2 py-2",
12298
- children: [
12299
- /* @__PURE__ */ jsx("img", {
12300
- src: !showProgress || progress > 0 && progress < 100 ? truck_default : truck_default$1,
12301
- alt: "🚛",
12302
- className: "pixelated aspect-square w-full"
12303
- }),
12304
- showProgress ? /* @__PURE__ */ jsx("div", {
12305
- className: "text-center text-3xl font-bold",
12306
- children: progressText
12307
- }) : null,
12308
- showProgress ? /* @__PURE__ */ jsx("div", {
12309
- className: "h-6 w-3/4 self-center rounded-2xl bg-gray-300",
12310
- children: /* @__PURE__ */ jsx("div", {
12311
- className: cn("bg-blue-primary h-[calc(100%-4px)] min-w-[calc(var(--spacing)*6-4px)] translate-x-0.5 translate-y-0.5 rounded-2xl transition-[width] ease-in-out", { "animate-skeet": !progress }),
12312
- style: { width: progress ? `calc(${Math.min(progress, 100)}% - 4px)` : "40px" }
12313
- })
12314
- }) : null,
12315
- loadingText ? /* @__PURE__ */ jsx("div", {
12316
- className: "text-center font-bold",
12317
- children: loadingText
12318
- }) : null
12319
- ]
12320
- })
12321
- });
12322
- }
12137
+ var stringSorter = (a, b) => a.toLowerCase().localeCompare(b.toLowerCase());
12323
12138
  function IconRadioGroup({ group, titleMap, defaultValue, className, onChange, large, iconMap }) {
12324
12139
  const enumEntries = Object.entries(group);
12325
12140
  return /* @__PURE__ */ jsx(RadioGroup, {
@@ -12415,42 +12230,631 @@ const Options = memo(function Options$1() {
12415
12230
  })] })
12416
12231
  ] });
12417
12232
  });
12418
- function Providers({ children, data }) {
12419
- const [options, setOptions] = useState(() => {
12420
- const savedOptions = typeof document !== "undefined" ? localStorage.getItem(OPTIONS_LOCAL_STORAGE_KEY) : null;
12421
- return {
12422
- ...getDefaultOptionsContextValue(),
12423
- ...savedOptions ? JSON.parse(savedOptions) : {},
12424
- hasLoadedSavedOptions: !!savedOptions
12425
- };
12233
+ function SettingsModal() {
12234
+ const { metricType, hierarchyType, transitionsEnabled, renderCutoff, showFilesWithoutChanges, linkMetricAndSizeMetric, showOnlySearchMatches, setLinkMetricAndSizeMetric, setTransitionsEnabled, labelsVisible, setLabelsVisible, setHierarchyType, setSizeMetricType, setRenderCutoff, setShowFilesWithoutChanges, setShowOnlySearchMatches } = useOptions();
12235
+ const [isTransitioning, startTransition$1] = useTransition();
12236
+ return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs("div", {
12237
+ className: "flex min-w-120 flex-col items-start justify-center gap-4 p-2 pl-0",
12238
+ children: [
12239
+ /* @__PURE__ */ jsxs(CheckboxWithLabel, {
12240
+ className: "group text-sm hover:text-blue-500 hover:opacity-100",
12241
+ checked: Boolean(linkMetricAndSizeMetric),
12242
+ title: "Enable to sync size metric with color metric",
12243
+ onChange: (e) => {
12244
+ setLinkMetricAndSizeMetric(e.target.checked);
12245
+ if (e.target.checked) setSizeMetricType(relatedSizeMetric[metricType]);
12246
+ },
12247
+ children: [/* @__PURE__ */ jsx(Icon, {
12248
+ className: "ml-1.5",
12249
+ path: mdiLink,
12250
+ size: "1.25em"
12251
+ }), /* @__PURE__ */ jsx("span", { children: "Link size and color option" })]
12252
+ }),
12253
+ /* @__PURE__ */ jsxs(CheckboxWithLabel, {
12254
+ className: "group text-sm hover:text-blue-500 hover:opacity-100",
12255
+ checked: transitionsEnabled,
12256
+ title: "Disable to improve performance when zooming",
12257
+ onChange: (e) => setTransitionsEnabled(e.target.checked),
12258
+ children: [/* @__PURE__ */ jsx(Icon, {
12259
+ className: "ml-1.5",
12260
+ path: mdiTransition,
12261
+ size: "1.25em"
12262
+ }), "Transitions"]
12263
+ }),
12264
+ /* @__PURE__ */ jsxs(CheckboxWithLabel, {
12265
+ className: "group text-sm hover:text-blue-500 hover:opacity-100",
12266
+ checked: labelsVisible,
12267
+ title: "Disable to improve performance",
12268
+ onChange: (e) => setLabelsVisible(e.target.checked),
12269
+ children: [/* @__PURE__ */ jsx(Icon, {
12270
+ className: "ml-1.5",
12271
+ path: mdiLabel,
12272
+ size: "1.25em"
12273
+ }), "Labels"]
12274
+ }),
12275
+ /* @__PURE__ */ jsxs(CheckboxWithLabel, {
12276
+ className: "group text-sm hover:text-blue-500 hover:opacity-100",
12277
+ checked: showFilesWithoutChanges,
12278
+ title: "Show files that have had no changes in the selected time range",
12279
+ onChange: (e) => setShowFilesWithoutChanges(e.target.checked),
12280
+ children: [/* @__PURE__ */ jsx(Icon, {
12281
+ className: "ml-1.5",
12282
+ path: mdiClockEdit,
12283
+ size: "1.25em"
12284
+ }), "Show files with no activity"]
12285
+ }),
12286
+ /* @__PURE__ */ jsxs(CheckboxWithLabel, {
12287
+ className: "group text-sm hover:text-blue-500 hover:opacity-100",
12288
+ checked: hierarchyType === "FLAT",
12289
+ title: "Show all files on the same level, instead of as a file tree",
12290
+ onChange: () => {
12291
+ if (hierarchyType === "FLAT") setHierarchyType("NESTED");
12292
+ else setHierarchyType("FLAT");
12293
+ },
12294
+ children: [/* @__PURE__ */ jsx(Icon, {
12295
+ className: "ml-1.5",
12296
+ path: mdiFileTree,
12297
+ size: "1.25em"
12298
+ }), "Flatten file tree"]
12299
+ }),
12300
+ /* @__PURE__ */ jsxs(CheckboxWithLabel, {
12301
+ className: "group text-sm hover:text-blue-500 hover:opacity-100",
12302
+ checked: showOnlySearchMatches,
12303
+ title: "When searching, hide files that do not match the search query",
12304
+ onChange: (e) => setShowOnlySearchMatches(e.target.checked),
12305
+ children: [/* @__PURE__ */ jsx(Icon, {
12306
+ className: "ml-1.5",
12307
+ path: mdiFilter,
12308
+ size: "1.25em"
12309
+ }), "Show only search matches"]
12310
+ }),
12311
+ /* @__PURE__ */ jsxs("label", {
12312
+ className: "label group flex w-full items-center justify-start gap-2 text-sm hover:text-blue-500 hover:opacity-100",
12313
+ title: "Increase this to improve render performance, decrease it to get higher level of detail",
12314
+ children: [/* @__PURE__ */ jsxs("span", {
12315
+ className: "group flex grow items-center gap-2",
12316
+ children: [
12317
+ /* @__PURE__ */ jsx(Icon, {
12318
+ className: "ml-1.5",
12319
+ path: mdiContentCut,
12320
+ size: "1.25em"
12321
+ }),
12322
+ "Pixel render cut-off ",
12323
+ isTransitioning ? /* @__PURE__ */ jsx("img", {
12324
+ src: truck_default$1,
12325
+ alt: "...",
12326
+ className: "h-5"
12327
+ }) : ""
12328
+ ]
12329
+ }), /* @__PURE__ */ jsx("input", {
12330
+ type: "number",
12331
+ min: 0,
12332
+ defaultValue: renderCutoff,
12333
+ className: "mr-1 w-12 place-self-end border-b-2",
12334
+ onChange: (x) => startTransition$1(() => setRenderCutoff(x.target.valueAsNumber))
12335
+ })]
12336
+ })
12337
+ ]
12338
+ }) });
12339
+ }
12340
+ var modals = {
12341
+ "group-authors": {
12342
+ content: /* @__PURE__ */ jsx(UnionAuthorsModal, {}),
12343
+ title: "Group Authors",
12344
+ icon: mdiAccountMultipleCheck
12345
+ },
12346
+ "app-settings": {
12347
+ content: /* @__PURE__ */ jsx(SettingsModal, {}),
12348
+ title: "Settings",
12349
+ icon: mdiCog
12350
+ }
12351
+ };
12352
+ var modalSearchParamConfig = parseAsStringLiteral(Object.keys(modals));
12353
+ function useModal(modalKey = null) {
12354
+ const [modal, setModal] = useQueryState("modal", modalSearchParamConfig);
12355
+ const openModal = (modal$1 = modalKey) => void setModal(modal$1);
12356
+ const closeModal = () => setModal(null);
12357
+ return {
12358
+ modal,
12359
+ openModal,
12360
+ closeModal
12361
+ };
12362
+ }
12363
+ function ModalManager() {
12364
+ const [modalKey, setModal] = useQueryState("modal", modalSearchParamConfig);
12365
+ const dialogRef = useRef(null);
12366
+ const onClose = () => setModal(null);
12367
+ useEffect(() => {
12368
+ if (!dialogRef.current) return;
12369
+ const dialog = dialogRef.current;
12370
+ if (modalKey) {
12371
+ dialogRef.current.showModal();
12372
+ return;
12373
+ }
12374
+ dialog.close();
12375
+ return () => dialog.close();
12376
+ }, [modalKey]);
12377
+ if (!modalKey) return null;
12378
+ const modal = modals[modalKey];
12379
+ return /* @__PURE__ */ jsx("dialog", {
12380
+ ref: dialogRef,
12381
+ "aria-modal": true,
12382
+ open: false,
12383
+ closedby: "any",
12384
+ className: "z-10 m-auto flex flex-col items-start justify-stretch bg-transparent text-inherit backdrop:bg-gray-500/75 backdrop:p-0",
12385
+ onClose,
12386
+ children: /* @__PURE__ */ jsxs("div", {
12387
+ className: "card m-auto h-full w-full max-w-(--breakpoint-2xl) gap-2 overflow-hidden rounded-xl bg-gray-100 p-4 shadow-sm",
12388
+ children: [
12389
+ /* @__PURE__ */ jsxs("div", {
12390
+ className: "flex justify-between gap-2",
12391
+ children: [/* @__PURE__ */ jsxs("div", {
12392
+ className: "flex flex-row items-center gap-2",
12393
+ children: [/* @__PURE__ */ jsx(Icon, {
12394
+ path: modal.icon,
12395
+ size: "1.75em"
12396
+ }), /* @__PURE__ */ jsx("h2", {
12397
+ className: "text-2xl font-semibold",
12398
+ children: modal.title
12399
+ })]
12400
+ }), /* @__PURE__ */ jsx(CloseButton, {
12401
+ absolute: false,
12402
+ className: "justify-self-end px-1",
12403
+ onClick: onClose
12404
+ })]
12405
+ }),
12406
+ /* @__PURE__ */ jsx("span", { className: "w-full border-b-3 border-gray-500 px-20" }),
12407
+ modal.content ?? null
12408
+ ]
12409
+ })
12426
12410
  });
12427
- const [searchResults, setSearchResults] = useState({});
12428
- const hasSearchResults = useMemo(() => Object.values(searchResults).length > 0, [searchResults]);
12429
- const prefersLight = usePrefersLightMode();
12430
- const metricsData = useMemo(() => {
12431
- return createMetricData(data, data.databaseInfo.colorSeed, data.databaseInfo.authorColors, options?.dominantAuthorCutoff ?? 70, prefersLight);
12411
+ }
12412
+ function AuthorOptions() {
12413
+ const transitionState = useNavigation();
12414
+ const action$3 = href("/view");
12415
+ const { openModal } = useModal("group-authors");
12416
+ return /* @__PURE__ */ jsxs("div", {
12417
+ className: "mt-2 grid w-full grid-cols-[1fr_1fr] gap-2",
12418
+ children: [/* @__PURE__ */ jsxs("button", {
12419
+ className: "btn",
12420
+ onClick: () => openModal(),
12421
+ children: [/* @__PURE__ */ jsx(Icon, { path: mdiAccountMultiple }), "Group authors"]
12422
+ }), /* @__PURE__ */ jsxs(Form, {
12423
+ method: "post",
12424
+ action: action$3,
12425
+ children: [/* @__PURE__ */ jsx("input", {
12426
+ type: "hidden",
12427
+ name: "rerollColors",
12428
+ value: ""
12429
+ }), /* @__PURE__ */ jsxs("button", {
12430
+ className: "btn w-full",
12431
+ type: "submit",
12432
+ disabled: transitionState.state !== "idle",
12433
+ children: [/* @__PURE__ */ jsx(Icon, { path: mdiDiceMultipleOutline }), "Shuffle colors"]
12434
+ })]
12435
+ })]
12436
+ });
12437
+ }
12438
+ function GradientLegend({ hoveredObject }) {
12439
+ const { metricType } = useOptions();
12440
+ const [metricsData] = useMetrics();
12441
+ const metricCache = metricsData.get(metricType);
12442
+ if (metricCache === void 0) throw new Error("Metric cache is undefined");
12443
+ const { minValue, maxValue, minColor, maxColor } = metricCache.legend;
12444
+ const { clickedObject } = useClickedObject();
12445
+ const path$1 = clickedObject?.path ?? hoveredObject?.path ?? null;
12446
+ const color = path$1 ? metricCache.colormap.get(path$1) : null;
12447
+ let blobLightness = color ? getLightness(color) : -1;
12448
+ if (color === "#c0c0c0") blobLightness = -1;
12449
+ const offset = useMemo(() => {
12450
+ const min = getLightness(minColor);
12451
+ const diff = getLightness(maxColor) - min;
12452
+ if (diff === 0) return 1;
12453
+ return (blobLightness - min) / diff;
12432
12454
  }, [
12433
- data,
12434
- options?.dominantAuthorCutoff,
12435
- prefersLight
12455
+ blobLightness,
12456
+ maxColor,
12457
+ minColor
12436
12458
  ]);
12437
- const optionsValue = useMemo(() => ({
12438
- ...options,
12439
- setMetricType: (metricType) => setOptions((prevOptions) => ({
12440
- ...prevOptions,
12441
- metricType
12442
- })),
12443
- setChartType: (chartType) => setOptions((prevOptions) => ({
12444
- ...prevOptions,
12445
- chartType
12446
- })),
12447
- setHierarchyType: (hierarchyType) => setOptions((prevOptions) => ({
12448
- ...prevOptions,
12449
- hierarchyType
12450
- })),
12451
- setCommitSearch: (commitSearch) => setOptions((prevOptions) => ({
12452
- ...prevOptions,
12453
- commitSearch
12459
+ const visible = path$1 !== null;
12460
+ const midValue = Math.round((maxValue - minValue) / 2);
12461
+ return /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("div", {
12462
+ className: "relative h-4 rounded-sm",
12463
+ style: { backgroundImage: `linear-gradient(to right, ${minColor}, ${maxColor})` },
12464
+ children: /* @__PURE__ */ jsx(LegendBarIndicator, {
12465
+ offset: offset * 100,
12466
+ visible
12467
+ })
12468
+ }), /* @__PURE__ */ jsxs("div", {
12469
+ className: "flex justify-between",
12470
+ children: [
12471
+ /* @__PURE__ */ jsx("span", {
12472
+ className: "font-bold",
12473
+ title: minValue.toLocaleString(),
12474
+ children: numToFriendlyString(minValue)
12475
+ }),
12476
+ /* @__PURE__ */ jsx("span", {
12477
+ className: "absolute left-1/2 -translate-x-1/2 font-bold",
12478
+ title: midValue.toLocaleString(),
12479
+ children: numToFriendlyString(midValue)
12480
+ }),
12481
+ /* @__PURE__ */ jsx("span", {
12482
+ className: "font-bold",
12483
+ title: maxValue.toLocaleString(),
12484
+ children: numToFriendlyString(maxValue)
12485
+ })
12486
+ ]
12487
+ })] });
12488
+ }
12489
+ function Handle({ domain: [min, max], children, disabled, handle, handleType = "round", title, getHandleProps, className, onClick }) {
12490
+ return /* @__PURE__ */ jsx("button", {
12491
+ className: cn("absolute z-10 flex size-5 -translate-x-1/2 -translate-y-1.5 place-content-center disabled:grayscale", { "size-5 rounded-full": handleType === "round" }, disabled ? "cursor-progress" : "cursor-col-resize", className),
12492
+ role: "slider",
12493
+ "aria-disabled": disabled,
12494
+ "aria-valuemin": min,
12495
+ "aria-valuemax": max,
12496
+ "aria-valuenow": handle.value,
12497
+ title,
12498
+ style: { left: `${handle.percent}%` },
12499
+ onClick,
12500
+ ...getHandleProps(handle.id),
12501
+ children: /* @__PURE__ */ jsx("div", {
12502
+ className: cn("btn--primary", {
12503
+ "size-5 rounded-full": handleType === "round",
12504
+ "h-5 w-0.5": handleType === "square"
12505
+ }),
12506
+ children
12507
+ })
12508
+ });
12509
+ }
12510
+ function SliderRail({ getRailProps, className = "", children }) {
12511
+ return /* @__PURE__ */ jsx("div", {
12512
+ className: cn("bg-blue-secondary/20 absolute h-2 w-full cursor-pointer", {}, className),
12513
+ ...getRailProps(),
12514
+ children
12515
+ });
12516
+ }
12517
+ function Track({ source, target, trackType = "round", getTrackProps, disabled }) {
12518
+ return /* @__PURE__ */ jsx("div", {
12519
+ className: cn("btn btn--primary absolute h-2 cursor-pointer p-0", disabled ? "bg-primary-text-dark dark:bg-primary-text" : "bg-blue-primary", {
12520
+ "rounded-full": trackType === "round",
12521
+ "rounded-none": trackType === "square"
12522
+ }),
12523
+ style: {
12524
+ left: `${source.percent}%`,
12525
+ width: `${target.percent - source.percent}%`
12526
+ },
12527
+ ...getTrackProps()
12528
+ });
12529
+ }
12530
+ var alignToJustify = {
12531
+ left: "start",
12532
+ center: "center",
12533
+ right: "end"
12534
+ };
12535
+ function LabeledTicks({ valueMap, titleMap = valueMap, onTop = false }) {
12536
+ const count = Object.entries(valueMap).length;
12537
+ return /* @__PURE__ */ jsx(Ticks, {
12538
+ count,
12539
+ children: ({ ticks }) => /* @__PURE__ */ jsx("div", {
12540
+ className: "grid grid-flow-col grid-cols-3 grid-rows-[1fr_auto] pt-4",
12541
+ children: ticks.map((tick) => {
12542
+ const tickLabelInfo = valueMap[tick.value * 100];
12543
+ const align = tick.value === 0 ? "left" : tick.value === 1 ? "right" : "center";
12544
+ const Tick$1 = /* @__PURE__ */ jsx("div", {
12545
+ className: `flex justify-${tickLabelInfo ? alignToJustify[align] : "center"}`,
12546
+ children: /* @__PURE__ */ jsx("div", { className: "h-2 w-px bg-gray-950 dark:bg-gray-50" })
12547
+ });
12548
+ const Label$1 = /* @__PURE__ */ jsx("div", {
12549
+ title: titleMap[tick.value * 100],
12550
+ className: "truncate text-xs",
12551
+ style: { textAlign: align },
12552
+ children: tickLabelInfo
12553
+ });
12554
+ return /* @__PURE__ */ jsxs(Fragment, { children: [onTop ? Label$1 : Tick$1, onTop ? Tick$1 : Label$1] }, tick.id);
12555
+ })
12556
+ })
12557
+ });
12558
+ }
12559
+ function TicksByCount({ className = "", count, tickToLabel, onTop = true, align, below = false }) {
12560
+ return /* @__PURE__ */ jsx("div", {
12561
+ className: cn("grid grid-flow-col grid-cols-(--cols) gap-1 text-xs", {
12562
+ "grid-rows-[auto]": !below && !onTop,
12563
+ "grid-rows-[auto_auto]": below !== onTop,
12564
+ "grid-rows-[auto_auto_auto]": below && onTop
12565
+ }, className),
12566
+ style: { "--cols": `repeat(${count}, minmax(0, 1fr))` },
12567
+ children: Array.from({ length: count }).map((_, i) => {
12568
+ const tick = i / (count - 1);
12569
+ const tickLabelInfo = tickToLabel(tick, i);
12570
+ const alignment = align ?? (tick === 0 ? "left" : tick === 1 ? "right" : "center");
12571
+ const justification = tickLabelInfo ? alignToJustify[alignment] : "center";
12572
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
12573
+ onTop ? /* @__PURE__ */ jsx(Tick, { justification }) : null,
12574
+ /* @__PURE__ */ jsx("div", {
12575
+ className: cn(`text-${alignment}`),
12576
+ children: tickLabelInfo === 0 ? "" : tickLabelInfo
12577
+ }),
12578
+ below ? /* @__PURE__ */ jsx(Tick, { justification }) : null
12579
+ ] }, i);
12580
+ })
12581
+ });
12582
+ }
12583
+ const Tick = ({ className = "", justification = "start" }) => /* @__PURE__ */ jsx("div", {
12584
+ className: cn("flex", `justify-${justification}`, className),
12585
+ children: /* @__PURE__ */ jsx("div", { className: "h-2 w-px bg-gray-950 dark:bg-gray-50" })
12586
+ });
12587
+ function SegmentLegend({ hoveredObject }) {
12588
+ const { metricType } = useOptions();
12589
+ const [metricsData] = useMetrics();
12590
+ const { steps, textGenerator, colorGenerator, offsetStepCalc } = metricsData.get(metricType).legend;
12591
+ const width = 100 / steps;
12592
+ let arrowVisible = false;
12593
+ let arrowOffset = 0;
12594
+ const clickedObject = useClickedObject().clickedObject ?? hoveredObject ?? null;
12595
+ if (isBlob(clickedObject)) {
12596
+ arrowVisible = true;
12597
+ arrowOffset = width / 2 + width * offsetStepCalc(clickedObject);
12598
+ }
12599
+ return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx("div", {
12600
+ className: "relative",
12601
+ children: /* @__PURE__ */ jsxs("div", {
12602
+ className: "relative flex text-xs whitespace-nowrap",
12603
+ children: [[...Array(steps)].map((_, i) => {
12604
+ return steps >= 4 ? /* @__PURE__ */ jsx(MetricSegment, {
12605
+ className: i === 0 ? "rounded-l-sm" : i === steps - 1 ? "rounded-r-sm" : "",
12606
+ width,
12607
+ color: colorGenerator(i),
12608
+ text: textGenerator(i),
12609
+ top: i % 2 === 0
12610
+ }, i) : /* @__PURE__ */ jsx(TopMetricSegment, {
12611
+ width,
12612
+ color: colorGenerator(i),
12613
+ text: textGenerator(i)
12614
+ }, i);
12615
+ }), /* @__PURE__ */ jsx(LegendBarIndicator, {
12616
+ offset: arrowOffset,
12617
+ visible: arrowVisible
12618
+ })]
12619
+ })
12620
+ }) });
12621
+ }
12622
+ function MetricSegment({ className = "", width, color, text, top }) {
12623
+ if (top) return /* @__PURE__ */ jsxs("div", {
12624
+ className: "flex flex-col",
12625
+ style: { width: `${width}%` },
12626
+ children: [
12627
+ /* @__PURE__ */ jsx("div", {
12628
+ className: "h-5 truncate text-left",
12629
+ title: text,
12630
+ children: text
12631
+ }),
12632
+ /* @__PURE__ */ jsx(Tick, { className: "ml-1" }),
12633
+ /* @__PURE__ */ jsx("div", {
12634
+ className: cn("h-4", className),
12635
+ style: { backgroundColor: color }
12636
+ }),
12637
+ /* @__PURE__ */ jsx(Tick, { className: "invisible" })
12638
+ ]
12639
+ });
12640
+ else return /* @__PURE__ */ jsxs("div", {
12641
+ className: "flex flex-col",
12642
+ style: { width: `${width}%` },
12643
+ children: [
12644
+ /* @__PURE__ */ jsx("div", {
12645
+ className: "invisible h-5 text-left",
12646
+ children: text
12647
+ }),
12648
+ /* @__PURE__ */ jsx(Tick, { className: "invisible" }),
12649
+ /* @__PURE__ */ jsx("div", {
12650
+ className: cn("h-4", className),
12651
+ style: { backgroundColor: color }
12652
+ }),
12653
+ /* @__PURE__ */ jsx(Tick, {}),
12654
+ /* @__PURE__ */ jsx("div", {
12655
+ className: "h-5 truncate text-left",
12656
+ children: text
12657
+ })
12658
+ ]
12659
+ });
12660
+ }
12661
+ function TopMetricSegment({ width, color, text }) {
12662
+ return /* @__PURE__ */ jsxs("div", {
12663
+ className: "flex flex-col",
12664
+ style: { width: `${width}%` },
12665
+ children: [
12666
+ /* @__PURE__ */ jsx("div", {
12667
+ className: "h-5 truncate text-left",
12668
+ children: text
12669
+ }),
12670
+ /* @__PURE__ */ jsx(Tick, {}),
12671
+ /* @__PURE__ */ jsx("div", {
12672
+ className: "h-5",
12673
+ style: { backgroundColor: color }
12674
+ })
12675
+ ]
12676
+ });
12677
+ }
12678
+ function PercentageSlider({ className = "" }) {
12679
+ const { dominantAuthorCutoff, setDominantAuthorCutoff } = useOptions();
12680
+ const [displayPercentage, setDisplayPercentage] = useState(dominantAuthorCutoff);
12681
+ const domain = [0, 100];
12682
+ return /* @__PURE__ */ jsxs("div", {
12683
+ className: cn("grid grid-cols-[calc(11*var(--spacing))_1fr] px-3", className),
12684
+ children: [/* @__PURE__ */ jsxs("p", {
12685
+ title: "Min percentage of changes a file's top author must account for to be colored.",
12686
+ children: [displayPercentage, "%"]
12687
+ }), /* @__PURE__ */ jsx("div", {
12688
+ className: "relative",
12689
+ title: "Adjust the dominant author percentage cutoff",
12690
+ children: /* @__PURE__ */ jsxs(Slider, {
12691
+ mode: 1,
12692
+ step: 1,
12693
+ domain,
12694
+ values: [displayPercentage],
12695
+ onChange: (e) => {
12696
+ setDominantAuthorCutoff(e[0]);
12697
+ startTransition(() => {
12698
+ setDisplayPercentage(e[0]);
12699
+ });
12700
+ },
12701
+ onUpdate: (e) => {
12702
+ startTransition(() => {
12703
+ setDisplayPercentage(e[0]);
12704
+ });
12705
+ },
12706
+ children: [
12707
+ /* @__PURE__ */ jsx(Rail, { children: SliderRail }),
12708
+ /* @__PURE__ */ jsx(Handles, { children: ({ handles, getHandleProps }) => /* @__PURE__ */ jsx(Fragment$1, { children: handles.map((handle) => /* @__PURE__ */ jsx(Handle, {
12709
+ handle,
12710
+ domain,
12711
+ getHandleProps
12712
+ }, handle.id)) }) }),
12713
+ /* @__PURE__ */ jsx(Tracks, {
12714
+ right: false,
12715
+ children: ({ tracks, getTrackProps }) => /* @__PURE__ */ jsx(Fragment$1, { children: tracks.map(({ id, ...props }) => /* @__PURE__ */ createElement(Track, {
12716
+ ...props,
12717
+ key: id,
12718
+ backgroundColor: noEntryColor,
12719
+ getTrackProps
12720
+ })) })
12721
+ }),
12722
+ /* @__PURE__ */ jsx(LabeledTicks, { valueMap: {
12723
+ 0: "Top author",
12724
+ 50: "Majority author",
12725
+ 100: "Single author"
12726
+ } })
12727
+ ]
12728
+ })
12729
+ })]
12730
+ });
12731
+ }
12732
+ function Legend({ hoveredObject }) {
12733
+ const { sizeMetric, metricType } = useOptions();
12734
+ const [metricsData] = useMetrics();
12735
+ const deferredHoveredObject = useDeferredValue(hoveredObject);
12736
+ const metricCache = metricsData.get(metricType) ?? void 0;
12737
+ const legend = useMemo(() => {
12738
+ if (metricCache === void 0) return null;
12739
+ switch (getMetricLegendType(metricType)) {
12740
+ case "POINT": return /* @__PURE__ */ jsx(PointLegend, {});
12741
+ case "GRADIENT": return /* @__PURE__ */ jsx(GradientLegend, { hoveredObject: deferredHoveredObject });
12742
+ case "SEGMENTS": return /* @__PURE__ */ jsx(SegmentLegend, { hoveredObject: deferredHoveredObject });
12743
+ }
12744
+ }, [
12745
+ metricCache,
12746
+ deferredHoveredObject,
12747
+ metricType
12748
+ ]);
12749
+ if (legend === null) return null;
12750
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
12751
+ /* @__PURE__ */ jsx("h3", {
12752
+ className: "card__subtitle",
12753
+ children: "Size legend"
12754
+ }),
12755
+ /* @__PURE__ */ jsx("p", {
12756
+ className: "mb-2 text-sm",
12757
+ children: sizeMetricLegendDescriptions[sizeMetric]
12758
+ }),
12759
+ /* @__PURE__ */ jsx("h3", {
12760
+ className: "card__subtitle",
12761
+ children: "Color legend"
12762
+ }),
12763
+ /* @__PURE__ */ jsx("p", {
12764
+ className: "mb-4 text-sm",
12765
+ children: colorMetricDescriptions[metricType]
12766
+ }),
12767
+ metricType === "TOP_CONTRIBUTOR" ? /* @__PURE__ */ jsx(PercentageSlider, { className: "my-4" }) : null,
12768
+ legend,
12769
+ metricType === "TOP_CONTRIBUTOR" ? /* @__PURE__ */ jsx(AuthorOptions, {}) : null
12770
+ ] });
12771
+ }
12772
+ function LoadingIndicator({ className = "", showProgress = false, fetchProgress = true, loadingText }) {
12773
+ const [path$1] = useQueryState("path");
12774
+ const fetcher = useFetcher();
12775
+ useEffect(() => {
12776
+ if (fetcher.state === "idle" && showProgress && fetchProgress) fetcher.load(href("/view/progress") + viewSerializer({ path: path$1 }));
12777
+ }, [
12778
+ fetchProgress,
12779
+ fetcher,
12780
+ fetcher.state,
12781
+ path$1,
12782
+ showProgress
12783
+ ]);
12784
+ const [progressText, progress] = useMemo(() => {
12785
+ if (!fetcher.data) return ["Loading truck...", 0];
12786
+ const { progress: progress$1, analyzationStatus } = fetcher.data;
12787
+ switch (analyzationStatus) {
12788
+ case "Starting": return ["Loading truck...", 0];
12789
+ case "GeneratingChart": return ["Unloading truck...", 100];
12790
+ default: return [progress$1 < 100 ? "Driving to destination..." : "Parking truck...", progress$1];
12791
+ }
12792
+ }, [fetcher.data]);
12793
+ return /* @__PURE__ */ jsx("div", {
12794
+ className: clsx$1("grid h-full w-full place-items-center px-4", className),
12795
+ children: /* @__PURE__ */ jsxs("div", {
12796
+ className: "flex w-full max-w-[clamp(16rem,80vw,36rem)] flex-col gap-4 px-2 py-2",
12797
+ children: [
12798
+ /* @__PURE__ */ jsx("img", {
12799
+ src: !showProgress || progress > 0 && progress < 100 ? truck_default$1 : truck_default,
12800
+ alt: "🚛",
12801
+ className: "pixelated aspect-square w-full"
12802
+ }),
12803
+ showProgress ? /* @__PURE__ */ jsx("div", {
12804
+ className: "text-center text-3xl font-bold",
12805
+ children: progressText
12806
+ }) : null,
12807
+ showProgress ? /* @__PURE__ */ jsx("div", {
12808
+ className: "h-6 w-3/4 self-center rounded-2xl bg-gray-300",
12809
+ children: /* @__PURE__ */ jsx("div", {
12810
+ className: cn("bg-blue-primary h-[calc(100%-4px)] min-w-[calc(var(--spacing)*6-4px)] translate-x-0.5 translate-y-0.5 rounded-2xl transition-[width] ease-in-out", { "animate-skeet": !progress }),
12811
+ style: { width: progress ? `calc(${Math.min(progress, 100)}% - 4px)` : "40px" }
12812
+ })
12813
+ }) : null,
12814
+ loadingText ? /* @__PURE__ */ jsx("div", {
12815
+ className: "text-center font-bold",
12816
+ children: loadingText
12817
+ }) : null
12818
+ ]
12819
+ })
12820
+ });
12821
+ }
12822
+ function Providers({ children, data }) {
12823
+ const [options, setOptions] = useState(() => {
12824
+ const savedOptions = typeof document !== "undefined" ? localStorage.getItem(OPTIONS_LOCAL_STORAGE_KEY) : null;
12825
+ return {
12826
+ ...getDefaultOptionsContextValue(),
12827
+ ...savedOptions ? JSON.parse(savedOptions) : {},
12828
+ hasLoadedSavedOptions: !!savedOptions
12829
+ };
12830
+ });
12831
+ const [searchResults, setSearchResults] = useState({});
12832
+ const hasSearchResults = useMemo(() => Object.values(searchResults).length > 0, [searchResults]);
12833
+ const prefersLight = usePrefersLightMode();
12834
+ const metricsData = useMemo(() => {
12835
+ return createMetricData(data, data.databaseInfo.colorSeed, data.databaseInfo.authorColors, options?.dominantAuthorCutoff ?? 70, prefersLight);
12836
+ }, [
12837
+ data,
12838
+ options?.dominantAuthorCutoff,
12839
+ prefersLight
12840
+ ]);
12841
+ const optionsValue = useMemo(() => ({
12842
+ ...options,
12843
+ setMetricType: (metricType) => setOptions((prevOptions) => ({
12844
+ ...prevOptions,
12845
+ metricType
12846
+ })),
12847
+ setChartType: (chartType) => setOptions((prevOptions) => ({
12848
+ ...prevOptions,
12849
+ chartType
12850
+ })),
12851
+ setHierarchyType: (hierarchyType) => setOptions((prevOptions) => ({
12852
+ ...prevOptions,
12853
+ hierarchyType
12854
+ })),
12855
+ setCommitSearch: (commitSearch) => setOptions((prevOptions) => ({
12856
+ ...prevOptions,
12857
+ commitSearch
12454
12858
  })),
12455
12859
  setSizeMetricType: (sizeMetric) => setOptions((prevOptions) => ({
12456
12860
  ...prevOptions,
@@ -12653,507 +13057,238 @@ const SearchCard = memo(function SearchCard$1() {
12653
13057
  if (resultRefs.length === 0) return;
12654
13058
  const prevIndex = currentIndex - 1;
12655
13059
  if (prevIndex < 0) {
12656
- searchFieldRef.current?.focus();
12657
- searchFieldRef.current?.select();
12658
- return;
12659
- }
12660
- focusResultAtIndex(prevIndex);
12661
- }
12662
- },
12663
- onClick: () => onClickObject(object),
12664
- children: [object.type === "tree" ? /* @__PURE__ */ jsx(Icon, {
12665
- path: object.type === "tree" ? mdiFolder : mdiFileOutline,
12666
- size: .75,
12667
- className: "shrink-0"
12668
- }) : /* @__PURE__ */ jsx(Icon, {
12669
- color: metrics.get(options.metricType)?.colormap.get(object.path) ?? "grey",
12670
- path: mdiFile,
12671
- size: .75,
12672
- className: "shrink-0"
12673
- }), /* @__PURE__ */ jsx("span", {
12674
- className: "text-secondary-text dark:hover:text-primary-text-dark hover:text-primary-text dark:text-secondary-text-dark truncate",
12675
- children: object.path.split(getSeparator(object.path)).slice(1).join("/") ?? object.path
12676
- })]
12677
- }, object.path))
12678
- }) : null
12679
- ]
12680
- });
12681
- });
12682
- var BarChart = () => {
12683
- const svgRef = useRef(null);
12684
- const { databaseInfo } = useData();
12685
- const [ref, rawSize] = useComponentSize();
12686
- const size = useDeferredValue(rawSize);
12687
- const data = databaseInfo.commitCountPerTimeInterval;
12688
- const [start, end] = databaseInfo.selectedRange;
12689
- const height = 50;
12690
- const width = size.width;
12691
- const xScale = d3.scaleBand().domain(data.map((d) => d.date.toString())).range([0, width]);
12692
- const yScale = d3.scaleLinear().domain([0, d3.max(data, (d) => d.count) || 0]).range([height, 0]);
12693
- const [searchParams] = useSearchParams();
12694
- const submit = useSubmit();
12695
- function updateTimeseries(e) {
12696
- const form = new FormData();
12697
- form.append("timeseries", `${e[0]}-${e[1]}`);
12698
- submit(form, {
12699
- action: getPathFromRepoAndHead({
12700
- path: searchParams.get("path"),
12701
- branch: databaseInfo.branch
12702
- }),
12703
- method: "post"
12704
- });
12705
- }
12706
- return /* @__PURE__ */ jsx("div", {
12707
- ref,
12708
- className: "flex flex-col justify-center",
12709
- children: /* @__PURE__ */ jsx("svg", {
12710
- ref: svgRef,
12711
- width: "100%",
12712
- height,
12713
- className: "fill-transparent",
12714
- children: data.map((d, i) => {
12715
- const barX = (xScale(d.date) ?? 0) + 2;
12716
- const barWidth = Math.max(1, xScale.bandwidth() - 4);
12717
- const barHeight = height - yScale(d.count);
12718
- return /* @__PURE__ */ jsxs("g", { children: [/* @__PURE__ */ jsx("rect", {
12719
- x: barX,
12720
- y: yScale(d.count),
12721
- width: barWidth,
12722
- height: barHeight,
12723
- rx: 2,
12724
- ry: 2,
12725
- className: cn("transition-[height,y] duration-300 ease-out", d.timestamp >= start && d.timestamp < end ? "fill-blue-primary" : "fill-blue-primary/40")
12726
- }), /* @__PURE__ */ jsx("rect", {
12727
- x: barX,
12728
- y: 0,
12729
- width: barWidth,
12730
- height,
12731
- rx: 2,
12732
- ry: 2,
12733
- className: "hover:stroke-blue-secondary stroke-transparent stroke-1 hover:fill-transparent",
12734
- onClick: () => {
12735
- const [intervalStart, intervalEnd] = expandIntervalToRange(d.timestamp, databaseInfo.commitCountPerTimeIntervalUnit);
12736
- updateTimeseries([intervalStart, intervalEnd]);
12737
- },
12738
- children: /* @__PURE__ */ jsx("title", { children: `${d.date}: ${d.count.toLocaleString()} commit${d.count !== 1 ? "s" : ""}` })
12739
- })] }, `${d.date}-${i}`);
12740
- })
12741
- })
12742
- });
12743
- };
12744
- var BarChart_default = BarChart;
12745
- function Timeline({ className }) {
12746
- const [_, startTransition$1] = useTransition();
12747
- const { databaseInfo } = useData();
12748
- const { timerange, selectedRange } = databaseInfo;
12749
- const newestChangeDate = timerange[1];
12750
- const oldestChangeDate = timerange[0];
12751
- const submit = useSubmit();
12752
- const [range, setRange] = useState(selectedRange[0] === 0 ? timerange : selectedRange);
12753
- const disabled = useNavigation().state !== "idle";
12754
- const [searchParams] = useSearchParams();
12755
- function updateTimeseries(e) {
12756
- const form = new FormData();
12757
- form.append("timeseries", `${e[0]}-${e[1]}`);
12758
- submit(form, {
12759
- action: getPathFromRepoAndHead({
12760
- path: searchParams.get("path"),
12761
- branch: databaseInfo.branch
12762
- }),
12763
- method: "post"
12764
- });
12765
- }
12766
- const selectedStartDate = range[0] * 1e3;
12767
- const selectedEndDate = range[1] * 1e3;
12768
- return /* @__PURE__ */ jsxs("div", {
12769
- className: cn("group flex flex-col gap-2", className),
12770
- children: [
12771
- /* @__PURE__ */ jsx(BarChart_default, {}),
12772
- /* @__PURE__ */ jsxs(Slider, {
12773
- className: "relative",
12774
- mode: 2,
12775
- step: 1,
12776
- domain: timerange,
12777
- values: range,
12778
- disabled,
12779
- onUpdate: (e) => setRange([...e]),
12780
- onChange: (e) => {
12781
- startTransition$1(() => {
12782
- updateTimeseries(e);
12783
- });
12784
- },
12785
- children: [
12786
- /* @__PURE__ */ jsx(Rail, { children: SliderRail }),
12787
- /* @__PURE__ */ jsx(Handles, { children: ({ handles, getHandleProps }) => /* @__PURE__ */ jsx("div", {
12788
- className: "",
12789
- children: handles.map((handle, i) => /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("div", {
12790
- className: cn("absolute left-(--left) w-max translate-y-[calc(50%)] opacity-0 transition-opacity group-hover:opacity-100 focus:opacity-100", {
12791
- "left-(--left)": i === 0,
12792
- "right-[calc(100%-var(--left))] -translate-x-full": i === 1
12793
- }),
12794
- style: { "--left": `${handle.percent}%` },
12795
- children: /* @__PURE__ */ jsx(Popover$1, {
12796
- trigger: ({ onClick }) => /* @__PURE__ */ jsx("button", {
12797
- className: cn("bg-blue-primary text-primary-text-dark cursor-pointer rounded-lg px-1 break-keep shadow", {
12798
- "rounded-tl-none": i === 0,
12799
- "rounded-tr-none": i === 1
12800
- }),
12801
- onClick,
12802
- children: /* @__PURE__ */ jsx("time", {
12803
- className: "w-max text-xs",
12804
- dateTime: dateFormatISO(i === 0 ? selectedStartDate : selectedEndDate),
12805
- title: `Click or drag to set the ${i === 0 ? "start" : "end"} of time range`,
12806
- children: dateFormatShort(i === 0 ? selectedStartDate : selectedEndDate)
12807
- })
12808
- }),
12809
- children: /* @__PURE__ */ jsx(TimePicker, {
12810
- setsBeginning: i === 0,
12811
- range,
12812
- setRange,
12813
- timerange,
12814
- updateTimeseries,
12815
- disabled
12816
- })
12817
- }, handle.id)
12818
- }), /* @__PURE__ */ jsx(Handle, {
12819
- handleType: "square",
12820
- title: "Drag to adjust interval, click to set specific date",
12821
- className: cn(""),
12822
- handle,
12823
- domain: timerange,
12824
- getHandleProps
12825
- }, handle.id)] }, handle.id))
12826
- }) }),
12827
- /* @__PURE__ */ jsx(Tracks, {
12828
- left: false,
12829
- right: false,
12830
- children: ({ tracks, getTrackProps }) => /* @__PURE__ */ jsx("div", { children: tracks.map(({ id, source, target }) => /* @__PURE__ */ jsx(Track, {
12831
- trackType: "square",
12832
- backgroundColor: "#7aa0c4",
12833
- source,
12834
- target,
12835
- getTrackProps
12836
- }, id)) })
12837
- })
12838
- ]
12839
- }),
12840
- /* @__PURE__ */ jsx(TicksByCount, {
12841
- className: "mb-2",
12842
- count: 2,
12843
- tickToLabel: (t) => dateFormatShort((oldestChangeDate + (newestChangeDate - oldestChangeDate) * t) * 1e3)
12844
- })
12845
- ]
12846
- });
12847
- }
12848
- function TimePicker({ range, setRange, timerange, setsBeginning, updateTimeseries, disabled }) {
12849
- return /* @__PURE__ */ jsx("div", {
12850
- className: "z-30",
12851
- children: /* @__PURE__ */ jsx(DatePicker, {
12852
- inline: true,
12853
- fixedHeight: true,
12854
- renderCustomHeader: ({ date, decreaseMonth, increaseMonth, prevMonthButtonDisabled, nextMonthButtonDisabled }) => {
12855
- return /* @__PURE__ */ jsxs("div", {
12856
- className: "m-2 flex justify-between",
12857
- children: [
12858
- /* @__PURE__ */ jsx("button", {
12859
- disabled: prevMonthButtonDisabled,
12860
- onClick: decreaseMonth,
12861
- children: "<"
12862
- }),
12863
- /* @__PURE__ */ jsx("h2", { children: dateFormatCalendarHeader(date.getTime()) }),
12864
- /* @__PURE__ */ jsx("button", {
12865
- disabled: nextMonthButtonDisabled,
12866
- onClick: increaseMonth,
12867
- children: ">"
12868
- })
12869
- ]
12870
- });
12871
- },
12872
- disabled,
12873
- selected: /* @__PURE__ */ new Date(range[setsBeginning ? 0 : 1] * 1e3),
12874
- minDate: new Date(setsBeginning ? timerange[0] * 1e3 : Math.max(timerange[0] * 1e3, range[0] * 1e3)),
12875
- maxDate: new Date(setsBeginning ? Math.min(timerange[1] * 1e3, range[1] * 1e3) : timerange[1] * 1e3),
12876
- onChange: (x) => {
12877
- if (x) {
12878
- let newRange;
12879
- if (setsBeginning) newRange = [x.getTime() / 1e3, range[1]];
12880
- else newRange = [range[0], x.getTime() / 1e3];
12881
- setRange(newRange);
12882
- updateTimeseries(newRange);
12883
- }
12884
- }
12885
- })
13060
+ searchFieldRef.current?.focus();
13061
+ searchFieldRef.current?.select();
13062
+ return;
13063
+ }
13064
+ focusResultAtIndex(prevIndex);
13065
+ }
13066
+ },
13067
+ onClick: () => onClickObject(object),
13068
+ children: [object.type === "tree" ? /* @__PURE__ */ jsx(Icon, {
13069
+ path: object.type === "tree" ? mdiFolder : mdiFileOutline,
13070
+ size: .75,
13071
+ className: "shrink-0"
13072
+ }) : /* @__PURE__ */ jsx(Icon, {
13073
+ color: metrics.get(options.metricType)?.colormap.get(object.path) ?? "grey",
13074
+ path: mdiFile,
13075
+ size: .75,
13076
+ className: "shrink-0"
13077
+ }), /* @__PURE__ */ jsx("span", {
13078
+ className: "text-secondary-text dark:hover:text-primary-text-dark hover:text-primary-text dark:text-secondary-text-dark truncate",
13079
+ children: object.path.split(getSeparator(object.path)).slice(1).join("/") ?? object.path
13080
+ })]
13081
+ }, object.path))
13082
+ }) : null
13083
+ ]
12886
13084
  });
12887
- }
12888
- function UnionAuthorsModal({ open, onClose }) {
13085
+ });
13086
+ var BarChart = () => {
13087
+ const svgRef = useRef(null);
12889
13088
  const { databaseInfo } = useData();
13089
+ const [ref, rawSize] = useComponentSize();
13090
+ const size = useDeferredValue(rawSize);
13091
+ const data = databaseInfo.commitCountPerTimeInterval;
13092
+ const [start, end] = databaseInfo.selectedRange;
13093
+ const height = 50;
13094
+ const width = size.width;
13095
+ const xScale = d3.scaleBand().domain(data.map((d) => d.date.toString())).range([0, width]);
13096
+ const yScale = d3.scaleLinear().domain([0, d3.max(data, (d) => d.count) || 0]).range([height, 0]);
13097
+ const [searchParams] = useSearchParams();
12890
13098
  const submit = useSubmit();
12891
- const { authors } = databaseInfo;
12892
- const authorUnions = databaseInfo.authorUnions;
12893
- const [selectedAuthors, setSelectedAuthors] = useState([]);
12894
- const [filter, setFilter] = useState("");
12895
- const navigationData = useNavigation();
12896
- const [, authorColors] = useMetrics();
12897
- const [, startTransition$1] = useTransition();
12898
- const location$1 = useLocation();
12899
- const ref = useRef(null);
12900
- useEffect(() => {
12901
- if (!ref.current) return;
12902
- if (open) {
12903
- ref.current.showModal();
12904
- return;
12905
- }
12906
- ref.current.close();
12907
- }, [open]);
12908
- const flattedUnionedAuthors = authorUnions.reduce((acc, union) => {
12909
- return [...acc, ...union];
12910
- }, []).sort(stringSorter);
12911
- function ungroup(groupToUnGroup) {
12912
- const newAuthorUnions = authorUnions.filter((_, i) => i !== groupToUnGroup);
12913
- const form = new FormData();
12914
- form.append("unionedAuthors", JSON.stringify(newAuthorUnions));
12915
- submit(form, {
12916
- action: href("/view") + location$1.search,
12917
- method: "post"
12918
- });
12919
- }
12920
- function ungroupAll() {
12921
- const form = new FormData();
12922
- form.append("unionedAuthors", JSON.stringify([]));
12923
- submit(form, {
12924
- action: href("/view") + location$1.search,
12925
- method: "post"
12926
- });
12927
- }
12928
- function groupSelectedAuthors() {
12929
- if (selectedAuthors.length === 0) return;
12930
- const form = new FormData();
12931
- form.append("unionedAuthors", JSON.stringify([...authorUnions, selectedAuthors]));
12932
- submit(form, {
12933
- action: href("/view") + location$1.search,
12934
- method: "post"
12935
- });
12936
- setSelectedAuthors([]);
12937
- }
12938
- function makePrimaryAlias(alias, groupIndex) {
12939
- const newAuthorUnions = authorUnions.map((group, i) => {
12940
- if (i === groupIndex) return [alias, ...group.filter((a) => a !== alias)];
12941
- return group;
12942
- });
13099
+ function updateTimeseries(e) {
12943
13100
  const form = new FormData();
12944
- form.append("unionedAuthors", JSON.stringify(newAuthorUnions));
13101
+ form.append("timeseries", `${e[0]}-${e[1]}`);
12945
13102
  submit(form, {
12946
- action: href("/view") + location$1.search,
13103
+ action: getPathFromRepoAndHead({
13104
+ path: searchParams.get("path"),
13105
+ branch: databaseInfo.branch
13106
+ }),
12947
13107
  method: "post"
12948
13108
  });
12949
13109
  }
12950
- const disabled = navigationData.state !== "idle";
12951
- const ungroupedAuthorsSorted = authors.filter((a) => !flattedUnionedAuthors.includes(a)).slice(0).sort(stringSorter);
12952
- useKey({ key: "Escape" }, onClose);
12953
- const getColorFromDisplayName = (displayName) => authorColors.get(displayName) ?? "#333";
12954
- if (!open) return null;
12955
- const ungroupedAuthorsMessage = ungroupedAuthorsSorted.length === 0 ? "All detected authors have been grouped" : "Select the authors that you know are the same person";
12956
- const ungroupedAuthorsFiltered = ungroupedAuthorsSorted.filter((author) => author.toLowerCase().includes(filter.toLowerCase()));
12957
- const ungroupedAuthorsEntries = ungroupedAuthorsFiltered.map((author) => /* @__PURE__ */ jsx(CheckboxWithLabel, {
12958
- className: "hover:opacity-70",
12959
- checked: selectedAuthors.includes(author),
12960
- onChange: (e) => {
12961
- setSelectedAuthors(e.target?.checked ? [...selectedAuthors, author] : selectedAuthors.filter((a) => a !== author));
12962
- },
12963
- children: /* @__PURE__ */ jsxs("div", {
12964
- className: "inline-flex flex-row place-items-center gap-2",
12965
- children: [/* @__PURE__ */ jsx(LegendDot, { dotColor: getColorFromDisplayName(author) }), author]
13110
+ return /* @__PURE__ */ jsx("div", {
13111
+ ref,
13112
+ className: "flex flex-col justify-center",
13113
+ children: /* @__PURE__ */ jsx("svg", {
13114
+ ref: svgRef,
13115
+ width: "100%",
13116
+ height,
13117
+ className: "fill-transparent",
13118
+ children: data.map((d, i) => {
13119
+ const barX = (xScale(d.date) ?? 0) + 2;
13120
+ const barWidth = Math.max(1, xScale.bandwidth() - 4);
13121
+ const barHeight = height - yScale(d.count);
13122
+ return /* @__PURE__ */ jsxs("g", { children: [/* @__PURE__ */ jsx("rect", {
13123
+ x: barX,
13124
+ y: yScale(d.count),
13125
+ width: barWidth,
13126
+ height: barHeight,
13127
+ rx: 2,
13128
+ ry: 2,
13129
+ className: cn("transition-[height,y] duration-300 ease-out", d.timestamp >= start && d.timestamp < end ? "fill-blue-primary" : "fill-blue-primary/40")
13130
+ }), /* @__PURE__ */ jsx("rect", {
13131
+ x: barX,
13132
+ y: 0,
13133
+ width: barWidth,
13134
+ height,
13135
+ rx: 2,
13136
+ ry: 2,
13137
+ className: "hover:stroke-blue-secondary stroke-transparent stroke-1 hover:fill-transparent",
13138
+ onClick: () => {
13139
+ const [intervalStart, intervalEnd] = expandIntervalToRange(d.timestamp, databaseInfo.commitCountPerTimeIntervalUnit);
13140
+ updateTimeseries([intervalStart, intervalEnd]);
13141
+ },
13142
+ children: /* @__PURE__ */ jsx("title", { children: `${d.date}: ${d.count.toLocaleString()} commit${d.count !== 1 ? "s" : ""}` })
13143
+ })] }, `${d.date}-${i}`);
13144
+ })
12966
13145
  })
12967
- }, author));
12968
- const groupedAuthorsEntries = authorUnions.map((aliasGroup, aliasGroupIndex) => {
12969
- const displayName = aliasGroup[0];
12970
- const disabled$1 = navigationData.state !== "idle";
12971
- return /* @__PURE__ */ jsxs("div", {
12972
- className: "card group m-0 flex h-full flex-col p-2",
12973
- children: [
12974
- /* @__PURE__ */ jsxs("div", {
12975
- className: "inline-flex flex-row place-items-center gap-2",
12976
- children: [/* @__PURE__ */ jsx(LegendDot, { dotColor: getColorFromDisplayName(displayName) }), /* @__PURE__ */ jsx("b", {
12977
- className: "truncate",
12978
- title: displayName,
12979
- children: displayName
12980
- })]
12981
- }),
12982
- aliasGroup.slice(1).sort(stringSorter).map((alias) => /* @__PURE__ */ jsx(AliasEntry, {
12983
- alias,
12984
- disabled: disabled$1,
12985
- onClick: () => makePrimaryAlias(alias, aliasGroupIndex)
12986
- }, alias)),
12987
- /* @__PURE__ */ jsx("div", { className: "grow" }),
12988
- /* @__PURE__ */ jsxs("div", {
12989
- className: "flex items-end justify-end gap-2 opacity-0 transition-opacity duration-200 group-hover:opacity-100",
12990
- children: [/* @__PURE__ */ jsxs(Form, {
12991
- action: href("/view") + location$1.search,
12992
- method: "post",
12993
- children: [/* @__PURE__ */ jsx("input", {
12994
- type: "hidden",
12995
- name: "unionedAuthors",
12996
- value: JSON.stringify(authorUnions)
12997
- }), /* @__PURE__ */ jsx("button", {
12998
- className: "btn",
12999
- title: "Add selected authors to this group",
13000
- disabled: disabled$1 || selectedAuthors.length === 0,
13001
- onClick: () => {
13002
- const newAuthorUnions = authorUnions.map(([displayName$1, ...group], i) => {
13003
- if (i === aliasGroupIndex) return [
13004
- displayName$1,
13005
- ...group,
13006
- ...selectedAuthors
13007
- ];
13008
- return [displayName$1, ...group];
13009
- });
13010
- const form = new FormData();
13011
- form.append("unionedAuthors", JSON.stringify(newAuthorUnions));
13012
- submit(form, {
13013
- action: href("/view") + location$1.search,
13014
- method: "post"
13015
- });
13016
- setSelectedAuthors([]);
13017
- },
13018
- children: "Add selected"
13019
- })]
13020
- }), /* @__PURE__ */ jsx("button", {
13021
- className: "btn",
13022
- title: "Ungroup",
13023
- disabled: disabled$1,
13024
- onClick: () => ungroup(aliasGroupIndex),
13025
- children: "Ungroup"
13026
- })]
13027
- })
13028
- ]
13029
- }, aliasGroupIndex);
13030
13146
  });
13031
- return createPortal(/* @__PURE__ */ jsx("dialog", {
13032
- ref,
13033
- "aria-modal": true,
13034
- className: "z-10 m-auto flex h-full w-full flex-col items-start justify-stretch bg-transparent text-inherit backdrop:bg-gray-500/75 backdrop:p-0",
13035
- children: /* @__PURE__ */ jsxs("div", {
13036
- className: "card m-auto grid h-full w-full max-w-(--breakpoint-2xl) grow grid-cols-[1fr_1fr] grid-rows-[max-content_max-content_max-content_1fr_max-content] gap-2 overflow-hidden p-2 shadow-sm",
13037
- children: [
13038
- /* @__PURE__ */ jsx("h2", {
13039
- className: "text-2xl",
13040
- children: "Group authors"
13041
- }),
13042
- /* @__PURE__ */ jsx(CloseButton, {
13043
- absolute: false,
13044
- className: "justify-self-end",
13045
- onClick: onClose
13046
- }),
13047
- /* @__PURE__ */ jsxs("h3", {
13048
- className: "text-center text-lg font-bold",
13049
- children: [
13050
- "Ungrouped authors (",
13051
- ungroupedAuthorsSorted.length,
13052
- ")"
13053
- ]
13054
- }),
13055
- /* @__PURE__ */ jsx("h3", {
13056
- className: "text-center text-lg font-bold",
13057
- children: "Grouped authors"
13058
- }),
13059
- /* @__PURE__ */ jsx("div", {
13060
- className: "flex justify-end gap-2",
13061
- children: /* @__PURE__ */ jsxs("button", {
13062
- className: "btn btn--primary justify-self-end",
13063
- title: "Group the selected authors",
13064
- disabled: disabled || selectedAuthors.length === 0,
13065
- onClick: groupSelectedAuthors,
13066
- children: [/* @__PURE__ */ jsx(Icon, {
13067
- path: mdiAccountMultiple,
13068
- size: 1
13069
- }), "Create group"]
13070
- })
13071
- }),
13072
- /* @__PURE__ */ jsx("div", {
13073
- className: "flex justify-end gap-4",
13074
- children: /* @__PURE__ */ jsx("button", {
13075
- className: "btn btn--danger",
13076
- disabled: disabled || authorUnions.length === 0,
13077
- onClick: () => {
13078
- if (confirm("Are you sure you want to ungroup all grouped authors?")) ungroupAll();
13079
- },
13080
- children: "Ungroup all"
13081
- })
13082
- }),
13083
- /* @__PURE__ */ jsx("div", {
13084
- className: "overflow-y-auto",
13085
- children: /* @__PURE__ */ jsxs("div", {
13086
- className: "flex h-min min-h-0 flex-col gap-2 rounded-md bg-white p-4 pt-2 shadow-sm dark:bg-gray-700",
13087
- children: [/* @__PURE__ */ jsxs("div", {
13088
- className: "sticky top-0 flex gap-2 bg-inherit pt-2",
13089
- children: [
13090
- /* @__PURE__ */ jsx("input", {
13091
- className: "input min-w-0",
13092
- type: "search",
13093
- placeholder: "Filter...",
13094
- disabled: ungroupedAuthorsSorted.length === 0,
13095
- onChange: (e) => startTransition$1(() => setFilter(e.target.value))
13096
- }),
13097
- /* @__PURE__ */ jsx("button", {
13098
- disabled: disabled || selectedAuthors.length === 0,
13099
- className: "btn btn--outlined w-max grow",
13100
- title: "Clear selection",
13101
- onClick: () => setSelectedAuthors([]),
13102
- children: "Clear"
13147
+ };
13148
+ var BarChart_default = BarChart;
13149
+ function Timeline({ className }) {
13150
+ const [_, startTransition$1] = useTransition();
13151
+ const { databaseInfo } = useData();
13152
+ const { timerange, selectedRange } = databaseInfo;
13153
+ const newestChangeDate = timerange[1];
13154
+ const oldestChangeDate = timerange[0];
13155
+ const submit = useSubmit();
13156
+ const [range, setRange] = useState(selectedRange[0] === 0 ? timerange : selectedRange);
13157
+ const disabled = useNavigation().state !== "idle";
13158
+ const [searchParams] = useSearchParams();
13159
+ function updateTimeseries(e) {
13160
+ const form = new FormData();
13161
+ form.append("timeseries", `${e[0]}-${e[1]}`);
13162
+ submit(form, {
13163
+ action: getPathFromRepoAndHead({
13164
+ path: searchParams.get("path"),
13165
+ branch: databaseInfo.branch
13166
+ }),
13167
+ method: "post"
13168
+ });
13169
+ }
13170
+ const selectedStartDate = range[0] * 1e3;
13171
+ const selectedEndDate = range[1] * 1e3;
13172
+ return /* @__PURE__ */ jsxs("div", {
13173
+ className: cn("group flex flex-col gap-2", className),
13174
+ children: [
13175
+ /* @__PURE__ */ jsx(BarChart_default, {}),
13176
+ /* @__PURE__ */ jsxs(Slider, {
13177
+ className: "relative",
13178
+ mode: 2,
13179
+ step: 1,
13180
+ domain: timerange,
13181
+ values: range,
13182
+ disabled,
13183
+ onUpdate: (e) => setRange([...e]),
13184
+ onChange: (e) => {
13185
+ startTransition$1(() => {
13186
+ updateTimeseries(e);
13187
+ });
13188
+ },
13189
+ children: [
13190
+ /* @__PURE__ */ jsx(Rail, { children: SliderRail }),
13191
+ /* @__PURE__ */ jsx(Handles, { children: ({ handles, getHandleProps }) => /* @__PURE__ */ jsx("div", {
13192
+ className: "",
13193
+ children: handles.map((handle, i) => /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("div", {
13194
+ className: cn("absolute left-(--left) w-max translate-y-[calc(50%)] opacity-0 transition-opacity group-hover:opacity-100 focus:opacity-100", {
13195
+ "left-(--left)": i === 0,
13196
+ "right-[calc(100%-var(--left))] -translate-x-full": i === 1
13197
+ }),
13198
+ style: { "--left": `${handle.percent}%` },
13199
+ children: /* @__PURE__ */ jsx(Popover$1, {
13200
+ trigger: ({ onClick }) => /* @__PURE__ */ jsx("button", {
13201
+ className: cn("bg-blue-primary text-primary-text-dark cursor-pointer rounded-lg px-1 break-keep shadow", {
13202
+ "rounded-tl-none": i === 0,
13203
+ "rounded-tr-none": i === 1
13204
+ }),
13205
+ onClick,
13206
+ children: /* @__PURE__ */ jsx("time", {
13207
+ className: "w-max text-xs",
13208
+ dateTime: dateFormatISO(i === 0 ? selectedStartDate : selectedEndDate),
13209
+ title: `Click or drag to set the ${i === 0 ? "start" : "end"} of time range`,
13210
+ children: dateFormatShort(i === 0 ? selectedStartDate : selectedEndDate)
13211
+ })
13103
13212
  }),
13104
- /* @__PURE__ */ jsx("button", {
13105
- disabled: ungroupedAuthorsSorted.length === 0,
13106
- className: "btn btn--outlined w-max grow",
13107
- title: "Clear selection",
13108
- onClick: () => selectedAuthors.length === ungroupedAuthorsFiltered.length ? setSelectedAuthors([]) : setSelectedAuthors((selected) => Array.from(new Set([...selected, ...ungroupedAuthorsFiltered]))),
13109
- children: selectedAuthors.length === ungroupedAuthorsFiltered.length ? "Deselect all" : "Select all"
13213
+ children: /* @__PURE__ */ jsx(TimePicker, {
13214
+ setsBeginning: i === 0,
13215
+ range,
13216
+ setRange,
13217
+ timerange,
13218
+ updateTimeseries,
13219
+ disabled
13110
13220
  })
13111
- ]
13112
- }), ungroupedAuthorsEntries.length > 0 ? ungroupedAuthorsEntries : /* @__PURE__ */ jsx("p", {
13113
- className: "place-self-center",
13114
- children: filter.length > 0 ? "No authors found" : "All authors have been grouped"
13115
- })]
13221
+ }, handle.id)
13222
+ }), /* @__PURE__ */ jsx(Handle, {
13223
+ handleType: "square",
13224
+ title: "Drag to adjust interval, click to set specific date",
13225
+ className: cn(""),
13226
+ handle,
13227
+ domain: timerange,
13228
+ getHandleProps
13229
+ }, handle.id)] }, handle.id))
13230
+ }) }),
13231
+ /* @__PURE__ */ jsx(Tracks, {
13232
+ left: false,
13233
+ right: false,
13234
+ children: ({ tracks, getTrackProps }) => /* @__PURE__ */ jsx("div", { children: tracks.map(({ id, source, target }) => /* @__PURE__ */ jsx(Track, {
13235
+ trackType: "square",
13236
+ backgroundColor: "#7aa0c4",
13237
+ source,
13238
+ target,
13239
+ getTrackProps
13240
+ }, id)) })
13116
13241
  })
13117
- }),
13118
- /* @__PURE__ */ jsx("div", {
13119
- className: "overflow-y-auto",
13120
- children: /* @__PURE__ */ jsx("div", {
13121
- className: "grid h-min min-h-0 grid-cols-1 gap-4 rounded-md bg-white p-4 shadow-sm lg:grid-cols-2 xl:grid-cols-3 dark:bg-gray-700",
13122
- children: authorUnions.length > 0 ? groupedAuthorsEntries : /* @__PURE__ */ jsx("p", {
13123
- className: "place-self-center",
13124
- children: "No authors have been grouped yet"
13242
+ ]
13243
+ }),
13244
+ /* @__PURE__ */ jsx(TicksByCount, {
13245
+ className: "mb-2",
13246
+ count: 2,
13247
+ tickToLabel: (t) => dateFormatShort((oldestChangeDate + (newestChangeDate - oldestChangeDate) * t) * 1e3)
13248
+ })
13249
+ ]
13250
+ });
13251
+ }
13252
+ function TimePicker({ range, setRange, timerange, setsBeginning, updateTimeseries, disabled }) {
13253
+ return /* @__PURE__ */ jsx("div", {
13254
+ className: "z-30",
13255
+ children: /* @__PURE__ */ jsx(DatePicker, {
13256
+ inline: true,
13257
+ fixedHeight: true,
13258
+ renderCustomHeader: ({ date, decreaseMonth, increaseMonth, prevMonthButtonDisabled, nextMonthButtonDisabled }) => {
13259
+ return /* @__PURE__ */ jsxs("div", {
13260
+ className: "m-2 flex justify-between",
13261
+ children: [
13262
+ /* @__PURE__ */ jsx("button", {
13263
+ disabled: prevMonthButtonDisabled,
13264
+ onClick: decreaseMonth,
13265
+ children: "<"
13266
+ }),
13267
+ /* @__PURE__ */ jsx("h2", { children: dateFormatCalendarHeader(date.getTime()) }),
13268
+ /* @__PURE__ */ jsx("button", {
13269
+ disabled: nextMonthButtonDisabled,
13270
+ onClick: increaseMonth,
13271
+ children: ">"
13125
13272
  })
13126
- })
13127
- }),
13128
- /* @__PURE__ */ jsxs("div", {
13129
- className: "col-span-2 mr-6 grid w-full grid-cols-2 gap-4",
13130
- children: [/* @__PURE__ */ jsx("p", { children: ungroupedAuthorsMessage }), /* @__PURE__ */ jsx("button", {
13131
- className: "btn btn--primary justify-self-end",
13132
- onClick: onClose,
13133
- children: "Done"
13134
- })]
13135
- })
13136
- ]
13273
+ ]
13274
+ });
13275
+ },
13276
+ disabled,
13277
+ selected: /* @__PURE__ */ new Date(range[setsBeginning ? 0 : 1] * 1e3),
13278
+ minDate: new Date(setsBeginning ? timerange[0] * 1e3 : Math.max(timerange[0] * 1e3, range[0] * 1e3)),
13279
+ maxDate: new Date(setsBeginning ? Math.min(timerange[1] * 1e3, range[1] * 1e3) : timerange[1] * 1e3),
13280
+ onChange: (x) => {
13281
+ if (x) {
13282
+ let newRange;
13283
+ if (setsBeginning) newRange = [x.getTime() / 1e3, range[1]];
13284
+ else newRange = [range[0], x.getTime() / 1e3];
13285
+ setRange(newRange);
13286
+ updateTimeseries(newRange);
13287
+ }
13288
+ }
13137
13289
  })
13138
- }), document.body);
13139
- function AliasEntry({ alias, onClick, disabled: disabled$1 }) {
13140
- return /* @__PURE__ */ jsxs("button", {
13141
- className: "btn flex grid-flow-col gap-2 text-sm [&:hover>svg]:opacity-50 [&>svg]:opacity-0",
13142
- disabled: disabled$1,
13143
- title: "Make display name for this grouping",
13144
- onClick,
13145
- children: [/* @__PURE__ */ jsx(Icon, {
13146
- path: mdiArrowUp,
13147
- size: .75
13148
- }), /* @__PURE__ */ jsx("label", {
13149
- title: alias,
13150
- className: "label truncate",
13151
- children: alias
13152
- })]
13153
- }, alias);
13154
- }
13290
+ });
13155
13291
  }
13156
- var stringSorter = (a, b) => a.toLowerCase().localeCompare(b.toLowerCase());
13157
13292
  function RefreshButton() {
13158
13293
  const navigation = useNavigation();
13159
13294
  const isRefreshing = navigation.formData?.get("refresh") === "true";
@@ -13351,6 +13486,18 @@ function ChartTooltip({ hoveredObject }) {
13351
13486
  className: chartType === "BUBBLE_CHART" ? "rounded-xl" : "rounded-xs"
13352
13487
  });
13353
13488
  }
13489
+ function FullscreenButton() {
13490
+ const { isFullscreen, toggleFullscreen } = useFullscreen(() => document.documentElement);
13491
+ return /* @__PURE__ */ jsx("button", {
13492
+ className: cn("btn aspect-square p-1", { "btn--primary": isFullscreen }),
13493
+ title: isFullscreen ? "Exit fullscreen" : "Enter fullscreen",
13494
+ onClick: toggleFullscreen,
13495
+ children: /* @__PURE__ */ jsx(Icon, {
13496
+ path: isFullscreen ? mdiFullscreenExit : mdiFullscreen,
13497
+ size: 1
13498
+ })
13499
+ });
13500
+ }
13354
13501
  var CollapsibleHeaderContext = createContext$1((_open) => {});
13355
13502
  function useSetOpenCollapsibleHeader() {
13356
13503
  return use(CollapsibleHeaderContext);
@@ -13405,124 +13552,15 @@ function RevisionSelect({ headGroups, disabled, className = "", analyzedBranches
13405
13552
  })]
13406
13553
  });
13407
13554
  }
13408
- function CollapsableSettings({ className = "" }) {
13409
- return /* @__PURE__ */ jsx(Popover$1, {
13410
- trigger: ({ onClick }) => /* @__PURE__ */ jsx("button", {
13411
- title: "Visualization settings",
13412
- className: cn("btn hover:text-primary-text dark:hover:text-primary-text-dark relative flex cursor-pointer justify-between gap-2", className),
13413
- onClick,
13414
- children: /* @__PURE__ */ jsx(Icon, {
13415
- path: mdiCog,
13416
- size: "1.25em"
13417
- })
13418
- }),
13419
- children: /* @__PURE__ */ jsx(Settings, {})
13555
+ function SettingsButton() {
13556
+ const { openModal } = useModal("app-settings");
13557
+ return /* @__PURE__ */ jsx("button", {
13558
+ className: "btn hover:text-primary-text dark:hover:text-primary-text-dark relative flex cursor-pointer justify-between gap-2",
13559
+ title: "Visualization settings",
13560
+ onClick: () => openModal(),
13561
+ children: /* @__PURE__ */ jsx(Icon, { path: mdiCog })
13420
13562
  });
13421
13563
  }
13422
- function Settings() {
13423
- const { metricType, hierarchyType, transitionsEnabled, renderCutoff, showFilesWithoutChanges, linkMetricAndSizeMetric, showOnlySearchMatches, setLinkMetricAndSizeMetric, setTransitionsEnabled, labelsVisible, setLabelsVisible, setHierarchyType, setSizeMetricType, setRenderCutoff, setShowFilesWithoutChanges, setShowOnlySearchMatches } = useOptions();
13424
- const [isTransitioning, startTransition$1] = useTransition();
13425
- return /* @__PURE__ */ jsxs(Fragment, { children: [
13426
- /* @__PURE__ */ jsxs(CheckboxWithLabel, {
13427
- className: "text-sm",
13428
- checked: Boolean(linkMetricAndSizeMetric),
13429
- title: "Enable to sync size metric with color metric",
13430
- onChange: (e) => {
13431
- setLinkMetricAndSizeMetric(e.target.checked);
13432
- if (e.target.checked) setSizeMetricType(relatedSizeMetric[metricType]);
13433
- },
13434
- children: [/* @__PURE__ */ jsx(Icon, {
13435
- className: "ml-1.5",
13436
- path: mdiLink,
13437
- size: "1.25em"
13438
- }), /* @__PURE__ */ jsx("span", { children: "Link size and color option" })]
13439
- }),
13440
- /* @__PURE__ */ jsxs(CheckboxWithLabel, {
13441
- className: "text-sm",
13442
- checked: transitionsEnabled,
13443
- title: "Disable to improve performance when zooming",
13444
- onChange: (e) => setTransitionsEnabled(e.target.checked),
13445
- children: [/* @__PURE__ */ jsx(Icon, {
13446
- className: "ml-1.5",
13447
- path: mdiTransition,
13448
- size: "1.25em"
13449
- }), "Transitions"]
13450
- }),
13451
- /* @__PURE__ */ jsxs(CheckboxWithLabel, {
13452
- className: "text-sm",
13453
- checked: labelsVisible,
13454
- title: "Disable to improve performance",
13455
- onChange: (e) => setLabelsVisible(e.target.checked),
13456
- children: [/* @__PURE__ */ jsx(Icon, {
13457
- className: "ml-1.5",
13458
- path: mdiLabel,
13459
- size: "1.25em"
13460
- }), "Labels"]
13461
- }),
13462
- /* @__PURE__ */ jsxs(CheckboxWithLabel, {
13463
- className: "text-sm",
13464
- checked: showFilesWithoutChanges,
13465
- title: "Show files that have had no changes in the selected time range",
13466
- onChange: (e) => setShowFilesWithoutChanges(e.target.checked),
13467
- children: [/* @__PURE__ */ jsx(Icon, {
13468
- className: "ml-1.5",
13469
- path: mdiClockEdit,
13470
- size: "1.25em"
13471
- }), "Show files with no activity"]
13472
- }),
13473
- /* @__PURE__ */ jsxs(CheckboxWithLabel, {
13474
- className: "text-sm",
13475
- checked: hierarchyType === "FLAT",
13476
- title: "Show all files on the same level, instead of as a file tree",
13477
- onChange: () => {
13478
- if (hierarchyType === "FLAT") setHierarchyType("NESTED");
13479
- else setHierarchyType("FLAT");
13480
- },
13481
- children: [/* @__PURE__ */ jsx(Icon, {
13482
- className: "ml-1.5",
13483
- path: mdiFileTree,
13484
- size: "1.25em"
13485
- }), "Flatten file tree"]
13486
- }),
13487
- /* @__PURE__ */ jsxs(CheckboxWithLabel, {
13488
- className: "text-sm",
13489
- checked: showOnlySearchMatches,
13490
- title: "When searching, hide files that do not match the search query",
13491
- onChange: (e) => setShowOnlySearchMatches(e.target.checked),
13492
- children: [/* @__PURE__ */ jsx(Icon, {
13493
- className: "ml-1.5",
13494
- path: mdiFilter,
13495
- size: "1.25em"
13496
- }), "Show only search matches"]
13497
- }),
13498
- /* @__PURE__ */ jsxs("label", {
13499
- className: "label flex w-full items-center justify-start gap-2 text-sm",
13500
- title: "Increase this to improve render performance, decrease it to get higher level of detail",
13501
- children: [/* @__PURE__ */ jsxs("span", {
13502
- className: "flex grow items-center gap-2",
13503
- children: [
13504
- /* @__PURE__ */ jsx(Icon, {
13505
- className: "ml-1.5",
13506
- path: mdiContentCut,
13507
- size: "1.25em"
13508
- }),
13509
- "Pixel render cut-off ",
13510
- isTransitioning ? /* @__PURE__ */ jsx("img", {
13511
- src: truck_default,
13512
- alt: "...",
13513
- className: "h-5"
13514
- }) : ""
13515
- ]
13516
- }), /* @__PURE__ */ jsx("input", {
13517
- type: "number",
13518
- min: 0,
13519
- defaultValue: renderCutoff,
13520
- className: "mr-1 w-12 place-self-end border-b-2",
13521
- onChange: (x) => startTransition$1(() => setRenderCutoff(x.target.valueAsNumber))
13522
- })]
13523
- })
13524
- ] });
13525
- }
13526
13564
  function BrowseParentFolder() {
13527
13565
  const dataPromise = useRouteLoaderData(href("/view"))?.dataPromise;
13528
13566
  const data = dataPromise ? use(dataPromise) : null;
@@ -13537,11 +13575,9 @@ var view_exports = /* @__PURE__ */ __export({
13537
13575
  loader: () => loader$8,
13538
13576
  meta: () => meta,
13539
13577
  middleware: () => middleware,
13540
- useRepoContext: () => useRepoContext,
13541
13578
  viewSearchParamsConfig: () => viewSearchParamsConfig,
13542
13579
  viewSerializer: () => viewSerializer
13543
13580
  }, 1);
13544
- const useRepoContext = () => useOutletContext();
13545
13581
  const currentRepositoryContext = createContext();
13546
13582
  const viewSearchParamsConfig = {
13547
13583
  path: parseAsString,
@@ -13765,9 +13801,7 @@ var view_default = UNSAFE_withComponentProps(function Repo() {
13765
13801
  }
13766
13802
  }, { leftExpanded: true });
13767
13803
  const toggleLeft = () => dispatch("toggleLeft");
13768
- const [unionAuthorsModalOpen, setUnionAuthorsModalOpen] = useState(false);
13769
13804
  const [hoveredObject, setHoveredObject] = useState(null);
13770
- const showUnionAuthorsModal = () => setUnionAuthorsModalOpen(true);
13771
13805
  const location$1 = useLocation();
13772
13806
  const navigate = useNavigate();
13773
13807
  const [objectPath] = useQueryState("objectPath", viewSearchParamsConfig.objectPath);
@@ -13830,7 +13864,7 @@ var view_default = UNSAFE_withComponentProps(function Repo() {
13830
13864
  ]
13831
13865
  }) }) : "Details" }),
13832
13866
  contentClassName: "pb-6",
13833
- children: /* @__PURE__ */ jsx(Outlet, { context: { showUnionAuthorsModal } })
13867
+ children: /* @__PURE__ */ jsx(Outlet, {})
13834
13868
  }),
13835
13869
  /* @__PURE__ */ jsx(CollapsibleHeader, {
13836
13870
  className: "card",
@@ -13842,10 +13876,7 @@ var view_default = UNSAFE_withComponentProps(function Repo() {
13842
13876
  className: "card",
13843
13877
  title: "Legend",
13844
13878
  contentClassName: "pb-6",
13845
- children: /* @__PURE__ */ jsx(Legend, {
13846
- hoveredObject,
13847
- showUnionAuthorsModal
13848
- })
13879
+ children: /* @__PURE__ */ jsx(Legend, { hoveredObject })
13849
13880
  })
13850
13881
  ]
13851
13882
  })
@@ -13889,8 +13920,8 @@ var view_default = UNSAFE_withComponentProps(function Repo() {
13889
13920
  /* @__PURE__ */ jsx(SearchCard, {}),
13890
13921
  /* @__PURE__ */ jsx(RefreshButton, {}),
13891
13922
  /* @__PURE__ */ jsx(HiddenFiles, {}),
13892
- /* @__PURE__ */ jsx(FullscreenButton, {}),
13893
- /* @__PURE__ */ jsx(CollapsableSettings, {})
13923
+ /* @__PURE__ */ jsx(SettingsButton, {}),
13924
+ /* @__PURE__ */ jsx(FullscreenButton, {})
13894
13925
  ]
13895
13926
  })
13896
13927
  ]
@@ -13934,12 +13965,7 @@ var view_default = UNSAFE_withComponentProps(function Repo() {
13934
13965
  })
13935
13966
  ]
13936
13967
  })]
13937
- }), /* @__PURE__ */ jsx(UnionAuthorsModal, {
13938
- open: unionAuthorsModalOpen,
13939
- onClose: () => {
13940
- setUnionAuthorsModalOpen(false);
13941
- }
13942
- })]
13968
+ }), /* @__PURE__ */ jsx(ModalManager, {})]
13943
13969
  })
13944
13970
  })
13945
13971
  });
@@ -13984,14 +14010,6 @@ async function loader$4({ request, params }) {
13984
14010
  const path$1 = params.path ?? loadViewSearchParams(request).path ?? ".";
13985
14011
  throw redirect(await GitCaller.isValidGitRepo(path$1) ? href("/view") + viewSerializer({ path: path$1 }) : href("/browse") + browseSerializer({ path: path$1 }));
13986
14012
  }
13987
- var view_merge_authors_exports = /* @__PURE__ */ __export({ default: () => view_merge_authors_default }, 1);
13988
- var view_merge_authors_default = UNSAFE_withComponentProps(function MergeAuthorsView() {
13989
- const navigate = useNavigate();
13990
- return /* @__PURE__ */ jsx(UnionAuthorsModal, {
13991
- open: true,
13992
- onClose: () => navigate("..")
13993
- });
13994
- });
13995
14013
  var view_progress_exports = /* @__PURE__ */ __export({ loader: () => loader$3 }, 1);
13996
14014
  var POLLING_RATE = 500;
13997
14015
  const loader$3 = async ({ context }) => {
@@ -14294,7 +14312,6 @@ const action = async ({ request, context }) => {
14294
14312
  };
14295
14313
  var view_details_default = UNSAFE_withComponentProps(function Details() {
14296
14314
  const { setPath } = usePath();
14297
- const { showUnionAuthorsModal } = useRepoContext();
14298
14315
  const { path: path$1, authorDistributionPromise } = useLoaderData();
14299
14316
  const data = useData();
14300
14317
  const { state } = useNavigation();
@@ -14306,6 +14323,7 @@ var view_details_default = UNSAFE_withComponentProps(function Details() {
14306
14323
  ...viewSearchParams,
14307
14324
  zoomPath: path$1
14308
14325
  });
14326
+ const { openModal } = useModal("group-authors");
14309
14327
  useEffect(() => {
14310
14328
  setOpen(!!clickedObject);
14311
14329
  }, [clickedObject, setOpen]);
@@ -14344,7 +14362,7 @@ var view_details_default = UNSAFE_withComponentProps(function Details() {
14344
14362
  }),
14345
14363
  /* @__PURE__ */ jsxs("button", {
14346
14364
  className: "btn",
14347
- onClick: showUnionAuthorsModal,
14365
+ onClick: () => openModal(),
14348
14366
  children: [/* @__PURE__ */ jsx(Icon, { path: mdiAccountMultiple }), "Group authors"]
14349
14367
  }),
14350
14368
  /* @__PURE__ */ jsxs("div", {
@@ -14971,7 +14989,7 @@ var ui_default = UNSAFE_withComponentProps(function UI() {
14971
14989
  children: [/* @__PURE__ */ jsx("h2", {
14972
14990
  className: "card__title",
14973
14991
  children: "AuthorOptions"
14974
- }), /* @__PURE__ */ jsx(AuthorOptions, { showUnionAuthorsModal: () => alert("Show union authors modal") })]
14992
+ }), /* @__PURE__ */ jsx(AuthorOptions, {})]
14975
14993
  }),
14976
14994
  /* @__PURE__ */ jsxs("div", {
14977
14995
  className: "card",
@@ -15093,12 +15111,11 @@ async function loader({ request, params }) {
15093
15111
  }
15094
15112
  var server_manifest_default = {
15095
15113
  "entry": {
15096
- "module": "/assets/entry.client-NklqTk8N.js",
15114
+ "module": "/assets/entry.client-lU2GATMj.js",
15097
15115
  "imports": [
15098
- "/assets/chunk-LFPYN7LY-CPTP6242.js",
15099
- "/assets/react-dom-_FKEaPb7.js",
15100
- "/assets/chunk-JPUPSTYD-XO4UPun8.js",
15101
- "/assets/jsx-runtime-BVRj4wYA.js"
15116
+ "/assets/jsx-runtime-s_YwNCZb.js",
15117
+ "/assets/react-dom-BkDQo3BN.js",
15118
+ "/assets/chunk-JPUPSTYD-YBbsfvLw.js"
15102
15119
  ],
15103
15120
  "css": []
15104
15121
  },
@@ -15116,40 +15133,16 @@ var server_manifest_default = {
15116
15133
  "hasClientMiddleware": false,
15117
15134
  "hasDefaultExport": true,
15118
15135
  "hasErrorBoundary": true,
15119
- "module": "/assets/root-Djh_4EO9.js",
15136
+ "module": "/assets/root-CpxyjKLd.js",
15120
15137
  "imports": [
15121
- "/assets/chunk-LFPYN7LY-CPTP6242.js",
15122
- "/assets/react-dom-_FKEaPb7.js",
15123
- "/assets/chunk-JPUPSTYD-XO4UPun8.js",
15124
- "/assets/jsx-runtime-BVRj4wYA.js",
15125
- "/assets/styling-B8OEge8x.js",
15126
- "/assets/util-1G1Ez1rt.js",
15127
- "/assets/compare-Br3z3FUS-OODPzXmX.js",
15128
- "/assets/GitTruckInfo-C1VOiqSW.js",
15129
- "/assets/clear-cache-CrL0weu5.js"
15138
+ "/assets/jsx-runtime-s_YwNCZb.js",
15139
+ "/assets/react-dom-BkDQo3BN.js",
15140
+ "/assets/chunk-JPUPSTYD-YBbsfvLw.js",
15141
+ "/assets/GitTruckInfo-CiYjIacT.js",
15142
+ "/assets/compare-Br3z3FUS-CQWcjqXQ.js",
15143
+ "/assets/clear-cache-C5k9U711.js"
15130
15144
  ],
15131
- "css": ["/assets/root-qwQAVU-u.css"],
15132
- "clientActionModule": void 0,
15133
- "clientLoaderModule": void 0,
15134
- "clientMiddlewareModule": void 0,
15135
- "hydrateFallbackModule": void 0
15136
- },
15137
- "routes/FullscreenButton": {
15138
- "id": "routes/FullscreenButton",
15139
- "parentId": "root",
15140
- "path": "FullscreenButton",
15141
- "index": void 0,
15142
- "caseSensitive": void 0,
15143
- "hasAction": false,
15144
- "hasLoader": false,
15145
- "hasClientAction": false,
15146
- "hasClientLoader": false,
15147
- "hasClientMiddleware": false,
15148
- "hasDefaultExport": false,
15149
- "hasErrorBoundary": false,
15150
- "module": "/assets/FullscreenButton-CFqdBqf-.js",
15151
- "imports": ["/assets/styling-B8OEge8x.js", "/assets/jsx-runtime-BVRj4wYA.js"],
15152
- "css": [],
15145
+ "css": ["/assets/root-CaiUVml-.css"],
15153
15146
  "clientActionModule": void 0,
15154
15147
  "clientLoaderModule": void 0,
15155
15148
  "clientMiddlewareModule": void 0,
@@ -15168,7 +15161,7 @@ var server_manifest_default = {
15168
15161
  "hasClientMiddleware": false,
15169
15162
  "hasDefaultExport": false,
15170
15163
  "hasErrorBoundary": false,
15171
- "module": "/assets/_._well-known._-DIwl00oI.js",
15164
+ "module": "/assets/_._well-known._-8QUD84tm.js",
15172
15165
  "imports": [],
15173
15166
  "css": [],
15174
15167
  "clientActionModule": void 0,
@@ -15189,15 +15182,12 @@ var server_manifest_default = {
15189
15182
  "hasClientMiddleware": false,
15190
15183
  "hasDefaultExport": true,
15191
15184
  "hasErrorBoundary": false,
15192
- "module": "/assets/clear-cache-82hexQD7.js",
15185
+ "module": "/assets/clear-cache-Xrw5kNtu.js",
15193
15186
  "imports": [
15194
- "/assets/styling-B8OEge8x.js",
15195
- "/assets/util-1G1Ez1rt.js",
15196
- "/assets/react-dom-_FKEaPb7.js",
15197
- "/assets/jsx-runtime-BVRj4wYA.js",
15198
- "/assets/GitTruckInfo-C1VOiqSW.js",
15199
- "/assets/clear-cache-CrL0weu5.js",
15200
- "/assets/chunk-LFPYN7LY-CPTP6242.js"
15187
+ "/assets/jsx-runtime-s_YwNCZb.js",
15188
+ "/assets/GitTruckInfo-CiYjIacT.js",
15189
+ "/assets/react-dom-BkDQo3BN.js",
15190
+ "/assets/clear-cache-C5k9U711.js"
15201
15191
  ],
15202
15192
  "css": [],
15203
15193
  "clientActionModule": void 0,
@@ -15218,7 +15208,7 @@ var server_manifest_default = {
15218
15208
  "hasClientMiddleware": false,
15219
15209
  "hasDefaultExport": false,
15220
15210
  "hasErrorBoundary": false,
15221
- "module": "/assets/commitcount-eFguKtSp.js",
15211
+ "module": "/assets/commitcount-B_3CeIvr.js",
15222
15212
  "imports": [],
15223
15213
  "css": [],
15224
15214
  "clientActionModule": void 0,
@@ -15239,7 +15229,7 @@ var server_manifest_default = {
15239
15229
  "hasClientMiddleware": false,
15240
15230
  "hasDefaultExport": false,
15241
15231
  "hasErrorBoundary": false,
15242
- "module": "/assets/commits-DPfZp-l3.js",
15232
+ "module": "/assets/commits-DDIAHyHU.js",
15243
15233
  "imports": [],
15244
15234
  "css": [],
15245
15235
  "clientActionModule": void 0,
@@ -15260,7 +15250,7 @@ var server_manifest_default = {
15260
15250
  "hasClientMiddleware": false,
15261
15251
  "hasDefaultExport": false,
15262
15252
  "hasErrorBoundary": false,
15263
- "module": "/assets/_index-J0jvF8An.js",
15253
+ "module": "/assets/_index-VLoUE117.js",
15264
15254
  "imports": [],
15265
15255
  "css": [],
15266
15256
  "clientActionModule": void 0,
@@ -15281,18 +15271,14 @@ var server_manifest_default = {
15281
15271
  "hasClientMiddleware": false,
15282
15272
  "hasDefaultExport": true,
15283
15273
  "hasErrorBoundary": false,
15284
- "module": "/assets/browse-CPxPbHtx.js",
15274
+ "module": "/assets/browse-Ce7uXDK8.js",
15285
15275
  "imports": [
15286
- "/assets/browse-CuAoODR1.js",
15287
- "/assets/styling-B8OEge8x.js",
15288
- "/assets/util-1G1Ez1rt.js",
15289
- "/assets/react-dom-_FKEaPb7.js",
15290
- "/assets/jsx-runtime-BVRj4wYA.js",
15291
- "/assets/GitTruckInfo-C1VOiqSW.js",
15292
- "/assets/UnionAuthorsModal-D-H775E5.js",
15293
- "/assets/chunk-LFPYN7LY-CPTP6242.js",
15294
- "/assets/compare-Br3z3FUS-OODPzXmX.js",
15295
- "/assets/chunk-JPUPSTYD-XO4UPun8.js"
15276
+ "/assets/jsx-runtime-s_YwNCZb.js",
15277
+ "/assets/browse-wwHpvs7Z.js",
15278
+ "/assets/GitTruckInfo-CiYjIacT.js",
15279
+ "/assets/react-dom-BkDQo3BN.js",
15280
+ "/assets/compare-Br3z3FUS-CQWcjqXQ.js",
15281
+ "/assets/chunk-JPUPSTYD-YBbsfvLw.js"
15296
15282
  ],
15297
15283
  "css": [],
15298
15284
  "clientActionModule": void 0,
@@ -15313,7 +15299,7 @@ var server_manifest_default = {
15313
15299
  "hasClientMiddleware": false,
15314
15300
  "hasDefaultExport": false,
15315
15301
  "hasErrorBoundary": false,
15316
- "module": "/assets/_path-bRAhzWvL.js",
15302
+ "module": "/assets/_path-DzSNgPJP.js",
15317
15303
  "imports": [],
15318
15304
  "css": [],
15319
15305
  "clientActionModule": void 0,
@@ -15334,47 +15320,14 @@ var server_manifest_default = {
15334
15320
  "hasClientMiddleware": false,
15335
15321
  "hasDefaultExport": true,
15336
15322
  "hasErrorBoundary": false,
15337
- "module": "/assets/view-ESMBoxIY.js",
15338
- "imports": [
15339
- "/assets/browse-CuAoODR1.js",
15340
- "/assets/styling-B8OEge8x.js",
15341
- "/assets/util-1G1Ez1rt.js",
15342
- "/assets/react-dom-_FKEaPb7.js",
15343
- "/assets/jsx-runtime-BVRj4wYA.js",
15344
- "/assets/GitTruckInfo-C1VOiqSW.js",
15345
- "/assets/UnionAuthorsModal-D-H775E5.js",
15346
- "/assets/chunk-LFPYN7LY-CPTP6242.js",
15347
- "/assets/compare-Br3z3FUS-OODPzXmX.js",
15348
- "/assets/chunk-JPUPSTYD-XO4UPun8.js"
15349
- ],
15350
- "css": [],
15351
- "clientActionModule": void 0,
15352
- "clientLoaderModule": void 0,
15353
- "clientMiddlewareModule": void 0,
15354
- "hydrateFallbackModule": void 0
15355
- },
15356
- "routes/view.merge-authors": {
15357
- "id": "routes/view.merge-authors",
15358
- "parentId": "routes/view",
15359
- "path": "merge-authors",
15360
- "index": void 0,
15361
- "caseSensitive": void 0,
15362
- "hasAction": false,
15363
- "hasLoader": false,
15364
- "hasClientAction": false,
15365
- "hasClientLoader": false,
15366
- "hasClientMiddleware": false,
15367
- "hasDefaultExport": true,
15368
- "hasErrorBoundary": false,
15369
- "module": "/assets/view.merge-authors-VwPHWL-d.js",
15323
+ "module": "/assets/view-DhREUKDU.js",
15370
15324
  "imports": [
15371
- "/assets/chunk-LFPYN7LY-CPTP6242.js",
15372
- "/assets/styling-B8OEge8x.js",
15373
- "/assets/util-1G1Ez1rt.js",
15374
- "/assets/react-dom-_FKEaPb7.js",
15375
- "/assets/jsx-runtime-BVRj4wYA.js",
15376
- "/assets/UnionAuthorsModal-D-H775E5.js",
15377
- "/assets/chunk-JPUPSTYD-XO4UPun8.js"
15325
+ "/assets/jsx-runtime-s_YwNCZb.js",
15326
+ "/assets/browse-wwHpvs7Z.js",
15327
+ "/assets/GitTruckInfo-CiYjIacT.js",
15328
+ "/assets/react-dom-BkDQo3BN.js",
15329
+ "/assets/compare-Br3z3FUS-CQWcjqXQ.js",
15330
+ "/assets/chunk-JPUPSTYD-YBbsfvLw.js"
15378
15331
  ],
15379
15332
  "css": [],
15380
15333
  "clientActionModule": void 0,
@@ -15395,7 +15348,7 @@ var server_manifest_default = {
15395
15348
  "hasClientMiddleware": false,
15396
15349
  "hasDefaultExport": false,
15397
15350
  "hasErrorBoundary": false,
15398
- "module": "/assets/view.progress-B7Os3EHt.js",
15351
+ "module": "/assets/view.progress-CEmcNaLe.js",
15399
15352
  "imports": [],
15400
15353
  "css": [],
15401
15354
  "clientActionModule": void 0,
@@ -15416,19 +15369,15 @@ var server_manifest_default = {
15416
15369
  "hasClientMiddleware": false,
15417
15370
  "hasDefaultExport": true,
15418
15371
  "hasErrorBoundary": false,
15419
- "module": "/assets/view.commits-DP_ltd6_.js",
15372
+ "module": "/assets/view.commits-D8Z4b-x3.js",
15420
15373
  "imports": [
15421
- "/assets/chunk-LFPYN7LY-CPTP6242.js",
15422
- "/assets/browse-CuAoODR1.js",
15423
- "/assets/styling-B8OEge8x.js",
15424
- "/assets/util-1G1Ez1rt.js",
15425
- "/assets/react-dom-_FKEaPb7.js",
15426
- "/assets/jsx-runtime-BVRj4wYA.js",
15427
- "/assets/GitTruckInfo-C1VOiqSW.js",
15428
- "/assets/RepoTabs-xQ7LCUn1.js",
15429
- "/assets/UnionAuthorsModal-D-H775E5.js",
15430
- "/assets/compare-Br3z3FUS-OODPzXmX.js",
15431
- "/assets/chunk-JPUPSTYD-XO4UPun8.js"
15374
+ "/assets/jsx-runtime-s_YwNCZb.js",
15375
+ "/assets/browse-wwHpvs7Z.js",
15376
+ "/assets/GitTruckInfo-CiYjIacT.js",
15377
+ "/assets/react-dom-BkDQo3BN.js",
15378
+ "/assets/RepoTabs-CWt-EVgp.js",
15379
+ "/assets/compare-Br3z3FUS-CQWcjqXQ.js",
15380
+ "/assets/chunk-JPUPSTYD-YBbsfvLw.js"
15432
15381
  ],
15433
15382
  "css": [],
15434
15383
  "clientActionModule": void 0,
@@ -15449,19 +15398,15 @@ var server_manifest_default = {
15449
15398
  "hasClientMiddleware": false,
15450
15399
  "hasDefaultExport": true,
15451
15400
  "hasErrorBoundary": false,
15452
- "module": "/assets/view.details-C6kuGyhb.js",
15401
+ "module": "/assets/view.details-Bsu_yyiE.js",
15453
15402
  "imports": [
15454
- "/assets/chunk-LFPYN7LY-CPTP6242.js",
15455
- "/assets/browse-CuAoODR1.js",
15456
- "/assets/styling-B8OEge8x.js",
15457
- "/assets/util-1G1Ez1rt.js",
15458
- "/assets/react-dom-_FKEaPb7.js",
15459
- "/assets/jsx-runtime-BVRj4wYA.js",
15460
- "/assets/GitTruckInfo-C1VOiqSW.js",
15461
- "/assets/RepoTabs-xQ7LCUn1.js",
15462
- "/assets/UnionAuthorsModal-D-H775E5.js",
15463
- "/assets/compare-Br3z3FUS-OODPzXmX.js",
15464
- "/assets/chunk-JPUPSTYD-XO4UPun8.js"
15403
+ "/assets/jsx-runtime-s_YwNCZb.js",
15404
+ "/assets/browse-wwHpvs7Z.js",
15405
+ "/assets/GitTruckInfo-CiYjIacT.js",
15406
+ "/assets/react-dom-BkDQo3BN.js",
15407
+ "/assets/RepoTabs-CWt-EVgp.js",
15408
+ "/assets/compare-Br3z3FUS-CQWcjqXQ.js",
15409
+ "/assets/chunk-JPUPSTYD-YBbsfvLw.js"
15465
15410
  ],
15466
15411
  "css": [],
15467
15412
  "clientActionModule": void 0,
@@ -15482,8 +15427,8 @@ var server_manifest_default = {
15482
15427
  "hasClientMiddleware": false,
15483
15428
  "hasDefaultExport": true,
15484
15429
  "hasErrorBoundary": false,
15485
- "module": "/assets/view._index-CjsEuBV6.js",
15486
- "imports": ["/assets/chunk-LFPYN7LY-CPTP6242.js", "/assets/jsx-runtime-BVRj4wYA.js"],
15430
+ "module": "/assets/view._index-jmCCN9mA.js",
15431
+ "imports": ["/assets/jsx-runtime-s_YwNCZb.js"],
15487
15432
  "css": [],
15488
15433
  "clientActionModule": void 0,
15489
15434
  "clientLoaderModule": void 0,
@@ -15503,18 +15448,14 @@ var server_manifest_default = {
15503
15448
  "hasClientMiddleware": false,
15504
15449
  "hasDefaultExport": true,
15505
15450
  "hasErrorBoundary": false,
15506
- "module": "/assets/ui-nIuMvo3S.js",
15451
+ "module": "/assets/ui-EnssW_g_.js",
15507
15452
  "imports": [
15508
- "/assets/chunk-LFPYN7LY-CPTP6242.js",
15509
- "/assets/browse-CuAoODR1.js",
15510
- "/assets/styling-B8OEge8x.js",
15511
- "/assets/util-1G1Ez1rt.js",
15512
- "/assets/react-dom-_FKEaPb7.js",
15513
- "/assets/jsx-runtime-BVRj4wYA.js",
15514
- "/assets/GitTruckInfo-C1VOiqSW.js",
15515
- "/assets/UnionAuthorsModal-D-H775E5.js",
15516
- "/assets/compare-Br3z3FUS-OODPzXmX.js",
15517
- "/assets/chunk-JPUPSTYD-XO4UPun8.js"
15453
+ "/assets/jsx-runtime-s_YwNCZb.js",
15454
+ "/assets/browse-wwHpvs7Z.js",
15455
+ "/assets/GitTruckInfo-CiYjIacT.js",
15456
+ "/assets/react-dom-BkDQo3BN.js",
15457
+ "/assets/compare-Br3z3FUS-CQWcjqXQ.js",
15458
+ "/assets/chunk-JPUPSTYD-YBbsfvLw.js"
15518
15459
  ],
15519
15460
  "css": [],
15520
15461
  "clientActionModule": void 0,
@@ -15535,7 +15476,7 @@ var server_manifest_default = {
15535
15476
  "hasClientMiddleware": false,
15536
15477
  "hasDefaultExport": false,
15537
15478
  "hasErrorBoundary": false,
15538
- "module": "/assets/_-RagYKtT_.js",
15479
+ "module": "/assets/_-CT17qCl3.js",
15539
15480
  "imports": [],
15540
15481
  "css": [],
15541
15482
  "clientActionModule": void 0,
@@ -15544,8 +15485,8 @@ var server_manifest_default = {
15544
15485
  "hydrateFallbackModule": void 0
15545
15486
  }
15546
15487
  },
15547
- "url": "/assets/manifest-b5d81016.js",
15548
- "version": "b5d81016",
15488
+ "url": "/assets/manifest-3c232792.js",
15489
+ "version": "3c232792",
15549
15490
  "sri": void 0
15550
15491
  };
15551
15492
  const assetsBuildDirectory = "build/client";
@@ -15577,14 +15518,6 @@ const routes = {
15577
15518
  caseSensitive: void 0,
15578
15519
  module: root_exports
15579
15520
  },
15580
- "routes/FullscreenButton": {
15581
- id: "routes/FullscreenButton",
15582
- parentId: "root",
15583
- path: "FullscreenButton",
15584
- index: void 0,
15585
- caseSensitive: void 0,
15586
- module: FullscreenButton_exports
15587
- },
15588
15521
  "routes/[.]well-known.$": {
15589
15522
  id: "routes/[.]well-known.$",
15590
15523
  parentId: "root",
@@ -15649,14 +15582,6 @@ const routes = {
15649
15582
  caseSensitive: void 0,
15650
15583
  module: view_exports
15651
15584
  },
15652
- "routes/view.merge-authors": {
15653
- id: "routes/view.merge-authors",
15654
- parentId: "routes/view",
15655
- path: "merge-authors",
15656
- index: void 0,
15657
- caseSensitive: void 0,
15658
- module: view_merge_authors_exports
15659
- },
15660
15585
  "routes/view.progress": {
15661
15586
  id: "routes/view.progress",
15662
15587
  parentId: "routes/view",