react-terminal-viewer-cicd 3.0.0-beta.40 → 3.0.0-beta.41

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 (27) hide show
  1. package/dist/esm/Addon/WorkerLog/LogWorker.js +1 -1
  2. package/dist/esm/PipelineLogViewer/PipelineLogViewer.d.ts.map +1 -0
  3. package/dist/esm/PipelineLogViewer/PipelineLogViewer.js +135 -43
  4. package/dist/esm/PipelineLogViewer/components/GroupHeader.d.ts +2 -2
  5. package/dist/esm/PipelineLogViewer/components/GroupHeader.d.ts.map +1 -1
  6. package/dist/esm/PipelineLogViewer/components/GroupHeader.js +18 -5
  7. package/dist/esm/PipelineLogViewer/components/SearchBar.d.ts.map +1 -0
  8. package/dist/esm/PipelineLogViewer/components/StickyHeader.d.ts +3 -2
  9. package/dist/esm/PipelineLogViewer/components/StickyHeader.d.ts.map +1 -1
  10. package/dist/esm/PipelineLogViewer/components/StickyHeader.js +4 -2
  11. package/dist/esm/PipelineLogViewer/hooks/useDisplayLogs.d.ts +1 -1
  12. package/dist/esm/PipelineLogViewer/hooks/useDisplayLogs.d.ts.map +1 -1
  13. package/dist/esm/PipelineLogViewer/hooks/useDisplayLogs.js +62 -27
  14. package/dist/esm/PipelineLogViewer/hooks/useLogFetcher.d.ts +15 -5
  15. package/dist/esm/PipelineLogViewer/hooks/useLogFetcher.d.ts.map +1 -0
  16. package/dist/esm/PipelineLogViewer/hooks/useLogFetcher.js +236 -173
  17. package/dist/esm/PipelineLogViewer/index.less +20 -4
  18. package/dist/esm/PipelineLogViewer/types.d.ts +3 -1
  19. package/dist/esm/PipelineLogViewer/types.d.ts.map +1 -1
  20. package/dist/esm/TerminalViewerVirtualDom/HumenTime.d.ts.map +1 -1
  21. package/dist/esm/TerminalViewerVirtualDom/HumenTime.js +25 -15
  22. package/dist/worker/src/PipelineLogViewer/components/GroupHeader.d.ts +2 -2
  23. package/dist/worker/src/PipelineLogViewer/components/StickyHeader.d.ts +3 -2
  24. package/dist/worker/src/PipelineLogViewer/hooks/useDisplayLogs.d.ts +1 -1
  25. package/dist/worker/src/PipelineLogViewer/hooks/useLogFetcher.d.ts +15 -5
  26. package/dist/worker/src/PipelineLogViewer/types.d.ts +3 -1
  27. package/package.json +1 -1
@@ -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.40" || '0.0.0';
4
+ var version = "3.0.0-beta.41" || '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'
@@ -0,0 +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,qGAgrCtB,CAAC;AAIF,eAAe,iBAAiB,CAAC"}
@@ -99,6 +99,21 @@ var VirtuosoFooterWithLoading = function VirtuosoFooterWithLoading() {
99
99
  }, /*#__PURE__*/React.createElement(LoadingDots, null)));
100
100
  };
101
101
 
102
+ /** 控制日志:END_stepId_status_timestamp_stepName(用于阶段结束标识,不作为正文展示) */
103
+ var isPipelineEndMarkerText = function isPipelineEndMarkerText(text) {
104
+ if (!text) return false;
105
+ // 日志经 ansi_up 处理后可能夹带 HTML 标签,先转成纯文本再判断。
106
+ var plainText = text.replace(/<[^>]*>/g, '').trim();
107
+ return /^END_[a-zA-Z0-9]+_(running|succeed|failure|cancel)_\d+_.+/.test(plainText);
108
+ };
109
+
110
+ /** 控制日志:START_stepId_status_timestamp_stepName(用于阶段开始标识,不作为正文展示) */
111
+ var isPipelineStartMarkerText = function isPipelineStartMarkerText(text) {
112
+ if (!text) return false;
113
+ var plainText = text.replace(/<[^>]*>/g, '').trim();
114
+ return /^START_[a-zA-Z0-9]+_(running|succeed|failure|cancel)_\d+_.+/.test(plainText);
115
+ };
116
+
102
117
  // --------------------------------------------------------------------------
103
118
  // 主组件
104
119
  // --------------------------------------------------------------------------
