@danxbot/ui 3.9.0 → 3.9.1

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.
@@ -54,8 +54,16 @@ export interface DataTableProps<TData> {
54
54
  * styling the table picked — `cn` is tailwind-merge, and the last conflicting
55
55
  * utility wins. That is deliberate: a tint that loses to the zebra fill is a
56
56
  * tint nobody sees. It also means a background handed over here REPLACES the
57
- * row's hover fill, so pass the hover with it (`bg-warning-subtle
58
- * hover:bg-warning-subtle-hover`) if the row should still respond.
57
+ * row's hover fill, so pass the hover with it (`bg-danger-subtle
58
+ * hover:bg-danger-subtle-hover`) if the row should still respond.
59
+ *
60
+ * THAT EXAMPLE USED TO NAME `warning`, WHICH HAS NO HOVER STEP. `-subtle`
61
+ * exists for every semantic family; `-subtle-hover` exists for `accent` and
62
+ * `danger` only. A consumer following the old wording got a hover class that
63
+ * compiled to nothing — no rule, no fill, no error anywhere, which is the
64
+ * same silent-nothing failure the theme bridge in `postbuild-css.mjs` exists
65
+ * to prevent one level up. Pick a family that has the step, or supply the
66
+ * hover colour yourself. `token-parity.test.ts` now sweeps this docblock.
59
67
  *
60
68
  * `index` is the row's position in `data`, not on the current page — the same
61
69
  * number `getRowId` receives, so the two never disagree about which row is
@@ -64,10 +64,31 @@ export declare function Toolbar({ className, children, ref, ...props }: ToolbarP
64
64
  export declare function ToolbarSeparator({ className }: {
65
65
  className?: string;
66
66
  }): import("react").JSX.Element;
67
- export interface ToolbarButtonProps extends Omit<BaseToolbar.Button.Props, "className" | "nativeButton">, Pick<ButtonProps, "variant" | "size" | "iconOnly"> {
67
+ type ToolbarButtonBase = Omit<BaseToolbar.Button.Props, "className" | "nativeButton" | "render"> & {
68
68
  className?: string;
69
69
  ref?: Ref<HTMLButtonElement>;
70
- }
70
+ };
71
+ /**
72
+ * TWO FORMS, AND THE TYPE REFUSES TO MIX THEM.
73
+ *
74
+ * Either this renders its own `<button>` and wears `buttonVariants` — in which
75
+ * case `variant` / `size` / `iconOnly` choose which row of that table — or it
76
+ * renders somebody else's control through `render`, in which case that control
77
+ * already carries a variant table and this one must not add a second. The two
78
+ * are mutually exclusive rather than merely discouraged, because a `render`
79
+ * plus a `size` is not a preference somebody expressed: it is two size classes
80
+ * in one attribute with no rule for which wins. See `ToolbarButton`'s own doc
81
+ * for the measurement.
82
+ */
83
+ export type ToolbarButtonProps = (ToolbarButtonBase & Pick<ButtonProps, "variant" | "size" | "iconOnly"> & {
84
+ /** The styled form owns the element, so there is nothing to render into. */
85
+ render?: undefined;
86
+ }) | (ToolbarButtonBase & {
87
+ render: NonNullable<BaseToolbar.Button.Props["render"]>;
88
+ variant?: never;
89
+ size?: never;
90
+ iconOnly?: never;
91
+ });
71
92
  /**
72
93
  * A button that is part of the toolbar's rotation.
73
94
  *
@@ -85,8 +106,23 @@ export interface ToolbarButtonProps extends Omit<BaseToolbar.Button.Props, "clas
85
106
  * ANYTHING ALREADY BUILT ON `Button` GOES IN THROUGH `render`, which is what
86
107
  * keeps this from becoming a second button component:
87
108
  * `<ToolbarButton render={<IconButton icon={…} label="Bold" />} />` registers
88
- * exactly the same, keeps the tooltip and the `disabledReason`, and never
89
- * duplicates a variant table.
109
+ * exactly the same and keeps the tooltip and the `disabledReason`.
110
+ *
111
+ * THAT PATH APPLIES NO VARIANT TABLE AT ALL, and the type enforces it —
112
+ * `variant`, `size` and `iconOnly` are not accepted beside `render`. Base UI's
113
+ * render slot CONCATENATES `className`; it does not run `cn`. So a recipe
114
+ * applied here landed in the same attribute as the rendered component's own,
115
+ * and which one won was decided by Tailwind's emission order rather than by
116
+ * either component. It measured fine while the two happened to agree — a
117
+ * default `render={<IconButton/>}` came out 36×36, identical to a standalone
118
+ * one, while carrying both `aspect-square` and `px-3.5` — and broke visibly the
119
+ * moment they disagreed: `<ToolbarButton size="lg" render={<IconButton
120
+ * size="sm" …/>} />` carried `h-control-lg` AND `h-control-sm` and rendered
121
+ * 48 × 36 with 24px of padding, a squashed oval neither component asked for.
122
+ *
123
+ * Unrepresentable beats documented, and it is free to do NOW: nothing has
124
+ * adopted the pair yet, so narrowing the type costs nothing that removing the
125
+ * props after adoption would have cost.
90
126
  */
91
127
  export declare function ToolbarButton({ variant, size, iconOnly, className, children, ref, ...props }: ToolbarButtonProps): import("react").JSX.Element;
92
128
  export interface ToolbarInputProps extends Omit<InputProps, "ref">, Pick<BaseToolbar.Input.Props, "focusableWhenDisabled"> {
@@ -1,5 +1,6 @@
1
1
  import { type ReactNode, type Ref } from "react";
2
2
  import type { ChatFile } from "./types";
3
+ import type { IconName } from "../Icon";
3
4
  export type AttachmentLayout = "tile" | "row";
4
5
  export interface AttachmentProps {
5
6
  file: ChatFile;
@@ -24,6 +25,13 @@ export interface AttachmentProps {
24
25
  * It goes inside the cluster, above the full-card preview trigger — rendered
25
26
  * anywhere else it would sit UNDER that trigger and never receive a click.
26
27
  * Keep it to a control or two: this is a corner of a tile, not a menu bar.
28
+ *
29
+ * FILL IT WITH `AttachmentAction`, which is exported for exactly this. The
30
+ * built-in download and remove are that component, so anything else lands in
31
+ * the cluster at a different size and, on a tile, without the hover fade the
32
+ * two beside it have. Any node is accepted — the slot has no opinion — but
33
+ * "matches the built-ins" is a real requirement and there is no way to hit it
34
+ * by hand. Pass `tinted` when `layout` is `"tile"`.
27
35
  */
28
36
  action?: ReactNode;
29
37
  onRemove?: (file: ChatFile) => void;
@@ -36,6 +44,45 @@ export interface AttachmentProps {
36
44
  ref?: Ref<HTMLDivElement>;
37
45
  }
38
46
  export declare function Attachment({ file, layout, meta, action, onRemove, onPreview, onDownload, selected, disabled, className, ref, }: AttachmentProps): import("react").JSX.Element;
47
+ export interface AttachmentActionProps {
48
+ icon: IconName;
49
+ /** The accessible name AND the tooltip. Include the file: "Extract report.pdf". */
50
+ label: string;
51
+ onClick: () => void;
52
+ disabled?: boolean;
53
+ /**
54
+ * On a tile the buttons sit over imagery, so they need their own ground —
55
+ * and they FADE IN on hover rather than sitting permanently over the picture.
56
+ *
57
+ * Pass `layout === "tile"`. Getting it wrong is not a style preference: a
58
+ * tinted control on a row is dark-on-dark, and an untinted one on a tile is
59
+ * a grey glyph over an arbitrary photograph.
60
+ */
61
+ tinted?: boolean;
62
+ }
63
+ /**
64
+ * One control in an attachment's action cluster.
65
+ *
66
+ * THE CONTROL `Attachment.action` IS MEANT TO BE FILLED WITH, and until now it
67
+ * was module-private — so the slot existed with nothing in the library that
68
+ * fits it. A consumer's own `IconButton` is a different size and a different
69
+ * resting state, and on a tile it does not fade in on `group-hover/attachment`
70
+ * with the built-in download and remove beside it, so the one control the
71
+ * consumer added was the one that looked wrong.
72
+ *
73
+ * It is deliberately NOT `IconButton`: `size-6` rather than a control height,
74
+ * no ripple, no `disabledReason`, and a tinted variant that only means anything
75
+ * inside `.group/attachment`. That is a corner-of-a-tile control, not a general
76
+ * one, and generalising it would give the cluster two different buttons again.
77
+ *
78
+ * <Attachment
79
+ * file={file}
80
+ * layout="tile"
81
+ * action={<AttachmentAction icon="sparkles" label={`Extract ${file.name}`}
82
+ * onClick={() => extract(file)} tinted />}
83
+ * />
84
+ */
85
+ export declare function AttachmentAction({ icon, label, onClick, disabled, tinted, }: AttachmentActionProps): import("react").JSX.Element;
39
86
  export interface AttachmentPreviewProps {
40
87
  file: ChatFile | null;
41
88
  onOpenChange: (open: boolean) => void;
@@ -85,7 +85,7 @@ export { Message, MessagePartView, ToolCall, ChatMarker, type MessageProps, type
85
85
  export { MessageScroller, type MessageScrollerProps, } from "./components/chat/MessageScroller";
86
86
  export { Composer, type ComposerProps } from "./components/chat/Composer";
87
87
  export { Markdown, CodeBlock, type MarkdownProps, type CodeBlockProps } from "./components/chat/Markdown";
88
- export { Attachment, AttachmentList, AttachmentPreview, type AttachmentProps, type AttachmentListProps, type AttachmentPreviewProps, type AttachmentLayout, } from "./components/chat/Attachment";
88
+ export { Attachment, AttachmentAction, AttachmentList, AttachmentPreview, type AttachmentProps, type AttachmentActionProps, type AttachmentListProps, type AttachmentPreviewProps, type AttachmentLayout, } from "./components/chat/Attachment";
89
89
  export { ModelSelector, type ModelSelectorProps, type ChatModel, } from "./components/chat/ModelSelector";
90
90
  export { EffortControl, DEFAULT_EFFORT_LEVELS, type EffortControlProps, type EffortLevel, } from "./components/chat/EffortControl";
91
91
  export { ContextMeter, ChatStatus, type ContextMeterProps, type ContextSegment, type UsageLimit, type ChatStatusProps, } from "./components/chat/ContextMeter";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@danxbot/ui",
3
- "version": "3.9.0",
3
+ "version": "3.9.1",
4
4
  "type": "module",
5
5
  "description": "Danxbot — a domain-agnostic React design system with motion as a first-class primitive.",
6
6
  "license": "MIT",
@@ -66,7 +66,7 @@
66
66
  "build:types": "tsc -p tsconfig.build.json",
67
67
  "build:demo": "vite build",
68
68
  "build": "npm run build:lib && npm run build:demo",
69
- "verify": "npm run typecheck && npm run test && npm run build && npm run typecheck:previews && npm run audit:previews && npm run audit:visual-stale",
69
+ "verify": "npm run typecheck && npm run build && npm run test && npm run typecheck:previews && npm run audit:previews && npm run audit:visual-stale",
70
70
  "audit:visual-stale": "node scripts/audit-visual-stale.mjs",
71
71
  "preview": "vite preview",
72
72
  "preview:built": "vite preview --outDir demo-dist --port 5801 --strictPort",