> ## Documentation Index
> Fetch the complete documentation index at: https://docs.blockli.app/llms.txt
> Use this file to discover all available pages before exploring further.

# Advanced blökkli Adapter: Library, Media, AI, and More

> Optional adapter methods for advanced blökkli features: reusable library, media library, AI assistant, comments, search, import, and preview grants.

These optional adapter methods unlock blökkli's advanced features. Implement the ones relevant to your project — blökkli automatically activates the corresponding editor feature when the method is present in your adapter.

<Tip>
  All methods described on this page are fully typed. Check the TypeScript types
  exported from `@blokkli/editor` for the exact parameter shapes and return
  types, and use them in your adapter for end-to-end type safety.
</Tip>

***

## Library (Reusable Blocks)

The library feature lets editors save blocks as reusable items and insert them across multiple pages. All four methods below work together — implement them as a group to enable the full library workflow.

<Accordion title="getLibraryItems()">
  Returns the list of saved reusable block items available to the current user. blökkli calls this when the user opens the Library panel.

  **Signature**

  ```typescript theme={null}
  async getLibraryItems(): Promise<LibraryItem[]>
  ```

  **Example**

  ```typescript theme={null}
  async getLibraryItems() {
    return $fetch('/api/library')
  }
  ```
</Accordion>

<Accordion title="makeBlockReusable({ uuid, title })">
  Saves an existing block to the library under a user-supplied title. Called when the user selects "Save to library" from the block context menu.

  **Signature**

  ```typescript theme={null}
  async makeBlockReusable({
    uuid,
    title,
  }: {
    uuid: string
    title: string
  }): Promise<void>
  ```

  <ParamField body="uuid" type="string" required>
    UUID of the block to save as a reusable item.
  </ParamField>

  <ParamField body="title" type="string" required>
    The display label for the library item, as entered by the user.
  </ParamField>

  **Example**

  ```typescript theme={null}
  async makeBlockReusable({ uuid, title }) {
    await $fetch('/api/library', {
      method: 'POST',
      body: { uuid, title },
    })
  }
  ```
</Accordion>

<Accordion title="addLibraryItem({ uuid })">
  Inserts a library item at the current drop position. Called when the user drags a library item onto the canvas or clicks to insert it.

  **Signature**

  ```typescript theme={null}
  async addLibraryItem({
    uuid,
    hostField,
    preceedingUuid,
  }: {
    uuid: string
    hostField: string
    preceedingUuid: string | null
  }): Promise<void>
  ```

  <ParamField body="uuid" type="string" required>
    UUID of the library item to insert.
  </ParamField>

  <ParamField body="hostField" type="string" required>
    The target field name.
  </ParamField>

  <ParamField body="preceedingUuid" type="string | null" required>
    UUID of the block that the library item should be placed after. `null`
    inserts at the start of the field.
  </ParamField>

  **Example**

  ```typescript theme={null}
  async addLibraryItem({ uuid, hostField, preceedingUuid }) {
    await $fetch('/api/library/insert', {
      method: 'POST',
      body: { uuid, hostField, preceedingUuid },
    })
  }
  ```
</Accordion>

<Accordion title="detachReusableBlock({ uuid })">
  Converts a library reference back into a standalone block. Called when the user selects "Detach from library" on a reusable block instance.

  **Signature**

  ```typescript theme={null}
  async detachReusableBlock({ uuid }: { uuid: string }): Promise<void>
  ```

  <ParamField body="uuid" type="string" required>
    UUID of the library reference block to detach.
  </ParamField>

  **Example**

  ```typescript theme={null}
  async detachReusableBlock({ uuid }) {
    await $fetch(`/api/library/${uuid}/detach`, { method: 'POST' })
  }
  ```
</Accordion>

***

## Media Library

The media library panel lets editors search for existing media assets and insert blocks from them directly.

