jekyll-theme-zer0 1.28.0 → 1.29.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +430 -13
  3. data/_data/backlog.yml +356 -2
  4. data/_data/consumers.yml +109 -4
  5. data/_data/features.yml +157 -0
  6. data/_data/i18n/fr.yml +12 -7
  7. data/_data/i18n/manifest.yml +39 -12
  8. data/_data/ingredient_densities.yml +122 -0
  9. data/_data/navigation/main.yml +16 -0
  10. data/_data/recipe_courses.yml +64 -0
  11. data/_data/theme-manifest.yml +68 -40
  12. data/_data/ui-text.yml +8 -0
  13. data/_includes/README.md +15 -1
  14. data/_includes/components/background-customizer.html +2 -2
  15. data/_includes/components/cookie-consent.html +4 -4
  16. data/_includes/components/info-section.html +8 -3
  17. data/_includes/components/language-toggle.html +168 -21
  18. data/_includes/components/page-views-init.html +55 -0
  19. data/_includes/components/page-views.html +33 -0
  20. data/_includes/components/recipe-card.html +67 -0
  21. data/_includes/components/recipe-duration.html +50 -0
  22. data/_includes/components/recipe-grams.html +58 -0
  23. data/_includes/components/recipe-index.html +96 -0
  24. data/_includes/components/recipe-ingredients.html +90 -0
  25. data/_includes/components/recipe-meta.html +96 -0
  26. data/_includes/components/recipe-nutrition.html +57 -0
  27. data/_includes/components/recipe-qty.html +73 -0
  28. data/_includes/components/recipe-ratio.html +151 -0
  29. data/_includes/components/recipe-scaler.html +73 -0
  30. data/_includes/components/recipe-steps.html +86 -0
  31. data/_includes/components/recipe-temp.html +45 -0
  32. data/_includes/components/search-modal.html +2 -2
  33. data/_includes/components/shortcuts-modal.html +3 -0
  34. data/_includes/components/theme-info.html +8 -1
  35. data/_includes/content/jsonld-software.html +22 -3
  36. data/_includes/core/footer.html +18 -16
  37. data/_includes/core/head.html +12 -0
  38. data/_includes/core/header.html +7 -4
  39. data/_includes/navigation/section-sidebar.html +93 -11
  40. data/_includes/navigation/sidebar-left.html +2 -1
  41. data/_includes/navigation/sidebar-right.html +2 -1
  42. data/_includes/setup/wizard.html +173 -86
  43. data/_includes/stats/stats-metrics.html +2 -2
  44. data/_layouts/README.md +2 -0
  45. data/_layouts/admin.html +2 -2
  46. data/_layouts/article.html +5 -0
  47. data/_layouts/cookbook.html +88 -0
  48. data/_layouts/default.html +4 -4
  49. data/_layouts/recipe.html +274 -0
  50. data/_layouts/root.html +22 -3
  51. data/_layouts/section.html +39 -10
  52. data/_sass/components/_callout.scss +1 -1
  53. data/_sass/components/_footer.scss +37 -1
  54. data/_sass/components/_page-views.scss +36 -0
  55. data/_sass/components/_recipe.scss +506 -0
  56. data/_sass/components/_setup-wizard.scss +235 -0
  57. data/_sass/components/_ui-enhancements.scss +6 -6
  58. data/_sass/core/_navbar.scss +250 -15
  59. data/_sass/layouts/_landing.scss +2 -2
  60. data/_sass/tokens/_color.scss +6 -0
  61. data/_sass/tokens/_index.scss +2 -0
  62. data/_sass/tokens/_radius.scss +21 -0
  63. data/_sass/tokens/_typography.scss +4 -0
  64. data/_sass/utilities/_focus.scss +14 -0
  65. data/assets/css/main.scss +3 -0
  66. data/assets/js/modules/navigation/navbar.js +55 -0
  67. data/assets/js/page-views.js +372 -0
  68. data/assets/js/recipe-scaler.js +501 -0
  69. data/assets/js/search-modal.js +10 -0
  70. data/assets/js/setup-wizard.js +322 -20
  71. data/scripts/README.md +29 -0
  72. data/scripts/bin/audit-consumer +39 -7
  73. data/scripts/bin/giscus-discussions +213 -14
  74. data/scripts/bin/manifest +35 -12
  75. data/scripts/ci/agent_review_result.py +164 -0
  76. data/scripts/ci/test_agent_review_result.py +172 -0
  77. data/scripts/design-system-check.rb +170 -0
  78. data/scripts/lib/audit.sh +42 -2
  79. data/scripts/lint-liquid-raw.rb +137 -0
  80. data/scripts/test/lib/run_tests.sh +2 -1
  81. data/scripts/test/lib/test_agent_review_result.sh +27 -0
  82. data/scripts/translate.rb +71 -15
  83. metadata +31 -2
