{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "multimodal-vision-playground",
  "title": "Multimodal Vision Playground",
  "type": "registry:page",
  "files": [
    {
      "path": "packages/registry-angular/blocks/multimodal-vision-playground/multimodal-vision-playground.component.ts",
      "content": "// GPT-4o/Claude 3.5 Vision multimodal visual analysis studio with interactive bounding\n// box overlays, OCR entity extraction, tabular line item parsing, raw RFC-8259 JSON viewer,\n// and visual question answering (VQA). Port of the React block 1:1.\nimport {\n  ChangeDetectionStrategy,\n  Component,\n  DestroyRef,\n  Input,\n  OnDestroy,\n  OnInit,\n  computed,\n  inject,\n  signal,\n} from '@angular/core'\nimport {\n  Bot,\n  Braces,\n  Check,\n  CheckCircle2,\n  Copy,\n  Cpu,\n  Crosshair,\n  Eye,\n  FileCheck,\n  Info,\n  Layers,\n  LucideAngularModule,\n  RefreshCw,\n  Scan,\n  Send,\n  ShieldCheck,\n  Sparkles,\n  Tag,\n} from 'lucide-angular'\nimport { cn } from '@/lib/utils'\nimport { UiBadgeComponent } from '@/ui/badge/badge.component'\nimport { UiButtonComponent } from '@/ui/button/button.component'\nimport {\n  UiCardComponent,\n  UiCardContentComponent,\n  UiCardDescriptionComponent,\n  UiCardFooterComponent,\n  UiCardHeaderComponent,\n  UiCardTitleComponent,\n} from '@/ui/card/card.component'\nimport { UiInputComponent } from '@/ui/input/input.component'\nimport {\n  UiSelectComponent,\n  UiSelectContentComponent,\n  UiSelectItemComponent,\n  UiSelectTriggerComponent,\n  UiSelectValueComponent,\n} from '@/ui/select/select.component'\nimport { UiSeparatorComponent } from '@/ui/separator/separator.component'\nimport { UiSwitchComponent } from '@/ui/switch/switch.component'\nimport {\n  UiTableBodyComponent,\n  UiTableCellComponent,\n  UiTableComponent,\n  UiTableHeadComponent,\n  UiTableHeaderComponent,\n  UiTableRowComponent,\n} from '@/ui/table/table.component'\nimport {\n  UiTabsComponent,\n  UiTabsContentComponent,\n  UiTabsListComponent,\n  UiTabsTriggerComponent,\n} from '@/ui/tabs/tabs.component'\n\ninterface ModelOption {\n  id: string\n  name: string\n  provider: string\n  latency: string\n  tokenUsage: number\n  meanConfidence: number\n}\n\ninterface VisionTaskOption {\n  id: string\n  name: string\n  description: string\n}\n\ninterface BoundingBox {\n  id: string\n  label: string\n  text: string\n  category: string\n  confidence: number\n  coordinates: string\n  top: string\n  left: string\n  width: string\n  height: string\n  colorBorder: string\n  colorBg: string\n  colorBadge: string\n}\n\ninterface ExtractedEntity {\n  id: string\n  fieldName: string\n  extractedValue: string\n  confidence: number\n  boxId: string\n  dataType: string\n}\n\ninterface LineItem {\n  id: number\n  description: string\n  qty: number\n  unit: string\n  unitPrice: number\n  totalPrice: number\n  confidence: number\n}\n\ninterface VqaPreset {\n  id: string\n  question: string\n  answer: string\n  targetBoxId: string\n}\n\nconst models: ModelOption[] = [\n  {\n    id: 'claude-3-5-sonnet-vision',\n    name: 'Claude 3.5 Sonnet Vision',\n    provider: 'Anthropic',\n    latency: '642ms',\n    tokenUsage: 1420,\n    meanConfidence: 99.4,\n  },\n  {\n    id: 'gpt-4o-vision',\n    name: 'GPT-4o Vision',\n    provider: 'OpenAI',\n    latency: '584ms',\n    tokenUsage: 1380,\n    meanConfidence: 99.2,\n  },\n]\n\nconst visionTasks: VisionTaskOption[] = [\n  {\n    id: 'ocr-extraction',\n    name: 'Document OCR & Extraction',\n    description: 'High-precision layout bounding boxes, key-value extraction & structured RFC-8259 JSON parsing.',\n  },\n  {\n    id: 'object-detection',\n    name: 'Object & Bounding Box Detection',\n    description: 'Semantic region segmentation, visual entity localization, and coordinate spatial mapping.',\n  },\n  {\n    id: 'vqa',\n    name: 'Visual Question Answering',\n    description: 'Multi-step visual reasoning and spatial evidence grounding directly on the document image.',\n  },\n]\n\nconst boundingBoxes: BoundingBox[] = [\n  {\n    id: 'box-logo',\n    label: 'Vendor Logo',\n    text: 'APEX CLOUD SYSTEMS INC.',\n    category: 'vendor',\n    confidence: 99.9,\n    coordinates: '[42, 48, 110, 320]',\n    top: '4%',\n    left: '4%',\n    width: '42%',\n    height: '11%',\n    colorBorder: 'border-warning/70',\n    colorBg: 'bg-warning/10',\n    colorBadge: 'bg-warning/15 text-warning border-warning/30',\n  },\n  {\n    id: 'box-inv',\n    label: 'Invoice #',\n    text: 'INV-84920',\n    category: 'identifier',\n    confidence: 99.8,\n    coordinates: '[42, 680, 110, 1140]',\n    top: '4%',\n    left: '54%',\n    width: '42%',\n    height: '11%',\n    colorBorder: 'border-info/70',\n    colorBg: 'bg-info/10',\n    colorBadge: 'bg-info/15 text-info border-info/30',\n  },\n  {\n    id: 'box-billto',\n    label: 'Customer Entity',\n    text: 'Cyberdyne Systems Corp',\n    category: 'customer',\n    confidence: 99.3,\n    coordinates: '[145, 48, 250, 480]',\n    top: '17%',\n    left: '4%',\n    width: '46%',\n    height: '13%',\n    colorBorder: 'border-info/70',\n    colorBg: 'bg-info/10',\n    colorBadge: 'bg-info/15 text-info border-info/30',\n  },\n  {\n    id: 'box-due',\n    label: 'Due Date & Terms',\n    text: 'Due: 2026-09-01 (Net 10)',\n    category: 'date',\n    confidence: 99.4,\n    coordinates: '[145, 680, 250, 1140]',\n    top: '17%',\n    left: '54%',\n    width: '42%',\n    height: '13%',\n    colorBorder: 'border-destructive/70',\n    colorBg: 'bg-destructive/10',\n    colorBadge: 'bg-destructive/15 text-destructive border-destructive/30',\n  },\n  {\n    id: 'box-table',\n    label: 'Line Items Table',\n    text: '3 Items (GPU, NVMe, Bandwidth)',\n    category: 'table',\n    confidence: 99.5,\n    coordinates: '[320, 48, 710, 1140]',\n    top: '32%',\n    left: '4%',\n    width: '92%',\n    height: '39%',\n    colorBorder: 'border-chart-1/70',\n    colorBg: 'bg-chart-1/10',\n    colorBadge: 'bg-chart-1/15 text-chart-1 border-chart-1/30',\n  },\n  {\n    id: 'box-subtotal',\n    label: 'Subtotal & VAT',\n    text: 'Subtotal: $1,150.00 · VAT: $95.00',\n    category: 'financial',\n    confidence: 99.1,\n    coordinates: '[730, 620, 830, 1140]',\n    top: '73%',\n    left: '50%',\n    width: '46%',\n    height: '10%',\n    colorBorder: 'border-chart-2/70',\n    colorBg: 'bg-chart-2/10',\n    colorBadge: 'bg-chart-2/15 text-chart-2 border-chart-2/30',\n  },\n  {\n    id: 'box-total',\n    label: 'Total Amount $1,245.00',\n    text: 'Total Due: USD $1,245.00',\n    category: 'total',\n    confidence: 99.9,\n    coordinates: '[850, 620, 960, 1140]',\n    top: '85%',\n    left: '50%',\n    width: '46%',\n    height: '11%',\n    colorBorder: 'border-success/80',\n    colorBg: 'bg-success/15',\n    colorBadge: 'bg-success/20 text-success border-success/40 font-semibold',\n  },\n]\n\nconst extractedEntities: ExtractedEntity[] = [\n  {\n    id: 'ent-1',\n    fieldName: 'Invoice Number',\n    extractedValue: 'INV-84920',\n    confidence: 99.8,\n    boxId: 'box-inv',\n    dataType: 'string',\n  },\n  {\n    id: 'ent-2',\n    fieldName: 'Due Date',\n    extractedValue: '2026-09-01',\n    confidence: 99.4,\n    boxId: 'box-due',\n    dataType: 'date',\n  },\n  {\n    id: 'ent-3',\n    fieldName: 'Issue Date',\n    extractedValue: '2026-08-21',\n    confidence: 99.6,\n    boxId: 'box-due',\n    dataType: 'date',\n  },\n  {\n    id: 'ent-4',\n    fieldName: 'Vendor Name',\n    extractedValue: 'Apex Cloud Systems Inc.',\n    confidence: 99.9,\n    boxId: 'box-logo',\n    dataType: 'string',\n  },\n  {\n    id: 'ent-5',\n    fieldName: 'Vendor Tax ID',\n    extractedValue: 'US-EIN-94-8271923',\n    confidence: 98.9,\n    boxId: 'box-logo',\n    dataType: 'string',\n  },\n  {\n    id: 'ent-6',\n    fieldName: 'Customer Entity',\n    extractedValue: 'Cyberdyne Systems Corp',\n    confidence: 99.3,\n    boxId: 'box-billto',\n    dataType: 'string',\n  },\n  {\n    id: 'ent-7',\n    fieldName: 'Subtotal Amount',\n    extractedValue: '$1,150.00',\n    confidence: 99.7,\n    boxId: 'box-subtotal',\n    dataType: 'currency',\n  },\n  {\n    id: 'ent-8',\n    fieldName: 'VAT / Tax Rate (8.25%)',\n    extractedValue: '$95.00',\n    confidence: 99.1,\n    boxId: 'box-subtotal',\n    dataType: 'currency',\n  },\n  {\n    id: 'ent-9',\n    fieldName: 'Total Amount Due',\n    extractedValue: '$1,245.00',\n    confidence: 99.9,\n    boxId: 'box-total',\n    dataType: 'currency',\n  },\n]\n\nconst lineItems: LineItem[] = [\n  {\n    id: 1,\n    description: 'GPU Compute Instance (H100 NVLink 80GB)',\n    qty: 2,\n    unit: 'hours',\n    unitPrice: 450.0,\n    totalPrice: 900.0,\n    confidence: 99.5,\n  },\n  {\n    id: 2,\n    description: 'High-Throughput NVMe Block Storage 4TB',\n    qty: 1,\n    unit: 'month',\n    unitPrice: 150.0,\n    totalPrice: 150.0,\n    confidence: 99.2,\n  },\n  {\n    id: 3,\n    description: 'Dedicated Egress Bandwidth Allocation (10Gbps)',\n    qty: 1,\n    unit: 'allocation',\n    unitPrice: 100.0,\n    totalPrice: 100.0,\n    confidence: 98.8,\n  },\n]\n\nconst vqaPresets: VqaPreset[] = [\n  {\n    id: 'q-total',\n    question: 'What is the total amount due on this invoice?',\n    answer:\n      'The total amount due is $1,245.00 USD, parsed from the summary block with 99.9% confidence. It includes $1,150.00 subtotal plus $95.00 VAT (8.25%).',\n    targetBoxId: 'box-total',\n  },\n  {\n    id: 'q-due',\n    question: 'What is the payment due date and terms?',\n    answer:\n      'The invoice payment due date is September 01, 2026 under Net 10 Day terms (issued on August 21, 2026), extracted with 99.4% confidence.',\n    targetBoxId: 'box-due',\n  },\n  {\n    id: 'q-math',\n    question: 'Are all line item totals and taxes mathematically verified?',\n    answer:\n      'Yes. Calculated ($450 × 2) + $150 + $100 = $1,150.00 subtotal. VAT of 8.25% ($94.88 rounded to $95.00) brings grand total to $1,245.00 with 0 arithmetic errors.',\n    targetBoxId: 'box-table',\n  },\n  {\n    id: 'q-vendor',\n    question: 'What is the verified vendor entity and tax identifier?',\n    answer:\n      'Vendor is Apex Cloud Systems Inc. with verified Federal Tax EIN: US-EIN-94-8271923, extracted with 99.9% confidence.',\n    targetBoxId: 'box-logo',\n  },\n]\n\n@Component({\n  changeDetection: ChangeDetectionStrategy.Eager,\n  selector: 'ui-multimodal-vision-playground, [ui-multimodal-vision-playground]',\n  standalone: true,\n  // React renders the root <div> itself: the host stays out of layout and `class` goes to the root.\n  host: { '[attr.class]': '\"contents\"' },\n  imports: [\n    LucideAngularModule,\n    UiBadgeComponent,\n    UiButtonComponent,\n    UiCardComponent,\n    UiCardContentComponent,\n    UiCardDescriptionComponent,\n    UiCardFooterComponent,\n    UiCardHeaderComponent,\n    UiCardTitleComponent,\n    UiInputComponent,\n    UiSelectComponent,\n    UiSelectContentComponent,\n    UiSelectItemComponent,\n    UiSelectTriggerComponent,\n    UiSelectValueComponent,\n    UiSeparatorComponent,\n    UiSwitchComponent,\n    UiTableBodyComponent,\n    UiTableCellComponent,\n    UiTableComponent,\n    UiTableHeadComponent,\n    UiTableHeaderComponent,\n    UiTableRowComponent,\n    UiTabsComponent,\n    UiTabsContentComponent,\n    UiTabsListComponent,\n    UiTabsTriggerComponent,\n  ],\n  template: `\n    <div data-slot=\"multimodal-vision-playground\" [class]=\"rootClass\">\n      <!-- Header Controls Strip -->\n      <div\n        class=\"border-border bg-card flex flex-wrap items-center justify-between gap-4 rounded-lg border p-4 shadow-xs\"\n      >\n        <div class=\"flex min-w-0 flex-1 flex-wrap items-center gap-3\">\n          <div class=\"flex items-center gap-2.5\">\n            <div class=\"bg-primary/10 text-primary flex size-8 items-center justify-center rounded-md\">\n              <lucide-icon [img]=\"Scan\" class=\"size-4.5\" />\n            </div>\n            <div class=\"min-w-[12rem]\">\n              <h2 class=\"text-foreground text-sm font-semibold tracking-tight\">\n                Multimodal Vision & Document Intelligence\n              </h2>\n              <p class=\"text-muted-foreground text-xs\">Visual reasoning, layout parsing & entity extraction studio</p>\n            </div>\n          </div>\n\n          <ui-separator orientation=\"vertical\" class=\"hidden h-7 sm:block\" />\n\n          <!-- Model Selector -->\n          <div class=\"w-52\">\n            <ui-select [value]=\"selectedModel()\" (valueChange)=\"selectedModel.set($event)\">\n              <button ui-select-trigger class=\"h-9 w-full text-xs font-medium\" aria-label=\"Select Vision Model\">\n                <ui-select-value placeholder=\"Select Vision Model\" />\n              </button>\n              <ui-select-content>\n                @for (m of models; track m.id) {\n                  <ui-select-item [value]=\"m.id\">\n                    <div class=\"flex items-center gap-2\">\n                      <lucide-icon [img]=\"Sparkles\" class=\"text-primary size-3.5\" />\n                      <span>{{ m.name }}</span>\n                    </div>\n                  </ui-select-item>\n                }\n              </ui-select-content>\n            </ui-select>\n          </div>\n\n          <!-- Vision Task Selector -->\n          <div class=\"w-56\">\n            <ui-select [value]=\"selectedTask()\" (valueChange)=\"selectedTask.set($event)\">\n              <button ui-select-trigger class=\"h-9 w-full text-xs\" aria-label=\"Select Vision Task\">\n                <ui-select-value placeholder=\"Select Vision Task\" />\n              </button>\n              <ui-select-content>\n                @for (task of visionTasks; track task.id) {\n                  <ui-select-item [value]=\"task.id\">\n                    <span>{{ task.name }}</span>\n                  </ui-select-item>\n                }\n              </ui-select-content>\n            </ui-select>\n          </div>\n\n          <!-- Telemetry Pill -->\n          <div\n            class=\"border-border bg-muted/60 text-muted-foreground hidden items-center gap-2 rounded-full border px-3 py-1 font-mono text-xs lg:inline-flex\"\n          >\n            <lucide-icon [img]=\"Cpu\" class=\"text-primary size-3.5\" />\n            <span>{{ activeModel().latency }}</span>\n            <span>{{ activeModel().tokenUsage }} tokens</span>\n            <span class=\"text-success font-medium\">{{ activeModel().meanConfidence }}% conf</span>\n          </div>\n        </div>\n\n        <div class=\"flex items-center gap-2\">\n          <!-- Run Vision Model Primary Button -->\n          <button\n            ui-button\n            size=\"sm\"\n            class=\"bg-primary text-primary-foreground hover:bg-primary/90 h-9 gap-1.5 text-xs font-medium shadow-xs\"\n            [disabled]=\"isAnalyzing()\"\n            (click)=\"runVisionAnalysis()\"\n          >\n            @if (isAnalyzing()) {\n              <lucide-icon [img]=\"RefreshCw\" class=\"size-3.5 animate-spin\" />\n            } @else {\n              <lucide-icon [img]=\"Eye\" class=\"size-3.5\" />\n            }\n            <span>{{ isAnalyzing() ? 'Analyzing Image...' : 'Run Vision Model' }}</span>\n            <kbd\n              class=\"bg-primary-foreground/20 hidden items-center rounded px-1 py-0.5 font-mono text-xs sm:inline-flex\"\n            >\n              ⌘↵\n            </kbd>\n          </button>\n        </div>\n      </div>\n\n      <!-- 2-Column Vision Studio (50% / 50%) -->\n      <div class=\"grid grid-cols-1 gap-6 lg:grid-cols-12\">\n        <!-- Left Column: Visual Document Preview & Interactive Bounding Box Canvas (50%) -->\n        <div class=\"space-y-4 lg:col-span-6\">\n          <div ui-card class=\"border-border bg-card overflow-hidden shadow-xs\">\n            <div ui-card-header class=\"pb-3\">\n              <div class=\"flex flex-wrap items-center justify-between gap-2\">\n                <div class=\"flex items-center gap-2\">\n                  <div class=\"bg-primary/10 text-primary flex size-7 items-center justify-center rounded-md\">\n                    <lucide-icon [img]=\"Layers\" class=\"size-4\" />\n                  </div>\n                  <div>\n                    <h3 ui-card-title class=\"text-sm font-semibold\">Visual Document Canvas</h3>\n                    <p ui-card-description class=\"text-xs\">Spatial bounding polygon detection & OCR anchor layer</p>\n                  </div>\n                </div>\n                <span ui-badge variant=\"outline\" class=\"text-muted-foreground font-mono text-xs\">\n                  1240 × 1754 · 300 DPI\n                </span>\n              </div>\n\n              <!-- Bounding Box Layer Toggles -->\n              <div\n                class=\"border-border/80 bg-muted/40 mt-3 flex flex-wrap items-center justify-between gap-3 rounded-md border px-3 py-2 text-xs\"\n              >\n                <span class=\"text-muted-foreground flex items-center gap-1.5 font-medium\">\n                  <lucide-icon [img]=\"Crosshair\" class=\"text-primary size-3.5\" />\n                  <span>Layer Overlays:</span>\n                </span>\n\n                <div class=\"flex flex-wrap items-center gap-4\">\n                  <!-- Toggle: OCR Text -->\n                  <div class=\"flex items-center gap-1.5\">\n                    <ui-switch\n                      id=\"angular-show-ocr\"\n                      [checked]=\"showOcrText()\"\n                      (checkedChange)=\"showOcrText.set($event)\"\n                    />\n                    <label for=\"angular-show-ocr\" class=\"text-foreground cursor-pointer text-xs font-medium\">\n                      OCR Text\n                    </label>\n                  </div>\n\n                  <!-- Toggle: Confidence Scores -->\n                  <div class=\"flex items-center gap-1.5\">\n                    <ui-switch\n                      id=\"angular-show-conf\"\n                      [checked]=\"showConfidence()\"\n                      (checkedChange)=\"showConfidence.set($event)\"\n                    />\n                    <label for=\"angular-show-conf\" class=\"text-foreground cursor-pointer text-xs font-medium\">\n                      Confidence\n                    </label>\n                  </div>\n\n                  <!-- Toggle: Coordinates -->\n                  <div class=\"flex items-center gap-1.5\">\n                    <ui-switch\n                      id=\"angular-show-coords\"\n                      [checked]=\"showCoordinates()\"\n                      (checkedChange)=\"showCoordinates.set($event)\"\n                    />\n                    <label for=\"angular-show-coords\" class=\"text-foreground cursor-pointer text-xs font-medium\">\n                      Coordinates\n                    </label>\n                  </div>\n                </div>\n              </div>\n            </div>\n\n            <div ui-card-content class=\"pt-0\">\n              <!-- Document Viewport Container -->\n              <div\n                class=\"border-border bg-muted/30 relative w-full overflow-hidden rounded-lg border p-3 select-none sm:p-4\"\n              >\n                <!-- The Rendered High-Fidelity Document Sheet -->\n                <div\n                  class=\"border-border/90 bg-card text-card-foreground relative mx-auto flex aspect-[1/1.36] min-h-[420px] w-full max-w-[560px] max-w-full flex-col justify-between overflow-auto rounded-md border p-4 shadow-sm sm:p-6\"\n                >\n                  <!-- Watermark Background Texture -->\n                  <div\n                    class=\"pointer-events-none absolute inset-0 bg-[radial-gradient(#000_1px,transparent_1px)] [background-size:16px_16px] opacity-[0.03] dark:opacity-[0.05]\"\n                  ></div>\n\n                  <!-- Document Header Section -->\n                  <div class=\"border-border/70 relative z-10 flex items-start justify-between border-b pb-4\">\n                    <!-- Vendor Brand Info -->\n                    <div class=\"space-y-1\">\n                      <div class=\"flex items-center gap-2\">\n                        <div\n                          class=\"bg-primary/20 text-primary flex size-6 items-center justify-center rounded text-xs font-bold\"\n                        >\n                          ▲\n                        </div>\n                        <span class=\"text-foreground text-xs font-bold tracking-wider uppercase\">\n                          Apex Cloud Systems\n                        </span>\n                      </div>\n                      <p class=\"text-muted-foreground text-xs\">500 Howard St, Suite 400</p>\n                      <p class=\"text-muted-foreground text-xs\">San Francisco, CA 94105</p>\n                      <p class=\"text-muted-foreground font-mono text-xs\">EIN: US-94-8271923</p>\n                    </div>\n\n                    <!-- Invoice Meta -->\n                    <div class=\"space-y-1 text-right\">\n                      <div class=\"bg-primary/10 text-primary inline-block rounded px-2 py-0.5 text-xs font-bold\">\n                        TAX INVOICE\n                      </div>\n                      <p class=\"text-foreground font-mono text-xs font-semibold\">INV-84920</p>\n                      <p class=\"text-muted-foreground text-xs\">Issued: Aug 21, 2026</p>\n                      <p class=\"text-destructive text-xs font-medium\">Due: Sep 01, 2026</p>\n                    </div>\n                  </div>\n\n                  <!-- Bill To & Account Details -->\n                  <div\n                    class=\"border-border/60 relative z-10 grid grid-cols-1 gap-3 border-b py-3 text-xs sm:grid-cols-2\"\n                  >\n                    <div>\n                      <span class=\"text-muted-foreground text-xs font-semibold uppercase\">Billed To:</span>\n                      <p class=\"text-foreground mt-0.5 font-semibold\">Cyberdyne Systems Corp</p>\n                      <p class=\"text-muted-foreground\">100 Cyberdyne Blvd, Suite 800</p>\n                      <p class=\"text-muted-foreground\">San Francisco, CA 94107</p>\n                    </div>\n                    <div class=\"text-right\">\n                      <span class=\"text-muted-foreground text-xs font-semibold uppercase\">Payment Terms:</span>\n                      <p class=\"text-foreground mt-0.5 font-medium\">Net 10 Days (Wire Transfer)</p>\n                      <p class=\"text-muted-foreground font-mono\">PO Ref: PO-2026-90412</p>\n                      <p class=\"text-muted-foreground\">Currency: USD ($)</p>\n                    </div>\n                  </div>\n\n                  <!-- Document Line Items Table Mockup -->\n                  <div class=\"relative z-10 flex-1 py-3\">\n                    <div class=\"w-full text-xs\">\n                      <div\n                        class=\"border-border/80 text-muted-foreground grid grid-cols-12 gap-1 border-b pb-1.5 font-semibold\"\n                      >\n                        <div class=\"col-span-6\">Item & Description</div>\n                        <div class=\"col-span-2 text-center\">Qty</div>\n                        <div class=\"col-span-2 text-right\">Rate</div>\n                        <div class=\"col-span-2 text-right\">Amount</div>\n                      </div>\n                      <div class=\"divide-border/40 divide-y text-xs\">\n                        <div class=\"grid grid-cols-12 items-center gap-1 py-2\">\n                          <div class=\"col-span-6\">\n                            <p class=\"text-foreground font-medium\">GPU Instance (H100 NVLink 80GB)</p>\n                            <p class=\"text-muted-foreground text-xs\">Dedicated high-speed cluster</p>\n                          </div>\n                          <div class=\"col-span-2 text-center font-mono\">2 hrs</div>\n                          <div class=\"col-span-2 text-right font-mono\">$450.00</div>\n                          <div class=\"col-span-2 text-right font-mono font-medium\">$900.00</div>\n                        </div>\n                        <div class=\"grid grid-cols-12 items-center gap-1 py-2\">\n                          <div class=\"col-span-6\">\n                            <p class=\"text-foreground font-medium\">High-Throughput NVMe Storage</p>\n                            <p class=\"text-muted-foreground text-xs\">4TB Tier-1 SSD Volume</p>\n                          </div>\n                          <div class=\"col-span-2 text-center font-mono\">1 mo</div>\n                          <div class=\"col-span-2 text-right font-mono\">$150.00</div>\n                          <div class=\"col-span-2 text-right font-mono font-medium\">$150.00</div>\n                        </div>\n                        <div class=\"grid grid-cols-12 items-center gap-1 py-2\">\n                          <div class=\"col-span-6\">\n                            <p class=\"text-foreground font-medium\">Dedicated Egress Bandwidth</p>\n                            <p class=\"text-muted-foreground text-xs\">10Gbps Burstable Pipe</p>\n                          </div>\n                          <div class=\"col-span-2 text-center font-mono\">1 alloc</div>\n                          <div class=\"col-span-2 text-right font-mono\">$100.00</div>\n                          <div class=\"col-span-2 text-right font-mono font-medium\">$100.00</div>\n                        </div>\n                      </div>\n                    </div>\n                  </div>\n\n                  <!-- Financial Calculation Bottom Strip -->\n                  <div class=\"border-border/80 relative z-10 flex items-end justify-between border-t pt-3\">\n                    <div class=\"space-y-1\">\n                      <div class=\"text-muted-foreground flex items-center gap-1.5 text-xs\">\n                        <lucide-icon [img]=\"ShieldCheck\" class=\"text-success size-3.5\" />\n                        <span>Cryptographic Checksum: 0x8F9A2B</span>\n                      </div>\n                      <p class=\"text-muted-foreground text-xs\">Wire: Wells Fargo Routing 121000358</p>\n                    </div>\n                    <div class=\"w-48 space-y-1 text-right text-xs\">\n                      <div class=\"text-muted-foreground flex justify-between\">\n                        <span>Subtotal:</span>\n                        <span class=\"text-foreground font-mono font-medium\">$1,150.00</span>\n                      </div>\n                      <div class=\"text-muted-foreground flex justify-between\">\n                        <span>VAT (8.25%):</span>\n                        <span class=\"text-foreground font-mono font-medium\">$95.00</span>\n                      </div>\n                      <div\n                        class=\"border-border text-foreground flex justify-between border-t pt-1 text-xs font-bold sm:text-sm\"\n                      >\n                        <span>Total Due:</span>\n                        <span class=\"text-success font-mono\">$1,245.00</span>\n                      </div>\n                    </div>\n                  </div>\n\n                  <!-- INTERACTIVE BOUNDING BOX OVERLAYS LAYER -->\n                  <div class=\"pointer-events-auto absolute inset-0 z-20\">\n                    @for (box of boundingBoxes; track box.id) {\n                      <div\n                        tabindex=\"0\"\n                        role=\"button\"\n                        [attr.aria-label]=\"box.label + ': ' + box.text\"\n                        [style.top]=\"box.top\"\n                        [style.left]=\"box.left\"\n                        [style.width]=\"box.width\"\n                        [style.height]=\"box.height\"\n                        [class]=\"boxClass(box)\"\n                        (mouseenter)=\"activeBoxId.set(box.id)\"\n                        (mouseleave)=\"activeBoxId.set(null)\"\n                        (click)=\"activeBoxId.set(box.id)\"\n                        (focus)=\"activeBoxId.set(box.id)\"\n                        (blur)=\"activeBoxId.set(null)\"\n                      >\n                        <!-- Corner Anchor Dots -->\n                        <span\n                          class=\"bg-primary border-background absolute -top-1 -left-1 size-2 rounded-full border\"\n                        ></span>\n                        <span\n                          class=\"bg-primary border-background absolute -top-1 -right-1 size-2 rounded-full border\"\n                        ></span>\n                        <span\n                          class=\"bg-primary border-background absolute -bottom-1 -left-1 size-2 rounded-full border\"\n                        ></span>\n                        <span\n                          class=\"bg-primary border-background absolute -right-1 -bottom-1 size-2 rounded-full border\"\n                        ></span>\n\n                        <!-- Floating Metadata Tag on Box -->\n                        <div [class]=\"boxBadgeClass(box)\">\n                          <lucide-icon [img]=\"Tag\" class=\"size-3\" />\n                          <span class=\"font-semibold\">{{ box.label }}</span>\n                          @if (showConfidence()) {\n                            <span class=\"font-mono text-xs opacity-90\"> {{ box.confidence }}% </span>\n                          }\n                        </div>\n\n                        <!-- OCR Text Preview Ribbon (Bottom) -->\n                        @if (showOcrText()) {\n                          <div\n                            class=\"bg-foreground/90 text-background py-0.2 absolute -bottom-5 left-0 max-w-full truncate rounded px-1.5 font-mono text-xs shadow-xs\"\n                          >\n                            {{ box.text }}\n                          </div>\n                        }\n\n                        <!-- Coordinates Pill -->\n                        @if (showCoordinates() && activeBoxId() === box.id) {\n                          <div\n                            class=\"bg-background/95 border-border text-muted-foreground absolute top-1 right-1 rounded border px-1 py-0.5 font-mono text-xs shadow-xs\"\n                          >\n                            {{ box.coordinates }}\n                          </div>\n                        }\n                      </div>\n                    }\n                  </div>\n                </div>\n              </div>\n            </div>\n\n            <div\n              ui-card-footer\n              class=\"border-border text-muted-foreground flex items-center justify-between border-t pt-3 text-xs\"\n            >\n              <span class=\"flex items-center gap-1.5\">\n                <lucide-icon [img]=\"Info\" class=\"text-primary size-3.5\" />\n                <span>Hover or click any bounding box to isolate extracted entities</span>\n              </span>\n              @if (activeBox(); as box) {\n                <span class=\"text-foreground font-mono font-medium\">\n                  Active: {{ box.label }} ({{ box.confidence }}%)\n                </span>\n              } @else {\n                <span class=\"font-mono text-xs\">7 Bounding Anchors</span>\n              }\n            </div>\n          </div>\n        </div>\n\n        <!-- Right Column: Structured Output & Analysis Studio (50%) -->\n        <div class=\"space-y-4 lg:col-span-6\">\n          <div ui-card class=\"border-border bg-card shadow-xs\">\n            <div ui-card-header class=\"pb-3\">\n              <div class=\"flex flex-wrap items-center justify-between gap-3\">\n                <div class=\"flex items-center gap-2\">\n                  <div class=\"bg-primary/10 text-primary flex size-7 items-center justify-center rounded-md\">\n                    <lucide-icon [img]=\"Bot\" class=\"size-4\" />\n                  </div>\n                  <div>\n                    <h3 ui-card-title class=\"text-sm font-semibold\">Structured Output & Visual Analysis</h3>\n                    <p ui-card-description class=\"text-xs\">\n                      Multi-modal entity parsing, JSON schemas & visual reasoning\n                    </p>\n                  </div>\n                </div>\n\n                <!-- Status Badge -->\n                <span ui-badge variant=\"outline\" class=\"border-success/30 bg-success/10 text-success font-mono text-xs\">\n                  <lucide-icon [img]=\"CheckCircle2\" class=\"text-success mr-1 size-3\" />\n                  Validated · 0 Errors\n                </span>\n              </div>\n            </div>\n\n            <div ui-card-content class=\"space-y-4 pt-0\">\n              <ui-tabs [value]=\"activeTab()\" (valueChange)=\"activeTab.set($event)\" class=\"w-full\">\n                <ui-tabs-list class=\"grid h-9 w-full grid-cols-4\">\n                  <button ui-tabs-trigger value=\"entities\" class=\"text-xs\">Key-Values</button>\n                  <button ui-tabs-trigger value=\"table\" class=\"text-xs\">Line Items</button>\n                  <button ui-tabs-trigger value=\"json\" class=\"text-xs\">Raw JSON</button>\n                  <button ui-tabs-trigger value=\"reasoning\" class=\"text-xs\">Reasoning</button>\n                </ui-tabs-list>\n\n                <!-- TAB 1: Extracted Key-Value Data Table -->\n                <div ui-tabs-content value=\"entities\" class=\"mt-4 space-y-3\">\n                  <div class=\"border-border overflow-hidden rounded-md border\">\n                    <div class=\"overflow-x-auto\">\n                      <table ui-table>\n                        <thead ui-table-header>\n                          <tr ui-table-row class=\"bg-muted/50 text-xs\">\n                            <th ui-table-head class=\"font-semibold\">Field Name</th>\n                            <th ui-table-head class=\"font-semibold\">Extracted Value</th>\n                            <th ui-table-head class=\"text-center font-semibold\">Confidence</th>\n                            <th ui-table-head class=\"text-right font-semibold\">Action</th>\n                          </tr>\n                        </thead>\n                        <tbody ui-table-body class=\"text-xs\">\n                          @for (field of extractedEntities; track field.id) {\n                            <tr\n                              ui-table-row\n                              [class]=\"entityRowClass(field)\"\n                              (mouseenter)=\"activeBoxId.set(field.boxId)\"\n                              (mouseleave)=\"activeBoxId.set(null)\"\n                            >\n                              <td ui-table-cell class=\"text-foreground py-2.5 font-medium\">\n                                <div class=\"flex items-center gap-1.5\">\n                                  <span [class]=\"entityDotClass(field)\"></span>\n                                  <span>{{ field.fieldName }}</span>\n                                </div>\n                              </td>\n                              <td ui-table-cell class=\"text-foreground py-2.5 font-mono\">\n                                {{ field.extractedValue }}\n                              </td>\n                              <td ui-table-cell class=\"py-2.5 text-center\">\n                                <span ui-badge variant=\"secondary\" [class]=\"entityBadgeConfidenceClass(field)\">\n                                  {{ field.confidence }}%\n                                </span>\n                              </td>\n                              <td ui-table-cell class=\"py-2.5 text-right\">\n                                <button\n                                  ui-button\n                                  variant=\"ghost\"\n                                  size=\"sm\"\n                                  class=\"text-muted-foreground hover:text-foreground h-7 w-7 p-0\"\n                                  aria-label=\"Copy field value\"\n                                  (click)=\"handleCopyEntity(field)\"\n                                >\n                                  @if (copiedFieldId() === field.id) {\n                                    <lucide-icon [img]=\"Check\" class=\"text-success size-3.5\" />\n                                  } @else {\n                                    <lucide-icon [img]=\"Copy\" class=\"size-3.5\" />\n                                  }\n                                </button>\n                              </td>\n                            </tr>\n                          }\n                        </tbody>\n                      </table>\n                    </div>\n                  </div>\n                </div>\n\n                <!-- TAB 2: Line Items Tabular Structure -->\n                <div ui-tabs-content value=\"table\" class=\"mt-4 space-y-3\">\n                  <div class=\"border-border overflow-hidden rounded-md border\">\n                    <div class=\"overflow-x-auto\">\n                      <table ui-table>\n                        <thead ui-table-header>\n                          <tr ui-table-row class=\"bg-muted/50 text-xs\">\n                            <th ui-table-head class=\"w-8 font-semibold\">#</th>\n                            <th ui-table-head class=\"font-semibold\">Description</th>\n                            <th ui-table-head class=\"text-center font-semibold\">Qty</th>\n                            <th ui-table-head class=\"text-right font-semibold\">Unit Price</th>\n                            <th ui-table-head class=\"text-right font-semibold\">Total</th>\n                          </tr>\n                        </thead>\n                        <tbody ui-table-body class=\"text-xs\">\n                          @for (item of lineItems; track item.id) {\n                            <tr\n                              ui-table-row\n                              class=\"hover:bg-muted/40 cursor-pointer\"\n                              (mouseenter)=\"activeBoxId.set('box-table')\"\n                              (mouseleave)=\"activeBoxId.set(null)\"\n                            >\n                              <td ui-table-cell class=\"text-muted-foreground py-2.5 font-mono\">{{ item.id }}</td>\n                              <td ui-table-cell class=\"text-foreground py-2.5 font-medium\">\n                                <p>{{ item.description }}</p>\n                                <span class=\"text-muted-foreground font-mono text-xs\">\n                                  Confidence: {{ item.confidence }}%\n                                </span>\n                              </td>\n                              <td ui-table-cell class=\"py-2.5 text-center font-mono\">{{ item.qty }} {{ item.unit }}</td>\n                              <td ui-table-cell class=\"py-2.5 text-right font-mono\">\n                                {{ '$' + item.unitPrice.toFixed(2) }}\n                              </td>\n                              <td ui-table-cell class=\"text-foreground py-2.5 text-right font-mono font-semibold\">\n                                {{ '$' + item.totalPrice.toFixed(2) }}\n                              </td>\n                            </tr>\n                          }\n                        </tbody>\n                      </table>\n                    </div>\n                  </div>\n                </div>\n\n                <!-- TAB 3: Raw Structured JSON Schema -->\n                <div ui-tabs-content value=\"json\" class=\"mt-4 space-y-3\">\n                  <div\n                    class=\"bg-muted/60 border-border flex items-center justify-between rounded-t-md border border-b-0 px-3 py-2 text-xs\"\n                  >\n                    <span class=\"text-muted-foreground flex items-center gap-1.5 font-mono\">\n                      <lucide-icon [img]=\"Braces\" class=\"text-primary size-3.5\" />\n                      <span>rfc8259-invoice-schema.json</span>\n                    </span>\n                    <button\n                      ui-button\n                      variant=\"ghost\"\n                      size=\"sm\"\n                      class=\"text-muted-foreground hover:text-foreground h-7 px-2 text-xs\"\n                      (click)=\"handleCopyJson()\"\n                    >\n                      @if (isCopied()) {\n                        <lucide-icon [img]=\"Check\" class=\"text-success mr-1 size-3.5\" />\n                      } @else {\n                        <lucide-icon [img]=\"Copy\" class=\"mr-1 size-3.5\" />\n                      }\n                      <span>{{ isCopied() ? 'Copied JSON!' : 'Copy Schema' }}</span>\n                    </button>\n                  </div>\n                  <pre\n                    class=\"border-border bg-muted/30 text-foreground max-h-[320px] overflow-x-auto overflow-y-auto rounded-b-md border p-3.5 font-mono text-xs leading-relaxed\"\n                  ><code>{{ rawJsonSchema() }}</code></pre>\n                </div>\n\n                <!-- TAB 4: Visual Reasoning & Question Answering -->\n                <div ui-tabs-content value=\"reasoning\" class=\"mt-4 space-y-4\">\n                  <!-- Prose Summary Box -->\n                  <div class=\"border-border/90 bg-muted/30 space-y-2.5 rounded-lg border p-3.5 text-xs\">\n                    <div class=\"flex items-center gap-2\">\n                      <lucide-icon [img]=\"Sparkles\" class=\"text-primary size-4\" />\n                      <h4 class=\"text-foreground text-xs font-semibold\">Multimodal Reasoning Summary</h4>\n                    </div>\n                    <p class=\"text-muted-foreground leading-relaxed\">\n                      Identified standard commercial invoice with 3 line items. VAT calculation validated with 0\n                      arithmetic errors. Vendor entity verified against active directory with clean 0.0° skew angle and\n                      300 DPI clarity.\n                    </p>\n                    <div class=\"border-border/60 grid grid-cols-1 gap-2 border-t pt-1 sm:grid-cols-3\">\n                      <div class=\"border-border/70 bg-card rounded border p-2\">\n                        <span class=\"text-muted-foreground text-xs\">Arithmetic Check</span>\n                        <p class=\"text-success mt-0.5 font-semibold\">100% Match</p>\n                      </div>\n                      <div class=\"border-border/70 bg-card rounded border p-2\">\n                        <span class=\"text-muted-foreground text-xs\">Layout Skew</span>\n                        <p class=\"text-foreground mt-0.5 font-mono font-medium\">0.0° (Aligned)</p>\n                      </div>\n                      <div class=\"border-border/70 bg-card rounded border p-2\">\n                        <span class=\"text-muted-foreground text-xs\">Optical Clarity</span>\n                        <p class=\"text-foreground mt-0.5 font-semibold\">300 DPI High</p>\n                      </div>\n                    </div>\n                  </div>\n\n                  <!-- Interactive VQA Console -->\n                  <div class=\"space-y-2.5\">\n                    <label class=\"text-foreground flex items-center justify-between text-xs font-semibold\">\n                      <span>Visual Question Answering (VQA):</span>\n                      <span class=\"text-muted-foreground text-xs font-normal\">Click a sample prompt</span>\n                    </label>\n\n                    <!-- Preset Question Chips -->\n                    <div class=\"flex flex-wrap gap-1.5\">\n                      @for (preset of vqaPresets; track preset.id) {\n                        <button\n                          type=\"button\"\n                          class=\"border-border/80 bg-muted/40 hover:border-primary/60 hover:bg-primary/5 rounded-md border px-2 py-1 text-left text-xs transition-colors\"\n                          (click)=\"selectVqaPreset(preset)\"\n                        >\n                          {{ preset.question }}\n                        </button>\n                      }\n                    </div>\n\n                    <!-- Query Input Box -->\n                    <div class=\"flex items-center gap-2\">\n                      <ui-input\n                        [value]=\"vqaInput()\"\n                        (valueChange)=\"vqaInput.set($event)\"\n                        placeholder=\"Ask a visual question about this document...\"\n                        class=\"h-9 text-xs\"\n                        (keydown.enter)=\"submitCustomVqa()\"\n                      />\n                      <button\n                        ui-button\n                        size=\"sm\"\n                        class=\"h-9 gap-1.5 px-3 text-xs font-medium\"\n                        [disabled]=\"isAnsweringVqa()\"\n                        (click)=\"submitCustomVqa()\"\n                      >\n                        @if (isAnsweringVqa()) {\n                          <lucide-icon [img]=\"RefreshCw\" class=\"size-3.5 animate-spin\" />\n                        } @else {\n                          <lucide-icon [img]=\"Send\" class=\"size-3.5\" />\n                        }\n                        <span>Ask</span>\n                      </button>\n                    </div>\n\n                    <!-- Reasoning Answer Box -->\n                    <div\n                      class=\"border-primary/20 bg-primary/5 text-foreground flex items-start gap-2.5 rounded-md border p-3 text-xs leading-relaxed\"\n                    >\n                      <lucide-icon [img]=\"Bot\" class=\"text-primary mt-0.5 size-4 shrink-0\" />\n                      <div>\n                        <p class=\"text-foreground font-medium\">{{ currentVqaAnswer() }}</p>\n                      </div>\n                    </div>\n                  </div>\n                </div>\n              </ui-tabs>\n            </div>\n\n            <div\n              ui-card-footer\n              class=\"border-border text-muted-foreground flex items-center justify-between border-t pt-3 text-xs\"\n            >\n              <span class=\"flex items-center gap-1.5\">\n                <lucide-icon [img]=\"FileCheck\" class=\"text-success size-3.5\" />\n                <span>Extracted 10 Entities · 3 Line Items</span>\n              </span>\n              <span class=\"font-mono\">Throughput: ~412 tok/s</span>\n            </div>\n          </div>\n        </div>\n      </div>\n    </div>\n  `,\n})\nexport class UiMultimodalVisionPlaygroundComponent implements OnInit, OnDestroy {\n  protected readonly Bot = Bot\n  protected readonly Braces = Braces\n  protected readonly Check = Check\n  protected readonly CheckCircle2 = CheckCircle2\n  protected readonly Copy = Copy\n  protected readonly Cpu = Cpu\n  protected readonly Crosshair = Crosshair\n  protected readonly Eye = Eye\n  protected readonly FileCheck = FileCheck\n  protected readonly Info = Info\n  protected readonly Layers = Layers\n  protected readonly RefreshCw = RefreshCw\n  protected readonly Scan = Scan\n  protected readonly Send = Send\n  protected readonly ShieldCheck = ShieldCheck\n  protected readonly Sparkles = Sparkles\n  protected readonly Tag = Tag\n\n  protected readonly models = models\n  protected readonly visionTasks = visionTasks\n  protected readonly boundingBoxes = boundingBoxes\n  protected readonly extractedEntities = extractedEntities\n  protected readonly lineItems = lineItems\n  protected readonly vqaPresets = vqaPresets\n\n  @Input('class') className?: string\n\n  readonly selectedModel = signal('claude-3-5-sonnet-vision')\n  readonly selectedTask = signal('ocr-extraction')\n  readonly isAnalyzing = signal(false)\n  readonly activeBoxId = signal<string | null>(null)\n  readonly activeTab = signal('entities')\n  readonly isCopied = signal(false)\n  readonly copiedFieldId = signal<string | null>(null)\n\n  readonly showOcrText = signal(true)\n  readonly showConfidence = signal(true)\n  readonly showCoordinates = signal(true)\n\n  readonly vqaInput = signal(vqaPresets[0]!.question)\n  readonly currentVqaAnswer = signal(vqaPresets[0]!.answer)\n  readonly isAnsweringVqa = signal(false)\n\n  private timers = new Set<ReturnType<typeof setTimeout>>()\n  private readonly keyHandler = (e: KeyboardEvent) => {\n    if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') {\n      e.preventDefault()\n      this.runVisionAnalysis()\n    }\n  }\n\n  constructor() {\n    inject(DestroyRef).onDestroy(() => {\n      for (const t of this.timers) clearTimeout(t)\n      this.timers.clear()\n    })\n  }\n\n  ngOnInit(): void {\n    window.addEventListener('keydown', this.keyHandler)\n  }\n\n  ngOnDestroy(): void {\n    window.removeEventListener('keydown', this.keyHandler)\n  }\n\n  readonly activeModel = computed(() => this.models.find((m) => m.id === this.selectedModel()) ?? this.models[0]!)\n\n  readonly activeBox = computed(() => {\n    const id = this.activeBoxId()\n    if (!id) return null\n    return this.boundingBoxes.find((b) => b.id === id) ?? null\n  })\n\n  readonly rawJsonSchema = computed(() => {\n    const model = this.selectedModel()\n    const meanConfidence = this.activeModel().meanConfidence / 100\n    return JSON.stringify(\n      {\n        $schema: 'https://json-schema.org/draft/2020-12/schema',\n        document_type: 'commercial_invoice',\n        document_id: 'doc_vision_9824f8a1',\n        model,\n        mean_confidence: meanConfidence,\n        metadata: {\n          dimensions: { width: 1240, height: 1754, dpi: 300 },\n          orientation_degrees: 0,\n          language: 'en-US',\n          pages_analyzed: 1,\n          processed_at: new Date().toISOString(),\n        },\n        extracted_fields: {\n          invoice_number: { value: 'INV-84920', confidence: 0.998, bbox: [42, 680, 110, 1140] },\n          issue_date: { value: '2026-08-21', confidence: 0.996, bbox: [145, 680, 250, 1140] },\n          due_date: { value: '2026-09-01', confidence: 0.994, bbox: [145, 680, 250, 1140] },\n          vendor: {\n            name: 'Apex Cloud Systems Inc.',\n            tax_id: 'US-EIN-94-8271923',\n            address: '500 Howard St, Suite 400, San Francisco, CA 94105',\n            confidence: 0.999,\n          },\n          customer: { name: 'Cyberdyne Systems Corp', account_id: 'ACC-90412', confidence: 0.993 },\n          currency: 'USD',\n          subtotal: 1150.0,\n          tax_amount: 95.0,\n          tax_rate_pct: 8.25,\n          total_amount: 1245.0,\n        },\n        line_items: this.lineItems.map((item) => ({\n          item_id: item.id,\n          description: item.description,\n          quantity: item.qty,\n          unit: item.unit,\n          unit_price: item.unitPrice,\n          total_price: item.totalPrice,\n          confidence: item.confidence / 100,\n        })),\n        validation_rules: {\n          math_checksum_passed: true,\n          tax_arithmetic_valid: true,\n          vendor_sanctions_screened: true,\n          flagged_anomalies_count: 0,\n        },\n      },\n      null,\n      2,\n    )\n  })\n\n  get rootClass(): string {\n    return cn('w-full space-y-6', this.className)\n  }\n\n  boxClass(box: BoundingBox): string {\n    return cn(\n      'focus-visible:ring-primary absolute cursor-pointer rounded border-2 transition-colors duration-150 focus-visible:ring-2 focus-visible:outline-hidden',\n      box.colorBorder,\n      box.colorBg,\n      this.activeBoxId() === box.id\n        ? 'ring-primary z-30 scale-[1.01] opacity-100 shadow-md ring-2'\n        : 'hover:ring-primary/60 opacity-85 hover:opacity-100 hover:ring-1',\n    )\n  }\n\n  boxBadgeClass(box: BoundingBox): string {\n    return cn(\n      'absolute -top-5.5 left-0 flex items-center gap-1 rounded px-1.5 py-0.5 text-xs whitespace-nowrap shadow-xs transition-opacity',\n      box.colorBadge,\n    )\n  }\n\n  entityRowClass(field: ExtractedEntity): string {\n    return cn(\n      'cursor-pointer transition-colors',\n      this.activeBoxId() === field.boxId ? 'bg-primary/10 font-medium' : 'hover:bg-muted/40',\n    )\n  }\n\n  entityDotClass(field: ExtractedEntity): string {\n    return cn('size-1.5 rounded-full', this.activeBoxId() === field.boxId ? 'bg-primary' : 'bg-muted-foreground/40')\n  }\n\n  entityBadgeConfidenceClass(field: ExtractedEntity): string {\n    return cn(\n      'font-mono text-xs',\n      field.confidence >= 99.5\n        ? 'border-success/20 bg-success/10 text-success border'\n        : 'border-info/20 bg-info/10 text-info border',\n    )\n  }\n\n  private later(fn: () => void, ms: number): void {\n    const t = setTimeout(() => {\n      this.timers.delete(t)\n      fn()\n    }, ms)\n    this.timers.add(t)\n  }\n\n  runVisionAnalysis(): void {\n    if (this.isAnalyzing()) return\n    this.isAnalyzing.set(true)\n    this.activeBoxId.set(null)\n    this.later(() => this.isAnalyzing.set(false), 650)\n  }\n\n  handleCopyJson(): void {\n    void navigator.clipboard?.writeText(this.rawJsonSchema())\n    this.isCopied.set(true)\n    this.later(() => this.isCopied.set(false), 2000)\n  }\n\n  handleCopyEntity(field: ExtractedEntity): void {\n    void navigator.clipboard?.writeText(field.extractedValue)\n    this.copiedFieldId.set(field.id)\n    this.later(() => this.copiedFieldId.set(null), 2000)\n  }\n\n  selectVqaPreset(preset: VqaPreset): void {\n    this.vqaInput.set(preset.question)\n    this.isAnsweringVqa.set(true)\n    this.activeBoxId.set(preset.targetBoxId)\n    this.later(() => {\n      this.currentVqaAnswer.set(preset.answer)\n      this.isAnsweringVqa.set(false)\n    }, 350)\n  }\n\n  submitCustomVqa(): void {\n    if (!this.vqaInput().trim() || this.isAnsweringVqa()) return\n    this.isAnsweringVqa.set(true)\n    this.later(() => {\n      const input = this.vqaInput()\n      const match = this.vqaPresets.find((p) => p.question.toLowerCase().includes(input.toLowerCase().slice(0, 8)))\n      if (match) {\n        this.currentVqaAnswer.set(match.answer)\n        this.activeBoxId.set(match.targetBoxId)\n      } else {\n        this.currentVqaAnswer.set(\n          'Multimodal reasoning on visual region: Extracted relevant visual context from document canvas. Invoice INV-84920 Total is $1,245.00 due Sep 01, 2026. Arithmetic verified.',\n        )\n        this.activeBoxId.set('box-total')\n      }\n      this.isAnsweringVqa.set(false)\n    }, 400)\n  }\n}\n",
      "type": "registry:page",
      "target": "~/src/app/components/blocks/multimodal-vision-playground/multimodal-vision-playground.component.ts"
    },
    {
      "path": "packages/registry-angular/blocks/multimodal-vision-playground/index.ts",
      "content": "export { UiMultimodalVisionPlaygroundComponent } from './multimodal-vision-playground.component'\n",
      "type": "registry:page",
      "target": "~/src/app/components/blocks/multimodal-vision-playground/index.ts"
    }
  ],
  "dependencies": [
    "lucide-angular"
  ],
  "devDependencies": [],
  "registryDependencies": [
    "https://uipkge.dev/r/angular/badge.json",
    "https://uipkge.dev/r/angular/button.json",
    "https://uipkge.dev/r/angular/card.json",
    "https://uipkge.dev/r/angular/input.json",
    "https://uipkge.dev/r/angular/select.json",
    "https://uipkge.dev/r/angular/separator.json",
    "https://uipkge.dev/r/angular/switch.json",
    "https://uipkge.dev/r/angular/table.json",
    "https://uipkge.dev/r/angular/tabs.json"
  ],
  "description": "GPT-4o/Claude 3.5 Vision multimodal visual analysis studio with interactive bounding box overlays, OCR entity extraction, tabular line item parsing, raw RFC-8259 JSON viewer, and visual question answering (VQA).",
  "categories": [
    "ai",
    "app",
    "dashboard"
  ]
}