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

18
team-portal/index.html Normal file
View File

@@ -0,0 +1,18 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>DSS Team Portal POC</title>
<link rel="stylesheet" href="src/css/style.css">
<link rel="stylesheet" href="src/css/poc-theme.css">
</head>
<body>
<div id="ui-container" class="ui-container">
<h1>2D Workbench</h1>
<button id="toggle-view-btn">Toggle View</button>
</div>
<canvas id="webgl-canvas"></canvas>
<script type="module" src="src/js/main.js"></script>
</body>
</html>

1006
team-portal/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

20
team-portal/package.json Normal file
View File

@@ -0,0 +1,20 @@
{
"name": "dss-team-portal",
"version": "1.0.0",
"description": "DSS Team Portal - Immersive dashboard experience",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview",
"start": "node server.js"
},
"dependencies": {
"gsap": "^3.13.0",
"three": "^0.160.0",
"zustand": "^4.4.7"
},
"devDependencies": {
"vite": "^5.0.10"
}
}

View File

@@ -0,0 +1,29 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
<defs>
<linearGradient id="glow" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#00d4aa;stop-opacity:1" />
<stop offset="100%" style="stop-color:#7c3aed;stop-opacity:1" />
</linearGradient>
<filter id="blur">
<feGaussianBlur in="SourceGraphic" stdDeviation="2" />
</filter>
</defs>
<!-- Glow background -->
<circle cx="50" cy="50" r="40" fill="url(#glow)" opacity="0.3" filter="url(#blur)"/>
<!-- Main circle -->
<circle cx="50" cy="50" r="35" fill="none" stroke="url(#glow)" stroke-width="3"/>
<!-- Inner elements representing consciousness -->
<circle cx="50" cy="50" r="20" fill="none" stroke="#00d4aa" stroke-width="2" opacity="0.8"/>
<circle cx="50" cy="50" r="10" fill="none" stroke="#7c3aed" stroke-width="2" opacity="0.6"/>
<circle cx="50" cy="50" r="4" fill="#00d4aa"/>
<!-- Orbital dots -->
<circle cx="50" cy="20" r="3" fill="#00d4aa"/>
<circle cx="75" cy="40" r="2.5" fill="#7c3aed"/>
<circle cx="70" cy="70" r="2" fill="#00d4aa"/>
<circle cx="35" cy="75" r="2.5" fill="#7c3aed"/>
<circle cx="25" cy="45" r="2" fill="#00d4aa"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

108
team-portal/server.js Normal file
View File

@@ -0,0 +1,108 @@
// Production server for DSS Team Portal
import { createServer } from 'http';
import { readFile, stat } from 'fs/promises';
import { join, extname } from 'path';
import { fileURLToPath } from 'url';
const __dirname = fileURLToPath(new URL('.', import.meta.url));
const DIST_DIR = join(__dirname, 'dist');
const PORT = process.env.PORT || 3457;
const API_PROXY = process.env.API_PROXY || 'http://localhost:3456';
const MIME_TYPES = {
'.html': 'text/html',
'.css': 'text/css',
'.js': 'application/javascript',
'.json': 'application/json',
'.svg': 'image/svg+xml',
'.png': 'image/png',
'.jpg': 'image/jpeg',
'.ico': 'image/x-icon',
'.woff': 'font/woff',
'.woff2': 'font/woff2'
};
async function serveFile(res, filePath) {
try {
const content = await readFile(filePath);
const ext = extname(filePath);
const contentType = MIME_TYPES[ext] || 'application/octet-stream';
res.writeHead(200, {
'Content-Type': contentType,
'Cache-Control': ext === '.html' ? 'no-cache' : 'public, max-age=31536000'
});
res.end(content);
} catch (err) {
// File not found, serve index.html for SPA routing
const indexPath = join(DIST_DIR, 'index.html');
const content = await readFile(indexPath);
res.writeHead(200, { 'Content-Type': 'text/html', 'Cache-Control': 'no-cache' });
res.end(content);
}
}
async function proxyRequest(req, res) {
const url = new URL(req.url, `http://localhost:${PORT}`);
const proxyUrl = `${API_PROXY}${url.pathname}${url.search}`;
try {
const proxyRes = await fetch(proxyUrl, {
method: req.method,
headers: {
'Content-Type': 'application/json',
...Object.fromEntries(
Object.entries(req.headers).filter(([k]) => !['host', 'connection'].includes(k))
)
},
body: ['POST', 'PUT', 'PATCH'].includes(req.method)
? await getRequestBody(req)
: undefined
});
const data = await proxyRes.text();
res.writeHead(proxyRes.status, { 'Content-Type': 'application/json' });
res.end(data);
} catch (err) {
console.error('[Proxy Error]', err.message);
res.writeHead(502, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'Proxy error', message: err.message }));
}
}
function getRequestBody(req) {
return new Promise((resolve, reject) => {
let body = '';
req.on('data', chunk => body += chunk);
req.on('end', () => resolve(body));
req.on('error', reject);
});
}
const server = createServer(async (req, res) => {
const url = new URL(req.url, `http://localhost:${PORT}`);
// Health check
if (url.pathname === '/health') {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ status: 'ok', service: 'dss-team-portal' }));
return;
}
// Proxy API requests to DSS MCP server
if (url.pathname.startsWith('/api/')) {
url.pathname = url.pathname.replace('/api', '');
req.url = url.pathname + url.search;
await proxyRequest(req, res);
return;
}
// Serve static files
const filePath = join(DIST_DIR, url.pathname === '/' ? 'index.html' : url.pathname);
await serveFile(res, filePath);
});
server.listen(PORT, () => {
console.log(`[DSS Team Portal] Running on http://localhost:${PORT}`);
console.log(`[DSS Team Portal] API Proxy: ${API_PROXY}`);
});

View File

@@ -0,0 +1,85 @@
/* Base Reset and Typography */
*, *::before, *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
html {
font-size: 16px;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
body {
font-family: var(--dss-font-sans);
background: var(--dss-bg-void);
color: var(--dss-text-primary);
line-height: 1.5;
min-height: 100vh;
overflow: hidden;
}
#app {
width: 100vw;
height: 100vh;
position: relative;
}
a {
color: var(--dss-accent-pulse);
text-decoration: none;
transition: opacity var(--dss-duration-response) var(--dss-ease-smooth);
}
a:hover {
opacity: 0.8;
}
button {
font-family: inherit;
cursor: pointer;
border: none;
background: none;
}
.hidden {
display: none !important;
}
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
/* Focus styles */
:focus-visible {
outline: 2px solid var(--dss-accent-pulse);
outline-offset: 2px;
}
/* Scrollbar */
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
::-webkit-scrollbar-track {
background: var(--dss-bg-space);
}
::-webkit-scrollbar-thumb {
background: var(--dss-text-muted);
border-radius: var(--dss-radius-full);
}
::-webkit-scrollbar-thumb:hover {
background: var(--dss-text-secondary);
}

View File

@@ -0,0 +1,181 @@
/* Conscious Being Animation Styles */
#conscious-canvas {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 0;
pointer-events: none;
}
.landing {
position: relative;
z-index: 1;
width: 100%;
height: 100vh;
display: flex;
align-items: center;
justify-content: center;
}
.landing__content {
text-align: center;
padding: var(--dss-space-8);
}
/* Team Cards */
.team-cards {
display: flex;
gap: var(--dss-space-6);
justify-content: center;
margin-bottom: var(--dss-space-12);
}
.team-card {
width: 200px;
padding: var(--dss-space-8) var(--dss-space-6);
background: rgba(var(--dss-accent-pulse-rgb), 0.05);
border: 1px solid var(--dss-border);
border-radius: var(--dss-radius-xl);
cursor: pointer;
transition: all var(--dss-duration-response) var(--dss-ease-organic);
position: relative;
overflow: hidden;
}
.team-card::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: radial-gradient(
circle at center,
rgba(var(--dss-accent-pulse-rgb), 0.15) 0%,
transparent 70%
);
opacity: 0;
transition: opacity var(--dss-duration-response) var(--dss-ease-smooth);
}
.team-card:hover {
transform: translateY(-4px);
border-color: var(--dss-accent-pulse);
box-shadow: var(--dss-glow-medium);
}
.team-card:hover::before {
opacity: 1;
}
.team-card__icon {
width: 48px;
height: 48px;
margin: 0 auto var(--dss-space-4);
color: var(--dss-accent-pulse);
}
.team-card__icon svg {
width: 100%;
height: 100%;
}
.team-card h2 {
font-size: var(--dss-text-xl);
font-weight: 600;
margin-bottom: var(--dss-space-2);
color: var(--dss-text-primary);
}
.team-card p {
font-size: var(--dss-text-sm);
color: var(--dss-text-secondary);
}
/* Team-specific colors */
.team-card[data-team="ui"]:hover {
--dss-accent-pulse-rgb: 0, 212, 170;
border-color: #00d4aa;
}
.team-card[data-team="ux"]:hover {
--dss-accent-pulse-rgb: 124, 58, 237;
border-color: #7c3aed;
}
.team-card[data-team="qa"]:hover {
--dss-accent-pulse-rgb: 245, 158, 11;
border-color: #f59e0b;
}
/* Admin Link */
.admin-link {
display: inline-block;
font-size: var(--dss-text-sm);
color: var(--dss-text-muted);
padding: var(--dss-space-2) var(--dss-space-4);
border: 1px solid transparent;
border-radius: var(--dss-radius);
transition: all var(--dss-duration-response) var(--dss-ease-smooth);
}
.admin-link:hover {
color: var(--dss-text-secondary);
border-color: var(--dss-border);
opacity: 1;
}
/* Breathing animation for the being */
@keyframes breathe {
0%, 100% {
transform: scale(1);
opacity: 0.8;
}
50% {
transform: scale(1.05);
opacity: 1;
}
}
/* Pulse animation for cards on hover */
@keyframes pulse-glow {
0%, 100% {
box-shadow: var(--dss-glow-soft);
}
50% {
box-shadow: var(--dss-glow-intense);
}
}
.team-card:hover {
animation: pulse-glow 2s ease-in-out infinite;
}
/* Page transition */
.landing.transitioning {
animation: fade-out var(--dss-duration-transition) var(--dss-ease-smooth) forwards;
}
@keyframes fade-out {
to {
opacity: 0;
transform: scale(0.98);
}
}
.dashboard.entering {
animation: fade-in var(--dss-duration-transition) var(--dss-ease-smooth) forwards;
}
@keyframes fade-in {
from {
opacity: 0;
transform: scale(1.02);
}
to {
opacity: 1;
transform: scale(1);
}
}

View File

