-
Notifications
You must be signed in to change notification settings - Fork 10.3k
fix web clone prompt seed across type switches #7140
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -196,6 +196,10 @@ export interface ActivePlugin { | |
| // legitimately equal the chip's default plugin id (e.g. the prototype rail's | ||
| // `example-web-prototype`). | ||
| explicitPick: boolean; | ||
| // Temporary text inserted by a type tab rather than authored by the user. | ||
| // Persist it with the chip so a remount does not convert mode UI into a | ||
| // user-owned draft. | ||
| promptSeedKind?: 'web-clone' | null; | ||
| } | ||
|
|
||
| // `inlineBacked` distinguishes a context inserted as an inline `@mention` pill | ||
|
|
@@ -352,6 +356,7 @@ interface HomeComposerChipDraft { | |
| pluginId: string; | ||
| projectKind: ProjectKind | null; | ||
| prototypeSubtypeId?: string | null; | ||
| promptSeedKind?: 'web-clone' | null; | ||
| } | ||
| // `EntryShell` keeps `HomeView` permanently mounted and toggles it with CSS | ||
| // visibility instead of unmounting it on every Home/Community/... view | ||
|
|
@@ -426,6 +431,7 @@ function readHomeComposerChipDraft(): HomeComposerChipDraft | null { | |
| pluginId: parsed.pluginId, | ||
| projectKind: typeof parsed.projectKind === 'string' ? (parsed.projectKind as ProjectKind) : null, | ||
| prototypeSubtypeId: parsedPrototypeSubtype?.slug ?? legacyPrototypeSubtype?.slug ?? null, | ||
| promptSeedKind: parsed.promptSeedKind === 'web-clone' ? 'web-clone' : null, | ||
| }; | ||
| } catch { | ||
| return null; | ||
|
|
@@ -459,6 +465,23 @@ export function seedHomeComposerPrompt(prompt: string): void { | |
| } | ||
| } | ||
|
|
||
| function shouldClearWebClonePromptSeedOnTypeSwitch({ | ||
| activeChipId, | ||
| nextChipId, | ||
| promptEditedByUser, | ||
| promptSeedKind, | ||
| }: { | ||
| activeChipId: string | null; | ||
| nextChipId: string; | ||
| promptEditedByUser: boolean; | ||
| promptSeedKind: ActivePlugin['promptSeedKind']; | ||
| }): boolean { | ||
| return activeChipId === 'web-clone' | ||
| && nextChipId !== 'web-clone' | ||
| && !promptEditedByUser | ||
| && promptSeedKind === 'web-clone'; | ||
| } | ||
|
|
||
| export function HomeView({ | ||
| isActive = true, | ||
| projects, | ||
|
|
@@ -644,6 +667,15 @@ export function HomeView({ | |
| }; | ||
| } | ||
| const restoredDraft = restoredDraftRef.current; | ||
| // Upgrade cleanup for drafts produced by the old behavior: after a user | ||
| // switched away from Website clone, the chip draft already named the new | ||
| // type while the untouched Website-clone scaffold remained in the prompt. | ||
| // That exact impossible pairing is safe to discard on the first fixed load. | ||
| const restoredPrompt = pendingChipRestore?.chipId | ||
| && pendingChipRestore.chipId !== 'web-clone' | ||
| && restoredDraft.prompt === t('homeHero.chip.webClonePromptSeed') | ||
| ? '' | ||
| : restoredDraft.prompt; | ||
| const [designSystemId, setDesignSystemId] = useState<string | null>(() => | ||
| restoredDraft.designSystemId ?? | ||
| homeDefaultDesignSystemId(designSystems, defaultDesignSystemId), | ||
|
|
@@ -679,11 +711,20 @@ export function HomeView({ | |
| }, []); | ||
| const [mcpServers, setMcpServers] = useState<McpServerConfig[]>([]); | ||
| const [mcpLoading, setMcpLoading] = useState(true); | ||
| const [prompt, setPrompt] = useState(() => restoredDraft.prompt); | ||
| const [prompt, setPrompt] = useState(() => restoredPrompt); | ||
| // Treat a restored non-empty prompt as user-edited so the plugin/skill | ||
| // replacement guard still asks before clobbering it. | ||
| // replacement guard still asks before clobbering it. The Website-clone | ||
| // scaffold is mode UI, not a user draft; the exact-text fallback migrates | ||
| // drafts saved before promptSeedKind was persisted. | ||
| const [promptEditedByUser, setPromptEditedByUser] = useState( | ||
| () => restoredDraft.prompt.trim().length > 0, | ||
| () => restoredPrompt.trim().length > 0 | ||
| && !( | ||
| pendingChipRestore?.chipId === 'web-clone' | ||
| && ( | ||
| pendingChipRestore.promptSeedKind === 'web-clone' | ||
| || restoredPrompt === t('homeHero.chip.webClonePromptSeed') | ||
| ) | ||
| ), | ||
| ); | ||
| // Persist the composer draft on every change so it survives the unmount that | ||
| // a tab switch triggers (see the module note above). Empty values clear the | ||
|
|
@@ -716,6 +757,7 @@ export function HomeView({ | |
| ...(active.prototypeSubtypeId | ||
| ? { prototypeSubtypeId: active.prototypeSubtypeId } | ||
| : {}), | ||
| ...(active.promptSeedKind ? { promptSeedKind: active.promptSeedKind } : {}), | ||
| } | ||
| : null, | ||
| ); | ||
|
|
@@ -1391,6 +1433,7 @@ export function HomeView({ | |
| // or Community card / detail modal) rather than a type chip's default | ||
| // plugin. Stored on `active.explicitPick`; gates the chip's clear button. | ||
| explicitPick?: boolean; | ||
| promptSeedKind?: ActivePlugin['promptSeedKind']; | ||
| }, | ||
| // Resolves true when the bound plugin left the composer submittable | ||
| // (inputs valid, apply not failed/superseded) — callers use this to | ||
|
|
@@ -1460,6 +1503,7 @@ export function HomeView({ | |
| preserveInputFields: options?.preserveInputFields === true, | ||
| suppressPromptSync: suppressPromptUpdate, | ||
| explicitPick: options?.explicitPick === true, | ||
| promptSeedKind: options?.promptSeedKind ?? null, | ||
| }); | ||
| setFallbackProjectKind(null); | ||
| setFallbackProjectMetadata(null); | ||
|
|
@@ -1611,6 +1655,7 @@ export function HomeView({ | |
| replaceWithoutConfirmation?: boolean; | ||
| suppressPromptUpdate?: boolean; | ||
| deferApply?: boolean; | ||
| promptSeedKind?: ActivePlugin['promptSeedKind']; | ||
| }, | ||
| ) { | ||
| const inputFields = options?.inputFields ?? record.manifest?.od?.inputs ?? []; | ||
|
|
@@ -1833,6 +1878,13 @@ export function HomeView({ | |
| ? findChip(restore.chipId) | ||
| : null; | ||
| const restoredAction = restoredActionChip?.action; | ||
| const restoredPromptSeedKind = restore.promptSeedKind | ||
| ?? ( | ||
| restore.chipId === 'web-clone' | ||
| && prompt === t('homeHero.chip.webClonePromptSeed') | ||
| ? 'web-clone' | ||
| : null | ||
| ); | ||
| requestActivePlugin(record, undefined, { | ||
| chipId: restore.chipId ?? undefined, | ||
| prototypeSubtypeId: restoredSubtype?.slug ?? null, | ||
|
|
@@ -1848,6 +1900,7 @@ export function HomeView({ | |
| replaceWithoutConfirmation: true, | ||
| suppressPromptUpdate: true, | ||
| deferApply: true, | ||
| promptSeedKind: restoredPromptSeedKind, | ||
| }); | ||
| // eslint-disable-next-line react-hooks/exhaustive-deps | ||
| }, [pendingChipRestore, pluginsLoading, plugins, active, pendingPluginUseHandoff]); | ||
|
|
@@ -2030,6 +2083,9 @@ export function HomeView({ | |
| function handlePromptChange(nextPrompt: string) { | ||
| setPrompt(nextPrompt); | ||
| setPromptEditedByUser(true); | ||
| if (active?.promptSeedKind) { | ||
| setActive({ ...active, promptSeedKind: null }); | ||
|
Comment on lines
+2114
to
+2115
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Clear seed ownership for programmatic prompt replacements too. These changed lines revoke |
||
| } | ||
| if (!active?.queryTemplate) return; | ||
| const extracted = extractPluginInputsFromPrompt( | ||
| active.queryTemplate, | ||
|
|
@@ -2442,6 +2498,20 @@ export function HomeView({ | |
| ); | ||
| return; | ||
| } | ||
| // Website clone is the only type tab that inserts a scaffold directly | ||
| // into an empty composer. Treat that exact untouched scaffold as mode | ||
| // UI, not as a user draft, so it cannot leak into the next type tab. | ||
| // Any edit (including typing the same text manually) transfers | ||
| // ownership to the user and keeps the normal draft-preservation rule. | ||
| if (shouldClearWebClonePromptSeedOnTypeSwitch({ | ||
| activeChipId: active?.chipId ?? null, | ||
| nextChipId: activeChipId, | ||
| promptEditedByUser, | ||
| promptSeedKind: active?.promptSeedKind ?? null, | ||
| })) { | ||
| setPrompt(''); | ||
| setPromptEditedByUser(false); | ||
| } | ||
| const mediaSurface = homeMediaSurfaceForChipId(chip.id); | ||
| if (mediaSurface) { | ||
| const composer = buildHomeMediaComposer( | ||
|
|
@@ -2504,6 +2574,7 @@ export function HomeView({ | |
| ...pluginOptions, | ||
| suppressPromptUpdate: promptSeed === null, | ||
| deferApply: true, | ||
| promptSeedKind: promptSeed === null ? null : 'web-clone', | ||
| }); | ||
| } else { | ||
| requestActivePlugin(record, undefined, pluginOptions); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Revoke a stale scaffold marker before treating every restored prompt as system-owned. The existing
🔁 Powered by Looper · runner=reviewer · agent=codex · An autonomous AI dev team for your GitHub repos.HOME_COMPOSER_SEED_EVENTpath atonSeedreplaces the prompt and marks it edited, but it does not clearactive.promptSeedKind; the persistence effect therefore saves{ chipId: 'web-clone', promptSeedKind: 'web-clone' }beside the handed-off prompt. After a real unmount/remount, this branch marks that arbitrary non-empty prompt unedited solely because the marker exists, and switching away from Website clone then clears it. This loses the prompt passed byseedHomeComposerPrompt, a documented cross-surface flow. ClearpromptSeedKindwhenever a non-scaffold prompt replaces the seed (including the live seed event), or centralize prompt replacement so the marker can only remain while the prompt is exactly the scaffold. Add a regression that selects Website clone, dispatches a different live seed, unmounts/remounts, switches to Prototype, and verifies the handed-off prompt remains.