pandorga 1.7.3 → 1.7.4

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c5c04a8266084060b4aa4810ef57d5d9c25a29302ebd8d946183ba2b95a2daeb
4
- data.tar.gz: b0aceffe59d1febefc9a308db429f7da42856463b1b7143bdb23f125729de192
3
+ metadata.gz: 8935cbf5ca2ee93ea990c5e18d61831877f04f03b3ba569b3d4347dec3387718
4
+ data.tar.gz: 5c9663302fcce8b2ecf4a5197b03ca66f1f0cca0c7eb2ee0b9d9c9ab2d5e320e
5
5
  SHA512:
6
- metadata.gz: 22939efa0329adbf9cc11e0ba771a9fd11d0e154777df7918a1a7bbf821295f3592f4ca1233e43448578deb8a9c69955696f3df282c9f435389e371c69815c1d
7
- data.tar.gz: 70590f4574d43b101ce9e4148d53df33fc1f6f6e2225df5368d0b11a636d425b1300a10d150038c8d2494e004aaa78f7efd495d56a5cadadee1f671f6ae58158
6
+ metadata.gz: ba771e313ba7ecbc5d13473c75454468d22d18f6f2ec1d22ca896d3bf9bec124aa9b32f5efff0dce38c7495a20a422110313910d56808f7b123ce4e07b29e322
7
+ data.tar.gz: b29d2e32efc698af41f0fdcf7b9da605adfbc238a249dd050c157103493b9305e9fb71a355f919e8189a4a43d41b76b25df2a96b446627a3ba6380f6631fc1f5
data/CHANGELOG.md CHANGED
@@ -1,5 +1,31 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.7.4] — 2026-10-12
4
+
5
+ ### Fixed
6
+
7
+ - The footer imprint separator uses `outline` on the panel step.
8
+ `outline-variant` on that panel is 1.80:1 in light (`#b8b8b8` on
9
+ `#f4f4f4`) and 1.93:1 in dark (`#45454c` on `#141418`). `outline`
10
+ on the same panels is 6.08:1 and 5.95:1.
11
+
12
+ ### Changed
13
+
14
+ - Unused selectors are gone. `assets/css/ledger/home.css` drops
15
+ `.home-hero-pandorga-svg` (25 bytes). The hero SVG uses
16
+ `home-hero-art-svg`. `assets/css/ledger/detail.css` drops 916 bytes:
17
+ the author byline suppressor and the `.table-csv` companions. No
18
+ template or runtime emits either class. 941 bytes in all.
19
+ - The one-shot key backfill is gone (`scripts/content/backfill-content-keys.rb`,
20
+ 3,432 bytes). Nothing called it.
21
+ - The watch-path test that read `docs/ops/cloudflare.md` is gone
22
+ (1,230 bytes). That path is not in the tree.
23
+ - Deploy notes pin `~> 1.3` and tag `v1.7.4`. The Pages watch paths
24
+ include `*` and exclude `content/*` and `docs/*`.
25
+
26
+ No `pandorga:` config keys were added or renamed. Font roles and the
27
+ ledger fallbacks are unchanged.
28
+
3
29
  ## [1.7.3] — 2026-10-12
4
30
 
5
31
  ### Fixed
@@ -385,7 +385,7 @@
385
385
  }
386
386
 
387
387
  /* The home page's reference band opens each panel with a plate drawn from
388
- * that section's own content by the export (docs/features/home-band-art.md).
388
+ * that section's own content by the export (docs/deploy/cloudflare.md).
389
389
  * The markup arrives as an SVG document and is injected inline, never as an
390
390
  * <img>: every stroke and fill in it is a custom property, and an external
391
391
  * document cannot see the palette the page is currently wearing.
@@ -485,11 +485,9 @@ aside:has(> .text-detail-sidebar) {
485
485
  font-variant-numeric: tabular-nums;
486
486
  }
487
487
 
488
- /* Projects and products can carry no meta at all, and on articles and posts
489
- the byline below is the block's only child. Either way an all-empty meta
488
+ /* Projects and products can carry no meta at all. An all-empty meta
490
489
  block must not hang a stub of padding off the bottom of the panel. */
