@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.
- package/README.md +5 -0
- package/dist/components/APIExplorer.d.ts +2 -0
- package/dist/components/BranchSelector.d.ts +2 -0
- package/dist/components/CodeBlock.d.ts +2 -0
- package/dist/components/CodeEditor.d.ts +2 -0
- package/dist/components/CommitList.d.ts +2 -0
- package/dist/components/DiffTable.d.ts +2 -0
- package/dist/components/DiffViewer.d.ts +2 -0
- package/dist/components/EventPayloadViewer.d.ts +2 -0
- package/dist/components/GitRepositoryPanel.d.ts +2 -0
- package/dist/components/JSONViewer.d.ts +2 -0
- package/dist/components/LogConsole.d.ts +2 -0
- package/dist/components/PullRequestPanel.d.ts +2 -0
- package/dist/components/RequestResponseViewer.d.ts +2 -0
- package/dist/components/SchemaViewer.d.ts +2 -0
- package/dist/components/TerminalPanel.d.ts +2 -0
- package/dist/components/TraceTimeline.d.ts +2 -0
- package/dist/components/WebhookEventViewer.d.ts +2 -0
- package/dist/components/YAMLViewer.d.ts +2 -0
- package/dist/components/devUtils.d.ts +10 -0
- package/dist/components/types.d.ts +196 -0
- package/dist/index.cjs +2627 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.css +3651 -0
- package/dist/index.css.map +1 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.js +2572 -0
- package/dist/index.js.map +1 -0
- package/package.json +43 -0
- package/src/components/APIExplorer.tsx +205 -0
- package/src/components/BranchSelector.tsx +54 -0
- package/src/components/CodeBlock.tsx +127 -0
- package/src/components/CodeEditor.tsx +95 -0
- package/src/components/CommitList.tsx +100 -0
- package/src/components/DiffTable.tsx +288 -0
- package/src/components/DiffViewer.tsx +145 -0
- package/src/components/EventPayloadViewer.tsx +91 -0
- package/src/components/GitRepositoryPanel.tsx +73 -0
- package/src/components/JSONViewer.tsx +189 -0
- package/src/components/LogConsole.tsx +160 -0
- package/src/components/PullRequestPanel.test.tsx +52 -0
- package/src/components/PullRequestPanel.tsx +215 -0
- package/src/components/RequestResponseViewer.test.tsx +45 -0
- package/src/components/RequestResponseViewer.tsx +169 -0
- package/src/components/SchemaViewer.tsx +157 -0
- package/src/components/TerminalPanel.test.tsx +33 -0
- package/src/components/TerminalPanel.tsx +134 -0
- package/src/components/TraceTimeline.test.tsx +63 -0
- package/src/components/TraceTimeline.tsx +207 -0
- package/src/components/WebhookEventViewer.test.tsx +57 -0
- package/src/components/WebhookEventViewer.tsx +184 -0
- package/src/components/YAMLViewer.tsx +207 -0
- package/src/components/devUtils.ts +81 -0
- package/src/components/types.ts +230 -0
- package/src/index.tsx +72 -0
- package/src/styles.css +4296 -0
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { EmptyState } from "@echothink-ui/core";
|
|
3
|
+
import type { DiffTableChangeType, DiffTableProps, DiffTableRow } from "./types";
|
|
4
|
+
|
|
5
|
+
const changeTypes = new Set<DiffTableChangeType>([
|
|
6
|
+
"added",
|
|
7
|
+
"modified",
|
|
8
|
+
"removed",
|
|
9
|
+
"renamed",
|
|
10
|
+
"unchanged",
|
|
11
|
+
"conflict"
|
|
12
|
+
]);
|
|
13
|
+
|
|
14
|
+
function getNumeric(row: DiffTableRow, keys: string[]) {
|
|
15
|
+
for (const key of keys) {
|
|
16
|
+
const value = row[key];
|
|
17
|
+
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
18
|
+
if (typeof value === "string" && value.trim() !== "" && Number.isFinite(Number(value))) {
|
|
19
|
+
return Number(value);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return 0;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function getPath(row: DiffTableRow) {
|
|
26
|
+
const value = row.afterPath ?? row.path ?? row.file ?? row.name;
|
|
27
|
+
return typeof value === "string" && value.trim() ? value : "Unknown file";
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function getPreviousPath(row: DiffTableRow) {
|
|
31
|
+
const value = row.beforePath ?? row.previousPath;
|
|
32
|
+
return typeof value === "string" && value.trim() ? value : undefined;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function normalizeChangeType(value: unknown): DiffTableChangeType | undefined {
|
|
36
|
+
if (typeof value !== "string") return undefined;
|
|
37
|
+
const normalized = value
|
|
38
|
+
.trim()
|
|
39
|
+
.toLowerCase()
|
|
40
|
+
.replace(/[_\s]+/g, "-");
|
|
41
|
+
if (normalized === "delete" || normalized === "deleted" || normalized === "deletion") {
|
|
42
|
+
return "removed";
|
|
43
|
+
}
|
|
44
|
+
if (normalized === "add" || normalized === "created" || normalized === "create") {
|
|
45
|
+
return "added";
|
|
46
|
+
}
|
|
47
|
+
if (normalized === "rename" || normalized === "moved") {
|
|
48
|
+
return "renamed";
|
|
49
|
+
}
|
|
50
|
+
return changeTypes.has(normalized as DiffTableChangeType)
|
|
51
|
+
? (normalized as DiffTableChangeType)
|
|
52
|
+
: undefined;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function inferChangeType(row: DiffTableRow): DiffTableChangeType {
|
|
56
|
+
const explicit = normalizeChangeType(row.changeType ?? row.status ?? row.kind ?? row.type);
|
|
57
|
+
if (explicit) return explicit;
|
|
58
|
+
if (getPreviousPath(row) && getPreviousPath(row) !== getPath(row)) return "renamed";
|
|
59
|
+
|
|
60
|
+
const added = getAdded(row);
|
|
61
|
+
const removed = getRemoved(row);
|
|
62
|
+
if (added > 0 && removed > 0) return "modified";
|
|
63
|
+
if (added > 0) return "added";
|
|
64
|
+
if (removed > 0) return "removed";
|
|
65
|
+
return "unchanged";
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function getAdded(row: DiffTableRow) {
|
|
69
|
+
return getNumeric(row, ["added", "additions", "linesAdded", "insertions"]);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function getRemoved(row: DiffTableRow) {
|
|
73
|
+
return getNumeric(row, ["removed", "deletions", "deleted", "linesRemoved"]);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function changeLabel(type: DiffTableChangeType) {
|
|
77
|
+
if (type === "added") return "Added";
|
|
78
|
+
if (type === "removed") return "Removed";
|
|
79
|
+
if (type === "renamed") return "Renamed";
|
|
80
|
+
if (type === "unchanged") return "Unchanged";
|
|
81
|
+
if (type === "conflict") return "Conflict";
|
|
82
|
+
return "Modified";
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function formatAdded(value: number) {
|
|
86
|
+
return value > 0 ? `+${value}` : "0";
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function formatRemoved(value: number) {
|
|
90
|
+
return value > 0 ? `-${value}` : "0";
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function formatNet(value: number) {
|
|
94
|
+
if (value > 0) return `+${value}`;
|
|
95
|
+
if (value < 0) return String(value);
|
|
96
|
+
return "0";
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function defaultSummary(row: DiffTableRow, type: DiffTableChangeType) {
|
|
100
|
+
const hunks = getNumeric(row, ["hunks", "sections"]);
|
|
101
|
+
if (typeof row.summary !== "undefined") return row.summary;
|
|
102
|
+
if (type === "renamed") return "Path changed";
|
|
103
|
+
if (type === "unchanged") return "No line changes";
|
|
104
|
+
if (type === "conflict") return "Review required";
|
|
105
|
+
if (hunks > 0) return `${hunks} ${hunks === 1 ? "hunk" : "hunks"}`;
|
|
106
|
+
return "Line changes";
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function DiffTable({
|
|
110
|
+
rows,
|
|
111
|
+
mode = "side-by-side",
|
|
112
|
+
title = "Diff summary",
|
|
113
|
+
subtitle,
|
|
114
|
+
showSummary = true,
|
|
115
|
+
emptyState,
|
|
116
|
+
className,
|
|
117
|
+
"aria-label": ariaLabel,
|
|
118
|
+
...props
|
|
119
|
+
}: DiffTableProps) {
|
|
120
|
+
const totals = React.useMemo(
|
|
121
|
+
() =>
|
|
122
|
+
rows.reduce(
|
|
123
|
+
(acc, row) => {
|
|
124
|
+
const type = inferChangeType(row);
|
|
125
|
+
acc.added += getAdded(row);
|
|
126
|
+
acc.removed += getRemoved(row);
|
|
127
|
+
if (type !== "unchanged") acc.changed += 1;
|
|
128
|
+
return acc;
|
|
129
|
+
},
|
|
130
|
+
{ added: 0, removed: 0, changed: 0 }
|
|
131
|
+
),
|
|
132
|
+
[rows]
|
|
133
|
+
);
|
|
134
|
+
const effectiveSubtitle =
|
|
135
|
+
subtitle ??
|
|
136
|
+
`${totals.changed} of ${rows.length} files changed, ${totals.added} additions, ${totals.removed} deletions`;
|
|
137
|
+
const label = ariaLabel ?? (typeof title === "string" ? title : "Diff summary");
|
|
138
|
+
|
|
139
|
+
return (
|
|
140
|
+
<section
|
|
141
|
+
{...props}
|
|
142
|
+
className={[
|
|
143
|
+
"eth-dev-diff-table",
|
|
144
|
+
`eth-dev-diff-table--${mode}`,
|
|
145
|
+
rows.length ? undefined : "eth-dev-diff-table--empty",
|
|
146
|
+
className
|
|
147
|
+
]
|
|
148
|
+
.filter(Boolean)
|
|
149
|
+
.join(" ")}
|
|
150
|
+
data-eth-component="DiffTable"
|
|
151
|
+
aria-label={label}
|
|
152
|
+
>
|
|
153
|
+
<header className="eth-dev-diff-table__header">
|
|
154
|
+
<div className="eth-dev-diff-table__heading">
|
|
155
|
+
<h3>{title}</h3>
|
|
156
|
+
{effectiveSubtitle ? <p>{effectiveSubtitle}</p> : null}
|
|
157
|
+
</div>
|
|
158
|
+
{showSummary ? (
|
|
159
|
+
<dl className="eth-dev-diff-table__summary" aria-label="Diff totals">
|
|
160
|
+
<div>
|
|
161
|
+
<dt>Files</dt>
|
|
162
|
+
<dd>{totals.changed}</dd>
|
|
163
|
+
</div>
|
|
164
|
+
<div>
|
|
165
|
+
<dt>Added</dt>
|
|
166
|
+
<dd className="eth-dev-diff-table__summary-value--added">
|
|
167
|
+
{formatAdded(totals.added)}
|
|
168
|
+
</dd>
|
|
169
|
+
</div>
|
|
170
|
+
<div>
|
|
171
|
+
<dt>Removed</dt>
|
|
172
|
+
<dd className="eth-dev-diff-table__summary-value--removed">
|
|
173
|
+
{formatRemoved(totals.removed)}
|
|
174
|
+
</dd>
|
|
175
|
+
</div>
|
|
176
|
+
</dl>
|
|
177
|
+
) : null}
|
|
178
|
+
</header>
|
|
179
|
+
{rows.length ? (
|
|
180
|
+
<div className="eth-dev-diff-table__viewport">
|
|
181
|
+
<table className="eth-dev-diff-table__table">
|
|
182
|
+
<caption className="eth-dev-diff-table__caption">{label}</caption>
|
|
183
|
+
<thead>
|
|
184
|
+
<tr>
|
|
185
|
+
<th scope="col">File</th>
|
|
186
|
+
<th scope="col">State</th>
|
|
187
|
+
{mode === "inline" ? (
|
|
188
|
+
<th scope="col">Changes</th>
|
|
189
|
+
) : (
|
|
190
|
+
<>
|
|
191
|
+
<th scope="col">Added</th>
|
|
192
|
+
<th scope="col">Removed</th>
|
|
193
|
+
<th scope="col">Net</th>
|
|
194
|
+
</>
|
|
195
|
+
)}
|
|
196
|
+
<th scope="col">Summary</th>
|
|
197
|
+
</tr>
|
|
198
|
+
</thead>
|
|
199
|
+
<tbody>
|
|
200
|
+
{rows.map((row, index) => {
|
|
201
|
+
const added = getAdded(row);
|
|
202
|
+
const removed = getRemoved(row);
|
|
203
|
+
const type = inferChangeType(row);
|
|
204
|
+
const path = getPath(row);
|
|
205
|
+
const previousPath = getPreviousPath(row);
|
|
206
|
+
const key = row.id ?? `${path}-${index}`;
|
|
207
|
+
|
|
208
|
+
return (
|
|
209
|
+
<tr key={key} className={`eth-dev-diff-table__row--${type}`}>
|
|
210
|
+
<th scope="row">
|
|
211
|
+
<span className="eth-dev-diff-table__file">
|
|
212
|
+
<code>{path}</code>
|
|
213
|
+
{previousPath && previousPath !== path ? (
|
|
214
|
+
<span>was {previousPath}</span>
|
|
215
|
+
) : null}
|
|
216
|
+
</span>
|
|
217
|
+
</th>
|
|
218
|
+
<td>
|
|
219
|
+
<span
|
|
220
|
+
className={`eth-dev-diff-table__state eth-dev-diff-table__state--${type}`}
|
|
221
|
+
>
|
|
222
|
+
{changeLabel(type)}
|
|
223
|
+
</span>
|
|
224
|
+
</td>
|
|
225
|
+
{mode === "inline" ? (
|
|
226
|
+
<td>
|
|
227
|
+
<span className="eth-dev-diff-table__inline-delta">
|
|
228
|
+
<span
|
|
229
|
+
className="eth-dev-diff-table__count eth-dev-diff-table__count--added"
|
|
230
|
+
aria-label={`${added} added lines`}
|
|
231
|
+
>
|
|
232
|
+
{formatAdded(added)}
|
|
233
|
+
</span>
|
|
234
|
+
<span
|
|
235
|
+
className="eth-dev-diff-table__count eth-dev-diff-table__count--removed"
|
|
236
|
+
aria-label={`${removed} removed lines`}
|
|
237
|
+
>
|
|
238
|
+
{formatRemoved(removed)}
|
|
239
|
+
</span>
|
|
240
|
+
</span>
|
|
241
|
+
</td>
|
|
242
|
+
) : (
|
|
243
|
+
<>
|
|
244
|
+
<td>
|
|
245
|
+
<span
|
|
246
|
+
className="eth-dev-diff-table__count eth-dev-diff-table__count--added"
|
|
247
|
+
aria-label={`${added} added lines`}
|
|
248
|
+
>
|
|
249
|
+
{formatAdded(added)}
|
|
250
|
+
</span>
|
|
251
|
+
</td>
|
|
252
|
+
<td>
|
|
253
|
+
<span
|
|
254
|
+
className="eth-dev-diff-table__count eth-dev-diff-table__count--removed"
|
|
255
|
+
aria-label={`${removed} removed lines`}
|
|
256
|
+
>
|
|
257
|
+
{formatRemoved(removed)}
|
|
258
|
+
</span>
|
|
259
|
+
</td>
|
|
260
|
+
<td>
|
|
261
|
+
<span className="eth-dev-diff-table__count">
|
|
262
|
+
{formatNet(added - removed)}
|
|
263
|
+
</span>
|
|
264
|
+
</td>
|
|
265
|
+
</>
|
|
266
|
+
)}
|
|
267
|
+
<td>
|
|
268
|
+
<span className="eth-dev-diff-table__row-summary">
|
|
269
|
+
{defaultSummary(row, type)}
|
|
270
|
+
</span>
|
|
271
|
+
</td>
|
|
272
|
+
</tr>
|
|
273
|
+
);
|
|
274
|
+
})}
|
|
275
|
+
</tbody>
|
|
276
|
+
</table>
|
|
277
|
+
</div>
|
|
278
|
+
) : (
|
|
279
|
+
(emptyState ?? (
|
|
280
|
+
<EmptyState
|
|
281
|
+
title="No diff rows"
|
|
282
|
+
description="No files have line-level changes in this comparison."
|
|
283
|
+
/>
|
|
284
|
+
))
|
|
285
|
+
)}
|
|
286
|
+
</section>
|
|
287
|
+
);
|
|
288
|
+
}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { Button } from "@echothink-ui/core";
|
|
3
|
+
import type { DiffViewerProps } from "./types";
|
|
4
|
+
import { buildLineDiff, type DiffLine } from "./devUtils";
|
|
5
|
+
|
|
6
|
+
type DiffViewerMode = NonNullable<DiffViewerProps["mode"]>;
|
|
7
|
+
|
|
8
|
+
export function DiffViewer({
|
|
9
|
+
before,
|
|
10
|
+
after,
|
|
11
|
+
mode = "inline",
|
|
12
|
+
language,
|
|
13
|
+
className,
|
|
14
|
+
...props
|
|
15
|
+
}: DiffViewerProps) {
|
|
16
|
+
const [viewMode, setViewMode] = React.useState<DiffViewerMode>(mode);
|
|
17
|
+
const diff = React.useMemo(() => buildLineDiff(before, after), [before, after]);
|
|
18
|
+
const counts = React.useMemo(
|
|
19
|
+
() => ({
|
|
20
|
+
additions: diff.filter((line) => line.type === "add").length,
|
|
21
|
+
deletions: diff.filter((line) => line.type === "remove").length
|
|
22
|
+
}),
|
|
23
|
+
[diff]
|
|
24
|
+
);
|
|
25
|
+
const languageLabel = language?.trim() || "diff";
|
|
26
|
+
|
|
27
|
+
React.useEffect(() => setViewMode(mode), [mode]);
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<section
|
|
31
|
+
{...props}
|
|
32
|
+
className={`eth-dev-diff-viewer eth-dev-diff-viewer--${viewMode} ${className ?? ""}`.trim()}
|
|
33
|
+
aria-label={props["aria-label"] ?? `${languageLabel} diff viewer`}
|
|
34
|
+
data-eth-component="DiffViewer"
|
|
35
|
+
data-language={languageLabel}
|
|
36
|
+
>
|
|
37
|
+
<header className="eth-dev-diff-viewer__header">
|
|
38
|
+
<div className="eth-dev-diff-viewer__heading">
|
|
39
|
+
<span className="eth-dev-diff-viewer__language">{languageLabel}</span>
|
|
40
|
+
<span className="eth-dev-diff-viewer__mode">
|
|
41
|
+
{viewMode === "inline" ? "Inline" : "Side by side"}
|
|
42
|
+
</span>
|
|
43
|
+
</div>
|
|
44
|
+
<div className="eth-dev-diff-viewer__summary" aria-label="Diff summary">
|
|
45
|
+
<span className="eth-dev-diff-viewer__stat eth-dev-diff-viewer__stat--add">
|
|
46
|
+
<strong>{counts.additions}</strong> additions
|
|
47
|
+
</span>
|
|
48
|
+
<span className="eth-dev-diff-viewer__stat eth-dev-diff-viewer__stat--remove">
|
|
49
|
+
<strong>{counts.deletions}</strong> deletions
|
|
50
|
+
</span>
|
|
51
|
+
</div>
|
|
52
|
+
<div className="eth-dev-diff-viewer__view-switch" role="group" aria-label="Diff view mode">
|
|
53
|
+
<Button
|
|
54
|
+
intent={viewMode === "inline" ? "primary" : "secondary"}
|
|
55
|
+
density="compact"
|
|
56
|
+
aria-pressed={viewMode === "inline"}
|
|
57
|
+
onClick={() => setViewMode("inline")}
|
|
58
|
+
>
|
|
59
|
+
Inline
|
|
60
|
+
</Button>
|
|
61
|
+
<Button
|
|
62
|
+
intent={viewMode === "side-by-side" ? "primary" : "secondary"}
|
|
63
|
+
density="compact"
|
|
64
|
+
aria-pressed={viewMode === "side-by-side"}
|
|
65
|
+
onClick={() => setViewMode("side-by-side")}
|
|
66
|
+
>
|
|
67
|
+
Side by side
|
|
68
|
+
</Button>
|
|
69
|
+
</div>
|
|
70
|
+
</header>
|
|
71
|
+
{viewMode === "inline" ? (
|
|
72
|
+
<pre
|
|
73
|
+
className={`eth-dev-diff-viewer__inline language-${toLanguageClass(languageLabel)}`}
|
|
74
|
+
aria-label="Inline diff"
|
|
75
|
+
>
|
|
76
|
+
<code className="eth-dev-diff-viewer__code">
|
|
77
|
+
{diff.map((line, index) => (
|
|
78
|
+
<DiffLineView key={index} line={line} />
|
|
79
|
+
))}
|
|
80
|
+
</code>
|
|
81
|
+
</pre>
|
|
82
|
+
) : (
|
|
83
|
+
<div className="eth-dev-diff-viewer__split" role="table" aria-label="Side-by-side diff">
|
|
84
|
+
<div role="rowgroup">
|
|
85
|
+
<div className="eth-dev-diff-viewer__split-header" role="row">
|
|
86
|
+
<div role="columnheader">Before</div>
|
|
87
|
+
<div role="columnheader">After</div>
|
|
88
|
+
</div>
|
|
89
|
+
</div>
|
|
90
|
+
<div className="eth-dev-diff-viewer__split-body" role="rowgroup">
|
|
91
|
+
{diff.map((line, index) => (
|
|
92
|
+
<div
|
|
93
|
+
key={index}
|
|
94
|
+
className={`eth-dev-diff-viewer__split-row eth-dev-diff-viewer__split-row--${line.type}`}
|
|
95
|
+
role="row"
|
|
96
|
+
>
|
|
97
|
+
<DiffCell side="before" line={line} />
|
|
98
|
+
<DiffCell side="after" line={line} />
|
|
99
|
+
</div>
|
|
100
|
+
))}
|
|
101
|
+
</div>
|
|
102
|
+
</div>
|
|
103
|
+
)}
|
|
104
|
+
</section>
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function DiffLineView({ line }: { line: DiffLine }) {
|
|
109
|
+
return (
|
|
110
|
+
<span className={`eth-dev-diff-viewer__line eth-dev-diff-viewer__line--${line.type}`}>
|
|
111
|
+
<span className="eth-dev-diff-viewer__gutter">{line.beforeLine ?? line.afterLine ?? ""}</span>
|
|
112
|
+
<span className="eth-dev-diff-viewer__marker">{lineMarker(line.type)}</span>
|
|
113
|
+
<span className="eth-dev-diff-viewer__content">{line.text || " "}</span>
|
|
114
|
+
</span>
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function DiffCell({ side, line }: { side: "before" | "after"; line: DiffLine }) {
|
|
119
|
+
const empty =
|
|
120
|
+
(side === "before" && line.type === "add") || (side === "after" && line.type === "remove");
|
|
121
|
+
const lineNumber = side === "before" ? line.beforeLine : line.afterLine;
|
|
122
|
+
|
|
123
|
+
return (
|
|
124
|
+
<pre
|
|
125
|
+
className={`eth-dev-diff-viewer__cell eth-dev-diff-viewer__cell--${empty ? "empty" : line.type}`}
|
|
126
|
+
role="cell"
|
|
127
|
+
>
|
|
128
|
+
<code className="eth-dev-diff-viewer__cell-code">
|
|
129
|
+
<span className="eth-dev-diff-viewer__gutter">{empty ? "" : (lineNumber ?? "")}</span>
|
|
130
|
+
<span className="eth-dev-diff-viewer__marker">{empty ? " " : lineMarker(line.type)}</span>
|
|
131
|
+
<span className="eth-dev-diff-viewer__content">{empty ? " " : line.text || " "}</span>
|
|
132
|
+
</code>
|
|
133
|
+
</pre>
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function lineMarker(type: DiffLine["type"]) {
|
|
138
|
+
if (type === "add") return "+";
|
|
139
|
+
if (type === "remove") return "-";
|
|
140
|
+
return " ";
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function toLanguageClass(language: string) {
|
|
144
|
+
return language.toLowerCase().replace(/[^a-z0-9_-]/g, "-");
|
|
145
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import type { EventPayloadViewerProps } from "./types";
|
|
3
|
+
import { JSONViewer } from "./JSONViewer";
|
|
4
|
+
|
|
5
|
+
export function EventPayloadViewer({
|
|
6
|
+
eventId,
|
|
7
|
+
payload,
|
|
8
|
+
className,
|
|
9
|
+
"aria-label": ariaLabel,
|
|
10
|
+
...props
|
|
11
|
+
}: EventPayloadViewerProps) {
|
|
12
|
+
const summary = describePayload(payload);
|
|
13
|
+
|
|
14
|
+
return (
|
|
15
|
+
<section
|
|
16
|
+
{...props}
|
|
17
|
+
className={`eth-dev-event-payload-viewer ${className ?? ""}`}
|
|
18
|
+
aria-label={ariaLabel ?? (eventId ? `Event payload ${eventId}` : "Event payload")}
|
|
19
|
+
data-eth-component="EventPayloadViewer"
|
|
20
|
+
>
|
|
21
|
+
<header className="eth-dev-event-payload-viewer__header">
|
|
22
|
+
<div className="eth-dev-event-payload-viewer__heading">
|
|
23
|
+
<span>Event payload</span>
|
|
24
|
+
<h3>{eventId ? <code>{eventId}</code> : "Payload"}</h3>
|
|
25
|
+
</div>
|
|
26
|
+
<dl className="eth-dev-event-payload-viewer__summary" aria-label="Payload summary">
|
|
27
|
+
<div>
|
|
28
|
+
<dt>Root</dt>
|
|
29
|
+
<dd>{summary.root}</dd>
|
|
30
|
+
</div>
|
|
31
|
+
<div>
|
|
32
|
+
<dt>{summary.countLabel}</dt>
|
|
33
|
+
<dd>{summary.countValue}</dd>
|
|
34
|
+
</div>
|
|
35
|
+
<div>
|
|
36
|
+
<dt>Size</dt>
|
|
37
|
+
<dd>{summary.size}</dd>
|
|
38
|
+
</div>
|
|
39
|
+
</dl>
|
|
40
|
+
</header>
|
|
41
|
+
<JSONViewer className="eth-dev-event-payload-viewer__json" value={payload} defaultExpandedDepth={3} />
|
|
42
|
+
</section>
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function describePayload(value: unknown) {
|
|
47
|
+
const text = serializePayload(value);
|
|
48
|
+
|
|
49
|
+
if (Array.isArray(value)) {
|
|
50
|
+
return {
|
|
51
|
+
root: "array",
|
|
52
|
+
countLabel: "Items",
|
|
53
|
+
countValue: String(value.length),
|
|
54
|
+
size: formatPayloadSize(text.length)
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (value !== null && typeof value === "object") {
|
|
59
|
+
return {
|
|
60
|
+
root: "object",
|
|
61
|
+
countLabel: "Fields",
|
|
62
|
+
countValue: String(Object.keys(value as Record<string, unknown>).length),
|
|
63
|
+
size: formatPayloadSize(text.length)
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return {
|
|
68
|
+
root: value === null ? "null" : typeof value,
|
|
69
|
+
countLabel: "Shape",
|
|
70
|
+
countValue: "scalar",
|
|
71
|
+
size: formatPayloadSize(text.length)
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function serializePayload(value: unknown) {
|
|
76
|
+
if (typeof value === "string") return value;
|
|
77
|
+
if (typeof value === "undefined") return "";
|
|
78
|
+
|
|
79
|
+
try {
|
|
80
|
+
return JSON.stringify(value, null, 2) ?? "";
|
|
81
|
+
} catch {
|
|
82
|
+
return String(value);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function formatPayloadSize(size: number) {
|
|
87
|
+
if (size < 1000) return `${size} B`;
|
|
88
|
+
if (size < 1000 * 1000) return `${(size / 1000).toFixed(1)} KB`;
|
|
89
|
+
|
|
90
|
+
return `${(size / (1000 * 1000)).toFixed(1)} MB`;
|
|
91
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { Branch, Commit, GitRepo, Launch, PullRequest, Time } from "@carbon/icons-react";
|
|
3
|
+
import type { GitRepositoryPanelProps } from "./types";
|
|
4
|
+
|
|
5
|
+
export function GitRepositoryPanel({ repo, className, ...props }: GitRepositoryPanelProps) {
|
|
6
|
+
const summaryItems = [
|
|
7
|
+
{
|
|
8
|
+
label: "Default branch",
|
|
9
|
+
value: repo.defaultBranch,
|
|
10
|
+
icon: <Branch size={16} />,
|
|
11
|
+
valueClassName: "eth-dev-git-repository-panel__metric-value--code"
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
label: "Open PRs",
|
|
15
|
+
value: String(repo.openPrCount ?? 0),
|
|
16
|
+
icon: <PullRequest size={16} />
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
label: "Commits",
|
|
20
|
+
value: String(repo.commitCount ?? 0),
|
|
21
|
+
icon: <Commit size={16} />
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
label: "Last commit",
|
|
25
|
+
value: repo.lastCommitAt ?? "No activity",
|
|
26
|
+
icon: <Time size={16} />,
|
|
27
|
+
valueClassName: "eth-dev-git-repository-panel__metric-value--code"
|
|
28
|
+
}
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<section
|
|
33
|
+
{...props}
|
|
34
|
+
aria-label={props["aria-label"] ?? `${repo.name} repository summary`}
|
|
35
|
+
className={`eth-dev-git-repository-panel ${className ?? ""}`}
|
|
36
|
+
data-eth-component="GitRepositoryPanel"
|
|
37
|
+
>
|
|
38
|
+
<header className="eth-dev-git-repository-panel__header">
|
|
39
|
+
<div className="eth-dev-git-repository-panel__identity">
|
|
40
|
+
<span className="eth-dev-git-repository-panel__icon" aria-hidden="true">
|
|
41
|
+
<GitRepo size={20} />
|
|
42
|
+
</span>
|
|
43
|
+
<div className="eth-dev-git-repository-panel__heading">
|
|
44
|
+
<p>Repository</p>
|
|
45
|
+
<h3>{repo.name}</h3>
|
|
46
|
+
<a className="eth-dev-git-repository-panel__url" href={repo.url} title={repo.url}>
|
|
47
|
+
{repo.url}
|
|
48
|
+
</a>
|
|
49
|
+
</div>
|
|
50
|
+
</div>
|
|
51
|
+
<a
|
|
52
|
+
className="eth-dev-git-repository-panel__action"
|
|
53
|
+
href={repo.url}
|
|
54
|
+
aria-label={`Open ${repo.name} repository`}
|
|
55
|
+
>
|
|
56
|
+
<span>Open repository</span>
|
|
57
|
+
<Launch size={16} aria-hidden="true" />
|
|
58
|
+
</a>
|
|
59
|
+
</header>
|
|
60
|
+
<dl className="eth-dev-git-repository-panel__metrics" aria-label="Repository metrics">
|
|
61
|
+
{summaryItems.map((item) => (
|
|
62
|
+
<div key={item.label} className="eth-dev-git-repository-panel__metric">
|
|
63
|
+
<dt>
|
|
64
|
+
<span aria-hidden="true">{item.icon}</span>
|
|
65
|
+
{item.label}
|
|
66
|
+
</dt>
|
|
67
|
+
<dd className={item.valueClassName}>{item.value}</dd>
|
|
68
|
+
</div>
|
|
69
|
+
))}
|
|
70
|
+
</dl>
|
|
71
|
+
</section>
|
|
72
|
+
);
|
|
73
|
+
}
|