react-terminal-viewer-cicd 3.0.0-beta.42 → 3.0.0-beta.43

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.
@@ -1,7 +1,7 @@
1
1
  function LogWorker() {
2
2
  var workerPath = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '/worker';
3
3
  var worker = null;
4
- var version = "3.0.0-beta.42" || '0.0.0';
4
+ var version = "3.0.0-beta.43" || '0.0.0';
5
5
  var path = workerPath.includes('http') ? "".concat(workerPath, "/log.worker.js") : "".concat(window.location.origin).concat(workerPath, "/log.worker.js");
6
6
  var blob = new Blob(["importScripts(\"".concat(path, "?v=").concat(version, "\")")], {
7
7
  type: 'application/javascript'
@@ -1 +1 @@
1
- {"version":3,"file":"PipelineLogViewer.d.ts","sourceRoot":"","sources":["../../../src/PipelineLogViewer/PipelineLogViewer.tsx"],"names":[],"mappings":"AAoBA,OAAO,KASN,MAAM,OAAO,CAAC;AAef,OAAO,cAAc,CAAC;AACtB,OAAO,KAAK,EAGV,sBAAsB,EACtB,oBAAoB,EACrB,MAAM,SAAS,CAAC;AAuEjB,QAAA,MAAM,iBAAiB,qGAotCtB,CAAC;AAIF,eAAe,iBAAiB,CAAC"}
1
+ {"version":3,"file":"PipelineLogViewer.d.ts","sourceRoot":"","sources":["../../../src/PipelineLogViewer/PipelineLogViewer.tsx"],"names":[],"mappings":"AAoBA,OAAO,KASN,MAAM,OAAO,CAAC;AAef,OAAO,cAAc,CAAC;AACtB,OAAO,KAAK,EAGV,sBAAsB,EACtB,oBAAoB,EACrB,MAAM,SAAS,CAAC;AAwEjB,QAAA,MAAM,iBAAiB,qGAoxCtB,CAAC;AAIF,eAAe,iBAAiB,CAAC"}
@@ -44,6 +44,7 @@ import useLogSearch from "./hooks/useLogSearch";
44
44
  import useStickyHeader from "./hooks/useStickyHeader";
45
45
  import "./index.less";
46
46
  import { getGlobalVisibleLineNumberLabel } from "./utils/groupLineNumber";
47
+ import normalizeGroupsForPipelineStatus from "./utils/normalizeGroupsForPipelineStatus";
47
48
  import { applyBoundaryScrollCompensation } from "./utils/virtuosoBoundaryScroll";
48
49
  import { firstVisibleIndexFromRenderedItems, lastVisibleIndexFromRenderedItems } from "./utils/virtuosoVisibleItems";
49
50
 
@@ -156,14 +157,14 @@ var PipelineLogViewer = /*#__PURE__*/forwardRef(function (_ref2, ref) {
156
157
  case 2:
157
158
  groupData = _context.sent;
158
159
  if (groupData && groupData.length > 0) {
159
- setGroups(groupData);
160
+ setGroups(normalizeGroupsForPipelineStatus(groupData, status));
160
161
  }
161
162
  case 4:
162
163
  case "end":
163
164
  return _context.stop();
164
165
  }
165
166
  }, _callee);
166
- })), [fetchGroups]);
167
+ })), [fetchGroups, status]);
167
168
 
168
169
  // ===================================================================
169
170
  // 二、日志数据获取与游标管理
@@ -221,6 +222,8 @@ var PipelineLogViewer = /*#__PURE__*/forwardRef(function (_ref2, ref) {
221
222
  ensureGroupExpanded: groupManager.ensureGroupExpanded,
222
223
  expandAllGroups: groupManager.expandAll
223
224
  });
225
+ var searchStateRef = useRef(logSearchResult.searchState);
226
+ searchStateRef.current = logSearchResult.searchState;
224
227
 
225
228
  // ===================================================================
226
229
  // 五、展示数据计算 (折叠过滤)