@@ -0,0 +1,477 @@
/* Dashboard Workbench Styles */
.dashboard {
position: relative;
z-index: 1;
width: 100%;
height: 100vh;
display: flex;
flex-direction: column;
background: var(--dss-bg-void);
}
/* Dashboard Header */
.dashboard-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: var(--dss-space-4) var(--dss-space-6);
background: var(--dss-bg-surface);
border-bottom: 1px solid var(--dss-border);
}
.dashboard-header__left {
display: flex;
align-items: center;
gap: var(--dss-space-4);
}
.back-button {
display: flex;
align-items: center;
gap: var(--dss-space-2);
padding: var(--dss-space-2) var(--dss-space-3);
color: var(--dss-text-secondary);
border-radius: var(--dss-radius);
transition: all var(--dss-duration-response) var(--dss-ease-smooth);
}
.back-button:hover {
background: var(--dss-bg-elevated);
color: var(--dss-text-primary);
}
.back-button svg {
width: 16px;
height: 16px;
}
.team-indicator {
display: flex;
align-items: center;
gap: var(--dss-space-2);
}
.team-indicator__dots {
display: flex;
gap: 4px;
}
.team-indicator__dot {
width: 8px;
height: 8px;
border-radius: var(--dss-radius-full);
background: var(--dss-text-muted);
transition: background var(--dss-duration-response) var(--dss-ease-smooth);
}
.team-indicator__dot.active {
background: var(--dss-accent-pulse);
}
.team-indicator__name {
font-size: var(--dss-text-lg);
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.05em;
}
.dashboard-header__right {
display: flex;
align-items: center;
gap: var(--dss-space-3);
}
.icon-button {
display: flex;
align-items: center;
justify-content: center;
width: 36px;
height: 36px;
border-radius: var(--dss-radius);
color: var(--dss-text-secondary);
transition: all var(--dss-duration-response) var(--dss-ease-smooth);
}
.icon-button:hover {
background: var(--dss-bg-elevated);
color: var(--dss-text-primary);
}
.icon-button svg {
width: 18px;
height: 18px;
}
/* Metrics Bar */
.metrics-bar {
display: flex;
justify-content: center;
gap: var(--dss-space-8);
padding: var(--dss-space-6);
background: var(--dss-bg-space);
border-bottom: 1px solid var(--dss-border);
}
.metric {
text-align: center;
padding: var(--dss-space-4) var(--dss-space-6);
background: var(--dss-bg-surface);
border-radius: var(--dss-radius-lg);
min-width: 140px;
}
.metric__label {
font-size: var(--dss-text-xs);
font-weight: 500;
text-transform: uppercase;
letter-spacing: 0.1em;
color: var(--dss-text-muted);
margin-bottom: var(--dss-space-2);
}
.metric__value {
font-size: var(--dss-text-2xl);
font-weight: 700;
color: var(--dss-text-primary);
}
.metric__value--success {
color: var(--dss-accent-pulse);
}
.metric__value--warning {
color: var(--dss-accent-alert);
}
/* Action Zone */
.action-zone {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
padding: var(--dss-space-8);
overflow-y: auto;
}
.action-zone__content {
max-width: 800px;
width: 100%;
text-align: center;
}
.action-zone__title {
font-size: var(--dss-text-3xl);
font-weight: 300;
color: var(--dss-text-secondary);
margin-bottom: var(--dss-space-4);
}
.action-zone__subtitle {
font-size: var(--dss-text-base);
color: var(--dss-text-muted);
}
/* Action Dock */
.action-dock {
display: flex;
justify-content: center;
gap: var(--dss-space-4);
padding: var(--dss-space-6);
background: var(--dss-bg-surface);
border-top: 1px solid var(--dss-border);
}
.action-button {
display: flex;
flex-direction: column;
align-items: center;
gap: var(--dss-space-2);
padding: var(--dss-space-4) var(--dss-space-6);
background: var(--dss-bg-elevated);
border: 1px solid var(--dss-border);
border-radius: var(--dss-radius-lg);
color: var(--dss-text-secondary);
min-width: 120px;
transition: all var(--dss-duration-response) var(--dss-ease-smooth);
}
.action-button:hover {
background: var(--dss-bg-space);
border-color: var(--dss-accent-pulse);
color: var(--dss-text-primary);
box-shadow: var(--dss-glow-soft);
}
.action-button svg {
width: 24px;
height: 24px;
color: var(--dss-accent-pulse);
}
.action-button__label {
font-size: var(--dss-text-sm);
font-weight: 500;
}
/* Team-specific accent colors */
.dashboard[data-team="ui"] {
--dss-accent-pulse: #00d4aa;
--dss-accent-pulse-rgb: 0, 212, 170;
}
.dashboard[data-team="ux"] {
--dss-accent-pulse: #7c3aed;
--dss-accent-pulse-rgb: 124, 58, 237;
}
.dashboard[data-team="qa"] {
--dss-accent-pulse: #f59e0b;
--dss-accent-pulse-rgb: 245, 158, 11;
}
/* Loading state */
.loading {
display: flex;
align-items: center;
justify-content: center;
gap: var(--dss-space-3);
color: var(--dss-text-muted);
}
.loading__spinner {
width: 20px;
height: 20px;
border: 2px solid var(--dss-border);
border-top-color: var(--dss-accent-pulse);
border-radius: var(--dss-radius-full);
animation: spin 1s linear infinite;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
/* Exit transitions */
.dashboard.exiting {
animation: dashboardExit 0.3s var(--dss-ease-smooth) forwards;
}
@keyframes dashboardExit {
from {
opacity: 1;
transform: translateY(0);
}
to {
opacity: 0;
transform: translateY(-20px);
}
}
/* WebGL Fallback Styles */
.conscious-fallback {
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
pointer-events: none;
}
.fallback-orb {
width: 100px;
height: 100px;
border-radius: 50%;
background: radial-gradient(circle, var(--dss-accent-pulse) 0%, transparent 70%);
animation: fallbackPulse var(--dss-duration-breath) ease-in-out infinite;
}
.fallback-ring {
position: absolute;
width: 200px;
height: 200px;
border: 2px solid var(--dss-accent-pulse);
border-radius: 50%;
opacity: 0.3;
animation: fallbackRing var(--dss-duration-breath) ease-in-out infinite;
}
.fallback-ring--delayed {
animation-delay: calc(var(--dss-duration-breath) / 2);
}
@keyframes fallbackPulse {
0%, 100% {
transform: scale(0.9);
opacity: 0.6;
}
50% {
transform: scale(1.1);
opacity: 1;
}
}
@keyframes fallbackRing {
0%, 100% {
transform: scale(0.8);
opacity: 0.2;
}
50% {
transform: scale(1.2);
opacity: 0.4;
}
}
/* Toast Notifications */
.toast-container {
position: fixed;
bottom: var(--dss-space-6);
right: var(--dss-space-6);
z-index: 1000;
display: flex;
flex-direction: column;
gap: var(--dss-space-3);
}
.toast {
display: flex;
align-items: center;
gap: var(--dss-space-3);
padding: var(--dss-space-4) var(--dss-space-5);
background: var(--dss-bg-elevated);
border: 1px solid var(--dss-border);
border-radius: var(--dss-radius-lg);
box-shadow: var(--dss-glow-soft);
animation: toastIn 0.3s var(--dss-ease-organic);
min-width: 280px;
max-width: 400px;
}
.toast--success {
border-color: var(--dss-accent-pulse);
}
.toast--error {
border-color: var(--dss-accent-error);
}
.toast--warning {
border-color: var(--dss-accent-alert);
}
.toast__icon {
width: 20px;
height: 20px;
flex-shrink: 0;
}
.toast--success .toast__icon {
color: var(--dss-accent-pulse);
}
.toast--error .toast__icon {
color: var(--dss-accent-error);
}
.toast--warning .toast__icon {
color: var(--dss-accent-alert);
}
.toast__message {
flex: 1;
font-size: var(--dss-text-sm);
color: var(--dss-text-primary);
}
.toast__close {
width: 16px;
height: 16px;
color: var(--dss-text-muted);
cursor: pointer;
transition: color var(--dss-duration-response);
}
.toast__close:hover {
color: var(--dss-text-primary);
}
.toast.exiting {
animation: toastOut 0.2s var(--dss-ease-smooth) forwards;
}
@keyframes toastIn {
from {
opacity: 0;
transform: translateX(100%);
}
to {
opacity: 1;
transform: translateX(0);
}
}
@keyframes toastOut {
from {
opacity: 1;
transform: translateX(0);
}
to {
opacity: 0;
transform: translateX(100%);
}
}
/* Error state for metrics */
.metric__value--error {
color: var(--dss-accent-error);
}
/* Action button states */
.action-button.loading {
pointer-events: none;
opacity: 0.7;
}
.action-button.loading svg {
animation: spin 1s linear infinite;
}
.action-button.success {
border-color: var(--dss-accent-pulse);
box-shadow: var(--dss-glow-soft);
}
.action-button.error {
border-color: var(--dss-accent-error);
}
/* Responsive */
@media (max-width: 768px) {
.team-cards {
flex-direction: column;
align-items: center;
}
.team-card {
width: 100%;
max-width: 280px;
}
.metrics-bar {
flex-wrap: wrap;
gap: var(--dss-space-4);
}
.metric {
min-width: 120px;
}
.action-dock {
flex-wrap: wrap;
}
.action-button {
min-width: 100px;
}
}

View File

@@ -0,0 +1,25 @@
/* DSS Team Portal - POC Theme */
:root {
--dss-color-primary-500: #3b82f6;
--dss-color-neutral-100: #f3f4f6;
--dss-color-neutral-900: #111827;
--color-primary: var(--dss-color-primary-500);
}
[data-theme='dark'] {
--bg-glass: rgba(31, 41, 55, 0.5);
--border-glass: rgba(255, 255, 255, 0.1);
--text-primary: var(--dss-color-neutral-100);
--text-secondary: #9ca3af;
--bg-page: var(--dss-color-neutral-900);
}
[data-theme='light'] {
--bg-glass: rgba(255, 255, 255, 0.5);
--border-glass: rgba(0, 0, 0, 0.1);
--text-primary: var(--dss-color-neutral-900);
--text-secondary: #4b5563;
--bg-page: var(--dss-color-neutral-100);
}

View File

@@ -0,0 +1,32 @@
/* DSS Team Portal - Main Styles */
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
background-color: var(--bg-page);
color: var(--text-primary);
overflow: hidden;
}
#webgl-canvas {
position: fixed;
top: 0;
left: 0;
outline: none;
display: block;
}
.ui-container {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
padding: 2rem;
border-radius: 1rem;
background-color: var(--bg-glass);
border: 1px solid var(--border-glass);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
text-align: center;
z-index: 10;
}

View File

