@agent-native/toolkit 0.10.2 → 0.10.5

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.
Files changed (62) hide show
  1. package/README.md +8 -1
  2. package/agent-native.eject.json +1 -1
  3. package/dist/app-shell/index.d.ts +1 -0
  4. package/dist/app-shell/index.d.ts.map +1 -1
  5. package/dist/app-shell/index.js +1 -0
  6. package/dist/app-shell/index.js.map +1 -1
  7. package/dist/app-shell/sidebar-footer-actions.d.ts +15 -0
  8. package/dist/app-shell/sidebar-footer-actions.d.ts.map +1 -0
  9. package/dist/app-shell/sidebar-footer-actions.js +10 -0
  10. package/dist/app-shell/sidebar-footer-actions.js.map +1 -0
  11. package/dist/app-shell/sidebar-footer-actions.spec.d.ts +2 -0
  12. package/dist/app-shell/sidebar-footer-actions.spec.d.ts.map +1 -0
  13. package/dist/app-shell/sidebar-footer-actions.spec.js +35 -0
  14. package/dist/app-shell/sidebar-footer-actions.spec.js.map +1 -0
  15. package/dist/chat-history.css +52 -1
  16. package/dist/design-system/default-adapter.js +1 -1
  17. package/dist/design-system/default-adapter.js.map +1 -1
  18. package/dist/design-system/design-system.spec.js +47 -2
  19. package/dist/design-system/design-system.spec.js.map +1 -1
  20. package/dist/editor/SharedRichEditor.d.ts +3 -1
  21. package/dist/editor/SharedRichEditor.d.ts.map +1 -1
  22. package/dist/editor/SharedRichEditor.js +17 -3
  23. package/dist/editor/SharedRichEditor.js.map +1 -1
  24. package/dist/editor/SharedRichEditor.spec.d.ts +2 -0
  25. package/dist/editor/SharedRichEditor.spec.d.ts.map +1 -0
  26. package/dist/editor/SharedRichEditor.spec.js +63 -0
  27. package/dist/editor/SharedRichEditor.spec.js.map +1 -0
  28. package/dist/editor/SlashCommandMenu.d.ts +10 -0
  29. package/dist/editor/SlashCommandMenu.d.ts.map +1 -1
  30. package/dist/editor/SlashCommandMenu.js +54 -2
  31. package/dist/editor/SlashCommandMenu.js.map +1 -1
  32. package/dist/editor/SlashCommandMenu.spec.js +48 -1
  33. package/dist/editor/SlashCommandMenu.spec.js.map +1 -1
  34. package/dist/editor/index.d.ts +1 -1
  35. package/dist/editor/index.d.ts.map +1 -1
  36. package/dist/editor/index.js +1 -1
  37. package/dist/editor/index.js.map +1 -1
  38. package/dist/editor.css +75 -0
  39. package/dist/ui/button.d.ts.map +1 -1
  40. package/dist/ui/button.js +3 -3
  41. package/dist/ui/button.js.map +1 -1
  42. package/dist/ui/card.js +1 -1
  43. package/dist/ui/card.js.map +1 -1
  44. package/dist/ui/table.d.ts.map +1 -1
  45. package/dist/ui/table.js +4 -4
  46. package/dist/ui/table.js.map +1 -1
  47. package/package.json +1 -1
  48. package/src/app-shell/index.ts +1 -0
  49. package/src/app-shell/sidebar-footer-actions.spec.tsx +65 -0
  50. package/src/app-shell/sidebar-footer-actions.tsx +48 -0
  51. package/src/chat-history.css +52 -1
  52. package/src/design-system/default-adapter.tsx +1 -1
  53. package/src/design-system/design-system.spec.tsx +99 -1
  54. package/src/editor/SharedRichEditor.spec.tsx +82 -0
  55. package/src/editor/SharedRichEditor.tsx +23 -2
  56. package/src/editor/SlashCommandMenu.spec.ts +57 -0
  57. package/src/editor/SlashCommandMenu.tsx +68 -2
  58. package/src/editor/index.ts +1 -0
  59. package/src/editor.css +75 -0
  60. package/src/ui/button.tsx +4 -4
  61. package/src/ui/card.tsx +1 -1
  62. package/src/ui/table.tsx +4 -11
