{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "parquet-metadata-inspector",
  "title": "Parquet Metadata Inspector",
  "type": "registry:page",
  "files": [
    {
      "path": "packages/registry-angular/blocks/parquet-metadata-inspector/parquet-metadata-inspector.component.ts",
      "content": "// Apache Parquet file deep-dive inspector: file header with S3 URI copy + header-JSON\n// copy/download, four telemetry cards, a row-group switcher banner, and a four-tab view\n// (column-chunk statistics with search + encoding filter, schema tree, dictionary metrics,\n// Thrift FileMetaData JSON). Consolidates the five Vue SFCs + data files 1:1; the data,\n// types and schema siblings are verbatim copies of the Vue sources.\nimport { ChangeDetectionStrategy, Component, Input, computed, signal } from '@angular/core'\nimport {\n  ArrowRight,\n  Binary,\n  Check,\n  Code2,\n  Columns3,\n  Copy,\n  Download,\n  FileCode2,\n  FolderTree,\n  HardDrive,\n  Layers,\n  LucideAngularModule,\n  Search,\n  Table2,\n} from 'lucide-angular'\nimport { cn } from '@/lib/utils'\nimport { UiBadgeComponent } from '@/ui/badge/badge.component'\nimport { UiButtonComponent } from '@/ui/button/button.component'\nimport {\n  UiCardComponent,\n  UiCardContentComponent,\n  UiCardDescriptionComponent,\n  UiCardHeaderComponent,\n  UiCardTitleComponent,\n} from '@/ui/card/card.component'\nimport { UiProgressComponent } from '@/ui/progress/progress.component'\nimport {\n  UiTableBodyComponent,\n  UiTableCellComponent,\n  UiTableComponent,\n  UiTableHeadComponent,\n  UiTableHeaderComponent,\n  UiTableRowComponent,\n} from '@/ui/table/table.component'\nimport {\n  UiTabsComponent,\n  UiTabsContentComponent,\n  UiTabsListComponent,\n  UiTabsTriggerComponent,\n} from '@/ui/tabs/tabs.component'\nimport { defaultRowGroupsData, parquetHeaderJson } from './parquet-metadata-data'\nimport type { ColumnChunk, RowGroupMeta } from './parquet-metadata-types'\nimport { schemaTreeNodes, type SchemaTreeNode } from './parquet-schema'\n\nexport type { ColumnChunk, RowGroupMeta }\n\n@Component({\n  changeDetection: ChangeDetectionStrategy.Eager,\n  selector: 'ui-parquet-metadata-inspector, [ui-parquet-metadata-inspector]',\n  standalone: true,\n  // React renders the root <div> itself: the host stays out of layout and `class` goes to the root.\n  host: { '[attr.class]': '\"contents\"' },\n  imports: [\n    LucideAngularModule,\n    UiBadgeComponent,\n    UiButtonComponent,\n    UiCardComponent,\n    UiCardContentComponent,\n    UiCardDescriptionComponent,\n    UiCardHeaderComponent,\n    UiCardTitleComponent,\n    UiProgressComponent,\n    UiTableBodyComponent,\n    UiTableCellComponent,\n    UiTableComponent,\n    UiTableHeadComponent,\n    UiTableHeaderComponent,\n    UiTableRowComponent,\n    UiTabsComponent,\n    UiTabsContentComponent,\n    UiTabsListComponent,\n    UiTabsTriggerComponent,\n  ],\n  template: `\n    <div data-slot=\"parquet-metadata-inspector\" [class]=\"rootClass\">\n      <!-- Top Parquet File Header -->\n      <header class=\"border-border bg-card/70 border-b px-4 py-4 sm:px-6\">\n        <div class=\"flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between\">\n          <div class=\"space-y-1.5\">\n            <div class=\"flex flex-wrap items-center gap-2.5\">\n              <div class=\"bg-primary/10 text-primary flex size-8 items-center justify-center rounded-lg\">\n                <lucide-icon [img]=\"FileCode2\" class=\"size-4\" />\n              </div>\n              <h2 class=\"font-mono text-sm font-semibold tracking-tight break-all sm:text-base\">\n                {{ filePath() }}\n              </h2>\n              <button\n                type=\"button\"\n                class=\"text-muted-foreground hover:text-foreground cursor-pointer transition-colors\"\n                aria-label=\"Copy S3 URI\"\n                (click)=\"copyUriToClipboard()\"\n              >\n                @if (copiedUri()) {\n                  <lucide-icon [img]=\"Check\" class=\"text-success size-3.5\" />\n                } @else {\n                  <lucide-icon [img]=\"Copy\" class=\"size-3.5\" />\n                }\n              </button>\n            </div>\n\n            <div class=\"flex flex-wrap items-center gap-x-3 gap-y-1.5 text-xs\">\n              <span ui-badge variant=\"outline\" class=\"gap-1.5 font-mono text-xs font-normal\">\n                <span class=\"bg-success size-1.5 rounded-full\"></span>\n                <span>{{ formatSpec() }}</span>\n              </span>\n              <span class=\"text-muted-foreground\">\n                Total Rows:\n                <strong class=\"text-foreground font-semibold tabular-nums\">{{ totalRows() }}</strong>\n              </span>\n              <div\n                class=\"border-success/30 bg-success/10 text-success inline-flex items-center gap-1 rounded-md border px-2 py-0.5 font-mono text-xs font-medium\"\n              >\n                <span>{{ fileSize() }}</span>\n                <span>·</span>\n                <span class=\"tabular-nums\">{{ compressionRatio() }} Compression Ratio</span>\n              </div>\n            </div>\n          </div>\n\n          <div class=\"flex flex-wrap items-center gap-2\">\n            <button\n              ui-button\n              variant=\"outline\"\n              size=\"sm\"\n              class=\"h-8 gap-1.5 text-xs shadow-xs\"\n              (click)=\"copyJsonToClipboard()\"\n            >\n              @if (copiedJson()) {\n                <lucide-icon [img]=\"Check\" class=\"text-success size-3.5\" />\n              } @else {\n                <lucide-icon [img]=\"Copy\" class=\"size-3.5\" />\n              }\n              <span>{{ copiedJson() ? 'JSON Copied' : 'Copy Header JSON' }}</span>\n            </button>\n\n            <button\n              ui-button\n              aria-label=\"Download attachment\"\n              size=\"sm\"\n              class=\"h-8 gap-1.5 text-xs shadow-xs\"\n              [disabled]=\"downloadingJson()\"\n              (click)=\"handleDownloadJson()\"\n            >\n              @if (!downloadingJson()) {\n                <lucide-icon [img]=\"Download\" class=\"size-3.5\" />\n              } @else {\n                <span class=\"size-3.5 animate-spin rounded-full border-2 border-current border-t-transparent\"></span>\n              }\n              <span>Download Parquet Header JSON</span>\n            </button>\n          </div>\n        </div>\n      </header>\n\n      <div class=\"space-y-6 p-4 sm:p-6\">\n        <!-- 4 Parquet File Telemetry Cards -->\n        <div class=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n          <div ui-card class=\"flex flex-col justify-between shadow-xs\">\n            <div ui-card-header class=\"pb-2\">\n              <div class=\"flex items-center justify-between gap-2\">\n                <div class=\"flex items-center gap-2\">\n                  <div\n                    class=\"border-info/20 bg-info/10 text-info flex size-7 items-center justify-center rounded-md border\"\n                  >\n                    <lucide-icon [img]=\"Layers\" class=\"size-3.5\" />\n                  </div>\n                  <h3 ui-card-title class=\"text-xs font-medium\">Row Groups</h3>\n                </div>\n                <span ui-badge variant=\"outline\" class=\"font-mono text-xs tabular-nums\">2 Groups</span>\n              </div>\n            </div>\n            <div ui-card-content class=\"space-y-2 pt-1\">\n              <div class=\"flex items-baseline gap-1.5\">\n                <span class=\"text-foreground text-2xl font-bold tracking-tight tabular-nums sm:text-3xl\">2</span>\n                <span class=\"text-muted-foreground text-xs font-medium\">Row Groups</span>\n              </div>\n              <div class=\"border-border/60 border-t pt-2 text-xs\">\n                <div class=\"text-muted-foreground flex items-center justify-between gap-x-2\">\n                  <span>Chunk Partitioning:</span>\n                  <span class=\"text-foreground font-medium tabular-nums\">125,000 rows / group</span>\n                </div>\n              </div>\n            </div>\n          </div>\n\n          <div ui-card class=\"flex flex-col justify-between shadow-xs\">\n            <div ui-card-header class=\"pb-2\">\n              <div class=\"flex items-center justify-between gap-2\">\n                <div class=\"flex items-center gap-2\">\n                  <div\n                    class=\"border-chart-1/20 bg-chart-1/10 text-chart-1 flex size-7 items-center justify-center rounded-md border\"\n                  >\n                    <lucide-icon [img]=\"Columns3\" class=\"size-3.5\" />\n                  </div>\n                  <h3 ui-card-title class=\"text-xs font-medium\">Columns / Schema</h3>\n                </div>\n                <span ui-badge variant=\"outline\" class=\"font-mono text-xs tabular-nums\">14 Chunks</span>\n              </div>\n            </div>\n            <div ui-card-content class=\"space-y-2 pt-1\">\n              <div class=\"flex items-baseline gap-1.5\">\n                <span class=\"text-foreground text-2xl font-bold tracking-tight tabular-nums sm:text-3xl\">14</span>\n                <span class=\"text-muted-foreground text-xs font-medium\">Column Chunks</span>\n              </div>\n              <div class=\"border-border/60 border-t pt-2 text-xs\">\n                <div class=\"text-muted-foreground flex items-center justify-between gap-x-2\">\n                  <span>Nested Topology:</span>\n                  <span class=\"text-foreground font-medium tabular-nums\">2 Structs · 1 List</span>\n                </div>\n              </div>\n            </div>\n          </div>\n\n          <div ui-card class=\"flex flex-col justify-between shadow-xs\">\n            <div ui-card-header class=\"pb-2\">\n              <div class=\"flex items-center justify-between gap-2\">\n                <div class=\"flex items-center gap-2\">\n                  <div\n                    class=\"border-success/20 bg-success/10 text-success flex size-7 items-center justify-center rounded-md border\"\n                  >\n                    <lucide-icon [img]=\"HardDrive\" class=\"size-3.5\" />\n                  </div>\n                  <h3 ui-card-title class=\"text-xs font-medium\">Uncompressed Size</h3>\n                </div>\n                <span\n                  ui-badge\n                  variant=\"secondary\"\n                  class=\"border-success/30 text-success font-mono text-xs tabular-nums\"\n                >\n                  3.88x Savings\n                </span>\n              </div>\n            </div>\n            <div ui-card-content class=\"space-y-2 pt-1\">\n              <div class=\"flex items-baseline gap-1.5\">\n                <span class=\"text-foreground text-2xl font-bold tracking-tight tabular-nums sm:text-3xl\">{{\n                  uncompressedSize()\n                }}</span>\n                <span class=\"text-muted-foreground text-xs font-medium\">Raw</span>\n              </div>\n              <div class=\"border-border/60 border-t pt-2 text-xs\">\n                <div class=\"text-muted-foreground flex items-center justify-between gap-x-2\">\n                  <span>Snappy Footprint:</span>\n                  <span class=\"text-foreground font-medium tabular-nums\">{{ fileSize() }} (Saved 53.0 MB)</span>\n                </div>\n              </div>\n            </div>\n          </div>\n\n          <div ui-card class=\"flex flex-col justify-between shadow-xs\">\n            <div ui-card-header class=\"pb-2\">\n              <div class=\"flex items-center justify-between gap-2\">\n                <div class=\"flex items-center gap-2\">\n                  <div\n                    class=\"border-warning/20 bg-warning/10 text-warning flex size-7 items-center justify-center rounded-md border\"\n                  >\n                    <lucide-icon [img]=\"Binary\" class=\"size-3.5\" />\n                  </div>\n                  <h3 ui-card-title class=\"text-xs font-medium\">Dictionary Encoding</h3>\n                </div>\n                <span ui-badge variant=\"outline\" class=\"font-mono text-xs tabular-nums\">85.7%</span>\n              </div>\n            </div>\n            <div ui-card-content class=\"space-y-2 pt-1\">\n              <div class=\"flex items-baseline gap-1.5\">\n                <span class=\"text-foreground text-2xl font-bold tracking-tight tabular-nums sm:text-3xl\">12 / 14</span>\n                <span class=\"text-muted-foreground text-xs font-medium\">Columns Encoded</span>\n              </div>\n              <div class=\"border-border/60 border-t pt-2 text-xs\">\n                <div class=\"text-muted-foreground flex items-center justify-between gap-x-2\">\n                  <span>Encoding Algorithms:</span>\n                  <span class=\"text-foreground font-medium\">RLE + PLAIN_DICT</span>\n                </div>\n              </div>\n            </div>\n          </div>\n        </div>\n\n        <!-- Row Groups Switcher Banner -->\n        <div ui-card class=\"border-border bg-card shadow-xs\">\n          <div ui-card-content class=\"p-3\">\n            <div class=\"flex flex-col gap-3 md:flex-row md:items-center md:justify-between\">\n              <div class=\"flex flex-wrap items-center gap-2\">\n                <span class=\"text-muted-foreground text-xs font-semibold tracking-wider uppercase\"\n                  >Inspect Row Group:</span\n                >\n                <div class=\"flex flex-wrap items-center gap-1.5\">\n                  @for (rg of rowGroups(); track rg.id) {\n                    <button type=\"button\" [class]=\"rowGroupClass(rg.id)\" (click)=\"selectedRowGroup.set(rg.id)\">\n                      <div class=\"flex items-center gap-2\">\n                        <lucide-icon [img]=\"Layers\" class=\"size-3.5 opacity-70\" />\n                        <span class=\"font-mono font-semibold\">Row Group #{{ rg.id }}</span>\n                        <span\n                          class=\"bg-muted text-muted-foreground rounded px-1.5 py-0.5 font-mono text-xs tabular-nums\"\n                        >\n                          {{ rg.numRowsFormatted }}\n                        </span>\n                        <span class=\"text-muted-foreground font-mono text-xs tabular-nums\">\n                          ({{ rg.totalCompressedSize }})\n                        </span>\n                      </div>\n                    </button>\n                  }\n                </div>\n              </div>\n\n              <div class=\"flex flex-wrap items-center gap-2 text-xs\">\n                <span class=\"text-muted-foreground whitespace-nowrap\">Row Group Offset:</span>\n                <span ui-badge variant=\"outline\" class=\"min-w-0 truncate font-mono text-xs\">{{\n                  activeRowGroup().fileOffset\n                }}</span>\n                <span class=\"text-muted-foreground\">Raw Footprint:</span>\n                <span class=\"text-foreground font-mono font-medium tabular-nums\">{{\n                  activeRowGroup().totalByteSize\n                }}</span>\n              </div>\n            </div>\n          </div>\n        </div>\n\n        <!-- Main Tabs View -->\n        <ui-tabs [value]=\"activeTab()\" (valueChange)=\"activeTab.set($event)\" class=\"w-full\">\n          <div class=\"flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between\">\n            <ui-tabs-list class=\"grid h-9 w-full grid-cols-2 p-1 sm:w-auto md:grid-cols-4\">\n              <button ui-tabs-trigger value=\"chunks\" class=\"gap-1.5 text-xs\">\n                <lucide-icon [img]=\"Table2\" class=\"size-3.5\" />\n                <span>Column Chunks</span>\n                <span class=\"bg-muted py-0.2 rounded-full px-1.5 font-mono text-xs\">{{\n                  activeRowGroup().columns.length\n                }}</span>\n              </button>\n              <button ui-tabs-trigger value=\"schema\" class=\"gap-1.5 text-xs\">\n                <lucide-icon [img]=\"FolderTree\" class=\"size-3.5\" />\n                <span>Schema Tree</span>\n                <span class=\"bg-muted py-0.2 rounded-full px-1.5 font-mono text-xs\">{{ nodes().length }}</span>\n              </button>\n              <button ui-tabs-trigger value=\"dictionary\" class=\"gap-1.5 text-xs\">\n                <lucide-icon [img]=\"Binary\" class=\"size-3.5\" />\n                <span>Dictionary Metrics</span>\n                <span class=\"bg-muted py-0.2 rounded-full px-1.5 font-mono text-xs\">{{\n                  dictionaryColumns().length\n                }}</span>\n              </button>\n              <button ui-tabs-trigger value=\"thrift\" class=\"gap-1.5 text-xs\">\n                <lucide-icon [img]=\"Code2\" class=\"size-3.5\" />\n                <span>Thrift Metadata</span>\n              </button>\n            </ui-tabs-list>\n\n            @if (activeTab() === 'chunks') {\n              <div class=\"flex flex-wrap items-center gap-2\">\n                <div class=\"relative w-full sm:w-56\">\n                  <lucide-icon\n                    [img]=\"Search\"\n                    class=\"text-muted-foreground absolute top-1/2 left-2.5 size-3.5 -translate-y-1/2\"\n                  />\n                  <input\n                    [value]=\"searchQuery()\"\n                    (input)=\"searchQuery.set($any($event.target).value)\"\n                    type=\"text\"\n                    placeholder=\"Search column chunks...\"\n                    class=\"border-border bg-background text-foreground focus-visible:border-ring focus-visible:ring-ring/50 h-8 w-full rounded-md border pr-2.5 pl-8 text-xs focus-visible:ring-2 focus-visible:outline-none\"\n                  />\n                </div>\n                <div class=\"bg-muted border-border flex items-center rounded-md border p-0.5 text-xs\">\n                  <button type=\"button\" [class]=\"encodingClass('all')\" (click)=\"encodingFilter.set('all')\">All</button>\n                  <button\n                    type=\"button\"\n                    [class]=\"encodingClass('dictionary')\"\n                    (click)=\"encodingFilter.set('dictionary')\"\n                  >\n                    Dict Encoded\n                  </button>\n                  <button type=\"button\" [class]=\"encodingClass('plain')\" (click)=\"encodingFilter.set('plain')\">\n                    Plain\n                  </button>\n                </div>\n              </div>\n            }\n          </div>\n\n          <!-- TAB 1: Column Chunks Breakdown Table -->\n          <ui-tabs-content value=\"chunks\" class=\"mt-4 space-y-4\">\n            <div ui-card class=\"overflow-hidden border shadow-none\">\n              <div class=\"overflow-x-auto\">\n                <table ui-table>\n                  <thead ui-table-header>\n                    <tr ui-table-row class=\"bg-muted/40 hover:bg-muted/40\">\n                      <th ui-table-head class=\"min-w-[200px] text-xs font-semibold\">Column Name & Type</th>\n                      <th ui-table-head class=\"min-w-[170px] text-xs font-semibold\">Encoding & Codec</th>\n                      <th ui-table-head class=\"min-w-[280px] text-xs font-semibold\">Min / Max Column Statistics</th>\n                      <th ui-table-head class=\"min-w-[130px] text-xs font-semibold\">Null Values</th>\n                      <th ui-table-head class=\"min-w-[190px] text-xs font-semibold\">Compressed / Raw Footprint</th>\n                    </tr>\n                  </thead>\n                  <tbody ui-table-body>\n                    @for (col of filteredColumns(); track col.name) {\n                      <tr ui-table-row class=\"text-xs\">\n                        <td ui-table-cell>\n                          <div class=\"space-y-1\">\n                            <div class=\"flex items-center gap-1.5\">\n                              <span class=\"text-foreground font-mono text-xs font-semibold\">{{ col.name }}</span>\n                              <span ui-badge variant=\"outline\" class=\"font-mono text-xs\">ID: {{ col.fieldId }}</span>\n                            </div>\n                            <div class=\"flex flex-wrap items-center gap-1 text-xs\">\n                              <span ui-badge variant=\"secondary\" class=\"font-mono text-xs font-normal\">\n                                {{ col.physicalType }}\n                              </span>\n                              <lucide-icon [img]=\"ArrowRight\" class=\"text-muted-foreground size-2.5\" />\n                              <span class=\"text-muted-foreground font-mono text-xs\">{{ col.logicalType }}</span>\n                            </div>\n                          </div>\n                        </td>\n                        <td ui-table-cell>\n                          <div class=\"space-y-1.5\">\n                            <div class=\"flex flex-wrap items-center gap-1\">\n                              @for (enc of col.encodings; track enc) {\n                                <span\n                                  ui-badge\n                                  [variant]=\"enc.includes('DICTIONARY') ? 'default' : 'outline'\"\n                                  class=\"font-mono text-xs font-normal\"\n                                >\n                                  {{ enc }}\n                                </span>\n                              }\n                            </div>\n                            <div class=\"text-muted-foreground flex items-center gap-1.5 text-xs\">\n                              <span class=\"text-foreground font-medium\">Codec:</span>\n                              <span ui-badge variant=\"secondary\" class=\"font-mono text-xs\">{{ col.compression }}</span>\n                            </div>\n                          </div>\n                        </td>\n                        <td ui-table-cell>\n                          <div class=\"border-border/70 bg-muted/20 space-y-1 rounded-md border p-2 font-mono text-xs\">\n                            <div class=\"flex items-baseline gap-1.5\">\n                              <span class=\"text-muted-foreground shrink-0\">Min:</span>\n                              <span class=\"text-foreground truncate font-medium\" [attr.title]=\"col.minStat\">{{\n                                col.minStat\n                              }}</span>\n                            </div>\n                            <div class=\"flex items-baseline gap-1.5\">\n                              <span class=\"text-muted-foreground shrink-0\">Max:</span>\n                              <span class=\"text-foreground truncate font-medium\" [attr.title]=\"col.maxStat\">{{\n                                col.maxStat\n                              }}</span>\n                            </div>\n                          </div>\n                        </td>\n                        <td ui-table-cell>\n                          <div class=\"space-y-1\">\n                            <div class=\"flex items-center gap-1.5\">\n                              <span\n                                ui-badge\n                                [variant]=\"col.nullCount === 0 ? 'secondary' : 'outline'\"\n                                [class]=\"\n                                  col.nullCount === 0\n                                    ? 'border-success/30 text-success font-mono text-xs'\n                                    : 'font-mono text-xs'\n                                \"\n                              >\n                                {{ col.nullCount }} nulls\n                              </span>\n                            </div>\n                            <p class=\"text-muted-foreground font-mono text-xs tabular-nums\">\n                              {{ col.nullPct }} of {{ col.numValues }} rows\n                            </p>\n                          </div>\n                        </td>\n                        <td ui-table-cell>\n                          <div class=\"space-y-1.5\">\n                            <div class=\"flex items-center justify-between gap-x-2 font-mono text-xs\">\n                              <span class=\"text-foreground font-semibold tabular-nums\">{{ col.compressedBytes }}</span>\n                              <span class=\"text-muted-foreground tabular-nums\">/ {{ col.uncompressedBytes }}</span>\n                            </div>\n                            <div class=\"space-y-1\">\n                              <ui-progress [value]=\"col.compressionPct\" class=\"h-1.5\" />\n                              <div class=\"text-muted-foreground flex items-center justify-between gap-x-2 text-xs\">\n                                <span class=\"text-success font-mono font-medium tabular-nums\"\n                                  >{{ col.compressionRatio }} saved</span\n                                >\n                                <span class=\"font-mono text-xs\">Offset: {{ col.dataPageOffset }}</span>\n                              </div>\n                            </div>\n                          </div>\n                        </td>\n                      </tr>\n                    }\n                    @if (filteredColumns().length === 0) {\n                      <tr ui-table-row>\n                        <td ui-table-cell colspan=\"5\" class=\"text-muted-foreground py-8 text-center text-xs\">\n                          No matching column chunks found for filter criteria.\n                        </td>\n                      </tr>\n                    }\n                  </tbody>\n                </table>\n              </div>\n            </div>\n          </ui-tabs-content>\n\n          <!-- TAB 2: Parquet File Schema Tree Visualizer -->\n          <ui-tabs-content value=\"schema\" class=\"mt-4 space-y-4\">\n            <div ui-card class=\"shadow-none\">\n              <div ui-card-header class=\"border-border border-b pb-3\">\n                <div class=\"flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between\">\n                  <div>\n                    <h3 ui-card-title class=\"text-sm font-semibold\">Parquet Schema Definition & Field Hierarchy</h3>\n                    <p ui-card-description class=\"text-xs\">\n                      Hierarchical view of logical schema nodes, repetition rules (REQUIRED, OPTIONAL, REPEATED), and\n                      maximum definition / repetition levels for column projection.\n                    </p>\n                  </div>\n                  <div class=\"flex flex-wrap items-center gap-1.5\">\n                    <span ui-badge variant=\"outline\" class=\"font-mono text-xs\">D: Def Level</span>\n                    <span ui-badge variant=\"outline\" class=\"font-mono text-xs\">R: Rep Level</span>\n                  </div>\n                </div>\n              </div>\n              <div ui-card-content class=\"p-0\">\n                <div class=\"divide-border divide-y\">\n                  <div class=\"bg-muted/40 flex items-center justify-between gap-x-2 px-4 py-2.5 font-mono text-xs\">\n                    <div class=\"flex items-center gap-2\">\n                      <lucide-icon [img]=\"FolderTree\" class=\"text-primary size-4\" />\n                      <span class=\"text-foreground font-bold\">message schema</span>\n                      <span ui-badge variant=\"secondary\" class=\"font-mono text-xs\">Root Message</span>\n                    </div>\n                    <span class=\"text-muted-foreground font-mono text-xs\">14 column fields</span>\n                  </div>\n\n                  @for (node of nodes(); track node.id) {\n                    <div\n                      class=\"hover:bg-muted/30 flex flex-col justify-between gap-2 px-4 py-3 text-xs transition-colors md:flex-row md:items-center\"\n                      [style.padding-left.rem]=\"indentRem(node.depth)\"\n                    >\n                      <div class=\"flex min-w-0 items-center gap-2\">\n                        <span class=\"text-muted-foreground font-mono select-none\">\n                          {{ node.depth === 0 ? '├─' : '└─' }}\n                        </span>\n                        <span [class]=\"nodeNameClass(node.isGroup)\">\n                          {{ node.name }}\n                        </span>\n                        <span\n                          ui-badge\n                          [variant]=\"\n                            node.repetition === 'REQUIRED'\n                              ? 'default'\n                              : node.repetition === 'REPEATED'\n                                ? 'secondary'\n                                : 'outline'\n                          \"\n                          class=\"font-mono text-xs font-normal\"\n                        >\n                          {{ node.repetition }}\n                        </span>\n                        <span ui-badge variant=\"secondary\" class=\"font-mono text-xs font-normal\">\n                          {{ node.physicalType }}\n                        </span>\n                        <lucide-icon [img]=\"ArrowRight\" class=\"text-muted-foreground size-3 shrink-0\" />\n                        <span ui-badge variant=\"outline\" class=\"text-muted-foreground font-mono text-xs font-normal\">\n                          {{ node.logicalType }}\n                        </span>\n                      </div>\n\n                      <div class=\"flex shrink-0 flex-wrap items-center gap-2\">\n                        <span\n                          class=\"text-muted-foreground hidden max-w-[220px] truncate text-xs lg:inline-block\"\n                          [attr.title]=\"node.description\"\n                        >\n                          {{ node.description }}\n                        </span>\n                        <div\n                          class=\"bg-muted/60 border-border flex items-center gap-2 rounded border px-2 py-0.5 font-mono text-xs\"\n                        >\n                          <span title=\"Max Definition Level\"\n                            >max_def:\n                            <strong class=\"text-foreground font-bold tabular-nums\">{{ node.defLevel }}</strong></span\n                          >\n                          <span class=\"text-muted-foreground\">|</span>\n                          <span title=\"Max Repetition Level\"\n                            >max_rep:\n                            <strong class=\"text-foreground font-bold tabular-nums\">{{ node.repLevel }}</strong></span\n                          >\n                        </div>\n                        <span ui-badge variant=\"outline\" class=\"font-mono text-xs\">ID: {{ node.id }}</span>\n                      </div>\n                    </div>\n                  }\n                </div>\n              </div>\n            </div>\n          </ui-tabs-content>\n\n          <!-- TAB 3: Dictionary & Compression Deep Dive -->\n          <ui-tabs-content value=\"dictionary\" class=\"mt-4 space-y-4\">\n            <div class=\"grid grid-cols-1 gap-4 lg:grid-cols-3\">\n              <div ui-card class=\"shadow-none lg:col-span-1\">\n                <div ui-card-header class=\"pb-3\">\n                  <h3 ui-card-title class=\"text-sm font-semibold\">Dictionary Optimization Impact</h3>\n                  <p ui-card-description class=\"text-xs\">\n                    Plain dictionary replaces repeated byte strings and integers with compact 3-bit to 16-bit dictionary\n                    index pointers.\n                  </p>\n                </div>\n                <div ui-card-content class=\"space-y-4 text-xs\">\n                  <div class=\"border-border bg-muted/20 space-y-2 rounded-lg border p-3\">\n                    <div class=\"flex items-center justify-between gap-x-2\">\n                      <span class=\"text-muted-foreground\">Dictionary Enabled Columns:</span>\n                      <span class=\"text-foreground font-mono font-semibold\">6 / 8 Monitored</span>\n                    </div>\n                    <div class=\"flex items-center justify-between gap-x-2\">\n                      <span class=\"text-muted-foreground\">Avg Space Reduction:</span>\n                      <span class=\"text-success font-mono font-semibold tabular-nums\">78.4%</span>\n                    </div>\n                    <div class=\"flex items-center justify-between gap-x-2\">\n                      <span class=\"text-muted-foreground\">Dictionary Page Overhead:</span>\n                      <span class=\"text-foreground font-mono font-semibold tabular-nums\">0.34 MB total</span>\n                    </div>\n                    <div class=\"flex items-center justify-between gap-x-2\">\n                      <span class=\"text-muted-foreground\">RLE Run Length Efficiency:</span>\n                      <span class=\"text-foreground font-mono font-semibold\">Optimal</span>\n                    </div>\n                  </div>\n\n                  <div class=\"space-y-2\">\n                    <span class=\"text-muted-foreground font-medium\">Compression Distribution by Codec:</span>\n                    <div class=\"space-y-1.5\">\n                      <div class=\"flex items-center justify-between gap-x-2 font-mono\">\n                        <span>Snappy + RLE Dict</span>\n                        <span class=\"font-semibold tabular-nums\">74.2%</span>\n                      </div>\n                      <ui-progress [value]=\"74.2\" class=\"h-1.5\" />\n                    </div>\n                  </div>\n                </div>\n              </div>\n\n              <div ui-card class=\"overflow-hidden shadow-none lg:col-span-2\">\n                <div class=\"overflow-x-auto\">\n                  <table ui-table>\n                    <thead ui-table-header>\n                      <tr ui-table-row class=\"bg-muted/40 hover:bg-muted/40\">\n                        <th ui-table-head class=\"text-xs font-semibold\">Column</th>\n                        <th ui-table-head class=\"text-xs font-semibold\">Distinct Entries</th>\n                        <th ui-table-head class=\"text-xs font-semibold\">Bit Width</th>\n                        <th ui-table-head class=\"text-xs font-semibold\">Dict Page Offset</th>\n                        <th ui-table-head class=\"text-xs font-semibold\">Space Savings</th>\n                      </tr>\n                    </thead>\n                    <tbody ui-table-body>\n                      @for (col of dictionaryColumns(); track col.name) {\n                        <tr ui-table-row class=\"text-xs\">\n                          <td ui-table-cell class=\"text-foreground font-mono font-medium\">{{ col.name }}</td>\n                          <td ui-table-cell class=\"font-mono tabular-nums\">\n                            <span ui-badge variant=\"secondary\" class=\"font-mono text-xs\">\n                              {{ col.dictEntries?.toLocaleString() }} values\n                            </span>\n                          </td>\n                          <td ui-table-cell class=\"font-mono tabular-nums\">{{ col.bitWidth }} bits / row</td>\n                          <td ui-table-cell class=\"text-muted-foreground font-mono text-xs\">\n                            {{ col.dictPageOffset }}\n                          </td>\n                          <td ui-table-cell>\n                            <div class=\"flex items-center gap-2\">\n                              <span class=\"text-success font-mono font-semibold tabular-nums\">\n                                {{ col.compressionRatio }}\n                              </span>\n                              <span class=\"text-muted-foreground font-mono text-xs\">({{ col.compressedBytes }})</span>\n                            </div>\n                          </td>\n                        </tr>\n                      }\n                    </tbody>\n                  </table>\n                </div>\n              </div>\n            </div>\n          </ui-tabs-content>\n\n          <!-- TAB 4: Parquet Header JSON (Thrift) -->\n          <ui-tabs-content value=\"thrift\" class=\"mt-4 space-y-4\">\n            <div class=\"border-border bg-muted/30 overflow-hidden rounded-lg border\">\n              <div class=\"border-border bg-card flex items-center justify-between gap-x-2 border-b px-4 py-2.5\">\n                <div class=\"flex items-center gap-2\">\n                  <lucide-icon [img]=\"Code2\" class=\"text-primary size-4\" />\n                  <span class=\"text-foreground font-mono text-xs font-medium\">FileMetaData.thrift.json</span>\n                  <span ui-badge variant=\"secondary\" class=\"font-mono text-xs\">Thrift Compact Protocol</span>\n                </div>\n                <button ui-button variant=\"ghost\" size=\"sm\" class=\"h-7 gap-1 text-xs\" (click)=\"copyJsonToClipboard()\">\n                  @if (copiedJson()) {\n                    <lucide-icon [img]=\"Check\" class=\"text-success size-3\" />\n                  } @else {\n                    <lucide-icon [img]=\"Copy\" class=\"size-3\" />\n                  }\n                  <span>{{ copiedJson() ? 'Copied' : 'Copy JSON' }}</span>\n                </button>\n              </div>\n\n              <div\n                class=\"overflow-x-auto bg-neutral-950 p-4 font-mono text-xs leading-relaxed text-neutral-100 dark:bg-neutral-950\"\n              >\n                <pre class=\"whitespace-pre\"><code>{{ headerJson() }}</code></pre>\n              </div>\n            </div>\n          </ui-tabs-content>\n        </ui-tabs>\n      </div>\n    </div>\n  `,\n})\nexport class UiParquetMetadataInspectorComponent {\n  protected readonly ArrowRight = ArrowRight\n  protected readonly Binary = Binary\n  protected readonly Check = Check\n  protected readonly Code2 = Code2\n  protected readonly Columns3 = Columns3\n  protected readonly Copy = Copy\n  protected readonly Download = Download\n  protected readonly FileCode2 = FileCode2\n  protected readonly FolderTree = FolderTree\n  protected readonly HardDrive = HardDrive\n  protected readonly Layers = Layers\n  protected readonly Search = Search\n  protected readonly Table2 = Table2\n\n  readonly filePath = signal('s3://lakehouse-gold/orders_2026_q3_part0042.parquet')\n  readonly formatSpec = signal('Apache Parquet v2.10 · Snappy Compressed')\n  readonly totalRows = signal('250,000 Rows')\n  readonly fileSize = signal('18.4 MB')\n  readonly compressionRatio = signal('74.2%')\n  readonly uncompressedSize = signal('71.4 MB')\n  readonly rowGroups = signal<RowGroupMeta[]>(defaultRowGroupsData.map((rg) => ({ ...rg, columns: [...rg.columns] })))\n\n  @Input('filePath') set filePathAttr(v: string | undefined) {\n    if (v !== undefined) this.filePath.set(v)\n  }\n  @Input('formatSpec') set formatSpecAttr(v: string | undefined) {\n    if (v !== undefined) this.formatSpec.set(v)\n  }\n  @Input('totalRows') set totalRowsAttr(v: string | undefined) {\n    if (v !== undefined) this.totalRows.set(v)\n  }\n  @Input('fileSize') set fileSizeAttr(v: string | undefined) {\n    if (v !== undefined) this.fileSize.set(v)\n  }\n  @Input('compressionRatio') set compressionRatioAttr(v: string | undefined) {\n    if (v !== undefined) this.compressionRatio.set(v)\n  }\n  @Input('uncompressedSize') set uncompressedSizeAttr(v: string | undefined) {\n    if (v !== undefined) this.uncompressedSize.set(v)\n  }\n  @Input('rowGroups') set rowGroupsAttr(v: RowGroupMeta[] | undefined) {\n    if (v !== undefined) this.rowGroups.set(v.map((rg) => ({ ...rg, columns: [...rg.columns] })))\n  }\n  @Input('class') className?: string\n\n  readonly selectedRowGroup = signal(0)\n  readonly activeTab = signal('chunks')\n  readonly searchQuery = signal('')\n  readonly encodingFilter = signal<'all' | 'dictionary' | 'plain'>('all')\n  readonly copiedJson = signal(false)\n  readonly copiedUri = signal(false)\n  readonly downloadingJson = signal(false)\n\n  protected readonly nodes = signal<SchemaTreeNode[]>(schemaTreeNodes)\n\n  readonly activeRowGroup = computed(() => {\n    const groups = this.rowGroups()\n    return groups[this.selectedRowGroup()] ?? groups[0]!\n  })\n\n  readonly filteredColumns = computed(() => {\n    const query = this.searchQuery().trim().toLowerCase()\n    return this.activeRowGroup().columns.filter((col) => {\n      const matchesSearch =\n        !query ||\n        col.name.toLowerCase().includes(query) ||\n        col.physicalType.toLowerCase().includes(query) ||\n        col.logicalType.toLowerCase().includes(query) ||\n        col.encodings.some((enc) => enc.toLowerCase().includes(query))\n      const matchesEncoding =\n        this.encodingFilter() === 'all' ||\n        (this.encodingFilter() === 'dictionary' && col.isDictionaryEncoded) ||\n        (this.encodingFilter() === 'plain' && !col.isDictionaryEncoded)\n      return matchesSearch && matchesEncoding\n    })\n  })\n\n  readonly dictionaryColumns = computed(() => this.activeRowGroup().columns.filter((col) => col.isDictionaryEncoded))\n\n  headerJson(): string {\n    return parquetHeaderJson\n  }\n\n  get rootClass(): string {\n    return cn(\n      'bg-background text-foreground border-border w-full overflow-hidden rounded-xl border shadow-xs',\n      this.className,\n    )\n  }\n\n  rowGroupClass(id: number): string {\n    return cn(\n      'cursor-pointer rounded-lg border px-3 py-1.5 text-xs font-medium transition-colors',\n      this.selectedRowGroup() === id\n        ? 'bg-primary/10 text-primary border-primary/30 shadow-xs'\n        : 'bg-muted/40 text-muted-foreground hover:bg-muted hover:text-foreground border-transparent',\n    )\n  }\n\n  encodingClass(filter: 'all' | 'dictionary' | 'plain'): string {\n    return cn(\n      'cursor-pointer rounded px-2 py-1 transition-colors',\n      this.encodingFilter() === filter\n        ? 'bg-background text-foreground shadow-xs'\n        : 'text-muted-foreground hover:text-foreground',\n    )\n  }\n\n  nodeNameClass(isGroup?: boolean): string {\n    return cn('truncate font-mono font-medium', isGroup ? 'text-primary font-bold' : 'text-foreground')\n  }\n\n  indentRem(depth: number): number {\n    return Math.max(1, depth * 1.5 + 1)\n  }\n\n  copyJsonToClipboard(): void {\n    if (typeof navigator !== 'undefined' && navigator.clipboard) {\n      void navigator.clipboard.writeText(parquetHeaderJson)\n      this.copiedJson.set(true)\n      setTimeout(() => this.copiedJson.set(false), 2000)\n    }\n  }\n\n  copyUriToClipboard(): void {\n    if (typeof navigator !== 'undefined' && navigator.clipboard) {\n      void navigator.clipboard.writeText(this.filePath())\n      this.copiedUri.set(true)\n      setTimeout(() => this.copiedUri.set(false), 2000)\n    }\n  }\n\n  handleDownloadJson(): void {\n    this.downloadingJson.set(true)\n    setTimeout(() => {\n      if (typeof document !== 'undefined') {\n        const blob = new Blob([parquetHeaderJson], { type: 'application/json' })\n        const url = URL.createObjectURL(blob)\n        const a = document.createElement('a')\n        a.href = url\n        a.download = 'parquet-file-metadata.json'\n        document.body.appendChild(a)\n        a.click()\n        document.body.removeChild(a)\n        URL.revokeObjectURL(url)\n      }\n      this.downloadingJson.set(false)\n    }, 400)\n  }\n}\n",
      "type": "registry:page",
      "target": "~/src/app/components/blocks/parquet-metadata-inspector/parquet-metadata-inspector.component.ts"
    },
    {
      "path": "packages/registry-angular/blocks/parquet-metadata-inspector/parquet-metadata-data.ts",
      "content": "import type { RowGroupMeta } from './parquet-metadata-types'\n\nexport const parquetHeaderJson = `{\n  \"version\": 2,\n  \"created_by\": \"parquet-mr version 1.13.1 (build e29fa2b)\",\n  \"num_rows\": 250000,\n  \"num_row_groups\": 2,\n  \"file_size_bytes\": 19293798,\n  \"uncompressed_bytes\": 74868326,\n  \"compression_codec\": \"SNAPPY\",\n  \"encryption_algorithm\": null,\n  \"schema\": [\n    { \"name\": \"schema\", \"num_children\": 10 },\n    { \"name\": \"order_id\", \"type\": \"FIXED_LEN_BYTE_ARRAY\", \"type_length\": 16, \"repetition_type\": \"REQUIRED\", \"field_id\": 1, \"converted_type\": \"UUID\" },\n    { \"name\": \"customer_id\", \"type\": \"INT64\", \"repetition_type\": \"REQUIRED\", \"field_id\": 2, \"converted_type\": \"INT_64\" },\n    { \"name\": \"order_status\", \"type\": \"BYTE_ARRAY\", \"repetition_type\": \"OPTIONAL\", \"field_id\": 3, \"converted_type\": \"UTF8\" },\n    { \"name\": \"gross_amount\", \"type\": \"FIXED_LEN_BYTE_ARRAY\", \"type_length\": 9, \"repetition_type\": \"REQUIRED\", \"field_id\": 4, \"converted_type\": \"DECIMAL\", \"scale\": 2, \"precision\": 18 },\n    { \"name\": \"created_at\", \"type\": \"INT64\", \"repetition_type\": \"REQUIRED\", \"field_id\": 5, \"converted_type\": \"TIMESTAMP_MICROS\" },\n    { \"name\": \"customer_metadata\", \"num_children\": 2, \"repetition_type\": \"OPTIONAL\", \"field_id\": 6 },\n    { \"name\": \"loyalty_tier\", \"type\": \"BYTE_ARRAY\", \"repetition_type\": \"OPTIONAL\", \"field_id\": 7, \"converted_type\": \"UTF8\" },\n    { \"name\": \"lifetime_orders\", \"type\": \"INT32\", \"repetition_type\": \"OPTIONAL\", \"field_id\": 8, \"converted_type\": \"INT_32\" },\n    { \"name\": \"line_items\", \"num_children\": 1, \"repetition_type\": \"OPTIONAL\", \"field_id\": 9, \"converted_type\": \"LIST\" },\n    { \"name\": \"list\", \"num_children\": 1, \"repetition_type\": \"REPEATED\" },\n    { \"name\": \"element\", \"num_children\": 2, \"repetition_type\": \"REQUIRED\" },\n    { \"name\": \"sku\", \"type\": \"BYTE_ARRAY\", \"repetition_type\": \"REQUIRED\", \"field_id\": 12, \"converted_type\": \"UTF8\" },\n    { \"name\": \"quantity\", \"type\": \"INT32\", \"repetition_type\": \"REQUIRED\", \"field_id\": 13, \"converted_type\": \"INT_32\" },\n    { \"name\": \"shipping_country\", \"type\": \"BYTE_ARRAY\", \"repetition_type\": \"OPTIONAL\", \"field_id\": 14, \"converted_type\": \"UTF8\" },\n    { \"name\": \"discount_rate\", \"type\": \"FLOAT\", \"repetition_type\": \"OPTIONAL\", \"field_id\": 15 },\n    { \"name\": \"fulfillment_node\", \"type\": \"BYTE_ARRAY\", \"repetition_type\": \"OPTIONAL\", \"field_id\": 16, \"converted_type\": \"UTF8\" }\n  ],\n  \"row_groups\": [\n    {\n      \"ordinal\": 0,\n      \"num_rows\": 125000,\n      \"total_byte_size\": 37434163,\n      \"total_compressed_size\": 9646899,\n      \"file_offset\": 4,\n      \"columns_count\": 14\n    },\n    {\n      \"ordinal\": 1,\n      \"num_rows\": 125000,\n      \"total_byte_size\": 37434163,\n      \"total_compressed_size\": 9646899,\n      \"file_offset\": 9646903,\n      \"columns_count\": 14\n    }\n  ],\n  \"key_value_metadata\": [\n    { \"key\": \"org.apache.spark.sql.parquet.row.metadata\", \"value\": \"{\\\\\"type\\\\\":\\\\\"struct\\\\\",\\\\\"fields\\\\\":[...]}\" },\n    { \"key\": \"writer.model.name\", \"value\": \"lakehouse-batch-pipeline-gold-orders\" },\n    { \"key\": \"parquet.version\", \"value\": \"2.10.0\" }\n  ]\n}`\n\nexport const defaultRowGroupsData: RowGroupMeta[] = [\n  {\n    id: 0,\n    numRows: 125000,\n    numRowsFormatted: '125,000 rows',\n    totalByteSize: '35.7 MB',\n    totalCompressedSize: '9.2 MB',\n    compressionRatio: '74.2%',\n    fileOffset: '0x00000004',\n    columns: [\n      {\n        name: 'order_id',\n        physicalType: 'FIXED_LEN_BYTE_ARRAY(16)',\n        logicalType: 'UUID',\n        fieldId: 1,\n        encodings: ['PLAIN', 'RLE'],\n        compression: 'Snappy',\n        numValues: '125,000',\n        nullCount: 0,\n        nullPct: '0.0%',\n        minStat: '001a4e21-9a7c-4821-bc10-1a29f8c00192',\n        maxStat: 'ffe918b2-3c11-4fa0-8914-df7201bca908',\n        uncompressedBytes: '4.8 MB',\n        compressedBytes: '1.2 MB',\n        compressionRatio: '75.0%',\n        compressionPct: 75,\n        isDictionaryEncoded: false,\n        dictEntries: null,\n        dictPageOffset: null,\n        dataPageOffset: '0x00000020',\n        bitWidth: null,\n      },\n      {\n        name: 'customer_id',\n        physicalType: 'INT64',\n        logicalType: 'INTEGER(64, true)',\n        fieldId: 2,\n        encodings: ['PLAIN_DICTIONARY', 'RLE'],\n        compression: 'Snappy',\n        numValues: '125,000',\n        nullCount: 0,\n        nullPct: '0.0%',\n        minStat: '10,001',\n        maxStat: '994,210',\n        uncompressedBytes: '3.2 MB',\n        compressedBytes: '0.9 MB',\n        compressionRatio: '71.9%',\n        compressionPct: 71.9,\n        isDictionaryEncoded: true,\n        dictEntries: 54200,\n        dictPageOffset: '0x00125000',\n        dataPageOffset: '0x00140200',\n        bitWidth: 16,\n      },\n      {\n        name: 'order_status',\n        physicalType: 'BYTE_ARRAY',\n        logicalType: 'STRING (UTF8)',\n        fieldId: 3,\n        encodings: ['PLAIN_DICTIONARY', 'RLE'],\n        compression: 'Snappy',\n        numValues: '125,000',\n        nullCount: 0,\n        nullPct: '0.0%',\n        minStat: \"'cancelled'\",\n        maxStat: \"'shipped'\",\n        uncompressedBytes: '1.8 MB',\n        compressedBytes: '0.2 MB',\n        compressionRatio: '88.9%',\n        compressionPct: 88.9,\n        isDictionaryEncoded: true,\n        dictEntries: 5,\n        dictPageOffset: '0x0021a400',\n        dataPageOffset: '0x0021a480',\n        bitWidth: 3,\n      },\n      {\n        name: 'gross_amount',\n        physicalType: 'FIXED_LEN_BYTE_ARRAY(9)',\n        logicalType: 'DECIMAL(18, 2)',\n        fieldId: 4,\n        encodings: ['PLAIN_DICTIONARY', 'RLE'],\n        compression: 'Snappy',\n        numValues: '125,000',\n        nullCount: 0,\n        nullPct: '0.0%',\n        minStat: '$4.50',\n        maxStat: '$14,820.00',\n        uncompressedBytes: '3.4 MB',\n        compressedBytes: '0.8 MB',\n        compressionRatio: '76.5%',\n        compressionPct: 76.5,\n        isDictionaryEncoded: true,\n        dictEntries: 28450,\n        dictPageOffset: '0x0023a100',\n        dataPageOffset: '0x0026e000',\n        bitWidth: 15,\n      },\n      {\n        name: 'created_at',\n        physicalType: 'INT64',\n        logicalType: 'TIMESTAMP(MICROS, UTC)',\n        fieldId: 5,\n        encodings: ['DELTA_BINARY_PACKED', 'RLE'],\n        compression: 'Snappy',\n        numValues: '125,000',\n        nullCount: 0,\n        nullPct: '0.0%',\n        minStat: '2026-07-01 00:00:00.124000',\n        maxStat: '2026-07-26 23:59:58.910000',\n        uncompressedBytes: '3.1 MB',\n        compressedBytes: '0.7 MB',\n        compressionRatio: '77.4%',\n        compressionPct: 77.4,\n        isDictionaryEncoded: false,\n        dictEntries: null,\n        dictPageOffset: null,\n        dataPageOffset: '0x002e1000',\n        bitWidth: null,\n      },\n      {\n        name: 'shipping_country',\n        physicalType: 'BYTE_ARRAY',\n        logicalType: 'STRING (UTF8)',\n        fieldId: 6,\n        encodings: ['PLAIN_DICTIONARY', 'RLE'],\n        compression: 'Snappy',\n        numValues: '125,000',\n        nullCount: 24,\n        nullPct: '0.02%',\n        minStat: \"'AE'\",\n        maxStat: \"'US'\",\n        uncompressedBytes: '1.6 MB',\n        compressedBytes: '0.3 MB',\n        compressionRatio: '81.2%',\n        compressionPct: 81.2,\n        isDictionaryEncoded: true,\n        dictEntries: 48,\n        dictPageOffset: '0x0034a000',\n        dataPageOffset: '0x0034a240',\n        bitWidth: 6,\n      },\n      {\n        name: 'discount_rate',\n        physicalType: 'FLOAT',\n        logicalType: 'FLOAT(32)',\n        fieldId: 7,\n        encodings: ['PLAIN_DICTIONARY', 'RLE'],\n        compression: 'Snappy',\n        numValues: '125,000',\n        nullCount: 0,\n        nullPct: '0.0%',\n        minStat: '0.00',\n        maxStat: '0.35',\n        uncompressedBytes: '1.5 MB',\n        compressedBytes: '0.4 MB',\n        compressionRatio: '73.3%',\n        compressionPct: 73.3,\n        isDictionaryEncoded: true,\n        dictEntries: 12,\n        dictPageOffset: '0x00390100',\n        dataPageOffset: '0x00390180',\n        bitWidth: 4,\n      },\n      {\n        name: 'fulfillment_node',\n        physicalType: 'BYTE_ARRAY',\n        logicalType: 'STRING (UTF8)',\n        fieldId: 8,\n        encodings: ['PLAIN_DICTIONARY', 'RLE'],\n        compression: 'Snappy',\n        numValues: '125,000',\n        nullCount: 82,\n        nullPct: '0.07%',\n        minStat: \"'wh-ap-east-1'\",\n        maxStat: \"'wh-us-west-2'\",\n        uncompressedBytes: '1.7 MB',\n        compressedBytes: '0.3 MB',\n        compressionRatio: '82.4%',\n        compressionPct: 82.4,\n        isDictionaryEncoded: true,\n        dictEntries: 16,\n        dictPageOffset: '0x003e4000',\n        dataPageOffset: '0x003e4120',\n        bitWidth: 4,\n      },\n    ],\n  },\n  {\n    id: 1,\n    numRows: 125000,\n    numRowsFormatted: '125,000 rows',\n    totalByteSize: '35.7 MB',\n    totalCompressedSize: '9.2 MB',\n    compressionRatio: '74.2%',\n    fileOffset: '0x00933250',\n    columns: [\n      {\n        name: 'order_id',\n        physicalType: 'FIXED_LEN_BYTE_ARRAY(16)',\n        logicalType: 'UUID',\n        fieldId: 1,\n        encodings: ['PLAIN', 'RLE'],\n        compression: 'Snappy',\n        numValues: '125,000',\n        nullCount: 0,\n        nullPct: '0.0%',\n        minStat: '00021c90-410a-49bf-a870-19efbc441200',\n        maxStat: 'fff8a910-1840-4221-a109-ccb001928371',\n        uncompressedBytes: '4.8 MB',\n        compressedBytes: '1.2 MB',\n        compressionRatio: '75.0%',\n        compressionPct: 75,\n        isDictionaryEncoded: false,\n        dictEntries: null,\n        dictPageOffset: null,\n        dataPageOffset: '0x00933270',\n        bitWidth: null,\n      },\n      {\n        name: 'customer_id',\n        physicalType: 'INT64',\n        logicalType: 'INTEGER(64, true)',\n        fieldId: 2,\n        encodings: ['PLAIN_DICTIONARY', 'RLE'],\n        compression: 'Snappy',\n        numValues: '125,000',\n        nullCount: 0,\n        nullPct: '0.0%',\n        minStat: '10,042',\n        maxStat: '995,800',\n        uncompressedBytes: '3.2 MB',\n        compressedBytes: '0.9 MB',\n        compressionRatio: '71.9%',\n        compressionPct: 71.9,\n        isDictionaryEncoded: true,\n        dictEntries: 53900,\n        dictPageOffset: '0x00a58000',\n        dataPageOffset: '0x00a73000',\n        bitWidth: 16,\n      },\n      {\n        name: 'order_status',\n        physicalType: 'BYTE_ARRAY',\n        logicalType: 'STRING (UTF8)',\n        fieldId: 3,\n        encodings: ['PLAIN_DICTIONARY', 'RLE'],\n        compression: 'Snappy',\n        numValues: '125,000',\n        nullCount: 0,\n        nullPct: '0.0%',\n        minStat: \"'cancelled'\",\n        maxStat: \"'shipped'\",\n        uncompressedBytes: '1.8 MB',\n        compressedBytes: '0.2 MB',\n        compressionRatio: '88.9%',\n        compressionPct: 88.9,\n        isDictionaryEncoded: true,\n        dictEntries: 5,\n        dictPageOffset: '0x00b41000',\n        dataPageOffset: '0x00b41080',\n        bitWidth: 3,\n      },\n      {\n        name: 'gross_amount',\n        physicalType: 'FIXED_LEN_BYTE_ARRAY(9)',\n        logicalType: 'DECIMAL(18, 2)',\n        fieldId: 4,\n        encodings: ['PLAIN_DICTIONARY', 'RLE'],\n        compression: 'Snappy',\n        numValues: '125,000',\n        nullCount: 0,\n        nullPct: '0.0%',\n        minStat: '$3.90',\n        maxStat: '$16,240.00',\n        uncompressedBytes: '3.4 MB',\n        compressedBytes: '0.8 MB',\n        compressionRatio: '76.5%',\n        compressionPct: 76.5,\n        isDictionaryEncoded: true,\n        dictEntries: 28120,\n        dictPageOffset: '0x00b65000',\n        dataPageOffset: '0x00b99000',\n        bitWidth: 15,\n      },\n      {\n        name: 'created_at',\n        physicalType: 'INT64',\n        logicalType: 'TIMESTAMP(MICROS, UTC)',\n        fieldId: 5,\n        encodings: ['DELTA_BINARY_PACKED', 'RLE'],\n        compression: 'Snappy',\n        numValues: '125,000',\n        nullCount: 0,\n        nullPct: '0.0%',\n        minStat: '2026-07-27 00:00:01.002000',\n        maxStat: '2026-08-21 14:28:00.000000',\n        uncompressedBytes: '3.1 MB',\n        compressedBytes: '0.7 MB',\n        compressionRatio: '77.4%',\n        compressionPct: 77.4,\n        isDictionaryEncoded: false,\n        dictEntries: null,\n        dictPageOffset: null,\n        dataPageOffset: '0x00c12000',\n        bitWidth: null,\n      },\n      {\n        name: 'shipping_country',\n        physicalType: 'BYTE_ARRAY',\n        logicalType: 'STRING (UTF8)',\n        fieldId: 6,\n        encodings: ['PLAIN_DICTIONARY', 'RLE'],\n        compression: 'Snappy',\n        numValues: '125,000',\n        nullCount: 18,\n        nullPct: '0.01%',\n        minStat: \"'AE'\",\n        maxStat: \"'US'\",\n        uncompressedBytes: '1.6 MB',\n        compressedBytes: '0.3 MB',\n        compressionRatio: '81.2%',\n        compressionPct: 81.2,\n        isDictionaryEncoded: true,\n        dictEntries: 48,\n        dictPageOffset: '0x00c7b000',\n        dataPageOffset: '0x00c7b240',\n        bitWidth: 6,\n      },\n      {\n        name: 'discount_rate',\n        physicalType: 'FLOAT',\n        logicalType: 'FLOAT(32)',\n        fieldId: 7,\n        encodings: ['PLAIN_DICTIONARY', 'RLE'],\n        compression: 'Snappy',\n        numValues: '125,000',\n        nullCount: 0,\n        nullPct: '0.0%',\n        minStat: '0.00',\n        maxStat: '0.35',\n        uncompressedBytes: '1.5 MB',\n        compressedBytes: '0.4 MB',\n        compressionRatio: '73.3%',\n        compressionPct: 73.3,\n        isDictionaryEncoded: true,\n        dictEntries: 12,\n        dictPageOffset: '0x00cc1000',\n        dataPageOffset: '0x00cc1080',\n        bitWidth: 4,\n      },\n      {\n        name: 'fulfillment_node',\n        physicalType: 'BYTE_ARRAY',\n        logicalType: 'STRING (UTF8)',\n        fieldId: 8,\n        encodings: ['PLAIN_DICTIONARY', 'RLE'],\n        compression: 'Snappy',\n        numValues: '125,000',\n        nullCount: 64,\n        nullPct: '0.05%',\n        minStat: \"'wh-ap-east-1'\",\n        maxStat: \"'wh-us-west-2'\",\n        uncompressedBytes: '1.7 MB',\n        compressedBytes: '0.3 MB',\n        compressionRatio: '82.4%',\n        compressionPct: 82.4,\n        isDictionaryEncoded: true,\n        dictEntries: 16,\n        dictPageOffset: '0x00d15000',\n        dataPageOffset: '0x00d15120',\n        bitWidth: 4,\n      },\n    ],\n  },\n]\n",
      "type": "registry:page",
      "target": "~/src/app/components/blocks/parquet-metadata-inspector/parquet-metadata-data.ts"
    },
    {
      "path": "packages/registry-angular/blocks/parquet-metadata-inspector/parquet-metadata-types.ts",
      "content": "export interface ColumnChunk {\n  name: string\n  physicalType: string\n  logicalType: string\n  fieldId: number\n  encodings: string[]\n  compression: string\n  numValues: string\n  nullCount: number\n  nullPct: string\n  minStat: string\n  maxStat: string\n  uncompressedBytes: string\n  compressedBytes: string\n  compressionRatio: string\n  compressionPct: number\n  isDictionaryEncoded: boolean\n  dictEntries: number | null\n  dictPageOffset: string | null\n  dataPageOffset: string\n  bitWidth: number | null\n}\n\nexport interface RowGroupMeta {\n  id: number\n  numRows: number\n  numRowsFormatted: string\n  totalByteSize: string\n  totalCompressedSize: string\n  compressionRatio: string\n  fileOffset: string\n  columns: ColumnChunk[]\n}\n",
      "type": "registry:page",
      "target": "~/src/app/components/blocks/parquet-metadata-inspector/parquet-metadata-types.ts"
    },
    {
      "path": "packages/registry-angular/blocks/parquet-metadata-inspector/parquet-schema.ts",
      "content": "export interface SchemaTreeNode {\n  id: number\n  path: string\n  name: string\n  repetition: 'REQUIRED' | 'OPTIONAL' | 'REPEATED'\n  physicalType: string\n  logicalType: string\n  defLevel: number\n  repLevel: number\n  depth: number\n  isGroup?: boolean\n  description: string\n}\n\nexport const schemaTreeNodes: SchemaTreeNode[] = [\n  {\n    id: 1,\n    path: 'schema.order_id',\n    name: 'order_id',\n    repetition: 'REQUIRED',\n    physicalType: 'FIXED_LEN_BYTE_ARRAY(16)',\n    logicalType: 'UUID',\n    defLevel: 0,\n    repLevel: 0,\n    depth: 0,\n    description: 'Unique RFC 4122 order identifier binary token.',\n  },\n  {\n    id: 2,\n    path: 'schema.customer_id',\n    name: 'customer_id',\n    repetition: 'REQUIRED',\n    physicalType: 'INT64',\n    logicalType: 'INTEGER(64, true)',\n    defLevel: 0,\n    repLevel: 0,\n    depth: 0,\n    description: 'Foreign purchaser identity key.',\n  },\n  {\n    id: 3,\n    path: 'schema.order_status',\n    name: 'order_status',\n    repetition: 'OPTIONAL',\n    physicalType: 'BYTE_ARRAY',\n    logicalType: 'STRING (UTF8)',\n    defLevel: 1,\n    repLevel: 0,\n    depth: 0,\n    description: 'Lifecycle transition stage enumerated string.',\n  },\n  {\n    id: 4,\n    path: 'schema.gross_amount',\n    name: 'gross_amount',\n    repetition: 'REQUIRED',\n    physicalType: 'FIXED_LEN_BYTE_ARRAY(9)',\n    logicalType: 'DECIMAL(18, 2)',\n    defLevel: 0,\n    repLevel: 0,\n    depth: 0,\n    description: 'High-precision billing total charge with 2 scale points.',\n  },\n  {\n    id: 5,\n    path: 'schema.created_at',\n    name: 'created_at',\n    repetition: 'REQUIRED',\n    physicalType: 'INT64',\n    logicalType: 'TIMESTAMP(MICROS, UTC)',\n    defLevel: 0,\n    repLevel: 0,\n    depth: 0,\n    description: 'UTC microsecond epoch transaction timestamp.',\n  },\n  {\n    id: 6,\n    path: 'schema.customer_metadata',\n    name: 'customer_metadata',\n    repetition: 'OPTIONAL',\n    physicalType: 'GROUP',\n    logicalType: 'STRUCT',\n    defLevel: 1,\n    repLevel: 0,\n    depth: 0,\n    isGroup: true,\n    description: 'Nested profile attributes struct.',\n  },\n  {\n    id: 7,\n    path: 'schema.customer_metadata.loyalty_tier',\n    name: 'loyalty_tier',\n    repetition: 'OPTIONAL',\n    physicalType: 'BYTE_ARRAY',\n    logicalType: 'STRING (UTF8)',\n    defLevel: 2,\n    repLevel: 0,\n    depth: 1,\n    description: 'Customer loyalty tier status membership level.',\n  },\n  {\n    id: 8,\n    path: 'schema.customer_metadata.lifetime_orders',\n    name: 'lifetime_orders',\n    repetition: 'OPTIONAL',\n    physicalType: 'INT32',\n    logicalType: 'INTEGER(32, true)',\n    defLevel: 2,\n    repLevel: 0,\n    depth: 1,\n    description: 'Cumulative prior purchases count.',\n  },\n  {\n    id: 9,\n    path: 'schema.line_items',\n    name: 'line_items',\n    repetition: 'OPTIONAL',\n    physicalType: 'GROUP',\n    logicalType: 'LIST',\n    defLevel: 1,\n    repLevel: 0,\n    depth: 0,\n    isGroup: true,\n    description: '3-level Parquet Standard list layout.',\n  },\n  {\n    id: 10,\n    path: 'schema.line_items.list',\n    name: 'list',\n    repetition: 'REPEATED',\n    physicalType: 'GROUP',\n    logicalType: 'BAG',\n    defLevel: 2,\n    repLevel: 1,\n    depth: 1,\n    isGroup: true,\n    description: 'Repeated list item wrapper group.',\n  },\n  {\n    id: 11,\n    path: 'schema.line_items.list.element',\n    name: 'element',\n    repetition: 'REQUIRED',\n    physicalType: 'GROUP',\n    logicalType: 'STRUCT',\n    defLevel: 2,\n    repLevel: 1,\n    depth: 2,\n    isGroup: true,\n    description: 'Item tuple struct.',\n  },\n  {\n    id: 12,\n    path: 'schema.line_items.list.element.sku',\n    name: 'sku',\n    repetition: 'REQUIRED',\n    physicalType: 'BYTE_ARRAY',\n    logicalType: 'STRING (UTF8)',\n    defLevel: 2,\n    repLevel: 1,\n    depth: 3,\n    description: 'Stock keeping unit barcode tag.',\n  },\n  {\n    id: 13,\n    path: 'schema.line_items.list.element.quantity',\n    name: 'quantity',\n    repetition: 'REQUIRED',\n    physicalType: 'INT32',\n    logicalType: 'INTEGER(32, true)',\n    defLevel: 2,\n    repLevel: 1,\n    depth: 3,\n    description: 'Purchased quantity count.',\n  },\n  {\n    id: 14,\n    path: 'schema.shipping_country',\n    name: 'shipping_country',\n    repetition: 'OPTIONAL',\n    physicalType: 'BYTE_ARRAY',\n    logicalType: 'STRING (UTF8)',\n    defLevel: 1,\n    repLevel: 0,\n    depth: 0,\n    description: 'ISO-3166-1 alpha-2 destination country.',\n  },\n  {\n    id: 15,\n    path: 'schema.discount_rate',\n    name: 'discount_rate',\n    repetition: 'OPTIONAL',\n    physicalType: 'FLOAT',\n    logicalType: 'FLOAT(32)',\n    defLevel: 1,\n    repLevel: 0,\n    depth: 0,\n    description: 'Promotional discount multiplier applied.',\n  },\n  {\n    id: 16,\n    path: 'schema.fulfillment_node',\n    name: 'fulfillment_node',\n    repetition: 'OPTIONAL',\n    physicalType: 'BYTE_ARRAY',\n    logicalType: 'STRING (UTF8)',\n    defLevel: 1,\n    repLevel: 0,\n    depth: 0,\n    description: 'Assigned logistics warehouse cluster.',\n  },\n]\n",
      "type": "registry:page",
      "target": "~/src/app/components/blocks/parquet-metadata-inspector/parquet-schema.ts"
    },
    {
      "path": "packages/registry-angular/blocks/parquet-metadata-inspector/index.ts",
      "content": "export { UiParquetMetadataInspectorComponent } from './parquet-metadata-inspector.component'\nexport type { ColumnChunk, RowGroupMeta } from './parquet-metadata-types'\nexport type { SchemaTreeNode } from './parquet-schema'\n",
      "type": "registry:page",
      "target": "~/src/app/components/blocks/parquet-metadata-inspector/index.ts"
    }
  ],
  "dependencies": [
    "lucide-angular"
  ],
  "devDependencies": [],
  "registryDependencies": [
    "https://uipkge.dev/r/angular/badge.json",
    "https://uipkge.dev/r/angular/button.json",
    "https://uipkge.dev/r/angular/card.json",
    "https://uipkge.dev/r/angular/progress.json",
    "https://uipkge.dev/r/angular/table.json",
    "https://uipkge.dev/r/angular/tabs.json"
  ],
  "description": "Apache Parquet file format deep-dive inspector with telemetry overview, row groups switcher, column chunk statistics, dictionary encoding metrics, and hierarchical schema tree with repetition and definition levels.",
  "categories": [
    "devops",
    "app"
  ]
}