@leed-ai/cli 0.0.0-stage → 6.19.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 (263) hide show
  1. package/LICENSE.md +57 -0
  2. package/README.md +125 -2
  3. package/dist/LICENSE.md +57 -0
  4. package/dist/acorn-DAgDT1om.js +5087 -0
  5. package/dist/angular-l0_4PK8N.js +3598 -0
  6. package/dist/authShared-Dray38Ls.js +1161 -0
  7. package/dist/babel-CJeRQd5u.js +10038 -0
  8. package/dist/buildResults-tiq1nugP.js +21180 -0
  9. package/dist/chunk-1VJ9lAw7.js +42 -0
  10. package/dist/cliAuth-38JjD6hw.js +15 -0
  11. package/dist/env-B0bHiXJJ.js +29 -0
  12. package/dist/errors-CqKamGCu.js +481 -0
  13. package/dist/estree-E8WG8UHj.js +6930 -0
  14. package/dist/flow-6xaRIbPQ.js +52255 -0
  15. package/dist/glimmer-aJPjsfbT.js +7272 -0
  16. package/dist/graphql-mcgtPEiZ.js +1901 -0
  17. package/dist/html--eeea3Kl.js +6553 -0
  18. package/dist/leed.eleventy.mjs +6525 -0
  19. package/dist/leed.mjs +88952 -0
  20. package/dist/leed.sources.json +798 -0
  21. package/dist/leedLogger-Zkada5aO.js +78 -0
  22. package/dist/markdown-D73jNKwx.js +6836 -0
  23. package/dist/meriyah-CMp1nDDW.js +8932 -0
  24. package/dist/package.json +101 -0
  25. package/dist/postcss-h4fBr2Fn.js +6760 -0
  26. package/dist/prettier-CwFSKxfN.js +16821 -0
  27. package/dist/static/customer-site-worker/package.json +9 -0
  28. package/dist/static/customer-site-worker/src/assetsHydration.ts +55 -0
  29. package/dist/static/customer-site-worker/src/constants.ts +20 -0
  30. package/dist/static/customer-site-worker/src/cors.ts +120 -0
  31. package/dist/static/customer-site-worker/src/index.ts +242 -0
  32. package/dist/static/customer-site-worker/src/mcp-proxy.ts +98 -0
  33. package/dist/static/customer-site-worker/src/proxy.ts +92 -0
  34. package/dist/static/customer-site-worker/src/proxyHeaders.ts +47 -0
  35. package/dist/static/customer-site-worker/src/tier.ts +40 -0
  36. package/dist/static/customer-site-worker/src/types.ts +82 -0
  37. package/dist/static/customer-site-worker/test/functions-mock.worker.js +50 -0
  38. package/dist/static/customer-site-worker/tsconfig.json +19 -0
  39. package/dist/static/customer-site-worker/vitest.workers.config.ts +61 -0
  40. package/dist/static/install/husky/colors.mjs +5 -0
  41. package/dist/static/install/husky/hooks/commit-msg +1 -0
  42. package/dist/static/install/husky/hooks/pre-commit +1 -0
  43. package/dist/static/install/husky/hooks/pre-push +1 -0
  44. package/dist/static/install/husky/valid-command.mjs +29 -0
  45. package/dist/static/install/husky/valid-commit-msg.mjs +20 -0
  46. package/dist/static/install/package.json +10 -0
  47. package/dist/static/install/packages.txt +4 -0
  48. package/dist/static/install/tasks.json +111 -0
  49. package/dist/static/javascript/detectIncognito.js +195 -0
  50. package/dist/static/javascript/documentation.js +2781 -0
  51. package/dist/static/javascript/fp/fp.esm.js +2896 -0
  52. package/dist/static/javascript/fp/fp.js +3067 -0
  53. package/dist/static/javascript/live-search.js +986 -0
  54. package/dist/static/javascript/lunr-search.js +843 -0
  55. package/dist/static/javascript/timeme.js +451 -0
  56. package/dist/static/javascript/tracker/webmcp.js +67 -0
  57. package/dist/static/javascript/tracker/whisper.js +1 -0
  58. package/dist/static/javascript/utilities.js +819 -0
  59. package/dist/static/javascript/versions.json +74 -0
  60. package/dist/static/javascript/zoomable.js +488 -0
  61. package/dist/static/site/_data/eleventyComputed.js +228 -0
  62. package/dist/static/site/_data/leed-form-data.json +253 -0
  63. package/dist/static/site/_includes/leed/api/bits/array-type.hbs +24 -0
  64. package/dist/static/site/_includes/leed/api/bits/badges.hbs +9 -0
  65. package/dist/static/site/_includes/leed/api/bits/description.hbs +9 -0
  66. package/dist/static/site/_includes/leed/api/bits/examples.hbs +42 -0
  67. package/dist/static/site/_includes/leed/api/bits/scopes.hbs +28 -0
  68. package/dist/static/site/_includes/leed/api/object.hbs +110 -0
  69. package/dist/static/site/_includes/leed/api/parameters.hbs +11 -0
  70. package/dist/static/site/_includes/leed/api/property.hbs +153 -0
  71. package/dist/static/site/_includes/leed/api/request-body.hbs +51 -0
  72. package/dist/static/site/_includes/leed/api/responses.hbs +89 -0
  73. package/dist/static/site/_includes/leed/api/security-constraints.hbs +59 -0
  74. package/dist/static/site/_includes/leed/api/security.hbs +75 -0
  75. package/dist/static/site/_includes/leed/components/pagination.hbs +45 -0
  76. package/dist/static/site/_includes/leed/components/scroll-status-indicator-horizontal.hbs +22 -0
  77. package/dist/static/site/_includes/leed/docs/alpha/api.hbs +6 -0
  78. package/dist/static/site/_includes/leed/docs/alpha/base.hbs +26 -0
  79. package/dist/static/site/_includes/leed/docs/alpha/documentation.hbs +7 -0
  80. package/dist/static/site/_includes/leed/docs/api-article.hbs +75 -0
  81. package/dist/static/site/_includes/leed/docs/bravo/api.hbs +5 -0
  82. package/dist/static/site/_includes/leed/docs/bravo/base.hbs +31 -0
  83. package/dist/static/site/_includes/leed/docs/bravo/documentation.hbs +7 -0
  84. package/dist/static/site/_includes/leed/docs/charlie/api.hbs +6 -0
  85. package/dist/static/site/_includes/leed/docs/charlie/base.hbs +27 -0
  86. package/dist/static/site/_includes/leed/docs/charlie/documentation.hbs +10 -0
  87. package/dist/static/site/_includes/leed/docs/doc-article.hbs +40 -0
  88. package/dist/static/site/_includes/leed/docs/docs-footer.hbs +35 -0
  89. package/dist/static/site/_includes/leed/docs/docs-header.hbs +59 -0
  90. package/dist/static/site/_includes/leed/docs/document-search-modal.hbs +36 -0
  91. package/dist/static/site/_includes/leed/docs/document-search.hbs +23 -0
  92. package/dist/static/site/_includes/leed/docs/footer.hbs +12 -0
  93. package/dist/static/site/_includes/leed/docs/header/brand.hbs +19 -0
  94. package/dist/static/site/_includes/leed/docs/header/chrome.hbs +33 -0
  95. package/dist/static/site/_includes/leed/docs/header/compact.hbs +19 -0
  96. package/dist/static/site/_includes/leed/docs/header/cta.hbs +15 -0
  97. package/dist/static/site/_includes/leed/docs/header/nav.hbs +18 -0
  98. package/dist/static/site/_includes/leed/docs/header/top-search.hbs +15 -0
  99. package/dist/static/site/_includes/leed/docs/mini-bar.hbs +53 -0
  100. package/dist/static/site/_includes/leed/docs/pagination.hbs +19 -0
  101. package/dist/static/site/_includes/leed/docs/shell.hbs +56 -0
  102. package/dist/static/site/_includes/leed/docs/sidebar.hbs +52 -0
  103. package/dist/static/site/_includes/leed/docs/social-icons.hbs +81 -0
  104. package/dist/static/site/_includes/leed/docs/style.hbs +17 -0
  105. package/dist/static/site/_includes/leed/docs/toc.hbs +7 -0
  106. package/dist/static/site/_includes/leed/form.hbs +266 -0
  107. package/dist/static/site/_includes/leed/head.hbs +67 -0
  108. package/dist/static/site/_includes/leed/menu/builder.hbs +35 -0
  109. package/dist/static/site/_includes/leed/menu/item.hbs +67 -0
  110. package/dist/static/site/_includes/leed/menu/link.hbs +59 -0
  111. package/dist/static/site/_includes/leed/metadata/ogCard.hbs +40 -0
  112. package/dist/static/site/_includes/leed/metadata/siteMetadata.hbs +24 -0
  113. package/dist/static/site/_includes/leed/metadata/twitterCard.hbs +34 -0
  114. package/dist/static/site/_includes/leed/popup-video.hbs +21 -0
  115. package/dist/static/site/_includes/leed/stub-template.hbs +17 -0
  116. package/dist/static/site/_includes/leed/unsubscribe-form.hbs +30 -0
  117. package/dist/static/site/_layouts/leed-documentation.hbs +12 -0
  118. package/dist/static/site/leed-feed-atom.hbs +42 -0
  119. package/dist/static/site/leed-feed-json.hbs +56 -0
  120. package/dist/static/site/leed-feed-rss.hbs +39 -0
  121. package/dist/static/site/leed-headers.hbs +38 -0
  122. package/dist/static/site/leed-redirects.hbs +10 -0
  123. package/dist/static/site/leed-sitemap-pageType.hbs +28 -0
  124. package/dist/static/site/leed-sitemap.hbs +19 -0
  125. package/dist/static/site/leed-unsubscribe.hbs +44 -0
  126. package/dist/static/site/leed-unsubscribed.hbs +40 -0
  127. package/dist/static/tailwind/components/alerts.css +132 -0
  128. package/dist/static/tailwind/components/api-methods.css +39 -0
  129. package/dist/static/tailwind/components/breadcrumbs.css +31 -0
  130. package/dist/static/tailwind/components/code.css +88 -0
  131. package/dist/static/tailwind/components/collapsible.css +38 -0
  132. package/dist/static/tailwind/components/document-search.css +222 -0
  133. package/dist/static/tailwind/components/form.css +185 -0
  134. package/dist/static/tailwind/components/kbd.css +7 -0
  135. package/dist/static/tailwind/components/pagination.css +81 -0
  136. package/dist/static/tailwind/components/scroll-status-indicator.css +5 -0
  137. package/dist/static/tailwind/components/tab-groups.css +117 -0
  138. package/dist/static/tailwind/components/tables.css +0 -0
  139. package/dist/static/tailwind/components/toc.css +60 -0
  140. package/dist/static/tailwind/components.css +248 -0
  141. package/dist/static/tailwind/defaults.css +260 -0
  142. package/dist/static/tailwind/docs/api.css +701 -0
  143. package/dist/static/tailwind/docs/components/blockquote.css +5 -0
  144. package/dist/static/tailwind/docs/components/button.css +11 -0
  145. package/dist/static/tailwind/docs/components/docs-pagination.css +71 -0
  146. package/dist/static/tailwind/docs/components/mobile-bar.css +83 -0
  147. package/dist/static/tailwind/docs/components/navigation.css +279 -0
  148. package/dist/static/tailwind/docs/components/table.css +54 -0
  149. package/dist/static/tailwind/docs/components.css +6 -0
  150. package/dist/static/tailwind/docs/docs.css +427 -0
  151. package/dist/static/tailwind/docs/layouts/alpha.css +108 -0
  152. package/dist/static/tailwind/docs/layouts/bravo.css +218 -0
  153. package/dist/static/tailwind/docs/layouts/charlie.css +105 -0
  154. package/dist/static/tailwind/docs/layouts.css +3 -0
  155. package/dist/static/tailwind/docs/themes/amber.css +113 -0
  156. package/dist/static/tailwind/docs/themes/blue.css +94 -0
  157. package/dist/static/tailwind/docs/themes/cyan.css +111 -0
  158. package/dist/static/tailwind/docs/themes/emerald.css +111 -0
  159. package/dist/static/tailwind/docs/themes/fuchsia.css +111 -0
  160. package/dist/static/tailwind/docs/themes/green.css +95 -0
  161. package/dist/static/tailwind/docs/themes/indigo.css +111 -0
  162. package/dist/static/tailwind/docs/themes/lime.css +111 -0
  163. package/dist/static/tailwind/docs/themes/orange.css +111 -0
  164. package/dist/static/tailwind/docs/themes/pink.css +111 -0
  165. package/dist/static/tailwind/docs/themes/purple.css +111 -0
  166. package/dist/static/tailwind/docs/themes/red.css +111 -0
  167. package/dist/static/tailwind/docs/themes/rose.css +111 -0
  168. package/dist/static/tailwind/docs/themes/sky.css +111 -0
  169. package/dist/static/tailwind/docs/themes/teal.css +111 -0
  170. package/dist/static/tailwind/docs/themes/violet.css +111 -0
  171. package/dist/static/tailwind/docs/themes/yellow.css +111 -0
  172. package/dist/static/tailwind/docs/themes.css +84 -0
  173. package/dist/static/tailwind/font-awesome/free-7.3.1/brands.css +2455 -0
  174. package/dist/static/tailwind/font-awesome/free-7.3.1/fontawesome.css +8997 -0
  175. package/dist/static/tailwind/font-awesome/free-7.3.1/fonts.css +45 -0
  176. package/dist/static/tailwind/font-awesome/free-7.3.1/icons.json +2608 -0
  177. package/dist/static/tailwind/font-awesome/free-7.3.1.css +3 -0
  178. package/dist/static/tailwind/font-awesome/pro-7.0.0/brands.css +2215 -0
  179. package/dist/static/tailwind/font-awesome/pro-7.0.0/fontawesome.css +19281 -0
  180. package/dist/static/tailwind/font-awesome/pro-7.0.0/fonts.css +445 -0
  181. package/dist/static/tailwind/font-awesome/pro-7.0.0.css +3 -0
  182. package/dist/static/tailwind/font-awesome/pro-7.3.1/brands.css +2455 -0
  183. package/dist/static/tailwind/font-awesome/pro-7.3.1/fontawesome.css +20365 -0
  184. package/dist/static/tailwind/font-awesome/pro-7.3.1/fonts.css +589 -0
  185. package/dist/static/tailwind/font-awesome/pro-7.3.1.css +3 -0
  186. package/dist/static/tailwind/fonts/all.css +6 -0
  187. package/dist/static/tailwind/fonts/config.jsonc +31 -0
  188. package/dist/static/tailwind/fonts/geist-mono.css +142 -0
  189. package/dist/static/tailwind/fonts/geist.css +142 -0
  190. package/dist/static/tailwind/fonts/inter.css +215 -0
  191. package/dist/static/tailwind/fonts/jetbrains-mono.css +126 -0
  192. package/dist/static/tailwind/fonts/noto-sans.css +134 -0
  193. package/dist/static/tailwind/fonts/open-sans.css +89 -0
  194. package/dist/static/tailwind/highlighters.css +14 -0
  195. package/dist/static/tailwind/hljs/README.md +53 -0
  196. package/dist/static/tailwind/hljs/a11y.css +96 -0
  197. package/dist/static/tailwind/hljs/atom-fruit.css +90 -0
  198. package/dist/static/tailwind/hljs/atom-one.css +164 -0
  199. package/dist/static/tailwind/hljs/default.css +164 -0
  200. package/dist/static/tailwind/hljs/github.css +101 -0
  201. package/dist/static/tailwind/hljs/google-code.css +112 -0
  202. package/dist/static/tailwind/hljs/grayscale.css +115 -0
  203. package/dist/static/tailwind/hljs/gruvbox-hard.css +115 -0
  204. package/dist/static/tailwind/hljs/gruvbox-medium.css +115 -0
  205. package/dist/static/tailwind/hljs/gruvbox-soft.css +115 -0
  206. package/dist/static/tailwind/hljs/papercolor.css +120 -0
  207. package/dist/static/tailwind/hljs/qt-creator.css +91 -0
  208. package/dist/static/tailwind/hljs/summerfruit.css +115 -0
  209. package/dist/static/tailwind/hljs/tomorrow.css +115 -0
  210. package/dist/static/tailwind/leed.css +170 -0
  211. package/dist/static/tailwind/site-tokens.reference.css +105 -0
  212. package/dist/static/tailwind/utils/icons.css +102 -0
  213. package/dist/static/tailwind/utils/utilities.css +105 -0
  214. package/dist/static/templates/pages/PAGE.hbs +27 -0
  215. package/dist/static/templates/pages/PAGE_LAYOUT.hbs +19 -0
  216. package/dist/static/templates/pages/PAGE_TYPE_INDEX.hbs +89 -0
  217. package/dist/static/templates/pages/PAGINATED_PAGE_TYPE_ITEM.hbs +52 -0
  218. package/dist/static/templates/pages/PAGINATED_PAGE_TYPE_LIST.hbs +39 -0
  219. package/dist/static/templates/pages/PAGINATED_PAGE_TYPE_PAGINATION.hbs +11 -0
  220. package/dist/static/templates/raw-content/.ci/build.sh +58 -0
  221. package/dist/static/templates/raw-content/README.md +179 -0
  222. package/dist/static/templates/raw-content/_gitignore +50 -0
  223. package/dist/static/templates/raw-content/src/404.hbs +21 -0
  224. package/dist/static/templates/raw-content/src/_data/autolink.json +1 -0
  225. package/dist/static/templates/raw-content/src/_data/ctaList.json +1 -0
  226. package/dist/static/templates/raw-content/src/_data/leedForms.json +1 -0
  227. package/dist/static/templates/raw-content/src/_data/menu.json +1 -0
  228. package/dist/static/templates/raw-content/src/_includes/footer.hbs +18 -0
  229. package/dist/static/templates/raw-content/src/_includes/header-includes.hbs +25 -0
  230. package/dist/static/templates/raw-content/src/_includes/header.hbs +5 -0
  231. package/dist/static/templates/raw-content/src/_includes/site-template.hbs +23 -0
  232. package/dist/static/templates/raw-content/src/_includes/unsubscribe-DISABLED.hbs +17 -0
  233. package/dist/static/templates/raw-content/src/_includes/unsubscribed-DISABLED.hbs +13 -0
  234. package/dist/static/templates/raw-content/src/_layouts/empty.hbs +2 -0
  235. package/dist/static/templates/raw-content/src/index.hbs +21 -0
  236. package/dist/static/templates/raw-content/src/robots.hbs +9 -0
  237. package/dist/static/templates/raw-content/tailwind/site/base.css +44 -0
  238. package/dist/static/templates/raw-content/tailwind/site.config.css +3 -0
  239. package/dist/static/templates/sandbox/_gitignore +50 -0
  240. package/dist/static/templates/sandbox/identity.json +1 -0
  241. package/dist/static/templates/sandbox/src/404.hbs +21 -0
  242. package/dist/static/templates/sandbox/src/_data/autolink.json +1 -0
  243. package/dist/static/templates/sandbox/src/_data/ctaList.json +1 -0
  244. package/dist/static/templates/sandbox/src/_data/labelList.json +9 -0
  245. package/dist/static/templates/sandbox/src/_data/leedForms.json +20 -0
  246. package/dist/static/templates/sandbox/src/_data/menu.json +19 -0
  247. package/dist/static/templates/sandbox/src/_data/pageTypeList.json +14 -0
  248. package/dist/static/templates/sandbox/src/_data/searchIndexList.json +1 -0
  249. package/dist/static/templates/sandbox/src/_data/userList.json +11 -0
  250. package/dist/static/templates/sandbox/src/_includes/footer.hbs +18 -0
  251. package/dist/static/templates/sandbox/src/_includes/header-includes.hbs +25 -0
  252. package/dist/static/templates/sandbox/src/_includes/header.hbs +5 -0
  253. package/dist/static/templates/sandbox/src/_includes/site-template.hbs +23 -0
  254. package/dist/static/templates/sandbox/src/_layouts/empty.hbs +2 -0
  255. package/dist/static/templates/sandbox/src/index.hbs +20 -0
  256. package/dist/static/templates/sandbox/src/robots.hbs +9 -0
  257. package/dist/static/templates/sandbox/src/src.11tydata.json +39 -0
  258. package/dist/static/templates/sandbox/tailwind/site/base.css +44 -0
  259. package/dist/static/templates/sandbox/tailwind/site.config.css +3 -0
  260. package/dist/thrown-sukVkp_1.js +113 -0
  261. package/dist/typescript-D4T0V_Hn.js +23004 -0
  262. package/dist/yaml-9wR8GxF_.js +5236 -0
  263. package/package.json +157 -4
