pandorga 1.6.7 → 1.6.9
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 +45 -0
- data/README.md +3 -2
- data/_includes/content-runtime/40-listing.html +13 -5
- data/_includes/content-runtime/60-fragments.html +36 -10
- 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 +54 -18
- 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 +237 -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 +83 -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: 110f9fdf3636db9ef496093f969e15ef4cd3322ee143c5efadb210fb5e4b98b9
|
|
4
|
+
data.tar.gz: 6c5625b1cbdac652c0120d2b25e49a722c59941862cf91da7f44eff6f1ada004
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 22f67174268b9edd80046d916e6d8394d4ce1df62e8802be7dc4eab8771479825c1ba8b3aa2f668ebef53bc95f116f840ddd300428362b938a19b9955d0914d8
|
|
7
|
+
data.tar.gz: 85ce7605aae925b73d384a9ddbc0269b5b31bb58652b05b60617c5b784f8cfc2c68dc92cc3824ed07bf016f305aee36c5fba17a51c891ecce06cd58a0c3f94d0
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,50 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.6.9] — 2026-10-10
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- Home list hydration no longer fills every band in one turn. The project
|
|
8
|
+
specimen measurement yields between projects, each band starts on its
|
|
9
|
+
own turn, and each home section introduction yields before it is
|
|
10
|
+
parsed. Dates reuse one formatter. The lists still render.
|
|
11
|
+
|
|
12
|
+
No `pandorga:` config keys were added or renamed.
|
|
13
|
+
|
|
14
|
+
## [1.6.8] — 2026-10-10
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
|
|
18
|
+
- Home bands and the other listing pages fill again. Their scripts sit in
|
|
19
|
+
the page and used to run before the runtime existed, then return. The
|
|
20
|
+
titles stayed in the HTML and the lists stayed empty, on the phone and
|
|
21
|
+
on the desktop. Lighthouse scored that empty document. Those scripts
|
|
22
|
+
now start when the runtime is assigned. The runtime stays after the
|
|
23
|
+
page. The static hero summary, the deferred text faces, the deferred
|
|
24
|
+
markdown library, and the named icon subset stay as they are.
|
|
25
|
+
- CI loads the built example home and fails when the projects, articles,
|
|
26
|
+
or posts band has no items after load, on a phone viewport and on a
|
|
27
|
+
desktop viewport.
|
|
28
|
+
- On a phone, the featured card in the home Articles band has more space
|
|
29
|
+
above its text. The other article cards keep their padding. Article
|
|
30
|
+
titles in that band are 2pt larger.
|
|
31
|
+
- On the Rascunhos page, the licence sits under the entries again below
|
|
32
|
+
1024px. The per-page stylesheet split had stopped loading the sheet
|
|
33
|
+
that ordered it, and the page's own `display: block` put it back on
|
|
34
|
+
top. The order now lives on the sheet the blog layout always loads.
|
|
35
|
+
|
|
36
|
+
### Changed
|
|
37
|
+
|
|
38
|
+
- The site and Studio launcher icons are the same stamp, on the same
|
|
39
|
+
plate, in the same pastel triad. The stamp is the same size on both,
|
|
40
|
+
with the same padding, at 512, 192, 180, and 32. Studio adds a small
|
|
41
|
+
badge inside that stamp. The build writes `/site.webmanifest` and
|
|
42
|
+
`/studio.webmanifest` with maskable icons, so Android masks both to
|
|
43
|
+
one shape and one visual size. The shell links the site manifest.
|
|
44
|
+
Studio links the studio manifest.
|
|
45
|
+
|
|
46
|
+
No `pandorga:` config keys were added or renamed.
|
|
47
|
+
|
|
3
48
|
## [1.6.7] — 2026-10-10
|
|
4
49
|
|
|
5
50
|
### 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
|
|
|
@@ -113,15 +113,23 @@
|
|
|
113
113
|
return textExcerpt((item && item.body_markdown) || '');
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
+
var dateFormatters = Object.create(null);
|
|
117
|
+
|
|
116
118
|
function formatDate(value, locale) {
|
|
117
119
|
if (!value) return '';
|
|
118
120
|
var parts = String(value).slice(0, 10).split('-').map(Number);
|
|
119
121
|
if (parts.length !== 3 || parts.some(isNaN)) return value;
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
122
|
+
var key = locale || 'en';
|
|
123
|
+
var formatter = dateFormatters[key];
|
|
124
|
+
if (!formatter) {
|
|
125
|
+
formatter = new Intl.DateTimeFormat(key, {
|
|
126
|
+
day: '2-digit',
|
|
127
|
+
month: 'short',
|
|
128
|
+
year: 'numeric'
|
|
129
|
+
});
|
|
130
|
+
dateFormatters[key] = formatter;
|
|
131
|
+
}
|
|
132
|
+
return formatter.format(new Date(parts[0], parts[1] - 1, parts[2]));
|
|
125
133
|
}
|
|
126
134
|
|
|
127
135
|
function chronologySortKey(item) {
|
|
@@ -353,15 +353,31 @@
|
|
|
353
353
|
});
|
|
354
354
|
}
|
|
355
355
|
|
|
356
|
+
function yieldToMain() {
|
|
357
|
+
return new Promise(function (resolve) {
|
|
358
|
+
if (typeof scheduler !== 'undefined' && typeof scheduler.yield === 'function') {
|
|
359
|
+
scheduler.yield().then(resolve);
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
window.setTimeout(resolve, 0);
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
|
|
356
366
|
function hydrateHomeSections() {
|
|
357
367
|
var roots = Array.prototype.slice.call(
|
|
358
368
|
document.querySelectorAll('[data-content-home-section]')
|
|
359
369
|
);
|
|
360
370
|
if (!roots.length) return Promise.resolve();
|
|
361
371
|
return loadPageHeaders().then(function (sections) {
|
|
372
|
+
var chain = Promise.resolve();
|
|
362
373
|
roots.forEach(function (root) {
|
|
363
|
-
|
|
374
|
+
chain = chain.then(function () {
|
|
375
|
+
return yieldToMain().then(function () {
|
|
376
|
+
applyHomeSection(root, sections[root.getAttribute('data-content-home-section')]);
|
|
377
|
+
});
|
|
378
|
+
});
|
|
364
379
|
});
|
|
380
|
+
return chain;
|
|
365
381
|
}).catch(function (error) {
|
|
366
382
|
console.error(error);
|
|
367
383
|
roots.forEach(function (root) { applyHomeSection(root, null); });
|
|
@@ -605,21 +621,31 @@
|
|
|
605
621
|
}
|
|
606
622
|
|
|
607
623
|
function hydratePageFragments() {
|
|
608
|
-
document.querySelectorAll('[data-content-page-header]')
|
|
609
|
-
|
|
624
|
+
var headers = Array.prototype.slice.call(document.querySelectorAll('[data-content-page-header]'));
|
|
625
|
+
var chain = Promise.resolve();
|
|
626
|
+
headers.forEach(function (el) {
|
|
627
|
+
chain = chain.then(function () {
|
|
628
|
+
return yieldToMain().then(function () { return hydratePageHeader(el); });
|
|
629
|
+
});
|
|
610
630
|
});
|
|
611
631
|
document.querySelectorAll('[data-content-fragment]').forEach(function (el) {
|
|
612
632
|
var path = el.getAttribute('data-content-fragment');
|
|
613
633
|
if (!path) return;
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
634
|
+
chain = chain.then(function () {
|
|
635
|
+
return yieldToMain().then(function () {
|
|
636
|
+
return mountMarkdownFragment(path, el, {
|
|
637
|
+
loadingText: el.getAttribute('data-content-loading') || 'Loading...',
|
|
638
|
+
errorText: el.getAttribute('data-content-error') || 'Content could not be loaded right now.'
|
|
639
|
+
});
|
|
640
|
+
});
|
|
617
641
|
});
|
|
618
642
|
});
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
643
|
+
chain.then(function () {
|
|
644
|
+
hydrateHomeSections();
|
|
645
|
+
hydrateBandArt();
|
|
646
|
+
hydrateContacts();
|
|
647
|
+
hydrateHeroPortrait();
|
|
648
|
+
});
|
|
623
649
|
}
|
|
624
650
|
|
|
625
651
|
function schedulePageFragmentHydration() {
|
|
@@ -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 {
|
|
@@ -114,7 +121,21 @@ layout: shell
|
|
|
114
121
|
}
|
|
115
122
|
|
|
116
123
|
function byDateDesc(a, b) {
|
|
117
|
-
|
|
124
|
+
var left = String(b.date || '');
|
|
125
|
+
var right = String(a.date || '');
|
|
126
|
+
if (left > right) return 1;
|
|
127
|
+
if (left < right) return -1;
|
|
128
|
+
return 0;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function afterPaint() {
|
|
132
|
+
return new Promise(function (resolve) {
|
|
133
|
+
if (typeof scheduler !== 'undefined' && typeof scheduler.yield === 'function') {
|
|
134
|
+
scheduler.yield().then(resolve);
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
window.setTimeout(resolve, 0);
|
|
138
|
+
});
|
|
118
139
|
}
|
|
119
140
|
|
|
120
141
|
function tagHrefFrom(element) {
|
|
@@ -168,8 +189,13 @@ layout: shell
|
|
|
168
189
|
function draftFolios(posts) {
|
|
169
190
|
var folios = Object.create(null);
|
|
170
191
|
posts.slice().sort(function (a, b) {
|
|
171
|
-
var byDate = String(a.date || '').
|
|
172
|
-
|
|
192
|
+
var byDate = String(a.date || '') < String(b.date || '') ? -1 : (String(a.date || '') > String(b.date || '') ? 1 : 0);
|
|
193
|
+
if (byDate !== 0) return byDate;
|
|
194
|
+
var left = String(a.slug || '');
|
|
195
|
+
var right = String(b.slug || '');
|
|
196
|
+
if (left < right) return -1;
|
|
197
|
+
if (left > right) return 1;
|
|
198
|
+
return 0;
|
|
173
199
|
}).forEach(function (entry, index) {
|
|
174
200
|
folios[entry.slug] = index + 1;
|
|
175
201
|
});
|
|
@@ -214,7 +240,8 @@ layout: shell
|
|
|
214
240
|
element.appendChild(fragment);
|
|
215
241
|
}
|
|
216
242
|
|
|
217
|
-
function fillBand(section, index) {
|
|
243
|
+
async function fillBand(section, index) {
|
|
244
|
+
await afterPaint();
|
|
218
245
|
var kind = section.getAttribute('data-home-kind');
|
|
219
246
|
var collection = section.getAttribute('data-home-collection');
|
|
220
247
|
var limit = parseInt(section.getAttribute('data-home-limit') || '3', 10) || 3;
|
|
@@ -309,7 +336,7 @@ layout: shell
|
|
|
309
336
|
fitted(best);
|
|
310
337
|
}
|
|
311
338
|
|
|
312
|
-
function paintSpecimen(entry) {
|
|
339
|
+
async function paintSpecimen(entry) {
|
|
313
340
|
specimenSlot.innerHTML = '';
|
|
314
341
|
specimenSlot.appendChild(runtime.renderProjectSpecimen(asItem(entry), {
|
|
315
342
|
href: entry.url || runtime.projectUrl(entry.slug),
|
|
@@ -317,10 +344,11 @@ layout: shell
|
|
|
317
344
|
folio: positionOf(entry.slug) + 1,
|
|
318
345
|
openLabel: 'Open project'
|
|
319
346
|
}));
|
|
347
|
+
await afterPaint();
|
|
320
348
|
ellipsizeSpecimenCopy(specimenSlot);
|
|
321
349
|
}
|
|
322
350
|
|
|
323
|
-
function measureTallestSpecimen() {
|
|
351
|
+
async function measureTallestSpecimen() {
|
|
324
352
|
var bench = specimenSlot.closest('.home-projects-bench');
|
|
325
353
|
if (!bench) return 0;
|
|
326
354
|
var width = specimenSlot.getBoundingClientRect().width;
|
|
@@ -341,7 +369,8 @@ layout: shell
|
|
|
341
369
|
bench.appendChild(probe);
|
|
342
370
|
|
|
343
371
|
var max = 0;
|
|
344
|
-
portfolio.
|
|
372
|
+
for (var i = 0; i < portfolio.length; i += 1) {
|
|
373
|
+
var entry = portfolio[i];
|
|
345
374
|
probe.innerHTML = '';
|
|
346
375
|
var card = runtime.renderProjectSpecimen(asItem(entry), {
|
|
347
376
|
href: entry.url || runtime.projectUrl(entry.slug),
|
|
@@ -353,15 +382,16 @@ layout: shell
|
|
|
353
382
|
card.style.minHeight = '0';
|
|
354
383
|
probe.appendChild(card);
|
|
355
384
|
max = Math.max(max, Math.ceil(card.getBoundingClientRect().height));
|
|
356
|
-
|
|
385
|
+
await afterPaint();
|
|
386
|
+
}
|
|
357
387
|
probe.remove();
|
|
358
388
|
return max;
|
|
359
389
|
}
|
|
360
390
|
|
|
361
|
-
function lockSpecimenHeight() {
|
|
391
|
+
async function lockSpecimenHeight() {
|
|
362
392
|
var bench = specimenSlot.closest('.home-projects-bench');
|
|
363
393
|
if (!bench) return;
|
|
364
|
-
var tallest = measureTallestSpecimen();
|
|
394
|
+
var tallest = await measureTallestSpecimen();
|
|
365
395
|
/* Floor at the CSS inventory minimum so six rows always clear
|
|
366
396
|
three stacked resource icons (see --home-inventory-min-height). */
|
|
367
397
|
var inventoryMin = 0;
|
|
@@ -418,8 +448,9 @@ layout: shell
|
|
|
418
448
|
});
|
|
419
449
|
}
|
|
420
450
|
|
|
421
|
-
lockSpecimenHeight();
|
|
422
|
-
paintSpecimen(entryBySlug(selectedSlug));
|
|
451
|
+
await lockSpecimenHeight();
|
|
452
|
+
await paintSpecimen(entryBySlug(selectedSlug));
|
|
453
|
+
await afterPaint();
|
|
423
454
|
paintInventory();
|
|
424
455
|
|
|
425
456
|
var resizeTimer = null;
|
|
@@ -430,8 +461,9 @@ layout: shell
|
|
|
430
461
|
var nextWidth = Math.round(specimenSlot.getBoundingClientRect().width);
|
|
431
462
|
if (nextWidth === lastMeasureWidth) return;
|
|
432
463
|
lastMeasureWidth = nextWidth;
|
|
433
|
-
lockSpecimenHeight()
|
|
434
|
-
|
|
464
|
+
lockSpecimenHeight().then(function () {
|
|
465
|
+
return paintSpecimen(entryBySlug(selectedSlug));
|
|
466
|
+
});
|
|
435
467
|
}, 120);
|
|
436
468
|
});
|
|
437
469
|
return;
|
|
@@ -477,7 +509,11 @@ layout: shell
|
|
|
477
509
|
|
|
478
510
|
runtime.loadJson('data/writing_index.json').then(function (entries) {
|
|
479
511
|
var index = Array.isArray(entries) ? entries : [];
|
|
480
|
-
|
|
512
|
+
var chain = Promise.resolve();
|
|
513
|
+
sections.forEach(function (section) {
|
|
514
|
+
chain = chain.then(function () { return fillBand(section, index); });
|
|
515
|
+
});
|
|
516
|
+
return chain;
|
|
481
517
|
}).catch(function (error) {
|
|
482
518
|
console.error(error);
|
|
483
519
|
sections.forEach(function (section) {
|
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
|