@@ -237,11 +240,12 @@ var PipelineLogViewer = /*#__PURE__*/forwardRef(function (_ref2, ref) {
237
240
 
238
241
  /**
239
242
  * 有搜索关键字时禁用 followOutput、并阻止 running 下 currentEndRow 触发的兜底置底。
240
- * 必须在 handleSearch await 之前同步置 true:setSearchState 尚未提交时 ref 若仍为 false,
243
+ * 首次 Enter 搜索 await searchLogs 期间 searchState 尚未写入 keyword,仅靠 keyword 会为 false,
241
244
  * 轮询增大 currentEndRow 会误触 useLayoutEffect 的 scrollToIndex(LAST),导致刚跳转的命中行被「跳走」。
245
+ * 合并 searching:useLogSearch 在请求期间保持 searching=true,与 keyword 二选一即可顶住该窗口。
242
246
  */
243
247
  var searchKeywordActiveRef = useRef(false);
244
- searchKeywordActiveRef.current = Boolean((_logSearchResult$sear = logSearchResult.searchState.keyword) === null || _logSearchResult$sear === void 0 ? void 0 : _logSearchResult$sear.trim());
248
+ searchKeywordActiveRef.current = Boolean((_logSearchResult$sear = logSearchResult.searchState.keyword) === null || _logSearchResult$sear === void 0 ? void 0 : _logSearchResult$sear.trim()) || logSearchResult.searching;
245
249
 
246
250
  // ===================================================================
247
251
  // 待滚动行号(pending scroll)
@@ -292,6 +296,12 @@ var PipelineLogViewer = /*#__PURE__*/forwardRef(function (_ref2, ref) {
292
296
  // 用户主动停止跟随:分组操作、置顶等交互后持续抑制自动滚动与 poll,
293
297
  // 仅 scrollToBottom 时清除,恢复跟随。
294
298
  var userStoppedFollowingRef = useRef(false);
299
+ /**
300
+ * running→结束态瞬间:若仍在底部且未停跟,则结束后继续 followOutput / 兜底置底(衔接 drain 拉尾)。
301
+ * 再次进入 running 或切换数据源时清除。
302
+ */
303
+ var tailFollowAfterRunRef = useRef(false);
304
+ var prevStatusForTailFollowRef = useRef(status);
295
305
  // 正在空降加载的分组(可多组排队尝试,但 fetch 层同一时刻只跑一个 airdrop)
296
306
  var _useState3 = useState([]),
297
307
  _useState4 = _slicedToArray(_useState3, 2),
@@ -342,7 +352,7 @@ var PipelineLogViewer = /*#__PURE__*/forwardRef(function (_ref2, ref) {
342
352
  initializedRef.current = true;
343
353
  Promise.all([fetchGroups().then(function (groupData) {
344
354
  if (groupData && groupData.length > 0) {
345
- setGroups(groupData);
355
+ setGroups(normalizeGroupsForPipelineStatus(groupData, status));
346
356
  }
347
357
  }), initLoadRef.current()]).then(function () {
348
358
  ignoreBoundaryFetchRef.current = Date.now() + 500;
@@ -355,7 +365,7 @@ var PipelineLogViewer = /*#__PURE__*/forwardRef(function (_ref2, ref) {
355
365
  });
356
366
  });
357
367
  });
358
- }, [fetchLogs, fetchGroups]);
368
+ }, [fetchLogs, fetchGroups, status]);
359
369
 
360
370
  // fetchLogs 引用变化 = 新日志源(如外部 logUrl 切换):清空状态并允许首屏 init 重新跑
361
371
  /* eslint-disable react-hooks/exhaustive-deps -- 仅响应 fetchLogs 引用变化;reset/clearSearch/expandAll 为稳定回调 */
@@ -370,6 +380,8 @@ var PipelineLogViewer = /*#__PURE__*/forwardRef(function (_ref2, ref) {
370
380
  airdropExpandInFlightRef.current.clear();
371
381
  initializedRef.current = false;
372
382
  userStoppedFollowingRef.current = false;
383
+ tailFollowAfterRunRef.current = false;
384
+ prevStatusForTailFollowRef.current = status;
373
385
  collapseAirdropActiveRef.current = false;
374
386
  isAtBottomRef.current = true;
375
387
  pendingScrollRowRef.current = null;
@@ -378,7 +390,7 @@ var PipelineLogViewer = /*#__PURE__*/forwardRef(function (_ref2, ref) {
378
390
  pendingScrollAfterPrevRef.current = null;
379
391
  pendingScrollAfterNextRef.current = null;
380
392
  ignoreBoundaryFetchRef.current = Date.now() + 500;
381
- }, [fetchLogs, logFetcherResult.reset, groupManager.expandAll, logSearchResult.clearSearch]);
393
+ }, [fetchLogs, logFetcherResult.reset, groupManager.expandAll, logSearchResult.clearSearch, status]);
382
394
  /* eslint-enable react-hooks/exhaustive-deps */
383
395
 
384
396
  // ===================================================================
