django-bootstrap6-admin 0.1.1__tar.gz → 0.1.2__tar.gz

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.
Files changed (72) hide show
  1. {django_bootstrap6_admin-0.1.1 → django_bootstrap6_admin-0.1.2}/CHANGELOG.md +8 -0
  2. {django_bootstrap6_admin-0.1.1 → django_bootstrap6_admin-0.1.2}/CONTRIBUTING.md +2 -0
  3. {django_bootstrap6_admin-0.1.1 → django_bootstrap6_admin-0.1.2}/MANIFEST.in +1 -0
  4. {django_bootstrap6_admin-0.1.1 → django_bootstrap6_admin-0.1.2}/PKG-INFO +5 -1
  5. {django_bootstrap6_admin-0.1.1 → django_bootstrap6_admin-0.1.2}/README.md +4 -0
  6. {django_bootstrap6_admin-0.1.1 → django_bootstrap6_admin-0.1.2}/src/django_bootstrap6_admin/__init__.py +1 -1
  7. {django_bootstrap6_admin-0.1.1 → django_bootstrap6_admin-0.1.2}/src/django_bootstrap6_admin/static/django_bootstrap6_admin/js/theme.js +17 -5
  8. {django_bootstrap6_admin-0.1.1 → django_bootstrap6_admin-0.1.2}/src/django_bootstrap6_admin.egg-info/PKG-INFO +5 -1
  9. {django_bootstrap6_admin-0.1.1 → django_bootstrap6_admin-0.1.2}/src/django_bootstrap6_admin.egg-info/SOURCES.txt +1 -0
  10. django_bootstrap6_admin-0.1.2/tests/js/theme_toggle.test.js +141 -0
  11. {django_bootstrap6_admin-0.1.1 → django_bootstrap6_admin-0.1.2}/LICENSE +0 -0
  12. {django_bootstrap6_admin-0.1.1 → django_bootstrap6_admin-0.1.2}/NOTICE +0 -0
  13. {django_bootstrap6_admin-0.1.1 → django_bootstrap6_admin-0.1.2}/pyproject.toml +0 -0
  14. {django_bootstrap6_admin-0.1.1 → django_bootstrap6_admin-0.1.2}/setup.cfg +0 -0
  15. {django_bootstrap6_admin-0.1.1 → django_bootstrap6_admin-0.1.2}/src/django_bootstrap6_admin/apps.py +0 -0
  16. {django_bootstrap6_admin-0.1.1 → django_bootstrap6_admin-0.1.2}/src/django_bootstrap6_admin/static/django_bootstrap6_admin/css/admin.css +0 -0
  17. {django_bootstrap6_admin-0.1.1 → django_bootstrap6_admin-0.1.2}/src/django_bootstrap6_admin/static/django_bootstrap6_admin/css/widgets-overrides.css +0 -0
  18. {django_bootstrap6_admin-0.1.1 → django_bootstrap6_admin-0.1.2}/src/django_bootstrap6_admin/static/django_bootstrap6_admin/js/admin.js +0 -0
  19. {django_bootstrap6_admin-0.1.1 → django_bootstrap6_admin-0.1.2}/src/django_bootstrap6_admin/static/django_bootstrap6_admin/vendor/bootstrap/LICENSE +0 -0
  20. {django_bootstrap6_admin-0.1.1 → django_bootstrap6_admin-0.1.2}/src/django_bootstrap6_admin/static/django_bootstrap6_admin/vendor/bootstrap/css/bootstrap.min.css +0 -0
  21. {django_bootstrap6_admin-0.1.1 → django_bootstrap6_admin-0.1.2}/src/django_bootstrap6_admin/static/django_bootstrap6_admin/vendor/bootstrap/js/bootstrap.bundle.min.js +0 -0
  22. {django_bootstrap6_admin-0.1.1 → django_bootstrap6_admin-0.1.2}/src/django_bootstrap6_admin/templates/admin/404.html +0 -0
  23. {django_bootstrap6_admin-0.1.1 → django_bootstrap6_admin-0.1.2}/src/django_bootstrap6_admin/templates/admin/500.html +0 -0
  24. {django_bootstrap6_admin-0.1.1 → django_bootstrap6_admin-0.1.2}/src/django_bootstrap6_admin/templates/admin/actions.html +0 -0
  25. {django_bootstrap6_admin-0.1.1 → django_bootstrap6_admin-0.1.2}/src/django_bootstrap6_admin/templates/admin/app_index.html +0 -0
  26. {django_bootstrap6_admin-0.1.1 → django_bootstrap6_admin-0.1.2}/src/django_bootstrap6_admin/templates/admin/app_list.html +0 -0
  27. {django_bootstrap6_admin-0.1.1 → django_bootstrap6_admin-0.1.2}/src/django_bootstrap6_admin/templates/admin/auth/user/add_form.html +0 -0
  28. {django_bootstrap6_admin-0.1.1 → django_bootstrap6_admin-0.1.2}/src/django_bootstrap6_admin/templates/admin/auth/user/change_password.html +0 -0
  29. {django_bootstrap6_admin-0.1.1 → django_bootstrap6_admin-0.1.2}/src/django_bootstrap6_admin/templates/admin/base.html +0 -0
  30. {django_bootstrap6_admin-0.1.1 → django_bootstrap6_admin-0.1.2}/src/django_bootstrap6_admin/templates/admin/base_site.html +0 -0
  31. {django_bootstrap6_admin-0.1.1 → django_bootstrap6_admin-0.1.2}/src/django_bootstrap6_admin/templates/admin/change_form.html +0 -0
  32. {django_bootstrap6_admin-0.1.1 → django_bootstrap6_admin-0.1.2}/src/django_bootstrap6_admin/templates/admin/change_form_object_tools.html +0 -0
  33. {django_bootstrap6_admin-0.1.1 → django_bootstrap6_admin-0.1.2}/src/django_bootstrap6_admin/templates/admin/change_list.html +0 -0
  34. {django_bootstrap6_admin-0.1.1 → django_bootstrap6_admin-0.1.2}/src/django_bootstrap6_admin/templates/admin/change_list_object_tools.html +0 -0
  35. {django_bootstrap6_admin-0.1.1 → django_bootstrap6_admin-0.1.2}/src/django_bootstrap6_admin/templates/admin/change_list_results.html +0 -0
  36. {django_bootstrap6_admin-0.1.1 → django_bootstrap6_admin-0.1.2}/src/django_bootstrap6_admin/templates/admin/color_theme_toggle.html +0 -0
  37. {django_bootstrap6_admin-0.1.1 → django_bootstrap6_admin-0.1.2}/src/django_bootstrap6_admin/templates/admin/date_hierarchy.html +0 -0
  38. {django_bootstrap6_admin-0.1.1 → django_bootstrap6_admin-0.1.2}/src/django_bootstrap6_admin/templates/admin/delete_confirmation.html +0 -0
  39. {django_bootstrap6_admin-0.1.1 → django_bootstrap6_admin-0.1.2}/src/django_bootstrap6_admin/templates/admin/delete_selected_confirmation.html +0 -0
  40. {django_bootstrap6_admin-0.1.1 → django_bootstrap6_admin-0.1.2}/src/django_bootstrap6_admin/templates/admin/edit_inline/stacked.html +0 -0
  41. {django_bootstrap6_admin-0.1.1 → django_bootstrap6_admin-0.1.2}/src/django_bootstrap6_admin/templates/admin/edit_inline/tabular.html +0 -0
  42. {django_bootstrap6_admin-0.1.1 → django_bootstrap6_admin-0.1.2}/src/django_bootstrap6_admin/templates/admin/filter.html +0 -0
  43. {django_bootstrap6_admin-0.1.1 → django_bootstrap6_admin-0.1.2}/src/django_bootstrap6_admin/templates/admin/includes/fieldset.html +0 -0
  44. {django_bootstrap6_admin-0.1.1 → django_bootstrap6_admin-0.1.2}/src/django_bootstrap6_admin/templates/admin/includes/object_delete_summary.html +0 -0
  45. {django_bootstrap6_admin-0.1.1 → django_bootstrap6_admin-0.1.2}/src/django_bootstrap6_admin/templates/admin/index.html +0 -0
  46. {django_bootstrap6_admin-0.1.1 → django_bootstrap6_admin-0.1.2}/src/django_bootstrap6_admin/templates/admin/login.html +0 -0
  47. {django_bootstrap6_admin-0.1.1 → django_bootstrap6_admin-0.1.2}/src/django_bootstrap6_admin/templates/admin/nav_sidebar.html +0 -0
  48. {django_bootstrap6_admin-0.1.1 → django_bootstrap6_admin-0.1.2}/src/django_bootstrap6_admin/templates/admin/object_history.html +0 -0
  49. {django_bootstrap6_admin-0.1.1 → django_bootstrap6_admin-0.1.2}/src/django_bootstrap6_admin/templates/admin/pagination.html +0 -0
  50. {django_bootstrap6_admin-0.1.1 → django_bootstrap6_admin-0.1.2}/src/django_bootstrap6_admin/templates/admin/search_form.html +0 -0
  51. {django_bootstrap6_admin-0.1.1 → django_bootstrap6_admin-0.1.2}/src/django_bootstrap6_admin/templates/admin/submit_line.html +0 -0
  52. {django_bootstrap6_admin-0.1.1 → django_bootstrap6_admin-0.1.2}/src/django_bootstrap6_admin/templates/admin/widgets/foreign_key_raw_id.html +0 -0
  53. {django_bootstrap6_admin-0.1.1 → django_bootstrap6_admin-0.1.2}/src/django_bootstrap6_admin/templates/admin/widgets/related_widget_wrapper.html +0 -0
  54. {django_bootstrap6_admin-0.1.1 → django_bootstrap6_admin-0.1.2}/src/django_bootstrap6_admin/templates/registration/logged_out.html +0 -0
  55. {django_bootstrap6_admin-0.1.1 → django_bootstrap6_admin-0.1.2}/src/django_bootstrap6_admin/templates/registration/password_change_done.html +0 -0
  56. {django_bootstrap6_admin-0.1.1 → django_bootstrap6_admin-0.1.2}/src/django_bootstrap6_admin/templates/registration/password_change_form.html +0 -0
  57. {django_bootstrap6_admin-0.1.1 → django_bootstrap6_admin-0.1.2}/src/django_bootstrap6_admin/templatetags/__init__.py +0 -0
  58. {django_bootstrap6_admin-0.1.1 → django_bootstrap6_admin-0.1.2}/src/django_bootstrap6_admin/templatetags/bootstrap6_admin.py +0 -0
  59. {django_bootstrap6_admin-0.1.1 → django_bootstrap6_admin-0.1.2}/src/django_bootstrap6_admin.egg-info/dependency_links.txt +0 -0
  60. {django_bootstrap6_admin-0.1.1 → django_bootstrap6_admin-0.1.2}/src/django_bootstrap6_admin.egg-info/requires.txt +0 -0
  61. {django_bootstrap6_admin-0.1.1 → django_bootstrap6_admin-0.1.2}/src/django_bootstrap6_admin.egg-info/top_level.txt +0 -0
  62. {django_bootstrap6_admin-0.1.1 → django_bootstrap6_admin-0.1.2}/tests/__init__.py +0 -0
  63. {django_bootstrap6_admin-0.1.1 → django_bootstrap6_admin-0.1.2}/tests/settings.py +0 -0
  64. {django_bootstrap6_admin-0.1.1 → django_bootstrap6_admin-0.1.2}/tests/smoke_wheel.py +0 -0
  65. {django_bootstrap6_admin-0.1.1 → django_bootstrap6_admin-0.1.2}/tests/test_admin_ui.py +0 -0
  66. {django_bootstrap6_admin-0.1.1 → django_bootstrap6_admin-0.1.2}/tests/test_package.py +0 -0
  67. {django_bootstrap6_admin-0.1.1 → django_bootstrap6_admin-0.1.2}/tests/test_templatetags.py +0 -0
  68. {django_bootstrap6_admin-0.1.1 → django_bootstrap6_admin-0.1.2}/tests/testapp/__init__.py +0 -0
  69. {django_bootstrap6_admin-0.1.1 → django_bootstrap6_admin-0.1.2}/tests/testapp/admin.py +0 -0
  70. {django_bootstrap6_admin-0.1.1 → django_bootstrap6_admin-0.1.2}/tests/testapp/apps.py +0 -0
  71. {django_bootstrap6_admin-0.1.1 → django_bootstrap6_admin-0.1.2}/tests/testapp/models.py +0 -0
  72. {django_bootstrap6_admin-0.1.1 → django_bootstrap6_admin-0.1.2}/tests/urls.py +0 -0