@@ -332,7 +332,7 @@ const DefaultSurface: DesignSystemComponents["Surface"] = ({
332
332
  const elementProps = {
333
333
  ...props,
334
334
  className: cn(
335
- "rounded-lg border bg-card text-card-foreground",
335
+ "rounded-lg bg-card text-card-foreground",
336
336
  elevation === "none" && "shadow-none",
337
337
  elevation === "low" && "shadow-sm",
338
338
  elevation === "medium" && "shadow-md",
@@ -1,11 +1,17 @@
1
1
  // @vitest-environment happy-dom
2
2
 
3
- import { act, type ComponentProps } from "react";
3
+ import { act, forwardRef, type ComponentProps } from "react";
4
4
  import { createRoot, type Root } from "react-dom/client";
5
5
  import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
6
6
 
7
7
  import { ToolkitProvider } from "../provider.js";
8
8
  import { Button } from "../ui/button.js";
9
+ import {
10
+ DropdownMenu,
11
+ DropdownMenuContent,
12
+ DropdownMenuItem,
13
+ DropdownMenuTrigger,
14
+ } from "../ui/dropdown-menu.js";
9
15
  import { ActionButton } from "./components.js";
10
16
  import { defaultDesignSystemComponents } from "./default-adapter.js";
11
17
  import { defineDesignSystem } from "./definition.js";
@@ -157,6 +163,98 @@ describe("design-system contract", () => {
157
163
  );
158
164
  });
159
165
 
166
+ it.each(["pointer", "Enter", "Space", "ArrowDown"])(
167
+ "preserves Radix menu trigger behavior through a legacy Button override with %s activation",
168
+ async (activation) => {
169
+ const onOpenChange = vi.fn();
170
+ let buttonRef: HTMLButtonElement | null = null;
171
+ const LegacyButton = forwardRef<
172
+ HTMLButtonElement,
173
+ ComponentProps<typeof Button>
174
+ >(
175
+ (
176
+ {
177
+ asChild: _asChild,
178
+ emphasis: _emphasis,
179
+ intent: _intent,
180
+ size: _size,
181
+ variant: _variant,
182
+ ...props
183
+ },
184
+ ref,
185
+ ) => <button {...props} ref={ref} data-adapter="legacy" />,
186
+ );
187
+
188
+ await act(async () => {
189
+ root.render(
190
+ <ToolkitProvider
191
+ components={{ Button: LegacyButton }}
192
+ designSystem={{}}
193
+ >
194
+ <DropdownMenu onOpenChange={onOpenChange}>
195
+ <DropdownMenuTrigger asChild>
196
+ <Button
197
+ ref={(node) => {
198
+ buttonRef = node;
199
+ }}
200
+ >
201
+ Open menu
202
+ </Button>
203
+ </DropdownMenuTrigger>
204
+ <DropdownMenuContent>
205
+ <DropdownMenuItem>First item</DropdownMenuItem>
206
+ </DropdownMenuContent>
207
+ </DropdownMenu>
208
+ </ToolkitProvider>,
209
+ );
210
+ });
211
+
212
+ const trigger = container.querySelector<HTMLButtonElement>("button");
213
+ expect(trigger).not.toBeNull();
214
+ expect(buttonRef).toBe(trigger);
215
+ expect(trigger?.getAttribute("aria-haspopup")).toBe("menu");
216
+ expect(trigger?.getAttribute("aria-expanded")).toBe("false");
217
+ expect(trigger?.dataset.state).toBe("closed");
218
+
219
+ await act(async () => {
220
+ trigger?.focus();
221
+ if (activation === "pointer") {
222
+ trigger?.dispatchEvent(
223
+ new PointerEvent("pointerdown", {
224
+ bubbles: true,
225
+ button: 0,
226
+ pointerType: "mouse",
227
+ }),
228
+ );
229
+ } else {
230
+ const key = activation === "Space" ? " " : activation;
231
+ trigger?.dispatchEvent(
232
+ new KeyboardEvent("keydown", { bubbles: true, key }),
233
+ );
234
+ }
235
+ await Promise.resolve();
236
+ });
237
+
238
+ expect(onOpenChange).toHaveBeenCalledTimes(1);
239
+ expect(onOpenChange).toHaveBeenLastCalledWith(true);
240
+ expect(trigger?.getAttribute("aria-expanded")).toBe("true");
241
+ expect(trigger?.dataset.state).toBe("open");
242
+ expect(document.querySelector("[role=menu]")).not.toBeNull();
243
+
244
+ await act(async () => {
245
+ document.activeElement?.dispatchEvent(
246
+ new KeyboardEvent("keydown", { bubbles: true, key: "Escape" }),
247
+ );
248
+ await Promise.resolve();
249
+ });
250
+
251
+ expect(onOpenChange).toHaveBeenCalledTimes(2);
252
+ expect(onOpenChange).toHaveBeenLastCalledWith(false);
253
+ expect(trigger?.getAttribute("aria-expanded")).toBe("false");
254
+ expect(trigger?.dataset.state).toBe("closed");
255
+ },
256
+ );
257
+
160
258
  it("isolates a broken customer component and renders the default control", () => {
161
259
  vi.spyOn(console, "error").mockImplementation(() => {});
162
260
  const BrokenActionButton = () => {
@@ -0,0 +1,82 @@
1
+ // @vitest-environment happy-dom
2
+
3
+ import { act } from "react";
4
+ import { createRoot, type Root } from "react-dom/client";
5
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
6
+
7
+ import { SharedRichEditor } from "./SharedRichEditor.js";
8
+
9
+ describe("SharedRichEditor block controls", () => {
10
+ let container: HTMLDivElement;
11
+ let root: Root;
12
+
13
+ beforeEach(() => {
14
+ vi.stubGlobal("IS_REACT_ACT_ENVIRONMENT", true);
15
+ container = document.createElement("div");
16
+ document.body.appendChild(container);
17
+ root = createRoot(container);
18
+ });
19
+
20
+ afterEach(() => {
21
+ act(() => root.unmount());
22
+ container.remove();
23
+ vi.restoreAllMocks();
24
+ vi.unstubAllGlobals();
25
+ });
26
+
27
+ it("mounts the shared Notion-style block grip by default", async () => {
28
+ await act(async () => {
29
+ root.render(
30
+ <SharedRichEditor
31
+ value="<p>First</p><p>Second</p>"
32
+ onChange={() => undefined}
33
+ />,
34
+ );
35
+ await new Promise((resolve) => setTimeout(resolve, 10));
36
+ });
37
+
38
+ const prose = container.querySelector<HTMLElement>(".an-rich-md-prose");
39
+ const firstBlock = prose?.firstElementChild;
40
+ for (const [element, rect] of [
41
+ [prose, { left: 0, top: 0, right: 640, bottom: 100 }],
42
+ [firstBlock, { left: 0, top: 0, right: 640, bottom: 24 }],
43
+ ] as const) {
44
+ if (!element) continue;
45
+ Object.defineProperty(element, "getBoundingClientRect", {
46
+ configurable: true,
47
+ value: () => ({
48
+ ...rect,
49
+ x: rect.left,
50
+ y: rect.top,
51
+ width: rect.right - rect.left,
52
+ height: rect.bottom - rect.top,
53
+ toJSON: () => ({}),
54
+ }),
55
+ });
56
+ }
57
+ document.dispatchEvent(
58
+ new MouseEvent("mousemove", {
59
+ bubbles: true,
60
+ clientX: 8,
61
+ clientY: 8,
62
+ }),
63
+ );
64
+ expect(container.querySelector(".an-rich-md-wrapper")).not.toBeNull();
65
+ expect(container.querySelector(".drag-handle")).not.toBeNull();
66
+ });
67
+
68
+ it("allows hosts to opt out when they own block controls", async () => {
69
+ await act(async () => {
70
+ root.render(
71
+ <SharedRichEditor
72
+ value="First"
73
+ onChange={() => undefined}
74
+ dragHandle={false}
75
+ />,
76
+ );
77
+ await new Promise((resolve) => setTimeout(resolve, 10));
78
+ });
79
+
80
+ expect(container.querySelector(".drag-handle")).toBeNull();
81
+ });
82
+ });
@@ -6,6 +6,7 @@ import type { Doc as YDoc } from "yjs";
6
6
 
7
7
  import { cn } from "../utils.js";
8
8
  import { BubbleToolbar, type BubbleToolbarItem } from "./BubbleToolbar.js";
9
+ import { DragHandle } from "./DragHandle.js";
9
10
  import {
10
11
  createSharedEditorExtensions,
11
12
  type RichMarkdownDialect,
@@ -37,6 +38,8 @@ export interface SharedRichEditorProps {
37
38
  preset?: RichMarkdownEditorPreset;
38
39
  /** Toggle individual base extensions (tables/tasks/link/codeBlock/image). */
39
40
  features?: SharedEditorFeatures;
41
+ /** Show the Notion-style block grip and action menu. Defaults to `true`. */
42
+ dragHandle?: boolean;
40
43
  /**
41
44
  * Injectable image uploader for the shared image block. Used only when
42
45
  * `features.image` is on. Pass `uploadEditorImage` (the framework
@@ -141,6 +144,7 @@ export function SharedRichEditor({
141
144
  dialect = "gfm",
142
145
  preset = "plan",
143
146
  features,
147
+ dragHandle = true,
144
148
  onImageUpload = null,
145
149
  extraExtensions,
146
150
  placeholder = "Type '/' for commands...",
@@ -170,6 +174,23 @@ export function SharedRichEditor({
170
174
  onChangeRef.current = onChange;
171
175
  onBlurRef.current = onBlur;
172
176
 
177
+ const effectiveExtraExtensions = useMemo(() => {
178
+ const extras = extraExtensions ?? [];
179
+ if (
180
+ !dragHandle ||
181
+ extras.some((extension) => extension.name === "dragHandle")
182
+ ) {
183
+ return extras;
184
+ }
185
+
186
+ return [
187
+ DragHandle.configure({
188
+ wrapperSelector: ".an-rich-md-wrapper",
189
+ }),
190
+ ...extras,
191
+ ];
192
+ }, [dragHandle, extraExtensions]);
193
+
173
194
  const extensions = useMemo(
174
195
  () =>
175
196
  createSharedEditorExtensions({
@@ -177,7 +198,7 @@ export function SharedRichEditor({
177
198
  preset,
178
199
  placeholder,
179
200
  features,
180
- extraExtensions,
201
+ extraExtensions: effectiveExtraExtensions,
181
202
  onImageUpload,
182
203
  collab: ydoc ? { ydoc, awareness, user } : null,
183
204
  disableHistory,
@@ -192,7 +213,7 @@ export function SharedRichEditor({
192
213
  placeholder,
193
214
  preset,
194
215
  features,
195
- extraExtensions,
216
+ effectiveExtraExtensions,
196
217
  onImageUpload,
197
218
  ydoc,
198
219
  awareness,
@@ -1,8 +1,13 @@
1
+ // @vitest-environment happy-dom
2
+
3
+ import { Editor } from "@tiptap/core";
4
+ import StarterKit from "@tiptap/starter-kit";
1
5
  import { describe, expect, it } from "vitest";
2
6
 
3
7
  import {
4
8
  DEFAULT_SLASH_COMMANDS,
5
9
  filterSlashCommandItems,
10
+ focusEditorInInsertedBlock,
6
11
  } from "./SlashCommandMenu.js";
7
12
 
8
13
  describe("filterSlashCommandItems", () => {
@@ -31,3 +36,55 @@ describe("filterSlashCommandItems", () => {
31
36
  );
32
37
  });
33
38
  });
39
+
40
+ describe("focusEditorInInsertedBlock", () => {
41
+ it.each([
42
+ ["Text", "paragraph", "paragraph"],
43
+ ["Heading 1", "heading", "heading"],
44
+ ["Bulleted list", "bulletList", "paragraph"],
45
+ ["Numbered list", "orderedList", "paragraph"],
46
+ ["Quote", "blockquote", "paragraph"],
47
+ ["Code block", "codeBlock", "codeBlock"],
48
+ ])(
49
+ "leaves the caret inside the %s block instead of after it",
50
+ (title, activeNode, parentNode) => {
51
+ const element = document.createElement("div");
52
+ document.body.appendChild(element);
53
+ const editor = new Editor({
54
+ element,
55
+ extensions: [StarterKit],
56
+ content: {
57
+ type: "doc",
58
+ content: [
59
+ {
60
+ type: "paragraph",
61
+ content: [{ type: "text", text: "/code" }],
62
+ },
63
+ ],
64
+ },
65
+ });
66
+
67
+ try {
68
+ const slashRange = { from: 1, to: 6 };
69
+ editor.commands.setTextSelection(slashRange);
70
+ editor.chain().focus().deleteRange(slashRange).run();
71
+ const command = DEFAULT_SLASH_COMMANDS.find(
72
+ (item) => item.title === title,
73
+ );
74
+ if (!command) throw new Error(`Missing slash command: ${title}`);
75
+ command.action(editor);
76
+ focusEditorInInsertedBlock(editor, slashRange.from);
77
+
78
+ expect(editor.isActive(activeNode)).toBe(true);
79
+ expect(editor.state.selection.$from.parent.type.name).toBe(parentNode);
80
+ expect(editor.state.selection.from).toBeLessThanOrEqual(
81
+ editor.state.selection.$from.end(),
82
+ );
83
+ expect(editor.isFocused).toBe(true);
84
+ } finally {
85
+ editor.destroy();
86
+ element.remove();
87
+ }
88
+ },
89
+ );
90
+ });
@@ -1,3 +1,4 @@
1
+ import { TextSelection } from "@tiptap/pm/state";
1
2
  import type { Editor } from "@tiptap/react";
2
3
  import {
3
4
  useCallback,
@@ -27,6 +28,66 @@ export interface SlashCommandItem {
27
28
  action: (editor: Editor) => void;
28
29
  }
29
30
 
31
+ /**
32
+ * Put the caret inside the block a slash command just created or transformed.
33
+ *
34
+ * Tiptap commands usually preserve a text selection, but commands that replace
35
+ * the current paragraph (notably code blocks and horizontal rules) can leave a
36
+ * node selection or a caret at the following block. Resolving from the slash
37
+ * range keeps the behavior consistent for built-in and app-provided block
38
+ * commands: after selecting a block, typing starts in that block immediately.
39
+ */
40
+ export function focusEditorInInsertedBlock(
41
+ editor: Editor,
42
+ anchorPosition?: number,
43
+ ): void {
44
+ if (editor.isDestroyed) return;
45
+
46
+ const { state } = editor;
47
+ const anchor = Math.max(
48
+ 1,
49
+ Math.min(anchorPosition ?? state.selection.from, state.doc.content.size),
50
+ );
51
+ let containingTextblock: number | null = null;
52
+ let nextTextblock: number | null = null;
53
+ let previousTextblock: number | null = null;
54
+
55
+ state.doc.nodesBetween(0, state.doc.content.size, (node, pos) => {
56
+ if (!node.isTextblock) return;
57
+
58
+ const start = pos + 1;
59
+ const end = pos + node.nodeSize - 1;
60
+ if (anchor >= start && anchor <= end) {
61
+ containingTextblock ??= anchor;
62
+ return;
63
+ }
64
+ if (start > anchor) {
65
+ if (nextTextblock === null || start < nextTextblock) {
66
+ nextTextblock = start;
67
+ }
68
+ return;
69
+ }
70
+ if (previousTextblock === null || start > previousTextblock) {
71
+ previousTextblock = Math.min(anchor, end);
72
+ }
73
+ });
74
+
75
+ const target = containingTextblock ?? nextTextblock ?? previousTextblock;
76
+ if (target === null) {
77
+ editor.commands.focus();
78
+ return;
79
+ }
80
+
81
+ const transaction = state.tr.setSelection(
82
+ TextSelection.create(
83
+ state.doc,
84
+ Math.max(1, Math.min(target, state.doc.content.size)),
85
+ ),
86
+ );
87
+ editor.view.dispatch(transaction.scrollIntoView());
88
+ editor.view.focus();
89
+ }
90
+
30
91
  export interface SlashCommandFeatureFlags {
31
92
  tables?: boolean;
32
93
  tasks?: boolean;
@@ -192,13 +253,18 @@ export function SlashCommandMenu({
192
253
  (command: SlashCommandItem) => {
193
254
  if (slashPosRef.current !== null) {
194
255
  const { from } = editor.state.selection;
256
+ const anchorPosition = slashPosRef.current;
195
257
  editor
196
258
  .chain()
197
259
  .focus()
198
- .deleteRange({ from: slashPosRef.current, to: from })
260
+ .deleteRange({ from: anchorPosition, to: from })
199
261
  .run();
262
+ command.action(editor);
263
+ focusEditorInInsertedBlock(editor, anchorPosition);
264
+ } else {
265
+ command.action(editor);
266
+ focusEditorInInsertedBlock(editor);
200
267
  }
201
- command.action(editor);
202
268
  close();
203
269
  },
204
270
  [close, editor],
@@ -26,6 +26,7 @@ export {
26
26
  DEFAULT_SLASH_COMMANDS,
27
27
  createImageSlashCommand,
28
28
  filterSlashCommandItems,
29
+ focusEditorInInsertedBlock,
29
30
  type SlashCommandItem,
30
31
  type SlashCommandMenuProps,
31
32
  type SlashCommandFeatureFlags,
package/src/editor.css CHANGED
@@ -8,6 +8,81 @@
8
8
  cursor: default;
9
9
  }
10
10
 
11
+ /* Shared Notion-style block controls. Apps can override the color tokens, but
12
+ every SharedRichEditor gets a usable grip/menu without app-local CSS. */
13
+ .drag-handle {
14
+ position: absolute;
15
+ display: none;
16
+ width: 24px;
17
+ height: 24px;
18
+ align-items: center;
19
+ justify-content: center;
20
+ border-radius: 4px;
21
+ color: hsl(var(--muted-foreground) / 0.45);
22
+ cursor: grab;
23
+ user-select: none;
24
+ touch-action: none;
25
+ z-index: 10;
26
+ }
27
+
28
+ .drag-handle:hover,
29
+ .drag-handle:focus-visible {
30
+ background: hsl(var(--muted));
31
+ color: hsl(var(--muted-foreground));
32
+ outline: none;
33
+ }
34
+
35
+ .drag-handle:active {
36
+ cursor: grabbing;
37
+ }
38
+
39
+ .notion-editor-is-dragging,
40
+ .notion-editor-is-dragging * {
41
+ cursor: grabbing !important;
42
+ }
43
+
44
+ .notion-block--dragging {
45
+ opacity: 0.35;
46
+ transition: opacity 0.12s ease;
47
+ }
48
+
49
+ .notion-drag-preview {
50
+ position: fixed;
51
+ top: 0;
52
+ left: 0;
53
+ z-index: 9999;
54
+ max-width: min(720px, calc(100vw - 48px));
55
+ overflow: hidden;
56
+ background: transparent !important;
57
+ box-shadow: none;
58
+ opacity: 0.55;
59
+ pointer-events: none;
60
+ will-change: transform;
61
+ }
62
+
63
+ .notion-drag-preview > * {
64
+ margin: 0 !important;
65
+ max-height: 180px;
66
+ overflow: hidden;
67
+ }
68
+
69
+ .notion-drag-preview,
70
+ .notion-drag-preview * {
71
+ background: transparent !important;
72
+ background-color: transparent !important;
73
+ }
74
+
75
+ .notion-drop-indicator {
76
+ position: absolute;
77
+ z-index: 60;
78
+ height: 3px;
79
+ border-radius: 999px;
80
+ background: hsl(var(--ring));
81
+ box-shadow: 0 0 0 1px hsl(var(--background));
82
+ pointer-events: none;
83
+ transform: translateY(-1px);
84
+ }
85
+
11
86
  .an-rich-md-loading {
12
87
  min-height: 7rem;
13
88
  }
package/src/ui/button.tsx CHANGED
@@ -4,7 +4,7 @@ import * as React from "react";
4
4
 
5
5
  import {
6
6
  LegacyButtonRenderContext,
7
- useDesignSystemComponent,
7
+ useDesignSystem,
8
8
  } from "../design-system/context.js";
9
9
  import { DesignSystemErrorBoundary } from "../design-system/error-boundary.js";
10
10
  import type {
@@ -22,8 +22,7 @@ const buttonVariants = cva(
22
22
  default: "bg-primary text-primary-foreground hover:bg-primary/90",
23
23
  destructive:
24
24
  "bg-destructive text-destructive-foreground hover:bg-destructive/90",
25
- outline:
26
- "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
25
+ outline: "bg-accent text-accent-foreground hover:bg-accent/80",
27
26
  secondary:
28
27
  "bg-secondary text-secondary-foreground hover:bg-secondary/80",
29
28
  ghost: "hover:bg-accent hover:text-accent-foreground",
@@ -83,7 +82,8 @@ const ButtonOverrideRenderContext = React.createContext(false);
83
82
 
84
83
  const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
85
84
  ({ variant, size, asChild = false, intent, emphasis, ...props }, ref) => {
86
- const DesignSystemActionButton = useDesignSystemComponent("ActionButton");
85
+ const DesignSystemActionButton =
86
+ useDesignSystem()?.components?.ActionButton;
87
87
  const Override = useToolkitComponent("Button");
88
88
  const isRenderingOverride = React.useContext(ButtonOverrideRenderContext);
89
89
  const isRenderingLegacyButton = React.useContext(LegacyButtonRenderContext);
package/src/ui/card.tsx CHANGED
@@ -9,7 +9,7 @@ const Card = React.forwardRef<
9
9
  <div
10
10
  ref={ref}
11
11
  className={cn(
12
- "rounded-lg border bg-card text-card-foreground shadow-sm",
12
+ "rounded-lg bg-card text-card-foreground shadow-sm",
13
13
  className,
14
14
  )}
15
15
  {...props}
package/src/ui/table.tsx CHANGED
@@ -20,7 +20,7 @@ const TableHeader = React.forwardRef<
20
20
  HTMLTableSectionElement,
21
21
  React.HTMLAttributes<HTMLTableSectionElement>
22
22
  >(({ className, ...props }, ref) => (
23
- <thead ref={ref} className={cn("[&_tr]:border-b", className)} {...props} />
23
+ <thead ref={ref} className={className} {...props} />
24
24
  ));
25
25
  TableHeader.displayName = "TableHeader";
26
26
 
@@ -28,11 +28,7 @@ const TableBody = React.forwardRef<
28
28
  HTMLTableSectionElement,
29
29
  React.HTMLAttributes<HTMLTableSectionElement>
30
30
  >(({ className, ...props }, ref) => (
31
- <tbody
32
- ref={ref}
33
- className={cn("[&_tr:last-child]:border-0", className)}
34
- {...props}
35
- />
31
+ <tbody ref={ref} className={className} {...props} />
36
32
  ));
37
33
  TableBody.displayName = "TableBody";
38
34
 
@@ -42,10 +38,7 @@ const TableFooter = React.forwardRef<
42
38
  >(({ className, ...props }, ref) => (
43
39
  <tfoot
44
40
  ref={ref}
45
- className={cn(
46
- "border-t bg-muted/50 font-medium [&>tr]:last:border-b-0",
47
- className,
48
- )}
41
+ className={cn("bg-muted/50 font-medium", className)}
49
42
  {...props}
50
43
  />
51
44
  ));
@@ -58,7 +51,7 @@ const TableRow = React.forwardRef<
58
51
  <tr
59
52
  ref={ref}
60
53
  className={cn(
61
- "border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted",
54
+ "transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted",
62
55
  className,
63
56
  )}
64
57
  {...props}