@tangle-network/ui 11.32.2 → 11.34.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 (60) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/dist/editor/document-editor-pane.d.ts +7 -1
  3. package/dist/editor/document-editor-pane.js +27 -12
  4. package/dist/editor/markdown-document-editor.js +30 -12
  5. package/dist/editor/source-markdown-editor.js +256 -0
  6. package/dist/editor/source-markdown.js +495 -0
  7. package/dist/files/file-artifact-pane.d.ts +2 -0
  8. package/dist/files/file-artifact-pane.js +1 -0
  9. package/dist/index.d.ts +3 -2
  10. package/dist/index.js +3 -2
  11. package/dist/lib/control-presentation.js +2 -1
  12. package/dist/lib/focus.d.ts +5 -1
  13. package/dist/lib/focus.js +6 -1
  14. package/dist/lib/interaction.d.ts +41 -0
  15. package/dist/lib/interaction.js +42 -0
  16. package/dist/markdown/markdown-table.js +1 -1
  17. package/dist/primitives/button.js +19 -18
  18. package/dist/primitives/card.d.ts +14 -1
  19. package/dist/primitives/card.js +15 -5
  20. package/dist/primitives/dropdown-menu.js +7 -6
  21. package/dist/primitives/segmented-control.d.ts +5 -1
  22. package/dist/primitives/segmented-control.js +12 -5
  23. package/dist/primitives/select.js +4 -3
  24. package/dist/primitives/tabs.js +4 -3
  25. package/dist/run/inline-thinking-item.d.ts +7 -1
  26. package/dist/run/inline-thinking-item.js +6 -5
  27. package/dist/utils/format.d.ts +6 -0
  28. package/dist/utils/format.js +9 -1
  29. package/dist/utils.d.ts +3 -2
  30. package/dist/utils.js +3 -2
  31. package/package.json +2 -2
  32. package/src/editor/document-editor-pane.tsx +54 -16
  33. package/src/editor/markdown-document-editor.tsx +33 -18
  34. package/src/editor/source-markdown-editor.test.tsx +156 -0
  35. package/src/editor/source-markdown-editor.ts +296 -0
  36. package/src/editor/source-markdown.fixtures.ts +90 -0
  37. package/src/editor/source-markdown.test.ts +235 -0
  38. package/src/editor/source-markdown.ts +627 -0
  39. package/src/files/file-artifact-pane.test.tsx +38 -3
  40. package/src/files/file-artifact-pane.tsx +3 -0
  41. package/src/lib/control-presentation.ts +10 -4
  42. package/src/lib/focus.ts +7 -1
  43. package/src/lib/interaction.ts +64 -0
  44. package/src/markdown/markdown-table.test.tsx +9 -0
  45. package/src/markdown/markdown-table.tsx +5 -1
  46. package/src/primitives/button.tsx +29 -10
  47. package/src/primitives/card.tsx +23 -6
  48. package/src/primitives/control-presentation.test.tsx +6 -3
  49. package/src/primitives/date-picker.test.tsx +1 -1
  50. package/src/primitives/dropdown-menu.tsx +15 -10
  51. package/src/primitives/interaction-states.test.tsx +285 -0
  52. package/src/primitives/segmented-control.test.tsx +4 -2
  53. package/src/primitives/segmented-control.tsx +13 -14
  54. package/src/primitives/select.tsx +11 -5
  55. package/src/primitives/tabs.tsx +7 -3
  56. package/src/run/inline-thinking-item.test.tsx +29 -0
  57. package/src/run/inline-thinking-item.tsx +11 -4
  58. package/src/utils/format.test.ts +16 -1
  59. package/src/utils/format.ts +17 -0
  60. package/src/utils/index.ts +10 -1