data/scripts/translate.rb CHANGED
@@ -424,16 +424,26 @@ module Zer0Translate
424
424
  def initialize(model:, max_tokens:)
425
425
  @model = model
426
426
  @max_tokens = max_tokens
427
- @auth = resolve_auth
427
+ @candidates = resolve_auth_candidates
428
428
  raise "No Anthropic credential found. Set CLAUDE_CODE_OAUTH_TOKEN " \
429
- "(from `claude setup-token`), ANTHROPIC_AUTH_TOKEN, or ANTHROPIC_API_KEY." unless @auth
429
+ "(from `claude setup-token`), ANTHROPIC_AUTH_TOKEN, or ANTHROPIC_API_KEY." if @candidates.empty?
430
+
431
+ @auth = @candidates.first
430
432
  end
431
433
 
432
434
  def name = "claude (#{@model})"
433
435
 
434
436
  def translate(segments, target_lang, context)
435
- payload = build_payload(segments, target_lang, context)
436
- body = request_with_retries(payload)
437
+ body = begin
438
+ # Rebuilt on retry, not hoisted: the payload's first system block
439
+ # depends on @auth[:mode], so falling back to a different credential
440
+ # has to re-derive it.
441
+ request_with_retries(build_payload(segments, target_lang, context))
442
+ rescue AuthError => e
443
+ raise ProviderError, e.message unless advance_credential!(e)
444
+
445
+ retry
446
+ end
437
447
  text = (body["content"] || []).select { |b| b["type"] == "text" }
438
448
  .map { |b| b["text"] }.join("\n")
439
449
  parsed = extract_json(text)
@@ -444,16 +454,38 @@ module Zer0Translate
444
454
 
445
455
  class ProviderError < StandardError; end
446
456
 
457
+ # A credential the API refused (401/403), as opposed to any other failure.
458
+ # Separate so `translate` can fall through to the next configured
459
+ # credential rather than failing the run outright.
460
+ class AuthError < ProviderError; end
461
+
447
462
  private
448
463
 
449
- def resolve_auth
450
- if (token = ENV["CLAUDE_CODE_OAUTH_TOKEN"] || ENV["ANTHROPIC_AUTH_TOKEN"])
451
- return { mode: :oauth, token: token } unless token.empty?
452
- end
453
- if (key = ENV["ANTHROPIC_API_KEY"])
454
- return { mode: :api_key, token: key } unless key.empty?
455
- end
456
- nil
464
+ # Every configured credential, in precedence order.
465
+ #
466
+ # Returning the whole list rather than just the first match is the point:
467
+ # the old lookup skipped credentials that were UNSET but had no way to skip
468
+ # one the API refused, so a set-but-revoked CLAUDE_CODE_OAUTH_TOKEN shadowed
469
+ # a working ANTHROPIC_API_KEY and took the whole run down with it.
470
+ def resolve_auth_candidates
471
+ [
472
+ { mode: :oauth, token: ENV["CLAUDE_CODE_OAUTH_TOKEN"], label: "CLAUDE_CODE_OAUTH_TOKEN" },
473
+ { mode: :oauth, token: ENV["ANTHROPIC_AUTH_TOKEN"], label: "ANTHROPIC_AUTH_TOKEN" },
474
+ { mode: :api_key, token: ENV["ANTHROPIC_API_KEY"], label: "ANTHROPIC_API_KEY" },
475
+ ].reject { |c| c[:token].nil? || c[:token].empty? }
476
+ end
477
+
478
+ # Switch to the next configured credential after this one was refused.
479
+ # Sticky by design — @auth stays switched for the rest of the run, so a
480
+ # dead credential costs one rejection in total rather than one per chunk.
481
+ def advance_credential!(error)
482
+ current = @candidates.index { |c| c.equal?(@auth) }
483
+ nxt = @candidates[current + 1]
484
+ return false unless nxt
485
+
486
+ Log.warn "#{@auth[:label]} rejected (#{error.message}); retrying with #{nxt[:label]}"
487
+ @auth = nxt
488
+ true
457
489
  end
