jekyll-theme-zer0 1.29.0 → 1.31.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 (163) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +886 -2
  3. data/_data/README.md +2 -0
  4. data/_data/ai.yml +5 -3
  5. data/_data/ai_pricing.yml +36 -0
  6. data/_data/backlog.yml +196 -22
  7. data/_data/consumers.yml +147 -5
  8. data/_data/features.yml +147 -21
  9. data/_data/feedback_types.yml +17 -12
  10. data/_data/landing.yml +5 -2
  11. data/_data/navigation/quickstart.yml +4 -0
  12. data/_data/site_builder.yml +874 -0
  13. data/_data/theme-manifest.yml +132 -122
  14. data/_data/ui-text.yml +19 -0
  15. data/_includes/README.md +11 -1
  16. data/_includes/analytics/posthog.html +30 -11
  17. data/_includes/components/README.md +1 -1
  18. data/_includes/components/admin-links.html +2 -2
  19. data/_includes/components/admin-tabs.html +2 -2
  20. data/_includes/components/ai-chat.html +14 -11
  21. data/_includes/components/analytics-dashboard.html +8 -8
  22. data/_includes/components/author-bio.html +1 -1
  23. data/_includes/components/author-card.html +10 -2
  24. data/_includes/components/author-eeat.html +4 -4
  25. data/_includes/components/background-customizer.html +8 -8
  26. data/_includes/components/background-image.html +120 -0
  27. data/_includes/components/background-settings.html +28 -15
  28. data/_includes/components/collection-manager.html +5 -5
  29. data/_includes/components/component-showcase.html +13 -13
  30. data/_includes/components/config-editor.html +12 -12
  31. data/_includes/components/config-viewer.html +8 -8
  32. data/_includes/components/cookie-consent.html +41 -41
  33. data/_includes/components/cta-button.html +7 -2
  34. data/_includes/components/dev-shortcuts.html +7 -7
  35. data/_includes/components/env-dashboard.html +8 -8
  36. data/_includes/components/env-switcher.html +9 -9
  37. data/_includes/components/feature-card.html +2 -2
  38. data/_includes/components/halfmoon.html +2 -2
  39. data/_includes/components/info-section.html +36 -36
  40. data/_includes/components/js-cdn.html +15 -15
  41. data/_includes/components/language-toggle.html +4 -4
  42. data/_includes/components/mermaid.html +79 -435
  43. data/_includes/components/nanobar.html +5 -5
  44. data/_includes/components/nav-editor.html +2 -2
  45. data/_includes/components/nav-export.html +2 -2
  46. data/_includes/components/nav-overview.html +2 -2
  47. data/_includes/components/page-feedback.html +45 -30
  48. data/_includes/components/page-views-init.html +1 -1
  49. data/_includes/components/post-card.html +22 -22
  50. data/_includes/components/post-type-badge.html +2 -2
  51. data/_includes/components/powered-by.html +2 -2
  52. data/_includes/components/preview-image.html +28 -3
  53. data/_includes/components/quick-index.html +2 -2
  54. data/_includes/components/search-modal.html +27 -2
  55. data/_includes/components/searchbar.html +2 -2
  56. data/_includes/components/svg-background.html +2 -2
  57. data/_includes/components/theme-customizer.html +2 -2
  58. data/_includes/components/theme-info.html +6 -6
  59. data/_includes/components/theme-preview-gallery.html +23 -23
  60. data/_includes/content/giscus.html +2 -2
  61. data/_includes/content/intro.html +15 -8
  62. data/_includes/content/jsonld-faq.html +2 -2
  63. data/_includes/content/jsonld-software.html +2 -2
  64. data/_includes/content/seo.html +12 -4
  65. data/_includes/content/sitemap.html +247 -290
  66. data/_includes/content/toc.html +183 -183
  67. data/_includes/core/branding.html +13 -20
  68. data/_includes/core/console-capture.html +32 -74
  69. data/_includes/core/favicon.html +49 -7
  70. data/_includes/core/footer-fabs.html +17 -3
  71. data/_includes/core/footer.html +31 -18
  72. data/_includes/core/head.html +128 -92
  73. data/_includes/core/header.html +85 -54
  74. data/_includes/core/tokens-inline.html +16 -1
  75. data/_includes/docs/bootstrap-docs.html +8 -8
  76. data/_includes/landing/landing-install-cards.html +2 -2
  77. data/_includes/landing/landing-quick-links.html +1 -1
  78. data/_includes/navigation/admin-nav.html +2 -2
  79. data/_includes/navigation/nav-tree.html +8 -8
  80. data/_includes/navigation/navbar.html +21 -18
  81. data/_includes/navigation/section-sidebar.html +16 -16
  82. data/_includes/navigation/sidebar-config.html +36 -2
  83. data/_includes/navigation/sidebar-left.html +15 -15
  84. data/_includes/navigation/sidebar-right.html +6 -6
  85. data/_includes/obsidian/full-graph.html +2 -2
  86. data/_includes/setup/claude-session.html +72 -0
  87. data/_includes/setup/prereq-checklist.html +90 -0
  88. data/_includes/setup/wizard.html +910 -291
  89. data/_includes/stats/stats-categories.html +8 -8
  90. data/_includes/stats/stats-header.html +14 -14
  91. data/_includes/stats/stats-metrics.html +12 -12
  92. data/_includes/stats/stats-no-data.html +12 -12
  93. data/_includes/stats/stats-overview.html +6 -6
  94. data/_includes/stats/stats-tags.html +8 -8
  95. data/_layouts/404.html +38 -24
  96. data/_layouts/admin.html +22 -22
  97. data/_layouts/article.html +54 -37
  98. data/_layouts/author.html +20 -20
  99. data/_layouts/authors.html +2 -2
  100. data/_layouts/book-abc.html +12 -12
  101. data/_layouts/book-story.html +15 -15
  102. data/_layouts/book.html +12 -12
  103. data/_layouts/collection.html +33 -33
  104. data/_layouts/cookbook.html +12 -12
  105. data/_layouts/default.html +27 -24
  106. data/_layouts/home.html +36 -24
  107. data/_layouts/index.html +10 -10
  108. data/_layouts/landing.html +17 -17
  109. data/_layouts/news.html +44 -44
  110. data/_layouts/note.html +40 -39
  111. data/_layouts/notebook.html +36 -35
  112. data/_layouts/recipe.html +24 -24
  113. data/_layouts/root.html +95 -55
  114. data/_layouts/section.html +23 -23
  115. data/_layouts/setup.html +3 -3
  116. data/_layouts/sitemap-collection.html +49 -49
  117. data/_layouts/stats.html +40 -40
  118. data/_layouts/tag.html +12 -12
  119. data/_layouts/welcome.html +21 -21
  120. data/_sass/components/_cookie-banner.scss +48 -0
  121. data/_sass/components/_mermaid.scss +375 -0
  122. data/_sass/components/_setup-wizard.scss +588 -41
  123. data/_sass/components/_ui-enhancements.scss +11 -0
  124. data/_sass/core/_navbar.scss +147 -100
  125. data/_sass/core/_sidebar-extras.scss +2 -14
  126. data/_sass/core/code-copy.scss +27 -8
  127. data/_sass/layouts/_navbar-extras.scss +14 -4
  128. data/assets/css/extension-points-demo.css +27 -0
  129. data/assets/css/main.scss +1 -0
  130. data/assets/data/wiki-index.json +19 -0
  131. data/assets/js/ai-chat.js +47 -5
  132. data/assets/js/auto-hide-nav.js +53 -13
  133. data/assets/js/code-copy.js +72 -1
  134. data/assets/js/extension-points-demo.js +48 -0
  135. data/assets/js/fleet-feedback-capture.js +124 -0
  136. data/assets/js/fleet-feedback.js +853 -0
  137. data/assets/js/mermaid-diagrams.js +1373 -0
  138. data/assets/js/modules/navigation/config.js +9 -6
  139. data/assets/js/modules/navigation/scroll-spy.js +315 -80
  140. data/assets/js/modules/theme/appearance.js +8 -2
  141. data/assets/js/obsidian-local-graph.js +27 -5
  142. data/assets/js/obsidian-wiki-links.js +8 -3
  143. data/assets/js/page-feedback.js +125 -192
  144. data/assets/js/search-modal.js +26 -0
  145. data/assets/js/setup-wizard.js +2119 -363
  146. data/assets/js/site-builder.js +1834 -0
  147. data/assets/js/ui-enhancements.js +11 -3
  148. data/scripts/README.md +15 -0
  149. data/scripts/ai/README.md +38 -0
  150. data/scripts/ai/api_call.rb +124 -0
  151. data/scripts/ai/usage.rb +314 -0
  152. data/scripts/ai/usage_report.rb +225 -0
  153. data/scripts/ci/test_visual_evidence_autogen.py +403 -0
  154. data/scripts/ci/visual_evidence_autogen.py +1070 -0
  155. data/scripts/content-review.rb +20 -1
  156. data/scripts/features/install-preview-generator +1 -1
  157. data/scripts/lib/README.md +12 -0
  158. data/scripts/lib/preview_generator.py +2 -2
  159. data/scripts/test/integration/mermaid +22 -8
  160. data/scripts/test/lib/run_tests.sh +1 -0
  161. data/scripts/test/lib/test_visual_evidence_autogen.sh +24 -0
  162. data/scripts/translate.rb +23 -1
  163. metadata +21 -2
