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

# TextBuilder

> Uitgebreide uitleg over de TextBuilder-klasse voor het bouwen van rich text (facets) voor Bluesky in PHP.

## Wat is TextBuilder?

`TextBuilder` is een klasse waarmee je via method chaining de **facets** (rich-text-annotaties) opbouwt die het AT Protocol van Bluesky definieert.

De tekst van een Bluesky-post is platte tekst, maar om mentions, links en hashtags weer te geven moet je een `facets`-array meesturen die de positie in de tekst (byte-offsets) en het type aangeeft. `TextBuilder` verzorgt deze offsetberekening en het opbouwen van de array automatisch.

```mermaid theme={null}
sequenceDiagram
    participant App as Laravel-app
    participant TB as TextBuilder
    participant Post as Post-record
    participant AT as AT Protocol

    App->>TB: TextBuilder::make('Hello')
    App->>TB: ->mention('@alice.bsky.social')
    App->>TB: ->link('https://example.com')
    App->>TB: ->tag('#Laravel')
    TB->>TB: Byte-offsets berekenen en<br>facets-array opbouwen
    TB->>Post: toPost()
    Post->>AT: Bluesky::post($post)
    AT-->>App: Response
```

## Eenvoudige tekst maken

### TextBuilder::make()

Met `TextBuilder::make()` maak je een instantie met een begintekst. De begintekst is optioneel.

```php theme={null}
use Revolution\Bluesky\RichText\TextBuilder;

$builder = TextBuilder::make(text: 'Hello Bluesky');
```

### text()

Met `text()` voeg je tekst toe aan het einde.

```php theme={null}
$builder = TextBuilder::make()
    ->text('Hello ')
    ->text('Bluesky');

// $builder->text === 'Hello Bluesky'
```

### newLine()

Voegt een regeleinde toe. Met `count` geef je het aantal regels op (standaard: 1).

```php theme={null}
$builder = TextBuilder::make('1行目')
    ->newLine()
    ->text('2行目')
    ->newLine(count: 2)
    ->text('4行目');
```

### toPost()

Converteert de `TextBuilder`-instantie naar een `Post`-record. Je kunt deze rechtstreeks doorgeven aan `Bluesky::post()`.

```php theme={null}
use Revolution\Bluesky\Facades\Bluesky;
use Revolution\Bluesky\RichText\TextBuilder;

$post = TextBuilder::make('シンプルな投稿')->toPost();

$response = Bluesky::withToken()->post($post);
```

### Post::build()

Je kunt ook een closure doorgeven aan `Post::build()`. De returnwaarde is dan een `Post`.

```php theme={null}
use Revolution\Bluesky\Facades\Bluesky;
use Revolution\Bluesky\Record\Post;
use Revolution\Bluesky\RichText\TextBuilder;

$post = Post::build(function (TextBuilder $builder) {
    $builder->text('Hello Bluesky');
});

$response = Bluesky::withToken()->post($post);
```

## Mentions (`@mention`) toevoegen

Met `mention()` voeg je een mention-facet toe.

```php theme={null}
$builder->mention(text: '@alice.bsky.social');
```

### Automatische DID-resolutie

Als je `did` weglaat, wordt de DID automatisch geresolved vanuit de handle (`Bluesky::resolveHandle()` wordt aangeroepen).

```php theme={null}
// De DID automatisch resolven (dit veroorzaakt een API-aanroep)
$builder->mention('@alice.bsky.social');
```

### De DID expliciet opgeven

Als je de DID al kent, kun je die expliciet doorgeven en zo de API-aanroep vermijden.

```php theme={null}
// De DID rechtstreeks opgeven (aanbevolen: geen API-aanroep)
$builder->mention(text: '@alice.bsky.social', did: 'did:plc:xxxxxxxxxxxxxxxxxxxx');
```

<Tip>
  Gebruik je in productie veel mentions, dan kun je API-aanroepen verminderen door DID's te cachen.
