@parallelworks/ui 0.18.0 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (161) hide show
  1. package/README.md +86 -6
  2. package/dist/ai/adapter/memoryStore.d.ts +8 -0
  3. package/dist/ai/adapter/mock/index.d.ts +2 -0
  4. package/dist/ai/adapter/mock/index.js +81 -0
  5. package/dist/ai/adapter/mock/mockAdapter.d.ts +4 -0
  6. package/dist/ai/adapter/openai/createOpenAIStream.d.ts +9 -0
  7. package/dist/ai/adapter/openai/errors.d.ts +3 -0
  8. package/dist/ai/adapter/openai/index.d.ts +6 -0
  9. package/dist/ai/adapter/openai/index.js +334 -0
  10. package/dist/ai/adapter/openai/openaiAdapter.d.ts +9 -0
  11. package/dist/ai/adapter/openai/responses.d.ts +4 -0
  12. package/dist/ai/adapter/openai/sse.d.ts +24 -0
  13. package/dist/ai/adapter/openai/wire.d.ts +40 -0
  14. package/dist/ai/adapter/types.d.ts +131 -0
  15. package/dist/ai/agent/classifier.d.ts +2 -0
  16. package/dist/ai/agent/diff.d.ts +35 -0
  17. package/dist/ai/agent/diffHighlight.d.ts +6 -0
  18. package/dist/ai/agent/index.d.ts +6 -0
  19. package/dist/ai/agent/permissions.d.ts +11 -0
  20. package/dist/ai/agent/toolfmt.d.ts +18 -0
  21. package/dist/ai/agent/transcript.d.ts +38 -0
  22. package/dist/ai/ai.css +325 -0
  23. package/dist/ai/components/AllocationSelector.d.ts +4 -0
  24. package/dist/ai/components/AttachmentManager.d.ts +1 -0
  25. package/dist/ai/components/AttachmentUpload.d.ts +24 -0
  26. package/dist/ai/components/BlockedGroupBanner.d.ts +1 -0
  27. package/dist/ai/components/BranchNavigator.d.ts +8 -0
  28. package/dist/ai/components/ChatEmptyState.d.ts +3 -0
  29. package/dist/ai/components/ChatInput.d.ts +45 -0
  30. package/dist/ai/components/ChatLayout.d.ts +5 -0
  31. package/dist/ai/components/ChatMessage.d.ts +24 -0
  32. package/dist/ai/components/ChatMessageList.d.ts +35 -0
  33. package/dist/ai/components/ChatSidebar.d.ts +5 -0
  34. package/dist/ai/components/ChatThread.d.ts +3 -0
  35. package/dist/ai/components/ComposerChrome.d.ts +18 -0
  36. package/dist/ai/components/ComposerFrame.d.ts +16 -0
  37. package/dist/ai/components/DragOverlay.d.ts +5 -0
  38. package/dist/ai/components/ModelSelector.d.ts +6 -0
  39. package/dist/ai/components/ShareDialog.d.ts +11 -0
  40. package/dist/ai/components/SlashMenu.d.ts +28 -0
  41. package/dist/ai/components/agent/ActivityLine.d.ts +21 -0
  42. package/dist/ai/components/agent/AgentMessageParts.d.ts +54 -0
  43. package/dist/ai/components/agent/PermissionPicker.d.ts +11 -0
  44. package/dist/ai/components/attachmentKeys.d.ts +4 -0
  45. package/dist/ai/core/ChatProvider.d.ts +66 -0
  46. package/dist/ai/core/KeyboardShortcutsProvider.d.ts +5 -0
  47. package/dist/ai/core/chatReducer.d.ts +114 -0
  48. package/dist/ai/core/config.d.ts +47 -0
  49. package/dist/ai/core/events.d.ts +1 -0
  50. package/dist/ai/core/exportConversation.d.ts +4 -0
  51. package/dist/ai/core/greeting.d.ts +3 -0
  52. package/dist/ai/core/parts.d.ts +11 -0
  53. package/dist/ai/core/useDragDrop.d.ts +14 -0
  54. package/dist/ai/core/useKeyboardShortcuts.d.ts +12 -0
  55. package/dist/ai/core/useSSEStream.d.ts +34 -0
  56. package/dist/ai/index.d.ts +40 -0
  57. package/dist/ai/index.js +6645 -0
  58. package/dist/ai/strings.d.ts +167 -0
  59. package/dist/ai/types.d.ts +236 -0
  60. package/dist/ai/ui/Avatar.d.ts +8 -0
  61. package/dist/ai/ui/Dropdown.d.ts +31 -0
  62. package/dist/ai/ui/Markdown.d.ts +8 -0
  63. package/dist/ai/ui/Notice.d.ts +21 -0
  64. package/dist/ai/ui/buttons.d.ts +1 -0
  65. package/dist/ai/ui/codeBreaks.d.ts +16 -0
  66. package/dist/ai/ui/prose.d.ts +1 -0
  67. package/dist/ai/ui/sidebar.d.ts +81 -0
  68. package/dist/ai/utils.d.ts +4 -0
  69. package/dist/assets/{editor.worker-CuBRIwi-.js → editor.worker-BmHUIVG4.js} +194 -163
  70. package/dist/assets/{json.worker-8ytS02vz.js → json.worker-D8bJ92nw.js} +384 -338
  71. package/dist/assets/{yaml.worker-DJC-VUcv.js → yaml.worker-C4gyGAko.js} +15657 -13823
  72. package/dist/chunks/{CodePreviewEditor-4lx3kE-w.js → CodePreviewEditor-YqQBz1YL.js} +1 -1
  73. package/dist/chunks/ConfirmModal-aOR4O1QE.js +79 -0
  74. package/dist/chunks/{FieldWrapper-Bef01EWO.js → FieldWrapper-Cszwg-4B.js} +3 -7
  75. package/dist/chunks/{CreateModal-BY6DLmur.js → Markdown-CM-FenNE.js} +19 -91
  76. package/dist/chunks/Monaco-DJ8MW-Dq.js +110 -0
  77. package/dist/chunks/{PageHeader-l49p1nM9.js → PageHeader-0kG_iypP.js} +67 -65
  78. package/dist/chunks/{Provider-Ghua9PkN.js → Provider-CDYurraE.js} +54 -20
  79. package/dist/chunks/{SwitchToggle-CHPP8uZQ.js → SwitchToggle-DShNdcao.js} +135 -132
  80. package/dist/chunks/{Table-4OHjdKdO.js → Table-DU2MW8xz.js} +5 -3
  81. package/dist/chunks/{Tooltip-Crxo_eU_.js → Tooltip-K5HJr4_p.js} +31 -4
  82. package/dist/chunks/duration-BOv8Phl8.js +17 -0
  83. package/dist/chunks/{editor-9_uVtUyy.js → editor-Dk1rbV75.js} +32 -18
  84. package/dist/chunks/focus-D_iZJTW1.js +7 -0
  85. package/dist/chunks/{list-qrojZZNv.js → list-CJm875m7.js} +377 -370
  86. package/dist/chunks/logviewer-BTKuQWzw.js +828 -0
  87. package/dist/chunks/memoryStore-CC91S5LP.js +152 -0
  88. package/dist/chunks/types-Yy0NrerF.js +11 -0
  89. package/dist/components/Avatar.d.ts +1 -1
  90. package/dist/components/Breadcrumbs.d.ts +1 -1
  91. package/dist/components/Dropdown.d.ts +1 -1
  92. package/dist/components/EmptyState.d.ts +1 -1
  93. package/dist/components/Indicator.d.ts +2 -2
  94. package/dist/components/Input.d.ts +1 -1
  95. package/dist/components/LetterBadge.d.ts +1 -1
  96. package/dist/components/MarkdownImpl.d.ts +1 -1
  97. package/dist/components/Provider.d.ts +16 -65
  98. package/dist/components/SettingRow.d.ts +1 -1
  99. package/dist/components/SettingsGroup.d.ts +1 -1
  100. package/dist/components/focus.d.ts +2 -0
  101. package/dist/components/keys.d.ts +12 -0
  102. package/dist/duration.d.ts +3 -0
  103. package/dist/editor/Monaco.d.ts +1 -20
  104. package/dist/editor/index.d.ts +2 -1
  105. package/dist/editor/index.js +2 -2
  106. package/dist/editor/yaml.d.ts +5 -0
  107. package/dist/engine.d.ts +98 -0
  108. package/dist/file-explorer/FileExplorer.d.ts +11 -2
  109. package/dist/file-explorer/FileExplorerContext.d.ts +1 -1
  110. package/dist/file-explorer/FilePreview/CodePreviewEditor.d.ts +1 -1
  111. package/dist/file-explorer/FilePreview/previewType.d.ts +1 -1
  112. package/dist/file-explorer/FileRow.d.ts +1 -1
  113. package/dist/file-explorer/helpers.d.ts +4 -4
  114. package/dist/file-explorer/index.d.ts +3 -3
  115. package/dist/file-explorer/index.js +613 -647
  116. package/dist/file-explorer/lib/types.d.ts +6 -1
  117. package/dist/file-explorer/lib/utils.d.ts +1 -3
  118. package/dist/file-explorer/useNearEnd.d.ts +1 -1
  119. package/dist/form/FieldWrapper.d.ts +2 -2
  120. package/dist/form/Fields/ResourceOptionIcon.d.ts +1 -1
  121. package/dist/form/Form.d.ts +4 -21
  122. package/dist/form/index.d.ts +11 -12
  123. package/dist/form/index.js +1488 -1611
  124. package/dist/form/lib.d.ts +6 -1
  125. package/dist/form/types/fieldComponentTypes.d.ts +3 -2
  126. package/dist/form/types/fieldTypes.d.ts +6 -20
  127. package/dist/form/useParsedOpts.d.ts +1 -1
  128. package/dist/form/utils/{slurmDuration.d.ts → duration.d.ts} +2 -2
  129. package/dist/{workflow → graph}/MatrixGroup.d.ts +2 -2
  130. package/dist/{workflow → graph}/index.d.ts +1 -1
  131. package/dist/{workflow → graph}/index.js +215 -180
  132. package/dist/{workflow → graph}/types.d.ts +3 -3
  133. package/dist/icons.d.ts +20 -21
  134. package/dist/index.d.ts +38 -40
  135. package/dist/index.js +347 -346
  136. package/dist/list/ListTable.d.ts +1 -1
  137. package/dist/list/ListViewControls.d.ts +2 -2
  138. package/dist/list/index.d.ts +9 -9
  139. package/dist/list/index.js +2 -2
  140. package/dist/logviewer/LogViewer.d.ts +1 -1
  141. package/dist/logviewer/index.d.ts +1 -0
  142. package/dist/logviewer/index.js +2 -742
  143. package/dist/logviewer/segments.d.ts +9 -0
  144. package/dist/styles/fonts.css +4 -4
  145. package/dist/styles/theme.css +6 -4
  146. package/dist/styles.css +1 -1
  147. package/dist/theme/index.js +2 -2
  148. package/package.json +38 -14
  149. package/LICENSE +0 -202
  150. package/dist/chunks/Monaco-C1POOdVv.js +0 -1046
  151. package/dist/editor/usesGitlab.d.ts +0 -97
  152. package/dist/form/Fields/BucketReadOnlyField.d.ts +0 -7
  153. package/dist/form/types/workflow-schema.d.ts +0 -1145
  154. package/dist/form/utils/pwResolver.d.ts +0 -18
  155. package/dist/chunks/{MarkdownImpl-Bq4QhrtA.js → MarkdownImpl-CyeTVvSt.js} +1 -1
  156. /package/dist/{workflow → graph}/AnnotationBanner.d.ts +0 -0
  157. /package/dist/{workflow → graph}/Collapse.d.ts +0 -0
  158. /package/dist/{workflow → graph}/DependencyGraph.d.ts +0 -0
  159. /package/dist/{workflow → graph}/JobSummary.d.ts +0 -0
  160. /package/dist/{workflow → graph}/Reveal.d.ts +0 -0
  161. /package/dist/{workflow → graph}/TreeView.d.ts +0 -0
