{
  "$schema": "https://shadcn-vue.com/schema/registry-item.json",
  "name": "spark-job-stage-visualizer",
  "title": "Spark Job Stage Visualizer",
  "type": "registry:page",
  "files": [
    {
      "path": "packages/registry-vue/blocks/spark-job-stage-visualizer/SparkJobStageVisualizer.vue",
      "content": "<script setup lang=\"ts\">\nimport { computed, ref, type HTMLAttributes } from 'vue'\nimport { Check, CheckCircle2, Clock, Copy, Download, Flame, Server, ShieldAlert, X } from 'lucide-vue-next'\nimport { cn } from '@/lib/utils'\nimport { Badge } from '@/components/ui/badge'\nimport { Button } from '@/components/ui/button'\nimport { Tabs, TabsList, TabsTrigger } from '@/components/ui/tabs'\nimport SparkJobKpis from './SparkJobKpis.vue'\nimport SparkStageDagTab from './SparkStageDagTab.vue'\nimport SparkStageSkewTab from './SparkStageSkewTab.vue'\nimport SparkExecutorsAqe from './SparkExecutorsAqe.vue'\nimport type {\n  StageStatus,\n  ExecutorStatus,\n  TaskQuantiles,\n  TaskBreakdownMetrics,\n  SparkStage,\n  SparkExecutor,\n  AqeRule,\n} from './spark-stage-types'\nimport { defaultStages } from './spark-stage-data'\n\nexport type { StageStatus, ExecutorStatus, TaskQuantiles, TaskBreakdownMetrics, SparkStage, SparkExecutor, AqeRule }\n\ninterface Props {\n  initialStageId?: number\n  stages?: SparkStage[]\n  class?: HTMLAttributes['class']\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n  initialStageId: 2,\n  stages: () => defaultStages,\n})\n\nconst selectedStageId = ref<number>(props.initialStageId)\nconst activeTab = ref<string>('dag')\nconst copiedAppId = ref<boolean>(false)\nconst showKillConfirm = ref<boolean>(false)\nconst downloadStarted = ref<boolean>(false)\n\nconst selectedStage = computed(() => {\n  return props.stages.find((s) => s.id === selectedStageId.value) ?? props.stages[2] ?? props.stages[0]\n})\n\nfunction selectStage(id: number) {\n  selectedStageId.value = id\n}\n\nfunction copyAppId() {\n  navigator.clipboard?.writeText('app-20260821-142850-0042')\n  copiedAppId.value = true\n  setTimeout(() => {\n    copiedAppId.value = false\n  }, 2000)\n}\n\nfunction triggerDownload() {\n  downloadStarted.value = true\n  setTimeout(() => {\n    downloadStarted.value = false\n  }, 2500)\n}\n</script>\n\n<template>\n  <div :class=\"cn('text-foreground w-full space-y-6 font-sans antialiased', props.class)\">\n    <!-- Header Section -->\n    <div class=\"border-border bg-card rounded-xl border p-5 shadow-xs transition-colors\">\n      <div class=\"flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between\">\n        <!-- Title & Identifiers -->\n        <div class=\"space-y-1.5\">\n          <div class=\"flex flex-wrap items-center gap-2.5\">\n            <div\n              class=\"flex size-9 items-center justify-center rounded-lg bg-orange-500/10 text-orange-600 dark:text-orange-400\"\n            >\n              <Flame class=\"size-5\" />\n            </div>\n            <div>\n              <div class=\"flex flex-wrap items-center gap-2\">\n                <h1 class=\"text-base font-semibold tracking-tight sm:text-lg\">AggregatedCustomerLTVJob</h1>\n                <Badge wrap variant=\"outline\" class=\"text-muted-foreground font-mono text-xs\">\n                  app-20260821-142850-0042\n                </Badge>\n                <Button\n                  variant=\"ghost\"\n                  size=\"xs\"\n                  class=\"text-muted-foreground hover:text-foreground h-6 px-1.5 text-xs\"\n                  aria-label=\"Copy Spark Application ID\"\n                  @click=\"copyAppId\"\n                >\n                  <Check v-if=\"copiedAppId\" class=\"text-success size-3\" />\n                  <Copy v-else class=\"size-3\" />\n                  <span class=\"sr-only\">Copy Application ID</span>\n                </Button>\n              </div>\n              <p class=\"text-muted-foreground text-xs\">Apache Spark 3.5.1 · Adaptive Query Execution (AQE) Enabled</p>\n            </div>\n          </div>\n\n          <!-- Metadata Tags -->\n          <div class=\"text-muted-foreground flex flex-wrap items-center gap-x-4 gap-y-1 pt-1 text-xs\">\n            <div class=\"flex items-center gap-1.5\">\n              <span class=\"bg-success size-2 rounded-full\" />\n              <span class=\"text-foreground font-medium\">Job Succeeded</span>\n              <span>·</span>\n              <span class=\"font-mono tabular-nums\">03m:42s</span>\n            </div>\n            <div class=\"flex items-center gap-1\">\n              <Server class=\"size-3.5\" />\n              <span\n                >Cluster: <strong class=\"text-foreground font-medium\">c5d.4xlarge (4 nodes · 112 vCPUs)</strong></span\n              >\n            </div>\n            <div class=\"flex items-center gap-1\">\n              <Clock class=\"size-3.5\" />\n              <span>Started: <strong class=\"text-foreground font-medium\">14:28:50 UTC</strong></span>\n            </div>\n          </div>\n        </div>\n\n        <!-- Action Buttons -->\n        <div class=\"flex flex-wrap items-center gap-2\">\n          <Button\n            aria-label=\"Download attachment\"\n            variant=\"outline\"\n            size=\"sm\"\n            class=\"h-8 gap-1.5 text-xs\"\n            :disabled=\"downloadStarted\"\n            @click=\"triggerDownload\"\n          >\n            <CheckCircle2 v-if=\"downloadStarted\" class=\"text-success size-3.5\" />\n            <Download v-else class=\"size-3.5\" />\n            <span>{{ downloadStarted ? 'Log Exported' : 'Download EventLog' }}</span>\n          </Button>\n\n          <Button\n            variant=\"ghost\"\n            size=\"sm\"\n            class=\"text-destructive hover:bg-destructive/10 hover:text-destructive h-8 gap-1.5 text-xs\"\n            @click=\"showKillConfirm = !showKillConfirm\"\n          >\n            <X class=\"size-3.5\" />\n            <span>Kill Application</span>\n          </Button>\n        </div>\n      </div>\n\n      <!-- Kill Confirmation Alert (Collapsible) -->\n      <div\n        v-if=\"showKillConfirm\"\n        class=\"border-destructive/30 bg-destructive/5 text-destructive mt-4 flex items-center justify-between rounded-lg border p-3 text-xs\"\n      >\n        <div class=\"flex flex-wrap items-center gap-2\">\n          <ShieldAlert class=\"size-4 shrink-0\" />\n          <span\n            >This application is in finished state (SUCCEEDED). Terminating an inactive application will release log\n            retention locks.</span\n          >\n        </div>\n        <div class=\"flex flex-wrap items-center gap-2\">\n          <Button size=\"xs\" variant=\"destructive\" @click=\"showKillConfirm = false\"> Confirm Release </Button>\n          <Button size=\"xs\" variant=\"ghost\" @click=\"showKillConfirm = false\"> Cancel </Button>\n        </div>\n      </div>\n    </div>\n\n    <!-- 4 Spark Job Execution KPI Cards -->\n    <SparkJobKpis />\n\n    <!-- Main Content Tabs -->\n    <Tabs v-model=\"activeTab\" class=\"space-y-4\">\n      <div class=\"flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between\">\n        <TabsList class=\"grid w-full grid-cols-2 sm:w-auto sm:grid-cols-4\">\n          <TabsTrigger value=\"dag\" class=\"text-xs\"> Stages DAG Flow </TabsTrigger>\n          <TabsTrigger value=\"skew\" class=\"text-xs\"> Task Skew & Quantiles </TabsTrigger>\n          <TabsTrigger value=\"executors\" class=\"text-xs\"> Executors (4) </TabsTrigger>\n          <TabsTrigger value=\"aqe\" class=\"text-xs\"> AQE Optimizer </TabsTrigger>\n        </TabsList>\n\n        <div class=\"text-muted-foreground flex flex-wrap items-center gap-2 text-xs\">\n          <span class=\"bg-primary inline-block size-2 rounded-full\" />\n          <span>Active Selection:</span>\n          <span class=\"text-foreground font-medium\">Stage {{ selectedStage.id }} ({{ selectedStage.name }})</span>\n        </div>\n      </div>\n\n      <!-- TAB 1: INTERACTIVE SPARK STAGES DAG FLOW -->\n      <SparkStageDagTab\n        :selected-stage=\"selectedStage\"\n        :selected-stage-id=\"selectedStageId\"\n        :stages=\"props.stages\"\n        @select-stage=\"selectStage\"\n      />\n\n      <!-- TAB 2: TASK SKEW & QUANTILES DISTRIBUTION -->\n      <SparkStageSkewTab\n        :selected-stage=\"selectedStage\"\n        :stages=\"props.stages\"\n        :selected-stage-id=\"selectedStageId\"\n        @select-stage=\"selectStage\"\n      />\n\n      <!-- TAB 3 & 4: Executors + AQE tabs -->\n      <SparkExecutorsAqe />\n    </Tabs>\n  </div>\n</template>\n",
      "type": "registry:page",
      "target": "~/app/components/blocks/SparkJobStageVisualizer.vue"
    },
    {
      "path": "packages/registry-vue/blocks/spark-job-stage-visualizer/SparkJobKpis.vue",
      "content": "<script setup lang=\"ts\">\nimport { ArrowRightLeft, CheckCircle2, Cpu, Gauge } from 'lucide-vue-next'\nimport { Badge } from '@/components/ui/badge'\nimport { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'\n</script>\n\n<template>\n  <div class=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n    <!-- KPI 1: Stages -->\n    <Card class=\"border-border shadow-xs\">\n      <CardHeader class=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n        <CardTitle class=\"text-muted-foreground text-xs font-medium\"> Total Stages Completed </CardTitle>\n        <div class=\"bg-success/10 text-success flex size-7 items-center justify-center rounded-md\">\n          <CheckCircle2 class=\"size-4\" />\n        </div>\n      </CardHeader>\n      <CardContent class=\"space-y-2\">\n        <div class=\"text-foreground font-mono text-2xl font-bold tracking-tight\">6 / 6</div>\n        <div class=\"flex flex-wrap items-center gap-2\">\n          <Badge wrap variant=\"success\" class=\"text-xs\"> 6 / 6 Stages Succeeded </Badge>\n        </div>\n        <p class=\"text-muted-foreground text-xs\">0 failed · 0 skipped · 4 stages active in DAG</p>\n      </CardContent>\n    </Card>\n\n    <!-- KPI 2: Shuffle & Spill -->\n    <Card class=\"border-border shadow-xs\">\n      <CardHeader class=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n        <CardTitle class=\"text-muted-foreground text-xs font-medium\"> Shuffle Read / Write Volume </CardTitle>\n        <div class=\"bg-info/10 text-info flex size-7 items-center justify-center rounded-md\">\n          <ArrowRightLeft class=\"size-4\" />\n        </div>\n      </CardHeader>\n      <CardContent class=\"space-y-2\">\n        <div class=\"text-foreground font-mono text-2xl font-bold tracking-tight tabular-nums\">42.8 GB</div>\n        <div class=\"text-muted-foreground flex items-center gap-1.5 text-xs\">\n          <span class=\"text-foreground font-medium\">42.8 GB Shuffle Write</span>\n          <span>·</span>\n          <span class=\"text-warning font-mono\">1.48 GB Spill (Memory)</span>\n        </div>\n        <p class=\"text-muted-foreground text-xs\">38.6 GB shuffle read · 0 B disk spill</p>\n      </CardContent>\n    </Card>\n\n    <!-- KPI 3: Task Skew Ratio -->\n    <Card class=\"border-border shadow-xs\">\n      <CardHeader class=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n        <CardTitle class=\"text-muted-foreground text-xs font-medium\"> Task Skew Max/Median Ratio </CardTitle>\n        <div class=\"bg-success/10 text-success flex size-7 items-center justify-center rounded-md\">\n          <Gauge class=\"size-4\" />\n        </div>\n      </CardHeader>\n      <CardContent class=\"space-y-2\">\n        <div class=\"text-foreground font-mono text-2xl font-bold tracking-tight tabular-nums\">1.18x</div>\n        <div class=\"flex flex-wrap items-center gap-2\">\n          <Badge wrap variant=\"success\" class=\"text-xs\"> 1.18x Ratio · Well Balanced </Badge>\n        </div>\n        <p class=\"text-muted-foreground text-xs\">\n          Max task: <span class=\"text-foreground font-mono font-medium\">4.2s</span> · Median:\n          <span class=\"text-foreground font-mono font-medium\">3.5s</span>\n        </p>\n      </CardContent>\n    </Card>\n\n    <!-- KPI 4: JVM GC Overhead -->\n    <Card class=\"border-border shadow-xs\">\n      <CardHeader class=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n        <CardTitle class=\"text-muted-foreground text-xs font-medium\"> Executor JVM GC Time </CardTitle>\n        <div class=\"bg-chart-1/10 text-chart-1 flex size-7 items-center justify-center rounded-md\">\n          <Cpu class=\"size-4\" />\n        </div>\n      </CardHeader>\n      <CardContent class=\"space-y-2\">\n        <div class=\"text-foreground font-mono text-2xl font-bold tracking-tight tabular-nums\">2.1%</div>\n        <div class=\"flex flex-wrap items-center gap-2\">\n          <Badge wrap variant=\"success\" class=\"text-xs\"> 2.1% of Total CPU Time · Healthy </Badge>\n        </div>\n        <p class=\"text-muted-foreground text-xs\">18.4s GC time across 880s CPU total</p>\n      </CardContent>\n    </Card>\n  </div>\n</template>\n",
      "type": "registry:page",
      "target": "~/app/components/blocks/SparkJobKpis.vue"
    },
    {
      "path": "packages/registry-vue/blocks/spark-job-stage-visualizer/SparkStageDagTab.vue",
      "content": "<script setup lang=\"ts\">\nimport { ArrowDown, Database, Layers, Workflow } from 'lucide-vue-next'\nimport { cn } from '@/lib/utils'\nimport { Badge } from '@/components/ui/badge'\nimport { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'\nimport { TabsContent } from '@/components/ui/tabs'\nimport type { SparkStage } from './spark-stage-types'\n\ninterface Props {\n  selectedStage: SparkStage\n  selectedStageId: number\n  stages: SparkStage[]\n}\n\ndefineProps<Props>()\n\nconst emit = defineEmits<{\n  (e: 'selectStage', id: number): void\n}>()\n</script>\n\n<template>\n  <TabsContent value=\"dag\" class=\"space-y-6 focus-visible:outline-none\">\n    <Card class=\"border-border shadow-xs\">\n      <CardHeader class=\"border-border/60 border-b pb-4\">\n        <div class=\"flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between\">\n          <div>\n            <CardTitle class=\"text-base font-semibold\"> Distributed Job Execution DAG </CardTitle>\n            <CardDescription class=\"text-xs\">\n              Interactive stage graph showing task parallelization, shuffle boundaries, and operator fusion. Click any\n              stage to inspect.\n            </CardDescription>\n          </div>\n          <div class=\"flex flex-wrap items-center gap-2\">\n            <Badge wrap variant=\"outline\" class=\"font-mono text-xs\"> AQE Coalesced: 540 → 200 Partitions </Badge>\n          </div>\n        </div>\n      </CardHeader>\n\n      <CardContent class=\"p-6\">\n        <!-- DAG Topology Container -->\n        <div class=\"space-y-8\">\n          <!-- Two parallel branches joining together -->\n          <div class=\"grid grid-cols-1 gap-6 lg:grid-cols-2\">\n            <!-- Left Branch: Transactions -->\n            <div class=\"border-border/80 bg-muted/20 space-y-4 rounded-xl border p-4\">\n              <div class=\"border-border/60 flex items-center justify-between border-b pb-2\">\n                <div class=\"flex flex-wrap items-center gap-2\">\n                  <Database class=\"size-4 text-orange-500\" />\n                  <span class=\"text-muted-foreground text-xs font-semibold\">\n                    Branch 1: Customer Transactions Pipeline\n                  </span>\n                </div>\n                <Badge wrap variant=\"outline\" class=\"font-mono text-xs\">14.8M records</Badge>\n              </div>\n\n              <!-- Stage 0 -->\n              <div\n                tabindex=\"0\"\n                role=\"button\"\n                :aria-pressed=\"selectedStageId === 0\"\n                :class=\"\n                  cn(\n                    'group focus-visible:ring-ring relative cursor-pointer rounded-lg border p-4 transition-colors duration-150 focus-visible:ring-2 focus-visible:outline-none',\n                    selectedStageId === 0\n                      ? 'border-primary bg-primary/5 ring-primary shadow-xs ring-1'\n                      : 'border-border bg-card hover:border-border/80 hover:bg-muted/40',\n                  )\n                \"\n                @click=\"emit('selectStage', 0)\"\n                @keydown.enter=\"emit('selectStage', 0)\"\n                @keydown.space.prevent=\"emit('selectStage', 0)\"\n              >\n                <div class=\"flex items-start justify-between gap-3\">\n                  <div class=\"space-y-1\">\n                    <div class=\"flex flex-wrap items-center gap-2\">\n                      <Badge wrap variant=\"secondary\" class=\"font-mono text-xs\">Stage 0</Badge>\n                      <span class=\"text-foreground text-sm font-semibold\">FileScan parquet</span>\n                    </div>\n                    <p class=\"text-muted-foreground font-mono text-xs\">540 tasks · 18s · 14.2 GB read</p>\n                  </div>\n                  <Badge wrap variant=\"success\" class=\"text-xs\">Succeeded</Badge>\n                </div>\n                <div class=\"text-muted-foreground mt-3 flex items-center justify-between text-xs\">\n                  <span>Operator: FileScan parquet</span>\n                  <span class=\"text-foreground font-mono tabular-nums\">14.82M rows</span>\n                </div>\n              </div>\n\n              <!-- Connector Arrow -->\n              <div class=\"text-muted-foreground flex items-center justify-center py-0.5\">\n                <div\n                  class=\"border-border bg-card flex items-center gap-1.5 rounded-full border px-2.5 py-0.5 font-mono text-xs\"\n                >\n                  <ArrowDown class=\"text-muted-foreground size-3\" />\n                  <span>14.2 GB in-memory scan</span>\n                </div>\n              </div>\n\n              <!-- Stage 1 -->\n              <div\n                tabindex=\"0\"\n                role=\"button\"\n                :aria-pressed=\"selectedStageId === 1\"\n                :class=\"\n                  cn(\n                    'group focus-visible:ring-ring relative cursor-pointer rounded-lg border p-4 transition-colors duration-150 focus-visible:ring-2 focus-visible:outline-none',\n                    selectedStageId === 1\n                      ? 'border-primary bg-primary/5 ring-primary shadow-xs ring-1'\n                      : 'border-border bg-card hover:border-border/80 hover:bg-muted/40',\n                  )\n                \"\n                @click=\"emit('selectStage', 1)\"\n                @keydown.enter=\"emit('selectStage', 1)\"\n                @keydown.space.prevent=\"emit('selectStage', 1)\"\n              >\n                <div class=\"flex items-start justify-between gap-3\">\n                  <div class=\"space-y-1\">\n                    <div class=\"flex flex-wrap items-center gap-2\">\n                      <Badge wrap variant=\"secondary\" class=\"font-mono text-xs\">Stage 1</Badge>\n                      <span class=\"text-foreground text-sm font-semibold\">Exchange hashpartitioning</span>\n                    </div>\n                    <p class=\"text-muted-foreground font-mono text-xs\">200 tasks · 42s · 8.4 GB shuffle write</p>\n                  </div>\n                  <Badge wrap variant=\"success\" class=\"text-xs\">Succeeded</Badge>\n                </div>\n                <div class=\"text-muted-foreground mt-3 flex items-center justify-between text-xs\">\n                  <span>Key: Murmur3(customer_id)</span>\n                  <span class=\"text-foreground font-mono tabular-nums\">200 partitions</span>\n                </div>\n              </div>\n\n              <!-- Connector Arrow -->\n              <div class=\"text-muted-foreground flex items-center justify-center py-0.5\">\n                <div\n                  class=\"border-border bg-card flex items-center gap-1.5 rounded-full border px-2.5 py-0.5 font-mono text-xs\"\n                >\n                  <ArrowDown class=\"text-muted-foreground size-3\" />\n                  <span>8.4 GB shuffle transfer</span>\n                </div>\n              </div>\n\n              <!-- Stage 2 -->\n              <div\n                tabindex=\"0\"\n                role=\"button\"\n                :aria-pressed=\"selectedStageId === 2\"\n                :class=\"\n                  cn(\n                    'group focus-visible:ring-ring relative cursor-pointer rounded-lg border p-4 transition-colors duration-150 focus-visible:ring-2 focus-visible:outline-none',\n                    selectedStageId === 2\n                      ? 'border-primary bg-primary/5 ring-primary shadow-xs ring-1'\n                      : 'border-border bg-card hover:border-border/80 hover:bg-muted/40',\n                  )\n                \"\n                @click=\"emit('selectStage', 2)\"\n                @keydown.enter=\"emit('selectStage', 2)\"\n                @keydown.space.prevent=\"emit('selectStage', 2)\"\n              >\n                <div class=\"flex items-start justify-between gap-3\">\n                  <div class=\"space-y-1\">\n                    <div class=\"flex flex-wrap items-center gap-2\">\n                      <Badge wrap variant=\"secondary\" class=\"font-mono text-xs\">Stage 2</Badge>\n                      <span class=\"text-foreground text-sm font-semibold\">HashAggregate & Filter</span>\n                    </div>\n                    <p class=\"text-muted-foreground font-mono text-xs\">200 tasks · 1m 12s · 4.6 GB write</p>\n                  </div>\n                  <Badge wrap variant=\"success\" class=\"text-xs\">Succeeded</Badge>\n                </div>\n                <div class=\"text-muted-foreground mt-3 flex items-center justify-between text-xs\">\n                  <span>Aggregations: sum(amount), count(1)</span>\n                  <span class=\"text-warning text-warning font-mono tabular-nums\">420 MB spill</span>\n                </div>\n              </div>\n            </div>\n\n            <!-- Right Branch: Customer Dim -->\n            <div class=\"border-border/80 bg-muted/20 space-y-4 rounded-xl border p-4\">\n              <div class=\"border-border/60 flex items-center justify-between border-b pb-2\">\n                <div class=\"flex flex-wrap items-center gap-2\">\n                  <Layers class=\"text-info size-4\" />\n                  <span class=\"text-muted-foreground text-xs font-semibold\">\n                    Branch 2: Customer Dimension Pipeline\n                  </span>\n                </div>\n                <Badge wrap variant=\"outline\" class=\"font-mono text-xs\">2.4M records</Badge>\n              </div>\n\n              <!-- Stage 4 -->\n              <div\n                tabindex=\"0\"\n                role=\"button\"\n                :aria-pressed=\"selectedStageId === 4\"\n                :class=\"\n                  cn(\n                    'group focus-visible:ring-ring relative cursor-pointer rounded-lg border p-4 transition-colors duration-150 focus-visible:ring-2 focus-visible:outline-none',\n                    selectedStageId === 4\n                      ? 'border-primary bg-primary/5 ring-primary shadow-xs ring-1'\n                      : 'border-border bg-card hover:border-border/80 hover:bg-muted/40',\n                  )\n                \"\n                @click=\"emit('selectStage', 4)\"\n                @keydown.enter=\"emit('selectStage', 4)\"\n                @keydown.space.prevent=\"emit('selectStage', 4)\"\n              >\n                <div class=\"flex items-start justify-between gap-3\">\n                  <div class=\"space-y-1\">\n                    <div class=\"flex flex-wrap items-center gap-2\">\n                      <Badge wrap variant=\"secondary\" class=\"font-mono text-xs\">Stage 4</Badge>\n                      <span class=\"text-foreground text-sm font-semibold\">FileScan customer_dim</span>\n                    </div>\n                    <p class=\"text-muted-foreground font-mono text-xs\">120 tasks · 12s · 3.1 GB read</p>\n                  </div>\n                  <Badge wrap variant=\"success\" class=\"text-xs\">Succeeded</Badge>\n                </div>\n                <div class=\"text-muted-foreground mt-3 flex items-center justify-between text-xs\">\n                  <span>Operator: FileScan parquet</span>\n                  <span class=\"text-foreground font-mono tabular-nums\">2.40M rows</span>\n                </div>\n              </div>\n\n              <!-- Connector Arrow -->\n              <div class=\"text-muted-foreground flex items-center justify-center py-0.5\">\n                <div\n                  class=\"border-border bg-card flex items-center gap-1.5 rounded-full border px-2.5 py-0.5 font-mono text-xs\"\n                >\n                  <ArrowDown class=\"text-muted-foreground size-3\" />\n                  <span>3.1 GB dimension scan</span>\n                </div>\n              </div>\n\n              <!-- Stage 5 -->\n              <div\n                tabindex=\"0\"\n                role=\"button\"\n                :aria-pressed=\"selectedStageId === 5\"\n                :class=\"\n                  cn(\n                    'group focus-visible:ring-ring relative cursor-pointer rounded-lg border p-4 transition-colors duration-150 focus-visible:ring-2 focus-visible:outline-none',\n                    selectedStageId === 5\n                      ? 'border-primary bg-primary/5 ring-primary shadow-xs ring-1'\n                      : 'border-border bg-card hover:border-border/80 hover:bg-muted/40',\n                  )\n                \"\n                @click=\"emit('selectStage', 5)\"\n                @keydown.enter=\"emit('selectStage', 5)\"\n                @keydown.space.prevent=\"emit('selectStage', 5)\"\n              >\n                <div class=\"flex items-start justify-between gap-3\">\n                  <div class=\"space-y-1\">\n                    <div class=\"flex flex-wrap items-center gap-2\">\n                      <Badge wrap variant=\"secondary\" class=\"font-mono text-xs\">Stage 5</Badge>\n                      <span class=\"text-foreground text-sm font-semibold\">Exchange customer_dim</span>\n                    </div>\n                    <p class=\"text-muted-foreground font-mono text-xs\">120 tasks · 16s · 3.1 GB shuffle write</p>\n                  </div>\n                  <Badge wrap variant=\"success\" class=\"text-xs\">Succeeded</Badge>\n                </div>\n                <div class=\"text-muted-foreground mt-3 flex items-center justify-between text-xs\">\n                  <span>Key: Murmur3(customer_id)</span>\n                  <span class=\"text-foreground font-mono tabular-nums\">200 partitions</span>\n                </div>\n              </div>\n\n              <!-- Branch alignment spacer -->\n              <div class=\"text-muted-foreground hidden items-center justify-center py-5 lg:flex\">\n                <div class=\"text-muted-foreground flex items-center gap-1 text-xs\">\n                  <Workflow class=\"size-3.5\" />\n                  <span>Ready for hash join</span>\n                </div>\n              </div>\n            </div>\n          </div>\n\n          <!-- Convergence Flow Indicator -->\n          <div class=\"flex flex-col items-center justify-center gap-2\">\n            <div\n              class=\"border-border bg-muted/60 text-muted-foreground flex flex-wrap items-center gap-2 rounded-full border px-3 py-1 font-mono text-xs\"\n            >\n              <Workflow class=\"text-primary size-3.5\" />\n              <span>Shuffle Partition Alignment (200 Partitions × 2 Inputs)</span>\n            </div>\n            <ArrowDown class=\"text-muted-foreground size-4\" />\n          </div>\n\n          <!-- Joined Final Stage: Stage 3 -->\n          <div\n            tabindex=\"0\"\n            role=\"button\"\n            :aria-pressed=\"selectedStageId === 3\"\n            :class=\"\n              cn(\n                'group focus-visible:ring-ring relative cursor-pointer rounded-xl border p-5 transition-colors duration-150 focus-visible:ring-2 focus-visible:outline-none',\n                selectedStageId === 3\n                  ? 'border-primary bg-primary/5 ring-primary shadow-sm ring-1'\n                  : 'border-border bg-card hover:border-border/80 hover:bg-muted/40',\n              )\n            \"\n            @click=\"emit('selectStage', 3)\"\n            @keydown.enter=\"emit('selectStage', 3)\"\n            @keydown.space.prevent=\"emit('selectStage', 3)\"\n          >\n            <div class=\"flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between\">\n              <div class=\"space-y-1\">\n                <div class=\"flex flex-wrap items-center gap-2\">\n                  <Badge wrap variant=\"default\" class=\"font-mono text-xs\">Stage 3</Badge>\n                  <h3 class=\"text-foreground text-base font-semibold\">SortMergeJoin (Final Reduction)</h3>\n                  <Badge wrap variant=\"outline\" class=\"font-mono text-xs\">Terminal Stage</Badge>\n                </div>\n                <p class=\"text-muted-foreground font-mono text-xs\">\n                  200 tasks · 1m 20s · Joined 14.8M keys · 1.24M output rows\n                </p>\n              </div>\n              <div class=\"flex items-center gap-3\">\n                <div class=\"text-right text-xs\">\n                  <div class=\"text-foreground font-mono font-medium\">29.8 GB Output</div>\n                  <div class=\"text-warning font-mono\">1.06 GB Mem Spill</div>\n                </div>\n                <Badge wrap variant=\"success\" class=\"text-xs\">Succeeded</Badge>\n              </div>\n            </div>\n\n            <div class=\"border-border/60 mt-4 grid grid-cols-2 gap-2 border-t pt-3 text-xs sm:grid-cols-4\">\n              <div>\n                <span class=\"text-muted-foreground\">Join Type:</span>\n                <p class=\"text-foreground font-mono font-medium\">Inner SortMergeJoin</p>\n              </div>\n              <div>\n                <span class=\"text-muted-foreground\">Join Key:</span>\n                <p class=\"text-foreground font-mono font-medium\">customer_id#42 = customer_id#98</p>\n              </div>\n              <div>\n                <span class=\"text-muted-foreground\">Tasks:</span>\n                <p class=\"text-foreground font-mono font-medium\">200 / 200 (100%)</p>\n              </div>\n              <div>\n                <span class=\"text-muted-foreground\">Skew Max/Median:</span>\n                <p class=\"text-success font-mono font-medium\">1.21x (Balanced)</p>\n              </div>\n            </div>\n          </div>\n        </div>\n      </CardContent>\n    </Card>\n\n    <!-- Selected Stage Deep Inspector -->\n    <Card class=\"border-border shadow-xs\">\n      <CardHeader class=\"border-border/60 border-b pb-3\">\n        <div class=\"flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between\">\n          <div class=\"flex flex-wrap items-center gap-2\">\n            <Badge wrap variant=\"secondary\" class=\"font-mono text-xs\">Selected Stage {{ selectedStage.id }}</Badge>\n            <CardTitle class=\"text-sm font-semibold\">{{ selectedStage.name }} Details</CardTitle>\n          </div>\n          <div class=\"flex flex-wrap items-center gap-2\">\n            <span class=\"text-muted-foreground text-xs\">Duration:</span>\n            <Badge wrap variant=\"outline\" class=\"font-mono text-xs\">{{ selectedStage.duration }}</Badge>\n            <Badge wrap variant=\"success\" class=\"text-xs\">Completed</Badge>\n          </div>\n        </div>\n      </CardHeader>\n\n      <CardContent class=\"space-y-4 p-5\">\n        <!-- Call Site Code Box -->\n        <div class=\"space-y-1.5\">\n          <span class=\"text-muted-foreground text-xs font-medium\">Catalyst Operator Expression</span>\n          <div class=\"border-border bg-muted/40 text-foreground rounded-md border p-3 font-mono text-xs\">\n            <code>{{ selectedStage.operator }}</code>\n          </div>\n        </div>\n\n        <!-- Stage Metrics Grid -->\n        <div class=\"grid grid-cols-2 gap-3 text-xs sm:grid-cols-4 lg:grid-cols-6\">\n          <div class=\"border-border/80 bg-card rounded-lg border p-3\">\n            <span class=\"text-muted-foreground\">Tasks</span>\n            <p class=\"text-foreground mt-1 font-mono text-sm font-semibold\">\n              {{ selectedStage.tasksSucceeded }} / {{ selectedStage.tasksTotal }}\n            </p>\n          </div>\n          <div class=\"border-border/80 bg-card rounded-lg border p-3\">\n            <span class=\"text-muted-foreground\">Input Data</span>\n            <p class=\"text-foreground mt-1 font-mono text-sm font-semibold\">{{ selectedStage.inputBytes }}</p>\n          </div>\n          <div class=\"border-border/80 bg-card rounded-lg border p-3\">\n            <span class=\"text-muted-foreground\">Shuffle Read</span>\n            <p class=\"text-foreground mt-1 font-mono text-sm font-semibold\">{{ selectedStage.shuffleReadBytes }}</p>\n          </div>\n          <div class=\"border-border/80 bg-card rounded-lg border p-3\">\n            <span class=\"text-muted-foreground\">Shuffle Write</span>\n            <p class=\"text-foreground mt-1 font-mono text-sm font-semibold\">\n              {{ selectedStage.shuffleWriteBytes }}\n            </p>\n          </div>\n          <div class=\"border-border/80 bg-card rounded-lg border p-3\">\n            <span class=\"text-muted-foreground\">Spill (Memory)</span>\n            <p class=\"text-warning mt-1 font-mono text-sm font-semibold\">\n              {{ selectedStage.spillMemory }}\n            </p>\n          </div>\n          <div class=\"border-border/80 bg-card rounded-lg border p-3\">\n            <span class=\"text-muted-foreground\">Task Skew Ratio</span>\n            <p class=\"text-success mt-1 font-mono text-sm font-semibold\">{{ selectedStage.skewRatio }}x</p>\n          </div>\n        </div>\n\n        <p class=\"text-muted-foreground text-xs leading-relaxed\">\n          {{ selectedStage.details }}\n        </p>\n      </CardContent>\n    </Card>\n  </TabsContent>\n</template>\n",
      "type": "registry:page",
      "target": "~/app/components/blocks/SparkStageDagTab.vue"
    },
    {
      "path": "packages/registry-vue/blocks/spark-job-stage-visualizer/SparkStageSkewTab.vue",
      "content": "<script setup lang=\"ts\">\nimport { Badge } from '@/components/ui/badge'\nimport { Button } from '@/components/ui/button'\nimport { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'\nimport { TabsContent } from '@/components/ui/tabs'\nimport type { SparkStage } from './spark-stage-types'\nimport { getQuantilePosition } from './spark-stage-data'\n\ninterface Props {\n  selectedStage: SparkStage\n  stages: SparkStage[]\n  selectedStageId: number\n}\n\ndefineProps<Props>()\n\nconst emit = defineEmits<{\n  (e: 'selectStage', id: number): void\n}>()\n</script>\n\n<template>\n  <TabsContent value=\"skew\" class=\"space-y-6 focus-visible:outline-none\">\n    <Card class=\"border-border shadow-xs\">\n      <CardHeader class=\"border-border/60 border-b pb-4\">\n        <div class=\"flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between\">\n          <div>\n            <CardTitle class=\"text-base font-semibold\"> Stage Task Duration Skew & Quantiles </CardTitle>\n            <CardDescription class=\"text-xs\">\n              Distribution of task execution time across executor cores for the selected stage.\n            </CardDescription>\n          </div>\n\n          <!-- Quick Stage Selector Buttons -->\n          <div class=\"flex flex-wrap items-center gap-1.5\">\n            <Button\n              v-for=\"s in stages\"\n              :key=\"s.id\"\n              size=\"xs\"\n              :variant=\"selectedStageId === s.id ? 'default' : 'outline'\"\n              class=\"font-mono text-xs\"\n              @click=\"emit('selectStage', s.id)\"\n            >\n              Stage {{ s.id }}\n            </Button>\n          </div>\n        </div>\n      </CardHeader>\n\n      <CardContent class=\"space-y-6 p-6\">\n        <!-- Stage Title & Skew Ratio Banner -->\n        <div\n          class=\"border-border bg-muted/20 flex flex-col gap-3 rounded-lg border p-4 sm:flex-row sm:items-center sm:justify-between\"\n        >\n          <div class=\"space-y-0.5\">\n            <div class=\"flex flex-wrap items-center gap-2\">\n              <span class=\"text-foreground text-sm font-semibold\"\n                >Stage {{ selectedStage.id }}: {{ selectedStage.name }}</span\n              >\n              <Badge wrap variant=\"outline\" class=\"font-mono text-xs\">{{ selectedStage.tasksTotal }} Tasks</Badge>\n            </div>\n            <p class=\"text-muted-foreground text-xs\">\n              Duration: <span class=\"text-foreground font-mono font-medium\">{{ selectedStage.duration }}</span> · Peak\n              task memory:\n              <span class=\"text-foreground font-mono\">{{ selectedStage.taskBreakdown.peakMemory }}</span>\n            </p>\n          </div>\n\n          <div class=\"flex items-center gap-3\">\n            <div class=\"text-right\">\n              <span class=\"text-muted-foreground text-xs\">Max / Median Skew:</span>\n              <div class=\"text-success font-mono text-base font-bold\">{{ selectedStage.skewRatio }}x</div>\n            </div>\n            <Badge wrap variant=\"success\" class=\"text-xs\"> Well Balanced (&lt; 1.5x) </Badge>\n          </div>\n        </div>\n\n        <!-- Quantiles Metric Row (Min, P25, Median, P75, P95, Max) -->\n        <div class=\"grid grid-cols-2 gap-3 sm:grid-cols-3 lg:grid-cols-6\">\n          <div class=\"border-border bg-card rounded-lg border p-3 text-center\">\n            <span class=\"text-muted-foreground text-xs font-medium\">Min (0%)</span>\n            <p class=\"text-foreground mt-1 font-mono text-lg font-bold tabular-nums\">\n              {{ selectedStage.quantiles.min }}s\n            </p>\n            <span class=\"text-muted-foreground text-xs\">Fastest task</span>\n          </div>\n\n          <div class=\"border-border bg-card rounded-lg border p-3 text-center\">\n            <span class=\"text-muted-foreground text-xs font-medium\">25th Percentile</span>\n            <p class=\"text-foreground mt-1 font-mono text-lg font-bold tabular-nums\">\n              {{ selectedStage.quantiles.p25 }}s\n            </p>\n            <span class=\"text-muted-foreground text-xs\">Q1 boundary</span>\n          </div>\n\n          <div class=\"border-primary/40 bg-primary/5 ring-primary/20 rounded-lg border p-3 text-center ring-1\">\n            <span class=\"text-primary text-xs font-medium\">Median (P50)</span>\n            <p class=\"text-primary mt-1 font-mono text-lg font-bold tabular-nums\">\n              {{ selectedStage.quantiles.median }}s\n            </p>\n            <span class=\"text-muted-foreground text-xs\">Midpoint baseline</span>\n          </div>\n\n          <div class=\"border-border bg-card rounded-lg border p-3 text-center\">\n            <span class=\"text-muted-foreground text-xs font-medium\">75th Percentile</span>\n            <p class=\"text-foreground mt-1 font-mono text-lg font-bold tabular-nums\">\n              {{ selectedStage.quantiles.p75 }}s\n            </p>\n            <span class=\"text-muted-foreground text-xs\">Q3 boundary</span>\n          </div>\n\n          <div class=\"border-border bg-card rounded-lg border p-3 text-center\">\n            <span class=\"text-muted-foreground text-xs font-medium\">95th Percentile</span>\n            <p class=\"text-foreground mt-1 font-mono text-lg font-bold tabular-nums\">\n              {{ selectedStage.quantiles.p95 }}s\n            </p>\n            <span class=\"text-muted-foreground text-xs\">Tail latency</span>\n          </div>\n\n          <div class=\"border-border bg-card rounded-lg border p-3 text-center\">\n            <span class=\"text-muted-foreground text-xs font-medium\">Max (100%)</span>\n            <p class=\"text-foreground mt-1 font-mono text-lg font-bold tabular-nums\">\n              {{ selectedStage.quantiles.max }}s\n            </p>\n            <span class=\"text-muted-foreground text-xs\">Straggler peak</span>\n          </div>\n        </div>\n\n        <!-- Visual Distribution Bar -->\n        <div class=\"border-border bg-muted/10 space-y-3 rounded-lg border p-5\">\n          <div class=\"text-muted-foreground flex items-center justify-between text-xs\">\n            <span class=\"text-foreground font-medium\">Task Duration Quantiles Scale (0.0s – 5.0s)</span>\n            <span class=\"font-mono\"\n              >IQR: {{ (selectedStage.quantiles.p75 - selectedStage.quantiles.p25).toFixed(1) }}s</span\n            >\n          </div>\n\n          <!-- Custom Box & Whisker Horizontal Bar -->\n          <div class=\"relative pt-6 pb-2\">\n            <!-- Background track -->\n            <div class=\"bg-muted relative h-4 w-full overflow-hidden rounded-full\">\n              <!-- Min to Max Range Bar -->\n              <div\n                class=\"bg-primary/20 absolute top-0 bottom-0 rounded-full\"\n                :style=\"{\n                  left: `${getQuantilePosition(selectedStage.quantiles.min)}%`,\n                  width: `${getQuantilePosition(selectedStage.quantiles.max) - getQuantilePosition(selectedStage.quantiles.min)}%`,\n                }\"\n              />\n              <!-- P25 to P75 Interquartile Core -->\n              <div\n                class=\"bg-primary/70 absolute top-0 bottom-0\"\n                :style=\"{\n                  left: `${getQuantilePosition(selectedStage.quantiles.p25)}%`,\n                  width: `${getQuantilePosition(selectedStage.quantiles.p75) - getQuantilePosition(selectedStage.quantiles.p25)}%`,\n                }\"\n              />\n              <!-- Median Marker -->\n              <div\n                class=\"absolute top-0 bottom-0 z-10 w-1 bg-white dark:bg-black\"\n                :style=\"{\n                  left: `${getQuantilePosition(selectedStage.quantiles.median)}%`,\n                }\"\n              />\n            </div>\n\n            <!-- Markers with Pin Labels -->\n            <div\n              class=\"text-muted-foreground absolute top-0 -translate-x-1/2 font-mono text-xs\"\n              :style=\"{ left: `${getQuantilePosition(selectedStage.quantiles.min)}%` }\"\n            >\n              Min {{ selectedStage.quantiles.min }}s\n            </div>\n            <div\n              class=\"text-primary absolute top-0 -translate-x-1/2 font-mono text-xs font-semibold\"\n              :style=\"{ left: `${getQuantilePosition(selectedStage.quantiles.median)}%` }\"\n            >\n              Med {{ selectedStage.quantiles.median }}s\n            </div>\n            <div\n              class=\"text-muted-foreground absolute top-0 -translate-x-1/2 font-mono text-xs\"\n              :style=\"{ left: `${getQuantilePosition(selectedStage.quantiles.max)}%` }\"\n            >\n              Max {{ selectedStage.quantiles.max }}s\n            </div>\n          </div>\n\n          <div\n            class=\"border-border/60 text-muted-foreground flex flex-wrap items-center justify-between gap-2 border-t pt-3 text-xs\"\n          >\n            <div class=\"flex items-center gap-4\">\n              <div class=\"flex items-center gap-1.5\">\n                <span class=\"bg-primary/20 size-2.5 rounded\" />\n                <span>Full Range (Min - Max)</span>\n              </div>\n              <div class=\"flex items-center gap-1.5\">\n                <span class=\"bg-primary/70 size-2.5 rounded\" />\n                <span>IQR Core (P25 - P75)</span>\n              </div>\n              <div class=\"flex items-center gap-1.5\">\n                <span class=\"bg-foreground h-2.5 w-0.5\" />\n                <span>Median Point</span>\n              </div>\n            </div>\n            <span>Skew status: <strong class=\"text-success font-medium\">Optimal Partitioning</strong></span>\n          </div>\n        </div>\n\n        <!-- Task Internal Execution Time Breakdown -->\n        <div class=\"space-y-3\">\n          <h3 class=\"text-muted-foreground text-xs font-semibold\">Task Execution Phase Breakdown (Average per Task)</h3>\n\n          <div class=\"grid grid-cols-2 gap-3 text-xs sm:grid-cols-3 lg:grid-cols-6\">\n            <div class=\"border-border bg-card rounded-lg border p-3\">\n              <span class=\"text-muted-foreground\">CPU Run Time</span>\n              <p class=\"text-foreground mt-1 font-mono text-sm font-semibold\">\n                {{ selectedStage.taskBreakdown.executorRunTime }}\n              </p>\n              <span class=\"text-muted-foreground text-xs\">Active calculation</span>\n            </div>\n\n            <div class=\"border-border bg-card rounded-lg border p-3\">\n              <span class=\"text-muted-foreground\">JVM GC Overhead</span>\n              <p class=\"text-chart-1 mt-1 font-mono text-sm font-semibold\">\n                {{ selectedStage.taskBreakdown.jvmGcTime }}\n              </p>\n              <span class=\"text-muted-foreground text-xs\">Young Gen sweeps</span>\n            </div>\n\n            <div class=\"border-border bg-card rounded-lg border p-3\">\n              <span class=\"text-muted-foreground\">Shuffle Fetch Wait</span>\n              <p class=\"text-info mt-1 font-mono text-sm font-semibold\">\n                {{ selectedStage.taskBreakdown.shuffleFetchWait }}\n              </p>\n              <span class=\"text-muted-foreground text-xs\">Network remote blocks</span>\n            </div>\n\n            <div class=\"border-border bg-card rounded-lg border p-3\">\n              <span class=\"text-muted-foreground\">Deserialization</span>\n              <p class=\"text-foreground mt-1 font-mono text-sm font-semibold\">\n                {{ selectedStage.taskBreakdown.deserializationTime }}\n              </p>\n              <span class=\"text-muted-foreground text-xs\">Task definition</span>\n            </div>\n\n            <div class=\"border-border bg-card rounded-lg border p-3\">\n              <span class=\"text-muted-foreground\">Result Serialization</span>\n              <p class=\"text-foreground mt-1 font-mono text-sm font-semibold\">\n                {{ selectedStage.taskBreakdown.serializationTime }}\n              </p>\n              <span class=\"text-muted-foreground text-xs\">Map output buffers</span>\n            </div>\n\n            <div class=\"border-border bg-card rounded-lg border p-3\">\n              <span class=\"text-muted-foreground\">Peak Memory</span>\n              <p class=\"text-warning mt-1 font-mono text-sm font-semibold\">\n                {{ selectedStage.taskBreakdown.peakMemory }}\n              </p>\n              <span class=\"text-muted-foreground text-xs\">Off-heap aggregate</span>\n            </div>\n          </div>\n        </div>\n      </CardContent>\n    </Card>\n  </TabsContent>\n</template>\n",
      "type": "registry:page",
      "target": "~/app/components/blocks/SparkStageSkewTab.vue"
    },
    {
      "path": "packages/registry-vue/blocks/spark-job-stage-visualizer/SparkExecutorsAqe.vue",
      "content": "<script setup lang=\"ts\">\nimport { Badge } from '@/components/ui/badge'\nimport { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'\nimport { Progress } from '@/components/ui/progress'\nimport { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'\nimport { TabsContent } from '@/components/ui/tabs'\nimport { Server, Zap } from 'lucide-vue-next'\nimport type { AqeRule, SparkExecutor } from './spark-stage-types'\n\nconst executors: SparkExecutor[] = [\n  {\n    id: 'driver',\n    host: '10.0.12.4',\n    role: 'driver',\n    status: 'active',\n    cores: 16,\n    memoryUsedGB: 24.2,\n    memoryTotalGB: 32,\n    memoryPercent: 75.6,\n    gcTimeSec: 4.2,\n    gcPercent: 1.8,\n    tasksCompleted: 285,\n    tasksTotal: 285,\n    tasksFailed: 0,\n    shuffleReadGB: 10.8,\n    shuffleWriteGB: 10.8,\n    spillGB: 0.36,\n  },\n  {\n    id: 'exec-1',\n    host: '10.0.12.18',\n    role: 'executor',\n    status: 'active',\n    cores: 32,\n    memoryUsedGB: 28.8,\n    memoryTotalGB: 32,\n    memoryPercent: 90.0,\n    gcTimeSec: 5.1,\n    gcPercent: 2.2,\n    tasksCompleted: 284,\n    tasksTotal: 284,\n    tasksFailed: 0,\n    shuffleReadGB: 11.2,\n    shuffleWriteGB: 11.2,\n    spillGB: 0.42,\n  },\n  {\n    id: 'exec-2',\n    host: '10.0.12.29',\n    role: 'executor',\n    status: 'active',\n    cores: 32,\n    memoryUsedGB: 21.4,\n    memoryTotalGB: 32,\n    memoryPercent: 66.8,\n    gcTimeSec: 4.8,\n    gcPercent: 2.3,\n    tasksCompleted: 286,\n    tasksTotal: 286,\n    tasksFailed: 0,\n    shuffleReadGB: 10.4,\n    shuffleWriteGB: 10.4,\n    spillGB: 0.34,\n  },\n  {\n    id: 'exec-3',\n    host: '10.0.12.44',\n    role: 'executor',\n    status: 'active',\n    cores: 32,\n    memoryUsedGB: 18.6,\n    memoryTotalGB: 32,\n    memoryPercent: 58.1,\n    gcTimeSec: 4.3,\n    gcPercent: 2.1,\n    tasksCompleted: 285,\n    tasksTotal: 285,\n    tasksFailed: 0,\n    shuffleReadGB: 10.4,\n    shuffleWriteGB: 10.4,\n    spillGB: 0.36,\n  },\n]\n\nconst aqeRules: AqeRule[] = [\n  {\n    id: 'aqe-1',\n    name: 'CoalesceShufflePartitions',\n    status: 'applied',\n    description:\n      'Coalesced 540 initial map partitions down to 200 target shuffle partitions based on target partition size (64 MB).',\n    impact: 'Reduced network RPC overhead by 63% and avoided 340 small shuffle partition files.',\n  },\n  {\n    id: 'aqe-2',\n    name: 'OptimizeSkewedJoin',\n    status: 'applied',\n    description: 'Dynamic skew detection evaluated all 200 partition sizes against the 5x median threshold.',\n    impact: 'Max partition skew ratio remained well balanced at 1.18x. No partition splitting required.',\n  },\n  {\n    id: 'aqe-3',\n    name: 'DemoteBroadcastHashJoin',\n    status: 'skipped',\n    description: 'Evaluated right dimension relation size (3.1 GB) against auto-broadcast threshold (10 MB).',\n    impact: 'Relation exceeded broadcast threshold; preserved robust SortMergeJoin strategy.',\n  },\n  {\n    id: 'aqe-4',\n    name: 'EliminateSubqueryAliases',\n    status: 'applied',\n    description: 'Inlined and pruned unused redundant subquery attribute references from execution DAG.',\n    impact: 'Streamlined Catalyst physical plan tree depth from 14 nodes down to 8 nodes.',\n  },\n]\n</script>\n\n<template>\n  <!-- TAB 3: EXECUTORS TELEMETRY (4 NODES) -->\n  <TabsContent value=\"executors\" class=\"space-y-6 focus-visible:outline-none\">\n    <Card class=\"border-border shadow-xs\">\n      <CardHeader class=\"border-border/60 border-b pb-4\">\n        <div class=\"flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between\">\n          <div>\n            <CardTitle class=\"text-base font-semibold\">\n              Active Cluster Executors (4 Nodes · 112 Cores Total)\n            </CardTitle>\n            <CardDescription class=\"text-xs\">\n              Real-time worker telemetry: CPU utilization, JVM heap memory, garbage collection pauses, and task\n              throughput.\n            </CardDescription>\n          </div>\n\n          <div class=\"flex flex-wrap items-center gap-2\">\n            <Badge wrap variant=\"outline\" class=\"font-mono text-xs\"> Total Memory: 93.0 / 128 GB (72.7%) </Badge>\n            <Badge wrap variant=\"success\" class=\"text-xs\">4/4 Nodes Active</Badge>\n          </div>\n        </div>\n      </CardHeader>\n\n      <CardContent class=\"p-0\">\n        <div class=\"overflow-x-auto\">\n          <Table>\n            <TableHeader>\n              <TableRow class=\"hover:bg-transparent\">\n                <TableHead class=\"text-xs font-semibold\">Executor ID & Host</TableHead>\n                <TableHead class=\"text-xs font-semibold\">Role & Status</TableHead>\n                <TableHead class=\"text-xs font-semibold\">Cores</TableHead>\n                <TableHead class=\"text-xs font-semibold\">Memory Used</TableHead>\n                <TableHead class=\"text-xs font-semibold\">GC Pause Time</TableHead>\n                <TableHead class=\"text-xs font-semibold\">Tasks Completed</TableHead>\n                <TableHead class=\"text-xs font-semibold\">Shuffle I/O</TableHead>\n                <TableHead class=\"text-xs font-semibold\">Memory Spill</TableHead>\n              </TableRow>\n            </TableHeader>\n            <TableBody>\n              <TableRow v-for=\"exec in executors\" :key=\"exec.id\" class=\"text-xs\">\n                <!-- Executor ID & Host -->\n                <TableCell class=\"font-medium\">\n                  <div class=\"flex flex-wrap items-center gap-2\">\n                    <Server class=\"text-muted-foreground size-4\" />\n                    <div>\n                      <div class=\"text-foreground font-mono font-semibold\">{{ exec.id }}</div>\n                      <div class=\"text-muted-foreground font-mono\">{{ exec.host }}</div>\n                    </div>\n                  </div>\n                </TableCell>\n\n                <!-- Role & Status -->\n                <TableCell>\n                  <div class=\"flex items-center gap-1.5\">\n                    <Badge\n                      :variant=\"exec.role === 'driver' ? 'secondary' : 'outline'\"\n                      class=\"font-mono text-xs whitespace-normal capitalize\"\n                    >\n                      {{ exec.role }}\n                    </Badge>\n                    <span class=\"bg-success inline-block size-2 rounded-full\" />\n                  </div>\n                </TableCell>\n\n                <!-- Cores -->\n                <TableCell class=\"font-mono tabular-nums\"> {{ exec.cores }} cores </TableCell>\n\n                <!-- Memory Used -->\n                <TableCell>\n                  <div class=\"min-w-[140px] space-y-1\">\n                    <div class=\"flex items-center justify-between font-mono text-xs\">\n                      <span>{{ exec.memoryUsedGB }} / {{ exec.memoryTotalGB }} GB</span>\n                      <span :class=\"exec.memoryPercent > 85 ? 'text-warning font-semibold' : 'text-muted-foreground'\">\n                        {{ exec.memoryPercent }}%\n                      </span>\n                    </div>\n                    <Progress :model-value=\"exec.memoryPercent\" class=\"h-1.5\" />\n                  </div>\n                </TableCell>\n\n                <!-- GC Time -->\n                <TableCell class=\"font-mono tabular-nums\">\n                  <div class=\"flex items-center gap-1.5\">\n                    <Badge wrap variant=\"success\" class=\"font-mono text-xs\">\n                      {{ exec.gcTimeSec }}s ({{ exec.gcPercent }}%)\n                    </Badge>\n                  </div>\n                </TableCell>\n\n                <!-- Tasks Completed -->\n                <TableCell class=\"font-mono tabular-nums\">\n                  <span class=\"text-foreground font-medium\">{{ exec.tasksCompleted }}</span>\n                  <span class=\"text-muted-foreground\"> / {{ exec.tasksTotal }}</span>\n                  <span class=\"text-success ml-1\">(0 failed)</span>\n                </TableCell>\n\n                <!-- Shuffle I/O -->\n                <TableCell class=\"font-mono tabular-nums\"> {{ exec.shuffleReadGB }} GB </TableCell>\n\n                <!-- Spill -->\n                <TableCell class=\"text-muted-foreground font-mono tabular-nums\"> {{ exec.spillGB }} GB </TableCell>\n              </TableRow>\n            </TableBody>\n          </Table>\n        </div>\n      </CardContent>\n    </Card>\n  </TabsContent>\n\n  <!-- TAB 4: AQE OPTIMIZATION RULES -->\n  <TabsContent value=\"aqe\" class=\"space-y-6 focus-visible:outline-none\">\n    <Card class=\"border-border shadow-xs\">\n      <CardHeader class=\"border-border/60 border-b pb-4\">\n        <div class=\"flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between\">\n          <div>\n            <CardTitle class=\"text-base font-semibold\"> Adaptive Query Execution (AQE) Rules Engine </CardTitle>\n            <CardDescription class=\"text-xs\">\n              Runtime Catalyst optimizer decisions made dynamically based on actual stage map output partition\n              statistics.\n            </CardDescription>\n          </div>\n\n          <Badge wrap variant=\"success\" class=\"font-mono text-xs\"> AQE Status: Active (3 Rules Applied) </Badge>\n        </div>\n      </CardHeader>\n\n      <CardContent class=\"p-6\">\n        <div class=\"grid grid-cols-1 gap-4 md:grid-cols-2\">\n          <div v-for=\"rule in aqeRules\" :key=\"rule.id\" class=\"border-border bg-card space-y-3 rounded-xl border p-4\">\n            <div class=\"flex items-center justify-between\">\n              <div class=\"flex flex-wrap items-center gap-2\">\n                <Zap class=\"text-warning size-4\" />\n                <span class=\"text-foreground font-mono text-sm font-semibold\">{{ rule.name }}</span>\n              </div>\n              <Badge\n                :variant=\"rule.status === 'applied' ? 'success' : 'outline'\"\n                class=\"text-xs whitespace-normal capitalize\"\n              >\n                {{ rule.status }}\n              </Badge>\n            </div>\n\n            <p class=\"text-muted-foreground text-xs\">\n              {{ rule.description }}\n            </p>\n\n            <div class=\"border-border/60 bg-muted/40 rounded-md border p-2.5 text-xs\">\n              <span class=\"text-foreground font-medium\">Query Plan Impact: </span>\n              <span class=\"text-muted-foreground\">{{ rule.impact }}</span>\n            </div>\n          </div>\n        </div>\n      </CardContent>\n    </Card>\n  </TabsContent>\n</template>\n",
      "type": "registry:page",
      "target": "~/app/components/blocks/SparkExecutorsAqe.vue"
    },
    {
      "path": "packages/registry-vue/blocks/spark-job-stage-visualizer/spark-stage-data.ts",
      "content": "import type { SparkStage } from './spark-stage-types'\n\nexport function getQuantilePosition(val: number, max: number = 5.0): number {\n  return Math.min(100, Math.max(0, (val / max) * 100))\n}\n\nexport const defaultStages: SparkStage[] = [\n  {\n    id: 0,\n    name: 'FileScan parquet',\n    callSite: 'spark.read.parquet(\"s3://warehouse/customer_transactions\")',\n    operator: 'FileScan parquet default.customer_transactions [customer_id#42, amount#43, txn_date#44]',\n    status: 'succeeded',\n    tasksTotal: 540,\n    tasksSucceeded: 540,\n    tasksFailed: 0,\n    duration: '18s',\n    durationSec: 18,\n    inputBytes: '14.2 GB',\n    inputRecords: '14,820,400',\n    outputBytes: '14.2 GB',\n    outputRecords: '14,820,400',\n    shuffleReadBytes: '0 B',\n    shuffleWriteBytes: '0 B',\n    spillMemory: '0 B',\n    spillDisk: '0 B',\n    skewRatio: 1.12,\n    quantiles: { min: 0.8, p25: 1.2, median: 1.6, p75: 1.8, p95: 2.0, max: 2.1 },\n    taskBreakdown: {\n      executorRunTime: '1.52s',\n      jvmGcTime: '32ms',\n      shuffleFetchWait: '0ms',\n      serializationTime: '14ms',\n      deserializationTime: '28ms',\n      peakMemory: '128 MB',\n    },\n    details:\n      'Read partitioned parquet files from AWS S3 bucket s3://warehouse/customer_transactions. Pushed filters: [IsNotNull(customer_id), GreaterThanOrEqual(txn_date, 2026-01-01)].',\n    upstreamStageIds: [],\n    downstreamStageIds: [1],\n    branch: 'left',\n  },\n  {\n    id: 1,\n    name: 'Exchange hashpartitioning',\n    callSite: 'df.repartitionByRange(200, col(\"customer_id\"))',\n    operator: 'Exchange hashpartitioning(customer_id#42, 200), ENSURE_REQUIREMENTS, [plan_id=108]',\n    status: 'succeeded',\n    tasksTotal: 200,\n    tasksSucceeded: 200,\n    tasksFailed: 0,\n    duration: '42s',\n    durationSec: 42,\n    inputBytes: '0 B',\n    inputRecords: '14,820,400',\n    outputBytes: '8.4 GB',\n    outputRecords: '14,820,400',\n    shuffleReadBytes: '14.2 GB',\n    shuffleWriteBytes: '8.4 GB',\n    spillMemory: '0 B',\n    spillDisk: '0 B',\n    skewRatio: 1.15,\n    quantiles: { min: 1.8, p25: 2.4, median: 2.9, p75: 3.2, p95: 3.4, max: 3.6 },\n    taskBreakdown: {\n      executorRunTime: '2.74s',\n      jvmGcTime: '58ms',\n      shuffleFetchWait: '92ms',\n      serializationTime: '18ms',\n      deserializationTime: '22ms',\n      peakMemory: '240 MB',\n    },\n    details:\n      'Shuffle exchange hash-partitioning 14.82M rows across 200 AQE-coalesced partition buckets based on Murmur3 hash of customer_id column.',\n    upstreamStageIds: [0],\n    downstreamStageIds: [2],\n    branch: 'left',\n  },\n  {\n    id: 2,\n    name: 'HashAggregate & Filter',\n    callSite: 'df.groupBy(\"customer_id\").agg(sum(\"amount\").as(\"total_spend\"), count(\"txn_id\").as(\"tx_count\"))',\n    operator: 'HashAggregate(keys=[customer_id#42], functions=[sum(amount#43), count(txn_id#45)])',\n    status: 'succeeded',\n    tasksTotal: 200,\n    tasksSucceeded: 200,\n    tasksFailed: 0,\n    duration: '1m 12s',\n    durationSec: 72,\n    inputBytes: '0 B',\n    inputRecords: '14,820,400',\n    outputBytes: '4.6 GB',\n    outputRecords: '1,480,000',\n    shuffleReadBytes: '8.4 GB',\n    shuffleWriteBytes: '4.6 GB',\n    spillMemory: '420 MB',\n    spillDisk: '0 B',\n    skewRatio: 1.18,\n    quantiles: { min: 2.1, p25: 2.8, median: 3.5, p75: 3.9, p95: 4.1, max: 4.2 },\n    taskBreakdown: {\n      executorRunTime: '3.42s',\n      jvmGcTime: '74ms',\n      shuffleFetchWait: '110ms',\n      serializationTime: '14ms',\n      deserializationTime: '24ms',\n      peakMemory: '342 MB',\n    },\n    details:\n      'Aggregated raw transaction events down to 1.48M unique customer lifetime metrics. In-memory hash aggregation spilled 420 MB into JVM execution memory off-heap storage.',\n    upstreamStageIds: [1],\n    downstreamStageIds: [3],\n    branch: 'left',\n  },\n  {\n    id: 4,\n    name: 'FileScan customer_dim',\n    callSite: 'spark.read.parquet(\"s3://warehouse/customer_dim\")',\n    operator: 'FileScan parquet default.customer_dim [customer_id#98, full_name#99, tier#100, signup_date#101]',\n    status: 'succeeded',\n    tasksTotal: 120,\n    tasksSucceeded: 120,\n    tasksFailed: 0,\n    duration: '12s',\n    durationSec: 12,\n    inputBytes: '3.1 GB',\n    inputRecords: '2,400,000',\n    outputBytes: '3.1 GB',\n    outputRecords: '2,400,000',\n    shuffleReadBytes: '0 B',\n    shuffleWriteBytes: '0 B',\n    spillMemory: '0 B',\n    spillDisk: '0 B',\n    skewRatio: 1.09,\n    quantiles: { min: 0.6, p25: 0.9, median: 1.1, p75: 1.3, p95: 1.4, max: 1.5 },\n    taskBreakdown: {\n      executorRunTime: '1.04s',\n      jvmGcTime: '22ms',\n      shuffleFetchWait: '0ms',\n      serializationTime: '10ms',\n      deserializationTime: '18ms',\n      peakMemory: '96 MB',\n    },\n    details: 'Read customer demographic and subscription tier metadata from AWS S3 parquet catalog.',\n    upstreamStageIds: [],\n    downstreamStageIds: [5],\n    branch: 'right',\n  },\n  {\n    id: 5,\n    name: 'Exchange customer_dim',\n    callSite: 'customerDim.repartition(200, col(\"customer_id\"))',\n    operator: 'Exchange hashpartitioning(customer_id#98, 200), ENSURE_REQUIREMENTS, [plan_id=142]',\n    status: 'succeeded',\n    tasksTotal: 120,\n    tasksSucceeded: 120,\n    tasksFailed: 0,\n    duration: '16s',\n    durationSec: 16,\n    inputBytes: '0 B',\n    inputRecords: '2,400,000',\n    outputBytes: '3.1 GB',\n    outputRecords: '2,400,000',\n    shuffleReadBytes: '3.1 GB',\n    shuffleWriteBytes: '3.1 GB',\n    spillMemory: '0 B',\n    spillDisk: '0 B',\n    skewRatio: 1.11,\n    quantiles: { min: 0.8, p25: 1.1, median: 1.3, p75: 1.5, p95: 1.7, max: 1.8 },\n    taskBreakdown: {\n      executorRunTime: '1.24s',\n      jvmGcTime: '26ms',\n      shuffleFetchWait: '48ms',\n      serializationTime: '12ms',\n      deserializationTime: '16ms',\n      peakMemory: '160 MB',\n    },\n    details:\n      'Shuffle partitioned customer dimension records across 200 partitions matching left transaction dataset partition key.',\n    upstreamStageIds: [4],\n    downstreamStageIds: [3],\n    branch: 'right',\n  },\n  {\n    id: 3,\n    name: 'SortMergeJoin',\n    callSite: 'txnAgg.join(customerDim, col(\"customer_id#42\") === col(\"customer_id#98\"), \"inner\")',\n    operator: 'SortMergeJoin [customer_id#42], [customer_id#98], Inner',\n    status: 'succeeded',\n    tasksTotal: 200,\n    tasksSucceeded: 200,\n    tasksFailed: 0,\n    duration: '1m 20s',\n    durationSec: 80,\n    inputBytes: '0 B',\n    inputRecords: '1,480,000',\n    outputBytes: '29.8 GB',\n    outputRecords: '1,240,000 (Joined 14.8M keys)',\n    shuffleReadBytes: '7.7 GB',\n    shuffleWriteBytes: '29.8 GB',\n    spillMemory: '1.06 GB',\n    spillDisk: '0 B',\n    skewRatio: 1.21,\n    quantiles: { min: 2.4, p25: 3.1, median: 3.8, p75: 4.3, p95: 4.6, max: 4.8 },\n    taskBreakdown: {\n      executorRunTime: '3.72s',\n      jvmGcTime: '82ms',\n      shuffleFetchWait: '140ms',\n      serializationTime: '22ms',\n      deserializationTime: '32ms',\n      peakMemory: '480 MB',\n    },\n    details:\n      'Executed distributed SortMergeJoin matching customer_id keys across left aggregated metrics and right dimension tables. AQE coalesced skewed partitions and eliminated full table broadcast risk.',\n    upstreamStageIds: [2, 5],\n    downstreamStageIds: [],\n    branch: 'join',\n  },\n]\n",
      "type": "registry:page",
      "target": "~/app/components/blocks/spark-stage-data.ts"
    },
    {
      "path": "packages/registry-vue/blocks/spark-job-stage-visualizer/spark-stage-types.ts",
      "content": "export type StageStatus = 'succeeded' | 'running' | 'failed' | 'pending'\nexport type ExecutorStatus = 'active' | 'dead' | 'excluded'\n\nexport interface TaskQuantiles {\n  min: number\n  p25: number\n  median: number\n  p75: number\n  p95: number\n  max: number\n}\n\nexport interface TaskBreakdownMetrics {\n  executorRunTime: string\n  jvmGcTime: string\n  shuffleFetchWait: string\n  serializationTime: string\n  deserializationTime: string\n  peakMemory: string\n}\n\nexport interface SparkStage {\n  id: number\n  name: string\n  callSite: string\n  operator: string\n  status: StageStatus\n  tasksTotal: number\n  tasksSucceeded: number\n  tasksFailed: number\n  duration: string\n  durationSec: number\n  inputBytes: string\n  inputRecords: string\n  outputBytes: string\n  outputRecords: string\n  shuffleReadBytes: string\n  shuffleWriteBytes: string\n  spillMemory: string\n  spillDisk: string\n  skewRatio: number\n  quantiles: TaskQuantiles\n  taskBreakdown: TaskBreakdownMetrics\n  details: string\n  upstreamStageIds: number[]\n  downstreamStageIds: number[]\n  branch: 'left' | 'right' | 'join'\n}\n\nexport interface SparkExecutor {\n  id: string\n  host: string\n  role: 'driver' | 'executor'\n  status: ExecutorStatus\n  cores: number\n  memoryUsedGB: number\n  memoryTotalGB: number\n  memoryPercent: number\n  gcTimeSec: number\n  gcPercent: number\n  tasksCompleted: number\n  tasksTotal: number\n  tasksFailed: number\n  shuffleReadGB: number\n  shuffleWriteGB: number\n  spillGB: number\n}\n\nexport interface AqeRule {\n  id: string\n  name: string\n  status: 'applied' | 'skipped'\n  description: string\n  impact: string\n}\n",
      "type": "registry:page",
      "target": "~/app/components/blocks/spark-stage-types.ts"
    }
  ],
  "dependencies": [
    "lucide-vue-next"
  ],
  "devDependencies": [],
  "registryDependencies": [
    "https://uipkge.dev/r/vue/badge.json",
    "https://uipkge.dev/r/vue/button.json",
    "https://uipkge.dev/r/vue/card.json",
    "https://uipkge.dev/r/vue/progress.json",
    "https://uipkge.dev/r/vue/table.json",
    "https://uipkge.dev/r/vue/tabs.json"
  ],
  "description": "Databricks and Apache Spark distributed job execution DAG visualizer with stage performance metrics, task skew distribution quantiles, and executor JVM GC telemetry.",
  "categories": [
    "devops",
    "dashboard",
    "analytics",
    "data",
    "app"
  ]
}