@workflow/web-shared 4.1.5 → 4.1.7

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,4 +1,17 @@
1
1
  import type { Event, WorkflowRun } from '@workflow/world';
2
+ import { type ExactIdSearchResult, type ExactWorkflowSearchIdKind } from '../lib/exact-event-search-id';
3
+ export interface DurationInfo {
4
+ /** Time from created → started (ms) */
5
+ queued?: number;
6
+ /** Time from started → completed/failed/cancelled (ms) */
7
+ ran?: number;
8
+ }
9
+ /**
10
+ * Build a map from correlationId → duration info by diffing
11
+ * created ↔ started (queued) and started ↔ completed/failed/cancelled (ran).
12
+ * Also computes run-level durations under the key '__run__'.
13
+ */
14
+ export declare function buildDurationMap(events: Event[]): Map<string, DurationInfo>;
2
15
  interface EventsListProps {
3
16
  events: Event[] | null;
4
17
  run?: WorkflowRun | null;
@@ -21,7 +34,9 @@ interface EventsListProps {
21
34
  isDecrypting?: boolean;
22
35
  /** Run-level hint: the run contains encrypted data (from probe). */
23
36
  hasEncryptedData?: boolean;
37
+ /** Fetch events for an exact correlation or event ID. */
38
+ onExactIdSearch?: (id: string, kind: ExactWorkflowSearchIdKind, signal?: AbortSignal) => Promise<ExactIdSearchResult>;
24
39
  }
25
- export declare function EventListView({ events, run, onLoadEventData, hasMoreEvents, isLoadingMoreEvents, onLoadMoreEvents, encryptionKey, isLoading, sortOrder: sortOrderProp, onSortOrderChange, onDecrypt, isDecrypting, hasEncryptedData: hasEncryptedDataProp, }: EventsListProps): import("react/jsx-runtime").JSX.Element;
40
+ export declare function EventListView({ events, run, onLoadEventData, hasMoreEvents, isLoadingMoreEvents, onLoadMoreEvents, encryptionKey, isLoading, sortOrder: sortOrderProp, onSortOrderChange, onDecrypt, isDecrypting, hasEncryptedData: hasEncryptedDataProp, onExactIdSearch, }: EventsListProps): import("react/jsx-runtime").JSX.Element;
26
41
  export {};
27
42
  //# sourceMappingURL=event-list-view.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"event-list-view.d.ts","sourceRoot":"","sources":["../../src/components/event-list-view.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AA0qB1D,UAAU,eAAe;IACvB,MAAM,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC;IACvB,GAAG,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;IACzB,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;IAC5D,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,gBAAgB,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAC9C,6FAA6F;IAC7F,aAAa,CAAC,EAAE,UAAU,CAAC;IAC3B,oFAAoF;IACpF,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,gDAAgD;IAChD,SAAS,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IAC3B;iFAC6E;IAC7E,iBAAiB,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,GAAG,MAAM,KAAK,IAAI,CAAC;IACpD,sDAAsD;IACtD,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB,6DAA6D;IAC7D,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,oEAAoE;IACpE,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAoaD,wBAAgB,aAAa,CAAC,EAC5B,MAAM,EACN,GAAG,EACH,eAAe,EACf,aAAqB,EACrB,mBAA2B,EAC3B,gBAAgB,EAChB,aAAa,EACb,SAAiB,EACjB,SAAS,EAAE,aAAa,EACxB,iBAAiB,EACjB,SAAS,EACT,YAAoB,EACpB,gBAAgB,EAAE,oBAA4B,GAC/C,EAAE,eAAe,2CAqcjB"}
1
+ {"version":3,"file":"event-list-view.d.ts","sourceRoot":"","sources":["../../src/components/event-list-view.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAU1D,OAAO,EAGL,KAAK,mBAAmB,EACxB,KAAK,yBAAyB,EAC/B,MAAM,8BAA8B,CAAC;AAyItC,MAAM,WAAW,YAAY;IAC3B,uCAAuC;IACvC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,0DAA0D;IAC1D,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAwE3E;AAydD,UAAU,eAAe;IACvB,MAAM,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC;IACvB,GAAG,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;IACzB,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;IAC5D,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,gBAAgB,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAC9C,6FAA6F;IAC7F,aAAa,CAAC,EAAE,UAAU,CAAC;IAC3B,oFAAoF;IACpF,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,gDAAgD;IAChD,SAAS,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IAC3B;iFAC6E;IAC7E,iBAAiB,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,GAAG,MAAM,KAAK,IAAI,CAAC;IACpD,sDAAsD;IACtD,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB,6DAA6D;IAC7D,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,oEAAoE;IACpE,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,yDAAyD;IACzD,eAAe,CAAC,EAAE,CAChB,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,yBAAyB,EAC/B,MAAM,CAAC,EAAE,WAAW,KACjB,OAAO,CAAC,mBAAmB,CAAC,CAAC;CACnC;AAuaD,wBAAgB,aAAa,CAAC,EAC5B,MAAM,EACN,GAAG,EACH,eAAe,EACf,aAAqB,EACrB,mBAA2B,EAC3B,gBAAgB,EAChB,aAAa,EACb,SAAiB,EACjB,SAAS,EAAE,aAAa,EACxB,iBAAiB,EACjB,SAAS,EACT,YAAoB,EACpB,gBAAgB,EAAE,oBAA4B,EAC9C,eAAe,GAChB,EAAE,eAAe,2CAujBjB"}
@@ -5,8 +5,10 @@ import { Check, ChevronRight, Copy } from 'lucide-react';
5
5
  import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
6
6
  import { Virtuoso } from 'react-virtuoso';
7
7
  import { isEncryptedMarker } from '../lib/hydration';
8
+ import { parseExactWorkflowSearchId, looksLikeWorkflowIdSearchInput, } from '../lib/exact-event-search-id';
8
9
  import { DecryptButton } from './ui/decrypt-button';
9
10
  import { formatDuration } from '../lib/utils';
11
+ import { useToast } from '../lib/toast';
10
12
  import { DataInspector, DecryptClickContext } from './ui/data-inspector';
11
13
  import { ErrorStackBlock, isStructuredErrorWithStack, } from './ui/error-stack-block';
12
14
  import { LoadMoreButton } from './ui/load-more-button';
@@ -112,33 +114,46 @@ function buildNameMaps(events, run) {
112
114
  * created ↔ started (queued) and started ↔ completed/failed/cancelled (ran).
113
115
  * Also computes run-level durations under the key '__run__'.
114
116
  */
115
- function buildDurationMap(events) {
117
+ export function buildDurationMap(events) {
118
+ // Process events in chronological order so the result doesn't depend on
119
+ // the caller's sort direction. Retried steps emit multiple `step_started`
120
+ // events for the same correlationId; the queued duration must be measured
121
+ // against the first one, not the last.
122
+ const chronological = [...events].sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime());
116
123
  const createdTimes = new Map();
124
+ const firstStartedTimes = new Map();
117
125
  const startedTimes = new Map();
118
126
  const durations = new Map();
119
- for (const event of events) {
127
+ for (const event of chronological) {
120
128
  const ts = new Date(event.createdAt).getTime();
121
129
  const key = event.correlationId ?? '__run__';
122
130
  const type = event.eventType;
123
131
  // Track created times (first event for each correlation)
124
132
  if (type === 'step_created' || type === 'run_created') {
125
- createdTimes.set(key, ts);
133
+ if (!createdTimes.has(key)) {
134
+ createdTimes.set(key, ts);
135
+ }
126
136
  }
127
137
  // Track started times & compute queued duration
128
138
  if (type === 'step_started' ||
129
139
  type === 'run_started' ||
130
140
  type === 'workflow_started') {
131
141
  startedTimes.set(key, ts);
132
- // If no explicit created event was seen, use the started time as created
133
- if (!createdTimes.has(key)) {
134
- createdTimes.set(key, ts);
135
- }
136
- const createdAt = createdTimes.get(key);
137
- const info = durations.get(key) ?? {};
138
- if (createdAt !== undefined) {
139
- info.queued = ts - createdAt;
142
+ // The queued duration is anchored on the first start event only
143
+ // subsequent step_started events come from retries.
144
+ if (!firstStartedTimes.has(key)) {
145
+ firstStartedTimes.set(key, ts);
146
+ // If no explicit created event was seen, use the started time as created
147
+ if (!createdTimes.has(key)) {
148
+ createdTimes.set(key, ts);
149
+ }
150
+ const createdAt = createdTimes.get(key);
151
+ const info = durations.get(key) ?? {};
152
+ if (createdAt !== undefined) {
153
+ info.queued = ts - createdAt;
154
+ }
155
+ durations.set(key, info);
140
156
  }
141
- durations.set(key, info);
142
157
  }
143
158
  // Compute ran duration on terminal events
144
159
  if (type === 'step_completed' ||
@@ -390,7 +405,7 @@ function RowsSkeleton() {
390
405
  marginLeft: i % 4 === 0 ? 5 : 6,
391
406
  } })] }), _jsx("div", { className: "w-5 flex-shrink-0 flex items-center justify-center", children: _jsx(Skeleton, { className: "w-5 h-5", style: { borderRadius: 4 } }) }), _jsx("div", { className: "min-w-0 px-4", style: { flex: '2 1 0%' }, children: _jsx(Skeleton, { className: "h-3", style: { width: '70%' } }) }), _jsxs("div", { className: "min-w-0 px-4 flex items-center gap-1.5", style: { flex: '2 1 0%' }, children: [_jsx(Skeleton, { className: "flex-shrink-0", style: { width: 6, height: 6, borderRadius: '50%' } }), _jsx(Skeleton, { className: "h-3", style: { width: '60%' } })] }), _jsx("div", { className: "min-w-0 px-4", style: { flex: '2 1 0%' }, children: _jsx(Skeleton, { className: "h-3", style: { width: '50%' } }) }), _jsx("div", { className: "min-w-0 px-4", style: { flex: '3 1 0%' }, children: _jsx(Skeleton, { className: "h-3", style: { width: '75%' } }) }), _jsx("div", { className: "min-w-0 px-4", style: { flex: '3 1 0%' }, children: _jsx(Skeleton, { className: "h-3", style: { width: '75%' } }) })] }, i))) }));