@@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.1.2] - 2026-11-15
11
+
12
+ ### Fixed
13
+
14
+ - Clicking the theme toggle in a dark system no longer moves to an `auto`
15
+ state that resolves to dark and leaves the page unchanged. The toggle skips
16
+ that no-op step, so a single click always changes the visible theme.
17
+
10
18
  ## [0.1.1] - 2026-11-15
11
19
 
12
20
  ### Added
@@ -23,6 +23,8 @@ python -m django test --settings=tests.settings
23
23
  Run the linter and formatter check before opening a pull request:
24
24
 
25
25
  ```bash
26
+ python -m django test --settings=tests.settings
27
+ node tests/js/theme_toggle.test.js
26
28
  ruff check .
27
29
  ruff format --check .
28
30
  ```
@@ -6,5 +6,6 @@ include CONTRIBUTING.md
6
6
  recursive-include src/django_bootstrap6_admin/templates *.html
7
7
  recursive-include src/django_bootstrap6_admin/static *
8
8
  recursive-include tests *.py
9
+ recursive-include tests *.js
9
10
  global-exclude __pycache__
10
11
  global-exclude *.py[cod]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: django-bootstrap6-admin
3
- Version: 0.1.1
3
+ Version: 0.1.2
4
4
  Summary: A modern Bootstrap 6 skin for the Django admin.
