@if (loading && !data) {
<div class="p3xr-analysis-loading">
<mat-icon>hourglass_empty</mat-icon>
<span>{{ s().running || 'Analyzing...' }}</span>
</div>
}
@if (!loading && !data) {
<div class="p3xr-analysis-loading">
<mat-icon>analytics</mat-icon>
<span>{{ s().noData || 'No data. Click Run Analysis to start.' }}</span>
</div>
}
@if (data) {
<!-- Controls + Server Info -->
<p3xr-ng-accordion [title]="s().title || 'Memory Analysis'" accordionKey="analysis-controls">
<div actions>
<p3xr-ng-button
(click)="runAnalysis(); $event.stopPropagation()"
[label]="loading ? (s().running || 'Analyzing...') : (s().runAnalysis || 'Run Analysis')"
[mdIcon]="loading ? 'hourglass_empty' : 'play_arrow'"
[disabled]="loading">
</p3xr-ng-button>
<p3xr-ng-button
(click)="exportOverview(); $event.stopPropagation()"
[label]="strings().intention?.export || 'Export'"
mdIcon="download">
</p3xr-ng-button>
</div>
<div content>
<mat-list>
<mat-list-item>
<div class="p3xr-settings-pair-row">
<div class="p3xr-settings-row-label">{{ s().keysScanned || 'Keys Scanned' }}</div>
<div class="p3xr-settings-row-value p3xr-mono">{{ data.totalScanned | number }} / {{ data.dbSize | number }}</div>
</div>
</mat-list-item>
<mat-divider></mat-divider>
<mat-list-item>
<div class="p3xr-settings-pair-row">
<div class="p3xr-settings-row-label">{{ s().topN || 'Top N' }}</div>
<div class="p3xr-settings-row-value">
<p3xr-ng-input type="number" [(ngModel)]="topN" min="5" max="100"></p3xr-ng-input>
</div>
</div>
</mat-list-item>
<mat-divider></mat-divider>
<mat-list-item>
<div class="p3xr-settings-pair-row">
<div class="p3xr-settings-row-label">{{ s().maxScanKeys || 'Max Scan Keys' }}</div>
<div class="p3xr-settings-row-value">
<p3xr-ng-input type="number" [(ngModel)]="maxScanKeys" min="100" max="100000" step="1000"></p3xr-ng-input>
</div>
</div>
</mat-list-item>
</mat-list>
</div>
</p3xr-ng-accordion>
<br />
<!-- Memory Breakdown -->
<p3xr-ng-accordion [title]="s().memoryBreakdown || 'Memory Breakdown'" accordionKey="analysis-memory-info">
<div actions>
<p3xr-ng-button
(click)="exportMemoryBreakdown(); $event.stopPropagation()"
[label]="strings().intention?.export || 'Export'"
mdIcon="download">
</p3xr-ng-button>
</div>
<div content>
<mat-list>
<mat-list-item>
<div class="p3xr-settings-pair-row">
<div class="p3xr-settings-row-label">{{ s().totalMemory || 'Total Memory' }}</div>
<div class="p3xr-settings-row-value p3xr-mono">{{ data.memoryInfo.usedHuman }}</div>
</div>
</mat-list-item>
<mat-divider></mat-divider>
<mat-list-item>
<div class="p3xr-settings-pair-row">
<div class="p3xr-settings-row-label">{{ s().rssMemory || 'RSS Memory' }}</div>
<div class="p3xr-settings-row-value p3xr-mono">{{ data.memoryInfo.rssHuman }}</div>
</div>
</mat-list-item>
<mat-divider></mat-divider>
<mat-list-item>
<div class="p3xr-settings-pair-row">
<div class="p3xr-settings-row-label">{{ s().peakMemory || 'Peak Memory' }}</div>
<div class="p3xr-settings-row-value p3xr-mono">{{ data.memoryInfo.peakHuman }}</div>
</div>
</mat-list-item>
<mat-divider></mat-divider>
<mat-list-item>
<div class="p3xr-settings-pair-row">
<div class="p3xr-settings-row-label">{{ s().overheadMemory || 'Overhead' }}</div>
<div class="p3xr-settings-row-value p3xr-mono">{{ formatBytes(data.memoryInfo.overhead) }}</div>
</div>
</mat-list-item>
<mat-divider></mat-divider>
<mat-list-item>
<div class="p3xr-settings-pair-row">
<div class="p3xr-settings-row-label">{{ s().datasetMemory || 'Dataset' }}</div>
<div class="p3xr-settings-row-value p3xr-mono">{{ formatBytes(data.memoryInfo.dataset) }}</div>
</div>
</mat-list-item>
<mat-divider></mat-divider>
<mat-list-item>
<div class="p3xr-settings-pair-row">
<div class="p3xr-settings-row-label">{{ s().luaMemory || 'Lua Memory' }}</div>
<div class="p3xr-settings-row-value p3xr-mono">{{ formatBytes(data.memoryInfo.lua) }}</div>
</div>
</mat-list-item>
<mat-divider></mat-divider>
<mat-list-item>
<div class="p3xr-settings-pair-row">
<div class="p3xr-settings-row-label">{{ s().fragmentation || 'Fragmentation' }}</div>
<div class="p3xr-settings-row-value p3xr-mono">{{ data.memoryInfo.fragRatio }}x</div>
</div>
</mat-list-item>
<mat-divider></mat-divider>
<mat-list-item>
<div class="p3xr-settings-pair-row">
<div class="p3xr-settings-row-label">{{ s().allocator || 'Allocator' }}</div>
<div class="p3xr-settings-row-value p3xr-mono">{{ data.memoryInfo.allocator }}</div>
</div>
</mat-list-item>
</mat-list>
</div>
</p3xr-ng-accordion>
<br />
<!-- Type Distribution -->
<p3xr-ng-accordion [title]="s().typeDistribution || 'Type Distribution'" accordionKey="analysis-type-dist">
<div actions>
<p3xr-ng-button
(click)="exportChart(typeChartRef, 'type-distribution'); $event.stopPropagation()"
[label]="strings().intention?.export || 'Export'"
mdIcon="download">
</p3xr-ng-button>
</div>
<div content>
<div #typeChart class="p3xr-analysis-chart"></div>
<mat-list>
@for (item of typeEntries; track item.type) {
<mat-list-item>
<div class="p3xr-settings-pair-row">
<div class="p3xr-settings-row-label">
<span style="font-weight: 500;">{{ item.type }}</span>
<span class="p3xr-analysis-sub">{{ item.count }} keys</span>
</div>
<div class="p3xr-settings-row-value p3xr-mono">{{ formatBytes(item.bytes) }}</div>
</div>
</mat-list-item>
<mat-divider></mat-divider>
}
</mat-list>
</div>
</p3xr-ng-accordion>
<br />
<!-- Memory by Prefix -->
<p3xr-ng-accordion [title]="s().prefixMemory || 'Memory by Prefix'" accordionKey="analysis-prefix-mem">
<div actions>
<p3xr-ng-button
(click)="exportChart(prefixChartRef, 'memory-by-prefix'); $event.stopPropagation()"
[label]="strings().intention?.export || 'Export'"
mdIcon="download">
</p3xr-ng-button>
</div>
<div content>
<div #prefixChart class="p3xr-analysis-chart"></div>
<mat-list>
@for (item of data.prefixMemory; track item.prefix; let i = $index) {
<mat-list-item>
<div class="p3xr-settings-pair-row">
<div class="p3xr-settings-row-label">
<span style="opacity: 0.4; margin-right: 8px;">#{{ i + 1 }}</span>
<span class="p3xr-mono" style="font-size: 13px;">{{ item.prefix }}</span>
<span class="p3xr-analysis-sub">{{ item.keyCount }} keys</span>
</div>
<div class="p3xr-settings-row-value p3xr-mono">{{ formatBytes(item.totalBytes) }}</div>
</div>
</mat-list-item>
<mat-divider></mat-divider>
}
</mat-list>
</div>
</p3xr-ng-accordion>
<br />
<!-- Key Expiration Overview -->
<p3xr-ng-accordion [title]="s().expirationOverview || 'Key Expiration'" accordionKey="analysis-expiration">
<div actions>
<p3xr-ng-button
(click)="exportExpiration(); $event.stopPropagation()"
[label]="strings().intention?.export || 'Export'"
mdIcon="download">
</p3xr-ng-button>
</div>
<div content>
<mat-list>
<mat-list-item>
<div class="p3xr-settings-pair-row">
<div class="p3xr-settings-row-label">{{ s().withTTL || 'With TTL' }}</div>
<div class="p3xr-settings-row-value p3xr-mono">{{ data.expirationOverview.withTTL | number }}</div>
</div>
</mat-list-item>
<mat-divider></mat-divider>
<mat-list-item>
<div class="p3xr-settings-pair-row">
<div class="p3xr-settings-row-label">{{ s().persistent || 'Persistent' }}</div>
<div class="p3xr-settings-row-value p3xr-mono">{{ data.expirationOverview.persistent | number }}</div>
</div>
</mat-list-item>
<mat-divider></mat-divider>
<mat-list-item>
<div class="p3xr-settings-pair-row">
<div class="p3xr-settings-row-label">{{ s().avgTTL || 'Average TTL' }}</div>
<div class="p3xr-settings-row-value p3xr-mono">{{ formatTTL(data.expirationOverview.avgTTL) }}</div>
</div>
</mat-list-item>
</mat-list>
</div>
</p3xr-ng-accordion>
}