> ## 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

> Ausführliche Verwendung der TextBuilder-Klasse zum Aufbau von Bluesky-Rich-Text (facets) in PHP.

## Was ist der TextBuilder?

Der `TextBuilder` ist eine Klasse, mit der Sie die vom Bluesky-AT-Protocol definierten **facets** (Rich-Text-Annotations) über Method-Chaining zusammenstellen.

Der Body eines Bluesky-Posts ist reiner Text. Um Mentions, Links und Hashtags anzuzeigen, muss neben dem Text ein `facets`-Array übermittelt werden, das jeweils Position (Byte-Offset) und Typ angibt. Der `TextBuilder` übernimmt diese Offset-Berechnung und den Array-Aufbau automatisch.

```mermaid theme={null}
sequenceDiagram
    participant App as Laravel-Anwendung
    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 berechnen und<br>facets-Array aufbauen
    TB->>Post: toPost()
    Post->>AT: Bluesky::post($post)
    AT-->>App: Response
```

## Einfache Textausgabe

### TextBuilder::make()

Erzeugen Sie mit `TextBuilder::make()` eine Instanz mit optionalem Initialtext. Der Initialtext ist optional.

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

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

### text()

Mit `text()` hängen Sie Text am Ende an.

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

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

### newLine()

Fügt einen Zeilenumbruch hinzu. Über `count` können Sie die Zeilenanzahl angeben (Default: 1).

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

### toPost()

Wandelt die `TextBuilder`-Instanz in einen `Post`-Record um. Kann direkt an `Bluesky::post()` übergeben werden.

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

$post = TextBuilder::make('Einfacher Post')->toPost();

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

### Post::build()

Alternativ übergeben Sie eine Closure an `Post::build()`. Der Rückgabewert ist ein `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);
```

## Mention (`@mention`) hinzufügen

Fügen Sie mit `mention()` eine Mention-Facette hinzu.

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

### Automatische DID-Auflösung

Wenn Sie `did` weglassen, wird die DID automatisch aus dem Handle aufgelöst (`Bluesky::resolveHandle()` wird aufgerufen).

```php theme={null}
// DID automatisch auflösen (führt einen API-Aufruf aus)
$builder->mention('@alice.bsky.social');
```

### DID explizit angeben

Wenn die DID bekannt ist, können Sie den API-Aufruf vermeiden, indem Sie sie explizit übergeben.

```php theme={null}
// DID direkt angeben (empfohlen: kein API-Aufruf)
$builder->mention(text: '@alice.bsky.social', did: 'did:plc:xxxxxxxxxxxxxxxxxxxx');
```

<Tip>
  Wenn Sie in der Produktion häufig Mentions verwenden, können Sie die DIDs cachen, um API-Aufrufe zu reduzieren.
</Tip>

## Link (URL) einbetten

Fügen Sie mit `link()` eine Link-Facette hinzu.

```php theme={null}
// Die URL selbst als Anzeigetext nutzen
$builder->link('https://laravel.com');

// Anzeigetext und URL getrennt angeben
$builder->link(text: 'Offizielle Laravel-Website', uri: 'https://laravel.com');
```

Wird `uri` weggelassen, wird `text` als URI verwendet.

## Hashtags hinzufügen

Fügen Sie mit `tag()` eine Hashtag-Facette hinzu.

```php theme={null}
// Text, der mit # beginnt, extrahiert das Tag automatisch
$builder->tag('#Laravel');

// Tag-String explizit angeben (String ohne #)
$builder->tag(text: '#Laravel', tag: 'Laravel');
```

## Zusammengesetzte Texte

Kombinieren Sie mehrere Facetten für reichhaltige Post-Texte.

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

$post = TextBuilder::make('Ein neuer Artikel wurde veröffentlicht!')
    ->newLine(count: 2)
    ->mention('@alice.bsky.social', did: 'did:plc:xxxx')
    ->text(' sollte ihn unbedingt lesen.')
    ->newLine()
    ->link(text: 'Artikel lesen', uri: 'https://example.com/article/1')
    ->newLine()
    ->tag('#Laravel')
    ->text(' ')
    ->tag('#PHP')
    ->toPost();

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