</Tip>

## Links (URL's) insluiten

Met `link()` voeg je een link-facet toe.

```php theme={null}
// De URL zelf als weergavetekst gebruiken
$builder->link('https://laravel.com');

// Weergavetekst en URL apart opgeven
$builder->link(text: 'Laravel 公式サイト', uri: 'https://laravel.com');
```

Als je `uri` weglaat, wordt `text` direct als URI gebruikt.

## Hashtags toevoegen

Met `tag()` voeg je een hashtag-facet toe.

```php theme={null}
// Geef je tekst door die met # begint, dan wordt de tag automatisch geëxtraheerd
$builder->tag('#Laravel');

// De tagstring expliciet opgeven (string zonder #)
$builder->tag(text: '#Laravel', tag: 'Laravel');
```

## Samengestelde tekst bouwen

Door meerdere facets te combineren maak je rijke posttekst.

```php theme={null}
use Revolution\Bluesky\Facades\Bluesky;
use Revolution\Bluesky\RichText\TextBuilder;

$post = TextBuilder::make('新しい記事を公開しました！')
    ->newLine(count: 2)
    ->mention('@alice.bsky.social', did: 'did:plc:xxxx')
    ->text(' さんにもぜひ読んでほしいです。')
    ->newLine()
    ->link(text: '記事を読む', uri: 'https://example.com/article/1')
    ->newLine()
    ->tag('#Laravel')
    ->text(' ')
    ->tag('#PHP')
    ->toPost();

$response = Bluesky::withToken()->post($post);
```

### Schrijven met Post::build()

```php theme={null}
use Revolution\Bluesky\Facades\Bluesky;
use Revolution\Bluesky\Record\Post;
use Revolution\Bluesky\RichText\TextBuilder;

$post = Post::build(function (TextBuilder $builder) {
    $builder->text('新しい記事を公開しました！')
            ->newLine(count: 2)
            ->mention('@alice.bsky.social', did: 'did:plc:xxxx')
            ->text(' さんにもぜひ読んでほしいです。')
            ->newLine()
            ->link(text: '記事を読む', uri: 'https://example.com/article/1')
            ->newLine()
            ->tag('#Laravel')
            ->text(' ')
            ->tag('#PHP');
});

$response = Bluesky::withToken()->post($post);
```

## Integratie met posts

### Combineren met Bluesky::post()

```php theme={null}
use Revolution\Bluesky\Facades\Bluesky;
use Revolution\Bluesky\RichText\TextBuilder;

$post = TextBuilder::make('test')
    ->newLine()
    ->link('https://bsky.app/')
    ->toPost();

$response = Bluesky::withToken()->post($post);
```

### Combineren met het Notification-kanaal

Je kunt `Post::build()` gebruiken in de `toBluesky()`-method van `BlueskyChannel`.

```php theme={null}
use Illuminate\Notifications\Notification;
use Revolution\Bluesky\Notifications\BlueskyChannel;
use Revolution\Bluesky\Record\Post;
use Revolution\Bluesky\RichText\TextBuilder;

class DeployedNotification extends Notification
{
    public function __construct(
        private string $url,
    ) {}

    public function via(object $notifiable): array
    {
        return [BlueskyChannel::class];
    }

    public function toBluesky(object $notifiable): Post
    {
        return Post::build(function (TextBuilder $builder) {
            $builder->text('デプロイが完了しました')
                    ->newLine()
                    ->link(text: '確認する', uri: $this->url)
                    ->newLine()
                    ->tag('#Laravel');
        });
    }
}
```

## Facets automatisch detecteren

Je kunt `@mention`, URL's en `#hashtag` in de tekst ook automatisch laten detecteren en de facets laten instellen.

```php theme={null}
use Revolution\Bluesky\RichText\TextBuilder;

$builder = TextBuilder::make('@alice.bsky.social test https://example.com #alice')
    ->detectFacets();

// Na detectFacets() kun je nog meer facets toevoegen
$builder->newLine()->tag('#bob');

$post = $builder->toPost();
```

