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:
@@ -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 {
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user