Checkpoint
Marks a point in the conversation with a restore action.
import { Checkpoint, CheckpointIcon, CheckpointTrigger,} from "@/components/ai/checkpoint";import { IconPlaceholder } from "@/edmi/icon-placeholder";
export default function Demo() { return ( <Checkpoint className="w-full max-w-xl" time="14:02"> <CheckpointIcon /> <CheckpointTrigger tooltip="Restore the conversation and the agent's changes to this point"> <IconPlaceholder lucide="RotateCcwIcon" tabler="IconRotate" hugeicons="Undo02Icon" phosphor="ArrowCounterClockwiseIcon" remixicon="RiResetLeftLine" /> Restore checkpoint </CheckpointTrigger> </Checkpoint> );}<script setup lang="ts">import { Checkpoint, CheckpointIcon, CheckpointTrigger } from "@/components/ai/checkpoint";import { RotateCwIcon } from "@lucide/vue";</script>
<template> <Checkpoint class="w-full max-w-xl" time="14:02"> <CheckpointIcon /> <CheckpointTrigger tooltip="Restore the conversation and the agent's changes to this point"> <RotateCwIcon class="-scale-x-100" /> Restore checkpoint </CheckpointTrigger> </Checkpoint></template><script lang="ts"> import { Checkpoint, CheckpointIcon, CheckpointTrigger } from "$lib/components/ai/checkpoint"; import IconPlaceholder from "$lib/components/icon-placeholder/icon-placeholder.svelte";</script>
<Checkpoint class="w-full max-w-xl" time="14:02"> <CheckpointIcon /> <CheckpointTrigger tooltip="Restore the conversation and the agent's changes to this point"> <IconPlaceholder lucide="RotateCcwIcon" tabler="IconRotate" hugeicons="Undo02Icon" phosphor="ArrowCounterClockwiseIcon" remixicon="RiResetLeftLine" /> Restore checkpoint </CheckpointTrigger></Checkpoint>Built on button, separator and tooltip.
Installation
Section titled “Installation”npx shadcn@latest add @edmi-ui/ai-checkpointpnpm dlx shadcn@latest add @edmi-ui/ai-checkpointyarn dlx shadcn@latest add @edmi-ui/ai-checkpointbunx shadcn@latest add @edmi-ui/ai-checkpointPick 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:
button,separator,tooltip.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-checkpointpnpm dlx shadcn-vue@latest add @edmi-ui/ai-checkpointyarn dlx shadcn-vue@latest add @edmi-ui/ai-checkpointbunx shadcn-vue@latest add @edmi-ui/ai-checkpointPick 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/vueTerminal window pnpm add @lucide/vueTerminal window yarn add @lucide/vueTerminal window bun add @lucide/vueAdd the Edmi components this one builds on:
button,separator,tooltip.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-checkpoint.jsonpnpm dlx shadcn-svelte@latest add https://viandwi24.github.io/edmi-ui/r/svelte/ai-checkpoint.jsonyarn dlx shadcn-svelte@latest add https://viandwi24.github.io/edmi-ui/r/svelte/ai-checkpoint.jsonbunx --bun shadcn-svelte@latest add https://viandwi24.github.io/edmi-ui/r/svelte/ai-checkpoint.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:
button,separator,tooltip.Copy the code from Component source into your project and update the import paths to match your aliases.
import { Checkpoint, CheckpointIcon, CheckpointTrigger,} from "@/components/ai/checkpoint";
<Checkpoint className="w-full max-w-xl" time="14:02"> <CheckpointIcon /> <CheckpointTrigger tooltip="Restore the conversation and the agent's changes to this point"> <ArrowCounterClockwiseIcon /> Restore checkpoint </CheckpointTrigger></Checkpoint><script setup lang="ts">import { Checkpoint, CheckpointIcon, CheckpointTrigger,} from "@/components/ai/checkpoint";</script>
<template> <Checkpoint class="w-full max-w-xl" time="14:02"> <CheckpointIcon /> <CheckpointTrigger tooltip="Restore the conversation and the agent's changes to this point"> <RotateCwIcon class="-scale-x-100" /> Restore checkpoint </CheckpointTrigger> </Checkpoint></template><script lang="ts"> import { Checkpoint, CheckpointIcon, CheckpointTrigger, } from "$lib/components/ai/checkpoint";</script>
<Checkpoint class="w-full max-w-xl" time="14:02"> <CheckpointIcon /> <CheckpointTrigger tooltip="Restore the conversation and the agent's changes to this point"> <ArrowCounterClockwiseIcon /> Restore checkpoint </CheckpointTrigger></Checkpoint>Component source
Section titled “Component source”"use client";
import { cn } from "cn";import type { ComponentProps, HTMLAttributes, ReactNode } from "react";import { BookmarkIcon } from "@phosphor-icons/react";import { Button } from "@/registry/edmi/ui/button";import { Separator } from "@/registry/edmi/ui/separator";import { Tooltip, TooltipContent, TooltipTrigger,} from "@/registry/edmi/ui/tooltip";
export type CheckpointProps = HTMLAttributes<HTMLDivElement> & { /** ✦ Mono timestamp (or any label) after the line. */ time?: ReactNode;};
export const Checkpoint = ({ className, children, time, ...props}: CheckpointProps) => ( <div data-slot="ai-checkpoint" className={cn( "flex items-center gap-2.5 overflow-hidden text-muted-foreground", className, )} {...props} > {children} <Separator className="flex-1" /> {time && ( <span className="shrink-0 font-mono text-[11.5px] text-muted-foreground"> {time} </span> )} </div>);
export type CheckpointIconProps = ComponentProps<"span">;
export const CheckpointIcon = ({ className, children, ...props}: CheckpointIconProps) => ( <span data-slot="ai-checkpoint-icon" className={cn("inline-flex shrink-0 [&_svg]:size-4", className)} {...props} > {children ?? ( <BookmarkIcon /> )} </span>);
export type CheckpointTriggerProps = ComponentProps<typeof Button> & { tooltip?: string;};
export const CheckpointTrigger = ({ children, variant = "ghost", size = "xs", tooltip, ...props}: CheckpointTriggerProps) => tooltip ? ( <Tooltip> <TooltipTrigger render={ <Button data-slot="ai-checkpoint-trigger" size={size} type="button" variant={variant} {...props} /> } > {children} </TooltipTrigger> <TooltipContent align="start" side="bottom"> {tooltip} </TooltipContent> </Tooltip> ) : ( <Button data-slot="ai-checkpoint-trigger" size={size} type="button" variant={variant} {...props} > {children} </Button> );<script setup lang="ts">import type { HTMLAttributes } from "vue"import { cn } from "@/registry/edmi/lib/utils"import { Separator } from "@/registry/edmi/ui/separator"
const props = defineProps<{ class?: HTMLAttributes["class"] /** ✦ Mono timestamp (or any label) after the line; the `time` slot works too. */ time?: string}>()</script>
<template> <div data-slot="ai-checkpoint" :class="cn('flex items-center gap-2.5 overflow-hidden text-muted-foreground', props.class)" > <slot /> <Separator class="flex-1" /> <span v-if="time || $slots.time" class="shrink-0 font-mono text-[11.5px] text-muted-foreground"> <slot name="time">{{ time }}</slot> </span> </div></template><script setup lang="ts">import type { HTMLAttributes } from "vue"import { BookmarkIcon } from "@lucide/vue"import { cn } from "@/registry/edmi/lib/utils"
const props = defineProps<{ class?: HTMLAttributes["class"]}>()</script>
<template> <span data-slot="ai-checkpoint-icon" :class="cn('inline-flex shrink-0 [&_svg]:size-4', props.class)"> <slot><BookmarkIcon /></slot> </span></template><script setup lang="ts">import type { HTMLAttributes } from "vue"import type { ButtonVariants } from "@/registry/edmi/ui/button"import { Button } from "@/registry/edmi/ui/button"import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/registry/edmi/ui/tooltip"
const props = withDefaults(defineProps<{ tooltip?: string variant?: ButtonVariants["variant"] size?: ButtonVariants["size"] class?: HTMLAttributes["class"]}>(), { variant: "ghost", size: "xs",})</script>
<template> <TooltipProvider v-if="props.tooltip"> <Tooltip> <TooltipTrigger as-child> <Button data-slot="ai-checkpoint-trigger" type="button" :variant="props.variant" :size="props.size" :class="props.class"> <slot /> </Button> </TooltipTrigger> <TooltipContent align="start" side="bottom"> <p>{{ props.tooltip }}</p> </TooltipContent> </Tooltip> </TooltipProvider> <Button v-else data-slot="ai-checkpoint-trigger" type="button" :variant="props.variant" :size="props.size" :class="props.class"> <slot /> </Button></template>export { default as Checkpoint } from "./Checkpoint.vue";export { default as CheckpointIcon } from "./CheckpointIcon.vue";export { default as CheckpointTrigger } from "./CheckpointTrigger.vue";<script lang="ts"> import BookmarkIcon from "phosphor-svelte/lib/Bookmark"; import { cn } from "$lib/utils.js"; import type { HTMLAttributes } from "svelte/elements";
let { class: className, children, ...restProps }: HTMLAttributes<HTMLSpanElement> = $props();</script>
<span data-slot="ai-checkpoint-icon" class={cn("inline-flex shrink-0 [&_svg]:size-4", className)} {...restProps}> {#if children} {@render children()} {:else} <BookmarkIcon /> {/if}</span><script lang="ts"> import { Button, type ButtonProps } from "$lib/registry/ui/button/index.js"; import * as Tooltip from "$lib/registry/ui/tooltip/index.js";
let { tooltip, variant = "ghost", size = "xs", children, ...restProps }: Omit<ButtonProps, "href"> & { tooltip?: string } = $props();</script>
{#if tooltip} <Tooltip.Provider> <Tooltip.Root> <Tooltip.Trigger> {#snippet child({ props })} <Button {...props} data-slot="ai-checkpoint-trigger" type="button" {variant} {size} {...restProps}> {@render children?.()} </Button> {/snippet} </Tooltip.Trigger> <Tooltip.Content align="start" side="bottom"> <p>{tooltip}</p> </Tooltip.Content> </Tooltip.Root> </Tooltip.Provider>{:else} <Button data-slot="ai-checkpoint-trigger" type="button" {variant} {size} {...restProps}> {@render children?.()} </Button>{/if}<script lang="ts"> import { cn } from "$lib/utils.js"; import { Separator } from "$lib/registry/ui/separator/index.js"; import type { Snippet } from "svelte"; import type { HTMLAttributes } from "svelte/elements";
let { class: className, time, children, ...restProps }: Omit<HTMLAttributes<HTMLDivElement>, "children"> & { /** ✦ Mono timestamp (or any label) after the line. */ time?: string | Snippet; children?: Snippet; } = $props();</script>
<div data-slot="ai-checkpoint" class={cn("flex items-center gap-2.5 overflow-hidden text-muted-foreground", className)} {...restProps}> {@render children?.()} <Separator class="flex-1" /> {#if time} <span class="shrink-0 font-mono text-[11.5px] text-muted-foreground"> {#if typeof time === "string"}{time}{:else}{@render time()}{/if} </span> {/if}</div>import Root from "./checkpoint.svelte";import Icon from "./checkpoint-icon.svelte";import Trigger from "./checkpoint-trigger.svelte";
export { Icon, Icon as CheckpointIcon, Root, // Root as Checkpoint, Trigger, Trigger as CheckpointTrigger,};| Part | Prop | Type | Default |
|---|---|---|---|
| Checkpoint | time |
ReactNode (mono label after the line) |
|
| CheckpointIcon | children |
custom icon | bookmark |
| CheckpointTrigger | tooltip, variant, size |
Button props | ghost, xs |
✦ Edmi additions
Section titled “✦ Edmi additions”time✦ puts a mono timestamp after the line.- The trigger defaults to a ghost
xsbutton with a tooltip.