---
metadata:
  - name: generator
    content: Diplodoc Platform v5.54.5
  - property: og:type
    content: article
  - property: article:section
    content: Платформа плагинов
  - property: og:title
    content: Слот «Действие в галерее» (attachment.viewer.action)
  - property: article:tag
    content: Техническая инструкция
alternate:
  - https://yandex.com.tr/support/tracker/en/plugins/slots/attachment-viewer-action.md
  - href: en/plugins/slots/attachment-viewer-action.md
    type: text/markdown
    title: Markdown version
  - href: ../../llms.txt
    type: text/markdown
    title: llms.txt
---
> **Documentation Index:** Fetch the complete configuration index at https://yandex.com.tr/support/tracker/en/llms.txt


# The `attachment.viewer.action` slot

The `attachment.viewer.action` slot allows you to embed a plugin into the attachment viewer gallery. A plugin in this slot receives the binary data of the attachment and its metadata, displays its own UI (for example, an image editor), and allows you to return files to Tracker.

## 1. Creating a project via the CLI

To create a new plugin for this slot, use the application creation command and select the required template in the dialog:

```bash
yaweavix create
```

When prompted with **"Select a template:"**, choose **attachment.viewer.action**.
You will most likely need the **tracker:attachments:write** permission.

Fill in the remaining steps as requested. As a result, a project will be generated with a manifest already configured for the `attachment.viewer.action` slot and a code stub.

## 2. What is the attachment.viewer.action slot

The **attachment.viewer.action** slot is an integration point in the attachment viewer gallery in Tracker. A plugin in this slot:

- Adds a new action (button) to the gallery alongside all other available plugins.
- When the plugin is selected, it opens in a modal window.
- Receives the file contents as a blob and the attachment metadata from Tracker via `slotContext`.

When the plugin is closed, you can send a payload with an array of attachments. Tracker will process it and add each file to the current entity — the corresponding function will be called for each attachment in the array.

For example, if the plugin is opened from an issue description and the `close` method is called internally with a payload, Tracker will add the provided files to that issue.

## 3. Plugin manifest

In the **manifest**, you need to declare the `attachment.viewer.action` slot.

Example `manifest.json` structure:

```json
{
    "$schema": "./manifest.schema.json",
    "slug": "attachment-viewer-action",
    "version": "0.1.0",
    "permissions": {
        "data": ["tracker:attachments:read", "tracker:attachments:write"]
    },
    "slots": {
        "tracker": {
            "attachment.viewer.action": [
                {
                    "entrypoint": "index.html",
                    "title": {
                        "ru": "Название плагина",
                        "en": "Plugin name"
                    },
                    "description": {
                        "ru": "Описание плагина",
                        "en": "Description of the plugin"
                    }
                }
            ]
        }
    }
}
```

{% note alert "Important" %}

- The `title` value is displayed as the button label in the gallery—choose a short and clear name.
- **entrypoint** is the plugin's entry point, usually `index.html`.

{% endnote %}

## 4. Slot context

The plugin receives attachment data via `slotContext`. The context type is:

```ts
export type AttachmentViewerActionSlotContext = {
    attachmentBlob: Blob;
    meta: {
        id: string;
        url: string;
        date: string;
        size: number;
        mimetype: string;
    };
};
```

Fields:

| Field            | Type     | Description                             |
| ---------------- | -------- | --------------------------------------- |
| `attachmentBlob` | `Blob`   | Binary file content                     |
| `meta.id`        | `string` | Attachment identifier                   |
| `meta.url`       | `string` | File URL                                |
| `meta.date`      | `string` | Upload date                             |
| `meta.size`      | `number` | File size in bytes                      |
| `meta.mimetype`  | `string` | File MIME type, for example `image/png` |

Get the context in the code:

```tsx
const { slotContext } = useTrackerPluginContext();

const { attachmentBlob, meta } = slotContext;
```

## 5. Returning data to Tracker {#return-data}

To return the result of the plugin's work, you need to:

1.  Upload the modified file to Tracker using `trackerApi`.
2.  Close the plugin using `hostApi.close`, passing the created attachments.

Tracker will process the `payload` and add each file from the `attachments` array to the current entity. For example, if the plugin is opened from an issue description, the files will be attached to that issue.

#### Replacing the original attachment {#replace}

If you pass the `replace: true` flag in the `payload`, the original attachment will be replaced by the new one instead of adding an additional file.

Example:

```ts
hostApi.close({ attachments: [response.data], replace: true });
```

### 5.1. The useAttachmentSave hook

It's convenient to extract the saving logic into a separate hook:

```tsx
import { hostApi, trackerApi } from "@yandex-data-ui/tracker-plugin-sdk-react";
import { useState, useCallback } from "react";

type UseAttachmentSaveReturn = {
    save: (blob: Blob, filename: string) => Promise<void>;
    loading: boolean;
    error: Error | null;
    success: boolean;
    reset: () => void;
};

export function useAttachmentSave(): UseAttachmentSaveReturn {
    const [loading, setLoading] = useState(false);
    const [error, setError] = useState<Error | null>(null);
    const [success, setSuccess] = useState(false);

    const save = useCallback(async (blob: Blob, filename: string) => {
        try {
            setLoading(true);
            setError(null);
            setSuccess(false);

            const file = new File([blob], filename, {
                type: blob.type || "image/png",
            });

            const response = await trackerApi.v3.post["/attachments"]({
                bodyParams: { filename },
                file,
            });

            hostApi.close({ attachments: [response.data], replace: true });

            setSuccess(true);
        } catch (e) {
            setError(e as Error);
        } finally {
            setLoading(false);
        }
    }, []);

    const reset = useCallback(() => {
        setLoading(false);
        setError(null);
        setSuccess(false);
    }, []);

    return { save, loading, error, success, reset };
}
```

Here:

- **`trackerApi.v3.post['/attachments']`** uploads the file to Tracker and returns the data of the created attachment.
- **`hostApi.close({ attachments: [...] })`** closes the plugin's modal window and passes an array of attachments to Tracker for attaching to the entity.

## 6. Minimum application structure

1.  **Entry point** (for example, `main.tsx`): renders into the DOM root and wraps the app in **TrackerPluginProvider**.
2.  **Root component** (for example, `App.tsx`):
    - uses **useTrackerPluginContext** and gets `theme`, `slotContext`;
    - reads `attachmentBlob` and `meta` from `slotContext`;
    - if necessary, uses `useAttachmentSave` to upload the file and close the plugin via `hostApi.close`;
    - renders the UI (editor, viewer, etc.).

Wrapping with the provider is mandatory; otherwise, the plugin won't be able to get the context and interact with Tracker:

```tsx
import { TrackerPluginProvider } from "@yandex-data-ui/tracker-plugin-sdk-react";

root.render(
    <TrackerPluginProvider>
        <App />
    </TrackerPluginProvider>,
);
```

## 7. Don't forget about debugging

[How to debug](https://yandex.com.tr/support/tracker/en/plugins/tools/cli.md#debug).

## 8. Pre-publication checklist

- The **attachment.viewer.action** slot is specified in the manifest with a clear `title`.
- `slotContext` is read in the root component — `attachmentBlob` and `meta` are accessible.
- If the plugin returns a modified file — `trackerApi` is used for uploading and `hostApi.close` is used to pass the result to Tracker.
