@tangle-network/ui 11.16.7 → 11.16.9

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,17 @@
1
1
  # @tangle-network/ui
2
2
 
3
+ ## 11.16.9
4
+
5
+ ### Patch Changes
6
+
7
+ - 272704e: On a phone, terminal output keeps its own lines and scrolls sideways inside the block, so listing columns stay aligned and hashes and URLs are not split mid-token. A closed command shows at most two lines at every width.
8
+
9
+ ## 11.16.8
10
+
11
+ ### Patch Changes
12
+
13
+ - c8ab512: A terminal command whose tool reported an error with nothing recorded about the command shows its "tool reported error" badge in the warning tone and sans type, so it reads as a status rather than metadata. Red stays reserved for a nonzero exit, a timeout or a signal.
14
+
3
15
  ## 11.16.7
4
16
 
5
17
  ### Patch Changes
@@ -19,6 +19,10 @@ export interface CommandPreviewProps {
19
19
  * dark terminal. It scopes the dark token set onto itself with
20
20
  * `data-theme="dark"`, so stderr, the badges and the muted prompt keep the
21
21
  * contrast they were designed with on a dark ground, in a light console too.
22
+ *
23
+ * On a phone the output keeps its own lines and scrolls sideways inside the
24
+ * block: wrapped output breaks the columns of a listing and splits hashes
25
+ * and URLs mid-token. The command still wraps between its tokens.
22
26
  */
23
27
  export declare const CommandPreview: import("react").MemoExoticComponent<({ part, defaultExpanded, className }: CommandPreviewProps) => import("react").JSX.Element>;
24
28
  //#endregion
@@ -103,7 +103,7 @@ function parseStructured(text) {
103
103
  /** Exit status as a badge. Red only where the command itself is known to have
104
104
  * failed: a nonzero exit, a timeout, or a signal. A tool that reported an
105
105
  * error with nothing recorded about the command (no exit code, or exit 0
106
- * from a runner that failed afterwards) gets a neutral badge; its error text
106
+ * from a runner that failed afterwards) gets an amber badge; its error text
107
107
  * prints below. A clean run stays quiet green. */
108
108
  function ExitBadge({ output, status }) {
109
109
  if (status === "running" || status === "pending") return /* @__PURE__ */ jsxs("span", {
@@ -127,14 +127,14 @@ function ExitBadge({ output, status }) {
127
127
  tone = "danger";
128
128
  } else if (status === "error") {
129
129
  label = exitCode === 0 ? "exit 0 · tool reported error" : "tool reported error";
130
- tone = "neutral";
130
+ tone = "warning";
131
131
  } else {
132
132
  label = exitCode === 0 ? "exit 0" : null;
133
133
  tone = "success";
134
134
  }
135
135
  if (label === null) return null;
136
136
  return /* @__PURE__ */ jsx("span", {
137
- className: cn("inline-flex shrink-0 items-center rounded-full border px-1.5 py-px text-[11px] font-medium tabular-nums", tone === "danger" && "border-transparent bg-[var(--surface-danger-bg)] text-[var(--surface-danger-text)]", tone === "success" && "border-transparent bg-[var(--surface-success-bg)] text-[var(--surface-success-text)]", tone === "neutral" && "border-[var(--surface-neutral-border)] bg-[var(--surface-neutral-bg)] text-[var(--surface-neutral-text)]"),
137
+ className: cn("inline-flex shrink-0 items-center rounded-full border px-1.5 py-px font-sans text-[11px] font-medium tabular-nums", tone === "danger" && "border-transparent bg-[var(--surface-danger-bg)] text-[var(--surface-danger-text)]", tone === "success" && "border-transparent bg-[var(--surface-success-bg)] text-[var(--surface-success-text)]", tone === "warning" && "border-[var(--surface-warning-border)] bg-[var(--surface-warning-bg)] text-[var(--surface-warning-text)]"),
138
138
  children: label
139
139
  });
140
140
  }
@@ -162,6 +162,10 @@ function CommandText({ command, all }) {
162
162
  * dark terminal. It scopes the dark token set onto itself with
163
163
  * `data-theme="dark"`, so stderr, the badges and the muted prompt keep the
164
164
  * contrast they were designed with on a dark ground, in a light console too.
165
+ *
166
+ * On a phone the output keeps its own lines and scrolls sideways inside the
167
+ * block: wrapped output breaks the columns of a listing and splits hashes
168
+ * and URLs mid-token. The command still wraps between its tokens.
165
169
  */
166
170
  const CommandPreview = memo(({ part, defaultExpanded = false, className }) => {
167
171
  const [expanded, setExpanded] = useState(defaultExpanded);
@@ -179,7 +183,7 @@ const CommandPreview = memo(({ part, defaultExpanded = false, className }) => {
179
183
  children: "$"
180
184
  }),
181
185
  /* @__PURE__ */ jsx("code", {
182
- className: cn("min-w-0 flex-1 whitespace-pre-wrap [overflow-wrap:anywhere]", expanded ? "" : "line-clamp-2"),
186
+ className: cn("min-w-0 flex-1 whitespace-pre-wrap [overflow-wrap:anywhere]", expanded ? "" : "max-h-8 overflow-hidden"),
183
187
  children: /* @__PURE__ */ jsx(CommandText, {
184
188
  command,
185
189
  all: expanded
@@ -222,21 +226,21 @@ const CommandPreview = memo(({ part, defaultExpanded = false, className }) => {
222
226
  tabIndex: 0,
223
227
  role: "region",
224
228
  "aria-label": "stdout",
225
- className: cn("max-h-80 overflow-auto whitespace-pre-wrap [overflow-wrap:anywhere] px-3 py-2 leading-relaxed", focusRing),
229
+ className: cn("max-h-80 overflow-auto whitespace-pre-wrap [overflow-wrap:anywhere] max-sm:whitespace-pre max-sm:[overflow-wrap:normal] px-3 py-2 leading-relaxed", focusRing),
226
230
  children: output.stdout
227
231
  }) : null,
228
232
  output.stderr ? /* @__PURE__ */ jsx("pre", {
229
233
  tabIndex: 0,
230
234
  role: "region",
231
235
  "aria-label": "stderr",
232
- className: cn("max-h-80 overflow-auto whitespace-pre-wrap [overflow-wrap:anywhere] border-t border-white/10 px-3 py-2 leading-relaxed text-[var(--surface-danger-text)]", focusRing),
236
+ className: cn("max-h-80 overflow-auto whitespace-pre-wrap [overflow-wrap:anywhere] max-sm:whitespace-pre max-sm:[overflow-wrap:normal] border-t border-white/10 px-3 py-2 leading-relaxed text-[var(--surface-danger-text)]", focusRing),
233
237
  children: output.stderr
234
238
  }) : null,
235
239
  errorText ? /* @__PURE__ */ jsx("pre", {
236
240
  tabIndex: 0,
237
241
  role: "region",
238
242
  "aria-label": "error",
239
- className: cn("max-h-80 overflow-auto whitespace-pre-wrap [overflow-wrap:anywhere] border-t border-white/10 px-3 py-2 leading-relaxed text-[var(--surface-danger-text)]", focusRing),
243
+ className: cn("max-h-80 overflow-auto whitespace-pre-wrap [overflow-wrap:anywhere] max-sm:whitespace-pre max-sm:[overflow-wrap:normal] border-t border-white/10 px-3 py-2 leading-relaxed text-[var(--surface-danger-text)]", focusRing),
240
244
  children: errorText
241
245
  }) : null
242
246
  ]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/ui",
3
- "version": "11.16.7",
3
+ "version": "11.16.9",
4
4
  "description": "Generic React UI components for Tangle products — primitives, chat, run, files, editor, markdown.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -13,10 +13,10 @@ function part(state: Partial<ToolPart["state"]>): ToolPart {
13
13
  }
14
14
 
15
15
  describe("CommandPreview", () => {
16
- it("shows a neutral badge, not a red exit, when a tool errored without an exit code", () => {
16
+ it("shows an amber badge, not a red exit, when a tool errored without an exit code", () => {
17
17
  render(<CommandPreview part={part({ status: "error", output: "warning: x" })} />);
18
18
  const badge = screen.getByText("tool reported error");
19
- expect(badge.className).toContain("surface-neutral");
19
+ expect(badge.className).toContain("surface-warning");
20
20
  expect(badge.className).not.toContain("surface-danger");
21
21
  expect(screen.queryByText("error")).toBeNull();
22
22
  });
@@ -52,13 +52,13 @@ describe("CommandPreview", () => {
52
52
  expect(screen.getByText("timed out").className).toContain("surface-danger");
53
53
  });
54
54
 
55
- it("keeps a runner error after a clean exit neutral", () => {
55
+ it("keeps a runner error after a clean exit amber, not red", () => {
56
56
  render(
57
57
  <CommandPreview
58
58
  part={part({ status: "error", output: { stdout: "ok", stderr: "", exitCode: 0 } })}
59
59
  />,
60
60
  );
61
- expect(screen.getByText("exit 0 · tool reported error").className).toContain("surface-neutral");
61
+ expect(screen.getByText("exit 0 · tool reported error").className).toContain("surface-warning");
62
62
  });
63
63
 
64
64
  it("is a quiet row until opened, then a dark terminal", () => {
@@ -128,7 +128,7 @@ function parseStructured(text: string): CommandOutput | undefined {
128
128
  /** Exit status as a badge. Red only where the command itself is known to have
129
129
  * failed: a nonzero exit, a timeout, or a signal. A tool that reported an
130
130
  * error with nothing recorded about the command (no exit code, or exit 0
131
- * from a runner that failed afterwards) gets a neutral badge; its error text
131
+ * from a runner that failed afterwards) gets an amber badge; its error text
132
132
  * prints below. A clean run stays quiet green. */
133
133
  function ExitBadge({
134
134
  output,
@@ -147,7 +147,7 @@ function ExitBadge({
147
147
  }
148
148
  const { exitCode, timedOut, signal } = output;
149
149
  let label: string | null;
150
- let tone: "danger" | "success" | "neutral";
150
+ let tone: "danger" | "success" | "warning";
151
151
  if (timedOut) {
152
152
  label = "timed out";
153
153
  tone = "danger";
@@ -161,7 +161,7 @@ function ExitBadge({
161
161
  // Say what is known: the tool reported an error. Nothing recorded says
162
162
  // the command itself failed.
163
163
  label = exitCode === 0 ? "exit 0 · tool reported error" : "tool reported error";
164
- tone = "neutral";
164
+ tone = "warning";
165
165
  } else {
166
166
  label = exitCode === 0 ? "exit 0" : null;
167
167
  tone = "success";
@@ -171,13 +171,15 @@ function ExitBadge({
171
171
  <span
172
172
  className={cn(
173
173
  // Every badge has a border, so mixed rows keep one height.
174
- "inline-flex shrink-0 items-center rounded-full border px-1.5 py-px text-[11px] font-medium tabular-nums",
174
+ "inline-flex shrink-0 items-center rounded-full border px-1.5 py-px font-sans text-[11px] font-medium tabular-nums",
175
175
  tone === "danger" &&
176
176
  "border-transparent bg-[var(--surface-danger-bg)] text-[var(--surface-danger-text)]",
177
177
  tone === "success" &&
178
178
  "border-transparent bg-[var(--surface-success-bg)] text-[var(--surface-success-text)]",
179
- tone === "neutral" &&
180
- "border-[var(--surface-neutral-border)] bg-[var(--surface-neutral-bg)] text-[var(--surface-neutral-text)]",
179
+ // A reported error with nothing recorded about the command deserves
180
+ // attention, but it is not a failed command: amber, not red.
181
+ tone === "warning" &&
182
+ "border-[var(--surface-warning-border)] bg-[var(--surface-warning-bg)] text-[var(--surface-warning-text)]",
181
183
  )}
182
184
  >
183
185
  {label}
@@ -225,6 +227,10 @@ function CommandText({ command, all }: { command: string; all: boolean }) {
225
227
  * dark terminal. It scopes the dark token set onto itself with
226
228
  * `data-theme="dark"`, so stderr, the badges and the muted prompt keep the
227
229
  * contrast they were designed with on a dark ground, in a light console too.
230
+ *
231
+ * On a phone the output keeps its own lines and scrolls sideways inside the
232
+ * block: wrapped output breaks the columns of a listing and splits hashes
233
+ * and URLs mid-token. The command still wraps between its tokens.
228
234
  */
229
235
  export const CommandPreview = memo(
230
236
  ({ part, defaultExpanded = false, className }: CommandPreviewProps) => {
@@ -256,7 +262,10 @@ export const CommandPreview = memo(
256
262
  <code
257
263
  className={cn(
258
264
  "min-w-0 flex-1 whitespace-pre-wrap [overflow-wrap:anywhere]",
259
- expanded ? "" : "line-clamp-2",
265
+ // Closed, two lines of the command. A height cap, not
266
+ // line-clamp: each token is an inline box, which line-clamp
267
+ // does not count, so a phone showed three lines and more.
268
+ expanded ? "" : "max-h-8 overflow-hidden",
260
269
  )}
261
270
  >
262
271
  <CommandText command={command} all={expanded} />
@@ -317,7 +326,7 @@ export const CommandPreview = memo(
317
326
  role="region"
318
327
  aria-label="stdout"
319
328
  className={cn(
320
- "max-h-80 overflow-auto whitespace-pre-wrap [overflow-wrap:anywhere] px-3 py-2 leading-relaxed",
329
+ "max-h-80 overflow-auto whitespace-pre-wrap [overflow-wrap:anywhere] max-sm:whitespace-pre max-sm:[overflow-wrap:normal] px-3 py-2 leading-relaxed",
321
330
  focusRing,
322
331
  )}
323
332
  >
@@ -330,7 +339,7 @@ export const CommandPreview = memo(
330
339
  role="region"
331
340
  aria-label="stderr"
332
341
  className={cn(
333
- "max-h-80 overflow-auto whitespace-pre-wrap [overflow-wrap:anywhere] border-t border-white/10 px-3 py-2 leading-relaxed text-[var(--surface-danger-text)]",
342
+ "max-h-80 overflow-auto whitespace-pre-wrap [overflow-wrap:anywhere] max-sm:whitespace-pre max-sm:[overflow-wrap:normal] border-t border-white/10 px-3 py-2 leading-relaxed text-[var(--surface-danger-text)]",
334
343
  focusRing,
335
344
  )}
336
345
  >
@@ -343,7 +352,7 @@ export const CommandPreview = memo(
343
352
  role="region"
344
353
  aria-label="error"
345
354
  className={cn(
346
- "max-h-80 overflow-auto whitespace-pre-wrap [overflow-wrap:anywhere] border-t border-white/10 px-3 py-2 leading-relaxed text-[var(--surface-danger-text)]",
355
+ "max-h-80 overflow-auto whitespace-pre-wrap [overflow-wrap:anywhere] max-sm:whitespace-pre max-sm:[overflow-wrap:normal] border-t border-white/10 px-3 py-2 leading-relaxed text-[var(--surface-danger-text)]",
347
356
  focusRing,
348
357
  )}
349
358
  >