458
490
 
459
491
  def system_blocks(target_lang)
@@ -514,7 +546,10 @@ module Zer0Translate
514
546
  body = JSON.parse(response.body)
515
547
  unless code == 200
516
548
  message = body.dig("error", "message") || response.body[0, 300]
517
- raise ProviderError, "Anthropic API #{code}: #{message}"
549
+ # 401/403 is the credential, not the request — surface it as an
550
+ # AuthError so the caller can try the next configured one.
551
+ klass = [401, 403].include?(code) ? AuthError : ProviderError
552
+ raise klass, "Anthropic API #{code}: #{message}"
518
553
  end
519
554
  body
520
555
  rescue RetryableError
@@ -692,13 +727,34 @@ module Zer0Translate
692
727
  pages[url] ||= {}
693
728
  end
694
729
 
730
+ # Writes the manifest only when the mapping itself changed.
731
+ #
732
+ # `updated_at` used to be stamped on every save, which meant a run that
733
+ # translated NOTHING still rewrote the file. That is not hypothetical: with
734
+ # the API credential rejected, every page failed and the run still produced
735
+ # a one-line `updated_at` diff — enough for translate.yml to open a PR that
736
+ # reads like a routine translation refresh and contains no translations.
737
+ # Serializing with the PREVIOUS timestamp first and comparing against disk
738
+ # keeps that run a no-op, while any real change (a translated page, a prune)
739
+ # still stamps and writes.
695
740
  def save(source_lang, languages)
741
+ previous = @data["updated_at"]
696
742
  @data["version"] = 1
697
743
  @data["source_lang"] = source_lang
698
744
  @data["languages"] = languages
699
- @data["updated_at"] = Time.now.utc.iso8601
700
745
  @data["pages"] = pages.sort.to_h
746
+
747
+ @data["updated_at"] = previous
748
+ return if File.file?(@path) && File.read(@path, encoding: "bom|utf-8") == render
749
+
750
+ @data["updated_at"] = Time.now.utc.iso8601
701
751
  FileUtils.mkdir_p(File.dirname(@path))
752
+ File.write(@path, render)
753
+ end
754
+
755
+ private
756
+
757
+ def render
702
758
  header = <<~HEADER
703
759
  # ================================================================
704
760
  # GENERATED FILE — do not edit by hand.
@@ -707,7 +763,7 @@ module Zer0Translate
707
763
  # _includes/components/language-toggle.html and _includes/core/hreflang.html.
708
764
  # ================================================================
709
765
  HEADER
710
- File.write(@path, header + @data.to_yaml.sub(/\A---\n/, ""))
766
+ header + @data.to_yaml.sub(/\A---\n/, "")
711
767
  end
712
768
  end
713
769
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jekyll-theme-zer0
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.28.0
4
+ version: 1.29.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Amr Abdel
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-08-09 00:00:00.000000000 Z
11
+ date: 2026-09-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: jekyll
@@ -92,6 +92,7 @@ files:
92
92
  - _data/i18n/fr.yml
93
93
  - _data/i18n/languages.yml
94
94
  - _data/i18n/manifest.yml
95
+ - _data/ingredient_densities.yml
95
96
  - _data/landing.yml
96
97
  - _data/navigation/README.md
97
98
  - _data/navigation/about.yml
@@ -103,6 +104,7 @@ files:
103
104
  - _data/navigation/quickstart.yml
104
105
  - _data/posts_organization.yml
105
106
  - _data/prerequisites.yml
107
+ - _data/recipe_courses.yml
106
108
  - _data/roadmap.yml
107
109
  - _data/roadmap_plan.yml
108
110
  - _data/routing.yml
@@ -159,11 +161,25 @@ files:
159
161
  - _includes/components/nav-export.html
160
162
  - _includes/components/nav-overview.html
161
163
  - _includes/components/page-feedback.html
164
+ - _includes/components/page-views-init.html
165
+ - _includes/components/page-views.html
162
166
  - _includes/components/post-card.html
163
167
  - _includes/components/post-type-badge.html
164
168
  - _includes/components/powered-by.html
165
169
  - _includes/components/preview-image.html
166
170
  - _includes/components/quick-index.html