<Accordion title="mediaLibraryGetResults({ searchText, page })">
  Returns paginated media search results for the media library panel. Called whenever the user types into the search field or navigates to a new page of results.

  **Signature**

  ```typescript theme={null}
  async mediaLibraryGetResults({
    searchText,
    page,
  }: {
    searchText: string
    page: number
  }): Promise<MediaLibraryResults>
  ```

  <ParamField body="searchText" type="string" required>
    The current search query string. May be an empty string when the user has not
    typed anything.
  </ParamField>

  <ParamField body="page" type="number" required>
    Zero-based page index for pagination.
  </ParamField>

  **Example**

  ```typescript theme={null}
  async mediaLibraryGetResults({ searchText, page }) {
    return $fetch('/api/media', {
      query: { q: searchText, page },
    })
  }
  ```
</Accordion>

<Accordion title="mediaLibraryAddBlock({ mediaId, hostField, preceedingUuid })">
  Adds a block from a selected media item. Called when the user clicks a media result in the library panel.

  **Signature**

  ```typescript theme={null}
  async mediaLibraryAddBlock({
    mediaId,
    hostField,
    preceedingUuid,
  }: {
    mediaId: string
    hostField: string
    preceedingUuid: string | null
  }): Promise<void>
  ```

  <ParamField body="mediaId" type="string" required>
    The identifier of the selected media asset.
  </ParamField>

  <ParamField body="hostField" type="string" required>
    The target field name.
  </ParamField>

  <ParamField body="preceedingUuid" type="string | null" required>
    UUID of the preceding block. `null` inserts at the start of the field.
  </ParamField>

  **Example**

  ```typescript theme={null}
  async mediaLibraryAddBlock({ mediaId, hostField, preceedingUuid }) {
    await $fetch('/api/blocks/from-media', {
      method: 'POST',
      body: { mediaId, hostField, preceedingUuid },
    })
  }
  ```
</Accordion>

***

## AI Assistant

The AI assistant panel lets editors generate block content from a natural language prompt.

<Accordion title="assistantGetResults({ prompt })">
  Called when the user submits a prompt in the AI assistant panel. Return an array of suggested block definitions that the editor can present as insertion candidates.

  **Signature**

  ```typescript theme={null}
  async assistantGetResults({
    prompt,
  }: {
    prompt: string
  }): Promise<AssistantResult[]>
  ```

  <ParamField body="prompt" type="string" required>
    The natural language prompt entered by the user.
  </ParamField>

  **Example**

  ```typescript theme={null}
  async assistantGetResults({ prompt }) {
    return $fetch('/api/ai/suggest', {
      method: 'POST',
      body: { prompt },
    })
  }
  ```
</Accordion>

<Accordion title="assistantAddBlockFromResult({ result, hostField, preceedingUuid })">
  Inserts a block from an AI suggestion. Called when the user selects one of the results returned by `assistantGetResults()`.

  **Signature**

  ```typescript theme={null}
  async assistantAddBlockFromResult({
    result,
    hostField,
    preceedingUuid,
  }: {
    result: AssistantResult
    hostField: string
    preceedingUuid: string | null
  }): Promise<void>
  ```

  <ParamField body="result" type="AssistantResult" required>
    The full result object as returned by `assistantGetResults()`.
  </ParamField>

  <ParamField body="hostField" type="string" required>
    The target field name.
  </ParamField>

  <ParamField body="preceedingUuid" type="string | null" required>
    UUID of the preceding block. `null` inserts at the start of the field.
  </ParamField>

  **Example**

  ```typescript theme={null}
  async assistantAddBlockFromResult({ result, hostField, preceedingUuid }) {
    await $fetch('/api/blocks/from-ai', {
      method: 'POST',
      body: { result, hostField, preceedingUuid },
    })
  }
  ```
</Accordion>

***

## Comments

The comments feature allows editors to annotate blocks with discussion threads.

