diff --git a/src/components/__tests__/GenerateImageNode.test.tsx b/src/components/__tests__/GenerateImageNode.test.tsx
index 40689faa..0c485162 100644
--- a/src/components/__tests__/GenerateImageNode.test.tsx
+++ b/src/components/__tests__/GenerateImageNode.test.tsx
@@ -362,6 +362,46 @@ describe("GenerateImageNode", () => {
expect(screen.getByTitle("Next image")).toBeInTheDocument();
});
+ it("should hide carousel controls when generations path is not configured", () => {
+ mockUseWorkflowStore.mockImplementation((selector) => {
+ const state = {
+ updateNodeData: mockUpdateNodeData,
+ regenerateNode: mockRegenerateNode,
+ addNode: mockAddNode,
+ incrementModalCount: mockIncrementModalCount,
+ decrementModalCount: mockDecrementModalCount,
+ providerSettings: defaultProviderSettings,
+ generationsPath: null,
+ isRunning: false,
+ currentNodeIds: [],
+ groups: {},
+ nodes: [],
+ recentModels: [],
+ trackModelUsage: vi.fn(),
+ getNodesWithComments: vi.fn(() => []),
+ markCommentViewed: vi.fn(),
+ setNavigationTarget: vi.fn(),
+ };
+ return selector(state);
+ });
+
+ render(
+
+
+
+ );
+
+ expect(screen.queryByTitle("Previous image")).not.toBeInTheDocument();
+ expect(screen.queryByTitle("Next image")).not.toBeInTheDocument();
+ });
+
it("should show current position in carousel", () => {
render(
diff --git a/src/components/nodes/GenerateImageNode.tsx b/src/components/nodes/GenerateImageNode.tsx
index 08c4011e..e72c5c8b 100644
--- a/src/components/nodes/GenerateImageNode.tsx
+++ b/src/components/nodes/GenerateImageNode.tsx
@@ -335,7 +335,6 @@ export function GenerateImageNode({ id, data, selected }: NodeProps {
if (!generationsPath) {
- console.error("Generations path not configured");
return null;
}
@@ -445,7 +444,7 @@ export function GenerateImageNode({ id, data, selected }: NodeProps 1;
+ const hasCarouselImages = Boolean(generationsPath) && (nodeData.imageHistory || []).length > 1;
// Count visible Gemini controls to match ModelParameters grid/max-width rules
const geminiControlCount = 2 // Model + Aspect Ratio (always)
diff --git a/src/store/execution/__tests__/nanoBananaExecutor.test.ts b/src/store/execution/__tests__/nanoBananaExecutor.test.ts
index b63d265b..6f72a2fc 100644
--- a/src/store/execution/__tests__/nanoBananaExecutor.test.ts
+++ b/src/store/execution/__tests__/nanoBananaExecutor.test.ts
@@ -216,7 +216,8 @@ describe("executeNanoBanana", () => {
const body = JSON.parse(mockFetch.mock.calls[0][1].body);
expect(body.prompt).toContain("right side key light");
expect(body.prompt).toContain("70% brightness");
- expect(body.prompt).toContain("Preserve the original subject identity");
+ expect(body.prompt).toContain("same exact person");
+ expect(body.prompt).toContain("Only change illumination");
});
it("uses NewApiWG default for legacy image nodes when Gemini is not configured", async () => {
diff --git a/src/utils/__tests__/lighting.test.ts b/src/utils/__tests__/lighting.test.ts
index 25f6a76b..16e4729a 100644
--- a/src/utils/__tests__/lighting.test.ts
+++ b/src/utils/__tests__/lighting.test.ts
@@ -38,10 +38,13 @@ describe("lighting utilities", () => {
mainLightDirection: "front",
});
- expect(prompt).toContain("Image editing task");
+ expect(prompt).toContain("STRICT image-to-image relighting task");
expect(prompt).toContain("uploaded image");
expect(prompt).toContain("relight the uploaded image");
- expect(prompt).toContain("Only change the lighting");
- expect(prompt).toContain("Do not add new people or objects");
+ expect(prompt).toContain("same exact person");
+ expect(prompt).toContain("Preserve the original composition");
+ expect(prompt).toContain("Only change illumination");
+ expect(prompt).toContain("lighting reference only");
+ expect(prompt).toContain("Do not add or remove people or objects");
});
});
diff --git a/src/utils/lighting.ts b/src/utils/lighting.ts
index 6d3fc96f..40c5f5d8 100644
--- a/src/utils/lighting.ts
+++ b/src/utils/lighting.ts
@@ -27,7 +27,7 @@ export const LIGHTING_PRESETS: LightingPreset[] = [
mainLightDirection: "front",
lightPosition: { x: 0, y: 0, z: 1 },
beamAngle: 45,
- smartPrompt: "柯达胶片质感",
+ smartPrompt: "柯达胶片感的曝光和柔和高光,只调整打光",
},
},
{
@@ -71,7 +71,7 @@ export const LIGHTING_PRESETS: LightingPreset[] = [
lightPosition: { x: 0, y: 0, z: 1 },
beamAngle: 45,
rimLight: false,
- smartPrompt: "让画面拥有《银翼杀手2045》同款打光,极简背景",
+ smartPrompt: "《银翼杀手2045》同款青紫高反差打光,只调整光影",
},
},
{
@@ -113,7 +113,7 @@ export const LIGHTING_PRESETS: LightingPreset[] = [
mainLightDirection: "front",
lightPosition: { x: 0, y: 0, z: 1 },
beamAngle: 45,
- smartPrompt: "让画面光影变成“黄金时刻”",
+ smartPrompt: "黄金时刻暖色光影,只调整光照和阴影",
},
},
{
@@ -127,7 +127,7 @@ export const LIGHTING_PRESETS: LightingPreset[] = [
mainLightDirection: "front",
lightPosition: { x: 0, y: 0, z: 1 },
beamAngle: 45,
- smartPrompt: "让画面质感变成清晨同款冷灰色调",
+ smartPrompt: "清晨冷灰色温和柔和阴影,只调整光照和色温",
},
},
];
@@ -236,11 +236,13 @@ export function buildOptimizedLightingPrompt(
: "User intent: relight the uploaded image according to the lighting plan.";
return [
- "Image editing task: use the uploaded image as the source image and generate one finished relit image.",
+ "STRICT image-to-image relighting task: use the uploaded image as the locked source image.",
userIntent,
lightingPrompt,
- "Preserve the original subject identity, pose, clothing, composition, camera angle, and image style as much as possible.",
- "Only change the lighting, shadow direction, highlight intensity, and ambient mood required by the lighting plan.",
- "Do not add new people or objects, do not crop the subject, do not change the character design, and do not create a different scene.",
+ "The output must show the same exact person from the source image: identical face identity, facial features, age, expression, hair, body shape, hands, pose, clothing, accessories, and camera/phone.",
+ "Preserve the original composition, camera angle, crop, background, object layout, readable text overlays, and image style.",
+ "Only change illumination: light direction, brightness, color temperature, shadows, highlights, reflections, and ambient lighting mood required by the lighting plan.",
+ "Treat any preset or style wording as a lighting reference only, not permission to redesign the person, outfit, background, pose, scene, or photographic composition.",
+ "Do not add or remove people or objects, do not beautify or re-age the person, do not replace the face, do not change hairstyle or clothing, do not crop the subject, and do not create a different scene.",
].join("\n");
}