fxcss 0.7.0__tar.gz → 0.9.0__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.
- {fxcss-0.7.0 → fxcss-0.9.0}/PKG-INFO +71 -9
- {fxcss-0.7.0 → fxcss-0.9.0}/README.md +70 -8
- {fxcss-0.7.0 → fxcss-0.9.0}/fxcss/__init__.py +1 -1
- {fxcss-0.7.0 → fxcss-0.9.0}/fxcss/cli.py +108 -16
- {fxcss-0.7.0 → fxcss-0.9.0}/fxcss/compare.py +13 -3
- {fxcss-0.7.0 → fxcss-0.9.0}/fxcss/core.py +46 -12
- fxcss-0.9.0/fxcss/scaffold.py +152 -0
- fxcss-0.9.0/fxcss/templates/firefox-watch.yml +180 -0
- fxcss-0.9.0/fxcss/templates/pr-preview-cleanup.yml +46 -0
- fxcss-0.9.0/fxcss/templates/pr-preview-publish.yml +305 -0
- fxcss-0.9.0/fxcss/templates/pr-preview.yml +148 -0
- fxcss-0.9.0/fxcss/templates/showcase.yml +106 -0
- fxcss-0.9.0/fxcss/tweaks.py +191 -0
- {fxcss-0.7.0 → fxcss-0.9.0}/fxcss.egg-info/PKG-INFO +71 -9
- {fxcss-0.7.0 → fxcss-0.9.0}/fxcss.egg-info/SOURCES.txt +7 -0
- {fxcss-0.7.0 → fxcss-0.9.0}/pyproject.toml +3 -0
- {fxcss-0.7.0 → fxcss-0.9.0}/tests/test_units.py +110 -0
- {fxcss-0.7.0 → fxcss-0.9.0}/LICENSE +0 -0
- {fxcss-0.7.0 → fxcss-0.9.0}/fxcss/__main__.py +0 -0
- {fxcss-0.7.0 → fxcss-0.9.0}/fxcss/audit.py +0 -0
- {fxcss-0.7.0 → fxcss-0.9.0}/fxcss/catalogue.py +0 -0
- {fxcss-0.7.0 → fxcss-0.9.0}/fxcss/fetch.py +0 -0
- {fxcss-0.7.0 → fxcss-0.9.0}/fxcss/probe.py +0 -0
- {fxcss-0.7.0 → fxcss-0.9.0}/fxcss.egg-info/dependency_links.txt +0 -0
- {fxcss-0.7.0 → fxcss-0.9.0}/fxcss.egg-info/entry_points.txt +0 -0
- {fxcss-0.7.0 → fxcss-0.9.0}/fxcss.egg-info/requires.txt +0 -0
- {fxcss-0.7.0 → fxcss-0.9.0}/fxcss.egg-info/top_level.txt +0 -0
- {fxcss-0.7.0 → fxcss-0.9.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fxcss
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.9.0
|
|
4
4
|
Summary: Live-reload, inspect and screenshot-test Firefox userChrome.css themes
|
|
5
5
|
Author: AdamXweb
|
|
6
6
|
License: MIT
|
|
@@ -29,6 +29,14 @@ Edit your CSS and see it live, click any part of the UI to get its selector,
|
|
|
29
29
|
and screenshot-test changes in CI.
|
|
30
30
|
</p>
|
|
31
31
|
|
|
32
|
+
**Three ways in:**
|
|
33
|
+
|
|
34
|
+
| You are… | Start with |
|
|
35
|
+
| --- | --- |
|
|
36
|
+
| **Building a theme** | `fxcss watch` — edit CSS, see it live in ~50ms. `fxcss pick` names any element you click. |
|
|
37
|
+
| **Trying someone's theme** | `fxcss try owner/repo` — test-drive it in a throwaway profile; close the window and nothing remains. |
|
|
38
|
+
| **Maintaining a theme repo** | `fxcss init` — before/after screenshots on every pull request, on macOS, Windows and Linux. |
|
|
39
|
+
|
|
32
40
|
## Description
|
|
33
41
|
|
|
34
42
|
Working on a Firefox theme normally means: edit CSS, restart Firefox, squint,
|
|
@@ -41,10 +49,10 @@ gives you a live-reload loop, an element picker, and a screenshot differ.
|
|
|
41
49
|
|
|
42
50
|
Your real Firefox profile is never touched.
|
|
43
51
|
|
|
44
|
-

|
|
45
53
|
|
|
46
|
-
<p align="center"><sub
|
|
47
|
-
README was generated by fxcss itself.</sub></p>
|
|
54
|
+
<p align="center"><sub>Three saves in <code>fxcss watch</code> — the window updates in ~50ms.
|
|
55
|
+
Every image in this README was generated by fxcss itself.</sub></p>
|
|
48
56
|
|
|
49
57
|
## Requirements
|
|
50
58
|
|
|
@@ -93,6 +101,8 @@ at it with `--theme /path/to/theme`.
|
|
|
93
101
|
| [`watch`](#fxcss-watch) | Edit CSS and see it live, no restart |
|
|
94
102
|
| [`pick`](#fxcss-pick) | Click any part of the UI to get its CSS selector |
|
|
95
103
|
| [`inspect`](#fxcss-inspect) | Look up a selector you already have |
|
|
104
|
+
| [`init`](#fxcss-init) | Add PR previews and CI checks to your theme repo |
|
|
105
|
+
| [`tweaks`](#fxcss-tweaks) | Screenshot every install option into a committable doc |
|
|
96
106
|
| [`audit`](#fxcss-audit) | Find every selector that no longer matches, and suggest fixes |
|
|
97
107
|
| [`changelog`](#fxcss-changelog) | Diff two Firefox builds to see what chrome changed |
|
|
98
108
|
| [`snapshot`](#fxcss-changelog) | Record a Firefox's chrome names, to diff against later |
|
|
@@ -171,6 +181,8 @@ a file in your editor and the running window updates in about 50ms.
|
|
|
171
181
|
The window is yours to drive — open menus, resize it, type in the address bar,
|
|
172
182
|
right-click things. Nothing is scripted.
|
|
173
183
|
|
|
184
|
+

|
|
185
|
+
|
|
174
186
|
| flag | effect |
|
|
175
187
|
| --- | --- |
|
|
176
188
|
| `--dark` | start in dark mode, for testing `prefers-color-scheme` rules |
|
|
@@ -233,6 +245,55 @@ styled `#urlbar-background` by id, which many older themes still do. The id was
|
|
|
233
245
|
replaced by a class, so the rule silently did nothing and the address bar
|
|
234
246
|
rendered unstyled. One command found it; the fix was `.urlbar-background`.
|
|
235
247
|
|
|
248
|
+
### fxcss init
|
|
249
|
+
|
|
250
|
+
```bash
|
|
251
|
+
fxcss init # before/after previews on every PR
|
|
252
|
+
fxcss init --watch --showcase # plus the weekly Firefox audit and
|
|
253
|
+
# release screenshots
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
**Turn any theme repository into one with CI.** Run it from your theme's root
|
|
257
|
+
and it writes the preview workflows into `.github/workflows/`, ready to commit:
|
|
258
|
+
every pull request then gets a comment showing the browser chrome before and
|
|
259
|
+
after the change, with changed pixels highlighted — rendered on macOS, Windows
|
|
260
|
+
and Linux, across sixteen views and every variant stylesheet you ship.
|
|
261
|
+
|
|
262
|
+
The generation is the point, not a convenience: the fxcss version is pinned to
|
|
263
|
+
the one doing the generating, and the publish allowlist is enumerated from
|
|
264
|
+
*your* theme's variant folder — the two things that had to be hand-edited, and
|
|
265
|
+
the second one silently drops views when forgotten. Existing files are never
|
|
266
|
+
overwritten without `--force`, and the output tells you the things that
|
|
267
|
+
otherwise surprise people (the comment starts after the workflows reach your
|
|
268
|
+
default branch; first-time contributors need one approval click).
|
|
269
|
+
|
|
270
|
+
If you'd like people to know:
|
|
271
|
+
|
|
272
|
+
[](https://github.com/AdamXweb/fxcss)
|
|
273
|
+
|
|
274
|
+
### fxcss tweaks
|
|
275
|
+
|
|
276
|
+
```bash
|
|
277
|
+
fxcss tweaks
|
|
278
|
+
fxcss tweaks --combo compact-tabs+tabs-swapclose
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
**Document your install options with screenshots.** Themes describe their
|
|
282
|
+
optional stylesheets in prose — accordions of flags, `install.sh -c -n -s`
|
|
283
|
+
incantations — and a user assembles their preferred setup in their head. This
|
|
284
|
+
renders the answer instead: the base theme, every optional stylesheet, and any
|
|
285
|
+
combination you bless with `--combo`, each with a labelled **before/after crop
|
|
286
|
+
of the region it actually changes** and how much of the chrome it touches.
|
|
287
|
+
|
|
288
|
+
The output is a folder of PNGs plus `TWEAKS.md`, written to be committed:
|
|
289
|
+
relative links, and a `<details>` accordion per option so a long list stays
|
|
290
|
+
scannable on GitHub. If your README documents installer flags, they are parsed
|
|
291
|
+
and included as a table.
|
|
292
|
+
|
|
293
|
+
A tweak that changes nothing is reported as exactly that — *"changes nothing on
|
|
294
|
+
current Firefox, possibly stale"*. Optional sheets rot at least as fast as
|
|
295
|
+
selectors do, and nobody notices because nobody has them enabled.
|
|
296
|
+
|
|
236
297
|
### fxcss audit
|
|
237
298
|
|
|
238
299
|
```bash
|
|
@@ -480,11 +541,12 @@ Two things to know before wiring this up:
|
|
|
480
541
|
posted as a comment, build the images in the `pull_request` job (no write
|
|
481
542
|
permissions, no secrets) and publish from a separate `workflow_run` job.
|
|
482
543
|
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
544
|
+
Don't copy workflow files by hand — `fxcss init` generates them for your theme,
|
|
545
|
+
allowlist and version pin included. [`examples/README.md`](examples/README.md)
|
|
546
|
+
explains the shape of what it writes, most importantly why the preview is two
|
|
547
|
+
workflows (fork PRs get a read-only token, so the half that runs their code
|
|
548
|
+
cannot be the half that posts the comment). This repo's own CI runs the full
|
|
549
|
+
pipeline against `examples/minimal-theme` on macOS, Windows and Linux.
|
|
488
550
|
|
|
489
551
|
## Things worth knowing
|
|
490
552
|
|
|
@@ -8,6 +8,14 @@ Edit your CSS and see it live, click any part of the UI to get its selector,
|
|
|
8
8
|
and screenshot-test changes in CI.
|
|
9
9
|
</p>
|
|
10
10
|
|
|
11
|
+
**Three ways in:**
|
|
12
|
+
|
|
13
|
+
| You are… | Start with |
|
|
14
|
+
| --- | --- |
|
|
15
|
+
| **Building a theme** | `fxcss watch` — edit CSS, see it live in ~50ms. `fxcss pick` names any element you click. |
|
|
16
|
+
| **Trying someone's theme** | `fxcss try owner/repo` — test-drive it in a throwaway profile; close the window and nothing remains. |
|
|
17
|
+
| **Maintaining a theme repo** | `fxcss init` — before/after screenshots on every pull request, on macOS, Windows and Linux. |
|
|
18
|
+
|
|
11
19
|
## Description
|
|
12
20
|
|
|
13
21
|
Working on a Firefox theme normally means: edit CSS, restart Firefox, squint,
|
|
@@ -20,10 +28,10 @@ gives you a live-reload loop, an element picker, and a screenshot differ.
|
|
|
20
28
|
|
|
21
29
|
Your real Firefox profile is never touched.
|
|
22
30
|
|
|
23
|
-

|
|
24
32
|
|
|
25
|
-
<p align="center"><sub
|
|
26
|
-
README was generated by fxcss itself.</sub></p>
|
|
33
|
+
<p align="center"><sub>Three saves in <code>fxcss watch</code> — the window updates in ~50ms.
|
|
34
|
+
Every image in this README was generated by fxcss itself.</sub></p>
|
|
27
35
|
|
|
28
36
|
## Requirements
|
|
29
37
|
|
|
@@ -72,6 +80,8 @@ at it with `--theme /path/to/theme`.
|
|
|
72
80
|
| [`watch`](#fxcss-watch) | Edit CSS and see it live, no restart |
|
|
73
81
|
| [`pick`](#fxcss-pick) | Click any part of the UI to get its CSS selector |
|
|
74
82
|
| [`inspect`](#fxcss-inspect) | Look up a selector you already have |
|
|
83
|
+
| [`init`](#fxcss-init) | Add PR previews and CI checks to your theme repo |
|
|
84
|
+
| [`tweaks`](#fxcss-tweaks) | Screenshot every install option into a committable doc |
|
|
75
85
|
| [`audit`](#fxcss-audit) | Find every selector that no longer matches, and suggest fixes |
|
|
76
86
|
| [`changelog`](#fxcss-changelog) | Diff two Firefox builds to see what chrome changed |
|
|
77
87
|
| [`snapshot`](#fxcss-changelog) | Record a Firefox's chrome names, to diff against later |
|
|
@@ -150,6 +160,8 @@ a file in your editor and the running window updates in about 50ms.
|
|
|
150
160
|
The window is yours to drive — open menus, resize it, type in the address bar,
|
|
151
161
|
right-click things. Nothing is scripted.
|
|
152
162
|
|
|
163
|
+

|
|
164
|
+
|
|
153
165
|
| flag | effect |
|
|
154
166
|
| --- | --- |
|
|
155
167
|
| `--dark` | start in dark mode, for testing `prefers-color-scheme` rules |
|
|
@@ -212,6 +224,55 @@ styled `#urlbar-background` by id, which many older themes still do. The id was
|
|
|
212
224
|
replaced by a class, so the rule silently did nothing and the address bar
|
|
213
225
|
rendered unstyled. One command found it; the fix was `.urlbar-background`.
|
|
214
226
|
|
|
227
|
+
### fxcss init
|
|
228
|
+
|
|
229
|
+
```bash
|
|
230
|
+
fxcss init # before/after previews on every PR
|
|
231
|
+
fxcss init --watch --showcase # plus the weekly Firefox audit and
|
|
232
|
+
# release screenshots
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
**Turn any theme repository into one with CI.** Run it from your theme's root
|
|
236
|
+
and it writes the preview workflows into `.github/workflows/`, ready to commit:
|
|
237
|
+
every pull request then gets a comment showing the browser chrome before and
|
|
238
|
+
after the change, with changed pixels highlighted — rendered on macOS, Windows
|
|
239
|
+
and Linux, across sixteen views and every variant stylesheet you ship.
|
|
240
|
+
|
|
241
|
+
The generation is the point, not a convenience: the fxcss version is pinned to
|
|
242
|
+
the one doing the generating, and the publish allowlist is enumerated from
|
|
243
|
+
*your* theme's variant folder — the two things that had to be hand-edited, and
|
|
244
|
+
the second one silently drops views when forgotten. Existing files are never
|
|
245
|
+
overwritten without `--force`, and the output tells you the things that
|
|
246
|
+
otherwise surprise people (the comment starts after the workflows reach your
|
|
247
|
+
default branch; first-time contributors need one approval click).
|
|
248
|
+
|
|
249
|
+
If you'd like people to know:
|
|
250
|
+
|
|
251
|
+
[](https://github.com/AdamXweb/fxcss)
|
|
252
|
+
|
|
253
|
+
### fxcss tweaks
|
|
254
|
+
|
|
255
|
+
```bash
|
|
256
|
+
fxcss tweaks
|
|
257
|
+
fxcss tweaks --combo compact-tabs+tabs-swapclose
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
**Document your install options with screenshots.** Themes describe their
|
|
261
|
+
optional stylesheets in prose — accordions of flags, `install.sh -c -n -s`
|
|
262
|
+
incantations — and a user assembles their preferred setup in their head. This
|
|
263
|
+
renders the answer instead: the base theme, every optional stylesheet, and any
|
|
264
|
+
combination you bless with `--combo`, each with a labelled **before/after crop
|
|
265
|
+
of the region it actually changes** and how much of the chrome it touches.
|
|
266
|
+
|
|
267
|
+
The output is a folder of PNGs plus `TWEAKS.md`, written to be committed:
|
|
268
|
+
relative links, and a `<details>` accordion per option so a long list stays
|
|
269
|
+
scannable on GitHub. If your README documents installer flags, they are parsed
|
|
270
|
+
and included as a table.
|
|
271
|
+
|
|
272
|
+
A tweak that changes nothing is reported as exactly that — *"changes nothing on
|
|
273
|
+
current Firefox, possibly stale"*. Optional sheets rot at least as fast as
|
|
274
|
+
selectors do, and nobody notices because nobody has them enabled.
|
|
275
|
+
|
|
215
276
|
### fxcss audit
|
|
216
277
|
|
|
217
278
|
```bash
|
|
@@ -459,11 +520,12 @@ Two things to know before wiring this up:
|
|
|
459
520
|
posted as a comment, build the images in the `pull_request` job (no write
|
|
460
521
|
permissions, no secrets) and publish from a separate `workflow_run` job.
|
|
461
522
|
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
523
|
+
Don't copy workflow files by hand — `fxcss init` generates them for your theme,
|
|
524
|
+
allowlist and version pin included. [`examples/README.md`](examples/README.md)
|
|
525
|
+
explains the shape of what it writes, most importantly why the preview is two
|
|
526
|
+
workflows (fork PRs get a read-only token, so the half that runs their code
|
|
527
|
+
cannot be the half that posts the comment). This repo's own CI runs the full
|
|
528
|
+
pipeline against `examples/minimal-theme` on macOS, Windows and Linux.
|
|
467
529
|
|
|
468
530
|
## Things worth knowing
|
|
469
531
|
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
"""fxcss - a testing toolkit for Firefox userChrome.css themes.
|
|
3
3
|
|
|
4
4
|
fxcss try download a theme from GitHub and test-drive it
|
|
5
|
+
fxcss init add PR previews and CI checks to your theme repo
|
|
6
|
+
fxcss tweaks screenshot every install option, into a committable doc
|
|
5
7
|
fxcss watch edit CSS and see it live, no restart
|
|
6
8
|
fxcss pick click any part of the UI to get its CSS selector
|
|
7
9
|
fxcss inspect look up a selector you already have
|
|
@@ -26,6 +28,23 @@ from pathlib import Path
|
|
|
26
28
|
from . import core
|
|
27
29
|
from . import __version__
|
|
28
30
|
|
|
31
|
+
LANDING = """fxcss - a testing toolkit for Firefox userChrome.css themes
|
|
32
|
+
|
|
33
|
+
Building a theme?
|
|
34
|
+
fxcss watch edit CSS, see it live, no restart
|
|
35
|
+
fxcss pick click any part of the UI to get its selector
|
|
36
|
+
|
|
37
|
+
Trying someone else's theme?
|
|
38
|
+
fxcss try owner/repo test-drive it in a throwaway profile
|
|
39
|
+
|
|
40
|
+
Maintaining a theme repository?
|
|
41
|
+
fxcss init add before/after PR previews and CI checks
|
|
42
|
+
fxcss tweaks screenshot every install option for your README
|
|
43
|
+
fxcss audit find selectors Firefox has renamed
|
|
44
|
+
|
|
45
|
+
Run `fxcss --help` for the full command list, or `fxcss <command> --help`
|
|
46
|
+
for one command. Nothing here ever touches your real Firefox profile."""
|
|
47
|
+
|
|
29
48
|
TOOLBOX_KEY = {
|
|
30
49
|
"darwin": "Cmd+Opt+Shift+I",
|
|
31
50
|
}.get(sys.platform, "Ctrl+Alt+Shift+I")
|
|
@@ -159,6 +178,54 @@ def cmd_try(args):
|
|
|
159
178
|
shutil.rmtree(workdir, ignore_errors=True)
|
|
160
179
|
|
|
161
180
|
|
|
181
|
+
def cmd_tweaks(args):
|
|
182
|
+
from . import tweaks
|
|
183
|
+
theme = args.theme.resolve()
|
|
184
|
+
if not (theme / "chrome" / "userChrome.css").exists():
|
|
185
|
+
print(f"error: no chrome/userChrome.css under {theme}", file=sys.stderr)
|
|
186
|
+
return 2
|
|
187
|
+
available = core.find_variant_sheets(theme)
|
|
188
|
+
if not available:
|
|
189
|
+
print("This theme ships no optional stylesheets (looked in custom/, "
|
|
190
|
+
"optional/, options/, extras/, variants/), so there is nothing "
|
|
191
|
+
"to document.", file=sys.stderr)
|
|
192
|
+
return 2
|
|
193
|
+
variants = {slug: [path] for slug, path in available.items()}
|
|
194
|
+
for combo in args.combo or []:
|
|
195
|
+
try:
|
|
196
|
+
variants.update(core.parse_variant_spec(combo, available))
|
|
197
|
+
except ValueError as exc:
|
|
198
|
+
print(f"error: {exc}", file=sys.stderr)
|
|
199
|
+
return 2
|
|
200
|
+
|
|
201
|
+
firefox = core.find_firefox(args.firefox)
|
|
202
|
+
out = args.out.resolve()
|
|
203
|
+
print(f"fxcss tweaks\n theme: {theme}\n out: {out}\n")
|
|
204
|
+
with core.Session(theme, firefox, dark=args.dark) as session:
|
|
205
|
+
entries = tweaks.build(session, theme, out, variants, tweaks.readme_flags(theme))
|
|
206
|
+
stale = [e["slug"] for e in entries if not e["image"]]
|
|
207
|
+
print(f"\n wrote {out / 'TWEAKS.md'} with {len(entries)} option(s)")
|
|
208
|
+
if stale:
|
|
209
|
+
print(f" note: {', '.join(stale)} changed nothing — possibly stale, "
|
|
210
|
+
f"worth a look")
|
|
211
|
+
print(" Commit the folder and link TWEAKS.md from your README.")
|
|
212
|
+
return 0
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def cmd_init(args):
|
|
216
|
+
from . import scaffold
|
|
217
|
+
theme = args.theme.resolve()
|
|
218
|
+
if not (theme / "chrome" / "userChrome.css").exists():
|
|
219
|
+
print(f"error: no chrome/userChrome.css under {theme} - run this from "
|
|
220
|
+
f"your theme's root, or pass --theme", file=sys.stderr)
|
|
221
|
+
return 2
|
|
222
|
+
slugs = sorted(core.find_variant_sheets(theme))
|
|
223
|
+
written, skipped = scaffold.write_workflows(
|
|
224
|
+
theme, slugs, watch=args.watch, showcase=args.showcase, force=args.force)
|
|
225
|
+
print(scaffold.next_steps(written, skipped, slugs, args.watch, args.showcase))
|
|
226
|
+
return 0
|
|
227
|
+
|
|
228
|
+
|
|
162
229
|
def cmd_watch(args):
|
|
163
230
|
theme = args.theme.resolve()
|
|
164
231
|
firefox = core.find_firefox(args.firefox)
|
|
@@ -361,21 +428,13 @@ def cmd_shot(args):
|
|
|
361
428
|
variants = {}
|
|
362
429
|
spec = getattr(args, "variants", None)
|
|
363
430
|
if spec:
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
wanted = [w.strip().lower() for w in spec.split(",") if w.strip()]
|
|
372
|
-
missing = [w for w in wanted if w not in available]
|
|
373
|
-
if missing:
|
|
374
|
-
print("error: no optional stylesheet named "
|
|
375
|
-
+ ", ".join(missing) + "; available: "
|
|
376
|
-
+ (", ".join(sorted(available)) or "none"), file=sys.stderr)
|
|
377
|
-
return 2
|
|
378
|
-
variants = {w: available[w] for w in wanted}
|
|
431
|
+
try:
|
|
432
|
+
variants = core.parse_variant_spec(spec, core.find_variant_sheets(theme))
|
|
433
|
+
except ValueError as exc:
|
|
434
|
+
print(f"error: {exc}", file=sys.stderr)
|
|
435
|
+
return 2
|
|
436
|
+
if not variants:
|
|
437
|
+
print(" note: --variants all found no optional stylesheets", flush=True)
|
|
379
438
|
|
|
380
439
|
firefox = core.find_firefox(args.firefox)
|
|
381
440
|
with core.Session(theme, firefox) as session:
|
|
@@ -442,6 +501,8 @@ def cmd_doctor(args):
|
|
|
442
501
|
print(f"\ntheme: {len(sheets)} stylesheets, {total:,} bytes")
|
|
443
502
|
missing = [p for p in ("chrome/userChrome.css",) if not (theme / p).exists()]
|
|
444
503
|
print("missing expected files: " + (", ".join(missing) if missing else "none"))
|
|
504
|
+
print("\nnew here? `fxcss try owner/repo` test-drives a theme; `fxcss init`")
|
|
505
|
+
print("adds PR previews to yours.")
|
|
445
506
|
return 0
|
|
446
507
|
|
|
447
508
|
|
|
@@ -468,7 +529,7 @@ def main(argv=None):
|
|
|
468
529
|
prog="fxcss", description=__doc__,
|
|
469
530
|
formatter_class=argparse.RawDescriptionHelpFormatter)
|
|
470
531
|
ap.add_argument("--version", action="version", version=f"fxcss {__version__}")
|
|
471
|
-
sub = ap.add_subparsers(dest="cmd", required=
|
|
532
|
+
sub = ap.add_subparsers(dest="cmd", required=False)
|
|
472
533
|
|
|
473
534
|
for verb, helptext in (("try", "download a theme from GitHub and test-drive it"),
|
|
474
535
|
("install", "alias for try")):
|
|
@@ -492,6 +553,31 @@ def main(argv=None):
|
|
|
492
553
|
_menus(tr)
|
|
493
554
|
tr.set_defaults(func=cmd_try)
|
|
494
555
|
|
|
556
|
+
tw = sub.add_parser("tweaks",
|
|
557
|
+
help="screenshot every install option into a committable doc")
|
|
558
|
+
tw.add_argument("--theme", type=Path, default=Path.cwd(),
|
|
559
|
+
help="theme root, the folder containing chrome/ (default: cwd)")
|
|
560
|
+
tw.add_argument("--firefox", default=None,
|
|
561
|
+
help="path to the firefox binary (default: autodetect)")
|
|
562
|
+
tw.add_argument("--out", type=Path, default=Path("docs/tweaks"),
|
|
563
|
+
help="output folder (default: docs/tweaks)")
|
|
564
|
+
tw.add_argument("--combo", action="append", metavar="NAME+NAME",
|
|
565
|
+
help="also render options together, e.g. --combo "
|
|
566
|
+
"compact-tabs+tabs-swapclose; repeatable")
|
|
567
|
+
tw.add_argument("--dark", action="store_true", help="render in dark mode")
|
|
568
|
+
tw.set_defaults(func=cmd_tweaks)
|
|
569
|
+
|
|
570
|
+
ini = sub.add_parser("init", help="add PR previews and CI checks to your theme repo")
|
|
571
|
+
ini.add_argument("--theme", type=Path, default=Path.cwd(),
|
|
572
|
+
help="theme root, the folder containing chrome/ (default: cwd)")
|
|
573
|
+
ini.add_argument("--watch", action="store_true",
|
|
574
|
+
help="also add the weekly Firefox release/beta/nightly audit")
|
|
575
|
+
ini.add_argument("--showcase", action="store_true",
|
|
576
|
+
help="also add the on-release showcase screenshot workflow")
|
|
577
|
+
ini.add_argument("--force", action="store_true",
|
|
578
|
+
help="replace workflow files that already exist")
|
|
579
|
+
ini.set_defaults(func=cmd_init)
|
|
580
|
+
|
|
495
581
|
w = sub.add_parser("watch", help="live-reload the theme as you edit")
|
|
496
582
|
_common(w); _menus(w)
|
|
497
583
|
w.add_argument("--dark", action="store_true", help="start in dark mode")
|
|
@@ -577,6 +663,12 @@ def main(argv=None):
|
|
|
577
663
|
d.set_defaults(func=cmd_doctor)
|
|
578
664
|
|
|
579
665
|
args = ap.parse_args(argv)
|
|
666
|
+
if args.cmd is None:
|
|
667
|
+
# Bare `fxcss` used to be an argparse error. Greet by task instead:
|
|
668
|
+
# the three reasons someone installs this are not obvious from a
|
|
669
|
+
# subcommand list.
|
|
670
|
+
print(LANDING)
|
|
671
|
+
return 0
|
|
580
672
|
# Long-running commands are a live log; keep them readable when piped.
|
|
581
673
|
try:
|
|
582
674
|
sys.stdout.reconfigure(line_buffering=True)
|
|
@@ -73,9 +73,19 @@ def normalise(im, width=PANEL_WIDTH):
|
|
|
73
73
|
|
|
74
74
|
|
|
75
75
|
def diff_stats(base, head):
|
|
76
|
-
"""Return (changed_pixel_count, total, mask) ignoring sub-threshold noise.
|
|
77
|
-
|
|
78
|
-
|
|
76
|
+
"""Return (changed_pixel_count, total, mask) ignoring sub-threshold noise.
|
|
77
|
+
|
|
78
|
+
Thresholds the per-channel *maximum* difference, not the luminance of the
|
|
79
|
+
difference. Luminance averages the channels, so a chroma-only shift -- a
|
|
80
|
+
toolbar tinted from blue-grey to peach, say -- can slide under the
|
|
81
|
+
threshold while being plainly visible. Recolouring is exactly the kind of
|
|
82
|
+
change a theme differ exists to catch.
|
|
83
|
+
"""
|
|
84
|
+
delta = ImageChops.difference(base, head)
|
|
85
|
+
channel_max = delta.split()[0]
|
|
86
|
+
for channel in delta.split()[1:]:
|
|
87
|
+
channel_max = ImageChops.lighter(channel_max, channel)
|
|
88
|
+
mask = channel_max.point(lambda v: 255 if v >= NOISE_THRESHOLD else 0)
|
|
79
89
|
changed = mask.histogram()[255]
|
|
80
90
|
return changed, base.width * base.height, mask
|
|
81
91
|
|
|
@@ -634,21 +634,19 @@ return true;
|
|
|
634
634
|
LOAD_VARIANT_SHEET = """
|
|
635
635
|
const win = Services.wm.getMostRecentWindow("navigator:browser");
|
|
636
636
|
const u = win.windowUtils;
|
|
637
|
-
if (win.
|
|
638
|
-
try { u.removeSheetUsingURIString(win._fxcssVariantSheet, u.USER_SHEET); } catch (e) {}
|
|
639
|
-
}
|
|
637
|
+
if (!win._fxcssVariantSheets) { win._fxcssVariantSheets = []; }
|
|
640
638
|
u.loadSheetUsingURIString(arguments[0], u.USER_SHEET);
|
|
641
|
-
win.
|
|
642
|
-
return
|
|
639
|
+
win._fxcssVariantSheets.push(arguments[0]);
|
|
640
|
+
return win._fxcssVariantSheets.length;
|
|
643
641
|
"""
|
|
644
642
|
|
|
645
|
-
|
|
643
|
+
UNLOAD_VARIANT_SHEETS = """
|
|
646
644
|
const win = Services.wm.getMostRecentWindow("navigator:browser");
|
|
647
645
|
const u = win.windowUtils;
|
|
648
|
-
|
|
649
|
-
try { u.removeSheetUsingURIString(
|
|
650
|
-
win._fxcssVariantSheet = null;
|
|
646
|
+
for (const uri of (win._fxcssVariantSheets || [])) {
|
|
647
|
+
try { u.removeSheetUsingURIString(uri, u.USER_SHEET); } catch (e) {}
|
|
651
648
|
}
|
|
649
|
+
win._fxcssVariantSheets = [];
|
|
652
650
|
return true;
|
|
653
651
|
"""
|
|
654
652
|
|
|
@@ -981,11 +979,14 @@ def capture_views(session: Session, outdir: Path, modes=("light", "dark"),
|
|
|
981
979
|
# Optional stylesheets, one capture per variant. Loaded live as a user
|
|
982
980
|
# sheet by file URI -- relative url()s inside the sheet keep resolving --
|
|
983
981
|
# then removed, so variants never contaminate each other.
|
|
984
|
-
for slug,
|
|
985
|
-
|
|
982
|
+
for slug, sheets in sorted((variants or {}).items()):
|
|
983
|
+
# A value may be one sheet or several: a combo loads them together, the
|
|
984
|
+
# way a user stacking install options would run them.
|
|
985
|
+
for sheet in ([sheets] if isinstance(sheets, Path) else sheets):
|
|
986
|
+
m.script(LOAD_VARIANT_SHEET, [sheet.resolve().as_uri()])
|
|
986
987
|
time.sleep(1.5)
|
|
987
988
|
_shot(m, outdir, "variant-" + slug)
|
|
988
|
-
m.script(
|
|
989
|
+
m.script(UNLOAD_VARIANT_SHEETS)
|
|
989
990
|
time.sleep(0.8)
|
|
990
991
|
|
|
991
992
|
(outdir / "render-info.json").write_text(json.dumps(info, indent=2), encoding="utf-8")
|
|
@@ -1069,6 +1070,39 @@ def find_variant_sheets(theme: Path):
|
|
|
1069
1070
|
return sheets
|
|
1070
1071
|
|
|
1071
1072
|
|
|
1073
|
+
def parse_variant_spec(spec, available):
|
|
1074
|
+
"""Turn a --variants value into {slug: [sheet, ...]}.
|
|
1075
|
+
|
|
1076
|
+
Commas separate captures; a plus combines sheets within one capture, the
|
|
1077
|
+
way a user stacking install options would run them:
|
|
1078
|
+
|
|
1079
|
+
all every optional sheet, one capture each
|
|
1080
|
+
a,b two captures
|
|
1081
|
+
a+b,c a and b together, then c alone
|
|
1082
|
+
|
|
1083
|
+
Raises ValueError naming anything unknown, listing what exists.
|
|
1084
|
+
"""
|
|
1085
|
+
if not spec:
|
|
1086
|
+
return {}
|
|
1087
|
+
spec = spec.strip()
|
|
1088
|
+
if spec.lower() == "all":
|
|
1089
|
+
return {slug: [path] for slug, path in available.items()}
|
|
1090
|
+
chosen = {}
|
|
1091
|
+
unknown = []
|
|
1092
|
+
for part in (p.strip().lower() for p in spec.split(",") if p.strip()):
|
|
1093
|
+
names = [n.strip() for n in part.split("+") if n.strip()]
|
|
1094
|
+
missing = [n for n in names if n not in available]
|
|
1095
|
+
if missing:
|
|
1096
|
+
unknown += missing
|
|
1097
|
+
continue
|
|
1098
|
+
chosen["+".join(names)] = [available[n] for n in names]
|
|
1099
|
+
if unknown:
|
|
1100
|
+
raise ValueError(
|
|
1101
|
+
"no optional stylesheet named " + ", ".join(sorted(set(unknown)))
|
|
1102
|
+
+ "; available: " + (", ".join(sorted(available)) or "none"))
|
|
1103
|
+
return chosen
|
|
1104
|
+
|
|
1105
|
+
|
|
1072
1106
|
def find_firefox(explicit=None):
|
|
1073
1107
|
"""Locate a Firefox binary, preferring an explicit path.
|
|
1074
1108
|
|