5
5
  Author-email: Aliberk Sandıkçı <git@asandikci.com>
6
6
  Maintainer-email: Aliberk Sandıkçı <git@asandikci.com>
@@ -65,6 +65,9 @@ permissions and JavaScript remain in charge.
65
65
  preference is synchronised with Bootstrap's `data-bs-theme` attribute.
66
66
  - Soft fade-in/fade-out transition when switching themes, skipped
67
67
  automatically for users who prefer reduced motion.
68
+ - Every click changes the visible theme: if `auto` would resolve to the theme
69
+ already on screen, the toggle skips that step instead of appearing to do
70
+ nothing.
68
71
  - Accessible focus states, labels, table headers and ARIA attributes.
69
72
  - All Django admin JavaScript hooks are preserved: `actions.js`,
70
73
  `filters.js`, `nav_sidebar.js`, inlines, Select2/autocomplete, calendar
@@ -236,6 +239,7 @@ Run the tests and the linter:
236
239
 
237
240
  ```bash
238
241
  python -m django test --settings=tests.settings
242
+ node tests/js/theme_toggle.test.js
239
243
  ruff check .
240
244
  ruff format --check .
241
245
  ```
@@ -28,6 +28,9 @@ permissions and JavaScript remain in charge.
28
28
  preference is synchronised with Bootstrap's `data-bs-theme` attribute.
