diff --git a/package-lock.json b/package-lock.json index 3936b55f..3ebec3a3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "imwald", - "version": "22.0.2", + "version": "22.1.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "imwald", - "version": "22.0.2", + "version": "22.1.1", "license": "MIT", "dependencies": { "@asciidoctor/core": "^3.0.4", diff --git a/package.json b/package.json index 77278bd3..b7c69c3d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "imwald", - "version": "22.0.2", + "version": "22.1.1", "description": "Imwald — a user-friendly Nostr client focused on relay feed browsing, publications, and relay discovery", "private": true, "type": "module", diff --git a/src/components/Image/index.tsx b/src/components/Image/index.tsx index b0e7536a..a184a209 100644 --- a/src/components/Image/index.tsx +++ b/src/components/Image/index.tsx @@ -1,6 +1,6 @@ import { Skeleton } from '@/components/ui/skeleton' import { cn } from '@/lib/utils' -import { isSafeMediaUrl } from '@/lib/url' +import { isRenderableMediaUrl, isSafeMediaUrl } from '@/lib/url' import { TImetaInfo } from '@/types' import { decode } from 'blurhash' import { ImageOff } from 'lucide-react' @@ -54,7 +54,7 @@ export default function Image({ const openLinkHref = (isSafeMediaUrl(url) && url.trim()) || (isSafeMediaUrl(imageUrl) && imageUrl.trim()) || '' - const badSrc = !imageUrl?.trim() || !isSafeMediaUrl(imageUrl.trim()) + const badSrc = !imageUrl?.trim() || !isRenderableMediaUrl(imageUrl.trim()) const showErrorState = hasError || badSrc const clearLoadWatch = () => { diff --git a/src/constants.ts b/src/constants.ts index 81e37e8c..db26ec95 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -38,12 +38,13 @@ export const HIVETALK_BASE_URL = * Electron packaged builds use `file:` + client-side history paths like `/notes/…`, which replace * the document URL with `file:///notes/…`. Relative `BASE_URL` links would then resolve next to that * bogus path and 404. Resolve from this module's emitted chunk (`dist/assets/*.js`) instead. + * One `..` reaches `dist/` (sibling of `assets/`); `../..` would miss `public/` copies and 404. */ export function publicAssetUrl(assetPath: string): string { const trimmed = assetPath.replace(/^\//, '') if (typeof window !== 'undefined' && window.location.protocol === 'file:') { try { - return new URL(`../../${trimmed}`, import.meta.url).href + return new URL(`../${trimmed}`, import.meta.url).href } catch { // fall through to BASE_URL } diff --git a/src/lib/url.ts b/src/lib/url.ts index f8498c9c..9c41b461 100644 --- a/src/lib/url.ts +++ b/src/lib/url.ts @@ -336,6 +336,20 @@ export function isSafeMediaUrl(url: string): boolean { return t.startsWith('http://') || t.startsWith('https://') } +/** + * True if the URL may be used as an `` in-app: http(s), `data:image/…` (e.g. pubkey + * placeholders), `blob:`, or `file:` (Electron). Use {@link isSafeMediaUrl} for user-openable links only. + */ +export function isRenderableMediaUrl(url: string): boolean { + if (!url || typeof url !== 'string') return false + const t = url.trim() + if (isSafeMediaUrl(t)) return true + if (t.startsWith('data:image/')) return true + if (t.startsWith('blob:')) return true + if (t.startsWith('file:')) return true + return false +} + /** * Primal R2A CDN URL for media keyed by SHA-256 (same object as `https://blossom.primal.net/{hash}.ext`). * Used when the blossom host fails in-browser; aligns with NIP-B7-style alternate retrieval.