arsxy-theme 1.0.0.pre.rc

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 (56) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.md +166 -0
  4. data/_config.yml +131 -0
  5. data/_includes/footer.html +45 -0
  6. data/_includes/header.html +68 -0
  7. data/_includes/image.html +8 -0
  8. data/_includes/related-posts.html +83 -0
  9. data/_includes/search.html +32 -0
  10. data/_includes/social-sharing.html +86 -0
  11. data/_includes/toc.html +83 -0
  12. data/_layouts/default.html +76 -0
  13. data/_layouts/docs.html +90 -0
  14. data/_layouts/home.html +172 -0
  15. data/_layouts/post.html +114 -0
  16. data/_sass/_base.scss +264 -0
  17. data/_sass/_dark-mode.scss +749 -0
  18. data/_sass/_layout.scss +118 -0
  19. data/_sass/_responsive.scss +157 -0
  20. data/_sass/_syntax-highlighting.scss +147 -0
  21. data/_sass/_typography.scss +226 -0
  22. data/_sass/_utilities.scss +138 -0
  23. data/_sass/_variables.scss +63 -0
  24. data/_sass/components/_code.scss +245 -0
  25. data/_sass/components/_docs.scss +263 -0
  26. data/_sass/components/_footer.scss +170 -0
  27. data/_sass/components/_header.scss +313 -0
  28. data/_sass/components/_homepage.scss +597 -0
  29. data/_sass/components/_image.scss +22 -0
  30. data/_sass/components/_pagination.scss +146 -0
  31. data/_sass/components/_post.scss +1039 -0
  32. data/_sass/components/_related-posts.scss +70 -0
  33. data/_sass/components/_search.scss +276 -0
  34. data/_sass/components/_social-sharing.scss +95 -0
  35. data/_sass/components/_toc.scss +193 -0
  36. data/assets/css/main.scss +24 -0
  37. data/assets/images/favicon/android-chrome-192x192.png +0 -0
  38. data/assets/images/favicon/android-chrome-512x512.png +0 -0
  39. data/assets/images/favicon/apple-touch-icon.png +0 -0
  40. data/assets/images/favicon/favicon-16x16.png +0 -0
  41. data/assets/images/favicon/favicon-32x32.png +0 -0
  42. data/assets/images/favicon/favicon.ico +0 -0
  43. data/assets/images/favicon/site.webmanifest +19 -0
  44. data/assets/images/new-features-update.svg +58 -0
  45. data/assets/images/related-posts-example.svg +37 -0
  46. data/assets/images/seo-optimization.png +0 -0
  47. data/assets/images/social-sharing-sample.png +0 -0
  48. data/assets/images/ss-arsxy-theme.png +0 -0
  49. data/assets/images/table-of-content-thumbnail.png +0 -0
  50. data/assets/images/theme-demo-2.jpeg +0 -0
  51. data/assets/images/theme-demo.jpeg +0 -0
  52. data/assets/images/welcome-to-arsxy-theme.png +0 -0
  53. data/assets/js/main.js +369 -0
  54. data/assets/js/search-index.json +31 -0
  55. data/assets/js/search.js +207 -0
  56. metadata +226 -0
