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


# Как создать плагин «Действие в галерее»

Слот `attachment.viewer.action` позволяет встроить плагин в галерею просмотра вложений. Плагин в этом слоте получает бинарные данные вложения и его метаданные. Он показывает свой UI, например редактор изображений, и позволяет вернуть файлы в Трекер.

## 1. Создание проекта через CLI {#create-project}

Чтобы создать новый плагин для этого слота, используйте команду создания приложения и в диалоге выберите нужный шаблон:

```bash
weavix create
```

В ответ на вопрос **Select a template:** выберите **attachment.viewer.action**.
Скорее всего, вам понадобится разрешение **tracker:attachments:write**.

Остальные шаги заполните по запросу. В результате будет сгенерирован проект с уже настроенным манифестом под слот `attachment.viewer.action` и заготовкой кода.

## 2. Что такое слот attachment.viewer.action {#what-is}

Слот **attachment.viewer.action** — это точка интеграции в галерее просмотра вложений в Трекере. Плагин в этом слоте:

- добавляет новое действие в виде кнопки в галерее со всеми доступными плагинами;
- при выборе плагина он открывается в модальном окне;
- получает от Трекера содержимое файла в формате blob и метаданные вложения через `slotContext`.

При закрытии плагина можно передать payload с массивом вложений. Трекер обработает его и добавит каждый файл к текущей сущности — для каждого вложения из массива будет вызвана соответствующая функция.

Например, если плагин открыт из описания тикета и внутри вызван метод close с payload, Трекер добавит переданные файлы к этому тикету.

## 3. Манифест плагина {#manifest}

В **манифесте** нужно объявить слот `attachment.viewer.action`.

Пример структуры `manifest.json`:

```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 "Важно" %}

- Значение `title` отображается как подпись кнопки действия в галерее — выбирайте короткое и понятное название.
- **entrypoint** — точка входа плагина, обычно `index.html`.

{% endnote %}

## 4. Контекст слота {#slot-context}

Плагин получает данные вложения через `slotContext`. Тип контекста:

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

Поля:

| Поле             | Тип      | Описание                             |
| ---------------- | -------- | ------------------------------------ |
| `attachmentBlob` | `Blob`   | Бинарное содержимое файла            |
| `meta.id`        | `string` | Идентификатор вложения               |
| `meta.url`       | `string` | URL файла                            |
| `meta.date`      | `string` | Дата загрузки                        |
| `meta.size`      | `number` | Размер файла в байтах                |
| `meta.mimetype`  | `string` | MIME-тип файла, например `image/png` |

Получить контекст в коде:

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

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

## 5. Возврат данных в Трекер {#return-data}

Чтобы вернуть результат работы плагина, нужно:

1. Загрузить измененный файл в Трекер через `trackerApi`.
2. Закрыть плагин через `hostApi.close`, передав созданные вложения.

Трекер обработает `payload` и добавит каждый файл из массива `attachments` к текущей сущности. Например, если плагин открыт из описания тикета, файлы будут прикреплены к этому тикету.

#### Замена оригинального вложения {#replace}

Если передать флаг `replace: true` в `payload`, оригинальное вложение будет заменено на новое вместо добавления дополнительного файла.

Пример:

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


### 5.1. Хук useAttachmentSave {#use-attachment-save}

Удобно вынести логику сохранения в отдельный хук:

```tsx
import { hostApi, trackerApi } from "@weavix/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 };
}
```

Здесь:

- **`trackerApi.v3.post['/attachments']`** — загружает файл в Трекер и возвращает данные созданного вложения.
- **`hostApi.close({ attachments: [...] })`** — закрывает модальное окно плагина и передает Трекеру массив вложений для прикрепления к сущности.

## 6. Минимальная структура приложения {#app-structure}

1. **Точка входа**, например `main.tsx`: рендер в корень DOM и обертка в **TrackerPluginProvider**.
2. **Корневой компонент**, например `App.tsx`:
    - использует **useTrackerPluginContext** и получает `theme`, `slotContext`;
    - читает `attachmentBlob` и `meta` из `slotContext`;
    - при необходимости использует `useAttachmentSave` для загрузки файла и закрытия плагина через `hostApi.close`;
    - рендерит UI (редактор, просмотрщик и т.д.).

Обертка провайдером обязательна, иначе плагин не сможет получить контекст и взаимодействовать с Трекером:

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

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

## 7. Не забывайте про отладку {#debug}

[Как отлаживать плагин](https://yandex.ru/support/tracker/ru/plugins/tools/cli.md#debug).

## 8. Чеклист перед публикацией {#checklist}

- В манифесте указан слот **attachment.viewer.action** с понятным `title`.
- В корневом компоненте читается `slotContext` — доступны `attachmentBlob` и `meta`.
- Если плагин возвращает измененный файл — используется `trackerApi` для загрузки и `hostApi.close` для передачи результата Трекеру.
