tangled
alpha
login
or
join now
socialde.pt
/
atp-client
3
fork
atom
Laravel AT Protocol Client (alpha & unstable)
3
fork
atom
overview
issues
pulls
pipelines
Add BuildsRichText trait for fluent rich text building
Miguel Batres
3 months ago
a46c1773
6e435270
+197
1 changed file
expand all
collapse all
unified
split
src
Builders
Concerns
BuildsRichText.php
+197
src/Builders/Concerns/BuildsRichText.php
···
1
1
+
<?php
2
2
+
3
3
+
namespace SocialDept\AtpClient\Builders\Concerns;
4
4
+
5
5
+
use SocialDept\AtpClient\RichText\FacetDetector;
6
6
+
use SocialDept\AtpResolver\Facades\Resolver;
7
7
+
8
8
+
trait BuildsRichText
9
9
+
{
10
10
+
protected string $text = '';
11
11
+
12
12
+
protected array $facets = [];
13
13
+
14
14
+
/**
15
15
+
* Add plain text
16
16
+
*/
17
17
+
public function text(string $text): self
18
18
+
{
19
19
+
$this->text .= $text;
20
20
+
21
21
+
return $this;
22
22
+
}
23
23
+
24
24
+
/**
25
25
+
* Add one or more new lines
26
26
+
*/
27
27
+
public function newLine(int $count = 1): self
28
28
+
{
29
29
+
$this->text .= str_repeat("\n", $count);
30
30
+
31
31
+
return $this;
32
32
+
}
33
33
+
34
34
+
/**
35
35
+
* Add mention (@handle)
36
36
+
*/
37
37
+
public function mention(string $handle, ?string $did = null): self
38
38
+
{
39
39
+
$handle = ltrim($handle, '@');
40
40
+
$start = $this->getBytePosition();
41
41
+
$this->text .= '@'.$handle;
42
42
+
$end = $this->getBytePosition();
43
43
+
44
44
+
if (! $did) {
45
45
+
try {
46
46
+
$did = Resolver::handleToDid($handle);
47
47
+
} catch (\Exception $e) {
48
48
+
return $this;
49
49
+
}
50
50
+
}
51
51
+
52
52
+
$this->facets[] = [
53
53
+
'index' => [
54
54
+
'byteStart' => $start,
55
55
+
'byteEnd' => $end,
56
56
+
],
57
57
+
'features' => [
58
58
+
[
59
59
+
'$type' => 'app.bsky.richtext.facet#mention',
60
60
+
'did' => $did,
61
61
+
],
62
62
+
],
63
63
+
];
64
64
+
65
65
+
return $this;
66
66
+
}
67
67
+
68
68
+
/**
69
69
+
* Add link with custom display text
70
70
+
*/
71
71
+
public function link(string $text, string $uri): self
72
72
+
{
73
73
+
$start = $this->getBytePosition();
74
74
+
$this->text .= $text;
75
75
+
$end = $this->getBytePosition();
76
76
+
77
77
+
$this->facets[] = [
78
78
+
'index' => [
79
79
+
'byteStart' => $start,
80
80
+
'byteEnd' => $end,
81
81
+
],
82
82
+
'features' => [
83
83
+
[
84
84
+
'$type' => 'app.bsky.richtext.facet#link',
85
85
+
'uri' => $uri,
86
86
+
],
87
87
+
],
88
88
+
];
89
89
+
90
90
+
return $this;
91
91
+
}
92
92
+
93
93
+
/**
94
94
+
* Add a URL (displayed as-is)
95
95
+
*/
96
96
+
public function url(string $url): self
97
97
+
{
98
98
+
return $this->link($url, $url);
99
99
+
}
100
100
+
101
101
+
/**
102
102
+
* Add hashtag
103
103
+
*/
104
104
+
public function tag(string $tag): self
105
105
+
{
106
106
+
$tag = ltrim($tag, '#');
107
107
+
108
108
+
$start = $this->getBytePosition();
109
109
+
$this->text .= '#'.$tag;
110
110
+
$end = $this->getBytePosition();
111
111
+
112
112
+
$this->facets[] = [
113
113
+
'index' => [
114
114
+
'byteStart' => $start,
115
115
+
'byteEnd' => $end,
116
116
+
],
117
117
+
'features' => [
118
118
+
[
119
119
+
'$type' => 'app.bsky.richtext.facet#tag',
120
120
+
'tag' => $tag,
121
121
+
],
122
122
+
],
123
123
+
];
124
124
+
125
125
+
return $this;
126
126
+
}
127
127
+
128
128
+
/**
129
129
+
* Auto-detect and add facets from plain text
130
130
+
*/
131
131
+
public function autoDetect(string $text): self
132
132
+
{
133
133
+
$start = $this->getBytePosition();
134
134
+
$this->text .= $text;
135
135
+
136
136
+
$detected = FacetDetector::detect($text);
137
137
+
138
138
+
foreach ($detected as $facet) {
139
139
+
$facet['index']['byteStart'] += $start;
140
140
+
$facet['index']['byteEnd'] += $start;
141
141
+
$this->facets[] = $facet;
142
142
+
}
143
143
+
144
144
+
return $this;
145
145
+
}
146
146
+
147
147
+
/**
148
148
+
* Get current byte position (UTF-8 byte offset)
149
149
+
*/
150
150
+
protected function getBytePosition(): int
151
151
+
{
152
152
+
return strlen($this->text);
153
153
+
}
154
154
+
155
155
+
/**
156
156
+
* Get the text content
157
157
+
*/
158
158
+
public function getText(): string
159
159
+
{
160
160
+
return $this->text;
161
161
+
}
162
162
+
163
163
+
/**
164
164
+
* Get the facets
165
165
+
*/
166
166
+
public function getFacets(): array
167
167
+
{
168
168
+
return $this->facets;
169
169
+
}
170
170
+
171
171
+
/**
172
172
+
* Get text and facets as array
173
173
+
*/
174
174
+
protected function getTextAndFacets(): array
175
175
+
{
176
176
+
return [
177
177
+
'text' => $this->text,
178
178
+
'facets' => $this->facets,
179
179
+
];
180
180
+
}
181
181
+
182
182
+
/**
183
183
+
* Get grapheme count (closest to what AT Protocol uses for limits)
184
184
+
*/
185
185
+
public function getGraphemeCount(): int
186
186
+
{
187
187
+
return grapheme_strlen($this->text);
188
188
+
}
189
189
+
190
190
+
/**
191
191
+
* Check if text exceeds AT Protocol post limit (300 graphemes)
192
192
+
*/
193
193
+
public function exceedsLimit(int $limit = 300): bool
194
194
+
{
195
195
+
return $this->getGraphemeCount() > $limit;
196
196
+
}
197
197
+
}