skillui 1.1.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.
Files changed (59) hide show
  1. package/dist/cli.d.ts +3 -0
  2. package/dist/cli.js +202 -0
  3. package/dist/extractors/components.d.ts +11 -0
  4. package/dist/extractors/components.js +455 -0
  5. package/dist/extractors/framework.d.ts +4 -0
  6. package/dist/extractors/framework.js +126 -0
  7. package/dist/extractors/tokens/computed.d.ts +7 -0
  8. package/dist/extractors/tokens/computed.js +249 -0
  9. package/dist/extractors/tokens/css.d.ts +3 -0
  10. package/dist/extractors/tokens/css.js +510 -0
  11. package/dist/extractors/tokens/http-css.d.ts +14 -0
  12. package/dist/extractors/tokens/http-css.js +1689 -0
  13. package/dist/extractors/tokens/tailwind.d.ts +3 -0
  14. package/dist/extractors/tokens/tailwind.js +353 -0
  15. package/dist/extractors/tokens/tokens-file.d.ts +3 -0
  16. package/dist/extractors/tokens/tokens-file.js +229 -0
  17. package/dist/extractors/ultra/animations.d.ts +21 -0
  18. package/dist/extractors/ultra/animations.js +530 -0
  19. package/dist/extractors/ultra/components-dom.d.ts +13 -0
  20. package/dist/extractors/ultra/components-dom.js +152 -0
  21. package/dist/extractors/ultra/interactions.d.ts +14 -0
  22. package/dist/extractors/ultra/interactions.js +225 -0
  23. package/dist/extractors/ultra/layout.d.ts +14 -0
  24. package/dist/extractors/ultra/layout.js +126 -0
  25. package/dist/extractors/ultra/pages.d.ts +16 -0
  26. package/dist/extractors/ultra/pages.js +231 -0
  27. package/dist/font-resolver.d.ts +10 -0
  28. package/dist/font-resolver.js +280 -0
  29. package/dist/modes/dir.d.ts +6 -0
  30. package/dist/modes/dir.js +213 -0
  31. package/dist/modes/repo.d.ts +6 -0
  32. package/dist/modes/repo.js +76 -0
  33. package/dist/modes/ultra.d.ts +22 -0
  34. package/dist/modes/ultra.js +285 -0
  35. package/dist/modes/url.d.ts +14 -0
  36. package/dist/modes/url.js +161 -0
  37. package/dist/normalizer.d.ts +11 -0
  38. package/dist/normalizer.js +867 -0
  39. package/dist/screenshot.d.ts +9 -0
  40. package/dist/screenshot.js +94 -0
  41. package/dist/types-ultra.d.ts +157 -0
  42. package/dist/types-ultra.js +4 -0
  43. package/dist/types.d.ts +182 -0
  44. package/dist/types.js +4 -0
  45. package/dist/writers/animations-md.d.ts +17 -0
  46. package/dist/writers/animations-md.js +313 -0
  47. package/dist/writers/components-md.d.ts +8 -0
  48. package/dist/writers/components-md.js +151 -0
  49. package/dist/writers/design-md.d.ts +7 -0
  50. package/dist/writers/design-md.js +704 -0
  51. package/dist/writers/interactions-md.d.ts +8 -0
  52. package/dist/writers/interactions-md.js +146 -0
  53. package/dist/writers/layout-md.d.ts +8 -0
  54. package/dist/writers/layout-md.js +120 -0
  55. package/dist/writers/skill.d.ts +12 -0
  56. package/dist/writers/skill.js +1006 -0
  57. package/dist/writers/tokens-json.d.ts +11 -0
  58. package/dist/writers/tokens-json.js +164 -0
  59. package/package.json +78 -0
