@danxbot/ui 3.8.0 → 3.9.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/dist/index.js +8941 -8699
- package/dist/index.js.map +1 -1
- package/dist/pure.js +17 -0
- package/dist/pure.js.map +1 -0
- package/dist/types/components/Badge.d.ts +29 -1
- package/dist/types/components/StatusSelect.d.ts +33 -2
- package/dist/types/components/Table.d.ts +26 -1
- package/dist/types/components/Toggle.d.ts +62 -0
- package/dist/types/components/chat/Attachment.d.ts +31 -4
- package/dist/types/components/chat/ContextMeter.d.ts +0 -10
- package/dist/types/index.d.ts +5 -4
- package/dist/types/lib/format.d.ts +12 -0
- package/dist/types/pure.d.ts +36 -0
- package/package.json +6 -2
package/dist/pure.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
function u(r) {
|
|
2
|
+
if (!Number.isFinite(r) || r < 0) return "";
|
|
3
|
+
if (r < 1024) return `${r} B`;
|
|
4
|
+
const t = ["KB", "MB", "GB", "TB"];
|
|
5
|
+
let i = r / 1024, e = 0;
|
|
6
|
+
for (; i >= 1024 && e < t.length - 1; )
|
|
7
|
+
i /= 1024, e += 1;
|
|
8
|
+
return `${i < 10 ? i.toFixed(1) : Math.round(i)} ${t[e]}`;
|
|
9
|
+
}
|
|
10
|
+
function n(r) {
|
|
11
|
+
return Number.isFinite(r) ? r < 1e3 ? String(Math.round(r)) : r < 1e6 ? `${Number((r / 1e3).toFixed(1))}k` : `${Number((r / 1e6).toFixed(1))}M` : "—";
|
|
12
|
+
}
|
|
13
|
+
export {
|
|
14
|
+
u as formatBytes,
|
|
15
|
+
n as formatTokens
|
|
16
|
+
};
|
|
17
|
+
//# sourceMappingURL=pure.js.map
|
package/dist/pure.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pure.js","sources":["../src/lib/format.ts"],"sourcesContent":["/* ----------------------------------------------------------------------------\n NUMBERS, TURNED INTO THE STRINGS PEOPLE READ.\n\n These lived inside the components that needed them first — `formatBytes` in\n `chat/Attachment.tsx`, `formatTokens` in `chat/ContextMeter.tsx` — which was\n fine right up until somebody outside a component wanted one. The package\n exports a single entry, so importing `formatBytes` into an application's own\n formatting layer dragged React and every component in this library into a\n layer that is deliberately framework-free.\n\n They are here because they are DECISIONS, not utilities: how much precision\n a reader can act on, and where a rounded figure stops telling the truth. Two\n places rendering the same quantity to different precision is the defect this\n file prevents — see each function for the call it makes.\n\n NOTHING IN THIS FILE MAY IMPORT REACT, directly or transitively. It is the\n whole content of the `@danxbot/ui/pure` entry; `src/pure.ts` states the\n contract and `pure-entry.test.ts` enforces it against both the source graph\n and the built artifact.\n -------------------------------------------------------------------------- */\n\n/** Binary units, because these are file sizes and every OS reports them this way. */\nexport function formatBytes(bytes: number): string {\n if (!Number.isFinite(bytes) || bytes < 0) return \"\";\n if (bytes < 1024) return `${bytes} B`;\n const units = [\"KB\", \"MB\", \"GB\", \"TB\"];\n let value = bytes / 1024;\n let unit = 0;\n while (value >= 1024 && unit < units.length - 1) {\n value /= 1024;\n unit += 1;\n }\n /* One decimal below 10 and none above: \"1.4 MB\" is useful precision,\n \"847.3 KB\" is noise on a number nobody acts on. */\n return `${value < 10 ? value.toFixed(1) : Math.round(value)} ${units[unit]}`;\n}\n\n/**\n * 241_600 → \"241.6k\".\n *\n * One decimal all the way to 1M, and exact below 1k. Unlike a file size — where\n * a tenth of a kilobyte is noise on a number nobody acts on — the tenth here is\n * the digit that moves while you work: at 1M capacity, a whole percent is\n * 10_000 tokens, so rounding to \"242k\" holds a figure still for several\n * exchanges and makes a live meter look frozen.\n */\nexport function formatTokens(tokens: number): string {\n if (!Number.isFinite(tokens)) return \"—\";\n if (tokens < 1_000) return String(Math.round(tokens));\n if (tokens < 1_000_000) return `${Number((tokens / 1_000).toFixed(1))}k`;\n return `${Number((tokens / 1_000_000).toFixed(1))}M`;\n}\n"],"names":["formatBytes","bytes","units","value","unit","formatTokens","tokens"],"mappings":"AAsBO,SAASA,EAAYC,GAAuB;AACjD,MAAI,CAAC,OAAO,SAASA,CAAK,KAAKA,IAAQ,EAAG,QAAO;AACjD,MAAIA,IAAQ,KAAM,QAAO,GAAGA,CAAK;AACjC,QAAMC,IAAQ,CAAC,MAAM,MAAM,MAAM,IAAI;AACrC,MAAIC,IAAQF,IAAQ,MAChBG,IAAO;AACX,SAAOD,KAAS,QAAQC,IAAOF,EAAM,SAAS;AAC5C,IAAAC,KAAS,MACTC,KAAQ;AAIV,SAAO,GAAGD,IAAQ,KAAKA,EAAM,QAAQ,CAAC,IAAI,KAAK,MAAMA,CAAK,CAAC,IAAID,EAAME,CAAI,CAAC;AAC5E;AAWO,SAASC,EAAaC,GAAwB;AACnD,SAAK,OAAO,SAASA,CAAM,IACvBA,IAAS,MAAc,OAAO,KAAK,MAAMA,CAAM,CAAC,IAChDA,IAAS,MAAkB,GAAG,QAAQA,IAAS,KAAO,QAAQ,CAAC,CAAC,CAAC,MAC9D,GAAG,QAAQA,IAAS,KAAW,QAAQ,CAAC,CAAC,CAAC,MAHZ;AAIvC;"}
|
|
@@ -58,8 +58,36 @@ declare const badge: import("tailwind-variants").TVReturnType<{
|
|
|
58
58
|
false: "";
|
|
59
59
|
};
|
|
60
60
|
}, undefined>>;
|
|
61
|
+
/**
|
|
62
|
+
* What a badge needs in order to be removable, in one object.
|
|
63
|
+
*
|
|
64
|
+
* ONE PROP RATHER THAN TWO, and the label being a required FIELD is the point:
|
|
65
|
+
* a dismiss control with no accessible name is not expressible. An icon carries
|
|
66
|
+
* no text, and a row of filter chips is exactly where "Remove" spoken five
|
|
67
|
+
* times distinguishes nothing — the same argument that makes `IconButton.label`
|
|
68
|
+
* required. `SettingRow`'s `readOnly: { reason }` uses the same shape for the
|
|
69
|
+
* same reason: an explanation that is part of the type cannot be forgotten.
|
|
70
|
+
*
|
|
71
|
+
* Name what it removes ("Remove the infra filter"), not the picture ("Close").
|
|
72
|
+
*/
|
|
73
|
+
export interface BadgeDismiss {
|
|
74
|
+
label: string;
|
|
75
|
+
onDismiss: () => void;
|
|
76
|
+
}
|
|
61
77
|
export interface BadgeProps extends HTMLAttributes<HTMLSpanElement>, VariantProps<typeof badge> {
|
|
62
78
|
icon?: ReactNode;
|
|
79
|
+
/**
|
|
80
|
+
* Makes the badge a chip you can take off — a trailing × after the label.
|
|
81
|
+
*
|
|
82
|
+
* Absent means a plain label, which is what every existing badge is: an ×
|
|
83
|
+
* nobody asked for is a destructive control appearing inside a caption.
|
|
84
|
+
*
|
|
85
|
+
* NOT FOR A BADGE INSIDE A BUTTON. `StatusSelect` renders its Badge as the
|
|
86
|
+
* body of a trigger, and a `<button>` inside a `<button>` is invalid and
|
|
87
|
+
* unreachable by keyboard. A dismissible badge belongs on a surface, not in
|
|
88
|
+
* a control.
|
|
89
|
+
*/
|
|
90
|
+
dismiss?: BadgeDismiss;
|
|
63
91
|
/**
|
|
64
92
|
* Ping the dot outward — for genuinely LIVE states only (running, connected,
|
|
65
93
|
* streaming). A pulse on a static label is the badge equivalent of a blinking
|
|
@@ -69,5 +97,5 @@ export interface BadgeProps extends HTMLAttributes<HTMLSpanElement>, VariantProp
|
|
|
69
97
|
pulse?: boolean;
|
|
70
98
|
ref?: Ref<HTMLSpanElement>;
|
|
71
99
|
}
|
|
72
|
-
export declare function Badge({ className, tone, size, dot, icon, pulse, children, ref, ...props }: BadgeProps): import("react").JSX.Element;
|
|
100
|
+
export declare function Badge({ className, tone, size, dot, icon, pulse, dismiss, children, ref, ...props }: BadgeProps): import("react").JSX.Element;
|
|
73
101
|
export { badge as badgeVariants };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
2
|
import { type BadgeProps } from "./Badge";
|
|
3
3
|
export interface StatusOption<Value extends string = string> {
|
|
4
4
|
value: Value;
|
|
@@ -9,6 +9,29 @@ export interface StatusOption<Value extends string = string> {
|
|
|
9
9
|
dot?: boolean;
|
|
10
10
|
/** Reserved for a genuinely live state — see `Badge`'s own `pulse` doc. */
|
|
11
11
|
pulse?: boolean;
|
|
12
|
+
/**
|
|
13
|
+
* Why this status cannot be chosen HERE. Absent means it can.
|
|
14
|
+
*
|
|
15
|
+
* The option is still listed, still carries its own tone and label, and is
|
|
16
|
+
* still what the trigger shows when the record is sitting in it — it simply
|
|
17
|
+
* never accepts. That is the difference between a status the consumer does
|
|
18
|
+
* not assign and a status the consumer does not have, and only one of them
|
|
19
|
+
* belongs out of the list.
|
|
20
|
+
*
|
|
21
|
+
* Written for the person who would have picked it — "the pipeline enters
|
|
22
|
+
* this stage on its own", not "not allowed". It is rendered under the badge
|
|
23
|
+
* rather than only dimming the row, and attached to the row as its
|
|
24
|
+
* description, so the refusal is never mute. Same call `IconButton`'s
|
|
25
|
+
* `disabledReason` makes, and it is `aria-disabled` there for the same
|
|
26
|
+
* reason: a natively disabled control cannot be reached to be asked why.
|
|
27
|
+
*
|
|
28
|
+
* AN EMPTY STRING THROWS, matching `Step.blocked` rather than
|
|
29
|
+
* `KanbanLane.refusal`. There the reason is supporting detail on a lane that
|
|
30
|
+
* is already visibly refusing; here the sentence IS the mechanism, and an
|
|
31
|
+
* empty one produces exactly the dead row with nothing beside it that this
|
|
32
|
+
* prop exists to prevent.
|
|
33
|
+
*/
|
|
34
|
+
refusal?: string;
|
|
12
35
|
}
|
|
13
36
|
export interface StatusSelectProps<Value extends string = string> {
|
|
14
37
|
value: Value;
|
|
@@ -18,6 +41,14 @@ export interface StatusSelectProps<Value extends string = string> {
|
|
|
18
41
|
/** Accessible name for the trigger — there is no visible label otherwise. */
|
|
19
42
|
"aria-label"?: string;
|
|
20
43
|
disabled?: boolean;
|
|
44
|
+
/**
|
|
45
|
+
* What the trigger says when `value` matches no option — a state that is
|
|
46
|
+
* always a mistake somewhere, so this is the wording of the report rather
|
|
47
|
+
* than a label anyone should be seeing.
|
|
48
|
+
*
|
|
49
|
+
* Defaults to "Unknown". It is NOT the raw value: see the component body.
|
|
50
|
+
*/
|
|
51
|
+
placeholder?: ReactNode;
|
|
21
52
|
className?: string;
|
|
22
53
|
}
|
|
23
|
-
export declare function StatusSelect<Value extends string = string>({ value, options, onValueChange, size, "aria-label": ariaLabel, disabled, className, }: StatusSelectProps<Value>): import("react").JSX.Element;
|
|
54
|
+
export declare function StatusSelect<Value extends string = string>({ value, options, onValueChange, size, "aria-label": ariaLabel, disabled, placeholder, className, }: StatusSelectProps<Value>): import("react").JSX.Element;
|
|
@@ -40,13 +40,38 @@ export interface DataTableProps<TData> {
|
|
|
40
40
|
*/
|
|
41
41
|
pageSize?: number;
|
|
42
42
|
emptyState?: ReactNode;
|
|
43
|
+
/**
|
|
44
|
+
* An extra class for the rows a consumer needs to mark — "this figure could
|
|
45
|
+
* not be read from the source document", "this record is superseded". Return
|
|
46
|
+
* `undefined` for the rows that are unremarkable.
|
|
47
|
+
*
|
|
48
|
+
* WHY A HOOK AND NOT A `tone` UNION. The reasons a row deserves a tint are
|
|
49
|
+
* the consumer's domain and there is no useful shortlist of them; a fixed set
|
|
50
|
+
* would be wrong for the second consumer and every one after. This is the
|
|
51
|
+
* same call `getRowId` makes about identity.
|
|
52
|
+
*
|
|
53
|
+
* THE CLASS IS APPLIED LAST, so it beats the density, zebra and selection
|
|
54
|
+
* styling the table picked — `cn` is tailwind-merge, and the last conflicting
|
|
55
|
+
* utility wins. That is deliberate: a tint that loses to the zebra fill is a
|
|
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.
|
|
59
|
+
*
|
|
60
|
+
* `index` is the row's position in `data`, not on the current page — the same
|
|
61
|
+
* number `getRowId` receives, so the two never disagree about which row is
|
|
62
|
+
* being talked about.
|
|
63
|
+
*
|
|
64
|
+
* NOT CALLED FOR SKELETON ROWS. A skeleton stands in for a record nobody has
|
|
65
|
+
* yet, so there is nothing to classify and nothing to hand over.
|
|
66
|
+
*/
|
|
67
|
+
getRowClassName?: (row: TData, index: number) => string | undefined;
|
|
43
68
|
onRowClick?: (row: TData) => void;
|
|
44
69
|
/** Rendered above the table; receives the table instance for counts/filters. */
|
|
45
70
|
toolbar?: (table: TanstackTable<TData>) => ReactNode;
|
|
46
71
|
className?: string;
|
|
47
72
|
getRowId?: (row: TData, index: number) => string;
|
|
48
73
|
}
|
|
49
|
-
export declare function DataTable<TData>({ data, columns, density, selectable, maxHeight, zebra, loading, refreshing, loadingRows, pageSize, emptyState, onRowClick, toolbar, className, getRowId, }: DataTableProps<TData>): import("react").JSX.Element;
|
|
74
|
+
export declare function DataTable<TData>({ data, columns, density, selectable, maxHeight, zebra, loading, refreshing, loadingRows, pageSize, emptyState, getRowClassName, onRowClick, toolbar, className, getRowId, }: DataTableProps<TData>): import("react").JSX.Element;
|
|
50
75
|
export declare function TableEmptyState({ icon, title, description, action, }: {
|
|
51
76
|
icon?: ReactNode;
|
|
52
77
|
title?: string;
|
|
@@ -3,6 +3,8 @@ import { Toggle as BaseToggle } from "@base-ui/react/toggle";
|
|
|
3
3
|
import { ToggleGroup as BaseToggleGroup } from "@base-ui/react/toggle-group";
|
|
4
4
|
import { Toolbar as BaseToolbar } from "@base-ui/react/toolbar";
|
|
5
5
|
import { type VariantProps } from "tailwind-variants";
|
|
6
|
+
import { type ButtonProps } from "./Button";
|
|
7
|
+
import { type InputProps } from "./Input";
|
|
6
8
|
declare const toggle: import("tailwind-variants").TVReturnType<{
|
|
7
9
|
size: {
|
|
8
10
|
sm: "h-control-sm px-2.5 text-xs";
|
|
@@ -62,4 +64,64 @@ export declare function Toolbar({ className, children, ref, ...props }: ToolbarP
|
|
|
62
64
|
export declare function ToolbarSeparator({ className }: {
|
|
63
65
|
className?: string;
|
|
64
66
|
}): import("react").JSX.Element;
|
|
67
|
+
export interface ToolbarButtonProps extends Omit<BaseToolbar.Button.Props, "className" | "nativeButton">, Pick<ButtonProps, "variant" | "size" | "iconOnly"> {
|
|
68
|
+
className?: string;
|
|
69
|
+
ref?: Ref<HTMLButtonElement>;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* A button that is part of the toolbar's rotation.
|
|
73
|
+
*
|
|
74
|
+
* It wears `buttonVariants` — the same recipe `Button` compiles — rather than a
|
|
75
|
+
* second class list, so the two can never drift apart. It is not `Button`
|
|
76
|
+
* itself because the registration has to own the element: Base UI needs to put
|
|
77
|
+
* its own `tabIndex` and key handling on the rendered node, and the
|
|
78
|
+
* `focusableWhenDisabled` behaviour below depends on NOT setting the native
|
|
79
|
+
* `disabled` attribute, which `Button` sets unconditionally.
|
|
80
|
+
*
|
|
81
|
+
* `ghost` at `sm` by default — the right resting state in a row of controls,
|
|
82
|
+
* the same default `IconButton` picks. A row of solid buttons reads as a row of
|
|
83
|
+
* primary actions, which no toolbar is.
|
|
84
|
+
*
|
|
85
|
+
* ANYTHING ALREADY BUILT ON `Button` GOES IN THROUGH `render`, which is what
|
|
86
|
+
* keeps this from becoming a second button component:
|
|
87
|
+
* `<ToolbarButton render={<IconButton icon={…} label="Bold" />} />` registers
|
|
88
|
+
* exactly the same, keeps the tooltip and the `disabledReason`, and never
|
|
89
|
+
* duplicates a variant table.
|
|
90
|
+
*/
|
|
91
|
+
export declare function ToolbarButton({ variant, size, iconOnly, className, children, ref, ...props }: ToolbarButtonProps): import("react").JSX.Element;
|
|
92
|
+
export interface ToolbarInputProps extends Omit<InputProps, "ref">, Pick<BaseToolbar.Input.Props, "focusableWhenDisabled"> {
|
|
93
|
+
ref?: Ref<HTMLInputElement>;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* A text field that is part of the toolbar's rotation — a filter box beside the
|
|
97
|
+
* buttons that filter with it.
|
|
98
|
+
*
|
|
99
|
+
* Renders this library's own `Input` through Base UI's `render` slot, so the
|
|
100
|
+
* adornments, sizes and focus ring are the ones every other field in the
|
|
101
|
+
* application already has. The composite's props land on the `<input>` itself
|
|
102
|
+
* rather than on `Input`'s adornment wrapper, which is what keeps DOM order —
|
|
103
|
+
* and therefore the arrow-key order — matching what the reader sees.
|
|
104
|
+
*
|
|
105
|
+
* Arrow keys still move OUT of it, which is Base UI's deliberate call and not
|
|
106
|
+
* an oversight: a toolbar field is a filter box, not a document, and a caret
|
|
107
|
+
* that traps the only navigation key in the pattern is worse than one that
|
|
108
|
+
* moves on.
|
|
109
|
+
*/
|
|
110
|
+
export declare function ToolbarInput({ focusableWhenDisabled, ref, ...props }: ToolbarInputProps): import("react").JSX.Element;
|
|
111
|
+
export interface ToolbarGroupProps extends Omit<BaseToolbar.Group.Props, "className"> {
|
|
112
|
+
className?: string;
|
|
113
|
+
ref?: Ref<HTMLDivElement>;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* A labelled cluster of toolbar controls — "Alignment", "Text style".
|
|
117
|
+
*
|
|
118
|
+
* IT IS NOT ITSELF A STOP. The group renders `role="group"` and its CHILDREN
|
|
119
|
+
* register; a group that registered would insert a phantom position between the
|
|
120
|
+
* controls it contains. Give it an `aria-label`: a group announced as an
|
|
121
|
+
* unnamed group is noise in the rotation rather than context for it.
|
|
122
|
+
*
|
|
123
|
+
* Distinct from `ToggleGroup`, which is a set of related toggles with a shared
|
|
124
|
+
* VALUE. This one shares only a name.
|
|
125
|
+
*/
|
|
126
|
+
export declare function ToolbarGroup({ className, children, ref, ...props }: ToolbarGroupProps): import("react").JSX.Element;
|
|
65
127
|
export {};
|
|
@@ -1,13 +1,31 @@
|
|
|
1
1
|
import { type ReactNode, type Ref } from "react";
|
|
2
2
|
import type { ChatFile } from "./types";
|
|
3
|
-
/** Binary units, because these are file sizes and every OS reports them this way. */
|
|
4
|
-
export declare function formatBytes(bytes: number): string;
|
|
5
3
|
export type AttachmentLayout = "tile" | "row";
|
|
6
4
|
export interface AttachmentProps {
|
|
7
5
|
file: ChatFile;
|
|
8
6
|
layout?: AttachmentLayout;
|
|
9
7
|
/** Overrides the size line — "Sheet 2 of 4", a page count, anything truer. */
|
|
10
8
|
meta?: ReactNode;
|
|
9
|
+
/**
|
|
10
|
+
* The consumer's own control, rendered first in the action cluster — "Extract
|
|
11
|
+
* this", "Attach to the demand", "Cancel the upload".
|
|
12
|
+
*
|
|
13
|
+
* A SLOT RATHER THAN ANOTHER BOOLEAN, because the library has no opinion
|
|
14
|
+
* about what the verb is and a fixed list would be wrong for the second
|
|
15
|
+
* consumer and every one after. Remove, preview and download stay separate
|
|
16
|
+
* props: they are the three actions an attachment has REGARDLESS of the
|
|
17
|
+
* application, and each carries its own rule about when it may appear.
|
|
18
|
+
*
|
|
19
|
+
* NO RULE IS IMPOSED ON THIS ONE. Download hides itself while a file is still
|
|
20
|
+
* uploading, because there is nothing complete to fetch; the consumer's action
|
|
21
|
+
* may well BE "cancel this upload", so it is rendered whatever the file's
|
|
22
|
+
* state, and the consumer decides whether their verb makes sense yet.
|
|
23
|
+
*
|
|
24
|
+
* It goes inside the cluster, above the full-card preview trigger — rendered
|
|
25
|
+
* anywhere else it would sit UNDER that trigger and never receive a click.
|
|
26
|
+
* Keep it to a control or two: this is a corner of a tile, not a menu bar.
|
|
27
|
+
*/
|
|
28
|
+
action?: ReactNode;
|
|
11
29
|
onRemove?: (file: ChatFile) => void;
|
|
12
30
|
onPreview?: (file: ChatFile) => void;
|
|
13
31
|
onDownload?: (file: ChatFile) => void;
|
|
@@ -17,7 +35,7 @@ export interface AttachmentProps {
|
|
|
17
35
|
className?: string;
|
|
18
36
|
ref?: Ref<HTMLDivElement>;
|
|
19
37
|
}
|
|
20
|
-
export declare function Attachment({ file, layout, meta, onRemove, onPreview, onDownload, selected, disabled, className, ref, }: AttachmentProps): import("react").JSX.Element;
|
|
38
|
+
export declare function Attachment({ file, layout, meta, action, onRemove, onPreview, onDownload, selected, disabled, className, ref, }: AttachmentProps): import("react").JSX.Element;
|
|
21
39
|
export interface AttachmentPreviewProps {
|
|
22
40
|
file: ChatFile | null;
|
|
23
41
|
onOpenChange: (open: boolean) => void;
|
|
@@ -38,6 +56,15 @@ export declare function AttachmentPreview({ file, onOpenChange, onDownload, rend
|
|
|
38
56
|
export interface AttachmentListProps {
|
|
39
57
|
files: ChatFile[];
|
|
40
58
|
layout?: AttachmentLayout;
|
|
59
|
+
/**
|
|
60
|
+
* `Attachment.action`, per file — a FUNCTION rather than a node, because the
|
|
61
|
+
* control almost always has to know which file it is on.
|
|
62
|
+
*
|
|
63
|
+
* Without it the slot would exist only on a lone `Attachment`, and a consumer
|
|
64
|
+
* wanting one on a list would have to stop using the list — which is the
|
|
65
|
+
* rebuild this slot was added to prevent, one level up.
|
|
66
|
+
*/
|
|
67
|
+
action?: (file: ChatFile) => ReactNode;
|
|
41
68
|
onRemove?: (file: ChatFile) => void;
|
|
42
69
|
onPreview?: (file: ChatFile) => void;
|
|
43
70
|
onDownload?: (file: ChatFile) => void;
|
|
@@ -46,4 +73,4 @@ export interface AttachmentListProps {
|
|
|
46
73
|
disabled?: boolean;
|
|
47
74
|
className?: string;
|
|
48
75
|
}
|
|
49
|
-
export declare function AttachmentList({ files, layout, onRemove, onPreview, onDownload, max, disabled, className, }: AttachmentListProps): import("react").JSX.Element | null;
|
|
76
|
+
export declare function AttachmentList({ files, layout, action, onRemove, onPreview, onDownload, max, disabled, className, }: AttachmentListProps): import("react").JSX.Element | null;
|
|
@@ -6,16 +6,6 @@ export interface ContextSegment {
|
|
|
6
6
|
/** Any CSS colour. Defaults walk a fixed palette in declaration order. */
|
|
7
7
|
color?: string;
|
|
8
8
|
}
|
|
9
|
-
/**
|
|
10
|
-
* 241_600 → "241.6k".
|
|
11
|
-
*
|
|
12
|
-
* One decimal all the way to 1M, and exact below 1k. Unlike a file size — where
|
|
13
|
-
* a tenth of a kilobyte is noise on a number nobody acts on — the tenth here is
|
|
14
|
-
* the digit that moves while you work: at 1M capacity, a whole percent is
|
|
15
|
-
* 10_000 tokens, so rounding to "242k" holds a figure still for several
|
|
16
|
-
* exchanges and makes a live meter look frozen.
|
|
17
|
-
*/
|
|
18
|
-
export declare function formatTokens(tokens: number): string;
|
|
19
9
|
export interface UsageLimit {
|
|
20
10
|
id: string;
|
|
21
11
|
label: string;
|
package/dist/types/index.d.ts
CHANGED
|
@@ -14,7 +14,7 @@ export { registerIcon, registerIcons, getIcon, hasIcon, iconNames, type IconNode
|
|
|
14
14
|
export { coreIcons, type CoreIconName } from "./icons/data";
|
|
15
15
|
export { Button, buttonVariants, type ButtonProps } from "./components/Button";
|
|
16
16
|
export { IconButton, type IconButtonProps, type IconButtonVariant, type IconButtonTone, } from "./components/IconButton";
|
|
17
|
-
export { Badge, badgeVariants, type BadgeProps } from "./components/Badge";
|
|
17
|
+
export { Badge, badgeVariants, type BadgeProps, type BadgeDismiss, } from "./components/Badge";
|
|
18
18
|
export { Callout, calloutVariants, type CalloutProps } from "./components/Callout";
|
|
19
19
|
export { Spinner, type SpinnerProps } from "./components/Spinner";
|
|
20
20
|
export { Skeleton, SkeletonText, LoadingOverlay, type SkeletonProps, type SkeletonTextProps, type LoadingOverlayProps, } from "./components/Loading";
|
|
@@ -48,7 +48,7 @@ export { Accordion, AccordionItem, type AccordionProps, type AccordionItemProps,
|
|
|
48
48
|
export { Breadcrumb, Pagination, type BreadcrumbProps, type PaginationProps, type Crumb, } from "./components/Navigation";
|
|
49
49
|
export { Stepper, stepStatus, stepperProgress, advanceBlockReason, assertSteps, type StepperProps, type Step, type StepStatus, type StepperProgress, } from "./components/stepper";
|
|
50
50
|
export { StatusChecklist, tallyChecklist, type StatusChecklistProps, type ChecklistItem, type ChecklistState, type ChecklistTally, } from "./components/checklist";
|
|
51
|
-
export { Toggle, ToggleGroup, Toolbar, ToolbarSeparator, type ToggleProps, type ToggleGroupProps, type ToolbarProps, } from "./components/Toggle";
|
|
51
|
+
export { Toggle, ToggleGroup, Toolbar, ToolbarButton, ToolbarGroup, ToolbarInput, ToolbarSeparator, type ToggleProps, type ToggleGroupProps, type ToolbarProps, type ToolbarButtonProps, type ToolbarGroupProps, type ToolbarInputProps, } from "./components/Toggle";
|
|
52
52
|
export { Separator, ScrollArea, Collapsible, EmptyState, Timeline, type SeparatorProps, type ScrollAreaProps, type CollapsibleProps, type EmptyStateProps, type TimelineProps, type TimelineItem, } from "./components/Surface";
|
|
53
53
|
export { PanelGroup, Panel, PanelResizer, type PanelGroupProps, type PanelProps, type PanelResizerProps, type PanelDirection, type PanelNarrowMode, } from "./components/Panel";
|
|
54
54
|
export { Avatar, AvatarGroup, type AvatarProps, type AvatarGroupProps, type AvatarStatus, } from "./components/Avatar";
|
|
@@ -85,11 +85,12 @@ 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,
|
|
88
|
+
export { Attachment, AttachmentList, AttachmentPreview, type AttachmentProps, 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
|
-
export { ContextMeter, ChatStatus,
|
|
91
|
+
export { ContextMeter, ChatStatus, type ContextMeterProps, type ContextSegment, type UsageLimit, type ChatStatusProps, } from "./components/chat/ContextMeter";
|
|
92
92
|
export { cn } from "./lib/cn";
|
|
93
|
+
export { formatBytes, formatTokens } from "./lib/format";
|
|
93
94
|
export { useFillHeight } from "./lib/use-fill-height";
|
|
94
95
|
export { useTheme, useUiScale, useContrast, useMotionPreference, type Theme, type UiScale, type ContrastPreference, type MotionPreference, } from "./lib/preferences";
|
|
95
96
|
export { usePersistedState, readStored, writeStored, clearStored, storageKey, type PersistedStateOptions, } from "./lib/persist";
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** Binary units, because these are file sizes and every OS reports them this way. */
|
|
2
|
+
export declare function formatBytes(bytes: number): string;
|
|
3
|
+
/**
|
|
4
|
+
* 241_600 → "241.6k".
|
|
5
|
+
*
|
|
6
|
+
* One decimal all the way to 1M, and exact below 1k. Unlike a file size — where
|
|
7
|
+
* a tenth of a kilobyte is noise on a number nobody acts on — the tenth here is
|
|
8
|
+
* the digit that moves while you work: at 1M capacity, a whole percent is
|
|
9
|
+
* 10_000 tokens, so rounding to "242k" holds a figure still for several
|
|
10
|
+
* exchanges and makes a live meter look frozen.
|
|
11
|
+
*/
|
|
12
|
+
export declare function formatTokens(tokens: number): string;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@danxbot/ui/pure` — the half of this package that is not a UI.
|
|
3
|
+
*
|
|
4
|
+
* WHY A SECOND ENTRY EXISTS. The package exported exactly one, `.`, which is
|
|
5
|
+
* the whole design system. So an application's own formatting layer — a plain
|
|
6
|
+
* module that turns records into strings, imported by workers, tests, server
|
|
7
|
+
* code and components alike — could not reach `formatBytes` without importing
|
|
8
|
+
* React and every component in this library into a layer that is deliberately
|
|
9
|
+
* framework-free. The consumer's answer was to copy the function, which is how
|
|
10
|
+
* two places start rendering the same quantity to different precision.
|
|
11
|
+
*
|
|
12
|
+
* THE CONTRACT, AND IT IS THE ONLY REASON THIS ENTRY IS WORTH HAVING: nothing
|
|
13
|
+
* reachable from here imports React, `react-dom`, or the JSX runtime. An entry
|
|
14
|
+
* that quietly pulls the React graph back in is not a smaller import, it is the
|
|
15
|
+
* same import with a second name — and it would fail silently, because a
|
|
16
|
+
* consumer's bundle does not announce what got dragged into it.
|
|
17
|
+
*
|
|
18
|
+
* So the claim is checked against the BUILT ARTIFACT rather than reasoned about
|
|
19
|
+
* from the source: `pure-entry.test.ts` reads `dist/pure.js`, extracts every
|
|
20
|
+
* import specifier it actually emitted, and asserts none of them is React —
|
|
21
|
+
* plus a size ceiling, so React cannot have been inlined instead of imported.
|
|
22
|
+
* A source-graph sweep in the same file catches it one step earlier, before a
|
|
23
|
+
* build has run.
|
|
24
|
+
*
|
|
25
|
+
* WHAT BELONGS HERE. Decisions about how a value READS, with no dependencies:
|
|
26
|
+
* precision, units, the point at which a rounded figure stops telling the
|
|
27
|
+
* truth. Deliberately NOT the calendar and time layers — `formatDateRange`,
|
|
28
|
+
* `hoursInDay` and the rest are equally pure and would drag the whole `Intl`
|
|
29
|
+
* time layer behind them, which is a bigger entry answering a question nobody
|
|
30
|
+
* has asked yet. That is a separate decision, to be made when a consumer needs
|
|
31
|
+
* it and not before.
|
|
32
|
+
*
|
|
33
|
+
* Everything exported here is ALSO exported from the main entry, so nothing
|
|
34
|
+
* about this is a migration and no existing import has to change.
|
|
35
|
+
*/
|
|
36
|
+
export { formatBytes, formatTokens } from "./lib/format";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@danxbot/ui",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.9.0",
|
|
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",
|
|
@@ -35,6 +35,10 @@
|
|
|
35
35
|
"types": "./dist/types/index.d.ts",
|
|
36
36
|
"import": "./dist/index.js"
|
|
37
37
|
},
|
|
38
|
+
"./pure": {
|
|
39
|
+
"types": "./dist/types/pure.d.ts",
|
|
40
|
+
"import": "./dist/pure.js"
|
|
41
|
+
},
|
|
38
42
|
"./styles.css": "./dist/styles.css",
|
|
39
43
|
"./theme.css": "./dist/theme.css",
|
|
40
44
|
"./fonts.css": "./dist/fonts.css",
|
|
@@ -56,7 +60,7 @@
|
|
|
56
60
|
"audit:cards": "node scripts/audit-card-theme.mjs",
|
|
57
61
|
"audit:hover": "node scripts/audit-hover.mjs",
|
|
58
62
|
"test": "vitest run",
|
|
59
|
-
"build:lib": "vite build --config vite.lib.config.ts && node scripts/postbuild-css.mjs && npm run build:types",
|
|
63
|
+
"build:lib": "vite build --config vite.lib.config.ts && vite build --config vite.pure.config.ts && node scripts/postbuild-css.mjs && npm run build:types",
|
|
60
64
|
"fonts": "node scripts/fetch-fonts.mjs",
|
|
61
65
|
"icons": "node scripts/build-icons.mjs",
|
|
62
66
|
"build:types": "tsc -p tsconfig.build.json",
|