171
+ - _includes/components/recipe-card.html
172
+ - _includes/components/recipe-duration.html
173
+ - _includes/components/recipe-grams.html
174
+ - _includes/components/recipe-index.html
175
+ - _includes/components/recipe-ingredients.html
176
+ - _includes/components/recipe-meta.html
177
+ - _includes/components/recipe-nutrition.html
178
+ - _includes/components/recipe-qty.html
179
+ - _includes/components/recipe-ratio.html
180
+ - _includes/components/recipe-scaler.html
181
+ - _includes/components/recipe-steps.html
182
+ - _includes/components/recipe-temp.html
167
183
  - _includes/components/search-modal.html
168
184
  - _includes/components/searchbar.html
169
185
  - _includes/components/section.html
@@ -244,6 +260,7 @@ files:
244
260
  - _layouts/book-story.html
245
261
  - _layouts/book.html
246
262
  - _layouts/collection.html
263
+ - _layouts/cookbook.html
247
264
  - _layouts/default.html
248
265
  - _layouts/home.html
249
266
  - _layouts/index.html
@@ -251,6 +268,7 @@ files:
251
268
  - _layouts/news.html
252
269
  - _layouts/note.html
253
270
  - _layouts/notebook.html
271
+ - _layouts/recipe.html
254
272
  - _layouts/root.html
255
273
  - _layouts/search.html
256
274
  - _layouts/section.html
@@ -275,8 +293,11 @@ files:
275
293
  - _sass/components/_notes-index.scss
276
294
  - _sass/components/_notes.scss
277
295
  - _sass/components/_page-feedback.scss
296
+ - _sass/components/_page-views.scss
278
297
  - _sass/components/_post-navigation.scss
298
+ - _sass/components/_recipe.scss
279
299
  - _sass/components/_search-modal.scss
300
+ - _sass/components/_setup-wizard.scss
280
301
  - _sass/components/_skeleton.scss
281
302
  - _sass/components/_theme-preview.scss
282
303
  - _sass/components/_ui-enhancements.scss
@@ -310,6 +331,7 @@ files:
310
331
  - _sass/tokens/_index.scss
311
332
  - _sass/tokens/_layers.scss
312
333
  - _sass/tokens/_motion.scss
334
+ - _sass/tokens/_radius.scss
313
335
  - _sass/tokens/_shadow.scss
314
336
  - _sass/tokens/_spacing.scss
315
337
  - _sass/tokens/_typography.scss
@@ -356,10 +378,12 @@ files:
356
378
  - assets/js/obsidian-local-graph.js
357
379
  - assets/js/obsidian-wiki-links.js
358
380
  - assets/js/page-feedback.js
381
+ - assets/js/page-views.js
359
382
  - assets/js/palette-generator.js
360
383
  - assets/js/particles-source.js
361
384
  - assets/js/particles.js
362
385
  - assets/js/posts-pagination.js
386
+ - assets/js/recipe-scaler.js
363
387
  - assets/js/search-modal.js
364
388
  - assets/js/setup-wizard.js
365
389
  - assets/js/share-actions.js
@@ -420,9 +444,12 @@ files:
420
444
  - scripts/bin/test
421
445
  - scripts/bin/validate
422
446
  - scripts/build
447
+ - scripts/ci/agent_review_result.py
423
448
  - scripts/ci/classify_changes.py
449
+ - scripts/ci/test_agent_review_result.py
424
450
  - scripts/content-review.rb
425
451
  - scripts/convert-notebooks.sh
452
+ - scripts/design-system-check.rb
426
453
  - scripts/dev/css-diff.sh
427
454
  - scripts/dev/rasterize-svg.js
428
455
  - scripts/dev/shot.js
@@ -525,6 +552,7 @@ files:
525
552
  - scripts/lib/template.sh
526
553
  - scripts/lib/validation.sh
527
554
  - scripts/lib/version.sh
555
+ - scripts/lint-liquid-raw.rb
528
556
  - scripts/lint-pages
529
557
  - scripts/migrate-nav-modes.sh
530
558
  - scripts/migrate.sh
@@ -549,6 +577,7 @@ files:
549
577
  - scripts/test/integration/auto-version
550
578
  - scripts/test/integration/mermaid
551
579
  - scripts/test/lib/run_tests.sh
580
+ - scripts/test/lib/test_agent_review_result.sh
552
581
  - scripts/test/lib/test_analyze_commits.sh
553
582
  - scripts/test/lib/test_changelog.sh
554
583
  - scripts/test/lib/test_content_review.sh