docula 1.2.0 → 1.6.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.
@@ -4,24 +4,24 @@
4
4
  <button class="mobile-menu-toggle" id="mobile-menu-toggle" aria-label="Toggle navigation menu">
5
5
  <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="4" x2="20" y1="12" y2="12"/><line x1="4" x2="20" y1="6" y2="6"/><line x1="4" x2="20" y1="18" y2="18"/></svg>
6
6
  </button>
7
- <a href="/">
8
- <img alt="{{siteTitle}}" class="logo__img" src="/logo.svg">
7
+ <a href="{{baseUrl}}/">
8
+ <img alt="{{siteTitle}}" class="logo__img" src="{{baseUrl}}/logo.svg">
9
9
  </a>
10
10
  <nav class="header-bottom__nav">
11
11
  {{#if hasDocuments}}
12
- <a class="header-bottom__item" href="/docs/" id="nav-docs">
12
+ <a class="header-bottom__item" href="{{docsUrl}}/" id="nav-docs">
13
13
  <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="M12 7v14"/><path d="M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3 3 3 0 0 0-3-3z"/></svg>
14
14
  <span>Documentation</span>
15
15
  </a>
16
16
  {{/if}}
17
17
  {{#if openApiUrl}}
18
- <a class="header-bottom__item" href="/api" id="nav-api">
18
+ <a class="header-bottom__item" href="{{apiUrl}}" id="nav-api">
19
19
  <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="m16 18 6-6-6-6"/><path d="m8 6-6 6 6 6"/></svg>
20
20
  <span>API Reference</span>
21
21
  </a>
22
22
  {{/if}}
23
23
  {{#if hasChangelog}}
24
- <a class="header-bottom__item" href="/changelog" id="nav-changelog">
24
+ <a class="header-bottom__item" href="{{changelogUrl}}" id="nav-changelog">
25
25
  <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="M11 6a13 13 0 0 0 8.4-2.8A1 1 0 0 1 21 4v12a1 1 0 0 1-1.6.8A13 13 0 0 0 11 14H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2z"/><path d="M6 14a12 12 0 0 0 2.4 7.2 2 2 0 0 0 3.2-2.4A8 8 0 0 1 10 14"/><path d="M8 6v8"/></svg>
26
26
  <span>Changelog</span>
27
27
  </a>
@@ -39,37 +39,48 @@
39
39
  {{/each}}
40
40
  {{/if}}
41
41
  </nav>
42
- {{#if cookieAuth.loginUrl}}
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
- <span class="cookie-auth-user" id="cookie-auth-user" style="display:none"></span>
48
- <button class="cookie-auth-btn" id="cookie-auth-logout" style="display:none">
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="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>
50
- <span>Log Out</span>
51
- </button>
52
- {{/if}}
53
- {{> theme-toggle }}
42
+ <div class="header-actions">
43
+ {{#if cookieAuth.loginUrl}}
44
+ <div class="cookie-auth">
45
+ <a href="{{cookieAuth.loginUrl}}" class="cookie-auth-btn" id="cookie-auth-login">
46
+ <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>
47
+ <span>Log In</span>
48
+ </a>
49
+ <span class="cookie-auth-user" id="cookie-auth-user"></span>
50
+ <script>
51
+ var auth = window.__doculaAuth;
52
+ if (auth && auth.displayName) {
53
+ var userEl = document.getElementById('cookie-auth-user');
54
+ if (userEl) userEl.textContent = auth.displayName;
55
+ }
56
+ </script>
57
+ <button class="cookie-auth-btn cookie-auth-logout" id="cookie-auth-logout">
58
+ <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>
59
+ <span>Log Out</span>
60
+ </button>
61
+ </div>
62
+ {{/if}}
63
+ {{> theme-toggle }}
64
+ </div>
54
65
  </div>
55
66
  </div>
56
67
  </header>
57
68
  <aside class="mobile-sidebar" id="mobile-sidebar">
58
69
  <nav class="mobile-nav">
59
70
  {{#if hasDocuments}}
60
- <a class="mobile-nav__item" href="/docs/">
71
+ <a class="mobile-nav__item" href="{{docsUrl}}/">
61
72
  <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="M12 7v14"/><path d="M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3 3 3 0 0 0-3-3z"/></svg>
62
73
  <span>Documentation</span>
63
74
  </a>
64
75
  {{/if}}
65
76
  {{#if openApiUrl}}
66
- <a class="mobile-nav__item" href="/api">
77
+ <a class="mobile-nav__item" href="{{apiUrl}}">
67
78
  <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="m16 18 6-6-6-6"/><path d="m8 6-6 6 6 6"/></svg>
68
79
  <span>API Reference</span>
69
80
  </a>
70
81
  {{/if}}
71
82
  {{#if hasChangelog}}
72
- <a class="mobile-nav__item" href="/changelog">
83
+ <a class="mobile-nav__item" href="{{changelogUrl}}">
73
84
  <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="M11 6a13 13 0 0 0 8.4-2.8A1 1 0 0 1 21 4v12a1 1 0 0 1-1.6.8A13 13 0 0 0 11 14H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2z"/><path d="M6 14a12 12 0 0 0 2.4 7.2 2 2 0 0 0 3.2-2.4A8 8 0 0 1 10 14"/><path d="M8 6v8"/></svg>
74
85
  <span>Changelog</span>
75
86
  </a>
@@ -91,8 +102,15 @@
91
102
  <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>
92
103
  <span>Log In</span>
93
104
  </a>
94
- <span class="cookie-auth-user" id="cookie-auth-user-mobile" style="display:none"></span>
95
- <button class="mobile-nav__item cookie-auth-btn--mobile" id="cookie-auth-logout-mobile" style="display:none">
105
+ <span class="cookie-auth-user" id="cookie-auth-user-mobile"></span>
106
+ <script>
107
+ var auth = window.__doculaAuth;
108
+ if (auth && auth.displayName) {
109
+ var userEl = document.getElementById('cookie-auth-user-mobile');
110
+ if (userEl) userEl.textContent = auth.displayName;
111
+ }
112
+ </script>
113
+ <button class="mobile-nav__item cookie-auth-btn--mobile" id="cookie-auth-logout-mobile">
96
114
  <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>
97
115
  <span>Log Out</span>
98
116
  </button>
@@ -1,9 +1,10 @@
1
1
  <meta charset="UTF-8">
2
2
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
3
3
  <meta name="description" content="{{siteDescription}}">
4
- <link rel="stylesheet" href="/css/styles.css">
5
- <link rel="stylesheet" href="/css/highlight/styles/base16/docula.css">
6
- <link rel="icon" href="/favicon.ico">
4
+ <link rel="stylesheet" href="{{baseUrl}}/css/variables.css">
5
+ <link rel="stylesheet" href="{{baseUrl}}/css/styles.css">
6
+ <link rel="stylesheet" href="{{baseUrl}}/css/highlight/styles/base16/docula.css">
7
+ <link rel="icon" href="{{baseUrl}}/favicon.ico">
7
8
  <script>
8
9
  (function(){
9
10
  window.__doculaThemeKey = 'docula:theme:' + ({{#if siteUrl}}'{{siteUrl}}'{{else}}location.origin{{/if}}).replace(/^https?:\/\//, '');
@@ -12,5 +13,19 @@
12
13
  ? (window.matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark')
13
14
  : mode;
14
15
  if (resolved === 'light') document.documentElement.setAttribute('data-theme', 'light');
16
+ try {
17
+ var authState = JSON.parse(localStorage.getItem('docula-auth-state'));
18
+ if (authState) {
19
+ window.__doculaAuth = authState;
20
+ if (authState.loggedIn) {
21
+ document.documentElement.classList.add('docula-auth-logged-in');
22
+ }
23
+ }
24
+ } catch (e) {
25
+ // Ignore localStorage parsing errors
26
+ }
27
+ if (!window.__doculaAuth) {
28
+ window.__doculaAuth = { loggedIn: false, displayName: null };
29
+ }
15
30
  })();
16
31
  </script>
@@ -34,7 +34,7 @@
34
34
 
35
35
  {{#if (gt github.releases.length 6)}}
36
36
  <div>
37
- <a class="release-btn" href="/changelog">
37
+ <a class="release-btn" href="{{changelogUrl}}">
38
38
  Full Changelog
39
39
  <span>&rarr;</span>
40
40
  </a>
@@ -1,4 +1,4 @@
1
- <script src="/css/highlight/highlight.min.js"></script>
1
+ <script src="{{baseUrl}}/css/highlight/highlight.min.js"></script>
2
2
  <script>
3
3
  document.addEventListener('DOMContentLoaded', () => {
4
4
  document.querySelectorAll('pre code').forEach(el => { el.textContent = el.textContent.trimEnd(); });
@@ -90,43 +90,32 @@
90
90
  })();
91
91
  </script>
92
92
  {{#if cookieAuth}}
93
- <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
+ <div id="cookie-auth-config" hidden{{#if cookieAuth.logoutUrl}} data-logout-url="{{cookieAuth.logoutUrl}}"{{/if}}{{#if cookieAuth.authCheckUrl}} data-auth-check-url="{{cookieAuth.authCheckUrl}}"{{/if}}{{#if cookieAuth.authCheckMethod}} data-auth-check-method="{{cookieAuth.authCheckMethod}}"{{/if}}{{#if cookieAuth.authCheckUserPath}} data-auth-check-user-path="{{cookieAuth.authCheckUserPath}}"{{/if}}></div>
94
94
  <script>
95
95
  (function() {
96
96
  var configEl = document.getElementById('cookie-auth-config');
97
97
  if (!configEl) return;
98
- var cookieName = configEl.getAttribute('data-cookie-name');
99
98
  var logoutUrl = configEl.getAttribute('data-logout-url');
100
- function isSafeUrl(url) {
101
- if (!url) return false;
102
- try {
103
- var parsed = new URL(url, window.location.origin);
104
- return parsed.origin === window.location.origin;
105
- } catch (e) {
106
- return false;
99
+ var authCheckUrl = configEl.getAttribute('data-auth-check-url');
100
+ var authCheckMethod = configEl.getAttribute('data-auth-check-method') || 'GET';
101
+ var authCheckUserPath = configEl.getAttribute('data-auth-check-user-path');
102
+ function getNestedValue(obj, path) {
103
+ if (!path) return null;
104
+ var parts = path.split('.');
105
+ var current = obj;
106
+ for (var i = 0; i < parts.length; i++) {
107
+ if (current == null || typeof current !== 'object') return null;
108
+ current = current[parts[i]];
107
109
  }
110
+ return current || null;
108
111
  }
109
- function getCookieValue() {
110
- var match = document.cookie.split(';').find(function(c) {
111
- return c.trim().startsWith(cookieName + '=');
112
- });
113
- return match ? match.trim().substring(cookieName.length + 1) : null;
114
- }
115
- function getDisplayName(token) {
116
- try {
117
- var payload = token.split('.')[1];
118
- if (!payload) return null;
119
- var json = atob(payload.replace(/-/g, '+').replace(/_/g, '/'));
120
- var claims = JSON.parse(json);
121
- return claims.name || claims.preferred_username || claims.email || null;
122
- } catch (e) {
123
- return null;
124
- }
125
- }
126
- function updateAuthUI() {
127
- var token = getCookieValue();
128
- var loggedIn = !!token;
129
- var displayName = loggedIn ? getDisplayName(token) : null;
112
+ var cachedAuth = null;
113
+ try { cachedAuth = JSON.parse(localStorage.getItem('docula-auth-state')); } catch(e) {}
114
+ window.__doculaAuth = cachedAuth || { loggedIn: false, displayName: null };
115
+ function setAuthUI(loggedIn, displayName) {
116
+ window.__doculaAuth = { loggedIn: loggedIn, displayName: displayName };
117
+ try { localStorage.setItem('docula-auth-state', JSON.stringify(window.__doculaAuth)); } catch(e) {}
118
+ document.dispatchEvent(new CustomEvent('docula-auth-change'));
130
119
  var els = [
131
120
  { login: document.getElementById('cookie-auth-login'), logout: document.getElementById('cookie-auth-logout'), user: document.getElementById('cookie-auth-user') },
132
121
  { login: document.getElementById('cookie-auth-login-mobile'), logout: document.getElementById('cookie-auth-logout-mobile'), user: document.getElementById('cookie-auth-user-mobile') }
@@ -140,8 +129,29 @@
140
129
  }
141
130
  });
142
131
  }
132
+ function checkAuth() {
133
+ if (!authCheckUrl) return;
134
+ fetch(authCheckUrl, { method: authCheckMethod, credentials: 'include' }).then(function(res) {
135
+ if (!res.ok) {
136
+ setAuthUI(false, null);
137
+ return;
138
+ }
139
+ if (authCheckUserPath) {
140
+ res.json().then(function(data) {
141
+ setAuthUI(true, getNestedValue(data, authCheckUserPath));
142
+ }).catch(function() {
143
+ setAuthUI(true, null);
144
+ });
145
+ } else {
146
+ setAuthUI(true, null);
147
+ }
148
+ }).catch(function() {
149
+ setAuthUI(false, null);
150
+ });
151
+ }
143
152
  document.addEventListener('DOMContentLoaded', function() {
144
- updateAuthUI();
153
+ if (cachedAuth) setAuthUI(cachedAuth.loggedIn, cachedAuth.displayName);
154
+ checkAuth();
145
155
  var logoutEls = [
146
156
  document.getElementById('cookie-auth-logout'),
147
157
  document.getElementById('cookie-auth-logout-mobile')
@@ -149,10 +159,9 @@
149
159
  logoutEls.forEach(function(el) {
150
160
  if (el) {
151
161
  el.addEventListener('click', function() {
152
- if (isSafeUrl(logoutUrl)) {
162
+ if (logoutUrl) {
153
163
  window.location.href = logoutUrl;
154
164
  } else {
155
- document.cookie = cookieName + '=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/';
156
165
  window.location.reload();
157
166
  }
158
167
  });
@@ -130,11 +130,9 @@ document.addEventListener('DOMContentLoaded', function() {
130
130
  authValueInput.value = '';
131
131
  if (cookieStatusEl) {
132
132
  cookieStatusEl.classList.remove('api-auth__cookie-status--hidden');
133
- var configEl = document.getElementById('cookie-auth-config');
134
- var cookieName = configEl ? configEl.getAttribute('data-cookie-name') : (data.name || 'token');
135
- var hasCookie = document.cookie.split(';').some(function(c) { return c.trim().startsWith(cookieName + '='); });
136
- cookieStatusEl.textContent = hasCookie ? 'Logged in' : 'Not logged in — use Login button above';
137
- cookieStatusEl.className = 'api-auth__cookie-status' + (hasCookie ? ' api-auth__cookie-status--ok' : ' api-auth__cookie-status--warn');
133
+ var auth = window.__doculaAuth || { loggedIn: false };
134
+ cookieStatusEl.textContent = auth.loggedIn ? 'Logged in' : 'Not logged in — use Login button above';
135
+ cookieStatusEl.className = 'api-auth__cookie-status' + (auth.loggedIn ? ' api-auth__cookie-status--ok' : ' api-auth__cookie-status--warn');
138
136
  }
139
137
  } else {
140
138
  authValueInput.classList.remove('api-auth__value--hidden');
@@ -148,7 +146,20 @@ document.addEventListener('DOMContentLoaded', function() {
148
146
  if (cookieStatusEl) cookieStatusEl.classList.add('api-auth__cookie-status--hidden');
149
147
  }
150
148
  }
151
- authTypeSelect.addEventListener('change', updateAuthUI);
149
+ var savedAuth = localStorage.getItem('docula-api-auth-type');
150
+ if (savedAuth) {
151
+ for (var i = 0; i < authTypeSelect.options.length; i++) {
152
+ if (authTypeSelect.options[i].value === savedAuth) {
153
+ authTypeSelect.selectedIndex = i;
154
+ break;
155
+ }
156
+ }
157
+ }
158
+ authTypeSelect.addEventListener('change', function() {
159
+ localStorage.setItem('docula-api-auth-type', authTypeSelect.value);
160
+ updateAuthUI();
161
+ });
162
+ document.addEventListener('docula-auth-change', updateAuthUI);
152
163
  updateAuthUI();
153
164
  }
154
165