@umituz/react-native-ai-generation-content 1.61.54 → 1.61.56

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.
Files changed (29) hide show
  1. package/package.json +1 -1
  2. package/src/domains/content-moderation/infrastructure/constants/moderation.constants.ts +9 -0
  3. package/src/domains/content-moderation/infrastructure/services/moderators/image.moderator.ts +2 -4
  4. package/src/domains/content-moderation/infrastructure/services/moderators/text.moderator.ts +4 -89
  5. package/src/domains/content-moderation/infrastructure/services/moderators/video.moderator.ts +2 -4
  6. package/src/domains/content-moderation/infrastructure/utils/content-security.util.ts +39 -0
  7. package/src/domains/content-moderation/infrastructure/utils/prompt-injection.util.ts +49 -0
  8. package/src/domains/content-moderation/infrastructure/utils/validators.util.ts +51 -0
  9. package/src/domains/creations/index.ts +1 -1
  10. package/src/domains/creations/presentation/components/CreationsFilterBar.tsx +1 -1
  11. package/src/domains/creations/presentation/hooks/useGalleryState.ts +74 -0
  12. package/src/domains/creations/presentation/screens/CreationsGalleryScreen.tsx +13 -28
  13. package/src/domains/prompts/infrastructure/services/ImagePromptBuilder.ts +2 -33
  14. package/src/domains/prompts/infrastructure/utils/prompt-creators.util.ts +41 -0
  15. package/src/domains/scenarios/index.ts +2 -2
  16. package/src/infrastructure/utils/api-client.util.ts +205 -0
  17. package/src/infrastructure/utils/error-handling.util.ts +190 -0
  18. package/src/infrastructure/utils/index.ts +4 -0
  19. package/src/infrastructure/utils/type-guards.util.ts +153 -0
  20. package/src/infrastructure/utils/validation.util.ts +188 -0
  21. package/src/presentation/layouts/DualImageFeatureLayout.tsx +8 -27
  22. package/src/presentation/layouts/DualImageVideoFeatureLayout.tsx +8 -26
  23. package/src/presentation/layouts/SingleImageFeatureLayout.tsx +10 -29
  24. package/src/presentation/layouts/SingleImageWithPromptFeatureLayout.tsx +8 -26
  25. package/src/presentation/layouts/layout-styles.ts +26 -0
  26. package/src/presentation/layouts/types/layout-props.ts +20 -43
  27. /package/src/domains/creations/presentation/components/{CreationsFilterBar.helpers.ts → filter-bar-utils.ts} +0 -0
  28. /package/src/domains/creations/presentation/utils/{filterUtils.ts → gallery-filters.ts} +0 -0
  29. /package/src/domains/scenarios/infrastructure/{scenario-helpers.ts → scenario-utils.ts} +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/react-native-ai-generation-content",
3
- "version": "1.61.54",
3
+ "version": "1.61.56",
4
4
  "description": "Provider-agnostic AI generation orchestration for React Native with result preview components",
5
5
  "main": "src/index.ts",
6
6
  "types": "src/index.ts",
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Content Moderation Constants
3
+ * Shared constants for content moderation validators
4
+ */
5
+
6
+ export const DEFAULT_PROTOCOLS = ["http:", "https:", "file:", "data:"] as const;
7
+ export const VIDEO_PROTOCOLS = ["http:", "https:", "file:"] as const;
8
+ export const DEFAULT_MAX_URI_LENGTH = 2048;
9
+ export const DEFAULT_MAX_TEXT_LENGTH = 5000;
@@ -5,12 +5,10 @@
5
5
 
6
6
  import type { Violation } from "../../../domain/entities/moderation.types";
7
7
  import { BaseModerator, type ModerationResult } from "./base.moderator";
8
-
9
- const DEFAULT_PROTOCOLS = ["http:", "https:", "file:", "data:"];
10
- const DEFAULT_MAX_URI_LENGTH = 2048;
8
+ import { DEFAULT_PROTOCOLS, DEFAULT_MAX_URI_LENGTH } from "../../constants/moderation.constants";
11
9
 
