codeplay-common 4.5.6 → 4.5.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.
|
@@ -208,12 +208,15 @@ async function ensureEndCardBanner() {
|
|
|
208
208
|
+ '- Keep all logos and badge text sharp, correctly proportioned, and completely visible.\n'
|
|
209
209
|
+ '- Match the background colors and visual style to the actual app icon.\n'
|
|
210
210
|
+ '- Create a polished, professional background related to the app’s purpose.\n'
|
|
211
|
-
+ '- Keep the background subtle so it does not compete with the icon or download badges.\n'
|
|
211
|
+
+ '- Keep the background subtle so it does not compete with the icon, title, feature text, or download badges.\n'
|
|
212
212
|
+ '- Use a balanced landscape composition:\n'
|
|
213
213
|
+ ' - Large actual app icon on the left.\n'
|
|
214
214
|
+ ' - App Store badge followed by Google Play badge on the right.\n'
|
|
215
215
|
+ ' - Maintain comfortable margins and clear space around every element.\n'
|
|
216
|
-
+ '-
|
|
216
|
+
+ '- Add the application title prominently.\n'
|
|
217
|
+
+ '- Add concise promotional text describing the application’s actual features, presenting each feature one by one.\n'
|
|
218
|
+
+ '- Keep the title and feature text sharp, readable, accurate, and visually balanced with the icon and download badges.\n'
|
|
219
|
+
+ '- Do not invent unsupported features or add unrelated marketing text.\n'
|
|
217
220
|
+ '- Do not add watermarks, fake UI, generated symbols, or unrelated decorations.\n'
|
|
218
221
|
+ '- Rounded corners and a subtle shadow may be applied around the app icon, but its internal artwork must remain unchanged.\n'
|
|
219
222
|
+ '- Use image generation only for the background.\n'
|
|
@@ -590,13 +590,6 @@ async function getPageDetails(client) {
|
|
|
590
590
|
async function capturePageSnapshot(client) {
|
|
591
591
|
const evaluation = await client.send('Runtime.evaluate', {
|
|
592
592
|
expression: `(async () => {
|
|
593
|
-
const clonedRoot = document.documentElement.cloneNode(true);
|
|
594
|
-
const originalElements = [
|
|
595
|
-
document.documentElement,
|
|
596
|
-
...document.documentElement.querySelectorAll('*'),
|
|
597
|
-
];
|
|
598
|
-
const clonedElements = [clonedRoot, ...clonedRoot.querySelectorAll('*')];
|
|
599
|
-
|
|
600
593
|
const blobToDataUrl = (blob) => new Promise((resolve, reject) => {
|
|
601
594
|
const reader = new FileReader();
|
|
602
595
|
reader.onload = () => resolve(reader.result);
|
|
@@ -617,109 +610,147 @@ async function capturePageSnapshot(client) {
|
|
|
617
610
|
return canvas.toDataURL('image/png');
|
|
618
611
|
};
|
|
619
612
|
|
|
620
|
-
|
|
621
|
-
const
|
|
622
|
-
const
|
|
613
|
+
const serializeDocument = async (sourceDocument) => {
|
|
614
|
+
const clonedRoot = sourceDocument.documentElement.cloneNode(true);
|
|
615
|
+
const originalElements = [
|
|
616
|
+
sourceDocument.documentElement,
|
|
617
|
+
...sourceDocument.documentElement.querySelectorAll('*'),
|
|
618
|
+
];
|
|
619
|
+
const clonedElements = [clonedRoot, ...clonedRoot.querySelectorAll('*')];
|
|
623
620
|
|
|
624
|
-
|
|
621
|
+
clonedRoot.setAttribute(
|
|
622
|
+
'data-responsive-window-scroll-y',
|
|
623
|
+
String(sourceDocument.defaultView?.scrollY || 0),
|
|
624
|
+
);
|
|
625
625
|
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
clone
|
|
629
|
-
}
|
|
626
|
+
for (let index = 0; index < originalElements.length; index += 1) {
|
|
627
|
+
const original = originalElements[index];
|
|
628
|
+
const clone = clonedElements[index];
|
|
630
629
|
|
|
631
|
-
|
|
632
|
-
clone.setAttribute('data-responsive-swiper-width', String(original.clientWidth));
|
|
633
|
-
clone.setAttribute('data-responsive-swiper-height', String(original.clientHeight));
|
|
634
|
-
}
|
|
630
|
+
if (!clone) continue;
|
|
635
631
|
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
} else if (original instanceof HTMLTextAreaElement) {
|
|
640
|
-
clone.textContent = original.value;
|
|
641
|
-
} else if (original instanceof HTMLSelectElement) {
|
|
642
|
-
for (let optionIndex = 0; optionIndex < original.options.length; optionIndex += 1) {
|
|
643
|
-
clone.options[optionIndex].selected = original.options[optionIndex].selected;
|
|
644
|
-
}
|
|
645
|
-
} else if (original instanceof HTMLCanvasElement) {
|
|
646
|
-
try {
|
|
647
|
-
clone.style.setProperty(
|
|
648
|
-
'background-image',
|
|
649
|
-
'url("' + original.toDataURL('image/png') + '")',
|
|
650
|
-
'important',
|
|
651
|
-
);
|
|
652
|
-
clone.style.setProperty('background-position', 'center', 'important');
|
|
653
|
-
clone.style.setProperty('background-repeat', 'no-repeat', 'important');
|
|
654
|
-
clone.style.setProperty('background-size', '100% 100%', 'important');
|
|
655
|
-
} catch {
|
|
656
|
-
// Keep the canvas element when its pixels cannot be exported.
|
|
632
|
+
if (original.scrollTop || original.scrollLeft) {
|
|
633
|
+
clone.setAttribute('data-responsive-scroll-top', String(original.scrollTop));
|
|
634
|
+
clone.setAttribute('data-responsive-scroll-left', String(original.scrollLeft));
|
|
657
635
|
}
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
replacement.alt = original.getAttribute('aria-label') || '';
|
|
663
|
-
replacement.setAttribute('style', original.getAttribute('style') || '');
|
|
664
|
-
clone.replaceWith(replacement);
|
|
665
|
-
} catch {
|
|
666
|
-
clone.setAttribute('poster', original.poster || '');
|
|
636
|
+
|
|
637
|
+
if (original.classList.contains('swiper-initialized')) {
|
|
638
|
+
clone.setAttribute('data-responsive-swiper-width', String(original.clientWidth));
|
|
639
|
+
clone.setAttribute('data-responsive-swiper-height', String(original.clientHeight));
|
|
667
640
|
}
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
clone.
|
|
641
|
+
|
|
642
|
+
const tagName = original.tagName?.toLowerCase();
|
|
643
|
+
|
|
644
|
+
if (tagName === 'input') {
|
|
645
|
+
clone.setAttribute('value', original.value);
|
|
646
|
+
clone.toggleAttribute('checked', original.checked);
|
|
647
|
+
} else if (tagName === 'textarea') {
|
|
648
|
+
clone.textContent = original.value;
|
|
649
|
+
} else if (tagName === 'select') {
|
|
650
|
+
for (let optionIndex = 0; optionIndex < original.options.length; optionIndex += 1) {
|
|
651
|
+
clone.options[optionIndex].selected = original.options[optionIndex].selected;
|
|
652
|
+
}
|
|
653
|
+
} else if (tagName === 'canvas') {
|
|
654
|
+
try {
|
|
655
|
+
clone.style.setProperty(
|
|
656
|
+
'background-image',
|
|
657
|
+
'url("' + original.toDataURL('image/png') + '")',
|
|
658
|
+
'important',
|
|
659
|
+
);
|
|
660
|
+
clone.style.setProperty('background-position', 'center', 'important');
|
|
661
|
+
clone.style.setProperty('background-repeat', 'no-repeat', 'important');
|
|
662
|
+
clone.style.setProperty('background-size', '100% 100%', 'important');
|
|
663
|
+
} catch {
|
|
664
|
+
// Keep the canvas element when its pixels cannot be exported.
|
|
665
|
+
}
|
|
666
|
+
} else if (tagName === 'video' && original.readyState >= 2) {
|
|
667
|
+
try {
|
|
668
|
+
const replacement = sourceDocument.createElement('img');
|
|
669
|
+
replacement.src = imageToDataUrl(original);
|
|
670
|
+
replacement.alt = original.getAttribute('aria-label') || '';
|
|
671
|
+
replacement.setAttribute('style', original.getAttribute('style') || '');
|
|
672
|
+
clone.replaceWith(replacement);
|
|
673
|
+
} catch {
|
|
674
|
+
clone.setAttribute('poster', original.poster || '');
|
|
675
|
+
}
|
|
676
|
+
} else if (tagName === 'img' && original.complete) {
|
|
677
|
+
try {
|
|
678
|
+
clone.setAttribute('src', imageToDataUrl(original) || original.currentSrc || original.src);
|
|
679
|
+
clone.removeAttribute('srcset');
|
|
680
|
+
} catch {
|
|
681
|
+
clone.setAttribute('src', original.currentSrc || original.src);
|
|
682
|
+
}
|
|
683
|
+
} else if (tagName === 'iframe') {
|
|
684
|
+
try {
|
|
685
|
+
const frameDocument = original.contentDocument;
|
|
686
|
+
|
|
687
|
+
if (frameDocument?.documentElement) {
|
|
688
|
+
const frameHtml = await serializeDocument(frameDocument);
|
|
689
|
+
clone.removeAttribute('src');
|
|
690
|
+
clone.setAttribute('srcdoc', '<!doctype html>' + frameHtml);
|
|
691
|
+
}
|
|
692
|
+
} catch {
|
|
693
|
+
// Cross-origin frames cannot be serialized and keep their original source.
|
|
694
|
+
}
|
|
674
695
|
}
|
|
675
696
|
}
|
|
676
|
-
}
|
|
677
697
|
|
|
678
|
-
|
|
679
|
-
|
|
698
|
+
const stylesheetLinks = [...sourceDocument.querySelectorAll('link[rel="stylesheet"]')];
|
|
699
|
+
const clonedStylesheetLinks = [...clonedRoot.querySelectorAll('link[rel="stylesheet"]')];
|
|
680
700
|
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
+
await Promise.all(stylesheetLinks.map(async (link, index) => {
|
|
702
|
+
try {
|
|
703
|
+
const response = await fetch(link.href);
|
|
704
|
+
if (!response.ok) return;
|
|
705
|
+
|
|
706
|
+
const cssText = (await response.text()).replace(
|
|
707
|
+
/url\\(\\s*(['"]?)(?!data:|blob:|#)([^'"\\)]+)\\1\\s*\\)/gi,
|
|
708
|
+
(match, quote, resourceUrl) => {
|
|
709
|
+
try {
|
|
710
|
+
return 'url("' + new URL(resourceUrl.trim(), link.href).href + '")';
|
|
711
|
+
} catch {
|
|
712
|
+
return match;
|
|
713
|
+
}
|
|
714
|
+
},
|
|
715
|
+
);
|
|
716
|
+
const style = sourceDocument.createElement('style');
|
|
717
|
+
style.textContent = cssText;
|
|
718
|
+
clonedStylesheetLinks[index].replaceWith(style);
|
|
719
|
+
} catch {
|
|
720
|
+
clonedStylesheetLinks[index].href = link.href;
|
|
721
|
+
}
|
|
722
|
+
}));
|
|
723
|
+
|
|
724
|
+
clonedRoot.querySelectorAll('script, link[rel="modulepreload"]').forEach(
|
|
725
|
+
(element) => element.remove(),
|
|
726
|
+
);
|
|
727
|
+
clonedRoot.querySelectorAll('meta[http-equiv="Content-Security-Policy"]').forEach(
|
|
728
|
+
(element) => element.remove(),
|
|
729
|
+
);
|
|
730
|
+
|
|
731
|
+
const head = clonedRoot.querySelector('head');
|
|
732
|
+
|
|
733
|
+
if (head) {
|
|
734
|
+
const base = sourceDocument.createElement('base');
|
|
735
|
+
base.href = sourceDocument.location.href;
|
|
736
|
+
head.prepend(base);
|
|
737
|
+
|
|
738
|
+
const replayStyle = sourceDocument.createElement('style');
|
|
739
|
+
replayStyle.textContent = [
|
|
740
|
+
'*,*::before,*::after{animation:none!important;transition:none!important;}',
|
|
741
|
+
'*{scrollbar-width:none!important;}',
|
|
742
|
+
'*::-webkit-scrollbar{width:0!important;height:0!important;display:none!important;}',
|
|
743
|
+
].join('');
|
|
744
|
+
head.append(replayStyle);
|
|
701
745
|
}
|
|
702
|
-
}));
|
|
703
746
|
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
(element) => element.remove(),
|
|
707
|
-
);
|
|
708
|
-
|
|
709
|
-
const base = document.createElement('base');
|
|
710
|
-
base.href = location.href;
|
|
711
|
-
clonedRoot.querySelector('head').prepend(base);
|
|
747
|
+
return clonedRoot.outerHTML;
|
|
748
|
+
};
|
|
712
749
|
|
|
713
|
-
const
|
|
714
|
-
replayStyle.textContent = [
|
|
715
|
-
'*,*::before,*::after{animation-play-state:paused!important;}',
|
|
716
|
-
'*{scrollbar-width:none!important;}',
|
|
717
|
-
'*::-webkit-scrollbar{width:0!important;height:0!important;display:none!important;}',
|
|
718
|
-
].join('');
|
|
719
|
-
clonedRoot.querySelector('head').append(replayStyle);
|
|
750
|
+
const html = await serializeDocument(document);
|
|
720
751
|
|
|
721
752
|
return {
|
|
722
|
-
html: '<!doctype html>' +
|
|
753
|
+
html: '<!doctype html>' + html,
|
|
723
754
|
scrollY,
|
|
724
755
|
};
|
|
725
756
|
})()`,
|
|
@@ -876,7 +907,43 @@ async function closeReplayBrowser(browser) {
|
|
|
876
907
|
async function waitForReplayLayout(client, snapshot) {
|
|
877
908
|
const evaluation = await client.send('Runtime.evaluate', {
|
|
878
909
|
expression: `(async () => {
|
|
879
|
-
const
|
|
910
|
+
const collectDocuments = (rootDocument) => {
|
|
911
|
+
const documents = [rootDocument];
|
|
912
|
+
|
|
913
|
+
rootDocument.querySelectorAll('iframe').forEach((frame) => {
|
|
914
|
+
try {
|
|
915
|
+
if (frame.contentDocument?.documentElement) {
|
|
916
|
+
documents.push(...collectDocuments(frame.contentDocument));
|
|
917
|
+
}
|
|
918
|
+
} catch {
|
|
919
|
+
// Cross-origin frames cannot participate in replay layout restoration.
|
|
920
|
+
}
|
|
921
|
+
});
|
|
922
|
+
|
|
923
|
+
return documents;
|
|
924
|
+
};
|
|
925
|
+
|
|
926
|
+
const frameDeadline = Date.now() + 5000;
|
|
927
|
+
|
|
928
|
+
while (Date.now() < frameDeadline) {
|
|
929
|
+
const frames = [...document.querySelectorAll('iframe')];
|
|
930
|
+
const framesReady = frames.every((frame) => {
|
|
931
|
+
try {
|
|
932
|
+
return frame.contentDocument?.readyState === 'complete';
|
|
933
|
+
} catch {
|
|
934
|
+
return true;
|
|
935
|
+
}
|
|
936
|
+
});
|
|
937
|
+
|
|
938
|
+
if (framesReady) break;
|
|
939
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
const replayDocuments = collectDocuments(document);
|
|
943
|
+
const replayImages = replayDocuments.flatMap((replayDocument) => (
|
|
944
|
+
[...replayDocument.images]
|
|
945
|
+
));
|
|
946
|
+
const assetWait = Promise.all(replayImages.map((image) => {
|
|
880
947
|
if (image.complete) return Promise.resolve();
|
|
881
948
|
return new Promise((resolve) => {
|
|
882
949
|
image.addEventListener('load', resolve, { once: true });
|
|
@@ -885,11 +952,18 @@ async function waitForReplayLayout(client, snapshot) {
|
|
|
885
952
|
}));
|
|
886
953
|
|
|
887
954
|
await Promise.race([
|
|
888
|
-
Promise.all([
|
|
955
|
+
Promise.all([
|
|
956
|
+
...replayDocuments.map((replayDocument) => replayDocument.fonts.ready),
|
|
957
|
+
assetWait,
|
|
958
|
+
]),
|
|
889
959
|
new Promise((resolve) => setTimeout(resolve, 5000)),
|
|
890
960
|
]);
|
|
891
961
|
|
|
892
|
-
|
|
962
|
+
const replayElements = (selector) => replayDocuments.flatMap((replayDocument) => (
|
|
963
|
+
[...replayDocument.querySelectorAll(selector)]
|
|
964
|
+
));
|
|
965
|
+
|
|
966
|
+
replayElements('[data-responsive-swiper-width]').forEach((swiper) => {
|
|
893
967
|
const wrapper = [...swiper.children].find((element) => (
|
|
894
968
|
element.classList.contains('swiper-wrapper')
|
|
895
969
|
));
|
|
@@ -947,10 +1021,17 @@ async function waitForReplayLayout(client, snapshot) {
|
|
|
947
1021
|
}
|
|
948
1022
|
});
|
|
949
1023
|
|
|
950
|
-
|
|
1024
|
+
replayElements('[data-responsive-scroll-top]').forEach((element) => {
|
|
951
1025
|
element.scrollTop = Number(element.dataset.responsiveScrollTop);
|
|
952
1026
|
element.scrollLeft = Number(element.dataset.responsiveScrollLeft);
|
|
953
1027
|
});
|
|
1028
|
+
|
|
1029
|
+
replayDocuments.forEach((replayDocument) => {
|
|
1030
|
+
replayDocument.defaultView?.scrollTo(
|
|
1031
|
+
0,
|
|
1032
|
+
Number(replayDocument.documentElement.dataset.responsiveWindowScrollY) || 0,
|
|
1033
|
+
);
|
|
1034
|
+
});
|
|
954
1035
|
scrollTo(0, ${Number(snapshot.scrollY) || 0});
|
|
955
1036
|
|
|
956
1037
|
await new Promise((resolve) => requestAnimationFrame(() => requestAnimationFrame(resolve)));
|