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

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.41" || '0.0.0';
4
+ var version = "3.0.0-beta.42" || '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,qGAgrCtB,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;AAuEjB,QAAA,MAAM,iBAAiB,qGAotCtB,CAAC;AAIF,eAAe,iBAAiB,CAAC"}
@@ -421,7 +421,9 @@ var PipelineLogViewer = /*#__PURE__*/forwardRef(function (_ref2, ref) {
421
421
  if (jumpTarget !== null) {
422
422
  collapseAirdropActiveRef.current = true;
423
423
  logFetcherResult.setIsUserBrowsingHistory(true);
424
- logFetcherResult.airdropLoad(jumpTarget);
424
+ logFetcherResult.airdropLoad(jumpTarget, {
425
+ silent: true
426
+ });
425
427
  }
426
428
  // eslint-disable-next-line react-hooks/exhaustive-deps
427
429
  }, [displayLogs.length, logFetcherResult.boundaryLock.isFirstPage, logFetcherResult.loading, groupManager.collapsedGroups, groups]);
@@ -878,6 +880,37 @@ var PipelineLogViewer = /*#__PURE__*/forwardRef(function (_ref2, ref) {
878
880
  });
879
881
  }
880
882
  }, []);
883
+
884
+ /** 等待 fetchingRef 锁释放(轮询 loadNext 可能恰好持有),避免 airdrop / initLoad 被拒 */
885
+ var waitForFetchUnlock = useCallback( /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee4() {
886
+ return _regeneratorRuntime().wrap(function _callee4$(_context4) {
887
+ while (1) switch (_context4.prev = _context4.next) {
888
+ case 0:
889
+ if (logFetcherResult.isFetchLocked()) {
890
+ _context4.next = 2;
891
+ break;
892
+ }
893
+ return _context4.abrupt("return");
894
+ case 2:
895
+ _context4.next = 4;
896
+ return new Promise(function (resolve) {
897
+ var attempts = 0;
898
+ var check = function check() {
899
+ attempts += 1;
900
+ if (!logFetcherResult.isFetchLocked() || attempts > 300) {
901
+ resolve();
902
+ return;
903
+ }
904
+ requestAnimationFrame(check);
905
+ };
906
+ requestAnimationFrame(check);
907
+ });
908
+ case 4:
909
+ case "end":
910
+ return _context4.stop();
911
+ }
912
+ }, _callee4);
913
+ })), [logFetcherResult]);
881
914
  var handleToggleGroup = useCallback(function (name) {
882
915
  lastGroupToggleAtRef.current = Date.now();
883
916
  var isExpanding = groupManager.collapsedGroups.has(name);
@@ -887,74 +920,135 @@ var PipelineLogViewer = /*#__PURE__*/forwardRef(function (_ref2, ref) {
887
920
  });
888
921
  if (group) {
889
922
  if (airdropExpandInFlightRef.current.has(name)) return;
890
- var _logFetcherResult$cur3 = logFetcherResult.cursor,
891
- bufStart = _logFetcherResult$cur3.currentStartRow,
892
- bufEnd = _logFetcherResult$cur3.currentEndRow;
893
- // 当前内存窗口是否已包含 group.start(重复展开同一分组时不重复空降)
894
- var groupStartInBuffer = bufStart <= group.start && group.start <= bufEnd;
895
923
  suppressFollowForGroupAction();
896
- userStoppedFollowingRef.current = true;
897
- logFetcherResult.setIsUserBrowsingHistory(true);
898
- if (!groupStartInBuffer) {
924
+ if (group.status === 'running') {
925
+ // --- Running 分组:展开后跳到最新日志并恢复跟随 ---
899
926
  airdropExpandInFlightRef.current.add(name);
900
927
  setLoadingGroupNames(function (prev) {
901
928
  return prev.includes(name) ? prev : [].concat(_toConsumableArray(prev), [name]);
902
929
  });
903
- var runAirdropThenExpand = /*#__PURE__*/function () {
904
- var _ref8 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee4() {
905
- var ok;
906
- return _regeneratorRuntime().wrap(function _callee4$(_context4) {
907
- while (1) switch (_context4.prev = _context4.next) {
930
+ var runExpandToLatest = /*#__PURE__*/function () {
931
+ var _ref9 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee5() {
932
+ return _regeneratorRuntime().wrap(function _callee5$(_context5) {
933
+ while (1) switch (_context5.prev = _context5.next) {
908
934
  case 0:
909
- _context4.prev = 0;
910
- _context4.next = 3;
911
- return logFetcherResult.airdropLoad(group.start, {
912
- window: 'fromStart',
935
+ _context5.prev = 0;
936
+ _context5.next = 3;
937
+ return waitForFetchUnlock();
938
+ case 3:
939
+ _context5.next = 5;
940
+ return logFetcherResult.initLoad({
913
941
  silent: true
914
942
  });
915
- case 3:
916
- ok = _context4.sent;
917
- if (!ok) {
918
- _context4.next = 7;
919
- break;
920
- }
921
- _context4.next = 7;
922
- return new Promise(function (resolve) {
923
- requestAnimationFrame(function () {
924
- requestAnimationFrame(function () {
925
- maintainScrollPosition(function () {
926
- groupManager.toggleGroup(name);
927
- });
928
- resolve();
929
- });
943
+ case 5:
944
+ groupManager.toggleGroup(name);
945
+ userStoppedFollowingRef.current = false;
946
+ collapseAirdropActiveRef.current = false;
947
+ logFetcherResult.setIsUserBrowsingHistory(false);
948
+ ignoreBoundaryFetchRef.current = Date.now() + 500;
949
+ requestAnimationFrame(function () {
950
+ var _virtuosoRef$current6;
951
+ (_virtuosoRef$current6 = virtuosoRef.current) === null || _virtuosoRef$current6 === void 0 || _virtuosoRef$current6.scrollToIndex({
952
+ index: 'LAST',
953
+ align: 'end',
954
+ behavior: 'auto'
930
955
  });
931
956
  });
932
- case 7:
933
- _context4.prev = 7;
957
+ case 11:
958
+ _context5.prev = 11;
934
959
  airdropExpandInFlightRef.current.delete(name);
935
960
  setLoadingGroupNames(function (prev) {
936
961
  return prev.filter(function (n) {
937
962
  return n !== name;
938
963
  });
939
964
  });
940
- return _context4.finish(7);
941
- case 11:
965
+ return _context5.finish(11);
966
+ case 15:
942
967
  case "end":
943
- return _context4.stop();
968
+ return _context5.stop();
944
969
  }
945
- }, _callee4, null, [[0,, 7, 11]]);
970
+ }, _callee5, null, [[0,, 11, 15]]);
946
971
  }));
947
- return function runAirdropThenExpand() {
948
- return _ref8.apply(this, arguments);
972
+ return function runExpandToLatest() {
973
+ return _ref9.apply(this, arguments);
949
974
  };
950
975
  }();
951
- runAirdropThenExpand();
976
+ runExpandToLatest();
952
977
  } else {
953
- // 缓冲内展开:用当前视口锚点重定位,避免原先 scrollToIndex(group.start) 把起点硬拉到顶,
954
- // running 等大分组展开时整屏上跳;与折叠分支一致走 maintainScrollPosition。
955
- maintainScrollPosition(function () {
978
+ var _group$end2;
979
+ // --- 非 running 分组:展开后跳到分组最后一行 ---
980
+ var groupEnd = (_group$end2 = group.end) !== null && _group$end2 !== void 0 ? _group$end2 : group.start;
981
+ var _logFetcherResult$cur3 = logFetcherResult.cursor,
982
+ bufStart = _logFetcherResult$cur3.currentStartRow,
983
+ bufEnd = _logFetcherResult$cur3.currentEndRow;
984
+ var groupEndInBuffer = bufStart <= groupEnd && groupEnd <= bufEnd;
985
+ userStoppedFollowingRef.current = true;
986
+ logFetcherResult.setIsUserBrowsingHistory(true);
987
+ if (!groupEndInBuffer) {
988
+ airdropExpandInFlightRef.current.add(name);
989
+ setLoadingGroupNames(function (prev) {
990
+ return prev.includes(name) ? prev : [].concat(_toConsumableArray(prev), [name]);
991
+ });
992
+ var runAirdropThenExpand = /*#__PURE__*/function () {
993
+ var _ref10 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee6() {
994
+ var ok;
995
+ return _regeneratorRuntime().wrap(function _callee6$(_context6) {
996
+ while (1) switch (_context6.prev = _context6.next) {
997
+ case 0:
998
+ _context6.prev = 0;
999
+ _context6.next = 3;
1000
+ return waitForFetchUnlock();
1001
+ case 3:
1002
+ _context6.next = 5;
1003
+ return logFetcherResult.airdropLoad(groupEnd, {
1004
+ window: 'center',
1005
+ silent: true
1006
+ });
1007
+ case 5:
1008
+ ok = _context6.sent;
1009
+ if (ok) {
1010
+ groupManager.toggleGroup(name);
1011
+ pendingScrollAlignRef.current = 'center';
1012
+ pendingScrollRowRef.current = groupEnd;
1013
+ }
1014
+ case 7:
1015
+ _context6.prev = 7;
1016
+ airdropExpandInFlightRef.current.delete(name);
1017
+ setLoadingGroupNames(function (prev) {
1018
+ return prev.filter(function (n) {
1019
+ return n !== name;
1020
+ });
1021
+ });
1022
+ return _context6.finish(7);
1023
+ case 11:
1024
+ case "end":
1025
+ return _context6.stop();
1026
+ }
1027
+ }, _callee6, null, [[0,, 7, 11]]);
1028
+ }));
1029
+ return function runAirdropThenExpand() {
1030
+ return _ref10.apply(this, arguments);
1031
+ };
1032
+ }();
1033
+ runAirdropThenExpand();
1034
+ } else {
956
1035
  groupManager.toggleGroup(name);
957
- });
1036
+ ignoreBoundaryFetchRef.current = Date.now() + 500;
1037
+ requestAnimationFrame(function () {
1038
+ var currentLogs = displayLogsRef.current;
1039
+ var displayIndex = currentLogs.findIndex(function (l) {
1040
+ return l.absoluteRow === groupEnd;
1041
+ });
1042
+ if (displayIndex >= 0) {
1043
+ var _virtuosoRef$current7;
1044
+ (_virtuosoRef$current7 = virtuosoRef.current) === null || _virtuosoRef$current7 === void 0 || _virtuosoRef$current7.scrollToIndex({
1045
+ index: displayIndex,
1046
+ align: 'center',
1047
+ behavior: 'auto'
1048
+ });
1049
+ }
1050
+ });
1051
+ }
958
1052
  }
959
1053
  return;
960
1054
  }
@@ -967,7 +1061,7 @@ var PipelineLogViewer = /*#__PURE__*/forwardRef(function (_ref2, ref) {
967
1061
  logFetcherResult.setIsUserBrowsingHistory(true);
968
1062
  groupManager.toggleGroup(name);
969
1063
  });
970
- }, [groups, groupManager, suppressFollowForGroupAction, logFetcherResult, maintainScrollPosition]);
1064
+ }, [groups, groupManager, suppressFollowForGroupAction, logFetcherResult, maintainScrollPosition, waitForFetchUnlock]);
971
1065
  var handleCollapseAll = useCallback(function () {
972
1066
  lastGroupToggleAtRef.current = Date.now();
973
1067
  maintainScrollPosition(function () {
@@ -1106,6 +1200,7 @@ var PipelineLogViewer = /*#__PURE__*/forwardRef(function (_ref2, ref) {
1106
1200
  absoluteRow: line.absoluteRow,
1107
1201
  lineNumberLabel: lineNumberLabel,
1108
1202
  text: line.text,
1203
+ logTime: line.logTime,
1109
1204
  isMatched: isMatched,
1110
1205
  isActiveMatch: isActiveMatch,
1111
1206
  lineNumberWidth: lineNumberWidthRef.current,
@@ -1137,10 +1232,10 @@ var PipelineLogViewer = /*#__PURE__*/forwardRef(function (_ref2, ref) {
1137
1232
  useImperativeHandle(ref, function () {
1138
1233
  return {
1139
1234
  scrollToTop: function () {
1140
- var _scrollToTop = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee5() {
1235
+ var _scrollToTop = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee7() {
1141
1236
  var globalFirstRow, bufStart, needsTopWindow, ok;
1142
- return _regeneratorRuntime().wrap(function _callee5$(_context5) {
1143
- while (1) switch (_context5.prev = _context5.next) {
1237
+ return _regeneratorRuntime().wrap(function _callee7$(_context7) {
1238
+ while (1) switch (_context7.prev = _context7.next) {
1144
1239
  case 0:
1145
1240
  groupManager.expandAll();
1146
1241
  userStoppedFollowingRef.current = true;
@@ -1155,64 +1250,46 @@ var PipelineLogViewer = /*#__PURE__*/forwardRef(function (_ref2, ref) {
1155
1250
  needsTopWindow = bufStart > globalFirstRow;
1156
1251
  ignoreBoundaryFetchRef.current = Date.now() + 500;
1157
1252
  if (!needsTopWindow) {
1158
- _context5.next = 22;
1253
+ _context7.next = 21;
1159
1254
  break;
1160
1255
  }
1161
- // airdropLoad 完成时 React 尚未提交新 displayLogs,单帧 rAF 里 scrollToIndex(0)
1162
- // 仍作用在旧列表上(index 0 = 旧缓冲首行),无法置顶;与搜索跨页一致走 pending。
1163
1256
  pendingScrollAlignRef.current = 'start';
1164
1257
  pendingScrollRowRef.current = globalFirstRow;
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;
1258
+ _context7.next = 12;
1259
+ return waitForFetchUnlock();
1260
+ case 12:
1261
+ _context7.next = 14;
1185
1262
  return logFetcherResult.airdropLoad(globalFirstRow, {
1186
1263
  window: 'fromStart'
1187
1264
  });
1188
- case 15:
1189
- ok = _context5.sent;
1265
+ case 14:
1266
+ ok = _context7.sent;
1190
1267
  if (ok) {
1191
- _context5.next = 20;
1268
+ _context7.next = 19;
1192
1269
  break;
1193
1270
  }
1194
1271
  pendingScrollRowRef.current = null;
1195
1272
  pendingScrollAlignRef.current = 'center';
1196
- return _context5.abrupt("return");
1197
- case 20:
1273
+ return _context7.abrupt("return");
1274
+ case 19:
1198
1275
  logFetcherResult.updateBoundaryLock({
1199
1276
  isLastPage: false
1200
1277
  });
1201
- return _context5.abrupt("return");
1202
- case 22:
1278
+ return _context7.abrupt("return");
1279
+ case 21:
1203
1280
  requestAnimationFrame(function () {
1204
- var _virtuosoRef$current6;
1205
- (_virtuosoRef$current6 = virtuosoRef.current) === null || _virtuosoRef$current6 === void 0 || _virtuosoRef$current6.scrollToIndex({
1281
+ var _virtuosoRef$current8;
1282
+ (_virtuosoRef$current8 = virtuosoRef.current) === null || _virtuosoRef$current8 === void 0 || _virtuosoRef$current8.scrollToIndex({
1206
1283
  index: 0,
1207
1284
  align: 'start',
1208
1285
  behavior: 'auto'
1209
1286
  });
1210
1287
  });
1211
- case 23:
1288
+ case 22:
1212
1289
  case "end":
1213
- return _context5.stop();
1290
+ return _context7.stop();
1214
1291
  }
1215
- }, _callee5);
1292
+ }, _callee7);
1216
1293
  }));
1217
1294
  function scrollToTop() {
1218
1295
  return _scrollToTop.apply(this, arguments);
@@ -1220,20 +1297,20 @@ var PipelineLogViewer = /*#__PURE__*/forwardRef(function (_ref2, ref) {
1220
1297
  return scrollToTop;
1221
1298
  }(),
1222
1299
  scrollToBottom: function () {
1223
- var _scrollToBottom = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee6() {
1300
+ var _scrollToBottom = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee8() {
1224
1301
  var needsFetch;
1225
- return _regeneratorRuntime().wrap(function _callee6$(_context6) {
1226
- while (1) switch (_context6.prev = _context6.next) {
1302
+ return _regeneratorRuntime().wrap(function _callee8$(_context8) {
1303
+ while (1) switch (_context8.prev = _context8.next) {
1227
1304
  case 0:
1228
1305
  groupManager.expandAll();
1229
1306
  userStoppedFollowingRef.current = false;
1230
1307
  collapseAirdropActiveRef.current = false;
1231
1308
  needsFetch = status !== 'running' && !logFetcherResult.boundaryLock.isLastPage || status === 'running' && logFetcherResult.isUserBrowsingHistory;
1232
1309
  if (!needsFetch) {
1233
- _context6.next = 8;
1310
+ _context8.next = 8;
1234
1311
  break;
1235
1312
  }
1236
- _context6.next = 7;
1313
+ _context8.next = 7;
1237
1314
  return logFetcherResult.initLoad();
1238
1315
  case 7:
1239
1316
  logFetcherResult.updateBoundaryLock({
@@ -1242,17 +1319,17 @@ var PipelineLogViewer = /*#__PURE__*/forwardRef(function (_ref2, ref) {
1242
1319
  case 8:
1243
1320
  ignoreBoundaryFetchRef.current = Date.now() + 500;
1244
1321
  setTimeout(function () {
1245
- var _virtuosoRef$current7;
1246
- (_virtuosoRef$current7 = virtuosoRef.current) === null || _virtuosoRef$current7 === void 0 || _virtuosoRef$current7.scrollToIndex({
1322
+ var _virtuosoRef$current9;
1323
+ (_virtuosoRef$current9 = virtuosoRef.current) === null || _virtuosoRef$current9 === void 0 || _virtuosoRef$current9.scrollToIndex({
1247
1324
  index: 'LAST',
1248
1325
  align: 'end'
1249
1326
  });
1250
1327
  }, 50);
1251
1328
  case 10:
1252
1329
  case "end":
1253
- return _context6.stop();
1330
+ return _context8.stop();
1254
1331
  }
1255
- }, _callee6);
1332
+ }, _callee8);
1256
1333
  }));
1257
1334
  function scrollToBottom() {
1258
1335
  return _scrollToBottom.apply(this, arguments);
@@ -7,6 +7,8 @@ interface LogRowProps {
7
7
  lineNumberLabel?: number | null;
8
8
  /** 日志文本 (可能包含 ANSI 转 HTML 后的内容) */
9
9
  text: string;
10
+ /** 日志打印时间 */
11
+ logTime?: number;
10
12
  /** 是否为搜索匹配行 */
11
13
  isMatched?: boolean;
12
14
  /** 是否为当前聚焦的匹配行 */
@@ -1 +1 @@
1
- {"version":3,"file":"LogRow.d.ts","sourceRoot":"","sources":["../../../../src/PipelineLogViewer/components/LogRow.tsx"],"names":[],"mappings":"AAUA,OAAO,KAAkB,MAAM,OAAO,CAAC;AACvC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAmBlD,UAAU,WAAW;IACnB,aAAa;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,uDAAuD;IACvD,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,mCAAmC;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,eAAe;IACf,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,kBAAkB;IAClB,aAAa,EAAE,OAAO,CAAC;IACvB,aAAa;IACb,eAAe,EAAE,MAAM,CAAC;IACxB,aAAa;IACb,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,wBAAwB;IACxB,gBAAgB,CAAC,EAAE,iBAAiB,EAAE,CAAC;IACvC,8CAA8C;IAC9C,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;;AA4DD,wBAAkC"}
1
+ {"version":3,"file":"LogRow.d.ts","sourceRoot":"","sources":["../../../../src/PipelineLogViewer/components/LogRow.tsx"],"names":[],"mappings":"AAUA,OAAO,KAAkB,MAAM,OAAO,CAAC;AACvC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AA4BlD,UAAU,WAAW;IACnB,aAAa;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,uDAAuD;IACvD,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,mCAAmC;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,aAAa;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,eAAe;IACf,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,kBAAkB;IAClB,aAAa,EAAE,OAAO,CAAC;IACvB,aAAa;IACb,eAAe,EAAE,MAAM,CAAC;IACxB,aAAa;IACb,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,wBAAwB;IACxB,gBAAgB,CAAC,EAAE,iBAAiB,EAAE,CAAC;IACvC,8CAA8C;IAC9C,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;;AAiED,wBAAkC"}
@@ -10,6 +10,14 @@
10
10
 
11
11
  import React, { useMemo } from 'react';
12
12
  import wrapSearchKeywordInHtml from "../utils/wrapSearchKeywordInHtml";
13
+ function formatLogTime(ts) {
14
+ var ms = ts > 1e15 ? Math.floor(ts / 1000) : ts;
15
+ var d = new Date(ms);
16
+ var pad = function pad(n) {
17
+ return String(n).padStart(2, '0');
18
+ };
19
+ return "[".concat(d.getFullYear(), "-").concat(pad(d.getMonth() + 1), "-").concat(pad(d.getDate()), " ").concat(pad(d.getHours()), ":").concat(pad(d.getMinutes()), ":").concat(pad(d.getSeconds()), "]");
20
+ }
13
21
  function findHighlightOption(htmlForMatch, options) {
14
22
  if (!(options !== null && options !== void 0 && options.length)) return undefined;
15
23
  return options.find(function (option) {
@@ -26,6 +34,7 @@ var LogRow = function LogRow(_ref) {
26
34
  var absoluteRow = _ref.absoluteRow,
27
35
  lineNumberLabel = _ref.lineNumberLabel,
28
36
  text = _ref.text,
37
+ logTime = _ref.logTime,
29
38
  isMatched = _ref.isMatched,
30
39
  isActiveMatch = _ref.isActiveMatch,
31
40
  lineNumberWidth = _ref.lineNumberWidth,
@@ -60,7 +69,9 @@ var LogRow = function LogRow(_ref) {
60
69
  style: {
61
70
  minWidth: "".concat(lineNumberWidth, "px")
62
71
  }
63
- }, lineNumberLabel === null ? '' : lineNumberLabel !== null && lineNumberLabel !== void 0 ? lineNumberLabel : absoluteRow), /*#__PURE__*/React.createElement("span", {
72
+ }, lineNumberLabel === null ? '' : lineNumberLabel !== null && lineNumberLabel !== void 0 ? lineNumberLabel : absoluteRow), logTime != null && logTime > 0 && /*#__PURE__*/React.createElement("span", {
73
+ className: "pipeline-log-row__log-time"
74
+ }, formatLogTime(logTime)), /*#__PURE__*/React.createElement("span", {
64
75
  className: "pipeline-log-row__text",
65
76
  style: hlStyle,
66
77
  dangerouslySetInnerHTML: {
@@ -12,7 +12,9 @@ export interface SearchBarProps {
12
12
  onNext: () => void;
13
13
  /** 清除搜索 */
14
14
  onClear: () => void;
15
- /** 搜索框 placeholder */
15
+ /**
16
+ * 搜索框 placeholder;默认含快捷键说明,可被 `searchPlaceholder` 等覆盖
17
+ */
16
18
  placeholder?: string;
17
19
  /** 搜索栏前置内容 */
18
20
  inputAddonBefore?: React.ReactNode;
@@ -1 +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"}
1
+ {"version":3,"file":"SearchBar.d.ts","sourceRoot":"","sources":["../../../../src/PipelineLogViewer/components/SearchBar.tsx"],"names":[],"mappings":"AAoBA,OAAO,KAAiD,MAAM,OAAO,CAAC;AAKtE,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;;OAEG;IACH,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;;AAwND,wBAAqC"}
@@ -8,17 +8,21 @@ function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
8
8
  // SearchBar — 日志搜索工具栏组件
9
9
  //
10
10
  // 交互行为设计:
11
- // 1. 用户输入关键字后,必须按下 Enter 键或点击图标才会触发全新的查询。
12
- // 2. 使用 lastSearchedValue 缓存最后一次成功搜索的关键字。
11
+ // 1. 用户输入关键字后,须按 Enter 触发查询(关键字 trim 后非空);左侧图标仅装饰。
12
+ // 2. 使用 lastSubmittedKeyword 缓存最后一次成功提交的关键字(trim 后)。
13
13
  // 3. 按下 Enter 时:
14
- // - 若当前输入值与 lastSearchedValue 不同,触发全新查询。
15
- // - 若当前输入值与 lastSearchedValue 相同,则复用当前结果,触发 onNext(下一项) 操作。
16
- // - 若按住 Shift + Enter,触发 onPrev(上一项) 操作。
17
- // 4. 输入框内容被完全清空或按下 Escape 键时,触发 onClear 清除状态与高亮。
14
+ // - 若当前 trim 值与 lastSubmittedKeyword 不同,触发全新查询。
15
+ // - 若相同,则复用当前结果,触发 onNext(下一项);Shift+Enter 触发 onPrev。
16
+ // 4. 输入清空或 Escape 时触发 onClear。
17
+ // 5. 关键字已改但未提交时显示 Enter 轻提示;上下匹配按钮支持键盘操作与 aria。
18
18
  // ============================================================================
19
19
 
20
20
  import { CloseCircleFilled, DownOutlined, LoadingOutlined, SearchOutlined, UpOutlined } from '@ant-design/icons';
21
- import React, { useCallback, useRef, useState } from 'react';
21
+ import React, { useCallback, useMemo, useRef, useState } from 'react';
22
+ var DIRTY_HINT_ID = 'pipeline-log-search-dirty-hint';
23
+ var NAV_HINT_ID = 'pipeline-log-search-nav-hint';
24
+ /** 空框时即可见:搜索与「同词跳转」快捷键 */
25
+ var DEFAULT_PLACEHOLDER = '搜索日志(Enter 搜索;关键字不变时 Enter 下一处,Shift+Enter 上一处)';
22
26
  var SearchBar = function SearchBar(_ref) {
23
27
  var total = _ref.total,
24
28
  currentIndex = _ref.currentIndex,
@@ -27,7 +31,7 @@ var SearchBar = function SearchBar(_ref) {
27
31
  onNext = _ref.onNext,
28
32
  onClear = _ref.onClear,
29
33
  _ref$placeholder = _ref.placeholder,
30
- placeholder = _ref$placeholder === void 0 ? '搜索日志...' : _ref$placeholder,
34
+ placeholder = _ref$placeholder === void 0 ? DEFAULT_PLACEHOLDER : _ref$placeholder,
31
35
  inputAddonBefore = _ref.inputAddonBefore,
32
36
  inputAddonAfter = _ref.inputAddonAfter,
33
37
  icons = _ref.icons,
@@ -37,23 +41,31 @@ var SearchBar = function SearchBar(_ref) {
37
41
  _useState2 = _slicedToArray(_useState, 2),
38
42
  inputValue = _useState2[0],
39
43
  setInputValue = _useState2[1];
44
+ /** 最近一次成功提交给 onSearch 的关键字(trim 后),用于同词 Enter 跳转与 dirty 提示 */
45
+ var _useState3 = useState(''),
46
+ _useState4 = _slicedToArray(_useState3, 2),
47
+ lastSubmittedKeyword = _useState4[0],
48
+ setLastSubmittedKeyword = _useState4[1];
40
49
  var inputRef = useRef(null);
41
- var lastSearchedValue = useRef('');
50
+ var trimmedInput = inputValue.trim();
51
+ var isDirty = trimmedInput !== '' && trimmedInput !== lastSubmittedKeyword;
52
+ /** 已提交过且仍有结果:提示在输入框内用 Enter / Shift+Enter 跳转 */
53
+ var showNavHint = total > 0 && trimmedInput !== '' && !isDirty;
42
54
 
43
- // 回车或点击搜索按钮触发
55
+ // Enter 触发搜索提交
44
56
  var handleSearch = useCallback(function () {
45
- if (inputValue) {
46
- lastSearchedValue.current = inputValue;
47
- onSearch(inputValue);
48
- }
57
+ var q = inputValue.trim();
58
+ if (!q) return;
59
+ setLastSubmittedKeyword(q);
60
+ onSearch(q);
49
61
  }, [inputValue, onSearch]);
50
62
 
51
63
  // 键盘事件:Enter 搜索, Escape 清除
52
64
  var handleKeyDown = useCallback(function (e) {
53
65
  if (e.key === 'Enter') {
54
66
  e.preventDefault();
55
- // 如果值发生了变化,优先执行搜索
56
- if (inputValue !== lastSearchedValue.current) {
67
+ var t = inputValue.trim();
68
+ if (t !== lastSubmittedKeyword) {
57
69
  handleSearch();
58
70
  } else if (e.shiftKey) {
59
71
  onPrev();
@@ -64,17 +76,17 @@ var SearchBar = function SearchBar(_ref) {
64
76
  }
65
77
  } else if (e.key === 'Escape') {
66
78
  setInputValue('');
67
- lastSearchedValue.current = '';
79
+ setLastSubmittedKeyword('');
68
80
  onClear();
69
81
  }
70
- }, [handleSearch, onPrev, onNext, onClear, total, inputValue]);
82
+ }, [handleSearch, onPrev, onNext, onClear, total, inputValue, lastSubmittedKeyword]);
71
83
 
72
84
  // 输入变化
73
85
  var handleChange = useCallback(function (e) {
74
86
  var val = e.target.value;
75
87
  setInputValue(val);
76
88
  if (!val) {
77
- lastSearchedValue.current = '';
89
+ setLastSubmittedKeyword('');
78
90
  onClear();
79
91
  }
80
92
  }, [onClear]);
@@ -83,24 +95,49 @@ var SearchBar = function SearchBar(_ref) {
83
95
  var handleClear = useCallback(function () {
84
96
  var _inputRef$current;
85
97
  setInputValue('');
86
- lastSearchedValue.current = '';
98
+ setLastSubmittedKeyword('');
87
99
  onClear();
88
100
  (_inputRef$current = inputRef.current) === null || _inputRef$current === void 0 || _inputRef$current.focus();
89
101
  }, [onClear]);
90
- var searchPrefixIcon;
91
- if (searchLoading) {
92
- searchPrefixIcon = /*#__PURE__*/React.createElement(LoadingOutlined, {
102
+ var handlePrevClick = useCallback(function () {
103
+ if (total > 0) onPrev();
104
+ }, [total, onPrev]);
105
+ var handleNextClick = useCallback(function () {
106
+ if (total > 0) onNext();
107
+ }, [total, onNext]);
108
+ var handleNavKeyDown = useCallback(function (e, action) {
109
+ if (total <= 0) return;
110
+ if (e.key === 'Enter' || e.key === ' ') {
111
+ e.preventDefault();
112
+ action();
113
+ }
114
+ }, [total]);
115
+ var searchPrefixIcon = useMemo(function () {
116
+ if (searchLoading) {
117
+ return /*#__PURE__*/React.createElement(LoadingOutlined, {
118
+ className: "pipeline-log-search-bar__search-icon",
119
+ spin: true,
120
+ "aria-hidden": true
121
+ });
122
+ }
123
+ if ( /*#__PURE__*/React.isValidElement(icons === null || icons === void 0 ? void 0 : icons.search)) {
124
+ return /*#__PURE__*/React.cloneElement(icons === null || icons === void 0 ? void 0 : icons.search, {
125
+ className: 'pipeline-log-search-bar__search-icon',
126
+ 'aria-hidden': true
127
+ });
128
+ }
129
+ return /*#__PURE__*/React.createElement(SearchOutlined, {
93
130
  className: "pipeline-log-search-bar__search-icon",
94
- spin: true
95
- });
96
- } else if ( /*#__PURE__*/React.isValidElement(icons === null || icons === void 0 ? void 0 : icons.search)) {
97
- searchPrefixIcon = /*#__PURE__*/React.cloneElement(icons === null || icons === void 0 ? void 0 : icons.search, {
98
- className: 'pipeline-log-search-bar__search-icon'
131
+ "aria-hidden": true
99
132
  });
133
+ }, [icons === null || icons === void 0 ? void 0 : icons.search, searchLoading]);
134
+ var inputDescribedBy;
135
+ if (isDirty) {
136
+ inputDescribedBy = DIRTY_HINT_ID;
137
+ } else if (showNavHint) {
138
+ inputDescribedBy = NAV_HINT_ID;
100
139
  } else {
101
- searchPrefixIcon = /*#__PURE__*/React.createElement(SearchOutlined, {
102
- className: "pipeline-log-search-bar__search-icon"
103
- });
140
+ inputDescribedBy = undefined;
104
141
  }
105
142
  return /*#__PURE__*/React.createElement("div", {
106
143
  className: "pipeline-log-search-bar"
@@ -109,7 +146,10 @@ var SearchBar = function SearchBar(_ref) {
109
146
  }, inputAddonBefore), /*#__PURE__*/React.createElement("div", {
110
147
  className: "pipeline-log-search-bar__input-wrapper",
111
148
  "aria-busy": searchLoading || undefined
112
- }, searchPrefixIcon, /*#__PURE__*/React.createElement("input", {
149
+ }, /*#__PURE__*/React.createElement("span", {
150
+ className: ['pipeline-log-search-bar__search-prefix', trimmedInput && 'pipeline-log-search-bar__search-prefix--active'].filter(Boolean).join(' '),
151
+ "aria-hidden": "true"
152
+ }, searchPrefixIcon), /*#__PURE__*/React.createElement("input", {
113
153
  ref: inputRef,
114
154
  className: "pipeline-log-search-bar__input",
115
155
  type: "text",
@@ -117,22 +157,46 @@ var SearchBar = function SearchBar(_ref) {
117
157
  value: inputValue,
118
158
  onChange: handleChange,
119
159
  onKeyDown: handleKeyDown,
120
- "aria-label": "\u641C\u7D22\u65E5\u5FD7"
160
+ "aria-label": "\u641C\u7D22\u65E5\u5FD7\u5173\u952E\u5B57",
161
+ "aria-describedby": inputDescribedBy
121
162
  }), /*#__PURE__*/React.createElement("div", {
122
163
  className: "pipeline-log-search-bar__input-action"
123
- }, !!inputValue && /*#__PURE__*/React.createElement("span", {
164
+ }, !!inputValue && /*#__PURE__*/React.createElement(React.Fragment, null, !isDirty && /*#__PURE__*/React.createElement("span", {
124
165
  className: "pipeline-log-search-bar__result-count"
125
- }, "".concat(total > 0 ? currentIndex + 1 : 0, "/").concat(total >= 1000 ? '1000+' : total)), !!inputValue && /*#__PURE__*/React.createElement("span", {
126
- onClick: handleClear
166
+ }, "".concat(total > 0 ? currentIndex + 1 : 0, "/").concat(total >= 1000 ? '1000+' : total)), isDirty && /*#__PURE__*/React.createElement("span", {
167
+ id: DIRTY_HINT_ID,
168
+ className: "pipeline-log-search-bar__dirty-hint pipeline-log-search-bar__dirty-hint--pulse"
169
+ }, "\u6309 Enter \u641C\u7D22"), showNavHint && /*#__PURE__*/React.createElement("span", {
170
+ id: NAV_HINT_ID,
171
+ className: "pipeline-log-search-bar__nav-hint"
172
+ }, "Enter \u4E0B\u4E00\u5904 \xB7 Shift+Enter \u4E0A\u4E00\u5904")), !!inputValue && /*#__PURE__*/React.createElement("span", {
173
+ onClick: handleClear,
174
+ role: "presentation"
127
175
  }, (icons === null || icons === void 0 ? void 0 : icons.clear) || /*#__PURE__*/React.createElement(CloseCircleFilled, {
128
176
  className: "pipeline-log-search-bar__clear"
129
- })), /*#__PURE__*/React.createElement("span", {
177
+ })), !isDirty && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("span", {
130
178
  className: "pipeline-log-search-bar__tool",
131
- onClick: total > 0 ? onPrev : undefined
179
+ role: "button",
180
+ tabIndex: total > 0 ? 0 : -1,
181
+ title: "\u4E0A\u4E00\u5904\u5339\u914D\uFF08Shift+Enter\uFF09",
182
+ "aria-label": "\u4E0A\u4E00\u5904\u5339\u914D\uFF0C\u5FEB\u6377\u952E Shift+Enter",
183
+ "aria-disabled": total <= 0 || undefined,
184
+ onClick: handlePrevClick,
185
+ onKeyDown: function onKeyDown(e) {
186
+ return handleNavKeyDown(e, handlePrevClick);
187
+ }
132
188
  }, (icons === null || icons === void 0 ? void 0 : icons.up) || /*#__PURE__*/React.createElement(UpOutlined, null)), /*#__PURE__*/React.createElement("span", {
133
189
  className: "pipeline-log-search-bar__tool",
134
- onClick: total > 0 ? onNext : undefined
135
- }, (icons === null || icons === void 0 ? void 0 : icons.down) || /*#__PURE__*/React.createElement(DownOutlined, null)))), /*#__PURE__*/React.createElement("div", {
190
+ role: "button",
191
+ tabIndex: total > 0 ? 0 : -1,
192
+ title: "\u4E0B\u4E00\u5904\u5339\u914D\uFF08Enter\uFF09",
193
+ "aria-label": "\u4E0B\u4E00\u5904\u5339\u914D\uFF0CEnter",
194
+ "aria-disabled": total <= 0 || undefined,
195
+ onClick: handleNextClick,
196
+ onKeyDown: function onKeyDown(e) {
197
+ return handleNavKeyDown(e, handleNextClick);
198
+ }
199
+ }, (icons === null || icons === void 0 ? void 0 : icons.down) || /*#__PURE__*/React.createElement(DownOutlined, null))))), /*#__PURE__*/React.createElement("div", {
136
200
  className: "pipeline-log-search-bar__input-suffix"
137
201
  }, inputAddonAfter));
138
202
  };
@@ -1 +1 @@
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"}
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,CAgM5E;AAED,eAAe,cAAc,CAAC"}
@@ -53,7 +53,8 @@ function useDisplayLogs(options) {
53
53
  var main = (_logs$map = logs === null || logs === void 0 ? void 0 : logs.map(function (logEntry, i) {
54
54
  return {
55
55
  absoluteRow: firstItemIndex + i,
56
- text: logEntry.log
56
+ text: logEntry.log,
57
+ logTime: logEntry.logTime
57
58
  };
58
59
  })) !== null && _logs$map !== void 0 ? _logs$map : [];
59
60
  if (logs.length === 0) return main;
@@ -189,6 +190,7 @@ function useDisplayLogs(options) {
189
190
  result.push({
190
191
  absoluteRow: absoluteRow,
191
192
  text: logs[i].log,
193
+ logTime: logs[i].logTime,
192
194
  isFoldedGroupStart: true
193
195
  });
194
196
  }
@@ -209,7 +211,8 @@ function useDisplayLogs(options) {
209
211
  }
210
212
  result.push({
211
213
  absoluteRow: absoluteRow,
212
- text: logs[i].log
214
+ text: logs[i].log,
215
+ logTime: logs[i].logTime
213
216
  });
214
217
  }
215
218
 
@@ -183,10 +183,22 @@
183
183
  }
184
184
  }
185
185
 
186
+ .pipeline-log-search-bar__search-prefix {
187
+ display: inline-flex;
188
+ flex-shrink: 0;
189
+ align-items: center;
190
+ line-height: 0;
191
+ }
192
+
186
193
  .pipeline-log-search-bar__search-icon {
187
194
  margin-right: 6px;
188
- color: #fff;
195
+ color: rgba(255, 255, 255, 0.35);
189
196
  font-size: 16px;
197
+ transition: color 0.2s ease;
198
+ }
199
+
200
+ .pipeline-log-search-bar__search-prefix--active .pipeline-log-search-bar__search-icon {
201
+ color: #fff;
190
202
  }
191
203
  }
192
204
 
@@ -213,9 +225,51 @@ input.pipeline-log-search-bar__input {
213
225
 
214
226
  .pipeline-log-search-bar__input-action {
215
227
  display: flex;
228
+ flex-wrap: wrap;
229
+ gap: 4px 8px;
216
230
  align-items: center;
217
231
  }
218
232
 
233
+ .pipeline-log-search-bar__dirty-hint,
234
+ .pipeline-log-search-bar__nav-hint {
235
+ margin-right: 4px;
236
+ color: #959da5bf;
237
+ font-size: 11px;
238
+ line-height: 1.2;
239
+ white-space: nowrap;
240
+ }
241
+
242
+ /* 仅动画 opacity:比同时改 color 更易被浏览器合成、肉眼更明显 */
243
+ @keyframes pipeline-log-search-dirty-pulse {
244
+ 0%,
245
+ 100% {
246
+ opacity: 1;
247
+ }
248
+
249
+ 50% {
250
+ opacity: 0.22;
251
+ }
252
+ }
253
+
254
+ /* 双类名提高优先级,覆盖 .dirty-hint 的灰色;动效关闭时仍保持醒目 */
255
+ .pipeline-log-search-bar__dirty-hint.pipeline-log-search-bar__dirty-hint--pulse {
256
+ color: #e6edf3;
257
+ font-weight: 600;
258
+ letter-spacing: 0.02em;
259
+ animation: pipeline-log-search-dirty-pulse 0.75s ease-in-out infinite;
260
+ }
261
+
262
+ @media (prefers-reduced-motion: reduce) {
263
+ .pipeline-log-search-bar__dirty-hint.pipeline-log-search-bar__dirty-hint--pulse {
264
+ color: #e6edf3;
265
+ text-decoration: underline;
266
+ opacity: 1;
267
+ animation: none;
268
+ text-underline-offset: 2px;
269
+ text-decoration-color: rgba(230, 237, 243, 0.55);
270
+ }
271
+ }
272
+
219
273
  /*
220
274
  * 容器 / 视口 ≤1200px:首行仅前缀(元数据等),第二行搜索框 + 后缀同一行(避免 ~900px 仍单行重叠)。
221
275
  * @media 作无 container 支持时的回退。
@@ -301,13 +355,26 @@ input.pipeline-log-search-bar__input {
301
355
  padding: 2px 4px;
302
356
  font-size: 12px;
303
357
  border-radius: 4px;
358
+ outline: none;
359
+ cursor: default;
304
360
  .anticon {
305
361
  transform: scaleY(0.8);
306
362
  }
307
- &:hover {
363
+ &:hover:not([aria-disabled='true']) {
308
364
  background-color: rgba(77, 77, 77, 1);
309
365
  transition: all 0.3s;
310
366
  }
367
+ &:focus-visible {
368
+ box-shadow: 0 0 0 2px rgba(12, 98, 255, 0.45);
369
+ }
370
+ &[aria-disabled='true'] {
371
+ cursor: not-allowed;
372
+ opacity: 0.45;
373
+ }
374
+
375
+ &:not([aria-disabled='true']) {
376
+ cursor: pointer;
377
+ }
311
378
  }
312
379
 
313
380
  .pipeline-log-search-bar__refresh {
@@ -332,6 +399,10 @@ input.pipeline-log-search-bar__input {
332
399
  z-index: 10;
333
400
  box-sizing: border-box;
334
401
  background: #2e2e2e;
402
+
403
+ .terminal-viewer-virtuoso-list-row-step-time {
404
+ margin-right: -2px;
405
+ }
335
406
  }
336
407
 
337
408
  /* --------------------------------------------------------------------------
@@ -474,7 +545,6 @@ input.pipeline-log-search-bar__input {
474
545
  .pipeline-log-row__line-number {
475
546
  display: inline-block;
476
547
  flex-shrink: 0;
477
- margin-right: 4px;
478
548
  padding-right: 8px;
479
549
  overflow: hidden;
480
550
  color: #959da5bf;
@@ -486,6 +556,17 @@ input.pipeline-log-search-bar__input {
486
556
  user-select: none;
487
557
  }
488
558
 
559
+ .pipeline-log-row__log-time {
560
+ display: inline-block;
561
+ flex-shrink: 0;
562
+ margin-right: 8px;
563
+ color: #959da5bf;
564
+ font-size: 12px;
565
+ line-height: 20px;
566
+ white-space: nowrap;
567
+ user-select: none;
568
+ }
569
+
489
570
  .pipeline-log-row__text {
490
571
  display: inline-block;
491
572
  flex: 1 1 0;
@@ -106,6 +106,8 @@ export interface DisplayLogLine {
106
106
  absoluteRow: number;
107
107
  /** 日志文本 (或折叠占位标记) */
108
108
  text: string;
109
+ /** 日志打印时间 (ms) */
110
+ logTime?: number;
109
111
  /** 是否为折叠的分组的起始行(用于隐藏对应的 LogRow) */
110
112
  isFoldedGroupStart?: boolean;
111
113
  /** 分组在缓冲外且处于展开态时的占位行(避免仅显示标题却无正文) */
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/PipelineLogViewer/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/PipelineLogViewer/types.ts"],"names":[],"mappings":"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"}
@@ -7,6 +7,8 @@ interface LogRowProps {
7
7
  lineNumberLabel?: number | null;
8
8
  /** 日志文本 (可能包含 ANSI 转 HTML 后的内容) */
9
9
  text: string;
10
+ /** 日志打印时间 */
11
+ logTime?: number;
10
12
  /** 是否为搜索匹配行 */
11
13
  isMatched?: boolean;
12
14
  /** 是否为当前聚焦的匹配行 */
@@ -12,7 +12,9 @@ export interface SearchBarProps {
12
12
  onNext: () => void;
13
13
  /** 清除搜索 */
14
14
  onClear: () => void;
15
- /** 搜索框 placeholder */
15
+ /**
16
+ * 搜索框 placeholder;默认含快捷键说明,可被 `searchPlaceholder` 等覆盖
17
+ */
16
18
  placeholder?: string;
17
19
  /** 搜索栏前置内容 */
18
20
  inputAddonBefore?: React.ReactNode;
@@ -106,6 +106,8 @@ export interface DisplayLogLine {
106
106
  absoluteRow: number;
107
107
  /** 日志文本 (或折叠占位标记) */
108
108
  text: string;
109
+ /** 日志打印时间 (ms) */
110
+ logTime?: number;
109
111
  /** 是否为折叠的分组的起始行(用于隐藏对应的 LogRow) */
110
112
  isFoldedGroupStart?: boolean;
111
113
  /** 分组在缓冲外且处于展开态时的占位行(避免仅显示标题却无正文) */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-terminal-viewer-cicd",
3
- "version": "3.0.0-beta.41",
3
+ "version": "3.0.0-beta.42",
4
4
  "author": "https://gitee.com/gitee-frontend",
5
5
  "license": "MIT",
6
6
  "keywords": [