Compare commits
6 Commits
6359aefd18
...
reese/add-
| Author | SHA1 | Date | |
|---|---|---|---|
|
4fa64cfc70
|
|||
|
e09ad1edf2
|
|||
|
d0c84aef7f
|
|||
|
61bb818140
|
|||
|
b1b81a04ed
|
|||
|
9042468fe8
|
176
css/style.css
176
css/style.css
@@ -413,7 +413,6 @@ footer {
|
||||
padding: 1.8rem;
|
||||
font-size: 0.9rem;
|
||||
box-shadow: 0 -4px 20px rgba(255,20,147,0.3);
|
||||
margin-left: 240px;
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
@@ -1615,3 +1614,178 @@ footer {
|
||||
background: linear-gradient(135deg, var(--pink-500), #1a1a3e);
|
||||
}
|
||||
|
||||
/* Search */
|
||||
.sidebar-search {
|
||||
position: relative;
|
||||
margin-top: 0.5rem;
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.sidebar-search-input {
|
||||
width: 100%;
|
||||
padding: 0.55rem 0.8rem;
|
||||
border: 2px solid rgba(255,255,255,0.15);
|
||||
border-radius: 10px;
|
||||
font-size: 0.85rem;
|
||||
color: var(--white);
|
||||
background: rgba(0,0,0,0.2);
|
||||
font-family: inherit;
|
||||
outline: none;
|
||||
transition: border-color 0.2s, background 0.2s;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.sidebar-search-input::placeholder {
|
||||
color: rgba(255,255,255,0.4);
|
||||
}
|
||||
|
||||
.sidebar-search-input:focus {
|
||||
border-color: var(--pink-400);
|
||||
background: rgba(0,0,0,0.3);
|
||||
}
|
||||
|
||||
.search-results-dropdown {
|
||||
position: fixed;
|
||||
background: var(--white);
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 10px 40px rgba(0,0,0,0.2), 0 4px 10px rgba(255,20,147,0.15);
|
||||
border: 2px solid var(--pink-200);
|
||||
z-index: 9999;
|
||||
display: none;
|
||||
max-height: 400px;
|
||||
overflow-y: auto;
|
||||
margin-top: 0.3rem;
|
||||
}
|
||||
|
||||
.search-result-item {
|
||||
padding: 0.75rem 0.9rem;
|
||||
cursor: pointer;
|
||||
border-bottom: 1px solid var(--pink-100);
|
||||
transition: background 0.15s;
|
||||
}
|
||||
|
||||
.search-result-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.search-result-item:hover,
|
||||
.search-result-item.active {
|
||||
background: var(--pink-50);
|
||||
}
|
||||
|
||||
.search-result-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.4rem;
|
||||
margin-bottom: 0.2rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.search-result-page {
|
||||
font-size: 0.72rem;
|
||||
font-weight: 700;
|
||||
color: var(--pink-500);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.3px;
|
||||
}
|
||||
|
||||
.search-result-category {
|
||||
font-size: 0.68rem;
|
||||
font-weight: 600;
|
||||
color: var(--pink-400);
|
||||
background: var(--pink-100);
|
||||
padding: 0.1rem 0.45rem;
|
||||
border-radius: 999px;
|
||||
}
|
||||
|
||||
.search-result-heading {
|
||||
font-size: 0.92rem;
|
||||
font-weight: 700;
|
||||
color: var(--pink-700);
|
||||
margin-bottom: 0.15rem;
|
||||
}
|
||||
|
||||
.search-result-snippet {
|
||||
font-size: 0.82rem;
|
||||
color: var(--pink-800);
|
||||
line-height: 1.4;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
|
||||
.search-no-results {
|
||||
padding: 1rem 0.9rem;
|
||||
font-size: 0.88rem;
|
||||
color: var(--pink-400);
|
||||
text-align: center;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.search-highlight {
|
||||
background: var(--pink-200);
|
||||
color: var(--pink-900);
|
||||
font-weight: 700;
|
||||
border-radius: 2px;
|
||||
padding: 0 0.1rem;
|
||||
}
|
||||
|
||||
/* Dark mode search styles */
|
||||
[data-theme="dark"] .sidebar-search-input {
|
||||
border-color: rgba(255,255,255,0.1);
|
||||
color: var(--text-primary);
|
||||
background: rgba(255,255,255,0.05);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .sidebar-search-input::placeholder {
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .sidebar-search-input:focus {
|
||||
border-color: var(--border-accent);
|
||||
background: rgba(255,255,255,0.08);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .search-results-dropdown {
|
||||
background: var(--bg-card);
|
||||
border-color: var(--border-primary);
|
||||
box-shadow: 0 10px 40px rgba(0,0,0,0.4);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .search-result-item {
|
||||
border-bottom-color: var(--border-primary);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .search-result-item:hover,
|
||||
[data-theme="dark"] .search-result-item.active {
|
||||
background: var(--bg-secondary);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .search-result-page {
|
||||
color: var(--text-heading-strong);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .search-result-category {
|
||||
color: var(--text-heading);
|
||||
background: rgba(255,20,147,0.1);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .search-result-heading {
|
||||
color: var(--text-heading);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .search-result-snippet {
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .search-no-results {
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .search-highlight {
|
||||
background: rgba(255,20,147,0.2);
|
||||
color: var(--text-heading);
|
||||
}
|
||||
|
||||
|
||||
@@ -157,9 +157,13 @@
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="search-results-dropdown" id="searchResultsContainer"></div>
|
||||
|
||||
<footer>AI Cheat Sheet — A learning reference for artificial intelligence</footer>
|
||||
|
||||
<script src="lib/llm.js"></script>
|
||||
<script src="lib/search.js"></script>
|
||||
<script>Search.init();</script>
|
||||
<script>
|
||||
(function(){
|
||||
function askQuestion() {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// Shared LLM module - provides streaming chat to any page
|
||||
var LLM = (function() {
|
||||
var defaultApiUrl = 'https://llama-instruct.reeselink.com/v1';
|
||||
var defaultModel = 'instruct';
|
||||
|
||||
69
lib/marked.min.js
vendored
Normal file
69
lib/marked.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
566
lib/search.js
Normal file
566
lib/search.js
Normal file
@@ -0,0 +1,566 @@
|
||||
var Search = (function() {
|
||||
var dropdownVisible = false;
|
||||
var selectedIndex = -1;
|
||||
var results = [];
|
||||
var debounceTimer = null;
|
||||
var currentInput = null;
|
||||
|
||||
// Page title mapping
|
||||
var PAGE_TITLES = {
|
||||
'index.html': 'Home',
|
||||
'terminology.html': 'Terminology',
|
||||
'techniques.html': 'Techniques',
|
||||
'use-cases.html': 'Use Cases',
|
||||
'model-types.html': 'Model Types',
|
||||
'prompts.html': 'Prompt Guide',
|
||||
'math.html': 'Math & Concepts',
|
||||
'chat.html': 'Chat',
|
||||
'image-gen.html': 'Image Gen'
|
||||
};
|
||||
|
||||
// Content to skip during crawling
|
||||
var SKIP_CONTENT = [
|
||||
'AI Cheat Sheet',
|
||||
'Toggle dark mode',
|
||||
'Toggle menu',
|
||||
'Send',
|
||||
'Ask me anything about AI',
|
||||
'Ask a Question',
|
||||
'Ask anything about AI',
|
||||
'Powered by your configured LLM',
|
||||
'Try AI right now',
|
||||
'Full Chat',
|
||||
'Generate',
|
||||
'Clear',
|
||||
'Image Generation',
|
||||
'Enter image prompt'
|
||||
];
|
||||
|
||||
function assignAnchors() {
|
||||
var defCards = document.querySelectorAll('.def-card');
|
||||
defCards.forEach(function(card) {
|
||||
var h3 = card.querySelector('h3');
|
||||
if (h3) {
|
||||
var id = slugify(h3.textContent.trim());
|
||||
if (id) card.setAttribute('id', id);
|
||||
}
|
||||
});
|
||||
|
||||
var tables = document.querySelectorAll('.glossary-table tbody tr');
|
||||
tables.forEach(function(row) {
|
||||
var firstTd = row.querySelector('td');
|
||||
if (firstTd) {
|
||||
var id = slugify(firstTd.textContent.trim());
|
||||
if (id) row.setAttribute('id', id);
|
||||
}
|
||||
});
|
||||
|
||||
var useGrid = document.querySelector('.use-grid');
|
||||
if (useGrid) {
|
||||
var useCards = useGrid.querySelectorAll('.use-card');
|
||||
useCards.forEach(function(card) {
|
||||
var h3 = card.querySelector('h3');
|
||||
if (h3) {
|
||||
var id = slugify(h3.textContent.trim());
|
||||
if (id) card.setAttribute('id', id);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var promptBlocks = document.querySelectorAll('.prompt-block');
|
||||
promptBlocks.forEach(function(block) {
|
||||
var h3 = block.querySelector('h3');
|
||||
if (h3) {
|
||||
var id = slugify(h3.textContent.trim());
|
||||
if (id) block.setAttribute('id', id);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function init() {
|
||||
var navInner = document.querySelector('.nav-inner');
|
||||
if (!navInner) return;
|
||||
|
||||
var brand = navInner.querySelector('.nav-brand');
|
||||
if (!brand) return;
|
||||
|
||||
assignAnchors();
|
||||
|
||||
// Create search wrapper
|
||||
var searchWrapper = document.createElement('div');
|
||||
searchWrapper.className = 'sidebar-search';
|
||||
searchWrapper.style.cssText = 'position:relative; margin-top:0.5rem; margin-bottom:0.25rem;';
|
||||
|
||||
var searchInput = document.createElement('input');
|
||||
searchInput.type = 'text';
|
||||
searchInput.className = 'sidebar-search-input';
|
||||
searchInput.placeholder = 'Search...';
|
||||
searchInput.setAttribute('aria-label', 'Search all pages');
|
||||
searchInput.autocomplete = 'off';
|
||||
|
||||
searchWrapper.appendChild(searchInput);
|
||||
|
||||
// Insert after brand
|
||||
brand.parentNode.insertBefore(searchWrapper, brand.nextSibling);
|
||||
|
||||
currentInput = searchInput;
|
||||
|
||||
// Event listeners
|
||||
searchInput.addEventListener('input', function(e) {
|
||||
clearTimeout(debounceTimer);
|
||||
var query = e.target.value.trim();
|
||||
if (query.length === 0) {
|
||||
hideDropdown();
|
||||
return;
|
||||
}
|
||||
debounceTimer = setTimeout(function() {
|
||||
performSearch(query);
|
||||
}, 150);
|
||||
});
|
||||
|
||||
searchInput.addEventListener('keydown', function(e) {
|
||||
if (!dropdownVisible || results.length === 0) return;
|
||||
|
||||
if (e.key === 'ArrowDown') {
|
||||
e.preventDefault();
|
||||
selectedIndex = Math.min(selectedIndex + 1, results.length - 1);
|
||||
updateSelection();
|
||||
} else if (e.key === 'ArrowUp') {
|
||||
e.preventDefault();
|
||||
selectedIndex = Math.max(selectedIndex - 1, 0);
|
||||
updateSelection();
|
||||
} else if (e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
if (selectedIndex >= 0 && selectedIndex < results.length) {
|
||||
navigateToResult(results[selectedIndex]);
|
||||
} else if (results.length > 0) {
|
||||
navigateToResult(results[0]);
|
||||
}
|
||||
} else if (e.key === 'Escape') {
|
||||
e.preventDefault();
|
||||
hideDropdown();
|
||||
searchInput.blur();
|
||||
}
|
||||
});
|
||||
|
||||
searchInput.addEventListener('focus', function() {
|
||||
if (results.length > 0 && currentInput.value.trim().length > 0) {
|
||||
showDropdown();
|
||||
}
|
||||
});
|
||||
|
||||
// Close dropdown when clicking outside
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!searchWrapper.contains(e.target)) {
|
||||
hideDropdown();
|
||||
}
|
||||
});
|
||||
|
||||
// Reposition on scroll and resize
|
||||
function positionDropdown() {
|
||||
var container = document.getElementById('searchResultsContainer');
|
||||
if (!container || !dropdownVisible) return;
|
||||
var rect = currentInput.getBoundingClientRect();
|
||||
var vw = window.innerWidth;
|
||||
container.style.top = (rect.bottom + 5) + 'px';
|
||||
if (vw <= 900) {
|
||||
container.style.left = '0';
|
||||
container.style.width = '100vw';
|
||||
} else {
|
||||
container.style.left = rect.left + 'px';
|
||||
container.style.width = '520px';
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener('scroll', positionDropdown);
|
||||
window.addEventListener('resize', positionDropdown);
|
||||
}
|
||||
|
||||
function crawlPage(url, callback) {
|
||||
fetch(url)
|
||||
.then(function(response) { return response.text(); })
|
||||
.then(function(html) {
|
||||
var parser = new DOMParser();
|
||||
var doc = parser.parseFromString(html, 'text/html');
|
||||
var results = [];
|
||||
|
||||
// Get page title
|
||||
var pageTitle = PAGE_TITLES[doc.title.replace(' - Cheat Sheet', '').replace('AI Cheat Sheet', '').trim()] || doc.title;
|
||||
|
||||
// Extract def-cards
|
||||
var defCards = doc.querySelectorAll('.def-card');
|
||||
defCards.forEach(function(card) {
|
||||
var heading = card.querySelector('h3');
|
||||
var p = card.querySelector('p');
|
||||
var example = card.querySelector('.example');
|
||||
var category = card.querySelector('.category');
|
||||
|
||||
if (!heading || !p) return;
|
||||
|
||||
var headingText = heading.textContent.trim();
|
||||
var defText = p.textContent.trim();
|
||||
var catText = category ? category.textContent.trim() : '';
|
||||
var exText = example ? example.textContent.trim() : '';
|
||||
|
||||
var fullText = (catText + ' ' + headingText + ' ' + defText + ' ' + exText).trim();
|
||||
if (fullText.length < 10) return;
|
||||
|
||||
// Skip boilerplate content
|
||||
var skip = false;
|
||||
for (var i = 0; i < SKIP_CONTENT.length; i++) {
|
||||
if (fullText.indexOf(SKIP_CONTENT[i]) === 0) { skip = true; break; }
|
||||
}
|
||||
if (skip) return;
|
||||
|
||||
results.push({
|
||||
page: pageTitle,
|
||||
url: url,
|
||||
heading: headingText,
|
||||
category: catText,
|
||||
snippet: defText,
|
||||
fullText: fullText.toLowerCase(),
|
||||
anchor: slugify(headingText)
|
||||
});
|
||||
});
|
||||
|
||||
// Extract table rows
|
||||
var tables = doc.querySelectorAll('.glossary-table tbody tr');
|
||||
tables.forEach(function(row) {
|
||||
var tds = row.querySelectorAll('td');
|
||||
if (tds.length < 2) return;
|
||||
|
||||
var acronym = tds[0].textContent.trim();
|
||||
var meaning = tds[1].textContent.trim();
|
||||
|
||||
results.push({
|
||||
page: pageTitle,
|
||||
url: url,
|
||||
heading: acronym,
|
||||
category: 'Acronym',
|
||||
snippet: meaning,
|
||||
fullText: (acronym + ' ' + meaning).toLowerCase(),
|
||||
anchor: slugify(acronym)
|
||||
});
|
||||
});
|
||||
|
||||
// Extract section headings for pages without def-cards (like use-cases, model-types)
|
||||
var sections = doc.querySelectorAll('h2.section-title');
|
||||
sections.forEach(function(h2) {
|
||||
var sectionName = h2.textContent.trim();
|
||||
// Check if there are cards under this section
|
||||
var next = h2.nextElementSibling;
|
||||
var cards = [];
|
||||
while (next && !next.classList.contains('section-title')) {
|
||||
if (next.classList && (next.classList.contains('card') || next.classList.contains('use-card') || next.classList.contains('prompt-block') || next.classList.contains('def-card'))) {
|
||||
cards.push(next);
|
||||
}
|
||||
next = next.nextElementSibling;
|
||||
}
|
||||
|
||||
cards.forEach(function(card) {
|
||||
var h3 = card.querySelector('h3');
|
||||
var p = card.querySelector('p');
|
||||
if (!h3 || !p) return;
|
||||
|
||||
var headingText = h3.textContent.trim();
|
||||
var cardText = p.textContent.trim();
|
||||
|
||||
// Skip if already captured as def-card
|
||||
if (card.classList.contains('def-card')) return;
|
||||
|
||||
results.push({
|
||||
page: pageTitle,
|
||||
url: url,
|
||||
heading: headingText,
|
||||
category: sectionName,
|
||||
snippet: cardText,
|
||||
fullText: (sectionName + ' ' + headingText + ' ' + cardText).toLowerCase(),
|
||||
anchor: slugify(headingText)
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// For pages with .use-grid, extract use-cards
|
||||
var useGrid = doc.querySelector('.use-grid');
|
||||
if (useGrid) {
|
||||
var useCards = useGrid.querySelectorAll('.use-card');
|
||||
useCards.forEach(function(card) {
|
||||
var h3 = card.querySelector('h3');
|
||||
var p = card.querySelector('p');
|
||||
if (!h3 || !p) return;
|
||||
var headingText = h3.textContent.trim();
|
||||
var cardText = p.textContent.trim();
|
||||
|
||||
results.push({
|
||||
page: pageTitle,
|
||||
url: url,
|
||||
heading: headingText,
|
||||
category: 'Use Case',
|
||||
snippet: cardText,
|
||||
fullText: (headingText + ' ' + cardText).toLowerCase(),
|
||||
anchor: slugify(headingText)
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// For prompt guide, extract prompt-blocks
|
||||
var promptBlocks = doc.querySelectorAll('.prompt-block');
|
||||
if (promptBlocks.length > 0) {
|
||||
promptBlocks.forEach(function(block) {
|
||||
var h3 = block.querySelector('h3');
|
||||
var label = block.querySelector('.label');
|
||||
var p = block.querySelector('p');
|
||||
|
||||
if (!h3) return;
|
||||
var headingText = h3.textContent.trim();
|
||||
var labelText = label ? label.textContent.trim() : '';
|
||||
var cardText = p ? p.textContent.trim() : '';
|
||||
|
||||
results.push({
|
||||
page: pageTitle,
|
||||
url: url,
|
||||
heading: headingText,
|
||||
category: labelText || 'Prompt',
|
||||
snippet: cardText,
|
||||
fullText: (headingText + ' ' + labelText + ' ' + cardText).toLowerCase(),
|
||||
anchor: slugify(headingText)
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// For math page, extract def-cards (already handled above)
|
||||
// For model-types, also grab comparison table
|
||||
var modelTables = doc.querySelectorAll('.glossary-table');
|
||||
if (modelTables.length > 0) {
|
||||
modelTables.forEach(function(table) {
|
||||
var rows = table.querySelectorAll('tbody tr');
|
||||
rows.forEach(function(row) {
|
||||
var tds = row.querySelectorAll('td');
|
||||
if (tds.length === 0) return;
|
||||
var rowText = '';
|
||||
for (var i = 0; i < tds.length; i++) {
|
||||
rowText += ' ' + tds[i].textContent.trim();
|
||||
}
|
||||
if (rowText.length < 5) return;
|
||||
|
||||
results.push({
|
||||
page: pageTitle,
|
||||
url: url,
|
||||
heading: tds[0] ? tds[0].textContent.trim() : 'Table Entry',
|
||||
category: 'Comparison',
|
||||
snippet: rowText.trim(),
|
||||
fullText: rowText.toLowerCase(),
|
||||
anchor: slugify(tds[0] ? tds[0].textContent.trim() : 'Table Entry')
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
callback(results);
|
||||
})
|
||||
.catch(function() {
|
||||
callback([]);
|
||||
});
|
||||
}
|
||||
|
||||
function crawlAllPages(callback) {
|
||||
var pages = [
|
||||
{ url: '/index.html', title: 'Home' },
|
||||
{ url: '/pages/terminology.html', title: 'Terminology' },
|
||||
{ url: '/pages/techniques.html', title: 'Techniques' },
|
||||
{ url: '/pages/use-cases.html', title: 'Use Cases' },
|
||||
{ url: '/pages/model-types.html', title: 'Model Types' },
|
||||
{ url: '/pages/prompts.html', title: 'Prompt Guide' },
|
||||
{ url: '/pages/math.html', title: 'Math & Concepts' }
|
||||
];
|
||||
|
||||
var allResults = [];
|
||||
var completed = 0;
|
||||
|
||||
if (pages.length === 0) {
|
||||
callback(allResults);
|
||||
return;
|
||||
}
|
||||
|
||||
pages.forEach(function(page) {
|
||||
crawlPage(page.url, function(results) {
|
||||
allResults = allResults.concat(results);
|
||||
completed++;
|
||||
if (completed === pages.length) {
|
||||
callback(allResults);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function performSearch(query) {
|
||||
var q = query.toLowerCase().trim();
|
||||
if (q.length === 0) { hideDropdown(); return; }
|
||||
|
||||
crawlAllPages(function(allResults) {
|
||||
searchResults(allResults, q);
|
||||
});
|
||||
}
|
||||
|
||||
function searchResults(allResults, q) {
|
||||
var qWords = q.split(/\s+/).filter(function(w) { return w.length > 0; });
|
||||
var scored = [];
|
||||
|
||||
for (var i = 0; i < allResults.length; i++) {
|
||||
var item = allResults[i];
|
||||
var text = item.fullText || '';
|
||||
var score = 0;
|
||||
|
||||
if (text.indexOf(q) !== -1) score += 100;
|
||||
|
||||
for (var j = 0; j < qWords.length; j++) {
|
||||
if (text.indexOf(qWords[j]) !== -1) score += 10;
|
||||
if (item.heading && item.heading.toLowerCase().indexOf(qWords[j]) === 0) score += 20;
|
||||
if (item.category && item.category.toLowerCase().indexOf(qWords[j]) !== -1) score += 5;
|
||||
}
|
||||
|
||||
if (score > 0) {
|
||||
scored.push({ item: item, score: score });
|
||||
}
|
||||
}
|
||||
|
||||
scored.sort(function(a, b) { return b.score - a.score; });
|
||||
var topResults = scored.slice(0, 10).map(function(s) { return s.item; });
|
||||
displayResults(topResults, q);
|
||||
}
|
||||
|
||||
function highlightText(text, query) {
|
||||
if (!text || !query) return escapeHTML(text);
|
||||
var escaped = escapeHTML(text);
|
||||
var qWords = query.toLowerCase().split(/\s+/).filter(function(w) { return w.length > 0; });
|
||||
|
||||
for (var i = 0; i < qWords.length; i++) {
|
||||
var word = qWords[i];
|
||||
var regex = new RegExp('(' + escapeRegex(word) + ')', 'gi');
|
||||
escaped = escaped.replace(regex, '<mark class="search-highlight">$1</mark>');
|
||||
}
|
||||
|
||||
return escaped;
|
||||
}
|
||||
|
||||
function escapeRegex(str) {
|
||||
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
}
|
||||
|
||||
function escapeHTML(str) {
|
||||
var div = document.createElement('div');
|
||||
div.textContent = str;
|
||||
return div.innerHTML;
|
||||
}
|
||||
|
||||
function slugify(text) {
|
||||
return text
|
||||
.toLowerCase()
|
||||
.replace(/\(.*?\)/g, function(match) { return match.replace(/[()]/g, ''); })
|
||||
.replace(/[^a-z0-9]+/g, '-')
|
||||
.replace(/^-+|-+$/g, '');
|
||||
}
|
||||
|
||||
function displayResults(foundResults, query) {
|
||||
results = foundResults;
|
||||
selectedIndex = -1;
|
||||
|
||||
if (results.length === 0) {
|
||||
showDropdown();
|
||||
var container = document.getElementById('searchResultsContainer');
|
||||
if (container) {
|
||||
container.innerHTML = '<div class="search-no-results">No results found for "' + escapeHTML(query) + '"</div>';
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
var container = document.getElementById('searchResultsContainer');
|
||||
var html = '';
|
||||
for (var i = 0; i < results.length; i++) {
|
||||
var r = results[i];
|
||||
var categoryLabel = r.category ? '<span class="search-result-category">' + escapeHTML(r.category) + '</span>' : '';
|
||||
var highlightedSnippet = highlightText(r.snippet, query);
|
||||
|
||||
html += '<div class="search-result-item" data-index="' + i + '" data-url="' + escapeHTML(r.url) + '" data-anchor="' + escapeHTML(r.anchor || '') + '">';
|
||||
html += '<div class="search-result-title">';
|
||||
html += '<span class="search-result-page">' + escapeHTML(r.page) + '</span>';
|
||||
html += categoryLabel;
|
||||
html += '</div>';
|
||||
html += '<div class="search-result-heading">' + highlightText(r.heading, query) + '</div>';
|
||||
html += '<div class="search-result-snippet">' + highlightedSnippet + '</div>';
|
||||
html += '</div>';
|
||||
}
|
||||
container.innerHTML = html;
|
||||
showDropdown();
|
||||
|
||||
// Attach click handlers
|
||||
var items = container.querySelectorAll('.search-result-item');
|
||||
for (var j = 0; j < items.length; j++) {
|
||||
items[j].addEventListener('click', function() {
|
||||
var url = this.getAttribute('data-url');
|
||||
var anchor = this.getAttribute('data-anchor');
|
||||
navigateToResult({ url: url, anchor: anchor });
|
||||
});
|
||||
items[j].addEventListener('mouseenter', function() {
|
||||
var idx = parseInt(this.getAttribute('data-index'), 10);
|
||||
selectedIndex = idx;
|
||||
updateSelection();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function updateSelection() {
|
||||
var items = document.querySelectorAll('.search-result-item');
|
||||
for (var i = 0; i < items.length; i++) {
|
||||
if (i === selectedIndex) {
|
||||
items[i].classList.add('active');
|
||||
items[i].scrollIntoView({ block: 'nearest' });
|
||||
} else {
|
||||
items[i].classList.remove('active');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function showDropdown() {
|
||||
var container = document.getElementById('searchResultsContainer');
|
||||
if (container) {
|
||||
var rect = currentInput.getBoundingClientRect();
|
||||
var vw = window.innerWidth;
|
||||
var sidebarWidth = 240;
|
||||
|
||||
if (vw <= 900) {
|
||||
container.style.left = '0';
|
||||
container.style.width = '100vw';
|
||||
} else {
|
||||
container.style.left = rect.left + 'px';
|
||||
container.style.width = '520px';
|
||||
}
|
||||
container.style.top = (rect.bottom + 5) + 'px';
|
||||
container.style.display = 'block';
|
||||
dropdownVisible = true;
|
||||
}
|
||||
}
|
||||
|
||||
function hideDropdown() {
|
||||
var container = document.getElementById('searchResultsContainer');
|
||||
if (container) {
|
||||
container.style.display = 'none';
|
||||
}
|
||||
dropdownVisible = false;
|
||||
selectedIndex = -1;
|
||||
}
|
||||
|
||||
function navigateToResult(result) {
|
||||
hideDropdown();
|
||||
if (currentInput) currentInput.blur();
|
||||
var target = result.url;
|
||||
if (result.anchor) {
|
||||
target = result.url + '#' + result.anchor;
|
||||
}
|
||||
window.location.href = target;
|
||||
}
|
||||
|
||||
return {
|
||||
init: init
|
||||
};
|
||||
})();
|
||||
@@ -119,6 +119,12 @@
|
||||
|
||||
<footer>AI Cheat Sheet — A learning reference for artificial intelligence</footer>
|
||||
|
||||
<div class="search-results-dropdown" id="searchResultsContainer"></div>
|
||||
|
||||
<script src="../lib/search.js"></script>
|
||||
<script>Search.init();</script>
|
||||
<script src="../lib/marked.min.js"></script>
|
||||
|
||||
<script>
|
||||
(function(){
|
||||
var chatMessages = document.getElementById('chatMessages');
|
||||
@@ -129,6 +135,12 @@
|
||||
var apiTokenInput = document.getElementById('apiToken');
|
||||
var modelNameInput = document.getElementById('modelName');
|
||||
|
||||
// Parse markdown in initial welcome message
|
||||
var initialBubble = chatMessages.querySelector('.message-bubble p');
|
||||
if(initialBubble) {
|
||||
initialBubble.innerHTML = marked.parseInline(initialBubble.textContent);
|
||||
}
|
||||
|
||||
// Restore saved settings
|
||||
var savedToken = localStorage.getItem('apiToken');
|
||||
if(savedToken) apiTokenInput.value = savedToken;
|
||||
@@ -153,7 +165,11 @@
|
||||
|
||||
var p = document.createElement('p');
|
||||
p.className = 'message-content';
|
||||
p.textContent = content;
|
||||
if(role === 'assistant') {
|
||||
p.innerHTML = marked.parse(content);
|
||||
} else {
|
||||
p.textContent = content;
|
||||
}
|
||||
bubble.appendChild(p);
|
||||
|
||||
msgDiv.appendChild(avatar);
|
||||
@@ -271,7 +287,7 @@
|
||||
var delta = json.choices && json.choices[0] && json.choices[0].delta;
|
||||
if(delta && delta.content) {
|
||||
streamContent += delta.content;
|
||||
contentEl.textContent = streamContent;
|
||||
contentEl.innerHTML = marked.parse(streamContent);
|
||||
chatMessages.scrollTop = chatMessages.scrollHeight;
|
||||
}
|
||||
} catch(e) {
|
||||
@@ -313,7 +329,7 @@
|
||||
chatMessages.innerHTML = '';
|
||||
var welcomeMsg = document.createElement('div');
|
||||
welcomeMsg.className = 'message assistant-message';
|
||||
welcomeMsg.innerHTML = '<div class="message-avatar">AI</div><div class="message-bubble"><p>Hello! Chat cleared. How can I help you?</p></div>';
|
||||
welcomeMsg.innerHTML = '<div class="message-avatar">AI</div><div class="message-bubble"><p>' + marked.parse('Hello! Chat cleared. How can I help you?') + '</p></div>';
|
||||
chatMessages.appendChild(welcomeMsg);
|
||||
});
|
||||
|
||||
|
||||
@@ -115,6 +115,11 @@
|
||||
|
||||
<footer>AI Cheat Sheet — A learning reference for artificial intelligence</footer>
|
||||
|
||||
<div class="search-results-dropdown" id="searchResultsContainer"></div>
|
||||
|
||||
<script src="../lib/search.js"></script>
|
||||
<script>Search.init();</script>
|
||||
|
||||
<script>
|
||||
(function(){
|
||||
var apiUrlInput = document.getElementById('apiUrl');
|
||||
@@ -282,7 +287,7 @@
|
||||
prompt: prompt,
|
||||
model: model,
|
||||
n: 1,
|
||||
size: '1024x1024'
|
||||
size: '512x512'
|
||||
})
|
||||
})
|
||||
.then(function(response) {
|
||||
|
||||
@@ -260,8 +260,12 @@
|
||||
|
||||
<footer>AI Cheat Sheet — A learning reference for artificial intelligence</footer>
|
||||
|
||||
<div class="search-results-dropdown" id="searchResultsContainer"></div>
|
||||
|
||||
<script src="../lib/modal.js"></script>
|
||||
<script src="../lib/llm.js"></script>
|
||||
<script src="../lib/search.js"></script>
|
||||
<script>Search.init();</script>
|
||||
<script>
|
||||
(function(){
|
||||
function walkThrough(title, prompt) {
|
||||
|
||||
@@ -237,8 +237,12 @@
|
||||
|
||||
<footer>AI Cheat Sheet — A learning reference for artificial intelligence</footer>
|
||||
|
||||
<div class="search-results-dropdown" id="searchResultsContainer"></div>
|
||||
|
||||
<script src="../lib/modal.js"></script>
|
||||
<script src="../lib/llm.js"></script>
|
||||
<script src="../lib/search.js"></script>
|
||||
<script>Search.init();</script>
|
||||
<script>
|
||||
(function(){
|
||||
function explainModel(title, definition) {
|
||||
|
||||
@@ -236,8 +236,12 @@
|
||||
|
||||
<footer>AI Cheat Sheet — A learning reference for artificial intelligence</footer>
|
||||
|
||||
<div class="search-results-dropdown" id="searchResultsContainer"></div>
|
||||
|
||||
<script src="../lib/modal.js"></script>
|
||||
<script src="../lib/llm.js"></script>
|
||||
<script src="../lib/search.js"></script>
|
||||
<script>Search.init();</script>
|
||||
<script>
|
||||
(function(){
|
||||
var techniquePrompts = {
|
||||
|
||||
@@ -200,8 +200,12 @@
|
||||
|
||||
<footer>AI Cheat Sheet — A learning reference for artificial intelligence</footer>
|
||||
|
||||
<div class="search-results-dropdown" id="searchResultsContainer"></div>
|
||||
|
||||
<script src="../lib/modal.js"></script>
|
||||
<script src="../lib/llm.js"></script>
|
||||
<script src="../lib/search.js"></script>
|
||||
<script>Search.init();</script>
|
||||
<script>
|
||||
(function(){
|
||||
function demoTechnique(title, prompt) {
|
||||
|
||||
@@ -237,8 +237,12 @@
|
||||
|
||||
<footer>AI Cheat Sheet — A learning reference for artificial intelligence</footer>
|
||||
|
||||
<div class="search-results-dropdown" id="searchResultsContainer"></div>
|
||||
|
||||
<script src="../lib/modal.js"></script>
|
||||
<script src="../lib/llm.js"></script>
|
||||
<script src="../lib/search.js"></script>
|
||||
<script>Search.init();</script>
|
||||
<script>
|
||||
(function(){
|
||||
function explainTerm(title, definition) {
|
||||
|
||||
@@ -232,8 +232,12 @@
|
||||
|
||||
<footer>AI Cheat Sheet — A learning reference for artificial intelligence</footer>
|
||||
|
||||
<div class="search-results-dropdown" id="searchResultsContainer"></div>
|
||||
|
||||
<script src="../lib/modal.js"></script>
|
||||
<script src="../lib/llm.js"></script>
|
||||
<script src="../lib/search.js"></script>
|
||||
<script>Search.init();</script>
|
||||
<script>
|
||||
(function(){
|
||||
function brainstormUseCase(title, prompt) {
|
||||
|
||||
Reference in New Issue
Block a user