From beb98471fde1811ad1af55bcc0bc45ed068a19ce Mon Sep 17 00:00:00 2001 From: Louisa Chu Date: Mon, 25 May 2026 13:48:15 -0700 Subject: [PATCH 01/35] fix highlight attributes --- hlx_statics/components/code.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/hlx_statics/components/code.js b/hlx_statics/components/code.js index 9033e010f..caf41b7b9 100644 --- a/hlx_statics/components/code.js +++ b/hlx_statics/components/code.js @@ -106,7 +106,7 @@ function applyDataAttributesFromCodeClasses(pre, code) { parts.forEach((item) => { if (!item.includes('language-') && item.includes('=')) { - const match = item.match(/^-?([^=]+)="?([^"]*)"?$/); + const match = item.match(/^-?([^=]+)="([^"]*)"/); if (match) { const attrName = `data-${match[1]}`; const attrValue = match[2]; @@ -119,7 +119,10 @@ function applyDataAttributesFromCodeClasses(pre, code) { code.classList.remove(cls); pre.classList.remove(cls); if (languagePart) { - const cleanClass = languagePart.trim(); + let cleanClass = languagePart.trim(); + if (cls.includes('disableLineNumbers') && !cleanClass.includes('disableLineNumbers')) { + cleanClass += '-disableLineNumbers'; + } code.classList.add(cleanClass); pre.classList.add(cleanClass); } From 6babb8c8ff83b39e2ddb8f2e3f98ddcd9a8c0349 Mon Sep 17 00:00:00 2001 From: BaskarMitrah Date: Wed, 27 May 2026 10:55:33 +0530 Subject: [PATCH 02/35] fix : info card text overflow issue --- hlx_statics/blocks/info-card/info-card.css | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/hlx_statics/blocks/info-card/info-card.css b/hlx_statics/blocks/info-card/info-card.css index 710be2862..566cb821c 100644 --- a/hlx_statics/blocks/info-card/info-card.css +++ b/hlx_statics/blocks/info-card/info-card.css @@ -36,6 +36,14 @@ main :is(div.info-card-wrapper div.info-card, div.infocard-wrapper div.infocard) background-color: white; } +main :is(div.info-card-wrapper div.info-card, div.infocard-wrapper div.infocard) .cards-card-body > :is(h1, h2, h3, h4, h5, h6) { + overflow: hidden; + display: -webkit-box; + -webkit-box-orient: vertical; + line-clamp: 2; + -webkit-line-clamp: 2; +} + main :is(div.info-card-wrapper div.info-card, div.infocard-wrapper div.infocard) .cards-card-image { line-height: 0; } @@ -73,6 +81,14 @@ main :is(div.info-card-wrapper div.info-card, div.infocard-wrapper div.infocard) color: rgb(0, 0, 0); } +main :is(div.info-card-wrapper div.info-card, div.infocard-wrapper div.infocard) .cards-card-body > p { + overflow: hidden; + display: -webkit-box; + -webkit-box-orient: vertical; + line-clamp: 3; + -webkit-line-clamp: 3; +} + main :is(div.info-card-wrapper div.info-card, div.infocard-wrapper div.infocard) p { color: rgb(71, 71, 71) !important; } From d21914f3b4f3b779291c16a276557c6f32518e2b Mon Sep 17 00:00:00 2001 From: BaskarMitrah Date: Wed, 27 May 2026 12:14:43 +0530 Subject: [PATCH 03/35] fix : unquoted src and alt attributes in superhero video tag --- hlx_statics/blocks/superhero/superhero.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hlx_statics/blocks/superhero/superhero.js b/hlx_statics/blocks/superhero/superhero.js index e01a9b3f2..70cfc514f 100644 --- a/hlx_statics/blocks/superhero/superhero.js +++ b/hlx_statics/blocks/superhero/superhero.js @@ -136,7 +136,7 @@ async function decorateDevBizHalfWidth(block) { const muted = !isControl || wantAutoplay; const videoContainer = createTag('div', { class: 'superhero-video-container' }); - const videoTag = ``; + const videoTag = ``; videoContainer.innerHTML = videoTag; block.lastElementChild.replaceWith(videoContainer); } From 33743ec20453b038d02181401057add02d13b5dd Mon Sep 17 00:00:00 2001 From: BaskarMitrah Date: Wed, 27 May 2026 12:29:27 +0530 Subject: [PATCH 04/35] fix : malformed title attributes in embed block --- hlx_statics/blocks/embed/embed.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/hlx_statics/blocks/embed/embed.js b/hlx_statics/blocks/embed/embed.js index 170614bf8..7b978c82f 100644 --- a/hlx_statics/blocks/embed/embed.js +++ b/hlx_statics/blocks/embed/embed.js @@ -35,7 +35,7 @@ const getDefaultEmbed = (url, loop, controls, vidTitle, isShort, autoplay) => { params.push('mute=1'); } const query = params.length ? `?${params.join('&')}` : ''; - const titleAttr = vidTitle ? `title="${vidTitle}"` : `title="Content from ${url.hostname}"`; + const titleAttr = `title="${vidTitle ? vidTitle : `Content from ${url.hostname}`}"`; const embedHTML = `
`; loadScript("https://www.instagram.com/embed.js"); @@ -97,7 +97,7 @@ const embedYTPlaylist = (url, loop, controls, vidTitle, isShort, autoplay) => { const embedHTML = `
`; return embedHTML; @@ -106,7 +106,7 @@ const embedTikTok = (url, loop, controls, vidTitle, isShort, autoplay) => { const [, vidID] = url.pathname.split('video/') return `
`; } @@ -170,7 +170,7 @@ const embedVimeo = (url, loop, controls, vidTitle, isShort, autoplay) => { style="border: 0; top: 0; left: 0; width: 100%; height: 100%; position: absolute;" frameborder="0" allow="fullscreen; encrypted-media; accelerometer; gyroscope; picture-in-picture" allowfullscreen - ${vidTitle ? `title=${vidTitle}` : `title="Content from" ${url.hostname}`} loading="lazy"> + title="${vidTitle ? vidTitle : `Content from ${url.hostname}`}" loading="lazy"> `; return embedHTML; }; From 2dbc419f7e9f3ac78dc82728b44b2170b7b637c9 Mon Sep 17 00:00:00 2001 From: David Sima Date: Tue, 19 May 2026 11:28:57 +0300 Subject: [PATCH 05/35] feat(ai-assistants): implement analytics --- .../ai-assistant/ai-assistant_chat-bubble.js | 16 ++++++++++--- .../ai-assistant/ai-assistant_chat-ui.js | 23 +++++++++++++++---- .../ai-assistant_suggested-questions.js | 8 +++++-- 3 files changed, 38 insertions(+), 9 deletions(-) diff --git a/hlx_statics/blocks/ai-assistant/ai-assistant_chat-bubble.js b/hlx_statics/blocks/ai-assistant/ai-assistant_chat-bubble.js index face1a4e9..bb8bf7df1 100644 --- a/hlx_statics/blocks/ai-assistant/ai-assistant_chat-bubble.js +++ b/hlx_statics/blocks/ai-assistant/ai-assistant_chat-bubble.js @@ -40,7 +40,10 @@ export class ChatBubble { */ #_init() { const bubble = createTag("div", { class: "chat-bubble" }); - const contentElement = createTag("div", { class: "chat-bubble-content" }); + const contentElement = createTag("div", { + class: "chat-bubble-content", + "daa-lh": "AI Assistant - Message bubble", + }); if (this.source === "ai") { if (!this.isContinuingConversation) { @@ -111,6 +114,7 @@ export class ChatBubble { class: "chat-bubble-copy", type: "button", "aria-label": COPY_BUTTON_LABEL, + "daa-ll": COPY_BUTTON_LABEL, }) ); button.innerHTML = COPY_ICON_SVG; @@ -180,6 +184,7 @@ export class ChatBubble { class: "chat-bubble-feedback", type: "button", "aria-label": THUMB_UP_LABEL, + "daa-ll": THUMB_UP_LABEL, }) ); thumbUpButton.innerHTML = THUMB_UP_ICON_SVG; @@ -192,6 +197,7 @@ export class ChatBubble { class: "chat-bubble-feedback", type: "button", "aria-label": THUMB_DOWN_LABEL, + "daa-ll": THUMB_DOWN_LABEL, }) ); thumbDownButton.innerHTML = THUMB_DOWN_ICON_SVG; @@ -324,7 +330,10 @@ export class ChatBubble { this.references = references; - const wrapper = createTag("div", { class: "chat-bubble-sources" }); + const wrapper = createTag("div", { + class: "chat-bubble-sources", + "daa-lh": "AI Assistant - Message sources", + }); const heading = createTag("p", { class: "chat-bubble-sources-heading" }); heading.textContent = "Sources:"; wrapper.appendChild(heading); @@ -338,10 +347,11 @@ export class ChatBubble { rel: "noopener noreferrer", }); a.textContent = title || url; + a.setAttribute("daa-ll", a.textContent); li.appendChild(a); list.appendChild(li); }); wrapper.appendChild(list); this.element.appendChild(wrapper); } -} \ No newline at end of file +} diff --git a/hlx_statics/blocks/ai-assistant/ai-assistant_chat-ui.js b/hlx_statics/blocks/ai-assistant/ai-assistant_chat-ui.js index 997bcc138..cfa84b235 100644 --- a/hlx_statics/blocks/ai-assistant/ai-assistant_chat-ui.js +++ b/hlx_statics/blocks/ai-assistant/ai-assistant_chat-ui.js @@ -29,7 +29,10 @@ export const createAiAvatar = () => { * Creates the chat window header */ export const createChatWindowHeader = () => { - const chatWindowHeader = createTag("header", { class: "chat-window-header" }); + const chatWindowHeader = createTag("header", { + class: "chat-window-header", + "daa-lh": "AI Assistant - Window header", + }); chatWindowHeader.appendChild(createAiAvatar()); const label = createTag("h2", { class: "chat-window-label", @@ -40,6 +43,7 @@ export const createChatWindowHeader = () => { class: "chat-window-clear", type: "button", "aria-label": CHAT_BUTTON_LABEL_CLEAR, + "daa-ll": CHAT_BUTTON_LABEL_CLEAR, }); const clearButtonIcon = createTag("img", { src: "/hlx_statics/icons/delete.svg", @@ -52,6 +56,7 @@ export const createChatWindowHeader = () => { class: "chat-window-close", type: "button", "aria-label": CHAT_BUTTON_LABEL_CLOSE, + "daa-ll": CHAT_BUTTON_LABEL_CLOSE, }); const closeButtonIcon = createTag("img", { src: "/hlx_statics/icons/dismiss.svg", @@ -72,7 +77,10 @@ export const createChatWindowHeader = () => { * Creates the input section */ export const createInputSection = () => { - const inputSection = createTag("div", { class: "chat-window-input-section" }); + const inputSection = createTag("div", { + class: "chat-window-input-section", + "daa-lh": "AI Assistant - Input section", + }); const textarea = /** @type {HTMLTextAreaElement} */ ( createTag("textarea", { placeholder: "Type your message...", @@ -81,13 +89,14 @@ export const createInputSection = () => { }) ); const disclaimerText = createTag("div", { class: "chat-disclaimer-text" }); - disclaimerText.innerHTML = `By using AI Assistant, you agree to the Generative AI User Guidelines.`; + disclaimerText.innerHTML = `By using AI Assistant, you agree to the Generative AI User Guidelines.`; const sendButton = /** @type {HTMLButtonElement} */ ( createTag("button", { class: "chat-send-button", type: "button", "aria-label": "Send message", + "daa-ll": "Send message", }) ); const sendButtonIcon = createTag("img", { @@ -142,6 +151,7 @@ export const createChatButton = () => { "aria-expanded": "false", "aria-haspopup": "dialog", "aria-label": CHAT_BUTTON_LABEL_OPEN, + "daa-ll": CHAT_BUTTON_LABEL_OPEN, }); chatButton.innerHTML = ``; ELEMENTS.CHAT_BUTTON = chatButton; @@ -151,7 +161,10 @@ export const createChatButton = () => { export const createClearDialog = () => { const dialog = createTag("div", { class: "chat-window-dialog" }); - const card = createTag("section", { class: "chat-window-dialog-card" }); + const card = createTag("section", { + class: "chat-window-dialog-card", + "daa-lh": "AI Assistant - Clear dialog", + }); const heading = createTag("h2", { class: "chat-window-dialog-heading" }); heading.textContent = "Clear conversation"; @@ -167,6 +180,7 @@ export const createClearDialog = () => { const cancelButton = createTag("button", { class: "chat-window-dialog-cancel", type: "button", + "daa-ll": "Cancel", }); cancelButton.textContent = "Cancel"; cancelButton.addEventListener("click", () => dialog.remove()); @@ -174,6 +188,7 @@ export const createClearDialog = () => { const clearButton = createTag("button", { class: "chat-window-dialog-clear", type: "button", + "daa-ll": "Clear", }); clearButton.textContent = "Clear"; clearButton.addEventListener("click", () => { diff --git a/hlx_statics/blocks/ai-assistant/ai-assistant_suggested-questions.js b/hlx_statics/blocks/ai-assistant/ai-assistant_suggested-questions.js index 23cb262f0..44eb694b6 100644 --- a/hlx_statics/blocks/ai-assistant/ai-assistant_suggested-questions.js +++ b/hlx_statics/blocks/ai-assistant/ai-assistant_suggested-questions.js @@ -57,6 +57,7 @@ export const updateSuggestedQuestions = (questions) => { const button = createTag("button", { type: "button", class: "chat-suggested-questions-button", + "daa-ll": label, }); const icon = createTag("img", { src: "/hlx_statics/icons/arrow-curved.svg", @@ -97,7 +98,10 @@ export const createSuggestedQuestionsSection = () => { const wrapper = createTag("div", { class: "chat-suggested-questions" }); const title = createTag("p", { class: "chat-suggested-questions-title" }); title.textContent = "or choose from the following:"; - const list = createTag("div", { class: "chat-suggested-questions-list" }); + const list = createTag("div", { + class: "chat-suggested-questions-list", + "daa-lh": "AI Assistant - Suggested questions", + }); wrapper.appendChild(title); wrapper.appendChild(list); @@ -126,4 +130,4 @@ export const hideSuggestedQuestions = () => { el.classList.remove("animate-fade-in"); el.classList.add("hidden"); } -}; \ No newline at end of file +}; From fe41511a6d1b2abacb6cd0580cd544914195992e Mon Sep 17 00:00:00 2001 From: David Sima Date: Tue, 19 May 2026 12:18:53 +0300 Subject: [PATCH 06/35] feat(ai-assistant): update link rendering to include tracking attributes --- .../blocks/ai-assistant/ai-assistant_chat-bubble.js | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/hlx_statics/blocks/ai-assistant/ai-assistant_chat-bubble.js b/hlx_statics/blocks/ai-assistant/ai-assistant_chat-bubble.js index bb8bf7df1..358aa40d6 100644 --- a/hlx_statics/blocks/ai-assistant/ai-assistant_chat-bubble.js +++ b/hlx_statics/blocks/ai-assistant/ai-assistant_chat-bubble.js @@ -255,7 +255,18 @@ export class ChatBubble { // @ts-expect-error - DOMPurify is not on the Window object contentElement.innerHTML = window.DOMPurify.sanitize( // @ts-expect-error - marked is not on the Window object - window.marked.parse(this.content), + window.marked.parse(this.content, { + renderer: { + /** + * @param {string} href + * @param {string} title + * @param {string} text + */ + link(href, title, text) { + return `${text}`; + }, + }, + }), ); } } From 052c79800ad47e9071e461eddf2f0a2c1f1605ef Mon Sep 17 00:00:00 2001 From: David Sima Date: Tue, 19 May 2026 12:19:32 +0300 Subject: [PATCH 07/35] feat(ai-assistant): update clear button's daa-ll to describe action --- hlx_statics/blocks/ai-assistant/ai-assistant_chat-ui.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hlx_statics/blocks/ai-assistant/ai-assistant_chat-ui.js b/hlx_statics/blocks/ai-assistant/ai-assistant_chat-ui.js index cfa84b235..2b0049dd7 100644 --- a/hlx_statics/blocks/ai-assistant/ai-assistant_chat-ui.js +++ b/hlx_statics/blocks/ai-assistant/ai-assistant_chat-ui.js @@ -43,7 +43,7 @@ export const createChatWindowHeader = () => { class: "chat-window-clear", type: "button", "aria-label": CHAT_BUTTON_LABEL_CLEAR, - "daa-ll": CHAT_BUTTON_LABEL_CLEAR, + "daa-ll": "Open clear dialog", }); const clearButtonIcon = createTag("img", { src: "/hlx_statics/icons/delete.svg", From 9fa0a8f67be113d79311951a60dd69643ff666d2 Mon Sep 17 00:00:00 2001 From: David <136443815+davids-ensemble@users.noreply.github.com> Date: Thu, 28 May 2026 17:05:45 +0300 Subject: [PATCH 08/35] fix(ai-assistant): add noopener noreferrer to marked links Co-authored-by: melissag-ensemble <120194874+melissag-ensemble@users.noreply.github.com> --- hlx_statics/blocks/ai-assistant/ai-assistant_chat-bubble.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hlx_statics/blocks/ai-assistant/ai-assistant_chat-bubble.js b/hlx_statics/blocks/ai-assistant/ai-assistant_chat-bubble.js index 358aa40d6..c1fbe154e 100644 --- a/hlx_statics/blocks/ai-assistant/ai-assistant_chat-bubble.js +++ b/hlx_statics/blocks/ai-assistant/ai-assistant_chat-bubble.js @@ -263,7 +263,7 @@ export class ChatBubble { * @param {string} text */ link(href, title, text) { - return `${text}`; + return `${text}`; }, }, }), From 125518209c0a9b76df2b0931616cc8d3553c4231 Mon Sep 17 00:00:00 2001 From: David Sima Date: Tue, 19 May 2026 16:29:45 +0300 Subject: [PATCH 09/35] feat(ai-assistant): prevent auto-scroll when user scrolls up --- .../blocks/ai-assistant/ai-assistant.js | 2 + .../ai-assistant_chat-controller.js | 63 +++++++++++++++++-- .../ai-assistant_suggested-questions.js | 14 ++++- 3 files changed, 71 insertions(+), 8 deletions(-) diff --git a/hlx_statics/blocks/ai-assistant/ai-assistant.js b/hlx_statics/blocks/ai-assistant/ai-assistant.js index 959ff83b7..776d5c4ad 100644 --- a/hlx_statics/blocks/ai-assistant/ai-assistant.js +++ b/hlx_statics/blocks/ai-assistant/ai-assistant.js @@ -6,6 +6,7 @@ import { import { aiApiClient } from "./ai-assistant_api-client.js"; import { + onUserScroll, restoreChatHistory, toggleChatWindow, } from "./ai-assistant_chat-controller.js"; @@ -67,6 +68,7 @@ export default async function decorate(block) { block.appendChild(panel); + ELEMENTS.CHAT_WINDOW_CONTENT?.addEventListener("scroll", onUserScroll); ELEMENTS.CHAT_BUTTON?.addEventListener("click", toggleChatWindow); ELEMENTS.CHAT_WINDOW_CLEAR_BUTTON?.addEventListener("click", () => chatWindow.appendChild(createClearDialog()), diff --git a/hlx_statics/blocks/ai-assistant/ai-assistant_chat-controller.js b/hlx_statics/blocks/ai-assistant/ai-assistant_chat-controller.js index 2d81fd163..91d42651c 100644 --- a/hlx_statics/blocks/ai-assistant/ai-assistant_chat-controller.js +++ b/hlx_statics/blocks/ai-assistant/ai-assistant_chat-controller.js @@ -18,6 +18,34 @@ import { updateSuggestedQuestions, } from "./ai-assistant_suggested-questions.js"; +let userScrolledUp = false; +let lastScrollTop = 0; + +/** + * Handles scroll events in the chat window to detect when the user scrolls up or back to the bottom. + * Sets/resets a flag to pause or resume auto-scrolling during streaming. + * Uses scroll direction rather than absolute position so programmatic downward scrolls + * during streaming don't mask the user's upward intent. + * + * @param {Event} event - The scroll event from the chat container element + */ +export const onUserScroll = (event) => { + if (event.type !== "scroll" || !event.target) { + return; + } + const container = /** @type {HTMLDivElement} */ (event.target); + const distanceFromBottom = + container.scrollHeight - container.clientHeight - container.scrollTop; + const scrolledUp = container.scrollTop < lastScrollTop; + lastScrollTop = container.scrollTop; + + if (userScrolledUp && distanceFromBottom < 10) { + userScrolledUp = false; + } else if (!userScrolledUp && scrolledUp && distanceFromBottom > 100) { + userScrolledUp = true; + } +}; + /** * @param {Partial<{delay: number}>} [options] */ @@ -166,6 +194,8 @@ export const handleUserQuery = async ( messageContentOverride, collectionId = null, ) => { + userScrolledUp = false; + lastScrollTop = 0; let messageContent = messageContentOverride; const textarea = /** @type {HTMLTextAreaElement} */ (ELEMENTS.CHAT_TEXTAREA); @@ -222,7 +252,10 @@ export const handleUserQuery = async ( targetBubble.hideThinking(); targetBubble.showStreamingCursor(); targetBubble.updateContent(responseContent); - targetBubble.scrollIntoView(); + if (!userScrolledUp && ELEMENTS.CHAT_WINDOW_CONTENT) { + ELEMENTS.CHAT_WINDOW_CONTENT.scrollTop = + ELEMENTS.CHAT_WINDOW_CONTENT.scrollHeight; + } } }, onCitation: (data) => { @@ -245,7 +278,10 @@ export const handleUserQuery = async ( content: responseContent, references, }); - targetBubble.scrollIntoView(); + if (!userScrolledUp && ELEMENTS.CHAT_WINDOW_CONTENT) { + ELEMENTS.CHAT_WINDOW_CONTENT.scrollTop = + ELEMENTS.CHAT_WINDOW_CONTENT.scrollHeight; + } } } }, @@ -256,7 +292,11 @@ export const handleUserQuery = async ( responseContent = "_Response stopped by user._"; targetBubble.updateContent(responseContent); updateSuggestedQuestions(await getCollectionsQuestions()); - window.setTimeout(showSuggestedQuestions, suggestedQuestionsDelayMs); + window.setTimeout( + () => + showSuggestedQuestions({ shouldScrollIntoView: !userScrolledUp }), + suggestedQuestionsDelayMs, + ); return; } targetBubble.completeBubble(); @@ -264,10 +304,17 @@ export const handleUserQuery = async ( content: responseContent, references: accumulatedReferences, }); - targetBubble.scrollIntoView(); + if (!userScrolledUp && ELEMENTS.CHAT_WINDOW_CONTENT) { + ELEMENTS.CHAT_WINDOW_CONTENT.scrollTop = + ELEMENTS.CHAT_WINDOW_CONTENT.scrollHeight; + } updateSuggestedQuestions(null); - window.setTimeout(showSuggestedQuestions, suggestedQuestionsDelayMs); + window.setTimeout( + () => + showSuggestedQuestions({ shouldScrollIntoView: !userScrolledUp }), + suggestedQuestionsDelayMs, + ); await fetchAiSuggestedQuestions(); }, onError: (error) => { @@ -276,7 +323,11 @@ export const handleUserQuery = async ( console.error("[AI Assistant] Error:", error); showErrorMessage(); getCollectionsQuestions().then(updateSuggestedQuestions); - window.setTimeout(showSuggestedQuestions, suggestedQuestionsDelayMs); + window.setTimeout( + () => + showSuggestedQuestions({ shouldScrollIntoView: !userScrolledUp }), + suggestedQuestionsDelayMs, + ); }, }, }); diff --git a/hlx_statics/blocks/ai-assistant/ai-assistant_suggested-questions.js b/hlx_statics/blocks/ai-assistant/ai-assistant_suggested-questions.js index 44eb694b6..58778be56 100644 --- a/hlx_statics/blocks/ai-assistant/ai-assistant_suggested-questions.js +++ b/hlx_statics/blocks/ai-assistant/ai-assistant_suggested-questions.js @@ -112,14 +112,24 @@ export const createSuggestedQuestionsSection = () => { return wrapper; }; -export const showSuggestedQuestions = () => { +/** + * Shows the suggested questions section with optional scroll behavior. + * @param {Object} [options={}] - Options object + * @param {boolean} [options.shouldScrollIntoView=true] - Whether to scroll the element into view + */ +export const showSuggestedQuestions = ({ + shouldScrollIntoView = true, +} = {}) => { const el = ELEMENTS.CHAT_SUGGESTED_QUESTIONS; if (el) { el.classList.remove("hidden"); el.classList.remove("animate-fade-in"); requestAnimationFrame(() => { el.classList.add("animate-fade-in"); - el.scrollIntoView({ behavior: "smooth" }); + if (shouldScrollIntoView && ELEMENTS.CHAT_WINDOW_CONTENT) { + ELEMENTS.CHAT_WINDOW_CONTENT.scrollTop = + ELEMENTS.CHAT_WINDOW_CONTENT.scrollHeight; + } }); } }; From 8e85164d338d14e59e80073aeb98c5a3df5f0a6e Mon Sep 17 00:00:00 2001 From: David Sima Date: Wed, 20 May 2026 16:26:17 +0300 Subject: [PATCH 10/35] feat(ai-assistant): added prod url --- .../ai-assistant/ai-assistant_api-client.js | 23 +++++++++++++------ .../ai-assistant/ai-assistant_constants.js | 6 +---- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/hlx_statics/blocks/ai-assistant/ai-assistant_api-client.js b/hlx_statics/blocks/ai-assistant/ai-assistant_api-client.js index b8ce6def3..a05c3ab7c 100644 --- a/hlx_statics/blocks/ai-assistant/ai-assistant_api-client.js +++ b/hlx_statics/blocks/ai-assistant/ai-assistant_api-client.js @@ -1,5 +1,6 @@ // @ts-check -import { AI_API_BASE_URL, AI_API_KEY } from "./ai-assistant_constants.js"; + +import { isProdEnvironment } from "../../scripts/lib-adobeio.js"; /** * @typedef {Object} RequestBody @@ -87,11 +88,19 @@ import { AI_API_BASE_URL, AI_API_KEY } from "./ai-assistant_constants.js"; * @property {(error: unknown) => void} onError */ +const STAGE_API_URL = + "https://devsite-rag.stg.app-builder.corp.adp.adobe.io/v1/inference"; +const PROD_API_URL = + "https://devsite-rag.app-builder.adp.adobe.io/v1/inference"; +const API_KEY = "ai-assistant-devsite-rag-demo-01"; +const PROD_API_KEY = "test-prod-rag-devsite"; +const IS_PROD = isProdEnvironment(window.location.host); + export class AiApiClient { - static STREAMING_ENDPOINT = "/v1/inference/retrieve/generate/stream"; - static NON_STREAMING_ENDPOINT = "/v1/inference/retrieve/generate"; - static COLLECTIONS_ENDPOINT = "/v1/inference/collections"; - static FEEDBACK_ENDPOINT = "/v1/inference/feedback"; + static STREAMING_ENDPOINT = "/retrieve/generate/stream"; + static NON_STREAMING_ENDPOINT = "/retrieve/generate"; + static COLLECTIONS_ENDPOINT = "/collections"; + static FEEDBACK_ENDPOINT = "/feedback"; static LOCAL_STORAGE_COLLECTIONS_KEY = "ai-assistant__collections"; static LOCAL_STORAGE_COLLECTION_TTL = 1 * 24 * 60 * 60 * 1000; // 1 day @@ -414,6 +423,6 @@ export class AiApiClient { } export const aiApiClient = new AiApiClient({ - baseUrl: AI_API_BASE_URL, - apiKey: AI_API_KEY, + baseUrl: IS_PROD ? PROD_API_URL : STAGE_API_URL, + apiKey: IS_PROD ? PROD_API_KEY : API_KEY, }); diff --git a/hlx_statics/blocks/ai-assistant/ai-assistant_constants.js b/hlx_statics/blocks/ai-assistant/ai-assistant_constants.js index 5095d4fc2..fb6090b1d 100644 --- a/hlx_statics/blocks/ai-assistant/ai-assistant_constants.js +++ b/hlx_statics/blocks/ai-assistant/ai-assistant_constants.js @@ -1,8 +1,4 @@ // @ts-check -/** TODO: This should be different based on the environment */ -export const AI_API_BASE_URL = - "https://devsite-rag.stg.app-builder.corp.adp.adobe.io"; -export const AI_API_KEY = "ai-assistant-devsite-rag-demo-01"; export const CHAT_BUTTON_LABEL_OPEN = "Open AI Assistant"; export const CHAT_BUTTON_LABEL_CLOSE = "Close AI Assistant"; export const CHAT_BUTTON_LABEL_MINIMIZE = "Minimize AI Assistant"; @@ -38,4 +34,4 @@ export const FALLBACK_SUGGESTED_QUESTIONS = [ export const GENERIC_ERROR_MESSAGE = "Sorry, I encountered an error. Please try again later."; export const SEND_ICON_SRC = "/hlx_statics/icons/send-message.svg"; -export const STOP_ICON_SRC = "/hlx_statics/icons/stop-response.svg"; \ No newline at end of file +export const STOP_ICON_SRC = "/hlx_statics/icons/stop-response.svg"; From c6897ad04020112d32d684ed608266a78ea9e22a Mon Sep 17 00:00:00 2001 From: David Sima Date: Tue, 26 May 2026 15:27:07 +0300 Subject: [PATCH 11/35] feat(ai-assistant): added prod api key --- hlx_statics/blocks/ai-assistant/ai-assistant_api-client.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hlx_statics/blocks/ai-assistant/ai-assistant_api-client.js b/hlx_statics/blocks/ai-assistant/ai-assistant_api-client.js index a05c3ab7c..9d0df8d74 100644 --- a/hlx_statics/blocks/ai-assistant/ai-assistant_api-client.js +++ b/hlx_statics/blocks/ai-assistant/ai-assistant_api-client.js @@ -93,7 +93,7 @@ const STAGE_API_URL = const PROD_API_URL = "https://devsite-rag.app-builder.adp.adobe.io/v1/inference"; const API_KEY = "ai-assistant-devsite-rag-demo-01"; -const PROD_API_KEY = "test-prod-rag-devsite"; +const PROD_API_KEY = "devsite-rag"; const IS_PROD = isProdEnvironment(window.location.host); export class AiApiClient { From 3445f8a2ad7ed5ed8ce425ae37f9c89137ba8c2d Mon Sep 17 00:00:00 2001 From: David Sima Date: Tue, 26 May 2026 15:32:20 +0300 Subject: [PATCH 12/35] Rename `API_KEY` to `STAGE_API_KEY` for clarity and consistency --- hlx_statics/blocks/ai-assistant/ai-assistant_api-client.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hlx_statics/blocks/ai-assistant/ai-assistant_api-client.js b/hlx_statics/blocks/ai-assistant/ai-assistant_api-client.js index 9d0df8d74..1bfa0d076 100644 --- a/hlx_statics/blocks/ai-assistant/ai-assistant_api-client.js +++ b/hlx_statics/blocks/ai-assistant/ai-assistant_api-client.js @@ -92,7 +92,7 @@ const STAGE_API_URL = "https://devsite-rag.stg.app-builder.corp.adp.adobe.io/v1/inference"; const PROD_API_URL = "https://devsite-rag.app-builder.adp.adobe.io/v1/inference"; -const API_KEY = "ai-assistant-devsite-rag-demo-01"; +const STAGE_API_KEY = "ai-assistant-devsite-rag-demo-01"; const PROD_API_KEY = "devsite-rag"; const IS_PROD = isProdEnvironment(window.location.host); @@ -424,5 +424,5 @@ export class AiApiClient { export const aiApiClient = new AiApiClient({ baseUrl: IS_PROD ? PROD_API_URL : STAGE_API_URL, - apiKey: IS_PROD ? PROD_API_KEY : API_KEY, + apiKey: IS_PROD ? PROD_API_KEY : STAGE_API_KEY, }); From 4012cd1d5e173ba7156723e060c3ea6c4cd30f6f Mon Sep 17 00:00:00 2001 From: David Sima Date: Tue, 26 May 2026 17:06:23 +0300 Subject: [PATCH 13/35] feat(ai-assistant): add Beta badge and widen chat window to 460px --- .../blocks/ai-assistant/ai-assistant.css | 21 +++++++++++++++---- .../ai-assistant/ai-assistant_chat-ui.js | 6 ++++++ 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/hlx_statics/blocks/ai-assistant/ai-assistant.css b/hlx_statics/blocks/ai-assistant/ai-assistant.css index ec2a89801..1e42e5816 100644 --- a/hlx_statics/blocks/ai-assistant/ai-assistant.css +++ b/hlx_statics/blocks/ai-assistant/ai-assistant.css @@ -100,7 +100,7 @@ } &.show { - width: 400px; + width: 460px; height: 100%; border-radius: 8px; visibility: visible; @@ -151,8 +151,21 @@ margin: 0; font-size: 18px; font-weight: 700; - flex: 1; - margin-left: 12px; + margin-inline: 12px; + } + + .chat-window-badge { + box-sizing: border-box; + display: flex; + align-items: center; + justify-content: center; + padding: 2px 11px; + background-color: rgb(255, 232, 240); + color: black; + border-radius: 7px; + font-size: 12px; + min-height: 24px; + line-height: 14px; } .chat-window-close, @@ -734,4 +747,4 @@ max-width: 400px; } } -} \ No newline at end of file +} diff --git a/hlx_statics/blocks/ai-assistant/ai-assistant_chat-ui.js b/hlx_statics/blocks/ai-assistant/ai-assistant_chat-ui.js index 2b0049dd7..bfff6451a 100644 --- a/hlx_statics/blocks/ai-assistant/ai-assistant_chat-ui.js +++ b/hlx_statics/blocks/ai-assistant/ai-assistant_chat-ui.js @@ -39,6 +39,10 @@ export const createChatWindowHeader = () => { id: CHAT_WINDOW_LABEL_ID, }); label.textContent = "Adobe Developer AI assistant"; + + const betaBadge = createTag("div", { class: "chat-window-badge" }); + betaBadge.textContent = "Beta"; + const clearButton = createTag("button", { class: "chat-window-clear", type: "button", @@ -66,6 +70,8 @@ export const createChatWindowHeader = () => { closeButton.appendChild(closeButtonIcon); chatWindowHeader.appendChild(label); + chatWindowHeader.append(betaBadge); + chatWindowHeader.appendChild(createTag("div", { style: "flex: 1;" })); chatWindowHeader.appendChild(clearButton); chatWindowHeader.appendChild(closeButton); ELEMENTS.CHAT_WINDOW_CLEAR_BUTTON = clearButton; From edf7572ba4c936575e6e32af93fa93b2cb794481 Mon Sep 17 00:00:00 2001 From: David Sima Date: Fri, 29 May 2026 12:34:54 +0300 Subject: [PATCH 14/35] fix(ai-assistant): fixed marked crashing because of undefined renderer --- hlx_statics/blocks/ai-assistant/ai-assistant.js | 14 ++++++++++++++ .../ai-assistant/ai-assistant_chat-bubble.js | 13 +------------ 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/hlx_statics/blocks/ai-assistant/ai-assistant.js b/hlx_statics/blocks/ai-assistant/ai-assistant.js index 776d5c4ad..8253480b8 100644 --- a/hlx_statics/blocks/ai-assistant/ai-assistant.js +++ b/hlx_statics/blocks/ai-assistant/ai-assistant.js @@ -35,6 +35,20 @@ export default async function decorate(block) { document.body, "https://unpkg.com/marked@^17/lib/marked.umd.js", () => { + // @ts-expect-error - marked is not on the Window object + window.marked.use({ + renderer: { + /** + * @param {Object} options + * @param {string} options.href + * @param {string} options.title + * @param {string} options.text + */ + link({ href, title, text }) { + return `${text}`; + }, + }, + }); addExtraScriptWithLoad( document.body, "https://unpkg.com/dompurify@^3/dist/purify.min.js", diff --git a/hlx_statics/blocks/ai-assistant/ai-assistant_chat-bubble.js b/hlx_statics/blocks/ai-assistant/ai-assistant_chat-bubble.js index c1fbe154e..bb8bf7df1 100644 --- a/hlx_statics/blocks/ai-assistant/ai-assistant_chat-bubble.js +++ b/hlx_statics/blocks/ai-assistant/ai-assistant_chat-bubble.js @@ -255,18 +255,7 @@ export class ChatBubble { // @ts-expect-error - DOMPurify is not on the Window object contentElement.innerHTML = window.DOMPurify.sanitize( // @ts-expect-error - marked is not on the Window object - window.marked.parse(this.content, { - renderer: { - /** - * @param {string} href - * @param {string} title - * @param {string} text - */ - link(href, title, text) { - return `${text}`; - }, - }, - }), + window.marked.parse(this.content), ); } } From 13b7f09f055c37ebeb6df7d36591639689688b3e Mon Sep 17 00:00:00 2001 From: David Sima Date: Fri, 29 May 2026 12:56:55 +0300 Subject: [PATCH 15/35] feat(ai-assistant): new panel animation --- .../blocks/ai-assistant/ai-assistant.css | 62 +++++++------------ 1 file changed, 21 insertions(+), 41 deletions(-) diff --git a/hlx_statics/blocks/ai-assistant/ai-assistant.css b/hlx_statics/blocks/ai-assistant/ai-assistant.css index 1e42e5816..5dd65ee41 100644 --- a/hlx_statics/blocks/ai-assistant/ai-assistant.css +++ b/hlx_statics/blocks/ai-assistant/ai-assistant.css @@ -25,6 +25,7 @@ ); --container-padding: 40px; --button-size: 48px; + --opening-transition-duration: 0.25s; display: flex; flex-direction: column-reverse; @@ -51,6 +52,9 @@ var(--ai-border-linear-gradient) border-box; align-self: flex-end; pointer-events: auto; + visibility: visible; + transition: visibility 0s linear + calc(var(--opening-transition-duration) * 0.9); &:hover { --background-color: #f0f0f0; @@ -58,6 +62,7 @@ &.hidden { visibility: hidden; + transition: visibility 0s linear; } } @@ -65,13 +70,11 @@ * MARK: Chat Window */ .ai-assistant-panel .chat-window { - --transition-duration: 0.25s; - width: var(--button-size); - height: var(--button-size); - border-radius: calc(var(--button-size) / 2); - background: - linear-gradient(#ffffff, #ffffff) padding-box, - var(--ai-border-linear-gradient) border-box; + --chat-window-width: 460px; + width: var(--chat-window-width); + height: 100%; + border-radius: 8px; + background: #fff; /* Drop shadow/elevated (pretty much copy pasta from Figma) */ box-shadow: 0 0 2px 0 rgba(0, 0, 0, 0.12), @@ -80,48 +83,25 @@ display: flex; flex-direction: column; overflow: hidden; - transform: translateY(var(--button-size)); + transform: scale(0); + transform-origin: calc(100% - var(--button-size) / 2) + calc(100% - var(--button-size) / 2); + translate: 0 var(--button-size); visibility: hidden; - border: solid transparent; - border-width: 2px; + border: 0; transition: - width var(--transition-duration) ease-out var(--transition-duration), - height var(--transition-duration) ease-out var(--transition-duration), - border-radius var(--transition-duration) ease-out, - border-width var(--transition-duration) linear, - visibility 0s linear calc(var(--transition-duration) * 2); - - .chat-window-header, - .chat-window-content, - .chat-window-input-section { - opacity: 0; - transition: opacity var(--transition-duration) ease-out 0s; - } + transform var(--opening-transition-duration) cubic-bezier(0.5, 0, 1, 1), + visibility 0s linear var(--opening-transition-duration); &.show { - width: 460px; - height: 100%; - border-radius: 8px; visibility: visible; transition: - width var(--transition-duration) ease-out, - height var(--transition-duration) ease-out, - border-radius var(--transition-duration) ease-out - var(--transition-duration), - border-width var(--transition-duration) linear - var(--transition-duration), - visibility 0s linear 0s; + transform var(--opening-transition-duration) + cubic-bezier(0, 0, 0.4, 1), + visibility 0s linear; pointer-events: auto; - border-width: 0px; - - .chat-window-header, - .chat-window-content, - .chat-window-input-section { - opacity: 1; - transition: opacity var(--transition-duration) ease-out - calc(var(--transition-duration) * 2); - } + transform: scale(1); } .chat-ai-avatar { From d3d55d210811a7e7d7c1750f8b445ec4e0d64ce6 Mon Sep 17 00:00:00 2001 From: yuxuanj Date: Tue, 26 May 2026 15:09:26 -0700 Subject: [PATCH 16/35] hide OnThisPage option --- hlx_statics/scripts/scripts.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hlx_statics/scripts/scripts.js b/hlx_statics/scripts/scripts.js index 0358d0009..108646f43 100644 --- a/hlx_statics/scripts/scripts.js +++ b/hlx_statics/scripts/scripts.js @@ -770,7 +770,8 @@ async function loadLazy(doc) { const hasResources = Boolean(document.querySelector('.resources-wrapper')); const hasCredential = Boolean(document.querySelector('.getcredential-wrapper')); const hasHeading = main.querySelectorAll('h2:not(.side-nav h2):not(footer h2), h3:not(.side-nav h3):not(footer h3)').length !== 0; - const hasOnThisPage = !hasHero && hasHeading && !hasCredential; + const noToc = document.querySelector('meta[name="no-toc"]')?.content === 'true'; + const hasOnThisPage = !hasHero && hasHeading && !hasCredential && !noToc; const hasAside = hasOnThisPage || hasResources; if (hasAside) { From c7ef245bfaa41528ad8e296340763c331934ba9a Mon Sep 17 00:00:00 2001 From: yuxuanj Date: Wed, 27 May 2026 10:17:54 -0700 Subject: [PATCH 17/35] update naming noOnThisPage --- hlx_statics/scripts/scripts.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hlx_statics/scripts/scripts.js b/hlx_statics/scripts/scripts.js index 108646f43..0d187d5c6 100644 --- a/hlx_statics/scripts/scripts.js +++ b/hlx_statics/scripts/scripts.js @@ -770,8 +770,8 @@ async function loadLazy(doc) { const hasResources = Boolean(document.querySelector('.resources-wrapper')); const hasCredential = Boolean(document.querySelector('.getcredential-wrapper')); const hasHeading = main.querySelectorAll('h2:not(.side-nav h2):not(footer h2), h3:not(.side-nav h3):not(footer h3)').length !== 0; - const noToc = document.querySelector('meta[name="no-toc"]')?.content === 'true'; - const hasOnThisPage = !hasHero && hasHeading && !hasCredential && !noToc; + const noOnThisPage = document.querySelector('meta[name="noonthispage"]')?.content === 'true'; + const hasOnThisPage = !hasHero && hasHeading && !hasCredential && !noOnThisPage; const hasAside = hasOnThisPage || hasResources; if (hasAside) { From a97aabdee332e447e8e9facf62406875a6154efa Mon Sep 17 00:00:00 2001 From: yuxuanj Date: Wed, 27 May 2026 10:30:29 -0700 Subject: [PATCH 18/35] update naming --- hlx_statics/scripts/scripts.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hlx_statics/scripts/scripts.js b/hlx_statics/scripts/scripts.js index 0d187d5c6..11de7d00f 100644 --- a/hlx_statics/scripts/scripts.js +++ b/hlx_statics/scripts/scripts.js @@ -770,8 +770,8 @@ async function loadLazy(doc) { const hasResources = Boolean(document.querySelector('.resources-wrapper')); const hasCredential = Boolean(document.querySelector('.getcredential-wrapper')); const hasHeading = main.querySelectorAll('h2:not(.side-nav h2):not(footer h2), h3:not(.side-nav h3):not(footer h3)').length !== 0; - const noOnThisPage = document.querySelector('meta[name="noonthispage"]')?.content === 'true'; - const hasOnThisPage = !hasHero && hasHeading && !hasCredential && !noOnThisPage; + const hideOnThisPage = document.querySelector('meta[name="hideonthispage"]')?.content === 'true'; + const hasOnThisPage = !hasHero && hasHeading && !hasCredential && !hideOnThisPage; const hasAside = hasOnThisPage || hasResources; if (hasAside) { From 742492b4c802b741c3f6d0ac5fe65cb5fd4f67de Mon Sep 17 00:00:00 2001 From: yuxuanj Date: Thu, 28 May 2026 13:38:41 -0700 Subject: [PATCH 19/35] exclude block headings from OnThisPage sidebar --- hlx_statics/blocks/onthispage/onthispage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hlx_statics/blocks/onthispage/onthispage.js b/hlx_statics/blocks/onthispage/onthispage.js index 6ada92ebe..1eea26d55 100644 --- a/hlx_statics/blocks/onthispage/onthispage.js +++ b/hlx_statics/blocks/onthispage/onthispage.js @@ -16,7 +16,7 @@ export default async function decorate(block) { block.append(aside); const mainContainer = document.querySelector('main'); - const headings = mainContainer.querySelectorAll('h2:not(.side-nav h2):not(footer h2), h3:not(.side-nav h3):not(footer h3)'); + const headings = mainContainer.querySelectorAll('.heading2:not(.side-nav .heading2):not(footer .heading2) h2, .heading3:not(.side-nav .heading3):not(footer .heading3) h3'); Object.assign(aside.style, { display: 'flex', From 435193e403fe949ce8098b68004b6884087a7ca1 Mon Sep 17 00:00:00 2001 From: Louisa Chu Date: Tue, 2 Jun 2026 13:50:41 -0700 Subject: [PATCH 20/35] update edition color --- hlx_statics/blocks/edition/edition.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/hlx_statics/blocks/edition/edition.js b/hlx_statics/blocks/edition/edition.js index 0bc4f687f..d1491ff85 100644 --- a/hlx_statics/blocks/edition/edition.js +++ b/hlx_statics/blocks/edition/edition.js @@ -7,12 +7,13 @@ export default async function decorate(block) { const colorMap = { 'red': 'rgb(187, 2, 2)', 'green': 'rgb(0, 128, 0)', - 'blue': 'rgb(4, 105, 227)' + 'blue': 'rgb(4, 105, 227)', + 'grey': 'rgb(71,71,71)' }; // Get background color from data attribute or class name let requestedColor = block.getAttribute('data-backgroundcolor')?.toLowerCase(); - + // If no data attribute, check for class name like 'background-color-blue' if (!requestedColor) { const classList = Array.from(block.classList); @@ -21,7 +22,7 @@ export default async function decorate(block) { requestedColor = colorClass.replace('background-color-', ''); } } - + const backgroundColor = colorMap[requestedColor] || colorMap['red']; block.querySelectorAll('.edition > div > div').forEach((div) => { From f8a880f4bf9502307d4155e205d0b09e6c28df40 Mon Sep 17 00:00:00 2001 From: Louisa Chu Date: Tue, 2 Jun 2026 14:12:43 -0700 Subject: [PATCH 21/35] rename the color --- hlx_statics/blocks/edition/edition.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hlx_statics/blocks/edition/edition.js b/hlx_statics/blocks/edition/edition.js index d1491ff85..6a44dc806 100644 --- a/hlx_statics/blocks/edition/edition.js +++ b/hlx_statics/blocks/edition/edition.js @@ -8,7 +8,7 @@ export default async function decorate(block) { 'red': 'rgb(187, 2, 2)', 'green': 'rgb(0, 128, 0)', 'blue': 'rgb(4, 105, 227)', - 'grey': 'rgb(71,71,71)' + 'gray': 'rgb(71,71,71)' }; // Get background color from data attribute or class name From 9bfd5445ae6780ea5558b79d9276cf619091d043 Mon Sep 17 00:00:00 2001 From: yuxuanj Date: Wed, 27 May 2026 16:55:59 -0700 Subject: [PATCH 22/35] fixed vertical tab block decoration --- hlx_statics/blocks/tab/tab.css | 133 +++++++++++++++++++++++++++++++-- hlx_statics/blocks/tab/tab.js | 19 ++++- 2 files changed, 142 insertions(+), 10 deletions(-) diff --git a/hlx_statics/blocks/tab/tab.css b/hlx_statics/blocks/tab/tab.css index 812fd96b2..69c2d663e 100644 --- a/hlx_statics/blocks/tab/tab.css +++ b/hlx_statics/blocks/tab/tab.css @@ -123,6 +123,10 @@ main div.tab-wrapper .code-toolbar { main div.tab-wrapper .tab.vertical { flex-direction: row; + align-items: stretch; + width: 100%; + margin: 0; + gap: 0; } main div.tab-wrapper .tab.vertical .tabs-wrapper, @@ -130,14 +134,55 @@ main div.tab-wrapper .tab.horizontal { flex-direction: column; } -main div.tab-wrapper .tab.vertical>.tabs-wrapper { +main div.tab-wrapper .tab.vertical > .tabs-wrapper { display: flex; flex-direction: column; - width: 350px; + width: 240px; + min-width: 180px; + flex-shrink: 0; + gap: 4px; + padding: 16px 12px; + margin-bottom: 0; + box-sizing: border-box; + border-right: 1px solid rgba(0, 0, 0, 0.1); +} + +main div.tab-wrapper .tab.vertical > .content-wrapper { + flex: 1; + min-width: 0; + padding: 16px 24px; + width: auto; +} + +main div.tab-wrapper .tab.vertical .tab-button { + width: 100%; + height: auto; + min-height: 54px; + margin: 0; + padding: 10px 14px; + border-radius: 8px; + text-align: left; + white-space: normal; + line-height: 1.35; + font-size: 14px; + justify-content: flex-start; + align-items: center; + gap: 12px; +} + +main div.tab-wrapper .tab.vertical .tab-icon { + width: 36px; + height: 36px; + flex-shrink: 0; + display: flex; + align-items: center; + justify-content: center; } -main div.tab-wrapper .tab.vertical>.content-wrapper { - width: 80%; +main div.tab-wrapper .tab.vertical .tab-icon img { + width: 36px; + height: 36px; + object-fit: contain; } main div.tab-wrapper .tab { @@ -162,10 +207,13 @@ main div.tab-wrapper > div.background-color-navy .tab-button:not(.active) .tab-t } main div.tab-wrapper > div.background-color-navy .tab-button:hover .tab-title { - color: #000; + color: white; +} + +main div.tab-wrapper > div:not(.background-color-navy) .tab-button { + background: #eee; } -main div.tab-wrapper > div:not(.background-color-navy) .tab-button , main div.tab-wrapper > div.background-color-navy .tab-button:hover, main div.tab-wrapper > div.background-color-navy .tab-button.active { background: #eee; @@ -220,4 +268,77 @@ main div.tab-wrapper .sub-tabs-wrapper>pre[class*=language-].line-numbers { main div.tab-wrapper div.tab pre[class*=language-].no-line-numbers .line-highlight { transform: translateY(-1.6em); +} + + +main div.tab-wrapper:has(.tab.vertical.background-color-navy) { + background-color: rgb(15, 55, 95); + padding: 0; +} + +main div.tab-wrapper .tab.vertical.background-color-navy > .tabs-wrapper { + background-color: rgb(15, 55, 95); + border-right-color: rgba(255, 255, 255, 0.15); + padding: 20px 12px; +} + +main div.tab-wrapper .tab.vertical.background-color-navy .tab-title { + color: rgba(255, 255, 255, 0.9); +} + +main div.tab-wrapper .tab.vertical.background-color-navy .tab-button { + background: transparent; + border: none; +} + +main div.tab-wrapper .tab.vertical.background-color-navy .tab-button:hover { + background-color: rgba(255, 255, 255, 0.08); + color: white; +} + +main div.tab-wrapper .tab.vertical.background-color-navy .tab-button.active { + background-color: rgba(255, 255, 255, 0.18); + color: white; +} + +main div.tab-wrapper .tab.vertical.background-color-navy .tab-button.active .tab-title, +main div.tab-wrapper .tab.vertical.background-color-navy .tab-button:hover .tab-title { + color: white; +} + +main div.tab-wrapper .tab.vertical.background-color-navy > .content-wrapper { + background-color: #111a35; + padding: 20px 24px; +} + +@media only screen and (max-width: 860px) { + main div.tab-wrapper .tab.vertical { + flex-direction: column; + } + + main div.tab-wrapper .tab.vertical > .tabs-wrapper { + width: 100%; + min-width: unset; + flex-direction: row; + flex-wrap: wrap; + border-right: none; + border-bottom: 1px solid rgba(0, 0, 0, 0.1); + padding: 8px; + } + + main div.tab-wrapper .tab.vertical.background-color-navy > .tabs-wrapper { + border-bottom-color: rgba(255, 255, 255, 0.15); + border-right: none; + } + + main div.tab-wrapper .tab.vertical .tab-button { + width: auto; + min-height: unset; + white-space: nowrap; + } + + main div.tab-wrapper .tab.vertical > .content-wrapper { + padding: 12px 16px; + width: 100%; + } } \ No newline at end of file diff --git a/hlx_statics/blocks/tab/tab.js b/hlx_statics/blocks/tab/tab.js index 22549b1cf..d45bca55b 100644 --- a/hlx_statics/blocks/tab/tab.js +++ b/hlx_statics/blocks/tab/tab.js @@ -86,11 +86,22 @@ const createSubTabs = (table) => { } export default async function decorate(block) { - let orientation; - if (IS_DEV_DOCS) { - orientation = block.getAttribute('data-orientation') || 'horizontal'; + block.querySelectorAll(':scope > div > div > code').forEach((code) => { + const match = code.textContent.trim().match(/^(data-[^=]+)=(.*)$/); + if (!match) return; + const [, attr, value] = match; + if (attr === 'data-orientation') { + block.setAttribute('data-orientation', value.trim()); + } else if (attr === 'data-classname') { + value.trim().split(/\s+/).filter(Boolean).forEach((cls) => block.classList.add(cls)); + } + }); + + const dataOrientation = block.getAttribute('data-orientation'); + const orientation = dataOrientation || (block.classList.contains('vertical') ? 'vertical' : 'horizontal'); + if (!block.classList.contains(orientation)) { + block.classList.add(orientation); } - block.classList.add(orientation); block.setAttribute('daa-lh', 'tab'); const tabsWrapper = document.createElement('div'); From 743f77b053ea6b6b9fdb2e3f31700d8d7a0af698 Mon Sep 17 00:00:00 2001 From: yuxuanj Date: Thu, 28 May 2026 09:14:23 -0700 Subject: [PATCH 23/35] update query selector --- hlx_statics/blocks/tab/tab.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hlx_statics/blocks/tab/tab.js b/hlx_statics/blocks/tab/tab.js index d45bca55b..2c0addb33 100644 --- a/hlx_statics/blocks/tab/tab.js +++ b/hlx_statics/blocks/tab/tab.js @@ -86,7 +86,7 @@ const createSubTabs = (table) => { } export default async function decorate(block) { - block.querySelectorAll(':scope > div > div > code').forEach((code) => { + block.querySelectorAll(':scope > div > div > pre > code').forEach((code) => { const match = code.textContent.trim().match(/^(data-[^=]+)=(.*)$/); if (!match) return; const [, attr, value] = match; From 9cea319c154b1f1222306c340732c59b9fe7cd48 Mon Sep 17 00:00:00 2001 From: yuxuanj Date: Thu, 28 May 2026 11:43:27 -0700 Subject: [PATCH 24/35] make tab image optional --- hlx_statics/blocks/tab/tab.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hlx_statics/blocks/tab/tab.js b/hlx_statics/blocks/tab/tab.js index 2c0addb33..38216e799 100644 --- a/hlx_statics/blocks/tab/tab.js +++ b/hlx_statics/blocks/tab/tab.js @@ -123,7 +123,7 @@ export default async function decorate(block) { const tabButton = document.createElement('button'); tabButton.className = 'tab-button'; tabButton.innerHTML = ` -
${tabImage}
+ ${tabImage ? `
${tabImage}
` : ''} ${tabTitle} `; tabButton.setAttribute('data-tab', `tab${tabCount}`); From d12e4f1fd0997f85905477aac1d2af0d1a20392f Mon Sep 17 00:00:00 2001 From: melissag-ensemble Date: Tue, 2 Jun 2026 09:44:17 -0700 Subject: [PATCH 25/35] feat: enable text color on non-default superhero variants --- hlx_statics/blocks/superhero/superhero.css | 56 ++++++++++------------ hlx_statics/blocks/superhero/superhero.js | 22 ++++----- 2 files changed, 34 insertions(+), 44 deletions(-) diff --git a/hlx_statics/blocks/superhero/superhero.css b/hlx_statics/blocks/superhero/superhero.css index 05e5911e1..9a685b9d1 100644 --- a/hlx_statics/blocks/superhero/superhero.css +++ b/hlx_statics/blocks/superhero/superhero.css @@ -1,3 +1,28 @@ +main div.superhero-wrapper div.superhero a:not(.spectrum-Button) { + color: inherit !important; + text-decoration: underline; +} + +main div.superhero-wrapper div.superhero.text-color-white, +main div.superhero-wrapper div.superhero.text-color-white :is(h1, h2, h3, h4, h5, h6, p) { + color: rgb(255, 255, 255) !important; +} + +main div.superhero-wrapper div.superhero.text-color-black, +main div.superhero-wrapper div.superhero.text-color-black :is(h1, h2, h3, h4, h5, h6, p) { + color: rgb(0, 0, 0) !important; +} + +main div.superhero-wrapper div.superhero.text-color-gray, +main div.superhero-wrapper div.superhero.text-color-gray :is(h1, h2, h3, h4, h5, h6, p) { + color: rgb(110, 110, 110) !important; +} + +main div.superhero-wrapper div.superhero.text-color-navy, +main div.superhero-wrapper div.superhero.text-color-navy :is(h1, h2, h3, h4, h5, h6, p) { + color: rgb(15, 55, 95) !important; +} + main div.superhero-wrapper div.superhero span.icon { display: inline-block; } @@ -159,7 +184,6 @@ main div.superhero-wrapper div.superhero.half-width > div:last-child video { main div.superhero-wrapper div.superhero.half-width p.spectrum-Body--sizeL { margin-top: 0 !important; - color: rgb(80, 80, 80) !important; } main div.superhero-wrapper:has(div.superhero.half-width) { @@ -208,20 +232,6 @@ main div.superhero-wrapper div.superhero.half-width h1 + p.last-of-type { margin-bottom: 0 !important; } -main div.superhero-wrapper div.superhero.half-width a:not(.spectrum-Button) { - text-decoration: underline; -} - -main div.superhero-wrapper div.superhero.half-width.text-color-white h1, -main div.superhero-wrapper div.superhero.half-width.text-color-white a, -main div.superhero-wrapper div.superhero.half-width.text-color-white p { - color: white !important; -} - -main div.superhero-wrapper div.superhero.half-width.text-color-black { - color: black; -} - main div.superhero-wrapper div.superhero.half-width.over-gradient p.button-container:not(strong) a:not(.spectrum-Button--accent) { border-color: white; } @@ -296,22 +306,6 @@ main div.superhero-wrapper div.superhero.default .all-button-container { gap: 16px; } -main div.superhero-wrapper div.superhero.default.text-color-white { - color: rgb(255, 255, 255); -} - -main div.superhero-wrapper div.superhero.default.text-color-black { - color: rgb(0, 0, 0); -} - -main div.superhero-wrapper div.superhero.default.text-color-gray { - color: rgb(110, 110, 110); -} - -main div.superhero-wrapper div.superhero.default.text-color-navy { - color: rgb(15, 55, 95); -} - @media screen and (max-width: 1280px) { main div.superhero-wrapper div.superhero.default > div > div { width: 100%; diff --git a/hlx_statics/blocks/superhero/superhero.js b/hlx_statics/blocks/superhero/superhero.js index 70cfc514f..34c2dfba3 100644 --- a/hlx_statics/blocks/superhero/superhero.js +++ b/hlx_statics/blocks/superhero/superhero.js @@ -45,6 +45,10 @@ function hasAnyClass(block, classes) { return classes.some((c) => block.classList.contains(c)); } +function getTextColorModifier(block) { + return Object.values(TEXT_COLORS).find((color) => block.classList.contains(`text-color-${color}`)); +} + function unwrapIcons(block) { block.querySelectorAll('span.icon').forEach((span) => { span.textContent = ''; @@ -56,6 +60,7 @@ function unwrapIcons(block) { async function decorateDevBizCentered(block) { const defaultTextColor = TEXT_COLORS.white; + const textColor = getTextColorModifier(block); removeEmptyPTags(block); decorateButtons(block); @@ -69,7 +74,7 @@ async function decorateDevBizCentered(block) { block.classList.add('spectrum--dark'); block.querySelectorAll('h1, h2, h3, h4, h5, h6').forEach((h) => { h.classList.add('spectrum-Heading', 'spectrum-Heading--sizeXXL'); - h.style.color = defaultTextColor; + if (!textColor) h.style.color = defaultTextColor; h.parentElement.classList.add('superhero-content'); h.parentElement.append(button_div); }); @@ -77,7 +82,7 @@ async function decorateDevBizCentered(block) { block.querySelectorAll('p').forEach((p) => { if (!p.classList.contains('icon-container')) { p.classList.add('spectrum-Body', 'spectrum-Body--sizeL'); - p.style.color = defaultTextColor; + if (!textColor) p.style.color = defaultTextColor; } if (p.classList.contains('button-container')) { button_div.append(p); @@ -189,9 +194,8 @@ async function decorateDevBizDefault(block) { div.append(...newChildren); block.replaceChildren(div); - const defaultTextColor = TEXT_COLORS.white; - let textColor = Object.values(TEXT_COLORS).find((color) => block.classList.contains(`text-color-${color}`)) ?? defaultTextColor; - block.classList.add(`text-color-${defaultTextColor}`); + const textColor = getTextColorModifier(block) ?? TEXT_COLORS.white; + block.classList.add(`text-color-${textColor}`); block.querySelectorAll('h1, h2, h3, h4, h5, h6').forEach((h) => { h.style.color = textColor; @@ -356,14 +360,6 @@ function applyDataAttributeStyles(block) { } else { block.style.background = background; } - - const defaultTextColor = variant === VARIANTS.halfWidth ? TEXT_COLORS.black : TEXT_COLORS.white; - const textColor = block.getAttribute('data-textcolor') || defaultTextColor; - if (Object.keys(TEXT_COLORS).includes(textColor)) { - block.querySelectorAll('h1, h2, h3, h4, h5, h6, p').forEach((el) => { - el.style.color = textColor; - }); - } } /** From 0737d5bf7fc58ee2235c1195691c8017c7125e8b Mon Sep 17 00:00:00 2001 From: Diane Batres Date: Tue, 2 Jun 2026 14:50:12 -0400 Subject: [PATCH 26/35] fix: empty search and product checkboxes jumpiness --- hlx_statics/blocks/header/header.js | 54 ++++++++++++++--------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/hlx_statics/blocks/header/header.js b/hlx_statics/blocks/header/header.js index 0fb4842ea..629123a28 100644 --- a/hlx_statics/blocks/header/header.js +++ b/hlx_statics/blocks/header/header.js @@ -70,11 +70,25 @@ async function initSearch() { const { connectAutocomplete } = instantsearch.connectors; - const searchClient = window.algoliasearch.algoliasearch(ALGOLIA_CONFIG.APP_KEY, ALGOLIA_CONFIG.API_KEY); + const algoliaClient = window.algoliasearch.algoliasearch(ALGOLIA_CONFIG.APP_KEY, ALGOLIA_CONFIG.API_KEY); const SUGGESTION_MAX_RESULTS = 50; const SEARCH_MAX_RESULTS = 100; - const SEARCH_MIN_QUERY_LENGTH = 3; - const isSearchableQuery = (q) => q.trim().length >= SEARCH_MIN_QUERY_LENGTH; + const isSearchableQuery = (q) => q.trim() !== ''; + const searchClient = { // "To prevent the initial empty query, you must wrap a custom search client around..." source: https://www.algolia.com/doc/guides/building-search-ui/going-further/conditional-requests/js + ...algoliaClient, + search(requests) { + if (requests.every(({ params }) => !isSearchableQuery(params.query ?? ''))) { + return Promise.resolve({ + results: requests.map(() => ({ + hits: [], nbHits: 0, nbPages: 0, page: 0, + processingTimeMS: 0, hitsPerPage: 0, + exhaustiveNbHits: false, query: '', params: '', + })), + }); + } + return algoliaClient.search(requests); + }, + }; const indices = window.adp_search.indices const indexToProduct = window.adp_search.index_to_product; @@ -230,8 +244,12 @@ async function initSearch() { if (event.key === 'Enter') { searchCleared = false; // Reset cleared flag when user presses Enter const trimmed = searchInput.value.trim(); - if (trimmed !== '' && !isSearchableQuery(trimmed)) { - event.preventDefault(); + if (trimmed === '') { // If users presses enter with an empty query while in a full search, clear results and show suggestions again + searchResults.classList.remove('has-results'); + searchResults.style.visibility = 'hidden'; + outerSearchSuggestions.style.display = 'flex'; + suggestionsFlag = true; + searchExecuted = false; return; } helper.setQuery(searchInput.value).search(); @@ -405,24 +423,7 @@ async function initSearch() { }); } - // Function that is called after each search render to hide/show product checkboxes - function updateCheckboxVisibility(productsWithResults) { - const allSelected = selectedProducts.length === allProducts.length; - - // loop over each product‐wrapper - document.querySelectorAll('.search-checkbox-div[data-product]').forEach((div) => { - const product = div.dataset.product; - if (allSelected) { - // only show those with results - div.style.display = productsWithResults.has(product) ? '' : 'none'; - } else { - // specific‐product mode: show all product checkboxes - div.style.display = ''; - } - }); - } - - // Function that attaches event listeners to each checkbox +// Function that attaches event listeners to each checkbox function attachCheckboxEventListeners() { const allProductsCheckbox = document.getElementById('checkbox-all-products'); const productCheckboxes = document.querySelectorAll('.filters input[type="checkbox"]:not(#checkbox-all-products)'); @@ -447,6 +448,9 @@ async function initSearch() { selectedProducts = Array.from(productCheckboxes) .filter((cb) => cb.checked) // Get checked product checkboxes .map((cb) => cb.value); + if (checkbox.checked) { // Add new selected product to the beginning of the list to prioritize it in results + selectedProducts = [checkbox.value, ...selectedProducts.filter((p) => p !== checkbox.value)]; + } if (selectedProducts.length === 0) { // If no products selected, revert to "All Products" @@ -476,9 +480,6 @@ async function initSearch() { // figure out which products have at least one hit const productsWithResults = new Set(productGroupedResults.keys()); - // hide/show checkboxes based on current results + mode - updateCheckboxVisibility(productsWithResults); - // determine display order const allProductsCheckbox = document.getElementById('checkbox-all-products'); const productsToShow = allProductsCheckbox.checked ? allProducts : selectedProducts; @@ -536,7 +537,6 @@ async function initSearch() { // compute who has any suggestions const productsWithResults = new Set(productGroupedResults.keys()); - updateCheckboxVisibility(productsWithResults); const allProductsCheckbox = document.getElementById('checkbox-all-products'); const productsToShow = allProductsCheckbox.checked From 664f8ca0b236caa5f7ec5b752c19a34e93db5263 Mon Sep 17 00:00:00 2001 From: Diane Batres Date: Tue, 2 Jun 2026 16:20:09 -0400 Subject: [PATCH 27/35] fix: lag in between clicking products and All Products --- hlx_statics/blocks/header/header.js | 52 ++++++++++++++++++----------- 1 file changed, 32 insertions(+), 20 deletions(-) diff --git a/hlx_statics/blocks/header/header.js b/hlx_statics/blocks/header/header.js index 629123a28..037775c2f 100644 --- a/hlx_statics/blocks/header/header.js +++ b/hlx_statics/blocks/header/header.js @@ -139,9 +139,17 @@ async function initSearch() { let results = new Map(); search.start(); + + let currentDynamicWidgets = []; // Widgets that change on each call + let staticWidgetsAdded = false; // One-time widgets // Function to initialize or update the search function updateSearch() { + // Remove widgets from the previous call before adding new ones + if (currentDynamicWidgets.length) { + search.removeWidgets(currentDynamicWidgets); + currentDynamicWidgets = []; + } // Get indices corresponding to selected products const selectedIndices = indices.filter((indexName) => { const product = indexToProduct[indexName]; @@ -157,14 +165,14 @@ async function initSearch() { // Calculate hits dynamically based number of selected indices const hits = Math.min(15, Math.max(4, Math.floor(SUGGESTION_MAX_RESULTS / selectedIndices.length))); - // Add common widgets like hits per index and how long results are (content) - search.addWidgets([ - instantsearch.widgets.configure({ - hitsPerPage: hits, - attributesToHighlight: ['title', 'content'], - attributesToSnippet: ['content:50'], - }), - ]); + // Add common widgets like hits per index and how long results are (content) - and save reference so it can be removed on the next call + const configureWidget = instantsearch.widgets.configure({ + hitsPerPage: hits, + attributesToHighlight: ['title', 'content'], + attributesToSnippet: ['content:50'], + }); + currentDynamicWidgets.push(configureWidget); + search.addWidgets([configureWidget]); // Custom InstantSearch search box to deal with suggestions and full results which depends on user input function customSearchBox() { return { init({ helper }) { @@ -316,21 +324,25 @@ async function initSearch() { } const customMergedHits = connectAutocomplete(mergedHits); - search.addWidgets([ - customSearchBox(), - customMergedHits({ - container: document.querySelector(searchBoxContainer) - }), - ]); - - // Loop through rest of indices - selectedIndices.slice(1).forEach((indexName) => { + // Only add the search box and hits renderer once — fixes event listeners duplicates + if (!staticWidgetsAdded) { search.addWidgets([ - instantsearch.widgets.index({ - indexName: indexName, + customSearchBox(), + customMergedHits({ + container: document.querySelector(searchBoxContainer) }), ]); - }); + staticWidgetsAdded = true; + } + + // Instead of looping through other indices - add a child index widget for rest of indices (the main index is always searched, so it doesn't need a widget) + const indexWidgets = selectedIndices + .filter((indexName) => indexName !== initialIndex) + .map((indexName) => instantsearch.widgets.index({ indexName })); + if (indexWidgets.length) { + currentDynamicWidgets.push(...indexWidgets); + search.addWidgets(indexWidgets); + } search.refresh(); } From ef3ce00c801b21e603ad3436766d33274f6254ea Mon Sep 17 00:00:00 2001 From: Diane Batres Date: Wed, 3 Jun 2026 12:26:19 -0400 Subject: [PATCH 28/35] test: console log message --- hlx_statics/blocks/header/header.js | 55 +++++++++++++++-------------- 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/hlx_statics/blocks/header/header.js b/hlx_statics/blocks/header/header.js index 037775c2f..6ed46db87 100644 --- a/hlx_statics/blocks/header/header.js +++ b/hlx_statics/blocks/header/header.js @@ -253,6 +253,7 @@ async function initSearch() { searchCleared = false; // Reset cleared flag when user presses Enter const trimmed = searchInput.value.trim(); if (trimmed === '') { // If users presses enter with an empty query while in a full search, clear results and show suggestions again + console.log('TEST 1'); searchResults.classList.remove('has-results'); searchResults.style.visibility = 'hidden'; outerSearchSuggestions.style.display = 'flex'; @@ -1130,33 +1131,33 @@ export default async function decorate(block) { // check if documentation template then retrieve from config otherwise default back to google drive path let navPath; - if (IS_DEV_DOCS) { - const topNavHtml = await fetchTopNavHtml(); - if (topNavHtml) { - navigationLinks.innerHTML += topNavHtml; - } - } else { - navPath = cfg.nav || getClosestFranklinSubfolder(window.location.origin,'nav'); - let fragment = await loadFragment(navPath); - if (fragment == null) { - // load the default nav in franklin_assets folder nav - fragment = await loadFragment(getClosestFranklinSubfolder(window.location.origin, 'nav', true)); - } - const ul = fragment.querySelector("ul"); - ul.classList.add("menu"); - ul.setAttribute("id", "navigation-links"); - fragment.querySelectorAll("li").forEach((li, index) => { - if (index == 0) { - if (isTopLevelNav(window.location.pathname)) { - const homeLink = ul.querySelector('li:nth-child(1)'); - homeLink.className = 'navigation-home'; - } else { - li.classList.add("navigation-products"); - } - } - }); - navigationLinks = ul; - } + // if (IS_DEV_DOCS) { + // const topNavHtml = await fetchTopNavHtml(); + // if (topNavHtml) { + // navigationLinks.innerHTML += topNavHtml; + // } + // } else { + // navPath = cfg.nav || getClosestFranklinSubfolder(window.location.origin,'nav'); + // let fragment = await loadFragment(navPath); + // if (fragment == null) { + // // load the default nav in franklin_assets folder nav + // fragment = await loadFragment(getClosestFranklinSubfolder(window.location.origin, 'nav', true)); + // } + // const ul = fragment.querySelector("ul"); + // ul.classList.add("menu"); + // ul.setAttribute("id", "navigation-links"); + // fragment.querySelectorAll("li").forEach((li, index) => { + // if (index == 0) { + // if (isTopLevelNav(window.location.pathname)) { + // const homeLink = ul.querySelector('li:nth-child(1)'); + // homeLink.className = 'navigation-home'; + // } else { + // li.classList.add("navigation-products"); + // } + // } + // }); + // navigationLinks = ul; + // } navigationLinks.querySelectorAll('li > ul').forEach((dropDownList, index) => { let dropdownLinkDropdownHTML = ''; From 3386f55186075d002b3d5a27fc231a48aae8ebdd Mon Sep 17 00:00:00 2001 From: Diane Batres Date: Wed, 3 Jun 2026 12:43:49 -0400 Subject: [PATCH 29/35] fix: add anchor links and results order --- hlx_statics/blocks/header/header.js | 33 +++++++++++++++++++---------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/hlx_statics/blocks/header/header.js b/hlx_statics/blocks/header/header.js index 6ed46db87..e6d92743a 100644 --- a/hlx_statics/blocks/header/header.js +++ b/hlx_statics/blocks/header/header.js @@ -304,7 +304,8 @@ async function initSearch() { // Process each hit // results.set(instantsearch.highlight({ hit, attribute: "title" }), { - url: hit.url, + // Add anchor link if it exists to URL + url: hit.fragment ? `${hit.url}${hit.fragment}` : hit.url, product: hit.product, content: instantsearch.snippet({ hit, attribute: 'content' }), }); @@ -497,11 +498,16 @@ async function initSearch() { const allProductsCheckbox = document.getElementById('checkbox-all-products'); const productsToShow = allProductsCheckbox.checked ? allProducts : selectedProducts; - const productsWith = [], productsWithout = []; - productsToShow.forEach((p) => - productsWithResults.has(p) ? productsWith.push(p) : productsWithout.push(p) - ); - const sorted = [...productsWith, ...productsWithout]; + let sorted; + if (allProductsCheckbox.checked) { + const productsWith = [], productsWithout = []; + productsToShow.forEach((p) => + productsWithResults.has(p) ? productsWith.push(p) : productsWithout.push(p) + ); + sorted = [...productsWith, ...productsWithout]; + } else { + sorted = productsToShow; // preserve selected order regardless of results + } // render each group sorted.forEach((product) => { @@ -556,11 +562,16 @@ async function initSearch() { ? allProducts : selectedProducts; - const withHits = [], withoutHits = []; - productsToShow.forEach((p) => - productsWithResults.has(p) ? withHits.push(p) : withoutHits.push(p) - ); - const sorted = [...withHits, ...withoutHits]; + let sorted; + if (allProductsCheckbox.checked) { + const withHits = [], withoutHits = []; + productsToShow.forEach((p) => + productsWithResults.has(p) ? withHits.push(p) : withoutHits.push(p) + ); + sorted = [...withHits, ...withoutHits]; + } else { + sorted = productsToShow; // preserve selected order regardless of results + } // render each section sorted.forEach((product) => { From 0ed04b64a3436cc327e0798d711911a29785cdc6 Mon Sep 17 00:00:00 2001 From: Diane Batres Date: Wed, 3 Jun 2026 12:53:58 -0400 Subject: [PATCH 30/35] fix: remove console log --- hlx_statics/blocks/header/header.js | 1 - 1 file changed, 1 deletion(-) diff --git a/hlx_statics/blocks/header/header.js b/hlx_statics/blocks/header/header.js index e6d92743a..8c22d6d22 100644 --- a/hlx_statics/blocks/header/header.js +++ b/hlx_statics/blocks/header/header.js @@ -253,7 +253,6 @@ async function initSearch() { searchCleared = false; // Reset cleared flag when user presses Enter const trimmed = searchInput.value.trim(); if (trimmed === '') { // If users presses enter with an empty query while in a full search, clear results and show suggestions again - console.log('TEST 1'); searchResults.classList.remove('has-results'); searchResults.style.visibility = 'hidden'; outerSearchSuggestions.style.display = 'flex'; From dc41d74588ff9d962f0f3d3e821f8af48af5f338 Mon Sep 17 00:00:00 2001 From: David Sima Date: Thu, 4 Jun 2026 18:58:58 +0300 Subject: [PATCH 31/35] feat(ai-assistant): improve daa-ll tracking identifiers --- hlx_statics/blocks/ai-assistant/ai-assistant.js | 2 +- .../ai-assistant/ai-assistant_chat-bubble.js | 13 +++++++------ .../blocks/ai-assistant/ai-assistant_chat-ui.js | 17 +++++++---------- .../ai-assistant_suggested-questions.js | 3 +-- 4 files changed, 16 insertions(+), 19 deletions(-) diff --git a/hlx_statics/blocks/ai-assistant/ai-assistant.js b/hlx_statics/blocks/ai-assistant/ai-assistant.js index 8253480b8..3dfa21781 100644 --- a/hlx_statics/blocks/ai-assistant/ai-assistant.js +++ b/hlx_statics/blocks/ai-assistant/ai-assistant.js @@ -45,7 +45,7 @@ export default async function decorate(block) { * @param {string} options.text */ link({ href, title, text }) { - return `${text}`; + return `${text}`; }, }, }); diff --git a/hlx_statics/blocks/ai-assistant/ai-assistant_chat-bubble.js b/hlx_statics/blocks/ai-assistant/ai-assistant_chat-bubble.js index bb8bf7df1..dbc810b61 100644 --- a/hlx_statics/blocks/ai-assistant/ai-assistant_chat-bubble.js +++ b/hlx_statics/blocks/ai-assistant/ai-assistant_chat-bubble.js @@ -42,7 +42,6 @@ export class ChatBubble { const bubble = createTag("div", { class: "chat-bubble" }); const contentElement = createTag("div", { class: "chat-bubble-content", - "daa-lh": "AI Assistant - Message bubble", }); if (this.source === "ai") { @@ -114,7 +113,7 @@ export class ChatBubble { class: "chat-bubble-copy", type: "button", "aria-label": COPY_BUTTON_LABEL, - "daa-ll": COPY_BUTTON_LABEL, + "daa-ll": "Devsite AI Assistant:Message:Button:Copy", }) ); button.innerHTML = COPY_ICON_SVG; @@ -184,7 +183,7 @@ export class ChatBubble { class: "chat-bubble-feedback", type: "button", "aria-label": THUMB_UP_LABEL, - "daa-ll": THUMB_UP_LABEL, + "daa-ll": "Devsite AI Assistant:Message:Button:Upvote", }) ); thumbUpButton.innerHTML = THUMB_UP_ICON_SVG; @@ -197,7 +196,7 @@ export class ChatBubble { class: "chat-bubble-feedback", type: "button", "aria-label": THUMB_DOWN_LABEL, - "daa-ll": THUMB_DOWN_LABEL, + "daa-ll": "Devsite AI Assistant:Message:Button:Downvote", }) ); thumbDownButton.innerHTML = THUMB_DOWN_ICON_SVG; @@ -332,7 +331,6 @@ export class ChatBubble { const wrapper = createTag("div", { class: "chat-bubble-sources", - "daa-lh": "AI Assistant - Message sources", }); const heading = createTag("p", { class: "chat-bubble-sources-heading" }); heading.textContent = "Sources:"; @@ -347,7 +345,10 @@ export class ChatBubble { rel: "noopener noreferrer", }); a.textContent = title || url; - a.setAttribute("daa-ll", a.textContent); + a.setAttribute( + "daa-ll", + `Devsite AI Assistant:Message:Sources:Link:${a.textContent}|${url}`, + ); li.appendChild(a); list.appendChild(li); }); diff --git a/hlx_statics/blocks/ai-assistant/ai-assistant_chat-ui.js b/hlx_statics/blocks/ai-assistant/ai-assistant_chat-ui.js index bfff6451a..6165269d5 100644 --- a/hlx_statics/blocks/ai-assistant/ai-assistant_chat-ui.js +++ b/hlx_statics/blocks/ai-assistant/ai-assistant_chat-ui.js @@ -31,7 +31,6 @@ export const createAiAvatar = () => { export const createChatWindowHeader = () => { const chatWindowHeader = createTag("header", { class: "chat-window-header", - "daa-lh": "AI Assistant - Window header", }); chatWindowHeader.appendChild(createAiAvatar()); const label = createTag("h2", { @@ -47,7 +46,7 @@ export const createChatWindowHeader = () => { class: "chat-window-clear", type: "button", "aria-label": CHAT_BUTTON_LABEL_CLEAR, - "daa-ll": "Open clear dialog", + "daa-ll": "Devsite AI Assistant:Clear dialog:Open", }); const clearButtonIcon = createTag("img", { src: "/hlx_statics/icons/delete.svg", @@ -60,7 +59,7 @@ export const createChatWindowHeader = () => { class: "chat-window-close", type: "button", "aria-label": CHAT_BUTTON_LABEL_CLOSE, - "daa-ll": CHAT_BUTTON_LABEL_CLOSE, + "daa-ll": "Devsite AI Assistant:Close", }); const closeButtonIcon = createTag("img", { src: "/hlx_statics/icons/dismiss.svg", @@ -85,7 +84,6 @@ export const createChatWindowHeader = () => { export const createInputSection = () => { const inputSection = createTag("div", { class: "chat-window-input-section", - "daa-lh": "AI Assistant - Input section", }); const textarea = /** @type {HTMLTextAreaElement} */ ( createTag("textarea", { @@ -95,14 +93,14 @@ export const createInputSection = () => { }) ); const disclaimerText = createTag("div", { class: "chat-disclaimer-text" }); - disclaimerText.innerHTML = `By using AI Assistant, you agree to the Generative AI User Guidelines.`; + disclaimerText.innerHTML = `By using AI Assistant, you agree to the Generative AI User Guidelines.`; const sendButton = /** @type {HTMLButtonElement} */ ( createTag("button", { class: "chat-send-button", type: "button", "aria-label": "Send message", - "daa-ll": "Send message", + "daa-ll": "Devsite AI Assistant:Send message", }) ); const sendButtonIcon = createTag("img", { @@ -157,7 +155,7 @@ export const createChatButton = () => { "aria-expanded": "false", "aria-haspopup": "dialog", "aria-label": CHAT_BUTTON_LABEL_OPEN, - "daa-ll": CHAT_BUTTON_LABEL_OPEN, + "daa-ll": "Devsite AI Assistant:Open", }); chatButton.innerHTML = ``; ELEMENTS.CHAT_BUTTON = chatButton; @@ -169,7 +167,6 @@ export const createClearDialog = () => { const card = createTag("section", { class: "chat-window-dialog-card", - "daa-lh": "AI Assistant - Clear dialog", }); const heading = createTag("h2", { class: "chat-window-dialog-heading" }); @@ -186,7 +183,7 @@ export const createClearDialog = () => { const cancelButton = createTag("button", { class: "chat-window-dialog-cancel", type: "button", - "daa-ll": "Cancel", + "daa-ll": "Devsite AI Assistant:Clear dialog:Cancel", }); cancelButton.textContent = "Cancel"; cancelButton.addEventListener("click", () => dialog.remove()); @@ -194,7 +191,7 @@ export const createClearDialog = () => { const clearButton = createTag("button", { class: "chat-window-dialog-clear", type: "button", - "daa-ll": "Clear", + "daa-ll": "Devsite AI Assistant:Clear dialog:Clear", }); clearButton.textContent = "Clear"; clearButton.addEventListener("click", () => { diff --git a/hlx_statics/blocks/ai-assistant/ai-assistant_suggested-questions.js b/hlx_statics/blocks/ai-assistant/ai-assistant_suggested-questions.js index 58778be56..5f26a9501 100644 --- a/hlx_statics/blocks/ai-assistant/ai-assistant_suggested-questions.js +++ b/hlx_statics/blocks/ai-assistant/ai-assistant_suggested-questions.js @@ -57,7 +57,7 @@ export const updateSuggestedQuestions = (questions) => { const button = createTag("button", { type: "button", class: "chat-suggested-questions-button", - "daa-ll": label, + "daa-ll": `Devsite AI Assistant:Suggested questions:${label}`, }); const icon = createTag("img", { src: "/hlx_statics/icons/arrow-curved.svg", @@ -100,7 +100,6 @@ export const createSuggestedQuestionsSection = () => { title.textContent = "or choose from the following:"; const list = createTag("div", { class: "chat-suggested-questions-list", - "daa-lh": "AI Assistant - Suggested questions", }); wrapper.appendChild(title); From d9711c7e11c0c604463808a8b7c14d225b4f6337 Mon Sep 17 00:00:00 2001 From: David Sima Date: Thu, 4 Jun 2026 19:05:10 +0300 Subject: [PATCH 32/35] replace Devsite AI with DevsiteAI --- hlx_statics/blocks/ai-assistant/ai-assistant.js | 2 +- .../ai-assistant/ai-assistant_chat-bubble.js | 8 ++++---- .../blocks/ai-assistant/ai-assistant_chat-ui.js | 14 +++++++------- .../ai-assistant_suggested-questions.js | 2 +- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/hlx_statics/blocks/ai-assistant/ai-assistant.js b/hlx_statics/blocks/ai-assistant/ai-assistant.js index 3dfa21781..2d940d45e 100644 --- a/hlx_statics/blocks/ai-assistant/ai-assistant.js +++ b/hlx_statics/blocks/ai-assistant/ai-assistant.js @@ -45,7 +45,7 @@ export default async function decorate(block) { * @param {string} options.text */ link({ href, title, text }) { - return `${text}`; + return `${text}`; }, }, }); diff --git a/hlx_statics/blocks/ai-assistant/ai-assistant_chat-bubble.js b/hlx_statics/blocks/ai-assistant/ai-assistant_chat-bubble.js index dbc810b61..b16486dcf 100644 --- a/hlx_statics/blocks/ai-assistant/ai-assistant_chat-bubble.js +++ b/hlx_statics/blocks/ai-assistant/ai-assistant_chat-bubble.js @@ -113,7 +113,7 @@ export class ChatBubble { class: "chat-bubble-copy", type: "button", "aria-label": COPY_BUTTON_LABEL, - "daa-ll": "Devsite AI Assistant:Message:Button:Copy", + "daa-ll": "DevsiteAI Assistant:Message:Button:Copy", }) ); button.innerHTML = COPY_ICON_SVG; @@ -183,7 +183,7 @@ export class ChatBubble { class: "chat-bubble-feedback", type: "button", "aria-label": THUMB_UP_LABEL, - "daa-ll": "Devsite AI Assistant:Message:Button:Upvote", + "daa-ll": "DevsiteAI Assistant:Message:Button:Upvote", }) ); thumbUpButton.innerHTML = THUMB_UP_ICON_SVG; @@ -196,7 +196,7 @@ export class ChatBubble { class: "chat-bubble-feedback", type: "button", "aria-label": THUMB_DOWN_LABEL, - "daa-ll": "Devsite AI Assistant:Message:Button:Downvote", + "daa-ll": "DevsiteAI Assistant:Message:Button:Downvote", }) ); thumbDownButton.innerHTML = THUMB_DOWN_ICON_SVG; @@ -347,7 +347,7 @@ export class ChatBubble { a.textContent = title || url; a.setAttribute( "daa-ll", - `Devsite AI Assistant:Message:Sources:Link:${a.textContent}|${url}`, + `DevsiteAI Assistant:Message:Sources:Link:${a.textContent}|${url}`, ); li.appendChild(a); list.appendChild(li); diff --git a/hlx_statics/blocks/ai-assistant/ai-assistant_chat-ui.js b/hlx_statics/blocks/ai-assistant/ai-assistant_chat-ui.js index 6165269d5..9ca2b7e8e 100644 --- a/hlx_statics/blocks/ai-assistant/ai-assistant_chat-ui.js +++ b/hlx_statics/blocks/ai-assistant/ai-assistant_chat-ui.js @@ -46,7 +46,7 @@ export const createChatWindowHeader = () => { class: "chat-window-clear", type: "button", "aria-label": CHAT_BUTTON_LABEL_CLEAR, - "daa-ll": "Devsite AI Assistant:Clear dialog:Open", + "daa-ll": "DevsiteAI Assistant:Clear dialog:Open", }); const clearButtonIcon = createTag("img", { src: "/hlx_statics/icons/delete.svg", @@ -59,7 +59,7 @@ export const createChatWindowHeader = () => { class: "chat-window-close", type: "button", "aria-label": CHAT_BUTTON_LABEL_CLOSE, - "daa-ll": "Devsite AI Assistant:Close", + "daa-ll": "DevsiteAI Assistant:Close", }); const closeButtonIcon = createTag("img", { src: "/hlx_statics/icons/dismiss.svg", @@ -93,14 +93,14 @@ export const createInputSection = () => { }) ); const disclaimerText = createTag("div", { class: "chat-disclaimer-text" }); - disclaimerText.innerHTML = `By using AI Assistant, you agree to the Generative AI User Guidelines.`; + disclaimerText.innerHTML = `By using AI Assistant, you agree to the Generative AI User Guidelines.`; const sendButton = /** @type {HTMLButtonElement} */ ( createTag("button", { class: "chat-send-button", type: "button", "aria-label": "Send message", - "daa-ll": "Devsite AI Assistant:Send message", + "daa-ll": "DevsiteAI Assistant:Send message", }) ); const sendButtonIcon = createTag("img", { @@ -155,7 +155,7 @@ export const createChatButton = () => { "aria-expanded": "false", "aria-haspopup": "dialog", "aria-label": CHAT_BUTTON_LABEL_OPEN, - "daa-ll": "Devsite AI Assistant:Open", + "daa-ll": "DevsiteAI Assistant:Open", }); chatButton.innerHTML = ``; ELEMENTS.CHAT_BUTTON = chatButton; @@ -183,7 +183,7 @@ export const createClearDialog = () => { const cancelButton = createTag("button", { class: "chat-window-dialog-cancel", type: "button", - "daa-ll": "Devsite AI Assistant:Clear dialog:Cancel", + "daa-ll": "DevsiteAI Assistant:Clear dialog:Cancel", }); cancelButton.textContent = "Cancel"; cancelButton.addEventListener("click", () => dialog.remove()); @@ -191,7 +191,7 @@ export const createClearDialog = () => { const clearButton = createTag("button", { class: "chat-window-dialog-clear", type: "button", - "daa-ll": "Devsite AI Assistant:Clear dialog:Clear", + "daa-ll": "DevsiteAI Assistant:Clear dialog:Clear", }); clearButton.textContent = "Clear"; clearButton.addEventListener("click", () => { diff --git a/hlx_statics/blocks/ai-assistant/ai-assistant_suggested-questions.js b/hlx_statics/blocks/ai-assistant/ai-assistant_suggested-questions.js index 5f26a9501..b9603e947 100644 --- a/hlx_statics/blocks/ai-assistant/ai-assistant_suggested-questions.js +++ b/hlx_statics/blocks/ai-assistant/ai-assistant_suggested-questions.js @@ -57,7 +57,7 @@ export const updateSuggestedQuestions = (questions) => { const button = createTag("button", { type: "button", class: "chat-suggested-questions-button", - "daa-ll": `Devsite AI Assistant:Suggested questions:${label}`, + "daa-ll": `DevsiteAI Assistant:Suggested questions:${label}`, }); const icon = createTag("img", { src: "/hlx_statics/icons/arrow-curved.svg", From 743b25a92aa275165f53f0ad54337e25c6ab0472 Mon Sep 17 00:00:00 2001 From: David Sima Date: Mon, 1 Jun 2026 11:34:02 +0300 Subject: [PATCH 33/35] fix(ai-assistant): fixed chat button size --- hlx_statics/blocks/ai-assistant/ai-assistant.css | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hlx_statics/blocks/ai-assistant/ai-assistant.css b/hlx_statics/blocks/ai-assistant/ai-assistant.css index 5dd65ee41..8820c3e3e 100644 --- a/hlx_statics/blocks/ai-assistant/ai-assistant.css +++ b/hlx_statics/blocks/ai-assistant/ai-assistant.css @@ -42,7 +42,9 @@ position: relative; width: var(--button-size); + min-width: var(--button-size); height: var(--button-size); + min-height: var(--button-size); border-radius: 50%; border: 2px solid transparent; padding: 0; From c5f8ee14146da62da7e186c5ada55022191bb08b Mon Sep 17 00:00:00 2001 From: Louisa Chu Date: Fri, 5 Jun 2026 13:41:59 -0700 Subject: [PATCH 34/35] update the copy button --- hlx_statics/blocks/code/code.css | 8 +++++++- hlx_statics/blocks/codeblock/codeblock.css | 5 +++++ hlx_statics/blocks/tab/tab.css | 13 ++++++++++--- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/hlx_statics/blocks/code/code.css b/hlx_statics/blocks/code/code.css index 0cc3f08a9..d47e276e4 100644 --- a/hlx_statics/blocks/code/code.css +++ b/hlx_statics/blocks/code/code.css @@ -1,9 +1,15 @@ main div.code-wrapper pre[class*=language-], main div.nested-code-wrapper pre[class*=language-] { border-radius: 4px; + padding-top: 3.5em; +} + +main div.code-wrapper pre[class*=language-] .line-highlight, +main div.nested-code-wrapper pre[class*=language-] .line-highlight { + margin-top: 3.5em; } main div.code-wrapper pre[class*=language-].no-line-numbers .line-highlight, main div.nested-code-wrapper pre[class*=language-].no-line-numbers .line-highlight { transform: translateY(-1.6em); -} \ No newline at end of file +} diff --git a/hlx_statics/blocks/codeblock/codeblock.css b/hlx_statics/blocks/codeblock/codeblock.css index 352b8ed9d..9d9333f87 100644 --- a/hlx_statics/blocks/codeblock/codeblock.css +++ b/hlx_statics/blocks/codeblock/codeblock.css @@ -1,6 +1,11 @@ main div.codeblock-wrapper pre[class*="language-"] { border-bottom-left-radius: 4px; border-bottom-right-radius: 4px; + padding-top: 3.5em; +} + +main div.codeblock-wrapper pre[class*="language-"] .line-highlight { + margin-top: 3.5em; } main div.codeblock-wrapper { diff --git a/hlx_statics/blocks/tab/tab.css b/hlx_statics/blocks/tab/tab.css index 69c2d663e..30f3729b1 100644 --- a/hlx_statics/blocks/tab/tab.css +++ b/hlx_statics/blocks/tab/tab.css @@ -32,6 +32,14 @@ main div.tab-wrapper > div:not(.background-color-navy) .tab-button:hover { color: white; } +main div.tab-wrapper div.code-toolbar pre.line-numbers { + padding-top: 3.5em; +} + +main div.tab-wrapper div.code-toolbar pre.line-numbers .line-highlight { + margin-top: 3.5em; +} + main div.tab-wrapper div.code-toolbar>.toolbar>.toolbar-item>button { padding: 12px; height: 32px; @@ -55,7 +63,6 @@ main div.tab-wrapper .line-numbers-rows>span { @media only screen and (max-width: 860px) { main div.tab-wrapper div.code-toolbar>.toolbar>.toolbar-item>button { - padding: 0; height: 25px; right: 0px; } @@ -223,7 +230,7 @@ main div.tab-wrapper div.background-color-navy .code-toolbar pre { background-color: #111a35; border-radius: 2vh; } - + main div.sub-content-wrapper .code-toolbar pre { border-radius: 0%; } @@ -341,4 +348,4 @@ main div.tab-wrapper .tab.vertical.background-color-navy > .content-wrapper { padding: 12px 16px; width: 100%; } -} \ No newline at end of file +} From 02017235ad628e256ec6508bce959375aa5fbe65 Mon Sep 17 00:00:00 2001 From: Diane Batres Date: Mon, 8 Jun 2026 12:40:53 -0400 Subject: [PATCH 35/35] fix: uncomment navigation loading logic in header.js --- hlx_statics/blocks/header/header.js | 54 ++++++++++++++--------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/hlx_statics/blocks/header/header.js b/hlx_statics/blocks/header/header.js index 8c22d6d22..6373d5efc 100644 --- a/hlx_statics/blocks/header/header.js +++ b/hlx_statics/blocks/header/header.js @@ -1141,33 +1141,33 @@ export default async function decorate(block) { // check if documentation template then retrieve from config otherwise default back to google drive path let navPath; - // if (IS_DEV_DOCS) { - // const topNavHtml = await fetchTopNavHtml(); - // if (topNavHtml) { - // navigationLinks.innerHTML += topNavHtml; - // } - // } else { - // navPath = cfg.nav || getClosestFranklinSubfolder(window.location.origin,'nav'); - // let fragment = await loadFragment(navPath); - // if (fragment == null) { - // // load the default nav in franklin_assets folder nav - // fragment = await loadFragment(getClosestFranklinSubfolder(window.location.origin, 'nav', true)); - // } - // const ul = fragment.querySelector("ul"); - // ul.classList.add("menu"); - // ul.setAttribute("id", "navigation-links"); - // fragment.querySelectorAll("li").forEach((li, index) => { - // if (index == 0) { - // if (isTopLevelNav(window.location.pathname)) { - // const homeLink = ul.querySelector('li:nth-child(1)'); - // homeLink.className = 'navigation-home'; - // } else { - // li.classList.add("navigation-products"); - // } - // } - // }); - // navigationLinks = ul; - // } + if (IS_DEV_DOCS) { + const topNavHtml = await fetchTopNavHtml(); + if (topNavHtml) { + navigationLinks.innerHTML += topNavHtml; + } + } else { + navPath = cfg.nav || getClosestFranklinSubfolder(window.location.origin,'nav'); + let fragment = await loadFragment(navPath); + if (fragment == null) { + // load the default nav in franklin_assets folder nav + fragment = await loadFragment(getClosestFranklinSubfolder(window.location.origin, 'nav', true)); + } + const ul = fragment.querySelector("ul"); + ul.classList.add("menu"); + ul.setAttribute("id", "navigation-links"); + fragment.querySelectorAll("li").forEach((li, index) => { + if (index == 0) { + if (isTopLevelNav(window.location.pathname)) { + const homeLink = ul.querySelector('li:nth-child(1)'); + homeLink.className = 'navigation-home'; + } else { + li.classList.add("navigation-products"); + } + } + }); + navigationLinks = ul; + } navigationLinks.querySelectorAll('li > ul').forEach((dropDownList, index) => { let dropdownLinkDropdownHTML = '';