react-terminal-viewer-cicd 3.0.0-beta.44 → 3.0.0-beta.46

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.44" || '0.0.0';
4
+ var version = "3.0.0-beta.46" || '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;AAiBf,OAAO,cAAc,CAAC;AACtB,OAAO,KAAK,EAGV,sBAAsB,EACtB,oBAAoB,EACrB,MAAM,SAAS,CAAC;AAwEjB,QAAA,MAAM,iBAAiB,qGAkyCtB,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;AAiBf,OAAO,cAAc,CAAC;AACtB,OAAO,KAAK,EAGV,sBAAsB,EACtB,oBAAoB,EACrB,MAAM,SAAS,CAAC;AAwEjB,QAAA,MAAM,iBAAiB,qGAkzCtB,CAAC;AAIF,eAAe,iBAAiB,CAAC"}
@@ -580,26 +580,39 @@ var PipelineLogViewer = /*#__PURE__*/forwardRef(function (_ref2, ref) {
580
580
  }
581
581
  var anchorRowNext = pendingScrollAfterNextRef.current;
582
582
  if (anchorRowNext !== null) {
583
+ var _scrollerDomRef$curre3, _scrollerDomRef$curre4;
583
584
  var _lenBefore = displayLogsLenBeforeLoadNextRef.current;
584
585
  var _sameLength = _lenBefore !== null && _lenBefore === displayLogs.length && displayLogs.length > 0;
585
586
  var headRemoved = headRemovedFromNextRef.current;
586
587
  pendingScrollAfterNextRef.current = null;
587
588
  displayLogsLenBeforeLoadNextRef.current = null;
588
- ignoreBoundaryFetchRef.current = Date.now() + IGNORE_BOUNDARY_FETCH_MS_AFTER_SCROLL;
589
- var comp = applyBoundaryScrollCompensation(v, anchorRowNext, displayLogs, {
589
+
590
+ // 极大缩短屏蔽窗口(原本500ms):仅用 50ms 屏蔽当前帧由于 scrollTop 重置引起的乱序 rangeChanged。
591
+ // 不再长时间屏蔽,使得用户在快速下滚时 rangeChanged 依然灵敏,消除“触底发呆/卡顿”感。
592
+ ignoreBoundaryFetchRef.current = Date.now() + 50;
593
+
594
+ // 因为不再有漫长的屏蔽期,无需跟进定时器补偿,直接清理
595
+ if (boundaryIgnoreFollowUpTimeoutRef.current) {
596
+ clearTimeout(boundaryIgnoreFollowUpTimeoutRef.current);
597
+ boundaryIgnoreFollowUpTimeoutRef.current = null;
598
+ }
599
+
600
+ // 读取当前实时 scrollTop,而非请求前快照的 scrollTopBeforeLoadNextRef,
601
+ // 避免用户在 API 请求期间继续滚动导致补偿基准过时(跳屏)。
602
+ var realTimeScrollTop = (_scrollerDomRef$curre3 = (_scrollerDomRef$curre4 = scrollerDomRef.current) === null || _scrollerDomRef$curre4 === void 0 ? void 0 : _scrollerDomRef$curre4.scrollTop) !== null && _scrollerDomRef$curre3 !== void 0 ? _scrollerDomRef$curre3 : scrollTopBeforeLoadNextRef.current;
603
+ applyBoundaryScrollCompensation(v, anchorRowNext, displayLogs, {
590
604
  sameLength: _sameLength && headRemoved > 0,
591
605
  sameLengthPixelDeltaCount: headRemoved,
592
606
  scrollTopBefore: scrollTopBeforeLoadNextRef.current,
607
+ // 保留接口签名所需,但已被 currentScrollTop 覆盖作用
593
608
  defaultItemHeight: DEFAULT_VIRTUOSO_ITEM_HEIGHT,
594
- mode: 'appendHeadSlice'
609
+ mode: 'appendHeadSlice',
610
+ currentScrollTop: realTimeScrollTop
595
611
  });
596
- if (comp.kind === 'anchorMissing') {
597
- v.scrollToIndex({
598
- index: 'LAST',
599
- align: 'end',
600
- behavior: 'auto'
601
- });
602
- }
612
+
613
+ // 注意:这里不再添加 scrollIntoView 精修对齐。
614
+ // 实测在用户连续滚动滑轮/触摸板时,scrollIntoView 会强行接管视口,打断浏览器的滚动动量(momentum),
615
+ // 导致体感上每翻 5000 行截断时就会“硬卡一下”。仅做单纯的 scrollTop 像素增减,能保持滚动的平滑。
603
616
  }
604
617
  }, [displayLogs]);
605
618
 
@@ -9,7 +9,7 @@ export declare const DEFAULT_VIRTUOSO_ITEM_HEIGHT = 20;
9
9
  * 过小(如 10)时,必须把「缓冲区内第一屏」滚到几乎贴顶才触发,此时锚点往往是 bufferStart(如 7550),
10
10
  * 而不是用户刚看到的行(如 7571 在顶时 startIndex 可能已是 21,却满足不了 <=10)。
11
11
  */
12
- export declare const BOUNDARY_PREFETCH_INDEX_MARGIN = 200;
12
+ export declare const BOUNDARY_PREFETCH_INDEX_MARGIN = 400;
13
13
  /**
14
14
  * 首屏 init / 新日志源首包:屏蔽 rangeChanged 边界预取时长 (ms)。
15
15
  * 略长于普通窗口,避免弹窗首帧、scrollToIndex(LAST) 前误判触顶触发 loadPrev。
@@ -17,7 +17,7 @@ export var DEFAULT_VIRTUOSO_ITEM_HEIGHT = 20;
17
17
  * 过小(如 10)时,必须把「缓冲区内第一屏」滚到几乎贴顶才触发,此时锚点往往是 bufferStart(如 7550),
18
18
  * 而不是用户刚看到的行(如 7571 在顶时 startIndex 可能已是 21,却满足不了 <=10)。
19
19
  */
20
- export var BOUNDARY_PREFETCH_INDEX_MARGIN = 200;
20
+ export var BOUNDARY_PREFETCH_INDEX_MARGIN = 400;
21
21
 
22
22
  /**
23
23
  * 首屏 init / 新日志源首包:屏蔽 rangeChanged 边界预取时长 (ms)。
@@ -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,CA+fzE;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,CAkgBzE;AAED,eAAe,aAAa,CAAC"}
@@ -628,23 +628,26 @@ function useLogFetcher(options) {
628
628
  }
629
629
  return _context5.abrupt("return");
630
630
  case 2:
631
- if (isUserBrowsingHistory) {
632
- _context5.next = 7;
633
- break;
631
+ tasks = []; // 防打扰:若用户在向上查阅历史,暂停日志自动追加(不阻断分组列表刷新)
632
+ if (!isUserBrowsingHistory) {
633
+ tasks.push(initLoad({
634
+ silent: true
635
+ }));
634
636
  }
635
- tasks = [initLoad({
636
- silent: true
637
- })];
638
637
  if (runningPollSync) {
639
638
  tasks.push(runningPollSync());
640
639
  }
641
- _context5.next = 7;
640
+ if (!(tasks.length > 0)) {
641
+ _context5.next = 8;
642
+ break;
643
+ }
644
+ _context5.next = 8;
642
645
  return Promise.all(tasks).catch(function (err) {
643
646
  console.error('[PipelineLogViewer] running poll failed:', err);
644
647
  });
645
- case 7:
646
- if (!cancelled && mountedRef.current) scheduleNext();
647
648
  case 8:
649
+ if (!cancelled && mountedRef.current) scheduleNext();
650
+ case 9:
648
651
  case "end":
649
652
  return _context5.stop();
650
653
  }
@@ -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,CA0B/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;AA6CD;;GAEG;AACH,iBAAS,eAAe,CAAC,OAAO,EAAE,sBAAsB,GAAG,qBAAqB,CAoD/E;AAED,eAAe,eAAe,CAAC"}
@@ -14,7 +14,15 @@ function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
14
14
  // 与下一组 start 计算有效区间
15
15
  // ============================================================================
16
16
 
17
- import { useCallback, useEffect, useState } from 'react';
17
+ import { useCallback, useEffect, useRef, useState } from 'react';
18
+ /** 提前探测行数:让吸顶分组在边界前更早感知切换趋势 */
19
+ var STICKY_LEAD_ROWS = 2;
20
+ /**
21
+ * 新分组头仍在视口可见区时,暂不渲染吸顶,避免出现「吸顶头 + 列表头」双头重影。
22
+ * 该窗口用绝对行号近似:Top 仍落在 group.start 附近时,优先显示列表内真实头。
23
+ */
24
+ var STICKY_SUPPRESS_ROWS_NEAR_GROUP_START = 1;
25
+
18
26
  /** 与 useDisplayLogs 折叠区间算法一致的有效上界(用于判断行是否落在该组) */
19
27
  function effectiveGroupEnd(group, gi, groups, bufferEnd) {
20
28
  var start = group.start,
@@ -51,25 +59,46 @@ function useStickyHeader(options) {
51
59
  _useState2 = _slicedToArray(_useState, 2),
52
60
  stickyGroup = _useState2[0],
53
61
  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]);
66
- var handleRangeChanged = useCallback(function (absoluteTopRow, bufferEnd) {
62
+ var lastParamsRef = useRef(null);
63
+ var evaluateSticky = useCallback(function (absoluteTopRow, bufferEnd) {
67
64
  if (groups.length === 0) {
68
65
  setStickyGroup(null);
69
66
  return;
70
67
  }
71
- setStickyGroup(findStickyGroup(groups, absoluteTopRow, bufferEnd));
68
+ var stickyProbeRow = Math.max(0, absoluteTopRow + STICKY_LEAD_ROWS);
69
+ var nextStickyGroup = findStickyGroup(groups, stickyProbeRow, bufferEnd);
70
+ if (!nextStickyGroup) {
71
+ setStickyGroup(null);
72
+ return;
73
+ }
74
+ // 边界抑制:提前命中新分组时,其真实 GroupHeader 常仍在视口内,
75
+ // 这时渲染吸顶会出现双头。让列表内头部先过顶,再接管吸顶展示。
76
+ if (absoluteTopRow <= nextStickyGroup.start + STICKY_SUPPRESS_ROWS_NEAR_GROUP_START) {
77
+ setStickyGroup(null);
78
+ return;
79
+ }
80
+ setStickyGroup(function (prev) {
81
+ // 如果吸顶分组名和状态没变,直接返回原引用以避免不必要的重新渲染
82
+ if ((prev === null || prev === void 0 ? void 0 : prev.name) === nextStickyGroup.name && (prev === null || prev === void 0 ? void 0 : prev.status) === nextStickyGroup.status) {
83
+ return prev;
84
+ }
85
+ return nextStickyGroup;
86
+ });
72
87
  }, [groups]);
88
+ var handleRangeChanged = useCallback(function (absoluteTopRow, bufferEnd) {
89
+ lastParamsRef.current = {
90
+ absoluteTopRow: absoluteTopRow,
91
+ bufferEnd: bufferEnd
92
+ };
93
+ evaluateSticky(absoluteTopRow, bufferEnd);
94
+ }, [evaluateSticky]);
95
+
96
+ // 当外部的 groups 更新时,重新触发计算,避免只在滚动时才能展示新分组的吸顶头
97
+ useEffect(function () {
98
+ if (lastParamsRef.current) {
99
+ evaluateSticky(lastParamsRef.current.absoluteTopRow, lastParamsRef.current.bufferEnd);
100
+ }
101
+ }, [evaluateSticky]);
73
102
  return {
74
103
  stickyGroup: stickyGroup,
75
104
  handleRangeChanged: handleRangeChanged
@@ -8,7 +8,9 @@ export type BoundaryScrollResult = {
8
8
  };
9
9
  /**
10
10
  * 滑动窗口 totalCount 不变时,Virtuoso 不会自动补偿 scrollTop。
11
- * 用像素粗调 + scrollIntoView 对齐到锚点行(与 loadPrev/loadNext 对称)。
11
+ * - prepend 模式:像素粗调 + scrollIntoView 对齐到锚点行。
12
+ * - appendHeadSlice 模式:仅用当前实时 scrollTop 减去截断行的像素偏移量。
13
+ * 不做锚点行定位,避免用户在 API 请求期间继续滚动时被强行拉回旧位置(跳屏)。
12
14
  */
13
15
  export declare function applyBoundaryScrollCompensation(v: VirtuosoHandle, anchorRow: number, rowsWithAbsolute: Array<{
14
16
  absoluteRow: number;
@@ -19,4 +21,9 @@ export declare function applyBoundaryScrollCompensation(v: VirtuosoHandle, ancho
19
21
  scrollTopBefore: number;
20
22
  defaultItemHeight: number;
21
23
  mode: BoundaryScrollMode;
24
+ /**
25
+ * appendHeadSlice 专用:当前实时 scrollTop(从 scroller DOM 读取)。
26
+ * 优先于 scrollTopBefore 使用,避免请求期间用户继续滚动导致补偿基准过时。
27
+ */
28
+ currentScrollTop?: number;
22
29
  }): BoundaryScrollResult;
@@ -1 +1 @@
1
- {"version":3,"file":"virtuosoBoundaryScroll.d.ts","sourceRoot":"","sources":["../../../../src/PipelineLogViewer/utils/virtuosoBoundaryScroll.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAErD,6DAA6D;AAC7D,MAAM,MAAM,kBAAkB,GAAG,SAAS,GAAG,iBAAiB,CAAC;AAE/D,MAAM,MAAM,oBAAoB,GAAG;IAAE,IAAI,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,eAAe,CAAA;CAAE,CAAC;AAE9E;;;GAGG;AACH,wBAAgB,+BAA+B,CAC7C,CAAC,EAAE,cAAc,EACjB,SAAS,EAAE,MAAM,EACjB,gBAAgB,EAAE,KAAK,CAAC;IAAE,WAAW,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAAC,EAC5D,OAAO,EAAE;IACP,UAAU,EAAE,OAAO,CAAC;IACpB,+CAA+C;IAC/C,yBAAyB,EAAE,MAAM,CAAC;IAClC,eAAe,EAAE,MAAM,CAAC;IACxB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,IAAI,EAAE,kBAAkB,CAAC;CAC1B,GACA,oBAAoB,CAgCtB"}
1
+ {"version":3,"file":"virtuosoBoundaryScroll.d.ts","sourceRoot":"","sources":["../../../../src/PipelineLogViewer/utils/virtuosoBoundaryScroll.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAErD,6DAA6D;AAC7D,MAAM,MAAM,kBAAkB,GAAG,SAAS,GAAG,iBAAiB,CAAC;AAE/D,MAAM,MAAM,oBAAoB,GAAG;IAAE,IAAI,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,eAAe,CAAA;CAAE,CAAC;AAE9E;;;;;GAKG;AACH,wBAAgB,+BAA+B,CAC7C,CAAC,EAAE,cAAc,EACjB,SAAS,EAAE,MAAM,EACjB,gBAAgB,EAAE,KAAK,CAAC;IAAE,WAAW,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAAC,EAC5D,OAAO,EAAE;IACP,UAAU,EAAE,OAAO,CAAC;IACpB,+CAA+C;IAC/C,yBAAyB,EAAE,MAAM,CAAC;IAClC,eAAe,EAAE,MAAM,CAAC;IACxB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,IAAI,EAAE,kBAAkB,CAAC;IACzB;;;OAGG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B,GACA,oBAAoB,CAyDtB"}
@@ -2,9 +2,51 @@
2
2
 
3
3
  /**
4
4
  * 滑动窗口 totalCount 不变时,Virtuoso 不会自动补偿 scrollTop。
5
- * 用像素粗调 + scrollIntoView 对齐到锚点行(与 loadPrev/loadNext 对称)。
5
+ * - prepend 模式:像素粗调 + scrollIntoView 对齐到锚点行。
6
+ * - appendHeadSlice 模式:仅用当前实时 scrollTop 减去截断行的像素偏移量。
7
+ * 不做锚点行定位,避免用户在 API 请求期间继续滚动时被强行拉回旧位置(跳屏)。
6
8
  */
7
9
  export function applyBoundaryScrollCompensation(v, anchorRow, rowsWithAbsolute, options) {
10
+ var sameLength = options.sameLength,
11
+ sameLengthPixelDeltaCount = options.sameLengthPixelDeltaCount,
12
+ scrollTopBefore = options.scrollTopBefore,
13
+ defaultItemHeight = options.defaultItemHeight,
14
+ mode = options.mode;
15
+
16
+ // -----------------------------------------------------------------------
17
+ // appendHeadSlice(loadNext 满载截断头部):
18
+ // 仅做像素偏移补偿,不做锚点行定位。
19
+ //
20
+ // 原理:head 截断 N 行后,所有 item 的 index 减小了 N,
21
+ // 视觉上内容整体上移了 N * itemHeight 像素。
22
+ // 要保持用户看到的内容不变,scrollTop 需减去同样的像素量。
23
+ //
24
+ // 使用 currentScrollTop(实时值)而非 scrollTopBefore(请求前快照),
25
+ // 解决用户在 API 请求期间继续滚动导致的跳屏问题。
26
+ // -----------------------------------------------------------------------
27
+ if (mode === 'appendHeadSlice') {
28
+ if (sameLength && sameLengthPixelDeltaCount > 0) {
29
+ var _options$currentScrol;
30
+ var baseScrollTop = (_options$currentScrol = options.currentScrollTop) !== null && _options$currentScrol !== void 0 ? _options$currentScrol : scrollTopBefore;
31
+ var delta = -sameLengthPixelDeltaCount * defaultItemHeight;
32
+ var top = Math.max(0, baseScrollTop + delta);
33
+ v.scrollTo({
34
+ top: top,
35
+ behavior: 'auto'
36
+ });
37
+ }
38
+ // 不管有没有像素补偿,都不再用锚点行做 scrollIntoView,
39
+ // 避免强行拉回旧位置。
40
+ return {
41
+ kind: 'ok'
42
+ };
43
+ }
44
+
45
+ // -----------------------------------------------------------------------
46
+ // prepend(loadPrev 满载截断尾部):
47
+ // 像素粗调 + scrollIntoView 锚点行精确对齐。
48
+ // prepend 场景下用户通常在最顶部等待加载,scrollTopBefore 不会过时。
49
+ // -----------------------------------------------------------------------
8
50
  var newIndex = rowsWithAbsolute.findIndex(function (l) {
9
51
  return l && l.absoluteRow === anchorRow;
10
52
  });
@@ -13,22 +55,17 @@ export function applyBoundaryScrollCompensation(v, anchorRow, rowsWithAbsolute,
13
55
  kind: 'anchorMissing'
14
56
  };
15
57
  }
16
- var sameLength = options.sameLength,
17
- sameLengthPixelDeltaCount = options.sameLengthPixelDeltaCount,
18
- scrollTopBefore = options.scrollTopBefore,
19
- defaultItemHeight = options.defaultItemHeight,
20
- mode = options.mode;
21
58
  if (sameLength && sameLengthPixelDeltaCount > 0) {
22
- var delta = mode === 'prepend' ? sameLengthPixelDeltaCount * defaultItemHeight : -sameLengthPixelDeltaCount * defaultItemHeight;
23
- var top = Math.max(0, scrollTopBefore + delta);
59
+ var _delta = sameLengthPixelDeltaCount * defaultItemHeight;
60
+ var _top = Math.max(0, scrollTopBefore + _delta);
24
61
  v.scrollTo({
25
- top: top,
62
+ top: _top,
26
63
  behavior: 'auto'
27
64
  });
28
65
  requestAnimationFrame(function () {
29
66
  v.scrollIntoView({
30
67
  index: newIndex,
31
- align: mode === 'prepend' ? 'start' : 'end',
68
+ align: 'start',
32
69
  behavior: 'auto'
33
70
  });
34
71
  });
@@ -38,7 +75,7 @@ export function applyBoundaryScrollCompensation(v, anchorRow, rowsWithAbsolute,
38
75
  }
39
76
  v.scrollToIndex({
40
77
  index: newIndex,
41
- align: mode === 'prepend' ? 'start' : 'end',
78
+ align: 'start',
42
79
  behavior: 'auto'
43
80
  });
44
81
  return {
@@ -9,7 +9,7 @@ export declare const DEFAULT_VIRTUOSO_ITEM_HEIGHT = 20;
9
9
  * 过小(如 10)时,必须把「缓冲区内第一屏」滚到几乎贴顶才触发,此时锚点往往是 bufferStart(如 7550),
10
10
  * 而不是用户刚看到的行(如 7571 在顶时 startIndex 可能已是 21,却满足不了 <=10)。
11
11
  */
12
- export declare const BOUNDARY_PREFETCH_INDEX_MARGIN = 200;
12
+ export declare const BOUNDARY_PREFETCH_INDEX_MARGIN = 400;
13
13
  /**
14
14
  * 首屏 init / 新日志源首包:屏蔽 rangeChanged 边界预取时长 (ms)。
15
15
  * 略长于普通窗口,避免弹窗首帧、scrollToIndex(LAST) 前误判触顶触发 loadPrev。
@@ -0,0 +1,38 @@
1
+ import type { LogEntry } from '../types';
2
+ /** 单个分组的日志快照 */
3
+ export interface GroupSnapshot {
4
+ /** 分组名称(唯一索引) */
5
+ groupName: string;
6
+ /** 快照覆盖的起始绝对行号 */
7
+ startRow: number;
8
+ /** 快照覆盖的结束绝对行号 */
9
+ endRow: number;
10
+ /** 该分组范围内的日志条目(已经过 ANSI 处理) */
11
+ logs: LogEntry[];
12
+ /** 快照创建时间戳(用于可能的 LRU 淘汰) */
13
+ createdAt: number;
14
+ }
15
+ interface UseGroupSnapshotCacheReturn {
16
+ /** 按分组名获取快照 */
17
+ getSnapshot: (groupName: string) => GroupSnapshot | undefined;
18
+ /** 写入/更新分组快照 */
19
+ setSnapshot: (snapshot: GroupSnapshot) => void;
20
+ /** 删除指定分组快照(折叠时清理) */
21
+ removeSnapshot: (groupName: string) => void;
22
+ /** 清空所有快照(collapseAll / expandAll / reset 时) */
23
+ clearAll: () => void;
24
+ /** 当前所有快照的不可变 Map(供 useDisplayLogs 作为依赖) */
25
+ snapshots: Map<string, GroupSnapshot>;
26
+ /** 判断某绝对行号是否落在任一快照区间内 */
27
+ isRowInSnapshot: (absoluteRow: number) => boolean;
28
+ }
29
+ /**
30
+ * 分组日志快照缓存 Hook
31
+ *
32
+ * 核心策略:
33
+ * 1. 使用 Map<string, GroupSnapshot> 按分组名存储快照
34
+ * 2. 每次写入/删除都产生新的 Map 引用以触发 React 重渲染
35
+ * 3. 同时维护 ref 供同步读取(避免 stale closure)
36
+ */
37
+ declare function useGroupSnapshotCache(): UseGroupSnapshotCacheReturn;
38
+ export default useGroupSnapshotCache;
@@ -0,0 +1,17 @@
1
+ import type { FetchLogParams, GroupData, LogDataResult } from '../types';
2
+ import type { GroupSnapshot } from './useGroupSnapshotCache';
3
+ interface UseProactiveGroupSnapshotOptions {
4
+ groups: GroupData[];
5
+ collapsedGroups: Set<string>;
6
+ currentStartRow: number;
7
+ currentEndRow: number;
8
+ loading: boolean;
9
+ snapshotCache: {
10
+ snapshots: Map<string, GroupSnapshot>;
11
+ setSnapshot: (snapshot: GroupSnapshot) => void;
12
+ };
13
+ fetchLogs: (params: FetchLogParams) => Promise<LogDataResult>;
14
+ pageSize: number;
15
+ }
16
+ export default function useProactiveGroupSnapshot({ groups, collapsedGroups, currentStartRow, currentEndRow, loading, snapshotCache, fetchLogs, pageSize, }: UseProactiveGroupSnapshotOptions): void;
17
+ export {};
@@ -0,0 +1,7 @@
1
+ import type { DisplayLogLine, GroupData } from '../types';
2
+ /** 控制日志:END_stepId_...(与 PipelineLogViewer itemContent 一致) */
3
+ export declare function isPipelineEndMarkerText(text: string): boolean;
4
+ /** 控制日志:START_stepId_... */
5
+ export declare function isPipelineStartMarkerText(text: string): boolean;
6
+ export declare function maxPipelineLineNumberCellDigitValueInDisplayIndexRange(displayLogs: DisplayLogLine[], fromIndex: number, toIndex: number, groups: GroupData[], pipelineMinAbsoluteRow: number): number;
7
+ export declare function lineNumberColumnWidthPxForMaxValue(maxValue: number): number;
@@ -0,0 +1,3 @@
1
+ import type { LogEntry } from '../types';
2
+ declare const processAnsiLogs: (logs: LogEntry[]) => LogEntry[];
3
+ export default processAnsiLogs;
@@ -8,7 +8,9 @@ export type BoundaryScrollResult = {
8
8
  };
9
9
  /**
10
10
  * 滑动窗口 totalCount 不变时,Virtuoso 不会自动补偿 scrollTop。
11
- * 用像素粗调 + scrollIntoView 对齐到锚点行(与 loadPrev/loadNext 对称)。
11
+ * - prepend 模式:像素粗调 + scrollIntoView 对齐到锚点行。
12
+ * - appendHeadSlice 模式:仅用当前实时 scrollTop 减去截断行的像素偏移量。
13
+ * 不做锚点行定位,避免用户在 API 请求期间继续滚动时被强行拉回旧位置(跳屏)。
12
14
  */
13
15
  export declare function applyBoundaryScrollCompensation(v: VirtuosoHandle, anchorRow: number, rowsWithAbsolute: Array<{
14
16
  absoluteRow: number;
@@ -19,4 +21,9 @@ export declare function applyBoundaryScrollCompensation(v: VirtuosoHandle, ancho
19
21
  scrollTopBefore: number;
20
22
  defaultItemHeight: number;
21
23
  mode: BoundaryScrollMode;
24
+ /**
25
+ * appendHeadSlice 专用:当前实时 scrollTop(从 scroller DOM 读取)。
26
+ * 优先于 scrollTopBefore 使用,避免请求期间用户继续滚动导致补偿基准过时。
27
+ */
28
+ currentScrollTop?: number;
22
29
  }): BoundaryScrollResult;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-terminal-viewer-cicd",
3
- "version": "3.0.0-beta.44",
3
+ "version": "3.0.0-beta.46",
4
4
  "author": "https://gitee.com/gitee-frontend",
5
5
  "license": "MIT",
6
6
  "keywords": [