diff --git a/mempalace/backends/chroma.py b/mempalace/backends/chroma.py index afd8083..1a171c1 100644 --- a/mempalace/backends/chroma.py +++ b/mempalace/backends/chroma.py @@ -615,6 +615,8 @@ def _normalize_get_collection_args(args, kwargs): create = kwargs.pop("create", False) if rest: create = rest.pop(0) + if rest: + raise TypeError(f"unexpected positional args: {rest!r}") if kwargs: raise TypeError(f"unexpected kwargs: {sorted(kwargs)}") return ( diff --git a/mempalace/cli.py b/mempalace/cli.py index a5d3c7d..a4c8a88 100644 --- a/mempalace/cli.py +++ b/mempalace/cli.py @@ -179,12 +179,12 @@ def cmd_sweep(args): failures = result.get("failures") or [] if failures: print( - f" ⚠ {len(failures)} file(s) failed to sweep — see stderr / logs for details.", + f" WARNING: {len(failures)} file(s) failed to sweep - see stderr / logs for details.", file=sys.stderr, ) sys.exit(2) else: - print(f" ✗ Not a file or directory: {target}", file=sys.stderr) + print(f" ERROR: Not a file or directory: {target}", file=sys.stderr) sys.exit(1) diff --git a/mempalace/sweeper.py b/mempalace/sweeper.py index ce87153..51ea38d 100644 --- a/mempalace/sweeper.py +++ b/mempalace/sweeper.py @@ -321,7 +321,7 @@ def sweep_directory(dir_path: str, palace_path: str) -> dict: result = sweep(str(f), palace_path, source_label=str(f)) except Exception as exc: logger.error("sweeper: sweep failed on %s: %s", f, exc) - print(f" \u26a0 sweep failed on {f}: {exc}", file=sys.stderr) + print(f" WARNING: sweep failed on {f}: {exc}", file=sys.stderr) failures.append({"file": str(f), "error": str(exc)}) continue total_added += result["drawers_added"] diff --git a/website/.vitepress/config.mts b/website/.vitepress/config.mts index 6cfe855..49cf975 100644 --- a/website/.vitepress/config.mts +++ b/website/.vitepress/config.mts @@ -11,6 +11,7 @@ function normalizeBase(base?: string): string { const docsBase = normalizeBase(process.env.DOCS_BASE || '/') const editBranch = process.env.DOCS_EDIT_BRANCH || 'main' +const gaId = process.env.MEMPALACE_DOCS_GA_ID export default withMermaid( defineConfig({ @@ -26,8 +27,10 @@ export default withMermaid( ['meta', { property: 'og:title', content: 'MemPalace — AI Memory System' }], ['meta', { property: 'og:description', content: '96.6% LongMemEval recall. Zero API calls. Local, free, open source.' }], ['meta', { property: 'og:image', content: `${docsBase}mempalace_logo.png` }], - ['script', { async: '', src: 'https://www.googletagmanager.com/gtag/js?id=G-PPQE4Z7P1K' }], - ['script', {}, `window.dataLayer = window.dataLayer || [];\nfunction gtag(){dataLayer.push(arguments);}\ngtag('js', new Date());\ngtag('config', 'G-PPQE4Z7P1K');`], + ...(gaId ? [ + ['script', { async: '', src: `https://www.googletagmanager.com/gtag/js?id=${gaId}` }], + ['script', {}, `window.dataLayer = window.dataLayer || [];\nfunction gtag(){dataLayer.push(arguments);}\ngtag('js', new Date());\ngtag('config', '${gaId}');`], + ] as const : []), ], themeConfig: { diff --git a/website/.vitepress/theme/landing/useLandingEffects.js b/website/.vitepress/theme/landing/useLandingEffects.js index 815077f..82e69a5 100644 --- a/website/.vitepress/theme/landing/useLandingEffects.js +++ b/website/.vitepress/theme/landing/useLandingEffects.js @@ -1,6 +1,10 @@ import { onMounted, onBeforeUnmount } from 'vue' export function useLandingEffects() { +// Shared cleanup registry — IIFEs push disconnect/removeEventListener thunks +// here so onBeforeUnmount can tear everything down on SPA nav. +const cleanups = [] + onMounted(() => { if (typeof document === 'undefined') return @@ -25,7 +29,7 @@ onMounted(() => { if (text != null) msg.textContent = text } - form.addEventListener('submit', async (e) => { + const onSubmit = async (e) => { e.preventDefault() if (form.classList.contains('is-success') || form.classList.contains('is-pending')) return @@ -70,11 +74,17 @@ onMounted(() => { button.disabled = false input.disabled = false } - }) + } - // Clear error state as soon as the user edits - input.addEventListener('input', () => { + const onInput = () => { if (form.classList.contains('is-error')) setState(null, '') + } + + form.addEventListener('submit', onSubmit) + input.addEventListener('input', onInput) + cleanups.push(() => { + form.removeEventListener('submit', onSubmit) + input.removeEventListener('input', onInput) }) }) })() @@ -102,6 +112,7 @@ onMounted(() => { }) }, { rootMargin: '0px 0px -80px 0px' }) items.forEach(el => io.observe(el)) + cleanups.push(() => io.disconnect()) })() /* ---------- Forgetting demo ---------- */ @@ -369,17 +380,27 @@ onMounted(() => { } } - if (replayBtn) replayBtn.addEventListener('click', () => { + const onReplayClick = () => { resetAll() armObservers() - }) + } + if (replayBtn) replayBtn.addEventListener('click', onReplayClick) armObservers() + + cleanups.push(() => { + disconnectObservers() + if (replayBtn) replayBtn.removeEventListener('click', onReplayClick) + }) })() }) onBeforeUnmount(() => { if (typeof document === 'undefined') return document.body.classList.remove('mempalace-active') + while (cleanups.length) { + const fn = cleanups.pop() + try { fn() } catch (_) { /* swallow — teardown best-effort */ } + } }) }