@mcp-b/react-components 0.22.0 → 0.24.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.
@@ -0,0 +1,21 @@
1
+ //#region src/components/FormattedDate.d.ts
2
+ interface FormattedDateProps {
3
+ /** The date to render. */
4
+ date: Date;
5
+ /**
6
+ * Locale for the visible label.
7
+ * @default 'en-us'
8
+ */
9
+ locale?: string;
10
+ }
11
+ /**
12
+ * A `<time>` element with an ISO `datetime` and a short, UTC-stable visible
13
+ * label (e.g. "Jan 5, 2026"). UTC framing keeps server and client render
14
+ * identical regardless of the viewer's zone.
15
+ */
16
+ declare function FormattedDate({
17
+ date,
18
+ locale
19
+ }: FormattedDateProps): import("react").JSX.Element;
20
+ //#endregion
21
+ export { FormattedDate, FormattedDateProps };
@@ -0,0 +1,20 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ //#region src/components/FormattedDate.tsx
3
+ /**
4
+ * A `<time>` element with an ISO `datetime` and a short, UTC-stable visible
5
+ * label (e.g. "Jan 5, 2026"). UTC framing keeps server and client render
6
+ * identical regardless of the viewer's zone.
7
+ */
8
+ function FormattedDate({ date, locale = "en-us" }) {
9
+ return /* @__PURE__ */ jsx("time", {
10
+ dateTime: date.toISOString(),
11
+ children: date.toLocaleDateString(locale, {
12
+ year: "numeric",
13
+ month: "short",
14
+ day: "numeric",
15
+ timeZone: "UTC"
16
+ })
17
+ });
18
+ }
19
+ //#endregion
20
+ export { FormattedDate };
@@ -0,0 +1,46 @@
1
+ import { a as ButtonVariant, i as ButtonSize, n as ButtonColor } from "../Button-SBIz6Lqi.js";
2
+ import { AnchorHTMLAttributes } from "react";
3
+
4
+ //#region src/components/LinkButton.d.ts
5
+ interface LinkButtonProps extends AnchorHTMLAttributes<HTMLAnchorElement> {
6
+ /** Destination of the link. */
7
+ href: string;
8
+ /**
9
+ * The visual rendering style of the button chrome.
10
+ * @default 'normal'
11
+ */
12
+ variant?: ButtonVariant;
13
+ /**
14
+ * The semantic color intent of the button chrome.
15
+ * @default 'primary'
16
+ */
17
+ color?: ButtonColor;
18
+ /**
19
+ * The size of the button chrome.
20
+ * @default 'sm'
21
+ */
22
+ size?: ButtonSize;
23
+ }
24
+ /**
25
+ * A link that wears Button chrome. Renders a plain anchor composed through the
26
+ * {@link buttonVariants} seam, so the element stays a genuine link.
27
+ *
28
+ * `Button render={<a />}` gives the same visuals in React, but a link styled as
29
+ * a button still needs a component wrapper for non-React hosts: Astro templates
30
+ * cannot construct the React element the `render` prop expects (a `.astro` `<a />`
31
+ * is an Astro vnode, not a React element, and `@astrojs/react` rejects it). This
32
+ * component is that wrapper, so Astro can render a button-styled link by passing
33
+ * plain props.
34
+ */
35
+ declare function LinkButton({
36
+ variant,
37
+ color,
38
+ size,
39
+ className,
40
+ ref,
41
+ ...props
42
+ }: LinkButtonProps & {
43
+ ref?: React.Ref<HTMLAnchorElement>;
44
+ }): import("react").JSX.Element;
45
+ //#endregion
46
+ export { LinkButton, LinkButtonProps };
@@ -0,0 +1,28 @@
1
+ import { buttonVariants } from "./Button.js";
2
+ import { jsx } from "react/jsx-runtime";
3
+ //#region src/components/LinkButton.tsx
4
+ /**
5
+ * A link that wears Button chrome. Renders a plain anchor composed through the
6
+ * {@link buttonVariants} seam, so the element stays a genuine link.
7
+ *
8
+ * `Button render={<a />}` gives the same visuals in React, but a link styled as
9
+ * a button still needs a component wrapper for non-React hosts: Astro templates
10
+ * cannot construct the React element the `render` prop expects (a `.astro` `<a />`
11
+ * is an Astro vnode, not a React element, and `@astrojs/react` rejects it). This
12
+ * component is that wrapper, so Astro can render a button-styled link by passing
13
+ * plain props.
14
+ */
15
+ function LinkButton({ variant, color, size, className, ref, ...props }) {
16
+ return /* @__PURE__ */ jsx("a", {
17
+ ref,
18
+ className: buttonVariants({
19
+ variant,
20
+ color,
21
+ size,
22
+ className
23
+ }),
24
+ ...props
25
+ });
26
+ }
27
+ //#endregion
28
+ export { LinkButton };
@@ -37,6 +37,7 @@ function ScrollAreaViewport({ className, ref, ...props }) {
37
37
  return /* @__PURE__ */ jsx(ScrollArea$1.Viewport, {
38
38
  ref,
39
39
  "data-slot": "scroll-area-viewport",
40
+ "data-recipe": "scroll-fade-y",
40
41
  className: classes,
41
42
  ...props
42
43
  });
@@ -0,0 +1,149 @@
1
+ import * as React from "react";
2
+
3
+ //#region src/components/Shell.d.ts
4
+ /**
5
+ * Props for the Shell.Root component.
6
+ */
7
+ interface ShellRootProps extends React.HTMLAttributes<HTMLDivElement> {
8
+ /** Additional CSS class names */
9
+ className?: string;
10
+ /** The heading text displayed in the header */
11
+ heading?: string;
12
+ }
13
+ /**
14
+ * Props for the Shell.ActionBar component.
15
+ */
16
+ interface ShellActionBarProps extends React.HTMLAttributes<HTMLDivElement> {
17
+ /** Additional CSS class names */
18
+ className?: string;
19
+ }
20
+ /**
21
+ * Props for the Shell.Action component.
22
+ */
23
+ interface ShellActionProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
24
+ /** Additional CSS class names */
25
+ className?: string;
26
+ /** Icon to display */
27
+ icon?: React.ReactNode;
28
+ /** Whether this action is currently active */
29
+ active?: boolean;
30
+ /** Accessible label for the action */
31
+ label?: string;
32
+ }
33
+ /**
34
+ * Props for the Shell.Panel component.
35
+ */
36
+ interface ShellPanelProps extends React.HTMLAttributes<HTMLDivElement> {
37
+ /** Additional CSS class names */
38
+ className?: string;
39
+ /** The panel heading */
40
+ heading?: string;
41
+ /** Whether the panel is visible */
42
+ open?: boolean;
43
+ }
44
+ /**
45
+ * Props for the Shell.Content component.
46
+ */
47
+ interface ShellContentProps extends React.HTMLAttributes<HTMLDivElement> {
48
+ /** Additional CSS class names */
49
+ className?: string;
50
+ }
51
+ /**
52
+ * Root container for the shell layout.
53
+ * Provides a structured layout with optional header, action bar, panel, and content areas.
54
+ */
55
+ declare function ShellRoot({
56
+ className,
57
+ heading,
58
+ children,
59
+ ref,
60
+ ...props
61
+ }: ShellRootProps & {
62
+ ref?: React.Ref<HTMLDivElement>;
63
+ }): React.JSX.Element;
64
+ /**
65
+ * Container for action buttons that toggle panels.
66
+ * Typically placed on the left side of the shell.
67
+ */
68
+ declare function ShellActionBar({
69
+ className,
70
+ children,
71
+ ref,
72
+ ...props
73
+ }: ShellActionBarProps & {
74
+ ref?: React.Ref<HTMLDivElement>;
75
+ }): React.JSX.Element;
76
+ /**
77
+ * An action button within the action bar.
78
+ */
79
+ declare function ShellAction({
80
+ className,
81
+ icon,
82
+ active,
83
+ label,
84
+ ref,
85
+ ...props
86
+ }: ShellActionProps & {
87
+ ref?: React.Ref<HTMLButtonElement>;
88
+ }): React.JSX.Element;
89
+ /**
90
+ * A collapsible panel that slides in from the side.
91
+ */
92
+ declare function ShellPanel({
93
+ className,
94
+ heading,
95
+ open,
96
+ children,
97
+ ref,
98
+ ...props
99
+ }: ShellPanelProps & {
100
+ ref?: React.Ref<HTMLDivElement>;
101
+ }): React.JSX.Element;
102
+ /**
103
+ * The main content area of the shell.
104
+ */
105
+ declare function ShellContent({
106
+ className,
107
+ children,
108
+ ref,
109
+ ...props
110
+ }: ShellContentProps & {
111
+ ref?: React.Ref<HTMLDivElement>;
112
+ }): React.JSX.Element;
113
+ /**
114
+ * A shell layout component with an icon action bar, a collapsible side panel,
115
+ * and a content canvas. Useful for map applications and other interactive
116
+ * tools where a full chat or sidebar chrome (see ResponsiveSidebarShell) is
117
+ * too heavy.
118
+ *
119
+ * @example
120
+ * ```tsx
121
+ * const [filterOpen, setFilterOpen] = useState(false);
122
+ *
123
+ * <Shell.Root heading="My Map Application">
124
+ * <Shell.ActionBar>
125
+ * <Shell.Action
126
+ * icon={<FilterIcon />}
127
+ * active={filterOpen}
128
+ * onClick={() => setFilterOpen(!filterOpen)}
129
+ * label="Filter"
130
+ * />
131
+ * </Shell.ActionBar>
132
+ * <Shell.Panel heading="Filter" open={filterOpen}>
133
+ * <p>Filter controls here</p>
134
+ * </Shell.Panel>
135
+ * <Shell.Content>
136
+ * <div id="map" />
137
+ * </Shell.Content>
138
+ * </Shell.Root>
139
+ * ```
140
+ */
141
+ declare const Shell: {
142
+ Root: typeof ShellRoot;
143
+ ActionBar: typeof ShellActionBar;
144
+ Action: typeof ShellAction;
145
+ Panel: typeof ShellPanel;
146
+ Content: typeof ShellContent;
147
+ };
148
+ //#endregion
149
+ export { Shell, ShellActionBarProps, ShellActionProps, ShellContentProps, ShellPanelProps, ShellRootProps };
@@ -0,0 +1,116 @@
1
+ import { t as cx } from "../class-names-BiMDVpUo.js";
2
+ import "react";
3
+ import { jsx, jsxs } from "react/jsx-runtime";
4
+ //#region src/components/Shell.tsx
5
+ /**
6
+ * Root container for the shell layout.
7
+ * Provides a structured layout with optional header, action bar, panel, and content areas.
8
+ */
9
+ function ShellRoot({ className, heading, children, ref, ...props }) {
10
+ return /* @__PURE__ */ jsxs("div", {
11
+ ref,
12
+ className: cx("shell", className),
13
+ ...props,
14
+ children: [heading && /* @__PURE__ */ jsx("header", {
15
+ className: "shell__header",
16
+ children: /* @__PURE__ */ jsx("h3", {
17
+ className: "shell__heading",
18
+ children: heading
19
+ })
20
+ }), /* @__PURE__ */ jsx("div", {
21
+ className: "shell__body",
22
+ children
23
+ })]
24
+ });
25
+ }
26
+ /**
27
+ * Container for action buttons that toggle panels.
28
+ * Typically placed on the left side of the shell.
29
+ */
30
+ function ShellActionBar({ className, children, ref, ...props }) {
31
+ return /* @__PURE__ */ jsx("div", {
32
+ ref,
33
+ className: cx("shell__action-bar", className),
34
+ ...props,
35
+ children
36
+ });
37
+ }
38
+ /**
39
+ * An action button within the action bar.
40
+ */
41
+ function ShellAction({ className, icon, active, label, ref, ...props }) {
42
+ return /* @__PURE__ */ jsx("button", {
43
+ ref,
44
+ className: cx("shell__action", active && "shell__action--active", className),
45
+ "aria-label": label,
46
+ "aria-pressed": active,
47
+ ...props,
48
+ children: icon
49
+ });
50
+ }
51
+ /**
52
+ * A collapsible panel that slides in from the side.
53
+ */
54
+ function ShellPanel({ className, heading, open = false, children, ref, ...props }) {
55
+ return /* @__PURE__ */ jsxs("div", {
56
+ ref,
57
+ className: cx("shell__panel", open && "shell__panel--open", className),
58
+ hidden: !open,
59
+ ...props,
60
+ children: [heading && /* @__PURE__ */ jsx("div", {
61
+ className: "shell__panel-heading",
62
+ children: heading
63
+ }), /* @__PURE__ */ jsx("div", {
64
+ className: "shell__panel-content",
65
+ children
66
+ })]
67
+ });
68
+ }
69
+ /**
70
+ * The main content area of the shell.
71
+ */
72
+ function ShellContent({ className, children, ref, ...props }) {
73
+ return /* @__PURE__ */ jsx("div", {
74
+ ref,
75
+ className: cx("shell__content", className),
76
+ ...props,
77
+ children
78
+ });
79
+ }
80
+ /**
81
+ * A shell layout component with an icon action bar, a collapsible side panel,
82
+ * and a content canvas. Useful for map applications and other interactive
83
+ * tools where a full chat or sidebar chrome (see ResponsiveSidebarShell) is
84
+ * too heavy.
85
+ *
86
+ * @example
87
+ * ```tsx
88
+ * const [filterOpen, setFilterOpen] = useState(false);
89
+ *
90
+ * <Shell.Root heading="My Map Application">
91
+ * <Shell.ActionBar>
92
+ * <Shell.Action
93
+ * icon={<FilterIcon />}
94
+ * active={filterOpen}
95
+ * onClick={() => setFilterOpen(!filterOpen)}
96
+ * label="Filter"
97
+ * />
98
+ * </Shell.ActionBar>
99
+ * <Shell.Panel heading="Filter" open={filterOpen}>
100
+ * <p>Filter controls here</p>
101
+ * </Shell.Panel>
102
+ * <Shell.Content>
103
+ * <div id="map" />
104
+ * </Shell.Content>
105
+ * </Shell.Root>
106
+ * ```
107
+ */
108
+ const Shell = {
109
+ Root: ShellRoot,
110
+ ActionBar: ShellActionBar,
111
+ Action: ShellAction,
112
+ Panel: ShellPanel,
113
+ Content: ShellContent
114
+ };
115
+ //#endregion
116
+ export { Shell };
@@ -188,7 +188,6 @@ function ThinkThreadPicker({ threads, renderThreadLink, onCreate, onDelete, clas
188
188
  className: "think-thread-picker__scroll",
189
189
  children: [/* @__PURE__ */ jsx(ScrollArea.Viewport, {
190
190
  className: "think-thread-picker__viewport",
191
- "data-recipe": "scroll-fade-y",
192
191
  "aria-label": "Thread list",
193
192
  children: visibleThreads.length ? /* @__PURE__ */ jsx("ul", {
194
193
  className: "think-thread-picker__list",
@@ -65,6 +65,7 @@
65
65
  @import "./search-field.css" layer(components);
66
66
  @import "./select.css" layer(components);
67
67
  @import "./separator.css" layer(components);
68
+ @import "./shell.css" layer(components);
68
69
  @import "./slider.css" layer(components);
69
70
  @import "./status-pill.css" layer(components);
70
71
  @import "./stepper.css" layer(components);
@@ -24,7 +24,11 @@
24
24
  }
25
25
 
26
26
  .message-scroller__viewport[data-scrollable~="end"] {
27
- --sigvelo-scroll-fade-end: var(--message-scroller-fade-b-size, var(--message-scroller-fade-size));
27
+ --sigvelo-scroll-fade-end: var(--message-scroller-fade-size);
28
+ }
29
+
30
+ .message-scroller__viewport[data-scrollable~="start"] {
31
+ --sigvelo-scroll-fade-start: var(--message-scroller-fade-size);
28
32
  }
29
33
 
30
34
  .message-scroller__content {
@@ -166,12 +166,19 @@
166
166
  color: var(--sigvelo-color-text);
167
167
  }
168
168
 
169
- /* Reveal scrollability by fading the top/bottom edges. Owns only the mask
170
- structure; each scroller drives --sigvelo-scroll-fade-start/end (default 0px)
171
- from its own overflow signal. */
169
+ /* Reveal vertical scrollability by fading the top/bottom edges. Base UI
170
+ ScrollArea supplies live overflow distances; other scrollers can override
171
+ --sigvelo-scroll-fade-start/end from their own state. */
172
172
  :where([data-recipe~="scroll-fade-y"]) {
173
- --sigvelo-scroll-fade-start: 0px;
174
- --sigvelo-scroll-fade-end: 0px;
173
+ --sigvelo-scroll-fade-size: min(12%, 2.5rem);
174
+ --sigvelo-scroll-fade-start: min(
175
+ var(--sigvelo-scroll-fade-size),
176
+ var(--scroll-area-overflow-y-start, 0px)
177
+ );
178
+ --sigvelo-scroll-fade-end: min(
179
+ var(--sigvelo-scroll-fade-size),
180
+ var(--scroll-area-overflow-y-end, 0px)
181
+ );
175
182
  -webkit-mask-image: linear-gradient(
176
183
  to bottom,
177
184
  transparent 0,
@@ -0,0 +1,122 @@
1
+ /* Shell Component */
2
+ .shell {
3
+ display: flex;
4
+ flex-direction: column;
5
+ height: 100%;
6
+ min-height: 25rem;
7
+ border-radius: var(--sigvelo-radius-md);
8
+ border: var(--sigvelo-border-style) var(--sigvelo-border-width)
9
+ var(--sigvelo-color-neutral-border-subtle);
10
+ background: var(--sigvelo-color-surface-raised);
11
+ overflow: hidden;
12
+ }
13
+
14
+ .shell__header {
15
+ display: flex;
16
+ align-items: center;
17
+ padding: var(--sigvelo-spacing-3) var(--sigvelo-spacing-4);
18
+ background: var(--sigvelo-color-surface-sunken);
19
+ border-block-end: var(--sigvelo-border-style) var(--sigvelo-border-width)
20
+ var(--sigvelo-color-neutral-border-subtle);
21
+ }
22
+
23
+ .shell__heading {
24
+ margin: 0;
25
+ font-size: var(--sigvelo-text-sm);
26
+ font-weight: var(--sigvelo-font-weight-semibold);
27
+ color: var(--sigvelo-color-text);
28
+ }
29
+
30
+ .shell__body {
31
+ display: flex;
32
+ flex: 1;
33
+ min-height: 0;
34
+ position: relative;
35
+ }
36
+
37
+ /* Action Bar */
38
+ .shell__action-bar {
39
+ display: flex;
40
+ flex-direction: column;
41
+ gap: var(--sigvelo-spacing-1);
42
+ padding: var(--sigvelo-spacing-2);
43
+ background: var(--sigvelo-color-surface-sunken);
44
+ border-inline-end: var(--sigvelo-border-style) var(--sigvelo-border-width)
45
+ var(--sigvelo-color-neutral-border-subtle);
46
+ }
47
+
48
+ .shell__action {
49
+ display: flex;
50
+ align-items: center;
51
+ justify-content: center;
52
+ width: var(--sigvelo-spacing-10);
53
+ height: var(--sigvelo-spacing-10);
54
+ padding: 0;
55
+ border: none;
56
+ border-radius: var(--sigvelo-radius-md);
57
+ background: transparent;
58
+ color: var(--sigvelo-color-text-muted);
59
+ cursor: pointer;
60
+ transition:
61
+ background-color var(--sigvelo-motion-duration-interaction) var(--sigvelo-motion-ease-change),
62
+ color var(--sigvelo-motion-duration-interaction) var(--sigvelo-motion-ease-change);
63
+ }
64
+
65
+ .shell__action:hover {
66
+ background: var(--sigvelo-color-surface-sunken);
67
+ color: var(--sigvelo-color-text);
68
+ }
69
+
70
+ .shell__action--active {
71
+ background: var(--sigvelo-color-primary-bg);
72
+ color: var(--sigvelo-color-primary-text-on-bg);
73
+ }
74
+
75
+ .shell__action--active:hover {
76
+ background: color-mix(in oklab, var(--sigvelo-color-primary-bg), black 5%);
77
+ color: var(--sigvelo-color-primary-text-on-bg);
78
+ }
79
+
80
+ .shell__action svg {
81
+ width: var(--sigvelo-spacing-5);
82
+ height: var(--sigvelo-spacing-5);
83
+ }
84
+
85
+ /* Panel */
86
+ .shell__panel {
87
+ display: flex;
88
+ flex-direction: column;
89
+ width: 16.25rem;
90
+ max-width: 100%;
91
+ background: var(--sigvelo-color-surface-raised);
92
+ border-inline-end: var(--sigvelo-border-style) var(--sigvelo-border-width)
93
+ var(--sigvelo-color-neutral-border-subtle);
94
+ overflow: hidden;
95
+ }
96
+
97
+ .shell__panel[hidden] {
98
+ display: none;
99
+ }
100
+
101
+ .shell__panel-heading {
102
+ padding: var(--sigvelo-spacing-3) var(--sigvelo-spacing-4);
103
+ font-size: var(--sigvelo-text-sm);
104
+ font-weight: var(--sigvelo-font-weight-bold);
105
+ color: var(--sigvelo-color-text);
106
+ border-block-end: var(--sigvelo-border-style) var(--sigvelo-border-width)
107
+ var(--sigvelo-color-neutral-border-subtle);
108
+ }
109
+
110
+ .shell__panel-content {
111
+ flex: 1;
112
+ overflow-y: auto;
113
+ padding: var(--sigvelo-spacing-4);
114
+ }
115
+
116
+ /* Content */
117
+ .shell__content {
118
+ flex: 1;
119
+ min-width: 0;
120
+ position: relative;
121
+ overflow: hidden;
122
+ }
@@ -91,16 +91,6 @@
91
91
  }
92
92
 
93
93
  .think-thread-picker__viewport {
94
- --think-thread-picker-fade-size: min(12%, 2.5rem);
95
- --sigvelo-scroll-fade-start: min(
96
- var(--think-thread-picker-fade-size),
97
- var(--scroll-area-overflow-y-start, 0px)
98
- );
99
- --sigvelo-scroll-fade-end: min(
100
- var(--think-thread-picker-fade-size),
101
- var(--scroll-area-overflow-y-end, 0px)
102
- );
103
-
104
94
  padding: var(--sigvelo-spacing-1-5) var(--sigvelo-spacing-2) var(--sigvelo-spacing-3);
105
95
  overscroll-behavior: contain;
106
96
  scrollbar-gutter: stable;
@@ -1 +1,7 @@
1
1
  @import "@mcp-b/design-tokens";
2
+
3
+ /* Opt-in accent and design-language presets, inert until data-preset or
4
+ data-style is set on the root. Imported after the main tokens so their
5
+ equal-specificity overrides win by order. */
6
+ @import "@mcp-b/design-tokens/color-presets";
7
+ @import "@mcp-b/design-tokens/style-presets";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mcp-b/react-components",
3
- "version": "0.22.0",
3
+ "version": "0.24.0",
4
4
  "description": "MCP-B React components built on Base UI and shared design tokens, including Cloudflare Think chat primitives.",
5
5
  "homepage": "https://design-system.sigvelo.com",
6
6
  "bugs": {
@@ -57,7 +57,7 @@
57
57
  "streamdown": "^2.5.0",
58
58
  "unist-util-visit": "^5.1.0",
59
59
  "yet-another-react-lightbox": "^3.32.1",
60
- "@mcp-b/design-tokens": "0.22.0"
60
+ "@mcp-b/design-tokens": "0.24.0"
61
61
  },
62
62
  "devDependencies": {
63
63
  "@ai-sdk/react": "^3.0.222",