392
407
  }
393
- function EventRow({ event, index, isFirst, isLast, isExpanded, onToggleExpand, activeGroupKey, selectedGroupKey, selectedGroupRange, correlationNameMap, workflowName, durationMap, onSelectGroup, onHoverGroup, onLoadEventData, cachedEventData, onCacheEventData, encryptionKey, onEncryptedDataDetected, }) {
408
+ function EventRow({ event, index, isFirst, isLast, isExpanded, onToggleExpand, activeGroupKey, selectedGroupKey, selectedGroupRange, correlationNameMap, workflowName, durationMap, onSelectGroup, onHoverGroup, onLoadEventData, cachedEventData, onCacheEventData, encryptionKey, onEncryptedDataDetected, suppressGroupDimming = false, }) {
394
409
  const [isLoading, setIsLoading] = useState(false);
395
410
  const [loadedEventData, setLoadedEventData] = useState(cachedEventData);
396
411
  const [loadError, setLoadError] = useState(null);
@@ -420,7 +435,7 @@ function EventRow({ event, index, isFirst, isLast, isExpanded, onToggleExpand, a
420
435
  const durationInfo = durationKey ? durationMap.get(durationKey) : undefined;
421
436
  const hasActive = activeGroupKey !== undefined;
422
437
  const isRelated = rowGroupKey !== undefined && rowGroupKey === activeGroupKey;
423
- const isDimmed = hasActive && !isRelated;
438
+ const isDimmed = hasActive && !isRelated && !suppressGroupDimming;
424
439
  const isPulsing = hasActive && isRelated;
425
440
  // Gutter state derived from selectedGroupRange
426
441
  const showBranch = hasActive && isRelated && !isRun;
@@ -568,7 +583,8 @@ function EventRow({ event, index, isFirst, isLast, isExpanded, onToggleExpand, a
568
583
  // ──────────────────────────────────────────────────────────────────────────
569
584
  // Main component
570
585
  // ──────────────────────────────────────────────────────────────────────────
571
- export function EventListView({ events, run, onLoadEventData, hasMoreEvents = false, isLoadingMoreEvents = false, onLoadMoreEvents, encryptionKey, isLoading = false, sortOrder: sortOrderProp, onSortOrderChange, onDecrypt, isDecrypting = false, hasEncryptedData: hasEncryptedDataProp = false, }) {
586
+ export function EventListView({ events, run, onLoadEventData, hasMoreEvents = false, isLoadingMoreEvents = false, onLoadMoreEvents, encryptionKey, isLoading = false, sortOrder: sortOrderProp, onSortOrderChange, onDecrypt, isDecrypting = false, hasEncryptedData: hasEncryptedDataProp = false, onExactIdSearch, }) {
587
+ const toast = useToast();
572
588
  const [internalSortOrder, setInternalSortOrder] = useState('asc');
573
589
  const effectiveSortOrder = sortOrderProp ?? internalSortOrder;
574
590
  const handleSortOrderChange = useCallback((order) => {
@@ -579,24 +595,36 @@ export function EventListView({ events, run, onLoadEventData, hasMoreEvents = fa
579
595
  setInternalSortOrder(order);
580
596
  }
581
597
  }, [onSortOrderChange]);
598
+ const [searchQuery, setSearchQuery] = useState('');
599
+ const [searchResults, setSearchResults] = useState(null);
600
+ const [searchResultsTruncated, setSearchResultsTruncated] = useState(false);
601
+ const [searchError, setSearchError] = useState(null);
602
+ const [searchLoading, setSearchLoading] = useState(false);
603
+ const [searchNotFound, setSearchNotFound] = useState(false);
604
+ const searchRequestRef = useRef(0);
605
+ const virtuosoRef = useRef(null);
606
+ const parsedSearchId = useMemo(() => parseExactWorkflowSearchId(searchQuery), [searchQuery]);
607
+ const isExactSearchActive = searchResults !== null;
582
608
  const sortedEvents = useMemo(() => {
583
- if (!events || events.length === 0)
609
+ const sourceEvents = isExactSearchActive ? searchResults : (events ?? []);
610
+ if (sourceEvents.length === 0)
584
611
  return [];
585
612
  const dir = effectiveSortOrder === 'desc' ? -1 : 1;
586
- return [...events].sort((a, b) => dir *
613
+ return [...sourceEvents].sort((a, b) => dir *
587
614
  (new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime()));
588
- }, [events, effectiveSortOrder]);
615
+ }, [events, effectiveSortOrder, isExactSearchActive, searchResults]);
589
616
  // Detect encrypted fields across all loaded events (inline eventData).
590
617
  const hasEncryptedInlineData = useMemo(() => {
591
- if (!events)
618
+ const sourceEvents = isExactSearchActive ? searchResults : events;
619
+ if (!sourceEvents)
592
620
  return false;
593
- for (const event of events) {
621
+ for (const event of sourceEvents) {
594
622
  const ed = event.eventData;
595
623
  if (hasEncryptedValues(ed))
596
624
  return true;
597
625
  }
598
626
  return false;
599
- }, [events]);
627
+ }, [events, isExactSearchActive, searchResults]);
600
628
  // Tracks whether any expanded row's lazy-loaded data contained encrypted markers.
601
629
  // Set to true by EventRow via onEncryptedDataDetected; never reset (sticky).
602
630
  const [foundEncryptedInLazyData, setFoundEncryptedInLazyData] = useState(false);
@@ -604,7 +632,7 @@ export function EventListView({ events, run, onLoadEventData, hasMoreEvents = fa
604
632
  setFoundEncryptedInLazyData(true);
605
633
  }, []);
606
634
  const hasEncryptedData = hasEncryptedDataProp || hasEncryptedInlineData || foundEncryptedInLazyData;
607
- const { correlationNameMap, workflowName } = useMemo(() => buildNameMaps(events ?? null, run ?? null), [events, run]);
635
+ const { correlationNameMap, workflowName } = useMemo(() => buildNameMaps(isExactSearchActive ? searchResults : (events ?? null), run ?? null), [events, isExactSearchActive, run, searchResults]);
608
636
  const durationMap = useMemo(() => buildDurationMap(sortedEvents), [sortedEvents]);
609
637
  const [selectedGroupKey, setSelectedGroupKey] = useState(undefined);
610
638
  const [hoveredGroupKey, setHoveredGroupKey] = useState(undefined);
@@ -682,61 +710,112 @@ export function EventListView({ events, run, onLoadEventData, hasMoreEvents = fa
682
710
  }
683
711
  return first >= 0 ? { first, last } : null;
684
712
  }, [activeGroupKey, sortedEvents]);
685
- const [searchQuery, setSearchQuery] = useState('');
686
- const virtuosoRef = useRef(null);
687
- const searchIndex = useMemo(() => {
688
- const entries = [];
689
- for (let i = 0; i < sortedEvents.length; i++) {
690
- const ev = sortedEvents[i];
691
- const isRun = isRunLevel(ev.eventType);
692
- const name = isRun
693
- ? (workflowName ?? '')
694
- : ev.correlationId
695
- ? (correlationNameMap.get(ev.correlationId) ?? '')
696
- : '';
697
- entries.push({
698
- fields: [
699
- ev.eventId,
700
- ev.correlationId ?? '',
701
- ev.eventType,
702
- formatEventType(ev.eventType),
703
- name,
704
- ].map((f) => f.toLowerCase()),
705
- groupKey: ev.correlationId ?? (isRun ? '__run__' : undefined),
706
- eventId: ev.eventId,
707
- index: i,
708
- });
709
- }
710
- return entries;
711
- }, [sortedEvents, correlationNameMap, workflowName]);
712
713
  useEffect(() => {
713
- const q = searchQuery.trim().toLowerCase();
714
- if (!q) {
714
+ const trimmed = searchQuery.trim();
715
+ if (!trimmed) {
716
+ searchRequestRef.current += 1;
717
+ setSearchResults(null);
718
+ setSearchResultsTruncated(false);
719
+ setSearchError(null);
720
+ setSearchLoading(false);
721
+ setSearchNotFound(false);
715
722
  setSelectedGroupKey(undefined);
716
723
  return;
717
724
  }
718
- let bestMatch = null;
719
- let bestScore = 0;
720
- for (const entry of searchIndex) {
721
- for (const field of entry.fields) {
722
- if (field && field.includes(q)) {
723
- const score = q.length / field.length;
724
- if (score > bestScore) {
725
- bestScore = score;
726
- bestMatch = entry;
725
+ const parsed = parseExactWorkflowSearchId(trimmed);
726
+ if (!parsed || !onExactIdSearch) {
727
+ setSearchResults(null);
728
+ setSearchLoading(false);
729
+ setSearchNotFound(false);
730
+ return;
731
+ }
732
+ const requestId = ++searchRequestRef.current;
733
+ setSearchLoading(true);
734
+ setSearchNotFound(false);
735
+ setSearchError(null);
736
+ const abortController = new AbortController();
737
+ const timer = setTimeout(() => {
738
+ void (async () => {
739
+ try {
740
+ const results = await onExactIdSearch(parsed.id, parsed.kind, abortController.signal);
741
+ if (abortController.signal.aborted ||
742
+ searchRequestRef.current !== requestId) {
743
+ return;
744
+ }
745
+ if (results.status === 'error') {
746
+ setSearchResults([]);
747
+ setSearchResultsTruncated(false);
748
+ setSearchNotFound(false);
749
+ setSearchError(results.message);
750
+ setSelectedGroupKey(undefined);
751
+ return;
727
752
  }
753
+ if (results.status === 'not_found' ||
754
+ (results.status === 'ok' && results.events.length === 0)) {
755
+ setSearchResults([]);
756
+ setSearchResultsTruncated(false);
757
+ setSearchNotFound(true);
758
+ setSearchError(null);
759
+ setSelectedGroupKey(undefined);
760
+ return;
761
+ }
762
+ setSearchResults(results.events);
763
+ setSearchResultsTruncated(Boolean(results.truncated));
764
+ setSearchNotFound(false);
765
+ setSearchError(null);
766
+ setSelectedGroupKey(parsed.kind === 'event'
767
+ ? (() => {
768
+ const first = results.events[0];
769
+ if (!first)
770
+ return undefined;
771
+ return isRunLevel(first.eventType)
772
+ ? '__run__'
773
+ : (first.correlationId ?? undefined);
774
+ })()
775
+ : parsed.id);
776
+ virtuosoRef.current?.scrollToIndex({
777
+ index: 0,
778
+ align: 'start',
779
+ behavior: 'smooth',
780
+ });
728
781
  }
729
- }
782
+ catch {
783
+ if (abortController.signal.aborted ||
784
+ searchRequestRef.current !== requestId) {
785
+ return;
786
+ }
787
+ setSearchResults([]);
788
+ setSearchResultsTruncated(false);
789
+ setSearchNotFound(false);
790
+ setSearchError('Failed to search events. Try again.');
791
+ setSelectedGroupKey(undefined);
792
+ }
793
+ finally {
794
+ if (searchRequestRef.current === requestId &&
795
+ !abortController.signal.aborted) {
796
+ setSearchLoading(false);
797
+ }
798
+ }
799
+ })();
800
+ }, 300);
801
+ return () => {
802
+ clearTimeout(timer);
803
+ abortController.abort();
804
+ };
805
+ }, [searchQuery, onExactIdSearch]);
806
+ const handleSearchKeyDown = useCallback((event) => {
807
+ if (event.key !== 'Enter') {
808
+ return;
730
809
  }
731
- if (bestMatch) {
732
- setSelectedGroupKey(bestMatch.groupKey);
733
- virtuosoRef.current?.scrollToIndex({
734
- index: bestMatch.index,
735
- align: 'center',
736
- behavior: 'smooth',
737
- });
810
+ const trimmed = searchQuery.trim();
811
+ if (!trimmed ||
812
+ parseExactWorkflowSearchId(trimmed) ||
813
+ !onExactIdSearch ||
814
+ !looksLikeWorkflowIdSearchInput(trimmed)) {
815
+ return;
738
816
  }
739
- }, [searchQuery, searchIndex]);
817
+ toast.info('Enter a full step ID, wait ID, hook ID, or event ID');
818
+ }, [searchQuery, onExactIdSearch, toast]);
740
819
  // Track whether we've ever had events to distinguish initial load from refetch
741
820
  const hasHadEventsRef = useRef(false);
742
821
  if (sortedEvents.length > 0) {
@@ -747,9 +826,6 @@ export function EventListView({ events, run, onLoadEventData, hasMoreEvents = fa
747
826
  if (isInitialLoad) {
748
827
  return (_jsxs("div", { className: "h-full flex flex-col overflow-hidden", children: [_jsx("div", { style: { padding: 6 }, children: _jsx(Skeleton, { style: { height: 40, borderRadius: 6 } }) }), _jsxs("div", { className: "flex items-center gap-0 h-10 border-b flex-shrink-0", style: { borderColor: 'var(--ds-gray-alpha-200)' }, children: [_jsx("div", { className: "flex-shrink-0", style: { width: GUTTER_WIDTH } }), _jsx("div", { className: "w-5 flex-shrink-0" }), _jsx("div", { className: "min-w-0 px-4", style: { flex: '2 1 0%' }, children: _jsx(Skeleton, { className: "h-3", style: { width: 40 } }) }), _jsx("div", { className: "min-w-0 px-4", style: { flex: '2 1 0%' }, children: _jsx(Skeleton, { className: "h-3", style: { width: 72 } }) }), _jsx("div", { className: "min-w-0 px-4", style: { flex: '2 1 0%' }, children: _jsx(Skeleton, { className: "h-3", style: { width: 44 } }) }), _jsx("div", { className: "min-w-0 px-4", style: { flex: '3 1 0%' }, children: _jsx(Skeleton, { className: "h-3", style: { width: 92 } }) }), _jsx("div", { className: "min-w-0 px-4", style: { flex: '3 1 0%' }, children: _jsx(Skeleton, { className: "h-3", style: { width: 60 } }) })] }), _jsx(RowsSkeleton, {})] }));
749
828
  }
750
- if (!isLoading && (!events || events.length === 0)) {
751
- return (_jsx("div", { className: "flex items-center justify-center h-full text-sm", style: { color: 'var(--ds-gray-700)' }, children: "No events found" }));
752
- }
753
829
  return (_jsx(DecryptClickContext.Provider, { value: onDecrypt ? { onDecrypt, isDecrypting } : undefined, children: _jsxs("div", { className: "h-full flex flex-col overflow-hidden", children: [_jsx("style", { children: `@keyframes workflow-dot-pulse{0%{transform:scale(1);opacity:.7}70%,100%{transform:scale(2.2);opacity:0}}` }), _jsxs("div", { style: {
754
830
  padding: 6,
755
831
  backgroundColor: 'var(--ds-background-100)',
@@ -773,7 +849,9 @@ export function EventListView({ events, run, onLoadEventData, hasMoreEvents = fa
773
849
  justifyContent: 'center',
774
850
  color: 'var(--ds-gray-800)',
775
851
  flexShrink: 0,
776
- }, children: _jsxs("svg", { width: 16, height: 16, viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", focusable: "false", children: [_jsx("circle", { cx: "7", cy: "7", r: "4.5", stroke: "currentColor", strokeWidth: "1.5" }), _jsx("path", { d: "M11.5 11.5L14 14", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" })] }) }), _jsx("input", { type: "search", placeholder: "Search by name, event type, or ID\u2026", value: searchQuery, onChange: (e) => setSearchQuery(e.target.value), style: {
852
+ }, children: _jsxs("svg", { width: 16, height: 16, viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", focusable: "false", children: [_jsx("circle", { cx: "7", cy: "7", r: "4.5", stroke: "currentColor", strokeWidth: "1.5" }), _jsx("path", { d: "M11.5 11.5L14 14", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" })] }) }), _jsx("input", { type: "search", placeholder: "Search by step ID, wait ID, hook ID, or event ID\u2026", value: searchQuery, onChange: (e) => setSearchQuery(e.target.value), onKeyDown: handleSearchKeyDown, disabled: !onExactIdSearch, title: onExactIdSearch
853
+ ? undefined
854
+ : 'Exact ID search is unavailable in this view.', style: {
777
855
  marginLeft: -16,
778
856
  paddingInline: 12,
779
857
  fontFamily: 'inherit',
@@ -783,22 +861,38 @@ export function EventListView({ events, run, onLoadEventData, hasMoreEvents = fa
783
861
  outline: 'none',
784
862
  height: 40,
785
863
  width: '100%',
864
+ opacity: onExactIdSearch ? 1 : 0.5,
865
+ cursor: onExactIdSearch ? 'text' : 'not-allowed',
786
866
  } })] }), _jsx(MenuDropdown, { options: SORT_OPTIONS, value: effectiveSortOrder, onChange: handleSortOrderChange }), (hasEncryptedData || encryptionKey) && onDecrypt && (_jsx(DecryptButton, { decrypted: !!encryptionKey, loading: isDecrypting, onClick: onDecrypt }))] }), _jsxs("div", { className: "flex items-center gap-0 text-[13px] font-medium h-10 border-b flex-shrink-0", style: {
787
867
  borderColor: 'var(--ds-gray-alpha-200)',
788
868
  color: 'var(--ds-gray-900)',
789
869
  backgroundColor: 'var(--ds-background-100)',
790
- }, children: [_jsx("div", { className: "flex-shrink-0", style: { width: GUTTER_WIDTH } }), _jsx("div", { className: "w-5 flex-shrink-0" }), _jsx("div", { className: "min-w-0 px-4", style: { flex: '2 1 0%' }, children: "Time" }), _jsx("div", { className: "min-w-0 px-4", style: { flex: '2 1 0%' }, children: "Event Type" }), _jsx("div", { className: "min-w-0 px-4", style: { flex: '2 1 0%' }, children: "Name" }), _jsx("div", { className: "min-w-0 px-4", style: { flex: '3 1 0%' }, children: "Correlation ID" }), _jsx("div", { className: "min-w-0 px-4", style: { flex: '3 1 0%' }, children: "Event ID" })] }), isRefetching ? (_jsx(RowsSkeleton, {})) : (_jsx(Virtuoso, { ref: virtuosoRef, totalCount: sortedEvents.length, overscan: 20, defaultItemHeight: 40, endReached: () => {
791
- if (!hasMoreEvents || isLoadingMoreEvents) {
870
+ }, children: [_jsx("div", { className: "flex-shrink-0", style: { width: GUTTER_WIDTH } }), _jsx("div", { className: "w-5 flex-shrink-0" }), _jsx("div", { className: "min-w-0 px-4", style: { flex: '2 1 0%' }, children: "Time" }), _jsx("div", { className: "min-w-0 px-4", style: { flex: '2 1 0%' }, children: "Event Type" }), _jsx("div", { className: "min-w-0 px-4", style: { flex: '2 1 0%' }, children: "Name" }), _jsx("div", { className: "min-w-0 px-4", style: { flex: '3 1 0%' }, children: "Correlation ID" }), _jsx("div", { className: "min-w-0 px-4", style: { flex: '3 1 0%' }, children: "Event ID" })] }), isRefetching || searchLoading ? (_jsx(RowsSkeleton, {})) : sortedEvents.length === 0 ? (_jsx("div", { className: "flex flex-1 items-center justify-center px-6 text-center text-sm", style: { color: 'var(--ds-gray-700)' }, children: searchNotFound && searchQuery.trim()
871
+ ? `No events found for ${searchQuery.trim()}`
872
+ : searchError
873
+ ? searchError
874
+ : parsedSearchId && searchQuery.trim() && !onExactIdSearch
875
+ ? 'Exact ID search is unavailable in this view.'
876
+ : 'No events found' })) : (_jsx(Virtuoso, { ref: virtuosoRef, totalCount: sortedEvents.length, overscan: 20, defaultItemHeight: 40, endReached: () => {
877
+ if (isExactSearchActive ||
878
+ !hasMoreEvents ||
879
+ isLoadingMoreEvents) {
792
880
  return;
793
881
  }
794
882
  void onLoadMoreEvents?.();
795
883
  }, itemContent: (index) => {
796
884
  const ev = sortedEvents[index];
797
- return (_jsx(EventRow, { event: ev, index: index, isFirst: index === 0, isLast: index === sortedEvents.length - 1, isExpanded: expandedEventIds.has(ev.eventId), onToggleExpand: toggleEventExpanded, activeGroupKey: activeGroupKey, selectedGroupKey: selectedGroupKey, selectedGroupRange: selectedGroupRange, correlationNameMap: correlationNameMap, workflowName: workflowName, durationMap: durationMap, onSelectGroup: onSelectGroup, onHoverGroup: onHoverGroup, onLoadEventData: onLoadEventData, cachedEventData: eventDataCacheRef.current.get(ev.eventId) ?? null, onCacheEventData: cacheEventData, encryptionKey: encryptionKey, onEncryptedDataDetected: handleEncryptedDataDetected }));
885
+ return (_jsx(EventRow, { event: ev, index: index, isFirst: index === 0, isLast: index === sortedEvents.length - 1, isExpanded: expandedEventIds.has(ev.eventId), onToggleExpand: toggleEventExpanded, activeGroupKey: activeGroupKey, selectedGroupKey: selectedGroupKey, selectedGroupRange: selectedGroupRange, correlationNameMap: correlationNameMap, workflowName: workflowName, durationMap: durationMap, onSelectGroup: onSelectGroup, onHoverGroup: onHoverGroup, onLoadEventData: onLoadEventData, cachedEventData: eventDataCacheRef.current.get(ev.eventId) ?? null, onCacheEventData: cacheEventData, encryptionKey: encryptionKey, onEncryptedDataDetected: handleEncryptedDataDetected, suppressGroupDimming: isExactSearchActive }));
798
886
  }, style: { flex: 1, minHeight: 0 } })), _jsxs("div", { className: "relative flex-shrink-0 flex items-center h-10 border-t px-4 text-xs", style: {
799
887
  borderColor: 'var(--ds-gray-alpha-200)',
800
888
  color: 'var(--ds-gray-900)',
801
889
  backgroundColor: 'var(--ds-background-100)',
802
- }, children: [_jsxs("span", { children: [sortedEvents.length, " event", sortedEvents.length !== 1 ? 's' : '', " loaded"] }), hasMoreEvents && (_jsx("div", { className: "absolute inset-0 flex items-center justify-center pointer-events-none", children: _jsx("div", { className: "pointer-events-auto", children: _jsx(LoadMoreButton, { loading: isLoadingMoreEvents, onClick: () => void onLoadMoreEvents?.() }) }) }))] })] }) }));
890
+ }, children: [_jsx("span", { children: isExactSearchActive
891
+ ? searchError
892
+ ? searchError
893
+ : searchNotFound
894
+ ? `No events found for ${searchQuery.trim()}`
895
+ : `${sortedEvents.length} event${sortedEvents.length !== 1 ? 's' : ''} for ${searchQuery.trim()}${searchResultsTruncated ? ' (results may be truncated)' : ''}`
896
+ : `${sortedEvents.length} event${sortedEvents.length !== 1 ? 's' : ''} loaded` }), !isExactSearchActive && hasMoreEvents && (_jsx("div", { className: "absolute inset-0 flex items-center justify-center pointer-events-none", children: _jsx("div", { className: "pointer-events-auto", children: _jsx(LoadMoreButton, { loading: isLoadingMoreEvents, onClick: () => void onLoadMoreEvents?.() }) }) }))] })] }) }));
803
897
  }
804
898
  //# sourceMappingURL=event-list-view.js.map