### Schreibweise mit 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('Ein neuer Artikel wurde veröffentlicht!')
            ->newLine(count: 2)
            ->mention('@alice.bsky.social', did: 'did:plc:xxxx')
            ->text(' sollte ihn unbedingt lesen.')
            ->newLine()
            ->link(text: 'Artikel lesen', uri: 'https://example.com/article/1')
            ->newLine()
            ->tag('#Laravel')
            ->text(' ')
            ->tag('#PHP');
});

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

## Integration mit Posts

### Kombination mit 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);
```

### Kombination mit dem Notification-Kanal

In der Methode `toBluesky()` von `BlueskyChannel` können Sie `Post::build()` verwenden.

```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('Deployment abgeschlossen')
                    ->newLine()
                    ->link(text: 'Überprüfen', uri: $this->url)
                    ->newLine()
                    ->tag('#Laravel');
        });
    }
}
```

## Facets automatisch erkennen

Es ist auch möglich, `@mention`, URLs und `#hashtag` im Text automatisch zu erkennen und facets zu setzen.

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

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

// Nach detectFacets() können Sie weitere facets ergänzen
$builder->newLine()->tag('#bob');

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

<Warning>
  `detectFacets()` arbeitet regex-basiert. Wenn Sie Links garantiert setzen möchten, ist es sicherer, `link()`, `mention()` und `tag()` explizit zu verwenden.
</Warning>

## Eigene Facets hinzufügen

Mit der Methode `facet()` können Sie ein beliebiges Facet-Array direkt hinzufügen.

```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',
        ],
    ],
]);
```

## Zeichenbegrenzung und Hinweise

### Byte-Offsets und Graphemes

Der Facet-Index des AT Protocols wird in **UTF-8-Byte-Offsets** angegeben. `TextBuilder` berechnet die Byte-Anzahl intern über `strlen()`.

Multi-Byte-Zeichen wie japanische Zeichen und Emojis verbrauchen pro Zeichen mehrere Bytes. Die Offsets ergeben sich daher **aus der Byte-Anzahl, nicht aus der Zeichenanzahl**.

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

### Zeichenlimit für Posts

Bluesky-Posts sind auf **maximal 300 Zeichen in Graphemes (angezeigte Zeichenanzahl)** begrenzt. Da nicht in Bytes, sondern in Graphemes gerechnet wird, können Sie auch in Japanisch bis zu 300 Zeichen schreiben.

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

$text = 'Dies ist der Post-Text.';

// Zeichenanzahl in Graphemes ermitteln
$length = Str::length($text); // äquivalent zu mb_strlen()
```

<Info>
  `TextBuilder` selbst prüft die Zeichenanzahl nicht. Wenn Sie einen Post mit mehr als 300 Graphemes senden, gibt die AT-Protocol-API einen Fehler zurück.
</Info>

## Methodenübersicht

| Methode                                      | Beschreibung                                              |
| -------------------------------------------- | --------------------------------------------------------- |
| `TextBuilder::make(string $text = '')`       | Instanz erzeugen                                          |
| `text(string $text)`                         | Text am Ende hinzufügen                                   |
| `newLine(int $count = 1)`                    | Zeilenumbruch hinzufügen                                  |
| `mention(string $text, ?string $did = null)` | Mention-Facette hinzufügen                                |
| `link(string $text, ?string $uri = null)`    | Link-Facette hinzufügen                                   |
| `tag(string $text, ?string $tag = null)`     | Hashtag-Facette hinzufügen                                |
| `detectFacets()`                             | Facets automatisch aus dem Text erkennen                  |
| `facet(array $facet)`                        | Eigene Facette hinzufügen                                 |
| `resetFacets()`                              | Alle Facets zurücksetzen                                  |
| `toPost()`                                   | In einen `Post`-Record umwandeln                          |
| `toArray()`                                  | In das Array `['text' => ..., 'facets' => ...]` umwandeln |

<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](/de/packages/laravel-bluesky/basic-client.md)
- [Laravel Bluesky](/de/packages/laravel-bluesky/index.md)
- [Notification-Kanal - Laravel Bluesky](/de/packages/laravel-bluesky/notification.md)
- [Query Builder](/de/query-builder.md)
- [Suche](/de/search.md)
