diff --git a/.gitignore b/.gitignore index 19bb929be..60a34ce08 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ tech-doc-hugo .DS_Store .idea .hugo_build.lock +_vendor/ diff --git a/layouts/partials/custom/head-end.html b/layouts/partials/custom/head-end.html index 6933ef943..0e6396274 100644 --- a/layouts/partials/custom/head-end.html +++ b/layouts/partials/custom/head-end.html @@ -29,7 +29,9 @@ data-project-color="#41AFAF" data-project-logo="https://docs.communityhealthtoolkit.org/logo.png" data-font-family="Noto Sans,sans-serif" - data-modal-disclaimer="This is a custom LLM for Community Health Toolkit (CHT) with access to all [Documentation](https://docs.communityhealthtoolkit.org), [GitHub Issues and READMEs](https://github.com/medic/cht-core) and the [CHT Forum](https://forum.communityhealthtoolkit.org). Rate the answers to let us know what you think!" + data-modal-disclaimer="This is a custom LLM for Community Health Toolkit (CHT) with access to all [Documentation](https://docs.communityhealthtoolkit.org), [GitHub Issues and READMEs](https://github.com/medic/cht-core) and the [CHT Forum](https://forum.communityhealthtoolkit.org). Rate the answers to let us know what you think! + +⚠️ NOTICE: Do not share any PII, PHI or passwords. Queries are archived and accessible by both Medic and Kapa." data-modal-example-questions="How do I run the CHT locally?,How can I contribute to the CHT?" data-button-text-color="#FFFFFF" data-modal-header-bg-color="#41AFAF" @@ -96,6 +98,11 @@ SEARCH: 'search' }; + const SEARCH_NOTICE_ID = 'cht-search-notice'; + + // The PII warning text — kept in sync with data-modal-disclaimer above + const NOTICE_TEXT = '⚠️ NOTICE: Do not share any PII, PHI or passwords. Queries are archived and accessible by both Medic and Kapa.'; + /** * Open Kapa modal in specified mode * @param {string} mode - 'ai' or 'search' @@ -255,6 +262,59 @@ }); }; + const injectSearchNotice = () => { + const kapaContainer = document.querySelector('#kapa-widget-container'); + if (!kapaContainer || !kapaContainer.shadowRoot) return; + + const shadowRoot = kapaContainer.shadowRoot; + + const isDark = document.documentElement.classList.contains('dark'); + + const buildNotice = () => { + const notice = document.createElement('div'); + notice.id = SEARCH_NOTICE_ID; + notice.style.cssText = [ + 'font-size: 13px', + 'padding: 10px 14px', + 'margin: 8px 0 4px 0', + 'border-radius: 4px', + 'line-height: 1.5', + 'background-color: ' + (isDark ? '#2e2e2e' : '#DFEAEA'), + 'color: ' + (isDark ? '#ffffff' : '#000000'), + ].join(';'); + notice.textContent = NOTICE_TEXT; + return notice; + }; + + const tryInject = () => { + const existing = shadowRoot.querySelector('#' + SEARCH_NOTICE_ID); + + const searchInput = shadowRoot.querySelector('input[type="text"]') + || shadowRoot.querySelector('input[placeholder*="earch"]'); + + if (!searchInput) { + + if (existing) existing.remove(); + return; + } + + if (existing) return; + + const inputRoot = searchInput.closest('[class*="Input-wrapper"]') + || searchInput.closest('[class*="input-wrapper"]') + || searchInput.parentElement; + + if (!inputRoot) return; + + inputRoot.insertAdjacentElement('afterend', buildNotice()); + }; + + const observer = new MutationObserver(tryInject); + observer.observe(shadowRoot, { childList: true, subtree: true }); + + tryInject(); + }; + /** * Sync Kapa's dark mode with site theme. * Kapa uses Shadow DOM which blocks external CSS, so we must: @@ -266,84 +326,96 @@ const colorScheme = isDark ? 'dark' : 'light'; const kapaContainer = document.querySelector('#kapa-widget-container'); - if (kapaContainer && kapaContainer.shadowRoot) { - const shadowRoot = kapaContainer.shadowRoot; - const kapaWidgetRoot = shadowRoot.querySelector('#kapa-widget-root'); - if (kapaWidgetRoot) { - kapaWidgetRoot.setAttribute('data-mantine-color-scheme', colorScheme); + if (!kapaContainer || !kapaContainer.shadowRoot) return; + + const shadowRoot = kapaContainer.shadowRoot; + const kapaWidgetRoot = shadowRoot.querySelector('#kapa-widget-root'); + if (kapaWidgetRoot) { + kapaWidgetRoot.setAttribute('data-mantine-color-scheme', colorScheme); + } + + const styleId = 'kapa-dark-mode-overrides'; + let styleEl = shadowRoot.querySelector('#' + styleId); + + if (isDark) { + if (!styleEl) { + styleEl = document.createElement('style'); + styleEl.id = styleId; + shadowRoot.appendChild(styleEl); } + styleEl.textContent = ` - // Inject dark mode CSS overrides into shadow DOM - const styleId = 'kapa-dark-mode-overrides'; - let styleEl = shadowRoot.querySelector(`#${styleId}`); + [data-mantine-color-scheme="dark"] .mantine-Title-root { + color: #e0e0e0 !important; + } - if (isDark) { - if (!styleEl) { - styleEl = document.createElement('style'); - styleEl.id = styleId; - shadowRoot.appendChild(styleEl); + [data-mantine-color-scheme="dark"] .mantine-Text-root { + color: #e0e0e0 !important; } - styleEl.textContent = ` - /* Dark mode: Question title */ - [data-mantine-color-scheme="dark"] .mantine-Title-root { - color: #e0e0e0 !important; - } - /* Dark mode: Answer content text */ - [data-mantine-color-scheme="dark"] .mantine-Text-root { - color: #e0e0e0 !important; - } - /* Dark mode: List items */ - [data-mantine-color-scheme="dark"] .mantine-List-root, - [data-mantine-color-scheme="dark"] .mantine-List-item, - [data-mantine-color-scheme="dark"] .mantine-List-itemLabel, - [data-mantine-color-scheme="dark"] .mantine-List-itemWrapper { - color: #e0e0e0 !important; - } - /* Dark mode: Input/textarea text */ - [data-mantine-color-scheme="dark"] .mantine-Input-input, - [data-mantine-color-scheme="dark"] .mantine-Textarea-input { - color: #e0e0e0 !important; - } - /* Dark mode: Links */ - [data-mantine-color-scheme="dark"] a { - color: #5cc8c8 !important; - } - /* Dark mode: Disclaimer/consent boxes */ - [data-mantine-color-scheme="dark"] .mantine-tilrau { - background-color: #2e2e2e !important; - color: #ffffff !important; - } - [data-mantine-color-scheme="dark"] .mantine-tilrau * { - color: #ffffff !important; - } - [data-mantine-color-scheme="dark"] .mantine-tilrau a { - color: #5cc8c8 !important; - } - /* Dark mode: Search result text highlights */ - [data-mantine-color-scheme="dark"] mark, - [data-mantine-color-scheme="dark"] .mantine-Mark-root, - [data-mantine-color-scheme="dark"] .mantine-Highlight-root mark { - background-color: #1a4040 !important; - color: #b0b0b0 !important; - } - /* Dark mode: Search result row - base state and hover */ - [data-mantine-color-scheme="dark"] a.mantine-Paper-root { - background-color: transparent !important; - transition: background-color 0.1s ease !important; - } - [data-mantine-color-scheme="dark"] a.mantine-Paper-root:hover, - [data-mantine-color-scheme="dark"] a.mantine-Paper-root:focus { - background-color: #404040 !important; - } - [data-mantine-color-scheme="dark"] a.mantine-Paper-root:hover *, - [data-mantine-color-scheme="dark"] a.mantine-Paper-root:focus * { - color: #e0e0e0 !important; - } - `; - } else if (styleEl) { - styleEl.remove(); + /* Dark mode: List items */ + [data-mantine-color-scheme="dark"] .mantine-List-root, + [data-mantine-color-scheme="dark"] .mantine-List-item, + [data-mantine-color-scheme="dark"] .mantine-List-itemLabel, + [data-mantine-color-scheme="dark"] .mantine-List-itemWrapper { + color: #e0e0e0 !important; + } + + [data-mantine-color-scheme="dark"] .mantine-Input-input, + [data-mantine-color-scheme="dark"] .mantine-Textarea-input { + color: #e0e0e0 !important; + } + + [data-mantine-color-scheme="dark"] a { + color: #5cc8c8 !important; + } + + [data-mantine-color-scheme="dark"] .mantine-tilrau { + background-color: #2e2e2e !important; + color: #ffffff !important; + } + [data-mantine-color-scheme="dark"] .mantine-tilrau * { + color: #ffffff !important; + } + [data-mantine-color-scheme="dark"] .mantine-tilrau a { + color: #5cc8c8 !important; + } + + [data-mantine-color-scheme="dark"] mark, + [data-mantine-color-scheme="dark"] .mantine-Mark-root, + [data-mantine-color-scheme="dark"] .mantine-Highlight-root mark { + background-color: #1a4040 !important; + color: #b0b0b0 !important; + } + + [data-mantine-color-scheme="dark"] a.mantine-Paper-root { + background-color: transparent !important; + transition: background-color 0.1s ease !important; + } + [data-mantine-color-scheme="dark"] a.mantine-Paper-root:hover, + [data-mantine-color-scheme="dark"] a.mantine-Paper-root:focus { + background-color: #404040 !important; + } + [data-mantine-color-scheme="dark"] a.mantine-Paper-root:hover *, + [data-mantine-color-scheme="dark"] a.mantine-Paper-root:focus * { + color: #e0e0e0 !important; + } + + #cht-search-notice { + background-color: #2e2e2e !important; + color: #ffffff !important; + } + `; + } else { + if (styleEl) styleEl.remove(); + // Also reset light-mode notice colors if present + const notice = shadowRoot.querySelector('#' + SEARCH_NOTICE_ID); + if (notice) { + notice.style.backgroundColor = '#DFEAEA'; + notice.style.color = '#000000'; } } + + injectSearchNotice(); }; /** @@ -366,9 +438,11 @@ for (const node of mutation.addedNodes) { if (node.nodeType === Node.ELEMENT_NODE) { if (node.id === 'kapa-widget-container' || node.querySelector?.('#kapa-widget-container')) { - // Wait for shadow DOM to initialize + setTimeout(syncKapaDarkMode, 100); setTimeout(syncKapaDarkMode, 500); + setTimeout(injectSearchNotice, 300); + setTimeout(injectSearchNotice, 800); } } } @@ -391,9 +465,9 @@ injectKapaButton(); setupKapaSearchIntercept(); setupDarkModeSync(); + injectSearchNotice(); }; - // Set up keyboard shortcuts immediately (capture phase) setupKeyboardShortcuts(); // Initialize DOM-dependent features @@ -403,4 +477,4 @@ init(); } })(); - + \ No newline at end of file