@@ -16,8 +16,13 @@
16
16
  "categories", "tags", or the name of a
17
17
  _data/navigation/*.yml file (nil = none)
18
18
  sidebar_has_content — true only when sidebar_nav will actually
19
- render items (gates the left column and
20
- the mobile toggle)
19
+ render items (gates the left column)
20
+ sidebar_offcanvas_rendered
21
+ — true only when the CURRENT LAYOUT emits
22
+ the #bdSidebar / #tocContents offcanvases
23
+ at all. sidebar_has_content is not enough
24
+ to gate a toggle: see the note at the
25
+ bottom of this file (issue #373).
21
26
  sidebar_title — heading text for the sidebar panel
22
27
  sidebar_icon — Bootstrap Icons class for the heading
23
28
  Config precedence (most specific wins):
@@ -134,3 +139,32 @@
134
139
  {%- if page.sidebar.icon -%}
135
140
  {%- assign sidebar_icon = page.sidebar.icon -%}
136
141
  {%- endif -%}
142
+
143
+ {%- comment -%}
144
+ 7. Does THIS LAYOUT actually render the offcanvas panels? (issue #373)
145
+
146
+ `sidebar_has_content` answers "is there nav content", which is what gates the
147
+ left column inside _layouts/default.html. It is NOT sufficient to gate the
148
+ TOGGLES, because core/header.html and core/footer-fabs.html are included from
149
+ _layouts/root.html — so they render on every layout, while `#bdSidebar`
150
+ (navigation/sidebar-left.html) and `#tocContents` (navigation/sidebar-right.html)
151
+ are emitted by _layouts/default.html alone.
152
+
153
+ The result was a dead control plus a dangling ARIA reference on every page
154
+ whose layout inherits `root` directly: measured at 37 pages of a 415-page
155
+ build, including all of `/about/**` (layout: admin) and 404/CHANGELOG.
156
+ Clicking did nothing, and assistive tech followed aria-controls to an element
157
+ that was not there.
158
+
159
+ There is no Liquid API for "resolve the layout inheritance chain", so the set
160
+ of layouts that reach default.html is listed here explicitly. It is pinned by
161
+ a test (test/test_core.sh :: test_sidebar_offcanvas_layout_gate) that reads
162
+ the front matter of every _layouts/*.html and fails if reality drifts from
163
+ this list — the same invariant pattern #425 used for the pixel-gate filter.
164
+ Add a layout here when, and only when, its `layout:` chain reaches default.
165
+ {%- endcomment -%}
166
+ {%- assign _sidebar_offcanvas_layouts = "default,article,author,authors,collection,cookbook,note,notebook,recipe,tag" | split: "," -%}
167
+ {%- assign sidebar_offcanvas_rendered = false -%}
168
+ {%- if _sidebar_offcanvas_layouts contains page.layout -%}
169
+ {%- assign sidebar_offcanvas_rendered = true -%}
170
+ {%- endif -%}
@@ -1,5 +1,5 @@
1
1
  {% comment %} Feature: ZER0-008 {% endcomment %}
2
- <!--
2
+ {% comment %}
3
3
  ===================================================================
4
4
  SIDEBAR LEFT - Dynamic Navigation Sidebar
5
5
  ===================================================================
@@ -27,14 +27,14 @@
27
27
  - Heading text/icon: page.sidebar.title/icon → collection metadata →
28
28
  site.sidebar.title/icon → "Browse docs"
29
29
  ===================================================================
30
- -->
30
+ {% endcomment %}
31
31
 
32
32
  {% include navigation/sidebar-config.html %}
33
33
 
34
- <!-- ================================ -->
35
- <!-- SIDEBAR NAVIGATION CONTAINER -->
36
- <!-- ================================ -->
37
- <!-- Responsive offcanvas sidebar - full-width on mobile, fixed on desktop -->
34
+ {% comment %} ================================ {% endcomment %}
35
+ {% comment %} SIDEBAR NAVIGATION CONTAINER {% endcomment %}
36
+ {% comment %} ================================ {% endcomment %}
37
+ {% comment %} Responsive offcanvas sidebar - full-width on mobile, fixed on desktop {% endcomment %}
38
38
  <script>
39
39
  try {
40
40
  if (localStorage.getItem('zer0-nav-sidebar-visible') === 'false') {
@@ -44,10 +44,10 @@
44
44
  </script>
45
45
  <div class="offcanvas-lg offcanvas-start" tabindex="-1" id="bdSidebar" aria-labelledby="bdSidebarOffcanvasLabel">
46
46
 
47
- <!-- ========================== -->
48
- <!-- SIDEBAR HEADER -->
49
- <!-- ========================== -->
50
- <!-- Mobile-only header with close button -->
47
+ {% comment %} ========================== {% endcomment %}
48
+ {% comment %} SIDEBAR HEADER {% endcomment %}
49
+ {% comment %} ========================== {% endcomment %}
50
+ {% comment %} Mobile-only header with close button {% endcomment %}
51
51
  <div class="offcanvas-header">
52
52
  <h2 class="offcanvas-title h5 mb-0" id="bdSidebarOffcanvasLabel">{{ sidebar_title }}</h2>
53
53
  <div class="d-flex align-items-center gap-1">
@@ -63,10 +63,10 @@
63
63
  </div>
64
64
  </div>
65
65
 
66
- <!-- ========================== -->
67
- <!-- SIDEBAR CONTENT AREA -->
68
- <!-- ========================== -->
69
- <!-- Scrollable content area with dynamic navigation options -->
66
+ {% comment %} ========================== {% endcomment %}
67
+ {% comment %} SIDEBAR CONTENT AREA {% endcomment %}
68
+ {% comment %} ========================== {% endcomment %}
69
+ {% comment %} Scrollable content area with dynamic navigation options {% endcomment %}
70
70
  <div class="offcanvas-body overflow-auto">
71
71
 
72
72
  <div class="bd-sidebar-desktop-header mb-3 d-none d-lg-flex align-items-center justify-content-between gap-2 w-100">
@@ -86,7 +86,7 @@
86
86
  </button>
87
87
  </div>
88
88
 
89
- <!-- Resolved navigation content (see navigation/sidebar-nav.html) -->
89
+ {% comment %} Resolved navigation content (see navigation/sidebar-nav.html) {% endcomment %}
90
90
  {% include navigation/sidebar-nav.html id="sidebar-content" %}
91
91
 
92
92
  </div>
@@ -1,12 +1,12 @@
1
1
  {% comment %} Features: ZER0-008, ZER0-010 {% endcomment %}
2
- <!--
2
+ {% comment %}
3
3
  file: sidebar-right.html
4
4
  path: _includes/sidebar-right.html
5
5
  includes: nav_list.html, content/toc.html
6
- -->
6
+ {% endcomment %}
7
7
 
8
8
  {%- include core/i18n.html -%}
9
- <!-- FOUC guard: apply hidden TOC preference before paint -->
9
+ {% comment %} FOUC guard: apply hidden TOC preference before paint {% endcomment %}
10
10
  <script>
11
11
  try {
12
12
  if (localStorage.getItem('zer0-nav-toc-visible') === 'false') {
@@ -15,7 +15,7 @@
15
15
  } catch (e) {}
16
16
  </script>
17
17
 
18
- <!-- Right sidebar container -->
18
+ {% comment %} Right sidebar container {% endcomment %}
19
19
  <div class="offcanvas-lg offcanvas-end" tabindex="-1" id="tocContents" aria-labelledby="tocLabel">
20
20
  <div class="offcanvas-header">
21
21
  <h2 class="offcanvas-title h5 mb-0" id="tocLabel">{{ ui.toc_title | default: "Table of Contents" }}</h2>
@@ -32,9 +32,9 @@
32
32
  </div>
33
33
  </div>
34
34
  <div class="offcanvas-body">
35
- <!-- Table of contents -->
35
+ {% comment %} Table of contents {% endcomment %}
36
36
  <nav id="TableOfContents" role="navigation" aria-label="Table of Contents">
37
- <!-- TOC title -->
37
+ {% comment %} TOC title {% endcomment %}
38
38
  <div class="mb-3 d-flex align-items-center justify-content-between gap-2">
39
39
  <strong class="d-flex align-items-center mb-0">
40
40
  <i class="bi bi-file-text me-2" aria-hidden="true"></i>
@@ -197,9 +197,9 @@ unresolved targets show up as dashed red nodes so you can find dangling
197
197
  links at a glance. The graph is regenerated every Jekyll build; nothing
198
198
  runs client-side except cytoscape's force layout.
199
199
 
200
- <!-- Cytoscape.js is vendored (no runtime CDN) and lazy-loaded by
200
+ {% comment %} Cytoscape.js is vendored (no runtime CDN) and lazy-loaded by
201
201
  obsidian-graph.js from window.OBSIDIAN_CONFIG.cytoscapeUrl, only on this
202
- page, with an in-page recovery message if the fetch fails. -->
202
+ page, with an in-page recovery message if the fetch fails. {% endcomment %}
203
203
  {% if site.obsidian.enabled != false %}
204
204
  <script src="{{ '/assets/js/obsidian-graph.js' | relative_url }}?v={{ site.time | date: '%s' }}" defer></script>
205
205
  {% endif %}
@@ -0,0 +1,72 @@
1
+ {% comment %} Feature: ZER0-086, ZER0-087 {% endcomment %}
2
+ {%- comment -%}
3
+ Component: setup/claude-session
4
+ Path: _includes/setup/claude-session.html
5
+ Purpose: The embedded AI session that rides alongside every Site
6
+ Builder step: connection state, provider + mode badges,
7
+ transcript, suggested prompts, and the composer with a Stop
8
+ button for long tool runs. The assistant (Claude or Grok —
9
+ whichever the Connect step connected) sees the whole wizard
10
+ state on every turn and acts on it through tools (fill fields,
11
+ run checks, read theme source, write or modify the project,
12
+ generate images, run Docker).
13
+ Params: none — configured by #siteBuilderConfig in setup/wizard.html
14
+ Depends on: Bootstrap 5.3 + Icons, assets/js/site-builder.js,
15
+ _sass/components/_setup-wizard.scss
16
+ Notes:
17
+ - Proxy-only. Nothing renders a credential; the panel shows a connect
18
+ prompt until GET {endpoint}/status answers AND the chosen provider has
19
+ a key (data-ready="true"), then goes live.
20
+ - Rendering is escape-first (a limited Markdown subset); action cards are
21
+ built with createElement/textContent — see ai-chat.instructions.md.
22
+ - role="log" + aria-live so screen readers hear replies; Escape does not
23
+ close the panel (it is a page region, not a dialog).
24
+ {%- endcomment -%}
25
+
26
+ <section class="sb-panel card" id="siteBuilderPanel" aria-labelledby="sb-title" data-state="offline" data-ready="false" data-mode="guided">
27
+ <div class="card-header d-flex align-items-center justify-content-between gap-2 py-2">
28
+ <div class="d-flex align-items-center gap-2 min-w-0">
29
+ <i class="bi bi-stars" aria-hidden="true"></i>
30
+ <h2 class="h6 mb-0 text-truncate" id="sb-title">AI session</h2>
31
+ <span class="badge rounded-pill text-bg-light border sb-mode-badge" id="sb-mode-badge" hidden>guided</span>
32
+ </div>
33
+ <div class="d-flex align-items-center gap-2">
34
+ <span class="sb-status-badge badge rounded-pill text-bg-secondary" id="sb-status-badge" aria-live="polite">offline</span>
35
+ <button class="btn btn-sm btn-link text-body p-0" type="button" id="sb-clear" title="Clear the conversation" aria-label="Clear the conversation">
36
+ <i class="bi bi-eraser" aria-hidden="true"></i>
37
+ </button>
38
+ <button class="btn btn-sm btn-link text-body p-0" type="button" id="sb-collapse" aria-expanded="true" aria-controls="sb-body" title="Collapse">
39
+ <i class="bi bi-chevron-up" aria-hidden="true"></i>
40
+ </button>
41
+ </div>
42
+ </div>
43
+
44
+ <div class="sb-body" id="sb-body">
45
+ <div class="sb-messages p-3" id="sb-messages" role="log" aria-live="polite" aria-relevant="additions">
46
+ <div class="sb-offline text-body-secondary small" id="sb-offline">
47
+ <p class="mb-1"><strong>Not connected.</strong> Start the dev proxy and connect Claude or Grok on step 1. The assistant can then read your answers, fill in the forms, check your machine, read the theme's source, write or modify your project, generate images and run the build.</p>
48
+ <p class="mb-0">Everything else on this page works without it.</p>
49
+ </div>
50
+ </div>
51
+
52
+ <div class="sb-chips px-3 pb-2 d-flex gap-2 flex-wrap" id="sb-chips" aria-label="Suggested prompts"></div>
53
+
54
+ <form class="sb-composer p-3 pt-0" id="sb-form" autocomplete="off">
55
+ <label for="sb-input" class="visually-hidden">Message the assistant</label>
56
+ <div class="input-group">
57
+ <textarea class="form-control" id="sb-input" rows="1" maxlength="6000"
58
+ placeholder="Connect a provider to start chatting" disabled></textarea>
59
+ <button class="btn btn-primary" type="submit" id="sb-send" aria-label="Send" disabled>
60
+ <i class="bi bi-send" aria-hidden="true"></i>
61
+ </button>
62
+ <button class="btn btn-outline-danger" type="button" id="sb-stop" aria-label="Stop the current run" hidden disabled>
63
+ <i class="bi bi-stop-fill" aria-hidden="true"></i> Stop
64
+ </button>
65
+ </div>
66
+ <div class="d-flex justify-content-between align-items-center mt-1">
67
+ <small class="text-body-secondary sb-disclaimer" id="sb-disclaimer">The assistant sees every answer on this page. Actions ask for confirmation first.</small>
68
+ <small class="text-body-secondary" id="sb-context-note"></small>
69
+ </div>
70
+ </form>
71
+ </div>
72
+ </section>
@@ -0,0 +1,90 @@
1
+ {% comment %} Feature: ZER0-086 {% endcomment %}
2
+ {%- comment -%}
3
+ Component: setup/prereq-checklist
4
+ Path: _includes/setup/prereq-checklist.html
5
+ Purpose: The Prerequisites step of the Site Builder — one row per tool
6
+ from _data/site_builder.yml with live check state, a manual
7
+ "done" toggle, and per-OS install + verify commands that mirror
8
+ pages/_quickstart/machine-setup.md.
9
+ Params: none — reads site.data.site_builder.prerequisites
10
+ Depends on: Bootstrap 5.3 + Icons, assets/js/setup-wizard.js (OS picker,
11
+ manual toggles, copy buttons), assets/js/site-builder.js
12
+ (live checks via the dev proxy's POST /api/wizard/check)
13
+ Notes: Fully usable offline — the checks are an enhancement. The OS
14
+ picker defaults from navigator.platform in JS; the markup
15
+ defaults to macOS so the page is meaningful without JS.
16
+ {%- endcomment -%}
17
+
18
+ {% assign prereqs = site.data.site_builder.prerequisites %}
19
+
20
+ <div class="prereq-toolbar d-flex flex-wrap align-items-center justify-content-between gap-2 mb-3">
21
+ <div class="btn-group btn-group-sm" role="group" aria-label="Operating system" id="prereq-os">
22
+ <input type="radio" class="btn-check" name="prereq-os" id="prereq-os-macos" value="macos" autocomplete="off" checked>
23
+ <label class="btn btn-outline-secondary" for="prereq-os-macos"><i class="bi bi-apple" aria-hidden="true"></i> macOS</label>
24
+ <input type="radio" class="btn-check" name="prereq-os" id="prereq-os-windows" value="windows" autocomplete="off">
25
+ <label class="btn btn-outline-secondary" for="prereq-os-windows"><i class="bi bi-windows" aria-hidden="true"></i> Windows</label>
26
+ <input type="radio" class="btn-check" name="prereq-os" id="prereq-os-linux" value="linux" autocomplete="off">
27
+ <label class="btn btn-outline-secondary" for="prereq-os-linux"><i class="bi bi-ubuntu" aria-hidden="true"></i> Linux</label>
28
+ </div>
29
+ <div class="d-flex align-items-center gap-2">
30
+ <span class="small text-body-secondary" id="prereq-summary" aria-live="polite">0 of 0 required ready</span>
31
+ <button class="btn btn-sm btn-primary sb-needs-proxy" type="button" id="btn-run-checks" disabled
32
+ title="Runs a fixed set of read-only version checks through the local dev proxy">
33
+ <i class="bi bi-play-circle" aria-hidden="true"></i> Run checks
34
+ </button>
35
+ </div>
36
+ </div>
37
+
38
+ <ul class="list-group prereq-list mb-3" id="prereq-list">
39
+ {% for p in prereqs %}
40
+ <li class="list-group-item prereq-item" data-prereq="{{ p.id }}" data-check="{{ p.check }}"
41
+ data-required="{{ p.required }}" data-state="unknown">
42
+ <div class="d-flex align-items-start gap-3">
43
+ <span class="prereq-state-icon mt-1" aria-hidden="true"><i class="bi {{ p.icon }}"></i></span>
44
+ <div class="flex-grow-1 min-w-0">
45
+ <div class="d-flex flex-wrap align-items-center gap-2">
46
+ <span class="fw-semibold">{{ p.label }}</span>
47
+ {% if p.required %}
48
+ <span class="badge text-bg-primary rounded-pill">required</span>
49
+ {% else %}
50
+ <span class="badge text-bg-secondary rounded-pill">recommended</span>
51
+ {% endif %}
52
+ <span class="badge rounded-pill prereq-state-badge" aria-live="polite">not checked</span>
53
+ </div>
54
+ <p class="small text-body-secondary mb-1">{{ p.why }}</p>
55
+ <p class="small mb-2 prereq-output text-body-secondary font-monospace" hidden></p>
56
+
57
+ <details class="prereq-details small">
58
+ <summary>Install &amp; verify</summary>
59
+ <div class="mt-2">
60
+ <div class="prereq-install">
61
+ <span class="text-body-secondary">Install</span>
62
+ <div class="wizard-cmd" data-os="macos"><code>{{ p.install.macos }}</code><button class="btn btn-sm btn-link" type="button" data-copy="{{ p.install.macos | escape }}" aria-label="Copy install command"><i class="bi bi-clipboard" aria-hidden="true"></i></button></div>
63
+ <div class="wizard-cmd" data-os="windows" hidden><code>{{ p.install.windows }}</code><button class="btn btn-sm btn-link" type="button" data-copy="{{ p.install.windows | escape }}" aria-label="Copy install command"><i class="bi bi-clipboard" aria-hidden="true"></i></button></div>
64
+ <div class="wizard-cmd" data-os="linux" hidden><code>{{ p.install.linux }}</code><button class="btn btn-sm btn-link" type="button" data-copy="{{ p.install.linux | escape }}" aria-label="Copy install command"><i class="bi bi-clipboard" aria-hidden="true"></i></button></div>
65
+ </div>
66
+ <div class="prereq-verify mt-2">
67
+ <span class="text-body-secondary">Verify</span>
68
+ <div class="wizard-cmd"><code>{{ p.verify }}</code><button class="btn btn-sm btn-link" type="button" data-copy="{{ p.verify | escape }}" aria-label="Copy verify command"><i class="bi bi-clipboard" aria-hidden="true"></i></button></div>
69
+ </div>
70
+ {% if p.docs %}
71
+ <a class="d-inline-block mt-2" href="{{ p.docs }}" target="_blank" rel="noopener">Docs <i class="bi bi-box-arrow-up-right" aria-hidden="true"></i></a>
72
+ {% endif %}
73
+ </div>
74
+ </details>
75
+ </div>
76
+ <div class="form-check form-switch flex-shrink-0 ms-auto">
77
+ <input class="form-check-input prereq-manual" type="checkbox" id="prereq-{{ p.id }}" data-prereq="{{ p.id }}">
78
+ <label class="form-check-label small" for="prereq-{{ p.id }}">Done</label>
79
+ </div>
80
+ </div>
81
+ </li>
82
+ {% endfor %}
83
+ </ul>
84
+
85
+ <div class="alert alert-light border small mb-0">
86
+ <i class="bi bi-lightbulb" aria-hidden="true"></i>
87
+ On macOS you can install everything at once:
88
+ <span class="wizard-cmd d-inline-flex"><code id="cmd-brew-all">brew install git gh node && brew install --cask docker visual-studio-code</code><button class="btn btn-sm btn-link" type="button" data-copy-target="#cmd-brew-all" aria-label="Copy command"><i class="bi bi-clipboard" aria-hidden="true"></i></button></span>
89
+ Full guide: <a href="{{ '/quickstart/machine-setup/' | relative_url }}">Machine Setup</a>.
90
+ </div>