feat: show currently selected image on home screen frame card
CI / test (push) Has been cancelled

Decode the device's rendered 4bpp Spectra-6 .bin into a PNG (cached
next to the .bin) so the home-screen preview matches the dithered
6-color output the e-ink actually displays.

- New endpoint: GET /api/devices/{id}/preview
- Expose currentImageId on device JSON
- HomeView passes preview URL to FrameCard for both single and compact layouts
- Drive-by: fix vite.config.ts to import defineConfig from vitest/config
  so the build no longer fails on the unknown `test` property; remove
  unused useUploadStore import in HomeView test

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-06 12:45:06 -04:00
parent 199a75fd72
commit fc0111a18e
18 changed files with 122 additions and 13 deletions
@@ -33,6 +33,7 @@ const makeDevice = (overrides: Partial<Device> = {}): Device => ({
linkedAt: '2026-01-01T00:00:00Z',
lastSeenAt: null,
lockedImageId: null,
currentImageId: null,
...overrides,
})
+1
View File
@@ -15,6 +15,7 @@ const makeDevice = (overrides: Partial<Device> = {}): Device => ({
linkedAt: '2026-01-01T00:00:00Z',
lastSeenAt: null,
lockedImageId: null,
currentImageId: null,
...overrides,
})
+1 -1
View File
@@ -2,7 +2,6 @@ import { describe, it, expect, vi, beforeEach } from 'vitest'
import { mount } from '@vue/test-utils'
import { setActivePinia, createPinia } from 'pinia'
import { useDevicesStore } from '@/stores/devices'
import { useUploadStore } from '@/stores/upload'
import HomeView from '@/views/HomeView.vue'
import type { Device } from '@/types'
@@ -70,6 +69,7 @@ const makeDevice = (overrides: Partial<Device> = {}): Device => ({
linkedAt: '2026-01-01T00:00:00Z',
lastSeenAt: null,
lockedImageId: null,
currentImageId: null,
...overrides,
})
@@ -80,6 +80,7 @@ const makeDevice = (overrides: Partial<Device> = {}): Device => ({
linkedAt: '2026-01-01T00:00:00Z',
lastSeenAt: null,
lockedImageId: null,
currentImageId: null,
...overrides,
})
+1
View File
@@ -18,6 +18,7 @@ export interface Device {
linkedAt: string
lastSeenAt: string | null
lockedImageId: number | null
currentImageId: number | null
}
export interface CropParams {
+7
View File
@@ -29,6 +29,7 @@
size="large"
:status="deviceStatus(devicesStore.devices[0])"
:orientation="devicesStore.devices[0].orientation"
:thumbnailUrl="previewUrl(devicesStore.devices[0])"
@add-photo="onAddPhoto"
@edit="onEdit"
/>
@@ -44,6 +45,7 @@
size="compact"
:status="deviceStatus(device)"
:orientation="device.orientation"
:thumbnailUrl="previewUrl(device)"
@add-photo="onAddPhoto"
@edit="onEdit"
/>
@@ -109,6 +111,11 @@ function deviceStatus(device: Device): 'ok' | 'offline' {
const windowMs = Math.max(device.rotationIntervalMinutes * 2 * 60_000, 30 * 60_000)
return seenMs <= windowMs ? 'ok' : 'offline'
}
function previewUrl(device: Device): string | undefined {
const imageId = device.lockedImageId ?? device.currentImageId
return imageId ? `/api/devices/${device.id}/preview?v=${imageId}` : undefined
}
import FrameCard from '@/components/FrameCard.vue'
import BaseBottomSheet from '@/components/BaseBottomSheet.vue'
import BaseButton from '@/components/BaseButton.vue'
+1 -2
View File
@@ -1,5 +1,4 @@
/// <reference types="vitest" />
import { defineConfig } from 'vite'
import { defineConfig } from 'vitest/config'
import vue from '@vitejs/plugin-vue'
import { fileURLToPath, URL } from 'node:url'