@@ -0,0 +1,58 @@
1
+ <!--
2
+ This is an SVG placeholder for the new features update image.
3
+ It would be better to replace this with a real screenshot or custom image.
4
+ -->
5
+ <svg width="1200" height="630" xmlns="http://www.w3.org/2000/svg">
6
+ <style>
7
+ .title { fill: #333; font-family: 'Arial', sans-serif; font-size: 60px; font-weight: bold; }
8
+ .subtitle { fill: #666; font-family: 'Arial', sans-serif; font-size: 36px; }
9
+ .feature { fill: #444; font-family: 'Arial', sans-serif; font-size: 32px; font-weight: bold; }
10
+ .icon { stroke: #0066cc; stroke-width: 3; fill: none; }
11
+ .bg-rect { fill: #f8f9fa; stroke: #ddd; stroke-width: 2; }
12
+ </style>
13
+
14
+ <rect width="100%" height="100%" fill="#ffffff"/>
15
+
16
+ <!-- Header background -->
17
+ <rect width="100%" height="150" fill="#0066cc"/>
18
+
19
+ <!-- Title -->
20
+ <text x="50" y="100" class="title" fill="#ffffff">Arsxy Theme - New Features</text>
21
+
22
+ <!-- Feature sections -->
23
+ <rect x="50" y="200" width="340" height="380" rx="10" class="bg-rect"/>
24
+ <rect x="430" y="200" width="340" height="380" rx="10" class="bg-rect"/>
25
+ <rect x="810" y="200" width="340" height="380" rx="10" class="bg-rect"/>
26
+
27
+ <!-- Feature titles -->
28
+ <text x="170" y="250" text-anchor="middle" class="feature">Related Posts</text>
29
+ <text x="600" y="250" text-anchor="middle" class="feature">Social Sharing</text>
30
+ <text x="980" y="250" text-anchor="middle" class="feature">Search</text>
31
+
32
+ <!-- Feature icons -->
33
+ <!-- Related posts icon -->
34
+ <g transform="translate(170, 320)">
35
+ <rect x="-60" y="-35" width="120" height="70" rx="5" class="icon"/>
36
+ <rect x="-60" y="50" width="120" height="70" rx="5" class="icon"/>
37
+ <rect x="-60" y="135" width="120" height="70" rx="5" class="icon"/>
38
+ <line x1="-40" y1="0" x2="40" y2="0" class="icon"/>
39
+ <line x1="-40" y1="85" x2="40" y2="85" class="icon"/>
40
+ <line x1="-40" y1="170" x2="40" y2="170" class="icon"/>
41
+ </g>
42
+
43
+ <!-- Social sharing icon -->
44
+ <g transform="translate(600, 320)">
45
+ <circle cx="0" cy="0" r="50" class="icon"/>
46
+ <path d="M -25,15 L 0,-15 L 25,15" class="icon"/>
47
+ <path d="M 0,-15 L 0,50" class="icon"/>
48
+ </g>
49
+
50
+ <!-- Search icon -->
51
+ <g transform="translate(980, 320)">
52
+ <circle cx="-15" cy="-15" r="40" class="icon"/>
53
+ <line x1="15" y1="15" x2="45" y2="45" stroke-width="6" class="icon"/>
54
+ </g>
55
+
56
+ <!-- Bottom tagline -->
57
+ <text x="600" y="600" text-anchor="middle" class="subtitle">Enhancing user experience and content discovery</text>
58
+ </svg>
@@ -0,0 +1,37 @@
1
+ <!--
2
+ This is an SVG placeholder showing related posts example.
3
+ In a production environment, replace with actual screenshots.
4
+ -->
5
+ <svg width="800" height="400" xmlns="http://www.w3.org/2000/svg">
6
+ <style>
7
+ .title { fill: #333; font-family: 'Arial', sans-serif; font-size: 24px; font-weight: bold; }
8
+ .post-title { fill: #0066cc; font-family: 'Arial', sans-serif; font-size: 14px; font-weight: bold; }
9
+ .date { fill: #666; font-family: 'Arial', sans-serif; font-size: 12px; }
10
+ .post-card { fill: #ffffff; stroke: #ddd; stroke-width: 1; }
11
+ .post-img { fill: #f0f0f0; stroke: #ddd; stroke-width: 1; }
12
+ .section-title { fill: #333; font-family: 'Arial', sans-serif; font-size: 28px; font-weight: bold; }
13
+ </style>
14
+
15
+ <rect width="100%" height="100%" fill="#f9f9f9"/>
16
+
17
+ <!-- Section Title -->
18
+ <text x="30" y="50" class="section-title">Related Articles</text>
19
+
20
+ <!-- Post 1 -->
21
+ <rect x="30" y="80" width="230" height="280" rx="5" class="post-card"/>
22
+ <rect x="45" y="100" width="200" height="120" rx="3" class="post-img"/>
23
+ <text x="45" y="250" class="post-title">Customizing Your Theme</text>
24
+ <text x="45" y="270" class="date">May 13, 2025</text>
25
+
26
+ <!-- Post 2 -->
27
+ <rect x="285" y="80" width="230" height="280" rx="5" class="post-card"/>
28
+ <rect x="300" y="100" width="200" height="120" rx="3" class="post-img"/>
29
+ <text x="300" y="250" class="post-title">Dark Mode Implementation</text>
30
+ <text x="300" y="270" class="date">May 14, 2025</text>
31
+
32
+ <!-- Post 3 -->
33
+ <rect x="540" y="80" width="230" height="280" rx="5" class="post-card"/>
34
+ <rect x="555" y="100" width="200" height="120" rx="3" class="post-img"/>
35
+ <text x="555" y="250" class="post-title">SEO Optimization Tips</text>
36
+ <text x="555" y="270" class="date">May 13, 2025</text>
37
+ </svg>
Binary file
Binary file
Binary file
Binary file
data/assets/js/main.js ADDED
@@ -0,0 +1,369 @@
1
+ /**
2
+ * Main JavaScript file for the Arsxy Theme
3
+ */
4
+ document.addEventListener('DOMContentLoaded', function() {
5
+ // Initialize dark mode
6
+ initDarkMode();
7
+
8
+ // Initialize table of contents
9
+ initTableOfContents();
10
+
11
+ // Initialize code copy buttons
12
+ initCodeCopy();
13
+
14
+ // Initialize mobile navigation
15
+ initMobileNav();
16
+ });
17
+
18
+ /**
19
+ * Dark Mode Functionality
20
+ */
21
+ function initDarkMode() {
22
+ const themeToggle = document.querySelector('.theme-toggle');
23
+ if (!themeToggle) return;
24
+
25
+ // Get configuration from data attributes
26
+ const html = document.documentElement;
27
+ const darkModeEnabled = html.getAttribute('data-dark-mode-enabled') === 'true';
28
+
29
+ // If dark mode is completely disabled, ensure light mode is always applied
30
+ if (!darkModeEnabled) {
31
+ html.classList.remove('dark-mode');
32
+ localStorage.setItem('theme', 'light');
33
+ showLightModeIcon(themeToggle);
34
+ } else {
35
+ // Add dark-active class to the toggle button if currently in dark mode
36
+ if (html.classList.contains('dark-mode')) {
37
+ themeToggle.classList.add('dark-active');
38
+ showDarkModeIcon(themeToggle);
39
+ } else {
40
+ themeToggle.classList.remove('dark-active');
41
+ showLightModeIcon(themeToggle);
42
+ }
43
+ }
44
+
45
+ themeToggle.addEventListener('click', function() {
46
+ // If dark mode is completely disabled, don't allow toggling to dark mode
47
+ if (!darkModeEnabled) {
48
+ html.classList.remove('dark-mode');
49
+ localStorage.setItem('theme', 'light');
50
+ showLightModeIcon(themeToggle);
51
+ return;
52
+ }
53
+
54
+ if (html.classList.contains('dark-mode')) {
55
+ html.classList.remove('dark-mode');
56
+ themeToggle.classList.remove('dark-active');
57
+ localStorage.setItem('theme', 'light');
58
+ showLightModeIcon(themeToggle);
59
+ } else {
60
+ html.classList.add('dark-mode');
61
+ themeToggle.classList.add('dark-active');
62
+ localStorage.setItem('theme', 'dark');
63
+ showDarkModeIcon(themeToggle);
64
+ }
65
+ });
66
+
67
+ // Helper functions to toggle icons
68
+ function showDarkModeIcon(toggle) {
69
+ const sunIcon = toggle.querySelector('.sun');
70
+ const moonIcon = toggle.querySelector('.moon');
71
+ if (sunIcon) sunIcon.style.display = 'block';
72
+ if (moonIcon) moonIcon.style.display = 'none';
73
+ }
74
+
75
+ function showLightModeIcon(toggle) {
76
+ const sunIcon = toggle.querySelector('.sun');
77
+ const moonIcon = toggle.querySelector('.moon');
78
+ if (sunIcon) sunIcon.style.display = 'none';
79
+ if (moonIcon) moonIcon.style.display = 'block';
80
+ }
81
+ }
82
+
83
+ /**
84
+ * Table of Contents Functionality
85
+ */
86
+ function initTableOfContents() {
87
+ // Handle static TOC for mobile
88
+ const staticTocContainer = document.querySelector('.table-of-contents');
89
+ if (staticTocContainer) {
90
+ // Make TOC collapsible on mobile
91
+ if (window.innerWidth < 600) { // $on-palm value
92
+ const tocHeading = staticTocContainer.querySelector('h2');
93
+ const tocContent = document.createElement('div');
94
+ tocContent.className = 'toc-content';
95
+
96
+ // Move all elements after the heading into the content div
97
+ let nextSibling = tocHeading.nextElementSibling;
98
+ while (nextSibling) {
99
+ const currentSibling = nextSibling;
100
+ nextSibling = nextSibling.nextElementSibling;
101
+ tocContent.appendChild(currentSibling);
102
+ }
103
+
104
+ staticTocContainer.appendChild(tocContent);
105
+
106
+ // Add click event to toggle
107
+ tocHeading.addEventListener('click', function() {
108
+ tocHeading.classList.toggle('active');
109
+ tocContent.classList.toggle('expanded');
110
+ });
111
+ }
112
+ }
113
+
114
+ // Handle all TOC containers (floating TOC, docs sidebar, and static TOC)
115
+ const floatingToc = document.querySelector('.floating-toc');
116
+ const docsSidebar = document.querySelector('.docs-sidebar .docs-nav');
117
+ const tocContainers = [floatingToc, staticTocContainer, docsSidebar].filter(Boolean);
118
+
119
+ if (tocContainers.length) {
120
+ // Add scroll spy functionality for all content areas
121
+ const headings = document.querySelectorAll('.post-content h2, .post-content h3, .post-content h4, .docs-body h2, .docs-body h3, .docs-body h4, .docs-content h2, .docs-content h3, .docs-content h4');
122
+
123
+ // Get all possible TOC links across all containers
124
+ const tocLinks = document.querySelectorAll('.floating-toc .toc a, .table-of-contents .toc a, .docs-sidebar .docs-nav a');
125
+
126
+ if (headings.length && tocLinks.length) {
127
+ // Add debug logs
128
+ console.log(`TOC Initialization: Found ${headings.length} headings and ${tocLinks.length} TOC links`);
129
+ console.log('TOC Containers:', tocContainers);
130
+
131
+ // Set up Intersection Observer for headings
132
+ const headingsObserver = new IntersectionObserver((entries) => {
133
+ entries.forEach(entry => {
134
+ // Store entry information in a data attribute for later use
135
+ if (entry.isIntersecting) {
136
+ entry.target.dataset.visible = true;
137
+ } else {
138
+ delete entry.target.dataset.visible;
139
+ }
140
+
141
+ // Find the heading that's currently most visible
142
+ const visibleHeadings = Array.from(headings).filter(h => h.dataset.visible);
143
+
144
+ if (visibleHeadings.length > 0) {
145
+ // Sort visible headings by their position from the top
146
+ const sortedHeadings = visibleHeadings.sort((a, b) => {
147
+ return a.getBoundingClientRect().top - b.getBoundingClientRect().top;
148
+ });
149
+
150
+ // Use the first visible heading (closest to the top)
151
+ const activeHeading = sortedHeadings[0];
152
+ if (activeHeading && activeHeading.id) {
153
+ updateActiveTocLink(activeHeading.id);
154
+ }
155
+ }
156
+ });
157
+ }, {
158
+ rootMargin: '-100px 0px -80% 0px', // Adjust based on when you want to trigger the change
159
+ threshold: 0
160
+ });
161
+
162
+ // Observe all headings
163
+ headings.forEach(heading => {
164
+ if (heading.id) {
165
+ headingsObserver.observe(heading);
166
+ } else {
167
+ console.warn('Found heading without ID:', heading.textContent.trim());
168
+ }
169
+ });
170
+
171
+ // Add click event to smooth scroll to sections
172
+ tocLinks.forEach(link => {
173
+ link.addEventListener('click', function(e) {
174
+ const href = this.getAttribute('href');
175
+
176
+ // Only handle links that refer to page anchors
177
+ if (href && href.startsWith('#')) {
178
+ e.preventDefault();
179
+
180
+ const targetId = href.substring(1);
181
+ const targetElement = document.getElementById(targetId);
182
+
183
+ if (targetElement) {
184
+ // Calculate proper scroll position with offset
185
+ const headerOffset = 100; // Adjust this value based on your header height and desired spacing
186
+ const elementPosition = targetElement.getBoundingClientRect().top;
187
+ const offsetPosition = elementPosition + window.pageYOffset - headerOffset;
188
+
189
+ // Smooth scroll to target with proper offset
190
+ window.scrollTo({
191
+ top: offsetPosition - 50,
192
+ behavior: 'smooth'
193
+ });
194
+
195
+ // Update URL hash without jumping
196
+ history.pushState(null, null, `#${targetId}`);
197
+
198
+ // Update active TOC link
199
+ updateActiveTocLink(targetId);
200
+ }
201
+ }
202
+ });
203
+ });
204
+ }
205
+ }
206
+
207
+ // Function to update active TOC link and auto-scroll the TOC
208
+ function updateActiveTocLink(id) {
209
+ // Process all TOC containers
210
+ tocContainers.forEach(container => {
211
+ if (!container) return;
212
+
213
+ let activeLink;
214
+
215
+ // Handle docs sidebar differently from other TOCs
216
+ if (container.classList.contains('docs-nav')) {
217
+ // For docs sidebar, we need to handle the specific structure
218
+ const allLinks = container.querySelectorAll('.docs-nav-list a');
219
+
220
+ // Remove active class from all links in this container
221
+ allLinks.forEach(link => {
222
+ link.classList.remove('active');
223
+ });
224
+
225
+ // Find the matching link in the docs sidebar
226
+ // First, try direct href match
227
+ activeLink = container.querySelector(`a[href="${window.location.pathname}#${id}"], a[href="#${id}"]`);
228
+
229
+ // If no direct match, try partial match based on section name
230
+ if (!activeLink) {
231
+ // Sometimes the ID might be a transformed version of the text
232
+ const idParts = id.split('-');
233
+ for (const link of allLinks) {
234
+ const linkText = link.textContent.trim().toLowerCase();
235
+ // Check if the link text contains parts of the ID
236
+ if (idParts.some(part => linkText.includes(part.toLowerCase()))) {
237
+ activeLink = link;
238
+ break;
239
+ }
240
+ }
241
+ }
242
+ } else {
243
+ // Standard TOC (floating or static)
244
+ const allLinks = container.querySelectorAll('.toc a');
245
+
246
+ // Remove active class from all TOC links in this container
247
+ allLinks.forEach(link => {
248
+ link.classList.remove('active');
249
+ });
250
+
251
+ // Find the active link by href
252
+ activeLink = container.querySelector(`.toc a[href="#${id}"]`);
253
+ }
254
+
255
+ if (activeLink) {
256
+ // Add active class to current link
257
+ activeLink.classList.add('active');
258
+ console.log('Activated link:', activeLink.textContent, 'in container:', container.className);
259
+
260
+ // Find the scrollable container for this TOC
261
+ let tocElement;
262
+ if (container.classList.contains('docs-nav')) {
263
+ tocElement = container.closest('.docs-sidebar');
264
+ } else {
265
+ tocElement = container.closest('.table-of-contents, .floating-toc');
266
+ }
267
+
268
+ // Auto-scroll the TOC to show the active link
269
+ if (tocElement && tocElement.scrollHeight > tocElement.clientHeight) {
270
+ console.log('Autoscrolling container:', tocElement.className);
271
+
272
+ // Calculate the position of the active link relative to the TOC container
273
+ const activeLinkRect = activeLink.getBoundingClientRect();
274
+ const tocRect = tocElement.getBoundingClientRect();
275
+
276
+ // Check if the active link is outside the visible area of the TOC
277
+ const isLinkAbove = activeLinkRect.top < tocRect.top + 40; // Add padding for better visibility
278
+ const isLinkBelow = activeLinkRect.bottom > tocRect.bottom - 40; // Add padding for better visibility
279
+
280
+ if (isLinkAbove || isLinkBelow) {
281
+ console.log('Link is outside visible area, scrolling TOC');
282
+
283
+ // Get the scroll position of the link relative to the TOC
284
+ // This is more reliable than offsetTop in some cases
285
+ const linkTop = activeLink.getBoundingClientRect().top - tocElement.getBoundingClientRect().top + tocElement.scrollTop;
286
+
287
+ // Calculate a position that centers the active link in the TOC as much as possible
288
+ const scrollPosition = linkTop - (tocElement.clientHeight / 2) + (activeLink.clientHeight / 2);
289
+
290
+ // Scroll the TOC smoothly to show the active link
291
+ tocElement.scrollTo({
292
+ top: Math.max(0, scrollPosition),
293
+ behavior: 'smooth'
294
+ });
295
+ }
296
+ }
297
+ }
298
+ });
299
+ }
300
+ }
301
+
302
+ /**
303
+ * Code Copy Button Functionality
304
+ */
305
+ function initCodeCopy() {
306
+ const codeBlocks = document.querySelectorAll('pre');
307
+
308
+ codeBlocks.forEach(block => {
309
+ // Create copy button
310
+ const copyButton = document.createElement('button');
311
+ copyButton.className = 'copy-code-button';
312
+ copyButton.textContent = 'Copy';
313
+
314
+ // Add button to pre element's parent (usually div.highlight)
315
+ const container = block.parentNode;
316
+ container.style.position = 'relative';
317
+ container.insertBefore(copyButton, container.firstChild);
318
+
319
+ // Add click event
320
+ copyButton.addEventListener('click', function() {
321
+ const code = block.textContent;
322
+
323
+ navigator.clipboard.writeText(code).then(() => {
324
+ // Visual feedback
325
+ copyButton.textContent = 'Copied!';
326
+ setTimeout(() => {
327
+ copyButton.textContent = 'Copy';
328
+ }, 2000);
329
+ }).catch(err => {
330
+ console.error('Could not copy text: ', err);
331
+ copyButton.textContent = 'Failed';
332
+ setTimeout(() => {
333
+ copyButton.textContent = 'Copy';
334
+ }, 2000);
335
+ });
336
+ });
337
+ });
338
+ }
339
+
340
+ /**
341
+ * Mobile Navigation Functionality
342
+ */
343
+ function initMobileNav() {
344
+ const navTrigger = document.querySelector('.nav-trigger');
345
+ if (!navTrigger) return;
346
+
347
+ // Close menu when clicking outside
348
+ document.addEventListener('click', function(event) {
349
+ const isNavOpen = navTrigger.checked;
350
+ const isClickInsideNav = event.target.closest('.site-nav');
351
+
352
+ if (isNavOpen && !isClickInsideNav) {
353
+ navTrigger.checked = false;
354
+ }
355
+ });
356
+ }
357
+
358
+ /**
359
+ * Utility: Debounce function for scroll events
360
+ */
361
+ function debounce(func, wait) {
362
+ let timeout;
363
+ return function() {
364
+ const context = this;
365
+ const args = arguments;
366
+ clearTimeout(timeout);
367
+ timeout = setTimeout(() => func.apply(context, args), wait);
368
+ };
369
+ }
@@ -0,0 +1,31 @@
1
+ ---
2
+ layout: null
3
+ ---
4
+ [
5
+ {% for post in site.posts %}
6
+ {
7
+ "title": {{ post.title | jsonify }},
8
+ "url": {{ post.url | relative_url | jsonify }},
9
+ "date": {{ post.date | date: site.date_format | jsonify }},
10
+ "content": {{ post.content | strip_html | strip_newlines | jsonify }},
11
+ "excerpt": {{ post.excerpt | strip_html | strip_newlines | truncate: 160 | jsonify }},
12
+ {% if post.description %}
13
+ "description": {{ post.description | jsonify }},
14
+ {% endif %}
15
+ {% if post.categories %}
16
+ "categories": {{ post.categories | jsonify }},
17
+ {% endif %}
18
+ {% if post.tags %}
19
+ "tags": {{ post.tags | jsonify }},
20
+ {% endif %}
21
+ {% if post.author %}
22
+ "author": {{ post.author | jsonify }},
23
+ {% endif %}
24
+ {% if post.featured_image %}
25
+ "featured_image": {{ post.featured_image | relative_url | jsonify }}
26
+ {% else %}
27
+ "featured_image": ""
28
+ {% endif %}
29
+ }{% unless forloop.last %},{% endunless %}
30
+ {% endfor %}
31
+ ]