diff --git a/learner/e2e/edit.spec.ts b/learner/e2e/edit.spec.ts index d5f7567..c69c909 100644 --- a/learner/e2e/edit.spec.ts +++ b/learner/e2e/edit.spec.ts @@ -80,9 +80,20 @@ test('rename the book, edit a chapter into a new version and delete both', async await expect(page.getByRole('heading', { name: 'A long step' })).toBeVisible() // Edit the chapter text: the new version re-processes and later becomes ready again. + // The chapter editor is the main writing surface, so on a wide window it must use that width and + // a tall body instead of a fixed 620px box. + await page.setViewportSize({ width: 1280, height: 900 }) await page.getByTestId('edit-chapter-9').click() + await expectModalOverlay(page, page.getByTestId('chapter-dialog')) await expect(page.getByLabel('章节标题', { exact: true })).toHaveValue('First chapter') await expect(page.getByLabel('正文', { exact: true })).toHaveValue(firstText) + const dialog = await page.getByTestId('chapter-dialog').boundingBox() + expect(dialog!.width, 'the chapter editor follows the window').toBeGreaterThan(900) + const body = await page.getByLabel('正文', { exact: true }).boundingBox() + expect(body!.height, 'the text area is tall enough to write in').toBeGreaterThan(400) + const noOverflow = await page.evaluate(() => ({ scroll: document.documentElement.scrollWidth, inner: window.innerWidth })) + expect(noOverflow.scroll, 'the wide editor must not overflow').toBeLessThanOrEqual(noOverflow.inner) + await page.setViewportSize({ width: 1280, height: 900 }) await page.getByLabel('正文', { exact: true }).fill('A replacement body.\n') await page.getByTestId('save-chapter').click() await expect(page.getByTestId('book-notice')).toContainText('已保存为新版本,正在重新处理') diff --git a/learner/e2e/reading.spec.ts b/learner/e2e/reading.spec.ts index dfe7ff1..ac3b3e6 100644 --- a/learner/e2e/reading.spec.ts +++ b/learner/e2e/reading.spec.ts @@ -137,6 +137,18 @@ test('paste English text, watch a chapter finish processing, then read it verbat await expect(page.locator('.lookup-panel')).toContainText('Princeton WordNet') expect(await readerText.evaluate(element => element.textContent)).toBe(pasted) + // The reading and editing surfaces must grow with the window instead of staying at a fixed + // 820px column, and no intermediate width may overflow sideways (the blind spot before this). + for (const width of [768, 1024, 1280]) { + await page.setViewportSize({ width, height: 900 }) + await page.waitForTimeout(60) + const overflow = await page.evaluate(() => ({ scroll: document.documentElement.scrollWidth, inner: window.innerWidth })) + expect(overflow.scroll, `reader must not overflow at ${width}px`).toBeLessThanOrEqual(overflow.inner) + } + await page.setViewportSize({ width: 1280, height: 900 }) + await page.waitForTimeout(60) + const wideBody = await page.locator('.reader-body').boundingBox() + expect(wideBody!.width, 'the text column follows the window').toBeGreaterThan(820) // Browser narrow viewport check only; this is not real-device acceptance. await page.setViewportSize({ width: 390, height: 844 }) await word.click() diff --git a/learner/src/style.css b/learner/src/style.css index 139081f..ec3d31a 100644 --- a/learner/src/style.css +++ b/learner/src/style.css @@ -227,12 +227,12 @@ h1 { font-size: 30px; font-weight: 600; margin: 14px 0; letter-spacing: 1px; } a.chapter-name:hover { color: var(--accent); text-decoration: underline; } .chapter-meta { margin: 4px 0 0; color: var(--text-muted); font-size: 13px; overflow-wrap: anywhere; } /* Reader keeps the pasted text exactly as it was, including line breaks and tabs. */ -.reader-page { max-width: 820px; } +.reader-page { max-width: min(1040px, 92vw); } .chapter-author { margin: -6px 0 0; color: var(--text-muted); font-size: 14px; } .reader-text { white-space: pre-wrap; overflow-wrap: break-word; margin: 26px 0 0; font-family: Georgia, 'Times New Roman', 'Microsoft YaHei', serif; font-size: calc(17px * var(--reader-font-scale)); line-height: 2; } .processing-hint { padding: 36px 0; color: var(--text-muted); } .reader-nav { display: flex; align-items: center; justify-content: space-between; gap: 14px; flex-wrap: wrap; margin-top: 36px; padding-top: 22px; border-top: 1px solid var(--border); } -.reader-page.has-lookup { max-width: 1120px; } +.reader-page.has-lookup { max-width: min(1240px, 96vw); } .reader-workspace { display: grid; grid-template-columns: minmax(0, 1fr); gap: 32px; align-items: start; } .has-lookup .reader-workspace { grid-template-columns: minmax(0, 1fr) 320px; } .reader-body { min-width: 0; } @@ -270,6 +270,9 @@ a.chapter-name:hover { color: var(--accent); text-decoration: underline; } .lookup-term .el-radio-group { margin-top: 8px; flex-wrap: wrap; gap: 4px 12px; } .lookup-term .el-textarea { margin-top: 8px; } .lookup-term .el-textarea textarea { font: inherit; line-height: 1.6; } +/* The chapter body is the main editing surface, so it grows with the window instead of staying + fixed at its rows attribute; the dialog is sized by viewport so a small laptop still fits. */ +.chapter-text-input .el-textarea__inner { min-height: 62vh; line-height: 1.9; } .lookup-saved { color: var(--saved); font-size: 14px; margin: 12px 0 0; } .lookup-actions { display: flex; gap: 8px; margin-top: 14px; flex-wrap: wrap; } .review-page { max-width: 680px; } diff --git a/learner/src/views/BookView.vue b/learner/src/views/BookView.vue index a88cb83..6a23720 100644 --- a/learner/src/views/BookView.vue +++ b/learner/src/views/BookView.vue @@ -398,7 +398,7 @@ onUnmounted(() => library.closeBook()) - +
@@ -414,7 +414,7 @@ onUnmounted(() => library.closeBook()) - +

正在加载…