designlang 11.0.0 → 11.0.2
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/bin/design-extract.js +15 -0
- package/package.json +1 -1
- package/src/formatters/prompt-pack.js +5 -5
- package/src/formatters/tailwind.js +1 -1
- package/src/multipage.js +2 -2
- package/src/widgets.js +48 -0
package/bin/design-extract.js
CHANGED
|
@@ -84,6 +84,7 @@ program
|
|
|
84
84
|
.option('--user-agent <ua>', 'override the browser User-Agent string')
|
|
85
85
|
.option('--insecure', 'ignore HTTPS/SSL certificate errors (self-signed, dev, proxies)')
|
|
86
86
|
.option('--ignore <selectors...>', 'CSS selectors to remove before extraction')
|
|
87
|
+
.option('--ignore-widgets', 'Also ignore a curated list of third-party widgets (Intercom, Drift, HubSpot chat, cookie banners, reCAPTCHA, etc.) See `designlang widgets`.')
|
|
87
88
|
.option('--selector <css>', 'only extract design from elements matching this CSS selector (e.g. ".pricing-card")')
|
|
88
89
|
.option('--system-chrome', 'use the system Chrome install instead of the bundled Chromium (skips the 150MB Playwright download)')
|
|
89
90
|
.option('--tokens-legacy', 'Emit pre-v7 flat token JSON (backward compat)')
|
|
@@ -106,6 +107,11 @@ program
|
|
|
106
107
|
const config = loadConfig();
|
|
107
108
|
const merged = mergeConfig(opts, config);
|
|
108
109
|
|
|
110
|
+
if (merged.ignoreWidgets || opts.ignoreWidgets) {
|
|
111
|
+
const { widgetIgnoreList } = await import('../src/widgets.js');
|
|
112
|
+
merged.ignore = [...(merged.ignore || []), ...widgetIgnoreList()];
|
|
113
|
+
}
|
|
114
|
+
|
|
109
115
|
// Validate URL
|
|
110
116
|
validateUrl(url);
|
|
111
117
|
|
|
@@ -1056,6 +1062,15 @@ program
|
|
|
1056
1062
|
}
|
|
1057
1063
|
});
|
|
1058
1064
|
|
|
1065
|
+
// ── Widgets — print the curated third-party widget ignore list ─
|
|
1066
|
+
program
|
|
1067
|
+
.command('widgets')
|
|
1068
|
+
.description('Print the curated widget-ignore selector list used by --ignore-widgets')
|
|
1069
|
+
.action(async () => {
|
|
1070
|
+
const { WIDGET_SELECTORS } = await import('../src/widgets.js');
|
|
1071
|
+
for (const s of WIDGET_SELECTORS) console.log(s);
|
|
1072
|
+
});
|
|
1073
|
+
|
|
1059
1074
|
// ── CI command — single PR-comment-ready report ────────────
|
|
1060
1075
|
program
|
|
1061
1076
|
.command('ci <url>')
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "designlang",
|
|
3
|
-
"version": "11.0.
|
|
3
|
+
"version": "11.0.2",
|
|
4
4
|
"description": "Extract the complete design language from any website and ship it — clone to a working Next.js starter, guard tokens with a CI drift bot, or browse everything in a local studio. Outputs W3C DTCG tokens, motion tokens, typed anatomy stubs, Tailwind config, and ready-to-paste v0 / Lovable / Cursor / Claude-Artifacts prompts.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -15,16 +15,16 @@ function typeFamilies(design) {
|
|
|
15
15
|
|
|
16
16
|
function scaleSnippet(scale = []) {
|
|
17
17
|
if (!scale.length) return '(not detected)';
|
|
18
|
-
return scale.slice(0, 8).map(s => (s.value
|
|
18
|
+
return scale.slice(0, 8).map(s => String(s.value ?? s) + (s.label ? ` (${s.label})` : '')).join(', ');
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
function radiiSnippet(borders) {
|
|
22
|
-
const r = (borders?.radii || []).slice(0, 6).map(x => (x.value
|
|
22
|
+
const r = (borders?.radii || []).slice(0, 6).map(x => String(x.value ?? x));
|
|
23
23
|
return r.length ? r.join(', ') : '(none)';
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
function shadowSnippet(shadows) {
|
|
27
|
-
const s = (shadows?.values || []).slice(0, 3).map(x => (x.value
|
|
27
|
+
const s = (shadows?.values || []).slice(0, 3).map(x => String(x.raw ?? x.value ?? x));
|
|
28
28
|
return s.length ? s.join(' | ') : '(none)';
|
|
29
29
|
}
|
|
30
30
|
|
|
@@ -138,8 +138,8 @@ export function formatCursorPrompt(design) {
|
|
|
138
138
|
'export const tokens = {',
|
|
139
139
|
` colors: [${b.colors.map(c => `'${c}'`).join(', ')}],`,
|
|
140
140
|
` fonts: [${b.fonts.map(f => `'${f}'`).join(', ')}],`,
|
|
141
|
-
` radii: [${(design.borders?.radii || []).slice(0, 6).map(r => `'${(r.value
|
|
142
|
-
` shadows: [${(design.shadows?.values || []).slice(0, 3).map(s => `'${(s.value
|
|
141
|
+
` radii: [${(design.borders?.radii || []).slice(0, 6).map(r => `'${String(r.value ?? r)}'`).join(', ')}],`,
|
|
142
|
+
` shadows: [${(design.shadows?.values || []).slice(0, 3).map(s => `'${String(s.raw ?? s.value ?? s).replace(/'/g, "\\'")}'`).join(', ')}],`,
|
|
143
143
|
'};',
|
|
144
144
|
'```',
|
|
145
145
|
'',
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { rgbToHex, rgbToHsl } from '../utils.js';
|
|
2
2
|
|
|
3
3
|
function generateColorScale(hex, parsed) {
|
|
4
|
-
const { h, s } = rgbToHsl(parsed);
|
|
4
|
+
const { h, s } = parsed.hsl ?? rgbToHsl(parsed.rgb);
|
|
5
5
|
const scale = {};
|
|
6
6
|
const levels = [
|
|
7
7
|
{ name: '50', l: 97 }, { name: '100', l: 94 }, { name: '200', l: 86 },
|
package/src/multipage.js
CHANGED
|
@@ -127,11 +127,11 @@ function typeSet(typography) {
|
|
|
127
127
|
}
|
|
128
128
|
|
|
129
129
|
function spaceSet(spacing) {
|
|
130
|
-
return new Set(((spacing?.scale) || []).map(s => (s.value
|
|
130
|
+
return new Set(((spacing?.scale) || []).map(s => String(s.value ?? s)));
|
|
131
131
|
}
|
|
132
132
|
|
|
133
133
|
function radiusSet(borders) {
|
|
134
|
-
return new Set(((borders?.radii) || []).map(r => (r.value
|
|
134
|
+
return new Set(((borders?.radii) || []).map(r => String(r.value ?? r)));
|
|
135
135
|
}
|
|
136
136
|
|
|
137
137
|
export function computeCrossPageConsistency(pages) {
|
package/src/widgets.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// Curated selector list for third-party widgets that pollute extractions.
|
|
2
|
+
// Enabled via --ignore-widgets; appended to user-supplied --ignore selectors.
|
|
3
|
+
//
|
|
4
|
+
// Criteria for inclusion: widget renders on tens of thousands of sites, uses a
|
|
5
|
+
// stable ID/attr hook, and its styles would skew token extraction (chat bubbles
|
|
6
|
+
// with off-brand radii/shadows, cookie banners with alarm colors, etc.).
|
|
7
|
+
|
|
8
|
+
export const WIDGET_SELECTORS = [
|
|
9
|
+
// Live chat / support
|
|
10
|
+
'#intercom-container', '#intercom-frame', 'iframe[name*="intercom"]',
|
|
11
|
+
'#drift-widget', '#drift-frame-controller', '#drift-frame-chat',
|
|
12
|
+
'#hubspot-messages-iframe-container',
|
|
13
|
+
'#crisp-chatbox', '.crisp-client',
|
|
14
|
+
'iframe[title*="Messaging"]', '#launcher', '[data-product="web_widget"]', // Zendesk
|
|
15
|
+
'#tawk-default', 'iframe[title*="chat window"]',
|
|
16
|
+
'#chat-widget-container', '#livechat-compact-container', // LiveChat
|
|
17
|
+
'#helpshift-iframe',
|
|
18
|
+
'iframe[src*="freshchat"]', '#fc_frame',
|
|
19
|
+
'iframe[src*="olark"]', '#olark-wrapper',
|
|
20
|
+
|
|
21
|
+
// Cookie / consent banners
|
|
22
|
+
'#CybotCookiebotDialog', '#CybotCookiebotDialogBody',
|
|
23
|
+
'#onetrust-banner-sdk', '#onetrust-consent-sdk', '#onetrust-pc-sdk',
|
|
24
|
+
'.termly-banner-top', '.termly-styles-banner',
|
|
25
|
+
'#cookiebanner', '#cookie-banner', '#cookie-notice',
|
|
26
|
+
'.cc-window', '.cc-banner', // Cookieconsent by Insites
|
|
27
|
+
'#usercentrics-root',
|
|
28
|
+
'#iubenda-cs-banner',
|
|
29
|
+
|
|
30
|
+
// reCAPTCHA / anti-bot (visible v2 badge)
|
|
31
|
+
'.grecaptcha-badge', 'iframe[src*="recaptcha"]',
|
|
32
|
+
|
|
33
|
+
// Analytics / pixel iframes (invisible but can leak tokens)
|
|
34
|
+
'iframe[src*="doubleclick"]', 'iframe[src*="googletagmanager"]',
|
|
35
|
+
'iframe[src*="facebook.com/tr"]',
|
|
36
|
+
|
|
37
|
+
// Social share floating bars
|
|
38
|
+
'.addthis_floating_style', '#addthis-smartlayers',
|
|
39
|
+
'.sharethis-inline-share-buttons',
|
|
40
|
+
|
|
41
|
+
// Generic catch-alls (last so specific IDs take priority in logs)
|
|
42
|
+
'[id^="chat-widget"]', '[class*="chat-widget"]',
|
|
43
|
+
'[aria-label*="cookie" i][role="dialog"]',
|
|
44
|
+
];
|
|
45
|
+
|
|
46
|
+
export function widgetIgnoreList() {
|
|
47
|
+
return [...WIDGET_SELECTORS];
|
|
48
|
+
}
|