diff --git a/frontend/src/lib/geschichte/StoryDocumentPanel.svelte b/frontend/src/lib/geschichte/StoryDocumentPanel.svelte
new file mode 100644
index 00000000..9ac55d5d
--- /dev/null
+++ b/frontend/src/lib/geschichte/StoryDocumentPanel.svelte
@@ -0,0 +1,193 @@
+
+
+
+
+
+ {m.geschichte_documents_heading()}
+
+
+
+ {m.geschichte_documents_heading()}
+
+ {m.geschichte_documents_hint()}
+
+ {#if errorMessage}
+
+ {errorMessage}
+
+ {/if}
+
+ {#if items.length === 0}
+ {m.geschichte_documents_empty()}
+ {:else}
+
+ {#each items as item (item.id)}
+ -
+ {#if item.document}
+
+ {item.document.title}
+
+ {:else}
+
+ {m.geschichte_documents_deleted_placeholder()}
+
+ {/if}
+
+
+ {/each}
+
+ {/if}
+
+
+
+
+
diff --git a/frontend/src/lib/geschichte/StoryDocumentPanel.svelte.spec.ts b/frontend/src/lib/geschichte/StoryDocumentPanel.svelte.spec.ts
new file mode 100644
index 00000000..fd5113e4
--- /dev/null
+++ b/frontend/src/lib/geschichte/StoryDocumentPanel.svelte.spec.ts
@@ -0,0 +1,312 @@
+import { afterEach, describe, expect, it, vi } from 'vitest';
+import { cleanup, render } from 'vitest-browser-svelte';
+import { page, userEvent } from 'vitest/browser';
+import { m } from '$lib/paraglide/messages.js';
+import StoryDocumentPanel from './StoryDocumentPanel.svelte';
+
+const waitForDebounce = () => new Promise((r) => setTimeout(r, 350));
+
+const docSummary = (id: string, title: string) => ({
+ id,
+ title,
+ datePrecision: 'DAY' as const,
+ receiverCount: 0
+});
+
+const makeItem = (
+ id: string,
+ position: number,
+ document?: ReturnType