fix(desktop): artifacts page timestamps render 1970 and local images fail

All three artifact timestamp sources (message.timestamp,
session.last_active, session.started_at) are epoch SECONDS — the
transcript reader and session-date-groups both multiply by 1000 — but
the collector passed them straight to new Date() (ms), so every
artifact rendered as 1970-01-21. Normalize seconds to ms once at
collection; the Date.now() fallback stays ms.

Local file artifacts (e.g. D:\ComfyUI\output\*.png) fell through to
mediaExternalUrl() which yields a file:// URL the renderer cannot
load. Route through the desktop fs bridge whenever it exists —
readDesktopFileDataUrl already dispatches remote REST vs local
Electron internally (#83380).
This commit is contained in:
thatssoheil
2026-08-10 15:43:57 -08:00
committed by Teknium
parent 021950ac81
commit bceac696ae
3 changed files with 24 additions and 15 deletions
@@ -1,5 +1,4 @@
import { readDesktopFileDataUrl } from '@/lib/desktop-fs'
import { filePathFromMediaPath, isRemoteGateway, mediaExternalUrl } from '@/lib/media'
import { mediaExternalUrl, resolveMediaDisplaySrc } from '@/lib/media'
import type { SessionInfo, SessionMessage } from '@/types/hermes'
export type ArtifactKind = 'image' | 'file' | 'link'
@@ -185,16 +184,14 @@ function artifactHref(value: string): string {
return value
}
export async function artifactImageSrc(value: string, href = artifactHref(value)): Promise<string> {
if (/^(?:https?|data):/i.test(value)) {
return href
}
if (typeof window !== 'undefined' && window.hermesDesktop && isRemoteGateway()) {
return readDesktopFileDataUrl(filePathFromMediaPath(value))
}
return href
export async function artifactImageSrc(value: string): Promise<string> {
// Delegate the whole local/remote ladder to the shared media resolver:
// inline (http/data) stays as-is, remote gateway goes through the
// authenticated fs bridge, local desktop through the Electron
// readFileDataUrl, and bare non-path link values fall through untouched.
// Reimplementing that ladder here would drift from resolveMediaDisplaySrc
// and regress one of its legs (#83380).
return resolveMediaDisplaySrc(value)
}
function artifactLabel(value: string): string {
+14 -2
View File
@@ -309,6 +309,19 @@ ${payload}
expect(fromNow[0]?.timestamp).toBe(now)
})
it('resolves local file image artifacts through the desktop fs bridge', async () => {
const readFileDataUrl = vi.fn(async () => 'data:image/png;base64,TE9DQUw=')
vi.stubGlobal('window', { hermesDesktop: { readFileDataUrl } })
// Local desktop (connection mode != 'remote'): a local image_generate
// output path must be read through the Electron bridge, not left as a
// file:// URL the renderer cannot load (#83380).
const path = '/home/me/.hermes/cache/image_generate/out.png'
await expect(artifactImageSrc(path)).resolves.toBe('data:image/png;base64,TE9DQUw=')
expect(readFileDataUrl).toHaveBeenCalledWith(path)
})
it('resolves remote image artifact thumbnails through the desktop fs bridge', async () => {
const api = vi.fn(async ({ path }: { path: string }) => {
if (path.startsWith('/api/fs/read-data-url?')) {
@@ -322,9 +335,8 @@ ${payload}
$connection.set({ baseUrl: 'https://gw', mode: 'remote', token: 'secret' } as never)
const path = '/Users/me/.hermes/skills/work-esab/references/images/manual-step03.jpeg'
const downloadHref = `https://gw/api/files/download?path=${encodeURIComponent(path)}&token=secret`
await expect(artifactImageSrc(path, downloadHref)).resolves.toBe('data:image/jpeg;base64,cmVtb3Rl')
await expect(artifactImageSrc(path)).resolves.toBe('data:image/jpeg;base64,cmVtb3Rl')
expect(api).toHaveBeenCalledWith({
path: '/api/fs/read-data-url?path=%2FUsers%2Fme%2F.hermes%2Fskills%2Fwork-esab%2Freferences%2Fimages%2Fmanual-step03.jpeg'
+1 -1
View File
@@ -474,7 +474,7 @@ function ArtifactImageCard({ artifact, failedImage, onImageError, onOpenChat }:
let active = true
setSrc('')
void artifactImageSrc(artifact.value, artifact.href)
void artifactImageSrc(artifact.value)
.then(nextSrc => {
if (active) {
setSrc(nextSrc)