Сайт

Вебвью

Вебвью отображает ваш собственный HTML/JS-интерфейс внутри Fragmenta — во вкладке панели или в модальном окне. Вебвью, объявленные в contributes.webviews, регистрируются автоматически. Вы также можете создавать вебвью из кода, если нужен динамический контент или обмен сообщениями.

Создание и получение вебвью

createWebview(id: string, options?: ExtensionSdkCreateWebviewOptions): Promise<WebviewHandle>
getWebview(id: string): Promise<WebviewHandle | undefined>

Идентификаторы дополняются идентификатором вашего расширения: глобальный идентификатор вебвью— <extensionId>.<id>. createWebview ничего не открывает — отобразите веб-представление с помощью show() или откройте его в модальном окне.

Повторный вызов createWebview с тем же идентификатором возвращает существующий дескриптор, а новые опции игнорируются. Для идентификатора, объявленного в манифесте, используйте getWebview, при этом вызов createWebview будет отклонён.

getWebview возвращает дескриптор существующего вебвью вашего расширения по его идентификатору — включая вебвью, объявленные в манифесте, — или undefined.

| Опция | Описание | | --- | --- | | displayName | Заголовок вкладки. По умолчанию 'Webview'. | | icon | Имя иконки Lucide для вкладки. | | html | Начальный контент. | | options | Настройки песочницы, см. ниже. | | when | Сейчас ни на что не влияет. |

Настройки песочницы (options):

| Настройка | Описание | | --- | --- | | allowScripts | Разрешить скрипты, диалоги и всплывающие окна. Также нужна для acquireSDK(). По умолчанию true. | | allowForms | Разрешить отправку форм. По умолчанию true. | | localResourceRoots | Если список не пуст, asWebviewUri отклоняет локальные пути, которые не начинаются с одного из этих корней (сравнение идёт с итоговым абсолютным путём). По умолчанию [] — без ограничений. | | stylesUrl | URL таблицы стилей, подключаемой в страницу. | | injectBaseStyles | Базовые стили: html, body и #root на весь размер, цвета фона и текста из темы, моноширинный шрифт. По умолчанию true. | | enableCommandUris | Сейчас ни на что не влияет. | | retainContextWhenHidden | Сейчас ни на что не влияет. | | trustedDomains | Сейчас ни на что не влияет. | | cspOverride | Сейчас ни на что не влияет. |

ExtensionSdkCreateWebviewOptions
interface ExtensionSdkCreateWebviewOptions {
  displayName?: string // по умолчанию 'Webview'
  icon?: string // имя иконки Lucide
  html?: string
  options?: Partial<WebviewOptions>
  when?: string // сейчас ни на что не влияет
}

interface WebviewOptions {
  readonly allowScripts?: boolean // по умолчанию true
  readonly allowForms?: boolean // по умолчанию true
  readonly localResourceRoots?: readonly string[] // по умолчанию []
  readonly stylesUrl?: string
  readonly injectBaseStyles?: boolean // по умолчанию true
  readonly enableCommandUris?: boolean // сейчас ни на что не влияет
  readonly retainContextWhenHidden?: boolean // сейчас ни на что не влияет
  readonly trustedDomains?: readonly string[] // сейчас ни на что не влияет
  readonly cspOverride?: string // сейчас ни на что не влияет
}

Дескриптор вебвью

| Опция | Описание | | --- | --- | | id | Глобальный id webview, <extensionId>.<id> (используйте его в showModal({ webviewId })). | | html | Контент, последним заданный через этот handle (для getWebview — контент на момент вызова). | | setHtml(html) | Заменить контент. Страница перезагружается, её состояние теряется. | | postMessage(message) | Отправить сериализуемое значение в страницу. Сообщения до загрузки страницы ставятся в очередь. После dispose() ничего не делает. | | onDidReceiveMessage | Event<unknown> — сообщения, отправленные страницей. | | onDidContentReady | Срабатывает при каждой загрузке страницы, в том числе после setHtml и reload(). | | onDidDispose | Вебвью уничтожен. | | asWebviewUri(localPath) | Превращает путь в URI, доступный вебвью. Относительные пути отсчитываются от папки расширения, пути с / в начале используются как есть, URL со схемой возвращаются без изменений. | | show(options?) | Открыть вебвью во вкладке панели. | | focus() | Фокус на странице. Ничего не возвращает. | | reload() | Перезагрузить страницу. Ничего не возвращает. | | dispose() | Уничтожить вебвью. |

