@thxgg/steward 0.1.18 → 0.1.20

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 (126) hide show
  1. package/.output/nitro.json +1 -1
  2. package/.output/public/_nuxt/B3PShd4B.js +3 -0
  3. package/.output/public/_nuxt/{CJlXUkTg.js → B7kcsnX1.js} +1 -1
  4. package/.output/public/_nuxt/B9j1BHt9.js +1 -0
  5. package/.output/public/_nuxt/BGSfDLaX.js +1 -0
  6. package/.output/public/_nuxt/{C_HVaH3B.js → BTHAQvnA.js} +1 -1
  7. package/.output/public/_nuxt/C2LWefrW.js +1 -0
  8. package/.output/public/_nuxt/{WUF6Thhn.js → CV5HFGKm.js} +1 -1
  9. package/.output/public/_nuxt/{CGzrvVc6.js → ClxxfTZn.js} +1 -1
  10. package/.output/public/_nuxt/{QAzsKGuP.js → CzCDTesu.js} +1 -1
  11. package/.output/public/_nuxt/{DEr8q68O.js → D6aZT905.js} +1 -1
  12. package/.output/public/_nuxt/{TSsR_oCL.js → D9fz0wy8.js} +1 -1
  13. package/.output/public/_nuxt/DDV2bymk.js +60 -0
  14. package/.output/public/_nuxt/DK5VWQk7.js +1 -0
  15. package/.output/public/_nuxt/{DAnnHVQP.js → DSqaInP-.js} +1 -1
  16. package/.output/public/_nuxt/Detail.CYc96mGf.css +1 -0
  17. package/.output/public/_nuxt/{BA4e9-N5.js → Dn6yoG20.js} +2 -2
  18. package/.output/public/_nuxt/{-z_Gr0GN.js → a87LfEfa.js} +1 -1
  19. package/.output/public/_nuxt/builds/latest.json +1 -1
  20. package/.output/public/_nuxt/builds/meta/37438177-5e14-4a46-9af4-eae491ba1f24.json +1 -0
  21. package/.output/public/_nuxt/entry.Dp3jx0Yw.css +1 -0
  22. package/.output/public/_nuxt/zhOijcjw.js +30 -0
  23. package/.output/server/chunks/_/prd-service.mjs +101 -68
  24. package/.output/server/chunks/_/prd-service.mjs.map +1 -1
  25. package/.output/server/chunks/_/repos.mjs +3 -179
  26. package/.output/server/chunks/_/repos.mjs.map +1 -1
  27. package/.output/server/chunks/_/task-graph.mjs +8 -4
  28. package/.output/server/chunks/_/task-graph.mjs.map +1 -1
  29. package/.output/server/chunks/_/watcher.mjs +2 -32
  30. package/.output/server/chunks/_/watcher.mjs.map +1 -1
  31. package/.output/server/chunks/build/{Detail-BQSkP9Zm.mjs → Detail-B7yBNjgp.mjs} +324 -140
  32. package/.output/server/chunks/build/Detail-B7yBNjgp.mjs.map +1 -0
  33. package/.output/server/chunks/build/DiffViewer-styles-1.mjs-d2dQvARr.mjs +4 -0
  34. package/.output/server/chunks/build/DiffViewer-styles-1.mjs-d2dQvARr.mjs.map +1 -0
  35. package/.output/server/chunks/build/DiffViewer-styles.BDwAqkTk.mjs +8 -0
  36. package/.output/server/chunks/build/DiffViewer-styles.BDwAqkTk.mjs.map +1 -0
  37. package/.output/server/chunks/build/DiffViewer-styles.DRJh5Ui4.mjs +10 -0
  38. package/.output/server/chunks/build/DiffViewer-styles.DRJh5Ui4.mjs.map +1 -0
  39. package/.output/server/chunks/build/{_prd_-CBR_wm9i.mjs → _prd_-DY25apyl.mjs} +81 -6
  40. package/.output/server/chunks/build/_prd_-DY25apyl.mjs.map +1 -0
  41. package/.output/server/chunks/build/client.precomputed.mjs +1 -1
  42. package/.output/server/chunks/build/{default-Cao5eO80.mjs → default-BKKgG7HJ.mjs} +221 -23
  43. package/.output/server/chunks/build/default-BKKgG7HJ.mjs.map +1 -0
  44. package/.output/server/chunks/build/error-404-Bf6kdO80.mjs +2 -0
  45. package/.output/server/chunks/build/error-500-D_bcARXN.mjs +2 -0
  46. package/.output/server/chunks/build/{index-ljj9uTXI.mjs → index-DE1tjHAd.mjs} +4 -3
  47. package/.output/server/chunks/build/index-DE1tjHAd.mjs.map +1 -0
  48. package/.output/server/chunks/build/nuxt-link-SvT1nf8Z.mjs +1 -1
  49. package/.output/server/chunks/build/{repo-graph-CVnkmn8i.mjs → repo-graph-CUcJKW1F.mjs} +26 -11
  50. package/.output/server/chunks/build/repo-graph-CUcJKW1F.mjs.map +1 -0
  51. package/.output/server/chunks/build/server.mjs +15 -13
  52. package/.output/server/chunks/build/styles.mjs +2 -2
  53. package/.output/server/chunks/build/{usePrd-f7ylhIqs.mjs → usePrd-hXZOmvAv.mjs} +113 -9
  54. package/.output/server/chunks/build/usePrd-hXZOmvAv.mjs.map +1 -0
  55. package/.output/server/chunks/nitro/nitro.mjs +1311 -599
  56. package/.output/server/chunks/nitro/nitro.mjs.map +1 -1
  57. package/.output/server/chunks/routes/api/index.get.mjs +2 -1
  58. package/.output/server/chunks/routes/api/index.get.mjs.map +1 -1
  59. package/.output/server/chunks/routes/api/index.post.mjs +2 -2
  60. package/.output/server/chunks/routes/api/repos/_repoId/git/commits.get.mjs +2 -1
  61. package/.output/server/chunks/routes/api/repos/_repoId/git/commits.get.mjs.map +1 -1
  62. package/.output/server/chunks/routes/api/repos/_repoId/git/diff.get.mjs +2 -1
  63. package/.output/server/chunks/routes/api/repos/_repoId/git/diff.get.mjs.map +1 -1
  64. package/.output/server/chunks/routes/api/repos/_repoId/git/file-content.get.mjs +2 -1
  65. package/.output/server/chunks/routes/api/repos/_repoId/git/file-content.get.mjs.map +1 -1
  66. package/.output/server/chunks/routes/api/repos/_repoId/git/file-diff.get.mjs +2 -1
  67. package/.output/server/chunks/routes/api/repos/_repoId/git/file-diff.get.mjs.map +1 -1
  68. package/.output/server/chunks/routes/api/repos/_repoId/graph.get.mjs +27 -4
  69. package/.output/server/chunks/routes/api/repos/_repoId/graph.get.mjs.map +1 -1
  70. package/.output/server/chunks/routes/api/repos/_repoId/prd/_prdSlug/archive.post.mjs +93 -0
  71. package/.output/server/chunks/routes/api/repos/_repoId/prd/_prdSlug/archive.post.mjs.map +1 -0
  72. package/.output/server/chunks/routes/api/repos/_repoId/prd/_prdSlug/graph.get.mjs +2 -2
  73. package/.output/server/chunks/routes/api/repos/_repoId/prd/_prdSlug/progress.get.mjs +3 -3
  74. package/.output/server/chunks/routes/api/repos/_repoId/prd/_prdSlug/tasks/_taskId/commits.get.mjs +3 -3
  75. package/.output/server/chunks/routes/api/repos/_repoId/prd/_prdSlug/tasks.get.mjs +3 -3
  76. package/.output/server/chunks/routes/api/repos/_repoId/prd/_prdSlug_.get.mjs +2 -2
  77. package/.output/server/chunks/routes/api/repos/_repoId/prds.get.mjs +27 -4
  78. package/.output/server/chunks/routes/api/repos/_repoId/prds.get.mjs.map +1 -1
  79. package/.output/server/chunks/routes/api/repos/_repoId/refresh-git-repos.post.mjs +2 -1
  80. package/.output/server/chunks/routes/api/repos/_repoId/refresh-git-repos.post.mjs.map +1 -1
  81. package/.output/server/chunks/routes/api/repos/_repoId_.delete.mjs +2 -1
  82. package/.output/server/chunks/routes/api/repos/_repoId_.delete.mjs.map +1 -1
  83. package/.output/server/chunks/routes/api/runtime.get.mjs +2 -0
  84. package/.output/server/chunks/routes/api/runtime.get.mjs.map +1 -1
  85. package/.output/server/chunks/routes/api/state-migration/status.get.mjs +21 -0
  86. package/.output/server/chunks/routes/api/state-migration/status.get.mjs.map +1 -0
  87. package/.output/server/chunks/routes/api/watch.get.mjs +4 -3
  88. package/.output/server/chunks/routes/api/watch.get.mjs.map +1 -1
  89. package/.output/server/chunks/routes/renderer.mjs +1 -1
  90. package/.output/server/index.mjs +3 -1
  91. package/.output/server/index.mjs.map +1 -1
  92. package/.output/server/package.json +1 -1
  93. package/README.md +3 -0
  94. package/dist/app/lib/async-request.js +39 -0
  95. package/dist/server/utils/db.js +15 -0
  96. package/dist/server/utils/prd-archive.js +53 -0
  97. package/dist/server/utils/prd-service.js +26 -6
  98. package/dist/server/utils/prd-state.js +11 -2
  99. package/dist/server/utils/state-migration.js +225 -0
  100. package/dist/server/utils/state-schema.js +181 -4
  101. package/dist/server/utils/task-graph.js +10 -3
  102. package/package.json +1 -1
  103. package/.output/public/_nuxt/5LlyHjkF.js +0 -60
  104. package/.output/public/_nuxt/BA0u_CRT.js +0 -1
  105. package/.output/public/_nuxt/BO8EM227.js +0 -3
  106. package/.output/public/_nuxt/C0XT5P3Q.js +0 -1
  107. package/.output/public/_nuxt/CZsXZugv.js +0 -1
  108. package/.output/public/_nuxt/Detail.DSyVQNdr.css +0 -1
  109. package/.output/public/_nuxt/DrXxYwWw.js +0 -30
  110. package/.output/public/_nuxt/builds/meta/19e0e040-a531-4c25-b46d-a6ca54a1ae3e.json +0 -1
  111. package/.output/public/_nuxt/entry.LcDOtJnR.css +0 -1
  112. package/.output/public/_nuxt/i9wn3hS7.js +0 -1
  113. package/.output/server/chunks/build/Detail-BQSkP9Zm.mjs.map +0 -1
  114. package/.output/server/chunks/build/DiffViewer-styles-1.mjs-BFsE2PCW.mjs +0 -4
  115. package/.output/server/chunks/build/DiffViewer-styles-1.mjs-BFsE2PCW.mjs.map +0 -1
  116. package/.output/server/chunks/build/DiffViewer-styles.D2bqX3nK.mjs +0 -8
  117. package/.output/server/chunks/build/DiffViewer-styles.D2bqX3nK.mjs.map +0 -1
  118. package/.output/server/chunks/build/DiffViewer-styles.FoV36wuV.mjs +0 -10
  119. package/.output/server/chunks/build/DiffViewer-styles.FoV36wuV.mjs.map +0 -1
  120. package/.output/server/chunks/build/_prd_-CBR_wm9i.mjs.map +0 -1
  121. package/.output/server/chunks/build/default-Cao5eO80.mjs.map +0 -1
  122. package/.output/server/chunks/build/index-ByZO4Bvq.mjs +0 -76
  123. package/.output/server/chunks/build/index-ByZO4Bvq.mjs.map +0 -1
  124. package/.output/server/chunks/build/index-ljj9uTXI.mjs.map +0 -1
  125. package/.output/server/chunks/build/repo-graph-CVnkmn8i.mjs.map +0 -1
  126. package/.output/server/chunks/build/usePrd-f7ylhIqs.mjs.map +0 -1
