git-truck 3.1.1 → 3.2.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 (35) hide show
  1. package/build/client/assets/RepoTabs-36qUc-RT.js +1 -0
  2. package/build/client/assets/browse-B9wBPRaK.js +9 -0
  3. package/build/client/assets/browse-Bx9O5Mvl.js +1 -0
  4. package/build/client/assets/clear-cache-BJQQzFka.js +1 -0
  5. package/build/client/assets/clear-cache-DkN7P69Z.js +1 -0
  6. package/build/client/assets/dist-Bpk5WHYg.js +2 -0
  7. package/build/client/assets/{entry.client-lU2GATMj.js → entry.client-Ci1gLbyI.js} +1 -1
  8. package/build/client/assets/manifest-601d0239.js +1 -0
  9. package/build/client/assets/react-dom-B9ui1jR3.js +1 -0
  10. package/build/client/assets/root-BK0u4vgl.css +1 -0
  11. package/build/client/assets/root-CGqKHKfX.js +3 -0
  12. package/build/client/assets/ui-Bw635ygf.js +1 -0
  13. package/build/client/assets/view-mmd0Q4B8.js +1 -0
  14. package/build/client/assets/view.commits-b-k-A7BT.js +1 -0
  15. package/build/client/assets/view.details-B7LCPu46.js +1 -0
  16. package/build/server/assets/{server-build-BqWgrywa.js → server-build-voprGiDU.js} +565 -491
  17. package/build/server/index.js +1 -1
  18. package/cli.mjs +199 -37881
  19. package/package.json +3 -3
  20. package/build/client/assets/GitTruckInfo-Bu3InKk0.js +0 -1
  21. package/build/client/assets/RepoTabs-B_gVZfyQ.js +0 -1
  22. package/build/client/assets/browse-BVbfiHLu.js +0 -1
  23. package/build/client/assets/browse-DMoBg6Vh.js +0 -9
  24. package/build/client/assets/chunk-JPUPSTYD-YBbsfvLw.js +0 -1
  25. package/build/client/assets/clear-cache-CAdofhUa.js +0 -1
  26. package/build/client/assets/clear-cache-Ciosxv25.js +0 -1
  27. package/build/client/assets/compare-Br3z3FUS-Bm1os1Jk.js +0 -2
  28. package/build/client/assets/manifest-bbde1677.js +0 -1
  29. package/build/client/assets/react-dom-BkDQo3BN.js +0 -1
  30. package/build/client/assets/root-CMX9SEB-.js +0 -3
  31. package/build/client/assets/root-DEstG3hL.css +0 -1
  32. package/build/client/assets/ui-C21Ckl3a.js +0 -1
  33. package/build/client/assets/view-JlAiRFiK.js +0 -1
  34. package/build/client/assets/view.commits-CIgkE9N1.js +0 -1
  35. package/build/client/assets/view.details-DAXK445Q.js +0 -1