491
- .text-detail-sidebar .article-meta:has(> div:empty),
492
- .text-detail-sidebar .article-meta:has(> div > span.text-secondary:not(.font-bold):only-child) {
490
+ .text-detail-sidebar .article-meta:has(> div:empty) {
493
491
  display: none;
494
492
  }
495
493
 
@@ -497,14 +495,6 @@ aside:has(> .text-detail-sidebar) {
497
495
  gap: var(--space-sm);
498
496
  }
499
497
 
500
- /* Author byline. Dropped: single-author site. The runtime still emits it, so
501
- it is suppressed here until `buildTextSidebar` stops rendering it — see the
502
- runtime-change note in this branch's commit message. The product/company
503
- line is the same colour but carries `font-bold`, hence the :not(). */
504
- .text-detail-sidebar .article-meta>div>span.text-secondary:not(.font-bold) {
505
- display: none;
506
- }
507
-
508
498
  .text-detail-sidebar .article-meta .text-tertiary,
509
499
  .text-detail-sidebar .article-meta span.font-bold {
510
500
  font-family: var(--font-label);
@@ -861,8 +851,7 @@ aside:has(> .text-detail-sidebar) {
861
851
  header, and figures that line up. */
862
852
  /* No table surface: the header band and the hover row are the only fills, so
863
853
  both read against the page canvas in either palette. */
864
- .article-content table,
865
- .article-content .table-csv {
854
+ .article-content table {
866
855
  margin: var(--space-xl) 0;
867
856
  font-size: var(--type-dek);
868
857
  font-variant-numeric: tabular-nums;
@@ -870,9 +859,7 @@ aside:has(> .text-detail-sidebar) {
870
859
  }
871
860
 
872
861
  .article-content th,
873
- .article-content td,
874
- .article-content .table-csv th,
875
- .article-content .table-csv td {
862
+ .article-content td {
876
863
  border: 0;
877
864
  border-bottom: 1px solid var(--border-gentle-subtle);
878
865
  padding: var(--space-sm) var(--space-md);
@@ -899,8 +886,7 @@ aside:has(> .text-detail-sidebar) {
899
886
  font-variant-numeric: tabular-nums;
900
887
  }
901
888
 
902
- .article-content thead th,
903
- .article-content .table-csv thead th {
889
+ .article-content thead th {
904
890
  background: var(--color-surface-container);
905
891
  font-family: var(--font-body);
906
892
  font-size: var(--type-body);
@@ -910,13 +896,11 @@ aside:has(> .text-detail-sidebar) {
910
896
  }
911
897
 
912
898
  /* Zebra striping fights the row rules; the hover row is the reading aid. */
913
- .article-content tbody tr:nth-child(even),
914
- .article-content .table-csv tbody tr:nth-child(even) td {
899
+ .article-content tbody tr:nth-child(even) {
915
900
  background: transparent;
916
901
  }
917
902
 
918
- .article-content tbody tr:hover,
919
- .article-content .table-csv tbody tr:hover {
903
+ .article-content tbody tr:hover {
920
904
  background: var(--color-surface-container-low);
921
905
  }
922
906
 
@@ -1131,8 +1115,7 @@ nav.text-page-nav:not(:has(a)) {
1131
1115
  too wide then scrolls inside the table rather than moving the page. The
1132
1116
  runtime emits the table with no wrapper, so the table itself has to be the
1133
1117
  scroll container. */
1134
- .article-content table,
1135
- .article-content .table-csv {
1118
+ .article-content table {
1136
1119
  display: block;
1137
1120
  width: 100%;
1138
1121
  max-width: 100%;
@@ -1140,9 +1123,7 @@ nav.text-page-nav:not(:has(a)) {
1140
1123
  }
1141
1124
 
1142
1125
  .article-content th,
1143
- .article-content td,
1144
- .article-content .table-csv th,
1145
- .article-content .table-csv td {
1126
+ .article-content td {
1146
1127
  padding: var(--space-xs) var(--space-sm);
1147
1128
  }
1148
1129
  }
@@ -230,11 +230,14 @@
230
230
  }
231
231
 
232
232
  /* A middot between items rather than a separator element, so an absent
233
- license leaves no orphaned punctuation behind. */
233
+ license leaves no orphaned punctuation behind. outline-variant is the
234
+ hairline token: #b8b8b8 on #f4f4f4 is 1.80:1, and #45454c on #141418
235
+ is 1.93:1. outline clears 4.5:1 on the same panel (6.08:1 light,
236
+ 5.95:1 dark). */
234
237
  .site-footer-imprint-item+.site-footer-imprint-item::before {
235
238
  content: "·";
236
239
  margin-right: var(--space-md);
237
- color: var(--color-outline-variant);
240
+ color: var(--color-outline);
238
241
  }
239
242
 
240
243
  .site-footer-imprint-link {
@@ -91,8 +91,7 @@
91
91
  opacity: 0.42;
92
92
  }
93
93
 
94
- .home-hero-art-svg,
95
- .home-hero-pandorga-svg {
94
+ .home-hero-art-svg {
96
95
  width: 100%;
97
96
  height: 100%;
98
97
  display: block;
@@ -24,7 +24,7 @@ module Pandorga
24
24
 
25
25
  gem "pandorga", "~> 1.3"
26
26
  # Local checkout: gem "pandorga", path: "../pandorga"
27
- # GitHub tag: gem "pandorga", github: "alexandrelheinen/pandorga", tag: "v1.5.0"
27
+ # GitHub tag: gem "pandorga", github: "alexandrelheinen/pandorga", tag: "v1.7.4"
28
28
  gem "csv"
29
29
  gem "base64"
30
30
  RUBY
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Pandorga
4
- VERSION = "1.7.3"
4
+ VERSION = "1.7.4"
5
5
  end
@@ -27,7 +27,7 @@
27
27
  # because the plates went stale otherwise. Cloudflare Pages excludes `content/*`
28
28
  # from its build watch paths, so a Studio save never rebuilds the shell, and art
29
29
  # committed into a layout is art that stops counting the day it is written. See
30
- # docs/features/home-band-art.md.
30
+ # docs/deploy/cloudflare.md.
31
31
  #
32
32
  # The output is SVG whose every stroke and fill is a custom property, because the
33
33
  # site has two complete palettes and switches between them on a `data-theme`
@@ -653,7 +653,7 @@ def export_derived_data(content_root, output_root)
653
653
  end
654
654
 
655
655
  # The home page's reference band, drawn from the corpus this run just exported
656
- # rather than from committed markup. See docs/features/home-band-art.md for why
656
+ # rather than from committed markup. See docs/deploy/cloudflare.md for why
657
657
  # it lives here: a Studio save never rebuilds the shell, so art baked into a
658
658
  # layout stops counting the day it is written.
659
659
  def export_band_art(output_root, writing_index)
@@ -86,7 +86,12 @@ sheets.each do |path|
86
86
  home-index-shard--gamma
87
87
  home-index-note
88
88
  post-content
89
+ home-hero-pandorga-svg
90
+ table-csv
89
91
  ]
92
+ if cleaned.include?("span.text-secondary:not(.font-bold)")
93
+ errors << "#{relative}: author byline suppressor matches no markup"
94
+ end
90
95
  retired.each do |name|
91
96
  next unless cleaned.match?(/[.#]#{Regexp.escape(name)}\b/)
92
97
 
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
  # frozen_string_literal: true
3
3
 
4
- # Home band art (docs/features/home-band-art.md).
4
+ # Home band art. Watch paths: docs/deploy/cloudflare.md.
5
5
  #
6
6
  # AC-BAND-01 the export writes both plates under data/
7
7
  # AC-BAND-03 a second export with no content change reports them unchanged
@@ -32,6 +32,46 @@ if readme.include?("once 1.1 is on RubyGems")
32
32
  exit 1
33
33
  end
34
34
 
35
+ version = ROOT.join("lib/pandorga/version.rb").read[/VERSION = "([^"]+)"/, 1]
36
+ unless version
37
+ warn "FAIL test-new: Pandorga::VERSION missing"
38
+ exit 1
39
+ end
40
+
41
+ deploy = ROOT.join("docs/deploy/cloudflare.md").read
42
+ upgrading = ROOT.join("docs/upgrading.md").read
43
+ scaffold = ROOT.join("lib/pandorga/commands/new.rb").read
44
+ [
45
+ [deploy, "docs/deploy/cloudflare.md"],
46
+ [upgrading, "docs/upgrading.md"],
47
+ [scaffold, "lib/pandorga/commands/new.rb"]
48
+ ].each do |text, label|
49
+ if text.include?("v1.0.0") || text.include?("v1.3.11") || text.include?("v1.5.0")
50
+ warn "FAIL test-new: #{label} still names a stale release pin"
51
+ exit 1
52
+ end
53
+ end
54
+ unless deploy.include?('~> 1.3') && deploy.include?(%(tag: "v#{version}"))
55
+ warn "FAIL test-new: deploy doc must pin ~> 1.3 and tag v#{version}"
56
+ exit 1
57
+ end
58
+ unless deploy.include?("content/*") && deploy.include?("docs/*") && deploy.include?("Gemfile.lock")
59
+ warn "FAIL test-new: deploy doc lost the watch-path include and excludes"
60
+ exit 1
61
+ end
62
+ unless deploy.include?("3.4.4") && deploy.include?("SKIP_DEPENDENCY_INSTALL")
63
+ warn "FAIL test-new: deploy doc lost the Pages Ruby pin or SKIP_DEPENDENCY_INSTALL"
64
+ exit 1
65
+ end
66
+ unless upgrading.include?(%(tag: "v#{version}")) && upgrading.include?("pandorga-#{version}.gem")
67
+ warn "FAIL test-new: upgrading examples are not v#{version}"
68
+ exit 1
69
+ end
70
+ unless scaffold.include?(%(tag: "v#{version}"))
71
+ warn "FAIL test-new: scaffold comment is not tag v#{version}"
72
+ exit 1
73
+ end
74
+
35
75
  FileUtils.rm_rf(DEST)
36
76
 
37
77
  stdout, stderr, status = run_new(DEST.to_s)
@@ -36,7 +36,7 @@ end
36
36
 
37
37
  if ContentStorageConfig.placeholder?(base)
38
38
  puts "SKIP: R2 URL alignment checks (content_api_base_url is still placeholder: #{base})"
39
- puts " Fill the URL per docs/ops/cloudflare.md before production cutover."
39
+ puts " Fill the URL per docs/deploy/cloudflare.md before production cutover."
40
40
  exit 0
41
41
  end
42
42
 
@@ -40,4 +40,24 @@ fail!("dark tertiary container moved") unless background.casecmp("#6a684f").zero
40
40
  ratio = contrast(foreground, background)
41
41
  fail!("#{foreground} on #{background} is #{ratio.round(2)}:1") unless ratio >= 4.5
42
42
 
43
- puts "PASS test-theme-contrast (#{foreground} on #{background} = #{ratio.round(2)}:1)"
43
+ # Footer imprint middot. outline-variant is a hairline and fails as ink
44
+ # on the panel step. The separator uses outline, which clears 4.5:1.
45
+ footer = ROOT.join("assets/css/ledger/footer.css").read
46
+ fail!("footer imprint separator still uses outline-variant") if footer.include?("color: var(--color-outline-variant)")
47
+ fail!("footer imprint separator lost outline") unless footer.include?("color: var(--color-outline)")
48
+
49
+ light = YAML.safe_load(ROOT.join("_data/themes/light.yml").read) || {}
50
+ pairs = {
51
+ "light" => [light.dig("neutrals", "outline"), light.dig("neutrals", "panel_contrast")],
52
+ "dark" => [dark.dig("neutrals", "outline"), dark.dig("neutrals", "panel_contrast")]
53
+ }
54
+ reports = []
55
+ pairs.each do |name, (ink, panel)|
56
+ fail!("#{name} outline or panel missing") if ink.to_s.empty? || panel.to_s.empty?
57
+
58
+ pair_ratio = contrast(ink, panel)
59
+ fail!("#{name} outline #{ink} on panel #{panel} is #{pair_ratio.round(2)}:1") unless pair_ratio >= 4.5
60
+ reports << "#{name} #{ink} on #{panel} = #{pair_ratio.round(2)}:1"
61
+ end
62
+
63
+ puts "PASS test-theme-contrast (#{foreground} on #{background} = #{ratio.round(2)}:1; #{reports.join('; ')})"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pandorga
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.7.3
4
+ version: 1.7.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - The Pandorga contributors
@@ -328,7 +328,6 @@ files:
328
328
  - lib/pandorga/version.rb
329
329
  - scripts/cdn-allowlist.json
330
330
  - scripts/check_home_lists.mjs
331
- - scripts/content/backfill-content-keys.rb
332
331
  - scripts/content/band_art.rb
333
332
  - scripts/content/export-content-json.rb
334
333
  - scripts/content/flatten-ansi-capture.mjs
@@ -405,7 +404,6 @@ files:
405
404
  - scripts/test/render-parity/youtube.json
406
405
  - scripts/test/test-absent-field-absent-element.rb
407
406
  - scripts/test/test-cdn-pins.rb
408
- - scripts/test/test-cloudflare-watch-paths-doc.rb
409
407
  - scripts/test/test-configuration-doc.rb
410
408
  - scripts/test/test-content-backend.rb
411
409
  - scripts/test/test-content-detail-urls.rb
@@ -1,99 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- require "pathname"
5
- require "yaml"
6
-
7
- require_relative "../../_plugins/content_keys"
8
- require_relative "../../_plugins/content_detail_paths"
9
- require_relative "../../_plugins/git_chronology"
10
-
11
- REPO_ROOT = Pathname.new(__dir__).join("..", "..").expand_path
12
- CONTENT_ROOT = REPO_ROOT.join("content")
13
-
14
- KEY_OVERRIDES = {
15
- "content/collections/posts/2026-03-06-horse-auto-follow.md" => "horse-auto-follow-pt",
16
- "content/collections/posts/2026-03-02-mcmc.md" => "mcmc-pt"
17
- }.freeze
18
-
19
- def split_front_matter(text)
20
- ContentKeys.split_front_matter(text)
21
- end
22
-
23
- def write_front_matter(path, front_matter, body)
24
- yaml = front_matter.to_yaml.sub(/\A---\n/, "").strip
25
- path.write("---\n#{yaml}\n---\n\n#{body}")
26
- end
27
-
28
- def infer_key(entry)
29
- override = KEY_OVERRIDES[entry["repo_relative_path"]]
30
- return override if override
31
-
32
- key = ContentKeys.normalize_key(entry["key"])
33
- return key unless key.empty?
34
-
35
- ContentKeys.infer_key_from_filename(entry["path"], entry["type"])
36
- end
37
-
38
- def legacy_detail_path(type, legacy_segment)
39
- case type
40
- when "article" then ContentDetailPaths.article_detail_path(legacy_segment)
41
- when "post" then ContentDetailPaths.post_detail_path(legacy_segment)
42
- when "project" then ContentDetailPaths.project_detail_path(legacy_segment)
43
- when "product" then ContentDetailPaths.product_detail_path(legacy_segment)
44
- else
45
- raise "unknown type #{type}"
46
- end
47
- end
48
-
49
- def migrate_redirect_fields!(front_matter)
50
- redirects = ContentKeys.normalize_redirects(front_matter)
51
- front_matter.delete("redirect_from")
52
- front_matter["redirects"] = redirects unless redirects.empty?
53
- front_matter
54
- end
55
-
56
- def ensure_legacy_redirect!(front_matter, type, legacy_segment, canonical_path)
57
- redirects = ContentKeys.normalize_redirects(front_matter)
58
- legacy = legacy_detail_path(type, legacy_segment).chomp("/")
59
- canonical = canonical_path.chomp("/")
60
- redirects << legacy unless legacy == canonical
61
- redirects = redirects.map { |path| ContentKeys.normalize_redirect_path(path) }.compact.uniq
62
- front_matter["redirects"] = redirects unless redirects.empty?
63
- front_matter.delete("redirects") if redirects.empty?
64
- front_matter
65
- end
66
-
67
- updated = 0
68
- ContentKeys::CONTENT_PATHS.each do |type, relative_dir|
69
- directory = CONTENT_ROOT.join(relative_dir.sub(%r{\Acontent/}, ""))
70
- next unless directory.directory?
71
-
72
- Dir.glob(directory.join("*.{md,markdown}")).sort.each do |absolute_path|
73
- entry = ContentKeys.load_entry(absolute_path, type, repo_root: REPO_ROOT)
74
- text = File.read(absolute_path).sub(/\A\xEF\xBB\xBF/, "")
75
- front_matter, body = split_front_matter(text)
76
- original_yaml = front_matter.to_yaml
77
-
78
- key = infer_key(entry)
79
- front_matter["key"] = key
80
- migrate_redirect_fields!(front_matter)
81
-
82
- if %w[article post].include?(type)
83
- GitChronology.apply!(front_matter, entry["repo_relative_path"], type)
84
- end
85
-
86
- canonical = ContentKeys.detail_path(type, key, date: front_matter["date"])
87
- ensure_legacy_redirect!(front_matter, type, entry["legacy_segment"], canonical)
88
-
89
- next if front_matter.to_yaml == original_yaml
90
-
91
- write_front_matter(Pathname(absolute_path), front_matter, body.sub(/\A\n+/, ""))
92
- updated += 1
93
- puts "updated #{entry['repo_relative_path']} (key=#{key})"
94
- end
95
- end
96
-
97
- ContentKeys.validate!(REPO_ROOT)
98
- ContentKeys.sync_redirects_file!(REPO_ROOT)
99
- puts "Backfill complete (#{updated} file(s) updated). Redirects synced to _redirects."
@@ -1,31 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- require "pathname"
5
-
6
- ROOT = Pathname.new(__dir__).join("..", "..").expand_path
7
- DOC = ROOT.join("docs/ops/cloudflare.md")
8
-
9
- def check!(condition, message = "condition was false")
10
- raise "Assertion failed: #{message}" unless condition
11
- end
12
-
13
- text = DOC.read
14
-
15
- puts "Testing Cloudflare watch paths documentation..."
16
-
17
- check! text.include?("`*`"), "missing include pattern `*`"
18
- check! text.include?("`content/*`"), "missing exclude pattern content/*"
19
- check! text.include?("`docs/*`"), "missing exclude pattern docs/*"
20
- check! text.include?("Settings → Build → Build watch paths"),
21
- "missing dashboard navigation path"
22
- check! text.include?("SKIP_DEPENDENCY_INSTALL"),
23
- "missing SKIP_DEPENDENCY_INSTALL (avoids Pages pip install of study deps)"
24
- check! text.include?("bundle install") && text.include?("jekyll build"),
25
- "missing lean Pages build command with explicit bundle install"
26
- check! text.include?("pandorga install-functions"),
27
- "missing pandorga install-functions in Pages build command (PLT-0.2)"
28
- check! text.include?("3.4.4"),
29
- "missing Pages v3 Ruby 3.4.4 pin (avoids compiling Ruby from source)"
30
-
31
- puts "Cloudflare watch paths documentation OK."