Сайт

Window

sdk.window охватывает всё, что пользователь видит за пределами графа. Здесь же создаются вебвью — у них своя страница.

Уведомления

showInfoMessage(title: string, message?: string, options?: Partial<SDkNotificationOptions>): Promise<SDkNotificationHandle | undefined>
showWarnMessage(title, message?, options?)
showErrorMessage(title, message?, options?)
showSuccessMessage(title, message?, options?)

Метод задаёт вид уведомления (info, warning, error, success). Promise разрешается дескриптором или undefined, если уведомление не удалось показать.

| Опция | Описание | | --- | --- | | sticky | Не закрывать автоматически. По умолчанию false. | | timeout | Задержка автозакрытия в мс. По умолчанию 5000; 0 тоже означает значение по умолчанию. Игнорируется, пока задан sticky или loading. | | progress | Прогресс-бар, 0–100, или null — скрыть (по умолчанию). Не показывается при loading. | | loading | Показать спиннер. Уведомление с loading не закрывается автоматически. По умолчанию false. | | type | NotificationType: 'info', 'warning', 'error' или 'success'. Переопределяет вид, заданный методом. | | title / message | То же, что аргументы — пригодится в update(). | | source | Сейчас ни на что не влияет: источником всегда показывается display name (или id) вашего расширения. | | severity | Сейчас ни на что не влияет. |

Пока задан loading или ненулевой progress, у уведомления нет кнопки закрытия. Уведомление с progress всё равно закроется через timeout, если оно не sticky.

Дескриптор позволяет управлять уведомлением после его показа:

| Опция | Описание | | --- | --- | | id | Id уведомления. | | update(options) | Изменить заголовок, текст или любую опцию (кроме source). | | progress(value) | Прогресс-бар (0–100, null — скрыть). | | loading(value) | Спиннер вкл/выкл. | | dismiss() | Закрыть уведомление. | | dispose() | То же, что dismiss() — handle можно положить в context.subscriptions. | | markAsRead() | Пометить прочитанным, не закрывая. |

Вызовы на уже закрытом уведомлении ничего не делают.

const handle = await sdk.window.showInfoMessage('Деплой', 'Загружаем образ…', { sticky: true })
handle?.progress(40)
// ...
handle?.dismiss()
SDkNotificationOptions
interface SDkNotificationOptions {
  title: string
  message?: string
  type?: NotificationType // 'info' | 'warning' | 'error' | 'success'
  severity?: NotificationSeverity // 0 | 1 | 2 | 3 — сейчас ни на что не влияет
  source?: string // сейчас ни на что не влияет
  sticky?: boolean // по умолчанию false
  timeout?: number // мс, по умолчанию 5000
  progress?: number | null // 0–100, по умолчанию null
  loading?: boolean // по умолчанию false
}
SDkNotificationHandle
interface SDkNotificationHandle {
  readonly id: string
  update(options: Partial<SDkNotificationOptions>): void
  progress(value: number | null): void
  loading(value: boolean): void
  dismiss(): void
  markAsRead(): void
  dispose(): void
}

Быстрый ввод

showInputBox(options: SdkInputBoxOptions, abortSignal?: AbortSignal): Promise<string | undefined>
showQuickPick(items: QuickPickItemEntry[], options?: QuickPickOptions, abortSignal?: AbortSignal): Promise<QuickPickResult<QuickPickOptions> | undefined>
showConfirm(options?: ConfirmOptions, abortSignal?: AbortSignal): Promise<boolean | undefined>

Что возвращает каждый вызов:

| Метод | Результат | | --- | --- | | showInputBox | Введённый текст. undefined, если пользователь отменил ввод (Escape, закрытие) или отправил пустое значение. | | showQuickPick | Выбранный элемент или массив элементов при canSelectMany: true. undefined, если пользователь отменил выбор или принял пустой выбор. | | showConfirm | true — подтверждено, false — отменено (Cancel, Escape, закрытие). undefined — только если диалог не удалось показать. |

Срабатывание abortSignal закрывает диалог и завершает вызов как отменённый (undefined, для showConfirm — false).

Input box

