docula 2.1.0 → 2.2.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/dist/docula.d.ts CHANGED
@@ -202,6 +202,7 @@ type DoculaData = {
202
202
  }>;
203
203
  googleTagManager?: string;
204
204
  isGtag?: boolean;
205
+ googleTagManagerParams?: string;
205
206
  enableLlmsTxt?: boolean;
206
207
  enableSearch?: boolean;
207
208
  hasFeed?: boolean;
@@ -426,6 +427,16 @@ declare class DoculaOptions {
426
427
  * When set, injects the appropriate tracking script on every page.
427
428
  */
428
429
  googleTagManager?: string;
430
+ /**
431
+ * Google Tag Manager environment auth token (gtm_auth). Only applies to
432
+ * GTM container IDs; requires `googleTagManagerEnv` to also be set.
433
+ */
434
+ googleTagManagerAuth?: string;
435
+ /**
436
+ * Google Tag Manager environment name (gtm_preview), e.g., "env-3". Only
437
+ * applies to GTM container IDs; requires `googleTagManagerAuth` to also be set.
438
+ */
439
+ googleTagManagerEnv?: string;
429
440
  /**
430
441
  * AI-powered metadata enrichment configuration. When set, uses AI to fill
431
442
  * missing OpenGraph and HTML meta tag fields during the build.
package/dist/docula.js CHANGED
@@ -17,7 +17,7 @@ import { CacheableNet } from "@cacheable/net";
17
17
  import os from "node:os";
18
18
  import sea from "node:sea";
19
19
  //#region package.json
20
- var version = "2.1.0";
20
+ var version = "2.2.0";
21
21
  var package_default = {
22
22
  name: "docula",
23
23
  version,
@@ -731,6 +731,25 @@ function summarizeMarkdown(markdown, maxLength = 240) {
731
731
  function isRemoteUrl(url) {
732
732
  return /^https?:\/\//i.test(url);
733
733
  }
734
+ /**
735
+ * Encode a value for use in a URL query string. Unlike `encodeURIComponent`,
736
+ * this also escapes single quotes, so the result is safe to inject into a
737
+ * single-quoted JavaScript string literal (e.g., inline template scripts)
738
+ * where a raw quote would break the string.
739
+ */
740
+ function encodeQueryStringValue(value) {
741
+ return encodeURIComponent(value).replace(/'/g, "%27");
742
+ }
743
+ /**
744
+ * Build the extra query string appended to Google Tag Manager URLs when
745
+ * targeting a GTM environment. A GTM environment is identified by both an
746
+ * auth token and an environment name, so this returns `undefined` unless
747
+ * both are set.
748
+ */
749
+ function buildGtmEnvironmentParams(auth, env) {
750
+ if (!auth || !env) return;
751
+ return `&gtm_auth=${encodeQueryStringValue(auth)}&gtm_preview=${encodeQueryStringValue(env)}&gtm_cookies_win=x`;
752
+ }
734
753
  //#endregion
735
754
  //#region src/builder-seo.ts
736
755
  const writrOptions$5 = {
@@ -1437,7 +1456,9 @@ function hashOptions(hash, options) {
1437
1456
  apiPath: options.apiPath,
1438
1457
  changelogPath: options.changelogPath,
1439
1458
  ai: options.ai,
1440
- googleTagManager: options.googleTagManager
1459
+ googleTagManager: options.googleTagManager,
1460
+ googleTagManagerAuth: options.googleTagManagerAuth,
1461
+ googleTagManagerEnv: options.googleTagManagerEnv
1441
1462
  };
1442
1463
  const optionsHash = hash.toHashSync(JSON.stringify(relevant));
1443
1464
  const configHash = hashConfigFile(hash, options.sitePath);
@@ -3041,6 +3062,16 @@ var DoculaOptions = class {
3041
3062
  */
3042
3063
  googleTagManager;
3043
3064
  /**
3065
+ * Google Tag Manager environment auth token (gtm_auth). Only applies to
3066
+ * GTM container IDs; requires `googleTagManagerEnv` to also be set.
3067
+ */
3068
+ googleTagManagerAuth;
3069
+ /**
3070
+ * Google Tag Manager environment name (gtm_preview), e.g., "env-3". Only
3071
+ * applies to GTM container IDs; requires `googleTagManagerAuth` to also be set.
3072
+ */
3073
+ googleTagManagerEnv;
3074
+ /**
3044
3075
  * AI-powered metadata enrichment configuration. When set, uses AI to fill
3045
3076
  * missing OpenGraph and HTML meta tag fields during the build.
3046
3077
  * Requires provider name and API key. Omit to disable AI enrichment.
@@ -3131,6 +3162,8 @@ var DoculaOptions = class {
3131
3162
  if (options.autoReadme !== void 0 && typeof options.autoReadme === "boolean") this.autoReadme = options.autoReadme;
3132
3163
  if (options.quiet !== void 0 && typeof options.quiet === "boolean") this.quiet = options.quiet;
3133
3164
  if (options.googleTagManager !== void 0 && typeof options.googleTagManager === "string" && /^G[A-Z]*-[A-Z0-9]+$/i.test(options.googleTagManager)) this.googleTagManager = options.googleTagManager;
3165
+ if (options.googleTagManagerAuth !== void 0 && typeof options.googleTagManagerAuth === "string" && options.googleTagManagerAuth.length > 0) this.googleTagManagerAuth = options.googleTagManagerAuth;
3166
+ if (options.googleTagManagerEnv !== void 0 && typeof options.googleTagManagerEnv === "string" && options.googleTagManagerEnv.length > 0) this.googleTagManagerEnv = options.googleTagManagerEnv;
3134
3167
  if (options.ai && typeof options.ai === "object" && typeof options.ai.provider === "string" && typeof options.ai.apiKey === "string") this.ai = options.ai;
3135
3168
  if (options.cache && typeof options.cache === "object" && options.cache.github !== null && typeof options.cache.github === "object" && typeof options.cache.github.ttl === "number") this.cache = options.cache;
3136
3169
  if (options.homeUrl !== void 0 && typeof options.homeUrl === "string") this.homeUrl = options.homeUrl === "/" ? "/" : trimTrailingSlashes(options.homeUrl);
@@ -3272,6 +3305,7 @@ var DoculaBuilder = class {
3272
3305
  headerLinks: this.options.headerLinks,
3273
3306
  googleTagManager: this.options.googleTagManager,
3274
3307
  isGtag: this.options.googleTagManager?.startsWith("G-") ?? false,
3308
+ googleTagManagerParams: buildGtmEnvironmentParams(this.options.googleTagManagerAuth, this.options.googleTagManagerEnv),
3275
3309
  enableLlmsTxt: this.options.enableLlmsTxt,
3276
3310
  homeUrl: this.options.homeUrl,
3277
3311
  baseUrl: this.options.baseUrl,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "docula",
3
- "version": "2.1.0",
3
+ "version": "2.2.0",
4
4
  "description": "Beautiful Website for Your Projects",
5
5
  "type": "module",
6
6
  "main": "./dist/docula.js",
@@ -1,7 +1,7 @@
1
1
  {{#if googleTagManager}}
2
2
  {{#unless isGtag}}
3
3
  <!-- Google Tag Manager (noscript) -->
4
- <noscript><iframe src="https://www.googletagmanager.com/ns.html?id={{googleTagManager}}"
4
+ <noscript><iframe src="https://www.googletagmanager.com/ns.html?id={{googleTagManager}}{{{googleTagManagerParams}}}"
5
5
  height="0" width="0" style="display:none !important;visibility:hidden !important"></iframe></noscript>
6
6
  <!-- End Google Tag Manager (noscript) -->
7
7
  {{/unless}}
@@ -8,7 +8,7 @@
8
8
  <script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
9
9
  new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
10
10
  j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
11
- 'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
11
+ 'https://www.googletagmanager.com/gtm.js?id='+i+dl+'{{{googleTagManagerParams}}}';f.parentNode.insertBefore(j,f);
12
12
  })(window,document,'script','dataLayer','{{googleTagManager}}');</script>
13
13
  <!-- End Google Tag Manager -->
14
14
  {{/if}}
@@ -1,7 +1,7 @@
1
1
  {{#if googleTagManager}}
2
2
  {{#unless isGtag}}
3
3
  <!-- Google Tag Manager (noscript) -->
4
- <noscript><iframe src="https://www.googletagmanager.com/ns.html?id={{googleTagManager}}"
4
+ <noscript><iframe src="https://www.googletagmanager.com/ns.html?id={{googleTagManager}}{{{googleTagManagerParams}}}"
5
5
  height="0" width="0" style="display:none !important;visibility:hidden !important"></iframe></noscript>
6
6
  <!-- End Google Tag Manager (noscript) -->
7
7
  {{/unless}}
@@ -8,7 +8,7 @@
8
8
  <script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
9
9
  new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
10
10
  j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
11
- 'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
11
+ 'https://www.googletagmanager.com/gtm.js?id='+i+dl+'{{{googleTagManagerParams}}}';f.parentNode.insertBefore(j,f);
12
12
  })(window,document,'script','dataLayer','{{googleTagManager}}');</script>
13
13
  <!-- End Google Tag Manager -->
14
14
  {{/if}}
@@ -265,23 +265,34 @@
265
265
  });
266
266
  }
267
267
 
268
- // Active header nav link highlighting
269
- const navLinks = document.querySelectorAll('.header-bottom__item');
270
- navLinks.forEach((link) => {
271
- const href = link.getAttribute('href');
272
- if (currentPath.startsWith(href)) {
273
- link.classList.add('header-bottom__item--active');
274
- }
275
- });
276
-
277
- // Active mobile nav link highlighting
278
- const mobileNavLinks = document.querySelectorAll('.mobile-nav__item');
279
- mobileNavLinks.forEach((link) => {
280
- const href = link.getAttribute('href');
281
- if (currentPath.startsWith(href)) {
282
- link.classList.add('mobile-nav__item--active');
283
- }
284
- });
268
+ // Active nav link highlighting (header + mobile). Highlight only the
269
+ // single most-specific link: the one whose path is the longest prefix of
270
+ // the current path. A plain startsWith() check lights up every link whose
271
+ // path merely prefixes the current one, so when the site is embedded under
272
+ // a base path (e.g. served at /developer with docs at that root) the
273
+ // Documentation link — whose path is a prefix of every page — would stay
274
+ // highlighted everywhere. Trailing slashes are normalized so matches stop
275
+ // at a path boundary. We read the anchor's resolved origin/pathname so
276
+ // absolute, relative, and same-origin hrefs all work, while cross-origin
277
+ // links and non-anchor items (e.g. the mobile logout button) are skipped.
278
+ const withTrailingSlash = (p) => (p.endsWith('/') ? p : p + '/');
279
+ const here = withTrailingSlash(currentPath);
280
+ const markActiveNav = (selector, activeClass) => {
281
+ let bestLink = null;
282
+ let bestLength = -1;
283
+ document.querySelectorAll(selector).forEach((link) => {
284
+ // Only same-origin anchors can match the current pathname.
285
+ if (!link.href || link.origin !== window.location.origin) return;
286
+ const base = withTrailingSlash(link.pathname);
287
+ if (here.startsWith(base) && base.length > bestLength) {
288
+ bestLink = link;
289
+ bestLength = base.length;
290
+ }
291
+ });
292
+ if (bestLink) bestLink.classList.add(activeClass);
293
+ };
294
+ markActiveNav('.header-bottom__item', 'header-bottom__item--active');
295
+ markActiveNav('.mobile-nav__item', 'mobile-nav__item--active');
285
296
 
286
297
  // TOC extraction: move the inline TOC into the right sidebar and hide the original
287
298
  const tocHeading = document.getElementById('table-of-contents');