Compare commits

...

2 Commits

Author SHA1 Message Date
faa19beef3 Fix import paths and remove organ metaphors
Some checks failed
DSS Project Analysis / dss-context-update (push) Has been cancelled
- Update all `from storage.` imports to `from dss.storage.`
- Update `from config import config` to use `dss.settings`
- Update `from auth.` imports to `from dss.auth.`
- Update health check to use `dss.mcp.handler`
- Fix SmartMerger import (merger.py not smart_merger.py)
- Fix TranslationDictionary import path
- Fix test assertion for networkx edges/links
- Remove organ/body metaphors from:
  - API server health check
  - CLI status command and help text
  - Admin UI logger and error handler

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-10 13:05:00 -03:00
41fba59bf7 Major refactor: Consolidate DSS into unified package structure
- Create new dss/ Python package at project root
- Move MCP core from tools/dss_mcp/ to dss/mcp/
- Move storage layer from tools/storage/ to dss/storage/
- Move domain logic from dss-mvp1/dss/ to dss/
- Move services from tools/api/services/ to dss/services/
- Move API server to apps/api/
- Move CLI to apps/cli/
- Move Storybook assets to storybook/
- Create unified dss/__init__.py with comprehensive exports
- Merge configuration into dss/settings.py (Pydantic-based)
- Create pyproject.toml for proper package management
- Update startup scripts for new paths
- Remove old tools/ and dss-mvp1/ directories

Architecture changes:
- DSS is now MCP-first with 40+ tools for Claude Code
- Clean imports: from dss import Projects, Components, FigmaToolSuite
- No more sys.path.insert() hacking
- apps/ contains thin application wrappers (API, CLI)
- Single unified Python package for all DSS logic

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-10 12:46:43 -03:00
201 changed files with 3379 additions and 15738 deletions

View File

