Skip to content

Feature UI optimization nov 2025#16

Open
saravatpt wants to merge 37 commits intogoogle-gemini:mainfrom
saravatpt:feature-ui-optimization-nov-2025
Open

Feature UI optimization nov 2025#16
saravatpt wants to merge 37 commits intogoogle-gemini:mainfrom
saravatpt:feature-ui-optimization-nov-2025

Conversation

@saravatpt
Copy link

No description provided.

saravatpt and others added 30 commits November 20, 2025 08:28
added voice input, grouping in compose image, face cleanp, alias prompt.
…oject structure for a professional AI creative studio.
@google-cla
Copy link

google-cla bot commented Dec 11, 2025

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

@gemini-code-assist
Copy link

Summary of Changes

Hello @saravatpt, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly expands the application's capabilities, transforming it into a full-featured AI creative studio. It introduces core functionalities such as user authentication, a credit-based payment system, and advanced image generation and editing tools. The changes also include robust data persistence for user creations and a comprehensive set of documentation to guide users and developers through setup and usage, marking a major step towards a production-ready platform.

Highlights

  • Transformed Project Scope: The project has evolved from a 'quickstart' to a 'production-ready AI-Powered Creative Studio', introducing robust features like user authentication, a credit-based billing system, and comprehensive deployment documentation.
  • User Authentication & Billing System: Implemented Google OAuth via Supabase for user authentication, including a 14-day free trial with 100 credits for new users. A full credit-based billing system is integrated using Stripe for purchases, with webhooks to update user balances.
  • Enhanced Image Composition & Editing: Introduced new 'Compose Album' and advanced 'Compose Image' modes, alongside a 'Photo Editor' for fine-tuning images. Features like AI prompt enhancement, character consistency, lighting harmonization, and text integration are now available.
  • History, Folders & Prompt Management: Users can now save generated images to a personal history, organize them into custom folders, and utilize a 'Prompt Library' to save and reuse prompts. Image deletion and folder assignment functionalities are also included.
  • Comprehensive Documentation & Deployment: Extensive documentation has been added, including detailed guides for Google Cloud Run deployment, Stripe setup, and a user guide for all new features. The application is now Dockerized for easier deployment.