@@ -0,0 +1,90 @@
/* DSS Workbench Theme Tokens */
:root {
/* Deep Space Backgrounds */
--dss-bg-void: #0a0a0f;
--dss-bg-space: #0d1117;
--dss-bg-surface: #161b22;
--dss-bg-elevated: #1c2128;
/* Bioluminescent Accents */
--dss-accent-pulse: #00d4aa;
--dss-accent-pulse-rgb: 0, 212, 170;
--dss-accent-aware: #7c3aed;
--dss-accent-aware-rgb: 124, 58, 237;
--dss-accent-alert: #f59e0b;
--dss-accent-error: #ef4444;
/* Text Hierarchy */
--dss-text-primary: #f0f6fc;
--dss-text-secondary: #8b949e;
--dss-text-muted: #484f58;
/* Borders */
--dss-border: rgba(240, 246, 252, 0.1);
--dss-border-focus: rgba(0, 212, 170, 0.4);
/* Glow Effects */
--dss-glow-soft: 0 0 20px rgba(0, 212, 170, 0.15);
--dss-glow-medium: 0 0 40px rgba(0, 212, 170, 0.25);
--dss-glow-intense: 0 0 60px rgba(0, 212, 170, 0.4);
/* Motion */
--dss-ease-organic: cubic-bezier(0.34, 1.56, 0.64, 1);
--dss-ease-smooth: cubic-bezier(0.4, 0, 0.2, 1);
--dss-duration-breath: 4s;
--dss-duration-response: 0.3s;
--dss-duration-transition: 0.5s;
/* Spacing */
--dss-space-1: 0.25rem;
--dss-space-2: 0.5rem;
--dss-space-3: 0.75rem;
--dss-space-4: 1rem;
--dss-space-5: 1.25rem;
--dss-space-6: 1.5rem;
--dss-space-8: 2rem;
--dss-space-10: 2.5rem;
--dss-space-12: 3rem;
/* Typography */
--dss-font-sans: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
--dss-font-mono: ui-monospace, 'SF Mono', 'Cascadia Code', monospace;
--dss-text-xs: 0.75rem;
--dss-text-sm: 0.875rem;
--dss-text-base: 1rem;
--dss-text-lg: 1.125rem;
--dss-text-xl: 1.25rem;
--dss-text-2xl: 1.5rem;
--dss-text-3xl: 2rem;
/* Radius */
--dss-radius-sm: 0.25rem;
--dss-radius: 0.5rem;
--dss-radius-lg: 0.75rem;
--dss-radius-xl: 1rem;
--dss-radius-full: 9999px;
}
/* Light Mode */
[data-theme="light"] {
--dss-bg-void: #fafbfc;
--dss-bg-space: #f6f8fa;
--dss-bg-surface: #ffffff;
--dss-bg-elevated: #ffffff;
--dss-accent-pulse: #059669;
--dss-accent-pulse-rgb: 5, 150, 105;
--dss-accent-aware: #6d28d9;
--dss-accent-aware-rgb: 109, 40, 217;
--dss-text-primary: #1f2937;
--dss-text-secondary: #4b5563;
--dss-text-muted: #9ca3af;
--dss-border: rgba(31, 41, 55, 0.1);
--dss-border-focus: rgba(5, 150, 105, 0.4);
--dss-glow-soft: 0 0 20px rgba(5, 150, 105, 0.1);
--dss-glow-medium: 0 0 40px rgba(5, 150, 105, 0.15);
--dss-glow-intense: 0 0 60px rgba(5, 150, 105, 0.25);
}

View File

@@ -0,0 +1,75 @@
// DSS Team Portal - State Bridge & Token Synchronizer
import { subscribe } from './store.js';
import { camera } from './scene.js';
import { updateCrystalColor } from './scene.js';
import gsap from 'gsap';
const uiContainer = document.getElementById('ui-container');
function initializeBridge() {
// 1. StateBridge: Subscribe to store changes and trigger animations
subscribe((state) => {
const { viewState } = state;
if (viewState === 'TRANSITION_TO_2D') {
gsap.to(camera.position, {
duration: 1.5,
z: 10, // Dolly out
ease: 'power2.inOut',
});
gsap.to(uiContainer, {
duration: 1.5,
opacity: 1,
y: 0,
ease: 'power2.inOut',
onComplete: () => store.getState().actions.setViewState('2D_WORKBENCH')
});
}
if (viewState === 'TRANSITION_TO_3D') {
gsap.to(camera.position, {
duration: 1.5,
z: 5, // Dolly in
ease: 'power2.inOut',
});
gsap.to(uiContainer, {
duration: 1.5,
opacity: 0,
y: 20,
ease: 'power2.inOut',
onComplete: () => store.getState().actions.setViewState('3D_HOME')
});
}
});
// Set initial UI state
gsap.set(uiContainer, { opacity: 0, y: 20 });
}
function initializeTokenSynchronizer() {
// 2. TokenSynchronizer: Sync theme colors to the 3D scene
const sync = () => {
const style = getComputedStyle(document.body);
const primaryColor = style.getPropertyValue('--color-primary').trim();
if (primaryColor) {
updateCrystalColor(primaryColor);
}
};
// Sync on init
sync();
// Sync when theme changes
subscribe((state, prevState) => {
if (state.theme !== prevState.theme) {
document.body.dataset.theme = state.theme;
// Allow time for CSS to apply
setTimeout(sync, 50);
}
});
// Set initial theme
document.body.dataset.theme = store.getState().theme;
}
export { initializeBridge, initializeTokenSynchronizer };

View File

@@ -0,0 +1,59 @@
// Action Dock Component
export function ActionDock({ actions, onAction }) {
return `
<nav class="action-dock">
${actions.map(action => ActionButton(action, onAction)).join('')}
</nav>
`;
}
export function ActionButton({ id, label, icon }) {
return `
<button class="action-button" data-action="${id}">
${icon}
<span class="action-button__label">${label}</span>
</button>
`;
}
// Common action icons
export const ActionIcons = {
extract: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="12" cy="12" r="10"/>
<circle cx="12" cy="12" r="4"/>
</svg>`,
sync: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M23 4v6h-6"/>
<path d="M1 20v-6h6"/>
<path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"/>
</svg>`,
code: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<polyline points="16 18 22 12 16 6"/>
<polyline points="8 6 2 12 8 18"/>
</svg>`,
validate: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/>
<polyline points="22 4 12 14.01 9 11.01"/>
</svg>`,
search: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="11" cy="11" r="8"/>
<line x1="21" y1="21" x2="16.65" y2="16.65"/>
</svg>`,
report: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/>
<polyline points="14 2 14 8 20 8"/>
<line x1="16" y1="13" x2="8" y2="13"/>
<line x1="16" y1="17" x2="8" y2="17"/>
</svg>`,
lightning: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/>
</svg>`
};
export default ActionDock;

View File

@@ -0,0 +1,27 @@
// Header Component
export function Header({ team, onBack, onThemeToggle }) {
return `
<header class="dashboard-header">
<div class="dashboard-header__left">
<button class="back-button" onclick="${onBack}" aria-label="Back to home">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M19 12H5M12 19l-7-7 7-7"/>
</svg>
<span>back</span>
</button>
<div class="team-indicator">
<span class="team-indicator__name">${team.toUpperCase()} TEAM</span>
</div>
</div>
<div class="dashboard-header__right">
<button class="icon-button" onclick="${onThemeToggle}" aria-label="Toggle theme">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 1 1-8 0 4 4 0 0 1 8 0z"/>
</svg>
</button>
</div>
</header>
`;
}
export default Header;

View File

@@ -0,0 +1,28 @@
// Metrics Bar Component
export function MetricsBar({ metrics }) {
const entries = Object.entries(metrics);
return `
<div class="metrics-bar">
${entries.map(([label, value]) => `
<div class="metric">
<div class="metric__label">${label}</div>
<div class="metric__value">${value}</div>
</div>
`).join('')}
</div>
`;
}
export function Metric({ label, value, status }) {
const statusClass = status ? `metric__value--${status}` : '';
return `
<div class="metric">
<div class="metric__label">${label}</div>
<div class="metric__value ${statusClass}">${value}</div>
</div>
`;
}
export default MetricsBar;

View File

@@ -0,0 +1,47 @@
// Team Card Component
export function TeamCard({ team, title, description, icon, onClick }) {
return `
<div class="team-card" data-team="${team}" onclick="${onClick}">
<div class="team-card__icon">
${icon}
</div>
<h2>${title}</h2>
<p>${description}</p>
</div>
`;
}
// Team configurations
export const TeamConfigs = {
ui: {
title: 'UI Team',
description: 'Components & Tokens',
icon: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="3" y="3" width="18" height="18" rx="2"/>
<path d="M3 9h18"/>
<path d="M9 21V9"/>
</svg>`,
color: '#00d4aa'
},
ux: {
title: 'UX Team',
description: 'Consistency & Validation',
icon: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="12" cy="12" r="10"/>
<path d="M12 16v-4"/>
<path d="M12 8h.01"/>
</svg>`,
color: '#7c3aed'
},
qa: {
title: 'QA Team',
description: 'Quality & Testing',
icon: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/>
<polyline points="22 4 12 14.01 9 11.01"/>
</svg>`,
color: '#f59e0b'
}
};
export default TeamCard;

View File