@@ -4,9 +4,9 @@ import { Await, Form, Link, Links, Meta, Outlet, Scripts, ScrollRestoration, Ser
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
- import * as React$1 from "react";
7
+ import * as React 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, 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";
9
+ import { mdiAccount, mdiAccountGroup, mdiAccountMultiple, 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, 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";
@@ -30,11 +30,11 @@ import { existsSync as existsSync$1, promises as promises$1, readFileSync } from
30
30
  import os$1, { cpus, freemem, totalmem } from "node:os";
31
31
  import { inflateSync } from "node:zlib";
32
32
  import { readFile, readdir } from "node:fs/promises";
33
+ import { createSerializer, parseAsBoolean, parseAsInteger, parseAsNumberLiteral, parseAsString, parseAsStringLiteral, useQueryState, useQueryStates } from "nuqs";
33
34
  import { NuqsAdapter } from "nuqs/adapters/react-router/v7";
34
35
  import randomstring from "randomstring";
35
36
  import { createPortal } from "react-dom";
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";
37
+ import { createLoader, createSerializer as createSerializer$1, parseAsInteger as parseAsInteger$1, parseAsString as parseAsString$1, 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
40
  import { Field, Label, Radio, RadioGroup } from "@headlessui/react";
@@ -180,7 +180,7 @@ function handleBrowserRequest(request, responseStatusCode, responseHeaders, reac
180
180
  });
181
181
  }
182
182
  const name = "git-truck";
183
- const version = "3.1.1";
183
+ const version = "3.2.0";
184
184
  const description = "Visualizing a Git repository";
185
185
  const main = "./cli.mjs";
186
186
  const bin = "./cli.mjs";
@@ -239,6 +239,7 @@ const dependencies = {
239
239
  "clsx": "^2.1.1",
240
240
  "color-convert": "^3.1.3",
241
241
  "d3": "^7.9.0",
242
+ "express": "^5.2.1",
242
243
  "ignore": "^7.0.5",
243
244
  "nanospinner": "^1.2.2",
244
245
  "nuqs": "^2.8.8",
@@ -279,7 +280,6 @@ const devDependencies = {
279
280
  "eslint-plugin-jsx-a11y": "^6.10.2",
280
281
  "eslint-plugin-react": "^7.37.5",
281
282
  "eslint-plugin-react-hooks": "^7.0.1",
282
- "express": "^5.2.1",
283
283
  "get-port": "^7.1.0",
284
284
  "globals": "^17.3.0",
285
285
  "husky": "^9.1.7",
@@ -302,7 +302,7 @@ const devDependencies = {
302
302
  "typescript": "^5.9.3",
303
303
  "typescript-eslint": "^8.55.0",
304
304
  "vite": "npm:rolldown-vite@7.3.1",
305
- "vite-tsconfig-paths": "^5.1.4",
305
+ "vite-tsconfig-paths": "^6.1.1",
306
306
  "vitest": "^4.0.18"
307
307
  };
308
308
  const tsdown = {
@@ -444,6 +444,32 @@ function useDataNullable() {
444
444
  repo: context.repo
445
445
  };
446
446
  }
447
+ function useComponentSize$1() {
448
+ const [size, setSize] = React.useState({
449
+ height: 0,
450
+ width: 0
451
+ });
452
+ const ref = React.useRef(void 0);
453
+ const onResize = React.useCallback(() => {
454
+ if (!ref.current) return;
455
+ const newHeight = ref.current.offsetHeight;
456
+ const newWidth = ref.current.offsetWidth;
457
+ if (newHeight !== size.height || newWidth !== size.width) setSize({
458
+ height: newHeight,
459
+ width: newWidth
460
+ });
461
+ }, [size.height, size.width]);
462
+ React.useLayoutEffect(() => {
463
+ if (!ref || !ref.current) return;
464
+ const resizeObserver = new ResizeObserver(onResize);
465
+ resizeObserver.observe(ref.current);
466
+ return () => resizeObserver.disconnect();
467
+ }, [ref, onResize]);
468
+ return {
469
+ ref,
470
+ ...size
471
+ };
472
+ }
447
473
  function dateFormatLong(epochTime) {
448
474
  if (!epochTime) return "Invalid date";
449
475
  return (/* @__PURE__ */ new Date(epochTime * 1e3)).toLocaleString("en-gb", {
@@ -502,13 +528,6 @@ function getSeparator(path$1) {
502
528
  if (path$1.includes("\\")) return "\\";
503
529
  return "/";
504
530
  }
505
- const getPathFromRepoAndHead = (params, segments = ["view"]) => {
506
- const searchParams = new URLSearchParams();
507
- Object.entries(params).forEach(([key, value]) => {
508
- if (value) searchParams.set(key, value);
509
- });
510
- return `/${segments.map(encodeURIComponent).join("/")}?${searchParams.toString()}`;
511
- };
512
531
  const branchCompare = (a, b) => {
513
532
  const defaultBranchNames = ["main", "master"];
514
533
  if (defaultBranchNames.includes(a)) return -1;
@@ -766,32 +785,6 @@ function findSubTree(tree, path$1) {
766
785
  }
767
786
  return currentTree;
768
787
  }
769
- function useComponentSize$1() {
770
- const [size, setSize] = React$1.useState({
771
- height: 0,
772
- width: 0
773
- });
774
- const ref = React$1.useRef(void 0);
775
- const onResize = React$1.useCallback(() => {
776
- if (!ref.current) return;
777
- const newHeight = ref.current.offsetHeight;
778
- const newWidth = ref.current.offsetWidth;
779
- if (newHeight !== size.height || newWidth !== size.width) setSize({
780
- height: newHeight,
781
- width: newWidth
782
- });
783
- }, [size.height, size.width]);
784
- React$1.useLayoutEffect(() => {
785
- if (!ref || !ref.current) return;
786
- const resizeObserver = new ResizeObserver(onResize);
787
- resizeObserver.observe(ref.current);
788
- return () => resizeObserver.disconnect();
789
- }, [ref, onResize]);
790
- return {
791
- ref,
792
- ...size
793
- };
794
- }
795
788
  function useComponentSize() {
796
789
  const { ref, width, height } = useComponentSize$1();
797
790
  return useMemo(() => [ref, {
@@ -860,8 +853,8 @@ function useFullscreen(getElement) {
860
853
  const toggleFullscreen = () => {
861
854
  if (!document.fullscreenElement) {
862
855
  const element = getElement();
863
- if (element instanceof Element) promiseHelper(document.documentElement.requestFullscreen());
864
- else promiseHelper(element.current.requestFullscreen());
856
+ if (element instanceof Element) promiseHelper(element.requestFullscreen());
857
+ else if (element.current) promiseHelper(element.current.requestFullscreen());
865
858
  } else if (document.exitFullscreen) document.exitFullscreen();
866
859
  };
867
860
  return {
@@ -869,6 +862,20 @@ function useFullscreen(getElement) {
869
862
  toggleFullscreen
870
863
  };
871
864
  }
865
+ function useViewAction() {
866
+ const location = useLocation();
867
+ return href("/view") + location.search;
868
+ }
869
+ function useViewSubmit() {
870
+ const submit = useSubmit();
871
+ const location = useLocation();
872
+ return (target, options) => {
873
+ submit(target, {
874
+ ...options,
875
+ action: href("/view") + location.search
876
+ });
877
+ };
878
+ }
872
879
  const cn = (...args) => twMerge(clsx(args));
873
880
  function usePrefersLightMode() {
874
881
  return useMediaQuery("(prefers-color-scheme: light)");
@@ -8759,7 +8766,7 @@ var defaultOptions = {
8759
8766
  commitSearch: "",
8760
8767
  transitionsEnabled: true,
8761
8768
  labelsVisible: true,
8762
- renderCutoff: 2,
8769
+ renderCutOff: 2,
8763
8770
  showFilesWithoutChanges: true,
8764
8771
  dominantAuthorCutoff: 0,
8765
8772
  linkMetricAndSizeMetric: false,
@@ -8779,8 +8786,7 @@ const LegendDot = ({ className = "", dotColor, authorColorToChange = void 0 }) =
8779
8786
  const [color, setColor] = useState(dotColor);
8780
8787
  const { databaseInfo } = useData();
8781
8788
  const { chartType } = useOptions();
8782
- const submit = useSubmit();
8783
- const [searchParams] = useSearchParams();
8789
+ const submit = useViewSubmit();
8784
8790
  if (!authorColorToChange) return /* @__PURE__ */ jsx(Dot, {
8785
8791
  className,
8786
8792
  chartType,
@@ -8790,13 +8796,7 @@ const LegendDot = ({ className = "", dotColor, authorColorToChange = void 0 }) =
8790
8796
  const form = new FormData();
8791
8797
  form.append("authorname", author);
8792
8798
  form.append("authorcolor", color$1);
8793
- submit(form, {
8794
- action: getPathFromRepoAndHead({
8795
- path: searchParams.get("path"),
8796
- branch: databaseInfo.branch
8797
- }),
8798
- method: "post"
8799
- });
8799
+ submit(form, { method: "post" });
8800
8800
  }
8801
8801
  return /* @__PURE__ */ jsxs(Popover$1, {
8802
8802
  triggerClassName: "flex gap-1 items-center",
@@ -8948,9 +8948,13 @@ var DB = class DB {
8948
8948
  );
8949
8949
  CREATE TABLE IF NOT EXISTS files (
8950
8950
  path VARCHAR,
8951
- hash VARCHAR,
8952
- type VARCHAR
8953
8951
  );
8952
+
8953
+ -- Migrations
8954
+ CREATE SEQUENCE IF NOT EXISTS hiddenfiles_id_sequence START 1;
8955
+ ALTER TABLE hiddenfiles ADD COLUMN IF NOT EXISTS id INTEGER DEFAULT nextval('hiddenfiles_id_sequence');
8956
+ ALTER TABLE files ADD COLUMN IF NOT EXISTS hash VARCHAR;
8957
+ ALTER TABLE files ADD COLUMN IF NOT EXISTS type VARCHAR;
8954
8958
  `);
8955
8959
  }
8956
8960
  async getDbConnection() {
@@ -9106,19 +9110,33 @@ var DB = class DB {
9106
9110
  }
9107
9111
  async getHiddenFiles() {
9108
9112
  return (await this.query(`
9109
- SELECT path FROM hiddenfiles ORDER BY path ASC;
9113
+ SELECT path, id FROM hiddenfiles ORDER BY id DESC;
9110
9114
  `)).map((row) => row["path"]);
9111
9115
  }
9112
- async replaceHiddenFiles(hiddenFiles) {
9116
+ async addHiddenFile(path$1) {
9117
+ const statement = await this.prepare(`
9118
+ INSERT INTO hiddenfiles (id, path)
9119
+ SELECT nextval('hiddenfiles_id_sequence'), ?
9120
+ WHERE NOT EXISTS (
9121
+ SELECT 1 FROM hiddenfiles WHERE path = ?
9122
+ );
9123
+ `);
9124
+ statement.bindVarchar(1, path$1);
9125
+ statement.bindVarchar(2, path$1);
9126
+ await statement.run();
9127
+ }
9128
+ async removeHiddenFile(path$1) {
9129
+ const statement = await this.prepare(`
9130
+ DELETE FROM hiddenfiles
9131
+ WHERE path = ?;
9132
+ `);
9133
+ statement.bindVarchar(1, path$1);
9134
+ await statement.run();
9135
+ }
9136
+ async clearHiddenFiles() {
9113
9137
  await this.run(`
9114
9138
  DELETE FROM hiddenfiles;
9115
9139
  `);
9116
- await this.usingTableAppender("hiddenfiles", async (appender) => {
9117
- for (const path$1 of hiddenFiles) {
9118
- appender.appendVarchar(path$1);
9119
- appender.endRow();
9120
- }
9121
- });
9122
9140
  }
9123
9141
  async getCommits(path$1, count) {
9124
9142
  return (await this.query(`
@@ -9272,10 +9290,7 @@ var DB = class DB {
9272
9290
  async getObjectType(objectPath) {
9273
9291
  const statement = await (await this.connectionPromise).prepare(`SELECT type FROM files WHERE path = ?`);
9274
9292
  statement.bindVarchar(1, objectPath);
9275
- const results = (await statement.runAndReadAll()).getRowObjectsJS();
9276
- if (results.length === 0) return null;
9277
- const { type: type$1 } = results[0];
9278
- return type$1;
9293
+ return (await statement.runAndReadAll()).getRowObjectsJS()[0]?.type ?? null;
9279
9294
  }
9280
9295
  async commitTableEmpty() {
9281
9296
  return (await this.query(`
@@ -10398,29 +10413,29 @@ function GitTruckInfo({ className = "", installedVersion, latestVersion: latestV
10398
10413
  }
10399
10414
  var clear_cache_exports = /* @__PURE__ */ __export({
10400
10415
  ClearCacheForm: () => ClearCacheForm,
10401
- action: () => action$2,
10416
+ action: () => action$1,
10402
10417
  default: () => clear_cache_default,
10403
10418
  loader: () => loader$11
10404
10419
  }, 1);
10405
10420
  const loader$11 = async ({ context }) => {
10406
10421
  return { versionInfo: context.get(versionContext) };
10407
10422
  };
10408
- const action$2 = async ({ request }) => {
10423
+ const action$1 = async ({ request }) => {
10409
10424
  const redirectPath = new URL(request.url).searchParams.get("redirect");
10425
+ if (!redirectPath) throw new Error("Missing redirect path");
10410
10426
  await InstanceManager.closeAllDBConnections();
10411
10427
  await DB.clearCache();
10412
- throw redirect(redirectPath ?? "/");
10428
+ throw redirect(redirectPath);
10413
10429
  };
10414
10430
  function ClearCacheForm({ redirectPath, className = "" } = {}) {
10415
- const location$1 = useLocation();
10416
- const fetcher = useFetcher({ key: "clear-cache-form" });
10417
- const formAction = `/clear-cache?${new URLSearchParams({ redirect: redirectPath ?? location$1.pathname + location$1.search }).toString()}`;
10431
+ const location = useLocation();
10432
+ const formAction = href("/clear-cache") + "?" + new URLSearchParams({ redirect: redirectPath ?? location.pathname + location.search }).toString();
10433
+ const fetcher = useFetcher();
10418
10434
  const isTransitioning = fetcher.state !== "idle";
10419
10435
  return /* @__PURE__ */ jsx(fetcher.Form, {
10420
10436
  action: formAction,
10421
10437
  method: "post",
10422
10438
  children: /* @__PURE__ */ jsxs("button", {
10423
- type: "submit",
10424
10439
  disabled: isTransitioning,
10425
10440
  className: cn("btn btn--danger", className),
10426
10441
  title: "Click here if you are experiencing issues",
@@ -10429,10 +10444,11 @@ function ClearCacheForm({ redirectPath, className = "" } = {}) {
10429
10444
  className: "hover-swap inline-block h-full"
10430
10445
  }), isTransitioning ? "Clearing..." : "Clear all data"]
10431
10446
  })
10432
- }, fetcher.state);
10447
+ });
10433
10448
  }
10434
10449
  var clear_cache_default = UNSAFE_withComponentProps(function ClearCache() {
10435
10450
  const { versionInfo } = useLoaderData();
10451
+ const [redirect$1] = useQueryState("redirect", parseAsString.withDefault("/"));
10436
10452
  return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs("div", {
10437
10453
  className: "app-container flex flex-col gap-2 p-2",
10438
10454
  children: [/* @__PURE__ */ jsx("div", {
@@ -10457,7 +10473,7 @@ var clear_cache_default = UNSAFE_withComponentProps(function ClearCache() {
10457
10473
  }), /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("span", {
10458
10474
  className: "font-bold",
10459
10475
  children: "Warning: "
10460
- }), /* @__PURE__ */ jsx("span", { children: "Merged authors and hidden files will be reset." })] })]
10476
+ }), /* @__PURE__ */ jsx("span", { children: "Grouped authors and Hidden files will be reset." })] })]
10461
10477
  }),
10462
10478
  /* @__PURE__ */ jsxs("div", {
10463
10479
  className: "flex justify-end gap-2",
@@ -10465,7 +10481,7 @@ var clear_cache_default = UNSAFE_withComponentProps(function ClearCache() {
10465
10481
  to: "/",
10466
10482
  className: "btn btn--text",
10467
10483
  children: "Go back"
10468
- }), /* @__PURE__ */ jsx(ClearCacheForm, { redirectPath: "/" })]
10484
+ }), /* @__PURE__ */ jsx(ClearCacheForm, { redirectPath: redirect$1 })]
10469
10485
  })
10470
10486
  ]
10471
10487
  })]
@@ -10592,7 +10608,7 @@ var refreshPolicy = {
10592
10608
  "commitCountPerDay",
10593
10609
  "loadRepoData"
10594
10610
  ],
10595
- unignore: [
10611
+ show: [
10596
10612
  "cache",
10597
10613
  "commitCount",
10598
10614
  "dominantAuthor",
@@ -10606,7 +10622,7 @@ var refreshPolicy = {
10606
10622
  "authors",
10607
10623
  "hiddenfiles"
10608
10624
  ],
10609
- ignore: [
10625
+ hide: [
10610
10626
  "cache",
10611
10627
  "commitCount",
10612
10628
  "maxMinCommitCount",
@@ -10744,7 +10760,7 @@ var COUNT_OPTIONS = [
10744
10760
  100
10745
10761
  ];
10746
10762
  const browseSearchParamsConfig = {
10747
- path: parseAsString.withOptions({ shallow: false }),
10763
+ path: parseAsString$1.withOptions({ shallow: false }),
10748
10764
  count: parseAsNumberLiteral(COUNT_OPTIONS).withOptions({ shallow: false }).withDefault(DEFAULT_COUNT),
10749
10765
  offset: parseAsInteger$1.withOptions({ shallow: false }).withDefault(DEFAULT_OFFSET),
10750
10766
  sort: parseAsStringLiteral$1([
@@ -10753,7 +10769,7 @@ const browseSearchParamsConfig = {
10753
10769
  "asc",
10754
10770
  "desc"
10755
10771
  ]).withOptions({ shallow: false }).withDefault("most-recent"),
10756
- search: parseAsString.withOptions({
10772
+ search: parseAsString$1.withOptions({
10757
10773
  shallow: false,
10758
10774
  limitUrlUpdates: {
10759
10775
  method: "throttle",
@@ -10886,7 +10902,7 @@ const loader$9 = async ({ context, request }) => {
10886
10902
  };
10887
10903
  var browse_default = UNSAFE_withComponentProps(function Index() {
10888
10904
  const { error: error$1, versionInfo, directories, parentDirectoryPath, analyzedReposPromise, totalCount } = useLoaderData();
10889
- const location$1 = useLocation();
10905
+ const location = useLocation();
10890
10906
  const navigation = useNavigation();
10891
10907
  const [{ path: path$1, "include-dirs": includeDirs, sort: sortMethod, search: searchQuery, count }, setSearchParams] = useQueryStates(browseSearchParamsConfig);
10892
10908
  const placeholderCount = Math.max(0, count - directories.length);
@@ -11012,7 +11028,7 @@ var browse_default = UNSAFE_withComponentProps(function Index() {
11012
11028
  /* @__PURE__ */ jsx("div", {}),
11013
11029
  /* @__PURE__ */ jsx("div", {}),
11014
11030
  /* @__PURE__ */ jsx(Link, {
11015
- to: `/clear-cache?redirect=${encodeURIComponent(location$1.pathname + location$1.search)}`,
11031
+ to: `/clear-cache?redirect=${encodeURIComponent(location.pathname + location.search)}`,
11016
11032
  className: "btn btn--danger max-w-min justify-self-end",
11017
11033
  children: "Clear cache"
11018
11034
  })
@@ -11378,9 +11394,9 @@ function Breadcrumb({ className = "", zoom = false }) {
11378
11394
  });
11379
11395
  }
11380
11396
  function useClickedObject() {
11381
- const location$1 = useLocation();
11397
+ const location = useLocation();
11382
11398
  const [params] = useQueryStates(viewSearchParamsConfig);
11383
- const clickedObject = params.objectPath ? location$1.state?.clickedObject ?? null : null;
11399
+ const clickedObject = params.objectPath ? location.state?.clickedObject ?? null : null;
11384
11400
  const navigate = useNavigate();
11385
11401
  const tabURL = useMatch(href("/view/commits")) ? "/view/commits" : "/view/details";
11386
11402
  return {
@@ -11415,7 +11431,7 @@ const Chart = memo(function Chart$2({ hoveredObject, setHoveredObject }) {
11415
11431
  const { searchResults, hasSearchResults } = useSearch();
11416
11432
  const size = useDeferredValue(rawSize);
11417
11433
  const { databaseInfo } = useData();
11418
- const { chartType, sizeMetric, hierarchyType, labelsVisible, renderCutoff } = useOptions();
11434
+ const { chartType, sizeMetric, hierarchyType, labelsVisible, renderCutOff } = useOptions();
11419
11435
  const [params, setParams] = useQueryStates(viewSearchParamsConfig);
11420
11436
  const { zoomPath } = params;
11421
11437
  const setZoomPath = (zoomPathUpdate) => setParams((prev) => {
@@ -11469,7 +11485,7 @@ const Chart = memo(function Chart$2({ hoveredObject, setHoveredObject }) {
11469
11485
  size,
11470
11486
  chartType,
11471
11487
  sizeMetricType: sizeMetric,
11472
- renderCutoff
11488
+ renderCutOff
11473
11489
  }).descendants();
11474
11490
  if (process.env.NODE_ENV === "development") console.timeEnd("Create and pack hiearchy");
11475
11491
  return res;
@@ -11477,7 +11493,7 @@ const Chart = memo(function Chart$2({ hoveredObject, setHoveredObject }) {
11477
11493
  size,
11478
11494
  chartType,
11479
11495
  sizeMetric,
11480
- renderCutoff,
11496
+ renderCutOff,
11481
11497
  databaseInfo,
11482
11498
  filetree
11483
11499
  ]);
@@ -11763,7 +11779,7 @@ function NodeText({ d, isSearchMatch, children = null }) {
11763
11779
  function isCircularNode(d) {
11764
11780
  return typeof d.r === "number";
11765
11781
  }
11766
- function createPartitionedHiearchy({ databaseInfo, tree, size, chartType, sizeMetricType, renderCutoff }) {
11782
+ function createPartitionedHiearchy({ databaseInfo, tree, size, chartType, sizeMetricType, renderCutOff }) {
11767
11783
  const hiearchy = hierarchy(tree).sum((d) => {
11768
11784
  const blob = d;
11769
11785
  switch (sizeMetricType) {
@@ -11774,7 +11790,7 @@ function createPartitionedHiearchy({ databaseInfo, tree, size, chartType, sizeMe
11774
11790
  case "MOST_CONTRIBUTIONS": return databaseInfo.contribSumPerFile[blob.path] ?? 1;
11775
11791
  }
11776
11792
  }).sort((a, b) => (b.value ?? 1) - (a.value ?? 1));
11777
- const cutOff = Number.isNaN(renderCutoff) ? 2 : renderCutoff;
11793
+ const cutOff = Number.isNaN(renderCutOff) ? 2 : renderCutOff;
11778
11794
  if (chartType === "TREE_MAP" || chartType === "PARTITION") {
11779
11795
  const tmPartition = (chartType === "TREE_MAP" ? treemap().size([size.width, size.height]).round(true).tile(treemapResquarify).paddingInner(2).paddingOuter(4).paddingTop(21) : partition().size([size.width, size.height]).padding(2))(hiearchy);
11780
11796
  filterVisualization(tmPartition, (child) => {
@@ -11807,94 +11823,43 @@ function flatten(tree) {
11807
11823
  else flattened.push(...flatten(child));
11808
11824
  return flattened;
11809
11825
  }
11810
- function hiddenFileFormat(ignored) {
11811
- if (!ignored.includes("/")) return ignored;
11812
- const split = ignored.split("/");
11813
- return split[split.length - 1];
11814
- }
11815
- const HiddenFiles = memo(function HiddenFiles$1() {
11816
- const navigationState = useNavigation();
11817
- const { databaseInfo } = useData();
11818
- const expandHiddenFilesButtonId = useId();
11819
- const action$3 = href("/view/details");
11820
- return /* @__PURE__ */ jsx(Popover$1, {
11821
- popoverTitle: `Hidden files (${databaseInfo.hiddenFiles.length})`,
11822
- className: "min-w-10",
11823
- trigger: ({ onClick }) => /* @__PURE__ */ jsx("button", {
11824
- className: "btn",
11825
- title: "Hidden files",
11826
- "aria-label": "Hidden files",
11827
- "aria-controls": expandHiddenFilesButtonId,
11828
- onClick,
11829
- children: /* @__PURE__ */ jsx(Icon, { path: mdiEyeOff })
11830
- }),
11831
- children: /* @__PURE__ */ jsx("div", {
11832
- className: "flex flex-col gap-2",
11833
- children: databaseInfo.hiddenFiles.length > 0 ? databaseInfo.hiddenFiles.map((hidden) => /* @__PURE__ */ jsxs("div", {
11834
- className: "grid grid-cols-[auto_1fr] items-center gap-2",
11835
- title: hidden,
11836
- children: [/* @__PURE__ */ jsxs(Form, {
11837
- className: "w-4",
11838
- method: "post",
11839
- action: action$3,
11840
- children: [/* @__PURE__ */ jsx("input", {
11841
- type: "hidden",
11842
- name: "unignore",
11843
- value: hidden
11844
- }), /* @__PURE__ */ jsxs("button", {
11845
- className: "btn btn--hover-swap h-4",
11846
- title: "Show file",
11847
- disabled: navigationState.state !== "idle",
11848
- children: [/* @__PURE__ */ jsx(Icon, {
11849
- path: mdiEyeOff,
11850
- className: "inline-block h-full"
11851
- }), /* @__PURE__ */ jsx(Icon, {
11852
- path: mdiEye,
11853
- className: "hover-swap inline-block h-full"
11854
- })]
11855
- })]
11856
- }), /* @__PURE__ */ jsx("span", {
11857
- className: "text-sm opacity-70",
11858
- children: hiddenFileFormat(hidden)
11859
- })]
11860
- }, hidden)) : /* @__PURE__ */ jsx("div", {
11861
- className: "text-sm opacity-70",
11862
- children: "No hidden files"
11863
- })
11864
- })
11865
- });
11866
- });
11867
11826
  function CheckboxWithLabel({ children, checked, onChange, className = "", checkedIcon = mdiCheckboxOutline, uncheckedIcon = mdiCheckboxBlankOutline, ...props }) {
11827
+ const [value, setValue] = useState(checked);
11868
11828
  const [isTransitioning, startTransition$1] = useTransition();
11869
11829
  return /* @__PURE__ */ jsxs("label", {
11870
11830
  className: `label group flex w-full items-center justify-start gap-2 hover:text-blue-500 ${className}`,
11871
11831
  ...props,
11872
11832
  children: [
11873
- /* @__PURE__ */ jsxs("span", {
11874
- className: "flex grow items-center gap-2",
11875
- children: [children, isTransitioning ? /* @__PURE__ */ jsx("img", {
11876
- src: truck_default$1,
11877
- alt: "...",
11878
- className: "h-5"
11879
- }) : ""]
11833
+ /* @__PURE__ */ jsx("div", {
11834
+ className: "flex flex-1 items-center gap-2",
11835
+ children
11836
+ }),
11837
+ /* @__PURE__ */ jsx("img", {
11838
+ src: truck_default$1,
11839
+ alt: "",
11840
+ "aria-hidden": "true",
11841
+ className: cn("h-5", { "opacity-0": !isTransitioning })
11880
11842
  }),
11881
11843
  /* @__PURE__ */ jsx(Icon, {
11882
11844
  className: "place-self-end group-hover:text-blue-500",
11883
- path: checked ? checkedIcon : uncheckedIcon,
11845
+ path: value ? checkedIcon : uncheckedIcon,
11884
11846
  size: 1
11885
11847
  }),
11886
11848
  /* @__PURE__ */ jsx("input", {
11887
11849
  type: "checkbox",
11888
- defaultChecked: checked,
11850
+ checked: value,
11889
11851
  className: "hidden",
11890
- onChange: (e) => startTransition$1(() => onChange(e))
11852
+ onChange: (e) => {
11853
+ setValue(e.target.checked);
11854
+ startTransition$1(() => onChange(e));
11855
+ }
11891
11856
  })
11892
11857
  ]
11893
11858
  });
11894
11859
  }
11895
- function UnionAuthorsModal() {
11860
+ function GroupAuthorsModal() {
11896
11861
  const { databaseInfo } = useData();
11897
- const submit = useSubmit();
11862
+ const submit = useViewSubmit();
11898
11863
  const { authors } = databaseInfo;
11899
11864
  const authorUnions = databaseInfo.authorUnions;
11900
11865
  const [selectedAuthors, setSelectedAuthors] = useState([]);
@@ -11902,7 +11867,7 @@ function UnionAuthorsModal() {
11902
11867
  const navigationData = useNavigation();
11903
11868
  const [, authorColors] = useMetrics();
11904
11869
  const [, startTransition$1] = useTransition();
11905
- const location$1 = useLocation();
11870
+ const viewAction = useViewAction();
11906
11871
  const flattedUnionedAuthors = authorUnions.reduce((acc, union) => {
11907
11872
  return [...acc, ...union];
11908
11873
  }, []).sort(stringSorter);
@@ -11910,27 +11875,18 @@ function UnionAuthorsModal() {
11910
11875
  const newAuthorUnions = authorUnions.filter((_, i) => i !== groupToUnGroup);
11911
11876
  const form = new FormData();
11912
11877
  form.append("unionedAuthors", JSON.stringify(newAuthorUnions));
11913
- submit(form, {
11914
- action: href("/view") + location$1.search,
11915
- method: "post"
11916
- });
11878
+ submit(form, { method: "post" });
11917
11879
  }
11918
11880
  function ungroupAll() {
11919
11881
  const form = new FormData();
11920
11882
  form.append("unionedAuthors", JSON.stringify([]));
11921
- submit(form, {
11922
- action: href("/view") + location$1.search,
11923
- method: "post"
11924
- });
11883
+ submit(form, { method: "post" });
11925
11884
  }
11926
11885
  function groupSelectedAuthors() {
11927
11886
  if (selectedAuthors.length < 2) return;
11928
11887
  const form = new FormData();
11929
11888
  form.append("unionedAuthors", JSON.stringify([...authorUnions, selectedAuthors]));
11930
- submit(form, {
11931
- action: href("/view") + location$1.search,
11932
- method: "post"
11933
- });
11889
+ submit(form, { method: "post" });
11934
11890
  setSelectedAuthors([]);
11935
11891
  }
11936
11892
  function makePrimaryAlias(alias, groupIndex) {
@@ -11940,31 +11896,31 @@ function UnionAuthorsModal() {
11940
11896
  });
11941
11897
  const form = new FormData();
11942
11898
  form.append("unionedAuthors", JSON.stringify(newAuthorUnions));
11943
- submit(form, {
11944
- action: href("/view") + location$1.search,
11945
- method: "post"
11946
- });
11899
+ submit(form, { method: "post" });
11947
11900
  }
11948
11901
  const disabled = navigationData.state !== "idle";
11949
11902
  const ungroupedAuthorsSorted = authors.filter((a) => !flattedUnionedAuthors.includes(a)).slice(0).sort(stringSorter);
11950
11903
  const getColorFromDisplayName = (displayName) => authorColors.get(displayName) ?? "#333";
11951
11904
  const ungroupedAuthorsFiltered = ungroupedAuthorsSorted.filter((author) => author.toLowerCase().includes(filter.toLowerCase()));
11952
- const ungroupedAuthorsEntries = ungroupedAuthorsFiltered.map((author) => /* @__PURE__ */ jsx(CheckboxWithLabel, {
11953
- className: "hover:opacity-70",
11954
- checked: selectedAuthors.includes(author),
11955
- onChange: (e) => {
11956
- setSelectedAuthors(e.target?.checked ? [...selectedAuthors, author] : selectedAuthors.filter((a) => a !== author));
11957
- },
11958
- children: /* @__PURE__ */ jsxs("div", {
11959
- className: "inline-flex flex-row place-items-center gap-2",
11960
- children: [/* @__PURE__ */ jsx(LegendDot, { dotColor: getColorFromDisplayName(author) }), author]
11961
- })
11962
- }, author));
11905
+ const ungroupedAuthorsEntries = ungroupedAuthorsFiltered.map((author) => {
11906
+ const isAuthorSelected = selectedAuthors.includes(author);
11907
+ return /* @__PURE__ */ jsx(CheckboxWithLabel, {
11908
+ className: "hover:opacity-70",
11909
+ checked: isAuthorSelected,
11910
+ onChange: (e) => {
11911
+ setSelectedAuthors(e.target?.checked ? [...selectedAuthors, author] : selectedAuthors.filter((a) => a !== author));
11912
+ },
11913
+ children: /* @__PURE__ */ jsxs("div", {
11914
+ className: "inline-flex flex-row place-items-center gap-2",
11915
+ children: [/* @__PURE__ */ jsx(LegendDot, { dotColor: getColorFromDisplayName(author) }), author]
11916
+ })
11917
+ }, author + isAuthorSelected);
11918
+ });
11963
11919
  const groupedAuthorsEntries = authorUnions.map((aliasGroup, aliasGroupIndex) => {
11964
11920
  const displayName = aliasGroup[0];
11965
11921
  const disabled$1 = navigationData.state !== "idle";
11966
11922
  return /* @__PURE__ */ jsxs("div", {
11967
- className: "card group m-0 flex h-full flex-col p-2",
11923
+ className: "card bg-tertiary-bg dark:bg-tertiary-bg-dark group m-0 flex h-full flex-col p-2",
11968
11924
  children: [
11969
11925
  /* @__PURE__ */ jsxs("div", {
11970
11926
  className: "inline-flex flex-row place-items-center gap-2",
@@ -11983,8 +11939,8 @@ function UnionAuthorsModal() {
11983
11939
  /* @__PURE__ */ jsxs("div", {
11984
11940
  className: "flex items-end justify-end gap-2 opacity-0 transition-opacity duration-200 group-hover:opacity-100",
11985
11941
  children: [/* @__PURE__ */ jsxs(Form, {
11986
- action: href("/view") + location$1.search,
11987
11942
  method: "post",
11943
+ action: viewAction,
11988
11944
  children: [/* @__PURE__ */ jsx("input", {
11989
11945
  type: "hidden",
11990
11946
  name: "unionedAuthors",
@@ -12004,10 +11960,7 @@ function UnionAuthorsModal() {
12004
11960
  });
12005
11961
  const form = new FormData();
12006
11962
  form.append("unionedAuthors", JSON.stringify(newAuthorUnions));
12007
- submit(form, {
12008
- action: href("/view") + location$1.search,
12009
- method: "post"
12010
- });
11963
+ submit(form, { method: "post" });
12011
11964
  setSelectedAuthors([]);
12012
11965
  },
12013
11966
  children: "Add selected"
@@ -12024,61 +11977,32 @@ function UnionAuthorsModal() {
12024
11977
  }, aliasGroupIndex);
12025
11978
  });
12026
11979
  return /* @__PURE__ */ jsxs("div", {
12027
- 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",
11980
+ className: "flex min-h-0 w-auto max-w-(--breakpoint-lg) min-w-0 flex-col gap-2 p-4",
12028
11981
  children: [
12029
11982
  /* @__PURE__ */ jsxs("div", {
12030
- className: "flex justify-between",
11983
+ className: "grid grid-cols-[1fr_1fr] gap-2",
12031
11984
  children: [/* @__PURE__ */ jsxs("h3", {
12032
- className: "grow text-center text-lg font-bold",
11985
+ className: "text-center text-lg font-bold",
12033
11986
  children: [
12034
11987
  "Ungrouped Authors (",
12035
11988
  ungroupedAuthorsSorted.length,
12036
11989
  ")"
12037
11990
  ]
12038
- }), /* @__PURE__ */ jsx("div", {
12039
- className: "flex justify-end gap-2",
12040
- children: /* @__PURE__ */ jsxs("button", {
12041
- className: "btn btn--primary justify-self-end",
12042
- title: "Group the selected authors",
12043
- disabled: disabled || selectedAuthors.length < 2,
12044
- onClick: groupSelectedAuthors,
12045
- children: [/* @__PURE__ */ jsx(Icon, {
12046
- path: mdiAccountMultiplePlus,
12047
- size: 1
12048
- }), "Create group"]
12049
- })
12050
- })]
12051
- }),
12052
- /* @__PURE__ */ jsxs("div", {
12053
- className: "flex justify-between",
12054
- children: [/* @__PURE__ */ jsxs("h3", {
12055
- className: "grow text-center text-lg font-bold",
11991
+ }), /* @__PURE__ */ jsxs("h3", {
11992
+ className: "text-center text-lg font-bold",
12056
11993
  children: [
12057
11994
  "Author Groups (",
12058
11995
  groupedAuthorsEntries.length,
12059
11996
  ")"
12060
11997
  ]
12061
- }), /* @__PURE__ */ jsx("div", {
12062
- className: "flex justify-end gap-4",
12063
- children: /* @__PURE__ */ jsxs("button", {
12064
- className: "btn btn--danger",
12065
- disabled: disabled || authorUnions.length === 0,
12066
- onClick: () => {
12067
- if (confirm("Are you sure you want to ungroup all grouped authors?")) ungroupAll();
12068
- },
12069
- children: [/* @__PURE__ */ jsx(Icon, {
12070
- path: mdiAccountMultipleMinus,
12071
- size: 1
12072
- }), "Ungroup all"]
12073
- })
12074
11998
  })]
12075
11999
  }),
12076
- /* @__PURE__ */ jsx("div", {
12077
- className: "max-h-full overflow-y-scroll",
12078
- children: /* @__PURE__ */ jsxs("div", {
12079
- className: "h-fill flex min-h-0 flex-col rounded-md dark:bg-gray-700",
12000
+ /* @__PURE__ */ jsxs("div", {
12001
+ className: "grid min-h-0 flex-1 grid-cols-[1fr_1fr] gap-2",
12002
+ children: [/* @__PURE__ */ jsxs("div", {
12003
+ className: "flex min-h-0 flex-col rounded-md",
12080
12004
  children: [/* @__PURE__ */ jsxs("div", {
12081
- className: "sticky top-0 z-10 flex gap-2 bg-gray-100 p-2 dark:bg-gray-700",
12005
+ className: "flex gap-2 p-2",
12082
12006
  children: [
12083
12007
  /* @__PURE__ */ jsx("input", {
12084
12008
  className: "input min-w-0",
@@ -12103,23 +12027,45 @@ function UnionAuthorsModal() {
12103
12027
  })
12104
12028
  ]
12105
12029
  }), /* @__PURE__ */ jsx("div", {
12106
- className: "min-h-fill max-h-full overflow-y-auto p-2",
12030
+ className: "min-h-0 flex-1 overflow-y-auto p-2",
12107
12031
  children: ungroupedAuthorsEntries.length > 0 ? ungroupedAuthorsEntries : /* @__PURE__ */ jsx("p", {
12108
- className: "place-self-center",
12032
+ className: "place-self-center text-sm",
12109
12033
  children: filter.length > 0 ? "No authors found" : "All authors have been grouped"
12110
12034
  })
12111
12035
  })]
12112
- })
12113
- }),
12114
- /* @__PURE__ */ jsx("div", {
12115
- className: "min-h-0 overflow-y-auto",
12116
- children: /* @__PURE__ */ jsx("div", {
12117
- 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",
12118
- children: authorUnions.length > 0 ? groupedAuthorsEntries : /* @__PURE__ */ jsx("p", {
12119
- className: "place-self-center",
12120
- children: "No authors have been grouped yet"
12036
+ }), /* @__PURE__ */ jsx("div", {
12037
+ className: "min-h-0 overflow-y-auto",
12038
+ children: /* @__PURE__ */ jsx("div", {
12039
+ className: "grid h-min min-h-0 grid-cols-1 gap-4 rounded-md p-2 lg:grid-cols-2 xl:grid-cols-3",
12040
+ children: authorUnions.length > 0 ? groupedAuthorsEntries : /* @__PURE__ */ jsx("p", {
12041
+ className: "col-span-2 text-center text-sm",
12042
+ children: "No authors have been grouped yet"
12043
+ })
12121
12044
  })
12122
- })
12045
+ })]
12046
+ }),
12047
+ /* @__PURE__ */ jsxs("div", {
12048
+ className: "grid grid-cols-[1fr_1fr] gap-2",
12049
+ children: [/* @__PURE__ */ jsxs("button", {
12050
+ className: "btn btn--primary mx-auto w-fit",
12051
+ title: disabled || selectedAuthors.length < 2 ? "Select at least 2 authors to group them" : "Group the selected authors",
12052
+ disabled: disabled || selectedAuthors.length < 2,
12053
+ onClick: groupSelectedAuthors,
12054
+ children: [/* @__PURE__ */ jsx(Icon, {
12055
+ path: mdiAccountMultiplePlus,
12056
+ size: 1
12057
+ }), "Create group"]
12058
+ }), /* @__PURE__ */ jsxs("button", {
12059
+ className: "btn btn--danger mx-auto w-fit",
12060
+ disabled: disabled || authorUnions.length === 0,
12061
+ onClick: () => {
12062
+ if (confirm("Are you sure you want to ungroup all grouped authors?")) ungroupAll();
12063
+ },
12064
+ children: [/* @__PURE__ */ jsx(Icon, {
12065
+ path: mdiAccountMultipleMinus,
12066
+ size: 1
12067
+ }), "Ungroup all"]
12068
+ })]
12123
12069
  })
12124
12070
  ]
12125
12071
  });
@@ -12141,17 +12087,138 @@ function UnionAuthorsModal() {
12141
12087
  }
12142
12088
  }
12143
12089
  var stringSorter = (a, b) => a.toLowerCase().localeCompare(b.toLowerCase());
12144
- function IconRadioGroup({ group, titleMap, defaultValue, className, onChange, large, iconMap }) {
12090
+ function HideFilesModal() {
12091
+ const navigationState = useNavigation();
12092
+ const { databaseInfo } = useData();
12093
+ const inputRef = useRef(null);
12094
+ const viewAction = useViewAction();
12095
+ const optimisticHide = navigationState.state !== "idle" && navigationState.formData ? navigationState.formData.get("hide") : null;
12096
+ const optimisticUnhide = navigationState.state !== "idle" && navigationState.formData ? navigationState.formData.get("show") : null;
12097
+ const displayedHiddenFiles = [...databaseInfo.hiddenFiles];
12098
+ if (optimisticHide && !databaseInfo.hiddenFiles.includes(optimisticHide)) displayedHiddenFiles.unshift(optimisticHide);
12099
+ const filteredHiddenFiles = displayedHiddenFiles.filter((path$1) => path$1 !== optimisticUnhide);
12100
+ return /* @__PURE__ */ jsxs("div", {
12101
+ className: "flex min-h-0 max-w-[40ch] flex-col items-start justify-center gap-3 overflow-y-auto p-2 pl-0",
12102
+ children: [
12103
+ /* @__PURE__ */ jsx("p", {
12104
+ className: "text-sm",
12105
+ children: "Pattern-matched files are hidden from the file tree and excluded from metric computations."
12106
+ }),
12107
+ /* @__PURE__ */ jsxs("p", {
12108
+ className: "text-sm",
12109
+ children: [
12110
+ "Hidden files behave like entries in a ",
12111
+ /* @__PURE__ */ jsx(Code, {
12112
+ inline: true,
12113
+ children: ".gitignore"
12114
+ }),
12115
+ " file."
12116
+ ]
12117
+ }),
12118
+ /* @__PURE__ */ jsxs(Form, {
12119
+ action: viewAction,
12120
+ method: "post",
12121
+ className: "flex w-full flex-wrap items-end gap-2",
12122
+ onSubmit: () => {
12123
+ setTimeout(() => {
12124
+ if (inputRef.current) inputRef.current.value = "";
12125
+ }, 0);
12126
+ },
12127
+ children: [/* @__PURE__ */ jsxs("label", {
12128
+ className: "label flex min-w-0 flex-1 flex-col gap-2",
12129
+ children: ["Path or glob", /* @__PURE__ */ jsx("input", {
12130
+ ref: inputRef,
12131
+ type: "text",
12132
+ className: "input",
12133
+ name: "hide",
12134
+ placeholder: "Enter pattern...",
12135
+ onChange: (e) => {
12136
+ const exists = databaseInfo.hiddenFiles.includes(e.target.value);
12137
+ e.target.setCustomValidity(exists ? "Pattern already exists" : "");
12138
+ }
12139
+ })]
12140
+ }), /* @__PURE__ */ jsx("button", {
12141
+ className: "btn btn--primary whitespace-nowrap",
12142
+ children: "Hide"
12143
+ })]
12144
+ }),
12145
+ /* @__PURE__ */ jsxs("div", {
12146
+ className: "flex w-full items-center justify-between",
12147
+ children: [/* @__PURE__ */ jsx("h3", {
12148
+ className: "text-tertiary-text dark:text-tertiary-text-dark text-sm font-bold tracking-wide uppercase",
12149
+ children: "Hidden files/patterns"
12150
+ }), /* @__PURE__ */ jsxs(Form, {
12151
+ action: viewAction,
12152
+ method: "post",
12153
+ className: cn({ hidden: databaseInfo.hiddenFiles.length === 0 }),
12154
+ children: [/* @__PURE__ */ jsx("input", {
12155
+ type: "hidden",
12156
+ name: "unhideAll",
12157
+ value: "true"
12158
+ }), /* @__PURE__ */ jsx("button", {
12159
+ className: "btn btn--danger btn--text",
12160
+ children: "Reset"
12161
+ })]
12162
+ })]
12163
+ }),
12164
+ /* @__PURE__ */ jsx("div", {
12165
+ className: "flex max-h-96 min-h-0 w-full flex-col gap-2 overflow-y-auto",
12166
+ children: filteredHiddenFiles.length > 0 ? filteredHiddenFiles.map((hidden) => {
12167
+ const isOptimistic = hidden === optimisticHide;
12168
+ return /* @__PURE__ */ jsxs("div", {
12169
+ className: "primary grid grid-cols-[auto_1fr] items-center gap-2 rounded-md px-2 py-1 text-sm",
12170
+ style: { opacity: isOptimistic ? .6 : hidden === optimisticUnhide ? .4 : 1 },
12171
+ title: hidden,
12172
+ children: [/* @__PURE__ */ jsxs(Form, {
12173
+ action: viewAction,
12174
+ className: "w-4",
12175
+ method: "post",
12176
+ children: [/* @__PURE__ */ jsx("input", {
12177
+ type: "hidden",
12178
+ name: "show",
12179
+ value: hidden
12180
+ }), /* @__PURE__ */ jsxs("button", {
12181
+ className: "btn btn--text btn--hover-swap h-4",
12182
+ title: "Show file",
12183
+ disabled: navigationState.state !== "idle" || isOptimistic,
12184
+ children: [/* @__PURE__ */ jsx(Icon, {
12185
+ path: mdiEyeOff,
12186
+ className: "inline-block h-full"
12187
+ }), /* @__PURE__ */ jsx(Icon, {
12188
+ path: mdiEye,
12189
+ className: "hover-swap inline-block h-full"
12190
+ })]
12191
+ })]
12192
+ }), /* @__PURE__ */ jsx("span", {
12193
+ className: "truncate text-sm",
12194
+ title: hidden,
12195
+ children: hiddenFileFormat(hidden)
12196
+ })]
12197
+ }, hidden);
12198
+ }) : /* @__PURE__ */ jsx("div", {
12199
+ className: "text-secondary-text dark:text-secondary-text-dark text-sm opacity-70",
12200
+ children: "No hidden files"
12201
+ })
12202
+ })
12203
+ ]
12204
+ });
12205
+ }
12206
+ function hiddenFileFormat(ignored) {
12207
+ if (!ignored.includes(getSep(ignored))) return ignored;
12208
+ const split = ignored.split(getSep(ignored));
12209
+ return split[split.length - 1];
12210
+ }
12211
+ function IconRadioGroup({ group, titleMap, defaultValue, className, onChange, large, iconMap, ariaLabel }) {
12145
12212
  const enumEntries = Object.entries(group);
12146
12213
  return /* @__PURE__ */ jsx(RadioGroup, {
12147
12214
  value: defaultValue,
12148
12215
  className: cn("flex flex-wrap gap-0", { "gap-2": large }, className),
12149
- "aria-label": "Select branch or revision",
12216
+ "aria-label": ariaLabel,
12150
12217
  onChange,
12151
12218
  children: enumEntries.map(([key, value]) => /* @__PURE__ */ jsx(Field, { children: /* @__PURE__ */ jsx(Radio, {
12152
12219
  value: key,
12153
12220
  className: "group",
12154
- title: titleMap ? titleMap[key] : value,
12221
+ title: titleMap?.[key] ?? value,
12155
12222
  children: /* @__PURE__ */ jsxs("div", {
12156
12223
  className: clsx$1("group btn btn--text cursor-pointer gap-2 text-xs transition-all duration-200 hover:opacity-100! focus-visible:opacity-100!", {
12157
12224
  "group-data-checked:bg-blue-primary group-data-checked:fill-primary-text-dark group-data-checked:text-primary-text-dark group-data-checked:border-blue-primary hover:fill-blue-primary hover:text-blue-primary hover:border-blue-primary h-auto flex-col place-items-center items-center rounded border border-transparent p-1": large,
@@ -12191,6 +12258,7 @@ const Options = memo(function Options$1() {
12191
12258
  TREE_MAP: mdiChartTree,
12192
12259
  PARTITION: mdiKnife
12193
12260
  },
12261
+ ariaLabel: "Select layout",
12194
12262
  onChange: (chartType$1) => setChartType(chartType$1)
12195
12263
  })] }),
12196
12264
  /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("h3", {
@@ -12208,6 +12276,7 @@ const Options = memo(function Options$1() {
12208
12276
  LAST_CHANGED: mdiPulse,
12209
12277
  MOST_CONTRIBUTIONS: mdiPlusMinusVariant
12210
12278
  },
12279
+ ariaLabel: "Select size metric",
12211
12280
  onChange: (sizeMetric$1) => {
12212
12281
  setSizeMetricType(sizeMetric$1);
12213
12282
  }
@@ -12227,6 +12296,7 @@ const Options = memo(function Options$1() {
12227
12296
  TOP_CONTRIBUTOR: mdiPodiumGold,
12228
12297
  MOST_CONTRIBUTIONS: mdiPlusMinusVariant
12229
12298
  },
12299
+ ariaLabel: "Select color metric",
12230
12300
  onChange: (metric) => {
12231
12301
  setMetricType(metric);
12232
12302
  if (!linkMetricAndSizeMetric) return;
@@ -12237,10 +12307,9 @@ const Options = memo(function Options$1() {
12237
12307
  ] });
12238
12308
  });
12239
12309
  function SettingsModal() {
12240
- const { metricType, hierarchyType, transitionsEnabled, renderCutoff, showFilesWithoutChanges, linkMetricAndSizeMetric, showOnlySearchMatches, setLinkMetricAndSizeMetric, setTransitionsEnabled, labelsVisible, setLabelsVisible, setHierarchyType, setSizeMetricType, setRenderCutoff, setShowFilesWithoutChanges, setShowOnlySearchMatches } = useOptions();
12241
- const [isTransitioning, startTransition$1] = useTransition();
12310
+ const { metricType, hierarchyType, transitionsEnabled, showFilesWithoutChanges, linkMetricAndSizeMetric, showOnlySearchMatches, setLinkMetricAndSizeMetric, setTransitionsEnabled, labelsVisible, setLabelsVisible, setHierarchyType, setSizeMetricType, setShowFilesWithoutChanges, setShowOnlySearchMatches } = useOptions();
12242
12311
  return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs("div", {
12243
- className: "flex min-w-120 flex-col items-start justify-center gap-4 p-2 pl-0",
12312
+ className: "flex min-h-0 max-w-max flex-col items-start justify-center gap-3 overflow-y-auto p-2 pl-0",
12244
12313
  children: [
12245
12314
  /* @__PURE__ */ jsxs(CheckboxWithLabel, {
12246
12315
  className: "group text-sm hover:text-blue-500 hover:opacity-100",
@@ -12314,48 +12383,64 @@ function SettingsModal() {
12314
12383
  size: "1.25em"
12315
12384
  }), "Show only search matches"]
12316
12385
  }),
12317
- /* @__PURE__ */ jsxs("label", {
12318
- className: "label group flex w-full items-center justify-start gap-2 text-sm hover:text-blue-500 hover:opacity-100",
12319
- title: "Increase this to improve render performance, decrease it to get higher level of detail",
12320
- children: [/* @__PURE__ */ jsxs("span", {
12321
- className: "group flex grow items-center gap-2",
12322
- children: [
12323
- /* @__PURE__ */ jsx(Icon, {
12324
- className: "ml-1.5",
12325
- path: mdiContentCut,
12326
- size: "1.25em"
12327
- }),
12328
- "Pixel render cut-off ",
12329
- isTransitioning ? /* @__PURE__ */ jsx("img", {
12330
- src: truck_default$1,
12331
- alt: "...",
12332
- className: "h-5"
12333
- }) : ""
12334
- ]
12335
- }), /* @__PURE__ */ jsx("input", {
12336
- type: "number",
12337
- min: 0,
12338
- defaultValue: renderCutoff,
12339
- className: "mr-1 w-12 place-self-end border-b-2",
12340
- onChange: (x) => startTransition$1(() => setRenderCutoff(x.target.valueAsNumber))
12341
- })]
12342
- })
12386
+ /* @__PURE__ */ jsx(RenderCutOff, {})
12343
12387
  ]
12344
12388
  }) });
12345
12389
  }
12390
+ function RenderCutOff() {
12391
+ const { renderCutOff, setRenderCutOff } = useOptions();
12392
+ const [value, setValue] = useState(renderCutOff);
12393
+ const [isTransitioning, startTransition$1] = useTransition();
12394
+ return /* @__PURE__ */ jsxs("label", {
12395
+ className: "label group flex w-full items-center justify-start gap-2 text-sm hover:text-blue-500 hover:opacity-100",
12396
+ title: "Increase to improve render performance, decrease it to get higher level of detail",
12397
+ children: [/* @__PURE__ */ jsxs("span", {
12398
+ className: "group flex grow items-center gap-2",
12399
+ children: [
12400
+ /* @__PURE__ */ jsx(Icon, {
12401
+ className: "ml-1.5",
12402
+ path: mdiContentCut,
12403
+ size: "1.25em"
12404
+ }),
12405
+ "Pixel render cut-off ",
12406
+ isTransitioning ? /* @__PURE__ */ jsx("img", {
12407
+ src: truck_default$1,
12408
+ alt: "",
12409
+ "aria-hidden": "true",
12410
+ className: "h-5"
12411
+ }) : ""
12412
+ ]
12413
+ }), /* @__PURE__ */ jsx("input", {
12414
+ type: "number",
12415
+ min: 0,
12416
+ value,
12417
+ className: "mr-1 w-12 place-self-end border-b-2",
12418
+ onChange: (x) => {
12419
+ const v = Number.isNaN(x.target.valueAsNumber) ? getDefaultOptionsContextValue().renderCutOff : x.target.valueAsNumber;
12420
+ setValue(v);
12421
+ startTransition$1(() => setRenderCutOff(v));
12422
+ }
12423
+ })]
12424
+ });
12425
+ }
12346
12426
  var modals = {
12347
- "group-authors": {
12348
- content: /* @__PURE__ */ jsx(UnionAuthorsModal, {}),
12349
- title: "Group Authors",
12350
- icon: mdiAccountMultipleCheck
12351
- },
12352
12427
  "app-settings": {
12353
12428
  content: /* @__PURE__ */ jsx(SettingsModal, {}),
12354
12429
  title: "Settings",
12355
12430
  icon: mdiCog
12431
+ },
12432
+ "group-authors": {
12433
+ content: /* @__PURE__ */ jsx(GroupAuthorsModal, {}),
12434
+ title: "Group Authors",
12435
+ icon: mdiAccountMultiple
12436
+ },
12437
+ "ignore-files": {
12438
+ content: /* @__PURE__ */ jsx(HideFilesModal, {}),
12439
+ title: "Hide files",
12440
+ icon: mdiEyeOff
12356
12441
  }
12357
12442
  };
12358
- var modalSearchParamConfig = parseAsStringLiteral(Object.keys(modals));
12443
+ var modalSearchParamConfig = parseAsStringLiteral(Object.keys(modals)).withOptions({ shallow: false });
12359
12444
  function useModal(modalKey = null) {
12360
12445
  const [modal, setModal] = useQueryState("modal", modalSearchParamConfig);
12361
12446
  const openModal = (modal$1 = modalKey) => void setModal(modal$1);
@@ -12369,16 +12454,22 @@ function useModal(modalKey = null) {
12369
12454
  function ModalManager() {
12370
12455
  const [modalKey, setModal] = useQueryState("modal", modalSearchParamConfig);
12371
12456
  const dialogRef = useRef(null);
12372
- const onClose = () => setModal(null);
12457
+ const onClose = () => {
12458
+ return setModal(null);
12459
+ };
12373
12460
  useEffect(() => {
12374
- if (!dialogRef.current) return;
12375
12461
  const dialog = dialogRef.current;
12462
+ if (!dialog) return;
12376
12463
  if (modalKey) {
12377
- dialogRef.current.showModal();
12378
- return;
12464
+ document.body.style.setProperty("overflow", "hidden");
12465
+ if (!dialog.open) dialog.showModal();
12466
+ } else {
12467
+ document.body.style.setProperty("overflow", null);
12468
+ if (dialog.open) dialog.close();
12379
12469
  }
12380
- dialog.close();
12381
- return () => dialog.close();
12470
+ return () => {
12471
+ document.body.style.setProperty("overflow", null);
12472
+ };
12382
12473
  }, [modalKey]);
12383
12474
  if (!modalKey) return null;
12384
12475
  const modal = modals[modalKey];
@@ -12387,10 +12478,10 @@ function ModalManager() {
12387
12478
  "aria-modal": true,
12388
12479
  open: false,
12389
12480
  closedby: "any",
12390
- className: "z-10 m-auto flex flex-col items-start justify-stretch bg-transparent text-inherit backdrop:bg-gray-500/75 backdrop:p-0",
12481
+ className: "z-10 m-auto flex flex-col bg-transparent text-inherit backdrop:bg-gray-400/75 backdrop:backdrop-blur-xs dark:backdrop:bg-gray-800/75",
12391
12482
  onClose,
12392
12483
  children: /* @__PURE__ */ jsxs("div", {
12393
- 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",
12484
+ className: "card flex min-h-0 max-w-(--breakpoint-2xl) gap-2 rounded-xl p-4 shadow-sm",
12394
12485
  children: [
12395
12486
  /* @__PURE__ */ jsxs("div", {
12396
12487
  className: "flex justify-between gap-2",
@@ -12415,29 +12506,29 @@ function ModalManager() {
12415
12506
  })
12416
12507
  });
12417
12508
  }
12418
- function AuthorOptions() {
12509
+ function HideFilesButton() {
12510
+ const { openModal } = useModal("ignore-files");
12511
+ return /* @__PURE__ */ jsx("button", {
12512
+ className: "btn btn--icon",
12513
+ title: "Hidden files",
12514
+ "aria-label": "Hidden files",
12515
+ onClick: () => openModal(),
12516
+ children: /* @__PURE__ */ jsx(Icon, { path: mdiEyeOff })
12517
+ });
12518
+ }
12519
+ function ShuffleColorsButton() {
12419
12520
  const transitionState = useNavigation();
12420
- const action$3 = href("/view");
12421
- const { openModal } = useModal("group-authors");
12422
- return /* @__PURE__ */ jsxs("div", {
12423
- className: "mt-2 grid w-full grid-cols-[1fr_1fr] gap-2",
12424
- children: [/* @__PURE__ */ jsxs("button", {
12425
- className: "btn",
12426
- onClick: () => openModal(),
12427
- children: [/* @__PURE__ */ jsx(Icon, { path: mdiAccountMultiple }), "Group authors"]
12428
- }), /* @__PURE__ */ jsxs(Form, {
12429
- method: "post",
12430
- action: action$3,
12431
- children: [/* @__PURE__ */ jsx("input", {
12432
- type: "hidden",
12433
- name: "rerollColors",
12434
- value: ""
12435
- }), /* @__PURE__ */ jsxs("button", {
12436
- className: "btn w-full",
12437
- type: "submit",
12438
- disabled: transitionState.state !== "idle",
12439
- children: [/* @__PURE__ */ jsx(Icon, { path: mdiDiceMultipleOutline }), "Shuffle colors"]
12440
- })]
12521
+ return /* @__PURE__ */ jsxs(Form, {
12522
+ method: "post",
12523
+ action: useViewAction(),
12524
+ children: [/* @__PURE__ */ jsx("input", {
12525
+ type: "hidden",
12526
+ name: "rerollColors",
12527
+ value: ""
12528
+ }), /* @__PURE__ */ jsxs("button", {
12529
+ className: "btn w-full",
12530
+ disabled: transitionState.state !== "idle",
12531
+ children: [/* @__PURE__ */ jsx(Icon, { path: mdiDiceMultipleOutline }), "Shuffle colors"]
12441
12532
  })]
12442
12533
  });
12443
12534
  }
@@ -12735,6 +12826,16 @@ function PercentageSlider({ className = "" }) {
12735
12826
  })]
12736
12827
  });
12737
12828
  }
12829
+ function GroupAuthorsButton({ compact = false }) {
12830
+ const { openModal } = useModal("group-authors");
12831
+ return /* @__PURE__ */ jsxs("button", {
12832
+ className: cn("btn", { "btn--icon": compact }),
12833
+ title: "Group authors",
12834
+ "aria-label": "Group authors",
12835
+ onClick: () => openModal(),
12836
+ children: [/* @__PURE__ */ jsx(Icon, { path: mdiAccountMultiple }), compact ? null : /* @__PURE__ */ jsx("span", { children: "Group Authors" })]
12837
+ });
12838
+ }
12738
12839
  function Legend({ hoveredObject }) {
12739
12840
  const { sizeMetric, metricType } = useOptions();
12740
12841
  const [metricsData] = useMetrics();
@@ -12772,7 +12873,10 @@ function Legend({ hoveredObject }) {
12772
12873
  }),
12773
12874
  metricType === "TOP_CONTRIBUTOR" ? /* @__PURE__ */ jsx(PercentageSlider, { className: "my-4" }) : null,
12774
12875
  legend,
12775
- metricType === "TOP_CONTRIBUTOR" ? /* @__PURE__ */ jsx(AuthorOptions, {}) : null
12876
+ metricType === "TOP_CONTRIBUTOR" ? /* @__PURE__ */ jsxs("div", {
12877
+ className: "mt-2 grid w-full grid-cols-[1fr_1fr] gap-2",
12878
+ children: [/* @__PURE__ */ jsx(GroupAuthorsButton, {}), /* @__PURE__ */ jsx(ShuffleColorsButton, {})]
12879
+ }) : null
12776
12880
  ] });
12777
12881
  }
12778
12882
  function LoadingIndicator({ className = "", showProgress = false, fetchProgress = true, loadingText }) {
@@ -12887,9 +12991,9 @@ function Providers({ children, data }) {
12887
12991
  ...prevOptions,
12888
12992
  labelsVisible: visible
12889
12993
  })),
12890
- setRenderCutoff: (renderCutoff) => setOptions((prevOptions) => ({
12994
+ setRenderCutOff: (renderCutOff) => setOptions((prevOptions) => ({
12891
12995
  ...prevOptions,
12892
- renderCutoff
12996
+ renderCutOff
12893
12997
  })),
12894
12998
  setShowFilesWithoutChanges: (showFilesWithoutChanges) => setOptions((prevOptions) => ({
12895
12999
  ...prevOptions,
@@ -12971,6 +13075,16 @@ const SearchCard = memo(function SearchCard$1() {
12971
13075
  const options = useOptions();
12972
13076
  const [metrics] = useMetrics();
12973
13077
  const resultRefs = Object.keys(searchResults).map(() => createRef());
13078
+ useEffect(() => {
13079
+ startTransition$1(() => {
13080
+ if (searchText.trim() === "") setSearchResults({});
13081
+ else setSearchResults(findSearchResults(databaseInfo.fileTree, searchText));
13082
+ });
13083
+ }, [
13084
+ searchText,
13085
+ databaseInfo.fileTree,
13086
+ setSearchResults
13087
+ ]);
12974
13088
  useKey({
12975
13089
  key: "f",
12976
13090
  ctrlOrMeta: true
@@ -12993,7 +13107,6 @@ const SearchCard = memo(function SearchCard$1() {
12993
13107
  children: [
12994
13108
  /* @__PURE__ */ jsx("button", {
12995
13109
  className: "hidden min-w-max cursor-pointer peer-placeholder-shown:hidden peer-focus:inline",
12996
- type: "submit",
12997
13110
  children: /* @__PURE__ */ jsx(Icon, {
12998
13111
  path: mdiClose,
12999
13112
  size: "1em"
@@ -13033,17 +13146,11 @@ const SearchCard = memo(function SearchCard$1() {
13033
13146
  }
13034
13147
  },
13035
13148
  onChange: (event) => {
13036
- const value = event.target.value;
13037
- setSearchText(value);
13038
- startTransition$1(() => {
13039
- if (value.trim() === "") setSearchResults({});
13040
- setSearchResults(findSearchResults(databaseInfo.fileTree, value));
13041
- });
13149
+ setSearchText(event.target.value);
13042
13150
  }
13043
13151
  }),
13044
13152
  /* @__PURE__ */ jsx(Icon, {
13045
13153
  path: mdiMagnify,
13046
- size: "1em",
13047
13154
  className: "hidden min-w-max peer-placeholder-shown:inline peer-focus:hidden"
13048
13155
  }),
13049
13156
  /* @__PURE__ */ jsx("span", {
@@ -13117,18 +13224,11 @@ var BarChart = () => {
13117
13224
  const width = size.width;
13118
13225
  const xScale = d3.scaleBand().domain(data.map((d) => d.date.toString())).range([0, width]);
13119
13226
  const yScale = d3.scaleLinear().domain([0, d3.max(data, (d) => d.count) || 0]).range([height, 0]);
13120
- const [searchParams] = useSearchParams();
13121
- const submit = useSubmit();
13227
+ const submit = useViewSubmit();
13122
13228
  function updateTimeseries(e) {
13123
13229
  const form = new FormData();
13124
13230
  form.append("timeseries", `${e[0]}-${e[1]}`);
13125
- submit(form, {
13126
- action: getPathFromRepoAndHead({
13127
- path: searchParams.get("path"),
13128
- branch: databaseInfo.branch
13129
- }),
13130
- method: "post"
13131
- });
13231
+ submit(form, { method: "post" });
13132
13232
  }
13133
13233
  return /* @__PURE__ */ jsx("div", {
13134
13234
  ref,
@@ -13175,20 +13275,13 @@ function Timeline({ className }) {
13175
13275
  const { timerange, selectedRange } = databaseInfo;
13176
13276
  const newestChangeDate = timerange[1];
13177
13277
  const oldestChangeDate = timerange[0];
13178
- const submit = useSubmit();
13278
+ const submit = useViewSubmit();
13179
13279
  const [range, setRange] = useState(selectedRange[0] === 0 ? timerange : selectedRange);
13180
13280
  const disabled = useNavigation().state !== "idle";
13181
- const [searchParams] = useSearchParams();
13182
13281
  function updateTimeseries(e) {
13183
13282
  const form = new FormData();
13184
13283
  form.append("timeseries", `${e[0]}-${e[1]}`);
13185
- submit(form, {
13186
- action: getPathFromRepoAndHead({
13187
- path: searchParams.get("path"),
13188
- branch: databaseInfo.branch
13189
- }),
13190
- method: "post"
13191
- });
13284
+ submit(form, { method: "post" });
13192
13285
  }
13193
13286
  const selectedStartDate = range[0] * 1e3;
13194
13287
  const selectedEndDate = range[1] * 1e3;
@@ -13313,20 +13406,20 @@ function TimePicker({ range, setRange, timerange, setsBeginning, updateTimeserie
13313
13406
  });
13314
13407
  }
13315
13408
  function RefreshButton() {
13316
- const navigation = useNavigation();
13317
- const isRefreshing = navigation.formData?.get("refresh") === "true";
13409
+ const isRefreshing = useNavigation().formData?.get("refresh") === "true";
13318
13410
  return /* @__PURE__ */ jsxs(Form, {
13319
13411
  method: "post",
13320
- action: navigation.location?.pathname,
13412
+ action: useViewAction(),
13413
+ className: "contents",
13321
13414
  children: [/* @__PURE__ */ jsx("input", {
13322
13415
  type: "hidden",
13323
13416
  name: "refresh",
13324
13417
  value: "true"
13325
13418
  }), /* @__PURE__ */ jsx("button", {
13326
- type: "submit",
13327
- className: "btn",
13419
+ className: "btn btn--icon",
13328
13420
  disabled: isRefreshing,
13329
13421
  title: "Refresh analysis",
13422
+ "aria-label": "Refresh analysis",
13330
13423
  children: /* @__PURE__ */ jsx(Icon, {
13331
13424
  path: mdiRefresh,
13332
13425
  size: "1.25em",
@@ -13419,7 +13512,7 @@ function ColorMetricDependentInfo(props) {
13419
13512
  break;
13420
13513
  }
13421
13514
  if (!contribSum) {
13422
- content = `${icon}${dominant.author}`;
13515
+ content = dominant.author;
13423
13516
  break;
13424
13517
  }
13425
13518
  const authorPercentage = Math.round(dominant.contribcount / contribSum * 100);
@@ -13455,7 +13548,6 @@ function SizeMetricDependentInfo({ sizeMetric, hoveredBlob, databaseInfo }) {
13455
13548
  let icon = mdiCircleSmall;
13456
13549
  let content = null;
13457
13550
  if (!hoveredBlob) return null;
13458
- icon = mdiCircleSmall;
13459
13551
  switch (sizeMetric) {
13460
13552
  case "FILE_SIZE": {
13461
13553
  icon = mdiResize;
@@ -13512,8 +13604,9 @@ function ChartTooltip({ hoveredObject }) {
13512
13604
  function FullscreenButton() {
13513
13605
  const { isFullscreen, toggleFullscreen } = useFullscreen(() => document.documentElement);
13514
13606
  return /* @__PURE__ */ jsx("button", {
13515
- className: cn("btn aspect-square p-1", { "btn--primary": isFullscreen }),
13607
+ className: cn("btn btn--icon", { "btn--primary": isFullscreen }),
13516
13608
  title: isFullscreen ? "Exit fullscreen" : "Enter fullscreen",
13609
+ "aria-label": isFullscreen ? "Exit fullscreen" : "Enter fullscreen",
13517
13610
  onClick: toggleFullscreen,
13518
13611
  children: /* @__PURE__ */ jsx(Icon, {
13519
13612
  path: isFullscreen ? mdiFullscreenExit : mdiFullscreen,
@@ -13578,8 +13671,9 @@ function RevisionSelect({ headGroups, disabled, className = "", analyzedBranches
13578
13671
  function SettingsButton() {
13579
13672
  const { openModal } = useModal("app-settings");
13580
13673
  return /* @__PURE__ */ jsx("button", {
13581
- className: "btn hover:text-primary-text dark:hover:text-primary-text-dark relative flex cursor-pointer justify-between gap-2",
13582
- title: "Visualization settings",
13674
+ className: "btn btn--icon",
13675
+ title: "Settings",
13676
+ "aria-label": "Settings",
13583
13677
  onClick: () => openModal(),
13584
13678
  children: /* @__PURE__ */ jsx(Icon, { path: mdiCog })
13585
13679
  });
@@ -13590,8 +13684,25 @@ function BrowseParentFolder() {
13590
13684
  if (!data) return null;
13591
13685
  return /* @__PURE__ */ jsx("div", { children: data.repo.parentDirPath });
13592
13686
  }
13687
+ function ResetTimeIntervalButton() {
13688
+ const data = useData();
13689
+ const viewAction = useViewAction();
13690
+ return /* @__PURE__ */ jsxs(Form, {
13691
+ className: cn({ invisible: data.databaseInfo.timerange[0] === data.databaseInfo.selectedRange[0] && data.databaseInfo.timerange[1] === data.databaseInfo.selectedRange[1] }),
13692
+ method: "post",
13693
+ action: viewAction,
13694
+ children: [/* @__PURE__ */ jsx("input", {
13695
+ type: "hidden",
13696
+ name: "timeseries",
13697
+ value: `${data.databaseInfo.timerange[0]}-${data.databaseInfo.timerange[1]}`
13698
+ }), /* @__PURE__ */ jsxs("button", {
13699
+ className: cn("btn btn--text", {}),
13700
+ children: [/* @__PURE__ */ jsx(Icon, { path: mdiRestore }), "Reset time interval"]
13701
+ })]
13702
+ });
13703
+ }
13593
13704
  var view_exports = /* @__PURE__ */ __export({
13594
- action: () => action$1,
13705
+ action: () => action,
13595
13706
  currentRepositoryContext: () => currentRepositoryContext,
13596
13707
  default: () => view_default,
13597
13708
  loadViewSearchParams: () => loadViewSearchParams,
@@ -13603,10 +13714,10 @@ var view_exports = /* @__PURE__ */ __export({
13603
13714
  }, 1);
13604
13715
  const currentRepositoryContext = createContext();
13605
13716
  const viewSearchParamsConfig = {
13606
- path: parseAsString,
13607
- objectPath: parseAsString,
13608
- zoomPath: parseAsString,
13609
- branch: parseAsString
13717
+ path: parseAsString$1,
13718
+ objectPath: parseAsString$1,
13719
+ zoomPath: parseAsString$1,
13720
+ branch: parseAsString$1
13610
13721
  };
13611
13722
  const viewSerializer = createSerializer$1(viewSearchParamsConfig);
13612
13723
  const loadViewSearchParams = createLoader(viewSearchParamsConfig);
@@ -13665,9 +13776,10 @@ const loader$8 = async ({ request, context }) => {
13665
13776
  return params$1;
13666
13777
  }, viewSearchParams);
13667
13778
  if (shouldRedirect) {
13668
- const redirectUrl = href("/view") + viewSerializer(params);
13779
+ const redirectUrl = new URL(request.url);
13780
+ redirectUrl.search = viewSerializer(params);
13669
13781
  log.warn(`At least one required parameter is missing, redirecting to ${redirectUrl}`);
13670
- throw redirect(redirectUrl);
13782
+ throw redirect(redirectUrl.toString());
13671
13783
  }
13672
13784
  return {
13673
13785
  dataPromise: analyze({
@@ -13679,8 +13791,8 @@ const loader$8 = async ({ request, context }) => {
13679
13791
  versionInfo
13680
13792
  };
13681
13793
  };
13682
- const action$1 = async ({ request, context }) => {
13683
- const { instance } = context.get(currentRepositoryContext);
13794
+ const action = async ({ request, context }) => {
13795
+ const { instance, repositoryPath } = context.get(currentRepositoryContext);
13684
13796
  const formData = await request.formData();
13685
13797
  const refresh = formData.get("refresh");
13686
13798
  const unionedAuthors = formData.get("unionedAuthors");
@@ -13688,11 +13800,36 @@ const action$1 = async ({ request, context }) => {
13688
13800
  const timeseries = formData.get("timeseries");
13689
13801
  const authorname = formData.get("authorname");
13690
13802
  const authorcolor = formData.get("authorcolor");
13803
+ const hidePath = formData.get("hide");
13804
+ const unhidePath = formData.get("show");
13805
+ const unhideAll = formData.get("unhideAll");
13806
+ const openPath = formData.get("open");
13691
13807
  instance.prevInvokeReason = "unknown";
13692
13808
  if (refresh) {
13693
13809
  instance.prevInvokeReason = "refresh";
13694
13810
  return null;
13695
13811
  }
13812
+ if (hidePath && typeof hidePath === "string") {
13813
+ log.info("Ignoring path: " + hidePath);
13814
+ instance.prevInvokeReason = "hide";
13815
+ await instance.db.addHiddenFile(hidePath);
13816
+ return null;
13817
+ }
13818
+ if (unhidePath && typeof unhidePath === "string") {
13819
+ instance.prevInvokeReason = "show";
13820
+ await instance.db.removeHiddenFile(unhidePath);
13821
+ return null;
13822
+ }
13823
+ if (unhideAll && typeof unhideAll === "string") {
13824
+ instance.prevInvokeReason = "hide";
13825
+ await instance.db.clearHiddenFiles();
13826
+ return null;
13827
+ }
13828
+ if (typeof openPath === "string") {
13829
+ instance.prevInvokeReason = "open";
13830
+ openFile(repositoryPath, openPath);
13831
+ return null;
13832
+ }
13696
13833
  if (typeof unionedAuthors === "string") {
13697
13834
  instance.prevInvokeReason = "unionedAuthors";
13698
13835
  const json = JSON.parse(unionedAuthors);
@@ -13818,17 +13955,17 @@ async function analyze({ instance, path: path$1, branch }) {
13818
13955
  }
13819
13956
  var view_default = UNSAFE_withComponentProps(function Repo() {
13820
13957
  const { versionInfo, dataPromise } = useLoaderData();
13821
- const [{ leftExpanded }, dispatch] = useReducer((prevState, action$3) => {
13822
- switch (action$3) {
13958
+ const [{ leftExpanded }, dispatch] = useReducer((prevState, action$2) => {
13959
+ switch (action$2) {
13823
13960
  case "toggleLeft": return { leftExpanded: !prevState.leftExpanded };
13824
13961
  }
13825
13962
  }, { leftExpanded: true });
13826
13963
  const toggleLeft = () => dispatch("toggleLeft");
13827
13964
  const [hoveredObject, setHoveredObject] = useState(null);
13828
- const location$1 = useLocation();
13965
+ const location = useLocation();
13829
13966
  const navigate = useNavigate();
13830
13967
  const [objectPath] = useQueryState("objectPath", viewSearchParamsConfig.objectPath);
13831
- const clearCacheUrl = `/clear-cache?${new URLSearchParams({ redirect: location$1.pathname + location$1.search }).toString()}`;
13968
+ const clearCacheUrl = `/clear-cache?${new URLSearchParams({ redirect: location.pathname + location.search }).toString()}`;
13832
13969
  const objectPathIsFile = objectPath?.split("/").pop()?.includes(".");
13833
13970
  return /* @__PURE__ */ jsx(Suspense, {
13834
13971
  fallback: /* @__PURE__ */ jsx("div", {
@@ -13850,11 +13987,11 @@ var view_default = UNSAFE_withComponentProps(function Repo() {
13850
13987
  children: (data) => /* @__PURE__ */ jsxs(Providers, {
13851
13988
  data,
13852
13989
  children: [/* @__PURE__ */ jsxs("div", {
13853
- className: cn(`grid grid-cols-1 transition-all [grid-template-areas:"main"_"left"] lg:h-screen lg:grid-cols-[0_1fr] lg:grid-rows-[1fr] lg:overflow-hidden lg:[grid-template-areas:"left_main"]`, { "gap-2 lg:grid-cols-[var(--spacing-sidepanel)_1fr]": leftExpanded }),
13990
+ className: cn(`grid grid-cols-1 transition-all [grid-template-areas:"main"_"left"] lg:h-screen lg:grid-cols-[0_1fr] lg:grid-rows-[1fr] lg:overflow-hidden lg:[grid-template-areas:"left_main"]`, { "lg:grid-cols-[var(--spacing-sidepanel)_1fr]": leftExpanded }),
13854
13991
  children: [/* @__PURE__ */ jsx(Activity, {
13855
13992
  mode: leftExpanded ? "visible" : "hidden",
13856
13993
  children: /* @__PURE__ */ jsxs("aside", {
13857
- className: clsx$1("*:not-first:m-2 lg:pr-0 lg:transition-transform", leftExpanded ? "overflow-y-auto [grid-area:left]" : "lg:-translate-x-sidepanel"),
13994
+ className: clsx$1("*:not-first:m-2 lg:transition-transform", leftExpanded ? "overflow-y-auto [grid-area:left]" : "lg:-translate-x-sidepanel"),
13858
13995
  children: [
13859
13996
  /* @__PURE__ */ jsxs("div", {
13860
13997
  className: "bg-primary-bg dark:bg-primary-bg-dark sticky top-0 z-10 flex justify-between p-2",
@@ -13904,10 +14041,10 @@ var view_default = UNSAFE_withComponentProps(function Repo() {
13904
14041
  ]
13905
14042
  })
13906
14043
  }), /* @__PURE__ */ jsxs("main", {
13907
- className: cn("relative grid h-full min-h-screen min-w-25 grid-rows-[auto_1fr_auto] gap-2 [grid-area:main] lg:transition-transform"),
14044
+ className: cn("relative grid h-full min-h-screen min-w-25 grid-rows-[auto_1fr_auto] gap-2 p-2 [grid-area:main] lg:transition-transform"),
13908
14045
  children: [
13909
14046
  /* @__PURE__ */ jsxs("header", {
13910
- className: "from-primary-bg dark:from-primary-bg-dark to-primary-bg dark:to-primary-bg-dark top-0 right-0 left-0 z-10 grid grid-flow-col items-center justify-between gap-2 bg-linear-to-r via-transparent p-2",
14047
+ className: "grid grid-flow-col items-center justify-between gap-2",
13911
14048
  children: [
13912
14049
  /* @__PURE__ */ jsxs("div", {
13913
14050
  className: "flex gap-2",
@@ -13920,7 +14057,7 @@ var view_default = UNSAFE_withComponentProps(function Repo() {
13920
14057
  path: mdiMenu,
13921
14058
  size: 1
13922
14059
  })
13923
- }) : /* @__PURE__ */ jsx("div", {}),
14060
+ }) : null,
13924
14061
  /* @__PURE__ */ jsx(BrowseParentFolder, {}),
13925
14062
  /* @__PURE__ */ jsx(Breadcrumb, { zoom: true })
13926
14063
  ]
@@ -13939,13 +14076,14 @@ var view_default = UNSAFE_withComponentProps(function Repo() {
13939
14076
  defaultValue: data.databaseInfo.branch,
13940
14077
  headGroups: data.repo.refs,
13941
14078
  analyzedBranches: data.databaseInfo.analyzedRepos.filter((rep) => rep.repo === data.databaseInfo.repo),
13942
- onChange: (e) => navigate(getPathFromRepoAndHead({
14079
+ onChange: (e) => navigate(href("/view") + viewSerializer({
13943
14080
  path: data.repo.repositoryPath,
13944
14081
  branch: e.target.value
13945
14082
  }))
13946
14083
  }, data.databaseInfo.branch) : /* @__PURE__ */ jsx("div", {}),
13947
14084
  /* @__PURE__ */ jsx(RefreshButton, {}),
13948
- /* @__PURE__ */ jsx(HiddenFiles, {}),
14085
+ /* @__PURE__ */ jsx(HideFilesButton, {}),
14086
+ /* @__PURE__ */ jsx(GroupAuthorsButton, { compact: true }),
13949
14087
  /* @__PURE__ */ jsx(SettingsButton, {}),
13950
14088
  /* @__PURE__ */ jsx(FullscreenButton, {})
13951
14089
  ]
@@ -13969,24 +14107,13 @@ var view_default = UNSAFE_withComponentProps(function Repo() {
13969
14107
  }), createPortal(/* @__PURE__ */ jsx(ChartTooltip, { hoveredObject }), document.body)] }) })
13970
14108
  }),
13971
14109
  /* @__PURE__ */ jsxs("div", {
13972
- className: "flex flex-col gap-1 px-2 text-center select-none",
14110
+ className: "flex flex-col text-center select-none",
13973
14111
  children: [/* @__PURE__ */ jsxs("div", {
13974
14112
  className: "flex items-start justify-between gap-2",
13975
14113
  children: [/* @__PURE__ */ jsxs("h2", {
13976
14114
  className: "card__title",
13977
14115
  children: ["Commits per ", data.databaseInfo.commitCountPerTimeIntervalUnit]
13978
- }), /* @__PURE__ */ jsxs(Form, {
13979
- className: cn({ invisible: data.databaseInfo.timerange[0] === data.databaseInfo.selectedRange[0] && data.databaseInfo.timerange[1] === data.databaseInfo.selectedRange[1] }),
13980
- method: "post",
13981
- children: [/* @__PURE__ */ jsx("input", {
13982
- type: "hidden",
13983
- name: "timeseries",
13984
- value: `${data.databaseInfo.timerange[0]}-${data.databaseInfo.timerange[1]}`
13985
- }), /* @__PURE__ */ jsxs("button", {
13986
- className: cn("btn btn--text", {}),
13987
- children: [/* @__PURE__ */ jsx(Icon, { path: mdiRestore }), "Reset time interval"]
13988
- })]
13989
- })]
14116
+ }), /* @__PURE__ */ jsx(ResetTimeIntervalButton, {})]
13990
14117
  }), /* @__PURE__ */ jsx(Timeline, {}, `${data.databaseInfo.selectedRange[0]}-${data.databaseInfo.selectedRange[1]}`)]
13991
14118
  })
13992
14119
  ]
@@ -14165,7 +14292,7 @@ function CommitListEntry(props) {
14165
14292
  }
14166
14293
  function CommitHistory({ commits, commitCount }) {
14167
14294
  const navigation = useNavigation();
14168
- const location$1 = useLocation();
14295
+ const location = useLocation();
14169
14296
  const [searchParams, setSearchParams] = useSearchParams();
14170
14297
  const commitShowCount = Number(searchParams.get("count") ?? String(10));
14171
14298
  const { clickedObject } = useClickedObject();
@@ -14183,13 +14310,13 @@ function CommitHistory({ commits, commitCount }) {
14183
14310
  onClick: () => setSearchParams((prev) => {
14184
14311
  prev.set("count", String(Number(prev.get("count") ?? String(10)) + 10));
14185
14312
  return prev;
14186
- }, { state: location$1.state }),
14313
+ }, { state: location.state }),
14187
14314
  children: "Show more commits"
14188
14315
  }) : null : /* @__PURE__ */ jsx("h3", { children: "Loading commits..." })] }) });
14189
14316
  }
14190
14317
  function RepoTabs() {
14191
14318
  const navigate = useNavigate();
14192
- const location$1 = useLocation();
14319
+ const location = useLocation();
14193
14320
  const isCommits = useMatch(href("/view/commits"));
14194
14321
  return /* @__PURE__ */ jsx(IconRadioGroup, {
14195
14322
  large: true,
@@ -14202,8 +14329,9 @@ function RepoTabs() {
14202
14329
  "/commits": mdiSourceCommit
14203
14330
  },
14204
14331
  defaultValue: isCommits ? "/commits" : "/details",
14332
+ ariaLabel: "Select details tab",
14205
14333
  onChange: (v) => {
14206
- navigate(href(`/view${v}`) + location$1.search, { state: location$1.state });
14334
+ navigate(href(`/view${v}`) + location.search, { state: location.state });
14207
14335
  }
14208
14336
  });
14209
14337
  }
@@ -14288,7 +14416,6 @@ function usePath() {
14288
14416
  }
14289
14417
  var view_details_exports = /* @__PURE__ */ __export({
14290
14418
  HydrateFallback: () => HydrateFallback,
14291
- action: () => action,
14292
14419
  default: () => view_details_default,
14293
14420
  loader: () => loader$1
14294
14421
  }, 1);
@@ -14310,46 +14437,20 @@ const loader$1 = async ({ request, context }) => {
14310
14437
  authorDistributionPromise: instance.db.getAuthorContribsForPath(objectPath)
14311
14438
  };
14312
14439
  };
14313
- const action = async ({ request, context }) => {
14314
- const { instance } = context.get(currentRepositoryContext);
14315
- const formData = await request.formData();
14316
- const ignorePath = formData.get("ignore");
14317
- const unignorePath = formData.get("unignore");
14318
- const openPath = formData.get("open");
14319
- if (ignorePath && typeof ignorePath === "string") {
14320
- instance.prevInvokeReason = "ignore";
14321
- const hidden = await instance.db.getHiddenFiles();
14322
- hidden.push(ignorePath);
14323
- await instance.db.replaceHiddenFiles(hidden);
14324
- return null;
14325
- }
14326
- if (unignorePath && typeof unignorePath === "string") {
14327
- instance.prevInvokeReason = "unignore";
14328
- const hidden = await instance.db.getHiddenFiles();
14329
- await instance.db.replaceHiddenFiles(hidden.filter((path$1) => path$1 !== unignorePath));
14330
- return null;
14331
- }
14332
- if (typeof openPath === "string") {
14333
- instance.prevInvokeReason = "open";
14334
- openFile(instance.repositoryPath, openPath);
14335
- return null;
14336
- }
14337
- return null;
14338
- };
14339
14440
  var view_details_default = UNSAFE_withComponentProps(function Details() {
14340
14441
  const { setPath } = usePath();
14341
14442
  const { path: path$1, authorDistributionPromise } = useLoaderData();
14342
14443
  const data = useData();
14343
14444
  const { state } = useNavigation();
14344
- const location$1 = useLocation();
14345
- const clickedObject = location$1.state?.clickedObject;
14445
+ const location = useLocation();
14446
+ const viewAction = useViewAction();
14447
+ const clickedObject = location.state?.clickedObject;
14346
14448
  const setOpen = useSetOpenCollapsibleHeader();
14347
14449
  const [viewSearchParams] = useQueryStates(viewSearchParamsConfig);
14348
- const zoomLink = location$1.pathname + viewSerializer({
14450
+ const zoomLink = location.pathname + viewSerializer({
14349
14451
  ...viewSearchParams,
14350
14452
  zoomPath: path$1
14351
14453
  });
14352
- const { openModal } = useModal("group-authors");
14353
14454
  useEffect(() => {
14354
14455
  setOpen(!!clickedObject);
14355
14456
  }, [clickedObject, setOpen]);
@@ -14386,18 +14487,14 @@ var view_details_default = UNSAFE_withComponentProps(function Details() {
14386
14487
  })
14387
14488
  })]
14388
14489
  }),
14389
- /* @__PURE__ */ jsxs("button", {
14390
- className: "btn",
14391
- onClick: () => openModal(),
14392
- children: [/* @__PURE__ */ jsx(Icon, { path: mdiAccountMultiple }), "Group authors"]
14393
- }),
14490
+ /* @__PURE__ */ jsx(GroupAuthorsButton, {}),
14394
14491
  /* @__PURE__ */ jsxs("div", {
14395
14492
  className: "mt-2 flex flex-wrap gap-2",
14396
14493
  children: [/* @__PURE__ */ jsxs(Link, {
14397
14494
  className: "btn",
14398
14495
  to: zoomLink,
14399
14496
  children: [
14400
- /* @__PURE__ */ jsx(Icon, { path: mdiSearchWeb }),
14497
+ /* @__PURE__ */ jsx(Icon, { path: mdiMagnify }),
14401
14498
  "Zoom to this ",
14402
14499
  isBlob$1 ? "file" : "folder"
14403
14500
  ]
@@ -14406,11 +14503,10 @@ var view_details_default = UNSAFE_withComponentProps(function Details() {
14406
14503
  method: "post",
14407
14504
  children: [/* @__PURE__ */ jsx("input", {
14408
14505
  type: "hidden",
14409
- name: "ignore",
14506
+ name: "hide",
14410
14507
  value: clickedObject.path
14411
14508
  }), /* @__PURE__ */ jsxs("button", {
14412
14509
  className: "btn",
14413
- type: "submit",
14414
14510
  disabled: state !== "idle",
14415
14511
  title: "Hide this file",
14416
14512
  children: [/* @__PURE__ */ jsx(Icon, { path: mdiEyeOffOutline }), "Hide"]
@@ -14420,25 +14516,23 @@ var view_details_default = UNSAFE_withComponentProps(function Details() {
14420
14516
  method: "post",
14421
14517
  children: [/* @__PURE__ */ jsx("input", {
14422
14518
  type: "hidden",
14423
- name: "ignore",
14519
+ name: "hide",
14424
14520
  value: `*.${extension}`
14425
14521
  }), /* @__PURE__ */ jsxs("button", {
14426
14522
  className: "btn",
14427
- type: "submit",
14428
14523
  disabled: state !== "idle",
14429
14524
  title: `Hide all files with .${extension} extension`,
14430
14525
  children: [/* @__PURE__ */ jsx(Icon, { path: mdiEyeOffOutline }), /* @__PURE__ */ jsxs("span", { children: ["Hide *.", extension] })]
14431
14526
  })]
14432
14527
  }) : null] }) : /* @__PURE__ */ jsxs(Form, {
14433
14528
  method: "post",
14434
- action: location$1.pathname,
14529
+ action: viewAction,
14435
14530
  children: [/* @__PURE__ */ jsx("input", {
14436
14531
  type: "hidden",
14437
- name: "ignore",
14532
+ name: "hide",
14438
14533
  value: clickedObject.path
14439
14534
  }), /* @__PURE__ */ jsxs("button", {
14440
14535
  className: "btn",
14441
- type: "submit",
14442
14536
  disabled: state !== "idle",
14443
14537
  onClick: () => {
14444
14538
  setPath(resolveParentFolder(path$1));
@@ -14531,6 +14625,7 @@ function LastchangedEntry(props) {
14531
14625
  function PathEntry(props) {
14532
14626
  const { state } = useNavigation();
14533
14627
  const { clickedObject } = useClickedObject();
14628
+ const viewAction = useViewAction();
14534
14629
  if (!clickedObject) return null;
14535
14630
  return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("div", {
14536
14631
  className: "flex grow items-center overflow-hidden text-sm font-semibold text-ellipsis whitespace-pre",
@@ -14543,7 +14638,7 @@ function PathEntry(props) {
14543
14638
  children: props.path
14544
14639
  }), /* @__PURE__ */ jsxs(Form, {
14545
14640
  method: "post",
14546
- action: location.pathname,
14641
+ action: viewAction,
14547
14642
  children: [/* @__PURE__ */ jsx("input", {
14548
14643
  type: "hidden",
14549
14644
  name: "open",
@@ -15010,13 +15105,6 @@ var ui_default = UNSAFE_withComponentProps(function UI() {
15010
15105
  latestVersion: "1.0.1"
15011
15106
  })
15012
15107
  }),
15013
- /* @__PURE__ */ jsxs("div", {
15014
- className: "card",
15015
- children: [/* @__PURE__ */ jsx("h2", {
15016
- className: "card__title",
15017
- children: "AuthorOptions"
15018
- }), /* @__PURE__ */ jsx(AuthorOptions, {})]
15019
- }),
15020
15108
  /* @__PURE__ */ jsxs("div", {
15021
15109
  className: "card",
15022
15110
  children: [
@@ -15054,6 +15142,7 @@ var ui_default = UNSAFE_withComponentProps(function UI() {
15054
15142
  }),
15055
15143
  (() => {
15056
15144
  return /* @__PURE__ */ jsx(IconRadioGroup, {
15145
+ ariaLabel: "Select demo option",
15057
15146
  group: {
15058
15147
  A: "Alpha",
15059
15148
  B: "Beta",
@@ -15071,6 +15160,7 @@ var ui_default = UNSAFE_withComponentProps(function UI() {
15071
15160
  (() => {
15072
15161
  return /* @__PURE__ */ jsx(IconRadioGroup, {
15073
15162
  large: true,
15163
+ ariaLabel: "Select demo option large",
15074
15164
  group: {
15075
15165
  A: "Alpha",
15076
15166
  B: "Beta",
@@ -15137,12 +15227,8 @@ async function loader({ request, params }) {
15137
15227
  }
15138
15228
  var server_manifest_default = {
15139
15229
  "entry": {
15140
- "module": "/assets/entry.client-lU2GATMj.js",
15141
- "imports": [
15142
- "/assets/jsx-runtime-s_YwNCZb.js",
15143
- "/assets/react-dom-BkDQo3BN.js",
15144
- "/assets/chunk-JPUPSTYD-YBbsfvLw.js"
15145
- ],
15230
+ "module": "/assets/entry.client-Ci1gLbyI.js",
15231
+ "imports": ["/assets/jsx-runtime-s_YwNCZb.js", "/assets/react-dom-B9ui1jR3.js"],
15146
15232
  "css": []
15147
15233
  },
15148
15234
  "routes": {
@@ -15159,16 +15245,14 @@ var server_manifest_default = {
15159
15245
  "hasClientMiddleware": false,
15160
15246
  "hasDefaultExport": true,
15161
15247
  "hasErrorBoundary": true,
15162
- "module": "/assets/root-CMX9SEB-.js",
15248
+ "module": "/assets/root-CGqKHKfX.js",
15163
15249
  "imports": [
15164
15250
  "/assets/jsx-runtime-s_YwNCZb.js",
15165
- "/assets/react-dom-BkDQo3BN.js",
15166
- "/assets/chunk-JPUPSTYD-YBbsfvLw.js",
15167
- "/assets/GitTruckInfo-Bu3InKk0.js",
15168
- "/assets/compare-Br3z3FUS-Bm1os1Jk.js",
15169
- "/assets/clear-cache-CAdofhUa.js"
15251
+ "/assets/react-dom-B9ui1jR3.js",
15252
+ "/assets/dist-Bpk5WHYg.js",
15253
+ "/assets/clear-cache-BJQQzFka.js"
15170
15254
  ],
15171
- "css": ["/assets/root-DEstG3hL.css"],
15255
+ "css": ["/assets/root-BK0u4vgl.css"],
15172
15256
  "clientActionModule": void 0,
15173
15257
  "clientLoaderModule": void 0,
15174
15258
  "clientMiddlewareModule": void 0,
@@ -15208,12 +15292,12 @@ var server_manifest_default = {
15208
15292
  "hasClientMiddleware": false,
15209
15293
  "hasDefaultExport": true,
15210
15294
  "hasErrorBoundary": false,
15211
- "module": "/assets/clear-cache-Ciosxv25.js",
15295
+ "module": "/assets/clear-cache-DkN7P69Z.js",
15212
15296
  "imports": [
15213
15297
  "/assets/jsx-runtime-s_YwNCZb.js",
15214
- "/assets/GitTruckInfo-Bu3InKk0.js",
15215
- "/assets/react-dom-BkDQo3BN.js",
15216
- "/assets/clear-cache-CAdofhUa.js"
15298
+ "/assets/dist-Bpk5WHYg.js",
15299
+ "/assets/react-dom-B9ui1jR3.js",
15300
+ "/assets/clear-cache-BJQQzFka.js"
15217
15301
  ],
15218
15302
  "css": [],
15219
15303
  "clientActionModule": void 0,
@@ -15297,14 +15381,12 @@ var server_manifest_default = {
15297
15381
  "hasClientMiddleware": false,
15298
15382
  "hasDefaultExport": true,
15299
15383
  "hasErrorBoundary": false,
15300
- "module": "/assets/browse-BVbfiHLu.js",
15384
+ "module": "/assets/browse-Bx9O5Mvl.js",
15301
15385
  "imports": [
15302
15386
  "/assets/jsx-runtime-s_YwNCZb.js",
15303
- "/assets/browse-DMoBg6Vh.js",
15304
- "/assets/GitTruckInfo-Bu3InKk0.js",
15305
- "/assets/react-dom-BkDQo3BN.js",
15306
- "/assets/compare-Br3z3FUS-Bm1os1Jk.js",
15307
- "/assets/chunk-JPUPSTYD-YBbsfvLw.js"
15387
+ "/assets/browse-B9wBPRaK.js",
15388
+ "/assets/dist-Bpk5WHYg.js",
15389
+ "/assets/react-dom-B9ui1jR3.js"
15308
15390
  ],
15309
15391
  "css": [],
15310
15392
  "clientActionModule": void 0,
@@ -15346,14 +15428,12 @@ var server_manifest_default = {
15346
15428
  "hasClientMiddleware": false,
15347
15429
  "hasDefaultExport": true,
15348
15430
  "hasErrorBoundary": false,
15349
- "module": "/assets/view-JlAiRFiK.js",
15431
+ "module": "/assets/view-mmd0Q4B8.js",
15350
15432
  "imports": [
15351
15433
  "/assets/jsx-runtime-s_YwNCZb.js",
15352
- "/assets/browse-DMoBg6Vh.js",
15353
- "/assets/GitTruckInfo-Bu3InKk0.js",
15354
- "/assets/react-dom-BkDQo3BN.js",
15355
- "/assets/compare-Br3z3FUS-Bm1os1Jk.js",
15356
- "/assets/chunk-JPUPSTYD-YBbsfvLw.js"
15434
+ "/assets/browse-B9wBPRaK.js",
15435
+ "/assets/dist-Bpk5WHYg.js",
15436
+ "/assets/react-dom-B9ui1jR3.js"
15357
15437
  ],
15358
15438
  "css": [],
15359
15439
  "clientActionModule": void 0,
@@ -15395,15 +15475,13 @@ var server_manifest_default = {
15395
15475
  "hasClientMiddleware": false,
15396
15476
  "hasDefaultExport": true,
15397
15477
  "hasErrorBoundary": false,
15398
- "module": "/assets/view.commits-CIgkE9N1.js",
15478
+ "module": "/assets/view.commits-b-k-A7BT.js",
15399
15479
  "imports": [
15400
15480
  "/assets/jsx-runtime-s_YwNCZb.js",
15401
- "/assets/browse-DMoBg6Vh.js",
15402
- "/assets/GitTruckInfo-Bu3InKk0.js",
15403
- "/assets/react-dom-BkDQo3BN.js",
15404
- "/assets/RepoTabs-B_gVZfyQ.js",
15405
- "/assets/compare-Br3z3FUS-Bm1os1Jk.js",
15406
- "/assets/chunk-JPUPSTYD-YBbsfvLw.js"
15481
+ "/assets/browse-B9wBPRaK.js",
15482
+ "/assets/dist-Bpk5WHYg.js",
15483
+ "/assets/react-dom-B9ui1jR3.js",
15484
+ "/assets/RepoTabs-36qUc-RT.js"
15407
15485
  ],
15408
15486
  "css": [],
15409
15487
  "clientActionModule": void 0,
@@ -15417,22 +15495,20 @@ var server_manifest_default = {
15417
15495
  "path": "details",
15418
15496
  "index": void 0,
15419
15497
  "caseSensitive": void 0,
15420
- "hasAction": true,
15498
+ "hasAction": false,
15421
15499
  "hasLoader": true,
15422
15500
  "hasClientAction": false,
15423
15501
  "hasClientLoader": false,
15424
15502
  "hasClientMiddleware": false,
15425
15503
  "hasDefaultExport": true,
15426
15504
  "hasErrorBoundary": false,
15427
- "module": "/assets/view.details-DAXK445Q.js",
15505
+ "module": "/assets/view.details-B7LCPu46.js",
15428
15506
  "imports": [
15429
15507
  "/assets/jsx-runtime-s_YwNCZb.js",
15430
- "/assets/browse-DMoBg6Vh.js",
15431
- "/assets/GitTruckInfo-Bu3InKk0.js",
15432
- "/assets/react-dom-BkDQo3BN.js",
15433
- "/assets/RepoTabs-B_gVZfyQ.js",
15434
- "/assets/compare-Br3z3FUS-Bm1os1Jk.js",
15435
- "/assets/chunk-JPUPSTYD-YBbsfvLw.js"
15508
+ "/assets/browse-B9wBPRaK.js",
15509
+ "/assets/dist-Bpk5WHYg.js",
15510
+ "/assets/react-dom-B9ui1jR3.js",
15511
+ "/assets/RepoTabs-36qUc-RT.js"
15436
15512
  ],
15437
15513
  "css": [],
15438
15514
  "clientActionModule": void 0,
@@ -15474,14 +15550,12 @@ var server_manifest_default = {
15474
15550
  "hasClientMiddleware": false,
15475
15551
  "hasDefaultExport": true,
15476
15552
  "hasErrorBoundary": false,
15477
- "module": "/assets/ui-C21Ckl3a.js",
15553
+ "module": "/assets/ui-Bw635ygf.js",
15478
15554
  "imports": [
15479
15555
  "/assets/jsx-runtime-s_YwNCZb.js",
15480
- "/assets/browse-DMoBg6Vh.js",
15481
- "/assets/GitTruckInfo-Bu3InKk0.js",
15482
- "/assets/react-dom-BkDQo3BN.js",
15483
- "/assets/compare-Br3z3FUS-Bm1os1Jk.js",
15484
- "/assets/chunk-JPUPSTYD-YBbsfvLw.js"
15556
+ "/assets/browse-B9wBPRaK.js",
15557
+ "/assets/dist-Bpk5WHYg.js",
15558
+ "/assets/react-dom-B9ui1jR3.js"
15485
15559
  ],
15486
15560
  "css": [],
15487
15561
  "clientActionModule": void 0,
@@ -15511,8 +15585,8 @@ var server_manifest_default = {
15511
15585
  "hydrateFallbackModule": void 0
15512
15586
  }
15513
15587
  },
15514
- "url": "/assets/manifest-bbde1677.js",
15515
- "version": "bbde1677",
15588
+ "url": "/assets/manifest-601d0239.js",
15589
+ "version": "601d0239",
15516
15590
  "sri": void 0
15517
15591
  };
15518
15592
  const assetsBuildDirectory = "build/client";