Menu

Change language

GitHub repo

Feature Bento Ai Copilot

blockfeature

AI agent copilot bento workbench with model tuning sliders, live inference streaming, and token speed telemetry.

Installation

$npx uipkge-ng@latest add feature-bento-ai-copilot
Manifest:https://uipkge.dev/r/angular/feature-bento-ai-copilot.jsonInstalls to:src/app/components/blocks/feature-bento-ai-copilot/

Variants

Loading interactive previews…

Schema

Type aliases exported from this item's source. Use these to shape the data you pass in.

CopilotModelMetrics
interface CopilotModelMetrics {
  name: string
  ttftMs: number
  tokPerSec: number
  costPer1k: string
}

npm dependencies

Files installed (2)

  • src/app/components/blocks/feature-bento-ai-copilot/feature-bento-ai-copilot.component.ts12.3 kB
    // AI agent copilot bento workbench with model tuning sliders, live inference
    // streaming, and token speed telemetry. Port of the React block 1:1.
    import { ChangeDetectionStrategy, Component, DestroyRef, Input, computed, inject, signal } from '@angular/core'
    import {
      Activity,
      ArrowRight,
      BrainCircuit,
      Check,
      Copy,
      Cpu,
      LucideAngularModule,
      Play,
      Sliders,
      Sparkles,
      Zap,
    } from 'lucide-angular'
    import { cn } from '@/lib/utils'
    import { UiBadgeComponent } from '@/ui/badge/badge.component'
    import { UiButtonComponent } from '@/ui/button/button.component'
    import { UiCardComponent, UiCardContentComponent } from '@/ui/card/card.component'
    
    export type CopilotModel = 'sonnet' | 'gpt4o' | 'gemini'
    
    export interface CopilotModelMetrics {
      name: string
      ttftMs: number
      tokPerSec: number
      costPer1k: string
    }
    
    const models: { id: CopilotModel; label: string }[] = [
      { id: 'sonnet', label: 'Sonnet 3.5' },
      { id: 'gpt4o', label: 'GPT-4o' },
      { id: 'gemini', label: 'Gemini Pro' },
    ]
    
    @Component({
      changeDetection: ChangeDetectionStrategy.Eager,
      selector: 'ui-feature-bento-ai-copilot, [ui-feature-bento-ai-copilot]',
      standalone: true,
      // React renders the root <section> itself: the host stays out of layout and `class` goes to the root.
      host: { '[attr.class]': '"contents"' },
      imports: [LucideAngularModule, UiBadgeComponent, UiButtonComponent, UiCardComponent, UiCardContentComponent],
      template: `
        <section data-slot="feature-bento-ai-copilot" [class]="rootClass">
          <div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
            <!-- Section Header -->
            <div class="mx-auto max-w-3xl space-y-4 text-center">
              <a
                href="#ai-copilot"
                class="group border-border/80 bg-secondary/60 hover:bg-secondary text-foreground inline-flex items-center gap-2 rounded-full border px-3.5 py-1 text-xs font-medium shadow-2xs transition-colors"
              >
                <lucide-icon [img]="Sparkles" class="text-primary size-3.5" />
                <span>Next-Gen AI Agent Workbench</span>
                <lucide-icon
                  [img]="ArrowRight"
                  class="text-muted-foreground size-3 transition-transform group-hover:translate-x-0.5"
                />
              </a>
    
              <h2 class="text-foreground text-3xl font-bold tracking-tight sm:text-4xl">{{ title }}</h2>
    
              <p class="text-muted-foreground text-base sm:text-lg">{{ description }}</p>
            </div>
    
            <!-- Bento Grid (2 Cards) -->
            <div class="mt-12 grid grid-cols-1 gap-6 lg:grid-cols-12">
              <!-- Left: Model Tuning & Hyperparameters (5 Cols) -->
              <div
                ui-card
                class="border-border bg-card/80 flex flex-col justify-between shadow-xs backdrop-blur-xs lg:col-span-5"
              >
                <div ui-card-content class="space-y-5 p-6">
                  <div class="border-border flex items-center justify-between border-b pb-3">
                    <div class="flex items-center gap-2">
                      <lucide-icon [img]="Sliders" class="text-primary size-4" />
                      <span class="text-foreground text-sm font-semibold">Inference Hyperparameters</span>
                    </div>
                    <span ui-badge variant="outline" class="border-border font-mono text-xs">
                      {{ modelMetrics().name }}
                    </span>
                  </div>
    
                  <!-- Model Switcher -->
                  <div class="space-y-2">
                    <label class="text-muted-foreground text-xs font-semibold tracking-wider uppercase">
                      Foundation Model
                    </label>
                    <div class="grid grid-cols-3 gap-2">
                      @for (model of models; track model.id) {
                        <button type="button" [class]="modelButtonClass(model.id)" (click)="selectedModel.set(model.id)">
                          {{ model.label }}
                        </button>
                      }
                    </div>
                  </div>
    
                  <!-- Temperature Slider -->
                  <div class="space-y-2">
                    <div class="flex items-center justify-between text-xs">
                      <span class="text-foreground font-medium">Sampling Temperature</span>
                      <span class="text-primary font-mono font-bold">{{ temperature() }}</span>
                    </div>
                    <input
                      type="range"
                      min="0"
                      max="1"
                      step="0.1"
                      [value]="temperature()"
                      (input)="setTemperature($event)"
                      class="accent-primary w-full cursor-pointer"
                      aria-label="Sampling temperature"
                    />
                    <div class="text-muted-foreground flex justify-between text-xs">
                      <span>0.0 (Deterministic)</span>
                      <span>1.0 (Creative)</span>
                    </div>
                  </div>
    
                  <!-- System Prompt Input -->
                  <div class="space-y-2">
                    <label class="text-muted-foreground text-xs font-semibold tracking-wider uppercase">
                      System Directive
                    </label>
                    <textarea
                      rows="3"
                      [value]="systemPrompt()"
                      (input)="systemPrompt.set($any($event.target).value)"
                      class="border-border bg-muted/30 text-foreground focus-visible:ring-primary w-full rounded-md border p-2.5 font-mono text-xs focus-visible:ring-1 focus-visible:outline-none"
                    ></textarea>
                  </div>
    
                  <!-- Action Button -->
                  <button
                    ui-button
                    class="w-full gap-2 shadow-xs"
                    [disabled]="isGenerating()"
                    (click)="triggerGeneration()"
                  >
                    @if (!isGenerating()) {
                      <lucide-icon [img]="Play" class="size-3.5" />
                    } @else {
                      <lucide-icon [img]="Zap" class="size-3.5 animate-spin" />
                    }
                    <span>{{ isGenerating() ? 'Streaming Inference...' : 'Synthesize UI Primitive' }}</span>
                  </button>
                </div>
              </div>
    
              <!-- Right: Streaming Code Output & Real-time Telemetry (7 Cols) -->
              <div
                ui-card
                class="border-border bg-card/80 flex flex-col justify-between shadow-xs backdrop-blur-xs lg:col-span-7"
              >
                <div ui-card-content class="space-y-4 p-6">
                  <!-- Output Header & Telemetry -->
                  <div class="border-border flex flex-wrap items-center justify-between gap-3 border-b pb-3">
                    <div class="flex items-center gap-2">
                      <lucide-icon [img]="BrainCircuit" class="text-success size-4" />
                      <span class="text-foreground text-sm font-semibold">Live Streaming Synthesis</span>
                    </div>
    
                    <div class="flex items-center gap-2">
                      <button
                        ui-button
                        variant="ghost"
                        size="sm"
                        class="h-7 gap-1 px-2 font-mono text-xs"
                        (click)="copyCode()"
                      >
                        @if (copied()) {
                          <lucide-icon [img]="Check" class="text-primary size-3.5" />
                        } @else {
                          <lucide-icon [img]="Copy" class="size-3.5" />
                        }
                        <span>{{ copied() ? 'Copied' : 'Copy SFC' }}</span>
                      </button>
                    </div>
                  </div>
    
                  <!-- Telemetry KPI Pill Row -->
                  <div class="border-border bg-muted/20 grid grid-cols-3 gap-3 rounded-lg border p-3">
                    <div class="space-y-0.5">
                      <div class="text-muted-foreground flex items-center gap-1 text-xs">
                        <lucide-icon [img]="Activity" class="text-success size-3" />
                        <span>TTFT</span>
                      </div>
                      <div class="text-foreground font-mono text-sm font-bold">{{ modelMetrics().ttftMs }}ms</div>
                    </div>
                    <div class="space-y-0.5">
                      <div class="text-muted-foreground flex items-center gap-1 text-xs">
                        <lucide-icon [img]="Cpu" class="text-info size-3" />
                        <span>Velocity</span>
                      </div>
                      <div class="text-foreground font-mono text-sm font-bold">{{ modelMetrics().tokPerSec }} tok/s</div>
                    </div>
                    <div class="space-y-0.5">
                      <div class="text-muted-foreground flex items-center gap-1 text-xs">
                        <lucide-icon [img]="Zap" class="text-primary size-3" />
                        <span>Cost</span>
                      </div>
                      <div class="text-foreground font-mono text-sm font-bold">{{ modelMetrics().costPer1k }}/1k</div>
                    </div>
                  </div>
    
                  <!-- Code Sandbox Block -->
                  <div
                    class="border-border bg-muted/50 text-foreground relative overflow-x-auto rounded-lg border p-4 font-mono text-xs"
                  >
                    <pre class="leading-relaxed"><code>{{ generatedStream() }}</code></pre>
                  </div>
                </div>
              </div>
            </div>
          </div>
        </section>
      `,
    })
    export class UiFeatureBentoAiCopilotComponent {
      protected readonly Activity = Activity
      protected readonly ArrowRight = ArrowRight
      protected readonly BrainCircuit = BrainCircuit
      protected readonly Check = Check
      protected readonly Copy = Copy
      protected readonly Cpu = Cpu
      protected readonly Play = Play
      protected readonly Sliders = Sliders
      protected readonly Sparkles = Sparkles
      protected readonly Zap = Zap
    
      protected readonly models = models
    
      @Input() title = 'AI Copilot tooling built directly into your component architecture.'
      @Input() description =
        'Debug prompts, inspect LLM token streaming speeds, and synthesize Vue 3.5 and React 19 templates in real-time with sub-millisecond local latency.'
      @Input('class') className?: string
    
      readonly selectedModel = signal<CopilotModel>('sonnet')
      readonly temperature = signal(0.2)
      readonly isGenerating = signal(false)
      readonly systemPrompt = signal(
        'You are a senior UI design engineer. Generate an accessible, zero-dependency KPI metric card for Vue 3.5 and React 19 using semantic Tailwind v4 tokens.',
      )
      readonly generatedStream = signal(
        `<template>\n  <Card class="border-border bg-card p-4">\n    <div class="text-xs text-muted-foreground">MRR Growth</div>\n    <div class="text-2xl font-bold">$124,500</div>\n    <div class="text-xs text-success font-medium">+18.4% vs last month</div>\n  </Card>\n</template>`,
      )
      readonly copied = signal(false)
    
      private copyTimer: ReturnType<typeof setTimeout> | undefined
      private generateTimer: ReturnType<typeof setTimeout> | undefined
    
      constructor() {
        inject(DestroyRef).onDestroy(() => {
          clearTimeout(this.copyTimer)
          clearTimeout(this.generateTimer)
        })
      }
    
      readonly modelMetrics = computed<CopilotModelMetrics>(() => {
        if (this.selectedModel() === 'sonnet') {
          return { name: 'Claude 3.5 Sonnet', ttftMs: 180, tokPerSec: 142, costPer1k: '$0.003' }
        }
        if (this.selectedModel() === 'gpt4o') {
          return { name: 'GPT-4o Omnimodel', ttftMs: 140, tokPerSec: 165, costPer1k: '$0.0025' }
        }
        return { name: 'Gemini 1.5 Pro', ttftMs: 160, tokPerSec: 155, costPer1k: '$0.00125' }
      })
    
      get rootClass(): string {
        return cn('bg-background relative overflow-hidden py-16 sm:py-24', this.className)
      }
    
      modelButtonClass(id: CopilotModel): string {
        return cn(
          'rounded-lg border p-2 text-center text-xs font-medium transition-colors',
          this.selectedModel() === id
            ? 'border-primary bg-primary/10 text-primary font-semibold'
            : 'border-border bg-muted/40 text-muted-foreground hover:text-foreground',
        )
      }
    
      setTemperature(event: Event): void {
        this.temperature.set(parseFloat((event.target as HTMLInputElement).value))
      }
    
      async copyCode(): Promise<void> {
        try {
          await navigator.clipboard.writeText(this.generatedStream())
          this.copied.set(true)
          clearTimeout(this.copyTimer)
          this.copyTimer = setTimeout(() => this.copied.set(false), 2000)
        } catch {
          // fallback
        }
      }
    
      triggerGeneration(): void {
        this.isGenerating.set(true)
        clearTimeout(this.generateTimer)
        this.generateTimer = setTimeout(() => this.isGenerating.set(false), 1200)
      }
    }
    
  • src/app/components/blocks/feature-bento-ai-copilot/index.ts0.1 kB

Raw manifest:https://uipkge.dev/r/angular/feature-bento-ai-copilot.json