feat(dss-ui): Button component with design tokens only
Some checks failed
DSS Project Analysis / dss-context-update (push) Has been cancelled
Some checks failed
DSS Project Analysis / dss-context-update (push) Has been cancelled
- Button.css uses only CSS custom properties, no fallbacks - Token validator now blocks hardcoded values (strict_mode: true) - Hook scripts converted from ESM (.js) to CommonJS (.cjs) - Storybook unified config with HMR disabled for nginx proxy - Added dss-ui package with Figma-synced components and stories 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -56,7 +56,7 @@
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "node ${CLAUDE_PLUGIN_ROOT}/hooks/scripts/complexity-monitor.js",
|
||||
"command": "node ${CLAUDE_PLUGIN_ROOT}/hooks/scripts/complexity-monitor.cjs",
|
||||
"timeout": 5000,
|
||||
"continueOnError": true
|
||||
}
|
||||
@@ -85,7 +85,7 @@
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "node ${CLAUDE_PLUGIN_ROOT}/hooks/scripts/session-summary.js",
|
||||
"command": "node ${CLAUDE_PLUGIN_ROOT}/hooks/scripts/session-summary.cjs",
|
||||
"timeout": 10000,
|
||||
"continueOnError": true
|
||||
}
|
||||
@@ -100,7 +100,7 @@
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "node ${CLAUDE_PLUGIN_ROOT}/hooks/scripts/git-backup.js",
|
||||
"command": "node ${CLAUDE_PLUGIN_ROOT}/hooks/scripts/git-backup.cjs",
|
||||
"timeout": 10000,
|
||||
"continueOnError": false
|
||||
}
|
||||
|
||||
215
dss-claude-plugin/hooks/scripts/complexity-monitor.cjs
Executable file
215
dss-claude-plugin/hooks/scripts/complexity-monitor.cjs
Executable file
@@ -0,0 +1,215 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* DSS Complexity Monitor Hook
|
||||
* Tracks code complexity metrics and warns on high-complexity code.
|
||||
* Written from scratch for DSS.
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
// Configuration
|
||||
const DEFAULT_CONFIG = {
|
||||
complexity_monitor: {
|
||||
enabled: true,
|
||||
max_function_lines: 50,
|
||||
max_component_lines: 200,
|
||||
max_props: 10,
|
||||
max_nesting_depth: 4,
|
||||
warn_only: true
|
||||
}
|
||||
};
|
||||
|
||||
function loadConfig() {
|
||||
const configPath = path.join(process.env.HOME || '', '.dss', 'hooks-config.json');
|
||||
try {
|
||||
if (fs.existsSync(configPath)) {
|
||||
const userConfig = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
||||
return { ...DEFAULT_CONFIG, ...userConfig };
|
||||
}
|
||||
} catch (e) {
|
||||
// Use defaults
|
||||
}
|
||||
return DEFAULT_CONFIG;
|
||||
}
|
||||
|
||||
function countLines(content) {
|
||||
return content.split('\n').length;
|
||||
}
|
||||
|
||||
function countProps(content) {
|
||||
// Count props in interface/type definition
|
||||
const propsMatch = content.match(/(?:interface|type)\s+\w*Props[^{]*\{([^}]+)\}/);
|
||||
if (propsMatch) {
|
||||
const propsContent = propsMatch[1];
|
||||
// Count semicolons or newlines with property definitions
|
||||
const props = propsContent.split(/[;\n]/).filter(line => {
|
||||
const trimmed = line.trim();
|
||||
return trimmed && !trimmed.startsWith('//') && trimmed.includes(':');
|
||||
});
|
||||
return props.length;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
function countNestingDepth(content) {
|
||||
let maxDepth = 0;
|
||||
let currentDepth = 0;
|
||||
|
||||
for (const char of content) {
|
||||
if (char === '{' || char === '(') {
|
||||
currentDepth++;
|
||||
maxDepth = Math.max(maxDepth, currentDepth);
|
||||
} else if (char === '}' || char === ')') {
|
||||
currentDepth = Math.max(0, currentDepth - 1);
|
||||
}
|
||||
}
|
||||
|
||||
return maxDepth;
|
||||
}
|
||||
|
||||
function countFunctions(content) {
|
||||
const patterns = [
|
||||
/function\s+\w+\s*\([^)]*\)\s*\{/g,
|
||||
/const\s+\w+\s*=\s*(?:async\s*)?\([^)]*\)\s*=>/g,
|
||||
/const\s+\w+\s*=\s*(?:async\s*)?function/g
|
||||
];
|
||||
|
||||
let count = 0;
|
||||
for (const pattern of patterns) {
|
||||
const matches = content.match(pattern);
|
||||
if (matches) count += matches.length;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
function analyzeComplexity(content, filePath, config) {
|
||||
const issues = [];
|
||||
const monitorConfig = config.complexity_monitor || {};
|
||||
const ext = path.extname(filePath).toLowerCase();
|
||||
|
||||
// Only analyze JS/TS files
|
||||
if (!['.js', '.jsx', '.ts', '.tsx'].includes(ext)) {
|
||||
return issues;
|
||||
}
|
||||
|
||||
const lines = countLines(content);
|
||||
const props = countProps(content);
|
||||
const nesting = countNestingDepth(content);
|
||||
const functions = countFunctions(content);
|
||||
|
||||
// Check component size (for tsx/jsx files)
|
||||
if (['.tsx', '.jsx'].includes(ext)) {
|
||||
if (lines > monitorConfig.max_component_lines) {
|
||||
issues.push({
|
||||
type: 'component_size',
|
||||
severity: 'medium',
|
||||
message: `Component has ${lines} lines (max: ${monitorConfig.max_component_lines})`,
|
||||
suggestion: 'Consider breaking into smaller components'
|
||||
});
|
||||
}
|
||||
|
||||
if (props > monitorConfig.max_props) {
|
||||
issues.push({
|
||||
type: 'prop_count',
|
||||
severity: 'medium',
|
||||
message: `Component has ${props} props (max: ${monitorConfig.max_props})`,
|
||||
suggestion: 'Consider grouping related props or using composition'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Check nesting depth
|
||||
if (nesting > monitorConfig.max_nesting_depth) {
|
||||
issues.push({
|
||||
type: 'nesting_depth',
|
||||
severity: 'high',
|
||||
message: `Nesting depth of ${nesting} (max: ${monitorConfig.max_nesting_depth})`,
|
||||
suggestion: 'Extract nested logic into separate functions'
|
||||
});
|
||||
}
|
||||
|
||||
// Check function count (indicator of file doing too much)
|
||||
if (functions > 10) {
|
||||
issues.push({
|
||||
type: 'function_count',
|
||||
severity: 'low',
|
||||
message: `File contains ${functions} functions`,
|
||||
suggestion: 'Consider splitting into multiple modules'
|
||||
});
|
||||
}
|
||||
|
||||
return issues;
|
||||
}
|
||||
|
||||
function formatOutput(issues, filePath) {
|
||||
if (issues.length === 0) return '';
|
||||
|
||||
const severityIcons = {
|
||||
high: '[HIGH]',
|
||||
medium: '[MED]',
|
||||
low: '[LOW]'
|
||||
};
|
||||
|
||||
const lines = [`\n=== DSS Complexity Monitor: ${filePath} ===\n`];
|
||||
|
||||
for (const issue of issues) {
|
||||
const icon = severityIcons[issue.severity] || '[?]';
|
||||
lines.push(`${icon} ${issue.message}`);
|
||||
lines.push(` Suggestion: ${issue.suggestion}\n`);
|
||||
}
|
||||
|
||||
lines.push('='.repeat(50));
|
||||
return lines.join('\n');
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const config = loadConfig();
|
||||
|
||||
if (!config.complexity_monitor?.enabled) {
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
// Read input from stdin
|
||||
let inputData;
|
||||
try {
|
||||
const chunks = [];
|
||||
for await (const chunk of process.stdin) {
|
||||
chunks.push(chunk);
|
||||
}
|
||||
inputData = JSON.parse(Buffer.concat(chunks).toString());
|
||||
} catch (e) {
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
const toolName = inputData.tool_name || '';
|
||||
const toolInput = inputData.tool_input || {};
|
||||
|
||||
if (!['Edit', 'Write'].includes(toolName)) {
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
const filePath = toolInput.file_path || '';
|
||||
let content = '';
|
||||
|
||||
if (toolName === 'Write') {
|
||||
content = toolInput.content || '';
|
||||
} else if (toolName === 'Edit') {
|
||||
content = toolInput.new_string || '';
|
||||
}
|
||||
|
||||
if (!content || !filePath) {
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
const issues = analyzeComplexity(content, filePath, config);
|
||||
|
||||
if (issues.length > 0) {
|
||||
const output = formatOutput(issues, filePath);
|
||||
console.error(output);
|
||||
}
|
||||
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
main().catch(() => process.exit(0));
|
||||
182
dss-claude-plugin/hooks/scripts/git-backup.cjs
Executable file
182
dss-claude-plugin/hooks/scripts/git-backup.cjs
Executable file
@@ -0,0 +1,182 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* DSS Git Backup Hook
|
||||
* Automatically commits changes when Claude Code session ends.
|
||||
* Written from scratch for DSS.
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { execSync } = require('child_process');
|
||||
|
||||
// Configuration
|
||||
const DEFAULT_CONFIG = {
|
||||
git_backup: {
|
||||
enabled: true,
|
||||
require_git_repo: true,
|
||||
commit_only_if_changes: true,
|
||||
include_timestamp: true,
|
||||
commit_prefix: 'auto-backup',
|
||||
show_logs: false
|
||||
}
|
||||
};
|
||||
|
||||
// Prevent duplicate execution (Claude Code bug workaround)
|
||||
const STATE_DIR = path.join(__dirname, '..', '.state');
|
||||
const LOCK_FILE = path.join(STATE_DIR, '.git-backup.lock');
|
||||
const LOCK_TIMEOUT_MS = 3000;
|
||||
|
||||
function loadConfig() {
|
||||
const configPath = path.join(process.env.HOME || '', '.dss', 'hooks-config.json');
|
||||
try {
|
||||
if (fs.existsSync(configPath)) {
|
||||
const userConfig = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
||||
return { ...DEFAULT_CONFIG, ...userConfig };
|
||||
}
|
||||
} catch (e) {
|
||||
// Use defaults
|
||||
}
|
||||
return DEFAULT_CONFIG;
|
||||
}
|
||||
|
||||
function checkLock() {
|
||||
try {
|
||||
if (!fs.existsSync(STATE_DIR)) {
|
||||
fs.mkdirSync(STATE_DIR, { recursive: true });
|
||||
}
|
||||
|
||||
if (fs.existsSync(LOCK_FILE)) {
|
||||
const lastRun = parseInt(fs.readFileSync(LOCK_FILE, 'utf8'));
|
||||
if (!isNaN(lastRun) && (Date.now() - lastRun < LOCK_TIMEOUT_MS)) {
|
||||
return false; // Already ran recently
|
||||
}
|
||||
}
|
||||
|
||||
fs.writeFileSync(LOCK_FILE, Date.now().toString(), 'utf8');
|
||||
return true;
|
||||
} catch (e) {
|
||||
return true; // Proceed on error
|
||||
}
|
||||
}
|
||||
|
||||
function isGitRepo() {
|
||||
try {
|
||||
execSync('git rev-parse --is-inside-work-tree', { stdio: 'pipe' });
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function hasChanges() {
|
||||
try {
|
||||
const status = execSync('git status --porcelain', { encoding: 'utf8' });
|
||||
return status.trim().length > 0;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function getChangeSummary() {
|
||||
try {
|
||||
const status = execSync('git status --short', { encoding: 'utf8' });
|
||||
const lines = status.trim().split('\n').filter(Boolean);
|
||||
|
||||
let added = 0, modified = 0, deleted = 0;
|
||||
|
||||
for (const line of lines) {
|
||||
const status = line.trim().charAt(0);
|
||||
if (status === 'A' || status === '?') added++;
|
||||
else if (status === 'M') modified++;
|
||||
else if (status === 'D') deleted++;
|
||||
}
|
||||
|
||||
return { added, modified, deleted, total: lines.length };
|
||||
} catch (e) {
|
||||
return { added: 0, modified: 0, deleted: 0, total: 0 };
|
||||
}
|
||||
}
|
||||
|
||||
function createBackup(config) {
|
||||
const backupConfig = config.git_backup || {};
|
||||
|
||||
try {
|
||||
// Stage all changes
|
||||
execSync('git add -A', { stdio: 'pipe' });
|
||||
|
||||
// Build commit message
|
||||
const parts = [backupConfig.commit_prefix || 'auto-backup'];
|
||||
|
||||
if (backupConfig.include_timestamp) {
|
||||
const timestamp = new Date().toISOString().replace('T', ' ').replace(/\..+/, '');
|
||||
parts.push(timestamp);
|
||||
}
|
||||
|
||||
const summary = getChangeSummary();
|
||||
const summaryText = `(${summary.total} files: +${summary.added} ~${summary.modified} -${summary.deleted})`;
|
||||
|
||||
const commitMessage = `${parts.join(': ')} ${summaryText}\n\nGenerated by DSS Git Backup Hook`;
|
||||
|
||||
// Create commit
|
||||
execSync(`git commit -m "${commitMessage}"`, { stdio: 'pipe' });
|
||||
|
||||
// Get commit hash
|
||||
const commitHash = execSync('git rev-parse --short HEAD', { encoding: 'utf8' }).trim();
|
||||
|
||||
return { success: true, hash: commitHash, files: summary.total };
|
||||
} catch (e) {
|
||||
return { success: false, error: e.message };
|
||||
}
|
||||
}
|
||||
|
||||
function log(config, message) {
|
||||
if (config.git_backup?.show_logs) {
|
||||
console.log(JSON.stringify({
|
||||
systemMessage: message,
|
||||
continue: true
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
function main() {
|
||||
// Prevent duplicate execution
|
||||
if (!checkLock()) {
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
// Prevent hook recursion
|
||||
if (process.env.STOP_HOOK_ACTIVE === 'true') {
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
const config = loadConfig();
|
||||
|
||||
if (!config.git_backup?.enabled) {
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
// Check for git repo
|
||||
if (config.git_backup.require_git_repo && !isGitRepo()) {
|
||||
log(config, 'DSS Git Backup: Not a git repository, skipping');
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
// Check for changes
|
||||
if (config.git_backup.commit_only_if_changes && !hasChanges()) {
|
||||
log(config, 'DSS Git Backup: No changes to commit');
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
// Create backup
|
||||
const result = createBackup(config);
|
||||
|
||||
if (result.success) {
|
||||
log(config, `DSS Git Backup: Committed ${result.files} files (${result.hash})`);
|
||||
} else {
|
||||
log(config, `DSS Git Backup: Failed - ${result.error}`);
|
||||
}
|
||||
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
main();
|
||||
194
dss-claude-plugin/hooks/scripts/session-summary.cjs
Executable file
194
dss-claude-plugin/hooks/scripts/session-summary.cjs
Executable file
@@ -0,0 +1,194 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* DSS Session Summary Hook
|
||||
* Generates a summary report at the end of each Claude Code session.
|
||||
* Written from scratch for DSS.
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { execSync } = require('child_process');
|
||||
|
||||
// Configuration
|
||||
const DEFAULT_CONFIG = {
|
||||
session_summary: {
|
||||
enabled: true,
|
||||
output_file: '.dss-session-summary.md',
|
||||
include_git_diff: true,
|
||||
include_file_list: true,
|
||||
max_diff_lines: 100
|
||||
}
|
||||
};
|
||||
|
||||
function loadConfig() {
|
||||
const configPath = path.join(process.env.HOME || '', '.dss', 'hooks-config.json');
|
||||
try {
|
||||
if (fs.existsSync(configPath)) {
|
||||
const userConfig = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
||||
return { ...DEFAULT_CONFIG, ...userConfig };
|
||||
}
|
||||
} catch (e) {
|
||||
// Use defaults
|
||||
}
|
||||
return DEFAULT_CONFIG;
|
||||
}
|
||||
|
||||
function getGitInfo() {
|
||||
const info = {
|
||||
branch: '',
|
||||
status: '',
|
||||
diff: '',
|
||||
modifiedFiles: []
|
||||
};
|
||||
|
||||
try {
|
||||
// Check if in git repo
|
||||
execSync('git rev-parse --is-inside-work-tree', { stdio: 'pipe' });
|
||||
|
||||
// Get branch
|
||||
info.branch = execSync('git branch --show-current', { encoding: 'utf8' }).trim();
|
||||
|
||||
// Get status
|
||||
info.status = execSync('git status --short', { encoding: 'utf8' }).trim();
|
||||
|
||||
// Get modified files
|
||||
const statusLines = info.status.split('\n').filter(Boolean);
|
||||
info.modifiedFiles = statusLines.map(line => {
|
||||
const parts = line.trim().split(/\s+/);
|
||||
return {
|
||||
status: parts[0],
|
||||
file: parts.slice(1).join(' ')
|
||||
};
|
||||
});
|
||||
|
||||
// Get diff summary
|
||||
try {
|
||||
info.diff = execSync('git diff --stat', { encoding: 'utf8' }).trim();
|
||||
} catch (e) {
|
||||
info.diff = '';
|
||||
}
|
||||
} catch (e) {
|
||||
// Not a git repo or git not available
|
||||
}
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
function getSessionStats() {
|
||||
// Try to read from session state if available
|
||||
const stats = {
|
||||
startTime: new Date().toISOString(),
|
||||
filesModified: 0,
|
||||
linesAdded: 0,
|
||||
linesRemoved: 0
|
||||
};
|
||||
|
||||
try {
|
||||
// Get diff stats from git
|
||||
const diffStat = execSync('git diff --numstat', { encoding: 'utf8' });
|
||||
const lines = diffStat.trim().split('\n').filter(Boolean);
|
||||
|
||||
for (const line of lines) {
|
||||
const [added, removed] = line.split('\t');
|
||||
stats.linesAdded += parseInt(added) || 0;
|
||||
stats.linesRemoved += parseInt(removed) || 0;
|
||||
stats.filesModified++;
|
||||
}
|
||||
} catch (e) {
|
||||
// Git not available
|
||||
}
|
||||
|
||||
return stats;
|
||||
}
|
||||
|
||||
function generateReport(config) {
|
||||
const summaryConfig = config.session_summary || {};
|
||||
const gitInfo = getGitInfo();
|
||||
const stats = getSessionStats();
|
||||
|
||||
const timestamp = new Date().toLocaleString();
|
||||
const lines = [];
|
||||
|
||||
lines.push('# DSS Session Summary');
|
||||
lines.push(`\n**Generated:** ${timestamp}`);
|
||||
|
||||
if (gitInfo.branch) {
|
||||
lines.push(`**Branch:** ${gitInfo.branch}`);
|
||||
}
|
||||
|
||||
lines.push('\n## Changes Overview');
|
||||
lines.push('');
|
||||
lines.push(`- Files modified: ${stats.filesModified}`);
|
||||
lines.push(`- Lines added: +${stats.linesAdded}`);
|
||||
lines.push(`- Lines removed: -${stats.linesRemoved}`);
|
||||
|
||||
if (summaryConfig.include_file_list && gitInfo.modifiedFiles.length > 0) {
|
||||
lines.push('\n## Modified Files');
|
||||
lines.push('');
|
||||
lines.push('| Status | File |');
|
||||
lines.push('|--------|------|');
|
||||
|
||||
const statusLabels = {
|
||||
'M': 'Modified',
|
||||
'A': 'Added',
|
||||
'D': 'Deleted',
|
||||
'R': 'Renamed',
|
||||
'??': 'Untracked'
|
||||
};
|
||||
|
||||
for (const file of gitInfo.modifiedFiles.slice(0, 20)) {
|
||||
const label = statusLabels[file.status] || file.status;
|
||||
lines.push(`| ${label} | ${file.file} |`);
|
||||
}
|
||||
|
||||
if (gitInfo.modifiedFiles.length > 20) {
|
||||
lines.push(`| ... | +${gitInfo.modifiedFiles.length - 20} more files |`);
|
||||
}
|
||||
}
|
||||
|
||||
if (summaryConfig.include_git_diff && gitInfo.diff) {
|
||||
lines.push('\n## Diff Summary');
|
||||
lines.push('');
|
||||
lines.push('```');
|
||||
const diffLines = gitInfo.diff.split('\n');
|
||||
const maxLines = summaryConfig.max_diff_lines || 100;
|
||||
lines.push(diffLines.slice(0, maxLines).join('\n'));
|
||||
if (diffLines.length > maxLines) {
|
||||
lines.push(`... (${diffLines.length - maxLines} more lines)`);
|
||||
}
|
||||
lines.push('```');
|
||||
}
|
||||
|
||||
lines.push('\n---');
|
||||
lines.push('*Generated by DSS Session Summary Hook*');
|
||||
|
||||
return lines.join('\n');
|
||||
}
|
||||
|
||||
function main() {
|
||||
const config = loadConfig();
|
||||
|
||||
if (!config.session_summary?.enabled) {
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
try {
|
||||
const report = generateReport(config);
|
||||
const outputFile = config.session_summary.output_file || '.dss-session-summary.md';
|
||||
const outputPath = path.join(process.cwd(), outputFile);
|
||||
|
||||
fs.writeFileSync(outputPath, report, 'utf8');
|
||||
|
||||
// Output confirmation
|
||||
console.log(JSON.stringify({
|
||||
systemMessage: `Session summary saved to ${outputFile}`,
|
||||
continue: true
|
||||
}));
|
||||
} catch (e) {
|
||||
// Fail silently
|
||||
}
|
||||
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
main();
|
||||
@@ -105,12 +105,10 @@ HARDCODED_PATTERNS = [
|
||||
|
||||
# Allowlist patterns (common exceptions)
|
||||
ALLOWLIST = [
|
||||
r"#000000?", # Pure black
|
||||
r"#fff(fff)?", # Pure white
|
||||
r"transparent",
|
||||
r"inherit",
|
||||
r"currentColor",
|
||||
r"var\(--", # Already using CSS variables
|
||||
r"var\(--[^,)]+\)$", # CSS variables WITHOUT fallbacks only
|
||||
r"theme\.", # Already using theme
|
||||
r"colors\.", # Already using colors object
|
||||
]
|
||||
@@ -122,9 +120,9 @@ def get_config():
|
||||
default_config = {
|
||||
"token_validator": {
|
||||
"enabled": True,
|
||||
"strict_mode": False,
|
||||
"warn_only": True,
|
||||
"categories": ["color", "spacing", "typography"],
|
||||
"strict_mode": True, # Block writes with hardcoded values
|
||||
"warn_only": False, # Actually enforce token usage
|
||||
"categories": ["color", "spacing", "typography", "border", "effects"],
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user