# `PhoenixKitEcommerce.Shopify.TextDiff`
[🔗](https://github.com/BeamLabEU/phoenix_kit_ecommerce/blob/0.5.11/lib/phoenix_kit_ecommerce/shopify/text_diff.ex#L1)

Word-level diff between two versions of a text field.

Built on `List.myers_difference/2` over whitespace-preserving tokens, so
no diff dependency is needed and rejoining the fragments reproduces the
inputs exactly — the view relies on that to render "before" and "after"
from one pass.

Both functions are pure. `summary/2` needs an exact changed-fragment
count, which means it runs the same `List.myers_difference/2` pass as
`words/2` — through a shared private helper, not by calling `words/2`
itself (see that function's doc for why the distinction matters) — it
is not a cheap approximation, just a smaller return value.

Myers is O(N*D), so the cost tracks how DIFFERENT the two texts are, not
how long they are. Measured on a 1.7 KB `body_html`:

    small edit          0.55 ms
    half the text       2.33 ms
    wholly rewritten   12.0  ms

A caller listing many rows must bound how many it renders at once: 529
rows of the last kind is 6.3 seconds inside the LiveView process. Page
the rows and call this only for the page being shown.

Both functions emit `:telemetry` on every call
(`[:phoenix_kit_ecommerce, :shopify, :text_diff, :words | :summary]`,
empty measurements/metadata) — cheap (a no-op when nothing's attached)
and the only way a caller enforcing "only for the page being shown" or
"only for the row that's expanded" can prove it from outside this
module, since correct output looks identical whether or not those
guarantees held.

# `fragment`

```elixir
@type fragment() :: {:eq | :del | :ins, String.t()}
```

# `summary`

```elixir
@spec summary(String.t() | nil, String.t() | nil) :: %{
  fragments: non_neg_integer(),
  length_delta: integer()
}
```

Small-payload shape of the change: how many changed regions, and how
much longer or shorter the text became. Getting an exact count still
requires running the full diff (see the module doc) — this is smaller
to return and to render, not cheaper to compute.

# `words`

```elixir
@spec words(String.t() | nil, String.t() | nil) :: [fragment()]
```

Returns the diff as ordered fragments. `nil` is treated as an empty string.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