Ignored Files
  • Ignored by pattern: .github/workflows/** (1)
    • .github/workflows/deploy.yml
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This is a massive and impressive feature update that transforms the project from a simple quickstart into a full-fledged, production-ready application. The architecture, incorporating Supabase for authentication, Prisma with a serverless-friendly adapter, and Stripe for payments, is well-thought-out. The new UI is rich with features like image history, folders, and advanced composition controls. My review focuses on enhancing the robustness and maintainability of the new backend APIs and database schema, with particular attention to potential race conditions, API versioning risks, and opportunities for code simplification.

Comment on lines +62 to +76
let promptRecord = await prisma.prompt.findFirst({
where: {
userId: user.id,
text: prompt
}
})

if (!promptRecord) {
promptRecord = await prisma.prompt.create({
data: {
text: prompt,
userId: user.id
}
})
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

This logic for finding or creating a prompt has a potential race condition. If two requests for the same new prompt arrive concurrently, both could pass the findFirst check and then both attempt to create, leading to a unique constraint violation. To make this atomic and prevent errors, you should add a unique constraint to the Prompt model for userId and text, and then use prisma.prompt.upsert here.

const stripe = process.env.STRIPE_SECRET_KEY
? new Stripe(process.env.STRIPE_SECRET_KEY, {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
apiVersion: '2024-12-18.acacia' as any,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Using a future date for the Stripe apiVersion and casting it with as any is risky. Stripe API versions can introduce breaking changes, and this approach bypasses type safety. It's safer to use a specific, stable version that is known to be compatible with your code. Please use a valid, current API version string without the as any cast.

Suggested change
apiVersion: '2024-12-18.acacia' as any,
apiVersion: '2024-06-20',

Comment on lines +37 to +46
model Prompt {
id String @id @default(uuid())
text String
createdAt DateTime @default(now())

userId String
user UserProfile @relation(fields: [userId], references: [id])

images Image[]
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The Prompt model should enforce that a user cannot have multiple saved prompts with the exact same text. This helps maintain data integrity and prevents the race condition identified in app/api/images/save/route.ts. Please add a composite unique constraint on the userId and text fields.

model Prompt {
  id        String   @id @default(uuid())
  text      String
  createdAt DateTime @default(now())
  
  userId    String
  user      UserProfile @relation(fields: [userId], references: [id])
  
  images    Image[]

  @@unique([userId, text])
}

Comment on lines +42 to +44
if [ -f yarn.lock ]; then npx prisma generate && yarn run build; \
elif [ -f package-lock.json ]; then npx prisma generate && npm run build; \
elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && npx prisma generate && pnpm run build; \

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The build command for each package manager duplicates npx prisma generate. To improve maintainability and reduce redundancy, you could run npx prisma generate once before the conditional package manager logic.

  npx prisma generate && \
  if [ -f yarn.lock ]; then yarn run build; \
  elif [ -f package-lock.json ]; then npm run build; \
  elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm run build; \

Comment on lines +171 to +175
} catch (error: unknown) {
console.error("Error editing image with Gemini:", error instanceof Error ? error.message : String(error));
if (error && typeof error === 'object' && 'response' in error) {
console.error("Gemini API Error Response:", JSON.stringify(error.response, null, 2));
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Your error handling has been significantly improved with more specific error messages. However, you're using error instanceof Error ? error.message : String(error) which is good, but for API errors from libraries like @google/genai, the actual error details are often nested inside the error object. Logging the full error object or a stringified version can provide more context for debugging, especially for failed API requests.

    console.error("Error editing image with Gemini:", error);
    if (error instanceof Error && 'cause' in error) {
      console.error("Error Cause:", (error as any).cause);
    }

Comment on lines +76 to +80
} catch (error: unknown) {
console.error("Error generating image with Gemini:", error instanceof Error ? error.message : String(error));
if (error && typeof error === 'object' && 'response' in error) {
console.error("Gemini API Error Response:", JSON.stringify(error.response, null, 2));
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The error handling here is much better. To make debugging even easier, consider logging the entire error object, as API-specific details from the Gemini client might be nested within it. This can be invaluable for diagnosing issues with API requests.

Suggested change
} catch (error: unknown) {
console.error("Error generating image with Gemini:", error instanceof Error ? error.message : String(error));
if (error && typeof error === 'object' && 'response' in error) {
console.error("Gemini API Error Response:", JSON.stringify(error.response, null, 2));
}
} catch (error: unknown) {
console.error("Error generating image with Gemini:", error);
if (error instanceof Error && 'cause' in error) {
console.error("Error Cause:", (error as any).cause);
}

Comment on lines +64 to +68
} catch (error: unknown) {
console.error("Error generating image:", error instanceof Error ? error.message : String(error));
if (error && typeof error === 'object' && 'response' in error) {
console.error("Imagen API Error Response:", JSON.stringify(error.response, null, 2));
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The improved error handling is a great addition. For better debugging of API-related failures, it would be beneficial to log the full error object. Client libraries often wrap the original API response or error details, and logging the whole object can reveal crucial information that isn't in error.message.

Suggested change
} catch (error: unknown) {
console.error("Error generating image:", error instanceof Error ? error.message : String(error));
if (error && typeof error === 'object' && 'response' in error) {
console.error("Imagen API Error Response:", JSON.stringify(error.response, null, 2));
}
} catch (error: unknown) {
console.error("Error generating image:", error);
if (error instanceof Error && 'cause' in error) {
console.error("Imagen API Error Cause:", (error as any).cause);
}

Comment on lines 87 to +91
} catch (error: unknown) {
console.error("Error starting Veo generation:", error);
console.error("Error starting Veo generation:", error instanceof Error ? error.message : String(error));
if (error && typeof error === 'object' && 'response' in error) {
console.error("Veo API Error Response:", JSON.stringify(error.response, null, 2));
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The enhanced error handling is a good improvement. To further aid in debugging, especially for issues related to the Veo API, consider logging the entire error object. API client libraries often nest important details within the error object, which can be lost when only logging error.message.

Suggested change
} catch (error: unknown) {
console.error("Error starting Veo generation:", error);
console.error("Error starting Veo generation:", error instanceof Error ? error.message : String(error));
if (error && typeof error === 'object' && 'response' in error) {
console.error("Veo API Error Response:", JSON.stringify(error.response, null, 2));
}
} catch (error: unknown) {
console.error("Error starting Veo generation:", error);
if (error instanceof Error && 'cause' in error) {
console.error("Veo API Error Cause:", (error as any).cause);
}

Comment on lines +31 to +50
const existingProfile = await prisma.userProfile.findUnique({
where: { id: user.id }
})

if (!existingProfile) {
// New user: Give 14 days trial + 100 credits
const trialEndsAt = new Date()
trialEndsAt.setDate(trialEndsAt.getDate() + 14)

await prisma.userProfile.create({
data: {
id: user.id,
email: user.email!,
fullName: user.user_metadata.full_name,
avatarUrl: user.user_metadata.avatar_url,
credits: 100,
trialEndsAt: trialEndsAt
}
})
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This logic for creating a user profile if it doesn't exist can be simplified and made more robust by using prisma.userProfile.upsert. This combines the find and create operations into a single atomic database call, which is cleaner and avoids potential race conditions.

                const trialEndsAt = new Date();
                trialEndsAt.setDate(trialEndsAt.getDate() + 14);

                await prisma.userProfile.upsert({
                    where: { id: user.id },
                    update: {},
                    create: {
                        id: user.id,
                        email: user.email!,
                        fullName: user.user_metadata.full_name,
                        avatarUrl: user.user_metadata.avatar_url,
                        credits: 100,
                        trialEndsAt: trialEndsAt
                    }
                });

@@ -0,0 +1,19 @@
$p1 = "d:\AIgnite\AIG-POCs\PhotoAIG\PhotoAIG\public\prompt.json"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This script uses a hardcoded absolute Windows path, which makes it non-portable for other developers, especially those on different operating systems. It's better to use relative paths to ensure the script can be run from the project root by anyone.

$p1 = "./public/prompt.json"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant