Split WiredCreatorToolsView: extract types/constants/helpers into 3 sibling files

The single-file WiredCreatorToolsView.tsx was 4493 lines, which is one
of the main reasons the React Compiler reports
"Compilation Skipped: Existing memoization could not be preserved" on
this module. Split is conservative — only the pure leading sections
move out, the component itself is untouched (state, effects, JSX all
stay in place).

New files (sibling to the view):
- WiredCreatorTools.types.ts (~233 lines): every interface and type
  alias declared at the top of the original file.
- WiredCreatorTools.constants.ts (~225 lines): TABS, MONITOR_LOG_ORDER,
  poll constants, MONITOR_ERROR_INFO, INSPECTION_ELEMENTS,
  VARIABLES_ELEMENTS, EDITABLE_*, VARIABLE_DEFINITIONS,
  WIRED_FREEZE_EFFECT_IDS, TEAM_COLOR_NAMES, WEEKDAY/MONTH/DIRECTION
  names. The createVariableDefinition factory is kept as a local helper
  in this file (only used to build VARIABLE_DEFINITIONS).
- WiredCreatorTools.helpers.ts (~147 lines): createEmptyMonitorSnapshot,
  getHotelTimeFormatter (with its module-private cache map),
  getHotelDateTimeParts, formatMonitorLatestOccurrence,
  formatMonitorHistoryOccurrence, formatVariableTimestamp,
  formatMonitorSource, normalizeMonitorReason. All pure (or
  cache-stable), no closure on component state.

WiredCreatorToolsView.tsx changes:
- 4493 -> 3901 lines (-592, ~13% reduction).
- The four inspection-icon asset imports (furni/global/user/context)
  move to the constants file alongside the only consumers
  (INSPECTION_ELEMENTS / VARIABLES_ELEMENTS).
- AvatarInfoFurni was only referenced by the extracted
  InspectionFurniSelection interface and is removed from the main
  file's api import.
- New import block at the top pulls back the symbols actually used by
  the component body.

Verification:
- yarn eslint on the three new files: 0 errors / 0 warnings.
- yarn eslint on WiredCreatorToolsView.tsx: 26 errors before split,
  26 errors after split (identical pre-existing set; nothing new
  introduced).
- yarn tsc --noEmit on the four files: clean (only the project-wide
  pre-existing TS2307 about @nitrots/nitro-renderer not being
  installed locally remains, same as before).

This unblocks future per-tab splits (Monitor / Inspection / Variables
JSX panels are still inline in the view and represent the next ~1600
lines that could move out, but require introducing a shared state
context first since the current setState chain is intertwined).

https://claude.ai/code/session_01GrR87LAqnAEyKG2ZbmQt5Q
This commit is contained in:
simoleo89
2026-05-11 16:31:52 +00:00
parent 39eb2c6b84
commit 5d8717dedb
4 changed files with 609 additions and 597 deletions
@@ -0,0 +1,147 @@
import { HotelDateTimeParts, MonitorSnapshot } from './WiredCreatorTools.types';
const HOTEL_TIME_FORMATTERS: Map<string, Intl.DateTimeFormat> = new Map();
export const createEmptyMonitorSnapshot = (): MonitorSnapshot =>
({
usageCurrentWindow: 0,
usageLimitPerWindow: 0,
isHeavy: false,
delayedEventsPending: 0,
delayedEventsLimit: 0,
averageExecutionMs: 0,
peakExecutionMs: 0,
recursionDepthCurrent: 0,
recursionDepthLimit: 0,
killedRemainingSeconds: 0,
usageWindowMs: 0,
overloadAverageThresholdMs: 0,
overloadPeakThresholdMs: 0,
heavyUsageThresholdPercent: 0,
heavyConsecutiveWindowsThreshold: 0,
overloadConsecutiveWindowsThreshold: 0,
heavyDelayedThresholdPercent: 0,
logs: [],
history: []
});
export const getHotelTimeFormatter = (timeZone: string): Intl.DateTimeFormat =>
{
const formatterTimeZone = (timeZone || 'UTC');
const existingFormatter = HOTEL_TIME_FORMATTERS.get(formatterTimeZone);
if(existingFormatter) return existingFormatter;
let formatter: Intl.DateTimeFormat = null;
try
{
formatter = new Intl.DateTimeFormat('en-GB', {
timeZone: formatterTimeZone,
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
hourCycle: 'h23'
});
}
catch
{
formatter = new Intl.DateTimeFormat('en-GB', {
timeZone: 'UTC',
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
hourCycle: 'h23'
});
}
HOTEL_TIME_FORMATTERS.set(formatterTimeZone, formatter);
return formatter;
};
export const getHotelDateTimeParts = (epochMs: number, timeZone: string): HotelDateTimeParts =>
{
const normalizedEpochMs = Number.isFinite(epochMs) ? epochMs : Date.now();
const date = new Date(normalizedEpochMs);
const formatter = getHotelTimeFormatter(timeZone);
const formattedParts = formatter.formatToParts(date);
const partsMap = new Map<string, string>();
for(const part of formattedParts)
{
if(part.type === 'literal') continue;
partsMap.set(part.type, part.value);
}
return {
year: Number(partsMap.get('year') ?? date.getUTCFullYear()),
month: Number(partsMap.get('month') ?? (date.getUTCMonth() + 1)),
day: Number(partsMap.get('day') ?? date.getUTCDate()),
hour: Number(partsMap.get('hour') ?? date.getUTCHours()),
minute: Number(partsMap.get('minute') ?? date.getUTCMinutes()),
second: Number(partsMap.get('second') ?? date.getUTCSeconds()),
millisecond: (((normalizedEpochMs % 1000) + 1000) % 1000)
};
};
export const formatMonitorLatestOccurrence = (latestOccurrenceSeconds: number, nowMs: number): string =>
{
if(latestOccurrenceSeconds <= 0) return '/';
const diffMs = Math.max(0, (nowMs - (latestOccurrenceSeconds * 1000)));
const diffSeconds = Math.floor(diffMs / 1000);
if(diffSeconds < 5) return 'Just now';
if(diffSeconds < 60) return `${ diffSeconds }s ago`;
const diffMinutes = Math.floor(diffSeconds / 60);
if(diffMinutes < 60) return `${ diffMinutes }m ago`;
const diffHours = Math.floor(diffMinutes / 60);
if(diffHours < 24) return `${ diffHours }h ago`;
const diffDays = Math.floor(diffHours / 24);
return `${ diffDays }d ago`;
};
export const formatMonitorHistoryOccurrence = (occurredAtSeconds: number): string =>
{
if(occurredAtSeconds <= 0) return '/';
return new Date(occurredAtSeconds * 1000).toLocaleString('en-GB');
};
export const formatVariableTimestamp = (timestamp: number): string =>
{
if(!timestamp || (timestamp <= 0)) return '/';
return new Date(timestamp * 1000).toLocaleString('en-GB');
};
export const formatMonitorSource = (sourceLabel: string, sourceId: number): string =>
{
const normalizedLabel = (sourceLabel || '').trim();
if(!normalizedLabel && !(sourceId > 0)) return 'Room monitor';
if(sourceId > 0) return `${ normalizedLabel || 'wired' } (#${ sourceId })`;
return normalizedLabel;
};
export const normalizeMonitorReason = (reason: string): string =>
{
const normalizedReason = (reason || '').trim();
return normalizedReason || 'No detailed reason was recorded for this entry.';
};