@@ -1,14 +1,13 @@
1
- import { defineComponent, markRaw, ref, watch, computed, mergeProps, unref, withCtx, createVNode, useModel, createTextVNode, toDisplayString, openBlock, createBlock, createCommentVNode, resolveDynamicComponent, Fragment, renderList, mergeModels, renderSlot, readonly, useSSRContext } from 'vue';
2
- import { ssrRenderAttrs, ssrRenderComponent, ssrInterpolate, ssrRenderVNode, ssrRenderList, ssrRenderClass, ssrRenderSlot, ssrRenderAttr, ssrRenderStyle } from 'vue/server-renderer';
3
- import { useMediaQuery, reactiveOmit } from '@vueuse/core';
1
+ import { defineComponent, unref, mergeProps, withCtx, renderSlot, markRaw, ref, watch, computed, createVNode, useModel, createTextVNode, toDisplayString, openBlock, createBlock, createCommentVNode, resolveDynamicComponent, Fragment, renderList, mergeModels, useSSRContext } from 'vue';
2
+ import { ssrRenderComponent, ssrRenderSlot, ssrRenderAttrs, ssrInterpolate, ssrRenderVNode, ssrRenderList, ssrRenderClass, ssrRenderAttr, ssrRenderStyle } from 'vue/server-renderer';
3
+ import { reactiveOmit, useMediaQuery } from '@vueuse/core';
4
4
  import { Background } from '@vue-flow/background';
5
5
  import { Controls } from '@vue-flow/controls';
6
6
  import { MarkerType, VueFlow, Position, Handle } from '@vue-flow/core';
7
7
  import { Loader2, AlertCircle, GitBranch, Circle, Clock3, CheckCircle2, AlertTriangle, Clock, ArrowLeft, Tag, ListOrdered, CheckSquare, Check, Link2, Diff, Calendar, ExternalLink, X, RefreshCw, Keyboard, FileDiff, FileCode, GitCommit, FileText, Plus, Minus, FolderGit2, ArrowRight, FileWarning, ChevronDown, Link, Link2Off, FileEdit, FileX, FilePlus } from 'lucide-vue-next';
8
- import { B as Button } from './index-ByZO4Bvq.mjs';
8
+ import { c as cn, B as Button, b as useToast } from './usePrd-hXZOmvAv.mjs';
9
9
  import { _ as _export_sfc } from './_plugin-vue_export-helper-1tPrXgE0.mjs';
