@echothink-ui/developer 0.1.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 (56) hide show
  1. package/README.md +5 -0
  2. package/dist/components/APIExplorer.d.ts +2 -0
  3. package/dist/components/BranchSelector.d.ts +2 -0
  4. package/dist/components/CodeBlock.d.ts +2 -0
  5. package/dist/components/CodeEditor.d.ts +2 -0
  6. package/dist/components/CommitList.d.ts +2 -0
  7. package/dist/components/DiffTable.d.ts +2 -0
  8. package/dist/components/DiffViewer.d.ts +2 -0
  9. package/dist/components/EventPayloadViewer.d.ts +2 -0
  10. package/dist/components/GitRepositoryPanel.d.ts +2 -0
  11. package/dist/components/JSONViewer.d.ts +2 -0
  12. package/dist/components/LogConsole.d.ts +2 -0
  13. package/dist/components/PullRequestPanel.d.ts +2 -0
  14. package/dist/components/RequestResponseViewer.d.ts +2 -0
  15. package/dist/components/SchemaViewer.d.ts +2 -0
  16. package/dist/components/TerminalPanel.d.ts +2 -0
  17. package/dist/components/TraceTimeline.d.ts +2 -0
  18. package/dist/components/WebhookEventViewer.d.ts +2 -0
  19. package/dist/components/YAMLViewer.d.ts +2 -0
  20. package/dist/components/devUtils.d.ts +10 -0
  21. package/dist/components/types.d.ts +196 -0
  22. package/dist/index.cjs +2627 -0
  23. package/dist/index.cjs.map +1 -0
  24. package/dist/index.css +3651 -0
  25. package/dist/index.css.map +1 -0
  26. package/dist/index.d.ts +22 -0
  27. package/dist/index.js +2572 -0
  28. package/dist/index.js.map +1 -0
  29. package/package.json +43 -0
  30. package/src/components/APIExplorer.tsx +205 -0
  31. package/src/components/BranchSelector.tsx +54 -0
  32. package/src/components/CodeBlock.tsx +127 -0
  33. package/src/components/CodeEditor.tsx +95 -0
  34. package/src/components/CommitList.tsx +100 -0
  35. package/src/components/DiffTable.tsx +288 -0
  36. package/src/components/DiffViewer.tsx +145 -0
  37. package/src/components/EventPayloadViewer.tsx +91 -0
  38. package/src/components/GitRepositoryPanel.tsx +73 -0
  39. package/src/components/JSONViewer.tsx +189 -0
  40. package/src/components/LogConsole.tsx +160 -0
  41. package/src/components/PullRequestPanel.test.tsx +52 -0
  42. package/src/components/PullRequestPanel.tsx +215 -0
  43. package/src/components/RequestResponseViewer.test.tsx +45 -0
  44. package/src/components/RequestResponseViewer.tsx +169 -0
  45. package/src/components/SchemaViewer.tsx +157 -0
  46. package/src/components/TerminalPanel.test.tsx +33 -0
  47. package/src/components/TerminalPanel.tsx +134 -0
  48. package/src/components/TraceTimeline.test.tsx +63 -0
  49. package/src/components/TraceTimeline.tsx +207 -0
  50. package/src/components/WebhookEventViewer.test.tsx +57 -0
  51. package/src/components/WebhookEventViewer.tsx +184 -0
  52. package/src/components/YAMLViewer.tsx +207 -0
  53. package/src/components/devUtils.ts +81 -0
  54. package/src/components/types.ts +230 -0
  55. package/src/index.tsx +72 -0
  56. package/src/styles.css +4296 -0
