@tangle-network/ui 11.16.0 → 11.16.2
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/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# @tangle-network/ui
|
|
2
2
|
|
|
3
|
+
## 11.16.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 74e6dfb: `CommandPreview` output regions are keyboard-focusable and labelled (stdout, stderr, error), so a long output that scrolls inside the block can be scrolled without a pointer (WCAG 2.1.1; axe `scrollable-region-focusable`).
|
|
8
|
+
|
|
9
|
+
## 11.16.1
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- c59ed11: `CommandPreview` prints a failed command's text once when the producer persisted it as both the output and the error.
|
|
14
|
+
|
|
15
|
+
It also reads a JSON string as a `{ stdout, stderr, exitCode }` envelope only when every key is an envelope key, so a command whose own output is JSON with a `stdout` field is shown as printed, and an `error` status turns the exit badge red even when the retained exit code is 0.
|
|
16
|
+
|
|
3
17
|
## 11.16.0
|
|
4
18
|
|
|
5
19
|
### Minor Changes
|
|
@@ -67,6 +67,20 @@ function fromObject(obj) {
|
|
|
67
67
|
exitCode: finiteNumber(obj.exitCode ?? obj.exit_code ?? obj.code)
|
|
68
68
|
};
|
|
69
69
|
}
|
|
70
|
+
/** The keys a serialized command result carries, and nothing else. */
|
|
71
|
+
const ENVELOPE_KEYS = /* @__PURE__ */ new Set([
|
|
72
|
+
"stdout",
|
|
73
|
+
"stderr",
|
|
74
|
+
"output",
|
|
75
|
+
"exitCode",
|
|
76
|
+
"exit_code",
|
|
77
|
+
"code",
|
|
78
|
+
"signal",
|
|
79
|
+
"durationMs",
|
|
80
|
+
"duration_ms",
|
|
81
|
+
"timedOut",
|
|
82
|
+
"timed_out"
|
|
83
|
+
]);
|
|
70
84
|
/** A string that is a serialized `{ stdout, stderr, exitCode }` record. */
|
|
71
85
|
function parseStructured(text) {
|
|
72
86
|
const trimmed = text.trimStart();
|
|
@@ -76,6 +90,7 @@ function parseStructured(text) {
|
|
|
76
90
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return;
|
|
77
91
|
const obj = parsed;
|
|
78
92
|
if (!("stdout" in obj) && !("stderr" in obj)) return void 0;
|
|
93
|
+
if (!Object.keys(obj).every((key) => ENVELOPE_KEYS.has(key))) return;
|
|
79
94
|
return fromObject(obj);
|
|
80
95
|
} catch {
|
|
81
96
|
return;
|
|
@@ -91,7 +106,7 @@ function ExitBadge({ exitCode, status }) {
|
|
|
91
106
|
"aria-hidden": "true"
|
|
92
107
|
}), "running"]
|
|
93
108
|
});
|
|
94
|
-
const failed = exitCode !== void 0
|
|
109
|
+
const failed = status === "error" || exitCode !== void 0 && exitCode !== 0;
|
|
95
110
|
const label = exitCode !== void 0 ? `exit ${exitCode}` : failed ? "error" : null;
|
|
96
111
|
if (label === null) return null;
|
|
97
112
|
return /* @__PURE__ */ jsx("span", {
|
|
@@ -113,7 +128,8 @@ const CommandPreview = memo(({ part, defaultExpanded = false, className }) => {
|
|
|
113
128
|
const command = commandOf(part.state.input);
|
|
114
129
|
const { status } = part.state;
|
|
115
130
|
const output = extractCommandOutput(part.state.output);
|
|
116
|
-
const
|
|
131
|
+
const normalize = (text) => text.replace(/\s+/g, " ").trim();
|
|
132
|
+
const errorText = part.state.error !== void 0 && output.stdout.length > 0 && normalize(part.state.error) === normalize(output.stdout) ? void 0 : part.state.error;
|
|
117
133
|
const hasBody = output.stdout.length > 0 || output.stderr.length > 0 || errorText !== void 0 && errorText.length > 0;
|
|
118
134
|
const promptLine = /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
119
135
|
/* @__PURE__ */ jsx("span", {
|
|
@@ -155,16 +171,21 @@ const CommandPreview = memo(({ part, defaultExpanded = false, className }) => {
|
|
|
155
171
|
className: "border-t border-white/10",
|
|
156
172
|
children: [
|
|
157
173
|
output.stdout ? /* @__PURE__ */ jsx("pre", {
|
|
158
|
-
|
|
174
|
+
tabIndex: 0,
|
|
175
|
+
"aria-label": "stdout",
|
|
176
|
+
className: cn("max-h-80 overflow-auto whitespace-pre-wrap break-all px-3 py-2 leading-relaxed", focusRing),
|
|
159
177
|
children: output.stdout
|
|
160
178
|
}) : null,
|
|
161
179
|
output.stderr ? /* @__PURE__ */ jsx("pre", {
|
|
180
|
+
tabIndex: 0,
|
|
162
181
|
"aria-label": "stderr",
|
|
163
|
-
className: "max-h-80 overflow-auto whitespace-pre-wrap break-all border-t border-white/10 px-3 py-2 leading-relaxed text-[var(--surface-danger-text)]",
|
|
182
|
+
className: cn("max-h-80 overflow-auto whitespace-pre-wrap break-all border-t border-white/10 px-3 py-2 leading-relaxed text-[var(--surface-danger-text)]", focusRing),
|
|
164
183
|
children: output.stderr
|
|
165
184
|
}) : null,
|
|
166
185
|
errorText ? /* @__PURE__ */ jsx("pre", {
|
|
167
|
-
|
|
186
|
+
tabIndex: 0,
|
|
187
|
+
"aria-label": "error",
|
|
188
|
+
className: cn("max-h-80 overflow-auto whitespace-pre-wrap break-all border-t border-white/10 px-3 py-2 leading-relaxed text-[var(--surface-danger-text)]", focusRing),
|
|
168
189
|
children: errorText
|
|
169
190
|
}) : null
|
|
170
191
|
]
|
package/package.json
CHANGED
|
@@ -79,6 +79,21 @@ function fromObject(obj: Record<string, unknown>): CommandOutput {
|
|
|
79
79
|
};
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
+
/** The keys a serialized command result carries, and nothing else. */
|
|
83
|
+
const ENVELOPE_KEYS = new Set([
|
|
84
|
+
"stdout",
|
|
85
|
+
"stderr",
|
|
86
|
+
"output",
|
|
87
|
+
"exitCode",
|
|
88
|
+
"exit_code",
|
|
89
|
+
"code",
|
|
90
|
+
"signal",
|
|
91
|
+
"durationMs",
|
|
92
|
+
"duration_ms",
|
|
93
|
+
"timedOut",
|
|
94
|
+
"timed_out",
|
|
95
|
+
]);
|
|
96
|
+
|
|
82
97
|
/** A string that is a serialized `{ stdout, stderr, exitCode }` record. */
|
|
83
98
|
function parseStructured(text: string): CommandOutput | undefined {
|
|
84
99
|
const trimmed = text.trimStart();
|
|
@@ -89,7 +104,13 @@ function parseStructured(text: string): CommandOutput | undefined {
|
|
|
89
104
|
return undefined;
|
|
90
105
|
}
|
|
91
106
|
const obj = parsed as Record<string, unknown>;
|
|
107
|
+
// Only an unambiguous envelope: a stream key, and nothing but envelope keys.
|
|
108
|
+
// A command whose own stdout is JSON that happens to have a `stdout` field
|
|
109
|
+
// (`{"stdout":"v","result":"ok"}`) is shown as the text it printed.
|
|
92
110
|
if (!("stdout" in obj) && !("stderr" in obj)) return undefined;
|
|
111
|
+
if (!Object.keys(obj).every((key) => ENVELOPE_KEYS.has(key))) {
|
|
112
|
+
return undefined;
|
|
113
|
+
}
|
|
93
114
|
return fromObject(obj);
|
|
94
115
|
} catch {
|
|
95
116
|
return undefined;
|
|
@@ -113,7 +134,9 @@ function ExitBadge({
|
|
|
113
134
|
</span>
|
|
114
135
|
);
|
|
115
136
|
}
|
|
116
|
-
|
|
137
|
+
// The tool's own error status wins: a runner can fail after the shell exited
|
|
138
|
+
// 0, and a green "exit 0" beside that error would contradict it.
|
|
139
|
+
const failed = status === "error" || (exitCode !== undefined && exitCode !== 0);
|
|
117
140
|
const label =
|
|
118
141
|
exitCode !== undefined ? `exit ${exitCode}` : failed ? "error" : null;
|
|
119
142
|
if (label === null) return null;
|
|
@@ -146,7 +169,16 @@ export const CommandPreview = memo(
|
|
|
146
169
|
const command = commandOf(part.state.input);
|
|
147
170
|
const { status } = part.state;
|
|
148
171
|
const output = extractCommandOutput(part.state.output);
|
|
149
|
-
|
|
172
|
+
// A failed call often persists the same text twice — as its output and as
|
|
173
|
+
// its error. Print it once. Only an exact repeat (ignoring whitespace) is
|
|
174
|
+
// folded: an error that merely CONTAINS the output, or the reverse, is a
|
|
175
|
+
// distinct message and keeps its own region.
|
|
176
|
+
const normalize = (text: string) => text.replace(/\s+/g, " ").trim();
|
|
177
|
+
const errorRepeats =
|
|
178
|
+
part.state.error !== undefined &&
|
|
179
|
+
output.stdout.length > 0 &&
|
|
180
|
+
normalize(part.state.error) === normalize(output.stdout);
|
|
181
|
+
const errorText = errorRepeats ? undefined : part.state.error;
|
|
150
182
|
const hasBody =
|
|
151
183
|
output.stdout.length > 0 ||
|
|
152
184
|
output.stderr.length > 0 ||
|
|
@@ -203,20 +235,38 @@ export const CommandPreview = memo(
|
|
|
203
235
|
{expanded && hasBody ? (
|
|
204
236
|
<div id={bodyId} className="border-t border-white/10">
|
|
205
237
|
{output.stdout ? (
|
|
206
|
-
<pre
|
|
238
|
+
<pre
|
|
239
|
+
tabIndex={0}
|
|
240
|
+
aria-label="stdout"
|
|
241
|
+
className={cn(
|
|
242
|
+
"max-h-80 overflow-auto whitespace-pre-wrap break-all px-3 py-2 leading-relaxed",
|
|
243
|
+
focusRing,
|
|
244
|
+
)}
|
|
245
|
+
>
|
|
207
246
|
{output.stdout}
|
|
208
247
|
</pre>
|
|
209
248
|
) : null}
|
|
210
249
|
{output.stderr ? (
|
|
211
250
|
<pre
|
|
251
|
+
tabIndex={0}
|
|
212
252
|
aria-label="stderr"
|
|
213
|
-
className=
|
|
253
|
+
className={cn(
|
|
254
|
+
"max-h-80 overflow-auto whitespace-pre-wrap break-all border-t border-white/10 px-3 py-2 leading-relaxed text-[var(--surface-danger-text)]",
|
|
255
|
+
focusRing,
|
|
256
|
+
)}
|
|
214
257
|
>
|
|
215
258
|
{output.stderr}
|
|
216
259
|
</pre>
|
|
217
260
|
) : null}
|
|
218
261
|
{errorText ? (
|
|
219
|
-
<pre
|
|
262
|
+
<pre
|
|
263
|
+
tabIndex={0}
|
|
264
|
+
aria-label="error"
|
|
265
|
+
className={cn(
|
|
266
|
+
"max-h-80 overflow-auto whitespace-pre-wrap break-all border-t border-white/10 px-3 py-2 leading-relaxed text-[var(--surface-danger-text)]",
|
|
267
|
+
focusRing,
|
|
268
|
+
)}
|
|
269
|
+
>
|
|
220
270
|
{errorText}
|
|
221
271
|
</pre>
|
|
222
272
|
) : null}
|