docula 0.90.0 → 1.1.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "docula",
3
- "version": "0.90.0",
3
+ "version": "1.1.0",
4
4
  "description": "Beautiful Website for Your Projects",
5
5
  "type": "module",
6
6
  "main": "./dist/docula.js",
@@ -80,7 +80,7 @@
80
80
  "test:e2e": "pnpm build && playwright test",
81
81
  "generate-init-file": "tsx scripts/generate-init-file.ts",
82
82
  "website:build": "node bin/docula.js build -s ./site -o ./site/dist",
83
- "website:serve": "node bin/docula.js serve -s ./site -o ./site/dist -w -p 3333",
83
+ "website:serve": "pnpm build && node bin/docula.js serve -s ./site -o ./site/dist -w -p 3333",
84
84
  "website:mega": "node bin/docula.js serve -s ./test/fixtures/mega-page-site --watch --clean",
85
85
  "website:nohome": "node bin/docula.js serve -s ./test/fixtures/mega-page-site-no-home-page --watch --clean",
86
86
  "website:changelog": "node bin/docula.js serve -s ./test/fixtures/changelog-site --watch --clean"
@@ -3,6 +3,35 @@
3
3
  box-sizing: border-box;
4
4
  }
5
5
 
6
+ /* Mobile: prevent home page from scrolling vertically */
7
+ @media (max-width: 767px) {
8
+ .home {
9
+ height: 100dvh;
10
+ overflow: hidden;
11
+ display: flex;
12
+ flex-direction: column;
13
+ }
14
+
15
+ .home .home-hero {
16
+ flex: 1 1 auto;
17
+ height: auto;
18
+ padding: 1.5rem 0 1rem;
19
+ }
20
+
21
+ .home .home-hero img {
22
+ max-width: 8rem;
23
+ }
24
+
25
+ .home .home-actions {
26
+ padding-bottom: 1rem;
27
+ flex-shrink: 0;
28
+ }
29
+
30
+ .home .home-container {
31
+ display: none;
32
+ }
33
+ }
34
+
6
35
  .hidden {
7
36
  display: none;
8
37
  }
@@ -127,6 +127,32 @@
127
127
  align-items: center;
128
128
  }
129
129
 
