Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(frontend): fix some fd UI styling #136

Merged
merged 22 commits into from
Mar 2, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
2ec29de
feat(components): add tab component with transition effects
Sma1lboy Feb 26, 2025
55e9eab
feat(frontend): update color palette and add moving border component
Sma1lboy Feb 27, 2025
99aa232
feat(nav): add floating navbar component with tab transition effects
Sma1lboy Feb 27, 2025
f53556b
[autofix.ci] apply automated fixes
autofix-ci[bot] Feb 27, 2025
9bbead5
feat(layout): add About page and integrate NavLayout component
Sma1lboy Feb 27, 2025
3eb24af
Merge branch 'main' into chore-fix-some-fd-ui-styling
Sma1lboy Mar 1, 2025
6fe967d
feat(project): enhance project model with public status, subscription…
Sma1lboy Mar 1, 2025
451e06d
feat(project): add project visibility, forking functionality, and enh…
Sma1lboy Mar 1, 2025
bb76c4b
feat(chat): add userId relation for easier access to user ID in chat …
Sma1lboy Mar 1, 2025
ab4f8d1
feat(graphql): add userId field to Chat type and implement user proje…
Sma1lboy Mar 1, 2025
2990eb0
feat(chat): integrate ProjectProvider and add project status monitori…
Sma1lboy Mar 1, 2025
d787cf4
feat(docker): update Node.js version and improve Dockerfile commands
Sma1lboy Mar 1, 2025
bdfb818
feat: implement new authentication flow and improve user session mana…
Sma1lboy Mar 2, 2025
33951c5
feat(frontend): homepage sidebar (#138)
PengyuChen01 Mar 2, 2025
cd6029a
[autofix.ci] apply automated fixes
autofix-ci[bot] Mar 2, 2025
ce97993
feat(project): add fetchPublicProjects query and input type for proje…
Sma1lboy Mar 2, 2025
7ed0cd7
feat(prompt-form): add input focus tracking and optimize visibility d…
Sma1lboy Mar 2, 2025
6cfd88a
feat: refactor model tag fetching and enhance project name generation…
Sma1lboy Mar 2, 2025
5dab530
feat(backend): adding prompt tools (#141)
Sma1lboy Mar 2, 2025
14a016b
feat: remove unused public projects query and enhance prompt tool des…
Sma1lboy Mar 2, 2025
4c7b804
fix the file name not follow xxx-xxx
Mar 2, 2025
0295243
Merge branch 'main' into chore-fix-some-fd-ui-styling
Sma1lboy Mar 2, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions backend/.env
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ PORT=8080
JWT_SECRET="JACKSONCHENNAHEULALLENPENGYU"
JWT_REFRESH_SECRET="JACKSONCHENNAHEULALLENPENGYUREFRESH"
SALT_ROUNDS=123
NODE_ENV="DEV"
6 changes: 5 additions & 1 deletion backend/src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ import { APP_INTERCEPTOR } from '@nestjs/core';
import { LoggingInterceptor } from 'src/interceptor/LoggingInterceptor';
import { PromptToolModule } from './prompt-tool/prompt-tool.module';

// TODO(Sma1lboy): move to a separate file
function isProduction(): boolean {
return process.env.NODE_ENV === 'production';
}
@Module({
imports: [
ConfigModule.forRoot({ isGlobal: true }),
Expand All @@ -33,7 +37,7 @@ import { PromptToolModule } from './prompt-tool/prompt-tool.module';
TypeOrmModule.forRoot({
type: 'sqlite',
database: join(process.cwd(), './database.db'),
synchronize: true,
synchronize: !isProduction(),
entities: [__dirname + '/**/*.model{.ts,.js}'],
}),
InitModule,
Expand Down
6 changes: 6 additions & 0 deletions backend/src/chat/chat.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
Column,
ManyToOne,
JoinColumn,
RelationId,
} from 'typeorm';
import { Message } from 'src/chat/message.model';
import { SystemBaseModel } from 'src/system-base-model/system-base.model';
Expand Down Expand Up @@ -56,6 +57,11 @@ export class Chat extends SystemBaseModel {
@Field(() => User)
user: User;

// Adding relation id to easily access the user id
@RelationId((chat: Chat) => chat.user)
@Field(() => ID)
userId: string;

@ManyToOne(() => Project, (project) => project.chats)
@Field(() => Project, { nullable: true })
project: Project;
Expand Down
5 changes: 1 addition & 4 deletions backend/src/chat/chat.resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,7 @@ export class ChatResolver {
}

@Query(() => [String], { nullable: true })
async getAvailableModelTags(
@GetUserIdFromToken() userId: string,
): Promise<string[]> {
this.logger.log('Fetching model tags for user:', userId);
async getAvailableModelTags(): Promise<string[]> {
try {
const response = await this.chatProxyService.fetchModelTags();
this.logger.log('Loaded model tags:', response);
Expand Down
8 changes: 6 additions & 2 deletions backend/src/common/model-provider/openai-model-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export class OpenAIModelProvider implements IModelProvider {

for (const model of chatModels) {
if (model.default) {
this.defaultModel = model.model;
this.defaultModel = model.alias || model.model;
}
if (!model.endpoint || !model.token) continue;

Expand Down Expand Up @@ -95,7 +95,7 @@ export class OpenAIModelProvider implements IModelProvider {
const completion = await queue.add(async () => {
const result = await this.openai.chat.completions.create({
messages: input.messages,
model: input.model,
model: input.model || this.baseModel,
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Consider standardizing null checking operators

Line 98 uses logical OR (||) for fallback while line 94 uses nullish coalescing (??). This subtle difference could lead to unexpected behavior if input.model is falsy but not null/undefined (like an empty string).


🏁 Script executed:

#!/bin/bash
# Description: Search for other examples of model fallback patterns in the codebase

# Search for nullish coalescing pattern with model
rg -A1 "model\s+\?\?" --type ts

# Search for logical OR pattern with model 
rg -A1 "model\s+\|\|" --type ts

Length of output: 1079


Attention: Standardizing Fallback Operator Usage

It appears that in backend/src/common/model-provider/openai-model-provider.ts there is an inconsistent use of fallback operators. At line 94 the code uses nullish coalescing (??), while at line 98 it falls back using logical OR (||). This discrepancy can lead to unexpected behavior when input.model is a falsy value (like an empty string) that isn’t null or undefined. Similar patterns using || are also found in backend/src/project/project.service.ts.

  • File: backend/src/common/model-provider/openai-model-provider.ts
    • Line 94: const queue = this.getQueueForModel(input.model ?? this.defaultModel);
    • Line 98: model: input.model || this.baseModel,
  • File: backend/src/project/project.service.ts
    • Usage example: model: input.model || this.model.baseModel,

Recommendation: Please standardize the null checking operator across the codebase. If empty strings or other falsy values should be preserved, consider using ?? consistently for fallback. Otherwise, add inline comments to clarify the rationale behind using different operators if the behavior is intended.

stream: false,
});
if (!result) throw new Error('No completion result received');
Expand Down Expand Up @@ -255,4 +255,8 @@ export class OpenAIModelProvider implements IModelProvider {
// OpenAI SDK handles its own request management
return [];
}

get baseModel(): string {
return this.defaultModel;
}
}
44 changes: 26 additions & 18 deletions backend/src/project/build-system-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,29 +87,37 @@ export function buildProjectSequenceByProject(
* Generates a project name prompt based on the provided description.
*/
export function generateProjectNamePrompt(description: string): string {
return `You are a project name generator. Based on the following project description, generate a concise, memorable, and meaningful project name.
return `You are a project name generator specializing in creating clear, descriptive titles for technical projects. Based on the provided description, generate a concise yet comprehensive project title.

Input Description: ${description}

Requirements for the project name:
1. Must be 1-3 words maximum
2. Should be clear and professional
3. Avoid generic terms like "project" or "system"
4. Use camelCase or kebab-case format
5. Should reflect the core functionality or purpose
6. Must be unique and memorable
7. Should be easy to pronounce
8. Avoid acronyms unless they're very intuitive
Project Title Guidelines:
1. Create a descriptive title (not exceeding 20 words)
2. Format as a standard phrase that clearly identifies the project purpose
3. Include specific details about:
- The type of application (e.g., Backend, Frontend, Mobile App)
- The industry or organization it serves
- Key functionality or purpose
4. Ensure the title is:
- Clear and descriptive
- Professional and straightforward
- Easy to understand for stakeholders
- Specific enough to differentiate from other projects
5. Acceptable formatting examples:
- "Backend System for Financial Reporting"
- "Mobile App for Patient Monitoring"
- "Data Analytics Platform for Retail Inventory"
6. Include organization names when relevant (e.g., "Backend App for Chinese Sakura Bank")

Please respond ONLY with the project name, without any explanation or additional text.
Please respond ONLY with the project title. Do not include explanations or additional text.

Example inputs and outputs:
Description: "A task management system with real-time collaboration features"
Output: taskFlow
Example high-quality outputs:
Description: "A task management system with real-time collaboration features for marketing teams"
Output: Collaboration Platform for Marketing Task Management

Description: "An AI-powered document analysis and extraction system"
Output: docMind
Description: "An AI-powered document analysis system for legal departments"
Output: AI Document Analysis Tool for Legal Departments

Description: "A microservice-based e-commerce platform with advanced inventory management"
Output: tradeCore`;
Description: "A microservice-based e-commerce platform with advanced inventory management for a furniture retailer"
Output: E-commerce System for Furniture Inventory Management`;
}
60 changes: 59 additions & 1 deletion backend/src/project/dto/project.input.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// DTOs for Project APIs
import { InputType, Field, ID } from '@nestjs/graphql';
import { InputType, Field, ID, ObjectType } from '@nestjs/graphql';
import { IsNotEmpty, IsString, IsUUID, IsOptional } from 'class-validator';
import { Project } from '../project.model';

/**
* @deprecated We don't need project upsert
Expand Down Expand Up @@ -41,6 +42,12 @@ export class CreateProjectInput {

@Field(() => String, { nullable: true })
databaseType?: string;

@Field(() => Boolean, { nullable: true })
public: boolean;

@Field(() => String, { nullable: true, defaultValue: 'gpt-4o-mini' })
model: string;
}

@InputType()
Expand All @@ -60,3 +67,54 @@ export class IsValidProjectInput {
@Field(() => String, { nullable: true })
projectPath: string;
}

@InputType()
export class UpdateProjectPublicStatusInput {
@Field(() => ID)
projectId: string;

@Field()
isPublic: boolean;
}

@InputType()
export class UpdateProjectPhotoUrlInput {
@Field(() => ID)
projectId: string;

@Field()
photoUrl: string;
}

@InputType()
export class SubscribeToProjectInput {
@Field(() => ID)
projectId: string;
}

@InputType()
export class ForkProjectInput {
@Field(() => ID)
projectId: string;
}

@ObjectType()
export class ProjectSubscriptionResult {
@Field()
success: boolean;

@Field({ nullable: true })
message?: string;

@Field(() => Project, { nullable: true })
project?: Project;
}

@InputType()
export class FetchPublicProjectsInputs {
@Field()
strategy: 'trending' | 'latest';

@Field()
size: number;
}
66 changes: 65 additions & 1 deletion backend/src/project/project.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,71 @@ export class Project extends SystemBaseModel {
@OneToMany(() => Chat, (chat) => chat.project, {
cascade: true, // Automatically save related chats
lazy: true, // Load chats only when accessed
onDelete: 'CASCADE', // Delete chats when user is deleted
onDelete: 'CASCADE', // Delete chats when project is deleted
})
chats: Promise<Chat[]>;

/**
* Represents whether the project is public or private
*/
@Field()
@Column({ default: false })
isPublic: boolean;

/**
* Counts the number of times this project has been subscribed to (copied)
*/
@Field()
@Column({ default: 0 })
subNumber: number;

/**
* The URL to the project's screenshot or thumbnail
*/
@Field({ nullable: true })
@Column({ nullable: true })
photoUrl: string;

/**
* Unique identifier for tracking project lineage
* Used to track which projects are copies of others
*/
@Field()
@Column({ unique: true, default: () => 'uuid_generate_v4()' })
uniqueProjectId: string;

/**
* If this project is a copy/fork, stores the uniqueProjectId of the original project.
* Projects with forkedFromId are fully editable by their new owner while maintaining
* a reference to the original project they were copied from.
*/
@Field({ nullable: true })
@Column({ nullable: true })
forkedFromId: string;

/**
* Reference to the original project if this is a copy/fork
*/
@Field(() => Project, { nullable: true })
@ManyToOne(() => Project, (project) => project.forks, { nullable: true })
@JoinColumn({ name: 'forkedFromId', referencedColumnName: 'uniqueProjectId' })
forkedFrom: Project;

/**
* Projects that were copied from this project
*/
@Field(() => [Project], { nullable: true })
@OneToMany(() => Project, (project) => project.forkedFrom)
forks: Project[];

/**
* Projects copied from this one
* Maintained for backwards compatibility, same as forks
*/
@Field(() => [Project], {
nullable: true,
description: 'Projects that are copies of this project',
})
@OneToMany(() => Project, (project) => project.forkedFrom)
subscribers: Project[];
}
Loading
Loading