From 16de17e9c48c9fe1f99310a4dbc4b03e56aaa9d7 Mon Sep 17 00:00:00 2001 From: Yuhan Lei Date: Wed, 3 Jun 2026 09:43:57 +0800 Subject: [PATCH] refactor(app): extract prompt-input edit-load effect into edit-load-effect.ts Slice 3 of the prompt-input.tsx slimming line. Pure extraction, no behavior/DOM/aria/copy/storage-key change. Moves the deferred createEffect that loads an edit/followup draft into the editor (keyed on props.edit?.id) into createEditLoadEffect(deps) (returns void). The factory is called synchronously at the effect's original position so the effect registers in the component owner with { defer: true } intact. props.edit and props.onEditLoaded are injected as editDraft (accessor) and onEditLoaded (thunk wrapping the optional call); editorRef is injected as a getter. The only textual changes are those injection seams: props.edit -> editDraft(), editorRef.focus() -> editorRef().focus(), setCursorPosition(editorRef, ...) -> setCursorPosition(editorRef(), ...), props.onEditLoaded?.() -> onEditLoaded(). The on() source still tracks props.edit?.id, so reactivity is unchanged. prompt-input.tsx 601 -> 572. --- packages/app/src/components/prompt-input.tsx | 47 +++----------- .../prompt-input/edit-load-effect.ts | 63 +++++++++++++++++++ 2 files changed, 72 insertions(+), 38 deletions(-) create mode 100644 packages/app/src/components/prompt-input/edit-load-effect.ts diff --git a/packages/app/src/components/prompt-input.tsx b/packages/app/src/components/prompt-input.tsx index d2faa1893..cb9867b07 100644 --- a/packages/app/src/components/prompt-input.tsx +++ b/packages/app/src/components/prompt-input.tsx @@ -38,6 +38,7 @@ import { ACCEPTED_FILE_TYPES } from "./prompt-input/files" import { promptLength } from "./prompt-input/history" import { createPromptDerivedState } from "./prompt-input/derived-state" import { createPromptCommandsAndMode } from "./prompt-input/commands-mode" +import { createEditLoadEffect } from "./prompt-input/edit-load-effect" import type { PromptStore } from "./prompt-input/store-types" import type { FollowupDraft } from "./prompt-input/followup-draft" import { createPromptSubmit } from "./prompt-input/submit" @@ -273,44 +274,14 @@ export const PromptInput: Component = (props) => { addPart, } = editorInput - createEffect( - on( - () => props.edit?.id, - (id) => { - const edit = props.edit - if (!id || !edit) return - - for (const item of prompt.context.items()) { - prompt.context.remove(item.key) - } - - for (const item of edit.context) { - prompt.context.add({ - type: item.type, - path: item.path, - selection: item.selection, - comment: item.comment, - commentID: item.commentID, - commentOrigin: item.commentOrigin, - preview: item.preview, - }) - } - - setStore("mode", "normal") - setStore("popover", null) - setStore("historyIndex", -1) - setStore("savedPrompt", null) - prompt.set(edit.prompt, promptLength(edit.prompt)) - requestAnimationFrame(() => { - editorRef.focus() - setCursorPosition(editorRef, promptLength(edit.prompt)) - queueScroll() - }) - props.onEditLoaded?.() - }, - { defer: true }, - ), - ) + createEditLoadEffect({ + prompt, + setStore, + editorRef: () => editorRef, + queueScroll, + editDraft: () => props.edit, + onEditLoaded: () => props.onEditLoaded?.(), + }) const { addAttachments, addPickedPaths, removeAttachment, handlePaste } = createPromptAttachments({ editor: () => editorRef, diff --git a/packages/app/src/components/prompt-input/edit-load-effect.ts b/packages/app/src/components/prompt-input/edit-load-effect.ts new file mode 100644 index 000000000..4326582cc --- /dev/null +++ b/packages/app/src/components/prompt-input/edit-load-effect.ts @@ -0,0 +1,63 @@ +// Loads an edit / followup draft into the editor when props.edit changes. +// Extracted from prompt-input.tsx; sets up one deferred createEffect keyed on the +// edit id, so it must be called synchronously inside the component owner. + +import { createEffect, on } from "solid-js" +import type { SetStoreFunction } from "solid-js/store" +import { type Prompt, type usePrompt } from "@/context/prompt" +import { setCursorPosition } from "./editor-dom" +import { promptLength } from "./history" +import type { PromptStore } from "./store-types" +import type { FollowupDraft } from "./followup-draft" + +export interface EditLoadEffectDeps { + prompt: ReturnType + setStore: SetStoreFunction + editorRef: () => HTMLDivElement + queueScroll: () => void + editDraft: () => { id: string; prompt: Prompt; context: FollowupDraft["context"] } | undefined + onEditLoaded: () => void +} + +export function createEditLoadEffect(deps: EditLoadEffectDeps): void { + const { prompt, setStore, editorRef, queueScroll, editDraft, onEditLoaded } = deps + + createEffect( + on( + () => editDraft()?.id, + (id) => { + const edit = editDraft() + if (!id || !edit) return + + for (const item of prompt.context.items()) { + prompt.context.remove(item.key) + } + + for (const item of edit.context) { + prompt.context.add({ + type: item.type, + path: item.path, + selection: item.selection, + comment: item.comment, + commentID: item.commentID, + commentOrigin: item.commentOrigin, + preview: item.preview, + }) + } + + setStore("mode", "normal") + setStore("popover", null) + setStore("historyIndex", -1) + setStore("savedPrompt", null) + prompt.set(edit.prompt, promptLength(edit.prompt)) + requestAnimationFrame(() => { + editorRef().focus() + setCursorPosition(editorRef(), promptLength(edit.prompt)) + queueScroll() + }) + onEditLoaded() + }, + { defer: true }, + ), + ) +}