Pārlūkot izejas kodu

fix: chapter number override and input box boundary detection

- ingestChapter chapterNumberOverride, handleSaveAsFinal pass chapter number directly

- avoid snapshot chapter number mismatch from file path issues

- chat-input viewport-aware max height to prevent overflow on drag up
Mochocyang 3 mēneši atpakaļ
vecāks
revīzija
14db8f78c1

+ 15 - 0
src/components/chat/chat-input-resize.ts

@@ -1,4 +1,6 @@
 export const DEFAULT_RESIZABLE_INPUT_HEIGHT = 44
+/** 输入框底部预留空间:发送按钮 + padding + 拖拽条 + 模型选择器等 */
+const INPUT_BOTTOM_RESERVED = 80
 
 export interface ResizableInputBounds {
   minHeight: number
@@ -16,6 +18,19 @@ export function resolveResizableInputMaxHeight({
   return Math.max(DEFAULT_RESIZABLE_INPUT_HEIGHT, Math.floor(availableHeight / 2))
 }
 
+/**
+ * 根据输入框在视口中的位置计算最大高度,防止向上拖动时超出软件界面下沿边界。
+ * @param inputTopOffset 输入框顶部距视口顶部的距离(通过 getBoundingClientRect().top 获取)
+ * @param viewportHeight 视口高度(window.innerHeight)
+ */
+export function resolveViewportAwareMaxHeight(
+  inputTopOffset: number,
+  viewportHeight: number,
+): number {
+  const availableSpace = viewportHeight - inputTopOffset - INPUT_BOTTOM_RESERVED
+  return Math.max(DEFAULT_RESIZABLE_INPUT_HEIGHT, Math.floor(availableSpace))
+}
+
 export function clampResizableInputHeight(
   nextHeight: number,
   bounds: ResizableInputBounds,

+ 9 - 6
src/components/chat/chat-input.tsx

@@ -6,7 +6,7 @@ import { useChatStore } from "@/stores/chat-store"
 import {
   clampResizableInputHeight,
   DEFAULT_RESIZABLE_INPUT_HEIGHT,
-  resolveResizableInputMaxHeight,
+  resolveViewportAwareMaxHeight,
 } from "./chat-input-resize"
 
 interface ChatInputProps {
@@ -63,13 +63,16 @@ export function ChatInput({ onSend, onStop, isStreaming, placeholder, leadingCon
   )
 
   const getResizeBounds = useCallback(() => {
-    const panelHeight = resolveResizePanelHeight(rootRef.current)
+    const rootEl = rootRef.current
+    const inputTopOffset = rootEl?.getBoundingClientRect().top ?? 0
+    const viewportHeight = window.innerHeight
+    // 优先使用视口边界感知的最大高度,防止输入框下沿超出软件界面
+    const viewportMax = resolveViewportAwareMaxHeight(inputTopOffset, viewportHeight)
+    const panelHeight = resolveResizePanelHeight(rootEl)
+    const panelMax = panelHeight > 0 ? Math.max(DEFAULT_RESIZABLE_INPUT_HEIGHT, Math.floor(panelHeight / 2)) : viewportMax
     return {
       minHeight: DEFAULT_RESIZABLE_INPUT_HEIGHT,
-      maxHeight: resolveResizableInputMaxHeight({
-        panelHeight,
-        viewportHeight: window.innerHeight,
-      }),
+      maxHeight: Math.min(viewportMax, panelMax),
     }
   }, [])
 

+ 2 - 2
src/components/layout/preview-panel.tsx

@@ -645,7 +645,7 @@ export function PreviewPanel() {
             abortController: ingestAbortController,
           })
           const { ingestChapter } = await import("@/lib/novel/chapter-ingest")
-          const result = await ingestChapter(project.path, targetPath, resolveReviewModel(), ingestAbortController.signal)
+          const result = await ingestChapter(project.path, targetPath, resolveReviewModel(), ingestAbortController.signal, chapterFrontmatter?.chapterNumber as number | undefined)
           useImportProgressStore.getState().finishTask(ingestTaskId, result.snapshot ? "done" : "error", {
             completed: result.snapshot ? 1 : 0,
             total: 1,
@@ -703,7 +703,7 @@ export function PreviewPanel() {
     })
     try {
       const { ingestChapter } = await import("@/lib/novel/chapter-ingest")
-      const result = await ingestChapter(projectPath, filePath, resolveReviewModel(), ingestAbortController.signal)
+      const result = await ingestChapter(projectPath, filePath, resolveReviewModel(), ingestAbortController.signal, chapterFrontmatter?.chapterNumber as number | undefined)
       useImportProgressStore.getState().finishTask(ingestTaskId, result.snapshot ? "done" : "error", {
         completed: result.snapshot ? 1 : 0,
         total: 1,

+ 2 - 1
src/lib/novel/chapter-ingest.ts

@@ -298,6 +298,7 @@ export async function ingestChapter(
   chapterPath: string,
   _reviewModel?: string,
   signal?: AbortSignal,
+  chapterNumberOverride?: number,
 ): Promise<IngestResult> {
   const pp = normalizePath(projectPath)
   const state = useWikiStore.getState()
@@ -318,7 +319,7 @@ export async function ingestChapter(
     return { snapshot: null, failReason: "not_final" }
   }
 
-  const chapterNumber = parseChapterNumber(fm.chapter_number) ?? 0
+  const chapterNumber = chapterNumberOverride ?? parseChapterNumber(fm.chapter_number) ?? 0
   if (chapterNumber <= 0) {
     console.warn("[Chapter Ingest] Invalid chapter number, skipping ingest.")
     return { snapshot: null, failReason: "invalid_chapter_number" }