From deca74e881fb8546943d395cef5b8088b7d1ba3f Mon Sep 17 00:00:00 2001 From: Cody Ward Date: Wed, 15 Jul 2026 15:24:21 -0600 Subject: [PATCH] Add the ability to export a CSV representing a Entity x System timing matrix --- webview-ui/src/diagnostics_panel/App.css | 44 +++++++ .../prefabs/tabs/ClientEntitySystems.tsx | 109 +++++++++++++++++- 2 files changed, 151 insertions(+), 2 deletions(-) diff --git a/webview-ui/src/diagnostics_panel/App.css b/webview-ui/src/diagnostics_panel/App.css index 92556e1..4818ea5 100644 --- a/webview-ui/src/diagnostics_panel/App.css +++ b/webview-ui/src/diagnostics_panel/App.css @@ -397,6 +397,50 @@ main { padding-top: 16px; } +.minecraft-entity-system-csv-dialog-backdrop { + position: fixed; + inset: 0; + z-index: 1000; + display: flex; + align-items: center; + justify-content: center; + padding: 20px; + background: color-mix(in srgb, var(--vscode-editor-background) 70%, transparent); +} + +.minecraft-entity-system-csv-dialog { + box-sizing: border-box; + width: min(420px, 100%); + padding: 20px; + color: var(--vscode-editor-foreground); + background: var(--vscode-editorWidget-background); + border: 1px solid var(--vscode-editorWidget-border); + border-radius: 6px; + box-shadow: 0 8px 24px var(--vscode-widget-shadow); +} + +.minecraft-entity-system-csv-dialog h3 { + margin: 0 0 8px; +} + +.minecraft-entity-system-csv-dialog p { + margin: 0 0 16px; + color: var(--vscode-descriptionForeground); +} + +.minecraft-entity-system-csv-dialog-options { + display: flex; + flex-direction: column; + gap: 10px; +} + +.minecraft-entity-system-csv-dialog-actions { + display: flex; + justify-content: flex-end; + gap: 8px; + margin-top: 20px; +} + .minecraft-grouped-statistic-table-root { width: 100%; } diff --git a/webview-ui/src/diagnostics_panel/prefabs/tabs/ClientEntitySystems.tsx b/webview-ui/src/diagnostics_panel/prefabs/tabs/ClientEntitySystems.tsx index 95a3dc0..b4f92bc 100644 --- a/webview-ui/src/diagnostics_panel/prefabs/tabs/ClientEntitySystems.tsx +++ b/webview-ui/src/diagnostics_panel/prefabs/tabs/ClientEntitySystems.tsx @@ -24,7 +24,7 @@ import { import { DebuggerRequestResultBanner } from '../../controls/DebuggerRequestResult'; import { MultipleStatisticProvider, StatisticUpdatedMessage } from '../../StatisticProvider'; import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; -import { VSCodeButton, VSCodeDropdown, VSCodeOption } from '@vscode/webview-ui-toolkit/react'; +import { VSCodeButton, VSCodeCheckbox, VSCodeDropdown, VSCodeOption } from '@vscode/webview-ui-toolkit/react'; import { CsvCellValue, TableCsvExporter } from '../../exporters/TableCsvExporter'; import { sendExportDataRequest } from '../../utilities/exportData'; import { SparklineCell } from '../../controls/SparklineCell'; @@ -35,6 +35,7 @@ const START_ENTITY_SYSTEM_PROFILER_REQUEST = 'Start Entity System Profiler'; const FILTER_MULTI_ENTITY_REQUEST = 'Filter Multi Entity System Profiler'; const FILTER_SINGLE_ENTITY_REQUEST = 'Filter Single Entity System Profiler'; const FILTER_BY_SYSTEM_REQUEST = 'Filter By System Entity System Profiler'; +const REQUEST_CSV_ENTITY_SYSTEM_PROFILER_REQUEST = 'Request CSV Entity System Profiler'; const DEBUGGER_REQUEST_COMMANDS = [ { command: START_ENTITY_SYSTEM_PROFILER_REQUEST, label: 'Start' }, @@ -145,8 +146,12 @@ function buildExportFileName(tableType: DiagnosticsExportTableType, now: Date): return `${tableType}-trends-${formatExportTimestamp(now)}.csv`; } +function buildEntitySystemMatrixCsvFileName(now: Date): string { + return `entity-system-profiler-${formatExportTimestamp(now)}.csv`; +} + function getFilterSelectionModeTooltip(ecsVersion: number): string { - if (ecsVersion === 2 || ecsVersion === 3) { + if (ecsVersion >= 2) { return ( 'Select Filtering Mode:\n' + '\u2022 "No Filter" will display system and entity timings for all entities and systems.\n' + @@ -349,6 +354,11 @@ const StatsTab: TabPrefab = { const [ecsVersion, setECSVersion] = useState(1); const [isStarted, setIsStarted] = useState(false); const [trendDurationPoints, setTrendDurationPoints] = useState(DEFAULT_TREND_DURATION_POINTS); + const [isEntitySystemMatrixCsvExportPending, setIsEntitySystemMatrixCsvExportPending] = + useState(false); + const [isEntitySystemMatrixCsvDialogOpen, setIsEntitySystemMatrixCsvDialogOpen] = useState(false); + const [groupEntitySystemMatrixCsvEntities, setGroupEntitySystemMatrixCsvEntities] = useState(false); + const [groupEntitySystemMatrixCsvSystems, setGroupEntitySystemMatrixCsvSystems] = useState(false); const entityTimingsTableRef = useRef(null); const systemTimingsTableRef = useRef(null); const isMountRef = useRef(true); @@ -603,6 +613,7 @@ const StatsTab: TabPrefab = { const lastResult: DebuggerRequestResultMessage | undefined = lastRequestedCommand ? getDebuggerRequestResult(lastRequestedCommand) : undefined; + const entitySystemMatrixCsvResult = getDebuggerRequestResult(REQUEST_CSV_ENTITY_SYSTEM_PROFILER_REQUEST); useEffect(() => { if (lastRequestedCommand !== START_ENTITY_SYSTEM_PROFILER_REQUEST) { @@ -618,6 +629,51 @@ const StatsTab: TabPrefab = { console.log(`Entity System Profiler is started: ${isStarted}`); }, [lastResult, lastRequestedCommand]); + useEffect(() => { + if ( + !isEntitySystemMatrixCsvExportPending || + isDebuggerRequestInFlight(REQUEST_CSV_ENTITY_SYSTEM_PROFILER_REQUEST) + ) { + return; + } + + setIsEntitySystemMatrixCsvExportPending(false); + + if ( + entitySystemMatrixCsvResult?.response?.success !== true || + typeof entitySystemMatrixCsvResult.response.args !== 'string' + ) { + return; + } + + sendExportDataRequest({ + format: csvExporter.format, + mimeType: csvExporter.mimeType, + suggestedFileName: buildEntitySystemMatrixCsvFileName(new Date()), + content: entitySystemMatrixCsvResult.response.args, + }); + }, [entitySystemMatrixCsvResult, csvExporter, isEntitySystemMatrixCsvExportPending]); + + const openEntitySystemMatrixCsvDialog = useCallback((): void => { + setIsEntitySystemMatrixCsvDialogOpen(true); + }, []); + + const closeEntitySystemMatrixCsvDialog = useCallback((): void => { + setIsEntitySystemMatrixCsvDialogOpen(false); + }, []); + + const requestEntitySystemMatrixCsv = useCallback((): void => { + setIsEntitySystemMatrixCsvExportPending(true); + setIsEntitySystemMatrixCsvDialogOpen(false); + setLastRequestedCommand(REQUEST_CSV_ENTITY_SYSTEM_PROFILER_REQUEST); + sendDebuggerRequest(REQUEST_CSV_ENTITY_SYSTEM_PROFILER_REQUEST, { + csvArgs: { + groupEntities: groupEntitySystemMatrixCsvEntities, + groupSystems: groupEntitySystemMatrixCsvSystems, + }, + }); + }, [groupEntitySystemMatrixCsvEntities, groupEntitySystemMatrixCsvSystems]); + const exportTableData = useCallback( (tableType: DiagnosticsExportTableType): void => { const tableHandle = @@ -832,6 +888,17 @@ const StatsTab: TabPrefab = { exportTableData('system')} disabled={!isStarted}> Export System CSV + {ecsVersion >= 4 && ( + + Export Entity System Matrix CSV + + )} )} @@ -1063,6 +1130,44 @@ const StatsTab: TabPrefab = { )} + {isEntitySystemMatrixCsvDialogOpen && ( +
+
+

Export Entity System Matrix CSV

+

Select which data should be grouped in the CSV.

+
+ + setGroupEntitySystemMatrixCsvEntities(currentValue => !currentValue) + } + > + Group Entities + + setGroupEntitySystemMatrixCsvSystems(currentValue => !currentValue)} + > + Group Systems + +
+
+ Cancel + + Export CSV + +
+
+
+ )} ); },