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,79 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="{{ html_lang | default(current_locale, true) }}">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<title>{{ pick_locale(site_title, current_locale, default_locale) }}</title>
|
|
7
|
+
{% include "static/_seo_head.html.j2" %}
|
|
8
|
+
<link rel="stylesheet" href="./assets/guides.css">
|
|
9
|
+
</head>
|
|
10
|
+
<body>
|
|
11
|
+
<header class="steppy-site-header">
|
|
12
|
+
<span class="steppy-site-title">{{ pick_locale(site_title, current_locale, default_locale) }}</span>
|
|
13
|
+
{% if locales | length > 1 %}
|
|
14
|
+
<nav class="steppy-locale-switch" aria-label="Language">
|
|
15
|
+
<ul>
|
|
16
|
+
{% for link in lang_links %}
|
|
17
|
+
<li>
|
|
18
|
+
{% if link.active %}
|
|
19
|
+
<span aria-current="true">{{ link.label }}</span>
|
|
20
|
+
{% else %}
|
|
21
|
+
<a href="{{ link.href }}">{{ link.label }}</a>
|
|
22
|
+
{% endif %}
|
|
23
|
+
</li>
|
|
24
|
+
{% endfor %}
|
|
25
|
+
</ul>
|
|
26
|
+
</nav>
|
|
27
|
+
{% endif %}
|
|
28
|
+
</header>
|
|
29
|
+
|
|
30
|
+
<main class="steppy-main">
|
|
31
|
+
<section class="steppy-index">
|
|
32
|
+
<h1>{{ pick_locale(site_title, current_locale, default_locale) }}</h1>
|
|
33
|
+
<ul class="steppy-index-list">
|
|
34
|
+
{% for node in navigation %}
|
|
35
|
+
{% if node.kind == 'onboarding' %}
|
|
36
|
+
<li class="steppy-index-onboarding">
|
|
37
|
+
<a href="./{{ onboarding_slug }}/{{ current_locale }}.html">
|
|
38
|
+
<span class="steppy-index-eyebrow">{{ onboarding_eyebrow }}</span>
|
|
39
|
+
<span class="steppy-index-title">{{ pick_locale(node.title, current_locale, default_locale) }}</span>
|
|
40
|
+
<span class="steppy-index-meta">{{ node.guides | length }} {{ steps_label }}</span>
|
|
41
|
+
</a>
|
|
42
|
+
</li>
|
|
43
|
+
{% elif node.kind == 'group' %}
|
|
44
|
+
<li class="steppy-index-group">
|
|
45
|
+
<a href="./{{ node.slug }}/{{ current_locale }}.html">
|
|
46
|
+
<span class="steppy-index-eyebrow">{{ group_eyebrow }}</span>
|
|
47
|
+
<span class="steppy-index-title">{{ pick_locale(node.title, current_locale, default_locale) }}</span>
|
|
48
|
+
<span class="steppy-index-meta">{{ node.children | length }} {{ guides_label }}</span>
|
|
49
|
+
{% set desc = pick_locale(node.description, current_locale, default_locale) %}
|
|
50
|
+
{% if desc %}
|
|
51
|
+
<span class="steppy-index-desc">{{ desc }}</span>
|
|
52
|
+
{% endif %}
|
|
53
|
+
</a>
|
|
54
|
+
</li>
|
|
55
|
+
{% else %}
|
|
56
|
+
<li>
|
|
57
|
+
<a href="./{{ node.slug }}/{{ current_locale }}.html">
|
|
58
|
+
<span class="steppy-index-title">{{ pick_locale(node.title, current_locale, default_locale) }}</span>
|
|
59
|
+
</a>
|
|
60
|
+
</li>
|
|
61
|
+
{% endif %}
|
|
62
|
+
{% endfor %}
|
|
63
|
+
</ul>
|
|
64
|
+
{% if handbook_url %}
|
|
65
|
+
<p class="steppy-index-handbook">
|
|
66
|
+
<a href="{{ handbook_url }}">{{ handbook_label | default('Full handbook', true) }}</a>
|
|
67
|
+
{% if handbook_pdf_url %}
|
|
68
|
+
<a class="steppy-pdf-link" href="{{ handbook_pdf_url }}" download>PDF</a>
|
|
69
|
+
{% endif %}
|
|
70
|
+
</p>
|
|
71
|
+
{% endif %}
|
|
72
|
+
</section>
|
|
73
|
+
</main>
|
|
74
|
+
|
|
75
|
+
<footer class="steppy-site-footer">
|
|
76
|
+
<p>Generated by Steppy.</p>
|
|
77
|
+
</footer>
|
|
78
|
+
</body>
|
|
79
|
+
</html>
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="{{ html_lang | default(default_locale, true) }}">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<title>{{ pick_locale(entry.title, default_locale, default_locale) }} — {{ pick_locale(site_title, default_locale, default_locale) }}</title>
|
|
7
|
+
{% include "static/_seo_head.html.j2" %}
|
|
8
|
+
<link rel="stylesheet" href="../assets/guides.css">
|
|
9
|
+
<style>
|
|
10
|
+
body {
|
|
11
|
+
margin: 0;
|
|
12
|
+
font-family: system-ui, -apple-system, sans-serif;
|
|
13
|
+
background: #f8fafc;
|
|
14
|
+
color: #0f172a;
|
|
15
|
+
}
|
|
16
|
+
/* Wider than the 900px editorial column the guide pages use, on purpose:
|
|
17
|
+
this page is an app-like widget rather than prose, and the player's
|
|
18
|
+
sidebar template spends a fixed 340px of it on the step rail before the
|
|
19
|
+
screenshot gets any. At 1024px a 1440px-wide desktop capture rendered at
|
|
20
|
+
44%; here it reaches ~73%. */
|
|
21
|
+
.steppy-interactive-shell {
|
|
22
|
+
max-width: min(1440px, 100vw - 32px);
|
|
23
|
+
margin: 0 auto;
|
|
24
|
+
padding: 24px 16px;
|
|
25
|
+
}
|
|
26
|
+
.steppy-interactive-header {
|
|
27
|
+
display: flex;
|
|
28
|
+
align-items: baseline;
|
|
29
|
+
gap: 16px;
|
|
30
|
+
margin-bottom: 16px;
|
|
31
|
+
flex-wrap: wrap;
|
|
32
|
+
}
|
|
33
|
+
.steppy-interactive-header h1 {
|
|
34
|
+
margin: 0;
|
|
35
|
+
font-size: 24px;
|
|
36
|
+
font-weight: 600;
|
|
37
|
+
}
|
|
38
|
+
.steppy-interactive-back {
|
|
39
|
+
color: #2563eb;
|
|
40
|
+
text-decoration: none;
|
|
41
|
+
font-size: 14px;
|
|
42
|
+
}
|
|
43
|
+
.steppy-interactive-back:hover { text-decoration: underline; }
|
|
44
|
+
steppy-guide { display: block; }
|
|
45
|
+
</style>
|
|
46
|
+
</head>
|
|
47
|
+
<body>
|
|
48
|
+
<div class="steppy-interactive-shell">
|
|
49
|
+
<header class="steppy-interactive-header">
|
|
50
|
+
<a class="steppy-interactive-back" href="{{ index_href }}">‹ {{ pick_locale(site_title, default_locale, default_locale) }}</a>
|
|
51
|
+
<h1>{{ pick_locale(entry.title, default_locale, default_locale) }}</h1>
|
|
52
|
+
{% include "static/_mode_links.html.j2" %}
|
|
53
|
+
{% if layouts | length > 1 %}
|
|
54
|
+
<nav class="steppy-layout-switch" aria-label="Layout">
|
|
55
|
+
<ul>
|
|
56
|
+
{% for layout in layouts %}
|
|
57
|
+
<li>
|
|
58
|
+
<button type="button" data-layout="{{ layout }}" {% if layout == default_layout %}aria-current="true"{% endif %}>{{ layout | capitalize }}</button>
|
|
59
|
+
</li>
|
|
60
|
+
{% endfor %}
|
|
61
|
+
</ul>
|
|
62
|
+
</nav>
|
|
63
|
+
{% endif %}
|
|
64
|
+
</header>
|
|
65
|
+
<steppy-guide src="./guide.json" locale="{{ default_locale }}"{% if default_layout %} variant="{{ default_layout }}"{% endif %}{% if layouts | length > 1 %} hide-layout-switcher{% endif %}{% if cursor_mode %} cursor="{{ cursor_mode }}"{% endif %} template="{{ default_template }}"></steppy-guide>
|
|
66
|
+
</div>
|
|
67
|
+
{% if layouts | length > 1 %}
|
|
68
|
+
<script>
|
|
69
|
+
(function () {
|
|
70
|
+
const persistKey = 'steppy:active-layout';
|
|
71
|
+
const layouts = {{ layouts | tojson }};
|
|
72
|
+
const guide = document.querySelector('steppy-guide');
|
|
73
|
+
const persisted = (function () {
|
|
74
|
+
try { return localStorage.getItem(persistKey); } catch (_) { return null; }
|
|
75
|
+
})();
|
|
76
|
+
const initial = (persisted && layouts.indexOf(persisted) !== -1)
|
|
77
|
+
? persisted
|
|
78
|
+
: {{ default_layout | tojson }};
|
|
79
|
+
function applyLayout(name) {
|
|
80
|
+
if (layouts.indexOf(name) === -1) return;
|
|
81
|
+
if (guide) guide.setAttribute('variant', name);
|
|
82
|
+
document.querySelectorAll('.steppy-layout-switch button').forEach(function (b) {
|
|
83
|
+
if (b.getAttribute('data-layout') === name) {
|
|
84
|
+
b.setAttribute('aria-current', 'true');
|
|
85
|
+
} else {
|
|
86
|
+
b.removeAttribute('aria-current');
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
try { localStorage.setItem(persistKey, name); } catch (_) {}
|
|
90
|
+
}
|
|
91
|
+
applyLayout(initial);
|
|
92
|
+
document.querySelectorAll('.steppy-layout-switch button').forEach(function (b) {
|
|
93
|
+
b.addEventListener('click', function () { applyLayout(b.getAttribute('data-layout')); });
|
|
94
|
+
});
|
|
95
|
+
})();
|
|
96
|
+
</script>
|
|
97
|
+
{% endif %}
|
|
98
|
+
<script type="module" src="../assets/steppy-player.js"></script>
|
|
99
|
+
</body>
|
|
100
|
+
</html>
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="{{ html_lang | default(current_locale, true) }}">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<title>{{ pick_locale(onboarding.title, current_locale, default_locale) }} — {{ pick_locale(site_title, current_locale, default_locale) }}</title>
|
|
7
|
+
{% include "static/_seo_head.html.j2" %}
|
|
8
|
+
<link rel="stylesheet" href="../assets/guides.css">
|
|
9
|
+
</head>
|
|
10
|
+
<body>
|
|
11
|
+
<header class="steppy-site-header">
|
|
12
|
+
<a class="steppy-site-title" href="{{ index_href }}">{{ pick_locale(site_title, current_locale, default_locale) }}</a>
|
|
13
|
+
<nav class="steppy-breadcrumb" aria-label="Breadcrumb">
|
|
14
|
+
<a href="{{ index_href }}">Home</a>
|
|
15
|
+
<span class="steppy-breadcrumb-sep">›</span>
|
|
16
|
+
<span aria-current="page">{{ pick_locale(onboarding.title, current_locale, default_locale) }}</span>
|
|
17
|
+
</nav>
|
|
18
|
+
{% if locales | length > 1 %}
|
|
19
|
+
<nav class="steppy-locale-switch" aria-label="Language">
|
|
20
|
+
<ul>
|
|
21
|
+
{% for link in lang_links %}
|
|
22
|
+
<li>
|
|
23
|
+
{% if link.active %}
|
|
24
|
+
<span aria-current="true">{{ link.label }}</span>
|
|
25
|
+
{% else %}
|
|
26
|
+
<a href="{{ link.href }}">{{ link.label }}</a>
|
|
27
|
+
{% endif %}
|
|
28
|
+
</li>
|
|
29
|
+
{% endfor %}
|
|
30
|
+
</ul>
|
|
31
|
+
</nav>
|
|
32
|
+
{% endif %}
|
|
33
|
+
</header>
|
|
34
|
+
|
|
35
|
+
<main class="steppy-main">
|
|
36
|
+
<section class="steppy-onboarding">
|
|
37
|
+
<h1>{{ pick_locale(onboarding.title, current_locale, default_locale) }}</h1>
|
|
38
|
+
{% set desc = pick_locale(onboarding.description, current_locale, default_locale) %}
|
|
39
|
+
{% if desc %}
|
|
40
|
+
<p class="steppy-onboarding-description">{{ desc }}</p>
|
|
41
|
+
{% endif %}
|
|
42
|
+
<ol class="steppy-onboarding-steps">
|
|
43
|
+
{% for step in steps %}
|
|
44
|
+
<li>
|
|
45
|
+
<a href="../{{ step.slug }}/{{ current_locale }}.html?onboarding=1">
|
|
46
|
+
<span class="steppy-onboarding-step-number">{{ loop.index }}</span>
|
|
47
|
+
<span class="steppy-onboarding-step-title">{{ pick_locale(step.title, current_locale, default_locale) }}</span>
|
|
48
|
+
</a>
|
|
49
|
+
</li>
|
|
50
|
+
{% endfor %}
|
|
51
|
+
</ol>
|
|
52
|
+
{% if steps %}
|
|
53
|
+
<p class="steppy-onboarding-cta">
|
|
54
|
+
<a class="steppy-onboarding-start" href="../{{ steps[0].slug }}/{{ current_locale }}.html?onboarding=1">{{ start_label }}</a>
|
|
55
|
+
</p>
|
|
56
|
+
{% endif %}
|
|
57
|
+
</section>
|
|
58
|
+
</main>
|
|
59
|
+
|
|
60
|
+
<footer class="steppy-site-footer">
|
|
61
|
+
<p>Generated by Steppy.</p>
|
|
62
|
+
</footer>
|
|
63
|
+
</body>
|
|
64
|
+
</html>
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="{{ html_lang | default(locale, true) }}">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<title>{{ pick_locale(entry.title, locale, default_locale) }} — {{ pick_locale(site_title, locale, default_locale) }}</title>
|
|
7
|
+
{% include "static/_seo_head.html.j2" %}
|
|
8
|
+
<link rel="stylesheet" href="../assets/guides.css">
|
|
9
|
+
</head>
|
|
10
|
+
<body{% if layouts %} data-active-layout="{{ default_layout }}"{% endif %}>
|
|
11
|
+
<header class="steppy-site-header">
|
|
12
|
+
<a class="steppy-site-title" href="{{ index_href }}">{{ pick_locale(site_title, locale, default_locale) }}</a>
|
|
13
|
+
<nav class="steppy-breadcrumb" aria-label="Breadcrumb">
|
|
14
|
+
<a href="{{ index_href }}">Home</a>
|
|
15
|
+
<span class="steppy-breadcrumb-sep">›</span>
|
|
16
|
+
{% if group %}
|
|
17
|
+
<a href="../{{ group.slug }}/{{ locale }}.html">{{ pick_locale(group.title, locale, default_locale) }}</a>
|
|
18
|
+
<span class="steppy-breadcrumb-sep">›</span>
|
|
19
|
+
{% endif %}
|
|
20
|
+
<span aria-current="page">{{ pick_locale(entry.title, locale, default_locale) }}</span>
|
|
21
|
+
</nav>
|
|
22
|
+
{% if locales | length > 1 %}
|
|
23
|
+
<nav class="steppy-locale-switch" aria-label="Language">
|
|
24
|
+
<ul>
|
|
25
|
+
{% for link in lang_links %}
|
|
26
|
+
<li>
|
|
27
|
+
{% if link.active %}
|
|
28
|
+
<span aria-current="true">{{ link.label }}</span>
|
|
29
|
+
{% else %}
|
|
30
|
+
<a href="{{ link.href }}">{{ link.label }}</a>
|
|
31
|
+
{% endif %}
|
|
32
|
+
</li>
|
|
33
|
+
{% endfor %}
|
|
34
|
+
</ul>
|
|
35
|
+
</nav>
|
|
36
|
+
{% endif %}
|
|
37
|
+
{% if pdf_url %}
|
|
38
|
+
<a class="steppy-pdf-link" href="{{ pdf_url }}" download>PDF</a>
|
|
39
|
+
{% endif %}
|
|
40
|
+
{% include "static/_mode_links.html.j2" %}
|
|
41
|
+
{% if layouts | length > 1 %}
|
|
42
|
+
<nav class="steppy-layout-switch" aria-label="Layout">
|
|
43
|
+
<ul>
|
|
44
|
+
{% for layout in layouts %}
|
|
45
|
+
<li>
|
|
46
|
+
<button type="button" data-layout="{{ layout }}" {% if layout == default_layout %}aria-current="true"{% endif %}>{{ layout | capitalize }}</button>
|
|
47
|
+
</li>
|
|
48
|
+
{% endfor %}
|
|
49
|
+
</ul>
|
|
50
|
+
</nav>
|
|
51
|
+
{% endif %}
|
|
52
|
+
</header>
|
|
53
|
+
|
|
54
|
+
{% if onboarding_strip %}
|
|
55
|
+
<nav class="steppy-onboarding-strip" aria-label="Onboarding progress">
|
|
56
|
+
<span class="steppy-onboarding-strip-progress">
|
|
57
|
+
<a href="../{{ onboarding_strip.path_slug }}/{{ locale }}.html">{{ pick_locale(onboarding_strip.title, locale, default_locale) }}</a>
|
|
58
|
+
— {{ onboarding_strip.position_label }}
|
|
59
|
+
</span>
|
|
60
|
+
<span class="steppy-onboarding-strip-nav">
|
|
61
|
+
{% if onboarding_strip.prev %}
|
|
62
|
+
<a class="steppy-onboarding-prev" href="../{{ onboarding_strip.prev.slug }}/{{ locale }}.html?onboarding=1">‹ {{ pick_locale(onboarding_strip.prev.title, locale, default_locale) }}</a>
|
|
63
|
+
{% else %}
|
|
64
|
+
<span class="steppy-onboarding-prev steppy-onboarding-disabled" aria-disabled="true">‹ {{ onboarding_strip.prev_label }}</span>
|
|
65
|
+
{% endif %}
|
|
66
|
+
{% if onboarding_strip.next %}
|
|
67
|
+
<a class="steppy-onboarding-next" href="../{{ onboarding_strip.next.slug }}/{{ locale }}.html?onboarding=1">{{ pick_locale(onboarding_strip.next.title, locale, default_locale) }} ›</a>
|
|
68
|
+
{% else %}
|
|
69
|
+
<span class="steppy-onboarding-next steppy-onboarding-disabled" aria-disabled="true">{{ onboarding_strip.done_label }}</span>
|
|
70
|
+
{% endif %}
|
|
71
|
+
</span>
|
|
72
|
+
</nav>
|
|
73
|
+
{% endif %}
|
|
74
|
+
|
|
75
|
+
<main class="steppy-main">
|
|
76
|
+
{% if layouts %}
|
|
77
|
+
{%- for layout in layouts %}
|
|
78
|
+
<div class="steppy-layout{% if layout == default_layout %} steppy-layout-active{% endif %}" data-layout="{{ layout }}">
|
|
79
|
+
{{ fragments_by_layout[layout] | safe }}
|
|
80
|
+
</div>
|
|
81
|
+
{%- endfor %}
|
|
82
|
+
{% else %}
|
|
83
|
+
{{ fragments_by_layout['default'] | safe }}
|
|
84
|
+
{% endif %}
|
|
85
|
+
</main>
|
|
86
|
+
|
|
87
|
+
<footer class="steppy-site-footer">
|
|
88
|
+
<p>Generated by Steppy.</p>
|
|
89
|
+
</footer>
|
|
90
|
+
|
|
91
|
+
<script>
|
|
92
|
+
{% if layouts %}
|
|
93
|
+
window.__STEPPY_STEPS_BY_LAYOUT__ = {{ steps_by_layout | tojson }};
|
|
94
|
+
window.__STEPPY_STEPS__ = window.__STEPPY_STEPS_BY_LAYOUT__[{{ default_layout | tojson }}] || [];
|
|
95
|
+
window.__STEPPY_ACTIVE_LAYOUT__ = {{ default_layout | tojson }};
|
|
96
|
+
{% else %}
|
|
97
|
+
window.__STEPPY_STEPS__ = {{ steps_by_layout['default'] | tojson }};
|
|
98
|
+
{% endif %}
|
|
99
|
+
window.__STEPPY_TIMINGS__ = {{ cursor_timings | tojson }};
|
|
100
|
+
</script>
|
|
101
|
+
{% if layouts | length > 1 %}
|
|
102
|
+
<script>
|
|
103
|
+
(function () {
|
|
104
|
+
const persistKey = 'steppy:active-layout';
|
|
105
|
+
const persisted = (function () {
|
|
106
|
+
try { return localStorage.getItem(persistKey); } catch (_) { return null; }
|
|
107
|
+
})();
|
|
108
|
+
const layouts = {{ layouts | tojson }};
|
|
109
|
+
const initial = (persisted && layouts.indexOf(persisted) !== -1)
|
|
110
|
+
? persisted
|
|
111
|
+
: {{ default_layout | tojson }};
|
|
112
|
+
function applyLayout(name) {
|
|
113
|
+
if (layouts.indexOf(name) === -1) return;
|
|
114
|
+
document.body.setAttribute('data-active-layout', name);
|
|
115
|
+
document.querySelectorAll('.steppy-layout').forEach(function (el) {
|
|
116
|
+
if (el.getAttribute('data-layout') === name) {
|
|
117
|
+
el.classList.add('steppy-layout-active');
|
|
118
|
+
} else {
|
|
119
|
+
el.classList.remove('steppy-layout-active');
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
document.querySelectorAll('.steppy-layout-switch button').forEach(function (b) {
|
|
123
|
+
if (b.getAttribute('data-layout') === name) {
|
|
124
|
+
b.setAttribute('aria-current', 'true');
|
|
125
|
+
} else {
|
|
126
|
+
b.removeAttribute('aria-current');
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
if (window.__STEPPY_STEPS_BY_LAYOUT__) {
|
|
130
|
+
window.__STEPPY_STEPS__ = window.__STEPPY_STEPS_BY_LAYOUT__[name] || [];
|
|
131
|
+
window.__STEPPY_ACTIVE_LAYOUT__ = name;
|
|
132
|
+
}
|
|
133
|
+
try { localStorage.setItem(persistKey, name); } catch (_) {}
|
|
134
|
+
document.dispatchEvent(new CustomEvent('steppy:layout-change', { detail: { layout: name } }));
|
|
135
|
+
}
|
|
136
|
+
applyLayout(initial);
|
|
137
|
+
document.querySelectorAll('.steppy-layout-switch button').forEach(function (b) {
|
|
138
|
+
b.addEventListener('click', function () { applyLayout(b.getAttribute('data-layout')); });
|
|
139
|
+
});
|
|
140
|
+
})();
|
|
141
|
+
</script>
|
|
142
|
+
{% endif %}
|
|
143
|
+
<script src="../assets/cursor.js" defer></script>
|
|
144
|
+
</body>
|
|
145
|
+
</html>
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="{{ html_lang | default(default_locale, true) }}">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<title>{{ pick_locale(entry.title, default_locale, default_locale) }} — {{ pick_locale(site_title, default_locale, default_locale) }}</title>
|
|
7
|
+
{% include "static/_seo_head.html.j2" %}
|
|
8
|
+
<link rel="stylesheet" href="../assets/guides.css">
|
|
9
|
+
<style>
|
|
10
|
+
body {
|
|
11
|
+
margin: 0;
|
|
12
|
+
font-family: system-ui, -apple-system, sans-serif;
|
|
13
|
+
background: #f8fafc;
|
|
14
|
+
color: #0f172a;
|
|
15
|
+
}
|
|
16
|
+
.steppy-video-shell {
|
|
17
|
+
max-width: 1024px;
|
|
18
|
+
margin: 0 auto;
|
|
19
|
+
padding: 24px 16px;
|
|
20
|
+
}
|
|
21
|
+
.steppy-video-header {
|
|
22
|
+
display: flex;
|
|
23
|
+
align-items: baseline;
|
|
24
|
+
gap: 16px;
|
|
25
|
+
margin-bottom: 16px;
|
|
26
|
+
flex-wrap: wrap;
|
|
27
|
+
}
|
|
28
|
+
.steppy-video-header h1 { margin: 0; font-size: 24px; font-weight: 600; }
|
|
29
|
+
.steppy-video-back { color: #2563eb; text-decoration: none; font-size: 14px; }
|
|
30
|
+
.steppy-video-back:hover { text-decoration: underline; }
|
|
31
|
+
.steppy-video-player {
|
|
32
|
+
display: block;
|
|
33
|
+
width: 100%;
|
|
34
|
+
max-width: 100%;
|
|
35
|
+
background: #000;
|
|
36
|
+
border-radius: 8px;
|
|
37
|
+
}
|
|
38
|
+
.steppy-video-switch ul {
|
|
39
|
+
list-style: none;
|
|
40
|
+
display: flex;
|
|
41
|
+
gap: 8px;
|
|
42
|
+
margin: 0;
|
|
43
|
+
padding: 0;
|
|
44
|
+
}
|
|
45
|
+
.steppy-video-switch button {
|
|
46
|
+
font: inherit;
|
|
47
|
+
font-size: 13px;
|
|
48
|
+
padding: 4px 10px;
|
|
49
|
+
border: 1px solid #cbd5e1;
|
|
50
|
+
border-radius: 999px;
|
|
51
|
+
background: #fff;
|
|
52
|
+
cursor: pointer;
|
|
53
|
+
}
|
|
54
|
+
.steppy-video-switch button[aria-current='true'] {
|
|
55
|
+
background: #0f172a;
|
|
56
|
+
border-color: #0f172a;
|
|
57
|
+
color: #fff;
|
|
58
|
+
}
|
|
59
|
+
.steppy-video-downloads {
|
|
60
|
+
margin-top: 12px;
|
|
61
|
+
font-size: 13px;
|
|
62
|
+
color: #64748b;
|
|
63
|
+
}
|
|
64
|
+
.steppy-video-downloads a { color: #2563eb; }
|
|
65
|
+
</style>
|
|
66
|
+
</head>
|
|
67
|
+
<body>
|
|
68
|
+
<div class="steppy-video-shell">
|
|
69
|
+
<header class="steppy-video-header">
|
|
70
|
+
<a class="steppy-video-back" href="{{ index_href }}">‹ {{ pick_locale(site_title, default_locale, default_locale) }}</a>
|
|
71
|
+
<h1>{{ pick_locale(entry.title, default_locale, default_locale) }}</h1>
|
|
72
|
+
{% include "static/_mode_links.html.j2" %}
|
|
73
|
+
{% if variants | length > 1 %}
|
|
74
|
+
<nav class="steppy-video-switch" aria-label="Version">
|
|
75
|
+
<ul>
|
|
76
|
+
{% for variant in variants %}
|
|
77
|
+
<li>
|
|
78
|
+
<button type="button" data-variant="{{ variant.key }}" {% if loop.first %}aria-current="true"{% endif %}>{{ variant.label }}</button>
|
|
79
|
+
</li>
|
|
80
|
+
{% endfor %}
|
|
81
|
+
</ul>
|
|
82
|
+
</nav>
|
|
83
|
+
{% endif %}
|
|
84
|
+
</header>
|
|
85
|
+
|
|
86
|
+
<video
|
|
87
|
+
id="steppy-video-player"
|
|
88
|
+
class="steppy-video-player"
|
|
89
|
+
controls
|
|
90
|
+
preload="metadata"
|
|
91
|
+
playsinline
|
|
92
|
+
poster="{{ variants[0].poster }}"
|
|
93
|
+
>
|
|
94
|
+
<source src="{{ variants[0].src }}" type="video/mp4">
|
|
95
|
+
{% for variant in variants %}
|
|
96
|
+
<track kind="captions" srclang="{{ variant.locale }}" label="{{ variant.label }}" src="{{ variant.captions }}"{% if loop.first %} default{% endif %}>
|
|
97
|
+
{% endfor %}
|
|
98
|
+
</video>
|
|
99
|
+
|
|
100
|
+
<p class="steppy-video-downloads">
|
|
101
|
+
<a id="steppy-video-download" href="{{ variants[0].src }}" download>Download MP4</a>
|
|
102
|
+
</p>
|
|
103
|
+
</div>
|
|
104
|
+
{% if variants | length > 1 %}
|
|
105
|
+
<script>
|
|
106
|
+
(function () {
|
|
107
|
+
// Swapping <source> alone does nothing once the element has loaded — the
|
|
108
|
+
// src has to move onto the <video> itself, followed by load().
|
|
109
|
+
const variants = {{ variants | tojson }};
|
|
110
|
+
const player = document.getElementById('steppy-video-player');
|
|
111
|
+
const download = document.getElementById('steppy-video-download');
|
|
112
|
+
function apply(key) {
|
|
113
|
+
const variant = variants.find(function (v) { return v.key === key; });
|
|
114
|
+
if (!variant || !player) return;
|
|
115
|
+
player.pause();
|
|
116
|
+
player.setAttribute('src', variant.src);
|
|
117
|
+
player.setAttribute('poster', variant.poster);
|
|
118
|
+
player.load();
|
|
119
|
+
if (download) download.setAttribute('href', variant.src);
|
|
120
|
+
document.querySelectorAll('.steppy-video-switch button').forEach(function (b) {
|
|
121
|
+
if (b.getAttribute('data-variant') === key) {
|
|
122
|
+
b.setAttribute('aria-current', 'true');
|
|
123
|
+
} else {
|
|
124
|
+
b.removeAttribute('aria-current');
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
document.querySelectorAll('.steppy-video-switch button').forEach(function (b) {
|
|
129
|
+
b.addEventListener('click', function () { apply(b.getAttribute('data-variant')); });
|
|
130
|
+
});
|
|
131
|
+
})();
|
|
132
|
+
</script>
|
|
133
|
+
{% endif %}
|
|
134
|
+
</body>
|
|
135
|
+
</html>
|