UNPKG

polyv-live-cli

Version:

CLI tool for managing PolyV live streaming services.

538 lines 20.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.LayoutManager = void 0; class LayoutManager { constructor(screen, eventBus) { this.layouts = new Map(); this.panels = new Map(); this.originalPositions = new Map(); this.screen = screen; this.eventBus = eventBus; this.currentState = { currentLayout: 'default', panelStates: new Map(), history: [], }; this.initializeDefaultLayouts(); this.setupEventHandlers(); } initializeDefaultLayouts() { this.layouts.set('compact', { name: 'compact', description: 'Compact layout for small terminals', minSize: { width: 60, height: 20 }, panels: [ { id: 'main-status', title: 'Status', component: null, position: { top: 0, left: 0, width: '100%', height: '50%' }, border: { type: 'line', fg: 'cyan' }, visible: true, state: 'normal', }, { id: 'system-info', title: 'System', component: null, position: { top: '50%', left: 0, width: '100%', height: '50%' }, border: { type: 'line', fg: 'yellow' }, visible: true, state: 'normal', }, ], }); this.layouts.set('standard', { name: 'standard', description: 'Standard layout with balanced panels', minSize: { width: 80, height: 24 }, panels: [ { id: 'channel-status', title: 'Channel Status', component: null, position: { top: 0, left: 0, width: '60%', height: '60%' }, border: { type: 'line', fg: 'cyan' }, visible: true, state: 'normal', }, { id: 'system-resources', title: 'System Resources', component: null, position: { top: 0, left: '60%', width: '40%', height: '60%' }, border: { type: 'line', fg: 'yellow' }, visible: true, state: 'normal', }, { id: 'stream-metrics', title: 'Stream Metrics', component: null, position: { top: '60%', left: 0, width: '50%', height: '40%' }, border: { type: 'line', fg: 'green' }, visible: true, state: 'normal', }, { id: 'activity-log', title: 'Activity Log', component: null, position: { top: '60%', left: '50%', width: '50%', height: '40%' }, border: { type: 'line', fg: 'magenta' }, visible: true, state: 'normal', }, ], }); this.layouts.set('detailed', { name: 'detailed', description: 'Detailed layout with all panels visible', minSize: { width: 120, height: 30 }, panels: [ { id: 'channel-status', title: 'Channel Status', component: null, position: { top: 0, left: 0, width: '40%', height: '50%' }, border: { type: 'line', fg: 'cyan' }, visible: true, state: 'normal', }, { id: 'stream-metrics', title: 'Stream Metrics', component: null, position: { top: 0, left: '40%', width: '30%', height: '50%' }, border: { type: 'line', fg: 'green' }, visible: true, state: 'normal', }, { id: 'system-resources', title: 'System Resources', component: null, position: { top: 0, left: '70%', width: '30%', height: '50%' }, border: { type: 'line', fg: 'yellow' }, visible: true, state: 'normal', }, { id: 'activity-log', title: 'Activity Log', component: null, position: { top: '50%', left: 0, width: '60%', height: '50%' }, border: { type: 'line', fg: 'magenta' }, visible: true, state: 'normal', }, { id: 'performance-monitor', title: 'Performance', component: null, position: { top: '50%', left: '60%', width: '40%', height: '50%' }, border: { type: 'line', fg: 'red' }, visible: true, state: 'normal', }, ], }); this.currentState.currentLayout = 'standard'; } setupEventHandlers() { this.screen.on('resize', () => { this.handleScreenResize(); }); this.eventBus.on('panel:minimize', (data) => { this.minimizePanel(data.panelId); }); this.eventBus.on('panel:maximize', (data) => { this.maximizePanel(data.panelId); }); this.eventBus.on('panel:restore', (data) => { this.restorePanel(data.panelId); }); this.eventBus.on('panel:fullscreen', (data) => { this.toggleFullscreen(data.panelId); }); this.eventBus.on('layout:switch', (data) => { this.switchLayout(data.layoutName); }); } registerPanel(panelId, component) { this.panels.set(panelId, component); if (component && typeof component.on === 'function') { component.on('doubleclick', () => { this.toggleFullscreen(panelId); }); } this.eventBus.emit('panel:registered', { panelId, timestamp: new Date(), }); } unregisterPanel(panelId) { const component = this.panels.get(panelId); if (component) { this.panels.delete(panelId); this.currentState.panelStates.delete(panelId); this.eventBus.emit('panel:unregistered', { panelId, timestamp: new Date(), }); } } switchLayout(layoutName) { if (!this.layouts.has(layoutName)) { throw new Error(`Layout '${layoutName}' not found`); } const layout = this.layouts.get(layoutName); const terminalSize = this.getTerminalSize(); if (terminalSize.width < layout.minSize.width || terminalSize.height < layout.minSize.height) { this.eventBus.emit('layout:size:insufficient', { required: layout.minSize, current: terminalSize, layout: layoutName, }); return; } this.currentState.history.push(this.currentState.currentLayout); if (this.currentState.history.length > 10) { this.currentState.history.shift(); } this.currentState.currentLayout = layoutName; this.applyLayout(layout); this.eventBus.emit('layout:switched', { fromLayout: this.currentState.history[this.currentState.history.length - 1], toLayout: layoutName, timestamp: new Date(), }); } applyLayout(layout) { this.storeCurrentStates(); layout.panels.forEach(panelConfig => { const component = this.panels.get(panelConfig.id); if (component) { this.applyPanelConfig(panelConfig, component); this.currentState.panelStates.set(panelConfig.id, { ...panelConfig }); } }); this.panels.forEach((_component, panelId) => { if (!layout.panels.find(p => p.id === panelId)) { this.hidePanel(panelId); } }); this.screen.render(); } applyPanelConfig(config, component) { try { if (component.widget) { const position = this.calculatePosition(config.position); component.widget.left = position.left; component.widget.top = position.top; component.widget.width = position.width; component.widget.height = position.height; if (config.border) { component.widget.border = { type: config.border.type || 'line', }; if (component.widget.style) { component.widget.style.border = { fg: config.border.fg || 'white', bg: config.border.bg || 'black', }; } } if (config.visible !== false) { component.widget.show(); } else { component.widget.hide(); } if (config.title && component.widget.setLabel) { component.widget.setLabel(` ${config.title} `); } } } catch (error) { console.error(`Failed to apply panel config for ${config.id}:`, error); } } calculatePosition(position) { const screenSize = this.getTerminalSize(); const calculateValue = (value, dimension) => { if (typeof value === 'string' && value.endsWith('%')) { const percentage = parseInt(value) / 100; return Math.floor(percentage * screenSize[dimension]); } return typeof value === 'number' ? value : parseInt(String(value)); }; return { left: calculateValue(position.left, 'width'), top: calculateValue(position.top, 'height'), width: calculateValue(position.width, 'width'), height: calculateValue(position.height, 'height'), }; } toggleFullscreen(panelId) { const component = this.panels.get(panelId); if (!component) return; if (this.currentState.fullscreenPanel === panelId) { this.exitFullscreen(); } else { this.enterFullscreen(panelId); } } enterFullscreen(panelId) { const component = this.panels.get(panelId); if (!component || !component.widget) return; this.originalPositions.set(panelId, { id: panelId, title: '', component, position: { left: component.widget.left, top: component.widget.top, width: component.widget.width, height: component.widget.height, }, }); this.panels.forEach((otherComponent, otherId) => { if (otherId !== panelId && otherComponent.widget) { otherComponent.widget.hide(); } }); component.widget.left = 0; component.widget.top = 0; component.widget.width = '100%'; component.widget.height = '100%'; component.widget.show(); this.currentState.fullscreenPanel = panelId; this.screen.render(); this.eventBus.emit('panel:fullscreen:entered', { panelId, timestamp: new Date(), }); } exitFullscreen() { if (!this.currentState.fullscreenPanel) return; const panelId = this.currentState.fullscreenPanel; const component = this.panels.get(panelId); const originalPos = this.originalPositions.get(panelId); if (component && component.widget && originalPos) { component.widget.left = originalPos.position.left; component.widget.top = originalPos.position.top; component.widget.width = originalPos.position.width; component.widget.height = originalPos.position.height; } this.panels.forEach((otherComponent, otherId) => { if (otherId !== panelId && otherComponent.widget) { const panelState = this.currentState.panelStates.get(otherId); if (panelState && panelState.visible !== false) { otherComponent.widget.show(); } } }); this.currentState.fullscreenPanel = undefined; this.originalPositions.delete(panelId); this.screen.render(); this.eventBus.emit('panel:fullscreen:exited', { panelId, timestamp: new Date(), }); } minimizePanel(panelId) { const component = this.panels.get(panelId); if (!component || !component.widget) return; const panelState = this.currentState.panelStates.get(panelId); if (panelState) { panelState.state = 'minimized'; component.widget.hide(); this.screen.render(); this.eventBus.emit('panel:minimized', { panelId, timestamp: new Date(), }); } } maximizePanel(panelId) { const component = this.panels.get(panelId); if (!component || !component.widget) return; if (!this.originalPositions.has(panelId)) { this.originalPositions.set(panelId, { id: panelId, title: '', component, position: { left: component.widget.left, top: component.widget.top, width: component.widget.width, height: component.widget.height, }, }); } const screenSize = this.getTerminalSize(); component.widget.left = Math.floor(screenSize.width * 0.1); component.widget.top = Math.floor(screenSize.height * 0.1); component.widget.width = Math.floor(screenSize.width * 0.8); component.widget.height = Math.floor(screenSize.height * 0.8); component.widget.show(); const panelState = this.currentState.panelStates.get(panelId); if (panelState) { panelState.state = 'maximized'; } this.screen.render(); this.eventBus.emit('panel:maximized', { panelId, timestamp: new Date(), }); } restorePanel(panelId) { const component = this.panels.get(panelId); if (!component || !component.widget) return; const originalPos = this.originalPositions.get(panelId); if (originalPos) { component.widget.left = originalPos.position.left; component.widget.top = originalPos.position.top; component.widget.width = originalPos.position.width; component.widget.height = originalPos.position.height; component.widget.show(); this.originalPositions.delete(panelId); } const panelState = this.currentState.panelStates.get(panelId); if (panelState) { panelState.state = 'normal'; } this.screen.render(); this.eventBus.emit('panel:restored', { panelId, timestamp: new Date(), }); } hidePanel(panelId) { const component = this.panels.get(panelId); if (component && component.widget && typeof component.widget.hide === 'function') { component.widget.hide(); } } storeCurrentStates() { this.panels.forEach((component, panelId) => { if (component.widget) { try { const currentState = { id: panelId, title: '', component, position: { left: component.widget.left || 0, top: component.widget.top || 0, width: component.widget.width || 0, height: component.widget.height || 0, }, visible: !component.widget.hidden, state: 'normal', }; this.currentState.panelStates.set(panelId, currentState); } catch (error) { console.error(`Failed to store state for panel ${panelId}:`, error); } } }); } handleScreenResize() { const currentLayout = this.layouts.get(this.currentState.currentLayout); if (currentLayout) { const terminalSize = this.getTerminalSize(); if (terminalSize.width < currentLayout.minSize.width || terminalSize.height < currentLayout.minSize.height) { this.findAndSwitchToCompatibleLayout(terminalSize); } else { this.applyLayout(currentLayout); } } } findAndSwitchToCompatibleLayout(terminalSize) { const compatibleLayouts = Array.from(this.layouts.values()) .filter(layout => terminalSize.width >= layout.minSize.width && terminalSize.height >= layout.minSize.height) .sort((a, b) => (b.minSize.width * b.minSize.height) - (a.minSize.width * a.minSize.height)); if (compatibleLayouts.length > 0 && compatibleLayouts[0]) { this.switchLayout(compatibleLayouts[0].name); } } getTerminalSize() { return { width: this.screen.width || 80, height: this.screen.height || 24, }; } getAvailableLayouts() { return Array.from(this.layouts.keys()); } getCurrentLayout() { return this.currentState.currentLayout; } getLayoutInfo(layoutName) { const name = layoutName || this.currentState.currentLayout; return this.layouts.get(name) || null; } getPanelState(panelId) { return this.currentState.panelStates.get(panelId) || null; } isFullscreen() { return this.currentState.fullscreenPanel !== undefined; } getFullscreenPanel() { return this.currentState.fullscreenPanel; } addLayoutPreset(preset) { this.layouts.set(preset.name, preset); this.eventBus.emit('layout:preset:added', { layoutName: preset.name, timestamp: new Date(), }); } removeLayoutPreset(layoutName) { if (layoutName === this.currentState.currentLayout) { throw new Error('Cannot remove currently active layout'); } this.layouts.delete(layoutName); this.eventBus.emit('layout:preset:removed', { layoutName, timestamp: new Date(), }); } saveCurrentState() { const panels = []; this.currentState.panelStates.forEach((panelState, _panelId) => { panels.push({ ...panelState }); }); return { name: `custom-${Date.now()}`, description: 'User saved layout', minSize: this.getTerminalSize(), panels, }; } destroy() { if (this.currentState.fullscreenPanel) { this.exitFullscreen(); } this.currentState.panelStates.clear(); this.originalPositions.clear(); this.panels.clear(); this.layouts.clear(); } } exports.LayoutManager = LayoutManager; //# sourceMappingURL=layout-manager.js.map