<Accordion title="addComment({ uuid, body })">
  Adds a comment to a specific block. Called when the user submits a comment from the block's comment input.

  **Signature**

  ```typescript theme={null}
  async addComment({
    uuid,
    body,
  }: {
    uuid: string
    body: string
  }): Promise<void>
  ```

  <ParamField body="uuid" type="string" required>
    UUID of the block being commented on.
  </ParamField>

  <ParamField body="body" type="string" required>
    The comment text.
  </ParamField>

  **Example**

  ```typescript theme={null}
  async addComment({ uuid, body }) {
    await $fetch('/api/comments', {
      method: 'POST',
      body: { uuid, body },
    })
  }
  ```
</Accordion>

<Accordion title="loadComments()">
  Loads all comments for the current entity. blökkli calls this on editor activation and after each comment mutation.

  **Signature**

  ```typescript theme={null}
  async loadComments(): Promise<Comment[]>
  ```

  **Example**

  ```typescript theme={null}
  async loadComments() {
    return $fetch(
      `/api/content/${ctx.state.value.currentEntityUuid}/comments`
    )
  }
  ```
</Accordion>

<Accordion title="resolveComment({ commentUuid })">
  Marks a comment as resolved. Called when the user clicks the resolve button on a comment thread.

  **Signature**

  ```typescript theme={null}
  async resolveComment({ commentUuid }: { commentUuid: string }): Promise<void>
  ```

  <ParamField body="commentUuid" type="string" required>
    UUID of the comment to resolve.
  </ParamField>

  **Example**

  ```typescript theme={null}
  async resolveComment({ commentUuid }) {
    await $fetch(`/api/comments/${commentUuid}/resolve`, { method: 'POST' })
  }
  ```
</Accordion>

***

## Content Search

The content search panel lets editors find existing content items and insert blocks from them.

<Accordion title="getContentSearchTabs()">
  Returns tab definitions for the content search panel. Each tab represents a separate searchable content source (for example, "Articles", "Products").

  **Signature**

  ```typescript theme={null}
  async getContentSearchTabs(): Promise<ContentSearchTab[]>
  ```

  **Example**

  ```typescript theme={null}
  async getContentSearchTabs() {
    return [
      { id: 'articles', label: 'Articles' },
      { id: 'products', label: 'Products' },
    ]
  }
  ```
</Accordion>

<Accordion title="getContentSearchResults({ tab, query, page })">
  Returns paginated search results for the active tab and query. Called whenever the user types or changes tabs.

  **Signature**

  ```typescript theme={null}
  async getContentSearchResults({
    tab,
    query,
    page,
  }: {
    tab: string
    query: string
    page: number
  }): Promise<ContentSearchResults>
  ```

  <ParamField body="tab" type="string" required>
    The `id` of the active search tab.
  </ParamField>

  <ParamField body="query" type="string" required>
    The current search string.
  </ParamField>

  <ParamField body="page" type="number" required>
    Zero-based page index.
  </ParamField>

  **Example**

  ```typescript theme={null}
  async getContentSearchResults({ tab, query, page }) {
    return $fetch('/api/search', {
      query: { type: tab, q: query, page },
    })
  }
  ```
</Accordion>

<Accordion title="addContentSearchItem({ item, hostField, preceedingUuid })">
  Inserts a block from a search result. Called when the user clicks a result in the content search panel.

  **Signature**

  ```typescript theme={null}
  async addContentSearchItem({
    item,
    hostField,
    preceedingUuid,
  }: {
    item: ContentSearchItem
    hostField: string
    preceedingUuid: string | null
  }): Promise<void>
  ```

  <ParamField body="item" type="ContentSearchItem" required>
    The full result object as returned by `getContentSearchResults()`.
  </ParamField>

  <ParamField body="hostField" type="string" required>
    The target field name.
  </ParamField>

  <ParamField body="preceedingUuid" type="string | null" required>
    UUID of the preceding block. `null` inserts at the start of the field.
  </ParamField>

  **Example**

  ```typescript theme={null}
  async addContentSearchItem({ item, hostField, preceedingUuid }) {
    await $fetch('/api/blocks/from-search', {
      method: 'POST',
      body: { item, hostField, preceedingUuid },
    })
  }
  ```
