Card Component

Display content in a contained card layout with optional title, fields, and custom actions

Card Component

Overview

The Card component provides a simple, clean container for content with an optional title. It supports different styling variants, buttons, and other fields. You can also trigger tool calls via custom button actions.

Table of Contents

  1. Usage
  2. Configuration
  3. Fields
  4. Actions
  5. Examples

Usage

To use the Card component in your tool handler:

  1. First, import the required components:
import { CardUIBuilder, DainResponse } from "@dainprotocol/utils";
  1. Create and configure your card:
const cardUI = new CardUIBuilder()
  .title("Important Information")    // Optional
  .content("This is the main content of the card") // Optional but recommended
  .build();

return new DainResponse({
  text: "Generated card content",
  data: { /* Your data */ },
  ui: cardUI
});

Configuration

Card Props

NameTypeDescriptionRequired?Default
titlestringThe heading or title to display at the top of the card.No""
contentstringThe main content text of the card.Yes""
variant"default" | "destructive" | "outline"Styling variant for the card.No"default"
buttonTextstring(Retro-compat) Deprecated in favor of .addButton().No""
buttonUrlstring(Retro-compat) Deprecated in favor of .addButton() with link.No""
fieldsCardField[]Additional fields (label-value pairs) to display in the card.No[]
actionAlignment"row" | "column"How to align actions/buttons in the card.No"row"

CardField Interface

export interface CardField {
  label: string;        // Label text (e.g. "User ID")
  value: string;        // Value text (e.g. "12345")
  type?: "link" | "button"; // Optional: can display as a link or button
  link?: string;        // If 'type' is "link", provide a URL
}

Fields

Note: Fields can be added using several methods, giving you flexibility in how you structure your card's content.

You can add fields using these methods:

  • .addField(field: CardField): Add a single field
  • .addFields(fields: CardField[]): Add multiple fields at once
  • .addFields(items: T[], mapper: (item: T) => CardField): Add multiple fields using a mapping function

Example:

const cardUI = new CardUIBuilder()
  .title("Order Details")
  .content("Here are the details of your recent purchase.")
  .addField({ label: "Order ID", value: "12345" })
  .addField({ label: "Status", value: "Shipped" })
  .build();

Actions

Retro Compatibility: onConfirm

Warning: While still supported, onConfirm is considered legacy. Consider using the new button methods for more flexibility.

cardUI.onConfirm({
  tool: "yourToolName",
  paramSchema: {
    param1: { type: "string" }
  },
  params: {
    preset: "value"
  }
});

New Methods: addButton, addButtons

For more flexible actions, we provide several button-related methods:

addButton

export interface ButtonParams {
  label: string; // The text displayed on the button
  tool: string;  // The name of the tool to call
  paramSchema: paramSchema;  // Tool parameters
  params: Record<string, unknown>; // Predefined values
  variant?: ButtonVariant; // "primary" | "secondary" | "destructive" | "cancel" | "outline"
}

Example:

const cardUI = new CardUIBuilder()
  .title("Manage Account")
  .content("Choose an action to perform on your account")
  .addButton({
    label: "Delete Account",
    tool: "accountManager",
    paramSchema: {
      userId: { type: "string" },
      reason: { type: "string" }
    },
    params: {
      action: "delete",
      timestamp: Date.now()
    },
    variant: "destructive"
  })
  .build();

addButtons

You can add multiple buttons either as an array or using a mapper function:

Array Method:
const cardUI = new CardUIBuilder()
  .title("Account Actions")
  .content("Choose one of the following options")
  .addButtons([
    {
      label: "Suspend",
      tool: "accountManager",
      paramSchema: { userId: { type: "string" } },
      params: { action: "suspend" },
      variant: "secondary"
    },
    {
      label: "Delete",
      tool: "accountManager",
      paramSchema: { userId: { type: "string" } },
      params: { action: "delete" },
      variant: "destructive"
    }
  ])
  .build();
Mapper Method:
const actions = [
  { actionName: "suspend", label: "Suspend", variant: "secondary" },
  { actionName: "delete", label: "Delete", variant: "destructive" }
];

const cardUI = new CardUIBuilder()
  .title("Dynamic Actions")
  .content("These actions are generated from data")
  .addButtons(actions, (item) => ({
    label: item.label,
    tool: "accountManager",
    paramSchema: { userId: { type: "string" } },
    params: { action: item.actionName },
    variant: item.variant
  }))
  .build();

Aligning Actions

Use alignActions to control button layout:

const cardUI = new CardUIBuilder()
  .title("Actions Layout")
  .content("These buttons will be aligned horizontally")
  .alignActions("row")
  .addButton({
    label: "Action 1",
    tool: "sampleTool",
    paramSchema: {},
    params: {}
  })
  .addButton({
    label: "Action 2",
    tool: "sampleTool",
    paramSchema: {},
    params: {}
  })
  .build();

Examples

Note: Here are complete examples showcasing different card configurations.

Basic Card

const basicCard = new CardUIBuilder()
  .content("This is a simple card with just content.")
  .build();

return new DainResponse({
  text: "Basic Card",
  data: {},
  ui: basicCard
});

Card with Title, Variant, and Aligned Actions

const warningCard = new CardUIBuilder()
  .title("Subscription Warning")
  .content("Your subscription will expire soon.")
  .variant("destructive")
  .alignActions("row")
  .addButton({
    label: "Renew Now",
    tool: "subscriptionTool",
    paramSchema: { userId: { type: "string" } },
    params: { action: "renew" },
    variant: "primary"
  })
  .build();

return new DainResponse({
  text: "Warning Card",
  data: {},
  ui: warningCard
});

Card with Fields

const detailsCard = new CardUIBuilder()
  .title("Order Details")
  .content("Below are the details for your order.")
  .addFields([
    { label: "Order ID", value: "12345" },
    { label: "Status", value: "Shipped" }
  ])
  .build();

return new DainResponse({
  text: "Order Details Card",
  data: {},
  ui: detailsCard
});

Card with Multiple Buttons

const multiButtonCard = new CardUIBuilder()
  .title("Manage Profile")
  .content("Pick an action to perform on your profile.")
  .addButtons([
    {
      label: "Update Info",
      tool: "profileTool",
      paramSchema: { userId: { type: "string" } },
      params: { action: "update" },
      variant: "secondary"
    },
    {
      label: "Delete Profile",
      tool: "profileTool",
      paramSchema: { userId: { type: "string" } },
      params: { action: "delete" },
      variant: "destructive"
    }
  ])
  .build();

return new DainResponse({
  text: "Multi-Button Card",
  data: {},
  ui: multiButtonCard
});

Retro-Compatible Card with onConfirm

const retroCard = new CardUIBuilder()
  .title("Delete Account")
  .content("Are you sure you want to delete your account?")
  .onConfirm({
    tool: "accountManager",
    paramSchema: {
      userId: { type: "string" },
      reason: { type: "string" }
    },
    params: {
      action: "delete",
      timestamp: Date.now()
    }
  })
  .build();

return new DainResponse({
  text: "Retro-Confirm Card",
  data: {},
  ui: retroCard
});