@@ -0,0 +1,41 @@
1
+ //#region src/lib/interaction.d.ts
2
+ /**
3
+ * The interaction states every control shares: one palette in five shapes, all
4
+ * read from `@tangle-network/brand`'s `--ix-*` tokens (tokens.css), which derive
5
+ * from the focus ring hue `--hsl-ring`. So hover, press, after-click and
6
+ * selected are the same green in Hospitality and the same indigo on the spine
7
+ * as the keyboard focus ring.
8
+ *
9
+ * hover 1px edge at .45 plus a 4px halo at .16 (`--ix-hover`)
10
+ * press 1px edge at .95 plus a 4px halo at .30 (`--ix-press`)
11
+ * after a control focused by a mouse click keeps a soft ring (`--ix-after`)
12
+ * selected a tint plus a 1px inset ring at .85 (`--ix-sel-bg`, `--ix-sel-ring`)
13
+ *
14
+ * The keyboard focus-visible glow stays exactly as `focusRing` / `focusField`
15
+ * (lib/focus.ts) draw it. Tailwind composes `--tw-ring-shadow` with
16
+ * `--tw-shadow`, so these shadows coexist with that ring, and
17
+ * `focus-visible:shadow-none` drops the hover glow while the keyboard ring shows.
18
+ *
19
+ * Every class is written out in full because Tailwind reads class names
20
+ * statically.
21
+ */
22
+ /** Bordered or filled control: green edge on hover, deeper on press, a persistent soft ring after a mouse click. */
23
+ export declare const ixControl: string;
24
+ /** The after-click part of `ixControl` alone, for a field that opens a menu (SelectTrigger). */
25
+ export declare const ixControlAfter = "focus:not-focus-visible:border-[var(--ix-hover-border)] focus:not-focus-visible:shadow-[var(--ix-after)]";
26
+ /** Ghost, icon, link and bare controls: a wash, never a glow (too noisy in sidebars and toolbars). */
27
+ export declare const ixControlSoft = "hover:bg-[var(--ix-hover-bg)] active:bg-[var(--ix-sel-bg)]";
28
+ /**
29
+ * Field: its own well and a visible border; hover replaces `focusField`'s
30
+ * neutral `--border-strong`. The well is the `--well` context variable, which a
31
+ * card re-declares for its descendants, so a field never takes its parent's
32
+ * colour. `--field-surface` still lets a context such as Toolbar pick the fill.
33
+ */
34
+ export declare const ixField: string;
35
+ /** Selected / on / current: tint plus a 1px inset green ring. */
36
+ export declare const ixSelected: string;
37
+ /** Underline tab: 2px green underline plus a faint tint, never a box; a faint underline on hover. */
38
+ export declare const ixTabUnderline: string;
39
+ /** List row / menu item: wash plus inset edge on hover or keyboard highlight; checked = selected. */
40
+ export declare const ixRow: string;
41
+ //#endregion
@@ -0,0 +1,42 @@
1
+ //#region src/lib/interaction.ts
2
+ /**
3
+ * The interaction states every control shares: one palette in five shapes, all
4
+ * read from `@tangle-network/brand`'s `--ix-*` tokens (tokens.css), which derive
5
+ * from the focus ring hue `--hsl-ring`. So hover, press, after-click and
6
+ * selected are the same green in Hospitality and the same indigo on the spine
7
+ * as the keyboard focus ring.
8
+ *
9
+ * hover 1px edge at .45 plus a 4px halo at .16 (`--ix-hover`)
10
+ * press 1px edge at .95 plus a 4px halo at .30 (`--ix-press`)
11
+ * after a control focused by a mouse click keeps a soft ring (`--ix-after`)
12
+ * selected a tint plus a 1px inset ring at .85 (`--ix-sel-bg`, `--ix-sel-ring`)
13
+ *
14
+ * The keyboard focus-visible glow stays exactly as `focusRing` / `focusField`
15
+ * (lib/focus.ts) draw it. Tailwind composes `--tw-ring-shadow` with
16
+ * `--tw-shadow`, so these shadows coexist with that ring, and
17
+ * `focus-visible:shadow-none` drops the hover glow while the keyboard ring shows.
18
+ *
19
+ * Every class is written out in full because Tailwind reads class names
20
+ * statically.
21
+ */
22
+ /** Bordered or filled control: green edge on hover, deeper on press, a persistent soft ring after a mouse click. */
23
+ const ixControl = "hover:border-[var(--ix-hover-border)] hover:shadow-[var(--ix-hover)] focus:not-focus-visible:border-[var(--ix-hover-border)] focus:not-focus-visible:shadow-[var(--ix-after)] active:border-[hsl(var(--hsl-ring))] active:shadow-[var(--ix-press)] focus-visible:shadow-none";
24
+ /** The after-click part of `ixControl` alone, for a field that opens a menu (SelectTrigger). */
25
+ const ixControlAfter = "focus:not-focus-visible:border-[var(--ix-hover-border)] focus:not-focus-visible:shadow-[var(--ix-after)]";
26
+ /** Ghost, icon, link and bare controls: a wash, never a glow (too noisy in sidebars and toolbars). */
27
+ const ixControlSoft = "hover:bg-[var(--ix-hover-bg)] active:bg-[var(--ix-sel-bg)]";
28
+ /**
29
+ * Field: its own well and a visible border; hover replaces `focusField`'s
30
+ * neutral `--border-strong`. The well is the `--well` context variable, which a
31
+ * card re-declares for its descendants, so a field never takes its parent's
32
+ * colour. `--field-surface` still lets a context such as Toolbar pick the fill.
33
+ */
34
+ const ixField = "bg-[var(--field-surface,var(--well))] border-[var(--field-border)] shadow-[var(--shadow-well)] hover:border-[var(--ix-hover-border)] hover:shadow-[var(--shadow-well),var(--ix-hover)]";
35
+ /** Selected / on / current: tint plus a 1px inset green ring. */
36
+ const ixSelected = "aria-pressed:bg-[var(--ix-sel-bg)] aria-pressed:text-[var(--accent-text)] aria-pressed:shadow-[inset_0_0_0_1px_var(--ix-sel-ring)] aria-checked:bg-[var(--ix-sel-bg)] aria-checked:text-[var(--accent-text)] aria-checked:shadow-[inset_0_0_0_1px_var(--ix-sel-ring)] aria-[current=page]:bg-[var(--ix-sel-bg)] aria-[current=page]:text-[var(--accent-text)] aria-[current=page]:shadow-[inset_0_0_0_1px_var(--ix-sel-ring)] data-[state=on]:bg-[var(--ix-sel-bg)] data-[state=on]:shadow-[inset_0_0_0_1px_var(--ix-sel-ring)] data-[state=active]:bg-[var(--ix-sel-bg)] data-[state=active]:text-[var(--accent-text)] data-[state=active]:shadow-[inset_0_0_0_1px_var(--ix-sel-ring)]";
37
+ /** Underline tab: 2px green underline plus a faint tint, never a box; a faint underline on hover. */
38
+ const ixTabUnderline = "hover:shadow-[inset_0_-2px_0_hsl(var(--hsl-ring)/0.4)] data-[state=active]:bg-[hsl(var(--hsl-ring)/0.07)] data-[state=active]:text-[var(--accent-text)] data-[state=active]:shadow-[inset_0_-2px_0_var(--ix-sel-ring)] aria-checked:bg-[hsl(var(--hsl-ring)/0.07)] aria-checked:text-[var(--accent-text)] aria-checked:shadow-[inset_0_-2px_0_var(--ix-sel-ring)]";
39
+ /** List row / menu item: wash plus inset edge on hover or keyboard highlight; checked = selected. */
40
+ const ixRow = "hover:bg-[var(--ix-hover-bg)] hover:shadow-[inset_0_0_0_1px_var(--ix-hover-border)] data-[highlighted]:bg-[var(--ix-sel-bg)] data-[highlighted]:shadow-[inset_0_0_0_1px_var(--ix-hover-border)] focus:bg-[var(--ix-sel-bg)] data-[state=checked]:bg-[var(--ix-sel-bg)] data-[state=checked]:shadow-[inset_0_0_0_1px_var(--ix-sel-ring)] active:shadow-[inset_0_0_0_1px_hsl(var(--hsl-ring))]";
41
+ //#endregion
42
+ export { ixControl, ixControlAfter, ixControlSoft, ixField, ixRow, ixSelected, ixTabUnderline };
@@ -113,7 +113,7 @@ function MarkdownTable({ node, className, children, ...props }) {
113
113
  };
114
114
  }, [node]);
