@saeroon/cli 0.2.6 → 0.2.8
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/dist/index.js +425 -66
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -2514,19 +2514,27 @@ var ELEMENT_TYPE_PROPS = {
|
|
|
2514
2514
|
"pre",
|
|
2515
2515
|
"code",
|
|
2516
2516
|
"progress",
|
|
2517
|
-
"meter"
|
|
2517
|
+
"meter",
|
|
2518
|
+
"output",
|
|
2519
|
+
"label",
|
|
2520
|
+
"search",
|
|
2521
|
+
"time",
|
|
2522
|
+
"mark",
|
|
2523
|
+
"abbr"
|
|
2518
2524
|
]),
|
|
2519
2525
|
coerce: () => null
|
|
2520
2526
|
}
|
|
2521
2527
|
};
|
|
2522
2528
|
var VALID_BLOCK_TYPES = /* @__PURE__ */ new Set([
|
|
2523
|
-
// Primitives (
|
|
2529
|
+
// Primitives (13)
|
|
2524
2530
|
"container",
|
|
2525
2531
|
"text-block",
|
|
2532
|
+
"rich-text-block",
|
|
2526
2533
|
"heading-block",
|
|
2527
2534
|
"button-block",
|
|
2528
2535
|
"image-block",
|
|
2529
2536
|
"video-block",
|
|
2537
|
+
"audio-block",
|
|
2530
2538
|
"embed-block",
|
|
2531
2539
|
"icon-block",
|
|
2532
2540
|
"input-block",
|
|
@@ -3940,10 +3948,12 @@ var EXTRACTION_SCRIPT = `
|
|
|
3940
3948
|
|
|
3941
3949
|
function rgbToHex(rgb) {
|
|
3942
3950
|
if (!rgb || rgb === 'transparent') return 'transparent';
|
|
3943
|
-
if (rgb.startsWith('#')) return rgb;
|
|
3951
|
+
if (rgb.startsWith('#')) return rgb.length === 7 ? rgb : null;
|
|
3952
|
+
// rgb()/rgba()\uB9CC \uCC98\uB9AC, oklch/lab/color() \uB4F1 \uCD5C\uC2E0 \uD3EC\uB9F7\uC740 \uAC74\uB108\uB700
|
|
3953
|
+
if (!rgb.startsWith('rgb')) return null;
|
|
3944
3954
|
const match = rgb.match(/\\d+/g);
|
|
3945
|
-
if (!match || match.length < 3) return
|
|
3946
|
-
const [r, g, b] = match.map(Number);
|
|
3955
|
+
if (!match || match.length < 3) return null;
|
|
3956
|
+
const [r, g, b] = match.map(n => Math.min(255, Math.max(0, Number(n))));
|
|
3947
3957
|
return '#' + [r, g, b].map(c => c.toString(16).padStart(2, '0')).join('');
|
|
3948
3958
|
}
|
|
3949
3959
|
|
|
@@ -3968,18 +3978,18 @@ var EXTRACTION_SCRIPT = `
|
|
|
3968
3978
|
|
|
3969
3979
|
if (tag === 'header' || tag === 'nav') return 'header';
|
|
3970
3980
|
if (tag === 'footer') return 'footer';
|
|
3971
|
-
if (/hero|banner|jumbotron|splash|main-?visual/.test(combined)) return 'hero';
|
|
3972
|
-
if (/feature|service|benefit|what-?we/.test(combined)) return 'features';
|
|
3973
|
-
if (/testimonial|review|feedback|client|customer/.test(combined)) return 'testimonials';
|
|
3974
|
-
if (/faq|accordion|question|q-?and-?a/.test(combined)) return 'faq';
|
|
3975
|
-
if (/team|staff|member|about-?us|who-?we/.test(combined)) return 'team';
|
|
3976
|
-
if (/pricing|plan|package/.test(combined)) return 'pricing';
|
|
3977
|
-
if (/contact|inquiry|form|cta|call-?to-?action|get-?started/.test(combined)) return 'cta';
|
|
3978
|
-
if (/gallery|portfolio|work|project|showcase/.test(combined)) return 'gallery';
|
|
3979
|
-
if (/blog|news|article|post/.test(combined)) return 'blog';
|
|
3980
|
-
if (/partner|client|logo|brand|trust/.test(combined)) return 'partners';
|
|
3981
|
-
if (/map|location|address|direction/.test(combined)) return 'map';
|
|
3982
|
-
if (/stat|counter|number|achievement/.test(combined)) return 'stats';
|
|
3981
|
+
if (/hero|banner|jumbotron|splash|main-?visual|\uBA54\uC778s?\uBE44\uC8FC\uC5BC|\uD788\uC5B4\uB85C/.test(combined)) return 'hero';
|
|
3982
|
+
if (/feature|service|benefit|what-?we|\uC5C5\uBB34s?\uC601\uC5ED|\uC11C\uBE44\uC2A4|\uC9C4\uB8CCs?\uACFC\uBAA9|\uCE58\uB8CCs?\uBC29\uBC95|\uBC95\uB960s?\uC11C\uBE44\uC2A4/.test(combined)) return 'features';
|
|
3983
|
+
if (/testimonial|review|feedback|client|customer|\uD6C4\uAE30|\uB9AC\uBDF0|\uC218\uAC15s?\uD6C4\uAE30|\uACE0\uAC1Ds?\uD6C4\uAE30/.test(combined)) return 'testimonials';
|
|
3984
|
+
if (/faq|accordion|question|q-?and-?a|\uC790\uC8FCs?\uBB3B\uB294|\uC9C8\uBB38/.test(combined)) return 'faq';
|
|
3985
|
+
if (/team|staff|member|about-?us|who-?we|\uC758\uB8CC\uC9C4|\uC6D0\uC7A5|\uBCC0\uD638\uC0AC|\uAC15\uC0AC|\uC18C\uAC1C|\uC778\uC0AC\uB9D0/.test(combined)) return 'team';
|
|
3986
|
+
if (/pricing|plan|package|\uAC00\uACA9|\uC694\uAE08|\uD50C\uB79C/.test(combined)) return 'pricing';
|
|
3987
|
+
if (/contact|inquiry|form|cta|call-?to-?action|get-?started|\uC0C1\uB2F4|\uBB38\uC758|\uC608\uC57D|\uC2E0\uCCAD/.test(combined)) return 'cta';
|
|
3988
|
+
if (/gallery|portfolio|work|project|showcase|\uAC24\uB7EC\uB9AC|\uB458\uB7EC\uBCF4\uAE30|\uC2DC\uC124|\uB0B4\uBD80/.test(combined)) return 'gallery';
|
|
3989
|
+
if (/blog|news|article|post|\uBE14\uB85C\uADF8|\uB274\uC2A4|\uC18C\uC2DD|\uCE7C\uB7FC/.test(combined)) return 'blog';
|
|
3990
|
+
if (/partner|client|logo|brand|trust|\uD30C\uD2B8\uB108|\uC81C\uD734/.test(combined)) return 'partners';
|
|
3991
|
+
if (/map|location|address|direction|\uC624\uC2DC\uB294s?\uAE38|\uCC3E\uC544\uC624\uC2DC\uB294s?\uAE38|\uC9C0\uB3C4|\uC704\uCE58/.test(combined)) return 'map';
|
|
3992
|
+
if (/stat|counter|number|achievement|\uC2E4\uC801|\uC131\uACFC|\uC218\uCE58/.test(combined)) return 'stats';
|
|
3983
3993
|
return 'section';
|
|
3984
3994
|
}
|
|
3985
3995
|
|
|
@@ -4019,11 +4029,79 @@ var EXTRACTION_SCRIPT = `
|
|
|
4019
4029
|
// \u2500\u2500 1. Structure \u2500\u2500
|
|
4020
4030
|
const sectionSelectors = 'body > header, body > footer, body > nav, body > main, body > section, body > div, body > article, body > aside';
|
|
4021
4031
|
const topLevelEls = document.querySelectorAll(sectionSelectors);
|
|
4032
|
+
|
|
4033
|
+
// wrapper \uAC10\uC9C0: body \uC9C1\uACC4 \uC790\uC2DD\uC5D0\uC11C \uC2DC\uC791\uD558\uC5EC wrapper(\uC790\uC2DD 1\uAC1C) \uD480\uAE30
|
|
4034
|
+
let sectionEls = Array.from(topLevelEls).filter(el => el.getBoundingClientRect().height >= 10);
|
|
4035
|
+
|
|
4036
|
+
// \uC758\uBBF8 \uC788\uB294 \uC139\uC158\uC744 \uCC3E\uC744 \uB54C\uAE4C\uC9C0 wrapper\uB97C \uD480\uC5B4\uB098\uAC10 (\uCD5C\uB300 5\uB2E8\uACC4)
|
|
4037
|
+
function unwrapSections(els, depth) {
|
|
4038
|
+
if (depth <= 0) return els;
|
|
4039
|
+
const result = [];
|
|
4040
|
+
let expanded = false;
|
|
4041
|
+
els.forEach(el => {
|
|
4042
|
+
const tag = el.tagName.toLowerCase();
|
|
4043
|
+
// \uC2DC\uB9E8\uD2F1 \uD0DC\uADF8: header/footer/nav/section/article/aside\uB294 \uC139\uC158\uC73C\uB85C \uC720\uC9C0
|
|
4044
|
+
const keepTags = new Set(['header', 'footer', 'nav', 'section', 'article', 'aside']);
|
|
4045
|
+
// main/form\uC740 \uB798\uD37C \uC5ED\uD560\uC774 \uB9CE\uC74C \u2014 \uC790\uC2DD\uC774 \uC5EC\uB7EC \uAC1C\uBA74 \uD3BC\uCE68
|
|
4046
|
+
const unwrapTags = new Set(['main', 'form']);
|
|
4047
|
+
|
|
4048
|
+
if (keepTags.has(tag)) {
|
|
4049
|
+
result.push(el);
|
|
4050
|
+
return;
|
|
4051
|
+
}
|
|
4052
|
+
|
|
4053
|
+
const children = Array.from(el.children).filter(c => c.getBoundingClientRect().height >= 10);
|
|
4054
|
+
|
|
4055
|
+
if (unwrapTags.has(tag)) {
|
|
4056
|
+
// main/form: \uC790\uC2DD\uC774 \uC5EC\uB7EC \uAC1C\uBA74 \uD3BC\uCE68
|
|
4057
|
+
if (children.length > 1) {
|
|
4058
|
+
result.push(...children);
|
|
4059
|
+
expanded = true;
|
|
4060
|
+
} else {
|
|
4061
|
+
result.push(el);
|
|
4062
|
+
}
|
|
4063
|
+
return;
|
|
4064
|
+
}
|
|
4065
|
+
|
|
4066
|
+
// div \uB4F1 \uBE44\uC2DC\uB9E8\uD2F1 \uB798\uD37C \uBC97\uAE30\uAE30
|
|
4067
|
+
if (children.length <= 1 && children.length > 0) {
|
|
4068
|
+
result.push(...children);
|
|
4069
|
+
expanded = true;
|
|
4070
|
+
} else if (children.length > 1) {
|
|
4071
|
+
// \uD398\uC774\uC9C0 \uB192\uC774 \uB300\uBD80\uBD84\uC744 \uCC28\uC9C0\uD558\uB294 \uB798\uD37C\uB294 \uD3BC\uCE68
|
|
4072
|
+
const elHeight = el.getBoundingClientRect().height;
|
|
4073
|
+
const pageHeight = document.body.scrollHeight || 1;
|
|
4074
|
+
if (elHeight > pageHeight * 0.5) {
|
|
4075
|
+
result.push(...children);
|
|
4076
|
+
expanded = true;
|
|
4077
|
+
} else {
|
|
4078
|
+
result.push(el);
|
|
4079
|
+
}
|
|
4080
|
+
} else {
|
|
4081
|
+
result.push(el);
|
|
4082
|
+
}
|
|
4083
|
+
});
|
|
4084
|
+
if (!expanded) return result;
|
|
4085
|
+
return unwrapSections(result, depth - 1);
|
|
4086
|
+
}
|
|
4087
|
+
|
|
4088
|
+
sectionEls = unwrapSections(sectionEls, 5);
|
|
4089
|
+
|
|
4090
|
+
// leaf \uC694\uC18C \uD544\uD130 \u2014 \uC139\uC158\uC774 \uB420 \uC218 \uC5C6\uB294 \uD0DC\uADF8 \uC81C\uAC70
|
|
4091
|
+
const leafTags = new Set(['img', 'br', 'hr', 'span', 'a', 'input', 'button', 'label', 'iframe', 'video', 'source', 'svg', 'canvas', 'p', 'strong', 'em', 'b', 'i', 'small', 'path', 'circle', 'rect', 'line', 'polygon', 'polyline', 'ellipse', 'g', 'use', 'defs', 'symbol']);
|
|
4092
|
+
sectionEls = sectionEls.filter(el => {
|
|
4093
|
+
const tag = el.tagName.toLowerCase();
|
|
4094
|
+
// heading(h1-h6)\uC740 \uC139\uC158\uC774 \uC544\uB2CC heading \u2014 \uD56D\uC0C1 \uC81C\uC678
|
|
4095
|
+
if (/^h[1-6]$/.test(tag)) return false;
|
|
4096
|
+
// li\uB294 \uB9AC\uC2A4\uD2B8 \uC544\uC774\uD15C\uC774\uC9C0 \uC139\uC158\uC774 \uC544\uB2D8
|
|
4097
|
+
if (tag === 'li') return false;
|
|
4098
|
+
return !leafTags.has(tag);
|
|
4099
|
+
});
|
|
4100
|
+
|
|
4022
4101
|
const sections = [];
|
|
4023
4102
|
let maxDepth = 0;
|
|
4024
4103
|
|
|
4025
|
-
|
|
4026
|
-
// \uC228\uACA8\uC9C4 \uC694\uC18C \uB610\uB294 \uB108\uBE44 0\uC778 \uC694\uC18C \uC81C\uC678
|
|
4104
|
+
sectionEls.forEach(el => {
|
|
4027
4105
|
const rect = el.getBoundingClientRect();
|
|
4028
4106
|
if (rect.height < 10) return;
|
|
4029
4107
|
|
|
@@ -4050,10 +4128,39 @@ var EXTRACTION_SCRIPT = `
|
|
|
4050
4128
|
if (depth > maxDepth) maxDepth = depth;
|
|
4051
4129
|
});
|
|
4052
4130
|
|
|
4053
|
-
// heading hierarchy
|
|
4054
|
-
|
|
4131
|
+
// heading hierarchy (\uC2DC\uB9E8\uD2F1 + \uB300\uD615 \uD3F0\uD2B8 pseudo-heading)
|
|
4132
|
+
// \uD31D\uC5C5/\uBAA8\uB2EC/dialog \uB0B4\uBD80 heading \uC81C\uC678
|
|
4133
|
+
const headings = Array.from(document.querySelectorAll('h1, h2, h3, h4, h5, h6')).filter(h => {
|
|
4134
|
+
const popup = h.closest('dialog, [class*=popup], [class*=modal], [class*=layer], [role=dialog], [style*="display: none"], [style*="display:none"]');
|
|
4135
|
+
if (popup) {
|
|
4136
|
+
const rect = popup.getBoundingClientRect();
|
|
4137
|
+
// \uD654\uBA74 \uBC16\uC774\uAC70\uB098 display:none\uC774\uBA74 \uD31D\uC5C5 heading
|
|
4138
|
+
if (rect.width === 0 || rect.height === 0) return false;
|
|
4139
|
+
// \uD31D\uC5C5\uC774 \uD654\uBA74 \uAC00\uC6B4\uB370 \uB5A0 \uC788\uC73C\uBA74 (\uC804\uCCB4 \uB108\uBE44\uC758 90% \uBBF8\uB9CC) \uD31D\uC5C5 heading
|
|
4140
|
+
if (rect.width < window.innerWidth * 0.9) return false;
|
|
4141
|
+
}
|
|
4142
|
+
return true;
|
|
4143
|
+
});
|
|
4055
4144
|
const headingHierarchy = headings.map(h => h.tagName.toLowerCase() + ': ' + (h.textContent || '').trim().slice(0, 80));
|
|
4056
4145
|
|
|
4146
|
+
// \uC2DC\uB9E8\uD2F1 heading\uC774 \uBD80\uC871\uD558\uBA74 \uD070 \uD3F0\uD2B8 \uC694\uC18C\uB97C pseudo-heading\uC73C\uB85C \uBCF4\uC644
|
|
4147
|
+
if (headingHierarchy.length < 3) {
|
|
4148
|
+
const largeFontEls = [];
|
|
4149
|
+
document.querySelectorAll('p, span, div, a, strong, em').forEach(el => {
|
|
4150
|
+
const fs = parsePixel(getComputedProp(el, 'font-size'));
|
|
4151
|
+
const text = (el.textContent || '').trim();
|
|
4152
|
+
if (fs >= 24 && text.length > 0 && text.length < 100 && el.children.length <= 2) {
|
|
4153
|
+
largeFontEls.push({ el, fs, text });
|
|
4154
|
+
}
|
|
4155
|
+
});
|
|
4156
|
+
// \uD070 \uC21C\uC11C \uC815\uB82C \uD6C4 \uC0C1\uC704 20\uAC1C
|
|
4157
|
+
largeFontEls.sort((a, b) => b.fs - a.fs);
|
|
4158
|
+
largeFontEls.slice(0, 20).forEach(({ fs, text }) => {
|
|
4159
|
+
const level = fs >= 60 ? 'pseudo-h1' : fs >= 36 ? 'pseudo-h2' : 'pseudo-h3';
|
|
4160
|
+
headingHierarchy.push(level + ' (' + fs + 'px): ' + text.slice(0, 80));
|
|
4161
|
+
});
|
|
4162
|
+
}
|
|
4163
|
+
|
|
4057
4164
|
// \u2500\u2500 2. Design Tokens \u2500\u2500
|
|
4058
4165
|
|
|
4059
4166
|
// 2a. Colors \u2014 \uBAA8\uB4E0 \uC694\uC18C\uC758 color, backgroundColor \uC218\uC9D1
|
|
@@ -4074,11 +4181,61 @@ var EXTRACTION_SCRIPT = `
|
|
|
4074
4181
|
const sortedColors = Object.entries(colorMap).sort((a, b) => b[1] - a[1]);
|
|
4075
4182
|
const sortedBgs = Object.entries(bgColorMap).sort((a, b) => b[1] - a[1]);
|
|
4076
4183
|
|
|
4077
|
-
// primary = body\uB098 heading\uC758 \uAC00\uC7A5 \uBE48\uBC88\uD55C \uD14D\uC2A4\uD2B8 \uC0C9\uC0C1\uC774 \uC544\uB2CC \uC0C9 \uC911 1\uC704
|
|
4078
4184
|
const bodyColor = rgbToHex(getComputedProp(document.body, 'color'));
|
|
4079
4185
|
const bodyBg = rgbToHex(getComputedProp(document.body, 'background-color'));
|
|
4080
|
-
|
|
4081
|
-
const
|
|
4186
|
+
// \uAE30\uBCF8 \uB9C1\uD06C\uC0C9(#0000ee), \uD770/\uAC80 \uC81C\uC678
|
|
4187
|
+
const defaultLinkColors = ['#0000ee', '#0000ff', '#551a8b'];
|
|
4188
|
+
const nonBodyColors = sortedColors.filter(([c]) =>
|
|
4189
|
+
c !== bodyColor && c !== '#ffffff' && c !== '#000000' && !defaultLinkColors.includes(c)
|
|
4190
|
+
);
|
|
4191
|
+
|
|
4192
|
+
// heading \uC0C9\uC0C1 \uC218\uC9D1 \u2014 h1-h3 + \uB300\uD615 \uD3F0\uD2B8(24px+) \uC694\uC18C \uD3EC\uD568
|
|
4193
|
+
const headingColorMap = {};
|
|
4194
|
+
document.querySelectorAll('h1, h2, h3, h4').forEach(h => {
|
|
4195
|
+
const c = rgbToHex(getComputedProp(h, 'color'));
|
|
4196
|
+
if (c && c !== 'transparent' && c !== '#ffffff' && c !== '#000000' && !defaultLinkColors.includes(c)) {
|
|
4197
|
+
headingColorMap[c] = (headingColorMap[c] || 0) + 1;
|
|
4198
|
+
}
|
|
4199
|
+
});
|
|
4200
|
+
// \uC2DC\uB9E8\uD2F1 heading\uC774 \uC5C6\uC73C\uBA74 \uB300\uD615 \uD3F0\uD2B8 \uC694\uC18C\uC5D0\uC11C \uC0C9\uC0C1 \uCD94\uCD9C
|
|
4201
|
+
if (Object.keys(headingColorMap).length === 0) {
|
|
4202
|
+
document.querySelectorAll('p, span, div, strong, a').forEach(el => {
|
|
4203
|
+
const fs = parsePixel(getComputedProp(el, 'font-size'));
|
|
4204
|
+
if (fs >= 24 && (el.textContent || '').trim().length > 0 && (el.textContent || '').trim().length < 100) {
|
|
4205
|
+
const c = rgbToHex(getComputedProp(el, 'color'));
|
|
4206
|
+
if (c && c !== 'transparent' && c !== '#ffffff' && c !== '#000000' && !defaultLinkColors.includes(c)) {
|
|
4207
|
+
headingColorMap[c] = (headingColorMap[c] || 0) + 1;
|
|
4208
|
+
}
|
|
4209
|
+
}
|
|
4210
|
+
});
|
|
4211
|
+
}
|
|
4212
|
+
const topHeadingColor = Object.entries(headingColorMap).sort((a, b) => b[1] - a[1])[0];
|
|
4213
|
+
|
|
4214
|
+
const allPalette = [...new Set([...sortedColors.map(c => c[0]), ...sortedBgs.map(c => c[0])])].filter(c => !defaultLinkColors.includes(c)).slice(0, 20);
|
|
4215
|
+
|
|
4216
|
+
// accent \uD6C4\uBCF4: \uCC44\uB3C4\uAC00 \uB192\uC740 \uBE44-\uC911\uB9BD \uC0C9\uC0C1 (\uAC15\uC870/CTA\uC6A9)
|
|
4217
|
+
function hexToHsl(hex) {
|
|
4218
|
+
const r = parseInt(hex.slice(1, 3), 16) / 255;
|
|
4219
|
+
const g = parseInt(hex.slice(3, 5), 16) / 255;
|
|
4220
|
+
const b = parseInt(hex.slice(5, 7), 16) / 255;
|
|
4221
|
+
const max = Math.max(r, g, b), min = Math.min(r, g, b);
|
|
4222
|
+
const l = (max + min) / 2;
|
|
4223
|
+
if (max === min) return { h: 0, s: 0, l };
|
|
4224
|
+
const d = max - min;
|
|
4225
|
+
const s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
|
4226
|
+
let h = 0;
|
|
4227
|
+
if (max === r) h = ((g - b) / d + (g < b ? 6 : 0)) / 6;
|
|
4228
|
+
else if (max === g) h = ((b - r) / d + 2) / 6;
|
|
4229
|
+
else h = ((r - g) / d + 4) / 6;
|
|
4230
|
+
return { h, s, l };
|
|
4231
|
+
}
|
|
4232
|
+
|
|
4233
|
+
const accentFromPalette = allPalette
|
|
4234
|
+
.filter(c => c.length === 7 && c.startsWith('#'))
|
|
4235
|
+
.map(c => ({ color: c, ...hexToHsl(c) }))
|
|
4236
|
+
.filter(c => c.s > 0.3 && c.l > 0.15 && c.l < 0.85)
|
|
4237
|
+
.sort((a, b) => b.s - a.s);
|
|
4238
|
+
const topAccent = accentFromPalette.length > 0 ? [accentFromPalette[0].color] : null;
|
|
4082
4239
|
|
|
4083
4240
|
// 2b. Typography
|
|
4084
4241
|
const typographyScale = {};
|
|
@@ -4171,13 +4328,30 @@ var EXTRACTION_SCRIPT = `
|
|
|
4171
4328
|
document.querySelector('details, [class*=accordion], [class*=collapse], [data-toggle=collapse]')
|
|
4172
4329
|
);
|
|
4173
4330
|
const hasModal = !!(
|
|
4174
|
-
document.querySelector('dialog, [class*=modal], [
|
|
4331
|
+
document.querySelector('dialog, [class*=modal], [role=dialog], [class*=popup]')
|
|
4332
|
+
);
|
|
4333
|
+
const hasTab = !!(
|
|
4334
|
+
document.querySelector('[role=tablist], [role=tab], [class*=tab-content], [class*=tab-pane], [data-toggle=tab], .elementor-tab-title, .e-n-tabs, .e-n-tab-title')
|
|
4335
|
+
);
|
|
4336
|
+
const hasLightbox = !!(
|
|
4337
|
+
document.querySelector('[class*=lightbox], [data-lightbox], [class*=fancybox], [data-fancybox], [class*=glightbox], [data-gallery], [data-elementor-open-lightbox]')
|
|
4175
4338
|
);
|
|
4176
4339
|
const hasStickyHeader = (() => {
|
|
4177
|
-
|
|
4178
|
-
|
|
4179
|
-
|
|
4180
|
-
|
|
4340
|
+
// \uC2DC\uB9E8\uD2F1 \uD0DC\uADF8 \uC6B0\uC120
|
|
4341
|
+
const header = document.querySelector('header, [class*=header], nav, [class*=gnb], [class*=lnb], [id*=header], [id*=gnb]');
|
|
4342
|
+
if (header) {
|
|
4343
|
+
const pos = getComputedProp(header, 'position');
|
|
4344
|
+
if (pos === 'sticky' || pos === 'fixed') return true;
|
|
4345
|
+
}
|
|
4346
|
+
// \uC0C1\uB2E8 fixed/sticky \uC694\uC18C \uD0D0\uC0C9 (\uBE44\uC2DC\uB9E8\uD2F1 \uB9C8\uD06C\uC5C5 \uB300\uC751)
|
|
4347
|
+
const topEls = document.querySelectorAll('body > div, body > div > div');
|
|
4348
|
+
for (const el of topEls) {
|
|
4349
|
+
const pos = getComputedProp(el, 'position');
|
|
4350
|
+
if ((pos === 'sticky' || pos === 'fixed') && el.getBoundingClientRect().top <= 0 && el.getBoundingClientRect().height < 200) {
|
|
4351
|
+
return true;
|
|
4352
|
+
}
|
|
4353
|
+
}
|
|
4354
|
+
return false;
|
|
4181
4355
|
})();
|
|
4182
4356
|
const hasScrollAnimations = !!(
|
|
4183
4357
|
document.querySelector('[class*=aos], [data-aos], [class*=wow], [class*=scroll-animate], [class*=animate-on-scroll]') ||
|
|
@@ -4197,8 +4371,39 @@ var EXTRACTION_SCRIPT = `
|
|
|
4197
4371
|
const hasHoverEffects = transitionProps.size > 0;
|
|
4198
4372
|
|
|
4199
4373
|
// \u2500\u2500 4. Images \u2500\u2500
|
|
4374
|
+
// inline style + \uC8FC\uC694 \uCEE8\uD14C\uC774\uB108\uC758 computed background-image \uD0D0\uC0C9
|
|
4200
4375
|
const imgEls = document.querySelectorAll('img, picture source, [style*="background-image"]');
|
|
4201
4376
|
const images = [];
|
|
4377
|
+
const seenImageSrcs = new Set();
|
|
4378
|
+
|
|
4379
|
+
// computed background-image\uAC00 \uC788\uB294 \uCEE8\uD14C\uC774\uB108 \uC694\uC18C \uCD94\uAC00 \uD0D0\uC0C9
|
|
4380
|
+
const bgCandidates = document.querySelectorAll('div, section, header, footer, main, article, aside, figure, span, a');
|
|
4381
|
+
const bgCandidateArr = Array.from(bgCandidates).filter((_, i) => i % Math.max(1, Math.ceil(bgCandidates.length / 300)) === 0);
|
|
4382
|
+
bgCandidateArr.forEach(el => {
|
|
4383
|
+
const bg = getComputedProp(el, 'background-image');
|
|
4384
|
+
if (bg && bg !== 'none') {
|
|
4385
|
+
const match = bg.match(/url\\(["']?(.+?)["']?\\)/);
|
|
4386
|
+
if (match && match[1] && !match[1].startsWith('data:') && !match[1].includes('.svg')) {
|
|
4387
|
+
const src = match[1];
|
|
4388
|
+
if (seenImageSrcs.has(src)) return;
|
|
4389
|
+
seenImageSrcs.add(src);
|
|
4390
|
+
const rect = el.getBoundingClientRect();
|
|
4391
|
+
if (rect.width < 20 || rect.height < 20) return;
|
|
4392
|
+
const parentSection = el.closest('section, header, footer, main, [class*=hero], [class*=banner]');
|
|
4393
|
+
const parentRole = parentSection ? inferSectionRole(parentSection) : 'unknown';
|
|
4394
|
+
images.push({
|
|
4395
|
+
src: src.slice(0, 500),
|
|
4396
|
+
alt: '',
|
|
4397
|
+
width: Math.round(rect.width),
|
|
4398
|
+
height: Math.round(rect.height),
|
|
4399
|
+
aspectRatio: guessAspectRatio(Math.round(rect.width), Math.round(rect.height)),
|
|
4400
|
+
role: rect.width > window.innerWidth * 0.8 && rect.height > 300 ? 'hero-bg' : 'background',
|
|
4401
|
+
dominantColor: '',
|
|
4402
|
+
position: parentRole,
|
|
4403
|
+
});
|
|
4404
|
+
}
|
|
4405
|
+
}
|
|
4406
|
+
});
|
|
4202
4407
|
|
|
4203
4408
|
imgEls.forEach(el => {
|
|
4204
4409
|
let src = '';
|
|
@@ -4220,6 +4425,9 @@ var EXTRACTION_SCRIPT = `
|
|
|
4220
4425
|
}
|
|
4221
4426
|
|
|
4222
4427
|
if (!src || src.startsWith('data:image/svg') || src.includes('.svg')) return;
|
|
4428
|
+
// src \uC911\uBCF5 \uC81C\uAC70
|
|
4429
|
+
if (seenImageSrcs.has(src)) return;
|
|
4430
|
+
seenImageSrcs.add(src);
|
|
4223
4431
|
|
|
4224
4432
|
const rect = el.getBoundingClientRect();
|
|
4225
4433
|
if (!width) width = Math.round(rect.width);
|
|
@@ -4271,20 +4479,21 @@ var EXTRACTION_SCRIPT = `
|
|
|
4271
4479
|
src = el.src || el.dataset.src || '';
|
|
4272
4480
|
if (/youtube.com|youtu.be/.test(src)) {
|
|
4273
4481
|
platform = 'youtube';
|
|
4274
|
-
type = 'embed';
|
|
4275
4482
|
// YouTube autoplay \uD30C\uB77C\uBBF8\uD130 \uAC10\uC9C0
|
|
4276
4483
|
autoplay = /autoplay=1/.test(src);
|
|
4277
4484
|
muted = /mute=1/.test(src);
|
|
4278
4485
|
loop = /loop=1/.test(src);
|
|
4486
|
+
// autoplay+muted \u2192 background \uBE44\uB514\uC624\uB85C \uBD84\uB958 (YouTube \uBC30\uACBD \uC601\uC0C1 \uD328\uD134)
|
|
4487
|
+
type = (autoplay && muted) ? 'background' : 'embed';
|
|
4279
4488
|
// YouTube thumbnail \uCD94\uCD9C
|
|
4280
4489
|
const ytMatch = src.match(/(?:embed\\/|v=|youtu\\.be\\/)([a-zA-Z0-9_-]{11})/);
|
|
4281
4490
|
if (ytMatch) posterSrc = 'https://img.youtube.com/vi/' + ytMatch[1] + '/hqdefault.jpg';
|
|
4282
4491
|
} else if (/vimeo.com/.test(src)) {
|
|
4283
4492
|
platform = 'vimeo';
|
|
4284
|
-
type = 'embed';
|
|
4285
4493
|
autoplay = /autoplay=1/.test(src);
|
|
4286
4494
|
muted = /muted=1/.test(src);
|
|
4287
4495
|
loop = /loop=1/.test(src);
|
|
4496
|
+
type = (autoplay && muted) ? 'background' : 'embed';
|
|
4288
4497
|
}
|
|
4289
4498
|
}
|
|
4290
4499
|
|
|
@@ -4375,11 +4584,11 @@ var EXTRACTION_SCRIPT = `
|
|
|
4375
4584
|
},
|
|
4376
4585
|
designTokens: {
|
|
4377
4586
|
colors: {
|
|
4378
|
-
primary: (nonBodyColors[0] || sortedColors[0] || ['#000000'])[0],
|
|
4379
|
-
secondary: (nonBodyColors[
|
|
4587
|
+
primary: topHeadingColor ? topHeadingColor[0] : (nonBodyColors[0] || sortedColors[0] || ['#000000'])[0],
|
|
4588
|
+
secondary: (nonBodyColors[0] || sortedColors[0] || ['#666666'])[0],
|
|
4380
4589
|
background: bodyBg || '#ffffff',
|
|
4381
4590
|
text: bodyColor || '#000000',
|
|
4382
|
-
accent: (nonBodyColors[
|
|
4591
|
+
accent: topAccent ? topAccent[0] : (nonBodyColors[1] || sortedColors[1] || ['#0066ff'])[0],
|
|
4383
4592
|
palette: allPalette,
|
|
4384
4593
|
},
|
|
4385
4594
|
typography: {
|
|
@@ -4404,6 +4613,8 @@ var EXTRACTION_SCRIPT = `
|
|
|
4404
4613
|
hasCarousel,
|
|
4405
4614
|
hasAccordion,
|
|
4406
4615
|
hasModal,
|
|
4616
|
+
hasTab,
|
|
4617
|
+
hasLightbox,
|
|
4407
4618
|
hasStickyHeader,
|
|
4408
4619
|
hasParallax,
|
|
4409
4620
|
detectedAnimations: [...animationNames].slice(0, 20),
|
|
@@ -4441,26 +4652,97 @@ async function analyzeReference(options) {
|
|
|
4441
4652
|
await writeFile7(analysisPath, JSON.stringify(analysis, null, 2), "utf-8");
|
|
4442
4653
|
return analysis;
|
|
4443
4654
|
}
|
|
4655
|
+
var POPUP_DISMISS_SCRIPT = `
|
|
4656
|
+
// 1. HTML dialog \uB2EB\uAE30
|
|
4657
|
+
document.querySelectorAll('dialog[open]').forEach(d => d.close());
|
|
4658
|
+
// 2. \uC77C\uBC18 \uD31D\uC5C5/\uBAA8\uB2EC \uB2EB\uAE30 \uBC84\uD2BC \uD074\uB9AD
|
|
4659
|
+
const closeSelectors = [
|
|
4660
|
+
'[class*=popup] [class*=close]', '[class*=modal] [class*=close]',
|
|
4661
|
+
'[class*=popup] [class*=btn-close]', '[class*=modal] [class*=btn-close]',
|
|
4662
|
+
'[aria-label*=close]', '[aria-label*="\uB2EB\uAE30"]', '[aria-label*=Close]',
|
|
4663
|
+
'.popup-close', '.modal-close', '.btn-close',
|
|
4664
|
+
'[class*=overlay] [class*=close]',
|
|
4665
|
+
];
|
|
4666
|
+
for (const sel of closeSelectors) {
|
|
4667
|
+
document.querySelectorAll(sel).forEach(btn => {
|
|
4668
|
+
try { btn.click(); } catch {}
|
|
4669
|
+
});
|
|
4670
|
+
}
|
|
4671
|
+
// 3. \uC624\uBC84\uB808\uC774/\uB524 \uB808\uC774\uC5B4 \uC81C\uAC70
|
|
4672
|
+
document.querySelectorAll('[class*=overlay], [class*=dimmed], .modal-backdrop, [class*=popup-bg]').forEach(el => {
|
|
4673
|
+
if (el.getBoundingClientRect().width >= window.innerWidth * 0.8) {
|
|
4674
|
+
el.style.display = 'none';
|
|
4675
|
+
}
|
|
4676
|
+
});
|
|
4677
|
+
// 4. body overflow \uBCF5\uC6D0
|
|
4678
|
+
document.body.style.overflow = '';
|
|
4679
|
+
document.body.style.position = '';
|
|
4680
|
+
document.documentElement.style.overflow = '';
|
|
4681
|
+
`;
|
|
4444
4682
|
function captureScreenshot(url, outputPath, width, height, timeout) {
|
|
4445
|
-
const
|
|
4446
|
-
|
|
4447
|
-
|
|
4448
|
-
|
|
4449
|
-
|
|
4450
|
-
|
|
4451
|
-
|
|
4452
|
-
|
|
4453
|
-
|
|
4454
|
-
|
|
4455
|
-
|
|
4456
|
-
|
|
4457
|
-
|
|
4683
|
+
const scriptContent = `
|
|
4684
|
+
const { chromium } = require('playwright');
|
|
4685
|
+
(async () => {
|
|
4686
|
+
const browser = await chromium.launch({ headless: true });
|
|
4687
|
+
const context = await browser.newContext({
|
|
4688
|
+
viewport: { width: ${width}, height: ${height} },
|
|
4689
|
+
userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
|
|
4690
|
+
});
|
|
4691
|
+
const page = await context.newPage();
|
|
4692
|
+
await page.goto(${JSON.stringify(url)}, { waitUntil: 'domcontentloaded', timeout: ${timeout} });
|
|
4693
|
+
await page.waitForTimeout(3000);
|
|
4694
|
+
// \uD31D\uC5C5 \uC790\uB3D9 \uB2EB\uAE30
|
|
4695
|
+
await page.evaluate(() => { ${POPUP_DISMISS_SCRIPT} });
|
|
4696
|
+
await page.waitForTimeout(500);
|
|
4697
|
+
// scroll simulation \u2014 lazy-load/IntersectionObserver \uCF58\uD150\uCE20 \uD2B8\uB9AC\uAC70
|
|
4698
|
+
await page.evaluate(async () => {
|
|
4699
|
+
const delay = (ms) => new Promise(r => setTimeout(r, ms));
|
|
4700
|
+
const scrollHeight = document.body.scrollHeight;
|
|
4701
|
+
const viewportHeight = window.innerHeight;
|
|
4702
|
+
const step = Math.floor(viewportHeight * 0.7);
|
|
4703
|
+
for (let y = 0; y < scrollHeight; y += step) {
|
|
4704
|
+
window.scrollTo(0, y);
|
|
4705
|
+
await delay(300);
|
|
4706
|
+
}
|
|
4707
|
+
window.scrollTo(0, scrollHeight);
|
|
4708
|
+
await delay(500);
|
|
4709
|
+
window.scrollTo(0, 0);
|
|
4710
|
+
await delay(500);
|
|
4711
|
+
});
|
|
4712
|
+
await page.waitForTimeout(1000);
|
|
4713
|
+
await page.screenshot({ path: ${JSON.stringify(outputPath)}, fullPage: true });
|
|
4714
|
+
await browser.close();
|
|
4715
|
+
})().catch(e => {
|
|
4716
|
+
process.stderr.write(e.message);
|
|
4717
|
+
process.exit(1);
|
|
4718
|
+
});
|
|
4719
|
+
`;
|
|
4720
|
+
const result = spawnSync("node", ["-e", scriptContent], {
|
|
4458
4721
|
stdio: "pipe",
|
|
4459
|
-
timeout: timeout +
|
|
4722
|
+
timeout: timeout + 3e4,
|
|
4723
|
+
env: { ...process.env }
|
|
4460
4724
|
});
|
|
4461
4725
|
if (result.status !== 0) {
|
|
4462
4726
|
const stderr = result.stderr?.toString() ?? "";
|
|
4463
|
-
|
|
4727
|
+
const fallbackResult = spawnSync("npx", [
|
|
4728
|
+
"playwright",
|
|
4729
|
+
"screenshot",
|
|
4730
|
+
"--browser",
|
|
4731
|
+
"chromium",
|
|
4732
|
+
"--viewport-size",
|
|
4733
|
+
`${width},${height}`,
|
|
4734
|
+
"--wait-for-timeout",
|
|
4735
|
+
"3000",
|
|
4736
|
+
"--full-page",
|
|
4737
|
+
url,
|
|
4738
|
+
outputPath
|
|
4739
|
+
], {
|
|
4740
|
+
stdio: "pipe",
|
|
4741
|
+
timeout: timeout + 15e3
|
|
4742
|
+
});
|
|
4743
|
+
if (fallbackResult.status !== 0) {
|
|
4744
|
+
throw new Error(`\uC2A4\uD06C\uB9B0\uC0F7 \uCEA1\uCC98 \uC2E4\uD328 (${width}px): ${stderr || "unknown error"}`);
|
|
4745
|
+
}
|
|
4464
4746
|
}
|
|
4465
4747
|
}
|
|
4466
4748
|
async function extractPageData(url, timeout) {
|
|
@@ -4473,8 +4755,29 @@ async function extractPageData(url, timeout) {
|
|
|
4473
4755
|
userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
|
|
4474
4756
|
});
|
|
4475
4757
|
const page = await context.newPage();
|
|
4476
|
-
await page.goto(${JSON.stringify(url)}, { waitUntil: '
|
|
4477
|
-
await page.waitForTimeout(
|
|
4758
|
+
await page.goto(${JSON.stringify(url)}, { waitUntil: 'domcontentloaded', timeout: ${timeout} });
|
|
4759
|
+
await page.waitForTimeout(5000);
|
|
4760
|
+
|
|
4761
|
+
// \uD31D\uC5C5/\uBAA8\uB2EC/\uC624\uBC84\uB808\uC774 \uC790\uB3D9 \uB2EB\uAE30
|
|
4762
|
+
await page.evaluate(() => { ${POPUP_DISMISS_SCRIPT} });
|
|
4763
|
+
await page.waitForTimeout(500);
|
|
4764
|
+
|
|
4765
|
+
// scroll simulation \u2014 lazy-load \uCF58\uD150\uCE20 \uD2B8\uB9AC\uAC70 (DOM \uCD94\uCD9C \uC804)
|
|
4766
|
+
await page.evaluate(async () => {
|
|
4767
|
+
const delay = (ms) => new Promise(r => setTimeout(r, ms));
|
|
4768
|
+
const scrollHeight = document.body.scrollHeight;
|
|
4769
|
+
const step = Math.floor(window.innerHeight * 0.7);
|
|
4770
|
+
for (let y = 0; y < scrollHeight; y += step) {
|
|
4771
|
+
window.scrollTo(0, y);
|
|
4772
|
+
await delay(300);
|
|
4773
|
+
}
|
|
4774
|
+
window.scrollTo(0, scrollHeight);
|
|
4775
|
+
await delay(500);
|
|
4776
|
+
window.scrollTo(0, 0);
|
|
4777
|
+
await delay(500);
|
|
4778
|
+
});
|
|
4779
|
+
await page.waitForTimeout(1000);
|
|
4780
|
+
|
|
4478
4781
|
const data = await page.evaluate(${JSON.stringify(EXTRACTION_SCRIPT)});
|
|
4479
4782
|
await browser.close();
|
|
4480
4783
|
process.stdout.write(JSON.stringify(data));
|
|
@@ -4536,6 +4839,8 @@ function createFallbackData() {
|
|
|
4536
4839
|
hasCarousel: false,
|
|
4537
4840
|
hasAccordion: false,
|
|
4538
4841
|
hasModal: false,
|
|
4842
|
+
hasTab: false,
|
|
4843
|
+
hasLightbox: false,
|
|
4539
4844
|
hasStickyHeader: false,
|
|
4540
4845
|
hasParallax: false,
|
|
4541
4846
|
detectedAnimations: [],
|
|
@@ -4738,6 +5043,8 @@ async function commandAnalyze(url, options) {
|
|
|
4738
5043
|
interactions.hasCarousel && "Carousel",
|
|
4739
5044
|
interactions.hasAccordion && "Accordion",
|
|
4740
5045
|
interactions.hasModal && "Modal",
|
|
5046
|
+
interactions.hasTab && "Tab",
|
|
5047
|
+
interactions.hasLightbox && "Lightbox",
|
|
4741
5048
|
interactions.hasParallax && "Parallax",
|
|
4742
5049
|
interactions.hasHoverEffects && "Hover Effects"
|
|
4743
5050
|
].filter(Boolean);
|
|
@@ -5103,25 +5410,77 @@ function checkCommand(cmd) {
|
|
|
5103
5410
|
}
|
|
5104
5411
|
}
|
|
5105
5412
|
function captureScreenshot2(url, outputPath, width, height) {
|
|
5106
|
-
const
|
|
5107
|
-
|
|
5108
|
-
|
|
5109
|
-
|
|
5110
|
-
|
|
5111
|
-
|
|
5112
|
-
|
|
5113
|
-
|
|
5114
|
-
|
|
5115
|
-
|
|
5116
|
-
|
|
5117
|
-
|
|
5118
|
-
|
|
5413
|
+
const scriptContent = `
|
|
5414
|
+
const { chromium } = require('playwright');
|
|
5415
|
+
(async () => {
|
|
5416
|
+
const browser = await chromium.launch({ headless: true });
|
|
5417
|
+
const context = await browser.newContext({
|
|
5418
|
+
viewport: { width: ${width}, height: ${height} },
|
|
5419
|
+
userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
|
|
5420
|
+
});
|
|
5421
|
+
const page = await context.newPage();
|
|
5422
|
+
await page.goto(${JSON.stringify(url)}, { waitUntil: 'domcontentloaded', timeout: 30000 });
|
|
5423
|
+
await page.waitForTimeout(3000);
|
|
5424
|
+
// \uD31D\uC5C5/\uBAA8\uB2EC \uC790\uB3D9 \uB2EB\uAE30
|
|
5425
|
+
await page.evaluate(() => {
|
|
5426
|
+
document.querySelectorAll('dialog[open]').forEach(d => d.close());
|
|
5427
|
+
['[class*=popup] [class*=close]','[class*=modal] [class*=close]',
|
|
5428
|
+
'[aria-label*=close]','[aria-label*="\uB2EB\uAE30"]','[aria-label*=Close]',
|
|
5429
|
+
'.popup-close','.modal-close','.btn-close'].forEach(sel => {
|
|
5430
|
+
document.querySelectorAll(sel).forEach(btn => { try { btn.click(); } catch {} });
|
|
5431
|
+
});
|
|
5432
|
+
document.querySelectorAll('[class*=overlay],[class*=dimmed],.modal-backdrop').forEach(el => {
|
|
5433
|
+
if (el.getBoundingClientRect().width >= window.innerWidth * 0.8) el.style.display = 'none';
|
|
5434
|
+
});
|
|
5435
|
+
document.body.style.overflow = '';
|
|
5436
|
+
document.body.style.position = '';
|
|
5437
|
+
document.documentElement.style.overflow = '';
|
|
5438
|
+
});
|
|
5439
|
+
await page.waitForTimeout(500);
|
|
5440
|
+
// scroll simulation \u2014 lazy-load \uCF58\uD150\uCE20 \uD2B8\uB9AC\uAC70
|
|
5441
|
+
await page.evaluate(async () => {
|
|
5442
|
+
const delay = (ms) => new Promise(r => setTimeout(r, ms));
|
|
5443
|
+
const step = Math.floor(window.innerHeight * 0.7);
|
|
5444
|
+
for (let y = 0; y < document.body.scrollHeight; y += step) {
|
|
5445
|
+
window.scrollTo(0, y);
|
|
5446
|
+
await delay(300);
|
|
5447
|
+
}
|
|
5448
|
+
window.scrollTo(0, document.body.scrollHeight);
|
|
5449
|
+
await delay(500);
|
|
5450
|
+
window.scrollTo(0, 0);
|
|
5451
|
+
await delay(500);
|
|
5452
|
+
});
|
|
5453
|
+
await page.waitForTimeout(1000);
|
|
5454
|
+
await page.screenshot({ path: ${JSON.stringify(outputPath)}, fullPage: true });
|
|
5455
|
+
await browser.close();
|
|
5456
|
+
})().catch(e => {
|
|
5457
|
+
process.stderr.write(e.message);
|
|
5458
|
+
process.exit(1);
|
|
5459
|
+
});
|
|
5460
|
+
`;
|
|
5461
|
+
const result = spawnSync2("node", ["-e", scriptContent], {
|
|
5119
5462
|
stdio: "pipe",
|
|
5120
|
-
timeout:
|
|
5463
|
+
timeout: 9e4,
|
|
5464
|
+
env: { ...process.env }
|
|
5121
5465
|
});
|
|
5122
5466
|
if (result.status !== 0) {
|
|
5123
5467
|
const stderr = result.stderr?.toString() ?? "";
|
|
5124
|
-
|
|
5468
|
+
const fallback = spawnSync2("npx", [
|
|
5469
|
+
"playwright",
|
|
5470
|
+
"screenshot",
|
|
5471
|
+
"--browser",
|
|
5472
|
+
"chromium",
|
|
5473
|
+
"--viewport-size",
|
|
5474
|
+
`${width},${height}`,
|
|
5475
|
+
"--wait-for-timeout",
|
|
5476
|
+
"3000",
|
|
5477
|
+
"--full-page",
|
|
5478
|
+
url,
|
|
5479
|
+
outputPath
|
|
5480
|
+
], { stdio: "pipe", timeout: 6e4 });
|
|
5481
|
+
if (fallback.status !== 0) {
|
|
5482
|
+
throw new Error(`\uC2A4\uD06C\uB9B0\uC0F7 \uCEA1\uCC98 \uC2E4\uD328: ${stderr || "unknown error"}`);
|
|
5483
|
+
}
|
|
5125
5484
|
}
|
|
5126
5485
|
}
|
|
5127
5486
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@saeroon/cli",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.8",
|
|
4
4
|
"description": "Saeroon Hosting developer CLI — preview, validate, and deploy sites & templates",
|
|
5
5
|
"private": false,
|
|
6
6
|
"type": "module",
|
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
"@saeroon/tsconfig": "workspace:*",
|
|
41
41
|
"@types/node": "^25.0.3",
|
|
42
42
|
"@types/ws": "^8.5.0",
|
|
43
|
+
"playwright": "^1.57.0",
|
|
43
44
|
"tsup": "^8.3.5",
|
|
44
45
|
"typescript": "~5.9.3"
|
|
45
46
|
},
|