@@ -0,0 +1,215 @@
// Particle Behaviors - Flocking, Breathing, Following
import { lerp, distance, clamp } from '../utils/math.js';
// Base behavior class
class Behavior {
constructor() {
this.enabled = true;
this.strength = 1.0;
}
apply(particleSystem, params) {
// Override in subclasses
}
onModeChange(newMode, oldMode) {
// Override in subclasses
}
}
// Breathing behavior - organic pulsing
export class BreathingBehavior extends Behavior {
constructor() {
super();
this.frequency = 0.25; // Cycles per second (4s period)
this.amplitude = 0.15; // 15% scale variation
this.baseScale = 1.0;
}
apply(particleSystem, params) {
if (!this.enabled) return;
const { time } = params;
const phase = Math.sin(time * this.frequency * Math.PI * 2 * 0.001);
const scale = this.baseScale + phase * this.amplitude * this.strength;
// Apply breathing to all particles - expand/contract from center
for (let i = 0; i < particleSystem.count; i++) {
const pos = particleSystem.getPosition(i);
const dist = Math.sqrt(pos.x * pos.x + pos.y * pos.y);
if (dist > 0.01) {
const breathForce = (scale - 1) * 0.001;
const forceX = (pos.x / dist) * breathForce;
const forceY = (pos.y / dist) * breathForce;
particleSystem.applyForce(i, forceX, forceY);
}
}
}
onModeChange(newMode) {
if (newMode === 'background') {
this.strength = 0.3;
} else {
this.strength = 1.0;
}
}
}
// Flocking behavior - boids algorithm
export class FlockingBehavior extends Behavior {
constructor() {
super();
this.separationRadius = 0.05;
this.alignmentRadius = 0.1;
this.cohesionRadius = 0.15;
this.separationWeight = 0.003;
this.alignmentWeight = 0.001;
this.cohesionWeight = 0.001;
}
apply(particleSystem, params) {
if (!this.enabled) return;
const count = particleSystem.count;
// Sample subset for performance
const sampleSize = Math.min(100, count);
const step = Math.floor(count / sampleSize);
for (let i = 0; i < count; i += step) {
const pos = particleSystem.getPosition(i);
let sepX = 0, sepY = 0, sepCount = 0;
let alignX = 0, alignY = 0, alignCount = 0;
let cohX = 0, cohY = 0, cohCount = 0;
// Check neighbors (sample for performance)
for (let j = 0; j < count; j += step) {
if (i === j) continue;
const other = particleSystem.getPosition(j);
const dist = distance(pos.x, pos.y, other.x, other.y);
// Separation
if (dist < this.separationRadius && dist > 0) {
const factor = 1 - dist / this.separationRadius;
sepX += (pos.x - other.x) / dist * factor;
sepY += (pos.y - other.y) / dist * factor;
sepCount++;
}
// Alignment
if (dist < this.alignmentRadius) {
const vel = {
x: particleSystem.velocities[j * 3],
y: particleSystem.velocities[j * 3 + 1]
};
alignX += vel.x;
alignY += vel.y;
alignCount++;
}
// Cohesion
if (dist < this.cohesionRadius) {
cohX += other.x;
cohY += other.y;
cohCount++;
}
}
// Apply forces
let forceX = 0, forceY = 0;
if (sepCount > 0) {
forceX += (sepX / sepCount) * this.separationWeight * this.strength;
forceY += (sepY / sepCount) * this.separationWeight * this.strength;
}
if (alignCount > 0) {
const avgVelX = alignX / alignCount;
const avgVelY = alignY / alignCount;
forceX += avgVelX * this.alignmentWeight * this.strength;
forceY += avgVelY * this.alignmentWeight * this.strength;
}
if (cohCount > 0) {
const centerX = cohX / cohCount;
const centerY = cohY / cohCount;
forceX += (centerX - pos.x) * this.cohesionWeight * this.strength;
forceY += (centerY - pos.y) * this.cohesionWeight * this.strength;
}
particleSystem.applyForce(i, forceX, forceY);
}
}
onModeChange(newMode) {
if (newMode === 'background') {
this.strength = 0.2;
} else if (newMode === 'engaged') {
this.strength = 0.5;
} else {
this.strength = 1.0;
}
}
}
// Follow behavior - follow mouse or target
export class FollowBehavior extends Behavior {
constructor() {
super();
this.mouseWeight = 0.0005;
this.targetWeight = 0.002;
this.returnWeight = 0.0001;
}
apply(particleSystem, params) {
if (!this.enabled) return;
const { mousePosition, target, mode } = params;
for (let i = 0; i < particleSystem.count; i++) {
const pos = particleSystem.getPosition(i);
let forceX = 0, forceY = 0;
// Convert mouse to scene coordinates (-1 to 1)
const mouseX = (mousePosition.x - 0.5) * 2;
const mouseY = (mousePosition.y - 0.5) * 2;
if (target && mode === 'engaged') {
// Follow target
const targetX = (target.x - 0.5) * 2;
const targetY = (target.y - 0.5) * 2;
forceX += (targetX - pos.x) * this.targetWeight * this.strength;
forceY += (targetY - pos.y) * this.targetWeight * this.strength;
} else if (mode !== 'background') {
// Follow mouse
forceX += (mouseX - pos.x) * this.mouseWeight * this.strength;
forceY += (mouseY - pos.y) * this.mouseWeight * this.strength;
}
// Return to center force (keep particles from drifting too far)
const distFromCenter = Math.sqrt(pos.x * pos.x + pos.y * pos.y);
if (distFromCenter > 0.5) {
const returnStrength = (distFromCenter - 0.5) * this.returnWeight;
forceX -= pos.x * returnStrength;
forceY -= pos.y * returnStrength;
}
particleSystem.applyForce(i, forceX, forceY);
}
}
onModeChange(newMode) {
if (newMode === 'background') {
this.strength = 0;
} else if (newMode === 'engaged') {
this.strength = 2.0;
} else {
this.strength = 1.0;
}
}
}
export default { BreathingBehavior, FlockingBehavior, FollowBehavior };

View File

@@ -0,0 +1,201 @@
// The Conscious Being - Main Controller
import { Renderer } from './Renderer.js';
import { ParticleSystem } from './ParticleSystem.js';
import { FlockingBehavior, BreathingBehavior, FollowBehavior } from './Behaviors.js';
export class Being {
constructor(canvas) {
this.canvas = canvas;
this.renderer = null;
this.particleSystem = null;
this.behaviors = [];
this.mousePosition = { x: 0.5, y: 0.5 }; // Normalized 0-1
this.target = null;
this.mode = 'idle'; // idle, aware, engaged, background
this.isRunning = false;
this.lastTime = 0;
this.initialized = false;
// Store bound handlers for cleanup
this._resizeHandler = this.handleResize.bind(this);
this.init();
}
init() {
try {
// Initialize renderer with WebGL error handling
this.renderer = new Renderer(this.canvas);
// Initialize particle system
this.particleSystem = new ParticleSystem(800);
// Initialize behaviors
this.behaviors = [
new BreathingBehavior(),
new FlockingBehavior(),
new FollowBehavior()
];
// Add particle system to scene
this.renderer.add(this.particleSystem.mesh);
// Handle resize
window.addEventListener('resize', this._resizeHandler);
this.initialized = true;
} catch (error) {
console.error('[Being] WebGL initialization failed:', error);
this.showFallback();
}
}
showFallback() {
// Show CSS-based fallback animation when WebGL is unavailable
if (this.canvas) {
this.canvas.style.display = 'none';
const fallback = document.createElement('div');
fallback.className = 'conscious-fallback';
fallback.innerHTML = `
<div class="fallback-orb"></div>
<div class="fallback-ring"></div>
<div class="fallback-ring fallback-ring--delayed"></div>
`;
this.canvas.parentNode?.insertBefore(fallback, this.canvas);
}
}
start() {
if (this.isRunning || !this.initialized) return;
this.isRunning = true;
this.lastTime = performance.now();
this.animate();
console.log('[Being] Started');
}
stop() {
this.isRunning = false;
console.log('[Being] Stopped');
}
animate() {
if (!this.isRunning) return;
const currentTime = performance.now();
const deltaTime = (currentTime - this.lastTime) / 1000; // Convert to seconds
this.lastTime = currentTime;
this.update(deltaTime, currentTime);
this.renderer.render();
requestAnimationFrame(() => this.animate());
}
update(deltaTime, time) {
// Apply behaviors based on mode
const params = {
time,
deltaTime,
mousePosition: this.mousePosition,
target: this.target,
mode: this.mode
};
// Apply each behavior
this.behaviors.forEach(behavior => {
behavior.apply(this.particleSystem, params);
});
// Update particle system
this.particleSystem.update(deltaTime);
// Update uniforms
this.particleSystem.material.uniforms.uTime.value = time * 0.001;
this.particleSystem.material.uniforms.uMouse.value.set(
this.mousePosition.x,
this.mousePosition.y
);
}
updateMousePosition(x, y) {
// Normalize to 0-1
this.mousePosition = {
x: x / window.innerWidth,
y: 1 - (y / window.innerHeight) // Flip Y for WebGL
};
// Update mode based on mouse activity
if (this.mode === 'idle') {
this.setMode('aware');
}
}
setTarget(element) {
if (!element) {
this.target = null;
return;
}
const rect = element.getBoundingClientRect();
this.target = {
x: (rect.left + rect.width / 2) / window.innerWidth,
y: 1 - ((rect.top + rect.height / 2) / window.innerHeight),
width: rect.width / window.innerWidth,
height: rect.height / window.innerHeight
};
this.setMode('engaged');
}
clearTarget() {
this.target = null;
if (this.mode === 'engaged') {
this.setMode('aware');
}
}
setMode(mode) {
if (this.mode === mode) return;
const oldMode = this.mode;
this.mode = mode;
console.log(`[Being] Mode: ${oldMode} -> ${mode}`);
// Adjust behaviors based on mode
this.behaviors.forEach(behavior => {
behavior.onModeChange(mode, oldMode);
});
// Adjust particle appearance
if (mode === 'background') {
this.particleSystem.setOpacity(0.3);
} else {
this.particleSystem.setOpacity(1.0);
}
}
handleResize() {
this.renderer.handleResize();
}
dispose() {
this.stop();
// Remove event listeners
window.removeEventListener('resize', this._resizeHandler);
// Dispose of WebGL resources
if (this.renderer) {
this.renderer.dispose();
}
if (this.particleSystem) {
this.particleSystem.dispose();
}
this.initialized = false;
}
}
export default Being;

View File