@@ -596,21 +608,35 @@ var PipelineLogViewer = /*#__PURE__*/forwardRef(function (_ref2, ref) {
596
608
  // startReached / endReached 第二次以后不再触发。
597
609
  // ===================================================================
598
610
  var handleRangeChanged = useCallback(function (range) {
599
- var _el$scrollTop2;
611
+ var _el$scrollTop2, _el$clientHeight2, _topIdx, _bottomIdx;
600
612
  lastRangeChangedRef.current = range;
613
+ var prevStartIndex = currentStartIndexRef.current;
601
614
  currentStartIndexRef.current = range.startIndex;
615
+ var scrollDirection = 'none';
616
+ if (range.startIndex < prevStartIndex) {
617
+ scrollDirection = 'up';
618
+ } else if (range.startIndex > prevStartIndex) {
619
+ scrollDirection = 'down';
620
+ }
602
621
  var currentDisplayLogs = displayLogsRef.current;
603
622
  // 吸顶:在 rangeChanged 内用当前 scrollTop + 已渲染项算视口顶行,避免先于 itemsRendered
604
623
  // 时 ref 滞后,且排除 overscan 渲染在视口上方的项(不用 range.startIndex 当顶行)。
605
624
  var el = scrollerDomRef.current;
606
625
  var viewportScrollTop = (_el$scrollTop2 = el === null || el === void 0 ? void 0 : el.scrollTop) !== null && _el$scrollTop2 !== void 0 ? _el$scrollTop2 : 0;
626
+ var viewportHeight = (_el$clientHeight2 = el === null || el === void 0 ? void 0 : el.clientHeight) !== null && _el$clientHeight2 !== void 0 ? _el$clientHeight2 : 0;
607
627
  var items = lastRenderedItemsRef.current;
608
628
  var topIdx = items.length ? firstVisibleIndexFromRenderedItems(items, viewportScrollTop) : null;
629
+ var bottomIdx = items.length ? lastVisibleIndexFromRenderedItems(items, viewportScrollTop, viewportHeight) : null;
609
630
  if (topIdx !== null) {
610
631
  firstVisibleDisplayIndexRef.current = topIdx;
611
632
  } else if (currentDisplayLogs.length > 0) {
612
633
  topIdx = Math.max(0, Math.min(range.startIndex, currentDisplayLogs.length - 1));
613
634
  }
635
+ if (bottomIdx !== null) {
636
+ lastVisibleDisplayIndexRef.current = bottomIdx;
637
+ } else if (currentDisplayLogs.length > 0) {
638
+ bottomIdx = Math.max(0, Math.min(range.endIndex, currentDisplayLogs.length - 1));
639
+ }
614
640
  var topLine = topIdx !== null ? currentDisplayLogs[topIdx] : undefined;
615
641
  var absoluteTop = topLine === null || topLine === void 0 ? void 0 : topLine.absoluteRow;
616
642
  var bufferEnd = currentDisplayLogs.length > 0 ? currentDisplayLogs[currentDisplayLogs.length - 1].absoluteRow : logFetcherResult.cursor.currentStartRow;
@@ -620,14 +646,16 @@ var PipelineLogViewer = /*#__PURE__*/forwardRef(function (_ref2, ref) {
620
646
 
621
647
  // 程序触发滚动(如初始化、搜索跨页)时,短暂屏蔽 rangeChanged 的误触发加载
622
648
  if (Date.now() < ignoreBoundaryFetchRef.current) return;
649
+ if (currentDisplayLogs.length === 0) return;
650
+ var topThreshold = BOUNDARY_PREFETCH_INDEX_MARGIN;
651
+ var bottomThreshold = Math.max(0, currentDisplayLogs.length - 1 - BOUNDARY_PREFETCH_INDEX_MARGIN);
652
+ var nearTop = ((_topIdx = topIdx) !== null && _topIdx !== void 0 ? _topIdx : range.startIndex) <= topThreshold;
653
+ var nearBottom = ((_bottomIdx = bottomIdx) !== null && _bottomIdx !== void 0 ? _bottomIdx : range.endIndex) >= bottomThreshold;
623
654
 
624
- // 边界检测:触顶 加载历史(见 constants BOUNDARY_PREFETCH_INDEX_MARGIN)
625
- if (range.startIndex <= BOUNDARY_PREFETCH_INDEX_MARGIN && !logFetcherResult.boundaryLock.isFirstPage) {
655
+ // 边界检测(可见索引 + 方向感知 + 互斥触发):触顶 → 加载历史
656
+ if ((scrollDirection === 'up' || scrollDirection === 'none') && nearTop && !logFetcherResult.boundaryLock.isFirstPage) {
626
657
  handleStartReached();
627
- }
628
-
629
- // 边界检测:触底 → 加载更新
630
- if (range.endIndex >= currentDisplayLogs.length - 1 - BOUNDARY_PREFETCH_INDEX_MARGIN && !logFetcherResult.boundaryLock.isLastPage) {
658
+ } else if ((scrollDirection === 'down' || scrollDirection === 'none') && nearBottom && !logFetcherResult.boundaryLock.isLastPage) {
631
659
  handleEndReached();
632
660
  }
633
661
  }, [logFetcherResult.boundaryLock.isFirstPage, logFetcherResult.boundaryLock.isLastPage, logFetcherResult.cursor.currentStartRow, stickyHeader, handleStartReached, handleEndReached]);
@@ -666,12 +694,25 @@ var PipelineLogViewer = /*#__PURE__*/forwardRef(function (_ref2, ref) {
666
694
  // 避免依赖 isUserBrowsingHistory React 状态的异步更新导致跳帧抖动
667
695
  var statusRef = useRef(status);
668
696
  statusRef.current = status;
697
+ useLayoutEffect(function () {
698
+ var prev = prevStatusForTailFollowRef.current;
699
+ prevStatusForTailFollowRef.current = status;
700
+ if (prev === 'running' && status !== 'running') {
701
+ if (isAtBottomRef.current && !userStoppedFollowingRef.current) {
702
+ tailFollowAfterRunRef.current = true;
703
+ }
704
+ }
705
+ if (status === 'running') {
706
+ tailFollowAfterRunRef.current = false;
707
+ }
708
+ }, [status]);
669
709
  var handleFollowOutput = useCallback(function (isAtBottom) {
670
- if (statusRef.current !== 'running') return false;
671
710
  if (searchKeywordActiveRef.current) return false;
672
711
  if (groupActionRef.current) return false;
673
712
  if (collapseAirdropActiveRef.current) return false;
674
713
  if (userStoppedFollowingRef.current) return false;
714
+ var running = statusRef.current === 'running';
715
+ if (!running && !tailFollowAfterRunRef.current) return false;
675
716
  return isAtBottom ? 'auto' : false;
676
717
  }, []);
677
718
  var currentEndRow = logFetcherResult.cursor.currentEndRow;
@@ -682,7 +723,8 @@ var PipelineLogViewer = /*#__PURE__*/forwardRef(function (_ref2, ref) {
682
723
 
683
724
  // 仅在有新行到达时处理
684
725
  if (currentEndRow <= prevEndRow) return;
685
- if (status !== 'running') return;
726
+ var followNewRows = status === 'running' || tailFollowAfterRunRef.current;
727
+ if (!followNewRows) return;
686
728
  if (searchKeywordActiveRef.current) return;
687
729
  if (groupActionRef.current) return;
688
730
  if (collapseAirdropActiveRef.current) return;
@@ -691,7 +733,9 @@ var PipelineLogViewer = /*#__PURE__*/forwardRef(function (_ref2, ref) {
691
733
  requestAnimationFrame(function () {
692
734
  var _virtuosoRef$current4;
693
735
  // 在 rAF 回调执行时再次检查,防止在排队期间用户执行了置顶/搜索/折叠等操作(产生竞态被强行拉回底部)
694
- if (statusRef.current !== 'running') return;
736
+ if (statusRef.current !== 'running' && !tailFollowAfterRunRef.current) {
737
+ return;
738
+ }
695
739
  if (searchKeywordActiveRef.current) return;
696
740
  if (groupActionRef.current) return;
697
741
  if (collapseAirdropActiveRef.current) return;
@@ -744,16 +788,22 @@ var PipelineLogViewer = /*#__PURE__*/forwardRef(function (_ref2, ref) {
744
788
  return _regeneratorRuntime().wrap(function _callee2$(_context2) {
745
789
  while (1) switch (_context2.prev = _context2.next) {
746
790
  case 0:
747
- _context2.next = 2;
791
+ // 查询跳转优先级最高:取消边界 loadPrev/loadNext 的待执行滚动补偿,避免命中行到位后被拉回底部
792
+ pendingScrollAfterPrevRef.current = null;
793
+ pendingScrollAfterNextRef.current = null;
794
+ displayLogsLenBeforeLoadPrevRef.current = null;
795
+ displayLogsLenBeforeLoadNextRef.current = null;
796
+ ignoreBoundaryFetchRef.current = Math.max(ignoreBoundaryFetchRef.current, Date.now() + 1500);
797
+ _context2.next = 7;
748
798
  return logSearchResult.resolveTargetRow(absoluteRow);
749
- case 2:
799
+ case 7:
750
800
  targetRow = _context2.sent;
751
801
  if (!(targetRow === null)) {
752
- _context2.next = 5;
802
+ _context2.next = 10;
753
803
  break;
754
804
  }
755
805
  return _context2.abrupt("return");
756
- case 5:
806
+ case 10:
757
807
  currentDisplayLogs = displayLogsRef.current;
758
808
  displayIndex = currentDisplayLogs.findIndex(function (line) {
759
809
  return line.absoluteRow === targetRow;
@@ -766,7 +816,7 @@ var PipelineLogViewer = /*#__PURE__*/forwardRef(function (_ref2, ref) {
766
816
  pendingScrollAlignRef.current = 'center';
767
817
  pendingScrollRowRef.current = targetRow;
768
818
  }
769
- case 8:
819
+ case 13:
770
820
  case "end":
771
821
  return _context2.stop();
772
822
  }
@@ -781,7 +831,6 @@ var PipelineLogViewer = /*#__PURE__*/forwardRef(function (_ref2, ref) {
781
831
  var handleSearchPrev = useCallback(function () {
782
832
  var targetRow = logSearchResult.searchPrev();
783
833
  if (targetRow !== null) {
784
- searchKeywordActiveRef.current = true;
785
834
  scrollToAbsoluteRow(targetRow);
786
835
  }
787
836
  }, [logSearchResult, scrollToAbsoluteRow]);
@@ -790,7 +839,6 @@ var PipelineLogViewer = /*#__PURE__*/forwardRef(function (_ref2, ref) {
790
839
  var handleSearchNext = useCallback(function () {
791
840
  var targetRow = logSearchResult.searchNext();
792
841
  if (targetRow !== null) {
793
- searchKeywordActiveRef.current = true;
794
842
  scrollToAbsoluteRow(targetRow);
795
843
  }
796
844
  }, [logSearchResult, scrollToAbsoluteRow]);
@@ -802,17 +850,14 @@ var PipelineLogViewer = /*#__PURE__*/forwardRef(function (_ref2, ref) {
802
850
  return _regeneratorRuntime().wrap(function _callee3$(_context3) {
803
851
  while (1) switch (_context3.prev = _context3.next) {
804
852
  case 0:
805
- if (keyword.trim()) {
806
- searchKeywordActiveRef.current = true;
807
- }
808
- _context3.next = 3;
853
+ _context3.next = 2;
809
854
  return logSearchResult.search(keyword);
810
- case 3:
855
+ case 2:
811
856
  targetRow = _context3.sent;
812
857
  if (targetRow !== null) {
813
858
  scrollToAbsoluteRow(targetRow);
814
859
  }
815
- case 5:
860
+ case 4:
816
861
  case "end":
817
862
  return _context3.stop();
818
863
  }
@@ -1134,8 +1179,6 @@ var PipelineLogViewer = /*#__PURE__*/forwardRef(function (_ref2, ref) {
1134
1179
  groupManagerRef.current = groupManager;
1135
1180
  var handleToggleGroupRef = useRef(handleToggleGroup);
1136
1181
  handleToggleGroupRef.current = handleToggleGroup;
1137
- var searchStateRef = useRef(logSearchResult.searchState);
1138
- searchStateRef.current = logSearchResult.searchState;
1139
1182
  var lineNumberWidthRef = useRef(lineNumberWidth);
1140
1183
  lineNumberWidthRef.current = lineNumberWidth;
1141
1184
  var controlMarkerRowsRef = useRef(controlMarkerRows);
@@ -1343,7 +1386,7 @@ var PipelineLogViewer = /*#__PURE__*/forwardRef(function (_ref2, ref) {
1343
1386
  searchNext: handleSearchNext,
1344
1387
  clearSearch: logSearchResult.clearSearch
1345
1388
  };
1346
- }, [handleCollapseAll, handleExpandAll, handleSearch, handleSearchPrev, handleSearchNext, logSearchResult, logFetcherResult, groupManager, groups, status]);
1389
+ }, [handleCollapseAll, handleExpandAll, handleSearch, handleSearchPrev, handleSearchNext, logSearchResult, logFetcherResult, groupManager, groups, status, waitForFetchUnlock]);
1347
1390
  var lastGroupStatus = useMemo(function () {
1348
1391
  return groups.length > 0 ? groups[groups.length - 1].status : status;
1349
1392
  }, [groups, status]);
@@ -1 +1 @@
1
- {"version":3,"file":"useLogFetcher.d.ts","sourceRoot":"","sources":["../../../../src/PipelineLogViewer/hooks/useLogFetcher.ts"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EACV,YAAY,EACZ,cAAc,EACd,SAAS,EACT,aAAa,EACb,QAAQ,EACR,iBAAiB,EAClB,MAAM,UAAU,CAAC;AAElB,UAAU,oBAAoB;IAC5B,MAAM,EAAE,iBAAiB,CAAC;IAC1B,SAAS,EAAE,CAAC,MAAM,EAAE,cAAc,KAAK,OAAO,CAAC,aAAa,CAAC,CAAC;IAC9D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;OAEG;IACH,eAAe,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACvC;AAED,wCAAwC;AACxC,MAAM,MAAM,kBAAkB,GAAG,MAAM,GAAG,YAAY,GAAG,SAAS,GAAG,OAAO,GAAG,OAAO,CAAC;AAEvF,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,kBAAkB,CAAC;CACjC;AAED,UAAU,mBAAmB;IAC3B,8BAA8B;IAC9B,IAAI,EAAE,QAAQ,EAAE,CAAC;IACjB,WAAW;IACX,MAAM,EAAE,SAAS,CAAC;IAClB,YAAY;IACZ,YAAY,EAAE,YAAY,CAAC;IAC3B,aAAa;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,4CAA4C;IAC5C,QAAQ,EAAE,CAAC,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,OAAO,CAAA;KAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5D,gDAAgD;IAChD,QAAQ,EAAE,MAAM,OAAO,CAAC,cAAc,CAAC,CAAC;IACxC,+DAA+D;IAC/D,aAAa,EAAE,MAAM,OAAO,CAAC;IAC7B,mCAAmC;IACnC,iBAAiB,EAAE,MAAM,OAAO,CAAC;IACjC,iDAAiD;IACjD,QAAQ,EAAE,CAAC,OAAO,CAAC,EAAE;QACnB,mBAAmB,CAAC,EAAE,OAAO,CAAC;KAC/B,KAAK,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,aAAa,EAAE,MAAM,CAAC;QAAC,gBAAgB,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACrF;;;OAGG;IACH,8CAA8C;IAC9C,WAAW,EAAE,CACX,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,QAAQ,GAAG,WAAW,CAAC;QAAC,MAAM,CAAC,EAAE,OAAO,CAAA;KAAE,KAC5D,OAAO,CAAC,OAAO,CAAC,CAAC;IACtB,0BAA0B;IAC1B,qBAAqB,EAAE,OAAO,CAAC;IAC/B,kBAAkB;IAClB,wBAAwB,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,IAAI,CAAC;IACjD,WAAW;IACX,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY;IACZ,kBAAkB,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,YAAY,CAAC,KAAK,IAAI,CAAC;IAC7D,mCAAmC;IACnC,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB;AAeD;;;;;;;;GAQG;AACH,iBAAS,aAAa,CAAC,OAAO,EAAE,oBAAoB,GAAG,mBAAmB,CAwezE;AAED,eAAe,aAAa,CAAC"}
1
+ {"version":3,"file":"useLogFetcher.d.ts","sourceRoot":"","sources":["../../../../src/PipelineLogViewer/hooks/useLogFetcher.ts"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EACV,YAAY,EACZ,cAAc,EACd,SAAS,EACT,aAAa,EACb,QAAQ,EACR,iBAAiB,EAClB,MAAM,UAAU,CAAC;AAElB,UAAU,oBAAoB;IAC5B,MAAM,EAAE,iBAAiB,CAAC;IAC1B,SAAS,EAAE,CAAC,MAAM,EAAE,cAAc,KAAK,OAAO,CAAC,aAAa,CAAC,CAAC;IAC9D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;OAEG;IACH,eAAe,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACvC;AAED,wCAAwC;AACxC,MAAM,MAAM,kBAAkB,GAAG,MAAM,GAAG,YAAY,GAAG,SAAS,GAAG,OAAO,GAAG,OAAO,CAAC;AAEvF,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,kBAAkB,CAAC;CACjC;AAED,UAAU,mBAAmB;IAC3B,8BAA8B;IAC9B,IAAI,EAAE,QAAQ,EAAE,CAAC;IACjB,WAAW;IACX,MAAM,EAAE,SAAS,CAAC;IAClB,YAAY;IACZ,YAAY,EAAE,YAAY,CAAC;IAC3B,aAAa;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,4CAA4C;IAC5C,QAAQ,EAAE,CAAC,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,OAAO,CAAA;KAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5D,gDAAgD;IAChD,QAAQ,EAAE,MAAM,OAAO,CAAC,cAAc,CAAC,CAAC;IACxC,+DAA+D;IAC/D,aAAa,EAAE,MAAM,OAAO,CAAC;IAC7B,mCAAmC;IACnC,iBAAiB,EAAE,MAAM,OAAO,CAAC;IACjC,iDAAiD;IACjD,QAAQ,EAAE,CAAC,OAAO,CAAC,EAAE;QACnB,mBAAmB,CAAC,EAAE,OAAO,CAAC;KAC/B,KAAK,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,aAAa,EAAE,MAAM,CAAC;QAAC,gBAAgB,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACrF;;;OAGG;IACH,8CAA8C;IAC9C,WAAW,EAAE,CACX,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,QAAQ,GAAG,WAAW,CAAC;QAAC,MAAM,CAAC,EAAE,OAAO,CAAA;KAAE,KAC5D,OAAO,CAAC,OAAO,CAAC,CAAC;IACtB,0BAA0B;IAC1B,qBAAqB,EAAE,OAAO,CAAC;IAC/B,kBAAkB;IAClB,wBAAwB,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,IAAI,CAAC;IACjD,WAAW;IACX,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY;IACZ,kBAAkB,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,YAAY,CAAC,KAAK,IAAI,CAAC;IAC7D,mCAAmC;IACnC,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB;AAeD;;;;;;;;GAQG;AACH,iBAAS,aAAa,CAAC,OAAO,EAAE,oBAAoB,GAAG,mBAAmB,CA+fzE;AAED,eAAe,aAAa,CAAC"}
@@ -86,6 +86,10 @@ function useLogFetcher(options) {
86
86
  _useState8 = _slicedToArray(_useState7, 2),
87
87
  isUserBrowsingHistory = _useState8[0],
88
88
  setIsUserBrowsingHistory = _useState8[1];
89
+ var isUserBrowsingHistoryRef = useRef(false);
90
+ useEffect(function () {
91
+ isUserBrowsingHistoryRef.current = isUserBrowsingHistory;
92
+ }, [isUserBrowsingHistory]);
89
93
 
90
94
  // 游标 & 边界锁使用 ref 避免 stale closure,同时用 state 暴露给外部
91
95
  var cursorRef = useRef({
@@ -117,7 +121,10 @@ function useLogFetcher(options) {
117
121
  var mountedRef = useRef(true);
118
122
  // 轮询定时器(setTimeout 递归,等上一次完成后再安排下一次)
119
123
  var pollTimerRef = useRef(null);
120
- /** 用于检测 running→结束态,触发一次性拉尾(轮询已停,避免漏掉结束时暴增的日志) */
124
+ /**
125
+ * 用于检测 running→结束态,或首屏即结束态,触发一次性拉尾。
126
+ * null = 尚未写入(首帧);写入后保存上一 status。
127
+ */
121
128
  var prevStatusRef = useRef(null);
122
129
 
123
130
  // ----- 辅助:同步更新游标 -----
@@ -157,6 +164,7 @@ function useLogFetcher(options) {
157
164
  isFirstPage: false,
158
165
  isLastPage: false
159
166
  });
167
+ prevStatusRef.current = null;
160
168
  }, []);
161
169
 
162
170
  // ===================================================================
@@ -607,13 +615,14 @@ function useLogFetcher(options) {
607
615
  }
608
616
  return;
609
617
  }
618
+ var cancelled = false;
610
619
  var scheduleNext = function scheduleNext() {
611
620
  pollTimerRef.current = setTimeout( /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee5() {
612
621
  var tasks;
613
622
  return _regeneratorRuntime().wrap(function _callee5$(_context5) {
614
623
  while (1) switch (_context5.prev = _context5.next) {
615
624
  case 0:
616
- if (mountedRef.current) {
625
+ if (!(cancelled || !mountedRef.current)) {
617
626
  _context5.next = 2;
618
627
  break;
619
628
  }
@@ -623,7 +632,9 @@ function useLogFetcher(options) {
623
632
  _context5.next = 7;
624
633
  break;
625
634
  }
626
- tasks = [loadNext()];
635
+ tasks = [initLoad({
636
+ silent: true
637
+ })];
627
638
  if (runningPollSync) {
628
639
  tasks.push(runningPollSync());
629
640
  }
@@ -632,7 +643,7 @@ function useLogFetcher(options) {
632
643
  console.error('[PipelineLogViewer] running poll failed:', err);
633
644
  });
634
645
  case 7:
635
- if (mountedRef.current) scheduleNext();
646
+ if (!cancelled && mountedRef.current) scheduleNext();
636
647
  case 8:
637
648
  case "end":
638
649
  return _context5.stop();
@@ -642,19 +653,20 @@ function useLogFetcher(options) {
642
653
  };
643
654
  scheduleNext();
644
655
  return function () {
656
+ cancelled = true;
645
657
  if (pollTimerRef.current) {
646
658
  clearTimeout(pollTimerRef.current);
647
659
  pollTimerRef.current = null;
648
660
  }
649
661
  };
650
- }, [status, pollInterval, loadNext, isUserBrowsingHistory, runningPollSync]);
662
+ }, [status, pollInterval, initLoad, isUserBrowsingHistory, runningPollSync]);
651
663
 
652
664
  // ===================================================================
653
665
  // running→结束态:轮询已停;waitIdle 后 fetchGroups → silent initLoad → fetchGroups;再按间隔并行 loadNext+fetchGroups 追尾部。
654
666
  // ===================================================================
655
667
  useEffect(function () {
656
668
  var prev = prevStatusRef.current;
657
- var shouldDrain = prev === 'running' && status !== 'running';
669
+ var shouldDrain = prev === 'running' && status !== 'running' || prev === null && status !== 'running';
658
670
  prevStatusRef.current = status;
659
671
  if (!shouldDrain) return;
660
672
  var cancelled = false;
@@ -691,79 +703,96 @@ function useLogFetcher(options) {
691
703
  }
692
704
  return _context6.abrupt("return");
693
705
  case 4:
706
+ console.log('>>>drain start', isUserBrowsingHistoryRef.current);
707
+ // 用户正在查阅历史时,不打断当前窗口;仅同步分组状态/图标
708
+ if (!isUserBrowsingHistoryRef.current) {
709
+ _context6.next = 12;
710
+ break;
711
+ }
712
+ console.log('>>>drain runningPollSync');
694
713
  if (!runningPollSync) {
695
- _context6.next = 7;
714
+ _context6.next = 11;
696
715
  break;
697
716
  }
698
- _context6.next = 7;
717
+ console.log('>>>drain runningPollSync running');
718
+ _context6.next = 11;
699
719
  return runningPollSync();
700
- case 7:
720
+ case 11:
721
+ return _context6.abrupt("return");
722
+ case 12:
723
+ if (!runningPollSync) {
724
+ _context6.next = 15;
725
+ break;
726
+ }
727
+ _context6.next = 15;
728
+ return runningPollSync();
729
+ case 15:
701
730
  if (!(cancelled || !mountedRef.current)) {
702
- _context6.next = 9;
731
+ _context6.next = 17;
703
732
  break;
704
733
  }
705
734
  return _context6.abrupt("return");
706
- case 9:
707
- _context6.next = 11;
735
+ case 17:
736
+ _context6.next = 19;
708
737
  return initLoad({
709
738
  silent: true
710
739
  });
711
- case 11:
740
+ case 19:
712
741
  if (!(cancelled || !mountedRef.current)) {
713
- _context6.next = 13;
742
+ _context6.next = 21;
714
743
  break;
715
744
  }
716
745
  return _context6.abrupt("return");
717
- case 13:
746
+ case 21:
718
747
  if (!runningPollSync) {
719
- _context6.next = 16;
748
+ _context6.next = 24;
720
749
  break;
721
750
  }
722
- _context6.next = 16;
751
+ _context6.next = 24;
723
752
  return runningPollSync();
724
- case 16:
753
+ case 24:
725
754
  if (!(cancelled || !mountedRef.current)) {
726
- _context6.next = 18;
755
+ _context6.next = 26;
727
756
  break;
728
757
  }
729
758
  return _context6.abrupt("return");
730
- case 18:
759
+ case 26:
731
760
  i = 0;
732
- case 19:
761
+ case 27:
733
762
  if (!(i < TERMINAL_TAIL_LOAD_NEXT_COUNT)) {
734
- _context6.next = 32;
763
+ _context6.next = 40;
735
764
  break;
736
765
  }
737
766
  if (!(cancelled || !mountedRef.current)) {
738
- _context6.next = 22;
767
+ _context6.next = 30;
739
768
  break;
740
769
  }
741
770
  return _context6.abrupt("return");
742
- case 22:
771
+ case 30:
743
772
  if (!(i > 0)) {
744
- _context6.next = 27;
773
+ _context6.next = 35;
745
774
  break;
746
775
  }
747
- _context6.next = 25;
776
+ _context6.next = 33;
748
777
  return delay(TERMINAL_TAIL_LOAD_NEXT_INTERVAL_MS);
749
- case 25:
778
+ case 33:
750
779
  if (!(cancelled || !mountedRef.current)) {
751
- _context6.next = 27;
780
+ _context6.next = 35;
752
781
  break;
753
782
  }
754
783
  return _context6.abrupt("return");
755
- case 27:
784
+ case 35:
756
785
  tailTasks = [loadNext({
757
786
  bypassLastPageGuard: true
758
787
  })];
759
788
  if (runningPollSync) {
760
789
  tailTasks.push(runningPollSync());
761
790
  }
762
- case 29:
791
+ case 37:
763
792
  i += 1;
764
- _context6.next = 19;
793
+ _context6.next = 27;
765
794
  break;
766
- case 32:
795
+ case 40:
767
796
  case "end":
768
797
  return _context6.stop();
769
798
  }
@@ -773,13 +802,14 @@ function useLogFetcher(options) {
773
802
  return _ref6.apply(this, arguments);
774
803
  };
775
804
  }();
805
+ console.log('>>>drain start', isUserBrowsingHistory);
776
806
  drain().catch(function (err) {
777
807
  console.error('[PipelineLogViewer] terminal log drain failed:', err);
778
808
  });
779
809
  return function () {
780
810
  cancelled = true;
781
811
  };
782
- }, [status, initLoad, runningPollSync, loadNext]);
812
+ }, [status, initLoad, runningPollSync, loadNext]); // eslint-disable-line react-hooks/exhaustive-deps
783
813
 
784
814
  // ===================================================================
785
815
  // 组件卸载清理
@@ -1 +1 @@
1
- {"version":3,"file":"useStickyHeader.d.ts","sourceRoot":"","sources":["../../../../src/PipelineLogViewer/hooks/useStickyHeader.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAE1C,UAAU,sBAAsB;IAC9B,WAAW;IACX,MAAM,EAAE,SAAS,EAAE,CAAC;CACrB;AAED,UAAU,qBAAqB;IAC7B,0CAA0C;IAC1C,WAAW,EAAE,SAAS,GAAG,IAAI,CAAC;IAC9B;;;;OAIG;IACH,kBAAkB,EAAE,CAAC,cAAc,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;CACzE;AAqCD;;GAEG;AACH,iBAAS,eAAe,CAAC,OAAO,EAAE,sBAAsB,GAAG,qBAAqB,CAgB/E;AAED,eAAe,eAAe,CAAC"}
1
+ {"version":3,"file":"useStickyHeader.d.ts","sourceRoot":"","sources":["../../../../src/PipelineLogViewer/hooks/useStickyHeader.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAE1C,UAAU,sBAAsB;IAC9B,WAAW;IACX,MAAM,EAAE,SAAS,EAAE,CAAC;CACrB;AAED,UAAU,qBAAqB;IAC7B,0CAA0C;IAC1C,WAAW,EAAE,SAAS,GAAG,IAAI,CAAC;IAC9B;;;;OAIG;IACH,kBAAkB,EAAE,CAAC,cAAc,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;CACzE;AAqCD;;GAEG;AACH,iBAAS,eAAe,CAAC,OAAO,EAAE,sBAAsB,GAAG,qBAAqB,CA0B/E;AAED,eAAe,eAAe,CAAC"}
@@ -14,7 +14,7 @@ function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
14
14
  // 与下一组 start 计算有效区间
15
15
  // ============================================================================
16
16
 
17
- import { useCallback, useState } from 'react';
17
+ import { useCallback, useEffect, useState } from 'react';
18
18
  /** 与 useDisplayLogs 折叠区间算法一致的有效上界(用于判断行是否落在该组) */
19
19
  function effectiveGroupEnd(group, gi, groups, bufferEnd) {
20
20
  var start = group.start,
@@ -51,6 +51,18 @@ function useStickyHeader(options) {
51
51
  _useState2 = _slicedToArray(_useState, 2),
52
52
  stickyGroup = _useState2[0],
53
53
  setStickyGroup = _useState2[1];
54
+
55
+ // 当外部的 groups 更新时,如果当前有吸顶的分组,更新其状态
56
+ useEffect(function () {
57
+ if (stickyGroup) {
58
+ var updatedGroup = groups.find(function (g) {
59
+ return g.name === stickyGroup.name;
60
+ });
61
+ if (updatedGroup && updatedGroup.status !== stickyGroup.status) {
62
+ setStickyGroup(updatedGroup);
63
+ }
64
+ }
65
+ }, [groups, stickyGroup]);
54
66
  var handleRangeChanged = useCallback(function (absoluteTopRow, bufferEnd) {
55
67
  if (groups.length === 0) {
56
68
  setStickyGroup(null);
@@ -0,0 +1,7 @@
1
+ import type { GroupData, PipelineLogStatus } from '../types';
2
+ /**
3
+ * 当流水线已非 running 时,将仍标记为 running 的分组对齐为流水线终态,
4
+ * 避免 UI 与拉取策略误判为「仍在运行」。
5
+ */
6
+ declare function normalizeGroupsForPipelineStatus(groups: GroupData[], pipelineStatus: PipelineLogStatus): GroupData[];
7
+ export default normalizeGroupsForPipelineStatus;
@@ -0,0 +1 @@
1
+ {"version":3,"file":"normalizeGroupsForPipelineStatus.d.ts","sourceRoot":"","sources":["../../../../src/PipelineLogViewer/utils/normalizeGroupsForPipelineStatus.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,SAAS,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAE7D;;;GAGG;AACH,iBAAS,gCAAgC,CACvC,MAAM,EAAE,SAAS,EAAE,EACnB,cAAc,EAAE,iBAAiB,GAChC,SAAS,EAAE,CAgBb;AAED,eAAe,gCAAgC,CAAC"}
@@ -0,0 +1,35 @@
1
+ function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
2
+ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
3
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
4
+ function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
5
+ function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); }
6
+ function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
7
+ // ============================================================================
8
+ // 分组数据与流水线状态对齐
9
+ //
10
+ // 场景:任务被快速取消/结束时,/groups 仍返回某阶段 status: running、end/endTime 为 null,
11
+ // 而流水线 props.status 已是 cancel / failure / succeed。若不纠正,展开逻辑仍按「运行中」处理,
12
+ // 吸顶/折叠区间也与「已结束」不一致。
13
+ // ============================================================================
14
+
15
+ /**
16
+ * 当流水线已非 running 时,将仍标记为 running 的分组对齐为流水线终态,
17
+ * 避免 UI 与拉取策略误判为「仍在运行」。
18
+ */
19
+ function normalizeGroupsForPipelineStatus(groups, pipelineStatus) {
20
+ if (pipelineStatus === 'running' || groups.length === 0) {
21
+ return groups;
22
+ }
23
+ return groups.map(function (g) {
24
+ var _g$endTime;
25
+ if (g.status !== 'running') {
26
+ return g;
27
+ }
28
+ return _objectSpread(_objectSpread({}, g), {}, {
29
+ status: pipelineStatus,
30
+ /** 接口未回填结束时,用开始时间占位,避免时间展示永远「进行中」 */
31
+ endTime: (_g$endTime = g.endTime) !== null && _g$endTime !== void 0 ? _g$endTime : g.startTime
32
+ });
33
+ });
34
+ }
35
+ export default normalizeGroupsForPipelineStatus;
@@ -0,0 +1,7 @@
1
+ import type { GroupData, PipelineLogStatus } from '../types';
2
+ /**
3
+ * 当流水线已非 running 时,将仍标记为 running 的分组对齐为流水线终态,
4
+ * 避免 UI 与拉取策略误判为「仍在运行」。
5
+ */
6
+ declare function normalizeGroupsForPipelineStatus(groups: GroupData[], pipelineStatus: PipelineLogStatus): GroupData[];
7
+ export default normalizeGroupsForPipelineStatus;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-terminal-viewer-cicd",
3
- "version": "3.0.0-beta.42",
3
+ "version": "3.0.0-beta.43",
4
4
  "author": "https://gitee.com/gitee-frontend",
5
5
  "license": "MIT",
6
6
  "keywords": [