@@ -120,7 +135,7 @@ var PipelineLogViewer = /*#__PURE__*/forwardRef(function (_ref2, ref) {
120
135
  icons = _ref2.icons,
121
136
  groupStatusIcons = _ref2.groupStatusIcons,
122
137
  onCollapsedChange = _ref2.onCollapsedChange,
123
- serverTime = _ref2.serverTime,
138
+ serverOffsetTime = _ref2.serverOffsetTime,
124
139
  highlightOptions = _ref2.highlightOptions;
125
140
  // ===================================================================
126
141
  // 一、分组数据
@@ -170,6 +185,7 @@ var PipelineLogViewer = /*#__PURE__*/forwardRef(function (_ref2, ref) {
170
185
  });
171
186
 
172
187
  // 部分折叠模式下,缓冲窗口切换后若某分组已展开但与当前窗口无交集,自动折叠(避免出现展开图标却无日志)
188
+ // 必须随 collapsedGroups 变化重跑:仅折叠某一组时游标往往不变,否则不会自动折起缓冲外的展开组。
173
189
  var collapseExpandedGroupsOutsideBuffer = groupManager.collapseExpandedGroupsOutsideBuffer;
174
190
  /* eslint-disable react-hooks/exhaustive-deps -- 仅依赖游标起止行,避免整个 cursor 引用抖动 */
175
191
  useLayoutEffect(function () {
@@ -177,7 +193,7 @@ var PipelineLogViewer = /*#__PURE__*/forwardRef(function (_ref2, ref) {
177
193
  currentStartRow = _logFetcherResult$cur.currentStartRow,
178
194
  currentEndRow = _logFetcherResult$cur.currentEndRow;
179
195
  collapseExpandedGroupsOutsideBuffer(currentStartRow, currentEndRow);
180
- }, [logFetcherResult.cursor.currentStartRow, logFetcherResult.cursor.currentEndRow, collapseExpandedGroupsOutsideBuffer]);
196
+ }, [logFetcherResult.cursor.currentStartRow, logFetcherResult.cursor.currentEndRow, collapseExpandedGroupsOutsideBuffer, groupManager.collapsedGroups]);
181
197
  /* eslint-enable react-hooks/exhaustive-deps */
182
198
 
183
199
  // 传递折叠状态给外部
@@ -257,6 +273,11 @@ var PipelineLogViewer = /*#__PURE__*/forwardRef(function (_ref2, ref) {
257
273
  var headRemovedFromNextRef = useRef(0);
258
274
  // 程序触发滚动(如初始化、搜索跨页)时,短暂屏蔽 rangeChanged 的误触发
259
275
  var ignoreBoundaryFetchRef = useRef(0);
276
+ /** 最近一次 Virtuoso rangeChanged,用于 ignore 窗口结束后补一次边界检测(快速拖条时 range 可能不再变化) */
277
+ var lastRangeChangedRef = useRef(null);
278
+ var handleRangeChangedRef = useRef(null);
279
+ var handleStartReachedRef = useRef(null);
280
+ var boundaryIgnoreFollowUpTimeoutRef = useRef(null);
260
281
  // 记录当前是否在底部。达到 maxMemoryLines 后 totalCount 可能不再增长,
261
282
  // followOutput 不一定会触发,这里用于兜底自动跟随。
262
283
  var isAtBottomRef = useRef(true);
@@ -271,13 +292,13 @@ var PipelineLogViewer = /*#__PURE__*/forwardRef(function (_ref2, ref) {
271
292
  // 用户主动停止跟随:分组操作、置顶等交互后持续抑制自动滚动与 poll,
272
293
  // 仅 scrollToBottom 时清除,恢复跟随。
273
294
  var userStoppedFollowingRef = useRef(false);
274
- // 正在加载数据的分组名(点击展开时需要 airdrop),用于在 GroupHeader 上显示行内 loading
275
- var _useState3 = useState(null),
295
+ // 正在空降加载的分组(可多组排队尝试,但 fetch 层同一时刻只跑一个 airdrop)
296
+ var _useState3 = useState([]),
276
297
  _useState4 = _slicedToArray(_useState3, 2),
277
- loadingGroupName = _useState4[0],
278
- setLoadingGroupName = _useState4[1];
279
- /** 空降展开进行中:同步占位,防止重复点击触发二次 airdrop */
280
- var airdropExpandInFlightRef = useRef(false);
298
+ loadingGroupNames = _useState4[0],
299
+ setLoadingGroupNames = _useState4[1];
300
+ /** 按分组记录空降展开进行中,仅阻止「同一分组」重复触发,避免全局锁死其它阶段 */
301
+ var airdropExpandInFlightRef = useRef(new Set());
281
302
 
282
303
  // ===================================================================
283
304
  // 六、吸顶指示器
@@ -345,7 +366,8 @@ var PipelineLogViewer = /*#__PURE__*/forwardRef(function (_ref2, ref) {
345
366
  setGroups([]);
346
367
  groupManager.expandAll();
347
368
  logSearchResult.clearSearch();
348
- setLoadingGroupName(null);
369
+ setLoadingGroupNames([]);
370
+ airdropExpandInFlightRef.current.clear();
349
371
  initializedRef.current = false;
350
372
  userStoppedFollowingRef.current = false;
351
373
  collapseAirdropActiveRef.current = false;
@@ -451,9 +473,32 @@ var PipelineLogViewer = /*#__PURE__*/forwardRef(function (_ref2, ref) {
451
473
  if (!result.applied) {
452
474
  pendingScrollAfterPrevRef.current = null;
453
475
  displayLogsLenBeforeLoadPrevRef.current = null;
476
+ // initLoad / loadNext / airdrop 占用 fetching 时 loadPrev 会同步返回 busy,但不会再触发 rangeChanged,需解锁后重试
477
+ if (result.skipReason === 'busy') {
478
+ var rafAttempts = 0;
479
+ var retryWhenUnlocked = function retryWhenUnlocked() {
480
+ rafAttempts += 1;
481
+ if (rafAttempts > 600) return;
482
+ if (logFetcherResult.isFetchLocked()) {
483
+ requestAnimationFrame(retryWhenUnlocked);
484
+ return;
485
+ }
486
+ if (logFetcherResult.isFirstPageLocked()) return;
487
+ if (boundaryFetchingRef.current) return;
488
+ var el = scrollerDomRef.current;
489
+ if (!el) return;
490
+ var topIdx = firstVisibleIndexFromRenderedItems(lastRenderedItemsRef.current, el.scrollTop);
491
+ if (topIdx !== null && topIdx <= BOUNDARY_PREFETCH_INDEX_MARGIN) {
492
+ var _handleStartReachedRe;
493
+ (_handleStartReachedRe = handleStartReachedRef.current) === null || _handleStartReachedRe === void 0 || _handleStartReachedRe.call(handleStartReachedRef);
494
+ }
495
+ };
496
+ requestAnimationFrame(retryWhenUnlocked);
497
+ }
454
498
  }
455
499
  });
456
500
  }, [logFetcherResult]);
501
+ handleStartReachedRef.current = handleStartReached;
457
502
 
458
503
  // 向下滚动触及阈值 → 加载更多新日志
459
504
  var handleEndReached = useCallback(function () {
@@ -497,6 +542,16 @@ var PipelineLogViewer = /*#__PURE__*/forwardRef(function (_ref2, ref) {
497
542
  pendingScrollAfterPrevRef.current = null;
498
543
  displayLogsLenBeforeLoadPrevRef.current = null;
499
544
  ignoreBoundaryFetchRef.current = Date.now() + 500;
545
+ if (boundaryIgnoreFollowUpTimeoutRef.current) {
546
+ clearTimeout(boundaryIgnoreFollowUpTimeoutRef.current);
547
+ }
548
+ boundaryIgnoreFollowUpTimeoutRef.current = setTimeout(function () {
549
+ boundaryIgnoreFollowUpTimeoutRef.current = null;
550
+ var r = lastRangeChangedRef.current;
551
+ if (r && handleRangeChangedRef.current) {
552
+ handleRangeChangedRef.current(r);
553
+ }
554
+ }, 520);
500
555
  applyBoundaryScrollCompensation(v, anchorRowPrev, displayLogs, {
501
556
  sameLength: sameLength && prepended > 0,
502
557
  sameLengthPixelDeltaCount: prepended,
@@ -540,6 +595,7 @@ var PipelineLogViewer = /*#__PURE__*/forwardRef(function (_ref2, ref) {
540
595
  // ===================================================================
541
596
  var handleRangeChanged = useCallback(function (range) {
542
597
  var _el$scrollTop2;
598
+ lastRangeChangedRef.current = range;
543
599
  currentStartIndexRef.current = range.startIndex;
544
600
  var currentDisplayLogs = displayLogsRef.current;
545
601
  // 吸顶:在 rangeChanged 内用当前 scrollTop + 已渲染项算视口顶行,避免先于 itemsRendered
@@ -573,6 +629,14 @@ var PipelineLogViewer = /*#__PURE__*/forwardRef(function (_ref2, ref) {
573
629
  handleEndReached();
574
630
  }
575
631
  }, [logFetcherResult.boundaryLock.isFirstPage, logFetcherResult.boundaryLock.isLastPage, logFetcherResult.cursor.currentStartRow, stickyHeader, handleStartReached, handleEndReached]);
632
+ handleRangeChangedRef.current = handleRangeChanged;
633
+ useEffect(function () {
634
+ return function () {
635
+ if (boundaryIgnoreFollowUpTimeoutRef.current) {
636
+ clearTimeout(boundaryIgnoreFollowUpTimeoutRef.current);
637
+ }
638
+ };
639
+ }, []);
576
640
 
577
641
  // 判断用户是否在浏览历史 (防打扰策略)
578
642
  var handleAtBottomStateChange = useCallback(function (atBottom) {
@@ -822,7 +886,7 @@ var PipelineLogViewer = /*#__PURE__*/forwardRef(function (_ref2, ref) {
822
886
  return g.name === name;
823
887
  });
824
888
  if (group) {
825
- if (airdropExpandInFlightRef.current) return;
889
+ if (airdropExpandInFlightRef.current.has(name)) return;
826
890
  var _logFetcherResult$cur3 = logFetcherResult.cursor,
827
891
  bufStart = _logFetcherResult$cur3.currentStartRow,
828
892
  bufEnd = _logFetcherResult$cur3.currentEndRow;
@@ -832,8 +896,10 @@ var PipelineLogViewer = /*#__PURE__*/forwardRef(function (_ref2, ref) {
832
896
  userStoppedFollowingRef.current = true;
833
897
  logFetcherResult.setIsUserBrowsingHistory(true);
834
898
  if (!groupStartInBuffer) {
835
- airdropExpandInFlightRef.current = true;
836
- setLoadingGroupName(name);
899
+ airdropExpandInFlightRef.current.add(name);
900
+ setLoadingGroupNames(function (prev) {
901
+ return prev.includes(name) ? prev : [].concat(_toConsumableArray(prev), [name]);
902
+ });
837
903
  var runAirdropThenExpand = /*#__PURE__*/function () {
838
904
  var _ref8 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee4() {
839
905
  var ok;
@@ -865,8 +931,12 @@ var PipelineLogViewer = /*#__PURE__*/forwardRef(function (_ref2, ref) {
865
931
  });
866
932
  case 7:
867
933
  _context4.prev = 7;
868
- setLoadingGroupName(null);
869
- airdropExpandInFlightRef.current = false;
934
+ airdropExpandInFlightRef.current.delete(name);
935
+ setLoadingGroupNames(function (prev) {
936
+ return prev.filter(function (n) {
937
+ return n !== name;
938
+ });
939
+ });
870
940
  return _context4.finish(7);
871
941
  case 11:
872
942
  case "end":
@@ -934,16 +1004,17 @@ var PipelineLogViewer = /*#__PURE__*/forwardRef(function (_ref2, ref) {
934
1004
  return map;
935
1005
  }, [groups]);
936
1006
 
937
- /** 各分组首尾绝对行号(用于 itemContent 内压扁日志行,不改变 displayLogs 索引) */
938
- var groupEdgeAbsoluteRows = useMemo(function () {
1007
+ /** START/END 控制行绝对行号集合(用于行号体系扣减,不参与分组边界判断) */
1008
+ var controlMarkerRows = useMemo(function () {
939
1009
  var set = new Set();
940
- var _iterator2 = _createForOfIteratorHelper(groups),
1010
+ var _iterator2 = _createForOfIteratorHelper(displayLogs),
941
1011
  _step2;
942
1012
  try {
943
1013
  for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
944
- var g = _step2.value;
945
- set.add(g.start);
946
- if (g.end != null) set.add(g.end);
1014
+ var line = _step2.value;
1015
+ if (isPipelineEndMarkerText(line.text) || isPipelineStartMarkerText(line.text)) {
1016
+ set.add(line.absoluteRow);
1017
+ }
947
1018
  }
948
1019
  } catch (err) {
949
1020
  _iterator2.e(err);
@@ -951,7 +1022,7 @@ var PipelineLogViewer = /*#__PURE__*/forwardRef(function (_ref2, ref) {
951
1022
  _iterator2.f();
952
1023
  }
953
1024
  return set;
954
- }, [groups]);
1025
+ }, [displayLogs]);
955
1026
 
956
1027
  /** 整条流水线日志起算绝对行号(用于全局连续行号) */
957
1028
  var pipelineMinAbsoluteRow = useMemo(function () {
@@ -973,8 +1044,8 @@ var PipelineLogViewer = /*#__PURE__*/forwardRef(function (_ref2, ref) {
973
1044
  searchStateRef.current = logSearchResult.searchState;
974
1045
  var lineNumberWidthRef = useRef(lineNumberWidth);
975
1046
  lineNumberWidthRef.current = lineNumberWidth;
976
- var groupEdgeAbsoluteRowsRef = useRef(groupEdgeAbsoluteRows);
977
- groupEdgeAbsoluteRowsRef.current = groupEdgeAbsoluteRows;
1047
+ var controlMarkerRowsRef = useRef(controlMarkerRows);
1048
+ controlMarkerRowsRef.current = controlMarkerRows;
978
1049
  var pipelineMinAbsoluteRowRef = useRef(pipelineMinAbsoluteRow);
979
1050
  pipelineMinAbsoluteRowRef.current = pipelineMinAbsoluteRow;
980
1051
 
@@ -984,13 +1055,13 @@ var PipelineLogViewer = /*#__PURE__*/forwardRef(function (_ref2, ref) {
984
1055
  // 但滑动窗口满载后 head-trim 导致 totalCount 不变、同 index 映射到新数据,
985
1056
  // 此时需要通过 currentStartRow 变化来强制刷新。
986
1057
  var currentStartRow = logFetcherResult.cursor.currentStartRow;
987
- var loadingGroupNameRef = useRef(loadingGroupName);
988
- loadingGroupNameRef.current = loadingGroupName;
1058
+ var loadingGroupNamesRef = useRef(loadingGroupNames);
1059
+ loadingGroupNamesRef.current = loadingGroupNames;
989
1060
  var renderVersion = useMemo(function () {
990
1061
  return {};
991
1062
  },
992
1063
  // eslint-disable-next-line react-hooks/exhaustive-deps
993
- [groupManager.collapsedGroups, logSearchResult.searchState, lineNumberWidth, currentStartRow, highlightOptions, groups, loadingGroupName]);
1064
+ [groupManager.collapsedGroups, logSearchResult.searchState, lineNumberWidth, currentStartRow, highlightOptions, groups, loadingGroupNames]);
994
1065
  var itemContent = useCallback(function (index) {
995
1066
  var _groupsForHeader;
996
1067
  var currentDisplayLogs = displayLogsRef.current;
@@ -1025,9 +1096,12 @@ var PipelineLogViewer = /*#__PURE__*/forwardRef(function (_ref2, ref) {
1025
1096
  var isMatched = currentSearchState.matchedRowsSet.has(line.absoluteRow);
1026
1097
  var activeRow = currentSearchState.currentMatchIndex >= 0 ? currentSearchState.positions[currentSearchState.currentMatchIndex] : -1;
1027
1098
  var isActiveMatch = line.absoluteRow === activeRow;
1028
- var showLogRow = !line.isFoldedGroupStart;
1029
- var collapseGroupEdgeLog = showLogRow && groupEdgeAbsoluteRowsRef.current.has(line.absoluteRow);
1030
- var lineNumberLabel = groups.length > 0 && showLogRow ? getGlobalVisibleLineNumberLabel(line.absoluteRow, groupEdgeAbsoluteRowsRef.current, pipelineMinAbsoluteRowRef.current) : undefined;
1099
+ var showLogRow = !line.isFoldedGroupStart && !line.isEmptyGroupStart;
1100
+ var isControlEndMarker = isPipelineEndMarkerText(line.text);
1101
+ var isControlStartMarker = isPipelineStartMarkerText(line.text);
1102
+ /** 仅压扁 START/END 控制行;行号体系也仅基于控制行扣减 */
1103
+ var collapseGroupEdgeLog = showLogRow && !line.isEmptyGroupStart && (isControlEndMarker || isControlStartMarker);
1104
+ var lineNumberLabel = groups.length > 0 && showLogRow ? getGlobalVisibleLineNumberLabel(line.absoluteRow, controlMarkerRowsRef.current, pipelineMinAbsoluteRowRef.current) : undefined;
1031
1105
  var logRow = /*#__PURE__*/React.createElement(LogRow, {
1032
1106
  absoluteRow: line.absoluteRow,
1033
1107
  lineNumberLabel: lineNumberLabel,
@@ -1045,10 +1119,10 @@ var PipelineLogViewer = /*#__PURE__*/forwardRef(function (_ref2, ref) {
1045
1119
  key: group.name,
1046
1120
  group: group,
1047
1121
  collapsed: currentGroupManager.collapsedGroups.has(group.name),
1048
- loading: loadingGroupNameRef.current === group.name,
1122
+ loading: loadingGroupNamesRef.current.includes(group.name),
1049
1123
  onToggle: handleToggleGroupRef.current,
1050
1124
  groupStatusIcons: groupStatusIcons,
1051
- serverTime: serverTime
1125
+ serverOffsetTime: serverOffsetTime
1052
1126
  });
1053
1127
  }), showLogRow && (collapseGroupEdgeLog ? /*#__PURE__*/React.createElement("div", {
1054
1128
  className: "pipeline-log-hidden-row"
@@ -1081,32 +1155,51 @@ var PipelineLogViewer = /*#__PURE__*/forwardRef(function (_ref2, ref) {
1081
1155
  needsTopWindow = bufStart > globalFirstRow;
1082
1156
  ignoreBoundaryFetchRef.current = Date.now() + 500;
1083
1157
  if (!needsTopWindow) {
1084
- _context5.next = 19;
1158
+ _context5.next = 22;
1085
1159
  break;
1086
1160
  }
1087
1161
  // airdropLoad 完成时 React 尚未提交新 displayLogs,单帧 rAF 里 scrollToIndex(0)
1088
1162
  // 仍作用在旧列表上(index 0 = 旧缓冲首行),无法置顶;与搜索跨页一致走 pending。
1089
1163
  pendingScrollAlignRef.current = 'start';
1090
1164
  pendingScrollRowRef.current = globalFirstRow;
1091
- _context5.next = 12;
1165
+ // 轮询 loadNext 可能恰好持有 fetchingRef 锁,等其释放后再空降
1166
+ if (!logFetcherResult.isFetchLocked()) {
1167
+ _context5.next = 13;
1168
+ break;
1169
+ }
1170
+ _context5.next = 13;
1171
+ return new Promise(function (resolve) {
1172
+ var attempts = 0;
1173
+ var check = function check() {
1174
+ attempts += 1;
1175
+ if (!logFetcherResult.isFetchLocked() || attempts > 300) {
1176
+ resolve();
1177
+ return;
1178
+ }
1179
+ requestAnimationFrame(check);
1180
+ };
1181
+ requestAnimationFrame(check);
1182
+ });
1183
+ case 13:
1184
+ _context5.next = 15;
1092
1185
  return logFetcherResult.airdropLoad(globalFirstRow, {
1093
1186
  window: 'fromStart'
1094
1187
  });
1095
- case 12:
1188
+ case 15:
1096
1189
  ok = _context5.sent;
1097
1190
  if (ok) {
1098
- _context5.next = 17;
1191
+ _context5.next = 20;
1099
1192
  break;
1100
1193
  }
1101
1194
  pendingScrollRowRef.current = null;
1102
1195
  pendingScrollAlignRef.current = 'center';
1103
1196
  return _context5.abrupt("return");
1104
- case 17:
1197
+ case 20:
1105
1198
  logFetcherResult.updateBoundaryLock({
1106
1199
  isLastPage: false
1107
1200
  });
1108
1201
  return _context5.abrupt("return");
1109
- case 19:
1202
+ case 22:
1110
1203
  requestAnimationFrame(function () {
1111
1204
  var _virtuosoRef$current6;
1112
1205
  (_virtuosoRef$current6 = virtuosoRef.current) === null || _virtuosoRef$current6 === void 0 || _virtuosoRef$current6.scrollToIndex({
@@ -1115,7 +1208,7 @@ var PipelineLogViewer = /*#__PURE__*/forwardRef(function (_ref2, ref) {
1115
1208
  behavior: 'auto'
1116
1209
  });
1117
1210
  });
1118
- case 20:
1211
+ case 23:
1119
1212
  case "end":
1120
1213
  return _context5.stop();
1121
1214
  }
@@ -1181,7 +1274,7 @@ var PipelineLogViewer = /*#__PURE__*/forwardRef(function (_ref2, ref) {
1181
1274
  var isRunning = lastGroupStatus === 'running';
1182
1275
  return {
1183
1276
  EmptyPlaceholder: logFetcherResult.loading || isRunning ? undefined : EmptyPlaceholder,
1184
- Footer: !logFetcherResult.loading && isRunning ? VirtuosoFooterWithLoading : VirtuosoFooterEmpty
1277
+ Footer: isRunning ? VirtuosoFooterWithLoading : VirtuosoFooterEmpty
1185
1278
  };
1186
1279
  }, [lastGroupStatus, logFetcherResult.loading]);
1187
1280
 
@@ -1211,9 +1304,10 @@ var PipelineLogViewer = /*#__PURE__*/forwardRef(function (_ref2, ref) {
1211
1304
  }, /*#__PURE__*/React.createElement(StickyHeader, {
1212
1305
  group: stickyHeader.stickyGroup,
1213
1306
  collapsed: stickyHeader.stickyGroup ? groupManager.collapsedGroups.has(stickyHeader.stickyGroup.name) : false,
1307
+ loading: stickyHeader.stickyGroup != null && loadingGroupNames.includes(stickyHeader.stickyGroup.name),
1214
1308
  onToggle: handleToggleGroup,
1215
1309
  groupStatusIcons: groupStatusIcons,
1216
- serverTime: serverTime
1310
+ serverOffsetTime: serverOffsetTime
1217
1311
  }), /*#__PURE__*/React.createElement(Virtuoso, {
1218
1312
  ref: virtuosoRef,
1219
1313
  style: {
@@ -1226,9 +1320,7 @@ var PipelineLogViewer = /*#__PURE__*/forwardRef(function (_ref2, ref) {
1226
1320
  scrollerRef: function scrollerRef(el) {
1227
1321
  scrollerDomRef.current = el;
1228
1322
  },
1229
- itemsRendered: handleItemsRendered
1230
- // overscan={400}
1231
- ,
1323
+ itemsRendered: handleItemsRendered,
1232
1324
  defaultItemHeight: DEFAULT_VIRTUOSO_ITEM_HEIGHT,
1233
1325
  rangeChanged: handleRangeChanged,
1234
1326
  atBottomStateChange: handleAtBottomStateChange,
@@ -11,8 +11,8 @@ interface GroupHeaderProps {
11
11
  onToggle: (groupName: string) => void;
12
12
  /** 自定义状态图标 (可选) */
13
13
  groupStatusIcons?: Record<string, React.ReactNode>;
14
- /** 服务端当前时间 (用于 running 态计时) */
15
- serverTime?: number;
14
+ /** 服务端「当前」时间戳与本地 Date.now() 的偏移时间差 */
15
+ serverOffsetTime?: number;
16
16
  }
17
17
  declare const _default: React.NamedExoticComponent<GroupHeaderProps>;
18
18
  export default _default;
@@ -1 +1 @@
1
- {"version":3,"file":"GroupHeader.d.ts","sourceRoot":"","sources":["../../../../src/PipelineLogViewer/components/GroupHeader.tsx"],"names":[],"mappings":"AAOA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAE1C,UAAU,gBAAgB;IACxB,WAAW;IACX,KAAK,EAAE,SAAS,CAAC;IACjB,WAAW;IACX,SAAS,EAAE,OAAO,CAAC;IACnB,gCAAgC;IAChC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,eAAe;IACf,QAAQ,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IACtC,mBAAmB;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;IACnD,+BAA+B;IAC/B,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;;AAmED,wBAAuC"}
1
+ {"version":3,"file":"GroupHeader.d.ts","sourceRoot":"","sources":["../../../../src/PipelineLogViewer/components/GroupHeader.tsx"],"names":[],"mappings":"AAOA,OAAO,KAAiB,MAAM,OAAO,CAAC;AAGtC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAK1C,UAAU,gBAAgB;IACxB,WAAW;IACX,KAAK,EAAE,SAAS,CAAC;IACjB,WAAW;IACX,SAAS,EAAE,OAAO,CAAC;IACnB,gCAAgC;IAChC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,eAAe;IACf,QAAQ,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IACtC,mBAAmB;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;IACnD,sCAAsC;IACtC,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;;AA8ED,wBAAuC"}
@@ -5,9 +5,11 @@
5
5
  // 支持点击折叠/展开。独立于 Virtuoso 之外用于吸顶。
6
6
  // ============================================================================
7
7
 
8
- import React from 'react';
8
+ import React, { useRef } from 'react';
9
9
  import ChevronRight from "../../TerminalViewerVirtualDom/ChevronRight";
10
10
  import HumenTime from "../../TerminalViewerVirtualDom/HumenTime";
11
+ /** 仅抑制「双击第二下」级间隔(ms);大于此间隔的 detail>1 仍视为用户有意连点 */
12
+ var MULTI_CLICK_SUPPRESS_MS = 140;
11
13
  var GroupHeader = function GroupHeader(_ref) {
12
14
  var _group$endTime;
13
15
  var group = _ref.group,
@@ -15,21 +17,32 @@ var GroupHeader = function GroupHeader(_ref) {
15
17
  loading = _ref.loading,
16
18
  onToggle = _ref.onToggle,
17
19
  groupStatusIcons = _ref.groupStatusIcons,
18
- serverTime = _ref.serverTime;
19
- /** 双击会连续触发两次 click(detail 为 1、2),第二次会走「折叠」分支,造成展开后立即折叠与视口抖动 */
20
+ serverOffsetTime = _ref.serverOffsetTime;
21
+ /**
22
+ * 原生双击的第二下 click 的 detail>=2,且与上一击间隔极短;需抑制,避免「展开后立刻被再点收起」抖动。
23
+ * 不能用「仅 detail===1 才响应」:系统在约 500ms 内会把连续单击也记成多击,导致第 2、4、6… 次点击被整段丢弃,
24
+ * 用户快速连点分组头会感觉「点了没反应」。
25
+ */
26
+ var lastHandledClickTsRef = useRef(0);
20
27
  var handleClick = function handleClick(e) {
21
- if (e.detail !== 1) return;
22
28
  if (loading) return;
29
+ var t = e.timeStamp;
30
+ if (e.detail > 1 && t - lastHandledClickTsRef.current < MULTI_CLICK_SUPPRESS_MS) {
31
+ return;
32
+ }
33
+ lastHandledClickTsRef.current = t;
23
34
  onToggle(group.name);
24
35
  };
25
36
  var statusIcon = groupStatusIcons === null || groupStatusIcons === void 0 ? void 0 : groupStatusIcons[group.status];
26
37
  var runningIcon = groupStatusIcons === null || groupStatusIcons === void 0 ? void 0 : groupStatusIcons.running;
27
38
  return /*#__PURE__*/React.createElement("div", {
28
39
  className: "pipeline-log-group-header".concat(loading ? ' pipeline-log-group-header--loading' : ''),
40
+ title: loading ? '正在加载该阶段日志,请稍候' : undefined,
29
41
  onClick: handleClick,
30
42
  role: "button",
31
43
  tabIndex: loading ? -1 : 0,
32
44
  "aria-expanded": !collapsed,
45
+ "aria-disabled": loading || undefined,
33
46
  "aria-busy": loading || undefined,
34
47
  "aria-label": "".concat(collapsed ? '展开' : '折叠', " ").concat(group.name),
35
48
  onKeyDown: function onKeyDown(e) {
@@ -59,7 +72,7 @@ var GroupHeader = function GroupHeader(_ref) {
59
72
  startTime: group.startTime,
60
73
  endTime: (_group$endTime = group.endTime) !== null && _group$endTime !== void 0 ? _group$endTime : undefined,
61
74
  timing: group.status === 'running',
62
- serverTime: serverTime
75
+ offsetTime: serverOffsetTime
63
76
  })));
64
77
  };
65
78
  export default /*#__PURE__*/React.memo(GroupHeader);
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SearchBar.d.ts","sourceRoot":"","sources":["../../../../src/PipelineLogViewer/components/SearchBar.tsx"],"names":[],"mappings":"AAoBA,OAAO,KAAwC,MAAM,OAAO,CAAC;AAE7D,MAAM,WAAW,cAAc;IAC7B,aAAa;IACb,KAAK,EAAE,MAAM,CAAC;IACd,2BAA2B;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW;IACX,QAAQ,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,eAAe;IACf,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,eAAe;IACf,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,WAAW;IACX,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,sBAAsB;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc;IACd,gBAAgB,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACnC,cAAc;IACd,eAAe,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAClC,YAAY;IACZ,KAAK,CAAC,EAAE;QACN,MAAM,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;QACzB,EAAE,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;QACrB,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;QACvB,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;KACzB,CAAC;IACF;;OAEG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;;AAgID,wBAAqC"}
@@ -9,8 +9,9 @@ interface StickyHeaderProps {
9
9
  onToggle: (groupName: string) => void;
10
10
  /** 自定义分组状态图标 */
11
11
  groupStatusIcons?: Record<PipelineLogStatus, React.ReactNode>;
12
- /** 服务端当前时间 (running 态计时),与 GroupHeader 一致 */
13
- serverTime?: number;
12
+ /** 服务端「当前」时间戳与本地 Date.now() 的偏移时间差 */
13
+ serverOffsetTime?: number;
14
+ loading?: boolean;
14
15
  }
15
16
  declare const _default: React.NamedExoticComponent<StickyHeaderProps>;
16
17
  export default _default;
@@ -1 +1 @@
1
- {"version":3,"file":"StickyHeader.d.ts","sourceRoot":"","sources":["../../../../src/PipelineLogViewer/components/StickyHeader.tsx"],"names":[],"mappings":"AAOA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,KAAK,EAAE,SAAS,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAG7D,UAAU,iBAAiB;IACzB,yBAAyB;IACzB,KAAK,EAAE,SAAS,GAAG,IAAI,CAAC;IACxB,mCAAmC;IACnC,SAAS,EAAE,OAAO,CAAC;IACnB,+BAA+B;IAC/B,QAAQ,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IACtC,gBAAgB;IAChB,gBAAgB,CAAC,EAAE,MAAM,CAAC,iBAAiB,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;IAC9D,6CAA6C;IAC7C,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;;AAwBD,wBAAwC"}
1
+ {"version":3,"file":"StickyHeader.d.ts","sourceRoot":"","sources":["../../../../src/PipelineLogViewer/components/StickyHeader.tsx"],"names":[],"mappings":"AAOA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,KAAK,EAAE,SAAS,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAG7D,UAAU,iBAAiB;IACzB,yBAAyB;IACzB,KAAK,EAAE,SAAS,GAAG,IAAI,CAAC;IACxB,mCAAmC;IACnC,SAAS,EAAE,OAAO,CAAC;IACnB,+BAA+B;IAC/B,QAAQ,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IACtC,gBAAgB;IAChB,gBAAgB,CAAC,EAAE,MAAM,CAAC,iBAAiB,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;IAC9D,sCAAsC;IACtC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;;AA0BD,wBAAwC"}
@@ -12,7 +12,8 @@ var StickyHeader = function StickyHeader(_ref) {
12
12
  collapsed = _ref.collapsed,
13
13
  onToggle = _ref.onToggle,
14
14
  groupStatusIcons = _ref.groupStatusIcons,
15
- serverTime = _ref.serverTime;
15
+ serverOffsetTime = _ref.serverOffsetTime,
16
+ loading = _ref.loading;
16
17
  if (!group) return null;
17
18
  return /*#__PURE__*/React.createElement("div", {
18
19
  className: "pipeline-log-sticky-header"
@@ -21,7 +22,8 @@ var StickyHeader = function StickyHeader(_ref) {
21
22
  collapsed: collapsed,
22
23
  onToggle: onToggle,
23
24
  groupStatusIcons: groupStatusIcons,
24
- serverTime: serverTime
25
+ serverOffsetTime: serverOffsetTime,
26
+ loading: loading
25
27
  }));
26
28
  };
27
29
  export default /*#__PURE__*/React.memo(StickyHeader);
@@ -19,7 +19,7 @@ interface UseDisplayLogsReturn {
19
19
  * 核心策略:
20
20
  * 1. 遍历原始 logs,判断当前行所属 Group
21
21
  * 2. 若 Group 被折叠,则仅保留该 Group 的第一行,并标记 isFoldedGroupStart = true
22
- * 3. 剔除折叠区间内的其余所有日志行
22
+ * 3. 剔除折叠区间内的其余日志行
23
23
  */
24
24
  declare function useDisplayLogs(options: UseDisplayLogsOptions): UseDisplayLogsReturn;
25
25
  export default useDisplayLogs;
@@ -1 +1 @@
1
- {"version":3,"file":"useDisplayLogs.d.ts","sourceRoot":"","sources":["../../../../src/PipelineLogViewer/hooks/useDisplayLogs.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAEpE,UAAU,qBAAqB;IAC7B,eAAe;IACf,IAAI,EAAE,QAAQ,EAAE,CAAC;IACjB,qCAAqC;IACrC,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW;IACX,MAAM,EAAE,SAAS,EAAE,CAAC;IACpB,gBAAgB;IAChB,eAAe,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CAC9B;AAED,UAAU,oBAAoB;IAC5B,6BAA6B;IAC7B,WAAW,EAAE,cAAc,EAAE,CAAC;CAC/B;AAED;;;;;;;GAOG;AACH,iBAAS,cAAc,CAAC,OAAO,EAAE,qBAAqB,GAAG,oBAAoB,CAsK5E;AAED,eAAe,cAAc,CAAC"}
1
+ {"version":3,"file":"useDisplayLogs.d.ts","sourceRoot":"","sources":["../../../../src/PipelineLogViewer/hooks/useDisplayLogs.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAEpE,UAAU,qBAAqB;IAC7B,eAAe;IACf,IAAI,EAAE,QAAQ,EAAE,CAAC;IACjB,qCAAqC;IACrC,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW;IACX,MAAM,EAAE,SAAS,EAAE,CAAC;IACpB,gBAAgB;IAChB,eAAe,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CAC9B;AAED,UAAU,oBAAoB;IAC5B,6BAA6B;IAC7B,WAAW,EAAE,cAAc,EAAE,CAAC;CAC/B;AAED;;;;;;;GAOG;AACH,iBAAS,cAAc,CAAC,OAAO,EAAE,qBAAqB,GAAG,oBAAoB,CA8L5E;AAED,eAAe,cAAc,CAAC"}