# HeyNoterm – Codebase Overview

This repository contains the source code for **HeyNoterm**, a small
scratch-pad application that runs entirely inside a terminal using the
[Textual](https://github.com/Textualize/textual) TUI framework.

The main concepts are *blocks* of text (plain text, code, math
expressions, …) that can be added, removed and edited interactively.  All
content is persisted transparently between runs.

Below is a high-level description of the most important modules and
components.

## 1. Core modules

• **`heynoterm/app.py`**
  – Entry point that defines `HeyNoteApp`, a subclass of `textual.app.App`.
    It:
      * Reads the current state from the global data-manager (`dm`).
      * Builds the user interface (header, footer, a scrollable list of
        `BlockComponent`s).
      * Provides key bindings for adding/removing blocks and toggling the
        colour-scheme.

• **`heynoterm/state.py`**
  – Pure data-model layer.
      * `Language` – enum holding the supported block kinds.
      * `Block` – dataclass storing the raw *text* and its *language*.
        Includes `to_terminal()` which converts the dataclass into a
        `BlockComponent` widget.
      * `AppState` – wrapper that aggregates a list of blocks and handles
        (de)serialisation to JSON.
      * `DataManager` – light-weight repository responsible for loading and
        saving `AppState` to *~/.heynoterm.json* (or a custom path for
        testing).  Also offers small convenience helpers for adding,
        updating and removing blocks.  A module-level singleton `dm` is
        instantiated for global access.

• **`heynoterm/math_evaluator.py`**
  – Sand-boxed evaluator that processes a block of math-like statements
    (including variable assignments).  It re-uses Python’s `math` module
    whilst completely stripping built-ins to keep evaluation safe.

## 2. UI components

• **`heynoterm/block.py`** – `BlockComponent`
    – Container widget for a single block.  Depending on the *language*
      it embeds a `TextAreaComponent` (for editing) and, if the block is
      a *math* block, an additional `MathResult` widget that displays the
      evaluated results.  It listens for custom events coming from the
      contained widgets to update the global state or react to language
      changes.

• **`heynoterm/components.py`**
    – A collection of reusable widgets:
      * `TextAreaComponent` – extension of `textual.widgets.TextArea` with
        extra key-bindings (remove block, move focus, change language,
        …).  On every change the component persists its content via the
        `DataManager` and can post results of math evaluations back to its
        parent.
      * `LanguageList` – small radio-button pop-up listing all available
        languages; dispatches a `LanguageChanged` message when the user
        makes a selection.
      * `MathResult` – miniature widget that vertically prints the list of
        results generated by `MathBlockEvaluator`.

• **`heynoterm/heynoterm.tcss`** – Textual CSS file that defines the basic
  layout and colours of the widgets.

## 3. Tests

`tests/test_state.py` unit-tests the pure Python parts (dataclasses and the
`DataManager`). They serve as a regression-suite when refactoring the core
logic.

## 4. Recent refactorings / maintainability improvements

1. **Consistent language representation** – `Block.language` now defaults
   to `Language.PYTHON.value` (e.g. *"python"*) instead of the upper-case
   enum *name*.  This eliminates a subtle inconsistency between default
   values and the rest of the application which always works with
   lower-case values.

2. **Logging** – replaced scattered `print()` statements with structured
   calls to the standard `logging` module (using one logger per file).  It
   is less intrusive and allows fine-grained control over the verbosity.

3. **Enhanced doc-strings** – clarified the intent of several public
   classes and methods, making the code easier to understand for new
   contributors.

These changes do not alter any external behaviour; all existing tests pass
unchanged.
