Viewfinder
Camera-POV chrome: bold corner brackets, optional zoom and REC badges. Web apps have no camera access — this is presentation scaffolding for a camera-style UI; recording is app state you set.
Platform wishlist — built and waiting on camera access (getUserMedia or a capture API). The UI ships today; the day Meta exposes the API, it plugs in. See the wishlist →
Installation
npx @glasskit-ui/cli add viewfinderInstall the SDK (it provides GlassViewport, useDpad and the stylesheet), then copy these files into your project:
npm install @glasskit-ui/react// components/lib/utils.tsexport type ClassValue = string | number | null | undefined | false;/** * Join truthy class names. Dependency-free on purpose: the lens components * style via bespoke semantic classes (no conflicting Tailwind utilities to * de-dupe), so this needs no clsx/tailwind-merge and resolves from anywhere * the registry is vendored. */export function cn(...inputs: ClassValue[]): string { return inputs.filter(Boolean).join(" ");}/** * Accessible name from a free-form `label` prop: the label itself when it's a * plain string, otherwise undefined (a ReactNode can't become an aria-label). */export function stringLabel(label: unknown): string | undefined { return typeof label === "string" ? label : undefined;}// components/glasskit/viewfinder.tsximport type { ReactNode } from "react";import { cn } from "../lib/utils";/** * <Viewfinder> — camera-POV chrome: corner brackets framing the shot, an * optional zoom badge and a REC indicator. `children` overlays (e.g. a focus * reticle). World-anchored framing — corners use physical positioning, never * mirrored. * * Platform note (2026-06): web apps on the Display have NO camera access — * no getUserMedia, no capture API. This is presentation scaffolding for a * camera-style UI (the lens shows the real world through the brackets), not * a working viewfinder. `recording` is app state you set; nothing records. */export function Viewfinder({ zoom, recording = false, children, className,}: { /** Zoom badge text, e.g. "1×" / "3×". */ zoom?: ReactNode; recording?: boolean; children?: ReactNode; className?: string;}) { return ( <div className={cn("gk-viewfinder", className)} role="img" aria-label="Camera viewfinder" > <span className="gk-viewfinder__c gk-viewfinder__c--tl" /> <span className="gk-viewfinder__c gk-viewfinder__c--tr" /> <span className="gk-viewfinder__c gk-viewfinder__c--bl" /> <span className="gk-viewfinder__c gk-viewfinder__c--br" /> {zoom != null ? ( <span className="gk-viewfinder__zoom t-caption">{zoom}</span> ) : null} {recording ? ( <span className="gk-viewfinder__rec t-caption"> <span className="gk-viewfinder__dot" aria-hidden="true" /> REC </span> ) : null} {children} </div> );}Usage
<Viewfinder zoom="3×" recording> <Reticle active /></Viewfinder>Props
Prop
Type
LiveCaptions
Real-time transcription / translation read at a glance: a speaker label and the running caption text on a low-anchored surface (the Captions app).
ComposeFlow
The working text-entry recipe: a TextField that opens a picker of choices when activated; choosing writes back and returns. The picker rides history, so the back gesture closes it. The seam system dictation would replace.