Initial commit: Clean DSS implementation
Migrated from design-system-swarm with fresh git history.
Old project history preserved in /home/overbits/apps/design-system-swarm
Core components:
- MCP Server (Python FastAPI with mcp 1.23.1)
- Claude Plugin (agents, commands, skills, strategies, hooks, core)
- DSS Backend (dss-mvp1 - token translation, Figma sync)
- Admin UI (Node.js/React)
- Server (Node.js/Express)
- Storybook integration (dss-mvp1/.storybook)
Self-contained configuration:
- All paths relative or use DSS_BASE_PATH=/home/overbits/dss
- PYTHONPATH configured for dss-mvp1 and dss-claude-plugin
- .env file with all configuration
- Claude plugin uses ${CLAUDE_PLUGIN_ROOT} for portability
Migration completed: $(date)
🤖 Clean migration with full functionality preserved
This commit is contained in:
272
admin-ui/js/core/component-config.js
Normal file
272
admin-ui/js/core/component-config.js
Normal file
@@ -0,0 +1,272 @@
|
||||
/**
|
||||
* 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,
|
||||
};
|
||||
Reference in New Issue
Block a user