@@ -0,0 +1,843 @@
1
+ /**
2
+ * Client-side search implementation using the generated search index
3
+ *
4
+ * This is an example of how to use the search index generated by the search plugin.
5
+ * Include this script in your site and call initializeSearch() when the page loads.
6
+ *
7
+ * Dependencies:
8
+ * - lunr.js (https://lunrjs.com/)
9
+ *
10
+ * Usage:
11
+ * 1. Include lunr.js in your page
12
+ * 2. Include this script
13
+ * 3. Call initializeSearch() when DOM is ready
14
+ * 4. Use performSearch(query) to search
15
+ */
16
+
17
+ let searchIndex = null;
18
+ let searchDocuments = [];
19
+
20
+ /**
21
+ * Initialize the search functionality by loading the search index and documents
22
+ */
23
+ async function initializeSearch() {
24
+ // Safely check if variables are defined (they may not be if search is not configured)
25
+ if (
26
+ typeof searchIndexId === "undefined" ||
27
+ typeof searchIndexHash === "undefined" ||
28
+ typeof searchDocumentsHash === "undefined"
29
+ ) {
30
+ return false;
31
+ }
32
+
33
+ const indexPath = `/static/search/${searchIndexId}-index.json?v=${searchIndexHash}`;
34
+ const documentsPath = `/static/search/${searchIndexId}-documents.json?v=${searchDocumentsHash}`;
35
+
36
+ // Guard on the MODAL, not the trigger button. The docs shell always renders the
37
+ // modal, while the button lives in the header — which a site can replace via
38
+ // `_includes/docs-header.hbs`. Guarding on the button meant a custom header
39
+ // without it skipped index loading entirely, so Ctrl+K opened a dead modal.
40
+ const searchElement = document.querySelector("#document-search-modal");
41
+ if (!searchElement) {
42
+ // Silent: a page without a search component is not an error, and this script
43
+ // is copied verbatim to the customer site, so a log here prints in every
44
+ // reader's console on every page. console.warn/error below are kept.
45
+ return false;
46
+ }
47
+
48
+ try {
49
+ // Load the search index and documents in parallel
50
+ const [indexResponse, documentsResponse] = await Promise.all([fetch(indexPath), fetch(documentsPath)]);
51
+
52
+ if (!indexResponse.ok) {
53
+ throw new Error(`Failed to load search index ${indexPath}: ${indexResponse.status}`);
54
+ }
55
+ if (!documentsResponse.ok) {
56
+ throw new Error(`Failed to load search documents ${documentsPath}: ${documentsResponse.status}`);
57
+ }
58
+
59
+ const [indexData, documentsData] = await Promise.all([indexResponse.json(), documentsResponse.json()]);
60
+
61
+ // Load the pre-built lunr index
62
+ searchIndex = lunr.Index.load(indexData.index);
63
+ searchDocuments = documentsData.documents;
64
+
65
+ return true;
66
+ } catch (error) {
67
+ console.error("Failed to initialize search:", error);
68
+ return false;
69
+ }
70
+ }
71
+
72
+ /**
73
+ * Perform a search query
74
+ * @param {string} query - The search query
75
+ * @param {Object} options - Search options
76
+ * @param {number} options.limit - Maximum number of results (default: 10)
77
+ * @param {string[]} options.pageTypes - Filter by page types
78
+ * @returns {Array} Array of search results
79
+ */
80
+ function performSearch(query, options = {}) {
81
+ if (!searchIndex || !query.trim()) {
82
+ return [];
83
+ }
84
+
85
+ const { limit = 10, pageTypes = [] } = options;
86
+
87
+ try {
88
+ // Perform the search using lunr
89
+ const results = searchIndex.search(query);
90
+
91
+ // Map results to full document data
92
+ let searchResults = results
93
+ .map((result) => {
94
+ const document = searchDocuments.find((doc) => doc.id === result.ref);
95
+ if (!document) return null;
96
+
97
+ return {
98
+ ...document,
99
+ score: result.score,
100
+ matches: result.matchData,
101
+ };
102
+ })
103
+ .filter(Boolean);
104
+
105
+ // Filter by page types if specified
106
+ if (pageTypes.length > 0) {
107
+ searchResults = searchResults.filter((result) => pageTypes.includes(result.pageType));
108
+ }
109
+
110
+ // Limit results
111
+ return searchResults.slice(0, limit);
112
+ } catch (error) {
113
+ console.error("Search error:", error);
114
+ return [];
115
+ }
116
+ }
117
+
118
+ /**
119
+ * Get search suggestions based on partial query
120
+ * @param {string} partialQuery - Partial search query
121
+ * @param {number} limit - Maximum number of suggestions
122
+ * @returns {Array} Array of search suggestions
123
+ */
124
+ function getSearchSuggestions(partialQuery, limit = 5) {
125
+ if (!searchDocuments || !partialQuery.trim()) {
126
+ return [];
127
+ }
128
+
129
+ const query = partialQuery.toLowerCase();
130
+ const suggestions = [];
131
+
132
+ // Simple suggestion algorithm - match titles and excerpts
133
+ for (const doc of searchDocuments) {
134
+ if (suggestions.length >= limit) break;
135
+
136
+ const title = doc.title.toLowerCase();
137
+ const summary = doc.summary.toLowerCase();
138
+
139
+ if (title.includes(query) || summary.includes(query)) {
140
+ suggestions.push({
141
+ title: doc.title,
142
+ url: doc.url,
143
+ summary: doc.summary,
144
+ pageTypeName: doc.pageTypeName,
145
+ });
146
+ }
147
+ }
148
+
149
+ return suggestions;
150
+ }
151
+
152
+ /**
153
+ * Search UI integration for existing modal
154
+ */
155
+ class SearchUI {
156
+ constructor(options = {}) {
157
+ this.searchInput = null;
158
+ this.searchResults = null;
159
+ this.searchTimeout = null;
160
+ this.saveTimeout = null;
161
+ this.isInitialized = false;
162
+
163
+ // Selection tracking
164
+ this.selectedIndex = -1;
165
+ this.selectableItems = [];
166
+
167
+ // Search history configuration
168
+ this.maxHistoryItems = options.maxHistoryItems || 5;
169
+ this.historyStorageKey = options.historyStorageKey || "searchHistory";
170
+ this.searchHistory = this.loadSearchHistory();
171
+ }
172
+
173
+ /**
174
+ * Initialize search UI with existing modal elements
175
+ */
176
+ initialize() {
177
+ // Find the search input in the existing modal
178
+ this.searchInput = document.querySelector('#document-search-modal input[type="text"]');
179
+ this.searchResults = document.querySelector("#document-search-modal .search-results-container");
180
+
181
+ if (!this.searchInput || !this.searchResults) {
182
+ console.warn("Search modal elements not found");
183
+ return false;
184
+ }
185
+
186
+ this.setupEventListeners();
187
+
188
+ // Show recent searches by default
189
+ this.showRecentSearches();
190
+
191
+ // Set up modal open listener to clear input
192
+ this.setupModalOpenListener();
193
+
194
+ this.isInitialized = true;
195
+ return true;
196
+ }
197
+
198
+ /**
199
+ * Setup listener for when modal opens to clear input
200
+ */
201
+ setupModalOpenListener() {
202
+ // Listen for clicks on search trigger buttons
203
+ const searchTriggers = document.querySelectorAll("#document-search, #document-search-container");
204
+ searchTriggers.forEach((trigger) => {
205
+ trigger.addEventListener("click", () => {
206
+ // Clear input when modal opens via click and show recent searches
207
+ setTimeout(() => {
208
+ if (this.searchInput) {
209
+ this.searchInput.value = "";
210
+ this.searchInput.focus();
211
+ // Show recent searches when input is cleared
212
+ this.showRecentSearches();
213
+ }
214
+ }, 100);
215
+ });
216
+ });
217
+ }
218
+
219
+ /**
220
+ * Setup event listeners for search functionality
221
+ */
222
+ setupEventListeners() {
223
+ // Handle search input
224
+ this.searchInput.addEventListener("input", (e) => {
225
+ const query = e.target.value.trim();
226
+ this.handleSearchInput(query);
227
+ });
228
+
229
+ // Handle keyboard events
230
+ this.searchInput.addEventListener("keydown", (e) => {
231
+ if (e.key === "Enter") {
232
+ e.preventDefault();
233
+ this.handleEnterKey();
234
+ } else if (e.key === "ArrowDown") {
235
+ e.preventDefault();
236
+ this.navigateSelection(1);
237
+ } else if (e.key === "ArrowUp") {
238
+ e.preventDefault();
239
+ this.navigateSelection(-1);
240
+ } else if (e.key === "Escape") {
241
+ // Let Alpine.js handle the escape key
242
+ return;
243
+ }
244
+ });
245
+
246
+ // Handle result clicks
247
+ this.searchResults.addEventListener("click", (e) => {
248
+ const resultItem = e.target.closest("[data-url]");
249
+ if (resultItem) {
250
+ const url = resultItem.getAttribute("data-url");
251
+ // Save to history before navigating
252
+ this.addToSearchHistory(this.searchInput.value.trim());
253
+ this.navigateToUrl(url);
254
+ return;
255
+ }
256
+
257
+ // Handle search history item clicks
258
+ const historyItem = e.target.closest("[data-query]");
259
+ if (historyItem) {
260
+ const query = historyItem.getAttribute("data-query");
261
+ this.searchInput.value = query;
262
+ this.performSearch(query, true); // Save to history when clicking history item
263
+ }
264
+ });
265
+ }
266
+
267
+ /**
268
+ * Handle search input with debouncing
269
+ */
270
+ handleSearchInput(query) {
271
+ clearTimeout(this.searchTimeout);
272
+ clearTimeout(this.saveTimeout);
273
+
274
+ // Quick search for live results
275
+ this.searchTimeout = setTimeout(() => {
276
+ if (query.length >= 2) {
277
+ this.performSearch(query, false); // Don't save to history on live search
278
+ } else {
279
+ this.showRecentSearches();
280
+ }
281
+ }, 300);
282
+
283
+ // Longer timeout to save to history if user stops typing
284
+ if (query.length >= 2) {
285
+ this.saveTimeout = setTimeout(() => {
286
+ this.addToSearchHistory(query);
287
+ }, 2000); // Save to history after 2 seconds of no typing
288
+ }
289
+ }
290
+
291
+ /**
292
+ * Handle Enter key press
293
+ */
294
+ handleEnterKey() {
295
+ const query = this.searchInput.value.trim();
296
+
297
+ // If we have a selected item, navigate to it (regardless of input length)
298
+ if (this.selectedIndex >= 0 && this.selectedIndex < this.selectableItems.length) {
299
+ const selectedItem = this.selectableItems[this.selectedIndex];
300
+ const url = selectedItem.getAttribute("data-url");
301
+ const historyQuery = selectedItem.getAttribute("data-query");
302
+
303
+ if (url) {
304
+ this.addToSearchHistory(query);
305
+ this.navigateToUrl(url);
306
+ return;
307
+ } else if (historyQuery) {
308
+ // Handle history item selection
309
+ this.searchInput.value = historyQuery;
310
+ this.performSearch(historyQuery, true);
311
+ return;
312
+ }
313
+ }
314
+
315
+ // Only check for minimum query length if no item is selected
316
+ if (query.length < 2) {
317
+ return; // Don't do anything for short queries
318
+ }
319
+
320
+ // Fallback: check if there are search results
321
+ const firstResult = this.searchResults.querySelector("[data-url]");
322
+ if (firstResult) {
323
+ // Navigate to first result and save to history
324
+ const url = firstResult.getAttribute("data-url");
325
+ this.addToSearchHistory(query);
326
+ this.navigateToUrl(url);
327
+ } else {
328
+ // No results, just save the search to history
329
+ this.addToSearchHistory(query);
330
+ // Optionally show a "no results" message
331
+ this.displayResults([], query);
332
+ }
333
+ }
334
+
335
+ /**
336
+ * Navigate selection with arrow keys
337
+ */
338
+ navigateSelection(direction) {
339
+ if (this.selectableItems.length === 0) {
340
+ return;
341
+ }
342
+
343
+ // Update selected index
344
+ this.selectedIndex += direction;
345
+
346
+ // Wrap around
347
+ if (this.selectedIndex < 0) {
348
+ this.selectedIndex = this.selectableItems.length - 1;
349
+ } else if (this.selectedIndex >= this.selectableItems.length) {
350
+ this.selectedIndex = 0;
351
+ }
352
+
353
+ // Update visual selection
354
+ this.updateSelection();
355
+ }
356
+
357
+ /**
358
+ * Update visual selection highlighting
359
+ */
360
+ updateSelection() {
361
+ // Remove all selection classes
362
+ this.selectableItems.forEach((item) => {
363
+ item.classList.remove("search-result-selected");
364
+ });
365
+
366
+ // Add selection class to current item
367
+ if (this.selectedIndex >= 0 && this.selectedIndex < this.selectableItems.length) {
368
+ const selectedItem = this.selectableItems[this.selectedIndex];
369
+ selectedItem.classList.add("search-result-selected");
370
+
371
+ // Scroll the selected item into view
372
+ selectedItem.scrollIntoView({
373
+ behavior: "smooth",
374
+ block: "nearest",
375
+ inline: "nearest",
376
+ });
377
+ }
378
+ }
379
+
380
+ /**
381
+ * Reset selection
382
+ */
383
+ resetSelection() {
384
+ this.selectedIndex = -1;
385
+ this.selectableItems = [];
386
+ }
387
+
388
+ /**
389
+ * Navigate to a URL, handling current page case by closing modal
390
+ */
391
+ navigateToUrl(url) {
392
+ const currentUrl = window.location.pathname + window.location.search + window.location.hash;
393
+
394
+ // If the URL is exactly the same as current page (including hash), close the modal
395
+ if (url === currentUrl || url === window.location.href) {
396
+ this.closeModal();
397
+ } else {
398
+ // Check if it's the same page but with a different hash fragment
399
+ const currentPathAndQuery = window.location.pathname + window.location.search;
400
+ const targetPathAndQuery = url.split("#")[0]; // Remove hash for comparison
401
+
402
+ if (currentPathAndQuery === targetPathAndQuery) {
403
+ // Same page but different hash - navigate to the new hash and close modal
404
+ window.location.href = url;
405
+ // Close modal after navigation
406
+ setTimeout(() => this.closeModal(), 100);
407
+ } else {
408
+ // Different page - navigate normally
409
+ window.location.href = url;
410
+ }
411
+ }
412
+ }
413
+
414
+ /**
415
+ * Close the search modal
416
+ */
417
+ closeModal() {
418
+ const searchModal = document.getElementById("document-search-modal");
419
+ if (searchModal && window.Alpine) {
420
+ // Find the parent Alpine.js component that contains headerSearchShow
421
+ // The headerSearchShow variable is in the header component, not the modal
422
+ let currentElement = searchModal;
423
+ let alpineData = null;
424
+
425
+ // Walk up the DOM tree to find the Alpine.js component with headerSearchShow
426
+ while (currentElement && currentElement !== document.body) {
427
+ alpineData = Alpine.$data(currentElement);
428
+ if (alpineData && typeof alpineData.headerSearchShow !== "undefined") {
429
+ alpineData.headerSearchShow = false;
430
+ return;
431
+ }
432
+ currentElement = currentElement.parentElement;
433
+ }
434
+
435
+ // Fallback: try to find any Alpine.js component with headerSearchShow
436
+ if (!alpineData || typeof alpineData.headerSearchShow === "undefined") {
437
+ const allElements = document.querySelectorAll("[x-data]");
438
+ for (const element of allElements) {
439
+ const data = Alpine.$data(element);
440
+ if (data && typeof data.headerSearchShow !== "undefined") {
441
+ data.headerSearchShow = false;
442
+ return;
443
+ }
444
+ }
445
+ }
446
+ }
447
+ }
448
+
449
+ /**
450
+ * Perform search and display results
451
+ */
452
+ performSearch(query, saveToHistory = false) {
453
+ const results = performSearch(query, { limit: 10 });
454
+ this.displayResults(results, query);
455
+
456
+ // Only add to search history if explicitly requested
457
+ if (saveToHistory) {
458
+ this.addToSearchHistory(query);
459
+ }
460
+ }
461
+
462
+ /**
463
+ * Load search history from localStorage
464
+ */
465
+ loadSearchHistory() {
466
+ try {
467
+ const history = localStorage.getItem(this.historyStorageKey);
468
+ return history ? JSON.parse(history) : [];
469
+ } catch (error) {
470
+ console.warn("Failed to load search history:", error);
471
+ return [];
472
+ }
473
+ }
474
+
475
+ /**
476
+ * Save search history to localStorage
477
+ */
478
+ saveSearchHistory() {
479
+ try {
480
+ localStorage.setItem(this.historyStorageKey, JSON.stringify(this.searchHistory));
481
+ } catch (error) {
482
+ console.warn("Failed to save search history:", error);
483
+ }
484
+ }
485
+
486
+ /**
487
+ * Add a search query to history
488
+ */
489
+ addToSearchHistory(query) {
490
+ if (!query || query.trim().length < 2) return;
491
+
492
+ const trimmedQuery = query.trim();
493
+
494
+ // Remove if already exists
495
+ this.searchHistory = this.searchHistory.filter((item) => item.query !== trimmedQuery);
496
+
497
+ // Add to beginning
498
+ this.searchHistory.unshift({
499
+ query: trimmedQuery,
500
+ timestamp: Date.now(),
501
+ });
502
+
503
+ // Keep only the most recent items
504
+ this.searchHistory = this.searchHistory.slice(0, this.maxHistoryItems);
505
+
506
+ // Save to localStorage
507
+ this.saveSearchHistory();
508
+ }
509
+
510
+ /**
511
+ * Clear search history
512
+ */
513
+ clearSearchHistory() {
514
+ this.searchHistory = [];
515
+ this.saveSearchHistory();
516
+ }
517
+
518
+ /**
519
+ * Display search results
520
+ */
521
+ displayResults(results, query) {
522
+ // Reset selection when displaying new results
523
+ this.resetSelection();
524
+
525
+ if (results.length === 0) {
526
+ this.searchResults.innerHTML = `
527
+ <div class="search-no-results">
528
+ <div class="search-no-results-title">No results found for "${query}"</div>
529
+ <div class="search-no-results-subtitle">Try different keywords or check your spelling</div>
530
+ </div>
531
+ `;
532
+ return;
533
+ }
534
+
535
+ const resultsHTML = `
536
+ <div class="search-results-header">Search results (${results.length})</div>
537
+ ${results.map((result) => this.createResultHTML(result, query)).join("")}
538
+ `;
539
+
540
+ this.searchResults.innerHTML = resultsHTML;
541
+
542
+ // Set up selectable items for keyboard navigation
543
+ this.selectableItems = Array.from(this.searchResults.querySelectorAll("[data-url]"));
544
+
545
+ // Set first item as selected by default
546
+ if (this.selectableItems.length > 0) {
547
+ this.selectedIndex = 0;
548
+ this.updateSelection();
549
+ }
550
+ }
551
+
552
+ /**
553
+ * Create HTML for a single search result
554
+ */
555
+ createResultHTML(result, query = "") {
556
+ const breadcrumb = this.createBreadcrumb(result);
557
+ const labels = this.createLabels(result);
558
+
559
+ // Highlight matching keywords in title and summary
560
+ const highlightedTitle = this.highlightKeywords(result.title, query);
561
+ const highlightedSummary = this.highlightKeywords(result.content, query);
562
+
563
+ return `
564
+ <div class="searched-item" data-url="${result.url}">
565
+ <i class="search-result-icon"></i>
566
+ <div class="search-result-content">
567
+ <div class="search-result-breadcrumb">
568
+ ${breadcrumb}
569
+ ${labels}
570
+ </div>
571
+ <div class="search-result-title">${highlightedTitle}</div>
572
+ <div class="search-result-summary">${highlightedSummary}</div>
573
+ </div>
574
+ <i class="search-result-goto-icon"></i>
575
+ </div>
576
+ `;
577
+ }
578
+
579
+ createLabels(result) {
580
+ if (!result.labels || result.labels.length === 0) {
581
+ return "";
582
+ }
583
+
584
+ return `
585
+ <span class="search-result-labels">${result.labels.map((label) => `<span class="search-label">${label}</span>`).join("")}</span>
586
+ `;
587
+ }
588
+
589
+ /**
590
+ * Create breadcrumb navigation for search result
591
+ */
592
+ createBreadcrumb(result) {
593
+ if (!result.pageTypeName) {
594
+ return "";
595
+ }
596
+
597
+ return `
598
+ <div class="search-result-breadcrumb">
599
+ <span>${result.pageTypeName}</span>
600
+ </div>
601
+ `;
602
+ }
603
+
604
+ /**
605
+ * Highlight matching keywords in text
606
+ */
607
+ highlightKeywords(text, query) {
608
+ if (!query || !text) return text;
609
+
610
+ // Split query into individual words and filter out common words
611
+ const queryWords = query
612
+ .toLowerCase()
613
+ .split(/\s+/)
614
+ .filter((word) => word.length > 2) // Only highlight words longer than 2 characters
615
+ .filter(
616
+ (word) =>
617
+ ![
618
+ "the",
619
+ "and",
620
+ "for",
621
+ "are",
622
+ "but",
623
+ "not",
624
+ "you",
625
+ "all",
626
+ "can",
627
+ "had",
628
+ "her",
629
+ "was",
630
+ "one",
631
+ "our",
632
+ "out",
633
+ "day",
634
+ "get",
635
+ "has",
636
+ "him",
637
+ "his",
638
+ "how",
639
+ "its",
640
+ "may",
641
+ "new",
642
+ "now",
643
+ "old",
644
+ "see",
645
+ "two",
646
+ "way",
647
+ "who",
648
+ "boy",
649
+ "did",
650
+ "man",
651
+ "oil",
652
+ "sit",
653
+ "try",
654
+ "use",
655
+ ].includes(word),
656
+ );
657
+
658
+ if (queryWords.length === 0) {
659
+ return text;
660
+ }
661
+
662
+ // Create regex pattern to match any of the query words
663
+ const pattern = new RegExp(`(${queryWords.map((word) => this.escapeRegExp(word)).join("|")})`, "gi");
664
+
665
+ // Replace matches with highlighted spans
666
+ return text.replace(pattern, '<span class="search-highlight">$1</span>');
667
+ }
668
+
669
+ /**
670
+ * Escape special regex characters
671
+ */
672
+ escapeRegExp(string) {
673
+ return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
674
+ }
675
+
676
+ /**
677
+ * Show recent searches from history
678
+ */
679
+ showRecentSearches() {
680
+ // Reset selection when showing recent searches
681
+ this.resetSelection();
682
+
683
+ if (this.searchHistory.length === 0) {
684
+ this.searchResults.innerHTML = `
685
+ <div class="search-recent-header">Recent searches</div>
686
+ <div class="search-no-results">
687
+ <div class="search-no-results-subtitle">No recent searches</div>
688
+ </div>
689
+ `;
690
+ return;
691
+ }
692
+
693
+ const historyHTML = this.searchHistory
694
+ .map((item) => {
695
+ const timeAgo = this.getTimeAgo(item.timestamp);
696
+ return `
697
+ <div class="searched-item search-history-item" data-query="${item.query}">
698
+ <i class="search-history-icon"></i>
699
+ <div class="search-result-content">
700
+ <div class="search-result-title">${item.query}</div>
701
+ <div class="search-result-summary">Searched ${timeAgo}</div>
702
+ </div>
703
+ <i class="search-result-goto-icon"></i>
704
+ </div>
705
+ `;
706
+ })
707
+ .join("");
708
+
709
+ this.searchResults.innerHTML = `
710
+ <div class="search-recent-header">Recent searches</div>
711
+ ${historyHTML}
712
+ `;
713
+
714
+ // Set up selectable items for keyboard navigation (history items)
715
+ this.selectableItems = Array.from(this.searchResults.querySelectorAll("[data-query]"));
716
+
717
+ // Set first item as selected by default
718
+ if (this.selectableItems.length > 0) {
719
+ this.selectedIndex = 0;
720
+ this.updateSelection();
721
+ }
722
+ }
723
+
724
+ /**
725
+ * Get human-readable time ago string
726
+ */
727
+ getTimeAgo(timestamp) {
728
+ const now = Date.now();
729
+ const diff = now - timestamp;
730
+ const minutes = Math.floor(diff / 60000);
731
+ const hours = Math.floor(diff / 3600000);
732
+ const days = Math.floor(diff / 86400000);
733
+
734
+ if (minutes < 1) {
735
+ return "just now";
736
+ }
737
+ if (minutes < 60) {
738
+ return `${minutes} minute${minutes === 1 ? "" : "s"} ago`;
739
+ }
740
+ if (hours < 24) {
741
+ return `${hours} hour${hours === 1 ? "" : "s"} ago`;
742
+ }
743
+
744
+ return `${days} day${days === 1 ? "" : "s"} ago`;
745
+ }
746
+ }
747
+
748
+ // Global search UI instance with configurable options
749
+ const searchUI = new SearchUI({
750
+ maxHistoryItems: 7, // Number of recent searches to keep
751
+ historyStorageKey: "leedSearchHistory", // localStorage key
752
+ });
753
+
754
+ // Export functions for use in other scripts
755
+ if (typeof module !== "undefined" && module.exports) {
756
+ module.exports = {
757
+ initializeSearch,
758
+ performSearch,
759
+ getSearchSuggestions,
760
+ searchUI,
761
+ };
762
+ }
763
+
764
+ // Auto-initialize search when DOM is ready
765
+ if (typeof document !== "undefined") {
766
+ function initializeSearchUI() {
767
+ // Initialize the search index first
768
+ initializeSearch().then((success) => {
769
+ if (success) {
770
+ // Then initialize the UI
771
+ searchUI.initialize();
772
+
773
+ // Only add global Ctrl+K handler if search initialization was successful
774
+ document.addEventListener(
775
+ "keydown",
776
+ (e) => {
777
+ if (e.ctrlKey && e.key === "k") {
778
+ e.preventDefault();
779
+ e.stopPropagation();
780
+
781
+ // Trigger the search modal if it exists
782
+ const searchModal = document.getElementById("document-search-modal");
783
+ if (searchModal && window.Alpine) {
784
+ // Find the parent Alpine.js component that contains headerSearchShow
785
+ let currentElement = searchModal;
786
+ let alpineData = null;
787
+
788
+ // Walk up the DOM tree to find the Alpine.js component with headerSearchShow
789
+ while (currentElement && currentElement !== document.body) {
790
+ alpineData = Alpine.$data(currentElement);
791
+ if (alpineData && typeof alpineData.headerSearchShow !== "undefined") {
792
+ alpineData.headerSearchShow = true;
793
+ // Clear the search input when opening the modal and show recent searches
794
+ setTimeout(() => {
795
+ const searchInput = document.querySelector('#document-search-modal input[type="text"]');
796
+ if (searchInput) {
797
+ searchInput.value = "";
798
+ searchInput.focus();
799
+ // Show recent searches when input is cleared
800
+ searchUI.showRecentSearches();
801
+ }
802
+ }, 50);
803
+ return;
804
+ }
805
+ currentElement = currentElement.parentElement;
806
+ }
807
+
808
+ // Fallback: try to find any Alpine.js component with headerSearchShow
809
+ if (!alpineData || typeof alpineData.headerSearchShow === "undefined") {
810
+ const allElements = document.querySelectorAll("[x-data]");
811
+ for (const element of allElements) {
812
+ const data = Alpine.$data(element);
813
+ if (data && typeof data.headerSearchShow !== "undefined") {
814
+ data.headerSearchShow = true;
815
+ // Clear the search input when opening the modal and show recent searches
816
+ setTimeout(() => {
817
+ const searchInput = document.querySelector('#document-search-modal input[type="text"]');
818
+ if (searchInput) {
819
+ searchInput.value = "";
820
+ searchInput.focus();
821
+ // Show recent searches when input is cleared
822
+ searchUI.showRecentSearches();
823
+ }
824
+ }, 50);
825
+ return;
826
+ }
827
+ }
828
+ }
829
+ }
830
+ }
831
+ },
832
+ true,
833
+ ); // Use capture phase to intercept before other handlers
834
+ }
835
+ });
836
+ }
837
+
838
+ if (document.readyState === "loading") {
839
+ document.addEventListener("DOMContentLoaded", initializeSearchUI);
840
+ } else {
841
+ initializeSearchUI();
842
+ }
843
+ }