Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
b953e53
Add T-Maze Active Inference example
skoghoern Feb 22, 2026
21bc635
Merge branch 'main' of https://github.com/ReactiveBayes/RxInferExampl…
skoghoern Feb 22, 2026
5915a3c
Add EFE Minimization via Message Passing notebook
meditans Feb 12, 2026
2b9c524
EFEasVFE example: make dims of obs tensor generic
meditans Feb 27, 2026
4696404
feat: Added edits to enable limited searching across docs.rxinfer.com…
ofSingularMind Mar 10, 2026
d337f07
Merge branch 'main' into cross_reference_RxInfer_Examples_B
ofSingularMind Mar 10, 2026
fea1d44
fix: formatting
ofSingularMind Mar 10, 2026
4e0d614
fix: cross-site search working (as secondary search results)
ofSingularMind Mar 10, 2026
c500e0e
fix: on make docs-serve, page was constantly refreshing. Now not.
ofSingularMind Mar 10, 2026
861bf41
fix: there was a silent bug. Resolved.
ofSingularMind Mar 10, 2026
fede0c4
EFEasVFE example: address code review
meditans Mar 11, 2026
b1ed4e4
feat: add context window and term highlighting, easy peasy
ofSingularMind Mar 11, 2026
81e9ee7
feat: add GraphPPL and ReactiveMP search results. Also add n of N sea…
ofSingularMind Mar 12, 2026
3b991ef
Merge pull request #75 from ReactiveBayes/cross_reference_RxInfer_Exa…
bvdmitri Mar 12, 2026
9efa908
remove isdefined checks
bvdmitri Mar 12, 2026
eb307a8
Merge branch 'main' into main
bvdmitri Mar 12, 2026
155365e
Merge branch 'main' into LiveServer_Fix_ReloadBug
bvdmitri Mar 13, 2026
0c56ebf
Merge pull request #74 from ReactiveBayes/LiveServer_Fix_ReloadBug
bvdmitri Mar 13, 2026
83f1093
Merge pull request #73 from meditans/main
bvdmitri Mar 16, 2026
dc0c898
Only updated notation (1.) so far
skoghoern Mar 16, 2026
25b3847
gif
skoghoern Mar 16, 2026
0d6d211
Add T-Maze Active Inference example
skoghoern Feb 22, 2026
d1434b3
Only updated notation (1.) so far
skoghoern Mar 16, 2026
7775e15
gif
skoghoern Mar 16, 2026
55c9dad
Merge branch 'main' of https://github.com/skoghoern/RxInferExamples.jl
skoghoern Mar 20, 2026
6d8b079
Updated tutorial, added FactorGraph, Meta version and callback version
skoghoern Mar 25, 2026
f29448f
notebook versions without local env activation
skoghoern Mar 26, 2026
32380f0
Delete examples/Basic Examples/T-Maze Active Inference/T-Maze_Active_…
skoghoern Mar 30, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions docs/make.jl
Original file line number Diff line number Diff line change
Expand Up @@ -212,9 +212,9 @@ function generate_examples_list()
push!(get!(categories, folder, []), meta)
end

# Generate markdown content
# Generate markdown content in-memory and write only on changes.
output_path = joinpath(AUTOGEN_DIR, "list_of_examples.md")
open(output_path, "w") do io
io = IOBuffer()
# Add theme styles at the top
write(io, THEME_STYLES)

Expand Down Expand Up @@ -327,7 +327,12 @@ function generate_examples_list()
end
end

write(io, ALL_EXAMPLES_CONTRIBUTING_NOTE)
write(io, ALL_EXAMPLES_CONTRIBUTING_NOTE)

new_content = String(take!(io))
old_content = isfile(output_path) ? read(output_path, String) : ""
if new_content != old_content
write(output_path, new_content)
end
end

