polyv-live-cli
Version:
CLI tool for managing PolyV live streaming services.
491 lines • 18.6 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.themeManager = exports.ThemeManager = void 0;
const events_1 = require("events");
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const os = __importStar(require("os"));
const config_validator_1 = require("./config-validator");
const errors_1 = require("../utils/errors");
class ThemeManager extends events_1.EventEmitter {
constructor(options = {}) {
super();
this.themes = new Map();
this.currentThemeId = 'default';
this.customThemes = new Map();
this.previewMode = false;
this.options = {
themesDir: options.themesDir || path.join(os.homedir(), '.polyv-live-cli', 'themes'),
autoSave: options.autoSave ?? true,
enablePreview: options.enablePreview ?? true,
};
this.themesDir = this.options.themesDir;
this.ensureThemesDirectory();
this.loadBuiltInThemes();
this.loadCustomThemes();
}
getAvailableThemes() {
return Array.from(this.themes.keys());
}
getTheme(themeId) {
return this.themes.get(themeId);
}
getCurrentTheme() {
const theme = this.themes.get(this.currentThemeId);
if (!theme) {
throw new errors_1.ConfigurationError(`Current theme '${this.currentThemeId}' not found`);
}
return theme;
}
getCurrentThemeId() {
return this.currentThemeId;
}
async applyTheme(themeId) {
const theme = this.themes.get(themeId);
if (!theme) {
throw new errors_1.ConfigurationError(`Theme '${themeId}' not found`);
}
const validation = config_validator_1.ConfigValidator.validateThemeConfig(theme);
if (!validation.valid) {
throw new errors_1.ConfigurationError(`Cannot apply invalid theme: ${config_validator_1.ConfigValidator.createErrorMessage(validation)}`);
}
const previousThemeId = this.currentThemeId;
this.currentThemeId = themeId;
try {
await this.applyThemeToInterface(theme);
this.emit('theme:applied', theme, previousThemeId);
}
catch (error) {
this.currentThemeId = previousThemeId;
throw new errors_1.ConfigurationError(`Failed to apply theme '${themeId}': ${error instanceof Error ? error.message : String(error)}`);
}
}
async createCustomTheme(theme) {
const validation = config_validator_1.ConfigValidator.validateThemeConfig(theme);
if (!validation.valid) {
throw new errors_1.ConfigurationError(`Cannot create invalid theme: ${config_validator_1.ConfigValidator.createErrorMessage(validation)}`);
}
if (this.themes.has(theme.id)) {
throw new errors_1.ConfigurationError(`Theme with ID '${theme.id}' already exists`);
}
const customTheme = {
...theme,
isBuiltIn: false,
};
await this.saveCustomTheme(customTheme);
this.themes.set(customTheme.id, customTheme);
this.customThemes.set(customTheme.id, customTheme);
this.emit('theme:created', customTheme);
}
async updateCustomTheme(themeId, updates) {
const existingTheme = this.customThemes.get(themeId);
if (!existingTheme) {
throw new errors_1.ConfigurationError(`Custom theme '${themeId}' not found`);
}
if (existingTheme.isBuiltIn) {
throw new errors_1.ConfigurationError(`Cannot update built-in theme '${themeId}'`);
}
const updatedTheme = {
...existingTheme,
...updates,
id: themeId,
isBuiltIn: false,
};
const validation = config_validator_1.ConfigValidator.validateThemeConfig(updatedTheme);
if (!validation.valid) {
throw new errors_1.ConfigurationError(`Cannot update to invalid theme: ${config_validator_1.ConfigValidator.createErrorMessage(validation)}`);
}
await this.saveCustomTheme(updatedTheme);
this.themes.set(themeId, updatedTheme);
this.customThemes.set(themeId, updatedTheme);
if (this.currentThemeId === themeId) {
await this.applyThemeToInterface(updatedTheme);
}
this.emit('theme:updated', updatedTheme, existingTheme);
}
async deleteCustomTheme(themeId) {
const theme = this.customThemes.get(themeId);
if (!theme) {
throw new errors_1.ConfigurationError(`Custom theme '${themeId}' not found`);
}
if (theme.isBuiltIn) {
throw new errors_1.ConfigurationError(`Cannot delete built-in theme '${themeId}'`);
}
if (this.currentThemeId === themeId) {
await this.applyTheme('default');
}
const themeFile = path.join(this.themesDir, `${themeId}.json`);
if (fs.existsSync(themeFile)) {
await fs.promises.unlink(themeFile);
}
this.themes.delete(themeId);
this.customThemes.delete(themeId);
this.emit('theme:deleted', theme);
}
async exportTheme(themeId, filePath) {
const theme = this.themes.get(themeId);
if (!theme) {
throw new errors_1.ConfigurationError(`Theme '${themeId}' not found`);
}
try {
const themeJson = JSON.stringify(theme, null, 2);
await fs.promises.writeFile(filePath, themeJson, 'utf8');
this.emit('theme:exported', theme, filePath);
}
catch (error) {
throw new errors_1.ConfigurationError(`Failed to export theme to '${filePath}': ${error instanceof Error ? error.message : String(error)}`);
}
}
async importTheme(filePath) {
try {
const fileContent = await fs.promises.readFile(filePath, 'utf8');
const theme = JSON.parse(fileContent);
const validation = config_validator_1.ConfigValidator.validateThemeConfig(theme);
if (!validation.valid) {
throw new errors_1.ConfigurationError(`Invalid theme file: ${config_validator_1.ConfigValidator.createErrorMessage(validation)}`);
}
if (this.themes.has(theme.id)) {
const originalId = theme.id;
let counter = 1;
while (this.themes.has(`${originalId}-${counter}`)) {
counter++;
}
theme.id = `${originalId}-${counter}`;
}
theme.isBuiltIn = false;
await this.createCustomTheme(theme);
this.emit('theme:imported', theme, filePath);
return theme;
}
catch (error) {
if (error instanceof errors_1.ConfigurationError) {
throw error;
}
throw new errors_1.ConfigurationError(`Failed to import theme from '${filePath}': ${error instanceof Error ? error.message : String(error)}`);
}
}
async startPreview(themeId) {
if (!this.options.enablePreview) {
throw new errors_1.ConfigurationError('Theme preview is disabled');
}
const theme = this.themes.get(themeId);
if (!theme) {
throw new errors_1.ConfigurationError(`Theme '${themeId}' not found`);
}
if (!this.previewMode) {
this.originalThemeId = this.currentThemeId;
}
this.previewMode = true;
await this.applyThemeToInterface(theme);
this.emit('theme:preview-started', theme);
}
async endPreview() {
if (!this.previewMode) {
return;
}
this.previewMode = false;
if (this.originalThemeId) {
const originalTheme = this.themes.get(this.originalThemeId);
if (originalTheme) {
await this.applyThemeToInterface(originalTheme);
}
this.originalThemeId = undefined;
}
this.emit('theme:preview-ended');
}
getCustomThemes() {
return Array.from(this.customThemes.values());
}
getBuiltInThemes() {
return Array.from(this.themes.values()).filter(theme => theme.isBuiltIn);
}
isInPreviewMode() {
return this.previewMode;
}
ensureThemesDirectory() {
try {
if (!fs.existsSync(this.themesDir)) {
fs.mkdirSync(this.themesDir, { recursive: true });
}
}
catch (error) {
throw new errors_1.ConfigurationError(`Failed to create themes directory: ${error}`);
}
}
loadBuiltInThemes() {
const defaultTheme = this.createDefaultTheme();
this.themes.set(defaultTheme.id, defaultTheme);
const darkTheme = this.createDarkTheme();
this.themes.set(darkTheme.id, darkTheme);
const lightTheme = this.createLightTheme();
this.themes.set(lightTheme.id, lightTheme);
const highContrastTheme = this.createHighContrastTheme();
this.themes.set(highContrastTheme.id, highContrastTheme);
}
async loadCustomThemes() {
try {
if (!fs.existsSync(this.themesDir)) {
return;
}
const files = await fs.promises.readdir(this.themesDir);
const themeFiles = files.filter(file => file.endsWith('.json'));
for (const file of themeFiles) {
try {
const filePath = path.join(this.themesDir, file);
const fileContent = await fs.promises.readFile(filePath, 'utf8');
const theme = JSON.parse(fileContent);
const validation = config_validator_1.ConfigValidator.validateThemeConfig(theme);
if (validation.valid) {
this.themes.set(theme.id, theme);
this.customThemes.set(theme.id, theme);
}
else {
console.warn(`Invalid theme file ${file}: ${validation.errors.join(', ')}`);
}
}
catch (error) {
console.warn(`Failed to load theme file ${file}: ${error}`);
}
}
}
catch (error) {
console.warn(`Failed to load custom themes: ${error}`);
}
}
async saveCustomTheme(theme) {
const themeFile = path.join(this.themesDir, `${theme.id}.json`);
const themeJson = JSON.stringify(theme, null, 2);
try {
await fs.promises.writeFile(themeFile, themeJson, 'utf8');
}
catch (error) {
throw new errors_1.ConfigurationError(`Failed to save theme '${theme.id}': ${error instanceof Error ? error.message : String(error)}`);
}
}
async applyThemeToInterface(theme) {
this.emit('theme:interface-update', theme);
const delay = process.env['NODE_ENV'] === 'test' ? 1 : 100;
await new Promise(resolve => setTimeout(resolve, delay));
}
createDefaultTheme() {
return {
id: 'default',
name: 'Default',
description: 'Default monitoring interface theme',
isBuiltIn: true,
colors: {
primary: '#0066cc',
secondary: '#6c757d',
background: '#000000',
foreground: '#ffffff',
accent: '#17a2b8',
error: '#dc3545',
warning: '#ffc107',
success: '#28a745',
info: '#17a2b8',
muted: '#6c757d',
highlight: '#ffff00',
border: '#666666',
selection: '#0066cc',
},
fonts: {
family: 'monospace',
size: 12,
weight: 'normal',
style: 'normal',
},
borders: {
type: 'line',
style: 'solid',
color: '#666666',
},
components: this.createDefaultComponentStyles(),
};
}
createDarkTheme() {
return {
id: 'dark',
name: 'Dark',
description: 'Dark theme optimized for low-light environments',
isBuiltIn: true,
colors: {
primary: '#0d6efd',
secondary: '#6c757d',
background: '#121212',
foreground: '#e0e0e0',
accent: '#bb86fc',
error: '#cf6679',
warning: '#f9c74f',
success: '#4caf50',
info: '#2196f3',
muted: '#757575',
highlight: '#ffeb3b',
border: '#333333',
selection: '#bb86fc',
},
fonts: {
family: 'monospace',
size: 12,
weight: 'normal',
style: 'normal',
},
borders: {
type: 'line',
style: 'solid',
color: '#333333',
},
components: this.createDarkComponentStyles(),
};
}
createLightTheme() {
return {
id: 'light',
name: 'Light',
description: 'Light theme for bright environments',
isBuiltIn: true,
colors: {
primary: '#0066cc',
secondary: '#6c757d',
background: '#ffffff',
foreground: '#000000',
accent: '#6f42c1',
error: '#dc3545',
warning: '#fd7e14',
success: '#198754',
info: '#0dcaf0',
muted: '#6c757d',
highlight: '#fff3cd',
border: '#dee2e6',
selection: '#0066cc',
},
fonts: {
family: 'monospace',
size: 12,
weight: 'normal',
style: 'normal',
},
borders: {
type: 'line',
style: 'solid',
color: '#dee2e6',
},
components: this.createLightComponentStyles(),
};
}
createHighContrastTheme() {
return {
id: 'high-contrast',
name: 'High Contrast',
description: 'High contrast theme for accessibility',
isBuiltIn: true,
colors: {
primary: '#ffff00',
secondary: '#ffffff',
background: '#000000',
foreground: '#ffffff',
accent: '#00ffff',
error: '#ff0000',
warning: '#ffff00',
success: '#00ff00',
info: '#00ffff',
muted: '#808080',
highlight: '#ffff00',
border: '#ffffff',
selection: '#ffff00',
},
fonts: {
family: 'monospace',
size: 14,
weight: 'bold',
style: 'normal',
},
borders: {
type: 'line',
style: 'solid',
color: '#ffffff',
},
components: this.createHighContrastComponentStyles(),
};
}
createDefaultComponentStyles() {
return {
header: { fg: '#ffffff', bg: '#0066cc', bold: true },
content: { fg: '#ffffff', bg: '#000000' },
status: { fg: '#17a2b8', bg: '#000000' },
border: { fg: '#666666' },
scrollbar: { fg: '#666666', bg: '#333333' },
selection: { fg: '#ffffff', bg: '#0066cc' },
};
}
createDarkComponentStyles() {
return {
header: { fg: '#e0e0e0', bg: '#333333', bold: true },
content: { fg: '#e0e0e0', bg: '#121212' },
status: { fg: '#bb86fc', bg: '#121212' },
border: { fg: '#333333' },
scrollbar: { fg: '#555555', bg: '#222222' },
selection: { fg: '#000000', bg: '#bb86fc' },
};
}
createLightComponentStyles() {
return {
header: { fg: '#000000', bg: '#f8f9fa', bold: true },
content: { fg: '#000000', bg: '#ffffff' },
status: { fg: '#6f42c1', bg: '#ffffff' },
border: { fg: '#dee2e6' },
scrollbar: { fg: '#adb5bd', bg: '#f8f9fa' },
selection: { fg: '#ffffff', bg: '#0066cc' },
};
}
createHighContrastComponentStyles() {
return {
header: { fg: '#000000', bg: '#ffff00', bold: true },
content: { fg: '#ffffff', bg: '#000000' },
status: { fg: '#00ffff', bg: '#000000', bold: true },
border: { fg: '#ffffff' },
scrollbar: { fg: '#ffffff', bg: '#000000' },
selection: { fg: '#000000', bg: '#ffff00' },
};
}
destroy() {
this.removeAllListeners();
this.themes.clear();
this.customThemes.clear();
}
}
exports.ThemeManager = ThemeManager;
exports.themeManager = new ThemeManager();
//# sourceMappingURL=theme-manager.js.map