mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-15 00:41:09 +02:00
Three medium-sized improvements:
1. E2E Smoke Test (e2e/smoke.spec.ts)
Two Playwright tests that exercise the critical happy path:
- "boot → dashboard → navigate → verify": opens /, /todo,
/notes, /habits, /calc in sequence, verifies each renders
content, checks for console errors.
- "module routing: all core routes respond": iterates 11 core
routes (/todo, /calendar, /contacts, /notes, /habits, /calc,
/chat, /body, /dreams, /finance, /moodlit) and asserts no
SvelteKit error page or crash. Runs in guest mode using the
existing dismissWelcomeModal helper.
2. Lazy Widget Loading (WidgetContainer.svelte)
Dashboard widgets are now lazy-mounted via IntersectionObserver.
Offscreen widgets render a small pulse placeholder until they
scroll into the viewport (with 200px rootMargin for pre-loading).
Once visible, the widget stays mounted permanently. This defers
liveQuery subscriptions for the ~13 dashboard widgets so only
the ~3-4 above-the-fold widgets fire IndexedDB reads on initial
mount — the rest activate as the user scrolls.
3. Typed Module Context (lib/data/module-context.ts)
`createModuleContext<T>(key)` returns a `{ provide, consume }`
pair that wraps Svelte's setContext/getContext with compile-time
type safety. Replaces the manual
`getContext<{readonly value: T[]}>('key')` pattern that was
duplicated across every layout/page boundary with a fragile
inline type annotation. Example usage added for the body module
(body/context.ts) — other modules can adopt incrementally.
Turborepo type-check task was already in place (turbo.json + root
package.json). No changes needed.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
74 lines
2.3 KiB
TypeScript
74 lines
2.3 KiB
TypeScript
/**
|
|
* Typed Module Context — replaces manual getContext/setContext with
|
|
* type-safe wrappers that guarantee the shape at compile time.
|
|
*
|
|
* Problem:
|
|
* Layouts do `setContext('todos', useAllTodos())` and pages read
|
|
* `const ctx: { readonly value: Todo[] } = getContext('todos')`.
|
|
* The type annotation is manual, duplicated, and drifts silently
|
|
* when the query return shape changes.
|
|
*
|
|
* Solution:
|
|
* `createModuleContext<T>('todos')` returns a pair of typed
|
|
* functions: `provide(value)` for layouts and `consume()` for
|
|
* pages. Both enforce the same T at compile time.
|
|
*
|
|
* Usage:
|
|
*
|
|
* ```ts
|
|
* // In the module's queries.ts or a dedicated context.ts:
|
|
* import { createModuleContext } from '$lib/data/module-context';
|
|
* import type { Todo } from './types';
|
|
*
|
|
* export const todosContext = createModuleContext<Todo[]>('todos');
|
|
*
|
|
* // In +layout.svelte:
|
|
* todosContext.provide(useAllTodos());
|
|
*
|
|
* // In +page.svelte:
|
|
* const todos = todosContext.consume();
|
|
* // todos is { readonly value: Todo[]; readonly loading: boolean; readonly error: unknown }
|
|
* ```
|
|
*/
|
|
|
|
import { setContext, getContext } from 'svelte';
|
|
|
|
/**
|
|
* The shape returned by `useLiveQueryWithDefault` from @mana/local-store/svelte.
|
|
* We mirror it here so module-context.ts doesn't need a runtime import
|
|
* of the local-store package (it's types-only).
|
|
*/
|
|
export interface LiveQueryResult<T> {
|
|
readonly value: T;
|
|
readonly loading: boolean;
|
|
readonly error: unknown;
|
|
}
|
|
|
|
/**
|
|
* Creates a typed context pair for a module's reactive data.
|
|
*
|
|
* @param key — unique string key for the Svelte context. Must match
|
|
* across the layout (provide) and page (consume) in the same route
|
|
* subtree.
|
|
* @returns `{ provide, consume }` — call `provide` in the layout's
|
|
* `<script>` block and `consume` in any descendant page/component.
|
|
*/
|
|
export function createModuleContext<T>(key: string) {
|
|
return {
|
|
/**
|
|
* Set the reactive query result into the Svelte context.
|
|
* Call this in the `<script>` block of a +layout.svelte.
|
|
*/
|
|
provide(value: LiveQueryResult<T>): void {
|
|
setContext(key, value);
|
|
},
|
|
|
|
/**
|
|
* Read the reactive query result from the Svelte context.
|
|
* Call this in the `<script>` block of a +page.svelte or component.
|
|
*/
|
|
consume(): LiveQueryResult<T> {
|
|
return getContext<LiveQueryResult<T>>(key);
|
|
},
|
|
};
|
|
}
|