10
10
  import { Primitive, useForwardPropsEmits, DialogRoot, DialogPortal, DialogContent, DialogClose, DialogTitle, DialogDescription, Separator as Separator$1, DialogOverlay, TooltipProvider as TooltipProvider$1, TooltipRoot, TooltipTrigger as TooltipTrigger$1, TooltipPortal, TooltipContent as TooltipContent$1, TooltipArrow, ScrollAreaRoot, ScrollAreaViewport, ScrollAreaCorner, ScrollAreaScrollbar, ScrollAreaThumb, DialogTrigger } from 'reka-ui';
11
- import { c as cn, b as useToast } from './usePrd-f7ylhIqs.mjs';
12
11
  import { cva } from 'class-variance-authority';
13
12
  import { _ as __nuxt_component_0$3 } from './nuxt-link-SvT1nf8Z.mjs';
14
13
 
@@ -583,6 +582,14 @@ const _sfc_main$j = /* @__PURE__ */ defineComponent({
583
582
  ref(null);
584
583
  ref(null);
585
584
  ref(false);
585
+ let fullFileHighlightRunVersion = 0;
586
+ let diffHighlightRunVersion = 0;
587
+ watch(
588
+ () => props.filePath,
589
+ () => {
590
+ showAll.value = false;
591
+ }
592
+ );
586
593
  const totalLines = computed(() => {
587
594
  let count = 0;
588
595
  for (const hunk of props.hunks) {
@@ -672,6 +679,7 @@ const _sfc_main$j = /* @__PURE__ */ defineComponent({
672
679
  }
673
680
  const displayItems = computed(() => {
674
681
  const items = [];
682
+ const idPrefix = props.filePath;
675
683
  for (let hunkIndex = 0; hunkIndex < props.hunks.length; hunkIndex++) {
676
684
  const hunk = props.hunks[hunkIndex];
677
685
  if (hunkIndex > 0) {
@@ -685,7 +693,7 @@ const _sfc_main$j = /* @__PURE__ */ defineComponent({
685
693
  items.push({
686
694
  type: "line",
687
695
  pair: {
688
- id: `${hunkIndex}-${i}`,
696
+ id: `${idPrefix}:${hunkIndex}-${i}`,
689
697
  left: {
690
698
  lineNum: line.oldNumber,
691
699
  content: line.content,
@@ -717,7 +725,7 @@ const _sfc_main$j = /* @__PURE__ */ defineComponent({
717
725
  items.push({
718
726
  type: "line",
719
727
  pair: {
720
- id: `${hunkIndex}-${i - maxLen + j}`,
728
+ id: `${idPrefix}:${hunkIndex}-${i - maxLen + j}`,
721
729
  left: removeLine ? {
722
730
  lineNum: removeLine.oldNumber,
723
731
  content: removeLine.content,
@@ -735,7 +743,7 @@ const _sfc_main$j = /* @__PURE__ */ defineComponent({
735
743
  items.push({
736
744
  type: "line",
737
745
  pair: {
738
- id: `${hunkIndex}-${i}`,
746
+ id: `${idPrefix}:${hunkIndex}-${i}`,
739
747
  left: { content: "", type: "empty" },
740
748
  right: {
741
749
  lineNum: line.newNumber,
@@ -814,72 +822,109 @@ const _sfc_main$j = /* @__PURE__ */ defineComponent({
814
822
  const isLoadingFullFile = ref(false);
815
823
  watch(
816
824
  () => [props.fileContent, props.filePath],
817
- async ([content]) => {
825
+ async ([content], _, onCleanup) => {
826
+ const runVersion = ++fullFileHighlightRunVersion;
827
+ let invalidated = false;
828
+ onCleanup(() => {
829
+ invalidated = true;
830
+ });
831
+ highlightedFullFile.value = [];
818
832
  if (!content) {
819
- highlightedFullFile.value = [];
833
+ isLoadingFullFile.value = false;
820
834
  return;
821
835
  }
822
836
  isLoadingFullFile.value = true;
823
- const lang = language.value;
824
- highlightedFullFile.value = await highlightFullContent(content, lang);
825
- isLoadingFullFile.value = false;
837
+ try {
838
+ const highlighted = await highlightFullContent(content, language.value);
839
+ if (invalidated || runVersion !== fullFileHighlightRunVersion) {
840
+ return;
841
+ }
842
+ highlightedFullFile.value = highlighted;
843
+ } finally {
844
+ if (!invalidated && runVersion === fullFileHighlightRunVersion) {
845
+ isLoadingFullFile.value = false;
846
+ }
847
+ }
826
848
  },
827
849
  { immediate: true }
828
850
  );
829
851
  watch(
830
852
  () => [props.hunks, props.filePath, highlightedFullFile.value],
831
- async () => {
853
+ async (_, __, onCleanup) => {
854
+ const runVersion = ++diffHighlightRunVersion;
855
+ let invalidated = false;
856
+ onCleanup(() => {
857
+ invalidated = true;
858
+ });
832
859
  isLoading.value = true;
860
+ highlightedLines.value = /* @__PURE__ */ new Map();
833
861
  const lang = language.value;
834
862
  const newHighlighted = /* @__PURE__ */ new Map();
835
863
  const fullFileLines2 = highlightedFullFile.value;
836
864
  const linesToHighlight = [];
837
- for (const item of displayItems.value) {
838
- if (item.type === "line" && item.pair) {
839
- if (item.pair.left.content && item.pair.left.type !== "empty") {
840
- const key = `${item.pair.id}-old`;
841
- if (item.pair.left.type === "context" && item.pair.left.lineNum && fullFileLines2.length > 0) {
842
- const lineIndex = item.pair.right.lineNum ? item.pair.right.lineNum - 1 : -1;
843
- if (lineIndex >= 0 && lineIndex < fullFileLines2.length) {
844
- newHighlighted.set(key, fullFileLines2[lineIndex] || "");
865
+ try {
866
+ for (const item of displayItems.value) {
867
+ if (invalidated || runVersion !== diffHighlightRunVersion) {
868
+ return;
869
+ }
870
+ if (item.type === "line" && item.pair) {
871
+ if (item.pair.left.content && item.pair.left.type !== "empty") {
872
+ const key = `${item.pair.id}-old`;
873
+ if (item.pair.left.type === "context" && item.pair.left.lineNum && fullFileLines2.length > 0) {
874
+ const lineIndex = item.pair.right.lineNum ? item.pair.right.lineNum - 1 : -1;
875
+ if (lineIndex >= 0 && lineIndex < fullFileLines2.length) {
876
+ newHighlighted.set(key, fullFileLines2[lineIndex] || "");
877
+ } else {
878
+ linesToHighlight.push({ key, content: item.pair.left.content });
879
+ }
845
880
  } else {
846
881
  linesToHighlight.push({ key, content: item.pair.left.content });
847
882
  }
848
- } else {
849
- linesToHighlight.push({ key, content: item.pair.left.content });
850
883
  }
851
- }
852
- if (item.pair.right.content && item.pair.right.type !== "empty") {
853
- const key = `${item.pair.id}-new`;
854
- const lineNum = item.pair.right.lineNum;
855
- if (lineNum && fullFileLines2.length > 0 && lineNum <= fullFileLines2.length) {
856
- newHighlighted.set(key, fullFileLines2[lineNum - 1] || "");
857
- } else {
858
- linesToHighlight.push({ key, content: item.pair.right.content });
884
+ if (item.pair.right.content && item.pair.right.type !== "empty") {
885
+ const key = `${item.pair.id}-new`;
886
+ const lineNum = item.pair.right.lineNum;
887
+ if (lineNum && fullFileLines2.length > 0 && lineNum <= fullFileLines2.length) {
888
+ newHighlighted.set(key, fullFileLines2[lineNum - 1] || "");
889
+ } else {
890
+ linesToHighlight.push({ key, content: item.pair.right.content });
891
+ }
859
892
  }
860
893
  }
861
894
  }
862
- }
863
- const limitedLines = linesToHighlight.slice(0, MAX_DIFF_HIGHLIGHT_LINES);
864
- const overflowLines = linesToHighlight.slice(MAX_DIFF_HIGHLIGHT_LINES);
865
- for (const { key, content } of overflowLines) {
866
- newHighlighted.set(key, escapeHtml(content));
867
- }
868
- const batchSize = 50;
869
- for (let i = 0; i < limitedLines.length; i += batchSize) {
870
- const batch = limitedLines.slice(i, i + batchSize);
871
- const results = await Promise.all(
872
- batch.map(async ({ key, content }) => {
873
- const result = await highlightLine(content, lang);
874
- return { key, result };
875
- })
876
- );
877
- for (const { key, result } of results) {
878
- newHighlighted.set(key, result);
895
+ const limitedLines = linesToHighlight.slice(0, MAX_DIFF_HIGHLIGHT_LINES);
896
+ const overflowLines = linesToHighlight.slice(MAX_DIFF_HIGHLIGHT_LINES);
897
+ for (const { key, content } of overflowLines) {
898
+ newHighlighted.set(key, escapeHtml(content));
899
+ }
900
+ const batchSize = 50;
901
+ for (let i = 0; i < limitedLines.length; i += batchSize) {
902
+ if (invalidated || runVersion !== diffHighlightRunVersion) {
903
+ return;
904
+ }
905
+ const batch = limitedLines.slice(i, i + batchSize);
906
+ const results = await Promise.all(
907
+ batch.map(async ({ key, content }) => {
908
+ const result = await highlightLine(content, lang);
909
+ return { key, result };
910
+ })
911
+ );
912
+ if (invalidated || runVersion !== diffHighlightRunVersion) {
913
+ return;
914
+ }
915
+ for (const { key, result } of results) {
916
+ newHighlighted.set(key, result);
917
+ }
918
+ }
919
+ if (invalidated || runVersion !== diffHighlightRunVersion) {
920
+ return;
921
+ }
922
+ highlightedLines.value = newHighlighted;
923
+ } finally {
924
+ if (!invalidated && runVersion === diffHighlightRunVersion) {
925
+ isLoading.value = false;
879
926
  }
880
927
  }
881
- highlightedLines.value = newHighlighted;
882
- isLoading.value = false;
883
928
  },
884
929
  { immediate: true }
885
930
  );
@@ -891,21 +936,21 @@ const _sfc_main$j = /* @__PURE__ */ defineComponent({
891
936
  return "context";
892
937
  }
893
938
  return (_ctx, _push, _parent, _attrs) => {
894
- _push(`<div${ssrRenderAttrs(mergeProps({ class: "diff-viewer" }, _attrs))} data-v-a5da7e6e>`);
939
+ _push(`<div${ssrRenderAttrs(mergeProps({ class: "diff-viewer" }, _attrs))} data-v-9eb8ced7>`);
895
940
  if ((unref(isLoading) || __props.isLoadingContent || unref(isLoadingFullFile)) && !__props.binary) {
896
- _push(`<div class="flex items-center justify-center py-8" data-v-a5da7e6e><div class="size-6 animate-spin rounded-full border-2 border-primary border-t-transparent" data-v-a5da7e6e></div></div>`);
941
+ _push(`<div class="flex items-center justify-center py-8" data-v-9eb8ced7><div class="size-6 animate-spin rounded-full border-2 border-primary border-t-transparent" data-v-9eb8ced7></div></div>`);
897
942
  } else if (__props.binary) {
898
- _push(`<div class="flex flex-col items-center justify-center gap-3 py-12 text-muted-foreground" data-v-a5da7e6e>`);
943
+ _push(`<div class="flex flex-col items-center justify-center gap-3 py-12 text-muted-foreground" data-v-9eb8ced7>`);
899
944
  _push(ssrRenderComponent(unref(FileWarning), { class: "size-10 opacity-50" }, null, _parent));
900
- _push(`<div class="text-center" data-v-a5da7e6e><p class="font-medium" data-v-a5da7e6e>Binary file</p><p class="text-sm" data-v-a5da7e6e>This file cannot be displayed as a diff</p></div></div>`);
945
+ _push(`<div class="text-center" data-v-9eb8ced7><p class="font-medium" data-v-9eb8ced7>Binary file</p><p class="text-sm" data-v-9eb8ced7>This file cannot be displayed as a diff</p></div></div>`);
901
946
  } else if (unref(isEmpty) && !__props.showFullFile) {
902
- _push(`<div class="flex flex-col items-center justify-center gap-3 py-12 text-muted-foreground" data-v-a5da7e6e>`);
947
+ _push(`<div class="flex flex-col items-center justify-center gap-3 py-12 text-muted-foreground" data-v-9eb8ced7>`);
903
948
  _push(ssrRenderComponent(unref(AlertTriangle), { class: "size-10 opacity-50" }, null, _parent));
904
- _push(`<div class="text-center" data-v-a5da7e6e><p class="font-medium" data-v-a5da7e6e>No changes</p><p class="text-sm" data-v-a5da7e6e>This file was touched but has no content changes</p></div></div>`);
949
+ _push(`<div class="text-center" data-v-9eb8ced7><p class="font-medium" data-v-9eb8ced7>No changes</p><p class="text-sm" data-v-9eb8ced7>This file was touched but has no content changes</p></div></div>`);
905
950
  } else if (unref(isLargeFile) && !unref(showAll) && !__props.showFullFile) {
906
- _push(`<div class="diff-container" data-v-a5da7e6e><div class="flex flex-col items-center justify-center gap-3 border-b border-border bg-muted/30 py-6" data-v-a5da7e6e>`);
951
+ _push(`<div class="diff-container" data-v-9eb8ced7><div class="flex flex-col items-center justify-center gap-3 border-b border-border bg-muted/30 py-6" data-v-9eb8ced7>`);
907
952
  _push(ssrRenderComponent(unref(AlertTriangle), { class: "size-8 text-yellow-500" }, null, _parent));
908
- _push(`<div class="text-center" data-v-a5da7e6e><p class="font-medium" data-v-a5da7e6e>Large file</p><p class="text-sm text-muted-foreground" data-v-a5da7e6e> This file has ${ssrInterpolate(unref(totalLines).toLocaleString())} lines (threshold: ${ssrInterpolate(LINE_LIMIT.toLocaleString())}) </p></div>`);
953
+ _push(`<div class="text-center" data-v-9eb8ced7><p class="font-medium" data-v-9eb8ced7>Large file</p><p class="text-sm text-muted-foreground" data-v-9eb8ced7> This file has ${ssrInterpolate(unref(totalLines).toLocaleString())} lines (threshold: ${ssrInterpolate(LINE_LIMIT.toLocaleString())}) </p></div>`);
909
954
  _push(ssrRenderComponent(unref(Button), {
910
955
  variant: "outline",
911
956
  size: "sm",
@@ -926,15 +971,15 @@ const _sfc_main$j = /* @__PURE__ */ defineComponent({
926
971
  }, _parent));
927
972
  _push(`</div></div>`);
928
973
  } else if (__props.showFullFile && __props.fileContent) {
929
- _push(`<div class="diff-container" data-v-a5da7e6e><div class="full-file-view" data-v-a5da7e6e><!--[-->`);
974
+ _push(`<div class="diff-container" data-v-9eb8ced7><div class="full-file-view" data-v-9eb8ced7><!--[-->`);
930
975
  ssrRenderList(unref(fullFileLines), (line, index) => {
931
976
  _push(`<div class="${ssrRenderClass([{
932
977
  "diff-add": getFullFileLineType(index + 1) === "add"
933
- }, "full-file-line"])}" data-v-a5da7e6e><div class="diff-gutter" data-v-a5da7e6e><span class="line-number" data-v-a5da7e6e>${ssrInterpolate(index + 1)}</span></div><div class="diff-content" data-v-a5da7e6e><span class="diff-code" data-v-a5da7e6e>${(unref(highlightedFullFile)[index] || escapeHtml(line)) ?? ""}</span></div></div>`);
978
+ }, "full-file-line"])}" data-v-9eb8ced7><div class="diff-gutter" data-v-9eb8ced7><span class="line-number" data-v-9eb8ced7>${ssrInterpolate(index + 1)}</span></div><div class="diff-content" data-v-9eb8ced7><span class="diff-code" data-v-9eb8ced7>${(unref(highlightedFullFile)[index] || escapeHtml(line)) ?? ""}</span></div></div>`);
934
979
  });
935
980
  _push(`<!--]--></div></div>`);
936
981
  } else {
937
- _push(`<div class="diff-container" data-v-a5da7e6e><div class="diff-toolbar" data-v-a5da7e6e>`);
982
+ _push(`<div class="diff-container" data-v-9eb8ced7><div class="diff-toolbar" data-v-9eb8ced7>`);
938
983
  _push(ssrRenderComponent(unref(Button), {
939
984
  variant: "ghost",
940
985
  size: "sm",
@@ -954,25 +999,25 @@ const _sfc_main$j = /* @__PURE__ */ defineComponent({
954
999
  }),
955
1000
  _: 1
956
1001
  }, _parent));
957
- _push(`</div><div class="diff-split" data-v-a5da7e6e><div class="diff-column diff-column-left" data-v-a5da7e6e><div class="diff-column-content" data-v-a5da7e6e><!--[-->`);
1002
+ _push(`</div><div class="diff-split" data-v-9eb8ced7><div class="diff-column diff-column-left" data-v-9eb8ced7><div class="diff-column-content" data-v-9eb8ced7><!--[-->`);
958
1003
  ssrRenderList(unref(displayItems), (item) => {
959
1004
  _push(`<!--[-->`);
960
1005
  if (item.type === "separator") {
961
- _push(`<div class="diff-separator-half" data-v-a5da7e6e><div class="separator-line" data-v-a5da7e6e></div><span class="separator-text" data-v-a5da7e6e>···</span></div>`);
1006
+ _push(`<div class="diff-separator-half" data-v-9eb8ced7><div class="separator-line" data-v-9eb8ced7></div><span class="separator-text" data-v-9eb8ced7>···</span></div>`);
962
1007
  } else if (item.pair) {
963
1008
  _push(`<div class="${ssrRenderClass([{
964
1009
  "diff-remove": item.pair.left.type === "remove",
965
1010
  "diff-empty": item.pair.left.type === "empty",
966
1011
  "diff-context": item.pair.left.type === "context"
967
- }, "diff-line"])}" data-v-a5da7e6e><div class="diff-gutter" data-v-a5da7e6e>`);
1012
+ }, "diff-line"])}" data-v-9eb8ced7><div class="diff-gutter" data-v-9eb8ced7>`);
968
1013
  if (item.pair.left.lineNum) {
969
- _push(`<span class="line-number" data-v-a5da7e6e>${ssrInterpolate(item.pair.left.lineNum)}</span>`);
1014
+ _push(`<span class="line-number" data-v-9eb8ced7>${ssrInterpolate(item.pair.left.lineNum)}</span>`);
970
1015
  } else {
971
1016
  _push(`<!---->`);
972
1017
  }
973
- _push(`</div><div class="diff-content" data-v-a5da7e6e>`);
1018
+ _push(`</div><div class="diff-content" data-v-9eb8ced7>`);
974
1019
  if (item.pair.left.type !== "empty") {
975
- _push(`<span class="diff-code" data-v-a5da7e6e>${(getHighlightedContent(item.pair.id, "old") || escapeHtml(item.pair.left.content)) ?? ""}</span>`);
1020
+ _push(`<span class="diff-code" data-v-9eb8ced7>${(getHighlightedContent(item.pair.id, "old") || escapeHtml(item.pair.left.content)) ?? ""}</span>`);
976
1021
  } else {
977
1022
  _push(`<!---->`);
978
1023
  }
@@ -982,25 +1027,25 @@ const _sfc_main$j = /* @__PURE__ */ defineComponent({
982
1027
  }
983
1028
  _push(`<!--]-->`);
984
1029
  });
985
- _push(`<!--]--></div></div><div class="diff-column diff-column-right" data-v-a5da7e6e><div class="diff-column-content" data-v-a5da7e6e><!--[-->`);
1030
+ _push(`<!--]--></div></div><div class="diff-column diff-column-right" data-v-9eb8ced7><div class="diff-column-content" data-v-9eb8ced7><!--[-->`);
986
1031
  ssrRenderList(unref(displayItems), (item) => {
987
1032
  _push(`<!--[-->`);
988
1033
  if (item.type === "separator") {
989
- _push(`<div class="diff-separator-half" data-v-a5da7e6e><span class="separator-text" data-v-a5da7e6e>···</span><div class="separator-line" data-v-a5da7e6e></div></div>`);
1034
+ _push(`<div class="diff-separator-half" data-v-9eb8ced7><span class="separator-text" data-v-9eb8ced7>···</span><div class="separator-line" data-v-9eb8ced7></div></div>`);
990
1035
  } else if (item.pair) {
991
1036
  _push(`<div class="${ssrRenderClass([{
992
1037
  "diff-add": item.pair.right.type === "add",
993
1038
  "diff-empty": item.pair.right.type === "empty",
994
1039
  "diff-context": item.pair.right.type === "context"
995
- }, "diff-line"])}" data-v-a5da7e6e><div class="diff-gutter" data-v-a5da7e6e>`);
1040
+ }, "diff-line"])}" data-v-9eb8ced7><div class="diff-gutter" data-v-9eb8ced7>`);
996
1041
  if (item.pair.right.lineNum) {
997
- _push(`<span class="line-number" data-v-a5da7e6e>${ssrInterpolate(item.pair.right.lineNum)}</span>`);
1042
+ _push(`<span class="line-number" data-v-9eb8ced7>${ssrInterpolate(item.pair.right.lineNum)}</span>`);
998
1043
  } else {
999
1044
  _push(`<!---->`);
1000
1045
  }
1001
- _push(`</div><div class="diff-content" data-v-a5da7e6e>`);
1046
+ _push(`</div><div class="diff-content" data-v-9eb8ced7>`);
1002
1047
  if (item.pair.right.type !== "empty") {
1003
- _push(`<span class="diff-code" data-v-a5da7e6e>${(getHighlightedContent(item.pair.id, "new") || escapeHtml(item.pair.right.content)) ?? ""}</span>`);
1048
+ _push(`<span class="diff-code" data-v-9eb8ced7>${(getHighlightedContent(item.pair.id, "new") || escapeHtml(item.pair.right.content)) ?? ""}</span>`);
1004
1049
  } else {
1005
1050
  _push(`<!---->`);
1006
1051
  }
@@ -1022,7 +1067,7 @@ _sfc_main$j.setup = (props, ctx) => {
1022
1067
  (ssrContext.modules || (ssrContext.modules = /* @__PURE__ */ new Set())).add("components/git/DiffViewer.vue");
1023
1068
  return _sfc_setup$j ? _sfc_setup$j(props, ctx) : void 0;
1024
1069
  };
1025
- const __nuxt_component_1$2 = /* @__PURE__ */ Object.assign(_export_sfc(_sfc_main$j, [["__scopeId", "data-v-a5da7e6e"]]), { __name: "GitDiffViewer" });
1070
+ const __nuxt_component_1$2 = /* @__PURE__ */ Object.assign(_export_sfc(_sfc_main$j, [["__scopeId", "data-v-9eb8ced7"]]), { __name: "GitDiffViewer" });
1026
1071
  const _sfc_main$i = /* @__PURE__ */ defineComponent({
1027
1072
  __name: "ScrollBar",
1028
1073
  __ssrInlineRender: true,
@@ -1323,6 +1368,45 @@ _sfc_main$d.setup = (props, ctx) => {
1323
1368
  return _sfc_setup$d ? _sfc_setup$d(props, ctx) : void 0;
1324
1369
  };
1325
1370
  const TooltipTrigger = Object.assign(_sfc_main$d, { __name: "UiTooltipTrigger" });
1371
+ function createLatestRequestManager() {
1372
+ let activeTicket = null;
1373
+ function begin() {
1374
+ activeTicket?.controller.abort();
1375
+ const controller = new AbortController();
1376
+ const ticket = {
1377
+ controller,
1378
+ signal: controller.signal,
1379
+ isCurrent: () => activeTicket === ticket && !controller.signal.aborted
1380
+ };
1381
+ activeTicket = ticket;
1382
+ return ticket;
1383
+ }
1384
+ function clear(ticket) {
1385
+ if (activeTicket === ticket) {
1386
+ activeTicket = null;
1387
+ }
1388
+ }
1389
+ function cancel() {
1390
+ activeTicket?.controller.abort();
1391
+ activeTicket = null;
1392
+ }
1393
+ return {
1394
+ begin,
1395
+ clear,
1396
+ cancel
1397
+ };
1398
+ }
1399
+ function isAbortError(error) {
1400
+ if (!error || typeof error !== "object") {
1401
+ return false;
1402
+ }
1403
+ const abortLike = error;
1404
+ if (abortLike.name === "AbortError" || abortLike.cause?.name === "AbortError") {
1405
+ return true;
1406
+ }
1407
+ const message = abortLike.message || abortLike.cause?.message;
1408
+ return typeof message === "string" && message.toLowerCase().includes("aborted");
1409
+ }
1326
1410
  function useGit() {
1327
1411
  const { showError } = useToast();
1328
1412
  function getErrorMessage(error) {
@@ -1332,23 +1416,36 @@ function useGit() {
1332
1416
  }
1333
1417
  return "Unknown error";
1334
1418
  }
1335
- const isLoadingCommits = ref(false);
1336
- const isLoadingDiff = ref(false);
1337
- const isLoadingFileDiff = ref(false);
1338
- const isLoadingFileContent = ref(false);
1339
- async function fetchCommits(repoId, shas, repoPath) {
1419
+ const loadingCommitsCount = ref(0);
1420
+ const loadingDiffCount = ref(0);
1421
+ const loadingFileDiffCount = ref(0);
1422
+ const loadingFileContentCount = ref(0);
1423
+ const isLoadingCommits = computed(() => loadingCommitsCount.value > 0);
1424
+ const isLoadingDiff = computed(() => loadingDiffCount.value > 0);
1425
+ const isLoadingFileDiff = computed(() => loadingFileDiffCount.value > 0);
1426
+ const isLoadingFileContent = computed(() => loadingFileContentCount.value > 0);
1427
+ function startLoading(counter) {
1428
+ counter.value += 1;
1429
+ }
1430
+ function stopLoading(counter) {
1431
+ counter.value = Math.max(0, counter.value - 1);
1432
+ }
1433
+ async function fetchCommits(repoId, shas, options) {
1340
1434
  if (!repoId || shas.length === 0) {
1341
1435
  return { commits: [], failedShas: [] };
1342
1436
  }
1343
- isLoadingCommits.value = true;
1437
+ startLoading(loadingCommitsCount);
1344
1438
  try {
1345
1439
  const query = { shas: shas.join(",") };
1346
- if (repoPath) {
1347
- query.repo = repoPath;
1440
+ if (options?.repoPath) {
1441
+ query.repo = options.repoPath;
1348
1442
  }
1349
1443
  const commits = await $fetch(
1350
1444
  `/api/repos/${repoId}/git/commits`,
1351
- { query }
1445
+ {
1446
+ query,
1447
+ signal: options?.signal
1448
+ }
1352
1449
  );
1353
1450
  const returnedShortShas = commits.map((c) => c.shortSha);
1354
1451
  const failedShas = shas.filter((sha) => {
@@ -1356,76 +1453,96 @@ function useGit() {
1356
1453
  });
1357
1454
  return { commits, failedShas };
1358
1455
  } catch (error) {
1359
- showError("Failed to fetch commits", getErrorMessage(error));
1456
+ if (isAbortError(error)) {
1457
+ return { commits: [], failedShas: [] };
1458
+ }
1459
+ if (!options?.suppressError) {
1460
+ showError("Failed to fetch commits", getErrorMessage(error));
1461
+ }
1360
1462
  return { commits: [], failedShas: shas };
1361
1463
  } finally {
1362
- isLoadingCommits.value = false;
1464
+ stopLoading(loadingCommitsCount);
1363
1465
  }
1364
1466
  }
1365
- async function fetchDiff(repoId, commitSha, repoPath) {
1467
+ async function fetchDiff(repoId, commitSha, options) {
1366
1468
  if (!repoId || !commitSha) {
1367
1469
  return [];
1368
1470
  }
1369
- isLoadingDiff.value = true;
1471
+ startLoading(loadingDiffCount);
1370
1472
  try {
1371
1473
  const query = { commit: commitSha };
1372
- if (repoPath) {
1373
- query.repo = repoPath;
1474
+ if (options?.repoPath) {
1475
+ query.repo = options.repoPath;
1374
1476
  }
1375
1477
  const files = await $fetch(
1376
1478
  `/api/repos/${repoId}/git/diff`,
1377
- { query }
1479
+ {
1480
+ query,
1481
+ signal: options?.signal
1482
+ }
1378
1483
  );
1379
1484
  return files;
1380
1485
  } catch (error) {
1381
- showError("Failed to fetch diff", getErrorMessage(error));
1486
+ if (!isAbortError(error) && !options?.suppressError) {
1487
+ showError("Failed to fetch diff", getErrorMessage(error));
1488
+ }
1382
1489
  throw error;
1383
1490
  } finally {
1384
- isLoadingDiff.value = false;
1491
+ stopLoading(loadingDiffCount);
1385
1492
  }
1386
1493
  }
1387
- async function fetchFileDiff(repoId, commitSha, filePath, repoPath) {
1494
+ async function fetchFileDiff(repoId, commitSha, filePath, options) {
1388
1495
  if (!repoId || !commitSha || !filePath) {
1389
1496
  return [];
1390
1497
  }
1391
- isLoadingFileDiff.value = true;
1498
+ startLoading(loadingFileDiffCount);
1392
1499
  try {
1393
1500
  const query = { commit: commitSha, file: filePath };
1394
- if (repoPath) {
1395
- query.repo = repoPath;
1501
+ if (options?.repoPath) {
1502
+ query.repo = options.repoPath;
1396
1503
  }
1397
1504
  const hunks = await $fetch(
1398
1505
  `/api/repos/${repoId}/git/file-diff`,
1399
- { query }
1506
+ {
1507
+ query,
1508
+ signal: options?.signal
1509
+ }
1400
1510
  );
1401
1511
  return hunks;
1402
1512
  } catch (error) {
1403
- showError("Failed to fetch file diff", getErrorMessage(error));
1513
+ if (!isAbortError(error) && !options?.suppressError) {
1514
+ showError("Failed to fetch file diff", getErrorMessage(error));
1515
+ }
1404
1516
  throw error;
1405
1517
  } finally {
1406
- isLoadingFileDiff.value = false;
1518
+ stopLoading(loadingFileDiffCount);
1407
1519
  }
1408
1520
  }
1409
- async function fetchFileContent(repoId, commitSha, filePath, repoPath) {
1521
+ async function fetchFileContent(repoId, commitSha, filePath, options) {
1410
1522
  if (!repoId || !commitSha || !filePath) {
1411
1523
  return null;
1412
1524
  }
1413
- isLoadingFileContent.value = true;
1525
+ startLoading(loadingFileContentCount);
1414
1526
  try {
1415
1527
  const query = { commit: commitSha, file: filePath };
1416
- if (repoPath) {
1417
- query.repo = repoPath;
1528
+ if (options?.repoPath) {
1529
+ query.repo = options.repoPath;
1418
1530
  }
1419
1531
  const result = await $fetch(
1420
1532
  `/api/repos/${repoId}/git/file-content`,
1421
- { query }
1533
+ {
1534
+ query,
1535
+ signal: options?.signal
1536
+ }
1422
1537
  );
1423
1538
  return result.content;
1424
1539
  } catch (error) {
1425
- showError("Failed to fetch file content", getErrorMessage(error));
1540
+ if (!isAbortError(error) && !options?.suppressError) {
1541
+ showError("Failed to fetch file content", getErrorMessage(error));
1542
+ }
1426
1543
  throw error;
1427
1544
  } finally {
1428
- isLoadingFileContent.value = false;
1545
+ stopLoading(loadingFileContentCount);
1429
1546
  }
1430
1547
  }
1431
1548
  return {
@@ -1435,10 +1552,10 @@ function useGit() {
1435
1552
  fetchFileDiff,
1436
1553
  fetchFileContent,
1437
1554
  // Loading states
1438
- isLoadingCommits: readonly(isLoadingCommits),
1439
- isLoadingDiff: readonly(isLoadingDiff),
1440
- isLoadingFileDiff: readonly(isLoadingFileDiff),
1441
- isLoadingFileContent: readonly(isLoadingFileContent)
1555
+ isLoadingCommits,
1556
+ isLoadingDiff,
1557
+ isLoadingFileDiff,
1558
+ isLoadingFileContent
1442
1559
  };
1443
1560
  }
1444
1561
  const _sfc_main$c = /* @__PURE__ */ defineComponent({
@@ -1460,6 +1577,8 @@ const _sfc_main$c = /* @__PURE__ */ defineComponent({
1460
1577
  const error = ref(null);
1461
1578
  const fileDiffError = ref(null);
1462
1579
  const viewMode = ref("changes");
1580
+ const diffRequestManager = createLatestRequestManager();
1581
+ const fileRequestManager = createLatestRequestManager();
1463
1582
  function getErrorMessage(error2, fallback) {
1464
1583
  if (error2 && typeof error2 === "object") {
1465
1584
  const fetchError = error2;
@@ -1468,72 +1587,124 @@ const _sfc_main$c = /* @__PURE__ */ defineComponent({
1468
1587
  return fallback;
1469
1588
  }
1470
1589
  const selectedFileDiff = computed(() => files.value.find((f) => f.path === selectedFile.value));
1471
- async function loadDiff() {
1590
+ const diffViewerKey = computed(() => `${props.repoPath || ""}:${props.commitSha}:${selectedFile.value || ""}`);
1591
+ function resetDiffState() {
1472
1592
  error.value = null;
1473
1593
  files.value = [];
1474
1594
  selectedFile.value = void 0;
1475
1595
  hunks.value = [];
1476
1596
  fileContent.value = null;
1597
+ fileDiffError.value = null;
1598
+ }
1599
+ async function loadDiff() {
1600
+ const requestRepoId = props.repoId;
1601
+ const requestCommitSha = props.commitSha;
1602
+ const requestRepoPath = props.repoPath;
1603
+ const ticket = diffRequestManager.begin();
1604
+ fileRequestManager.cancel();
1605
+ resetDiffState();
1477
1606
  try {
1478
- const result = await fetchDiff(props.repoId, props.commitSha, props.repoPath);
1607
+ const result = await fetchDiff(requestRepoId, requestCommitSha, {
1608
+ repoPath: requestRepoPath,
1609
+ signal: ticket.signal,
1610
+ suppressError: true
1611
+ });
1612
+ const isCurrentRequest = ticket.isCurrent() && props.repoId === requestRepoId && props.commitSha === requestCommitSha && props.repoPath === requestRepoPath;
1613
+ if (!isCurrentRequest) {
1614
+ return;
1615
+ }
1479
1616
  files.value = result;
1480
1617
  if (result.length > 0) {
1481
1618
  selectedFile.value = result[0].path;
1482
1619
  }
1483
1620
  } catch (loadError) {
1621
+ if (isAbortError(loadError) || !ticket.isCurrent()) {
1622
+ return;
1623
+ }
1484
1624
  error.value = getErrorMessage(loadError, "Could not load commit diff.");
1625
+ } finally {
1626
+ diffRequestManager.clear(ticket);
1485
1627
  }
1486
1628
  }
1487
1629
  async function loadFileDiff() {
1488
- if (!selectedFile.value) {
1630
+ const selectedPath = selectedFile.value;
1631
+ if (!selectedPath) {
1632
+ fileDiffError.value = null;
1489
1633
  hunks.value = [];
1490
1634
  fileContent.value = null;
1491
1635
  return;
1492
1636
  }
1637
+ const requestRepoId = props.repoId;
1638
+ const requestCommitSha = props.commitSha;
1639
+ const requestRepoPath = props.repoPath;
1640
+ const requestMode = viewMode.value;
1641
+ const requestPath = selectedPath;
1642
+ const ticket = fileRequestManager.begin();
1493
1643
  fileDiffError.value = null;
1644
+ hunks.value = [];
1645
+ fileContent.value = null;
1646
+ const isCurrentRequest = () => {
1647
+ return ticket.isCurrent() && props.repoId === requestRepoId && props.commitSha === requestCommitSha && props.repoPath === requestRepoPath && viewMode.value === requestMode && selectedFile.value === requestPath;
1648
+ };
1494
1649
  try {
1495
- const result = await fetchFileDiff(props.repoId, props.commitSha, selectedFile.value, props.repoPath);
1496
- hunks.value = result;
1497
- if (viewMode.value === "full") {
1498
- const content = await fetchFileContent(props.repoId, props.commitSha, selectedFile.value, props.repoPath);
1650
+ const diffPromise = fetchFileDiff(requestRepoId, requestCommitSha, requestPath, {
1651
+ repoPath: requestRepoPath,
1652
+ signal: ticket.signal,
1653
+ suppressError: true
1654
+ });
1655
+ if (requestMode === "full") {
1656
+ const [result, content] = await Promise.all([
1657
+ diffPromise,
1658
+ fetchFileContent(requestRepoId, requestCommitSha, requestPath, {
1659
+ repoPath: requestRepoPath,
1660
+ signal: ticket.signal,
1661
+ suppressError: true
1662
+ })
1663
+ ]);
1664
+ if (!isCurrentRequest()) {
1665
+ return;
1666
+ }
1667
+ hunks.value = result;
1499
1668
  fileContent.value = content;
1500
1669
  } else {
1670
+ const result = await diffPromise;
1671
+ if (!isCurrentRequest()) {
1672
+ return;
1673
+ }
1674
+ hunks.value = result;
1501
1675
  fileContent.value = null;
1502
1676
  }
1503
1677
  } catch (loadError) {
1678
+ if (!isCurrentRequest() || isAbortError(loadError)) {
1679
+ return;
1680
+ }
1504
1681
  fileDiffError.value = getErrorMessage(loadError, "Could not load file diff.");
1682
+ } finally {
1683
+ fileRequestManager.clear(ticket);
1505
1684
  }
1506
1685
  }
1507
1686
  function toggleViewMode() {
1508
1687
  viewMode.value = viewMode.value === "changes" ? "full" : "changes";
1509
1688
  }
1510
- watch(viewMode, async (mode) => {
1511
- if (mode === "full" && selectedFile.value && !fileContent.value) {
1512
- try {
1513
- const content = await fetchFileContent(props.repoId, props.commitSha, selectedFile.value, props.repoPath);
1514
- fileContent.value = content;
1515
- fileDiffError.value = null;
1516
- } catch (loadError) {
1517
- fileDiffError.value = getErrorMessage(loadError, "Could not load file content.");
1518
- }
1519
- }
1520
- });
1521
1689
  function handleFileSelect(path) {
1522
1690
  selectedFile.value = path;
1523
1691
  }
1524
1692
  function retry() {
1525
- loadDiff();
1693
+ void loadDiff();
1526
1694
  }
1527
1695
  function retryFileDiff() {
1528
- loadFileDiff();
1696
+ void loadFileDiff();
1529
1697
  }
1530
- watch(selectedFile, () => {
1531
- loadFileDiff();
1532
- });
1698
+ watch(
1699
+ () => [selectedFile.value, viewMode.value],
1700
+ () => {
1701
+ void loadFileDiff();
1702
+ }
1703
+ );
1533
1704
  watch(
1534
1705
  () => [props.repoId, props.commitSha, props.repoPath],
1535
1706
  () => {
1536
- loadDiff();
1707
+ void loadDiff();
1537
1708
  },
1538
1709
  { immediate: true }
1539
1710
  );
@@ -1895,6 +2066,7 @@ const _sfc_main$c = /* @__PURE__ */ defineComponent({
1895
2066
  if (_push2) {
1896
2067
  _push2(`<div${_scopeId}>`);
1897
2068
  _push2(ssrRenderComponent(_component_GitDiffViewer, {
2069
+ key: unref(diffViewerKey),
1898
2070
  hunks: unref(hunks),
1899
2071
  "file-path": unref(selectedFile),
1900
2072
  binary: unref(selectedFileDiff)?.binary,
@@ -1910,7 +2082,8 @@ const _sfc_main$c = /* @__PURE__ */ defineComponent({
1910
2082
  ref_key: "diffViewerRef",
1911
2083
  ref: diffViewerRef
1912
2084
  }, [
1913
- createVNode(_component_GitDiffViewer, {
2085
+ (openBlock(), createBlock(_component_GitDiffViewer, {
2086
+ key: unref(diffViewerKey),
1914
2087
  hunks: unref(hunks),
1915
2088
  "file-path": unref(selectedFile),
1916
2089
  binary: unref(selectedFileDiff)?.binary,
@@ -1918,7 +2091,7 @@ const _sfc_main$c = /* @__PURE__ */ defineComponent({
1918
2091
  "file-content": unref(fileContent),
1919
2092
  "show-full-file": unref(viewMode) === "full",
1920
2093
  "is-loading-content": unref(isLoadingFileContent)
1921
- }, null, 8, ["hunks", "file-path", "binary", "old-path", "file-content", "show-full-file", "is-loading-content"])
2094
+ }, null, 8, ["hunks", "file-path", "binary", "old-path", "file-content", "show-full-file", "is-loading-content"]))
1922
2095
  ], 512)
1923
2096
  ];
1924
2097
  }
@@ -1973,9 +2146,14 @@ const _sfc_main$b = /* @__PURE__ */ defineComponent({
1973
2146
  const repos = new Set(props.commits.map((c) => c.repo).filter(Boolean));
1974
2147
  return repos.size > 1;
1975
2148
  });
2149
+ const commitsRequestManager = createLatestRequestManager();
1976
2150
  watch(
1977
2151
  () => ({ commits: props.commits, repoId: props.repoId }),
1978
- async ({ commits, repoId }) => {
2152
+ async ({ commits, repoId }, _, onCleanup) => {
2153
+ const ticket = commitsRequestManager.begin();
2154
+ onCleanup(() => {
2155
+ commitsRequestManager.clear(ticket);
2156
+ });
1979
2157
  if (commits.length === 0 || !repoId) {
1980
2158
  commitDetails.value = /* @__PURE__ */ new Map();
1981
2159
  failedCommits.value = /* @__PURE__ */ new Set();
@@ -1991,7 +2169,10 @@ const _sfc_main$b = /* @__PURE__ */ defineComponent({
1991
2169
  }
1992
2170
  const results = await Promise.all(
1993
2171
  Array.from(commitsByRepo.entries()).map(async ([repoPath, shas]) => {
1994
- const result = await fetchCommits(repoId, shas, repoPath || void 0);
2172
+ const result = await fetchCommits(repoId, shas, {
2173
+ repoPath: repoPath || void 0,
2174
+ signal: ticket.signal
2175
+ });
1995
2176
  return {
1996
2177
  repoPath,
1997
2178
  requestedShas: [...shas],
@@ -2023,6 +2204,9 @@ const _sfc_main$b = /* @__PURE__ */ defineComponent({
2023
2204
  failed.add(createCommitKey(sha, repoPath));
2024
2205
  }
2025
2206
  }
2207
+ if (!ticket.isCurrent()) {
2208
+ return;
2209
+ }
2026
2210
  commitDetails.value = detailsMap;
2027
2211
  failedCommits.value = failed;
2028
2212
  },
@@ -3541,4 +3725,4 @@ _sfc_main.setup = (props, ctx) => {
3541
3725
  const __nuxt_component_1 = Object.assign(_sfc_main, { __name: "TasksDetail" });
3542
3726
 
3543
3727
  export { Badge as B, __nuxt_component_0$2 as _, __nuxt_component_1 as a };
3544
- //# sourceMappingURL=Detail-BQSkP9Zm.mjs.map
3728
+ //# sourceMappingURL=Detail-B7yBNjgp.mjs.map