pandorga 1.2.1
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 +7 -0
- data/CHANGELOG.md +77 -0
- data/DESIGN.md +1434 -0
- data/LICENSE +21 -0
- data/README.md +81 -0
- data/_data/themes/dark.yml +97 -0
- data/_data/themes/light.yml +120 -0
- data/_data/themes/shared.yml +37 -0
- data/_data/translations.yml +85 -0
- data/_includes/chord-tab.html +38 -0
- data/_includes/content-runtime/00-config.html +22 -0
- data/_includes/content-runtime/10-utils.html +176 -0
- data/_includes/content-runtime/20-markdown.html +264 -0
- data/_includes/content-runtime/25-code-blocks.html +315 -0
- data/_includes/content-runtime/30-includes-bib.html +190 -0
- data/_includes/content-runtime/30-includes-xcite.html +122 -0
- data/_includes/content-runtime/30-includes.html +365 -0
- data/_includes/content-runtime/40-listing.html +225 -0
- data/_includes/content-runtime/41-listing-helpers.html +71 -0
- data/_includes/content-runtime/42-listing-filter.html +208 -0
- data/_includes/content-runtime/43-blocks.html +379 -0
- data/_includes/content-runtime/50-detail.html +861 -0
- data/_includes/content-runtime/55-toc.html +176 -0
- data/_includes/content-runtime/60-fragments.html +567 -0
- data/_includes/content-runtime/90-bootstrap.html +76 -0
- data/_includes/cut-in.html +10 -0
- data/_includes/cv/cv-box.html +18 -0
- data/_includes/dict-card.html +24 -0
- data/_includes/figure.html +36 -0
- data/_includes/home/band-index.html +43 -0
- data/_includes/home/band-listing.html +49 -0
- data/_includes/mathjax.html +10 -0
- data/_includes/ongoing-product.html +1 -0
- data/_includes/page/content-runtime.html +20 -0
- data/_includes/page/date_evolution.html +19 -0
- data/_includes/page/figure-lightbox.html +102 -0
- data/_includes/page/formatted_date.html +15 -0
- data/_includes/page/hero-default.html +18 -0
- data/_includes/page/listing-runtime-attrs.html +8 -0
- data/_includes/page/mermaid.html +101 -0
- data/_includes/page/network-graph.html +1253 -0
- data/_includes/page/page-header.html +41 -0
- data/_includes/page/professional-connections.html +2 -0
- data/_includes/page/site-footer.html +101 -0
- data/_includes/page/timeline-section.html +868 -0
- data/_includes/plotly.html +22 -0
- data/_includes/theme/dev-reexport.html +100 -0
- data/_includes/theme/favicons.html +4 -0
- data/_includes/theme/font-loader.html +55 -0
- data/_includes/theme/shell-theme-boot.html +115 -0
- data/_includes/theme/site-logo.html +6 -0
- data/_includes/theme/tailwind-theme-colors.json.liquid +65 -0
- data/_includes/theme/tailwind-theme-fonts.json.liquid +6 -0
- data/_includes/theme/theme-context.liquid +20 -0
- data/_includes/theme/theme-toggle.html +94 -0
- data/_includes/theme/theme-vars-block.liquid +161 -0
- data/_includes/theme/theme-vars.html +16 -0
- data/_includes/transcription.html +80 -0
- data/_includes/tweet.html +22 -0
- data/_includes/video.html +11 -0
- data/_includes/xcite.html +3 -0
- data/_includes/youtube-short.html +26 -0
- data/_includes/youtube.html +12 -0
- data/_layouts/article.html +27 -0
- data/_layouts/articles.html +534 -0
- data/_layouts/bibliography.html +417 -0
- data/_layouts/blog.html +544 -0
- data/_layouts/cv.html +393 -0
- data/_layouts/home.html +319 -0
- data/_layouts/network.html +9 -0
- data/_layouts/pandorga/articles.html +4 -0
- data/_layouts/pandorga/bibliography.html +4 -0
- data/_layouts/pandorga/blog.html +4 -0
- data/_layouts/pandorga/cv.html +4 -0
- data/_layouts/pandorga/media.html +4 -0
- data/_layouts/pandorga/network.html +4 -0
- data/_layouts/pandorga/portfolio.html +4 -0
- data/_layouts/post.html +7 -0
- data/_layouts/product.html +5 -0
- data/_layouts/project.html +27 -0
- data/_layouts/projects.html +534 -0
- data/_layouts/resources.html +387 -0
- data/_layouts/shell.html +203 -0
- data/_layouts/text.html +246 -0
- data/_plugins/bibliography_artifact.rb +57 -0
- data/_plugins/content_backend.rb +37 -0
- data/_plugins/content_collections.rb +51 -0
- data/_plugins/content_detail_paths.rb +58 -0
- data/_plugins/content_headers.rb +73 -0
- data/_plugins/content_keys.rb +310 -0
- data/_plugins/content_local_serve.rb +168 -0
- data/_plugins/content_site_adapter.rb +159 -0
- data/_plugins/content_validators_hook.rb +20 -0
- data/_plugins/content_writing_index.rb +52 -0
- data/_plugins/content_writing_paths.rb +55 -0
- data/_plugins/force_utf8_encoding.rb +13 -0
- data/_plugins/git_chronology.rb +211 -0
- data/_plugins/git_metadata.rb +43 -0
- data/_plugins/job_sections.rb +25 -0
- data/_plugins/lib/content_validators.rb +27 -0
- data/_plugins/lib/resource_validator.rb +56 -0
- data/_plugins/lib/tag_validator.rb +153 -0
- data/_plugins/lib/text_excerpt.rb +56 -0
- data/_plugins/lib/writing_entry.rb +107 -0
- data/_plugins/local_detail_rewrites.rb +194 -0
- data/_plugins/timeline.rb +507 -0
- data/_plugins/writing_slug_limits.rb +70 -0
- data/assets/css/article.css +1330 -0
- data/assets/css/base.css +1432 -0
- data/assets/css/cv.css +13 -0
- data/assets/css/global-link.css +94 -0
- data/assets/css/ledger/bibliography.css +307 -0
- data/assets/css/ledger/chrome.css +118 -0
- data/assets/css/ledger/cv.css +836 -0
- data/assets/css/ledger/detail.css +1270 -0
- data/assets/css/ledger/footer.css +298 -0
- data/assets/css/ledger/home.css +1490 -0
- data/assets/css/ledger/network.css +106 -0
- data/assets/css/ledger/projects.css +716 -0
- data/assets/css/ledger/rascunhos.css +379 -0
- data/assets/css/ledger/sources.css +327 -0
- data/assets/css/ledger/writing.css +754 -0
- data/assets/tailwind-play.js +15 -0
- data/examples/full/Gemfile +7 -0
- data/examples/full/_config.yml +92 -0
- data/examples/full/_redirects +3 -0
- data/examples/full/content/collections/articles/2026-01-01-welcome.md +9 -0
- data/examples/full/content/collections/data/contacts.yml +3 -0
- data/examples/full/content/collections/data/education.yml +4 -0
- data/examples/full/content/collections/jobs/research-fellow.md +12 -0
- data/examples/full/content/collections/posts/2026-01-02-note.md +9 -0
- data/examples/full/content/collections/products/ledger-tool.md +6 -0
- data/examples/full/content/collections/projects/demo-kit.md +10 -0
- data/examples/full/content/collections/resources/style-guide.md +7 -0
- data/examples/full/content/pages/cv/summary.md +1 -0
- data/examples/full/content/pages/headers.yml +20 -0
- data/examples/full/index.html +4 -0
- data/examples/minimal/Gemfile +7 -0
- data/examples/minimal/_config.yml +47 -0
- data/examples/minimal/_redirects +3 -0
- data/examples/minimal/content/collections/articles/2026-01-01-hello.md +10 -0
- data/examples/minimal/content/collections/articles/2026-01-02-second.md +9 -0
- data/examples/minimal/content/pages/headers.yml +6 -0
- data/examples/minimal/index.html +4 -0
- data/exe/pandorga +7 -0
- data/functions/api/studio/[[path]].js +562 -0
- data/functions/api/studio/_lib/auth.js +131 -0
- data/functions/api/studio/_lib/blocks.js +156 -0
- data/functions/api/studio/_lib/gemini.js +62 -0
- data/functions/api/studio/_lib/github.js +278 -0
- data/functions/api/studio/_lib/guidelines.js +143 -0
- data/functions/api/studio/_lib/paths.js +76 -0
- data/functions/api/studio/_lib/prompts.js +165 -0
- data/functions/api/studio/_lib/rate-limit.js +40 -0
- data/functions/api/studio/_lib/rewrite.js +136 -0
- data/functions/api/studio/_lib/sanitize.js +143 -0
- data/lib/pandorga/cli.rb +66 -0
- data/lib/pandorga/commands/doctor.rb +66 -0
- data/lib/pandorga/commands/export.rb +30 -0
- data/lib/pandorga/commands/install_functions.rb +41 -0
- data/lib/pandorga/commands/new.rb +113 -0
- data/lib/pandorga/commands/publish.rb +53 -0
- data/lib/pandorga/commands/serve.rb +34 -0
- data/lib/pandorga/commands/studio_schema.rb +309 -0
- data/lib/pandorga/jekyll/generator.rb +74 -0
- data/lib/pandorga/jekyll/plugins/bibliography_artifact.rb +57 -0
- data/lib/pandorga/jekyll/plugins/content_backend.rb +37 -0
- data/lib/pandorga/jekyll/plugins/content_collections.rb +51 -0
- data/lib/pandorga/jekyll/plugins/content_detail_paths.rb +58 -0
- data/lib/pandorga/jekyll/plugins/content_headers.rb +73 -0
- data/lib/pandorga/jekyll/plugins/content_keys.rb +310 -0
- data/lib/pandorga/jekyll/plugins/content_local_serve.rb +168 -0
- data/lib/pandorga/jekyll/plugins/content_site_adapter.rb +159 -0
- data/lib/pandorga/jekyll/plugins/content_validators_hook.rb +20 -0
- data/lib/pandorga/jekyll/plugins/content_writing_index.rb +52 -0
- data/lib/pandorga/jekyll/plugins/content_writing_paths.rb +55 -0
- data/lib/pandorga/jekyll/plugins/force_utf8_encoding.rb +13 -0
- data/lib/pandorga/jekyll/plugins/git_chronology.rb +211 -0
- data/lib/pandorga/jekyll/plugins/git_metadata.rb +43 -0
- data/lib/pandorga/jekyll/plugins/job_sections.rb +25 -0
- data/lib/pandorga/jekyll/plugins/lib/content_validators.rb +27 -0
- data/lib/pandorga/jekyll/plugins/lib/resource_validator.rb +56 -0
- data/lib/pandorga/jekyll/plugins/lib/tag_validator.rb +153 -0
- data/lib/pandorga/jekyll/plugins/lib/text_excerpt.rb +56 -0
- data/lib/pandorga/jekyll/plugins/lib/writing_entry.rb +107 -0
- data/lib/pandorga/jekyll/plugins/local_detail_rewrites.rb +194 -0
- data/lib/pandorga/jekyll/plugins/timeline.rb +507 -0
- data/lib/pandorga/jekyll/plugins/writing_slug_limits.rb +70 -0
- data/lib/pandorga/jekyll.rb +15 -0
- data/lib/pandorga/object_store_env.rb +51 -0
- data/lib/pandorga/registry/schema.yml +115 -0
- data/lib/pandorga/registry.rb +279 -0
- data/lib/pandorga/version.rb +5 -0
- data/lib/pandorga.rb +19 -0
- data/scripts/cdn-allowlist.json +19 -0
- data/scripts/content/backfill-content-keys.rb +99 -0
- data/scripts/content/band_art.rb +362 -0
- data/scripts/content/export-content-json.rb +741 -0
- data/scripts/content/flatten-ansi-capture.mjs +153 -0
- data/scripts/content/hero_portraits.rb +28 -0
- data/scripts/content/publish-content-to-object-store.sh +78 -0
- data/scripts/content/publish-content-to-r2.sh +5 -0
- data/scripts/content/sync-content-json-to-r2.rb +167 -0
- data/scripts/content/sync-content-redirects.rb +12 -0
- data/scripts/generate-configuration-doc.rb +127 -0
- data/scripts/lib/content_license.rb +50 -0
- data/scripts/lib/content_storage_config.rb +70 -0
- data/scripts/lib/normalize-display-math.mjs +52 -0
- data/scripts/lib/r2_json_sync.rb +76 -0
- data/scripts/lib/withdrawn_public_objects.rb +23 -0
- data/scripts/lib/writing_index_contract.rb +61 -0
- data/scripts/pdf/md-to-pdf.mjs +1400 -0
- data/scripts/pdf/md-to-pdf.sh +27 -0
- data/scripts/test/fixtures/dual-template/_config.yml +32 -0
- data/scripts/test/fixtures/dual-template/index.html +4 -0
- data/scripts/test/render-parity/article-note.json +5 -0
- data/scripts/test/render-parity/blockquote.json +5 -0
- data/scripts/test/render-parity/blocks/ansi-basic.json +7 -0
- data/scripts/test/render-parity/blocks/ansi-bold-is-bright.json +7 -0
- data/scripts/test/render-parity/blocks/ansi-drops-cursor-moves.json +7 -0
- data/scripts/test/render-parity/blocks/ansi-escapes-markup.json +7 -0
- data/scripts/test/render-parity/blocks/ansi-extended-colour.json +7 -0
- data/scripts/test/render-parity/blocks/ansi-real-control-byte.json +7 -0
- data/scripts/test/render-parity/blocks/archive-card-date-once.json +19 -0
- data/scripts/test/render-parity/blocks/archive-card-empty-foot.json +7 -0
- data/scripts/test/render-parity/blocks/archive-card-footer-dates.json +22 -0
- data/scripts/test/render-parity/blocks/archive-card-meta-parts.json +26 -0
- data/scripts/test/render-parity/blocks/archive-card.json +32 -0
- data/scripts/test/render-parity/blocks/cta-link.json +13 -0
- data/scripts/test/render-parity/blocks/lang-pill-pt.json +8 -0
- data/scripts/test/render-parity/blocks/lang-pill.json +8 -0
- data/scripts/test/render-parity/blocks/ledger-row-meta-parts.json +29 -0
- data/scripts/test/render-parity/blocks/ledger-row.json +35 -0
- data/scripts/test/render-parity/blocks/meta-strip.json +27 -0
- data/scripts/test/render-parity/blocks/poster-media-image.json +8 -0
- data/scripts/test/render-parity/blocks/poster-media-video.json +9 -0
- data/scripts/test/render-parity/blocks/section-banner.json +13 -0
- data/scripts/test/render-parity/blocks/system-card-custom-meta.json +22 -0
- data/scripts/test/render-parity/blocks/system-card.json +48 -0
- data/scripts/test/render-parity/blocks/tag-chips-links.json +12 -0
- data/scripts/test/render-parity/blocks/tag-chips.json +9 -0
- data/scripts/test/render-parity/cut-in-blank-line.json +9 -0
- data/scripts/test/render-parity/cut-in-right.json +9 -0
- data/scripts/test/render-parity/cut-in.json +8 -0
- data/scripts/test/render-parity/figure.json +5 -0
- data/scripts/test/render-parity/load-runtime.mjs +337 -0
- data/scripts/test/render-parity/ongoing-product.json +5 -0
- data/scripts/test/render-parity/plotly.json +5 -0
- data/scripts/test/render-parity/poem.json +5 -0
- data/scripts/test/render-parity/run-absent-field-checks.mjs +232 -0
- data/scripts/test/render-parity/run-block-fixture.mjs +86 -0
- data/scripts/test/render-parity/run-detail-parity-checks.mjs +123 -0
- data/scripts/test/render-parity/run-field-flow-checks.mjs +227 -0
- data/scripts/test/render-parity/run-fixture.mjs +159 -0
- data/scripts/test/render-parity/selectors.mjs +43 -0
- data/scripts/test/render-parity/video.json +5 -0
- data/scripts/test/render-parity/youtube-short.json +10 -0
- data/scripts/test/render-parity/youtube.json +8 -0
- data/scripts/test/test-absent-field-absent-element.rb +43 -0
- data/scripts/test/test-cdn-pins.rb +44 -0
- data/scripts/test/test-cloudflare-watch-paths-doc.rb +31 -0
- data/scripts/test/test-configuration-doc.rb +36 -0
- data/scripts/test/test-content-detail-urls.rb +346 -0
- data/scripts/test/test-content-keys.rb +110 -0
- data/scripts/test/test-content-license.rb +45 -0
- data/scripts/test/test-content-render-parity.rb +38 -0
- data/scripts/test/test-content-validators.rb +12 -0
- data/scripts/test/test-css-structure.rb +72 -0
- data/scripts/test/test-cv-export-contract.rb +158 -0
- data/scripts/test/test-detail-render-parity.rb +40 -0
- data/scripts/test/test-dev-reexport.rb +122 -0
- data/scripts/test/test-doctor.rb +39 -0
- data/scripts/test/test-dual-template-instances.rb +87 -0
- data/scripts/test/test-dual-template-runtime.rb +167 -0
- data/scripts/test/test-examples-build.rb +60 -0
- data/scripts/test/test-export-content-json-incremental.rb +138 -0
- data/scripts/test/test-export.rb +53 -0
- data/scripts/test/test-exported-fields-reach-the-view.rb +60 -0
- data/scripts/test/test-form-control-theming.rb +143 -0
- data/scripts/test/test-git-chronology.rb +414 -0
- data/scripts/test/test-hero-portraits.mjs +97 -0
- data/scripts/test/test-hero-portraits.rb +162 -0
- data/scripts/test/test-home-band-art.rb +135 -0
- data/scripts/test/test-install-functions.rb +39 -0
- data/scripts/test/test-language-export.rb +79 -0
- data/scripts/test/test-listing-filter-contract.rb +119 -0
- data/scripts/test/test-math-row-breaks.mjs +79 -0
- data/scripts/test/test-math-row-breaks.rb +18 -0
- data/scripts/test/test-new.rb +113 -0
- data/scripts/test/test-no-personal-data.rb +85 -0
- data/scripts/test/test-public-cv-export.rb +164 -0
- data/scripts/test/test-r2-config-consistency.rb +57 -0
- data/scripts/test/test-r2-json-sync.rb +193 -0
- data/scripts/test/test-registry-home.rb +34 -0
- data/scripts/test/test-runtime-js-syntax.rb +64 -0
- data/scripts/test/test-slim-writing-index.rb +126 -0
- data/scripts/test/test-studio-api-contract.rb +99 -0
- data/scripts/test/test-studio-content-files.rb +79 -0
- data/scripts/test/test-studio-liquid-roundtrip.mjs +179 -0
- data/scripts/test/test-studio-rate-limit.mjs +70 -0
- data/scripts/test/test-studio-rewrite-api-contract.rb +59 -0
- data/scripts/test/test-studio-rewrite-sanitize.mjs +210 -0
- data/scripts/test/test-studio-schema-composition.rb +126 -0
- data/scripts/test/test-studio-slug-config.rb +167 -0
- data/scripts/test/test-template-packages.rb +51 -0
- data/scripts/test/test-text-excerpt.rb +51 -0
- data/scripts/test/test-unwrap-media-src.mjs +39 -0
- data/scripts/test/test-unwrap-media-src.rb +19 -0
- data/scripts/test/test-withdrawn-content-export.rb +69 -0
- data/scripts/test/test-writing-index-export-slugs.rb +84 -0
- data/scripts/test/test-writing-slug-limits.rb +61 -0
- data/scripts/test/test-xcite-chronology.rb +106 -0
- data/scripts/validate.sh +74 -0
- data/scripts/visual/build-cv-pages-json.js +33 -0
- data/scripts/visual/build-site-pages-json.js +94 -0
- data/scripts/visual/publish-visual-inspection-to-r2.sh +42 -0
- data/scripts/visual/take-screenshots.js +88 -0
- data/scripts/visual/visual-inspection-pages.json +113 -0
- data/studio/assets/index-B_j0pMD5.css +1 -0
- data/studio/assets/index-Ch4YEv9g.js +285 -0
- data/studio/index.html +27 -0
- data/studio/schema.yml +869 -0
- data/studio-app/README.md +27 -0
- data/studio-app/index.html +26 -0
- data/studio-app/package-lock.json +2499 -0
- data/studio-app/package.json +26 -0
- data/studio-app/skills-lock.json +47 -0
- data/studio-app/src/api/client.js +71 -0
- data/studio-app/src/editor/body-editor.js +70 -0
- data/studio-app/src/editor/liquid-core.js +382 -0
- data/studio-app/src/editor/liquid.js +77 -0
- data/studio-app/src/lib/document.js +207 -0
- data/studio-app/src/lib/list-controls.js +317 -0
- data/studio-app/src/lib/schema.js +66 -0
- data/studio-app/src/lib/yaml-doc.js +100 -0
- data/studio-app/src/main.js +3693 -0
- data/studio-app/src/media/browser.js +578 -0
- data/studio-app/src/preview/render.js +430 -0
- data/studio-app/src/styles/studio.css +2826 -0
- data/studio-app/vite.config.js +25 -0
- data/templates/articles/README.md +40 -0
- data/templates/articles/fixtures/article.md +9 -0
- data/templates/articles/screenshots/README.md +6 -0
- data/templates/articles/studio.yml +73 -0
- data/templates/articles/template.yml +21 -0
- data/templates/bibliography/README.md +41 -0
- data/templates/bibliography/fixtures/references.json +9 -0
- data/templates/bibliography/screenshots/README.md +6 -0
- data/templates/bibliography/studio.yml +66 -0
- data/templates/bibliography/template.yml +18 -0
- data/templates/blog/README.md +38 -0
- data/templates/blog/fixtures/post.md +9 -0
- data/templates/blog/screenshots/README.md +6 -0
- data/templates/blog/studio.yml +69 -0
- data/templates/blog/template.yml +20 -0
- data/templates/cv/README.md +34 -0
- data/templates/cv/fixtures/job.md +12 -0
- data/templates/cv/screenshots/README.md +6 -0
- data/templates/cv/studio.yml +200 -0
- data/templates/cv/template.yml +25 -0
- data/templates/media/README.md +38 -0
- data/templates/media/fixtures/resource.md +7 -0
- data/templates/media/screenshots/README.md +6 -0
- data/templates/media/studio.yml +58 -0
- data/templates/media/template.yml +17 -0
- data/templates/network/README.md +34 -0
- data/templates/network/fixtures/sources.json +3 -0
- data/templates/network/screenshots/README.md +6 -0
- data/templates/network/studio.yml +28 -0
- data/templates/network/template.yml +10 -0
- data/templates/portfolio/README.md +39 -0
- data/templates/portfolio/fixtures/project.md +10 -0
- data/templates/portfolio/screenshots/README.md +6 -0
- data/templates/portfolio/studio.yml +89 -0
- data/templates/portfolio/template.yml +18 -0
- metadata +505 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{% if include.src %}
|
|
2
|
+
<figure class="plotly-figure" style="margin: 1.5rem auto; text-align: center; max-width: {% if include.width %}{{ include.width }}{% else %}100{% endif %}%;">
|
|
3
|
+
{% assign _cap = include.caption | strip %}
|
|
4
|
+
{% if _cap != "" %}
|
|
5
|
+
{% assign _last = _cap | slice: -1, 1 %}
|
|
6
|
+
{% unless _last == '.' or _last == '!' or _last == '?' %}
|
|
7
|
+
{% assign _cap = _cap | append: '.' %}
|
|
8
|
+
{% endunless %}
|
|
9
|
+
{% endif %}
|
|
10
|
+
<iframe
|
|
11
|
+
class="plotly-embed-frame"
|
|
12
|
+
src="{{ include.src }}"
|
|
13
|
+
title="{{ include.alt | default: include.caption | default: 'Interactive chart' }}"
|
|
14
|
+
loading="lazy"
|
|
15
|
+
scrolling="no"
|
|
16
|
+
style="width: 100%; height: {% if include.height %}{{ include.height }}{% else %}480{% endif %}px; border: 0; display: block; border-radius: 0; background: transparent;"
|
|
17
|
+
></iframe>
|
|
18
|
+
{% if include.caption or include.source %}
|
|
19
|
+
<figcaption>{% if _cap != "" %}{{ _cap }}{% endif %}{% if include.source %} Source: {{ include.source | strip }}{% endif %}</figcaption>
|
|
20
|
+
{% endif %}
|
|
21
|
+
</figure>
|
|
22
|
+
{% endif %}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
<div id="dev-reexport" class="contents">
|
|
2
|
+
<button id="dev-reexport-btn" type="button"
|
|
3
|
+
class="flex items-center justify-center w-7 h-7 p-1 border border-tertiary/40 hover:border-tertiary text-tertiary hover:bg-surface-container-low transition-colors duration-300 cursor-pointer shrink-0"
|
|
4
|
+
aria-label="Export content JSON"
|
|
5
|
+
title="Export">
|
|
6
|
+
<span id="dev-reexport-label" class="sr-only">Export</span>
|
|
7
|
+
<span id="dev-reexport-icon" class="material-symbols-outlined text-xl" aria-hidden="true">sync</span>
|
|
8
|
+
</button>
|
|
9
|
+
<button id="dev-open-studio-btn" type="button"
|
|
10
|
+
class="flex items-center justify-center w-7 h-7 p-1 border border-tertiary/40 hover:border-tertiary text-tertiary hover:bg-surface-container-low transition-colors duration-300 cursor-pointer shrink-0"
|
|
11
|
+
aria-label="Open Studio"
|
|
12
|
+
title="Open Studio"
|
|
13
|
+
data-studio-href="{{ site.baseurl }}/studio/">
|
|
14
|
+
<span class="sr-only">Open Studio</span>
|
|
15
|
+
<span class="material-symbols-outlined text-xl" aria-hidden="true">architecture</span>
|
|
16
|
+
</button>
|
|
17
|
+
</div>
|
|
18
|
+
<script>
|
|
19
|
+
(function () {
|
|
20
|
+
var openBtn = document.getElementById("dev-open-studio-btn");
|
|
21
|
+
if (openBtn) {
|
|
22
|
+
openBtn.addEventListener("click", function (e) {
|
|
23
|
+
e.preventDefault();
|
|
24
|
+
var href = openBtn.getAttribute("data-studio-href") || "/studio/";
|
|
25
|
+
window.open(href, "_blank");
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
var btn = document.getElementById("dev-reexport-btn");
|
|
30
|
+
var label = document.getElementById("dev-reexport-label");
|
|
31
|
+
var icon = document.getElementById("dev-reexport-icon");
|
|
32
|
+
if (!btn || !label || !icon) return;
|
|
33
|
+
|
|
34
|
+
var busy = false;
|
|
35
|
+
|
|
36
|
+
function forcePageRefresh() {
|
|
37
|
+
try {
|
|
38
|
+
var url = new URL(window.location.href);
|
|
39
|
+
url.searchParams.set("_refresh", String(Date.now()));
|
|
40
|
+
window.location.replace(url.toString());
|
|
41
|
+
} catch (err) {
|
|
42
|
+
window.location.reload();
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
btn.addEventListener("click", function (e) {
|
|
47
|
+
e.preventDefault();
|
|
48
|
+
if (busy) return;
|
|
49
|
+
busy = true;
|
|
50
|
+
btn.disabled = true;
|
|
51
|
+
btn.classList.add("is-busy");
|
|
52
|
+
btn.setAttribute("aria-busy", "true");
|
|
53
|
+
|
|
54
|
+
var originalText = label.textContent;
|
|
55
|
+
var originalIcon = icon.textContent;
|
|
56
|
+
var originalTitle = btn.title || "Export";
|
|
57
|
+
|
|
58
|
+
label.textContent = "Exporting";
|
|
59
|
+
btn.title = "Exporting...";
|
|
60
|
+
icon.textContent = "sync";
|
|
61
|
+
|
|
62
|
+
fetch("/__dev/reexport", {
|
|
63
|
+
method: "POST",
|
|
64
|
+
headers: { "Accept": "application/json" }
|
|
65
|
+
})
|
|
66
|
+
.then(function (response) {
|
|
67
|
+
return response.json().then(function (data) {
|
|
68
|
+
if (!response.ok || !data.ok) {
|
|
69
|
+
throw new Error((data && data.error) || ("Export failed: " + response.status));
|
|
70
|
+
}
|
|
71
|
+
return data;
|
|
72
|
+
});
|
|
73
|
+
})
|
|
74
|
+
.then(function () {
|
|
75
|
+
btn.classList.remove("is-busy");
|
|
76
|
+
icon.textContent = "check";
|
|
77
|
+
label.textContent = "Refreshed";
|
|
78
|
+
btn.title = "Refreshed!";
|
|
79
|
+
setTimeout(function () {
|
|
80
|
+
forcePageRefresh();
|
|
81
|
+
}, 200);
|
|
82
|
+
})
|
|
83
|
+
.catch(function (err) {
|
|
84
|
+
console.error("Local dev export error:", err);
|
|
85
|
+
btn.classList.remove("is-busy");
|
|
86
|
+
icon.textContent = "error";
|
|
87
|
+
label.textContent = "Failed";
|
|
88
|
+
btn.title = "Export failed";
|
|
89
|
+
setTimeout(function () {
|
|
90
|
+
icon.textContent = originalIcon;
|
|
91
|
+
label.textContent = originalText;
|
|
92
|
+
btn.title = originalTitle;
|
|
93
|
+
btn.disabled = false;
|
|
94
|
+
btn.removeAttribute("aria-busy");
|
|
95
|
+
busy = false;
|
|
96
|
+
}, 2500);
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
})();
|
|
100
|
+
</script>
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
<link rel="icon" href="/assets/icons/favicon.svg" type="image/svg+xml" sizes="any" />
|
|
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" />
|
|
4
|
+
<link rel="icon" href="/favicon.ico" sizes="16x16 32x32" />
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{% include theme/theme-context.liquid %}
|
|
2
|
+
{% assign font_title = sf.title | default: "Lora" %}
|
|
3
|
+
{% assign font_subtitle = sf.subtitle | default: "Lato" %}
|
|
4
|
+
{% assign font_label = sf.label | default: "Ysabeau SC" %}
|
|
5
|
+
{% assign font_body = sf.body | default: "Newsreader" %}
|
|
6
|
+
{% assign font_mono = sf.mono %}
|
|
7
|
+
{% assign font_code = sf.code %}
|
|
8
|
+
{% assign font_cv_body = sf.cv_body %}
|
|
9
|
+
{% assign title_slug = font_title | replace: ' ', '+' %}
|
|
10
|
+
{% assign subtitle_slug = font_subtitle | replace: ' ', '+' %}
|
|
11
|
+
{% assign label_slug = font_label | replace: ' ', '+' %}
|
|
12
|
+
{% assign body_slug = font_body | replace: ' ', '+' %}
|
|
13
|
+
{% assign mono_slug = font_mono | replace: ' ', '+' %}
|
|
14
|
+
{% assign code_slug = font_code | replace: ' ', '+' %}
|
|
15
|
+
{% assign cv_body_slug = font_cv_body | replace: ' ', '+' %}
|
|
16
|
+
{% assign title_spec = sf.google_title_spec | default: "ital,wght@0,400;0,500;0,700;1,400;1,500" %}
|
|
17
|
+
{% assign subtitle_spec = sf.google_subtitle_spec | default: "ital,wght@0,300;0,400;0,500;0,700;1,400;1,500" %}
|
|
18
|
+
{% assign label_spec = sf.google_label_spec | default: "ital,wght@0,400;0,500;0,700;1,400;1,500" %}
|
|
19
|
+
{% assign body_spec = sf.google_body_spec | default: "ital,wght@0,300;0,400;0,500;0,700;1,400;1,500;1,700" %}
|
|
20
|
+
{% assign mono_spec = sf.google_mono_spec | default: "wght@400;500;700" %}
|
|
21
|
+
{% assign code_spec = sf.google_code_spec | default: "wght@300;400;500;600;700" %}
|
|
22
|
+
{% assign cv_body_spec = sf.google_cv_body_spec | default: "wght@300;400;500;600;700" %}
|
|
23
|
+
{% comment %}
|
|
24
|
+
Build the Google Fonts URL without repeating a family. Title is always
|
|
25
|
+
requested. Body / label / subtitle / mono / code append only when their
|
|
26
|
+
family name differs from ones already on the URL.
|
|
27
|
+
{% endcomment %}
|
|
28
|
+
{% assign fonts_href = "https://fonts.googleapis.com/css2?family=" | append: title_slug | append: ":" | append: title_spec %}
|
|
29
|
+
{% unless font_label == font_title %}
|
|
30
|
+
{% assign fonts_href = fonts_href | append: "&family=" | append: label_slug | append: ":" | append: label_spec %}
|
|
31
|
+
{% endunless %}
|
|
32
|
+
{% unless font_body == font_title or font_body == font_label %}
|
|
33
|
+
{% assign fonts_href = fonts_href | append: "&family=" | append: body_slug | append: ":" | append: body_spec %}
|
|
34
|
+
{% endunless %}
|
|
35
|
+
{% if font_mono and font_mono != '' %}
|
|
36
|
+
{% unless font_mono == font_title or font_mono == font_label or font_mono == font_body %}
|
|
37
|
+
{% assign fonts_href = fonts_href | append: "&family=" | append: mono_slug | append: ":" | append: mono_spec %}
|
|
38
|
+
{% endunless %}
|
|
39
|
+
{% endif %}
|
|
40
|
+
{% if font_code and font_code != '' %}
|
|
41
|
+
{% unless font_code == font_title or font_code == font_label or font_code == font_body or font_code == font_mono %}
|
|
42
|
+
{% assign fonts_href = fonts_href | append: "&family=" | append: code_slug | append: ":" | append: code_spec %}
|
|
43
|
+
{% endunless %}
|
|
44
|
+
{% endif %}
|
|
45
|
+
{% if font_subtitle != font_body and font_subtitle != font_label and font_subtitle != font_title %}
|
|
46
|
+
{% assign fonts_href = fonts_href | append: "&family=" | append: subtitle_slug | append: ":" | append: subtitle_spec %}
|
|
47
|
+
{% endif %}
|
|
48
|
+
{% if font_cv_body and font_cv_body != '' %}
|
|
49
|
+
{% unless font_cv_body == font_title or font_cv_body == font_label or font_cv_body == font_body or font_cv_body == font_subtitle %}
|
|
50
|
+
{% assign fonts_href = fonts_href | append: "&family=" | append: cv_body_slug | append: ":" | append: cv_body_spec %}
|
|
51
|
+
{% endunless %}
|
|
52
|
+
{% endif %}
|
|
53
|
+
{% assign fonts_href = fonts_href | append: "&display=swap" %}
|
|
54
|
+
<link rel="preload" as="style" href="{{ fonts_href }}" />
|
|
55
|
+
<link href="{{ fonts_href }}" rel="stylesheet" />
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
(function () {
|
|
3
|
+
var config = {
|
|
4
|
+
active: {{ site.shell_theme.active | default: "system" | jsonify }},
|
|
5
|
+
storageKey: {{ site.shell_theme.storage_key | default: "site-theme" | jsonify }},
|
|
6
|
+
available: {{ site.shell_theme.available | default: site.shell_theme.active | jsonify }}
|
|
7
|
+
};
|
|
8
|
+
var root = document.documentElement;
|
|
9
|
+
var media = window.matchMedia("(prefers-color-scheme: dark)");
|
|
10
|
+
var paletteChoices = config.available.filter(function (name) {
|
|
11
|
+
return name === "light" || name === "dark";
|
|
12
|
+
});
|
|
13
|
+
if (paletteChoices.length === 0) {
|
|
14
|
+
paletteChoices = ["light"];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function resolvePalette(choice) {
|
|
18
|
+
if (choice === "system") {
|
|
19
|
+
return media.matches ? "dark" : "light";
|
|
20
|
+
}
|
|
21
|
+
if (paletteChoices.indexOf(choice) !== -1) {
|
|
22
|
+
return choice;
|
|
23
|
+
}
|
|
24
|
+
return paletteChoices[0];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function applyTheme(choice) {
|
|
28
|
+
var palette = resolvePalette(choice);
|
|
29
|
+
root.setAttribute("data-theme", palette);
|
|
30
|
+
root.style.colorScheme = palette;
|
|
31
|
+
if (window.ShellTheme) {
|
|
32
|
+
window.ShellTheme.active = choice;
|
|
33
|
+
window.ShellTheme.resolved = palette;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
var choice = config.active;
|
|
38
|
+
try {
|
|
39
|
+
var stored = localStorage.getItem(config.storageKey);
|
|
40
|
+
if (stored && config.available.indexOf(stored) !== -1) {
|
|
41
|
+
choice = stored;
|
|
42
|
+
}
|
|
43
|
+
} catch (e) { /* ignore */ }
|
|
44
|
+
|
|
45
|
+
applyTheme(choice);
|
|
46
|
+
|
|
47
|
+
window.ShellTheme = {
|
|
48
|
+
active: choice,
|
|
49
|
+
resolved: resolvePalette(choice),
|
|
50
|
+
storageKey: config.storageKey,
|
|
51
|
+
available: config.available.slice(),
|
|
52
|
+
paletteChoices: paletteChoices.slice(),
|
|
53
|
+
setTheme: function (name) {
|
|
54
|
+
if (config.available.indexOf(name) === -1) return;
|
|
55
|
+
try { localStorage.setItem(config.storageKey, name); } catch (e) { /* ignore */ }
|
|
56
|
+
this.active = name;
|
|
57
|
+
applyTheme(name);
|
|
58
|
+
document.dispatchEvent(new CustomEvent("shell-theme-change", {
|
|
59
|
+
detail: { choice: name, resolved: this.resolved }
|
|
60
|
+
}));
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
if (typeof media.addEventListener === "function") {
|
|
65
|
+
media.addEventListener("change", function () {
|
|
66
|
+
if (window.ShellTheme.active === "system") {
|
|
67
|
+
applyTheme("system");
|
|
68
|
+
document.dispatchEvent(new CustomEvent("shell-theme-change", {
|
|
69
|
+
detail: { choice: "system", resolved: window.ShellTheme.resolved }
|
|
70
|
+
}));
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
} else if (typeof media.addListener === "function") {
|
|
74
|
+
media.addListener(function () {
|
|
75
|
+
if (window.ShellTheme.active === "system") {
|
|
76
|
+
applyTheme("system");
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
})();
|
|
81
|
+
</script>
|
|
82
|
+
<script>
|
|
83
|
+
(function () {
|
|
84
|
+
var offsets = [-4, -3, -2, -1, 0, 1, 2, 3, 4];
|
|
85
|
+
var key = "site-prose-offset";
|
|
86
|
+
|
|
87
|
+
function read() {
|
|
88
|
+
try {
|
|
89
|
+
var n = Number(localStorage.getItem(key));
|
|
90
|
+
if (offsets.indexOf(n) !== -1) return n;
|
|
91
|
+
} catch (e) { /* ignore */ }
|
|
92
|
+
return 0;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function apply(n) {
|
|
96
|
+
if (offsets.indexOf(n) === -1) n = 0;
|
|
97
|
+
document.documentElement.style.setProperty("--prose-size-offset", n + "pt");
|
|
98
|
+
return n;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
window.ShellProseSize = {
|
|
102
|
+
offsets: offsets.slice(),
|
|
103
|
+
storageKey: key,
|
|
104
|
+
read: read,
|
|
105
|
+
apply: apply,
|
|
106
|
+
set: function (n) {
|
|
107
|
+
n = apply(Number(n));
|
|
108
|
+
try { localStorage.setItem(key, String(n)); } catch (e) { /* ignore */ }
|
|
109
|
+
return n;
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
apply(read());
|
|
114
|
+
})();
|
|
115
|
+
</script>
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
<svg class="site-logo" viewBox="0 0 32 32" role="img" aria-hidden="true" focusable="false">
|
|
2
|
+
<polygon class="site-logo-primary" points="0,0 23,0 15,17 0,20"/>
|
|
3
|
+
<polygon class="site-logo-secondary" points="23,0 32,0 32,32 17,21 15,17"/>
|
|
4
|
+
<polygon class="site-logo-tertiary" points="0,20 15,17 17,21 32,32 0,32"/>
|
|
5
|
+
<polygon class="site-logo-frame" points="1,1 31,1 31,31 1,31"/>
|
|
6
|
+
</svg>
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
{
|
|
2
|
+
"primary": "var(--color-primary)",
|
|
3
|
+
"secondary": "var(--color-secondary)",
|
|
4
|
+
"tertiary": "var(--color-tertiary)",
|
|
5
|
+
"primary-container": "var(--color-primary-container)",
|
|
6
|
+
"secondary-container": "var(--color-secondary-container)",
|
|
7
|
+
"tertiary-container": "var(--color-tertiary-container)",
|
|
8
|
+
"on-primary": "var(--color-on-primary)",
|
|
9
|
+
"on-secondary": "var(--color-on-secondary)",
|
|
10
|
+
"on-tertiary": "var(--color-on-tertiary)",
|
|
11
|
+
"on-primary-container": "var(--color-on-primary-container)",
|
|
12
|
+
"on-secondary-container": "var(--color-on-secondary-container)",
|
|
13
|
+
"on-tertiary-container": "var(--color-on-tertiary-container)",
|
|
14
|
+
"on-surface": "var(--color-on-surface)",
|
|
15
|
+
"on-surface-variant": "var(--color-on-surface-variant)",
|
|
16
|
+
"on-background": "var(--color-on-background)",
|
|
17
|
+
"outline": "var(--color-outline)",
|
|
18
|
+
"outline-variant": "var(--color-outline-variant)",
|
|
19
|
+
"gentle-faint": "var(--border-gentle-faint)",
|
|
20
|
+
"gentle-subtle": "var(--border-gentle-subtle)",
|
|
21
|
+
"gentle-divider": "var(--border-gentle-divider)",
|
|
22
|
+
"gentle": "var(--border-gentle)",
|
|
23
|
+
"gentle-hover": "var(--border-gentle-hover)",
|
|
24
|
+
"background": "var(--color-background)",
|
|
25
|
+
"background-secondary": "var(--color-background-secondary)",
|
|
26
|
+
"surface": "var(--color-surface)",
|
|
27
|
+
"surface-bright": "var(--color-surface-bright)",
|
|
28
|
+
"surface-container-lowest": "var(--color-surface-container-lowest)",
|
|
29
|
+
"surface-container-low": "var(--color-surface-container-low)",
|
|
30
|
+
"surface-container": "var(--color-surface-container)",
|
|
31
|
+
"surface-container-high": "var(--color-surface-container-high)",
|
|
32
|
+
"surface-container-highest": "var(--color-surface-container-highest)",
|
|
33
|
+
"surface-variant": "var(--color-surface-variant)",
|
|
34
|
+
"surface-dim": "var(--color-surface-dim)",
|
|
35
|
+
"panel-contrast": "var(--color-panel-contrast)",
|
|
36
|
+
"surface-card": "var(--color-surface-card)",
|
|
37
|
+
"surface-card-contrast": "var(--color-surface-card-contrast)",
|
|
38
|
+
"surface-high": "var(--color-surface-high)",
|
|
39
|
+
"surface-high-contrast": "var(--color-surface-high-contrast)",
|
|
40
|
+
"surface-tint": "var(--color-surface-tint)",
|
|
41
|
+
"inverse-primary": "var(--color-inverse-primary)",
|
|
42
|
+
"inverse-surface": "var(--color-inverse-surface)",
|
|
43
|
+
"inverse-on-surface": "var(--color-inverse-on-surface)",
|
|
44
|
+
"primary-fixed": "var(--color-primary-fixed)",
|
|
45
|
+
"primary-fixed-dim": "var(--color-primary-fixed-dim)",
|
|
46
|
+
"secondary-fixed": "var(--color-secondary-fixed)",
|
|
47
|
+
"secondary-fixed-dim": "var(--color-secondary-fixed-dim)",
|
|
48
|
+
"tertiary-fixed": "var(--color-tertiary-fixed)",
|
|
49
|
+
"tertiary-fixed-dim": "var(--color-tertiary-fixed-dim)",
|
|
50
|
+
"on-primary-fixed": "var(--color-on-primary-fixed)",
|
|
51
|
+
"on-primary-fixed-variant": "var(--color-on-primary-fixed-variant)",
|
|
52
|
+
"on-secondary-fixed": "var(--color-on-secondary-fixed)",
|
|
53
|
+
"on-secondary-fixed-variant": "var(--color-on-secondary-fixed-variant)",
|
|
54
|
+
"on-tertiary-fixed": "var(--color-on-tertiary-fixed)",
|
|
55
|
+
"on-tertiary-fixed-variant": "var(--color-on-tertiary-fixed-variant)",
|
|
56
|
+
"error": "var(--color-error)",
|
|
57
|
+
"on-error": "var(--color-on-error)",
|
|
58
|
+
"error-container": "var(--color-error-container)",
|
|
59
|
+
"on-error-container": "var(--color-on-error-container)",
|
|
60
|
+
"res-youtube": "var(--color-res-youtube)",
|
|
61
|
+
"res-podcast": "var(--color-res-podcast)",
|
|
62
|
+
"res-doc": "var(--color-res-doc)",
|
|
63
|
+
"link": "var(--color-link)",
|
|
64
|
+
"link-hover": "var(--color-link-hover)"
|
|
65
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
{% include theme/theme-context.liquid %}
|
|
2
|
+
{% assign font_title = sf.title | default: "Lora" %}
|
|
3
|
+
{% assign font_subtitle = sf.subtitle | default: "Lato" %}
|
|
4
|
+
{% assign font_label = sf.label | default: "Ysabeau SC" %}
|
|
5
|
+
{% assign font_body = sf.body | default: "Newsreader" %}
|
|
6
|
+
{"title":["var(--font-title)"],"subtitle":["var(--font-subtitle)","serif"],"label":["var(--font-label)","serif"],"body":["var(--font-body)","sans-serif"],"headline":["var(--font-title)"],"editorial":["var(--font-title)"],"serif":["var(--font-title)"],"sans":["var(--font-body)","sans-serif"],"mono":["var(--font-mono)"],"code":["var(--font-code)"]}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{% comment %}
|
|
2
|
+
Resolves the active colour theme from _config.yml → shell_theme.active and
|
|
3
|
+
_data/themes/<name>.yml. Fonts and other non-colour tokens come from
|
|
4
|
+
_data/themes/shared.yml so light/dark cannot drift apart on type.
|
|
5
|
+
Sets tc, tn, tl, sf (colors, neutrals, links, fonts), ts (static),
|
|
6
|
+
sm (motion), sg (geometry).
|
|
7
|
+
{% endcomment %}
|
|
8
|
+
{% assign theme_active = site.shell_theme.active | default: "system" %}
|
|
9
|
+
{% assign theme_profile = site.data.themes[theme_active] %}
|
|
10
|
+
{% if theme_profile == nil %}
|
|
11
|
+
{% assign theme_profile = site.data.themes.light %}
|
|
12
|
+
{% endif %}
|
|
13
|
+
{% assign theme_shared = site.data.themes.shared %}
|
|
14
|
+
{% assign tc = theme_profile.colors %}
|
|
15
|
+
{% assign tn = theme_profile.neutrals %}
|
|
16
|
+
{% assign tl = theme_profile.links %}
|
|
17
|
+
{% assign sf = theme_shared.fonts %}
|
|
18
|
+
{% assign sm = theme_shared.motion %}
|
|
19
|
+
{% assign sg = theme_shared.geometry %}
|
|
20
|
+
{% assign ts = theme_profile.static %}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
<div id="theme-toggle" class="relative flex items-center shrink-0">
|
|
2
|
+
<button id="theme-toggle-btn" type="button"
|
|
3
|
+
class="flex items-center justify-center w-7 h-7 p-1 text-on-surface-variant hover:text-primary transition-colors duration-300"
|
|
4
|
+
aria-label="Theme"
|
|
5
|
+
title="Theme"
|
|
6
|
+
aria-haspopup="true" aria-expanded="false" aria-controls="theme-toggle-menu">
|
|
7
|
+
<span class="sr-only">Theme</span>
|
|
8
|
+
<span id="theme-toggle-icon" class="material-symbols-outlined text-xl" aria-hidden="true">light_mode</span>
|
|
9
|
+
<span id="theme-toggle-state" class="sr-only">current: light</span>
|
|
10
|
+
</button>
|
|
11
|
+
<div id="theme-toggle-menu"
|
|
12
|
+
class="hidden absolute right-0 top-full mt-1 min-w-[9.5rem] py-1 bg-surface border border-gentle-subtle shadow-sm z-50"
|
|
13
|
+
role="menu" aria-label="Choose color theme">
|
|
14
|
+
<button type="button" role="menuitemradio" data-theme-choice="light"
|
|
15
|
+
class="theme-choice w-full flex items-center gap-2.5 px-3 py-2 text-left font-body text-on-surface-variant hover:text-primary hover:bg-surface-container-low transition-colors leading-none">
|
|
16
|
+
<span class="material-symbols-outlined text-lg shrink-0 flex items-center justify-center" aria-hidden="true">light_mode</span>
|
|
17
|
+
<span class="inline-flex items-center leading-none">Light</span>
|
|
18
|
+
</button>
|
|
19
|
+
<button type="button" role="menuitemradio" data-theme-choice="dark"
|
|
20
|
+
class="theme-choice w-full flex items-center gap-2.5 px-3 py-2 text-left font-body text-on-surface-variant hover:text-primary hover:bg-surface-container-low transition-colors leading-none">
|
|
21
|
+
<span class="material-symbols-outlined text-lg shrink-0 flex items-center justify-center" aria-hidden="true">dark_mode</span>
|
|
22
|
+
<span class="inline-flex items-center leading-none">Dark</span>
|
|
23
|
+
</button>
|
|
24
|
+
<button type="button" role="menuitemradio" data-theme-choice="system"
|
|
25
|
+
class="theme-choice w-full flex items-center gap-2.5 px-3 py-2 text-left font-body text-on-surface-variant hover:text-primary hover:bg-surface-container-low transition-colors leading-none">
|
|
26
|
+
<span class="material-symbols-outlined text-lg shrink-0 flex items-center justify-center" aria-hidden="true">routine</span>
|
|
27
|
+
<span class="inline-flex items-center leading-none">System</span>
|
|
28
|
+
</button>
|
|
29
|
+
</div>
|
|
30
|
+
</div>
|
|
31
|
+
<script>
|
|
32
|
+
(function () {
|
|
33
|
+
var icons = { light: "light_mode", dark: "dark_mode", system: "routine" };
|
|
34
|
+
// The visible "Theme" word is the accessible name now; this only appends
|
|
35
|
+
// the current choice for screen readers.
|
|
36
|
+
var states = { light: "current: light", dark: "current: dark", system: "current: system" };
|
|
37
|
+
|
|
38
|
+
function syncToggle() {
|
|
39
|
+
var theme = window.ShellTheme;
|
|
40
|
+
if (!theme) return;
|
|
41
|
+
var active = theme.active;
|
|
42
|
+
var iconEl = document.getElementById("theme-toggle-icon");
|
|
43
|
+
var stateEl = document.getElementById("theme-toggle-state");
|
|
44
|
+
if (iconEl) iconEl.textContent = icons[active] || icons.light;
|
|
45
|
+
if (stateEl) stateEl.textContent = states[active] || states.light;
|
|
46
|
+
document.querySelectorAll(".theme-choice").forEach(function (el) {
|
|
47
|
+
var choice = el.getAttribute("data-theme-choice");
|
|
48
|
+
var selected = choice === active;
|
|
49
|
+
el.setAttribute("aria-checked", String(selected));
|
|
50
|
+
el.classList.toggle("text-primary", selected);
|
|
51
|
+
el.classList.toggle("font-semibold", selected);
|
|
52
|
+
el.classList.toggle("bg-surface-container-low", selected);
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function init() {
|
|
57
|
+
var btn = document.getElementById("theme-toggle-btn");
|
|
58
|
+
var menu = document.getElementById("theme-toggle-menu");
|
|
59
|
+
if (!btn || !menu || !window.ShellTheme) return;
|
|
60
|
+
|
|
61
|
+
syncToggle();
|
|
62
|
+
|
|
63
|
+
btn.addEventListener("click", function (e) {
|
|
64
|
+
e.stopPropagation();
|
|
65
|
+
var open = !menu.classList.contains("hidden");
|
|
66
|
+
menu.classList.toggle("hidden", open);
|
|
67
|
+
btn.setAttribute("aria-expanded", String(!open));
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
menu.querySelectorAll(".theme-choice").forEach(function (el) {
|
|
71
|
+
el.addEventListener("click", function () {
|
|
72
|
+
var choice = el.getAttribute("data-theme-choice");
|
|
73
|
+
window.ShellTheme.setTheme(choice);
|
|
74
|
+
syncToggle();
|
|
75
|
+
menu.classList.add("hidden");
|
|
76
|
+
btn.setAttribute("aria-expanded", "false");
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
document.addEventListener("click", function () {
|
|
81
|
+
menu.classList.add("hidden");
|
|
82
|
+
btn.setAttribute("aria-expanded", "false");
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
document.addEventListener("shell-theme-change", syncToggle);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (document.readyState === "loading") {
|
|
89
|
+
document.addEventListener("DOMContentLoaded", init);
|
|
90
|
+
} else {
|
|
91
|
+
init();
|
|
92
|
+
}
|
|
93
|
+
})();
|
|
94
|
+
</script>
|