playwright-director 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +860 -0
- package/dist/Tutorial.d.ts +108 -0
- package/dist/Tutorial.d.ts.map +1 -0
- package/dist/Tutorial.js +767 -0
- package/dist/Tutorial.js.map +1 -0
- package/dist/bin/export-transcript.d.ts +3 -0
- package/dist/bin/export-transcript.d.ts.map +1 -0
- package/dist/bin/export-transcript.js +111 -0
- package/dist/bin/export-transcript.js.map +1 -0
- package/dist/cursor.d.ts +24 -0
- package/dist/cursor.d.ts.map +1 -0
- package/dist/cursor.js +96 -0
- package/dist/cursor.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/init.d.ts +3 -0
- package/dist/init.d.ts.map +1 -0
- package/dist/init.js +183 -0
- package/dist/init.js.map +1 -0
- package/dist/merge.d.ts +26 -0
- package/dist/merge.d.ts.map +1 -0
- package/dist/merge.js +65 -0
- package/dist/merge.js.map +1 -0
- package/dist/music.d.ts +29 -0
- package/dist/music.d.ts.map +1 -0
- package/dist/music.js +107 -0
- package/dist/music.js.map +1 -0
- package/dist/overlay-html.d.ts +44 -0
- package/dist/overlay-html.d.ts.map +1 -0
- package/dist/overlay-html.js +150 -0
- package/dist/overlay-html.js.map +1 -0
- package/dist/overlay.d.ts +39 -0
- package/dist/overlay.d.ts.map +1 -0
- package/dist/overlay.js +134 -0
- package/dist/overlay.js.map +1 -0
- package/dist/postinstall.d.ts +3 -0
- package/dist/postinstall.d.ts.map +1 -0
- package/dist/postinstall.js +38 -0
- package/dist/postinstall.js.map +1 -0
- package/dist/reporter.d.ts +22 -0
- package/dist/reporter.d.ts.map +1 -0
- package/dist/reporter.js +166 -0
- package/dist/reporter.js.map +1 -0
- package/dist/site/build-site.d.ts +2 -0
- package/dist/site/build-site.d.ts.map +1 -0
- package/dist/site/build-site.js +82 -0
- package/dist/site/build-site.js.map +1 -0
- package/dist/site/embed.d.ts +48 -0
- package/dist/site/embed.d.ts.map +1 -0
- package/dist/site/embed.js +45 -0
- package/dist/site/embed.js.map +1 -0
- package/dist/site/generate-manifest.d.ts +15 -0
- package/dist/site/generate-manifest.d.ts.map +1 -0
- package/dist/site/generate-manifest.js +106 -0
- package/dist/site/generate-manifest.js.map +1 -0
- package/dist/site/scaffold.d.ts +3 -0
- package/dist/site/scaffold.d.ts.map +1 -0
- package/dist/site/scaffold.js +70 -0
- package/dist/site/scaffold.js.map +1 -0
- package/dist/site/scan-tutorials.d.ts +12 -0
- package/dist/site/scan-tutorials.d.ts.map +1 -0
- package/dist/site/scan-tutorials.js +54 -0
- package/dist/site/scan-tutorials.js.map +1 -0
- package/dist/site/types.d.ts +75 -0
- package/dist/site/types.d.ts.map +1 -0
- package/dist/site/types.js +2 -0
- package/dist/site/types.js.map +1 -0
- package/dist/skill-stamp.d.ts +21 -0
- package/dist/skill-stamp.d.ts.map +1 -0
- package/dist/skill-stamp.js +39 -0
- package/dist/skill-stamp.js.map +1 -0
- package/dist/slugify.d.ts +19 -0
- package/dist/slugify.d.ts.map +1 -0
- package/dist/slugify.js +29 -0
- package/dist/slugify.js.map +1 -0
- package/dist/stage-presets.d.ts +51 -0
- package/dist/stage-presets.d.ts.map +1 -0
- package/dist/stage-presets.js +45 -0
- package/dist/stage-presets.js.map +1 -0
- package/dist/styles.css +637 -0
- package/dist/timeline.d.ts +87 -0
- package/dist/timeline.d.ts.map +1 -0
- package/dist/timeline.js +108 -0
- package/dist/timeline.js.map +1 -0
- package/dist/transcript.d.ts +87 -0
- package/dist/transcript.d.ts.map +1 -0
- package/dist/transcript.js +291 -0
- package/dist/transcript.js.map +1 -0
- package/dist/tts-provider.d.ts +69 -0
- package/dist/tts-provider.d.ts.map +1 -0
- package/dist/tts-provider.js +182 -0
- package/dist/tts-provider.js.map +1 -0
- package/dist/types.d.ts +109 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/voice-prerender.d.ts +41 -0
- package/dist/voice-prerender.d.ts.map +1 -0
- package/dist/voice-prerender.js +122 -0
- package/dist/voice-prerender.js.map +1 -0
- package/dist/voice.d.ts +76 -0
- package/dist/voice.d.ts.map +1 -0
- package/dist/voice.js +244 -0
- package/dist/voice.js.map +1 -0
- package/package.json +87 -0
- package/skills/tutorialize/SKILL.md +54 -0
- package/skills/tutorialize/agent.md +58 -0
- package/skills/tutorialize/references/api.md +489 -0
- package/skills/tutorialize/references/storytelling.md +193 -0
- package/src/styles.css +637 -0
- package/templates/site/public/.gitkeep +0 -0
- package/templates/site/public/widget.js +273 -0
- package/templates/site/src/components/TutorialsHome.astro +141 -0
- package/templates/site/src/components/VideoCard.astro +192 -0
- package/templates/site/src/components/VideoModal.astro +170 -0
- package/templates/site/src/data/.gitkeep +0 -0
- package/templates/site/src/layouts/Base.astro +56 -0
- package/templates/site/src/pages/[slug].astro +745 -0
- package/templates/site/src/pages/index.astro +5 -0
- package/templates/site/src/styles/tutorials.css +303 -0
package/dist/styles.css
ADDED
|
@@ -0,0 +1,637 @@
|
|
|
1
|
+
/* Tutorial overlay styles - injected in tutorial mode */
|
|
2
|
+
/* Uses scoped CSS variables for easy customization */
|
|
3
|
+
|
|
4
|
+
:root {
|
|
5
|
+
/* Colors */
|
|
6
|
+
--tutorial-primary: #0066fd;
|
|
7
|
+
--tutorial-primary-light: rgba(59, 130, 246, 0.5);
|
|
8
|
+
--tutorial-primary-lighter: rgba(59, 130, 246, 0.3);
|
|
9
|
+
--tutorial-secondary: #8b5cf6;
|
|
10
|
+
--tutorial-bg-start: #1e293b;
|
|
11
|
+
--tutorial-bg-end: #0f172a;
|
|
12
|
+
--tutorial-border: rgb(231, 234, 239.5);
|
|
13
|
+
--tutorial-text: #000000;
|
|
14
|
+
--tutorial-text-muted: #1b1b1b;
|
|
15
|
+
--tutorial-text-bright: #000000;
|
|
16
|
+
--tutorial-shadow: rgba(0, 0, 0, 0.5);
|
|
17
|
+
--tutorial-cursor-stroke: #1e293b;
|
|
18
|
+
|
|
19
|
+
/* Sizing */
|
|
20
|
+
--tutorial-overlay-width: 320px;
|
|
21
|
+
--tutorial-overlay-top: 20px;
|
|
22
|
+
--tutorial-overlay-offset: 20px;
|
|
23
|
+
--tutorial-border-radius: 12px;
|
|
24
|
+
--tutorial-padding: 20px;
|
|
25
|
+
--tutorial-z-index: 999999;
|
|
26
|
+
|
|
27
|
+
/* Overlay surface — override --tutorial-overlay-bg with an rgba() for transparency */
|
|
28
|
+
--tutorial-overlay-bg: #fff;
|
|
29
|
+
--tutorial-overlay-blur: 12px;
|
|
30
|
+
--tutorial-overlay-shadow: 0 25px 50px -12px var(--tutorial-shadow), 0 0 0 1px rgba(255, 255, 255, 0.05) inset;
|
|
31
|
+
--tutorial-icon-size: 24px;
|
|
32
|
+
--tutorial-context-max-width: 500px;
|
|
33
|
+
--tutorial-scene-inactive-opacity: 0.55;
|
|
34
|
+
|
|
35
|
+
/* Animation */
|
|
36
|
+
--tutorial-animation-duration: 0.3s;
|
|
37
|
+
--tutorial-pulse-duration: 1.5s;
|
|
38
|
+
|
|
39
|
+
/* Multi-scene stage (tab bar + scene panes) */
|
|
40
|
+
--tutorial-stage-bg: #1e293b;
|
|
41
|
+
--tutorial-stage-gap: 4px;
|
|
42
|
+
--tutorial-scene-bg: #ffffff;
|
|
43
|
+
--tutorial-tabbar-padding: 8px 8px 0;
|
|
44
|
+
--tutorial-tab-bg: #334155;
|
|
45
|
+
--tutorial-tab-text: #94a3b8;
|
|
46
|
+
--tutorial-tab-bg-active: #f8fafc;
|
|
47
|
+
--tutorial-tab-text-active: #0f172a;
|
|
48
|
+
--tutorial-tab-dot: #64748b;
|
|
49
|
+
--tutorial-tab-dot-active: #22c55e;
|
|
50
|
+
--tutorial-tab-padding: 10px 20px;
|
|
51
|
+
--tutorial-tab-radius: 10px 10px 0 0;
|
|
52
|
+
--tutorial-tab-size: 15px;
|
|
53
|
+
--tutorial-tab-transition: 250ms;
|
|
54
|
+
|
|
55
|
+
/* Typography */
|
|
56
|
+
--tutorial-font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
57
|
+
--tutorial-title-size: 14px;
|
|
58
|
+
--tutorial-step-title-size: 18px;
|
|
59
|
+
--tutorial-description-size: 14px;
|
|
60
|
+
--tutorial-badge-size: 12px;
|
|
61
|
+
|
|
62
|
+
/* Mobile variant values — the html[data-tutorial-variant='mobile'] block below
|
|
63
|
+
only remaps the base variables to these, so tuning the mobile look from a
|
|
64
|
+
consuming project is a plain `:root { ... }` override. */
|
|
65
|
+
--tutorial-overlay-width-mobile: min(250px, calc(100vw - 2 * var(--tutorial-overlay-offset)));
|
|
66
|
+
--tutorial-overlay-top-mobile: 12px;
|
|
67
|
+
--tutorial-overlay-offset-mobile: 12px;
|
|
68
|
+
--tutorial-padding-mobile: 12px;
|
|
69
|
+
--tutorial-title-size-mobile: 12px;
|
|
70
|
+
--tutorial-step-title-size-mobile: 14px;
|
|
71
|
+
--tutorial-description-size-mobile: 12px;
|
|
72
|
+
--tutorial-badge-size-mobile: 10px;
|
|
73
|
+
--tutorial-icon-size-mobile: 16px;
|
|
74
|
+
--tutorial-overlay-blur-mobile: 6px;
|
|
75
|
+
--tutorial-overlay-shadow-mobile: 0 10px 25px -8px var(--tutorial-shadow);
|
|
76
|
+
--tutorial-context-max-width-mobile: min(420px, 85vw);
|
|
77
|
+
/* Lightened content: icon and step badge hidden — bring them back with
|
|
78
|
+
`:root { --tutorial-icon-display-mobile: inline-flex; }` */
|
|
79
|
+
--tutorial-icon-display-mobile: none;
|
|
80
|
+
--tutorial-badge-display-mobile: none;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/* ── Mobile variant (data-tutorial-variant set by the Tutorial class) ──────
|
|
84
|
+
Compact overlay for phone-sized panes: pure variable remaps + two display
|
|
85
|
+
toggles, nothing structural. */
|
|
86
|
+
html[data-tutorial-variant='mobile'] {
|
|
87
|
+
--tutorial-overlay-width: var(--tutorial-overlay-width-mobile);
|
|
88
|
+
--tutorial-overlay-top: var(--tutorial-overlay-top-mobile);
|
|
89
|
+
--tutorial-overlay-offset: var(--tutorial-overlay-offset-mobile);
|
|
90
|
+
--tutorial-padding: var(--tutorial-padding-mobile);
|
|
91
|
+
--tutorial-title-size: var(--tutorial-title-size-mobile);
|
|
92
|
+
--tutorial-step-title-size: var(--tutorial-step-title-size-mobile);
|
|
93
|
+
--tutorial-description-size: var(--tutorial-description-size-mobile);
|
|
94
|
+
--tutorial-badge-size: var(--tutorial-badge-size-mobile);
|
|
95
|
+
--tutorial-icon-size: var(--tutorial-icon-size-mobile);
|
|
96
|
+
--tutorial-overlay-blur: var(--tutorial-overlay-blur-mobile);
|
|
97
|
+
--tutorial-overlay-shadow: var(--tutorial-overlay-shadow-mobile);
|
|
98
|
+
--tutorial-context-max-width: var(--tutorial-context-max-width-mobile);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
html[data-tutorial-variant='mobile'] .tutorial-icon {
|
|
102
|
+
display: var(--tutorial-icon-display-mobile);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
html[data-tutorial-variant='mobile'] .tutorial-step-badge {
|
|
106
|
+
display: var(--tutorial-badge-display-mobile);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
html[data-tutorial-variant='mobile'] .tutorial-header {
|
|
110
|
+
margin-bottom: 8px;
|
|
111
|
+
padding-bottom: 6px;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
html[data-tutorial-variant='mobile'] .tutorial-progress {
|
|
115
|
+
margin-top: 8px;
|
|
116
|
+
height: 3px;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
@keyframes tutorial-pulse {
|
|
120
|
+
0%, 100% {
|
|
121
|
+
box-shadow: 0 0 0 4px var(--tutorial-primary-light);
|
|
122
|
+
}
|
|
123
|
+
50% {
|
|
124
|
+
box-shadow: 0 0 0 8px var(--tutorial-primary-lighter), 0 0 20px var(--tutorial-primary-light);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
@keyframes tutorial-fade-in {
|
|
129
|
+
from {
|
|
130
|
+
opacity: 0;
|
|
131
|
+
transform: translateY(-10px);
|
|
132
|
+
}
|
|
133
|
+
to {
|
|
134
|
+
opacity: 1;
|
|
135
|
+
transform: translateY(0);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
@keyframes tutorial-fade-in-center {
|
|
140
|
+
from {
|
|
141
|
+
opacity: 0;
|
|
142
|
+
transform: translate(-50%, calc(-50% - 10px));
|
|
143
|
+
}
|
|
144
|
+
to {
|
|
145
|
+
opacity: 1;
|
|
146
|
+
transform: translate(-50%, -50%);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
@keyframes tutorial-fade-in-bottom {
|
|
151
|
+
from {
|
|
152
|
+
opacity: 0;
|
|
153
|
+
transform: translateY(10px);
|
|
154
|
+
}
|
|
155
|
+
to {
|
|
156
|
+
opacity: 1;
|
|
157
|
+
transform: translateY(0);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
@keyframes cursor-click {
|
|
162
|
+
0%, 100% {
|
|
163
|
+
transform: translate(-50%, -50%) scale(1);
|
|
164
|
+
}
|
|
165
|
+
50% {
|
|
166
|
+
transform: translate(-50%, -50%) scale(0.8);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
.tutorial-overlay {
|
|
171
|
+
position: fixed;
|
|
172
|
+
top: var(--tutorial-overlay-top);
|
|
173
|
+
left: var(--tutorial-overlay-offset);
|
|
174
|
+
width: var(--tutorial-overlay-width);
|
|
175
|
+
background: var(--tutorial-overlay-bg);
|
|
176
|
+
backdrop-filter: blur(var(--tutorial-overlay-blur));
|
|
177
|
+
-webkit-backdrop-filter: blur(var(--tutorial-overlay-blur));
|
|
178
|
+
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
179
|
+
border-radius: var(--tutorial-border-radius);
|
|
180
|
+
padding: var(--tutorial-padding);
|
|
181
|
+
z-index: var(--tutorial-z-index);
|
|
182
|
+
font-family: var(--tutorial-font-family);
|
|
183
|
+
color: var(--tutorial-text);
|
|
184
|
+
box-shadow: var(--tutorial-overlay-shadow);
|
|
185
|
+
animation: tutorial-fade-in var(--tutorial-animation-duration) ease-out;
|
|
186
|
+
/* Never swallow clicks on app elements behind the overlay — the tutorial
|
|
187
|
+
* is read-only and narrating, not interactive. Without this, fixed-position
|
|
188
|
+
* UI that overlaps the overlay (e.g. the top-right scan-fab) becomes
|
|
189
|
+
* un-clickable and tutorial.click() times out. */
|
|
190
|
+
pointer-events: none;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
.tutorial-overlay-rtl {
|
|
194
|
+
left: auto;
|
|
195
|
+
right: var(--tutorial-overlay-offset);
|
|
196
|
+
direction: rtl;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
.tutorial-overlay-tr {
|
|
200
|
+
left: auto;
|
|
201
|
+
right: var(--tutorial-overlay-offset);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
.tutorial-overlay-bl {
|
|
205
|
+
top: auto;
|
|
206
|
+
bottom: var(--tutorial-overlay-top);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
.tutorial-overlay-br {
|
|
210
|
+
left: auto;
|
|
211
|
+
right: var(--tutorial-overlay-offset);
|
|
212
|
+
top: auto;
|
|
213
|
+
bottom: var(--tutorial-overlay-top);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
.tutorial-overlay-bl,
|
|
217
|
+
.tutorial-overlay-br {
|
|
218
|
+
animation: tutorial-fade-in-bottom var(--tutorial-animation-duration) ease-out;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
.tutorial-overlay-context {
|
|
222
|
+
/* Context overlay: centered, larger, more prominent */
|
|
223
|
+
top: 50%;
|
|
224
|
+
left: 50%;
|
|
225
|
+
right: auto;
|
|
226
|
+
transform: translate(-50%, -50%);
|
|
227
|
+
width: auto;
|
|
228
|
+
min-width: var(--tutorial-overlay-width);
|
|
229
|
+
max-width: var(--tutorial-context-max-width);
|
|
230
|
+
text-align: center;
|
|
231
|
+
padding: calc(var(--tutorial-padding) * 1.5);
|
|
232
|
+
background: var(--tutorial-overlay-bg);
|
|
233
|
+
border: 1px solid var(--tutorial-border);
|
|
234
|
+
box-shadow: 0 25px 80px -12px var(--tutorial-shadow), 0 0 40px var(--tutorial-primary-lighter);
|
|
235
|
+
animation: tutorial-fade-in-center var(--tutorial-animation-duration) ease-out;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
.tutorial-overlay-context .tutorial-header {
|
|
239
|
+
justify-content: center;
|
|
240
|
+
border-bottom: none;
|
|
241
|
+
margin-bottom: 12px;
|
|
242
|
+
padding-bottom: 0;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
.tutorial-overlay-context .tutorial-icon {
|
|
246
|
+
font-size: 32px;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
.tutorial-overlay-context .tutorial-title {
|
|
250
|
+
font-size: calc(var(--tutorial-title-size) + 2px);
|
|
251
|
+
color: var(--tutorial-text);
|
|
252
|
+
flex: none;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
.tutorial-overlay-context .tutorial-step-title {
|
|
256
|
+
font-size: calc(var(--tutorial-step-title-size) + 2px);
|
|
257
|
+
color: var(--tutorial-text-bright);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
.tutorial-context-text {
|
|
261
|
+
font-size: var(--tutorial-description-size);
|
|
262
|
+
color: var(--tutorial-text-muted);
|
|
263
|
+
margin: 8px 0 0 0;
|
|
264
|
+
line-height: 1.5;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/* Context style variations */
|
|
268
|
+
.tutorial-context-goal {
|
|
269
|
+
box-shadow: 0 25px 80px -12px var(--tutorial-shadow), 0 0 40px rgba(59, 130, 246, 0.3);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
.tutorial-context-clarification {
|
|
273
|
+
box-shadow: 0 25px 80px -12px var(--tutorial-shadow), 0 0 40px rgba(250, 204, 21, 0.2);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
.tutorial-context-clarification .tutorial-icon {
|
|
277
|
+
filter: drop-shadow(0 0 8px rgba(250, 204, 21, 0.5));
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
.tutorial-context-attention {
|
|
281
|
+
box-shadow: 0 25px 80px -12px var(--tutorial-shadow), 0 0 40px rgba(239, 68, 68, 0.2);
|
|
282
|
+
border-color: rgba(239, 68, 68, 0.3);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
.tutorial-context-attention .tutorial-icon {
|
|
286
|
+
filter: drop-shadow(0 0 8px rgba(239, 68, 68, 0.5));
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
.tutorial-header {
|
|
290
|
+
display: flex;
|
|
291
|
+
align-items: center;
|
|
292
|
+
gap: 10px;
|
|
293
|
+
margin-bottom: 16px;
|
|
294
|
+
padding-bottom: 12px;
|
|
295
|
+
border-bottom: 1px solid var(--tutorial-border);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
.tutorial-icon {
|
|
299
|
+
font-size: var(--tutorial-icon-size);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
.tutorial-title {
|
|
303
|
+
font-size: var(--tutorial-title-size);
|
|
304
|
+
font-weight: 600;
|
|
305
|
+
color: var(--tutorial-text-muted);
|
|
306
|
+
margin: 0;
|
|
307
|
+
flex: 1;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
.tutorial-step-badge {
|
|
311
|
+
background: var(--tutorial-primary);
|
|
312
|
+
color: white;
|
|
313
|
+
font-size: var(--tutorial-badge-size);
|
|
314
|
+
font-weight: 700;
|
|
315
|
+
padding: 4px 10px;
|
|
316
|
+
border-radius: 20px;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
.tutorial-step-title {
|
|
320
|
+
font-size: var(--tutorial-step-title-size);
|
|
321
|
+
font-weight: 600;
|
|
322
|
+
color: var(--tutorial-text-bright);
|
|
323
|
+
margin: 0 0 8px 0;
|
|
324
|
+
line-height: 1.3;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
.tutorial-step-description {
|
|
328
|
+
font-size: var(--tutorial-description-size);
|
|
329
|
+
color: var(--tutorial-text-muted);
|
|
330
|
+
margin: 0;
|
|
331
|
+
line-height: 1.5;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
.tutorial-progress {
|
|
335
|
+
margin-top: 16px;
|
|
336
|
+
height: 4px;
|
|
337
|
+
background: var(--tutorial-border);
|
|
338
|
+
border-radius: 2px;
|
|
339
|
+
overflow: hidden;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
.tutorial-progress-bar {
|
|
343
|
+
height: 100%;
|
|
344
|
+
background: linear-gradient(90deg, var(--tutorial-primary), var(--tutorial-secondary));
|
|
345
|
+
border-radius: 2px;
|
|
346
|
+
transition: width var(--tutorial-animation-duration) ease;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
.tutorial-highlight {
|
|
350
|
+
position: relative;
|
|
351
|
+
z-index: calc(var(--tutorial-z-index) - 1);
|
|
352
|
+
animation: tutorial-pulse var(--tutorial-pulse-duration) ease-in-out infinite;
|
|
353
|
+
border-radius: 4px;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
.tutorial-cursor {
|
|
357
|
+
position: fixed;
|
|
358
|
+
width: 24px;
|
|
359
|
+
height: 24px;
|
|
360
|
+
pointer-events: none;
|
|
361
|
+
z-index: var(--tutorial-z-index);
|
|
362
|
+
transform: translate(-50%, -50%);
|
|
363
|
+
transition: none;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
.tutorial-cursor-clicking {
|
|
367
|
+
animation: cursor-click 0.15s ease-out;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
.tutorial-cursor svg {
|
|
371
|
+
width: 100%;
|
|
372
|
+
height: 100%;
|
|
373
|
+
filter: drop-shadow(2px 2px 2px var(--tutorial-shadow));
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
.tutorial-spotlight {
|
|
377
|
+
position: fixed;
|
|
378
|
+
top: 0;
|
|
379
|
+
left: 0;
|
|
380
|
+
width: 100vw;
|
|
381
|
+
height: 100vh;
|
|
382
|
+
background: var(--tutorial-shadow);
|
|
383
|
+
z-index: calc(var(--tutorial-z-index) - 2);
|
|
384
|
+
pointer-events: none;
|
|
385
|
+
opacity: 0;
|
|
386
|
+
transition: opacity var(--tutorial-animation-duration) ease;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
.tutorial-spotlight.active {
|
|
390
|
+
opacity: 1;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/* Email preview popup - simulates receiving an email */
|
|
394
|
+
.tutorial-email-preview {
|
|
395
|
+
position: fixed;
|
|
396
|
+
top: 50%;
|
|
397
|
+
left: 50%;
|
|
398
|
+
transform: translate(-50%, -50%);
|
|
399
|
+
z-index: calc(var(--tutorial-z-index) + 1);
|
|
400
|
+
font-family: var(--tutorial-font-family);
|
|
401
|
+
animation: tutorial-fade-in-center var(--tutorial-animation-duration) ease-out;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
.tutorial-email-window {
|
|
405
|
+
width: 900px;
|
|
406
|
+
max-width: 90vw;
|
|
407
|
+
background: #ffffff;
|
|
408
|
+
border-radius: 12px;
|
|
409
|
+
box-shadow: 0 25px 80px -12px rgba(0, 0, 0, 0.5), 0 0 0 1px rgba(0, 0, 0, 0.1);
|
|
410
|
+
overflow: hidden;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
.tutorial-email-titlebar {
|
|
414
|
+
display: flex;
|
|
415
|
+
align-items: center;
|
|
416
|
+
gap: 8px;
|
|
417
|
+
padding: 12px 16px;
|
|
418
|
+
background: linear-gradient(180deg, #f7f7f7 0%, #e8e8e8 100%);
|
|
419
|
+
border-bottom: 1px solid #d1d1d1;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
.tutorial-email-dot {
|
|
423
|
+
width: 12px;
|
|
424
|
+
height: 12px;
|
|
425
|
+
border-radius: 50%;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
.tutorial-email-dot.red { background: #ff5f57; }
|
|
429
|
+
.tutorial-email-dot.yellow { background: #ffbd2e; }
|
|
430
|
+
.tutorial-email-dot.green { background: #28c840; }
|
|
431
|
+
|
|
432
|
+
.tutorial-email-app {
|
|
433
|
+
margin-left: auto;
|
|
434
|
+
font-size: 13px;
|
|
435
|
+
color: #666;
|
|
436
|
+
font-weight: 500;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
.tutorial-email-header {
|
|
440
|
+
padding: 16px;
|
|
441
|
+
background: #fafafa;
|
|
442
|
+
border-bottom: 1px solid #e5e5e5;
|
|
443
|
+
font-size: 13px;
|
|
444
|
+
color: #333;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
.tutorial-email-field {
|
|
448
|
+
margin-bottom: 4px;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
.tutorial-email-field:last-child {
|
|
452
|
+
margin-bottom: 0;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
.tutorial-email-field strong {
|
|
456
|
+
color: #666;
|
|
457
|
+
font-weight: 500;
|
|
458
|
+
margin-right: 8px;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
.tutorial-email-body-container {
|
|
462
|
+
background: #fff;
|
|
463
|
+
max-height: 350px;
|
|
464
|
+
overflow: hidden;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
/* The email body is an isolated iframe — sizing the outer iframe gives the
|
|
468
|
+
* preview a fixed viewport; the inner document scrolls via requestAnimationFrame. */
|
|
469
|
+
.tutorial-email-iframe {
|
|
470
|
+
width: 100%;
|
|
471
|
+
height: 350px;
|
|
472
|
+
border: none;
|
|
473
|
+
display: block;
|
|
474
|
+
background: #fff;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
.tutorial-email-code {
|
|
478
|
+
background: #fff3cd;
|
|
479
|
+
color: #856404;
|
|
480
|
+
padding: 4px 12px;
|
|
481
|
+
border-radius: 4px;
|
|
482
|
+
font-weight: 700;
|
|
483
|
+
font-size: 18px;
|
|
484
|
+
letter-spacing: 2px;
|
|
485
|
+
display: inline-block;
|
|
486
|
+
margin: 8px 0;
|
|
487
|
+
border: 2px solid #ffc107;
|
|
488
|
+
animation: tutorial-pulse 1.5s ease-in-out infinite;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
.tutorial-email-preview.tutorial-overlay-rtl .tutorial-email-field strong {
|
|
492
|
+
margin-right: 0;
|
|
493
|
+
margin-left: 8px;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
/* ── Multi-scene stage ──────────────────────────────────────
|
|
497
|
+
A browser-like tab bar over a row of iframes. Every value is a
|
|
498
|
+
--tutorial-* variable, like the rest of this file: override them in
|
|
499
|
+
`customStyles` to match the product being demonstrated. */
|
|
500
|
+
|
|
501
|
+
.tutorial-stage {
|
|
502
|
+
position: fixed;
|
|
503
|
+
inset: 0;
|
|
504
|
+
display: flex;
|
|
505
|
+
flex-direction: column;
|
|
506
|
+
background: var(--tutorial-stage-bg);
|
|
507
|
+
z-index: 0;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
.tutorial-tabbar {
|
|
511
|
+
display: flex;
|
|
512
|
+
gap: var(--tutorial-stage-gap);
|
|
513
|
+
padding: var(--tutorial-tabbar-padding);
|
|
514
|
+
flex: 0 0 auto;
|
|
515
|
+
font-family: var(--tutorial-font-family);
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
/* Hide the shared tab bar in split mode — per-scene labels take over */
|
|
519
|
+
.tutorial-stage[data-split='true'] .tutorial-tabbar {
|
|
520
|
+
display: none;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
.tutorial-tab {
|
|
524
|
+
display: flex;
|
|
525
|
+
align-items: center;
|
|
526
|
+
gap: 8px;
|
|
527
|
+
padding: var(--tutorial-tab-padding);
|
|
528
|
+
border-radius: var(--tutorial-tab-radius);
|
|
529
|
+
background: var(--tutorial-tab-bg);
|
|
530
|
+
color: var(--tutorial-tab-text);
|
|
531
|
+
font-size: var(--tutorial-tab-size);
|
|
532
|
+
font-weight: 500;
|
|
533
|
+
white-space: nowrap;
|
|
534
|
+
transition: background var(--tutorial-tab-transition) ease,
|
|
535
|
+
color var(--tutorial-tab-transition) ease;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
.tutorial-tab[data-active='true'] {
|
|
539
|
+
background: var(--tutorial-tab-bg-active);
|
|
540
|
+
color: var(--tutorial-tab-text-active);
|
|
541
|
+
font-weight: 600;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
.tutorial-tab-dot {
|
|
545
|
+
width: 8px;
|
|
546
|
+
height: 8px;
|
|
547
|
+
border-radius: 50%;
|
|
548
|
+
background: var(--tutorial-tab-dot);
|
|
549
|
+
transition: background var(--tutorial-tab-transition) ease;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
.tutorial-tab[data-active='true'] .tutorial-tab-dot {
|
|
553
|
+
background: var(--tutorial-tab-dot-active);
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
.tutorial-scenes {
|
|
557
|
+
flex: 1 1 auto;
|
|
558
|
+
display: flex;
|
|
559
|
+
gap: 0;
|
|
560
|
+
min-height: 0;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
.tutorial-stage[data-split='true'] .tutorial-scenes {
|
|
564
|
+
gap: var(--tutorial-stage-gap);
|
|
565
|
+
background: var(--tutorial-stage-bg);
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
.tutorial-scene {
|
|
569
|
+
flex: 1 1 0;
|
|
570
|
+
min-width: 0;
|
|
571
|
+
display: flex;
|
|
572
|
+
flex-direction: column;
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
.tutorial-scene[data-active='false'] {
|
|
576
|
+
display: none;
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
/* Per-scene label: hidden in single mode, visible in split mode */
|
|
580
|
+
.tutorial-scene-label {
|
|
581
|
+
display: none;
|
|
582
|
+
align-items: center;
|
|
583
|
+
justify-content: center;
|
|
584
|
+
gap: 8px;
|
|
585
|
+
padding: 8px 16px;
|
|
586
|
+
background: var(--tutorial-tab-bg-active);
|
|
587
|
+
color: var(--tutorial-tab-text-active);
|
|
588
|
+
font-size: var(--tutorial-tab-size);
|
|
589
|
+
font-weight: 600;
|
|
590
|
+
font-family: var(--tutorial-font-family);
|
|
591
|
+
white-space: nowrap;
|
|
592
|
+
flex: 0 0 auto;
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
.tutorial-stage[data-split='true'] .tutorial-scene-label {
|
|
596
|
+
display: flex;
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
.tutorial-scene-label .tutorial-tab-dot {
|
|
600
|
+
background: var(--tutorial-tab-dot-active);
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
.tutorial-scene iframe {
|
|
604
|
+
flex: 1 1 auto;
|
|
605
|
+
width: 100%;
|
|
606
|
+
height: 100%;
|
|
607
|
+
border: 0;
|
|
608
|
+
background: var(--tutorial-scene-bg);
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
/* ── Pinned split (mobile multi-scene) ──────────────────────
|
|
612
|
+
Every scene stays visible at equal width for the whole tutorial: the tab
|
|
613
|
+
bar never shows, per-scene labels take over, and focus() only dims the
|
|
614
|
+
inactive labels. Set via data-layout="split" by the Tutorial class. */
|
|
615
|
+
|
|
616
|
+
.tutorial-stage[data-layout='split'] .tutorial-tabbar {
|
|
617
|
+
display: none;
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
.tutorial-stage[data-layout='split'] .tutorial-scenes {
|
|
621
|
+
gap: var(--tutorial-stage-gap);
|
|
622
|
+
background: var(--tutorial-stage-bg);
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
.tutorial-stage[data-layout='split'] .tutorial-scene,
|
|
626
|
+
.tutorial-stage[data-layout='split'] .tutorial-scene[data-active='false'] {
|
|
627
|
+
display: flex;
|
|
628
|
+
flex: 1 1 0;
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
.tutorial-stage[data-layout='split'] .tutorial-scene-label {
|
|
632
|
+
display: flex;
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
.tutorial-stage[data-layout='split'] .tutorial-scene[data-active='false'] .tutorial-scene-label {
|
|
636
|
+
opacity: var(--tutorial-scene-inactive-opacity);
|
|
637
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
export interface TimelineStep {
|
|
2
|
+
step: number;
|
|
3
|
+
title: string;
|
|
4
|
+
text?: string;
|
|
5
|
+
key?: string;
|
|
6
|
+
audioFile: string;
|
|
7
|
+
startMs: number;
|
|
8
|
+
durationMs: number;
|
|
9
|
+
/** Scene(s) on stage for this step — tells a transcript reader who was on
|
|
10
|
+
* screen. An array means two scenes shared the stage. */
|
|
11
|
+
scene?: string | string[];
|
|
12
|
+
}
|
|
13
|
+
export interface TimelineData {
|
|
14
|
+
testName: string;
|
|
15
|
+
/** Raw test title passed to Playwright's `test(...)` — the i18n key.
|
|
16
|
+
* Lets the reporter match a timeline to its testCase without re-deriving testName. */
|
|
17
|
+
testTitle: string;
|
|
18
|
+
/** Display title from the Tutorial options — what the gallery site shows on the card. */
|
|
19
|
+
title?: string;
|
|
20
|
+
testFile: string;
|
|
21
|
+
projectName: string;
|
|
22
|
+
lang: string;
|
|
23
|
+
totalDurationMs: number;
|
|
24
|
+
/** Milliseconds to trim from video start (preload time) */
|
|
25
|
+
videoTrimMs: number;
|
|
26
|
+
/** True when a full-screen black sync marker was recorded right before
|
|
27
|
+
* timeline zero — the reporter then computes the exact trim from the
|
|
28
|
+
* video itself (blackdetect) instead of trusting videoTrimMs. */
|
|
29
|
+
syncMarker?: boolean;
|
|
30
|
+
steps: TimelineStep[];
|
|
31
|
+
/** Video file path from Playwright */
|
|
32
|
+
videoPath: string;
|
|
33
|
+
/** Pre-built ffmpeg command */
|
|
34
|
+
mergeCommand: string;
|
|
35
|
+
/** Feature name sourced from @feature:* test tag */
|
|
36
|
+
feature?: string;
|
|
37
|
+
/** Recording variant (e.g. 'mobile') — testName already carries its suffix.
|
|
38
|
+
* Lets the reporter pick the right timeline when the same test was recorded
|
|
39
|
+
* in several variants, and the site filter videos by variant. */
|
|
40
|
+
variant?: string;
|
|
41
|
+
}
|
|
42
|
+
export interface TimelineMusicOptions {
|
|
43
|
+
musicFile?: string;
|
|
44
|
+
musicVolume?: number;
|
|
45
|
+
voiceVolume?: number;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Tracks timing of tutorial steps for post-processing audio merge.
|
|
49
|
+
*/
|
|
50
|
+
export declare class TutorialTimeline {
|
|
51
|
+
private testName;
|
|
52
|
+
private testTitle;
|
|
53
|
+
private title;
|
|
54
|
+
private testFile;
|
|
55
|
+
private projectName;
|
|
56
|
+
private lang;
|
|
57
|
+
private feature;
|
|
58
|
+
private variant;
|
|
59
|
+
private musicOptions;
|
|
60
|
+
private startTime;
|
|
61
|
+
private videoTrimMs;
|
|
62
|
+
private syncMarker;
|
|
63
|
+
private steps;
|
|
64
|
+
private videoPath;
|
|
65
|
+
constructor(testName: string, testFile?: string, projectName?: string, lang?: string, testTitle?: string, feature?: string, musicOptions?: TimelineMusicOptions, title?: string, variant?: string);
|
|
66
|
+
/**
|
|
67
|
+
* Mark the start of the tutorial (second 0 for video)
|
|
68
|
+
*/
|
|
69
|
+
start(videoTrimMs?: number, syncMarker?: boolean): void;
|
|
70
|
+
/**
|
|
71
|
+
* Set the video path (from Playwright's page.video()?.path())
|
|
72
|
+
*/
|
|
73
|
+
setVideoPath(path: string): void;
|
|
74
|
+
/**
|
|
75
|
+
* Record a step at a specific timestamp (for accurate voice timing)
|
|
76
|
+
*/
|
|
77
|
+
addStep(step: number, title: string, audioFile: string, durationMs: number, timestamp: number, text?: string, key?: string, scene?: string | string[]): void;
|
|
78
|
+
/**
|
|
79
|
+
* Get the timeline data with merge command
|
|
80
|
+
*/
|
|
81
|
+
getData(): TimelineData;
|
|
82
|
+
/**
|
|
83
|
+
* Save timeline to JSON file and auto-generate transcript markdown.
|
|
84
|
+
*/
|
|
85
|
+
save(outputPath: string): void;
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=timeline.d.ts.map
|