diff --git a/src/pdealchemy/notebook_utils.py b/src/pdealchemy/notebook_utils.py index a5e3539..d80dcba 100644 --- a/src/pdealchemy/notebook_utils.py +++ b/src/pdealchemy/notebook_utils.py @@ -96,16 +96,18 @@ def math_eq_editor( source_text = _read_equation_source(source_path) except OSError as exc: return mo.md(f"**Error loading equation file** `{content}`: {exc}") + get_source_text, set_source_text = mo.state(source_text) source_editor = mo.ui.code_editor( value=source_text, language="markdown", label=f"Equation source: {content}", + on_change=set_source_text, ) def _save_source(_value: object) -> str: try: - source_path.write_text(str(source_editor.value), encoding="utf-8") + source_path.write_text(str(get_source_text()), encoding="utf-8") except OSError as exc: return f"Save failed: {exc}" return f"Saved `{content}`." @@ -116,7 +118,7 @@ def _save_source(_value: object) -> str: kind="success", ) preview = _render_equation_block( - latex=_extract_first_latex_block(str(source_editor.value)), + latex=_extract_first_latex_block(str(get_source_text())), name=name, mo=mo, ) diff --git a/tests/notebook/test_notebook_utils.py b/tests/notebook/test_notebook_utils.py index 949ae1b..6092216 100644 --- a/tests/notebook/test_notebook_utils.py +++ b/tests/notebook/test_notebook_utils.py @@ -18,8 +18,18 @@ def _fake_marimo_module() -> SimpleNamespace: class _FakeCodeEditor: - def __init__(self, value: str) -> None: - self.value = value + def __init__(self, value: str, on_change: Any | None = None) -> None: + self._value = value + self._on_change = on_change + + @property + def value(self) -> str: + return self._value + + def set_value(self, value: str) -> None: + self._value = value + if self._on_change is not None: + self._on_change(value) class _FakeButton: @@ -43,6 +53,12 @@ def click(self) -> Any: return self._on_click(None) +class _NoValueReadEditor(_FakeCodeEditor): + @property + def value(self) -> str: + raise RuntimeError("Accessing editor.value in creating cell is not allowed") + + class _FakeUi: def code_editor( self, @@ -50,9 +66,10 @@ def code_editor( *, language: str, label: str, + on_change: Any | None = None, ) -> _FakeCodeEditor: _ = (language, label) - return _FakeCodeEditor(value) + return _FakeCodeEditor(value, on_change=on_change) def button( self, @@ -76,8 +93,31 @@ def md(text: str) -> tuple[str, str]: def vstack(blocks: list[object]) -> tuple[str, list[object]]: return ("vstack", blocks) + @staticmethod + def state(value: str) -> tuple[Any, Any]: + payload: dict[str, str] = {"value": value} + + def _get() -> str: + return payload["value"] + + def _set(new_value: str) -> None: + payload["value"] = new_value + + return _get, _set + class _NoValueReadUi(_FakeUi): + def code_editor( + self, + value: str, + *, + language: str, + label: str, + on_change: Any | None = None, + ) -> _NoValueReadEditor: + _ = (language, label) + return _NoValueReadEditor(value, on_change=on_change) + def button( self, *, @@ -100,6 +140,18 @@ def md(text: str) -> tuple[str, str]: def vstack(blocks: list[object]) -> tuple[str, list[object]]: return ("vstack", blocks) + @staticmethod + def state(value: str) -> tuple[Any, Any]: + payload: dict[str, str] = {"value": value} + + def _get() -> str: + return payload["value"] + + def _set(new_value: str) -> None: + payload["value"] = new_value + + return _get, _set + def test_math_eq_renders_raw_latex(monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setattr(notebook_utils, "_load_marimo_module", _fake_marimo_module) @@ -242,7 +294,7 @@ def test_math_eq_editor_persists_edited_source( rendered = notebook_utils.math_eq_editor(str(equation_file)) editor = next(block for block in rendered[1] if isinstance(block, _FakeCodeEditor)) button = next(block for block in rendered[1] if isinstance(block, _FakeButton)) - editor.value = "\\[\nS-K\n\\]" + editor.set_value("\\[\nS-K\n\\]") button.click()