A complete rewrite of Hypernote Pages using zig-mdx and zig-nostr-loader.
Write .hnmd documents and .hnmc components using declarative MDX syntax where:
- Components declare
queries:in frontmatter - zig-nostr-loader automatically deduplicates all queries
- Live preview in split editor updates as you type
bun installNote: This project depends on local packages:
zig-mdx- MDX parser with WASM (TreeBuilder for proper tree structure)zig-nostr-loader- Nostr event loader with React hooks
Make sure React is symlinked to avoid duplication:
ln -s ../../hn-pages-v2/node_modules/react ../zig-nostr-loader/node_modules/react
ln -s ../../hn-pages-v2/node_modules/react-dom ../zig-nostr-loader/node_modules/react-dombun run devServer runs at http://localhost:3420
bun test src/parser/mdxParser.test.tszig-mdx- Fast MDX parser with TreeBuilder for nested tree structurezig-nostr-loader- Nostr query execution with automatic deduplication
SplitView- Full-width split editor/preview layoutEditorPane- Overtype-based markdown editor with dark themePreviewPane- Live MDX rendering
mdxParser.ts- Wraps zig-mdx, extracts frontmatterNodeRenderer.tsx- Recursively renders AST nodesComponentRenderer.tsx- Renders .hnmc with query executionHnmdRenderer.tsx- Document renderer with component importsevaluator.ts- jq-style expression evaluationqueryTransform.ts- Query expression resolution and pipes
- ❌ No Hypersauce dependencies
- ❌ No queryRuntime.ts or useQuery.ts (old query code)
- ❌ No built-in Nostr components (Profile/Note in React)
✅ Working:
- Split view editor with Overtype
- MDX parsing with zig-mdx TreeBuilder
- Component imports (document and nested)
- Basic rendering of markdown and JSX elements
🐛 Known Issues:
Error: Maximum update depth exceeded
Root Cause: ComponentRenderer calls useFilter hooks in a for loop, which violates React's Rules of Hooks. Different components have different numbers of queries, causing varying hook call counts.
Location: src/runtime/ComponentRenderer.tsx lines 72-86 (now commented out)
Temporary Fix: Queries are disabled (empty object) to prevent crash
Proper Solutions:
- Use fixed number of query slots (query0, query1, query2, etc.)
- Move query execution outside React components to a manager
- Generate dynamic components at build time
- Use a query hook wrapper that handles dynamic queries properly
See HN_PAGES_V2_PLAN.md for full implementation plan.
src/
├── components/
│ ├── SplitView.tsx # Main layout
│ ├── EditorPane.tsx # Overtype editor
│ ├── PreviewPane.tsx # Live preview
│ ├── markdown/ # Heading, Paragraph, Text, etc.
│ ├── layout/ # HStack, VStack
│ └── builtin/ # Each component
├── parser/
│ ├── mdxParser.ts # zig-mdx wrapper
│ ├── frontmatterParser.ts # YAML parsing
│ └── types.ts # Type definitions
├── runtime/
│ ├── NodeRenderer.tsx # AST → React
│ ├── ComponentRenderer.tsx # .hnmc renderer (BROKEN - see above)
│ ├── HnmdRenderer.tsx # .hnmd renderer
│ ├── evaluator.ts # Expression evaluator
│ └── queryTransform.ts # Query pipes
└── lib/
├── wasm.ts # zig-mdx WASM loader
├── componentLoader.ts # .hnmc file fetcher
└── overtypeTheme.ts # Editor theme
public/
├── mdx.wasm # zig-mdx parser
├── zig-nostr.wasm # Nostr loader
└── sample_components/
├── Feed.hnmc
├── Profile.hnmc
└── test.hnmd