SPB Git forge

spb/polyllm

Public
15commits 1branches 0releases
2.2 MBsize
maindefault branch
13 days agolast push
TypeScript 97.4% SQL 1% JavaScript 0.9% CSS 0.6%
3.8 KB · 67 lines tsx
Raw Blame History
1import * as React from "react";2import { cn } from "@/lib/utils";34/**5 * Phone-shaped frame (390 × 844 proportions) for the mobile product mocks. The screen is a flex6 * column: mocks render a header, a scrolling body (`min-h-0 flex-1`) and a fixed bottom bar.7 * Nothing inside is interactive by default; callers opt in.8 */9export function PhoneFrame({ children, className, screenClassName, label }: { children: React.ReactNode; className?: string; screenClassName?: string; label?: string }) {10  return (11    <div className={cn("relative mx-auto w-full max-w-[300px]", className)} role={label ? "img" : undefined} aria-label={label}>12      <div className="relative aspect-[390/844] w-full rounded-[44px] border border-border-strong/70 bg-[#0e0f14] p-[7px] shadow-lg dark:border-border-strong">13        {/* Side buttons */}14        <span aria-hidden className="absolute -left-[3px] top-[18%] h-[6%] w-[3px] rounded-l-sm bg-border-strong/80" />15        <span aria-hidden className="absolute -left-[3px] top-[27%] h-[10%] w-[3px] rounded-l-sm bg-border-strong/80" />16        <span aria-hidden className="absolute -right-[3px] top-[22%] h-[14%] w-[3px] rounded-r-sm bg-border-strong/80" />17        <div className={cn("relative flex h-full w-full flex-col overflow-hidden rounded-[37px] bg-bg text-fg", screenClassName)}>18          {/* Status bar + dynamic island */}19          <div className="relative flex h-[42px] shrink-0 items-end justify-between px-6 pb-1.5 text-[11px] font-semibold tabular-nums">20            <span>9:41</span>21            <span aria-hidden className="absolute left-1/2 top-2 h-[24px] w-[84px] -translate-x-1/2 rounded-full bg-[#0e0f14]" />22            <span className="flex items-center gap-1" aria-hidden>23              <svg width="14" height="10" viewBox="0 0 14 10" fill="currentColor">24                <rect x="0" y="6" width="2.5" height="4" rx="0.6" />25                <rect x="3.8" y="4" width="2.5" height="6" rx="0.6" />26                <rect x="7.6" y="2" width="2.5" height="8" rx="0.6" />27                <rect x="11.4" y="0" width="2.5" height="10" rx="0.6" />28              </svg>29              <svg width="22" height="11" viewBox="0 0 22 11" fill="none" stroke="currentColor" strokeWidth="1">30                <rect x="0.5" y="0.5" width="18" height="10" rx="2.5" />31                <rect x="2" y="2" width="14" height="7" rx="1.2" fill="currentColor" stroke="none" />32                <path d="M20 4v3" strokeLinecap="round" />33              </svg>34            </span>35          </div>36          {children}37          {/* Home indicator */}38          <div aria-hidden className="pointer-events-none absolute inset-x-0 bottom-1.5 flex justify-center">39            <span className="h-[4px] w-[96px] rounded-full bg-fg/70" />40          </div>41        </div>42      </div>43    </div>44  );45}4647/** Desktop app window chrome. Same look as the marketing `Window`, sized for the product mocks. */48export function DesktopFrame({ title, children, className, url }: { title?: string; children: React.ReactNode; className?: string; url?: string }) {49  return (50    <div className={cn("overflow-hidden rounded-xl border border-border bg-bg shadow-lg", className)}>51      <div className="flex h-9 items-center gap-2 border-b border-border bg-bg-subtle/80 px-3">52        <span className="flex gap-1.5" aria-hidden>53          <span className="size-2.5 rounded-full bg-border-strong" />54          <span className="size-2.5 rounded-full bg-border-strong" />55          <span className="size-2.5 rounded-full bg-border-strong" />56        </span>57        {url ? (58          <span className="mx-auto hidden h-6 min-w-[40%] items-center justify-center rounded-md bg-bg px-3 font-mono text-[11px] text-fg-subtle sm:inline-flex">{url}</span>59        ) : title ? (60          <span className="ml-2 truncate font-mono text-[11px] text-fg-subtle">{title}</span>61        ) : null}62      </div>63      {children}64    </div>65  );66}67