setHtml, postMessage, asWebviewUri и show возвращают promise, которые отклоняются, если вызов завершается неудачно, — например, если вебвью больше не существует.

Опции show():

| Опция | Описание | | --- | --- | | panel | Id целевой панели: sdk.project.panel.left.panelId (или right, bottom) либо ProjectPanelIds.Browser для центральной области. | | activate | Сделать вкладку активной. По умолчанию true. | | pinned | Закрепить вкладку. |

WebviewHandle
interface WebviewHandle {
  readonly id: string
  readonly html: string
  readonly onDidReceiveMessage: Event<unknown>
  readonly onDidContentReady: Event<void>
  readonly onDidDispose: Event<void>
  setHtml(html: string): Promise<void>
  postMessage(message: unknown): Promise<void>
  asWebviewUri(localPath: string): Promise<string>
  show(options?: { panel?: PanelId; activate?: boolean; pinned?: boolean }): Promise<void>
  focus(): void
  reload(): void
  dispose(): void
}

Загрузка собранных ресурсов

Соберите ваш интерфейс (например, Vite-сборка внутри расширения) и подключите файлы через asWebviewUri:

const webview = await sdk.window.createWebview('mapper-view', { displayName: 'Mapper' })

const scriptUri = await webview.asWebviewUri('dist/webview/mapper/mapper.js')
const styleUri = await webview.asWebviewUri('dist/webview/mapper/index.css')

await webview.setHtml(`<!doctype html>
<html>
  <head><link rel="stylesheet" href="${styleUri}" /></head>
  <body>
    <div id="root"></div>
    <script type="module" src="${scriptUri}"></script>
  </body>
</html>`)

webview.onDidReceiveMessage((message) => {
  // обработка сообщений из UI
})

await webview.show({ panel: sdk.project.panel.bottom.panelId, activate: true })

Внутри вебвью

Если allowScripts не false, каждая страница получает небольшой API ещё до запуска ваших скриптов:

| Опция | Описание | | --- | --- | | window.acquireSDK() | Возвращает { postMessage, onMessage }. | | postMessage(message) | Отправить сериализуемое значение в расширение (onDidReceiveMessage). Возвращает false, если отправка не удалась. | | onMessage(callback) | Получать значения, отправленные через webview.postMessage. Возвращает функцию отписки. | | window.webviewId | Глобальный id webview. |

Сообщения, которые расширение отправило до загрузки страницы, ставятся в очередь и доставляются, когда страница готова.

Активная тема доступна как CSS-переменные --ds-* (например, --ds-color-background, --ds-color-foreground-secondary, --ds-color-border). Они обновляются при смене темы; с injectBaseStyles страница уже использует их для фона, текста и рамок. Нажатия клавиш вне полей ввода передаются в Fragmenta, так что сочетания клавиш приложения работают, пока фокус в webview.

Типы для страницы не поставляются — объявите их сами:

declare function acquireSDK(): {
  postMessage(message: unknown): boolean
  onMessage(callback: (message: unknown) => void): () => void
}

const api = acquireSDK()

const unsubscribe = api.onMessage((message) => {
  // значения из webview.postMessage()
})

api.postMessage({ type: 'ready' })
webview.onDidReceiveMessage((message) => {
  if ((message as { type?: string }).type === 'ready') {
    webview.postMessage({ type: 'init', items })
  }
})
.card {
  background: var(--ds-color-background);
  color: var(--ds-color-foreground-secondary);
  border: 1px solid var(--ds-color-border);
}