designlang 2.0.0 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +138 -77
- package/bin/design-extract.js +272 -2
- package/designlang.png +0 -0
- package/package.json +1 -1
- package/skills/extract-design/SKILL.md +52 -25
- package/src/clone.js +218 -0
- package/src/crawler.js +7 -0
- package/src/extractors/components.js +86 -0
- package/src/extractors/interactions.js +128 -0
- package/src/extractors/layout.js +114 -0
- package/src/extractors/responsive.js +132 -0
- package/src/extractors/scoring.js +132 -0
- package/src/formatters/markdown.js +178 -0
- package/src/index.js +13 -0
- package/src/multibrand.js +151 -0
- package/src/sync.js +69 -0
- package/src/watch.js +47 -0
package/src/sync.js
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
// Sync command — watch a live site and auto-update local tokens when design changes
|
|
2
|
+
|
|
3
|
+
import { extractDesignLanguage } from './index.js';
|
|
4
|
+
import { formatTokens } from './formatters/tokens.js';
|
|
5
|
+
import { formatTailwind } from './formatters/tailwind.js';
|
|
6
|
+
import { formatCssVars } from './formatters/css-vars.js';
|
|
7
|
+
import { saveSnapshot, getHistory } from './history.js';
|
|
8
|
+
import { writeFileSync, readFileSync, existsSync } from 'fs';
|
|
9
|
+
import { join } from 'path';
|
|
10
|
+
|
|
11
|
+
export async function syncDesign(url, options = {}) {
|
|
12
|
+
const { out = '.', interval = 3600000 } = options; // default 1 hour
|
|
13
|
+
|
|
14
|
+
const current = await extractDesignLanguage(url, options);
|
|
15
|
+
const history = getHistory(url);
|
|
16
|
+
const previous = history.length > 1 ? history[history.length - 2] : null;
|
|
17
|
+
|
|
18
|
+
const changes = [];
|
|
19
|
+
|
|
20
|
+
if (previous) {
|
|
21
|
+
// Detect changes
|
|
22
|
+
if (previous.colors.primary !== current.colors.primary?.hex) {
|
|
23
|
+
changes.push({ type: 'color', property: 'primary', from: previous.colors.primary, to: current.colors.primary?.hex });
|
|
24
|
+
}
|
|
25
|
+
if (previous.colors.secondary !== current.colors.secondary?.hex) {
|
|
26
|
+
changes.push({ type: 'color', property: 'secondary', from: previous.colors.secondary, to: current.colors.secondary?.hex });
|
|
27
|
+
}
|
|
28
|
+
if (previous.typography.families.join(',') !== current.typography.families.map(f => f.name).join(',')) {
|
|
29
|
+
changes.push({ type: 'typography', property: 'fonts', from: previous.typography.families.join(', '), to: current.typography.families.map(f => f.name).join(', ') });
|
|
30
|
+
}
|
|
31
|
+
if (previous.colors.count !== current.colors.all.length) {
|
|
32
|
+
changes.push({ type: 'color', property: 'count', from: String(previous.colors.count), to: String(current.colors.all.length) });
|
|
33
|
+
}
|
|
34
|
+
if (previous.a11yScore !== current.accessibility?.score) {
|
|
35
|
+
changes.push({ type: 'accessibility', property: 'score', from: `${previous.a11yScore}%`, to: `${current.accessibility?.score}%` });
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Save snapshot
|
|
40
|
+
saveSnapshot(current);
|
|
41
|
+
|
|
42
|
+
// Update local files
|
|
43
|
+
const updates = [];
|
|
44
|
+
|
|
45
|
+
const tokensPath = join(out, 'design-tokens.json');
|
|
46
|
+
if (existsSync(tokensPath)) {
|
|
47
|
+
writeFileSync(tokensPath, formatTokens(current), 'utf-8');
|
|
48
|
+
updates.push('design-tokens.json');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const tailwindPath = join(out, 'tailwind.config.js');
|
|
52
|
+
if (existsSync(tailwindPath)) {
|
|
53
|
+
writeFileSync(tailwindPath, formatTailwind(current), 'utf-8');
|
|
54
|
+
updates.push('tailwind.config.js');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const cssPath = join(out, 'variables.css');
|
|
58
|
+
if (existsSync(cssPath)) {
|
|
59
|
+
writeFileSync(cssPath, formatCssVars(current), 'utf-8');
|
|
60
|
+
updates.push('variables.css');
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
changes,
|
|
65
|
+
updatedFiles: updates,
|
|
66
|
+
isFirstRun: !previous,
|
|
67
|
+
design: current,
|
|
68
|
+
};
|
|
69
|
+
}
|
package/src/watch.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// Watch command — monitor a site for design changes on a schedule
|
|
2
|
+
|
|
3
|
+
import { extractDesignLanguage } from './index.js';
|
|
4
|
+
import { saveSnapshot, getHistory } from './history.js';
|
|
5
|
+
|
|
6
|
+
export async function watchSite(url, options = {}) {
|
|
7
|
+
const { intervalMs = 3600000 } = options; // default 1 hour
|
|
8
|
+
|
|
9
|
+
const design = await extractDesignLanguage(url);
|
|
10
|
+
const history = getHistory(url);
|
|
11
|
+
const previous = history.length > 0 ? history[history.length - 1] : null;
|
|
12
|
+
|
|
13
|
+
const snapshot = saveSnapshot(design);
|
|
14
|
+
const changes = [];
|
|
15
|
+
|
|
16
|
+
if (previous) {
|
|
17
|
+
if (previous.colors.primary !== design.colors.primary?.hex) {
|
|
18
|
+
changes.push({ type: 'color', what: 'Primary color', from: previous.colors.primary, to: design.colors.primary?.hex });
|
|
19
|
+
}
|
|
20
|
+
if (previous.colors.secondary !== design.colors.secondary?.hex) {
|
|
21
|
+
changes.push({ type: 'color', what: 'Secondary color', from: previous.colors.secondary, to: design.colors.secondary?.hex });
|
|
22
|
+
}
|
|
23
|
+
if (previous.colors.count !== design.colors.all.length) {
|
|
24
|
+
changes.push({ type: 'color', what: 'Color count', from: String(previous.colors.count), to: String(design.colors.all.length) });
|
|
25
|
+
}
|
|
26
|
+
if (previous.typography.families.join(',') !== design.typography.families.map(f => f.name).join(',')) {
|
|
27
|
+
changes.push({ type: 'typography', what: 'Font families', from: previous.typography.families.join(', '), to: design.typography.families.map(f => f.name).join(', ') });
|
|
28
|
+
}
|
|
29
|
+
if (previous.a11yScore !== design.accessibility?.score) {
|
|
30
|
+
changes.push({ type: 'accessibility', what: 'A11y score', from: `${previous.a11yScore}%`, to: `${design.accessibility?.score}%` });
|
|
31
|
+
}
|
|
32
|
+
if (previous.spacing.base !== design.spacing.base) {
|
|
33
|
+
changes.push({ type: 'spacing', what: 'Spacing base', from: `${previous.spacing.base}px`, to: `${design.spacing.base}px` });
|
|
34
|
+
}
|
|
35
|
+
if (Math.abs(previous.cssVarCount - Object.values(design.variables).reduce((s, v) => s + Object.keys(v).length, 0)) > 10) {
|
|
36
|
+
const newCount = Object.values(design.variables).reduce((s, v) => s + Object.keys(v).length, 0);
|
|
37
|
+
changes.push({ type: 'tokens', what: 'CSS var count', from: String(previous.cssVarCount), to: String(newCount) });
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return {
|
|
42
|
+
changes,
|
|
43
|
+
isFirstRun: !previous,
|
|
44
|
+
snapshot,
|
|
45
|
+
design,
|
|
46
|
+
};
|
|
47
|
+
}
|