@@ -0,0 +1,152 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.detectDOMComponents = detectDOMComponents;
4
+ /**
5
+ * Ultra mode — DOM Component Detector
6
+ *
7
+ * Detects repeated UI components by analyzing DOM structure:
8
+ * - Elements with the same class pattern appearing 3+ times → component
9
+ * - Groups by normalized class fingerprint
10
+ * - Extracts representative HTML snippet
11
+ *
12
+ * Requires Playwright (optional peer dependency).
13
+ */
14
+ async function detectDOMComponents(url) {
15
+ let playwright;
16
+ try {
17
+ playwright = require('playwright');
18
+ }
19
+ catch {
20
+ return [];
21
+ }
22
+ const browser = await playwright.chromium.launch({ headless: true });
23
+ try {
24
+ const context = await browser.newContext({
25
+ viewport: { width: 1440, height: 900 },
26
+ userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
27
+ });
28
+ const page = await context.newPage();
29
+ await page.goto(url, { waitUntil: 'networkidle', timeout: 25000 });
30
+ await page.waitForTimeout(1000);
31
+ const components = await page.evaluate(() => {
32
+ // ── Fingerprint an element by its structure ───────────────────────
33
+ function fingerprint(el) {
34
+ const tag = el.tagName.toLowerCase();
35
+ // Normalize class names: remove dynamic/utility classes, sort
36
+ const stableClasses = Array.from(el.classList)
37
+ .filter(c => {
38
+ // Skip Tailwind utility classes, JS hooks, state classes
39
+ if (/^(js-|is-|has-|data-|aria-)/.test(c))
40
+ return false;
41
+ if (/^(hover:|focus:|active:|sm:|md:|lg:|xl:|2xl:)/.test(c))
42
+ return false;
43
+ // Keep semantic/BEM-like classes
44
+ return c.length >= 3 && c.length <= 40 && /^[a-zA-Z]/.test(c);
45
+ })
46
+ .sort()
47
+ .slice(0, 4);
48
+ const childTags = Array.from(el.children)
49
+ .slice(0, 4)
50
+ .map(c => c.tagName.toLowerCase())
51
+ .join(',');
52
+ return `${tag}[${stableClasses.join('.')}](${childTags})`;
53
+ }
54
+ // ── Serialize HTML snippet (truncated) ─────────────────────────────
55
+ function htmlSnippet(el) {
56
+ const clone = el.cloneNode(true);
57
+ // Remove deeply nested children to keep snippet readable
58
+ const children = clone.querySelectorAll('*');
59
+ if (children.length > 12) {
60
+ Array.from(children).slice(12).forEach(c => c.remove());
61
+ }
62
+ // Truncate text nodes
63
+ clone.querySelectorAll('*').forEach(n => {
64
+ if (n.children.length === 0 && n.textContent && n.textContent.length > 40) {
65
+ n.textContent = n.textContent.slice(0, 40) + '…';
66
+ }
67
+ });
68
+ return clone.outerHTML.replace(/\s+/g, ' ').slice(0, 600);
69
+ }
70
+ // ── Categorize a component ────────────────────────────────────────
71
+ function categorize(el, classes) {
72
+ const tag = el.tagName.toLowerCase();
73
+ const classStr = classes.join(' ').toLowerCase();
74
+ if (/card|tile|item|product|post/.test(classStr))
75
+ return 'card';
76
+ if (/nav.*item|menu.*item|tab/.test(classStr))
77
+ return 'nav-item';
78
+ if (tag === 'li' || /list.*item/.test(classStr))
79
+ return 'list-item';
80
+ if (tag === 'button' || /btn|button/.test(classStr))
81
+ return 'button';
82
+ if (/badge|tag|chip|label/.test(classStr))
83
+ return 'badge';
84
+ if (/field|input|form/.test(classStr))
85
+ return 'form-field';
86
+ return 'unknown';
87
+ }
88
+ // ── Walk all elements with 1+ class ─────────────────────────────────
89
+ const allElements = document.querySelectorAll('[class]');
90
+ const groups = new Map();
91
+ allElements.forEach(el => {
92
+ const rect = el.getBoundingClientRect();
93
+ // Must be visible and reasonably sized
94
+ if (rect.width < 40 || rect.height < 20)
95
+ return;
96
+ // Skip wrapper-only elements (body, html, main, etc.)
97
+ const tag = el.tagName.toLowerCase();
98
+ if (['html', 'body', 'main', 'head', 'script', 'style', 'link', 'meta'].includes(tag))
99
+ return;
100
+ const fp = fingerprint(el);
101
+ if (!groups.has(fp))
102
+ groups.set(fp, []);
103
+ groups.get(fp).push(el);
104
+ });
105
+ // ── Filter to repeated components (3+ instances) ──────────────────
106
+ const results = [];
107
+ for (const [fp, els] of groups.entries()) {
108
+ if (els.length < 3)
109
+ continue;
110
+ const representative = els[0];
111
+ const stableClasses = Array.from(representative.classList)
112
+ .filter(c => {
113
+ if (/^(js-|is-|has-)/.test(c))
114
+ return false;
115
+ if (/^(hover:|focus:|sm:|md:|lg:)/.test(c))
116
+ return false;
117
+ return c.length >= 3;
118
+ })
119
+ .sort()
120
+ .slice(0, 6);
121
+ // Generate a human-readable component name
122
+ const tag = representative.tagName.toLowerCase();
123
+ const mainClass = stableClasses[0] || tag;
124
+ const name = mainClass
125
+ .replace(/[-_]/g, ' ')
126
+ .replace(/\b\w/g, l => l.toUpperCase())
127
+ .trim() || tag;
128
+ const category = categorize(representative, stableClasses);
129
+ results.push({
130
+ name,
131
+ pattern: fp,
132
+ instances: els.length,
133
+ commonClasses: stableClasses,
134
+ htmlSnippet: htmlSnippet(representative),
135
+ category,
136
+ });
137
+ if (results.length >= 20)
138
+ break;
139
+ }
140
+ // Sort by instance count descending
141
+ return results.sort((a, b) => b.instances - a.instances);
142
+ });
143
+ await page.close();
144
+ await browser.close();
145
+ return components;
146
+ }
147
+ catch {
148
+ await browser.close().catch(() => { });
149
+ return [];
150
+ }
151
+ }
152
+ //# sourceMappingURL=components-dom.js.map
@@ -0,0 +1,14 @@
1
+ import { InteractionRecord } from '../../types-ultra';
2
+ /**
3
+ * Ultra mode — Micro-Interaction Extractor
4
+ *
5
+ * For each interactive element type (button, link, input, role-button):
6
+ * - Capture default screenshot
7
+ * - Simulate hover → capture screenshot + diff computed styles
8
+ * - Simulate focus → capture screenshot + diff computed styles
9
+ *
10
+ * Saves screenshots to screens/states/
11
+ * Returns InteractionRecord[] for INTERACTIONS.md generation.
12
+ */
13
+ export declare function captureInteractions(url: string, skillDir: string): Promise<InteractionRecord[]>;
14
+ //# sourceMappingURL=interactions.d.ts.map
@@ -0,0 +1,225 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.captureInteractions = captureInteractions;
37
+ const fs = __importStar(require("fs"));
38
+ const path = __importStar(require("path"));
39
+ const TRACKED_PROPS = [
40
+ 'backgroundColor',
41
+ 'color',
42
+ 'borderColor',
43
+ 'borderWidth',
44
+ 'boxShadow',
45
+ 'opacity',
46
+ 'transform',
47
+ 'outline',
48
+ 'outlineColor',
49
+ 'textDecoration',
50
+ 'transition',
51
+ ];
52
+ const INTERACTIVE_SELECTORS = [
53
+ { type: 'button', selector: 'button:not([disabled])' },
54
+ { type: 'role-button', selector: '[role="button"]:not([disabled])' },
55
+ { type: 'link', selector: 'a[href]:not([href^="#"]):not([href^="mailto"])' },
56
+ { type: 'input', selector: 'input:not([type="hidden"]):not([disabled])' },
57
+ ];
58
+ /**
59
+ * Ultra mode — Micro-Interaction Extractor
60
+ *
61
+ * For each interactive element type (button, link, input, role-button):
62
+ * - Capture default screenshot
63
+ * - Simulate hover → capture screenshot + diff computed styles
64
+ * - Simulate focus → capture screenshot + diff computed styles
65
+ *
66
+ * Saves screenshots to screens/states/
67
+ * Returns InteractionRecord[] for INTERACTIONS.md generation.
68
+ */
69
+ async function captureInteractions(url, skillDir) {
70
+ let playwright;
71
+ try {
72
+ playwright = require('playwright');
73
+ }
74
+ catch {
75
+ return [];
76
+ }
77
+ const statesDir = path.join(skillDir, 'screens', 'states');
78
+ fs.mkdirSync(statesDir, { recursive: true });
79
+ const records = [];
80
+ const browser = await playwright.chromium.launch({ headless: true });
81
+ try {
82
+ const context = await browser.newContext({
83
+ viewport: { width: 1440, height: 900 },
84
+ userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
85
+ });
86
+ const page = await context.newPage();
87
+ await page.goto(url, { waitUntil: 'networkidle', timeout: 25000 });
88
+ await page.waitForTimeout(1500);
89
+ for (const { type, selector } of INTERACTIVE_SELECTORS) {
90
+ try {
91
+ // Find up to 3 visible elements of this type
92
+ const elements = await page.locator(selector).all();
93
+ const visible = [];
94
+ for (const el of elements) {
95
+ try {
96
+ const box = await el.boundingBox();
97
+ if (box && box.width > 0 && box.height > 0 && box.width < 800) {
98
+ visible.push(el);
99
+ if (visible.length >= 3)
100
+ break;
101
+ }
102
+ }
103
+ catch { /* skip */ }
104
+ }
105
+ for (let i = 0; i < visible.length; i++) {
106
+ const el = visible[i];
107
+ const prefix = `${type}-${i + 1}`;
108
+ try {
109
+ // Get label text
110
+ const label = await el.evaluate((node) => {
111
+ const text = node.innerText?.trim() ||
112
+ node.getAttribute('aria-label') ||
113
+ node.getAttribute('placeholder') ||
114
+ node.getAttribute('type') ||
115
+ node.tagName.toLowerCase();
116
+ return text.slice(0, 40);
117
+ });
118
+ // ── Default state ─────────────────────────────────────
119
+ const defaultStyles = await getStyles(el);
120
+ const defaultFile = `${prefix}-default.png`;
121
+ await screenshotElement(el, path.join(statesDir, defaultFile));
122
+ // ── Hover state ───────────────────────────────────────
123
+ let hoverStyles = null;
124
+ let hoverFile;
125
+ try {
126
+ await el.hover({ force: true, timeout: 3000 });
127
+ await page.waitForTimeout(300);
128
+ hoverStyles = await getStyles(el);
129
+ hoverFile = `${prefix}-hover.png`;
130
+ await screenshotElement(el, path.join(statesDir, hoverFile));
131
+ // Move away
132
+ await page.mouse.move(0, 0);
133
+ await page.waitForTimeout(200);
134
+ }
135
+ catch { /* hover not supported */ }
136
+ // ── Focus state ───────────────────────────────────────
137
+ let focusStyles = null;
138
+ let focusFile;
139
+ try {
140
+ await el.focus({ timeout: 3000 });
141
+ await page.waitForTimeout(300);
142
+ focusStyles = await getStyles(el);
143
+ focusFile = `${prefix}-focus.png`;
144
+ await screenshotElement(el, path.join(statesDir, focusFile));
145
+ // Blur
146
+ await page.evaluate(() => document.activeElement?.blur?.());
147
+ await page.waitForTimeout(200);
148
+ }
149
+ catch { /* focus not supported */ }
150
+ const hoverChanges = hoverStyles
151
+ ? diffStyles(defaultStyles, hoverStyles)
152
+ : [];
153
+ const focusChanges = focusStyles
154
+ ? diffStyles(defaultStyles, focusStyles)
155
+ : [];
156
+ // Only record if there are actual visual changes
157
+ if (hoverChanges.length > 0 ||
158
+ focusChanges.length > 0 ||
159
+ defaultFile) {
160
+ records.push({
161
+ componentType: type,
162
+ label: label || type,
163
+ selector: `${selector}:nth-of-type(${i + 1})`,
164
+ index: i + 1,
165
+ screenshots: {
166
+ default: `screens/states/${defaultFile}`,
167
+ hover: hoverFile ? `screens/states/${hoverFile}` : undefined,
168
+ focus: focusFile ? `screens/states/${focusFile}` : undefined,
169
+ },
170
+ hoverChanges,
171
+ focusChanges,
172
+ transitionValue: defaultStyles.transition,
173
+ });
174
+ }
175
+ }
176
+ catch { /* element failed — skip */ }
177
+ }
178
+ }
179
+ catch { /* selector failed — skip */ }
180
+ }
181
+ await page.close();
182
+ }
183
+ finally {
184
+ await browser.close().catch(() => { });
185
+ }
186
+ return records;
187
+ }
188
+ async function getStyles(el) {
189
+ return el.evaluate((node) => {
190
+ const s = window.getComputedStyle(node);
191
+ return {
192
+ backgroundColor: s.backgroundColor,
193
+ color: s.color,
194
+ borderColor: s.borderColor,
195
+ borderWidth: s.borderWidth,
196
+ boxShadow: s.boxShadow,
197
+ opacity: s.opacity,
198
+ transform: s.transform,
199
+ outline: s.outline,
200
+ outlineColor: s.outlineColor,
201
+ textDecoration: s.textDecoration,
202
+ transition: s.transition,
203
+ };
204
+ });
205
+ }
206
+ async function screenshotElement(el, filePath) {
207
+ try {
208
+ await el.screenshot({ path: filePath });
209
+ }
210
+ catch {
211
+ // Element screenshot failed — ignore
212
+ }
213
+ }
214
+ function diffStyles(before, after) {
215
+ const diffs = [];
216
+ for (const prop of TRACKED_PROPS) {
217
+ const b = before[prop] || '';
218
+ const a = after[prop] || '';
219
+ if (b !== a && a !== '' && a !== 'none' && a !== 'normal') {
220
+ diffs.push({ property: prop, from: b, to: a });
221
+ }
222
+ }
223
+ return diffs;
224
+ }
225
+ //# sourceMappingURL=interactions.js.map
@@ -0,0 +1,14 @@
1
+ import { LayoutRecord } from '../../types-ultra';
2
+ /**
3
+ * Ultra mode — DOM Layout Extractor
4
+ *
5
+ * Crawls the page DOM and extracts layout information from significant containers:
6
+ * - flex/grid parents
7
+ * - section-level wrappers
8
+ * - nav/header/footer
9
+ *
10
+ * Returns LayoutRecord[] for LAYOUT.md generation.
11
+ * Requires Playwright (optional peer dependency).
12
+ */
13
+ export declare function extractLayouts(url: string): Promise<LayoutRecord[]>;
14
+ //# sourceMappingURL=layout.d.ts.map
@@ -0,0 +1,126 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.extractLayouts = extractLayouts;
4
+ /**
5
+ * Ultra mode — DOM Layout Extractor
6
+ *
7
+ * Crawls the page DOM and extracts layout information from significant containers:
8
+ * - flex/grid parents
9
+ * - section-level wrappers
10
+ * - nav/header/footer
11
+ *
12
+ * Returns LayoutRecord[] for LAYOUT.md generation.
13
+ * Requires Playwright (optional peer dependency).
14
+ */
15
+ async function extractLayouts(url) {
16
+ let playwright;
17
+ try {
18
+ playwright = require('playwright');
19
+ }
20
+ catch {
21
+ return [];
22
+ }
23
+ const browser = await playwright.chromium.launch({ headless: true });
24
+ try {
25
+ const context = await browser.newContext({
26
+ viewport: { width: 1440, height: 900 },
27
+ userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
28
+ });
29
+ const page = await context.newPage();
30
+ await page.goto(url, { waitUntil: 'networkidle', timeout: 25000 });
31
+ await page.waitForTimeout(1000);
32
+ const records = await page.evaluate(() => {
33
+ const LAYOUT_SELECTORS = [
34
+ 'header',
35
+ 'nav',
36
+ 'main',
37
+ 'footer',
38
+ 'section',
39
+ 'article',
40
+ '[class*="container"]',
41
+ '[class*="wrapper"]',
42
+ '[class*="layout"]',
43
+ '[class*="grid"]',
44
+ '[class*="flex"]',
45
+ '[class*="row"]',
46
+ '[class*="col"]',
47
+ '[class*="hero"]',
48
+ '[class*="card"]',
49
+ ];
50
+ function getDepth(el) {
51
+ let depth = 0;
52
+ let node = el.parentElement;
53
+ while (node) {
54
+ depth++;
55
+ node = node.parentElement;
56
+ }
57
+ return depth;
58
+ }
59
+ function buildSelector(el) {
60
+ const tag = el.tagName.toLowerCase();
61
+ const id = el.id ? `#${el.id}` : '';
62
+ const classes = Array.from(el.classList)
63
+ .filter(c => !/^(js-|is-|has-)/.test(c))
64
+ .slice(0, 2)
65
+ .map(c => `.${c}`)
66
+ .join('');
67
+ return `${tag}${id}${classes}`.slice(0, 60);
68
+ }
69
+ const seen = new WeakSet();
70
+ const results = [];
71
+ for (const sel of LAYOUT_SELECTORS) {
72
+ const elements = document.querySelectorAll(sel);
73
+ elements.forEach((el) => {
74
+ if (seen.has(el))
75
+ return;
76
+ seen.add(el);
77
+ const s = window.getComputedStyle(el);
78
+ const display = s.display;
79
+ // Only capture flex/grid/block containers with children
80
+ if (!['flex', 'grid', 'block', 'inline-flex', 'inline-grid'].includes(display))
81
+ return;
82
+ const rect = el.getBoundingClientRect();
83
+ if (rect.width < 100 || rect.height < 30)
84
+ return;
85
+ const childCount = el.children.length;
86
+ if (childCount === 0)
87
+ return;
88
+ results.push({
89
+ tag: el.tagName.toLowerCase(),
90
+ selector: buildSelector(el),
91
+ display,
92
+ flexDirection: s.flexDirection || '',
93
+ flexWrap: s.flexWrap || '',
94
+ justifyContent: s.justifyContent || '',
95
+ alignItems: s.alignItems || '',
96
+ gap: s.gap || '',
97
+ rowGap: s.rowGap || '',
98
+ columnGap: s.columnGap || '',
99
+ padding: s.padding || '',
100
+ margin: s.margin || '',
101
+ gridTemplateColumns: s.gridTemplateColumns || '',
102
+ gridTemplateRows: s.gridTemplateRows || '',
103
+ maxWidth: s.maxWidth || '',
104
+ width: s.width || '',
105
+ height: s.height || '',
106
+ position: s.position || '',
107
+ childCount,
108
+ depth: getDepth(el),
109
+ });
110
+ if (results.length >= 60)
111
+ return;
112
+ });
113
+ }
114
+ // Sort by depth (shallower = more structural)
115
+ return results.sort((a, b) => a.depth - b.depth).slice(0, 40);
116
+ });
117
+ await page.close();
118
+ await browser.close();
119
+ return records;
120
+ }
121
+ catch {
122
+ await browser.close().catch(() => { });
123
+ return [];
124
+ }
125
+ }
126
+ //# sourceMappingURL=layout.js.map
@@ -0,0 +1,16 @@
1
+ import { PageScreenshot, SectionScreenshot } from '../../types-ultra';
2
+ /**
3
+ * Ultra mode — Page & Section Screenshots
4
+ *
5
+ * 1. Crawl up to `maxPages` internal links from the origin URL
6
+ * 2. Take a full-page screenshot for each (screens/pages/[slug].png)
7
+ * 3. Detect major sections (section, article, main > div, height > 300px)
8
+ * and clip one screenshot per section (screens/sections/[page]-section-N.png)
9
+ *
10
+ * Requires Playwright (optional peer dependency).
11
+ */
12
+ export declare function capturePageScreenshots(originUrl: string, skillDir: string, maxPages: number): Promise<{
13
+ pages: PageScreenshot[];
14
+ sections: SectionScreenshot[];
15
+ }>;
16
+ //# sourceMappingURL=pages.d.ts.map