115
115
  return /* @__PURE__ */ jsxs("div", {
116
- className: "not-prose my-4 overflow-hidden rounded-lg border border-border bg-card",
116
+ className: "not-prose relative my-4 overflow-hidden rounded-lg border border-border bg-card",
117
117
  children: [/* @__PURE__ */ jsxs("div", {
118
118
  className: "flex items-center justify-between gap-2 border-b border-border px-3 py-1",
119
119
  children: [/* @__PURE__ */ jsx("span", {
@@ -1,3 +1,4 @@
1
+ import { ixControl, ixControlSoft, ixSelected } from "../lib/interaction.js";
1
2
  import { focusRing } from "../lib/focus.js";
2
3
  import { cn } from "../lib/utils.js";
3
4
  import { controlHeight, controlMotion, controlRadius, controlSizes, controlSquare, controlText } from "../lib/control-presentation.js";
@@ -9,13 +10,13 @@ import { jsx, jsxs } from "react/jsx-runtime";
9
10
  const buttonVariants = cva(`inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-lg font-medium transition-[color,background-color,border-color,box-shadow,transform,scale] ${controlMotion} ${focusRing} disabled:pointer-events-none disabled:opacity-50 aria-disabled:cursor-not-allowed aria-disabled:opacity-50 motion-reduce:active:scale-100 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0`, {
10
11
  variants: {
11
12
  variant: {
12
- default: "bg-primary text-primary-foreground hover:bg-primary/90 active:scale-[0.97]",
13
- destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90 active:scale-[0.97]",
14
- outline: "border border-border bg-card hover:bg-muted active:scale-[0.97] text-foreground",
15
- secondary: "bg-muted border border-border text-foreground hover:bg-muted/80 active:scale-[0.97]",
16
- ghost: "hover:bg-muted hover:text-foreground text-muted-foreground border border-transparent",
17
- link: "text-[var(--accent-text)] underline-offset-4 hover:underline",
18
- sandbox: "bg-[var(--btn-primary-bg)] text-[var(--btn-primary-text)] border border-[var(--border-accent)] hover:bg-[var(--btn-primary-hover)] active:scale-[0.97]"
13
+ default: `bg-primary text-primary-foreground hover:bg-primary/90 active:scale-[0.97] ${ixControl}`,
14
+ destructive: `bg-destructive text-destructive-foreground hover:bg-destructive/90 active:scale-[0.97] ${ixControl}`,
15
+ outline: `border border-border bg-card hover:bg-muted active:scale-[0.97] text-foreground ${ixControl}`,
16
+ secondary: `bg-muted border border-border text-foreground hover:bg-muted/80 active:scale-[0.97] ${ixControl}`,
17
+ ghost: `hover:text-foreground text-muted-foreground border border-transparent ${ixControlSoft}`,
18
+ link: `text-[var(--accent-text)] underline-offset-4 hover:underline ${ixControlSoft}`,
19
+ sandbox: `bg-[var(--btn-primary-bg)] text-[var(--btn-primary-text)] border border-[var(--border-accent)] hover:bg-[var(--btn-primary-hover)] active:scale-[0.97] ${ixControl}`
19
20
  },
20
21
  size: {
21
22
  default: `${controlHeight.md} ${controlText.md} ${controlRadius.md} px-4 py-2`,
@@ -34,7 +35,15 @@ const buttonVariants = cva(`inline-flex items-center justify-center gap-2 whites
34
35
  size: "default"
35
36
  }
36
37
  });
37
- const bareButton = `${focusRing} disabled:pointer-events-none disabled:opacity-50 aria-disabled:cursor-not-allowed aria-disabled:opacity-50`;
38
+ const bareButton = `${focusRing} ${ixControlSoft} disabled:pointer-events-none disabled:opacity-50 aria-disabled:cursor-not-allowed aria-disabled:opacity-50`;
39
+ /** One Button render's classes: variant and size, the toggle states, then the caller's. */
40
+ function buttonClassName(variant, size, pressed, className) {
41
+ const toggle = pressed === void 0 ? void 0 : `${ixControl} ${ixSelected}`;
42
+ return variant === "bare" ? cn(bareButton, toggle, className) : cn(buttonVariants({
43
+ variant,
44
+ size
45
+ }), toggle, className);
46
+ }
38
47
  function preventActivation(event) {
39
48
  event.preventDefault();
40
49
  event.stopPropagation();
@@ -61,11 +70,7 @@ const Button = React.forwardRef(({ className, variant, size, asChild = false, lo
61
70
  const child = React.isValidElement(children) ? children : null;
62
71
  const isDisabled = disabled || loading || child?.props.disabled;
63
72
  return /* @__PURE__ */ jsx(Slot, {
64
- className: variant === "bare" ? cn(bareButton, className) : cn(buttonVariants({
65
- variant,
66
- size,
67
- className
68
- })),
73
+ className: buttonClassName(variant, size, props["aria-pressed"], className),
69
74
  ref,
70
75
  ...props,
71
76
  ...isDisabled ? disabledHandlers(props) : {},
@@ -84,11 +89,7 @@ const Button = React.forwardRef(({ className, variant, size, asChild = false, lo
84
89
  });
85
90
  }
86
91
  return /* @__PURE__ */ jsxs("button", {
87
- className: variant === "bare" ? cn(bareButton, className) : cn(buttonVariants({
88
- variant,
89
- size,
90
- className
91
- })),
92
+ className: buttonClassName(variant, size, props["aria-pressed"], className),
92
93
  ref,
93
94
  ...props,
94
95
  disabled: disabled || loading,
@@ -1,9 +1,22 @@
1
1
  import * as React from "react";
2
2
  //#region src/primitives/card.d.ts
3
+ /**
4
+ * A card is one level of the brand surface ladder. It carries
5
+ * `data-slot="card"`, and the brand's surface-context rule (tokens.css)
6
+ * re-declares `--card` and `--well` for everything inside it: a Card or a
7
+ * `bg-card` box inside a card paints the raised level, one inside that the
8
+ * raised-2 level, and a field in any of them takes the well that is the
9
+ * opposite step from its parent. Nesting needs no prop and no app selector.
10
+ */
3
11
  declare const Card: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLDivElement> & {
4
12
  variant?: "default" | "glass" | "sandbox" | "elevated";
5
- /** Decorative border feedback only. Put actions in a native button or link. */
13
+ /** Decorative hover feedback only (the shared green edge). Put actions in a native button or link. */
6
14
  hover?: boolean;
15
+ /**
16
+ * The card is itself the control, such as a card wrapped in a link: it takes
17
+ * the full control states (hover glow, press, the ring after a click).
18
+ */
19
+ interactive?: boolean;
7
20
  } & React.RefAttributes<HTMLDivElement>>;
8
21
  declare const CardHeader: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLDivElement> & React.RefAttributes<HTMLDivElement>>;
9
22
  export interface CardTitleProps extends React.HTMLAttributes<HTMLHeadingElement> {
@@ -1,17 +1,27 @@
1
+ import "../lib/interaction.js";
1
2
  import { cn } from "../lib/utils.js";
2
3
  import { Heading } from "./heading.js";
3
4
  import * as React from "react";
4
5
  import { jsx } from "react/jsx-runtime";
5
6
  //#region src/primitives/card.tsx
6
- const Card = React.forwardRef(({ className, variant = "default", hover = false, ...props }, ref) => {
7
+ /**
8
+ * A card is one level of the brand surface ladder. It carries
9
+ * `data-slot="card"`, and the brand's surface-context rule (tokens.css)
10
+ * re-declares `--card` and `--well` for everything inside it: a Card or a
11
+ * `bg-card` box inside a card paints the raised level, one inside that the
12
+ * raised-2 level, and a field in any of them takes the well that is the
13
+ * opposite step from its parent. Nesting needs no prop and no app selector.
14
+ */
15
+ const Card = React.forwardRef(({ className, variant = "default", hover = false, interactive = false, ...props }, ref) => {
7
16
  return /* @__PURE__ */ jsx("div", {
8
17
  ref,
9
- className: cn("rounded-[var(--radius-lg)] border text-card-foreground transition-[border-color,box-shadow]", "duration-[var(--transition-default)]", {
10
- default: "bg-card border-border",
18
+ className: cn("rounded-[var(--radius-lg)] border text-card-foreground transition-[border-color,box-shadow,background-color]", "duration-[var(--transition-default)]", {
19
+ default: "bg-card border-border shadow-[var(--edge-highlight),var(--shadow-card)]",
11
20
  elevated: "bg-muted/50 border-border shadow-[var(--shadow-card)]",
12
21
  glass: "bg-card/80 backdrop-blur-xl border-border shadow-[var(--shadow-card)]",
13
22
  sandbox: "bg-muted/50 border-primary/20 shadow-[var(--shadow-accent)]"
14
- }[variant], hover && "hover:border-[var(--border-strong)]", className),
23
+ }[variant], (hover || interactive) && "hover:border-[var(--ix-hover-border)] hover:shadow-[var(--ix-hover)] focus:not-focus-visible:border-[var(--ix-hover-border)] focus:not-focus-visible:shadow-[var(--ix-after)] active:border-[hsl(var(--hsl-ring))] active:shadow-[var(--ix-press)] focus-visible:shadow-none", className),
24
+ "data-slot": "card",
15
25
  ...props
16
26
  });
17
27
  });
@@ -32,7 +42,7 @@ const CardTitle = React.forwardRef(({ className, as = "h3", ...props }, ref) =>
32
42
  CardTitle.displayName = "CardTitle";
33
43
  const CardDescription = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("p", {
34
44
  ref,
35
- className: cn("text-muted-foreground text-sm", className),
45
+ className: cn("text-muted-foreground text-sm line-clamp-2", className),
36
46
  ...props
37
47
  }));
38
48
  CardDescription.displayName = "CardDescription";
@@ -1,3 +1,4 @@
1
+ import { ixRow } from "../lib/interaction.js";
1
2
  import { cn } from "../lib/utils.js";
2
3
  import * as React from "react";
3
4
  import { jsx, jsxs } from "react/jsx-runtime";
@@ -12,33 +13,33 @@ const DropdownMenuSub = DropdownMenuPrimitive.Sub;
12
13
  const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup;
13
14
  const DropdownMenuSubTrigger = React.forwardRef(({ className, inset, children, ...props }, ref) => /* @__PURE__ */ jsxs(DropdownMenuPrimitive.SubTrigger, {
14
15
  ref,
15
- className: cn("flex cursor-default select-none items-center rounded-md px-2 py-1.5 text-sm outline-none", "focus:bg-accent data-[state=open]:bg-muted/50", inset && "pl-8", className),
16
+ className: cn("flex cursor-default select-none items-center rounded-md px-2 py-1.5 text-sm outline-none transition-[color,background-color,box-shadow]", "data-[state=open]:bg-[var(--ix-sel-bg)]", ixRow, inset && "pl-8", className),
16
17
  ...props,
17
18
  children: [children, /* @__PURE__ */ jsx(ChevronRight, { className: "ml-auto h-4 w-4" })]
18
19
  }));
19
20
  DropdownMenuSubTrigger.displayName = DropdownMenuPrimitive.SubTrigger.displayName;
20
21
  const DropdownMenuSubContent = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(DropdownMenuPrimitive.SubContent, {
21
22
  ref,
22
- className: cn("z-50 min-w-[8rem] overflow-hidden rounded-xl border border-border bg-card p-1 text-foreground shadow-[var(--shadow-card)]", "data-[state=closed]:animate-out data-[state=open]:animate-in", "data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0", "data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95", "data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2", "data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2", className),
23
+ className: cn("z-50 min-w-[8rem] overflow-hidden rounded-xl border border-border bg-popover p-1 text-popover-foreground shadow-[var(--edge-highlight),var(--shadow-card)]", "data-[state=closed]:animate-out data-[state=open]:animate-in", "data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0", "data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95", "data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2", "data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2", className),
23
24
  ...props
24
25
  }));
25
26
  DropdownMenuSubContent.displayName = DropdownMenuPrimitive.SubContent.displayName;
26
27
  const DropdownMenuContent = React.forwardRef(({ className, sideOffset = 4, ...props }, ref) => /* @__PURE__ */ jsx(DropdownMenuPrimitive.Portal, { children: /* @__PURE__ */ jsx(DropdownMenuPrimitive.Content, {
27
28
  ref,
28
29
  sideOffset,
29
- className: cn("z-50 min-w-[8rem] overflow-hidden rounded-xl border border-border bg-card p-1 text-foreground shadow-[var(--shadow-card)]", "data-[state=closed]:animate-out data-[state=open]:animate-in", "data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0", "data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95", "data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2", "data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2", className),
30
+ className: cn("z-50 min-w-[8rem] overflow-hidden rounded-xl border border-border bg-popover p-1 text-popover-foreground shadow-[var(--edge-highlight),var(--shadow-card)]", "data-[state=closed]:animate-out data-[state=open]:animate-in", "data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0", "data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95", "data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2", "data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2", className),
30
31
  ...props
31
32
  }) }));
32
33
  DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName;
33
34
  const DropdownMenuItem = React.forwardRef(({ className, inset, ...props }, ref) => /* @__PURE__ */ jsx(DropdownMenuPrimitive.Item, {
34
35
  ref,
35
- className: cn("relative flex cursor-pointer select-none items-center rounded-md px-2 py-1.5 text-sm outline-none transition-colors", "focus:bg-muted/50 focus:text-foreground", "data-[disabled]:pointer-events-none data-[disabled]:opacity-50", inset && "pl-8", className),
36
+ className: cn("relative flex cursor-pointer select-none items-center rounded-md px-2 py-1.5 text-sm outline-none transition-[color,background-color,box-shadow]", "focus:text-foreground", ixRow, "data-[disabled]:pointer-events-none data-[disabled]:opacity-50", inset && "pl-8", className),
36
37
  ...props
37
38
  }));
38
39
  DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName;
39
40
  const DropdownMenuCheckboxItem = React.forwardRef(({ className, children, checked, ...props }, ref) => /* @__PURE__ */ jsxs(DropdownMenuPrimitive.CheckboxItem, {
40
41
  ref,
41
- className: cn("relative flex cursor-pointer select-none items-center rounded-md py-1.5 pr-2 pl-8 text-sm outline-none transition-colors", "focus:bg-muted/50 focus:text-foreground", "data-[disabled]:pointer-events-none data-[disabled]:opacity-50", className),
42
+ className: cn("relative flex cursor-pointer select-none items-center rounded-md py-1.5 pr-2 pl-8 text-sm outline-none transition-[color,background-color,box-shadow]", "focus:text-foreground", ixRow, "data-[disabled]:pointer-events-none data-[disabled]:opacity-50", className),
42
43
  checked,
43
44
  ...props,
44
45
  children: [/* @__PURE__ */ jsx("span", {
@@ -49,7 +50,7 @@ const DropdownMenuCheckboxItem = React.forwardRef(({ className, children, checke
49
50
  DropdownMenuCheckboxItem.displayName = DropdownMenuPrimitive.CheckboxItem.displayName;
50
51
  const DropdownMenuRadioItem = React.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(DropdownMenuPrimitive.RadioItem, {
51
52
  ref,
52
- className: cn("relative flex cursor-pointer select-none items-center rounded-md py-1.5 pr-2 pl-8 text-sm outline-none transition-colors", "focus:bg-muted/50 focus:text-foreground", "data-[disabled]:pointer-events-none data-[disabled]:opacity-50", className),
53
+ className: cn("relative flex cursor-pointer select-none items-center rounded-md py-1.5 pr-2 pl-8 text-sm outline-none transition-[color,background-color,box-shadow]", "focus:text-foreground", ixRow, "data-[disabled]:pointer-events-none data-[disabled]:opacity-50", className),
53
54
  ...props,
54
55
  children: [/* @__PURE__ */ jsx("span", {
55
56
  className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center",
@@ -12,7 +12,11 @@ import * as React from "react";
12
12
  * console warning fires when both are omitted.
13
13
  *
14
14
  * Design rules baked into the default variant:
15
- * - Only the SELECTED segment shows a surface colour + accent text.
15
+ * - The row track is a field well (`--well`, `--field-border`), so the
16
+ * control reads as one inset group on whatever plane it sits on.
17
+ * - Only the SELECTED segment shows a surface colour + accent text: the
18
+ * shared selected state (`ixSelected`: tint plus an inset ring). A tab
19
+ * shows it as an underline instead (`ixTabUnderline`).
16
20
  * - Unselected segments have NO background — they're plain-text labels
17
21
  * that darken on hover. This keeps the selected segment as the
18
22
  * single visual anchor instead of the whole group competing with
@@ -1,4 +1,5 @@
1
1
  "use client";
2
+ import "../lib/interaction.js";
2
3
  import { focusRing, focusRingInset } from "../lib/focus.js";
3
4
  import { cn } from "../lib/utils.js";
4
5
  import "../lib/control-presentation.js";
@@ -83,7 +84,7 @@ function SegmentedControl({ value, onValueChange, options, variant = "row", clas
83
84
  ref: stripRef,
84
85
  onKeyDown: handleKeyDown,
85
86
  onScroll: tabs ? measureOverflow : void 0,
86
- className: cn("flex gap-1", variant === "row" && "flex-wrap items-center rounded-lg border border-border bg-card p-1", variant === "tabs" && ["relative flex-nowrap items-end border-b border-border pb-0 overflow-x-auto", tabEdgeFade], className),
87
+ className: cn("flex gap-1", variant === "row" && "flex-wrap items-center rounded-lg border border-[var(--field-border)] bg-[var(--well)] p-1", variant === "tabs" && ["relative flex-nowrap items-end border-b border-border pb-0 overflow-x-auto", tabEdgeFade], className),
87
88
  children: options.map((option, i) => {
88
89
  const active = option.value === value;
89
90
  const focusable = active || !hasMatch && i === 0;
@@ -100,11 +101,17 @@ function SegmentedControl({ value, onValueChange, options, variant = "row", clas
100
101
  onClick: () => {
101
102
  if (!active) onValueChange(option.value);
102
103
  },
103
- className: cn("relative inline-flex items-center gap-2 whitespace-nowrap text-sm font-medium transition-[color,background-color,border-color,box-shadow]", variant === "row" && ["rounded-md px-3 py-1.5", focusRing], variant === "tabs" && [
104
- "shrink-0 rounded-none border-b-2 -mb-px px-4 py-2",
104
+ className: cn("relative inline-flex items-center gap-2 whitespace-nowrap text-sm font-medium transition-[color,background-color,border-color,box-shadow]", variant === "row" && [
105
+ "rounded-md border border-transparent px-3 py-1.5",
106
+ focusRing,
107
+ "hover:bg-[var(--ix-hover-bg)] active:bg-[var(--ix-sel-bg)]",
108
+ "aria-pressed:bg-[var(--ix-sel-bg)] aria-pressed:text-[var(--accent-text)] aria-pressed:shadow-[inset_0_0_0_1px_var(--ix-sel-ring)] aria-checked:bg-[var(--ix-sel-bg)] aria-checked:text-[var(--accent-text)] aria-checked:shadow-[inset_0_0_0_1px_var(--ix-sel-ring)] aria-[current=page]:bg-[var(--ix-sel-bg)] aria-[current=page]:text-[var(--accent-text)] aria-[current=page]:shadow-[inset_0_0_0_1px_var(--ix-sel-ring)] data-[state=on]:bg-[var(--ix-sel-bg)] data-[state=on]:shadow-[inset_0_0_0_1px_var(--ix-sel-ring)] data-[state=active]:bg-[var(--ix-sel-bg)] data-[state=active]:text-[var(--accent-text)] data-[state=active]:shadow-[inset_0_0_0_1px_var(--ix-sel-ring)]"
109
+ ], variant === "tabs" && [
110
+ "shrink-0 rounded-none border-b-2 border-transparent -mb-px px-4 py-2",
105
111
  "pointer-coarse:min-h-11",
106
- focusRingInset
107
- ], active ? variant === "row" ? "border border-transparent bg-[var(--accent-surface)] text-[var(--accent-text)]" : "border-[var(--accent-text)] text-[var(--accent-text)]" : variant === "row" ? "border border-transparent text-muted-foreground hover:text-foreground" : "border-transparent text-muted-foreground hover:text-foreground"),
112
+ focusRingInset,
113
+ "hover:shadow-[inset_0_-2px_0_hsl(var(--hsl-ring)/0.4)] data-[state=active]:bg-[hsl(var(--hsl-ring)/0.07)] data-[state=active]:text-[var(--accent-text)] data-[state=active]:shadow-[inset_0_-2px_0_var(--ix-sel-ring)] aria-checked:bg-[hsl(var(--hsl-ring)/0.07)] aria-checked:text-[var(--accent-text)] aria-checked:shadow-[inset_0_-2px_0_var(--ix-sel-ring)]"
114
+ ], active ? "text-[var(--accent-text)]" : "text-muted-foreground hover:text-foreground"),
108
115
  children: [/* @__PURE__ */ jsx("span", { children: option.label }), option.adornment && /* @__PURE__ */ jsx("span", {
109
116
  className: cn("text-xs", active ? "text-[var(--accent-text)]/80" : "text-muted-foreground"),
110
117
  children: option.adornment
@@ -1,4 +1,5 @@
1
1
  "use client";
2
+ import { ixControlAfter, ixRow } from "../lib/interaction.js";
2
3
  import { cn } from "../lib/utils.js";
3
4
  import { controlSizes, fieldPresentation, fieldSizes, fieldTouchText } from "../lib/control-presentation.js";
4
5
  import * as React from "react";
@@ -11,7 +12,7 @@ const SelectGroup = SelectPrimitive.Group;
11
12
  const SelectValue = SelectPrimitive.Value;
12
13
  const SelectTrigger = React.forwardRef(({ className, children, size = "default", ...props }, ref) => /* @__PURE__ */ jsxs(SelectPrimitive.Trigger, {
13
14
  ref,
14
- className: cn("flex w-full items-center justify-between gap-2 whitespace-nowrap border py-1 shadow-sm placeholder:text-[var(--text-dim)] disabled:cursor-not-allowed disabled:opacity-50", fieldPresentation, fieldTouchText, "data-[placeholder]:text-[var(--text-dim)] [&>span]:min-w-0 [&>span]:truncate [&>span]:text-left", "data-[state=open]:border-primary data-[state=open]:bg-[var(--accent-surface-strong)] [&[data-state=open]>svg]:rotate-180 [&>svg]:transition-transform", size === "compact" || size === "touch" ? cn(fieldSizes.md, controlSizes[size]) : fieldSizes[size === "default" ? "md" : size], className),
15
+ className: cn("flex w-full items-center justify-between gap-2 whitespace-nowrap border py-1 placeholder:text-[var(--text-dim)] disabled:cursor-not-allowed disabled:opacity-50", fieldPresentation, ixControlAfter, fieldTouchText, "data-[placeholder]:text-[var(--text-dim)] [&>span]:min-w-0 [&>span]:truncate [&>span]:text-left", "data-[state=open]:border-[var(--ix-hover-border)] data-[state=open]:shadow-[var(--ix-after)] [&[data-state=open]>svg]:rotate-180 [&>svg]:transition-transform", size === "compact" || size === "touch" ? cn(fieldSizes.md, controlSizes[size]) : fieldSizes[size === "default" ? "md" : size], className),
15
16
  ...props,
16
17
  children: [children, /* @__PURE__ */ jsx(SelectPrimitive.Icon, {
17
18
  asChild: true,
@@ -35,7 +36,7 @@ const SelectScrollDownButton = React.forwardRef(({ className, ...props }, ref) =
35
36
  SelectScrollDownButton.displayName = SelectPrimitive.ScrollDownButton.displayName;
36
37
  const SelectContent = React.forwardRef(({ className, children, position = "popper", ...props }, ref) => /* @__PURE__ */ jsx(SelectPrimitive.Portal, { children: /* @__PURE__ */ jsxs(SelectPrimitive.Content, {
37
38
  ref,
38
- className: cn("data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-lg border border-border bg-card text-foreground shadow-[var(--shadow-dropdown)] data-[state=closed]:animate-out data-[state=open]:animate-in", position === "popper" && "data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=bottom]:translate-y-1 data-[side=top]:-translate-y-1", className),
39
+ className: cn("data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-lg border border-border bg-popover text-popover-foreground shadow-[var(--edge-highlight),var(--shadow-card)] data-[state=closed]:animate-out data-[state=open]:animate-in", position === "popper" && "data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=bottom]:translate-y-1 data-[side=top]:-translate-y-1", className),
39
40
  position,
40
41
  ...props,
41
42
  children: [
@@ -56,7 +57,7 @@ const SelectLabel = React.forwardRef(({ className, ...props }, ref) => /* @__PUR
56
57
  SelectLabel.displayName = SelectPrimitive.Label.displayName;
57
58
  const SelectItem = React.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(SelectPrimitive.Item, {
58
59
  ref,
59
- className: cn("relative flex w-full cursor-default select-none items-center rounded-md py-1.5 pr-8 pl-2 text-sm outline-none transition-colors focus:bg-[var(--accent-surface-strong)] focus:text-foreground data-[state=checked]:bg-[var(--accent-surface-strong)] data-[state=checked]:font-medium data-[disabled]:pointer-events-none data-[disabled]:opacity-50", className),
60
+ className: cn("relative flex w-full cursor-default select-none items-center rounded-md py-1.5 pr-8 pl-2 text-sm outline-none transition-[color,background-color,box-shadow] focus:text-foreground data-[state=checked]:font-medium data-[disabled]:pointer-events-none data-[disabled]:opacity-50", ixRow, className),
60
61
  ...props,
61
62
  children: [/* @__PURE__ */ jsx("span", {
62
63
  className: "absolute right-2 flex h-3.5 w-3.5 items-center justify-center",
@@ -1,3 +1,4 @@
1
+ import { ixControlSoft, ixSelected, ixTabUnderline } from "../lib/interaction.js";
1
2
  import { focusRing } from "../lib/focus.js";
2
3
  import { cn } from "../lib/utils.js";
3
4
  import * as React from "react";
@@ -19,9 +20,9 @@ const TabsList = React.forwardRef(({ className, variant = "default", ...props },
19
20
  TabsList.displayName = TabsPrimitive.List.displayName;
20
21
  const TabsTrigger = React.forwardRef(({ className, variant = "default", ...props }, ref) => {
21
22
  const variants = {
22
- default: `inline-flex items-center justify-center whitespace-nowrap rounded-md px-3 py-1.5 text-sm font-medium transition-all ${focusRing} disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-sm`,
23
- pills: `inline-flex items-center justify-center whitespace-nowrap rounded-full px-4 py-2 text-sm font-medium transition-all ${focusRing} disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-primary data-[state=active]:text-primary-foreground data-[state=inactive]:text-muted-foreground data-[state=inactive]:hover:bg-muted`,
24
- underline: `inline-flex items-center justify-center whitespace-nowrap rounded-t-md pb-3 text-sm font-medium transition-all ${focusRing} disabled:pointer-events-none disabled:opacity-50 border-b-2 border-transparent data-[state=active]:border-primary data-[state=active]:text-foreground data-[state=inactive]:text-muted-foreground`
23
+ default: `inline-flex items-center justify-center whitespace-nowrap rounded-md px-3 py-1.5 text-sm font-medium transition-all ${focusRing} disabled:pointer-events-none disabled:opacity-50 ${ixControlSoft} ${ixSelected}`,
24
+ pills: `inline-flex items-center justify-center whitespace-nowrap rounded-full px-4 py-2 text-sm font-medium transition-all ${focusRing} disabled:pointer-events-none disabled:opacity-50 data-[state=inactive]:text-muted-foreground ${ixControlSoft} ${ixSelected}`,
25
+ underline: `inline-flex items-center justify-center whitespace-nowrap rounded-t-md pb-3 text-sm font-medium transition-all ${focusRing} disabled:pointer-events-none disabled:opacity-50 border-b-2 border-transparent data-[state=inactive]:text-muted-foreground ${ixTabUnderline}`
25
26
  };
26
27
  return /* @__PURE__ */ jsx(TabsPrimitive.Trigger, {
27
28
  ref,
@@ -4,8 +4,14 @@ export interface InlineThinkingItemProps {
4
4
  part: ReasoningPart;
5
5
  defaultOpen?: boolean;
6
6
  autoCollapse?: boolean;
7
+ /**
8
+ * Open the row while the model is still thinking. A product that keeps
9
+ * deliberation out of the reader's way passes false: the row stays as
10
+ * `defaultOpen` left it, with its one-line preview, until the reader opens it.
11
+ */
12
+ openWhileActive?: boolean;
7
13
  className?: string;
8
14
  contentClassName?: string;
9
15
  }
10
- export declare const InlineThinkingItem: import("react").MemoExoticComponent<({ part, defaultOpen, autoCollapse, className, contentClassName }: InlineThinkingItemProps) => import("react").JSX.Element>;
16
+ export declare const InlineThinkingItem: import("react").MemoExoticComponent<({ part, defaultOpen, autoCollapse, openWhileActive, className, contentClassName }: InlineThinkingItemProps) => import("react").JSX.Element>;
11
17
  //#endregion
@@ -1,5 +1,5 @@
1
1
  import { TextShimmer } from "../primitives/text-shimmer.js";
2
- import { truncateText } from "../utils/format.js";
2
+ import { plainTextFromMarkdown, truncateText } from "../utils/format.js";
3
3
  import { ExpandableContent } from "./expandable-content.js";
4
4
  import { RunRowShell } from "./run-row-shell.js";
5
5
  import { Markdown } from "../markdown/markdown.js";
@@ -7,18 +7,18 @@ import { memo, useEffect, useRef, useState } from "react";
7
7
  import { jsx } from "react/jsx-runtime";
8
8
  import { Brain } from "lucide-react";
9
9
  //#region src/run/inline-thinking-item.tsx
10
- const InlineThinkingItem = memo(({ part, defaultOpen = false, autoCollapse = true, className, contentClassName }) => {
10
+ const InlineThinkingItem = memo(({ part, defaultOpen = false, autoCollapse = true, openWhileActive = true, className, contentClassName }) => {
11
11
  const [open, setOpen] = useState(defaultOpen);
12
12
  const autoCollapsedRef = useRef(false);
13
13
  const startTime = part.time?.start;
14
14
  const endTime = part.time?.end;
15
15
  const durationMs = startTime != null && endTime != null ? endTime - startTime : void 0;
16
16
  const isActive = startTime != null && endTime == null;
17
- const preview = part.text ? truncateText(part.text, 120) : void 0;
17
+ const preview = part.text ? truncateText(plainTextFromMarkdown(part.text), 120) : void 0;
18
18
  useEffect(() => {
19
19
  if (isActive) {
20
20
  autoCollapsedRef.current = false;
21
- setOpen(true);
21
+ if (openWhileActive) setOpen(true);
22
22
  return;
23
23
  }
24
24
  if (autoCollapse && !autoCollapsedRef.current && durationMs != null) {
@@ -31,7 +31,8 @@ const InlineThinkingItem = memo(({ part, defaultOpen = false, autoCollapse = tru
31
31
  }, [
32
32
  autoCollapse,
33
33
  durationMs,
34
- isActive
34
+ isActive,
35
+ openWhileActive
35
36
  ]);
36
37
  return /* @__PURE__ */ jsx(RunRowShell, {
37
38
  icon: /* @__PURE__ */ jsx(Brain, { className: "h-3.5 w-3.5" }),
@@ -1,6 +1,12 @@
1
1
  //#region src/utils/format.d.ts
2
2
  /** Format a duration in milliseconds to a human-readable string. */
3
3
  export declare function formatDuration(ms: number): string;
4
+ /**
5
+ * Markdown read as plain text, for a one-line preview: code fences, emphasis,
6
+ * headings, list and quote markers, and link syntax are removed, so a preview
7
+ * never shows `**` or `#`. Underscores inside words (`snake_case`) are kept.
8
+ */
9
+ export declare function plainTextFromMarkdown(text: string): string;
4
10
  /** Truncate text to `max` characters, appending "..." if truncated. */
5
11
  export declare function truncateText(text: string, max: number): string;
6
12
  /**
@@ -8,6 +8,14 @@ function formatDuration(ms) {
8
8
  const remaining = seconds % 60;
9
9
  return remaining > 0 ? `${minutes}m ${remaining}s` : `${minutes}m`;
10
10
  }
11
+ /**
12
+ * Markdown read as plain text, for a one-line preview: code fences, emphasis,
13
+ * headings, list and quote markers, and link syntax are removed, so a preview
14
+ * never shows `**` or `#`. Underscores inside words (`snake_case`) are kept.
15
+ */
16
+ function plainTextFromMarkdown(text) {
17
+ return text.replace(/```[^\n]*\n?([\s\S]*?)```/g, "$1").replace(/`([^`]*)`/g, "$1").replace(/!?\[([^\]]*)\]\([^)]*\)/g, "$1").replace(/^[ \t]{0,3}(?:#{1,6}[ \t]+|>[ \t]?|[-*+][ \t]+|\d+[.)][ \t]+)/gm, "").replace(/(\*\*|__)(?=\S)([\s\S]*?\S)\1/g, "$2").replace(/(^|[^\w*])\*(?=\S)([^*\n]*?\S)\*(?![\w*])/g, "$1$2").replace(/(^|[^\w])_(?=\S)([^_\n]*?\S)_(?!\w)/g, "$1$2").replace(/~~(?=\S)([\s\S]*?\S)~~/g, "$1");
18
+ }
11
19
  /** Truncate text to `max` characters, appending "..." if truncated. */
12
20
  function truncateText(text, max) {
13
21
  const cleaned = text.replace(/\s+/g, " ").trim();
@@ -51,4 +59,4 @@ function formatBytes(bytes) {
51
59
  return `${gb < 10 ? gb.toFixed(2) : gb.toFixed(1)} GB`;
52
60
  }
53
61
  //#endregion
54
- export { formatBytes, formatDuration, formatUptime, truncateText };
62
+ export { formatBytes, formatDuration, formatUptime, plainTextFromMarkdown, truncateText };
package/dist/utils.d.ts CHANGED
@@ -1,7 +1,8 @@
1
1
  import { copyText } from "./utils/copy-text.js";
2
- import { formatBytes, formatDuration, formatUptime, truncateText } from "./utils/format.js";
2
+ import { formatBytes, formatDuration, formatUptime, plainTextFromMarkdown, truncateText } from "./utils/format.js";
3
3
  import { timeAgo } from "./utils/time-ago.js";
4
4
  import { TOOL_CATEGORY_ICONS, getToolCategory, getToolDisplayMetadata, getToolErrorText } from "./utils/tool-display.js";
5
5
  import { cn } from "./lib/utils.js";
6
6
  import { focusField, focusFieldInvalid, focusFieldWithin, focusRing, focusRingInset } from "./lib/focus.js";
7
- export { TOOL_CATEGORY_ICONS, cn, copyText, focusField, focusFieldInvalid, focusFieldWithin, focusRing, focusRingInset, formatBytes, formatDuration, formatUptime, getToolCategory, getToolDisplayMetadata, getToolErrorText, timeAgo, truncateText };
7
+ import { ixControl, ixControlAfter, ixControlSoft, ixField, ixRow, ixSelected, ixTabUnderline } from "./lib/interaction.js";
8
+ export { TOOL_CATEGORY_ICONS, cn, copyText, focusField, focusFieldInvalid, focusFieldWithin, focusRing, focusRingInset, formatBytes, formatDuration, formatUptime, getToolCategory, getToolDisplayMetadata, getToolErrorText, ixControl, ixControlAfter, ixControlSoft, ixField, ixRow, ixSelected, ixTabUnderline, plainTextFromMarkdown, timeAgo, truncateText };
package/dist/utils.js CHANGED
@@ -1,7 +1,8 @@
1
+ import { ixControl, ixControlAfter, ixControlSoft, ixField, ixRow, ixSelected, ixTabUnderline } from "./lib/interaction.js";
1
2
  import { focusField, focusFieldInvalid, focusFieldWithin, focusRing, focusRingInset } from "./lib/focus.js";
2
3
  import { cn } from "./lib/utils.js";
3
4
  import { TOOL_CATEGORY_ICONS, getToolCategory, getToolDisplayMetadata, getToolErrorText } from "./utils/tool-display.js";
4
- import { formatBytes, formatDuration, formatUptime, truncateText } from "./utils/format.js";
5
+ import { formatBytes, formatDuration, formatUptime, plainTextFromMarkdown, truncateText } from "./utils/format.js";
5
6
  import { copyText } from "./utils/copy-text.js";
6
7
  import { timeAgo } from "./utils/time-ago.js";
7
- export { TOOL_CATEGORY_ICONS, cn, copyText, focusField, focusFieldInvalid, focusFieldWithin, focusRing, focusRingInset, formatBytes, formatDuration, formatUptime, getToolCategory, getToolDisplayMetadata, getToolErrorText, timeAgo, truncateText };
8
+ export { TOOL_CATEGORY_ICONS, cn, copyText, focusField, focusFieldInvalid, focusFieldWithin, focusRing, focusRingInset, formatBytes, formatDuration, formatUptime, getToolCategory, getToolDisplayMetadata, getToolErrorText, ixControl, ixControlAfter, ixControlSoft, ixField, ixRow, ixSelected, ixTabUnderline, plainTextFromMarkdown, timeAgo, truncateText };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/ui",
3
- "version": "11.32.2",
3
+ "version": "11.34.0",
4
4
  "description": "Generic React UI components for Tangle products — primitives, chat, run, files, editor, markdown.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -150,7 +150,7 @@
150
150
  "peerDependencies": {
151
151
  "@hocuspocus/provider": ">=2.15.0 <5.0.0",
152
152
  "@nanostores/react": ">=0.8.4 <3.0.0",
153
- "@tangle-network/brand": "^1.22.1",
153
+ "@tangle-network/brand": "^1.24.1",
154
154
  "@tiptap/core": ">=3.20.4 <4.0.0",
155
155
  "@tiptap/extension-collaboration": ">=3.20.4 <4.0.0",
156
156
  "@tiptap/extension-collaboration-caret": ">=3.7.0 <4.0.0",