Marker
Free, copy-and-go Marker components built on the SevenUI Marker primitive.Read the primitive docs.
- Maya Chen joined #design‑reviews
- Jordan Park renamed the channel from #ui‑crit
- Maya Chen pinned “Q4 navigation audit”
- Sam Ortiz left the channel
"use client";
import {
PencilLineIcon,
PinIcon,
UserMinusIcon,
UserPlusIcon,
} from "lucide-react";
import {
Marker,
MarkerContent,
MarkerIcon,
} from "@/components/ui/marker";
const events = [
{
id: "joined",
icon: UserPlusIcon,
actor: "Maya Chen",
action: "joined #design\u2011reviews",
time: "9:12 AM",
dateTime: "09:12",
},
{
id: "renamed",
icon: PencilLineIcon,
actor: "Jordan Park",
action: "renamed the channel from #ui\u2011crit",
time: "9:20 AM",
dateTime: "09:20",
},
{
id: "pinned",
icon: PinIcon,
actor: "Maya Chen",
action: "pinned “Q4 navigation audit”",
time: "10:04 AM",
dateTime: "10:04",
},
{
id: "left",
icon: UserMinusIcon,
actor: "Sam Ortiz",
action: "left the channel",
time: "11:37 AM",
dateTime: "11:37",
},
];
export default function Marker01() {
return (
<ul
aria-label="Channel activity"
className="flex w-full max-w-sm flex-col gap-3"
>
{events.map((event) => (
<li key={event.id}>
<Marker>
<MarkerIcon>
<event.icon />
</MarkerIcon>
<MarkerContent className="flex-1">
<span className="font-medium text-foreground">
{event.actor}
</span>{" "}
{event.action}
</MarkerContent>
<time
dateTime={event.dateTime}
className="shrink-0 text-xs tabular-nums"
>
{event.time}
</time>
</Marker>
</li>
))}
</ul>
);
}
npx shadcn@latest add @sevenui/component/marker-01pnpm dlx shadcn@latest add @sevenui/component/marker-01yarn dlx shadcn@latest add @sevenui/component/marker-01bunx --bun shadcn@latest add @sevenui/component/marker-01- Priya Kickoff notes are in the shared folder.
- Leo Thanks — I'll draft the API contract tonight.
- Leo Contract draft is up for review.
- Priya Approved with two small comments.
- Leo Addressed both, merging after lunch.
"use client";
import { CalendarDaysIcon } from "lucide-react";
import {
Marker,
MarkerContent,
MarkerIcon,
} from "@/components/ui/marker";
const days = [
{
label: "Monday, September 22",
dateTime: "2026-09-22",
messages: [
{ author: "Priya", text: "Kickoff notes are in the shared folder." },
{ author: "Leo", text: "Thanks — I'll draft the API contract tonight." },
],
},
{
label: "Yesterday",
dateTime: "2026-09-24",
messages: [
{ author: "Leo", text: "Contract draft is up for review." },
],
},
{
label: "Today",
dateTime: "2026-09-25",
messages: [
{ author: "Priya", text: "Approved with two small comments." },
{ author: "Leo", text: "Addressed both, merging after lunch." },
],
},
];
export default function Marker02() {
return (
<div className="flex w-full max-w-sm flex-col gap-4">
{days.map((day, index) => (
<section
key={day.dateTime}
aria-label={day.label}
className="flex flex-col gap-3"
>
<Marker variant="separator" className="text-xs font-medium">
{index === 0 ? (
<MarkerIcon className="size-3.5">
<CalendarDaysIcon className="size-3.5" />
</MarkerIcon>
) : null}
<MarkerContent>
<time dateTime={day.dateTime}>{day.label}</time>
</MarkerContent>
</Marker>
<ul className="flex flex-col gap-2">
{day.messages.map((message) => (
<li
key={message.text}
className="text-sm leading-relaxed text-foreground"
>
<span className="font-medium">{message.author}</span>{" "}
<span className="text-muted-foreground">{message.text}</span>
</li>
))}
</ul>
</section>
))}
</div>
);
}
npx shadcn@latest add @sevenui/component/marker-02pnpm dlx shadcn@latest add @sevenui/component/marker-02yarn dlx shadcn@latest add @sevenui/component/marker-02bunx --bun shadcn@latest add @sevenui/component/marker-02- Nadia Staging deploy is green.
- Owen I'll start the smoke tests.
3 new since 2:14 PM
- Owen Checkout flow fails on Safari 17.
- Nadia Reproduced it — the payment iframe is blocked.
- Owen Rolling back the CSP change now.
"use client";
import * as React from "react";
import { CircleDotIcon } from "lucide-react";
import { Button } from "@/components/ui/button";
import {
Marker,
MarkerContent,
MarkerIcon,
} from "@/components/ui/marker";
const earlier = [
{ author: "Nadia", text: "Staging deploy is green." },
{ author: "Owen", text: "I'll start the smoke tests." },
];
const unread = [
{ author: "Owen", text: "Checkout flow fails on Safari 17." },
{ author: "Nadia", text: "Reproduced it — the payment iframe is blocked." },
{ author: "Owen", text: "Rolling back the CSP change now." },
];
export default function Marker03() {
const [hasUnread, setHasUnread] = React.useState(true);
return (
<div className="flex w-full max-w-sm flex-col gap-3">
<ul className="flex flex-col gap-2">
{earlier.map((message) => (
<li key={message.text} className="text-sm text-muted-foreground">
<span className="font-medium text-foreground">
{message.author}
</span>{" "}
{message.text}
</li>
))}
</ul>
{hasUnread ? (
<Marker
variant="border"
role="status"
className="border-primary/40 text-primary"
>
<MarkerIcon>
<CircleDotIcon />
</MarkerIcon>
<MarkerContent className="flex-1 font-medium">
{unread.length} new since 2:14 PM
</MarkerContent>
<Button
variant="ghost"
size="xs"
className="-my-1 text-primary hover:text-primary"
onClick={() => setHasUnread(false)}
>
Mark as read
</Button>
</Marker>
) : null}
<ul className="flex flex-col gap-2">
{unread.map((message) => (
<li
key={message.text}
className="text-sm text-muted-foreground"
>
<span className="font-medium text-foreground">
{message.author}
</span>{" "}
{message.text}
</li>
))}
</ul>
</div>
);
}
npx shadcn@latest add @sevenui/component/marker-03pnpm dlx shadcn@latest add @sevenui/component/marker-03yarn dlx shadcn@latest add @sevenui/component/marker-03bunx --bun shadcn@latest add @sevenui/component/marker-03- Info: Build 1842 queued on runner eu‑west‑2
- Passed: 412 unit tests passed in 38s
- Warning: Bundle grew 18 KB — above the 10 KB budget
- Failed: E2E: checkout.spec.ts timed out after 30s
"use client";
import {
CircleAlertIcon,
CircleCheckIcon,
CircleXIcon,
InfoIcon,
} from "lucide-react";
import { cn } from "cn";
import {
Marker,
MarkerContent,
MarkerIcon,
} from "@/components/ui/marker";
const tones = {
info: { icon: InfoIcon, className: "text-muted-foreground", label: "Info" },
success: { icon: CircleCheckIcon, className: "text-success", label: "Passed" },
warning: {
icon: CircleAlertIcon,
className: "text-warning",
label: "Warning",
},
error: { icon: CircleXIcon, className: "text-destructive", label: "Failed" },
} as const;
const steps: { id: string; tone: keyof typeof tones; text: string }[] = [
{ id: "queued", tone: "info", text: "Build 1842 queued on runner eu\u2011west\u20112" },
{ id: "tests", tone: "success", text: "412 unit tests passed in 38s" },
{
id: "bundle",
tone: "warning",
text: "Bundle grew 18 KB — above the 10 KB budget",
},
{
id: "e2e",
tone: "error",
text: "E2E: checkout.spec.ts timed out after 30s",
},
];
export default function Marker04() {
return (
<ol
aria-label="Build 1842 log"
className="flex w-full max-w-sm flex-col gap-3"
>
{steps.map((step) => {
const tone = tones[step.tone];
return (
<li key={step.id}>
<Marker
data-tone={step.tone}
className="items-start data-[tone=error]:text-foreground"
>
<MarkerIcon className={cn("mt-0.5", tone.className)}>
<tone.icon />
</MarkerIcon>
<MarkerContent>
<span className="sr-only">{tone.label}: </span>
{step.text}
</MarkerContent>
</Marker>
</li>
);
})}
</ol>
);
}
npx shadcn@latest add @sevenui/component/marker-04pnpm dlx shadcn@latest add @sevenui/component/marker-04yarn dlx shadcn@latest add @sevenui/component/marker-04bunx --bun shadcn@latest add @sevenui/component/marker-04Iris Shipping the CSV export redesign on Thursday.
- Theo Can we keep the old export as a fallback?
- Iris Yes, behind a flag until the next release.
- Theo Works for me. I'll update the changelog.
"use client";
import * as React from "react";
import { flushSync } from "react-dom";
import { ArrowDownIcon, ChevronRightIcon, MessagesSquareIcon } from "lucide-react";
import {
Marker,
MarkerContent,
MarkerIcon,
} from "@/components/ui/marker";
const hiddenReplies = [
{ author: "Theo", text: "Can we keep the old export as a fallback?" },
{ author: "Iris", text: "Yes, behind a flag until the next release." },
{ author: "Theo", text: "Works for me. I'll update the changelog." },
];
const interactive =
"w-full cursor-pointer rounded-md px-2 py-1.5 no-underline outline-none transition-colors hover:bg-muted hover:text-foreground focus-visible:ring-3 focus-visible:ring-ring/50";
export default function Marker05() {
const [expanded, setExpanded] = React.useState(false);
const listId = React.useId();
const latestId = `${listId}-latest`;
const latestRef = React.useRef<HTMLLIElement>(null);
function jumpToLatest(event: React.MouseEvent) {
event.preventDefault();
// The latest reply lives in the collapsed list, so reveal it first.
flushSync(() => setExpanded(true));
latestRef.current?.scrollIntoView({ block: "nearest", behavior: "smooth" });
latestRef.current?.focus({ preventScroll: true });
}
return (
<div className="flex w-full max-w-sm flex-col gap-2">
<p className="px-2 text-sm">
<span className="font-medium">Iris</span>{" "}
<span className="text-muted-foreground">
Shipping the CSV export redesign on Thursday.
</span>
</p>
<Marker
render={<button type="button" />}
aria-expanded={expanded}
aria-controls={listId}
className={interactive}
onClick={() => setExpanded((value) => !value)}
>
<MarkerIcon>
<ChevronRightIcon
className={
expanded
? "rotate-90 transition-transform"
: "transition-transform"
}
/>
</MarkerIcon>
<MarkerContent>
{expanded
? "Hide replies"
: `Show ${hiddenReplies.length} earlier replies`}
</MarkerContent>
</Marker>
<ul id={listId} hidden={!expanded} className="flex flex-col gap-2 pr-2 pl-8">
{hiddenReplies.map((reply, index) => {
const latest = index === hiddenReplies.length - 1;
return (
<li
key={reply.text}
id={latest ? latestId : undefined}
ref={latest ? latestRef : undefined}
tabIndex={latest ? -1 : undefined}
className="-mx-2 rounded-md px-2 text-sm outline-none transition-colors focus:bg-muted"
>
<span className="font-medium">{reply.author}</span>{" "}
<span className="text-muted-foreground">{reply.text}</span>
</li>
);
})}
</ul>
<Marker render={<a href={`#${latestId}`} />}
className={interactive}
onClick={jumpToLatest}
>
<MarkerIcon>
<MessagesSquareIcon />
</MarkerIcon>
<MarkerContent className="flex-1">
Jump to latest reply from Theo
</MarkerContent>
<ArrowDownIcon aria-hidden="true" className="shrink-0" />
</Marker>
</div>
);
}
npx shadcn@latest add @sevenui/component/marker-05pnpm dlx shadcn@latest add @sevenui/component/marker-05yarn dlx shadcn@latest add @sevenui/component/marker-05bunx --bun shadcn@latest add @sevenui/component/marker-05src/billing/proration.ts
+7 additions-2 deletionstype Plan = { price: number; cycleStart: Date }; const differenceInDays = (a: Date, b: Date) => Math.round((a.getTime() - b.getTime()) / 86_400_000);export function prorate(plan: Plan, changedAt: Date) {Removed: const days = 30;Added: const days = daysInCycle(plan.cycleStart); const used = differenceInDays(changedAt, plan.cycleStart);Added: const remaining = Math.max(days - used, 0);Removed: return (plan.price / days) * (days - used);Added: return roundCents((plan.price / days) * remaining);} function roundCents(value: number) { return Math.round(value * 100) / 100;} Added: function daysInCycle(start: Date) {Added: const end = new Date(start.getFullYear(), start.getMonth() + 1, start.getDate());Added: return differenceInDays(end, start);Added: }"use client";
import * as React from "react";
import {
FileCodeIcon,
FoldVerticalIcon,
UnfoldVerticalIcon,
} from "lucide-react";
import { cn } from "cn";
import {
Marker,
MarkerContent,
MarkerIcon,
} from "@/components/ui/marker";
type Line = {
id: string;
number?: number;
kind: "context" | "add" | "remove";
code: string;
};
type Segment =
| { kind: "hidden"; id: string; lines: Line[] }
| { kind: "hunk"; id: string; lines: Line[] };
const segments: Segment[] = [
{
kind: "hidden",
id: "top",
lines: [
{
id: "1",
number: 1,
kind: "context",
code: "type Plan = { price: number; cycleStart: Date };",
},
{ id: "2", number: 2, kind: "context", code: "" },
{
id: "3",
number: 3,
kind: "context",
code: "const differenceInDays = (a: Date, b: Date) =>",
},
{
id: "4",
number: 4,
kind: "context",
code: " Math.round((a.getTime() - b.getTime()) / 86_400_000);",
},
],
},
{
kind: "hunk",
id: "prorate",
lines: [
{
id: "5",
number: 5,
kind: "context",
code: "export function prorate(plan: Plan, changedAt: Date) {",
},
{ id: "6-old", kind: "remove", code: " const days = 30;" },
{
id: "6",
number: 6,
kind: "add",
code: " const days = daysInCycle(plan.cycleStart);",
},
{
id: "7",
number: 7,
kind: "context",
code: " const used = differenceInDays(changedAt, plan.cycleStart);",
},
{
id: "8",
number: 8,
kind: "add",
code: " const remaining = Math.max(days - used, 0);",
},
{
id: "9-old",
kind: "remove",
code: " return (plan.price / days) * (days - used);",
},
{
id: "9",
number: 9,
kind: "add",
code: " return roundCents((plan.price / days) * remaining);",
},
{ id: "10", number: 10, kind: "context", code: "}" },
],
},
{
kind: "hidden",
id: "middle",
lines: [
{ id: "11", number: 11, kind: "context", code: "" },
{
id: "12",
number: 12,
kind: "context",
code: "function roundCents(value: number) {",
},
{
id: "13",
number: 13,
kind: "context",
code: " return Math.round(value * 100) / 100;",
},
{ id: "14", number: 14, kind: "context", code: "}" },
],
},
{
kind: "hunk",
id: "days-in-cycle",
lines: [
{ id: "15", number: 15, kind: "context", code: "" },
{
id: "16",
number: 16,
kind: "add",
code: "function daysInCycle(start: Date) {",
},
{
id: "17",
number: 17,
kind: "add",
code: " const end = new Date(start.getFullYear(), start.getMonth() + 1, start.getDate());",
},
{
id: "18",
number: 18,
kind: "add",
code: " return differenceInDays(end, start);",
},
{ id: "19", number: 19, kind: "add", code: "}" },
],
},
];
const signs = {
context: { symbol: " ", label: "" },
add: { symbol: "+", label: "Added: " },
remove: { symbol: "-", label: "Removed: " },
} as const;
function DiffLine({ line }: { line: Line }) {
const sign = signs[line.kind];
return (
<div
className={cn(
"flex px-3",
line.kind === "add" && "bg-success/10",
line.kind === "remove" && "bg-destructive/10",
)}
>
<span
aria-hidden="true"
className="w-7 shrink-0 pr-2 text-right text-muted-foreground tabular-nums select-none"
>
{line.number}
</span>
<span
aria-hidden="true"
className={cn(
"w-4 shrink-0 select-none",
line.kind === "add" && "text-success",
line.kind === "remove" && "text-destructive",
)}
>
{sign.symbol}
</span>
<code className="pr-3 whitespace-pre">
{sign.label ? <span className="sr-only">{sign.label}</span> : null}
{line.code || " "}
</code>
</div>
);
}
export default function Marker06() {
const [expanded, setExpanded] = React.useState<string[]>([]);
const baseId = React.useId();
function toggle(id: string) {
setExpanded((current) =>
current.includes(id)
? current.filter((item) => item !== id)
: [...current, id],
);
}
return (
<section
aria-labelledby={`${baseId}-file`}
className="w-full max-w-md overflow-hidden rounded-xl border border-border bg-card text-card-foreground"
>
<header className="flex items-center gap-2 border-b border-border px-3 py-2.5">
<FileCodeIcon
aria-hidden="true"
className="size-4 shrink-0 text-muted-foreground"
/>
<h3 id={`${baseId}-file`} className="min-w-0 truncate font-mono text-xs">
src/billing/proration.ts
</h3>
<span className="ml-auto flex shrink-0 gap-2 font-mono text-xs tabular-nums">
<span className="text-success">
+7<span className="sr-only"> additions</span>
</span>
<span className="text-destructive">
-2<span className="sr-only"> deletions</span>
</span>
</span>
</header>
<div className="@container overflow-x-auto py-1.5">
<div className="w-max min-w-full font-mono text-xs leading-5">
{segments.map((segment) => {
if (segment.kind === "hunk") {
return segment.lines.map((line) => (
<DiffLine key={line.id} line={line} />
));
}
const isOpen = expanded.includes(segment.id);
const panelId = `${baseId}-${segment.id}`;
const count = segment.lines.length;
return (
<React.Fragment key={segment.id}>
<Marker
variant="separator"
render={<button type="button" />}
aria-expanded={isOpen}
aria-controls={panelId}
onClick={() => toggle(segment.id)}
className="sticky left-0 my-1 w-[100cqw] cursor-pointer bg-muted/50 px-3 py-1 font-sans text-xs transition-colors outline-none hover:bg-muted hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-inset"
>
<MarkerIcon className="size-3.5">
{isOpen ? (
<FoldVerticalIcon className="size-3.5" />
) : (
<UnfoldVerticalIcon className="size-3.5" />
)}
</MarkerIcon>
<MarkerContent>
{isOpen ? "Hide" : "Expand"} {count} unchanged lines
</MarkerContent>
</Marker>
<div id={panelId} hidden={!isOpen}>
{segment.lines.map((line) => (
<DiffLine key={line.id} line={line} />
))}
</div>
</React.Fragment>
);
})}
</div>
</div>
</section>
);
}
npx shadcn@latest add @sevenui/component/marker-06pnpm dlx shadcn@latest add @sevenui/component/marker-06yarn dlx shadcn@latest add @sevenui/component/marker-06bunx --bun shadcn@latest add @sevenui/component/marker-06Notifications
Today
- 11:42
Lena commented on Q4 roadmap (unread)
“Can we move the SSO work before the pricing launch?”
- 10:05
Review requested on PR 1289 (unread)
Add rate limiting to the export endpoint
Yesterday
- 17:30
Invoice INV-2093 was paid (unread)
Northwind Traders · $2,400.00
Already read below this line - 14:12
Theo mentioned you in Design sync
“@you the empty state copy is ready for review.”
"use client";
import * as React from "react";
import {
BellIcon,
CheckIcon,
GitPullRequestIcon,
MessageSquareIcon,
} from "lucide-react";
import { Button } from "@/components/ui/button";
import {
Marker,
MarkerContent,
MarkerIcon,
} from "@/components/ui/marker";
type Notice = {
id: string;
icon: React.ComponentType;
title: string;
detail: string;
time: string;
day: "Today" | "Yesterday";
};
const notices: Notice[] = [
{
id: "n1",
icon: MessageSquareIcon,
title: "Lena commented on Q4 roadmap",
detail: "“Can we move the SSO work before the pricing launch?”",
time: "11:42",
day: "Today",
},
{
id: "n2",
icon: GitPullRequestIcon,
title: "Review requested on PR 1289",
detail: "Add rate limiting to the export endpoint",
time: "10:05",
day: "Today",
},
{
id: "n3",
icon: BellIcon,
title: "Invoice INV-2093 was paid",
detail: "Northwind Traders · $2,400.00",
time: "17:30",
day: "Yesterday",
},
{
id: "n4",
icon: MessageSquareIcon,
title: "Theo mentioned you in Design sync",
detail: "“@you the empty state copy is ready for review.”",
time: "14:12",
day: "Yesterday",
},
];
export default function Marker07() {
// The first `unreadCount` notices are unread; the rest were already seen.
const [unreadCount, setUnreadCount] = React.useState(3);
const days = ["Today", "Yesterday"] as const;
return (
<section
aria-labelledby="marker-07-title"
className="flex w-full max-w-sm flex-col rounded-xl border border-border bg-card text-card-foreground"
>
<header className="flex items-center justify-between gap-3 px-4 pt-4 pb-2">
<h3 id="marker-07-title" className="text-sm font-medium">
Notifications
</h3>
<Button
variant="ghost"
size="xs"
disabled={unreadCount === 0}
onClick={() => setUnreadCount(0)}
>
<CheckIcon data-icon="inline-start" aria-hidden="true" />
Mark all read
</Button>
</header>
<div className="flex flex-col gap-1 px-4 pb-4">
{days.map((day) => (
<div key={day} className="flex flex-col">
<Marker variant="separator" className="py-2 text-xs">
<MarkerContent>{day}</MarkerContent>
</Marker>
<ul className="flex flex-col">
{notices
.filter((notice) => notice.day === day)
.map((notice) => {
const index = notices.indexOf(notice);
const unread = index < unreadCount;
const Icon = notice.icon;
return (
<li key={notice.id} className="flex flex-col">
<div className="flex gap-3 py-2.5">
<span
aria-hidden="true"
className="mt-0.5 flex size-7 shrink-0 items-center justify-center rounded-full bg-muted text-muted-foreground [&_svg]:size-3.5"
>
<Icon />
</span>
<div className="flex min-w-0 flex-1 flex-col gap-0.5">
<p
className={
unread
? "text-sm font-medium"
: "text-sm text-muted-foreground"
}
>
{notice.title}
{unread && (
<span className="sr-only"> (unread)</span>
)}
</p>
<p className="truncate text-xs text-muted-foreground">
{notice.detail}
</p>
</div>
<span className="shrink-0 text-xs text-muted-foreground tabular-nums">
{notice.time}
</span>
</div>
{unreadCount > 0 && index === unreadCount - 1 && (
<Marker variant="border" className="text-xs">
<MarkerIcon>
<CheckIcon />
</MarkerIcon>
<MarkerContent>Already read below this line</MarkerContent>
</Marker>
)}
</li>
);
})}
</ul>
</div>
))}
{unreadCount === 0 && (
<Marker role="status" className="pt-2 text-xs">
<MarkerIcon>
<CheckIcon />
</MarkerIcon>
<MarkerContent>All notifications marked as read</MarkerContent>
</Marker>
)}
</div>
</section>
);
}
npx shadcn@latest add @sevenui/component/marker-07pnpm dlx shadcn@latest add @sevenui/component/marker-07yarn dlx shadcn@latest add @sevenui/component/marker-07bunx --bun shadcn@latest add @sevenui/component/marker-07"use client";
import * as React from "react";
import {
CircleAlertIcon,
CircleCheckIcon,
CloudUploadIcon,
} from "lucide-react";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import {
Marker,
MarkerContent,
MarkerIcon,
} from "@/components/ui/marker";
import { Textarea } from "@/components/ui/textarea";
type SaveState = "saved" | "saving" | "invalid";
const statusCopy: Record<SaveState, string> = {
saved: "All changes saved",
saving: "Saving changes…",
invalid: "Not saved — workspace name is required",
};
export default function Marker08() {
const [name, setName] = React.useState("Acme Logistics");
const [about, setAbout] = React.useState(
"Freight routing and warehouse tooling for mid-size carriers.",
);
const [state, setState] = React.useState<SaveState>("saved");
const timer = React.useRef<ReturnType<typeof setTimeout> | null>(null);
React.useEffect(() => {
return () => {
if (timer.current) clearTimeout(timer.current);
};
}, []);
function queueSave(nextName: string) {
if (timer.current) clearTimeout(timer.current);
if (nextName.trim() === "") {
setState("invalid");
return;
}
setState("saving");
// Simulate a debounced autosave round-trip.
timer.current = setTimeout(() => setState("saved"), 900);
}
const Icon =
state === "saved"
? CircleCheckIcon
: state === "saving"
? CloudUploadIcon
: CircleAlertIcon;
return (
<form
aria-labelledby="marker-08-title"
onSubmit={(event) => event.preventDefault()}
className="flex w-full max-w-md flex-col gap-5 rounded-xl border border-border bg-card p-5 text-card-foreground"
>
<div className="flex flex-col gap-1">
<h3 id="marker-08-title" className="text-sm font-medium">
Workspace profile
</h3>
<p className="text-sm text-muted-foreground">
Changes save automatically as you type.
</p>
</div>
<div className="flex flex-col gap-2">
<Label htmlFor="marker-08-name">Workspace name</Label>
<Input
id="marker-08-name"
value={name}
aria-invalid={state === "invalid" || undefined}
aria-describedby="marker-08-status"
onChange={(event) => {
setName(event.target.value);
queueSave(event.target.value);
}}
/>
</div>
<div className="flex flex-col gap-2">
<Label htmlFor="marker-08-about">Description</Label>
<Textarea
id="marker-08-about"
rows={3}
value={about}
onChange={(event) => {
setAbout(event.target.value);
queueSave(name);
}}
/>
</div>
<Marker
id="marker-08-status"
role="status"
data-state={state}
className="border-t border-border pt-4 text-xs data-[state=invalid]:text-destructive data-[state=saving]:text-foreground"
>
<MarkerIcon
className={state === "saving" ? "motion-safe:animate-pulse" : undefined}
>
<Icon />
</MarkerIcon>
<MarkerContent>{statusCopy[state]}</MarkerContent>
</Marker>
</form>
);
}
npx shadcn@latest add @sevenui/component/marker-08pnpm dlx shadcn@latest add @sevenui/component/marker-08yarn dlx shadcn@latest add @sevenui/component/marker-08bunx --bun shadcn@latest add @sevenui/component/marker-08Order #SV-30418
In transitMerino crew sweater · Charcoal · M
Arrives today by 8 PM
- Thursday, Sep 25
- Out for delivery from Portland, OR (latest update)
- Arrived at Portland distribution center
- Wednesday, Sep 24
- Departed Sacramento, CA
- Picked up by carrier
- Order packed and labeled
"use client";
import {
CircleCheckIcon,
MapPinIcon,
PackageCheckIcon,
TruckIcon,
WarehouseIcon,
} from "lucide-react";
import { Badge } from "@/components/ui/badge";
import {
Marker,
MarkerContent,
MarkerIcon,
} from "@/components/ui/marker";
const days = [
{
label: "Thursday, Sep 25",
events: [
{
id: "e5",
icon: TruckIcon,
time: "07:48",
text: "Out for delivery from Portland, OR",
current: true,
},
{
id: "e4",
icon: WarehouseIcon,
time: "03:12",
text: "Arrived at Portland distribution center",
},
],
},
{
label: "Wednesday, Sep 24",
events: [
{
id: "e3",
icon: MapPinIcon,
time: "18:30",
text: "Departed Sacramento, CA",
},
{
id: "e2",
icon: PackageCheckIcon,
time: "11:05",
text: "Picked up by carrier",
},
{
id: "e1",
icon: CircleCheckIcon,
time: "09:40",
text: "Order packed and labeled",
},
],
},
];
export default function Marker09() {
return (
<section
aria-labelledby="marker-09-title"
className="flex w-full max-w-sm flex-col gap-4 rounded-xl border border-border bg-card p-4 text-card-foreground"
>
<div className="flex items-start gap-3">
<img
src="/placeholder.svg"
alt=""
className="size-14 shrink-0 rounded-lg border border-border bg-muted object-cover"
/>
<div className="flex min-w-0 flex-1 flex-col gap-1">
<div className="flex flex-wrap items-center justify-between gap-x-2 gap-y-1">
<h3 id="marker-09-title" className="text-sm font-medium">
Order #SV-30418
</h3>
<Badge variant="secondary">In transit</Badge>
</div>
<p className="text-xs text-muted-foreground">
Merino crew sweater · Charcoal · M
</p>
<p className="text-xs">
Arrives <span className="font-medium">today by 8 PM</span>
</p>
</div>
</div>
<ol aria-label="Shipment history" className="flex flex-col gap-3">
{days.map((day) => (
<li key={day.label} className="flex flex-col gap-2.5">
<Marker variant="separator" className="text-xs">
<MarkerContent>{day.label}</MarkerContent>
</Marker>
<ol className="flex flex-col gap-2.5">
{day.events.map((event) => {
const Icon = event.icon;
return (
<li key={event.id}>
<Marker
aria-current={event.current ? "step" : undefined}
className="items-start aria-[current=step]:text-foreground"
>
<MarkerIcon className="mt-0.5">
<Icon />
</MarkerIcon>
<MarkerContent className="flex-1">
{event.text}
{event.current && (
<span className="sr-only"> (latest update)</span>
)}
</MarkerContent>
<time className="shrink-0 text-xs text-muted-foreground tabular-nums">
{event.time}
</time>
</Marker>
</li>
);
})}
</ol>
</li>
))}
</ol>
</section>
);
}
npx shadcn@latest add @sevenui/component/marker-09pnpm dlx shadcn@latest add @sevenui/component/marker-09yarn dlx shadcn@latest add @sevenui/component/marker-09bunx --bun shadcn@latest add @sevenui/component/marker-09Billing history
Invoices and plan changes for Brightline Studio.
- 2026
- Sep 1 invoiceINV-0931 · Team · 8 seatsPaid$96.00
- Upgraded from Starter to Team on Aug 14 · $31.20 prorated
- Aug 1 invoiceINV-0874 · StarterPaid$29.00
- Jul 1 invoiceINV-0812 · StarterRefunded$29.00
- 2025
- LAUNCH25 applied — 25% off for 3 months
- Dec 1 invoiceINV-0655 · StarterPaid$21.75
"use client";
import * as React from "react";
import {
ArrowUpCircleIcon,
CheckIcon,
DownloadIcon,
TicketPercentIcon,
} from "lucide-react";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import {
Marker,
MarkerContent,
MarkerIcon,
} from "@/components/ui/marker";
type Row =
| { kind: "year"; id: string; label: string }
| {
kind: "invoice";
id: string;
date: string;
amount: string;
plan: string;
status: "Paid" | "Refunded";
}
| { kind: "note"; id: string; icon: "upgrade" | "coupon"; text: string };
const rows: Row[] = [
{ kind: "year", id: "y2026", label: "2026" },
{
kind: "invoice",
id: "INV-0931",
date: "Sep 1",
amount: "$96.00",
plan: "Team · 8 seats",
status: "Paid",
},
{
kind: "note",
id: "n-upgrade",
icon: "upgrade",
text: "Upgraded from Starter to Team on Aug 14 · $31.20 prorated",
},
{
kind: "invoice",
id: "INV-0874",
date: "Aug 1",
amount: "$29.00",
plan: "Starter",
status: "Paid",
},
{
kind: "invoice",
id: "INV-0812",
date: "Jul 1",
amount: "$29.00",
plan: "Starter",
status: "Refunded",
},
{ kind: "year", id: "y2025", label: "2025" },
{
kind: "note",
id: "n-coupon",
icon: "coupon",
text: "LAUNCH25 applied — 25% off for 3 months",
},
{
kind: "invoice",
id: "INV-0655",
date: "Dec 1",
amount: "$21.75",
plan: "Starter",
status: "Paid",
},
];
export default function Marker10() {
const [downloaded, setDownloaded] = React.useState<string | null>(null);
const timer = React.useRef<ReturnType<typeof setTimeout> | null>(null);
React.useEffect(() => {
return () => {
if (timer.current) clearTimeout(timer.current);
};
}, []);
function download(id: string) {
if (timer.current) clearTimeout(timer.current);
setDownloaded(id);
// Simulate the file download finishing, then restore the icon.
timer.current = setTimeout(() => setDownloaded(null), 2000);
}
return (
<section
aria-labelledby="marker-10-title"
className="flex w-full max-w-md flex-col gap-3 rounded-xl border border-border bg-card p-4 text-card-foreground"
>
<div className="flex flex-col gap-0.5">
<h3 id="marker-10-title" className="text-sm font-medium">
Billing history
</h3>
<p className="text-sm text-muted-foreground">
Invoices and plan changes for Brightline Studio.
</p>
</div>
<ul className="flex flex-col">
{rows.map((row) => {
if (row.kind === "year") {
return (
<li key={row.id} className="py-2">
<Marker variant="separator" className="text-xs">
<MarkerContent>{row.label}</MarkerContent>
</Marker>
</li>
);
}
if (row.kind === "note") {
return (
<li key={row.id} className="py-1.5 pl-1">
<Marker className="text-xs">
<MarkerIcon>
{row.icon === "upgrade" ? (
<ArrowUpCircleIcon />
) : (
<TicketPercentIcon />
)}
</MarkerIcon>
<MarkerContent>{row.text}</MarkerContent>
</Marker>
</li>
);
}
return (
<li
key={row.id}
className="flex items-center gap-2 border-b border-border py-2.5 last:border-b-0 sm:gap-3"
>
<div className="flex min-w-0 flex-1 flex-col">
<span className="text-sm font-medium">
{row.date} <span className="sr-only">invoice</span>
</span>
<span className="text-xs text-muted-foreground">
<span className="whitespace-nowrap">{row.id} ·</span>{" "}
{row.plan}
</span>
</div>
<Badge variant={row.status === "Paid" ? "outline" : "secondary"}>
{row.status}
</Badge>
<span
className={
row.status === "Refunded"
? "w-16 text-right text-sm text-muted-foreground tabular-nums line-through"
: "w-16 text-right text-sm tabular-nums"
}
>
{row.amount}
</span>
<Button
variant="ghost"
size="icon-sm"
aria-label={
downloaded === row.id
? `Invoice ${row.id} downloaded`
: `Download invoice ${row.id}`
}
onClick={() => download(row.id)}
>
{downloaded === row.id ? (
<CheckIcon aria-hidden="true" className="text-success" />
) : (
<DownloadIcon aria-hidden="true" />
)}
</Button>
</li>
);
})}
</ul>
<p role="status" className="sr-only">
{downloaded ? `Invoice ${downloaded} downloaded` : ""}
</p>
</section>
);
}
npx shadcn@latest add @sevenui/component/marker-10pnpm dlx shadcn@latest add @sevenui/component/marker-10yarn dlx shadcn@latest add @sevenui/component/marker-10bunx --bun shadcn@latest add @sevenui/component/marker-10Thursday agenda
Sep 25- Now · 11:24
- Lunch with AmaraCafé Lumen
- Vendor call — ParcellyVideo callOverlaps with Lunch with Amara · Suggest a new time
- 2h 30m free · good for focus time
- 1:1 with DevonRoom 4B
"use client";
import * as React from "react";
import { ChevronsUpDownIcon, TriangleAlertIcon } from "lucide-react";
import {
Marker,
MarkerContent,
MarkerIcon,
} from "@/components/ui/marker";
type Slot =
| {
kind: "event";
id: string;
start: string;
end: string;
title: string;
room: string;
conflict?: boolean;
}
| { kind: "now"; id: string; time: string }
| { kind: "gap"; id: string; text: string };
const slots: Slot[] = [
{
kind: "event",
id: "s1",
start: "09:00",
end: "09:30",
title: "Platform standup",
room: "Huddle room 2",
},
{
kind: "event",
id: "s2",
start: "10:00",
end: "11:00",
title: "Checkout redesign review",
room: "Video call",
},
{ kind: "now", id: "now", time: "11:24" },
{
kind: "event",
id: "s3",
start: "12:00",
end: "13:00",
title: "Lunch with Amara",
room: "Café Lumen",
},
{
kind: "event",
id: "s4",
start: "12:30",
end: "13:00",
title: "Vendor call — Parcelly",
room: "Video call",
conflict: true,
},
{ kind: "gap", id: "gap", text: "2h 30m free · good for focus time" },
{
kind: "event",
id: "s5",
start: "15:30",
end: "16:15",
title: "1:1 with Devon",
room: "Room 4B",
},
];
export default function Marker11() {
const [showPast, setShowPast] = React.useState(false);
const nowIndex = slots.findIndex((slot) => slot.kind === "now");
const pastCount = nowIndex;
const visible = showPast ? slots : slots.slice(nowIndex);
const listId = React.useId();
return (
<section
aria-labelledby="marker-11-title"
className="flex w-full max-w-sm flex-col gap-3 rounded-xl border border-border bg-card p-4 text-card-foreground"
>
<div className="flex items-baseline justify-between gap-3">
<h3 id="marker-11-title" className="text-sm font-medium">
Thursday agenda
</h3>
<span className="text-xs text-muted-foreground">Sep 25</span>
</div>
<Marker
render={<button type="button" />}
aria-expanded={showPast}
aria-controls={listId}
onClick={() => setShowPast((value) => !value)}
className="cursor-pointer rounded-md py-1 text-xs outline-none hover:text-foreground focus-visible:ring-3 focus-visible:ring-ring/50"
>
<MarkerIcon>
<ChevronsUpDownIcon />
</MarkerIcon>
<MarkerContent>
{showPast ? "Hide" : "Show"} {pastCount} earlier meetings
</MarkerContent>
</Marker>
<ol id={listId} aria-label="Meetings" className="flex flex-col gap-2">
{visible.map((slot) => {
if (slot.kind === "now") {
return (
<li key={slot.id} aria-label={`Current time ${slot.time}`}>
<Marker
variant="separator"
className="text-xs font-medium text-primary before:bg-primary after:bg-primary"
>
<MarkerContent>Now · {slot.time}</MarkerContent>
</Marker>
</li>
);
}
if (slot.kind === "gap") {
return (
<li key={slot.id}>
<Marker className="justify-center py-1 text-xs">
<MarkerContent>{slot.text}</MarkerContent>
</Marker>
</li>
);
}
const past = slots.indexOf(slot) < nowIndex;
return (
<li key={slot.id} className="flex flex-col gap-1">
<div
className={
past
? "flex gap-3 rounded-lg bg-muted/50 p-2.5 text-muted-foreground"
: "flex gap-3 rounded-lg border border-border bg-background p-2.5"
}
>
<div className="flex w-11 shrink-0 flex-col text-xs tabular-nums">
<time className="font-medium">{slot.start}</time>
<time className="text-muted-foreground">{slot.end}</time>
</div>
<div className="flex min-w-0 flex-col">
<span className="truncate text-sm font-medium">
{slot.title}
</span>
<span className="truncate text-xs text-muted-foreground">
{slot.room}
</span>
</div>
</div>
{slot.conflict && (
<Marker className="pl-1 text-xs text-destructive">
<MarkerIcon>
<TriangleAlertIcon />
</MarkerIcon>
<MarkerContent>
Overlaps with Lunch with Amara ·{" "}
<a href="#reschedule-parcelly">Suggest a new time</a>
</MarkerContent>
</Marker>
)}
</li>
);
})}
</ol>
</section>
);
}
npx shadcn@latest add @sevenui/component/marker-11pnpm dlx shadcn@latest add @sevenui/component/marker-11yarn dlx shadcn@latest add @sevenui/component/marker-11bunx --bun shadcn@latest add @sevenui/component/marker-11OpenPR 1289
Rate limit the CSV export endpoint
- nadia-k pushed 2 commits
- Add token bucket to export endpoint
a41c9e2 - Return Retry-After header on 429
7d03b18
- Add token bucket to export endpoint
- src/routes/export.ts · line 42RB
Ravi Bose — Should the bucket be per API key instead of per IP? Shared office networks will hit this fast.
1 unresolved thread - All checks passed on 7d03b18 · Details
Resolve open threads before merging.
"use client";
import * as React from "react";
import {
CircleCheckIcon,
GitCommitHorizontalIcon,
GitMergeIcon,
GitPullRequestArrowIcon,
MessageSquareDotIcon,
} from "lucide-react";
import { Avatar, AvatarFallback } from "@/components/ui/avatar";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import {
Marker,
MarkerContent,
MarkerIcon,
} from "@/components/ui/marker";
const commits = [
{ sha: "a41c9e2", message: "Add token bucket to export endpoint" },
{ sha: "7d03b18", message: "Return Retry-After header on 429" },
];
export default function Marker12() {
const [resolved, setResolved] = React.useState(false);
const [merged, setMerged] = React.useState(false);
return (
<section
aria-labelledby="marker-12-title"
className="flex w-full max-w-md flex-col gap-4 rounded-xl border border-border bg-card p-4 text-card-foreground"
>
<header className="flex flex-col gap-1.5">
<div className="flex items-center gap-2">
{merged ? (
<Badge>
<GitMergeIcon aria-hidden="true" />
Merged
</Badge>
) : (
<Badge variant="outline">
<GitPullRequestArrowIcon aria-hidden="true" />
Open
</Badge>
)}
<span className="text-xs text-muted-foreground">PR 1289</span>
</div>
<h3 id="marker-12-title" className="text-sm font-medium">
Rate limit the CSV export endpoint
</h3>
</header>
<ol aria-label="Pull request activity" className="flex flex-col gap-3">
<li className="flex flex-col gap-1.5">
<Marker className="text-xs">
<MarkerIcon>
<GitCommitHorizontalIcon />
</MarkerIcon>
<MarkerContent>
<span className="font-medium text-foreground">nadia-k</span>{" "}
pushed 2 commits
</MarkerContent>
</Marker>
<ul className="ml-6 flex flex-col gap-1">
{commits.map((commit) => (
<li
key={commit.sha}
className="flex items-center justify-between gap-3 text-xs"
>
<span className="truncate">{commit.message}</span>
<code className="shrink-0 rounded bg-muted px-1.5 py-0.5 font-mono text-[0.7rem] text-muted-foreground">
{commit.sha}
</code>
</li>
))}
</ul>
</li>
<li
className={
resolved
? "flex flex-col gap-2 rounded-lg border border-border bg-muted/40 p-3"
: "flex flex-col gap-2 rounded-lg border border-border p-3"
}
>
<Marker variant="border" className="text-xs">
<MarkerIcon>
<MessageSquareDotIcon />
</MarkerIcon>
<MarkerContent className="font-mono">
src/routes/export.ts · line 42
</MarkerContent>
</Marker>
<div className="flex gap-2.5">
<Avatar className="size-6">
<AvatarFallback className="text-[0.65rem]">RB</AvatarFallback>
</Avatar>
<p className="text-sm">
<span className="font-medium">Ravi Bose</span> — Should the bucket
be per API key instead of per IP? Shared office networks will hit
this fast.
</p>
</div>
<div className="flex flex-wrap items-center justify-between gap-2">
<Marker
role="status"
className="w-auto text-xs data-[resolved=true]:text-foreground"
data-resolved={resolved}
>
{resolved && (
<MarkerIcon>
<CircleCheckIcon />
</MarkerIcon>
)}
<MarkerContent>
{resolved ? "Resolved by you" : "1 unresolved thread"}
</MarkerContent>
</Marker>
{merged ? null : (
<Button
variant="outline"
size="xs"
onClick={() => setResolved((value) => !value)}
>
{resolved ? "Unresolve" : "Resolve conversation"}
</Button>
)}
</div>
</li>
<li>
<Marker className="text-xs">
<MarkerIcon>
<CircleCheckIcon />
</MarkerIcon>
<MarkerContent>
All checks passed on 7d03b18 · <a href="#pr-1289-checks">Details</a>
</MarkerContent>
</Marker>
</li>
{merged && (
<li>
<Marker className="text-xs text-foreground">
<MarkerIcon>
<GitMergeIcon />
</MarkerIcon>
<MarkerContent>
<span className="font-medium">You</span> squashed and merged
into main
</MarkerContent>
</Marker>
</li>
)}
</ol>
<div className="flex flex-wrap items-center justify-between gap-3 border-t border-border pt-3">
<p role="status" className="text-xs text-muted-foreground">
{merged
? "Merged just now."
: resolved
? "Ready to merge."
: "Resolve open threads before merging."}
</p>
{merged ? (
<Button size="sm" variant="outline" onClick={() => setMerged(false)}>
Undo merge
</Button>
) : (
<Button
size="sm"
disabled={!resolved}
onClick={() => setMerged(true)}
>
Squash and merge
</Button>
)}
</div>
</section>
);
}
npx shadcn@latest add @sevenui/component/marker-12pnpm dlx shadcn@latest add @sevenui/component/marker-12yarn dlx shadcn@latest add @sevenui/component/marker-12bunx --bun shadcn@latest add @sevenui/component/marker-12Sources: Q3 retention review, subscriptions
"use client";
import * as React from "react";
import {
CheckIcon,
ChevronRightIcon,
FileSearchIcon,
SearchIcon,
SparklesIcon,
TableIcon,
} from "lucide-react";
import { Bubble, BubbleContent } from "@/components/ui/bubble";
import { Button } from "@/components/ui/button";
import {
Collapsible,
CollapsibleContent,
CollapsibleTrigger,
} from "@/components/ui/collapsible";
import {
Marker,
MarkerContent,
MarkerIcon,
} from "@/components/ui/marker";
import { Message, MessageContent } from "@/components/ui/message";
const steps = [
{
id: "search",
icon: SearchIcon,
label: "Searched workspace for “churn”",
detail: "12 results in 3 sources",
},
{
id: "read",
icon: FileSearchIcon,
label: "Read Q3 retention review.pdf",
detail: "Pages 4–9",
},
{
id: "query",
icon: TableIcon,
label: "Queried subscriptions table",
detail: "2,184 rows · 380 ms",
},
];
export default function Marker13() {
const [open, setOpen] = React.useState(false);
const [feedback, setFeedback] = React.useState<"up" | "down" | null>(null);
return (
<section
aria-label="Assistant conversation"
className="flex w-full max-w-md flex-col gap-4 rounded-xl border border-border bg-card p-4 text-card-foreground"
>
<Message align="end">
<MessageContent>
<Bubble align="end">
<BubbleContent>
Why did churn go up for annual plans last quarter?
</BubbleContent>
</Bubble>
</MessageContent>
</Message>
<Collapsible open={open} onOpenChange={setOpen}>
<Marker
render={<CollapsibleTrigger />}
className="w-fit cursor-pointer rounded-md py-0.5 text-xs outline-none hover:text-foreground focus-visible:ring-3 focus-visible:ring-ring/50"
>
<MarkerIcon>
<SparklesIcon />
</MarkerIcon>
<MarkerContent>Used 3 tools · 4.2s</MarkerContent>
<ChevronRightIcon
aria-hidden="true"
className="size-3.5 transition-transform group-aria-expanded/marker:rotate-90"
/>
</Marker>
<CollapsibleContent>
<ol
aria-label="Tool calls"
className="mt-2 ml-2 flex flex-col gap-2 border-l border-border pl-4"
>
{steps.map((step) => {
const Icon = step.icon;
return (
<li key={step.id}>
<Marker className="items-start text-xs">
<MarkerIcon className="mt-0.5">
<Icon />
</MarkerIcon>
<MarkerContent className="flex flex-col">
<span className="text-foreground">{step.label}</span>
<span>{step.detail}</span>
</MarkerContent>
</Marker>
</li>
);
})}
</ol>
</CollapsibleContent>
</Collapsible>
<Message>
<MessageContent>
<Bubble variant="ghost">
<BubbleContent>
Annual churn rose from 4.1% to 6.8%, and 70% of the increase came
from accounts that never invited a second teammate. The Q3 review
ties this to the removal of the onboarding checklist in July.
</BubbleContent>
</Bubble>
</MessageContent>
</Message>
<div className="flex flex-wrap items-center justify-between gap-2">
<Marker className="w-auto text-xs">
<MarkerContent>
Sources: <a href="#source-retention">Q3 retention review</a>,{" "}
<a href="#source-subscriptions">subscriptions</a>
</MarkerContent>
</Marker>
<fieldset className="flex gap-1">
<legend className="sr-only">Rate this answer</legend>
<Button
variant={feedback === "up" ? "secondary" : "ghost"}
size="xs"
aria-pressed={feedback === "up"}
onClick={() => setFeedback(feedback === "up" ? null : "up")}
>
Helpful
</Button>
<Button
variant={feedback === "down" ? "secondary" : "ghost"}
size="xs"
aria-pressed={feedback === "down"}
onClick={() => setFeedback(feedback === "down" ? null : "down")}
>
Not helpful
</Button>
</fieldset>
</div>
<div role="status" className="empty:hidden">
{feedback && (
<Marker className="text-xs">
<MarkerIcon>
<CheckIcon />
</MarkerIcon>
<MarkerContent>Thanks — your feedback helps tune answers</MarkerContent>
</Marker>
)}
</div>
</section>
);
}
npx shadcn@latest add @sevenui/component/marker-13pnpm dlx shadcn@latest add @sevenui/component/marker-13yarn dlx shadcn@latest add @sevenui/component/marker-13bunx --bun shadcn@latest add @sevenui/component/marker-13