---
title: "Explore: backlinks, related Blocks and the trail"
description: "What is connected to a Block: the Blocks that point at it, the Blocks related to it, and the chain of Derivations behind it. The explore Feature, the panel, the related route and derivationTrailOf."
canonical_url: "https://skaile-ai.github.io/forge-editor/guides/explore"
---
# Explore: backlinks, related Blocks and the trail

> What is connected to a Block: the Blocks that point at it, the Blocks related to it, and the chain of Derivations behind it. The explore Feature, the panel, the related route and derivationTrailOf.

The **Explore** panel answers three questions about one Block, all from the [Block index](https://skaile-ai.github.io/forge-editor/guides/workspace), and a fourth from the host's [knowledge graph](https://skaile-ai.github.io/forge-editor/guides/knowledge-graph) when it has one:

<table>
<thead>
  <tr>
    <th>
      Tab
    </th>
    
    <th>
      Question
    </th>
    
    <th>
      Source
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        backlinks
      </code>
    </td>
    
    <td>
      Which Blocks point at this one?
    </td>
    
    <td>
      <code>
        EditorBackend.index.edges(address, 'in')
      </code>
      
       without the <code>
        tag
      </code>
      
       Edges (see <a href="https://skaile-ai.github.io/forge-editor/guides/edges">
        Edges
      </a>
      
      )
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        related
      </code>
    </td>
    
    <td>
      Which Blocks are about the same thing?
    </td>
    
    <td>
      <code>
        EditorBackend.index.related(address)
      </code>
      
      , the related route below
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        trail
      </code>
    </td>
    
    <td>
      How was this Block made?
    </td>
    
    <td>
      <code>
        derivationTrailOf(address, index)
      </code>
      
      , the chain of Derivations, the original first
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        knowledge
      </code>
    </td>
    
    <td>
      What does the knowledge graph know about it?
    </td>
    
    <td>
      <code>
        EditorBackend.knowledge.search(title)
      </code>
      
      , then <code>
        neighbours(id)
      </code>
      
       of the node you choose; see below
    </td>
  </tr>
</tbody>
</table>

A tab shows only when the backend can fill it: `backlinks` needs `index.edges`, `related` needs `index.related`, `trail` needs only `index`, `knowledge` needs the `knowledge` capability. A backend without the `index` capability has no Explore panel.

## Enable it

The `explore` Feature is in no level list of the presets. A host that composes with `composeEditor` opts in by id: `interaction: [...INTERACTION.full, 'explore']`, with `explore: { open, show }` in the options. The Feature `requires` the `composites` Feature and the backend capability `index`; over a backend without it, `composeEditor` reports the Feature in `omitted`.

```ts
import { INTERACTION } from '@skaile/forge-editor/presets/compose'
import { composeEditor } from '@skaile/forge-editor/vue/advanced'

const composed = composeEditor({
  content: 'rich',
  interaction: [...INTERACTION.full, 'explore'],
  ai: false,
  backend,
  explore: {
    // What choosing a Block does: open its Document and reveal the Block.
    open: address => workspace.openBlock(address),
  },
})
```

<table>
<thead>
  <tr>
    <th>
      Entry
    </th>
    
    <th>
      Content
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        @skaile/forge-editor/explore
      </code>
    </td>
    
    <td>
      framework-free: <code>
        exploreFeature
      </code>
      
      , <code>
        exploreFeatureCore
      </code>
      
      , <code>
        createExploreModel
      </code>
      
      , <code>
        exploreTabsOf
      </code>
      
      , <code>
        exploreCountOf
      </code>
      
      , <code>
        exploreTabLabel
      </code>
      
      , <code>
        exploreKindLabel
      </code>
      
      , <code>
        exploreKnowledgeLinksOf
      </code>
      
       and the types
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        @skaile/forge-editor/explore/vue
      </code>
      
      , <code>
        @skaile/forge-editor/explore/react
      </code>
    </td>
    
    <td>
      <code>
        exploreFeature
      </code>
      
       with the inspector section, plus <code>
        ExplorePanel
      </code>
      
      , <code>
        ExploreSection
      </code>
      
       and <code>
        useExploreModel
      </code>
    </td>
  </tr>
</tbody>
</table>

The Feature adds the section **Verknüpfungen** to the inspector and the details card of a Typed block: the count of each tab and a button to the panel. `show(address)` in the options is where the host places its panel (a sidebar tab, a drawer); without it, the section shows the panel inline under the counts.

## The panel

`ExplorePanel` takes the Block to show (`address`, `null` for none) and `open`, the host's way to a Block. It renders the `ExploreModel` of `createExploreModel({ backend, address, open })`; the Vue and React components render the same model.

The model keeps one state: the selected `tab` and, per tab, `{ status: 'idle' | 'loading' | 'done' | 'failed', items }`. It loads the three lists when the address changes, cancels what a newer Block replaced, and never loads the same Block twice (`refresh()` loads again). A failing list is `failed` and the others stay usable. `open(address)` calls the host and resolves whether it happened; a throwing host is logged, never rethrown.

## The related route

`GET /api/editor/blocks/:documentId/:blockId/related` answers the Blocks most related to one Block as `BlockSearchHit[]` (`documentId`, `blockId`, `title`, `type`), best first, at most ten, the Block itself never. The score of a candidate Block is:

<table>
<thead>
  <tr>
    <th>
      Evidence
    </th>
    
    <th>
      Points
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      each tag it shares with the Block
    </td>
    
    <td>
      2
    </td>
  </tr>
  
  <tr>
    <td>
      each reference target both point at with a chip
    </td>
    
    <td>
      3
    </td>
  </tr>
  
  <tr>
    <td>
      it holds every word of the Block's title
    </td>
    
    <td>
      1
    </td>
  </tr>
</tbody>
</table>

A candidate with score 0 is left out, and equal scores keep the order of the Documents. `relatedBlocks(row, candidates)` and `relatedScore` in `@skaile/forge-editor/core` are the ranking; the route and the in-memory backend both use them.

The route reads only Documents the user may read (the scope of the [Workspace search](https://skaile-ai.github.io/forge-editor/guides/workspace)). It answers `[]` for an unknown Block, `403` when the user may not read the Document of the Block, `400` without a Document or Block ID, `401` without a user and `501` without the `index` port. An adapter with vectors can rank better behind the same route.

<table>
<thead>
  <tr>
    <th>
      
    </th>
    
    <th>
      
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      Route
    </td>
    
    <td>
      <code>
        API.blockRelated
      </code>
      
       (port <code>
        index
      </code>
      
      )
    </td>
  </tr>
  
  <tr>
    <td>
      Client
    </td>
    
    <td>
      <code>
        EditorBackend.index.related(address, signal?)
      </code>
      
      , optional: a backend without it leaves the <code>
        related
      </code>
      
       tab out
    </td>
  </tr>
</tbody>
</table>

## The trail

`derivationTrailOf(address, source, signal?)` (`@skaile/forge-editor/core`) follows `derivedFrom` of the [Derivation](https://skaile-ai.github.io/forge-editor/guides/composites) through `source.blockGet` (the Document's current content, so `EditorBackend.index` is a `TrailSource`). It answers `TrailStep[]`, the original first and the Block itself last:

```ts
const trail = await derivationTrailOf({ documentId: 'doc', blockId: 'rendition-of-rendition' }, backend.index!)
// [{ address: …'original', method: null, state: null },
//  { address: …'summary',  method: 'ai:summary', state: 'fresh' },
//  { address: …'short',    method: 'ai:rewrite', state: 'stale' }]
```

- It goes at most `TRAIL_MAX_HOPS` (8) Derivations back and never visits a Block twice.
- A Block without a Derivation is a trail of one step with `method: null` and `state: null`.
- A Block made from an uploaded asset ends the trail there; its `method` says how it was made.
- Each step's `state` is the one the row badge shows (`fresh`, `stale`, `edited`, `missing`, `foreign`). An original that is gone gives `missing`; one the user may not read gives `foreign`.
- It answers `[]` for a Block that does not exist and rejects when the Block itself cannot be read.

## The knowledge tab

The `knowledge` tab („Wissensgraph“) is offered only when the backend has the `knowledge` capability (`EditorBackend.knowledge`, served by the `KnowledgeGraph` port; see [Knowledge graph](https://skaile-ai.github.io/forge-editor/guides/knowledge-graph)). It sits after the trail.

- **List.** Per Block the model reads the title through `index.blockGet` and searches the graph for it. `state.query` is that text (`''` for a Block without a title, which searches nothing), `state.knowledge` the hits (`{ id, label, kind, score }`, best first).
- **Neighbours.** `model.focusNode(hit)` loads `knowledge.neighbours(hit.id)` into `state.focus` (`{ node, status, nodes, edges }`); `exploreKnowledgeLinksOf(state.focus)` lists the nodes joined to it with the edge type and its direction. `focusNode(node)` on one of them moves on, `focusNode(null)` returns to the hits. Another Block resets the focus.
- **No Block.** A knowledge node is no Block, so choosing one never calls the host's `open`.


## Sitemap

See the full [sitemap](https://skaile-ai.github.io/forge-editor/sitemap.md) for all pages.
