ref(seer): Replace PageContext with SeerContextProvider#111554
ref(seer): Replace PageContext with SeerContextProvider#111554Mihir-Mavalankar wants to merge 5 commits intomasterfrom
Conversation
Add the core infrastructure for capturing semantic page state as a structured context tree, replacing the lossy ASCII DOM snapshot. This includes the context types, a pure reducer for tree operations, a React provider with getSnapshot() serialization, a registerPageContext HOC for automatic node lifecycle, and unit tests. Stage 1 of the structured page context plan — no app wiring yet. Co-Authored-By: Claude Sonnet 4 <noreply@anthropic.com>
Use Record<string, unknown> constraint and props-as-any spread to match existing HOC patterns (withOrganization). Simplify test to avoid OOM. Mount PageContextProvider in the app shell so files are in the production import chain. Co-Authored-By: Claude Sonnet 4 <noreply@anthropic.com>
| return <div>{props.title}</div>; | ||
| } | ||
|
|
||
| const ContextAwareWidget = registerPageContext('widget', DummyWidget, { |
There was a problem hiding this comment.
I don't think we need the third prop here per previous conversations. I'd mostly use the register function just to wrap + name and make the third prop optional (we might want to use it for filtering data later or some such)
| }); | ||
|
|
||
| describe('registerPageContext HOC', () => { | ||
| function DummyWidget(props: {title: string}) { |
There was a problem hiding this comment.
I think this would be better served using the hook escape hatch (usePageContext({ title: 'Error Rate'});) or whatever it should be called to dedupe the "context" overloading
| expect(snapshot.nodes[0].nodeType).toBe('widget'); | ||
| expect(snapshot.nodes[0].data).toEqual({title: 'Error Rate'}); | ||
| }); | ||
|
|
There was a problem hiding this comment.
there should probably be a test in here which mounts, renders, then confirms that the PageContextProvider is actually providing the page context for sending to the AI. I'd also add a test with a nested case so we can test that DummyChart in DummyWidget in DummyDashboard (or something similar) nests appropriately and all are providing the right data in the right structure
Renames and rewrites the structured page context system for Seer Explorer.
The original implementation had three blocking issues: nodes were never
nested (parentId was never passed), initial data writes were silently
dropped (useSeerContext effect fired before registerNode), and an infinite
re-render loop caused OOM in tests (unstable context value reference on
every dispatch).
Key design decisions:
- Flat node map with parentId pointers; tree assembled lazily at
getSnapshot() time to avoid ordering dependencies on registration
- Node data stored in an imperative ref (not reducer state) so writes
from useSeerContext(data) are visible immediately regardless of whether
registerNode has fired yet
- Context value memoized so HOC useEffect deps stay stable and don't
retrigger on every registration
- registerSeerContext(nodeType, Component) — two args only, no options bag
- useSeerContext(data) write overload / useSeerContext() read overload
returning { getSeerContext(componentOnly?) }
Co-Authored-By: Claude <noreply@anthropic.com>
…et data Silences lint warnings on chained property access in snapshot assertions while still failing the test if a node is unexpectedly absent. Adds type/unit fields to DummyWidget's useSeerContext call so the nesting test verifies that multi-field payloads survive the full register → write → snapshot round-trip. Co-Authored-By: Claude <noreply@anthropic.com>
Replaces the per-property assertions in the nesting test with a single deep equality check against the expected tree, so a failure prints the full actual vs expected shape in one diff. Co-Authored-By: Claude <noreply@anthropic.com>
Renames and rewrites the structured page context system for Seer Explorer. The original draft had three blocking issues that prevented it from working:
Nesting never worked —
registerPageContextcalledctx.registerNode(nodeType)without passingparentId, so all nodes landed at root level. Fix: a second React context (SeerNodeContext) carries the current component'snodeIddownward so child HOC wrappers can read it as theirparentIdsynchronously during render.Initial data writes were silently dropped —
useSeerContext(data)effects fire before the parent HOC'sregisterNodeeffect (children run before parents in React). With the reducer-based approach theUPDATE_NODE_DATAcase was a no-op if the node didn't exist yet. Fix: data is now stored in an imperativeuseRef<Map>and written synchronously without dispatch.getSnapshot()reads structure from the reducer state and data from the ref at call time.Infinite re-render loop (OOM) in tests — the context value object was recreated on every
useReducerdispatch, giving the HOC'suseEffecta newctxdep on every registration, triggering cleanup+setup in a loop. Fix:useMemoon the context value so callbacks are stable.Other changes:
registerSeerContext(nodeType, Component)— two args, no options/extract baguseSeerContext(data)write overload anduseSeerContext()read overload returning{ getSeerContext(componentOnly?) }parentIdpointers; tree assembled lazily atgetSnapshot()time