import * as PIXI from "pixi.js-legacy"; const monitors = []; const MONITOR_PADDING = 8; const MONITOR_FONT_SIZE = 14; const MONITOR_MIN_WIDTH = 80; const MONITOR_BG_COLOR = 0xFF8C1A; // Orange for variables const MONITOR_TEXT_COLOR = 0xFFFFFF; export class Monitor { constructor(app, type, name, getValue, x = 10, y = 10) { this.app = app; this.type = type; // 'variable', 'timer', 'answer', etc. this.name = name; this.label = name; this.getValue = getValue; this.visible = true; this.dragging = false; this.container = new PIXI.Container(); this.container.x = x; this.container.y = y; this.container.interactive = true; this.container.buttonMode = true; // Background this.background = new PIXI.Graphics(); this.container.addChild(this.background); // Label text this.labelText = new PIXI.Text(name, { fontFamily: 'Arial', fontSize: MONITOR_FONT_SIZE, fill: MONITOR_TEXT_COLOR, fontWeight: 'bold' }); this.labelText.x = MONITOR_PADDING; this.labelText.y = MONITOR_PADDING; this.container.addChild(this.labelText); // Value text this.valueText = new PIXI.Text('0', { fontFamily: 'Arial', fontSize: MONITOR_FONT_SIZE + 2, fill: MONITOR_TEXT_COLOR }); this.valueText.x = MONITOR_PADDING; this.valueText.y = MONITOR_PADDING + MONITOR_FONT_SIZE + 4; this.container.addChild(this.valueText); // Make draggable this.container.on('pointerdown', this.onDragStart.bind(this)); this.container.on('pointerup', this.onDragEnd.bind(this)); this.container.on('pointerupoutside', this.onDragEnd.bind(this)); this.container.on('pointermove', this.onDragMove.bind(this)); this.app.stage.addChild(this.container); this.updateDisplay(); monitors.push(this); } onDragStart(event) { this.dragging = true; this.dragData = event.data; const pos = this.dragData.getLocalPosition(this.app.stage); this.dragOffset = { x: pos.x - this.container.x, y: pos.y - this.container.y }; } onDragEnd() { this.dragging = false; this.dragData = null; } onDragMove() { if (this.dragging && this.dragData) { const pos = this.dragData.getLocalPosition(this.app.stage); this.container.x = pos.x - this.dragOffset.x; this.container.y = pos.y - this.dragOffset.y; } } updateDisplay() { const value = this.getValue(); this.valueText.text = String(value); // Resize background const width = Math.max( MONITOR_MIN_WIDTH, Math.max(this.labelText.width, this.valueText.width) + MONITOR_PADDING * 2 ); const height = this.labelText.height + this.valueText.height + MONITOR_PADDING * 3; this.background.clear(); this.background.beginFill(MONITOR_BG_COLOR); this.background.drawRoundedRect(0, 0, width, height, 8); this.background.endFill(); this.background.lineStyle(2, 0x000000, 0.2); this.background.drawRoundedRect(0, 0, width, height, 8); } setVisible(visible) { this.visible = visible; this.container.visible = visible; } destroy() { this.container.destroy({ children: true }); const index = monitors.indexOf(this); if (index > -1) { monitors.splice(index, 1); } } toJSON() { return { type: this.type, name: this.name, x: this.container.x, y: this.container.y, visible: this.visible }; } } export function updateAllMonitors() { monitors.forEach(monitor => monitor.updateDisplay()); } export function getMonitor(type, name) { return monitors.find(m => m.type === type && m.name === name); } export function createMonitor(app, type, name, getValue, x, y) { // Check if monitor already exists const existing = getMonitor(type, name); if (existing) { existing.setVisible(true); return existing; } return new Monitor(app, type, name, getValue, x, y); } export function removeMonitor(type, name) { const monitor = getMonitor(type, name); if (monitor) { monitor.destroy(); } } export function getAllMonitors() { return monitors; } export function clearAllMonitors() { monitors.forEach(m => m.destroy()); monitors.length = 0; } export function loadMonitors(app, monitorsData, valueGetters) { monitorsData.forEach(data => { const getter = valueGetters[data.type]?.[data.name]; if (getter) { const monitor = new Monitor(app, data.type, data.name, getter, data.x, data.y); monitor.setVisible(data.visible); } }); }