@@ -0,0 +1,193 @@
// WebGL Particle System with Custom Shaders
import * as THREE from 'three';
import { randomInRange, lerp } from '../utils/math.js';
export class ParticleSystem {
constructor(count = 800) {
this.count = count;
this.positions = null;
this.velocities = null;
this.colors = null;
this.sizes = null;
this.geometry = null;
this.material = null;
this.mesh = null;
this.baseOpacity = 1.0;
this.targetOpacity = 1.0;
this.init();
}
init() {
// Initialize arrays
this.positions = new Float32Array(this.count * 3);
this.velocities = new Float32Array(this.count * 3);
this.colors = new Float32Array(this.count * 3);
this.sizes = new Float32Array(this.count);
// Initialize particles in organic cluster
this.initializeParticles();
// Create geometry
this.geometry = new THREE.BufferGeometry();
this.geometry.setAttribute('position', new THREE.BufferAttribute(this.positions, 3));
this.geometry.setAttribute('color', new THREE.BufferAttribute(this.colors, 3));
this.geometry.setAttribute('size', new THREE.BufferAttribute(this.sizes, 1));
// Create shader material
this.material = this.createMaterial();
// Create points mesh
this.mesh = new THREE.Points(this.geometry, this.material);
}
initializeParticles() {
const primaryColor = new THREE.Color(0x00d4aa); // Teal
const secondaryColor = new THREE.Color(0x7c3aed); // Purple
for (let i = 0; i < this.count; i++) {
const i3 = i * 3;
// Position in organic cluster (gaussian-like distribution)
const radius = Math.pow(Math.random(), 0.5) * 0.5;
const theta = Math.random() * Math.PI * 2;
const phi = Math.acos(2 * Math.random() - 1);
this.positions[i3] = radius * Math.sin(phi) * Math.cos(theta);
this.positions[i3 + 1] = radius * Math.sin(phi) * Math.sin(theta);
this.positions[i3 + 2] = 0; // Keep 2D for now
// Random velocities
this.velocities[i3] = randomInRange(-0.001, 0.001);
this.velocities[i3 + 1] = randomInRange(-0.001, 0.001);
this.velocities[i3 + 2] = 0;
// Colors - mix between primary and secondary
const mixRatio = Math.random();
const color = primaryColor.clone().lerp(secondaryColor, mixRatio);
this.colors[i3] = color.r;
this.colors[i3 + 1] = color.g;
this.colors[i3 + 2] = color.b;
// Sizes - varied for organic look
this.sizes[i] = randomInRange(0.005, 0.02);
}
}
createMaterial() {
const vertexShader = `
attribute float size;
attribute vec3 color;
uniform float uTime;
uniform vec2 uMouse;
uniform float uOpacity;
varying vec3 vColor;
varying float vOpacity;
void main() {
vColor = color;
vOpacity = uOpacity;
vec4 mvPosition = modelViewMatrix * vec4(position, 1.0);
// Size attenuation
gl_PointSize = size * (300.0 / -mvPosition.z);
gl_Position = projectionMatrix * mvPosition;
}
`;
const fragmentShader = `
varying vec3 vColor;
varying float vOpacity;
void main() {
// Circular particle with soft edges
float dist = length(gl_PointCoord - vec2(0.5));
if (dist > 0.5) discard;
// Soft glow falloff
float alpha = 1.0 - smoothstep(0.0, 0.5, dist);
alpha *= vOpacity;
// Add inner glow
float innerGlow = 1.0 - smoothstep(0.0, 0.3, dist);
vec3 glowColor = vColor + vec3(0.2) * innerGlow;
gl_FragColor = vec4(glowColor, alpha * 0.8);
}
`;
return new THREE.ShaderMaterial({
uniforms: {
uTime: { value: 0 },
uMouse: { value: new THREE.Vector2(0.5, 0.5) },
uOpacity: { value: 1.0 }
},
vertexShader,
fragmentShader,
transparent: true,
depthWrite: false,
blending: THREE.AdditiveBlending
});
}
update(deltaTime) {
// Smooth opacity transition
this.baseOpacity = lerp(this.baseOpacity, this.targetOpacity, 0.05);
this.material.uniforms.uOpacity.value = this.baseOpacity;
// Update positions based on velocities
for (let i = 0; i < this.count; i++) {
const i3 = i * 3;
this.positions[i3] += this.velocities[i3];
this.positions[i3 + 1] += this.velocities[i3 + 1];
this.positions[i3 + 2] += this.velocities[i3 + 2];
// Apply friction
this.velocities[i3] *= 0.99;
this.velocities[i3 + 1] *= 0.99;
this.velocities[i3 + 2] *= 0.99;
}
// Mark geometry for update
this.geometry.attributes.position.needsUpdate = true;
}
setOpacity(opacity) {
this.targetOpacity = opacity;
}
applyForce(index, forceX, forceY, forceZ = 0) {
const i3 = index * 3;
this.velocities[i3] += forceX;
this.velocities[i3 + 1] += forceY;
this.velocities[i3 + 2] += forceZ;
}
getPosition(index) {
const i3 = index * 3;
return {
x: this.positions[i3],
y: this.positions[i3 + 1],
z: this.positions[i3 + 2]
};
}
setPosition(index, x, y, z = 0) {
const i3 = index * 3;
this.positions[i3] = x;
this.positions[i3 + 1] = y;
this.positions[i3 + 2] = z;
}
dispose() {
this.geometry.dispose();
this.material.dispose();
}
}
export default ParticleSystem;

View File

@@ -0,0 +1,72 @@
// Three.js Renderer Setup
import * as THREE from 'three';
export class Renderer {
constructor(canvas) {
this.canvas = canvas;
this.scene = null;
this.camera = null;
this.renderer = null;
this.init();
}
init() {
// Create scene
this.scene = new THREE.Scene();
// Create camera (orthographic for 2D-like particles)
const aspect = window.innerWidth / window.innerHeight;
this.camera = new THREE.OrthographicCamera(
-aspect, aspect, 1, -1, 0.1, 1000
);
this.camera.position.z = 1;
// Create renderer
this.renderer = new THREE.WebGLRenderer({
canvas: this.canvas,
antialias: true,
alpha: true
});
this.renderer.setSize(window.innerWidth, window.innerHeight);
this.renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
this.renderer.setClearColor(0x000000, 0);
console.log('[Renderer] Initialized');
}
add(object) {
this.scene.add(object);
}
remove(object) {
this.scene.remove(object);
}
render() {
this.renderer.render(this.scene, this.camera);
}
handleResize() {
const width = window.innerWidth;
const height = window.innerHeight;
const aspect = width / height;
// Update camera
this.camera.left = -aspect;
this.camera.right = aspect;
this.camera.updateProjectionMatrix();
// Update renderer
this.renderer.setSize(width, height);
this.renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
}
dispose() {
this.renderer.dispose();
this.scene.clear();
}
}
export default Renderer;

View File

@@ -0,0 +1,177 @@
// API Client for DSS MCP Server
const API_BASE = import.meta.env?.VITE_API_BASE || '/api';
const isDev = import.meta.env?.DEV;
class APIClient {
constructor(baseUrl = API_BASE) {
this.baseUrl = baseUrl;
this.cache = new Map();
this.cacheTimeout = 30000; // 30 seconds
}
async request(endpoint, options = {}) {
const url = `${this.baseUrl}${endpoint}`;
const config = {
headers: {
'Content-Type': 'application/json',
...options.headers
},
...options
};
try {
const response = await fetch(url, config);
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
throw new APIError(
errorData.message || `API Error: ${response.status} ${response.statusText}`,
response.status,
errorData
);
}
return await response.json();
} catch (error) {
if (error instanceof APIError) throw error;
if (isDev) console.error('[API]', error);
throw new APIError(error.message || 'Network error', 0, null);
}
}
async get(endpoint, useCache = false) {
if (useCache) {
const cached = this.cache.get(endpoint);
if (cached && Date.now() - cached.timestamp < this.cacheTimeout) {
return cached.data;
}
}
const data = await this.request(endpoint, { method: 'GET' });
if (useCache) {
this.cache.set(endpoint, { data, timestamp: Date.now() });
}
return data;
}
async post(endpoint, data) {
return this.request(endpoint, {
method: 'POST',
body: JSON.stringify(data)
});
}
clearCache() {
this.cache.clear();
}
// DSS-specific methods
async getHealth() {
return this.get('/health', true);
}
async analyzeProject(path) {
return this.post('/tools/dss_analyze_project', { path });
}
async extractTokens(path, sources = ['css', 'scss']) {
return this.post('/tools/dss_extract_tokens', { path, sources });
}
async auditComponents(path) {
return this.post('/tools/dss_audit_components', { path });
}
async findQuickWins(path) {
return this.post('/tools/dss_find_quick_wins', { path });
}
async generateTheme(format, themeName = 'default') {
return this.post('/tools/dss_generate_theme', { format, theme_name: themeName });
}
async syncFigma(fileKey) {
return this.post('/tools/dss_sync_figma', { file_key: fileKey });
}
async setupStorybook(path, action = 'scan') {
return this.post('/tools/dss_setup_storybook', { path, action });
}
async getStatus() {
return this.get('/status', true);
}
async listThemes() {
return this.get('/themes', true);
}
// Aggregated metrics for dashboards
async getTeamMetrics(team) {
try {
const status = await this.getStatus();
// Derive metrics from DSS status
const baseMetrics = {
tokens: status.token_count || 0,
themes: status.themes?.length || 0,
healthy: status.healthy || false
};
switch (team) {
case 'ui':
return {
components: status.component_count || 0,
tokens: baseMetrics.tokens,
synced: status.figma_connected || false,
stories: status.storybook_stories || 0
};
case 'ux':
return {
consistency: status.consistency_score || 0,
violations: status.violation_count || 0,
patterns: status.pattern_count || 0,
tokens: baseMetrics.tokens
};
case 'qa':
return {
health: status.health_score || 0,
quickWins: status.quick_win_count || 0,
unused: status.unused_count || 0,
coverage: status.coverage_percent || 0
};
default:
return {};
}
} catch (error) {
if (isDev) console.warn('[API] Failed to fetch metrics, using defaults');
// Return sensible defaults on error
return this.getDefaultMetrics(team);
}
}
getDefaultMetrics(team) {
const defaults = {
ui: { components: 0, tokens: 0, synced: false, stories: 0 },
ux: { consistency: 0, violations: 0, patterns: 0, tokens: 0 },
qa: { health: 0, quickWins: 0, unused: 0, coverage: 0 }
};
return defaults[team] || {};
}
}
// Custom API Error class
class APIError extends Error {
constructor(message, status, data) {
super(message);
this.name = 'APIError';
this.status = status;
this.data = data;
}
}
export const api = new APIClient();
export { APIError };
export default api;

View File

@@ -0,0 +1,36 @@
// SPA Router using History API
let routeHandler = null;
export function initRouter(handler) {
routeHandler = handler;
// Handle browser back/forward
window.addEventListener('popstate', () => {
if (routeHandler) {
routeHandler(window.location.pathname);
}
});
// Handle initial load
const path = window.location.pathname;
if (routeHandler && path !== '/') {
routeHandler(path);
}
}
export function navigate(path) {
if (window.location.pathname !== path) {
window.history.pushState({}, '', path);
if (routeHandler) {
routeHandler(path);
}
}
}
export function getCurrentRoute() {
return window.location.pathname;
}
export function goBack() {
window.history.back();
}

View File

@@ -0,0 +1,55 @@
// Zustand-like store (vanilla implementation for simplicity)
const createStore = (initialState) => {
let state = { ...initialState };
const listeners = new Set();
const getState = () => state;
const setState = (partial) => {
const nextState = typeof partial === 'function' ? partial(state) : partial;
state = { ...state, ...nextState };
listeners.forEach(listener => listener(state));
};
const subscribe = (listener) => {
listeners.add(listener);
return () => listeners.delete(listener);
};
return { getState, setState, subscribe };
};
// Create the store
const store = createStore({
route: '/',
team: null,
theme: 'dark',
beingMode: 'idle',
metrics: {
ui: { components: 0, tokens: 0, synced: false, stories: 0 },
ux: { consistency: 0, violations: 0, patterns: 0, tokens: 0 },
qa: { health: 0, quickWins: 0, unused: 0, coverage: 0 }
}
});
// Helper functions
export const useStore = {
getState: store.getState,
subscribe: store.subscribe,
setRoute: (route) => store.setState({ route }),
setTeam: (team) => store.setState({ team }),
setTheme: (theme) => store.setState({ theme }),
setBeingMode: (beingMode) => store.setState({ beingMode }),
setMetrics: (team, metrics) => {
const currentMetrics = store.getState().metrics;
store.setState({
metrics: {
...currentMetrics,
[team]: { ...currentMetrics[team], ...metrics }
}
});
}
};
export default store;

