utopia-project 0.37.6 → 0.39.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 (181) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/context/documentation-guidelines.md +12 -7
  4. data/lib/utopia/project/import_map.rb +1 -0
  5. data/lib/utopia/project/version.rb +1 -1
  6. data/pages/_page.xnode +2 -24
  7. data/pages/guides/show.xnode +0 -1
  8. data/pages/releases/index.xnode +0 -1
  9. data/public/_components/@socketry/syntax/Syntax/CodeElement.js +293 -0
  10. data/public/_components/@socketry/syntax/Syntax/Errors.js +52 -0
  11. data/public/_components/@socketry/syntax/Syntax/Language/apache.js +49 -0
  12. data/public/_components/@socketry/syntax/Syntax/Language/applescript.js +157 -0
  13. data/public/_components/@socketry/syntax/Syntax/Language/assembly.js +42 -0
  14. data/public/_components/@socketry/syntax/Syntax/Language/bash-script.js +108 -0
  15. data/public/_components/@socketry/syntax/Syntax/Language/bash.js +32 -0
  16. data/public/_components/@socketry/syntax/Syntax/Language/basic.js +232 -0
  17. data/public/_components/@socketry/syntax/Syntax/Language/c++.js +1 -0
  18. data/public/_components/@socketry/syntax/Syntax/Language/c.js +1 -0
  19. data/public/_components/@socketry/syntax/Syntax/Language/clang.js +201 -0
  20. data/public/_components/@socketry/syntax/Syntax/Language/cpp.js +1 -0
  21. data/public/_components/@socketry/syntax/Syntax/Language/csharp.js +166 -0
  22. data/public/_components/@socketry/syntax/Syntax/Language/css.js +244 -0
  23. data/public/_components/@socketry/syntax/Syntax/Language/diff.js +24 -0
  24. data/public/_components/@socketry/syntax/Syntax/Language/go.js +135 -0
  25. data/public/_components/@socketry/syntax/Syntax/Language/haskell.js +110 -0
  26. data/public/_components/@socketry/syntax/Syntax/Language/html.js +69 -0
  27. data/public/_components/@socketry/syntax/Syntax/Language/io.js +68 -0
  28. data/public/_components/@socketry/syntax/Syntax/Language/java.js +134 -0
  29. data/public/_components/@socketry/syntax/Syntax/Language/javascript.js +89 -0
  30. data/public/_components/@socketry/syntax/Syntax/Language/json.js +36 -0
  31. data/public/_components/@socketry/syntax/Syntax/Language/lisp.js +38 -0
  32. data/public/_components/@socketry/syntax/Syntax/Language/lua.js +87 -0
  33. data/public/_components/@socketry/syntax/Syntax/Language/markdown.js +112 -0
  34. data/public/_components/@socketry/syntax/Syntax/Language/nginx.js +37 -0
  35. data/public/_components/@socketry/syntax/Syntax/Language/objective-c.js +1 -0
  36. data/public/_components/@socketry/syntax/Syntax/Language/ocaml.js +225 -0
  37. data/public/_components/@socketry/syntax/Syntax/Language/pascal.js +166 -0
  38. data/public/_components/@socketry/syntax/Syntax/Language/patch.js +2 -0
  39. data/public/_components/@socketry/syntax/Syntax/Language/perl5.js +317 -0
  40. data/public/_components/@socketry/syntax/Syntax/Language/php-script.js +112 -0
  41. data/public/_components/@socketry/syntax/Syntax/Language/php.js +18 -0
  42. data/public/_components/@socketry/syntax/Syntax/Language/plain.js +20 -0
  43. data/public/_components/@socketry/syntax/Syntax/Language/protobuf.js +77 -0
  44. data/public/_components/@socketry/syntax/Syntax/Language/python.js +208 -0
  45. data/public/_components/@socketry/syntax/Syntax/Language/ruby.js +124 -0
  46. data/public/_components/@socketry/syntax/Syntax/Language/scala.js +81 -0
  47. data/public/_components/@socketry/syntax/Syntax/Language/smalltalk.js +30 -0
  48. data/public/_components/@socketry/syntax/Syntax/Language/sql.js +865 -0
  49. data/public/_components/@socketry/syntax/Syntax/Language/super-collider.js +70 -0
  50. data/public/_components/@socketry/syntax/Syntax/Language/swift.js +176 -0
  51. data/public/_components/@socketry/syntax/Syntax/Language/xml.js +76 -0
  52. data/public/_components/@socketry/syntax/Syntax/Language/xrb.js +33 -0
  53. data/public/_components/@socketry/syntax/Syntax/Language/yaml.js +29 -0
  54. data/public/_components/@socketry/syntax/Syntax/Language.js +276 -0
  55. data/public/_components/@socketry/syntax/Syntax/Loader.js +78 -0
  56. data/public/_components/@socketry/syntax/Syntax/Match.js +546 -0
  57. data/public/_components/@socketry/syntax/Syntax/Rule.js +306 -0
  58. data/public/_components/@socketry/syntax/Syntax.js +356 -0
  59. data/public/_components/@socketry/syntax/license.md +21 -0
  60. data/public/_components/@socketry/syntax/package.json +43 -0
  61. data/public/_components/@socketry/syntax/readme.md +162 -0
  62. data/public/_components/@socketry/syntax/themes/base/apache.css +1 -0
  63. data/public/_components/@socketry/syntax/themes/base/applescript.css +1 -0
  64. data/public/_components/@socketry/syntax/themes/base/assembly.css +1 -0
  65. data/public/_components/@socketry/syntax/themes/base/bash.css +1 -0
  66. data/public/_components/@socketry/syntax/themes/base/basic.css +1 -0
  67. data/public/_components/@socketry/syntax/themes/base/c.css +1 -0
  68. data/public/_components/@socketry/syntax/themes/base/clang.css +0 -0
  69. data/public/_components/@socketry/syntax/themes/base/csharp.css +1 -0
  70. data/public/_components/@socketry/syntax/themes/base/css.css +22 -0
  71. data/public/_components/@socketry/syntax/themes/base/diff.css +48 -0
  72. data/public/_components/@socketry/syntax/themes/base/go.css +1 -0
  73. data/public/_components/@socketry/syntax/themes/base/haskell.css +1 -0
  74. data/public/_components/@socketry/syntax/themes/base/html.css +1 -0
  75. data/public/_components/@socketry/syntax/themes/base/io.css +1 -0
  76. data/public/_components/@socketry/syntax/themes/base/java.css +1 -0
  77. data/public/_components/@socketry/syntax/themes/base/javascript.css +1 -0
  78. data/public/_components/@socketry/syntax/themes/base/json.css +41 -0
  79. data/public/_components/@socketry/syntax/themes/base/lisp.css +1 -0
  80. data/public/_components/@socketry/syntax/themes/base/lua.css +1 -0
  81. data/public/_components/@socketry/syntax/themes/base/markdown.css +16 -0
  82. data/public/_components/@socketry/syntax/themes/base/nginx.css +1 -0
  83. data/public/_components/@socketry/syntax/themes/base/ocaml.css +1 -0
  84. data/public/_components/@socketry/syntax/themes/base/pascal.css +1 -0
  85. data/public/_components/@socketry/syntax/themes/base/perl5.css +1 -0
  86. data/public/_components/@socketry/syntax/themes/base/php-script.css +1 -0
  87. data/public/_components/@socketry/syntax/themes/base/php.css +1 -0
  88. data/public/_components/@socketry/syntax/themes/base/plain.css +1 -0
  89. data/public/_components/@socketry/syntax/themes/base/protobuf.css +1 -0
  90. data/public/_components/@socketry/syntax/themes/base/python.css +1 -0
  91. data/public/_components/@socketry/syntax/themes/base/ruby.css +23 -0
  92. data/public/_components/@socketry/syntax/themes/base/scala.css +3 -0
  93. data/public/_components/@socketry/syntax/themes/base/smalltalk.css +1 -0
  94. data/public/_components/@socketry/syntax/themes/base/sql.css +1 -0
  95. data/public/_components/@socketry/syntax/themes/base/super-collider.css +33 -0
  96. data/public/_components/@socketry/syntax/themes/base/swift.css +1 -0
  97. data/public/_components/@socketry/syntax/themes/base/syntax.css +63 -0
  98. data/public/_components/@socketry/syntax/themes/base/xml.css +1 -0
  99. data/public/_components/@socketry/syntax/themes/base/xrb.css +29 -0
  100. data/public/_components/@socketry/syntax/themes/base/yaml.css +1 -0
  101. data/public/_components/@socketry/syntax/themes/theming.md +233 -0
  102. data/public/_static/application.js +24 -0
  103. data/public/_static/links.js +18 -16
  104. data/public/_static/sidebar.js +216 -111
  105. data/public/_static/site.css +0 -4
  106. data.tar.gz.sig +0 -0
  107. metadata +95 -74
  108. metadata.gz.sig +0 -0
  109. data/public/_components/jquery/jquery.js +0 -10716
  110. data/public/_components/jquery/jquery.min.js +0 -2
  111. data/public/_components/jquery/jquery.min.map +0 -1
  112. data/public/_components/jquery/jquery.slim.js +0 -8617
  113. data/public/_components/jquery/jquery.slim.min.js +0 -2
  114. data/public/_components/jquery/jquery.slim.min.map +0 -1
  115. data/public/_components/jquery-litebox/jquery.litebox.css +0 -23
  116. data/public/_components/jquery-litebox/jquery.litebox.gallery.css +0 -48
  117. data/public/_components/jquery-litebox/jquery.litebox.js +0 -30
  118. data/public/_components/jquery-syntax/base/jquery.syntax.brush.apache.css +0 -12
  119. data/public/_components/jquery-syntax/base/jquery.syntax.brush.applescript.css +0 -5
  120. data/public/_components/jquery-syntax/base/jquery.syntax.brush.assembly.css +0 -8
  121. data/public/_components/jquery-syntax/base/jquery.syntax.brush.bash-script.css +0 -4
  122. data/public/_components/jquery-syntax/base/jquery.syntax.brush.bash.css +0 -2
  123. data/public/_components/jquery-syntax/base/jquery.syntax.brush.clang.css +0 -6
  124. data/public/_components/jquery-syntax/base/jquery.syntax.brush.css.css +0 -14
  125. data/public/_components/jquery-syntax/base/jquery.syntax.brush.diff.css +0 -16
  126. data/public/_components/jquery-syntax/base/jquery.syntax.brush.html.css +0 -3
  127. data/public/_components/jquery-syntax/base/jquery.syntax.brush.ocaml.css +0 -3
  128. data/public/_components/jquery-syntax/base/jquery.syntax.brush.protobuf.css +0 -2
  129. data/public/_components/jquery-syntax/base/jquery.syntax.brush.python.css +0 -6
  130. data/public/_components/jquery-syntax/base/jquery.syntax.brush.ruby.css +0 -2
  131. data/public/_components/jquery-syntax/base/jquery.syntax.brush.xml.css +0 -35
  132. data/public/_components/jquery-syntax/base/jquery.syntax.core.css +0 -58
  133. data/public/_components/jquery-syntax/base/jquery.syntax.editor.css +0 -6
  134. data/public/_components/jquery-syntax/base/theme.js +0 -1
  135. data/public/_components/jquery-syntax/bright/jquery.syntax.core.css +0 -27
  136. data/public/_components/jquery-syntax/bright/theme.js +0 -1
  137. data/public/_components/jquery-syntax/jquery.syntax.brush.apache.js +0 -3
  138. data/public/_components/jquery-syntax/jquery.syntax.brush.applescript.js +0 -5
  139. data/public/_components/jquery-syntax/jquery.syntax.brush.assembly.js +0 -3
  140. data/public/_components/jquery-syntax/jquery.syntax.brush.bash-script.js +0 -4
  141. data/public/_components/jquery-syntax/jquery.syntax.brush.bash.js +0 -2
  142. data/public/_components/jquery-syntax/jquery.syntax.brush.basic.js +0 -5
  143. data/public/_components/jquery-syntax/jquery.syntax.brush.clang.js +0 -5
  144. data/public/_components/jquery-syntax/jquery.syntax.brush.csharp.js +0 -4
  145. data/public/_components/jquery-syntax/jquery.syntax.brush.css.js +0 -5
  146. data/public/_components/jquery-syntax/jquery.syntax.brush.diff.js +0 -2
  147. data/public/_components/jquery-syntax/jquery.syntax.brush.go.js +0 -3
  148. data/public/_components/jquery-syntax/jquery.syntax.brush.haskell.js +0 -3
  149. data/public/_components/jquery-syntax/jquery.syntax.brush.html.js +0 -4
  150. data/public/_components/jquery-syntax/jquery.syntax.brush.io.js +0 -3
  151. data/public/_components/jquery-syntax/jquery.syntax.brush.java.js +0 -4
  152. data/public/_components/jquery-syntax/jquery.syntax.brush.javascript.js +0 -3
  153. data/public/_components/jquery-syntax/jquery.syntax.brush.kai.js +0 -2
  154. data/public/_components/jquery-syntax/jquery.syntax.brush.lisp.js +0 -2
  155. data/public/_components/jquery-syntax/jquery.syntax.brush.lua.js +0 -3
  156. data/public/_components/jquery-syntax/jquery.syntax.brush.nginx.js +0 -2
  157. data/public/_components/jquery-syntax/jquery.syntax.brush.ocaml.js +0 -4
  158. data/public/_components/jquery-syntax/jquery.syntax.brush.ooc.js +0 -4
  159. data/public/_components/jquery-syntax/jquery.syntax.brush.pascal.js +0 -4
  160. data/public/_components/jquery-syntax/jquery.syntax.brush.perl5.js +0 -3
  161. data/public/_components/jquery-syntax/jquery.syntax.brush.php-script.js +0 -4
  162. data/public/_components/jquery-syntax/jquery.syntax.brush.php.js +0 -2
  163. data/public/_components/jquery-syntax/jquery.syntax.brush.plain.js +0 -2
  164. data/public/_components/jquery-syntax/jquery.syntax.brush.protobuf.js +0 -3
  165. data/public/_components/jquery-syntax/jquery.syntax.brush.python.js +0 -5
  166. data/public/_components/jquery-syntax/jquery.syntax.brush.ruby.js +0 -5
  167. data/public/_components/jquery-syntax/jquery.syntax.brush.scala.js +0 -4
  168. data/public/_components/jquery-syntax/jquery.syntax.brush.smalltalk.js +0 -2
  169. data/public/_components/jquery-syntax/jquery.syntax.brush.sql.js +0 -4
  170. data/public/_components/jquery-syntax/jquery.syntax.brush.super-collider.js +0 -3
  171. data/public/_components/jquery-syntax/jquery.syntax.brush.swift.js +0 -3
  172. data/public/_components/jquery-syntax/jquery.syntax.brush.xml.js +0 -4
  173. data/public/_components/jquery-syntax/jquery.syntax.brush.xrb.js +0 -2
  174. data/public/_components/jquery-syntax/jquery.syntax.brush.yaml.js +0 -2
  175. data/public/_components/jquery-syntax/jquery.syntax.cache.js +0 -7
  176. data/public/_components/jquery-syntax/jquery.syntax.core.js +0 -34
  177. data/public/_components/jquery-syntax/jquery.syntax.editor.js +0 -11
  178. data/public/_components/jquery-syntax/jquery.syntax.js +0 -8
  179. data/public/_components/jquery-syntax/jquery.syntax.min.js +0 -13
  180. data/public/_components/jquery-syntax/paper/jquery.syntax.core.css +0 -31
  181. data/public/_components/jquery-syntax/paper/theme.js +0 -1
