@danxbot/ui 3.7.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 +13332 -11864
- 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 +8 -4
- package/dist/types/lib/format.d.ts +12 -0
- package/dist/types/lib/reads/hooks.d.ts +49 -0
- package/dist/types/lib/reads/index.d.ts +4 -0
- package/dist/types/lib/reads/refetch.d.ts +52 -0
- package/dist/types/lib/reads/slots.d.ts +52 -0
- package/dist/types/lib/reads/types.d.ts +139 -0
- package/dist/types/lib/realtime/hooks.d.ts +38 -0
- package/dist/types/lib/realtime/index.d.ts +4 -0
- package/dist/types/lib/realtime/provider.d.ts +38 -0
- package/dist/types/lib/realtime/registry.d.ts +172 -0
- package/dist/types/lib/realtime/types.d.ts +202 -0
- package/dist/types/lib/records/define-record-type.d.ts +29 -0
- package/dist/types/lib/records/hooks.d.ts +46 -0
- package/dist/types/lib/records/index.d.ts +5 -0
- package/dist/types/lib/records/store.d.ts +117 -0
- package/dist/types/lib/records/types.d.ts +188 -0
- package/dist/types/lib/records/wire.d.ts +96 -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";
|
|
@@ -110,3 +111,6 @@ export { UseCaseCard, RoadmapItemCard, RoadmapSwimlaneBoard, UseCaseLedgerPage,
|
|
|
110
111
|
export { ProvenanceDisclosure, ProvenanceBadge, ConfidenceMeter, provenanceStateLabel, confidenceBand, type ProvenanceRecord, type ProvenanceState, type ProvenanceSource, type ProvenanceCheck, type ProvenanceCall, type ProvenanceSignoff, type ConfidenceBand, type ProvenanceDisclosureProps, type ProvenanceTriggerBadge, type ProvenanceBadgeProps, type ProvenanceBadgeAppearance, type ConfidenceMeterProps, } from "./components/provenance";
|
|
111
112
|
export { createSimStore, resetSimStores, useSimSettings, setSimSettings, getNetworkSpeed, getDataSource, simulateLatency, SimNetworkError, type SimStore, type SimStoreOptions, type SimSettings, type NetworkSpeed, type DataSource, type SimNamespace, } from "./lib/sim";
|
|
112
113
|
export { NodeCanvas, LAYOUT_DEFAULTS, layoutTree, pathToNode, type NodeCanvasProps, type CanvasEdge, type CanvasLayout, type CanvasNode, type CanvasOrientation, type LayoutOptions, type NodeRenderContext, type NodeRenderer, type PlacedNode, } from "./components/node-canvas";
|
|
114
|
+
export { defineRecordType, resetRecordTypes, useRecord, useRecords, useStaleness, write, writeAll, peekRecord, readState, readStaleness, subscribeToKey, subscribeToStaleness, retainKeys, sweepRecords, setRecordStoreTenant, getRecordStoreTenant, flushRecordNotifications, resetRecordStore, recordStoreStats, UNKNOWN_STATE, normalizeTypeName, normalizeTimestamp, recordKey, isRecordRef, type RecordRef, type ReadOptions, type RecordPayload, type RecordState, type RecordTypeOptions, type RecordTypeToken, type StalenessEntry, type WriteAllResult, type WriteOptions, type WriteResult, type WriteSource, type WriteVerdict, } from "./lib/records";
|
|
115
|
+
export { RealtimeProvider, useRealtime, useRealtimeEpoch, useRealtimeHealth, useRealtimeRegistry, SubscriptionRegistry, normalizeScope, scopeKey, readRealtimeEpoch, subscribeToRealtimeEpoch, subscribeToRealtimeEvents, resetRealtimeSignals, type RealtimeChannel, type RealtimeConnectionState, type RealtimeDiagnostic, type RealtimeEvent, type RealtimeHealth, type RealtimeProviderProps, type RealtimeScope, type RealtimeScopeInput, type RealtimeStatus, type RealtimeTransport, type RegistryOptions, type SubscriptionHealth, type SubscriptionLease, type SubscriptionRequest, } from "./lib/realtime";
|
|
116
|
+
export { useRecordRead, useRecordListRead, useRecordStoreSweep, applyMutationResult, acquireSlot, appendCreatedKey, emptySlotSnapshot, readSlot, resetSlots, slotKeys, subscribeToSlot, writeSlot, RefetchRunner, registerActiveRead, resetActiveReads, type ActiveRead, type ListFetch, type ListFetchResult, type ListReadOptions, type ListReadResult, type ReadError, type RecordFetch, type RecordReadOptions, type RecordReadResult, type RunnerOptions, type SlotSnapshot, } from "./lib/reads";
|
|
@@ -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,49 @@
|
|
|
1
|
+
import { type RecordPayload, type RecordTypeToken, type WriteResult } from "../records";
|
|
2
|
+
import type { ListReadOptions, ListReadResult, RecordReadOptions, RecordReadResult } from "./types";
|
|
3
|
+
/**
|
|
4
|
+
* Read one record: fetch it, write it into the store tagged with the field set
|
|
5
|
+
* it asked for, and return the store's answer.
|
|
6
|
+
*
|
|
7
|
+
* `id` may be null for a conditional read — the hook then fetches nothing and
|
|
8
|
+
* reports `unknown`, rather than forcing a caller around a rule of hooks.
|
|
9
|
+
*/
|
|
10
|
+
export declare function useRecordRead<T>(token: RecordTypeToken<T>, id: string | number | null | undefined, options: RecordReadOptions): RecordReadResult<T>;
|
|
11
|
+
/**
|
|
12
|
+
* Read a page: fetch it, write every row into the store, and record which keys
|
|
13
|
+
* answered the query in a LIST SLOT.
|
|
14
|
+
*
|
|
15
|
+
* The slot is what makes a `created` broadcast visible and a tombstone
|
|
16
|
+
* removable without a fetch — and it is where `meta` lives, because pagination
|
|
17
|
+
* has no identity and can never be a record.
|
|
18
|
+
*/
|
|
19
|
+
export declare function useRecordListRead<T, TMeta = unknown>(token: RecordTypeToken<T>, options: ListReadOptions<TMeta>): ListReadResult<T, TMeta>;
|
|
20
|
+
/**
|
|
21
|
+
* Write a mutation's returned item into the store.
|
|
22
|
+
*
|
|
23
|
+
* A MUTATION RESPONSE IS A FETCH, and this exists because that is a question an
|
|
24
|
+
* implementer will otherwise answer by coin flip. It is a full server answer to
|
|
25
|
+
* a request this client made: it may satisfy a field-set tag, it clears
|
|
26
|
+
* staleness on every field it carries, and it must NEVER be dropped by the
|
|
27
|
+
* self-echo filter — that filter applies to broadcasts, and dropping a
|
|
28
|
+
* mutation's own response would discard the server-computed fields the caller
|
|
29
|
+
* asked for. Structurally it cannot be dropped, because the filter lives on the
|
|
30
|
+
* broadcast path and this does not go through it.
|
|
31
|
+
*/
|
|
32
|
+
export declare function applyMutationResult(payload: RecordPayload, options: {
|
|
33
|
+
tag: string;
|
|
34
|
+
tenant?: string | number;
|
|
35
|
+
}): WriteResult;
|
|
36
|
+
/**
|
|
37
|
+
* Sweep unreachable records on a bounded schedule, for as long as this is
|
|
38
|
+
* mounted.
|
|
39
|
+
*
|
|
40
|
+
* IT IS A HOOK, NOT AN INTERVAL STARTED ON IMPORT. A design-system module that
|
|
41
|
+
* schedules work because somebody imported it is a worse defect than the leak
|
|
42
|
+
* it prevents — it runs in every consumer, including ones with no store at all.
|
|
43
|
+
* So the library exports the sweep and the CONSUMER decides when: this hook at
|
|
44
|
+
* an app root, a call at a navigation boundary, or both. A navigation boundary
|
|
45
|
+
* alone is not enough, because a broadcast can create a key no root reaches and
|
|
46
|
+
* a screen nobody navigates away from accumulates those for as long as the tab
|
|
47
|
+
* is open.
|
|
48
|
+
*/
|
|
49
|
+
export declare function useRecordStoreSweep(intervalMs?: number): void;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { useRecordRead, useRecordListRead, applyMutationResult, useRecordStoreSweep } from "./hooks";
|
|
2
|
+
export { acquireSlot, appendCreatedKey, emptySlotSnapshot, readSlot, resetSlots, slotKeys, subscribeToSlot, writeSlot, type SlotSnapshot, } from "./slots";
|
|
3
|
+
export { RefetchRunner, registerActiveRead, resetActiveReads, type ActiveRead, type RunnerOptions } from "./refetch";
|
|
4
|
+
export type { ListFetch, ListFetchResult, ListReadOptions, ListReadResult, ReadError, RecordFetch, RecordReadOptions, RecordReadResult, } from "./types";
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
export interface RunnerOptions {
|
|
2
|
+
readonly minIntervalMs?: number;
|
|
3
|
+
/** Test seam. Defaults to `Date.now`. */
|
|
4
|
+
readonly now?: () => number;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* One read's refetch discipline: a floor between runs, and one request in
|
|
8
|
+
* flight at a time.
|
|
9
|
+
*
|
|
10
|
+
* A SECOND SIGNAL ARRIVING MID-FLIGHT SETS A RE-RUN FLAG rather than issuing a
|
|
11
|
+
* second request. Two overlapping refetches of the same target are not twice as
|
|
12
|
+
* fresh; they are two answers racing to write, and the loser can be the newer
|
|
13
|
+
* one.
|
|
14
|
+
*/
|
|
15
|
+
export declare class RefetchRunner {
|
|
16
|
+
private readonly run;
|
|
17
|
+
private inFlight;
|
|
18
|
+
private rerun;
|
|
19
|
+
private lastFinishedAt;
|
|
20
|
+
private timer;
|
|
21
|
+
private disposed;
|
|
22
|
+
private readonly minIntervalMs;
|
|
23
|
+
private readonly now;
|
|
24
|
+
constructor(run: () => Promise<void>, options?: RunnerOptions);
|
|
25
|
+
/** Ask for a refetch. Respects the floor and the in-flight request. */
|
|
26
|
+
request(): void;
|
|
27
|
+
/**
|
|
28
|
+
* Run now, floor and all.
|
|
29
|
+
*
|
|
30
|
+
* For the FIRST fetch and for a key change — neither is a refetch, and making
|
|
31
|
+
* a fresh screen wait out an interval that exists to damp a broadcast stream
|
|
32
|
+
* would be the floor working against its own purpose.
|
|
33
|
+
*/
|
|
34
|
+
runNow(): void;
|
|
35
|
+
private execute;
|
|
36
|
+
dispose(): void;
|
|
37
|
+
}
|
|
38
|
+
/** One mounted read, as the driver sees it. */
|
|
39
|
+
export interface ActiveRead {
|
|
40
|
+
/** The keys this read is responsible for. Re-read per signal: a list's move. */
|
|
41
|
+
roots(): readonly string[];
|
|
42
|
+
/** Ask this read to re-issue the request it already made. */
|
|
43
|
+
request(): void;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Register a read so staleness and liveness recovery can reach it.
|
|
47
|
+
*
|
|
48
|
+
* @returns unregister
|
|
49
|
+
*/
|
|
50
|
+
export declare function registerActiveRead(read: ActiveRead): () => void;
|
|
51
|
+
/** Test seam. Nothing in the library calls this. */
|
|
52
|
+
export declare function resetActiveReads(): void;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
export interface SlotSnapshot<T = unknown, TMeta = unknown> {
|
|
2
|
+
readonly items: readonly T[];
|
|
3
|
+
readonly pending: readonly T[];
|
|
4
|
+
readonly pendingCount: number;
|
|
5
|
+
readonly meta: TMeta | undefined;
|
|
6
|
+
/** Every key the slot holds, fetched and pending. The refetch roots. */
|
|
7
|
+
readonly keys: readonly string[];
|
|
8
|
+
}
|
|
9
|
+
/** `getServerSnapshot`, and the answer for a slot nothing has written yet. */
|
|
10
|
+
export declare function emptySlotSnapshot(): SlotSnapshot;
|
|
11
|
+
/**
|
|
12
|
+
* Claim a slot for the duration of the returned release.
|
|
13
|
+
*
|
|
14
|
+
* REFCOUNTED AND IDEMPOTENT, which is what makes StrictMode's mount / unmount /
|
|
15
|
+
* mount leave the slot — and its retained keys — exactly as it found them.
|
|
16
|
+
*/
|
|
17
|
+
export declare function acquireSlot(id: string, options: {
|
|
18
|
+
type: string;
|
|
19
|
+
tag: string;
|
|
20
|
+
acceptsCreated: boolean;
|
|
21
|
+
}): () => void;
|
|
22
|
+
/**
|
|
23
|
+
* Replace a slot's membership with a fetch's answer.
|
|
24
|
+
*
|
|
25
|
+
* A key that arrived through a `created` event and is now in the fetched page
|
|
26
|
+
* stops being pending — the fetch has placed it, which is the only thing that
|
|
27
|
+
* ever could.
|
|
28
|
+
*/
|
|
29
|
+
export declare function writeSlot(id: string, keys: readonly string[], meta: unknown): void;
|
|
30
|
+
/**
|
|
31
|
+
* Append a record that entered the set elsewhere.
|
|
32
|
+
*
|
|
33
|
+
* WITH NO POSITION. The server owns sort order and page boundary; a client that
|
|
34
|
+
* inserts at a guessed index renders a list that is subtly, confidently wrong.
|
|
35
|
+
*/
|
|
36
|
+
export declare function appendCreatedKey(type: string, key: string): void;
|
|
37
|
+
export declare function subscribeToSlot(id: string, listener: () => void): () => void;
|
|
38
|
+
/** The keys a slot holds right now — the roots a targeted refetch matches on. */
|
|
39
|
+
export declare function slotKeys(id: string): readonly string[];
|
|
40
|
+
/**
|
|
41
|
+
* The slot's projection, memoized to a stable reference.
|
|
42
|
+
*
|
|
43
|
+
* IT RECOMPUTES AND THEN COMPARES rather than trusting its dirty flag, because
|
|
44
|
+
* a member notification does not always change what this slot renders — a
|
|
45
|
+
* record whose field-set tag is still unsatisfied moves nothing here — and
|
|
46
|
+
* handing back a fresh array for an unchanged list re-renders every consumer
|
|
47
|
+
* for nothing. The elements themselves are the store's own reference-stable
|
|
48
|
+
* snapshots, so comparing them is one identity check each.
|
|
49
|
+
*/
|
|
50
|
+
export declare function readSlot(id: string): SlotSnapshot;
|
|
51
|
+
/** Test seam. Nothing in the library calls this. */
|
|
52
|
+
export declare function resetSlots(): void;
|