| Опция | Описание | | --- | --- | | title | Заголовок. | | value | Начальное значение. | | placeholder | Текст-заглушка. | | prompt | Подсказка под полем. | | password | Скрывать ввод. Игнорируется при textArea. | | textArea | Многострочное поле. Enter отправляет значение, поэтому перенос строки не ввести. | | wrapperClassName | CSS-класс контейнера диалога. | | validateInputDebounceMs | Сейчас ни на что не влияет — функцию валидации нельзя передать через SDK. | | skippable | Сейчас ни на что не влияет — показывает кнопку Skip, которая ничего не делает. Не задавайте. |

Быстрый выбор

Элементы — объекты QuickPickItem, между группами — разделители { type: 'separator' }:

| Поле | Описание | | --- | --- | | id | Id элемента — возвращается в результате. | | displayName | Подпись. Участвует в поиске. | | type | 'item'. | | detail | Дополнительный текст справа. Участвует в поиске. | | picked | Отмечен заранее в режиме множественного выбора (только элементы верхнего уровня). | | children | Вложенные элементы. Выбор элемента (или →) открывает подсписок; Escape или ← — назад. В режиме множественного выбора выбранный дочерний элемент заменяет выбранного соседа и возвращает к родительскому списку. | | commandId | Показывает рядом с элементом сочетание клавиш этой команды. Выбор элемента команду не запускает. | | color | Сейчас ни на что не влияет. |

| Опция | Описание | | --- | --- | | title | Заголовок. | | placeholder | Заглушка поля поиска. | | value | id элемента, подсвеченного при открытии списка. | | canSelectMany | Режим с чекбоксами: клик или Enter переключает элемент, кнопка Accept подтверждает. Результат становится массивом. | | acceptLabel | Подпись кнопки Accept. По умолчанию "Accept". | | hideSearch | Скрыть поле поиска. | | wrapperClassName | CSS-класс контейнера диалога. | | skippable | Сейчас ни на что не влияет — показывает кнопку Skip, которая ничего не делает. Не задавайте. |

В режиме одиночного выбора клик по элементу или Enter на нём сразу завершает вызов.

const pick = await sdk.window.showQuickPick(
  [
    { id: 'dev', displayName: 'Development', type: 'item' },
    { id: 'prod', displayName: 'Production', type: 'item' },
  ],
  { title: 'Выберите окружение' },
)
if (pick) { /* pick.id */ }

Подтверждение

| Опция | Описание | | --- | --- | | title | Заголовок. | | message | Текст. Вставьте в него {highlight}, чтобы показать highlightedText бейджем в этом месте. | | highlightedText | Текст на месте {highlight}. | | confirmLabel | Подпись кнопки подтверждения. По умолчанию 'Confirm'. | | cancelLabel | Подпись кнопки отмены. По умолчанию 'Cancel'. | | wrapperClassName | CSS-класс контейнера диалога. |

Enter подтверждает, Escape отменяет.

import type { ConfirmOptions } from '@fragmenta/extension'

const ui = sdk.window as typeof sdk.window & {
  showConfirm(options?: ConfirmOptions, abortSignal?: AbortSignal): Promise<boolean | undefined>
}

const confirmed = await ui.showConfirm({
  title: 'Удаление окружения',
  message: 'Удалить {highlight}? Это нельзя отменить.',
  highlightedText: 'production',
  confirmLabel: 'Удалить',
})
if (confirmed) { /* ... */ }
SdkInputBoxOptions
interface SdkInputBoxOptions {
  title?: string
  value?: string
  placeholder?: string
  prompt?: string
  password?: boolean
  textArea?: boolean
  wrapperClassName?: string
  validateInputDebounceMs?: number // сейчас ни на что не влияет
  skippable?: boolean // сейчас ни на что не влияет
}
QuickPickItemEntry
type QuickPickItemEntry = QuickPickItem | QuickPickSeparator

interface QuickPickItem {
  id: string
  displayName: string
  type: 'item'
  detail?: string
  picked?: boolean
  children?: QuickPickItemEntry[]
  commandId?: string
  color?: string // сейчас ни на что не влияет
}

interface QuickPickSeparator {
  type: 'separator'
}
QuickPickOptions
interface QuickPickOptions {
  title?: string
  placeholder?: string
  value?: string
  canSelectMany?: boolean
  acceptLabel?: string
  hideSearch?: boolean
  wrapperClassName?: string
  skippable?: boolean // сейчас ни на что не влияет
}
QuickPickResult
type QuickPickResult<T extends QuickPickOptions> = T extends { canSelectMany: true }
  ? QuickPickItem[] | undefined
  : QuickPickItem | undefined
