@@ -34,11 +34,12 @@ function generateLogoPrompt(projectName: string, projectType?: string): string {
3434
3535 return `Minimalist logo icon for "${ name } " software project.
3636Style: Warm academic, intellectual, high-end minimalist.
37- Colors: Primary terracotta clay (#CC785C), warm off-white background, deep charcoal accents.
37+ Colors: Primary terracotta clay (#CC785C), deep charcoal accents.
38+ IMPORTANT: Transparent background (PNG with alpha channel).
3839Shape: Simple geometric form, abstract symbol, clean lines.
3940${ projectType ? `Theme: ${ projectType } tool/application.` : "" }
40- No text, no gradients. Single iconic shape. Suitable for app icon and favicon .
41- Professional, memorable, works at small sizes (32x32).` ;
41+ No text, no gradients, no background . Single iconic shape on transparent background .
42+ Suitable for app icon and favicon. Professional, memorable, works at small sizes (32x32).`;
4243}
4344
4445export function LogoManager ( { projectPath, embedded = false } : LogoManagerProps ) {
@@ -61,6 +62,36 @@ export function LogoManager({ projectPath, embedded = false }: LogoManagerProps)
6162 // Auto-generated prompt based on project
6263 const autoPrompt = useMemo ( ( ) => generateLogoPrompt ( projectName ) , [ projectName ] ) ;
6364
65+ // Load previously generated logos from Application Support
66+ const loadGeneratedLogos = useCallback ( async ( ) => {
67+ try {
68+ const result = await invoke < string > ( "exec_shell_command" , {
69+ command : `ls -t "$HOME/Library/Application Support/com.lovstudio.lovcode/generated-logos/"*.png 2>/dev/null | head -20` ,
70+ cwd : projectPath ,
71+ } ) ;
72+ const files = result . trim ( ) . split ( '\n' ) . filter ( f => f ) ;
73+ if ( files . length === 0 ) return ;
74+
75+ const previews = await Promise . all (
76+ files . map ( async ( path ) => {
77+ const base64 = await invoke < string > ( "read_file_base64" , { path } ) ;
78+ return `data:image/png;base64,${ base64 } ` ;
79+ } )
80+ ) ;
81+ setGenPreviews ( previews ) ;
82+ if ( previews . length > 0 ) setSelectedPreview ( 0 ) ;
83+ } catch {
84+ // No logos yet, that's fine
85+ }
86+ } , [ projectPath ] ) ;
87+
88+ // Load generated logos when dialog opens
89+ useEffect ( ( ) => {
90+ if ( showGenDialog && genPreviews . length === 0 ) {
91+ loadGeneratedLogos ( ) ;
92+ }
93+ } , [ showGenDialog , genPreviews . length , loadGeneratedLogos ] ) ;
94+
6495 // Load current logo and versions
6596 const loadLogoData = useCallback ( async ( ) => {
6697 try {
@@ -91,13 +122,17 @@ export function LogoManager({ projectPath, embedded = false }: LogoManagerProps)
91122 const escapedPrompt = prompt . replace ( / ' / g, "'\\''" ) . replace ( / \n / g, ' ' ) ;
92123 const scriptPath = "$HOME/.claude/plugins/marketplaces/lovstudio-plugins-official/skills/image-gen/gen_image.py" ;
93124
94- // Generate 2 variants in parallel
125+ // Generate 2 variants in parallel, save to Application Support
95126 const generateOne = async ( index : number ) : Promise < string > => {
96- const outputPath = `/tmp/lovcode-logo-${ Date . now ( ) } -${ index } .png` ;
97- await invoke < string > ( "exec_shell_command" , {
98- command : `python3 ${ scriptPath } '${ escapedPrompt } ' -o '${ outputPath } ' -q high --no-open` ,
127+ const timestamp = Date . now ( ) ;
128+ const filename = `logo-${ timestamp } -${ index } .png` ;
129+ // Get actual path from shell (expands $HOME)
130+ const result = await invoke < string > ( "exec_shell_command" , {
131+ command : `dir="$HOME/Library/Application Support/com.lovstudio.lovcode/generated-logos" && mkdir -p "$dir" && python3 ${ scriptPath } '${ escapedPrompt } ' -o "$dir/${ filename } " -q high --no-open && echo "$dir/${ filename } "` ,
99132 cwd : projectPath ,
100133 } ) ;
134+ // Extract the echoed path (last line of output)
135+ const outputPath = result . trim ( ) . split ( '\n' ) . pop ( ) || '' ;
101136 const base64 = await invoke < string > ( "read_file_base64" , { path : outputPath } ) ;
102137 return `data:image/png;base64,${ base64 } ` ;
103138 } ;
@@ -127,10 +162,10 @@ export function LogoManager({ projectPath, embedded = false }: LogoManagerProps)
127162 } ) ;
128163
129164 setShowGenDialog ( false ) ;
130- setGenPreviews ( [ ] ) ;
131- setSelectedPreview ( null ) ;
132- setGenPrompt ( "" ) ;
133165 await loadLogoData ( ) ;
166+
167+ // Notify ProjectLogo to refresh
168+ window . dispatchEvent ( new CustomEvent ( "logo-updated" , { detail : { projectPath } } ) ) ;
134169 } catch ( err ) {
135170 setError ( err instanceof Error ? err . message : String ( err ) ) ;
136171 }
@@ -152,6 +187,7 @@ export function LogoManager({ projectPath, embedded = false }: LogoManagerProps)
152187 targetFilename : `logo${ selected . substring ( selected . lastIndexOf ( "." ) ) } ` ,
153188 } ) ;
154189 await loadLogoData ( ) ;
190+ window . dispatchEvent ( new CustomEvent ( "logo-updated" , { detail : { projectPath } } ) ) ;
155191 }
156192 } catch ( err ) {
157193 console . error ( "Failed to upload logo:" , err ) ;
@@ -166,6 +202,7 @@ export function LogoManager({ projectPath, embedded = false }: LogoManagerProps)
166202 logoPath : version . path ,
167203 } ) ;
168204 await loadLogoData ( ) ;
205+ window . dispatchEvent ( new CustomEvent ( "logo-updated" , { detail : { projectPath } } ) ) ;
169206 } catch ( err ) {
170207 console . error ( "Failed to set logo:" , err ) ;
171208 }
0 commit comments