View File

@@ -0,0 +1,37 @@
// Theme Management
import { useStore } from './store.js';
const THEME_KEY = 'dss-portal-theme';
export function initTheme() {
// Check for saved theme or system preference
const savedTheme = localStorage.getItem(THEME_KEY);
const systemPrefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
const initialTheme = savedTheme || (systemPrefersDark ? 'dark' : 'light');
setTheme(initialTheme);
// Listen for system theme changes
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (e) => {
if (!localStorage.getItem(THEME_KEY)) {
setTheme(e.matches ? 'dark' : 'light');
}
});
}
export function setTheme(theme) {
document.documentElement.setAttribute('data-theme', theme);
localStorage.setItem(THEME_KEY, theme);
useStore.setTheme(theme);
}
export function toggleTheme() {
const currentTheme = useStore.getState().theme;
const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
setTheme(newTheme);
return newTheme;
}
export function getTheme() {
return useStore.getState().theme;
}

View File

@@ -0,0 +1,175 @@
// Base Dashboard - Shared Dashboard Logic
import { navigate } from '../core/router.js';
import { toggleTheme, getTheme } from '../core/theme.js';
import { api } from '../core/api.js';
import { UIDashboard } from './UIDashboard.js';
import { UXDashboard } from './UXDashboard.js';
import { QADashboard } from './QADashboard.js';
const dashboards = {
ui: UIDashboard,
ux: UXDashboard,
qa: QADashboard
};
export function renderDashboard(team, container) {
const DashboardClass = dashboards[team];
if (!DashboardClass) {
console.error(`[Dashboard] Unknown team: ${team}`);
return;
}
const dashboard = new DashboardClass(team);
container.innerHTML = dashboard.render();
// Setup event listeners after render
setupDashboardEvents(dashboard, container);
// Load metrics
dashboard.loadMetrics();
}
export function hideDashboard(container) {
container.classList.add('hidden');
container.innerHTML = '';
}
function setupDashboardEvents(dashboard, container) {
// Back button
const backBtn = container.querySelector('.back-button');
if (backBtn) {
backBtn.addEventListener('click', () => {
container.classList.remove('entering');
container.classList.add('exiting');
const handleTransitionEnd = () => {
container.removeEventListener('transitionend', handleTransitionEnd);
navigate('/');
};
container.addEventListener('transitionend', handleTransitionEnd);
// Fallback in case transitionend doesn't fire
setTimeout(() => {
container.removeEventListener('transitionend', handleTransitionEnd);
if (window.location.pathname !== '/') {
navigate('/');
}
}, 500);
});
}
// Theme toggle
const themeBtn = container.querySelector('.theme-toggle');
if (themeBtn) {
themeBtn.addEventListener('click', () => {
const newTheme = toggleTheme();
updateThemeIcon(themeBtn, newTheme);
});
updateThemeIcon(themeBtn, getTheme());
}
// Action buttons
container.querySelectorAll('.action-button').forEach(btn => {
btn.addEventListener('click', () => {
const action = btn.dataset.action;
if (action && dashboard.handleAction) {
dashboard.handleAction(action);
}
});
});
}
function updateThemeIcon(button, theme) {
const icon = button.querySelector('svg');
if (theme === 'dark') {
icon.innerHTML = `
<path d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 1 1-8 0 4 4 0 0 1 8 0z"/>
`;
} else {
icon.innerHTML = `
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/>
`;
}
}
// Base Dashboard Class
export class BaseDashboard {
constructor(team) {
this.team = team;
this.metrics = {};
}
render() {
return `
<header class="dashboard-header">
<div class="dashboard-header__left">
<button class="back-button" aria-label="Back to home">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M19 12H5M12 19l-7-7 7-7"/>
</svg>
<span>back</span>
</button>
<div class="team-indicator">
<div class="team-indicator__dots">
<span class="team-indicator__dot ${this.team === 'ui' ? 'active' : ''}"></span>
<span class="team-indicator__dot ${this.team === 'ux' ? 'active' : ''}"></span>
<span class="team-indicator__dot ${this.team === 'qa' ? 'active' : ''}"></span>
</div>
<span class="team-indicator__name">${this.team.toUpperCase()} Team</span>
</div>
</div>
<div class="dashboard-header__right">
<button class="icon-button theme-toggle" aria-label="Toggle theme">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 1 1-8 0 4 4 0 0 1 8 0z"/>
</svg>
</button>
</div>
</header>
${this.renderMetricsBar()}
${this.renderActionZone()}
${this.renderActionDock()}
`;
}
renderMetricsBar() {
// Override in subclasses
return '<div class="metrics-bar"></div>';
}
renderActionZone() {
return `
<main class="action-zone">
<div class="action-zone__content">
<h1 class="action-zone__title">Select an action</h1>
<p class="action-zone__subtitle">Choose from the dock below to get started</p>
</div>
</main>
`;
}
renderActionDock() {
// Override in subclasses
return '<nav class="action-dock"></nav>';
}
async loadMetrics() {
try {
this.metrics = await api.getTeamMetrics(this.team);
this.updateMetricsDisplay();
} catch (error) {
console.error('[Dashboard] Failed to load metrics:', error);
}
}
updateMetricsDisplay() {
// Override in subclasses
}
handleAction(action) {
console.log(`[Dashboard] Action: ${action}`);
// Override in subclasses
}
}
export default BaseDashboard;

View File

@@ -0,0 +1,180 @@
// QA Team Dashboard
import { BaseDashboard } from './BaseDashboard.js';
import { api } from '../core/api.js';
export class QADashboard extends BaseDashboard {
constructor(team) {
super(team);
}
renderMetricsBar() {
return `
<div class="metrics-bar">
<div class="metric">
<div class="metric__label">Health Score</div>
<div class="metric__value" id="metric-health">--%</div>
</div>
<div class="metric">
<div class="metric__label">Quick Wins</div>
<div class="metric__value" id="metric-quickwins">--</div>
</div>
<div class="metric">
<div class="metric__label">Unused Styles</div>
<div class="metric__value" id="metric-unused">--</div>
</div>
<div class="metric">
<div class="metric__label">Coverage</div>
<div class="metric__value" id="metric-coverage">--%</div>
</div>
</div>
`;
}
renderActionZone() {
return `
<main class="action-zone">
<div class="action-zone__content">
<h1 class="action-zone__title">Quality Assurance</h1>
<p class="action-zone__subtitle">Find issues, validate components, and improve coverage</p>
</div>
</main>
`;
}
renderActionDock() {
return `
<nav class="action-dock">
<button class="action-button" data-action="quick-wins">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/>
</svg>
<span class="action-button__label">Quick Wins</span>
</button>
<button class="action-button" data-action="find-unused">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="11" cy="11" r="8"/>
<line x1="21" y1="21" x2="16.65" y2="16.65"/>
<line x1="8" y1="11" x2="14" y2="11"/>
</svg>
<span class="action-button__label">Find Unused</span>
</button>
<button class="action-button" data-action="audit-components">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="3" y="3" width="18" height="18" rx="2"/>
<path d="M3 9h18"/>
<path d="M9 21V9"/>
</svg>
<span class="action-button__label">Audit Components</span>
</button>
<button class="action-button" data-action="coverage">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M22 12h-4l-3 9L9 3l-3 9H2"/>
</svg>
<span class="action-button__label">Coverage</span>
</button>
</nav>
`;
}
updateMetricsDisplay() {
const { health, quickWins, unused, coverage } = this.metrics;
const healthEl = document.getElementById('metric-health');
const quickWinsEl = document.getElementById('metric-quickwins');
const unusedEl = document.getElementById('metric-unused');
const coverageEl = document.getElementById('metric-coverage');
if (healthEl) {
healthEl.textContent = `${health || 0}%`;
healthEl.classList.toggle('metric__value--success', health >= 80);
healthEl.classList.toggle('metric__value--warning', health < 80 && health >= 60);
}
if (quickWinsEl) {
quickWinsEl.textContent = quickWins || 0;
quickWinsEl.classList.toggle('metric__value--success', quickWins > 0);
}
if (unusedEl) {
unusedEl.textContent = unused || 0;
unusedEl.classList.toggle('metric__value--warning', unused > 10);
}
if (coverageEl) {
coverageEl.textContent = `${coverage || 0}%`;
coverageEl.classList.toggle('metric__value--success', coverage >= 80);
}
}
async handleAction(action) {
console.log(`[QA Dashboard] Action: ${action}`);
switch (action) {
case 'quick-wins':
await this.findQuickWins();
break;
case 'find-unused':
this.showMessage('Find Unused - Coming soon');
break;
case 'audit-components':
await this.auditComponents();
break;
case 'coverage':
this.showMessage('Coverage Analysis - Coming soon');
break;
}
}
async findQuickWins() {
this.showLoading('Finding quick wins...');
try {
const result = await api.findQuickWins('.');
const wins = result.quick_wins?.length || 0;
this.showMessage(`Found ${wins} quick wins`);
await this.loadMetrics();
} catch (error) {
this.showError('Failed to find quick wins');
}
}
async auditComponents() {
this.showLoading('Auditing components...');
try {
const result = await api.auditComponents('.');
const issues = result.issues?.length || 0;
this.showMessage(`Audit complete: ${issues} issues found`);
await this.loadMetrics();
} catch (error) {
this.showError('Failed to audit components');
}
}
showLoading(message) {
const zone = document.querySelector('.action-zone__content');
if (zone) {
zone.innerHTML = `
<div class="loading">
<div class="loading__spinner"></div>
<span>${message}</span>
</div>
`;
}
}
showMessage(message) {
const zone = document.querySelector('.action-zone__content');
if (zone) {
zone.innerHTML = `
<h1 class="action-zone__title">${message}</h1>
`;
}
}
showError(message) {
const zone = document.querySelector('.action-zone__content');
if (zone) {
zone.innerHTML = `
<h1 class="action-zone__title" style="color: var(--dss-accent-error)">${message}</h1>
`;
}
}
}
export default QADashboard;

View File