130
+ .sidebar-links {
131
+ display: none;
132
+ }
133
+
134
+ @media screen and (min-width: 992px) {
135
+ .sidebar-links {
136
+ display: flex;
137
+ flex-direction: column;
138
+ gap: 0.5rem;
139
+ padding: 1rem 0;
140
+ border-top: 1px solid var(--border);
141
+ margin-top: 1rem;
142
+ }
143
+
144
+ .sidebar-header-link {
145
+ color: var(--sidebar-text);
146
+ text-decoration: none;
147
+ font-size: 0.875rem;
148
+ padding: 0.25rem 0;
149
+ }
150
+
151
+ .sidebar-header-link:hover {
152
+ text-decoration: underline;
153
+ }
154
+ }
155
+
130
156
  .header-logo {
131
157
  flex-shrink: 0;
132
158
  margin-right: 1.5rem;
@@ -5,6 +5,11 @@
5
5
  <a class="header-logo" href="/">
6
6
  <img src="/logo.svg" alt="logo" />
7
7
  </a>
8
+ {{#if headerLinks}}
9
+ {{#each headerLinks}}
10
+ <a class="header-link" href="{{this.url}}" target="_blank" rel="noopener noreferrer">{{#if this.icon}}{{{this.icon}}} {{/if}}{{this.label}}</a>
11
+ {{/each}}
12
+ {{/if}}
8
13
  <button id="open-sidebar" class="icon menu-btn">
9
14
  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path d="M0 96C0 78.3 14.3 64 32 64H416c17.7 0 32 14.3 32 32s-14.3 32-32 32H32C14.3 128 0 113.7 0 96zM0 256c0-17.7 14.3-32 32-32H416c17.7 0 32 14.3 32 32s-14.3 32-32 32H32c-17.7 0-32-14.3-32-32zM448 416c0 17.7-14.3 32-32 32H32c-17.7 0-32-14.3-32-32s14.3-32 32-32H416c17.7 0 32 14.3 32 32z"/></svg>
10
15
  <span>Menu</span>
@@ -36,6 +36,13 @@
36
36
  {{/forEach}}
37
37
  </ul>
38
38
 
39
+ {{#if headerLinks}}
40
+ <div class="sidebar-links">
41
+ {{#each headerLinks}}
42
+ <a class="sidebar-header-link" href="{{this.url}}" target="_blank" rel="noopener noreferrer">{{#if this.icon}}{{{this.icon}}} {{/if}}{{this.label}}</a>
43
+ {{/each}}
44
+ </div>
45
+ {{/if}}
39
46
  </div>
40
47
  </section>
41
48
 
@@ -82,6 +82,43 @@
82
82
  }
83
83
 
84
84
 
85
+ /* Mobile adjustments */
86
+ @media (max-width: 767px) {
87
+ .home-hero {
88
+ min-height: auto;
89
+ padding: 2rem 1rem 1rem;
90
+ }
91
+
92
+ .home-hero img {
93
+ max-width: 8rem;
94
+ }
95
+
96
+ .hero-container {
97
+ margin: 1rem auto;
98
+ }
99
+
100
+ .hero-container h1 {
101
+ font-size: 1.125rem;
102
+ }
103
+
104
+ .home-actions {
105
+ padding-bottom: 1rem;
106
+ }
107
+
108
+ .home-content {
109
+ padding: 0.75rem;
110
+ }
111
+
112
+ .home-content > section {
113
+ padding: 0.75rem 0;
114
+ }
115
+
116
+ .home-content .home-title {
117
+ font-size: 1.25rem;
118
+ margin-bottom: 0.5rem;
119
+ }
120
+ }
121
+
85
122
  /* Responsive hero */
86
123
  @media (min-width: 768px) {
87
124
  .home-hero { min-height: 50vh; }
@@ -137,6 +137,49 @@ body {
137
137
  display: none;
138
138
  }
139
139
 
140
+ .cookie-auth-btn {
141
+ border: 1px solid var(--border);
142
+ border-radius: 4px;
143
+ padding: 4px 10px;
144
+ height: 30px;
145
+ display: flex;
146
+ align-items: center;
147
+ gap: 6px;
148
+ background: transparent;
149
+ color: var(--fg);
150
+ font-size: 13px;
151
+ cursor: pointer;
152
+ text-decoration: none;
153
+ white-space: nowrap;
154
+
155
+ &:hover {
156
+ border-color: var(--border-hover);
157
+ background: var(--surface-hover);
158
+ }
159
+ }
160
+
161
+ .cookie-auth-btn--fixed {
162
+ position: fixed;
163
+ bottom: 16px;
164
+ left: 56px;
165
+ z-index: 50;
166
+ background: var(--surface);
167
+ border: 1px solid var(--border);
168
+ border-radius: 8px;
169
+ padding: 6px 12px;
170
+ height: auto;
171
+ }
172
+
173
+ .cookie-auth-btn--mobile {
174
+ width: 100%;
175
+ background: transparent;
176
+ border: none;
177
+ cursor: pointer;
178
+ text-align: left;
179
+ font-size: inherit;
180
+ color: inherit;
181
+ }
182
+
140
183
  .mobile-menu-toggle {
141
184
  display: none;
142
185
  align-items: center;
@@ -512,6 +555,7 @@ body {
512
555
  margin: 0 auto;
513
556
  font-size: 15px;
514
557
  line-height: 1.5;
558
+ overflow-wrap: break-word;
515
559
  }
516
560
 
517
561
  .home-content > section {
@@ -654,6 +698,7 @@ body {
654
698
  margin-top: 12px;
655
699
  font-size: 15px;
656
700
  line-height: 1.6;
701
+ overflow-wrap: break-word;
657
702
  }
658
703
 
659
704
  .changelog-entry-nav {
@@ -44,6 +44,17 @@
44
44
  </main>
45
45
  {{/if}}
46
46
 
47
+ {{#if cookieAuth}}
48
+ <a href="{{cookieAuth.loginUrl}}" class="cookie-auth-btn cookie-auth-btn--fixed" id="cookie-auth-login">
49
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 3h4a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-4"/><polyline points="10 17 15 12 10 7"/><line x1="15" x2="3" y1="12" y2="12"/></svg>
50
+ <span>Log In</span>
51
+ </a>
52
+ <button class="cookie-auth-btn cookie-auth-btn--fixed" id="cookie-auth-logout" style="display:none">
53
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" x2="9" y1="12" y2="12"/></svg>
54
+ <span>Log Out</span>
55
+ </button>
56
+ {{/if}}
57
+
47
58
  {{> theme-toggle fixed=true }}
48
59
 
49
60
  {{> footer }}
@@ -26,7 +26,29 @@
26
26
  <span>Changelog</span>
27
27
  </a>
28
28
  {{/if}}
29
+ {{#if headerLinks}}
30
+ {{#each headerLinks}}
31
+ <a class="header-bottom__item" href="{{this.url}}" target="_blank" rel="noopener noreferrer">
32
+ {{#if this.icon}}
33
+ {{{this.icon}}}
34
+ {{else}}
35
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 3h6v6"/><path d="M10 14 21 3"/><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/></svg>
36
+ {{/if}}
37
+ <span>{{this.label}}</span>
38
+ </a>
39
+ {{/each}}
40
+ {{/if}}
29
41
  </nav>
42
+ {{#if cookieAuth}}
43
+ <a href="{{cookieAuth.loginUrl}}" class="cookie-auth-btn" id="cookie-auth-login">
44
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 3h4a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-4"/><polyline points="10 17 15 12 10 7"/><line x1="15" x2="3" y1="12" y2="12"/></svg>
45
+ <span>Log In</span>
46
+ </a>
47
+ <button class="cookie-auth-btn" id="cookie-auth-logout" style="display:none">
48
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" x2="9" y1="12" y2="12"/></svg>
49
+ <span>Log Out</span>
50
+ </button>
51
+ {{/if}}
30
52
  {{> theme-toggle }}
31
53
  </div>
32
54
  </div>
@@ -51,6 +73,28 @@
51
73
  <span>Changelog</span>
52
74
  </a>
53
75
  {{/if}}
76
+ {{#if headerLinks}}
77
+ {{#each headerLinks}}
78
+ <a class="mobile-nav__item" href="{{this.url}}" target="_blank" rel="noopener noreferrer">
79
+ {{#if this.icon}}
80
+ {{{this.icon}}}
81
+ {{else}}
82
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 3h6v6"/><path d="M10 14 21 3"/><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/></svg>
83
+ {{/if}}
84
+ <span>{{this.label}}</span>
85
+ </a>
86
+ {{/each}}
87
+ {{/if}}
88
+ {{#if cookieAuth}}
89
+ <a class="mobile-nav__item" href="{{cookieAuth.loginUrl}}" id="cookie-auth-login-mobile">
90
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 3h4a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-4"/><polyline points="10 17 15 12 10 7"/><line x1="15" x2="3" y1="12" y2="12"/></svg>
91
+ <span>Log In</span>
92
+ </a>
93
+ <button class="mobile-nav__item cookie-auth-btn--mobile" id="cookie-auth-logout-mobile" style="display:none">
94
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" x2="9" y1="12" y2="12"/></svg>
95
+ <span>Log Out</span>
96
+ </button>
97
+ {{/if}}
54
98
  </nav>
55
99
  <div class="mobile-sidebar__docs" id="mobile-sidebar-docs"></div>
56
100
  </aside>
@@ -88,6 +88,61 @@
88
88
  });
89
89
  })();
90
90
  </script>
91
+ {{#if cookieAuth}}
92
+ <div id="cookie-auth-config" hidden data-cookie-name="{{#if cookieAuth.cookieName}}{{cookieAuth.cookieName}}{{else}}token{{/if}}"{{#if cookieAuth.logoutUrl}} data-logout-url="{{cookieAuth.logoutUrl}}"{{/if}}></div>
93
+ <script>
94
+ (function() {
95
+ var configEl = document.getElementById('cookie-auth-config');
96
+ if (!configEl) return;
97
+ var cookieName = configEl.getAttribute('data-cookie-name');
98
+ var logoutUrl = configEl.getAttribute('data-logout-url');
99
+ function isSafeUrl(url) {
100
+ if (!url) return false;
101
+ try {
102
+ var parsed = new URL(url, window.location.origin);
103
+ return parsed.origin === window.location.origin;
104
+ } catch (e) {
105
+ return false;
106
+ }
107
+ }
108
+ function hasCookie() {
109
+ return document.cookie.split(';').some(function(c) {
110
+ return c.trim().startsWith(cookieName + '=');
111
+ });
112
+ }
113
+ function updateAuthUI() {
114
+ var loggedIn = hasCookie();
115
+ var els = [
116
+ { login: document.getElementById('cookie-auth-login'), logout: document.getElementById('cookie-auth-logout') },
117
+ { login: document.getElementById('cookie-auth-login-mobile'), logout: document.getElementById('cookie-auth-logout-mobile') }
118
+ ];
119
+ els.forEach(function(pair) {
120
+ if (pair.login) pair.login.style.display = loggedIn ? 'none' : '';
121
+ if (pair.logout) pair.logout.style.display = loggedIn ? '' : 'none';
122
+ });
123
+ }
124
+ document.addEventListener('DOMContentLoaded', function() {
125
+ updateAuthUI();
126
+ var logoutEls = [
127
+ document.getElementById('cookie-auth-logout'),
128
+ document.getElementById('cookie-auth-logout-mobile')
129
+ ];
130
+ logoutEls.forEach(function(el) {
131
+ if (el) {
132
+ el.addEventListener('click', function() {
133
+ if (isSafeUrl(logoutUrl)) {
134
+ window.location.href = logoutUrl;
135
+ } else {
136
+ document.cookie = cookieName + '=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/';
137
+ window.location.reload();
138
+ }
139
+ });
140
+ }
141
+ });
142
+ });
143
+ })();
144
+ </script>
145
+ {{/if}}
91
146
  <script defer>
92
147
  document.addEventListener('DOMContentLoaded', () => {
93
148
  const menuToggle = document.getElementById('mobile-menu-toggle');