{
  "name": "Yap",
  "tagline": "Your slides composite over your live camera. You’re the show.",
  "platform": "macOS 26 (Tahoe), Apple Silicon",
  "docs": "https://cadence.host/yap/docs.html",
  "llms": "https://cadence.host/yap/llms.txt",
  "download": "https://github.com/bwats/yap-releases/releases/latest/download/Yap.dmg",
  "features": [
    {
      "id": "shared-capture-session",
      "name": "Shared camera capture session",
      "description": "Owns one AVCaptureSession, enumerates and switches built-in/external/iPhone Continuity/Desk View cameras, and reacts to device connect/disconnect.",
      "requirements": "Camera permission (NSCameraUsageDescription); macOS 26.0. Continuity Camera and Desk View require a paired iPhone.",
      "framework": "AVFoundation (AVCaptureSession, AVCaptureDeviceInput, AVCaptureVideoDataOutput, AVCaptureDevice.DiscoverySession)",
      "docAnchor": "#install",
      "family": "core",
      "docUrl": "https://cadence.host/yap/docs.html#install"
    },
    {
      "id": "live-preview",
      "name": "Live camera preview",
      "description": "Shows the live camera filling the stage at aspect-fill via AVCaptureVideoPreviewLayer; multiple previews share the single session.",
      "requirements": "Camera permission; a running AVCaptureSession.",
      "framework": "AVFoundation (AVCaptureVideoPreviewLayer); AppKit/SwiftUI (NSViewRepresentable, CALayer, CATransaction)",
      "docAnchor": "#slide-anatomy",
      "family": "core",
      "docUrl": "https://cadence.host/yap/docs.html#slide-anatomy"
    },
    {
      "id": "mirroring",
      "name": "Camera mirroring",
      "description": "Left-right mirror of the presenter applied consistently to live preview and segmentation preview while keeping raw recorded footage unmirrored.",
      "requirements": "A running AVCaptureSession; connection.isVideoMirroringSupported for the preview path.",
      "framework": "AVFoundation (AVCaptureConnection.isVideoMirrored); CoreGraphics (CGAffineTransform); CoreImage (export path)",
      "docAnchor": "#slide-anatomy",
      "family": "core",
      "docUrl": "https://cadence.host/yap/docs.html#slide-anatomy"
    },
    {
      "id": "per-slide-camera",
      "name": "Per-slide camera source",
      "description": "Each slide can designate a specific camera (Default keeps current); a deck can cut between built-in, external, iPhone Continuity, etc. across slides.",
      "requirements": "Camera permission; designated device must be available.",
      "framework": "AVFoundation device list via CameraManager; SwiftUI",
      "docAnchor": "#slide-anatomy",
      "family": "editor",
      "docUrl": "https://cadence.host/yap/docs.html#slide-anatomy"
    },
    {
      "id": "camera-backgrounded-power",
      "name": "Camera pause when backgrounded",
      "description": "In edit mode, pauses the camera on the last frame when Yap loses focus to save power; present mode always stays live.",
      "requirements": "None beyond a running session.",
      "framework": "AVFoundation (AVCaptureSession); SwiftUI @AppStorage",
      "docAnchor": "#slide-anatomy",
      "family": "core",
      "docUrl": "https://cadence.host/yap/docs.html#slide-anatomy"
    },
    {
      "id": "green-screen",
      "name": "Green screen (Vision person segmentation)",
      "description": "Removes the camera background live on-device via Vision person segmentation (not chroma key) so the presenter floats over slide media or a live web page; export/record path does NOT contain segmented footage — live effect only.",
      "requirements": "Camera permission (NSCameraUsageDescription); per-slide greenScreen flag enabled; macOS 12.0+ Vision floor; no separate TCC prompt for Vision itself.",
      "framework": "Vision (VNGeneratePersonSegmentationRequest, .balanced quality); CoreImage (CIBlendWithMask, CIContext); AVFoundation (sample-buffer delegate, GCD throttle); SwiftUI (SegmentationCameraView, NSViewRepresentable, CALayer)",
      "docAnchor": "#present",
      "family": "core",
      "docUrl": "https://cadence.host/yap/docs.html#present"
    },
    {
      "id": "live-captions",
      "name": "Live on-device captions",
      "description": "Transcribes the presenter's speech on-device via Apple Speech (requiresOnDeviceRecognition=true), displays centered subtitles at the bottom of the stage, and auto-restarts for continuous coverage; en-US only, not Apple Intelligence.",
      "requirements": "Microphone permission (NSMicrophoneUsageDescription) and Speech Recognition permission (NSSpeechRecognitionUsageDescription), both prompted in context on first use. Per-slide captions flag enabled.",
      "framework": "Speech (SFSpeechRecognizer, SFSpeechAudioBufferRecognitionRequest, SFSpeechRecognitionTask); AVFoundation (AVAudioEngine mic tap); SwiftUI (caption overlay, rounded semi-transparent subtitle)",
      "docAnchor": "#live-captions",
      "family": "present",
      "docUrl": "https://cadence.host/yap/docs.html#live-captions"
    },
    {
      "id": "effect-none",
      "name": "Effect: None (default)",
      "description": "No build-in animation; text appears settled instantly — the default moment for every new slide.",
      "requirements": "None.",
      "framework": "SwiftUI (live, MomentModifier); CoreImage (export)",
      "docAnchor": "#effects",
      "family": "effect-transform",
      "docUrl": "https://cadence.host/yap/docs.html#effects"
    },
    {
      "id": "effect-slam",
      "name": "Effect: Slam",
      "description": "Text drops in hard from above, scales down past 1, and lands with a quick horizontal shake.",
      "requirements": "None.",
      "framework": "SwiftUI MomentModifier (live); CoreImage affine transforms (export)",
      "docAnchor": "#effects",
      "family": "effect-transform",
      "docUrl": "https://cadence.host/yap/docs.html#effects"
    },
    {
      "id": "effect-scale-up",
      "name": "Effect: Scale up",
      "description": "Text dollies up toward the viewer from tiny/far, with blur resolving to sharp as it settles.",
      "requirements": "None.",
      "framework": "SwiftUI (live); CoreImage affine + CIGaussianBlur (export)",
      "docAnchor": "#effects",
      "family": "effect-transform",
      "docUrl": "https://cadence.host/yap/docs.html#effects"
    },
    {
      "id": "effect-star-wars",
      "name": "Effect: Star Wars",
      "description": "Text rises from below laid back in 3D perspective, then stands up to face the viewer and settles readable — the crawl-into-distance look.",
      "requirements": "None.",
      "framework": "SwiftUI (live); CoreImage perspective transform (export)",
      "docAnchor": "#effects",
      "family": "effect-transform",
      "docUrl": "https://cadence.host/yap/docs.html#effects"
    },
    {
      "id": "effect-zoom-punch",
      "name": "Effect: Zoom punch",
      "description": "Snap zoom: scales in from large with a slight overshoot past 1 (the punch), fast.",
      "requirements": "None.",
      "framework": "SwiftUI (live); CoreImage affine (export)",
      "docAnchor": "#effects",
      "family": "effect-transform",
      "docUrl": "https://cadence.host/yap/docs.html#effects"
    },
    {
      "id": "effect-bounce",
      "name": "Effect: Bounce",
      "description": "Text drops in from above and bounces to settle, with a quick squash compress on impact.",
      "requirements": "None.",
      "framework": "SwiftUI (live); CoreImage affine (export)",
      "docAnchor": "#effects",
      "family": "effect-transform",
      "docUrl": "https://cadence.host/yap/docs.html#effects"
    },
    {
      "id": "effect-typewriter",
      "name": "Effect: Typewriter",
      "description": "Reveals text character-by-character in a linear sweep — no transform, pure reveal.",
      "requirements": "None.",
      "framework": "SwiftUI (live, prefix-of-string reveal); pre-rendered frame sequence (export)",
      "docAnchor": "#effects",
      "family": "effect-reveal",
      "docUrl": "https://cadence.host/yap/docs.html#effects"
    },
    {
      "id": "effect-rgb-split",
      "name": "Effect: RGB split",
      "description": "Deterministic chromatic split: a hard horizontal RGB-channel offset at t=0 eases to clean at t=1.",
      "requirements": "default.metallib present in app bundle (YapEffects.metal compiled into the app target).",
      "framework": "Metal Shading Language kernel; SwiftUI ShaderLibrary .layerEffect (live); CoreImage CIKernel (export)",
      "docAnchor": "#effects",
      "family": "effect-shader",
      "docUrl": "https://cadence.host/yap/docs.html#effects"
    },
    {
      "id": "effect-sparkle",
      "name": "Effect: Sparkle",
      "description": "Shimmer-sweep plus soft bloom plus hash-placed twinkles that settle to clean text.",
      "requirements": "default.metallib in app bundle.",
      "framework": "Metal kernel; SwiftUI .layerEffect (live); CoreImage CIKernel (export)",
      "docAnchor": "#effects",
      "family": "effect-shader",
      "docUrl": "https://cadence.host/yap/docs.html#effects"
    },
    {
      "id": "effect-fire",
      "name": "Effect: Fire",
      "description": "Procedural fbm flame rises over the text's alpha with a warm color ramp, then settles clean.",
      "requirements": "default.metallib in app bundle.",
      "framework": "Metal kernel; SwiftUI .layerEffect (live); CoreImage CIKernel (export)",
      "docAnchor": "#effects",
      "family": "effect-shader",
      "docUrl": "https://cadence.host/yap/docs.html#effects"
    },
    {
      "id": "effect-glitch",
      "name": "Effect: Glitch",
      "description": "Digital tear-in: per-block-row horizontal tear plus RGB channel split plus bright digital-noise blocks; time is quantized to discrete frames.",
      "requirements": "default.metallib in app bundle.",
      "framework": "Metal kernel; SwiftUI .layerEffect (live); CoreImage CIKernel (export)",
      "docAnchor": "#effects",
      "family": "effect-shader",
      "docUrl": "https://cadence.host/yap/docs.html#effects"
    },
    {
      "id": "effect-neon",
      "name": "Effect: Neon",
      "description": "Alpha-edge tube fill plus outer glow halo plus subtle pulsing tube flicker, settling clean.",
      "requirements": "default.metallib in app bundle.",
      "framework": "Metal kernel; SwiftUI .layerEffect (live); CoreImage CIKernel (export)",
      "docAnchor": "#effects",
      "family": "effect-shader",
      "docUrl": "https://cadence.host/yap/docs.html#effects"
    },
    {
      "id": "effect-film-burn",
      "name": "Effect: Film burn",
      "description": "Vintage projector burn: warm sepia grade plus film grain plus exposure flicker plus a moving scorch bloom plus vignette, settling to clean white.",
      "requirements": "default.metallib in app bundle.",
      "framework": "Metal kernel; SwiftUI .layerEffect (live); CoreImage CIKernel (export)",
      "docAnchor": "#effects",
      "family": "effect-shader",
      "docUrl": "https://cadence.host/yap/docs.html#effects"
    },
    {
      "id": "effect-drip",
      "name": "Effect: Drip",
      "description": "Per-column vertical melt: each column sags downward by a deterministic per-column amount, then settles back to clean — the vertical mirror of Glitch's horizontal tear.",
      "requirements": "default.metallib in app bundle.",
      "framework": "Metal kernel; SwiftUI .layerEffect (live); CoreImage CIKernel (export)",
      "docAnchor": "#effects",
      "family": "effect-shader",
      "docUrl": "https://cadence.host/yap/docs.html#effects"
    },
    {
      "id": "live-export-parity",
      "name": "Live-vs-export effect parity guarantee",
      "description": "Every text effect looks identical in the live edit preview and in the rendered export file; a headless ExportSelfTest (ImageRenderer-based) gates this invariant.",
      "requirements": "default.metallib in app bundle; Metal device; a deck .yap and a camera clip (env-overridable: YAP_SELFTEST_DECK / _CAMERA / _STEP) for the full self-test.",
      "framework": "SwiftUI + Metal (live); AVFoundation + CoreImage + Metal (export); SwiftUI ImageRenderer (self-test); AppKit",
      "docAnchor": "#effects",
      "family": "effect-shader",
      "docUrl": "https://cadence.host/yap/docs.html#effects"
    },
    {
      "id": "ai-deck-builder",
      "name": "AI deck builder (tool-use agent loop)",
      "description": "User types a request; Claude edits the deck live by calling a fixed set of tools (add/edit/reorder slides, set layout/media/effects/reactions), each mutating ShowStore and appearing in the canvas as built.",
      "requirements": "An Anthropic API key (BYOK via Settings) or managed proxy (currently off/inert). Network egress to api.anthropic.com.",
      "framework": "SwiftUI/Combine (ChatController, @MainActor ObservableObject); Foundation URLSession (AnthropicClient); Anthropic Messages API with tool-use and prompt caching (GA, no beta header)",
      "docAnchor": "#ai",
      "family": "ai",
      "docUrl": "https://cadence.host/yap/docs.html#ai"
    },
    {
      "id": "byok",
      "name": "Bring-your-own Anthropic key (BYOK)",
      "description": "AI runs on the user's own Anthropic key stored in UserDefaults (not Keychain — known issue BWA-1318); default model is claude-sonnet-4-6, switchable to Opus 4.8 or Haiku 4.5.",
      "requirements": "User pastes an Anthropic key from console.anthropic.com into Settings.",
      "framework": "Foundation UserDefaults / SwiftUI @AppStorage; URLSession + Anthropic v1/messages",
      "docAnchor": "#ai",
      "family": "ai",
      "docUrl": "https://cadence.host/yap/docs.html#ai"
    },
    {
      "id": "image-generation",
      "name": "On-device AI image generation (ImagePlayground)",
      "description": "The AI's generate_image tool conjures an original illustration from a text prompt on-device via Apple ImagePlayground and places it on a slide as image media; throws and falls back to a placeholder if Apple Intelligence is unavailable.",
      "requirements": "Apple Intelligence enabled on an Apple-silicon Mac; @available(macOS 15.4, *) — app deploys to macOS 26.0 so API is always present; ImageCreator throws gracefully otherwise.",
      "framework": "Apple ImagePlayground (ImageCreator); AppKit (NSBitmapImageRep PNG); Foundation",
      "docAnchor": "#ai",
      "family": "ai",
      "docUrl": "https://cadence.host/yap/docs.html#ai"
    },
    {
      "id": "surprise-me",
      "name": "Surprise me (AI creative brief)",
      "description": "One tap has the AI invent a fun topic and build a complete deck live in silent mode, then jumps to Present on completion.",
      "requirements": "canUseAI true (Anthropic key set or managed proxy active).",
      "framework": "SwiftUI/Combine; Anthropic Messages API (same agent loop as ai-deck-builder)",
      "docAnchor": "#ai",
      "family": "ai",
      "docUrl": "https://cadence.host/yap/docs.html#ai"
    },
    {
      "id": "ai-checkpoints",
      "name": "AI build checkpoints and deck restore",
      "description": "Each AI turn snapshots the deck so the user can restore to any prior chat checkpoint; live build auto-advances the canvas slide by slide; silent/Surprise Me mode hides work and reveals the finished deck at the end.",
      "requirements": "An open ShowStore document.",
      "framework": "SwiftUI; JSONEncoder/Decoder of [Slide]",
      "docAnchor": "#ai",
      "family": "ai",
      "docUrl": "https://cadence.host/yap/docs.html#ai"
    },
    {
      "id": "ai-privacy",
      "name": "AI text-only payload (on-device data boundary)",
      "description": "Only typed prompts and the deck's text/structure are sent to Claude; camera frames, image bytes, screenshots, video, and recordings never leave the Mac.",
      "requirements": "None (data-flow property of the AI pipeline).",
      "framework": "n/a",
      "docAnchor": "#privacy",
      "family": "ai",
      "docUrl": "https://cadence.host/yap/docs.html#privacy"
    },
    {
      "id": "reactions",
      "name": "Reaction GIF library (browse + AI drop + personal)",
      "description": "Ships 21 curated looping reaction GIFs tagged by mood; browse/search in a grid popover, drag onto slides, let the AI drop a fitting reaction via add_reaction tool, or import your own GIFs into a personal library.",
      "requirements": "Selected slide for tap/drop (else NSSound.beep). AI reactions require an Anthropic key (BYOK). Personal library requires write access to the Yap folder.",
      "framework": "Foundation (Codable JSON, FileManager) for library; SwiftUI (LazyVGrid, draggable/dropDestination, Form); AppKit (NSImageView GIF animation, NSOpenPanel); Anthropic tool-use (add_reaction in ChatController)",
      "docAnchor": "#slide-anatomy",
      "family": "reactions",
      "docUrl": "https://cadence.host/yap/docs.html#slide-anatomy"
    },
    {
      "id": "present-mode",
      "name": "Present mode (full-canvas live stage)",
      "description": "Replaces the editor with a black full-canvas stage compositing the live camera with each slide's media, text, symbol, and captions; shows a Turn-on-your-camera overlay with Settings deep-link if camera is denied.",
      "requirements": "Camera permission (NSCameraUsageDescription) for the live feed.",
      "framework": "SwiftUI + AppKit; AVFoundation (CameraManager/CameraPreview); macOS 26 Liquid Glass UI (glassEffect)",
      "docAnchor": "#present",
      "family": "present",
      "docUrl": "https://cadence.host/yap/docs.html#present"
    },
    {
      "id": "layouts",
      "name": "Stage layouts (Full / PiP / Split / Media-Only)",
      "description": "Four per-slide camera/media layouts: Full (camera full-frame with inset media card), PiP (small camera in a chosen corner over full media), Split (side-by-side or stacked halves), Media-Only (camera hidden); layout animates with a spring on change.",
      "requirements": "Camera permission for camera-visible layouts; contextual sub-controls (PiP corner/size, Split side) appear only for the relevant layout.",
      "framework": "SwiftUI (live stage spring animations, ZStack/zIndex geometry); CoreImage compositor (export reframe)",
      "docAnchor": "#slide-anatomy",
      "family": "present",
      "docUrl": "https://cadence.host/yap/docs.html#slide-anatomy"
    },
    {
      "id": "web-slides",
      "name": "Live interactive web slides (WKWebView on air)",
      "description": "A slide shows a real, interactive browser the presenter can click, scroll, and type into while presenting — not a screenshot; presenter can stand in front of the page via green screen.",
      "requirements": "Network access for the loaded page; slide.mediaType == .web with webURLString set. No App Sandbox entitlement restricts network access.",
      "framework": "WebKit (WKWebView, NSViewRepresentable)",
      "docAnchor": "#slide-anatomy",
      "family": "present",
      "docUrl": "https://cadence.host/yap/docs.html#slide-anatomy"
    },
    {
      "id": "video-slides",
      "name": "Video slide media (autoplay + loop, muted by default)",
      "description": "A slide plays a looping video that autoplays muted and shows a paused first frame in edit; a floating mute button toggles audio live; autoplay and loop are per-slide toggles.",
      "requirements": "None (local file playback); no mic permission needed.",
      "framework": "AVFoundation (AVQueuePlayer, AVPlayerLooper, AVPlayerLayer); NSViewRepresentable",
      "docAnchor": "#slide-anatomy",
      "family": "present",
      "docUrl": "https://cadence.host/yap/docs.html#slide-anatomy"
    },
    {
      "id": "presenter-panel",
      "name": "Private Presenter panel (floating NSPanel)",
      "description": "A floating NSPanel off-stage holding slide counter, next-up preview, prev/next controls, record buttons, live camera chooser, per-slide inspector, and End — kept private while sharing only the stage window.",
      "requirements": "Microphone permission when recording with audio; Screen Recording (ScreenCaptureKit, runtime-prompted, no plist key) for Record This View from within the panel.",
      "framework": "AppKit (NSPanel, NSHostingController); SwiftUI; ScreenCaptureKit (Record This View path); AVFoundation SessionRecorder (Record Session path)",
      "docAnchor": "#present",
      "family": "present",
      "docUrl": "https://cadence.host/yap/docs.html#present"
    },
    {
      "id": "multi-display",
      "name": "Multi-display stage routing (DisplayRouter)",
      "description": "When a non-mirrored external display is attached, the stage auto-moves onto it and goes full-screen; the Presenter panel stays pinned to the built-in laptop display and never drifts to the projector.",
      "requirements": "External routing requires a non-mirrored external display; single-display setups present in-window or full-screen on current display.",
      "framework": "AppKit (NSWindow toggleFullScreen, NSScreen, NotificationCenter); CoreGraphics (CGDisplayIsBuiltin, CGDisplayIsInMirrorSet); DisplayRouter",
      "docAnchor": "#present",
      "family": "present",
      "docUrl": "https://cadence.host/yap/docs.html#present"
    },
    {
      "id": "slide-transitions",
      "name": "Slide-advance transitions (Cut / Fade / Slide)",
      "description": "Animates how each slide's overlay enters when advancing — Cut (instant), Fade, or Slide (directional move) — and burns the same transitions into the exported video.",
      "requirements": "None.",
      "framework": "SwiftUI (AnyTransition, YapMotion.morph spring, live/edit preview); AVFoundation custom AVVideoCompositing + CoreImage (export render)",
      "docAnchor": "#slide-anatomy",
      "family": "present",
      "docUrl": "https://cadence.host/yap/docs.html#slide-anatomy"
    },
    {
      "id": "present-keyboard",
      "name": "In-present keyboard controls",
      "description": "Live keyboard control during a talk: advance/retreat slides, end, switch/cycle layouts, toggle text style, edit text in place, hide everything, and caps-lock to hide just the text.",
      "requirements": "None.",
      "framework": "AppKit (NSEvent local monitors)",
      "docAnchor": "#present",
      "family": "present",
      "docUrl": "https://cadence.host/yap/docs.html#present"
    },
    {
      "id": "present-shortcuts",
      "name": "Present launch shortcuts (⌥⌘P / ⇧⌥⌘P)",
      "description": "Start presenting from the beginning (⌥⌘P) or from the current slide (⇧⌥⌘P) via menu bar, toolbar, or floating action bar.",
      "requirements": "None.",
      "framework": "SwiftUI Commands / keyboardShortcut",
      "docAnchor": "#present",
      "family": "present",
      "docUrl": "https://cadence.host/yap/docs.html#present"
    },
    {
      "id": "recording-session",
      "name": "Record Session (.yapsession bundle — recompose later)",
      "description": "Records a performance as a format-agnostic .yapsession bundle — raw un-mirrored camera+mic (camera.mov) plus a slide-advance timeline and resolved deck snapshot — so it can be recomposed into 16:9 and 9:16 afterward rather than baking one look.",
      "requirements": "Camera permission; Microphone permission (prompted on first record when mic input is added). Records at .high session preset (full sensor resolution so portrait crops have pixels).",
      "framework": "AVFoundation (AVCaptureMovieFileOutput, AVCaptureFileOutputRecordingDelegate, AVCaptureDeviceInput); Combine; Foundation (JSONEncoder)",
      "docAnchor": "#record",
      "family": "record",
      "docUrl": "https://cadence.host/yap/docs.html#record"
    },
    {
      "id": "recording-this-view",
      "name": "Record This View (ScreenCaptureKit baked .mp4)",
      "description": "Records exactly what the present window shows — one baked .mp4 with every effect already composited live — plus the microphone, via ScreenCaptureKit; output goes to the global Recordings folder.",
      "requirements": "Screen Recording permission (TCC, runtime-prompted, no plist key); Microphone permission (config.captureMicrophone = true).",
      "framework": "ScreenCaptureKit (SCStream, SCContentFilter, SCStreamConfiguration, SCStreamOutput); AVFoundation (AVAssetWriter)",
      "docAnchor": "#record",
      "family": "record",
      "docUrl": "https://cadence.host/yap/docs.html#record"
    },
    {
      "id": "export-reels",
      "name": "Export Reels (16:9 + 9:16 multi-format export)",
      "description": "Turns one recorded .yapsession into separate finished H.264 .mp4 files per aspect — 1920x1080 landscape and/or 1080x1920 portrait — reframing the camera and burning in slide overlays, transitions, and effects per aspect.",
      "requirements": "A valid .yapsession with camera.mov + timeline.json + deck.json; writable ~/Documents/Yap recordings folder. No camera/screen permission needed (offline render). GPU CIContext (useSoftwareRenderer:false).",
      "framework": "AVFoundation (AVMutableComposition, AVAssetReader/Writer, AVVideoCompositing, AVAssetImageGenerator); CoreImage (CIContext, CIImage filters); SwiftUI ImageRenderer (overlay burns); AppKit",
      "docAnchor": "#export",
      "family": "export",
      "docUrl": "https://cadence.host/yap/docs.html#export"
    },
    {
      "id": "export-shortcut",
      "name": "Export sheet shortcut (⇧⌘E)",
      "description": "Opens the Export Reels and Video sheet from anywhere in the app.",
      "requirements": "None.",
      "framework": "SwiftUI (Commands / keyboardShortcut)",
      "docAnchor": "#export",
      "family": "export",
      "docUrl": "https://cadence.host/yap/docs.html#export"
    },
    {
      "id": "editor-layout",
      "name": "Three-pane edit layout",
      "description": "Slide list left, live WYSIWYG canvas center, Inspector/Chat right; right column is collapsible so the canvas can take full width while shooting.",
      "requirements": "None.",
      "framework": "SwiftUI (HSplitView); AppKit",
      "docAnchor": "#slide-anatomy",
      "family": "editor",
      "docUrl": "https://cadence.host/yap/docs.html#slide-anatomy"
    },
    {
      "id": "wysiwyg-canvas",
      "name": "Live WYSIWYG canvas",
      "description": "Center pane: real-time composite of the selected slide over the live camera, matching present-mode z-order; text is click-to-edit in place.",
      "requirements": "Camera permission for the live feed; shows a Turn-on-your-camera prompt and Settings deep-link when unauthorized.",
      "framework": "SwiftUI; AppKit; AVFoundation (VideoPlayerView for video media); WebKit (WKWebView for web media)",
      "docAnchor": "#slide-anatomy",
      "family": "editor",
      "docUrl": "https://cadence.host/yap/docs.html#slide-anatomy"
    },
    {
      "id": "text-control",
      "name": "Text control (Off / Headline / Lower Third)",
      "description": "Sets whether the slide has text, which style (Headline or Lower Third), the copy itself, and its opacity.",
      "requirements": "None.",
      "framework": "SwiftUI; AppKit (NSFont measuring in fittingFontSize/wrappedFittingFontSize)",
      "docAnchor": "#slide-anatomy",
      "family": "editor",
      "docUrl": "https://cadence.host/yap/docs.html#slide-anatomy"
    },
    {
      "id": "text-effects-catalog",
      "name": "Text effects inspector catalog (14 ready + 2 SOON)",
      "description": "Inspector panel showing 14 selectable text build-in effects and 2 grayed-out SOON placeholders (Confetti, Shatter); selecting an effect previews it live on the edit canvas.",
      "requirements": "default.metallib in app bundle for the 7 Metal-shader effects; SwiftUI TimelineView for live preview.",
      "framework": "SwiftUI (TimelineView, filter-style picker); Effects subsystem (Moment/MomentModifier/shaders)",
      "docAnchor": "#effects",
      "family": "editor",
      "docUrl": "https://cadence.host/yap/docs.html#effects"
    },
    {
      "id": "media-control",
      "name": "Media control (None / Image / Web / Video + green screen)",
      "description": "Sets the slide's media type and source, opacity, video autoplay/loop, and the on-device green-screen toggle; controls are hidden when the slide inherits media via extends.",
      "requirements": "Green screen needs camera permission. Web media loads live. Image import via NSOpenPanel.",
      "framework": "SwiftUI; AppKit (NSOpenPanel, NSImage); Vision + CoreImage (green screen toggle)",
      "docAnchor": "#slide-anatomy",
      "family": "editor",
      "docUrl": "https://cadence.host/yap/docs.html#slide-anatomy"
    },
    {
      "id": "extends",
      "name": "Build-on-previous-slide (extends)",
      "description": "Holds the previous slide's media/camera/effects as a base and layers only this slide's text/style/layout/transition on top — Keynote-style build bullets without re-loading the background.",
      "requirements": "No effect on the first slide; resolved only when slide index > 0.",
      "framework": "SwiftUI; ShowStore (resolution logic)",
      "docAnchor": "#slide-anatomy",
      "family": "editor",
      "docUrl": "https://cadence.host/yap/docs.html#slide-anatomy"
    },
    {
      "id": "enter-transition",
      "name": "Enter transition with edit preview (Cut / Fade / Slide)",
      "description": "Sets how the slide's overlay enters when advanced to; selecting one previews the motion on the edit canvas; transitions also render in the exported video.",
      "requirements": "None (Cut is instant — preview is a no-op for Cut).",
      "framework": "SwiftUI (AnyTransition, live preview); AVFoundation AVVideoCompositing + CoreImage (export)",
      "docAnchor": "#slide-anatomy",
      "family": "editor",
      "docUrl": "https://cadence.host/yap/docs.html#slide-anatomy"
    },
    {
      "id": "layout-transition",
      "name": "Layout transition (stored + UI only, not yet wired)",
      "description": "Per-slide override for how the stage morphs when the layout changes from the previous slide — Auto, Morph, Cut, Crossfade, Push, Dip to black — stored and shown in the inspector but no live or export behavior is wired as shipped.",
      "requirements": "None (UI-only as shipped).",
      "framework": "SwiftUI (enum + inspector only; no live/export behavior)",
      "docAnchor": "#slide-anatomy",
      "family": "editor",
      "docUrl": "https://cadence.host/yap/docs.html#slide-anatomy"
    },
    {
      "id": "format-guides",
      "name": "Crop/viewfinder guides (FormatGuides)",
      "description": "Edit-only overlay showing where the other aspect ratio's safe frame falls so subject and text stay within both crops; in 16:9 marks the 9:16 center column, in 9:16 marks the bottom platform safe-zone band.",
      "requirements": "None.",
      "framework": "SwiftUI (GeometryReader)",
      "docAnchor": "#slide-anatomy",
      "family": "editor",
      "docUrl": "https://cadence.host/yap/docs.html#slide-anatomy"
    },
    {
      "id": "slide-sidebar",
      "name": "Slide sidebar (drag-reorder, drop media)",
      "description": "Left list of slides with index, build-arrow indicator, thumbnail, camera badge, and overflow menu; reorder by drag; drop image/GIF files onto a slide; bottom rows for Help/Recordings/Settings.",
      "requirements": "None.",
      "framework": "SwiftUI (LazyVStack, draggable/dropDestination); AppKit (AnimatedImageView for GIF playback)",
      "docAnchor": "#slide-anatomy",
      "family": "editor",
      "docUrl": "https://cadence.host/yap/docs.html#slide-anatomy"
    },
    {
      "id": "floating-action-bar",
      "name": "Floating action bar (glass capsule)",
      "description": "Figma-style glass capsule floating at the bottom of the canvas for fast actions: Add slide, Text, Image, Web, Video, Reactions, Present — each with a primary action and a caret menu/popover.",
      "requirements": "Most items disabled when no slide is selected; macOS 26 glassEffect required.",
      "framework": "SwiftUI (glassEffect, Menu, popover)",
      "docAnchor": "#slide-anatomy",
      "family": "editor",
      "docUrl": "https://cadence.host/yap/docs.html#slide-anatomy"
    },
    {
      "id": "right-pane-switcher",
      "name": "Right-pane mode switcher + aspect toggle",
      "description": "Segmented control flips the right column between Inspector (Edit), AI Chat (Create), and hidden (Off); a separate segmented toggle sets the output aspect (16:9 / 9:16).",
      "requirements": "None.",
      "framework": "SwiftUI (Picker .segmented, matchedGeometryEffect)",
      "docAnchor": "#slide-anatomy",
      "family": "editor",
      "docUrl": "https://cadence.host/yap/docs.html#slide-anatomy"
    },
    {
      "id": "yap-package",
      "name": ".yap deck package with autosave",
      "description": "Decks save as .yap packages (folder-as-file with show.json + assets/), autosave continuously, and restore the in-progress deck on next launch via Autosave.yap at the Yap-folder root.",
      "requirements": "None.",
      "framework": "Foundation (JSONEncoder/Decoder, FileManager); CryptoKit (SHA256 asset dedup); UniformTypeIdentifiers; Combine (debounce/sink for autosave); AppKit (NSOpenPanel/NSSavePanel)",
      "docAnchor": "#files",
      "family": "files",
      "docUrl": "https://cadence.host/yap/docs.html#files"
    },
    {
      "id": "version-history",
      "name": "Version history (NSFileVersion restore points)",
      "description": "Keeps periodic and on-demand restore points of a deck; browse by date and slide count and restore an earlier version; a restore is itself undoable by snapshotting the pre-restore state first.",
      "requirements": "None.",
      "framework": "Foundation (NSFileVersion); SwiftUI",
      "docAnchor": "#files",
      "family": "files",
      "docUrl": "https://cadence.host/yap/docs.html#files"
    },
    {
      "id": "yap-folder",
      "name": "Yap folder location (movable in Settings and onboarding)",
      "description": "Defines where Yap keeps decks, recordings, and reactions; defaults to ~/Documents/Yap and can be relocated by the user.",
      "requirements": "None (no special TCC permission; folder is user-chosen/default).",
      "framework": "Foundation (UserDefaults, FileManager); SwiftUI (@AppStorage); AppKit (NSOpenPanel)",
      "docAnchor": "#files",
      "family": "files",
      "docUrl": "https://cadence.host/yap/docs.html#files"
    },
    {
      "id": "welcome-deck",
      "name": "Welcome deck (built-in 13-slide sample)",
      "description": "A built-in 13-slide sample deck that demonstrates the product — slides over camera, layouts, text effects, a live web page, the AI builder, record-once-two-cuts — reopenable any time from Help.",
      "requirements": "None.",
      "framework": "Foundation / SwiftUI (Slide model, bundled JSON)",
      "docAnchor": "#install",
      "family": "files",
      "docUrl": "https://cadence.host/yap/docs.html#install"
    },
    {
      "id": "macos-26-requirement",
      "name": "macOS 26 (Tahoe) minimum requirement",
      "description": "App requires macOS 26 Tahoe and will not launch on earlier versions; the Liquid Glass UI (glassEffect, .glass buttonStyle) depends on Tahoe APIs.",
      "requirements": "macOS 26 Tahoe.",
      "framework": "SwiftUI (Liquid Glass: glassEffect, buttonStyle(.glass))",
      "docAnchor": "#install",
      "family": "app",
      "docUrl": "https://cadence.host/yap/docs.html#install"
    },
    {
      "id": "apple-silicon-nuance",
      "name": "Apple silicon optional (AI image gen only)",
      "description": "Core Yap runs on any macOS-26 Mac (Intel or Apple silicon); Apple silicon is required only for the optional on-device AI image-generation feature (ImagePlayground).",
      "requirements": "Apple silicon only for the image-generation path; core app: any macOS-26 Mac.",
      "framework": "ImagePlayground (optional path only)",
      "docAnchor": "#install",
      "family": "app",
      "docUrl": "https://cadence.host/yap/docs.html#install"
    },
    {
      "id": "onboarding",
      "name": "First-run onboarding (4-step sheet)",
      "description": "A short first-run walkthrough: welcome, the magic, in-context camera permission, choose Files folder; replayable from Settings > Help.",
      "requirements": "Shown once until hasOnboarded is set.",
      "framework": "SwiftUI (sheet, @AppStorage); AppKit (NSOpenPanel)",
      "docAnchor": "#install",
      "family": "app",
      "docUrl": "https://cadence.host/yap/docs.html#install"
    },
    {
      "id": "onboarding-pill",
      "name": "First-run canvas hint pill (OnboardingPill)",
      "description": "A floating glass PREVIEW capsule shown over the stage on first runs — 'click the text to edit · drop an image' — until dismissed.",
      "requirements": "macOS 26 glassEffect.",
      "framework": "SwiftUI (glassEffect / Liquid Glass)",
      "docAnchor": "#install",
      "family": "app",
      "docUrl": "https://cadence.host/yap/docs.html#install"
    },
    {
      "id": "settings",
      "name": "Settings modal (AI / Files / Camera / Help)",
      "description": "Claude-style settings sheet with left nav and grouped rows for AI key and model, output folder, camera defaults, and help/onboarding replay.",
      "requirements": "None.",
      "framework": "SwiftUI (@AppStorage, SecureField, Picker, Toggle); AppKit (NSOpenPanel)",
      "docAnchor": "#for-agents",
      "family": "app",
      "docUrl": "https://cadence.host/yap/docs.html#for-agents"
    },
    {
      "id": "analytics",
      "name": "Anonymous content-free analytics (opt-out)",
      "description": "Records that events happened — never deck content, camera data, or anything identifying — via PostHog direct capture API; user can opt out in Settings. Inert until AnalyticsConfig.postHogProjectKey is set.",
      "requirements": "None (inert as shipped — project key not set).",
      "framework": "Foundation / URLSession (PostHog capture API)",
      "docAnchor": "#privacy",
      "family": "app",
      "docUrl": "https://cadence.host/yap/docs.html#privacy"
    },
    {
      "id": "crash-reporting",
      "name": "Content-free crash reporting",
      "description": "On the next launch, reports a content-free summary of any crash from the prior run; forwards via the Analytics pipeline.",
      "requirements": "Inert until analytics sink is keyed (same as analytics).",
      "framework": "Foundation (FileManager, JSONSerialization); Analytics.track",
      "docAnchor": "#privacy",
      "family": "app",
      "docUrl": "https://cadence.host/yap/docs.html#privacy"
    },
    {
      "id": "sparkle-update",
      "name": "Sparkle over-the-air auto-update",
      "description": "Over-the-air app updates via Sparkle with a Check for Updates menu item.",
      "requirements": "Published appcast at SUFeedURL and a Sparkle private key to sign update deltas.",
      "framework": "Sparkle (SPM; SPUStandardUpdaterController)",
      "docAnchor": "#install",
      "family": "app",
      "docUrl": "https://cadence.host/yap/docs.html#install"
    },
    {
      "id": "anonymous-install-id",
      "name": "Anonymous install ID (Keychain)",
      "description": "A stable, anonymous per-install UUID stored in the Keychain used by analytics and the managed-AI credit meter; survives reinstalls.",
      "requirements": "None (no entitlement; generic-password keychain item).",
      "framework": "Security (Keychain: SecItemCopyMatching/Add/Delete)",
      "docAnchor": "#privacy",
      "family": "app",
      "docUrl": "https://cadence.host/yap/docs.html#privacy"
    },
    {
      "id": "in-app-feedback",
      "name": "In-app feedback (Discord webhook to Linear)",
      "description": "Send Feedback posts a user note plus app and macOS version to a Discord incoming webhook that an agent triages into Linear. Inert until FeedbackConfig.discordWebhookURL is set.",
      "requirements": "None (inert as shipped — webhook URL not set).",
      "framework": "SwiftUI; Foundation/URLSession (Discord incoming webhook)",
      "docAnchor": "#for-agents",
      "family": "app",
      "docUrl": "https://cadence.host/yap/docs.html#for-agents"
    },
    {
      "id": "help-menu",
      "name": "Help menu (offline guide, website, privacy, welcome deck)",
      "description": "Help menu opens the bundled offline user guide, the welcome deck, what's-new, feedback, website, and privacy page.",
      "requirements": "None (guide is bundled; works offline).",
      "framework": "SwiftUI Commands + AppKit (NSWorkspace.open); bundled HTML",
      "docAnchor": "#for-agents",
      "family": "app",
      "docUrl": "https://cadence.host/yap/docs.html#for-agents"
    },
    {
      "id": "code-signing",
      "name": "Code-signing and distribution (Developer-ID notarized DMG)",
      "description": "Ships as a Developer-ID-signed, notarized DMG with hardened runtime (not Mac App Store); Sparkle generate_appcast signs update deltas.",
      "requirements": "Developer ID Application cert on team 46KD7D8UJ4 + notary app-specific password + Sparkle private key (all pending).",
      "framework": "Xcode build settings / codesign / notarytool / Sparkle generate_appcast",
      "docAnchor": "#install",
      "family": "app",
      "docUrl": "https://cadence.host/yap/docs.html#install"
    },
    {
      "id": "privacy-on-device",
      "name": "Privacy: on-device guarantee",
      "description": "Camera frames, image bytes, screenshots, video, and recordings never leave the Mac; only typed prompts and deck text/structure are sent to Claude; Vision segmentation and Speech recognition run on-device; analytics are content-free and opt-out.",
      "requirements": "None (data-flow and API-flag properties of the app architecture).",
      "framework": "Vision (on-device VNGeneratePersonSegmentationRequest); Speech (requiresOnDeviceRecognition=true); n/a for data-flow properties",
      "docAnchor": "#privacy",
      "family": "app",
      "docUrl": "https://cadence.host/yap/docs.html#privacy"
    }
  ]
}
