utopia-project 0.38.0 → 0.40.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 (94) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/lib/utopia/project/base.rb +4 -14
  4. data/lib/utopia/project/guides.rb +66 -0
  5. data/lib/utopia/project/import_map.rb +2 -0
  6. data/lib/utopia/project/sidebar.rb +6 -16
  7. data/lib/utopia/project/version.rb +1 -1
  8. data/pages/_page.xnode +1 -18
  9. data/pages/_usage.xnode +2 -2
  10. data/pages/guides/index.xnode +17 -8
  11. data/pages/guides/show.xnode +34 -1
  12. data/pages/releases/index.xnode +0 -1
  13. data/public/_static/application.js +24 -0
  14. data/public/_static/links.js +18 -16
  15. data/public/_static/sidebar.js +205 -133
  16. data/public/_static/site.css +34 -0
  17. data/readme.md +6 -4
  18. data/releases.md +6 -0
  19. data.tar.gz.sig +0 -0
  20. metadata +6 -77
  21. metadata.gz.sig +3 -1
  22. data/public/_components/jquery/jquery.js +0 -10716
  23. data/public/_components/jquery/jquery.min.js +0 -2
  24. data/public/_components/jquery/jquery.min.map +0 -1
  25. data/public/_components/jquery/jquery.slim.js +0 -8617
  26. data/public/_components/jquery/jquery.slim.min.js +0 -2
  27. data/public/_components/jquery/jquery.slim.min.map +0 -1
  28. data/public/_components/jquery-litebox/jquery.litebox.css +0 -23
  29. data/public/_components/jquery-litebox/jquery.litebox.gallery.css +0 -48
  30. data/public/_components/jquery-litebox/jquery.litebox.js +0 -30
  31. data/public/_components/jquery-syntax/base/jquery.syntax.brush.apache.css +0 -12
  32. data/public/_components/jquery-syntax/base/jquery.syntax.brush.applescript.css +0 -5
  33. data/public/_components/jquery-syntax/base/jquery.syntax.brush.assembly.css +0 -8
  34. data/public/_components/jquery-syntax/base/jquery.syntax.brush.bash-script.css +0 -4
  35. data/public/_components/jquery-syntax/base/jquery.syntax.brush.bash.css +0 -2
  36. data/public/_components/jquery-syntax/base/jquery.syntax.brush.clang.css +0 -6
  37. data/public/_components/jquery-syntax/base/jquery.syntax.brush.css.css +0 -14
  38. data/public/_components/jquery-syntax/base/jquery.syntax.brush.diff.css +0 -16
  39. data/public/_components/jquery-syntax/base/jquery.syntax.brush.html.css +0 -3
  40. data/public/_components/jquery-syntax/base/jquery.syntax.brush.ocaml.css +0 -3
  41. data/public/_components/jquery-syntax/base/jquery.syntax.brush.protobuf.css +0 -2
  42. data/public/_components/jquery-syntax/base/jquery.syntax.brush.python.css +0 -6
  43. data/public/_components/jquery-syntax/base/jquery.syntax.brush.ruby.css +0 -2
  44. data/public/_components/jquery-syntax/base/jquery.syntax.brush.xml.css +0 -35
  45. data/public/_components/jquery-syntax/base/jquery.syntax.core.css +0 -58
  46. data/public/_components/jquery-syntax/base/jquery.syntax.editor.css +0 -6
  47. data/public/_components/jquery-syntax/base/theme.js +0 -1
  48. data/public/_components/jquery-syntax/bright/jquery.syntax.core.css +0 -27
  49. data/public/_components/jquery-syntax/bright/theme.js +0 -1
  50. data/public/_components/jquery-syntax/jquery.syntax.brush.apache.js +0 -3
  51. data/public/_components/jquery-syntax/jquery.syntax.brush.applescript.js +0 -5
  52. data/public/_components/jquery-syntax/jquery.syntax.brush.assembly.js +0 -3
  53. data/public/_components/jquery-syntax/jquery.syntax.brush.bash-script.js +0 -4
  54. data/public/_components/jquery-syntax/jquery.syntax.brush.bash.js +0 -2
  55. data/public/_components/jquery-syntax/jquery.syntax.brush.basic.js +0 -5
  56. data/public/_components/jquery-syntax/jquery.syntax.brush.clang.js +0 -5
  57. data/public/_components/jquery-syntax/jquery.syntax.brush.csharp.js +0 -4
  58. data/public/_components/jquery-syntax/jquery.syntax.brush.css.js +0 -5
  59. data/public/_components/jquery-syntax/jquery.syntax.brush.diff.js +0 -2
  60. data/public/_components/jquery-syntax/jquery.syntax.brush.go.js +0 -3
  61. data/public/_components/jquery-syntax/jquery.syntax.brush.haskell.js +0 -3
  62. data/public/_components/jquery-syntax/jquery.syntax.brush.html.js +0 -4
  63. data/public/_components/jquery-syntax/jquery.syntax.brush.io.js +0 -3
  64. data/public/_components/jquery-syntax/jquery.syntax.brush.java.js +0 -4
  65. data/public/_components/jquery-syntax/jquery.syntax.brush.javascript.js +0 -3
  66. data/public/_components/jquery-syntax/jquery.syntax.brush.kai.js +0 -2
  67. data/public/_components/jquery-syntax/jquery.syntax.brush.lisp.js +0 -2
  68. data/public/_components/jquery-syntax/jquery.syntax.brush.lua.js +0 -3
  69. data/public/_components/jquery-syntax/jquery.syntax.brush.nginx.js +0 -2
  70. data/public/_components/jquery-syntax/jquery.syntax.brush.ocaml.js +0 -4
  71. data/public/_components/jquery-syntax/jquery.syntax.brush.ooc.js +0 -4
  72. data/public/_components/jquery-syntax/jquery.syntax.brush.pascal.js +0 -4
  73. data/public/_components/jquery-syntax/jquery.syntax.brush.perl5.js +0 -3
  74. data/public/_components/jquery-syntax/jquery.syntax.brush.php-script.js +0 -4
  75. data/public/_components/jquery-syntax/jquery.syntax.brush.php.js +0 -2
  76. data/public/_components/jquery-syntax/jquery.syntax.brush.plain.js +0 -2
  77. data/public/_components/jquery-syntax/jquery.syntax.brush.protobuf.js +0 -3
  78. data/public/_components/jquery-syntax/jquery.syntax.brush.python.js +0 -5
  79. data/public/_components/jquery-syntax/jquery.syntax.brush.ruby.js +0 -5
  80. data/public/_components/jquery-syntax/jquery.syntax.brush.scala.js +0 -4
  81. data/public/_components/jquery-syntax/jquery.syntax.brush.smalltalk.js +0 -2
  82. data/public/_components/jquery-syntax/jquery.syntax.brush.sql.js +0 -4
  83. data/public/_components/jquery-syntax/jquery.syntax.brush.super-collider.js +0 -3
  84. data/public/_components/jquery-syntax/jquery.syntax.brush.swift.js +0 -3
  85. data/public/_components/jquery-syntax/jquery.syntax.brush.xml.js +0 -4
  86. data/public/_components/jquery-syntax/jquery.syntax.brush.xrb.js +0 -2
  87. data/public/_components/jquery-syntax/jquery.syntax.brush.yaml.js +0 -2
  88. data/public/_components/jquery-syntax/jquery.syntax.cache.js +0 -7
  89. data/public/_components/jquery-syntax/jquery.syntax.core.js +0 -34
  90. data/public/_components/jquery-syntax/jquery.syntax.editor.js +0 -11
  91. data/public/_components/jquery-syntax/jquery.syntax.js +0 -8
  92. data/public/_components/jquery-syntax/jquery.syntax.min.js +0 -13
  93. data/public/_components/jquery-syntax/paper/jquery.syntax.core.css +0 -31
  94. data/public/_components/jquery-syntax/paper/theme.js +0 -1
