Menu

Change language

GitHub repo

Feature Interactive Api Curl Builder

blockfeature

Interactive API cURL builder and live payload testing sandbox with multi-language code export.

Installation

$npx uipkge-ng@latest add feature-interactive-api-curl-builder
Manifest:https://uipkge.dev/r/angular/feature-interactive-api-curl-builder.jsonInstalls to:src/app/components/blocks/feature-interactive-api-curl-builder/

Variants

Loading interactive previews…

Schema

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

CurlExecutionData
interface CurlExecutionData {
  bundleId: string
  name: string
  frameworks: string[]
  astCompiled: boolean
  sizeBytes: number
  oklchTokensInlined: number
  edgeDeliveryTime: string
}
CurlExecutionResult
interface CurlExecutionResult {
  status: number
  latencyMs: number
  data: CurlExecutionData
}

npm dependencies

Files installed (2)

  • src/app/components/blocks/feature-interactive-api-curl-builder/feature-interactive-api-curl-builder.component.ts13.7 kB
    // Interactive edge API console: endpoint + payload flag config on the left, live multi-language
    // snippet generation (cURL / Fetch / Python / Go) with copy on the right, and a simulated HTTP
    // response payload. Port of the React block 1:1.
    import { ChangeDetectionStrategy, Component, Input, computed, signal } from '@angular/core'
    import { ArrowRight, Check, Copy, Globe, LucideAngularModule, Play, Terminal } 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 CurlHttpMethod = 'GET' | 'POST'
    export type CurlLanguage = 'curl' | 'javascript' | 'python' | 'go'
    
    export interface CurlExecutionData {
      bundleId: string
      name: string
      frameworks: string[]
      astCompiled: boolean
      sizeBytes: number
      oklchTokensInlined: number
      edgeDeliveryTime: string
    }
    
    export interface CurlExecutionResult {
      status: number
      latencyMs: number
      data: CurlExecutionData
    }
    
    export const CURL_ENDPOINTS = ['/v1/components/bundle', '/v1/tokens/compile'] as const
    
    export const CURL_LANGUAGES: { id: CurlLanguage; label: string }[] = [
      { id: 'curl', label: 'cURL' },
      { id: 'javascript', label: 'Fetch' },
      { id: 'python', label: 'Python' },
      { id: 'go', label: 'Go' },
    ]
    
    @Component({
      changeDetection: ChangeDetectionStrategy.Eager,
      selector: 'ui-feature-interactive-api-curl-builder, [ui-feature-interactive-api-curl-builder]',
      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-interactive-api-curl-builder" [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="#api-builder"
                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]="Globe" class="text-primary size-3.5" />
                <span>Interactive Edge API Console</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>
    
            <!-- API Builder Workbench -->
            <div class="mt-12 grid grid-cols-1 gap-6 lg:grid-cols-12">
              <!-- Left Config Parameters (5 cols) -->
              <div class="space-y-4 lg:col-span-5">
                <div ui-card class="border-border bg-card shadow-xs">
                  <div ui-card-content class="space-y-5 p-6">
                    <div class="border-border flex items-center justify-between border-b pb-3">
                      <span class="text-muted-foreground text-xs font-bold tracking-wider uppercase"
                        >Request Parameters</span
                      >
                      <span
                        ui-badge
                        variant="outline"
                        class="border-success/30 bg-success/10 text-success font-mono text-xs"
                      >
                        Edge v1
                      </span>
                    </div>
    
                    <!-- Endpoint Selector -->
                    <div class="space-y-2">
                      <label class="text-foreground text-xs font-bold">Endpoint Path</label>
                      <div class="border-border bg-muted/40 flex rounded-lg border p-1">
                        @for (ep of endpoints; track ep) {
                          <button
                            type="button"
                            [class]="endpointClass(ep)"
                            class="flex-1 rounded-md py-1 font-mono text-xs transition-colors"
                            (click)="selectEndpoint(ep)"
                          >
                            {{ ep }}
                          </button>
                        }
                      </div>
                    </div>
    
                    <!-- Payload Toggles -->
                    <div class="space-y-2.5 pt-2">
                      <label class="text-foreground text-xs font-bold">Payload Flags</label>
                      <div class="space-y-2">
                        <label
                          class="border-border/80 bg-background hover:bg-muted/20 flex cursor-pointer items-center justify-between rounded-lg border p-3 text-xs"
                        >
                          <span class="text-muted-foreground">Emit AST Syntax Tree</span>
                          <input
                            type="checkbox"
                            [checked]="enableAstTree()"
                            (change)="enableAstTree.set($any($event.target).checked)"
                            class="accent-primary size-4 cursor-pointer"
                          />
                        </label>
                        <label
                          class="border-border/80 bg-background hover:bg-muted/20 flex cursor-pointer items-center justify-between rounded-lg border p-3 text-xs"
                        >
                          <span class="text-muted-foreground">Inline OKLCH CSS Tokens</span>
                          <input
                            type="checkbox"
                            [checked]="enableOklchTokens()"
                            (change)="enableOklchTokens.set($any($event.target).checked)"
                            class="accent-primary size-4 cursor-pointer"
                          />
                        </label>
                      </div>
                    </div>
    
                    <button ui-button class="w-full gap-2 shadow-xs" [disabled]="isExecuting()" (click)="executeRequest()">
                      <lucide-icon [img]="Play" class="fill-primary-foreground size-3.5" />
                      <span>{{ isExecuting() ? 'Executing Edge Call...' : 'Send Live HTTP Request' }}</span>
                    </button>
                  </div>
                </div>
              </div>
    
              <!-- Right Code Preview & Simulated Response (7 cols) -->
              <div class="space-y-4 lg:col-span-7">
                <div ui-card class="border-border bg-card overflow-hidden font-mono text-xs shadow-xs">
                  <!-- Language Selector Tabs -->
                  <div class="border-border bg-muted/40 flex items-center justify-between border-b px-4 py-2">
                    <div class="flex items-center gap-1">
                      @for (lang of languages; track lang.id) {
                        <button
                          type="button"
                          [class]="languageClass(lang.id)"
                          class="rounded px-2.5 py-1 text-xs transition-colors"
                          (click)="selectLanguage(lang.id)"
                        >
                          {{ lang.label }}
                        </button>
                      }
                    </div>
    
                    <button
                      type="button"
                      class="text-muted-foreground hover:text-foreground inline-flex items-center gap-1 text-xs transition-colors"
                      (click)="copySnippet()"
                    >
                      @if (copiedSnippet()) {
                        <lucide-icon [img]="Check" class="text-success size-3" />
                      } @else {
                        <lucide-icon [img]="Copy" class="size-3" />
                      }
                      <span>{{ copiedSnippet() ? 'Copied' : 'Copy' }}</span>
                    </button>
                  </div>
    
                  <div ui-card-content class="bg-muted/10 p-4">
                    <pre
                      class="text-foreground overflow-x-auto text-xs leading-relaxed"
                    ><code>{{ generatedCode() }}</code></pre>
                  </div>
                </div>
    
                <!-- Simulated Live HTTP Response -->
                <div class="border-border bg-muted/30 space-y-2 rounded-xl border p-4 font-mono text-xs">
                  <div class="border-border/80 flex items-center justify-between border-b pb-2">
                    <div class="flex items-center gap-2">
                      <lucide-icon [img]="Terminal" class="text-primary size-3.5" />
                      <span class="text-foreground font-bold">Response Payload</span>
                    </div>
                    <div class="flex items-center gap-3 text-xs">
                      <span class="text-success font-bold">HTTP {{ executionResult().status }} OK</span>
                      <span class="text-muted-foreground">{{ executionResult().latencyMs }}ms</span>
                    </div>
                  </div>
                  <pre
                    class="text-muted-foreground overflow-x-auto text-xs leading-relaxed"
                  ><code>{{ responseJson() }}</code></pre>
                </div>
              </div>
            </div>
          </div>
        </section>
      `,
    })
    export class UiFeatureInteractiveApiCurlBuilderComponent {
      protected readonly ArrowRight = ArrowRight
      protected readonly Check = Check
      protected readonly Copy = Copy
      protected readonly Globe = Globe
      protected readonly Play = Play
      protected readonly Terminal = Terminal
    
      protected readonly endpoints: readonly string[] = CURL_ENDPOINTS
      protected readonly languages = CURL_LANGUAGES
    
      @Input() title = 'Composable REST APIs designed for programmatic automation.'
      @Input() description =
        'Synthesize type-safe HTTP payloads, test endpoints in real time, and export snippets directly to your production workflow.'
      @Input('class') className?: string
    
      readonly selectedMethod = signal<CurlHttpMethod>('POST')
      readonly selectedEndpoint = signal<string>('/v1/components/bundle')
      readonly selectedLanguage = signal<CurlLanguage>('curl')
      readonly enableAstTree = signal(true)
      readonly enableOklchTokens = signal(true)
      readonly copiedSnippet = signal(false)
      readonly isExecuting = signal(false)
      readonly executionResult = signal<CurlExecutionResult>({
        status: 200,
        latencyMs: 14,
        data: {
          bundleId: 'pkg_9981x2',
          name: 'button',
          frameworks: ['vue', 'react'],
          astCompiled: true,
          sizeBytes: 1480,
          oklchTokensInlined: 48,
          edgeDeliveryTime: '0.8ms',
        },
      })
      private copyTimer: ReturnType<typeof setTimeout> | undefined
    
      readonly generatedCode = computed(() => {
        const method = this.selectedMethod()
        const endpoint = this.selectedEndpoint()
        const language = this.selectedLanguage()
        const payload = JSON.stringify(
          {
            component: 'button',
            framework: 'vue',
            astTree: this.enableAstTree(),
            inlineTokens: this.enableOklchTokens(),
          },
          null,
          2,
        )
    
        if (language === 'curl') {
          return `curl -X ${method} https://api.uipkge.dev${endpoint} \\
      -H "Authorization: Bearer uipkge_sec_89201" \\
      -H "Content-Type: application/json" \\
      -d '${payload.replace(/\n/g, '\n  ')}'`
        }
    
        if (language === 'javascript') {
          return `const res = await fetch("https://api.uipkge.dev${endpoint}", {
      method: "${method}",
      headers: {
        "Authorization": "Bearer uipkge_sec_89201",
        "Content-Type": "application/json"
      },
      body: JSON.stringify(${payload})
    });
    const data = await res.json();`
        }
    
        if (language === 'python') {
          return `import requests
    
    res = requests.${method.toLowerCase()}(
        "https://api.uipkge.dev${endpoint}",
        headers={"Authorization": "Bearer uipkge_sec_89201"},
        json=${payload.replace(/true/g, 'True').replace(/false/g, 'False')}
    )
    data = res.json()`
        }
    
        return `// Go HTTP Request
    req, _ := http.NewRequest("${method}", "https://api.uipkge.dev${endpoint}", payload)
    req.Header.Set("Authorization", "Bearer uipkge_sec_89201")
    res, _ := client.Do(req)`
      })
    
      readonly responseJson = computed(() => JSON.stringify(this.executionResult().data, null, 2))
    
      get rootClass(): string {
        return cn('bg-background relative overflow-hidden py-16 sm:py-24', this.className)
      }
    
      endpointClass(ep: string): string {
        return cn(
          this.selectedEndpoint() === ep
            ? 'bg-background text-foreground font-bold shadow-2xs'
            : 'text-muted-foreground hover:text-foreground',
        )
      }
    
      languageClass(lang: CurlLanguage): string {
        return cn(
          this.selectedLanguage() === lang
            ? 'bg-background text-foreground font-bold shadow-2xs'
            : 'text-muted-foreground hover:text-foreground',
        )
      }
    
      selectEndpoint(ep: string): void {
        this.selectedEndpoint.set(ep)
      }
    
      selectLanguage(lang: CurlLanguage): void {
        this.selectedLanguage.set(lang)
      }
    
      async copySnippet(): Promise<void> {
        try {
          await navigator.clipboard.writeText(this.generatedCode())
          this.copiedSnippet.set(true)
          clearTimeout(this.copyTimer)
          this.copyTimer = setTimeout(() => this.copiedSnippet.set(false), 2000)
        } catch {
          // Clipboard unavailable (insecure context / jsdom): leave the label unchanged.
        }
      }
    
      executeRequest(): void {
        if (this.isExecuting()) return
        this.isExecuting.set(true)
        setTimeout(() => {
          this.isExecuting.set(false)
          const endpoint = this.selectedEndpoint()
          const ast = this.enableAstTree()
          const tokens = this.enableOklchTokens()
          this.executionResult.set({
            status: 200,
            latencyMs: Math.floor(Math.random() * 8) + 11,
            data: {
              bundleId: `pkg_${Math.random().toString(36).substring(2, 8)}`,
              name: endpoint.includes('bundle') ? 'button' : 'tokens',
              frameworks: ['vue', 'react'],
              astCompiled: ast,
              sizeBytes: ast ? 1480 : 1120,
              oklchTokensInlined: tokens ? 48 : 0,
              edgeDeliveryTime: '0.8ms',
            },
          })
        }, 350)
      }
    }
    
  • src/app/components/blocks/feature-interactive-api-curl-builder/index.ts0.2 kB

Raw manifest:https://uipkge.dev/r/angular/feature-interactive-api-curl-builder.json