pandorga 1.5.0 → 1.6.0
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 +51 -0
- data/README.md +1 -1
- data/_includes/chord-tab.html +7 -7
- data/_includes/content-runtime/41-listing-helpers.html +7 -0
- data/_includes/content-runtime/43-blocks.html +4 -3
- data/_includes/content-runtime/60-fragments.html +77 -11
- data/_includes/page/site-footer.html +3 -2
- data/_includes/theme/critical-preload.html +35 -0
- data/_includes/theme/json-ld.html +35 -0
- data/_includes/transcription.html +5 -5
- data/_layouts/articles.html +8 -2
- data/_layouts/cv.html +20 -14
- data/_layouts/home.html +23 -3
- data/_layouts/shell.html +53 -12
- data/assets/css/ledger/chrome.css +23 -0
- data/assets/css/ledger/cv.css +13 -0
- data/assets/css/ledger/home.css +27 -20
- data/assets/css/ledger/writing.css +27 -3
- data/assets/css/tailwind.css +3 -0
- data/assets/tailwind-play.js +2 -1
- data/lib/pandorga/commands/doctor.rb +3 -1
- data/lib/pandorga/commands/new.rb +1 -1
- data/lib/pandorga/discovery.rb +78 -0
- data/lib/pandorga/jekyll/plugins/discovery.rb +32 -0
- data/lib/pandorga/version.rb +1 -1
- data/scripts/cdn-allowlist.json +2 -5
- data/scripts/tailwind/build.sh +19 -0
- data/scripts/tailwind/input.css +12 -0
- data/scripts/tailwind/tailwind.config.js +102 -0
- data/scripts/test/render-parity/blocks/project-inventory-row.json +3 -2
- data/scripts/test/test-css-delivery.rb +32 -0
- data/scripts/test/test-css-structure.rb +8 -0
- data/scripts/test/test-discovery.rb +46 -0
- data/scripts/test/test-doctor.rb +38 -0
- data/scripts/test/test-examples-build.rb +15 -0
- data/scripts/test/test-hero-portraits.mjs +1 -1
- data/scripts/test/test-hydrate-reserve.rb +64 -0
- data/scripts/test/test-new.rb +11 -0
- data/scripts/test/test-shell-a11y.rb +30 -0
- data/scripts/test/test-template-packages.rb +8 -0
- data/scripts/test/test-validate-allowlist.rb +29 -0
- data/scripts/validate.sh +7 -1
- metadata +14 -2
- data/assets/css/cv.css +0 -13
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2bb5b2e689bf56f24930270b0f6dff89d91674594b67267a7e971a96252f1ac1
|
|
4
|
+
data.tar.gz: f5ded06e9a148e5095437afae98852a63aaf994737e38a91c5b5c9d4f6a9ada9
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0e18926a06e09cab67eea5ff3df47893e70abda71e2dad6ef7686f64a69f2b0d7527a65efe4069ec55342c51f6d7aa86cbb47f5005ded2650a1abe7c9ef55724
|
|
7
|
+
data.tar.gz: fda5ff032a0dcf24995bdfa280b80e3b2470c2e8e5bf455cfbbcac149df30d02637fd69e947562c0c7fe1f09a0834ae15dd6976c15411e92dea5a9a95dca2d00
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,56 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.6.0] — 2026-10-10
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- `/sitemap.xml` lists the generated pages. `/robots.txt` allows the public
|
|
8
|
+
site, skips `/studio/`, and names the sitemap. Both use
|
|
9
|
+
`pandorga.identity.url` when it is set, otherwise `site.url`.
|
|
10
|
+
- JSON-LD `WebSite` and `Person` in the shell, from `identity.name` and the
|
|
11
|
+
optional `identity.url`. `site.description` is included when it is set.
|
|
12
|
+
- Hero portrait entries may include optional `srcset` and `sizes`. A portrait
|
|
13
|
+
that only has `src` is unchanged.
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
|
|
17
|
+
- The home subtitle and summary are requested before the stylesheets and
|
|
18
|
+
painted without a "Loading..." swap. Article rows, CV sections, and empty
|
|
19
|
+
home bands keep a reserved box until the real content replaces it.
|
|
20
|
+
- The hero subtitle stays at `--type-body`, the same size as the summary.
|
|
21
|
+
- `.writing-revised` uses the solid variant ink. The transparent mix was
|
|
22
|
+
3.69 and 3.90 on the light paper.
|
|
23
|
+
- A selectable project inventory row is a `button`.
|
|
24
|
+
- Utilities ship as `assets/css/tailwind.css`. The shell no longer loads the
|
|
25
|
+
Tailwind Play CDN. Each layout links the shared chrome and its own ledger
|
|
26
|
+
sheet, not every sheet.
|
|
27
|
+
|
|
28
|
+
No `pandorga:` config keys were added or renamed. `srcset` and `sizes` are
|
|
29
|
+
optional fields on objects in `pages/home/portraits.json`.
|
|
30
|
+
|
|
31
|
+
## [1.5.1] — 2026-10-10
|
|
32
|
+
|
|
33
|
+
### Fixed
|
|
34
|
+
|
|
35
|
+
- The shell has a "Skip to content" link. When `site.author` is blank, the
|
|
36
|
+
home link's accessible name uses `pandorga.identity.name`, then the top-bar
|
|
37
|
+
wordmark, instead of a bare dash. The footer wordmark uses the same
|
|
38
|
+
fallback. A site that sets `site.author` is unchanged.
|
|
39
|
+
- `pandorga doctor` says when `pandorga.pages` is missing, and says when the
|
|
40
|
+
list is empty.
|
|
41
|
+
- `./scripts/validate.sh` fails if a listed gate file is missing.
|
|
42
|
+
|
|
43
|
+
### Changed
|
|
44
|
+
|
|
45
|
+
- Getting started and the README name the scaffold pin `~> 1.3`.
|
|
46
|
+
- `pandorga new` comments the `v1.5.0` tag.
|
|
47
|
+
- Home CSS drops an empty `@media (min-width: 768px)` block and the unused
|
|
48
|
+
`.home-profile-summary` rules.
|
|
49
|
+
- Chord chart and transcript include comments are English. The visible
|
|
50
|
+
transcript labels are unchanged.
|
|
51
|
+
|
|
52
|
+
No `pandorga:` config keys were added or renamed.
|
|
53
|
+
|
|
3
54
|
## [1.5.0] — 2026-10-10
|
|
4
55
|
|
|
5
56
|
### Added
|
data/README.md
CHANGED
|
@@ -17,7 +17,7 @@ An aesthetic Jekyll publishing platform for creative and technical writing. The
|
|
|
17
17
|
Scaffold a site (Ada Example defaults, `content.backend: static`):
|
|
18
18
|
|
|
19
19
|
```bash
|
|
20
|
-
gem install pandorga #
|
|
20
|
+
gem install pandorga # or use a local checkout
|
|
21
21
|
pandorga new my-site
|
|
22
22
|
cd my-site
|
|
23
23
|
bundle install
|
data/_includes/chord-tab.html
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{% comment %}
|
|
2
2
|
chord-tab.html
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
intro: "
|
|
3
|
+
Renders a chord chart in N monospaced columns, using the full available width.
|
|
4
|
+
Each column stays within 79 characters per line.
|
|
5
|
+
Use: {% include chord-tab.html tab=tab_data %}
|
|
6
|
+
tab_data has:
|
|
7
|
+
intro: "intro chord line (optional)"
|
|
8
8
|
sections:
|
|
9
|
-
- label: "
|
|
9
|
+
- label: "section label (optional)"
|
|
10
10
|
content: |
|
|
11
|
-
|
|
11
|
+
chord and lyric lines
|
|
12
12
|
{% endcomment %}
|
|
13
13
|
|
|
14
14
|
<div class="chord-tab-container mt-16 mb-8"
|
|
@@ -60,6 +60,13 @@
|
|
|
60
60
|
},
|
|
61
61
|
showLoadingState: function (element, message) {
|
|
62
62
|
if (!element) return;
|
|
63
|
+
// Reserved skeletons already occupy the box. Replacing them with a
|
|
64
|
+
// one-line message is the shift the listing then repeats when the
|
|
65
|
+
// cards arrive.
|
|
66
|
+
if (element.hasAttribute('data-content-reserve')) {
|
|
67
|
+
element.setAttribute('aria-busy', 'true');
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
63
70
|
element.innerHTML = '<p class="font-body text-on-surface-variant italic col-span-full">' + escapeHtml(message) + '</p>';
|
|
64
71
|
},
|
|
65
72
|
showErrorState: function (element, message) {
|
|
@@ -344,14 +344,15 @@
|
|
|
344
344
|
var slug = item.slug || f.slug || '';
|
|
345
345
|
var index = options.index;
|
|
346
346
|
|
|
347
|
-
|
|
347
|
+
// A selectable row is a real button. role=button on an article is
|
|
348
|
+
// what the audit flags, and the button already handles Enter and Space.
|
|
349
|
+
var row = document.createElement(selectable ? 'button' : 'article');
|
|
348
350
|
row.className = 'home-project-inventory-row group' +
|
|
349
351
|
(selected ? ' is-selected' : '');
|
|
352
|
+
if (selectable) row.type = 'button';
|
|
350
353
|
if (slug) row.setAttribute('data-slug', slug);
|
|
351
354
|
|
|
352
355
|
if (selectable) {
|
|
353
|
-
row.setAttribute('role', 'button');
|
|
354
|
-
row.setAttribute('tabindex', '0');
|
|
355
356
|
row.setAttribute('aria-pressed', selected ? 'true' : 'false');
|
|
356
357
|
row.setAttribute('aria-label', name);
|
|
357
358
|
}
|
|
@@ -13,6 +13,19 @@
|
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
async function listPaths(collection, manifestKey) {
|
|
16
|
+
var pre = window.__pandorgaPreload && window.__pandorgaPreload['manifest.json'];
|
|
17
|
+
if (pre && typeof pre.then === 'function') {
|
|
18
|
+
try {
|
|
19
|
+
var preloaded = await pre;
|
|
20
|
+
assertManifestSchema(preloaded);
|
|
21
|
+
var preloadedFiles = preloaded.files && preloaded.files[manifestKey];
|
|
22
|
+
if (Array.isArray(preloadedFiles)) return preloadedFiles;
|
|
23
|
+
} catch (error) {
|
|
24
|
+
if (error && String(error.message || '').indexOf('Unsupported content schema') === 0) throw error;
|
|
25
|
+
delete window.__pandorgaPreload['manifest.json'];
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
16
29
|
var manifestResponse = await fetch(buildUrl('manifest.json'));
|
|
17
30
|
if (manifestResponse.ok) {
|
|
18
31
|
var manifest = await manifestResponse.json();
|
|
@@ -63,16 +76,31 @@
|
|
|
63
76
|
return collectionCache[cacheKey];
|
|
64
77
|
}
|
|
65
78
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
if (jsonCache[key]) return jsonCache[key];
|
|
69
|
-
jsonCache[key] = fetch(buildUrl(path)).then(function (response) {
|
|
79
|
+
function fetchJson(path) {
|
|
80
|
+
return fetch(buildUrl(path)).then(function (response) {
|
|
70
81
|
if (!response.ok) throw new Error('Content JSON fetch failed: ' + path);
|
|
71
82
|
return response.json();
|
|
72
|
-
}).catch(function (error) {
|
|
73
|
-
delete jsonCache[key];
|
|
74
|
-
throw error;
|
|
75
83
|
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async function loadJson(path) {
|
|
87
|
+
var key = String(path || '');
|
|
88
|
+
if (jsonCache[key]) return jsonCache[key];
|
|
89
|
+
var pre = window.__pandorgaPreload && window.__pandorgaPreload[key];
|
|
90
|
+
jsonCache[key] = Promise.resolve()
|
|
91
|
+
.then(function () {
|
|
92
|
+
if (pre && typeof pre.then === 'function') return pre;
|
|
93
|
+
return fetchJson(path);
|
|
94
|
+
})
|
|
95
|
+
.catch(function () {
|
|
96
|
+
if (!(pre && typeof pre.then === 'function')) throw new Error('Content JSON fetch failed: ' + path);
|
|
97
|
+
delete window.__pandorgaPreload[key];
|
|
98
|
+
return fetchJson(path);
|
|
99
|
+
})
|
|
100
|
+
.catch(function (error) {
|
|
101
|
+
delete jsonCache[key];
|
|
102
|
+
throw error;
|
|
103
|
+
});
|
|
76
104
|
return jsonCache[key];
|
|
77
105
|
}
|
|
78
106
|
|
|
@@ -192,10 +220,21 @@
|
|
|
192
220
|
options = options || {};
|
|
193
221
|
var el = typeof elementOrId === 'string' ? document.getElementById(elementOrId) : elementOrId;
|
|
194
222
|
if (!el) return Promise.resolve();
|
|
195
|
-
|
|
196
|
-
|
|
223
|
+
// A quiet slot keeps its reserved box. Writing "Loading..." into the
|
|
224
|
+
// hero summary makes that placeholder the LCP, then swaps it away.
|
|
225
|
+
var quiet = el.hasAttribute('data-content-quiet');
|
|
226
|
+
var loadingText = quiet ? '' : (options.loadingText || 'Loading...');
|
|
227
|
+
if (loadingText) {
|
|
228
|
+
el.innerHTML = '<p class="font-body text-on-surface-variant italic">' + escapeHtml(loadingText) + '</p>';
|
|
229
|
+
}
|
|
197
230
|
return loadJson(pageFragmentPath(relativePath)).then(function (fragment) {
|
|
198
|
-
|
|
231
|
+
var html = renderMarkdown(fragment.body_markdown || '');
|
|
232
|
+
if (el.getAttribute('data-content-painted') === '1' && el.innerHTML === html) {
|
|
233
|
+
el.removeAttribute('data-content-painted');
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
el.innerHTML = html;
|
|
237
|
+
el.removeAttribute('data-content-painted');
|
|
199
238
|
}).catch(function (error) {
|
|
200
239
|
console.error(error);
|
|
201
240
|
el.innerHTML = '<p class="font-body text-on-surface-variant italic">' + escapeHtml(options.errorText || 'Content could not be loaded right now.') + '</p>';
|
|
@@ -473,11 +512,36 @@
|
|
|
473
512
|
var src = typeof entry.src === 'string' ? entry.src.trim() : '';
|
|
474
513
|
if (!src) continue;
|
|
475
514
|
var alt = typeof entry.alt === 'string' ? entry.alt.trim() : '';
|
|
476
|
-
|
|
515
|
+
var record = { src: src, alt: alt };
|
|
516
|
+
// Optional responsive fields. Absent keys stay off the record so a
|
|
517
|
+
// pool that only has src keeps the previous shape.
|
|
518
|
+
var srcset = typeof entry.srcset === 'string' ? entry.srcset.trim() : '';
|
|
519
|
+
var sizes = typeof entry.sizes === 'string' ? entry.sizes.trim() : '';
|
|
520
|
+
if (srcset) record.srcset = srcset;
|
|
521
|
+
if (sizes) record.sizes = sizes;
|
|
522
|
+
entries.push(record);
|
|
477
523
|
}
|
|
478
524
|
return entries;
|
|
479
525
|
}
|
|
480
526
|
|
|
527
|
+
function resolveSrcset(srcset) {
|
|
528
|
+
return String(srcset || '').split(',').map(function (part) {
|
|
529
|
+
var bits = part.trim().split(/\s+/);
|
|
530
|
+
if (!bits[0]) return '';
|
|
531
|
+
bits[0] = resolveAssetUrl(bits[0]);
|
|
532
|
+
return bits.filter(Boolean).join(' ');
|
|
533
|
+
}).filter(Boolean).join(', ');
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
function applyHeroPortraitSources(img, chosen) {
|
|
537
|
+
var srcset = resolveSrcset(chosen && chosen.srcset);
|
|
538
|
+
var sizes = chosen && typeof chosen.sizes === 'string' ? chosen.sizes.trim() : '';
|
|
539
|
+
if (srcset) img.setAttribute('srcset', srcset);
|
|
540
|
+
else img.removeAttribute('srcset');
|
|
541
|
+
if (sizes) img.setAttribute('sizes', sizes);
|
|
542
|
+
else img.removeAttribute('sizes');
|
|
543
|
+
}
|
|
544
|
+
|
|
481
545
|
function pickHeroPortrait(entries, random) {
|
|
482
546
|
if (!entries || !entries.length) return null;
|
|
483
547
|
var roll = typeof random === 'function' ? random() : Math.random();
|
|
@@ -491,6 +555,7 @@
|
|
|
491
555
|
if (!nextSrc) return Promise.resolve(false);
|
|
492
556
|
if (img.getAttribute('src') === nextSrc) {
|
|
493
557
|
img.alt = nextAlt;
|
|
558
|
+
applyHeroPortraitSources(img, chosen);
|
|
494
559
|
img.classList.add('is-shown');
|
|
495
560
|
return Promise.resolve(true);
|
|
496
561
|
}
|
|
@@ -499,6 +564,7 @@
|
|
|
499
564
|
probe.onload = function () {
|
|
500
565
|
img.src = nextSrc;
|
|
501
566
|
img.alt = nextAlt;
|
|
567
|
+
applyHeroPortraitSources(img, chosen);
|
|
502
568
|
img.classList.add('is-shown');
|
|
503
569
|
resolve(true);
|
|
504
570
|
};
|
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
{%- endcomment -%}
|
|
30
30
|
|
|
31
31
|
{%- assign footer_active = include.nav_active -%}
|
|
32
|
+
{%- assign footer_name = site.author | default: site.pandorga.identity.name -%}
|
|
32
33
|
{%- assign footer_license = site.content_license -%}
|
|
33
34
|
|
|
34
35
|
<footer class="site-footer">
|
|
@@ -39,9 +40,9 @@
|
|
|
39
40
|
<div class="site-footer-grid">
|
|
40
41
|
|
|
41
42
|
<div class="site-footer-block site-footer-masthead">
|
|
42
|
-
<a href="/" class="site-footer-wordmark-link" aria-label="{{
|
|
43
|
+
<a href="/" class="site-footer-wordmark-link" aria-label="{% if footer_name != blank %}{{ footer_name }} – Home{% else %}Home{% endif %}">
|
|
43
44
|
{% include theme/site-logo.html %}
|
|
44
|
-
<span class="site-footer-wordmark">{{
|
|
45
|
+
<span class="site-footer-wordmark">{{ footer_name }}</span>
|
|
45
46
|
</a>
|
|
46
47
|
{%- comment -%}
|
|
47
48
|
The same fragment the home hero mounts, so the line the site opens
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{%- comment -%}
|
|
2
|
+
Start the JSON fetches the first paint depends on, before the stylesheets
|
|
3
|
+
and the content runtime. loadJson and listPaths reuse window.__pandorgaPreload.
|
|
4
|
+
{%- endcomment -%}
|
|
5
|
+
{%- case page.layout -%}
|
|
6
|
+
{%- when "home" -%}
|
|
7
|
+
{%- assign _preload_paths = "pages/cv/summary.json|pages/home/subtitle.json|pages/home/portraits.json|pages/headers.json|data/writing_index.json" -%}
|
|
8
|
+
{%- when "articles" -%}
|
|
9
|
+
{%- assign _preload_paths = "data/writing_index.json|pages/headers.json|manifest.json" -%}
|
|
10
|
+
{%- when "blog" -%}
|
|
11
|
+
{%- assign _preload_paths = "data/writing_index.json|pages/headers.json|manifest.json" -%}
|
|
12
|
+
{%- when "cv" -%}
|
|
13
|
+
{%- assign _preload_paths = "pages/cv/summary.json|pages/cv/disclaimer.json|data/education.json|data/languages.json|pages/headers.json|manifest.json" -%}
|
|
14
|
+
{%- when "projects" -%}
|
|
15
|
+
{%- assign _preload_paths = "data/writing_index.json|pages/headers.json|manifest.json" -%}
|
|
16
|
+
{%- else -%}
|
|
17
|
+
{%- assign _preload_paths = "" -%}
|
|
18
|
+
{%- endcase -%}
|
|
19
|
+
{%- if _preload_paths != blank -%}
|
|
20
|
+
<script>
|
|
21
|
+
(function () {
|
|
22
|
+
var base = {{ site.content_api_base_url | default: "" | jsonify }};
|
|
23
|
+
base = String(base || "").replace(/\/$/, "");
|
|
24
|
+
var paths = {{ _preload_paths | split: "|" | jsonify }};
|
|
25
|
+
window.__pandorgaPreload = window.__pandorgaPreload || {};
|
|
26
|
+
paths.forEach(function (path) {
|
|
27
|
+
var url = base ? base + "/" + path : "/" + path;
|
|
28
|
+
window.__pandorgaPreload[path] = fetch(url, { credentials: "same-origin" }).then(function (response) {
|
|
29
|
+
if (!response.ok) throw new Error(path);
|
|
30
|
+
return response.json();
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
})();
|
|
34
|
+
</script>
|
|
35
|
+
{%- endif -%}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{%- comment -%}
|
|
2
|
+
WebSite and Person from identity. No new required keys: name is already
|
|
3
|
+
required, and url is the existing optional canonical origin.
|
|
4
|
+
{%- endcomment -%}
|
|
5
|
+
{%- assign ld_name = site.pandorga.identity.name | default: site.author | default: site.title -%}
|
|
6
|
+
{%- assign ld_url = site.pandorga.identity.url | default: site.url -%}
|
|
7
|
+
{%- if ld_url != blank -%}
|
|
8
|
+
{%- assign ld_website = ld_url -%}
|
|
9
|
+
{%- else -%}
|
|
10
|
+
{%- assign ld_website = "/" | absolute_url -%}
|
|
11
|
+
{%- endif -%}
|
|
12
|
+
{%- if ld_name != blank -%}
|
|
13
|
+
<script type="application/ld+json">
|
|
14
|
+
{
|
|
15
|
+
"@context": "https://schema.org",
|
|
16
|
+
"@graph": [
|
|
17
|
+
{
|
|
18
|
+
"@type": "WebSite",
|
|
19
|
+
"name": {{ ld_name | jsonify }},
|
|
20
|
+
"url": {{ ld_website | jsonify }}
|
|
21
|
+
{%- if site.description != blank -%},
|
|
22
|
+
"description": {{ site.description | jsonify }}
|
|
23
|
+
{%- endif -%}
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"@type": "Person",
|
|
27
|
+
"name": {{ ld_name | jsonify }}
|
|
28
|
+
{%- if ld_url != blank -%},
|
|
29
|
+
"url": {{ ld_url | jsonify }}
|
|
30
|
+
{%- endif -%}
|
|
31
|
+
}
|
|
32
|
+
]
|
|
33
|
+
}
|
|
34
|
+
</script>
|
|
35
|
+
{%- endif -%}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{% comment %}
|
|
2
2
|
transcription.html
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
Renders a structured transcript, with up to 4 speakers (colors follow order of appearance).
|
|
4
|
+
Use: {% include transcription.html transcription=page.transcription youtube_id='fpmZiY24U0k' %}
|
|
5
|
+
Or to load a file from _data:
|
|
6
6
|
{% assign transcription_data = site.data.transcriptions[page.transcription].transcription %}
|
|
7
7
|
{% include transcription.html transcription=transcription_data youtube_id='fpmZiY24U0k' %}
|
|
8
|
-
|
|
8
|
+
Also accepts url and image for a thumbnail:
|
|
9
9
|
{% include transcription.html transcription=transcription_data url='https://...' image='https://...' %}
|
|
10
10
|
|
|
11
11
|
Speaker labels are title-cased at render time (Patrícia, not PATRÍCIA) so
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
<hr class="border-t border-gentle-divider mb-6">
|
|
27
27
|
{% if include.youtube_id %}
|
|
28
28
|
<div class="mb-2">
|
|
29
|
-
{% include youtube.html id=include.youtube_id %} <!--
|
|
29
|
+
{% include youtube.html id=include.youtube_id %} <!-- YouTube video -->
|
|
30
30
|
<span class="font-label text-xs text-on-surface-variant">Transcrição do vídeo</span>
|
|
31
31
|
</div>
|
|
32
32
|
{% elsif include.url and include.image %}
|
data/_layouts/articles.html
CHANGED
|
@@ -90,8 +90,14 @@ layout: shell
|
|
|
90
90
|
</div>
|
|
91
91
|
|
|
92
92
|
<div class="writing-archive" id="articles-list">
|
|
93
|
-
<section id="empty-state" class="writing-archive-
|
|
94
|
-
<p class="
|
|
93
|
+
<section id="empty-state" class="writing-archive-reserve" aria-busy="true">
|
|
94
|
+
<p class="sr-only">Loading articles...</p>
|
|
95
|
+
<span class="writing-reserve-row" aria-hidden="true"></span>
|
|
96
|
+
<span class="writing-reserve-row" aria-hidden="true"></span>
|
|
97
|
+
<span class="writing-reserve-row" aria-hidden="true"></span>
|
|
98
|
+
<span class="writing-reserve-row" aria-hidden="true"></span>
|
|
99
|
+
<span class="writing-reserve-row" aria-hidden="true"></span>
|
|
100
|
+
<span class="writing-reserve-row" aria-hidden="true"></span>
|
|
95
101
|
</section>
|
|
96
102
|
<section id="no-results" class="writing-archive-state hidden">
|
|
97
103
|
<p class="font-body text-on-surface-variant italic">Nothing matches these filters yet.</p>
|
data/_layouts/cv.html
CHANGED
|
@@ -25,43 +25,45 @@ layout: shell
|
|
|
25
25
|
|
|
26
26
|
<div id="cv-disclaimer" class="cv-disclaimer-box">
|
|
27
27
|
<span class="material-symbols-outlined cv-dossier-note-icon" aria-hidden="true">info</span>
|
|
28
|
-
<div class="cv-dossier-note font-body" data-content-fragment="cv/disclaimer">
|
|
29
|
-
<p class="font-body text-on-surface-variant italic">Loading...</p>
|
|
30
|
-
</div>
|
|
28
|
+
<div class="cv-dossier-note font-body" data-content-fragment="cv/disclaimer" data-content-quiet></div>
|
|
31
29
|
</div>
|
|
32
30
|
|
|
33
31
|
<div id="cv-listing">
|
|
34
32
|
{% capture cv_summary %}
|
|
35
|
-
<div class="cv-summary font-body" data-content-fragment="cv/summary">
|
|
36
|
-
<p class="font-body text-on-surface-variant italic">Loading...</p>
|
|
37
|
-
</div>
|
|
33
|
+
<div class="cv-summary font-body" data-content-fragment="cv/summary" data-content-quiet></div>
|
|
38
34
|
{% endcapture %}
|
|
39
35
|
{% include cv/cv-box.html id="summary" title="Summary" content=cv_summary %}
|
|
40
36
|
|
|
41
37
|
{% capture cv_experience %}
|
|
42
|
-
<div id="cv-experience-list" class="cv-entry-stack">
|
|
43
|
-
<
|
|
38
|
+
<div id="cv-experience-list" class="cv-entry-stack" data-content-reserve aria-busy="true">
|
|
39
|
+
<span class="cv-reserve-card" aria-hidden="true"></span>
|
|
40
|
+
<span class="cv-reserve-card" aria-hidden="true"></span>
|
|
41
|
+
<span class="cv-reserve-card" aria-hidden="true"></span>
|
|
44
42
|
</div>
|
|
45
43
|
{% endcapture %}
|
|
46
44
|
{% include cv/cv-box.html id="experience" title="Experience" content=cv_experience %}
|
|
47
45
|
|
|
48
46
|
{% capture cv_products %}
|
|
49
|
-
<div id="cv-products-grid" class="cv-product-grid">
|
|
50
|
-
<
|
|
47
|
+
<div id="cv-products-grid" class="cv-product-grid" data-content-reserve aria-busy="true">
|
|
48
|
+
<span class="cv-reserve-card" aria-hidden="true"></span>
|
|
49
|
+
<span class="cv-reserve-card" aria-hidden="true"></span>
|
|
50
|
+
<span class="cv-reserve-card" aria-hidden="true"></span>
|
|
51
51
|
</div>
|
|
52
52
|
{% endcapture %}
|
|
53
53
|
{% include cv/cv-box.html id="products" title="Products" content=cv_products %}
|
|
54
54
|
|
|
55
55
|
{% capture cv_education %}
|
|
56
|
-
<div id="cv-education-list" class="cv-entry-stack">
|
|
57
|
-
<
|
|
56
|
+
<div id="cv-education-list" class="cv-entry-stack" data-content-reserve aria-busy="true">
|
|
57
|
+
<span class="cv-reserve-card" aria-hidden="true"></span>
|
|
58
|
+
<span class="cv-reserve-card" aria-hidden="true"></span>
|
|
58
59
|
</div>
|
|
59
60
|
{% endcapture %}
|
|
60
61
|
{% include cv/cv-box.html id="education" title="Education" content=cv_education %}
|
|
61
62
|
|
|
62
63
|
{% capture cv_languages %}
|
|
63
|
-
<div id="cv-languages-list" class="cv-language-list">
|
|
64
|
-
<
|
|
64
|
+
<div id="cv-languages-list" class="cv-language-list" data-content-reserve aria-busy="true">
|
|
65
|
+
<span class="cv-reserve-card" aria-hidden="true"></span>
|
|
66
|
+
<span class="cv-reserve-card" aria-hidden="true"></span>
|
|
65
67
|
</div>
|
|
66
68
|
{% endcapture %}
|
|
67
69
|
{% include cv/cv-box.html id="languages" title="Languages" content=cv_languages %}
|
|
@@ -327,6 +329,7 @@ layout: shell
|
|
|
327
329
|
productMap[product.slug] = product;
|
|
328
330
|
});
|
|
329
331
|
experienceList.innerHTML = '';
|
|
332
|
+
experienceList.removeAttribute('aria-busy');
|
|
330
333
|
jobs.sort(function (a, b) {
|
|
331
334
|
var as = (a.front_matter && a.front_matter.start) || '';
|
|
332
335
|
var bs = (b.front_matter && b.front_matter.start) || '';
|
|
@@ -344,6 +347,7 @@ layout: shell
|
|
|
344
347
|
runtime.Listing.showLoadingState(educationList, 'Loading education...');
|
|
345
348
|
runtime.loadJson('data/education.json').then(function (entries) {
|
|
346
349
|
educationList.innerHTML = '';
|
|
350
|
+
educationList.removeAttribute('aria-busy');
|
|
347
351
|
entries.forEach(function (entry) { educationList.appendChild(renderEducation(entry)); });
|
|
348
352
|
}).catch(function (error) {
|
|
349
353
|
console.error(error);
|
|
@@ -355,6 +359,7 @@ layout: shell
|
|
|
355
359
|
runtime.Listing.showLoadingState(languagesList, 'Loading languages...');
|
|
356
360
|
runtime.loadJson('data/languages.json').then(function (entries) {
|
|
357
361
|
languagesList.innerHTML = '';
|
|
362
|
+
languagesList.removeAttribute('aria-busy');
|
|
358
363
|
entries.forEach(function (entry) { languagesList.appendChild(renderLanguage(entry)); });
|
|
359
364
|
}).catch(function (error) {
|
|
360
365
|
console.error(error);
|
|
@@ -366,6 +371,7 @@ layout: shell
|
|
|
366
371
|
runtime.Listing.showLoadingState(productsGrid, 'Loading products...');
|
|
367
372
|
runtime.loadWritingIndex('products').then(function (products) {
|
|
368
373
|
productsGrid.innerHTML = '';
|
|
374
|
+
productsGrid.removeAttribute('aria-busy');
|
|
369
375
|
var ordered = products.slice().sort(compareProductsByEndDate);
|
|
370
376
|
ordered.forEach(function (product) {
|
|
371
377
|
productsGrid.appendChild(renderProduct(product));
|
data/_layouts/home.html
CHANGED
|
@@ -29,12 +29,12 @@ layout: shell
|
|
|
29
29
|
<div class="home-hero">
|
|
30
30
|
<div class="home-hero-copy">
|
|
31
31
|
<div id="home-subtitle-content" class="home-hero-tagline font-label"
|
|
32
|
-
data-content-fragment="home/subtitle"></div>
|
|
32
|
+
data-content-fragment="home/subtitle" data-content-quiet></div>
|
|
33
33
|
<h1 class="home-hero-title">
|
|
34
34
|
<span class="home-hero-given">{{ _hero_name.given }}</span>{%- if _hero_name.family != "" %} <span class="home-hero-family">{{ _hero_name.family }}</span>{%- endif %}
|
|
35
35
|
</h1>
|
|
36
36
|
<div id="home-cv-summary" class="home-hero-summary font-body"
|
|
37
|
-
data-content-fragment="cv/summary"></div>
|
|
37
|
+
data-content-fragment="cv/summary" data-content-quiet></div>
|
|
38
38
|
<div class="home-hero-actions">
|
|
39
39
|
{%- if _cv_page -%}
|
|
40
40
|
<a class="ledger-cta ledger-cta--emphasis home-hero-cta" href="{{ _cv_page.url }}">
|
|
@@ -50,7 +50,7 @@ layout: shell
|
|
|
50
50
|
<div class="home-hero-plate-wrap">
|
|
51
51
|
<span class="ledger-frame-crosshairs" aria-hidden="true"></span>
|
|
52
52
|
<figure class="home-hero-plate">
|
|
53
|
-
<img alt="" width="1200" height="800" data-content-hero-portrait />
|
|
53
|
+
<img alt="" width="1200" height="800" fetchpriority="high" data-content-hero-portrait />
|
|
54
54
|
<span class="ledger-frame-corners" aria-hidden="true"></span>
|
|
55
55
|
</figure>
|
|
56
56
|
</div>
|
|
@@ -58,6 +58,26 @@ layout: shell
|
|
|
58
58
|
</div>
|
|
59
59
|
</div>
|
|
60
60
|
</section>
|
|
61
|
+
<script>
|
|
62
|
+
(function () {
|
|
63
|
+
var runtime = window.ContentRuntime;
|
|
64
|
+
var pre = window.__pandorgaPreload || {};
|
|
65
|
+
function paint(id, path) {
|
|
66
|
+
var el = document.getElementById(id);
|
|
67
|
+
var pending = pre[path];
|
|
68
|
+
if (!el || !pending || !runtime || !runtime.renderMarkdown) return;
|
|
69
|
+
pending.then(function (fragment) {
|
|
70
|
+
if (!fragment || el.getAttribute('data-content-painted') === '1') return;
|
|
71
|
+
var html = runtime.renderMarkdown(fragment.body_markdown || '');
|
|
72
|
+
if (!html) return;
|
|
73
|
+
el.innerHTML = html;
|
|
74
|
+
el.setAttribute('data-content-painted', '1');
|
|
75
|
+
}).catch(function () {});
|
|
76
|
+
}
|
|
77
|
+
paint('home-subtitle-content', 'pages/home/subtitle.json');
|
|
78
|
+
paint('home-cv-summary', 'pages/cv/summary.json');
|
|
79
|
+
})();
|
|
80
|
+
</script>
|
|
61
81
|
|
|
62
82
|
{%- for band in _home_bands -%}
|
|
63
83
|
{%- if band.kind == "index" -%}
|