@@ -0,0 +1,174 @@
// UI Team Dashboard
import { BaseDashboard } from './BaseDashboard.js';
import { api } from '../core/api.js';
export class UIDashboard extends BaseDashboard {
constructor(team) {
super(team);
}
renderMetricsBar() {
return `
<div class="metrics-bar">
<div class="metric">
<div class="metric__label">Components</div>
<div class="metric__value" id="metric-components">--</div>
</div>
<div class="metric">
<div class="metric__label">Tokens</div>
<div class="metric__value" id="metric-tokens">--</div>
</div>
<div class="metric">
<div class="metric__label">Figma Sync</div>
<div class="metric__value" id="metric-synced">--</div>
</div>
<div class="metric">
<div class="metric__label">Stories</div>
<div class="metric__value" id="metric-stories">--</div>
</div>
</div>
`;
}
renderActionZone() {
return `
<main class="action-zone">
<div class="action-zone__content">
<h1 class="action-zone__title">Component Library</h1>
<p class="action-zone__subtitle">Manage tokens, components, and Figma synchronization</p>
</div>
</main>
`;
}
renderActionDock() {
return `
<nav class="action-dock">
<button class="action-button" data-action="extract-tokens">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="12" cy="12" r="10"/>
<circle cx="12" cy="12" r="4"/>
</svg>
<span class="action-button__label">Extract Tokens</span>
</button>
<button class="action-button" data-action="sync-figma">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M5 5.5A3.5 3.5 0 0 1 8.5 2H12v7H8.5A3.5 3.5 0 0 1 5 5.5z"/>
<path d="M12 2h3.5a3.5 3.5 0 1 1 0 7H12V2z"/>
<path d="M12 12.5a3.5 3.5 0 1 1 7 0 3.5 3.5 0 1 1-7 0z"/>
<path d="M5 19.5A3.5 3.5 0 0 1 8.5 16H12v3.5a3.5 3.5 0 1 1-7 0z"/>
<path d="M5 12.5A3.5 3.5 0 0 1 8.5 9H12v7H8.5A3.5 3.5 0 0 1 5 12.5z"/>
</svg>
<span class="action-button__label">Sync Figma</span>
</button>
<button class="action-button" data-action="generate-code">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<polyline points="16 18 22 12 16 6"/>
<polyline points="8 6 2 12 8 18"/>
</svg>
<span class="action-button__label">Generate Code</span>
</button>
<button class="action-button" data-action="storybook">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"/>
<path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"/>
</svg>
<span class="action-button__label">Storybook</span>
</button>
</nav>
`;
}
updateMetricsDisplay() {
const { components, tokens, synced, stories } = this.metrics;
const componentsEl = document.getElementById('metric-components');
const tokensEl = document.getElementById('metric-tokens');
const syncedEl = document.getElementById('metric-synced');
const storiesEl = document.getElementById('metric-stories');
if (componentsEl) componentsEl.textContent = components || 0;
if (tokensEl) tokensEl.textContent = tokens || 0;
if (syncedEl) {
syncedEl.textContent = synced ? 'OK' : '--';
syncedEl.classList.toggle('metric__value--success', synced);
}
if (storiesEl) storiesEl.textContent = stories || 0;
}
async handleAction(action) {
console.log(`[UI Dashboard] Action: ${action}`);
switch (action) {
case 'extract-tokens':
await this.extractTokens();
break;
case 'sync-figma':
await this.syncFigma();
break;
case 'generate-code':
this.showMessage('Generate Code - Coming soon');
break;
case 'storybook':
window.open('http://localhost:6006', '_blank');
break;
}
}
async extractTokens() {
this.showLoading('Extracting tokens...');
try {
const result = await api.extractTokens('.', ['css', 'scss']);
this.showMessage(`Extracted ${result.tokens?.length || 0} tokens`);
await this.loadMetrics();
} catch (error) {
this.showError('Failed to extract tokens');
}
}
async syncFigma() {
const fileKey = prompt('Enter Figma file key:');
if (!fileKey) return;
this.showLoading('Syncing with Figma...');
try {
const result = await api.syncFigma(fileKey);
this.showMessage('Figma sync complete');
await this.loadMetrics();
} catch (error) {
this.showError('Failed to sync Figma');
}
}
showLoading(message) {
const zone = document.querySelector('.action-zone__content');
if (zone) {
zone.innerHTML = `
<div class="loading">
<div class="loading__spinner"></div>
<span>${message}</span>
</div>
`;
}
}
showMessage(message) {
const zone = document.querySelector('.action-zone__content');
if (zone) {
zone.innerHTML = `
<h1 class="action-zone__title">${message}</h1>
`;
}
}
showError(message) {
const zone = document.querySelector('.action-zone__content');
if (zone) {
zone.innerHTML = `
<h1 class="action-zone__title" style="color: var(--dss-accent-error)">${message}</h1>
`;
}
}
}
export default UIDashboard;

View File

@@ -0,0 +1,178 @@
// UX Team Dashboard
import { BaseDashboard } from './BaseDashboard.js';
import { api } from '../core/api.js';
export class UXDashboard extends BaseDashboard {
constructor(team) {
super(team);
}
renderMetricsBar() {
return `
<div class="metrics-bar">
<div class="metric">
<div class="metric__label">Consistency</div>
<div class="metric__value" id="metric-consistency">--%</div>
</div>
<div class="metric">
<div class="metric__label">Violations</div>
<div class="metric__value" id="metric-violations">--</div>
</div>
<div class="metric">
<div class="metric__label">Patterns</div>
<div class="metric__value" id="metric-patterns">--</div>
</div>
<div class="metric">
<div class="metric__label">Tokens</div>
<div class="metric__value" id="metric-tokens">--</div>
</div>
</div>
`;
}
renderActionZone() {
return `
<main class="action-zone">
<div class="action-zone__content">
<h1 class="action-zone__title">Design Consistency</h1>
<p class="action-zone__subtitle">Validate tokens, analyze patterns, and ensure brand compliance</p>
</div>
</main>
`;
}
renderActionDock() {
return `
<nav class="action-dock">
<button class="action-button" data-action="validate-tokens">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/>
<polyline points="22 4 12 14.01 9 11.01"/>
</svg>
<span class="action-button__label">Validate Tokens</span>
</button>
<button class="action-button" data-action="analyze-patterns">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M21 21H3"/>
<path d="M21 21V9l-9-6-9 6v12"/>
<path d="M9 21V12h6v9"/>
</svg>
<span class="action-button__label">Style Patterns</span>
</button>
<button class="action-button" data-action="check-naming">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M12 2L2 7l10 5 10-5-10-5z"/>
<path d="M2 17l10 5 10-5"/>
<path d="M2 12l10 5 10-5"/>
</svg>
<span class="action-button__label">Check Naming</span>
</button>
<button class="action-button" data-action="export-report">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/>
<polyline points="14 2 14 8 20 8"/>
<line x1="16" y1="13" x2="8" y2="13"/>
<line x1="16" y1="17" x2="8" y2="17"/>
</svg>
<span class="action-button__label">Export Report</span>
</button>
</nav>
`;
}
updateMetricsDisplay() {
const { consistency, violations, patterns, tokens } = this.metrics;
const consistencyEl = document.getElementById('metric-consistency');
const violationsEl = document.getElementById('metric-violations');
const patternsEl = document.getElementById('metric-patterns');
const tokensEl = document.getElementById('metric-tokens');
if (consistencyEl) {
consistencyEl.textContent = `${consistency || 0}%`;
consistencyEl.classList.toggle('metric__value--success', consistency >= 80);
consistencyEl.classList.toggle('metric__value--warning', consistency < 80 && consistency >= 60);
}
if (violationsEl) {
violationsEl.textContent = violations || 0;
violationsEl.classList.toggle('metric__value--warning', violations > 0);
}
if (patternsEl) patternsEl.textContent = patterns || 0;
if (tokensEl) tokensEl.textContent = tokens || 0;
}
async handleAction(action) {
console.log(`[UX Dashboard] Action: ${action}`);
switch (action) {
case 'validate-tokens':
await this.validateTokens();
break;
case 'analyze-patterns':
await this.analyzePatterns();
break;
case 'check-naming':
this.showMessage('Naming Check - Coming soon');
break;
case 'export-report':
this.showMessage('Export Report - Coming soon');
break;
}
}
async validateTokens() {
this.showLoading('Validating tokens...');
try {
const result = await api.analyzeProject('.');
const violations = result.issues?.length || 0;
this.showMessage(`Found ${violations} validation issues`);
await this.loadMetrics();
} catch (error) {
this.showError('Failed to validate tokens');
}
}
async analyzePatterns() {
this.showLoading('Analyzing patterns...');
try {
const result = await api.analyzeProject('.');
const patterns = result.patterns?.length || 0;
this.showMessage(`Found ${patterns} style patterns`);
await this.loadMetrics();
} catch (error) {
this.showError('Failed to analyze patterns');
}
}
showLoading(message) {
const zone = document.querySelector('.action-zone__content');
if (zone) {
zone.innerHTML = `
<div class="loading">
<div class="loading__spinner"></div>
<span>${message}</span>
</div>
`;
}
}
showMessage(message) {
const zone = document.querySelector('.action-zone__content');
if (zone) {
zone.innerHTML = `
<h1 class="action-zone__title">${message}</h1>
`;
}
}
showError(message) {
const zone = document.querySelector('.action-zone__content');
if (zone) {
zone.innerHTML = `
<h1 class="action-zone__title" style="color: var(--dss-accent-error)">${message}</h1>
`;
}
}
}
export default UXDashboard;

View File

@@ -0,0 +1,21 @@
// DSS Team Portal - Main Entry Point
import { initScene } from './scene.js';
import { actions } from './store.js';
import { initializeBridge, initializeTokenSynchronizer } from './bridge.js';
// Initialize the 3D Scene
initScene();
// Initialize the bridge to connect state to UI/3D
initializeBridge();
// Initialize the synchronizer to connect theme to 3D
initializeTokenSynchronizer();
// Add event listener for the toggle button
const toggleButton = document.getElementById('toggle-view-btn');
toggleButton.addEventListener('click', () => {
actions.toggleViewState();
});
console.log("DSS Team Portal POC Initialized");

View File