package/dist/index.js ADDED
@@ -0,0 +1,2572 @@
1
+ // src/components/APIExplorer.tsx
2
+ import * as React from "react";
3
+ import { Button, FormField, Select, TextInput, Textarea } from "@echothink-ui/core";
4
+
5
+ // src/components/devUtils.ts
6
+ function splitLines(value) {
7
+ return value.length ? value.replace(/\r\n/g, "\n").split("\n") : [""];
8
+ }
9
+ function buildLineDiff(before, after) {
10
+ const a = splitDiffLines(before);
11
+ const b = splitDiffLines(after);
12
+ const table = Array.from({ length: a.length + 1 }, () => Array(b.length + 1).fill(0));
13
+ for (let i2 = a.length - 1; i2 >= 0; i2 -= 1) {
14
+ for (let j2 = b.length - 1; j2 >= 0; j2 -= 1) {
15
+ table[i2][j2] = a[i2] === b[j2] ? (table[i2 + 1]?.[j2 + 1] ?? 0) + 1 : Math.max(table[i2 + 1]?.[j2] ?? 0, table[i2]?.[j2 + 1] ?? 0);
16
+ }
17
+ }
18
+ const lines = [];
19
+ let i = 0;
20
+ let j = 0;
21
+ let beforeLine = 1;
22
+ let afterLine = 1;
23
+ while (i < a.length && j < b.length) {
24
+ if (a[i] === b[j]) {
25
+ lines.push({ type: "equal", beforeLine, afterLine, text: a[i] ?? "" });
26
+ i += 1;
27
+ j += 1;
28
+ beforeLine += 1;
29
+ afterLine += 1;
30
+ } else if ((table[i + 1]?.[j] ?? 0) >= (table[i]?.[j + 1] ?? 0)) {
31
+ lines.push({ type: "remove", beforeLine, text: a[i] ?? "" });
32
+ i += 1;
33
+ beforeLine += 1;
34
+ } else {
35
+ lines.push({ type: "add", afterLine, text: b[j] ?? "" });
36
+ j += 1;
37
+ afterLine += 1;
38
+ }
39
+ }
40
+ while (i < a.length) {
41
+ lines.push({ type: "remove", beforeLine, text: a[i] ?? "" });
42
+ i += 1;
43
+ beforeLine += 1;
44
+ }
45
+ while (j < b.length) {
46
+ lines.push({ type: "add", afterLine, text: b[j] ?? "" });
47
+ j += 1;
48
+ afterLine += 1;
49
+ }
50
+ return lines;
51
+ }
52
+ function splitDiffLines(value) {
53
+ const lines = value.length ? value.replace(/\r\n/g, "\n").split("\n") : [];
54
+ return lines.length > 1 && lines[lines.length - 1] === "" ? lines.slice(0, -1) : lines;
55
+ }
56
+ function formatUnknown(value) {
57
+ if (typeof value === "string") return value;
58
+ if (value == null) return "";
59
+ try {
60
+ return JSON.stringify(value, null, 2);
61
+ } catch {
62
+ return String(value);
63
+ }
64
+ }
65
+ function compactSha(sha) {
66
+ return sha.length > 10 ? sha.slice(0, 10) : sha;
67
+ }
68
+
69
+ // src/components/APIExplorer.tsx
70
+ import { jsx, jsxs } from "react/jsx-runtime";
71
+ function APIExplorer({
72
+ endpoints,
73
+ selectedId,
74
+ onSelect,
75
+ onSend,
76
+ className,
77
+ ...props
78
+ }) {
79
+ const [internalId, setInternalId] = React.useState(selectedId ?? endpoints[0]?.id);
80
+ const selected = endpoints.find((endpoint) => endpoint.id === (selectedId ?? internalId)) ?? endpoints[0];
81
+ const [method, setMethod] = React.useState(selected?.method ?? "GET");
82
+ const [path, setPath] = React.useState(selected?.path ?? "");
83
+ const [headers, setHeaders] = React.useState(() => formatEndpointValue(selected?.headers, "{}"));
84
+ const [body, setBody] = React.useState(() => formatEndpointValue(selected?.body, ""));
85
+ const [response, setResponse] = React.useState({ status: "idle" });
86
+ const headerError = validateHeaderJson(headers);
87
+ React.useEffect(() => {
88
+ if (!selected) return;
89
+ setMethod(selected.method);
90
+ setPath(selected.path);
91
+ setHeaders(formatEndpointValue(selected.headers, "{}"));
92
+ setBody(formatEndpointValue(selected.body, ""));
93
+ }, [selected?.id]);
94
+ const choose = (endpoint) => {
95
+ setInternalId(endpoint.id);
96
+ onSelect?.(endpoint.id);
97
+ };
98
+ const send = async () => {
99
+ if (headerError || !path.trim()) return;
100
+ const request = { endpointId: selected?.id, method, path, headers, body };
101
+ setResponse({ status: "loading" });
102
+ try {
103
+ const result = await onSend?.(request);
104
+ setResponse({ status: "success", value: result ?? { ok: true, request } });
105
+ } catch (error) {
106
+ setResponse({ status: "error", value: formatError(error) });
107
+ }
108
+ };
109
+ const isLoading = response.status === "loading";
110
+ const responseStateLabel = response.status === "idle" ? "Ready" : response.status;
111
+ return /* @__PURE__ */ jsxs(
112
+ "section",
113
+ {
114
+ ...props,
115
+ className: `eth-dev-api-explorer ${className ?? ""}`,
116
+ "data-eth-component": "APIExplorer",
117
+ children: [
118
+ /* @__PURE__ */ jsxs("nav", { className: "eth-dev-api-explorer__nav", "aria-label": "Endpoints", children: [
119
+ /* @__PURE__ */ jsxs("div", { className: "eth-dev-api-explorer__nav-heading", children: [
120
+ /* @__PURE__ */ jsx("span", { children: "Endpoints" }),
121
+ /* @__PURE__ */ jsx("span", { children: endpoints.length })
122
+ ] }),
123
+ /* @__PURE__ */ jsx("div", { className: "eth-dev-api-explorer__endpoint-list", children: endpoints.length ? endpoints.map((endpoint) => /* @__PURE__ */ jsxs(
124
+ "button",
125
+ {
126
+ type: "button",
127
+ "aria-current": selected?.id === endpoint.id,
128
+ onClick: () => choose(endpoint),
129
+ children: [
130
+ /* @__PURE__ */ jsx(
131
+ "span",
132
+ {
133
+ className: `eth-dev-api-explorer__method eth-dev-api-explorer__method--${methodToken(endpoint.method)}`,
134
+ children: endpoint.method
135
+ }
136
+ ),
137
+ /* @__PURE__ */ jsxs("span", { className: "eth-dev-api-explorer__endpoint-copy", children: [
138
+ /* @__PURE__ */ jsx("code", { children: endpoint.path }),
139
+ endpoint.summary ? /* @__PURE__ */ jsx("small", { children: endpoint.summary }) : null
140
+ ] })
141
+ ]
142
+ },
143
+ endpoint.id
144
+ )) : /* @__PURE__ */ jsx("p", { className: "eth-dev-api-explorer__empty", children: "No endpoints configured." }) })
145
+ ] }),
146
+ /* @__PURE__ */ jsxs(
147
+ "form",
148
+ {
149
+ className: "eth-dev-api-explorer__request",
150
+ onSubmit: (event) => {
151
+ event.preventDefault();
152
+ void send();
153
+ },
154
+ children: [
155
+ /* @__PURE__ */ jsxs("header", { className: "eth-dev-api-explorer__request-header", children: [
156
+ /* @__PURE__ */ jsxs("div", { children: [
157
+ /* @__PURE__ */ jsx("p", { children: "Request" }),
158
+ /* @__PURE__ */ jsx("h3", { children: selected?.summary ?? "Custom endpoint" })
159
+ ] }),
160
+ selected ? /* @__PURE__ */ jsx("code", { children: selected.id }) : null
161
+ ] }),
162
+ /* @__PURE__ */ jsxs("div", { className: "eth-dev-api-explorer__method-path", children: [
163
+ /* @__PURE__ */ jsx(FormField, { label: "Method", children: /* @__PURE__ */ jsx(
164
+ Select,
165
+ {
166
+ value: method,
167
+ options: ["GET", "POST", "PUT", "PATCH", "DELETE"].map((item) => ({
168
+ value: item,
169
+ label: item
170
+ })),
171
+ onChange: (event) => setMethod(event.currentTarget.value)
172
+ }
173
+ ) }),
174
+ /* @__PURE__ */ jsx(FormField, { label: "Path", children: /* @__PURE__ */ jsx(TextInput, { value: path, onChange: (event) => setPath(event.currentTarget.value) }) })
175
+ ] }),
176
+ /* @__PURE__ */ jsx(FormField, { label: "Headers", error: headerError, children: /* @__PURE__ */ jsx(
177
+ Textarea,
178
+ {
179
+ className: "eth-dev-api-explorer__code-field",
180
+ value: headers,
181
+ onChange: (event) => setHeaders(event.currentTarget.value),
182
+ rows: 5
183
+ }
184
+ ) }),
185
+ /* @__PURE__ */ jsx(FormField, { label: "Body", children: /* @__PURE__ */ jsx(
186
+ Textarea,
187
+ {
188
+ className: "eth-dev-api-explorer__code-field",
189
+ value: body,
190
+ onChange: (event) => setBody(event.currentTarget.value),
191
+ rows: 8
192
+ }
193
+ ) }),
194
+ /* @__PURE__ */ jsxs("div", { className: "eth-dev-api-explorer__actions", children: [
195
+ /* @__PURE__ */ jsxs("span", { children: [
196
+ method,
197
+ " ",
198
+ path || "/"
199
+ ] }),
200
+ /* @__PURE__ */ jsx(Button, { type: "submit", loading: isLoading, disabled: Boolean(headerError) || !path.trim(), children: "Send" })
201
+ ] })
202
+ ]
203
+ }
204
+ ),
205
+ /* @__PURE__ */ jsxs("section", { className: "eth-dev-api-explorer__response", "aria-live": "polite", "aria-busy": isLoading, children: [
206
+ /* @__PURE__ */ jsxs("header", { className: "eth-dev-api-explorer__response-header", children: [
207
+ /* @__PURE__ */ jsx("h3", { children: "Response" }),
208
+ /* @__PURE__ */ jsx(
209
+ "span",
210
+ {
211
+ className: `eth-dev-api-explorer__response-state eth-dev-api-explorer__response-state--${response.status}`,
212
+ children: responseStateLabel
213
+ }
214
+ )
215
+ ] }),
216
+ response.status === "idle" ? /* @__PURE__ */ jsx("div", { className: "eth-dev-api-explorer__response-empty", children: "No response yet." }) : response.status === "loading" ? /* @__PURE__ */ jsx("div", { className: "eth-dev-api-explorer__response-empty", children: "Waiting for response." }) : /* @__PURE__ */ jsx("pre", { children: /* @__PURE__ */ jsx("code", { children: formatUnknown(response.value) }) })
217
+ ] })
218
+ ]
219
+ }
220
+ );
221
+ }
222
+ function formatEndpointValue(value, fallback) {
223
+ if (value == null) return fallback;
224
+ if (typeof value === "string") return value;
225
+ return formatUnknown(value);
226
+ }
227
+ function validateHeaderJson(value) {
228
+ const trimmed = value.trim();
229
+ if (!trimmed) return null;
230
+ try {
231
+ const parsed = JSON.parse(trimmed);
232
+ if (!parsed || Array.isArray(parsed) || typeof parsed !== "object") {
233
+ return "Headers must be a JSON object.";
234
+ }
235
+ } catch {
236
+ return "Headers must be valid JSON.";
237
+ }
238
+ return null;
239
+ }
240
+ function formatError(error) {
241
+ if (error instanceof Error) {
242
+ return { error: error.message };
243
+ }
244
+ return error;
245
+ }
246
+ function methodToken(method) {
247
+ return method.toLowerCase().replace(/[^a-z0-9-]/g, "");
248
+ }
249
+
250
+ // src/components/BranchSelector.tsx
251
+ import { Select as Select2 } from "@echothink-ui/core";
252
+ import { jsx as jsx2 } from "react/jsx-runtime";
253
+ function BranchSelector({
254
+ branches,
255
+ value,
256
+ onChange,
257
+ className,
258
+ disabled,
259
+ labelText = "Branch",
260
+ helperText,
261
+ density = "compact",
262
+ ...props
263
+ }) {
264
+ const hasBranches = branches.length > 0;
265
+ const branchCountLabel = branches.length === 1 ? "1 branch available" : `${branches.length} branches available`;
266
+ const resolvedHelperText = helperText === void 0 ? hasBranches ? branchCountLabel : "No branches available" : helperText;
267
+ return /* @__PURE__ */ jsx2(
268
+ Select2,
269
+ {
270
+ ...props,
271
+ className: [
272
+ "eth-dev-branch-selector",
273
+ !hasBranches && "eth-dev-branch-selector--empty",
274
+ className
275
+ ].filter(Boolean).join(" "),
276
+ "data-eth-component": "BranchSelector",
277
+ density,
278
+ disabled: disabled || !hasBranches,
279
+ helperText: resolvedHelperText,
280
+ labelText,
281
+ value: hasBranches ? value : "",
282
+ options: hasBranches ? branches.map((branch) => ({ value: branch, label: branch })) : [{ value: "", label: "No branches available", disabled: true }],
283
+ onChange: (event) => {
284
+ if (hasBranches) {
285
+ onChange?.(event.currentTarget.value);
286
+ }
287
+ }
288
+ }
289
+ );
290
+ }
291
+
292
+ // src/components/CodeBlock.tsx
293
+ import * as React2 from "react";
294
+ import { Checkmark, Copy } from "@carbon/icons-react";
295
+ import { IconButton } from "@echothink-ui/core";
296
+ import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
297
+ var keywordPattern = /\b(?:async|await|break|case|catch|class|const|continue|default|else|export|extends|false|finally|for|from|function|if|import|interface|let|new|null|return|throw|true|try|type|undefined|var|while)\b/;
298
+ var tokenPattern = /\/\/.*|\/\*.*?\*\/|"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|`(?:\\.|[^`\\])*`|\b\d+(?:\.\d+)?\b|\b(?:async|await|break|case|catch|class|const|continue|default|else|export|extends|false|finally|for|from|function|if|import|interface|let|new|null|return|throw|true|try|type|undefined|var|while)\b/g;
299
+ function getTokenKind(token) {
300
+ if (token.startsWith("//") || token.startsWith("/*")) return "comment";
301
+ if (token.startsWith('"') || token.startsWith("'") || token.startsWith("`")) return "string";
302
+ if (/^\d/.test(token)) return "number";
303
+ if (keywordPattern.test(token)) return "keyword";
304
+ return "plain";
305
+ }
306
+ function renderCodeLine(line) {
307
+ const parts = [];
308
+ let lastIndex = 0;
309
+ tokenPattern.lastIndex = 0;
310
+ for (let match = tokenPattern.exec(line); match; match = tokenPattern.exec(line)) {
311
+ const token = match[0];
312
+ if (match.index > lastIndex) {
313
+ parts.push(line.slice(lastIndex, match.index));
314
+ }
315
+ const kind = getTokenKind(token);
316
+ parts.push(
317
+ /* @__PURE__ */ jsx3(
318
+ "span",
319
+ {
320
+ className: `eth-dev-code-block__token eth-dev-code-block__token--${kind}`,
321
+ children: token
322
+ },
323
+ `${match.index}-${token}`
324
+ )
325
+ );
326
+ lastIndex = match.index + token.length;
327
+ }
328
+ if (lastIndex < line.length) {
329
+ parts.push(line.slice(lastIndex));
330
+ }
331
+ return parts.length ? parts : line;
332
+ }
333
+ function CodeBlock({
334
+ code,
335
+ language = "text",
336
+ showLineNumbers = false,
337
+ className,
338
+ ...props
339
+ }) {
340
+ const [copied, setCopied] = React2.useState(false);
341
+ const resetTimer = React2.useRef(void 0);
342
+ const lines = splitLines(code);
343
+ React2.useEffect(() => {
344
+ return () => {
345
+ if (resetTimer.current !== void 0 && typeof window !== "undefined") {
346
+ window.clearTimeout(resetTimer.current);
347
+ }
348
+ };
349
+ }, []);
350
+ const copy = React2.useCallback(() => {
351
+ if (typeof navigator === "undefined" || !navigator.clipboard) return;
352
+ void navigator.clipboard.writeText(code).then(() => {
353
+ if (typeof window !== "undefined") {
354
+ setCopied(true);
355
+ if (resetTimer.current !== void 0) {
356
+ window.clearTimeout(resetTimer.current);
357
+ }
358
+ resetTimer.current = window.setTimeout(() => setCopied(false), 1200);
359
+ }
360
+ }).catch(() => {
361
+ setCopied(false);
362
+ });
363
+ }, [code]);
364
+ const normalizedLanguage = language.trim() || "text";
365
+ const languageClass = normalizedLanguage.toLowerCase().replace(/[^a-z0-9_-]/g, "-");
366
+ return /* @__PURE__ */ jsxs2(
367
+ "section",
368
+ {
369
+ ...props,
370
+ className: `eth-dev-code-block ${showLineNumbers ? "eth-dev-code-block--numbered" : ""} ${className ?? ""}`,
371
+ "data-eth-component": "CodeBlock",
372
+ "data-language": normalizedLanguage,
373
+ children: [
374
+ /* @__PURE__ */ jsxs2("header", { className: "eth-dev-code-block__header", children: [
375
+ /* @__PURE__ */ jsx3("span", { className: "eth-dev-code-block__language", children: normalizedLanguage }),
376
+ /* @__PURE__ */ jsx3(
377
+ IconButton,
378
+ {
379
+ className: "eth-dev-code-block__copy-button",
380
+ label: copied ? "Copied" : "Copy code",
381
+ intent: "ghost",
382
+ density: "compact",
383
+ icon: copied ? /* @__PURE__ */ jsx3(Checkmark, { size: 16 }) : /* @__PURE__ */ jsx3(Copy, { size: 16 }),
384
+ onClick: copy
385
+ }
386
+ ),
387
+ /* @__PURE__ */ jsx3("span", { className: "eth-dev-code-block__copy-status", "aria-live": "polite", children: copied ? "Code copied" : "" })
388
+ ] }),
389
+ /* @__PURE__ */ jsx3("pre", { className: `eth-dev-code-block__pre language-${languageClass}`, children: /* @__PURE__ */ jsx3("code", { className: "eth-dev-code-block__code", children: lines.map((line, index) => /* @__PURE__ */ jsxs2("span", { className: "eth-dev-code-block__line", children: [
390
+ showLineNumbers ? /* @__PURE__ */ jsx3("span", { className: "eth-dev-code-block__gutter", "aria-hidden": true, children: index + 1 }) : null,
391
+ /* @__PURE__ */ jsx3("span", { className: "eth-dev-code-block__line-code", children: renderCodeLine(line) })
392
+ ] }, index)) }) })
393
+ ]
394
+ }
395
+ );
396
+ }
397
+
398
+ // src/components/CodeEditor.tsx
399
+ import * as React3 from "react";
400
+ import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
401
+ function CodeEditor({
402
+ value,
403
+ onChange,
404
+ language = "text",
405
+ readonly,
406
+ readOnly,
407
+ schemaRef,
408
+ className,
409
+ disabled,
410
+ onScroll,
411
+ rows,
412
+ wrap,
413
+ "aria-describedby": ariaDescribedBy,
414
+ "aria-invalid": ariaInvalid,
415
+ "aria-label": ariaLabel,
416
+ ...props
417
+ }) {
418
+ const lines = splitLines(value);
419
+ const isReadonly = readonly ?? readOnly ?? false;
420
+ const isInvalid = ariaInvalid === true || ariaInvalid === "true" || ariaInvalid === "grammar" || ariaInvalid === "spelling";
421
+ const languageLabel = language || "text";
422
+ const lineCount = lines.length;
423
+ const visibleRows = rows ?? Math.min(Math.max(lineCount, 8), 16);
424
+ const statusId = React3.useId();
425
+ const gutterRef = React3.useRef(null);
426
+ const describedBy = [ariaDescribedBy, statusId].filter(Boolean).join(" ") || void 0;
427
+ const modeLabel = disabled ? "Disabled" : isReadonly ? "Read-only" : "Editable";
428
+ const handleScroll = (event) => {
429
+ if (gutterRef.current) {
430
+ gutterRef.current.scrollTop = event.currentTarget.scrollTop;
431
+ }
432
+ onScroll?.(event);
433
+ };
434
+ return /* @__PURE__ */ jsxs3(
435
+ "section",
436
+ {
437
+ className: `eth-dev-code-editor ${className ?? ""}`,
438
+ "data-eth-component": "CodeEditor",
439
+ "data-language": language,
440
+ "data-readonly": isReadonly ? "true" : void 0,
441
+ "data-disabled": disabled ? "true" : void 0,
442
+ "data-invalid": isInvalid ? "true" : void 0,
443
+ "data-schema-ref": schemaRef,
444
+ children: [
445
+ /* @__PURE__ */ jsxs3("header", { className: "eth-dev-code-editor__header", children: [
446
+ /* @__PURE__ */ jsxs3("div", { className: "eth-dev-code-editor__heading", children: [
447
+ /* @__PURE__ */ jsx4("span", { className: "eth-dev-code-editor__title", children: "Code editor" }),
448
+ /* @__PURE__ */ jsx4("span", { className: "eth-dev-code-editor__language", children: languageLabel })
449
+ ] }),
450
+ /* @__PURE__ */ jsxs3("div", { className: "eth-dev-code-editor__badges", "aria-hidden": "true", children: [
451
+ /* @__PURE__ */ jsx4("span", { className: "eth-dev-code-editor__badge", children: modeLabel }),
452
+ schemaRef ? /* @__PURE__ */ jsx4("span", { className: "eth-dev-code-editor__badge", children: schemaRef }) : null
453
+ ] })
454
+ ] }),
455
+ /* @__PURE__ */ jsxs3("div", { className: "eth-dev-code-editor__body", children: [
456
+ /* @__PURE__ */ jsx4("div", { ref: gutterRef, className: "eth-dev-code-editor__gutter", "aria-hidden": "true", children: lines.map((_, index) => /* @__PURE__ */ jsx4("span", { children: index + 1 }, index)) }),
457
+ /* @__PURE__ */ jsx4(
458
+ "textarea",
459
+ {
460
+ ...props,
461
+ "aria-describedby": describedBy,
462
+ "aria-invalid": ariaInvalid,
463
+ "aria-label": ariaLabel ?? `${languageLabel} code editor`,
464
+ value,
465
+ disabled,
466
+ readOnly: isReadonly,
467
+ rows: visibleRows,
468
+ spellCheck: false,
469
+ wrap: wrap ?? "off",
470
+ className: "eth-dev-code-editor__textarea",
471
+ onChange: (event) => onChange?.(event.currentTarget.value),
472
+ onScroll: handleScroll
473
+ }
474
+ )
475
+ ] }),
476
+ /* @__PURE__ */ jsxs3("footer", { id: statusId, className: "eth-dev-code-editor__footer", children: [
477
+ /* @__PURE__ */ jsx4("span", { children: modeLabel }),
478
+ /* @__PURE__ */ jsxs3("span", { children: [
479
+ lineCount,
480
+ " ",
481
+ lineCount === 1 ? "line" : "lines",
482
+ " / ",
483
+ value.length,
484
+ " chars"
485
+ ] })
486
+ ] })
487
+ ]
488
+ }
489
+ );
490
+ }
491
+
492
+ // src/components/CommitList.tsx
493
+ import { EmptyState } from "@echothink-ui/core";
494
+ import { DataTable } from "@echothink-ui/data";
495
+ import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
496
+ function authorInitials(author) {
497
+ const parts = author.trim().split(/\s+/).filter(Boolean);
498
+ if (!parts.length) return "?";
499
+ if (parts.length === 1) return parts[0].slice(0, 2).toUpperCase();
500
+ return `${parts[0][0]}${parts[parts.length - 1][0]}`.toUpperCase();
501
+ }
502
+ function CommitList({ commits, className, ...props }) {
503
+ const latestCommit = commits[0];
504
+ const authorCount = new Set(commits.map((commit) => commit.author)).size;
505
+ const columns = [
506
+ {
507
+ key: "sha",
508
+ header: "SHA",
509
+ width: "8.5rem",
510
+ render: (row) => /* @__PURE__ */ jsx5("code", { className: "eth-dev-commit-list__sha", title: row.sha, children: compactSha(row.sha) })
511
+ },
512
+ {
513
+ key: "message",
514
+ header: "Message",
515
+ render: (row) => /* @__PURE__ */ jsx5("span", { className: "eth-dev-commit-list__message", title: row.message, children: row.message })
516
+ },
517
+ {
518
+ key: "author",
519
+ header: "Author",
520
+ width: "12rem",
521
+ render: (row) => /* @__PURE__ */ jsxs4("span", { className: "eth-dev-commit-list__author", children: [
522
+ /* @__PURE__ */ jsx5("span", { className: "eth-dev-commit-list__avatar", "aria-hidden": "true", children: authorInitials(row.author) }),
523
+ /* @__PURE__ */ jsx5("span", { className: "eth-dev-commit-list__author-name", children: row.author })
524
+ ] })
525
+ },
526
+ {
527
+ key: "date",
528
+ header: "Date",
529
+ width: "10rem",
530
+ render: (row) => /* @__PURE__ */ jsx5("span", { className: "eth-dev-commit-list__date", children: row.date })
531
+ }
532
+ ];
533
+ return /* @__PURE__ */ jsxs4(
534
+ "section",
535
+ {
536
+ ...props,
537
+ "aria-label": props["aria-label"] ?? "Commit history",
538
+ className: `eth-dev-commit-list ${className ?? ""}`,
539
+ "data-eth-component": "CommitList",
540
+ children: [
541
+ /* @__PURE__ */ jsxs4("header", { className: "eth-dev-commit-list__header", children: [
542
+ /* @__PURE__ */ jsxs4("div", { className: "eth-dev-commit-list__heading", children: [
543
+ /* @__PURE__ */ jsx5("p", { children: "Source control" }),
544
+ /* @__PURE__ */ jsx5("h3", { children: "Commit history" })
545
+ ] }),
546
+ /* @__PURE__ */ jsxs4("dl", { className: "eth-dev-commit-list__summary", "aria-label": "Commit history summary", children: [
547
+ /* @__PURE__ */ jsxs4("div", { children: [
548
+ /* @__PURE__ */ jsx5("dt", { children: "Commits" }),
549
+ /* @__PURE__ */ jsx5("dd", { children: commits.length })
550
+ ] }),
551
+ /* @__PURE__ */ jsxs4("div", { children: [
552
+ /* @__PURE__ */ jsx5("dt", { children: "Authors" }),
553
+ /* @__PURE__ */ jsx5("dd", { children: authorCount })
554
+ ] }),
555
+ /* @__PURE__ */ jsxs4("div", { children: [
556
+ /* @__PURE__ */ jsx5("dt", { children: "Latest" }),
557
+ /* @__PURE__ */ jsx5("dd", { children: latestCommit ? compactSha(latestCommit.sha) : "None" })
558
+ ] })
559
+ ] })
560
+ ] }),
561
+ /* @__PURE__ */ jsx5(
562
+ DataTable,
563
+ {
564
+ rows: commits,
565
+ columns,
566
+ rowKey: "sha",
567
+ density: "compact",
568
+ className: "eth-dev-commit-list__table",
569
+ emptyState: /* @__PURE__ */ jsx5(
570
+ EmptyState,
571
+ {
572
+ title: "No commits",
573
+ description: "Commit history will appear when this branch receives changes."
574
+ }
575
+ )
576
+ }
577
+ )
578
+ ]
579
+ }
580
+ );
581
+ }
582
+
583
+ // src/components/DiffTable.tsx
584
+ import * as React4 from "react";
585
+ import { EmptyState as EmptyState2 } from "@echothink-ui/core";
586
+ import { Fragment, jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
587
+ var changeTypes = /* @__PURE__ */ new Set([
588
+ "added",
589
+ "modified",
590
+ "removed",
591
+ "renamed",
592
+ "unchanged",
593
+ "conflict"
594
+ ]);
595
+ function getNumeric(row, keys) {
596
+ for (const key of keys) {
597
+ const value = row[key];
598
+ if (typeof value === "number" && Number.isFinite(value)) return value;
599
+ if (typeof value === "string" && value.trim() !== "" && Number.isFinite(Number(value))) {
600
+ return Number(value);
601
+ }
602
+ }
603
+ return 0;
604
+ }
605
+ function getPath(row) {
606
+ const value = row.afterPath ?? row.path ?? row.file ?? row.name;
607
+ return typeof value === "string" && value.trim() ? value : "Unknown file";
608
+ }
609
+ function getPreviousPath(row) {
610
+ const value = row.beforePath ?? row.previousPath;
611
+ return typeof value === "string" && value.trim() ? value : void 0;
612
+ }
613
+ function normalizeChangeType(value) {
614
+ if (typeof value !== "string") return void 0;
615
+ const normalized = value.trim().toLowerCase().replace(/[_\s]+/g, "-");
616
+ if (normalized === "delete" || normalized === "deleted" || normalized === "deletion") {
617
+ return "removed";
618
+ }
619
+ if (normalized === "add" || normalized === "created" || normalized === "create") {
620
+ return "added";
621
+ }
622
+ if (normalized === "rename" || normalized === "moved") {
623
+ return "renamed";
624
+ }
625
+ return changeTypes.has(normalized) ? normalized : void 0;
626
+ }
627
+ function inferChangeType(row) {
628
+ const explicit = normalizeChangeType(row.changeType ?? row.status ?? row.kind ?? row.type);
629
+ if (explicit) return explicit;
630
+ if (getPreviousPath(row) && getPreviousPath(row) !== getPath(row)) return "renamed";
631
+ const added = getAdded(row);
632
+ const removed = getRemoved(row);
633
+ if (added > 0 && removed > 0) return "modified";
634
+ if (added > 0) return "added";
635
+ if (removed > 0) return "removed";
636
+ return "unchanged";
637
+ }
638
+ function getAdded(row) {
639
+ return getNumeric(row, ["added", "additions", "linesAdded", "insertions"]);
640
+ }
641
+ function getRemoved(row) {
642
+ return getNumeric(row, ["removed", "deletions", "deleted", "linesRemoved"]);
643
+ }
644
+ function changeLabel(type) {
645
+ if (type === "added") return "Added";
646
+ if (type === "removed") return "Removed";
647
+ if (type === "renamed") return "Renamed";
648
+ if (type === "unchanged") return "Unchanged";
649
+ if (type === "conflict") return "Conflict";
650
+ return "Modified";
651
+ }
652
+ function formatAdded(value) {
653
+ return value > 0 ? `+${value}` : "0";
654
+ }
655
+ function formatRemoved(value) {
656
+ return value > 0 ? `-${value}` : "0";
657
+ }
658
+ function formatNet(value) {
659
+ if (value > 0) return `+${value}`;
660
+ if (value < 0) return String(value);
661
+ return "0";
662
+ }
663
+ function defaultSummary(row, type) {
664
+ const hunks = getNumeric(row, ["hunks", "sections"]);
665
+ if (typeof row.summary !== "undefined") return row.summary;
666
+ if (type === "renamed") return "Path changed";
667
+ if (type === "unchanged") return "No line changes";
668
+ if (type === "conflict") return "Review required";
669
+ if (hunks > 0) return `${hunks} ${hunks === 1 ? "hunk" : "hunks"}`;
670
+ return "Line changes";
671
+ }
672
+ function DiffTable({
673
+ rows,
674
+ mode = "side-by-side",
675
+ title = "Diff summary",
676
+ subtitle,
677
+ showSummary = true,
678
+ emptyState,
679
+ className,
680
+ "aria-label": ariaLabel,
681
+ ...props
682
+ }) {
683
+ const totals = React4.useMemo(
684
+ () => rows.reduce(
685
+ (acc, row) => {
686
+ const type = inferChangeType(row);
687
+ acc.added += getAdded(row);
688
+ acc.removed += getRemoved(row);
689
+ if (type !== "unchanged") acc.changed += 1;
690
+ return acc;
691
+ },
692
+ { added: 0, removed: 0, changed: 0 }
693
+ ),
694
+ [rows]
695
+ );
696
+ const effectiveSubtitle = subtitle ?? `${totals.changed} of ${rows.length} files changed, ${totals.added} additions, ${totals.removed} deletions`;
697
+ const label = ariaLabel ?? (typeof title === "string" ? title : "Diff summary");
698
+ return /* @__PURE__ */ jsxs5(
699
+ "section",
700
+ {
701
+ ...props,
702
+ className: [
703
+ "eth-dev-diff-table",
704
+ `eth-dev-diff-table--${mode}`,
705
+ rows.length ? void 0 : "eth-dev-diff-table--empty",
706
+ className
707
+ ].filter(Boolean).join(" "),
708
+ "data-eth-component": "DiffTable",
709
+ "aria-label": label,
710
+ children: [
711
+ /* @__PURE__ */ jsxs5("header", { className: "eth-dev-diff-table__header", children: [
712
+ /* @__PURE__ */ jsxs5("div", { className: "eth-dev-diff-table__heading", children: [
713
+ /* @__PURE__ */ jsx6("h3", { children: title }),
714
+ effectiveSubtitle ? /* @__PURE__ */ jsx6("p", { children: effectiveSubtitle }) : null
715
+ ] }),
716
+ showSummary ? /* @__PURE__ */ jsxs5("dl", { className: "eth-dev-diff-table__summary", "aria-label": "Diff totals", children: [
717
+ /* @__PURE__ */ jsxs5("div", { children: [
718
+ /* @__PURE__ */ jsx6("dt", { children: "Files" }),
719
+ /* @__PURE__ */ jsx6("dd", { children: totals.changed })
720
+ ] }),
721
+ /* @__PURE__ */ jsxs5("div", { children: [
722
+ /* @__PURE__ */ jsx6("dt", { children: "Added" }),
723
+ /* @__PURE__ */ jsx6("dd", { className: "eth-dev-diff-table__summary-value--added", children: formatAdded(totals.added) })
724
+ ] }),
725
+ /* @__PURE__ */ jsxs5("div", { children: [
726
+ /* @__PURE__ */ jsx6("dt", { children: "Removed" }),
727
+ /* @__PURE__ */ jsx6("dd", { className: "eth-dev-diff-table__summary-value--removed", children: formatRemoved(totals.removed) })
728
+ ] })
729
+ ] }) : null
730
+ ] }),
731
+ rows.length ? /* @__PURE__ */ jsx6("div", { className: "eth-dev-diff-table__viewport", children: /* @__PURE__ */ jsxs5("table", { className: "eth-dev-diff-table__table", children: [
732
+ /* @__PURE__ */ jsx6("caption", { className: "eth-dev-diff-table__caption", children: label }),
733
+ /* @__PURE__ */ jsx6("thead", { children: /* @__PURE__ */ jsxs5("tr", { children: [
734
+ /* @__PURE__ */ jsx6("th", { scope: "col", children: "File" }),
735
+ /* @__PURE__ */ jsx6("th", { scope: "col", children: "State" }),
736
+ mode === "inline" ? /* @__PURE__ */ jsx6("th", { scope: "col", children: "Changes" }) : /* @__PURE__ */ jsxs5(Fragment, { children: [
737
+ /* @__PURE__ */ jsx6("th", { scope: "col", children: "Added" }),
738
+ /* @__PURE__ */ jsx6("th", { scope: "col", children: "Removed" }),
739
+ /* @__PURE__ */ jsx6("th", { scope: "col", children: "Net" })
740
+ ] }),
741
+ /* @__PURE__ */ jsx6("th", { scope: "col", children: "Summary" })
742
+ ] }) }),
743
+ /* @__PURE__ */ jsx6("tbody", { children: rows.map((row, index) => {
744
+ const added = getAdded(row);
745
+ const removed = getRemoved(row);
746
+ const type = inferChangeType(row);
747
+ const path = getPath(row);
748
+ const previousPath = getPreviousPath(row);
749
+ const key = row.id ?? `${path}-${index}`;
750
+ return /* @__PURE__ */ jsxs5("tr", { className: `eth-dev-diff-table__row--${type}`, children: [
751
+ /* @__PURE__ */ jsx6("th", { scope: "row", children: /* @__PURE__ */ jsxs5("span", { className: "eth-dev-diff-table__file", children: [
752
+ /* @__PURE__ */ jsx6("code", { children: path }),
753
+ previousPath && previousPath !== path ? /* @__PURE__ */ jsxs5("span", { children: [
754
+ "was ",
755
+ previousPath
756
+ ] }) : null
757
+ ] }) }),
758
+ /* @__PURE__ */ jsx6("td", { children: /* @__PURE__ */ jsx6(
759
+ "span",
760
+ {
761
+ className: `eth-dev-diff-table__state eth-dev-diff-table__state--${type}`,
762
+ children: changeLabel(type)
763
+ }
764
+ ) }),
765
+ mode === "inline" ? /* @__PURE__ */ jsx6("td", { children: /* @__PURE__ */ jsxs5("span", { className: "eth-dev-diff-table__inline-delta", children: [
766
+ /* @__PURE__ */ jsx6(
767
+ "span",
768
+ {
769
+ className: "eth-dev-diff-table__count eth-dev-diff-table__count--added",
770
+ "aria-label": `${added} added lines`,
771
+ children: formatAdded(added)
772
+ }
773
+ ),
774
+ /* @__PURE__ */ jsx6(
775
+ "span",
776
+ {
777
+ className: "eth-dev-diff-table__count eth-dev-diff-table__count--removed",
778
+ "aria-label": `${removed} removed lines`,
779
+ children: formatRemoved(removed)
780
+ }
781
+ )
782
+ ] }) }) : /* @__PURE__ */ jsxs5(Fragment, { children: [
783
+ /* @__PURE__ */ jsx6("td", { children: /* @__PURE__ */ jsx6(
784
+ "span",
785
+ {
786
+ className: "eth-dev-diff-table__count eth-dev-diff-table__count--added",
787
+ "aria-label": `${added} added lines`,
788
+ children: formatAdded(added)
789
+ }
790
+ ) }),
791
+ /* @__PURE__ */ jsx6("td", { children: /* @__PURE__ */ jsx6(
792
+ "span",
793
+ {
794
+ className: "eth-dev-diff-table__count eth-dev-diff-table__count--removed",
795
+ "aria-label": `${removed} removed lines`,
796
+ children: formatRemoved(removed)
797
+ }
798
+ ) }),
799
+ /* @__PURE__ */ jsx6("td", { children: /* @__PURE__ */ jsx6("span", { className: "eth-dev-diff-table__count", children: formatNet(added - removed) }) })
800
+ ] }),
801
+ /* @__PURE__ */ jsx6("td", { children: /* @__PURE__ */ jsx6("span", { className: "eth-dev-diff-table__row-summary", children: defaultSummary(row, type) }) })
802
+ ] }, key);
803
+ }) })
804
+ ] }) }) : emptyState ?? /* @__PURE__ */ jsx6(
805
+ EmptyState2,
806
+ {
807
+ title: "No diff rows",
808
+ description: "No files have line-level changes in this comparison."
809
+ }
810
+ )
811
+ ]
812
+ }
813
+ );
814
+ }
815
+
816
+ // src/components/DiffViewer.tsx
817
+ import * as React5 from "react";
818
+ import { Button as Button2 } from "@echothink-ui/core";
819
+ import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
820
+ function DiffViewer({
821
+ before,
822
+ after,
823
+ mode = "inline",
824
+ language,
825
+ className,
826
+ ...props
827
+ }) {
828
+ const [viewMode, setViewMode] = React5.useState(mode);
829
+ const diff = React5.useMemo(() => buildLineDiff(before, after), [before, after]);
830
+ const counts = React5.useMemo(
831
+ () => ({
832
+ additions: diff.filter((line) => line.type === "add").length,
833
+ deletions: diff.filter((line) => line.type === "remove").length
834
+ }),
835
+ [diff]
836
+ );
837
+ const languageLabel = language?.trim() || "diff";
838
+ React5.useEffect(() => setViewMode(mode), [mode]);
839
+ return /* @__PURE__ */ jsxs6(
840
+ "section",
841
+ {
842
+ ...props,
843
+ className: `eth-dev-diff-viewer eth-dev-diff-viewer--${viewMode} ${className ?? ""}`.trim(),
844
+ "aria-label": props["aria-label"] ?? `${languageLabel} diff viewer`,
845
+ "data-eth-component": "DiffViewer",
846
+ "data-language": languageLabel,
847
+ children: [
848
+ /* @__PURE__ */ jsxs6("header", { className: "eth-dev-diff-viewer__header", children: [
849
+ /* @__PURE__ */ jsxs6("div", { className: "eth-dev-diff-viewer__heading", children: [
850
+ /* @__PURE__ */ jsx7("span", { className: "eth-dev-diff-viewer__language", children: languageLabel }),
851
+ /* @__PURE__ */ jsx7("span", { className: "eth-dev-diff-viewer__mode", children: viewMode === "inline" ? "Inline" : "Side by side" })
852
+ ] }),
853
+ /* @__PURE__ */ jsxs6("div", { className: "eth-dev-diff-viewer__summary", "aria-label": "Diff summary", children: [
854
+ /* @__PURE__ */ jsxs6("span", { className: "eth-dev-diff-viewer__stat eth-dev-diff-viewer__stat--add", children: [
855
+ /* @__PURE__ */ jsx7("strong", { children: counts.additions }),
856
+ " additions"
857
+ ] }),
858
+ /* @__PURE__ */ jsxs6("span", { className: "eth-dev-diff-viewer__stat eth-dev-diff-viewer__stat--remove", children: [
859
+ /* @__PURE__ */ jsx7("strong", { children: counts.deletions }),
860
+ " deletions"
861
+ ] })
862
+ ] }),
863
+ /* @__PURE__ */ jsxs6("div", { className: "eth-dev-diff-viewer__view-switch", role: "group", "aria-label": "Diff view mode", children: [
864
+ /* @__PURE__ */ jsx7(
865
+ Button2,
866
+ {
867
+ intent: viewMode === "inline" ? "primary" : "secondary",
868
+ density: "compact",
869
+ "aria-pressed": viewMode === "inline",
870
+ onClick: () => setViewMode("inline"),
871
+ children: "Inline"
872
+ }
873
+ ),
874
+ /* @__PURE__ */ jsx7(
875
+ Button2,
876
+ {
877
+ intent: viewMode === "side-by-side" ? "primary" : "secondary",
878
+ density: "compact",
879
+ "aria-pressed": viewMode === "side-by-side",
880
+ onClick: () => setViewMode("side-by-side"),
881
+ children: "Side by side"
882
+ }
883
+ )
884
+ ] })
885
+ ] }),
886
+ viewMode === "inline" ? /* @__PURE__ */ jsx7(
887
+ "pre",
888
+ {
889
+ className: `eth-dev-diff-viewer__inline language-${toLanguageClass(languageLabel)}`,
890
+ "aria-label": "Inline diff",
891
+ children: /* @__PURE__ */ jsx7("code", { className: "eth-dev-diff-viewer__code", children: diff.map((line, index) => /* @__PURE__ */ jsx7(DiffLineView, { line }, index)) })
892
+ }
893
+ ) : /* @__PURE__ */ jsxs6("div", { className: "eth-dev-diff-viewer__split", role: "table", "aria-label": "Side-by-side diff", children: [
894
+ /* @__PURE__ */ jsx7("div", { role: "rowgroup", children: /* @__PURE__ */ jsxs6("div", { className: "eth-dev-diff-viewer__split-header", role: "row", children: [
895
+ /* @__PURE__ */ jsx7("div", { role: "columnheader", children: "Before" }),
896
+ /* @__PURE__ */ jsx7("div", { role: "columnheader", children: "After" })
897
+ ] }) }),
898
+ /* @__PURE__ */ jsx7("div", { className: "eth-dev-diff-viewer__split-body", role: "rowgroup", children: diff.map((line, index) => /* @__PURE__ */ jsxs6(
899
+ "div",
900
+ {
901
+ className: `eth-dev-diff-viewer__split-row eth-dev-diff-viewer__split-row--${line.type}`,
902
+ role: "row",
903
+ children: [
904
+ /* @__PURE__ */ jsx7(DiffCell, { side: "before", line }),
905
+ /* @__PURE__ */ jsx7(DiffCell, { side: "after", line })
906
+ ]
907
+ },
908
+ index
909
+ )) })
910
+ ] })
911
+ ]
912
+ }
913
+ );
914
+ }
915
+ function DiffLineView({ line }) {
916
+ return /* @__PURE__ */ jsxs6("span", { className: `eth-dev-diff-viewer__line eth-dev-diff-viewer__line--${line.type}`, children: [
917
+ /* @__PURE__ */ jsx7("span", { className: "eth-dev-diff-viewer__gutter", children: line.beforeLine ?? line.afterLine ?? "" }),
918
+ /* @__PURE__ */ jsx7("span", { className: "eth-dev-diff-viewer__marker", children: lineMarker(line.type) }),
919
+ /* @__PURE__ */ jsx7("span", { className: "eth-dev-diff-viewer__content", children: line.text || " " })
920
+ ] });
921
+ }
922
+ function DiffCell({ side, line }) {
923
+ const empty = side === "before" && line.type === "add" || side === "after" && line.type === "remove";
924
+ const lineNumber = side === "before" ? line.beforeLine : line.afterLine;
925
+ return /* @__PURE__ */ jsx7(
926
+ "pre",
927
+ {
928
+ className: `eth-dev-diff-viewer__cell eth-dev-diff-viewer__cell--${empty ? "empty" : line.type}`,
929
+ role: "cell",
930
+ children: /* @__PURE__ */ jsxs6("code", { className: "eth-dev-diff-viewer__cell-code", children: [
931
+ /* @__PURE__ */ jsx7("span", { className: "eth-dev-diff-viewer__gutter", children: empty ? "" : lineNumber ?? "" }),
932
+ /* @__PURE__ */ jsx7("span", { className: "eth-dev-diff-viewer__marker", children: empty ? " " : lineMarker(line.type) }),
933
+ /* @__PURE__ */ jsx7("span", { className: "eth-dev-diff-viewer__content", children: empty ? " " : line.text || " " })
934
+ ] })
935
+ }
936
+ );
937
+ }
938
+ function lineMarker(type) {
939
+ if (type === "add") return "+";
940
+ if (type === "remove") return "-";
941
+ return " ";
942
+ }
943
+ function toLanguageClass(language) {
944
+ return language.toLowerCase().replace(/[^a-z0-9_-]/g, "-");
945
+ }
946
+
947
+ // src/components/JSONViewer.tsx
948
+ import * as React6 from "react";
949
+ import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
950
+ function JSONViewer({
951
+ value,
952
+ defaultExpandedDepth = 2,
953
+ className,
954
+ ...props
955
+ }) {
956
+ const [view, setView] = React6.useState("tree");
957
+ const treeTabRef = React6.useRef(null);
958
+ const rawTabRef = React6.useRef(null);
959
+ const treeTabId = React6.useId();
960
+ const rawTabId = React6.useId();
961
+ const treePanelId = React6.useId();
962
+ const rawPanelId = React6.useId();
963
+ const selectView = (nextView, shouldFocus = false) => {
964
+ setView(nextView);
965
+ if (shouldFocus) {
966
+ window.requestAnimationFrame(() => {
967
+ (nextView === "tree" ? treeTabRef : rawTabRef).current?.focus();
968
+ });
969
+ }
970
+ };
971
+ const onTabKeyDown = (event) => {
972
+ if (event.key === "ArrowLeft" || event.key === "Home") {
973
+ event.preventDefault();
974
+ selectView("tree", true);
975
+ } else if (event.key === "ArrowRight" || event.key === "End") {
976
+ event.preventDefault();
977
+ selectView("raw", true);
978
+ }
979
+ };
980
+ return /* @__PURE__ */ jsxs7(
981
+ "section",
982
+ {
983
+ ...props,
984
+ className: `eth-dev-json-viewer ${className ?? ""}`,
985
+ "data-eth-component": "JSONViewer",
986
+ children: [
987
+ /* @__PURE__ */ jsxs7("header", { className: "eth-dev-json-viewer__tabs", role: "tablist", "aria-label": "JSON view mode", children: [
988
+ /* @__PURE__ */ jsx8(
989
+ "button",
990
+ {
991
+ "aria-controls": treePanelId,
992
+ "aria-selected": view === "tree",
993
+ className: "eth-dev-json-viewer__tab",
994
+ id: treeTabId,
995
+ ref: treeTabRef,
996
+ role: "tab",
997
+ tabIndex: view === "tree" ? 0 : -1,
998
+ type: "button",
999
+ onClick: () => selectView("tree"),
1000
+ onKeyDown: onTabKeyDown,
1001
+ children: "Tree"
1002
+ }
1003
+ ),
1004
+ /* @__PURE__ */ jsx8(
1005
+ "button",
1006
+ {
1007
+ "aria-controls": rawPanelId,
1008
+ "aria-selected": view === "raw",
1009
+ className: "eth-dev-json-viewer__tab",
1010
+ id: rawTabId,
1011
+ ref: rawTabRef,
1012
+ role: "tab",
1013
+ tabIndex: view === "raw" ? 0 : -1,
1014
+ type: "button",
1015
+ onClick: () => selectView("raw"),
1016
+ onKeyDown: onTabKeyDown,
1017
+ children: "Raw"
1018
+ }
1019
+ )
1020
+ ] }),
1021
+ /* @__PURE__ */ jsx8(
1022
+ "div",
1023
+ {
1024
+ "aria-labelledby": treeTabId,
1025
+ className: "eth-dev-json-viewer__tree",
1026
+ hidden: view !== "tree",
1027
+ id: treePanelId,
1028
+ role: "tabpanel",
1029
+ tabIndex: 0,
1030
+ children: /* @__PURE__ */ jsx8("div", { className: "eth-dev-json-viewer__tree-body", children: /* @__PURE__ */ jsx8(JSONNode, { name: "root", value, depth: 0, expandedDepth: defaultExpandedDepth }) })
1031
+ }
1032
+ ),
1033
+ /* @__PURE__ */ jsx8(
1034
+ "pre",
1035
+ {
1036
+ "aria-labelledby": rawTabId,
1037
+ className: "eth-dev-json-viewer__raw",
1038
+ hidden: view !== "raw",
1039
+ id: rawPanelId,
1040
+ role: "tabpanel",
1041
+ tabIndex: 0,
1042
+ children: /* @__PURE__ */ jsx8("code", { children: formatRawValue(value) })
1043
+ }
1044
+ )
1045
+ ]
1046
+ }
1047
+ );
1048
+ }
1049
+ function JSONNode({
1050
+ name,
1051
+ value,
1052
+ depth,
1053
+ expandedDepth
1054
+ }) {
1055
+ if (value === null || typeof value !== "object") {
1056
+ const type = leafType(value);
1057
+ return /* @__PURE__ */ jsxs7("div", { className: "eth-dev-json-viewer__leaf", children: [
1058
+ /* @__PURE__ */ jsx8("span", { className: "eth-dev-json-viewer__key", children: name }),
1059
+ /* @__PURE__ */ jsx8("span", { className: "eth-dev-json-viewer__punctuation", children: ":" }),
1060
+ /* @__PURE__ */ jsx8("span", { className: `eth-dev-json-viewer__value eth-dev-json-viewer__value--${type}`, children: formatLeafValue(value) })
1061
+ ] });
1062
+ }
1063
+ const entries = Array.isArray(value) ? value.map((item, index) => [String(index), item]) : Object.entries(value);
1064
+ const isArray = Array.isArray(value);
1065
+ const collectionMeta = isArray ? `[${entries.length}]` : `{${entries.length}}`;
1066
+ if (entries.length === 0) {
1067
+ return /* @__PURE__ */ jsxs7("div", { className: "eth-dev-json-viewer__leaf eth-dev-json-viewer__leaf--empty", children: [
1068
+ /* @__PURE__ */ jsx8("span", { className: "eth-dev-json-viewer__key", children: name }),
1069
+ /* @__PURE__ */ jsx8("span", { className: "eth-dev-json-viewer__node-meta", children: isArray ? "[]" : "{}" })
1070
+ ] });
1071
+ }
1072
+ return /* @__PURE__ */ jsxs7("details", { open: depth < expandedDepth, className: "eth-dev-json-viewer__branch", children: [
1073
+ /* @__PURE__ */ jsxs7("summary", { children: [
1074
+ /* @__PURE__ */ jsx8("span", { className: "eth-dev-json-viewer__disclosure", "aria-hidden": "true" }),
1075
+ /* @__PURE__ */ jsx8("span", { className: "eth-dev-json-viewer__summary-label", children: name }),
1076
+ /* @__PURE__ */ jsx8("span", { className: "eth-dev-json-viewer__node-meta", children: collectionMeta })
1077
+ ] }),
1078
+ /* @__PURE__ */ jsx8("div", { className: "eth-dev-json-viewer__children", children: entries.map(([key, child]) => /* @__PURE__ */ jsx8(
1079
+ JSONNode,
1080
+ {
1081
+ name: key,
1082
+ value: child,
1083
+ depth: depth + 1,
1084
+ expandedDepth
1085
+ },
1086
+ key
1087
+ )) })
1088
+ ] });
1089
+ }
1090
+ function leafType(value) {
1091
+ if (value === null) return "null";
1092
+ return typeof value;
1093
+ }
1094
+ function formatRawValue(value) {
1095
+ try {
1096
+ const formatted = JSON.stringify(value, null, 2);
1097
+ if (typeof formatted === "string") return formatted;
1098
+ } catch {
1099
+ }
1100
+ return formatLeafValue(value);
1101
+ }
1102
+ function formatLeafValue(value) {
1103
+ if (typeof value === "string") return JSON.stringify(value);
1104
+ if (typeof value === "undefined") return "undefined";
1105
+ if (typeof value === "symbol") return value.toString();
1106
+ if (typeof value === "function") return "[Function]";
1107
+ if (value === null) return "null";
1108
+ try {
1109
+ return JSON.stringify(value);
1110
+ } catch {
1111
+ return String(value);
1112
+ }
1113
+ }
1114
+
1115
+ // src/components/EventPayloadViewer.tsx
1116
+ import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
1117
+ function EventPayloadViewer({
1118
+ eventId,
1119
+ payload,
1120
+ className,
1121
+ "aria-label": ariaLabel,
1122
+ ...props
1123
+ }) {
1124
+ const summary = describePayload(payload);
1125
+ return /* @__PURE__ */ jsxs8(
1126
+ "section",
1127
+ {
1128
+ ...props,
1129
+ className: `eth-dev-event-payload-viewer ${className ?? ""}`,
1130
+ "aria-label": ariaLabel ?? (eventId ? `Event payload ${eventId}` : "Event payload"),
1131
+ "data-eth-component": "EventPayloadViewer",
1132
+ children: [
1133
+ /* @__PURE__ */ jsxs8("header", { className: "eth-dev-event-payload-viewer__header", children: [
1134
+ /* @__PURE__ */ jsxs8("div", { className: "eth-dev-event-payload-viewer__heading", children: [
1135
+ /* @__PURE__ */ jsx9("span", { children: "Event payload" }),
1136
+ /* @__PURE__ */ jsx9("h3", { children: eventId ? /* @__PURE__ */ jsx9("code", { children: eventId }) : "Payload" })
1137
+ ] }),
1138
+ /* @__PURE__ */ jsxs8("dl", { className: "eth-dev-event-payload-viewer__summary", "aria-label": "Payload summary", children: [
1139
+ /* @__PURE__ */ jsxs8("div", { children: [
1140
+ /* @__PURE__ */ jsx9("dt", { children: "Root" }),
1141
+ /* @__PURE__ */ jsx9("dd", { children: summary.root })
1142
+ ] }),
1143
+ /* @__PURE__ */ jsxs8("div", { children: [
1144
+ /* @__PURE__ */ jsx9("dt", { children: summary.countLabel }),
1145
+ /* @__PURE__ */ jsx9("dd", { children: summary.countValue })
1146
+ ] }),
1147
+ /* @__PURE__ */ jsxs8("div", { children: [
1148
+ /* @__PURE__ */ jsx9("dt", { children: "Size" }),
1149
+ /* @__PURE__ */ jsx9("dd", { children: summary.size })
1150
+ ] })
1151
+ ] })
1152
+ ] }),
1153
+ /* @__PURE__ */ jsx9(JSONViewer, { className: "eth-dev-event-payload-viewer__json", value: payload, defaultExpandedDepth: 3 })
1154
+ ]
1155
+ }
1156
+ );
1157
+ }
1158
+ function describePayload(value) {
1159
+ const text = serializePayload(value);
1160
+ if (Array.isArray(value)) {
1161
+ return {
1162
+ root: "array",
1163
+ countLabel: "Items",
1164
+ countValue: String(value.length),
1165
+ size: formatPayloadSize(text.length)
1166
+ };
1167
+ }
1168
+ if (value !== null && typeof value === "object") {
1169
+ return {
1170
+ root: "object",
1171
+ countLabel: "Fields",
1172
+ countValue: String(Object.keys(value).length),
1173
+ size: formatPayloadSize(text.length)
1174
+ };
1175
+ }
1176
+ return {
1177
+ root: value === null ? "null" : typeof value,
1178
+ countLabel: "Shape",
1179
+ countValue: "scalar",
1180
+ size: formatPayloadSize(text.length)
1181
+ };
1182
+ }
1183
+ function serializePayload(value) {
1184
+ if (typeof value === "string") return value;
1185
+ if (typeof value === "undefined") return "";
1186
+ try {
1187
+ return JSON.stringify(value, null, 2) ?? "";
1188
+ } catch {
1189
+ return String(value);
1190
+ }
1191
+ }
1192
+ function formatPayloadSize(size) {
1193
+ if (size < 1e3) return `${size} B`;
1194
+ if (size < 1e3 * 1e3) return `${(size / 1e3).toFixed(1)} KB`;
1195
+ return `${(size / (1e3 * 1e3)).toFixed(1)} MB`;
1196
+ }
1197
+
1198
+ // src/components/GitRepositoryPanel.tsx
1199
+ import { Branch, Commit, GitRepo, Launch, PullRequest, Time } from "@carbon/icons-react";
1200
+ import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
1201
+ function GitRepositoryPanel({ repo, className, ...props }) {
1202
+ const summaryItems = [
1203
+ {
1204
+ label: "Default branch",
1205
+ value: repo.defaultBranch,
1206
+ icon: /* @__PURE__ */ jsx10(Branch, { size: 16 }),
1207
+ valueClassName: "eth-dev-git-repository-panel__metric-value--code"
1208
+ },
1209
+ {
1210
+ label: "Open PRs",
1211
+ value: String(repo.openPrCount ?? 0),
1212
+ icon: /* @__PURE__ */ jsx10(PullRequest, { size: 16 })
1213
+ },
1214
+ {
1215
+ label: "Commits",
1216
+ value: String(repo.commitCount ?? 0),
1217
+ icon: /* @__PURE__ */ jsx10(Commit, { size: 16 })
1218
+ },
1219
+ {
1220
+ label: "Last commit",
1221
+ value: repo.lastCommitAt ?? "No activity",
1222
+ icon: /* @__PURE__ */ jsx10(Time, { size: 16 }),
1223
+ valueClassName: "eth-dev-git-repository-panel__metric-value--code"
1224
+ }
1225
+ ];
1226
+ return /* @__PURE__ */ jsxs9(
1227
+ "section",
1228
+ {
1229
+ ...props,
1230
+ "aria-label": props["aria-label"] ?? `${repo.name} repository summary`,
1231
+ className: `eth-dev-git-repository-panel ${className ?? ""}`,
1232
+ "data-eth-component": "GitRepositoryPanel",
1233
+ children: [
1234
+ /* @__PURE__ */ jsxs9("header", { className: "eth-dev-git-repository-panel__header", children: [
1235
+ /* @__PURE__ */ jsxs9("div", { className: "eth-dev-git-repository-panel__identity", children: [
1236
+ /* @__PURE__ */ jsx10("span", { className: "eth-dev-git-repository-panel__icon", "aria-hidden": "true", children: /* @__PURE__ */ jsx10(GitRepo, { size: 20 }) }),
1237
+ /* @__PURE__ */ jsxs9("div", { className: "eth-dev-git-repository-panel__heading", children: [
1238
+ /* @__PURE__ */ jsx10("p", { children: "Repository" }),
1239
+ /* @__PURE__ */ jsx10("h3", { children: repo.name }),
1240
+ /* @__PURE__ */ jsx10("a", { className: "eth-dev-git-repository-panel__url", href: repo.url, title: repo.url, children: repo.url })
1241
+ ] })
1242
+ ] }),
1243
+ /* @__PURE__ */ jsxs9(
1244
+ "a",
1245
+ {
1246
+ className: "eth-dev-git-repository-panel__action",
1247
+ href: repo.url,
1248
+ "aria-label": `Open ${repo.name} repository`,
1249
+ children: [
1250
+ /* @__PURE__ */ jsx10("span", { children: "Open repository" }),
1251
+ /* @__PURE__ */ jsx10(Launch, { size: 16, "aria-hidden": "true" })
1252
+ ]
1253
+ }
1254
+ )
1255
+ ] }),
1256
+ /* @__PURE__ */ jsx10("dl", { className: "eth-dev-git-repository-panel__metrics", "aria-label": "Repository metrics", children: summaryItems.map((item) => /* @__PURE__ */ jsxs9("div", { className: "eth-dev-git-repository-panel__metric", children: [
1257
+ /* @__PURE__ */ jsxs9("dt", { children: [
1258
+ /* @__PURE__ */ jsx10("span", { "aria-hidden": "true", children: item.icon }),
1259
+ item.label
1260
+ ] }),
1261
+ /* @__PURE__ */ jsx10("dd", { className: item.valueClassName, children: item.value })
1262
+ ] }, item.label)) })
1263
+ ]
1264
+ }
1265
+ );
1266
+ }
1267
+
1268
+ // src/components/LogConsole.tsx
1269
+ import { Pause, Play } from "@carbon/icons-react";
1270
+ import { Badge, Button as Button3 } from "@echothink-ui/core";
1271
+ import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
1272
+ var levelAliases = {
1273
+ debug: "debug",
1274
+ error: "error",
1275
+ err: "error",
1276
+ fatal: "error",
1277
+ info: "info",
1278
+ notice: "info",
1279
+ stderr: "stderr",
1280
+ stdout: "stdout",
1281
+ trace: "debug",
1282
+ warn: "warn",
1283
+ warning: "warn"
1284
+ };
1285
+ function getEntryLevel(entry) {
1286
+ return (entry.level ?? entry.stream ?? "info").toString();
1287
+ }
1288
+ function getEntryTone(entry) {
1289
+ return levelAliases[getEntryLevel(entry).toLowerCase()] ?? "info";
1290
+ }
1291
+ function isWarning(entry) {
1292
+ return getEntryTone(entry) === "warn";
1293
+ }
1294
+ function isError(entry) {
1295
+ const tone = getEntryTone(entry);
1296
+ return tone === "error" || tone === "stderr";
1297
+ }
1298
+ function formatLevel(entry) {
1299
+ return getEntryLevel(entry).toUpperCase();
1300
+ }
1301
+ function formatMessage(entry) {
1302
+ return entry.message ?? entry.text ?? "";
1303
+ }
1304
+ function LogConsole({
1305
+ entries,
1306
+ onPause,
1307
+ streaming = false,
1308
+ filters,
1309
+ className,
1310
+ role = "region",
1311
+ "aria-label": ariaLabel = "Streaming log console",
1312
+ "aria-labelledby": ariaLabelledBy,
1313
+ ...props
1314
+ }) {
1315
+ const warningCount = entries.filter(isWarning).length;
1316
+ const errorCount = entries.filter(isError).length;
1317
+ const latestEntry = entries[entries.length - 1];
1318
+ const latestTimestamp = latestEntry?.timestamp ?? "No activity";
1319
+ const hasToolbar = Boolean(filters || onPause);
1320
+ return /* @__PURE__ */ jsxs10(
1321
+ "section",
1322
+ {
1323
+ ...props,
1324
+ role,
1325
+ "aria-label": ariaLabelledBy ? void 0 : ariaLabel,
1326
+ "aria-labelledby": ariaLabelledBy,
1327
+ className: `eth-dev-log-console ${streaming ? "eth-dev-log-console--streaming" : "eth-dev-log-console--paused"} ${className ?? ""}`,
1328
+ "data-eth-component": "LogConsole",
1329
+ children: [
1330
+ /* @__PURE__ */ jsxs10("header", { className: "eth-dev-log-console__header", children: [
1331
+ /* @__PURE__ */ jsxs10("div", { className: "eth-dev-log-console__heading", children: [
1332
+ /* @__PURE__ */ jsx11("p", { className: "eth-dev-log-console__eyebrow", children: "Developer console" }),
1333
+ /* @__PURE__ */ jsx11("h3", { children: "Streaming logs" }),
1334
+ /* @__PURE__ */ jsxs10("p", { children: [
1335
+ entries.length,
1336
+ " ",
1337
+ entries.length === 1 ? "entry" : "entries",
1338
+ " buffered from the active stream."
1339
+ ] })
1340
+ ] }),
1341
+ /* @__PURE__ */ jsxs10("div", { className: "eth-dev-log-console__status", "aria-live": "polite", children: [
1342
+ /* @__PURE__ */ jsx11(Badge, { severity: streaming ? "success" : "neutral", children: streaming ? "Streaming" : "Paused" }),
1343
+ /* @__PURE__ */ jsx11("span", { children: errorCount ? `${errorCount} errors` : "No errors" })
1344
+ ] })
1345
+ ] }),
1346
+ hasToolbar ? /* @__PURE__ */ jsxs10("div", { className: "eth-dev-log-console__toolbar", role: "toolbar", "aria-label": "Log controls", children: [
1347
+ filters ? /* @__PURE__ */ jsx11("div", { className: "eth-dev-log-console__filters", children: filters }) : null,
1348
+ onPause ? /* @__PURE__ */ jsx11(
1349
+ Button3,
1350
+ {
1351
+ intent: streaming ? "secondary" : "primary",
1352
+ density: "compact",
1353
+ icon: streaming ? /* @__PURE__ */ jsx11(Pause, { size: 16 }) : /* @__PURE__ */ jsx11(Play, { size: 16 }),
1354
+ onClick: onPause,
1355
+ children: streaming ? "Pause stream" : "Resume stream"
1356
+ }
1357
+ ) : null
1358
+ ] }) : null,
1359
+ /* @__PURE__ */ jsxs10("dl", { className: "eth-dev-log-console__summary", "aria-label": "Log summary", children: [
1360
+ /* @__PURE__ */ jsxs10("div", { children: [
1361
+ /* @__PURE__ */ jsx11("dt", { children: "Entries" }),
1362
+ /* @__PURE__ */ jsx11("dd", { children: entries.length })
1363
+ ] }),
1364
+ /* @__PURE__ */ jsxs10("div", { children: [
1365
+ /* @__PURE__ */ jsx11("dt", { children: "Warnings" }),
1366
+ /* @__PURE__ */ jsx11("dd", { children: warningCount })
1367
+ ] }),
1368
+ /* @__PURE__ */ jsxs10("div", { children: [
1369
+ /* @__PURE__ */ jsx11("dt", { children: "Errors" }),
1370
+ /* @__PURE__ */ jsx11("dd", { children: errorCount })
1371
+ ] }),
1372
+ /* @__PURE__ */ jsxs10("div", { children: [
1373
+ /* @__PURE__ */ jsx11("dt", { children: "Latest" }),
1374
+ /* @__PURE__ */ jsx11("dd", { children: latestTimestamp })
1375
+ ] })
1376
+ ] }),
1377
+ /* @__PURE__ */ jsx11("div", { className: "eth-dev-log-console__viewport", children: entries.length ? /* @__PURE__ */ jsx11(
1378
+ "div",
1379
+ {
1380
+ className: "eth-dev-log-console__entries",
1381
+ role: "log",
1382
+ "aria-live": streaming ? "polite" : "off",
1383
+ "aria-relevant": "additions text",
1384
+ "aria-atomic": "false",
1385
+ children: entries.map((entry) => {
1386
+ const tone = getEntryTone(entry);
1387
+ return /* @__PURE__ */ jsxs10(
1388
+ "div",
1389
+ {
1390
+ className: `eth-dev-log-console__entry eth-dev-log-console__entry--${tone}`,
1391
+ children: [
1392
+ /* @__PURE__ */ jsx11("time", { className: "eth-dev-log-console__timestamp", children: entry.timestamp ?? "--:--:--" }),
1393
+ /* @__PURE__ */ jsx11("span", { className: "eth-dev-log-console__level", children: formatLevel(entry) }),
1394
+ /* @__PURE__ */ jsx11("span", { className: "eth-dev-log-console__source", children: entry.source ?? entry.stream ?? "system" }),
1395
+ /* @__PURE__ */ jsx11("code", { className: "eth-dev-log-console__message", children: formatMessage(entry) })
1396
+ ]
1397
+ },
1398
+ entry.id
1399
+ );
1400
+ })
1401
+ }
1402
+ ) : /* @__PURE__ */ jsxs10("div", { className: "eth-dev-log-console__empty", role: "status", children: [
1403
+ /* @__PURE__ */ jsx11("h4", { children: "No log entries" }),
1404
+ /* @__PURE__ */ jsx11("p", { children: "Events will appear here when the stream receives output." })
1405
+ ] }) })
1406
+ ]
1407
+ }
1408
+ );
1409
+ }
1410
+
1411
+ // src/components/PullRequestPanel.tsx
1412
+ import * as React7 from "react";
1413
+ import { Checkmark as Checkmark2, PullRequest as PullRequest2 } from "@carbon/icons-react";
1414
+ import { Button as Button4, EmptyState as EmptyState3, Textarea as Textarea2 } from "@echothink-ui/core";
1415
+ import { DataTable as DataTable2 } from "@echothink-ui/data";
1416
+ import { jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
1417
+ function formatState(state) {
1418
+ const normalized = state.trim();
1419
+ if (!normalized) return "Unknown";
1420
+ return normalized.split(/[\s_-]+/).map((part) => `${part.charAt(0).toUpperCase()}${part.slice(1).toLowerCase()}`).join(" ");
1421
+ }
1422
+ function stateClassName(state) {
1423
+ const normalized = state.trim().toLowerCase();
1424
+ if (["open", "opened", "ready"].includes(normalized)) {
1425
+ return "eth-dev-pull-request-panel__state--open";
1426
+ }
1427
+ if (["merged", "approved"].includes(normalized)) {
1428
+ return "eth-dev-pull-request-panel__state--merged";
1429
+ }
1430
+ if (["closed", "rejected", "blocked"].includes(normalized)) {
1431
+ return "eth-dev-pull-request-panel__state--closed";
1432
+ }
1433
+ if (["draft", "pending"].includes(normalized)) {
1434
+ return "eth-dev-pull-request-panel__state--draft";
1435
+ }
1436
+ return "eth-dev-pull-request-panel__state--unknown";
1437
+ }
1438
+ function PullRequestPanel({
1439
+ pr,
1440
+ onApprove,
1441
+ onComment,
1442
+ className,
1443
+ ...props
1444
+ }) {
1445
+ const [comment, setComment] = React7.useState("");
1446
+ const titleId = React7.useId().replace(/:/g, "");
1447
+ const filesTitleId = React7.useId().replace(/:/g, "");
1448
+ const files = pr.files ?? [];
1449
+ const reviewers = pr.reviewers?.length ? pr.reviewers.join(", ") : "No reviewers";
1450
+ const additions = files.reduce((sum, file) => sum + file.additions, 0);
1451
+ const deletions = files.reduce((sum, file) => sum + file.deletions, 0);
1452
+ const formattedState = formatState(pr.state);
1453
+ const ariaLabel = props["aria-label"] ?? (props["aria-labelledby"] ? void 0 : `Pull request ${pr.title}`);
1454
+ const columns = [
1455
+ {
1456
+ key: "path",
1457
+ header: "File",
1458
+ render: (row) => /* @__PURE__ */ jsx12("code", { className: "eth-dev-pull-request-panel__file-path", title: row.path, children: row.path })
1459
+ },
1460
+ {
1461
+ key: "additions",
1462
+ header: "Additions",
1463
+ align: "end",
1464
+ width: "7rem",
1465
+ render: (row) => /* @__PURE__ */ jsxs11("span", { className: "eth-dev-pull-request-panel__count eth-dev-pull-request-panel__count--added", children: [
1466
+ "+",
1467
+ row.additions
1468
+ ] })
1469
+ },
1470
+ {
1471
+ key: "deletions",
1472
+ header: "Deletions",
1473
+ align: "end",
1474
+ width: "7rem",
1475
+ render: (row) => /* @__PURE__ */ jsxs11("span", { className: "eth-dev-pull-request-panel__count eth-dev-pull-request-panel__count--removed", children: [
1476
+ "-",
1477
+ row.deletions
1478
+ ] })
1479
+ }
1480
+ ];
1481
+ const submitComment = (event) => {
1482
+ event.preventDefault();
1483
+ const nextComment = comment.trim();
1484
+ if (!nextComment) return;
1485
+ onComment?.(nextComment);
1486
+ setComment("");
1487
+ };
1488
+ return /* @__PURE__ */ jsxs11(
1489
+ "section",
1490
+ {
1491
+ ...props,
1492
+ "aria-label": ariaLabel,
1493
+ className: `eth-dev-pull-request-panel ${className ?? ""}`,
1494
+ "data-eth-component": "PullRequestPanel",
1495
+ role: "region",
1496
+ children: [
1497
+ /* @__PURE__ */ jsxs11("header", { className: "eth-dev-pull-request-panel__header", children: [
1498
+ /* @__PURE__ */ jsxs11("div", { className: "eth-dev-pull-request-panel__identity", children: [
1499
+ /* @__PURE__ */ jsx12("span", { className: "eth-dev-pull-request-panel__icon", "aria-hidden": "true", children: /* @__PURE__ */ jsx12(PullRequest2, { size: 20 }) }),
1500
+ /* @__PURE__ */ jsxs11("div", { className: "eth-dev-pull-request-panel__heading", children: [
1501
+ /* @__PURE__ */ jsxs11("p", { children: [
1502
+ "Pull request ",
1503
+ /* @__PURE__ */ jsxs11("span", { children: [
1504
+ "#",
1505
+ pr.id
1506
+ ] })
1507
+ ] }),
1508
+ /* @__PURE__ */ jsx12("h3", { id: titleId, children: pr.title }),
1509
+ /* @__PURE__ */ jsxs11("div", { className: "eth-dev-pull-request-panel__meta", children: [
1510
+ /* @__PURE__ */ jsx12(
1511
+ "span",
1512
+ {
1513
+ className: `eth-dev-pull-request-panel__state ${stateClassName(pr.state)}`,
1514
+ children: formattedState
1515
+ }
1516
+ ),
1517
+ /* @__PURE__ */ jsxs11("span", { children: [
1518
+ "by ",
1519
+ pr.author
1520
+ ] })
1521
+ ] })
1522
+ ] })
1523
+ ] }),
1524
+ onApprove ? /* @__PURE__ */ jsx12(
1525
+ Button4,
1526
+ {
1527
+ "aria-label": "Approve pull request",
1528
+ className: "eth-dev-pull-request-panel__approve",
1529
+ density: "compact",
1530
+ icon: /* @__PURE__ */ jsx12(Checkmark2, { size: 16 }),
1531
+ intent: "success",
1532
+ onClick: onApprove,
1533
+ children: "Approve"
1534
+ }
1535
+ ) : null
1536
+ ] }),
1537
+ /* @__PURE__ */ jsxs11("div", { className: "eth-dev-pull-request-panel__body", children: [
1538
+ pr.body ? /* @__PURE__ */ jsx12("p", { className: "eth-dev-pull-request-panel__description", children: pr.body }) : null,
1539
+ /* @__PURE__ */ jsxs11("dl", { className: "eth-dev-pull-request-panel__metrics", "aria-label": "Pull request summary", children: [
1540
+ /* @__PURE__ */ jsxs11("div", { children: [
1541
+ /* @__PURE__ */ jsx12("dt", { children: "Reviewers" }),
1542
+ /* @__PURE__ */ jsx12("dd", { title: reviewers, children: reviewers })
1543
+ ] }),
1544
+ /* @__PURE__ */ jsxs11("div", { children: [
1545
+ /* @__PURE__ */ jsx12("dt", { children: "Commits" }),
1546
+ /* @__PURE__ */ jsx12("dd", { children: pr.commits ?? 0 })
1547
+ ] }),
1548
+ /* @__PURE__ */ jsxs11("div", { children: [
1549
+ /* @__PURE__ */ jsx12("dt", { children: "Files changed" }),
1550
+ /* @__PURE__ */ jsx12("dd", { children: files.length })
1551
+ ] }),
1552
+ /* @__PURE__ */ jsxs11("div", { children: [
1553
+ /* @__PURE__ */ jsx12("dt", { children: "Line delta" }),
1554
+ /* @__PURE__ */ jsxs11("dd", { children: [
1555
+ /* @__PURE__ */ jsxs11("span", { className: "eth-dev-pull-request-panel__count eth-dev-pull-request-panel__count--added", children: [
1556
+ "+",
1557
+ additions
1558
+ ] }),
1559
+ /* @__PURE__ */ jsx12("span", { "aria-hidden": "true", children: " / " }),
1560
+ /* @__PURE__ */ jsxs11("span", { className: "eth-dev-pull-request-panel__count eth-dev-pull-request-panel__count--removed", children: [
1561
+ "-",
1562
+ deletions
1563
+ ] })
1564
+ ] })
1565
+ ] })
1566
+ ] })
1567
+ ] }),
1568
+ /* @__PURE__ */ jsxs11("section", { className: "eth-dev-pull-request-panel__files", "aria-labelledby": filesTitleId, children: [
1569
+ /* @__PURE__ */ jsxs11("div", { className: "eth-dev-pull-request-panel__files-header", children: [
1570
+ /* @__PURE__ */ jsx12("h4", { id: filesTitleId, children: "Changed files" }),
1571
+ /* @__PURE__ */ jsxs11("span", { children: [
1572
+ files.length,
1573
+ " files"
1574
+ ] })
1575
+ ] }),
1576
+ files.length ? /* @__PURE__ */ jsx12(
1577
+ DataTable2,
1578
+ {
1579
+ "aria-label": "Changed files",
1580
+ className: "eth-dev-pull-request-panel__table",
1581
+ columns,
1582
+ density: "compact",
1583
+ rowKey: "path",
1584
+ rows: files
1585
+ }
1586
+ ) : /* @__PURE__ */ jsx12(
1587
+ EmptyState3,
1588
+ {
1589
+ title: "No file changes",
1590
+ description: "Changed files will appear when this pull request has a diff."
1591
+ }
1592
+ )
1593
+ ] }),
1594
+ onComment ? /* @__PURE__ */ jsxs11(
1595
+ "form",
1596
+ {
1597
+ "aria-label": "Review comment form",
1598
+ className: "eth-dev-pull-request-panel__comment",
1599
+ onSubmit: submitComment,
1600
+ children: [
1601
+ /* @__PURE__ */ jsx12(
1602
+ Textarea2,
1603
+ {
1604
+ helperText: "Add a blocking question, approval note, or evidence reference.",
1605
+ labelText: "Review comment",
1606
+ onChange: (event) => setComment(event.currentTarget.value),
1607
+ placeholder: "Reference tests, policy checks, or follow-up questions.",
1608
+ rows: 4,
1609
+ value: comment
1610
+ }
1611
+ ),
1612
+ /* @__PURE__ */ jsxs11("div", { className: "eth-dev-pull-request-panel__comment-footer", children: [
1613
+ /* @__PURE__ */ jsx12("span", { children: "Comment will be attached to this pull request." }),
1614
+ /* @__PURE__ */ jsx12(
1615
+ Button4,
1616
+ {
1617
+ "aria-label": "Submit review comment",
1618
+ density: "compact",
1619
+ disabled: !comment.trim(),
1620
+ intent: "secondary",
1621
+ type: "submit",
1622
+ children: "Comment"
1623
+ }
1624
+ )
1625
+ ] })
1626
+ ]
1627
+ }
1628
+ ) : null
1629
+ ]
1630
+ }
1631
+ );
1632
+ }
1633
+
1634
+ // src/components/RequestResponseViewer.tsx
1635
+ import * as React8 from "react";
1636
+ import { jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
1637
+ var tabs = [
1638
+ { id: "request-headers", label: "Request headers", emptyLabel: "No request headers." },
1639
+ { id: "request-body", label: "Request body", emptyLabel: "No request body." },
1640
+ { id: "response-headers", label: "Response headers", emptyLabel: "No response headers." },
1641
+ { id: "response-body", label: "Response body", emptyLabel: "No response body." }
1642
+ ];
1643
+ function RequestResponseViewer({
1644
+ request,
1645
+ response,
1646
+ className,
1647
+ "aria-label": ariaLabel,
1648
+ ...props
1649
+ }) {
1650
+ const [tab, setTab] = React8.useState("request-headers");
1651
+ const tabRefs = React8.useRef([]);
1652
+ const generatedId = React8.useId().replace(/:/g, "");
1653
+ const activeIndex = Math.max(
1654
+ tabs.findIndex((item) => item.id === tab),
1655
+ 0
1656
+ );
1657
+ const regionLabel = `${request.method} ${request.url} ${response.status}`;
1658
+ const durationLabel = typeof response.durationMs === "number" ? `${response.durationMs} ms` : "Duration unavailable";
1659
+ const selectTab = (nextIndex, shouldFocus = false) => {
1660
+ const nextTab = tabs[nextIndex] ?? tabs[0];
1661
+ setTab(nextTab.id);
1662
+ if (shouldFocus) {
1663
+ window.requestAnimationFrame(() => tabRefs.current[nextIndex]?.focus());
1664
+ }
1665
+ };
1666
+ const onTabKeyDown = (event) => {
1667
+ if (event.key === "ArrowLeft") {
1668
+ event.preventDefault();
1669
+ selectTab((activeIndex - 1 + tabs.length) % tabs.length, true);
1670
+ } else if (event.key === "ArrowRight") {
1671
+ event.preventDefault();
1672
+ selectTab((activeIndex + 1) % tabs.length, true);
1673
+ } else if (event.key === "Home") {
1674
+ event.preventDefault();
1675
+ selectTab(0, true);
1676
+ } else if (event.key === "End") {
1677
+ event.preventDefault();
1678
+ selectTab(tabs.length - 1, true);
1679
+ }
1680
+ };
1681
+ return /* @__PURE__ */ jsxs12(
1682
+ "section",
1683
+ {
1684
+ ...props,
1685
+ "aria-label": ariaLabel ?? regionLabel,
1686
+ className: `eth-dev-request-response-viewer ${className ?? ""}`.trim(),
1687
+ "data-eth-component": "RequestResponseViewer",
1688
+ children: [
1689
+ /* @__PURE__ */ jsxs12("header", { className: "eth-dev-request-response-viewer__header", children: [
1690
+ /* @__PURE__ */ jsxs12("div", { className: "eth-dev-request-response-viewer__request", children: [
1691
+ /* @__PURE__ */ jsx13(
1692
+ "span",
1693
+ {
1694
+ className: `eth-dev-request-response-viewer__method eth-dev-request-response-viewer__method--${tokenize(request.method)}`,
1695
+ children: request.method
1696
+ }
1697
+ ),
1698
+ /* @__PURE__ */ jsx13("code", { title: request.url, children: request.url })
1699
+ ] }),
1700
+ /* @__PURE__ */ jsxs12("dl", { className: "eth-dev-request-response-viewer__meta", "aria-label": "HTTP response summary", children: [
1701
+ /* @__PURE__ */ jsxs12("div", { children: [
1702
+ /* @__PURE__ */ jsx13("dt", { children: "Status" }),
1703
+ /* @__PURE__ */ jsx13(
1704
+ "dd",
1705
+ {
1706
+ className: `eth-dev-request-response-viewer__status eth-dev-request-response-viewer__status--${statusTone(response.status)}`,
1707
+ children: response.status
1708
+ }
1709
+ )
1710
+ ] }),
1711
+ /* @__PURE__ */ jsxs12("div", { children: [
1712
+ /* @__PURE__ */ jsx13("dt", { children: "Time" }),
1713
+ /* @__PURE__ */ jsx13("dd", { children: durationLabel })
1714
+ ] })
1715
+ ] })
1716
+ ] }),
1717
+ /* @__PURE__ */ jsx13(
1718
+ "div",
1719
+ {
1720
+ className: "eth-dev-request-response-viewer__tabs",
1721
+ role: "tablist",
1722
+ "aria-label": "HTTP message sections",
1723
+ children: tabs.map((item, index) => /* @__PURE__ */ jsx13(
1724
+ "button",
1725
+ {
1726
+ "aria-controls": `${generatedId}-${item.id}-panel`,
1727
+ "aria-selected": tab === item.id,
1728
+ className: "eth-dev-request-response-viewer__tab",
1729
+ id: `${generatedId}-${item.id}-tab`,
1730
+ ref: (node) => {
1731
+ tabRefs.current[index] = node;
1732
+ },
1733
+ role: "tab",
1734
+ tabIndex: tab === item.id ? 0 : -1,
1735
+ type: "button",
1736
+ onClick: () => selectTab(index),
1737
+ onKeyDown: onTabKeyDown,
1738
+ children: item.label
1739
+ },
1740
+ item.id
1741
+ ))
1742
+ }
1743
+ ),
1744
+ tabs.map((item) => {
1745
+ const content = contentFor(item.id, request, response);
1746
+ return /* @__PURE__ */ jsx13(
1747
+ "pre",
1748
+ {
1749
+ "aria-labelledby": `${generatedId}-${item.id}-tab`,
1750
+ className: `eth-dev-request-response-viewer__panel ${content ? "" : "eth-dev-request-response-viewer__panel--empty"}`.trim(),
1751
+ hidden: tab !== item.id,
1752
+ id: `${generatedId}-${item.id}-panel`,
1753
+ role: "tabpanel",
1754
+ tabIndex: tab === item.id ? 0 : -1,
1755
+ children: /* @__PURE__ */ jsx13("code", { children: content || item.emptyLabel })
1756
+ },
1757
+ item.id
1758
+ );
1759
+ })
1760
+ ]
1761
+ }
1762
+ );
1763
+ }
1764
+ function contentFor(tab, request, response) {
1765
+ switch (tab) {
1766
+ case "request-headers":
1767
+ return formatHeaders(request.headers);
1768
+ case "request-body":
1769
+ return formatUnknown(request.body);
1770
+ case "response-headers":
1771
+ return formatHeaders(response.headers);
1772
+ case "response-body":
1773
+ return formatUnknown(response.body);
1774
+ }
1775
+ }
1776
+ function formatHeaders(headers) {
1777
+ if (!headers || Object.keys(headers).length === 0) return "";
1778
+ return formatUnknown(headers);
1779
+ }
1780
+ function statusTone(status) {
1781
+ if (status >= 200 && status < 300) return "success";
1782
+ if (status >= 300 && status < 400) return "redirect";
1783
+ if (status >= 400 && status < 500) return "warning";
1784
+ if (status >= 500) return "error";
1785
+ return "neutral";
1786
+ }
1787
+ function tokenize(value) {
1788
+ return value.toLowerCase().replace(/[^a-z0-9-]/g, "");
1789
+ }
1790
+
1791
+ // src/components/SchemaViewer.tsx
1792
+ import * as React9 from "react";
1793
+ import { CheckmarkFilled, ErrorFilled, WarningFilled } from "@carbon/icons-react";
1794
+ import { jsx as jsx14, jsxs as jsxs13 } from "react/jsx-runtime";
1795
+ function SchemaViewer({
1796
+ schema,
1797
+ validationState,
1798
+ className,
1799
+ ...props
1800
+ }) {
1801
+ const stats = React9.useMemo(() => collectSchemaStats(schema), [schema]);
1802
+ const tone = normalizeValidationState(validationState);
1803
+ const status = getStatusConfig(tone, stats);
1804
+ return /* @__PURE__ */ jsxs13(
1805
+ "section",
1806
+ {
1807
+ ...props,
1808
+ className: `eth-dev-schema-viewer ${className ?? ""}`,
1809
+ "data-eth-component": "SchemaViewer",
1810
+ "data-validation-state": tone,
1811
+ children: [
1812
+ /* @__PURE__ */ jsxs13("header", { className: "eth-dev-schema-viewer__status", children: [
1813
+ /* @__PURE__ */ jsx14("span", { className: "eth-dev-schema-viewer__status-icon", "aria-hidden": "true", children: /* @__PURE__ */ jsx14(StatusIcon, { tone }) }),
1814
+ /* @__PURE__ */ jsxs13("div", { className: "eth-dev-schema-viewer__status-copy", children: [
1815
+ /* @__PURE__ */ jsx14("p", { className: "eth-dev-schema-viewer__status-title", children: status.title }),
1816
+ /* @__PURE__ */ jsx14("p", { className: "eth-dev-schema-viewer__status-description", children: status.description })
1817
+ ] }),
1818
+ /* @__PURE__ */ jsxs13("dl", { className: "eth-dev-schema-viewer__stats", "aria-label": "Schema summary", children: [
1819
+ /* @__PURE__ */ jsxs13("div", { children: [
1820
+ /* @__PURE__ */ jsx14("dt", { children: "Keys" }),
1821
+ /* @__PURE__ */ jsx14("dd", { children: stats.topLevelKeys })
1822
+ ] }),
1823
+ /* @__PURE__ */ jsxs13("div", { children: [
1824
+ /* @__PURE__ */ jsx14("dt", { children: "Properties" }),
1825
+ /* @__PURE__ */ jsx14("dd", { children: stats.properties })
1826
+ ] }),
1827
+ /* @__PURE__ */ jsxs13("div", { children: [
1828
+ /* @__PURE__ */ jsx14("dt", { children: "Required" }),
1829
+ /* @__PURE__ */ jsx14("dd", { children: stats.required })
1830
+ ] })
1831
+ ] })
1832
+ ] }),
1833
+ /* @__PURE__ */ jsx14(JSONViewer, { className: "eth-dev-schema-viewer__json", value: schema, defaultExpandedDepth: 2 })
1834
+ ]
1835
+ }
1836
+ );
1837
+ }
1838
+ function normalizeValidationState(validationState) {
1839
+ if (validationState === "valid" || validationState === "invalid" || validationState === "warning") {
1840
+ return validationState;
1841
+ }
1842
+ return "unknown";
1843
+ }
1844
+ function getStatusConfig(tone, stats) {
1845
+ const summary = `${stats.nodes} nodes inspected across ${stats.properties} properties`;
1846
+ switch (tone) {
1847
+ case "valid":
1848
+ return {
1849
+ title: "Schema valid",
1850
+ description: summary
1851
+ };
1852
+ case "invalid":
1853
+ return {
1854
+ title: "Schema invalid",
1855
+ description: "Review schema structure and validation rules before publishing."
1856
+ };
1857
+ case "warning":
1858
+ return {
1859
+ title: "Schema warning",
1860
+ description: "Schema is readable, but validation reported conditions that need review."
1861
+ };
1862
+ default:
1863
+ return {
1864
+ title: "Schema status unknown",
1865
+ description: summary
1866
+ };
1867
+ }
1868
+ }
1869
+ function collectSchemaStats(schema) {
1870
+ const stats = {
1871
+ nodes: 0,
1872
+ topLevelKeys: isPlainObject(schema) ? Object.keys(schema).length : Array.isArray(schema) ? schema.length : 1,
1873
+ properties: 0,
1874
+ required: 0
1875
+ };
1876
+ const seen = /* @__PURE__ */ new WeakSet();
1877
+ const visit = (node, key) => {
1878
+ stats.nodes += 1;
1879
+ if (Array.isArray(node)) {
1880
+ if (seen.has(node)) {
1881
+ return;
1882
+ }
1883
+ seen.add(node);
1884
+ if (key === "required" && node.every((item) => typeof item === "string")) {
1885
+ stats.required += node.length;
1886
+ }
1887
+ node.forEach((item) => visit(item));
1888
+ return;
1889
+ }
1890
+ if (!isPlainObject(node) || seen.has(node)) {
1891
+ return;
1892
+ }
1893
+ seen.add(node);
1894
+ if (isPlainObject(node.properties)) {
1895
+ stats.properties += Object.keys(node.properties).length;
1896
+ }
1897
+ Object.entries(node).forEach(([entryKey, entryValue]) => visit(entryValue, entryKey));
1898
+ };
1899
+ visit(schema);
1900
+ return stats;
1901
+ }
1902
+ function isPlainObject(value) {
1903
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1904
+ }
1905
+ function StatusIcon({ tone }) {
1906
+ if (tone === "valid") {
1907
+ return /* @__PURE__ */ jsx14(CheckmarkFilled, { size: 20 });
1908
+ }
1909
+ if (tone === "invalid") {
1910
+ return /* @__PURE__ */ jsx14(ErrorFilled, { size: 20 });
1911
+ }
1912
+ return /* @__PURE__ */ jsx14(WarningFilled, { size: 20 });
1913
+ }
1914
+
1915
+ // src/components/TerminalPanel.tsx
1916
+ import * as React10 from "react";
1917
+ import { Play as Play2 } from "@carbon/icons-react";
1918
+ import { Button as Button5 } from "@echothink-ui/core";
1919
+ import { jsx as jsx15, jsxs as jsxs14 } from "react/jsx-runtime";
1920
+ function TerminalPanel({
1921
+ lines,
1922
+ prompt = "$",
1923
+ onCommand,
1924
+ disabled,
1925
+ className,
1926
+ role = "region",
1927
+ "aria-label": ariaLabel = "Sandbox terminal",
1928
+ "aria-labelledby": ariaLabelledBy,
1929
+ ...props
1930
+ }) {
1931
+ const [command, setCommand] = React10.useState("");
1932
+ const inputId = React10.useId();
1933
+ const errorCount = lines.filter((line) => line.stream === "stderr").length;
1934
+ const canSubmit = !disabled && command.trim().length > 0;
1935
+ const statusLabel2 = disabled ? "Input disabled" : "Sandboxed";
1936
+ const panelClassName = [
1937
+ "eth-dev-terminal-panel",
1938
+ disabled ? "eth-dev-terminal-panel--disabled" : "",
1939
+ className ?? ""
1940
+ ].filter(Boolean).join(" ");
1941
+ const submit = (event) => {
1942
+ event.preventDefault();
1943
+ if (!canSubmit) return;
1944
+ onCommand?.(command);
1945
+ setCommand("");
1946
+ };
1947
+ return /* @__PURE__ */ jsxs14(
1948
+ "section",
1949
+ {
1950
+ ...props,
1951
+ className: panelClassName,
1952
+ "data-eth-component": "TerminalPanel",
1953
+ role,
1954
+ "aria-label": ariaLabelledBy ? void 0 : ariaLabel,
1955
+ "aria-labelledby": ariaLabelledBy,
1956
+ children: [
1957
+ /* @__PURE__ */ jsxs14("header", { className: "eth-dev-terminal-panel__header", children: [
1958
+ /* @__PURE__ */ jsxs14("div", { className: "eth-dev-terminal-panel__heading", children: [
1959
+ /* @__PURE__ */ jsx15("p", { className: "eth-dev-terminal-panel__eyebrow", children: "Developer terminal" }),
1960
+ /* @__PURE__ */ jsx15("h3", { children: "Sandbox terminal" }),
1961
+ /* @__PURE__ */ jsx15("p", { children: "Review command output and send scoped commands into the configured sandbox." })
1962
+ ] }),
1963
+ /* @__PURE__ */ jsxs14("div", { className: "eth-dev-terminal-panel__status", "aria-live": "polite", children: [
1964
+ /* @__PURE__ */ jsx15("span", { className: "eth-dev-terminal-panel__status-indicator", "aria-hidden": "true" }),
1965
+ /* @__PURE__ */ jsx15("span", { children: statusLabel2 })
1966
+ ] })
1967
+ ] }),
1968
+ /* @__PURE__ */ jsxs14("dl", { className: "eth-dev-terminal-panel__summary", "aria-label": "Terminal summary", children: [
1969
+ /* @__PURE__ */ jsxs14("div", { children: [
1970
+ /* @__PURE__ */ jsx15("dt", { children: "Lines" }),
1971
+ /* @__PURE__ */ jsx15("dd", { children: lines.length })
1972
+ ] }),
1973
+ /* @__PURE__ */ jsxs14("div", { children: [
1974
+ /* @__PURE__ */ jsx15("dt", { children: "Stderr" }),
1975
+ /* @__PURE__ */ jsx15("dd", { children: errorCount })
1976
+ ] }),
1977
+ /* @__PURE__ */ jsxs14("div", { children: [
1978
+ /* @__PURE__ */ jsx15("dt", { children: "Prompt" }),
1979
+ /* @__PURE__ */ jsx15("dd", { children: prompt })
1980
+ ] })
1981
+ ] }),
1982
+ /* @__PURE__ */ jsx15("div", { className: "eth-dev-terminal-panel__viewport", children: lines.length ? /* @__PURE__ */ jsx15(
1983
+ "pre",
1984
+ {
1985
+ className: "eth-dev-terminal-panel__output",
1986
+ role: "log",
1987
+ "aria-label": "Terminal output",
1988
+ "aria-live": "polite",
1989
+ "aria-relevant": "additions text",
1990
+ "aria-atomic": "false",
1991
+ children: /* @__PURE__ */ jsx15("code", { className: "eth-dev-terminal-panel__output-code", children: lines.map((line) => /* @__PURE__ */ jsxs14(
1992
+ "span",
1993
+ {
1994
+ className: `eth-dev-terminal-panel__line eth-dev-terminal-panel__line--${line.stream}`,
1995
+ children: [
1996
+ /* @__PURE__ */ jsx15(
1997
+ "span",
1998
+ {
1999
+ className: "eth-dev-terminal-panel__stream",
2000
+ "aria-label": line.stream === "stderr" ? "Standard error" : "Standard output",
2001
+ children: line.stream === "stderr" ? "ERR" : "OUT"
2002
+ }
2003
+ ),
2004
+ /* @__PURE__ */ jsx15("span", { className: "eth-dev-terminal-panel__line-text", children: line.text })
2005
+ ]
2006
+ },
2007
+ line.id
2008
+ )) })
2009
+ }
2010
+ ) : /* @__PURE__ */ jsxs14("div", { className: "eth-dev-terminal-panel__empty", role: "status", children: [
2011
+ /* @__PURE__ */ jsx15("h4", { children: "No terminal output" }),
2012
+ /* @__PURE__ */ jsx15("p", { children: "Command output will appear here when the sandbox emits data." })
2013
+ ] }) }),
2014
+ /* @__PURE__ */ jsxs14("form", { className: "eth-dev-terminal-panel__input", onSubmit: submit, children: [
2015
+ /* @__PURE__ */ jsx15("label", { className: "eth-dev-terminal-panel__visually-hidden", htmlFor: inputId, children: "Terminal command" }),
2016
+ /* @__PURE__ */ jsx15("span", { className: "eth-dev-terminal-panel__prompt", "aria-hidden": "true", children: prompt }),
2017
+ /* @__PURE__ */ jsx15(
2018
+ "input",
2019
+ {
2020
+ id: inputId,
2021
+ value: command,
2022
+ disabled,
2023
+ onChange: (event) => setCommand(event.currentTarget.value),
2024
+ autoComplete: "off",
2025
+ spellCheck: false,
2026
+ placeholder: disabled ? "Input disabled" : "Type a command"
2027
+ }
2028
+ ),
2029
+ /* @__PURE__ */ jsx15(
2030
+ Button5,
2031
+ {
2032
+ type: "submit",
2033
+ density: "compact",
2034
+ intent: "primary",
2035
+ icon: /* @__PURE__ */ jsx15(Play2, { size: 16 }),
2036
+ disabled: !canSubmit,
2037
+ "aria-label": "Run command",
2038
+ children: "Run"
2039
+ }
2040
+ )
2041
+ ] })
2042
+ ]
2043
+ }
2044
+ );
2045
+ }
2046
+
2047
+ // src/components/TraceTimeline.tsx
2048
+ import * as React11 from "react";
2049
+ import { Badge as Badge2, StatusDot, statusLabel } from "@echothink-ui/core";
2050
+ import { jsx as jsx16, jsxs as jsxs15 } from "react/jsx-runtime";
2051
+ function TraceTimeline({
2052
+ spans,
2053
+ className,
2054
+ role = "region",
2055
+ "aria-label": ariaLabel = "Trace timeline",
2056
+ "aria-labelledby": ariaLabelledBy,
2057
+ ...props
2058
+ }) {
2059
+ const timeline = React11.useMemo(() => buildTimeline(spans), [spans]);
2060
+ const issueCount = spans.filter((span) => isIssueStatus(getSpanStatus(span))).length;
2061
+ const activeCount = spans.filter((span) => isActiveStatus(getSpanStatus(span))).length;
2062
+ const services = new Set(spans.flatMap((span) => span.service ? [span.service] : []));
2063
+ const serviceCount = services.size;
2064
+ const describedServiceCount = spans.length ? serviceCount || 1 : 0;
2065
+ const traceDescription = spans.length ? `${spans.length} spans across ${describedServiceCount} service${describedServiceCount === 1 ? "" : "s"}.` : "No spans have been recorded for this trace window.";
2066
+ const healthLabel = issueCount ? `${issueCount} ${issueCount === 1 ? "issue" : "issues"}` : activeCount ? `${activeCount} active` : "All complete";
2067
+ const healthSeverity = issueCount ? "danger" : activeCount ? "info" : "success";
2068
+ const rootClassName = ["eth-dev-trace-timeline", className].filter(Boolean).join(" ");
2069
+ return /* @__PURE__ */ jsxs15(
2070
+ "section",
2071
+ {
2072
+ ...props,
2073
+ "aria-label": ariaLabelledBy ? void 0 : ariaLabel,
2074
+ "aria-labelledby": ariaLabelledBy,
2075
+ className: rootClassName,
2076
+ "data-eth-component": "TraceTimeline",
2077
+ role,
2078
+ children: [
2079
+ /* @__PURE__ */ jsxs15("header", { className: "eth-dev-trace-timeline__header", children: [
2080
+ /* @__PURE__ */ jsxs15("div", { className: "eth-dev-trace-timeline__heading", children: [
2081
+ /* @__PURE__ */ jsx16("p", { className: "eth-dev-trace-timeline__eyebrow", children: "Distributed trace" }),
2082
+ /* @__PURE__ */ jsx16("h3", { children: "Trace timeline" }),
2083
+ /* @__PURE__ */ jsx16("p", { children: traceDescription })
2084
+ ] }),
2085
+ /* @__PURE__ */ jsx16(Badge2, { severity: healthSeverity, children: healthLabel })
2086
+ ] }),
2087
+ /* @__PURE__ */ jsxs15("dl", { className: "eth-dev-trace-timeline__summary", "aria-label": "Trace summary", children: [
2088
+ /* @__PURE__ */ jsxs15("div", { children: [
2089
+ /* @__PURE__ */ jsx16("dt", { children: "Spans" }),
2090
+ /* @__PURE__ */ jsx16("dd", { children: spans.length })
2091
+ ] }),
2092
+ /* @__PURE__ */ jsxs15("div", { children: [
2093
+ /* @__PURE__ */ jsx16("dt", { children: "Window" }),
2094
+ /* @__PURE__ */ jsx16("dd", { children: formatDuration(timeline.total) })
2095
+ ] }),
2096
+ /* @__PURE__ */ jsxs15("div", { children: [
2097
+ /* @__PURE__ */ jsx16("dt", { children: "Issues" }),
2098
+ /* @__PURE__ */ jsx16("dd", { children: issueCount ? `${issueCount} ${issueCount === 1 ? "issue" : "issues"}` : "0" })
2099
+ ] }),
2100
+ /* @__PURE__ */ jsxs15("div", { children: [
2101
+ /* @__PURE__ */ jsx16("dt", { children: "Services" }),
2102
+ /* @__PURE__ */ jsx16("dd", { children: serviceCount })
2103
+ ] })
2104
+ ] }),
2105
+ spans.length ? /* @__PURE__ */ jsxs15("div", { className: "eth-dev-trace-timeline__viewport", children: [
2106
+ /* @__PURE__ */ jsxs15("div", { className: "eth-dev-trace-timeline__axis", "aria-hidden": "true", children: [
2107
+ /* @__PURE__ */ jsx16("span", { children: "Span" }),
2108
+ /* @__PURE__ */ jsxs15("div", { className: "eth-dev-trace-timeline__axis-scale", children: [
2109
+ /* @__PURE__ */ jsx16("span", { children: "0 ms" }),
2110
+ /* @__PURE__ */ jsx16("span", { children: formatDuration(timeline.total / 2) }),
2111
+ /* @__PURE__ */ jsx16("span", { children: formatDuration(timeline.total) })
2112
+ ] })
2113
+ ] }),
2114
+ /* @__PURE__ */ jsx16("div", { className: "eth-dev-trace-timeline__rows", role: "list", "aria-label": "Trace spans", children: timeline.items.map(({ span, start, end, depth }) => {
2115
+ const status = getSpanStatus(span);
2116
+ const startOffset = start - timeline.min;
2117
+ const duration = end - start;
2118
+ const left = startOffset / timeline.total * 100;
2119
+ const width = Math.max(1, duration / timeline.total * 100);
2120
+ const spanLabel = formatSpanLabel(span, status, startOffset, duration);
2121
+ return /* @__PURE__ */ jsxs15(
2122
+ "article",
2123
+ {
2124
+ className: `eth-dev-trace-timeline__row eth-dev-trace-timeline__row--${status}`,
2125
+ role: "listitem",
2126
+ style: {
2127
+ "--eth-dev-trace-indent": `${depth}rem`,
2128
+ "--eth-dev-trace-left": `${left}%`,
2129
+ "--eth-dev-trace-width": `${width}%`
2130
+ },
2131
+ children: [
2132
+ /* @__PURE__ */ jsxs15("div", { className: "eth-dev-trace-timeline__label", children: [
2133
+ /* @__PURE__ */ jsxs15("div", { className: "eth-dev-trace-timeline__span-title", children: [
2134
+ /* @__PURE__ */ jsx16(StatusDot, { status, label: statusLabel(status) }),
2135
+ /* @__PURE__ */ jsx16("strong", { children: span.name })
2136
+ ] }),
2137
+ /* @__PURE__ */ jsxs15("div", { className: "eth-dev-trace-timeline__span-meta", children: [
2138
+ span.service ? /* @__PURE__ */ jsx16("span", { children: span.service }) : null,
2139
+ span.parentId ? /* @__PURE__ */ jsxs15("span", { children: [
2140
+ "Parent ",
2141
+ span.parentId
2142
+ ] }) : null,
2143
+ /* @__PURE__ */ jsx16("span", { children: formatDuration(duration) })
2144
+ ] })
2145
+ ] }),
2146
+ /* @__PURE__ */ jsx16("div", { className: "eth-dev-trace-timeline__track", role: "img", "aria-label": spanLabel, children: /* @__PURE__ */ jsx16(
2147
+ "span",
2148
+ {
2149
+ "aria-hidden": "true",
2150
+ className: `eth-dev-trace-timeline__bar eth-dev-trace-timeline__bar--${status}`,
2151
+ title: spanLabel
2152
+ }
2153
+ ) })
2154
+ ]
2155
+ },
2156
+ span.id
2157
+ );
2158
+ }) })
2159
+ ] }) : /* @__PURE__ */ jsxs15("div", { className: "eth-dev-trace-timeline__empty", role: "status", children: [
2160
+ /* @__PURE__ */ jsx16("h4", { children: "No spans recorded" }),
2161
+ /* @__PURE__ */ jsx16("p", { children: "Trace spans will appear here when instrumentation emits timing data." })
2162
+ ] })
2163
+ ]
2164
+ }
2165
+ );
2166
+ }
2167
+ function buildTimeline(spans) {
2168
+ if (!spans.length) return { items: [], min: 0, total: 0 };
2169
+ const spansById = new Map(spans.map((span) => [span.id, span]));
2170
+ const depthById = /* @__PURE__ */ new Map();
2171
+ const depthFor = (span, seen = /* @__PURE__ */ new Set()) => {
2172
+ if (depthById.has(span.id)) return depthById.get(span.id) ?? 0;
2173
+ if (!span.parentId || seen.has(span.id)) {
2174
+ depthById.set(span.id, 0);
2175
+ return 0;
2176
+ }
2177
+ const parent = spansById.get(span.parentId);
2178
+ const depth = parent ? depthFor(parent, new Set(seen).add(span.id)) + 1 : 0;
2179
+ depthById.set(span.id, depth);
2180
+ return depth;
2181
+ };
2182
+ const items = spans.map((span, index) => {
2183
+ const start = Number.isFinite(span.startMs) ? span.startMs : index * 10;
2184
+ const duration = Number.isFinite(span.durationMs) ? Math.max(span.durationMs, 1) : 1;
2185
+ return { span, start, end: start + duration, depth: depthFor(span) };
2186
+ }).sort((a, b) => a.start - b.start || a.span.name.localeCompare(b.span.name));
2187
+ const min = Math.min(...items.map((item) => item.start));
2188
+ const max = Math.max(...items.map((item) => item.end));
2189
+ return { items, min, total: Math.max(max - min, 1) };
2190
+ }
2191
+ function getSpanStatus(span) {
2192
+ return span.status ?? "running";
2193
+ }
2194
+ function isActiveStatus(status) {
2195
+ return status === "running" || status === "in-progress";
2196
+ }
2197
+ function isIssueStatus(status) {
2198
+ return status === "failed" || status === "blocked" || status === "warning" || status === "stale";
2199
+ }
2200
+ function formatSpanLabel(span, status, startOffset, duration) {
2201
+ return `${span.name}${span.service ? ` in ${span.service}` : ""}, ${statusLabel(
2202
+ status
2203
+ )}, starts at ${formatDuration(startOffset)}, duration ${formatDuration(duration)}`;
2204
+ }
2205
+ function formatDuration(ms) {
2206
+ if (!Number.isFinite(ms) || ms <= 0) return "0 ms";
2207
+ if (ms < 1e3) return `${Math.round(ms)} ms`;
2208
+ const seconds = ms / 1e3;
2209
+ const precision = seconds < 10 ? 2 : 1;
2210
+ return `${seconds.toFixed(precision).replace(/\.?0+$/, "")} s`;
2211
+ }
2212
+
2213
+ // src/components/WebhookEventViewer.tsx
2214
+ import { Badge as Badge3, EmptyState as EmptyState4 } from "@echothink-ui/core";
2215
+ import { DataTable as DataTable3 } from "@echothink-ui/data";
2216
+ import { jsx as jsx17, jsxs as jsxs16 } from "react/jsx-runtime";
2217
+ function WebhookEventViewer({
2218
+ events,
2219
+ onRetry,
2220
+ className,
2221
+ "aria-label": ariaLabel,
2222
+ ...props
2223
+ }) {
2224
+ const failedCount = events.filter(
2225
+ (event) => webhookStatusTone(event.status) === "danger"
2226
+ ).length;
2227
+ const retryCount = events.reduce((total, event) => total + event.retries, 0);
2228
+ const columns = [
2229
+ {
2230
+ key: "id",
2231
+ header: "Event",
2232
+ width: "16rem",
2233
+ render: (row) => /* @__PURE__ */ jsxs16("span", { className: "eth-dev-webhook-event-viewer__event", children: [
2234
+ /* @__PURE__ */ jsx17("code", { title: row.id, children: row.id }),
2235
+ /* @__PURE__ */ jsx17("span", { children: payloadEventLabel(row.payload) })
2236
+ ] })
2237
+ },
2238
+ {
2239
+ key: "url",
2240
+ header: "Endpoint",
2241
+ render: (row) => /* @__PURE__ */ jsx17("code", { className: "eth-dev-webhook-event-viewer__url", title: row.url, children: row.url })
2242
+ },
2243
+ {
2244
+ key: "status",
2245
+ header: "Status",
2246
+ width: "9rem",
2247
+ render: (row) => /* @__PURE__ */ jsx17(Badge3, { severity: webhookStatusTone(row.status), children: formatWebhookStatus(row.status) })
2248
+ },
2249
+ {
2250
+ key: "retries",
2251
+ header: "Retries",
2252
+ align: "end",
2253
+ width: "6.5rem",
2254
+ render: (row) => /* @__PURE__ */ jsx17("span", { className: "eth-dev-webhook-event-viewer__count", children: row.retries })
2255
+ },
2256
+ {
2257
+ key: "deliveredAt",
2258
+ header: "Delivered",
2259
+ width: "10rem",
2260
+ render: (row) => row.deliveredAt ? /* @__PURE__ */ jsx17("time", { className: "eth-dev-webhook-event-viewer__time", children: row.deliveredAt }) : /* @__PURE__ */ jsx17("span", { className: "eth-dev-webhook-event-viewer__missing", children: "Not delivered" })
2261
+ }
2262
+ ];
2263
+ return /* @__PURE__ */ jsxs16(
2264
+ "section",
2265
+ {
2266
+ ...props,
2267
+ "aria-label": ariaLabel ?? "Webhook deliveries",
2268
+ className: `eth-dev-webhook-event-viewer ${className ?? ""}`.trim(),
2269
+ "data-eth-component": "WebhookEventViewer",
2270
+ children: [
2271
+ /* @__PURE__ */ jsxs16("header", { className: "eth-dev-webhook-event-viewer__header", children: [
2272
+ /* @__PURE__ */ jsxs16("div", { className: "eth-dev-webhook-event-viewer__heading", children: [
2273
+ /* @__PURE__ */ jsx17("p", { children: "Webhook events" }),
2274
+ /* @__PURE__ */ jsx17("h3", { children: "Webhook deliveries" })
2275
+ ] }),
2276
+ /* @__PURE__ */ jsxs16(
2277
+ "dl",
2278
+ {
2279
+ className: "eth-dev-webhook-event-viewer__summary",
2280
+ "aria-label": "Webhook delivery summary",
2281
+ children: [
2282
+ /* @__PURE__ */ jsxs16("div", { children: [
2283
+ /* @__PURE__ */ jsx17("dt", { children: "Events" }),
2284
+ /* @__PURE__ */ jsx17("dd", { children: pluralize(events.length, "event") })
2285
+ ] }),
2286
+ /* @__PURE__ */ jsxs16("div", { children: [
2287
+ /* @__PURE__ */ jsx17("dt", { children: "Failed" }),
2288
+ /* @__PURE__ */ jsx17("dd", { children: pluralize(failedCount, "failed", "failed") })
2289
+ ] }),
2290
+ /* @__PURE__ */ jsxs16("div", { children: [
2291
+ /* @__PURE__ */ jsx17("dt", { children: "Retries" }),
2292
+ /* @__PURE__ */ jsx17("dd", { children: pluralize(retryCount, "retry", "retries") })
2293
+ ] })
2294
+ ]
2295
+ }
2296
+ )
2297
+ ] }),
2298
+ /* @__PURE__ */ jsx17(
2299
+ DataTable3,
2300
+ {
2301
+ rows: events,
2302
+ columns,
2303
+ rowKey: "id",
2304
+ density: "compact",
2305
+ className: "eth-dev-webhook-event-viewer__table",
2306
+ "aria-label": "Webhook delivery events",
2307
+ rowActions: onRetry ? (row) => isRetryableWebhookStatus(row.status) ? [
2308
+ {
2309
+ id: `retry-${row.id}`,
2310
+ label: "Retry",
2311
+ onSelect: () => onRetry(row.id)
2312
+ }
2313
+ ] : [] : void 0,
2314
+ emptyState: /* @__PURE__ */ jsx17(
2315
+ EmptyState4,
2316
+ {
2317
+ title: "No webhook events",
2318
+ description: "Delivery attempts and retry outcomes will appear here when webhooks fire."
2319
+ }
2320
+ )
2321
+ }
2322
+ )
2323
+ ]
2324
+ }
2325
+ );
2326
+ }
2327
+ function webhookStatusTone(status) {
2328
+ const token = normalizeWebhookStatus(status);
2329
+ if (["delivered", "success", "succeeded", "completed", "ok"].includes(token)) {
2330
+ return "success";
2331
+ }
2332
+ if (isRetryableWebhookStatus(token)) return "danger";
2333
+ if (["retrying", "queued", "pending", "running", "in-progress"].includes(token)) {
2334
+ return "info";
2335
+ }
2336
+ if (["warning", "throttled", "rate-limited"].includes(token)) {
2337
+ return "warning";
2338
+ }
2339
+ return "neutral";
2340
+ }
2341
+ function isRetryableWebhookStatus(status) {
2342
+ return ["failed", "error", "timeout", "timed-out", "undelivered"].includes(
2343
+ normalizeWebhookStatus(status)
2344
+ );
2345
+ }
2346
+ function normalizeWebhookStatus(status) {
2347
+ return status.trim().toLowerCase().replace(/_/g, "-");
2348
+ }
2349
+ function formatWebhookStatus(status) {
2350
+ const normalized = normalizeWebhookStatus(status);
2351
+ if (!normalized) return "Unknown";
2352
+ if (normalized === "ok") return "OK";
2353
+ return normalized.split("-").filter(Boolean).map((part) => part[0].toUpperCase() + part.slice(1)).join(" ");
2354
+ }
2355
+ function payloadEventLabel(payload) {
2356
+ if (payload && typeof payload === "object") {
2357
+ const record = payload;
2358
+ const eventName = record.event ?? record.type ?? record.name;
2359
+ if (typeof eventName === "string" && eventName.trim()) return eventName;
2360
+ }
2361
+ return "Webhook event";
2362
+ }
2363
+ function pluralize(count, singular, plural = `${singular}s`) {
2364
+ return `${count} ${count === 1 ? singular : plural}`;
2365
+ }
2366
+
2367
+ // src/components/YAMLViewer.tsx
2368
+ import * as React12 from "react";
2369
+ import { Checkmark as Checkmark3, Copy as Copy2 } from "@carbon/icons-react";
2370
+ import { IconButton as IconButton2 } from "@echothink-ui/core";
2371
+ import { Fragment as Fragment2, jsx as jsx18, jsxs as jsxs17 } from "react/jsx-runtime";
2372
+ function countYamlEntries(lines) {
2373
+ return lines.filter(
2374
+ (line) => /^\s*(?:-\s+)?[\w.-]+\s*:/.test(line) || /^\s*-\s+\S/.test(line)
2375
+ ).length;
2376
+ }
2377
+ function getScalarClass(value) {
2378
+ if (/^["'].*["']$/.test(value)) return "string";
2379
+ if (/^(?:true|false)$/i.test(value)) return "boolean";
2380
+ if (/^(?:null|~)$/i.test(value)) return "null";
2381
+ if (/^-?\d+(?:\.\d+)?$/.test(value)) return "number";
2382
+ return "plain";
2383
+ }
2384
+ function renderScalar(value) {
2385
+ if (!value) return null;
2386
+ const commentIndex = value.search(/(^|\s)#/);
2387
+ const scalar = commentIndex >= 0 ? value.slice(0, commentIndex) : value;
2388
+ const comment = commentIndex >= 0 ? value.slice(commentIndex) : "";
2389
+ const scalarClass = getScalarClass(scalar.trim());
2390
+ return /* @__PURE__ */ jsxs17(Fragment2, { children: [
2391
+ scalar ? /* @__PURE__ */ jsx18(
2392
+ "span",
2393
+ {
2394
+ className: `eth-dev-yaml-viewer__token eth-dev-yaml-viewer__token--${scalarClass}`,
2395
+ children: scalar
2396
+ }
2397
+ ) : null,
2398
+ comment ? /* @__PURE__ */ jsx18("span", { className: "eth-dev-yaml-viewer__token eth-dev-yaml-viewer__token--comment", children: comment }) : null
2399
+ ] });
2400
+ }
2401
+ function renderYamlLine(line) {
2402
+ if (!line.trim()) return line;
2403
+ if (/^\s*#/.test(line)) {
2404
+ return /* @__PURE__ */ jsx18("span", { className: "eth-dev-yaml-viewer__token eth-dev-yaml-viewer__token--comment", children: line });
2405
+ }
2406
+ const keyMatch = line.match(/^(\s*)(-\s+)?([\w.-]+)(\s*:)(.*)$/);
2407
+ if (keyMatch) {
2408
+ const [, indent, listMarker = "", key, separator, rest] = keyMatch;
2409
+ return /* @__PURE__ */ jsxs17(Fragment2, { children: [
2410
+ indent,
2411
+ listMarker ? /* @__PURE__ */ jsx18("span", { className: "eth-dev-yaml-viewer__token eth-dev-yaml-viewer__token--marker", children: listMarker }) : null,
2412
+ /* @__PURE__ */ jsx18("span", { className: "eth-dev-yaml-viewer__token eth-dev-yaml-viewer__token--key", children: key }),
2413
+ /* @__PURE__ */ jsx18("span", { className: "eth-dev-yaml-viewer__token eth-dev-yaml-viewer__token--punctuation", children: separator }),
2414
+ renderScalar(rest)
2415
+ ] });
2416
+ }
2417
+ const listMatch = line.match(/^(\s*)(-\s+)(.*)$/);
2418
+ if (listMatch) {
2419
+ const [, indent, marker, rest] = listMatch;
2420
+ return /* @__PURE__ */ jsxs17(Fragment2, { children: [
2421
+ indent,
2422
+ /* @__PURE__ */ jsx18("span", { className: "eth-dev-yaml-viewer__token eth-dev-yaml-viewer__token--marker", children: marker }),
2423
+ renderScalar(rest)
2424
+ ] });
2425
+ }
2426
+ return renderScalar(line) ?? line;
2427
+ }
2428
+ function YAMLViewer({
2429
+ value,
2430
+ editable = false,
2431
+ onChange,
2432
+ className,
2433
+ "aria-label": ariaLabel,
2434
+ ...props
2435
+ }) {
2436
+ const [copied, setCopied] = React12.useState(false);
2437
+ const resetTimer = React12.useRef(void 0);
2438
+ const lines = splitLines(value);
2439
+ const lineCount = lines.length;
2440
+ const entryCount = countYamlEntries(lines);
2441
+ const modeLabel = editable ? "Editable" : "Read-only";
2442
+ const viewerLabel = ariaLabel ?? (editable ? "YAML editor" : "YAML document");
2443
+ React12.useEffect(() => {
2444
+ return () => {
2445
+ if (resetTimer.current !== void 0 && typeof window !== "undefined") {
2446
+ window.clearTimeout(resetTimer.current);
2447
+ }
2448
+ };
2449
+ }, []);
2450
+ const copy = React12.useCallback(() => {
2451
+ if (typeof navigator === "undefined" || !navigator.clipboard) return;
2452
+ void navigator.clipboard.writeText(value).then(() => {
2453
+ if (typeof window !== "undefined") {
2454
+ setCopied(true);
2455
+ if (resetTimer.current !== void 0) {
2456
+ window.clearTimeout(resetTimer.current);
2457
+ }
2458
+ resetTimer.current = window.setTimeout(() => setCopied(false), 1200);
2459
+ }
2460
+ }).catch(() => {
2461
+ setCopied(false);
2462
+ });
2463
+ }, [value]);
2464
+ return /* @__PURE__ */ jsxs17(
2465
+ "section",
2466
+ {
2467
+ ...props,
2468
+ "aria-label": viewerLabel,
2469
+ className: `eth-dev-yaml-viewer ${className ?? ""}`.trim(),
2470
+ "data-eth-component": "YAMLViewer",
2471
+ "data-editable": editable ? "true" : void 0,
2472
+ children: [
2473
+ /* @__PURE__ */ jsxs17("header", { className: "eth-dev-yaml-viewer__header", children: [
2474
+ /* @__PURE__ */ jsxs17("div", { className: "eth-dev-yaml-viewer__heading", children: [
2475
+ /* @__PURE__ */ jsx18("span", { className: "eth-dev-yaml-viewer__eyebrow", children: "YAML" }),
2476
+ /* @__PURE__ */ jsx18("span", { className: "eth-dev-yaml-viewer__title", children: editable ? "YAML editor" : "YAML document" })
2477
+ ] }),
2478
+ /* @__PURE__ */ jsxs17(
2479
+ "div",
2480
+ {
2481
+ className: "eth-dev-yaml-viewer__meta",
2482
+ role: "group",
2483
+ "aria-label": "YAML document metadata",
2484
+ children: [
2485
+ /* @__PURE__ */ jsx18("span", { className: "eth-dev-yaml-viewer__badge", children: modeLabel }),
2486
+ /* @__PURE__ */ jsxs17("span", { className: "eth-dev-yaml-viewer__badge", children: [
2487
+ lineCount,
2488
+ " ",
2489
+ lineCount === 1 ? "line" : "lines"
2490
+ ] }),
2491
+ /* @__PURE__ */ jsxs17("span", { className: "eth-dev-yaml-viewer__badge", children: [
2492
+ entryCount,
2493
+ " ",
2494
+ entryCount === 1 ? "entry" : "entries"
2495
+ ] }),
2496
+ /* @__PURE__ */ jsx18(
2497
+ IconButton2,
2498
+ {
2499
+ className: "eth-dev-yaml-viewer__copy-button",
2500
+ label: copied ? "Copied YAML" : "Copy YAML",
2501
+ intent: "ghost",
2502
+ density: "compact",
2503
+ icon: copied ? /* @__PURE__ */ jsx18(Checkmark3, { size: 16 }) : /* @__PURE__ */ jsx18(Copy2, { size: 16 }),
2504
+ onClick: copy
2505
+ }
2506
+ ),
2507
+ /* @__PURE__ */ jsx18("span", { className: "eth-dev-yaml-viewer__copy-status", "aria-live": "polite", children: copied ? "YAML copied" : "" })
2508
+ ]
2509
+ }
2510
+ )
2511
+ ] }),
2512
+ editable ? /* @__PURE__ */ jsx18(
2513
+ CodeEditor,
2514
+ {
2515
+ value,
2516
+ onChange,
2517
+ language: "yaml",
2518
+ className: "eth-dev-yaml-viewer__editor",
2519
+ "aria-label": viewerLabel
2520
+ }
2521
+ ) : /* @__PURE__ */ jsx18("pre", { className: "eth-dev-yaml-viewer__pre", "aria-label": viewerLabel, tabIndex: 0, children: /* @__PURE__ */ jsx18("code", { className: "eth-dev-yaml-viewer__code", children: lines.map((line, index) => /* @__PURE__ */ jsxs17("span", { className: "eth-dev-yaml-viewer__line", children: [
2522
+ /* @__PURE__ */ jsx18("span", { className: "eth-dev-yaml-viewer__gutter", "aria-hidden": "true", children: index + 1 }),
2523
+ /* @__PURE__ */ jsx18("span", { className: "eth-dev-yaml-viewer__line-code", children: renderYamlLine(line) })
2524
+ ] }, index)) }) })
2525
+ ]
2526
+ }
2527
+ );
2528
+ }
2529
+
2530
+ // src/index.tsx
2531
+ var DeveloperComponentNames = [
2532
+ "CodeBlock",
2533
+ "CodeEditor",
2534
+ "DiffTable",
2535
+ "DiffViewer",
2536
+ "JSONViewer",
2537
+ "YAMLViewer",
2538
+ "SchemaViewer",
2539
+ "APIExplorer",
2540
+ "EventPayloadViewer",
2541
+ "LogConsole",
2542
+ "TerminalPanel",
2543
+ "TraceTimeline",
2544
+ "RequestResponseViewer",
2545
+ "WebhookEventViewer",
2546
+ "GitRepositoryPanel",
2547
+ "PullRequestPanel",
2548
+ "CommitList",
2549
+ "BranchSelector"
2550
+ ];
2551
+ export {
2552
+ APIExplorer,
2553
+ BranchSelector,
2554
+ CodeBlock,
2555
+ CodeEditor,
2556
+ CommitList,
2557
+ DeveloperComponentNames,
2558
+ DiffTable,
2559
+ DiffViewer,
2560
+ EventPayloadViewer,
2561
+ GitRepositoryPanel,
2562
+ JSONViewer,
2563
+ LogConsole,
2564
+ PullRequestPanel,
2565
+ RequestResponseViewer,
2566
+ SchemaViewer,
2567
+ TerminalPanel,
2568
+ TraceTimeline,
2569
+ WebhookEventViewer,
2570
+ YAMLViewer
2571
+ };
2572
+ //# sourceMappingURL=index.js.map