ultimate-jekyll-manager 0.0.145 → 0.0.147
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/.playwright-mcp/page-2025-12-09T08-57-21-148Z.png +0 -0
- package/.playwright-mcp/page-2025-12-09T09-11-17-887Z.png +0 -0
- package/.playwright-mcp/page-2025-12-09T09-11-41-348Z.png +0 -0
- package/dist/assets/css/pages/extension/installed/index.scss +240 -0
- package/dist/assets/css/pages/status/index.scss +174 -0
- package/dist/assets/css/pages/updates/index.scss +13 -0
- package/dist/assets/css/pages/updates/update.scss +46 -0
- package/dist/assets/js/pages/extension/index.js +160 -0
- package/dist/assets/js/pages/status/index.js +508 -0
- package/dist/defaults/dist/_includes/core/head.html +9 -0
- package/dist/defaults/dist/_layouts/blueprint/blog/categories/category.html +8 -0
- package/dist/defaults/dist/_layouts/blueprint/blog/categories/index.html +12 -0
- package/dist/defaults/dist/_layouts/blueprint/blog/tags/index.html +12 -0
- package/dist/defaults/dist/_layouts/blueprint/blog/tags/tag.html +8 -0
- package/dist/defaults/dist/_layouts/blueprint/extension/index.html +12 -0
- package/dist/defaults/dist/_layouts/blueprint/extension/installed.html +13 -0
- package/dist/defaults/dist/_layouts/blueprint/status.html +12 -0
- package/dist/defaults/dist/_layouts/blueprint/updates/index.html +12 -0
- package/dist/defaults/dist/_layouts/blueprint/updates/update.html +13 -0
- package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/blog/categories/category.html +152 -0
- package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/blog/categories/index.html +94 -0
- package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/blog/index.html +78 -3
- package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/blog/post.html +3 -3
- package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/blog/tags/index.html +90 -0
- package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/blog/tags/tag.html +153 -0
- package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/extension/index.html +434 -0
- package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/extension/installed.html +188 -0
- package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/payment/confirmation.html +1 -1
- package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/status.html +296 -0
- package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/updates/index.html +128 -0
- package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/updates/update.html +122 -0
- package/dist/defaults/dist/_updates/v0.0.0.md +32 -0
- package/dist/defaults/dist/_updates/v0.0.1.md +64 -0
- package/dist/defaults/dist/pages/blog/categories.md +7 -0
- package/dist/defaults/dist/pages/blog/tags.md +7 -0
- package/dist/defaults/dist/pages/{extension.md → extension/index.html} +2 -2
- package/dist/defaults/dist/pages/extension/installed.html +7 -0
- package/dist/defaults/dist/pages/status.md +7 -0
- package/dist/defaults/dist/pages/updates/index.md +7 -0
- package/dist/defaults/dist/sitemap.html +8 -0
- package/dist/defaults/dist/sitemap.xml +4 -0
- package/dist/defaults/src/_config.yml +8 -0
- package/dist/gulp/main.js +4 -0
- package/dist/gulp/tasks/serve.js +16 -36
- package/firebase-debug.log +182 -0
- package/package.json +2 -1
- package/dist/defaults/dist/_layouts/blueprint/extension.md +0 -47
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
// Extension Installed Page Styles
|
|
2
|
+
// Only custom styles that cannot be achieved with Bootstrap
|
|
3
|
+
|
|
4
|
+
// Success icon animation
|
|
5
|
+
.extension-success-icon {
|
|
6
|
+
animation: success-appear 0.6s ease-out;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
body {
|
|
10
|
+
color: red !important;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
@keyframes success-appear {
|
|
14
|
+
0% {
|
|
15
|
+
transform: scale(0);
|
|
16
|
+
opacity: 0;
|
|
17
|
+
}
|
|
18
|
+
50% {
|
|
19
|
+
transform: scale(1.2);
|
|
20
|
+
}
|
|
21
|
+
100% {
|
|
22
|
+
transform: scale(1);
|
|
23
|
+
opacity: 1;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Arrow container positioning and animation
|
|
28
|
+
.extension-arrow-container {
|
|
29
|
+
top: 80px;
|
|
30
|
+
right: 40px;
|
|
31
|
+
animation: arrow-container-appear 0.5s ease-out 0.3s both;
|
|
32
|
+
|
|
33
|
+
@media (max-width: 991px) {
|
|
34
|
+
right: 20px;
|
|
35
|
+
top: 70px;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
@media (max-width: 576px) {
|
|
39
|
+
right: 10px;
|
|
40
|
+
top: 60px;
|
|
41
|
+
|
|
42
|
+
.extension-arrow svg {
|
|
43
|
+
width: 50px;
|
|
44
|
+
height: 70px;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
@keyframes arrow-container-appear {
|
|
50
|
+
0% {
|
|
51
|
+
opacity: 0;
|
|
52
|
+
transform: translateY(20px);
|
|
53
|
+
}
|
|
54
|
+
100% {
|
|
55
|
+
opacity: 1;
|
|
56
|
+
transform: translateY(0);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Arrow bounce animation
|
|
61
|
+
.extension-arrow {
|
|
62
|
+
animation: arrow-bounce 1.5s ease-in-out infinite;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
@keyframes arrow-bounce {
|
|
66
|
+
0%, 100% {
|
|
67
|
+
transform: translateY(0);
|
|
68
|
+
}
|
|
69
|
+
50% {
|
|
70
|
+
transform: translateY(-8px);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Label glow animation
|
|
75
|
+
.extension-arrow-label {
|
|
76
|
+
animation: label-glow 2s ease-in-out infinite;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
@keyframes label-glow {
|
|
80
|
+
0%, 100% {
|
|
81
|
+
box-shadow: 0 4px 15px rgba(var(--bs-primary-rgb), 0.4);
|
|
82
|
+
}
|
|
83
|
+
50% {
|
|
84
|
+
box-shadow: 0 4px 25px rgba(var(--bs-primary-rgb), 0.7);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// URL bar min width
|
|
89
|
+
.extension-url-bar {
|
|
90
|
+
min-width: 100px;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Extension dropdown width
|
|
94
|
+
.extension-dropdown {
|
|
95
|
+
width: 280px;
|
|
96
|
+
|
|
97
|
+
@media (max-width: 576px) {
|
|
98
|
+
width: 100%;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Icon/brandmark sizing
|
|
103
|
+
.extension-item-icon {
|
|
104
|
+
width: 28px;
|
|
105
|
+
height: 28px;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.extension-brandmark {
|
|
109
|
+
width: 20px;
|
|
110
|
+
height: 20px;
|
|
111
|
+
object-fit: contain;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// ============================================================================
|
|
115
|
+
// Click Indicator Animation (same pattern as download page)
|
|
116
|
+
// ============================================================================
|
|
117
|
+
|
|
118
|
+
.click-indicator {
|
|
119
|
+
pointer-events: none;
|
|
120
|
+
|
|
121
|
+
.pointer-icon {
|
|
122
|
+
width: 48px;
|
|
123
|
+
height: 48px;
|
|
124
|
+
|
|
125
|
+
svg {
|
|
126
|
+
width: 100%;
|
|
127
|
+
height: 100%;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.click-pulse {
|
|
132
|
+
width: 30px;
|
|
133
|
+
height: 30px;
|
|
134
|
+
top: -2px;
|
|
135
|
+
left: -2px;
|
|
136
|
+
opacity: 0;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Step 1: Puzzle piece - mouse moves to target, clicks, snaps back
|
|
141
|
+
.extension-puzzle-target {
|
|
142
|
+
.click-indicator {
|
|
143
|
+
.pointer-icon {
|
|
144
|
+
animation: puzzle-mouse-move 2.5s ease-out infinite;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
.click-pulse {
|
|
148
|
+
animation: puzzle-click-pulse 2.5s ease-out infinite;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
@keyframes puzzle-mouse-move {
|
|
154
|
+
// Start off-screen (bottom-right)
|
|
155
|
+
0% {
|
|
156
|
+
transform: translate(40px, 30px);
|
|
157
|
+
}
|
|
158
|
+
// Arrive at target (cursor tip on button)
|
|
159
|
+
30% {
|
|
160
|
+
transform: translate(-5px, -12px);
|
|
161
|
+
}
|
|
162
|
+
// Stay on target during clicks
|
|
163
|
+
70% {
|
|
164
|
+
transform: translate(-5px, -12px);
|
|
165
|
+
}
|
|
166
|
+
// Snap back instantly
|
|
167
|
+
70.1%, 100% {
|
|
168
|
+
transform: translate(40px, 30px);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
@keyframes puzzle-click-pulse {
|
|
173
|
+
0%, 35% {
|
|
174
|
+
opacity: 0;
|
|
175
|
+
transform: translate(-5px, -12px) scale(0.5);
|
|
176
|
+
}
|
|
177
|
+
40%, 50% {
|
|
178
|
+
opacity: 0.8;
|
|
179
|
+
transform: translate(-5px, -12px) scale(1.3);
|
|
180
|
+
}
|
|
181
|
+
55%, 65% {
|
|
182
|
+
opacity: 0.8;
|
|
183
|
+
transform: translate(-5px, -12px) scale(1.3);
|
|
184
|
+
}
|
|
185
|
+
70%, 100% {
|
|
186
|
+
opacity: 0;
|
|
187
|
+
transform: translate(-5px, -12px) scale(0.5);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Step 2: Pin button - mouse moves to target, clicks, snaps back (delayed)
|
|
192
|
+
.extension-pin-target {
|
|
193
|
+
.click-indicator {
|
|
194
|
+
.pointer-icon {
|
|
195
|
+
animation: pin-mouse-move 2.5s ease-out infinite 1.5s;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
.click-pulse {
|
|
199
|
+
animation: pin-click-pulse 2.5s ease-out infinite 1.5s;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
@keyframes pin-mouse-move {
|
|
205
|
+
// Start off-screen (bottom-right)
|
|
206
|
+
0% {
|
|
207
|
+
transform: translate(35px, 25px);
|
|
208
|
+
}
|
|
209
|
+
// Arrive at target (cursor tip on button)
|
|
210
|
+
30% {
|
|
211
|
+
transform: translate(-5px, -12px);
|
|
212
|
+
}
|
|
213
|
+
// Stay on target during clicks
|
|
214
|
+
70% {
|
|
215
|
+
transform: translate(-5px, -12px);
|
|
216
|
+
}
|
|
217
|
+
// Snap back instantly
|
|
218
|
+
70.1%, 100% {
|
|
219
|
+
transform: translate(35px, 25px);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
@keyframes pin-click-pulse {
|
|
224
|
+
0%, 35% {
|
|
225
|
+
opacity: 0;
|
|
226
|
+
transform: translate(-5px, -12px) scale(0.5);
|
|
227
|
+
}
|
|
228
|
+
40%, 50% {
|
|
229
|
+
opacity: 0.8;
|
|
230
|
+
transform: translate(-5px, -12px) scale(1.3);
|
|
231
|
+
}
|
|
232
|
+
55%, 65% {
|
|
233
|
+
opacity: 0.8;
|
|
234
|
+
transform: translate(-5px, -12px) scale(1.3);
|
|
235
|
+
}
|
|
236
|
+
70%, 100% {
|
|
237
|
+
opacity: 0;
|
|
238
|
+
transform: translate(-5px, -12px) scale(0.5);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Status Page Styles
|
|
3
|
+
*
|
|
4
|
+
* Uses Bootstrap semantic colors:
|
|
5
|
+
* - success for operational (green)
|
|
6
|
+
* - warning for degraded (amber)
|
|
7
|
+
* - danger for major outage (red)
|
|
8
|
+
* - info for maintenance (blue)
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
// Status Pulse - size for the blinking dot (uses global animation-pulse class)
|
|
12
|
+
.status-pulse {
|
|
13
|
+
width: 8px;
|
|
14
|
+
height: 8px;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Status Banner - gradient backgrounds based on status
|
|
18
|
+
.status-banner {
|
|
19
|
+
&.status-operational {
|
|
20
|
+
background: linear-gradient(135deg, rgba(var(--bs-success-rgb), 0.1) 0%, rgba(var(--bs-success-rgb), 0.05) 100%);
|
|
21
|
+
border-left: 4px solid var(--bs-success) !important;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
&.status-degraded {
|
|
25
|
+
background: linear-gradient(135deg, rgba(var(--bs-warning-rgb), 0.1) 0%, rgba(var(--bs-warning-rgb), 0.05) 100%);
|
|
26
|
+
border-left: 4px solid var(--bs-warning) !important;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
&.status-major {
|
|
30
|
+
background: linear-gradient(135deg, rgba(var(--bs-danger-rgb), 0.1) 0%, rgba(var(--bs-danger-rgb), 0.05) 100%);
|
|
31
|
+
border-left: 4px solid var(--bs-danger) !important;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
&.status-maintenance {
|
|
35
|
+
background: linear-gradient(135deg, rgba(var(--bs-info-rgb), 0.1) 0%, rgba(var(--bs-info-rgb), 0.05) 100%);
|
|
36
|
+
border-left: 4px solid var(--bs-info) !important;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Uptime Bars - custom component, no Bootstrap equivalent
|
|
41
|
+
// Responsive: Show 30 bars on mobile, 60 on tablet, 90 on desktop
|
|
42
|
+
.uptime-bars {
|
|
43
|
+
height: 32px;
|
|
44
|
+
flex-wrap: nowrap;
|
|
45
|
+
|
|
46
|
+
@media (max-width: 767.98px) {
|
|
47
|
+
height: 24px;
|
|
48
|
+
gap: 1px !important;
|
|
49
|
+
|
|
50
|
+
// Hide first 60 bars on mobile (show last 30)
|
|
51
|
+
.uptime-bar:nth-child(-n+60) {
|
|
52
|
+
display: none;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
@media (min-width: 768px) and (max-width: 991.98px) {
|
|
57
|
+
height: 28px;
|
|
58
|
+
|
|
59
|
+
// Hide first 30 bars on tablet (show last 60)
|
|
60
|
+
.uptime-bar:nth-child(-n+30) {
|
|
61
|
+
display: none;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.uptime-bar {
|
|
67
|
+
flex: 1 1 0;
|
|
68
|
+
min-width: 0;
|
|
69
|
+
|
|
70
|
+
&.status-operational { background-color: var(--bs-success); }
|
|
71
|
+
&.status-degraded { background-color: var(--bs-warning); }
|
|
72
|
+
&.status-major { background-color: var(--bs-danger); }
|
|
73
|
+
&.status-maintenance { background-color: var(--bs-info); }
|
|
74
|
+
&.status-unknown { background-color: var(--bs-secondary-bg); }
|
|
75
|
+
|
|
76
|
+
&:hover {
|
|
77
|
+
transform: scaleY(1.2);
|
|
78
|
+
opacity: 0.8;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Uptime Bar Legend
|
|
83
|
+
.uptime-bar-legend {
|
|
84
|
+
width: 16px;
|
|
85
|
+
height: 16px;
|
|
86
|
+
|
|
87
|
+
&.status-operational { background-color: var(--bs-success); }
|
|
88
|
+
&.status-degraded { background-color: var(--bs-warning); }
|
|
89
|
+
&.status-major { background-color: var(--bs-danger); }
|
|
90
|
+
&.status-maintenance { background-color: var(--bs-info); }
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Incident & Maintenance Cards - border colors
|
|
94
|
+
.incident-card,
|
|
95
|
+
.maintenance-card {
|
|
96
|
+
border-left: 4px solid var(--bs-border-color) !important;
|
|
97
|
+
|
|
98
|
+
&.status-operational,
|
|
99
|
+
&.status-resolved { border-left-color: var(--bs-success) !important; }
|
|
100
|
+
&.status-degraded { border-left-color: var(--bs-warning) !important; }
|
|
101
|
+
&.status-major { border-left-color: var(--bs-danger) !important; }
|
|
102
|
+
&.status-maintenance { border-left-color: var(--bs-info) !important; }
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Incident Timeline - custom component
|
|
106
|
+
.incident-timeline {
|
|
107
|
+
position: relative;
|
|
108
|
+
padding-left: 1.5rem;
|
|
109
|
+
|
|
110
|
+
&::before {
|
|
111
|
+
content: '';
|
|
112
|
+
position: absolute;
|
|
113
|
+
left: 0;
|
|
114
|
+
top: 0;
|
|
115
|
+
bottom: 0;
|
|
116
|
+
width: 2px;
|
|
117
|
+
background-color: var(--bs-border-color);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.timeline-item {
|
|
121
|
+
position: relative;
|
|
122
|
+
|
|
123
|
+
&::before {
|
|
124
|
+
content: '';
|
|
125
|
+
position: absolute;
|
|
126
|
+
left: -1.5rem;
|
|
127
|
+
top: 0.5rem;
|
|
128
|
+
width: 8px;
|
|
129
|
+
height: 8px;
|
|
130
|
+
border-radius: 50%;
|
|
131
|
+
background-color: var(--bs-secondary-bg);
|
|
132
|
+
border: 2px solid var(--bs-border-color);
|
|
133
|
+
transform: translateX(-3px);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
&.status-investigating::before {
|
|
137
|
+
background-color: var(--bs-warning);
|
|
138
|
+
border-color: var(--bs-warning);
|
|
139
|
+
}
|
|
140
|
+
&.status-identified::before {
|
|
141
|
+
background-color: var(--bs-danger);
|
|
142
|
+
border-color: var(--bs-danger);
|
|
143
|
+
}
|
|
144
|
+
&.status-monitoring::before {
|
|
145
|
+
background-color: var(--bs-info);
|
|
146
|
+
border-color: var(--bs-info);
|
|
147
|
+
}
|
|
148
|
+
&.status-resolved::before {
|
|
149
|
+
background-color: var(--bs-success);
|
|
150
|
+
border-color: var(--bs-success);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Tooltip for uptime bars - custom floating element
|
|
156
|
+
.uptime-tooltip {
|
|
157
|
+
position: fixed;
|
|
158
|
+
z-index: 1000;
|
|
159
|
+
background-color: var(--bs-body-bg);
|
|
160
|
+
border: 1px solid var(--bs-border-color);
|
|
161
|
+
box-shadow: var(--bs-box-shadow);
|
|
162
|
+
pointer-events: none;
|
|
163
|
+
white-space: nowrap;
|
|
164
|
+
|
|
165
|
+
.status-dot {
|
|
166
|
+
width: 8px;
|
|
167
|
+
height: 8px;
|
|
168
|
+
|
|
169
|
+
&.status-operational { background-color: var(--bs-success); }
|
|
170
|
+
&.status-degraded { background-color: var(--bs-warning); }
|
|
171
|
+
&.status-major { background-color: var(--bs-danger); }
|
|
172
|
+
&.status-maintenance { background-color: var(--bs-info); }
|
|
173
|
+
}
|
|
174
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Individual Update Page Styles
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
// Update content - markdown styling
|
|
6
|
+
.update-content {
|
|
7
|
+
h2 {
|
|
8
|
+
margin-top: 2rem;
|
|
9
|
+
margin-bottom: 1rem;
|
|
10
|
+
padding-bottom: 0.5rem;
|
|
11
|
+
border-bottom: 1px solid var(--bs-border-color);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
h3 {
|
|
15
|
+
margin-top: 1.5rem;
|
|
16
|
+
margin-bottom: 0.75rem;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
ul, ol {
|
|
20
|
+
margin-bottom: 1rem;
|
|
21
|
+
padding-left: 1.5rem;
|
|
22
|
+
|
|
23
|
+
li {
|
|
24
|
+
margin-bottom: 0.5rem;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
code {
|
|
29
|
+
padding: 0.2em 0.4em;
|
|
30
|
+
background-color: var(--bs-body-tertiary-bg);
|
|
31
|
+
border-radius: var(--bs-border-radius-sm);
|
|
32
|
+
font-size: 0.875em;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
pre {
|
|
36
|
+
padding: 1rem;
|
|
37
|
+
background-color: var(--bs-body-tertiary-bg);
|
|
38
|
+
border-radius: var(--bs-border-radius);
|
|
39
|
+
overflow-x: auto;
|
|
40
|
+
|
|
41
|
+
code {
|
|
42
|
+
padding: 0;
|
|
43
|
+
background-color: transparent;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extension Page JavaScript
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
// Libraries
|
|
6
|
+
let webManager = null;
|
|
7
|
+
|
|
8
|
+
// Module
|
|
9
|
+
export default (Manager) => {
|
|
10
|
+
return new Promise(async function (resolve) {
|
|
11
|
+
// Shortcuts
|
|
12
|
+
webManager = Manager.webManager;
|
|
13
|
+
|
|
14
|
+
// Initialize when DOM is ready
|
|
15
|
+
await webManager.dom().ready();
|
|
16
|
+
|
|
17
|
+
setupBrowserDetection();
|
|
18
|
+
setupInstallTracking();
|
|
19
|
+
|
|
20
|
+
// Expose test function globally
|
|
21
|
+
window.triggerExtensionInstall = triggerInstall;
|
|
22
|
+
|
|
23
|
+
/* @dev-only:start */
|
|
24
|
+
// {
|
|
25
|
+
// window.triggerExtensionInstall('chrome');
|
|
26
|
+
// }
|
|
27
|
+
/* @dev-only:end */
|
|
28
|
+
|
|
29
|
+
// Resolve after initialization
|
|
30
|
+
return resolve();
|
|
31
|
+
});
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
// Configuration
|
|
35
|
+
const config = {
|
|
36
|
+
selectors: {
|
|
37
|
+
browserButtons: '.browser-btn',
|
|
38
|
+
browserPanes: '[data-browser]',
|
|
39
|
+
installButtons: '.tab-pane[data-browser] .btn-primary',
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
// Browser detection mapping
|
|
44
|
+
const browserMap = {
|
|
45
|
+
chrome: /chrome|chromium|crios/i,
|
|
46
|
+
firefox: /firefox|fxios/i,
|
|
47
|
+
safari: /safari/i,
|
|
48
|
+
edge: /edg/i,
|
|
49
|
+
opera: /opera|opr/i,
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
// Setup browser detection and auto-select
|
|
53
|
+
function setupBrowserDetection() {
|
|
54
|
+
const detectedBrowser = detectBrowser();
|
|
55
|
+
console.log('Detected browser:', detectedBrowser);
|
|
56
|
+
|
|
57
|
+
// Show loading state initially, then switch to detected browser
|
|
58
|
+
setTimeout(() => {
|
|
59
|
+
// Activate the detected browser tab using Bootstrap's tab API
|
|
60
|
+
const $detectedTab = document.querySelector(`#${detectedBrowser}-tab`);
|
|
61
|
+
if ($detectedTab) {
|
|
62
|
+
const tab = new bootstrap.Tab($detectedTab);
|
|
63
|
+
tab.show();
|
|
64
|
+
}
|
|
65
|
+
}, 800);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Detect user's browser from user agent
|
|
69
|
+
function detectBrowser() {
|
|
70
|
+
const userAgent = navigator.userAgent;
|
|
71
|
+
|
|
72
|
+
// Edge must be checked before Chrome (Edge includes "Chrome" in UA)
|
|
73
|
+
if (browserMap.edge.test(userAgent)) {
|
|
74
|
+
return 'edge';
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Opera must be checked before Chrome (Opera includes "Chrome" in UA)
|
|
78
|
+
if (browserMap.opera.test(userAgent)) {
|
|
79
|
+
return 'opera';
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Chrome
|
|
83
|
+
if (browserMap.chrome.test(userAgent)) {
|
|
84
|
+
return 'chrome';
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Firefox
|
|
88
|
+
if (browserMap.firefox.test(userAgent)) {
|
|
89
|
+
return 'firefox';
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Safari must be checked last (Chrome and other browsers include "Safari" in UA)
|
|
93
|
+
if (browserMap.safari.test(userAgent) && !browserMap.chrome.test(userAgent)) {
|
|
94
|
+
return 'safari';
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Default to Chrome
|
|
98
|
+
return 'chrome';
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Setup install button tracking
|
|
102
|
+
function setupInstallTracking() {
|
|
103
|
+
const $installButtons = document.querySelectorAll(config.selectors.installButtons);
|
|
104
|
+
|
|
105
|
+
$installButtons.forEach($button => {
|
|
106
|
+
$button.addEventListener('click', function() {
|
|
107
|
+
const $browserPane = this.closest('[data-browser]');
|
|
108
|
+
const browserId = $browserPane ? $browserPane.dataset.browser : 'unknown';
|
|
109
|
+
const installUrl = this.getAttribute('href');
|
|
110
|
+
|
|
111
|
+
trackInstallClick(browserId, installUrl);
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Tracking function
|
|
117
|
+
function trackInstallClick(browser, installUrl) {
|
|
118
|
+
console.log('Extension install clicked:', browser, installUrl);
|
|
119
|
+
|
|
120
|
+
gtag('event', 'extension_install', {
|
|
121
|
+
browser: browser,
|
|
122
|
+
install_url: installUrl,
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
fbq('trackCustom', 'ExtensionInstall', {
|
|
126
|
+
content_name: `${browser} extension`,
|
|
127
|
+
content_category: browser,
|
|
128
|
+
content_type: 'extension',
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
ttq.track('Download', {
|
|
132
|
+
content_id: `extension-${browser}`,
|
|
133
|
+
content_type: 'product',
|
|
134
|
+
content_name: `${browser} extension`,
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Trigger install for testing (simulates clicking the install button)
|
|
139
|
+
function triggerInstall(browser) {
|
|
140
|
+
const browserId = browser || detectBrowser();
|
|
141
|
+
const $button = document.querySelector(`.tab-pane[data-browser="${browserId}"] .btn-primary`);
|
|
142
|
+
|
|
143
|
+
if (!$button) {
|
|
144
|
+
console.error(`No install button found for browser: ${browserId}`);
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const installUrl = $button.getAttribute('href');
|
|
149
|
+
|
|
150
|
+
if (!installUrl) {
|
|
151
|
+
console.error(`No install URL configured for browser: ${browserId}`);
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Track the click
|
|
156
|
+
trackInstallClick(browserId, installUrl);
|
|
157
|
+
|
|
158
|
+
// Open the extension store in a new tab
|
|
159
|
+
window.open(installUrl, '_blank');
|
|
160
|
+
}
|