<Warning>
  `detectFacets()` detecteert op basis van reguliere expressies. Wil je zeker weten dat er gelinkt wordt, gebruik dan expliciet `link()`, `mention()` en `tag()`.
</Warning>

## Custom facets toevoegen

Met de `facet()`-method kun je rechtstreeks een willekeurige facet-array toevoegen.

```php theme={null}
use Revolution\Bluesky\RichText\TextBuilder;

$builder = TextBuilder::make();

$builder->facet([
    'index' => [
        'byteStart' => 0,
        'byteEnd' => 5,
    ],
    'features' => [
        [
            '$type' => 'app.bsky.richtext.facet#link',
            'uri' => 'https://example.com',
        ],
    ],
]);
```

## Tekenlimieten en aandachtspunten

### Byte-offsets en graphemes

De facet-indexen van het AT Protocol worden opgegeven als **UTF-8-byte-offsets**. `TextBuilder` berekent het aantal bytes intern met `strlen()`.

Multibyte-tekens zoals Japans en emoji nemen meerdere bytes per teken in beslag, dus de offset wordt bepaald door het **aantal bytes, niet het aantal tekens**.

```php theme={null}
// 'Hello' is 5 bytes in UTF-8 (1 teken = 1 byte)
// '日本語' is 9 bytes in UTF-8 (1 teken = 3 bytes)
$builder = TextBuilder::make('日本語');
// strlen($builder->text) === 9
```

### Tekenlimiet voor posts

Een Bluesky-post is beperkt tot **maximaal 300 tekens in graphemes (zichtbare tekens)**. De limiet geldt in graphemes en niet in bytes, dus ook in het Japans kun je 300 tekens schrijven.

```php theme={null}
use Illuminate\Support\Str;

$text = 'これが投稿テキストです。';

// Het aantal tekens in graphemes controleren
$length = Str::length($text); // equivalent aan mb_strlen()
```

<Info>
  `TextBuilder` zelf controleert het aantal tekens niet. Stuur je een post van meer dan 300 graphemes, dan geeft de AT Protocol-API een fout terug.
</Info>

## Overzicht van methods

| Method                                       | Beschrijving                                                  |
| -------------------------------------------- | ------------------------------------------------------------- |
| `TextBuilder::make(string $text = '')`       | Een instantie maken                                           |
| `text(string $text)`                         | Tekst toevoegen aan het einde                                 |
| `newLine(int $count = 1)`                    | Een regeleinde toevoegen                                      |
| `mention(string $text, ?string $did = null)` | Een mention-facet toevoegen                                   |
| `link(string $text, ?string $uri = null)`    | Een link-facet toevoegen                                      |
| `tag(string $text, ?string $tag = null)`     | Een hashtag-facet toevoegen                                   |
| `detectFacets()`                             | Facets automatisch detecteren in de tekst                     |
| `facet(array $facet)`                        | Een custom facet toevoegen                                    |
| `resetFacets()`                              | Alle facets resetten                                          |
| `toPost()`                                   | Converteren naar een `Post`-record                            |
| `toArray()`                                  | Converteren naar een `['text' => ..., 'facets' => ...]`-array |

<Info>
  Source: [src/RichText/TextBuilder.php](https://github.com/invokable/laravel-bluesky/blob/main/src/RichText/TextBuilder.php)
</Info>


## Related topics

- [Basic client - Laravel Bluesky](/nl/packages/laravel-bluesky/basic-client.md)
- [Laravel Bluesky](/nl/packages/laravel-bluesky/index.md)
- [Notificatiekanaal - Laravel Bluesky](/nl/packages/laravel-bluesky/notification.md)
- [MongoDB](/nl/mongodb.md)
- [Scopes in Eloquent](/nl/advanced/eloquent-scopes.md)
