codeplay-common 4.10.5 → 4.10.7

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.
@@ -86,8 +86,8 @@ const analysisFrameDirectory = path.join(temporaryDirectory, 'ai-analysis');
86
86
  const endCardBannerPath = path.join(outputVideoDirectory, `${appFilePrefix}-ai-end-card-banner.png`);
87
87
  const promoCopySchemaPath = path.join(temporaryDirectory, 'promo-copy-schema.json');
88
88
  const promoCopyResultPath = path.join(temporaryDirectory, 'promo-copy-result.json');
89
- const ffmpegFallbackDirectory = String(process.env.FFMPEG_BIN || '').trim()
90
- || path.join(projectDirectory, 'tools', 'ffmpeg');
89
+ const ffmpegFallbackDirectory = String(process.env.FFMPEG_BIN || '').trim()
90
+ || path.join(projectDirectory, 'tools', 'ffmpeg');
91
91
  const minimumSceneCount = 7;
92
92
  const minimumSourceDuration = 12;
93
93
  let expectedDuration = 34;
@@ -209,15 +209,15 @@ async function ensureEndCardBanner() {
209
209
  + '- Keep all logos and badge text sharp, correctly proportioned, and completely visible.\n'
210
210
  + '- Match the background colors and visual style to the actual app icon.\n'
211
211
  + '- Create a polished, professional background related to the app’s purpose.\n'
212
- + '- Keep the background subtle so it does not compete with the icon, title, feature text, or download badges.\n'
212
+ + '- Keep the background subtle so it does not compete with the icon, title, feature text, or download badges.\n'
213
213
  + '- Use a balanced landscape composition:\n'
214
214
  + ' - Large actual app icon on the left.\n'
215
215
  + ' - App Store badge followed by Google Play badge on the right.\n'
216
216
  + ' - Maintain comfortable margins and clear space around every element.\n'
217
- + '- Add the application title prominently.\n'
218
- + '- Add concise promotional text describing the application’s actual features, presenting each feature one by one.\n'
219
- + '- Keep the title and feature text sharp, readable, accurate, and visually balanced with the icon and download badges.\n'
220
- + '- Do not invent unsupported features or add unrelated marketing text.\n'
217
+ + '- Add the application title prominently.\n'
218
+ + '- Add concise promotional text describing the application’s actual features, presenting each feature one by one.\n'
219
+ + '- Keep the title and feature text sharp, readable, accurate, and visually balanced with the icon and download badges.\n'
220
+ + '- Do not invent unsupported features or add unrelated marketing text.\n'
221
221
  + '- Do not add watermarks, fake UI, generated symbols, or unrelated decorations.\n'
222
222
  + '- Rounded corners and a subtle shadow may be applied around the app icon, but its internal artwork must remain unchanged.\n'
223
223
  + '- Use image generation only for the background.\n'
@@ -467,12 +467,12 @@ function commandExists(command) {
467
467
  return !result.error && result.status === 0;
468
468
  }
469
469
 
470
- function getExecutable(command) {
471
- const executableName = process.platform === 'win32' ? `${command}.exe` : command;
472
- const fallbackPath = path.join(ffmpegFallbackDirectory, executableName);
473
- if (fs.existsSync(fallbackPath)) return fallbackPath;
474
- return commandExists(command) ? command : '';
475
- }
470
+ function getExecutable(command) {
471
+ const executableName = process.platform === 'win32' ? `${command}.exe` : command;
472
+ const fallbackPath = path.join(ffmpegFallbackDirectory, executableName);
473
+ if (fs.existsSync(fallbackPath)) return fallbackPath;
474
+ return commandExists(command) ? command : '';
475
+ }
476
476
 
477
477
  function detectRedundantStaticRanges(ffmpegCommand, inputPath, sourceDuration) {
478
478
  const minimumStaticDuration = Math.max(2, Math.min(4, sourceDuration / 60));
@@ -640,7 +640,7 @@ function resolvePromoLayout(requestedLayout) {
640
640
  }
641
641
 
642
642
  function getPythonExecutable() {
643
- for (const candidate of ['py', 'python', 'python3']) {
643
+ for (const candidate of ['py', 'python', 'python3']) {
644
644
  const result = spawnSync(candidate, ['-m', 'edge_tts', '--version'], {
645
645
  encoding: 'utf8',
646
646
  windowsHide: true,
@@ -915,7 +915,7 @@ Rules:
915
915
  - footer_keywords must contain exactly 3 distinct, natural app-search phrases, each 2-24 characters.
916
916
  - Generate a YouTube upload title and description from the same verified features.
917
917
  - YouTube title: 40-100 characters, include ${JSON.stringify(appName)} and one strong natural search phrase, without clickbait.
918
- - YouTube description: 350-1500 characters with readable paragraphs. Put the clearest app summary and primary search phrase in the opening two lines, explain the demonstrated features, add a simple call to action, then insert a newline and finish with exactly 3 relevant hashtags on that separate final line. The hashtags must not be appended to the preceding paragraph.
918
+ - YouTube description: 350-1500 characters with readable paragraphs. Put the clearest app summary and primary search phrase in the opening two lines, explain the demonstrated features, add a simple call to action, then insert a newline and finish with exactly 3 relevant hashtags on that separate final line. The hashtags must not be appended to the preceding paragraph.
919
919
  - Do not add download links or URLs; the script will append the verified Google Play URL built from the configured package ID. Do not add chapter timestamps or unsupported availability claims.
920
920
  - Keep YouTube text natural and useful; do not repeat keywords unnaturally.
921
921
  - Avoid prices, ratings, awards, competitor names, unsupported superlatives, and words such as best, guaranteed, or free.
@@ -1025,14 +1025,14 @@ function validatePromoCopy(value) {
1025
1025
  throw new Error(`The final scene must mention the app name ${JSON.stringify(appName)}.`);
1026
1026
  }
1027
1027
 
1028
- const youtubeTitle = cleanGeneratedString(value.youtube.title);
1029
- let youtubeDescription = cleanGeneratedDescription(value.youtube.description);
1030
- const trailingHashtagGroup = youtubeDescription.match(
1031
- /(?:^|\s)(#[\p{L}\p{N}_]+\s+#[\p{L}\p{N}_]+\s+#[\p{L}\p{N}_]+)\s*$/u,
1032
- );
1033
- if (trailingHashtagGroup) {
1034
- youtubeDescription = `${youtubeDescription.slice(0, trailingHashtagGroup.index).trimEnd()}\n${trailingHashtagGroup[1]}`;
1035
- }
1028
+ const youtubeTitle = cleanGeneratedString(value.youtube.title);
1029
+ let youtubeDescription = cleanGeneratedDescription(value.youtube.description);
1030
+ const trailingHashtagGroup = youtubeDescription.match(
1031
+ /(?:^|\s)(#[\p{L}\p{N}_]+\s+#[\p{L}\p{N}_]+\s+#[\p{L}\p{N}_]+)\s*$/u,
1032
+ );
1033
+ if (trailingHashtagGroup) {
1034
+ youtubeDescription = `${youtubeDescription.slice(0, trailingHashtagGroup.index).trimEnd()}\n${trailingHashtagGroup[1]}`;
1035
+ }
1036
1036
  if (youtubeTitle.length < 40 || youtubeTitle.length > 100) {
1037
1037
  throw new Error('YouTube title must be 40-100 characters.');
1038
1038
  }
@@ -1,7 +1,7 @@
1
- {
2
- "name": "merbin-test-app",
3
- "integrations": {
4
- "capacitor": {}
5
- },
6
- "type": "custom"
1
+ {
2
+ "name": "merbin-test-app",
3
+ "integrations": {
4
+ "capacitor": {}
5
+ },
6
+ "type": "custom"
7
7
  }
@@ -1,21 +1,21 @@
1
- const fs = require('fs');
2
- const path = require('path');
3
- const { execFileSync } = require('child_process');
4
-
5
- const root = path.resolve(__dirname, '..');
6
- const buildDir = path.join(root, 'buildCodeplay');
7
-
8
- function run(scriptPath) {
9
- execFileSync(process.execPath, [scriptPath], { cwd: root, stdio: 'inherit' });
10
- }
11
-
12
- const splashIcon = path.join(root, 'resources', 'splash_icon.png');
13
- if (fs.existsSync(splashIcon)) {
14
- run(path.join(buildDir, 'add-splash-screen.js'));
15
- run(path.join(buildDir, 'setSplashAnimation.js'));
16
- } else {
17
- console.log('ℹ️ Keeping the existing native splash because resources/splash_icon.png is not configured.');
18
- }
19
-
20
- run(path.join(buildDir, 'manifestModification.js'));
21
- run(path.join(__dirname, 'setup-baseline-profile.js'));
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const { execFileSync } = require('child_process');
4
+
5
+ const root = path.resolve(__dirname, '..');
6
+ const buildDir = path.join(root, 'buildCodeplay');
7
+
8
+ function run(scriptPath) {
9
+ execFileSync(process.execPath, [scriptPath], { cwd: root, stdio: 'inherit' });
10
+ }
11
+
12
+ const splashIcon = path.join(root, 'resources', 'splash_icon.png');
13
+ if (fs.existsSync(splashIcon)) {
14
+ run(path.join(buildDir, 'add-splash-screen.js'));
15
+ run(path.join(buildDir, 'setSplashAnimation.js'));
16
+ } else {
17
+ console.log('ℹ️ Keeping the existing native splash because resources/splash_icon.png is not configured.');
18
+ }
19
+
20
+ run(path.join(buildDir, 'manifestModification.js'));
21
+ run(path.join(__dirname, 'setup-baseline-profile.js'));
@@ -1,120 +1,120 @@
1
- const decorationShapes = Object.freeze({
2
- ring: { className: 'ring', scale: 1, opacity: 0.28 },
3
- doubleRing: { className: 'double-ring', scale: 1.08, opacity: 0.24 },
4
- pill: { className: 'pill', scale: 1.15, opacity: 0.22 },
5
- dots: { className: 'dots', scale: 0.72, opacity: 0.25 },
6
- sparkle: { className: 'sparkle', scale: 0.72, opacity: 0.25 },
7
- diamond: { className: 'diamond', scale: 0.78, opacity: 0.2 },
8
- triangle: { className: 'triangle', scale: 0.8, opacity: 0.2 },
9
- hexagon: { className: 'hexagon', scale: 0.9, opacity: 0.2 },
10
- squircle: { className: 'squircle', scale: 0.9, opacity: 0.2 },
11
- plus: { className: 'plus', scale: 0.7, opacity: 0.22 },
12
- chevron: { className: 'chevron', scale: 0.82, opacity: 0.24 },
13
- orbit: { className: 'orbit', scale: 1.25, opacity: 0.25 },
14
- bars: { className: 'bars', scale: 0.82, opacity: 0.2 },
15
- flower: { className: 'flower', scale: 0.8, opacity: 0.2 },
16
- crescent: { className: 'crescent', scale: 0.82, opacity: 0.22 },
17
- wave: { className: 'wave', scale: 1.18, opacity: 0.24 },
18
- cube: { className: 'cube', scale: 0.88, opacity: 0.18 },
19
- burst: { className: 'burst', scale: 0.72, opacity: 0.2 },
20
- });
21
-
22
- const decorationSets = Object.freeze([
23
- ['ring', 'sparkle', 'pill', 'dots', 'diamond', 'orbit'],
24
- ['doubleRing', 'triangle', 'bars', 'crescent', 'sparkle', 'pill'],
25
- ['hexagon', 'wave', 'dots', 'plus', 'ring', 'flower'],
26
- ['squircle', 'orbit', 'chevron', 'burst', 'pill', 'diamond'],
27
- ['flower', 'doubleRing', 'cube', 'dots', 'wave', 'plus'],
28
- ['crescent', 'sparkle', 'hexagon', 'bars', 'ring', 'chevron'],
29
- ['cube', 'pill', 'burst', 'orbit', 'triangle', 'dots'],
30
- ['wave', 'diamond', 'flower', 'doubleRing', 'squircle', 'sparkle'],
31
- ]);
32
-
33
- const safeAnchors = Object.freeze([
34
- [5, 11],
35
- [91, 13],
36
- [5, 38],
37
- [92, 42],
38
- [6, 70],
39
- [91, 73],
40
- [12, 91],
41
- [85, 90],
42
- ]);
43
-
44
- function getDecorationSeed(value) {
45
- let hash = 2166136261;
46
- for (const character of String(value)) {
47
- hash ^= character.charCodeAt(0);
48
- hash = Math.imul(hash, 16777619);
49
- }
50
- return hash >>> 0;
51
- }
52
-
53
- function createSeededRandom(seed) {
54
- let state = seed >>> 0;
55
- return () => {
56
- state += 0x6D2B79F5;
57
- let value = state;
58
- value = Math.imul(value ^ (value >>> 15), value | 1);
59
- value ^= value + Math.imul(value ^ (value >>> 7), value | 61);
60
- return ((value ^ (value >>> 14)) >>> 0) / 4294967296;
61
- };
62
- }
63
-
64
- function createPromotionalDecorations(palette) {
65
- const random = createSeededRandom(palette.seed);
66
- const selectedSet = decorationSets[palette.seed % decorationSets.length];
67
- const objectCount = 5 + (palette.seed % 2);
68
- const anchorOffset = palette.seed % safeAnchors.length;
69
- const objects = selectedSet.slice(0, objectCount).map((shapeName, index) => {
70
- const shape = decorationShapes[shapeName];
71
- const [baseX, baseY] = safeAnchors[(index + anchorOffset) % safeAnchors.length];
72
- const size = (3.4 + random() * 4.8) * shape.scale;
73
- const left = Math.max(1, Math.min(94, baseX + (random() - 0.5) * 5));
74
- const top = Math.max(2, Math.min(92, baseY + (random() - 0.5) * 6));
75
- const rotation = Math.round(random() * 150 - 75);
76
- const color = index % 2 ? palette.secondary : palette.accent;
77
- return `<span class="promo-object ${shape.className}" style="left:${left.toFixed(1)}%;top:${top.toFixed(1)}%;width:${size.toFixed(1)}vmin;transform:rotate(${rotation}deg);color:${color};opacity:${shape.opacity};"></span>`;
78
- }).join('');
79
-
80
- return `<div class="promo-decor" aria-hidden="true">${objects}</div>`;
81
- }
82
-
83
- function getPromotionalDecorationStyles() {
84
- return `
85
- .promo-decor { position: absolute; z-index: 0; inset: 0; overflow: hidden; pointer-events: none; }
86
- .promo-object { position: absolute; display: block; aspect-ratio: 1; }
87
- .promo-object::before,
88
- .promo-object::after { content: ''; position: absolute; }
89
- .promo-object.ring { border: clamp(2px, 0.28vw, 6px) solid currentColor; border-radius: 50%; }
90
- .promo-object.double-ring { border: clamp(2px, 0.24vw, 5px) solid currentColor; border-radius: 50%; }
91
- .promo-object.double-ring::after { inset: 20%; border: clamp(1px, 0.15vw, 3px) solid currentColor; border-radius: 50%; }
92
- .promo-object.pill { aspect-ratio: 2.3; border-radius: 999px; background: currentColor; }
93
- .promo-object.dots { border-radius: 50%; background: currentColor; box-shadow: 1.2vmin 0 0 -0.22vmin currentColor, 0 1.2vmin 0 -0.22vmin currentColor, 1.2vmin 1.2vmin 0 -0.22vmin currentColor; }
94
- .promo-object.sparkle { background: currentColor; clip-path: polygon(50% 0, 61% 39%, 100% 50%, 61% 61%, 50% 100%, 39% 61%, 0 50%, 39% 39%); }
95
- .promo-object.diamond { border: clamp(2px, 0.25vw, 5px) solid currentColor; border-radius: 18%; transform-origin: center; }
96
- .promo-object.triangle { background: currentColor; clip-path: polygon(50% 4%, 96% 91%, 4% 91%); }
97
- .promo-object.hexagon { background: currentColor; clip-path: polygon(25% 7%, 75% 7%, 100% 50%, 75% 93%, 25% 93%, 0 50%); }
98
- .promo-object.squircle { border: clamp(2px, 0.25vw, 5px) solid currentColor; border-radius: 32%; }
99
- .promo-object.plus { background: currentColor; clip-path: polygon(36% 0, 64% 0, 64% 36%, 100% 36%, 100% 64%, 64% 64%, 64% 100%, 36% 100%, 36% 64%, 0 64%, 0 36%, 36% 36%); }
100
- .promo-object.chevron { background: currentColor; clip-path: polygon(0 18%, 50% 66%, 100% 18%, 100% 48%, 50% 96%, 0 48%); }
101
- .promo-object.orbit { aspect-ratio: 1.8; border: clamp(2px, 0.23vw, 5px) solid currentColor; border-radius: 50%; }
102
- .promo-object.orbit::after { top: 50%; left: 88%; width: 20%; aspect-ratio: 1; transform: translate(-50%, -50%); border-radius: 50%; background: currentColor; }
103
- .promo-object.bars { background: repeating-linear-gradient(90deg, currentColor 0 12%, transparent 12% 24%); border-radius: 18%; }
104
- .promo-object.flower { background: currentColor; clip-path: polygon(50% 25%, 65% 5%, 76% 28%, 100% 24%, 88% 50%, 100% 76%, 76% 72%, 65% 95%, 50% 75%, 35% 95%, 24% 72%, 0 76%, 12% 50%, 0 24%, 24% 28%, 35% 5%); }
105
- .promo-object.crescent { border-radius: 50%; box-shadow: inset 1.7vmin -0.45vmin 0 0 currentColor; }
106
- .promo-object.wave { aspect-ratio: 2.2; border-top: clamp(3px, 0.35vw, 7px) solid currentColor; border-radius: 50%; }
107
- .promo-object.wave::after { top: 28%; left: 38%; width: 62%; height: 48%; border-bottom: clamp(3px, 0.35vw, 7px) solid currentColor; border-radius: 50%; }
108
- .promo-object.cube { background: currentColor; clip-path: polygon(50% 0, 94% 24%, 94% 74%, 50% 100%, 6% 74%, 6% 24%); }
109
- .promo-object.cube::after { inset: 17% 20%; background: rgba(255,255,255,0.42); clip-path: polygon(50% 0, 100% 24%, 100% 70%, 50% 100%, 0 70%, 0 24%); }
110
- .promo-object.burst { background: currentColor; clip-path: polygon(50% 0, 59% 34%, 85% 15%, 66% 41%, 100% 50%, 66% 59%, 85% 85%, 59% 66%, 50% 100%, 41% 66%, 15% 85%, 34% 59%, 0 50%, 34% 41%, 15% 15%, 41% 34%); }
111
- `;
112
- }
113
-
114
- module.exports = {
115
- decorationShapes,
116
- decorationSets,
117
- getDecorationSeed,
118
- createPromotionalDecorations,
119
- getPromotionalDecorationStyles,
120
- };
1
+ const decorationShapes = Object.freeze({
2
+ ring: { className: 'ring', scale: 1, opacity: 0.28 },
3
+ doubleRing: { className: 'double-ring', scale: 1.08, opacity: 0.24 },
4
+ pill: { className: 'pill', scale: 1.15, opacity: 0.22 },
5
+ dots: { className: 'dots', scale: 0.72, opacity: 0.25 },
6
+ sparkle: { className: 'sparkle', scale: 0.72, opacity: 0.25 },
7
+ diamond: { className: 'diamond', scale: 0.78, opacity: 0.2 },
8
+ triangle: { className: 'triangle', scale: 0.8, opacity: 0.2 },
9
+ hexagon: { className: 'hexagon', scale: 0.9, opacity: 0.2 },
10
+ squircle: { className: 'squircle', scale: 0.9, opacity: 0.2 },
11
+ plus: { className: 'plus', scale: 0.7, opacity: 0.22 },
12
+ chevron: { className: 'chevron', scale: 0.82, opacity: 0.24 },
13
+ orbit: { className: 'orbit', scale: 1.25, opacity: 0.25 },
14
+ bars: { className: 'bars', scale: 0.82, opacity: 0.2 },
15
+ flower: { className: 'flower', scale: 0.8, opacity: 0.2 },
16
+ crescent: { className: 'crescent', scale: 0.82, opacity: 0.22 },
17
+ wave: { className: 'wave', scale: 1.18, opacity: 0.24 },
18
+ cube: { className: 'cube', scale: 0.88, opacity: 0.18 },
19
+ burst: { className: 'burst', scale: 0.72, opacity: 0.2 },
20
+ });
21
+
22
+ const decorationSets = Object.freeze([
23
+ ['ring', 'sparkle', 'pill', 'dots', 'diamond', 'orbit'],
24
+ ['doubleRing', 'triangle', 'bars', 'crescent', 'sparkle', 'pill'],
25
+ ['hexagon', 'wave', 'dots', 'plus', 'ring', 'flower'],
26
+ ['squircle', 'orbit', 'chevron', 'burst', 'pill', 'diamond'],
27
+ ['flower', 'doubleRing', 'cube', 'dots', 'wave', 'plus'],
28
+ ['crescent', 'sparkle', 'hexagon', 'bars', 'ring', 'chevron'],
29
+ ['cube', 'pill', 'burst', 'orbit', 'triangle', 'dots'],
30
+ ['wave', 'diamond', 'flower', 'doubleRing', 'squircle', 'sparkle'],
31
+ ]);
32
+
33
+ const safeAnchors = Object.freeze([
34
+ [5, 11],
35
+ [91, 13],
36
+ [5, 38],
37
+ [92, 42],
38
+ [6, 70],
39
+ [91, 73],
40
+ [12, 91],
41
+ [85, 90],
42
+ ]);
43
+
44
+ function getDecorationSeed(value) {
45
+ let hash = 2166136261;
46
+ for (const character of String(value)) {
47
+ hash ^= character.charCodeAt(0);
48
+ hash = Math.imul(hash, 16777619);
49
+ }
50
+ return hash >>> 0;
51
+ }
52
+
53
+ function createSeededRandom(seed) {
54
+ let state = seed >>> 0;
55
+ return () => {
56
+ state += 0x6D2B79F5;
57
+ let value = state;
58
+ value = Math.imul(value ^ (value >>> 15), value | 1);
59
+ value ^= value + Math.imul(value ^ (value >>> 7), value | 61);
60
+ return ((value ^ (value >>> 14)) >>> 0) / 4294967296;
61
+ };
62
+ }
63
+
64
+ function createPromotionalDecorations(palette) {
65
+ const random = createSeededRandom(palette.seed);
66
+ const selectedSet = decorationSets[palette.seed % decorationSets.length];
67
+ const objectCount = 5 + (palette.seed % 2);
68
+ const anchorOffset = palette.seed % safeAnchors.length;
69
+ const objects = selectedSet.slice(0, objectCount).map((shapeName, index) => {
70
+ const shape = decorationShapes[shapeName];
71
+ const [baseX, baseY] = safeAnchors[(index + anchorOffset) % safeAnchors.length];
72
+ const size = (3.4 + random() * 4.8) * shape.scale;
73
+ const left = Math.max(1, Math.min(94, baseX + (random() - 0.5) * 5));
74
+ const top = Math.max(2, Math.min(92, baseY + (random() - 0.5) * 6));
75
+ const rotation = Math.round(random() * 150 - 75);
76
+ const color = index % 2 ? palette.secondary : palette.accent;
77
+ return `<span class="promo-object ${shape.className}" style="left:${left.toFixed(1)}%;top:${top.toFixed(1)}%;width:${size.toFixed(1)}vmin;transform:rotate(${rotation}deg);color:${color};opacity:${shape.opacity};"></span>`;
78
+ }).join('');
79
+
80
+ return `<div class="promo-decor" aria-hidden="true">${objects}</div>`;
81
+ }
82
+
83
+ function getPromotionalDecorationStyles() {
84
+ return `
85
+ .promo-decor { position: absolute; z-index: 0; inset: 0; overflow: hidden; pointer-events: none; }
86
+ .promo-object { position: absolute; display: block; aspect-ratio: 1; }
87
+ .promo-object::before,
88
+ .promo-object::after { content: ''; position: absolute; }
89
+ .promo-object.ring { border: clamp(2px, 0.28vw, 6px) solid currentColor; border-radius: 50%; }
90
+ .promo-object.double-ring { border: clamp(2px, 0.24vw, 5px) solid currentColor; border-radius: 50%; }
91
+ .promo-object.double-ring::after { inset: 20%; border: clamp(1px, 0.15vw, 3px) solid currentColor; border-radius: 50%; }
92
+ .promo-object.pill { aspect-ratio: 2.3; border-radius: 999px; background: currentColor; }
93
+ .promo-object.dots { border-radius: 50%; background: currentColor; box-shadow: 1.2vmin 0 0 -0.22vmin currentColor, 0 1.2vmin 0 -0.22vmin currentColor, 1.2vmin 1.2vmin 0 -0.22vmin currentColor; }
94
+ .promo-object.sparkle { background: currentColor; clip-path: polygon(50% 0, 61% 39%, 100% 50%, 61% 61%, 50% 100%, 39% 61%, 0 50%, 39% 39%); }
95
+ .promo-object.diamond { border: clamp(2px, 0.25vw, 5px) solid currentColor; border-radius: 18%; transform-origin: center; }
96
+ .promo-object.triangle { background: currentColor; clip-path: polygon(50% 4%, 96% 91%, 4% 91%); }
97
+ .promo-object.hexagon { background: currentColor; clip-path: polygon(25% 7%, 75% 7%, 100% 50%, 75% 93%, 25% 93%, 0 50%); }
98
+ .promo-object.squircle { border: clamp(2px, 0.25vw, 5px) solid currentColor; border-radius: 32%; }
99
+ .promo-object.plus { background: currentColor; clip-path: polygon(36% 0, 64% 0, 64% 36%, 100% 36%, 100% 64%, 64% 64%, 64% 100%, 36% 100%, 36% 64%, 0 64%, 0 36%, 36% 36%); }
100
+ .promo-object.chevron { background: currentColor; clip-path: polygon(0 18%, 50% 66%, 100% 18%, 100% 48%, 50% 96%, 0 48%); }
101
+ .promo-object.orbit { aspect-ratio: 1.8; border: clamp(2px, 0.23vw, 5px) solid currentColor; border-radius: 50%; }
102
+ .promo-object.orbit::after { top: 50%; left: 88%; width: 20%; aspect-ratio: 1; transform: translate(-50%, -50%); border-radius: 50%; background: currentColor; }
103
+ .promo-object.bars { background: repeating-linear-gradient(90deg, currentColor 0 12%, transparent 12% 24%); border-radius: 18%; }
104
+ .promo-object.flower { background: currentColor; clip-path: polygon(50% 25%, 65% 5%, 76% 28%, 100% 24%, 88% 50%, 100% 76%, 76% 72%, 65% 95%, 50% 75%, 35% 95%, 24% 72%, 0 76%, 12% 50%, 0 24%, 24% 28%, 35% 5%); }
105
+ .promo-object.crescent { border-radius: 50%; box-shadow: inset 1.7vmin -0.45vmin 0 0 currentColor; }
106
+ .promo-object.wave { aspect-ratio: 2.2; border-top: clamp(3px, 0.35vw, 7px) solid currentColor; border-radius: 50%; }
107
+ .promo-object.wave::after { top: 28%; left: 38%; width: 62%; height: 48%; border-bottom: clamp(3px, 0.35vw, 7px) solid currentColor; border-radius: 50%; }
108
+ .promo-object.cube { background: currentColor; clip-path: polygon(50% 0, 94% 24%, 94% 74%, 50% 100%, 6% 74%, 6% 24%); }
109
+ .promo-object.cube::after { inset: 17% 20%; background: rgba(255,255,255,0.42); clip-path: polygon(50% 0, 100% 24%, 100% 70%, 50% 100%, 0 70%, 0 24%); }
110
+ .promo-object.burst { background: currentColor; clip-path: polygon(50% 0, 59% 34%, 85% 15%, 66% 41%, 100% 50%, 66% 59%, 85% 85%, 59% 66%, 50% 100%, 41% 66%, 15% 85%, 34% 59%, 0 50%, 34% 41%, 15% 15%, 41% 34%); }
111
+ `;
112
+ }
113
+
114
+ module.exports = {
115
+ decorationShapes,
116
+ decorationSets,
117
+ getDecorationSeed,
118
+ createPromotionalDecorations,
119
+ getPromotionalDecorationStyles,
120
+ };