Agent
Agent configuration card: model, instructions, tools and output schema.
import { Agent, AgentContent, AgentHeader, AgentInstructions, AgentOutput, AgentTool, AgentTools,} from "@/components/ai/agent";
const getPrices = { description: "Quotes for tokens", inputSchema: "{ symbols: string[] }",};const rebalance = { description: "Send one rebalance tx", inputSchema: `{ "index": string, "maxSlippage"?: number }`,};const postFeed = { description: "Write a feed post", inputSchema: "{ text: string }",};
export default function Demo() { return ( <Agent className="max-w-lg"> <AgentHeader name="Keeper agent" model="claude-opus" /> <AgentContent> <AgentInstructions> Keep every index within its drift limit. Never trade without approval when the order is above <b>$500</b>. </AgentInstructions> <AgentTools defaultValue={["rebalance"]}> <AgentTool name="get_prices" tool={getPrices} value="get_prices" /> <AgentTool name="rebalance" tool={rebalance} value="rebalance" /> <AgentTool name="post_feed" tool={postFeed} value="post_feed" /> </AgentTools> <AgentOutput schema={`{ status: "ok" | "skipped"; tx?: string }`} /> </AgentContent> </Agent> );}<script setup lang="ts">import { Agent, AgentContent, AgentHeader, AgentInstructions, AgentOutput, AgentTool, AgentTools,} from "@/components/ai/agent";
const getPrices = { description: "Quotes for tokens", inputSchema: "{ symbols: string[] }",};const rebalance = { description: "Send one rebalance tx", inputSchema: `{ "index": string, "maxSlippage"?: number }`,};const postFeed = { description: "Write a feed post", inputSchema: "{ text: string }",};
const outputSchema = `{ status: "ok" | "skipped"; tx?: string }`;</script>
<template> <Agent class="max-w-lg"> <AgentHeader name="Keeper agent" model="claude-opus" /> <AgentContent> <AgentInstructions> Keep every index within its drift limit. Never trade without approval when the order is above <b>$500</b>. </AgentInstructions> <AgentTools default-value="rebalance"> <AgentTool name="get_prices" :tool="getPrices" value="get_prices" /> <AgentTool name="rebalance" :tool="rebalance" value="rebalance" /> <AgentTool name="post_feed" :tool="postFeed" value="post_feed" /> </AgentTools> <AgentOutput :schema="outputSchema" /> </AgentContent> </Agent></template><script lang="ts"> import { Agent, AgentContent, AgentHeader, AgentInstructions, AgentOutput, AgentTool, AgentTools, } from "$lib/components/ai/agent";
const getPrices = { description: "Quotes for tokens", inputSchema: "{ symbols: string[] }" }; const rebalance = { description: "Send one rebalance tx", inputSchema: `{ "index": string, "maxSlippage"?: number }`, }; const postFeed = { description: "Write a feed post", inputSchema: "{ text: string }" }; const outputSchema = `{ status: "ok" | "skipped"; tx?: string }`;</script>
<Agent class="max-w-lg"> <AgentHeader name="Keeper agent" model="claude-opus" /> <AgentContent> <AgentInstructions> Keep every index within its drift limit. Never trade without approval when the order is above <b >$500</b >. </AgentInstructions> <AgentTools value="rebalance"> <AgentTool name="get_prices" tool={getPrices} value="get_prices" /> <AgentTool name="rebalance" tool={rebalance} value="rebalance" /> <AgentTool name="post_feed" tool={postFeed} value="post_feed" /> </AgentTools> <AgentOutput schema={outputSchema} /> </AgentContent></Agent>Built on card, accordion, badge and ai-code-block.
Installation
Section titled “Installation”npx shadcn@latest add @edmi-ui/ai-agentpnpm dlx shadcn@latest add @edmi-ui/ai-agentyarn dlx shadcn@latest add @edmi-ui/ai-agentbunx shadcn@latest add @edmi-ui/ai-agentPick your package manager above; the choice is remembered. Add the @edmi-ui registry first, see Getting started.
Add the Edmi components this one builds on:
accordion,badge,card,ai-code-block,elevation.Copy the code from Component source into your project and update the import paths to match your aliases.
npx shadcn-vue@latest add @edmi-ui/ai-agentpnpm dlx shadcn-vue@latest add @edmi-ui/ai-agentyarn dlx shadcn-vue@latest add @edmi-ui/ai-agentbunx shadcn-vue@latest add @edmi-ui/ai-agentPick your package manager above; the choice is remembered. Add the @edmi-ui registry first, see Getting started.
Install the dependencies:
Terminal window npm install @lucide/vue @vueuse/core reka-uiTerminal window pnpm add @lucide/vue @vueuse/core reka-uiTerminal window yarn add @lucide/vue @vueuse/core reka-uiTerminal window bun add @lucide/vue @vueuse/core reka-uiAdd the Edmi components this one builds on:
accordion,badge,card,ai-code-block,elevation.Copy the code from Component source into your project and update the import paths to match your aliases.
npx shadcn-svelte@latest add https://viandwi24.github.io/edmi-ui/r/svelte/ai-agent.jsonpnpm dlx shadcn-svelte@latest add https://viandwi24.github.io/edmi-ui/r/svelte/ai-agent.jsonyarn dlx shadcn-svelte@latest add https://viandwi24.github.io/edmi-ui/r/svelte/ai-agent.jsonbunx --bun shadcn-svelte@latest add https://viandwi24.github.io/edmi-ui/r/svelte/ai-agent.jsonPick your package manager above; the choice is remembered. Add the @edmi-ui registry first, see Getting started.
Add the Edmi components this one builds on:
accordion,badge,card,ai-code-block,elevation.Copy the code from Component source into your project and update the import paths to match your aliases.
import { Agent, AgentContent, AgentHeader, AgentInstructions, AgentOutput, AgentTool, AgentTools,} from "@/components/ai/agent";
<Agent className="max-w-lg"> <AgentHeader name="Keeper agent" model="claude-opus" /> <AgentContent> <AgentInstructions> Keep every index within its drift limit. Never trade without approval when the order is above <b>$500</b>. </AgentInstructions> <AgentTools> <AgentTool name="get_prices" value="get_prices" /> <AgentTool name="rebalance" value="rebalance" /> <AgentTool name="post_feed" value="post_feed" /> </AgentTools> <AgentOutput schema={`{ status: "ok" | "skipped"; tx?: string }`} /> </AgentContent></Agent><script setup lang="ts">import { Agent, AgentContent, AgentHeader, AgentInstructions, AgentOutput, AgentTool, AgentTools,} from "@/components/ai/agent";</script>
<template> <Agent class="max-w-lg"> <AgentHeader name="Keeper agent" model="claude-opus" /> <AgentContent> <AgentInstructions> Keep every index within its drift limit. Never trade without approval when the order is above <b>$500</b>. </AgentInstructions> <AgentTools default-value="rebalance"> <AgentTool name="get_prices" value="get_prices" /> <AgentTool name="rebalance" value="rebalance" /> <AgentTool name="post_feed" value="post_feed" /> </AgentTools> <AgentOutput /> </AgentContent> </Agent></template><script lang="ts"> import { Agent, AgentContent, AgentHeader, AgentInstructions, AgentOutput, AgentTool, AgentTools, } from "$lib/components/ai/agent";</script>
<Agent class="max-w-lg"> <AgentHeader name="Keeper agent" model="claude-opus" /> <AgentContent> <AgentInstructions> Keep every index within its drift limit. Never trade without approval when the order is above <b >$500</b >. </AgentInstructions> <AgentTools value="rebalance"> <AgentTool name="get_prices" value="get_prices" /> <AgentTool name="rebalance" value="rebalance" /> <AgentTool name="post_feed" value="post_feed" /> </AgentTools> <AgentOutput /> </AgentContent></Agent>Component source
Section titled “Component source”"use client";
import { cn } from "cn";import type { ComponentProps, ReactNode } from "react";import { Children, memo } from "react";import { SparkleIcon, GearIcon } from "@phosphor-icons/react";import { CodeBlock } from "@/registry/edmi/components/ai/code-block";import { Accordion, AccordionContent, AccordionItem, AccordionTrigger,} from "@/registry/edmi/ui/accordion";import { Badge } from "@/registry/edmi/ui/badge";import { Card } from "@/registry/edmi/ui/card";
// Section label (DESIGN §5b `.ai-lbl`): mono 11px caps.const LABEL = "font-mono text-[11px] font-medium tracking-[0.8px] text-muted-foreground uppercase";
export type AgentProps = ComponentProps<typeof Card>;
/** An agent's configuration at a glance. Built on the ui card; `elevation` ✦ is forwarded to the card. */export const Agent = memo(({ className, ...props }: AgentProps) => ( <Card data-slot="ai-agent" className={cn("not-prose w-full gap-0 py-0", className)} {...props} />));
export type AgentHeaderProps = ComponentProps<"div"> & { name: string; model?: string;};
export const AgentHeader = memo( ({ className, name, model, ...props }: AgentHeaderProps) => ( <div data-slot="ai-agent-header" className={cn( "flex w-full items-center gap-2.5 border-b border-border px-4 py-3.5", className, )} {...props} > <span className="inline-flex size-[30px] shrink-0 items-center justify-center rounded-full bg-brand-soft text-brand-text"> <SparkleIcon className="size-4" /> </span> <span className="text-sm font-semibold">{name}</span> {model && ( <Badge className="h-5 font-mono text-[11px]" variant="secondary"> {model} </Badge> )} </div> ),);
export type AgentContentProps = ComponentProps<"div">;
export const AgentContent = memo( ({ className, ...props }: AgentContentProps) => ( <div className={cn("flex flex-col gap-4 p-4", className)} {...props} /> ),);
export type AgentInstructionsProps = Omit<ComponentProps<"div">, "children"> & { children: ReactNode;};
export const AgentInstructions = memo( ({ className, children, ...props }: AgentInstructionsProps) => ( <div className={cn("flex flex-col gap-1.5", className)} {...props}> <span className={LABEL}>Instructions</span> <div className="text-[13.5px] leading-[1.6] text-foreground-2"> {children} </div> </div> ),);
export type AgentToolsProps = ComponentProps<typeof Accordion>;
export const AgentTools = memo( ({ className, children, ...props }: AgentToolsProps) => ( <div className="flex flex-col gap-1"> <span className={LABEL}>Tools · {Children.count(children)}</span> <Accordion className={cn("border-t border-border-2", className)} {...props} > {children} </Accordion> </div> ),);
export type AgentToolProps = ComponentProps<typeof AccordionItem> & { tool: { // Matches the AI SDK `Tool` shape (`description`, `inputSchema`) without requiring it; // a string schema is shown as typescript, anything else as JSON. description?: string; inputSchema?: unknown; jsonSchema?: unknown; }; /** ✦ Tool name shown in mono before the description; falls back to `value`. */ name?: string;};
export const AgentTool = memo( ({ className, tool, value, name, ...props }: AgentToolProps) => { const schema = "jsonSchema" in tool && tool.jsonSchema ? tool.jsonSchema : tool.inputSchema;
return ( <AccordionItem className={cn("border-t-0 border-b border-border-2", className)} value={value} {...props} > <AccordionTrigger className="gap-2 py-[9px] text-[13px] font-normal"> <GearIcon className="size-3.5 shrink-0 text-muted-foreground" /> <span className="font-mono text-[12.5px]"> {name ?? String(value ?? "")} </span> <span className="min-w-0 flex-1 truncate text-[13px] font-normal text-muted-foreground"> {tool.description ?? "No description"} </span> </AccordionTrigger> <AccordionContent className="pb-2.5"> <CodeBlock className="rounded-lg border-0 bg-muted" code={ typeof schema === "string" ? schema : JSON.stringify(schema, null, 2) } language={typeof schema === "string" ? "typescript" : "json"} /> </AccordionContent> </AccordionItem> ); },);
export type AgentOutputProps = ComponentProps<"div"> & { schema: string;};
export const AgentOutput = memo( ({ className, schema, ...props }: AgentOutputProps) => ( <div className={cn("flex flex-col gap-1.5", className)} {...props}> <span className={LABEL}>Output schema</span> <CodeBlock className="rounded-lg border-0 bg-muted" code={schema} language="typescript" /> </div> ),);
Agent.displayName = "Agent";AgentHeader.displayName = "AgentHeader";AgentContent.displayName = "AgentContent";AgentInstructions.displayName = "AgentInstructions";AgentTools.displayName = "AgentTools";AgentTool.displayName = "AgentTool";AgentOutput.displayName = "AgentOutput";<script setup lang="ts">import type { HTMLAttributes } from "vue"import { cn } from "@/registry/edmi/lib/utils"import { Card } from "@/registry/edmi/ui/card"import type { Elevation } from "@/registry/edmi/ui/elevation"
// An agent's configuration at a glance. Built on the ui card; `elevation` ✦ is forwarded to the card.const props = withDefaults(defineProps<{ class?: HTMLAttributes["class"] /** ✦ depth: sunken -1, flat 0, raised +1, floating +2. */ elevation?: Elevation}>(), { elevation: undefined,})</script>
<template> <Card data-slot="ai-agent" :elevation="props.elevation" :class="cn('not-prose w-full gap-0 py-0', props.class)" > <slot /> </Card></template><script setup lang="ts">import type { HTMLAttributes } from "vue"import { cn } from "@/registry/edmi/lib/utils"
const props = defineProps<{ class?: HTMLAttributes["class"]}>()</script>
<template> <div :class="cn('flex flex-col gap-4 p-4', props.class)"> <slot /> </div></template><script setup lang="ts">import type { HTMLAttributes } from "vue"import { SparklesIcon } from "@lucide/vue"import { cn } from "@/registry/edmi/lib/utils"import { Badge } from "@/registry/edmi/ui/badge"
const props = defineProps<{ name: string model?: string class?: HTMLAttributes["class"]}>()</script>
<template> <div data-slot="ai-agent-header" :class="cn('flex w-full items-center gap-2.5 border-b border-border px-4 py-3.5', props.class)" > <span class="inline-flex size-[30px] shrink-0 items-center justify-center rounded-full bg-brand-soft text-brand-text"> <SparklesIcon class="size-4" /> </span> <span class="text-sm font-semibold">{{ props.name }}</span> <Badge v-if="props.model" class="h-5 font-mono text-[11px]" variant="secondary"> {{ props.model }} </Badge> </div></template><script setup lang="ts">import type { HTMLAttributes } from "vue"import { cn } from "@/registry/edmi/lib/utils"
const props = defineProps<{ class?: HTMLAttributes["class"]}>()</script>
<template> <div :class="cn('flex flex-col gap-1.5', props.class)"> <span class="font-mono text-[11px] font-medium tracking-[0.8px] text-muted-foreground uppercase">Instructions</span> <div class="text-[13.5px] leading-[1.6] text-foreground-2"> <slot /> </div> </div></template><script setup lang="ts">import type { HTMLAttributes } from "vue"import { cn } from "@/registry/edmi/lib/utils"import { CodeBlock } from "@/registry/edmi/components/ai/code-block"
const props = defineProps<{ schema: string class?: HTMLAttributes["class"]}>()</script>
<template> <div :class="cn('flex flex-col gap-1.5', props.class)"> <span class="font-mono text-[11px] font-medium tracking-[0.8px] text-muted-foreground uppercase">Output schema</span> <CodeBlock class="rounded-lg border-0 bg-muted" :code="props.schema" language="typescript" /> </div></template><script setup lang="ts">import type { HTMLAttributes } from "vue"import { Settings } from "@lucide/vue"import { computed } from "vue"import { cn } from "@/registry/edmi/lib/utils"import { AccordionContent, AccordionItem, AccordionTrigger } from "@/registry/edmi/ui/accordion"import { CodeBlock } from "@/registry/edmi/components/ai/code-block"
const props = defineProps<{ /** Matches the AI SDK `Tool` shape (`description`, `inputSchema`); a string schema is shown as typescript, anything else as JSON. */ tool: { description?: string, inputSchema?: unknown, jsonSchema?: unknown } value: string /** ✦ Tool name shown in mono before the description; falls back to `value`. */ name?: string class?: HTMLAttributes["class"]}>()
const schema = computed(() => "jsonSchema" in props.tool && props.tool.jsonSchema ? props.tool.jsonSchema : props.tool.inputSchema,)const isString = computed(() => typeof schema.value === "string")const code = computed(() => (isString.value ? (schema.value as string) : JSON.stringify(schema.value, null, 2)))</script>
<template> <AccordionItem :value="props.value" :class="cn('border-t-0 border-b border-border-2', props.class)" > <AccordionTrigger class="gap-2 py-[9px] text-[13px] font-normal"> <Settings class="size-3.5 shrink-0 text-muted-foreground" /> <span class="font-mono text-[12.5px]">{{ props.name ?? props.value }}</span> <span class="min-w-0 flex-1 truncate text-[13px] font-normal text-muted-foreground"> {{ props.tool.description ?? "No description" }} </span> </AccordionTrigger> <AccordionContent class="pb-2.5"> <CodeBlock class="rounded-lg border-0 bg-muted" :code="code" :language="isString ? 'typescript' : 'json'" /> </AccordionContent> </AccordionItem></template><script setup lang="ts">import type { AccordionRootEmits, AccordionRootProps } from "reka-ui"import type { HTMLAttributes, VNode } from "vue"import { reactiveOmit } from "@vueuse/core"import { useForwardPropsEmits } from "reka-ui"import { Comment, Fragment, useSlots } from "vue"import { cn } from "@/registry/edmi/lib/utils"import { Accordion } from "@/registry/edmi/ui/accordion"
// Reka needs `type`; single + collapsible matches the React port (Base UI default).const props = withDefaults( defineProps<AccordionRootProps & { class?: HTMLAttributes["class"] }>(), { type: "single", collapsible: true },)const emits = defineEmits<AccordionRootEmits>()
const delegatedProps = reactiveOmit(props, "class")const forwarded = useForwardPropsEmits(delegatedProps, emits)
const slots = useSlots()
// Number of AgentTool children (looks through v-for fragments); called from the template, never at setup.function toolCount() { const walk = (nodes: VNode[]): number => nodes.reduce((n, node) => { if (node.type === Fragment) { return n + walk(Array.isArray(node.children) ? (node.children as VNode[]) : []) } return typeof node.type === "symbol" && (node.type as symbol) === (Comment as unknown as symbol) ? n : n + 1 }, 0) return walk(slots.default?.() ?? [])}</script>
<template> <div class="flex flex-col gap-1"> <span class="font-mono text-[11px] font-medium tracking-[0.8px] text-muted-foreground uppercase">Tools · {{ toolCount() }}</span> <Accordion v-bind="forwarded" :class="cn('border-t border-border-2', props.class)"> <slot /> </Accordion> </div></template>export { default as Agent } from "./Agent.vue";export { default as AgentContent } from "./AgentContent.vue";export { default as AgentHeader } from "./AgentHeader.vue";export { default as AgentInstructions } from "./AgentInstructions.vue";export { default as AgentOutput } from "./AgentOutput.vue";export { default as AgentTool } from "./AgentTool.vue";export { default as AgentTools } from "./AgentTools.vue";<script lang="ts"> import { cn } from "$lib/utils.js"; import type { Snippet } from "svelte"; import type { HTMLAttributes } from "svelte/elements";
let { class: className, children, ...restProps }: HTMLAttributes<HTMLDivElement> & { children?: Snippet } = $props();</script>
<div class={cn("flex flex-col gap-4 p-4", className)} {...restProps}> {@render children?.()}</div><script lang="ts"> import SparkleIcon from "phosphor-svelte/lib/Sparkle"; import { Badge } from "$lib/registry/ui/badge/index.js"; import { cn } from "$lib/utils.js"; import type { HTMLAttributes } from "svelte/elements";
let { name, model, class: className, ...restProps }: HTMLAttributes<HTMLDivElement> & { name: string; model?: string } = $props();</script>
<div data-slot="ai-agent-header" class={cn("flex w-full items-center gap-2.5 border-b border-border px-4 py-3.5", className)} {...restProps}> <span class="inline-flex size-[30px] shrink-0 items-center justify-center rounded-full bg-brand-soft text-brand-text" > <SparkleIcon class="size-4" /> </span> <span class="text-sm font-semibold">{name}</span> {#if model} <Badge class="h-5 font-mono text-[11px]" variant="secondary">{model}</Badge> {/if}</div><script lang="ts"> import { cn } from "$lib/utils.js"; import type { Snippet } from "svelte"; import type { HTMLAttributes } from "svelte/elements";
let { class: className, children, ...restProps }: HTMLAttributes<HTMLDivElement> & { children?: Snippet } = $props();</script>
<div class={cn("flex flex-col gap-1.5", className)} {...restProps}> <span class="font-mono text-[11px] font-medium tracking-[0.8px] text-muted-foreground uppercase" >Instructions</span > <div class="text-[13.5px] leading-[1.6] text-foreground-2"> {@render children?.()} </div></div><script lang="ts"> import { cn } from "$lib/utils.js"; import type { HTMLAttributes } from "svelte/elements"; import { CodeBlock } from "../code-block/index.js";
let { schema, class: className, ...restProps }: HTMLAttributes<HTMLDivElement> & { schema: string } = $props();</script>
<div class={cn("flex flex-col gap-1.5", className)} {...restProps}> <span class="font-mono text-[11px] font-medium tracking-[0.8px] text-muted-foreground uppercase" >Output schema</span > <CodeBlock class="rounded-lg border-0 bg-muted" code={schema} language="typescript" /></div><script lang="ts"> import GearIcon from "phosphor-svelte/lib/Gear"; import { AccordionContent, AccordionItem, AccordionTrigger, } from "$lib/registry/ui/accordion/index.js"; import { cn } from "$lib/utils.js"; import { CodeBlock } from "../code-block/index.js"; import { useAgentToolsContext } from "./use-agent.svelte.js";
let { tool, value, name, class: className, }: { /** Matches the AI SDK `Tool` shape (`description`, `inputSchema`); a string schema is shown as typescript, anything else as JSON. */ tool: { description?: string; inputSchema?: unknown; jsonSchema?: unknown }; value: string; /** ✦ Tool name shown in mono before the description; falls back to `value`. */ name?: string; class?: string; } = $props();
const tools = useAgentToolsContext(); $effect(() => tools?.register());
const schema = $derived( "jsonSchema" in tool && tool.jsonSchema ? tool.jsonSchema : tool.inputSchema ); const isString = $derived(typeof schema === "string"); const code = $derived(isString ? (schema as string) : JSON.stringify(schema, null, 2));</script>
<AccordionItem {value} class={cn("border-t-0 border-b border-border-2", className)}> <AccordionTrigger class="gap-2 py-[9px] text-[13px] font-normal"> <GearIcon class="size-3.5 shrink-0 text-muted-foreground" /> <span class="font-mono text-[12.5px]">{name ?? value}</span> <span class="min-w-0 flex-1 truncate text-[13px] font-normal text-muted-foreground"> {tool.description ?? "No description"} </span> </AccordionTrigger> <AccordionContent class="pb-2.5"> <CodeBlock class="rounded-lg border-0 bg-muted" {code} language={isString ? "typescript" : "json"} /> </AccordionContent></AccordionItem><script lang="ts"> import { Accordion } from "$lib/registry/ui/accordion/index.js"; import { cn } from "$lib/utils.js"; import { type ComponentProps, untrack } from "svelte"; import { setAgentToolsContext } from "./use-agent.svelte.js";
// single + collapsible matches the React port (Base UI default). let { class: className, children, value = $bindable<string | undefined>(), ...restProps }: Omit<ComponentProps<typeof Accordion>, "type" | "value"> & { value?: string } = $props();
let count = $state(0); setAgentToolsContext({ register() { // untrack: register runs inside the child's effect and must not subscribe to `count`. untrack(() => { count += 1; }); return () => { untrack(() => { count -= 1; }); }; }, });</script>
<div class="flex flex-col gap-1"> <span class="font-mono text-[11px] font-medium tracking-[0.8px] text-muted-foreground uppercase" >Tools · {count}</span > <Accordion type="single" bind:value class={cn("border-t border-border-2", className)} {...restProps as object} > {@render children?.()} </Accordion></div><script lang="ts"> import { Card } from "$lib/registry/ui/card/index.js"; import { cn } from "$lib/utils.js"; import type { ComponentProps } from "svelte";
// An agent's configuration at a glance. Built on the ui card; `elevation` ✦ is forwarded to the card (Card props). let { class: className, children, ...restProps }: ComponentProps<typeof Card> = $props();</script>
<Card data-slot="ai-agent" class={cn("not-prose w-full gap-0 py-0", className)} {...restProps}> {@render children?.()}</Card>import { getContext, setContext } from "svelte";
/** AgentTools counts its AgentTool children through this context ("Tools · 3"). */export interface AgentToolsContextValue { register: () => () => void;}
const KEY = Symbol("ai-agent-tools");
export function setAgentToolsContext(value: AgentToolsContextValue) { return setContext(KEY, value);}
export function useAgentToolsContext(): AgentToolsContextValue | undefined { return getContext<AgentToolsContextValue | undefined>(KEY);}import Root from "./agent.svelte";import Content from "./agent-content.svelte";import Header from "./agent-header.svelte";import Instructions from "./agent-instructions.svelte";import Output from "./agent-output.svelte";import Tool from "./agent-tool.svelte";import Tools from "./agent-tools.svelte";
export { Content as AgentContent, Header as AgentHeader, Instructions as AgentInstructions, Output as AgentOutput, Root as Agent, Tool as AgentTool, Tools as AgentTools,};Elevation ✦
Section titled “Elevation ✦”See the Elevation guide for the levels, roles and layered mode.
The agent card are flat by default and take the surface level in layered mode (raised). sunken (-1) is a soft inset, raised (+1) a bevel and floating (+2) a bevel with a soft drop.
Sunken (-1)
Flat (0)
Raised (+1)
Floating (+2)
Sunken (-1)
Flat (0)
Raised (+1)
Floating (+2)
Sunken (-1)
Flat (0)
Raised (+1)
Floating (+2)
import { Agent, AgentContent, AgentHeader, AgentInstructions,} from "@/components/ai/agent";
const levels = [ { value: "sunken", label: "Sunken (-1)" }, { value: "flat", label: "Flat (0)" }, { value: "raised", label: "Raised (+1)" }, { value: "floating", label: "Floating (+2)" },] as const;
export default function Demo() { return ( <div className="flex w-full max-w-2xl flex-col gap-5"> {levels.map(({ value, label }) => ( <div key={value} className="flex flex-col gap-2"> <p className="text-xs font-medium text-muted-foreground">{label}</p> <Agent elevation={value} className="max-w-lg"> <AgentHeader name="Keeper agent" model="claude-opus" /> <AgentContent> <AgentInstructions> Keep every index within its drift limit. Never trade without approval when the order is above <b>$500</b>. </AgentInstructions> </AgentContent> </Agent> </div> ))} </div> );}<script setup lang="ts">import { Agent, AgentContent, AgentHeader, AgentInstructions,} from "@/components/ai/agent";
const levels = [{ value: "sunken", label: "Sunken (-1)" }, { value: "flat", label: "Flat (0)" }, { value: "raised", label: "Raised (+1)" }, { value: "floating", label: "Floating (+2)" }];</script>
<template> <div class="flex w-full max-w-2xl flex-col gap-5"> <div v-for="level in levels" :key="level.value" class="flex flex-col gap-2"> <p class="text-xs font-medium text-muted-foreground">{{ level.label }}</p> <Agent :elevation="level.value" class="max-w-lg"> <AgentHeader name="Keeper agent" model="claude-opus" /> <AgentContent> <AgentInstructions> Keep every index within its drift limit. Never trade without approval when the order is above <b>$500</b>. </AgentInstructions> </AgentContent> </Agent> </div> </div></template><script lang="ts"> import { Agent, AgentContent, AgentHeader, AgentInstructions, } from "$lib/components/ai/agent";
const levels = [{ value: "sunken", label: "Sunken (-1)" }, { value: "flat", label: "Flat (0)" }, { value: "raised", label: "Raised (+1)" }, { value: "floating", label: "Floating (+2)" }] as const;</script>
<div class="flex w-full max-w-2xl flex-col gap-5"> {#each levels as level (level.value)} <div class="flex flex-col gap-2"> <p class="text-xs font-medium text-muted-foreground">{level.label}</p> <Agent elevation={level.value} class="max-w-lg"> <AgentHeader name="Keeper agent" model="claude-opus" /> <AgentContent> <AgentInstructions> Keep every index within its drift limit. Never trade without approval when the order is above <b>$500</b>. </AgentInstructions> </AgentContent> </Agent> </div> {/each}</div>| Part | Prop | Type | Default |
|---|---|---|---|
| Agent | elevation |
"auto" | "sunken" | "flat" | "raised" | "floating" |
"auto" |
| AgentHeader | name, model |
string, string |
|
| AgentInstructions | children |
ReactNode |
|
| AgentTools | Accordion props (multiple, defaultValue) |
||
| AgentTool | tool, value |
{ description?, inputSchema?, jsonSchema? }, string |
|
| AgentOutput | schema |
string |
✦ Edmi additions
Section titled “✦ Edmi additions”elevation: depth of the card (forwarded to the Card).AgentToolname: tool name shown in mono before the description (falls back tovalue).- A string
inputSchemais shown as TypeScript; anything else as JSON. - Vue and Svelte:
AgentToolstakes the Reka / Bits accordion props (typedefaults tosingle,collapsible), and the tool count in the label is derived from itsAgentToolchildren.