diff --git a/e2e/import-document.spec.ts b/e2e/import-document.spec.ts index 0c40811..8e9c93b 100644 --- a/e2e/import-document.spec.ts +++ b/e2e/import-document.spec.ts @@ -57,16 +57,21 @@ test('story import: preview and import a Word manuscript', async ({ page }) => { title ); + // The settings page's Import button is the modal's second entry point; + // import-story.spec covers the library-menu one. Picking a file previews + // it at once. await gotoReady(page, `/universes/docland-${stamp}`); await page.getByRole('link', { name: 'Import and export' }).click(); - await page.locator('input[name="archive"]').setInputFiles({ + await page.getByRole('button', { name: 'Import a story...' }).click(); + const dialog = page.getByRole('dialog', { name: 'Import a story' }); + await expect(dialog).toBeVisible(); + await dialog.locator('input[name="archive"]').setInputFiles({ name: 'manuscript.docx', mimeType: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', buffer: docx }); - await page.getByRole('button', { name: 'Preview' }).click(); - const report = page.locator('.import-report'); + const report = dialog.locator('.import-report'); await expect(report).toContainText(`"${title}": 2 chapters, 3 scenes`); await page.getByRole('button', { name: 'Import story' }).click(); diff --git a/e2e/import-story.spec.ts b/e2e/import-story.spec.ts index c5ad9a1..12c788f 100644 --- a/e2e/import-story.spec.ts +++ b/e2e/import-story.spec.ts @@ -3,7 +3,7 @@ import { gotoReady } from './navigate'; import { pickFromLibraryMenu, startStoryInUniverse } from './library'; // The story import round trip: write a story, download its export zip, and -// import the zip back through the universe settings preview flow. +// import the zip back through the library's import modal preview flow. test('story import: preview and import a story export zip', async ({ page }) => { await gotoReady(page, '/'); @@ -63,16 +63,23 @@ test('story import: preview and import a story export zip', async ({ page }) => expect(archive.status()).toBe(200); const zipBytes = await archive.body(); - await gotoReady(page, `/universes/importland-${stamp}`); - await page.getByRole('link', { name: 'Import and export' }).click(); - await page.locator('input[name="archive"]').setInputFiles({ + // Feed it back through the import modal, opened from the universe's New + // story menu on the library. Picking a file previews it at once. + await gotoReady(page, '/'); + await page + .locator('.universe-section', { hasText: universeName }) + .getByRole('button', { name: 'New story', exact: true }) + .click(); + await page.getByRole('menuitem', { name: 'Import a story into this universe...' }).click(); + const dialog = page.getByRole('dialog', { name: 'Import a story' }); + await expect(dialog).toBeVisible(); + await dialog.locator('input[name="archive"]').setInputFiles({ name: 'roundtrip.zip', mimeType: 'application/zip', buffer: zipBytes }); - await page.getByRole('button', { name: 'Preview' }).click(); - const report = page.locator('.import-report'); + const report = dialog.locator('.import-report'); await expect(report).toContainText('"Roundtrip": 1 chapter, 1 scene'); await expect(report).toContainText('A story named "Roundtrip" already exists'); await expect(report).toContainText('Bram'); diff --git a/src/lib/components/ImportStoryModal.svelte b/src/lib/components/ImportStoryModal.svelte new file mode 100644 index 0000000..dea7c77 --- /dev/null +++ b/src/lib/components/ImportStoryModal.svelte @@ -0,0 +1,354 @@ + + +{#if universe} + +{/if} + + diff --git a/src/lib/docs/getting-started.md b/src/lib/docs/getting-started.md index 49e6c9f..23b88a8 100644 --- a/src/lib/docs/getting-started.md +++ b/src/lib/docs/getting-started.md @@ -26,9 +26,9 @@ Right-click a scene in a story's sidebar to delete it; it sits in the story's "D Everything you write can leave as markdown: each story's settings offer a zip of its scenes, a universe's settings bundle the whole world, and your account page has a download of everything you own. -A story export zip can also come back in. Open the universe's settings, go to "Import and export", and upload the zip under "Import a story". You get a preview first: how many chapters, scenes, and words are coming, and how each story note will be handled. A note whose character, place, or lore entry already exists in the universe (matched by name) attaches to it; a name with no match creates a new entry; a name shared by more than one entry is skipped, and the preview says so. Nothing is written until you press "Import story", and the import always creates a new story, even if one with the same title exists. +A story export zip can also come back in. Choose "Import a story" from a New menu on the library, or from the universe's settings under "Import and export". A dialog opens: pick the zip or drag it in, and a preview appears at once - how many chapters, scenes, and words are coming, and how each story note will be handled. A note whose character, place, or lore entry already exists in the universe (matched by name) attaches to it; a name with no match creates a new entry; a name shared by more than one entry is skipped, and the preview says so. Nothing is written until you press "Import story", and the import always creates a new story, even if one with the same title exists. -You can bring in a manuscript written elsewhere the same way. Upload a Word document (.docx) or an EPUB (.epub) in that same "Import a story" box, or choose "Import a story from a file..." from a New menu on the library, which opens that same box. Codex reads the chapters and scenes from the way the document marks them: chapter headings, "Chapter 1" lines, and scene breaks such as a centered row of asterisks. When a document has no such markers, the whole text comes in as a single scene that you can split up afterward. Pictures embedded in the document come along when image storage is set up; the preview tells you if any will be left out. As with a zip, you check the preview before anything is written. +You can bring in a manuscript written elsewhere the same way. Pick a Word document (.docx) or an EPUB (.epub) in that same "Import a story" dialog. Codex reads the chapters and scenes from the way the document marks them: chapter headings, "Chapter 1" lines, and scene breaks such as a centered row of asterisks. When a document has no such markers, the whole text comes in as a single scene that you can split up afterward. Pictures embedded in the document come along when image storage is set up; the preview tells you if any will be left out. As with a zip, you check the preview before anything is written. ## Jumping around diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index 8d46c6b..cadab9c 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -1,6 +1,7 @@