ConfirmOptions
interface ConfirmOptions {
  title?: string
  message?: string
  highlightedText?: string
  confirmLabel?: string // по умолчанию 'Confirm'
  cancelLabel?: string // по умолчанию 'Cancel'
  wrapperClassName?: string
  skippable?: boolean // для confirm ни на что не влияет
}

Модальные окна

showModal(options: SdkModalOptions): Promise<void>
hideModal(): Promise<void>

Модальное окно содержит либо вебвью ({ webviewId } — id из handle webview), либо встроенное представление ({ viewId, viewArgs? }).

| Опция | Описание | | --- | --- | | size | Пресет или свой размер (см. ниже). По умолчанию 'md'. | | position | 'center' (по умолчанию), 'top', 'bottom', 'left' или 'right'. | | closeOnOverlayClick | Закрывать по клику вне модалки. По умолчанию true. | | closeOnEscape | Закрывать по Escape. По умолчанию true. | | preventScroll | По умолчанию true: модальный диалог с затемнённым фоном, который блокирует прокрутку и удерживает фокус внутри. false — показать без затемнения. | | transparent | Без фона, рамки и тени — рамку рисует ваш контент. По умолчанию false. | | className | Дополнительный CSS-класс контейнера модалки. |

Пресеты размера: 'xs', 'sm', 'md', 'lg', 'xl', '2xl' задают возрастающую максимальную ширину, высота подстраивается под контент. 'full' — до 90% окна по обеим осям, 'auto' — по ширине контента.

Пользовательский размер: { width, height, maxWidth, maxHeight, minWidth, minHeight }, все поля необязательны. Строки — CSS-значения ('85%', '40rem'), числа — пиксели.

Одновременно может быть открыто только одно модальное окно — вызов showModal заменяет текущее. showModal разрешается, когда модальное окно открыто, а не когда оно закрывается. Если представление модального окна не существует, ничего не открывается, и promise всё равно разрешается. hideModal закрывает любое открытое модальное окно. Закрытие модального окна не освобождает его веб-представление.

const webview = await sdk.window.createWebview('mapper-view', { html })
await sdk.window.showModal({
  webviewId: webview.id,
  position: 'center',
  size: { width: '85%', height: '85%' },
  closeOnEscape: true,
})
SdkModalOptions
type SdkModalOptions =
  | (SdkModalBaseOptions & { webviewId: string })
  | (SdkModalBaseOptions & { viewId: string; viewArgs?: unknown })

interface SdkModalBaseOptions {
  size?: ModalSizePreset | ModalCustomSize // по умолчанию 'md'
  position?: 'center' | 'top' | 'bottom' | 'left' | 'right' // по умолчанию 'center'
  closeOnOverlayClick?: boolean // по умолчанию true
  closeOnEscape?: boolean // по умолчанию true
  preventScroll?: boolean // по умолчанию true
  transparent?: boolean // по умолчанию false
  className?: string
}

type ModalSizePreset = 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | 'full' | 'auto'

interface ModalCustomSize {
  width?: string | number
  height?: string | number
  maxWidth?: string | number
  maxHeight?: string | number
  minWidth?: string | number
  minHeight?: string | number
}

Файловые диалоги

showOpenDialog(options: SdkFsOpenDialogOptions): Promise<FsHandle[] | undefined>
showSaveDialog(options: SdkFsSaveDialogOptions): Promise<FsHandle | undefined>

showOpenDialog возвращает выбранные записи или undefined, если пользователь отменил выбор.

| Опция | Описание | | --- | --- | | title | Заголовок диалога. | | buttonLabel | Подпись кнопки подтверждения. | | canSelectFiles | Разрешить файлы. По умолчанию true. | | canSelectFolders | Разрешить папки. По умолчанию false. | | canSelectMany | Разрешить несколько записей. По умолчанию false. | | filters | [{ name, extensions }]. Все фильтры объединяются в один список разрешённых расширений, name не показывается. Расширения можно писать с точкой или без; '*' игнорируется. |

