@umituz/react-native-ai-generation-content 1.17.166 → 1.17.168
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/domains/creations/presentation/hooks/useCreationPersistence.ts +49 -3
- package/src/domains/prompts/domain/entities/image-prompt-segments.ts +119 -0
- package/src/domains/prompts/index.ts +33 -1
- package/src/domains/prompts/infrastructure/services/ImagePromptBuilder.ts +200 -0
package/package.json
CHANGED
|
@@ -10,6 +10,8 @@ import { useAuth } from "@umituz/react-native-auth";
|
|
|
10
10
|
import { createCreationsRepository } from "../../infrastructure/adapters";
|
|
11
11
|
import type { Creation } from "../../domain/entities/Creation";
|
|
12
12
|
|
|
13
|
+
declare const __DEV__: boolean;
|
|
14
|
+
|
|
13
15
|
/**
|
|
14
16
|
* Configuration for creation persistence
|
|
15
17
|
*/
|
|
@@ -70,13 +72,24 @@ export function useCreationPersistence(
|
|
|
70
72
|
|
|
71
73
|
const onProcessingStart = useCallback(
|
|
72
74
|
<T extends BaseProcessingStartData>(data: T) => {
|
|
73
|
-
if (
|
|
75
|
+
if (__DEV__) {
|
|
76
|
+
console.log("[useCreationPersistence] onProcessingStart called", { type, userId, data });
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (!userId) {
|
|
80
|
+
if (__DEV__) console.log("[useCreationPersistence] No userId, skipping");
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
74
83
|
|
|
75
84
|
const { creationId, ...rest } = data;
|
|
76
85
|
const cleanMetadata = Object.fromEntries(
|
|
77
86
|
Object.entries(rest).filter(([, v]) => v !== undefined && v !== null),
|
|
78
87
|
);
|
|
79
88
|
|
|
89
|
+
if (__DEV__) {
|
|
90
|
+
console.log("[useCreationPersistence] cleanMetadata", cleanMetadata);
|
|
91
|
+
}
|
|
92
|
+
|
|
80
93
|
const creation: Creation = {
|
|
81
94
|
id: creationId,
|
|
82
95
|
uri: "",
|
|
@@ -88,6 +101,10 @@ export function useCreationPersistence(
|
|
|
88
101
|
metadata: cleanMetadata,
|
|
89
102
|
};
|
|
90
103
|
|
|
104
|
+
if (__DEV__) {
|
|
105
|
+
console.log("[useCreationPersistence] Creating document", { creationId, type });
|
|
106
|
+
}
|
|
107
|
+
|
|
91
108
|
repository.create(userId, creation);
|
|
92
109
|
queryClient.invalidateQueries({ queryKey: ["creations"] });
|
|
93
110
|
},
|
|
@@ -96,7 +113,18 @@ export function useCreationPersistence(
|
|
|
96
113
|
|
|
97
114
|
const onProcessingComplete = useCallback(
|
|
98
115
|
<T extends BaseProcessingResult>(result: T) => {
|
|
99
|
-
if (
|
|
116
|
+
if (__DEV__) {
|
|
117
|
+
console.log("[useCreationPersistence] onProcessingComplete called", {
|
|
118
|
+
creationId: result.creationId,
|
|
119
|
+
hasImageUrl: !!result.imageUrl,
|
|
120
|
+
hasVideoUrl: !!result.videoUrl,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (!userId || !result.creationId) {
|
|
125
|
+
if (__DEV__) console.log("[useCreationPersistence] Missing userId or creationId, skipping");
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
100
128
|
|
|
101
129
|
const uri = result.imageUrl || result.videoUrl || "";
|
|
102
130
|
const output = result.imageUrl
|
|
@@ -105,6 +133,13 @@ export function useCreationPersistence(
|
|
|
105
133
|
? { videoUrl: result.videoUrl }
|
|
106
134
|
: undefined;
|
|
107
135
|
|
|
136
|
+
if (__DEV__) {
|
|
137
|
+
console.log("[useCreationPersistence] Updating document to completed", {
|
|
138
|
+
creationId: result.creationId,
|
|
139
|
+
uri: uri.substring(0, 50) + "...",
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
|
|
108
143
|
repository.update(userId, result.creationId, {
|
|
109
144
|
uri,
|
|
110
145
|
status: "completed",
|
|
@@ -117,7 +152,18 @@ export function useCreationPersistence(
|
|
|
117
152
|
|
|
118
153
|
const onError = useCallback(
|
|
119
154
|
(error: string, creationId?: string) => {
|
|
120
|
-
if (
|
|
155
|
+
if (__DEV__) {
|
|
156
|
+
console.log("[useCreationPersistence] onError called", { error, creationId });
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (!userId || !creationId) {
|
|
160
|
+
if (__DEV__) console.log("[useCreationPersistence] Missing userId or creationId, skipping");
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (__DEV__) {
|
|
165
|
+
console.log("[useCreationPersistence] Updating document to failed", { creationId });
|
|
166
|
+
}
|
|
121
167
|
|
|
122
168
|
repository.update(userId, creationId, {
|
|
123
169
|
status: "failed",
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Image Prompt Segments
|
|
3
|
+
* Reusable building blocks for composing AI image generation prompts
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// =============================================================================
|
|
7
|
+
// IDENTITY PRESERVATION
|
|
8
|
+
// =============================================================================
|
|
9
|
+
|
|
10
|
+
export const IDENTITY_SEGMENTS = {
|
|
11
|
+
samePerson: "same person",
|
|
12
|
+
preserveIdentity: "preserve identity",
|
|
13
|
+
preserveGender: "preserve original gender",
|
|
14
|
+
preserveFace: "preserve original face structure",
|
|
15
|
+
preserveHair: "preserve original hair color and style",
|
|
16
|
+
preserveEyes: "preserve original eye color",
|
|
17
|
+
sameFeatures: "same facial features",
|
|
18
|
+
maintainLikeness: "maintain likeness",
|
|
19
|
+
} as const;
|
|
20
|
+
|
|
21
|
+
export const IDENTITY_NEGATIVE_SEGMENTS = {
|
|
22
|
+
genderSwap: "gender swap",
|
|
23
|
+
differentPerson: "different person",
|
|
24
|
+
wrongGender: "wrong gender",
|
|
25
|
+
changedIdentity: "changed identity",
|
|
26
|
+
} as const;
|
|
27
|
+
|
|
28
|
+
// =============================================================================
|
|
29
|
+
// ANIME STYLE
|
|
30
|
+
// =============================================================================
|
|
31
|
+
|
|
32
|
+
export const ANIME_STYLE_SEGMENTS = {
|
|
33
|
+
base: "2D anime illustration",
|
|
34
|
+
japaneseStyle: "japanese anime art style",
|
|
35
|
+
celShaded: "cel-shaded anime character",
|
|
36
|
+
animeEyes: "large sparkling anime eyes with detailed iris",
|
|
37
|
+
animeSkin: "smooth cel-shaded skin with subtle anime blush",
|
|
38
|
+
animeHair: "stylized anime hair with highlights",
|
|
39
|
+
ghibli: "Studio Ghibli inspired",
|
|
40
|
+
vibrantColors: "vibrant anime colors",
|
|
41
|
+
cleanLineart: "clean anime lineart",
|
|
42
|
+
professionalPortrait: "professional anime portrait",
|
|
43
|
+
} as const;
|
|
44
|
+
|
|
45
|
+
// =============================================================================
|
|
46
|
+
// QUALITY
|
|
47
|
+
// =============================================================================
|
|
48
|
+
|
|
49
|
+
export const QUALITY_SEGMENTS = {
|
|
50
|
+
highQuality: "high quality",
|
|
51
|
+
detailed: "highly detailed",
|
|
52
|
+
sharp: "sharp focus",
|
|
53
|
+
professional: "professional",
|
|
54
|
+
masterpiece: "masterpiece",
|
|
55
|
+
bestQuality: "best quality",
|
|
56
|
+
} as const;
|
|
57
|
+
|
|
58
|
+
export const QUALITY_NEGATIVE_SEGMENTS = {
|
|
59
|
+
lowQuality: "low quality",
|
|
60
|
+
blurry: "blurry",
|
|
61
|
+
pixelated: "pixelated",
|
|
62
|
+
artifacts: "jpeg artifacts",
|
|
63
|
+
watermark: "watermark",
|
|
64
|
+
signature: "signature",
|
|
65
|
+
} as const;
|
|
66
|
+
|
|
67
|
+
// =============================================================================
|
|
68
|
+
// REALISM AVOIDANCE (for stylized outputs)
|
|
69
|
+
// =============================================================================
|
|
70
|
+
|
|
71
|
+
export const ANTI_REALISM_SEGMENTS = {
|
|
72
|
+
photorealistic: "photorealistic",
|
|
73
|
+
realisticPhoto: "realistic photo",
|
|
74
|
+
render3D: "3D render",
|
|
75
|
+
hyperrealistic: "hyperrealistic",
|
|
76
|
+
realPerson: "real person",
|
|
77
|
+
naturalSkin: "natural skin texture",
|
|
78
|
+
pores: "pores",
|
|
79
|
+
wrinkles: "wrinkles",
|
|
80
|
+
} as const;
|
|
81
|
+
|
|
82
|
+
// =============================================================================
|
|
83
|
+
// ANATOMY NEGATIVE
|
|
84
|
+
// =============================================================================
|
|
85
|
+
|
|
86
|
+
export const ANATOMY_NEGATIVE_SEGMENTS = {
|
|
87
|
+
deformedFace: "deformed face",
|
|
88
|
+
badAnatomy: "bad anatomy",
|
|
89
|
+
extraLimbs: "extra limbs",
|
|
90
|
+
mutatedHands: "mutated hands",
|
|
91
|
+
extraFingers: "extra fingers",
|
|
92
|
+
missingFingers: "missing fingers",
|
|
93
|
+
} as const;
|
|
94
|
+
|
|
95
|
+
// =============================================================================
|
|
96
|
+
// PRESET COLLECTIONS
|
|
97
|
+
// =============================================================================
|
|
98
|
+
|
|
99
|
+
export const PRESET_COLLECTIONS = {
|
|
100
|
+
fullIdentityPreservation: Object.values(IDENTITY_SEGMENTS),
|
|
101
|
+
basicIdentityPreservation: [
|
|
102
|
+
IDENTITY_SEGMENTS.samePerson,
|
|
103
|
+
IDENTITY_SEGMENTS.preserveGender,
|
|
104
|
+
IDENTITY_SEGMENTS.preserveFace,
|
|
105
|
+
],
|
|
106
|
+
animeStyle: Object.values(ANIME_STYLE_SEGMENTS),
|
|
107
|
+
highQuality: Object.values(QUALITY_SEGMENTS),
|
|
108
|
+
antiRealism: Object.values(ANTI_REALISM_SEGMENTS),
|
|
109
|
+
anatomyNegative: Object.values(ANATOMY_NEGATIVE_SEGMENTS),
|
|
110
|
+
identityNegative: Object.values(IDENTITY_NEGATIVE_SEGMENTS),
|
|
111
|
+
} as const;
|
|
112
|
+
|
|
113
|
+
// =============================================================================
|
|
114
|
+
// TYPES
|
|
115
|
+
// =============================================================================
|
|
116
|
+
|
|
117
|
+
export type IdentitySegment = keyof typeof IDENTITY_SEGMENTS;
|
|
118
|
+
export type AnimeStyleSegment = keyof typeof ANIME_STYLE_SEGMENTS;
|
|
119
|
+
export type QualitySegment = keyof typeof QUALITY_SEGMENTS;
|
|
@@ -320,4 +320,36 @@ export type {
|
|
|
320
320
|
UsePromptGenerationActions,
|
|
321
321
|
} from './presentation/hooks/usePromptGeneration';
|
|
322
322
|
|
|
323
|
-
export { usePromptGeneration } from './presentation/hooks/usePromptGeneration';
|
|
323
|
+
export { usePromptGeneration } from './presentation/hooks/usePromptGeneration';
|
|
324
|
+
|
|
325
|
+
// =============================================================================
|
|
326
|
+
// IMAGE PROMPT BUILDING
|
|
327
|
+
// =============================================================================
|
|
328
|
+
|
|
329
|
+
export {
|
|
330
|
+
IDENTITY_SEGMENTS,
|
|
331
|
+
IDENTITY_NEGATIVE_SEGMENTS,
|
|
332
|
+
ANIME_STYLE_SEGMENTS,
|
|
333
|
+
QUALITY_SEGMENTS,
|
|
334
|
+
QUALITY_NEGATIVE_SEGMENTS,
|
|
335
|
+
ANTI_REALISM_SEGMENTS,
|
|
336
|
+
ANATOMY_NEGATIVE_SEGMENTS,
|
|
337
|
+
PRESET_COLLECTIONS,
|
|
338
|
+
} from './domain/entities/image-prompt-segments';
|
|
339
|
+
|
|
340
|
+
export type {
|
|
341
|
+
IdentitySegment,
|
|
342
|
+
AnimeStyleSegment,
|
|
343
|
+
QualitySegment,
|
|
344
|
+
} from './domain/entities/image-prompt-segments';
|
|
345
|
+
|
|
346
|
+
export {
|
|
347
|
+
ImagePromptBuilder,
|
|
348
|
+
createAnimeSelfiePrompt,
|
|
349
|
+
createStyleTransferPrompt,
|
|
350
|
+
} from './infrastructure/services/ImagePromptBuilder';
|
|
351
|
+
|
|
352
|
+
export type {
|
|
353
|
+
ImagePromptResult,
|
|
354
|
+
ImagePromptBuilderOptions,
|
|
355
|
+
} from './infrastructure/services/ImagePromptBuilder';
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ImagePromptBuilder
|
|
3
|
+
* Fluent builder for composing AI image generation prompts
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* const { prompt, negativePrompt } = ImagePromptBuilder.create()
|
|
7
|
+
* .withIdentityPreservation()
|
|
8
|
+
* .withAnimeStyle()
|
|
9
|
+
* .withQuality()
|
|
10
|
+
* .build();
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import {
|
|
14
|
+
IDENTITY_NEGATIVE_SEGMENTS,
|
|
15
|
+
ANIME_STYLE_SEGMENTS,
|
|
16
|
+
QUALITY_SEGMENTS,
|
|
17
|
+
QUALITY_NEGATIVE_SEGMENTS,
|
|
18
|
+
ANTI_REALISM_SEGMENTS,
|
|
19
|
+
ANATOMY_NEGATIVE_SEGMENTS,
|
|
20
|
+
PRESET_COLLECTIONS,
|
|
21
|
+
} from "../../domain/entities/image-prompt-segments";
|
|
22
|
+
|
|
23
|
+
export interface ImagePromptResult {
|
|
24
|
+
prompt: string;
|
|
25
|
+
negativePrompt: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface ImagePromptBuilderOptions {
|
|
29
|
+
separator?: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export class ImagePromptBuilder {
|
|
33
|
+
private positiveSegments: string[] = [];
|
|
34
|
+
private negativeSegments: string[] = [];
|
|
35
|
+
private readonly separator: string;
|
|
36
|
+
|
|
37
|
+
private constructor(options?: ImagePromptBuilderOptions) {
|
|
38
|
+
this.separator = options?.separator ?? ", ";
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Create a new ImagePromptBuilder instance
|
|
43
|
+
*/
|
|
44
|
+
static create(options?: ImagePromptBuilderOptions): ImagePromptBuilder {
|
|
45
|
+
return new ImagePromptBuilder(options);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Add identity preservation prompts
|
|
50
|
+
* Ensures the AI preserves the original person's features
|
|
51
|
+
*/
|
|
52
|
+
withIdentityPreservation(full = true): this {
|
|
53
|
+
const segments = full
|
|
54
|
+
? PRESET_COLLECTIONS.fullIdentityPreservation
|
|
55
|
+
: PRESET_COLLECTIONS.basicIdentityPreservation;
|
|
56
|
+
|
|
57
|
+
this.positiveSegments.push(...segments);
|
|
58
|
+
this.negativeSegments.push(...Object.values(IDENTITY_NEGATIVE_SEGMENTS));
|
|
59
|
+
return this;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Add anime style prompts
|
|
64
|
+
*/
|
|
65
|
+
withAnimeStyle(): this {
|
|
66
|
+
this.positiveSegments.push(...Object.values(ANIME_STYLE_SEGMENTS));
|
|
67
|
+
this.negativeSegments.push(...Object.values(ANTI_REALISM_SEGMENTS));
|
|
68
|
+
return this;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Add quality enhancement prompts
|
|
73
|
+
*/
|
|
74
|
+
withQuality(): this {
|
|
75
|
+
this.positiveSegments.push(...Object.values(QUALITY_SEGMENTS));
|
|
76
|
+
this.negativeSegments.push(...Object.values(QUALITY_NEGATIVE_SEGMENTS));
|
|
77
|
+
return this;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Add anatomy safety negative prompts
|
|
82
|
+
*/
|
|
83
|
+
withAnatomySafety(): this {
|
|
84
|
+
this.negativeSegments.push(...Object.values(ANATOMY_NEGATIVE_SEGMENTS));
|
|
85
|
+
return this;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Add anti-realism prompts (for stylized outputs)
|
|
90
|
+
*/
|
|
91
|
+
withAntiRealism(): this {
|
|
92
|
+
this.negativeSegments.push(...Object.values(ANTI_REALISM_SEGMENTS));
|
|
93
|
+
return this;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Add custom positive segments
|
|
98
|
+
*/
|
|
99
|
+
withSegments(segments: string[]): this {
|
|
100
|
+
this.positiveSegments.push(...segments);
|
|
101
|
+
return this;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Add custom negative segments
|
|
106
|
+
*/
|
|
107
|
+
withNegativeSegments(segments: string[]): this {
|
|
108
|
+
this.negativeSegments.push(...segments);
|
|
109
|
+
return this;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Add a single custom segment
|
|
114
|
+
*/
|
|
115
|
+
withSegment(segment: string): this {
|
|
116
|
+
this.positiveSegments.push(segment);
|
|
117
|
+
return this;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Add a single negative segment
|
|
122
|
+
*/
|
|
123
|
+
withNegativeSegment(segment: string): this {
|
|
124
|
+
this.negativeSegments.push(segment);
|
|
125
|
+
return this;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Prepend segments (add to beginning)
|
|
130
|
+
*/
|
|
131
|
+
prependSegments(segments: string[]): this {
|
|
132
|
+
this.positiveSegments.unshift(...segments);
|
|
133
|
+
return this;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Create a new builder extending this one
|
|
138
|
+
*/
|
|
139
|
+
extend(): ImagePromptBuilder {
|
|
140
|
+
const builder = ImagePromptBuilder.create({ separator: this.separator });
|
|
141
|
+
builder.positiveSegments = [...this.positiveSegments];
|
|
142
|
+
builder.negativeSegments = [...this.negativeSegments];
|
|
143
|
+
return builder;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Build the final prompt strings
|
|
148
|
+
*/
|
|
149
|
+
build(): ImagePromptResult {
|
|
150
|
+
// Remove duplicates and filter empty values
|
|
151
|
+
const uniquePositive = [...new Set(this.positiveSegments)].filter(Boolean);
|
|
152
|
+
const uniqueNegative = [...new Set(this.negativeSegments)].filter(Boolean);
|
|
153
|
+
|
|
154
|
+
return {
|
|
155
|
+
prompt: uniquePositive.join(this.separator),
|
|
156
|
+
negativePrompt: uniqueNegative.join(this.separator),
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Get current positive segments (for debugging)
|
|
162
|
+
*/
|
|
163
|
+
getPositiveSegments(): string[] {
|
|
164
|
+
return [...this.positiveSegments];
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Get current negative segments (for debugging)
|
|
169
|
+
*/
|
|
170
|
+
getNegativeSegments(): string[] {
|
|
171
|
+
return [...this.negativeSegments];
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// =============================================================================
|
|
176
|
+
// PRESET BUILDERS
|
|
177
|
+
// =============================================================================
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Create an anime selfie prompt builder with identity preservation
|
|
181
|
+
*/
|
|
182
|
+
export function createAnimeSelfiePrompt(): ImagePromptResult {
|
|
183
|
+
return ImagePromptBuilder.create()
|
|
184
|
+
.withIdentityPreservation()
|
|
185
|
+
.withAnimeStyle()
|
|
186
|
+
.withAnatomySafety()
|
|
187
|
+
.build();
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Create a style transfer prompt builder with identity preservation
|
|
192
|
+
*/
|
|
193
|
+
export function createStyleTransferPrompt(style: string): ImagePromptResult {
|
|
194
|
+
return ImagePromptBuilder.create()
|
|
195
|
+
.withIdentityPreservation()
|
|
196
|
+
.withSegment(`${style} style`)
|
|
197
|
+
.withQuality()
|
|
198
|
+
.withAnatomySafety()
|
|
199
|
+
.build();
|
|
200
|
+
}
|