steppy-dev 0.3.0__py3-none-any.whl
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.
- steppy/CHANGELOG.md +862 -0
- steppy/__init__.py +9 -0
- steppy/__main__.py +13 -0
- steppy/_cli_common.py +183 -0
- steppy/_docker/Dockerfile.installed +53 -0
- steppy/_parallel/__init__.py +7 -0
- steppy/_parallel/compose.py +349 -0
- steppy/_parallel/event_bus.py +418 -0
- steppy/_parallel/failures.py +267 -0
- steppy/_parallel/fingerprint.py +141 -0
- steppy/_parallel/image.py +380 -0
- steppy/_parallel/lastfailed.py +125 -0
- steppy/_parallel/lifecycle.py +90 -0
- steppy/_parallel/preauth.py +109 -0
- steppy/_parallel/progress.py +101 -0
- steppy/_parallel/queue.py +385 -0
- steppy/_parallel/renderer.py +489 -0
- steppy/_parallel/retry.py +316 -0
- steppy/_player_bundle/THIRD_PARTY_LICENSES.txt +158 -0
- steppy/_player_bundle/__init__.py +100 -0
- steppy/_player_bundle/steppy-cursor-legacy.js +2 -0
- steppy/_player_bundle/steppy-cursor-legacy.js.map +1 -0
- steppy/_player_bundle/steppy-player.js +5419 -0
- steppy/_player_bundle/steppy-player.js.map +1 -0
- steppy/_player_bundle/steppy-video.js +472 -0
- steppy/_player_bundle/steppy-video.js.map +1 -0
- steppy/_templating.py +117 -0
- steppy/assertions.py +251 -0
- steppy/auth.py +493 -0
- steppy/build.py +2798 -0
- steppy/build_models.py +669 -0
- steppy/buildid.py +134 -0
- steppy/bundle/__init__.py +47 -0
- steppy/bundle/archive.py +239 -0
- steppy/bundle/classify.py +329 -0
- steppy/bundle/create.py +490 -0
- steppy/bundle/hashing.py +28 -0
- steppy/bundle/manifest.py +334 -0
- steppy/bundle/results.py +546 -0
- steppy/bundle_cli.py +455 -0
- steppy/captures.py +207 -0
- steppy/cli.py +2489 -0
- steppy/cloud/__init__.py +58 -0
- steppy/cloud/client.py +267 -0
- steppy/cloud/upload.py +497 -0
- steppy/compat.py +109 -0
- steppy/config.py +231 -0
- steppy/config_schema.py +87 -0
- steppy/contracts/__init__.py +54 -0
- steppy/contracts/bundle.py +215 -0
- steppy/contracts/report.py +346 -0
- steppy/contracts/tours.py +505 -0
- steppy/coordinator.py +1352 -0
- steppy/coverage/__init__.py +9 -0
- steppy/coverage/gaps.py +190 -0
- steppy/coverage/manifest.py +193 -0
- steppy/coverage/merge.py +363 -0
- steppy/coverage/normalize.py +174 -0
- steppy/coverage/report.py +178 -0
- steppy/coverage/sourcemap.py +219 -0
- steppy/coverage/store.py +473 -0
- steppy/coverage/triage.py +151 -0
- steppy/coverage_cli.py +265 -0
- steppy/durations.py +48 -0
- steppy/exclusive.py +178 -0
- steppy/flaky_cli.py +169 -0
- steppy/generator.py +633 -0
- steppy/guide.py +1467 -0
- steppy/history.py +352 -0
- steppy/init_cli.py +330 -0
- steppy/locales.py +282 -0
- steppy/migrate/__init__.py +35 -0
- steppy/migrate/apply.py +194 -0
- steppy/migrate/plan.py +199 -0
- steppy/migrate/rules.py +465 -0
- steppy/migrate/verify.py +163 -0
- steppy/migrate_cli.py +152 -0
- steppy/models.py +2539 -0
- steppy/naming.py +37 -0
- steppy/parser.py +475 -0
- steppy/pdf.py +193 -0
- steppy/presets.py +284 -0
- steppy/record_cli.py +796 -0
- steppy/report/__init__.py +37 -0
- steppy/report/assets.py +197 -0
- steppy/report/build.py +261 -0
- steppy/report/coverage_source.py +277 -0
- steppy/report/model.py +423 -0
- steppy/report/render.py +303 -0
- steppy/report/source.py +238 -0
- steppy/report_cli.py +173 -0
- steppy/runmeta.py +114 -0
- steppy/selection.py +183 -0
- steppy/sitemap.py +205 -0
- steppy/skills/steppy-app-tours/SKILL.md +448 -0
- steppy/skills/steppy-ci-report/SKILL.md +567 -0
- steppy/skills/steppy-scenario-author/SKILL.md +2176 -0
- steppy/skills/steppy-template/SKILL.md +676 -0
- steppy/templates/_steppy_events.py +291 -0
- steppy/templates/_steppy_tours.py +404 -0
- steppy/templates/angular/component-interactive.ts.j2 +109 -0
- steppy/templates/angular/component-video.ts.j2 +112 -0
- steppy/templates/angular/component.ts.j2 +229 -0
- steppy/templates/angular/content.ts.j2 +31 -0
- steppy/templates/angular/gitignore.j2 +3 -0
- steppy/templates/angular/group.component.ts.j2 +39 -0
- steppy/templates/angular/onboarding.component.ts.j2 +43 -0
- steppy/templates/angular/routes.ts.j2 +34 -0
- steppy/templates/ci/github-actions.yml +284 -0
- steppy/templates/ci/gitlab-ci.yml +178 -0
- steppy/templates/conftest.py.j2 +2405 -0
- steppy/templates/guide.book.html.j2 +145 -0
- steppy/templates/guide.fragment.html.j2 +257 -0
- steppy/templates/guide.html.j2 +301 -0
- steppy/templates/guide.md.j2 +18 -0
- steppy/templates/init/config.yaml +124 -0
- steppy/templates/init/credentials.yaml.example +30 -0
- steppy/templates/init/scenarios/example--first-flow.md +53 -0
- steppy/templates/report/_head.html.j2 +14 -0
- steppy/templates/report/_mark.html.j2 +3 -0
- steppy/templates/report/coverage.html.j2 +124 -0
- steppy/templates/report/index.html.j2 +203 -0
- steppy/templates/report/report.css +401 -0
- steppy/templates/report/scenario.html.j2 +137 -0
- steppy/templates/static/_headers.j2 +30 -0
- steppy/templates/static/_mode_links.html.j2 +22 -0
- steppy/templates/static/_seo_head.html.j2 +37 -0
- steppy/templates/static/cursor.js.j2 +10 -0
- steppy/templates/static/group.html.j2 +78 -0
- steppy/templates/static/guides.css +678 -0
- steppy/templates/static/index.html.j2 +79 -0
- steppy/templates/static/interactive.html.j2 +100 -0
- steppy/templates/static/onboarding.html.j2 +64 -0
- steppy/templates/static/page.html.j2 +145 -0
- steppy/templates/static/robots.txt.j2 +7 -0
- steppy/templates/static/video.html.j2 +135 -0
- steppy/templates/test_scenario.py.j2 +767 -0
- steppy/testids.py +166 -0
- steppy/totp.py +26 -0
- steppy/tour_spec.py +163 -0
- steppy/tours/__init__.py +22 -0
- steppy/tours/actions.py +402 -0
- steppy/tours/compiler.py +144 -0
- steppy/tours/diagnostics.py +137 -0
- steppy/tours/emit.py +239 -0
- steppy/tours/ids.py +40 -0
- steppy/tours/inference.py +399 -0
- steppy/tours/model.py +120 -0
- steppy/tours/paths.py +126 -0
- steppy/tours/pipeline.py +277 -0
- steppy/tours/publish.py +124 -0
- steppy/tours/replay.py +234 -0
- steppy/tours/routes.py +175 -0
- steppy/tours/settings.py +164 -0
- steppy/tours/text.py +237 -0
- steppy/tours_cli.py +219 -0
- steppy/tts/__init__.py +280 -0
- steppy/tts/cache.py +185 -0
- steppy/tts/presets.py +125 -0
- steppy/tts/providers/__init__.py +6 -0
- steppy/tts/providers/http.py +184 -0
- steppy/tts/providers/local.py +207 -0
- steppy/tts/providers/null.py +28 -0
- steppy/tts/registry.py +201 -0
- steppy/tts/track.py +158 -0
- steppy/tts/types.py +94 -0
- steppy/update_cli.py +460 -0
- steppy/video.py +730 -0
- steppy/video_cache.py +239 -0
- steppy/video_cli.py +271 -0
- steppy/worker.py +301 -0
- steppy_dev-0.3.0.dist-info/METADATA +1335 -0
- steppy_dev-0.3.0.dist-info/RECORD +177 -0
- steppy_dev-0.3.0.dist-info/WHEEL +4 -0
- steppy_dev-0.3.0.dist-info/entry_points.txt +2 -0
- steppy_dev-0.3.0.dist-info/licenses/LICENSE +202 -0
- steppy_dev-0.3.0.dist-info/licenses/NOTICE +13 -0
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
{# guide.book.html.j2 — the "handbook": every guide for one (role, locale, layout)
|
|
2
|
+
in a single self-contained document, designed to be read on screen and
|
|
3
|
+
printed to PDF (`steppy guide --handbook`, `steppy build --handbook`).
|
|
4
|
+
|
|
5
|
+
The guide bodies are pre-rendered `guide.fragment.html.j2` fragments passed in
|
|
6
|
+
as `guides[].html`, and the styling is the shared static stylesheet
|
|
7
|
+
(`guides_css`) so a handbook always looks like the site it came from — there
|
|
8
|
+
is no fourth copy of the guide CSS to keep in sync.
|
|
9
|
+
|
|
10
|
+
Chromium's print-to-PDF cannot emit PDF bookmarks or resolve page numbers in
|
|
11
|
+
a table of contents, so the TOC links to in-document anchors only. #}
|
|
12
|
+
<!DOCTYPE html>
|
|
13
|
+
<html lang="{{ html_lang | default(locale, true) | default('en', true) }}">
|
|
14
|
+
<head>
|
|
15
|
+
<meta charset="UTF-8">
|
|
16
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
17
|
+
<meta name="robots" content="noindex,nofollow,noarchive">
|
|
18
|
+
<title>{{ title }}</title>
|
|
19
|
+
<style>
|
|
20
|
+
{{ guides_css }}
|
|
21
|
+
|
|
22
|
+
/* ── Handbook ────────────────────────────────────────────────────────── */
|
|
23
|
+
|
|
24
|
+
.steppy-book {
|
|
25
|
+
max-width: 900px;
|
|
26
|
+
margin: 0 auto;
|
|
27
|
+
padding: 32px 16px;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.steppy-book-cover {
|
|
31
|
+
text-align: center;
|
|
32
|
+
padding: 64px 0 48px;
|
|
33
|
+
border-bottom: 3px solid #3498db;
|
|
34
|
+
margin-bottom: 32px;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.steppy-book-cover h1 {
|
|
38
|
+
font-size: 40px;
|
|
39
|
+
color: #2c3e50;
|
|
40
|
+
margin: 0 0 12px;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.steppy-book-subtitle {
|
|
44
|
+
font-size: 18px;
|
|
45
|
+
color: #7f8c8d;
|
|
46
|
+
margin: 0 0 20px;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.steppy-book-toc {
|
|
50
|
+
margin-bottom: 32px;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.steppy-book-toc h2 {
|
|
54
|
+
font-size: 22px;
|
|
55
|
+
color: #2c3e50;
|
|
56
|
+
margin: 0 0 12px;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.steppy-book-toc ol {
|
|
60
|
+
margin: 0;
|
|
61
|
+
padding-left: 24px;
|
|
62
|
+
line-height: 2;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.steppy-book-toc a {
|
|
66
|
+
color: #2c3e50;
|
|
67
|
+
text-decoration: none;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.steppy-book-toc a:hover {
|
|
71
|
+
text-decoration: underline;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.steppy-book-section {
|
|
75
|
+
font-size: 14px;
|
|
76
|
+
text-transform: uppercase;
|
|
77
|
+
letter-spacing: 0.08em;
|
|
78
|
+
color: #7f8c8d;
|
|
79
|
+
margin: 0 0 8px;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.steppy-book-guide {
|
|
83
|
+
margin-bottom: 48px;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
@media print {
|
|
87
|
+
.steppy-book {
|
|
88
|
+
padding: 0;
|
|
89
|
+
max-width: none;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/* Cover and contents each own a page; every guide starts on a fresh one.
|
|
93
|
+
Adjacent forced breaks collapse into one, so the first guide does not
|
|
94
|
+
get a blank page after the cover/contents break. */
|
|
95
|
+
.steppy-book-cover,
|
|
96
|
+
.steppy-book-toc {
|
|
97
|
+
page-break-after: always;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.steppy-book-section,
|
|
101
|
+
.steppy-book-guide {
|
|
102
|
+
page-break-before: always;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/* A section heading already opened the page its first guide starts on. */
|
|
106
|
+
.steppy-book-section + .steppy-book-guide {
|
|
107
|
+
page-break-before: auto;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
</style>
|
|
111
|
+
</head>
|
|
112
|
+
<body>
|
|
113
|
+
<div class="steppy-book">
|
|
114
|
+
<header class="steppy-book-cover">
|
|
115
|
+
<h1>{{ title }}</h1>
|
|
116
|
+
{% if subtitle %}
|
|
117
|
+
<p class="steppy-book-subtitle">{{ subtitle }}</p>
|
|
118
|
+
{% endif %}
|
|
119
|
+
{% if role %}
|
|
120
|
+
<div class="steppy-role-badge">{{ role }}</div>
|
|
121
|
+
{% endif %}
|
|
122
|
+
</header>
|
|
123
|
+
|
|
124
|
+
{% if guides | length > 1 %}
|
|
125
|
+
<nav class="steppy-book-toc" aria-label="{{ toc_label | default('Contents', true) }}">
|
|
126
|
+
<h2>{{ toc_label | default('Contents', true) }}</h2>
|
|
127
|
+
<ol>
|
|
128
|
+
{% for guide in guides %}
|
|
129
|
+
<li><a href="#{{ guide.slug }}">{{ guide.title }}</a></li>
|
|
130
|
+
{% endfor %}
|
|
131
|
+
</ol>
|
|
132
|
+
</nav>
|
|
133
|
+
{% endif %}
|
|
134
|
+
|
|
135
|
+
{% for guide in guides %}
|
|
136
|
+
{% if guide.section %}
|
|
137
|
+
<h2 class="steppy-book-section">{{ guide.section }}</h2>
|
|
138
|
+
{% endif %}
|
|
139
|
+
<section class="steppy-book-guide" id="{{ guide.slug }}">
|
|
140
|
+
{{ guide.html | safe }}
|
|
141
|
+
</section>
|
|
142
|
+
{% endfor %}
|
|
143
|
+
</div>
|
|
144
|
+
</body>
|
|
145
|
+
</html>
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
{# guide.fragment.html.j2 — innerHTML-safe HTML fragment for Angular embedding.
|
|
2
|
+
No <!DOCTYPE>, <html>, <head>, <body>, or <script> tags.
|
|
3
|
+
CSS is scoped under .steppy-guide to avoid conflicts with the host app.
|
|
4
|
+
Pass include_styles=false to suppress the inline <style> block when the host
|
|
5
|
+
page already links the shared guides.css. #}
|
|
6
|
+
{% if include_styles | default(true) %}
|
|
7
|
+
<style>
|
|
8
|
+
.steppy-guide {
|
|
9
|
+
--steppy-cursor-size: 24px;
|
|
10
|
+
--steppy-cursor-color: #0f172a;
|
|
11
|
+
--steppy-cursor-accent: #2563eb;
|
|
12
|
+
--steppy-cursor-ring: rgba(37, 99, 235, 0.55);
|
|
13
|
+
--steppy-cursor-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
|
|
14
|
+
--steppy-z-screenshot: 1;
|
|
15
|
+
--steppy-z-cursor: 10;
|
|
16
|
+
--steppy-z-ripple: 11;
|
|
17
|
+
--steppy-z-glyph: 12;
|
|
18
|
+
/* Print/PDF: cap how tall a screenshot may render. Chromium cannot
|
|
19
|
+
split an image across a page break — an uncapped full-page or mobile
|
|
20
|
+
capture is clipped instead. Scaled down, it fits. */
|
|
21
|
+
--steppy-print-max-image-height: 180mm;
|
|
22
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', sans-serif;
|
|
23
|
+
line-height: 1.6;
|
|
24
|
+
color: #333;
|
|
25
|
+
max-width: 900px;
|
|
26
|
+
margin: 0 auto;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.steppy-guide h1 {
|
|
30
|
+
font-size: 32px;
|
|
31
|
+
color: #2c3e50;
|
|
32
|
+
margin-bottom: 10px;
|
|
33
|
+
border-bottom: 3px solid #3498db;
|
|
34
|
+
padding-bottom: 10px;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.steppy-guide .steppy-role-badge {
|
|
38
|
+
display: inline-block;
|
|
39
|
+
background-color: #3498db;
|
|
40
|
+
color: white;
|
|
41
|
+
padding: 6px 16px;
|
|
42
|
+
border-radius: 20px;
|
|
43
|
+
font-size: 14px;
|
|
44
|
+
font-weight: 600;
|
|
45
|
+
margin-bottom: 30px;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.steppy-guide .steppy-step {
|
|
49
|
+
margin-bottom: 50px;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.steppy-guide .steppy-step h2 {
|
|
53
|
+
font-size: 24px;
|
|
54
|
+
color: #2c3e50;
|
|
55
|
+
margin-bottom: 15px;
|
|
56
|
+
display: flex;
|
|
57
|
+
align-items: center;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.steppy-guide .steppy-step-number {
|
|
61
|
+
display: inline-flex;
|
|
62
|
+
align-items: center;
|
|
63
|
+
justify-content: center;
|
|
64
|
+
width: 36px;
|
|
65
|
+
height: 36px;
|
|
66
|
+
background-color: #3498db;
|
|
67
|
+
color: white;
|
|
68
|
+
border-radius: 50%;
|
|
69
|
+
font-weight: bold;
|
|
70
|
+
font-size: 18px;
|
|
71
|
+
margin-right: 12px;
|
|
72
|
+
flex-shrink: 0;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.steppy-guide .steppy-step-subtitle {
|
|
76
|
+
font-size: 18px;
|
|
77
|
+
color: #7f8c8d;
|
|
78
|
+
font-weight: 500;
|
|
79
|
+
margin-top: -10px;
|
|
80
|
+
margin-bottom: 15px;
|
|
81
|
+
font-style: italic;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.steppy-guide .steppy-step-content {
|
|
85
|
+
font-size: 16px;
|
|
86
|
+
color: #555;
|
|
87
|
+
margin-bottom: 20px;
|
|
88
|
+
line-height: 1.8;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.steppy-guide .steppy-stage {
|
|
92
|
+
position: relative;
|
|
93
|
+
line-height: 0;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.steppy-guide .steppy-stage img {
|
|
97
|
+
/* width:auto (not 100%) caps display at the screenshot's intrinsic pixel
|
|
98
|
+
width: a dense capture (e.g. mobile at device_scale_factor=3) still
|
|
99
|
+
fills the column, but a low-res capture renders at native size instead
|
|
100
|
+
of being upscaled and blurred. max-width:100% keeps it responsive. */
|
|
101
|
+
width: auto;
|
|
102
|
+
max-width: 100%;
|
|
103
|
+
height: auto;
|
|
104
|
+
border: 1px solid #ddd;
|
|
105
|
+
border-radius: 4px;
|
|
106
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
107
|
+
display: block;
|
|
108
|
+
position: relative;
|
|
109
|
+
z-index: var(--steppy-z-screenshot);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
.steppy-guide .steppy-cursor {
|
|
113
|
+
position: absolute;
|
|
114
|
+
top: 0;
|
|
115
|
+
left: 0;
|
|
116
|
+
width: var(--steppy-cursor-size);
|
|
117
|
+
height: var(--steppy-cursor-size);
|
|
118
|
+
pointer-events: none;
|
|
119
|
+
z-index: var(--steppy-z-cursor);
|
|
120
|
+
opacity: 0;
|
|
121
|
+
transition: opacity 200ms ease-in-out;
|
|
122
|
+
background: var(--steppy-cursor-color);
|
|
123
|
+
clip-path: polygon(0 0, 0 75%, 25% 55%, 45% 95%, 60% 85%, 40% 50%, 75% 40%);
|
|
124
|
+
filter: drop-shadow(var(--steppy-cursor-shadow));
|
|
125
|
+
transform-origin: 0 0;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
.steppy-guide .steppy-cursor.visible {
|
|
129
|
+
opacity: 1;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
.steppy-guide .steppy-cursor.pulse {
|
|
133
|
+
animation: steppy-pulse-anim 200ms ease-in-out;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
@keyframes steppy-pulse-anim {
|
|
137
|
+
0%, 100% { transform: scale(1); }
|
|
138
|
+
50% { transform: scale(1.25); }
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
.steppy-guide .steppy-ripple {
|
|
142
|
+
position: absolute;
|
|
143
|
+
top: 0;
|
|
144
|
+
left: 0;
|
|
145
|
+
width: 12px;
|
|
146
|
+
height: 12px;
|
|
147
|
+
margin-left: -6px;
|
|
148
|
+
margin-top: -6px;
|
|
149
|
+
border-radius: 50%;
|
|
150
|
+
border: 2px solid var(--steppy-cursor-ring);
|
|
151
|
+
background: transparent;
|
|
152
|
+
z-index: var(--steppy-z-ripple);
|
|
153
|
+
pointer-events: none;
|
|
154
|
+
opacity: 0;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
.steppy-guide .steppy-ripple.fire {
|
|
158
|
+
animation: steppy-ripple-anim 500ms ease-out forwards;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
@keyframes steppy-ripple-anim {
|
|
162
|
+
0% { width: 12px; height: 12px; margin-left: -6px; margin-top: -6px; opacity: 0.9; }
|
|
163
|
+
100% { width: 72px; height: 72px; margin-left: -36px; margin-top: -36px; opacity: 0; }
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
.steppy-guide .steppy-kbd {
|
|
167
|
+
position: absolute;
|
|
168
|
+
top: 0;
|
|
169
|
+
left: 0;
|
|
170
|
+
background: #111827;
|
|
171
|
+
color: #fff;
|
|
172
|
+
font: 12px/1 ui-monospace, Menlo, Consolas, monospace;
|
|
173
|
+
padding: 5px 7px;
|
|
174
|
+
border-radius: 4px;
|
|
175
|
+
z-index: var(--steppy-z-glyph);
|
|
176
|
+
opacity: 0;
|
|
177
|
+
pointer-events: none;
|
|
178
|
+
white-space: nowrap;
|
|
179
|
+
transition: opacity 150ms ease-in-out;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
.steppy-guide .steppy-kbd.visible {
|
|
183
|
+
opacity: 1;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
@media print {
|
|
187
|
+
.steppy-guide {
|
|
188
|
+
/* Keep the step badges / role chip coloured even when the viewer
|
|
189
|
+
disables background graphics. */
|
|
190
|
+
-webkit-print-color-adjust: exact;
|
|
191
|
+
print-color-adjust: exact;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
.steppy-guide .steppy-step {
|
|
195
|
+
page-break-inside: avoid;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
.steppy-guide .steppy-stage img {
|
|
199
|
+
max-height: var(--steppy-print-max-image-height);
|
|
200
|
+
box-shadow: none;
|
|
201
|
+
page-break-inside: avoid;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
.steppy-guide .steppy-cursor,
|
|
205
|
+
.steppy-guide .steppy-ripple,
|
|
206
|
+
.steppy-guide .steppy-kbd {
|
|
207
|
+
display: none;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
@media (max-width: 768px) {
|
|
212
|
+
.steppy-guide h1 {
|
|
213
|
+
font-size: 24px;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
.steppy-guide .steppy-step h2 {
|
|
217
|
+
font-size: 20px;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
.steppy-guide .steppy-step-number {
|
|
221
|
+
width: 30px;
|
|
222
|
+
height: 30px;
|
|
223
|
+
font-size: 16px;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
</style>
|
|
227
|
+
{% endif %}
|
|
228
|
+
|
|
229
|
+
<div class="steppy-guide">
|
|
230
|
+
<h1>{{ title }}</h1>
|
|
231
|
+
{% if role %}
|
|
232
|
+
<div class="steppy-role-badge">{{ role }}</div>
|
|
233
|
+
{% endif %}
|
|
234
|
+
|
|
235
|
+
{% for step in steps %}
|
|
236
|
+
<div class="steppy-step" data-step-index="{{ step.index }}">
|
|
237
|
+
<h2>
|
|
238
|
+
<span class="steppy-step-number">{{ step.index }}</span>
|
|
239
|
+
<span>{{ step.title }}</span>
|
|
240
|
+
</h2>
|
|
241
|
+
{% if step.subtitle %}
|
|
242
|
+
<div class="steppy-step-subtitle">{{ step.subtitle }}</div>
|
|
243
|
+
{% endif %}
|
|
244
|
+
{% if step.text %}
|
|
245
|
+
<div class="steppy-step-content">{{ step.text | markdown }}</div>
|
|
246
|
+
{% endif %}
|
|
247
|
+
{% for frame in step.frames %}
|
|
248
|
+
<div class="steppy-stage" data-frame-index="{{ loop.index0 }}">
|
|
249
|
+
<img src="{{ frame.screenshot }}" alt="Step {{ step.index }}" loading="lazy">
|
|
250
|
+
<div class="steppy-cursor" aria-hidden="true"></div>
|
|
251
|
+
<div class="steppy-ripple" aria-hidden="true"></div>
|
|
252
|
+
<div class="steppy-kbd" aria-hidden="true"></div>
|
|
253
|
+
</div>
|
|
254
|
+
{% endfor %}
|
|
255
|
+
</div>
|
|
256
|
+
{% endfor %}
|
|
257
|
+
</div>
|
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="{{ html_lang | default(locale, true) | default('fr', true) }}">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>{{ title }}</title>
|
|
7
|
+
<style>
|
|
8
|
+
:root {
|
|
9
|
+
/* Cursor design tokens — override these to retheme the guided tour. */
|
|
10
|
+
--steppy-cursor-size: 24px;
|
|
11
|
+
--steppy-cursor-color: #0f172a;
|
|
12
|
+
--steppy-cursor-accent: #2563eb;
|
|
13
|
+
--steppy-cursor-ring: rgba(37, 99, 235, 0.55);
|
|
14
|
+
--steppy-cursor-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
|
|
15
|
+
--steppy-z-screenshot: 1;
|
|
16
|
+
--steppy-z-cursor: 10;
|
|
17
|
+
--steppy-z-ripple: 11;
|
|
18
|
+
--steppy-z-glyph: 12;
|
|
19
|
+
--steppy-z-tooltip: 20;
|
|
20
|
+
/* Print/PDF: cap how tall a screenshot may render. Chromium cannot
|
|
21
|
+
split an image across a page break — an uncapped full-page or
|
|
22
|
+
mobile capture is clipped instead. Scaled down, it fits. */
|
|
23
|
+
--steppy-print-max-image-height: 180mm;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
* {
|
|
27
|
+
margin: 0;
|
|
28
|
+
padding: 0;
|
|
29
|
+
box-sizing: border-box;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
body {
|
|
33
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', sans-serif;
|
|
34
|
+
line-height: 1.6;
|
|
35
|
+
color: #333;
|
|
36
|
+
background-color: #f5f5f5;
|
|
37
|
+
padding: 20px;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.container {
|
|
41
|
+
max-width: 900px;
|
|
42
|
+
margin: 0 auto;
|
|
43
|
+
background-color: white;
|
|
44
|
+
padding: 40px;
|
|
45
|
+
border-radius: 8px;
|
|
46
|
+
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
h1 {
|
|
50
|
+
font-size: 32px;
|
|
51
|
+
color: #2c3e50;
|
|
52
|
+
margin-bottom: 10px;
|
|
53
|
+
border-bottom: 3px solid #3498db;
|
|
54
|
+
padding-bottom: 10px;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.role-badge {
|
|
58
|
+
display: inline-block;
|
|
59
|
+
background-color: #3498db;
|
|
60
|
+
color: white;
|
|
61
|
+
padding: 6px 16px;
|
|
62
|
+
border-radius: 20px;
|
|
63
|
+
font-size: 14px;
|
|
64
|
+
font-weight: 600;
|
|
65
|
+
margin-bottom: 30px;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.step {
|
|
69
|
+
margin-bottom: 50px;
|
|
70
|
+
page-break-inside: avoid;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.step h2 {
|
|
74
|
+
font-size: 24px;
|
|
75
|
+
color: #2c3e50;
|
|
76
|
+
margin-bottom: 15px;
|
|
77
|
+
display: flex;
|
|
78
|
+
align-items: center;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.step-number {
|
|
82
|
+
display: inline-flex;
|
|
83
|
+
align-items: center;
|
|
84
|
+
justify-content: center;
|
|
85
|
+
width: 36px;
|
|
86
|
+
height: 36px;
|
|
87
|
+
background-color: #3498db;
|
|
88
|
+
color: white;
|
|
89
|
+
border-radius: 50%;
|
|
90
|
+
font-weight: bold;
|
|
91
|
+
font-size: 18px;
|
|
92
|
+
margin-right: 12px;
|
|
93
|
+
flex-shrink: 0;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.step-subtitle {
|
|
97
|
+
font-size: 18px;
|
|
98
|
+
color: #7f8c8d;
|
|
99
|
+
font-weight: 500;
|
|
100
|
+
margin-top: -10px;
|
|
101
|
+
margin-bottom: 15px;
|
|
102
|
+
font-style: italic;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.step-content {
|
|
106
|
+
font-size: 16px;
|
|
107
|
+
color: #555;
|
|
108
|
+
margin-bottom: 20px;
|
|
109
|
+
line-height: 1.8;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
.steppy-stage {
|
|
113
|
+
position: relative;
|
|
114
|
+
line-height: 0;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.steppy-stage img {
|
|
118
|
+
/* width:auto caps display at the screenshot's intrinsic pixel width
|
|
119
|
+
so dense captures fill the column and low-res captures render at
|
|
120
|
+
native size instead of upscaled/blurry. max-width keeps responsive. */
|
|
121
|
+
width: auto;
|
|
122
|
+
max-width: 100%;
|
|
123
|
+
height: auto;
|
|
124
|
+
border: 1px solid #ddd;
|
|
125
|
+
border-radius: 4px;
|
|
126
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
127
|
+
display: block;
|
|
128
|
+
position: relative;
|
|
129
|
+
z-index: var(--steppy-z-screenshot);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
.steppy-cursor {
|
|
133
|
+
position: absolute;
|
|
134
|
+
top: 0;
|
|
135
|
+
left: 0;
|
|
136
|
+
width: var(--steppy-cursor-size);
|
|
137
|
+
height: var(--steppy-cursor-size);
|
|
138
|
+
pointer-events: none;
|
|
139
|
+
z-index: var(--steppy-z-cursor);
|
|
140
|
+
opacity: 0;
|
|
141
|
+
transition: opacity 200ms ease-in-out;
|
|
142
|
+
background: var(--steppy-cursor-color);
|
|
143
|
+
clip-path: polygon(0 0, 0 75%, 25% 55%, 45% 95%, 60% 85%, 40% 50%, 75% 40%);
|
|
144
|
+
filter: drop-shadow(var(--steppy-cursor-shadow));
|
|
145
|
+
transform-origin: 0 0;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
.steppy-cursor.visible {
|
|
149
|
+
opacity: 1;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
.steppy-cursor.pulse {
|
|
153
|
+
animation: steppy-pulse-anim 200ms ease-in-out;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
@keyframes steppy-pulse-anim {
|
|
157
|
+
0%, 100% { transform: scale(1); }
|
|
158
|
+
50% { transform: scale(1.25); }
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
.steppy-ripple {
|
|
162
|
+
position: absolute;
|
|
163
|
+
top: 0;
|
|
164
|
+
left: 0;
|
|
165
|
+
width: 12px;
|
|
166
|
+
height: 12px;
|
|
167
|
+
margin-left: -6px;
|
|
168
|
+
margin-top: -6px;
|
|
169
|
+
border-radius: 50%;
|
|
170
|
+
border: 2px solid var(--steppy-cursor-ring);
|
|
171
|
+
background: transparent;
|
|
172
|
+
z-index: var(--steppy-z-ripple);
|
|
173
|
+
pointer-events: none;
|
|
174
|
+
opacity: 0;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
.steppy-ripple.fire {
|
|
178
|
+
animation: steppy-ripple-anim 500ms ease-out forwards;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
@keyframes steppy-ripple-anim {
|
|
182
|
+
0% { width: 12px; height: 12px; margin-left: -6px; margin-top: -6px; opacity: 0.9; }
|
|
183
|
+
100% { width: 72px; height: 72px; margin-left: -36px; margin-top: -36px; opacity: 0; }
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
.steppy-kbd {
|
|
187
|
+
position: absolute;
|
|
188
|
+
top: 0;
|
|
189
|
+
left: 0;
|
|
190
|
+
background: #111827;
|
|
191
|
+
color: #fff;
|
|
192
|
+
font: 12px/1 ui-monospace, Menlo, Consolas, monospace;
|
|
193
|
+
padding: 5px 7px;
|
|
194
|
+
border-radius: 4px;
|
|
195
|
+
z-index: var(--steppy-z-glyph);
|
|
196
|
+
opacity: 0;
|
|
197
|
+
pointer-events: none;
|
|
198
|
+
white-space: nowrap;
|
|
199
|
+
transition: opacity 150ms ease-in-out;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
.steppy-kbd.visible {
|
|
203
|
+
opacity: 1;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
@media print {
|
|
207
|
+
body {
|
|
208
|
+
background-color: white;
|
|
209
|
+
padding: 0;
|
|
210
|
+
/* Keep the step badges / role chip coloured even when the
|
|
211
|
+
viewer disables background graphics. */
|
|
212
|
+
-webkit-print-color-adjust: exact;
|
|
213
|
+
print-color-adjust: exact;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
.container {
|
|
217
|
+
box-shadow: none;
|
|
218
|
+
padding: 20px;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
.step {
|
|
222
|
+
page-break-inside: avoid;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
.steppy-stage img {
|
|
226
|
+
max-height: var(--steppy-print-max-image-height);
|
|
227
|
+
box-shadow: none;
|
|
228
|
+
page-break-inside: avoid;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
.steppy-cursor,
|
|
232
|
+
.steppy-ripple,
|
|
233
|
+
.steppy-kbd {
|
|
234
|
+
display: none;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
@media (max-width: 768px) {
|
|
239
|
+
.container {
|
|
240
|
+
padding: 20px;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
h1 {
|
|
244
|
+
font-size: 24px;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
.step h2 {
|
|
248
|
+
font-size: 20px;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
.step-number {
|
|
252
|
+
width: 30px;
|
|
253
|
+
height: 30px;
|
|
254
|
+
font-size: 16px;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
</style>
|
|
258
|
+
</head>
|
|
259
|
+
<body>
|
|
260
|
+
<div class="container">
|
|
261
|
+
<h1>{{ title }}</h1>
|
|
262
|
+
<div class="role-badge">{{ role }}</div>
|
|
263
|
+
|
|
264
|
+
{% for step in steps %}
|
|
265
|
+
<div class="step" data-step-index="{{ step.index }}">
|
|
266
|
+
<h2>
|
|
267
|
+
<span class="step-number">{{ step.index }}</span>
|
|
268
|
+
<span>{{ step.title }}</span>
|
|
269
|
+
</h2>
|
|
270
|
+
{% if step.subtitle %}
|
|
271
|
+
<div class="step-subtitle">{{ step.subtitle }}</div>
|
|
272
|
+
{% endif %}
|
|
273
|
+
{% if step.text %}
|
|
274
|
+
<div class="step-content">
|
|
275
|
+
{{ step.text | markdown }}
|
|
276
|
+
</div>
|
|
277
|
+
{% endif %}
|
|
278
|
+
{% for frame in step.frames %}
|
|
279
|
+
<div class="steppy-stage" data-frame-index="{{ loop.index0 }}">
|
|
280
|
+
<img src="{{ frame.screenshot }}" alt="Étape {{ step.index }}" loading="lazy">
|
|
281
|
+
<div class="steppy-cursor" aria-hidden="true"></div>
|
|
282
|
+
<div class="steppy-ripple" aria-hidden="true"></div>
|
|
283
|
+
<div class="steppy-kbd" aria-hidden="true"></div>
|
|
284
|
+
</div>
|
|
285
|
+
{% endfor %}
|
|
286
|
+
</div>
|
|
287
|
+
{% endfor %}
|
|
288
|
+
</div>
|
|
289
|
+
|
|
290
|
+
<script>
|
|
291
|
+
window.__STEPPY_STEPS__ = {{ steps | tojson }};
|
|
292
|
+
window.__STEPPY_TIMINGS__ = {{ cursor_timings | tojson }};
|
|
293
|
+
</script>
|
|
294
|
+
<!--
|
|
295
|
+
Cursor IIFE — sourced from packages/player/src/cursor/legacy-iife.ts and
|
|
296
|
+
bundled to src/steppy/_player_bundle/steppy-cursor-legacy.js. Inlining
|
|
297
|
+
keeps the standalone HTML self-contained (the spec's "works offline").
|
|
298
|
+
-->
|
|
299
|
+
<script>{{ cursor_iife | safe }}</script>
|
|
300
|
+
</body>
|
|
301
|
+
</html>
|