refactor: move conversation domain to lib/conversation/

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Marcel
2026-05-05 13:48:50 +02:00
parent c7fda6a027
commit 422e86fbf1
3 changed files with 1 additions and 1 deletions

View File

@@ -1,45 +0,0 @@
<script lang="ts">
import type { components } from '$lib/generated/api';
type Doc = Pick<
components['schemas']['Document'],
'id' | 'thumbnailUrl' | 'thumbnailAspect' | 'pageCount'
>;
let { doc }: { doc: Doc } = $props();
const url = $derived(doc.thumbnailUrl ?? null);
const aspect = $derived(doc.thumbnailAspect ?? 'PORTRAIT');
const pageCount = $derived(doc.pageCount ?? 1);
const tileClass = $derived(aspect === 'LANDSCAPE' ? 'h-[120px] w-[168px]' : 'h-[168px] w-[120px]');
</script>
<div
data-testid="conv-thumb-tile"
data-aspect={aspect}
class="relative {tileClass} flex-shrink-0 overflow-hidden rounded-sm border border-line bg-white"
>
{#if url}
<img
src={url}
alt=""
class="h-full w-full object-cover object-top dark:mix-blend-multiply"
loading="lazy"
decoding="async"
/>
{:else}
<div
data-testid="conv-thumb-skeleton"
class="h-full w-full bg-line/60 motion-safe:animate-pulse"
aria-hidden="true"
></div>
{/if}
{#if pageCount > 1}
<span
data-testid="conv-thumb-page-badge"
class="absolute top-1 right-1 rounded-full bg-primary/90 px-2 py-1 text-sm leading-none font-bold text-surface"
>{pageCount}</span
>
{/if}
</div>

View File

@@ -1,112 +0,0 @@
import { describe, it, expect, afterEach } from 'vitest';
import { cleanup, render } from 'vitest-browser-svelte';
import ConversationThumbnail from './ConversationThumbnail.svelte';
afterEach(() => {
cleanup();
});
describe('ConversationThumbnail', () => {
it('renders the thumbnail image with a cache-busting v= query param', () => {
render(ConversationThumbnail, {
doc: {
id: '1111',
thumbnailUrl: '/api/documents/1111/thumbnail?v=2026-04-10T09%3A00%3A00Z',
thumbnailAspect: 'PORTRAIT',
pageCount: 1
}
});
const img = document.querySelector('img') as HTMLImageElement | null;
expect(img).not.toBeNull();
expect(img!.getAttribute('src')).toContain('/api/documents/1111/thumbnail');
expect(img!.getAttribute('src')).toContain('v=');
});
it('uses portrait dimensions when aspect is PORTRAIT', () => {
render(ConversationThumbnail, {
doc: {
id: 'p1',
thumbnailUrl: '/api/documents/p1/thumbnail?v=2026-04-10T09%3A00%3A00Z',
thumbnailAspect: 'PORTRAIT',
pageCount: 1
}
});
const tile = document.querySelector('[data-testid="conv-thumb-tile"]') as HTMLElement;
expect(tile.getAttribute('data-aspect')).toBe('PORTRAIT');
});
it('uses landscape dimensions when aspect is LANDSCAPE', () => {
render(ConversationThumbnail, {
doc: {
id: 'l1',
thumbnailUrl: '/api/documents/l1/thumbnail?v=2026-04-10T09%3A00%3A00Z',
thumbnailAspect: 'LANDSCAPE',
pageCount: 1
}
});
const tile = document.querySelector('[data-testid="conv-thumb-tile"]') as HTMLElement;
expect(tile.getAttribute('data-aspect')).toBe('LANDSCAPE');
});
it('falls back to PORTRAIT when thumbnailAspect is missing', () => {
render(ConversationThumbnail, {
doc: {
id: 'n1',
thumbnailUrl: '/api/documents/n1/thumbnail?v=2026-04-10T09%3A00%3A00Z'
}
});
const tile = document.querySelector('[data-testid="conv-thumb-tile"]') as HTMLElement;
expect(tile.getAttribute('data-aspect')).toBe('PORTRAIT');
});
it('renders the page badge when pageCount is greater than 1', () => {
render(ConversationThumbnail, {
doc: {
id: 'm1',
thumbnailUrl: '/api/documents/m1/thumbnail?v=2026-04-10T09%3A00%3A00Z',
thumbnailAspect: 'PORTRAIT',
pageCount: 4
}
});
const badge = document.querySelector('[data-testid="conv-thumb-page-badge"]') as HTMLElement;
expect(badge).not.toBeNull();
expect(badge.textContent).toContain('4');
// Senior-readable size: text-sm (14px) rather than text-xs (12px) on a
// small tile avoids marginal legibility on a 320px phone.
expect(badge.className).toContain('text-sm');
});
it('hides the page badge when pageCount is 1 or missing', () => {
render(ConversationThumbnail, {
doc: {
id: 's1',
thumbnailUrl: '/api/documents/s1/thumbnail?v=2026-04-10T09%3A00%3A00Z',
thumbnailAspect: 'PORTRAIT',
pageCount: 1
}
});
const badge = document.querySelector('[data-testid="conv-thumb-page-badge"]');
expect(badge).toBeNull();
});
it('renders a skeleton placeholder when no thumbnailUrl is set yet', () => {
render(ConversationThumbnail, {
doc: {
id: 'blank',
thumbnailAspect: 'PORTRAIT'
}
});
expect(document.querySelector('img')).toBeNull();
const skeleton = document.querySelector('[data-testid="conv-thumb-skeleton"]');
expect(skeleton).not.toBeNull();
expect(skeleton!.className).toContain('motion-safe:animate-pulse');
});
});

View File

@@ -1,5 +1,5 @@
<script lang="ts">
import ConversationThumbnail from '$lib/components/ConversationThumbnail.svelte';
import ConversationThumbnail from '$lib/conversation/ConversationThumbnail.svelte';
import TagChipList from '$lib/components/TagChipList.svelte';
import { formatDate } from '$lib/utils/date';
import * as m from '$lib/paraglide/messages.js';