@@ -0,0 +1,828 @@
1
+ import { a as useOptionalWorkflowEngine, i as useNotify, u as useStrings } from "./Provider-CDYurraE.js";
2
+ import { ArrowDownIcon, ArrowUpIcon, CheckIcon, ChevronDownIcon, ChevronRightIcon, ClockIcon, CopyIcon, DownloadIcon, ExpandIcon, ShrinkIcon, XIcon } from "../icons.js";
3
+ import { r as TooltipInfo } from "./Tooltip-K5HJr4_p.js";
4
+ import cx from "classnames";
5
+ import { Fragment, memo, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
6
+ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
7
+ import { DateTime } from "luxon";
8
+ import Anser from "anser";
9
+ //#region src/logviewer/segments.ts
10
+ function segmentClassName(chunk) {
11
+ const classes = [];
12
+ if (chunk.fg) classes.push(`${chunk.fg}-fg`);
13
+ if (chunk.bg) classes.push(`${chunk.bg}-bg`);
14
+ for (const decoration of chunk.decorations) classes.push(`ansi-${decoration}`);
15
+ return classes.join(" ");
16
+ }
17
+ function parseAnsiSegments(line) {
18
+ return Anser.ansiToJson(line, {
19
+ use_classes: true,
20
+ remove_empty: true
21
+ }).map((chunk) => ({
22
+ content: chunk.content,
23
+ className: segmentClassName(chunk)
24
+ }));
25
+ }
26
+ function plainSegments(text) {
27
+ return [{
28
+ content: text,
29
+ className: ""
30
+ }];
31
+ }
32
+ /**
33
+ * Matches are located on the concatenated line text so a term straddling an
34
+ * ANSI colour boundary still highlights, then sliced back onto the segments it
35
+ * covers.
36
+ */
37
+ function highlightSegments(segments, searchTerm) {
38
+ if (!searchTerm) return segments;
39
+ const haystack = segments.map((segment) => segment.content).join("").toLowerCase();
40
+ const needle = searchTerm.toLowerCase();
41
+ if (!haystack.includes(needle)) return segments;
42
+ const matchStarts = [];
43
+ let found = haystack.indexOf(needle);
44
+ while (found !== -1) {
45
+ matchStarts.push(found);
46
+ found = haystack.indexOf(needle, found + needle.length);
47
+ }
48
+ const result = [];
49
+ let matchIndex = 0;
50
+ let offset = 0;
51
+ for (const segment of segments) {
52
+ const segmentEnd = offset + segment.content.length;
53
+ let cursor = offset;
54
+ while (cursor < segmentEnd) {
55
+ let matchStart = matchStarts[matchIndex];
56
+ while (matchStart !== void 0 && matchStart + needle.length <= cursor) {
57
+ matchIndex++;
58
+ matchStart = matchStarts[matchIndex];
59
+ }
60
+ if (matchStart === void 0 || matchStart >= segmentEnd) {
61
+ result.push({
62
+ ...segment,
63
+ content: segment.content.slice(cursor - offset)
64
+ });
65
+ break;
66
+ }
67
+ if (matchStart > cursor) {
68
+ result.push({
69
+ ...segment,
70
+ content: segment.content.slice(cursor - offset, matchStart - offset)
71
+ });
72
+ cursor = matchStart;
73
+ }
74
+ const matchEnd = Math.min(matchStart + needle.length, segmentEnd);
75
+ result.push({
76
+ ...segment,
77
+ content: segment.content.slice(cursor - offset, matchEnd - offset),
78
+ highlighted: true
79
+ });
80
+ cursor = matchEnd;
81
+ }
82
+ offset = segmentEnd;
83
+ }
84
+ return result;
85
+ }
86
+ //#endregion
87
+ //#region src/logviewer/LogViewer.tsx
88
+ function LogSegments({ segments }) {
89
+ return segments.map((segment, i) => segment.highlighted ? /* @__PURE__ */ jsx("mark", {
90
+ className: cx("search-highlight", segment.className),
91
+ children: segment.content
92
+ }, i) : segment.className ? /* @__PURE__ */ jsx("span", {
93
+ className: segment.className,
94
+ children: segment.content
95
+ }, i) : /* @__PURE__ */ jsx(Fragment, { children: segment.content }, i));
96
+ }
97
+ var LogLine = memo(function LogLine({ index, segments, lineNumbers, isSelected, lineHeight, onLineClick, searchTerm, command, groupDepth = 0, isGroupCollapsed, onGroupToggle, annotationLabels }) {
98
+ const displaySegments = useMemo(() => highlightSegments(command?.message ? plainSegments(command.message) : segments, searchTerm), [
99
+ segments,
100
+ searchTerm,
101
+ command
102
+ ]);
103
+ const annotationLabel = useMemo(() => {
104
+ if (command?.type === "error") return /* @__PURE__ */ jsx("span", {
105
+ className: "text-red-500 font-semibold mr-1",
106
+ children: annotationLabels.error
107
+ });
108
+ if (command?.type === "warning") return /* @__PURE__ */ jsx("span", {
109
+ className: "text-yellow-500 font-semibold mr-1",
110
+ children: annotationLabels.warning
111
+ });
112
+ if (command?.type === "notice") return /* @__PURE__ */ jsx("span", {
113
+ className: "text-blue-500 font-semibold mr-1",
114
+ children: annotationLabels.notice
115
+ });
116
+ return null;
117
+ }, [command?.type, annotationLabels]);
118
+ const groupToggle = command?.type === "group" && onGroupToggle ? () => {
119
+ if (window.getSelection()?.isCollapsed === false) return;
120
+ onGroupToggle(index);
121
+ } : null;
122
+ const groupToggleProps = groupToggle ? {
123
+ role: "button",
124
+ tabIndex: 0,
125
+ "aria-expanded": !isGroupCollapsed,
126
+ onClick: groupToggle,
127
+ onKeyDown: (e) => {
128
+ if (e.target !== e.currentTarget) return;
129
+ if (e.key === "Enter" || e.key === " ") {
130
+ e.preventDefault();
131
+ groupToggle();
132
+ }
133
+ }
134
+ } : void 0;
135
+ return /* @__PURE__ */ jsxs("div", {
136
+ "data-line-number": index + 1,
137
+ ...groupToggleProps,
138
+ className: cx("flex items-start text-sm font-mono group", isSelected ? "bg-(--theme-link)/15" : "hover:bg-(--theme-hover)", "text-(--theme-app)", command?.type === "error" && "bg-red-500/10", command?.type === "warning" && "bg-yellow-500/10", command?.type === "notice" && "bg-blue-500/10", command?.type === "debug" && "opacity-60", command?.type === "group" && "bg-(--theme-hover) cursor-pointer"),
139
+ style: {
140
+ minHeight: `${lineHeight}px`,
141
+ lineHeight: `${lineHeight}px`,
142
+ paddingLeft: groupDepth > 0 ? `${groupDepth * 16}px` : void 0
143
+ },
144
+ children: [lineNumbers && /* @__PURE__ */ jsx("button", {
145
+ type: "button",
146
+ onClick: (e) => {
147
+ e.stopPropagation();
148
+ onLineClick(index + 1, e);
149
+ },
150
+ className: cx("inline-flex items-center justify-end w-14 pr-3 shrink-0 cursor-pointer select-none", "hover:underline hover:text-(--theme-link) transition-colors", "text-(--theme-muted-text-color)", isSelected && "text-(--theme-link)"),
151
+ children: index + 1
152
+ }), /* @__PURE__ */ jsxs("pre", {
153
+ className: cx("grow text-left whitespace-pre m-0 py-0.5 pr-2 flex items-center", lineNumbers && "pl-2 border-l border-(--theme-border)", command?.type === "error" && "border-l-red-500 border-l-2", command?.type === "warning" && "border-l-yellow-500 border-l-2", command?.type === "notice" && "border-l-blue-500 border-l-2", command?.type === "group" && "font-bold"),
154
+ children: [
155
+ command?.type === "group" && /* @__PURE__ */ jsx("span", {
156
+ className: "mr-1 shrink-0 inline-flex items-center",
157
+ children: isGroupCollapsed ? /* @__PURE__ */ jsx(ChevronRightIcon, { className: "w-3 h-3" }) : /* @__PURE__ */ jsx(ChevronDownIcon, { className: "w-3 h-3" })
158
+ }),
159
+ annotationLabel,
160
+ /* @__PURE__ */ jsx("span", { children: /* @__PURE__ */ jsx(LogSegments, { segments: displaySegments }) })
161
+ ]
162
+ })]
163
+ });
164
+ });
165
+ function validateTimeRange(from, to) {
166
+ if (from === "" && to === "") return "";
167
+ const fromTime = from === "" ? null : DateTime.fromISO(from);
168
+ const toTime = to === "" ? null : DateTime.fromISO(to);
169
+ if (fromTime && !fromTime.isValid) return "invalidStartTime";
170
+ if (toTime && !toTime.isValid) return "invalidEndTime";
171
+ if (fromTime && toTime && toTime <= fromTime) return "endBeforeStart";
172
+ if (toTime && toTime > DateTime.now()) return "endInFuture";
173
+ return "";
174
+ }
175
+ var TopPaginationBanner = memo(function TopPaginationBanner({ endOfLog, loadingTop, onLoadOlder }) {
176
+ const { logviewer: t } = useStrings();
177
+ const baseCx = cx("flex items-center justify-center gap-2 h-7 text-xs font-mono select-none", "text-[var(--theme-muted-text-color)]", "border-b border-[var(--theme-border)]");
178
+ if (endOfLog) return /* @__PURE__ */ jsx("div", {
179
+ className: cx(baseCx, "opacity-70"),
180
+ children: t.beginningOfLog
181
+ });
182
+ if (loadingTop) return /* @__PURE__ */ jsxs("div", {
183
+ className: baseCx,
184
+ children: [/* @__PURE__ */ jsx("span", {
185
+ className: "w-3 h-3 rounded-full border-2 border-current border-t-transparent animate-spin",
186
+ "aria-hidden": true
187
+ }), /* @__PURE__ */ jsx("span", { children: t.loadingOlderEntries })]
188
+ });
189
+ return /* @__PURE__ */ jsx("button", {
190
+ type: "button",
191
+ onClick: onLoadOlder,
192
+ className: cx(baseCx, "w-full hover:text-[var(--theme-app)] hover:bg-[var(--theme-hover)] transition-colors"),
193
+ children: t.loadOlderEntries
194
+ });
195
+ });
196
+ function LogViewer({ log, height = "unset", width, roundedBottom = false, lineNumbers = true, additionalLeftBarComponents = null, additionalRightBarComponents = null, additionalBottomLeftBarComponents = null, additionalBottomRightBarComponents = null, hideExpand = false, inline = false, contentClassName, enableWorkflowCommands: workflowCommandsRequested = false, onReachTop, loadingTop = false, endOfLog = false, prependedSignal = 0, enableTimeRangeFilter = false, onTimeRangeChange, initialTimeRangeFrom = "", initialTimeRangeTo = "" }) {
197
+ const engine = useOptionalWorkflowEngine(workflowCommandsRequested);
198
+ const enableWorkflowCommands = workflowCommandsRequested && engine !== void 0;
199
+ const notify = useNotify();
200
+ const { common: tCommon, logviewer: t } = useStrings();
201
+ const annotationLabels = useMemo(() => ({
202
+ error: t.error,
203
+ warning: t.warning,
204
+ notice: t.notice
205
+ }), [t]);
206
+ const [fullscreen, setFullscreen] = useState(false);
207
+ const [follow, _setFollow] = useState(true);
208
+ const followRef = useRef(true);
209
+ const updateFollow = useCallback((value) => {
210
+ followRef.current = value;
211
+ _setFollow(value);
212
+ }, []);
213
+ const [logUrl, setLogUrl] = useState("");
214
+ const [fullParsedLog, setFullParsedLog] = useState([]);
215
+ const [processedLines, setProcessedLines] = useState([]);
216
+ const [rawLines, setRawLines] = useState([]);
217
+ const [showDebug, setShowDebug] = useState(false);
218
+ const [collapsedGroups, setCollapsedGroups] = useState(/* @__PURE__ */ new Set());
219
+ const [filter, setFilter] = useState("");
220
+ const [timeRangeFrom, setTimeRangeFrom] = useState(initialTimeRangeFrom);
221
+ const [timeRangeTo, setTimeRangeTo] = useState(initialTimeRangeTo);
222
+ const [timeRangeOpen, setTimeRangeOpen] = useState(false);
223
+ const timeRangePopoverRef = useRef(null);
224
+ const timeRangeError = validateTimeRange(timeRangeFrom, timeRangeTo);
225
+ useEffect(() => {
226
+ setTimeRangeFrom(initialTimeRangeFrom);
227
+ }, [initialTimeRangeFrom]);
228
+ useEffect(() => {
229
+ setTimeRangeTo(initialTimeRangeTo);
230
+ }, [initialTimeRangeTo]);
231
+ const [visibleStart, setVisibleStart] = useState(0);
232
+ const [visibleEnd, setVisibleEnd] = useState(0);
233
+ const [selectedLines, setSelectedLines] = useState(/* @__PURE__ */ new Set());
234
+ const [lastClickedLine, setLastClickedLine] = useState(null);
235
+ const [copied, setCopied] = useState(false);
236
+ const [highlightCopyPosition, setHighlightCopyPosition] = useState(null);
237
+ const [highlightedText, setHighlightedText] = useState("");
238
+ const logContainerRef = useRef(null);
239
+ const ansiCache = useRef({});
240
+ const scrollAnimationRef = useRef(null);
241
+ const prevLogLengthRef = useRef(0);
242
+ const userScrollUpRef = useRef(false);
243
+ const reachTopFiredRef = useRef(false);
244
+ const prevScrollHeightRef = useRef(0);
245
+ const prevPrependedSignalRef = useRef(prependedSignal);
246
+ const pendingPrependRef = useRef(false);
247
+ const LINE_HEIGHT = 20;
248
+ const OVERSCAN = 20;
249
+ const handleLineClick = useCallback((lineNum, e) => {
250
+ e.preventDefault();
251
+ updateFollow(false);
252
+ if (e.shiftKey && lastClickedLine !== null) {
253
+ const start = Math.min(lastClickedLine, lineNum);
254
+ const end = Math.max(lastClickedLine, lineNum);
255
+ const newSelected = /* @__PURE__ */ new Set();
256
+ for (let i = start; i <= end; i++) newSelected.add(i);
257
+ setSelectedLines(newSelected);
258
+ } else if (selectedLines.has(lineNum) && selectedLines.size === 1) setSelectedLines(/* @__PURE__ */ new Set());
259
+ else {
260
+ setSelectedLines(/* @__PURE__ */ new Set([lineNum]));
261
+ setLastClickedLine(lineNum);
262
+ }
263
+ }, [
264
+ lastClickedLine,
265
+ selectedLines,
266
+ updateFollow
267
+ ]);
268
+ const copyAllLogs = useCallback(async () => {
269
+ const text = rawLines.join("\n");
270
+ try {
271
+ await navigator.clipboard.writeText(text);
272
+ setCopied(true);
273
+ setTimeout(() => setCopied(false), 2e3);
274
+ } catch (_err) {
275
+ notify.error("Failed to copy logs to clipboard");
276
+ }
277
+ }, [rawLines, notify]);
278
+ const handleTextMouseUp = useCallback((e) => {
279
+ const selectedText = window.getSelection()?.toString().trim();
280
+ if (selectedText && selectedText.length > 0) {
281
+ setHighlightCopyPosition({
282
+ x: e.clientX,
283
+ y: e.clientY
284
+ });
285
+ setHighlightedText(selectedText);
286
+ } else {
287
+ setHighlightCopyPosition(null);
288
+ setHighlightedText("");
289
+ }
290
+ }, []);
291
+ const copyHighlightedText = useCallback(async () => {
292
+ if (!highlightedText) return;
293
+ try {
294
+ await navigator.clipboard.writeText(highlightedText);
295
+ notify.success("Text copied to clipboard");
296
+ window.getSelection()?.removeAllRanges();
297
+ setHighlightCopyPosition(null);
298
+ setHighlightedText("");
299
+ } catch (_err) {
300
+ notify.error("Failed to copy text to clipboard");
301
+ }
302
+ }, [highlightedText, notify]);
303
+ useEffect(() => {
304
+ const handleClickOutside = (e) => {
305
+ if (e.target.closest("[data-highlight-copy-button]")) return;
306
+ setTimeout(() => {
307
+ setHighlightCopyPosition(null);
308
+ setHighlightedText("");
309
+ }, 100);
310
+ };
311
+ const handleKeyDown = (e) => {
312
+ if (e.key === "Escape") {
313
+ setHighlightCopyPosition(null);
314
+ setHighlightedText("");
315
+ }
316
+ };
317
+ if (highlightCopyPosition) {
318
+ document.addEventListener("mousedown", handleClickOutside);
319
+ document.addEventListener("keydown", handleKeyDown);
320
+ }
321
+ return () => {
322
+ document.removeEventListener("mousedown", handleClickOutside);
323
+ document.removeEventListener("keydown", handleKeyDown);
324
+ };
325
+ }, [highlightCopyPosition]);
326
+ useEffect(() => {
327
+ if (!timeRangeOpen) return;
328
+ const handleClickOutside = (e) => {
329
+ const target = e.target;
330
+ if (timeRangePopoverRef.current?.contains(target)) return;
331
+ if (target.closest("[data-time-range-trigger]")) return;
332
+ setTimeRangeOpen(false);
333
+ };
334
+ const handleKey = (e) => {
335
+ if (e.key === "Escape") setTimeRangeOpen(false);
336
+ };
337
+ document.addEventListener("mousedown", handleClickOutside);
338
+ document.addEventListener("keydown", handleKey);
339
+ return () => {
340
+ document.removeEventListener("mousedown", handleClickOutside);
341
+ document.removeEventListener("keydown", handleKey);
342
+ };
343
+ }, [timeRangeOpen]);
344
+ const copySelectedLines = useCallback(async () => {
345
+ if (selectedLines.size === 0) return;
346
+ const text = Array.from(selectedLines).sort((a, b) => a - b).map((ln) => rawLines[ln - 1] || "").join("\n");
347
+ try {
348
+ await navigator.clipboard.writeText(text);
349
+ setCopied(true);
350
+ setTimeout(() => setCopied(false), 2e3);
351
+ } catch (_err) {
352
+ notify.error("Failed to copy selected logs to clipboard");
353
+ }
354
+ }, [
355
+ selectedLines,
356
+ rawLines,
357
+ notify
358
+ ]);
359
+ const handleGroupToggle = useCallback((lineIndex) => {
360
+ setCollapsedGroups((prev) => {
361
+ const next = new Set(prev);
362
+ if (next.has(lineIndex)) next.delete(lineIndex);
363
+ else next.add(lineIndex);
364
+ return next;
365
+ });
366
+ }, []);
367
+ const parsedLog = useMemo(() => {
368
+ let filtered = enableWorkflowCommands ? processedLines : fullParsedLog;
369
+ if (enableWorkflowCommands) filtered = filtered.filter((item) => {
370
+ if (item.command?.type === "endgroup") return false;
371
+ if (!showDebug && item.command?.type === "debug") return false;
372
+ if (item.groupId !== void 0 && item.command?.type !== "group" && collapsedGroups.has(item.groupId)) return false;
373
+ return true;
374
+ });
375
+ if (filter !== "") {
376
+ const normalizedFilter = filter.toLowerCase();
377
+ filtered = filtered.filter((item) => {
378
+ const command = enableWorkflowCommands ? item.command : void 0;
379
+ return (command?.message ? command.message : item.segments.map((segment) => segment.content).join("")).toLowerCase().includes(normalizedFilter);
380
+ });
381
+ }
382
+ return filtered;
383
+ }, [
384
+ fullParsedLog,
385
+ processedLines,
386
+ filter,
387
+ enableWorkflowCommands,
388
+ showDebug,
389
+ collapsedGroups
390
+ ]);
391
+ const smoothScrollToBottom = useCallback(() => {
392
+ if (!logContainerRef.current) return;
393
+ if (scrollAnimationRef.current) {
394
+ cancelAnimationFrame(scrollAnimationRef.current);
395
+ scrollAnimationRef.current = null;
396
+ }
397
+ const container = logContainerRef.current;
398
+ const targetScroll = container.scrollHeight - container.clientHeight;
399
+ const currentScroll = container.scrollTop;
400
+ const distance = targetScroll - currentScroll;
401
+ if (Math.abs(distance) < 5) {
402
+ container.scrollTop = targetScroll;
403
+ return;
404
+ }
405
+ const startTime = performance.now();
406
+ const duration = 150;
407
+ const animate = (currentTime) => {
408
+ const elapsed = currentTime - startTime;
409
+ const progress = Math.min(elapsed / duration, 1);
410
+ const eased = 1 - (1 - progress) ** 3;
411
+ container.scrollTop = currentScroll + distance * eased;
412
+ if (progress < 1) scrollAnimationRef.current = requestAnimationFrame(animate);
413
+ else scrollAnimationRef.current = null;
414
+ };
415
+ scrollAnimationRef.current = requestAnimationFrame(animate);
416
+ }, []);
417
+ const scrollLogViewer = useCallback((location, ref) => {
418
+ if (!ref?.current) return;
419
+ if (location === "top") ref.current.scroll({
420
+ top: 0,
421
+ left: 0,
422
+ behavior: "smooth"
423
+ });
424
+ else smoothScrollToBottom();
425
+ }, [smoothScrollToBottom]);
426
+ useEffect(() => {
427
+ const container = logContainerRef.current;
428
+ if (!container || inline) return;
429
+ const onWheel = (e) => {
430
+ if (e.deltaY < 0) userScrollUpRef.current = true;
431
+ if (scrollAnimationRef.current) {
432
+ cancelAnimationFrame(scrollAnimationRef.current);
433
+ scrollAnimationRef.current = null;
434
+ }
435
+ };
436
+ const onTouchStart = () => {
437
+ userScrollUpRef.current = true;
438
+ if (scrollAnimationRef.current) {
439
+ cancelAnimationFrame(scrollAnimationRef.current);
440
+ scrollAnimationRef.current = null;
441
+ }
442
+ };
443
+ container.addEventListener("wheel", onWheel, { passive: true });
444
+ container.addEventListener("touchstart", onTouchStart, { passive: true });
445
+ return () => {
446
+ container.removeEventListener("wheel", onWheel);
447
+ container.removeEventListener("touchstart", onTouchStart);
448
+ };
449
+ }, [inline]);
450
+ const maybeFireReachTop = useCallback((scrollTop) => {
451
+ if (!onReachTop || loadingTop || endOfLog) return;
452
+ if (scrollTop < 50 && !reachTopFiredRef.current) {
453
+ reachTopFiredRef.current = true;
454
+ onReachTop();
455
+ } else if (scrollTop > 100) reachTopFiredRef.current = false;
456
+ }, [
457
+ onReachTop,
458
+ loadingTop,
459
+ endOfLog
460
+ ]);
461
+ const handleLogScroll = (event) => {
462
+ const { scrollTop, clientHeight, scrollHeight } = event.currentTarget;
463
+ const isAnimating = scrollAnimationRef.current !== null;
464
+ const atBottom = scrollHeight - clientHeight - scrollTop < 2;
465
+ if (!isAnimating) {
466
+ if (atBottom && !follow) updateFollow(true);
467
+ else if (userScrollUpRef.current && follow) updateFollow(false);
468
+ userScrollUpRef.current = false;
469
+ }
470
+ maybeFireReachTop(scrollTop);
471
+ if (!followRef.current) {
472
+ const visibleCount = Math.ceil(clientHeight / LINE_HEIGHT) + 40;
473
+ const maxStart = Math.max(0, parsedLog.length - visibleCount);
474
+ const startIndex = Math.min(maxStart, Math.max(0, Math.floor(scrollTop / LINE_HEIGHT) - OVERSCAN));
475
+ setVisibleStart(startIndex);
476
+ setVisibleEnd(startIndex + visibleCount);
477
+ }
478
+ };
479
+ useEffect(() => {
480
+ if (!log) return;
481
+ const lines = (typeof log === "object" ? JSON.stringify(log) : log.toString()).split("\n");
482
+ const cleanedLines = lines[lines.length - 1] === "" ? lines.slice(0, -1) : lines;
483
+ const isAppending = cleanedLines.length > prevLogLengthRef.current;
484
+ prevLogLengthRef.current = cleanedLines.length;
485
+ setRawLines(cleanedLines);
486
+ const processed = cleanedLines.map((line, index) => {
487
+ if (ansiCache.current[line] === void 0) ansiCache.current[line] = parseAnsiSegments(line);
488
+ return {
489
+ index,
490
+ segments: ansiCache.current[line]
491
+ };
492
+ });
493
+ setFullParsedLog(processed);
494
+ if (enableWorkflowCommands && engine) setProcessedLines(engine.processLogLines(processed, cleanedLines));
495
+ if (followRef.current && logContainerRef.current) {
496
+ setVisibleEnd((prev) => Math.max(prev, cleanedLines.length));
497
+ if (isAppending) requestAnimationFrame(() => {
498
+ requestAnimationFrame(() => {
499
+ smoothScrollToBottom();
500
+ });
501
+ });
502
+ }
503
+ const file = new Blob([log], { type: "text/plaintext" });
504
+ const url = URL.createObjectURL(file);
505
+ setLogUrl(url);
506
+ return () => {
507
+ URL.revokeObjectURL(url);
508
+ if (scrollAnimationRef.current) cancelAnimationFrame(scrollAnimationRef.current);
509
+ };
510
+ }, [
511
+ log,
512
+ smoothScrollToBottom,
513
+ enableWorkflowCommands,
514
+ engine
515
+ ]);
516
+ const parsedLogLengthRef = useRef(parsedLog.length);
517
+ parsedLogLengthRef.current = parsedLog.length;
518
+ useEffect(() => {
519
+ if (!logContainerRef.current) return;
520
+ const observer = new ResizeObserver(() => {
521
+ if (!logContainerRef.current) return;
522
+ if (parsedLogLengthRef.current === 0) return;
523
+ const container = logContainerRef.current;
524
+ const clientHeight = container.clientHeight;
525
+ const visibleCount = Math.ceil(clientHeight / LINE_HEIGHT) + 40;
526
+ if (followRef.current) {
527
+ const minStart = Math.max(0, parsedLogLengthRef.current - visibleCount);
528
+ setVisibleStart((prev) => Math.min(prev, minStart));
529
+ setVisibleEnd(parsedLogLengthRef.current);
530
+ requestAnimationFrame(() => {
531
+ requestAnimationFrame(() => {
532
+ smoothScrollToBottom();
533
+ });
534
+ });
535
+ } else {
536
+ const maxStart = Math.max(0, parsedLogLengthRef.current - visibleCount);
537
+ const startIndex = Math.min(maxStart, Math.max(0, Math.floor(container.scrollTop / LINE_HEIGHT) - OVERSCAN));
538
+ setVisibleStart(startIndex);
539
+ setVisibleEnd(startIndex + visibleCount);
540
+ }
541
+ });
542
+ observer.observe(logContainerRef.current);
543
+ return () => observer.disconnect();
544
+ }, [smoothScrollToBottom]);
545
+ useLayoutEffect(() => {
546
+ if (prependedSignal > prevPrependedSignalRef.current) pendingPrependRef.current = true;
547
+ else if (prependedSignal < prevPrependedSignalRef.current) pendingPrependRef.current = false;
548
+ prevPrependedSignalRef.current = prependedSignal;
549
+ if (!pendingPrependRef.current) return;
550
+ const container = logContainerRef.current;
551
+ if (!container) return;
552
+ const delta = container.scrollHeight - prevScrollHeightRef.current;
553
+ if (delta <= 0) return;
554
+ pendingPrependRef.current = false;
555
+ container.scrollTop += delta;
556
+ if (!followRef.current) {
557
+ const visibleCount = Math.ceil(container.clientHeight / LINE_HEIGHT) + 40;
558
+ const maxStart = Math.max(0, parsedLog.length - visibleCount);
559
+ const startIndex = Math.min(maxStart, Math.max(0, Math.floor(container.scrollTop / LINE_HEIGHT) - OVERSCAN));
560
+ setVisibleStart(startIndex);
561
+ setVisibleEnd(startIndex + visibleCount);
562
+ }
563
+ }, [prependedSignal, parsedLog.length]);
564
+ useLayoutEffect(() => {
565
+ if (logContainerRef.current) prevScrollHeightRef.current = logContainerRef.current.scrollHeight;
566
+ });
567
+ const handleFilterChange = (event) => {
568
+ const newFilter = event.target.value;
569
+ setFilter(newFilter);
570
+ updateFollow(false);
571
+ if (logContainerRef.current) {
572
+ const clientHeight = logContainerRef.current.clientHeight;
573
+ const visibleCount = Math.ceil(clientHeight / LINE_HEIGHT) + 40;
574
+ setVisibleStart(0);
575
+ setVisibleEnd(Math.min(parsedLog.length, visibleCount));
576
+ scrollLogViewer("top", logContainerRef);
577
+ } else {
578
+ setVisibleStart(0);
579
+ setVisibleEnd(50);
580
+ }
581
+ };
582
+ const ExpandButtonIcon = fullscreen ? ShrinkIcon : ExpandIcon;
583
+ const expandButtonText = fullscreen ? "Collapse" : "Expand";
584
+ const paddingTop = visibleStart * LINE_HEIGHT;
585
+ const paddingBottom = Math.max(0, parsedLog.length - visibleEnd) * LINE_HEIGHT;
586
+ const visibleLogs = inline ? parsedLog : parsedLog.slice(visibleStart, Math.min(parsedLog.length, visibleEnd));
587
+ return /* @__PURE__ */ jsxs("div", {
588
+ "data-testid": "logviewer-container",
589
+ className: cx("flex flex-col grow", roundedBottom && "rounded-lg"),
590
+ style: !fullscreen ? {
591
+ height,
592
+ width
593
+ } : {
594
+ height: "calc(100% - 50px)",
595
+ width: "100%",
596
+ position: "fixed",
597
+ left: 0,
598
+ top: 50,
599
+ zIndex: 9999
600
+ },
601
+ children: [
602
+ /* @__PURE__ */ jsxs("div", {
603
+ className: cx("flex select-none items-center gap-x-2 p-2", inline ? "bg-[var(--theme-panel-bg)] border-b border-[var(--theme-border)]" : "theme-panel"),
604
+ children: [/* @__PURE__ */ jsxs("div", {
605
+ className: cx("w-full flex flex-row gap-x-2 items-center", hideExpand && "justify-between"),
606
+ children: [
607
+ /* @__PURE__ */ jsxs("div", {
608
+ className: "flex gap-x-2 w-full",
609
+ children: [
610
+ !inline && /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs("button", {
611
+ type: "button",
612
+ className: "btn btn-neutral",
613
+ onClick: () => {
614
+ scrollLogViewer("top", logContainerRef);
615
+ updateFollow(false);
616
+ },
617
+ children: [/* @__PURE__ */ jsx(ArrowUpIcon, {}), /* @__PURE__ */ jsx("span", { children: t.top })]
618
+ }), /* @__PURE__ */ jsxs("button", {
619
+ type: "button",
620
+ className: "btn btn-neutral",
621
+ onClick: () => {
622
+ if (!follow) {
623
+ updateFollow(true);
624
+ setVisibleEnd((prev) => Math.max(prev, parsedLog.length));
625
+ requestAnimationFrame(() => {
626
+ smoothScrollToBottom();
627
+ });
628
+ } else updateFollow(false);
629
+ },
630
+ children: [/* @__PURE__ */ jsx(ArrowDownIcon, {}), /* @__PURE__ */ jsx("span", { children: !follow ? "Follow" : "Following" })]
631
+ })] }),
632
+ selectedLines.size > 0 ? /* @__PURE__ */ jsxs("div", {
633
+ className: "relative flex items-center h-full",
634
+ children: [/* @__PURE__ */ jsxs("button", {
635
+ type: "button",
636
+ className: "btn btn-neutral",
637
+ onClick: copySelectedLines,
638
+ children: [copied ? /* @__PURE__ */ jsx(CheckIcon, { className: "text-green-500" }) : /* @__PURE__ */ jsx(CopyIcon, {}), /* @__PURE__ */ jsx("span", { children: copied ? tCommon.copied : t.copyLines(selectedLines.size) })]
639
+ }), /* @__PURE__ */ jsx("button", {
640
+ type: "button",
641
+ "aria-label": t.clearSelection,
642
+ className: "absolute -top-1.5 -right-1.5 btn-neutral rounded-full p-1 border transition-colors shadow-sm cursor-pointer z-10",
643
+ onClick: () => setSelectedLines(/* @__PURE__ */ new Set()),
644
+ title: t.clearSelection,
645
+ children: /* @__PURE__ */ jsx(XIcon, { className: "text-[10px]" })
646
+ })]
647
+ }) : /* @__PURE__ */ jsxs("button", {
648
+ type: "button",
649
+ className: "btn btn-neutral",
650
+ onClick: copyAllLogs,
651
+ children: [copied ? /* @__PURE__ */ jsx(CheckIcon, { className: "text-green-500" }) : /* @__PURE__ */ jsx(CopyIcon, {}), /* @__PURE__ */ jsx("span", { children: copied ? tCommon.copied : t.copyAll })]
652
+ }),
653
+ additionalLeftBarComponents,
654
+ enableWorkflowCommands && /* @__PURE__ */ jsx("button", {
655
+ type: "button",
656
+ className: cx("btn btn-neutral", showDebug && "btn-active"),
657
+ onClick: () => setShowDebug((prev) => !prev),
658
+ children: /* @__PURE__ */ jsx("span", { children: showDebug ? t.hideDebug : t.showDebug })
659
+ }),
660
+ enableTimeRangeFilter && /* @__PURE__ */ jsxs("div", {
661
+ className: "relative",
662
+ children: [/* @__PURE__ */ jsxs("button", {
663
+ type: "button",
664
+ "data-time-range-trigger": true,
665
+ className: "btn btn-neutral",
666
+ onClick: () => setTimeRangeOpen((o) => !o),
667
+ children: [
668
+ /* @__PURE__ */ jsx(ClockIcon, {}),
669
+ /* @__PURE__ */ jsx("span", { children: t.timeRange }),
670
+ (timeRangeFrom !== "" || timeRangeTo !== "") && /* @__PURE__ */ jsx("span", {
671
+ title: t.timeRangeActive,
672
+ className: "ml-1 px-1.5 py-0.5 text-[10px] rounded-full bg-[var(--theme-element)]/15 text-[var(--theme-element)]",
673
+ children: t.timeRangeOn
674
+ })
675
+ ]
676
+ }), timeRangeOpen && /* @__PURE__ */ jsxs("div", {
677
+ ref: timeRangePopoverRef,
678
+ className: cx("absolute top-full left-0 mt-1 z-50 w-80 p-3 rounded-lg shadow-lg", "border border-[var(--theme-border)] bg-[var(--theme-panel-bg)]", "flex flex-col gap-y-3"),
679
+ children: [
680
+ /* @__PURE__ */ jsxs("div", {
681
+ className: "flex items-center gap-x-1.5",
682
+ children: [/* @__PURE__ */ jsx("span", {
683
+ className: "text-xs font-semibold text-[var(--theme-app)]",
684
+ children: t.filterByTimeRange
685
+ }), /* @__PURE__ */ jsx(TooltipInfo, { text: t.timeRangeHelp })]
686
+ }),
687
+ /* @__PURE__ */ jsxs("label", {
688
+ className: "flex flex-col gap-y-1",
689
+ children: [/* @__PURE__ */ jsx("span", {
690
+ className: "text-[11px] uppercase tracking-wide text-[var(--theme-muted-text-color)]",
691
+ children: t.timeRangeFrom
692
+ }), /* @__PURE__ */ jsx("input", {
693
+ type: "datetime-local",
694
+ value: timeRangeFrom,
695
+ onChange: (e) => setTimeRangeFrom(e.target.value),
696
+ className: "rounded-md text-sm h-8 px-2 bg-[var(--theme-input-bg)] text-[var(--theme-input)] border border-[var(--theme-border)]"
697
+ })]
698
+ }),
699
+ /* @__PURE__ */ jsxs("label", {
700
+ className: "flex flex-col gap-y-1",
701
+ children: [/* @__PURE__ */ jsx("span", {
702
+ className: "text-[11px] uppercase tracking-wide text-[var(--theme-muted-text-color)]",
703
+ children: t.timeRangeTo
704
+ }), /* @__PURE__ */ jsx("input", {
705
+ type: "datetime-local",
706
+ value: timeRangeTo,
707
+ onChange: (e) => setTimeRangeTo(e.target.value),
708
+ className: "rounded-md text-sm h-8 px-2 bg-[var(--theme-input-bg)] text-[var(--theme-input)] border border-[var(--theme-border)]"
709
+ })]
710
+ }),
711
+ timeRangeError !== "" && /* @__PURE__ */ jsx("span", {
712
+ className: "text-[11px] text-red-500",
713
+ children: t[timeRangeError]
714
+ }),
715
+ /* @__PURE__ */ jsxs("div", {
716
+ className: "flex gap-x-2",
717
+ children: [/* @__PURE__ */ jsx("button", {
718
+ type: "button",
719
+ disabled: timeRangeError !== "",
720
+ className: "btn btn-primary flex-1 justify-center",
721
+ onClick: () => {
722
+ onTimeRangeChange?.(timeRangeFrom, timeRangeTo);
723
+ setTimeRangeOpen(false);
724
+ },
725
+ children: t.apply
726
+ }), (timeRangeFrom !== "" || timeRangeTo !== "") && /* @__PURE__ */ jsx("button", {
727
+ type: "button",
728
+ className: "btn btn-neutral flex-1 justify-center",
729
+ onClick: () => {
730
+ setTimeRangeFrom("");
731
+ setTimeRangeTo("");
732
+ onTimeRangeChange?.("", "");
733
+ setTimeRangeOpen(false);
734
+ },
735
+ children: t.clear
736
+ })]
737
+ })
738
+ ]
739
+ })]
740
+ })
741
+ ]
742
+ }),
743
+ /* @__PURE__ */ jsx("input", {
744
+ type: "text",
745
+ className: "rounded-md text-sm h-8",
746
+ placeholder: t.filterLogs,
747
+ onChange: handleFilterChange
748
+ }),
749
+ /* @__PURE__ */ jsxs("a", {
750
+ href: logUrl,
751
+ className: cx(!logUrl && "disabled", "btn btn-neutral"),
752
+ download: true,
753
+ children: [/* @__PURE__ */ jsx(DownloadIcon, {}), /* @__PURE__ */ jsx("span", { children: t.download })]
754
+ }),
755
+ /* @__PURE__ */ jsxs("button", {
756
+ type: "button",
757
+ className: cx("btn btn-neutral", hideExpand && "hidden"),
758
+ onClick: () => setFullscreen((prev) => !prev),
759
+ children: [/* @__PURE__ */ jsx(ExpandButtonIcon, {}), /* @__PURE__ */ jsx("span", { children: expandButtonText })]
760
+ })
761
+ ]
762
+ }), additionalRightBarComponents]
763
+ }),
764
+ /* @__PURE__ */ jsxs("div", {
765
+ "data-testid": "logviewer",
766
+ role: "none",
767
+ className: cx(roundedBottom && "rounded-b-lg", "w-full overflow-x-auto", !inline && "h-full grow overflow-y-auto", !inline && "bg-(--theme-muted-panel-bg)", contentClassName),
768
+ style: inline ? void 0 : {
769
+ overflowY: "auto",
770
+ position: "relative",
771
+ overscrollBehavior: "contain"
772
+ },
773
+ ref: logContainerRef,
774
+ onScroll: inline ? void 0 : handleLogScroll,
775
+ onMouseUp: handleTextMouseUp,
776
+ children: [!inline && onReachTop && /* @__PURE__ */ jsx(TopPaginationBanner, {
777
+ endOfLog,
778
+ loadingTop,
779
+ onLoadOlder: onReachTop
780
+ }), /* @__PURE__ */ jsx("div", {
781
+ className: "min-w-max",
782
+ style: inline ? void 0 : {
783
+ paddingTop,
784
+ paddingBottom
785
+ },
786
+ children: visibleLogs.map((item) => /* @__PURE__ */ jsx(LogLine, {
787
+ index: item.index,
788
+ segments: item.segments,
789
+ lineNumbers,
790
+ isSelected: selectedLines.has(item.index + 1),
791
+ lineHeight: LINE_HEIGHT,
792
+ onLineClick: handleLineClick,
793
+ searchTerm: filter,
794
+ command: enableWorkflowCommands ? item.command : void 0,
795
+ groupDepth: enableWorkflowCommands ? item.groupDepth : 0,
796
+ isGroupCollapsed: enableWorkflowCommands && item.command?.type === "group" ? collapsedGroups.has(item.index) : void 0,
797
+ onGroupToggle: enableWorkflowCommands ? handleGroupToggle : void 0,
798
+ annotationLabels
799
+ }, item.index))
800
+ })]
801
+ }),
802
+ highlightCopyPosition && highlightedText && /* @__PURE__ */ jsx("button", {
803
+ type: "button",
804
+ "data-highlight-copy-button": true,
805
+ className: cx("fixed z-[10000] flex items-center gap-1 px-2 py-1.5 text-xs font-medium", "rounded-md shadow-lg border cursor-pointer transition-colors duration-150", "bg-(--theme-panel-bg) border-(--theme-border) hover:bg-(--theme-hover) text-(--theme-app)"),
806
+ style: {
807
+ left: highlightCopyPosition.x,
808
+ top: highlightCopyPosition.y + 8,
809
+ transform: "translateX(-50%)"
810
+ },
811
+ onClick: copyHighlightedText,
812
+ children: copied ? /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(CheckIcon, { className: "text-green-500 w-3.5 h-3.5" }), /* @__PURE__ */ jsx("span", { children: tCommon.copied })] }) : /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(CopyIcon, { className: "w-3.5 h-3.5" }), /* @__PURE__ */ jsx("span", { children: tCommon.copy })] })
813
+ }),
814
+ (additionalBottomLeftBarComponents || additionalBottomRightBarComponents) && /* @__PURE__ */ jsxs("div", {
815
+ className: cx("flex theme-panel select-none items-center gap-x-2 p-2"),
816
+ children: [/* @__PURE__ */ jsx("div", {
817
+ className: cx("w-full flex flex-row gap-x-2 items-center", hideExpand && "justify-between"),
818
+ children: /* @__PURE__ */ jsx("div", {
819
+ className: "flex gap-x-2 w-full",
820
+ children: additionalBottomLeftBarComponents
821
+ })
822
+ }), additionalBottomRightBarComponents]
823
+ })
824
+ ]
825
+ });
826
+ }
827
+ //#endregion
828
+ export { plainSegments as i, highlightSegments as n, parseAnsiSegments as r, LogViewer as t };