</Accordion>

***

## Import

The import feature lets editors pull blocks from other entities into the current one.

<Accordion title="getImportItems({ entityType, entityBundle })">
  Returns available blocks to import from other entities of the given type and bundle. Displayed in the Import panel.

  **Signature**

  ```typescript theme={null}
  async getImportItems({
    entityType,
    entityBundle,
  }: {
    entityType: string
    entityBundle: string
  }): Promise<ImportItem[]>
  ```

  <ParamField body="entityType" type="string" required>
    The entity type to search for importable blocks (e.g., `'node'`).
  </ParamField>

  <ParamField body="entityBundle" type="string" required>
    The entity bundle to search (e.g., `'page'`, `'article'`).
  </ParamField>

  **Example**

  ```typescript theme={null}
  async getImportItems({ entityType, entityBundle }) {
    return $fetch('/api/blocks/importable', {
      query: { entityType, entityBundle },
    })
  }
  ```
</Accordion>

<Accordion title="importFromExisting({ uuids, hostField, preceedingUuid })">
  Imports the selected blocks from another entity into the current one. Called when the user confirms an import selection.

  **Signature**

  ```typescript theme={null}
  async importFromExisting({
    uuids,
    hostField,
    preceedingUuid,
  }: {
    uuids: string[]
    hostField: string
    preceedingUuid: string | null
  }): Promise<void>
  ```

  <ParamField body="uuids" type="string[]" required>
    UUIDs of the blocks to import.
  </ParamField>

  <ParamField body="hostField" type="string" required>
    The target field name in the current entity.
  </ParamField>

  <ParamField body="preceedingUuid" type="string | null" required>
    UUID of the block to insert after. `null` inserts at the start of the field.
  </ParamField>

  **Example**

  ```typescript theme={null}
  async importFromExisting({ uuids, hostField, preceedingUuid }) {
    await $fetch('/api/blocks/import', {
      method: 'POST',
      body: { uuids, hostField, preceedingUuid },
    })
  }
  ```
</Accordion>

***

## Preview

<Accordion title="buildEditableFrameUrl({ uuid })">
  Builds the URL for the block edit form iframe. When implemented, blökkli renders this URL in an iframe panel when the user opens the edit form for a block — useful for embedding your existing backend edit forms.

  **Signature**

  ```typescript theme={null}
  async buildEditableFrameUrl({ uuid }: { uuid: string }): Promise<string>
  ```

  <ParamField body="uuid" type="string" required>
    UUID of the block to build the edit URL for.
  </ParamField>

  **Example**

  ```typescript theme={null}
  async buildEditableFrameUrl({ uuid }) {
    return `/admin/blocks/${uuid}/edit?mode=iframe`
  }
  ```
</Accordion>

<Accordion title="getPreviewGrantUrl({ uuid })">
  Generates a shareable preview URL for the current draft state — accessible without logging in. Useful for sharing a draft with stakeholders.

  **Signature**

  ```typescript theme={null}
  async getPreviewGrantUrl({ uuid }: { uuid: string }): Promise<string>
  ```

  <ParamField body="uuid" type="string" required>
    UUID of the entity to generate a preview grant for.
  </ParamField>

  **Example**

  ```typescript theme={null}
  async getPreviewGrantUrl({ uuid }) {
    const data = await $fetch(`/api/preview-grant`, {
      method: 'POST',
      body: { uuid },
    })
    return data.url
  }
  ```

  <Note>
    The URL returned by `getPreviewGrantUrl()` is copied to the clipboard when
    the user clicks the Share Preview button in the toolbar. Ensure the URL is
    publicly accessible and includes a time-limited token to prevent permanent
    unauthorised access.
  </Note>
</Accordion>
