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,264 @@
|
|
|
1
|
+
{% raw %}
|
|
2
|
+
function preprocessWpBlockMath(markdown) {
|
|
3
|
+
return String(markdown || '').replace(
|
|
4
|
+
/<div\s+class="wp-block-math"[^>]*>\s*<math([^>]*)>[\s\S]*?<annotation\s+encoding="application\/x-tex">([\s\S]*?)<\/annotation>[\s\S]*?<\/math>\s*<\/div>/gi,
|
|
5
|
+
function (_, mathAttrs, tex) {
|
|
6
|
+
var trimmed = String(tex || '').trim();
|
|
7
|
+
if (!trimmed) return '';
|
|
8
|
+
// Prefer the MathML display attribute; CMS-exported block math is display="block".
|
|
9
|
+
var attrDisplay = /\bdisplay\s*=\s*["']block["']/i.test(String(mathAttrs || ''));
|
|
10
|
+
var useDisplay = attrDisplay
|
|
11
|
+
|| /\\begin\{|\\(?:lim|int|sum|prod|frac)(?![a-zA-Z])|\\\\/.test(trimmed)
|
|
12
|
+
|| trimmed.length > 72;
|
|
13
|
+
// CMS Markdown often eats \, \; \! \: (backslash + punctuation). Prefer
|
|
14
|
+
// letter-named spacing. Bare ";" left by a corrupted \; becomes \thinspace.
|
|
15
|
+
trimmed = trimmed.replace(/([A-Za-z0-9)}\]|])\s*;\s*(?=\\?[A-Za-z])/g, '$1\\thinspace ');
|
|
16
|
+
return useDisplay ? ('\n\n$$' + trimmed + '$$\n\n') : ('$' + trimmed + '$');
|
|
17
|
+
}
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Repair `\\` row breaks that editorial/CMS pipelines reduced to `\` or `\ `.
|
|
22
|
+
// Keep in sync with scripts/lib/normalize-display-math.mjs.
|
|
23
|
+
function normalizeDisplayMathTex(math) {
|
|
24
|
+
var text = String(math || '');
|
|
25
|
+
var rowEnv = 'align\\*?|aligned|gather\\*?|eqnarray\\*?|split|cases|matrix|bmatrix|pmatrix|vmatrix|Vmatrix|smallmatrix|array';
|
|
26
|
+
text = text.split('\n').map(function (line) {
|
|
27
|
+
var match = line.match(/(\\+)([ \t]*)$/);
|
|
28
|
+
if (!match) return line;
|
|
29
|
+
if (match[1].length % 2 === 0) return line;
|
|
30
|
+
return line.slice(0, line.length - match[0].length) + match[1] + '\\' + match[2];
|
|
31
|
+
}).join('\n');
|
|
32
|
+
text = text.replace(new RegExp('\\\\begin\\{(' + rowEnv + ')\\}([\\s\\S]*?)\\\\end\\{\\1\\}', 'g'), function (full, env, body) {
|
|
33
|
+
var fixed = body.replace(/(^|[^\\])\\([ \t]+)/g, function (_, prefix, ws) {
|
|
34
|
+
return prefix + '\\\\' + ws;
|
|
35
|
+
});
|
|
36
|
+
return '\\begin{' + env + '}' + fixed + '\\end{' + env + '}';
|
|
37
|
+
});
|
|
38
|
+
return text;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function protectMath(markdown) {
|
|
42
|
+
var store = [];
|
|
43
|
+
var protectedText = String(markdown || '');
|
|
44
|
+
|
|
45
|
+
protectedText = protectedText.replace(/\$\$([\s\S]+?)\$\$/g, function (_, math) {
|
|
46
|
+
var token = '@@CONTENT_RUNTIME_MATH_' + store.length + '@@';
|
|
47
|
+
store.push({ display: true, math: normalizeDisplayMathTex(math) });
|
|
48
|
+
return token;
|
|
49
|
+
});
|
|
50
|
+
protectedText = protectedText.replace(/\\\[([\s\S]+?)\\\]/g, function (_, math) {
|
|
51
|
+
var token = '@@CONTENT_RUNTIME_MATH_' + store.length + '@@';
|
|
52
|
+
store.push({ display: true, math: normalizeDisplayMathTex(math) });
|
|
53
|
+
return token;
|
|
54
|
+
});
|
|
55
|
+
protectedText = protectedText.replace(/\\\(([\s\S]+?)\\\)/g, function (_, math) {
|
|
56
|
+
var token = '@@CONTENT_RUNTIME_MATH_' + store.length + '@@';
|
|
57
|
+
store.push({ display: false, math: math });
|
|
58
|
+
return token;
|
|
59
|
+
});
|
|
60
|
+
protectedText = protectedText.replace(/(^|[^\\$])\$(?!\$)([^\$\n]+?)\$(?!\$)/g, function (match, prefix, math) {
|
|
61
|
+
var token = '@@CONTENT_RUNTIME_MATH_' + store.length + '@@';
|
|
62
|
+
store.push({ display: false, math: math });
|
|
63
|
+
return prefix + token;
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
return { text: protectedText, store: store };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function restoreMath(html, store) {
|
|
70
|
+
store.forEach(function (entry, index) {
|
|
71
|
+
var replacement = entry.display ? ('$$' + entry.math + '$$') : ('$' + entry.math + '$');
|
|
72
|
+
html = html.split('@@CONTENT_RUNTIME_MATH_' + index + '@@').join(replacement);
|
|
73
|
+
});
|
|
74
|
+
return unwrapDisplayMath(html);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function unwrapDisplayMath(html) {
|
|
78
|
+
return String(html || '')
|
|
79
|
+
.replace(/<p>\s*(\$\$[\s\S]*?\$\$)\s*<\/p>/g, '<div class="math-display">$1</div>')
|
|
80
|
+
.replace(/<p>\s*(\\\[[\s\S]*?\\\])\s*<\/p>/g, '<div class="math-display">$1</div>');
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function hasMarkdownAttr(openTag) {
|
|
84
|
+
return /\bmarkdown\s*=\s*["']1["']/i.test(String(openTag || ''));
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function parseInlineMarkdown(markdown) {
|
|
88
|
+
var prepared = preprocessWpBlockMath(String(markdown || '').trim());
|
|
89
|
+
var math = protectMath(prepared);
|
|
90
|
+
var html;
|
|
91
|
+
if (window.marked && typeof window.marked.parse === 'function') {
|
|
92
|
+
html = window.marked.parse(math.text, { gfm: true, breaks: false });
|
|
93
|
+
} else {
|
|
94
|
+
html = '<p>' + escapeHtml(math.text).replace(/\n/g, ' ') + '</p>';
|
|
95
|
+
}
|
|
96
|
+
return restoreMath(html, math.store);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function renderPoemHtml(inner) {
|
|
100
|
+
var stanzas = String(inner || '').replace(/^\s+|\s+$/g, '').split(/\n\s*\n/);
|
|
101
|
+
var parts = ['<div class="poem">'];
|
|
102
|
+
stanzas.forEach(function (stanza) {
|
|
103
|
+
var text = stanza.trim();
|
|
104
|
+
if (!text) return;
|
|
105
|
+
// One paragraph per stanza/refrain; single newlines are verse lines (pre-line in CSS).
|
|
106
|
+
parts.push('<p>' + escapeHtml(text) + '</p>');
|
|
107
|
+
});
|
|
108
|
+
parts.push('</div>');
|
|
109
|
+
return parts.join('');
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function preprocessMarkdownDivs(markdown) {
|
|
113
|
+
var text = String(markdown || '');
|
|
114
|
+
text = text.replace(/<div\s+([^>]*class="[^"]*\barticle-note\b[^"]*"[^>]*)>([\s\S]*?)<\/div>/gi, function (match, openTag, inner) {
|
|
115
|
+
if (!hasMarkdownAttr(openTag)) return match;
|
|
116
|
+
return '<div class="article-note">' + parseInlineMarkdown(inner) + '</div>';
|
|
117
|
+
});
|
|
118
|
+
text = text.replace(/<div\s+([^>]*class="[^"]*\bpoem\b[^"]*"[^>]*)>([\s\S]*?)<\/div>/gi, function (match, openTag, inner) {
|
|
119
|
+
if (!hasMarkdownAttr(openTag)) return match;
|
|
120
|
+
return renderPoemHtml(inner);
|
|
121
|
+
});
|
|
122
|
+
return text;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function renderMarkdown(markdown, options) {
|
|
126
|
+
options = options || {};
|
|
127
|
+
var prepared = preprocessLiquid(markdown, options.xciteIndex);
|
|
128
|
+
prepared = preprocessWpBlockMath(prepared);
|
|
129
|
+
prepared = preprocessMarkdownDivs(prepared);
|
|
130
|
+
var math = protectMath(prepared);
|
|
131
|
+
var html;
|
|
132
|
+
if (window.marked && typeof window.marked.parse === 'function') {
|
|
133
|
+
html = window.marked.parse(math.text, { gfm: true, breaks: false });
|
|
134
|
+
} else {
|
|
135
|
+
html = math.text
|
|
136
|
+
.split(/\n\s*\n/)
|
|
137
|
+
.map(function (block) { return block.trim(); })
|
|
138
|
+
.filter(Boolean)
|
|
139
|
+
.map(function (block) { return '<p>' + escapeHtml(block).replace(/\n/g, '<br>') + '</p>'; })
|
|
140
|
+
.join('');
|
|
141
|
+
}
|
|
142
|
+
html = restoreMath(html, math.store);
|
|
143
|
+
if (options.citedKeys && options.citedKeys.length) {
|
|
144
|
+
html += buildReferencesSection(options.citedKeys, options.bibIndex, options.lang);
|
|
145
|
+
}
|
|
146
|
+
if (options.citedKeys && options.citedKeys.length) {
|
|
147
|
+
html = enhanceReferencesHtml(html);
|
|
148
|
+
}
|
|
149
|
+
return html;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
async function waitForMathJax() {
|
|
153
|
+
var mj = window.MathJax;
|
|
154
|
+
if (!mj) return false;
|
|
155
|
+
if (mj.startup && mj.startup.promise) {
|
|
156
|
+
try {
|
|
157
|
+
await mj.startup.promise;
|
|
158
|
+
} catch (error) {
|
|
159
|
+
console.error(error);
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return typeof mj.typesetPromise === 'function';
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
async function typesetMath(root) {
|
|
167
|
+
try {
|
|
168
|
+
if (!(await waitForMathJax())) return;
|
|
169
|
+
await window.MathJax.typesetPromise(root ? [root] : undefined);
|
|
170
|
+
} catch (error) {
|
|
171
|
+
console.error(error);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
var mermaidReady = null;
|
|
176
|
+
|
|
177
|
+
/* Mermaid bakes its palette into the SVG at render time, so a diagram
|
|
178
|
+
drawn under one theme keeps those colours when the reader switches.
|
|
179
|
+
That is why diagrams showed pale nodes and cluster backgrounds on a
|
|
180
|
+
dark page. The config is rebuilt from the live custom properties on
|
|
181
|
+
every draw, and every diagram is redrawn when the theme changes. */
|
|
182
|
+
function mermaidConfig() {
|
|
183
|
+
return {
|
|
184
|
+
startOnLoad: false,
|
|
185
|
+
theme: 'base',
|
|
186
|
+
darkMode: document.documentElement.getAttribute('data-theme') === 'dark',
|
|
187
|
+
themeVariables: {
|
|
188
|
+
fontFamily: themeFont(),
|
|
189
|
+
background: themeColor('background', '#ffffff'),
|
|
190
|
+
mainBkg: themeColor('surface', '#f1f1ef'),
|
|
191
|
+
primaryColor: themeColor('surface', '#f1f1ef'),
|
|
192
|
+
primaryTextColor: themeColor('on-surface', '#1b1c15'),
|
|
193
|
+
primaryBorderColor: themeColor('outline-variant', '#c4ccc6'),
|
|
194
|
+
secondaryColor: themeColor('panel-contrast', '#f7f7f5'),
|
|
195
|
+
secondaryTextColor: themeColor('on-surface', '#1b1c15'),
|
|
196
|
+
secondaryBorderColor: themeColor('outline-variant', '#c4ccc6'),
|
|
197
|
+
tertiaryColor: themeColor('surface-high', '#e8e8e4'),
|
|
198
|
+
tertiaryTextColor: themeColor('on-surface', '#1b1c15'),
|
|
199
|
+
tertiaryBorderColor: themeColor('outline-variant', '#c4ccc6'),
|
|
200
|
+
lineColor: themeColor('outline', '#6b7569'),
|
|
201
|
+
textColor: themeColor('on-surface', '#1b1c15'),
|
|
202
|
+
nodeBorder: themeColor('outline-variant', '#c4ccc6'),
|
|
203
|
+
nodeTextColor: themeColor('on-surface', '#1b1c15'),
|
|
204
|
+
clusterBkg: themeColor('panel-contrast', '#f7f7f5'),
|
|
205
|
+
clusterBorder: themeColor('outline-variant', '#c4ccc6'),
|
|
206
|
+
titleColor: themeColor('on-surface', '#1b1c15'),
|
|
207
|
+
edgeLabelBackground: themeColor('background', '#ffffff'),
|
|
208
|
+
labelBackground: themeColor('background', '#ffffff'),
|
|
209
|
+
labelTextColor: themeColor('on-surface', '#1b1c15'),
|
|
210
|
+
fontSize: '14px'
|
|
211
|
+
},
|
|
212
|
+
flowchart: {
|
|
213
|
+
diagramPadding: 16,
|
|
214
|
+
padding: 16,
|
|
215
|
+
subGraphTitleMargin: { top: 8, bottom: 0 }
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function ensureMermaid() {
|
|
221
|
+
if (window.mermaid) return Promise.resolve(window.mermaid);
|
|
222
|
+
if (mermaidReady) return mermaidReady;
|
|
223
|
+
mermaidReady = import('https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs')
|
|
224
|
+
.then(function (mod) {
|
|
225
|
+
window.mermaid = mod.default;
|
|
226
|
+
window.mermaid.initialize(mermaidConfig());
|
|
227
|
+
return window.mermaid;
|
|
228
|
+
})
|
|
229
|
+
.catch(function (error) {
|
|
230
|
+
mermaidReady = null;
|
|
231
|
+
throw error;
|
|
232
|
+
});
|
|
233
|
+
return mermaidReady;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/* Rendering replaces the node's text with an SVG, so the source is kept on
|
|
237
|
+
the element to make a redraw possible. */
|
|
238
|
+
function redrawMermaid() {
|
|
239
|
+
if (!window.mermaid) return;
|
|
240
|
+
var nodes = Array.prototype.slice.call(document.querySelectorAll('.mermaid'))
|
|
241
|
+
.filter(function (node) { return node.dataset.mermaidSource; });
|
|
242
|
+
if (!nodes.length) return;
|
|
243
|
+
nodes.forEach(function (node) {
|
|
244
|
+
node.removeAttribute('data-processed');
|
|
245
|
+
node.innerHTML = '';
|
|
246
|
+
node.textContent = node.dataset.mermaidSource;
|
|
247
|
+
});
|
|
248
|
+
window.mermaid.initialize(mermaidConfig());
|
|
249
|
+
if (typeof window.mermaid.run === 'function') {
|
|
250
|
+
window.mermaid.run({ nodes: nodes }).catch(function (error) {
|
|
251
|
+
console.error('Mermaid redraw failed:', error);
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
document.addEventListener('shell-theme-change', redrawMermaid);
|
|
257
|
+
|
|
258
|
+
function translate(key, lang) {
|
|
259
|
+
var table = (window.ContentRuntimeConfig && window.ContentRuntimeConfig.translations) || {};
|
|
260
|
+
var bucket = table[key] || {};
|
|
261
|
+
return bucket[lang] || bucket.enus || '';
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
{% endraw %}
|
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
{% raw %}
|
|
2
|
+
/* ══ Code blocks ══
|
|
3
|
+
`marked` renders a fence as a bare `<pre><code class="language-x">`, so a
|
|
4
|
+
reader met a grey box with no indication of what was in it. Every fenced
|
|
5
|
+
block in the prose is given the chrome an editor gives a file: a bar
|
|
6
|
+
across the top with the language on a tab and a copy control on the
|
|
7
|
+
right, and the pane below it.
|
|
8
|
+
|
|
9
|
+
This runs on the rendered DOM rather than through a marked extension.
|
|
10
|
+
The same three lines of markup then cover both render paths — the public
|
|
11
|
+
detail page, which assembles in the browser, and the studio build, where
|
|
12
|
+
kramdown emits the identical `pre > code.language-*`. A marked extension
|
|
13
|
+
would only ever have reached the first. */
|
|
14
|
+
|
|
15
|
+
/* Fence tokens are whatever an author types, so the label is a lookup with
|
|
16
|
+
a fallback rather than a closed set: an unlisted token is uppercased and
|
|
17
|
+
shown as itself, which is right often enough (`toml`, `sql`) and honest
|
|
18
|
+
the rest of the time. */
|
|
19
|
+
var CODE_LANGUAGE_NAMES = {
|
|
20
|
+
ansi: 'Terminal',
|
|
21
|
+
bash: 'Bash',
|
|
22
|
+
bibtex: 'BibTeX',
|
|
23
|
+
c: 'C',
|
|
24
|
+
cmake: 'CMake',
|
|
25
|
+
console: 'Console',
|
|
26
|
+
cpp: 'C++',
|
|
27
|
+
css: 'CSS',
|
|
28
|
+
diff: 'Diff',
|
|
29
|
+
docker: 'Dockerfile',
|
|
30
|
+
dockerfile: 'Dockerfile',
|
|
31
|
+
go: 'Go',
|
|
32
|
+
html: 'HTML',
|
|
33
|
+
ini: 'INI',
|
|
34
|
+
java: 'Java',
|
|
35
|
+
js: 'JavaScript',
|
|
36
|
+
javascript: 'JavaScript',
|
|
37
|
+
json: 'JSON',
|
|
38
|
+
jsx: 'JSX',
|
|
39
|
+
kotlin: 'Kotlin',
|
|
40
|
+
liquid: 'Liquid',
|
|
41
|
+
lua: 'Lua',
|
|
42
|
+
make: 'Makefile',
|
|
43
|
+
makefile: 'Makefile',
|
|
44
|
+
markdown: 'Markdown',
|
|
45
|
+
matlab: 'MATLAB',
|
|
46
|
+
md: 'Markdown',
|
|
47
|
+
mermaid: 'Mermaid',
|
|
48
|
+
php: 'PHP',
|
|
49
|
+
powershell: 'PowerShell',
|
|
50
|
+
ps1: 'PowerShell',
|
|
51
|
+
pwsh: 'PowerShell',
|
|
52
|
+
py: 'Python',
|
|
53
|
+
python: 'Python',
|
|
54
|
+
r: 'R',
|
|
55
|
+
rb: 'Ruby',
|
|
56
|
+
ruby: 'Ruby',
|
|
57
|
+
rs: 'Rust',
|
|
58
|
+
rust: 'Rust',
|
|
59
|
+
scss: 'SCSS',
|
|
60
|
+
sh: 'Shell',
|
|
61
|
+
shell: 'Shell',
|
|
62
|
+
sql: 'SQL',
|
|
63
|
+
svelte: 'Svelte',
|
|
64
|
+
swift: 'Swift',
|
|
65
|
+
text: 'Text',
|
|
66
|
+
toml: 'TOML',
|
|
67
|
+
ts: 'TypeScript',
|
|
68
|
+
typescript: 'TypeScript',
|
|
69
|
+
tsx: 'TSX',
|
|
70
|
+
vim: 'Vim',
|
|
71
|
+
xml: 'XML',
|
|
72
|
+
yaml: 'YAML',
|
|
73
|
+
yml: 'YAML',
|
|
74
|
+
zsh: 'Zsh'
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
function codeLanguageLabel(token) {
|
|
78
|
+
var key = String(token || '').trim().toLowerCase();
|
|
79
|
+
if (!key) return '';
|
|
80
|
+
return CODE_LANGUAGE_NAMES[key] || key.toUpperCase();
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function codeLanguageToken(code) {
|
|
84
|
+
var match = /(?:^|\s)language-([\w+#.-]+)/.exec(code.className || '');
|
|
85
|
+
return match ? match[1].toLowerCase() : '';
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/* ── ANSI colour ──
|
|
89
|
+
A `ansi` fence carries a real terminal capture, escape codes and all, so
|
|
90
|
+
`neofetch` and friends can be shown the way they actually look rather
|
|
91
|
+
than as the grey text a screenshot-free article is otherwise limited to.
|
|
92
|
+
|
|
93
|
+
The escapes are read in their printable spellings — `\e[31m`, `\033[31m`,
|
|
94
|
+
`\x1b[31m`, `[31m` — as well as the real 0x1b control byte. The
|
|
95
|
+
printable forms are what a Markdown file can actually hold: an editor,
|
|
96
|
+
a JSON export and a diff all survive a backslash and an `e`, and none of
|
|
97
|
+
them reliably survives a raw control character. The control byte is
|
|
98
|
+
accepted anyway, for output pasted straight out of a pipe. A bare
|
|
99
|
+
`[31m` is deliberately not read as an escape: it is ordinary prose often
|
|
100
|
+
enough that matching it would repaint text nobody coloured.
|
|
101
|
+
|
|
102
|
+
Only SGR (`m`) is honoured. A capture that moves the cursor to draw
|
|
103
|
+
columns — which is exactly what neofetch does to put its info block
|
|
104
|
+
beside the logo — has to be flattened before it is pasted, because
|
|
105
|
+
there is no cursor here to move. Anything else is dropped rather than
|
|
106
|
+
printed, so a stray sequence never appears as literal text. */
|
|
107
|
+
var ANSI_PATTERN = /(?:\x1b|\\(?:e|x1b|X1B|033|u001b|u001B))\[([0-9;]*)([A-Za-z])/g;
|
|
108
|
+
|
|
109
|
+
/* The sixteen base colours are CSS custom properties rather than literals,
|
|
110
|
+
so the palette lives in the stylesheet beside the terminal ground it is
|
|
111
|
+
drawn against. Indices past 15 are the 6×6×6 cube and the 24-step grey
|
|
112
|
+
ramp, which are defined arithmetically by the standard and so are
|
|
113
|
+
computed rather than tabulated. */
|
|
114
|
+
function ansiColor(index) {
|
|
115
|
+
var n = Number(index);
|
|
116
|
+
if (!(n >= 0 && n <= 255)) return '';
|
|
117
|
+
if (n < 16) return 'var(--ansi-' + n + ')';
|
|
118
|
+
if (n < 232) {
|
|
119
|
+
var offset = n - 16;
|
|
120
|
+
var steps = [0, 95, 135, 175, 215, 255];
|
|
121
|
+
var rgb = [
|
|
122
|
+
steps[Math.floor(offset / 36) % 6],
|
|
123
|
+
steps[Math.floor(offset / 6) % 6],
|
|
124
|
+
steps[offset % 6]
|
|
125
|
+
];
|
|
126
|
+
return 'rgb(' + rgb.join(', ') + ')';
|
|
127
|
+
}
|
|
128
|
+
var level = 8 + (n - 232) * 10;
|
|
129
|
+
return 'rgb(' + level + ', ' + level + ', ' + level + ')';
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function newAnsiState() {
|
|
133
|
+
return { fg: null, bg: null, bold: false, dim: false, italic: false, underline: false, inverse: false };
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function applyAnsiSgr(state, params) {
|
|
137
|
+
var codes = params === '' ? ['0'] : params.split(';');
|
|
138
|
+
for (var i = 0; i < codes.length; i++) {
|
|
139
|
+
var n = parseInt(codes[i], 10);
|
|
140
|
+
if (isNaN(n) || n === 0) {
|
|
141
|
+
var fresh = newAnsiState();
|
|
142
|
+
state.fg = fresh.fg; state.bg = fresh.bg; state.bold = false;
|
|
143
|
+
state.dim = false; state.italic = false; state.underline = false; state.inverse = false;
|
|
144
|
+
} else if (n === 1) state.bold = true;
|
|
145
|
+
else if (n === 2) state.dim = true;
|
|
146
|
+
else if (n === 3) state.italic = true;
|
|
147
|
+
else if (n === 4) state.underline = true;
|
|
148
|
+
else if (n === 7) state.inverse = true;
|
|
149
|
+
else if (n === 22) { state.bold = false; state.dim = false; }
|
|
150
|
+
else if (n === 23) state.italic = false;
|
|
151
|
+
else if (n === 24) state.underline = false;
|
|
152
|
+
else if (n === 27) state.inverse = false;
|
|
153
|
+
else if (n === 39) state.fg = null;
|
|
154
|
+
else if (n === 49) state.bg = null;
|
|
155
|
+
else if (n >= 30 && n <= 37) state.fg = n - 30;
|
|
156
|
+
else if (n >= 90 && n <= 97) state.fg = n - 90 + 8;
|
|
157
|
+
else if (n >= 40 && n <= 47) state.bg = n - 40;
|
|
158
|
+
else if (n >= 100 && n <= 107) state.bg = n - 100 + 8;
|
|
159
|
+
else if (n === 38 || n === 48) {
|
|
160
|
+
// Extended selectors: `5;N` picks out of the 256-colour table,
|
|
161
|
+
// `2;R;G;B` states a colour outright. Both are consumed here so
|
|
162
|
+
// their arguments are never mistaken for further attributes.
|
|
163
|
+
var target = n === 38 ? 'fg' : 'bg';
|
|
164
|
+
if (codes[i + 1] === '5') { state[target] = parseInt(codes[i + 2], 10); i += 2; }
|
|
165
|
+
else if (codes[i + 1] === '2') {
|
|
166
|
+
state[target] = 'rgb(' + [codes[i + 2], codes[i + 3], codes[i + 4]].map(function (v) {
|
|
167
|
+
return parseInt(v, 10) || 0;
|
|
168
|
+
}).join(', ') + ')';
|
|
169
|
+
i += 4;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function ansiResolve(value) {
|
|
176
|
+
if (value == null) return '';
|
|
177
|
+
return typeof value === 'string' ? value : ansiColor(value);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function ansiStyle(state) {
|
|
181
|
+
var fg = state.fg;
|
|
182
|
+
var bg = state.bg;
|
|
183
|
+
if (state.inverse) {
|
|
184
|
+
var swap = fg;
|
|
185
|
+
fg = bg == null ? 'var(--ansi-bg)' : bg;
|
|
186
|
+
bg = swap == null ? 'var(--ansi-fg)' : swap;
|
|
187
|
+
}
|
|
188
|
+
/* Bold is a brightness in a terminal, not only a weight: the eight base
|
|
189
|
+
colours have bright twins at 8..15 and every tool that paints a bold
|
|
190
|
+
red means the bright one. Bold with no colour set takes the bright
|
|
191
|
+
default instead. */
|
|
192
|
+
if (state.bold && typeof fg === 'number' && fg < 8) fg += 8;
|
|
193
|
+
var parts = [];
|
|
194
|
+
var fgColor = ansiResolve(fg);
|
|
195
|
+
var bgColor = ansiResolve(bg);
|
|
196
|
+
if (fgColor) parts.push('color:' + fgColor);
|
|
197
|
+
else if (state.bold) parts.push('color:var(--ansi-fg-bold)');
|
|
198
|
+
if (bgColor) parts.push('background-color:' + bgColor);
|
|
199
|
+
if (state.bold) parts.push('font-weight:700');
|
|
200
|
+
if (state.dim) parts.push('opacity:0.65');
|
|
201
|
+
if (state.italic) parts.push('font-style:italic');
|
|
202
|
+
if (state.underline) parts.push('text-decoration:underline');
|
|
203
|
+
return parts.join(';');
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function renderAnsiHtml(text) {
|
|
207
|
+
var source = String(text || '');
|
|
208
|
+
var state = newAnsiState();
|
|
209
|
+
var html = '';
|
|
210
|
+
var cursor = 0;
|
|
211
|
+
var match;
|
|
212
|
+
ANSI_PATTERN.lastIndex = 0;
|
|
213
|
+
function emit(chunk) {
|
|
214
|
+
if (!chunk) return;
|
|
215
|
+
var style = ansiStyle(state);
|
|
216
|
+
html += style
|
|
217
|
+
? '<span style="' + escapeAttr(style) + '">' + escapeHtml(chunk) + '</span>'
|
|
218
|
+
: escapeHtml(chunk);
|
|
219
|
+
}
|
|
220
|
+
while ((match = ANSI_PATTERN.exec(source)) !== null) {
|
|
221
|
+
emit(source.slice(cursor, match.index));
|
|
222
|
+
if (match[2] === 'm') applyAnsiSgr(state, match[1]);
|
|
223
|
+
cursor = match.index + match[0].length;
|
|
224
|
+
}
|
|
225
|
+
emit(source.slice(cursor));
|
|
226
|
+
return html;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/* ── Chrome ──
|
|
230
|
+
Mermaid fences are left alone: enhanceArticleContent replaces those
|
|
231
|
+
with a rendered diagram, and a diagram does not want an editor bar. */
|
|
232
|
+
function decorateCodeBlock(code) {
|
|
233
|
+
var pre = code.parentElement;
|
|
234
|
+
if (!pre || pre.tagName !== 'PRE') return;
|
|
235
|
+
if (pre.closest('.code-block')) return;
|
|
236
|
+
var token = codeLanguageToken(code);
|
|
237
|
+
if (token === 'mermaid') return;
|
|
238
|
+
|
|
239
|
+
if (token === 'ansi') {
|
|
240
|
+
code.innerHTML = renderAnsiHtml(code.textContent);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
var block = document.createElement('div');
|
|
244
|
+
block.className = 'code-block' + (token === 'ansi' ? ' code-block--ansi' : '');
|
|
245
|
+
if (token) block.setAttribute('data-code-language', token);
|
|
246
|
+
|
|
247
|
+
var bar = document.createElement('div');
|
|
248
|
+
bar.className = 'code-block-bar';
|
|
249
|
+
|
|
250
|
+
var label = codeLanguageLabel(token);
|
|
251
|
+
if (label) {
|
|
252
|
+
var tab = document.createElement('span');
|
|
253
|
+
tab.className = 'code-block-tab font-label';
|
|
254
|
+
tab.textContent = label;
|
|
255
|
+
bar.appendChild(tab);
|
|
256
|
+
} else {
|
|
257
|
+
// The tab is what pushes the copy control to the right edge, so an
|
|
258
|
+
// unlabelled fence still needs something holding its place.
|
|
259
|
+
bar.appendChild(document.createElement('span'));
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/* The clipboard API is absent over plain HTTP and in older browsers. A
|
|
263
|
+
control that cannot do its job is not drawn, rather than drawn and
|
|
264
|
+
left to fail under the reader's hand. */
|
|
265
|
+
if (navigator.clipboard && typeof navigator.clipboard.writeText === 'function') {
|
|
266
|
+
bar.appendChild(buildCopyButton(code));
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
pre.replaceWith(block);
|
|
270
|
+
block.appendChild(bar);
|
|
271
|
+
block.appendChild(pre);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function buildCopyButton(code) {
|
|
275
|
+
var button = document.createElement('button');
|
|
276
|
+
button.type = 'button';
|
|
277
|
+
button.className = 'code-block-copy font-label';
|
|
278
|
+
button.textContent = 'Copy';
|
|
279
|
+
button.addEventListener('click', function () {
|
|
280
|
+
navigator.clipboard.writeText(code.textContent).then(function () {
|
|
281
|
+
button.textContent = 'Copied';
|
|
282
|
+
button.classList.add('is-copied');
|
|
283
|
+
window.setTimeout(function () {
|
|
284
|
+
button.textContent = 'Copy';
|
|
285
|
+
button.classList.remove('is-copied');
|
|
286
|
+
}, 1600);
|
|
287
|
+
}).catch(function (error) {
|
|
288
|
+
/* The API is present but can still refuse: a denied permission, or a
|
|
289
|
+
document that does not have focus. Selecting the block is the next
|
|
290
|
+
best thing, because it leaves the reader one keystroke from the
|
|
291
|
+
result they asked for instead of a dead control. */
|
|
292
|
+
console.error('Copy failed:', error);
|
|
293
|
+
selectContents(code);
|
|
294
|
+
button.textContent = 'Press Ctrl+C';
|
|
295
|
+
window.setTimeout(function () { button.textContent = 'Copy'; }, 2400);
|
|
296
|
+
});
|
|
297
|
+
});
|
|
298
|
+
return button;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
function selectContents(node) {
|
|
302
|
+
var selection = window.getSelection && window.getSelection();
|
|
303
|
+
if (!selection || typeof document.createRange !== 'function') return;
|
|
304
|
+
var range = document.createRange();
|
|
305
|
+
range.selectNodeContents(node);
|
|
306
|
+
selection.removeAllRanges();
|
|
307
|
+
selection.addRange(range);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
function decorateCodeBlocks(root) {
|
|
311
|
+
if (!root) return;
|
|
312
|
+
Array.prototype.forEach.call(root.querySelectorAll('pre > code'), decorateCodeBlock);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
{% endraw %}
|