> For the complete documentation index, see [llms.txt](https://seanime.gitbook.io/seanime-extensions/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://seanime.gitbook.io/seanime-extensions/plugins/ui/user-interface/action.md).

# Action

The `ctx.action` API allows your plugin to add UI elements that trigger custom actions to different parts of the Seanime interface.

## Core Methods

### newAnimePageButton

Creates a button that appears on anime detail pages.

**Parameters:**

* `props`: Object containing:
  * `label`: String - Button text
  * `intent`: String (Optional) - Button style ("primary", "success", "warning", etc.)
  * `style`: Object (Optional) - Custom CSS styles

**Example:**

```typescript
// Create a play button for anime pages
const playButton = ctx.action.newAnimePageButton({
  label: "Play All Episodes",
  intent: "primary",
  style: { marginRight: "8px" }
})

// Mount the button to make it visible
playButton.mount()

// Handle clicks
playButton.onClick((event) => {
  const anime = event.media
  console.log(`Play all episodes for: ${anime.title.userPreferred}`)
  // Implement playback logic
})
```

### newAnimePageDropdownItem

Creates a dropdown menu item that appears in the anime page's action menu.

**Parameters:**

* `props`: Object containing:
  * `label`: String - Menu item text
  * `style`: Object (Optional) - Custom CSS styles

**Example:**

```typescript
// Add a download option to anime page dropdown
const downloadItem = ctx.action.newAnimePageDropdownItem({
  label: "Download Episodes"
})

downloadItem.mount()

downloadItem.onClick((event) => {
  const anime = event.media
  console.log(`Preparing download for: ${anime.title.userPreferred}`)
  // Show download dialog
})
```

### newAnimeLibraryDropdownItem

Creates a dropdown menu item that appears in the anime library's global action menu.

**Parameters:**

* `props`: Object containing:
  * `label`: String - Menu item text
  * `style`: Object (Optional) - Custom CSS styles

**Example:**

```typescript
// Add a scan option to library menu
const scanItem = ctx.action.newAnimeLibraryDropdownItem({
  label: "Scan for Missing Files"
})

scanItem.mount()

scanItem.onClick(() => {
  console.log("Starting library scan")
  // Implement scan logic
})
```

### newMediaCardContextMenuItem

Creates a context menu item that appears when right-clicking on media cards.

**Parameters:**

* `props`: Object containing:
  * `label`: String - Menu item text
  * `for`: String (Optional) - Which media types to show for ("anime", "manga", or "both")
  * `style`: Object (Optional) - Custom CSS styles

**Example:**

```typescript
// Add a quick-watch option to anime cards
const watchItem = ctx.action.newMediaCardContextMenuItem({
  label: "Quick Watch",
  for: "anime" // Only show for anime cards
})

watchItem.mount()

watchItem.onClick((event) => {
  const media = event.media
  console.log(`Quick watching: ${media.title.userPreferred}`)
  // Implement quick watch feature
})
```

### newMangaPageButton

Creates a button that appears on manga detail pages.

**Parameters:**

* `props`: Object containing:
  * `label`: String - Button text
  * `intent`: String (Optional) - Button style ("primary", "success", "warning", etc.)
  * `style`: Object (Optional) - Custom CSS styles

**Example:**

```typescript
// Create a read button for manga pages
const readButton = ctx.action.newMangaPageButton({
  label: "Continue Reading",
  intent: "primary"
})

readButton.mount()

readButton.onClick((event) => {
  const manga = event.media
  console.log(`Opening reader for: ${manga.title.userPreferred}`)
  // Open manga reader
})
```

### newEpisodeCardContextMenuItem

Creates an item that appears on episode card context menus.

**Parameters:**

* `props`: Object containing:

  * `label`: String - Button text
  * `style`: Object (Optional) - Custom CSS styles
  * `type?` : "library" | "torrentstream" | "debridstream" | string | undefined

{% hint style="info" %}
`type` can also be `episodeTab:<extension-id>` when using `ctx.anime.registerEntryEpisodeTab`
{% endhint %}

```typescript
const episodeDetailsButton = ctx.action.newEpisodeCardContextMenuItem({
  label: "View details",
})

episodeDetailsButton.mount()

episodeDetailsButton.onClick((event) => {
  // You get the episode object
  const episode = event.episode
})
```

### newEpisodeGridItemMenuItem

Creates an item that appears on episode grid item menus.

**Parameters:**

* `props`: Object containing:
  * `label`: String - Button text
  * `type` : "library" | "torrentstream" | "debridstream" | "onlinestream" | "undownloaded" | "medialinks" | "mediastream" | string
  * `style`: Object (Optional) - Custom CSS styles

{% hint style="info" %}
`type` can also be `episodeTab:<extension-id>` when using `ctx.anime.registerEntryEpisodeTab`
{% endhint %}

```typescript
const episodeDetailsButton = ctx.action.newEpisodeGridItemMenuItem({
  label: "View details",
  type: "library"
})

episodeDetailsButton.mount()

episodeDetailsButton.onClick((event) => {
  // You get the episode object
  // It is of type [Anime_Episode], unless the chosen type is 'onlinestream',
  // in which case it will be of type [Onlinestream_Episode]
  const episode = event.episode
})
```

### Action Object Methods

All action objects share these common methods:

#### mount()

Makes the action visible in the UI.

**Example:**

```typescript
const button = ctx.action.newAnimePageButton({ label: "My Button" })
button.mount() // Now visible
```

#### unmount()

Removes the action from the UI.

**Example:**

```typescript
// Remove when no longer needed
button.unmount()
```

#### setLabel(label)

Updates the action's label text.

**Parameters:**

* `label`: String - New label text

**Example:**

```typescript
// Change button text based on state
if (isDownloading) {
  button.setLabel("Downloading...")
} else {
  button.setLabel("Download")
}
```

#### setStyle(style)

Updates the action's custom CSS styles.

**Parameters:**

* `style`: Object - CSS style properties

**Example:**

```typescript
// Highlight button when active
if (isActive) {
  button.setStyle({ backgroundColor: "#4caf50", color: "white" })
} else {
  button.setStyle({})
}
```

#### onClick(callback)

Sets a function to be called when the action is clicked.

**Parameters:**

* `callback`: Function(event) - Function to call when clicked

**Example:**

```typescript
button.onClick((event) => {
  // For anime/manga actions, event contains the media object
  if (event.media) {
    console.log(`Clicked on ${event.media.title.userPreferred}`)
  }
  
  // Your custom action logic
  performAction()
})
```

### Additional Properties

#### AnimePageButton and MangaPageButton

These button types have an additional method:

**setIntent(intent)**

Sets the button's visual style.

**Parameters:**

* `intent`: String - Intent style ("primary", "success", "warning", "error", etc.)

**Example:**

```typescript
const button = ctx.action.newAnimePageButton({ label: "Watch" })
button.setIntent("primary") // Blue button
// Other options: "primary-subtle", "success", "warning", "alert", etc.
```

#### MediaCardContextMenuItem

This action type has an additional method:

**setFor(type)**

Sets which media types the context menu item appears for.

**Parameters:**

* `type`: String - "anime", "manga", or "both"

**Example:**

```typescript
const menuItem = ctx.action.newMediaCardContextMenuItem({ label: "Open" })
menuItem.setFor("anime") // Only show for anime cards
```

### Best Practices

#### Limit Number of Actions

Each plugin is limited to a maximum of 3 actions per type. Choose the most important actions to display.

#### Dynamic UI Updates

Update action properties based on application state:

```typescript
// Good: Update button state dynamically
let isProcessing = false

button.onClick((event) => {
  if (isProcessing) return
  
  isProcessing = true
  button.setLabel("Processing...")
  button.setIntent("warning")
  button.mount()
  
  performLongOperation().then(() => {
    isProcessing = false
    button.setLabel("Done!")
    button.setIntent("success")
    button.mount()
    
    // Reset after a delay
    setTimeout(() => {
      button.setLabel("Process Again")
      button.setIntent("primary")
      button.mount()
    }, 3000)
  })
})
```

#### Conditional Mounting

Only mount actions when they're relevant:

```typescript
// Good: Mount/unmount based on context
function updateButtonVisibility(media) {
  if (media.format === "MOVIE") {
    watchButton.mount()
    episodesButton.unmount() // No episodes for movies
  } else {
    watchButton.mount()
    episodesButton.mount()
  }
}
```