12
10
  class ImageModerator extends BaseModerator {
13
- private allowedProtocols = DEFAULT_PROTOCOLS;
11
+ private allowedProtocols: readonly string[] = DEFAULT_PROTOCOLS;
14
12
  private maxUriLength = DEFAULT_MAX_URI_LENGTH;
15
13
 
16
14
  setAllowedProtocols(protocols: string[]): void {
@@ -7,90 +7,12 @@ import type { Violation } from "../../../domain/entities/moderation.types";
7
7
  import { patternMatcherService } from "../pattern-matcher.service";
8
8
  import { rulesRegistry } from "../../rules/rules-registry";
9
9
  import { BaseModerator, type ModerationResult } from "./base.moderator";
10
- import { DEFAULT_MAX_TEXT_LENGTH } from "../../../../../infrastructure/constants/content.constants";
10
+ import { DEFAULT_MAX_TEXT_LENGTH } from "../../constants/moderation.constants";
11
+ import { containsMaliciousPatterns } from "../../utils/content-security.util";
12
+ import { containsPromptInjection } from "../../utils/prompt-injection.util";
11
13
 
12
14
  declare const __DEV__: boolean;
13
15
 
14
- /**
15
- * HTML entity encoding detection
16
- * More reliable than regex for detecting encoded malicious content
17
- */
18
- function containsHTMLEntities(content: string): boolean {
19
- const htmlEntities = [
20
- /</gi, />/gi, /"/gi, /&/gi, /'/gi,
21
- /&#\d+;/gi, /&#x[0-9a-fA-F]+;/gi,
22
- ];
23
- return htmlEntities.some(entity => entity.test(content));
24
- }
25
-
26
- /**
27
- * Safe string matching for malicious code detection
28
- * Uses string operations instead of regex where possible
29
- */
30
- function containsMaliciousPatterns(content: string): boolean {
31
- const lowerContent = content.toLowerCase();
32
-
33
- // Check for script tags (case-insensitive)
34
- const scriptPatterns = ["<script", "</script>", "javascript:", "onclick=", "onerror=", "onload="];
35
- for (const pattern of scriptPatterns) {
36
- if (lowerContent.includes(pattern)) {
37
- return true;
38
- }
39
- }
40
-
41
- // Check for HTML entities (potential evasion)
42
- if (containsHTMLEntities(content)) {
43
- return true;
44
- }
45
-
46
- return false;
47
- }
48
-
49
- /**
50
- * Multi-layered prompt injection detection
51
- * Combines regex with string matching for better security
52
- */
53
- function containsPromptInjection(content: string): boolean {
54
- const lowerContent = content.toLowerCase();
55
-
56
- // Critical injection patterns (string-based for safety)
57
- const criticalPatterns = [
58
- "ignore all instructions",
59
- "ignore previous instructions",
60
- "disregard all instructions",
61
- "forget all instructions",
62
- "you are now a",
63
- "jailbreak",
64
- "dan mode",
65
- "developer mode",
66
- "system:",
67
- "[system]",
68
- "<<system>>",
69
- ];
70
-
71
- for (const pattern of criticalPatterns) {
72
- if (lowerContent.includes(pattern)) {
73
- return true;
74
- }
75
- }
76
-
77
- // Additional regex patterns for more complex matching
78
- const regexPatterns = [
79
- /act\s+as\s+(if|though)\s+you/gi,
80
- /pretend\s+(you\s+are|to\s+be)/gi,
81
- /bypass\s+(your\s+)?(safety|content|moderation)/gi,
82
- /override\s+(your\s+)?(restrictions?|limitations?|rules?)/gi,
83
- ];
84
-
85
- return regexPatterns.some(pattern => {
86
- try {
87
- return pattern.test(content);
88
- } catch {
89
- return false;
90
- }
91
- });
92
- }
93
-
94
16
  class TextModerator extends BaseModerator {
95
17
  private maxLength = DEFAULT_MAX_TEXT_LENGTH;
96
18
 
@@ -100,7 +22,6 @@ class TextModerator extends BaseModerator {
100
22
 
101
23
  moderate(content: string): ModerationResult {
102
24
  if (typeof __DEV__ !== "undefined" && __DEV__) {
103
-
104
25
  console.log("[TextModerator] moderate() called", {
105
26
  contentLength: content?.length ?? 0,
106
27
  });
@@ -109,7 +30,6 @@ class TextModerator extends BaseModerator {
109
30
  const validationError = this.validate(content);
110
31
  if (validationError) {
111
32
  if (typeof __DEV__ !== "undefined" && __DEV__) {
112
-
113
33
  console.log("[TextModerator] validation failed", {
114
34
  ruleId: validationError.ruleId,
115
35
  violationType: validationError.violationType,
@@ -121,7 +41,6 @@ class TextModerator extends BaseModerator {
121
41
  const violations = this.evaluateRules(content);
122
42
 
123
43
  if (typeof __DEV__ !== "undefined" && __DEV__) {
124
-
125
44
  console.log("[TextModerator] moderate() completed", {
126
45
  isAllowed: violations.length === 0,
127
46
  violationsCount: violations.length,
@@ -149,7 +68,7 @@ class TextModerator extends BaseModerator {
149
68
  );
150
69
  }
151
70
 
152
- if (this.containsPromptInjection(content)) {
71
+ if (containsPromptInjection(content)) {
153
72
  return this.createViolation(
154
73
  "prompt-injection",
155
74
  "Security",
@@ -165,10 +84,6 @@ class TextModerator extends BaseModerator {
165
84
  return containsMaliciousPatterns(content);
166
85
  }
167
86
 
168
- private containsPromptInjection(content: string): boolean {
169
- return containsPromptInjection(content);
170
- }
171
-
172
87
  private evaluateRules(content: string): Violation[] {
173
88
  const rules = rulesRegistry.getRulesByContentType("text");
174
89
  const violations: Violation[] = [];
@@ -5,12 +5,10 @@
5
5
 
6
6
  import type { Violation } from "../../../domain/entities/moderation.types";
7
7
  import { BaseModerator, type ModerationResult } from "./base.moderator";
8
-
9
- const DEFAULT_PROTOCOLS = ["http:", "https:", "file:"];
10
- const DEFAULT_MAX_URI_LENGTH = 2048;
8
+ import { VIDEO_PROTOCOLS, DEFAULT_MAX_URI_LENGTH } from "../../constants/moderation.constants";
11
9
 
12
10
  class VideoModerator extends BaseModerator {
13
- private allowedProtocols = DEFAULT_PROTOCOLS;
11
+ private allowedProtocols: readonly string[] = VIDEO_PROTOCOLS;
14
12
  private maxUriLength = DEFAULT_MAX_URI_LENGTH;
15
13
 
16
14
  setAllowedProtocols(protocols: string[]): void {
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Content Security Utilities
3
+ * Provides security validation for content including malicious code detection
4
+ */
5
+
6
+ /**
7
+ * HTML entity encoding detection
8
+ * More reliable than regex for detecting encoded malicious content
9
+ */
10
+ export function containsHTMLEntities(content: string): boolean {
11
+ const htmlEntities = [
12
+ /&lt;/gi, /&gt;/gi, /&quot;/gi, /&amp;/gi, /&apos;/gi,
13
+ /&#\d+;/gi, /&#x[0-9a-fA-F]+;/gi,
14
+ ];
15
+ return htmlEntities.some(entity => entity.test(content));
16
+ }
17
+
18
+ /**
19
+ * Safe string matching for malicious code detection
20
+ * Uses string operations instead of regex where possible
21
+ */
22
+ export function containsMaliciousPatterns(content: string): boolean {
23
+ const lowerContent = content.toLowerCase();
24
+
25
+ // Check for script tags (case-insensitive)
26
+ const scriptPatterns = ["<script", "</script>", "javascript:", "onclick=", "onerror=", "onload="];
27
+ for (const pattern of scriptPatterns) {
28
+ if (lowerContent.includes(pattern)) {
29
+ return true;
30
+ }
31
+ }
32
+
33
+ // Check for HTML entities (potential evasion)
34
+ if (containsHTMLEntities(content)) {
35
+ return true;
36
+ }
37
+
38
+ return false;
39
+ }
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Prompt Injection Detection Utilities
3
+ * Detects various forms of prompt injection attacks
4
+ */
5
+
6
+ /**
7
+ * Multi-layered prompt injection detection
8
+ * Combines regex with string matching for better security
9
+ */
10
+ export function containsPromptInjection(content: string): boolean {
11
+ const lowerContent = content.toLowerCase();
12
+
13
+ // Critical injection patterns (string-based for safety)
14
+ const criticalPatterns = [
15
+ "ignore all instructions",
16
+ "ignore previous instructions",
17
+ "disregard all instructions",
18
+ "forget all instructions",
19
+ "you are now a",
20
+ "jailbreak",
21
+ "dan mode",
22
+ "developer mode",
23
+ "system:",
24
+ "[system]",
25
+ "<<system>>",
26
+ ];
27
+
28
+ for (const pattern of criticalPatterns) {
29
+ if (lowerContent.includes(pattern)) {
30
+ return true;
31
+ }
32
+ }
33
+
34
+ // Additional regex patterns for more complex matching
35
+ const regexPatterns = [
36
+ /act\s+as\s+(if|though)\s+you/gi,
37
+ /pretend\s+(you\s+are|to\s+be)/gi,
38
+ /bypass\s+(your\s+)?(safety|content|moderation)/gi,
39
+ /override\s+(your\s+)?(restrictions?|limitations?|rules?)/gi,
40
+ ];
41
+
42
+ return regexPatterns.some(pattern => {
43
+ try {
44
+ return pattern.test(content);
45
+ } catch {
46
+ return false;
47
+ }
48
+ });
49
+ }
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Content Moderation Validators
3
+ * Reusable validation functions for content moderation
4
+ */
5
+
6
+ /**
7
+ * Validates that content is not empty and is a string
8
+ */
9
+ export function validateContentPresence(content: unknown): boolean {
10
+ return Boolean(content && typeof content === "string");
11
+ }
12
+
13
+ /**
14
+ * Validates content length against maximum
15
+ */
16
+ export function validateContentLength(content: string, maxLength: number): boolean {
17
+ return content.length <= maxLength;
18
+ }
19
+
20
+ /**
21
+ * Validates URI protocol
22
+ */
23
+ export function validateUriProtocol(uri: string, allowedProtocols: readonly string[]): boolean {
24
+ const lowerUri = uri.toLowerCase();
25
+ return allowedProtocols.some((protocol) => lowerUri.startsWith(protocol));
26
+ }
27
+
28
+ /**
29
+ * Validates URI
30
+ */
31
+ export function validateUri(
32
+ uri: string,
33
+ options: {
34
+ maxLength?: number;
35
+ allowedProtocols?: readonly string[];
36
+ } = {}
37
+ ): { isValid: boolean; error?: { type: string; message: string } } {
38
+ if (!validateContentPresence(uri)) {
39
+ return { isValid: false, error: { type: "empty-uri", message: "empty URI" } };
40
+ }
41
+
42
+ if (options.maxLength && !validateContentLength(uri, options.maxLength)) {
43
+ return { isValid: false, error: { type: "uri-too-long", message: "URI too long" } };
44
+ }
45
+
46
+ if (options.allowedProtocols && !validateUriProtocol(uri, options.allowedProtocols)) {
47
+ return { isValid: false, error: { type: "invalid-protocol", message: "invalid protocol" } };
48
+ }
49
+
50
+ return { isValid: true };
51
+ }
@@ -168,7 +168,7 @@ export {
168
168
  getLocalizedTitle,
169
169
  getFilterCategoriesFromConfig,
170
170
  getTranslatedTypes,
171
- } from "./presentation/utils/filterUtils";
171
+ } from "./presentation/utils/gallery-filters";
172
172
 
173
173
  // =============================================================================
174
174
  // PRESENTATION LAYER - Screens
@@ -136,4 +136,4 @@ export type { FilterButton, CreationsFilterBarProps } from "./CreationsFilterBar
136
136
  export {
137
137
  createMediaFilterButtons,
138
138
  createStatusFilterButtons,
139
- } from "./CreationsFilterBar.helpers";
139
+ } from "./filter-bar-utils";
@@ -0,0 +1,74 @@
1
+ /**
2
+ * useGalleryState Hook
3
+ * Manages the state for the gallery screen including selection and media URL handling
4
+ */
5
+
6
+ import { useState, useEffect, useRef, useMemo } from "react";
7
+ import type { Creation } from "../../domain/entities/Creation";
8
+ import { getPreviewUrl } from "../../domain/utils";
9
+
10
+ export interface GalleryStateOptions {
11
+ initialCreationId?: string;
12
+ creations: Creation[] | undefined;
13
+ }
14
+
15
+ export interface GalleryStateReturn {
16
+ selectedCreation: Creation | null;
17
+ showRatingPicker: boolean;
18
+ selectedImageUrl: string | undefined;
19
+ selectedVideoUrl: string | undefined;
20
+ hasMediaToShow: boolean;
21
+ showPreview: boolean;
22
+ setSelectedCreation: (creation: Creation | null) => void;
23
+ setShowRatingPicker: (show: boolean) => void;
24
+ }
25
+
26
+ export function useGalleryState(options: GalleryStateOptions): GalleryStateReturn {
27
+ const { initialCreationId, creations } = options;
28
+ const [selectedCreation, setSelectedCreation] = useState<Creation | null>(null);
29
+ const [showRatingPicker, setShowRatingPicker] = useState(false);
30
+ const hasAutoSelectedRef = useRef(false);
31
+
32
+ // Auto-select creation when initialCreationId is provided
33
+ useEffect(() => {
34
+ if (initialCreationId && creations && creations.length > 0 && !hasAutoSelectedRef.current) {
35
+ const creation = creations.find((c) => c.id === initialCreationId);
36
+ if (creation) {
37
+ hasAutoSelectedRef.current = true;
38
+ setSelectedCreation(creation);
39
+ }
40
+ }
41
+ }, [initialCreationId, creations]);
42
+
43
+ // Extract media URLs from selected creation
44
+ const selectedImageUrl = useMemo(
45
+ () => selectedCreation ? (getPreviewUrl(selectedCreation.output) || selectedCreation.uri) : undefined,
46
+ [selectedCreation]
47
+ );
48
+
49
+ const selectedVideoUrl = useMemo(
50
+ () => selectedCreation?.output?.videoUrl,
51
+ [selectedCreation]
52
+ );
53
+
54
+ const hasMediaToShow = useMemo(
55
+ () => Boolean(selectedImageUrl || selectedVideoUrl),
56
+ [selectedImageUrl, selectedVideoUrl]
57
+ );
58
+
59
+ const showPreview = useMemo(
60
+ () => Boolean(selectedCreation && hasMediaToShow),
61
+ [selectedCreation, hasMediaToShow]
62
+ );
63
+
64
+ return {
65
+ selectedCreation,
66
+ showRatingPicker,
67
+ selectedImageUrl,
68
+ selectedVideoUrl,
69
+ hasMediaToShow,
70
+ showPreview,
71
+ setSelectedCreation,
72
+ setShowRatingPicker,
73
+ };
74
+ }
@@ -1,4 +1,4 @@
1
- import React, { useState, useMemo, useCallback, useEffect, useRef } from "react";
1
+ import React, { useMemo, useCallback } from "react";
2
2
  import { View, FlatList, RefreshControl } from "react-native";
3
3
  import {
4
4
  useAppDesignTokens,
@@ -11,11 +11,11 @@ import { useDeleteCreation } from "../hooks/useDeleteCreation";
11
11
  import { useProcessingJobsPoller } from "../hooks/useProcessingJobsPoller";
12
12
  import { useGalleryFilters } from "../hooks/useGalleryFilters";
13
13
  import { useGalleryCallbacks } from "../hooks/useGalleryCallbacks";
14
+ import { useGalleryState } from "../hooks/useGalleryState";
14
15
  import { GalleryHeader, CreationCard, GalleryEmptyStates } from "../components";
15
16
  import { GalleryResultPreview } from "../components/GalleryResultPreview";
16
17
  import { GalleryScreenHeader } from "../components/GalleryScreenHeader";
17
18
  import { MEDIA_FILTER_OPTIONS, STATUS_FILTER_OPTIONS } from "../../domain/types/creation-filter";
18
- import { getPreviewUrl } from "../../domain/utils";
19
19
  import { createFilterButtons, createItemTitle } from "../utils/filter-buttons.util";
20
20
  import type { Creation } from "../../domain/entities/Creation";
21
21
  import type { CreationsGalleryScreenProps } from "./creations-gallery.types";
@@ -37,9 +37,6 @@ export function CreationsGalleryScreen({
37
37
  getCreationTitle,
38
38
  }: CreationsGalleryScreenProps) {
39
39
  const tokens = useAppDesignTokens();
40
- const [selectedCreation, setSelectedCreation] = useState<Creation | null>(null);
41
- const [showRatingPicker, setShowRatingPicker] = useState(false);
42
- const hasAutoSelectedRef = useRef(false);
43
40
 
44
41
  const { data: creations, isLoading, refetch } = useCreations({ userId, repository });
45
42
  const deleteMutation = useDeleteCreation({ userId, repository });
@@ -51,15 +48,8 @@ export function CreationsGalleryScreen({
51
48
  enabled: !!userId && (creations?.length ?? 0) > 0,
52
49
  });
53
50
 
54
- useEffect(() => {
55
- if (initialCreationId && creations && creations.length > 0 && !hasAutoSelectedRef.current) {
56
- const creation = creations.find((c) => c.id === initialCreationId);
57
- if (creation) {
58
- hasAutoSelectedRef.current = true;
59
- setSelectedCreation(creation);
60
- }
61
- }
62
- }, [initialCreationId, creations]);
51
+ // Gallery state management
52
+ const galleryState = useGalleryState({ initialCreationId, creations });
63
53
 
64
54
  const callbacks = useGalleryCallbacks({
65
55
  userId,
@@ -68,9 +58,9 @@ export function CreationsGalleryScreen({
68
58
  t,
69
59
  deleteMutation,
70
60
  refetch: async () => { await refetch(); },
71
- setSelectedCreation,
72
- setShowRatingPicker,
73
- selectedCreation,
61
+ setSelectedCreation: galleryState.setSelectedCreation,
62
+ setShowRatingPicker: galleryState.setShowRatingPicker,
63
+ selectedCreation: galleryState.selectedCreation,
74
64
  onTryAgain,
75
65
  });
76
66
 
@@ -145,30 +135,25 @@ export function CreationsGalleryScreen({
145
135
  />
146
136
  ), [isLoading, creations, filters.isFiltered, tokens, t, config, emptyActionLabel, onEmptyAction, filters.clearAllFilters]);
147
137
 
148
- const selectedImageUrl = selectedCreation ? (getPreviewUrl(selectedCreation.output) || selectedCreation.uri) : undefined;
149
- const selectedVideoUrl = selectedCreation?.output?.videoUrl;
150
- const hasMediaToShow = selectedImageUrl || selectedVideoUrl;
151
- const showPreview = selectedCreation && hasMediaToShow;
152
-
153
138
  const screenHeader = useMemo(() => {
154
139
  if (!onBack) return undefined;
155
140
  return <GalleryScreenHeader title={t(config.translations.title)} onBack={onBack} />;
156
141
  }, [onBack, t, config.translations.title]);
157
142
 
158
- if (showPreview) {
143
+ if (galleryState.showPreview && galleryState.selectedCreation) {
159
144
  return (
160
145
  <GalleryResultPreview
161
- selectedCreation={selectedCreation}
162
- imageUrl={selectedVideoUrl ? undefined : selectedImageUrl}
163
- videoUrl={selectedVideoUrl}
164
- showRatingPicker={showRatingPicker}
146
+ selectedCreation={galleryState.selectedCreation}
147
+ imageUrl={galleryState.selectedVideoUrl ? undefined : galleryState.selectedImageUrl}
148
+ videoUrl={galleryState.selectedVideoUrl}
149
+ showRatingPicker={galleryState.showRatingPicker}
165
150
  config={config}
166
151
  t={t}
167
152
  onBack={callbacks.handleBack}
168
153
  onTryAgain={callbacks.handleTryAgain}
169
154
  onRate={callbacks.handleOpenRatingPicker}
170
155
  onSubmitRating={callbacks.handleSubmitRating}
171
- onCloseRating={() => setShowRatingPicker(false)}
156
+ onCloseRating={() => galleryState.setShowRatingPicker(false)}
172
157
  />
173
158
  );
174
159
  }
@@ -164,36 +164,5 @@ export class ImagePromptBuilder {
164
164
  }
165
165
  }
166
166
 
167
- /**
168
- * Create anime selfie prompt for Kontext model
169
- * Kontext uses instruction-based editing that preserves character identity automatically
170
- */
171
- export function createAnimeSelfiePrompt(customStyle?: string): AnimeSelfiePromptResult {
172
- const stylePrefix = customStyle ? `${customStyle} anime style` : "anime style";
173
-
174
- const prompt = [
175
- `Transform this person into a ${stylePrefix} illustration.`,
176
- "IMPORTANT: Preserve the exact same gender - if male keep male, if female keep female.",
177
- "Keep the same face structure, hair color, eye color, skin tone, and facial expression.",
178
- "Make it look like a high-quality Japanese anime character portrait.",
179
- "Use vibrant anime colors, clean lineart, and cel-shaded rendering.",
180
- "Large expressive anime eyes with detailed iris, smooth anime skin with subtle blush.",
181
- ].join(" ");
182
-
183
- return {
184
- prompt,
185
- guidance_scale: 4.0,
186
- };
187
- }
188
-
189
- /**
190
- * Create a style transfer prompt with identity preservation
191
- */
192
- export function createStyleTransferPrompt(style: string): ImagePromptResult {
193
- return ImagePromptBuilder.create()
194
- .withSegment(`${style} style`) // Style first
195
- .withIdentityPreservation()
196
- .withQuality()
197
- .withAnatomySafety()
198
- .build();
199
- }
167
+ // Re-export prompt creation utilities for backward compatibility
168
+ export { createAnimeSelfiePrompt, createStyleTransferPrompt } from "../utils/prompt-creators.util";
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Prompt Creation Utilities
3
+ * Provides helper functions for creating specific types of prompts
4
+ */
5
+
6
+ import type { ImagePromptResult, AnimeSelfiePromptResult } from "../services/ImagePromptBuilder";
7
+ import { ImagePromptBuilder } from "../services/ImagePromptBuilder";
8
+
9
+ /**
10
+ * Create anime selfie prompt for Kontext model
11
+ * Kontext uses instruction-based editing that preserves character identity automatically
12
+ */
13
+ export function createAnimeSelfiePrompt(customStyle?: string): AnimeSelfiePromptResult {
14
+ const stylePrefix = customStyle ? `${customStyle} anime style` : "anime style";
15
+
16
+ const prompt = [
17
+ `Transform this person into a ${stylePrefix} illustration.`,
18
+ "IMPORTANT: Preserve the exact same gender - if male keep male, if female keep female.",
19
+ "Keep the same face structure, hair color, eye color, skin tone, and facial expression.",
20
+ "Make it look like a high-quality Japanese anime character portrait.",
21
+ "Use vibrant anime colors, clean lineart, and cel-shaded rendering.",
22
+ "Large expressive anime eyes with detailed iris, smooth anime skin with subtle blush.",
23
+ ].join(" ");
24
+
25
+ return {
26
+ prompt,
27
+ guidance_scale: 4.0,
28
+ };
29
+ }
30
+
31
+ /**
32
+ * Create a style transfer prompt with identity preservation
33
+ */
34
+ export function createStyleTransferPrompt(style: string): ImagePromptResult {
35
+ return ImagePromptBuilder.create()
36
+ .withSegment(`${style} style`)
37
+ .withIdentityPreservation()
38
+ .withQuality()
39
+ .withAnatomySafety()
40
+ .build();
41
+ }
@@ -20,8 +20,8 @@ export {
20
20
  filterScenariosByCategory,
21
21
  getScenarioCategories,
22
22
  findScenarioById,
23
- } from "./infrastructure/scenario-helpers";
24
- export type { AppScenarioConfig } from "./infrastructure/scenario-helpers";
23
+ } from "./infrastructure/scenario-utils";
24
+ export type { AppScenarioConfig } from "./infrastructure/scenario-utils";
25
25
 
26
26
  // Scenario Registry - Singleton for app configuration
27
27
  export {