@@ -3,77 +3,116 @@
3
3
  * Progressive enhancement for sidebar navigation with scroll tracking
4
4
  */
5
5
 
6
- (function() {
7
- 'use strict';
8
-
9
- // Only run if we have a sidebar navigation
10
- const sidebarNav = document.querySelector('.sidebar nav');
11
- if (!sidebarNav) return;
12
-
13
- const navLinks = sidebarNav.querySelectorAll('a[href*="#"]');
14
-
15
- // Build a map of sidebar links by their fragment IDs
16
- const sidebarLinksByFragment = new Map();
17
- navLinks.forEach(link => {
18
- const href = link.getAttribute('href');
19
- const fragmentIndex = href.indexOf('#');
20
- if (fragmentIndex !== -1) {
21
- const fragment = href.substring(fragmentIndex + 1);
22
- sidebarLinksByFragment.set(fragment, link);
23
- }
24
- });
6
+ class SidebarNavigation {
7
+ static extractFragment(href) {
8
+ const index = href.indexOf('#');
9
+ return index !== -1 ? href.substring(index + 1) : null;
10
+ }
25
11
 
26
- if (sidebarLinksByFragment.size === 0) return;
12
+ constructor(sidebarNav, sidebarLinksByFragment, allSections, sections, navLinks, sectionToSidebarLinkMap) {
13
+ this.sidebarNav = sidebarNav;
14
+ this.navLinks = navLinks;
15
+ this.currentActive = null;
16
+ this.sidebarLinksByFragment = sidebarLinksByFragment;
17
+ this.allSections = allSections;
18
+ this.sections = sections;
19
+ this.sectionToSidebarLinkMap = sectionToSidebarLinkMap;
20
+
21
+ // Store references for cleanup
22
+ this.scrollTimer = null;
23
+ this.scrollHandler = null;
24
+ this.resizeHandler = null;
25
+ this.clickHandlers = new Map();
26
+
27
+ // Initialize
28
+ this.setupEventListeners();
29
+
30
+ // Initial update without changing page state (preserves URL fragments)
31
+ this.updateActiveLink(false);
32
+ }
27
33
 
28
- // Get all sections/headings on the page and annotate them with their sidebar link
29
- const allSections = Array.from(document.querySelectorAll('section, h1, h2, h3, h4, h5, h6'))
30
- .filter(el => el.id) // Only keep elements with IDs
31
- .map(el => {
32
- // Get the section element (section or heading's parent)
33
- const sectionElement = el.tagName === 'SECTION' ? el : (el.closest('section') || el.parentElement);
34
- return { element: sectionElement, id: el.id };
34
+ setupEventListeners() {
35
+ // Throttled scroll handler
36
+ this.scrollHandler = () => {
37
+ if (this.scrollTimer) return;
38
+
39
+ this.scrollTimer = setTimeout(() => {
40
+ this.updateActiveLink();
41
+ this.scrollTimer = null;
42
+ }, 100);
43
+ };
44
+
45
+ this.resizeHandler = () => this.updateActiveLink();
46
+
47
+ // Set up event listeners
48
+ window.addEventListener('scroll', this.scrollHandler, { passive: true });
49
+ window.addEventListener('resize', this.resizeHandler, { passive: true });
50
+
51
+ // Smooth scroll enhancement for sidebar navigation links
52
+ this.navLinks.forEach(link => {
53
+ const clickHandler = (event) => {
54
+ const href = link.getAttribute('href');
55
+ const fragment = SidebarNavigation.extractFragment(href);
56
+
57
+ if (!fragment) return;
58
+
59
+ const target = document.getElementById(fragment);
60
+
61
+ if (target) {
62
+ event.preventDefault();
63
+
64
+ // Update URL to trigger :target and let browser handle scrolling
65
+ window.location.hash = fragment;
66
+
67
+ link.focus();
68
+ }
69
+ };
70
+
71
+ this.clickHandlers.set(link, clickHandler);
72
+ link.addEventListener('click', clickHandler);
35
73
  });
74
+ }
36
75
 
37
- // Annotate each section with which sidebar link should be active
38
- // Walk through in DOM order and track the "last seen" sidebar link
39
- let lastSeenSidebarId = null;
40
- allSections.forEach(({element, id}) => {
41
- if (sidebarLinksByFragment.has(id)) {
42
- // This section has a sidebar link - use it
43
- lastSeenSidebarId = id;
44
- }
45
- // Annotate the section element with the sidebar link to activate
46
- if (lastSeenSidebarId) {
47
- element.dataset.sidebarLink = lastSeenSidebarId;
76
+ destroy() {
77
+ // Clear any pending timer
78
+ if (this.scrollTimer) {
79
+ clearTimeout(this.scrollTimer);
80
+ this.scrollTimer = null;
48
81
  }
49
- });
50
-
51
- // Build sections array with link references for active state tracking
52
- const sections = Array.from(sidebarLinksByFragment.entries()).map(([id, link]) => {
53
- let sectionElement = document.getElementById(id);
54
82
 
55
- if (!sectionElement && id !== CSS.escape(id)) {
56
- sectionElement = document.querySelector(`#${CSS.escape(id)}`);
83
+ // Remove window event listeners
84
+ if (this.scrollHandler) {
85
+ window.removeEventListener('scroll', this.scrollHandler);
86
+ this.scrollHandler = null;
57
87
  }
58
88
 
59
- if (sectionElement && sectionElement.tagName.match(/^H[1-6]$/)) {
60
- sectionElement = sectionElement.closest('section') || sectionElement.parentElement;
89
+ if (this.resizeHandler) {
90
+ window.removeEventListener('resize', this.resizeHandler);
91
+ this.resizeHandler = null;
61
92
  }
62
93
 
63
- return sectionElement ? { link, sectionElement, id } : null;
64
- }).filter(Boolean);
65
-
66
- if (sections.length === 0) return;
67
-
68
- let currentActive = null;
94
+ // Remove click handlers from all links
95
+ this.clickHandlers.forEach((handler, link) => {
96
+ link.removeEventListener('click', handler);
97
+ });
98
+ this.clickHandlers.clear();
99
+
100
+ // Clear the section-to-sidebar-link mapping
101
+ this.sectionToSidebarLinkMap.clear();
102
+
103
+ // Remove active class from current link
104
+ if (this.currentActive) {
105
+ this.currentActive.link.classList.remove('active');
106
+ this.currentActive = null;
107
+ }
108
+ }
69
109
 
70
- function updateActiveLink(updatePageState = true) {
71
- let activeSectionData = null;
110
+ findCurrentSection() {
72
111
  let smallestValidBottom = Infinity;
73
112
  let currentSectionElement = null;
74
113
 
75
114
  // Find which section the user is currently viewing
76
- allSections.forEach(({element}) => {
115
+ this.allSections.forEach(({element}) => {
77
116
  const rect = element.getBoundingClientRect();
78
117
  const sectionBottom = rect.bottom;
79
118
 
@@ -87,54 +126,68 @@
87
126
  }
88
127
  });
89
128
 
90
- // Look up which sidebar link should be active using the data attribute
91
- if (currentSectionElement && currentSectionElement.dataset.sidebarLink) {
92
- const sidebarLinkId = currentSectionElement.dataset.sidebarLink;
93
- activeSectionData = sections.find(s => s.id === sidebarLinkId);
129
+ // Look up which sidebar link should be active using the map
130
+ if (currentSectionElement) {
131
+ const sidebarLinkId = this.sectionToSidebarLinkMap.get(currentSectionElement);
132
+ if (sidebarLinkId) {
133
+ return this.sections.find(s => s.id === sidebarLinkId);
134
+ }
94
135
  }
95
136
 
96
- // If still no section found, fall back to the last section
97
- if (!activeSectionData && sections.length > 0) {
98
- activeSectionData = sections[sections.length - 1];
137
+ // If no section found, fall back to the last section
138
+ return this.sections.length > 0 ? this.sections[this.sections.length - 1] : null;
139
+ }
140
+
141
+ setActiveSection(activeSectionData) {
142
+ // Remove previous active
143
+ if (this.currentActive) {
144
+ this.currentActive.link.classList.remove('active');
99
145
  }
100
146
 
101
- // Update active link
102
- if (activeSectionData !== currentActive) {
103
- // Remove previous active
104
- if (currentActive) {
105
- currentActive.link.classList.remove('active');
147
+ // Set new active
148
+ if (activeSectionData) {
149
+ activeSectionData.link.classList.add('active');
150
+ this.currentActive = activeSectionData;
151
+ } else {
152
+ this.currentActive = null;
153
+ }
154
+ }
155
+
156
+ updateUrlFragment(sectionId) {
157
+ if (sectionId) {
158
+ const newFragment = '#' + sectionId;
159
+ if (window.location.hash !== newFragment) {
160
+ history.replaceState(null, null, newFragment);
161
+ }
162
+ } else {
163
+ // Clear fragment if no active section
164
+ if (window.location.hash) {
165
+ history.replaceState(null, null, window.location.pathname);
106
166
  }
167
+ }
168
+ }
169
+
170
+ updateActiveLink(updatePageState = true) {
171
+ const activeSectionData = this.findCurrentSection();
172
+
173
+ // Only update if section changed
174
+ if (activeSectionData !== this.currentActive) {
175
+ this.setActiveSection(activeSectionData);
107
176
 
108
- // Set new active
109
- if (activeSectionData) {
110
- activeSectionData.link.classList.add('active');
111
- currentActive = activeSectionData;
112
-
113
- if (updatePageState) {
114
- // Update URL fragment to reflect current section
115
- const newFragment = '#' + activeSectionData.id;
116
- if (window.location.hash !== newFragment) {
117
- history.replaceState(null, null, newFragment);
118
- }
119
-
120
- // Auto-scroll sidebar to keep active item visible
121
- scrollToActiveItem(activeSectionData.link);
122
- }
123
- } else {
124
- currentActive = null;
125
-
126
- if (updatePageState) {
127
- // Clear fragment if no active section
128
- if (window.location.hash) {
129
- history.replaceState(null, null, window.location.pathname);
130
- }
177
+ if (updatePageState) {
178
+ if (activeSectionData) {
179
+ this.updateUrlFragment(activeSectionData.id);
180
+ this.scrollToActiveItem(activeSectionData.link);
181
+ } else {
182
+ this.updateUrlFragment(null);
131
183
  }
132
184
  }
133
185
  }
134
186
  }
135
187
 
136
- function scrollToActiveItem(activeLink) {
137
- const sidebar = activeLink.closest('.sidebar');
188
+ scrollToActiveItem(activeLink) {
189
+ // Use the sidebar nav container we already have
190
+ const sidebar = this.sidebarNav.closest('.sidebar');
138
191
  if (!sidebar) return;
139
192
 
140
193
  const sidebarRect = sidebar.getBoundingClientRect();
@@ -159,50 +212,69 @@
159
212
  }
160
213
  }
161
214
 
162
- // Throttled scroll handler
163
- let scrollTimer = null;
164
-
165
- function onScroll() {
166
- if (scrollTimer) return;
215
+ // Static method to initialize with default selector
216
+ static initialize(selector = '.sidebar nav') {
217
+ const sidebarNav = document.querySelector(selector);
218
+ if (!sidebarNav) return null;
167
219
 
168
- scrollTimer = setTimeout(() => {
169
- updateActiveLink();
170
- scrollTimer = null;
171
- }, 100);
172
- }
173
-
174
- // Initialize and set up event listeners
175
- window.addEventListener('scroll', onScroll, { passive: true });
176
- window.addEventListener('resize', updateActiveLink, { passive: true });
177
-
178
- // Initial update without changing page state (preserves URL fragments):
179
- updateActiveLink(false);
180
-
181
- // Smooth scroll enhancement for sidebar navigation links
182
- navLinks.forEach(link => {
183
- link.addEventListener('click', (event) => {
220
+ const navLinks = sidebarNav.querySelectorAll('a[href*="#"]');
221
+
222
+ // Build a map of sidebar links by their fragment IDs
223
+ const sidebarLinksByFragment = new Map();
224
+ navLinks.forEach(link => {
184
225
  const href = link.getAttribute('href');
185
- // Extract fragment from both "#section" and "page.html#section" formats
186
- const fragmentIndex = href.indexOf('#');
187
- if (fragmentIndex === -1) return;
188
- const fragment = href.substring(fragmentIndex + 1);
226
+ const fragment = SidebarNavigation.extractFragment(href);
189
227
 
190
- // Try to find the target using the fragment
191
- let target = document.getElementById(fragment);
192
-
193
- // If not found, try with CSS.escape for special characters
194
- if (!target && fragment !== CSS.escape(fragment)) {
195
- target = document.querySelector(`#${CSS.escape(fragment)}`);
228
+ if (fragment) {
229
+ sidebarLinksByFragment.set(fragment, link);
230
+ }
231
+ });
232
+
233
+ // Early exit if no sidebar links with fragments
234
+ if (sidebarLinksByFragment.size === 0) return null;
235
+
236
+ // Get all sections/headings on the page and annotate them with their sidebar link
237
+ const allSections = Array.from(document.querySelectorAll('section, h1, h2, h3, h4, h5, h6'))
238
+ .filter(el => el.id) // Only keep elements with IDs
239
+ .map(el => {
240
+ // Get the section element (section or heading's parent)
241
+ const sectionElement = el.tagName === 'SECTION' ? el : (el.closest('section') || el.parentElement);
242
+ return { element: sectionElement, id: el.id };
243
+ });
244
+
245
+ // Annotate each section with which sidebar link should be active
246
+ // Walk through in DOM order and track the "last seen" sidebar link
247
+ // Store mapping in a Map instead of mutating DOM with data attributes
248
+ const sectionToSidebarLinkMap = new Map();
249
+ let lastSeenSidebarId = null;
250
+ allSections.forEach(({element, id}) => {
251
+ if (sidebarLinksByFragment.has(id)) {
252
+ // This section has a sidebar link - use it
253
+ lastSeenSidebarId = id;
254
+ }
255
+ // Map the section element to the sidebar link to activate
256
+ if (lastSeenSidebarId) {
257
+ sectionToSidebarLinkMap.set(element, lastSeenSidebarId);
196
258
  }
259
+ });
260
+
261
+ // Build sections array with link references for active state tracking
262
+ const sections = Array.from(sidebarLinksByFragment.entries()).map(([id, link]) => {
263
+ let sectionElement = document.getElementById(id);
197
264
 
198
- if (target) {
199
- event.preventDefault();
200
-
201
- // Update URL to trigger :target and let browser handle scrolling
202
- window.location.hash = fragment;
265
+ if (sectionElement && sectionElement.tagName.match(/^H[1-6]$/)) {
266
+ sectionElement = sectionElement.closest('section') || sectionElement.parentElement;
203
267
  }
268
+
269
+ return sectionElement ? { link, sectionElement, id } : null;
270
+ }).filter(Boolean);
271
+
272
+ // Early exit if no valid sections found
273
+ if (sections.length === 0) return null;
274
+
275
+ // All checks passed - create the instance
276
+ return new SidebarNavigation(sidebarNav, sidebarLinksByFragment, allSections, sections, navLinks, sectionToSidebarLinkMap);
277
+ }
278
+ }
204
279
 
205
- link.focus();
206
- });
207
- });
208
- })();
280
+ export { SidebarNavigation };
@@ -594,3 +594,37 @@ ul.pragmas li.asynchronous {
594
594
  border-left-color: var(--accent-color);
595
595
  font-weight: 500;
596
596
  }
597
+
598
+ /* Guide navigation (previous/next) */
599
+ .content nav {
600
+ display: flex;
601
+ justify-content: space-between;
602
+ gap: 1rem;
603
+ margin: 1rem;
604
+ font-size: 0.9rem;
605
+ font-weight: 500;
606
+ }
607
+
608
+ .content nav.top {
609
+ border-bottom: 1px solid var(--underlay-color);
610
+ padding-bottom: 0.5rem;
611
+ }
612
+
613
+ .content nav.bottom {
614
+ border-top: 1px solid var(--underlay-color);
615
+ padding-top: 0.5rem;
616
+ }
617
+
618
+ .content nav .next {
619
+ margin-left: auto;
620
+ }
621
+
622
+ @media (max-width: 768px) {
623
+ .content nav {
624
+ flex-direction: column;
625
+ }
626
+
627
+ .content nav .next {
628
+ margin-left: 0;
629
+ }
630
+ }
data/readme.md CHANGED
@@ -33,6 +33,12 @@ Please see the [project documentation](https://socketry.github.io/utopia-project
33
33
 
34
34
  Please see the [project releases](https://socketry.github.io/utopia-project/releases/index) for all releases.
35
35
 
36
+ ### v0.40.0
37
+
38
+ - Fixed duplicate heading IDs when multiple sections have the same title. Permalinks and sidebar scroll tracking now work correctly when you have headings with identical text in different sections (e.g., multiple "Deployment" subsections under "Kubernetes" and "Systemd").
39
+ - Improved guides index page to show guide summaries (first paragraph) instead of just listing titles.
40
+ - Added previous/next navigation at the top and bottom of guide pages for easier sequential reading.
41
+
36
42
  ### v0.37.3
37
43
 
38
44
  - Support for `@example` pragmas from the `decode` gem, allowing inline code examples to be rendered in API documentation.
@@ -69,10 +75,6 @@ Please see the [project releases](https://socketry.github.io/utopia-project/rele
69
75
 
70
76
  - [Rename `changes.md` to `releases.md`](https://socketry.github.io/utopia-project/releases/index#rename-changes.md-to-releases.md)
71
77
 
72
- ### v0.29.0
73
-
74
- - [Improve `changes.md` document organization](https://socketry.github.io/utopia-project/releases/index#improve-changes.md-document-organization)
75
-
76
78
  ## See Also
77
79
 
78
80
  - [Utopia](https://github.com/socketry/utopia) — The website framework which powers this web application.
data/releases.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changes
2
2
 
3
+ ## v0.40.0
4
+
5
+ - Fixed duplicate heading IDs when multiple sections have the same title. Permalinks and sidebar scroll tracking now work correctly when you have headings with identical text in different sections (e.g., multiple "Deployment" subsections under "Kubernetes" and "Systemd").
6
+ - Improved guides index page to show guide summaries (first paragraph) instead of just listing titles.
7
+ - Added previous/next navigation at the top and bottom of guide pages for easier sequential reading.
8
+
3
9
  ## v0.37.3
4
10
 
5
11
  - Support for `@example` pragmas from the `decode` gem, allowing inline code examples to be rendered in API documentation.
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: utopia-project
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.38.0
4
+ version: 0.40.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Williams
@@ -75,14 +75,14 @@ dependencies:
75
75
  requirements:
76
76
  - - "~>"
77
77
  - !ruby/object:Gem::Version
78
- version: '0.7'
78
+ version: '0.15'
79
79
  type: :runtime
80
80
  prerelease: false
81
81
  version_requirements: !ruby/object:Gem::Requirement
82
82
  requirements:
83
83
  - - "~>"
84
84
  - !ruby/object:Gem::Version
85
- version: '0.7'
85
+ version: '0.15'
86
86
  - !ruby/object:Gem::Dependency
87
87
  name: rackula
88
88
  requirement: !ruby/object:Gem::Requirement
@@ -142,6 +142,7 @@ files:
142
142
  - lib/utopia/project/base.rb
143
143
  - lib/utopia/project/document.rb
144
144
  - lib/utopia/project/guide.rb
145
+ - lib/utopia/project/guides.rb
145
146
  - lib/utopia/project/import_map.rb
146
147
  - lib/utopia/project/linkify.rb
147
148
  - lib/utopia/project/releases_document.rb
@@ -267,79 +268,6 @@ files:
267
268
  - public/_components/@socketry/syntax/themes/base/xrb.css
268
269
  - public/_components/@socketry/syntax/themes/base/yaml.css
269
270
  - public/_components/@socketry/syntax/themes/theming.md
270
- - public/_components/jquery-litebox/jquery.litebox.css
271
- - public/_components/jquery-litebox/jquery.litebox.gallery.css
272
- - public/_components/jquery-litebox/jquery.litebox.js
273
- - public/_components/jquery-syntax/base/jquery.syntax.brush.apache.css
274
- - public/_components/jquery-syntax/base/jquery.syntax.brush.applescript.css
275
- - public/_components/jquery-syntax/base/jquery.syntax.brush.assembly.css
276
- - public/_components/jquery-syntax/base/jquery.syntax.brush.bash-script.css
277
- - public/_components/jquery-syntax/base/jquery.syntax.brush.bash.css
278
- - public/_components/jquery-syntax/base/jquery.syntax.brush.clang.css
279
- - public/_components/jquery-syntax/base/jquery.syntax.brush.css.css
280
- - public/_components/jquery-syntax/base/jquery.syntax.brush.diff.css
281
- - public/_components/jquery-syntax/base/jquery.syntax.brush.html.css
282
- - public/_components/jquery-syntax/base/jquery.syntax.brush.ocaml.css
283
- - public/_components/jquery-syntax/base/jquery.syntax.brush.protobuf.css
284
- - public/_components/jquery-syntax/base/jquery.syntax.brush.python.css
285
- - public/_components/jquery-syntax/base/jquery.syntax.brush.ruby.css
286
- - public/_components/jquery-syntax/base/jquery.syntax.brush.xml.css
287
- - public/_components/jquery-syntax/base/jquery.syntax.core.css
288
- - public/_components/jquery-syntax/base/jquery.syntax.editor.css
289
- - public/_components/jquery-syntax/base/theme.js
290
- - public/_components/jquery-syntax/bright/jquery.syntax.core.css
291
- - public/_components/jquery-syntax/bright/theme.js
292
- - public/_components/jquery-syntax/jquery.syntax.brush.apache.js
293
- - public/_components/jquery-syntax/jquery.syntax.brush.applescript.js
294
- - public/_components/jquery-syntax/jquery.syntax.brush.assembly.js
295
- - public/_components/jquery-syntax/jquery.syntax.brush.bash-script.js
296
- - public/_components/jquery-syntax/jquery.syntax.brush.bash.js
297
- - public/_components/jquery-syntax/jquery.syntax.brush.basic.js
298
- - public/_components/jquery-syntax/jquery.syntax.brush.clang.js
299
- - public/_components/jquery-syntax/jquery.syntax.brush.csharp.js
300
- - public/_components/jquery-syntax/jquery.syntax.brush.css.js
301
- - public/_components/jquery-syntax/jquery.syntax.brush.diff.js
302
- - public/_components/jquery-syntax/jquery.syntax.brush.go.js
303
- - public/_components/jquery-syntax/jquery.syntax.brush.haskell.js
304
- - public/_components/jquery-syntax/jquery.syntax.brush.html.js
305
- - public/_components/jquery-syntax/jquery.syntax.brush.io.js
306
- - public/_components/jquery-syntax/jquery.syntax.brush.java.js
307
- - public/_components/jquery-syntax/jquery.syntax.brush.javascript.js
308
- - public/_components/jquery-syntax/jquery.syntax.brush.kai.js
309
- - public/_components/jquery-syntax/jquery.syntax.brush.lisp.js
310
- - public/_components/jquery-syntax/jquery.syntax.brush.lua.js
311
- - public/_components/jquery-syntax/jquery.syntax.brush.nginx.js
312
- - public/_components/jquery-syntax/jquery.syntax.brush.ocaml.js
313
- - public/_components/jquery-syntax/jquery.syntax.brush.ooc.js
314
- - public/_components/jquery-syntax/jquery.syntax.brush.pascal.js
315
- - public/_components/jquery-syntax/jquery.syntax.brush.perl5.js
316
- - public/_components/jquery-syntax/jquery.syntax.brush.php-script.js
317
- - public/_components/jquery-syntax/jquery.syntax.brush.php.js
318
- - public/_components/jquery-syntax/jquery.syntax.brush.plain.js
319
- - public/_components/jquery-syntax/jquery.syntax.brush.protobuf.js
320
- - public/_components/jquery-syntax/jquery.syntax.brush.python.js
321
- - public/_components/jquery-syntax/jquery.syntax.brush.ruby.js
322
- - public/_components/jquery-syntax/jquery.syntax.brush.scala.js
323
- - public/_components/jquery-syntax/jquery.syntax.brush.smalltalk.js
324
- - public/_components/jquery-syntax/jquery.syntax.brush.sql.js
325
- - public/_components/jquery-syntax/jquery.syntax.brush.super-collider.js
326
- - public/_components/jquery-syntax/jquery.syntax.brush.swift.js
327
- - public/_components/jquery-syntax/jquery.syntax.brush.xml.js
328
- - public/_components/jquery-syntax/jquery.syntax.brush.xrb.js
329
- - public/_components/jquery-syntax/jquery.syntax.brush.yaml.js
330
- - public/_components/jquery-syntax/jquery.syntax.cache.js
331
- - public/_components/jquery-syntax/jquery.syntax.core.js
332
- - public/_components/jquery-syntax/jquery.syntax.editor.js
333
- - public/_components/jquery-syntax/jquery.syntax.js
334
- - public/_components/jquery-syntax/jquery.syntax.min.js
335
- - public/_components/jquery-syntax/paper/jquery.syntax.core.css
336
- - public/_components/jquery-syntax/paper/theme.js
337
- - public/_components/jquery/jquery.js
338
- - public/_components/jquery/jquery.min.js
339
- - public/_components/jquery/jquery.min.map
340
- - public/_components/jquery/jquery.slim.js
341
- - public/_components/jquery/jquery.slim.min.js
342
- - public/_components/jquery/jquery.slim.min.map
343
271
  - public/_components/mermaid/chunks/mermaid.core/architectureDiagram-VXUJARFQ.mjs
344
272
  - public/_components/mermaid/chunks/mermaid.core/architectureDiagram-VXUJARFQ.mjs.map
345
273
  - public/_components/mermaid/chunks/mermaid.core/blockDiagram-VD42YOAC.mjs
@@ -738,6 +666,7 @@ files:
738
666
  - public/_components/mermaid/mermaid.js.map
739
667
  - public/_components/mermaid/mermaid.min.js
740
668
  - public/_components/mermaid/mermaid.min.js.map
669
+ - public/_static/application.js
741
670
  - public/_static/icon.png
742
671
  - public/_static/links.js
743
672
  - public/_static/sidebar.js
@@ -769,7 +698,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
769
698
  - !ruby/object:Gem::Version
770
699
  version: '0'
771
700
  requirements: []
772
- rubygems_version: 3.7.2
701
+ rubygems_version: 3.6.9
773
702
  specification_version: 4
774
703
  summary: A project documentation tool based on Utopia.
775
704
  test_files: []
metadata.gz.sig CHANGED
@@ -1 +1,3 @@
1
- dd^�N�=!dѰ�Cޠ�����M���6����er�<�\��FA<M�M��詴���)>$g��'�e2k�ep������.�f�#��m �}3��Vx���߹�c`�G�ڷ)�/���h��~��p�OK��T2,C죄Ӹ���S/�-�t|�w٨� (�C}��U
1
+ h��j���d�D1}i����HB�*���%4V��7^��Eki>�#n&����æ�I^�
2
+ ��i��t����g�&1�����7� o�~��^jLԪ�r^=s~������Q����s�_���4 ��
3
+ @;(>H\��n��Bc��&;\pȝ��w�1�9��Lq��x!���kMJ�s�RМ*d