/** * Component Configuration Registry * * Extensible registry for external tools and components. * Each component defines its config schema, making it easy to: * - Add new tools without code changes * - Generate settings UI dynamically * - Validate configurations * - Store and retrieve settings consistently */ import { getConfig, getDssHost, getStorybookPort } from './config-loader.js'; /** * Component Registry * Add new components here to extend the settings system. */ export const componentRegistry = { storybook: { id: 'storybook', name: 'Storybook', description: 'Component documentation and playground', icon: 'book', category: 'documentation', // Config schema - defines available settings config: { port: { type: 'number', label: 'Port', default: 6006, readonly: true, // Derived from server config description: 'Storybook runs on this port', }, theme: { type: 'select', label: 'Theme', options: [ { value: 'light', label: 'Light' }, { value: 'dark', label: 'Dark' }, { value: 'auto', label: 'Auto (System)' }, ], default: 'auto', description: 'Storybook UI theme preference', }, showDocs: { type: 'boolean', label: 'Show Docs Tab', default: true, description: 'Display the documentation tab in stories', }, }, // Dynamic URL builder (uses nginx path-based routing) getUrl() { try { const host = getDssHost(); const protocol = window.location.protocol; // Admin configured path-based routing at /storybook/ return `${protocol}//${host}/storybook/`; } catch { return null; } }, // Status check async checkStatus() { const url = this.getUrl(); if (!url) return { status: 'unknown', message: 'Configuration not loaded' }; try { const response = await fetch(url, { mode: 'no-cors', cache: 'no-cache' }); return { status: 'available', message: 'Storybook is running' }; } catch { return { status: 'unavailable', message: 'Storybook is not responding' }; } }, }, figma: { id: 'figma', name: 'Figma', description: 'Design file integration and token extraction', icon: 'figma', category: 'design', config: { apiKey: { type: 'password', label: 'API Token', placeholder: 'figd_xxxxxxxxxx', description: 'Your Figma Personal Access Token', sensitive: true, // Never display actual value }, fileKey: { type: 'text', label: 'Default File Key', placeholder: 'Enter Figma file key', description: 'Default Figma file to use for token extraction', }, autoSync: { type: 'boolean', label: 'Auto-sync Tokens', default: false, description: 'Automatically sync tokens when file changes detected', }, }, getUrl() { return 'https://www.figma.com'; }, async checkStatus() { // Check if API key is configured via backend try { const response = await fetch('/api/figma/health'); const data = await response.json(); if (data.configured) { return { status: 'connected', message: `Connected as ${data.user || 'user'}` }; } return { status: 'not_configured', message: 'API token not set' }; } catch { return { status: 'error', message: 'Failed to check Figma status' }; } }, }, // Future components can be added here jira: { id: 'jira', name: 'Jira', description: 'Issue tracking integration', icon: 'clipboard', category: 'project', enabled: false, // Not yet implemented config: { baseUrl: { type: 'url', label: 'Jira URL', placeholder: 'https://your-org.atlassian.net', description: 'Your Jira instance URL', }, projectKey: { type: 'text', label: 'Project Key', placeholder: 'DS', description: 'Default Jira project key', }, }, getUrl() { return localStorage.getItem('jira_base_url') || null; }, async checkStatus() { return { status: 'not_implemented', message: 'Coming soon' }; }, }, confluence: { id: 'confluence', name: 'Confluence', description: 'Documentation wiki integration', icon: 'file-text', category: 'documentation', enabled: false, // Not yet implemented config: { baseUrl: { type: 'url', label: 'Confluence URL', placeholder: 'https://your-org.atlassian.net/wiki', description: 'Your Confluence instance URL', }, spaceKey: { type: 'text', label: 'Space Key', placeholder: 'DS', description: 'Default Confluence space key', }, }, getUrl() { return localStorage.getItem('confluence_base_url') || null; }, async checkStatus() { return { status: 'not_implemented', message: 'Coming soon' }; }, }, }; /** * Get all enabled components */ export function getEnabledComponents() { return Object.values(componentRegistry).filter(c => c.enabled !== false); } /** * Get components by category */ export function getComponentsByCategory(category) { return Object.values(componentRegistry).filter(c => c.category === category && c.enabled !== false); } /** * Get component by ID */ export function getComponent(id) { return componentRegistry[id] || null; } /** * Get component setting value */ export function getComponentSetting(componentId, settingKey) { const storageKey = `dss_component_${componentId}_${settingKey}`; const stored = localStorage.getItem(storageKey); if (stored !== null) { try { return JSON.parse(stored); } catch { return stored; } } // Return default value from schema const component = getComponent(componentId); if (component && component.config[settingKey]) { const defaultValue = component.config[settingKey].default; if (defaultValue !== undefined) { return defaultValue; } } return null; } /** * Set component setting value */ export function setComponentSetting(componentId, settingKey, value) { const storageKey = `dss_component_${componentId}_${settingKey}`; localStorage.setItem(storageKey, JSON.stringify(value)); } /** * Get all settings for a component */ export function getComponentSettings(componentId) { const component = getComponent(componentId); if (!component) return {}; const settings = {}; for (const key of Object.keys(component.config)) { settings[key] = getComponentSetting(componentId, key); } return settings; } export default { componentRegistry, getEnabledComponents, getComponentsByCategory, getComponent, getComponentSetting, setComponentSetting, getComponentSettings, };