| 1 | import { cn } from "../../lib/utils"; |
| 2 | import type { ThemeCardPreviewColors } from "./ThemePreviewCircles"; |
| 3 | |
| 4 | // A simple miniature of the app: sidebar, a short conversation, the |
| 5 | // composer, and the orchestrator panel floating over the interface as an |
| 6 | // island with horizontal agent rows. |
| 7 | function ThemeWireframePane({ |
| 8 | colors, |
| 9 | clip, |
| 10 | }: { |
| 11 | colors: ThemeCardPreviewColors; |
| 12 | clip?: "left" | "right" | undefined; |
| 13 | }) { |
| 14 | const line = "rgb(127 127 127 / 0.25)"; |
| 15 | return ( |
| 16 | <span |
| 17 | className="absolute inset-0" |
| 18 | style={ |
| 19 | clip === undefined |
| 20 | ? undefined |
| 21 | : { |
| 22 | clipPath: |
| 23 | clip === "left" |
| 24 | ? "polygon(0 0, calc(50% - 1px) 0, calc(50% - 1px) 100%, 0 100%)" |
| 25 | : "polygon(calc(50% + 1px) 0, 100% 0, 100% 100%, calc(50% + 1px) 100%)", |
| 26 | } |
| 27 | } |
| 28 | > |
| 29 | <span className="absolute inset-0" style={{ backgroundColor: colors.canvas }} /> |
| 30 | <span |
| 31 | className="absolute inset-y-0 left-0 w-[22%]" |
| 32 | style={{ backgroundColor: colors.sidebar, boxShadow: `inset -1px 0 0 ${line}` }} |
| 33 | /> |
| 34 | |
| 35 | {/* Sidebar: search, then thread rows */} |
| 36 | <span |
| 37 | className="absolute left-[3%] top-[8%] h-[8%] w-[16%] rounded-md" |
| 38 | style={{ backgroundColor: colors.surface, boxShadow: `inset 0 0 0 1px ${line}` }} |
| 39 | /> |
| 40 | <span |
| 41 | className="absolute left-[3%] top-[22%] h-[7%] w-[16%] rounded-md" |
| 42 | style={{ backgroundColor: colors.accentSurface }} |
| 43 | /> |
| 44 | <span |
| 45 | className="absolute left-[3%] top-[32%] h-[7%] w-[16%] rounded-md" |
| 46 | style={{ backgroundColor: colors.messageSurface, opacity: 0.7 }} |
| 47 | /> |
| 48 | <span |
| 49 | className="absolute left-[3%] top-[42%] h-[7%] w-[16%] rounded-md" |
| 50 | style={{ backgroundColor: colors.messageSurface, opacity: 0.5 }} |
| 51 | /> |
| 52 | |
| 53 | {/* Conversation */} |
| 54 | <span |
| 55 | className="absolute right-[28%] top-[11%] h-[9%] w-[24%] rounded-lg" |
| 56 | style={{ backgroundColor: colors.messageSurface }} |
| 57 | /> |
| 58 | <span |
| 59 | className="absolute left-[27%] top-[28%] h-[5%] w-[34%] rounded-sm" |
| 60 | style={{ backgroundColor: line }} |
| 61 | /> |
| 62 | <span |
| 63 | className="absolute left-[27%] top-[38%] h-[5%] w-[26%] rounded-sm" |
| 64 | style={{ backgroundColor: line }} |
| 65 | /> |
| 66 | |
| 67 | {/* Composer */} |
| 68 | <span |
| 69 | className="absolute bottom-[8%] left-[26%] right-[6%] flex h-[15%] items-center justify-between rounded-md px-1" |
| 70 | style={{ |
| 71 | backgroundColor: colors.surface, |
| 72 | boxShadow: `inset 0 0 0 1px ${line}`, |
| 73 | }} |
| 74 | > |
| 75 | <span |
| 76 | className="block h-[26%] w-[34%] rounded-full" |
| 77 | style={{ backgroundColor: line, opacity: 0.7 }} |
| 78 | /> |
| 79 | <span |
| 80 | className="block aspect-square h-[58%] rounded-full" |
| 81 | style={{ backgroundColor: colors.messageAction }} |
| 82 | /> |
| 83 | </span> |
| 84 | |
| 85 | {/* Orchestrator island floating over the composer */} |
| 86 | <span |
| 87 | className="absolute right-[5%] top-[8%] h-[46%] w-[20%] rounded-lg" |
| 88 | style={{ |
| 89 | backgroundColor: colors.surface, |
| 90 | boxShadow: `inset 0 0 0 1px ${line}, 0 2px 5px rgb(0 0 0 / 0.14)`, |
| 91 | }} |
| 92 | > |
| 93 | {[0, 1, 2].map((row) => ( |
| 94 | <span |
| 95 | className="absolute left-[11%] right-[11%] flex items-center gap-1" |
| 96 | key={row} |
| 97 | style={{ top: `${10 + row * 30}%`, height: "20%" }} |
| 98 | > |
| 99 | <span |
| 100 | className="block aspect-square h-[26%] rounded-full" |
| 101 | style={{ |
| 102 | backgroundColor: |
| 103 | row === 0 ? "#34d399" : row === 1 ? colors.messageAction : "#fbbf24", |
| 104 | opacity: 0.55, |
| 105 | }} |
| 106 | /> |
| 107 | <span className="block h-[30%] w-[52%] rounded-sm" style={{ backgroundColor: line }} /> |
| 108 | </span> |
| 109 | ))} |
| 110 | </span> |
| 111 | </span> |
| 112 | ); |
| 113 | } |
| 114 | |
| 115 | export function ThemeWireframe({ |
| 116 | className, |
| 117 | panes, |
| 118 | }: { |
| 119 | /** Sizing (height) for the frame; the pane geometry is percentage based. */ |
| 120 | className?: string; |
| 121 | panes: ReadonlyArray<{ colors: ThemeCardPreviewColors; clip?: "left" | "right" }>; |
| 122 | }) { |
| 123 | return ( |
| 124 | <span |
| 125 | aria-hidden |
| 126 | className={cn( |
| 127 | "relative block w-full overflow-hidden rounded-lg border border-border/60", |
| 128 | className, |
| 129 | )} |
| 130 | > |
| 131 | {panes.map((pane) => ( |
| 132 | <ThemeWireframePane clip={pane.clip} colors={pane.colors} key={pane.clip ?? "pane"} /> |
| 133 | ))} |
| 134 | </span> |
| 135 | ); |
| 136 | } |