Когда разрешены и файлы, и папки, macOS позволяет пользователю выбрать любое, а в Windows и Linux показывается выбор папки. В браузере title и buttonLabel игнорируются, и если выбор папок разрешён, всегда показывается выбор папки.

Возвращаемый FsHandle даёт scoped-доступ к выбранной записи:

| Член | Описание | | --- | --- | | kind | 'file' или 'directory'. У записей из readDir() также бывает 'symbolicLink' или 'unknown'. | | name | Имя файла или папки. | | path | Полный путь в формате ОС (на Windows через \). | | size | Размер в байтах; 0 у папок. | | type | 'application/octet-stream' у файлов и '' у папок. Файлы, выбранные в браузере, сообщают свой MIME-тип. | | readFile() | Promise<Uint8Array>. | | readTextFile() | Promise<string>. | | stat() | Promise<FileStats \| null> — { name, size, type }. | | readDir() | Записи папки с name, type, полным path и собственным handle — так обходятся вложенные папки. | | writeFile(content) | Записать Uint8Array или строку. См. ограничение ниже. |

Дескрипторы принадлежат вашему расширению и истекают через 30 минут после выдачи — вызовы на истёкшем дескрипторе отклоняются. readDir() выдаёт записям новые дескрипторы.

const [folder] = (await sdk.window.showOpenDialog({ canSelectFiles: false, canSelectFolders: true })) ?? []
for (const entry of (await folder?.readDir()) ?? []) {
  if (entry.handle.kind === 'directory') await entry.handle.readDir()
  else await entry.handle.readTextFile()
}
SdkFsOpenDialogOptions / SdkFsSaveDialogOptions
interface SdkFsOpenDialogOptions {
  title?: string
  buttonLabel?: string
  canSelectFiles?: boolean // по умолчанию true
  canSelectFolders?: boolean // по умолчанию false
  canSelectMany?: boolean // по умолчанию false
  filters?: SdkFsFileFilter[]
}

interface SdkFsSaveDialogOptions {
  title?: string
  buttonLabel?: string
  defaultFileName?: string
  filters?: SdkFsFileFilter[]
}

interface SdkFsFileFilter {
  name: string
  extensions: string[]
}
FsHandle
type FsHandleKind = 'file' | 'directory' | 'symbolicLink' | 'unknown'

interface FsHandle {
  readonly kind: FsHandleKind
  readonly name: string
  readonly path: string
  readonly size: number
  readonly type: string
  readonly readFile: () => Promise<Uint8Array>
  readonly readTextFile: () => Promise<string>
  readonly writeFile: (content: Uint8Array | string) => Promise<void>
  readonly stat: () => Promise<FileStats | null>
  readonly readDir: () => Promise<FsHandleDirEntry[]>
}

interface FsHandleDirEntry {
  readonly name: string
  readonly type: FsHandleKind
  readonly path: string
  readonly handle: FsHandle
}

interface FileStats {
  name: string
  size: number
  type: FsHandleKind
}

Statusbar

createStatusbarItem(id: string, alignment?: 'left' | 'right', priority?: number, workspace?: AppActiveWorkspace): IExtensionSdkStatusbarItem

Элемент создаётся сразу видимым, с пустым текстом — задайте text сразу после создания или сначала выставьте visible = false. id должен быть уникальным в пределах расширения; к нему добавляется префикс с id расширения. Методов show()/hide() нет — переключайте visible.

Элемент настраивается мутабельными свойствами:

| Свойство | Описание | | --- | --- | | id | Только чтение. <extensionId>.<id>. | | extensionId | Только чтение. Id вашего расширения. | | text | Текст в статус-баре. По умолчанию ''. $(icon-name) вставляет иконку Lucide, например '$(check) Готово'. Длинный текст обрезается. | | tooltip | Текст при наведении, рендерится как Markdown. string или { value: string }. | | command | Id вашей команды, выполняемой по клику. Id расширения добавляется автоматически. | | color / backgroundColor | CSS-цвета. | | showProgress | Крутящаяся иконка после текста: true, 'loading' или 'syncing' (у каждого своя иконка). false или не задано — скрыта. | | when | Контекстное выражение. Элемент виден, только пока оно истинно и visible равно true. | | visible | Показать или скрыть элемент. По умолчанию true. | | alignment | 'left' или 'right'. По умолчанию 'right'. | | priority | Порядок на своей стороне. Слева больший приоритет — левее, справа — правее. По умолчанию 0. | | workspace | Показывать элемент только в одном workspace: 'auth', 'project', 'team', 'extensionWorkshop' или 'home'. Не задано — во всех. | | dispose() | Удалить элемент. |

