pandorga 1.6.7 → 1.6.8
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +34 -0
- data/README.md +3 -2
- data/_includes/page/content-runtime.html +3 -0
- data/_includes/page/network-graph.html +5 -2
- data/_includes/page/timeline-section.html +13 -1
- data/_includes/theme/favicons.html +3 -1
- data/_layouts/articles.html +5 -2
- data/_layouts/bibliography.html +5 -2
- data/_layouts/blog.html +5 -2
- data/_layouts/cv.html +5 -2
- data/_layouts/home.html +10 -3
- data/_layouts/projects.html +5 -2
- data/_layouts/resources.html +5 -2
- data/_layouts/shell.html +6 -1
- data/assets/css/ledger/home.css +20 -0
- data/assets/css/ledger/rascunhos.css +40 -2
- data/assets/icons/apple-touch-icon.png +0 -0
- data/assets/icons/favicon-32.png +0 -0
- data/assets/icons/favicon.ico +0 -0
- data/assets/icons/favicon.svg +23 -0
- data/assets/icons/icon-192.png +0 -0
- data/assets/icons/icon-32.png +0 -0
- data/assets/icons/icon-512.png +0 -0
- data/assets/icons/icon-maskable-192.png +0 -0
- data/assets/icons/icon-maskable-512.png +0 -0
- data/assets/icons/studio-apple-touch-icon.png +0 -0
- data/assets/icons/studio-icon-192.png +0 -0
- data/assets/icons/studio-icon-512.png +0 -0
- data/assets/icons/studio-logo-32.png +0 -0
- data/assets/icons/studio-logo.ico +0 -0
- data/assets/icons/studio-logo.svg +25 -5
- data/assets/icons/studio-maskable-192.png +0 -0
- data/assets/icons/studio-maskable-512.png +0 -0
- data/lib/pandorga/jekyll/plugins/web_manifest.rb +81 -0
- data/lib/pandorga/version.rb +1 -1
- data/scripts/check_home_lists.mjs +209 -0
- data/scripts/lighthouse-home.sh +6 -0
- data/scripts/render-pwa-icons.py +141 -0
- data/scripts/test/test-home-articles.rb +30 -0
- data/scripts/test/test-home-lists.rb +74 -0
- data/scripts/test/test-pwa-icons.rb +280 -0
- data/scripts/test/test-rascunhos-licence.rb +43 -0
- data/scripts/validate.sh +4 -0
- data/studio/index.html +4 -1
- data/studio-app/index.html +4 -1
- metadata +24 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 355770a826a6483d7fbc1e014c76fc0ef865d23bba44674cc2153d25ed840a9c
|
|
4
|
+
data.tar.gz: 6fd8451064d1a3bc58a8ff4ed53b90606183644756c28984018ff20ae485a543
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 331cf7a4687f241ee1b653e2435986bd511536ac3d1bfd21b65c91deef8969994999d8d65696e54a5725e7be41169d10ff9abac70475844a018480869cb19c1c
|
|
7
|
+
data.tar.gz: 88fc4c23d1cb5680b054c2e7028e374caab861977114ae235de52547b482de74b0c282957fc5bdf31764c9c3f11e39a65bbd4372ee7ee8fbf397b238b41f1c66
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,39 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.6.8] — 2026-10-10
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- Home bands and the other listing pages fill again. Their scripts sit in
|
|
8
|
+
the page and used to run before the runtime existed, then return. The
|
|
9
|
+
titles stayed in the HTML and the lists stayed empty, on the phone and
|
|
10
|
+
on the desktop. Lighthouse scored that empty document. Those scripts
|
|
11
|
+
now start when the runtime is assigned. The runtime stays after the
|
|
12
|
+
page. The static hero summary, the deferred text faces, the deferred
|
|
13
|
+
markdown library, and the named icon subset stay as they are.
|
|
14
|
+
- CI loads the built example home and fails when the projects, articles,
|
|
15
|
+
or posts band has no items after load, on a phone viewport and on a
|
|
16
|
+
desktop viewport.
|
|
17
|
+
- On a phone, the featured card in the home Articles band has more space
|
|
18
|
+
above its text. The other article cards keep their padding. Article
|
|
19
|
+
titles in that band are 2pt larger.
|
|
20
|
+
- On the Rascunhos page, the licence sits under the entries again below
|
|
21
|
+
1024px. The per-page stylesheet split had stopped loading the sheet
|
|
22
|
+
that ordered it, and the page's own `display: block` put it back on
|
|
23
|
+
top. The order now lives on the sheet the blog layout always loads.
|
|
24
|
+
|
|
25
|
+
### Changed
|
|
26
|
+
|
|
27
|
+
- The site and Studio launcher icons are the same stamp, on the same
|
|
28
|
+
plate, in the same pastel triad. The stamp is the same size on both,
|
|
29
|
+
with the same padding, at 512, 192, 180, and 32. Studio adds a small
|
|
30
|
+
badge inside that stamp. The build writes `/site.webmanifest` and
|
|
31
|
+
`/studio.webmanifest` with maskable icons, so Android masks both to
|
|
32
|
+
one shape and one visual size. The shell links the site manifest.
|
|
33
|
+
Studio links the studio manifest.
|
|
34
|
+
|
|
35
|
+
No `pandorga:` config keys were added or renamed.
|
|
36
|
+
|
|
3
37
|
## [1.6.7] — 2026-10-10
|
|
4
38
|
|
|
5
39
|
### Fixed
|
data/README.md
CHANGED
|
@@ -85,8 +85,9 @@ and an urgent performance campaign must then bring it back to 90 or above.
|
|
|
85
85
|
Below 75 is never acceptable for a release. A pull request or a merge may
|
|
86
86
|
score lower when that is unavoidable to keep the work modular. The release
|
|
87
87
|
build publishes at 90 or above. Notes that include `LIGHTHOUSE_BUGFIX=1`
|
|
88
|
-
may publish a bug fix from 75 to 89. Pull requests and merges only warn
|
|
89
|
-
The
|
|
88
|
+
may publish a bug fix from 75 to 89. Pull requests and merges only warn
|
|
89
|
+
on the score. The same run fails when the loaded example home has no
|
|
90
|
+
project or article items. The checklist is in [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
90
91
|
|
|
91
92
|
## License
|
|
92
93
|
|
|
@@ -17,4 +17,7 @@
|
|
|
17
17
|
{% include content-runtime/43-blocks.html %}
|
|
18
18
|
{% include content-runtime/90-bootstrap.html %}
|
|
19
19
|
})();
|
|
20
|
+
/* Band and listing scripts sit in the page, so they run before this
|
|
21
|
+
assignment. They listen for the event and start once the runtime exists. */
|
|
22
|
+
document.dispatchEvent(new CustomEvent('pandorga:runtime'));
|
|
20
23
|
</script>
|
|
@@ -454,11 +454,14 @@
|
|
|
454
454
|
|
|
455
455
|
<script src="https://cdn.jsdelivr.net/npm/d3@7.9.0/dist/d3.min.js"></script>
|
|
456
456
|
<script>
|
|
457
|
-
(function () {
|
|
457
|
+
(function bootNetwork() {
|
|
458
458
|
'use strict';
|
|
459
459
|
|
|
460
460
|
var runtime = window.ContentRuntime;
|
|
461
|
-
if (!runtime ||
|
|
461
|
+
if (!runtime || typeof runtime.loadJson !== 'function') {
|
|
462
|
+
document.addEventListener('pandorga:runtime', bootNetwork, { once: true });
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
462
465
|
|
|
463
466
|
var params = new URLSearchParams(window.location.search);
|
|
464
467
|
var filterRoot = document.querySelector('[data-listing-filter-root]');
|
|
@@ -852,14 +852,26 @@
|
|
|
852
852
|
var runtime = window.ContentRuntime;
|
|
853
853
|
/* The content base is empty when the JSON is served from the site root
|
|
854
854
|
(local preview), which is a valid source, not a missing one — gate on
|
|
855
|
-
the loader instead, and let the catch fall back to the inline stub.
|
|
855
|
+
the loader instead, and let the catch fall back to the inline stub.
|
|
856
|
+
The runtime script follows the page, so the first call waits for it. */
|
|
856
857
|
if (runtime && typeof runtime.loadJson === 'function') {
|
|
858
|
+
loadTimeline.started = true;
|
|
857
859
|
runtime.loadJson('data/timeline.json').then(bootTimeline).catch(function (error) {
|
|
858
860
|
console.error(error);
|
|
859
861
|
if (el) bootTimeline(JSON.parse(el.textContent));
|
|
860
862
|
});
|
|
861
863
|
return;
|
|
862
864
|
}
|
|
865
|
+
if (!loadTimeline.armed) {
|
|
866
|
+
loadTimeline.armed = true;
|
|
867
|
+
document.addEventListener('pandorga:runtime', loadTimeline, { once: true });
|
|
868
|
+
window.setTimeout(function () {
|
|
869
|
+
if (loadTimeline.started || !el) return;
|
|
870
|
+
loadTimeline.started = true;
|
|
871
|
+
bootTimeline(JSON.parse(el.textContent));
|
|
872
|
+
}, 0);
|
|
873
|
+
return;
|
|
874
|
+
}
|
|
863
875
|
if (el) bootTimeline(JSON.parse(el.textContent));
|
|
864
876
|
}
|
|
865
877
|
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
<link rel="icon" href="/assets/icons/favicon.svg" type="image/svg+xml" sizes="any" />
|
|
2
2
|
<link rel="icon" href="/assets/icons/favicon-32.png" type="image/png" sizes="32x32" />
|
|
3
|
-
<link rel="apple-touch-icon" href="/assets/icons/apple-touch-icon.png" />
|
|
3
|
+
<link rel="apple-touch-icon" href="/assets/icons/apple-touch-icon.png" sizes="180x180" />
|
|
4
4
|
<link rel="icon" href="/favicon.ico" sizes="16x16 32x32" />
|
|
5
|
+
<link rel="manifest" href="/site.webmanifest" />
|
|
6
|
+
<meta name="theme-color" content="#5e5d59" />
|
data/_layouts/articles.html
CHANGED
|
@@ -129,11 +129,14 @@ layout: shell
|
|
|
129
129
|
{% include mathjax.html %}
|
|
130
130
|
|
|
131
131
|
<script>
|
|
132
|
-
(function () {
|
|
132
|
+
(function bootArticles() {
|
|
133
133
|
'use strict';
|
|
134
134
|
|
|
135
135
|
var runtime = window.ContentRuntime;
|
|
136
|
-
if (!runtime ||
|
|
136
|
+
if (!runtime || typeof runtime.loadJson !== 'function') {
|
|
137
|
+
document.addEventListener('pandorga:runtime', bootArticles, { once: true });
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
137
140
|
|
|
138
141
|
var configRoot = document.querySelector('[data-listing-collection]');
|
|
139
142
|
var collection = (configRoot && configRoot.getAttribute('data-listing-collection')) || 'articles';
|
data/_layouts/bibliography.html
CHANGED
|
@@ -24,11 +24,14 @@ layout: shell
|
|
|
24
24
|
</main>
|
|
25
25
|
|
|
26
26
|
<script>
|
|
27
|
-
(function () {
|
|
27
|
+
(function bootBibliography() {
|
|
28
28
|
'use strict';
|
|
29
29
|
|
|
30
30
|
var runtime = window.ContentRuntime;
|
|
31
|
-
if (!runtime ||
|
|
31
|
+
if (!runtime || typeof runtime.loadJson !== 'function') {
|
|
32
|
+
document.addEventListener('pandorga:runtime', bootBibliography, { once: true });
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
32
35
|
|
|
33
36
|
var listing = runtime.Listing;
|
|
34
37
|
var params = new URLSearchParams(window.location.search);
|
data/_layouts/blog.html
CHANGED
|
@@ -93,11 +93,14 @@ layout: shell
|
|
|
93
93
|
{% include mathjax.html %}
|
|
94
94
|
|
|
95
95
|
<script>
|
|
96
|
-
(function () {
|
|
96
|
+
(function bootBlog() {
|
|
97
97
|
'use strict';
|
|
98
98
|
|
|
99
99
|
var runtime = window.ContentRuntime;
|
|
100
|
-
if (!runtime ||
|
|
100
|
+
if (!runtime || typeof runtime.loadJson !== 'function') {
|
|
101
|
+
document.addEventListener('pandorga:runtime', bootBlog, { once: true });
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
101
104
|
|
|
102
105
|
var configRoot = document.querySelector('[data-listing-collection]');
|
|
103
106
|
var collection = (configRoot && configRoot.getAttribute('data-listing-collection')) || 'posts';
|
data/_layouts/cv.html
CHANGED
|
@@ -80,11 +80,14 @@ layout: shell
|
|
|
80
80
|
</main>
|
|
81
81
|
|
|
82
82
|
<script>
|
|
83
|
-
(function () {
|
|
83
|
+
(function bootCv() {
|
|
84
84
|
'use strict';
|
|
85
85
|
|
|
86
86
|
var runtime = window.ContentRuntime;
|
|
87
|
-
if (!runtime)
|
|
87
|
+
if (!runtime || typeof runtime.loadJson !== 'function') {
|
|
88
|
+
document.addEventListener('pandorga:runtime', bootCv, { once: true });
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
88
91
|
|
|
89
92
|
var experienceList = document.getElementById('cv-experience-list');
|
|
90
93
|
var educationList = document.getElementById('cv-education-list');
|
data/_layouts/home.html
CHANGED
|
@@ -63,8 +63,12 @@ layout: shell
|
|
|
63
63
|
</div>
|
|
64
64
|
</section>
|
|
65
65
|
<script>
|
|
66
|
-
(function () {
|
|
66
|
+
(function bootHomePaint() {
|
|
67
67
|
var runtime = window.ContentRuntime;
|
|
68
|
+
if (!runtime || typeof runtime.renderMarkdown !== 'function') {
|
|
69
|
+
document.addEventListener('pandorga:runtime', bootHomePaint, { once: true });
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
68
72
|
var pre = window.__pandorgaPreload || {};
|
|
69
73
|
function paint(id, path) {
|
|
70
74
|
var el = document.getElementById(id);
|
|
@@ -95,11 +99,14 @@ layout: shell
|
|
|
95
99
|
|
|
96
100
|
<script>
|
|
97
101
|
{% raw %}
|
|
98
|
-
(function () {
|
|
102
|
+
(function bootHomeBands() {
|
|
99
103
|
'use strict';
|
|
100
104
|
|
|
101
105
|
var runtime = window.ContentRuntime;
|
|
102
|
-
if (!runtime)
|
|
106
|
+
if (!runtime || typeof runtime.loadJson !== 'function') {
|
|
107
|
+
document.addEventListener('pandorga:runtime', bootHomeBands, { once: true });
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
103
110
|
|
|
104
111
|
function asItem(entry) {
|
|
105
112
|
return {
|
data/_layouts/projects.html
CHANGED
|
@@ -59,11 +59,14 @@ layout: shell
|
|
|
59
59
|
</main>
|
|
60
60
|
|
|
61
61
|
<script>
|
|
62
|
-
(function () {
|
|
62
|
+
(function bootProjects() {
|
|
63
63
|
'use strict';
|
|
64
64
|
|
|
65
65
|
var runtime = window.ContentRuntime;
|
|
66
|
-
if (!runtime)
|
|
66
|
+
if (!runtime || typeof runtime.loadJson !== 'function') {
|
|
67
|
+
document.addEventListener('pandorga:runtime', bootProjects, { once: true });
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
67
70
|
|
|
68
71
|
var esc = runtime.escapeHtml;
|
|
69
72
|
var escAttr = runtime.escapeAttr;
|
data/_layouts/resources.html
CHANGED
|
@@ -34,11 +34,14 @@ layout: shell
|
|
|
34
34
|
</main>
|
|
35
35
|
|
|
36
36
|
<script>
|
|
37
|
-
(function () {
|
|
37
|
+
(function bootResources() {
|
|
38
38
|
'use strict';
|
|
39
39
|
|
|
40
40
|
var runtime = window.ContentRuntime;
|
|
41
|
-
if (!runtime ||
|
|
41
|
+
if (!runtime || typeof runtime.loadJson !== 'function') {
|
|
42
|
+
document.addEventListener('pandorga:runtime', bootResources, { once: true });
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
42
45
|
|
|
43
46
|
var configRoot = document.querySelector('[data-listing-collection]');
|
|
44
47
|
var collection = (configRoot && configRoot.getAttribute('data-listing-collection')) || 'resources';
|
data/_layouts/shell.html
CHANGED
|
@@ -85,7 +85,12 @@
|
|
|
85
85
|
{%- assign _ledger = "writing|detail" | split: "|" -%}
|
|
86
86
|
{%- assign _article_css = true -%}
|
|
87
87
|
{%- when "blog" -%}
|
|
88
|
-
{%-
|
|
88
|
+
{%- comment -%}
|
|
89
|
+
writing.css holds the rail, and it has to follow rascunhos.css. The
|
|
90
|
+
body rule in rascunhos.css is `display: block`, which would put the
|
|
91
|
+
licence back above the entries if it won the cascade.
|
|
92
|
+
{%- endcomment -%}
|
|
93
|
+
{%- assign _ledger = "rascunhos|writing|detail" | split: "|" -%}
|
|
89
94
|
{%- assign _article_css = true -%}
|
|
90
95
|
{%- when "cv" -%}
|
|
91
96
|
{%- assign _ledger = "cv|detail" | split: "|" -%}
|
data/assets/css/ledger/home.css
CHANGED
|
@@ -740,6 +740,26 @@ html[data-theme="light"] .home-hero-plate {
|
|
|
740
740
|
line-height: var(--home-featured-title-line);
|
|
741
741
|
}
|
|
742
742
|
|
|
743
|
+
/* Articles band only. Two points over the size the title already uses,
|
|
744
|
+
the same point step as the phone hero name. The section heading is
|
|
745
|
+
untouched. Listed cards in other bands are untouched. */
|
|
746
|
+
[data-home-kind="articles"] .home-featured-title {
|
|
747
|
+
font-size: calc(var(--home-featured-title-size) + 2pt);
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
[data-home-kind="articles"] .home-card-grid .card-title,
|
|
751
|
+
[data-home-kind="articles"] .home-card-grid .ledger-card-title {
|
|
752
|
+
font-size: calc(var(--type-card) + 2pt);
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
/* Phone only: the featured card's text sat against the top rule. One
|
|
756
|
+
step more than the shared top padding, and only on this card. */
|
|
757
|
+
@media (max-width: 767px) {
|
|
758
|
+
[data-home-kind="articles"] .home-featured {
|
|
759
|
+
padding-top: calc(var(--space-md) + 0.75rem);
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
|
|
743
763
|
/* Home article / rascunho / project cards — content-title voice, not display. */
|
|
744
764
|
.home-card-grid .ledger-card-title,
|
|
745
765
|
.home-card-grid .card-title,
|
|
@@ -66,8 +66,46 @@
|
|
|
66
66
|
display: block;
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
/*
|
|
70
|
-
.
|
|
69
|
+
/* Below 1024px the rail is a disclosure above the ledger, and the licence
|
|
70
|
+
is a child of that rail, so source order paints it above the texts.
|
|
71
|
+
`writing.css` already sets this column, but the per-page split dropped
|
|
72
|
+
that sheet from the blog layout, and this `display: block` beats the
|
|
73
|
+
flex rule whenever rascunhos.css is the later file. The selector here is
|
|
74
|
+
more specific than that block display, so the licence stays under the
|
|
75
|
+
entries even if the sheet list changes again. */
|
|
76
|
+
@media (max-width: 1023px) {
|
|
77
|
+
.rascunhos-main .rascunhos-body {
|
|
78
|
+
display: flex;
|
|
79
|
+
flex-direction: column;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.rascunhos-main .rascunhos-body>.writing-rail:not([hidden]) {
|
|
83
|
+
display: contents;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.rascunhos-main .rascunhos-body>.rascunhos-ledger {
|
|
87
|
+
order: 1;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.rascunhos-main .writing-rail-licence {
|
|
91
|
+
order: 2;
|
|
92
|
+
margin-top: var(--space-xl);
|
|
93
|
+
padding-top: var(--space-lg);
|
|
94
|
+
border-top: 1px solid var(--border-gentle-subtle);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.rascunhos-main .writing-rail-panel {
|
|
98
|
+
margin-bottom: var(--space-xl);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.rascunhos-main .writing-rail:has(> .writing-rail-panel[hidden]) .writing-rail-trigger {
|
|
102
|
+
margin-bottom: var(--space-xl);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/* `.hidden` swaps the listing for the detail view; it has to beat the
|
|
107
|
+
mobile flex column above, which is why the page class is on the selector. */
|
|
108
|
+
.rascunhos-main .rascunhos-body.hidden {
|
|
71
109
|
display: none;
|
|
72
110
|
}
|
|
73
111
|
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" role="img" aria-label="Site">
|
|
2
|
+
<title>Site</title>
|
|
3
|
+
<style>
|
|
4
|
+
/* Light plate: pastel triad on the dark frame. Dark plate: production triad
|
|
5
|
+
on the light frame. The mark stays inside the maskable safe zone. */
|
|
6
|
+
.primary { fill: #a4c8da; }
|
|
7
|
+
.secondary { fill: #95c8a6; }
|
|
8
|
+
.tertiary { fill: #ddd8a8; }
|
|
9
|
+
.plate { fill: #5e5d59; }
|
|
10
|
+
@media (prefers-color-scheme: dark) {
|
|
11
|
+
.primary { fill: #1c5a7a; }
|
|
12
|
+
.secondary { fill: #287445; }
|
|
13
|
+
.tertiary { fill: #746f30; }
|
|
14
|
+
.plate { fill: #b8b7ae; }
|
|
15
|
+
}
|
|
16
|
+
</style>
|
|
17
|
+
<rect class="plate" width="32" height="32"/>
|
|
18
|
+
<g transform="translate(7.68 7.68) scale(0.52)">
|
|
19
|
+
<polygon class="primary" points="0,0 23,0 15,17 0,20"/>
|
|
20
|
+
<polygon class="secondary" points="23,0 32,0 32,32 17,21 15,17"/>
|
|
21
|
+
<polygon class="tertiary" points="0,20 15,17 17,21 32,32 0,32"/>
|
|
22
|
+
</g>
|
|
23
|
+
</svg>
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -1,6 +1,26 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" role="img" aria-
|
|
2
|
-
<
|
|
3
|
-
<
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" role="img" aria-label="Studio">
|
|
2
|
+
<title>Studio</title>
|
|
3
|
+
<style>
|
|
4
|
+
/* Same stamp and plate as the site icon. The small badge is the only
|
|
5
|
+
difference. It stays inside the maskable safe zone. */
|
|
6
|
+
.primary { fill: #a4c8da; }
|
|
7
|
+
.secondary { fill: #95c8a6; }
|
|
8
|
+
.tertiary { fill: #ddd8a8; }
|
|
9
|
+
.plate { fill: #5e5d59; }
|
|
10
|
+
.badge { fill: #b8b7ae; }
|
|
11
|
+
@media (prefers-color-scheme: dark) {
|
|
12
|
+
.primary { fill: #1c5a7a; }
|
|
13
|
+
.secondary { fill: #287445; }
|
|
14
|
+
.tertiary { fill: #746f30; }
|
|
15
|
+
.plate { fill: #b8b7ae; }
|
|
16
|
+
.badge { fill: #5e5d59; }
|
|
17
|
+
}
|
|
18
|
+
</style>
|
|
19
|
+
<rect class="plate" width="32" height="32"/>
|
|
20
|
+
<g transform="translate(7.68 7.68) scale(0.52)">
|
|
21
|
+
<polygon class="primary" points="0,0 23,0 15,17 0,20"/>
|
|
22
|
+
<polygon class="secondary" points="23,0 32,0 32,32 17,21 15,17"/>
|
|
23
|
+
<polygon class="tertiary" points="0,20 15,17 17,21 32,32 0,32"/>
|
|
24
|
+
</g>
|
|
25
|
+
<rect class="badge" x="20.32" y="20.32" width="2.88" height="2.88"/>
|
|
6
26
|
</svg>
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "jekyll"
|
|
4
|
+
require "json"
|
|
5
|
+
|
|
6
|
+
module Pandorga
|
|
7
|
+
module Jekyll
|
|
8
|
+
# Writes the web app manifests the shell and Studio link. The icons are
|
|
9
|
+
# theme assets. A site file at the same path overrides an icon; the
|
|
10
|
+
# manifest itself is generated so the name follows the site identity.
|
|
11
|
+
class WebManifestGenerator < ::Jekyll::Generator
|
|
12
|
+
safe true
|
|
13
|
+
priority :low
|
|
14
|
+
|
|
15
|
+
def generate(site)
|
|
16
|
+
emit(site, "site.webmanifest", site_manifest(site))
|
|
17
|
+
emit(site, "studio.webmanifest", studio_manifest)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def emit(site, name, payload)
|
|
23
|
+
page = ::Jekyll::PageWithoutAFile.new(site, site.source, "", name)
|
|
24
|
+
page.data["layout"] = nil
|
|
25
|
+
page.data["sitemap"] = false
|
|
26
|
+
page.content = "#{JSON.pretty_generate(payload)}\n"
|
|
27
|
+
site.pages << page
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def site_manifest(site)
|
|
31
|
+
config = site.config
|
|
32
|
+
pandorga = config["pandorga"].is_a?(Hash) ? config["pandorga"] : {}
|
|
33
|
+
identity = pandorga["identity"].is_a?(Hash) ? pandorga["identity"] : {}
|
|
34
|
+
name = identity["name"].to_s.strip
|
|
35
|
+
name = config["title"].to_s.strip if name.empty?
|
|
36
|
+
name = "Site" if name.empty?
|
|
37
|
+
short = identity["short_name"].to_s.strip
|
|
38
|
+
short = name if short.empty?
|
|
39
|
+
manifest("/", name, short, studio: false)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def studio_manifest
|
|
43
|
+
manifest("/studio/", "Studio", "Studio", studio: true)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def manifest(start, name, short, studio:)
|
|
47
|
+
{
|
|
48
|
+
"id" => start,
|
|
49
|
+
"name" => name,
|
|
50
|
+
"short_name" => short,
|
|
51
|
+
"start_url" => start,
|
|
52
|
+
"scope" => start,
|
|
53
|
+
"display" => "standalone",
|
|
54
|
+
"background_color" => "#5e5d59",
|
|
55
|
+
"theme_color" => "#5e5d59",
|
|
56
|
+
"icons" => icons(studio: studio)
|
|
57
|
+
}
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Stems match scripts/render-pwa-icons.py. The site maskable file is
|
|
61
|
+
# icon-maskable-*.png. Studio's is studio-maskable-*.png.
|
|
62
|
+
def icons(studio:)
|
|
63
|
+
any_stem = studio ? "studio-icon" : "icon"
|
|
64
|
+
mask_stem = studio ? "studio-maskable" : "icon-maskable"
|
|
65
|
+
[
|
|
66
|
+
[512, any_stem, "any"],
|
|
67
|
+
[512, mask_stem, "maskable"],
|
|
68
|
+
[192, any_stem, "any"],
|
|
69
|
+
[192, mask_stem, "maskable"]
|
|
70
|
+
].map do |size, stem, purpose|
|
|
71
|
+
{
|
|
72
|
+
"src" => "/assets/icons/#{stem}-#{size}.png",
|
|
73
|
+
"sizes" => "#{size}x#{size}",
|
|
74
|
+
"type" => "image/png",
|
|
75
|
+
"purpose" => purpose
|
|
76
|
+
}
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
data/lib/pandorga/version.rb
CHANGED