aboutsummaryrefslogtreecommitdiffstats
path: root/src/components
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-03-06 23:53:50 -0800
committerAdam Soltys <[email protected]>2026-03-06 23:53:50 -0800
commitc67d03fba0c1cc2fae9e8da4c7dedb41ea22d03f (patch)
tree27e9a3b762365a2cbd08ae2fa5d061268fca8ca8 /src/components
parentc75c05020dcf9208adfeb82d81bae194e4104ab9 (diff)
downloadcoinos-ui-c67d03fba0c1cc2fae9e8da4c7dedb41ea22d03f.tar.gz
coinos-ui-c67d03fba0c1cc2fae9e8da4c7dedb41ea22d03f.zip
render imeta media directly instead of routing through parseContent
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Diffstat (limited to 'src/components')
-rw-r--r--src/components/ChatMessage.svelte38
1 files changed, 22 insertions, 16 deletions
diff --git a/src/components/ChatMessage.svelte b/src/components/ChatMessage.svelte
index 8dc276e8..53baafcd 100644
--- a/src/components/ChatMessage.svelte
+++ b/src/components/ChatMessage.svelte
@@ -3,25 +3,18 @@
let { content, tags = [] }: { content: string; tags?: any[] } = $props();
- // Parse imeta tags for URLs and MIME types
- let imeta = $derived(
+ // Extract media from imeta tags (NIP-92)
+ let imetaMedia = $derived(
tags
.filter((t) => t[0] === "imeta")
- .map((t) => {
- const url = t.find((v) => v.startsWith("url "))?.slice(4);
- const mime = t.find((v) => v.startsWith("m "))?.slice(2) || "";
- return { url, mime };
- })
- .filter((m): m is { url: string; mime: string } => !!m.url),
+ .map((t) => ({
+ url: t.find((v) => v.startsWith("url "))?.slice(4) || "",
+ mime: t.find((v) => v.startsWith("m "))?.slice(2) || "",
+ }))
+ .filter((m) => m.url),
);
- // URLs from imeta not already in content
- let imetaUrls = $derived(imeta.filter((m) => !content.includes(m.url!)).map((m) => m.url));
-
- // Check if a URL is an image according to imeta MIME types
- const isImetaImage = (url: string) => imeta.some((m) => m.url === url && m.mime.startsWith("image/"));
-
- let parts = $derived(parseContent({ content: [content, ...imetaUrls].join("\n"), tags }));
+ let parts = $derived(parseContent({ content, tags }));
const truncUrl = (url: string) => {
const short = url.replace(/^https?:\/\/(www\.)?/, "");
@@ -40,13 +33,26 @@
};
</script>
+{#each imetaMedia as media}
+ {#if media.mime.startsWith("image/")}
+ <a href={media.url} target="_blank" rel="noopener noreferrer">
+ <img src={media.url} alt="" class="chat-img" />
+ </a>
+ {:else if media.mime.startsWith("video/")}
+ <!-- svelte-ignore a11y_media_has_caption -->
+ <video src={media.url} controls class="chat-video"></video>
+ {:else}
+ <a href={media.url} target="_blank" rel="noopener noreferrer" class="chat-link">{truncUrl(media.url)}</a>
+ {/if}
+{/each}
+
{#each parts as { type, value }}
{#if type === "text"}
{value}
{:else if type === "newline"}
<br />
{:else if type === "link"}
- {#if isImage(value.url) || isImetaImage(value.url)}
+ {#if isImage(value.url)}
<a href={value.url} target="_blank" rel="noopener noreferrer">
<img src={value.url} alt="" class="chat-img" />
</a>