# `Plushie.Effect`
[🔗](https://github.com/plushie-ui/plushie-elixir/blob/v0.7.2/lib/plushie/effect.ex#L1)

Native platform effect requests.

Effects are asynchronous I/O operations that require the renderer to
interact with the OS on behalf of the Elixir app: file dialogs,
clipboard access, notifications, and similar.

Each function takes an atom `tag` as the first argument and returns a
`Plushie.Command` struct. Dispatch it from `update/2` like any other
command. The result arrives later as a `%Plushie.Event.EffectEvent{tag: tag,
result: result}` event in `update/2`, where `result` is a typed
`Plushie.Effect.Result.*` struct. Pattern match on the tag plus the
struct module to identify the outcome.

Only one effect per tag can be in flight at a time. Starting a new
effect with a tag that already has a pending request discards the
previous one.

## Example

    alias Plushie.Effect.Result

    def update(model, %Plushie.Event.WidgetEvent{type: :click, id: "open"}) do
      {model, Plushie.Effect.file_open(:import, title: "Pick a file")}
    end

    def update(model, %Plushie.Event.EffectEvent{
      tag: :import,
      result: %Result.FileOpened{path: path}
    }) do
      %{model | file: path}
    end

    def update(model, %Plushie.Event.EffectEvent{
      tag: :import,
      result: %Result.Cancelled{}
    }) do
      model
    end

## Timeouts

Each effect has a default timeout. If the renderer does not respond in
time, `%Plushie.Event.EffectEvent{tag: tag, result: %Plushie.Effect.Result.Timeout{}}`
arrives in `update/2`.

Default timeouts:

- File dialogs (`file_open`, `file_open_multiple`, `file_save`,
  `directory_select`, `directory_select_multiple`): 120 seconds
- Clipboard operations: 5 seconds
- Notifications: 5 seconds

Override the default by passing a `:timeout` option (milliseconds):

    Plushie.Effect.file_open(:import, title: "Pick a file", timeout: 300_000)

# `kind`

```elixir
@type kind() ::
  :file_open
  | :file_open_multiple
  | :file_save
  | :directory_select
  | :directory_select_multiple
  | :clipboard_read
  | :clipboard_write
  | :clipboard_read_html
  | :clipboard_write_html
  | :clipboard_clear
  | :clipboard_read_primary
  | :clipboard_write_primary
  | :notification
```

Valid effect kind atoms.

# `clipboard_clear`

```elixir
@spec clipboard_clear(tag :: atom()) :: Plushie.Command.t()
```

Clear the clipboard. Returns a command.

# `clipboard_read`

```elixir
@spec clipboard_read(tag :: atom()) :: Plushie.Command.t()
```

Read clipboard contents. Returns a command.

# `clipboard_read_html`

```elixir
@spec clipboard_read_html(tag :: atom()) :: Plushie.Command.t()
```

Read HTML content from the clipboard. Returns a command.

# `clipboard_read_primary`

```elixir
@spec clipboard_read_primary(tag :: atom()) :: Plushie.Command.t()
```

Read primary clipboard (middle-click paste on Linux). Returns a command.

# `clipboard_write`

```elixir
@spec clipboard_write(tag :: atom(), text :: String.t()) :: Plushie.Command.t()
```

Write `text` to the clipboard. Returns a command.

# `clipboard_write_html`

```elixir
@spec clipboard_write_html(
  tag :: atom(),
  html :: String.t(),
  alt_text :: String.t() | nil
) ::
  Plushie.Command.t()
```

Write HTML content to the clipboard. Returns a command.

# `clipboard_write_primary`

```elixir
@spec clipboard_write_primary(tag :: atom(), text :: String.t()) ::
  Plushie.Command.t()
```

Write `text` to the primary clipboard. Returns a command.

# `default_timeout`

```elixir
@spec default_timeout(kind :: String.t()) :: non_neg_integer() | nil
```

Returns the default timeout (in ms) for the given effect kind, or nil
if no specific default is configured.

# `directory_select`

```elixir
@spec directory_select(tag :: atom(), opts :: keyword()) :: Plushie.Command.t()
```

Directory picker. Returns a command.

# `directory_select_multiple`

```elixir
@spec directory_select_multiple(tag :: atom(), opts :: keyword()) ::
  Plushie.Command.t()
```

Multi-directory picker. Returns a command.

# `file_open`

```elixir
@spec file_open(tag :: atom(), opts :: keyword()) :: Plushie.Command.t()
```

Open-file dialog. Returns a command.

Filter format is `{"Label", "*.ext"}`. The renderer translates to
platform-native format: glob patterns on Linux (GTK), UTIs on macOS,
and filter patterns on Windows.

# `file_open_multiple`

```elixir
@spec file_open_multiple(tag :: atom(), opts :: keyword()) :: Plushie.Command.t()
```

Multi-file open dialog. Returns a command.

# `file_save`

```elixir
@spec file_save(tag :: atom(), opts :: keyword()) :: Plushie.Command.t()
```

Save-file dialog. Returns a command.

# `notification`

```elixir
@spec notification(
  tag :: atom(),
  title :: String.t(),
  body :: String.t(),
  opts :: keyword()
) ::
  Plushie.Command.t()
```

Show an OS notification. Returns a command.

On macOS, notifications may require the app to be bundled (.app) or have
notification entitlements to display.

## Options

  * `:icon` - Icon name or path (string).
  * `:timeout` - Auto-dismiss timeout in milliseconds (integer).
  * `:urgency` - `:low`, `:normal`, or `:critical` (atom).
  * `:sound` - Sound name to play (string).

# `request`

```elixir
@spec request(tag :: atom(), kind :: kind(), opts :: keyword()) :: Plushie.Command.t()
```

Generic effect request. Returns a command struct.

`tag` is an atom that identifies this effect. It appears in the
`%EffectEvent{tag: tag}` result event. `kind` must be one of the supported
effect types. `opts` is a keyword list of parameters sent as the effect
payload.

---

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