Skip to main content

CHANGELOG.md

# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Added

### Changed

### Deprecated

### Removed

### Fixed

### Security

## [0.3.0] - 2026-05-07

### Added

- `on_key` attribute for zero-config keyboard event handling. Add it to any
  element to bind a window-level keydown handler; the handler receives the key
  string and a `%Filament.KeyModifiers{}` struct with `ctrl`, `shift`, `alt`,
  and `meta` boolean fields — no `phx-key`, no custom JS hook required:

  ```elixir
  ~F"""
  <div on_key={fn "Escape", _ -> close() end}>
  </div>
  """
  ```

  Pattern match on the key string to filter; use `_` to ignore modifiers you
  don't care about.

- Bang variants for all `Filament.Test` helpers: `mount!/2`, `click!/2`,
  `submit!/3`, `change!/3`, `blur!/2`, `key_down!/2`, `key_down!/3`. Each
  unwraps `{:ok, view}` and raises on error, enabling pipeline-style test
  composition:

  ```elixir
  mount!(Counter, %{initial: 0})
  |> click!("button")
  |> click!("button")
  |> assert_text("2")
  ```

- `Filament.Test.change/3` — triggers a `phx-change` event on a form element.
- `Filament.Test.blur/2` — triggers a `phx-blur` event on an element.
- `Filament.Test.key_down/3` — element-scoped `phx-keydown` (3-arity, alongside
  the existing 2-arity window-scoped `key_down/2`).

### Fixed

- Fixed event handler index collision between compile-time `on_*` handlers and
  runtime `register_event_handler` calls. Previously, handlers registered inside
  `{for … do}` loops could silently overwrite `on_*` handlers in the same
  component.

## [0.2.1] - 2026-05-07

### Changed

- The project license has changed from Apache-2.0 to MIT.

### Fixed

- `~F` formatter now preserves `<script>` block content verbatim. Previously,
  JavaScript inside colocated `<script :type={ColocatedHook}>` blocks was
  re-indented as if it were HTML, corrupting indentation-sensitive code.

## [0.2.0] - 2026-05-06

### Added

- `use_observable/2` now accepts a positional projection fn as its second argument. The fn
  receives `:disconnected` when the server is unavailable, or the raw server state otherwise,
  and its return value becomes the hook's result:

  ```elixir
  count = use_observable(CartServer, fn
    :disconnected -> 0
    state -> Cart.State.item_count(state)
  end)
  ```

- `static_subscribe` option on `Filament.LiveView` (default: `true`) controls whether the
  HTTP render pass subscribes to observables. Set to `false` on a live view to prevent
  double-counting presence or other mount side effects on page reload — subscriptions are
  then established only once the WebSocket session connects.

- Support for `<script :type={Phoenix.LiveView.ColocatedHook}>` in `~F` templates.
  Modules using `use Filament.Component` now correctly register colocated JS hooks
  alongside those from `use Phoenix.Component`.

### Changed

- Projection fns now run **client-side at render time** rather than server-side at broadcast
  time. This means a projection fn can close over local component state (filters, selections,
  etc.) so changing that local state correctly re-projects without a new server broadcast.
  The server sends raw state; change-or-bust comparison is `new_raw_state !== last_raw_state`
  per subscriber.

- `handle_subscribe/3``handle_subscribe/2`: the `request` argument has been removed.
  Update your `Observable.GenServer` implementations:

  ```elixir
  # before
  def handle_subscribe(_request, _subscriber, state), do: {:ok, state, state}

  # after
  def handle_subscribe(_subscriber, state), do: {:ok, state, state}
  ```

- `Observable.subscribe/3``Observable.subscribe/2`: the `request` argument has been removed.
- `Observable.remove_projection/5``Observable.remove_projection/4`: the `request` argument
  has been removed.
- `Subscriber` struct: `request` and `projections` fields replaced by `proj_keys` and `last_raw`.

- `~F` templates no longer accept `@foo` assign syntax — use bare lexical variables from
  destructured function arguments instead. `@foo` in a `~F` template now raises a compile
  error. `{if … do}`, `{for … do}`, `{else}`, and `{end}` are handled natively by the tag
  engine rather than via a regex preprocessing pass (no behaviour change for existing templates).

### Removed

- The `request` parameter has been removed from the entire observable stack
  (`handle_subscribe`, `Observable.subscribe`, `Observable.remove_projection`, `Subscriber`
  struct).

### Fixed

- Fixed `keyed_list` removal leaking observable projection keys, causing stale subscriptions
  when list items are removed.

## [0.1.0] - 2026-05-01

### Added
- Initial project scaffold
- Mix project structure with Elixir 1.17+ and OTP 26+ support
- GitHub Actions CI with matrix testing
- ExDoc configuration for documentation
- Basic supervision tree structure