(** Individual code cell within an x-ocaml page. A cell is a single code editor + output region embedded in the page. Each cell has a {!mode} that controls its appearance and behaviour: - {b Interactive}: visible, editable. The default for cells without a [mode] attribute. Edits are ephemeral (lost on page refresh). - {b Exercise}: visible, editable. The reader writes or modifies code here. - {b Test}: visible, read-only. Contains assertions that validate exercise code. Linked to an exercise cell and run automatically after it. - {b Hidden}: invisible. Provides definitions (types, helpers) available to subsequent cells without cluttering the page. Cells form a linear chain via {!set_prev}. When a cell is run, all preceding cells that haven't completed are run first, ensuring definitions are available. The chain determines line-number offsets so error locations map correctly to the page. {2 Test linking} A {!Test} cell can be linked to an {!Exercise} cell in two ways: - {b Positional}: if no [data-for] is set, a test cell links to the nearest preceding exercise cell. - {b Explicit}: [data-for="some-id"] links to the exercise cell whose [data-id] is ["some-id"], regardless of position. Linking is established by the page-level orchestrator ({!Page}), not by the cell itself. The cell exposes {!set_on_completed} so the page can register a callback that triggers linked test cells after a successful run. *) (** {1 Mode} *) type mode = | Interactive (** Visible, editable. Edits are ephemeral. *) | Exercise (** Visible, editable. *) | Test (** Visible, read-only. Linked to an exercise cell. *) | Hidden (** Invisible. Provides definitions to later cells. *) val mode_of_string : string -> mode (** [mode_of_string s] parses a mode from an HTML attribute value. Returns {!Interactive} for unrecognised strings. *) val string_of_mode : mode -> string (** [string_of_mode m] returns the canonical lowercase string for a mode. *) (** {1 Cell type} *) type t (** An individual code cell. Mutable: tracks execution status, editor state, and position in the cell chain. *) (** {1 Creation} *) val init : id:int -> mode:mode -> run_on:[ `Click | `Load ] -> ?cell_id:string -> ?cell_for:string -> ?cell_env:string -> ?filename:string -> ?merlin:bool -> ?extra_style:Jstr.t -> ?inline_style:Jstr.t -> eval_fn:(id:int -> line_number:int -> string -> unit) -> fmt_fn:(id:int -> string -> unit) -> post_fn:(X_protocol.request -> unit) -> Webcomponent.t -> t (** Create and initialise a cell. Attaches a shadow DOM to the given web component, creates the CodeMirror editor (unless the mode is {!Hidden}), and sets up the merlin integration (unless [~merlin:false] is passed). @param id Unique numeric identifier (typically the cell's position on page). @param mode Determines editability and visibility. @param run_on Whether to run automatically on page load or only on click. @param cell_id Optional [data-id] attribute for test-linking targets. @param cell_for Optional [data-for] attribute linking a test to an exercise. @param cell_env Optional [data-env] for future environment scoping. @param filename Optional filename for merlin and error reporting. @param merlin Whether to enable merlin integration. Default [true]; set to [false] to disable completions and type-on-hover for this cell. Has no effect on {!Hidden} cells, which never have merlin regardless. @param extra_style URL to an external stylesheet to load. @param inline_style Inline CSS for the [:host] selector. @param eval_fn Called to evaluate code. Receives the cell [~id], [~line_number] offset, and the source code string. @param fmt_fn Called to format code. Receives the cell [~id] and source. @param post_fn Called to send raw protocol messages (for merlin). *) (** {1 Accessors} *) val id : t -> int (** The cell's numeric identifier. *) val mode : t -> mode (** The cell's mode. *) val cell_id : t -> string option (** The [data-id] attribute, if set. Used as a target for [data-for] linking. *) val cell_for : t -> string option (** The [data-for] attribute, if set. Links a test cell to an exercise cell. *) val cell_env : t -> string option (** The [data-env] attribute, if set. Reserved for environment scoping. *) val source : t -> string (** The current source code. Reads from the CodeMirror editor if present, otherwise from the internal text buffer (for hidden cells). *) val has_completed : t -> bool (** [true] if the cell has been run and completed successfully since the last invalidation. *) (** {1 Execution} *) val run : t -> unit (** Run this cell. If preceding cells in the chain have not completed, they are run first (cascading). Does nothing if the cell is already running. *) val loadable : t -> bool (** [true] if the cell's [run_on] is [`Load]. *) val completed_run : t -> X_protocol.output list -> unit (** Called by the backend when evaluation completes. Displays the output and marks the cell as successfully run. If the next cell in the chain was waiting, triggers it. After marking the cell complete, fires any callback registered via {!set_on_completed}. The callback fires after every completed run, regardless of whether the output contains errors. *) val set_on_completed : t -> (t -> unit) -> unit (** [set_on_completed cell f] registers [f] to be called each time [cell] completes a run. Used by {!Page} to trigger linked test cells after an exercise cell finishes. Replaces any previously registered callback. *) val set_stop_fn : t -> (unit -> unit) -> unit (** [set_stop_fn cell f] registers [f] to be called when the user clicks "Stop" while the cell is running. Typically [f] resets the backend and all cells. *) val reset_status : t -> unit (** Reset the cell to {!Not_run} state: clears output messages, restores the "Run" button text, and resets test-result tracking flags. Does {e not} invalidate the cell chain. *) (** {1 Chain management} *) val set_prev : prev:t option -> t -> unit (** [set_prev ~prev cell] sets [cell]'s predecessor in the execution chain. Updates line-number offsets for all subsequent cells. The previous cell's [next] pointer is updated as well. *) (** {1 Content and display} *) val set_source : t -> string -> unit (** Replace the cell's source code. Updates the editor if present. *) val add_message : t -> int -> X_protocol.output list -> unit (** [add_message cell loc msgs] adds output messages at character offset [loc] in the cell's editor. No-op for hidden cells. *) val start : t -> Webcomponent.t -> unit (** [start cell element] reads the initial source from the element's [textContent] and loads it into the cell. Called once after the WebComponent's [connectedCallback]. *) (** {1 Merlin integration} *) val receive_merlin : t -> Protocol.answer -> unit (** Handle a merlin response for this cell. No-op if the cell has no merlin worker. *)