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:
Digital Production Factory
2025-12-09 18:45:48 -03:00
commit 276ed71f31
884 changed files with 373737 additions and 0 deletions

View File

@@ -0,0 +1,355 @@
/**
* ds-chat-panel.js
* AI chatbot panel with team+project context
* MVP1: Integrates claude-service with ContextStore for team-aware assistance
*/
import claudeService from '../../services/claude-service.js';
import contextStore from '../../stores/context-store.js';
import { ComponentHelpers } from '../../utils/component-helpers.js';
class DSChatPanel extends HTMLElement {
constructor() {
super();
this.messages = [];
this.isLoading = false;
}
async connectedCallback() {
// Sync claude-service with ContextStore
const context = contextStore.getMCPContext();
if (context.project_id) {
claudeService.setProject(context.project_id);
}
// Subscribe to project changes
this.unsubscribe = contextStore.subscribeToKey('projectId', (newProjectId) => {
if (newProjectId) {
claudeService.setProject(newProjectId);
this.showSystemMessage(`Switched to project: ${newProjectId}`);
}
});
// Initialize MCP tools in background
this.initializeMcpTools();
this.render();
this.setupEventListeners();
this.loadHistory();
this.showWelcomeMessage();
}
/**
* Initialize MCP tools to enable AI tool awareness
*/
async initializeMcpTools() {
try {
console.log('[DSChatPanel] Initializing MCP tools...');
await claudeService.getMcpTools();
console.log('[DSChatPanel] MCP tools initialized successfully');
} catch (error) {
console.warn('[DSChatPanel] Failed to load MCP tools (non-blocking):', error.message);
}
}
/**
* Set context from parent component (ds-ai-chat-sidebar)
* @param {Object} context - Context object with project, team, page
*/
setContext(context) {
if (!context) return;
// Handle project context (could be object with id or string id)
if (context.project) {
const projectId = typeof context.project === 'object'
? context.project.id
: context.project;
if (projectId && projectId !== claudeService.currentProjectId) {
claudeService.setProject(projectId);
console.log('[DSChatPanel] Context updated via setContext:', { projectId });
}
}
// Store team and page context for reference
if (context.team) {
this.currentTeam = context.team;
}
if (context.page) {
this.currentPage = context.page;
}
}
disconnectedCallback() {
if (this.unsubscribe) {
this.unsubscribe();
}
}
setupEventListeners() {
const input = this.querySelector('#chat-input');
const sendBtn = this.querySelector('#chat-send-btn');
const clearBtn = this.querySelector('#chat-clear-btn');
const exportBtn = this.querySelector('#chat-export-btn');
if (sendBtn && input) {
sendBtn.addEventListener('click', () => this.sendMessage());
input.addEventListener('keypress', (e) => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
this.sendMessage();
}
});
}
if (clearBtn) {
clearBtn.addEventListener('click', () => this.clearChat());
}
if (exportBtn) {
exportBtn.addEventListener('click', () => this.exportChat());
}
}
loadHistory() {
const history = claudeService.getHistory();
if (history && history.length > 0) {
this.messages = history;
this.renderMessages();
}
}
showWelcomeMessage() {
if (this.messages.length === 0) {
const context = contextStore.getMCPContext();
const teamId = context.team_id || 'ui';
const teamGreetings = {
ui: 'I can help with token extraction, component audits, Storybook comparisons, and quick wins analysis.',
ux: 'I can assist with Figma syncing, design tokens, asset management, and navigation flows.',
qa: 'I can help with visual regression testing, accessibility audits, and ESRE validation.',
admin: 'I can help manage projects, configure integrations, and oversee the design system.'
};
const greeting = teamGreetings[teamId] || teamGreetings.admin;
this.showSystemMessage(
`👋 Welcome to the ${teamId.toUpperCase()} team workspace!\n\n${greeting}\n\nI have access to all MCP tools for the active project.`
);
}
}
showSystemMessage(text) {
this.messages.push({
role: 'system',
content: text,
timestamp: new Date().toISOString()
});
this.renderMessages();
}
async sendMessage() {
const input = this.querySelector('#chat-input');
const message = input?.value.trim();
if (!message || this.isLoading) return;
// Check project context
const context = contextStore.getMCPContext();
if (!context.project_id) {
ComponentHelpers.showToast?.('Please select a project before chatting', 'error');
return;
}
// Add user message
this.messages.push({
role: 'user',
content: message,
timestamp: new Date().toISOString()
});
// Clear input
input.value = '';
// Render and scroll
this.renderMessages();
this.scrollToBottom();
// Show loading
this.isLoading = true;
this.updateLoadingState();
try {
// Add team context to the request
const teamContext = {
projectId: context.project_id,
teamId: context.team_id,
userId: context.user_id,
page: 'workdesk',
capabilities: context.capabilities
};
// Send to Claude with team+project context
const response = await claudeService.chat(message, teamContext);
// Add assistant response
this.messages.push({
role: 'assistant',
content: response,
timestamp: new Date().toISOString()
});
this.renderMessages();
this.scrollToBottom();
} catch (error) {
console.error('[DSChatPanel] Failed to send message:', error);
ComponentHelpers.showToast?.(`Chat error: ${error.message}`, 'error');
this.messages.push({
role: 'system',
content: `❌ Error: ${error.message}\n\nPlease try again or check your connection.`,
timestamp: new Date().toISOString()
});
this.renderMessages();
} finally {
this.isLoading = false;
this.updateLoadingState();
}
}
clearChat() {
if (!confirm('Clear all chat history?')) return;
claudeService.clearHistory();
this.messages = [];
this.renderMessages();
this.showWelcomeMessage();
ComponentHelpers.showToast?.('Chat history cleared', 'success');
}
exportChat() {
claudeService.exportConversation();
ComponentHelpers.showToast?.('Chat exported successfully', 'success');
}
updateLoadingState() {
const sendBtn = this.querySelector('#chat-send-btn');
const input = this.querySelector('#chat-input');
if (sendBtn) {
sendBtn.disabled = this.isLoading;
sendBtn.textContent = this.isLoading ? '⏳ Sending...' : '📤 Send';
}
if (input) {
input.disabled = this.isLoading;
}
}
scrollToBottom() {
const messagesContainer = this.querySelector('#chat-messages');
if (messagesContainer) {
setTimeout(() => {
messagesContainer.scrollTop = messagesContainer.scrollHeight;
}, 100);
}
}
renderMessages() {
const messagesContainer = this.querySelector('#chat-messages');
if (!messagesContainer) return;
if (this.messages.length === 0) {
messagesContainer.innerHTML = `
<div style="text-align: center; padding: 48px; color: var(--vscode-text-dim);">
<div style="font-size: 48px; margin-bottom: 16px;">💬</div>
<h3 style="font-size: 14px; font-weight: 600; margin-bottom: 8px;">No messages yet</h3>
<p style="font-size: 12px;">Start a conversation to get help with your design system.</p>
</div>
`;
return;
}
messagesContainer.innerHTML = this.messages.map(msg => {
const isUser = msg.role === 'user';
const isSystem = msg.role === 'system';
const alignStyle = isUser ? 'flex-end' : 'flex-start';
const bgColor = isUser
? 'var(--vscode-button-background)'
: isSystem
? 'rgba(255, 191, 0, 0.1)'
: 'var(--vscode-sidebar)';
const textColor = isUser ? 'var(--vscode-button-foreground)' : 'var(--vscode-text)';
const maxWidth = isSystem ? '100%' : '80%';
const icon = isUser ? '👤' : isSystem ? '' : '🤖';
return `
<div style="display: flex; justify-content: ${alignStyle}; margin-bottom: 16px;">
<div style="max-width: ${maxWidth}; background: ${bgColor}; padding: 12px; border-radius: 8px; color: ${textColor};">
<div style="font-size: 10px; color: var(--vscode-text-dim); margin-bottom: 6px; display: flex; align-items: center; gap: 6px;">
<span>${icon}</span>
<span>${isUser ? 'You' : isSystem ? 'System' : 'AI Assistant'}</span>
<span>•</span>
<span>${ComponentHelpers.formatRelativeTime(new Date(msg.timestamp))}</span>
</div>
<div style="font-size: 12px; white-space: pre-wrap; word-wrap: break-word;">
${ComponentHelpers.escapeHtml(msg.content)}
</div>
</div>
</div>
`;
}).join('');
}
render() {
this.innerHTML = `
<div style="height: 100%; display: flex; flex-direction: column; background: var(--vscode-bg);">
<!-- Header -->
<div style="padding: 12px 16px; border-bottom: 1px solid var(--vscode-border); display: flex; justify-content: space-between; align-items: center;">
<div>
<h3 style="font-size: 12px; font-weight: 600; margin-bottom: 4px;">AI Assistant</h3>
<div style="font-size: 10px; color: var(--vscode-text-dim);">Team-contextualized help with MCP tools</div>
</div>
<div style="display: flex; gap: 8px;">
<button id="chat-export-btn" class="button" style="padding: 4px 8px; font-size: 10px;">
📥 Export
</button>
<button id="chat-clear-btn" class="button" style="padding: 4px 8px; font-size: 10px;">
🗑️ Clear
</button>
</div>
</div>
<!-- Messages -->
<div id="chat-messages" style="flex: 1; overflow-y: auto; padding: 16px;">
${ComponentHelpers.renderLoading('Loading chat...')}
</div>
<!-- Input -->
<div style="padding: 16px; border-top: 1px solid var(--vscode-border);">
<div style="display: flex; gap: 8px;">
<textarea
id="chat-input"
placeholder="Ask me anything about your design system..."
class="input"
style="flex: 1; min-height: 60px; resize: vertical; font-size: 12px;"
rows="2"
></textarea>
<button id="chat-send-btn" class="button" style="padding: 8px 16px; font-size: 12px; height: 60px;">
📤 Send
</button>
</div>
<div style="font-size: 10px; color: var(--vscode-text-dim); margin-top: 8px;">
Press Enter to send • Shift+Enter for new line
</div>
</div>
</div>
`;
}
}
customElements.define('ds-chat-panel', DSChatPanel);
export default DSChatPanel;