+48
-18
src/components/PostComposer.svelte
+48
-18
src/components/PostComposer.svelte
···
7
import BskyPost from './BskyPost.svelte';
8
import { parseCanonicalResourceUri } from '@atcute/lexicons';
9
import type { ComAtprotoRepoStrongRef } from '@atcute/atproto';
10
11
export type State =
12
| { type: 'null' }
···
32
cid: p.cid!,
33
uri: p.uri
34
});
35
const record: AppBskyFeedPost.Main = {
36
$type: 'app.bsky.feed.post',
37
-
text,
38
reply:
39
_state.type === 'focused' && _state.replying
40
? {
···
117
/>
118
{/snippet}
119
120
{#snippet composer(replying?: PostWithUri, quoting?: PostWithUri)}
121
<div class="flex items-center gap-2">
122
<div class="grow"></div>
···
144
{@render renderPost(replying)}
145
{/if}
146
<div class="composer space-y-2">
147
-
<textarea
148
-
bind:this={textareaEl}
149
-
bind:value={postText}
150
-
onfocus={() => (_state.type = 'focused')}
151
-
onblur={unfocus}
152
-
onkeydown={(event) => {
153
-
if (event.key === 'Escape') unfocus();
154
-
if (event.key === 'Enter' && (event.metaKey || event.ctrlKey)) doPost();
155
-
}}
156
-
placeholder="what's on your mind?"
157
-
rows="4"
158
-
class="field-sizing-content resize-none"
159
-
></textarea>
160
{#if quoting}
161
{@render renderPost(quoting)}
162
{/if}
···
209
</div>
210
</div>
211
212
-
<!-- TODO: this fucking blows -->
213
<style>
214
@reference "../app.css";
215
···
224
}
225
226
textarea {
227
-
@apply w-full bg-transparent p-0;
228
}
229
230
input {
···
235
@apply focus:scale-100;
236
}
237
238
-
input::placeholder,
239
-
textarea::placeholder {
240
color: color-mix(in srgb, var(--acc-color) 45%, var(--nucleus-bg));
241
}
242
···
7
import BskyPost from './BskyPost.svelte';
8
import { parseCanonicalResourceUri } from '@atcute/lexicons';
9
import type { ComAtprotoRepoStrongRef } from '@atcute/atproto';
10
+
import { parseToRichText } from '$lib/richtext';
11
+
import { tokenize } from '$lib/richtext/parser';
12
13
export type State =
14
| { type: 'null' }
···
34
cid: p.cid!,
35
uri: p.uri
36
});
37
+
38
+
// Parse rich text (mentions, links, tags)
39
+
const rt = await parseToRichText(client, text);
40
+
41
const record: AppBskyFeedPost.Main = {
42
$type: 'app.bsky.feed.post',
43
+
text: rt.text,
44
+
facets: rt.facets,
45
reply:
46
_state.type === 'focused' && _state.replying
47
? {
···
124
/>
125
{/snippet}
126
127
+
{#snippet highlighter(text: string)}
128
+
{#each tokenize(text) as token, idx (idx)}
129
+
{@const highlighted =
130
+
token.type === 'mention' ||
131
+
token.type === 'topic' ||
132
+
token.type === 'link' ||
133
+
token.type === 'autolink'}
134
+
<span class={highlighted ? 'text-(--nucleus-accent2)' : ''}>{token.raw}</span>
135
+
{/each}
136
+
{#if text.endsWith('\n')}
137
+
<br />
138
+
{/if}
139
+
{/snippet}
140
+
141
{#snippet composer(replying?: PostWithUri, quoting?: PostWithUri)}
142
<div class="flex items-center gap-2">
143
<div class="grow"></div>
···
165
{@render renderPost(replying)}
166
{/if}
167
<div class="composer space-y-2">
168
+
<div class="relative grid">
169
+
<!-- todo: replace this with a proper rich text editor -->
170
+
<div
171
+
class="pointer-events-none col-start-1 row-start-1 min-h-[5lh] w-full bg-transparent text-wrap break-all whitespace-pre-wrap text-(--nucleus-fg)"
172
+
aria-hidden="true"
173
+
>
174
+
{@render highlighter(postText)}
175
+
</div>
176
+
177
+
<textarea
178
+
bind:this={textareaEl}
179
+
bind:value={postText}
180
+
onfocus={() => (_state.type = 'focused')}
181
+
onblur={unfocus}
182
+
onkeydown={(event) => {
183
+
if (event.key === 'Escape') unfocus();
184
+
if (event.key === 'Enter' && (event.metaKey || event.ctrlKey)) doPost();
185
+
}}
186
+
placeholder="what's on your mind?"
187
+
rows="4"
188
+
class="col-start-1 row-start-1 field-sizing-content min-h-[5lh] w-full resize-none overflow-hidden bg-transparent text-wrap break-all whitespace-pre-wrap text-transparent caret-(--nucleus-fg) placeholder:text-(--nucleus-fg)/45"
189
+
></textarea>
190
+
</div>
191
+
192
{#if quoting}
193
{@render renderPost(quoting)}
194
{/if}
···
241
</div>
242
</div>
243
244
<style>
245
@reference "../app.css";
246
···
255
}
256
257
textarea {
258
+
@apply w-full p-0;
259
}
260
261
input {
···
266
@apply focus:scale-100;
267
}
268
269
+
input::placeholder {
270
color: color-mix(in srgb, var(--acc-color) 45%, var(--nucleus-bg));
271
}
272