diff --git a/webview-ui/src/diagnostics_panel/App.css b/webview-ui/src/diagnostics_panel/App.css
index 7524b74..fbabd68 100644
--- a/webview-ui/src/diagnostics_panel/App.css
+++ b/webview-ui/src/diagnostics_panel/App.css
@@ -428,7 +428,7 @@ main {
.minecraft-grouped-statistic-table-grid-select {
text-align: center !important;
- width: 72px;
+ width: 50px;
padding: 6px 4px !important;
}
@@ -438,6 +438,12 @@ main {
padding: 6px 4px !important;
}
+.minecraft-grouped-statistic-table-grid-numeric {
+ text-align: right !important;
+ white-space: nowrap;
+ width: 10%;
+}
+
.minecraft-grouped-statistic-table-grid-sparkline {
width: 240px;
min-width: 240px;
@@ -469,13 +475,19 @@ main {
display: inline-block;
margin-right: 0;
font-weight: 600;
- white-space: nowrap;
+ white-space: normal;
+ overflow-wrap: anywhere;
+ max-width: 100%;
+ vertical-align: middle;
}
.minecraft-grouped-statistic-group-meta {
display: block;
margin-left: 0;
color: var(--vscode-descriptionForeground);
+ white-space: normal;
+ overflow-wrap: anywhere;
+ max-width: 100%;
}
.minecraft-grouped-statistic-toggle {
@@ -492,6 +504,11 @@ main {
.minecraft-grouped-statistic-child-key {
display: inline-block;
padding-left: 18px;
+ white-space: normal;
+ overflow: hidden;
+ overflow-wrap: anywhere;
+ max-width: 100%;
+ vertical-align: middle;
}
.minecraft-grouped-statistic-row-pinned {
diff --git a/webview-ui/src/diagnostics_panel/controls/MinecraftGroupedStatisticTable.tsx b/webview-ui/src/diagnostics_panel/controls/MinecraftGroupedStatisticTable.tsx
index 8852864..6684014 100644
--- a/webview-ui/src/diagnostics_panel/controls/MinecraftGroupedStatisticTable.tsx
+++ b/webview-ui/src/diagnostics_panel/controls/MinecraftGroupedStatisticTable.tsx
@@ -91,6 +91,7 @@ type MinecraftGroupedStatisticTableProps = {
rowAction?: GroupedStatisticTableRowAction;
nonConsolidatedColumnResolver?: NonConsolidatedColumnResolver;
valueFormatter?: (value: string | number, columnIndex: number) => string;
+ keyFormatter?: (key: string) => string;
prettifyNames?: boolean;
selectionEnabled?: boolean;
selectionHeaderLabel?: string;
@@ -116,6 +117,27 @@ const SPARKLINE_BOUNDS_EXPAND_LERP = 0.35;
const SPARKLINE_BOUNDS_CONTRACT_LERP = 0.12;
const SPARKLINE_HISTORY_RETENTION_TICKS = 8;
+const TICKS_PER_SECOND = 20;
+const TICKS_PER_SPARKLINE_UPDATE = 12; // TODO I don't know if this is accurate or not, but it gives us the right values for now
+
+function sparklineTickRangeToSeconds(updatePoints: number): string {
+ const totalTicks = updatePoints * TICKS_PER_SPARKLINE_UPDATE;
+ const seconds = totalTicks / TICKS_PER_SECOND;
+
+ if (seconds <= 0) {
+ return '0s';
+ }
+
+ if (seconds >= 60) {
+ const minutes = Math.floor(seconds / 60);
+ const remainingSeconds = Math.round(seconds % 60);
+
+ return remainingSeconds > 0 ? `${minutes}m ${remainingSeconds}s` : `${minutes}m`;
+ }
+
+ // For short ranges, show one decimal place for precision
+ return seconds < 10 ? `${seconds.toFixed(1)}s` : `${Math.round(seconds)}s`;
+}
function getRowSparklineSeriesKey(category: string): string {
return `row:${category}`;
}
@@ -400,6 +422,7 @@ const MinecraftGroupedStatisticTable = forwardRef<
rowAction,
nonConsolidatedColumnResolver,
valueFormatter,
+ keyFormatter,
prettifyNames = false,
selectionEnabled = false,
selectionHeaderLabel = 'Selected',
@@ -1264,7 +1287,9 @@ const MinecraftGroupedStatisticTable = forwardRef<
)}
- {row.category}
+
+ {keyFormatter ? keyFormatter(row.category) : row.category}
+
|
{row.values.map((value, valueIndex) => (
@@ -1363,7 +1388,9 @@ const MinecraftGroupedStatisticTable = forwardRef<
))}
{sparklineColumnIndex !== undefined && (
- | Trend |
+
+ Trend ({sparklineTickRangeToSeconds(sparklineTickRange)})
+ |
)}
{rowAction && (
@@ -1434,7 +1461,7 @@ const MinecraftGroupedStatisticTable = forwardRef<
{isExpanded ? '▾' : '▸'}
- {group.key}
+ {keyFormatter ? keyFormatter(group.key) : group.key}
diff --git a/webview-ui/src/diagnostics_panel/controls/SparklineCell.tsx b/webview-ui/src/diagnostics_panel/controls/SparklineCell.tsx
index bbe2676..a299874 100644
--- a/webview-ui/src/diagnostics_panel/controls/SparklineCell.tsx
+++ b/webview-ui/src/diagnostics_panel/controls/SparklineCell.tsx
@@ -13,6 +13,30 @@ function formatSparklineLabelValue(value: number, formatValue?: (value: number)
return formatValue ? formatValue(value) : value.toFixed(1);
}
+function calculateMedian(values: number[]): number {
+ if (values.length === 0) {
+ return 0;
+ }
+
+ // For median, we need to sort the values and find the middle one (or average of two middle ones)
+ const sorted = [...values].sort((a, b) => a - b);
+ const mid = Math.floor(sorted.length / 2);
+ return sorted.length % 2 !== 0 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2;
+}
+
+function calculateStandardDeviation(values: number[]): number {
+ if (values.length === 0) {
+ return 0;
+ }
+
+ // Calculate the mean by summing all values and dividing by the count
+ const mean = values.reduce((acc, v) => acc + v, 0) / values.length;
+ // Calculate the variance by averaging the squared differences from the mean
+ const variance = values.reduce((acc, v) => acc + Math.pow(v - mean, 2), 0) / values.length;
+ // Finally, take the square root of the variance to get the standard deviation
+ return Math.sqrt(variance);
+}
+
// A simple sparkline component that renders
// a basic line chart of the provided values along with min and max labels.
// Implemented using a dynamic SVG polyline.
@@ -30,6 +54,9 @@ export function SparklineCell({
const rawMax = Math.max(...values);
const rawMin = Math.min(...values);
+ const median = calculateMedian(values);
+ const stddev = calculateStandardDeviation(values);
+
const max = displayedMax ?? rawMax;
const min = displayedMin ?? rawMin;
const range = max - min || 1;
@@ -44,6 +71,8 @@ export function SparklineCell({
const maxLabel = formatSparklineLabelValue(rawMax, formatValue);
const minLabel = formatSparklineLabelValue(rawMin, formatValue);
+ const medianLabel = formatSparklineLabelValue(median, formatValue);
+ const stddevLabel = formatSparklineLabelValue(stddev, formatValue);
return (
@@ -67,6 +96,14 @@ export function SparklineCell({
Min {minLabel}
+
+
+ Median {medianLabel}
+
+
+ Std Dev {stddevLabel}
+
+
);
}
diff --git a/webview-ui/src/diagnostics_panel/prefabs/tabs/ClientEntitySystems.tsx b/webview-ui/src/diagnostics_panel/prefabs/tabs/ClientEntitySystems.tsx
index b965783..054abce 100644
--- a/webview-ui/src/diagnostics_panel/prefabs/tabs/ClientEntitySystems.tsx
+++ b/webview-ui/src/diagnostics_panel/prefabs/tabs/ClientEntitySystems.tsx
@@ -77,14 +77,14 @@ function ceilToDecimalPlace(value: number, decimalPlaces: number): string {
function getTimingColumnLabel(unit: TimingUnit): string {
if (unit === 'ms') {
- return 'Time In Milliseconds';
+ return 'Time (ms)';
}
if (unit === 'us') {
- return 'Time In Microseconds';
+ return 'Time (µs)';
}
- return 'Time In Nanoseconds';
+ return 'Time (ns)';
}
function formatTimingValue(value: number, unit: TimingUnit): string {
@@ -97,7 +97,7 @@ function formatTimingValue(value: number, unit: TimingUnit): string {
}
if (unit === 'us') {
- return `${ceilToDecimalPlace(value / 1_000, 1)} us`;
+ return `${ceilToDecimalPlace(value / 1_000, 1)} µs`;
}
return `${value} ns`;
@@ -136,6 +136,10 @@ function extractEntityId(entityCategory: string): string | undefined {
return match?.[1];
}
+function formatStatKey(key: string): string {
+ return key.split('(')[0].trim();
+}
+
function resolveSystemId(fullName: string): string | undefined {
// Example fullname: "System Name (4)"
const indexAndBracket = fullName ? fullName.split('(')[1] : undefined;
@@ -299,7 +303,7 @@ const StatsTab: TabPrefab = {
[filterSelectionMode],
);
- const entityValueLabels = [getTimingColumnLabel(entityTimingUnit), 'Percent Of Total'];
+ const entityValueLabels = [getTimingColumnLabel(entityTimingUnit), '% Of Total'];
const filteredEntityLabel = (() => {
if (filterSelectionMode === 'filter-by-single-entity') {
@@ -604,7 +608,7 @@ const StatsTab: TabPrefab = {
: undefined
}
keyLabel="System"
- valueLabels={[getTimingColumnLabel(systemTimingUnit), 'Percent Of Total']}
+ valueLabels={[getTimingColumnLabel(systemTimingUnit), '% Of Total']}
displayMode={
systemViewMode === 'grouped'
? MinecraftGroupedStatisticTableDisplayMode.Grouped
@@ -641,6 +645,7 @@ const StatsTab: TabPrefab = {
sparklineColumnIndex={0}
sparklineTickRange={100}
sparklineValueFormatter={value => formatTimingValue(value, systemTimingUnit)}
+ keyFormatter={formatStatKey}
valueFormatter={(value, columnIndex) => {
// Timing column
if (columnIndex === 0) {
|