# `PhoenixKitWeb.Components.Core.MarkdownEditor`
[🔗](https://github.com/BeamLabEU/phoenix_kit/blob/v1.7.165/lib/phoenix_kit_web/components/core/markdown_editor.ex#L1)

Reusable markdown editor LiveComponent with cursor tracking,
markdown formatting toolbar, component insertion, and unsaved changes protection.

## Features

- Monospace textarea optimized for markdown/code editing
- **Markdown formatting toolbar** with selection-aware text manipulation
- Cursor position tracking for inserting components at cursor
- Optional toolbar for inserting images, videos, etc.
- Save status indicator (saving/unsaved/saved)
- Browser navigation protection for unsaved changes
- Debounced content updates

## Usage

    <.live_component
      module={PhoenixKitWeb.Components.Core.MarkdownEditor}
      id="content-editor"
      content={@content}
      save_status={@save_status}
      show_formatting_toolbar={true}
      toolbar={[:image, :video]}
      on_change="content_changed"
      on_save="save_content"
      on_insert_component="insert_component"
    />

## Formatting Toolbar

The formatting toolbar includes:
- **Headings**: H1-H6 (adds `#` prefix to current line)
- **Inline styles**: Bold, Italic, Strikethrough, Inline Code (wraps selection)
- **Links**: Prompts for URL and wraps selection as link text
- **Lists**: Bullet and numbered lists (adds prefix to current line)

When text is selected, formatting wraps the selection. When no text is
selected, a placeholder is inserted and auto-selected for easy replacement.

## Events Sent to Parent — required host wiring (silent failure otherwise)

This is a `LiveComponent`, so it has no `handle_info` of its own: it
reports edits by sending **process messages to the host LiveView** via
`send/2`. The host MUST handle these, or the edits are silently lost
(the editor looks live but the parent never sees the typed content —
no crash, no warning):

- `{:editor_content_changed, %{content: content, editor_id: id}}` —
  **required.** Content updated; the host folds `content` into its own
  changeset/assign. Forget this and the form never sees what's typed.
- `{:editor_insert_component, %{type: :image | :video, editor_id: id}}` —
  toolbar insert button clicked (handle if you support media inserts).
- `{:editor_save_requested, %{editor_id: id}}` — save button clicked.

Each host folds the content into its own form differently, so there is
intentionally no `use ...Embed` macro — the handling is yours to write.

## Commands from Parent

Use `send_update/2` to send commands to the component:

    # Insert text at cursor position
    send_update(PhoenixKitWeb.Components.Core.MarkdownEditor,
      id: "content-editor",
      action: :insert_at_cursor,
      text: "<Image file_id=\"abc123\" />"
    )

    # Prompt the user (window.prompt) on the client, then insert the value by
    # substituting `%{value}` in `template` (e.g. a video URL):
    send_update(PhoenixKitWeb.Components.Core.MarkdownEditor,
      id: "content-editor",
      action: :prompt_insert,
      prompt: "Enter YouTube URL:",
      template: "\n![Video](%{value})\n"
    )

## JavaScript / CSP

All behavior is driven by the `MarkdownEditor` LiveView hook (shipped in
`priv/static/assets/phoenix_kit.js` as `window.PhoenixKitHooks.MarkdownEditor`),
not by inline `<script>` or `onclick=` handlers. That means it works under a
strict Content-Security-Policy with no nonce and survives LiveView navigation.
The host only needs the vendored `phoenix_kit.js` loaded (the standard install;
`mix phoenix_kit.update` refreshes it) and `window.PhoenixKitHooks` spread into
its LiveSocket — exactly like every other PhoenixKit hook.

---

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