Expand Down Expand Up @@ -759,4 +764,4 @@ if get(ENV, "CI", nothing) == "true"
versions=nothing,
cname="examples.rxinfer.com"
)
end
end
209 changes: 166 additions & 43 deletions docs/src/assets/header.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,9 @@ window.onload = function() {
})())

const documenterTarget = document.querySelector('#documenter');
documenterTarget.parentNode.insertBefore(header, documenterTarget);
if (documenterTarget && documenterTarget.parentNode) {
documenterTarget.parentNode.insertBefore(header, documenterTarget);
}

// === Site context banner for Docs ===
// Add banner directly to navbar after header is created
Expand All @@ -104,48 +106,169 @@ window.onload = function() {
}
}

// === Search results banner ===
// Add banner inside modal-card-head at the bottom when search results appear
function addSearchBanner() {
const searchModal = document.getElementById('search-modal');
if (!searchModal) {
return;
}

document.addEventListener('DOMContentLoaded', function() {
// === Cross-site search across external docs ===
(function () {
const REMOTE_SOURCES = [
{ base: 'https://docs.rxinfer.com/stable', label: 'RxInfer Docs', index: null, promise: null },
{ base: 'https://reactivebayes.github.io/ReactiveMP.jl/stable', label: 'ReactiveMP Docs', index: null, promise: null },
{ base: 'https://reactivebayes.github.io/GraphPPL.jl/stable', label: 'GraphPPL Docs', index: null, promise: null },
];

function esc(s) {
return (s || '').replace(/[&<>"']/g, c =>
({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[c]));
}

const modalCardHead = searchModal.querySelector('.modal-card-head');
if (!modalCardHead) {
return;

async function fetchRemoteIndex(source) {
if (source.index !== null) return;
if (source.promise) return source.promise;

source.promise = (async () => {
try {
const res = await fetch(source.base.replace(/\/+$/, '') + '/search_index.js');
if (!res.ok) return;
const text = await res.text();
const start = text.indexOf('{');
const end = text.lastIndexOf('}');
if (start < 0 || end < start) throw new Error('Invalid format');
source.index = JSON.parse(text.slice(start, end + 1)).docs || [];
} catch (e) {
source.index = [];
}
})();

return source.promise;
}

// Check if banner already exists
if (modalCardHead.querySelector('#search-results-banner')) {
return;

async function fetchAllRemoteIndices() {
await Promise.all(REMOTE_SOURCES.map(fetchRemoteIndex));
}

// Create a wrapper div for the banner
const bannerWrapper = document.createElement('div');
bannerWrapper.style.cssText = 'width: 100%; position: absolute; bottom: 0; display: flex; justify-content: center; align-items: center;';
bannerWrapper.id = 'search-results-banner';
bannerWrapper.className = 'is-size-7';
bannerWrapper.innerHTML = `
<strong>Note:</strong>&nbsp;Search results do not include the&nbsp;<a href="https://docs.rxinfer.com/">documentation website</a>
`;

// Insert after all existing children in modal-card-head
modalCardHead.appendChild(bannerWrapper);
}

// Watch for search modal and results to appear (search results are dynamically loaded)
const observer = new MutationObserver(function(mutations) {
addSearchBanner();
});

// Start observing the document body for changes
observer.observe(document.body, {
childList: true,
subtree: true
});

// Also try immediately in case search modal already exists
setTimeout(addSearchBanner, 100);
}

function searchRemote(source, query) {
if (!source.index || !source.index.length) return [];
const words = query.trim().toLowerCase().split(/\s+/).filter(w => w.length > 1);
if (!words.length) return [];
return source.index.filter(doc => {
const hay = (doc.title + ' ' + doc.text).toLowerCase();
return words.every(w => hay.includes(w));
});
}

function extractSnippet(text, query, contextLength = 80) {
const words = query.trim().toLowerCase().split(/\s+/).filter(w => w.length > 1);
if (!words.length || !text) return '';

const lowerText = text.toLowerCase();
let firstMatchIdx = -1;
for (const word of words) {
const idx = lowerText.indexOf(word);
if (idx !== -1 && (firstMatchIdx === -1 || idx < firstMatchIdx)) {
firstMatchIdx = idx;
}
}

if (firstMatchIdx === -1) return '';

const start = Math.max(0, firstMatchIdx - contextLength);
const end = Math.min(text.length, firstMatchIdx + contextLength);
let snippet = text.slice(start, end);
if (start > 0) snippet = '…' + snippet;
if (end < text.length) snippet = snippet + '…';

for (const word of words) {
const regex = new RegExp(`(${word.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})`, 'gi');
snippet = snippet.replace(regex, '<mark style="background-color:var(--mark-bg,#fff3cd);padding:0 2px">$1</mark>');
}

return snippet;
}

function renderSourceResults(source, results, totalCount, query) {
const items = results.map(doc => {
const snippet = extractSnippet(doc.text, query, 80);
return `
<a href="${source.base.replace(/\/+$/, '')}/${doc.location || ''}" class="search-result-link w-100 is-flex is-flex-direction-column gap-2 px-4 py-2">
<div class="w-100 is-flex is-flex-wrap-wrap is-justify-content-space-between is-align-items-flex-start">
<div class="search-result-title has-text-weight-bold">${esc(doc.title)}</div>
<div class="property-search-result-badge">${esc(doc.category)}</div>
</div>
${snippet ? `<div style="font-size:smaller;opacity:0.8;line-height:1.5">${snippet}</div>` : ''}
<div class="has-text-left" style="font-size:smaller;opacity:0.7">
<i class="fas fa-external-link-alt"></i> ${source.label}: ${esc((doc.location || '').slice(0, 60))}
</div>
</a>
<div class="search-divider w-100"></div>`;
}).join('');

const countText = totalCount > results.length
? `${results.length} of ${totalCount} results`
: `${results.length} result${results.length !== 1 ? 's' : ''}`;

return `
<div style="padding:0.5rem 1rem;border-top:1px solid var(--card-border-color,#e9ecef);margin-top:0.5rem">
<span class="is-size-7" style="opacity:0.7">Also from <strong>${source.label}</strong> — ${countText}</span>
</div>
${items}`;
}

function renderResults(sections, query) {
const html = sections.map(section =>
renderSourceResults(section.source, section.results, section.totalCount, query)
).join('');
return `<div id="cross-site-results" class="w-100 is-flex is-flex-direction-column gap-2">${html}</div>`;
}

let injecting = false;

function inject() {
if (injecting) return;
const body = document.querySelector('.search-modal-card-body');
const input = document.querySelector('.documenter-search-input');
if (!body || !input || body.querySelector('#cross-site-results')) return;

if (REMOTE_SOURCES.some(source => source.index === null)) {
fetchAllRemoteIndices().then(() => setTimeout(inject, 0));
return;
}

const query = input.value || '';
if (query.trim().length < 2) return;

const sections = REMOTE_SOURCES.map(source => {
const fullResults = searchRemote(source, query);
return {
source,
totalCount: fullResults.length,
results: fullResults.slice(0, 8),
};
}).filter(section => section.totalCount > 0);

if (!sections.length) return;

injecting = true;
body.insertAdjacentHTML('beforeend', renderResults(sections, query));
injecting = false;
}

let bodyObserver = null;
function connectBodyObserver() {
const body = document.querySelector('.search-modal-card-body');
if (!body || bodyObserver) return;
bodyObserver = new MutationObserver(() => { if (!injecting) setTimeout(inject, 30); });
bodyObserver.observe(body, { childList: true });
}

new MutationObserver(() => {
const modal = document.getElementById('search-modal');
if (modal) {
if (modal.classList.contains('is-active')) fetchAllRemoteIndices();
connectBodyObserver();
}
}).observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['class'] });

fetchAllRemoteIndices();
})();
});
Loading
Loading