-
Notifications
You must be signed in to change notification settings - Fork 61
Hazel HTML #2115
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
disconcision
wants to merge
63
commits into
dev
Choose a base branch
from
hazel-html
base: dev
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Hazel HTML #2115
Conversation
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
# 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 <[email protected]>
- 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 <[email protected]>
- 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 <[email protected]>
- 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 <[email protected]>
- 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 <[email protected]>
- 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 <[email protected]>
- 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 <[email protected]>
- 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 <[email protected]>
- 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 <[email protected]>
- 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 <[email protected]>
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 <[email protected]>
Co-Authored-By: Claude Opus 4.5 <[email protected]>
- Mark HTMLProj init improvements as complete - Mark example programs as complete - Mark sidebar panel as partially complete (placeholder) - Note architectural limitations for resize and sidebar features Co-Authored-By: Claude Opus 4.5 <[email protected]>
Thread CellEditor.Model.t through to AppViewPanel so it can access the evaluation result. When the evaluated expression is a valid HTML type (Div, Span, Text, etc.), render it using HazelDOM.render_elem. - Add get_cell_editor to Page.re to extract full CellEditor - Update Sidebar.re to accept and pass cell_editor parameter - Rewrite AppViewPanel to render HTML from evaluation results - Show instructions when no valid HTML result is available Co-Authored-By: Claude Opus 4.5 <[email protected]>
The HTMLProj model now includes UI state (width, height, resizing mode). A resize handle on the right edge allows dragging to change width. The projector wrapper applies size styles from the UI state. - Add ui_state type with width, height, resizing fields - Add actions: SetWidth, SetHeight, StartResize, StopResize, ResetSize - Update view to wrap content in resizable container - Add resize handle with mousedown/mousemove handlers Co-Authored-By: Claude Opus 4.5 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
Hazel uses 'fun x -> body' for lambdas, not '=>'. Co-Authored-By: Claude Opus 4.5 <[email protected]>
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 <[email protected]>
- 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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
- 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 <[email protected]>
- 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 <[email protected]>
…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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
- 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 <[email protected]>
- 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 <[email protected]>
…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 <[email protected]>
…, 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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Member
Author
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.