diff --git a/assets/css/style.css b/assets/css/style.css index d8469b419a5f..8315621f425d 100644 --- a/assets/css/style.css +++ b/assets/css/style.css @@ -41,5 +41,6 @@ @import "syntax-dark.css"; @import "syntax-light.css"; @import "components.css"; +@import "highlight-github-dark.css"; @variant dark (&:where(.dark, .dark *)); diff --git a/static/assets/images/logo-icon-white.svg b/assets/images/ask-ai-logo.svg similarity index 100% rename from static/assets/images/logo-icon-white.svg rename to assets/images/ask-ai-logo.svg diff --git a/assets/js/src/alpine.js b/assets/js/src/alpine.js index cdcdfeb93055..7249a31def29 100644 --- a/assets/js/src/alpine.js +++ b/assets/js/src/alpine.js @@ -2,12 +2,106 @@ import Alpine from 'alpinejs' import collapse from '@alpinejs/collapse' import persist from '@alpinejs/persist' import focus from '@alpinejs/focus' - +import { marked } from 'marked' +import hljs from 'highlight.js/lib/core' +// Import languages relevant to Docker docs +import bash from 'highlight.js/lib/languages/bash' +import dockerfile from 'highlight.js/lib/languages/dockerfile' +import yaml from 'highlight.js/lib/languages/yaml' +import json from 'highlight.js/lib/languages/json' +import javascript from 'highlight.js/lib/languages/javascript' +import python from 'highlight.js/lib/languages/python' +import go from 'highlight.js/lib/languages/go' + window.Alpine = Alpine Alpine.plugin(collapse) Alpine.plugin(persist) Alpine.plugin(focus) + +// Register highlight.js languages +hljs.registerLanguage('bash', bash) +hljs.registerLanguage('sh', bash) +hljs.registerLanguage('shell', bash) +hljs.registerLanguage('console', bash) +hljs.registerLanguage('dockerfile', dockerfile) +hljs.registerLanguage('yaml', yaml) +hljs.registerLanguage('yml', yaml) +hljs.registerLanguage('json', json) +hljs.registerLanguage('javascript', javascript) +hljs.registerLanguage('js', javascript) +hljs.registerLanguage('python', python) +hljs.registerLanguage('py', python) +hljs.registerLanguage('go', go) +hljs.registerLanguage('golang', go) + +// Configure marked to escape HTML in text tokens only (not code blocks) +marked.use({ + walkTokens(token) { + // Escape HTML in text and HTML tokens, preserve code blocks + if (token.type === 'text' || token.type === 'html') { + const text = token.text || token.raw + const escaped = text + .replace(/&/g, '&') + .replace(//g, '>') + if (token.text) token.text = escaped + if (token.raw) token.raw = escaped + } + } +}) + +// Add $markdown magic for rendering markdown with syntax highlighting +Alpine.magic('markdown', () => { + return (content) => { + if (!content) return '' + const html = marked(content) + + // Parse and highlight code blocks + const div = document.createElement('div') + div.innerHTML = html + + // Handle code blocks (pre > code) + div.querySelectorAll('pre').forEach((pre) => { + // Add not-prose to prevent Tailwind Typography styling + pre.classList.add('not-prose') + const code = pre.querySelector('code') + if (code) { + // Preserve the original text with newlines + const codeText = code.textContent + + // Clear and set as plain text first to preserve structure + code.textContent = codeText + + // Now apply highlight.js which will work with the text nodes + hljs.highlightElement(code) + } + }) + + // Handle inline code elements (not in pre blocks) + div.querySelectorAll('code:not(pre code)').forEach((code) => { + code.classList.add('not-prose') + }) + + return div.innerHTML + } +}) + +// Stores Alpine.store("showSidebar", false) +Alpine.store('gordon', { + isOpen: false, + query: '', + toggle() { + this.isOpen = !this.isOpen + }, + open(query) { + this.isOpen = true + if (query) this.query = query + }, + close() { + this.isOpen = false + } +}) Alpine.start() diff --git a/hugo.yaml b/hugo.yaml index 369dc8d8c411..c6d79b2eb3ef 100644 --- a/hugo.yaml +++ b/hugo.yaml @@ -271,6 +271,9 @@ module: # Mount the icon files to assets so we can access them with resources.Get - source: node_modules/@material-symbols/svg-400/rounded target: assets/icons + # Mount highlight.js theme for Gordon chat syntax highlighting + - source: node_modules/highlight.js/styles/github-dark.css + target: assets/css/highlight-github-dark.css imports: diff --git a/layouts/_default/baseof.html b/layouts/_default/baseof.html index 7b06c790c819..946147a21e9c 100644 --- a/layouts/_default/baseof.html +++ b/layouts/_default/baseof.html @@ -8,6 +8,7 @@ class="dark:bg-navbar-bg-dark bg-navbar-bg flex flex-col items-center text-base text-black dark:text-white" > {{ partial "header.html" . }} + {{ partial "gordon-chat.html" . }}
{{ partial "header.html" . }} + {{ partial "gordon-chat.html" . }}
+ +
+ +
+ + +
+ +
+
+ {{ partial "utils/svg.html" "images/ask-ai-logo.svg" }} +
+
+ + +
+
+ + +
+ + + + + + + + + + + + + + +
+ + +
+
+
+
+ +
+ +
+
+
+ + + +
+ + Share feedback + +
+
+
+ + +
+ This is a custom LLM for answering questions about Docker. Answers are + based on the documentation. +
+
+
+ + + diff --git a/layouts/partials/head.html b/layouts/partials/head.html index 4c1455512c37..af9892537126 100644 --- a/layouts/partials/head.html +++ b/layouts/partials/head.html @@ -51,59 +51,6 @@ })(window,document,'https://static.hotjar.com/c/hotjar-','.js?sv='); {{ end }} -{{/* kapa.ai widget */}} - - {{/* preload Roboto Flex as it's a critical font: https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/rel/preload */}}
. + .
` }} {{- $emptyState | safe.HTML }} diff --git a/package-lock.json b/package-lock.json index 4aefc775b6d7..3b7316c8b938 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,6 +17,8 @@ "@tailwindcss/cli": "^4.1.6", "@tailwindcss/typography": "^0.5.15", "alpinejs": "^3.14.3", + "highlight.js": "^11.11.1", + "marked": "^17.0.0", "tailwindcss": "^4.1.6" }, "devDependencies": { @@ -976,6 +978,15 @@ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "license": "ISC" }, + "node_modules/highlight.js": { + "version": "11.11.1", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.11.1.tgz", + "integrity": "sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/is-alphabetical": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", @@ -1367,6 +1378,18 @@ "url": "https://github.com/sponsors/DavidAnson" } }, + "node_modules/marked": { + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-17.0.0.tgz", + "integrity": "sha512-KkDYEWEEiYJw/KC+DVm1zzlpMQSMIu6YRltkcCvwheCp8HWPXCk9JwOmHJKBlGfzcpzcIt6x3sMnTsRm/51oDg==", + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 20" + } + }, "node_modules/micromark": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz", diff --git a/package.json b/package.json index de8098b00b20..795ed12561b4 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,8 @@ "@tailwindcss/cli": "^4.1.6", "@tailwindcss/typography": "^0.5.15", "alpinejs": "^3.14.3", + "highlight.js": "^11.11.1", + "marked": "^17.0.0", "tailwindcss": "^4.1.6" }, "devDependencies": {