diff --git a/deno.lock b/deno.lock
index 0af664f..5de8e38 100644
--- a/deno.lock
+++ b/deno.lock
@@ -6,8 +6,10 @@
"npm:@atcute/client@^4.0.5": "4.0.5",
"npm:@atcute/identity@^1.1.1": "1.1.1",
"npm:@atcute/lexicons@^1.2.2": "1.2.2",
+ "npm:@atcute/tid@^1.0.3": "1.0.3",
"npm:@eslint/compat@^1.4.0": "1.4.0_eslint@9.37.0",
"npm:@eslint/js@^9.36.0": "9.37.0",
+ "npm:@iconify/svelte@^5.0.2": "5.0.2_svelte@5.40.1__acorn@8.15.0",
"npm:@soffinal/websocket@~0.2.1": "0.2.1_typescript@5.9.3",
"npm:@sveltejs/adapter-auto@^6.1.0": "6.1.1_@sveltejs+kit@2.47.0__@sveltejs+vite-plugin-svelte@6.2.1___svelte@5.40.1____acorn@8.15.0___vite@7.1.10____@types+node@24.8.0____picomatch@4.0.3___@types+node@24.8.0__svelte@5.40.1___acorn@8.15.0__vite@7.1.10___@types+node@24.8.0___picomatch@4.0.3__acorn@8.15.0__@types+node@24.8.0_@sveltejs+vite-plugin-svelte@6.2.1__svelte@5.40.1___acorn@8.15.0__vite@7.1.10___@types+node@24.8.0___picomatch@4.0.3__@types+node@24.8.0_svelte@5.40.1__acorn@8.15.0_vite@7.1.10__@types+node@24.8.0__picomatch@4.0.3_@types+node@24.8.0",
"npm:@sveltejs/kit@^2.43.2": "2.47.0_@sveltejs+vite-plugin-svelte@6.2.1__svelte@5.40.1___acorn@8.15.0__vite@7.1.10___@types+node@24.8.0___picomatch@4.0.3__@types+node@24.8.0_svelte@5.40.1__acorn@8.15.0_vite@7.1.10__@types+node@24.8.0__picomatch@4.0.3_acorn@8.15.0_@types+node@24.8.0",
@@ -69,6 +71,9 @@
"esm-env"
]
},
+ "@atcute/tid@1.0.3": {
+ "integrity": "sha512-wfMJx1IMdnu0CZgWl0uR4JO2s6PGT1YPhpytD4ZHzEYKKQVuqV6Eb/7vieaVo1eYNMp2FrY67FZObeR7utRl2w=="
+ },
"@badrap/valita@0.4.6": {
"integrity": "sha512-4kdqcjyxo/8RQ8ayjms47HCWZIF5981oE5nIenbfThKDxWXtEHKipAOWlflpPJzZx9y/JWYQkp18Awr7VuepFg=="
},
@@ -285,6 +290,16 @@
"@humanwhocodes/retry@0.4.3": {
"integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ=="
},
+ "@iconify/svelte@5.0.2_svelte@5.40.1__acorn@8.15.0": {
+ "integrity": "sha512-1iWUT+1veS/QOAzKDG0NPgBtJYGoJqEPwF97voTm8jw6PQ6yU0hL73lEwFoTGMrZmatLvh9cjRBmeSHHaltmrg==",
+ "dependencies": [
+ "@iconify/types",
+ "svelte"
+ ]
+ },
+ "@iconify/types@2.0.0": {
+ "integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg=="
+ },
"@isaacs/fs-minipass@4.0.1": {
"integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==",
"dependencies": [
@@ -1752,8 +1767,10 @@
"npm:@atcute/client@^4.0.5",
"npm:@atcute/identity@^1.1.1",
"npm:@atcute/lexicons@^1.2.2",
+ "npm:@atcute/tid@^1.0.3",
"npm:@eslint/compat@^1.4.0",
"npm:@eslint/js@^9.36.0",
+ "npm:@iconify/svelte@^5.0.2",
"npm:@soffinal/websocket@~0.2.1",
"npm:@sveltejs/adapter-auto@^6.1.0",
"npm:@sveltejs/kit@^2.43.2",
diff --git a/package.json b/package.json
index b080f08..d0b007e 100644
--- a/package.json
+++ b/package.json
@@ -19,6 +19,7 @@
"@atcute/client": "^4.0.5",
"@atcute/identity": "^1.1.1",
"@atcute/lexicons": "^1.2.2",
+ "@atcute/tid": "^1.0.3",
"@soffinal/websocket": "^0.2.1",
"@wora/cache-persist": "^2.2.1",
"hash-wasm": "^4.12.0",
@@ -28,6 +29,7 @@
"devDependencies": {
"@eslint/compat": "^1.4.0",
"@eslint/js": "^9.36.0",
+ "@iconify/svelte": "^5.0.2",
"@sveltejs/adapter-auto": "^6.1.0",
"@sveltejs/kit": "^2.43.2",
"@sveltejs/vite-plugin-svelte": "^6.2.0",
diff --git a/src/components/BskyPost.svelte b/src/components/BskyPost.svelte
index bb5fb08..34992a9 100644
--- a/src/components/BskyPost.svelte
+++ b/src/components/BskyPost.svelte
@@ -1,10 +1,12 @@
@@ -88,7 +187,7 @@
class="rounded-full px-2.5 py-0.5 text-xs font-medium"
style="background: color-mix(in srgb, {mini
? 'var(--nucleus-fg)'
- : color} 13%, transparent); color: {mini ? 'var(--nucleus-fg)' : color};"
+ : color} 10%, transparent); color: {mini ? 'var(--nucleus-fg)' : color};"
>
{getEmbedText(record.embed.$type)}
@@ -96,12 +195,12 @@
{/snippet}
{#if mini}
-
+
{#await post}
loading...
{:then post}
{#if post.ok}
- {@const record = post.value}
+ {@const record = post.value.record}
@{handle}: {@render embedBadge(record)}
{record.text}
{:else}
@@ -122,10 +221,12 @@
{:then post}
{#if post.ok}
- {@const record = post.value}
+ {@const record = post.value.record}
{profileValue.displayName}(@{handle})
+ >(@{handle})
{:else}
{handle}
{/if}
{/await}
-
-
·
{getRelativeTime(new Date(record.createdAt))}
-
+
{record.text}
+ {#if isOnPostComposer}
+ {@render embedBadge(record)}
+ {/if}
- {#if record.embed}
+ {#if !isOnPostComposer && record.embed}
{@const embed = record.embed}
{#snippet embedPost(uri: ResourceUri)}
{@const parsedUri = expect(parseCanonicalResourceUri(uri))}
{#if !(did === parsedUri.repo && rkey === parsedUri.rkey)}
-
+
{:else}
you think you're funny with that recursive quote but i'm onto you
{/if}
@@ -222,6 +311,10 @@
{/if}
+ {#if !isOnPostComposer}
+ {@const backlinks = postActions.get(`${$selectedDid!}:${post.value.uri}`)}
+ {@render postControls(post.value, backlinks)}
+ {/if}
{:else}
@@ -230,3 +323,58 @@
{/if}
{/await}
{/if}
+
+{#snippet postControls(post: PostWithUri, backlinks?: PostActions)}
+
+ {#snippet label(
+ name: string,
+ icon: string,
+ onClick: (link: Backlink | null | undefined) => void,
+ backlink?: Backlink | null,
+ hasSolid?: boolean
+ )}
+
+ {/snippet}
+ {@render label('reply', 'heroicons:chat-bubble-left', () => {
+ onReply?.(post);
+ })}
+ {@render label(
+ 'repost',
+ 'heroicons:arrow-path-rounded-square-20-solid',
+ async (link) => {
+ if (link === undefined) return;
+ postActions.set(`${$selectedDid!}:${aturi}`, {
+ ...backlinks!,
+ repost: await toggleLink(link, 'app.bsky.feed.repost')
+ });
+ },
+ backlinks?.repost
+ )}
+ {@render label('quote', 'heroicons:paper-clip-20-solid', () => {
+ onQuote?.(post);
+ })}
+ {@render label(
+ 'like',
+ 'heroicons:star',
+ async (link) => {
+ if (link === undefined) return;
+ postActions.set(`${$selectedDid!}:${aturi}`, {
+ ...backlinks!,
+ like: await toggleLink(link, 'app.bsky.feed.like')
+ });
+ },
+ backlinks?.like,
+ true
+ )}
+
+{/snippet}
diff --git a/src/components/PfpPlaceholder.svelte b/src/components/PfpPlaceholder.svelte
index 106c994..4d8b988 100644
--- a/src/components/PfpPlaceholder.svelte
+++ b/src/components/PfpPlaceholder.svelte
@@ -8,7 +8,7 @@
-
+ /> -->
{/snippet}
+{#snippet replyPost(post: ThreadPost, reverse: boolean = reverseChronological)}
+
+ {reverse ? '↱' : '↳'}
+
+
+{/snippet}
+
{#snippet threadsView()}
- {#each threads as thread ([thread.rootUri, thread.branchParentPost, ...thread.posts.map((post) => post.uri)])}
+ {#each threads as thread (thread.rootUri)}
{#if thread.branchParentPost}
- {@const post = thread.branchParentPost}
-
- {reverseChronological ? '↱' : '↳'}
-
-
+ {@render replyPost(thread.branchParentPost)}
{/if}
- {#each thread.posts as post (post.uri)}
-
-
-
+ {#each thread.posts as post, idx (post.data.uri)}
+ {@const mini =
+ !expandedThreads.has(thread.rootUri) &&
+ thread.posts.length > 4 &&
+ idx > 0 &&
+ idx < thread.posts.length - 2}
+ {#if !mini}
+
+ (quoting = post)}
+ onReply={(post) => (replying = post)}
+ {...post}
+ />
+
+ {:else if mini}
+ {#if idx === 1}
+ {@render replyPost(post, !reverseChronological)}
+
+ {:else if idx === thread.posts.length - 3}
+ {@render replyPost(post)}
+ {/if}
+ {/if}
{/each}
{/each}