29
29
  - Soft fade-in/fade-out transition when switching themes, skipped
30
30
  automatically for users who prefer reduced motion.
31
+ - Every click changes the visible theme: if `auto` would resolve to the theme
32
+ already on screen, the toggle skips that step instead of appearing to do
33
+ nothing.
31
34
  - Accessible focus states, labels, table headers and ARIA attributes.
32
35
  - All Django admin JavaScript hooks are preserved: `actions.js`,
33
36
  `filters.js`, `nav_sidebar.js`, inlines, Select2/autocomplete, calendar
@@ -199,6 +202,7 @@ Run the tests and the linter:
199
202
 
200
203
  ```bash
201
204
  python -m django test --settings=tests.settings
205
+ node tests/js/theme_toggle.test.js
202
206
  ruff check .
203
207
  ruff format --check .
204
208
  ```
@@ -1,3 +1,3 @@
1
1
  """Bootstrap 6 skin for the Django admin."""
2
2
 
3
- __version__ = "0.1.1"
3
+ __version__ = "0.1.2"
@@ -24,9 +24,10 @@
24
24
  if (modes.includes(stored)) {
25
25
  return stored;
26
26
  }
27
- // First visit: start in the effective OS theme so the visible page
28
- // and the toggle icon agree immediately. Auto remains in the cycle.
29
- return media.matches ? "dark" : "light";
27
+ // No explicit preference yet: follow the system. Keeping the mode as
28
+ // "auto" prevents the first click from landing on an auto state that
29
+ // resolves to the same theme.
30
+ return "auto";
30
31
  }
