Draft
Conversation
# Conflicts: # src/haz3lcore/projectors/ProjectorCore.re # src/haz3lcore/projectors/ProjectorInit.re # src/language/term/Typ.re
Documents a staged plan for building a full-featured HTML/DOM wrapper for Hazel programs, including: - Expanded Html element types (structural, forms, tables, semantic) - Expanded Attr types with typed common attributes + string fallback - Event data types (KeyEvent, MouseEvent) - Cmd system for fire-and-forget effects (focus, scroll, clipboard) - Sub system for event source subscriptions (resize, keyboard, time) - App viewer integration options Uses "self-modifying" pattern (handlers return Html -> Html) to work without type parameters. Notes future work to parameterize over message type once Hazel gains type parameters. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add KeyEvent and MouseEvent product types for event data - Expand HTML.t with ~40 element constructors (forms, tables, semantic sections) - Expand HTML.attr with ~45 attribute/event constructors - Add mouse/keyboard event handlers with proper JS interop - Update HazelDOM.re renderer to support all new elements and attributes - Use Node.create() escape hatch for HTML5 elements without Virtual_dom functions - Fix constructor registration to handle non-sum types (product types) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add Cmd type to BuiltinsADT.re with variants: - CmdNone, Batch(List(Cmd)) - Focus(String), Blur(String), ScrollIntoView(String), ScrollTo(String, Float, Float) - CopyToClipboard(String), Delay(Float, Html -> Html), Log(String) - Create CmdRunner.re to interpret Cmd values as Ui_effect.t - Commands are fire-and-forget effects that work without type parameters - Delay command uses Bonsai.Effect.Expert.handle for async state updates Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add Sub type to BuiltinsADT.re with variants: - SubNone, SubBatch(List(Sub)) - OnResize((Html, Int, Int) -> Html) - OnVisibilityChange((Html, Bool) -> Html) - OnDocumentKeyDown/OnDocumentKeyUp((Html, KeyEvent) -> Html) - Every(Float, (Html, Float) -> Html) - AnimationFrame((Html, Float) -> Html) - Create SubManager.re to manage subscription lifecycle - Track event listener IDs for proper cleanup - Rename Cmd.Batch to CmdBatch for consistency with SubBatch Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add App type as product: ((HTML, Cmd), HTML -> Sub) - init provides initial state and startup command - subscriptions function returns event sources based on current state - Foundation for projector-based app runner Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add process_handler_result() to detect (Html, Cmd) tuples - Event handlers now support returning either Html or (Html, Cmd) - When handler returns tuple, CmdRunner executes the command - Backwards compatible: plain Html returns still work Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Mark completed deliverables in Phases 1-5 - Add implementation status summary at top - Note remaining work: Sub lifecycle integration, App runner, error boundaries Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add projector_id and subscriptions fields to HazelDOM.t - Add global active_subscriptions registry keyed by projector ID - Add manage_subscriptions() to set up/clean up subs on render - HTMLProj passes projector_id for subscription tracking - Subscriptions are cleaned up when projector re-renders Note: App type detection for automatic subscription extraction is still TODO - currently subscriptions must be passed explicitly. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- detect_app() checks if expression is App type: ((HTML, Cmd), HTML -> Sub) - When App detected, extracts html model and evaluates subscriptions function - Subscriptions are passed to HazelDOM for lifecycle management - Plain Html expressions continue to work as before Note: init_cmd from App is detected but not yet executed on startup. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Create hazel-html-implementation.md with complete technical reference - Architecture overview with diagram - All types with full constructor listings - Self-modifying pattern explanation - Runtime components (HazelDOM, CmdRunner, SubManager) - Usage examples for plain HTML, commands, and full App - Known limitations and future enhancements - Update hazel-html-plan.md - Point to implementation doc as primary reference - Mark completed items - Note deviations from original plan Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Run the startup command from App init tuple via CmdRunner when the projector first renders an App type expression. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Event handlers in the App View sidebar now work: - Add app_view_html field to Globals.Model for runtime HTML state - Add SetAppViewHtml/ResetAppView actions to Globals.Action - Wire inject in Sidebar.re to dispatch SetAppViewHtml - Update AppViewPanel to prefer state over evaluation result - Add Reset button to return to evaluation result When an event handler fires in the App View, it updates the global state, which triggers a re-render with the new HTML. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The App View sidebar now properly handles both plain HTML and full App types ((HTML, Cmd), HTML -> Sub): - Add detect_app and looks_like_app functions - Add evaluate helper for evaluating subscription expressions - Run init command when App type is detected - Evaluate and pass subscriptions to HazelDOM - Fall back to plain HTML rendering for non-App expressions This enables full app functionality in the sidebar, including commands and subscriptions (timer, keyboard events, animation frames, etc.) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Wrap HTML rendering and App handling in try/catch blocks to gracefully handle runtime errors: - render_html_content catches exceptions during HazelDOM rendering - App type handling catches exceptions during cmd/sub evaluation - Errors display a red error box with the exception message This prevents crashes from bubbling up and breaking the entire UI. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Mark completed items in the plan: - Resizable HTML projector with drag handle - App View sidebar panel with full interactivity - Interactive state management with Reset button - Error boundaries for graceful failure handling - Example programs Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Hazel uses 'fun x -> body' for lambdas, not '=>'. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Browser profiling showed 95%+ of cursor movement time was spent in Equality.typ comparing Sum types via ConstructorMap operations. The root cause was venn_regions using List.partition for each element, giving O(n²) complexity. With the HTML type having many constructors, this was devastating for performance (~900ms per cursor movement). Fix: Add physical equality short-circuits (===) to avoid expensive structural comparison when comparing identical objects: - Equality.re: typ and exp functions return immediately if t1 === t2 - ConstructorMap.re: equal, meet, match_synswitch check physical equality first, and equal also checks length mismatch early This works because the statics system often compares the same type objects against themselves due to AST sharing. Result: Cursor movement is now responsive (<100ms vs 900ms+). Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Root cause: Typ.normalize and all_ids_temp traverse entire type structure for every expression during elaboration - For large types like HTML (~40 constructors), this is expensive - Added experimental Typ.normalize memoization (with soundness caveat) - Profiling shows 13k+ normalize calls for simple counter program - Low cache hit rate suggests types are not being shared/reused - Documents remaining questions and future directions Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
WARNING: This optimization may not be semantically sound. The cache keys by type ID only, ignoring context. This could be unsafe if the same type ID appears in different contexts where it should normalize differently (e.g., shadowed type aliases). For concrete types like HTML (no free type variables), this should be safe. But the general case needs more analysis. Changes: - Typ.re: Add normalize_cache (Id.Map), memoize normalize results - Elaborator.re: Reset cache at start of elaboration, add instrumentation Performance improvement observed but not fully validated. Consider reverting if issues arise. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit includes work-in-progress changes: 1. Performance instrumentation (TimeUtil, CachedStatics, Statics, Evaluator, CodeViewable, CodeWithStatics, Page) - timing logs for profiling 2. MVU architecture changes (Globals, AppViewPanel, Sidebar, Page) - moving evaluation from view to update handler These changes are useful for debugging but may need cleanup. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Refactor the event system from model-passing (Html -> Html handlers) to message-based dispatch (handlers produce msgs routed through update). Architecture: - Apps are 4-tuples: (init_model, update, view, subs) - update: (msg, model) -> model (or (model, Cmd)) - Handlers: OnClick(msg), OnInput(String -> msg), etc. - Dual-mode via update_fn: option(DHExp.t) on HazelDOM.t Some = Elm mode, None = legacy mode (inline projector unchanged) Key changes: - Add update_fn field to HazelDOM.t, SubManager.context, CmdRunner.context - Add AppViewMsg action + handler in Page.re with evaluate_direct (skips re-elaboration for already-evaluated runtime values) - Dual-mode handler dispatch in HazelDOM (on_, on_input, on_mouse, on_key) - Dual-mode SubManager.apply_handler and CmdRunner.Delay - Smart inject in Sidebar.re: AppViewMsg for Elm apps, SetAppViewModel for legacy - 4-tuple detection in AppViewPanel.re (ElmApp vs LegacyMvuApp) - Update BuiltinsADT.re handler types to use Unknown(Internal) for msg/model - strip_wrappers utility to handle Asc/Closure/Parens from evaluator - Improved fallback rendering: abbreviated Hazel code instead of raw AST dump - Update example programs for new architecture Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fallback view now renders abbreviated Hazel code using Abbreviate → ExpToSegment → ProjectorView.flex_code pipeline instead of raw DHExp.show text dump - Add type annotations to MVU example programs (mvu-counter, timer, full-app) - Note: full-app update left unannotated (polymorphic return: model or (model, Cmd)) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- keyboard-game.hz: Rewritten as 4-tuple MVU app with inline max/min helpers, OnDocumentKeyDown(fun key_event -> key_event) subscription pattern - animation.hz: Rewritten as 4-tuple MVU app with nested pair model, AnimationFrame(fun timestamp -> timestamp) subscription pattern - counter.hz: Added missing fun keywords (legacy mode unchanged) - todo-list.hz: Added missing fun keywords (legacy mode unchanged) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…l_ids_temp/fix_typ_ids - Add physical equality (===) check at top of Typ.meet: 12x statics speedup (252ms -> 21ms for full_app). Unconditionally correct since meet is idempotent. - Add === short-circuits to ConstructorMap.meet/equal/match_synswitch and Equality.typ/exp. - Remove unsound normalize cache (57 test failures, inconsistent perf). - Disable all_ids_temp and fix_typ_ids in Elaborator: 1.8x elab speedup (1273ms -> 703ms). These traversals replaced all type IDs with temporaries then reassigned real IDs — the traversal cost itself was 55% of elab time. - Add benchmark harness (bench/bench.re) with profiling counters. - Add elaboration profiling instrumentation for normalize, match_synswitch, all_ids_temp, fix_typ_ids breakdown. - Document findings in docs/large-sum-type-performance.md. - Fix constructor collision in sum type tests (A->Qux, B->Baz). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Elaborator: remove eager normalize from elaborated_type/elaborated_pat_type. Normalize now only at specific use sites: uexp_elab return (once per program), fresh_ascription (with fast_equal shortcut + ctx param), case-specific calls (Asc, Constructor, TypAp). Let label rearrangement uses weak_head_normalize. full_app elab: 1,273ms -> 151ms (8.4x speedup). Meet calls: 89K -> 184. Sum meets in elab: 786 -> 0 (eliminated). Benchmark: add post-eval statics profiling (Statics.mk on evaluated result). Reveals post-eval statics as new bottleneck: 2,353ms for full_app (102x slower than pre-eval statics) due to physical equality broken by web worker boundary. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Post-eval statics for full_app: 2,249ms → ~1,270ms (-44%). 1. Hash-based venn_regions in ConstructorMap (O(n²) → O(n+m)) 2. Skip subst in Rec-Rec meet when type variable names match 3. Allocation-preserving subst (return original when unchanged) 4. Unknown-Unknown meet: skip allocation when provenances equal 5. Arrow/List/TupLabel meet: return original when children unchanged 6. ConstructorMap.map: preserve physical equality for normalize Also: unthunk IdTagged.IdTag.temp to avoid unnecessary allocations. Update performance investigation docs with benchmark results. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Change IdTag.temp() to IdTag.temp (constant, not thunked) across CachedStatics, Exp, TPat, TempGrammar, Test_Menhir, Test_Statics_Prelude - Reformat BuiltinsADT.re Sub/App definitions for readability - Disable always_render in ExpToSegment fold_fun_if (perf concern) - Update keyboard-game example to extract key name from event tuple - Remove unused counter.hz example (superseded by MVU counter) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix AnimationFrame subscription: controlled recursive loop with proper cleanup via running ref and EventHandle - Fix keyboard event capture: use capture phase (Js._true) so OnDocumentKeyDown/Up subscriptions fire before editor handlers - Add cleanup_sidebar_subscriptions for proper ResetAppView cleanup - Add stable sidebar projector ID for consistent subscription management - Refactor Page.re evaluate helpers for clarity - Add Test_MVU.re with comprehensive MVU architecture tests Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…path
Post-eval statics on HTML apps (47-constructor sum type) dropped from
2,133ms to 3.7ms — a 577x improvement. Sum meets: 17,733 → 0.
Root cause: ctr_ana_typ unrolls Rec types, producing expanded types that
survive through meet results into constructor annotations. Post-eval
statics then does expensive O(n^2) structural comparison on every meet.
Fix has three parts:
1. compact_builtin_recs (Elaborator.re): Replaces Rec("HTML",...) back
to Var("HTML") in constructor type annotations for unshadowed builtin
aliases. fresh_ascription stores unnormalized types so Asc nodes keep
compact Var references.
2. Lazy resolution (Ascriptions.re): Instead of assuming pre-normalized
types, resolves Var references lazily via weak_head_normalize with a
builtin context (set via Ascriptions.set_ctx in Evaluator.re). This
is architecturally aligned with future type closures.
3. Var-Rec fast path (Typ.meet): When meet(Var(name), Rec(name,...))
encounters a Var that resolves to the same-named Rec type alias,
returns the compact Var form directly without structural comparison.
Also adds meet profiling counters (phys_eq, var_expand, unknown),
enhanced shape diagnostics in bench.re, regression tests for
constructor annotation compactness, and Statics.re optimization
to reuse existing constructor type annotations.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…, add CLI test command - Rewrite all 5 existing MVU programs (timer, full-app, animation, keyboard-game, todo-list) to use labeled tuples and sum type messages instead of positional tuples and bare values. Add test suites (49 tests). - Add 2 new MVU programs: emojipaint (3x3 emoji grid) and tictactoe (with win/draw detection), both with HTML views and tests. - Fix critical runtime bug in Page.re: Tuple([m, c]) pattern matched any 2-element tuple as (model, cmd), breaking labeled tuple models with 2 fields. Now checks is_cmd(c) guard before splitting. - Add CLI `test` command for running .hz test suites from command line. Upgrade `analyze` with Rust-style error output (line numbers, carets). - Fix keyboard focus: Page.re key_handler now yields to focused INPUT/TEXTAREA/SELECT elements instead of intercepting all keystrokes. - Add debug logging to HazelDOM render_elem for diagnosing render failures. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Documents the Elm-style MVU system: program structure, event handlers, commands, subscriptions, runtime dispatch cycle, divergences from Elm, and known limitations. Legacy self-modifying pattern documented in isolated section with specific entanglement points listed. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace the inline(10) placeholder with a Block(rows-1) placeholder sized in character units (default 40×12). Add corner drag resize using pointer capture: during drag, cursor position is converted to char units and SetDimensions is dispatched when values change, giving live feedback as the editor layout reflows. Fix framework bug where SetModel was not treated as an edit in Action.is_edit, preventing CachedSyntax from recomputing the shape_map when projector models changed. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Change AppViewState.update_fn from DHExp.t to option(DHExp.t) to cleanly distinguish Elm apps (Some) from legacy apps (None) at the type level. This replaces runtime function-shape inspection in Sidebar.re and the is_cmd check in Page.re. Remove debug print_endline calls from HazelDOM.re, SubManager.re, and Page.re. Update MVU docs to reflect the simplified architecture. Add MVU-render CSS height rule. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fix performance.now() call in SubManager.re: use ##. (property access) instead of ## (method call) for window.performance, which was causing a TypeError that silently broke all Every-based subscriptions (timer, game of life auto-play). Add AppViewMsg debug logging in Page.re for diagnosing subscription dispatch issues. Add 6 new MVU example programs: gameoflife, harvest-streak, lunar-growth, nutrient-rotation, seed-catalog, tamagotchi. Expand Test_MVU.re from 15 to 49 tests covering real MVU pattern, commands, subscriptions, HTML elements, and negative cases. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Member
Author
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## dev #2115 +/- ##
==========================================
- Coverage 50.40% 49.50% -0.90%
==========================================
Files 230 235 +5
Lines 25365 26529 +1164
==========================================
+ Hits 12784 13132 +348
- Misses 12581 13397 +816
🚀 New features to boost your workflow:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.












Adds an Elm-like vdom wrapper via Hazel builtins. Subsumes #1812.