@@ -0,0 +1,62 @@
// DSS Team Portal - Three.js Scene
import * as THREE from 'three';
let scene, camera, renderer, crystal;
function init() {
// Scene
scene = new THREE.Scene();
// Camera
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.z = 5;
// Renderer
const canvas = document.getElementById('webgl-canvas');
renderer = new THREE.WebGLRenderer({ canvas, antialias: true, alpha: true });
renderer.setSize(window.innerWidth, window.innerHeight);
// "Fake Crystal" - using a simple material for POC
const geometry = new THREE.IcosahedronGeometry(1.5, 0);
const material = new THREE.MeshBasicMaterial({ color: 0xff0000, wireframe: true });
// Add a uniform for color control
material.onBeforeCompile = (shader) => {
shader.uniforms.uColor = { value: new THREE.Color(0xff0000) };
shader.fragmentShader = `
uniform vec3 uColor;
${shader.fragmentShader}
`.replace(
`vec4 diffuseColor = vec4( diffuse, opacity );`,
`vec4 diffuseColor = vec4( uColor, opacity );`
);
crystal.userData.shader = shader;
};
crystal = new THREE.Mesh(geometry, material);
scene.add(crystal);
// Animation Loop
function animate() {
requestAnimationFrame(animate);
crystal.rotation.x += 0.001;
crystal.rotation.y += 0.001;
renderer.render(scene, camera);
}
animate();
// Handle window resize
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
}
function updateCrystalColor(color) {
if (crystal && crystal.userData.shader) {
crystal.userData.shader.uniforms.uColor.value.set(color);
}
}
export { init as initScene, updateCrystalColor, camera, crystal };

View File

@@ -0,0 +1,28 @@
// DSS Team Portal - Zustand Store
import { createStore } from 'zustand/vanilla';
const store = createStore((set) => ({
viewState: '3D_HOME', // '3D_HOME' | 'TRANSITION_TO_2D' | '2D_WORKBENCH' | 'TRANSITION_TO_3D'
theme: 'dark', // 'dark' | 'light'
actions: {
toggleViewState: () => set((state) => {
if (state.viewState === '3D_HOME') {
return { viewState: 'TRANSITION_TO_2D' };
}
if (state.viewState === '2D_WORKBENCH') {
return { viewState: 'TRANSITION_TO_3D' };
}
return {}; // Do nothing during transitions
}),
setViewState: (viewState) => set({ viewState }),
toggleTheme: () => set((state) => ({ theme: state.theme === 'dark' ? 'light' : 'dark' })),
}
}));
// Export the store and actions for convenience
const { getState, setState, subscribe } = store;
const { actions } = store.getState();
export { getState, setState, subscribe, actions };
export default store;

View File

@@ -0,0 +1,85 @@
// Easing Functions
export const easing = {
// Linear
linear: t => t,
// Quadratic
easeInQuad: t => t * t,
easeOutQuad: t => t * (2 - t),
easeInOutQuad: t => t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t,
// Cubic
easeInCubic: t => t * t * t,
easeOutCubic: t => (--t) * t * t + 1,
easeInOutCubic: t => t < 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1,
// Quartic
easeInQuart: t => t * t * t * t,
easeOutQuart: t => 1 - (--t) * t * t * t,
easeInOutQuart: t => t < 0.5 ? 8 * t * t * t * t : 1 - 8 * (--t) * t * t * t,
// Sine
easeInSine: t => 1 - Math.cos((t * Math.PI) / 2),
easeOutSine: t => Math.sin((t * Math.PI) / 2),
easeInOutSine: t => -(Math.cos(Math.PI * t) - 1) / 2,
// Exponential
easeInExpo: t => t === 0 ? 0 : Math.pow(2, 10 * t - 10),
easeOutExpo: t => t === 1 ? 1 : 1 - Math.pow(2, -10 * t),
easeInOutExpo: t => {
if (t === 0) return 0;
if (t === 1) return 1;
if (t < 0.5) return Math.pow(2, 20 * t - 10) / 2;
return (2 - Math.pow(2, -20 * t + 10)) / 2;
},
// Elastic
easeInElastic: t => {
const c4 = (2 * Math.PI) / 3;
return t === 0 ? 0 : t === 1 ? 1 : -Math.pow(2, 10 * t - 10) * Math.sin((t * 10 - 10.75) * c4);
},
easeOutElastic: t => {
const c4 = (2 * Math.PI) / 3;
return t === 0 ? 0 : t === 1 ? 1 : Math.pow(2, -10 * t) * Math.sin((t * 10 - 0.75) * c4) + 1;
},
easeInOutElastic: t => {
const c5 = (2 * Math.PI) / 4.5;
if (t === 0) return 0;
if (t === 1) return 1;
if (t < 0.5) return -(Math.pow(2, 20 * t - 10) * Math.sin((20 * t - 11.125) * c5)) / 2;
return (Math.pow(2, -20 * t + 10) * Math.sin((20 * t - 11.125) * c5)) / 2 + 1;
},
// Back
easeInBack: t => {
const c1 = 1.70158;
const c3 = c1 + 1;
return c3 * t * t * t - c1 * t * t;
},
easeOutBack: t => {
const c1 = 1.70158;
const c3 = c1 + 1;
return 1 + c3 * Math.pow(t - 1, 3) + c1 * Math.pow(t - 1, 2);
},
// Bounce
easeOutBounce: t => {
const n1 = 7.5625;
const d1 = 2.75;
if (t < 1 / d1) return n1 * t * t;
if (t < 2 / d1) return n1 * (t -= 1.5 / d1) * t + 0.75;
if (t < 2.5 / d1) return n1 * (t -= 2.25 / d1) * t + 0.9375;
return n1 * (t -= 2.625 / d1) * t + 0.984375;
},
// Organic - custom for the being
organic: t => {
// Combination of sine and elastic for natural movement
const sine = Math.sin(t * Math.PI);
const elastic = Math.pow(2, -10 * t) * Math.sin((t * 10 - 0.75) * (2 * Math.PI / 3)) + 1;
return (sine + elastic) / 2;
}
};
export default easing;

View File

@@ -0,0 +1,47 @@
// Math Utilities
export function randomInRange(min, max) {
return Math.random() * (max - min) + min;
}
export function lerp(start, end, t) {
return start + (end - start) * t;
}
export function clamp(value, min, max) {
return Math.max(min, Math.min(max, value));
}
export function distance(x1, y1, x2, y2) {
const dx = x2 - x1;
const dy = y2 - y1;
return Math.sqrt(dx * dx + dy * dy);
}
export function normalize(x, y) {
const len = Math.sqrt(x * x + y * y);
if (len === 0) return { x: 0, y: 0 };
return { x: x / len, y: y / len };
}
export function map(value, inMin, inMax, outMin, outMax) {
return ((value - inMin) / (inMax - inMin)) * (outMax - outMin) + outMin;
}
export function smoothstep(edge0, edge1, x) {
const t = clamp((x - edge0) / (edge1 - edge0), 0, 1);
return t * t * (3 - 2 * t);
}
export function easeOutElastic(t) {
const c4 = (2 * Math.PI) / 3;
return t === 0
? 0
: t === 1
? 1
: Math.pow(2, -10 * t) * Math.sin((t * 10 - 0.75) * c4) + 1;
}
export function easeInOutCubic(t) {
return t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2;
}

159
team-portal/src/main.js Normal file
View File

@@ -0,0 +1,159 @@
// DSS Team Portal - Main Entry Point
import { initRouter, navigate } from './js/core/router.js';
import { useStore } from './js/core/store.js';
import { initTheme } from './js/core/theme.js';
import { Being } from './js/conscious/Being.js';
import { renderDashboard, hideDashboard } from './js/dashboards/BaseDashboard.js';
// Initialize the application
class TeamPortal {
constructor() {
this.being = null;
this.canvas = null;
}
async init() {
console.log('[DSS] Initializing Team Portal...');
// Initialize theme
initTheme();
// Initialize router
initRouter(this.handleRoute.bind(this));
// Initialize canvas and being
this.canvas = document.getElementById('conscious-canvas');
if (this.canvas) {
this.being = new Being(this.canvas);
this.being.start();
}
// Setup event listeners
this.setupEventListeners();
// Handle initial route
this.handleRoute(window.location.pathname);
console.log('[DSS] Team Portal initialized');
}
setupEventListeners() {
// Team card clicks and keyboard navigation
document.querySelectorAll('.team-card').forEach(card => {
const handleActivation = () => {
const team = card.dataset.team;
this.navigateToTeam(team);
};
card.addEventListener('click', handleActivation);
// Keyboard accessibility
card.addEventListener('keydown', (e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
handleActivation();
}
});
// Mouse interaction with being
card.addEventListener('mouseenter', () => {
if (this.being) {
this.being.setTarget(card);
}
});
card.addEventListener('mouseleave', () => {
if (this.being) {
this.being.clearTarget();
}
});
});
// Admin link - points to DSS API admin interface
const ADMIN_URL = import.meta.env?.VITE_ADMIN_URL || '/api/admin';
const adminLink = document.querySelector('.admin-link');
if (adminLink) {
adminLink.addEventListener('click', (e) => {
e.preventDefault();
// Open API docs/admin in new tab
window.open(ADMIN_URL, '_blank');
});
}
// Global mouse tracking for being
document.addEventListener('mousemove', (e) => {
if (this.being) {
this.being.updateMousePosition(e.clientX, e.clientY);
}
});
}
navigateToTeam(team) {
const landing = document.getElementById('landing');
landing.classList.add('transitioning');
const handleTransitionEnd = () => {
landing.removeEventListener('transitionend', handleTransitionEnd);
navigate(`/${team}`);
};
landing.addEventListener('transitionend', handleTransitionEnd);
// Fallback for browsers/cases where transitionend doesn't fire
setTimeout(() => {
landing.removeEventListener('transitionend', handleTransitionEnd);
if (window.location.pathname !== `/${team}`) {
navigate(`/${team}`);
}
}, 400);
}
handleRoute(path) {
const store = useStore.getState();
const landing = document.getElementById('landing');
const dashboard = document.getElementById('dashboard');
if (path === '/' || path === '') {
// Show landing
landing.classList.remove('hidden', 'transitioning');
dashboard.classList.add('hidden');
dashboard.classList.remove('entering');
store.setRoute('/');
if (this.being) {
this.being.setMode('idle');
}
} else {
// Extract team from path (handles /ui, /ux, /qa)
const segments = path.split('/').filter(Boolean);
const team = segments[0];
if (['ui', 'ux', 'qa'].includes(team)) {
// Show dashboard
landing.classList.add('hidden');
dashboard.classList.remove('hidden');
dashboard.classList.add('entering');
dashboard.dataset.team = team;
store.setRoute(path);
store.setTeam(team);
renderDashboard(team, dashboard);
if (this.being) {
this.being.setMode('background');
}
} else {
// Redirect unknown routes to landing
navigate('/');
}
}
}
}
// Initialize on DOM ready
document.addEventListener('DOMContentLoaded', () => {
const portal = new TeamPortal();
portal.init();
});
export default TeamPortal;

View File

@@ -0,0 +1,20 @@
import { defineConfig } from 'vite';
export default defineConfig({
root: '.',
publicDir: 'public',
server: {
port: 3457,
host: true,
proxy: {
'/api': {
target: 'http://localhost:3456',
changeOrigin: true
}
}
},
build: {
outDir: 'dist',
assetsDir: 'assets'
}
});