[feat] dreamverse: main-UI steering mode switch + first-segment-only seeding

Add a user-facing Auto rollout / Steering segmented control to the main composer (not just
devtools), wired to manualContinuationMode and authoritative at session start. In steering
mode a preset seeds only its first segment and auto/loop are forced off, so the backend waits
for the user to describe each subsequent scene by hand.
This commit is contained in:
alexzms
2026-07-15 23:20:22 +00:00
committed by Will Lin
parent 1bc7ff79e6
commit cb8be0d3f3
2 changed files with 69 additions and 5 deletions
+27 -5
View File
@@ -237,6 +237,7 @@ export default function Page() {
enhancementEnabled,
promptExtensionError,
autoExtensionEnabled,
manualContinuationMode,
autoExtensionTimeoutHint,
loopGenerationEnabled,
generationPaused,
@@ -1555,6 +1556,14 @@ export default function Page() {
);
}
// Steering (manual continuation) vs the automatic 6-segment rollout — a pre-session
// preference honored when the session starts.
function handleManualContinuationToggle(event: any) {
sessionStore.patch({
manualContinuationMode: Boolean(event.currentTarget.checked),
});
}
function handleLoopGenerationToggle(event: any) {
sessionStore.patch({
loopGenerationEnabled: Boolean(event.currentTarget.checked),
@@ -1769,7 +1778,14 @@ export default function Page() {
}
function buildProjectInitPayload(type: "session_init_v2" | "project_init_v1") {
const segmentPrompts = getSessionInitPrompts();
const manualMode = Boolean(sessionStore.get().manualContinuationMode);
let segmentPrompts = getSessionInitPrompts();
// Steering mode: a preset/rollout only seeds the FIRST segment; the user
// drives every subsequent segment by hand. Force auto/loop off so the
// backend waits for the user's next prompt instead of auto-continuing.
if (manualMode) {
segmentPrompts = segmentPrompts.slice(0, 1);
}
setSeedPrompts(segmentPrompts);
return {
type,
@@ -1780,9 +1796,9 @@ export default function Page() {
initial_image: null,
single_clip_mode: false,
enhancement_enabled: sessionStore.get().enhancementEnabled,
auto_extension_enabled: sessionStore.get().autoExtensionEnabled,
loop_generation_enabled: sessionStore.get().loopGenerationEnabled,
manual_continuation_mode: sessionStore.get().manualContinuationMode,
auto_extension_enabled: manualMode ? false : sessionStore.get().autoExtensionEnabled,
loop_generation_enabled: manualMode ? false : sessionStore.get().loopGenerationEnabled,
manual_continuation_mode: manualMode,
};
}
@@ -1962,7 +1978,9 @@ export default function Page() {
const initialPrompt = normalizeInitialPrompt(sessionStore.get().livePromptDraft as string);
pendingInitialPromptRef.current = initialPrompt;
const rCAR = !uiStore.get().devtoolsMode && !uiStore.get().demoMode;
const nextManualContinuationMode = Boolean(initialPrompt) && getSessionInitPrompts().length === 0;
// The "Steering mode" toggle is authoritative: checked = manual per-segment steering,
// unchecked = automatic 6-segment rollout (default).
const nextManualContinuationMode = Boolean(sessionStore.get().manualContinuationMode);
sessionStore.patch({
sessionNotice: "",
sessionExpired: false,
@@ -2513,6 +2531,7 @@ export default function Page() {
selectedPresetId={selectedPresetId as string}
enhancementEnabled={enhancementEnabled as boolean}
autoExtensionEnabled={autoExtensionEnabled as boolean}
manualContinuationEnabled={manualContinuationMode as boolean}
loopGenerationEnabled={loopGenerationEnabled as boolean}
canJoinSession={canJoinSession as boolean}
canSubmitContinuation={canSubmitContinuation}
@@ -2525,6 +2544,7 @@ export default function Page() {
onEnhancementToggle={handleEnhancementToggle}
onCuratedPromptLimitChange={handleCuratedPromptLimitChange}
onAutoExtensionToggle={handleAutoExtensionToggle}
onManualContinuationToggle={handleManualContinuationToggle}
onLoopToggle={handleLoopGenerationToggle}
onJoin={joinSession}
onLeave={leaveSession}
@@ -2794,6 +2814,8 @@ export default function Page() {
isGenerating={loadingAnimation as boolean}
storyPresets={storyPresets as any[]}
continuationDraft={livePromptDraft as string}
manualContinuationEnabled={manualContinuationMode as boolean}
onModeChange={(manual) => sessionStore.patch({ manualContinuationMode: manual })}
canJoinSession={canStartSession}
canSubmitContinuation={canSubmitContinuation}
sessionExpired={sessionExpired as boolean}
@@ -23,6 +23,8 @@ interface Props {
projectResetPending?: boolean;
viewingReadOnly?: boolean;
waitingForSegmentPrompt?: boolean;
manualContinuationEnabled?: boolean;
onModeChange?: (manual: boolean) => void;
onPresetGenerate?: (presetId: string) => void;
onContinuationInput?: (e: React.ChangeEvent<HTMLTextAreaElement>) => void;
onContinuationKeydown?: (e: React.KeyboardEvent<HTMLTextAreaElement>) => void;
@@ -48,6 +50,8 @@ export default function ChatBar({
projectResetPending = false,
viewingReadOnly = false,
waitingForSegmentPrompt = false,
manualContinuationEnabled = false,
onModeChange = () => {},
onPresetGenerate = () => {},
onContinuationInput = () => {},
onContinuationKeydown = () => {},
@@ -356,6 +360,44 @@ export default function ChatBar({
</div>
)}
{!sessionStarted && (
<div className="flex flex-col items-center gap-1.5">
<div className="inline-flex rounded-full border border-input bg-card/65 p-0.5 shadow-sm backdrop-blur-sm">
<button
type="button"
onClick={() => onModeChange(false)}
aria-pressed={!manualContinuationEnabled}
className={cn(
"rounded-full px-4 py-1 text-xs font-medium transition-colors",
!manualContinuationEnabled
? "bg-primary text-primary-foreground shadow-sm"
: "text-muted-foreground hover:text-foreground",
)}
>
Auto rollout
</button>
<button
type="button"
onClick={() => onModeChange(true)}
aria-pressed={manualContinuationEnabled}
className={cn(
"rounded-full px-4 py-1 text-xs font-medium transition-colors",
manualContinuationEnabled
? "bg-primary text-primary-foreground shadow-sm"
: "text-muted-foreground hover:text-foreground",
)}
>
Steering
</button>
</div>
<p className="text-[11px] text-muted-foreground">
{manualContinuationEnabled
? "Drive each scene yourself — type what happens next, one segment at a time."
: "Automatic 6-segment story rollout."}
</p>
</div>
)}
<div
className={cn(
"flex min-w-0 items-center gap-1.5 rounded-4xl border py-2.5 pl-5 pr-2.5 shadow-md backdrop-blur-sm transition-all duration-200",