harebear-ui 0.10.0 → 0.11.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/components/admin/detail-sheet.js +3 -0
- package/dist/components/ai/action-receipt.d.ts +1 -1
- package/dist/components/ai/action-receipt.js +1 -1
- package/dist/components/ai/approval.d.ts +1 -1
- package/dist/components/ai/approval.js +2 -2
- package/dist/components/ai/ask-box.js +1 -1
- package/dist/components/ai/assistant.d.ts +3 -1
- package/dist/components/ai/assistant.js +2 -1
- package/dist/components/ai/chat.js +1 -1
- package/dist/components/ai/tool-steps.d.ts +2 -2
- package/dist/components/ai/tool-steps.js +5 -4
- package/dist/components/ui/dialog.d.ts +1 -1
- package/dist/components/ui/dialog.js +3 -1
- package/dist/components/ui/drawer.d.ts +1 -1
- package/dist/components/ui/drawer.js +3 -1
- package/dist/components/ui/sheet.d.ts +1 -1
- package/dist/components/ui/sheet.js +3 -1
- package/dist/components/ui/sonner.js +2 -1
- package/dist/components/ui/trace.js +1 -1
- package/dist/lib/internal/toast-clearance.js +11 -1
- package/dist/styles.css +19 -4
- package/docs/COMPONENTS.md +7 -7
- package/docs/ROADMAP.md +1 -1
- package/package.json +1 -1
|
@@ -3,6 +3,7 @@ import { Button } from "../ui/button.js";
|
|
|
3
3
|
import { Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerTitle } from "../ui/drawer.js";
|
|
4
4
|
import { Sheet, SheetContent, SheetDescription, SheetTitle } from "../ui/sheet.js";
|
|
5
5
|
import { useMediaQuery } from "../../hooks/use-media-query.js";
|
|
6
|
+
import { useToastClearance } from "../../lib/internal/toast-clearance.js";
|
|
6
7
|
import { cn } from "../../lib/utils.js";
|
|
7
8
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
8
9
|
import { XIcon } from "lucide-react";
|
|
@@ -13,6 +14,8 @@ import { XIcon } from "lucide-react";
|
|
|
13
14
|
*/
|
|
14
15
|
function DetailSheet({ open, onOpenChange, title, description, footer, className, children }) {
|
|
15
16
|
const desktop = useMediaQuery("(min-width: 768px)");
|
|
17
|
+
useToastClearance("--hb-toast-sheet", "calc(min(420px, 100vw - 32px) + 1rem)", open && desktop);
|
|
18
|
+
useToastClearance("--hb-toast-drawer", footer ? "calc(3.5625rem + max(1rem, env(safe-area-inset-bottom)))" : "env(safe-area-inset-bottom, 0px)", open && !desktop);
|
|
16
19
|
const noDescription = description ? {} : { "aria-describedby": void 0 };
|
|
17
20
|
const body = (padBottom) => /* @__PURE__ */ jsx("div", {
|
|
18
21
|
className: cn("min-h-0 flex-1 overflow-y-auto px-6 pb-6", !footer && padBottom),
|
|
@@ -2,7 +2,7 @@ import * as React from "react";
|
|
|
2
2
|
export interface ActionReceiptProps extends Omit<React.ComponentProps<"div">, "title"> {
|
|
3
3
|
/** What was done, past tense: "Added milk to Groceries". */
|
|
4
4
|
title: React.ReactNode;
|
|
5
|
-
/**
|
|
5
|
+
/** Short line under the title, e.g. "Groceries · just now". Wraps when long. */
|
|
6
6
|
meta?: React.ReactNode;
|
|
7
7
|
/** Shows an Undo button. */
|
|
8
8
|
onUndo?: () => void;
|
|
@@ -26,7 +26,7 @@ function ActionReceipt({ title, meta, onUndo, undoLabel = "Undo", undone, classN
|
|
|
26
26
|
className: cn("text-sm font-medium wrap-anywhere", undone && "text-muted-foreground line-through"),
|
|
27
27
|
children: title
|
|
28
28
|
}), meta && /* @__PURE__ */ jsx("span", {
|
|
29
|
-
className: "
|
|
29
|
+
className: "text-xs text-muted-foreground wrap-anywhere",
|
|
30
30
|
children: meta
|
|
31
31
|
})]
|
|
32
32
|
}),
|
|
@@ -27,6 +27,6 @@ export interface ApprovalResultProps extends React.ComponentProps<"div"> {
|
|
|
27
27
|
/** Trailing time, e.g. "10:42". */
|
|
28
28
|
time?: React.ReactNode;
|
|
29
29
|
}
|
|
30
|
-
/**
|
|
30
|
+
/** Short record of a decision in the thread, e.g. "You approved `docker restart x`, once". */
|
|
31
31
|
declare function ApprovalResult({ decision, time, className, children, ...props }: ApprovalResultProps): React.JSX.Element;
|
|
32
32
|
export { ApprovalCard, ApprovalResult };
|
|
@@ -56,7 +56,7 @@ function ApprovalCard({ title, description, icon = /* @__PURE__ */ jsx(ShieldAle
|
|
|
56
56
|
]
|
|
57
57
|
});
|
|
58
58
|
}
|
|
59
|
-
/**
|
|
59
|
+
/** Short record of a decision in the thread, e.g. "You approved `docker restart x`, once". */
|
|
60
60
|
function ApprovalResult({ decision, time, className, children, ...props }) {
|
|
61
61
|
const denied = decision === "denied";
|
|
62
62
|
return /* @__PURE__ */ jsxs("div", {
|
|
@@ -71,7 +71,7 @@ function ApprovalResult({ decision, time, className, children, ...props }) {
|
|
|
71
71
|
children: denied ? /* @__PURE__ */ jsx(XIcon, {}) : /* @__PURE__ */ jsx(CheckIcon, {})
|
|
72
72
|
}),
|
|
73
73
|
/* @__PURE__ */ jsx("span", {
|
|
74
|
-
className: "min-w-0 flex-1
|
|
74
|
+
className: "min-w-0 flex-1 wrap-anywhere",
|
|
75
75
|
children
|
|
76
76
|
}),
|
|
77
77
|
time && /* @__PURE__ */ jsx("span", {
|
|
@@ -91,7 +91,7 @@ function AskBox({ value, onValueChange, onSubmit, placeholder = "Ask anything",
|
|
|
91
91
|
"data-slot": "ask-box-reply",
|
|
92
92
|
"aria-live": "polite",
|
|
93
93
|
"aria-busy": pending || void 0,
|
|
94
|
-
className: "flex min-w-0 flex-col gap-3 px-2 text-[15px] leading-relaxed",
|
|
94
|
+
className: "flex min-w-0 flex-col gap-3 px-2 text-[15px] leading-relaxed wrap-anywhere",
|
|
95
95
|
children: [hasReply ? reply : /* @__PURE__ */ jsx(TypingIndicator, { label: "Working" }), chatHref && /* @__PURE__ */ jsx(Button, {
|
|
96
96
|
asChild: true,
|
|
97
97
|
variant: "link",
|
|
@@ -80,6 +80,8 @@ export interface AssistantDockProps extends Omit<React.ComponentProps<"section">
|
|
|
80
80
|
icon?: React.ReactNode;
|
|
81
81
|
/** Link to the full-screen chat. Shows an expand button in the header. */
|
|
82
82
|
fullScreenHref?: string;
|
|
83
|
+
/** Header controls before the full-screen link and close button, e.g. a "New chat" icon button. */
|
|
84
|
+
actions?: React.ReactNode;
|
|
83
85
|
/** Empty state heading. Default "How can I help?". */
|
|
84
86
|
emptyTitle?: React.ReactNode;
|
|
85
87
|
/** Empty state line under the heading. */
|
|
@@ -100,5 +102,5 @@ export interface AssistantDockProps extends Omit<React.ComponentProps<"section">
|
|
|
100
102
|
* suggestions, message list, AskBox. A soft launcher button opens it. Escape closes it. Needs AssistantProvider.
|
|
101
103
|
* Sits above AppNav's phone tab bar.
|
|
102
104
|
*/
|
|
103
|
-
declare function AssistantDock({ title, icon, fullScreenHref, emptyTitle, emptyDescription, suggestions, placeholder, dictation, launcher, launcherLabel, className, ...props }: AssistantDockProps): React.JSX.Element | null;
|
|
105
|
+
declare function AssistantDock({ title, icon, fullScreenHref, actions, emptyTitle, emptyDescription, suggestions, placeholder, dictation, launcher, launcherLabel, className, ...props }: AssistantDockProps): React.JSX.Element | null;
|
|
104
106
|
export { AssistantDock, AssistantProvider };
|
|
@@ -144,7 +144,7 @@ function useAssistantPage(page) {
|
|
|
144
144
|
* suggestions, message list, AskBox. A soft launcher button opens it. Escape closes it. Needs AssistantProvider.
|
|
145
145
|
* Sits above AppNav's phone tab bar.
|
|
146
146
|
*/
|
|
147
|
-
function AssistantDock({ title = "Assistant", icon = /* @__PURE__ */ jsx(SparklesIcon, {}), fullScreenHref, emptyTitle = "How can I help?", emptyDescription, suggestions, placeholder, dictation, launcher = true, launcherLabel, className, ...props }) {
|
|
147
|
+
function AssistantDock({ title = "Assistant", icon = /* @__PURE__ */ jsx(SparklesIcon, {}), fullScreenHref, actions, emptyTitle = "How can I help?", emptyDescription, suggestions, placeholder, dictation, launcher = true, launcherLabel, className, ...props }) {
|
|
148
148
|
const { open, setOpen, messages, pending, page, send, stop } = useAssistant();
|
|
149
149
|
const [draft, setDraft] = React.useState("");
|
|
150
150
|
const titleId = React.useId();
|
|
@@ -200,6 +200,7 @@ function AssistantDock({ title = "Assistant", icon = /* @__PURE__ */ jsx(Sparkle
|
|
|
200
200
|
className: "min-w-0 flex-1 truncate text-[15px] font-semibold",
|
|
201
201
|
children: title
|
|
202
202
|
}),
|
|
203
|
+
actions,
|
|
203
204
|
fullScreenHref && /* @__PURE__ */ jsx(Button, {
|
|
204
205
|
asChild: true,
|
|
205
206
|
variant: "ghost",
|
|
@@ -59,7 +59,7 @@ function ChatMessage({ role, avatar, actions, variant = "muted", className, chil
|
|
|
59
59
|
className: cn("flex items-start gap-4", className),
|
|
60
60
|
...props,
|
|
61
61
|
children: [avatar === false ? null : avatar ?? /* @__PURE__ */ jsx(AssistantAvatar, {}), /* @__PURE__ */ jsxs("div", {
|
|
62
|
-
className: "flex min-w-0 flex-1 flex-col gap-3.5",
|
|
62
|
+
className: "flex min-w-0 flex-1 flex-col gap-3.5 wrap-anywhere",
|
|
63
63
|
children: [children, actions]
|
|
64
64
|
})]
|
|
65
65
|
});
|
|
@@ -24,9 +24,9 @@ declare function ToolGroup({ title, status, meta, autoOpen, open: openProp, onOp
|
|
|
24
24
|
export interface ToolStepProps extends Omit<React.ComponentProps<"div">, "title"> {
|
|
25
25
|
/** Tool icon, e.g. <TerminalIcon />. */
|
|
26
26
|
icon?: React.ReactNode;
|
|
27
|
-
/** Short verb column, e.g. "Run", "Read", "Edit". */
|
|
27
|
+
/** Short verb column, e.g. "Run", "Read", "Edit". With an empty `detail` it is the whole label, e.g. "Added milk". */
|
|
28
28
|
kind: React.ReactNode;
|
|
29
|
-
/** What it acted on, shown monospace: the command or path. */
|
|
29
|
+
/** What it acted on, shown monospace: the command or path. Empty for a plain-language step. */
|
|
30
30
|
detail: string;
|
|
31
31
|
status: ToolStatus;
|
|
32
32
|
/** Trailing text. Default derived from status ("running", "failed"…). */
|
|
@@ -52,7 +52,7 @@ function ToolGroup({ title, status, meta, autoOpen, open: openProp, onOpenChange
|
|
|
52
52
|
children: [
|
|
53
53
|
/* @__PURE__ */ jsx(ChevronRightIcon, { className: cn("size-4 shrink-0 text-muted-foreground transition-transform", open && "rotate-90") }),
|
|
54
54
|
/* @__PURE__ */ jsx("span", {
|
|
55
|
-
className: "min-w-0 flex-1
|
|
55
|
+
className: "min-w-0 flex-1 font-medium wrap-anywhere",
|
|
56
56
|
children: title
|
|
57
57
|
}),
|
|
58
58
|
meta !== void 0 && /* @__PURE__ */ jsx("span", {
|
|
@@ -85,11 +85,12 @@ function ToolStep({ icon, kind, detail, status, meta, output, autoOpen, classNam
|
|
|
85
85
|
children: icon
|
|
86
86
|
}),
|
|
87
87
|
/* @__PURE__ */ jsx("span", {
|
|
88
|
-
|
|
88
|
+
"data-slot": "tool-step-kind",
|
|
89
|
+
className: cn("text-[13px] text-muted-foreground wrap-anywhere", detail ? "w-18 shrink-0" : "min-w-0 flex-1 text-foreground"),
|
|
89
90
|
children: kind
|
|
90
91
|
}),
|
|
91
|
-
/* @__PURE__ */ jsx("span", {
|
|
92
|
-
className: "min-w-0 flex-1
|
|
92
|
+
detail && /* @__PURE__ */ jsx("span", {
|
|
93
|
+
className: "min-w-0 flex-1 font-mono text-[12.5px] wrap-anywhere",
|
|
93
94
|
children: detail
|
|
94
95
|
}),
|
|
95
96
|
/* @__PURE__ */ jsx("span", {
|
|
@@ -5,7 +5,7 @@ declare function DialogTrigger({ ...props }: React.ComponentProps<typeof Dialog$
|
|
|
5
5
|
declare function DialogPortal({ ...props }: React.ComponentProps<typeof Dialog$1.Portal>): React.JSX.Element;
|
|
6
6
|
declare function DialogClose({ ...props }: React.ComponentProps<typeof Dialog$1.Close>): React.JSX.Element;
|
|
7
7
|
declare function DialogOverlay({ className, ...props }: React.ComponentProps<typeof Dialog$1.Overlay>): React.JSX.Element;
|
|
8
|
-
declare function DialogContent({ className, children, showCloseButton, ...props }: React.ComponentProps<typeof Dialog$1.Content> & {
|
|
8
|
+
declare function DialogContent({ className, children, showCloseButton, onInteractOutside, ...props }: React.ComponentProps<typeof Dialog$1.Content> & {
|
|
9
9
|
showCloseButton?: boolean;
|
|
10
10
|
}): React.JSX.Element;
|
|
11
11
|
declare function DialogHeader({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
|
+
import { keepOpenOnToast } from "../../lib/internal/toast-clearance.js";
|
|
2
3
|
import { cn } from "../../lib/utils.js";
|
|
3
4
|
import { Button } from "./button.js";
|
|
4
5
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -36,11 +37,12 @@ function DialogOverlay({ className, ...props }) {
|
|
|
36
37
|
...props
|
|
37
38
|
});
|
|
38
39
|
}
|
|
39
|
-
function DialogContent({ className, children, showCloseButton = true, ...props }) {
|
|
40
|
+
function DialogContent({ className, children, showCloseButton = true, onInteractOutside, ...props }) {
|
|
40
41
|
return /* @__PURE__ */ jsxs(DialogPortal, { children: [/* @__PURE__ */ jsx(DialogOverlay, {}), /* @__PURE__ */ jsxs(Dialog$1.Content, {
|
|
41
42
|
"data-slot": "dialog-content",
|
|
42
43
|
className: cn("fixed top-1/2 left-1/2 z-50 grid w-full max-w-[calc(100%-2rem)] -translate-x-1/2 -translate-y-1/2 gap-4 rounded-xl bg-popover p-4 text-sm text-popover-foreground ring-1 ring-foreground/10 duration-100 outline-none sm:max-w-sm data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", className),
|
|
43
44
|
...props,
|
|
45
|
+
onInteractOutside: keepOpenOnToast(onInteractOutside),
|
|
44
46
|
children: [children, showCloseButton && /* @__PURE__ */ jsx(Dialog$1.Close, {
|
|
45
47
|
"data-slot": "dialog-close",
|
|
46
48
|
asChild: true,
|
|
@@ -5,7 +5,7 @@ declare function DrawerTrigger({ ...props }: React.ComponentProps<typeof Drawer$
|
|
|
5
5
|
declare function DrawerPortal({ ...props }: React.ComponentProps<typeof Drawer$1.Portal>): React.JSX.Element;
|
|
6
6
|
declare function DrawerClose({ ...props }: React.ComponentProps<typeof Drawer$1.Close>): React.JSX.Element;
|
|
7
7
|
declare function DrawerOverlay({ className, ...props }: React.ComponentProps<typeof Drawer$1.Overlay>): React.JSX.Element;
|
|
8
|
-
declare function DrawerContent({ className, children, ...props }: React.ComponentProps<typeof Drawer$1.Content>): React.JSX.Element;
|
|
8
|
+
declare function DrawerContent({ className, children, onInteractOutside, ...props }: React.ComponentProps<typeof Drawer$1.Content>): React.JSX.Element;
|
|
9
9
|
declare function DrawerHeader({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
|
|
10
10
|
declare function DrawerFooter({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
|
|
11
11
|
declare function DrawerTitle({ className, ...props }: React.ComponentProps<typeof Drawer$1.Title>): React.JSX.Element;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
|
+
import { keepOpenOnToast } from "../../lib/internal/toast-clearance.js";
|
|
2
3
|
import { cn } from "../../lib/utils.js";
|
|
3
4
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
5
|
import { Drawer as Drawer$1 } from "vaul";
|
|
@@ -34,13 +35,14 @@ function DrawerOverlay({ className, ...props }) {
|
|
|
34
35
|
...props
|
|
35
36
|
});
|
|
36
37
|
}
|
|
37
|
-
function DrawerContent({ className, children, ...props }) {
|
|
38
|
+
function DrawerContent({ className, children, onInteractOutside, ...props }) {
|
|
38
39
|
return /* @__PURE__ */ jsxs(DrawerPortal, {
|
|
39
40
|
"data-slot": "drawer-portal",
|
|
40
41
|
children: [/* @__PURE__ */ jsx(DrawerOverlay, {}), /* @__PURE__ */ jsxs(Drawer$1.Content, {
|
|
41
42
|
"data-slot": "drawer-content",
|
|
42
43
|
className: cn("group/drawer-content fixed z-50 flex h-auto flex-col bg-popover text-sm text-popover-foreground data-[vaul-drawer-direction=bottom]:inset-x-0 data-[vaul-drawer-direction=bottom]:bottom-0 data-[vaul-drawer-direction=bottom]:mt-24 data-[vaul-drawer-direction=bottom]:max-h-[80vh] data-[vaul-drawer-direction=bottom]:rounded-t-xl data-[vaul-drawer-direction=bottom]:border-t data-[vaul-drawer-direction=left]:inset-y-0 data-[vaul-drawer-direction=left]:left-0 data-[vaul-drawer-direction=left]:w-3/4 data-[vaul-drawer-direction=left]:rounded-r-xl data-[vaul-drawer-direction=left]:border-r data-[vaul-drawer-direction=right]:inset-y-0 data-[vaul-drawer-direction=right]:right-0 data-[vaul-drawer-direction=right]:w-3/4 data-[vaul-drawer-direction=right]:rounded-l-xl data-[vaul-drawer-direction=right]:border-l data-[vaul-drawer-direction=top]:inset-x-0 data-[vaul-drawer-direction=top]:top-0 data-[vaul-drawer-direction=top]:mb-24 data-[vaul-drawer-direction=top]:max-h-[80vh] data-[vaul-drawer-direction=top]:rounded-b-xl data-[vaul-drawer-direction=top]:border-b data-[vaul-drawer-direction=left]:sm:max-w-sm data-[vaul-drawer-direction=right]:sm:max-w-sm", className),
|
|
43
44
|
...props,
|
|
45
|
+
onInteractOutside: keepOpenOnToast(onInteractOutside),
|
|
44
46
|
children: [/* @__PURE__ */ jsx("div", { className: "mx-auto mt-4 hidden h-1 w-[100px] shrink-0 rounded-full bg-muted group-data-[vaul-drawer-direction=bottom]/drawer-content:block" }), children]
|
|
45
47
|
})]
|
|
46
48
|
});
|
|
@@ -3,7 +3,7 @@ import { Dialog } from "radix-ui";
|
|
|
3
3
|
declare function Sheet({ ...props }: React.ComponentProps<typeof Dialog.Root>): React.JSX.Element;
|
|
4
4
|
declare function SheetTrigger({ ...props }: React.ComponentProps<typeof Dialog.Trigger>): React.JSX.Element;
|
|
5
5
|
declare function SheetClose({ ...props }: React.ComponentProps<typeof Dialog.Close>): React.JSX.Element;
|
|
6
|
-
declare function SheetContent({ className, children, side, showCloseButton, ...props }: React.ComponentProps<typeof Dialog.Content> & {
|
|
6
|
+
declare function SheetContent({ className, children, side, showCloseButton, onInteractOutside, ...props }: React.ComponentProps<typeof Dialog.Content> & {
|
|
7
7
|
side?: "top" | "right" | "bottom" | "left";
|
|
8
8
|
showCloseButton?: boolean;
|
|
9
9
|
}): React.JSX.Element;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
|
+
import { keepOpenOnToast } from "../../lib/internal/toast-clearance.js";
|
|
2
3
|
import { cn } from "../../lib/utils.js";
|
|
3
4
|
import { Button } from "./button.js";
|
|
4
5
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -36,12 +37,13 @@ function SheetOverlay({ className, ...props }) {
|
|
|
36
37
|
...props
|
|
37
38
|
});
|
|
38
39
|
}
|
|
39
|
-
function SheetContent({ className, children, side = "right", showCloseButton = true, ...props }) {
|
|
40
|
+
function SheetContent({ className, children, side = "right", showCloseButton = true, onInteractOutside, ...props }) {
|
|
40
41
|
return /* @__PURE__ */ jsxs(SheetPortal, { children: [/* @__PURE__ */ jsx(SheetOverlay, {}), /* @__PURE__ */ jsxs(Dialog.Content, {
|
|
41
42
|
"data-slot": "sheet-content",
|
|
42
43
|
"data-side": side,
|
|
43
44
|
className: cn("fixed z-50 flex flex-col gap-4 bg-popover bg-clip-padding text-sm text-popover-foreground shadow-lg transition duration-200 ease-in-out data-[side=bottom]:inset-x-0 data-[side=bottom]:bottom-0 data-[side=bottom]:h-auto data-[side=bottom]:border-t data-[side=left]:inset-y-0 data-[side=left]:left-0 data-[side=left]:h-full data-[side=left]:w-3/4 data-[side=left]:border-r data-[side=right]:inset-y-0 data-[side=right]:right-0 data-[side=right]:h-full data-[side=right]:w-3/4 data-[side=right]:border-l data-[side=top]:inset-x-0 data-[side=top]:top-0 data-[side=top]:h-auto data-[side=top]:border-b data-[side=left]:sm:max-w-sm data-[side=right]:sm:max-w-sm data-open:animate-in data-open:fade-in-0 data-[side=bottom]:data-open:slide-in-from-bottom-10 data-[side=left]:data-open:slide-in-from-left-10 data-[side=right]:data-open:slide-in-from-right-10 data-[side=top]:data-open:slide-in-from-top-10 data-closed:animate-out data-closed:fade-out-0 data-[side=bottom]:data-closed:slide-out-to-bottom-10 data-[side=left]:data-closed:slide-out-to-left-10 data-[side=right]:data-closed:slide-out-to-right-10 data-[side=top]:data-closed:slide-out-to-top-10", className),
|
|
44
45
|
...props,
|
|
46
|
+
onInteractOutside: keepOpenOnToast(onInteractOutside),
|
|
45
47
|
children: [children, showCloseButton && /* @__PURE__ */ jsx(Dialog.Close, {
|
|
46
48
|
"data-slot": "sheet-close",
|
|
47
49
|
asChild: true,
|
|
@@ -20,7 +20,8 @@ const Toaster = ({ ...props }) => {
|
|
|
20
20
|
"--normal-bg": "var(--popover)",
|
|
21
21
|
"--normal-text": "var(--popover-foreground)",
|
|
22
22
|
"--normal-border": "var(--border)",
|
|
23
|
-
"--border-radius": "var(--radius)"
|
|
23
|
+
"--border-radius": "var(--radius)",
|
|
24
|
+
pointerEvents: "auto"
|
|
24
25
|
},
|
|
25
26
|
toastOptions: { classNames: {
|
|
26
27
|
toast: "cn-toast",
|
|
@@ -30,7 +30,7 @@ function TraceIcon({ className, ...props }) {
|
|
|
30
30
|
function TraceTitle({ className, ...props }) {
|
|
31
31
|
return /* @__PURE__ */ jsx("span", {
|
|
32
32
|
"data-slot": "trace-title",
|
|
33
|
-
className: cn("min-w-0
|
|
33
|
+
className: cn("min-w-0 wrap-anywhere motion-reduce:shimmer-none", className),
|
|
34
34
|
...props
|
|
35
35
|
});
|
|
36
36
|
}
|
|
@@ -2,6 +2,16 @@
|
|
|
2
2
|
import * as React from "react";
|
|
3
3
|
// src/lib/internal/toast-clearance.ts
|
|
4
4
|
const counts = /* @__PURE__ */ new Map();
|
|
5
|
+
/**
|
|
6
|
+
* For a modal's `onInteractOutside`: a press or focus inside the Toaster (e.g. a toast's Undo) does not dismiss the
|
|
7
|
+
* modal. The Toaster opts out of the modal's `pointer-events: none` on <body> (src/components/ui/sonner.tsx).
|
|
8
|
+
*/
|
|
9
|
+
function keepOpenOnToast(handler) {
|
|
10
|
+
return (event) => {
|
|
11
|
+
handler?.(event);
|
|
12
|
+
if (event.target instanceof Element && event.target.closest("[data-sonner-toaster]")) event.preventDefault();
|
|
13
|
+
};
|
|
14
|
+
}
|
|
5
15
|
function useToastClearance(name, value, enabled = true) {
|
|
6
16
|
React.useEffect(() => {
|
|
7
17
|
if (!enabled || typeof document === "undefined") return;
|
|
@@ -19,4 +29,4 @@ function useToastClearance(name, value, enabled = true) {
|
|
|
19
29
|
enabled
|
|
20
30
|
]);
|
|
21
31
|
}
|
|
22
|
-
export { useToastClearance };
|
|
32
|
+
export { keepOpenOnToast, useToastClearance };
|
package/dist/styles.css
CHANGED
|
@@ -319,22 +319,37 @@
|
|
|
319
319
|
/* Toasts clear floating controls. AppNav (tab bar, action button) and AssistantDock (launcher) set --hb-toast-* on
|
|
320
320
|
<html> while mounted (src/lib/internal/toast-clearance.ts); this adds them to Sonner's bottom offset. Unlayered and
|
|
321
321
|
!important because Sonner injects its own unlayered rule at runtime. Breakpoints: AppNav's tab bar shows under
|
|
322
|
-
768px; Sonner switches to full-width mobile toasts under 600px.
|
|
322
|
+
768px; Sonner switches to full-width mobile toasts under 600px.
|
|
323
|
+
An open DetailSheet sets --hb-toast-sheet (desktop side sheet: its width plus gap) or --hb-toast-drawer (phone
|
|
324
|
+
full-screen drawer: its footer height). The sheet moves toasts left of it and narrows them to fit; the drawer
|
|
325
|
+
replaces the other bottom offsets, since it covers the tab bar, action button and launcher. */
|
|
323
326
|
[data-sonner-toaster][data-y-position="bottom"] {
|
|
324
327
|
bottom: calc(var(--offset-bottom) + var(--hb-toast-launcher, 0px)) !important;
|
|
325
328
|
}
|
|
329
|
+
@media (min-width: 768px) {
|
|
330
|
+
[data-sonner-toaster] {
|
|
331
|
+
--width: min(356px, 100vw - var(--hb-toast-sheet, 0px) - 2 * var(--offset-right)) !important;
|
|
332
|
+
}
|
|
333
|
+
[data-sonner-toaster][data-x-position="right"] {
|
|
334
|
+
right: calc(var(--offset-right) + var(--hb-toast-sheet, 0px)) !important;
|
|
335
|
+
}
|
|
336
|
+
[data-sonner-toaster][data-x-position="center"] {
|
|
337
|
+
transform: translateX(calc(-50% - var(--hb-toast-sheet, 0px) / 2)) !important;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
326
340
|
@media (max-width: 767px) {
|
|
327
341
|
[data-sonner-toaster][data-y-position="bottom"] {
|
|
328
342
|
bottom: calc(
|
|
329
|
-
var(--offset-bottom) +
|
|
343
|
+
var(--offset-bottom) +
|
|
344
|
+
var(--hb-toast-drawer, var(--hb-toast-nav, 0px) + var(--hb-toast-fab, 0px) + var(--hb-toast-launcher, 0px))
|
|
330
345
|
) !important;
|
|
331
346
|
}
|
|
332
347
|
}
|
|
333
348
|
@media (max-width: 600px) {
|
|
334
349
|
[data-sonner-toaster][data-y-position="bottom"] {
|
|
335
350
|
bottom: calc(
|
|
336
|
-
var(--mobile-offset-bottom) +
|
|
337
|
-
var(--hb-toast-launcher, 0px)
|
|
351
|
+
var(--mobile-offset-bottom) +
|
|
352
|
+
var(--hb-toast-drawer, var(--hb-toast-nav, 0px) + var(--hb-toast-fab, 0px) + var(--hb-toast-launcher, 0px))
|
|
338
353
|
) !important;
|
|
339
354
|
}
|
|
340
355
|
}
|
package/docs/COMPONENTS.md
CHANGED
|
@@ -373,7 +373,7 @@ Searchable IANA time zone field built on `Combobox`, listing `Intl.supportedValu
|
|
|
373
373
|
|
|
374
374
|
### `DetailSheet`
|
|
375
375
|
|
|
376
|
-
A record's details, one API for both sizes: a floating side sheet from 768px up, a full-screen drawer with a close button on phones. Props: `open`, `onOpenChange`, `title` (heading and accessible name), `description?`, `footer?` (pinned under the scrolling body, e.g. Delete), `children` (the fields). `data-layout` is `"sheet"` or `"drawer"`.
|
|
376
|
+
A record's details, one API for both sizes: a floating side sheet from 768px up, a full-screen drawer with a close button on phones. Props: `open`, `onOpenChange`, `title` (heading and accessible name), `description?`, `footer?` (pinned under the scrolling body, e.g. Delete), `children` (the fields). `data-layout` is `"sheet"` or `"drawer"`. While open, toasts (e.g. `toastUndo`) stay off it: on desktop they move to the left of the sheet and narrow to fit; on phones they sit above the footer (one row of buttons) or the bottom safe area, in place of the hidden tab bar's space.
|
|
377
377
|
|
|
378
378
|
```tsx
|
|
379
379
|
;<DetailSheet
|
|
@@ -651,7 +651,7 @@ Scrollable log that sticks to the bottom as content streams in (unless the user
|
|
|
651
651
|
|
|
652
652
|
### `ChatMessage`
|
|
653
653
|
|
|
654
|
-
One turn. Props: `role: "user" | "assistant"` (matches AI SDK message roles), `avatar?` (assistant only, defaults to `AssistantAvatar`; `false` for none and full-width content, for agent threads where one turn is many blocks), `actions?` (assistant only, under the content), `variant?` (user bubble: `"muted"` default or `"soft"` accent tint), `children`. Long words wrap.
|
|
654
|
+
One turn. Props: `role: "user" | "assistant"` (matches AI SDK message roles), `avatar?` (assistant only, defaults to `AssistantAvatar`; `false` for none and full-width content, for agent threads where one turn is many blocks), `actions?` (assistant only, under the content), `variant?` (user bubble: `"muted"` default or `"soft"` accent tint), `children`. Long words wrap, in assistant content too.
|
|
655
655
|
User turns render as a right-aligned bubble with preserved newlines. Assistant turns render free-flowing: put `Prose`, `CodeBlock`, `ToolCall`, `TypingIndicator` inside.
|
|
656
656
|
|
|
657
657
|
```tsx
|
|
@@ -722,9 +722,9 @@ Three pulsing dots. Props: `label?` (text after the dots, e.g. "Working"), `vari
|
|
|
722
722
|
|
|
723
723
|
### `ToolGroup`, `ToolStep`, `ToolStatusIcon`
|
|
724
724
|
|
|
725
|
-
Agent tool calls as a bordered, collapsible card. `ToolStatus = "queued" | "waiting" | "running" | "done" | "error" | "stopped"`.
|
|
725
|
+
Agent tool calls as a bordered, collapsible card. Titles, labels and details wrap instead of cutting off. `ToolStatus = "queued" | "waiting" | "running" | "done" | "error" | "stopped"`.
|
|
726
726
|
`ToolGroup` props: `title`, `status`, `meta?` (e.g. time), `autoOpen?` (default open while active, until the user toggles), `open?`/`onOpenChange?`.
|
|
727
|
-
`ToolStep` props: `icon?`, `kind` (verb column), `detail` (monospace command or path), `status`, `meta?` (default from status), `output?` (monospace, shown when expanded; omit for a fixed row), `autoOpen?` (default open while running).
|
|
727
|
+
`ToolStep` props: `icon?`, `kind` (verb column; with an empty `detail` it is the whole label, for plain-language steps such as "Added milk"), `detail` (monospace command or path, or `""`), `status`, `meta?` (default from status), `output?` (monospace, shown when expanded; omit for a fixed row), `autoOpen?` (default open while running).
|
|
728
728
|
`ToolStatusIcon`: spinner while running, else a toned dot. Lighter alternative: `Trace`.
|
|
729
729
|
|
|
730
730
|
```tsx
|
|
@@ -736,7 +736,7 @@ Agent tool calls as a bordered, collapsible card. `ToolStatus = "queued" | "wait
|
|
|
736
736
|
### `ApprovalCard`, `ApprovalResult`
|
|
737
737
|
|
|
738
738
|
`ApprovalCard`: warning-ringed card for anything the agent waits on the user for. Props: `title`, `description?`, `icon?` (default shield, `null` for none), `reason?` (shown first after a bold "Why:"), `code?` + `codeLabel?` (wrapped code block with copy), `actions?` (buttons), `footer?`, `children` (more body).
|
|
739
|
-
`ApprovalResult`:
|
|
739
|
+
`ApprovalResult`: short record of a decision in the thread; wraps when long. Props: `decision: "approved" | "denied"`, `time?`, `children`.
|
|
740
740
|
|
|
741
741
|
### `AssistantProvider`, `useAssistant`, `useAssistantPage`
|
|
742
742
|
|
|
@@ -744,7 +744,7 @@ One assistant conversation across pages. `AssistantProvider` props: `onSend(text
|
|
|
744
744
|
|
|
745
745
|
### `AssistantDock`
|
|
746
746
|
|
|
747
|
-
The assistant as a floating panel over the page (never reflows it): header (icon, title, full-screen link, close), empty state with suggestions, thread, `AskBox` (Stop while the assistant works, wired to `useAssistant().stop`). Closed, it is a soft launcher button. Escape closes and returns focus to the launcher. Props: `title?` (default "Assistant"), `icon?`, `fullScreenHref?`, `emptyTitle?`, `emptyDescription?`, `suggestions?` (default: the page's), `placeholder?`, `dictation?`, `launcher?` (false when another control opens it), `launcherLabel?`.
|
|
747
|
+
The assistant as a floating panel over the page (never reflows it): header (icon, title, your `actions`, full-screen link, close), empty state with suggestions, thread, `AskBox` (Stop while the assistant works, wired to `useAssistant().stop`). Closed, it is a soft launcher button. Escape closes and returns focus to the launcher. Props: `title?` (default "Assistant"), `icon?`, `fullScreenHref?`, `actions?` (header controls before the full-screen link, e.g. a "New chat" icon button with an `aria-label`), `emptyTitle?`, `emptyDescription?`, `suggestions?` (default: the page's), `placeholder?`, `dictation?`, `launcher?` (false when another control opens it), `launcherLabel?`.
|
|
748
748
|
|
|
749
749
|
### `AskBox`
|
|
750
750
|
|
|
@@ -774,7 +774,7 @@ const { send, pending, stop, reply } = useAssistant()
|
|
|
774
774
|
|
|
775
775
|
### `ActionReceipt`
|
|
776
776
|
|
|
777
|
-
What the assistant did, as a compact card: round check, `title` (past tense), `meta
|
|
777
|
+
What the assistant did, as a compact card: round check, `title` (past tense), `meta?` (wraps when long), Undo (`onUndo?`, `undoLabel?`). `undone` strikes it through and shows "Undone".
|
|
778
778
|
|
|
779
779
|
### `CodeSnippet`, `InlineCode`, `Prose`, `ProseBlockquote`
|
|
780
780
|
|
package/docs/ROADMAP.md
CHANGED
|
@@ -48,7 +48,7 @@ Calendar views and SmartInput follow when a second app needs them.
|
|
|
48
48
|
| AssistantDock (built) | Floating panel over the page (never reflows it) with header, empty state, thread, receipts and composer; a soft launcher button when closed; a link to full screen. Same on every page; pages pass suggestions only. | Extends ChatShell. |
|
|
49
49
|
| AskBox (built) | Large rounded input with chips that appear as you type, suggestion pills under it, and the dictation button. Focus ring on the box, not the field. | One placeholder per app ("Ask HomeBot anything"). |
|
|
50
50
|
| DictationButton, `useDictation` (built) | One button that is a microphone when empty, a stop button while listening (with listening bars and a live region), and send once there is text. The speech source plugs in (browser speech recognition, a server model). | Off-animation under reduced motion. |
|
|
51
|
-
| ActionReceipt (built) | What the assistant did, as a compact card: round check, title,
|
|
51
|
+
| ActionReceipt (built) | What the assistant did, as a compact card: round check, title, meta, Undo. One per action. | The core of "safe actions run on their own". |
|
|
52
52
|
| WhatIDid | Collapsed list of plain past-tense steps under an answer. | A plain-language variant of ToolSteps. |
|
|
53
53
|
| ProgressCard | A long-running job: title, status word, progress bar, time left, an action when finished. | Downloads, imports, syncs. |
|
|
54
54
|
| ChatsHome | Recent chats grouped Today, This week, Earlier, with search and a row menu (Rename, Delete with inline confirm). | Extends ConversationList. |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "harebear-ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"description": "Opinionated, pre-themed shadcn/ui component library for React 19 + Tailwind CSS v4. Admin, data, and AI chat building blocks, ready for agents.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Harrison Milbradt",
|