@@ -1,147 +1,139 @@
/**
* DSS Error Handler - Immune System Antibodies
* DSS Error Handler
*
* The DSS Component's immune system uses these antibodies to detect and report threats.
* Converts technical errors into human-friendly, actionable treatment plans.
* Converts technical errors into user-friendly, actionable messages.
* Integrates with the messaging system for structured error reporting.
*
* Biological Framework: These error messages use component metaphors to make
* issues intuitive. See docs/DSS_ORGANISM_GUIDE.md for the full framework.
*
* @module error-handler
*/
import { notifyError, ErrorCode } from './messaging.js';
/**
* Error message templates with component metaphors
*
* These messages use biological language from the DSS Component Framework.
* Each error is framed as a symptom the immune system detected, with
* a diagnosis and treatment plan.
* Error message templates with user-friendly descriptions
*/
const errorMessages = {
// Figma API Errors - Sensory System Issues
// Figma API Errors
figma_403: {
title: '🛡️ IMMUNE ALERT: Sensory Input Blocked',
message: 'The DSS sensory organs cannot perceive the Figma file. Your access credentials lack permission.',
title: 'Access Denied',
message: 'Cannot access the Figma file. Your access credentials lack permission.',
actions: [
'Verify your Figma authentication token in Settings (nervous system communication)',
'Confirm you have access to this file in Figma (sensory perception)',
'Check if the file still exists (component awareness)',
'Verify your Figma authentication token in Settings',
'Confirm you have access to this file in Figma',
'Check if the file still exists',
],
code: ErrorCode.FIGMA_API_ERROR,
},
figma_404: {
title: '🛡️ IMMUNE ALERT: Sensory Target Lost',
message: 'The Figma file the DSS sensory organs were trying to perceive doesn\'t exist or is inaccessible.',
title: 'File Not Found',
message: 'The Figma file doesn\'t exist or is inaccessible.',
actions: [
'Double-check your Figma file key in Settings (sensory focus)',
'Double-check your Figma file key in Settings',
'Verify the file hasn\'t been deleted in Figma',
'Confirm you have access to the file in Figma (sensory perception)',
'Confirm you have access to the file in Figma',
],
code: ErrorCode.FIGMA_API_ERROR,
},
figma_401: {
title: '🔌 NERVOUS SYSTEM ALERT: Authentication Expired',
message: 'The DSS nervous system\'s authentication with Figma has failed. Your sensory input token is invalid or expired.',
title: 'Authentication Failed',
message: 'Figma authentication failed. Your access token is invalid or expired.',
actions: [
'Refresh your Figma authentication token in Settings (nervous system repair)',
'Get a fresh token from figma.com/settings (Account Personal Access Tokens)',
'Refresh your Figma authentication token in Settings',
'Get a fresh token from figma.com/settings (Account -> Personal Access Tokens)',
'Ensure you copied the full token without truncation',
],
code: ErrorCode.FIGMA_CONNECTION_FAILED,
},
figma_429: {
title: '⚡ METABOLISM ALERT: Sensory Overload',
message: 'The DSS is sensing too quickly. Figma\'s rate limits have been triggered.',
title: 'Rate Limit Exceeded',
message: 'Too many requests. Figma\'s rate limits have been triggered.',
actions: [
'Let the component rest for 1-2 minutes before sensing again',
'Reduce how frequently the sensory system extracts data',
'Wait 1-2 minutes before trying again',
'Reduce how frequently you extract data',
],
code: ErrorCode.FIGMA_API_ERROR,
},
figma_500: {
title: '🔌 EXTERNAL SYSTEM ALERT: Figma Component Stressed',
message: 'Figma\'s servers are experiencing stress. This is external to DSS.',
title: 'Figma Server Error',
message: 'Figma\'s servers are experiencing issues. This is external to DSS.',
actions: [
'Wait while the external component recovers',
'Check Figma health: status.figma.com',
'Wait and try again later',
'Check Figma status: status.figma.com',
],
code: ErrorCode.FIGMA_API_ERROR,
},
figma_demo: {
title: '🛡️ IMMUNE ALERT: Invalid Sensory Configuration',
message: 'The sensory organs are configured to look at "demo" which doesn\'t exist in Figma.',
title: 'Invalid Configuration',
message: 'The Figma file key is configured to "demo" which doesn\'t exist in Figma.',
actions: [
'Update Settings with your real Figma file key (configure sensory input)',
'Update Settings with your real Figma file key',
'Find your file key in the Figma URL: figma.com/file/[FILE_KEY]/...',
'Use the Figma file selector in Settings',
],
code: ErrorCode.FIGMA_INVALID_KEY,
},
// API Connection Errors - Nervous System / Heart Issues
// API Connection Errors
api_network: {
title: '❤️ CRITICAL: Heart Not Responding',
message: 'The DSS nervous system cannot reach the heart (server). The component is not responding.',
title: 'Server Not Responding',
message: 'Cannot connect to the DSS server.',
actions: [
'Verify the heart is beating: curl http://localhost:3456/health',
'Restart the heart: cd tools/api && python3 -m uvicorn server:app --port 3456',
'Check your network connection to the component',
'Verify the server is running: curl http://localhost:3456/health',
'Restart the server: ./scripts/dss start',
'Check your network connection',
],
code: ErrorCode.SYSTEM_NETWORK,
},
api_timeout: {
title: '⚡ METABOLISM ALERT: Component Overloaded',
message: 'The DSS component took too long to respond. The heart may be stressed or metabolism sluggish.',
title: 'Request Timeout',
message: 'The server took too long to respond.',
actions: [
'Let the component rest and try again shortly',
'Check the heart\'s logs for signs of stress: tail -f /tmp/dss-demo.log',
'Reduce metabolic load (try processing smaller batches)',
'Try again in a few moments',
'Check the server logs: ./scripts/dss logs',
'Try processing smaller batches',
],
code: ErrorCode.API_TIMEOUT,
},
api_500: {
title: '🧠 BRAIN ALERT: Critical Processing Error',
message: 'The DSS brain encountered a fatal error while processing your request.',
title: 'Server Error',
message: 'The server encountered an error while processing your request.',
actions: [
'Examine the brain\'s thoughts in the logs: tail -f /tmp/dss-demo.log',
'Retry the operation to see if it recovers',
'Report the issue if the component keeps failing',
'Check the server logs: ./scripts/dss logs',
'Retry the operation',
'Report the issue if it persists',
],
code: ErrorCode.API_SERVER_ERROR,
},
// Validation Errors - Immune System / Genetics
// Validation Errors
validation_missing_field: {
title: '🛡️ IMMUNE ALERT: DNA Incomplete',
message: 'The genetic code (configuration) is missing essential information. The component cannot proceed.',
title: 'Missing Required Field',
message: 'The configuration is missing required information.',
actions: [
'Fill in all fields marked as required (complete the genetic code)',
'Fill in all fields marked as required',
'Ensure each input field contains valid information',
],
code: ErrorCode.VALIDATION_MISSING_FIELD,
},
validation_invalid_format: {
title: '🛡️ IMMUNE ALERT: Genetic Mutation Detected',
message: 'One or more genetic sequences (configuration values) have an invalid format.',
title: 'Invalid Format',
message: 'One or more configuration values have an invalid format.',
actions: [
'Verify URLs start with http:// or https:// (correct genetic sequence)',
'Check email addresses follow standard format (valid genetic code)',
'Ensure file keys contain only letters, numbers, and hyphens (genetic pattern match)',
'Verify URLs start with http:// or https://',
'Check email addresses follow standard format',
'Ensure file keys contain only letters, numbers, and hyphens',
],
code: ErrorCode.VALIDATION_INVALID_FORMAT,
},
// Generic fallback
unknown: {
title: '🧬 ORGANISM ALERT: Unexplained Symptom',
message: 'The DSS component experienced an unexpected problem. The root cause is unclear.',
title: 'Unexpected Error',
message: 'An unexpected problem occurred.',
actions: [
'Try the operation again (component may self-heal)',
'Refresh if the issue persists (restart vitals)',
'Check browser console for clues about the component\'s condition',
'Try the operation again',
'Refresh the page if the issue persists',
'Check browser console for details',
],
code: ErrorCode.SYSTEM_UNEXPECTED,
},
@@ -253,7 +245,7 @@ export function handleError(error, context = {}) {
});
// Log full details to console for debugging
console.group(`🔴 ${parsed.title}`);
console.group(`[ERROR] ${parsed.title}`);
console.log('Message:', parsed.message);
if (parsed.actions) {
console.log('Actions:', parsed.actions);
@@ -281,23 +273,23 @@ export async function tryWithErrorHandling(fn, context = {}) {
}
/**
* Get user-friendly HTTP status message using component metaphors
* Get user-friendly HTTP status message
* @param {number} status - HTTP status code
* @returns {string} User-friendly message with biological context
* @returns {string} User-friendly message
*/
export function getStatusMessage(status) {
const messages = {
400: '🛡️ Genetic Code Invalid - the DNA sequence doesn\'t compile',
401: '🔐 Authentication Failed - the nervous system can\'t verify identity',
403: '🚫 Access Forbidden - immune system rejected this component',
404: '👻 Target Lost - sensory organs can\'t perceive the resource',
429: '⚡ Metabolism Overloaded - component sensing too quickly',
500: '🧠 Brain Error - critical neural processing failure',
502: '💀 Component Unresponsive - the heart has stopped beating',
503: '🏥 Component In Recovery - temporarily unable to metabolize requests',
400: 'Bad Request - invalid input data',
401: 'Authentication Failed - invalid credentials',
403: 'Access Forbidden - permission denied',
404: 'Not Found - resource doesn\'t exist',
429: 'Rate Limited - too many requests',
500: 'Server Error - internal processing failure',
502: 'Bad Gateway - server not responding',
503: 'Service Unavailable - temporarily unable to handle requests',
};
return messages[status] || `🔴 Unknown Component State - HTTP ${status}`;
return messages[status] || `Unknown Error - HTTP ${status}`;
}
export default {

View File

@@ -1,35 +1,28 @@
/**
* DSS Logger - Component Brain Consciousness System
* DSS Logger - Structured Logging System
*
* The DSS brain uses this logger to become conscious of what's happening.
* Log levels represent the component's level of awareness and concern.
* Provides structured logging with categories and levels for the DSS admin UI.
*
* Framework: DSS Component Framework
* See: docs/DSS_ORGANISM_GUIDE.md#brain
*
* Log Categories (Organ Systems):
* 'heart' - ❤️ Database operations and data persistence
* 'brain' - 🧠 Validation, analysis, and decision making
* 'nervous' - 🔌 API calls, webhooks, communication
* 'digestive' - 🍽️ Data ingestion, parsing, transformation
* 'circulatory' - 🩸 Design token flow and distribution
* 'metabolic' - ⚡ Style-dictionary transformations
* 'endocrine' - 🎛️ Theme system and configuration
* 'immune' - 🛡️ Validation, error detection, security
* 'sensory' - 👁️ Asset loading, Figma perception
* 'skin' - 🎨 UI rendering, Storybook output
* 'skeleton' - 🦴 Schema and structure validation
*
* Provides structured logging with biological awareness levels and optional remote logging.
* Log Categories:
* 'storage' - Database operations and data persistence
* 'validation' - Validation, analysis, and decision making
* 'api' - API calls, webhooks, communication
* 'parser' - Data ingestion, parsing, transformation
* 'tokens' - Design token flow and distribution
* 'transform' - Style-dictionary transformations
* 'config' - Theme system and configuration
* 'security' - Validation, error detection, security
* 'assets' - Asset loading, Figma integration
* 'ui' - UI rendering, Storybook output
* 'schema' - Schema and structure validation
*/
// Component awareness levels - how conscious is the system?
const LOG_LEVELS = {
DEBUG: 0, // 🧠 Deep thought - brain analyzing internal processes
INFO: 1, // 💭 Awareness - component knows what's happening
WARN: 2, // ⚠️ Symptom - component detected something unusual
ERROR: 3, // 🛡️ Immune alert - component detected a threat
NONE: 4 // 🌙 Sleep - component is silent
DEBUG: 0,
INFO: 1,
WARN: 2,
ERROR: 3,
NONE: 4
};
class Logger {
@@ -76,23 +69,22 @@ class Logger {
this.logs.shift();
}
// Console output with component awareness emojis
const levelEmojis = {
DEBUG: '🧠', // Brain thinking deeply
INFO: '💭', // Component aware
WARN: '⚠️', // Symptom detected
ERROR: '🛡️' // Immune alert - threat detected
const levelIcons = {
DEBUG: '[D]',
INFO: '[I]',
WARN: '[W]',
ERROR: '[E]'
};
const colors = {
DEBUG: 'color: #666; font-style: italic', // Gray, thoughtful
INFO: 'color: #2196F3; font-weight: bold', // Blue, informative
WARN: 'color: #FF9800; font-weight: bold', // Orange, warning
ERROR: 'color: #F44336; font-weight: bold' // Red, critical
DEBUG: 'color: #666; font-style: italic',
INFO: 'color: #2196F3; font-weight: bold',
WARN: 'color: #FF9800; font-weight: bold',
ERROR: 'color: #F44336; font-weight: bold'
};
const emoji = levelEmojis[level] || '🔘';
const prefix = `${emoji} [${category}]`;
const icon = levelIcons[level] || '[?]';
const prefix = `${icon} [${category}]`;
const style = colors[level] || '';
if (data) {
@@ -120,56 +112,49 @@ class Logger {
}
/**
* 🧠 DEBUG - Brain's deep thoughts
* Internal analysis and detailed consciousness
* DEBUG - Detailed internal information
*/
debug(category, message, data) {
this._log('DEBUG', category, message, data);
}
/**
* 💭 INFO - Component awareness
* The system knows what's happening, stays informed
* INFO - General information
*/
info(category, message, data) {
this._log('INFO', category, message, data);
}
/**
* ⚠️ WARN - Symptom detection
* Component detected something unusual but not critical
* WARN - Warning, something unusual detected
*/
warn(category, message, data) {
this._log('WARN', category, message, data);
}
/**
* 🛡️ ERROR - Immune alert
* Component detected a threat - critical consciousness
* ERROR - Error, something went wrong
*/
error(category, message, data) {
this._log('ERROR', category, message, data);
}
/**
* 📜 Get recent consciousness records
* Retrieve the component's recent thoughts and awareness
* Get recent log entries
*/
getRecentLogs(count = 50) {
return this.logs.slice(-count);
}
/**
* 🧠 Clear the mind
* Erase recent consciousness logs
* Clear all logs
*/
clear() {
this.logs = [];
}
/**
* 📤 Export consciousness
* Save the component's awareness to a file for analysis
* Export logs to file
*/
export() {
const dataStr = JSON.stringify(this.logs, null, 2);
@@ -185,13 +170,11 @@ class Logger {
}
/**
* 🧠 ORGANISM CONSCIOUSNESS
* Create the DSS component's brain - a singleton logger that tracks all awareness
* DSS Logger singleton
*/
const logger = new Logger('DSS', 'INFO');
// Set log level from localStorage or URL param
// Allow tuning the component's consciousness level (awareness sensitivity)
const urlParams = new URLSearchParams(window.location.search);
const logLevel = urlParams.get('log') || localStorage.getItem('dss_log_level') || 'INFO';
logger.setLevel(logLevel.toUpperCase());

View File

@@ -21,15 +21,15 @@ import os
from pathlib import Path
from dotenv import load_dotenv
# Get project root - tools/api/server.py -> tools/api -> tools -> project_root
# Get project root - apps/api/server.py -> apps/api -> apps -> project_root
_server_file = Path(__file__).resolve()
_project_root = _server_file.parent.parent.parent # /home/.../dss
# Try loading from multiple possible .env locations
env_paths = [
_project_root / "dss-mvp1" / ".env", # dss-mvp1/.env (primary)
_project_root / ".env", # root .env
_server_file.parent / ".env", # tools/api/.env
_project_root / ".env", # root .env (primary)
_project_root / "storybook" / ".env", # storybook/.env
_server_file.parent / ".env", # apps/api/.env
]
for env_path in env_paths:
if env_path.exists():
@@ -50,33 +50,70 @@ from pydantic import BaseModel
from typing import Optional
import sys
# Add tools directory to path (legacy imports)
sys.path.insert(0, str(Path(__file__).parent.parent))
# Add dss-mvp1 directory to path (consolidated dss package)
sys.path.insert(0, str(Path(__file__).parent.parent.parent / "dss-mvp1"))
# Add project root to path for dss package
sys.path.insert(0, str(Path(__file__).parent.parent.parent))
# Import browser logger router
from browser_logger import router as browser_log_router
# Import browser logger router (local import from same directory)
from apps.api.browser_logger import router as browser_log_router
# Legacy imports (will gradually migrate these)
from config import config
from storage.json_store import (
# DSS package imports - unified package
from dss import settings
from dss.storage.json_store import (
Projects, Components, SyncHistory, ActivityLog, Teams, Cache, get_stats,
FigmaFiles, CodeMetrics, TestResults, TokenDrift, Tokens, Styles,
Integrations, IntegrationHealth
)
from figma.figma_tools import FigmaToolSuite
from dss.figma.figma_tools import FigmaToolSuite
from dss.services.project_manager import ProjectManager
from dss.services.config_service import ConfigService, DSSConfig
from dss.services.sandboxed_fs import SandboxedFS
# New consolidated dss imports - now available!
# from dss import DesignToken, TokenSource, ProjectScanner, etc.
# Additional DSS imports available:
# from dss import DesignToken, TokenSource, ProjectScanner
# from dss.ingest import CSSTokenSource, SCSSTokenSource, TailwindTokenSource
# from dss.analyze import ReactAnalyzer, StyleAnalyzer, QuickWinFinder
# from dss.storybook import StorybookScanner, StoryGenerator
# MVP1 Configuration Architecture - Services
from services.project_manager import ProjectManager
from services.config_service import ConfigService, DSSConfig
from services.sandboxed_fs import SandboxedFS
# === Legacy Config Compatibility ===
# Wrapper to maintain compatibility with old config.x.y references
class _FigmaConfigCompat:
@property
def is_configured(self):
return settings.figma_configured
@property
def token(self):
return settings.FIGMA_TOKEN
@property
def cache_ttl(self):
return settings.FIGMA_CACHE_TTL
class _ServerConfigCompat:
@property
def env(self):
return settings.SERVER_ENV
@property
def port(self):
return settings.SERVER_PORT
@property
def host(self):
return settings.SERVER_HOST
@property
def is_production(self):
return settings.is_production
class _ConfigCompat:
figma = _FigmaConfigCompat()
server = _ServerConfigCompat()
def summary(self):
return {
"figma": {"configured": settings.figma_configured, "cache_ttl": settings.FIGMA_CACHE_TTL},
"server": {"port": settings.SERVER_PORT, "env": settings.SERVER_ENV, "log_level": settings.LOG_LEVEL},
"database": {"path": str(settings.DATABASE_PATH)},
}
config = _ConfigCompat()
# === Runtime Configuration ===
@@ -316,7 +353,7 @@ class TokenDriftCreate(BaseModel):
# === Authentication ===
from auth.atlassian_auth import get_auth
from dss.auth.atlassian_auth import get_auth
async def get_current_user(authorization: Optional[str] = Header(None)) -> Dict[str, Any]:
"""
@@ -384,71 +421,62 @@ async def root():
@app.get("/health")
async def health():
"""
🏥 ORGANISM VITAL SIGNS CHECK
Health check endpoint.
Performs a complete health diagnostic on the DSS component.
Returns 200 OK with vital signs if component is healthy.
Performs a complete health diagnostic on the DSS server.
Returns 200 OK with service status.
Vital Signs Checked:
- Heart (Database) - Is the source of truth responsive?
- 🧠 Brain (MCP Handler) - Is the decision-making system online?
- 👁 Sensory (Figma) - Are the external perception organs configured?
Services Checked:
- Storage - Is the data directory accessible?
- MCP Handler - Is the MCP handler initialized?
- Figma - Is the Figma integration configured?
"""
import os
import psutil
from pathlib import Path
# ❤️ Check Heart (storage) connectivity
db_ok = False
# Check storage connectivity
storage_ok = False
try:
from storage.json_store import DATA_DIR
db_ok = DATA_DIR.exists()
from dss.storage.json_store import DATA_DIR
storage_ok = DATA_DIR.exists()
except Exception as e:
import traceback
error_trace = traceback.format_exc()
print(f"🏥 VITAL SIGN: Heart (storage) error: {type(e).__name__}: {e}", flush=True)
print(f" Traceback:\n{error_trace}", flush=True)
pass
print(f"[Health] Storage check error: {type(e).__name__}: {e}", flush=True)
# 🧠 Check Brain (MCP handler) functionality
# Check MCP handler functionality
mcp_ok = False
try:
import sys
from pathlib import Path
# Add project root to path (two levels up from tools/api)
project_root = Path(__file__).parent.parent.parent
if str(project_root) not in sys.path:
sys.path.insert(0, str(project_root))
from tools.dss_mcp.handler import get_mcp_handler
from dss.mcp.handler import get_mcp_handler
handler = get_mcp_handler()
mcp_ok = handler is not None
except Exception as e:
import traceback
error_trace = ''.join(traceback.format_exception(type(e), e, e.__traceback__))
print(f"🧠 BRAIN CHECK: MCP handler error: {type(e).__name__}: {e}", flush=True)
print(f" Traceback:\n{error_trace}", flush=True)
print(f"[Health] MCP handler check error: {type(e).__name__}: {e}", flush=True)
# Get uptime (how long component has been conscious)
# Get uptime
try:
process = psutil.Process(os.getpid())
uptime_seconds = int((datetime.now() - datetime.fromtimestamp(process.create_time())).total_seconds())
except:
uptime_seconds = 0
# Overall vitality assessment
status = "healthy" if (db_ok and mcp_ok) else "degraded"
# Overall status
status = "healthy" if (storage_ok and mcp_ok) else "degraded"
return {
"status": status,
"vital_signs": {
"overall": "🟢 All systems nominal" if status == "healthy" else "🟡 System degradation detected",
"consciousness_duration_seconds": uptime_seconds
},
"uptime_seconds": uptime_seconds,
"version": "0.8.0",
"timestamp": datetime.utcnow().isoformat() + "Z",
"services": {
"storage": "ok" if db_ok else "error",
"storage": "ok" if storage_ok else "error",
"mcp": "ok" if mcp_ok else "error",
"figma": "connected" if config.figma.is_configured else "not configured"
}

624
apps/cli/package-lock.json generated Normal file
View File

@@ -0,0 +1,624 @@
{
"name": "@overbits/dss",
"version": "0.1.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@overbits/dss",
"version": "0.1.0",
"license": "MIT",
"dependencies": {
"chalk": "^5.3.0",
"commander": "^12.0.0",
"conf": "^12.0.0",
"node-fetch": "^3.3.2",
"ora": "^8.0.1"
},
"bin": {
"dss": "dist/cli.js"
},
"devDependencies": {
"@types/node": "^20.10.0",
"typescript": "^5.3.0"
},
"engines": {
"node": ">=18.0.0"
}
},
"node_modules/@types/node": {
"version": "20.19.25",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.25.tgz",
"integrity": "sha512-ZsJzA5thDQMSQO788d7IocwwQbI8B5OPzmqNvpf3NY/+MHDAS759Wo0gd2WQeXYt5AAAQjzcrTVC6SKCuYgoCQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"undici-types": "~6.21.0"
}
},
"node_modules/ajv": {
"version": "8.17.1",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
"license": "MIT",
"dependencies": {
"fast-deep-equal": "^3.1.3",
"fast-uri": "^3.0.1",
"json-schema-traverse": "^1.0.0",
"require-from-string": "^2.0.2"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/epoberezkin"
}
},
"node_modules/ajv-formats": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz",
"integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==",
"license": "MIT",
"dependencies": {
"ajv": "^8.0.0"
},
"peerDependencies": {
"ajv": "^8.0.0"
},
"peerDependenciesMeta": {
"ajv": {
"optional": true
}
}
},
"node_modules/ansi-regex": {
"version": "6.2.2",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
"integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
"license": "MIT",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/chalk/ansi-regex?sponsor=1"
}
},
"node_modules/atomically": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/atomically/-/atomically-2.1.0.tgz",
"integrity": "sha512-+gDffFXRW6sl/HCwbta7zK4uNqbPjv4YJEAdz7Vu+FLQHe77eZ4bvbJGi4hE0QPeJlMYMA3piXEr1UL3dAwx7Q==",
"license": "MIT",
"dependencies": {
"stubborn-fs": "^2.0.0",
"when-exit": "^2.1.4"
}
},
"node_modules/chalk": {
"version": "5.6.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz",
"integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==",
"license": "MIT",
"engines": {
"node": "^12.17.0 || ^14.13 || >=16.0.0"
},
"funding": {
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
"node_modules/cli-cursor": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz",
"integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==",
"license": "MIT",
"dependencies": {
"restore-cursor": "^5.0.0"
},
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/cli-spinners": {
"version": "2.9.2",
"resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz",
"integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==",
"license": "MIT",
"engines": {
"node": ">=6"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/commander": {
"version": "12.1.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz",
"integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==",
"license": "MIT",
"engines": {
"node": ">=18"
}
},
"node_modules/conf": {
"version": "12.0.0",
"resolved": "https://registry.npmjs.org/conf/-/conf-12.0.0.tgz",
"integrity": "sha512-fIWyWUXrJ45cHCIQX+Ck1hrZDIf/9DR0P0Zewn3uNht28hbt5OfGUq8rRWsxi96pZWPyBEd0eY9ama01JTaknA==",
"license": "MIT",
"dependencies": {
"ajv": "^8.12.0",
"ajv-formats": "^2.1.1",
"atomically": "^2.0.2",
"debounce-fn": "^5.1.2",
"dot-prop": "^8.0.2",
"env-paths": "^3.0.0",
"json-schema-typed": "^8.0.1",
"semver": "^7.5.4",
"uint8array-extras": "^0.3.0"
},
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/data-uri-to-buffer": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz",
"integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==",
"license": "MIT",
"engines": {
"node": ">= 12"
}
},
"node_modules/debounce-fn": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/debounce-fn/-/debounce-fn-5.1.2.tgz",
"integrity": "sha512-Sr4SdOZ4vw6eQDvPYNxHogvrxmCIld/VenC5JbNrFwMiwd7lY/Z18ZFfo+EWNG4DD9nFlAujWAo/wGuOPHmy5A==",
"license": "MIT",
"dependencies": {
"mimic-fn": "^4.0.0"
},
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/dot-prop": {
"version": "8.0.2",
"resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-8.0.2.tgz",
"integrity": "sha512-xaBe6ZT4DHPkg0k4Ytbvn5xoxgpG0jOS1dYxSOwAHPuNLjP3/OzN0gH55SrLqpx8cBfSaVt91lXYkApjb+nYdQ==",
"license": "MIT",
"dependencies": {
"type-fest": "^3.8.0"
},
"engines": {
"node": ">=16"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/emoji-regex": {
"version": "10.6.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz",
"integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==",
"license": "MIT"
},
"node_modules/env-paths": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/env-paths/-/env-paths-3.0.0.tgz",
"integrity": "sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A==",
"license": "MIT",
"engines": {
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
"license": "MIT"
},
"node_modules/fast-uri": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz",
"integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/fastify"
},
{
"type": "opencollective",
"url": "https://opencollective.com/fastify"
}
],
"license": "BSD-3-Clause"
},
"node_modules/fetch-blob": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz",
"integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/jimmywarting"
},
{
"type": "paypal",
"url": "https://paypal.me/jimmywarting"
}
],
"license": "MIT",
"dependencies": {
"node-domexception": "^1.0.0",
"web-streams-polyfill": "^3.0.3"
},
"engines": {
"node": "^12.20 || >= 14.13"
}
},
"node_modules/formdata-polyfill": {
"version": "4.0.10",
"resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz",
"integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==",
"license": "MIT",
"dependencies": {
"fetch-blob": "^3.1.2"
},
"engines": {
"node": ">=12.20.0"
}
},
"node_modules/get-east-asian-width": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.4.0.tgz",
"integrity": "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==",
"license": "MIT",
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/is-interactive": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz",
"integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==",
"license": "MIT",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/is-unicode-supported": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz",
"integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==",
"license": "MIT",
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/json-schema-traverse": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
"license": "MIT"
},
"node_modules/json-schema-typed": {
"version": "8.0.2",
"resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-8.0.2.tgz",
"integrity": "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==",
"license": "BSD-2-Clause"
},
"node_modules/log-symbols": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-6.0.0.tgz",
"integrity": "sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==",
"license": "MIT",
"dependencies": {
"chalk": "^5.3.0",
"is-unicode-supported": "^1.3.0"
},
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/log-symbols/node_modules/is-unicode-supported": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz",
"integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==",
"license": "MIT",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/mimic-fn": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz",
"integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==",
"license": "MIT",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/mimic-function": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz",
"integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==",
"license": "MIT",
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/node-domexception": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
"integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==",
"deprecated": "Use your platform's native DOMException instead",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/jimmywarting"
},
{
"type": "github",
"url": "https://paypal.me/jimmywarting"
}
],
"license": "MIT",
"engines": {
"node": ">=10.5.0"
}
},
"node_modules/node-fetch": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz",
"integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==",
"license": "MIT",
"dependencies": {
"data-uri-to-buffer": "^4.0.0",
"fetch-blob": "^3.1.4",
"formdata-polyfill": "^4.0.10"
},
"engines": {
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/node-fetch"
}
},
"node_modules/onetime": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz",
"integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==",
"license": "MIT",
"dependencies": {
"mimic-function": "^5.0.0"
},
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/ora": {
"version": "8.2.0",
"resolved": "https://registry.npmjs.org/ora/-/ora-8.2.0.tgz",
"integrity": "sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==",
"license": "MIT",
"dependencies": {
"chalk": "^5.3.0",
"cli-cursor": "^5.0.0",
"cli-spinners": "^2.9.2",
"is-interactive": "^2.0.0",
"is-unicode-supported": "^2.0.0",
"log-symbols": "^6.0.0",
"stdin-discarder": "^0.2.2",
"string-width": "^7.2.0",
"strip-ansi": "^7.1.0"
},
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/require-from-string": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
"integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/restore-cursor": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz",
"integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==",
"license": "MIT",
"dependencies": {
"onetime": "^7.0.0",
"signal-exit": "^4.1.0"
},
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/semver": {
"version": "7.7.3",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
"integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
"license": "ISC",
"bin": {
"semver": "bin/semver.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/signal-exit": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
"integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
"license": "ISC",
"engines": {
"node": ">=14"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/stdin-discarder": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz",
"integrity": "sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==",
"license": "MIT",
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/string-width": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
"integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==",
"license": "MIT",
"dependencies": {
"emoji-regex": "^10.3.0",
"get-east-asian-width": "^1.0.0",
"strip-ansi": "^7.1.0"
},
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/strip-ansi": {
"version": "7.1.2",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz",
"integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==",
"license": "MIT",
"dependencies": {
"ansi-regex": "^6.0.1"
},
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/chalk/strip-ansi?sponsor=1"
}
},
"node_modules/stubborn-fs": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/stubborn-fs/-/stubborn-fs-2.0.0.tgz",
"integrity": "sha512-Y0AvSwDw8y+nlSNFXMm2g6L51rBGdAQT20J3YSOqxC53Lo3bjWRtr2BKcfYoAf352WYpsZSTURrA0tqhfgudPA==",
"license": "MIT",
"dependencies": {
"stubborn-utils": "^1.0.1"
}
},
"node_modules/stubborn-utils": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/stubborn-utils/-/stubborn-utils-1.0.2.tgz",
"integrity": "sha512-zOh9jPYI+xrNOyisSelgym4tolKTJCQd5GBhK0+0xJvcYDcwlOoxF/rnFKQ2KRZknXSG9jWAp66fwP6AxN9STg==",
"license": "MIT"
},
"node_modules/type-fest": {
"version": "3.13.1",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-3.13.1.tgz",
"integrity": "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==",
"license": "(MIT OR CC0-1.0)",
"engines": {
"node": ">=14.16"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/typescript": {
"version": "5.9.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"dev": true,
"license": "Apache-2.0",
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=14.17"
}
},
"node_modules/uint8array-extras": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/uint8array-extras/-/uint8array-extras-0.3.0.tgz",
"integrity": "sha512-erJsJwQ0tKdwuqI0359U8ijkFmfiTcq25JvvzRVc1VP+2son1NJRXhxcAKJmAW3ajM8JSGAfsAXye8g4s+znxA==",
"license": "MIT",
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/undici-types": {
"version": "6.21.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
"dev": true,
"license": "MIT"
},
"node_modules/web-streams-polyfill": {
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz",
"integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==",
"license": "MIT",
"engines": {
"node": ">= 8"
}
},
"node_modules/when-exit": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/when-exit/-/when-exit-2.1.5.tgz",
"integrity": "sha512-VGkKJ564kzt6Ms1dbgPP/yuIoQCrsFAnRbptpC5wOEsDaNsbCB2bnfnaA8i/vRs5tjUSEOtIuvl9/MyVsvQZCg==",
"license": "MIT"
}
}
}

51
apps/cli/package.json Normal file
View File

@@ -0,0 +1,51 @@
{
"name": "@overbits/dss",
"version": "0.1.0",
"description": "Design System Server - UI Developer Companion",
"type": "module",
"main": "dist/index.js",
"bin": {
"dss": "dist/cli.js"
},
"scripts": {
"build": "tsc",
"dev": "tsc --watch",
"start": "node dist/cli.js",
"postinstall": "node scripts/postinstall.js",
"prepublishOnly": "npm run build"
},
"keywords": [
"design-system",
"figma",
"tokens",
"components",
"ui",
"developer-tools"
],
"author": "Overbits",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/overbits/dss"
},
"engines": {
"node": ">=18.0.0"
},
"dependencies": {
"commander": "^12.0.0",
"chalk": "^5.3.0",
"ora": "^8.0.1",
"conf": "^12.0.0",
"node-fetch": "^3.3.2"
},
"devDependencies": {
"@types/node": "^20.10.0",
"typescript": "^5.3.0"
},
"files": [
"dist",
"python/api",
"scripts",
"README.md"
]
}

View File

@@ -3,5 +3,3 @@ uvicorn[standard]>=0.23.0
httpx>=0.24.0
python-dotenv>=1.0.0
pydantic>=2.0.0
mcp>=1.0.0
google-generativeai>=0.3.0

View File

@@ -0,0 +1,724 @@
"""
Design System Server (DSS) - FastAPI Server
Portable API server providing:
- Project management (CRUD)
- Figma integration endpoints
- Discovery & health endpoints
- Activity tracking
- Runtime configuration management
- Service discovery (Storybook, etc.)
Modes:
- Server: Deployed remotely, serves design systems to teams
- Local: Dev companion, UI advisor, local services
Uses SQLite for persistence, integrates with Figma tools.
"""
import asyncio
import subprocess
import json
import os
from pathlib import Path
from typing import Optional, List, Dict, Any
from datetime import datetime
from fastapi import FastAPI, HTTPException, Query, BackgroundTasks
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse
from fastapi.staticfiles import StaticFiles
from pydantic import BaseModel
import sys
sys.path.insert(0, str(Path(__file__).parent.parent))
from config import config
from storage.json_store import (
Projects, Components, SyncHistory, ActivityLog, Teams, Cache, get_stats
)
from figma.figma_tools import FigmaToolSuite
# === Runtime Configuration ===
class RuntimeConfig:
"""
Runtime configuration that can be modified from the dashboard.
Persists to .dss/runtime-config.json for portability.
"""
def __init__(self):
self.config_path = Path(__file__).parent.parent.parent / ".dss" / "runtime-config.json"
self.config_path.parent.mkdir(parents=True, exist_ok=True)
self._data = self._load()
def _load(self) -> dict:
if self.config_path.exists():
try:
return json.loads(self.config_path.read_text())
except:
pass
return {
"mode": "local", # "local" or "server"
"figma": {"token": "", "configured": False},
"services": {
"storybook": {"enabled": False, "port": 6006, "url": ""},
"chromatic": {"enabled": False, "project_token": ""},
"github": {"enabled": False, "repo": ""},
},
"features": {
"visual_qa": True,
"token_sync": True,
"code_gen": True,
"ai_advisor": False,
}
}
def _save(self):
self.config_path.write_text(json.dumps(self._data, indent=2))
def get(self, key: str = None):
if key is None:
# Return safe copy without secrets
safe = self._data.copy()
if safe.get("figma", {}).get("token"):
safe["figma"]["token"] = "***configured***"
return safe
return self._data.get(key)
def set(self, key: str, value: Any):
self._data[key] = value
self._save()
return self._data[key]
def update(self, updates: dict):
for key, value in updates.items():
if isinstance(value, dict) and isinstance(self._data.get(key), dict):
self._data[key].update(value)
else:
self._data[key] = value
self._save()
return self.get()
def set_figma_token(self, token: str):
self._data["figma"]["token"] = token
self._data["figma"]["configured"] = bool(token)
self._save()
# Also update the global config
os.environ["FIGMA_TOKEN"] = token
return {"configured": bool(token)}
runtime_config = RuntimeConfig()
# === Service Discovery ===
class ServiceDiscovery:
"""Discovers and manages companion services."""
KNOWN_SERVICES = {
"storybook": {"ports": [6006, 6007], "health": "/"},
"chromatic": {"ports": [], "health": None},
"vite": {"ports": [5173, 5174, 3000], "health": "/"},
"webpack": {"ports": [8080, 8081], "health": "/"},
"nextjs": {"ports": [3000, 3001], "health": "/"},
}
@classmethod
async def discover(cls) -> dict:
"""Discover running services by checking known ports."""
import socket
discovered = {}
for service, info in cls.KNOWN_SERVICES.items():
for port in info["ports"]:
try:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(0.5)
result = sock.connect_ex(('127.0.0.1', port))
sock.close()
if result == 0:
discovered[service] = {
"running": True,
"port": port,
"url": f"http://localhost:{port}"
}
break
except:
pass
if service not in discovered:
discovered[service] = {"running": False, "port": None, "url": None}
return discovered
@classmethod
async def check_storybook(cls) -> dict:
"""Check Storybook status specifically."""
import httpx
configured = runtime_config.get("services").get("storybook", {})
port = configured.get("port", 6006)
url = configured.get("url") or f"http://localhost:{port}"
try:
async with httpx.AsyncClient(timeout=2.0) as client:
resp = await client.get(url)
return {
"running": resp.status_code == 200,
"url": url,
"port": port
}
except:
return {"running": False, "url": url, "port": port}
# === App Setup ===
app = FastAPI(
title="Design System Server (DSS)",
description="API for design system management and Figma integration",
version="1.0.0"
)
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# Mount Admin UI static files
UI_DIR = Path(__file__).parent.parent.parent / "admin-ui"
if UI_DIR.exists():
app.mount("/admin-ui", StaticFiles(directory=str(UI_DIR), html=True), name="admin-ui")
# Initialize Figma tools
figma_suite = FigmaToolSuite(output_dir=str(Path(__file__).parent.parent.parent / ".dss" / "output"))
# === Request/Response Models ===
class ProjectCreate(BaseModel):
name: str
description: str = ""
figma_file_key: str = ""
class ProjectUpdate(BaseModel):
name: Optional[str] = None
description: Optional[str] = None
figma_file_key: Optional[str] = None
status: Optional[str] = None
class FigmaExtractRequest(BaseModel):
file_key: str
format: str = "css"
class FigmaSyncRequest(BaseModel):
file_key: str
target_path: str
format: str = "css"
class TeamCreate(BaseModel):
name: str
description: str = ""
# === Root & Health ===
@app.get("/")
async def root():
"""Redirect to Admin UI dashboard."""
from fastapi.responses import RedirectResponse
return RedirectResponse(url="/admin-ui/index.html")
@app.get("/health")
async def health():
"""Health check endpoint."""
return {
"status": "ok",
"name": "dss-api",
"version": "1.0.0",
"timestamp": datetime.utcnow().isoformat() + "Z",
"figma_mode": figma_suite.mode,
"config": config.summary()
}
@app.get("/api/stats")
async def get_statistics():
"""Get database and system statistics."""
db_stats = get_stats()
return {
"database": db_stats,
"figma": {
"mode": figma_suite.mode,
"configured": config.figma.is_configured
}
}
# === Projects ===
@app.get("/api/projects")
async def list_projects(status: Optional[str] = None):
"""List all projects."""
projects = Projects.list(status=status)
return projects
@app.get("/api/projects/{project_id}")
async def get_project(project_id: str):
"""Get a specific project."""
project = Projects.get(project_id)
if not project:
raise HTTPException(status_code=404, detail="Project not found")
return project
@app.post("/api/projects")
async def create_project(project: ProjectCreate):
"""Create a new project."""
project_id = f"proj-{int(datetime.utcnow().timestamp() * 1000)}"
created = Projects.create(
id=project_id,
name=project.name,
description=project.description,
figma_file_key=project.figma_file_key
)
ActivityLog.log(
action="project_created",
entity_type="project",
entity_id=project_id,
project_id=project_id,
details={"name": project.name}
)
return created
@app.put("/api/projects/{project_id}")
async def update_project(project_id: str, update: ProjectUpdate):
"""Update a project."""
existing = Projects.get(project_id)
if not existing:
raise HTTPException(status_code=404, detail="Project not found")
update_data = {k: v for k, v in update.dict().items() if v is not None}
if not update_data:
return existing
updated = Projects.update(project_id, **update_data)
ActivityLog.log(
action="project_updated",
entity_type="project",
entity_id=project_id,
project_id=project_id,
details=update_data
)
return updated
@app.delete("/api/projects/{project_id}")
async def delete_project(project_id: str):
"""Delete a project."""
if not Projects.delete(project_id):
raise HTTPException(status_code=404, detail="Project not found")
ActivityLog.log(
action="project_deleted",
entity_type="project",
entity_id=project_id
)
return {"success": True}
# === Components ===
@app.get("/api/projects/{project_id}/components")
async def list_components(project_id: str):
"""List components for a project."""
if not Projects.get(project_id):
raise HTTPException(status_code=404, detail="Project not found")
return Components.list(project_id)
# === Figma Integration ===
@app.post("/api/figma/extract-variables")
async def extract_variables(request: FigmaExtractRequest, background_tasks: BackgroundTasks):
"""Extract design tokens from Figma file."""
try:
result = await figma_suite.extract_variables(request.file_key, request.format)
ActivityLog.log(
action="figma_extract_variables",
entity_type="figma",
details={"file_key": request.file_key, "format": request.format, "count": result.get("tokens_count")}
)
return result
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@app.post("/api/figma/extract-components")
async def extract_components(request: FigmaExtractRequest):
"""Extract components from Figma file."""
try:
result = await figma_suite.extract_components(request.file_key)
ActivityLog.log(
action="figma_extract_components",
entity_type="figma",
details={"file_key": request.file_key, "count": result.get("components_count")}
)
return result
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@app.post("/api/figma/extract-styles")
async def extract_styles(request: FigmaExtractRequest):
"""Extract styles from Figma file."""
try:
result = await figma_suite.extract_styles(request.file_key)
return result
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@app.post("/api/figma/sync-tokens")
async def sync_tokens(request: FigmaSyncRequest):
"""Sync tokens from Figma to target path."""
try:
result = await figma_suite.sync_tokens(request.file_key, request.target_path, request.format)
ActivityLog.log(
action="figma_sync_tokens",
entity_type="figma",
details={"file_key": request.file_key, "target": request.target_path, "synced": result.get("tokens_synced")}
)
return result
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@app.post("/api/figma/validate")
async def validate_components(request: FigmaExtractRequest):
"""Validate components against design system rules."""
try:
result = await figma_suite.validate_components(request.file_key)
return result
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@app.post("/api/figma/generate-code")
async def generate_code(file_key: str, component_name: str, framework: str = "webcomponent"):
"""Generate component code from Figma."""
try:
result = await figma_suite.generate_code(file_key, component_name, framework)
return result
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
# === Discovery ===
@app.get("/api/discovery")
async def run_discovery(path: str = "."):
"""Run project discovery."""
script_path = Path(__file__).parent.parent / "discovery" / "discover.sh"
try:
result = subprocess.run(
[str(script_path), path],
capture_output=True,
text=True,
timeout=30
)
if result.returncode == 0:
return json.loads(result.stdout)
else:
return {"error": result.stderr}
except subprocess.TimeoutExpired:
raise HTTPException(status_code=504, detail="Discovery timed out")
except json.JSONDecodeError:
return {"raw_output": result.stdout}
@app.get("/api/discovery/ports")
async def discover_ports():
"""Discover listening ports and services."""
script_path = Path(__file__).parent.parent / "discovery" / "discover-ports.sh"
try:
result = subprocess.run(
[str(script_path)],
capture_output=True,
text=True,
timeout=10
)
return json.loads(result.stdout)
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@app.get("/api/discovery/env")
async def discover_env(path: str = "."):
"""Analyze environment configuration."""
script_path = Path(__file__).parent.parent / "discovery" / "discover-env.sh"
try:
result = subprocess.run(
[str(script_path), path],
capture_output=True,
text=True,
timeout=10
)
return json.loads(result.stdout)
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
# === Activity & Sync History ===
@app.get("/api/activity")
async def get_activity(limit: int = Query(default=50, le=100)):
"""Get recent activity log."""
return ActivityLog.recent(limit=limit)
@app.get("/api/sync-history")
async def get_sync_history(project_id: Optional[str] = None, limit: int = Query(default=20, le=100)):
"""Get sync history."""
return SyncHistory.recent(project_id=project_id, limit=limit)
# === Teams ===
@app.get("/api/teams")
async def list_teams():
"""List all teams."""
return Teams.list()
@app.post("/api/teams")
async def create_team(team: TeamCreate):
"""Create a new team."""
team_id = f"team-{int(datetime.utcnow().timestamp() * 1000)}"
created = Teams.create(team_id, team.name, team.description)
return created
@app.get("/api/teams/{team_id}")
async def get_team(team_id: str):
"""Get a specific team."""
team = Teams.get(team_id)
if not team:
raise HTTPException(status_code=404, detail="Team not found")
return team
# === Cache Management ===
@app.post("/api/cache/clear")
async def clear_cache():
"""Clear expired cache entries."""
count = Cache.clear_expired()
return {"cleared": count}
@app.delete("/api/cache")
async def purge_cache():
"""Purge all cache entries."""
Cache.clear_all()
return {"success": True}
# === Configuration Management ===
class ConfigUpdate(BaseModel):
mode: Optional[str] = None
figma_token: Optional[str] = None
services: Optional[Dict[str, Any]] = None
features: Optional[Dict[str, bool]] = None
@app.get("/api/config")
async def get_config():
"""Get current runtime configuration (secrets masked)."""
return {
"config": runtime_config.get(),
"env": config.summary(),
"mode": runtime_config.get("mode")
}
@app.put("/api/config")
async def update_config(update: ConfigUpdate):
"""Update runtime configuration."""
updates = {}
if update.mode:
updates["mode"] = update.mode
if update.figma_token is not None:
runtime_config.set_figma_token(update.figma_token)
# Reinitialize Figma tools with new token
global figma_suite
figma_suite = FigmaToolSuite(output_dir=str(Path(__file__).parent.parent.parent / ".dss" / "output"))
ActivityLog.log(
action="figma_token_updated",
entity_type="config",
details={"configured": bool(update.figma_token)}
)
if update.services:
updates["services"] = update.services
if update.features:
updates["features"] = update.features
if updates:
runtime_config.update(updates)
ActivityLog.log(
action="config_updated",
entity_type="config",
details={"keys": list(updates.keys())}
)
return runtime_config.get()
@app.get("/api/config/figma")
async def get_figma_config():
"""Get Figma configuration status."""
figma_cfg = runtime_config.get("figma")
return {
"configured": figma_cfg.get("configured", False),
"mode": figma_suite.mode,
"features": {
"extract_variables": True,
"extract_components": True,
"extract_styles": True,
"sync_tokens": True,
"validate": True,
"generate_code": True,
}
}
@app.post("/api/config/figma/test")
async def test_figma_connection():
"""Test Figma API connection."""
try:
# Try to make a simple API call
if not runtime_config.get("figma").get("configured"):
return {"success": False, "error": "Figma token not configured"}
# Test with a minimal API call
import httpx
token = runtime_config._data["figma"]["token"]
async with httpx.AsyncClient() as client:
resp = await client.get(
"https://api.figma.com/v1/me",
headers={"X-Figma-Token": token}
)
if resp.status_code == 200:
user = resp.json()
return {
"success": True,
"user": user.get("email", "connected"),
"handle": user.get("handle")
}
else:
return {"success": False, "error": f"API returned {resp.status_code}"}
except Exception as e:
return {"success": False, "error": str(e)}
# === Service Discovery ===
@app.get("/api/services")
async def list_services():
"""List configured and discovered services."""
configured = runtime_config.get("services")
discovered = await ServiceDiscovery.discover()
return {
"configured": configured,
"discovered": discovered,
"storybook": await ServiceDiscovery.check_storybook()
}
@app.put("/api/services/{service_name}")
async def configure_service(service_name: str, config_data: Dict[str, Any]):
"""Configure a service."""
services = runtime_config.get("services") or {}
services[service_name] = {**services.get(service_name, {}), **config_data}
runtime_config.set("services", services)
ActivityLog.log(
action="service_configured",
entity_type="service",
entity_id=service_name,
details={"keys": list(config_data.keys())}
)
return services[service_name]
@app.get("/api/services/storybook")
async def get_storybook_status():
"""Get Storybook service status."""
return await ServiceDiscovery.check_storybook()
# === DSS Mode ===
@app.get("/api/mode")
async def get_mode():
"""Get current DSS mode."""
mode = runtime_config.get("mode")
return {
"mode": mode,
"description": "Local dev companion" if mode == "local" else "Remote design system server",
"features": runtime_config.get("features")
}
@app.put("/api/mode")
async def set_mode(mode: str):
"""Set DSS mode (local or server)."""
if mode not in ["local", "server"]:
raise HTTPException(status_code=400, detail="Mode must be 'local' or 'server'")
runtime_config.set("mode", mode)
ActivityLog.log(
action="mode_changed",
entity_type="config",
details={"mode": mode}
)
return {"mode": mode, "success": True}
# === Run Server ===
# === Static Files (Admin UI) ===
# Mount at the end so API routes take precedence
# This enables portable mode: ./dss start serves everything on one port
UI_DIR = Path(__file__).parent.parent.parent / "admin-ui"
if UI_DIR.exists():
app.mount("/", StaticFiles(directory=str(UI_DIR), html=True), name="ui")
if __name__ == "__main__":
import uvicorn
port = int(os.getenv("PORT", "3456"))
host = os.getenv("HOST", "0.0.0.0")
url = f"http://{host}:{port}"
print(f"""
╔═══════════════════════════════════════════════════════════════╗
║ Design System Server (DSS) - Portable Server ║
╠═══════════════════════════════════════════════════════════════╣
║ Dashboard: {url + '/':^47}
║ API: {url + '/api':^47}
║ Docs: {url + '/docs':^47}
║ Environment: {config.server.env:^47}
║ Figma Mode: {figma_suite.mode:^47}
╚═══════════════════════════════════════════════════════════════╝
""")
uvicorn.run(
"server:app",
host=host,
port=port,
reload=config.server.env == "development"
)

View File

@@ -0,0 +1,74 @@
#!/usr/bin/env node
/**
* DSS Postinstall Script
*
* Sets up Python virtual environment and installs dependencies
* after npm install.
*/
import { spawn, execSync } from 'child_process';
import { existsSync, mkdirSync } from 'fs';
import { join, dirname } from 'path';
import { fileURLToPath } from 'url';
const __dirname = dirname(fileURLToPath(import.meta.url));
const pythonDir = join(__dirname, '..', 'python');
const venvDir = join(pythonDir, 'venv');
const apiDir = join(pythonDir, 'api');
console.log('[DSS] Setting up Python environment...');
// Check if Python 3 is available
function getPythonCmd() {
const commands = ['python3', 'python'];
for (const cmd of commands) {
try {
execSync(`${cmd} --version`, { stdio: 'ignore' });
return cmd;
} catch {
continue;
}
}
return null;
}
const python = getPythonCmd();
if (!python) {
console.error('[DSS] Error: Python 3 is required but not found.');
console.error('[DSS] Please install Python 3.8+ and try again.');
process.exit(1);
}
console.log(`[DSS] Using ${python}`);
// Create virtual environment if needed
if (!existsSync(venvDir)) {
console.log('[DSS] Creating virtual environment...');
try {
execSync(`${python} -m venv ${venvDir}`, { stdio: 'inherit' });
} catch (error) {
console.error('[DSS] Failed to create virtual environment');
console.error('[DSS] Try: pip install virtualenv');
process.exit(1);
}
}
// Install requirements
const pip = join(venvDir, 'bin', 'pip');
const requirements = join(apiDir, 'requirements.txt');
if (existsSync(requirements)) {
console.log('[DSS] Installing Python dependencies...');
try {
execSync(`${pip} install -q -r ${requirements}`, { stdio: 'inherit' });
console.log('[DSS] Python environment ready!');
} catch (error) {
console.error('[DSS] Failed to install Python dependencies');
process.exit(1);
}
} else {
console.log('[DSS] No requirements.txt found, skipping dependency install');
}
console.log('[DSS] Setup complete!');

40
apps/cli/scripts/publish.sh Executable file
View File

@@ -0,0 +1,40 @@
#!/bin/bash
# DSS npm publish script
set -e
echo "🚀 Publishing @overbits/dss to npm..."
# Ensure we're in the cli directory
cd "$(dirname "$0")/.."
# Build
echo "📦 Building..."
npm run build
# Check if logged in to npm
if ! npm whoami &> /dev/null; then
echo "❌ Not logged in to npm. Run: npm login"
exit 1
fi
# Verify package
echo "📋 Package contents:"
npm pack --dry-run
# Confirm
read -p "Publish to npm? (y/N) " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
echo "Cancelled."
exit 0
fi
# Publish
npm publish --access public
echo "✅ Published @overbits/dss!"
echo ""
echo "Users can now install with:"
echo " npm install -g @overbits/dss"
echo " npx @overbits/dss"

101
apps/cli/src/cli.ts Normal file
View File

@@ -0,0 +1,101 @@
#!/usr/bin/env node
/**
* DSS CLI - Design System Server Command Line Interface
*
* A portable companion for UI developers. Commands:
*
* - init: Initialize DSS in a project
* - status: Check server status
* - extract: Extract tokens/components from Figma
* - sync: Sync tokens to codebase
* - config: Manage configuration
* - start: Start the server
* - stop: Stop the server
*/
import { Command } from 'commander';
import chalk from 'chalk';
import { initCommand } from './commands/init.js';
import { startCommand } from './commands/start.js';
import { syncCommand } from './commands/sync.js';
import { extractCommand } from './commands/extract.js';
import { configCommand } from './commands/config.js';
import { statusCommand } from './commands/status.js';
import { stopCommand } from './commands/stop.js';
const program = new Command();
program
.name('dss')
.description('Design System Server - CLI for UI Developers')
.version('0.1.0');
// Init command - setup DSS in a project
program
.command('init')
.description('Initialize DSS in your project')
.option('-f, --figma-key <key>', 'Figma file key')
.option('-t, --figma-token <token>', 'Figma access token')
.action(initCommand);
// Start command - start the DSS server
program
.command('start')
.description('Start the DSS server')
.option('-p, --port <port>', 'Server port', '3456')
.option('-d, --dev', 'Development mode with hot-reload')
.option('--no-open', 'Do not open browser')
.action(startCommand);
// Sync command - sync tokens from Figma
program
.command('sync')
.description('Sync tokens from Figma to codebase')
.option('-f, --format <format>', 'Output format: css, scss, json, ts', 'css')
.option('-o, --output <path>', 'Output directory')
.option('--file-key <key>', 'Figma file key (overrides config)')
.action(syncCommand);
// Extract command - extract components or tokens
program
.command('extract <type>')
.description('Extract components or tokens from Figma')
.option('-f, --format <format>', 'Output format', 'json')
.option('-o, --output <path>', 'Output location')
.option('--file-key <key>', 'Figma file key')
.action(extractCommand);
// Config command - manage configuration
program
.command('config')
.description('Manage DSS configuration')
.option('--set <key=value>', 'Set configuration value')
.option('--get <key>', 'Get configuration value')
.option('--list', 'List all configuration')
.action(configCommand);
// Stop command - stop the server
program
.command('stop')
.description('Stop the DSS server')
.action(stopCommand);
// Status command - check DSS status
program
.command('status')
.description('Check DSS server status and configuration')
.action(statusCommand);
// Parse arguments
program.parse();
// Show help if no command provided
if (!process.argv.slice(2).length) {
console.log(chalk.blue(`
╔═══════════════════════════════════════════════════════════════╗
${chalk.bold('DSS')} - Design System Server ║
║ UI Developer Companion ║
╚═══════════════════════════════════════════════════════════════╝
`));
program.outputHelp();
}

View File

@@ -0,0 +1,138 @@
/**
* DSS Config Command
*
* Manage DSS configuration.
*/
import chalk from 'chalk';
import {
getConfig,
loadProjectConfig,
saveProjectConfig,
setGlobalConfig,
getGlobalConfig,
listGlobalConfig,
hasProjectConfig,
} from '../lib/config.js';
interface ConfigOptions {
set?: string;
get?: string;
list?: boolean;
}
export async function configCommand(options: ConfigOptions): Promise<void> {
if (options.set) {
await setConfig(options.set);
} else if (options.get) {
await getConfigValue(options.get);
} else if (options.list) {
await listConfig();
} else {
await listConfig();
}
}
async function setConfig(keyValue: string): Promise<void> {
const [key, ...valueParts] = keyValue.split('=');
const value = valueParts.join('=');
if (!key || value === undefined) {
console.log(chalk.red(' Invalid format. Use: --set key=value'));
process.exit(1);
}
// Determine if this is a global or project config
const globalKeys = ['figmaToken', 'defaultPort', 'defaultFormat'];
const projectKeys = ['figmaFileKey', 'port', 'outputDir', 'tokenFormat', 'componentFramework'];
if (globalKeys.includes(key)) {
// Global config
const parsedValue = parseValue(value);
setGlobalConfig(key, parsedValue as string | number);
console.log(chalk.green(` Set global config: ${key}`));
console.log(chalk.dim(` Value: ${key === 'figmaToken' ? '***hidden***' : value}`));
} else if (projectKeys.includes(key)) {
// Project config
if (!hasProjectConfig()) {
console.log(chalk.yellow(' No project config found. Run: dss init'));
process.exit(1);
}
const config = loadProjectConfig();
(config as Record<string, unknown>)[key] = parseValue(value);
saveProjectConfig(config);
console.log(chalk.green(` Set project config: ${key}`));
console.log(chalk.dim(` Value: ${value}`));
} else {
console.log(chalk.yellow(` Unknown config key: ${key}`));
console.log('');
console.log(chalk.dim(' Global keys: figmaToken, defaultPort, defaultFormat'));
console.log(chalk.dim(' Project keys: figmaFileKey, port, outputDir, tokenFormat, componentFramework'));
}
}
async function getConfigValue(key: string): Promise<void> {
const config = getConfig();
const value = (config as Record<string, unknown>)[key];
if (value === undefined) {
// Try global config
const globalValue = getGlobalConfig(key);
if (globalValue !== undefined) {
console.log(chalk.dim(` ${key} (global):`), key === 'figmaToken' ? '***hidden***' : String(globalValue));
} else {
console.log(chalk.yellow(` Config key not found: ${key}`));
}
} else {
console.log(chalk.dim(` ${key}:`), key === 'figmaToken' ? '***hidden***' : String(value));
}
}
async function listConfig(): Promise<void> {
console.log('');
console.log(chalk.blue(' DSS Configuration'));
console.log(chalk.dim(' ─────────────────'));
console.log('');
// Project config
if (hasProjectConfig()) {
console.log(chalk.green(' Project Config:'));
const projectConfig = loadProjectConfig();
Object.entries(projectConfig).forEach(([key, value]) => {
console.log(chalk.dim(` ${key}:`), String(value));
});
} else {
console.log(chalk.yellow(' No project config (run: dss init)'));
}
console.log('');
// Global config
console.log(chalk.green(' Global Config:'));
const globalConf = listGlobalConfig();
Object.entries(globalConf).forEach(([key, value]) => {
const displayValue = key === 'figmaToken' ? '***hidden***' : String(value);
console.log(chalk.dim(` ${key}:`), displayValue);
});
console.log('');
// Merged config
console.log(chalk.green(' Effective Config:'));
const merged = getConfig();
Object.entries(merged).forEach(([key, value]) => {
const displayValue = key === 'figmaToken' ? (value ? '***configured***' : 'not set') : String(value);
console.log(chalk.dim(` ${key}:`), displayValue);
});
console.log('');
}
function parseValue(value: string): string | number | boolean {
if (value === 'true') return true;
if (value === 'false') return false;
if (!isNaN(Number(value))) return Number(value);
return value;
}

View File

@@ -0,0 +1,146 @@
/**
* DSS Extract Command
*
* Extract tokens or components from Figma.
*/
import chalk from 'chalk';
import ora from 'ora';
import { writeFileSync, existsSync, mkdirSync } from 'fs';
import { join } from 'path';
import { getConfig, getProjectRoot } from '../lib/config.js';
import { getApiClient } from '../lib/api.js';
import { isServerRunning } from '../lib/server.js';
interface ExtractOptions {
format: string;
output?: string;
fileKey?: string;
}
export async function extractCommand(
type: string,
options: ExtractOptions
): Promise<void> {
const validTypes = ['tokens', 'components', 'styles', 'all'];
if (!validTypes.includes(type)) {
console.log(chalk.red(` Invalid type: ${type}`));
console.log(chalk.dim(` Valid types: ${validTypes.join(', ')}`));
process.exit(1);
}
const config = getConfig();
const cwd = getProjectRoot();
// Check if server is running
if (!isServerRunning(cwd)) {
console.log(chalk.yellow(' DSS server is not running'));
console.log(chalk.dim(' Start it with: dss start'));
process.exit(1);
}
const fileKey = options.fileKey || config.figmaFileKey;
if (!fileKey) {
console.log(chalk.red(' No Figma file key configured'));
console.log(chalk.dim(' Set it with: dss config --set figmaFileKey=YOUR_KEY'));
process.exit(1);
}
const outputDir = options.output || join(cwd, '.dss', 'output');
if (!existsSync(outputDir)) {
mkdirSync(outputDir, { recursive: true });
}
const api = getApiClient({ port: config.port });
if (type === 'tokens' || type === 'all') {
await extractTokens(api, fileKey, options.format, outputDir);
}
if (type === 'components' || type === 'all') {
await extractComponents(api, fileKey, outputDir);
}
if (type === 'styles' || type === 'all') {
await extractStyles(api, fileKey, outputDir);
}
}
async function extractTokens(
api: ReturnType<typeof getApiClient>,
fileKey: string,
format: string,
outputDir: string
): Promise<void> {
const spinner = ora('Extracting tokens...').start();
try {
const result = await api.extractTokens(fileKey, format || 'json');
const filename = format === 'css' ? 'tokens.css' :
format === 'scss' ? '_tokens.scss' :
format === 'ts' ? 'tokens.ts' : 'tokens.json';
const outputPath = join(outputDir, filename);
writeFileSync(outputPath, result.formatted_output);
spinner.succeed(`Extracted ${result.tokens_count} tokens`);
console.log(chalk.dim(` Output: ${outputPath}`));
} catch (error) {
spinner.fail('Failed to extract tokens');
console.error(chalk.red(` ${(error as Error).message}`));
}
}
async function extractComponents(
api: ReturnType<typeof getApiClient>,
fileKey: string,
outputDir: string
): Promise<void> {
const spinner = ora('Extracting components...').start();
try {
const result = await api.extractComponents(fileKey);
const outputPath = join(outputDir, 'components.json');
writeFileSync(outputPath, JSON.stringify(result.components, null, 2));
spinner.succeed(`Extracted ${result.components_count} components`);
console.log(chalk.dim(` Output: ${outputPath}`));
// Show component summary
console.log('');
result.components.forEach(comp => {
console.log(chalk.dim(` - ${comp.name}`));
if (comp.variants?.length) {
console.log(chalk.dim(` Variants: ${comp.variants.join(', ')}`));
}
});
} catch (error) {
spinner.fail('Failed to extract components');
console.error(chalk.red(` ${(error as Error).message}`));
}
}
async function extractStyles(
api: ReturnType<typeof getApiClient>,
fileKey: string,
outputDir: string
): Promise<void> {
const spinner = ora('Extracting styles...').start();
try {
// Note: This would need a corresponding API endpoint
// For now, we'll extract tokens which include style information
const result = await api.extractTokens(fileKey, 'json');
const outputPath = join(outputDir, 'styles.json');
writeFileSync(outputPath, JSON.stringify(result.tokens, null, 2));
spinner.succeed(`Extracted styles`);
console.log(chalk.dim(` Output: ${outputPath}`));
} catch (error) {
spinner.fail('Failed to extract styles');
console.error(chalk.red(` ${(error as Error).message}`));
}
}

View File

@@ -0,0 +1,107 @@
/**
* DSS Init Command
*
* Initialize DSS in the current project.
*/
import chalk from 'chalk';
import ora from 'ora';
import { existsSync, mkdirSync } from 'fs';
import { join } from 'path';
import {
getProjectRoot,
hasProjectConfig,
saveProjectConfig,
setGlobalConfig,
type DSSConfig,
} from '../lib/config.js';
interface InitOptions {
figmaKey?: string;
figmaToken?: string;
}
export async function initCommand(options: InitOptions): Promise<void> {
const spinner = ora('Initializing DSS...').start();
try {
const projectRoot = getProjectRoot();
const dssDir = join(projectRoot, '.dss');
// Check if already initialized
if (hasProjectConfig()) {
spinner.warn('DSS is already initialized in this project');
console.log(chalk.dim(` Config: ${join(dssDir, 'config.json')}`));
return;
}
// Create .dss directory
if (!existsSync(dssDir)) {
mkdirSync(dssDir, { recursive: true });
}
// Create output directory
const outputDir = join(dssDir, 'output');
if (!existsSync(outputDir)) {
mkdirSync(outputDir, { recursive: true });
}
// Build config
const config: DSSConfig = {
port: 3456,
outputDir: '.dss/output',
tokenFormat: 'css',
componentFramework: 'react',
};
if (options.figmaKey) {
config.figmaFileKey = options.figmaKey;
}
// Save Figma token globally (not in project config for security)
if (options.figmaToken) {
setGlobalConfig('figmaToken', options.figmaToken);
spinner.info('Figma token saved to global config');
}
// Save project config
saveProjectConfig(config);
spinner.succeed('DSS initialized successfully!');
console.log('');
console.log(chalk.green(' Created:'));
console.log(chalk.dim(` .dss/config.json`));
console.log(chalk.dim(` .dss/output/`));
console.log('');
// Next steps
console.log(chalk.blue(' Next steps:'));
if (!options.figmaToken) {
console.log(chalk.dim(' 1. Set your Figma token:'));
console.log(chalk.white(' dss config --set figmaToken=figd_xxxxx'));
}
if (!options.figmaKey) {
console.log(chalk.dim(` ${options.figmaToken ? '1' : '2'}. Set your Figma file key:`));
console.log(chalk.white(' dss config --set figmaFileKey=abc123'));
}
console.log(chalk.dim(` ${options.figmaToken && options.figmaKey ? '1' : '3'}. Start the server:`));
console.log(chalk.white(' dss start'));
console.log('');
// Add to .gitignore if exists
const gitignorePath = join(projectRoot, '.gitignore');
if (existsSync(gitignorePath)) {
const { readFileSync, appendFileSync } = await import('fs');
const gitignore = readFileSync(gitignorePath, 'utf-8');
if (!gitignore.includes('.dss/')) {
appendFileSync(gitignorePath, '\n# DSS\n.dss/\n');
console.log(chalk.dim(' Added .dss/ to .gitignore'));
}
}
} catch (error) {
spinner.fail('Failed to initialize DSS');
console.error(chalk.red(` ${(error as Error).message}`));
process.exit(1);
}
}

View File

@@ -0,0 +1,109 @@
/**
* DSS Start Command
*
* Start the DSS server.
*/
import chalk from 'chalk';
import ora from 'ora';
import { exec } from 'child_process';
import { getConfig, getProjectRoot, hasProjectConfig } from '../lib/config.js';
import {
startServer,
isServerRunning,
getServerPid,
waitForServer,
stopServer,
} from '../lib/server.js';
interface StartOptions {
port: string;
dev: boolean;
open: boolean;
}
export async function startCommand(options: StartOptions): Promise<void> {
const port = parseInt(options.port, 10);
const cwd = getProjectRoot();
const config = getConfig();
// Check if already running
if (isServerRunning(cwd)) {
const pid = getServerPid(cwd);
console.log(chalk.yellow(` DSS is already running (PID: ${pid})`));
console.log(chalk.dim(` Dashboard: http://localhost:${port}`));
console.log('');
console.log(chalk.dim(' To restart: dss stop && dss start'));
return;
}
const spinner = ora('Starting DSS server...').start();
try {
// Start server
const serverProcess = await startServer({
port,
dev: options.dev,
cwd,
});
if (options.dev) {
spinner.succeed('DSS running in development mode');
console.log(chalk.dim(' Press Ctrl+C to stop'));
console.log('');
// In dev mode, we're attached to the process
serverProcess.on('exit', (code) => {
console.log(chalk.dim(`\n Server exited with code ${code}`));
process.exit(code || 0);
});
// Handle Ctrl+C
process.on('SIGINT', () => {
console.log(chalk.dim('\n Stopping server...'));
serverProcess.kill('SIGTERM');
});
} else {
// Wait for server to be ready
spinner.text = 'Waiting for server to be ready...';
const ready = await waitForServer(port, 15000);
if (!ready) {
spinner.fail('Server failed to start');
console.error(chalk.red(' Check logs: .dss/dss.log'));
await stopServer(cwd);
process.exit(1);
}
spinner.succeed(`DSS started (PID: ${serverProcess.pid})`);
console.log('');
console.log(chalk.green(' Dashboard:'), chalk.blue(`http://localhost:${port}`));
console.log(chalk.green(' API: '), chalk.blue(`http://localhost:${port}/api`));
console.log(chalk.green(' Docs: '), chalk.blue(`http://localhost:${port}/docs`));
console.log('');
console.log(chalk.dim(' Logs: .dss/dss.log'));
console.log(chalk.dim(' Stop: dss stop'));
console.log('');
// Show Figma status
if (config.figmaFileKey) {
console.log(chalk.dim(` Figma file: ${config.figmaFileKey}`));
} else {
console.log(chalk.yellow(' No Figma file configured'));
console.log(chalk.dim(' Run: dss config --set figmaFileKey=YOUR_KEY'));
}
// Open browser if requested
if (options.open) {
const url = `http://localhost:${port}`;
const openCmd = process.platform === 'darwin' ? 'open' :
process.platform === 'win32' ? 'start' : 'xdg-open';
exec(`${openCmd} ${url}`);
}
}
} catch (error) {
spinner.fail('Failed to start DSS');
console.error(chalk.red(` ${(error as Error).message}`));
process.exit(1);
}
}

View File

@@ -0,0 +1,95 @@
/**
* DSS Status Command
*
* Check DSS server status and configuration.
*/
import chalk from 'chalk';
import { getConfig, getProjectRoot, hasProjectConfig } from '../lib/config.js';
import { isServerRunning, getServerPid } from '../lib/server.js';
import { getApiClient } from '../lib/api.js';
export async function statusCommand(): Promise<void> {
const cwd = getProjectRoot();
const config = getConfig();
console.log('');
console.log(chalk.cyan(' DSS Status'));
console.log(chalk.dim(' ────────────────────────'));
console.log('');
// Project status
if (hasProjectConfig()) {
console.log(chalk.green(' Project:'), chalk.dim('Initialized'));
console.log(chalk.dim(` Root: ${cwd}`));
} else {
console.log(chalk.yellow(' Project:'), chalk.dim('Not initialized'));
console.log(chalk.dim(' Initialize: dss init'));
}
console.log('');
// Server status
const running = isServerRunning(cwd);
const pid = getServerPid(cwd);
const port = config.port || 3456;
if (running) {
console.log(chalk.green(' Server:'), chalk.dim(`Running (PID: ${pid})`));
console.log(chalk.dim(` URL: http://localhost:${port}`));
// Try to get health info
try {
const api = getApiClient({ port });
const health = await api.health();
console.log(chalk.dim(` Mode: ${health.figma_mode}`));
} catch {
console.log(chalk.yellow(' Unable to get health status'));
}
} else {
console.log(chalk.yellow(' Server:'), chalk.dim('Stopped'));
console.log(chalk.dim(' Start: dss start'));
}
console.log('');
// Figma integration
if (config.figmaToken) {
console.log(chalk.green(' Figma:'), chalk.dim('Configured'));
// Test connection if server is running
if (running) {
try {
const api = getApiClient({ port });
const test = await api.testFigmaConnection();
if (test.success) {
console.log(chalk.green(' Connection:'), chalk.dim(`OK (${test.user})`));
} else {
console.log(chalk.red(' Connection:'), chalk.dim(test.error || 'Failed'));
}
} catch {
console.log(chalk.yellow(' Connection:'), chalk.dim('Cannot test'));
}
}
} else {
console.log(chalk.yellow(' Figma:'), chalk.dim('Not configured'));
console.log(chalk.dim(' Configure: dss config --set figmaToken=figd_xxxxx'));
}
if (config.figmaFileKey) {
console.log(chalk.green(' Figma File:'), chalk.dim(config.figmaFileKey));
} else {
console.log(chalk.yellow(' Figma File:'), chalk.dim('Not configured'));
console.log(chalk.dim(' Set: dss config --set figmaFileKey=abc123'));
}
console.log('');
// Output config
console.log(chalk.dim(' Output:'));
console.log(chalk.dim(` Format: ${config.tokenFormat || 'css'}`));
console.log(chalk.dim(` Framework: ${config.componentFramework || 'react'}`));
console.log(chalk.dim(` Directory: ${config.outputDir || '.dss/output'}`));
console.log('');
}

View File

@@ -0,0 +1,25 @@
/**
* DSS Stop Command
*
* Stop the DSS server.
*/
import chalk from 'chalk';
import { getProjectRoot } from '../lib/config.js';
import { isServerRunning, stopServer, getServerPid } from '../lib/server.js';
export async function stopCommand(): Promise<void> {
const cwd = getProjectRoot();
if (!isServerRunning(cwd)) {
console.log(chalk.yellow(' DSS is not running'));
return;
}
const pid = getServerPid(cwd);
console.log(chalk.dim(` Stopping DSS (PID: ${pid})...`));
await stopServer(cwd);
console.log(chalk.green(' DSS stopped'));
}

View File

@@ -0,0 +1,102 @@
/**
* DSS Sync Command
*
* Sync design tokens from Figma to local files.
*/
import chalk from 'chalk';
import ora from 'ora';
import { writeFileSync, existsSync, mkdirSync } from 'fs';
import { dirname, join } from 'path';
import { getConfig, getProjectRoot } from '../lib/config.js';
import { getApiClient } from '../lib/api.js';
import { isServerRunning } from '../lib/server.js';
interface SyncOptions {
format: string;
output?: string;
fileKey?: string;
}
export async function syncCommand(options: SyncOptions): Promise<void> {
const config = getConfig();
const cwd = getProjectRoot();
// Check if server is running
if (!isServerRunning(cwd)) {
console.log(chalk.yellow(' DSS server is not running'));
console.log(chalk.dim(' Start it with: dss start'));
console.log('');
console.log(chalk.dim(' Or sync directly via API if running remotely'));
process.exit(1);
}
const fileKey = options.fileKey || config.figmaFileKey;
if (!fileKey) {
console.log(chalk.red(' No Figma file key configured'));
console.log(chalk.dim(' Set it with: dss config --set figmaFileKey=YOUR_KEY'));
process.exit(1);
}
const format = options.format || config.tokenFormat || 'css';
const outputPath = options.output || getDefaultOutputPath(format, cwd);
const spinner = ora(`Syncing tokens from Figma (${format})...`).start();
try {
const api = getApiClient({ port: config.port });
// Extract tokens
spinner.text = 'Extracting tokens from Figma...';
const result = await api.extractTokens(fileKey, format);
if (!result.success) {
throw new Error('Token extraction failed');
}
// Write output file
spinner.text = `Writing ${result.tokens_count} tokens to ${outputPath}...`;
const dir = dirname(outputPath);
if (!existsSync(dir)) {
mkdirSync(dir, { recursive: true });
}
writeFileSync(outputPath, result.formatted_output);
spinner.succeed(`Synced ${result.tokens_count} tokens`);
console.log('');
console.log(chalk.green(' Output:'), chalk.dim(outputPath));
console.log('');
// Show token summary
const categories = result.tokens.reduce((acc, t) => {
acc[t.category] = (acc[t.category] || 0) + 1;
return acc;
}, {} as Record<string, number>);
console.log(chalk.dim(' Token breakdown:'));
Object.entries(categories).forEach(([cat, count]) => {
console.log(chalk.dim(` ${cat}: ${count}`));
});
console.log('');
} catch (error) {
spinner.fail('Sync failed');
console.error(chalk.red(` ${(error as Error).message}`));
process.exit(1);
}
}
function getDefaultOutputPath(format: string, cwd: string): string {
const extensions: Record<string, string> = {
css: 'tokens.css',
scss: '_tokens.scss',
json: 'tokens.json',
ts: 'tokens.ts',
js: 'tokens.js',
};
const filename = extensions[format] || 'tokens.css';
return join(cwd, '.dss', 'output', filename);
}

15
apps/cli/src/index.ts Normal file
View File

@@ -0,0 +1,15 @@
/**
* DSS - Design System Server
*
* Programmatic API for DSS.
*/
export { DSSApiClient, getApiClient, type ApiOptions } from './lib/api.js';
export { getConfig, getProjectRoot, hasProjectConfig, type DSSConfig } from './lib/config.js';
export {
startServer,
stopServer,
isServerRunning,
getServerPid,
waitForServer,
} from './lib/server.js';

144
apps/cli/src/lib/api.ts Normal file
View File

@@ -0,0 +1,144 @@
/**
* DSS API Client
*
* Communicates with the DSS server.
*/
import { getConfig } from './config.js';
export interface ApiOptions {
port?: number;
baseUrl?: string;
}
export class DSSApiClient {
private baseUrl: string;
constructor(options: ApiOptions = {}) {
const config = getConfig();
const port = options.port || config.port || 3456;
this.baseUrl = options.baseUrl || `http://localhost:${port}/api`;
}
private async request<T>(
endpoint: string,
options: RequestInit = {}
): Promise<T> {
const url = `${this.baseUrl}${endpoint}`;
const response = await fetch(url, {
...options,
headers: {
'Content-Type': 'application/json',
...options.headers,
},
});
if (!response.ok) {
const errorData = await response.json().catch(() => ({ message: response.statusText })) as { message?: string };
throw new Error(errorData.message || `Request failed: ${response.status}`);
}
const text = await response.text();
return text ? JSON.parse(text) as T : {} as T;
}
async health(): Promise<{ status: string; figma_mode: string }> {
// Health endpoint is at root, not under /api
const url = this.baseUrl.replace('/api', '') + '/health';
const response = await fetch(url);
return response.json() as Promise<{ status: string; figma_mode: string }>;
}
async getConfig(): Promise<Record<string, unknown>> {
return this.request('/config');
}
async setFigmaToken(token: string): Promise<void> {
await this.request('/config', {
method: 'PUT',
body: JSON.stringify({ figma_token: token }),
});
}
async testFigmaConnection(): Promise<{ success: boolean; user?: string; error?: string }> {
return this.request('/config/figma/test', { method: 'POST' });
}
async extractTokens(fileKey: string, format: string = 'css'): Promise<{
success: boolean;
tokens_count: number;
tokens: Array<{ name: string; value: string; type: string; category: string }>;
formatted_output: string;
output_path: string;
}> {
return this.request('/figma/extract-variables', {
method: 'POST',
body: JSON.stringify({ file_key: fileKey, format }),
});
}
async extractComponents(fileKey: string): Promise<{
success: boolean;
components_count: number;
components: Array<{ name: string; key: string; description: string; variants: string[] }>;
output_path: string;
}> {
return this.request('/figma/extract-components', {
method: 'POST',
body: JSON.stringify({ file_key: fileKey }),
});
}
async syncTokens(fileKey: string, targetPath: string, format: string = 'css'): Promise<{
success: boolean;
tokens_synced: number;
output_file: string;
}> {
return this.request('/figma/sync-tokens', {
method: 'POST',
body: JSON.stringify({ file_key: fileKey, target_path: targetPath, format }),
});
}
async generateCode(fileKey: string, componentName: string, framework: string = 'react'): Promise<{
success: boolean;
component: string;
framework: string;
code: string;
}> {
return this.request('/figma/generate-code', {
method: 'POST',
body: JSON.stringify({ file_key: fileKey, component_name: componentName, framework }),
});
}
async getProjects(): Promise<Array<{
id: string;
name: string;
figma_file_key: string;
status: string;
}>> {
return this.request('/projects');
}
async createProject(data: {
name: string;
description?: string;
figma_file_key?: string;
}): Promise<{ id: string; name: string }> {
return this.request('/projects', {
method: 'POST',
body: JSON.stringify(data),
});
}
}
// Singleton instance
let apiClient: DSSApiClient | null = null;
export function getApiClient(options?: ApiOptions): DSSApiClient {
if (!apiClient || options) {
apiClient = new DSSApiClient(options);
}
return apiClient;
}

View File

@@ -0,0 +1,95 @@
/**
* DSS Configuration Manager
*
* Manages local and project configuration.
*/
import Conf from 'conf';
import { existsSync, readFileSync, writeFileSync } from 'fs';
import { join } from 'path';
// Global user config (stored in home directory)
const globalConfig = new Conf({
projectName: 'dss',
schema: {
figmaToken: { type: 'string' },
defaultPort: { type: 'number', default: 3456 },
defaultFormat: { type: 'string', default: 'css' },
},
});
// Project-level config file
const PROJECT_CONFIG_FILE = '.dss/config.json';
export interface DSSConfig {
figmaFileKey?: string;
figmaToken?: string;
port?: number;
outputDir?: string;
tokenFormat?: 'css' | 'scss' | 'json' | 'ts';
componentFramework?: 'react' | 'vue' | 'svelte' | 'webcomponent';
}
export function getProjectRoot(): string {
return process.cwd();
}
export function getProjectConfigPath(): string {
return join(getProjectRoot(), PROJECT_CONFIG_FILE);
}
export function hasProjectConfig(): boolean {
return existsSync(getProjectConfigPath());
}
export function loadProjectConfig(): DSSConfig {
const configPath = getProjectConfigPath();
if (!existsSync(configPath)) {
return {};
}
try {
const content = readFileSync(configPath, 'utf-8');
return JSON.parse(content);
} catch (error) {
console.warn('Failed to parse project config:', error);
return {};
}
}
export function saveProjectConfig(config: DSSConfig): void {
const configPath = getProjectConfigPath();
const dir = join(getProjectRoot(), '.dss');
// Ensure .dss directory exists
if (!existsSync(dir)) {
const { mkdirSync } = require('fs');
mkdirSync(dir, { recursive: true });
}
writeFileSync(configPath, JSON.stringify(config, null, 2));
}
export function getConfig(): DSSConfig {
const project = loadProjectConfig();
return {
figmaToken: project.figmaToken || globalConfig.get('figmaToken') as string | undefined,
figmaFileKey: project.figmaFileKey,
port: project.port || globalConfig.get('defaultPort') as number,
outputDir: project.outputDir || '.dss/output',
tokenFormat: project.tokenFormat || (globalConfig.get('defaultFormat') as DSSConfig['tokenFormat']),
componentFramework: project.componentFramework || 'react',
};
}
export function setGlobalConfig(key: string, value: string | number): void {
globalConfig.set(key, value);
}
export function getGlobalConfig(key: string): unknown {
return globalConfig.get(key);
}
export function listGlobalConfig(): Record<string, unknown> {
return globalConfig.store;
}

197
apps/cli/src/lib/server.ts Normal file
View File

@@ -0,0 +1,197 @@
/**
* DSS Server Manager
*
* Manages the Python server subprocess.
*/
import { spawn, ChildProcess } from 'child_process';
import { existsSync, writeFileSync, readFileSync, unlinkSync } from 'fs';
import { join, dirname } from 'path';
import { fileURLToPath } from 'url';
const __dirname = dirname(fileURLToPath(import.meta.url));
interface ServerOptions {
port: number;
dev: boolean;
cwd: string;
}
let serverProcess: ChildProcess | null = null;
export function getPythonPath(): string {
// Check for bundled Python venv in npm package (dist/lib -> ../../python/venv)
const bundledPython = join(__dirname, '../../python/venv/bin/python3');
if (existsSync(bundledPython)) {
return bundledPython;
}
// Development path (src/lib -> ../../python/venv)
const devPython = join(__dirname, '../../../python/venv/bin/python3');
if (existsSync(devPython)) {
return devPython;
}
return 'python3';
}
export function getServerPath(): string {
// Check for bundled server or use from package
const bundledServer = join(__dirname, '../../python/api/server.py');
if (existsSync(bundledServer)) {
return dirname(bundledServer);
}
// Fall back to relative path from CLI (cli/dist/lib -> ../../../../tools/api)
const devServer = join(__dirname, '../../../../tools/api');
if (existsSync(join(devServer, 'server.py'))) {
return devServer;
}
// Also check development src path (cli/src/lib -> ../../../../tools/api)
const srcServer = join(__dirname, '../../../tools/api');
if (existsSync(join(srcServer, 'server.py'))) {
return srcServer;
}
throw new Error('Could not find DSS server. Run from the design-system-swarm directory or install the full package.');
}
export function getPidFile(cwd: string): string {
return join(cwd, '.dss', 'dss.pid');
}
export function getLogFile(cwd: string): string {
return join(cwd, '.dss', 'dss.log');
}
export function isServerRunning(cwd: string): boolean {
const pidFile = getPidFile(cwd);
if (!existsSync(pidFile)) {
return false;
}
try {
const pid = parseInt(readFileSync(pidFile, 'utf-8').trim(), 10);
// Check if process exists
process.kill(pid, 0);
return true;
} catch {
// Process doesn't exist, clean up stale PID file
try {
unlinkSync(pidFile);
} catch {
// Ignore
}
return false;
}
}
export function getServerPid(cwd: string): number | null {
const pidFile = getPidFile(cwd);
if (!existsSync(pidFile)) {
return null;
}
try {
return parseInt(readFileSync(pidFile, 'utf-8').trim(), 10);
} catch {
return null;
}
}
export async function startServer(options: ServerOptions): Promise<ChildProcess> {
const { port, dev, cwd } = options;
if (isServerRunning(cwd)) {
throw new Error(`Server is already running (PID: ${getServerPid(cwd)})`);
}
const pythonPath = getPythonPath();
const serverDir = getServerPath();
// Build command args
const args = [
'-m', 'uvicorn',
'server:app',
'--host', '0.0.0.0',
'--port', String(port),
];
if (dev) {
args.push('--reload');
}
// Set environment
const env = {
...process.env,
PYTHONPATH: join(serverDir, '..'),
PORT: String(port),
};
// Spawn server process
serverProcess = spawn(pythonPath, args, {
cwd: serverDir,
env,
stdio: dev ? 'inherit' : ['ignore', 'pipe', 'pipe'],
detached: !dev,
});
if (!dev && serverProcess.pid) {
// Write PID file
const dssDir = join(cwd, '.dss');
if (!existsSync(dssDir)) {
const { mkdirSync } = await import('fs');
mkdirSync(dssDir, { recursive: true });
}
writeFileSync(getPidFile(cwd), String(serverProcess.pid));
// Write logs
const logStream = await import('fs').then(fs =>
fs.createWriteStream(getLogFile(cwd), { flags: 'a' })
);
serverProcess.stdout?.pipe(logStream);
serverProcess.stderr?.pipe(logStream);
// Detach from parent
serverProcess.unref();
}
return serverProcess;
}
export async function stopServer(cwd: string): Promise<boolean> {
const pid = getServerPid(cwd);
if (!pid) {
return false;
}
try {
process.kill(pid, 'SIGTERM');
unlinkSync(getPidFile(cwd));
return true;
} catch {
return false;
}
}
export async function waitForServer(port: number, timeout = 10000): Promise<boolean> {
const start = Date.now();
const url = `http://localhost:${port}/health`;
while (Date.now() - start < timeout) {
try {
const response = await fetch(url);
if (response.ok) {
return true;
}
} catch {
// Server not ready yet
}
await new Promise(resolve => setTimeout(resolve, 200));
}
return false;
}

20
apps/cli/tsconfig.json Normal file
View File

@@ -0,0 +1,20 @@
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"lib": ["ES2022"],
"outDir": "dist",
"rootDir": "src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"declaration": true,
"declarationMap": true,
"sourceMap": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}

View File

@@ -1,18 +1,16 @@
#!/usr/bin/env node
/**
* 🧬 DSS CLI - Design System Server Organism Controller
* DSS CLI - Design System Server Command Line Interface
*
* A portable companion for UI developers - think of it as the organism's
* command-line nervous system. Through these commands, you can:
* A portable companion for UI developers. Commands:
*
* - 🧬 Awaken a new organism (init)
* - 💚 Check the organism's vital signs (status)
* - 🩸 Direct the sensory organs to perceive Figma (extract)
* - 🔄 Circulate extracted nutrients (sync)
* - ⚙️ Adjust the organism's behavior (config)
* - 🧠 Birth a conscious instance (start)
*
* Framework: DSS Organism Framework
* - init: Initialize DSS in a project
* - status: Check server status
* - extract: Extract tokens/components from Figma
* - sync: Sync tokens to codebase
* - config: Manage configuration
* - start: Start the server
* - stop: Stop the server
*/
import { Command } from 'commander';
@@ -29,63 +27,63 @@ const program = new Command();
program
.name('dss')
.description('🧬 Design System Server - Organism Controller for UI Developers')
.description('Design System Server - CLI for UI Developers')
.version('0.1.0');
// Init command - setup DSS in a project
program
.command('init')
.description('🧬 ORGANISM GENESIS - Create a new design system organism in your project')
.option('-f, --figma-key <key>', 'Link to Figma genetic blueprint')
.option('-t, --figma-token <token>', 'Figma sensory organ connection token')
.description('Initialize DSS in your project')
.option('-f, --figma-key <key>', 'Figma file key')
.option('-t, --figma-token <token>', 'Figma access token')
.action(initCommand);
// Start command - start the DSS server
program
.command('start')
.description('💚 ORGANISM AWAKENING - Bring the design system organism to life')
.option('-p, --port <port>', 'Neural pathway port', '3456')
.option('-d, --dev', 'Live consciousness mode with hot-reload')
.option('--no-open', 'Do not open sensory interface')
.description('Start the DSS server')
.option('-p, --port <port>', 'Server port', '3456')
.option('-d, --dev', 'Development mode with hot-reload')
.option('--no-open', 'Do not open browser')
.action(startCommand);
// Sync command - sync tokens from Figma
program
.command('sync')
.description('🩸 NUTRIENT CIRCULATION - Distribute extracted tokens through the codebase')
.option('-f, --format <format>', 'Nutrient format: css, scss, json, ts', 'css')
.option('-o, --output <path>', 'Circulation destination')
.description('Sync tokens from Figma to codebase')
.option('-f, --format <format>', 'Output format: css, scss, json, ts', 'css')
.option('-o, --output <path>', 'Output directory')
.option('--file-key <key>', 'Figma file key (overrides config)')
.action(syncCommand);
// Extract command - extract components or tokens
program
.command('extract <type>')
.description('👁️ SENSORY PERCEPTION - Direct organism eyes to perceive Figma designs')
.option('-f, --format <format>', 'Perception output format', 'json')
.option('-o, --output <path>', 'Memory storage location')
.description('Extract components or tokens from Figma')
.option('-f, --format <format>', 'Output format', 'json')
.option('-o, --output <path>', 'Output location')
.option('--file-key <key>', 'Figma file key')
.action(extractCommand);
// Config command - manage configuration
program
.command('config')
.description('⚙️ ENDOCRINE ADJUSTMENT - Configure organism behavior and preferences')
.option('--set <key=value>', 'Set organism hormone value')
.option('--get <key>', 'Read organism hormone value')
.option('--list', 'View all hormones')
.description('Manage DSS configuration')
.option('--set <key=value>', 'Set configuration value')
.option('--get <key>', 'Get configuration value')
.option('--list', 'List all configuration')
.action(configCommand);
// Stop command - stop the server
program
.command('stop')
.description('😴 ORGANISM REST - Put the design system organism into sleep mode')
.description('Stop the DSS server')
.action(stopCommand);
// Status command - check DSS status
program
.command('status')
.description('🏥 VITAL SIGNS CHECK - Monitor organism health and configuration')
.description('Check DSS server status and configuration')
.action(statusCommand);
// Parse arguments

View File

@@ -1,8 +1,7 @@
/**
* 🏥 DSS Status Command - Organism Vital Signs
* DSS Status Command
*
* Check DSS design system organism's vital signs, consciousness state,
* and sensory organ configuration.
* Check DSS server status and configuration.
*/
import chalk from 'chalk';
@@ -15,48 +14,48 @@ export async function statusCommand(): Promise<void> {
const config = getConfig();
console.log('');
console.log(chalk.cyan(' 🏥 ORGANISM VITAL SIGNS'));
console.log(chalk.cyan(' DSS Status'));
console.log(chalk.dim(' ────────────────────────'));
console.log('');
// Organism status
// Project status
if (hasProjectConfig()) {
console.log(chalk.green(' 🧬 Organism:'), chalk.dim('Born and conscious'));
console.log(chalk.dim(` Home: ${cwd}`));
console.log(chalk.green(' Project:'), chalk.dim('Initialized'));
console.log(chalk.dim(` Root: ${cwd}`));
} else {
console.log(chalk.yellow(' 🧬 Organism:'), chalk.dim('Not yet born'));
console.log(chalk.dim(' Genesis: dss init'));
console.log(chalk.yellow(' Project:'), chalk.dim('Not initialized'));
console.log(chalk.dim(' Initialize: dss init'));
}
console.log('');
// Consciousness status (server)
// Server status
const running = isServerRunning(cwd);
const pid = getServerPid(cwd);
const port = config.port || 3456;
if (running) {
console.log(chalk.green(' 💚 Consciousness:'), chalk.dim(`Awake (PID: ${pid})`));
console.log(chalk.dim(` Neural port: http://localhost:${port}`));
console.log(chalk.green(' Server:'), chalk.dim(`Running (PID: ${pid})`));
console.log(chalk.dim(` URL: http://localhost:${port}`));
// Try to get health info
try {
const api = getApiClient({ port });
const health = await api.health();
console.log(chalk.dim(` Awareness: ${health.figma_mode}`));
console.log(chalk.dim(` Mode: ${health.figma_mode}`));
} catch {
console.log(chalk.yellow(' ⚠️ Unable to read consciousness'));
console.log(chalk.yellow(' Unable to get health status'));
}
} else {
console.log(chalk.yellow(' 💚 Consciousness:'), chalk.dim('Sleeping'));
console.log(chalk.dim(' Awaken: dss start'));
console.log(chalk.yellow(' Server:'), chalk.dim('Stopped'));
console.log(chalk.dim(' Start: dss start'));
}
console.log('');
// Sensory organs (Figma)
// Figma integration
if (config.figmaToken) {
console.log(chalk.green(' 👁️ Sensory Eyes:'), chalk.dim('Configured'));
console.log(chalk.green(' Figma:'), chalk.dim('Configured'));
// Test connection if server is running
if (running) {
@@ -64,21 +63,21 @@ export async function statusCommand(): Promise<void> {
const api = getApiClient({ port });
const test = await api.testFigmaConnection();
if (test.success) {
console.log(chalk.green(' Perception:'), chalk.dim(`Clear (${test.user})`));
console.log(chalk.green(' Connection:'), chalk.dim(`OK (${test.user})`));
} else {
console.log(chalk.red(' Perception:'), chalk.dim(test.error || 'Blinded'));
console.log(chalk.red(' Connection:'), chalk.dim(test.error || 'Failed'));
}
} catch {
console.log(chalk.yellow(' Perception:'), chalk.dim('Cannot test'));
console.log(chalk.yellow(' Connection:'), chalk.dim('Cannot test'));
}
}
} else {
console.log(chalk.yellow(' 👁️ Sensory Eyes:'), chalk.dim('Not configured'));
console.log(chalk.yellow(' Figma:'), chalk.dim('Not configured'));
console.log(chalk.dim(' Configure: dss config --set figmaToken=figd_xxxxx'));
}
if (config.figmaFileKey) {
console.log(chalk.green(' 📋 Genetic Blueprint:'), chalk.dim(config.figmaFileKey));
console.log(chalk.green(' Figma File:'), chalk.dim(config.figmaFileKey));
} else {
console.log(chalk.yellow(' Figma File:'), chalk.dim('Not configured'));
console.log(chalk.dim(' Set: dss config --set figmaFileKey=abc123'));

View File

@@ -1,57 +0,0 @@
# DSS MVP1 Test Environment Variables
# This file contains mock/test values for running tests
# DO NOT use these values in production!
# =============================================================================
# Mock API Keys for Testing
# =============================================================================
# These are MOCK keys from tests/fixtures/api_keys.json
# They will NOT work with real APIs
# Mock Anthropic API key (for testing only)
ANTHROPIC_API_KEY=sk-ant-api03-test-mock-key-for-testing-only-do-not-use-in-production-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# Mock Figma token (for testing only)
FIGMA_TOKEN=figd_test_mock_token_for_testing_only_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# =============================================================================
# Test Configuration
# =============================================================================
# Use test database
DATABASE_PATH=.dss/test.db
# Disable caching in tests
FIGMA_CACHE_TTL=0
DSS_CACHE_DIR=.dss/test_cache
# Test mode
NODE_ENV=test
LOG_LEVEL=debug
# =============================================================================
# Server Configuration for Tests
# =============================================================================
PORT=3456
DSS_MCP_PORT=3457
DSS_MCP_HOST=127.0.0.1
# =============================================================================
# For Real API Testing (Optional)
# =============================================================================
# If you want to test with REAL APIs, uncomment and add your real keys:
# REAL_ANTHROPIC_API_KEY=sk-ant-api03-your-real-key-here
# REAL_FIGMA_TOKEN=your-real-figma-token-here
# REAL_FIGMA_FILE_KEY=your-real-file-key-here
# =============================================================================
# Usage Instructions
# =============================================================================
# To use this file:
# 1. Copy to .env: cp .env.test .env
# 2. Run tests: pytest tests/
# 3. Mock APIs will be used automatically
#
# To test with real APIs:
# 1. Add your real keys above (REAL_* variables)
# 2. Update test code to use real keys when REAL_* vars are set
# 3. Run tests: pytest tests/ --real-api

View File

@@ -1,26 +0,0 @@
import { create } from '@storybook/theming/create';
export const dssTheme = create({
base: 'light',
brandTitle: 'Design System',
brandUrl: '',
brandImage: '',
brandTarget: '_self',
colorPrimary: '#3B82F6',
colorSecondary: '#10B981',
appBg: '#FFFFFF',
appContentBg: '#FFFFFF',
appBorderColor: '#E5E7EB',
textColor: '#1F2937',
textInverseColor: '#FFFFFF',
textMutedColor: '#6B7280',
barTextColor: '#6B7280',
barSelectedColor: '#3B82F6',
barBg: '#FFFFFF',
inputBg: '#FFFFFF',
inputBorder: '#D1D5DB',
inputTextColor: '#1F2937',
inputBorderRadius: 4,
fontBase: '"Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
fontCode: '"Fira Code", "Monaco", monospace',
});

View File

@@ -1,20 +0,0 @@
/** @type { import("@storybook/html").StorybookConfig } */
const config = {
stories: [
"../stories/Welcome.stories.js",
"../stories/generated/**/*.mdx",
"../stories/generated/**/*.stories.@(js|jsx|mjs|ts|tsx)"
],
addons: [
"@storybook/addon-essentials",
"@storybook/addon-webpack5-compiler-babel",
"@chromatic-com/storybook"
],
framework: {
name: "@storybook/html-webpack5",
options: {}
},
docs: {}
};
export default config;

View File

@@ -1,6 +0,0 @@
import { addons } from '@storybook/manager-api';
import { dssTheme } from './dss-theme';
addons.setConfig({
theme: dssTheme,
});

View File

@@ -1,188 +0,0 @@
/**
* Storybook Preview Configuration
*
* Integrates DSS design tokens into Storybook:
* - Applies tokens globally to all stories
* - Configures Storybook UI with DSS colors
* - Sets up theme switching with token variables
* - Enables component stories to use own design system
*/
// Import tokens generated from Figma
// These would be auto-generated via build process from token exporters
const DSSTokens = {
colors: {
primary: '#0066FF',
secondary: '#FF6B00',
success: '#00B600',
warning: '#FFB800',
danger: '#FF0000',
text: '#1A1A1A',
textLight: '#666666',
surface: '#FFFFFF',
background: '#F5F5F5',
border: '#E0E0E0',
},
spacing: {
xs: '4px',
sm: '8px',
md: '16px',
lg: '24px',
xl: '32px',
},
typography: {
headingFamily: "'Inter', sans-serif",
bodyFamily: "'Inter', sans-serif",
monospaceFamily: "'Courier New', monospace",
},
borderRadius: {
sm: '4px',
md: '8px',
lg: '12px',
full: '9999px',
},
shadows: {
sm: '0 1px 2px rgba(0, 0, 0, 0.05)',
md: '0 4px 6px rgba(0, 0, 0, 0.1)',
lg: '0 10px 15px rgba(0, 0, 0, 0.1)',
},
};
// Create Storybook theme using DSS tokens
const createStorybookTheme = () => ({
base: 'light',
colorPrimary: DSSTokens.colors.primary,
colorSecondary: DSSTokens.colors.secondary,
appBg: DSSTokens.colors.background,
appContentBg: DSSTokens.colors.surface,
appBorderColor: DSSTokens.colors.border,
appBorderRadius: parseInt(DSSTokens.borderRadius.md),
textColor: DSSTokens.colors.text,
textInverseColor: '#FFFFFF',
barTextColor: DSSTokens.colors.text,
barBg: DSSTokens.colors.surface,
barSelectedColor: DSSTokens.colors.primary,
barHoverColor: DSSTokens.colors.primary,
barBorderColor: DSSTokens.colors.border,
inputBg: '#FFFFFF',
inputBorder: DSSTokens.colors.border,
inputTextColor: DSSTokens.colors.text,
inputBorderRadius: parseInt(DSSTokens.borderRadius.md),
brandUrl: 'https://dss.overbits.luz.uy',
brandImage: '/dss-logo.svg',
brandTitle: 'DSS Design System',
});
// Register all CSS variables globally
const registerCSSVariables = () => {
const style = document.documentElement.style;
// Register color tokens
Object.entries(DSSTokens.colors).forEach(([name, value]) => {
style.setProperty(`--dss-color-${name}`, value);
});
// Register spacing tokens
Object.entries(DSSTokens.spacing).forEach(([name, value]) => {
style.setProperty(`--dss-spacing-${name}`, value);
});
// Register typography tokens
Object.entries(DSSTokens.typography).forEach(([name, value]) => {
style.setProperty(`--dss-typography-${name}`, value);
});
// Register border radius tokens
Object.entries(DSSTokens.borderRadius).forEach(([name, value]) => {
style.setProperty(`--dss-radius-${name}`, value);
});
// Register shadow tokens
Object.entries(DSSTokens.shadows).forEach(([name, value]) => {
style.setProperty(`--dss-shadow-${name}`, value);
});
};
// Export preview configuration
export const preview = {
parameters: {
// Apply DSS theme to Storybook UI
docs: {
theme: createStorybookTheme(),
},
// Configure viewport options
viewport: {
viewports: {
mobile: {
name: 'Mobile',
styles: { width: '375px', height: '812px' },
type: 'mobile',
},
tablet: {
name: 'Tablet',
styles: { width: '768px', height: '1024px' },
type: 'tablet',
},
desktop: {
name: 'Desktop',
styles: { width: '1280px', height: '720px' },
type: 'desktop',
},
},
},
// Setup backgrounds
backgrounds: {
default: 'light',
values: [
{ name: 'light', value: DSSTokens.colors.surface },
{ name: 'dark', value: '#1A1A1A' },
{ name: 'gray', value: DSSTokens.colors.background },
],
},
},
// Global decorator to apply DSS tokens to all stories
decorators: [
(Story, context) => {
// Register CSS variables
registerCSSVariables();
// Get the story content
const storyContent = Story();
// Create wrapper div with DSS token styles
const wrapper = document.createElement('div');
wrapper.style.cssText = `
--dss-primary: ${DSSTokens.colors.primary};
--dss-secondary: ${DSSTokens.colors.secondary};
--dss-text: ${DSSTokens.colors.text};
--dss-surface: ${DSSTokens.colors.surface};
--dss-background: ${DSSTokens.colors.background};
--dss-border: ${DSSTokens.colors.border};
--dss-spacing-base: ${DSSTokens.spacing.md};
--dss-font-body: ${DSSTokens.typography.bodyFamily};
--dss-font-heading: ${DSSTokens.typography.headingFamily};
--dss-radius: ${DSSTokens.borderRadius.md};
font-family: ${DSSTokens.typography.bodyFamily};
color: ${DSSTokens.colors.text};
background-color: ${DSSTokens.colors.surface};
padding: ${DSSTokens.spacing.lg};
`;
// Append story content to wrapper
if (typeof storyContent === 'string') {
wrapper.innerHTML = storyContent;
} else if (storyContent instanceof Node) {
wrapper.appendChild(storyContent);
}
return wrapper;
},
],
};
// Export Storybook theme (for use in stories)
export const dssTheme = createStorybookTheme();
// Export DSS tokens for use in stories
export const dssTokens = DSSTokens;

View File

@@ -1,435 +0,0 @@
import type { Preview } from '@storybook/react';
// Inject design tokens as CSS variables
const tokenStyles = `
:root {
--version: 1.0.0;
--source: figma;
--figma_file: evCZlaeZrP7X20NIViSJbl;
--synced_at: 2025-12-09T12:50:40.840869;
--categories-color-neutral-50: {'6:0': {'r': 0.9803921580314636, 'g': 0.9803921580314636, 'b': 0.9803921580314636, 'a': 1}};
--categories-color-neutral-100: {'6:0': {'r': 0.9607843160629272, 'g': 0.9607843160629272, 'b': 0.9607843160629272, 'a': 1}};
--categories-color-neutral-200: {'6:0': {'r': 0.8980392217636108, 'g': 0.8980392217636108, 'b': 0.8980392217636108, 'a': 1}};
--categories-color-neutral-300: {'6:0': {'r': 0.8313725590705872, 'g': 0.8313725590705872, 'b': 0.8313725590705872, 'a': 1}};
--categories-color-neutral-400: {'6:0': {'r': 0.6392157077789307, 'g': 0.6392157077789307, 'b': 0.6392157077789307, 'a': 1}};
--categories-color-neutral-500: {'6:0': {'r': 0.45098039507865906, 'g': 0.45098039507865906, 'b': 0.45098039507865906, 'a': 1}};
--categories-color-neutral-600: {'6:0': {'r': 0.32156863808631897, 'g': 0.32156863808631897, 'b': 0.32156863808631897, 'a': 1}};
--categories-color-neutral-700: {'6:0': {'r': 0.250980406999588, 'g': 0.250980406999588, 'b': 0.250980406999588, 'a': 1}};
--categories-color-neutral-800: {'6:0': {'r': 0.14901961386203766, 'g': 0.14901961386203766, 'b': 0.14901961386203766, 'a': 1}};
--categories-color-neutral-900: {'6:0': {'r': 0.09019608050584793, 'g': 0.09019608050584793, 'b': 0.09019608050584793, 'a': 1}};
--categories-color-neutral-950: {'6:0': {'r': 0.03921568766236305, 'g': 0.03921568766236305, 'b': 0.03921568766236305, 'a': 1}};
--categories-color-red-50: {'6:0': {'r': 0.9960784316062927, 'g': 0.9490196108818054, 'b': 0.9490196108818054, 'a': 1}};
--categories-color-red-100: {'6:0': {'r': 1, 'g': 0.886274516582489, 'b': 0.886274516582489, 'a': 1}};
--categories-color-red-200: {'6:0': {'r': 0.9960784316062927, 'g': 0.7921568751335144, 'b': 0.7921568751335144, 'a': 1}};
--categories-color-red-300: {'6:0': {'r': 0.9882352948188782, 'g': 0.6470588445663452, 'b': 0.6470588445663452, 'a': 1}};
--categories-color-red-400: {'6:0': {'r': 0.9725490212440491, 'g': 0.4431372582912445, 'b': 0.4431372582912445, 'a': 1}};
--categories-color-red-500: {'6:0': {'r': 0.9372549057006836, 'g': 0.2666666805744171, 'b': 0.2666666805744171, 'a': 1}};
--categories-color-red-600: {'6:0': {'r': 0.8627451062202454, 'g': 0.14901961386203766, 'b': 0.14901961386203766, 'a': 1}};
--categories-color-red-700: {'6:0': {'r': 0.7254902124404907, 'g': 0.10980392247438431, 'b': 0.10980392247438431, 'a': 1}};
--categories-color-red-800: {'6:0': {'r': 0.6000000238418579, 'g': 0.10588235408067703, 'b': 0.10588235408067703, 'a': 1}};
--categories-color-red-900: {'6:0': {'r': 0.49803921580314636, 'g': 0.11372549086809158, 'b': 0.11372549086809158, 'a': 1}};
--categories-color-red-950: {'6:0': {'r': 0.2705882489681244, 'g': 0.03921568766236305, 'b': 0.03921568766236305, 'a': 1}};
--categories-color-blue-50: {'6:0': {'r': 0.9372549057006836, 'g': 0.9647058844566345, 'b': 1, 'a': 1}};
--categories-color-blue-100: {'6:0': {'r': 0.8588235378265381, 'g': 0.9176470637321472, 'b': 0.9960784316062927, 'a': 1}};
--categories-color-blue-200: {'6:0': {'r': 0.7490196228027344, 'g': 0.8588235378265381, 'b': 0.9960784316062927, 'a': 1}};
--categories-color-blue-300: {'6:0': {'r': 0.5764706134796143, 'g': 0.772549033164978, 'b': 0.9921568632125854, 'a': 1}};
--categories-color-blue-400: {'6:0': {'r': 0.3764705955982208, 'g': 0.6470588445663452, 'b': 0.9803921580314636, 'a': 1}};
--categories-color-blue-500: {'6:0': {'r': 0.23137255012989044, 'g': 0.5098039507865906, 'b': 0.9647058844566345, 'a': 1}};
--categories-color-blue-600: {'6:0': {'r': 0.14509804546833038, 'g': 0.38823530077934265, 'b': 0.9215686321258545, 'a': 1}};
--categories-color-blue-700: {'6:0': {'r': 0.11372549086809158, 'g': 0.30588236451148987, 'b': 0.8470588326454163, 'a': 1}};
--categories-color-blue-800: {'6:0': {'r': 0.11764705926179886, 'g': 0.250980406999588, 'b': 0.686274528503418, 'a': 1}};
--categories-color-blue-900: {'6:0': {'r': 0.11764705926179886, 'g': 0.22745098173618317, 'b': 0.5411764979362488, 'a': 1}};
--categories-color-blue-950: {'6:0': {'r': 0.09019608050584793, 'g': 0.14509804546833038, 'b': 0.3294117748737335, 'a': 1}};
--categories-color-white: {'6:0': {'r': 1, 'g': 1, 'b': 1, 'a': 1}};
--categories-color-color: {'6:0': {'r': 1, 'g': 1, 'b': 1, 'a': 1}};
--categories-color-black: {'6:0': {'r': 0, 'g': 0, 'b': 0, 'a': 1}};
--categories-color-slate-50: {'6:0': {'r': 0.9725490212440491, 'g': 0.9803921580314636, 'b': 0.9882352948188782, 'a': 1}};
--categories-color-slate-100: {'6:0': {'r': 0.9450980424880981, 'g': 0.9607843160629272, 'b': 0.9764705896377563, 'a': 1}};
--categories-color-slate-200: {'6:0': {'r': 0.886274516582489, 'g': 0.9098039269447327, 'b': 0.9411764740943909, 'a': 1}};
--categories-color-slate-300: {'6:0': {'r': 0.7960784435272217, 'g': 0.8352941274642944, 'b': 0.8823529481887817, 'a': 1}};
--categories-color-slate-400: {'6:0': {'r': 0.5803921818733215, 'g': 0.6392157077789307, 'b': 0.7215686440467834, 'a': 1}};
--categories-color-slate-500: {'6:0': {'r': 0.3921568691730499, 'g': 0.45490196347236633, 'b': 0.545098066329956, 'a': 1}};
--categories-color-slate-600: {'6:0': {'r': 0.27843138575553894, 'g': 0.3333333432674408, 'b': 0.4117647111415863, 'a': 1}};
--categories-color-slate-700: {'6:0': {'r': 0.20000000298023224, 'g': 0.2549019753932953, 'b': 0.3333333432674408, 'a': 1}};
--categories-color-slate-800: {'6:0': {'r': 0.11764705926179886, 'g': 0.16078431904315948, 'b': 0.23137255012989044, 'a': 1}};
--categories-color-slate-900: {'6:0': {'r': 0.05882352963089943, 'g': 0.09019608050584793, 'b': 0.16470588743686676, 'a': 1}};
--categories-color-slate-950: {'6:0': {'r': 0.007843137718737125, 'g': 0.0235294122248888, 'b': 0.09019608050584793, 'a': 1}};
--categories-color-gray-50: {'6:0': {'r': 0.9764705896377563, 'g': 0.9803921580314636, 'b': 0.9843137264251709, 'a': 1}};
--categories-color-gray-100: {'6:0': {'r': 0.9529411792755127, 'g': 0.95686274766922, 'b': 0.9647058844566345, 'a': 1}};
--categories-color-gray-200: {'6:0': {'r': 0.8980392217636108, 'g': 0.9058823585510254, 'b': 0.9215686321258545, 'a': 1}};
--categories-color-gray-300: {'6:0': {'r': 0.8196078538894653, 'g': 0.8352941274642944, 'b': 0.8588235378265381, 'a': 1}};
--categories-color-gray-400: {'6:0': {'r': 0.6117647290229797, 'g': 0.6392157077789307, 'b': 0.686274528503418, 'a': 1}};
--categories-color-gray-500: {'6:0': {'r': 0.41960784792900085, 'g': 0.4470588266849518, 'b': 0.501960813999176, 'a': 1}};
--categories-color-gray-600: {'6:0': {'r': 0.29411765933036804, 'g': 0.3333333432674408, 'b': 0.38823530077934265, 'a': 1}};
--categories-color-gray-700: {'6:0': {'r': 0.21568627655506134, 'g': 0.2549019753932953, 'b': 0.3176470696926117, 'a': 1}};
--categories-color-gray-800: {'6:0': {'r': 0.12156862765550613, 'g': 0.16078431904315948, 'b': 0.21568627655506134, 'a': 1}};
--categories-color-gray-900: {'6:0': {'r': 0.06666667014360428, 'g': 0.0941176488995552, 'b': 0.15294118225574493, 'a': 1}};
--categories-color-gray-950: {'6:0': {'r': 0.0117647061124444, 'g': 0.027450980618596077, 'b': 0.07058823853731155, 'a': 1}};
--categories-color-zinc-50: {'6:0': {'r': 0.9803921580314636, 'g': 0.9803921580314636, 'b': 0.9803921580314636, 'a': 1}};
--categories-color-zinc-100: {'6:0': {'r': 0.95686274766922, 'g': 0.95686274766922, 'b': 0.9607843160629272, 'a': 1}};
--categories-color-zinc-200: {'6:0': {'r': 0.8941176533699036, 'g': 0.8941176533699036, 'b': 0.9058823585510254, 'a': 1}};
--categories-color-zinc-300: {'6:0': {'r': 0.8313725590705872, 'g': 0.8313725590705872, 'b': 0.8470588326454163, 'a': 1}};
--categories-color-zinc-400: {'6:0': {'r': 0.6313725709915161, 'g': 0.6313725709915161, 'b': 0.6666666865348816, 'a': 1}};
--categories-color-zinc-500: {'6:0': {'r': 0.4431372582912445, 'g': 0.4431372582912445, 'b': 0.47843137383461, 'a': 1}};
--categories-color-zinc-600: {'6:0': {'r': 0.32156863808631897, 'g': 0.32156863808631897, 'b': 0.35686275362968445, 'a': 1}};
--categories-color-zinc-700: {'6:0': {'r': 0.24705882370471954, 'g': 0.24705882370471954, 'b': 0.27450981736183167, 'a': 1}};
--categories-color-zinc-800: {'6:0': {'r': 0.15294118225574493, 'g': 0.15294118225574493, 'b': 0.16470588743686676, 'a': 1}};
--categories-color-zinc-900: {'6:0': {'r': 0.0941176488995552, 'g': 0.0941176488995552, 'b': 0.10588235408067703, 'a': 1}};
--categories-color-zinc-950: {'6:0': {'r': 0.03529411926865578, 'g': 0.03529411926865578, 'b': 0.04313725605607033, 'a': 1}};
--categories-color-stone-50: {'6:0': {'r': 0.9803921580314636, 'g': 0.9803921580314636, 'b': 0.9764705896377563, 'a': 1}};
--categories-color-stone-100: {'6:0': {'r': 0.9607843160629272, 'g': 0.9607843160629272, 'b': 0.95686274766922, 'a': 1}};
--categories-color-stone-200: {'6:0': {'r': 0.9058823585510254, 'g': 0.8980392217636108, 'b': 0.8941176533699036, 'a': 1}};
--categories-color-stone-300: {'6:0': {'r': 0.8392156958580017, 'g': 0.8274509906768799, 'b': 0.8196078538894653, 'a': 1}};
--categories-color-stone-400: {'6:0': {'r': 0.658823549747467, 'g': 0.6352941393852234, 'b': 0.6196078658103943, 'a': 1}};
--categories-color-stone-500: {'6:0': {'r': 0.47058823704719543, 'g': 0.4431372582912445, 'b': 0.42352941632270813, 'a': 1}};
--categories-color-stone-600: {'6:0': {'r': 0.34117648005485535, 'g': 0.32549020648002625, 'b': 0.30588236451148987, 'a': 1}};
--categories-color-stone-700: {'6:0': {'r': 0.2666666805744171, 'g': 0.250980406999588, 'b': 0.23529411852359772, 'a': 1}};
--categories-color-stone-800: {'6:0': {'r': 0.16078431904315948, 'g': 0.14509804546833038, 'b': 0.1411764770746231, 'a': 1}};
--categories-color-stone-900: {'6:0': {'r': 0.10980392247438431, 'g': 0.09803921729326248, 'b': 0.09019608050584793, 'a': 1}};
--categories-color-stone-950: {'6:0': {'r': 0.0470588244497776, 'g': 0.03921568766236305, 'b': 0.03529411926865578, 'a': 1}};
--categories-color-orange-50: {'6:0': {'r': 1, 'g': 0.9686274528503418, 'b': 0.929411768913269, 'a': 1}};
--categories-color-orange-100: {'6:0': {'r': 1, 'g': 0.929411768913269, 'b': 0.8352941274642944, 'a': 1}};
--categories-color-orange-200: {'6:0': {'r': 0.9960784316062927, 'g': 0.843137264251709, 'b': 0.6666666865348816, 'a': 1}};
--categories-color-orange-300: {'6:0': {'r': 0.9921568632125854, 'g': 0.729411780834198, 'b': 0.45490196347236633, 'a': 1}};
--categories-color-orange-400: {'6:0': {'r': 0.9843137264251709, 'g': 0.572549045085907, 'b': 0.23529411852359772, 'a': 1}};
--categories-color-orange-500: {'6:0': {'r': 0.9764705896377563, 'g': 0.45098039507865906, 'b': 0.08627451211214066, 'a': 1}};
--categories-color-orange-600: {'6:0': {'r': 0.9176470637321472, 'g': 0.3450980484485626, 'b': 0.0470588244497776, 'a': 1}};
--categories-color-orange-700: {'6:0': {'r': 0.7607843279838562, 'g': 0.2549019753932953, 'b': 0.0470588244497776, 'a': 1}};
--categories-color-orange-800: {'6:0': {'r': 0.6039215922355652, 'g': 0.20392157137393951, 'b': 0.07058823853731155, 'a': 1}};
--categories-color-orange-900: {'6:0': {'r': 0.48627451062202454, 'g': 0.1764705926179886, 'b': 0.07058823853731155, 'a': 1}};
--categories-color-orange-950: {'6:0': {'r': 0.26274511218070984, 'g': 0.0784313753247261, 'b': 0.027450980618596077, 'a': 1}};
--categories-color-amber-50: {'6:0': {'r': 1, 'g': 0.9843137264251709, 'b': 0.9215686321258545, 'a': 1}};
--categories-color-amber-100: {'6:0': {'r': 0.9960784316062927, 'g': 0.9529411792755127, 'b': 0.7803921699523926, 'a': 1}};
--categories-color-amber-200: {'6:0': {'r': 0.9921568632125854, 'g': 0.9019607901573181, 'b': 0.5411764979362488, 'a': 1}};
--categories-color-amber-300: {'6:0': {'r': 0.9882352948188782, 'g': 0.8274509906768799, 'b': 0.3019607961177826, 'a': 1}};
--categories-color-amber-400: {'6:0': {'r': 0.9843137264251709, 'g': 0.7490196228027344, 'b': 0.1411764770746231, 'a': 1}};
--categories-color-amber-500: {'6:0': {'r': 0.9607843160629272, 'g': 0.6196078658103943, 'b': 0.04313725605607033, 'a': 1}};
--categories-color-amber-600: {'6:0': {'r': 0.8509804010391235, 'g': 0.46666666865348816, 'b': 0.0235294122248888, 'a': 1}};
--categories-color-amber-700: {'6:0': {'r': 0.7058823704719543, 'g': 0.32549020648002625, 'b': 0.03529411926865578, 'a': 1}};
--categories-color-amber-800: {'6:0': {'r': 0.572549045085907, 'g': 0.250980406999588, 'b': 0.054901961237192154, 'a': 1}};
--categories-color-amber-900: {'6:0': {'r': 0.47058823704719543, 'g': 0.2078431397676468, 'b': 0.05882352963089943, 'a': 1}};
--categories-color-amber-950: {'6:0': {'r': 0.2705882489681244, 'g': 0.10196078568696976, 'b': 0.0117647061124444, 'a': 1}};
--categories-color-lime-50: {'6:0': {'r': 0.9686274528503418, 'g': 0.9960784316062927, 'b': 0.9058823585510254, 'a': 1}};
--categories-color-lime-100: {'6:0': {'r': 0.9254902005195618, 'g': 0.9882352948188782, 'b': 0.7960784435272217, 'a': 1}};
--categories-color-lime-200: {'6:0': {'r': 0.8509804010391235, 'g': 0.9764705896377563, 'b': 0.615686297416687, 'a': 1}};
--categories-color-lime-300: {'6:0': {'r': 0.7450980544090271, 'g': 0.9490196108818054, 'b': 0.3921568691730499, 'a': 1}};
--categories-color-lime-400: {'6:0': {'r': 0.6392157077789307, 'g': 0.9019607901573181, 'b': 0.2078431397676468, 'a': 1}};
--categories-color-lime-500: {'6:0': {'r': 0.5176470875740051, 'g': 0.800000011920929, 'b': 0.08627451211214066, 'a': 1}};
--categories-color-lime-600: {'6:0': {'r': 0.3960784375667572, 'g': 0.6392157077789307, 'b': 0.05098039284348488, 'a': 1}};
--categories-color-lime-700: {'6:0': {'r': 0.3019607961177826, 'g': 0.48627451062202454, 'b': 0.05882352963089943, 'a': 1}};
--categories-color-lime-800: {'6:0': {'r': 0.24705882370471954, 'g': 0.3843137323856354, 'b': 0.07058823853731155, 'a': 1}};
--categories-color-lime-900: {'6:0': {'r': 0.21176470816135406, 'g': 0.32549020648002625, 'b': 0.0784313753247261, 'a': 1}};
--categories-color-lime-950: {'6:0': {'r': 0.10196078568696976, 'g': 0.18039216101169586, 'b': 0.019607843831181526, 'a': 1}};
--categories-color-emerald-50: {'6:0': {'r': 0.9254902005195618, 'g': 0.9921568632125854, 'b': 0.9607843160629272, 'a': 1}};
--categories-color-emerald-100: {'6:0': {'r': 0.8196078538894653, 'g': 0.9803921580314636, 'b': 0.8980392217636108, 'a': 1}};
--categories-color-emerald-200: {'6:0': {'r': 0.6549019813537598, 'g': 0.9529411792755127, 'b': 0.8156862854957581, 'a': 1}};
--categories-color-emerald-300: {'6:0': {'r': 0.4313725531101227, 'g': 0.9058823585510254, 'b': 0.7176470756530762, 'a': 1}};
--categories-color-emerald-400: {'6:0': {'r': 0.20392157137393951, 'g': 0.8274509906768799, 'b': 0.6000000238418579, 'a': 1}};
--categories-color-emerald-500: {'6:0': {'r': 0.062745101749897, 'g': 0.7254902124404907, 'b': 0.5058823823928833, 'a': 1}};
--categories-color-emerald-600: {'6:0': {'r': 0.019607843831181526, 'g': 0.5882353186607361, 'b': 0.4117647111415863, 'a': 1}};
--categories-color-emerald-700: {'6:0': {'r': 0.01568627543747425, 'g': 0.47058823704719543, 'b': 0.34117648005485535, 'a': 1}};
--categories-color-emerald-800: {'6:0': {'r': 0.0235294122248888, 'g': 0.37254902720451355, 'b': 0.27450981736183167, 'a': 1}};
--categories-color-emerald-900: {'6:0': {'r': 0.0235294122248888, 'g': 0.30588236451148987, 'b': 0.23137255012989044, 'a': 1}};
--categories-color-emerald-950: {'6:0': {'r': 0.007843137718737125, 'g': 0.1725490242242813, 'b': 0.13333334028720856, 'a': 1}};
--categories-color-teal-50: {'6:0': {'r': 0.9411764740943909, 'g': 0.9921568632125854, 'b': 0.9803921580314636, 'a': 1}};
--categories-color-teal-100: {'6:0': {'r': 0.800000011920929, 'g': 0.9843137264251709, 'b': 0.9450980424880981, 'a': 1}};
--categories-color-teal-200: {'6:0': {'r': 0.6000000238418579, 'g': 0.9647058844566345, 'b': 0.8941176533699036, 'a': 1}};
--categories-color-teal-300: {'6:0': {'r': 0.3686274588108063, 'g': 0.9176470637321472, 'b': 0.8313725590705872, 'a': 1}};
--categories-color-teal-400: {'6:0': {'r': 0.1764705926179886, 'g': 0.8313725590705872, 'b': 0.7490196228027344, 'a': 1}};
--categories-color-teal-500: {'6:0': {'r': 0.0784313753247261, 'g': 0.7215686440467834, 'b': 0.6509804129600525, 'a': 1}};
--categories-color-teal-600: {'6:0': {'r': 0.05098039284348488, 'g': 0.5803921818733215, 'b': 0.5333333611488342, 'a': 1}};
--categories-color-teal-700: {'6:0': {'r': 0.05882352963089943, 'g': 0.4627451002597809, 'b': 0.4313725531101227, 'a': 1}};
--categories-color-teal-800: {'6:0': {'r': 0.06666667014360428, 'g': 0.3686274588108063, 'b': 0.3490196168422699, 'a': 1}};
--categories-color-teal-900: {'6:0': {'r': 0.07450980693101883, 'g': 0.30588236451148987, 'b': 0.29019609093666077, 'a': 1}};
--categories-color-teal-950: {'6:0': {'r': 0.01568627543747425, 'g': 0.18431372940540314, 'b': 0.18039216101169586, 'a': 1}};
--categories-color-cyan-50: {'6:0': {'r': 0.9254902005195618, 'g': 0.9960784316062927, 'b': 1, 'a': 1}};
--categories-color-cyan-100: {'6:0': {'r': 0.8117647171020508, 'g': 0.9803921580314636, 'b': 0.9960784316062927, 'a': 1}};
--categories-color-cyan-200: {'6:0': {'r': 0.6470588445663452, 'g': 0.9529411792755127, 'b': 0.9882352948188782, 'a': 1}};
--categories-color-cyan-300: {'6:0': {'r': 0.40392157435417175, 'g': 0.9098039269447327, 'b': 0.9764705896377563, 'a': 1}};
--categories-color-cyan-400: {'6:0': {'r': 0.13333334028720856, 'g': 0.8274509906768799, 'b': 0.9333333373069763, 'a': 1}};
--categories-color-cyan-500: {'6:0': {'r': 0.0235294122248888, 'g': 0.7137255072593689, 'b': 0.8313725590705872, 'a': 1}};
--categories-color-cyan-600: {'6:0': {'r': 0.0313725508749485, 'g': 0.5686274766921997, 'b': 0.6980392336845398, 'a': 1}};
--categories-color-cyan-700: {'6:0': {'r': 0.054901961237192154, 'g': 0.45490196347236633, 'b': 0.5647059082984924, 'a': 1}};
--categories-color-cyan-800: {'6:0': {'r': 0.08235294371843338, 'g': 0.3686274588108063, 'b': 0.4588235318660736, 'a': 1}};
--categories-color-cyan-900: {'6:0': {'r': 0.08627451211214066, 'g': 0.30588236451148987, 'b': 0.38823530077934265, 'a': 1}};
--categories-color-cyan-950: {'6:0': {'r': 0.0313725508749485, 'g': 0.20000000298023224, 'b': 0.2666666805744171, 'a': 1}};
--categories-color-sky-50: {'6:0': {'r': 0.9411764740943909, 'g': 0.9764705896377563, 'b': 1, 'a': 1}};
--categories-color-sky-100: {'6:0': {'r': 0.8784313797950745, 'g': 0.9490196108818054, 'b': 0.9960784316062927, 'a': 1}};
--categories-color-sky-200: {'6:0': {'r': 0.729411780834198, 'g': 0.9019607901573181, 'b': 0.9921568632125854, 'a': 1}};
--categories-color-sky-300: {'6:0': {'r': 0.4901960790157318, 'g': 0.8274509906768799, 'b': 0.9882352948188782, 'a': 1}};
--categories-color-sky-400: {'6:0': {'r': 0.21960784494876862, 'g': 0.7411764860153198, 'b': 0.9725490212440491, 'a': 1}};
--categories-color-sky-500: {'6:0': {'r': 0.054901961237192154, 'g': 0.6470588445663452, 'b': 0.9137254953384399, 'a': 1}};
--categories-color-sky-600: {'6:0': {'r': 0.007843137718737125, 'g': 0.5176470875740051, 'b': 0.7803921699523926, 'a': 1}};
--categories-color-sky-700: {'6:0': {'r': 0.0117647061124444, 'g': 0.4117647111415863, 'b': 0.6313725709915161, 'a': 1}};
--categories-color-sky-800: {'6:0': {'r': 0.027450980618596077, 'g': 0.3490196168422699, 'b': 0.5215686559677124, 'a': 1}};
--categories-color-sky-900: {'6:0': {'r': 0.0470588244497776, 'g': 0.29019609093666077, 'b': 0.4313725531101227, 'a': 1}};
--categories-color-sky-950: {'6:0': {'r': 0.0313725508749485, 'g': 0.18431372940540314, 'b': 0.2862745225429535, 'a': 1}};
--categories-color-indigo-50: {'6:0': {'r': 0.9333333373069763, 'g': 0.9490196108818054, 'b': 1, 'a': 1}};
--categories-color-indigo-100: {'6:0': {'r': 0.8784313797950745, 'g': 0.9058823585510254, 'b': 1, 'a': 1}};
--categories-color-indigo-200: {'6:0': {'r': 0.7803921699523926, 'g': 0.8235294222831726, 'b': 0.9960784316062927, 'a': 1}};
--categories-color-indigo-300: {'6:0': {'r': 0.6470588445663452, 'g': 0.7058823704719543, 'b': 0.9882352948188782, 'a': 1}};
--categories-color-indigo-400: {'6:0': {'r': 0.5058823823928833, 'g': 0.5490196347236633, 'b': 0.9725490212440491, 'a': 1}};
--categories-color-indigo-500: {'6:0': {'r': 0.38823530077934265, 'g': 0.4000000059604645, 'b': 0.9450980424880981, 'a': 1}};
--categories-color-indigo-600: {'6:0': {'r': 0.30980393290519714, 'g': 0.27450981736183167, 'b': 0.8980392217636108, 'a': 1}};
--categories-color-indigo-700: {'6:0': {'r': 0.26274511218070984, 'g': 0.21960784494876862, 'b': 0.7921568751335144, 'a': 1}};
--categories-color-indigo-800: {'6:0': {'r': 0.21568627655506134, 'g': 0.1882352977991104, 'b': 0.6392157077789307, 'a': 1}};
--categories-color-indigo-900: {'6:0': {'r': 0.1921568661928177, 'g': 0.18039216101169586, 'b': 0.5058823823928833, 'a': 1}};
--categories-color-indigo-950: {'6:0': {'r': 0.11764705926179886, 'g': 0.10588235408067703, 'b': 0.29411765933036804, 'a': 1}};
--categories-color-violet-50: {'6:0': {'r': 0.9607843160629272, 'g': 0.9529411792755127, 'b': 1, 'a': 1}};
--categories-color-violet-100: {'6:0': {'r': 0.929411768913269, 'g': 0.9137254953384399, 'b': 0.9960784316062927, 'a': 1}};
--categories-color-violet-200: {'6:0': {'r': 0.8666666746139526, 'g': 0.8392156958580017, 'b': 0.9960784316062927, 'a': 1}};
--categories-color-violet-300: {'6:0': {'r': 0.7686274647712708, 'g': 0.7098039388656616, 'b': 0.9921568632125854, 'a': 1}};
--categories-color-violet-400: {'6:0': {'r': 0.6549019813537598, 'g': 0.545098066329956, 'b': 0.9803921580314636, 'a': 1}};
--categories-color-violet-500: {'6:0': {'r': 0.545098066329956, 'g': 0.3607843220233917, 'b': 0.9647058844566345, 'a': 1}};
--categories-color-violet-600: {'6:0': {'r': 0.48627451062202454, 'g': 0.22745098173618317, 'b': 0.929411768913269, 'a': 1}};
--categories-color-violet-700: {'6:0': {'r': 0.4274509847164154, 'g': 0.1568627506494522, 'b': 0.8509804010391235, 'a': 1}};
--categories-color-violet-800: {'6:0': {'r': 0.35686275362968445, 'g': 0.12941177189350128, 'b': 0.7137255072593689, 'a': 1}};
--categories-color-violet-900: {'6:0': {'r': 0.2980392277240753, 'g': 0.11372549086809158, 'b': 0.5843137502670288, 'a': 1}};
--categories-color-violet-950: {'6:0': {'r': 0.18039216101169586, 'g': 0.062745101749897, 'b': 0.3960784375667572, 'a': 1}};
--categories-color-purple-50: {'6:0': {'r': 0.9803921580314636, 'g': 0.9607843160629272, 'b': 1, 'a': 1}};
--categories-color-purple-100: {'6:0': {'r': 0.9529411792755127, 'g': 0.9098039269447327, 'b': 1, 'a': 1}};
--categories-color-purple-200: {'6:0': {'r': 0.9137254953384399, 'g': 0.8352941274642944, 'b': 1, 'a': 1}};
--categories-color-purple-300: {'6:0': {'r': 0.8470588326454163, 'g': 0.7058823704719543, 'b': 0.9960784316062927, 'a': 1}};
--categories-color-purple-400: {'6:0': {'r': 0.7529411911964417, 'g': 0.5176470875740051, 'b': 0.9882352948188782, 'a': 1}};
--categories-color-purple-500: {'6:0': {'r': 0.658823549747467, 'g': 0.3333333432674408, 'b': 0.9686274528503418, 'a': 1}};
--categories-color-purple-600: {'6:0': {'r': 0.5764706134796143, 'g': 0.20000000298023224, 'b': 0.9176470637321472, 'a': 1}};
--categories-color-purple-700: {'6:0': {'r': 0.4941176474094391, 'g': 0.13333334028720856, 'b': 0.8078431487083435, 'a': 1}};
--categories-color-purple-800: {'6:0': {'r': 0.41960784792900085, 'g': 0.12941177189350128, 'b': 0.658823549747467, 'a': 1}};
--categories-color-purple-900: {'6:0': {'r': 0.3450980484485626, 'g': 0.10980392247438431, 'b': 0.529411792755127, 'a': 1}};
--categories-color-purple-950: {'6:0': {'r': 0.23137255012989044, 'g': 0.027450980618596077, 'b': 0.3921568691730499, 'a': 1}};
--categories-color-fuchsia-50: {'6:0': {'r': 0.9921568632125854, 'g': 0.95686274766922, 'b': 1, 'a': 1}};
--categories-color-fuchsia-100: {'6:0': {'r': 0.9803921580314636, 'g': 0.9098039269447327, 'b': 1, 'a': 1}};
--categories-color-fuchsia-200: {'6:0': {'r': 0.9607843160629272, 'g': 0.8156862854957581, 'b': 0.9960784316062927, 'a': 1}};
--categories-color-fuchsia-300: {'6:0': {'r': 0.9411764740943909, 'g': 0.6705882549285889, 'b': 0.9882352948188782, 'a': 1}};
--categories-color-fuchsia-400: {'6:0': {'r': 0.9098039269447327, 'g': 0.4745098054409027, 'b': 0.9764705896377563, 'a': 1}};
--categories-color-fuchsia-500: {'6:0': {'r': 0.8509804010391235, 'g': 0.27450981736183167, 'b': 0.9372549057006836, 'a': 1}};
--categories-color-fuchsia-600: {'6:0': {'r': 0.7529411911964417, 'g': 0.14901961386203766, 'b': 0.8274509906768799, 'a': 1}};
--categories-color-fuchsia-700: {'6:0': {'r': 0.6352941393852234, 'g': 0.10980392247438431, 'b': 0.686274528503418, 'a': 1}};
--categories-color-fuchsia-800: {'6:0': {'r': 0.5254902243614197, 'g': 0.09803921729326248, 'b': 0.5607843399047852, 'a': 1}};
--categories-color-fuchsia-900: {'6:0': {'r': 0.43921568989753723, 'g': 0.10196078568696976, 'b': 0.4588235318660736, 'a': 1}};
--categories-color-fuchsia-950: {'6:0': {'r': 0.29019609093666077, 'g': 0.01568627543747425, 'b': 0.30588236451148987, 'a': 1}};
--categories-color-pink-50: {'6:0': {'r': 0.9921568632125854, 'g': 0.9490196108818054, 'b': 0.9725490212440491, 'a': 1}};
--categories-color-pink-100: {'6:0': {'r': 0.9882352948188782, 'g': 0.9058823585510254, 'b': 0.9529411792755127, 'a': 1}};
--categories-color-pink-200: {'6:0': {'r': 0.9843137264251709, 'g': 0.8117647171020508, 'b': 0.9098039269447327, 'a': 1}};
--categories-color-pink-300: {'6:0': {'r': 0.9764705896377563, 'g': 0.658823549747467, 'b': 0.8313725590705872, 'a': 1}};
--categories-color-pink-400: {'6:0': {'r': 0.95686274766922, 'g': 0.4470588266849518, 'b': 0.7137255072593689, 'a': 1}};
--categories-color-pink-500: {'6:0': {'r': 0.9254902005195618, 'g': 0.2823529541492462, 'b': 0.6000000238418579, 'a': 1}};
--categories-color-pink-600: {'6:0': {'r': 0.8588235378265381, 'g': 0.15294118225574493, 'b': 0.46666666865348816, 'a': 1}};
--categories-color-pink-700: {'6:0': {'r': 0.7450980544090271, 'g': 0.0941176488995552, 'b': 0.364705890417099, 'a': 1}};
--categories-color-pink-800: {'6:0': {'r': 0.615686297416687, 'g': 0.09019608050584793, 'b': 0.3019607961177826, 'a': 1}};
--categories-color-pink-900: {'6:0': {'r': 0.5137255191802979, 'g': 0.0941176488995552, 'b': 0.26274511218070984, 'a': 1}};
--categories-color-pink-950: {'6:0': {'r': 0.3137255012989044, 'g': 0.027450980618596077, 'b': 0.1411764770746231, 'a': 1}};
--categories-color-rose-50: {'6:0': {'r': 1, 'g': 0.9450980424880981, 'b': 0.9490196108818054, 'a': 1}};
--categories-color-rose-100: {'6:0': {'r': 1, 'g': 0.8941176533699036, 'b': 0.9019607901573181, 'a': 1}};
--categories-color-rose-200: {'6:0': {'r': 0.9960784316062927, 'g': 0.8039215803146362, 'b': 0.8274509906768799, 'a': 1}};
--categories-color-rose-300: {'6:0': {'r': 0.9921568632125854, 'g': 0.6431372761726379, 'b': 0.686274528503418, 'a': 1}};
--categories-color-rose-400: {'6:0': {'r': 0.9843137264251709, 'g': 0.4431372582912445, 'b': 0.5215686559677124, 'a': 1}};
--categories-color-rose-500: {'6:0': {'r': 0.95686274766922, 'g': 0.24705882370471954, 'b': 0.3686274588108063, 'a': 1}};
--categories-color-rose-600: {'6:0': {'r': 0.8823529481887817, 'g': 0.11372549086809158, 'b': 0.2823529541492462, 'a': 1}};
--categories-color-rose-700: {'6:0': {'r': 0.7450980544090271, 'g': 0.07058823853731155, 'b': 0.23529411852359772, 'a': 1}};
--categories-color-rose-800: {'6:0': {'r': 0.6235294342041016, 'g': 0.07058823853731155, 'b': 0.2235294133424759, 'a': 1}};
--categories-color-rose-900: {'6:0': {'r': 0.5333333611488342, 'g': 0.07450980693101883, 'b': 0.21568627655506134, 'a': 1}};
--categories-color-rose-950: {'6:0': {'r': 0.2980392277240753, 'g': 0.019607843831181526, 'b': 0.09803921729326248, 'a': 1}};
--categories-color-green-50: {'6:0': {'r': 0.9411764740943909, 'g': 0.9921568632125854, 'b': 0.95686274766922, 'a': 1}};
--categories-color-green-100: {'6:0': {'r': 0.8627451062202454, 'g': 0.9882352948188782, 'b': 0.9058823585510254, 'a': 1}};
--categories-color-green-200: {'6:0': {'r': 0.7333333492279053, 'g': 0.9686274528503418, 'b': 0.8156862854957581, 'a': 1}};
--categories-color-green-300: {'6:0': {'r': 0.5254902243614197, 'g': 0.9372549057006836, 'b': 0.6745098233222961, 'a': 1}};
--categories-color-green-400: {'6:0': {'r': 0.29019609093666077, 'g': 0.8705882430076599, 'b': 0.501960813999176, 'a': 1}};
--categories-color-green-500: {'6:0': {'r': 0.13333334028720856, 'g': 0.772549033164978, 'b': 0.3686274588108063, 'a': 1}};
--categories-color-green-600: {'6:0': {'r': 0.08627451211214066, 'g': 0.6392157077789307, 'b': 0.29019609093666077, 'a': 1}};
--categories-color-green-700: {'6:0': {'r': 0.08235294371843338, 'g': 0.501960813999176, 'b': 0.239215686917305, 'a': 1}};
--categories-color-green-800: {'6:0': {'r': 0.08627451211214066, 'g': 0.3960784375667572, 'b': 0.20392157137393951, 'a': 1}};
--categories-color-green-900: {'6:0': {'r': 0.0784313753247261, 'g': 0.32549020648002625, 'b': 0.1764705926179886, 'a': 1}};
--categories-color-green-950: {'6:0': {'r': 0.019607843831181526, 'g': 0.18039216101169586, 'b': 0.08627451211214066, 'a': 1}};
--categories-color-yellow-50: {'6:0': {'r': 0.9960784316062927, 'g': 0.9882352948188782, 'b': 0.9098039269447327, 'a': 1}};
--categories-color-yellow-100: {'6:0': {'r': 0.9960784316062927, 'g': 0.9764705896377563, 'b': 0.7647058963775635, 'a': 1}};
--categories-color-yellow-200: {'6:0': {'r': 0.9960784316062927, 'g': 0.9411764740943909, 'b': 0.5411764979362488, 'a': 1}};
--categories-color-yellow-300: {'6:0': {'r': 0.9921568632125854, 'g': 0.8784313797950745, 'b': 0.27843138575553894, 'a': 1}};
--categories-color-yellow-400: {'6:0': {'r': 0.9803921580314636, 'g': 0.800000011920929, 'b': 0.08235294371843338, 'a': 1}};
--categories-color-yellow-500: {'6:0': {'r': 0.9176470637321472, 'g': 0.7019608020782471, 'b': 0.0313725508749485, 'a': 1}};
--categories-color-yellow-600: {'6:0': {'r': 0.7921568751335144, 'g': 0.5411764979362488, 'b': 0.01568627543747425, 'a': 1}};
--categories-color-yellow-700: {'6:0': {'r': 0.6313725709915161, 'g': 0.3843137323856354, 'b': 0.027450980618596077, 'a': 1}};
--categories-color-yellow-800: {'6:0': {'r': 0.5215686559677124, 'g': 0.3019607961177826, 'b': 0.054901961237192154, 'a': 1}};
--categories-color-yellow-900: {'6:0': {'r': 0.4431372582912445, 'g': 0.24705882370471954, 'b': 0.07058823853731155, 'a': 1}};
--categories-color-yellow-950: {'6:0': {'r': 0.25882354378700256, 'g': 0.125490203499794, 'b': 0.0235294122248888, 'a': 1}};
--categories-color-general-background: {'618:0': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:55:5102'}, '618:1': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:55:5103'}, '847:4': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:55:5102'}};
--categories-color-general-foreground: {'618:0': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:6:111'}, '618:1': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:6:101'}, '847:4': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:534:30321'}};
--categories-color-general-primary: {'618:0': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:6:110'}, '618:1': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:6:102'}, '847:4': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:534:30320'}};
--categories-color-general-primary-foreground: {'618:0': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:6:101'}, '618:1': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:6:111'}, '847:4': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:534:30311'}};
--categories-color-general-secondary: {'618:0': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:6:102'}, '618:1': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:6:109'}, '847:4': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:534:30312'}};
--categories-color-general-secondary-foreground: {'618:0': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:6:110'}, '618:1': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:6:102'}, '847:4': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:534:30320'}};
--categories-color-general-accent: {'618:0': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:6:102'}, '618:1': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:6:110'}, '847:4': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:534:30312'}};
--categories-color-general-accent-foreground: {'618:0': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:6:110'}, '618:1': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:6:102'}, '847:4': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:534:30320'}};
--categories-color-general-muted: {'618:0': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:6:102'}, '618:1': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:6:110'}, '847:4': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:534:30312'}};
--categories-color-general-muted-foreground: {'618:0': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:6:106'}, '618:1': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:6:105'}, '847:4': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:534:30316'}};
--categories-color-general-destructive: {'618:0': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:16:1997'}, '618:1': {'r': 0.6196078658103943, 'g': 0.250980406999588, 'b': 0.25882354378700256, 'a': 1}, '847:4': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:16:1997'}};
--categories-color-general-border: {'618:0': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:6:103'}, '618:1': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:6:108'}, '847:4': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:534:30313'}};
--categories-color-general-input: {'618:0': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:55:5102'}, '618:1': {'r': 1, 'g': 1, 'b': 1, 'a': 0.05000000074505806}, '847:4': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:55:5102'}};
--categories-color-card-card: {'618:0': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:55:5102'}, '618:1': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:6:110'}, '847:4': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:55:5102'}};
--categories-color-card-card-foreground: {'618:0': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:6:111'}, '618:1': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:55:5102'}, '847:4': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:534:30321'}};
--categories-color-popover-popover: {'618:0': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:55:5103'}, '618:1': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:55:5102'}, '847:4': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:55:5103'}};
--categories-color-popover-popover-foreground: {'618:0': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:55:5102'}, '618:1': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:55:5103'}, '847:4': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:55:5102'}};
--categories-color-unofficial-foreground-alt: {'618:0': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:6:108'}, '618:1': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:6:104'}, '847:4': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:534:30318'}};
--categories-color-unofficial-body-background: {'618:0': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:55:5102'}, '618:1': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:6:111'}, '847:4': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:55:5102'}};
--categories-color-unofficial-destructive-border: {'618:0': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:16:1996'}, '618:1': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:16:1996'}, '847:4': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:16:1996'}};
--categories-color-unofficial-destructive-subtle: {'618:0': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:16:1991'}, '618:1': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:16:2001'}, '847:4': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:16:1991'}};
--categories-color-unofficial-contrast-(deprecated): {'618:0': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:55:5103'}, '618:1': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:55:5102'}, '847:4': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:55:5103'}};
--categories-color-unofficial-backdrop: {'618:0': {'r': 0, 'g': 0, 'b': 0, 'a': 0.6000000238418579}, '618:1': {'r': 0, 'g': 0, 'b': 0, 'a': 0.6000000238418579}, '847:4': {'r': 0.20000000298023224, 'g': 0.2549019753932953, 'b': 0.3333333432674408, 'a': 0.6000000238418579}};
--categories-color-unofficial-mid-(deprecated): {'618:0': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:6:106'}, '618:1': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:6:106'}, '847:4': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:534:30316'}};
--categories-color-unofficial-mid-alt: {'618:0': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:6:107'}, '618:1': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:6:105'}, '847:4': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:534:30317'}};
--categories-color-unofficial-destructive-foreground: {'618:0': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:16:1997'}, '618:1': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:16:1995'}, '847:4': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:16:1997'}};
--categories-color-unofficial-ghost-foreground: {'618:0': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:6:108'}, '618:1': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:6:103'}, '847:4': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:534:30318'}};
--categories-color-unofficial-ghost: {'618:0': {'r': 1, 'g': 1, 'b': 1, 'a': 9.999999747378752e-05}, '618:1': {'r': 1, 'g': 1, 'b': 1, 'a': 9.999999747378752e-05}, '847:4': {'r': 1, 'g': 1, 'b': 1, 'a': 9.999999747378752e-05}};
--categories-color-unofficial-ghost-hover: {'618:0': {'r': 0, 'g': 0, 'b': 0, 'a': 0.05000000074505806}, '618:1': {'r': 1, 'g': 1, 'b': 1, 'a': 0.10000000149011612}, '847:4': {'r': 0, 'g': 0, 'b': 0, 'a': 0.05000000074505806}};
--categories-color-unofficial-primary-hover: {'618:0': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:6:108'}, '618:1': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:6:104'}, '847:4': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:534:30318'}};
--categories-color-unofficial-secondary-hover: {'618:0': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:6:101'}, '618:1': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:6:110'}, '847:4': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:534:30311'}};
--categories-color-unofficial-outline: {'618:0': {'r': 1, 'g': 1, 'b': 1, 'a': 0.10000000149011612}, '618:1': {'r': 1, 'g': 1, 'b': 1, 'a': 0.05000000074505806}, '847:4': {'r': 1, 'g': 1, 'b': 1, 'a': 0.10000000149011612}};
--categories-color-unofficial-outline-hover: {'618:0': {'r': 0, 'g': 0, 'b': 0, 'a': 0.0333000011742115}, '618:1': {'r': 1, 'g': 1, 'b': 1, 'a': 0.10000000149011612}, '847:4': {'r': 0.20000000298023224, 'g': 0.2549019753932953, 'b': 0.3333333432674408, 'a': 0.0333000011742115}};
--categories-color-unofficial-outline-active: {'618:0': {'r': 0, 'g': 0, 'b': 0, 'a': 0.05000000074505806}, '618:1': {'r': 1, 'g': 1, 'b': 1, 'a': 0.15000000596046448}, '847:4': {'r': 0.20000000298023224, 'g': 0.2549019753932953, 'b': 0.3333333432674408, 'a': 0.05000000074505806}};
--categories-color-unofficial-accent-0: {'618:0': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:6:101'}, '618:1': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:6:111'}, '847:4': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:534:30311'}};
--categories-color-unofficial-accent-2: {'618:0': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:6:103'}, '618:1': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:6:109'}, '847:4': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:534:30313'}};
--categories-color-unofficial-accent-3: {'618:0': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:6:104'}, '618:1': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:6:108'}, '847:4': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:534:30314'}};
--categories-color-unofficial-border-0: {'618:0': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:6:101'}, '618:1': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:6:111'}, '847:4': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:534:30311'}};
--categories-color-unofficial-border-1: {'618:0': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:6:102'}, '618:1': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:6:110'}, '847:4': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:534:30312'}};
--categories-color-unofficial-border-3: {'618:0': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:6:104'}, '618:1': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:6:108'}, '847:4': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:534:30314'}};
--categories-color-unofficial-border-4: {'618:0': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:6:105'}, '618:1': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:6:107'}, '847:4': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:534:30315'}};
--categories-color-unofficial-border-5: {'618:0': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:6:106'}, '618:1': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:6:106'}, '847:4': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:534:30316'}};
--categories-color-focus-ring: {'618:0': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:6:104'}, '618:1': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:6:108'}, '847:4': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:534:30314'}};
--categories-color-sidebar-sidebar: {'618:0': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:6:101'}, '618:1': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:6:111'}, '847:4': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:534:30312'}};
--categories-color-sidebar-sidebar-foreground: {'618:0': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:6:108'}, '618:1': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:6:104'}, '847:4': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:534:30318'}};
--categories-color-sidebar-sidebar-accent: {'618:0': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:6:102'}, '618:1': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:6:110'}, '847:4': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:534:30313'}};
--categories-color-sidebar-sidebar-accent-foreground: {'618:0': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:6:110'}, '618:1': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:6:102'}, '847:4': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:534:30318'}};
--categories-color-sidebar-sidebar-primary: {'618:0': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:6:110'}, '618:1': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:6:101'}, '847:4': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:534:30320'}};
--categories-color-sidebar-sidebar-primary-foreground: {'618:0': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:6:101'}, '618:1': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:6:110'}, '847:4': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:534:30311'}};
--categories-color-sidebar-sidebar-border: {'618:0': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:6:103'}, '618:1': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:6:109'}, '847:4': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:534:30313'}};
--categories-color-sidebar-sidebar-ring: {'618:0': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:6:104'}, '618:1': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:6:108'}, '847:4': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:534:30314'}};
--categories-color-sidebar-unofficial-sidebar-muted: {'618:0': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:6:106'}, '618:1': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:6:106'}, '847:4': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:534:30316'}};
--categories-color-focus-ring-error: {'618:0': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:16:1994'}, '618:1': {'r': 0.4274509847164154, 'g': 0.18039216101169586, 'b': 0.18431372940540314, 'a': 1}, '847:4': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:16:1994'}};
--categories-color-chart-legacy-chart-1: {'618:0': {'r': 0.9607843160629272, 'g': 0.29019609093666077, 'b': 0, 'a': 1}, '618:1': {'r': 0.0784313753247261, 'g': 0.27843138575553894, 'b': 0.9019607901573181, 'a': 1}, '847:4': {'r': 0.12156862765550613, 'g': 0.46666666865348816, 'b': 0.7058823704719543, 'a': 1}};
--categories-color-chart-legacy-chart-2: {'618:0': {'r': 0, 'g': 0.5882353186607361, 'b': 0.5372549295425415, 'a': 1}, '618:1': {'r': 0, 'g': 0.7372549176216125, 'b': 0.4901960790157318, 'a': 1}, '847:4': {'r': 1, 'g': 0.49803921580314636, 'b': 0.054901961237192154, 'a': 1}};
--categories-color-chart-legacy-chart-3: {'618:0': {'r': 0.062745101749897, 'g': 0.30588236451148987, 'b': 0.3921568691730499, 'a': 1}, '618:1': {'r': 0.9921568632125854, 'g': 0.6039215922355652, 'b': 0, 'a': 1}, '847:4': {'r': 0.1725490242242813, 'g': 0.6274510025978088, 'b': 0.1725490242242813, 'a': 1}};
--categories-color-chart-legacy-chart-4: {'618:0': {'r': 1, 'g': 0.7254902124404907, 'b': 0, 'a': 1}, '618:1': {'r': 0.6784313917160034, 'g': 0.27450981736183167, 'b': 1, 'a': 1}, '847:4': {'r': 0.8392156958580017, 'g': 0.15294118225574493, 'b': 0.1568627506494522, 'a': 1}};
--categories-color-chart-legacy-chart-5: {'618:0': {'r': 0.9960784316062927, 'g': 0.6039215922355652, 'b': 0, 'a': 1}, '618:1': {'r': 1, 'g': 0.125490203499794, 'b': 0.33725491166114807, 'a': 1}, '847:4': {'r': 0.5803921818733215, 'g': 0.40392157435417175, 'b': 0.7411764860153198, 'a': 1}};
--categories-color-chart-area-orange-fill: {'618:0': {'r': 0.9920479655265808, 'g': 0.8146340250968933, 'b': 0.6118749976158142, 'a': 0.699999988079071}, '618:1': {'r': 0.4588235318660736, 'g': 0.34117648005485535, 'b': 0.21960784494876862, 'a': 0.699999988079071}, '847:4': {'r': 0.9920479655265808, 'g': 0.8146340250968933, 'b': 0.6118749976158142, 'a': 0.699999988079071}};
--categories-color-chart-area-orange-fill-2: {'618:0': {'r': 0.9708533883094788, 'g': 0.690407395362854, 'b': 0.49409517645835876, 'a': 0.699999988079071}, '618:1': {'r': 0.4627451002597809, 'g': 0.21960784494876862, 'b': 0.054901961237192154, 'a': 0.699999988079071}, '847:4': {'r': 0.9708533883094788, 'g': 0.690407395362854, 'b': 0.49409517645835876, 'a': 0.699999988079071}};
--categories-color-chart-area-orange-stroke: {'618:0': {'r': 1, 'g': 0.7215686440467834, 'b': 0.4156862795352936, 'a': 1}, '618:1': {'r': 1, 'g': 0.7215686440467834, 'b': 0.4156862795352936, 'a': 1}, '847:4': {'r': 1, 'g': 0.7215686440467834, 'b': 0.4156862795352936, 'a': 1}};
--categories-color-chart-area-orange-stroke-2: {'618:0': {'r': 1, 'g': 0.4117647111415863, 'b': 0, 'a': 1}, '618:1': {'r': 1, 'g': 0.4745098054409027, 'b': 0.08235294371843338, 'a': 1}, '847:4': {'r': 1, 'g': 0.4117647111415863, 'b': 0, 'a': 1}};
--categories-color-chart-area-blue-fill: {'618:0': {'r': 0.7496619820594788, 'g': 0.8687251806259155, 'b': 1, 'a': 0.699999988079071}, '618:1': {'r': 0.27843138575553894, 'g': 0.364705890417099, 'b': 0.4588235318660736, 'a': 0.699999988079071}, '847:4': {'r': 0.7496619820594788, 'g': 0.8687251806259155, 'b': 1, 'a': 0.699999988079071}};
--categories-color-chart-area-blue-stroke: {'618:0': {'r': 0.5568627715110779, 'g': 0.772549033164978, 'b': 1, 'a': 1}, '618:1': {'r': 0.5568627715110779, 'g': 0.772549033164978, 'b': 1, 'a': 1}, '847:4': {'r': 0.5568627715110779, 'g': 0.772549033164978, 'b': 1, 'a': 1}};
--categories-color-chart-area-blue-fill-2: {'618:0': {'r': 0.6666666865348816, 'g': 0.800000011920929, 'b': 1, 'a': 0.699999988079071}, '618:1': {'r': 0.12156862765550613, 'g': 0.2549019753932953, 'b': 0.4627451002597809, 'a': 0.699999988079071}, '847:4': {'r': 0.6666666865348816, 'g': 0.800000011920929, 'b': 1, 'a': 0.699999988079071}};
--categories-color-chart-area-blue-stroke-2: {'618:0': {'r': 0.24705882370471954, 'g': 0.5529412031173706, 'b': 1, 'a': 1}, '618:1': {'r': 0.32549020648002625, 'g': 0.6078431606292725, 'b': 1, 'a': 1}, '847:4': {'r': 0.24705882370471954, 'g': 0.5529412031173706, 'b': 1, 'a': 1}};
--categories-color-chart-area-green-fill: {'618:0': {'r': 0.7252106666564941, 'g': 0.9840070009231567, 'b': 0.822259247303009, 'a': 0.699999988079071}, '618:1': {'r': 0.24705882370471954, 'g': 0.4313725531101227, 'b': 0.3176470696926117, 'a': 0.699999988079071}, '847:4': {'r': 0.7252106666564941, 'g': 0.9840070009231567, 'b': 0.822259247303009, 'a': 0.699999988079071}};
--categories-color-chart-area-green-stroke: {'618:0': {'r': 0.48235294222831726, 'g': 0.9450980424880981, 'b': 0.6687395572662354, 'a': 1}, '618:1': {'r': 0.48235294222831726, 'g': 0.9450980424880981, 'b': 0.658823549747467, 'a': 1}, '847:4': {'r': 0.48235294222831726, 'g': 0.9450980424880981, 'b': 0.6687395572662354, 'a': 1}};
--categories-color-chart-area-green-fill-2: {'618:0': {'r': 0.5098943710327148, 'g': 0.8876308798789978, 'b': 0.660988986492157, 'a': 0.699999988079071}, '618:1': {'r': 0.054901961237192154, 'g': 0.3686274588108063, 'b': 0.18039216101169586, 'a': 0.699999988079071}, '847:4': {'r': 0.5098943710327148, 'g': 0.8876308798789978, 'b': 0.660988986492157, 'a': 0.699999988079071}};
--categories-color-chart-area-green-stroke-2: {'618:0': {'r': 0.09803921729326248, 'g': 0.8196078538894653, 'b': 0.38823530077934265, 'a': 1}, '618:1': {'r': 0.09803921729326248, 'g': 0.8196078538894653, 'b': 0.38823530077934265, 'a': 1}, '847:4': {'r': 0.09803921729326248, 'g': 0.8196078538894653, 'b': 0.38823530077934265, 'a': 1}};
--categories-color-chart-area-rose-fill: {'618:0': {'r': 1, 'g': 0.8509804010391235, 'b': 0.8705882430076599, 'a': 0.699999988079071}, '618:1': {'r': 0.4588235318660736, 'g': 0.30588236451148987, 'b': 0.32549020648002625, 'a': 0.699999988079071}, '847:4': {'r': 1, 'g': 0.8509804010391235, 'b': 0.8705882430076599, 'a': 0.699999988079071}};
--categories-color-chart-area-rose-stroke: {'618:0': {'r': 1, 'g': 0.6313725709915161, 'b': 0.6784313917160034, 'a': 1}, '618:1': {'r': 1, 'g': 0.6313725709915161, 'b': 0.6784313917160034, 'a': 1}, '847:4': {'r': 1, 'g': 0.6313725709915161, 'b': 0.6784313917160034, 'a': 1}};
--categories-color-chart-area-rose-fill-2: {'618:0': {'r': 0.9578414559364319, 'g': 0.5668655633926392, 'b': 0.6601600646972656, 'a': 0.699999988079071}, '618:1': {'r': 0.45490196347236633, 'g': 0.10588235408067703, 'b': 0.1882352977991104, 'a': 0.699999988079071}, '847:4': {'r': 0.9578414559364319, 'g': 0.5668655633926392, 'b': 0.6601600646972656, 'a': 0.699999988079071}};
--categories-color-chart-area-rose-stroke-2: {'618:0': {'r': 1, 'g': 0.30980393290519714, 'b': 0.4745098054409027, 'a': 1}, '618:1': {'r': 1, 'g': 0.27450981736183167, 'b': 0.43921568989753723, 'a': 1}, '847:4': {'r': 1, 'g': 0.30980393290519714, 'b': 0.4745098054409027, 'a': 1}};
--categories-color-chart-area-teal-fill: {'618:0': {'r': 0.6622663736343384, 'g': 0.9549305438995361, 'b': 0.9112495183944702, 'a': 0.699999988079071}, '618:1': {'r': 0.2528286874294281, 'g': 0.5769955515861511, 'b': 0.5327908992767334, 'a': 0.699999988079071}, '847:4': {'r': 0.6622663736343384, 'g': 0.9549305438995361, 'b': 0.9112495183944702, 'a': 0.699999988079071}};
--categories-color-chart-area-teal-stroke: {'618:0': {'r': 0.27450981736183167, 'g': 0.929411768913269, 'b': 0.8352941274642944, 'a': 1}, '618:1': {'r': 0.27450981736183167, 'g': 0.929411768913269, 'b': 0.8352941274642944, 'a': 1}, '847:4': {'r': 0.27450981736183167, 'g': 0.929411768913269, 'b': 0.8352941274642944, 'a': 1}};
--categories-color-chart-area-teal-fill-2: {'618:0': {'r': 0.4880538582801819, 'g': 0.9056999683380127, 'b': 0.8611510396003723, 'a': 0.699999988079071}, '618:1': {'r': 0.054901961237192154, 'g': 0.3490196168422699, 'b': 0.3176470696926117, 'a': 0.699999988079071}, '847:4': {'r': 0.4880538582801819, 'g': 0.9056999683380127, 'b': 0.8611510396003723, 'a': 0.699999988079071}};
--categories-color-chart-area-teal-stroke-2: {'618:0': {'r': 0.027450980618596077, 'g': 0.7529411911964417, 'b': 0.6745098233222961, 'a': 1}, '618:1': {'r': 0.10980392247438431, 'g': 0.8117647171020508, 'b': 0.7254902124404907, 'a': 1}, '847:4': {'r': 0.027450980618596077, 'g': 0.7529411911964417, 'b': 0.6745098233222961, 'a': 1}};
--categories-color-chart-area-purple-fill: {'618:0': {'r': 0.9411764740943909, 'g': 0.8784313797950745, 'b': 1, 'a': 0.699999988079071}, '618:1': {'r': 0.3960784375667572, 'g': 0.3333333432674408, 'b': 0.4627451002597809, 'a': 0.699999988079071}, '847:4': {'r': 0.9411764740943909, 'g': 0.8784313797950745, 'b': 1, 'a': 0.699999988079071}};
--categories-color-chart-area-purple-stroke: {'618:0': {'r': 0.8549019694328308, 'g': 0.6980392336845398, 'b': 1, 'a': 1}, '618:1': {'r': 0.8549019694328308, 'g': 0.6980392336845398, 'b': 1, 'a': 1}, '847:4': {'r': 0.8549019694328308, 'g': 0.6980392336845398, 'b': 1, 'a': 1}};
--categories-color-chart-area-purple-fill-2: {'618:0': {'r': 0.8705882430076599, 'g': 0.7098039388656616, 'b': 1, 'a': 0.699999988079071}, '618:1': {'r': 0.32549020648002625, 'g': 0.16470588743686676, 'b': 0.46666666865348816, 'a': 0.699999988079071}, '847:4': {'r': 0.8705882430076599, 'g': 0.7098039388656616, 'b': 1, 'a': 0.699999988079071}};
--categories-color-chart-area-purple-stroke-2: {'618:0': {'r': 0.7764706015586853, 'g': 0.4941176474094391, 'b': 1, 'a': 1}, '618:1': {'r': 0.6627451181411743, 'g': 0.4156862795352936, 'b': 0.8666666746139526, 'a': 1}, '847:4': {'r': 0.7764706015586853, 'g': 0.4941176474094391, 'b': 1, 'a': 1}};
--categories-color-obra-shadn-docs-obra-shadcn-ui-docs-1: {'618:0': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:16:2015'}, '618:1': {'r': 0.06697625666856766, 'g': 0.08797097951173782, 'b': 0.15845328569412231, 'a': 1}, '847:4': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:55:5102'}};
--categories-color-chart-area-amber-fill: {'618:0': {'r': 1, 'g': 0.929411768913269, 'b': 0.6745098233222961, 'a': 0.699999988079071}, '618:1': {'r': 0.45490196347236633, 'g': 0.3843137323856354, 'b': 0.12941177189350128, 'a': 0.699999988079071}, '847:4': {'r': 1, 'g': 0.929411768913269, 'b': 0.6745098233222961, 'a': 0.699999988079071}};
--categories-color-obra-shadn-docs-obra-shadcn-ui-docs-2: {'618:0': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:534:30510'}, '618:1': {'r': 0.12658406794071198, 'g': 0.11423555761575699, 'b': 0.10479257255792618, 'a': 1}, '847:4': {'type': 'VARIABLE_ALIAS', 'id': 'VariableID:55:5102'}};
--categories-color-chart-area-amber-stroke: {'618:0': {'r': 1, 'g': 0.8235294222831726, 'b': 0.1882352977991104, 'a': 1}, '618:1': {'r': 1, 'g': 0.8235294222831726, 'b': 0.1882352977991104, 'a': 1}, '847:4': {'r': 1, 'g': 0.8235294222831726, 'b': 0.1882352977991104, 'a': 1}};
--categories-color-chart-area-amber-fill-2: {'618:0': {'r': 0.9960784316062927, 'g': 0.8392156958580017, 'b': 0.6000000238418579, 'a': 0.699999988079071}, '618:1': {'r': 0.45098039507865906, 'g': 0.29411765933036804, 'b': 0.054901961237192154, 'a': 0.699999988079071}, '847:4': {'r': 0.9960784316062927, 'g': 0.8392156958580017, 'b': 0.6000000238418579, 'a': 0.699999988079071}};
--categories-color-chart-area-amber-stroke-2: {'618:0': {'r': 0.9960784316062927, 'g': 0.6039215922355652, 'b': 0, 'a': 1}, '618:1': {'r': 1, 'g': 0.6470588445663452, 'b': 0.03921568766236305, 'a': 1}, '847:4': {'r': 0.9960784316062927, 'g': 0.6039215922355652, 'b': 0, 'a': 1}};
--categories-color-chart-static-blue-1: {'618:0': {'r': 0.5568627715110779, 'g': 0.772549033164978, 'b': 1, 'a': 1}, '618:1': {'r': 0.5568627715110779, 'g': 0.772549033164978, 'b': 1, 'a': 1}, '847:4': {'r': 0.5568627715110779, 'g': 0.772549033164978, 'b': 1, 'a': 1}};
--categories-color-chart-static-rose-1: {'618:0': {'r': 1, 'g': 0.6313725709915161, 'b': 0.6784313917160034, 'a': 1}, '618:1': {'r': 1, 'g': 0.6313725709915161, 'b': 0.6784313917160034, 'a': 1}, '847:4': {'r': 1, 'g': 0.6313725709915161, 'b': 0.6784313917160034, 'a': 1}};
--categories-color-chart-static-rose-2: {'618:0': {'r': 1, 'g': 0.125490203499794, 'b': 0.33725491166114807, 'a': 1}, '618:1': {'r': 1, 'g': 0.125490203499794, 'b': 0.33725491166114807, 'a': 1}, '847:4': {'r': 1, 'g': 0.125490203499794, 'b': 0.33725491166114807, 'a': 1}};
--categories-color-chart-static-rose-3: {'618:0': {'r': 0.9254902005195618, 'g': 0, 'b': 0.24705882370471954, 'a': 1}, '618:1': {'r': 0.9254902005195618, 'g': 0, 'b': 0.24705882370471954, 'a': 1}, '847:4': {'r': 0.9254902005195618, 'g': 0, 'b': 0.24705882370471954, 'a': 1}};
--categories-color-chart-static-rose-4: {'618:0': {'r': 0.7803921699523926, 'g': 0, 'b': 0.21176470816135406, 'a': 1}, '618:1': {'r': 0.7803921699523926, 'g': 0, 'b': 0.21176470816135406, 'a': 1}, '847:4': {'r': 0.7803921699523926, 'g': 0, 'b': 0.21176470816135406, 'a': 1}};
--categories-color-chart-static-rose-5: {'618:0': {'r': 0.6470588445663452, 'g': 0, 'b': 0.21176470816135406, 'a': 1}, '618:1': {'r': 0.6470588445663452, 'g': 0, 'b': 0.21176470816135406, 'a': 1}, '847:4': {'r': 0.6470588445663452, 'g': 0, 'b': 0.21176470816135406, 'a': 1}};
--categories-color-chart-static-purple-1: {'618:0': {'r': 0.8549019694328308, 'g': 0.6980392336845398, 'b': 1, 'a': 1}, '618:1': {'r': 0.8549019694328308, 'g': 0.6980392336845398, 'b': 1, 'a': 1}, '847:4': {'r': 0.8549019694328308, 'g': 0.6980392336845398, 'b': 1, 'a': 1}};
--categories-color-chart-static-purple-2: {'618:0': {'r': 0.6784313917160034, 'g': 0.27450981736183167, 'b': 1, 'a': 1}, '618:1': {'r': 0.6784313917160034, 'g': 0.27450981736183167, 'b': 1, 'a': 1}, '847:4': {'r': 0.6784313917160034, 'g': 0.27450981736183167, 'b': 1, 'a': 1}};
--categories-color-chart-static-purple-3: {'618:0': {'r': 0.5960784554481506, 'g': 0.062745101749897, 'b': 0.9803921580314636, 'a': 1}, '618:1': {'r': 0.5960784554481506, 'g': 0.062745101749897, 'b': 0.9803921580314636, 'a': 1}, '847:4': {'r': 0.5960784554481506, 'g': 0.062745101749897, 'b': 0.9803921580314636, 'a': 1}};
--categories-color-chart-static-purple-4: {'618:0': {'r': 0.5098039507865906, 'g': 0, 'b': 0.8588235378265381, 'a': 1}, '618:1': {'r': 0.5098039507865906, 'g': 0, 'b': 0.8588235378265381, 'a': 1}, '847:4': {'r': 0.5098039507865906, 'g': 0, 'b': 0.8588235378265381, 'a': 1}};
--categories-color-chart-static-purple-5: {'618:0': {'r': 0.4313725531101227, 'g': 0.06666667014360428, 'b': 0.6901960968971252, 'a': 1}, '618:1': {'r': 0.4313725531101227, 'g': 0.06666667014360428, 'b': 0.6901960968971252, 'a': 1}, '847:4': {'r': 0.4313725531101227, 'g': 0.06666667014360428, 'b': 0.6901960968971252, 'a': 1}};
--categories-color-chart-static-orange-1: {'618:0': {'r': 1, 'g': 0.7215686440467834, 'b': 0.4156862795352936, 'a': 1}, '618:1': {'r': 1, 'g': 0.7215686440467834, 'b': 0.4156862795352936, 'a': 1}, '847:4': {'r': 1, 'g': 0.7215686440467834, 'b': 0.4156862795352936, 'a': 1}};
--categories-color-chart-static-orange-2: {'618:0': {'r': 1, 'g': 0.4117647111415863, 'b': 0, 'a': 1}, '618:1': {'r': 1, 'g': 0.4117647111415863, 'b': 0, 'a': 1}, '847:4': {'r': 1, 'g': 0.4117647111415863, 'b': 0, 'a': 1}};
--categories-color-chart-static-orange-3: {'618:0': {'r': 0.9607843160629272, 'g': 0.29019609093666077, 'b': 0, 'a': 1}, '618:1': {'r': 0.9607843160629272, 'g': 0.29019609093666077, 'b': 0, 'a': 1}, '847:4': {'r': 0.9607843160629272, 'g': 0.29019609093666077, 'b': 0, 'a': 1}};
--categories-color-chart-static-orange-4: {'618:0': {'r': 0.7921568751335144, 'g': 0.2078431397676468, 'b': 0, 'a': 1}, '618:1': {'r': 0.7921568751335144, 'g': 0.2078431397676468, 'b': 0, 'a': 1}, '847:4': {'r': 0.7921568751335144, 'g': 0.2078431397676468, 'b': 0, 'a': 1}};
--categories-color-chart-static-orange-5: {'618:0': {'r': 0.6235294342041016, 'g': 0.1764705926179886, 'b': 0, 'a': 1}, '618:1': {'r': 0.6235294342041016, 'g': 0.1764705926179886, 'b': 0, 'a': 1}, '847:4': {'r': 0.6235294342041016, 'g': 0.1764705926179886, 'b': 0, 'a': 1}};
--categories-color-chart-static-teal-1: {'618:0': {'r': 0.27450981736183167, 'g': 0.929411768913269, 'b': 0.8352941274642944, 'a': 1}, '618:1': {'r': 0.27450981736183167, 'g': 0.929411768913269, 'b': 0.8352941274642944, 'a': 1}, '847:4': {'r': 0.27450981736183167, 'g': 0.929411768913269, 'b': 0.8352941274642944, 'a': 1}};
--categories-color-chart-static-teal-2: {'618:0': {'r': 0, 'g': 0.7333333492279053, 'b': 0.6549019813537598, 'a': 1}, '618:1': {'r': 0, 'g': 0.7333333492279053, 'b': 0.6549019813537598, 'a': 1}, '847:4': {'r': 0, 'g': 0.7333333492279053, 'b': 0.6549019813537598, 'a': 1}};
--categories-color-chart-static-teal-3: {'618:0': {'r': 0, 'g': 0.5882353186607361, 'b': 0.5372549295425415, 'a': 1}, '618:1': {'r': 0, 'g': 0.5882353186607361, 'b': 0.5372549295425415, 'a': 1}, '847:4': {'r': 0, 'g': 0.5882353186607361, 'b': 0.5372549295425415, 'a': 1}};
--categories-color-chart-static-teal-4: {'618:0': {'r': 0, 'g': 0.47058823704719543, 'b': 0.43529412150382996, 'a': 1}, '618:1': {'r': 0, 'g': 0.47058823704719543, 'b': 0.43529412150382996, 'a': 1}, '847:4': {'r': 0, 'g': 0.47058823704719543, 'b': 0.43529412150382996, 'a': 1}};
--categories-color-chart-static-teal-5: {'618:0': {'r': 0, 'g': 0.37254902720451355, 'b': 0.3529411852359772, 'a': 1}, '618:1': {'r': 0, 'g': 0.37254902720451355, 'b': 0.3529411852359772, 'a': 1}, '847:4': {'r': 0, 'g': 0.37254902720451355, 'b': 0.3529411852359772, 'a': 1}};
--categories-color-chart-static-blue-2: {'618:0': {'r': 0.16862745583057404, 'g': 0.49803921580314636, 'b': 1, 'a': 1}, '618:1': {'r': 0.16862745583057404, 'g': 0.49803921580314636, 'b': 1, 'a': 1}, '847:4': {'r': 0.16862745583057404, 'g': 0.49803921580314636, 'b': 1, 'a': 1}};
--categories-color-chart-static-blue-3: {'618:0': {'r': 0.08235294371843338, 'g': 0.364705890417099, 'b': 0.9882352948188782, 'a': 1}, '618:1': {'r': 0.08235294371843338, 'g': 0.364705890417099, 'b': 0.9882352948188782, 'a': 1}, '847:4': {'r': 0.08235294371843338, 'g': 0.364705890417099, 'b': 0.9882352948188782, 'a': 1}};
--categories-color-chart-static-blue-4: {'618:0': {'r': 0.0784313753247261, 'g': 0.27843138575553894, 'b': 0.9019607901573181, 'a': 1}, '618:1': {'r': 0.0784313753247261, 'g': 0.27843138575553894, 'b': 0.9019607901573181, 'a': 1}, '847:4': {'r': 0.0784313753247261, 'g': 0.27843138575553894, 'b': 0.9019607901573181, 'a': 1}};
--categories-color-chart-static-blue-5: {'618:0': {'r': 0.09803921729326248, 'g': 0.23529411852359772, 'b': 0.7215686440467834, 'a': 1}, '618:1': {'r': 0.09803921729326248, 'g': 0.23529411852359772, 'b': 0.7215686440467834, 'a': 1}, '847:4': {'r': 0.09803921729326248, 'g': 0.23529411852359772, 'b': 0.7215686440467834, 'a': 1}};
--categories-color-chart-static-amber-1: {'618:0': {'r': 1, 'g': 0.8235294222831726, 'b': 0.1882352977991104, 'a': 1}, '618:1': {'r': 1, 'g': 0.8235294222831726, 'b': 0.1882352977991104, 'a': 1}, '847:4': {'r': 1, 'g': 0.8235294222831726, 'b': 0.1882352977991104, 'a': 1}};
--categories-color-chart-static-amber-2: {'618:0': {'r': 0.9960784316062927, 'g': 0.6039215922355652, 'b': 0, 'a': 1}, '618:1': {'r': 0.9960784316062927, 'g': 0.6039215922355652, 'b': 0, 'a': 1}, '847:4': {'r': 0.9960784316062927, 'g': 0.6039215922355652, 'b': 0, 'a': 1}};
--categories-color-chart-static-amber-3: {'618:0': {'r': 0.8823529481887817, 'g': 0.4431372582912445, 'b': 0, 'a': 1}, '618:1': {'r': 0.8823529481887817, 'g': 0.4431372582912445, 'b': 0, 'a': 1}, '847:4': {'r': 0.8823529481887817, 'g': 0.4431372582912445, 'b': 0, 'a': 1}};
--categories-color-chart-static-amber-4: {'618:0': {'r': 0.7333333492279053, 'g': 0.3019607961177826, 'b': 0, 'a': 1}, '618:1': {'r': 0.7333333492279053, 'g': 0.3019607961177826, 'b': 0, 'a': 1}, '847:4': {'r': 0.7333333492279053, 'g': 0.3019607961177826, 'b': 0, 'a': 1}};
--categories-color-chart-static-amber-5: {'618:0': {'r': 0.5921568870544434, 'g': 0.23529411852359772, 'b': 0, 'a': 1}, '618:1': {'r': 0.5921568870544434, 'g': 0.23529411852359772, 'b': 0, 'a': 1}, '847:4': {'r': 0.5921568870544434, 'g': 0.23529411852359772, 'b': 0, 'a': 1}};
--categories-color-chart-static-green-1: {'618:0': {'r': 0.48235294222831726, 'g': 0.9450980424880981, 'b': 0.658823549747467, 'a': 1}, '618:1': {'r': 0.48235294222831726, 'g': 0.9450980424880981, 'b': 0.658823549747467, 'a': 1}, '847:4': {'r': 0.48235294222831726, 'g': 0.9450980424880981, 'b': 0.658823549747467, 'a': 1}};
--categories-color-chart-static-green-2: {'618:0': {'r': 0, 'g': 0.7882353067398071, 'b': 0.3176470696926117, 'a': 1}, '618:1': {'r': 0, 'g': 0.7882353067398071, 'b': 0.3176470696926117, 'a': 1}, '847:4': {'r': 0, 'g': 0.7882353067398071, 'b': 0.3176470696926117, 'a': 1}};
--categories-color-chart-static-green-3: {'618:0': {'r': 0, 'g': 0.6509804129600525, 'b': 0.24313725531101227, 'a': 1}, '618:1': {'r': 0, 'g': 0.6509804129600525, 'b': 0.24313725531101227, 'a': 1}, '847:4': {'r': 0, 'g': 0.6509804129600525, 'b': 0.24313725531101227, 'a': 1}};
--categories-color-chart-static-green-4: {'618:0': {'r': 0, 'g': 0.5098039507865906, 'b': 0.21176470816135406, 'a': 1}, '618:1': {'r': 0, 'g': 0.5098039507865906, 'b': 0.21176470816135406, 'a': 1}, '847:4': {'r': 0, 'g': 0.5098039507865906, 'b': 0.21176470816135406, 'a': 1}};
--categories-color-chart-static-green-5: {'618:0': {'r': 0.003921568859368563, 'g': 0.4000000059604645, 'b': 0.1882352977991104, 'a': 1}, '618:1': {'r': 0.003921568859368563, 'g': 0.4000000059604645, 'b': 0.1882352977991104, 'a': 1}, '847:4': {'r': 0.003921568859368563, 'g': 0.4000000059604645, 'b': 0.1882352977991104, 'a': 1}};
--categories-color-2xs-color: {'848:1': {'r': 0, 'g': 0, 'b': 0, 'a': 0.05000000074505806}, '851:1': {'r': 0, 'g': 0, 'b': 0, 'a': 0.05000000074505806}};
--categories-color-xs-color: {'848:1': {'r': 0, 'g': 0, 'b': 0, 'a': 0.05000000074505806}, '851:1': {'r': 0, 'g': 0, 'b': 0, 'a': 0.05000000074505806}};
--categories-color-sm-color: {'848:1': {'r': 0, 'g': 0, 'b': 0, 'a': 0.10000000149011612}, '851:1': {'r': 0, 'g': 0, 'b': 0, 'a': 0.10000000149011612}};
--categories-color-md-color: {'848:1': {'r': 0, 'g': 0, 'b': 0, 'a': 0.10000000149011612}, '851:1': {'r': 0, 'g': 0, 'b': 0, 'a': 0.10000000149011612}};
--categories-color-lg-color: {'848:1': {'r': 0, 'g': 0, 'b': 0, 'a': 0.10000000149011612}, '851:1': {'r': 0, 'g': 0, 'b': 0, 'a': 0.10000000149011612}};
--categories-color-xl-color: {'848:1': {'r': 0, 'g': 0, 'b': 0, 'a': 0.10000000149011612}, '851:1': {'r': 0, 'g': 0, 'b': 0, 'a': 0.10000000149011612}};
--categories-color-2xl-color: {'848:1': {'r': 0, 'g': 0, 'b': 0, 'a': 0.25}, '851:1': {'r': 0, 'g': 0, 'b': 0, 'a': 0.25}};
--categories-typography-heading-1: None;
--categories-typography-heading-2: None;
--categories-typography-paragraph-small-regular: None;
--categories-typography-paragraph-small-bold: None;
--categories-typography-heading-3: None;
--categories-typography-paragraph-bold: None;
--categories-typography-paragraph-regular: None;
--categories-typography-paragraph-mini-regular: None;
--categories-typography-heading-4: None;
--categories-typography-monospaced: None;
--categories-typography-paragraph-medium: None;
--categories-typography-paragraph-small-medium: None;
--categories-typography-paragraph-mini-bold: None;
--categories-typography-paragraph-mini-medium: None;
--categories-effect-shadow-sm: None;
--categories-effect-shadow-lg: None;
--categories-effect-shadow-2xs: None;
--categories-effect-shadow-xs: None;
--categories-effect-shadow-md: None;
--categories-effect-shadow-xl: None;
--categories-effect-shadow-2xl: None;
--categories-effect-focus-ring: None;
--categories-effect-focus-ring-error: None;
--categories-effect-focus-ring-sidebar: None;
}
`;
// Add styles to document
const styleSheet = document.createElement('style');
styleSheet.textContent = tokenStyles;
document.head.appendChild(styleSheet);
const preview: Preview = {
parameters: {
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/i,
},
},
backgrounds: {
default: 'light',
values: [
{ name: 'light', value: '#FFFFFF' },
{ name: 'dark', value: '#1F2937' },
],
},
},
};
export default preview;

View File

@@ -1,211 +0,0 @@
"""
Design System Server (DSS) - Consolidated Platform
A modern design system orchestration platform with comprehensive tooling
"""
__version__ = "1.0.0-consolidated"
# Core models
from dss.models import (
Theme,
Component,
ComponentVariant,
Project,
ProjectMetadata,
DesignToken as ModelDesignToken,
TokenCategory as ModelTokenCategory,
)
# Validators
from dss.validators import (
ProjectValidator,
ValidationResult,
ValidationError,
ValidationStage,
)
# Tools
from dss.tools import (
StyleDictionaryTool,
StyleDictionaryWrapper,
ShadcnTool,
ShadcnWrapper,
FigmaWrapper,
FigmaAPIError,
)
# Ingest (multi-source token extraction)
from dss.ingest import (
DesignToken,
TokenSource,
TokenCollection,
CSSTokenSource,
SCSSTokenSource,
TailwindTokenSource,
JSONTokenSource,
TokenMerger,
MergeStrategy,
)
# Analyze (code analysis and scanning)
from dss.analyze import (
ProjectAnalysis,
StylePattern,
QuickWin,
ProjectScanner,
ReactAnalyzer,
StyleAnalyzer,
DependencyGraph,
QuickWinFinder,
)
# Storybook (integration and generation)
from dss.storybook import (
StorybookScanner,
StoryGenerator,
ThemeGenerator,
)
# Settings and configuration
from dss.settings import (
DSSSettings,
DSSManager,
settings,
manager,
)
# Status dashboard
from dss.status import (
StatusDashboard,
HealthMetric,
)
# Translations (dictionary system)
from dss.translations import (
TranslationSource,
MappingType,
TokenMapping,
ComponentMapping,
PatternMapping,
CustomProp,
TranslationMappings,
TranslationDictionary,
TranslationRegistry,
ResolvedToken,
ResolvedTheme,
TranslationDictionaryLoader,
TokenResolver,
ThemeMerger,
TranslationValidator,
TranslationDictionaryWriter,
DSS_CANONICAL_TOKENS,
DSS_CANONICAL_COMPONENTS,
DSS_TOKEN_ALIASES,
DSS_COMPONENT_VARIANTS,
is_valid_dss_token,
resolve_alias,
get_canonical_token_categories,
)
# Project Management
from dss.project import (
DSSProject,
ProjectConfig,
FigmaSource,
FigmaFile,
OutputConfig,
ProjectStatus,
ProjectManager,
ProjectRegistry,
FigmaProjectSync,
)
__all__ = [
# Version
"__version__",
# Models
"Theme",
"Component",
"ComponentVariant",
"Project",
"ProjectMetadata",
"ModelDesignToken",
"ModelTokenCategory",
# Validators
"ProjectValidator",
"ValidationResult",
"ValidationError",
"ValidationStage",
# Tools
"StyleDictionaryTool",
"StyleDictionaryWrapper",
"ShadcnTool",
"ShadcnWrapper",
"FigmaWrapper",
"FigmaAPIError",
# Ingest
"DesignToken",
"TokenSource",
"TokenCollection",
"CSSTokenSource",
"SCSSTokenSource",
"TailwindTokenSource",
"JSONTokenSource",
"TokenMerger",
"MergeStrategy",
# Analyze
"ProjectAnalysis",
"StylePattern",
"QuickWin",
"ProjectScanner",
"ReactAnalyzer",
"StyleAnalyzer",
"DependencyGraph",
"QuickWinFinder",
# Storybook
"StorybookScanner",
"StoryGenerator",
"ThemeGenerator",
# Settings
"DSSSettings",
"DSSManager",
"settings",
"manager",
# Status
"StatusDashboard",
"HealthMetric",
# Translations
"TranslationSource",
"MappingType",
"TokenMapping",
"ComponentMapping",
"PatternMapping",
"CustomProp",
"TranslationMappings",
"TranslationDictionary",
"TranslationRegistry",
"ResolvedToken",
"ResolvedTheme",
"TranslationDictionaryLoader",
"TokenResolver",
"ThemeMerger",
"TranslationValidator",
"TranslationDictionaryWriter",
"DSS_CANONICAL_TOKENS",
"DSS_CANONICAL_COMPONENTS",
"DSS_TOKEN_ALIASES",
"DSS_COMPONENT_VARIANTS",
"is_valid_dss_token",
"resolve_alias",
"get_canonical_token_categories",
# Project Management
"DSSProject",
"ProjectConfig",
"FigmaSource",
"FigmaFile",
"OutputConfig",
"ProjectStatus",
"ProjectManager",
"ProjectRegistry",
"FigmaProjectSync",
]

View File

@@ -1 +0,0 @@
"""FastAPI routes and application"""

View File

@@ -1,522 +0,0 @@
"""
FastAPI routes for DSS Export/Import system
Provides REST API endpoints for project export, import, merge, and analysis.
All operations support both synchronous and asynchronous (background job) modes.
"""
from fastapi import APIRouter, File, UploadFile, HTTPException, BackgroundTasks, Query
from fastapi.responses import FileResponse
from pydantic import BaseModel
from pathlib import Path
from datetime import datetime
from typing import Optional, List, Dict, Any
import logging
from dss.export_import.service import DSSProjectService, ExportSummary, ImportSummary, MergeSummary
from dss.models.project import Project
logger = logging.getLogger(__name__)
router = APIRouter(prefix="/api/projects", tags=["export_import"])
# Initialize service layer
service = DSSProjectService(busy_timeout_ms=5000)
# In-memory job tracking (replace with Redis/database in production)
_jobs: Dict[str, Dict[str, Any]] = {}
# ============================================================================
# Pydantic Models for API Responses
# ============================================================================
class ExportResponse(BaseModel):
"""Response from export endpoint"""
success: bool
file_size_bytes: Optional[int] = None
token_count: Optional[int] = None
component_count: Optional[int] = None
error: Optional[str] = None
duration_seconds: Optional[float] = None
class ImportResponse(BaseModel):
"""Response from import endpoint"""
success: bool
project_name: Optional[str] = None
token_count: Optional[int] = None
component_count: Optional[int] = None
migration_performed: Optional[bool] = None
warnings: Optional[List[str]] = None
error: Optional[str] = None
duration_seconds: Optional[float] = None
job_id: Optional[str] = None
class MergeResponse(BaseModel):
"""Response from merge endpoint"""
success: bool
new_items: Optional[int] = None
updated_items: Optional[int] = None
conflicts: Optional[int] = None
resolution_strategy: Optional[str] = None
error: Optional[str] = None
duration_seconds: Optional[float] = None
job_id: Optional[str] = None
class AnalysisResponse(BaseModel):
"""Response from analysis endpoint"""
is_valid: bool
project_name: Optional[str] = None
schema_version: Optional[str] = None
token_count: Optional[int] = None
component_count: Optional[int] = None
migration_needed: Optional[bool] = None
errors: Optional[List[str]] = None
warnings: Optional[List[str]] = None
class JobStatus(BaseModel):
"""Status of a background job"""
job_id: str
status: str # pending, running, completed, failed
result: Optional[Dict[str, Any]] = None
error: Optional[str] = None
created_at: str
completed_at: Optional[str] = None
# ============================================================================
# Export Endpoints
# ============================================================================
@router.post("/{project_id}/export", response_class=FileResponse)
async def export_project(
project_id: str,
background_tasks: Optional[BackgroundTasks] = None,
background: bool = Query(False, description="Run as background job")
) -> FileResponse:
"""
Export a project to a .dss archive file
Args:
project_id: ID of project to export
background: If true, schedule as background job (for large projects)
Returns:
.dss archive file download
Examples:
```bash
# Export synchronously
curl -X POST http://localhost:8000/api/projects/my-project/export \
-o my-project.dss
# Export as background job
curl -X POST "http://localhost:8000/api/projects/my-project/export?background=true"
```
"""
try:
# Load project (adapt to your data source)
project = _load_project(project_id)
if not project:
raise HTTPException(status_code=404, detail=f"Project not found: {project_id}")
# Export
output_path = Path("/tmp") / f"{project_id}_export.dss"
result: ExportSummary = service.export_project(project, output_path)
if not result.success:
raise HTTPException(status_code=400, detail=result.error)
# Return file for download
return FileResponse(
result.archive_path,
media_type="application/zip",
filename=f"{project.name}.dss"
)
except HTTPException:
raise
except Exception as e:
logger.error(f"Export failed for {project_id}: {e}")
raise HTTPException(status_code=500, detail=str(e))
# ============================================================================
# Import Endpoints
# ============================================================================
@router.post("/import", response_model=ImportResponse)
async def import_project(
file: UploadFile = File(...),
strategy: str = Query("replace", description="Import strategy: replace or merge"),
background: bool = Query(False, description="Run as background job")
) -> ImportResponse:
"""
Import a project from a .dss archive file
Args:
file: .dss archive file to import
strategy: Import strategy (replace=full restoration, merge=smart update)
background: If true, schedule as background job (for large archives)
Returns:
Import result summary
Examples:
```bash
# Import synchronously
curl -X POST http://localhost:8000/api/projects/import \
-F "file=@my-project.dss"
# Import with merge strategy
curl -X POST "http://localhost:8000/api/projects/import?strategy=merge" \
-F "file=@updates.dss"
# Import as background job
curl -X POST "http://localhost:8000/api/projects/import?background=true" \
-F "file=@large-project.dss"
```
"""
archive_path = None
job_id = None
try:
# Save uploaded file
archive_path = Path("/tmp") / f"import_{datetime.now().timestamp()}.dss"
contents = await file.read()
archive_path.write_bytes(contents)
# Check if should run as background job
if service._should_schedule_background(archive_path):
# Schedule background job
job_id = _create_job_id()
_jobs[job_id] = {
"status": "pending",
"created_at": datetime.now().isoformat(),
"type": "import",
"archive_path": str(archive_path),
"strategy": strategy
}
# In production: queue with Celery, RQ, or similar
# For now: return job ID for polling
return ImportResponse(
success=True,
job_id=job_id,
duration_seconds=0
)
# Run synchronously
result: ImportSummary = service.import_project(archive_path, strategy)
if not result.success:
raise HTTPException(status_code=400, detail=result.error)
return ImportResponse(
success=True,
project_name=result.project_name,
token_count=result.item_counts.get("tokens") if result.item_counts else None,
component_count=result.item_counts.get("components") if result.item_counts else None,
migration_performed=result.migration_performed,
warnings=result.warnings or [],
duration_seconds=result.duration_seconds
)
except HTTPException:
raise
except Exception as e:
logger.error(f"Import failed: {e}")
raise HTTPException(status_code=500, detail=str(e))
finally:
# Cleanup uploaded file after async processing
if archive_path and archive_path.exists() and job_id is None:
try:
archive_path.unlink()
except Exception:
pass
# ============================================================================
# Merge Endpoints
# ============================================================================
@router.post("/{project_id}/merge", response_model=MergeResponse)
async def merge_project(
project_id: str,
file: UploadFile = File(...),
strategy: str = Query("keep_local", description="Conflict resolution: overwrite, keep_local, or fork"),
background: bool = Query(False, description="Run as background job")
) -> MergeResponse:
"""
Merge updates from a .dss archive into a project
Args:
project_id: ID of project to merge into
file: .dss archive with updates
strategy: Conflict resolution strategy
background: If true, schedule as background job
Returns:
Merge result summary
Examples:
```bash
# Merge with keep_local strategy (preserve local changes)
curl -X POST "http://localhost:8000/api/projects/my-project/merge?strategy=keep_local" \
-F "file=@updates.dss"
# Merge with overwrite strategy (accept remote changes)
curl -X POST "http://localhost:8000/api/projects/my-project/merge?strategy=overwrite" \
-F "file=@updates.dss"
# Merge as background job (for large archives)
curl -X POST "http://localhost:8000/api/projects/my-project/merge?background=true" \
-F "file=@large-update.dss"
```
"""
archive_path = None
job_id = None
try:
# Load project
project = _load_project(project_id)
if not project:
raise HTTPException(status_code=404, detail=f"Project not found: {project_id}")
# Save uploaded file
archive_path = Path("/tmp") / f"merge_{datetime.now().timestamp()}.dss"
contents = await file.read()
archive_path.write_bytes(contents)
# Check if should run as background job
if service._should_schedule_background(archive_path):
job_id = _create_job_id()
_jobs[job_id] = {
"status": "pending",
"created_at": datetime.now().isoformat(),
"type": "merge",
"project_id": project_id,
"archive_path": str(archive_path),
"strategy": strategy
}
return MergeResponse(
success=True,
job_id=job_id,
duration_seconds=0
)
# Run synchronously
result: MergeSummary = service.merge_project(project, archive_path, strategy)
if not result.success:
raise HTTPException(status_code=400, detail=result.error)
return MergeResponse(
success=True,
new_items=result.new_items_count,
updated_items=result.updated_items_count,
conflicts=result.conflicts_count,
resolution_strategy=result.resolution_strategy,
duration_seconds=result.duration_seconds
)
except HTTPException:
raise
except Exception as e:
logger.error(f"Merge failed for {project_id}: {e}")
raise HTTPException(status_code=500, detail=str(e))
finally:
if archive_path and archive_path.exists() and job_id is None:
try:
archive_path.unlink()
except Exception:
pass
# ============================================================================
# Analysis Endpoints
# ============================================================================
@router.post("/{project_id}/analyze-merge")
async def analyze_merge(
project_id: str,
file: UploadFile = File(...)
) -> AnalysisResponse:
"""
Analyze merge without applying it (safe preview)
Args:
project_id: ID of project to analyze merge into
file: .dss archive to analyze
Returns:
Merge analysis (what changes would happen)
Examples:
```bash
curl -X POST http://localhost:8000/api/projects/my-project/analyze-merge \
-F "file=@updates.dss"
```
"""
archive_path = None
try:
# Load project
project = _load_project(project_id)
if not project:
raise HTTPException(status_code=404, detail=f"Project not found: {project_id}")
# Save uploaded file
archive_path = Path("/tmp") / f"analyze_{datetime.now().timestamp()}.dss"
contents = await file.read()
archive_path.write_bytes(contents)
# Analyze
analysis = service.analyze_merge(project, archive_path)
return AnalysisResponse(
is_valid=analysis.is_valid,
new_items=len(analysis.new_items.get("tokens", [])),
conflicts=len(analysis.conflicted_items)
)
except HTTPException:
raise
except Exception as e:
logger.error(f"Merge analysis failed for {project_id}: {e}")
raise HTTPException(status_code=500, detail=str(e))
finally:
if archive_path and archive_path.exists():
try:
archive_path.unlink()
except Exception:
pass
@router.post("/analyze-archive")
async def analyze_archive(
file: UploadFile = File(...)
) -> AnalysisResponse:
"""
Analyze a .dss archive without importing it (safe preview)
Args:
file: .dss archive to analyze
Returns:
Archive analysis details
Examples:
```bash
curl -X POST http://localhost:8000/api/projects/analyze-archive \
-F "file=@project.dss"
```
"""
archive_path = None
try:
# Save uploaded file
archive_path = Path("/tmp") / f"analyze_archive_{datetime.now().timestamp()}.dss"
contents = await file.read()
archive_path.write_bytes(contents)
# Analyze
analysis = service.analyze_import(archive_path)
return AnalysisResponse(
is_valid=analysis.is_valid,
project_name=analysis.project_name,
schema_version=analysis.schema_version,
token_count=analysis.content_summary.get("tokens", {}).get("count"),
component_count=analysis.content_summary.get("components", {}).get("count"),
migration_needed=analysis.migration_needed,
errors=[e.message for e in analysis.errors],
warnings=analysis.warnings
)
except Exception as e:
logger.error(f"Archive analysis failed: {e}")
raise HTTPException(status_code=500, detail=str(e))
finally:
if archive_path and archive_path.exists():
try:
archive_path.unlink()
except Exception:
pass
# ============================================================================
# Job Status Endpoint
# ============================================================================
@router.get("/jobs/{job_id}", response_model=JobStatus)
async def get_job_status(job_id: str) -> JobStatus:
"""
Get status of a background job
Args:
job_id: ID of the job (returned from async endpoint)
Returns:
Current job status and result (if completed)
Examples:
```bash
curl http://localhost:8000/api/projects/jobs/job-123
```
"""
job = _jobs.get(job_id)
if not job:
raise HTTPException(status_code=404, detail=f"Job not found: {job_id}")
return JobStatus(
job_id=job_id,
status=job.get("status", "unknown"),
result=job.get("result"),
error=job.get("error"),
created_at=job.get("created_at", ""),
completed_at=job.get("completed_at")
)
# ============================================================================
# Helper Functions
# ============================================================================
def _load_project(project_id: str) -> Optional[Project]:
"""
Load a project by ID
ADAPT THIS to your actual data source (database, API, etc.)
"""
try:
# Example: Load from database
# return db.query(Project).filter(Project.id == project_id).first()
# For now: return a dummy project
# In production: implement actual loading
logger.warning(f"Using dummy project for {project_id} - implement _load_project()")
return Project(
name=project_id,
description="Auto-loaded project",
author="system"
)
except Exception as e:
logger.error(f"Failed to load project {project_id}: {e}")
return None
def _create_job_id() -> str:
"""Generate unique job ID"""
import uuid
return str(uuid.uuid4())[:8]
# ============================================================================
# Export router for inclusion in FastAPI app
# ============================================================================
__all__ = ["router"]

View File

@@ -1,14 +0,0 @@
"""Wrappers for external design system tools"""
from .style_dictionary import StyleDictionaryTool, StyleDictionaryWrapper
from .shadcn import ShadcnTool, ShadcnWrapper
from .figma import FigmaWrapper, FigmaAPIError
__all__ = [
"StyleDictionaryTool",
"StyleDictionaryWrapper",
"ShadcnTool",
"ShadcnWrapper",
"FigmaWrapper",
"FigmaAPIError"
]

View File

@@ -1,316 +0,0 @@
"""
Figma API wrapper for design token extraction
Based on Figmagic architecture and W3C DTCG format standards
"""
import json
import requests
from pathlib import Path
from typing import Dict, List, Optional, Any
from dss.models.theme import Theme, DesignToken, TokenCategory
class FigmaWrapper:
"""
Wrapper for Figma REST API
Extracts design tokens from Figma Variables and converts to W3C DTCG format
Architecture:
Figma Variables API → W3C DTCG format → DSS Theme model → StyleDictionary → outputs
"""
FIGMA_API_BASE = "https://api.figma.com/v1"
def __init__(self, api_token: str, file_key: str, use_cache: bool = True):
"""
Initialize Figma wrapper
Args:
api_token: Figma personal access token
file_key: Figma file key (from URL)
use_cache: Whether to cache API responses
"""
if not api_token or not file_key:
raise ValueError("Figma API token and file key are required")
self.api_token = api_token
self.file_key = file_key
self.use_cache = use_cache
self.cache_path = Path.home() / ".dss" / "figma_cache.json"
self.headers = {
"X-Figma-Token": self.api_token
}
def get_variables(self) -> Dict[str, Any]:
"""
Fetch variables from Figma file using Variables API
Returns:
Raw Figma Variables API response
Raises:
FigmaAPIError: If API request fails
"""
# Check cache first
if self.use_cache and self.cache_path.exists():
with open(self.cache_path, 'r') as f:
return json.load(f)
# Fetch from API
url = f"{self.FIGMA_API_BASE}/files/{self.file_key}/variables/local"
try:
response = requests.get(url, headers=self.headers)
response.raise_for_status()
data = response.json()
# Cache response
if self.use_cache:
self.cache_path.parent.mkdir(parents=True, exist_ok=True)
with open(self.cache_path, 'w') as f:
json.dump(data, f, indent=2)
return data
except requests.exceptions.HTTPError as e:
if e.response.status_code == 403:
raise FigmaAPIError("Invalid Figma API token (403 Forbidden)")
elif e.response.status_code == 404:
raise FigmaAPIError(f"Figma file '{self.file_key}' not found (404)")
else:
raise FigmaAPIError(f"Figma API error: {e}")
except Exception as e:
raise FigmaAPIError(f"Failed to fetch Figma variables: {e}")
def extract_themes(self) -> Dict[str, Theme]:
"""
Extract themes from Figma Variables
Figma uses "variable collections" with "modes" for themes.
Example: Collection "Colors" might have modes "Light" and "Dark"
Returns:
Dict mapping theme name to DSS Theme object
"""
figma_data = self.get_variables()
# Build mode ID → theme name mapping
mode_map = self._build_mode_map(figma_data.get("meta", {}).get("variableCollections", {}))
# Extract variables and convert to DTCG format
variables = figma_data.get("meta", {}).get("variables", {})
dtcg_tokens = self._convert_to_dtcg(variables)
# Structure tokens by theme
themes = self._structure_by_theme(dtcg_tokens, mode_map)
return themes
def _build_mode_map(self, variable_collections: Dict[str, Any]) -> Dict[str, str]:
"""
Build mapping of mode ID → theme name
Args:
variable_collections: Figma variable collections data
Returns:
Dict mapping mode ID to theme name (e.g., {"331:7": "Light"})
"""
mode_map = {}
for collection_id, collection in variable_collections.items():
modes = collection.get("modes", [])
for mode in modes:
mode_id = mode.get("modeId")
mode_name = mode.get("name")
if mode_id and mode_name:
mode_map[mode_id] = mode_name
return mode_map
def _convert_to_dtcg(self, variables: Dict[str, Any]) -> Dict[str, Any]:
"""
Convert Figma variables to W3C DTCG format
DTCG format:
{
"color": {
"primary": {
"$value": "#0066cc",
"$type": "color",
"$description": "Primary brand color"
}
}
}
Args:
variables: Figma variables data
Returns:
DTCG-formatted token tree
"""
tokens = {}
for var_id, variable in variables.items():
# Skip remote variables
if variable.get("remote", False):
continue
name = variable.get("name", "")
resolved_type = variable.get("resolvedType", "")
values_by_mode = variable.get("valuesByMode", {})
description = variable.get("description", "")
# Convert Figma type to DTCG type
dtcg_type = self._map_figma_type_to_dtcg(resolved_type)
# Parse name into nested structure (e.g., "colors/primary/500" → colors.primary.500)
path_parts = name.split("/")
# Create token object with values by mode
token_obj = {
"$type": dtcg_type,
"valuesByMode": values_by_mode
}
if description:
token_obj["$description"] = description
# Set in nested structure
self._set_nested(tokens, path_parts, token_obj)
return tokens
def _structure_by_theme(self, dtcg_tokens: Dict[str, Any], mode_map: Dict[str, str]) -> Dict[str, Theme]:
"""
Structure tokens by theme using mode mapping
Args:
dtcg_tokens: DTCG tokens with valuesByMode
mode_map: Mapping of mode ID to theme name
Returns:
Dict of theme name → DSS Theme
"""
themes = {}
# Initialize themes
for mode_name in set(mode_map.values()):
themes[mode_name] = Theme(
name=f"DSS {mode_name}",
version="1.0.0",
tokens={}
)
# Recursively extract tokens for each theme
def extract_tokens(node: Dict[str, Any], path: str = ""):
for key, value in node.items():
if key.startswith("$"):
# Skip metadata keys
continue
current_path = f"{path}/{key}" if path else key
if isinstance(value, dict) and "valuesByMode" in value:
# This is a token leaf node
dtcg_type = value.get("$type", "other")
description = value.get("$description", "")
values_by_mode = value["valuesByMode"]
# Create token for each mode
for mode_id, token_value in values_by_mode.items():
theme_name = mode_map.get(mode_id)
if theme_name and theme_name in themes:
# Convert path to token name (use last part for simplicity)
token_name = key
# Format value based on type
formatted_value = self._format_value(token_value, dtcg_type)
# Map DTCG type to DSS TokenCategory
category = self._map_dtcg_type_to_category(dtcg_type)
# Create DesignToken
design_token = DesignToken(
name=token_name,
value=formatted_value,
type=dtcg_type,
category=category,
description=description or f"{token_name} token"
)
themes[theme_name].tokens[token_name] = design_token
elif isinstance(value, dict):
# Recurse into nested groups
extract_tokens(value, current_path)
extract_tokens(dtcg_tokens)
return themes
def _map_figma_type_to_dtcg(self, figma_type: str) -> str:
"""Map Figma variable type to W3C DTCG type"""
type_map = {
"COLOR": "color",
"FLOAT": "number", # Could be dimension, duration, etc.
"STRING": "string",
"BOOLEAN": "boolean"
}
return type_map.get(figma_type, "other")
def _map_dtcg_type_to_category(self, dtcg_type: str) -> TokenCategory:
"""Map DTCG type to DSS TokenCategory"""
category_map = {
"color": TokenCategory.COLOR,
"dimension": TokenCategory.SPACING,
"fontFamily": TokenCategory.TYPOGRAPHY,
"fontSize": TokenCategory.TYPOGRAPHY,
"fontWeight": TokenCategory.TYPOGRAPHY,
"lineHeight": TokenCategory.TYPOGRAPHY,
"borderRadius": TokenCategory.RADIUS,
"shadow": TokenCategory.SHADOW,
"border": TokenCategory.BORDER,
}
return category_map.get(dtcg_type, TokenCategory.OTHER)
def _format_value(self, value: Any, dtcg_type: str) -> str:
"""
Format Figma value to string representation
Args:
value: Raw Figma value
dtcg_type: DTCG type
Returns:
Formatted value string
"""
if dtcg_type == "color" and isinstance(value, dict):
# Figma color format: {r: 0-1, g: 0-1, b: 0-1, a: 0-1}
r = int(value.get("r", 0) * 255)
g = int(value.get("g", 0) * 255)
b = int(value.get("b", 0) * 255)
a = value.get("a", 1)
if a == 1:
return f"rgb({r}, {g}, {b})"
else:
return f"rgba({r}, {g}, {b}, {a})"
return str(value)
def _set_nested(self, obj: Dict, path: List[str], value: Any):
"""Set value in nested dictionary using path"""
current = obj
for part in path[:-1]:
if part not in current:
current[part] = {}
current = current[part]
current[path[-1]] = value
class FigmaAPIError(Exception):
"""Exception raised for Figma API errors"""
pass

View File

@@ -1,112 +0,0 @@
"""
Shadcn CLI wrapper for component management
"""
import subprocess
from pathlib import Path
from typing import List, Optional, Dict, Any
class ShadcnWrapper:
"""
Wrapper for shadcn/ui CLI
Manages shadcn component installation and configuration
"""
def __init__(self, shadcn_path: str = "npx shadcn-ui@latest"):
"""
Initialize Shadcn wrapper
Args:
shadcn_path: Path to shadcn executable (default: npx shadcn-ui@latest)
"""
self.shadcn_path = shadcn_path
def add_component(
self,
component_name: str,
project_path: Path,
overwrite: bool = False
) -> Dict[str, Any]:
"""
Add a shadcn component to project
Args:
component_name: Component to add (e.g., 'button', 'card')
project_path: Project root directory
overwrite: Whether to overwrite existing components
Returns:
Dict with installation result
"""
cmd = [
"npx", "shadcn-ui@latest", "add", component_name,
"--yes" # Auto-confirm
]
if overwrite:
cmd.append("--overwrite")
result = subprocess.run(
cmd,
cwd=project_path,
capture_output=True,
text=True
)
return {
"success": result.returncode == 0,
"component": component_name,
"stdout": result.stdout,
"stderr": result.stderr
}
def init_shadcn(self, project_path: Path) -> Dict[str, Any]:
"""
Initialize shadcn in a project
Args:
project_path: Project root directory
Returns:
Dict with initialization result
"""
cmd = ["npx", "shadcn-ui@latest", "init", "--yes"]
result = subprocess.run(
cmd,
cwd=project_path,
capture_output=True,
text=True
)
return {
"success": result.returncode == 0,
"stdout": result.stdout,
"stderr": result.stderr
}
def list_available_components(self) -> List[str]:
"""
List available shadcn components
Returns:
List of component names
"""
# Hardcoded list of common shadcn components
# In a real implementation, this would query the shadcn registry
return [
"accordion", "alert", "alert-dialog", "aspect-ratio",
"avatar", "badge", "button", "calendar", "card",
"checkbox", "collapsible", "command", "context-menu",
"dialog", "dropdown-menu", "form", "hover-card",
"input", "label", "menubar", "navigation-menu",
"popover", "progress", "radio-group", "scroll-area",
"select", "separator", "sheet", "skeleton", "slider",
"switch", "table", "tabs", "textarea", "toast",
"toggle", "tooltip"
]
# Alias for backward compatibility
ShadcnTool = ShadcnWrapper

View File

@@ -1,247 +0,0 @@
"""
Style Dictionary wrapper for design token transformation
Converts DSS tokens to various output formats (CSS, SCSS, JSON)
"""
import json
import subprocess
import tempfile
from pathlib import Path
from typing import Dict, List, Optional, Any
from dss.models.theme import Theme, DesignToken, TokenCategory
class StyleDictionaryWrapper:
"""
Wrapper for Amazon Style Dictionary CLI
Transforms design tokens into platform-specific outputs
"""
def __init__(self, sd_path: str = "npx style-dictionary"):
"""
Initialize Style Dictionary wrapper
Args:
sd_path: Path to style-dictionary executable (default: npx style-dictionary)
"""
self.sd_path = sd_path
def transform_theme(
self,
theme: Theme,
output_format: str = "css",
output_path: Optional[Path] = None
) -> Dict[str, Any]:
"""
Transform a DSS theme using style-dictionary
Args:
theme: DSS Theme to transform
output_format: Output format (css, scss, json, js)
output_path: Optional output directory
Returns:
Dict with transformation result
"""
# Create temporary directory for style-dictionary config
with tempfile.TemporaryDirectory() as tmpdir:
tmppath = Path(tmpdir)
# Convert DSS theme to style-dictionary format
sd_tokens = self._convert_theme_to_sd_format(theme)
# Write tokens to JSON file
tokens_file = tmppath / "tokens.json"
with open(tokens_file, "w") as f:
json.dump(sd_tokens, f, indent=2)
# Create style-dictionary config
config = self._create_sd_config(output_format, tmppath)
config_file = tmppath / "config.json"
with open(config_file, "w") as f:
json.dump(config, f, indent=2)
# Run style-dictionary build
result = self._run_sd_build(config_file, tmppath)
# Read output files
output_files = self._read_output_files(tmppath, output_format)
return {
"success": result.returncode == 0,
"output_format": output_format,
"files": output_files,
"errors": result.stderr if result.returncode != 0 else None
}
def _convert_theme_to_sd_format(self, theme: Theme) -> Dict[str, Any]:
"""
Convert DSS theme to style-dictionary token format
Style Dictionary format:
{
"color": {
"primary": { "value": "#0066cc" }
}
}
"""
sd_tokens = {}
for token_name, token in theme.tokens.items():
# Group tokens by category
category = token.category.value if token.category else "other"
if category not in sd_tokens:
sd_tokens[category] = {}
# Convert token to SD format
sd_tokens[category][token_name] = {
"value": token.value,
"type": token.type,
}
if token.description:
sd_tokens[category][token_name]["comment"] = token.description
return sd_tokens
def _create_sd_config(self, output_format: str, build_path: Path) -> Dict[str, Any]:
"""
Create style-dictionary configuration
Args:
output_format: Desired output format
build_path: Build directory path
Returns:
Style Dictionary config dict
"""
config = {
"source": ["tokens.json"],
"platforms": {}
}
if output_format == "css":
config["platforms"]["css"] = {
"transformGroup": "css",
"buildPath": str(build_path) + "/",
"files": [{
"destination": "theme.css",
"format": "css/variables"
}]
}
elif output_format == "scss":
config["platforms"]["scss"] = {
"transformGroup": "scss",
"buildPath": str(build_path) + "/",
"files": [{
"destination": "theme.scss",
"format": "scss/variables"
}]
}
elif output_format == "json":
config["platforms"]["json"] = {
"transformGroup": "js",
"buildPath": str(build_path) + "/",
"files": [{
"destination": "theme.json",
"format": "json/nested"
}]
}
elif output_format == "js":
config["platforms"]["js"] = {
"transformGroup": "js",
"buildPath": str(build_path) + "/",
"files": [{
"destination": "theme.js",
"format": "javascript/module"
}]
}
return config
def _run_sd_build(self, config_file: Path, cwd: Path) -> subprocess.CompletedProcess:
"""
Run style-dictionary build command
Args:
config_file: Path to config.json
cwd: Working directory
Returns:
Subprocess result
"""
cmd = [
"npx", "style-dictionary", "build",
"--config", str(config_file)
]
result = subprocess.run(
cmd,
cwd=cwd,
capture_output=True,
text=True
)
return result
def _read_output_files(self, build_path: Path, output_format: str) -> Dict[str, str]:
"""
Read generated output files
Args:
build_path: Directory containing built files
output_format: Output format used
Returns:
Dict of filename -> content
"""
files = {}
# Map format to expected file
format_files = {
"css": "theme.css",
"scss": "theme.scss",
"json": "theme.json",
"js": "theme.js"
}
filename = format_files.get(output_format)
if filename:
filepath = build_path / filename
if filepath.exists():
with open(filepath, "r") as f:
files[filename] = f.read()
return files
def convert_tokens_to_css_vars(self, theme: Theme) -> str:
"""
Convert DSS theme tokens to CSS custom properties
Args:
theme: DSS Theme
Returns:
CSS string with :root variables
"""
css_lines = [":root {"]
for token_name, token in theme.tokens.items():
# Convert token name to CSS variable format
css_var_name = f"--{token_name}"
# Add comment if description exists
if token.description:
css_lines.append(f" /* {token.description} */")
css_lines.append(f" {css_var_name}: {token.value};")
css_lines.append("}")
return "\n".join(css_lines)
# Alias for backward compatibility
StyleDictionaryTool = StyleDictionaryWrapper

View File

@@ -1,295 +0,0 @@
#!/usr/bin/env python3
"""
DSS Database Migration Runner
This script runs SQL migrations in the correct order, with proper error handling
and transaction safety.
Usage:
python run_migrations.py # Run all pending migrations
python run_migrations.py --check # Show pending migrations only
python run_migrations.py --rollback 0001 # Rollback specific migration (CAREFUL!)
"""
import os
import sys
import sqlite3
import argparse
from pathlib import Path
from datetime import datetime
import json
# Add parent directory to path for imports
sys.path.insert(0, str(Path(__file__).parent.parent))
class MigrationRunner:
"""Manages database migrations with version tracking and rollback support"""
def __init__(self, db_path: Path = None):
"""
Initialize migration runner
Args:
db_path: Path to database file. If None, uses default DSS location.
"""
if db_path is None:
# Default DSS database location
db_path = Path.cwd() / ".dss" / "dss.db"
self.db_path = Path(db_path)
self.migrations_dir = Path(__file__).parent.parent / "dss" / "storage" / "migrations"
self.migrations_table = "_dss_migrations"
def _ensure_migrations_table(self, conn: sqlite3.Connection):
"""Create migrations tracking table if it doesn't exist"""
conn.execute(f"""
CREATE TABLE IF NOT EXISTS {self.migrations_table} (
id TEXT PRIMARY KEY,
applied_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
description TEXT,
checksum TEXT,
status TEXT DEFAULT 'applied'
)
""")
conn.commit()
def _get_migration_checksum(self, migration_file: Path) -> str:
"""Calculate checksum of migration file for integrity verification"""
import hashlib
content = migration_file.read_text()
return hashlib.sha256(content.encode()).hexdigest()
def _get_applied_migrations(self, conn: sqlite3.Connection) -> dict:
"""Get dictionary of applied migrations"""
cursor = conn.execute(f"SELECT id, checksum FROM {self.migrations_table}")
return {row[0]: row[1] for row in cursor.fetchall()}
def _get_pending_migrations(self) -> list:
"""Get list of pending migrations in order"""
applied = {}
try:
conn = sqlite3.connect(self.db_path)
self._ensure_migrations_table(conn)
applied = self._get_applied_migrations(conn)
conn.close()
except Exception as e:
print(f"Warning: Could not read migration history: {e}")
pending = []
if self.migrations_dir.exists():
for migration_file in sorted(self.migrations_dir.glob("*.sql")):
migration_id = migration_file.stem # e.g., "0002_add_uuid_columns"
if migration_id not in applied:
pending.append({
'id': migration_id,
'file': migration_file,
'checksum': self._get_migration_checksum(migration_file),
'status': 'pending'
})
return pending
def check(self) -> bool:
"""Check for pending migrations without applying them"""
pending = self._get_pending_migrations()
if not pending:
print("✓ No pending migrations - database is up to date")
return True
print(f"Found {len(pending)} pending migration(s):\n")
for migration in pending:
print(f" - {migration['id']}")
print(f" File: {migration['file'].name}")
print(f" Checksum: {migration['checksum'][:16]}...")
return False
def run(self, dry_run: bool = False) -> bool:
"""
Run all pending migrations
Args:
dry_run: If True, show migrations but don't apply them
Returns:
True if successful, False if any migration failed
"""
pending = self._get_pending_migrations()
if not pending:
print("✓ No pending migrations - database is up to date")
return True
print(f"Found {len(pending)} pending migration(s)")
if dry_run:
print("\nDRY RUN - No changes will be applied\n")
else:
print("Running migrations...\n")
# Backup database before running migrations
if not dry_run:
backup_path = self.db_path.with_suffix(f".backup-{datetime.now().strftime('%Y%m%d-%H%M%S')}")
import shutil
try:
shutil.copy2(self.db_path, backup_path)
print(f"✓ Database backed up to: {backup_path}\n")
except Exception as e:
print(f"✗ Failed to create backup: {e}")
print(" Aborting migration")
return False
conn = sqlite3.connect(self.db_path)
conn.row_factory = sqlite3.Row
self._ensure_migrations_table(conn)
try:
for migration in pending:
migration_id = migration['id']
migration_file = migration['file']
print(f"Running: {migration_id}")
# Read migration SQL
sql_content = migration_file.read_text()
if not dry_run:
try:
# Execute migration with transaction
conn.executescript(sql_content)
# Record migration as applied
conn.execute(f"""
INSERT INTO {self.migrations_table}
(id, description, checksum, status)
VALUES (?, ?, ?, 'applied')
""", (migration_id, migration_file.name, migration['checksum']))
conn.commit()
print(f" ✓ Migration applied successfully\n")
except sqlite3.Error as e:
conn.rollback()
print(f" ✗ Migration failed: {e}")
print(f" ✗ Changes rolled back")
return False
else:
# Dry run: just show what would happen
print(" (DRY RUN - Would execute)")
lines = sql_content.split('\n')[:5] # Show first 5 lines
for line in lines:
if line.strip() and not line.strip().startswith('--'):
print(f" {line[:70]}")
if len(sql_content.split('\n')) > 5:
print(f" ... ({len(sql_content.split(chr(10)))} lines total)")
print()
if not dry_run:
print("\n✓ All migrations applied successfully")
return True
else:
print("✓ Dry run complete - no changes made")
return True
finally:
conn.close()
def status(self) -> None:
"""Show migration status"""
try:
conn = sqlite3.connect(self.db_path)
self._ensure_migrations_table(conn)
cursor = conn.execute(f"""
SELECT id, applied_at, status FROM {self.migrations_table}
ORDER BY applied_at
""")
applied = cursor.fetchall()
print(f"Applied migrations ({len(applied)}):\n")
if applied:
for row in applied:
print(f"{row[0]}")
print(f" Applied at: {row[1]}")
else:
print(" (none)")
pending = self._get_pending_migrations()
print(f"\nPending migrations ({len(pending)}):\n")
if pending:
for migration in pending:
print(f"{migration['id']}")
else:
print(" (none)")
conn.close()
except Exception as e:
print(f"Error reading migration status: {e}")
def main():
parser = argparse.ArgumentParser(
description="DSS Database Migration Runner",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
python run_migrations.py # Run all pending migrations
python run_migrations.py --check # Check for pending migrations
python run_migrations.py --dry-run # Show what would be applied
python run_migrations.py --status # Show migration status
"""
)
parser.add_argument(
'--check',
action='store_true',
help='Check for pending migrations without applying them'
)
parser.add_argument(
'--dry-run',
action='store_true',
help='Show migrations that would be applied without applying them'
)
parser.add_argument(
'--status',
action='store_true',
help='Show migration status (applied and pending)'
)
parser.add_argument(
'--db',
type=Path,
help='Path to database file (default: .dss/dss.db)'
)
args = parser.parse_args()
runner = MigrationRunner(db_path=args.db)
try:
if args.status:
runner.status()
return 0
elif args.check:
success = runner.check()
return 0 if success else 1
elif args.dry_run:
success = runner.run(dry_run=True)
return 0 if success else 1
else:
# Run migrations
success = runner.run(dry_run=False)
return 0 if success else 1
except KeyboardInterrupt:
print("\nMigration cancelled by user")
return 1
except Exception as e:
print(f"Error: {e}")
import traceback
traceback.print_exc()
return 1
if __name__ == '__main__':
sys.exit(main())

View File

@@ -1,121 +0,0 @@
#!/bin/bash
# DSS MVP1 Setup Script
# Configures DSS for dss.overbits.luz.uy
set -e # Exit on error
echo "🚀 DSS MVP1 Setup for dss.overbits.luz.uy"
echo "=========================================="
echo ""
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# Check if running as overbits user
if [ "$USER" != "overbits" ]; then
echo -e "${YELLOW}⚠️ Warning: This script is designed for user 'overbits'${NC}"
echo -e " Current user: $USER"
read -p "Continue anyway? (y/n) " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
exit 1
fi
fi
# 1. Check dependencies
echo "📦 Checking dependencies..."
python3 -m dss.settings check-deps || {
echo -e "${RED}❌ Dependency check failed${NC}"
echo " Installing Python dependencies..."
pip install -r requirements.txt
echo " Installing Node dependencies..."
npm install
}
# 2. Create necessary directories
echo ""
echo "📁 Creating directories..."
mkdir -p ~/.dss/{cache,logs,backups}
mkdir -p dist/tokens
mkdir -p components
echo -e "${GREEN}✅ Directories created${NC}"
# 3. Check for API keys
echo ""
echo "🔑 Checking API keys..."
if [ ! -f .env ]; then
echo -e "${YELLOW}⚠️ .env file not found - already created${NC}"
fi
# Check if keys are configured
if grep -q "^ANTHROPIC_API_KEY=$" .env 2>/dev/null; then
echo -e "${YELLOW}⚠️ ANTHROPIC_API_KEY not set in .env${NC}"
echo " Get your key from: https://console.anthropic.com/settings/keys"
fi
if grep -q "^FIGMA_TOKEN=$" .env 2>/dev/null; then
echo -e "${YELLOW}⚠️ FIGMA_TOKEN not set in .env${NC}"
echo " Get your token from: https://www.figma.com/developers/api#access-tokens"
fi
if grep -q "^JWT_SECRET=$" .env 2>/dev/null; then
echo -e "${YELLOW}⚠️ JWT_SECRET not set in .env${NC}"
echo " Generate with: openssl rand -hex 32"
read -p "Generate JWT_SECRET now? (y/n) " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
JWT_SECRET=$(openssl rand -hex 32)
sed -i "s/^JWT_SECRET=$/JWT_SECRET=$JWT_SECRET/" .env
echo -e "${GREEN}✅ JWT_SECRET generated and saved to .env${NC}"
fi
fi
# 4. Run tests
echo ""
read -p "Run tests to verify setup? (y/n) " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
echo "🧪 Running tests..."
python3 -m dss.settings test || {
echo -e "${RED}❌ Tests failed${NC}"
echo " Check the output above for errors"
exit 1
}
echo -e "${GREEN}✅ All tests passed!${NC}"
fi
# 5. System info
echo ""
echo "📊 System Information:"
python3 -m dss.settings info
# 6. Final instructions
echo ""
echo "=========================================="
echo -e "${GREEN}✅ Setup Complete!${NC}"
echo ""
echo "Next steps:"
echo " 1. Add your API keys to .env:"
echo " - ANTHROPIC_API_KEY (https://console.anthropic.com/settings/keys)"
echo " - FIGMA_TOKEN (https://www.figma.com/developers/api#access-tokens)"
echo ""
echo " 2. Configure your Figma file:"
echo " - Add FIGMA_FILE_KEY to .env"
echo " - Format: figma.com/file/{FILE_KEY}/..."
echo ""
echo " 3. Start the server:"
echo " python3 -m uvicorn dss.api.server:app --host 0.0.0.0 --port 3456"
echo ""
echo " 4. Visit: https://dss.overbits.luz.uy"
echo ""
echo "Commands:"
echo " python3 -m dss.settings test # Run tests"
echo " python3 -m dss.settings reset # Reset to fresh state"
echo " python3 -m dss.settings info # Show system info"
echo ""
echo "See SETTINGS.md for full documentation"
echo "=========================================="

View File

@@ -1,40 +0,0 @@
export default {
title: "DSS/Welcome",
tags: ["autodocs"],
};
export const GettingStarted = {
render: () => {
const div = document.createElement("div");
div.innerHTML = `
<div style="font-family: system-ui; padding: 2rem; max-width: 700px;">
<h1 style="color: #1a1a2e; margin-bottom: 1rem;">Design System Server</h1>
<p style="color: #666; font-size: 1.1rem; line-height: 1.6;">
Welcome to DSS Storybook. This is your interactive component library.
</p>
<div style="background: #f8f9fa; border-radius: 8px; padding: 1.5rem; margin: 1.5rem 0;">
<h2 style="color: #1a1a2e; font-size: 1.2rem; margin-bottom: 1rem;">Getting Started</h2>
<ol style="color: #444; line-height: 2;">
<li>Go to the <strong>Admin UI</strong> to configure your design system</li>
<li>Import components from Figma or your component library</li>
<li>Click <strong>"Initialize Storybook"</strong> to generate component stories</li>
<li>Browse your components in the sidebar</li>
</ol>
</div>
<div style="background: #e8f4fd; border-left: 4px solid #0066cc; padding: 1rem; margin: 1.5rem 0;">
<strong style="color: #0066cc;">No components loaded yet</strong>
<p style="color: #444; margin: 0.5rem 0 0 0;">
Initialize your design system from the Admin UI to populate this Storybook.
</p>
</div>
<p style="color: #888; font-size: 0.9rem; margin-top: 2rem;">
DSS v1.0.0 | <a href="/admin-ui/" style="color: #0066cc;">Open Admin UI</a>
</p>
</div>
`;
return div;
},
};

View File

@@ -1,82 +0,0 @@
import pytest
from pathlib import Path
@pytest.fixture(scope="function")
def mock_react_project(tmp_path: Path) -> Path:
"""
Creates a temporary mock React project structure for testing.
"""
project_dir = tmp_path / "test-project"
project_dir.mkdir()
# Create src directory
src_dir = project_dir / "src"
src_dir.mkdir()
# Create components directory
components_dir = src_dir / "components"
components_dir.mkdir()
# Component A
(components_dir / "ComponentA.jsx").write_text("""
import React from 'react';
import './ComponentA.css';
const ComponentA = () => {
return <div className="component-a">Component A</div>;
};
export default ComponentA;
""")
(components_dir / "ComponentA.css").write_text("""
.component-a {
color: blue;
}
""")
# Component B
(components_dir / "ComponentB.tsx").write_text("""
import React from 'react';
import ComponentA from './ComponentA';
const ComponentB = () => {
return (
<div>
<ComponentA />
</div>
);
};
export default ComponentB;
""")
# App.js
(src_dir / "App.js").write_text("""
import React from 'react';
import ComponentB from './components/ComponentB';
function App() {
return (
<div className="App">
<ComponentB />
</div>
);
}
export default App;
""")
# package.json
(project_dir / "package.json").write_text("""
{
"name": "test-project",
"version": "0.1.0",
"private": true,
"dependencies": {
"react": "^18.0.0"
}
}
""")
return project_dir

151
dss/__init__.py Normal file
View File

@@ -0,0 +1,151 @@
"""
DSS - Design System Server
A Model Context Protocol (MCP) server that provides Claude Code with 40+ design system tools.
Supports local development and remote team deployment.
Usage:
from dss import settings, Projects, Components
from dss.mcp import MCPServer
from dss.storage import Projects, Components, Tokens
"""
__version__ = "1.0.0"
# Settings & Configuration
from dss.settings import settings, DSSSettings, DSSManager, manager
# Storage Layer
from dss.storage.json_store import (
Projects,
Components,
Tokens,
Styles,
SyncHistory,
ActivityLog,
Teams,
Cache,
FigmaFiles,
CodeMetrics,
TestResults,
TokenDrift,
Integrations,
IntegrationHealth,
get_stats,
)
# Analyze
from dss.analyze.base import (
ProjectAnalysis,
QuickWin,
ComponentInfo,
StylePattern,
Framework,
StylingApproach,
)
from dss.analyze.scanner import ProjectScanner
# Ingest
from dss.ingest.base import (
DesignToken,
TokenCollection,
TokenSource,
TokenType,
TokenCategory,
)
# Export/Import
from dss.export_import.service import DSSArchiveExporter, DSSArchiveImporter
from dss.export_import.merger import SmartMerger
# Storybook
from dss.storybook.generator import StoryGenerator
from dss.storybook.scanner import StorybookScanner
# Translations
from dss.translations import TranslationDictionary, TokenResolver
# Services
from dss.services.project_manager import ProjectManager
from dss.services.config_service import ConfigService, DSSConfig
from dss.services.sandboxed_fs import SandboxedFS
# Figma
from dss.figma.figma_tools import FigmaToolSuite
# Project
from dss.project.manager import DSSProject
# Models
from dss.models.theme import Theme
from dss.models.component import Component
from dss.models.project import Project
# Validators
from dss.validators.schema import ProjectValidator, ValidationResult
__all__ = [
# Version
"__version__",
# Settings
"settings",
"DSSSettings",
"DSSManager",
"manager",
# Storage
"Projects",
"Components",
"Tokens",
"Styles",
"SyncHistory",
"ActivityLog",
"Teams",
"Cache",
"FigmaFiles",
"CodeMetrics",
"TestResults",
"TokenDrift",
"Integrations",
"IntegrationHealth",
"get_stats",
# Analyze
"ProjectAnalysis",
"QuickWin",
"ComponentInfo",
"StylePattern",
"Framework",
"StylingApproach",
"ProjectScanner",
# Ingest
"DesignToken",
"TokenCollection",
"TokenSource",
"TokenType",
"TokenCategory",
# Export/Import
"DSSArchiveExporter",
"DSSArchiveImporter",
"SmartMerger",
# Storybook
"StoryGenerator",
"StorybookScanner",
# Translations
"TranslationDictionary",
"TokenResolver",
# Services
"ProjectManager",
"ConfigService",
"DSSConfig",
"SandboxedFS",
# Figma
"FigmaToolSuite",
# Project
"DSSProject",
# Models
"Theme",
"Component",
"Project",
# Validators
"ProjectValidator",
"ValidationResult",
]

View File

@@ -12,7 +12,7 @@ from datetime import datetime, timedelta
from typing import Optional, Dict, Any
from atlassian import Jira, Confluence
from storage.json_store import read_json, write_json, SYSTEM_DIR
from dss.storage.json_store import read_json, write_json, SYSTEM_DIR
class AtlassianAuth:

View File

@@ -10,7 +10,7 @@ from .security import TimestampConflictResolver
from ..models.project import Project
from ..models.theme import DesignToken
from ..models.component import Component
from storage.json_store import Projects, Components, Tokens
from dss.storage.json_store import Projects, Components, Tokens
MergeStrategy = Literal["overwrite", "keep_local", "fork", "skip"]

View File

@@ -20,7 +20,7 @@ from .importer import DSSArchiveImporter, ImportAnalysis
from .merger import SmartMerger, ConflictResolutionMode, MergeAnalysis
from .security import DatabaseLockingStrategy, MemoryLimitManager
from ..models.project import Project
from storage.json_store import Projects, ActivityLog
from dss.storage.json_store import Projects, ActivityLog
@dataclass

0
dss/figma/__init__.py Normal file
View File

View File

@@ -19,18 +19,14 @@ Tools:
import json
import hashlib
import asyncio
import sys
from datetime import datetime
from typing import Optional, Dict, List, Any
from dataclasses import dataclass, asdict
from pathlib import Path
import httpx
# Add parent to path for imports
sys.path.insert(0, str(Path(__file__).parent.parent))
from config import config
from storage.json_store import Cache, ActivityLog
from dss.settings import settings
from dss.storage.json_store import Cache, ActivityLog
@dataclass
class DesignToken:
@@ -67,9 +63,9 @@ class FigmaClient:
"""
def __init__(self, token: Optional[str] = None):
self.token = token or config.figma.token
self.token = token or settings.FIGMA_TOKEN
self.base_url = "https://api.figma.com/v1"
self.cache_ttl = config.figma.cache_ttl
self.cache_ttl = settings.FIGMA_CACHE_TTL
self._use_real_api = bool(self.token)
def _cache_key(self, endpoint: str) -> str:

View File

@@ -11,7 +11,7 @@ from typing import Optional, Dict, Any
from datetime import datetime
from enum import Enum
from storage.json_store import ActivityLog, append_jsonl, read_jsonl, SYSTEM_DIR # JSON storage
from dss.storage.json_store import ActivityLog, append_jsonl, read_jsonl, SYSTEM_DIR # JSON storage
class AuditEventType(Enum):

View File

View File

@@ -17,7 +17,7 @@ from pathlib import Path
import sys
sys.path.insert(0, str(Path(__file__).parent.parent.parent))
from storage.json_store import Projects, Components, Tokens
from dss.storage.json_store import Projects, Components, Tokens
from analyze.scanner import ProjectScanner
from ..config import mcp_config

View File

@@ -22,7 +22,7 @@ from pathlib import Path
# Note: sys.path is set up by the importing module (server.py)
# Do NOT modify sys.path here as it causes relative import issues
from storage.json_store import Projects, ActivityLog
from dss.storage.json_store import Projects, ActivityLog
from .config import mcp_config, integration_config
from .context.project_context import get_context_manager, ProjectContext
from .tools.project_tools import PROJECT_TOOLS, ProjectTools

View File

View File

@@ -12,7 +12,7 @@ from datetime import datetime, timedelta
from enum import Enum
from ..config import mcp_config
from storage.json_store import Cache, read_json, write_json, SYSTEM_DIR
from dss.storage.json_store import Cache, read_json, write_json, SYSTEM_DIR
class CircuitState(Enum):

View File

@@ -13,7 +13,7 @@ from datetime import datetime
from enum import Enum
from .config import mcp_config
from storage.json_store import ActivityLog, read_json, write_json, DATA_DIR # JSON storage
from dss.storage.json_store import ActivityLog, read_json, write_json, DATA_DIR # JSON storage
class OperationStatus(Enum):

Some files were not shown because too many files have changed in this diff Show More