@@ -0,0 +1,233 @@
1
+ # Theming Guide
2
+
3
+ The `@socketry/syntax` library uses CSS custom properties (variables) with HSL colors to provide a flexible and maintainable theming system.
4
+
5
+ ## Key Benefits
6
+
7
+ 1. **Ultra Simple**: Change just 3 variables to create a complete theme
8
+ 2. **Automatic Dark Mode**: Light and dark mode handled automatically
9
+ 3. **Automatic Color Harmony**: All colors derived from one base color using hue rotation
10
+ 4. **Easy Customization**: Override the base or individual colors as needed
11
+
12
+ ## Theme Architecture
13
+
14
+ ### The Base Color System
15
+
16
+ Everything derives from three base variables:
17
+
18
+ ```css
19
+ :host {
20
+ --syntax-base-hue: 270; /* 0-360 degrees on the color wheel */
21
+ --syntax-base-saturation: 60%; /* How vibrant the colors are */
22
+ --syntax-base-lightness: 50%; /* How light/dark the colors are */
23
+ }
24
+ ```
25
+
26
+ **Dark mode** just adjusts saturation and lightness:
27
+
28
+ ```css
29
+ @media (prefers-color-scheme: dark) {
30
+ :host {
31
+ --syntax-base-saturation: 50%; /* Slightly less saturated */
32
+ --syntax-base-lightness: 70%; /* Much lighter for dark backgrounds */
33
+ }
34
+ }
35
+ ```
36
+
37
+ ### Automatic Color Derivation
38
+
39
+ All token colors are automatically calculated using hue rotation:
40
+
41
+ ```css
42
+ --syntax-keyword: hsl(calc(var(--syntax-base-hue) + 30), ...); /* 30° rotation */
43
+ --syntax-string: hsl(calc(var(--syntax-base-hue) - 90), ...); /* 90° opposite */
44
+ --syntax-function: hsl(calc(var(--syntax-base-hue) - 60), ...); /* 60° rotation */
45
+ /* etc. */
46
+ ```
47
+
48
+ This ensures all colors are harmonious and work together!
49
+
50
+ ## Creating Custom Themes
51
+
52
+ ### Method 1: Change the Base Hue (Recommended)
53
+
54
+ The simplest approach - just pick a different base color:
55
+
56
+ ```css
57
+ .ocean-theme {
58
+ --syntax-base-hue: 200; /* Blue - everything becomes ocean-themed! */
59
+ }
60
+
61
+ .sunset-theme {
62
+ --syntax-base-hue: 30; /* Orange - warm sunset colors */
63
+ }
64
+
65
+ .forest-theme {
66
+ --syntax-base-hue: 140; /* Green - natural forest palette */
67
+ }
68
+ ```
69
+
70
+ Then apply it:
71
+
72
+ ```html
73
+ <syntax-code language="javascript" class="ocean-theme">
74
+ // Your code here
75
+ </syntax-code>
76
+ ```
77
+
78
+ ### Method 2: Adjust Saturation and Lightness
79
+
80
+ For more subtle themes:
81
+
82
+ ```css
83
+ .pastel-theme {
84
+ --syntax-base-saturation: 40%; /* Less vibrant */
85
+ --syntax-base-lightness: 65%; /* Lighter */
86
+ }
87
+
88
+ .vivid-theme {
89
+ --syntax-base-saturation: 90%; /* Very saturated */
90
+ --syntax-base-lightness: 45%; /* Slightly darker */
91
+ }
92
+ ```
93
+
94
+ ### Method 3: Override Specific Colors
95
+
96
+ For fine-grained control:
97
+
98
+ ```css
99
+ .custom-theme {
100
+ --syntax-base-hue: 280; /* Purple base */
101
+
102
+ /* But make strings green instead of the derived color */
103
+ --syntax-string: hsl(120, 60%, 40%);
104
+ }
105
+ ```
106
+
107
+ ### Method 4: Custom Dark Mode
108
+
109
+ Override dark mode values for specific themes:
110
+
111
+ ```css
112
+ .my-theme {
113
+ --syntax-base-hue: 200;
114
+ }
115
+
116
+ @media (prefers-color-scheme: dark) {
117
+ .my-theme {
118
+ /* Different hue in dark mode */
119
+ --syntax-base-hue: 180;
120
+ --syntax-base-saturation: 45%;
121
+ --syntax-base-lightness: 75%;
122
+ }
123
+ }
124
+ ```
125
+
126
+ ## Token Types
127
+
128
+ All token types automatically get colors derived from the base. You can override individual ones if needed:
129
+
130
+ ### Programming Language Tokens
131
+
132
+ - `keyword` - Language keywords (if, function, class)
133
+ - `string` - String literals
134
+ - `comment` - Code comments
135
+ - `number` - Numeric literals
136
+ - `operator` - Operators (+, -, *, etc.)
137
+ - `function` - Function names
138
+ - `constant` - Constants and literals
139
+ - `type` - Type names
140
+ - `preprocessor` - Preprocessor directives
141
+ - `access` - Access modifiers (public, private)
142
+ - `escape` - Escape sequences
143
+
144
+ ### HTML/XML Tokens
145
+
146
+ - `tag` - HTML/XML tags
147
+ - `tag-name` - Tag names
148
+ - `attribute` - Attributes
149
+ - `entity` - HTML entities
150
+ - `doctype` - DOCTYPE declarations
151
+ - `cdata` - CDATA sections
152
+
153
+ ### Semantic Markdown Tokens
154
+
155
+ - `heading` - Headers (# Heading)
156
+ - `strong` - Bold text (**bold**)
157
+ - `emphasis` - Italic text (*italic*)
158
+ - `code` - Code blocks and inline code
159
+ - `link` - Links and images
160
+ - `quote` - Blockquotes
161
+ - `list-marker` - List markers
162
+
163
+ ## Dark Mode
164
+
165
+ Dark mode colors are automatically applied using `prefers-color-scheme`:
166
+
167
+ ```css
168
+ @media (prefers-color-scheme: dark) {
169
+ :host {
170
+ --syntax-keyword: hsl(var(--syntax-hue-primary), 50%, 70%);
171
+ /* Automatically lighter and less saturated for dark backgrounds */
172
+ }
173
+ }
174
+ ```
175
+
176
+ You can override dark mode colors in your custom theme:
177
+
178
+ ```css
179
+ .my-theme {
180
+ --syntax-keyword: hsl(300, 80%, 40%);
181
+ }
182
+
183
+ @media (prefers-color-scheme: dark) {
184
+ .my-theme {
185
+ --syntax-keyword: hsl(300, 60%, 75%);
186
+ }
187
+ }
188
+ ```
189
+
190
+ ## Example Themes
191
+
192
+ ### Monochrome
193
+
194
+ ```css
195
+ .monochrome {
196
+ --syntax-keyword: hsl(0, 0%, 20%);
197
+ --syntax-string: hsl(0, 0%, 30%);
198
+ --syntax-comment: hsl(0, 0%, 60%);
199
+ --syntax-function: hsl(0, 0%, 25%);
200
+ }
201
+ ```
202
+
203
+ ### High Contrast
204
+
205
+ ```css
206
+ .high-contrast {
207
+ --syntax-hue-primary: 270;
208
+ /* Increase saturation and contrast */
209
+ --syntax-keyword: hsl(var(--syntax-hue-primary), 100%, 30%);
210
+ --syntax-string: hsl(0, 100%, 35%);
211
+ --syntax-comment: hsl(0, 0%, 40%);
212
+ }
213
+ ```
214
+
215
+ ### Pastel
216
+
217
+ ```css
218
+ .pastel {
219
+ /* Use same hues but with high lightness and low saturation */
220
+ --syntax-keyword: hsl(var(--syntax-hue-primary), 40%, 60%);
221
+ --syntax-string: hsl(var(--syntax-hue-tertiary), 40%, 60%);
222
+ --syntax-comment: hsl(var(--syntax-hue-neutral), 20%, 70%);
223
+ }
224
+ ```
225
+
226
+ ## Tips
227
+
228
+ 1. **Hue Relationships**: Use complementary (180° apart) or analogous (30° apart) hues for harmony
229
+ 2. **Saturation**: Lower saturation (20-40%) for subtle themes, higher (70-90%) for vibrant themes
230
+ 3. **Lightness**: Keep 30-50% for light mode, 60-80% for dark mode
231
+ 4. **Contrast**: Ensure sufficient contrast between text and background (WCAG AA: 4.5:1 minimum)
232
+ 5. **Testing**: Test themes in both light and dark mode
233
+ 6. **Consistency**: Use the hue variables to maintain color relationships across tokens
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Application Initialization
3
+ * Main entry point for client-side functionality
4
+ */
5
+
6
+ // Import dependencies:
7
+ import {Syntax} from '@socketry/syntax';
8
+ import mermaid from 'mermaid';
9
+ import {initializeSectionHeadingLinks} from './links.js';
10
+ import {SidebarNavigation} from './sidebar.js';
11
+
12
+ // Initialize Mermaid with theme detection:
13
+ const isDarkMode = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
14
+ const theme = isDarkMode ? 'dark' : 'default';
15
+ mermaid.initialize({startOnLoad: true, theme: theme});
16
+
17
+ // Initialize section self-links:
18
+ initializeSectionHeadingLinks();
19
+
20
+ // Initialize sidebar navigation:
21
+ SidebarNavigation.initialize();
22
+
23
+ // Initialize syntax highlighting:
24
+ await Syntax.highlight();
@@ -1,21 +1,23 @@
1
- jQuery(function() {
2
- $.each($('section[id]'), function(index, element) {
3
- let anchor = document.createElement('a');
4
-
5
- anchor.appendChild(
6
- document.createTextNode("¶")
7
- );
1
+ /**
2
+ * Section Heading Links
3
+ * Adds pilcrow () self-links to section headings for easy linking
4
+ */
5
+
6
+ export function initializeSectionHeadingLinks() {
7
+ const sections = document.querySelectorAll('section[id]');
8
+
9
+ sections.forEach(element => {
10
+ const anchor = document.createElement('a');
8
11
 
12
+ anchor.appendChild(document.createTextNode("¶"));
9
13
  anchor.href = "#" + element.id;
10
14
  anchor.className = "self";
11
15
 
12
- let heading = element.firstChild;
13
- anchor.title = heading.innerText;
14
-
15
- heading.appendChild(
16
- document.createTextNode(' ')
17
- );
18
-
19
- heading.appendChild(anchor);
16
+ const heading = element.firstChild;
17
+ if (heading) {
18
+ anchor.title = heading.innerText;
19
+ heading.appendChild(document.createTextNode(' '));
20
+ heading.appendChild(anchor);
21
+ }
20
22
  });
21
- });
23
+ }
@@ -3,105 +3,191 @@
3
3
  * Progressive enhancement for sidebar navigation with scroll tracking
4
4
  */
5
5
 
6
- (function() {
7
- 'use strict';
6
+ class SidebarNavigation {
7
+ static extractFragment(href) {
8
+ const index = href.indexOf('#');
9
+ return index !== -1 ? href.substring(index + 1) : null;
10
+ }
8
11
 
9
- // Only run if we have a sidebar navigation
10
- const sidebarNav = document.querySelector('.sidebar nav');
11
- if (!sidebarNav) 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
+ }
33
+
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);
73
+ });
74
+ }
12
75
 
13
- const navLinks = sidebarNav.querySelectorAll('a[href*="#"]');
14
- const sections = Array.from(navLinks).map(link => {
15
- const href = link.getAttribute('href');
16
- // Extract fragment from both "#section" and "page.html#section" formats
17
- const fragmentIndex = href.indexOf('#');
18
- if (fragmentIndex === -1) return null;
19
- const fragment = href.substring(fragmentIndex + 1);
20
- // Try to find the section element using the fragment
21
- let sectionElement = document.getElementById(fragment);
22
-
23
- // If not found, try with CSS.escape for special characters
24
- if (!sectionElement && fragment !== CSS.escape(fragment)) {
25
- sectionElement = document.querySelector(`#${CSS.escape(fragment)}`);
76
+ destroy() {
77
+ // Clear any pending timer
78
+ if (this.scrollTimer) {
79
+ clearTimeout(this.scrollTimer);
80
+ this.scrollTimer = null;
26
81
  }
27
82
 
28
- // The element we found should be the section container, not just the heading
29
- // If it's a heading, find its parent section
30
- if (sectionElement && sectionElement.tagName.match(/^H[1-6]$/)) {
31
- sectionElement = sectionElement.closest('section') || sectionElement.parentElement;
83
+ // Remove window event listeners
84
+ if (this.scrollHandler) {
85
+ window.removeEventListener('scroll', this.scrollHandler);
86
+ this.scrollHandler = null;
32
87
  }
33
88
 
34
- return { link, sectionElement, id: fragment };
35
- }).filter(item => item && item.sectionElement);
36
-
37
- if (sections.length === 0) return;
38
-
39
- let currentActive = null;
89
+ if (this.resizeHandler) {
90
+ window.removeEventListener('resize', this.resizeHandler);
91
+ this.resizeHandler = null;
92
+ }
93
+
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
+ }
40
109
 
41
- function updateActiveLink(updatePageState = true) {
42
- let activeSectionData = null;
110
+ findCurrentSection() {
43
111
  let smallestValidBottom = Infinity;
112
+ let currentSectionElement = null;
44
113
 
45
- // Find the section with the smallest bottom position that is still >= 0
46
- // This gives us the section that we're currently reading through
47
- sections.forEach((sectionData) => {
48
- const { sectionElement } = sectionData;
49
- const rect = sectionElement.getBoundingClientRect();
114
+ // Find which section the user is currently viewing
115
+ this.allSections.forEach(({element}) => {
116
+ const rect = element.getBoundingClientRect();
50
117
  const sectionBottom = rect.bottom;
51
118
 
52
119
  // Get the actual bottom padding for this section
53
- const bottomPadding = parseFloat(getComputedStyle(sectionElement).paddingBottom);
120
+ const bottomPadding = parseFloat(getComputedStyle(element).paddingBottom);
54
121
 
55
122
  // We want the section whose bottom is closest to the top but still visible
56
123
  if (sectionBottom > bottomPadding && sectionBottom < smallestValidBottom) {
57
124
  smallestValidBottom = sectionBottom;
58
- activeSectionData = sectionData;
125
+ currentSectionElement = element;
59
126
  }
60
127
  });
61
128
 
62
- // If no section has bottom >= 0, fall back to the last section
63
- // (meaning we've scrolled past all content)
64
- if (!activeSectionData && sections.length > 0) {
65
- activeSectionData = sections[sections.length - 1];
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
+ }
135
+ }
136
+
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');
66
145
  }
67
146
 
68
- // Update active link
69
- if (activeSectionData !== currentActive) {
70
- // Remove previous active
71
- if (currentActive) {
72
- 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);
73
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);
74
176
 
75
- // Set new active
76
- if (activeSectionData) {
77
- activeSectionData.link.classList.add('active');
78
- currentActive = activeSectionData;
79
-
80
- if (updatePageState) {
81
- // Update URL fragment to reflect current section
82
- const newFragment = '#' + activeSectionData.id;
83
- if (window.location.hash !== newFragment) {
84
- history.replaceState(null, null, newFragment);
85
- }
86
-
87
- // Auto-scroll sidebar to keep active item visible
88
- scrollToActiveItem(activeSectionData.link);
89
- }
90
- } else {
91
- currentActive = null;
92
-
93
- if (updatePageState) {
94
- // Clear fragment if no active section
95
- if (window.location.hash) {
96
- history.replaceState(null, null, window.location.pathname);
97
- }
177
+ if (updatePageState) {
178
+ if (activeSectionData) {
179
+ this.updateUrlFragment(activeSectionData.id);
180
+ this.scrollToActiveItem(activeSectionData.link);
181
+ } else {
182
+ this.updateUrlFragment(null);
98
183
  }
99
184
  }
100
185
  }
101
186
  }
102
187
 
103
- function scrollToActiveItem(activeLink) {
104
- const sidebar = activeLink.closest('.sidebar');
188
+ scrollToActiveItem(activeLink) {
189
+ // Use the sidebar nav container we already have
190
+ const sidebar = this.sidebarNav.closest('.sidebar');
105
191
  if (!sidebar) return;
106
192
 
107
193
  const sidebarRect = sidebar.getBoundingClientRect();
@@ -126,50 +212,69 @@
126
212
  }
127
213
  }
128
214
 
129
- // Throttled scroll handler
130
- let scrollTimer = null;
131
-
132
- function onScroll() {
133
- 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;
134
219
 
135
- scrollTimer = setTimeout(() => {
136
- updateActiveLink();
137
- scrollTimer = null;
138
- }, 100);
139
- }
140
-
141
- // Initialize and set up event listeners
142
- window.addEventListener('scroll', onScroll, { passive: true });
143
- window.addEventListener('resize', updateActiveLink, { passive: true });
144
-
145
- // Initial update without changing page state (preserves URL fragments):
146
- updateActiveLink(false);
147
-
148
- // Smooth scroll enhancement for sidebar navigation links
149
- navLinks.forEach(link => {
150
- 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 => {
151
225
  const href = link.getAttribute('href');
152
- // Extract fragment from both "#section" and "page.html#section" formats
153
- const fragmentIndex = href.indexOf('#');
154
- if (fragmentIndex === -1) return;
155
- const fragment = href.substring(fragmentIndex + 1);
226
+ const fragment = SidebarNavigation.extractFragment(href);
156
227
 
157
- // Try to find the target using the fragment
158
- let target = document.getElementById(fragment);
159
-
160
- // If not found, try with CSS.escape for special characters
161
- if (!target && fragment !== CSS.escape(fragment)) {
162
- 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);
163
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);
164
264
 
165
- if (target) {
166
- event.preventDefault();
167
-
168
- // Update URL to trigger :target and let browser handle scrolling
169
- window.location.hash = fragment;
265
+ if (sectionElement && sectionElement.tagName.match(/^H[1-6]$/)) {
266
+ sectionElement = sectionElement.closest('section') || sectionElement.parentElement;
170
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
+ }
171
279
 
172
- link.focus();
173
- });
174
- });
175
- })();
280
+ export { SidebarNavigation };
@@ -244,10 +244,6 @@ div.giscus {
244
244
  padding: 0 1rem;
245
245
  }
246
246
 
247
- pre {
248
- margin: 2rem 1rem;
249
- }
250
-
251
247
  h1 span {
252
248
  display: inline-block;
253
249
  }
data.tar.gz.sig CHANGED
Binary file