const item = sdk.window.createStatusbarItem('my-status', 'right')
item.text = 'Готово'
item.command = 'sayHello'
item.visible = true
context.subscriptions.push(item)
IExtensionSdkStatusbarItem
interface IExtensionSdkStatusbarItem {
  readonly id: string
  readonly extensionId: string
  alignment: 'left' | 'right'
  priority?: number
  workspace?: AppActiveWorkspace // 'auth' | 'project' | 'team' | 'extensionWorkshop' | 'home'
  text: string
  tooltip?: string | { value: string }
  command?: string
  color?: string
  backgroundColor?: string
  showProgress?: boolean | 'loading' | 'syncing'
  when?: string
  visible: boolean
  dispose(): void
}

Output-каналы

createOutputChannel(name: string, options?: ExtensionSdkCreateOutputChannelOptions): Promise<ExtensionSdkOutputChannelHandle | undefined>
createLogOutputChannel(name: string, options?: ExtensionSdkCreateLogOutputChannelOptions): Promise<ExtensionSdkLogOutputChannelHandle | undefined>

| Опция | Описание | | --- | --- | | id | Id канала. Если не задан, генерируется. | | maxLines | Сколько строк хранить; старые строки отбрасываются. По умолчанию 5000. | | minLevel | Только для log-каналов: минимальный показываемый уровень. По умолчанию INFO (1). |

Оба метода возвращают undefined, если канал с таким же id или таким же name уже существует — имена общие для всех расширений. Каналы удаляются при деактивации расширения.

Обычный канал — текстовый поток:

| Член | Описание | | --- | --- | | id / name | Id и имя канала. | | append(value) | Добавить текст без переноса строки. | | appendLine(value) | Добавить текст и перенос строки. | | replace(value) | Заменить всё содержимое. | | clear() | Очистить содержимое. | | reveal() | Открыть view Output в нижней панели на вашем канале. | | dispose() | Удалить канал. |

Все члены, кроме dispose(), возвращают Promise<void>.

Log-канал добавляет уровни — trace, debug, info, warn(message, ...args) и error(messageOrError, ...args) — и setMinLevel(level). Если в error передан Error, логируется его сообщение, а сама ошибка уходит дополнительным аргументом.

Значения LogLevel:

| Уровень | Значение | | --- | --- | | DEBUG | 0 | | INFO | 1 | | WARN | 2 | | ERROR | 3 | | TRACE | 4 |

@fragmenta/extension не экспортирует LogLevel — передавайте число.

const log = await sdk.window.createLogOutputChannel('My Extension')
log?.info('Запустились', { version: context.extension.version })
log?.error(new Error('Что-то сломалось'))
Опции канала
interface ExtensionSdkCreateOutputChannelOptions {
  readonly id?: string
  readonly maxLines?: number // по умолчанию 5000
}

interface ExtensionSdkCreateLogOutputChannelOptions extends ExtensionSdkCreateOutputChannelOptions {
  readonly minLevel?: LogLevel // по умолчанию INFO (1)
}

enum LogLevel {
  DEBUG = 0,
  INFO = 1,
  WARN = 2,
  ERROR = 3,
  TRACE = 4,
}
ExtensionSdkOutputChannelHandle
interface ExtensionSdkOutputChannelHandle {
  readonly id: string
  readonly name: string
  append(value: string): Promise<void>
  appendLine(value: string): Promise<void>
  replace(value: string): Promise<void>
  clear(): Promise<void>
  reveal(): Promise<void>
  dispose(): void
}
ExtensionSdkLogOutputChannelHandle
interface ExtensionSdkLogOutputChannelHandle extends ExtensionSdkOutputChannelHandle {
  setMinLevel(level: LogLevel): Promise<void>
  trace(message: string, ...args: unknown[]): Promise<void>
  debug(message: string, ...args: unknown[]): Promise<void>
  info(message: string, ...args: unknown[]): Promise<void>
  warn(message: string, ...args: unknown[]): Promise<void>
  error(message: string | Error, ...args: unknown[]): Promise<void>
}