31
32
 
32
33
  function updateButtons(mode) {
@@ -43,11 +44,15 @@
43
44
  });
44
45
  }
45
46
 
47
+ function effectiveMode(mode) {
48
+ mode = normalize(mode);
49
+ return mode === "auto" ? (media.matches ? "dark" : "light") : mode;
50
+ }
51
+
46
52
  function apply(mode) {
47
53
  mode = normalize(mode);
48
54
  const root = document.documentElement;
49
- const effective =
50
- mode === "auto" ? (media.matches ? "dark" : "light") : mode;
55
+ const effective = effectiveMode(mode);
51
56
 
52
57
  root.dataset.theme = mode;
53
58
  root.dataset.effectiveTheme = effective;
@@ -121,6 +126,7 @@
121
126
 
122
127
  function cycle() {
123
128
  const mode = current();
129
+ const effective = effectiveMode(mode);
124
130
  let next;
125
131
 
126
132
  if (media.matches) {
@@ -131,6 +137,12 @@
131
137
  mode === "auto" ? "dark" : mode === "dark" ? "light" : "auto";
132
138
  }
133
139
 
140
+ // In dark systems auto is dark, and in light systems auto is light.
141
+ // Skip that no-op step so every click visibly changes the theme.
142
+ if (effectiveMode(next) === effective) {
143
+ next = effective === "dark" ? "light" : "dark";
144
+ }
145
+
134
146
  set(next);
135
147
  }
136
148
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: django-bootstrap6-admin
3
- Version: 0.1.1
3
+ Version: 0.1.2
4
4
  Summary: A modern Bootstrap 6 skin for the Django admin.
5
5
  Author-email: Aliberk Sandıkçı <git@asandikci.com>
6
6
  Maintainer-email: Aliberk Sandıkçı <git@asandikci.com>
@@ -65,6 +65,9 @@ permissions and JavaScript remain in charge.
65
65
  preference is synchronised with Bootstrap's `data-bs-theme` attribute.
66
66
  - Soft fade-in/fade-out transition when switching themes, skipped
67
67
  automatically for users who prefer reduced motion.
68
+ - Every click changes the visible theme: if `auto` would resolve to the theme
69
+ already on screen, the toggle skips that step instead of appearing to do
70
+ nothing.
68
71
  - Accessible focus states, labels, table headers and ARIA attributes.
69
72
  - All Django admin JavaScript hooks are preserved: `actions.js`,
70
73
  `filters.js`, `nav_sidebar.js`, inlines, Select2/autocomplete, calendar
@@ -236,6 +239,7 @@ Run the tests and the linter:
236
239
 
237
240
  ```bash
238
241
  python -m django test --settings=tests.settings
242
+ node tests/js/theme_toggle.test.js
239
243
  ruff check .
240
244
  ruff format --check .
241
245
  ```
@@ -63,6 +63,7 @@ tests/test_admin_ui.py
63
63
  tests/test_package.py
64
64
  tests/test_templatetags.py
65
65
  tests/urls.py
66
+ tests/js/theme_toggle.test.js
66
67
  tests/testapp/__init__.py
67
68
  tests/testapp/admin.py
68
69
  tests/testapp/apps.py
@@ -0,0 +1,141 @@
1
+ const fs = require("fs");
2
+ const vm = require("vm");
3
+
4
+ const source = fs.readFileSync(
5
+ "src/django_bootstrap6_admin/static/django_bootstrap6_admin/js/theme.js",
6
+ "utf8",
7
+ );
8
+
9
+ class ClassList {
10
+ constructor() {
11
+ this.values = new Set();
12
+ }
13
+ add(value) {
14
+ this.values.add(value);
15
+ }
16
+ remove(value) {
17
+ this.values.delete(value);
18
+ }
19
+ contains(value) {
20
+ return this.values.has(value);
21
+ }
22
+ }
23
+
24
+ function makeElement() {
25
+ return {
26
+ style: {},
27
+ classList: new ClassList(),
28
+ dataset: {},
29
+ attrs: {},
30
+ offsetWidth: 0,
31
+ setAttribute(name, value) {
32
+ this.attrs[name] = value;
33
+ },
34
+ appendChild() {},
35
+ };
36
+ }
37
+
38
+ function sleep(ms) {
39
+ return new Promise((resolve) => setTimeout(resolve, ms));
40
+ }
41
+
42
+ function effective(mode, prefersDark) {
43
+ return mode === "auto" ? (prefersDark ? "dark" : "light") : mode;
44
+ }
45
+
46
+ async function run({ prefersDark, stored, reducedMotion = false }) {
47
+ const root = makeElement();
48
+ const body = makeElement();
49
+ const localStorage = {
50
+ value: stored,
51
+ getItem() {
52
+ return this.value;
53
+ },
54
+ setItem(_key, value) {
55
+ this.value = value;
56
+ },
57
+ };
58
+ const sandbox = {
59
+ console,
60
+ setTimeout,
61
+ clearTimeout,
62
+ localStorage,
63
+ getComputedStyle() {
64
+ return { backgroundColor: "rgb(13, 20, 28)" };
65
+ },
66
+ document: {
67
+ documentElement: root,
68
+ body,
69
+ readyState: "complete",
70
+ createElement: makeElement,
71
+ querySelectorAll() {
72
+ return [];
73
+ },
74
+ addEventListener() {},
75
+ },
76
+ };
77
+ sandbox.window = sandbox;
78
+ sandbox.window.matchMedia = (query) => ({
79
+ matches: query.includes("prefers-reduced-motion")
80
+ ? reducedMotion
81
+ : query.includes("prefers-color-scheme") && prefersDark,
82
+ addEventListener() {},
83
+ });
84
+ sandbox.window.setTimeout = setTimeout;
85
+
86
+ vm.runInNewContext(source, sandbox, { filename: "theme.js" });
87
+
88
+ const api = sandbox.window.Bootstrap6AdminTheme;
89
+ const before = root.dataset.theme;
90
+ const beforeEffective = root.dataset.effectiveTheme;
91
+ api.cycle();
92
+ await sleep(reducedMotion ? 10 : 600);
93
+ const after = root.dataset.theme;
94
+ const afterEffective = root.dataset.effectiveTheme;
95
+
96
+ return { before, beforeEffective, after, afterEffective };
97
+ }
98
+
99
+ (async () => {
100
+ const cases = [
101
+ { label: "dark system, no stored theme", prefersDark: true, stored: null },
102
+ { label: "dark system, stored auto", prefersDark: true, stored: "auto" },
103
+ { label: "dark system, stored dark", prefersDark: true, stored: "dark" },
104
+ { label: "dark system, stored light", prefersDark: true, stored: "light" },
105
+ { label: "light system, no stored theme", prefersDark: false, stored: null },
106
+ { label: "light system, stored auto", prefersDark: false, stored: "auto" },
107
+ { label: "light system, stored light", prefersDark: false, stored: "light" },
108
+ { label: "light system, stored dark", prefersDark: false, stored: "dark" },
109
+ ];
110
+
111
+ let failed = false;
112
+ for (const testCase of cases) {
113
+ const result = await run(testCase);
114
+ const changed = result.beforeEffective !== result.afterEffective;
115
+ if (!changed) {
116
+ failed = true;
117
+ }
118
+ console.log(
119
+ `${changed ? "PASS" : "FAIL"} ${testCase.label}: ` +
120
+ `${result.before}/${result.beforeEffective} -> ` +
121
+ `${result.after}/${result.afterEffective}`,
122
+ );
123
+ }
124
+
125
+ const reduced = await run({
126
+ prefersDark: true,
127
+ stored: "dark",
128
+ reducedMotion: true,
129
+ });
130
+ const reducedChanged = reduced.beforeEffective !== reduced.afterEffective;
131
+ if (!reducedChanged) {
132
+ failed = true;
133
+ }
134
+ console.log(
135
+ `${reducedChanged ? "PASS" : "FAIL"} reduced motion: ` +
136
+ `${reduced.before}/${reduced.beforeEffective} -> ` +
137
+ `${reduced.after}/${reduced.afterEffective}`,
138
+ );
139
+
140
+ process.exit(failed ? 1 : 0);
141
+ })();