@miragon/mcp-toolkit-ui 2.8.0 → 2.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -20,6 +20,11 @@ export interface DrillButtonProps {
20
20
  * it for every in-app drill (row "Open", header nav) so they read identically
21
21
  * and stay visually quieter than primary/agentic actions. Decoupled from any
22
22
  * host — pass an `onDrill` callback (e.g. wrapping `useHostActions().showWidget`).
23
+ *
24
+ * It IS the toolkit's outline `Button` (`buttonVariants`): `sm` is 32 px,
25
+ * 14 px, weight 600, the size of a `Button size="sm"` or an `AskAiButton`
26
+ * beside it in a row; `md` is the `Button` default (36 px). The design
27
+ * system gives every button weight 600 and one radius.
23
28
  */
24
29
  export declare function DrillButton({ children, onDrill, icon, size, ariaLabel }: DrillButtonProps): import("react").JSX.Element;
25
30
  //# sourceMappingURL=DrillButton.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"DrillButton.d.ts","sourceRoot":"","sources":["../../src/components/DrillButton.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAKtC,MAAM,WAAW,gBAAgB;IAC/B,oBAAoB;IACpB,QAAQ,EAAE,SAAS,CAAA;IACnB,mFAAmF;IACnF,OAAO,EAAE,MAAM,IAAI,CAAA;IACnB;;;OAGG;IACH,IAAI,CAAC,EAAE,SAAS,CAAA;IAChB,qEAAqE;IACrE,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,CAAA;IAClB,iGAAiG;IACjG,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,IAAW,EAAE,SAAS,EAAE,EAAE,gBAAgB,+BAsBhG"}
1
+ {"version":3,"file":"DrillButton.d.ts","sourceRoot":"","sources":["../../src/components/DrillButton.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAItC,MAAM,WAAW,gBAAgB;IAC/B,oBAAoB;IACpB,QAAQ,EAAE,SAAS,CAAA;IACnB,mFAAmF;IACnF,OAAO,EAAE,MAAM,IAAI,CAAA;IACnB;;;OAGG;IACH,IAAI,CAAC,EAAE,SAAS,CAAA;IAChB,qEAAqE;IACrE,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,CAAA;IAClB,iGAAiG;IACjG,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAKD;;;;;;;;;;;GAWG;AACH,wBAAgB,WAAW,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,IAAW,EAAE,SAAS,EAAE,EAAE,gBAAgB,+BAiBhG"}
@@ -1,16 +1,21 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { ArrowRight } from "lucide-react";
3
- import { FOCUS_RING } from "../lib/focus-ring.js";
4
- import { cn } from "../lib/utils.js";
3
+ import { buttonVariants } from "../primitives/button.js";
4
+ /** The `Button` size each drill size is: one scale for every action of a view. */
5
+ const BUTTON_SIZE = { sm: "sm", md: "default" };
5
6
  /**
6
7
  * The single, deterministic in-widget navigation control: a neutral outline
7
8
  * button with a trailing Lucide arrow that drills into a detail/sub-view. Use
8
9
  * it for every in-app drill (row "Open", header nav) so they read identically
9
10
  * and stay visually quieter than primary/agentic actions. Decoupled from any
10
11
  * host — pass an `onDrill` callback (e.g. wrapping `useHostActions().showWidget`).
12
+ *
13
+ * It IS the toolkit's outline `Button` (`buttonVariants`): `sm` is 32 px,
14
+ * 14 px, weight 600, the size of a `Button size="sm"` or an `AskAiButton`
15
+ * beside it in a row; `md` is the `Button` default (36 px). The design
16
+ * system gives every button weight 600 and one radius.
11
17
  */
12
18
  export function DrillButton({ children, onDrill, icon, size = "sm", ariaLabel }) {
13
- const pad = size === "md" ? "px-3 py-1.5 text-dense" : "px-2.5 py-1 text-small";
14
- return (_jsxs("button", { type: "button", onClick: onDrill, "aria-label": ariaLabel, className: cn("border-border text-foreground hover:border-focus hover:bg-accent hover:text-accent-foreground inline-flex items-center gap-1 rounded-md border font-medium transition-colors [&_svg:not([class*='size-'])]:size-4", FOCUS_RING, pad), children: [icon && (_jsx("span", { "aria-hidden": "true", className: "inline-flex", children: icon })), children, _jsx(ArrowRight, { "aria-hidden": "true", className: "size-4 shrink-0" })] }));
19
+ return (_jsxs("button", { type: "button", onClick: onDrill, "aria-label": ariaLabel, className: buttonVariants({ variant: "outline", size: BUTTON_SIZE[size] }), children: [icon && (_jsx("span", { "aria-hidden": "true", className: "inline-flex", children: icon })), children, _jsx(ArrowRight, { "aria-hidden": "true", className: "size-4 shrink-0" })] }));
15
20
  }
16
21
  //# sourceMappingURL=DrillButton.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"DrillButton.js","sourceRoot":"","sources":["../../src/components/DrillButton.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AACzC,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAA;AACjD,OAAO,EAAE,EAAE,EAAE,MAAM,iBAAiB,CAAA;AAkBpC;;;;;;GAMG;AACH,MAAM,UAAU,WAAW,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,GAAG,IAAI,EAAE,SAAS,EAAoB;IAC/F,MAAM,GAAG,GAAG,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,wBAAwB,CAAC,CAAC,CAAC,wBAAwB,CAAA;IAC/E,OAAO,CACL,kBACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,OAAO,gBACJ,SAAS,EACrB,SAAS,EAAE,EAAE,CACX,mNAAmN,EACnN,UAAU,EACV,GAAG,CACJ,aAEA,IAAI,IAAI,CACP,8BAAkB,MAAM,EAAC,SAAS,EAAC,aAAa,YAC7C,IAAI,GACA,CACR,EACA,QAAQ,EACT,KAAC,UAAU,mBAAa,MAAM,EAAC,SAAS,EAAC,iBAAiB,GAAG,IACtD,CACV,CAAA;AACH,CAAC"}
1
+ {"version":3,"file":"DrillButton.js","sourceRoot":"","sources":["../../src/components/DrillButton.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AACzC,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAA;AAkBxD,kFAAkF;AAClF,MAAM,WAAW,GAAG,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,SAAS,EAAW,CAAA;AAExD;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,WAAW,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,GAAG,IAAI,EAAE,SAAS,EAAoB;IAC/F,OAAO,CACL,kBACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,OAAO,gBACJ,SAAS,EACrB,SAAS,EAAE,cAAc,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC,aAEzE,IAAI,IAAI,CACP,8BAAkB,MAAM,EAAC,SAAS,EAAC,aAAa,YAC7C,IAAI,GACA,CACR,EACA,QAAQ,EACT,KAAC,UAAU,mBAAa,MAAM,EAAC,SAAS,EAAC,iBAAiB,GAAG,IACtD,CACV,CAAA;AACH,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@miragon/mcp-toolkit-ui",
3
- "version": "2.8.0",
3
+ "version": "2.8.1",
4
4
  "description": "React UI for MCP servers in the Miragon design system (white-label through role tokens): primitives, composite components, TanStack Query hooks, and the MCP App shell (McpAppView + WidgetRenderer).",
5
5
  "repository": {
6
6
  "type": "git",
@@ -45,7 +45,7 @@
45
45
  "react": "^19.2.0",
46
46
  "react-dom": "^19.2.0",
47
47
  "tailwindcss": "^4.3.0",
48
- "@miragon/mcp-toolkit-core": "~2.8.0"
48
+ "@miragon/mcp-toolkit-core": "~2.8.1"
49
49
  },
50
50
  "dependencies": {
51
51
  "@base-ui/react": "1.8.0",
@@ -74,7 +74,7 @@
74
74
  "tailwindcss": "4.3.3",
75
75
  "typescript": "6.0.3",
76
76
  "vitest": "4.1.11",
77
- "@miragon/mcp-toolkit-core": "2.8.0"
77
+ "@miragon/mcp-toolkit-core": "2.8.1"
78
78
  },
79
79
  "engines": {
80
80
  "node": ">=22.22.2"
@@ -0,0 +1,48 @@
1
+ import { renderToStaticMarkup } from "react-dom/server"
2
+ import { describe, expect, it } from "vitest"
3
+ import { ExternalLink } from "lucide-react"
4
+ import { attribute, openingTags, userFacingTexts } from "../render-scan.js"
5
+ import { buttonVariants } from "../primitives/button.js"
6
+ import { DrillButton } from "./DrillButton.js"
7
+
8
+ const noop = () => undefined
9
+ const classOf = (tag: string | undefined) => attribute(tag ?? "", "class") ?? ""
10
+
11
+ describe("DrillButton: the outline Button with a trailing arrow (mcp-toolkit#194)", () => {
12
+ it("sm is a Button size sm: 32 px, 14 px, weight 600, like the buttons beside it in a row", () => {
13
+ const html = renderToStaticMarkup(<DrillButton onDrill={noop}>Open</DrillButton>)
14
+ const [button] = openingTags(html, "button")
15
+ expect(classOf(button)).toBe(buttonVariants({ variant: "outline", size: "sm" }))
16
+ expect(classOf(button)).toMatch(/\bh-8\b/)
17
+ expect(classOf(button)).toMatch(/\btext-sm\b/)
18
+ expect(classOf(button)).toMatch(/\bfont-semibold\b/)
19
+ expect(attribute(button ?? "", "type")).toBe("button")
20
+ })
21
+
22
+ it("md is the Button default size (surface headers)", () => {
23
+ const html = renderToStaticMarkup(
24
+ <DrillButton onDrill={noop} size="md">
25
+ View running instances
26
+ </DrillButton>,
27
+ )
28
+ expect(classOf(openingTags(html, "button")[0])).toBe(
29
+ buttonVariants({ variant: "outline", size: "default" }),
30
+ )
31
+ })
32
+
33
+ it("keeps its words: the label, a decorative leading icon and a decorative trailing arrow", () => {
34
+ const html = renderToStaticMarkup(
35
+ <DrillButton onDrill={noop} icon={<ExternalLink />} ariaLabel="Open order o-1001">
36
+ Open
37
+ </DrillButton>,
38
+ )
39
+ const [button] = openingTags(html, "button")
40
+ expect(attribute(button ?? "", "aria-label")).toBe("Open order o-1001")
41
+ expect(userFacingTexts(html).map((t) => t.value)).toContain("Open")
42
+ const svgs = openingTags(html, "svg")
43
+ expect(svgs).toHaveLength(2)
44
+ expect(svgs.at(-1)).toContain("lucide-arrow-right")
45
+ for (const svg of svgs) expect(attribute(svg, "aria-hidden")).toBe("true")
46
+ expect(openingTags(html, "span")[0]).toContain('aria-hidden="true"')
47
+ })
48
+ })
@@ -1,7 +1,6 @@
1
1
  import type { ReactNode } from "react"
2
2
  import { ArrowRight } from "lucide-react"
3
- import { FOCUS_RING } from "../lib/focus-ring.js"
4
- import { cn } from "../lib/utils.js"
3
+ import { buttonVariants } from "../primitives/button.js"
5
4
 
6
5
  export interface DrillButtonProps {
7
6
  /** Button label. */
@@ -19,25 +18,28 @@ export interface DrillButtonProps {
19
18
  ariaLabel?: string
20
19
  }
21
20
 
21
+ /** The `Button` size each drill size is: one scale for every action of a view. */
22
+ const BUTTON_SIZE = { sm: "sm", md: "default" } as const
23
+
22
24
  /**
23
25
  * The single, deterministic in-widget navigation control: a neutral outline
24
26
  * button with a trailing Lucide arrow that drills into a detail/sub-view. Use
25
27
  * it for every in-app drill (row "Open", header nav) so they read identically
26
28
  * and stay visually quieter than primary/agentic actions. Decoupled from any
27
29
  * host — pass an `onDrill` callback (e.g. wrapping `useHostActions().showWidget`).
30
+ *
31
+ * It IS the toolkit's outline `Button` (`buttonVariants`): `sm` is 32 px,
32
+ * 14 px, weight 600, the size of a `Button size="sm"` or an `AskAiButton`
33
+ * beside it in a row; `md` is the `Button` default (36 px). The design
34
+ * system gives every button weight 600 and one radius.
28
35
  */
29
36
  export function DrillButton({ children, onDrill, icon, size = "sm", ariaLabel }: DrillButtonProps) {
30
- const pad = size === "md" ? "px-3 py-1.5 text-dense" : "px-2.5 py-1 text-small"
31
37
  return (
32
38
  <button
33
39
  type="button"
34
40
  onClick={onDrill}
35
41
  aria-label={ariaLabel}
36
- className={cn(
37
- "border-border text-foreground hover:border-focus hover:bg-accent hover:text-accent-foreground inline-flex items-center gap-1 rounded-md border font-medium transition-colors [&_svg:not([class*='size-'])]:size-4",
38
- FOCUS_RING,
39
- pad,
40
- )}
42
+ className={buttonVariants({ variant: "outline", size: BUTTON_SIZE[size] })}
41
43
  >
42
44
  {icon && (
43
45
  <span aria-hidden="true" className="inline-flex">