Skip to content

Commit 6c90fa1

Browse files
authored
add AI footer (#141)
1 parent b290b31 commit 6c90fa1

3 files changed

Lines changed: 280 additions & 1 deletion

File tree

docs/javascripts/page-toolbar.js

Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
// Page toolbar functionality for documentation pages
2+
// Adds toolbar with Copy Page, View Markdown, Ask in ChatGPT, and Ask in Claude
3+
(function() {
4+
'use strict';
5+
6+
// Check if we're on the setup page
7+
function isSetupPage() {
8+
// Check if current page path includes 'setup'
9+
var path = window.location.pathname;
10+
// Handle various URL formats: /setup/, /setup, /setup.html, /users/setup/, etc.
11+
var normalizedPath = path.replace(/\/$/, '').replace(/\.html$/, '').toLowerCase();
12+
return normalizedPath.endsWith('/setup') || normalizedPath.endsWith('setup') ||
13+
normalizedPath === '/setup' || normalizedPath === 'setup';
14+
}
15+
16+
// Create the toolbar HTML
17+
function createToolbar() {
18+
var toolbar = document.createElement('div');
19+
toolbar.className = 'page-toolbar';
20+
toolbar.innerHTML =
21+
'<a href="#" class="page-toolbar-link" id="copy-page-btn">' +
22+
'<img src="data:image/svg+xml,%3Csvg xmlns=\'http://www.w3.org/2000/svg\' width=\'16\' height=\'16\' viewBox=\'0 0 24 24\' fill=\'none\' stroke=\'%23ffffff\' stroke-width=\'2\' stroke-linecap=\'round\' stroke-linejoin=\'round\'%3E%3Crect x=\'9\' y=\'9\' width=\'13\' height=\'13\' rx=\'2\' ry=\'2\'%3E%3C/rect%3E%3Cpath d=\'M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1\'%3E%3C/path%3E%3C/svg%3E" class="page-toolbar-icon" alt="" />' +
23+
'Copy Page' +
24+
'</a>' +
25+
'<span class="page-toolbar-separator">|</span>' +
26+
'<a href="#" class="page-toolbar-link" id="view-markdown-btn">' +
27+
'<img src="data:image/svg+xml,%3Csvg xmlns=\'http://www.w3.org/2000/svg\' width=\'16\' height=\'16\' viewBox=\'0 0 24 24\' fill=\'none\' stroke=\'%23ffffff\' stroke-width=\'2\' stroke-linecap=\'round\' stroke-linejoin=\'round\'%3E%3Cpath d=\'M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z\'%3E%3C/path%3E%3Cpolyline points=\'14 2 14 8 20 8\'%3E%3C/polyline%3E%3Cline x1=\'16\' y1=\'13\' x2=\'8\' y2=\'13\'%3E%3C/line%3E%3Cline x1=\'16\' y1=\'17\' x2=\'8\' y2=\'17\'%3E%3C/line%3E%3Cpolyline points=\'10 9 9 9 8 9\'%3E%3C/polyline%3E%3C/svg%3E" class="page-toolbar-icon" alt="" />' +
28+
'View Markdown' +
29+
'</a>' +
30+
'<span class="page-toolbar-separator">|</span>' +
31+
'<a href="#" class="page-toolbar-link" id="ask-chatgpt-btn">' +
32+
'<img src="data:image/svg+xml,%3Csvg xmlns=\'http://www.w3.org/2000/svg\' width=\'16\' height=\'16\' fill=\'%23ffffff\' viewBox=\'0 0 256 256\'%3E%3Cpath d=\'M224.32,114.24a56,56,0,0,0-60.07-76.57A56,56,0,0,0,67.93,51.44a56,56,0,0,0-36.25,90.32A56,56,0,0,0,69,217,56.39,56.39,0,0,0,83.59,219a55.75,55.75,0,0,0,8.17-.61,56,56,0,0,0,96.31-13.78,56,56,0,0,0,36.25-90.32ZM182.85,54.43a40,40,0,0,1,28.56,48c-.95-.63-1.91-1.24-2.91-1.81L164,74.88a8,8,0,0,0-8,0l-44,25.41V81.81l40.5-23.38A39.76,39.76,0,0,1,182.85,54.43ZM144,137.24l-16,9.24-16-9.24V118.76l16-9.24,16,9.24ZM80,72a40,40,0,0,1,67.53-29c-1,.51-2,1-3,1.62L100,70.27a8,8,0,0,0-4,6.92V128l-16-9.24ZM40.86,86.93A39.75,39.75,0,0,1,64.12,68.57C64.05,69.71,64,70.85,64,72v51.38a8,8,0,0,0,4,6.93l44,25.4L96,165,55.5,141.57A40,40,0,0,1,40.86,86.93ZM73.15,201.57a40,40,0,0,1-28.56-48c.95.63,1.91,1.24,2.91,1.81L92,181.12a8,8,0,0,0,8,0l44-25.41v18.48l-40.5,23.38A39.76,39.76,0,0,1,73.15,201.57ZM176,184a40,40,0,0,1-67.52,29.05c1-.51,2-1.05,3-1.63L156,185.73a8,8,0,0,0,4-6.92V128l16,9.24Zm39.14-14.93a39.75,39.75,0,0,1-23.26,18.36c.07-1.14.12-2.28.12-3.43V132.62a8,8,0,0,0-4-6.93l-44-25.4,16-9.24,40.5,23.38A40,40,0,0,1,215.14,169.07Z\'/%3E%3C/svg%3E" class="page-toolbar-icon" alt="" />' +
33+
'Ask in ChatGPT' +
34+
'</a>' +
35+
'<span class="page-toolbar-separator">|</span>' +
36+
'<a href="#" class="page-toolbar-link" id="ask-claude-btn">' +
37+
'<img src="https://www.google.com/s2/favicons?domain=claude.ai&sz=16" class="page-toolbar-icon page-toolbar-favicon" alt="" />' +
38+
'Ask in Claude' +
39+
'</a>';
40+
return toolbar;
41+
}
42+
43+
// Get page content as text
44+
function getPageContent() {
45+
var content = document.querySelector('.md-content__inner');
46+
if (!content) {
47+
content = document.querySelector('main');
48+
}
49+
if (!content) {
50+
content = document.body;
51+
}
52+
return content ? content.innerText || content.textContent : '';
53+
}
54+
55+
// Get page title
56+
function getPageTitle() {
57+
var title = document.querySelector('h1');
58+
return title ? title.textContent.trim() : document.title;
59+
}
60+
61+
// Copy page content to clipboard
62+
function copyPageToClipboard() {
63+
var content = getPageContent();
64+
var title = getPageTitle();
65+
var textToCopy = title + '\n\n' + content;
66+
67+
if (navigator.clipboard && navigator.clipboard.writeText) {
68+
navigator.clipboard.writeText(textToCopy).then(function() {
69+
showCopyFeedback('copy-page-btn');
70+
}).catch(function(err) {
71+
console.error('Failed to copy:', err);
72+
fallbackCopy(textToCopy);
73+
});
74+
} else {
75+
fallbackCopy(textToCopy);
76+
}
77+
}
78+
79+
// Fallback copy method for older browsers
80+
function fallbackCopy(text) {
81+
var textArea = document.createElement('textarea');
82+
textArea.value = text;
83+
textArea.style.position = 'fixed';
84+
textArea.style.left = '-999999px';
85+
document.body.appendChild(textArea);
86+
textArea.select();
87+
try {
88+
document.execCommand('copy');
89+
showCopyFeedback('copy-page-btn');
90+
} catch (err) {
91+
console.error('Fallback copy failed:', err);
92+
}
93+
document.body.removeChild(textArea);
94+
}
95+
96+
// Show copy feedback
97+
function showCopyFeedback(buttonId) {
98+
var btn = document.getElementById(buttonId);
99+
if (btn) {
100+
var icon = btn.querySelector('.page-toolbar-icon');
101+
var textNode = Array.from(btn.childNodes).find(function(node) {
102+
return node.nodeType === 3 && node.textContent.trim();
103+
});
104+
if (textNode) {
105+
var originalText = textNode.textContent.trim();
106+
textNode.textContent = 'Copied!';
107+
btn.classList.add('copied');
108+
setTimeout(function() {
109+
textNode.textContent = originalText;
110+
btn.classList.remove('copied');
111+
}, 2000);
112+
}
113+
}
114+
}
115+
116+
// View markdown source
117+
function viewMarkdown() {
118+
// Get the current page path
119+
var path = window.location.pathname;
120+
// Remove leading/trailing slashes and .html extension
121+
path = path.replace(/^\/|\/$/g, '').replace(/\.html$/, '');
122+
123+
// Default to setup.md if path is empty or just 'setup'
124+
if (!path || path === 'setup') {
125+
path = 'setup';
126+
}
127+
128+
// Construct GitHub raw URL
129+
// Repository: malbeclabs/docs
130+
var repoOwner = 'malbeclabs';
131+
var repoName = 'docs';
132+
var branch = 'main';
133+
134+
// Try to construct GitHub raw URL
135+
var markdownUrl = 'https://raw.githubusercontent.com/' + repoOwner + '/' + repoName + '/' + branch + '/docs/' + path + '.md';
136+
137+
// Open in new tab
138+
window.open(markdownUrl, '_blank');
139+
}
140+
141+
// Ask in ChatGPT
142+
function askInChatGPT() {
143+
var content = getPageContent();
144+
var title = getPageTitle();
145+
var prompt = 'I\'m reading this documentation page: "' + title + '"\n\n' + content.substring(0, 3000) + (content.length > 3000 ? '...' : '');
146+
var url = 'https://chat.openai.com/?q=' + encodeURIComponent(prompt);
147+
window.open(url, '_blank');
148+
}
149+
150+
// Ask in Claude
151+
function askInClaude() {
152+
var content = getPageContent();
153+
var title = getPageTitle();
154+
var prompt = 'I\'m reading this documentation page: "' + title + '"\n\n' + content.substring(0, 3000) + (content.length > 3000 ? '...' : '');
155+
var url = 'https://claude.ai/new?prompt=' + encodeURIComponent(prompt);
156+
window.open(url, '_blank');
157+
}
158+
159+
// Initialize toolbar
160+
function initToolbar() {
161+
// Only add toolbar on setup page
162+
if (!isSetupPage()) {
163+
return;
164+
}
165+
166+
// Wait for page to be ready
167+
if (document.readyState === 'loading') {
168+
document.addEventListener('DOMContentLoaded', function() {
169+
addToolbar();
170+
});
171+
} else {
172+
addToolbar();
173+
}
174+
}
175+
176+
// Add toolbar to page
177+
function addToolbar() {
178+
// Create a fixed footer at the bottom of the screen
179+
var footer = document.createElement('footer');
180+
footer.className = 'page-toolbar-footer';
181+
182+
// Create and insert toolbar
183+
var toolbar = createToolbar();
184+
footer.appendChild(toolbar);
185+
186+
// Insert at the end of body
187+
document.body.appendChild(footer);
188+
189+
// Attach event listeners
190+
document.getElementById('copy-page-btn').addEventListener('click', function(e) {
191+
e.preventDefault();
192+
copyPageToClipboard();
193+
});
194+
document.getElementById('view-markdown-btn').addEventListener('click', function(e) {
195+
e.preventDefault();
196+
viewMarkdown();
197+
});
198+
document.getElementById('ask-chatgpt-btn').addEventListener('click', function(e) {
199+
e.preventDefault();
200+
askInChatGPT();
201+
});
202+
document.getElementById('ask-claude-btn').addEventListener('click', function(e) {
203+
e.preventDefault();
204+
askInClaude();
205+
});
206+
}
207+
208+
// Initialize when script loads
209+
initToolbar();
210+
})();

docs/stylesheets/extra.css

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,71 @@
11
:root > * {
22
--md-accent-fg-color: #16B5F4;
3-
}
3+
}
4+
5+
/* Page toolbar styles - text-based links like Pyth docs */
6+
.page-toolbar-footer {
7+
position: fixed;
8+
bottom: 0;
9+
left: 0;
10+
right: 0;
11+
background: var(--md-default-bg-color, #1e1e1e);
12+
border-top: 1px solid rgba(255, 255, 255, 0.1);
13+
z-index: 100;
14+
padding: 0;
15+
}
16+
17+
.page-toolbar {
18+
display: flex;
19+
align-items: center;
20+
justify-content: center;
21+
gap: 0.5rem;
22+
padding: 0.75rem 1rem;
23+
font-size: 0.875rem;
24+
color: #ffffff;
25+
max-width: 100%;
26+
margin: 0 auto;
27+
}
28+
29+
.page-toolbar-link {
30+
display: inline-flex;
31+
align-items: center;
32+
gap: 0.375rem;
33+
color: #ffffff;
34+
text-decoration: none;
35+
cursor: pointer;
36+
transition: opacity 0.2s ease;
37+
}
38+
39+
.page-toolbar-link:hover {
40+
text-decoration: underline;
41+
opacity: 0.8;
42+
}
43+
44+
.page-toolbar-link.copied {
45+
color: #ffffff;
46+
font-weight: 500;
47+
}
48+
49+
.page-toolbar-icon {
50+
width: 16px;
51+
height: 16px;
52+
flex-shrink: 0;
53+
vertical-align: middle;
54+
opacity: 0.9;
55+
}
56+
57+
.page-toolbar-favicon {
58+
filter: brightness(0) invert(1);
59+
}
60+
61+
.page-toolbar-separator {
62+
color: rgba(255, 255, 255, 0.5);
63+
user-select: none;
64+
}
65+
66+
/* Responsive design for smaller screens */
67+
@media screen and (max-width: 768px) {
68+
.page-toolbar {
69+
flex-wrap: wrap;
70+
}
71+
}

mkdocs.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,5 +62,6 @@ extra_javascript:
6262
- javascripts/cloudsmith-versions.js
6363
- javascripts/connection-wizard.js
6464
- javascripts/mathjax.js
65+
- javascripts/page-toolbar.js
6566
- https://polyfill.io/v3/polyfill.min.js?features=es6
6667
- https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js

0 commit comments

Comments
 (0)